stride-align 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/NOTICE +255 -0
- data/README.md +107 -0
- data/data/bmpm_data/gen_approx_any.txt +131 -0
- data/data/bmpm_data/gen_approx_arabic.txt +26 -0
- data/data/bmpm_data/gen_approx_common.txt +233 -0
- data/data/bmpm_data/gen_approx_cyrillic.txt +18 -0
- data/data/bmpm_data/gen_approx_czech.txt +18 -0
- data/data/bmpm_data/gen_approx_dutch.txt +18 -0
- data/data/bmpm_data/gen_approx_english.txt +47 -0
- data/data/bmpm_data/gen_approx_french.txt +25 -0
- data/data/bmpm_data/gen_approx_german.txt +73 -0
- data/data/bmpm_data/gen_approx_greek.txt +18 -0
- data/data/bmpm_data/gen_approx_greeklatin.txt +20 -0
- data/data/bmpm_data/gen_approx_hebrew.txt +18 -0
- data/data/bmpm_data/gen_approx_hungarian.txt +18 -0
- data/data/bmpm_data/gen_approx_italian.txt +18 -0
- data/data/bmpm_data/gen_approx_polish.txt +84 -0
- data/data/bmpm_data/gen_approx_portuguese.txt +18 -0
- data/data/bmpm_data/gen_approx_romanian.txt +18 -0
- data/data/bmpm_data/gen_approx_russian.txt +48 -0
- data/data/bmpm_data/gen_approx_spanish.txt +21 -0
- data/data/bmpm_data/gen_approx_turkish.txt +18 -0
- data/data/bmpm_data/gen_exact_any.txt +40 -0
- data/data/bmpm_data/gen_exact_approx_common.txt +79 -0
- data/data/bmpm_data/gen_exact_arabic.txt +18 -0
- data/data/bmpm_data/gen_exact_common.txt +32 -0
- data/data/bmpm_data/gen_exact_cyrillic.txt +18 -0
- data/data/bmpm_data/gen_exact_czech.txt +18 -0
- data/data/bmpm_data/gen_exact_dutch.txt +18 -0
- data/data/bmpm_data/gen_exact_english.txt +18 -0
- data/data/bmpm_data/gen_exact_french.txt +18 -0
- data/data/bmpm_data/gen_exact_german.txt +18 -0
- data/data/bmpm_data/gen_exact_greek.txt +18 -0
- data/data/bmpm_data/gen_exact_greeklatin.txt +18 -0
- data/data/bmpm_data/gen_exact_hebrew.txt +18 -0
- data/data/bmpm_data/gen_exact_hungarian.txt +18 -0
- data/data/bmpm_data/gen_exact_italian.txt +18 -0
- data/data/bmpm_data/gen_exact_polish.txt +23 -0
- data/data/bmpm_data/gen_exact_portuguese.txt +18 -0
- data/data/bmpm_data/gen_exact_romanian.txt +18 -0
- data/data/bmpm_data/gen_exact_russian.txt +19 -0
- data/data/bmpm_data/gen_exact_spanish.txt +19 -0
- data/data/bmpm_data/gen_exact_turkish.txt +18 -0
- data/data/bmpm_data/gen_hebrew_common.txt +113 -0
- data/data/bmpm_data/gen_lang.txt +295 -0
- data/data/bmpm_data/gen_languages.txt +36 -0
- data/data/bmpm_data/gen_rules_any.txt +367 -0
- data/data/bmpm_data/gen_rules_arabic.txt +76 -0
- data/data/bmpm_data/gen_rules_cyrillic.txt +99 -0
- data/data/bmpm_data/gen_rules_czech.txt +67 -0
- data/data/bmpm_data/gen_rules_dutch.txt +78 -0
- data/data/bmpm_data/gen_rules_english.txt +113 -0
- data/data/bmpm_data/gen_rules_french.txt +114 -0
- data/data/bmpm_data/gen_rules_german.txt +129 -0
- data/data/bmpm_data/gen_rules_greek.txt +97 -0
- data/data/bmpm_data/gen_rules_greeklatin.txt +118 -0
- data/data/bmpm_data/gen_rules_hebrew.txt +62 -0
- data/data/bmpm_data/gen_rules_hungarian.txt +83 -0
- data/data/bmpm_data/gen_rules_italian.txt +77 -0
- data/data/bmpm_data/gen_rules_polish.txt +185 -0
- data/data/bmpm_data/gen_rules_portuguese.txt +105 -0
- data/data/bmpm_data/gen_rules_romanian.txt +64 -0
- data/data/bmpm_data/gen_rules_russian.txt +142 -0
- data/data/bmpm_data/gen_rules_spanish.txt +85 -0
- data/data/bmpm_data/gen_rules_turkish.txt +50 -0
- data/data/keyboard_data/qwerty.npy +0 -0
- data/data/matrix_data/BLOSUM100 +31 -0
- data/data/matrix_data/BLOSUM30 +31 -0
- data/data/matrix_data/BLOSUM35 +31 -0
- data/data/matrix_data/BLOSUM40 +31 -0
- data/data/matrix_data/BLOSUM45 +25 -0
- data/data/matrix_data/BLOSUM50 +25 -0
- data/data/matrix_data/BLOSUM55 +31 -0
- data/data/matrix_data/BLOSUM60 +31 -0
- data/data/matrix_data/BLOSUM62 +25 -0
- data/data/matrix_data/BLOSUM65 +31 -0
- data/data/matrix_data/BLOSUM70 +31 -0
- data/data/matrix_data/BLOSUM75 +31 -0
- data/data/matrix_data/BLOSUM80 +25 -0
- data/data/matrix_data/BLOSUM85 +31 -0
- data/data/matrix_data/BLOSUM90 +25 -0
- data/data/matrix_data/NUC.4.4 +25 -0
- data/data/matrix_data/PAM10 +34 -0
- data/data/matrix_data/PAM100 +34 -0
- data/data/matrix_data/PAM110 +34 -0
- data/data/matrix_data/PAM120 +34 -0
- data/data/matrix_data/PAM130 +34 -0
- data/data/matrix_data/PAM140 +34 -0
- data/data/matrix_data/PAM150 +34 -0
- data/data/matrix_data/PAM160 +34 -0
- data/data/matrix_data/PAM170 +34 -0
- data/data/matrix_data/PAM180 +34 -0
- data/data/matrix_data/PAM190 +34 -0
- data/data/matrix_data/PAM20 +34 -0
- data/data/matrix_data/PAM200 +34 -0
- data/data/matrix_data/PAM210 +34 -0
- data/data/matrix_data/PAM220 +34 -0
- data/data/matrix_data/PAM230 +34 -0
- data/data/matrix_data/PAM240 +34 -0
- data/data/matrix_data/PAM250 +25 -0
- data/data/matrix_data/PAM260 +34 -0
- data/data/matrix_data/PAM270 +34 -0
- data/data/matrix_data/PAM280 +34 -0
- data/data/matrix_data/PAM290 +34 -0
- data/data/matrix_data/PAM30 +25 -0
- data/data/matrix_data/PAM300 +34 -0
- data/data/matrix_data/PAM310 +34 -0
- data/data/matrix_data/PAM320 +34 -0
- data/data/matrix_data/PAM330 +34 -0
- data/data/matrix_data/PAM340 +34 -0
- data/data/matrix_data/PAM350 +34 -0
- data/data/matrix_data/PAM360 +34 -0
- data/data/matrix_data/PAM370 +34 -0
- data/data/matrix_data/PAM380 +34 -0
- data/data/matrix_data/PAM390 +34 -0
- data/data/matrix_data/PAM40 +34 -0
- data/data/matrix_data/PAM400 +34 -0
- data/data/matrix_data/PAM410 +34 -0
- data/data/matrix_data/PAM420 +34 -0
- data/data/matrix_data/PAM430 +34 -0
- data/data/matrix_data/PAM440 +34 -0
- data/data/matrix_data/PAM450 +34 -0
- data/data/matrix_data/PAM460 +34 -0
- data/data/matrix_data/PAM470 +34 -0
- data/data/matrix_data/PAM480 +34 -0
- data/data/matrix_data/PAM490 +34 -0
- data/data/matrix_data/PAM50 +34 -0
- data/data/matrix_data/PAM500 +34 -0
- data/data/matrix_data/PAM60 +34 -0
- data/data/matrix_data/PAM70 +25 -0
- data/data/matrix_data/PAM80 +34 -0
- data/data/matrix_data/PAM90 +34 -0
- data/ext/stride_align/backend_avx2.cpp +2 -0
- data/ext/stride_align/backend_avx512bwvl.cpp +2 -0
- data/ext/stride_align/backend_generic.cpp +3 -0
- data/ext/stride_align/backend_impl.hpp +983 -0
- data/ext/stride_align/backend_lasx.cpp +2 -0
- data/ext/stride_align/backend_lsx.cpp +2 -0
- data/ext/stride_align/backend_neon.cpp +2 -0
- data/ext/stride_align/backend_rvv.cpp +2 -0
- data/ext/stride_align/backend_sse41.cpp +2 -0
- data/ext/stride_align/backend_sve.cpp +2 -0
- data/ext/stride_align/backend_sve2.cpp +2 -0
- data/ext/stride_align/backend_vsx.cpp +2 -0
- data/ext/stride_align/beider_morse_impl.cpp +5 -0
- data/ext/stride_align/cpu_detect.cpp +172 -0
- data/ext/stride_align/cpu_detect.hpp +6 -0
- data/ext/stride_align/extconf.rb +114 -0
- data/ext/stride_align/target_profile.hpp +82 -0
- data/ext/stride_align/vendor/beider_morse_impl.cpp +1467 -0
- data/ext/stride_align/vendor/stride_align/alignment.hpp +199 -0
- data/ext/stride_align/vendor/stride_align/batch.hpp +812 -0
- data/ext/stride_align/vendor/stride_align/beider_morse.hpp +121 -0
- data/ext/stride_align/vendor/stride_align/caverphone.hpp +222 -0
- data/ext/stride_align/vendor/stride_align/cologne_phonetic.hpp +202 -0
- data/ext/stride_align/vendor/stride_align/core.hpp +731 -0
- data/ext/stride_align/vendor/stride_align/daitch_mokotoff.hpp +631 -0
- data/ext/stride_align/vendor/stride_align/double_metaphone.hpp +796 -0
- data/ext/stride_align/vendor/stride_align/dtw.hpp +300 -0
- data/ext/stride_align/vendor/stride_align/encoded.hpp +235 -0
- data/ext/stride_align/vendor/stride_align/hamming.hpp +55 -0
- data/ext/stride_align/vendor/stride_align/indel.hpp +1200 -0
- data/ext/stride_align/vendor/stride_align/jaro.hpp +517 -0
- data/ext/stride_align/vendor/stride_align/lcs.hpp +159 -0
- data/ext/stride_align/vendor/stride_align/levenshtein.hpp +1247 -0
- data/ext/stride_align/vendor/stride_align/levenshtein_prepared.hpp +193 -0
- data/ext/stride_align/vendor/stride_align/match_rating.hpp +168 -0
- data/ext/stride_align/vendor/stride_align/metaphone.hpp +291 -0
- data/ext/stride_align/vendor/stride_align/ngram.hpp +176 -0
- data/ext/stride_align/vendor/stride_align/nysiis.hpp +199 -0
- data/ext/stride_align/vendor/stride_align/pairwise_alignment.hpp +465 -0
- data/ext/stride_align/vendor/stride_align/partial_ratio.hpp +486 -0
- data/ext/stride_align/vendor/stride_align/ratcliff_obershelp.hpp +101 -0
- data/ext/stride_align/vendor/stride_align/soundex.hpp +108 -0
- data/ext/stride_align/vendor/stride_align/token_ratios.hpp +445 -0
- data/ext/stride_align/vendor/stride_align/types.hpp +16 -0
- data/ext/stride_align/vendor/stride_align/utf8.hpp +512 -0
- data/ext/stride_align/vendor/stride_align/wratio.hpp +363 -0
- data/lib/stride_align/algorithms.rb +296 -0
- data/lib/stride_align/alignment_path.rb +217 -0
- data/lib/stride_align/backend.rb +47 -0
- data/lib/stride_align/batch.rb +705 -0
- data/lib/stride_align/core.rb +180 -0
- data/lib/stride_align/keyboard.rb +200 -0
- data/lib/stride_align/matrices.rb +403 -0
- data/lib/stride_align/version.rb +5 -0
- data/lib/stride_align.rb +87 -0
- metadata +231 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cstddef>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <limits>
|
|
7
|
+
#include <span>
|
|
8
|
+
#include <stdexcept>
|
|
9
|
+
#include <string>
|
|
10
|
+
#include <type_traits>
|
|
11
|
+
#include <utility>
|
|
12
|
+
#include <variant>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
#include "stride_align/hamming.hpp"
|
|
16
|
+
#include "stride_align/indel.hpp"
|
|
17
|
+
#include "stride_align/jaro.hpp"
|
|
18
|
+
#include "stride_align/levenshtein.hpp"
|
|
19
|
+
#include "stride_align/alignment.hpp"
|
|
20
|
+
#include "stride_align/pairwise_alignment.hpp"
|
|
21
|
+
#include "stride_align/utf8.hpp"
|
|
22
|
+
|
|
23
|
+
namespace stride_align::core {
|
|
24
|
+
|
|
25
|
+
namespace detail {
|
|
26
|
+
|
|
27
|
+
template <typename Token>
|
|
28
|
+
inline std::span<const Token> token_span(const utf8::TokenBuffer& buffer) {
|
|
29
|
+
if (const auto* borrowed = std::get_if<std::span<const Token>>(&buffer)) {
|
|
30
|
+
return *borrowed;
|
|
31
|
+
}
|
|
32
|
+
const auto& owned = std::get<std::vector<Token>>(buffer);
|
|
33
|
+
return std::span<const Token>(owned.data(), owned.size());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
template <typename Function>
|
|
37
|
+
decltype(auto) visit_pair(const utf8::PreparedPair& pair, Function&& function) {
|
|
38
|
+
switch (pair.width) {
|
|
39
|
+
case utf8::TokenWidth::u8:
|
|
40
|
+
return function(
|
|
41
|
+
token_span<std::uint8_t>(pair.query),
|
|
42
|
+
token_span<std::uint8_t>(pair.target));
|
|
43
|
+
case utf8::TokenWidth::u16: {
|
|
44
|
+
return function(
|
|
45
|
+
token_span<std::uint16_t>(pair.query),
|
|
46
|
+
token_span<std::uint16_t>(pair.target));
|
|
47
|
+
}
|
|
48
|
+
case utf8::TokenWidth::u32: {
|
|
49
|
+
return function(
|
|
50
|
+
token_span<std::uint32_t>(pair.query),
|
|
51
|
+
token_span<std::uint32_t>(pair.target));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw std::logic_error("unsupported token width");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
template <typename Token>
|
|
58
|
+
inline std::size_t levenshtein_typed(
|
|
59
|
+
std::span<const Token> query,
|
|
60
|
+
std::span<const Token> target,
|
|
61
|
+
std::size_t cutoff) {
|
|
62
|
+
std::span<const Token> pattern = query;
|
|
63
|
+
std::span<const Token> text = target;
|
|
64
|
+
if (pattern.size() > text.size()) std::swap(pattern, text);
|
|
65
|
+
if constexpr (std::is_same_v<Token, std::uint8_t>) {
|
|
66
|
+
return pattern.size() <= 64U
|
|
67
|
+
? levenshtein::myers_single_word_u8(pattern, text, cutoff)
|
|
68
|
+
: levenshtein::myers_multi_word_u8(pattern, text, cutoff);
|
|
69
|
+
} else {
|
|
70
|
+
return levenshtein::myers_distance<Token>(pattern, text, cutoff);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
template <typename Token>
|
|
75
|
+
inline std::size_t indel_typed(
|
|
76
|
+
std::span<const Token> query,
|
|
77
|
+
std::span<const Token> target,
|
|
78
|
+
std::size_t cutoff) {
|
|
79
|
+
std::span<const Token> pattern = query;
|
|
80
|
+
std::span<const Token> text = target;
|
|
81
|
+
if (pattern.size() > text.size()) std::swap(pattern, text);
|
|
82
|
+
if constexpr (std::is_same_v<Token, std::uint8_t>) {
|
|
83
|
+
return indel::indel_distance_u8(pattern, text, cutoff);
|
|
84
|
+
} else {
|
|
85
|
+
return indel::indel_distance<Token>(pattern, text, cutoff);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
template <typename Token>
|
|
90
|
+
inline double jaro_typed(
|
|
91
|
+
std::span<const Token> query,
|
|
92
|
+
std::span<const Token> target) {
|
|
93
|
+
if constexpr (std::is_same_v<Token, std::uint8_t>) {
|
|
94
|
+
return query.size() <= 64U && target.size() <= 64U
|
|
95
|
+
? jaro::jaro_bp_byte_64(query, target)
|
|
96
|
+
: jaro::jaro_bp_byte_multiword(query, target);
|
|
97
|
+
} else {
|
|
98
|
+
return jaro::jaro_bp_token_multiword<Token>(query, target);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
} // namespace detail
|
|
103
|
+
|
|
104
|
+
inline std::size_t levenshtein_distance(
|
|
105
|
+
const utf8::PreparedPair& pair,
|
|
106
|
+
std::size_t cutoff = levenshtein::kNoCutoff) {
|
|
107
|
+
return detail::visit_pair(pair, [cutoff](auto query, auto target) {
|
|
108
|
+
return detail::levenshtein_typed(query, target, cutoff);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
inline double levenshtein_similarity(const utf8::PreparedPair& pair) {
|
|
113
|
+
return levenshtein::normalize(
|
|
114
|
+
levenshtein_distance(pair), pair.query_size(), pair.target_size());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
inline std::size_t osa_distance(const utf8::PreparedPair& pair) {
|
|
118
|
+
return detail::visit_pair(pair, [](auto query, auto target) {
|
|
119
|
+
using Token = typename decltype(query)::value_type;
|
|
120
|
+
if constexpr (std::is_same_v<Token, std::uint8_t>) {
|
|
121
|
+
return levenshtein::osa_distance_u8(query, target);
|
|
122
|
+
} else {
|
|
123
|
+
return levenshtein::osa_distance<Token>(query, target);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
inline double osa_similarity(const utf8::PreparedPair& pair) {
|
|
129
|
+
return levenshtein::normalize(
|
|
130
|
+
osa_distance(pair), pair.query_size(), pair.target_size());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
inline std::size_t true_damerau_levenshtein_distance(
|
|
134
|
+
const utf8::PreparedPair& pair) {
|
|
135
|
+
return detail::visit_pair(pair, [](auto query, auto target) {
|
|
136
|
+
using Token = typename decltype(query)::value_type;
|
|
137
|
+
if constexpr (std::is_same_v<Token, std::uint8_t>) {
|
|
138
|
+
return levenshtein::true_damerau_levenshtein_distance_u8(query, target);
|
|
139
|
+
} else {
|
|
140
|
+
return levenshtein::true_damerau_levenshtein_distance<Token>(query, target);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
inline double true_damerau_levenshtein_similarity(const utf8::PreparedPair& pair) {
|
|
146
|
+
return levenshtein::normalize(
|
|
147
|
+
true_damerau_levenshtein_distance(pair), pair.query_size(), pair.target_size());
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
inline std::size_t indel_distance(
|
|
151
|
+
const utf8::PreparedPair& pair,
|
|
152
|
+
std::size_t cutoff = indel::kNoCutoff) {
|
|
153
|
+
return detail::visit_pair(pair, [cutoff](auto query, auto target) {
|
|
154
|
+
return detail::indel_typed(query, target, cutoff);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
inline double indel_similarity(const utf8::PreparedPair& pair) {
|
|
159
|
+
return indel::normalize(
|
|
160
|
+
indel_distance(pair), pair.query_size(), pair.target_size());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
inline std::size_t hamming_distance(const utf8::PreparedPair& pair) {
|
|
164
|
+
if (pair.query_size() != pair.target_size()) {
|
|
165
|
+
throw std::invalid_argument("Hamming distance requires equal-length strings");
|
|
166
|
+
}
|
|
167
|
+
return detail::visit_pair(pair, [](auto query, auto target) {
|
|
168
|
+
using Token = typename decltype(query)::value_type;
|
|
169
|
+
if constexpr (std::is_same_v<Token, std::uint8_t>) {
|
|
170
|
+
return hamming::hamming_u8(query, target);
|
|
171
|
+
} else {
|
|
172
|
+
return hamming::hamming_scalar<Token>(query, target);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
inline double hamming_similarity(const utf8::PreparedPair& pair) {
|
|
178
|
+
return hamming::normalize(hamming_distance(pair), pair.query_size());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
inline double jaro_similarity(const utf8::PreparedPair& pair) {
|
|
182
|
+
return detail::visit_pair(pair, [](auto query, auto target) {
|
|
183
|
+
return detail::jaro_typed(query, target);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
inline double jaro_winkler_similarity(
|
|
188
|
+
const utf8::PreparedPair& pair,
|
|
189
|
+
double prefix_weight = jaro::kDefaultPrefixWeight,
|
|
190
|
+
double prefix_threshold = jaro::kDefaultPrefixThreshold,
|
|
191
|
+
std::size_t prefix_cap = jaro::kDefaultPrefixCap) {
|
|
192
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
193
|
+
using Token = typename decltype(query)::value_type;
|
|
194
|
+
const double base = detail::jaro_typed(query, target);
|
|
195
|
+
if (base < prefix_threshold) return base;
|
|
196
|
+
const std::size_t prefix = jaro::common_prefix<Token>(query, target, prefix_cap);
|
|
197
|
+
return base + static_cast<double>(prefix) * prefix_weight * (1.0 - base);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
inline Score smith_waterman_score(
|
|
202
|
+
const utf8::PreparedPair& pair,
|
|
203
|
+
Score match_score = 2,
|
|
204
|
+
Score mismatch_score = -1,
|
|
205
|
+
Score gap_score = -1) {
|
|
206
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
207
|
+
return pairwise_alignment::linear_score<
|
|
208
|
+
typename decltype(query)::value_type, true>(
|
|
209
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
inline Score needleman_wunsch_score(
|
|
214
|
+
const utf8::PreparedPair& pair,
|
|
215
|
+
Score match_score = 2,
|
|
216
|
+
Score mismatch_score = -1,
|
|
217
|
+
Score gap_score = -1) {
|
|
218
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
219
|
+
return pairwise_alignment::linear_score<
|
|
220
|
+
typename decltype(query)::value_type, false>(
|
|
221
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
inline Score smith_waterman_affine_score(
|
|
226
|
+
const utf8::PreparedPair& pair,
|
|
227
|
+
Score match_score = 2,
|
|
228
|
+
Score mismatch_score = -1,
|
|
229
|
+
Score gap_open_score = -2,
|
|
230
|
+
Score gap_extend_score = -1) {
|
|
231
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
232
|
+
return pairwise_alignment::affine_score<
|
|
233
|
+
typename decltype(query)::value_type, true>(
|
|
234
|
+
query, target, match_score, mismatch_score,
|
|
235
|
+
gap_open_score, gap_extend_score);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
inline Score needleman_wunsch_affine_score(
|
|
240
|
+
const utf8::PreparedPair& pair,
|
|
241
|
+
Score match_score = 2,
|
|
242
|
+
Score mismatch_score = -1,
|
|
243
|
+
Score gap_open_score = -2,
|
|
244
|
+
Score gap_extend_score = -1) {
|
|
245
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
246
|
+
return pairwise_alignment::affine_score<
|
|
247
|
+
typename decltype(query)::value_type, false>(
|
|
248
|
+
query, target, match_score, mismatch_score,
|
|
249
|
+
gap_open_score, gap_extend_score);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
namespace detail {
|
|
254
|
+
|
|
255
|
+
enum class TraceDirection : std::uint8_t { stop, diagonal, up, left };
|
|
256
|
+
enum class TraceState : std::uint8_t { h, vertical, horizontal };
|
|
257
|
+
|
|
258
|
+
template <typename Token, bool Local>
|
|
259
|
+
AlignmentPath linear_path_typed(
|
|
260
|
+
std::span<const Token> query,
|
|
261
|
+
std::span<const Token> target,
|
|
262
|
+
Score match_score,
|
|
263
|
+
Score mismatch_score,
|
|
264
|
+
Score gap_score) {
|
|
265
|
+
const std::size_t rows = query.size() + 1U;
|
|
266
|
+
const std::size_t columns = target.size() + 1U;
|
|
267
|
+
if (rows != 0U && columns > std::numeric_limits<std::size_t>::max() / rows) {
|
|
268
|
+
throw std::length_error("alignment traceback dimensions overflow");
|
|
269
|
+
}
|
|
270
|
+
const auto index = [columns](std::size_t row, std::size_t column) {
|
|
271
|
+
return row * columns + column;
|
|
272
|
+
};
|
|
273
|
+
std::vector<Score> scores(rows * columns, 0);
|
|
274
|
+
std::vector<TraceDirection> directions(rows * columns, TraceDirection::stop);
|
|
275
|
+
if constexpr (!Local) {
|
|
276
|
+
for (std::size_t row = 1; row < rows; ++row) {
|
|
277
|
+
scores[index(row, 0)] = static_cast<Score>(row) * gap_score;
|
|
278
|
+
directions[index(row, 0)] = TraceDirection::up;
|
|
279
|
+
}
|
|
280
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
281
|
+
scores[index(0, column)] = static_cast<Score>(column) * gap_score;
|
|
282
|
+
directions[index(0, column)] = TraceDirection::left;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
Score best_score = 0;
|
|
287
|
+
std::size_t best_row = 0;
|
|
288
|
+
std::size_t best_column = 0;
|
|
289
|
+
for (std::size_t row = 1; row < rows; ++row) {
|
|
290
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
291
|
+
const Score diagonal = scores[index(row - 1U, column - 1U)] +
|
|
292
|
+
(query[row - 1U] == target[column - 1U] ? match_score : mismatch_score);
|
|
293
|
+
const Score up = scores[index(row - 1U, column)] + gap_score;
|
|
294
|
+
const Score left = scores[index(row, column - 1U)] + gap_score;
|
|
295
|
+
Score value = diagonal;
|
|
296
|
+
TraceDirection direction = TraceDirection::diagonal;
|
|
297
|
+
if (up > value) {
|
|
298
|
+
value = up;
|
|
299
|
+
direction = TraceDirection::up;
|
|
300
|
+
}
|
|
301
|
+
if (left > value) {
|
|
302
|
+
value = left;
|
|
303
|
+
direction = TraceDirection::left;
|
|
304
|
+
}
|
|
305
|
+
if constexpr (Local) {
|
|
306
|
+
if (value <= 0) {
|
|
307
|
+
value = 0;
|
|
308
|
+
direction = TraceDirection::stop;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
scores[index(row, column)] = value;
|
|
312
|
+
directions[index(row, column)] = direction;
|
|
313
|
+
if constexpr (Local) {
|
|
314
|
+
if (value > best_score) {
|
|
315
|
+
best_score = value;
|
|
316
|
+
best_row = row;
|
|
317
|
+
best_column = column;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if constexpr (!Local) {
|
|
323
|
+
best_row = query.size();
|
|
324
|
+
best_column = target.size();
|
|
325
|
+
best_score = scores[index(best_row, best_column)];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const std::size_t query_end = best_row;
|
|
329
|
+
const std::size_t target_end = best_column;
|
|
330
|
+
std::string operations;
|
|
331
|
+
operations.reserve(best_row + best_column);
|
|
332
|
+
while (best_row > 0U || best_column > 0U) {
|
|
333
|
+
const TraceDirection direction = directions[index(best_row, best_column)];
|
|
334
|
+
if (direction == TraceDirection::stop) break;
|
|
335
|
+
if (direction == TraceDirection::diagonal) {
|
|
336
|
+
operations.push_back(
|
|
337
|
+
query[best_row - 1U] == target[best_column - 1U] ? '=' : 'X');
|
|
338
|
+
--best_row;
|
|
339
|
+
--best_column;
|
|
340
|
+
} else if (direction == TraceDirection::up) {
|
|
341
|
+
operations.push_back('D');
|
|
342
|
+
--best_row;
|
|
343
|
+
} else {
|
|
344
|
+
operations.push_back('I');
|
|
345
|
+
--best_column;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
std::reverse(operations.begin(), operations.end());
|
|
349
|
+
return make_alignment_path(
|
|
350
|
+
best_score, best_row, query_end, best_column, target_end, operations);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
inline constexpr Score kTraceNegativeInfinity =
|
|
354
|
+
std::numeric_limits<Score>::lowest() / 4;
|
|
355
|
+
|
|
356
|
+
inline Score trace_add(Score value, Score delta) noexcept {
|
|
357
|
+
return value <= kTraceNegativeInfinity / 2
|
|
358
|
+
? kTraceNegativeInfinity
|
|
359
|
+
: value + delta;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
template <typename Token, bool Local>
|
|
363
|
+
AlignmentPath affine_path_typed(
|
|
364
|
+
std::span<const Token> query,
|
|
365
|
+
std::span<const Token> target,
|
|
366
|
+
Score match_score,
|
|
367
|
+
Score mismatch_score,
|
|
368
|
+
Score gap_open_score,
|
|
369
|
+
Score gap_extend_score) {
|
|
370
|
+
const std::size_t rows = query.size() + 1U;
|
|
371
|
+
const std::size_t columns = target.size() + 1U;
|
|
372
|
+
if (rows != 0U && columns > std::numeric_limits<std::size_t>::max() / rows) {
|
|
373
|
+
throw std::length_error("alignment traceback dimensions overflow");
|
|
374
|
+
}
|
|
375
|
+
const auto index = [columns](std::size_t row, std::size_t column) {
|
|
376
|
+
return row * columns + column;
|
|
377
|
+
};
|
|
378
|
+
const auto gap_cost = [&](std::size_t length) {
|
|
379
|
+
return length == 0U
|
|
380
|
+
? Score{0}
|
|
381
|
+
: gap_open_score + static_cast<Score>(length - 1U) * gap_extend_score;
|
|
382
|
+
};
|
|
383
|
+
std::vector<Score> h(
|
|
384
|
+
rows * columns, Local ? Score{0} : kTraceNegativeInfinity);
|
|
385
|
+
std::vector<Score> vertical(rows * columns, kTraceNegativeInfinity);
|
|
386
|
+
std::vector<Score> horizontal(rows * columns, kTraceNegativeInfinity);
|
|
387
|
+
h[index(0, 0)] = 0;
|
|
388
|
+
if constexpr (!Local) {
|
|
389
|
+
for (std::size_t row = 1; row < rows; ++row) {
|
|
390
|
+
h[index(row, 0)] = gap_cost(row);
|
|
391
|
+
vertical[index(row, 0)] = h[index(row, 0)];
|
|
392
|
+
}
|
|
393
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
394
|
+
h[index(0, column)] = gap_cost(column);
|
|
395
|
+
horizontal[index(0, column)] = h[index(0, column)];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
Score best_score = 0;
|
|
399
|
+
std::size_t best_row = 0;
|
|
400
|
+
std::size_t best_column = 0;
|
|
401
|
+
for (std::size_t row = 1; row < rows; ++row) {
|
|
402
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
403
|
+
const std::size_t cell = index(row, column);
|
|
404
|
+
vertical[cell] = std::max(
|
|
405
|
+
trace_add(h[index(row - 1U, column)], gap_open_score),
|
|
406
|
+
trace_add(vertical[index(row - 1U, column)], gap_extend_score));
|
|
407
|
+
horizontal[cell] = std::max(
|
|
408
|
+
trace_add(h[index(row, column - 1U)], gap_open_score),
|
|
409
|
+
trace_add(horizontal[index(row, column - 1U)], gap_extend_score));
|
|
410
|
+
const Score diagonal = trace_add(
|
|
411
|
+
h[index(row - 1U, column - 1U)],
|
|
412
|
+
query[row - 1U] == target[column - 1U]
|
|
413
|
+
? match_score
|
|
414
|
+
: mismatch_score);
|
|
415
|
+
Score value = std::max({diagonal, vertical[cell], horizontal[cell]});
|
|
416
|
+
if constexpr (Local) value = std::max<Score>(0, value);
|
|
417
|
+
h[cell] = value;
|
|
418
|
+
if constexpr (Local) {
|
|
419
|
+
if (value > best_score) {
|
|
420
|
+
best_score = value;
|
|
421
|
+
best_row = row;
|
|
422
|
+
best_column = column;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if constexpr (!Local) {
|
|
428
|
+
best_row = query.size();
|
|
429
|
+
best_column = target.size();
|
|
430
|
+
best_score = h[index(best_row, best_column)];
|
|
431
|
+
}
|
|
432
|
+
const std::size_t query_end = best_row;
|
|
433
|
+
const std::size_t target_end = best_column;
|
|
434
|
+
TraceState state = TraceState::h;
|
|
435
|
+
std::string operations;
|
|
436
|
+
operations.reserve(best_row + best_column);
|
|
437
|
+
while (best_row > 0U || best_column > 0U) {
|
|
438
|
+
const std::size_t cell = index(best_row, best_column);
|
|
439
|
+
if constexpr (Local) {
|
|
440
|
+
if (state == TraceState::h && h[cell] <= 0) break;
|
|
441
|
+
}
|
|
442
|
+
if (state == TraceState::h) {
|
|
443
|
+
if (best_row > 0U && best_column > 0U) {
|
|
444
|
+
const Score diagonal = trace_add(
|
|
445
|
+
h[index(best_row - 1U, best_column - 1U)],
|
|
446
|
+
query[best_row - 1U] == target[best_column - 1U]
|
|
447
|
+
? match_score
|
|
448
|
+
: mismatch_score);
|
|
449
|
+
if (h[cell] == diagonal) {
|
|
450
|
+
operations.push_back(
|
|
451
|
+
query[best_row - 1U] == target[best_column - 1U] ? '=' : 'X');
|
|
452
|
+
--best_row;
|
|
453
|
+
--best_column;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (best_row > 0U && h[cell] == vertical[cell]) {
|
|
458
|
+
state = TraceState::vertical;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (best_column > 0U && h[cell] == horizontal[cell]) {
|
|
462
|
+
state = TraceState::horizontal;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
if (state == TraceState::vertical) {
|
|
468
|
+
operations.push_back('D');
|
|
469
|
+
const bool continues = best_row > 1U &&
|
|
470
|
+
vertical[cell] == trace_add(
|
|
471
|
+
vertical[index(best_row - 1U, best_column)], gap_extend_score);
|
|
472
|
+
--best_row;
|
|
473
|
+
state = continues ? TraceState::vertical : TraceState::h;
|
|
474
|
+
} else {
|
|
475
|
+
operations.push_back('I');
|
|
476
|
+
const bool continues = best_column > 1U &&
|
|
477
|
+
horizontal[cell] == trace_add(
|
|
478
|
+
horizontal[index(best_row, best_column - 1U)], gap_extend_score);
|
|
479
|
+
--best_column;
|
|
480
|
+
state = continues ? TraceState::horizontal : TraceState::h;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
std::reverse(operations.begin(), operations.end());
|
|
484
|
+
return make_alignment_path(
|
|
485
|
+
best_score, best_row, query_end, best_column, target_end, operations);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
} // namespace detail
|
|
489
|
+
|
|
490
|
+
inline AlignmentPath smith_waterman_path(
|
|
491
|
+
const utf8::PreparedPair& pair,
|
|
492
|
+
Score match_score = 2,
|
|
493
|
+
Score mismatch_score = -1,
|
|
494
|
+
Score gap_score = -1) {
|
|
495
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
496
|
+
return detail::linear_path_typed<
|
|
497
|
+
typename decltype(query)::value_type, true>(
|
|
498
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
inline AlignmentPath needleman_wunsch_path(
|
|
503
|
+
const utf8::PreparedPair& pair,
|
|
504
|
+
Score match_score = 2,
|
|
505
|
+
Score mismatch_score = -1,
|
|
506
|
+
Score gap_score = -1) {
|
|
507
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
508
|
+
return detail::linear_path_typed<
|
|
509
|
+
typename decltype(query)::value_type, false>(
|
|
510
|
+
query, target, match_score, mismatch_score, gap_score);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
inline AlignmentPath smith_waterman_affine_path(
|
|
515
|
+
const utf8::PreparedPair& pair,
|
|
516
|
+
Score match_score = 2,
|
|
517
|
+
Score mismatch_score = -1,
|
|
518
|
+
Score gap_open_score = -2,
|
|
519
|
+
Score gap_extend_score = -1) {
|
|
520
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
521
|
+
return detail::affine_path_typed<
|
|
522
|
+
typename decltype(query)::value_type, true>(
|
|
523
|
+
query, target, match_score, mismatch_score,
|
|
524
|
+
gap_open_score, gap_extend_score);
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
inline AlignmentPath needleman_wunsch_affine_path(
|
|
529
|
+
const utf8::PreparedPair& pair,
|
|
530
|
+
Score match_score = 2,
|
|
531
|
+
Score mismatch_score = -1,
|
|
532
|
+
Score gap_open_score = -2,
|
|
533
|
+
Score gap_extend_score = -1) {
|
|
534
|
+
return detail::visit_pair(pair, [&](auto query, auto target) {
|
|
535
|
+
return detail::affine_path_typed<
|
|
536
|
+
typename decltype(query)::value_type, false>(
|
|
537
|
+
query, target, match_score, mismatch_score,
|
|
538
|
+
gap_open_score, gap_extend_score);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Host-neutral substitution-matrix kernels. The binding supplies an encoded
|
|
543
|
+
// token sequence and a score lookup; no Python/nanobind types cross this API.
|
|
544
|
+
template <bool Local, typename Lookup>
|
|
545
|
+
inline Score substitution_matrix_affine_score(
|
|
546
|
+
std::span<const std::uint16_t> query,
|
|
547
|
+
std::span<const std::uint16_t> target,
|
|
548
|
+
Lookup&& lookup,
|
|
549
|
+
Score gap_open_score,
|
|
550
|
+
Score gap_extend_score) {
|
|
551
|
+
const std::size_t columns = target.size() + 1U;
|
|
552
|
+
constexpr Score negative_infinity =
|
|
553
|
+
std::numeric_limits<Score>::lowest() / 4;
|
|
554
|
+
const auto add = [](Score value, Score delta) {
|
|
555
|
+
return value <= negative_infinity / 2 ? negative_infinity : value + delta;
|
|
556
|
+
};
|
|
557
|
+
const auto gap_cost = [&](std::size_t length) {
|
|
558
|
+
return length == 0U
|
|
559
|
+
? Score{0}
|
|
560
|
+
: gap_open_score + static_cast<Score>(length - 1U) * gap_extend_score;
|
|
561
|
+
};
|
|
562
|
+
std::vector<Score> previous(columns, Local ? Score{0} : negative_infinity);
|
|
563
|
+
std::vector<Score> current(columns, Local ? Score{0} : negative_infinity);
|
|
564
|
+
std::vector<Score> current_horizontal(columns, negative_infinity);
|
|
565
|
+
std::vector<Score> previous_vertical(columns, negative_infinity);
|
|
566
|
+
std::vector<Score> current_vertical(columns, negative_infinity);
|
|
567
|
+
if constexpr (!Local) {
|
|
568
|
+
previous[0] = 0;
|
|
569
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
570
|
+
previous[column] = gap_cost(column);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
Score best = 0;
|
|
574
|
+
for (std::size_t row = 1; row <= query.size(); ++row) {
|
|
575
|
+
current[0] = Local ? 0 : gap_cost(row);
|
|
576
|
+
current_vertical[0] = Local ? negative_infinity : current[0];
|
|
577
|
+
current_horizontal[0] = negative_infinity;
|
|
578
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
579
|
+
current_vertical[column] = std::max(
|
|
580
|
+
add(previous[column], gap_open_score),
|
|
581
|
+
add(previous_vertical[column], gap_extend_score));
|
|
582
|
+
current_horizontal[column] = std::max(
|
|
583
|
+
add(current[column - 1U], gap_open_score),
|
|
584
|
+
add(current_horizontal[column - 1U], gap_extend_score));
|
|
585
|
+
const Score diagonal = add(
|
|
586
|
+
previous[column - 1U],
|
|
587
|
+
static_cast<Score>(lookup(
|
|
588
|
+
query[row - 1U], target[column - 1U])));
|
|
589
|
+
Score value = std::max(
|
|
590
|
+
{diagonal, current_vertical[column], current_horizontal[column]});
|
|
591
|
+
if constexpr (Local) {
|
|
592
|
+
value = std::max<Score>(0, value);
|
|
593
|
+
best = std::max(best, value);
|
|
594
|
+
}
|
|
595
|
+
current[column] = value;
|
|
596
|
+
}
|
|
597
|
+
std::swap(previous, current);
|
|
598
|
+
std::swap(previous_vertical, current_vertical);
|
|
599
|
+
}
|
|
600
|
+
if constexpr (Local) return best;
|
|
601
|
+
return previous.back();
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
template <bool Local, typename Lookup>
|
|
605
|
+
inline AlignmentPath substitution_matrix_affine_path(
|
|
606
|
+
std::span<const std::uint16_t> query,
|
|
607
|
+
std::span<const std::uint16_t> target,
|
|
608
|
+
Lookup&& lookup,
|
|
609
|
+
Score gap_open_score,
|
|
610
|
+
Score gap_extend_score) {
|
|
611
|
+
const std::size_t rows = query.size() + 1U;
|
|
612
|
+
const std::size_t columns = target.size() + 1U;
|
|
613
|
+
if (rows != 0U && columns > std::numeric_limits<std::size_t>::max() / rows) {
|
|
614
|
+
throw std::length_error("alignment traceback dimensions overflow");
|
|
615
|
+
}
|
|
616
|
+
constexpr Score negative_infinity =
|
|
617
|
+
std::numeric_limits<Score>::lowest() / 4;
|
|
618
|
+
const auto index = [columns](std::size_t row, std::size_t column) {
|
|
619
|
+
return row * columns + column;
|
|
620
|
+
};
|
|
621
|
+
const auto add = [](Score value, Score delta) {
|
|
622
|
+
return value <= negative_infinity / 2 ? negative_infinity : value + delta;
|
|
623
|
+
};
|
|
624
|
+
const auto gap_cost = [&](std::size_t length) {
|
|
625
|
+
return length == 0U
|
|
626
|
+
? Score{0}
|
|
627
|
+
: gap_open_score + static_cast<Score>(length - 1U) * gap_extend_score;
|
|
628
|
+
};
|
|
629
|
+
std::vector<Score> h(rows * columns, Local ? Score{0} : negative_infinity);
|
|
630
|
+
std::vector<Score> vertical(rows * columns, negative_infinity);
|
|
631
|
+
std::vector<Score> horizontal(rows * columns, negative_infinity);
|
|
632
|
+
h[0] = 0;
|
|
633
|
+
if constexpr (!Local) {
|
|
634
|
+
for (std::size_t row = 1; row < rows; ++row) {
|
|
635
|
+
h[index(row, 0)] = gap_cost(row);
|
|
636
|
+
vertical[index(row, 0)] = h[index(row, 0)];
|
|
637
|
+
}
|
|
638
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
639
|
+
h[index(0, column)] = gap_cost(column);
|
|
640
|
+
horizontal[index(0, column)] = h[index(0, column)];
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
Score best_score = 0;
|
|
644
|
+
std::size_t best_row = 0;
|
|
645
|
+
std::size_t best_column = 0;
|
|
646
|
+
for (std::size_t row = 1; row < rows; ++row) {
|
|
647
|
+
for (std::size_t column = 1; column < columns; ++column) {
|
|
648
|
+
const std::size_t cell = index(row, column);
|
|
649
|
+
vertical[cell] = std::max(
|
|
650
|
+
add(h[index(row - 1U, column)], gap_open_score),
|
|
651
|
+
add(vertical[index(row - 1U, column)], gap_extend_score));
|
|
652
|
+
horizontal[cell] = std::max(
|
|
653
|
+
add(h[index(row, column - 1U)], gap_open_score),
|
|
654
|
+
add(horizontal[index(row, column - 1U)], gap_extend_score));
|
|
655
|
+
const Score diagonal = add(
|
|
656
|
+
h[index(row - 1U, column - 1U)],
|
|
657
|
+
static_cast<Score>(lookup(
|
|
658
|
+
query[row - 1U], target[column - 1U])));
|
|
659
|
+
Score value = std::max({diagonal, vertical[cell], horizontal[cell]});
|
|
660
|
+
if constexpr (Local) value = std::max<Score>(0, value);
|
|
661
|
+
h[cell] = value;
|
|
662
|
+
if constexpr (Local) {
|
|
663
|
+
if (value > best_score) {
|
|
664
|
+
best_score = value;
|
|
665
|
+
best_row = row;
|
|
666
|
+
best_column = column;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if constexpr (!Local) {
|
|
672
|
+
best_row = query.size();
|
|
673
|
+
best_column = target.size();
|
|
674
|
+
best_score = h[index(best_row, best_column)];
|
|
675
|
+
}
|
|
676
|
+
const std::size_t query_end = best_row;
|
|
677
|
+
const std::size_t target_end = best_column;
|
|
678
|
+
detail::TraceState state = detail::TraceState::h;
|
|
679
|
+
std::string operations;
|
|
680
|
+
operations.reserve(best_row + best_column);
|
|
681
|
+
while (best_row > 0U || best_column > 0U) {
|
|
682
|
+
const std::size_t cell = index(best_row, best_column);
|
|
683
|
+
if constexpr (Local) {
|
|
684
|
+
if (state == detail::TraceState::h && h[cell] <= 0) break;
|
|
685
|
+
}
|
|
686
|
+
if (state == detail::TraceState::h) {
|
|
687
|
+
if (best_row > 0U && best_column > 0U) {
|
|
688
|
+
const Score diagonal = add(
|
|
689
|
+
h[index(best_row - 1U, best_column - 1U)],
|
|
690
|
+
static_cast<Score>(lookup(
|
|
691
|
+
query[best_row - 1U], target[best_column - 1U])));
|
|
692
|
+
if (h[cell] == diagonal) {
|
|
693
|
+
operations.push_back(
|
|
694
|
+
query[best_row - 1U] == target[best_column - 1U] ? '=' : 'X');
|
|
695
|
+
--best_row;
|
|
696
|
+
--best_column;
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (best_row > 0U && h[cell] == vertical[cell]) {
|
|
701
|
+
state = detail::TraceState::vertical;
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (best_column > 0U && h[cell] == horizontal[cell]) {
|
|
705
|
+
state = detail::TraceState::horizontal;
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
if (state == detail::TraceState::vertical) {
|
|
711
|
+
operations.push_back('D');
|
|
712
|
+
const bool continues = best_row > 1U &&
|
|
713
|
+
vertical[cell] == add(
|
|
714
|
+
vertical[index(best_row - 1U, best_column)], gap_extend_score);
|
|
715
|
+
--best_row;
|
|
716
|
+
state = continues ? detail::TraceState::vertical : detail::TraceState::h;
|
|
717
|
+
} else {
|
|
718
|
+
operations.push_back('I');
|
|
719
|
+
const bool continues = best_column > 1U &&
|
|
720
|
+
horizontal[cell] == add(
|
|
721
|
+
horizontal[index(best_row, best_column - 1U)], gap_extend_score);
|
|
722
|
+
--best_column;
|
|
723
|
+
state = continues ? detail::TraceState::horizontal : detail::TraceState::h;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
std::reverse(operations.begin(), operations.end());
|
|
727
|
+
return make_alignment_path(
|
|
728
|
+
best_score, best_row, query_end, best_column, target_end, operations);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
} // namespace stride_align::core
|