7#include <rapidfuzz/details/PatternMatchVector.hpp>
8#include <rapidfuzz/details/Range.hpp>
9#include <rapidfuzz/details/common.hpp>
10#include <rapidfuzz/details/distance.hpp>
11#include <rapidfuzz/details/simd.hpp>
34template <
typename PM_Vec,
typename InputIt1,
typename InputIt2>
35size_t osa_hyrroe2003(
const PM_Vec& PM,
const Range<InputIt1>& s1,
const Range<InputIt2>& s2,
size_t max)
38 uint64_t VP = ~UINT64_C(0);
41 uint64_t PM_j_old = 0;
42 size_t currDist = s1.size();
43 assert(s1.size() != 0);
46 uint64_t mask = UINT64_C(1) << (s1.size() - 1);
49 for (
const auto& ch : s2) {
51 uint64_t PM_j = PM.get(0, ch);
52 uint64_t TR = (((~D0) & PM_j) << 1) & PM_j_old;
53 D0 = (((PM_j & VP) + VP) ^ VP) | PM_j | VN;
57 uint64_t HP = VN | ~(D0 | VP);
58 uint64_t HN = D0 & VP;
61 currDist += bool(HP & mask);
62 currDist -= bool(HN & mask);
73 return (currDist <= max) ? currDist : max + 1;
77template <
typename VecType,
typename InputIt,
int _lto_hack = RAPIDFUZZ_LTO_HACK>
78void osa_hyrroe2003_simd(Range<size_t*> scores,
const detail::BlockPatternMatchVector& block,
79 const std::vector<size_t>& s1_lengths,
const Range<InputIt>& s2,
80 size_t score_cutoff)
noexcept
83 using namespace simd_avx2;
85 using namespace simd_sse2;
87 static constexpr size_t alignment = native_simd<VecType>::alignment;
88 static constexpr size_t vec_width = native_simd<VecType>::size;
89 static constexpr size_t vecs = native_simd<uint64_t>::size;
90 assert(block.size() % vecs == 0);
92 native_simd<VecType> zero(VecType(0));
93 native_simd<VecType> one(1);
94 size_t result_index = 0;
96 for (
size_t cur_vec = 0; cur_vec < block.size(); cur_vec += vecs) {
98 native_simd<VecType> VP(
static_cast<VecType
>(-1));
99 native_simd<VecType> VN(VecType(0));
100 native_simd<VecType> D0(VecType(0));
101 native_simd<VecType> PM_j_old(VecType(0));
103 alignas(alignment) std::array<VecType, vec_width> currDist_;
104 unroll<size_t, vec_width>(
105 [&](
size_t i) { currDist_[i] =
static_cast<VecType
>(s1_lengths[result_index + i]); });
106 native_simd<VecType> currDist(
reinterpret_cast<uint64_t*
>(currDist_.data()));
108 alignas(alignment) std::array<VecType, vec_width> mask_;
109 unroll<size_t, vec_width>([&](
size_t i) {
110 if (s1_lengths[result_index + i] == 0)
113 mask_[i] =
static_cast<VecType
>(UINT64_C(1) << (s1_lengths[result_index + i] - 1));
115 native_simd<VecType> mask(
reinterpret_cast<uint64_t*
>(mask_.data()));
117 for (
const auto& ch : s2) {
119 alignas(alignment) std::array<uint64_t, vecs> stored;
120 unroll<size_t, vecs>([&](
size_t i) { stored[i] = block.get(cur_vec + i, ch); });
122 native_simd<VecType> PM_j(stored.data());
123 auto TR = (andnot(PM_j, D0) << 1) & PM_j_old;
124 D0 = (((PM_j & VP) + VP) ^ VP) | PM_j | VN;
128 auto HP = VN | ~(D0 | VP);
132 currDist += andnot(one, (HP & mask) == zero);
133 currDist -= andnot(one, (HN & mask) == zero);
136 HP = (HP << 1) | one;
139 VP = HN | ~(D0 | HP);
144 alignas(alignment) std::array<VecType, vec_width> distances;
145 currDist.store(distances.data());
147 unroll<size_t, vec_width>([&](
size_t i) {
150 if (s1_lengths[result_index] == 0) {
155 RAPIDFUZZ_IF_CONSTEXPR (std::numeric_limits<VecType>::max() <
156 std::numeric_limits<size_t>::max())
158 size_t min_dist = abs_diff(s1_lengths[result_index], s2.size());
159 size_t wraparound_score =
static_cast<size_t>(std::numeric_limits<VecType>::max()) + 1;
161 score = (min_dist / wraparound_score) * wraparound_score;
162 VecType remainder =
static_cast<VecType
>(min_dist % wraparound_score);
164 if (distances[i] < remainder) score += wraparound_score;
167 score += distances[i];
169 scores[result_index] = (score <= score_cutoff) ? score : score_cutoff + 1;
176template <
typename InputIt1,
typename InputIt2>
177size_t osa_hyrroe2003_block(
const BlockPatternMatchVector& PM,
const Range<InputIt1>& s1,
178 const Range<InputIt2>& s2,
size_t max = std::numeric_limits<size_t>::max())
186 Row() : VP(~UINT64_C(0)), VN(0), D0(0), PM(0)
190 size_t word_size =
sizeof(uint64_t) * 8;
191 size_t words = PM.size();
192 uint64_t Last = UINT64_C(1) << ((s1.size() - 1) % word_size);
194 size_t currDist = s1.size();
195 std::vector<Row> old_vecs(words + 1);
196 std::vector<Row> new_vecs(words + 1);
199 auto iter_s2 = s2.begin();
200 for (
size_t row = 0; row < s2.size(); ++iter_s2, ++row) {
201 uint64_t HP_carry = 1;
202 uint64_t HN_carry = 0;
204 for (
size_t word = 0; word < words; word++) {
206 uint64_t VN = old_vecs[word + 1].VN;
207 uint64_t VP = old_vecs[word + 1].VP;
208 uint64_t D0 = old_vecs[word + 1].D0;
210 uint64_t D0_last = old_vecs[word].D0;
213 uint64_t PM_j_old = old_vecs[word + 1].PM;
215 uint64_t PM_last = new_vecs[word].PM;
217 uint64_t PM_j = PM.get(word, *iter_s2);
219 uint64_t TR = ((((~D0) & X) << 1) | (((~D0_last) & PM_last) >> 63)) & PM_j_old;
222 D0 = (((X & VP) + VP) ^ VP) | X | VN | TR;
224 uint64_t HP = VN | ~(D0 | VP);
225 uint64_t HN = D0 & VP;
227 if (word == words - 1) {
228 currDist += bool(HP & Last);
229 currDist -= bool(HN & Last);
232 uint64_t HP_carry_temp = HP_carry;
234 HP = (HP << 1) | HP_carry_temp;
235 uint64_t HN_carry_temp = HN_carry;
237 HN = (HN << 1) | HN_carry_temp;
239 new_vecs[word + 1].VP = HN | ~(D0 | HP);
240 new_vecs[word + 1].VN = HP & D0;
241 new_vecs[word + 1].D0 = D0;
242 new_vecs[word + 1].PM = PM_j;
245 std::swap(new_vecs, old_vecs);
248 return (currDist <= max) ? currDist : max + 1;
251class OSA :
public DistanceBase<OSA, size_t, 0, std::numeric_limits<int64_t>::max()> {
252 friend DistanceBase<OSA, size_t, 0, std::numeric_limits<int64_t>::max()>;
253 friend NormalizedMetricBase<OSA>;
255 template <
typename InputIt1,
typename InputIt2>
256 static size_t maximum(
const Range<InputIt1>& s1,
const Range<InputIt2>& s2)
258 return std::max(s1.size(), s2.size());
261 template <
typename InputIt1,
typename InputIt2>
262 static size_t _distance(Range<InputIt1> s1, Range<InputIt2> s2,
size_t score_cutoff,
size_t score_hint)
264 if (s2.size() < s1.size())
return _distance(s2, s1, score_cutoff, score_hint);
266 remove_common_affix(s1, s2);
268 return (s2.size() <= score_cutoff) ? s2.size() : score_cutoff + 1;
269 else if (s1.size() < 64)
270 return osa_hyrroe2003(PatternMatchVector(s1), s1, s2, score_cutoff);
272 return osa_hyrroe2003_block(BlockPatternMatchVector(s1), s1, s2, score_cutoff);