btllib
Loading...
Searching...
No Matches
bihash_kmer.hpp
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cstdint>
6#include <cstring>
7#include <deque>
8#include <iostream>
9#include <memory>
10#include <string>
11#include <unordered_set>
12#include <vector>
13
14#include <btllib/bihash_hashing_internals.hpp>
15#include <btllib/status.hpp>
16
17namespace btllib {
18
19using hashing_internals::extend_hashes;
20using hashing_internals::srol;
21using hashing_internals::sror;
22
23using hashing_internals::base_forward_bi_hash;
24using hashing_internals::next_forward_bi_hash;
25using hashing_internals::prev_forward_bi_hash;
26
27using hashing_internals::base_reverse_bi_hash;
28using hashing_internals::next_reverse_bi_hash;
29using hashing_internals::prev_reverse_bi_hash;
30
31using hashing_internals::BI_CONVERT_TAB;
32using hashing_internals::BI_RC_CONVERT_TAB;
33
34using hashing_internals::CA_DIMER_TAB;
35using hashing_internals::CC_DIMER_TAB;
36using hashing_internals::CG_DIMER_TAB;
37using hashing_internals::CT_DIMER_TAB;
38
39using hashing_internals::MS_TAB_31L_CA;
40using hashing_internals::MS_TAB_31L_CC;
41using hashing_internals::MS_TAB_31L_CG;
42using hashing_internals::MS_TAB_31L_CT;
43using hashing_internals::MS_TAB_33R_CA;
44using hashing_internals::MS_TAB_33R_CC;
45using hashing_internals::MS_TAB_33R_CG;
46using hashing_internals::MS_TAB_33R_CT;
47
48class BiHash
49{
50
51public:
67 BiHash(const char* seq,
68 size_t seq_len,
69 hashing_internals::NUM_HASHES_TYPE num_hashes,
70 hashing_internals::K_TYPE k,
71 std::string mode,
72 size_t pos = 0)
73 : seq(seq)
74 , seq_len(seq_len)
75 , num_hashes(num_hashes)
76 , k(k)
77 , pos(pos)
78 , initialized(false)
79 , hash_arr(new uint64_t[num_hashes])
80 {
81 check_error(k == 0, "BiHash: k must be greater than 0");
82 check_error(k % 2 == 1, "BiHash: k must be even");
83 check_error((k / 2) % 2 == 0, "BiHash: k must have odd number of dimers");
84 check_error(this->seq_len < k,
85 "BiHash: sequence length (" + std::to_string(this->seq_len) +
86 ") is smaller than k (" + std::to_string(k) + ")");
87 check_error(pos > this->seq_len - k,
88 "BiHash: passed position (" + std::to_string(pos) +
89 ") is larger than sequence length (" +
90 std::to_string(this->seq_len) + ")");
91 static const std::unordered_set<std::string> valid_modes = {
92 "CG", "CC", "CT", "CA"
93 };
94 check_error(valid_modes.find(mode) == valid_modes.end(),
95 "BiHash: mode must be one of [CG, CC, CT, CA], but got '" +
96 mode + "'");
97
98 if (mode == "CC") {
99 DIMER_TAB = CC_DIMER_TAB;
100 TAB_33R = MS_TAB_33R_CC;
101 TAB_31L = MS_TAB_31L_CC;
102 meth_signal = { "CC", "GG" };
103 } else if (mode == "CG") {
104 DIMER_TAB = CG_DIMER_TAB;
105 TAB_33R = MS_TAB_33R_CG;
106 TAB_31L = MS_TAB_31L_CG;
107 meth_signal = { "CG" };
108 } else if (mode == "CT") {
109 DIMER_TAB = CT_DIMER_TAB;
110 TAB_33R = MS_TAB_33R_CT;
111 TAB_31L = MS_TAB_31L_CT;
112 meth_signal = { "CT", "AG" };
113 } else if (mode == "CA") {
114 DIMER_TAB = CA_DIMER_TAB;
115 TAB_33R = MS_TAB_33R_CA;
116 TAB_31L = MS_TAB_31L_CA;
117 meth_signal = { "CA", "TG" };
118 }
119 }
120
135 BiHash(const std::string& seq,
136 hashing_internals::NUM_HASHES_TYPE num_hashes,
137 hashing_internals::K_TYPE k,
138 std::string mode = "CG",
139 size_t pos = 0)
140 : BiHash(seq.data(), seq.size(), num_hashes, k, mode, pos)
141 {
142 }
143
144 BiHash(const BiHash& obj)
145 : seq(obj.seq)
146 , seq_len(obj.seq_len)
147 , num_hashes(obj.num_hashes)
148 , k(obj.k)
149 , pos(obj.pos)
150 , initialized(obj.initialized)
151 , fwd_hashes(obj.fwd_hashes)
152 , rev_hashes(obj.rev_hashes)
153 , hash_arr(new uint64_t[obj.num_hashes])
154 {
155 DIMER_TAB = obj.DIMER_TAB;
156 TAB_33R = obj.TAB_33R;
157 TAB_31L = obj.TAB_31L;
158 std::memcpy(
159 hash_arr.get(), obj.hash_arr.get(), num_hashes * sizeof(uint64_t));
160 }
161
162 BiHash(BiHash&&) = default;
163
176 bool roll()
177 {
178 if (!initialized) {
179 return init();
180 }
181
182 auto dimer_valid = [&](size_t p) -> bool {
183 if (p + 1 > seq_len - 1)
184 return false;
185 char c1 = seq[p];
186 char c2 = seq[p + 1];
187 uint8_t a = BI_CONVERT_TAB[(unsigned char)c1];
188 uint8_t b = BI_CONVERT_TAB[(unsigned char)c2];
189 uint8_t loc = 10 * a + b;
190 return DIMER_TAB[loc] != 0;
191 };
192
193 if (even) {
194 if (pos >= seq_len - k)
195 return false;
196 if (!dimer_valid(pos + k - 1)) {
197 pos += k + 1;
198 // pos += k;
199 return init();
200 }
201 even = false;
202 ++pos;
203
204 extend_hashes(
205 fwd_hashes[1], rev_hashes[1], k, num_hashes, hash_arr.get());
206 return true;
207 }
208
209 if (pos >= seq_len - k)
210 return false;
211
212 if (!dimer_valid(pos + k - 1)) {
213 pos += k + 1;
214 return init();
215 }
216
217 bool can_roll_second = dimer_valid(pos + k);
218
219 for (int i = 0; i < 2; i++) {
220 if (i == 0 || can_roll_second) {
221
222 char out1 = seq[pos + i - 1];
223 char out2 = seq[pos + i + 1 - 1];
224
225 char in1 = seq[pos + k + i - 1];
226 char in2 = seq[pos + k + i + 1 - 1];
227
228 fwd_hashes[i] = next_forward_bi_hash(
229 fwd_hashes[i], k, out1, out2, in1, in2, DIMER_TAB, TAB_33R, TAB_31L);
230
231 rev_hashes[i] = next_reverse_bi_hash(
232 rev_hashes[i], k, out1, out2, in1, in2, DIMER_TAB, TAB_33R, TAB_31L);
233 }
234 }
235
236 extend_hashes(fwd_hashes[0], rev_hashes[0], k, num_hashes, hash_arr.get());
237
238 ++pos;
239 even = true;
240
241 if (!can_roll_second) {
242 fwd_hashes[1] = 0;
243 rev_hashes[1] = 0;
244 }
245
246 return true;
247 }
248
259 std::string_view center_dimer() const
260 {
261 constexpr size_t DIMER_LEN = 2;
262 const size_t center_idx = pos + (k / 4) * DIMER_LEN;
263 return std::string_view(seq + center_idx, DIMER_LEN);
264 }
265
275 {
276
277 size_t num_dimers = k / 2;
278 size_t center_idx = pos + (num_dimers / 2) * 2;
279
280 std::string dimer;
281 dimer += seq[center_idx];
282 dimer += seq[center_idx + 1];
283
284 std::transform(dimer.begin(), dimer.end(), dimer.begin(), ::toupper);
285 return std::find(meth_signal.begin(), meth_signal.end(), dimer) !=
286 meth_signal.end();
287 }
288
294 {
295 size_t idx = even ? 0 : 1;
296 return fwd_hashes[idx] > rev_hashes[idx];
297 }
298
304 {
305 if (!initialized) {
306 return init();
307 }
308
309 if (pos == 0) {
310 return false;
311 }
312
313 auto dimer_valid = [&](size_t p) -> bool {
314 if (p + 1 >= seq_len)
315 return false;
316 uint8_t a = BI_CONVERT_TAB[(unsigned char)seq[p]];
317 uint8_t b = BI_CONVERT_TAB[(unsigned char)seq[p + 1]];
318 uint8_t loc = 10 * a + b;
319 return DIMER_TAB[loc] != 0;
320 };
321
322 // Odd State
323 if (!even) {
324 extend_hashes(
325 fwd_hashes[0], rev_hashes[0], k, num_hashes, hash_arr.get());
326 --pos;
327 even = true;
328 return true;
329 }
330
331 // Even state
332 // First dimer (the one at pos-1, pos)
333 if (!dimer_valid(pos - 1)) {
334 if (pos <= k)
335 return false;
336 pos -= k;
337 return init();
338 }
339
340 // Check if the second rollback is possible
341 bool can_roll_second = true;
342 if (pos <= 1 || !dimer_valid(pos - 2)) {
343 can_roll_second = false;
344 }
345
346 // If the second hash is missing, restore it by reinitializing around pos-2
347 if (fwd_hashes[1] == 0) {
348 pos -= 2;
349 init();
350 pos++;
351 extend_hashes(
352 fwd_hashes[1], rev_hashes[1], k, num_hashes, hash_arr.get());
353 even = false;
354 return true;
355 }
356
357 // Perform 1 or 2 backward hash updates
358 for (int i = 0; i < 2; i++) {
359 if (i == 1 || can_roll_second) {
360
361 char out1 = seq[pos + k - 2 + i];
362 char out2 = seq[pos + k - 1 + i];
363 char in1 = seq[pos - 2 + i];
364 char in2 = seq[pos - 1 + i];
365
366 fwd_hashes[i] = prev_forward_bi_hash(
367 fwd_hashes[i], k, out1, out2, in1, in2, DIMER_TAB, TAB_33R, TAB_31L);
368
369 rev_hashes[i] = prev_reverse_bi_hash(
370 rev_hashes[i], k, out1, out2, in1, in2, DIMER_TAB, TAB_33R, TAB_31L);
371 }
372 }
373
374 extend_hashes(fwd_hashes[1], rev_hashes[1], k, num_hashes, hash_arr.get());
375
376 --pos;
377 even = false;
378 return true;
379 }
380
385 const uint64_t* hashes() const { return hash_arr.get(); }
386
392 size_t get_pos() const { return pos; }
393
398 hashing_internals::NUM_HASHES_TYPE get_hash_num() const { return num_hashes; }
399
404 hashing_internals::K_TYPE get_k() const { return k; }
405
410 uint64_t get_forward_hash() const
411 {
412 if (even) {
413 return fwd_hashes[0];
414 } else {
415 return fwd_hashes[1];
416 }
417 }
418
423 uint64_t get_reverse_hash() const
424 {
425 if (even) {
426 return rev_hashes[0];
427 } else {
428 return rev_hashes[1];
429 }
430 }
431
432private:
433 const char* seq;
434 const size_t seq_len;
435 hashing_internals::NUM_HASHES_TYPE num_hashes;
436 hashing_internals::K_TYPE k;
437 size_t pos;
438 bool initialized;
439 std::vector<uint64_t> fwd_hashes = { 0, 0 };
440 std::vector<uint64_t> rev_hashes = { 0, 0 };
441 std::vector<std::string> meth_signal;
442 bool even = true;
443 std::unique_ptr<uint64_t[]> hash_arr;
444 const uint64_t* DIMER_TAB = nullptr;
445 const uint64_t* const* TAB_33R = nullptr;
446 const uint64_t* const* TAB_31L = nullptr;
447
453 bool init()
454 {
455 // std::cerr << "running init" << std::endl;
456 even = true;
457 bool has_n = true;
458 while (pos <= seq_len - k + 1 && has_n) {
459 has_n = false;
460 for (unsigned i = 0; i < k && pos <= seq_len - k + 1; i += 2) {
461 const char c1 = seq[pos + k - i - 2];
462 const char c2 = seq[pos + k - i - 1];
463 const uint8_t a = BI_CONVERT_TAB[(unsigned char)c1];
464 const uint8_t b = BI_CONVERT_TAB[(unsigned char)c2];
465 const uint8_t loc = 10 * a + b;
466
467 if (DIMER_TAB[loc] == 0) {
468 if (b == 0) {
469 pos += k - i;
470 } else {
471 pos += k - i - 1;
472 }
473
474 has_n = true;
475 }
476 }
477 }
478
479 if (pos > seq_len - k) {
480 return false;
481 }
482
483 auto dimer_valid = [&](size_t p) -> bool {
484 if (p + 1 > seq_len - 1)
485 return false;
486 char c1 = seq[p];
487 char c2 = seq[p + 1];
488 uint8_t a = BI_CONVERT_TAB[(unsigned char)c1];
489 uint8_t b = BI_CONVERT_TAB[(unsigned char)c2];
490 uint8_t loc = 10 * a + b;
491 return DIMER_TAB[loc] != 0;
492 };
493 bool has_second = true;
494 if (pos == seq_len - k || !dimer_valid(pos + k - 1)) {
495 has_second = false;
496 }
497
498 fwd_hashes = base_forward_bi_hash(seq + pos, has_second, k, DIMER_TAB);
499 rev_hashes = base_reverse_bi_hash(seq + pos, has_second, k, DIMER_TAB);
500
501 extend_hashes(fwd_hashes[0], rev_hashes[0], k, num_hashes, hash_arr.get());
502
503 initialized = true;
504 return true;
505 }
506};
507}
Definition bihash_kmer.hpp:49
bool is_methylated()
Definition bihash_kmer.hpp:274
bool roll_back()
Definition bihash_kmer.hpp:303
const uint64_t * hashes() const
Definition bihash_kmer.hpp:385
BiHash(const char *seq, size_t seq_len, hashing_internals::NUM_HASHES_TYPE num_hashes, hashing_internals::K_TYPE k, std::string mode, size_t pos=0)
Definition bihash_kmer.hpp:67
uint64_t get_forward_hash() const
Definition bihash_kmer.hpp:410
hashing_internals::NUM_HASHES_TYPE get_hash_num() const
Definition bihash_kmer.hpp:398
BiHash(const std::string &seq, hashing_internals::NUM_HASHES_TYPE num_hashes, hashing_internals::K_TYPE k, std::string mode="CG", size_t pos=0)
Definition bihash_kmer.hpp:135
uint64_t get_reverse_hash() const
Definition bihash_kmer.hpp:423
std::string_view center_dimer() const
Definition bihash_kmer.hpp:259
size_t get_pos() const
Definition bihash_kmer.hpp:392
hashing_internals::K_TYPE get_k() const
Definition bihash_kmer.hpp:404
bool is_forward_higher()
Definition bihash_kmer.hpp:293
bool roll()
Definition bihash_kmer.hpp:176
Definition aahash.hpp:12
void check_error(bool condition, const std::string &msg)