RapidFuzz
Loading...
Searching...
No Matches
Jaro.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright © 2022-present Max Bachmann */
3
4#pragma once
5
6#include <rapidfuzz/details/Range.hpp>
7#include <rapidfuzz/distance/Jaro_impl.hpp>
8#include <stdlib.h>
9
10namespace rapidfuzz {
11
17template <typename InputIt1, typename InputIt2>
18double jaro_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
19 double score_cutoff = 1.0)
20{
21 return detail::Jaro::distance(first1, last1, first2, last2, score_cutoff, score_cutoff);
22}
23
24template <typename Sentence1, typename Sentence2>
25double jaro_distance(const Sentence1& s1, const Sentence2& s2, double score_cutoff = 1.0)
26{
27 return detail::Jaro::distance(s1, s2, score_cutoff, score_cutoff);
28}
29
30template <typename InputIt1, typename InputIt2>
31double jaro_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
32 double score_cutoff = 0.0)
33{
34 return detail::Jaro::similarity(first1, last1, first2, last2, score_cutoff, score_cutoff);
35}
36
37template <typename Sentence1, typename Sentence2>
38double jaro_similarity(const Sentence1& s1, const Sentence2& s2, double score_cutoff = 0.0)
39{
40 return detail::Jaro::similarity(s1, s2, score_cutoff, score_cutoff);
41}
42
43template <typename InputIt1, typename InputIt2>
44double jaro_normalized_distance(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
45 double score_cutoff = 1.0)
46{
47 return detail::Jaro::normalized_distance(first1, last1, first2, last2, score_cutoff, score_cutoff);
48}
49
50template <typename Sentence1, typename Sentence2>
51double jaro_normalized_distance(const Sentence1& s1, const Sentence2& s2, double score_cutoff = 1.0)
52{
53 return detail::Jaro::normalized_distance(s1, s2, score_cutoff, score_cutoff);
54}
55
56template <typename InputIt1, typename InputIt2>
57double jaro_normalized_similarity(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2,
58 double score_cutoff = 0.0)
59{
60 return detail::Jaro::normalized_similarity(first1, last1, first2, last2, score_cutoff, score_cutoff);
61}
62
63template <typename Sentence1, typename Sentence2>
64double jaro_normalized_similarity(const Sentence1& s1, const Sentence2& s2, double score_cutoff = 0.0)
65{
66 return detail::Jaro::normalized_similarity(s1, s2, score_cutoff, score_cutoff);
67}
68
69#ifdef RAPIDFUZZ_SIMD
70namespace experimental {
71template <int MaxLen>
72struct MultiJaro : public detail::MultiSimilarityBase<MultiJaro<MaxLen>, double, 0, 1> {
73
74private:
75 friend detail::MultiSimilarityBase<MultiJaro<MaxLen>, double, 0, 1>;
76 friend detail::MultiNormalizedMetricBase<MultiJaro<MaxLen>, double>;
77
78 static_assert(MaxLen == 8 || MaxLen == 16 || MaxLen == 32 || MaxLen == 64, "incorrect MaxLen used");
79
80 using VecType = typename std::conditional<
81 MaxLen == 8, uint8_t,
82 typename std::conditional<MaxLen == 16, uint16_t,
83 typename std::conditional<MaxLen == 32, uint32_t, uint64_t>::type>::type>::
84 type;
85
86 constexpr static size_t get_vec_size()
87 {
88# ifdef RAPIDFUZZ_AVX2
89 return detail::simd_avx2::native_simd<VecType>::size;
90# else
91 return detail::simd_sse2::native_simd<VecType>::size;
92# endif
93 }
94
95 constexpr static size_t get_vec_alignment()
96 {
97# ifdef RAPIDFUZZ_AVX2
98 return detail::simd_avx2::native_simd<VecType>::alignment;
99# else
100 return detail::simd_sse2::native_simd<VecType>::alignment;
101# endif
102 }
103
104 static size_t find_block_count(size_t count)
105 {
106 size_t vec_size = get_vec_size();
107 size_t simd_vec_count = detail::ceil_div(count, vec_size);
108 return detail::ceil_div(simd_vec_count * vec_size * MaxLen, 64);
109 }
110
111public:
112 MultiJaro(size_t count) : input_count(count), PM(find_block_count(count) * 64)
113 {
114 /* align for avx2 so we can directly load into avx2 registers */
115 str_lens_size = result_count();
116
117 str_lens = static_cast<VecType*>(
118 detail::rf_aligned_alloc(get_vec_alignment(), sizeof(VecType) * str_lens_size));
119 std::fill(str_lens, str_lens + str_lens_size, VecType(0));
120 }
121
122 ~MultiJaro()
123 {
124 detail::rf_aligned_free(str_lens);
125 }
126
136 size_t result_count() const
137 {
138 size_t vec_size = get_vec_size();
139 size_t simd_vec_count = detail::ceil_div(input_count, vec_size);
140 return simd_vec_count * vec_size;
141 }
142
143 template <typename Sentence1>
144 void insert(const Sentence1& s1_)
145 {
146 insert(detail::to_begin(s1_), detail::to_end(s1_));
147 }
148
149 template <typename InputIt1>
150 void insert(InputIt1 first1, InputIt1 last1)
151 {
152 auto len = std::distance(first1, last1);
153 int block_pos = static_cast<int>((pos * MaxLen) % 64);
154 auto block = (pos * MaxLen) / 64;
155 assert(len <= MaxLen);
156
157 if (pos >= input_count) throw std::invalid_argument("out of bounds insert");
158
159 str_lens[pos] = static_cast<VecType>(len);
160 for (; first1 != last1; ++first1) {
161 PM.insert(block, *first1, block_pos);
162 block_pos++;
163 }
164 pos++;
165 }
166
167private:
168 template <typename InputIt2>
169 void _similarity(double* scores, size_t score_count, const detail::Range<InputIt2>& s2,
170 double score_cutoff = 0.0) const
171 {
172 if (score_count < result_count())
173 throw std::invalid_argument("scores has to have >= result_count() elements");
174
175 auto scores_ = detail::make_range(scores, scores + score_count);
176 detail::jaro_similarity_simd<VecType>(scores_, PM, str_lens, str_lens_size, s2, score_cutoff);
177 }
178
179 template <typename InputIt2>
180 double maximum(size_t, const detail::Range<InputIt2>&) const
181 {
182 return 1.0;
183 }
184
185 size_t get_input_count() const noexcept
186 {
187 return input_count;
188 }
189
190 size_t input_count;
191 size_t pos = 0;
192 detail::BlockPatternMatchVector PM;
193 VecType* str_lens;
194 size_t str_lens_size;
195};
196
197} /* namespace experimental */
198#endif /* RAPIDFUZZ_SIMD */
199
200template <typename CharT1>
201struct CachedJaro : public detail::CachedSimilarityBase<CachedJaro<CharT1>, double, 0, 1> {
202 template <typename Sentence1>
203 explicit CachedJaro(const Sentence1& s1_) : CachedJaro(detail::to_begin(s1_), detail::to_end(s1_))
204 {}
205
206 template <typename InputIt1>
207 CachedJaro(InputIt1 first1, InputIt1 last1) : s1(first1, last1), PM(detail::make_range(first1, last1))
208 {}
209
210private:
211 friend detail::CachedSimilarityBase<CachedJaro<CharT1>, double, 0, 1>;
212 friend detail::CachedNormalizedMetricBase<CachedJaro<CharT1>>;
213
214 template <typename InputIt2>
215 double maximum(const detail::Range<InputIt2>&) const
216 {
217 return 1.0;
218 }
219
220 template <typename InputIt2>
221 double _similarity(const detail::Range<InputIt2>& s2, double score_cutoff, double) const
222 {
223 return detail::jaro_similarity(PM, detail::make_range(s1), s2, score_cutoff);
224 }
225
226 std::vector<CharT1> s1;
227 detail::BlockPatternMatchVector PM;
228};
229
230#ifdef RAPIDFUZZ_DEDUCTION_GUIDES
231template <typename Sentence1>
232explicit CachedJaro(const Sentence1& s1_) -> CachedJaro<char_type<Sentence1>>;
233
234template <typename InputIt1>
235CachedJaro(InputIt1 first1, InputIt1 last1) -> CachedJaro<iter_value_t<InputIt1>>;
236#endif
237
240} // namespace rapidfuzz