rapidfuzz/details/
common.rs

1use crate::{Hash, HashableChar};
2use std::iter::{Skip, Take};
3
4pub fn norm_sim_to_norm_dist(score_cutoff: f64) -> f64 {
5    let imprecision = 0.00001;
6    (1.0 - score_cutoff + imprecision).min(1.0)
7}
8
9macro_rules! impl_hashable_char {
10    ($base_type:ty, $kind:tt $(, $t:ty)*) => {
11        impl HashableChar for $base_type {
12            #[inline]
13            fn hash_char(&self) -> Hash
14            {
15                Hash::$kind(*self $(as $t)*)
16            }
17        }
18
19        impl HashableChar for &$base_type {
20            #[inline]
21            fn hash_char(&self) -> Hash
22            {
23                Hash::$kind(**self $(as $t)*)
24            }
25        }
26    }
27}
28
29impl_hashable_char!(char, UNSIGNED, u32, u64);
30impl_hashable_char!(i8, SIGNED, i64);
31impl_hashable_char!(i16, SIGNED, i64);
32impl_hashable_char!(i32, SIGNED, i64);
33impl_hashable_char!(i64, SIGNED, i64);
34impl_hashable_char!(u8, UNSIGNED, u64);
35impl_hashable_char!(u16, UNSIGNED, u64);
36impl_hashable_char!(u32, UNSIGNED, u64);
37impl_hashable_char!(u64, UNSIGNED, u64);
38
39pub fn find_common_prefix<Iter1, Iter2>(s1: Iter1, s2: Iter2) -> usize
40where
41    Iter1: Iterator + Clone,
42    Iter2: Iterator + Clone,
43    Iter1::Item: PartialEq<Iter2::Item>,
44    Iter2::Item: PartialEq<Iter1::Item>,
45{
46    s1.zip(s2)
47        .take_while(|(a_char, b_char)| a_char == b_char)
48        .count()
49}
50
51pub fn find_common_suffix<Iter1, Iter2>(s1: Iter1, s2: Iter2) -> usize
52where
53    Iter1: DoubleEndedIterator + Clone,
54    Iter2: DoubleEndedIterator + Clone,
55    Iter1::Item: PartialEq<Iter2::Item>,
56    Iter2::Item: PartialEq<Iter1::Item>,
57{
58    s1.rev()
59        .zip(s2.rev())
60        .take_while(|(a_char, b_char)| a_char == b_char)
61        .count()
62}
63
64pub struct RemovedAffix<Iter1, Iter2>
65where
66    Iter1: DoubleEndedIterator + Clone,
67    Iter2: DoubleEndedIterator + Clone,
68    Iter1::Item: PartialEq<Iter2::Item>,
69    Iter2::Item: PartialEq<Iter1::Item>,
70{
71    pub s1: Skip<Take<Iter1>>,
72    pub len1: usize,
73    pub s2: Skip<Take<Iter2>>,
74    pub len2: usize,
75    pub prefix_len: usize,
76    pub suffix_len: usize,
77}
78
79pub fn remove_common_affix<Iter1, Iter2>(
80    s1: Iter1,
81    mut len1: usize,
82    s2: Iter2,
83    mut len2: usize,
84) -> RemovedAffix<Iter1, Iter2>
85where
86    Iter1: DoubleEndedIterator + Clone,
87    Iter2: DoubleEndedIterator + Clone,
88    Iter1::Item: PartialEq<Iter2::Item> + HashableChar,
89    Iter2::Item: PartialEq<Iter1::Item> + HashableChar,
90{
91    let suffix_len = find_common_suffix(s1.clone(), s2.clone());
92    let s1_iter_no_suffix = s1.take(len1 - suffix_len);
93    let s2_iter_no_suffix = s2.take(len2 - suffix_len);
94    let prefix_len = find_common_prefix(s1_iter_no_suffix.clone(), s2_iter_no_suffix.clone());
95    let s1_iter = s1_iter_no_suffix.skip(prefix_len);
96    let s2_iter = s2_iter_no_suffix.skip(prefix_len);
97    len1 -= prefix_len + suffix_len;
98    len2 -= prefix_len + suffix_len;
99
100    RemovedAffix {
101        s1: s1_iter,
102        len1,
103        s2: s2_iter,
104        len2,
105        prefix_len,
106        suffix_len,
107    }
108}