6#include <rapidfuzz/details/Range.hpp>
7#include <rapidfuzz/distance/Levenshtein_impl.hpp>
145template <
typename InputIt1,
typename InputIt2>
147 LevenshteinWeightTable weights = {1, 1, 1},
148 size_t score_cutoff = std::numeric_limits<size_t>::max(),
149 size_t score_hint = std::numeric_limits<size_t>::max())
151 return detail::Levenshtein::distance(first1, last1, first2, last2, weights, score_cutoff, score_hint);
154template <
typename Sentence1,
typename Sentence2>
156 LevenshteinWeightTable weights = {1, 1, 1},
157 size_t score_cutoff = std::numeric_limits<size_t>::max(),
158 size_t score_hint = std::numeric_limits<size_t>::max())
160 return detail::Levenshtein::distance(s1, s2, weights, score_cutoff, score_hint);
163template <
typename InputIt1,
typename InputIt2>
164size_t levenshtein_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
165 LevenshteinWeightTable weights = {1, 1, 1},
size_t score_cutoff = 0,
166 size_t score_hint = 0)
168 return detail::Levenshtein::similarity(first1, last1, first2, last2, weights, score_cutoff, score_hint);
171template <
typename Sentence1,
typename Sentence2>
172size_t levenshtein_similarity(
const Sentence1& s1,
const Sentence2& s2,
173 LevenshteinWeightTable weights = {1, 1, 1},
size_t score_cutoff = 0,
174 size_t score_hint = 0)
176 return detail::Levenshtein::similarity(s1, s2, weights, score_cutoff, score_hint);
179template <
typename InputIt1,
typename InputIt2>
180double levenshtein_normalized_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
181 LevenshteinWeightTable weights = {1, 1, 1},
double score_cutoff = 1.0,
182 double score_hint = 1.0)
184 return detail::Levenshtein::normalized_distance(first1, last1, first2, last2, weights, score_cutoff,
188template <
typename Sentence1,
typename Sentence2>
189double levenshtein_normalized_distance(
const Sentence1& s1,
const Sentence2& s2,
190 LevenshteinWeightTable weights = {1, 1, 1},
double score_cutoff = 1.0,
191 double score_hint = 1.0)
193 return detail::Levenshtein::normalized_distance(s1, s2, weights, score_cutoff, score_hint);
255template <
typename InputIt1,
typename InputIt2>
257 LevenshteinWeightTable weights = {1, 1, 1},
258 double score_cutoff = 0.0,
double score_hint = 0.0)
260 return detail::Levenshtein::normalized_similarity(first1, last1, first2, last2, weights, score_cutoff,
264template <
typename Sentence1,
typename Sentence2>
266 LevenshteinWeightTable weights = {1, 1, 1},
267 double score_cutoff = 0.0,
double score_hint = 0.0)
269 return detail::Levenshtein::normalized_similarity(s1, s2, weights, score_cutoff, score_hint);
287template <
typename InputIt1,
typename InputIt2>
289 size_t score_hint = std::numeric_limits<size_t>::max())
291 return detail::levenshtein_editops(detail::make_range(first1, last1), detail::make_range(first2, last2),
295template <
typename Sentence1,
typename Sentence2>
297 size_t score_hint = std::numeric_limits<size_t>::max())
299 return detail::levenshtein_editops(detail::make_range(s1), detail::make_range(s2), score_hint);
303namespace experimental {
305struct MultiLevenshtein :
public detail::MultiDistanceBase<MultiLevenshtein<MaxLen>, size_t, 0,
306 std::numeric_limits<int64_t>::max()> {
308 friend detail::MultiDistanceBase<MultiLevenshtein<MaxLen>, size_t, 0,
309 std::numeric_limits<int64_t>::max()>;
310 friend detail::MultiNormalizedMetricBase<MultiLevenshtein<MaxLen>,
size_t>;
312 RAPIDFUZZ_CONSTEXPR_CXX14
static size_t get_vec_size()
314# ifdef RAPIDFUZZ_AVX2
315 using namespace detail::simd_avx2;
317 using namespace detail::simd_sse2;
319 RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 8)
320 return native_simd<uint8_t>::size;
321 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 16)
322 return native_simd<uint16_t>::size;
323 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 32)
324 return native_simd<uint32_t>::size;
325 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen <= 64)
326 return native_simd<uint64_t>::size;
328 static_assert(MaxLen <= 64, "expected MaxLen <= 64");
331 static
size_t find_block_count(
size_t count)
333 size_t vec_size = get_vec_size();
334 size_t simd_vec_count = detail::ceil_div(count, vec_size);
335 return detail::ceil_div(simd_vec_count * vec_size * MaxLen, 64);
339 MultiLevenshtein(
size_t count, LevenshteinWeightTable aWeights = {1, 1, 1})
340 : input_count(count), PM(find_block_count(count) * 64), weights(aWeights)
342 str_lens.resize(result_count());
343 if (weights.delete_cost != 1 || weights.insert_cost != 1 || weights.replace_cost > 2)
344 throw std::invalid_argument(
"unsupported weights");
356 size_t result_count()
const
358 size_t vec_size = get_vec_size();
359 size_t simd_vec_count = detail::ceil_div(input_count, vec_size);
360 return simd_vec_count * vec_size;
363 template <
typename Sentence1>
364 void insert(
const Sentence1& s1_)
366 insert(detail::to_begin(s1_), detail::to_end(s1_));
369 template <
typename InputIt1>
370 void insert(InputIt1 first1, InputIt1 last1)
372 auto len = std::distance(first1, last1);
373 int block_pos =
static_cast<int>((pos * MaxLen) % 64);
374 auto block = (pos * MaxLen) / 64;
375 assert(len <= MaxLen);
377 if (pos >= input_count)
throw std::invalid_argument(
"out of bounds insert");
379 str_lens[pos] =
static_cast<size_t>(len);
380 for (; first1 != last1; ++first1) {
381 PM.insert(block, *first1, block_pos);
388 template <
typename InputIt2>
389 void _distance(
size_t* scores,
size_t score_count,
const detail::Range<InputIt2>& s2,
390 size_t score_cutoff = std::numeric_limits<size_t>::max())
const
392 if (score_count < result_count())
393 throw std::invalid_argument(
"scores has to have >= result_count() elements");
395 auto scores_ = detail::make_range(scores, scores + score_count);
396 RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 8)
397 detail::levenshtein_hyrroe2003_simd<uint8_t>(scores_, PM, str_lens, s2, score_cutoff);
398 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 16)
399 detail::levenshtein_hyrroe2003_simd<uint16_t>(scores_, PM, str_lens, s2, score_cutoff);
400 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 32)
401 detail::levenshtein_hyrroe2003_simd<uint32_t>(scores_, PM, str_lens, s2, score_cutoff);
402 else RAPIDFUZZ_IF_CONSTEXPR (MaxLen == 64)
403 detail::levenshtein_hyrroe2003_simd<uint64_t>(scores_, PM, str_lens, s2, score_cutoff);
406 template <typename InputIt2>
407 size_t maximum(
size_t s1_idx, const detail::Range<InputIt2>& s2)
const
409 return detail::levenshtein_maximum(str_lens[s1_idx], s2.size(), weights);
412 size_t get_input_count() const noexcept
419 detail::BlockPatternMatchVector PM;
420 std::vector<size_t> str_lens;
421 LevenshteinWeightTable weights;
426template <
typename CharT1>
427struct CachedLevenshtein :
public detail::CachedDistanceBase<CachedLevenshtein<CharT1>, size_t, 0,
428 std::numeric_limits<int64_t>::max()> {
429 template <
typename Sentence1>
430 explicit CachedLevenshtein(
const Sentence1& s1_, LevenshteinWeightTable aWeights = {1, 1, 1})
431 : CachedLevenshtein(detail::to_begin(s1_), detail::to_end(s1_), aWeights)
434 template <
typename InputIt1>
435 CachedLevenshtein(InputIt1 first1, InputIt1 last1, LevenshteinWeightTable aWeights = {1, 1, 1})
436 : s1(first1, last1), PM(detail::make_range(first1, last1)), weights(aWeights)
440 friend detail::CachedDistanceBase<CachedLevenshtein<CharT1>, size_t, 0,
441 std::numeric_limits<int64_t>::max()>;
442 friend detail::CachedNormalizedMetricBase<CachedLevenshtein<CharT1>>;
444 template <
typename InputIt2>
445 size_t maximum(
const detail::Range<InputIt2>& s2)
const
447 return detail::levenshtein_maximum(s1.size(), s2.size(), weights);
450 template <
typename InputIt2>
451 size_t _distance(
const detail::Range<InputIt2>& s2,
size_t score_cutoff,
size_t score_hint)
const
453 if (weights.insert_cost == weights.delete_cost) {
455 if (weights.insert_cost == 0)
return 0;
458 if (weights.insert_cost == weights.replace_cost) {
460 size_t new_score_cutoff = detail::ceil_div(score_cutoff, weights.insert_cost);
461 size_t new_score_hint = detail::ceil_div(score_hint, weights.insert_cost);
462 size_t dist = detail::uniform_levenshtein_distance(PM, detail::make_range(s1), s2,
463 new_score_cutoff, new_score_hint);
464 dist *= weights.insert_cost;
466 return (dist <= score_cutoff) ? dist : score_cutoff + 1;
472 else if (weights.replace_cost >= weights.insert_cost + weights.delete_cost) {
474 size_t new_max = detail::ceil_div(score_cutoff, weights.insert_cost);
475 size_t dist = detail::indel_distance(PM, detail::make_range(s1), s2, new_max);
476 dist *= weights.insert_cost;
477 return (dist <= score_cutoff) ? dist : score_cutoff + 1;
481 return detail::generalized_levenshtein_distance(detail::make_range(s1), s2, weights, score_cutoff);
484 std::vector<CharT1> s1;
485 detail::BlockPatternMatchVector PM;
486 LevenshteinWeightTable weights;
489#ifdef RAPIDFUZZ_DEDUCTION_GUIDES
490template <
typename Sentence1>
491explicit CachedLevenshtein(
const Sentence1& s1_, LevenshteinWeightTable aWeights = {
492 1, 1, 1}) -> CachedLevenshtein<char_type<Sentence1>>;
494template <
typename InputIt1>
495CachedLevenshtein(InputIt1 first1, InputIt1 last1,
496 LevenshteinWeightTable aWeights = {1, 1, 1}) -> CachedLevenshtein<iter_value_t<InputIt1>>;
Editops levenshtein_editops(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, size_t score_hint=std::numeric_limits< size_t >::max())
Return list of EditOp describing how to turn s1 into s2.
Definition Levenshtein.hpp:288
size_t levenshtein_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, LevenshteinWeightTable weights={1, 1, 1}, size_t score_cutoff=std::numeric_limits< size_t >::max(), size_t score_hint=std::numeric_limits< size_t >::max())
Calculates the minimum number of insertions, deletions, and substitutions required to change one sequ...
Definition Levenshtein.hpp:146
double levenshtein_normalized_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, LevenshteinWeightTable weights={1, 1, 1}, double score_cutoff=0.0, double score_hint=0.0)
Calculates a normalized levenshtein distance using custom costs for insertion, deletion and substitut...
Definition Levenshtein.hpp:256