RapidFuzz
Loading...
Searching...
No Matches
distance.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright © 2022 Max Bachmann */
3
4#pragma once
5
6#include <cmath>
7#include <rapidfuzz/details/Range.hpp>
8#include <rapidfuzz/details/common.hpp>
9#include <rapidfuzz/details/simd.hpp>
10#include <type_traits>
11
12namespace rapidfuzz {
13namespace detail {
14
15template <typename T, typename... Args>
16struct NormalizedMetricBase {
17 template <typename InputIt1, typename InputIt2,
18 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
19 static double normalized_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
20 Args... args, double score_cutoff, double score_hint)
21 {
22 return _normalized_distance(make_range(first1, last1), make_range(first2, last2),
23 std::forward<Args>(args)..., score_cutoff, score_hint);
24 }
25
26 template <typename Sentence1, typename Sentence2>
27 static double normalized_distance(const Sentence1& s1, const Sentence2& s2, Args... args,
28 double score_cutoff, double score_hint)
29 {
30 return _normalized_distance(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
31 score_hint);
32 }
33
34 template <typename InputIt1, typename InputIt2,
35 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
36 static double normalized_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
37 Args... args, double score_cutoff, double score_hint)
38 {
39 return _normalized_similarity(make_range(first1, last1), make_range(first2, last2),
40 std::forward<Args>(args)..., score_cutoff, score_hint);
41 }
42
43 template <typename Sentence1, typename Sentence2>
44 static double normalized_similarity(const Sentence1& s1, const Sentence2& s2, Args... args,
45 double score_cutoff, double score_hint)
46 {
47 return _normalized_similarity(make_range(s1), make_range(s2), std::forward<Args>(args)...,
48 score_cutoff, score_hint);
49 }
50
51protected:
52 template <typename InputIt1, typename InputIt2>
53 static double _normalized_distance(const Range<InputIt1>& s1, const Range<InputIt2>& s2, Args... args,
54 double score_cutoff, double score_hint)
55 {
56 auto maximum = T::maximum(s1, s2, args...);
57 auto cutoff_distance =
58 static_cast<decltype(maximum)>(std::ceil(static_cast<double>(maximum) * score_cutoff));
59 auto hint_distance =
60 static_cast<decltype(maximum)>(std::ceil(static_cast<double>(maximum) * score_hint));
61 auto dist = T::_distance(s1, s2, std::forward<Args>(args)..., cutoff_distance, hint_distance);
62 double norm_dist = (maximum != 0) ? static_cast<double>(dist) / static_cast<double>(maximum) : 0.0;
63 return (norm_dist <= score_cutoff) ? norm_dist : 1.0;
64 }
65
66 template <typename InputIt1, typename InputIt2>
67 static double _normalized_similarity(const Range<InputIt1>& s1, const Range<InputIt2>& s2, Args... args,
68 double score_cutoff, double score_hint)
69 {
70 double cutoff_score = NormSim_to_NormDist(score_cutoff);
71 double hint_score = NormSim_to_NormDist(score_hint);
72 double norm_dist =
73 _normalized_distance(s1, s2, std::forward<Args>(args)..., cutoff_score, hint_score);
74 double norm_sim = 1.0 - norm_dist;
75 return (norm_sim >= score_cutoff) ? norm_sim : 0.0;
76 }
77
78 NormalizedMetricBase()
79 {}
80 friend T;
81};
82
83template <typename T, typename ResType, int64_t WorstSimilarity, int64_t WorstDistance, typename... Args>
84struct DistanceBase : public NormalizedMetricBase<T, Args...> {
85 template <typename InputIt1, typename InputIt2,
86 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
87 static ResType distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
88 ResType score_cutoff, ResType score_hint)
89 {
90 return T::_distance(make_range(first1, last1), make_range(first2, last2), std::forward<Args>(args)...,
91 score_cutoff, score_hint);
92 }
93
94 template <typename Sentence1, typename Sentence2>
95 static ResType distance(const Sentence1& s1, const Sentence2& s2, Args... args, ResType score_cutoff,
96 ResType score_hint)
97 {
98 return T::_distance(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
99 score_hint);
100 }
101
102 template <typename InputIt1, typename InputIt2,
103 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
104 static ResType similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
105 ResType score_cutoff, ResType score_hint)
106 {
107 return _similarity(make_range(first1, last1), make_range(first2, last2), std::forward<Args>(args)...,
108 score_cutoff, score_hint);
109 }
110
111 template <typename Sentence1, typename Sentence2>
112 static ResType similarity(const Sentence1& s1, const Sentence2& s2, Args... args, ResType score_cutoff,
113 ResType score_hint)
114 {
115 return _similarity(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
116 score_hint);
117 }
118
119protected:
120 template <typename InputIt1, typename InputIt2>
121 static ResType _similarity(Range<InputIt1> s1, Range<InputIt2> s2, Args... args, ResType score_cutoff,
122 ResType score_hint)
123 {
124 auto maximum = T::maximum(s1, s2, args...);
125 if (score_cutoff > maximum) return 0;
126
127 score_hint = std::min(score_cutoff, score_hint);
128 ResType cutoff_distance = maximum - score_cutoff;
129 ResType hint_distance = maximum - score_hint;
130 ResType dist = T::_distance(s1, s2, std::forward<Args>(args)..., cutoff_distance, hint_distance);
131 ResType sim = maximum - dist;
132 return (sim >= score_cutoff) ? sim : 0;
133 }
134
135 DistanceBase()
136 {}
137 friend T;
138};
139
140template <typename T, typename ResType, int64_t WorstSimilarity, int64_t WorstDistance, typename... Args>
141struct SimilarityBase : public NormalizedMetricBase<T, Args...> {
142 template <typename InputIt1, typename InputIt2,
143 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
144 static ResType distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
145 ResType score_cutoff, ResType score_hint)
146 {
147 return _distance(make_range(first1, last1), make_range(first2, last2), std::forward<Args>(args)...,
148 score_cutoff, score_hint);
149 }
150
151 template <typename Sentence1, typename Sentence2>
152 static ResType distance(const Sentence1& s1, const Sentence2& s2, Args... args, ResType score_cutoff,
153 ResType score_hint)
154 {
155 return _distance(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
156 score_hint);
157 }
158
159 template <typename InputIt1, typename InputIt2,
160 typename = rapidfuzz::rf_enable_if_t<!std::is_same<InputIt2, double>::value>>
161 static ResType similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Args... args,
162 ResType score_cutoff, ResType score_hint)
163 {
164 return T::_similarity(make_range(first1, last1), make_range(first2, last2),
165 std::forward<Args>(args)..., score_cutoff, score_hint);
166 }
167
168 template <typename Sentence1, typename Sentence2>
169 static ResType similarity(const Sentence1& s1, const Sentence2& s2, Args... args, ResType score_cutoff,
170 ResType score_hint)
171 {
172 return T::_similarity(make_range(s1), make_range(s2), std::forward<Args>(args)..., score_cutoff,
173 score_hint);
174 }
175
176protected:
177 template <typename InputIt1, typename InputIt2>
178 static ResType _distance(const Range<InputIt1>& s1, const Range<InputIt2>& s2, Args... args,
179 ResType score_cutoff, ResType score_hint)
180 {
181 auto maximum = T::maximum(s1, s2, args...);
182 ResType cutoff_similarity =
183 (maximum >= score_cutoff) ? maximum - score_cutoff : static_cast<ResType>(WorstSimilarity);
184 ResType hint_similarity =
185 (maximum >= score_hint) ? maximum - score_hint : static_cast<ResType>(WorstSimilarity);
186 ResType sim = T::_similarity(s1, s2, std::forward<Args>(args)..., cutoff_similarity, hint_similarity);
187 ResType dist = maximum - sim;
188 return _apply_distance_score_cutoff(dist, score_cutoff);
189 }
190
191 template <typename U>
192 static rapidfuzz::rf_enable_if_t<std::is_floating_point<U>::value, U>
193 _apply_distance_score_cutoff(U score, U score_cutoff)
194 {
195 return (score <= score_cutoff) ? score : 1.0;
196 }
197
198 template <typename U>
199 static rapidfuzz::rf_enable_if_t<!std::is_floating_point<U>::value, U>
200 _apply_distance_score_cutoff(U score, U score_cutoff)
201 {
202 return (score <= score_cutoff) ? score : score_cutoff + 1;
203 }
204
205 SimilarityBase()
206 {}
207 friend T;
208};
209
210template <typename T>
211struct CachedNormalizedMetricBase {
212 template <typename InputIt2>
213 double normalized_distance(InputIt2 first2, InputIt2 last2, double score_cutoff = 1.0,
214 double score_hint = 1.0) const
215 {
216 return _normalized_distance(make_range(first2, last2), score_cutoff, score_hint);
217 }
218
219 template <typename Sentence2>
220 double normalized_distance(const Sentence2& s2, double score_cutoff = 1.0, double score_hint = 1.0) const
221 {
222 return _normalized_distance(make_range(s2), score_cutoff, score_hint);
223 }
224
225 template <typename InputIt2>
226 double normalized_similarity(InputIt2 first2, InputIt2 last2, double score_cutoff = 0.0,
227 double score_hint = 0.0) const
228 {
229 return _normalized_similarity(make_range(first2, last2), score_cutoff, score_hint);
230 }
231
232 template <typename Sentence2>
233 double normalized_similarity(const Sentence2& s2, double score_cutoff = 0.0,
234 double score_hint = 0.0) const
235 {
236 return _normalized_similarity(make_range(s2), score_cutoff, score_hint);
237 }
238
239protected:
240 template <typename InputIt2>
241 double _normalized_distance(const Range<InputIt2>& s2, double score_cutoff, double score_hint) const
242 {
243 const T& derived = static_cast<const T&>(*this);
244 auto maximum = derived.maximum(s2);
245 auto cutoff_distance =
246 static_cast<decltype(maximum)>(std::ceil(static_cast<double>(maximum) * score_cutoff));
247 auto hint_distance =
248 static_cast<decltype(maximum)>(std::ceil(static_cast<double>(maximum) * score_hint));
249 double dist = static_cast<double>(derived._distance(s2, cutoff_distance, hint_distance));
250 double norm_dist = (maximum != 0) ? dist / static_cast<double>(maximum) : 0.0;
251 return (norm_dist <= score_cutoff) ? norm_dist : 1.0;
252 }
253
254 template <typename InputIt2>
255 double _normalized_similarity(const Range<InputIt2>& s2, double score_cutoff, double score_hint) const
256 {
257 double cutoff_score = NormSim_to_NormDist(score_cutoff);
258 double hint_score = NormSim_to_NormDist(score_hint);
259 double norm_dist = _normalized_distance(s2, cutoff_score, hint_score);
260 double norm_sim = 1.0 - norm_dist;
261 return (norm_sim >= score_cutoff) ? norm_sim : 0.0;
262 }
263
264 CachedNormalizedMetricBase()
265 {}
266 friend T;
267};
268
269template <typename T, typename ResType, int64_t WorstSimilarity, int64_t WorstDistance>
270struct CachedDistanceBase : public CachedNormalizedMetricBase<T> {
271 template <typename InputIt2>
272 ResType distance(InputIt2 first2, InputIt2 last2,
273 ResType score_cutoff = static_cast<ResType>(WorstDistance),
274 ResType score_hint = static_cast<ResType>(WorstDistance)) const
275 {
276 const T& derived = static_cast<const T&>(*this);
277 return derived._distance(make_range(first2, last2), score_cutoff, score_hint);
278 }
279
280 template <typename Sentence2>
281 ResType distance(const Sentence2& s2, ResType score_cutoff = static_cast<ResType>(WorstDistance),
282 ResType score_hint = static_cast<ResType>(WorstDistance)) const
283 {
284 const T& derived = static_cast<const T&>(*this);
285 return derived._distance(make_range(s2), score_cutoff, score_hint);
286 }
287
288 template <typename InputIt2>
289 ResType similarity(InputIt2 first2, InputIt2 last2,
290 ResType score_cutoff = static_cast<ResType>(WorstSimilarity),
291 ResType score_hint = static_cast<ResType>(WorstSimilarity)) const
292 {
293 return _similarity(make_range(first2, last2), score_cutoff, score_hint);
294 }
295
296 template <typename Sentence2>
297 ResType similarity(const Sentence2& s2, ResType score_cutoff = static_cast<ResType>(WorstSimilarity),
298 ResType score_hint = static_cast<ResType>(WorstSimilarity)) const
299 {
300 return _similarity(make_range(s2), score_cutoff, score_hint);
301 }
302
303protected:
304 template <typename InputIt2>
305 ResType _similarity(const Range<InputIt2>& s2, ResType score_cutoff, ResType score_hint) const
306 {
307 const T& derived = static_cast<const T&>(*this);
308 ResType maximum = derived.maximum(s2);
309 if (score_cutoff > maximum) return 0;
310
311 score_hint = std::min(score_cutoff, score_hint);
312 ResType cutoff_distance = maximum - score_cutoff;
313 ResType hint_distance = maximum - score_hint;
314 ResType dist = derived._distance(s2, cutoff_distance, hint_distance);
315 ResType sim = maximum - dist;
316 return (sim >= score_cutoff) ? sim : 0;
317 }
318
319 CachedDistanceBase()
320 {}
321 friend T;
322};
323
324template <typename T, typename ResType, int64_t WorstSimilarity, int64_t WorstDistance>
325struct CachedSimilarityBase : public CachedNormalizedMetricBase<T> {
326 template <typename InputIt2>
327 ResType distance(InputIt2 first2, InputIt2 last2,
328 ResType score_cutoff = static_cast<ResType>(WorstDistance),
329 ResType score_hint = static_cast<ResType>(WorstDistance)) const
330 {
331 return _distance(make_range(first2, last2), score_cutoff, score_hint);
332 }
333
334 template <typename Sentence2>
335 ResType distance(const Sentence2& s2, ResType score_cutoff = static_cast<ResType>(WorstDistance),
336 ResType score_hint = static_cast<ResType>(WorstDistance)) const
337 {
338 return _distance(make_range(s2), score_cutoff, score_hint);
339 }
340
341 template <typename InputIt2>
342 ResType similarity(InputIt2 first2, InputIt2 last2,
343 ResType score_cutoff = static_cast<ResType>(WorstSimilarity),
344 ResType score_hint = static_cast<ResType>(WorstSimilarity)) const
345 {
346 const T& derived = static_cast<const T&>(*this);
347 return derived._similarity(make_range(first2, last2), score_cutoff, score_hint);
348 }
349
350 template <typename Sentence2>
351 ResType similarity(const Sentence2& s2, ResType score_cutoff = static_cast<ResType>(WorstSimilarity),
352 ResType score_hint = static_cast<ResType>(WorstSimilarity)) const
353 {
354 const T& derived = static_cast<const T&>(*this);
355 return derived._similarity(make_range(s2), score_cutoff, score_hint);
356 }
357
358protected:
359 template <typename InputIt2>
360 ResType _distance(const Range<InputIt2>& s2, ResType score_cutoff, ResType score_hint) const
361 {
362 const T& derived = static_cast<const T&>(*this);
363 ResType maximum = derived.maximum(s2);
364 ResType cutoff_similarity = (maximum > score_cutoff) ? maximum - score_cutoff : 0;
365 ResType hint_similarity = (maximum > score_hint) ? maximum - score_hint : 0;
366 ResType sim = derived._similarity(s2, cutoff_similarity, hint_similarity);
367 ResType dist = maximum - sim;
368 return _apply_distance_score_cutoff(dist, score_cutoff);
369 }
370
371 template <typename U>
372 static rapidfuzz::rf_enable_if_t<std::is_floating_point<U>::value, U>
373 _apply_distance_score_cutoff(U score, U score_cutoff)
374 {
375 return (score <= score_cutoff) ? score : 1.0;
376 }
377
378 template <typename U>
379 static rapidfuzz::rf_enable_if_t<!std::is_floating_point<U>::value, U>
380 _apply_distance_score_cutoff(U score, U score_cutoff)
381 {
382 return (score <= score_cutoff) ? score : score_cutoff + 1;
383 }
384
385 CachedSimilarityBase()
386 {}
387 friend T;
388};
389
390template <typename T, typename ResType>
391struct MultiNormalizedMetricBase {
392 template <typename InputIt2>
393 void normalized_distance(double* scores, size_t score_count, InputIt2 first2, InputIt2 last2,
394 double score_cutoff = 1.0) const
395 {
396 _normalized_distance(scores, score_count, make_range(first2, last2), score_cutoff);
397 }
398
399 template <typename Sentence2>
400 void normalized_distance(double* scores, size_t score_count, const Sentence2& s2,
401 double score_cutoff = 1.0) const
402 {
403 _normalized_distance(scores, score_count, make_range(s2), score_cutoff);
404 }
405
406 template <typename InputIt2>
407 void normalized_similarity(double* scores, size_t score_count, InputIt2 first2, InputIt2 last2,
408 double score_cutoff = 0.0) const
409 {
410 _normalized_similarity(scores, score_count, make_range(first2, last2), score_cutoff);
411 }
412
413 template <typename Sentence2>
414 void normalized_similarity(double* scores, size_t score_count, const Sentence2& s2,
415 double score_cutoff = 0.0) const
416 {
417 _normalized_similarity(scores, score_count, make_range(s2), score_cutoff);
418 }
419
420protected:
421 template <typename InputIt2>
422 void _normalized_distance(double* scores, size_t score_count, const Range<InputIt2>& s2,
423 double score_cutoff = 1.0) const
424 {
425 const T& derived = static_cast<const T&>(*this);
426 if (score_count < derived.result_count())
427 throw std::invalid_argument("scores has to have >= result_count() elements");
428
429 // reinterpretation only works when the types have the same size
430 ResType* scores_orig = nullptr;
431
432 RAPIDFUZZ_IF_CONSTEXPR (sizeof(double) == sizeof(ResType))
433 scores_orig = reinterpret_cast<ResType*>(scores);
434 else
435 scores_orig = new ResType[derived.result_count()];
436
437 derived.distance(scores_orig, derived.result_count(), s2);
438
439 for (size_t i = 0; i < derived.get_input_count(); ++i) {
440 auto maximum = derived.maximum(i, s2);
441 double norm_dist =
442 (maximum != 0) ? static_cast<double>(scores_orig[i]) / static_cast<double>(maximum) : 0.0;
443 scores[i] = (norm_dist <= score_cutoff) ? norm_dist : 1.0;
444 }
445
446 RAPIDFUZZ_IF_CONSTEXPR (sizeof(double) != sizeof(ResType)) delete[] scores_orig;
447 }
448
449 template <typename InputIt2>
450 void _normalized_similarity(double* scores, size_t score_count, const Range<InputIt2>& s2,
451 double score_cutoff) const
452 {
453 const T& derived = static_cast<const T&>(*this);
454 _normalized_distance(scores, score_count, s2);
455
456 for (size_t i = 0; i < derived.get_input_count(); ++i) {
457 double norm_sim = 1.0 - scores[i];
458 scores[i] = (norm_sim >= score_cutoff) ? norm_sim : 0.0;
459 }
460 }
461
462 MultiNormalizedMetricBase()
463 {}
464 friend T;
465};
466
467template <typename T, typename ResType, int64_t WorstSimilarity, int64_t WorstDistance>
468struct MultiDistanceBase : public MultiNormalizedMetricBase<T, ResType> {
469 template <typename InputIt2>
470 void distance(ResType* scores, size_t score_count, InputIt2 first2, InputIt2 last2,
471 ResType score_cutoff = static_cast<ResType>(WorstDistance)) const
472 {
473 const T& derived = static_cast<const T&>(*this);
474 derived._distance(scores, score_count, make_range(first2, last2), score_cutoff);
475 }
476
477 template <typename Sentence2>
478 void distance(ResType* scores, size_t score_count, const Sentence2& s2,
479 ResType score_cutoff = static_cast<ResType>(WorstDistance)) const
480 {
481 const T& derived = static_cast<const T&>(*this);
482 derived._distance(scores, score_count, make_range(s2), score_cutoff);
483 }
484
485 template <typename InputIt2>
486 void similarity(ResType* scores, size_t score_count, InputIt2 first2, InputIt2 last2,
487 ResType score_cutoff = static_cast<ResType>(WorstSimilarity)) const
488 {
489 _similarity(scores, score_count, make_range(first2, last2), score_cutoff);
490 }
491
492 template <typename Sentence2>
493 void similarity(ResType* scores, size_t score_count, const Sentence2& s2,
494 ResType score_cutoff = static_cast<ResType>(WorstSimilarity)) const
495 {
496 _similarity(scores, score_count, make_range(s2), score_cutoff);
497 }
498
499protected:
500 template <typename InputIt2>
501 void _similarity(ResType* scores, size_t score_count, const Range<InputIt2>& s2,
502 ResType score_cutoff) const
503 {
504 const T& derived = static_cast<const T&>(*this);
505 derived._distance(scores, score_count, s2);
506
507 for (size_t i = 0; i < derived.get_input_count(); ++i) {
508 ResType maximum = derived.maximum(i, s2);
509 ResType sim = maximum - scores[i];
510 scores[i] = (sim >= score_cutoff) ? sim : 0;
511 }
512 }
513
514 MultiDistanceBase()
515 {}
516 friend T;
517};
518
519template <typename T, typename ResType, int64_t WorstSimilarity, int64_t WorstDistance>
520struct MultiSimilarityBase : public MultiNormalizedMetricBase<T, ResType> {
521 template <typename InputIt2>
522 void distance(ResType* scores, size_t score_count, InputIt2 first2, InputIt2 last2,
523 ResType score_cutoff = static_cast<ResType>(WorstDistance)) const
524 {
525 _distance(scores, score_count, make_range(first2, last2), score_cutoff);
526 }
527
528 template <typename Sentence2>
529 void distance(ResType* scores, size_t score_count, const Sentence2& s2,
530 ResType score_cutoff = static_cast<ResType>(WorstDistance)) const
531 {
532 _distance(scores, score_count, make_range(s2), score_cutoff);
533 }
534
535 template <typename InputIt2>
536 void similarity(ResType* scores, size_t score_count, InputIt2 first2, InputIt2 last2,
537 ResType score_cutoff = static_cast<ResType>(WorstSimilarity)) const
538 {
539 const T& derived = static_cast<const T&>(*this);
540 derived._similarity(scores, score_count, make_range(first2, last2), score_cutoff);
541 }
542
543 template <typename Sentence2>
544 void similarity(ResType* scores, size_t score_count, const Sentence2& s2,
545 ResType score_cutoff = static_cast<ResType>(WorstSimilarity)) const
546 {
547 const T& derived = static_cast<const T&>(*this);
548 derived._similarity(scores, score_count, make_range(s2), score_cutoff);
549 }
550
551protected:
552 template <typename InputIt2>
553 void _distance(ResType* scores, size_t score_count, const Range<InputIt2>& s2, ResType score_cutoff) const
554 {
555 const T& derived = static_cast<const T&>(*this);
556 derived._similarity(scores, score_count, s2);
557
558 for (size_t i = 0; i < derived.get_input_count(); ++i) {
559 ResType maximum = derived.maximum(i, s2);
560 ResType dist = maximum - scores[i];
561 scores[i] = _apply_distance_score_cutoff(dist, score_cutoff);
562 }
563 }
564
565 template <typename U>
566 static rapidfuzz::rf_enable_if_t<std::is_floating_point<U>::value, U>
567 _apply_distance_score_cutoff(U score, U score_cutoff)
568 {
569 return (score <= score_cutoff) ? score : 1.0;
570 }
571
572 template <typename U>
573 static rapidfuzz::rf_enable_if_t<!std::is_floating_point<U>::value, U>
574 _apply_distance_score_cutoff(U score, U score_cutoff)
575 {
576 return (score <= score_cutoff) ? score : score_cutoff + 1;
577 }
578
579 MultiSimilarityBase()
580 {}
581 friend T;
582};
583
584} // namespace detail
585} // namespace rapidfuzz