| 119 | } |
| 120 | |
| 121 | void ReverseComplementImpl(const char* src, char* dst, int len) { |
| 122 | const hn::ScalableTag<uint8_t> d; |
| 123 | const int N = hn::Lanes(d); |
| 124 | |
| 125 | // Complement via single table lookup on low nibble of ASCII code. |
| 126 | // DNA base low nibbles: A/a=1, C/c=3, T/t=4, G/g=7, N=14. |
| 127 | // Uppercase and lowercase share the same low nibble, so one table |
| 128 | // handles both. Unmapped nibbles default to 'N'. |
| 129 | HWY_ALIGN static const uint8_t kComplement[16] = { |
| 130 | 'N', 'T', 'N', 'G', 'A', 'N', 'N', 'C', |
| 131 | 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N' |
| 132 | }; |
| 133 | const auto table = hn::LoadDup128(d, kComplement); |
| 134 | const auto vMask = hn::Set(d, static_cast<uint8_t>(0x0F)); |
| 135 | |
| 136 | int i = 0; |
| 137 | for (; i + N <= len; i += N) { |
| 138 | const auto v = hn::LoadU(d, reinterpret_cast<const uint8_t*>(src + i)); |
| 139 | const auto indices = hn::And(v, vMask); |
| 140 | const auto comp = hn::TableLookupBytes(table, indices); |
| 141 | |
| 142 | // Reverse and store at mirrored position |
| 143 | const auto revComp = hn::Reverse(d, comp); |
| 144 | hn::StoreU(revComp, d, reinterpret_cast<uint8_t*>(dst + len - i - N)); |
| 145 | } |
| 146 | |
| 147 | // Scalar tail |
| 148 | for (; i < len; i++) { |
| 149 | char base = src[i]; |
| 150 | char comp; |
| 151 | switch (base) { |
| 152 | case 'A': case 'a': comp = 'T'; break; |
| 153 | case 'T': case 't': comp = 'A'; break; |
| 154 | case 'C': case 'c': comp = 'G'; break; |
| 155 | case 'G': case 'g': comp = 'C'; break; |
| 156 | default: comp = 'N'; break; |
| 157 | } |
| 158 | dst[len - 1 - i] = comp; |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | int CountAdjacentDiffsImpl(const char* data, int len) { |
| 163 | if (len <= 1) return 0; |
nothing calls this directly
no outgoing calls
no test coverage detected