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,983 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include <cstddef>
|
|
7
|
+
#include <cstdint>
|
|
8
|
+
#include <cstdio>
|
|
9
|
+
#include <limits>
|
|
10
|
+
#include <optional>
|
|
11
|
+
#include <span>
|
|
12
|
+
#include <stdexcept>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <string_view>
|
|
15
|
+
#include <unordered_map>
|
|
16
|
+
#include <utility>
|
|
17
|
+
#include <vector>
|
|
18
|
+
|
|
19
|
+
#include <ruby.h>
|
|
20
|
+
#include <ruby/encoding.h>
|
|
21
|
+
|
|
22
|
+
// Ruby 2.6's compatibility header defines these C99 names as macros. They
|
|
23
|
+
// otherwise rewrite qualified C++ calls such as std::isfinite.
|
|
24
|
+
#ifdef isfinite
|
|
25
|
+
#undef isfinite
|
|
26
|
+
#endif
|
|
27
|
+
#ifdef isnan
|
|
28
|
+
#undef isnan
|
|
29
|
+
#endif
|
|
30
|
+
#ifdef isinf
|
|
31
|
+
#undef isinf
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
#include "target_profile.hpp"
|
|
35
|
+
#include "stride_align/core.hpp"
|
|
36
|
+
|
|
37
|
+
#if defined(STRIDE_ALIGN_RUBY_PRIMARY)
|
|
38
|
+
#include "cpu_detect.hpp"
|
|
39
|
+
#include "stride_align/alignment.hpp"
|
|
40
|
+
#include "stride_align/beider_morse.hpp"
|
|
41
|
+
#include "stride_align/caverphone.hpp"
|
|
42
|
+
#include "stride_align/cologne_phonetic.hpp"
|
|
43
|
+
#include "stride_align/daitch_mokotoff.hpp"
|
|
44
|
+
#include "stride_align/double_metaphone.hpp"
|
|
45
|
+
#include "stride_align/dtw.hpp"
|
|
46
|
+
#include "stride_align/lcs.hpp"
|
|
47
|
+
#include "stride_align/match_rating.hpp"
|
|
48
|
+
#include "stride_align/metaphone.hpp"
|
|
49
|
+
#include "stride_align/ngram.hpp"
|
|
50
|
+
#include "stride_align/nysiis.hpp"
|
|
51
|
+
#include "stride_align/partial_ratio.hpp"
|
|
52
|
+
#include "stride_align/ratcliff_obershelp.hpp"
|
|
53
|
+
#include "stride_align/soundex.hpp"
|
|
54
|
+
#include "stride_align/token_ratios.hpp"
|
|
55
|
+
#include "stride_align/wratio.hpp"
|
|
56
|
+
#endif
|
|
57
|
+
|
|
58
|
+
#if defined(STRIDE_ALIGN_RUBY_TARGET_GENERIC)
|
|
59
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__generic
|
|
60
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Generic"
|
|
61
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_SSE41)
|
|
62
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__sse41
|
|
63
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Sse41"
|
|
64
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_AVX2)
|
|
65
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__avx2
|
|
66
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Avx2"
|
|
67
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_AVX512BWVL)
|
|
68
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__avx512bwvl
|
|
69
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Avx512bwvl"
|
|
70
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_NEON)
|
|
71
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__neon
|
|
72
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Neon"
|
|
73
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_SVE)
|
|
74
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__sve
|
|
75
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Sve"
|
|
76
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_SVE2)
|
|
77
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__sve2
|
|
78
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Sve2"
|
|
79
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_LSX)
|
|
80
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__lsx
|
|
81
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Lsx"
|
|
82
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_LASX)
|
|
83
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__lasx
|
|
84
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Lasx"
|
|
85
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_VSX)
|
|
86
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__vsx
|
|
87
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Vsx"
|
|
88
|
+
#elif defined(STRIDE_ALIGN_RUBY_TARGET_RVV)
|
|
89
|
+
#define STRIDE_ALIGN_RUBY_INIT Init__rvv
|
|
90
|
+
#define STRIDE_ALIGN_RUBY_MODULE "Rvv"
|
|
91
|
+
#endif
|
|
92
|
+
|
|
93
|
+
namespace {
|
|
94
|
+
|
|
95
|
+
template <typename Function>
|
|
96
|
+
VALUE guarded(Function&& function) {
|
|
97
|
+
std::array<char, 1024> message{};
|
|
98
|
+
VALUE exception = rb_eRuntimeError;
|
|
99
|
+
try {
|
|
100
|
+
return function();
|
|
101
|
+
} catch (const std::invalid_argument& error) {
|
|
102
|
+
ruby_snprintf(message.data(), message.size(), "%s", error.what());
|
|
103
|
+
exception = rb_eArgError;
|
|
104
|
+
} catch (const std::exception& error) {
|
|
105
|
+
ruby_snprintf(message.data(), message.size(), "%s", error.what());
|
|
106
|
+
} catch (...) {
|
|
107
|
+
ruby_snprintf(message.data(), message.size(), "%s", "unknown C++ exception");
|
|
108
|
+
}
|
|
109
|
+
rb_raise(exception, "stride-align: %s", message.data());
|
|
110
|
+
return Qnil;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
std::string ruby_string(VALUE value, const char* name) {
|
|
114
|
+
if (!RB_TYPE_P(value, T_STRING)) {
|
|
115
|
+
throw std::invalid_argument(std::string(name) + " must be a String");
|
|
116
|
+
}
|
|
117
|
+
return std::string(
|
|
118
|
+
RSTRING_PTR(value), static_cast<std::size_t>(RSTRING_LEN(value)));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#if defined(STRIDE_ALIGN_RUBY_PRIMARY)
|
|
122
|
+
VALUE utf8_string(const std::string& value) {
|
|
123
|
+
return rb_enc_str_new(
|
|
124
|
+
value.data(), static_cast<long>(value.size()), rb_utf8_encoding());
|
|
125
|
+
}
|
|
126
|
+
#endif
|
|
127
|
+
|
|
128
|
+
bool is_numeric(VALUE value) noexcept {
|
|
129
|
+
const int type = TYPE(value);
|
|
130
|
+
return type == T_FIXNUM || type == T_BIGNUM || type == T_FLOAT;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
double numeric_value(VALUE value, const char* name) {
|
|
134
|
+
if (!is_numeric(value)) {
|
|
135
|
+
throw std::invalid_argument(std::string(name) + " must be numeric");
|
|
136
|
+
}
|
|
137
|
+
const double result = NUM2DBL(value);
|
|
138
|
+
if (!std::isfinite(result)) {
|
|
139
|
+
throw std::invalid_argument(std::string(name) + " must be finite");
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
::stride_align::Score score_value(VALUE value, const char* name) {
|
|
145
|
+
const double result = numeric_value(value, name);
|
|
146
|
+
constexpr double kLargestExactInteger = 9007199254740991.0;
|
|
147
|
+
if (std::trunc(result) != result || std::abs(result) > kLargestExactInteger) {
|
|
148
|
+
throw std::invalid_argument(
|
|
149
|
+
std::string(name) + " must be an exactly representable integer");
|
|
150
|
+
}
|
|
151
|
+
return static_cast<::stride_align::Score>(result);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
std::size_t size_value(VALUE value, const char* name) {
|
|
155
|
+
const double result = numeric_value(value, name);
|
|
156
|
+
constexpr double kLargestExactInteger = 9007199254740991.0;
|
|
157
|
+
if (std::trunc(result) != result || result < 0.0 ||
|
|
158
|
+
result > kLargestExactInteger) {
|
|
159
|
+
throw std::invalid_argument(std::string(name) + " must be a non-negative integer");
|
|
160
|
+
}
|
|
161
|
+
return static_cast<std::size_t>(result);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#if defined(STRIDE_ALIGN_RUBY_PRIMARY)
|
|
165
|
+
bool boolean_value(VALUE value, const char* name) {
|
|
166
|
+
if (value != Qtrue && value != Qfalse) {
|
|
167
|
+
throw std::invalid_argument(std::string(name) + " must be true or false");
|
|
168
|
+
}
|
|
169
|
+
return value == Qtrue;
|
|
170
|
+
}
|
|
171
|
+
#endif
|
|
172
|
+
|
|
173
|
+
::stride_align::utf8::PreparedPair prepare_pair(
|
|
174
|
+
const std::string& query,
|
|
175
|
+
const std::string& target) {
|
|
176
|
+
if (!::stride_align::utf8::is_ascii(query) ||
|
|
177
|
+
!::stride_align::utf8::is_ascii(target)) {
|
|
178
|
+
return ::stride_align::utf8::prepare_pair(query, target);
|
|
179
|
+
}
|
|
180
|
+
const auto as_bytes = [](const std::string& value) {
|
|
181
|
+
return std::span<const std::uint8_t>(
|
|
182
|
+
reinterpret_cast<const std::uint8_t*>(value.data()), value.size());
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
::stride_align::utf8::TokenWidth::u8,
|
|
186
|
+
true,
|
|
187
|
+
false,
|
|
188
|
+
as_bytes(query),
|
|
189
|
+
as_bytes(target),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
template <typename Scorer, typename Converter>
|
|
194
|
+
VALUE apply_pair(VALUE query, VALUE target, Scorer&& scorer, Converter&& converter) {
|
|
195
|
+
if (NIL_P(query) || NIL_P(target)) return Qnil;
|
|
196
|
+
const std::string left = ruby_string(query, "query");
|
|
197
|
+
const std::string right = ruby_string(target, "target");
|
|
198
|
+
const auto pair = prepare_pair(left, right);
|
|
199
|
+
return converter(scorer(pair));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
template <typename Scorer>
|
|
203
|
+
VALUE apply_integer_pair(VALUE query, VALUE target, Scorer&& scorer) {
|
|
204
|
+
return apply_pair(
|
|
205
|
+
query, target, std::forward<Scorer>(scorer),
|
|
206
|
+
[](auto value) { return LL2NUM(static_cast<long long>(value)); });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
template <typename Scorer>
|
|
210
|
+
VALUE apply_float_pair(VALUE query, VALUE target, Scorer&& scorer) {
|
|
211
|
+
return apply_pair(
|
|
212
|
+
query, target, std::forward<Scorer>(scorer),
|
|
213
|
+
[](auto value) { return DBL2NUM(static_cast<double>(value)); });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
VALUE rb_levenshtein(VALUE, VALUE query, VALUE target) {
|
|
217
|
+
return guarded([&] {
|
|
218
|
+
return apply_integer_pair(query, target, [](const auto& pair) {
|
|
219
|
+
return ::stride_align::core::levenshtein_distance(pair);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
VALUE rb_levenshtein_similarity(VALUE, VALUE query, VALUE target) {
|
|
225
|
+
return guarded([&] {
|
|
226
|
+
return apply_float_pair(query, target, [](const auto& pair) {
|
|
227
|
+
return ::stride_align::core::levenshtein_similarity(pair);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
VALUE rb_osa(VALUE, VALUE query, VALUE target) {
|
|
233
|
+
return guarded([&] {
|
|
234
|
+
return apply_integer_pair(query, target, [](const auto& pair) {
|
|
235
|
+
return ::stride_align::core::osa_distance(pair);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
VALUE rb_osa_similarity(VALUE, VALUE query, VALUE target) {
|
|
241
|
+
return guarded([&] {
|
|
242
|
+
return apply_float_pair(query, target, [](const auto& pair) {
|
|
243
|
+
return ::stride_align::core::osa_similarity(pair);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
VALUE rb_true_damerau(VALUE, VALUE query, VALUE target) {
|
|
249
|
+
return guarded([&] {
|
|
250
|
+
return apply_integer_pair(query, target, [](const auto& pair) {
|
|
251
|
+
return ::stride_align::core::true_damerau_levenshtein_distance(pair);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
VALUE rb_true_damerau_similarity(VALUE, VALUE query, VALUE target) {
|
|
257
|
+
return guarded([&] {
|
|
258
|
+
return apply_float_pair(query, target, [](const auto& pair) {
|
|
259
|
+
return ::stride_align::core::true_damerau_levenshtein_similarity(pair);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
VALUE rb_indel(VALUE, VALUE query, VALUE target) {
|
|
265
|
+
return guarded([&] {
|
|
266
|
+
return apply_integer_pair(query, target, [](const auto& pair) {
|
|
267
|
+
return ::stride_align::core::indel_distance(pair);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
VALUE rb_indel_similarity(VALUE, VALUE query, VALUE target) {
|
|
273
|
+
return guarded([&] {
|
|
274
|
+
return apply_float_pair(query, target, [](const auto& pair) {
|
|
275
|
+
return ::stride_align::core::indel_similarity(pair);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
VALUE rb_hamming(VALUE, VALUE query, VALUE target) {
|
|
281
|
+
return guarded([&] {
|
|
282
|
+
return apply_integer_pair(query, target, [](const auto& pair) {
|
|
283
|
+
return ::stride_align::core::hamming_distance(pair);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
VALUE rb_hamming_similarity(VALUE, VALUE query, VALUE target) {
|
|
289
|
+
return guarded([&] {
|
|
290
|
+
return apply_float_pair(query, target, [](const auto& pair) {
|
|
291
|
+
return ::stride_align::core::hamming_similarity(pair);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
VALUE rb_jaro(VALUE, VALUE query, VALUE target) {
|
|
297
|
+
return guarded([&] {
|
|
298
|
+
return apply_float_pair(query, target, [](const auto& pair) {
|
|
299
|
+
return ::stride_align::core::jaro_similarity(pair);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
VALUE rb_jaro_winkler(
|
|
305
|
+
VALUE, VALUE query, VALUE target, VALUE weight, VALUE threshold, VALUE cap) {
|
|
306
|
+
return guarded([&] {
|
|
307
|
+
const double prefix_weight = numeric_value(weight, "prefix_weight");
|
|
308
|
+
const double prefix_threshold = numeric_value(threshold, "prefix_threshold");
|
|
309
|
+
const std::size_t prefix_cap = size_value(cap, "prefix_cap");
|
|
310
|
+
return apply_float_pair(query, target, [&](const auto& pair) {
|
|
311
|
+
return ::stride_align::core::jaro_winkler_similarity(
|
|
312
|
+
pair, prefix_weight, prefix_threshold, prefix_cap);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
VALUE rb_smith_waterman(
|
|
318
|
+
VALUE, VALUE query, VALUE target, VALUE match, VALUE mismatch, VALUE gap) {
|
|
319
|
+
return guarded([&] {
|
|
320
|
+
const auto match_score = score_value(match, "match_score");
|
|
321
|
+
const auto mismatch_score = score_value(mismatch, "mismatch_score");
|
|
322
|
+
const auto gap_score = score_value(gap, "gap_score");
|
|
323
|
+
return apply_integer_pair(query, target, [&](const auto& pair) {
|
|
324
|
+
return ::stride_align::core::smith_waterman_score(
|
|
325
|
+
pair, match_score, mismatch_score, gap_score);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
VALUE rb_needleman_wunsch(
|
|
331
|
+
VALUE, VALUE query, VALUE target, VALUE match, VALUE mismatch, VALUE gap) {
|
|
332
|
+
return guarded([&] {
|
|
333
|
+
const auto match_score = score_value(match, "match_score");
|
|
334
|
+
const auto mismatch_score = score_value(mismatch, "mismatch_score");
|
|
335
|
+
const auto gap_score = score_value(gap, "gap_score");
|
|
336
|
+
return apply_integer_pair(query, target, [&](const auto& pair) {
|
|
337
|
+
return ::stride_align::core::needleman_wunsch_score(
|
|
338
|
+
pair, match_score, mismatch_score, gap_score);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
VALUE rb_smith_waterman_affine(
|
|
344
|
+
VALUE, VALUE query, VALUE target, VALUE match, VALUE mismatch,
|
|
345
|
+
VALUE gap_open, VALUE gap_extend) {
|
|
346
|
+
return guarded([&] {
|
|
347
|
+
const auto match_score = score_value(match, "match_score");
|
|
348
|
+
const auto mismatch_score = score_value(mismatch, "mismatch_score");
|
|
349
|
+
const auto open_score = score_value(gap_open, "gap_open_score");
|
|
350
|
+
const auto extend_score = score_value(gap_extend, "gap_extend_score");
|
|
351
|
+
return apply_integer_pair(query, target, [&](const auto& pair) {
|
|
352
|
+
return ::stride_align::core::smith_waterman_affine_score(
|
|
353
|
+
pair, match_score, mismatch_score, open_score, extend_score);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
VALUE rb_needleman_wunsch_affine(
|
|
359
|
+
VALUE, VALUE query, VALUE target, VALUE match, VALUE mismatch,
|
|
360
|
+
VALUE gap_open, VALUE gap_extend) {
|
|
361
|
+
return guarded([&] {
|
|
362
|
+
const auto match_score = score_value(match, "match_score");
|
|
363
|
+
const auto mismatch_score = score_value(mismatch, "mismatch_score");
|
|
364
|
+
const auto open_score = score_value(gap_open, "gap_open_score");
|
|
365
|
+
const auto extend_score = score_value(gap_extend, "gap_extend_score");
|
|
366
|
+
return apply_integer_pair(query, target, [&](const auto& pair) {
|
|
367
|
+
return ::stride_align::core::needleman_wunsch_affine_score(
|
|
368
|
+
pair, match_score, mismatch_score, open_score, extend_score);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
#if defined(STRIDE_ALIGN_RUBY_PRIMARY)
|
|
374
|
+
|
|
375
|
+
std::vector<std::uint32_t> codepoints(const std::string& input) {
|
|
376
|
+
return ::stride_align::utf8::prepare_streaming(input);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
std::string encode_utf8(std::span<const std::uint32_t> input) {
|
|
380
|
+
std::string output;
|
|
381
|
+
output.reserve(input.size());
|
|
382
|
+
for (const std::uint32_t value : input) {
|
|
383
|
+
if (value <= 0x7fU) {
|
|
384
|
+
output.push_back(static_cast<char>(value));
|
|
385
|
+
} else if (value <= 0x7ffU) {
|
|
386
|
+
output.push_back(static_cast<char>(0xc0U | (value >> 6U)));
|
|
387
|
+
output.push_back(static_cast<char>(0x80U | (value & 0x3fU)));
|
|
388
|
+
} else if (value <= 0xffffU) {
|
|
389
|
+
output.push_back(static_cast<char>(0xe0U | (value >> 12U)));
|
|
390
|
+
output.push_back(static_cast<char>(0x80U | ((value >> 6U) & 0x3fU)));
|
|
391
|
+
output.push_back(static_cast<char>(0x80U | (value & 0x3fU)));
|
|
392
|
+
} else {
|
|
393
|
+
output.push_back(static_cast<char>(0xf0U | (value >> 18U)));
|
|
394
|
+
output.push_back(static_cast<char>(0x80U | ((value >> 12U) & 0x3fU)));
|
|
395
|
+
output.push_back(static_cast<char>(0x80U | ((value >> 6U) & 0x3fU)));
|
|
396
|
+
output.push_back(static_cast<char>(0x80U | (value & 0x3fU)));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return output;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
template <typename Function, typename Converter>
|
|
403
|
+
VALUE apply_codepoint_pair(
|
|
404
|
+
VALUE query, VALUE target, Function&& function, Converter&& converter) {
|
|
405
|
+
if (NIL_P(query) || NIL_P(target)) return Qnil;
|
|
406
|
+
const auto left = codepoints(ruby_string(query, "query"));
|
|
407
|
+
const auto right = codepoints(ruby_string(target, "target"));
|
|
408
|
+
return converter(function(left, right));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
VALUE rb_lcs_length(VALUE, VALUE query, VALUE target) {
|
|
412
|
+
return guarded([&] {
|
|
413
|
+
return apply_codepoint_pair(
|
|
414
|
+
query, target,
|
|
415
|
+
[](const auto& left, const auto& right) {
|
|
416
|
+
return ::stride_align::lcs::lcs_length(left, right);
|
|
417
|
+
},
|
|
418
|
+
[](auto value) { return ULL2NUM(static_cast<unsigned long long>(value)); });
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
VALUE rb_lcs_substring_length(VALUE, VALUE query, VALUE target) {
|
|
423
|
+
return guarded([&] {
|
|
424
|
+
return apply_codepoint_pair(
|
|
425
|
+
query, target,
|
|
426
|
+
[](const auto& left, const auto& right) {
|
|
427
|
+
return ::stride_align::lcs::lcs_substring_length(left, right);
|
|
428
|
+
},
|
|
429
|
+
[](auto value) { return ULL2NUM(static_cast<unsigned long long>(value)); });
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
VALUE rb_lcs_substring(VALUE, VALUE query, VALUE target) {
|
|
434
|
+
return guarded([&] {
|
|
435
|
+
return apply_codepoint_pair(
|
|
436
|
+
query, target,
|
|
437
|
+
[](const auto& left, const auto& right) {
|
|
438
|
+
return ::stride_align::lcs::lcs_substring(left, right);
|
|
439
|
+
},
|
|
440
|
+
[](const auto& value) { return utf8_string(encode_utf8(value)); });
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
enum class NgramMetric { jaccard, dice, cosine, overlap };
|
|
445
|
+
|
|
446
|
+
VALUE rb_ngram(VALUE query, VALUE target, VALUE n, NgramMetric metric) {
|
|
447
|
+
const std::size_t size = size_value(n, "n");
|
|
448
|
+
if (size == 0U) throw std::invalid_argument("n must be positive");
|
|
449
|
+
return apply_codepoint_pair(
|
|
450
|
+
query, target,
|
|
451
|
+
[&](const auto& left, const auto& right) {
|
|
452
|
+
switch (metric) {
|
|
453
|
+
case NgramMetric::jaccard:
|
|
454
|
+
return ::stride_align::ngram::jaccard(left, right, size);
|
|
455
|
+
case NgramMetric::dice:
|
|
456
|
+
return ::stride_align::ngram::dice(left, right, size);
|
|
457
|
+
case NgramMetric::cosine:
|
|
458
|
+
return ::stride_align::ngram::cosine(left, right, size);
|
|
459
|
+
case NgramMetric::overlap:
|
|
460
|
+
return ::stride_align::ngram::overlap(left, right, size);
|
|
461
|
+
}
|
|
462
|
+
return 0.0;
|
|
463
|
+
},
|
|
464
|
+
[](double value) { return DBL2NUM(value); });
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
VALUE rb_jaccard(VALUE, VALUE query, VALUE target, VALUE n) {
|
|
468
|
+
return guarded([&] { return rb_ngram(query, target, n, NgramMetric::jaccard); });
|
|
469
|
+
}
|
|
470
|
+
VALUE rb_dice(VALUE, VALUE query, VALUE target, VALUE n) {
|
|
471
|
+
return guarded([&] { return rb_ngram(query, target, n, NgramMetric::dice); });
|
|
472
|
+
}
|
|
473
|
+
VALUE rb_cosine(VALUE, VALUE query, VALUE target, VALUE n) {
|
|
474
|
+
return guarded([&] { return rb_ngram(query, target, n, NgramMetric::cosine); });
|
|
475
|
+
}
|
|
476
|
+
VALUE rb_overlap(VALUE, VALUE query, VALUE target, VALUE n) {
|
|
477
|
+
return guarded([&] { return rb_ngram(query, target, n, NgramMetric::overlap); });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
VALUE rb_ratcliff_obershelp(VALUE, VALUE query, VALUE target) {
|
|
481
|
+
return guarded([&] {
|
|
482
|
+
return apply_codepoint_pair(
|
|
483
|
+
query, target,
|
|
484
|
+
[](const auto& left, const auto& right) {
|
|
485
|
+
return ::stride_align::ratcliff_obershelp::
|
|
486
|
+
ratcliff_obershelp_similarity(left, right);
|
|
487
|
+
},
|
|
488
|
+
[](double value) { return DBL2NUM(value); });
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
enum class TokenMetric {
|
|
493
|
+
partial,
|
|
494
|
+
token_sort,
|
|
495
|
+
token_set,
|
|
496
|
+
partial_token_sort,
|
|
497
|
+
partial_token_set,
|
|
498
|
+
weighted,
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
VALUE rb_token_metric(VALUE query, VALUE target, TokenMetric metric) {
|
|
502
|
+
return apply_codepoint_pair(
|
|
503
|
+
query, target,
|
|
504
|
+
[&](const auto& left, const auto& right) {
|
|
505
|
+
const auto left_span =
|
|
506
|
+
std::span<const std::uint32_t>(left.data(), left.size());
|
|
507
|
+
const auto right_span =
|
|
508
|
+
std::span<const std::uint32_t>(right.data(), right.size());
|
|
509
|
+
switch (metric) {
|
|
510
|
+
case TokenMetric::partial:
|
|
511
|
+
return ::stride_align::partial_ratio::partial_ratio(left, right);
|
|
512
|
+
case TokenMetric::token_sort:
|
|
513
|
+
return ::stride_align::token_ratios::token_sort_ratio(left, right);
|
|
514
|
+
case TokenMetric::token_set:
|
|
515
|
+
return ::stride_align::token_ratios::token_set_ratio(left, right);
|
|
516
|
+
case TokenMetric::partial_token_sort:
|
|
517
|
+
return ::stride_align::wratio::
|
|
518
|
+
partial_token_sort_ratio_engine<std::uint32_t>(left_span, right_span);
|
|
519
|
+
case TokenMetric::partial_token_set:
|
|
520
|
+
return ::stride_align::wratio::
|
|
521
|
+
partial_token_set_ratio_engine<std::uint32_t>(left_span, right_span);
|
|
522
|
+
case TokenMetric::weighted:
|
|
523
|
+
return ::stride_align::wratio::native_wratio(left, right);
|
|
524
|
+
}
|
|
525
|
+
return 0.0;
|
|
526
|
+
},
|
|
527
|
+
[](double value) { return DBL2NUM(value); });
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#define STRIDE_ALIGN_RUBY_TOKEN_METHOD(name, kind) \
|
|
531
|
+
VALUE name(VALUE, VALUE query, VALUE target) { \
|
|
532
|
+
return guarded([&] { return rb_token_metric(query, target, kind); }); \
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
STRIDE_ALIGN_RUBY_TOKEN_METHOD(rb_partial_ratio, TokenMetric::partial)
|
|
536
|
+
STRIDE_ALIGN_RUBY_TOKEN_METHOD(rb_token_sort_ratio, TokenMetric::token_sort)
|
|
537
|
+
STRIDE_ALIGN_RUBY_TOKEN_METHOD(rb_token_set_ratio, TokenMetric::token_set)
|
|
538
|
+
STRIDE_ALIGN_RUBY_TOKEN_METHOD(
|
|
539
|
+
rb_partial_token_sort_ratio, TokenMetric::partial_token_sort)
|
|
540
|
+
STRIDE_ALIGN_RUBY_TOKEN_METHOD(
|
|
541
|
+
rb_partial_token_set_ratio, TokenMetric::partial_token_set)
|
|
542
|
+
STRIDE_ALIGN_RUBY_TOKEN_METHOD(rb_wratio, TokenMetric::weighted)
|
|
543
|
+
|
|
544
|
+
#undef STRIDE_ALIGN_RUBY_TOKEN_METHOD
|
|
545
|
+
|
|
546
|
+
template <typename Function>
|
|
547
|
+
VALUE apply_unary_string(VALUE input, Function&& function) {
|
|
548
|
+
if (NIL_P(input)) return Qnil;
|
|
549
|
+
return utf8_string(function(ruby_string(input, "input")));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
VALUE rb_soundex(VALUE, VALUE input) {
|
|
553
|
+
return guarded([&] {
|
|
554
|
+
return apply_unary_string(input, [](const std::string& value) {
|
|
555
|
+
return ::stride_align::phonetic::soundex(value);
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
VALUE rb_soundex_equal(VALUE, VALUE query, VALUE target) {
|
|
561
|
+
return guarded([&] {
|
|
562
|
+
if (NIL_P(query) || NIL_P(target)) return Qnil;
|
|
563
|
+
return ::stride_align::phonetic::soundex(ruby_string(query, "query")) ==
|
|
564
|
+
::stride_align::phonetic::soundex(ruby_string(target, "target"))
|
|
565
|
+
? Qtrue
|
|
566
|
+
: Qfalse;
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
::stride_align::phonetic::MetaphoneVariant metaphone_variant(VALUE value) {
|
|
571
|
+
const auto variant = static_cast<::stride_align::phonetic::MetaphoneVariant>(
|
|
572
|
+
size_value(value, "variant"));
|
|
573
|
+
if (variant != ::stride_align::phonetic::MetaphoneVariant::kPhilips &&
|
|
574
|
+
variant != ::stride_align::phonetic::MetaphoneVariant::kJellyfish) {
|
|
575
|
+
throw std::invalid_argument("variant must be 0 or 1");
|
|
576
|
+
}
|
|
577
|
+
return variant;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
VALUE rb_metaphone(VALUE, VALUE input, VALUE variant) {
|
|
581
|
+
return guarded([&] {
|
|
582
|
+
const auto selected = metaphone_variant(variant);
|
|
583
|
+
return apply_unary_string(input, [&](const std::string& value) {
|
|
584
|
+
return ::stride_align::phonetic::metaphone(value, selected);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
VALUE rb_metaphone_equal(VALUE, VALUE query, VALUE target, VALUE variant) {
|
|
590
|
+
return guarded([&] {
|
|
591
|
+
if (NIL_P(query) || NIL_P(target)) return Qnil;
|
|
592
|
+
const auto selected = metaphone_variant(variant);
|
|
593
|
+
return ::stride_align::phonetic::metaphone(
|
|
594
|
+
ruby_string(query, "query"), selected) ==
|
|
595
|
+
::stride_align::phonetic::metaphone(
|
|
596
|
+
ruby_string(target, "target"), selected)
|
|
597
|
+
? Qtrue
|
|
598
|
+
: Qfalse;
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
VALUE rb_nysiis(VALUE, VALUE input) {
|
|
603
|
+
return guarded([&] {
|
|
604
|
+
return apply_unary_string(input, [](const std::string& value) {
|
|
605
|
+
return ::stride_align::phonetic::nysiis(value);
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
VALUE rb_nysiis_equal(VALUE, VALUE query, VALUE target) {
|
|
611
|
+
return guarded([&] {
|
|
612
|
+
if (NIL_P(query) || NIL_P(target)) return Qnil;
|
|
613
|
+
return ::stride_align::phonetic::nysiis(ruby_string(query, "query")) ==
|
|
614
|
+
::stride_align::phonetic::nysiis(ruby_string(target, "target"))
|
|
615
|
+
? Qtrue
|
|
616
|
+
: Qfalse;
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
VALUE rb_match_rating_codex(VALUE, VALUE input) {
|
|
621
|
+
return guarded([&] {
|
|
622
|
+
return apply_unary_string(input, [](const std::string& value) {
|
|
623
|
+
return ::stride_align::phonetic::match_rating_codex(value);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
VALUE rb_match_rating_compare(VALUE, VALUE query, VALUE target) {
|
|
629
|
+
return guarded([&] {
|
|
630
|
+
if (NIL_P(query) || NIL_P(target)) return Qnil;
|
|
631
|
+
return ::stride_align::phonetic::match_rating_compare(
|
|
632
|
+
ruby_string(query, "query"), ruby_string(target, "target"))
|
|
633
|
+
? Qtrue
|
|
634
|
+
: Qfalse;
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
VALUE rb_caverphone(VALUE, VALUE input) {
|
|
639
|
+
return guarded([&] {
|
|
640
|
+
return apply_unary_string(input, [](const std::string& value) {
|
|
641
|
+
return ::stride_align::phonetic::caverphone(value);
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
VALUE rb_cologne_phonetic(VALUE, VALUE input) {
|
|
647
|
+
return guarded([&] {
|
|
648
|
+
if (NIL_P(input)) return Qnil;
|
|
649
|
+
return utf8_string(::stride_align::phonetic::cologne_phonetic(
|
|
650
|
+
codepoints(ruby_string(input, "input"))));
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
VALUE rb_daitch_mokotoff(
|
|
655
|
+
VALUE, VALUE input, VALUE branching, VALUE folding) {
|
|
656
|
+
return guarded([&] {
|
|
657
|
+
if (NIL_P(input)) return Qnil;
|
|
658
|
+
return utf8_string(::stride_align::phonetic::daitch_mokotoff(
|
|
659
|
+
codepoints(ruby_string(input, "input")),
|
|
660
|
+
boolean_value(branching, "branching"),
|
|
661
|
+
boolean_value(folding, "folding")));
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
VALUE rb_double_metaphone(
|
|
666
|
+
VALUE, VALUE input, VALUE max_length, VALUE variant) {
|
|
667
|
+
return guarded([&] {
|
|
668
|
+
if (NIL_P(input)) return Qnil;
|
|
669
|
+
const auto selected =
|
|
670
|
+
static_cast<::stride_align::phonetic::DoubleMetaphoneVariant>(
|
|
671
|
+
size_value(variant, "variant"));
|
|
672
|
+
if (selected !=
|
|
673
|
+
::stride_align::phonetic::DoubleMetaphoneVariant::kCommons &&
|
|
674
|
+
selected !=
|
|
675
|
+
::stride_align::phonetic::DoubleMetaphoneVariant::kPython) {
|
|
676
|
+
throw std::invalid_argument("variant must be 0 or 1");
|
|
677
|
+
}
|
|
678
|
+
const auto result = ::stride_align::phonetic::double_metaphone(
|
|
679
|
+
ruby_string(input, "input"), size_value(max_length, "max_length"), selected);
|
|
680
|
+
return rb_ary_new_from_args(
|
|
681
|
+
2, utf8_string(result.primary), utf8_string(result.alternate));
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
VALUE rb_bmpm_register(VALUE, VALUE resources) {
|
|
686
|
+
return guarded([&] {
|
|
687
|
+
if (!RB_TYPE_P(resources, T_HASH)) {
|
|
688
|
+
throw std::invalid_argument("BMPM resources must be a Hash");
|
|
689
|
+
}
|
|
690
|
+
const ID keys_method = rb_intern("keys");
|
|
691
|
+
VALUE keys = rb_funcall(resources, keys_method, 0);
|
|
692
|
+
std::unordered_map<std::string, std::string> table;
|
|
693
|
+
table.reserve(static_cast<std::size_t>(RARRAY_LEN(keys)));
|
|
694
|
+
for (long index = 0; index < RARRAY_LEN(keys); ++index) {
|
|
695
|
+
VALUE key = rb_ary_entry(keys, index);
|
|
696
|
+
VALUE value = rb_hash_aref(resources, key);
|
|
697
|
+
table.emplace(
|
|
698
|
+
ruby_string(key, "BMPM resource name"),
|
|
699
|
+
ruby_string(value, "BMPM resource value"));
|
|
700
|
+
}
|
|
701
|
+
::stride_align::phonetic::bmpm_register_resources(table);
|
|
702
|
+
return Qnil;
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
VALUE rb_beider_morse(
|
|
707
|
+
VALUE, VALUE input, VALUE rule_type, VALUE concat, VALUE max_phonemes) {
|
|
708
|
+
return guarded([&] {
|
|
709
|
+
if (NIL_P(input)) return Qnil;
|
|
710
|
+
const auto rule = static_cast<::stride_align::phonetic::BmpmRuleType>(
|
|
711
|
+
size_value(rule_type, "rule_type"));
|
|
712
|
+
if (rule != ::stride_align::phonetic::BmpmRuleType::kApprox &&
|
|
713
|
+
rule != ::stride_align::phonetic::BmpmRuleType::kExact) {
|
|
714
|
+
throw std::invalid_argument("rule_type must be 0 or 1");
|
|
715
|
+
}
|
|
716
|
+
return utf8_string(::stride_align::phonetic::beider_morse(
|
|
717
|
+
codepoints(ruby_string(input, "input")), rule,
|
|
718
|
+
boolean_value(concat, "concat"),
|
|
719
|
+
size_value(max_phonemes, "max_phonemes")));
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
std::vector<double> numeric_sequence(VALUE value, const char* name) {
|
|
724
|
+
if (!RB_TYPE_P(value, T_ARRAY)) {
|
|
725
|
+
throw std::invalid_argument(std::string(name) + " must be an Array of numbers");
|
|
726
|
+
}
|
|
727
|
+
const long length = RARRAY_LEN(value);
|
|
728
|
+
if (length == 0) {
|
|
729
|
+
throw std::invalid_argument(std::string(name) + " must be non-empty");
|
|
730
|
+
}
|
|
731
|
+
std::vector<double> output(static_cast<std::size_t>(length));
|
|
732
|
+
for (long index = 0; index < length; ++index) {
|
|
733
|
+
output[static_cast<std::size_t>(index)] =
|
|
734
|
+
numeric_value(rb_ary_entry(value, index), name);
|
|
735
|
+
}
|
|
736
|
+
return output;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
std::optional<std::size_t> dtw_window(
|
|
740
|
+
VALUE value, std::size_t query_size, std::size_t target_size) {
|
|
741
|
+
if (NIL_P(value)) return std::nullopt;
|
|
742
|
+
const double number = numeric_value(value, "window");
|
|
743
|
+
if (number < 0.0) throw std::invalid_argument("window must be non-negative");
|
|
744
|
+
if (number > 0.0 && number < 1.0) {
|
|
745
|
+
return static_cast<std::size_t>(std::ceil(
|
|
746
|
+
number * static_cast<double>(std::max(query_size, target_size))));
|
|
747
|
+
}
|
|
748
|
+
if (std::trunc(number) != number) {
|
|
749
|
+
throw std::invalid_argument(
|
|
750
|
+
"window must be an integer radius or a fraction in (0, 1)");
|
|
751
|
+
}
|
|
752
|
+
return static_cast<std::size_t>(number);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
VALUE rb_dtw(
|
|
756
|
+
VALUE, VALUE query, VALUE target, VALUE window, VALUE distance,
|
|
757
|
+
VALUE score_cutoff) {
|
|
758
|
+
return guarded([&] {
|
|
759
|
+
const auto left = numeric_sequence(query, "query");
|
|
760
|
+
const auto right = numeric_sequence(target, "target");
|
|
761
|
+
const std::string metric = ruby_string(distance, "distance");
|
|
762
|
+
if (metric != "l1" && metric != "l2_squared") {
|
|
763
|
+
throw std::invalid_argument("distance must be 'l1' or 'l2_squared'");
|
|
764
|
+
}
|
|
765
|
+
const auto kind = metric == "l1"
|
|
766
|
+
? ::stride_align::dtw::DistanceKind::kL1
|
|
767
|
+
: ::stride_align::dtw::DistanceKind::kL2Squared;
|
|
768
|
+
std::optional<double> cutoff;
|
|
769
|
+
if (!NIL_P(score_cutoff)) {
|
|
770
|
+
cutoff = numeric_value(score_cutoff, "score_cutoff");
|
|
771
|
+
if (*cutoff < 0.0) {
|
|
772
|
+
throw std::invalid_argument("score_cutoff must be non-negative");
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
const double result = ::stride_align::dtw::dtw_score_scalar<double, double>(
|
|
776
|
+
left, right, kind, dtw_window(window, left.size(), right.size()), cutoff);
|
|
777
|
+
return DBL2NUM(result);
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
VALUE hash_set(VALUE hash, const char* name, VALUE value) {
|
|
782
|
+
const ID name_id = rb_intern(name);
|
|
783
|
+
rb_hash_aset(hash, ID2SYM(name_id), value);
|
|
784
|
+
return hash;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
VALUE alignment_path_to_ruby(
|
|
788
|
+
const ::stride_align::AlignmentPath& path,
|
|
789
|
+
std::span<const std::uint32_t> query,
|
|
790
|
+
std::span<const std::uint32_t> target) {
|
|
791
|
+
VALUE result = rb_hash_new();
|
|
792
|
+
hash_set(result, "score", LL2NUM(static_cast<long long>(path.score)));
|
|
793
|
+
hash_set(result, "query_start", ULL2NUM(path.query_start));
|
|
794
|
+
hash_set(result, "query_end", ULL2NUM(path.query_end));
|
|
795
|
+
hash_set(result, "target_start", ULL2NUM(path.target_start));
|
|
796
|
+
hash_set(result, "target_end", ULL2NUM(path.target_end));
|
|
797
|
+
hash_set(result, "operations", utf8_string(path.operations));
|
|
798
|
+
hash_set(result, "cigar", utf8_string(path.cigar));
|
|
799
|
+
hash_set(result, "matches", ULL2NUM(path.matches));
|
|
800
|
+
hash_set(result, "mismatches", ULL2NUM(path.mismatches));
|
|
801
|
+
hash_set(result, "insertions", ULL2NUM(path.insertions));
|
|
802
|
+
hash_set(result, "deletions", ULL2NUM(path.deletions));
|
|
803
|
+
hash_set(result, "aligned_length", ULL2NUM(path.aligned_length));
|
|
804
|
+
|
|
805
|
+
std::vector<std::uint32_t> aligned_query;
|
|
806
|
+
std::vector<std::uint32_t> aligned_target;
|
|
807
|
+
aligned_query.reserve(path.operations.size());
|
|
808
|
+
aligned_target.reserve(path.operations.size());
|
|
809
|
+
std::size_t query_index = path.query_start;
|
|
810
|
+
std::size_t target_index = path.target_start;
|
|
811
|
+
constexpr std::uint32_t kGap = static_cast<std::uint32_t>('-');
|
|
812
|
+
for (const char operation : path.operations) {
|
|
813
|
+
if (operation == '=' || operation == 'X') {
|
|
814
|
+
aligned_query.push_back(query[query_index++]);
|
|
815
|
+
aligned_target.push_back(target[target_index++]);
|
|
816
|
+
} else if (operation == 'D') {
|
|
817
|
+
aligned_query.push_back(query[query_index++]);
|
|
818
|
+
aligned_target.push_back(kGap);
|
|
819
|
+
} else {
|
|
820
|
+
aligned_query.push_back(kGap);
|
|
821
|
+
aligned_target.push_back(target[target_index++]);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
VALUE aligned = rb_hash_new();
|
|
825
|
+
hash_set(aligned, "query", utf8_string(encode_utf8(aligned_query)));
|
|
826
|
+
hash_set(aligned, "target", utf8_string(encode_utf8(aligned_target)));
|
|
827
|
+
hash_set(result, "aligned", aligned);
|
|
828
|
+
return result;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
VALUE rb_alignment_path(
|
|
832
|
+
VALUE, VALUE query, VALUE target, VALUE local, VALUE match,
|
|
833
|
+
VALUE mismatch, VALUE gap_open, VALUE gap_extend) {
|
|
834
|
+
return guarded([&] {
|
|
835
|
+
const std::string left = ruby_string(query, "query");
|
|
836
|
+
const std::string right = ruby_string(target, "target");
|
|
837
|
+
const auto pair = prepare_pair(left, right);
|
|
838
|
+
const bool is_local = boolean_value(local, "local");
|
|
839
|
+
const auto match_score = score_value(match, "match_score");
|
|
840
|
+
const auto mismatch_score = score_value(mismatch, "mismatch_score");
|
|
841
|
+
const auto open_score = score_value(gap_open, "gap_open_score");
|
|
842
|
+
const auto extend_score = score_value(gap_extend, "gap_extend_score");
|
|
843
|
+
::stride_align::AlignmentPath path;
|
|
844
|
+
if (open_score == extend_score) {
|
|
845
|
+
path = is_local
|
|
846
|
+
? ::stride_align::core::smith_waterman_path(
|
|
847
|
+
pair, match_score, mismatch_score, open_score)
|
|
848
|
+
: ::stride_align::core::needleman_wunsch_path(
|
|
849
|
+
pair, match_score, mismatch_score, open_score);
|
|
850
|
+
} else {
|
|
851
|
+
path = is_local
|
|
852
|
+
? ::stride_align::core::smith_waterman_affine_path(
|
|
853
|
+
pair, match_score, mismatch_score, open_score, extend_score)
|
|
854
|
+
: ::stride_align::core::needleman_wunsch_affine_path(
|
|
855
|
+
pair, match_score, mismatch_score, open_score, extend_score);
|
|
856
|
+
}
|
|
857
|
+
const auto left_points = codepoints(left);
|
|
858
|
+
const auto right_points = codepoints(right);
|
|
859
|
+
return alignment_path_to_ruby(path, left_points, right_points);
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
VALUE rb_backend_candidates(VALUE) {
|
|
864
|
+
return guarded([&] {
|
|
865
|
+
const auto candidates = stride_align_ruby_backend_candidates();
|
|
866
|
+
VALUE result = rb_ary_new_capa(static_cast<long>(candidates.size()));
|
|
867
|
+
for (const auto& candidate : candidates) {
|
|
868
|
+
rb_ary_push(result, utf8_string(candidate));
|
|
869
|
+
}
|
|
870
|
+
return result;
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
#endif // STRIDE_ALIGN_RUBY_PRIMARY
|
|
875
|
+
|
|
876
|
+
void define_backend_methods(VALUE backend) {
|
|
877
|
+
rb_define_singleton_method(backend, "levenshtein", RUBY_METHOD_FUNC(rb_levenshtein), 2);
|
|
878
|
+
rb_define_singleton_method(
|
|
879
|
+
backend, "levenshtein_similarity",
|
|
880
|
+
RUBY_METHOD_FUNC(rb_levenshtein_similarity), 2);
|
|
881
|
+
rb_define_singleton_method(backend, "osa", RUBY_METHOD_FUNC(rb_osa), 2);
|
|
882
|
+
rb_define_singleton_method(
|
|
883
|
+
backend, "osa_similarity", RUBY_METHOD_FUNC(rb_osa_similarity), 2);
|
|
884
|
+
rb_define_singleton_method(
|
|
885
|
+
backend, "true_damerau_levenshtein", RUBY_METHOD_FUNC(rb_true_damerau), 2);
|
|
886
|
+
rb_define_singleton_method(
|
|
887
|
+
backend, "true_damerau_levenshtein_similarity",
|
|
888
|
+
RUBY_METHOD_FUNC(rb_true_damerau_similarity), 2);
|
|
889
|
+
rb_define_singleton_method(backend, "indel", RUBY_METHOD_FUNC(rb_indel), 2);
|
|
890
|
+
rb_define_singleton_method(
|
|
891
|
+
backend, "indel_similarity", RUBY_METHOD_FUNC(rb_indel_similarity), 2);
|
|
892
|
+
rb_define_singleton_method(backend, "hamming", RUBY_METHOD_FUNC(rb_hamming), 2);
|
|
893
|
+
rb_define_singleton_method(
|
|
894
|
+
backend, "hamming_similarity", RUBY_METHOD_FUNC(rb_hamming_similarity), 2);
|
|
895
|
+
rb_define_singleton_method(backend, "jaro", RUBY_METHOD_FUNC(rb_jaro), 2);
|
|
896
|
+
rb_define_singleton_method(
|
|
897
|
+
backend, "jaro_winkler", RUBY_METHOD_FUNC(rb_jaro_winkler), 5);
|
|
898
|
+
rb_define_singleton_method(
|
|
899
|
+
backend, "smith_waterman", RUBY_METHOD_FUNC(rb_smith_waterman), 5);
|
|
900
|
+
rb_define_singleton_method(
|
|
901
|
+
backend, "needleman_wunsch", RUBY_METHOD_FUNC(rb_needleman_wunsch), 5);
|
|
902
|
+
rb_define_singleton_method(
|
|
903
|
+
backend, "smith_waterman_affine",
|
|
904
|
+
RUBY_METHOD_FUNC(rb_smith_waterman_affine), 6);
|
|
905
|
+
rb_define_singleton_method(
|
|
906
|
+
backend, "needleman_wunsch_affine",
|
|
907
|
+
RUBY_METHOD_FUNC(rb_needleman_wunsch_affine), 6);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
#if defined(STRIDE_ALIGN_RUBY_PRIMARY)
|
|
911
|
+
void define_primary_methods(VALUE backend) {
|
|
912
|
+
rb_define_singleton_method(
|
|
913
|
+
backend, "backend_candidates", RUBY_METHOD_FUNC(rb_backend_candidates), 0);
|
|
914
|
+
rb_define_singleton_method(backend, "lcs_length", RUBY_METHOD_FUNC(rb_lcs_length), 2);
|
|
915
|
+
rb_define_singleton_method(
|
|
916
|
+
backend, "lcs_substring_length", RUBY_METHOD_FUNC(rb_lcs_substring_length), 2);
|
|
917
|
+
rb_define_singleton_method(
|
|
918
|
+
backend, "lcs_substring", RUBY_METHOD_FUNC(rb_lcs_substring), 2);
|
|
919
|
+
rb_define_singleton_method(backend, "jaccard", RUBY_METHOD_FUNC(rb_jaccard), 3);
|
|
920
|
+
rb_define_singleton_method(backend, "dice", RUBY_METHOD_FUNC(rb_dice), 3);
|
|
921
|
+
rb_define_singleton_method(backend, "cosine", RUBY_METHOD_FUNC(rb_cosine), 3);
|
|
922
|
+
rb_define_singleton_method(backend, "overlap", RUBY_METHOD_FUNC(rb_overlap), 3);
|
|
923
|
+
rb_define_singleton_method(
|
|
924
|
+
backend, "ratcliff_obershelp", RUBY_METHOD_FUNC(rb_ratcliff_obershelp), 2);
|
|
925
|
+
rb_define_singleton_method(
|
|
926
|
+
backend, "partial_ratio", RUBY_METHOD_FUNC(rb_partial_ratio), 2);
|
|
927
|
+
rb_define_singleton_method(
|
|
928
|
+
backend, "token_sort_ratio", RUBY_METHOD_FUNC(rb_token_sort_ratio), 2);
|
|
929
|
+
rb_define_singleton_method(
|
|
930
|
+
backend, "token_set_ratio", RUBY_METHOD_FUNC(rb_token_set_ratio), 2);
|
|
931
|
+
rb_define_singleton_method(
|
|
932
|
+
backend, "partial_token_sort_ratio",
|
|
933
|
+
RUBY_METHOD_FUNC(rb_partial_token_sort_ratio), 2);
|
|
934
|
+
rb_define_singleton_method(
|
|
935
|
+
backend, "partial_token_set_ratio",
|
|
936
|
+
RUBY_METHOD_FUNC(rb_partial_token_set_ratio), 2);
|
|
937
|
+
rb_define_singleton_method(backend, "wratio", RUBY_METHOD_FUNC(rb_wratio), 2);
|
|
938
|
+
rb_define_singleton_method(backend, "soundex", RUBY_METHOD_FUNC(rb_soundex), 1);
|
|
939
|
+
rb_define_singleton_method(
|
|
940
|
+
backend, "soundex_equal", RUBY_METHOD_FUNC(rb_soundex_equal), 2);
|
|
941
|
+
rb_define_singleton_method(backend, "metaphone", RUBY_METHOD_FUNC(rb_metaphone), 2);
|
|
942
|
+
rb_define_singleton_method(
|
|
943
|
+
backend, "metaphone_equal", RUBY_METHOD_FUNC(rb_metaphone_equal), 3);
|
|
944
|
+
rb_define_singleton_method(backend, "nysiis", RUBY_METHOD_FUNC(rb_nysiis), 1);
|
|
945
|
+
rb_define_singleton_method(
|
|
946
|
+
backend, "nysiis_equal", RUBY_METHOD_FUNC(rb_nysiis_equal), 2);
|
|
947
|
+
rb_define_singleton_method(
|
|
948
|
+
backend, "match_rating_codex", RUBY_METHOD_FUNC(rb_match_rating_codex), 1);
|
|
949
|
+
rb_define_singleton_method(
|
|
950
|
+
backend, "match_rating_compare", RUBY_METHOD_FUNC(rb_match_rating_compare), 2);
|
|
951
|
+
rb_define_singleton_method(backend, "caverphone", RUBY_METHOD_FUNC(rb_caverphone), 1);
|
|
952
|
+
rb_define_singleton_method(
|
|
953
|
+
backend, "cologne_phonetic", RUBY_METHOD_FUNC(rb_cologne_phonetic), 1);
|
|
954
|
+
rb_define_singleton_method(
|
|
955
|
+
backend, "daitch_mokotoff", RUBY_METHOD_FUNC(rb_daitch_mokotoff), 3);
|
|
956
|
+
rb_define_singleton_method(
|
|
957
|
+
backend, "double_metaphone", RUBY_METHOD_FUNC(rb_double_metaphone), 3);
|
|
958
|
+
rb_define_singleton_method(
|
|
959
|
+
backend, "bmpm_register", RUBY_METHOD_FUNC(rb_bmpm_register), 1);
|
|
960
|
+
rb_define_singleton_method(
|
|
961
|
+
backend, "beider_morse", RUBY_METHOD_FUNC(rb_beider_morse), 4);
|
|
962
|
+
rb_define_singleton_method(backend, "dtw", RUBY_METHOD_FUNC(rb_dtw), 5);
|
|
963
|
+
rb_define_singleton_method(
|
|
964
|
+
backend, "alignment_path", RUBY_METHOD_FUNC(rb_alignment_path), 7);
|
|
965
|
+
}
|
|
966
|
+
#endif
|
|
967
|
+
|
|
968
|
+
} // namespace
|
|
969
|
+
|
|
970
|
+
extern "C" {
|
|
971
|
+
RUBY_FUNC_EXPORTED void STRIDE_ALIGN_RUBY_INIT() {
|
|
972
|
+
VALUE stride_align = rb_define_module("StrideAlign");
|
|
973
|
+
VALUE native = rb_define_module_under(stride_align, "Native");
|
|
974
|
+
VALUE backend = rb_define_module_under(native, STRIDE_ALIGN_RUBY_MODULE);
|
|
975
|
+
define_backend_methods(backend);
|
|
976
|
+
#if defined(STRIDE_ALIGN_RUBY_PRIMARY)
|
|
977
|
+
define_primary_methods(backend);
|
|
978
|
+
#endif
|
|
979
|
+
}
|
|
980
|
+
} // extern "C"
|
|
981
|
+
|
|
982
|
+
#undef STRIDE_ALIGN_RUBY_INIT
|
|
983
|
+
#undef STRIDE_ALIGN_RUBY_MODULE
|