├── .gitignore ├── LICENSE ├── README.md ├── avx2.hh ├── avx2_double.hh ├── bitman.hh ├── bitstream.hh ├── bose_chaudhuri_hocquenghem_decoder.hh ├── bose_chaudhuri_hocquenghem_encoder.hh ├── cauchy_prime_field_erasure_coding.hh ├── cauchy_reed_solomon_erasure_coding.hh ├── crc.hh ├── exclusive_reduce.hh ├── galois_field.hh ├── hadamard_decoder.hh ├── hadamard_encoder.hh ├── interleave.hh ├── ldpc_decoder.hh ├── ldpc_encoder.hh ├── mls.hh ├── neon.hh ├── neon_double.hh ├── neon_quadruple.hh ├── neon_triple.hh ├── osd.hh ├── osd_bch.png ├── permute.hh ├── polar_ber.png ├── polar_decoder.hh ├── polar_encoder.hh ├── polar_freezer.hh ├── polar_helper.hh ├── polar_list_decoder.hh ├── polar_parity_aided.hh ├── polar_sequence.hh ├── prime_field.hh ├── reed_solomon_decoder.hh ├── reed_solomon_encoder.hh ├── reed_solomon_error_correction.hh ├── rotate.hh ├── short_bch_code_decoder.hh ├── short_bch_code_encoder.hh ├── simd.hh ├── simd_sort.hh ├── simplex_decoder.hh ├── simplex_encoder.hh ├── sort.hh ├── sse4_1.hh ├── sse4_1_double.hh ├── sse4_1_quadruple.hh ├── sse4_1_triple.hh ├── tests ├── .gitignore ├── Makefile ├── bch_decoder_test.cc ├── bch_encoder_test.cc ├── bch_regression_test.cc ├── cpf_regression_test.cc ├── crc_test.cc ├── crs_regression_test.cc ├── gf_test.cc ├── hadamard_encoder_test.cc ├── hadamard_regression_test.cc ├── ldpc_regression_test.cc ├── mls_test.cc ├── osd_regression_test.cc ├── osld_regression_test.cc ├── pf_test.cc ├── polar_list_regression_test.cc ├── polar_regression_test.cc ├── rs_decoder_test.cc ├── rs_encoder_test.cc ├── rs_regression_test.cc ├── sequence.h ├── short_bch_code_decoder_test.cc ├── short_bch_code_encoder_test.cc ├── short_bch_code_regression_test.cc ├── simd_sort_regression_test.cc ├── simplex_encoder_test.cc ├── simplex_regression_test.cc ├── soft_bch_regression_test.cc └── sort_regression_test.cc └── xorshift.hh /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 by Ahmet Inan 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a work in progress and a long overdue attempt to bring all our coding-related code together and make it reusable for our future projects. 2 | 3 | Before using any of this you should enter the tests directory and execute "make". 4 | This will check if your compiler is able to create binaries that are able to produce correct results when executed. 5 | 6 | What we have included so far: 7 | 8 | ### [crc.hh](crc.hh) 9 | 10 | A [Cyclic redundancy check](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) helps ensuring data integrity. 11 | 12 | For example, if we need to integrate CRC32 checking for a few bytes, like in the following: 13 | ``` 14 | # echo -n 'Hello World!' | rhash -C - 15 | (stdin) 1C291CA3 16 | ``` 17 | We can add it to our project as simple as that: 18 | ``` 19 | CODE::CRC crc(0xEDB88320, 0xFFFFFFFF); 20 | for (uint8_t c: std::string("Hello World!")) crc(c); 21 | assert(~crc() == 0x1C291CA3); 22 | ``` 23 | 24 | ### [xorshift.hh](xorshift.hh) 25 | 26 | Sometimes we need a sequence of ["random enough"](https://en.wikipedia.org/wiki/Diehard_tests) numbers but don't want to store them in an array to get a repeatable sequence. 27 | Here a [Pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) can help by prodiving a deterministic and thus repeatable sequence of numbers. 28 | [George Marsaglia](https://en.wikipedia.org/wiki/George_Marsaglia) discovered a class of simple and fast pseudorandom number generators, which he called [Xorshift](https://en.wikipedia.org/wiki/Xorshift). 29 | 30 | ### [mls.hh](mls.hh) 31 | 32 | [Maximum length sequences](https://en.wikipedia.org/wiki/Maximum_length_sequence) have useful [correlation properties](https://en.wikipedia.org/wiki/Maximum_length_sequence#Correlation_property). 33 | 34 | ### [bitstream.hh](bitstream.hh) 35 | 36 | When dealing with unaligned and arbitrary-bit-sized elements in a data stream, the bitwise stream container might help avoiding some headaches. 37 | 38 | ### [bitman.hh](bitman.hh) 39 | 40 | Simple bit manipulation on byte arrays. 41 | 42 | ### [galois_field.hh](galois_field.hh) 43 | 44 | We have to thank [Évariste Galois](https://en.wikipedia.org/wiki/%C3%89variste_Galois) for his contribution of the [Finite field](https://en.wikipedia.org/wiki/Finite_field) to mathematics, which laid the cornerstone for a variety of applications that we take for granted today. 45 | One of them is [Reed–Solomon error correction](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction): 46 | 47 | ### [reed_solomon_error_correction.hh](reed_solomon_error_correction.hh) 48 | 49 | Implemented are the following Encoders and Decoders: 50 | * [reed_solomon_encoder.hh](reed_solomon_encoder.hh) 51 | * [reed_solomon_decoder.hh](reed_solomon_decoder.hh) 52 | * [bose_chaudhuri_hocquenghem_encoder.hh](bose_chaudhuri_hocquenghem_encoder.hh) 53 | * [bose_chaudhuri_hocquenghem_decoder.hh](bose_chaudhuri_hocquenghem_decoder.hh) 54 | 55 | ### [short_bch_code_encoder.hh](short_bch_code_encoder.hh) 56 | 57 | Encoder for short [BCH codes](https://en.wikipedia.org/wiki/BCH_code) 58 | 59 | ### [short_bch_code_decoder.hh](short_bch_code_decoder.hh) 60 | 61 | [Syndrome decoding](https://en.wikipedia.org/wiki/Decoding_methods#Syndrome_decoding) based [Soft-decision decoder](https://en.wikipedia.org/wiki/Soft-decision_decoder) for short [BCH codes](https://en.wikipedia.org/wiki/BCH_code) 62 | 63 | ### [simplex_encoder.hh](simplex_encoder.hh) 64 | 65 | Encoder for [Simplex codes](https://en.wikipedia.org/wiki/Linear_code). 66 | 67 | ### [simplex_decoder.hh](simplex_decoder.hh) 68 | 69 | [Soft-decision decoder](https://en.wikipedia.org/wiki/Soft-decision_decoder) for [Simplex codes](https://en.wikipedia.org/wiki/Linear_code). 70 | 71 | ### [hadamard_encoder.hh](hadamard_encoder.hh) 72 | 73 | Encoder for [augmented Hadamard codes](https://en.wikipedia.org/wiki/Hadamard_code). 74 | 75 | ### [hadamard_decoder.hh](hadamard_decoder.hh) 76 | 77 | [Soft-decision decoder](https://en.wikipedia.org/wiki/Soft-decision_decoder) for [augmented Hadamard codes](https://en.wikipedia.org/wiki/Hadamard_code). 78 | 79 | ### [ldpc_encoder.hh](ldpc_encoder.hh) 80 | 81 | [Low-density parity-check](https://en.wikipedia.org/wiki/Low-density_parity-check_code) encoder 82 | 83 | ### [ldpc_decoder.hh](ldpc_decoder.hh) 84 | 85 | [SIMD](https://en.wikipedia.org/wiki/SIMD) intra-frame accelerated [Low-density parity-check](https://en.wikipedia.org/wiki/Low-density_parity-check_code) layered decoder. 86 | 87 | ### [polar_freezer.hh](polar_freezer.hh) 88 | 89 | Bit freezers for the construction of [polar codes](https://en.wikipedia.org/wiki/Polar_code_(coding_theory)). 90 | 91 | * PolarFreezer: Constructs code for a given erasure probability without the need for storing nor sorting of erasure probabilities for the virtual channels. 92 | * PolarCodeConst0: Constructs code by choosing the K best virtual channels computed from a given erasure probability. 93 | 94 | ### [polar_sequence.hh](polar_sequence.hh) 95 | 96 | Construction of reliability sequences for [polar codes](https://en.wikipedia.org/wiki/Polar_code_(coding_theory)). 97 | 98 | * PolarSeqConst0: Constructs a sequence by sorting virtual channel reliabilities computed from a given erasure probability. 99 | 100 | ### [polar_encoder.hh](polar_encoder.hh) 101 | 102 | Encoders for [non-systematic and systematic](https://en.wikipedia.org/wiki/Systematic_code) [polar codes](https://en.wikipedia.org/wiki/Polar_code_(coding_theory)). 103 | 104 | ### [polar_decoder.hh](polar_decoder.hh) 105 | 106 | Successive cancellation decoding of [polar codes](https://en.wikipedia.org/wiki/Polar_code_(coding_theory)). 107 | 108 | ### [polar_list_decoder.hh](polar_list_decoder.hh) 109 | 110 | Successive cancellation [list decoding](https://en.wikipedia.org/wiki/List_decoding) of [polar codes](https://en.wikipedia.org/wiki/Polar_code_(coding_theory)). 111 | 112 | List size depends on used SIMD type. Decoding performance of the fixed-point implementation is better with shorter codes, as the list size is larger but a deterioration can be observed with larger codes. 113 | 114 | ### [polar_parity_aided.hh](polar_parity_aided.hh) 115 | 116 | Parity check aided successive cancellation [list decoding](https://en.wikipedia.org/wiki/List_decoding) of [polar codes](https://en.wikipedia.org/wiki/Polar_code_(coding_theory)). 117 | ![Polar decoder comparisons](polar_ber.png) 118 | 119 | ### [osd.hh](osd.hh) 120 | 121 | Ordered statistics decoding allows the practical [soft-decision decoding](https://en.wikipedia.org/wiki/Soft-decision_decoder) of short to medium sized [linear codes](https://en.wikipedia.org/wiki/Linear_code). 122 | Below are the BER plots of the BCH(127, 64) T=10 code, using the OSD [Soft-decision decoder](https://en.wikipedia.org/wiki/Soft-decision_decoder) and the [Reed–Solomon error correction](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) BCH decoder with erasures. 123 | ![OSD and BCH decoder comparison](osd_bch.png) 124 | 125 | ### [exclusive_reduce.hh](exclusive_reduce.hh) 126 | 127 | Reduce N times while excluding ith input element 128 | 129 | It computes the following, but having only O(N) complexity and using O(1) extra storage: 130 | 131 | ``` 132 | output[0] = input[1]; 133 | output[1] = input[0]; 134 | for (int i = 2; i < N; ++i) 135 | output[i] = op(input[0], input[1]); 136 | for (int i = 0; i < N; ++i) 137 | for (int j = 2; j < N; ++j) 138 | if (i != j) 139 | output[i] = op(output[i], input[j]); 140 | ``` 141 | 142 | ### [simd.hh](simd.hh) 143 | 144 | Single instruction, multiple data ([SIMD](https://en.wikipedia.org/wiki/SIMD)) wrappers for: 145 | * [ARM NEON](https://en.wikipedia.org/wiki/ARM_architecture#Advanced_SIMD_(NEON)) ([neon.hh](neon.hh)) 146 | * [Intel SSE4.1](https://en.wikipedia.org/wiki/SSE4) ([sse4_1.hh](sse4_1.hh)) 147 | * [Intel AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) ([avx2.hh](avx2.hh)) 148 | 149 | ### [rotate.hh](rotate.hh) 150 | 151 | [SIMD](https://en.wikipedia.org/wiki/SIMD) element wise horizontal rotation 152 | 153 | It computes the following, but faster: 154 | 155 | ``` 156 | SIMD rotate(SIMD input, int shift, int WIDTH) 157 | { 158 | SIMD output; 159 | for (int n = 0; n < WIDTH; ++n) 160 | output.v[(n + shift + WIDTH) % WIDTH] = input.v[n]; 161 | return output; 162 | } 163 | ``` 164 | 165 | -------------------------------------------------------------------------------- /bitman.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Bit manipulation of byte arrays 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | void xor_be_bit(uint8_t *buf, int pos, bool val) 12 | { 13 | buf[pos/8] ^= val<<(7-pos%8); 14 | } 15 | 16 | void xor_le_bit(uint8_t *buf, int pos, bool val) 17 | { 18 | buf[pos/8] ^= val<<(pos%8); 19 | } 20 | 21 | void set_be_bit(uint8_t *buf, int pos, bool val) 22 | { 23 | buf[pos/8] = (~(1<<(7-pos%8))&buf[pos/8])|(val<<(7-pos%8)); 24 | } 25 | 26 | void set_le_bit(uint8_t *buf, int pos, bool val) 27 | { 28 | buf[pos/8] = (~(1<<(pos%8))&buf[pos/8])|(val<<(pos%8)); 29 | } 30 | 31 | bool get_be_bit(const uint8_t *buf, int pos) 32 | { 33 | return (buf[pos/8]>>(7-pos%8))&1; 34 | } 35 | 36 | bool get_le_bit(const uint8_t *buf, int pos) 37 | { 38 | return (buf[pos/8]>>(pos%8))&1; 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /bitstream.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Bitwise stream container 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class Bitstream 13 | { 14 | public: 15 | static const int BITS = SIZE; 16 | static const int BYTES = (SIZE + 7) / 8; 17 | private: 18 | uint8_t buf_[BYTES]; 19 | int pos_ = 0; 20 | public: 21 | void seek(int pos) 22 | { 23 | pos_ = pos; 24 | } 25 | int tell() 26 | { 27 | return pos_; 28 | } 29 | uint8_t *data() 30 | { 31 | return buf_; 32 | } 33 | void reset() 34 | { 35 | pos_ = 0; 36 | for (int i = 0; i < BYTES; ++i) 37 | buf_[i] = 0; 38 | } 39 | void putbit(bool b) 40 | { 41 | int bit = pos_ % 8; 42 | int byte = pos_ / 8; 43 | ++pos_; 44 | uint8_t mask = 1 << bit; 45 | uint8_t tmp = ~mask & buf_[byte]; 46 | buf_[byte] = tmp | b << bit; 47 | } 48 | bool getbit() 49 | { 50 | int bit = pos_ % 8; 51 | int byte = pos_ / 8; 52 | ++pos_; 53 | return (buf_[byte] >> bit) & 1; 54 | } 55 | void putbyte(const uint8_t b) 56 | { 57 | int bit = pos_ % 8; 58 | int byte = pos_ / 8; 59 | pos_ += 8; 60 | if (bit) { 61 | uint8_t mask = (1 << bit) - 1; 62 | uint8_t lsb = mask & buf_[byte]; 63 | buf_[byte++] = lsb | b << bit; 64 | uint8_t msb = ~mask & buf_[byte]; 65 | buf_[byte] = msb | b >> (8 - bit); 66 | } else { 67 | buf_[byte] = b; 68 | } 69 | } 70 | uint8_t getbyte() 71 | { 72 | int bit = pos_ % 8; 73 | int byte = pos_ / 8; 74 | pos_ += 8; 75 | if (bit) { 76 | uint8_t lsb = buf_[byte++] >> bit; 77 | uint8_t msb = buf_[byte] << (8 - bit); 78 | return msb | lsb; 79 | } 80 | return buf_[byte]; 81 | } 82 | void writebytes(const uint8_t *buf, int len) 83 | { 84 | int bit = pos_ % 8; 85 | int byte = pos_ / 8; 86 | pos_ += 8 * len; 87 | if (bit) { 88 | uint8_t mask = (1 << bit) - 1; 89 | for (int i = 0; i < len; ++i) { 90 | uint8_t lsb = mask & buf_[byte]; 91 | buf_[byte++] = lsb | buf[i] << bit; 92 | uint8_t msb = ~mask & buf_[byte]; 93 | buf_[byte] = msb | buf[i] >> (8 - bit); 94 | } 95 | } else { 96 | for (int i = 0; i < len; ++i) 97 | buf_[byte++] = buf[i]; 98 | } 99 | } 100 | void readbytes(uint8_t *buf, int len) 101 | { 102 | int bit = pos_ % 8; 103 | int byte = pos_ / 8; 104 | pos_ += 8 * len; 105 | if (bit) { 106 | for (int i = 0; i < len; ++i) { 107 | uint8_t lsb = buf_[byte++] >> bit; 108 | uint8_t msb = buf_[byte] << (8 - bit); 109 | buf[i] = msb | lsb; 110 | } 111 | } else { 112 | for (int i = 0; i < len; ++i) 113 | buf[i] = buf_[byte++]; 114 | } 115 | } 116 | template 117 | void writebits(TYPE b, int num) 118 | { 119 | for (int sum = 0; num;) { 120 | int bit = pos_ % 8; 121 | int byte = pos_ / 8; 122 | int copy = std::min(8 - bit, num); 123 | uint8_t mask = (1 << copy) - 1; 124 | uint8_t src = (mask & (b >> sum)) << bit; 125 | uint8_t dst = ~(mask << bit) & buf_[byte]; 126 | buf_[byte++] = dst | src; 127 | pos_ += copy; 128 | num -= copy; 129 | sum += copy; 130 | } 131 | } 132 | template 133 | void readbits(TYPE *b, int num) 134 | { 135 | TYPE a = 0; 136 | for (int sum = 0; num;) { 137 | int bit = pos_ % 8; 138 | int byte = pos_ / 8; 139 | int copy = std::min(8 - bit, num); 140 | uint8_t mask = (1 << copy) - 1; 141 | TYPE tmp = mask & (buf_[byte++] >> bit); 142 | a |= tmp << sum; 143 | pos_ += copy; 144 | num -= copy; 145 | sum += copy; 146 | } 147 | *b = a; 148 | } 149 | }; 150 | 151 | } 152 | 153 | -------------------------------------------------------------------------------- /bose_chaudhuri_hocquenghem_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Bose Chaudhuri Hocquenghem Decoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "reed_solomon_error_correction.hh" 10 | #include "bitman.hh" 11 | 12 | namespace CODE { 13 | 14 | template 15 | class BoseChaudhuriHocquenghemDecoder 16 | { 17 | public: 18 | typedef typename GF::value_type value_type; 19 | typedef typename GF::ValueType ValueType; 20 | typedef typename GF::IndexType IndexType; 21 | static const int NR = ROOTS; 22 | static const int N = GF::N, K = MSG, NP = N - K; 23 | private: 24 | ReedSolomonErrorCorrection algorithm; 25 | void update_syndromes(const uint8_t *poly, ValueType *syndromes, int begin, int end) 26 | { 27 | for (int j = begin; j < end; ++j) { 28 | ValueType coeff(get_be_bit(poly, j)); 29 | IndexType root(FCR), pe(1); 30 | for (int i = 0; i < NR; ++i) { 31 | syndromes[i] = fma(root, syndromes[i], coeff); 32 | root *= pe; 33 | } 34 | } 35 | } 36 | public: 37 | int compute_syndromes(const uint8_t *data, const uint8_t *parity, ValueType *syndromes, int data_len = K) 38 | { 39 | assert(0 < data_len && data_len <= K); 40 | // $syndromes_i = code(pe^{FCR+i})$ 41 | ValueType coeff(get_be_bit(data, 0)); 42 | for (int i = 0; i < NR; ++i) 43 | syndromes[i] = coeff; 44 | update_syndromes(data, syndromes, 1, data_len); 45 | update_syndromes(parity, syndromes, 0, NP); 46 | int nonzero = 0; 47 | for (int i = 0; i < NR; ++i) 48 | nonzero += !!syndromes[i]; 49 | return nonzero; 50 | } 51 | int compute_syndromes(const uint8_t *data, const uint8_t *parity, value_type *syndromes, int data_len = K) 52 | { 53 | return compute_syndromes(data, parity, reinterpret_cast(syndromes), data_len); 54 | } 55 | int operator()(uint8_t *data, uint8_t *parity, value_type *erasures = 0, int erasures_count = 0, int data_len = K) 56 | { 57 | assert(0 <= erasures_count && erasures_count <= NR); 58 | assert(0 < data_len && data_len <= K); 59 | if (0) { 60 | for (int i = 0; i < erasures_count; ++i) { 61 | int idx = (int)erasures[i]; 62 | if (idx < data_len) 63 | set_be_bit(data, idx, 0); 64 | else 65 | set_be_bit(parity, idx-data_len, 0); 66 | } 67 | } 68 | if (erasures_count && data_len < K) { 69 | for (int i = 0; i < erasures_count; ++i) 70 | erasures[i] += K - data_len; 71 | } 72 | ValueType syndromes[NR]; 73 | if (!compute_syndromes(data, parity, syndromes, data_len)) 74 | return 0; 75 | IndexType locations[NR]; 76 | ValueType magnitudes[NR]; 77 | int count = algorithm(syndromes, locations, magnitudes, reinterpret_cast(erasures), erasures_count); 78 | if (count <= 0) 79 | return count; 80 | for (int i = 0; i < count; ++i) 81 | if ((int)locations[i] < K - data_len) 82 | return -1; 83 | for (int i = 0; i < count; ++i) 84 | if (1 < (int)magnitudes[i]) 85 | return -1; 86 | for (int i = 0; i < count; ++i) { 87 | int idx = (int)locations[i] + data_len - K; 88 | bool err = (bool)magnitudes[i]; 89 | if (idx < data_len) 90 | xor_be_bit(data, idx, err); 91 | else 92 | xor_be_bit(parity, idx-data_len, err); 93 | } 94 | int corrections_count = 0; 95 | for (int i = 0; i < count; ++i) 96 | corrections_count += !!magnitudes[i]; 97 | return corrections_count; 98 | } 99 | }; 100 | 101 | template 102 | class BoseChaudhuriHocquenghemDecoderReference 103 | { 104 | public: 105 | typedef typename GF::value_type value_type; 106 | typedef typename GF::ValueType ValueType; 107 | typedef typename GF::IndexType IndexType; 108 | static const int NR = ROOTS; 109 | static const int N = GF::N, K = MSG, NP = N - K; 110 | private: 111 | ReedSolomonErrorCorrection algorithm; 112 | void update_syndromes(const ValueType *poly, ValueType *syndromes, int begin, int end) 113 | { 114 | for (int j = begin; j < end; ++j) { 115 | ValueType coeff(poly[j]); 116 | IndexType root(FCR), pe(1); 117 | for (int i = 0; i < NR; ++i) { 118 | syndromes[i] = fma(root, syndromes[i], coeff); 119 | root *= pe; 120 | } 121 | } 122 | } 123 | public: 124 | int compute_syndromes(const ValueType *data, const ValueType *parity, ValueType *syndromes, int data_len = K) 125 | { 126 | assert(0 < data_len && data_len <= K); 127 | // $syndromes_i = code(pe^{FCR+i})$ 128 | ValueType coeff(data[0]); 129 | for (int i = 0; i < NR; ++i) 130 | syndromes[i] = coeff; 131 | update_syndromes(data, syndromes, 1, data_len); 132 | update_syndromes(parity, syndromes, 0, NP); 133 | int nonzero = 0; 134 | for (int i = 0; i < NR; ++i) 135 | nonzero += !!syndromes[i]; 136 | return nonzero; 137 | } 138 | int operator()(ValueType *data, ValueType *parity, IndexType *erasures = 0, int erasures_count = 0, int data_len = K) 139 | { 140 | assert(0 <= erasures_count && erasures_count <= NR); 141 | assert(0 < data_len && data_len <= K); 142 | if (0) { 143 | for (int i = 0; i < erasures_count; ++i) { 144 | int idx = (int)erasures[i]; 145 | if (idx < data_len) 146 | data[idx] = ValueType(0); 147 | else 148 | parity[idx-data_len] = ValueType(0); 149 | } 150 | } 151 | if (erasures_count && data_len < K) { 152 | for (int i = 0; i < erasures_count; ++i) 153 | erasures[i] = IndexType((int)erasures[i] + K - data_len); 154 | } 155 | ValueType syndromes[NR]; 156 | if (!compute_syndromes(data, parity, syndromes, data_len)) 157 | return 0; 158 | IndexType locations[NR]; 159 | ValueType magnitudes[NR]; 160 | int count = algorithm(syndromes, locations, magnitudes, erasures, erasures_count); 161 | if (count <= 0) 162 | return count; 163 | for (int i = 0; i < count; ++i) 164 | if ((int)locations[i] < K - data_len) 165 | return -1; 166 | for (int i = 0; i < count; ++i) 167 | if (1 < (int)magnitudes[i]) 168 | return -1; 169 | for (int i = 0; i < count; ++i) { 170 | int idx = (int)locations[i] + data_len - K; 171 | if (idx < data_len) 172 | data[idx] += magnitudes[i]; 173 | else 174 | parity[idx-data_len] += magnitudes[i]; 175 | } 176 | int corrections_count = 0; 177 | for (int i = 0; i < count; ++i) 178 | corrections_count += !!magnitudes[i]; 179 | return corrections_count; 180 | } 181 | }; 182 | 183 | } 184 | 185 | -------------------------------------------------------------------------------- /bose_chaudhuri_hocquenghem_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Bose Chaudhuri Hocquenghem Encoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "bitman.hh" 11 | 12 | namespace CODE { 13 | 14 | template 15 | class BoseChaudhuriHocquenghemEncoder 16 | { 17 | public: 18 | static const int N = LEN, K = MSG, NP = N - K; 19 | static const int G = ((NP+1)+7)/8; 20 | private: 21 | uint8_t generator[G]; 22 | static constexpr uint8_t slb1(uint8_t *buf, int pos) 23 | { 24 | return (buf[pos]<<1) | (buf[pos+1]>>7); 25 | } 26 | public: 27 | BoseChaudhuriHocquenghemEncoder(std::initializer_list minimal_polynomials) 28 | { 29 | // $generator(x) = \prod_i(minpoly_i(x))$ 30 | int generator_degree = 1; 31 | for (int i = 0; i < G; ++i) 32 | generator[i] = 0; 33 | set_be_bit(generator, NP, 1); 34 | for (auto m: minimal_polynomials) { 35 | assert(0 < m); 36 | int m_degree = 0; 37 | while (m>>m_degree) 38 | ++m_degree; 39 | --m_degree; 40 | assert(generator_degree + m_degree <= NP + 1); 41 | for (int i = generator_degree; i >= 0; --i) { 42 | if (!get_be_bit(generator, NP-i)) 43 | continue; 44 | set_be_bit(generator, NP-i, m&1); 45 | for (int j = 1; j <= m_degree; ++j) 46 | xor_be_bit(generator, NP-(i+j), (m>>j)&1); 47 | } 48 | generator_degree += m_degree; 49 | } 50 | assert(generator_degree == NP + 1); 51 | if (0) { 52 | std::cerr << "generator ="; 53 | for (int i = 0; i <= NP; ++i) 54 | std::cerr << " " << get_be_bit(generator, NP-i); 55 | std::cerr << std::endl; 56 | } 57 | for (int i = 0; i < NP; ++i) 58 | set_be_bit(generator, i, get_be_bit(generator, i+1)); 59 | set_be_bit(generator, NP, 0); 60 | } 61 | void operator()(const uint8_t *data, uint8_t *parity, int data_len = K) 62 | { 63 | assert(0 < data_len && data_len <= K); 64 | // $code = data * x^{NP} + (data * x^{NP}) \mod{generator}$ 65 | for (int l = 0; l <= (NP-1)/8; ++l) 66 | parity[l] = 0; 67 | for (int i = 0; i < data_len; ++i) { 68 | if (get_be_bit(data, i) != get_be_bit(parity, 0)) { 69 | for (int l = 0; l < (NP-1)/8; ++l) 70 | parity[l] = generator[l] ^ slb1(parity, l); 71 | parity[(NP-1)/8] = generator[(NP-1)/8] ^ (parity[(NP-1)/8]<<1); 72 | } else { 73 | for (int l = 0; l < (NP-1)/8; ++l) 74 | parity[l] = slb1(parity, l); 75 | parity[(NP-1)/8] <<= 1; 76 | } 77 | } 78 | } 79 | }; 80 | 81 | template 82 | class BoseChaudhuriHocquenghemEncoderReference 83 | { 84 | public: 85 | typedef typename GF::value_type value_type; 86 | typedef typename GF::ValueType ValueType; 87 | typedef typename GF::IndexType IndexType; 88 | static const int NR = ROOTS; 89 | static const int N = GF::N, K = MSG, NP = N - K; 90 | private: 91 | ValueType generator[NP+1]; 92 | public: 93 | BoseChaudhuriHocquenghemEncoderReference(std::initializer_list minimal_polynomials) 94 | { 95 | // $generator(x) = \prod_i(minpoly_i(x))$ 96 | int generator_degree = 1; 97 | generator[0] = ValueType(1); 98 | for (int i = 1; i <= NP; ++i) 99 | generator[i] = ValueType(0); 100 | for (auto m: minimal_polynomials) { 101 | assert(0 < m && m < 1<<(GF::M+1)); 102 | int m_degree = GF::M; 103 | while (!(m>>m_degree)) 104 | --m_degree; 105 | assert(generator_degree + m_degree <= NP + 1); 106 | for (int i = generator_degree; i >= 0; --i) { 107 | if (!generator[i]) 108 | continue; 109 | generator[i] = ValueType(m&1); 110 | for (int j = 1; j <= m_degree; ++j) 111 | generator[i+j] += ValueType((m>>j)&1); 112 | } 113 | generator_degree += m_degree; 114 | } 115 | assert(generator_degree == NP + 1); 116 | if (0) { 117 | IndexType root(FCR), pe(1); 118 | for (int i = 0; i < NR; ++i) { 119 | ValueType tmp(generator[NP]); 120 | for (int j = 1; j <= NP; ++j) 121 | tmp = fma(root, tmp, generator[NP-j]); 122 | assert(!tmp); 123 | root *= pe; 124 | } 125 | std::cerr << "generator ="; 126 | for (int i = 0; i <= NP; ++i) 127 | std::cerr << " " << (int)generator[i]; 128 | std::cerr << std::endl; 129 | } 130 | } 131 | void operator()(const ValueType *data, ValueType *parity, int data_len = K) 132 | { 133 | assert(0 < data_len && data_len <= K); 134 | // $code = data * x^{NP} + (data * x^{NP}) \mod{generator}$ 135 | for (int i = 0; i < NP; ++i) 136 | parity[i] = ValueType(0); 137 | for (int i = 0; i < data_len; ++i) { 138 | if (data[i] != parity[0]) { 139 | for (int j = 1; j < NP; ++j) 140 | parity[j-1] = generator[NP-j] + parity[j]; 141 | parity[NP-1] = generator[0]; 142 | } else { 143 | for (int j = 1; j < NP; ++j) 144 | parity[j-1] = parity[j]; 145 | parity[NP-1] = ValueType(0); 146 | } 147 | } 148 | } 149 | }; 150 | 151 | } 152 | 153 | -------------------------------------------------------------------------------- /cauchy_prime_field_erasure_coding.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Cauchy Prime Field Erasure Coding 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | struct CauchyPrimeFieldErasureCoding 13 | { 14 | static_assert(MAX_LEN < int(PF::P-1), "Block length must be smaller than largest field value"); 15 | PF temp[MAX_LEN]; 16 | typedef unsigned used_word; 17 | static constexpr int used_width = 8 * sizeof(used_word); 18 | static constexpr int used_length = (PF::P + used_width - 1) / used_width; 19 | used_word used_values[used_length]; 20 | PF row_num, row_den; 21 | // $a_{ij} = \frac{1}{x_i + y_j}$ 22 | PF cauchy_matrix(int i, int j) 23 | { 24 | PF row(i), col(j); 25 | return rcp(row + col); 26 | } 27 | // $b_{ij} = \frac{\prod_{k=1}^{n}{(x_j + y_k)(x_k + y_i)}}{(x_j + y_i)\prod_{k \ne j}^{n}{(x_j - x_k)}\prod_{k \ne i}^{n}{(y_i - y_k)}}$ 28 | PF inverse_cauchy_matrix(const IO *rows, int i, int j, int n) 29 | { 30 | #if 0 31 | PF row_j(rows[j]), col_i(i); 32 | PF prod_xy(1), prod_x(1), prod_y(1); 33 | for (int k = 0; k < n; k++) { 34 | PF row_k(rows[k]), col_k(k); 35 | prod_xy *= (row_j + col_k) * (row_k + col_i); 36 | if (k != j) 37 | prod_x *= (row_j - row_k); 38 | if (k != i) 39 | prod_y *= (col_i - col_k); 40 | } 41 | return prod_xy / ((row_j + col_i) * prod_x * prod_y); 42 | #else 43 | PF row_j(rows[j]), col_i(i); 44 | if (j == 0) { 45 | PF num(1), den(1); 46 | for (int k = 0, r = 2; k < n; k++, --r) { 47 | PF row_k(rows[k]), col_k(k); 48 | num = mul(num, add(row_k, col_i)); 49 | if (k != i) 50 | den = mul(den, sub(col_i, col_k)); 51 | if (!r) { 52 | r = 3; 53 | num = reduce(num); 54 | den = reduce(den); 55 | } 56 | } 57 | row_num = reduce(num); 58 | row_den = reduce(den); 59 | } 60 | PF num(row_num), den(row_den); 61 | for (int k = 0, r = 2; k < n; k++, --r) { 62 | PF row_k(rows[k]), col_k(k); 63 | num = mul(num, add(row_j, col_k)); 64 | if (k != j) 65 | den = mul(den, sub(row_j, row_k)); 66 | if (!r) { 67 | r = 3; 68 | num = reduce(num); 69 | den = reduce(den); 70 | } 71 | } 72 | num = reduce(num); 73 | den = reduce(den); 74 | return num / (add(row_j, col_i) * den); 75 | #endif 76 | } 77 | void mac(const IO *a, PF b, int len, bool first, bool last) 78 | { 79 | if (first && last) { 80 | for (int i = 0; i < len; i++) 81 | temp[i] = b * PF(a[i]); 82 | } else if (first) { 83 | for (int i = 0; i < len; i++) 84 | temp[i] = mul(b, PF(a[i])); 85 | } else if (last) { 86 | for (int i = 0; i < len; i++) 87 | temp[i] = reduce(add(temp[i], mul(b, PF(a[i])))); 88 | } else { 89 | for (int i = 0; i < len; i++) 90 | temp[i] = add(temp[i], mul(b, PF(a[i]))); 91 | } 92 | } 93 | void mac_sub(IO *c, const IO *a, PF b, IO s, int len, bool first, bool last) 94 | { 95 | int v = PF::P-1; 96 | if (first && last) { 97 | for (int i = 0; i < len; i++) 98 | c[i] = (b * PF(a[i] == s ? v : a[i]))(); 99 | } else if (first) { 100 | for (int i = 0; i < len; i++) 101 | temp[i] = mul(b, PF(a[i] == s ? v : a[i])); 102 | } else if (last) { 103 | for (int i = 0; i < len; i++) 104 | c[i] = reduce(add(temp[i], mul(b, PF(a[i] == s ? v : a[i]))))(); 105 | } else { 106 | for (int i = 0; i < len; i++) 107 | temp[i] = add(temp[i], mul(b, PF(a[i] == s ? v : a[i]))); 108 | } 109 | } 110 | int find_unused(int block_len) 111 | { 112 | for (int i = 0; i < used_length; ++i) 113 | used_values[i] = 0; 114 | for (int i = 0; i < block_len; ++i) 115 | used_values[temp[i]()/used_width] |= 1 << temp[i]()%used_width; 116 | int s = 0; 117 | while (used_values[s/used_width] & 1 << s%used_width) 118 | ++s; 119 | return s; 120 | } 121 | int encode(const IO *data, IO *block, int block_id, int block_len, int block_cnt) 122 | { 123 | assert(block_id >= block_cnt && block_id < int(PF::P) / 2); 124 | assert(block_len <= MAX_LEN); 125 | for (int k = 0; k < block_cnt; k++) { 126 | PF a_ik = cauchy_matrix(block_id, k); 127 | mac(data + block_len * k, a_ik, block_len, !k, k == block_cnt - 1); 128 | } 129 | int sub = find_unused(block_len); 130 | for (int i = 0; i < block_len; ++i) 131 | block[i] = temp[i]() == PF::P-1 ? sub : temp[i](); 132 | return sub; 133 | } 134 | void decode(IO *data, const IO *blocks, const IO *block_subs, const IO *block_ids, int block_idx, int block_len, int block_cnt) 135 | { 136 | assert(block_len <= MAX_LEN); 137 | for (int k = 0; k < block_cnt; k++) { 138 | PF b_ik = inverse_cauchy_matrix(block_ids, block_idx, k, block_cnt); 139 | mac_sub(data, blocks + block_len * k, b_ik, block_subs[k], block_len, !k, k == block_cnt - 1); 140 | } 141 | } 142 | }; 143 | 144 | } 145 | 146 | -------------------------------------------------------------------------------- /crc.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Cyclic redundancy check 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class CRC 13 | { 14 | TYPE lut[256]; 15 | TYPE poly; 16 | TYPE crc; 17 | TYPE update(TYPE prev, bool data) 18 | { 19 | TYPE tmp = prev ^ data; 20 | return (prev >> 1) ^ ((tmp & 1) * poly); 21 | } 22 | public: 23 | CRC(TYPE poly, TYPE crc = 0) : poly(poly), crc(crc) 24 | { 25 | for (int j = 0; j < 256; ++j) { 26 | TYPE tmp = j; 27 | for (int i = 8; i; --i) 28 | tmp = update(tmp, 0); 29 | lut[j] = tmp; 30 | } 31 | } 32 | void reset(TYPE v = 0) 33 | { 34 | crc = v; 35 | } 36 | TYPE operator()() 37 | { 38 | return crc; 39 | } 40 | TYPE operator()(bool data) 41 | { 42 | return crc = update(crc, data); 43 | } 44 | TYPE operator()(uint8_t data) 45 | { 46 | TYPE tmp = crc ^ data; 47 | return crc = (crc >> 8) ^ lut[tmp & 255]; 48 | } 49 | 50 | TYPE operator()(uint16_t data) 51 | { 52 | (*this)(uint8_t(data & 255)); 53 | (*this)(uint8_t((data >> 8) & 255)); 54 | return crc; 55 | } 56 | TYPE operator()(uint32_t data) 57 | { 58 | (*this)(uint8_t(data & 255)); 59 | (*this)(uint8_t((data >> 8) & 255)); 60 | (*this)(uint8_t((data >> 16) & 255)); 61 | (*this)(uint8_t((data >> 24) & 255)); 62 | return crc; 63 | } 64 | TYPE operator()(uint64_t data) 65 | { 66 | (*this)(uint8_t(data & 255)); 67 | (*this)(uint8_t((data >> 8) & 255)); 68 | (*this)(uint8_t((data >> 16) & 255)); 69 | (*this)(uint8_t((data >> 24) & 255)); 70 | (*this)(uint8_t((data >> 32) & 255)); 71 | (*this)(uint8_t((data >> 40) & 255)); 72 | (*this)(uint8_t((data >> 48) & 255)); 73 | (*this)(uint8_t((data >> 56) & 255)); 74 | return crc; 75 | } 76 | }; 77 | 78 | template<> 79 | uint8_t CRC::operator()(uint8_t data) 80 | { 81 | return crc = lut[crc ^ data]; 82 | } 83 | 84 | } 85 | 86 | -------------------------------------------------------------------------------- /exclusive_reduce.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Reduce N times while excluding ith input element 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | void exclusive_reduce(const TYPE *in, TYPE *out, int N, OPERATOR op) 13 | { 14 | TYPE pre = in[0]; 15 | for (int i = 1; i < N-1; ++i) { 16 | out[i] = pre; 17 | pre = op(pre, in[i]); 18 | } 19 | out[N-1] = pre; 20 | TYPE suf = in[N-1]; 21 | for (int i = N-2; i > 0; --i) { 22 | out[i] = op(out[i], suf); 23 | suf = op(suf, in[i]); 24 | } 25 | out[0] = suf; 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /hadamard_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Soft decoder for augmented Hadamard codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class HadamardDecoder 13 | { 14 | static const int N = 1 << (K - 1); 15 | public: 16 | int operator()(const int8_t *code) 17 | { 18 | int sum[N]; 19 | for (int i = 0; i < N-1; i += 2) { 20 | sum[i] = code[i] + code[i+1]; 21 | sum[i+1] = code[i] - code[i+1]; 22 | } 23 | for (int h = 2; h < N; h *= 2) { 24 | for (int i = 0; i < N; i += 2 * h) { 25 | for (int j = i; j < i + h; ++j) { 26 | int x = sum[j] + sum[j+h]; 27 | int y = sum[j] - sum[j+h]; 28 | sum[j] = x; 29 | sum[j+h] = y; 30 | } 31 | } 32 | } 33 | int word = 0, best = 0, next = 0; 34 | for (int i = 0; i < N; ++i) { 35 | int mag = std::abs(sum[i]); 36 | int msg = i + N * (sum[i] < 0); 37 | if (mag > best) { 38 | next = best; 39 | best = mag; 40 | word = msg; 41 | } else if (mag > next) { 42 | next = mag; 43 | } 44 | } 45 | if (best == next) 46 | return -1; 47 | return word; 48 | } 49 | }; 50 | 51 | template <> 52 | struct HadamardDecoder<2> 53 | { 54 | int operator()(const int8_t *code) 55 | { 56 | int x = code[0] + code[1]; 57 | int y = code[0] - code[1]; 58 | int mx = std::abs(x); 59 | int my = std::abs(y); 60 | if (mx == my) 61 | return -1; 62 | if (mx > my) 63 | return 2 * (x < 0); 64 | return 1 + 2 * (y < 0); 65 | } 66 | }; 67 | 68 | template <> 69 | class HadamardDecoder<3> 70 | { 71 | static const int K = 3; 72 | static const int N = 1 << (K - 1); 73 | public: 74 | int operator()(const int8_t *code) 75 | { 76 | int tmp[4] = { 77 | code[0] + code[1], 78 | code[0] - code[1], 79 | code[2] + code[3], 80 | code[2] - code[3], 81 | }; 82 | int sum[4] = { 83 | tmp[0] + tmp[2], 84 | tmp[1] + tmp[3], 85 | tmp[0] - tmp[2], 86 | tmp[1] - tmp[3], 87 | }; 88 | int word = 0, best = 0, next = 0; 89 | for (int i = 0; i < N; ++i) { 90 | int mag = std::abs(sum[i]); 91 | int msg = i + N * (sum[i] < 0); 92 | if (mag > best) { 93 | next = best; 94 | best = mag; 95 | word = msg; 96 | } else if (mag > next) { 97 | next = mag; 98 | } 99 | } 100 | if (best == next) 101 | return -1; 102 | return word; 103 | } 104 | }; 105 | 106 | template <> 107 | class HadamardDecoder<4> 108 | { 109 | static const int K = 4; 110 | static const int N = 1 << (K - 1); 111 | public: 112 | int operator()(const int8_t *c) 113 | { 114 | int d[8] = { 115 | c[0] + c[1], 116 | c[0] - c[1], 117 | c[2] + c[3], 118 | c[2] - c[3], 119 | c[4] + c[5], 120 | c[4] - c[5], 121 | c[6] + c[7], 122 | c[6] - c[7], 123 | }; 124 | int e[8] = { 125 | d[0] + d[2], 126 | d[1] + d[3], 127 | d[0] - d[2], 128 | d[1] - d[3], 129 | d[4] + d[6], 130 | d[5] + d[7], 131 | d[4] - d[6], 132 | d[5] - d[7], 133 | }; 134 | int sum[8] = { 135 | e[0] + e[4], 136 | e[1] + e[5], 137 | e[2] + e[6], 138 | e[3] + e[7], 139 | e[0] - e[4], 140 | e[1] - e[5], 141 | e[2] - e[6], 142 | e[3] - e[7], 143 | }; 144 | int word = 0, best = 0, next = 0; 145 | for (int i = 0; i < N; ++i) { 146 | int mag = std::abs(sum[i]); 147 | int msg = i + N * (sum[i] < 0); 148 | if (mag > best) { 149 | next = best; 150 | best = mag; 151 | word = msg; 152 | } else if (mag > next) { 153 | next = mag; 154 | } 155 | } 156 | if (best == next) 157 | return -1; 158 | return word; 159 | } 160 | }; 161 | 162 | } 163 | 164 | -------------------------------------------------------------------------------- /hadamard_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Encoder for augmented Hadamard codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class HadamardEncoder 13 | { 14 | static const int W = 1 << K; 15 | static const int N = 1 << (K - 1); 16 | int8_t mod[W]; 17 | 18 | static bool parity(unsigned x) 19 | { 20 | x ^= x >> 16; 21 | x ^= x >> 8; 22 | x ^= x >> 4; 23 | x ^= x >> 2; 24 | x ^= x >> 1; 25 | return x & 1; 26 | } 27 | public: 28 | HadamardEncoder() 29 | { 30 | for (int i = 0; i < W; ++i) 31 | mod[i] = 1 - 2 * parity(i); 32 | } 33 | void operator()(int8_t *code, int msg) 34 | { 35 | for (int i = 0; i < N; ++i) 36 | code[i] = mod[msg&(i|N)]; 37 | } 38 | }; 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /interleave.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Interleavers 3 | 4 | Copyright 2023 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | static void Interleave(TYPE *out, const TYPE *in) 13 | { 14 | static_assert(SIZE % ORDER == 0, "ORDER does not divide SIZE"); 15 | int LENGTH = SIZE / ORDER; 16 | for (int i = 0; i < LENGTH; ++i) 17 | for (int j = 0; j < ORDER; ++j) 18 | out[ORDER*i+j] = in[i+j*LENGTH]; 19 | } 20 | 21 | template 22 | static void Deinterleave(TYPE *out, const TYPE *in) 23 | { 24 | static_assert(SIZE % ORDER == 0, "ORDER does not divide SIZE"); 25 | int LENGTH = SIZE / ORDER; 26 | for (int i = 0; i < LENGTH; ++i) 27 | for (int j = 0; j < ORDER; ++j) 28 | out[i+j*LENGTH] = in[ORDER*i+j]; 29 | } 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /ldpc_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | LDPC SISO layered decoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "simd.hh" 11 | #include "rotate.hh" 12 | 13 | namespace CODE { 14 | 15 | template 16 | class LDPCDecoder 17 | { 18 | #ifdef __AVX2__ 19 | static const int SIMD_SIZE = 32; 20 | // M = 360 = 30 * 12 21 | static const int WORD_SIZE = 30; 22 | #else 23 | static const int SIMD_SIZE = 16; 24 | // M = 360 = 15 * 24 25 | static const int WORD_SIZE = 15; 26 | #endif 27 | static_assert(TABLE::M % WORD_SIZE == 0, "M must be multiple of word size"); 28 | static_assert(WORD_SIZE <= SIMD_SIZE, "SIMD size must be bigger or equal word size"); 29 | static const int M = TABLE::M; 30 | static const int N = TABLE::N; 31 | static const int K = TABLE::K; 32 | static const int R = N-K; 33 | static const int q = R/M; 34 | static const int D = WORD_SIZE; 35 | static const int W = M/D; 36 | static const int PTY = R/D; 37 | static const int MSG = K/D; 38 | static const int CNC = TABLE::LINKS_MAX_CN - 2; 39 | static const int BNL = (TABLE::LINKS_TOTAL + D-1) / D; 40 | static const int LOC = (TABLE::LINKS_TOTAL - (2*R-1) + D-1) / D; 41 | 42 | typedef SIMD TYPE; 43 | typedef struct { uint16_t off; uint16_t shi; } Loc; 44 | typedef uint32_t wd_t; 45 | static_assert(sizeof(wd_t) * 8 >= CNC, "write disable mask needs at least as many bits as max check node links"); 46 | Rotate rotate; 47 | 48 | TYPE bnl[BNL]; 49 | TYPE msg[MSG]; 50 | TYPE pty[PTY]; 51 | Loc loc[LOC]; 52 | wd_t wd[PTY]; 53 | uint8_t cnc[q]; 54 | 55 | static TYPE eor(TYPE a, TYPE b) 56 | { 57 | return vreinterpret(veor(vmask(a), vmask(b))); 58 | } 59 | static TYPE orr(TYPE a, TYPE b) 60 | { 61 | return vreinterpret(vorr(vmask(a), vmask(b))); 62 | } 63 | static TYPE other(TYPE a, TYPE b, TYPE c) 64 | { 65 | return vreinterpret(vbsl(vceq(a, b), vmask(c), vmask(b))); 66 | } 67 | static TYPE mine(TYPE a, TYPE b) 68 | { 69 | return orr(eor(a, b), vdup(127)); 70 | } 71 | static TYPE selfcorr(TYPE a, TYPE b) 72 | { 73 | return vreinterpret(vand(vmask(b), vorr(vceqz(a), veor(vcgtz(a), vcltz(b))))); 74 | } 75 | 76 | bool bad() 77 | { 78 | Loc *lo = loc; 79 | for (int i = 0; i < q; ++i) { 80 | int cnt = cnc[i]; 81 | int deg = cnt + 2; 82 | auto res = vmask(vzero()); 83 | for (int j = 0; j < W; ++j) { 84 | TYPE cnv = vdup(1); 85 | for (int k = 0; k < deg; ++k) { 86 | TYPE tmp; 87 | if (k < cnt) { 88 | tmp = rotate(msg[lo[k].off], -lo[k].shi); 89 | } else if (k == cnt) { 90 | tmp = pty[W*i+j]; 91 | } else { 92 | if (i) { 93 | tmp = pty[W*(i-1)+j]; 94 | } else if (j) { 95 | tmp = pty[W*(q-1)+j-1]; 96 | } else { 97 | tmp = rotate(pty[PTY-1], 1); 98 | tmp.v[0] = 127; 99 | } 100 | } 101 | cnv = vsign(cnv, tmp); 102 | } 103 | res = vorr(res, vclez(cnv)); 104 | lo += cnt; 105 | } 106 | for (int n = 0; n < D; ++n) 107 | if (res.v[n]) 108 | return true; 109 | } 110 | return false; 111 | } 112 | void update() 113 | { 114 | TYPE *bl = bnl; 115 | Loc *lo = loc; 116 | for (int i = 0; i < q; ++i) { 117 | int cnt = cnc[i]; 118 | int deg = cnt + 2; 119 | for (int j = 0; j < W; ++j) { 120 | TYPE mags[deg], inps[deg]; 121 | TYPE min0 = vdup(127); 122 | TYPE min1 = vdup(127); 123 | TYPE signs = vdup(127); 124 | 125 | for (int k = 0; k < deg; ++k) { 126 | TYPE tmp; 127 | if (k < cnt) { 128 | tmp = rotate(msg[lo[k].off], -lo[k].shi); 129 | } else if (k == cnt) { 130 | tmp = pty[W*i+j]; 131 | } else { 132 | if (i) { 133 | tmp = pty[W*(i-1)+j]; 134 | } else if (j) { 135 | tmp = pty[W*(q-1)+j-1]; 136 | } else { 137 | tmp = rotate(pty[PTY-1], 1); 138 | tmp.v[0] = 127; 139 | } 140 | } 141 | 142 | TYPE inp = vqsub(tmp, bl[k]); 143 | 144 | TYPE mag = vqabs(inp); 145 | 146 | if (BETA) { 147 | auto beta = vunsigned(vdup(BETA)); 148 | mag = vsigned(vqsub(vunsigned(mag), beta)); 149 | } 150 | 151 | min1 = vmin(min1, vmax(min0, mag)); 152 | min0 = vmin(min0, mag); 153 | 154 | signs = eor(signs, inp); 155 | 156 | inps[k] = inp; 157 | mags[k] = mag; 158 | } 159 | for (int k = 0; k < deg; ++k) { 160 | TYPE mag = mags[k]; 161 | TYPE inp = inps[k]; 162 | 163 | TYPE out = vsign(other(mag, min0, min1), mine(signs, inp)); 164 | 165 | out = vclamp(out, -31, 31); 166 | 167 | out = selfcorr(bl[k], out); 168 | 169 | TYPE tmp = vqadd(inp, out); 170 | 171 | if (k < cnt) { 172 | if (!((wd[W*i+j]>>k)&1)) { 173 | bl[k] = out; 174 | msg[lo[k].off] = rotate(tmp, lo[k].shi); 175 | } 176 | } else if (k == cnt) { 177 | bl[k] = out; 178 | pty[W*i+j] = tmp; 179 | } else { 180 | bl[k] = out; 181 | if (i) { 182 | pty[W*(i-1)+j] = tmp; 183 | } else if (j) { 184 | pty[W*(q-1)+j-1] = tmp; 185 | } else { 186 | tmp.v[0] = pty[PTY-1].v[D-1]; 187 | pty[PTY-1] = rotate(tmp, -1); 188 | } 189 | } 190 | } 191 | if (wd[W*i+j]) { 192 | for (int first = 0, c = 1; c < cnt; ++c) { 193 | if (lo[first].off != lo[c].off || c == cnt-1) { 194 | int last = c - 1; 195 | if (c == cnt-1) 196 | ++last; 197 | if (last != first) { 198 | int count = last - first + 1; 199 | wd_t mask = ((1 << count) - 1) << first; 200 | wd_t cur = wd[W*i+j]; 201 | wd_t tmp = cur & mask; 202 | wd_t ror = (tmp >> 1) | (tmp << (count-1)); 203 | wd[W*i+j] = (cur & ~mask) | (ror & mask); 204 | } 205 | first = c; 206 | } 207 | } 208 | } 209 | lo += cnt; 210 | bl += deg; 211 | } 212 | } 213 | //assert(bl <= bnl + BNL); 214 | //std::cerr << BNL - (bl - bnl) << std::endl; 215 | } 216 | public: 217 | LDPCDecoder() 218 | { 219 | uint16_t pos[q * CNC]; 220 | for (int i = 0; i < q; ++i) 221 | cnc[i] = 0; 222 | int bit_pos = 0; 223 | const int *row_ptr = TABLE::POS; 224 | for (int g = 0; TABLE::LEN[g]; ++g) { 225 | int bit_deg = TABLE::DEG[g]; 226 | for (int r = 0; r < TABLE::LEN[g]; ++r) { 227 | for (int d = 0; d < bit_deg; ++d) { 228 | int n = row_ptr[d] % q; 229 | int m = row_ptr[d] / q; 230 | pos[CNC*n+cnc[n]++] = bit_pos + (M - m) % M; 231 | } 232 | row_ptr += bit_deg; 233 | bit_pos += M; 234 | } 235 | } 236 | Loc *lo = loc; 237 | for (int i = 0; i < q; ++i) { 238 | int cnt = cnc[i]; 239 | int offset[cnt], shift[cnt]; 240 | for (int c = 0; c < cnt; ++c) { 241 | shift[c] = pos[CNC*i+c] % M; 242 | offset[c] = pos[CNC*i+c] - shift[c]; 243 | } 244 | for (int j = 0; j < W; ++j) { 245 | for (int c = 0; c < cnt; ++c) { 246 | lo[c].off = offset[c] / D + shift[c] % W; 247 | lo[c].shi = shift[c] / W; 248 | shift[c] = (shift[c] + 1) % M; 249 | } 250 | std::sort(lo, lo + cnt, [](const Loc &a, const Loc &b){ return a.off < b.off; }); 251 | wd[W*i+j] = 0; 252 | for (int c = 0; c < cnt-1; ++c) 253 | if (lo[c].off == lo[c+1].off) 254 | wd[W*i+j] |= 1 << c; 255 | lo += cnt; 256 | } 257 | } 258 | //assert(lo <= loc + LOC); 259 | //std::cerr << LOC - (lo - loc) << std::endl; 260 | } 261 | int operator()(int8_t *message, int8_t *parity, int trials = 25) 262 | { 263 | for (int i = 0; i < BNL; ++i) 264 | bnl[i] = vzero(); 265 | for (int i = 0; i < K/M; ++i) 266 | for (int j = 0; j < W; ++j) 267 | for (int n = 0; n < D; ++n) 268 | msg[W*i+j].v[n] = message[M*i+W*n+j]; 269 | for (int i = 0; i < q; ++i) 270 | for (int j = 0; j < W; ++j) 271 | for (int n = 0; n < D; ++n) 272 | pty[W*i+j].v[n] = parity[q*(W*n+j)+i]; 273 | while (bad() && --trials >= 0) 274 | update(); 275 | for (int i = 0; i < K/M; ++i) 276 | for (int j = 0; j < W; ++j) 277 | for (int n = 0; n < D; ++n) 278 | message[M*i+W*n+j] = msg[W*i+j].v[n]; 279 | for (int i = 0; i < q; ++i) 280 | for (int j = 0; j < W; ++j) 281 | for (int n = 0; n < D; ++n) 282 | parity[q*(W*n+j)+i] = pty[W*i+j].v[n]; 283 | return trials; 284 | } 285 | }; 286 | 287 | } 288 | 289 | -------------------------------------------------------------------------------- /ldpc_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | LDPC SISO encoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class LDPCEncoder 13 | { 14 | static const int M = TABLE::M; 15 | static const int N = TABLE::N; 16 | static const int K = TABLE::K; 17 | static const int R = N-K; 18 | static const int q = R/M; 19 | static const int CNC = TABLE::LINKS_MAX_CN - 2; 20 | 21 | uint16_t pos[R * CNC]; 22 | uint8_t cnc[q]; 23 | public: 24 | LDPCEncoder() 25 | { 26 | for (int i = 0; i < q; ++i) 27 | cnc[i] = 0; 28 | int bit_pos = 0; 29 | const int *row_ptr = TABLE::POS; 30 | for (int g = 0; TABLE::LEN[g]; ++g) { 31 | int bit_deg = TABLE::DEG[g]; 32 | for (int r = 0; r < TABLE::LEN[g]; ++r) { 33 | for (int d = 0; d < bit_deg; ++d) { 34 | int n = row_ptr[d] % q; 35 | int m = row_ptr[d] / q; 36 | pos[CNC*n+cnc[n]++] = bit_pos + (M - m) % M; 37 | } 38 | row_ptr += bit_deg; 39 | bit_pos += M; 40 | } 41 | } 42 | for (int i = 0; i < q; ++i) { 43 | int cnt = cnc[i]; 44 | int offset[cnt], shift[cnt]; 45 | for (int c = 0; c < cnt; ++c) { 46 | shift[c] = pos[CNC*i+c] % M; 47 | offset[c] = pos[CNC*i+c] - shift[c]; 48 | } 49 | for (int j = 1; j < M; ++j) { 50 | for (int c = 0; c < cnt; ++c) { 51 | shift[c] = (shift[c] + 1) % M; 52 | pos[CNC*(q*j+i)+c] = offset[c] + shift[c]; 53 | } 54 | } 55 | } 56 | } 57 | void operator()(const int8_t *data, int8_t *parity) 58 | { 59 | int8_t tmp = 1; 60 | for (int j = 0; j < M; ++j) { 61 | for (int i = 0; i < q; ++i) { 62 | int cnt = cnc[i]; 63 | for (int c = 0; c < cnt; ++c) 64 | tmp *= data[pos[CNC*(q*j+i)+c]]; 65 | parity[q*j+i] = tmp; 66 | } 67 | } 68 | } 69 | }; 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /mls.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Maximum length sequence 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | class MLS 12 | { 13 | static int hibit(unsigned n) 14 | { 15 | n |= n >> 1; 16 | n |= n >> 2; 17 | n |= n >> 4; 18 | n |= n >> 8; 19 | n |= n >> 16; 20 | return n ^ (n >> 1); 21 | } 22 | int poly, test, reg; 23 | public: 24 | MLS(int poly = 0b100000000000000001001, int reg = 1) : poly(poly), test(hibit(poly)>>1), reg(reg) {} 25 | void reset(int r = 1) 26 | { 27 | reg = r; 28 | } 29 | bool bad(int r = 1) 30 | { 31 | reg = r; 32 | int len = hibit(poly) - 1; 33 | for (int i = 1; i < len; ++i) { 34 | (*this)(); 35 | if (reg == r) 36 | return true; 37 | } 38 | (*this)(); 39 | return reg != r; 40 | } 41 | bool operator()() 42 | { 43 | bool fb = reg & test; 44 | reg <<= 1; 45 | reg ^= fb * poly; 46 | return fb; 47 | } 48 | }; 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /osd.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Ordered statistics decoding 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "bitman.hh" 11 | #include "sort.hh" 12 | 13 | namespace CODE { 14 | 15 | template 16 | class LinearEncoder 17 | { 18 | public: 19 | void operator()(uint8_t *codeword, const uint8_t *message, const int8_t *genmat) 20 | { 21 | for (int i = 0; i < N; ++i) 22 | set_be_bit(codeword, i, get_be_bit(message, 0) & genmat[i]); 23 | for (int j = 1; j < K; ++j) 24 | if (get_be_bit(message, j)) 25 | for (int i = 0; i < N; ++i) 26 | xor_be_bit(codeword, i, genmat[N*j+i]); 27 | } 28 | }; 29 | 30 | template 31 | class BoseChaudhuriHocquenghemGenerator 32 | { 33 | static const int NP = N - K; 34 | public: 35 | static void poly(int8_t *genpoly, std::initializer_list minimal_polynomials) 36 | { 37 | // $genpoly(x) = \prod_i(minpoly_i(x))$ 38 | int genpoly_degree = 1; 39 | for (int i = 0; i < NP; ++i) 40 | genpoly[i] = 0; 41 | genpoly[NP] = 1; 42 | for (auto m: minimal_polynomials) { 43 | assert(0 < m); 44 | assert(m & 1); 45 | int m_degree = 0; 46 | while (m>>m_degree) 47 | ++m_degree; 48 | --m_degree; 49 | assert(genpoly_degree + m_degree <= NP + 1); 50 | for (int i = genpoly_degree; i >= 0; --i) { 51 | if (!genpoly[NP-i]) 52 | continue; 53 | genpoly[NP-i] = m&1; 54 | for (int j = 1; j <= m_degree; ++j) 55 | genpoly[NP-(i+j)] ^= (m>>j)&1; 56 | } 57 | genpoly_degree += m_degree; 58 | } 59 | assert(genpoly_degree == NP + 1); 60 | assert(genpoly[0]); 61 | assert(genpoly[NP]); 62 | if (0) { 63 | std::cerr << "generator polynomial =" << std::endl; 64 | for (int i = 0; i <= NP; ++i) 65 | std::cerr << (int)genpoly[i]; 66 | std::cerr << std::endl; 67 | } 68 | } 69 | static void matrix(int8_t *genmat, bool systematic, std::initializer_list minimal_polynomials) 70 | { 71 | poly(genmat, minimal_polynomials); 72 | for (int i = NP+1; i < N; ++i) 73 | genmat[i] = 0; 74 | for (int j = 1; j < K; ++j) { 75 | for (int i = 0; i < j; ++i) 76 | genmat[N*j+i] = 0; 77 | for (int i = 0; i <= NP; ++i) 78 | genmat[(N+1)*j+i] = genmat[i]; 79 | for (int i = j+NP+1; i < N; ++i) 80 | genmat[N*j+i] = 0; 81 | } 82 | if (systematic) 83 | for (int k = K-1; k; --k) 84 | for (int j = 0; j < k; ++j) 85 | if (genmat[N*j+k]) 86 | for (int i = k; i < N; ++i) 87 | genmat[N*j+i] ^= genmat[N*k+i]; 88 | if (0) { 89 | std::cerr << "generator matrix =" << std::endl; 90 | for (int j = 0; j < K; ++j) { 91 | for (int i = 0; i < N; ++i) 92 | std::cerr << (int)genmat[N*j+i]; 93 | std::cerr << std::endl; 94 | } 95 | } 96 | } 97 | }; 98 | 99 | template 100 | class OrderedStatisticsDecoder 101 | { 102 | static const int S = sizeof(size_t); 103 | static const int W = (N+S-1) & ~(S-1); 104 | int8_t G[W*K]; 105 | int8_t codeword[W], candidate[W]; 106 | int8_t softperm[W]; 107 | int16_t perm[W]; 108 | MergeSort sort; 109 | void row_echelon() 110 | { 111 | for (int k = 0; k < K; ++k) { 112 | // find pivot in this column 113 | for (int j = k; j < K; ++j) { 114 | if (G[W*j+k]) { 115 | for (int i = k; j != k && i < N; ++i) 116 | std::swap(G[W*j+i], G[W*k+i]); 117 | break; 118 | } 119 | } 120 | // keep searching for suitable column for pivot 121 | // beware: this will use columns >= K if necessary. 122 | for (int j = k + 1; !G[W*k+k] && j < N; ++j) { 123 | for (int h = k; h < K; ++h) { 124 | if (G[W*h+j]) { 125 | // account column swap 126 | std::swap(perm[k], perm[j]); 127 | for (int i = 0; i < K; ++i) 128 | std::swap(G[W*i+k], G[W*i+j]); 129 | for (int i = k; h != k && i < N; ++i) 130 | std::swap(G[W*h+i], G[W*k+i]); 131 | break; 132 | } 133 | } 134 | } 135 | assert(G[W*k+k]); 136 | // zero out column entries below pivot 137 | for (int j = k + 1; j < K; ++j) 138 | if (G[W*j+k]) 139 | for (int i = k; i < N; ++i) 140 | G[W*j+i] ^= G[W*k+i]; 141 | } 142 | } 143 | void systematic() 144 | { 145 | for (int k = K-1; k; --k) 146 | for (int j = 0; j < k; ++j) 147 | if (G[W*j+k]) 148 | for (int i = k; i < N; ++i) 149 | G[W*j+i] ^= G[W*k+i]; 150 | } 151 | void encode() 152 | { 153 | for (int i = K; i < N; ++i) 154 | codeword[i] = codeword[0] & G[i]; 155 | for (int j = 1; j < K; ++j) 156 | for (int i = K; i < N; ++i) 157 | codeword[i] ^= codeword[j] & G[W*j+i]; 158 | } 159 | void flip(int j) 160 | { 161 | for (int i = 0; i < W; ++i) 162 | codeword[i] ^= G[W*j+i]; 163 | } 164 | static int metric(const int8_t *hard, const int8_t *soft) 165 | { 166 | int sum = 0; 167 | for (int i = 0; i < W; ++i) 168 | sum += (1 - 2 * hard[i]) * soft[i]; 169 | return sum; 170 | } 171 | public: 172 | bool operator()(uint8_t *hard, const int8_t *soft, const int8_t *genmat) 173 | { 174 | for (int i = 0; i < N; ++i) 175 | perm[i] = i; 176 | for (int i = 0; i < N; ++i) 177 | softperm[i] = std::abs(std::max(soft[i], -127)); 178 | sort(perm, N, [this](int a, int b){ return softperm[a] > softperm[b]; }); 179 | for (int j = 0; j < K; ++j) 180 | for (int i = 0; i < N; ++i) 181 | G[W*j+i] = genmat[N*j+perm[i]]; 182 | row_echelon(); 183 | systematic(); 184 | for (int i = 0; i < N; ++i) 185 | softperm[i] = std::max(soft[perm[i]], -127); 186 | for (int i = N; i < W; ++i) 187 | softperm[i] = 0; 188 | for (int i = 0; i < K; ++i) 189 | codeword[i] = softperm[i] < 0; 190 | encode(); 191 | for (int i = 0; i < N; ++i) 192 | candidate[i] = codeword[i]; 193 | int best = metric(codeword, softperm); 194 | int next = -1; 195 | auto update = [this, &best, &next]() { 196 | int met = metric(codeword, softperm); 197 | if (met > best) { 198 | next = best; 199 | best = met; 200 | for (int i = 0; i < N; ++i) 201 | candidate[i] = codeword[i]; 202 | } else if (met > next) { 203 | next = met; 204 | } 205 | }; 206 | for (int a = 0; O >= 1 && a < K; ++a) { 207 | flip(a); 208 | update(); 209 | for (int b = a + 1; O >= 2 && b < K; ++b) { 210 | flip(b); 211 | update(); 212 | for (int c = b + 1; O >= 3 && c < K; ++c) { 213 | flip(c); 214 | update(); 215 | for (int d = c + 1; O >= 4 && d < K; ++d) { 216 | flip(d); 217 | update(); 218 | for (int e = d + 1; O >= 5 && e < K; ++e) { 219 | flip(e); 220 | update(); 221 | for (int f = e + 1; O >= 6 && f < K; ++f) { 222 | flip(f); 223 | update(); 224 | flip(f); 225 | } 226 | flip(e); 227 | } 228 | flip(d); 229 | } 230 | flip(c); 231 | } 232 | flip(b); 233 | } 234 | flip(a); 235 | } 236 | for (int i = 0; i < N; ++i) 237 | set_be_bit(hard, perm[i], candidate[i]); 238 | return best != next; 239 | } 240 | }; 241 | 242 | template 243 | class OrderedStatisticsListDecoder 244 | { 245 | static const int S = sizeof(size_t); 246 | static const int W = (N+S-1) & ~(S-1); 247 | int8_t G[W*K]; 248 | int8_t codeword[W], candidate[W*L]; 249 | int8_t softperm[W]; 250 | int16_t perm[W]; 251 | int score[L], cperm[L]; 252 | MergeSort sort; 253 | void row_echelon() 254 | { 255 | for (int k = 0; k < K; ++k) { 256 | // find pivot in this column 257 | for (int j = k; j < K; ++j) { 258 | if (G[W*j+k]) { 259 | for (int i = k; j != k && i < N; ++i) 260 | std::swap(G[W*j+i], G[W*k+i]); 261 | break; 262 | } 263 | } 264 | // keep searching for suitable column for pivot 265 | // beware: this will use columns >= K if necessary. 266 | for (int j = k + 1; !G[W*k+k] && j < N; ++j) { 267 | for (int h = k; h < K; ++h) { 268 | if (G[W*h+j]) { 269 | // account column swap 270 | std::swap(perm[k], perm[j]); 271 | for (int i = 0; i < K; ++i) 272 | std::swap(G[W*i+k], G[W*i+j]); 273 | for (int i = k; h != k && i < N; ++i) 274 | std::swap(G[W*h+i], G[W*k+i]); 275 | break; 276 | } 277 | } 278 | } 279 | assert(G[W*k+k]); 280 | // zero out column entries below pivot 281 | for (int j = k + 1; j < K; ++j) 282 | if (G[W*j+k]) 283 | for (int i = k; i < N; ++i) 284 | G[W*j+i] ^= G[W*k+i]; 285 | } 286 | } 287 | void systematic() 288 | { 289 | for (int k = K-1; k; --k) 290 | for (int j = 0; j < k; ++j) 291 | if (G[W*j+k]) 292 | for (int i = k; i < N; ++i) 293 | G[W*j+i] ^= G[W*k+i]; 294 | } 295 | void encode() 296 | { 297 | for (int i = K; i < N; ++i) 298 | codeword[i] = codeword[0] & G[i]; 299 | for (int j = 1; j < K; ++j) 300 | for (int i = K; i < N; ++i) 301 | codeword[i] ^= codeword[j] & G[W*j+i]; 302 | } 303 | void flip(int j) 304 | { 305 | for (int i = 0; i < W; ++i) 306 | codeword[i] ^= G[W*j+i]; 307 | } 308 | int metric() 309 | { 310 | int sum = 0; 311 | for (int i = 0; i < W; ++i) 312 | sum += (1 - 2 * codeword[i]) * softperm[i]; 313 | return sum; 314 | } 315 | void update() 316 | { 317 | int j = L-1; 318 | int met = metric(); 319 | if (met <= score[j]) 320 | return; 321 | int pos = cperm[j]; 322 | for (int i = 0; i < W; ++i) 323 | candidate[pos*W+i] = codeword[i]; 324 | for (; j > 0 && met > score[j-1]; --j) { 325 | score[j] = score[j-1]; 326 | cperm[j] = cperm[j-1]; 327 | } 328 | score[j] = met; 329 | cperm[j] = pos; 330 | } 331 | public: 332 | void operator()(int *rank, uint8_t *hard, const int8_t *soft, const int8_t *genmat) 333 | { 334 | for (int i = 0; i < N; ++i) 335 | perm[i] = i; 336 | for (int i = 0; i < N; ++i) 337 | softperm[i] = std::abs(std::max(soft[i], -127)); 338 | sort(perm, N, [this](int a, int b){ return softperm[a] > softperm[b]; }); 339 | for (int j = 0; j < K; ++j) 340 | for (int i = 0; i < N; ++i) 341 | G[W*j+i] = genmat[N*j+perm[i]]; 342 | row_echelon(); 343 | systematic(); 344 | for (int i = 0; i < N; ++i) 345 | softperm[i] = std::max(soft[perm[i]], -127); 346 | for (int i = N; i < W; ++i) 347 | softperm[i] = 0; 348 | for (int i = 0; i < K; ++i) 349 | codeword[i] = softperm[i] < 0; 350 | encode(); 351 | for (int i = 0; i < W; ++i) 352 | candidate[i] = codeword[i]; 353 | score[0] = metric(); 354 | for (int i = 1; i < L; ++i) 355 | score[i] = -1; 356 | for (int i = 0; i < L; ++i) 357 | cperm[i] = i; 358 | for (int a = 0; O >= 1 && a < K; ++a) { 359 | flip(a); 360 | update(); 361 | for (int b = a + 1; O >= 2 && b < K; ++b) { 362 | flip(b); 363 | update(); 364 | for (int c = b + 1; O >= 3 && c < K; ++c) { 365 | flip(c); 366 | update(); 367 | for (int d = c + 1; O >= 4 && d < K; ++d) { 368 | flip(d); 369 | update(); 370 | for (int e = d + 1; O >= 5 && e < K; ++e) { 371 | flip(e); 372 | update(); 373 | for (int f = e + 1; O >= 6 && f < K; ++f) { 374 | flip(f); 375 | update(); 376 | flip(f); 377 | } 378 | flip(e); 379 | } 380 | flip(d); 381 | } 382 | flip(c); 383 | } 384 | flip(b); 385 | } 386 | flip(a); 387 | } 388 | for (int j = 0, r = 0; j < L; ++j) { 389 | if (j > 0 && score[j-1] != score[j]) 390 | ++r; 391 | rank[j] = r; 392 | for (int i = 0; i < N; ++i) 393 | set_be_bit(hard+j*((N+7)/8), perm[i], candidate[cperm[j]*W+i]); 394 | } 395 | } 396 | }; 397 | 398 | } 399 | 400 | -------------------------------------------------------------------------------- /osd_bch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aicodix/code/c73cc186a2418f906ff97827047b542255bb83c8/osd_bch.png -------------------------------------------------------------------------------- /permute.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Reversible permutations 3 | 4 | Copyright 2023 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "xorshift.hh" 10 | 11 | namespace CODE { 12 | 13 | template 14 | struct FisherYatesShuffle 15 | { 16 | template 17 | void operator()(TYPE *array) 18 | { 19 | CODE::Xorshift32 prng; 20 | for (int i = 0; i < SIZE-1; ++i) 21 | std::swap(array[i], array[i + prng() % (SIZE - i)]); 22 | } 23 | }; 24 | 25 | template 26 | class ReverseFisherYatesShuffle 27 | { 28 | int seq[SIZE-1]; 29 | public: 30 | ReverseFisherYatesShuffle() 31 | { 32 | CODE::Xorshift32 prng; 33 | for (int i = 0; i < SIZE-1; ++i) 34 | seq[i] = i + prng() % (SIZE - i); 35 | } 36 | template 37 | void operator()(TYPE *array) 38 | { 39 | for (int i = SIZE-2; i >= 0; --i) 40 | std::swap(array[i], array[seq[i]]); 41 | } 42 | }; 43 | 44 | template 45 | static void BitReversalPermute(TYPE *array) 46 | { 47 | static_assert(SIZE > 0 && (SIZE & (SIZE - 1)) == 0, "SIZE not power of two"); 48 | for (int i = 0, j = 0; i < SIZE - 1; ++i) { 49 | if (i < j) 50 | std::swap(array[i], array[j]); 51 | int k = SIZE >> 1; 52 | while (j & k) { 53 | j ^= k; 54 | k >>= 1; 55 | } 56 | j ^= k; 57 | } 58 | } 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /polar_ber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aicodix/code/c73cc186a2418f906ff97827047b542255bb83c8/polar_ber.png -------------------------------------------------------------------------------- /polar_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Successive cancellation decoding of polar codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "polar_helper.hh" 11 | 12 | namespace CODE { 13 | 14 | template 15 | struct PolarNode 16 | { 17 | typedef PolarHelper PH; 18 | static const int N = 1 << M; 19 | static void trans(TYPE *out, const TYPE *inp) 20 | { 21 | for (int i = 0; i < N; i += 2) { 22 | out[i] = PH::qmul(inp[i], inp[i+1]); 23 | out[i+1] = inp[i+1]; 24 | } 25 | for (int h = 2; h < N; h *= 2) 26 | for (int i = 0; i < N; i += 2 * h) 27 | for (int j = i; j < i + h; ++j) 28 | out[j] = PH::qmul(out[j], out[j+h]); 29 | } 30 | static void rate0(TYPE *hard) 31 | { 32 | for (int i = 0; i < N; ++i) 33 | hard[i] = PH::one(); 34 | } 35 | static void rate1(TYPE **message, TYPE *hard, TYPE *soft) 36 | { 37 | for (int i = 0; i < N; ++i) 38 | hard[i] = PH::signum(soft[i+N]); 39 | trans(*message, hard); 40 | *message += N; 41 | } 42 | }; 43 | 44 | template 45 | struct PolarNode 46 | { 47 | typedef PolarHelper PH; 48 | static void rate0(TYPE *hard) 49 | { 50 | *hard = PH::one(); 51 | } 52 | static void rate1(TYPE **message, TYPE *hard, TYPE *soft) 53 | { 54 | *hard = PH::signum(soft[1]); 55 | *(*message)++ = *hard; 56 | } 57 | }; 58 | 59 | template 60 | struct PolarTree 61 | { 62 | typedef PolarHelper PH; 63 | static const int N = 1 << M; 64 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, const uint32_t *frozen) 65 | { 66 | for (int i = 0; i < N/2; ++i) 67 | soft[i+N/2] = PH::prod(soft[i+N], soft[i+N/2+N]); 68 | PolarTree::decode(message, hard, soft, frozen); 69 | for (int i = 0; i < N/2; ++i) 70 | soft[i+N/2] = PH::madd(hard[i], soft[i+N], soft[i+N/2+N]); 71 | PolarTree::decode(message, hard+N/2, soft, frozen+N/2/32); 72 | for (int i = 0; i < N/2; ++i) 73 | hard[i] = PH::qmul(hard[i], hard[i+N/2]); 74 | } 75 | }; 76 | 77 | template 78 | struct PolarTree 79 | { 80 | typedef PolarHelper PH; 81 | static const int M = 6; 82 | static const int N = 1 << M; 83 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, const uint32_t *frozen) 84 | { 85 | for (int i = 0; i < N/2; ++i) 86 | soft[i+N/2] = PH::prod(soft[i+N], soft[i+N/2+N]); 87 | if (frozen[0] == 0xffffffff) 88 | PolarNode::rate0(hard); 89 | else if (frozen[0] == 0) 90 | PolarNode::rate1(message, hard, soft); 91 | else 92 | PolarTree::decode(message, hard, soft, frozen[0]); 93 | for (int i = 0; i < N/2; ++i) 94 | soft[i+N/2] = PH::madd(hard[i], soft[i+N], soft[i+N/2+N]); 95 | if (frozen[1] == 0xffffffff) 96 | PolarNode::rate0(hard+N/2); 97 | else if (frozen[1] == 0) 98 | PolarNode::rate1(message, hard+N/2, soft); 99 | else 100 | PolarTree::decode(message, hard+N/2, soft, frozen[1]); 101 | for (int i = 0; i < N/2; ++i) 102 | hard[i] = PH::qmul(hard[i], hard[i+N/2]); 103 | } 104 | }; 105 | 106 | template 107 | struct PolarTree 108 | { 109 | typedef PolarHelper PH; 110 | static const int M = 5; 111 | static const int N = 1 << M; 112 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, uint32_t frozen) 113 | { 114 | for (int i = 0; i < N/2; ++i) 115 | soft[i+N/2] = PH::prod(soft[i+N], soft[i+N/2+N]); 116 | if ((frozen & ((1<<(1<<(M-1)))-1)) == ((1<<(1<<(M-1)))-1)) 117 | PolarNode::rate0(hard); 118 | else if ((frozen & ((1<<(1<<(M-1)))-1)) == 0) 119 | PolarNode::rate1(message, hard, soft); 120 | else 121 | PolarTree::decode(message, hard, soft, frozen & ((1<<(1<<(M-1)))-1)); 122 | for (int i = 0; i < N/2; ++i) 123 | soft[i+N/2] = PH::madd(hard[i], soft[i+N], soft[i+N/2+N]); 124 | if (frozen >> (N/2) == ((1<<(1<<(M-1)))-1)) 125 | PolarNode::rate0(hard+N/2); 126 | else if (frozen >> (N/2) == 0) 127 | PolarNode::rate1(message, hard+N/2, soft); 128 | else 129 | PolarTree::decode(message, hard+N/2, soft, frozen >> (N/2)); 130 | for (int i = 0; i < N/2; ++i) 131 | hard[i] = PH::qmul(hard[i], hard[i+N/2]); 132 | } 133 | }; 134 | 135 | template 136 | struct PolarTree 137 | { 138 | typedef PolarHelper PH; 139 | static const int M = 4; 140 | static const int N = 1 << M; 141 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, uint32_t frozen) 142 | { 143 | for (int i = 0; i < N/2; ++i) 144 | soft[i+N/2] = PH::prod(soft[i+N], soft[i+N/2+N]); 145 | if ((frozen & ((1<<(1<<(M-1)))-1)) == ((1<<(1<<(M-1)))-1)) 146 | PolarNode::rate0(hard); 147 | else if ((frozen & ((1<<(1<<(M-1)))-1)) == 0) 148 | PolarNode::rate1(message, hard, soft); 149 | else 150 | PolarTree::decode(message, hard, soft, frozen & ((1<<(1<<(M-1)))-1)); 151 | for (int i = 0; i < N/2; ++i) 152 | soft[i+N/2] = PH::madd(hard[i], soft[i+N], soft[i+N/2+N]); 153 | if (frozen >> (N/2) == ((1<<(1<<(M-1)))-1)) 154 | PolarNode::rate0(hard+N/2); 155 | else if (frozen >> (N/2) == 0) 156 | PolarNode::rate1(message, hard+N/2, soft); 157 | else 158 | PolarTree::decode(message, hard+N/2, soft, frozen >> (N/2)); 159 | for (int i = 0; i < N/2; ++i) 160 | hard[i] = PH::qmul(hard[i], hard[i+N/2]); 161 | } 162 | }; 163 | 164 | template 165 | struct PolarTree 166 | { 167 | typedef PolarHelper PH; 168 | static const int M = 3; 169 | static const int N = 1 << M; 170 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, uint32_t frozen) 171 | { 172 | for (int i = 0; i < N/2; ++i) 173 | soft[i+N/2] = PH::prod(soft[i+N], soft[i+N/2+N]); 174 | if ((frozen & ((1<<(1<<(M-1)))-1)) == ((1<<(1<<(M-1)))-1)) 175 | PolarNode::rate0(hard); 176 | else if ((frozen & ((1<<(1<<(M-1)))-1)) == 0) 177 | PolarNode::rate1(message, hard, soft); 178 | else 179 | PolarTree::decode(message, hard, soft, frozen & ((1<<(1<<(M-1)))-1)); 180 | for (int i = 0; i < N/2; ++i) 181 | soft[i+N/2] = PH::madd(hard[i], soft[i+N], soft[i+N/2+N]); 182 | if (frozen >> (N/2) == ((1<<(1<<(M-1)))-1)) 183 | PolarNode::rate0(hard+N/2); 184 | else if (frozen >> (N/2) == 0) 185 | PolarNode::rate1(message, hard+N/2, soft); 186 | else 187 | PolarTree::decode(message, hard+N/2, soft, frozen >> (N/2)); 188 | for (int i = 0; i < N/2; ++i) 189 | hard[i] = PH::qmul(hard[i], hard[i+N/2]); 190 | } 191 | }; 192 | 193 | template 194 | struct PolarTree 195 | { 196 | typedef PolarHelper PH; 197 | static const int M = 2; 198 | static const int N = 1 << M; 199 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, uint32_t frozen) 200 | { 201 | for (int i = 0; i < N/2; ++i) 202 | soft[i+N/2] = PH::prod(soft[i+N], soft[i+N/2+N]); 203 | if ((frozen & ((1<<(1<<(M-1)))-1)) == ((1<<(1<<(M-1)))-1)) 204 | PolarNode::rate0(hard); 205 | else if ((frozen & ((1<<(1<<(M-1)))-1)) == 0) 206 | PolarNode::rate1(message, hard, soft); 207 | else 208 | PolarTree::decode(message, hard, soft, frozen & ((1<<(1<<(M-1)))-1)); 209 | for (int i = 0; i < N/2; ++i) 210 | soft[i+N/2] = PH::madd(hard[i], soft[i+N], soft[i+N/2+N]); 211 | if (frozen >> (N/2) == ((1<<(1<<(M-1)))-1)) 212 | PolarNode::rate0(hard+N/2); 213 | else if (frozen >> (N/2) == 0) 214 | PolarNode::rate1(message, hard+N/2, soft); 215 | else 216 | PolarTree::decode(message, hard+N/2, soft, frozen >> (N/2)); 217 | for (int i = 0; i < N/2; ++i) 218 | hard[i] = PH::qmul(hard[i], hard[i+N/2]); 219 | } 220 | }; 221 | 222 | template 223 | struct PolarTree 224 | { 225 | typedef PolarHelper PH; 226 | static void decode(TYPE **message, TYPE *hard, TYPE *soft, uint32_t frozen) 227 | { 228 | soft[1] = PH::prod(soft[2], soft[3]); 229 | if (frozen & 1) 230 | PolarNode::rate0(hard); 231 | else 232 | PolarNode::rate1(message, hard, soft); 233 | soft[1] = PH::madd(hard[0], soft[2], soft[3]); 234 | if (frozen >> 1) 235 | PolarNode::rate0(hard+1); 236 | else 237 | PolarNode::rate1(message, hard+1, soft); 238 | hard[0] = PH::qmul(hard[0], hard[1]); 239 | } 240 | }; 241 | 242 | template 243 | class PolarDecoder 244 | { 245 | static_assert(MAX_M >= 5 && MAX_M <= 20); 246 | typedef PolarHelper PH; 247 | static const int MAX_N = 1 << MAX_M; 248 | TYPE soft[2*MAX_N]; 249 | TYPE hard[MAX_N]; 250 | public: 251 | void operator()(TYPE *message, const TYPE *codeword, const uint32_t *frozen, int level) 252 | { 253 | assert(level <= MAX_M); 254 | int length = 1 << level; 255 | for (int i = 0; i < length; ++i) 256 | soft[length+i] = codeword[i]; 257 | 258 | switch (level) { 259 | case 5: PolarTree::decode(&message, hard, soft, *frozen); break; 260 | case 6: PolarTree::decode(&message, hard, soft, frozen); break; 261 | case 7: PolarTree::decode(&message, hard, soft, frozen); break; 262 | case 8: PolarTree::decode(&message, hard, soft, frozen); break; 263 | case 9: PolarTree::decode(&message, hard, soft, frozen); break; 264 | case 10: PolarTree::decode(&message, hard, soft, frozen); break; 265 | case 11: PolarTree::decode(&message, hard, soft, frozen); break; 266 | case 12: PolarTree::decode(&message, hard, soft, frozen); break; 267 | case 13: PolarTree::decode(&message, hard, soft, frozen); break; 268 | case 14: PolarTree::decode(&message, hard, soft, frozen); break; 269 | case 15: PolarTree::decode(&message, hard, soft, frozen); break; 270 | case 16: PolarTree::decode(&message, hard, soft, frozen); break; 271 | case 17: PolarTree::decode(&message, hard, soft, frozen); break; 272 | case 18: PolarTree::decode(&message, hard, soft, frozen); break; 273 | case 19: PolarTree::decode(&message, hard, soft, frozen); break; 274 | case 20: PolarTree::decode(&message, hard, soft, frozen); break; 275 | default: assert(false); 276 | } 277 | } 278 | }; 279 | 280 | } 281 | 282 | -------------------------------------------------------------------------------- /polar_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Polar encoder for non-systematic and systematic codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "polar_helper.hh" 10 | 11 | namespace CODE { 12 | 13 | template 14 | class PolarEncoder 15 | { 16 | typedef PolarHelper PH; 17 | static bool get(const uint32_t *bits, int idx) 18 | { 19 | return (bits[idx/32] >> (idx%32)) & 1; 20 | } 21 | public: 22 | void operator()(TYPE *codeword, const TYPE *message, const uint32_t *frozen, int level) 23 | { 24 | int length = 1 << level; 25 | for (int i = 0; i < length; i += 2) { 26 | TYPE msg0 = get(frozen, i) ? PH::one() : *message++; 27 | TYPE msg1 = get(frozen, i+1) ? PH::one() : *message++; 28 | codeword[i] = PH::qmul(msg0, msg1); 29 | codeword[i+1] = msg1; 30 | } 31 | for (int h = 2; h < length; h *= 2) 32 | for (int i = 0; i < length; i += 2 * h) 33 | for (int j = i; j < i + h; ++j) 34 | codeword[j] = PH::qmul(codeword[j], codeword[j+h]); 35 | } 36 | }; 37 | 38 | template 39 | class PolarSysEnc 40 | { 41 | typedef PolarHelper PH; 42 | static bool get(const uint32_t *bits, int idx) 43 | { 44 | return (bits[idx/32] >> (idx%32)) & 1; 45 | } 46 | public: 47 | void operator()(TYPE *codeword, const TYPE *message, const uint32_t *frozen, int level) 48 | { 49 | int length = 1 << level; 50 | for (int i = 0; i < length; i += 2) { 51 | TYPE msg0 = get(frozen, i) ? PH::one() : *message++; 52 | TYPE msg1 = get(frozen, i+1) ? PH::one() : *message++; 53 | codeword[i] = PH::qmul(msg0, msg1); 54 | codeword[i+1] = msg1; 55 | } 56 | for (int h = 2; h < length; h *= 2) 57 | for (int i = 0; i < length; i += 2 * h) 58 | for (int j = i; j < i + h; ++j) 59 | codeword[j] = PH::qmul(codeword[j], codeword[j+h]); 60 | for (int i = 0; i < length; i += 2) { 61 | TYPE msg0 = get(frozen, i) ? PH::one() : codeword[i]; 62 | TYPE msg1 = get(frozen, i+1) ? PH::one() : codeword[i+1]; 63 | codeword[i] = PH::qmul(msg0, msg1); 64 | codeword[i+1] = msg1; 65 | } 66 | for (int h = 2; h < length; h *= 2) 67 | for (int i = 0; i < length; i += 2 * h) 68 | for (int j = i; j < i + h; ++j) 69 | codeword[j] = PH::qmul(codeword[j], codeword[j+h]); 70 | } 71 | }; 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /polar_freezer.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Bit freezers for polar codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace CODE { 12 | 13 | class PolarFreezer 14 | { 15 | static bool get_bit(const uint32_t *bits, int idx) 16 | { 17 | return (bits[idx/32] >> (idx%32)) & 1; 18 | } 19 | static void set_bit(uint32_t *bits, int idx, bool val) 20 | { 21 | bits[idx/32] &= ~(1 << (idx%32)); 22 | bits[idx/32] |= (uint32_t)val << (idx%32); 23 | } 24 | static void freeze(uint32_t *bits, double pe, double th, int i, int h) 25 | { 26 | if (h) { 27 | freeze(bits, pe * (2-pe), th, i, h/2); 28 | freeze(bits, pe * pe, th, i+h, h/2); 29 | } else { 30 | set_bit(bits, i, pe > th); 31 | } 32 | } 33 | public: 34 | int operator()(uint32_t *frozen_bits, int level, double erasure_probability = 0.5, double freezing_threshold = 0.5) 35 | { 36 | int length = 1 << level; 37 | freeze(frozen_bits, erasure_probability, freezing_threshold, 0, length / 2); 38 | int K = length; 39 | for (int i = 0; i < length; ++i) 40 | K -= (frozen_bits[i/32] >> (i%32)) & 1; 41 | return K; 42 | } 43 | }; 44 | 45 | template 46 | class PolarCodeConst0 47 | { 48 | static void inform_bit(uint32_t *bits, int idx) 49 | { 50 | bits[idx/32] &= ~(1 << (idx%32)); 51 | } 52 | static void frozen_bit(uint32_t *bits, int idx) 53 | { 54 | bits[idx/32] |= 1 << (idx%32); 55 | } 56 | void compute(double pe, int i, int h) 57 | { 58 | if (h) { 59 | compute(pe * (2-pe), i, h/2); 60 | compute(pe * pe, i+h, h/2); 61 | } else { 62 | prob[i] = pe; 63 | } 64 | } 65 | double prob[1< 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "simd.hh" 10 | 11 | namespace CODE { 12 | 13 | template 14 | struct PolarHelper 15 | { 16 | typedef TYPE PATH; 17 | static TYPE one() 18 | { 19 | return 1; 20 | } 21 | static TYPE zero() 22 | { 23 | return 0; 24 | } 25 | static TYPE signum(TYPE v) 26 | { 27 | return (v > 0) - (v < 0); 28 | } 29 | template 30 | static TYPE quant(IN in) 31 | { 32 | return in; 33 | } 34 | static TYPE qabs(TYPE a) 35 | { 36 | return std::abs(a); 37 | } 38 | static TYPE qmin(TYPE a, TYPE b) 39 | { 40 | return std::min(a, b); 41 | } 42 | static TYPE qadd(TYPE a, TYPE b) 43 | { 44 | return a + b; 45 | } 46 | static TYPE qmul(TYPE a, TYPE b) 47 | { 48 | return a * b; 49 | } 50 | static TYPE prod(TYPE a, TYPE b) 51 | { 52 | return signum(a) * signum(b) * qmin(qabs(a), qabs(b)); 53 | } 54 | static TYPE madd(TYPE a, TYPE b, TYPE c) 55 | { 56 | return a * b + c; 57 | } 58 | }; 59 | 60 | template 61 | struct PolarHelper> 62 | { 63 | typedef SIMD TYPE; 64 | typedef VALUE PATH; 65 | typedef SIMD MAP; 66 | static TYPE one() 67 | { 68 | return vdup(1); 69 | } 70 | static TYPE zero() 71 | { 72 | return vzero(); 73 | } 74 | static TYPE signum(TYPE a) 75 | { 76 | return vsignum(a); 77 | } 78 | static TYPE qabs(TYPE a) 79 | { 80 | return vabs(a); 81 | } 82 | static TYPE qmin(TYPE a, TYPE b) 83 | { 84 | return vmin(a, b); 85 | } 86 | static TYPE qadd(TYPE a, TYPE b) 87 | { 88 | return vadd(a, b); 89 | } 90 | static TYPE qmul(TYPE a, TYPE b) 91 | { 92 | return vmul(a, b); 93 | } 94 | static TYPE prod(TYPE a, TYPE b) 95 | { 96 | return vmul(vmul(vsignum(a), vsignum(b)), vmin(vabs(a), vabs(b))); 97 | } 98 | static TYPE madd(TYPE a, TYPE b, TYPE c) 99 | { 100 | return vadd(vmul(a, b), c); 101 | } 102 | }; 103 | 104 | template 105 | struct PolarHelper> 106 | { 107 | typedef SIMD TYPE; 108 | typedef int PATH; 109 | typedef SIMD MAP; 110 | static TYPE one() 111 | { 112 | return vdup(1); 113 | } 114 | static TYPE zero() 115 | { 116 | return vzero(); 117 | } 118 | static TYPE signum(TYPE a) 119 | { 120 | return vsignum(a); 121 | } 122 | static TYPE qabs(TYPE a) 123 | { 124 | return vqabs(a); 125 | } 126 | static TYPE qadd(TYPE a, TYPE b) 127 | { 128 | return vqadd(a, b); 129 | } 130 | static TYPE qmul(TYPE a, TYPE b) 131 | { 132 | #ifdef __ARM_NEON 133 | return vmul(a, b); 134 | #else 135 | return vsign(a, b); 136 | #endif 137 | } 138 | static TYPE prod(TYPE a, TYPE b) 139 | { 140 | #ifdef __ARM_NEON 141 | return vmul(vmul(vsignum(a), vsignum(b)), vmin(vqabs(a), vqabs(b))); 142 | #else 143 | return vsign(vmin(vqabs(a), vqabs(b)), vsign(vsignum(a), b)); 144 | #endif 145 | } 146 | static TYPE madd(TYPE a, TYPE b, TYPE c) 147 | { 148 | #ifdef __ARM_NEON 149 | return vmax(vqadd(vmul(a, vmax(b, vdup(-127))), c), vdup(-127)); 150 | #else 151 | return vmax(vqadd(vsign(vmax(b, vdup(-127)), a), c), vdup(-127)); 152 | #endif 153 | } 154 | }; 155 | 156 | template <> 157 | struct PolarHelper 158 | { 159 | typedef int PATH; 160 | static int8_t one() 161 | { 162 | return 1; 163 | } 164 | static int8_t zero() 165 | { 166 | return 0; 167 | } 168 | static int8_t signum(int8_t v) 169 | { 170 | return (v > 0) - (v < 0); 171 | } 172 | template 173 | static int8_t quant(IN in) 174 | { 175 | return std::min(std::max(std::nearbyint(in), -127), 127); 176 | } 177 | static int8_t qabs(int8_t a) 178 | { 179 | return std::abs(std::max(a, -127)); 180 | } 181 | static int8_t qmin(int8_t a, int8_t b) 182 | { 183 | return std::min(a, b); 184 | } 185 | static int8_t qadd(int8_t a, int8_t b) 186 | { 187 | return std::min(std::max(int16_t(a) + int16_t(b), -127), 127); 188 | } 189 | static int8_t qmul(int8_t a, int8_t b) 190 | { 191 | // return std::min(std::max(int16_t(a) * int16_t(b), -127), 127); 192 | // only used for hard decision values anyway 193 | return a * b; 194 | } 195 | static int8_t prod(int8_t a, int8_t b) 196 | { 197 | return signum(a) * signum(b) * qmin(qabs(a), qabs(b)); 198 | } 199 | static int8_t madd(int8_t a, int8_t b, int8_t c) 200 | { 201 | return std::min(std::max(int16_t(a) * int16_t(b) + int16_t(c), -127), 127); 202 | } 203 | }; 204 | 205 | } 206 | 207 | -------------------------------------------------------------------------------- /polar_sequence.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Construct reliability sequences for polar codes 3 | 4 | Copyright 2023 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace CODE { 12 | 13 | template 14 | class PolarSeqConst0 15 | { 16 | void compute(double pe, int i, int h) 17 | { 18 | if (h) { 19 | compute(pe * (2-pe), i, h/2); 20 | compute(pe * pe, i+h, h/2); 21 | } else { 22 | prob[i] = pe; 23 | } 24 | } 25 | double prob[1< prob[b]; }); 36 | } 37 | }; 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /prime_field.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Prime field arithmetic 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | struct PrimeField 13 | { 14 | static constexpr TYPE P = PRIME; 15 | TYPE v; 16 | PrimeField() = default; 17 | explicit PrimeField(TYPE v) : v(v) 18 | { 19 | } 20 | PrimeField operator *= (PrimeField a) 21 | { 22 | return *this = *this * a; 23 | } 24 | PrimeField operator /= (PrimeField a) 25 | { 26 | return *this = *this / a; 27 | } 28 | PrimeField operator += (PrimeField a) 29 | { 30 | return *this = *this + a; 31 | } 32 | PrimeField operator -= (PrimeField a) 33 | { 34 | return *this = *this - a; 35 | } 36 | TYPE operator () () 37 | { 38 | return v; 39 | } 40 | }; 41 | 42 | template 43 | PrimeField reduce(PrimeField a) 44 | { 45 | return PrimeField(a.v % a.P); 46 | } 47 | 48 | template 49 | bool operator == (PrimeField a, PrimeField b) 50 | { 51 | return a.v == b.v; 52 | } 53 | 54 | template 55 | bool operator != (PrimeField a, PrimeField b) 56 | { 57 | return a.v != b.v; 58 | } 59 | 60 | template 61 | PrimeField add(PrimeField a, PrimeField b) 62 | { 63 | return PrimeField(a.v + b.v); 64 | } 65 | 66 | template 67 | PrimeField operator + (PrimeField a, PrimeField b) 68 | { 69 | return reduce(add(a, b)); 70 | } 71 | 72 | template 73 | PrimeField sub(PrimeField a, PrimeField b) 74 | { 75 | return PrimeField(a.v - b.v + (a.v < b.v ? a.P : 0)); 76 | } 77 | 78 | template 79 | PrimeField operator - (PrimeField a, PrimeField b) 80 | { 81 | return reduce(sub(a, b)); 82 | } 83 | 84 | template 85 | PrimeField neg(PrimeField a) 86 | { 87 | return PrimeField(a.P - a.v); 88 | } 89 | 90 | template 91 | PrimeField operator - (PrimeField a) 92 | { 93 | return reduce(neg(a)); 94 | } 95 | 96 | template 97 | PrimeField mul(PrimeField a, PrimeField b) 98 | { 99 | return PrimeField(a.v * b.v); 100 | } 101 | 102 | template 103 | PrimeField operator * (PrimeField a, PrimeField b) 104 | { 105 | return reduce(mul(a, b)); 106 | } 107 | 108 | template 109 | PrimeField pow(PrimeField a, TYPE m) 110 | { 111 | PrimeField t(1); 112 | for (;m; m >>= 1, a *= a) 113 | if (m & 1) 114 | t *= a; 115 | return t; 116 | } 117 | 118 | template 119 | PrimeField rcp(PrimeField a) 120 | { 121 | return pow(a, a.P - 2); 122 | } 123 | 124 | template 125 | PrimeField div(PrimeField a, PrimeField b) 126 | { 127 | return mul(a, rcp(b)); 128 | } 129 | 130 | template 131 | PrimeField operator / (PrimeField a, PrimeField b) 132 | { 133 | return reduce(div(a, b)); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /reed_solomon_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Reed Solomon Decoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "reed_solomon_error_correction.hh" 10 | 11 | namespace CODE { 12 | 13 | template 14 | class ReedSolomonDecoder 15 | { 16 | public: 17 | typedef typename GF::value_type value_type; 18 | typedef typename GF::ValueType ValueType; 19 | typedef typename GF::IndexType IndexType; 20 | static const int NR = ROOTS; 21 | static const int N = GF::N, K = N - NR, NP = NR; 22 | private: 23 | ReedSolomonErrorCorrection algorithm; 24 | void update_syndromes(const ValueType *poly, ValueType *syndromes, int begin, int end) 25 | { 26 | for (int j = begin; j < end; ++j) { 27 | ValueType coeff(poly[j]); 28 | IndexType root(FCR), pe(1); 29 | for (int i = 0; i < NR; ++i) { 30 | syndromes[i] = fma(root, syndromes[i], coeff); 31 | root *= pe; 32 | } 33 | } 34 | } 35 | public: 36 | int compute_syndromes(const ValueType *data, const ValueType *parity, ValueType *syndromes, int data_len = K) 37 | { 38 | assert(0 < data_len && data_len <= K); 39 | // $syndromes_i = code(pe^{FCR+i})$ 40 | ValueType coeff(data[0]); 41 | for (int i = 0; i < NR; ++i) 42 | syndromes[i] = coeff; 43 | update_syndromes(data, syndromes, 1, data_len); 44 | update_syndromes(parity, syndromes, 0, NP); 45 | int nonzero = 0; 46 | for (int i = 0; i < NR; ++i) 47 | nonzero += !!syndromes[i]; 48 | return nonzero; 49 | } 50 | int operator()(ValueType *data, ValueType *parity, IndexType *erasures = 0, int erasures_count = 0, int data_len = K) 51 | { 52 | assert(0 <= erasures_count && erasures_count <= NR); 53 | assert(0 < data_len && data_len <= K); 54 | if (0) { 55 | for (int i = 0; i < erasures_count; ++i) { 56 | int idx = (int)erasures[i]; 57 | if (idx < data_len) 58 | data[idx] = ValueType(0); 59 | else 60 | parity[idx-data_len] = ValueType(0); 61 | } 62 | } 63 | if (erasures_count && data_len < K) { 64 | for (int i = 0; i < erasures_count; ++i) 65 | erasures[i] = IndexType((int)erasures[i] + K - data_len); 66 | } 67 | ValueType syndromes[NR]; 68 | if (!compute_syndromes(data, parity, syndromes, data_len)) 69 | return 0; 70 | IndexType locations[NR]; 71 | ValueType magnitudes[NR]; 72 | int count = algorithm(syndromes, locations, magnitudes, erasures, erasures_count); 73 | if (count <= 0) 74 | return count; 75 | for (int i = 0; i < count; ++i) 76 | if ((int)locations[i] < K - data_len) 77 | return -1; 78 | for (int i = 0; i < count; ++i) { 79 | int idx = (int)locations[i] + data_len - K; 80 | if (idx < data_len) 81 | data[idx] += magnitudes[i]; 82 | else 83 | parity[idx-data_len] += magnitudes[i]; 84 | } 85 | int corrections_count = 0; 86 | for (int i = 0; i < count; ++i) 87 | corrections_count += !!magnitudes[i]; 88 | return corrections_count; 89 | } 90 | int operator()(value_type *data, value_type *parity, value_type *erasures = 0, int erasures_count = 0, int data_len = K) 91 | { 92 | return (*this)(reinterpret_cast(data), reinterpret_cast(parity), reinterpret_cast(erasures), erasures_count, data_len); 93 | } 94 | int compute_syndromes(const value_type *data, const value_type *parity, value_type *syndromes, int data_len = K) 95 | { 96 | return compute_syndromes(reinterpret_cast(data), reinterpret_cast(parity), reinterpret_cast(syndromes), data_len); 97 | } 98 | }; 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /reed_solomon_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Reed Solomon Encoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class ReedSolomonEncoder 13 | { 14 | public: 15 | typedef typename GF::value_type value_type; 16 | typedef typename GF::ValueType ValueType; 17 | typedef typename GF::IndexType IndexType; 18 | static const int NR = ROOTS; 19 | static const int N = GF::N, K = N - NR, NP = NR; 20 | private: 21 | IndexType generator[NR+1]; 22 | public: 23 | ReedSolomonEncoder() 24 | { 25 | // $generator = \prod_{i=0}^{NR}(x-pe^{FCR+i})$ 26 | ValueType tmp[NR+1]; 27 | IndexType root(FCR), pe(1); 28 | for (int i = 0; i < NR; ++i) { 29 | tmp[i] = ValueType(1); 30 | for (int j = i; j > 0; --j) 31 | tmp[j] = fma(root, tmp[j], tmp[j-1]); 32 | tmp[0] *= root; 33 | root *= pe; 34 | } 35 | tmp[NR] = ValueType(1); 36 | if (0) { 37 | std::cerr << "generator = "; 38 | for (int i = NR; i > 0; --i) { 39 | if (!tmp[i]) 40 | continue; 41 | if (tmp[i] != ValueType(1)) 42 | std::cerr << (int)tmp[i] << "*"; 43 | std::cerr << "x"; 44 | if (i != 1) 45 | std::cerr << "^" << i; 46 | std::cerr << " + "; 47 | } 48 | std::cerr << (int)tmp[0] << std::endl; 49 | } 50 | for (int i = 0; i <= NR; ++i) 51 | generator[i] = index(tmp[i]); 52 | } 53 | void operator()(const ValueType *data, ValueType *parity, int data_len = K) 54 | { 55 | assert(0 < data_len && data_len <= K); 56 | // $code = data * x^{NR} + (data * x^{NR}) \mod{generator}$ 57 | for (int i = 0; i < NR; ++i) 58 | parity[i] = ValueType(0); 59 | for (int i = 0; i < data_len; ++i) { 60 | ValueType feedback = data[i] + parity[0]; 61 | if (feedback) { 62 | IndexType fb = index(feedback); 63 | for (int j = 1; j < NR; ++j) 64 | parity[j-1] = fma(fb, generator[NR-j], parity[j]); 65 | parity[NP-1] = value(generator[0] * fb); 66 | } else { 67 | for (int j = 1; j < NR; ++j) 68 | parity[j-1] = parity[j]; 69 | parity[NP-1] = ValueType(0); 70 | } 71 | } 72 | } 73 | void operator()(const value_type *data, value_type *parity, int data_len = K) 74 | { 75 | (*this)(reinterpret_cast(data), reinterpret_cast(parity), data_len); 76 | } 77 | }; 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /reed_solomon_error_correction.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Reed Solomon Error Correction 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | namespace RS { 11 | 12 | template 13 | struct Chien 14 | { 15 | typedef typename GF::ValueType ValueType; 16 | typedef typename GF::IndexType IndexType; 17 | static int search(const ValueType *locator, int locator_degree, IndexType *locations) 18 | { 19 | ValueType tmp[locator_degree+1]; 20 | for (int i = 0; i <= locator_degree; ++i) 21 | tmp[i] = locator[i]; 22 | int count = 0; 23 | for (int i = 0; i < GF::N; ++i) { 24 | ValueType sum(tmp[0]); 25 | for (int j = 1; j <= locator_degree; ++j) 26 | sum += tmp[j] *= IndexType(j); 27 | if (!sum) 28 | locations[count++] = IndexType(i); 29 | } 30 | return count; 31 | } 32 | }; 33 | 34 | template 35 | struct ArtinSchreier 36 | { 37 | typedef typename GF::ValueType ValueType; 38 | typedef typename GF::IndexType IndexType; 39 | ValueType imap[GF::Q]; 40 | ArtinSchreier() 41 | { 42 | for (int i = 0; i < GF::Q; ++i) 43 | imap[i] = ValueType(0); 44 | for (int i = 2; i < GF::N; i += 2) { 45 | ValueType x(i); 46 | ValueType xxx(x * x + x); 47 | if (xxx == ValueType(GF::N)) 48 | continue; 49 | assert(xxx.v); 50 | assert(!imap[xxx.v].v); 51 | imap[xxx.v] = x; 52 | } 53 | } 54 | ValueType operator()(ValueType a) { 55 | assert(a.v <= a.N); 56 | assert(a.v); 57 | return imap[a.v]; 58 | } 59 | }; 60 | 61 | template 62 | struct LocationFinder 63 | { 64 | typedef typename GF::ValueType ValueType; 65 | typedef typename GF::IndexType IndexType; 66 | ArtinSchreier imap; 67 | int operator()(const ValueType *locator, int locator_degree, IndexType *locations) 68 | { 69 | if (locator_degree == 1) { 70 | locations[0] = (index(locator[0]) / index(locator[1])) / IndexType(1); 71 | return 1; 72 | } 73 | if (locator_degree == 2) { 74 | if (!locator[1] || !locator[0]) 75 | return 0; 76 | ValueType a(locator[2]), b(locator[1]), c(locator[0]); 77 | ValueType ba(b/a), R(imap(a*c/(b*b))); 78 | if (!R) 79 | return 0; 80 | locations[0] = index(ba * R) / IndexType(1); 81 | locations[1] = index(ba * R + ba) / IndexType(1); 82 | return 2; 83 | } 84 | return Chien::search(locator, locator_degree, locations); 85 | } 86 | }; 87 | 88 | template 89 | struct Forney 90 | { 91 | typedef typename GF::ValueType ValueType; 92 | typedef typename GF::IndexType IndexType; 93 | static int compute_evaluator(const ValueType *syndromes, const ValueType *locator, int locator_degree, ValueType *evaluator) 94 | { 95 | // $evaluator = (syndromes * locator) \bmod{x^{NR}}$ 96 | int tmp = std::min(locator_degree, NR-1); 97 | int degree = -1; 98 | for (int i = 0; i <= tmp; ++i) { 99 | evaluator[i] = syndromes[i] * locator[0]; 100 | for (int j = 1; j <= i; ++j) 101 | evaluator[i] += syndromes[i-j] * locator[j]; 102 | if (evaluator[i]) 103 | degree = i; 104 | } 105 | return degree; 106 | } 107 | static void compute_magnitudes(const ValueType *locator, const IndexType *locations, int count, const ValueType *evaluator, int evaluator_degree, ValueType *magnitudes) 108 | { 109 | // $magnitude = root^{FCR-1} * \frac{evaluator(root)}{locator'(root)}$ 110 | for (int i = 0; i < count; ++i) { 111 | IndexType root(locations[i] * IndexType(1)), tmp(root); 112 | ValueType eval(evaluator[0]); 113 | for (int j = 1; j <= evaluator_degree; ++j) { 114 | eval += evaluator[j] * tmp; 115 | tmp *= root; 116 | } 117 | if (!eval) { 118 | magnitudes[i] = ValueType(0); 119 | continue; 120 | } 121 | ValueType deriv(locator[1]); 122 | IndexType root2(root * root), tmp2(root2); 123 | for (int j = 3; j <= count; j += 2) { 124 | deriv += locator[j] * tmp2; 125 | tmp2 *= root2; 126 | } 127 | IndexType magnitude(index(eval) / index(deriv)); 128 | if (FCR == 0) 129 | magnitude /= root; 130 | if (FCR > 1) 131 | for (int j = 1; j < FCR; ++j) 132 | magnitude *= root; 133 | magnitudes[i] = value(magnitude); 134 | } 135 | } 136 | static int algorithm(const ValueType *syndromes, const ValueType *locator, const IndexType *locations, int count, ValueType *evaluator, ValueType *magnitudes) 137 | { 138 | int evaluator_degree = compute_evaluator(syndromes, locator, count, evaluator); 139 | compute_magnitudes(locator, locations, count, evaluator, evaluator_degree, magnitudes); 140 | return evaluator_degree; 141 | } 142 | }; 143 | 144 | template 145 | struct BerlekampMassey 146 | { 147 | typedef typename GF::ValueType ValueType; 148 | typedef typename GF::IndexType IndexType; 149 | static int algorithm(const ValueType *s, ValueType *C, int count = 0) 150 | { 151 | ValueType B[NR+1]; 152 | for (int i = 0; i <= NR; ++i) 153 | B[i] = C[i]; 154 | int L = count; 155 | for (int n = count, m = 1; n < NR; ++n) { 156 | ValueType d(s[n]); 157 | for (int i = 1; i <= L; ++i) 158 | d += C[i] * s[n-i]; 159 | if (!d) { 160 | ++m; 161 | } else { 162 | ValueType T[NR+1]; 163 | for (int i = 0; i < m; ++i) 164 | T[i] = C[i]; 165 | for (int i = m; i <= NR; ++i) 166 | T[i] = fma(d, B[i-m], C[i]); 167 | if (2 * L <= n + count) { 168 | L = n + count + 1 - L; 169 | for (int i = 0; i <= NR; ++i) 170 | B[i] = C[i] / d; 171 | m = 1; 172 | } else { 173 | ++m; 174 | } 175 | for (int i = 0; i <= NR; ++i) 176 | C[i] = T[i]; 177 | } 178 | } 179 | return L; 180 | } 181 | }; 182 | 183 | } 184 | 185 | template 186 | struct ReedSolomonErrorCorrection 187 | { 188 | typedef typename GF::ValueType ValueType; 189 | typedef typename GF::IndexType IndexType; 190 | RS::LocationFinder search; 191 | int operator()(const ValueType *syndromes, IndexType *locations, ValueType *magnitudes, const IndexType *erasures = 0, int erasures_count = 0) 192 | { 193 | assert(0 <= erasures_count && erasures_count <= NR); 194 | ValueType locator[NR+1]; 195 | locator[0] = ValueType(1); 196 | for (int i = 1; i <= NR; ++i) 197 | locator[i] = ValueType(0); 198 | // $locator = \prod_{i=0}^{count}(1-x\,pe^{N-1-erasures_i})$ 199 | if (erasures_count) 200 | locator[1] = value(IndexType(GF::N-1) / erasures[0]); 201 | for (int i = 1; i < erasures_count; ++i) { 202 | IndexType tmp(IndexType(GF::N-1) / erasures[i]); 203 | for (int j = i; j >= 0; --j) 204 | locator[j+1] += tmp * locator[j]; 205 | } 206 | int locator_degree = RS::BerlekampMassey::algorithm(syndromes, locator, erasures_count); 207 | assert(locator_degree); 208 | assert(locator_degree <= NR); 209 | assert(locator[0] == ValueType(1)); 210 | while (!locator[locator_degree]) 211 | if (--locator_degree < 0) 212 | return -1; 213 | int count = search(locator, locator_degree, locations); 214 | if (count < locator_degree) 215 | return -1; 216 | ValueType evaluator[NR]; 217 | int evaluator_degree = RS::Forney::algorithm(syndromes, locator, locations, count, evaluator, magnitudes); 218 | if (0) { 219 | static bool once; 220 | if (!once) { 221 | once = true; 222 | std::cerr << "syndromes ="; 223 | for (int i = 0; i < NR; ++i) 224 | std::cerr << " " << (int)syndromes[i]; 225 | std::cerr << std::endl; 226 | std::cerr << "locator = "; 227 | for (int i = NR; i > 0; --i) { 228 | if (!locator[i]) 229 | continue; 230 | if (locator[i] != ValueType(1)) 231 | std::cerr << (int)locator[i] << "*"; 232 | std::cerr << "x"; 233 | if (i != 1) 234 | std::cerr << "^" << i; 235 | std::cerr << " + "; 236 | } 237 | std::cerr << (int)locator[0] << std::endl; 238 | std::cerr << "locations ="; 239 | for (int i = 0; i < count; ++i) 240 | std::cerr << " " << (int)locations[i]; 241 | std::cerr << std::endl; 242 | std::cerr << "evaluator = "; 243 | for (int i = evaluator_degree; i > 0; --i) { 244 | if (!evaluator[i]) 245 | continue; 246 | if (evaluator[i] != ValueType(1)) 247 | std::cerr << (int)evaluator[i] << "*"; 248 | std::cerr << "x"; 249 | if (i != 1) 250 | std::cerr << "^" << i; 251 | if (i != 1 || evaluator[0]) 252 | std::cerr << " + "; 253 | } 254 | if (evaluator[0]) 255 | std::cerr << (int)evaluator[0]; 256 | std::cerr << std::endl; 257 | std::cerr << "magnitudes ="; 258 | for (int i = 0; i < count; ++i) 259 | std::cerr << " " << (int)magnitudes[i]; 260 | std::cerr << std::endl; 261 | } 262 | } 263 | return count; 264 | } 265 | }; 266 | 267 | } 268 | 269 | -------------------------------------------------------------------------------- /rotate.hh: -------------------------------------------------------------------------------- 1 | /* 2 | SIMD element wise horizontal rotation 3 | 4 | Copyright 2019 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "simd.hh" 10 | 11 | namespace CODE { 12 | 13 | template 14 | class Rotate 15 | { 16 | static const int SIZE = TYPE::SIZE; 17 | static_assert(WIDTH <= SIZE, "width must be smaller or equal to SIMD size"); 18 | public: 19 | TYPE operator()(TYPE a, int s) 20 | { 21 | if (s < 0) 22 | s += WIDTH; 23 | int t = WIDTH - s; 24 | TYPE ret; 25 | for (int n = 0; n < s; ++n) 26 | ret.v[n] = a.v[n+t]; 27 | for (int n = 0; n < t; ++n) 28 | ret.v[n+s] = a.v[n]; 29 | return ret; 30 | } 31 | }; 32 | 33 | #ifdef __ARM_NEON 34 | template 35 | class Rotate, WIDTH> 36 | { 37 | static const int SIZE = 16; 38 | static_assert(WIDTH <= SIZE, "width must be smaller or equal to SIMD size"); 39 | typedef SIMD TYPE; 40 | TYPE rot[WIDTH]; 41 | public: 42 | Rotate() 43 | { 44 | for (int i = 0; i < WIDTH; ++i) { 45 | rot[i] = vdup(0x80); 46 | for (int j = 0; j < WIDTH; ++j) 47 | rot[i].v[j] = (j - i + WIDTH) % WIDTH; 48 | } 49 | } 50 | TYPE operator()(TYPE a, int s) 51 | { 52 | if (s < 0) 53 | s += WIDTH; 54 | TYPE ret; 55 | #ifdef __aarch64__ 56 | ret.m = vqtbl1q_s8(a.m, vunsigned(rot[s]).m); 57 | #else 58 | int8x8x2_t b { vget_low_s8(a.m), vget_high_s8(a.m) }; 59 | int8x8_t c = vtbl2_s8(b, vget_low_s8(rot[s].m)); 60 | int8x8_t d = vtbl2_s8(b, vget_high_s8(rot[s].m)); 61 | ret.m = vcombine_s8(c, d); 62 | #endif 63 | return ret; 64 | } 65 | }; 66 | #endif 67 | 68 | #ifdef __AVX2__ 69 | template 70 | class Rotate, WIDTH> 71 | { 72 | static const int SIZE = 32; 73 | static_assert(WIDTH <= SIZE, "width must be smaller or equal to SIMD size"); 74 | typedef SIMD TYPE; 75 | TYPE rot0[WIDTH], rot1[WIDTH]; 76 | public: 77 | Rotate() 78 | { 79 | for (int i = 0; i < WIDTH; ++i) { 80 | rot0[i] = vdup(0x80); 81 | rot1[i] = vdup(0x80); 82 | for (int j = 0; j < WIDTH; ++j) { 83 | int pos = (j - i + WIDTH) % WIDTH; 84 | if (j < 16) { 85 | if (pos < 16) { 86 | rot0[i].v[j] = pos; 87 | } else { 88 | rot1[i].v[j+16] = pos-16; 89 | } 90 | } else { 91 | if (pos < 16) { 92 | rot1[i].v[j-16] = pos; 93 | } else { 94 | rot0[i].v[j] = pos-16; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | TYPE operator()(TYPE a, int s) 101 | { 102 | if (s < 0) 103 | s += WIDTH; 104 | __m256i b = _mm256_shuffle_epi8(a.m, rot0[s].m); 105 | __m256i c = _mm256_shuffle_epi8(a.m, rot1[s].m); 106 | __m256i d = _mm256_permute2x128_si256(c, c, 1); 107 | TYPE ret; 108 | ret.m = _mm256_or_si256(b, d); 109 | return ret; 110 | } 111 | }; 112 | #else 113 | #ifdef __SSE4_1__ 114 | template 115 | class Rotate, WIDTH> 116 | { 117 | static const int SIZE = 16; 118 | static_assert(WIDTH <= SIZE, "width must be smaller or equal to SIMD size"); 119 | typedef SIMD TYPE; 120 | TYPE rot[WIDTH]; 121 | public: 122 | Rotate() 123 | { 124 | for (int i = 0; i < WIDTH; ++i) { 125 | rot[i] = vdup(0x80); 126 | for (int j = 0; j < WIDTH; ++j) 127 | rot[i].v[j] = (j - i + WIDTH) % WIDTH; 128 | } 129 | } 130 | TYPE operator()(TYPE a, int s) 131 | { 132 | if (s < 0) 133 | s += WIDTH; 134 | TYPE ret; 135 | ret.m = _mm_shuffle_epi8(a.m, rot[s].m); 136 | return ret; 137 | } 138 | }; 139 | #endif 140 | #endif 141 | 142 | } 143 | 144 | -------------------------------------------------------------------------------- /short_bch_code_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Soft decoder for short BCH codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class ShortBCHCodeDecoder 13 | { 14 | static const int P = N - K; 15 | static const int W = 1 << K; 16 | static const int R = 1 << P; 17 | int err[R]; 18 | short par[W]; 19 | static_assert(N < 8 * sizeof(err[0]), "codeword type not wide enough"); 20 | static_assert(P < 8 * sizeof(par[0]), "parity type not wide enough"); 21 | static_assert(T > 0 && T <= 3, "unsupported radius T"); 22 | static int modgen(int inp) 23 | { 24 | for (int i = K-1; i >= 0; --i) { 25 | int tmp = inp >> (i+P); 26 | inp ^= (tmp & 1) * (G << i); 27 | } 28 | return inp; 29 | } 30 | static int metric(const int8_t *soft, int hard) 31 | { 32 | if (hard < 0) 33 | return -1; 34 | int lol = 8 * sizeof(hard) - 1; 35 | int sum = 0; 36 | for (int i = 0; i < N; ++i) 37 | sum += ((hard << (lol-i)) >> lol) * soft[i]; 38 | return sum; 39 | } 40 | public: 41 | ShortBCHCodeDecoder() 42 | { 43 | for (int i = 0; i < W; ++i) 44 | par[i] = modgen(i << P); 45 | for (int i = 0; i < R; ++i) 46 | err[i] = 0; 47 | for (int a = 1<<(N-1); T >= 1 && a; a >>= 1) { 48 | err[modgen(a)] = a; 49 | for (int b = a >> 1; T >= 2 && b; b >>= 1) { 50 | err[modgen(a|b)] = a|b; 51 | for (int c = b >> 1; T >= 3 && c; c >>= 1) { 52 | err[modgen(a|b|c)] = a|b|c; 53 | } 54 | } 55 | } 56 | } 57 | int operator()(int inp) 58 | { 59 | int syn = (par[inp>>P] ^ inp) & (R-1); 60 | int pat = err[syn]; 61 | return pat || !syn ? inp ^ pat : -1; 62 | } 63 | int operator()(const int8_t *code) 64 | { 65 | int cw = 0; 66 | for (int i = 0; i < N; ++i) 67 | cw |= (code[i] < 0) << i; 68 | int word = (*this)(cw); 69 | // hard decision 70 | if (0) 71 | return word; 72 | // metric of hard decision 73 | int best = metric(code, word); 74 | int next = -2; 75 | auto update = [code, &word, &best, &next](int hard) { 76 | if (hard == word) 77 | return; 78 | int met = metric(code, hard); 79 | if (met > best) { 80 | next = best; 81 | best = met; 82 | word = hard; 83 | } else if (met > next) { 84 | next = met; 85 | } 86 | }; 87 | // flip each bit and see .. 88 | if (0) { 89 | for (int j = 0; j < N; ++j) { 90 | int tmp = 1 << j; 91 | int dec = (*this)(cw ^ tmp); 92 | update(dec); 93 | } 94 | } 95 | // Chase algorithm 96 | if (K > 4) { 97 | const int L = T; 98 | int worst[L] = { 0 }; 99 | for (int k = 0; k < N; ++k) { 100 | int j = 0; 101 | while (j < L && std::abs(code[worst[j]]) < std::abs(code[k])) 102 | ++j; 103 | for (int i = L-1; i > j; --i) 104 | worst[i] = worst[i-1]; 105 | if (j < L) 106 | worst[j] = k; 107 | } 108 | for (int j = 1; j < (1 << L); ++j) { 109 | int tmp = 0; 110 | for (int i = 0; i < L; ++i) 111 | tmp |= ((j>>i)&1) << worst[i]; 112 | int dec = (*this)(cw ^ tmp); 113 | update(dec); 114 | } 115 | } 116 | // maximum likelihood 117 | if (K <= 4) { 118 | for (int msg = 0; msg < W; ++msg) { 119 | int enc = (msg << P) | par[msg]; 120 | update(enc); 121 | } 122 | } 123 | if (best == next) 124 | return -1; 125 | return word; 126 | } 127 | }; 128 | 129 | } 130 | 131 | -------------------------------------------------------------------------------- /short_bch_code_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Encoder for short BCH codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class ShortBCHCodeEncoder 13 | { 14 | static const int P = N - K; 15 | static const int W = 1 << K; 16 | short par[W]; 17 | static_assert(N < 8 * sizeof(int), "codeword type not wide enough"); 18 | static_assert(P < 8 * sizeof(par[0]), "parity type not wide enough"); 19 | static int modgen(int inp) 20 | { 21 | for (int i = K-1; i >= 0; --i) { 22 | int tmp = inp >> (i+P); 23 | inp ^= (tmp & 1) * (G << i); 24 | } 25 | return inp; 26 | } 27 | public: 28 | ShortBCHCodeEncoder() 29 | { 30 | for (int i = 0; i < W; ++i) 31 | par[i] = modgen(i << P); 32 | } 33 | int operator()(int msg) 34 | { 35 | return (msg << P) | par[msg]; 36 | } 37 | int operator()(int8_t *code, int msg) 38 | { 39 | int cw = (*this)(msg); 40 | for (int i = 0; i < N; ++i) 41 | code[i] = 1 - 2 * ((cw >> i) & 1); 42 | return cw; 43 | } 44 | }; 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /simd_sort.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Sorting for SIMD types 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "sort.hh" 10 | 11 | template 12 | static inline SIMD::uint_type, WIDTH> vorder(SIMD a) 13 | { 14 | SIMD::uint_type, WIDTH> p; 15 | CODE::insertion_sort(p.v, a.v, WIDTH); 16 | return p; 17 | } 18 | 19 | template 20 | static inline SIMD vsort(SIMD a) 21 | { 22 | CODE::insertion_sort(a.v, WIDTH); 23 | return a; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /simplex_decoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Soft decoder for Simplex codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class SimplexDecoder 13 | { 14 | static const int W = 1 << K; 15 | public: 16 | int operator()(const int8_t *code) 17 | { 18 | int sum[W]; 19 | sum[0] = code[0]; 20 | sum[1] = -code[0]; 21 | for (int i = 1; i < W-2; i += 2) { 22 | sum[i+1] = code[i] + code[i+1]; 23 | sum[i+2] = code[i] - code[i+1]; 24 | } 25 | for (int h = 2; h < W; h *= 2) { 26 | for (int i = 0; i < W; i += 2 * h) { 27 | for (int j = i; j < i + h; ++j) { 28 | int x = sum[j] + sum[j+h]; 29 | int y = sum[j] - sum[j+h]; 30 | sum[j] = x; 31 | sum[j+h] = y; 32 | } 33 | } 34 | } 35 | int word = 0, best = 0, next = 0; 36 | for (int msg = 0; msg < W; ++msg) { 37 | int mag = sum[msg]; 38 | if (mag > best) { 39 | next = best; 40 | best = mag; 41 | word = msg; 42 | } else if (mag > next) { 43 | next = mag; 44 | } 45 | } 46 | if (best == next) 47 | return -1; 48 | return word; 49 | } 50 | }; 51 | 52 | template <> 53 | class SimplexDecoder<2> 54 | { 55 | static const int K = 2; 56 | static const int W = 1 << K; 57 | public: 58 | int operator()(const int8_t *code) 59 | { 60 | int tmp[4] = { 61 | code[0], -code[0], 62 | code[1] + code[2], 63 | code[1] - code[2], 64 | }; 65 | int sum[W] = { 66 | tmp[0] + tmp[2], 67 | tmp[1] + tmp[3], 68 | tmp[0] - tmp[2], 69 | tmp[1] - tmp[3], 70 | }; 71 | int word = 0, best = 0, next = 0; 72 | for (int msg = 0; msg < W; ++msg) { 73 | int mag = sum[msg]; 74 | if (mag > best) { 75 | next = best; 76 | best = mag; 77 | word = msg; 78 | } else if (mag > next) { 79 | next = mag; 80 | } 81 | } 82 | if (best == next) 83 | return -1; 84 | return word; 85 | } 86 | }; 87 | 88 | template <> 89 | class SimplexDecoder<3> 90 | { 91 | static const int K = 3; 92 | static const int W = 1 << K; 93 | public: 94 | int operator()(const int8_t *c) 95 | { 96 | int d[8] = { 97 | c[0], -c[0], 98 | c[1] + c[2], 99 | c[1] - c[2], 100 | c[3] + c[4], 101 | c[3] - c[4], 102 | c[5] + c[6], 103 | c[5] - c[6], 104 | }; 105 | int e[8] = { 106 | d[0] + d[2], 107 | d[1] + d[3], 108 | d[0] - d[2], 109 | d[1] - d[3], 110 | d[4] + d[6], 111 | d[5] + d[7], 112 | d[4] - d[6], 113 | d[5] - d[7], 114 | }; 115 | int sum[W] = { 116 | e[0] + e[4], 117 | e[1] + e[5], 118 | e[2] + e[6], 119 | e[3] + e[7], 120 | e[0] - e[4], 121 | e[1] - e[5], 122 | e[2] - e[6], 123 | e[3] - e[7], 124 | }; 125 | int word = 0, best = 0, next = 0; 126 | for (int msg = 0; msg < W; ++msg) { 127 | int mag = sum[msg]; 128 | if (mag > best) { 129 | next = best; 130 | best = mag; 131 | word = msg; 132 | } else if (mag > next) { 133 | next = mag; 134 | } 135 | } 136 | if (best == next) 137 | return -1; 138 | return word; 139 | } 140 | }; 141 | 142 | template <> 143 | class SimplexDecoder<4> 144 | { 145 | static const int K = 4; 146 | static const int W = 1 << K; 147 | public: 148 | int operator()(const int8_t *c) 149 | { 150 | int d[16] = { 151 | c[0], -c[0], 152 | c[1] + c[2], 153 | c[1] - c[2], 154 | c[3] + c[4], 155 | c[3] - c[4], 156 | c[5] + c[6], 157 | c[5] - c[6], 158 | c[7] + c[8], 159 | c[7] - c[8], 160 | c[9] + c[10], 161 | c[9] - c[10], 162 | c[11] + c[12], 163 | c[11] - c[12], 164 | c[13] + c[14], 165 | c[13] - c[14], 166 | }; 167 | int e[16] = { 168 | d[0] + d[2], 169 | d[1] + d[3], 170 | d[0] - d[2], 171 | d[1] - d[3], 172 | d[4] + d[6], 173 | d[5] + d[7], 174 | d[4] - d[6], 175 | d[5] - d[7], 176 | d[8] + d[10], 177 | d[9] + d[11], 178 | d[8] - d[10], 179 | d[9] - d[11], 180 | d[12] + d[14], 181 | d[13] + d[15], 182 | d[12] - d[14], 183 | d[13] - d[15], 184 | }; 185 | int f[16] = { 186 | e[0] + e[4], 187 | e[1] + e[5], 188 | e[2] + e[6], 189 | e[3] + e[7], 190 | e[0] - e[4], 191 | e[1] - e[5], 192 | e[2] - e[6], 193 | e[3] - e[7], 194 | e[8] + e[12], 195 | e[9] + e[13], 196 | e[10] + e[14], 197 | e[11] + e[15], 198 | e[8] - e[12], 199 | e[9] - e[13], 200 | e[10] - e[14], 201 | e[11] - e[15], 202 | }; 203 | int sum[W] = { 204 | f[0] + f[8], 205 | f[1] + f[9], 206 | f[2] + f[10], 207 | f[3] + f[11], 208 | f[4] + f[12], 209 | f[5] + f[13], 210 | f[6] + f[14], 211 | f[7] + f[15], 212 | f[0] - f[8], 213 | f[1] - f[9], 214 | f[2] - f[10], 215 | f[3] - f[11], 216 | f[4] - f[12], 217 | f[5] - f[13], 218 | f[6] - f[14], 219 | f[7] - f[15], 220 | }; 221 | int word = 0, best = 0, next = 0; 222 | for (int msg = 0; msg < W; ++msg) { 223 | int mag = sum[msg]; 224 | if (mag > best) { 225 | next = best; 226 | best = mag; 227 | word = msg; 228 | } else if (mag > next) { 229 | next = mag; 230 | } 231 | } 232 | if (best == next) 233 | return -1; 234 | return word; 235 | } 236 | }; 237 | 238 | } 239 | 240 | -------------------------------------------------------------------------------- /simplex_encoder.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Encoder for Simplex codes 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | class SimplexEncoder 13 | { 14 | static const int W = 1 << K; 15 | static const int N = (1 << K) - 1; 16 | int8_t mod[W]; 17 | 18 | static bool parity(unsigned x) 19 | { 20 | x ^= x >> 16; 21 | x ^= x >> 8; 22 | x ^= x >> 4; 23 | x ^= x >> 2; 24 | x ^= x >> 1; 25 | return x & 1; 26 | } 27 | public: 28 | SimplexEncoder() 29 | { 30 | for (int i = 0; i < W; ++i) 31 | mod[i] = 1 - 2 * parity(i); 32 | } 33 | void operator()(int8_t *code, int msg) 34 | { 35 | for (int i = 0; i < N; ++i) 36 | code[i] = mod[msg&(i+1)]; 37 | } 38 | }; 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /sort.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Some stable sorting algorithms 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | template 12 | static void insertion_sort(TYPE *a, int n) 13 | { 14 | for (int i = 1, j; i < n; ++i) { 15 | TYPE t = a[i]; 16 | for (j = i; j > 0 && t < a[j-1]; --j) 17 | a[j] = a[j-1]; 18 | a[j] = t; 19 | } 20 | } 21 | 22 | template 23 | static void insertion_sort(TYPE *a, int n, COMP comp) 24 | { 25 | for (int i = 1, j; i < n; ++i) { 26 | TYPE t = a[i]; 27 | for (j = i; j > 0 && comp(t, a[j-1]); --j) 28 | a[j] = a[j-1]; 29 | a[j] = t; 30 | } 31 | } 32 | 33 | template 34 | static void insertion_sort(INDEX *p, TYPE *a, int n) 35 | { 36 | p[0] = 0; 37 | for (int i = 1, j; i < n; ++i) { 38 | TYPE t = a[i]; 39 | for (j = i; j > 0 && t < a[j-1]; --j) { 40 | a[j] = a[j-1]; 41 | p[j] = p[j-1]; 42 | } 43 | a[j] = t; 44 | p[j] = i; 45 | } 46 | } 47 | 48 | template 49 | class MergeSort 50 | { 51 | TYPE tmp[MAX_N]; 52 | template 53 | void merge(TYPE *a, int n, int left, int right, int end, COMP comp) 54 | { 55 | if (right > n) 56 | right = n; 57 | if (end > n) 58 | end = n; 59 | for (int i = left, j = right, k = left; k < end; ++k) 60 | tmp[k] = (i >= right || (j < end && comp(a[j], a[i]))) ? a[j++] : a[i++]; 61 | } 62 | public: 63 | template 64 | void operator()(TYPE *a, int n, COMP comp) 65 | { 66 | for (int i = 0; i < n; i += M) 67 | insertion_sort(a+i, i > n-M ? n-i : M, comp); 68 | for (int l = M; l < n; l *= 2) { 69 | for (int i = 0; i < n; i += 2*l) 70 | merge(a, n, i, i+l, i+2*l, comp); 71 | for (int i = 0; i < n; ++i) 72 | a[i] = tmp[i]; 73 | } 74 | } 75 | void operator()(TYPE *a, int n) 76 | { 77 | operator()(a, n, [](TYPE x, TYPE y){ return x < y; }); 78 | } 79 | }; 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *_test 2 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CXXFLAGS = -I.. -std=c++17 -W -Wall -O2 -fno-exceptions -fno-rtti -ffast-math -ftree-vectorize 3 | 4 | CXX = clang++ -stdlib=libc++ -march=native 5 | 6 | #CXX = g++ -march=native 7 | 8 | #CXX = armv7a-hardfloat-linux-gnueabi-g++ -static -mfpu=neon -march=armv7-a 9 | #QEMU = qemu-arm 10 | 11 | #CXX = aarch64-unknown-linux-gnu-g++ -static -march=armv8-a+crc+simd -mtune=cortex-a72 12 | #QEMU = qemu-aarch64 13 | 14 | .PHONY: clean test 15 | 16 | tests := $(basename $(wildcard *_test.*)) 17 | 18 | test: $(tests) 19 | $(patsubst %,$(QEMU) ./% >/dev/null;,$(tests)) 20 | 21 | clean: 22 | rm -f *_test 23 | 24 | -------------------------------------------------------------------------------- /tests/bch_decoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Bose Chaudhuri Hocquenghem Decoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "bitman.hh" 12 | #include "galois_field.hh" 13 | #include "bose_chaudhuri_hocquenghem_decoder.hh" 14 | 15 | int main() 16 | { 17 | std::random_device rd; 18 | std::default_random_engine generator(rd()); 19 | if (1) { 20 | // NASA INTRO BCH(15, 5) T=3 21 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 22 | typedef CODE::BoseChaudhuriHocquenghemDecoder<6, 1, 5, GF> BCH; 23 | const int D = (BCH::K + 7) / 8; 24 | const int P = (BCH::NP + 7) / 8; 25 | const int L = D + P; 26 | GF instance; 27 | BCH decode; 28 | uint8_t target[L] = { 0b11001000, 0b00011110, 0b10000000 }; 29 | uint8_t code[L]; 30 | for (int i = 0; i < L; ++i) 31 | code[i] = target[i]; 32 | typedef std::uniform_int_distribution distribution; 33 | auto noise = std::bind(distribution(0, BCH::N-1), generator); 34 | for (int i = 0; i < BCH::NR/2; ++i) { 35 | int n = noise(); 36 | if (n < BCH::K) 37 | CODE::xor_be_bit(code, n, 1); 38 | else 39 | CODE::xor_be_bit(code+D, n-BCH::K, 1); 40 | } 41 | decode(code, code + D); 42 | for (int i = 0; i < L; ++i) 43 | assert(code[i] == target[i]); 44 | } 45 | if (1) { 46 | // DVB-S2 FULL BCH(65535, 65343) T=12 47 | typedef CODE::GaloisField<16, 0b10000000000101101, uint16_t> GF; 48 | typedef CODE::BoseChaudhuriHocquenghemDecoder<24, 1, 65343, GF> BCH; 49 | const int D = (BCH::K + 7) / 8; 50 | const int P = (BCH::NP + 7) / 8; 51 | const int L = D + P; 52 | GF *instance = new GF(); 53 | BCH *decode = new BCH(); 54 | uint8_t *target = new uint8_t[L]; 55 | for (int i = 0; i < L; ++i) 56 | target[i] = 0; 57 | for (int i = 0, s = 0; i < BCH::K; ++i, s=(s*(s*s*51767+71287)+35149)&0xffffff) 58 | CODE::set_be_bit(target, i, (s^=s>>7)&1); 59 | bool parity[BCH::NP] = { 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0 }; 60 | for (int i = 0; i < BCH::NP; ++i) 61 | CODE::set_be_bit(target + D, i, parity[i]); 62 | uint8_t *code = new uint8_t[L]; 63 | for (int i = 0; i < L; ++i) 64 | code[i] = target[i]; 65 | typedef std::uniform_int_distribution distribution; 66 | auto noise = std::bind(distribution(0, BCH::N-1), generator); 67 | for (int i = 0; i < BCH::NR/2; ++i) { 68 | int n = noise(); 69 | if (n < BCH::K) 70 | CODE::xor_be_bit(code, n, 1); 71 | else 72 | CODE::xor_be_bit(code+D, n-BCH::K, 1); 73 | } 74 | (*decode)(code, code + D); 75 | for (int i = 0; i < L; ++i) 76 | assert(code[i] == target[i]); 77 | delete[] target; 78 | delete[] code; 79 | delete decode; 80 | delete instance; 81 | } 82 | if (1) { 83 | // NASA INTRO BCH(15, 5) T=3 84 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 85 | typedef CODE::BoseChaudhuriHocquenghemDecoderReference<6, 1, 5, GF> BCH; 86 | GF instance; 87 | BCH decode; 88 | BCH::value_type target[BCH::N] = { 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0 }; 89 | BCH::value_type code[BCH::N]; 90 | for (int i = 0; i < BCH::N; ++i) 91 | code[i] = target[i]; 92 | typedef std::uniform_int_distribution distribution; 93 | auto noise = std::bind(distribution(0, BCH::N-1), generator); 94 | for (int i = 0; i < BCH::NR/2; ++i) 95 | code[noise()] ^= 1; 96 | decode(reinterpret_cast(code), reinterpret_cast(code) + BCH::K); 97 | for (int i = 0; i < BCH::N; ++i) 98 | assert(code[i] == target[i]); 99 | } 100 | if (1) { 101 | // DVB-S2 FULL BCH(65535, 65343) T=12 102 | typedef CODE::GaloisField<16, 0b10000000000101101, uint16_t> GF; 103 | typedef CODE::BoseChaudhuriHocquenghemDecoderReference<24, 1, 65343, GF> BCH; 104 | GF *instance = new GF(); 105 | BCH *decode = new BCH(); 106 | BCH::value_type *target = new BCH::value_type[BCH::N]; 107 | for (int i = 0, s = 0; i < BCH::K; ++i, s=(s*(s*s*51767+71287)+35149)&0xffffff) 108 | target[i] = (s^=s>>7)&1; 109 | BCH::value_type parity[BCH::NP] = { 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0 }; 110 | for (int i = 0; i < BCH::NP; ++i) 111 | target[BCH::K+i] = parity[i]; 112 | BCH::value_type *code = new BCH::value_type[BCH::N]; 113 | for (int i = 0; i < BCH::N; ++i) 114 | code[i] = target[i]; 115 | typedef std::uniform_int_distribution distribution; 116 | auto noise = std::bind(distribution(0, BCH::N-1), generator); 117 | for (int i = 0; i < BCH::NR/2; ++i) 118 | code[noise()] ^= 1; 119 | (*decode)(reinterpret_cast(code), reinterpret_cast(code) + BCH::K); 120 | for (int i = 0; i < BCH::N; ++i) 121 | assert(code[i] == target[i]); 122 | delete[] target; 123 | delete[] code; 124 | delete decode; 125 | delete instance; 126 | } 127 | std::cerr << "Bose Chaudhuri Hocquenghem Decoder test passed!" << std::endl; 128 | return 0; 129 | } 130 | 131 | -------------------------------------------------------------------------------- /tests/bch_encoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Bose Chaudhuri Hocquenghem Encoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "bitman.hh" 11 | #include "galois_field.hh" 12 | #include "bose_chaudhuri_hocquenghem_encoder.hh" 13 | 14 | int main() 15 | { 16 | if (1) { 17 | // NASA INTRO BCH(15, 5) T=3 18 | typedef CODE::BoseChaudhuriHocquenghemEncoder<15, 5> BCH; 19 | const int D = (BCH::K + 7) / 8; 20 | const int P = (BCH::NP + 7) / 8; 21 | const int L = D + P; 22 | BCH encode({0b10011, 0b11111, 0b00111}); 23 | uint8_t code[L] = { 0b11001000 }; 24 | uint8_t target[L] = { 0b11001000, 0b00011110, 0b10000000 }; 25 | encode(code, code + D); 26 | for (int i = 0; i < L; ++i) 27 | assert(code[i] == target[i]); 28 | } 29 | if (1) { 30 | // DVB-S2 FULL BCH(65535, 65343) T=12 31 | typedef CODE::BoseChaudhuriHocquenghemEncoder<65535, 65343> BCH; 32 | const int D = (BCH::K + 7) / 8; 33 | const int P = (BCH::NP + 7) / 8; 34 | const int L = D + P; 35 | BCH *encode = new BCH({ 36 | 0b10000000000101101, 0b10000000101110011, 0b10000111110111101, 37 | 0b10101101001010101, 0b10001111100101111, 0b11111011110110101, 38 | 0b11010111101100101, 0b10111001101100111, 0b10000111010100001, 39 | 0b10111010110100111, 0b10011101000101101, 0b10001101011100011}); 40 | uint8_t *target = new uint8_t[L]; 41 | for (int i = 0; i < L; ++i) 42 | target[i] = 0; 43 | for (int i = 0, s = 0; i < BCH::K; ++i, s=(s*(s*s*51767+71287)+35149)&0xffffff) 44 | CODE::set_be_bit(target, i, (s^=s>>7)&1); 45 | uint8_t *code = new uint8_t[L]; 46 | for (int i = 0; i < L; ++i) 47 | code[i] = target[i]; 48 | bool parity[BCH::NP] = { 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0 }; 49 | for (int i = 0; i < BCH::NP; ++i) 50 | CODE::set_be_bit(target + D, i, parity[i]); 51 | (*encode)(code, code + D); 52 | for (int i = 0; i < L; ++i) 53 | assert(code[i] == target[i]); 54 | delete[] target; 55 | delete[] code; 56 | delete encode; 57 | } 58 | if (1) { 59 | // NASA INTRO BCH(15, 5) T=3 60 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 61 | typedef CODE::BoseChaudhuriHocquenghemEncoderReference<6, 1, 5, GF> BCH; 62 | GF instance; 63 | BCH encode({0b10011, 0b11111, 0b00111}); 64 | BCH::value_type code[BCH::N] = { 1, 1, 0, 0, 1 }; 65 | BCH::value_type target[BCH::N] = { 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0 }; 66 | encode(reinterpret_cast(code), reinterpret_cast(code) + BCH::K); 67 | for (int i = 0; i < BCH::N; ++i) 68 | assert(code[i] == target[i]); 69 | } 70 | if (1) { 71 | // DVB-S2 FULL BCH(65535, 65343) T=12 72 | typedef CODE::GaloisField<16, 0b10000000000101101, uint16_t> GF; 73 | typedef CODE::BoseChaudhuriHocquenghemEncoderReference<24, 1, 65343, GF> BCH; 74 | GF *instance = new GF(); 75 | BCH *encode = new BCH({ 76 | 0b10000000000101101, 0b10000000101110011, 0b10000111110111101, 77 | 0b10101101001010101, 0b10001111100101111, 0b11111011110110101, 78 | 0b11010111101100101, 0b10111001101100111, 0b10000111010100001, 79 | 0b10111010110100111, 0b10011101000101101, 0b10001101011100011}); 80 | BCH::value_type *code = new BCH::value_type[BCH::N]; 81 | BCH::value_type *target = new BCH::value_type[BCH::N]; 82 | for (int i = 0, s = 0; i < BCH::K; ++i, s=(s*(s*s*51767+71287)+35149)&0xffffff) 83 | target[i] = code[i] = (s^=s>>7)&1; 84 | BCH::value_type parity[BCH::NP] = { 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0 }; 85 | for (int i = 0; i < BCH::NP; ++i) 86 | target[BCH::K+i] = parity[i]; 87 | (*encode)(reinterpret_cast(code), reinterpret_cast(code) + BCH::K); 88 | for (int i = 0; i < BCH::N; ++i) 89 | assert(code[i] == target[i]); 90 | delete[] target; 91 | delete[] code; 92 | delete encode; 93 | delete instance; 94 | } 95 | std::cerr << "Bose Chaudhuri Hocquenghem Encoder test passed!" << std::endl; 96 | return 0; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /tests/cpf_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the Cauchy Prime Field Encoder and Decoder 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "prime_field.hh" 14 | #include "cauchy_prime_field_erasure_coding.hh" 15 | 16 | template 17 | void cpf_test(int trials) 18 | { 19 | int value_bytes = sizeof(IO); 20 | int value_bits = value_bytes * 8; 21 | const int MAX_LEN = std::min(PF::P - 2, 1024); 22 | CODE::CauchyPrimeFieldErasureCoding crs; 23 | std::random_device rd; 24 | std::default_random_engine generator(rd()); 25 | typedef std::uniform_int_distribution distribution; 26 | auto rnd_cnt = std::bind(distribution(1, std::min(PF::P / 4, 256)), generator); 27 | auto rnd_len = std::bind(distribution(1, MAX_LEN), generator); 28 | auto rnd_dat = std::bind(distribution(0, (1 << value_bits) - 1), generator); 29 | while (--trials) { 30 | int block_count = rnd_cnt(); 31 | int idents_total = PF::P / 2 - block_count; 32 | int block_values = rnd_len(); 33 | int block_bytes = block_values * value_bytes; 34 | int data_values = block_count * block_values; 35 | int data_bytes = data_values * value_bytes; 36 | IO *subs = new IO[block_count]; 37 | IO *orig = new IO[data_values]; 38 | IO *data = new IO[data_values]; 39 | IO *blocks = new IO[data_values]; 40 | IO *idents = new IO[idents_total]; 41 | for (int i = 0; i < data_values; ++i) 42 | orig[i] = rnd_dat(); 43 | for (int i = 0; i < idents_total; ++i) 44 | idents[i] = block_count + i; 45 | for (int i = 0; i < block_count; i++) { 46 | std::uniform_int_distribution hat(i, idents_total - 1); 47 | std::swap(idents[i], idents[hat(generator)]); 48 | } 49 | auto enc_start = std::chrono::system_clock::now(); 50 | for (int i = 0; i < block_count; ++i) 51 | subs[i] = crs.encode(orig, blocks + block_values * i, idents[i], block_values, block_count); 52 | auto enc_end = std::chrono::system_clock::now(); 53 | auto enc_usec = std::chrono::duration_cast(enc_end - enc_start); 54 | double enc_mbs = double(data_bytes) / enc_usec.count(); 55 | auto dec_start = std::chrono::system_clock::now(); 56 | for (int i = 0; i < block_count; ++i) 57 | crs.decode(data + block_values * i, blocks, subs, idents, i, block_values, block_count); 58 | auto dec_end = std::chrono::system_clock::now(); 59 | auto dec_usec = std::chrono::duration_cast(dec_end - dec_start); 60 | double dec_mbs = double(data_bytes) / dec_usec.count(); 61 | std::cout << "block count = " << block_count << ", block size = " << block_bytes << " bytes, encoding speed = " << enc_mbs << " megabyte per second, decoding speed = " << dec_mbs << " megabyte per second" << std::endl; 62 | for (int i = 0; i < data_values; ++i) 63 | assert(data[i] == orig[i]); 64 | delete[] idents; 65 | delete[] blocks; 66 | delete[] orig; 67 | delete[] data; 68 | delete[] subs; 69 | } 70 | } 71 | 72 | int main() 73 | { 74 | if (1) { 75 | cpf_test, uint8_t>(200); 76 | } 77 | if (1) { 78 | cpf_test, uint16_t>(100); 79 | } 80 | std::cerr << "Cauchy prime field regression test passed!" << std::endl; 81 | return 0; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /tests/crc_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Cyclic redundancy check 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "crc.hh" 12 | 13 | int main() 14 | { 15 | if (1) { 16 | CODE::CRC crc(0xEDB88320, 0xFFFFFFFF); 17 | for (uint8_t c: std::string("Hello World!")) crc(c); 18 | assert(~crc() == 0x1C291CA3); 19 | } 20 | if (1) { 21 | // Optimization of cyclic redundancy-check codes with 24 and 32 parity bits 22 | // by G. Castagnoli, S. Brauer and M. Herrmann - 1993 23 | // Hamming distance of at least 6 up to a payload of 32736 bits. 24 | CODE::CRC crc(0xC8DF352F); 25 | for (uint8_t c: std::string("Hello World!")) crc(c); 26 | assert(!crc(uint32_t(0xAB94FCAB))); 27 | } 28 | if (1) { 29 | // Optimization of cyclic redundancy-check codes with 24 and 32 parity bits 30 | // by G. Castagnoli, S. Brauer and M. Herrmann - 1993 31 | // Hamming distance of at least 5 up to a payload of 65505 bits. 32 | CODE::CRC crc(0xD419CC15); 33 | for (uint8_t c: std::string("Hello World!")) crc(c); 34 | assert(!crc(uint32_t(0xECFE423E))); 35 | } 36 | if (1) { 37 | // Performance of cyclic redundancy codes for embedded networks 38 | // by T. Chakravarty and P. Koopman - 2001 39 | // Optimal for payloads <= 64 bits 40 | CODE::CRC crc(0xA8F4); 41 | for (uint8_t c: std::string("Hello World!")) crc(c); 42 | assert(!crc(uint16_t(0x8FEF))); 43 | crc.reset(0x9B38); 44 | std::bitset<32> hw("00000010001001110111110100100100"); 45 | for (size_t i = 0; i < hw.size(); ++i) crc(hw[i]); 46 | //std::cerr << "0x" << std::hex << crc() << std::endl; 47 | assert(crc() == 0x915D); 48 | } 49 | if (1) { 50 | CODE::CRC crc(0x8C); 51 | for (uint8_t c: std::string("Hello World!")) crc(c); 52 | assert(!crc(uint8_t(0x9E))); 53 | } 54 | std::cerr << "Cyclic redundancy check test passed!" << std::endl; 55 | return 0; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /tests/crs_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the Cauchy Reed Solomon Encoder and Decoder 3 | 4 | Copyright 2023 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "galois_field.hh" 14 | #include "cauchy_reed_solomon_erasure_coding.hh" 15 | 16 | template 17 | void crs_test(int trials) 18 | { 19 | #ifdef __AVX2__ 20 | int SIMD = 32; 21 | #else 22 | int SIMD = 16; 23 | #endif 24 | CODE::CauchyReedSolomonErasureCoding crs; 25 | std::random_device rd; 26 | std::default_random_engine generator(rd()); 27 | typedef std::uniform_int_distribution distribution; 28 | auto rnd_cnt = std::bind(distribution(1, std::min(GF::Q / 2, 256)), generator); 29 | auto rnd_len = std::bind(distribution(1, 1 << 10), generator); 30 | auto rnd_dat = std::bind(distribution(0, 255), generator); 31 | while (--trials) { 32 | int block_count = rnd_cnt(); 33 | int identifiers_total = GF::Q - block_count; 34 | int block_bytes = rnd_len() * sizeof(typename GF::value_type) * SIMD; 35 | int data_bytes = block_count * block_bytes; 36 | uint8_t *orig = reinterpret_cast(std::aligned_alloc(SIMD, data_bytes)); 37 | uint8_t *data = reinterpret_cast(std::aligned_alloc(SIMD, data_bytes)); 38 | uint8_t *blocks = reinterpret_cast(std::aligned_alloc(SIMD, data_bytes)); 39 | for (int i = 0; i < data_bytes; ++i) 40 | orig[i] = rnd_dat(); 41 | auto identifiers = new typename GF::value_type[identifiers_total]; 42 | for (int i = 0; i < identifiers_total; ++i) 43 | identifiers[i] = block_count + i; 44 | for (int i = 0; i < block_count; i++) { 45 | std::uniform_int_distribution hat(i, identifiers_total - 1); 46 | std::swap(identifiers[i], identifiers[hat(generator)]); 47 | } 48 | auto enc_start = std::chrono::system_clock::now(); 49 | for (int i = 0; i < block_count; ++i) 50 | crs.encode(orig, blocks + block_bytes * i, identifiers[i], block_bytes, block_count); 51 | auto enc_end = std::chrono::system_clock::now(); 52 | auto enc_usec = std::chrono::duration_cast(enc_end - enc_start); 53 | double enc_mbs = double(data_bytes) / enc_usec.count(); 54 | auto dec_start = std::chrono::system_clock::now(); 55 | for (int i = 0; i < block_count; ++i) 56 | crs.decode(data + block_bytes * i, blocks, identifiers, i, block_bytes, block_count); 57 | auto dec_end = std::chrono::system_clock::now(); 58 | auto dec_usec = std::chrono::duration_cast(dec_end - dec_start); 59 | double dec_mbs = double(data_bytes) / dec_usec.count(); 60 | std::cout << "block count = " << block_count << ", block size = " << block_bytes << " bytes, encoding speed = " << enc_mbs << " megabyte per second, decoding speed = " << dec_mbs << " megabyte per second" << std::endl; 61 | for (int i = 0; i < data_bytes; ++i) 62 | assert(data[i] == orig[i]); 63 | delete[] identifiers; 64 | std::free(blocks); 65 | std::free(orig); 66 | std::free(data); 67 | } 68 | } 69 | 70 | int main() 71 | { 72 | if (1) { 73 | typedef CODE::GaloisField<8, 0b100011101, uint8_t> GF; 74 | GF instance; 75 | crs_test(200); 76 | } 77 | if (1) { 78 | typedef CODE::GaloisField<16, 0b10001000000001011, uint16_t> GF; 79 | GF *instance = new GF(); 80 | crs_test(100); 81 | delete instance; 82 | } 83 | std::cerr << "Cauchy Reed Solomon regression test passed!" << std::endl; 84 | return 0; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /tests/gf_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Galois field arithmetic 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "galois_field.hh" 11 | 12 | template 13 | void exhaustive_test() 14 | { 15 | for (int j = 0; j < GF::Q; ++j) { 16 | typename GF::ValueType a(j); 17 | for (int i = 0; i < GF::Q; ++i) { 18 | typename GF::ValueType b(i); 19 | assert((a * b).v == (GFR(j) * GFR(i)).v); 20 | } 21 | } 22 | for (int i = 1; i < GF::Q; ++i) { 23 | typename GF::ValueType a(i); 24 | assert(rcp(a).v == rcp(GFR(i)).v); 25 | } 26 | } 27 | 28 | int main() 29 | { 30 | if (1) { 31 | // BBC WHP031 RS(15, 11) T=2 32 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 33 | typedef CODE::GaloisFieldReference<4, 0b10011, uint8_t> GFR; 34 | GF instance; 35 | GF::ValueType a(3), b(7), c(15), d(6); 36 | assert(a * b + c == d); 37 | exhaustive_test(); 38 | } 39 | if (1) { 40 | // DVB-T RS(255, 239) T=8 41 | typedef CODE::GaloisField<8, 0b100011101, uint8_t> GF; 42 | typedef CODE::GaloisFieldReference<8, 0b100011101, uint8_t> GFR; 43 | GF instance; 44 | GF::ValueType a(154), b(83), c(144), d(63); 45 | assert(fma(a, b, c) == d); 46 | exhaustive_test(); 47 | } 48 | if (1) { 49 | // FUN RS(65535, 65471) T=32 50 | typedef CODE::GaloisField<16, 0b10001000000001011, uint16_t> GF; 51 | typedef CODE::GaloisFieldReference<16, 0b10001000000001011, uint16_t> GFR; 52 | GF *instance = new GF(); 53 | GF::ValueType a(42145), b(13346), c(40958), d(35941); 54 | assert(a / b + c == d); 55 | exhaustive_test(); 56 | delete instance; 57 | } 58 | if (1) { 59 | // DVB-S2 FULL BCH(65535, 65343) T=12 60 | typedef CODE::GaloisField<16, 0b10000000000101101, uint16_t> GF; 61 | typedef CODE::GaloisFieldReference<16, 0b10000000000101101, uint16_t> GFR; 62 | GF *instance = new GF(); 63 | GF::ValueType a(38532), b(7932), c(34283), d(22281); 64 | assert(a / (rcp(b) + c) == d); 65 | exhaustive_test(); 66 | delete instance; 67 | } 68 | std::cerr << "Galois field arithmetic test passed!" << std::endl; 69 | return 0; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /tests/hadamard_encoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the augmented Hadamard codes encoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include "hadamard_encoder.hh" 10 | 11 | template 12 | void hadamard_test() 13 | { 14 | CODE::HadamardEncoder encode; 15 | const int W = 1 << K; 16 | const int N = 1 << (K - 1); 17 | const int D = 1 << (K - 2); 18 | int hist[N+1] = { 0 }; 19 | for (int j = 0; j < W; ++j) { 20 | int8_t cj[N]; 21 | encode(cj, j); 22 | for (int i = j + 1; i < W; ++i) { 23 | int8_t ci[N]; 24 | encode(ci, i); 25 | int d = 0; 26 | for (int n = 0; n < N; ++n) 27 | d += ci[n] != cj[n]; 28 | ++hist[d]; 29 | } 30 | } 31 | std::cout << "Hadamard(" << N << ", " << K << ", " << D << ") hist:"; 32 | for (int i = 0; i < N + 1; ++i) 33 | if (hist[i]) 34 | std::cout << " " << i << ":" << hist[i]; 35 | std::cout << std::endl; 36 | int d = 0; 37 | while (!hist[d]) 38 | ++d; 39 | assert(d == D); 40 | } 41 | 42 | int main() 43 | { 44 | if (1) { 45 | hadamard_test<2>(); 46 | } 47 | if (1) { 48 | hadamard_test<3>(); 49 | } 50 | if (1) { 51 | hadamard_test<4>(); 52 | } 53 | if (1) { 54 | hadamard_test<5>(); 55 | } 56 | if (1) { 57 | hadamard_test<6>(); 58 | } 59 | if (1) { 60 | hadamard_test<7>(); 61 | } 62 | if (1) { 63 | hadamard_test<8>(); 64 | } 65 | std::cerr << "Hadamard encoder test passed!" << std::endl; 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tests/hadamard_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the augmented Hadamard code Encoder and soft Decoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "hadamard_encoder.hh" 14 | #include "hadamard_decoder.hh" 15 | 16 | template 17 | int popcnt(TYPE x) 18 | { 19 | int cnt = 0; 20 | while (x) { 21 | ++cnt; 22 | x &= x-1; 23 | } 24 | return cnt; 25 | } 26 | 27 | #if 0 28 | const int LOOPS = 640000; 29 | const float QEF_SNR = 9.5; 30 | const int DATA_LEN = 2; 31 | #endif 32 | #if 0 33 | const int LOOPS = 320000; 34 | const float QEF_SNR = 7.0; 35 | const int DATA_LEN = 3; 36 | #endif 37 | #if 1 38 | const int LOOPS = 160000; 39 | const float QEF_SNR = 4.5; 40 | const int DATA_LEN = 4; 41 | #endif 42 | #if 0 43 | const int LOOPS = 80000; 44 | const float QEF_SNR = 2.0; 45 | const int DATA_LEN = 5; 46 | #endif 47 | #if 0 48 | const int LOOPS = 40000; 49 | const float QEF_SNR = -1.0; 50 | const int DATA_LEN = 6; 51 | #endif 52 | 53 | int main() 54 | { 55 | const int CODE_LEN = 1 << (DATA_LEN - 1); 56 | 57 | CODE::HadamardEncoder encode; 58 | CODE::HadamardDecoder decode; 59 | 60 | std::random_device rd; 61 | std::default_random_engine generator(rd()); 62 | typedef std::uniform_int_distribution uniform; 63 | typedef std::normal_distribution normal; 64 | 65 | float min_SNR = 20; 66 | 67 | for (float SNR = -10; SNR <= 10; SNR += 0.1) { 68 | //float mean_signal = 0; 69 | float sigma_signal = 1; 70 | float mean_noise = 0; 71 | float sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 72 | 73 | auto data = std::bind(uniform(0, (1 << DATA_LEN) - 1), generator); 74 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator); 75 | 76 | int awgn_errors = 0; 77 | int quantization_erasures = 0; 78 | int uncorrected_errors = 0; 79 | int ambiguity_errors = 0; 80 | for (int loop = 0; loop < LOOPS; ++loop) { 81 | int8_t code[CODE_LEN], orig[CODE_LEN], noisy[CODE_LEN]; 82 | float symb[CODE_LEN]; 83 | 84 | int dat = data(); 85 | encode(code, dat); 86 | 87 | for (int i = 0; i < CODE_LEN; ++i) 88 | orig[i] = code[i]; 89 | 90 | for (int i = 0; i < CODE_LEN; ++i) 91 | symb[i] = code[i]; 92 | 93 | for (int i = 0; i < CODE_LEN; ++i) 94 | symb[i] += awgn(); 95 | 96 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 97 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 98 | float DIST = 2; // BPSK 99 | float fact = DIST / (sigma_noise * sigma_noise); 100 | for (int i = 0; i < CODE_LEN; ++i) 101 | code[i] = std::min(std::max(std::nearbyint(fact * symb[i]), -127), 127); 102 | 103 | for (int i = 0; i < CODE_LEN; ++i) 104 | noisy[i] = code[i]; 105 | 106 | int dec = decode(code); 107 | 108 | for (int i = 0; i < CODE_LEN; ++i) 109 | awgn_errors += noisy[i] * orig[i] < 0; 110 | for (int i = 0; i < CODE_LEN; ++i) 111 | quantization_erasures += !noisy[i]; 112 | uncorrected_errors += dec < 0 ? DATA_LEN : popcnt(dat^dec); 113 | ambiguity_errors += dec < 0; 114 | } 115 | float bit_error_rate = (float)uncorrected_errors / (float)(DATA_LEN * LOOPS); 116 | if (bit_error_rate < 0.0001) 117 | min_SNR = std::min(min_SNR, SNR); 118 | 119 | if (0) { 120 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 121 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 122 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 123 | std::cerr << ambiguity_errors << " ambiguity errors." << std::endl; 124 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 125 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 126 | } else { 127 | std::cout << SNR << " " << bit_error_rate << std::endl; 128 | } 129 | } 130 | 131 | std::cerr << "QEF at: " << min_SNR << " SNR" << std::endl; 132 | assert(min_SNR < QEF_SNR); 133 | std::cerr << "Simplex code regression test passed!" << std::endl; 134 | return 0; 135 | } 136 | 137 | -------------------------------------------------------------------------------- /tests/mls_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Maximum length sequence 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include "mls.hh" 10 | 11 | static const int poly[] = { 12 | 0b111, 13 | 0b1011, 14 | 0b1101, 15 | 0b10011, 16 | 0b11001, 17 | 0b100101, 18 | 0b101001, 19 | 0b101111, 20 | 0b110111, 21 | 0b111011, 22 | 0b111101, 23 | 0b1000011, 24 | 0b1011011, 25 | 0b1100001, 26 | 0b1100111, 27 | 0b1101101, 28 | 0b1110011, 29 | 0b10000011, 30 | 0b10001001, 31 | 0b10001111, 32 | 0b10010001, 33 | 0b10011101, 34 | 0b10100111, 35 | 0b10101011, 36 | 0b10111001, 37 | 0b10111111, 38 | 0b11000001, 39 | 0b11001011, 40 | 0b11010011, 41 | 0b11010101, 42 | 0b11100101, 43 | 0b11101111, 44 | 0b11110001, 45 | 0b11110111, 46 | 0b11111101, 47 | 0b100011101, 48 | 0b100101011, 49 | 0b100101101, 50 | 0b101001101, 51 | 0b101011111, 52 | 0b101100011, 53 | 0b101100101, 54 | 0b101101001, 55 | 0b101110001, 56 | 0b110000111, 57 | 0b110001101, 58 | 0b110101001, 59 | 0b111000011, 60 | 0b111001111, 61 | 0b111100111, 62 | 0b111110101, 63 | 0b1000010001, 64 | 0b1000011011, 65 | 0b1000100001, 66 | 0b1000101101, 67 | 0b1000110011, 68 | 0b1001011001, 69 | 0b1001011111, 70 | 0b1001101001, 71 | 0b1001101111, 72 | 0b1001110111, 73 | 0b1001111101, 74 | 0b1010000111, 75 | 0b1010010101, 76 | 0b1010100011, 77 | 0b1010100101, 78 | 0b1010101111, 79 | 0b1010110111, 80 | 0b1010111101, 81 | 0b1011001111, 82 | 0b1011010001, 83 | 0b1011011011, 84 | 0b1011110101, 85 | 0b1011111001, 86 | 0b1100010011, 87 | 0b1100010101, 88 | 0b1100011111, 89 | 0b1100100011, 90 | 0b1100110001, 91 | 0b1100111011, 92 | 0b1101001111, 93 | 0b1101011011, 94 | 0b1101100001, 95 | 0b1101101011, 96 | 0b1101101101, 97 | 0b1101110011, 98 | 0b1101111111, 99 | 0b1110000101, 100 | 0b1110001111, 101 | 0b1110110101, 102 | 0b1110111001, 103 | 0b1111000111, 104 | 0b1111001011, 105 | 0b1111001101, 106 | 0b1111010101, 107 | 0b1111011001, 108 | 0b1111100011, 109 | 0b1111101001, 110 | 0b1111111011, 111 | 0b10000001001, 112 | 0b10000011011, 113 | 0b10000100111, 114 | 0b10000101101, 115 | 0b10001100101, 116 | 0b10001101111, 117 | 0b10010000001, 118 | 0b10010001011, 119 | 0b10011000101, 120 | 0b10011010111, 121 | 0b10011100111, 122 | 0b10011110011, 123 | 0b10011111111, 124 | 0b10100001101, 125 | 0b10100011001, 126 | 0b10100100011, 127 | 0b10100110001, 128 | 0b10100111101, 129 | 0b10101000011, 130 | 0b10101010111, 131 | 0b10101101011, 132 | 0b10110000101, 133 | 0b10110001111, 134 | 0b10110010111, 135 | 0b10110100001, 136 | 0b10111000111, 137 | 0b10111100101, 138 | 0b10111110111, 139 | 0b10111111011, 140 | 0b11000010011, 141 | 0b11000010101, 142 | 0b11000100101, 143 | 0b11000110111, 144 | 0b11001000011, 145 | 0b11001001111, 146 | 0b11001011011, 147 | 0b11001111001, 148 | 0b11001111111, 149 | 0b11010001001, 150 | 0b11010110101, 151 | 0b11011000001, 152 | 0b11011010011, 153 | 0b11011011111, 154 | 0b11011111101, 155 | 0b11100010111, 156 | 0b11100011101, 157 | 0b11100100001, 158 | 0b11100111001, 159 | 0b11101000111, 160 | 0b11101001101, 161 | 0b11101010101, 162 | 0b11101011001, 163 | 0b11101100011, 164 | 0b11101111101, 165 | 0b11110001101, 166 | 0b11110010011, 167 | 0b11110110001, 168 | 0b11111011011, 169 | 0b11111110011, 170 | 0b11111111001, 171 | 0b100000000101, 172 | 0b100000010111, 173 | 0b100000101011, 174 | 0b100000101101, 175 | 0b100001000111, 176 | 0b100001100011, 177 | 0b100001100101, 178 | 0b100001110001, 179 | 0b100001111011, 180 | 0b100010001101, 181 | 0b100010010101, 182 | 0b100010011111, 183 | 0b100010101001, 184 | 0b100010110001, 185 | 0b100011001111, 186 | 0b100011010001, 187 | 0b100011100001, 188 | 0b100011100111, 189 | 0b100011101011, 190 | 0b100011110101, 191 | 0b100100001101, 192 | 0b100100010011, 193 | 0b100100100101, 194 | 0b100100101001, 195 | 0b100100111011, 196 | 0b100100111101, 197 | 0b100101000101, 198 | 0b100101001001, 199 | 0b100101010001, 200 | 0b100101011011, 201 | 0b100101110011, 202 | 0b100101110101, 203 | 0b100101111111, 204 | 0b100110000011, 205 | 0b100110001111, 206 | 0b100110101011, 207 | 0b100110101101, 208 | 0b100110111001, 209 | 0b100111000111, 210 | 0b100111011001, 211 | 0b100111100101, 212 | 0b100111110111, 213 | 0b101000000001, 214 | 0b101000000111, 215 | 0b101000010011, 216 | 0b101000010101, 217 | 0b101000101001, 218 | 0b101001001001, 219 | 0b101001100001, 220 | 0b101001101101, 221 | 0b101001111001, 222 | 0b101001111111, 223 | 0b101010000101, 224 | 0b101010010001, 225 | 0b101010011101, 226 | 0b101010100111, 227 | 0b101010101011, 228 | 0b101010110011, 229 | 0b101010110101, 230 | 0b101011010101, 231 | 0b101011011111, 232 | 0b101011101001, 233 | 0b101011101111, 234 | 0b101011110001, 235 | 0b101011111011, 236 | 0b101100000011, 237 | 0b101100001001, 238 | 0b101100010001, 239 | 0b101100110011, 240 | 0b101100111111, 241 | 0b101101000001, 242 | 0b101101001011, 243 | 0b101101011001, 244 | 0b101101011111, 245 | 0b101101100101, 246 | 0b101101101111, 247 | 0b101101111101, 248 | 0b101110000111, 249 | 0b101110001011, 250 | 0b101110010011, 251 | 0b101110010101, 252 | 0b101110101111, 253 | 0b101110110111, 254 | 0b101110111101, 255 | 0b101111001001, 256 | 0b101111011011, 257 | 0b101111011101, 258 | 0b101111100111, 259 | 0b101111101101, 260 | 0b110000001011, 261 | 0b110000001101, 262 | 0b110000011001, 263 | 0b110000011111, 264 | 0b110001010111, 265 | 0b110001100001, 266 | 0b110001101011, 267 | 0b110001110011, 268 | 0b110010000101, 269 | 0b110010001001, 270 | 0b110010010111, 271 | 0b110010011011, 272 | 0b110010011101, 273 | 0b110010110011, 274 | 0b110010111111, 275 | 0b110011000111, 276 | 0b110011001101, 277 | 0b110011010011, 278 | 0b110011010101, 279 | 0b110011100011, 280 | 0b110011101001, 281 | 0b110011110111, 282 | 0b110100000011, 283 | 0b110100001111, 284 | 0b110100011101, 285 | 0b110100100111, 286 | 0b110100101101, 287 | 0b110101000001, 288 | 0b110101000111, 289 | 0b110101010101, 290 | 0b110101011001, 291 | 0b110101100011, 292 | 0b110101101111, 293 | 0b110101110001, 294 | 0b110110010011, 295 | 0b110110011111, 296 | 0b110110101001, 297 | 0b110110111011, 298 | 0b110110111101, 299 | 0b110111001001, 300 | 0b110111010111, 301 | 0b110111011011, 302 | 0b110111100001, 303 | 0b110111100111, 304 | 0b110111110101, 305 | 0b111000000101, 306 | 0b111000011101, 307 | 0b111000100001, 308 | 0b111000100111, 309 | 0b111000101011, 310 | 0b111000110011, 311 | 0b111000111001, 312 | 0b111001000111, 313 | 0b111001001011, 314 | 0b111001010101, 315 | 0b111001011111, 316 | 0b111001110001, 317 | 0b111001111011, 318 | 0b111001111101, 319 | 0b111010000001, 320 | 0b111010010011, 321 | 0b111010011111, 322 | 0b111010100011, 323 | 0b111010111011, 324 | 0b111011001111, 325 | 0b111011011101, 326 | 0b111011110011, 327 | 0b111011111001, 328 | 0b111100001011, 329 | 0b111100011001, 330 | 0b111100110001, 331 | 0b111100110111, 332 | 0b111101011101, 333 | 0b111101101011, 334 | 0b111101101101, 335 | 0b111101110101, 336 | 0b111110000011, 337 | 0b111110010001, 338 | 0b111110010111, 339 | 0b111110011011, 340 | 0b111110100111, 341 | 0b111110101101, 342 | 0b111110110101, 343 | 0b111111001101, 344 | 0b111111010011, 345 | 0b111111100101, 346 | 0b111111101001, 347 | 0b1000001010011, 348 | 0b1000001101001, 349 | 0b1000001111011, 350 | 0b1000001111101, 351 | 0b1000010011001, 352 | 0b1000011010001, 353 | 0b1000011101011, 354 | 0b1000100000111, 355 | 0b1000100011111, 356 | 0b1000100100011, 357 | 0b1000100111011, 358 | 0b1000101001111, 359 | 0b1000101010111, 360 | 0b1000101100001, 361 | 0b1000101101011, 362 | 0b1000110000101, 363 | 0b1000110110011, 364 | 0b1000111011001, 365 | 0b1000111011111, 366 | 0b1001000001101, 367 | 0b1001000110111, 368 | 0b1001000111101, 369 | 0b1001001100111, 370 | 0b1001001110011, 371 | 0b1001001111111, 372 | 0b1001010111001, 373 | 0b1001011000001, 374 | 0b1001011001011, 375 | 0b1001100001111, 376 | 0b1001100011101, 377 | 0b1001100100001, 378 | 0b1001100111001, 379 | 0b1001100111111, 380 | 0b1001101001101, 381 | 0b1001101110001, 382 | 0b1001110011001, 383 | 0b1001110100011, 384 | 0b1001110101001, 385 | 0b1010000000111, 386 | 0b1010000110001, 387 | 0b1010000110111, 388 | 0b1010001001111, 389 | 0b1010001011101, 390 | 0b1010001100111, 391 | 0b1010001110101, 392 | 0b1010010100111, 393 | 0b1010010101101, 394 | 0b1010011010011, 395 | 0b1010100001111, 396 | 0b1010100011101, 397 | 0b1010101001101, 398 | 0b1010110010011, 399 | 0b1010111000101, 400 | 0b1010111010111, 401 | 0b1010111011101, 402 | 0b1010111101011, 403 | 0b1011000001001, 404 | 0b1011001000111, 405 | 0b1011001010101, 406 | 0b1011001011001, 407 | 0b1011010100101, 408 | 0b1011010111101, 409 | 0b1011100010101, 410 | 0b1011100011001, 411 | 0b1011101000011, 412 | 0b1011101000101, 413 | 0b1011101110101, 414 | 0b1011110001001, 415 | 0b1011110101101, 416 | 0b1011110110011, 417 | 0b1011110111111, 418 | 0b1011111000001, 419 | 0b1100001010111, 420 | 0b1100001011101, 421 | 0b1100010010001, 422 | 0b1100010010111, 423 | 0b1100010111001, 424 | 0b1100011101111, 425 | 0b1100100011011, 426 | 0b1100100110101, 427 | 0b1100101000001, 428 | 0b1100101100101, 429 | 0b1100101111011, 430 | 0b1100110001011, 431 | 0b1100110110001, 432 | 0b1100110111101, 433 | 0b1100111001001, 434 | 0b1100111001111, 435 | 0b1100111100111, 436 | 0b1101000011011, 437 | 0b1101000101011, 438 | 0b1101000110011, 439 | 0b1101001101001, 440 | 0b1101010001011, 441 | 0b1101011010001, 442 | 0b1101011100001, 443 | 0b1101011110101, 444 | 0b1101100001011, 445 | 0b1101100010011, 446 | 0b1101100011111, 447 | 0b1101101010111, 448 | 0b1101110010001, 449 | 0b1101110100111, 450 | 0b1101110111111, 451 | 0b1101111000001, 452 | 0b1101111010011, 453 | 0b1110000000101, 454 | 0b1110000010001, 455 | 0b1110000010111, 456 | 0b1110000100111, 457 | 0b1110001001101, 458 | 0b1110010000111, 459 | 0b1110010011111, 460 | 0b1110010100101, 461 | 0b1110010111011, 462 | 0b1110011000101, 463 | 0b1110011001001, 464 | 0b1110011001111, 465 | 0b1110011110011, 466 | 0b1110100000111, 467 | 0b1110100100011, 468 | 0b1110101000011, 469 | 0b1110101010001, 470 | 0b1110101011011, 471 | 0b1110101110101, 472 | 0b1110110000101, 473 | 0b1110110001001, 474 | 0b1111000010101, 475 | 0b1111000011001, 476 | 0b1111000101111, 477 | 0b1111001000101, 478 | 0b1111001010001, 479 | 0b1111001100111, 480 | 0b1111001110011, 481 | 0b1111010001111, 482 | 0b1111011100011, 483 | 0b1111100010001, 484 | 0b1111100011011, 485 | 0b1111100100111, 486 | 0b1111101110001, 487 | 0b1111110011001, 488 | 0b1111110111011, 489 | 0b1111110111101, 490 | 0b1111111001001, 491 | }; 492 | 493 | int main() 494 | { 495 | const int num = sizeof(poly) / sizeof(*poly); 496 | int pos = 0; 497 | for (int i = 0b101; i <= 0b1111111111111; ++i) { 498 | CODE::MLS seq(i); 499 | if (seq.bad()) 500 | continue; 501 | assert(pos < num); 502 | assert(poly[pos] == i); 503 | ++pos; 504 | } 505 | assert(pos == num); 506 | std::cerr << "Maximum length sequence test passed!" << std::endl; 507 | return 0; 508 | } 509 | 510 | -------------------------------------------------------------------------------- /tests/osd_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for ordered statistics decoding 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "osd.hh" 12 | #include "galois_field.hh" 13 | #include "bose_chaudhuri_hocquenghem_encoder.hh" 14 | #include "bose_chaudhuri_hocquenghem_decoder.hh" 15 | 16 | int main() 17 | { 18 | #if 1 19 | // BCH(127, 64) T=10 20 | const int O = 4; 21 | const int N = 127; 22 | const int K = 64; 23 | const int NR = 20; 24 | const int loops = 10; 25 | const double low_SNR = -5; 26 | const double high_SNR = 5; 27 | const double QEF_SNR = -1.5; 28 | typedef CODE::GaloisField<7, 0b10001001, uint8_t> GF; 29 | std::initializer_list minpols { 30 | 0b10001001, 0b10001111, 0b10011101, 31 | 0b11110111, 0b10111111, 0b11010101, 32 | 0b10000011, 0b11101111, 0b11001011, 33 | }; 34 | #endif 35 | #if 0 36 | // NASA INTRO BCH(15, 5) T=3 37 | const int O = 1; 38 | const int N = 15; 39 | const int K = 5; 40 | const int NR = 6; 41 | const int loops = 100000; 42 | const double low_SNR = -7; 43 | const double high_SNR = 7; 44 | const double QEF_SNR = 3.5; 45 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 46 | std::initializer_list minpols { 47 | 0b10011, 0b11111, 0b00111 48 | }; 49 | #endif 50 | GF instance; 51 | const int NW = (N+7)/8; 52 | const int KW = (K+7)/8; 53 | const int PW = (N-K+7)/8; 54 | int8_t genmat[N*K]; 55 | CODE::BoseChaudhuriHocquenghemGenerator::matrix(genmat, true, minpols); 56 | CODE::BoseChaudhuriHocquenghemEncoder bchenc(minpols); 57 | CODE::LinearEncoder linenc; 58 | CODE::OrderedStatisticsDecoder osddec; 59 | CODE::BoseChaudhuriHocquenghemDecoder bchdec; 60 | GF::value_type erasures[NR]; 61 | uint8_t message[KW], parity[PW], decoded[NW], codeword[NW]; 62 | int8_t orig[N], noisy[N]; 63 | 64 | std::random_device rd; 65 | typedef std::default_random_engine generator; 66 | typedef std::uniform_int_distribution distribution; 67 | auto data = std::bind(distribution(0, 255), generator(rd())); 68 | 69 | double symb[N]; 70 | double min_SNR = high_SNR; 71 | for (double SNR = low_SNR; SNR <= high_SNR; SNR += 0.1) { 72 | //double mean_signal = 0; 73 | double sigma_signal = 1; 74 | double mean_noise = 0; 75 | double sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 76 | 77 | typedef std::normal_distribution normal; 78 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator(rd())); 79 | 80 | int awgn_errors = 0; 81 | int quantization_erasures = 0; 82 | int uncorrected_errors = 0; 83 | int ambiguity_erasures = 0; 84 | int frame_errors = 0; 85 | int bchdec_errors = 0; 86 | for (int l = 0; l < loops; ++l) { 87 | for (int i = 0; i < KW; ++i) 88 | message[i] = data(); 89 | 90 | linenc(codeword, message, genmat); 91 | 92 | for (int i = 0; i < K; ++i) 93 | assert(CODE::get_be_bit(codeword, i) == CODE::get_be_bit(message, i)); 94 | 95 | bchenc(message, parity); 96 | 97 | for (int i = K; i < N; ++i) 98 | assert(CODE::get_be_bit(codeword, i) == CODE::get_be_bit(parity, i-K)); 99 | 100 | for (int i = 0; i < N; ++i) 101 | orig[i] = 1 - 2 * CODE::get_be_bit(codeword, i); 102 | 103 | for (int i = 0; i < N; ++i) 104 | symb[i] = orig[i]; 105 | 106 | for (int i = 0; i < N; ++i) 107 | symb[i] += awgn(); 108 | 109 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 110 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 111 | double DIST = 2; // BPSK 112 | double fact = DIST / (sigma_noise * sigma_noise); 113 | for (int i = 0; i < N; ++i) 114 | noisy[i] = std::min(std::max(std::nearbyint(fact * symb[i]), -127), 127); 115 | 116 | bool unique = osddec(decoded, noisy, genmat); 117 | 118 | for (int i = 0; i < N; ++i) 119 | awgn_errors += noisy[i] * orig[i] < 0; 120 | for (int i = 0; i < N; ++i) 121 | quantization_erasures += !noisy[i]; 122 | for (int i = 0; i < N; ++i) 123 | uncorrected_errors += CODE::get_be_bit(decoded, i) != CODE::get_be_bit(codeword, i); 124 | if (unique) { 125 | bool error = false; 126 | for (int i = 0; i < N; ++i) 127 | error |= CODE::get_be_bit(decoded, i) != CODE::get_be_bit(codeword, i); 128 | frame_errors += error; 129 | } else { 130 | ambiguity_erasures += N; 131 | ++frame_errors; 132 | } 133 | 134 | for (int i = 0; i < K; ++i) 135 | CODE::set_be_bit(message, i, noisy[i] < 0); 136 | for (int i = K; i < N; ++i) 137 | CODE::set_be_bit(parity, i-K, noisy[i] < 0); 138 | int erasures_count = 0; 139 | for (int i = 0; erasures_count < NR && i < N; ++i) 140 | if (!noisy[i]) 141 | erasures[erasures_count++] = i; 142 | 143 | bchdec(message, parity, erasures, erasures_count); 144 | 145 | for (int i = 0; i < K; ++i) 146 | bchdec_errors += CODE::get_be_bit(message, i) != CODE::get_be_bit(codeword, i); 147 | for (int i = K; i < N; ++i) 148 | bchdec_errors += CODE::get_be_bit(parity, i-K) != CODE::get_be_bit(codeword, i); 149 | } 150 | 151 | double frame_error_rate = (double)frame_errors / (double)loops; 152 | double bit_error_rate = (double)uncorrected_errors / (double)(N * loops); 153 | if (!uncorrected_errors && !ambiguity_erasures) 154 | min_SNR = std::min(min_SNR, SNR); 155 | 156 | int MOD_BITS = 1; // BPSK 157 | double code_rate = (double)K / (double)N; 158 | double spectral_efficiency = code_rate * MOD_BITS; 159 | double EbN0 = 10 * std::log10(sigma_signal * sigma_signal / (spectral_efficiency * 2 * sigma_noise * sigma_noise)); 160 | 161 | double bchdec_ber = (double)bchdec_errors / (double)(N * loops); 162 | 163 | if (0) { 164 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 165 | std::cerr << EbN0 << " Eb/N0, using spectral efficiency of " << spectral_efficiency << " from " << code_rate << " code rate and " << MOD_BITS << " bits per symbol." << std::endl; 166 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 167 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 168 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 169 | std::cerr << ambiguity_erasures << " ambiguity erasures." << std::endl; 170 | std::cerr << frame_error_rate << " frame error rate." << std::endl; 171 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 172 | std::cerr << bchdec_ber << " BCH decoder bit error rate." << std::endl; 173 | } else { 174 | std::cout << SNR << " " << frame_error_rate << " " << bit_error_rate << " " << bchdec_ber << " " << EbN0 << std::endl; 175 | } 176 | } 177 | std::cerr << "QEF at: " << min_SNR << " SNR" << std::endl; 178 | assert(min_SNR < QEF_SNR); 179 | std::cerr << "Ordered statistics decoding regression test passed!" << std::endl; 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /tests/osld_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for ordered statistics list decoding 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "osd.hh" 12 | #include "galois_field.hh" 13 | #include "bose_chaudhuri_hocquenghem_encoder.hh" 14 | #include "bose_chaudhuri_hocquenghem_decoder.hh" 15 | 16 | int main() 17 | { 18 | const int L = 16; 19 | const bool oracle = true; 20 | #if 1 21 | // BCH(127, 64) T=10 22 | const int O = 4; 23 | const int N = 127; 24 | const int K = 64; 25 | const int NR = 20; 26 | const int loops = 10; 27 | const double low_SNR = -5; 28 | const double high_SNR = 5; 29 | const double QEF_SNR = -2; 30 | typedef CODE::GaloisField<7, 0b10001001, uint8_t> GF; 31 | std::initializer_list minpols { 32 | 0b10001001, 0b10001111, 0b10011101, 33 | 0b11110111, 0b10111111, 0b11010101, 34 | 0b10000011, 0b11101111, 0b11001011, 35 | }; 36 | #endif 37 | #if 0 38 | // NASA INTRO BCH(15, 5) T=3 39 | const int O = 1; 40 | const int N = 15; 41 | const int K = 5; 42 | const int NR = 6; 43 | const int loops = 100000; 44 | const double low_SNR = -7; 45 | const double high_SNR = 7; 46 | const double QEF_SNR = 3.5; 47 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 48 | std::initializer_list minpols { 49 | 0b10011, 0b11111, 0b00111 50 | }; 51 | #endif 52 | GF instance; 53 | const int NW = (N+7)/8; 54 | const int KW = (K+7)/8; 55 | const int PW = (N-K+7)/8; 56 | int8_t genmat[N*K]; 57 | CODE::BoseChaudhuriHocquenghemGenerator::matrix(genmat, true, minpols); 58 | CODE::BoseChaudhuriHocquenghemEncoder bchenc(minpols); 59 | CODE::LinearEncoder linenc; 60 | CODE::OrderedStatisticsListDecoder osddec; 61 | CODE::BoseChaudhuriHocquenghemDecoder bchdec; 62 | GF::value_type erasures[NR]; 63 | uint8_t message[KW], parity[PW], decoded[NW*L], codeword[NW]; 64 | int8_t orig[N], noisy[N]; 65 | 66 | std::random_device rd; 67 | typedef std::default_random_engine generator; 68 | typedef std::uniform_int_distribution distribution; 69 | auto data = std::bind(distribution(0, 255), generator(rd())); 70 | 71 | int rank[L]; 72 | double symb[N]; 73 | double min_SNR = high_SNR; 74 | for (double SNR = low_SNR; SNR <= high_SNR; SNR += 0.1) { 75 | //double mean_signal = 0; 76 | double sigma_signal = 1; 77 | double mean_noise = 0; 78 | double sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 79 | 80 | typedef std::normal_distribution normal; 81 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator(rd())); 82 | 83 | int awgn_errors = 0; 84 | int quantization_erasures = 0; 85 | int uncorrected_errors = 0; 86 | int ambiguity_erasures = 0; 87 | int frame_errors = 0; 88 | int bchdec_errors = 0; 89 | for (int l = 0; l < loops; ++l) { 90 | for (int i = 0; i < KW; ++i) 91 | message[i] = data(); 92 | 93 | linenc(codeword, message, genmat); 94 | 95 | for (int i = 0; i < K; ++i) 96 | assert(CODE::get_be_bit(codeword, i) == CODE::get_be_bit(message, i)); 97 | 98 | bchenc(message, parity); 99 | 100 | for (int i = K; i < N; ++i) 101 | assert(CODE::get_be_bit(codeword, i) == CODE::get_be_bit(parity, i-K)); 102 | 103 | for (int i = 0; i < N; ++i) 104 | orig[i] = 1 - 2 * CODE::get_be_bit(codeword, i); 105 | 106 | for (int i = 0; i < N; ++i) 107 | symb[i] = orig[i]; 108 | 109 | for (int i = 0; i < N; ++i) 110 | symb[i] += awgn(); 111 | 112 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 113 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 114 | double DIST = 2; // BPSK 115 | double fact = DIST / (sigma_noise * sigma_noise); 116 | for (int i = 0; i < N; ++i) 117 | noisy[i] = std::min(std::max(std::nearbyint(fact * symb[i]), -127), 127); 118 | 119 | osddec(rank, decoded, noisy, genmat); 120 | bool unique = rank[0] != rank[1]; 121 | 122 | if (oracle) { 123 | for (int j = 0; j < L; ++j) { 124 | bool found = true; 125 | for (int i = 0; i < N; ++i) 126 | found &= CODE::get_be_bit(decoded+NW*j, i) == CODE::get_be_bit(codeword, i); 127 | if (found) { 128 | for (int i = 0; j && i < NW; ++i) 129 | decoded[i] = decoded[NW*j+i]; 130 | unique = true; 131 | break; 132 | } 133 | } 134 | } 135 | 136 | for (int i = 0; i < N; ++i) 137 | awgn_errors += noisy[i] * orig[i] < 0; 138 | for (int i = 0; i < N; ++i) 139 | quantization_erasures += !noisy[i]; 140 | for (int i = 0; i < N; ++i) 141 | uncorrected_errors += CODE::get_be_bit(decoded, i) != CODE::get_be_bit(codeword, i); 142 | if (unique) { 143 | bool error = false; 144 | for (int i = 0; i < N; ++i) 145 | error |= CODE::get_be_bit(decoded, i) != CODE::get_be_bit(codeword, i); 146 | frame_errors += error; 147 | } else { 148 | ambiguity_erasures += N; 149 | ++frame_errors; 150 | } 151 | 152 | for (int i = 0; i < K; ++i) 153 | CODE::set_be_bit(message, i, noisy[i] < 0); 154 | for (int i = K; i < N; ++i) 155 | CODE::set_be_bit(parity, i-K, noisy[i] < 0); 156 | int erasures_count = 0; 157 | for (int i = 0; erasures_count < NR && i < N; ++i) 158 | if (!noisy[i]) 159 | erasures[erasures_count++] = i; 160 | 161 | bchdec(message, parity, erasures, erasures_count); 162 | 163 | for (int i = 0; i < K; ++i) 164 | bchdec_errors += CODE::get_be_bit(message, i) != CODE::get_be_bit(codeword, i); 165 | for (int i = K; i < N; ++i) 166 | bchdec_errors += CODE::get_be_bit(parity, i-K) != CODE::get_be_bit(codeword, i); 167 | } 168 | 169 | double frame_error_rate = (double)frame_errors / (double)loops; 170 | double bit_error_rate = (double)uncorrected_errors / (double)(N * loops); 171 | if (!uncorrected_errors && !ambiguity_erasures) 172 | min_SNR = std::min(min_SNR, SNR); 173 | 174 | int MOD_BITS = 1; // BPSK 175 | double code_rate = (double)K / (double)N; 176 | double spectral_efficiency = code_rate * MOD_BITS; 177 | double EbN0 = 10 * std::log10(sigma_signal * sigma_signal / (spectral_efficiency * 2 * sigma_noise * sigma_noise)); 178 | 179 | double bchdec_ber = (double)bchdec_errors / (double)(N * loops); 180 | 181 | if (0) { 182 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 183 | std::cerr << EbN0 << " Eb/N0, using spectral efficiency of " << spectral_efficiency << " from " << code_rate << " code rate and " << MOD_BITS << " bits per symbol." << std::endl; 184 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 185 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 186 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 187 | std::cerr << ambiguity_erasures << " ambiguity erasures." << std::endl; 188 | std::cerr << frame_error_rate << " frame error rate." << std::endl; 189 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 190 | std::cerr << bchdec_ber << " BCH decoder bit error rate." << std::endl; 191 | } else { 192 | std::cout << SNR << " " << frame_error_rate << " " << bit_error_rate << " " << bchdec_ber << " " << EbN0 << std::endl; 193 | } 194 | } 195 | std::cerr << "QEF at: " << min_SNR << " SNR" << std::endl; 196 | assert(min_SNR < QEF_SNR); 197 | std::cerr << "Ordered statistics list decoding regression test passed!" << std::endl; 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /tests/pf_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the prime field arithmetic 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "prime_field.hh" 13 | 14 | template 15 | void exhaustive_test() 16 | { 17 | assert(std::is_unsigned::value); 18 | assert(std::numeric_limits::max() / (PRIME-1) >= (PRIME-1)); 19 | typedef CODE::PrimeField PF; 20 | for (TYPE a = 0; a < PRIME; ++a) 21 | for (TYPE b = 0; b < PRIME; ++b) 22 | assert((PF(a) * PF(b))() == (a * b) % PRIME); 23 | for (TYPE a = 1; a < PRIME; ++a) 24 | assert(rcp(PF(a)) * PF(a) == PF(1)); 25 | for (TYPE a = 0; a < PRIME; ++a) 26 | for (TYPE b = 0; b < PRIME; ++b) 27 | assert((PF(a) + PF(b))() == (a + b) % PRIME); 28 | for (TYPE a = 0; a < PRIME; ++a) 29 | for (TYPE b = 0; b < PRIME; ++b) 30 | assert((PF(a) - PF(b))() == (a - b + PRIME) % PRIME); 31 | } 32 | 33 | int main() 34 | { 35 | exhaustive_test(); 36 | exhaustive_test(); 37 | std::cerr << "Prime field arithmetic test passed!" << std::endl; 38 | return 0; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /tests/polar_list_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the Polar Encoder and List Decoders 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "polar_helper.hh" 16 | #include "polar_list_decoder.hh" 17 | #include "polar_encoder.hh" 18 | #include "polar_sequence.hh" 19 | #include "polar_parity_aided.hh" 20 | #include "crc.hh" 21 | #include "sequence.h" 22 | 23 | bool get_bit(const uint32_t *bits, int idx) 24 | { 25 | return (bits[idx/32] >> (idx%32)) & 1; 26 | } 27 | 28 | int main() 29 | { 30 | const int M = 10; 31 | const int N = 1 << M; 32 | const bool systematic = false; 33 | const bool crc_aided = true; 34 | const bool par_aided = true; 35 | static_assert(!par_aided || !systematic, "systematic and parity aided are mutually exclusive"); 36 | CODE::CRC crc(0xD419CC15); 37 | const int C = 32; 38 | const int S = 32; 39 | #if 1 40 | const int L = 32; 41 | typedef int8_t code_type; 42 | #else 43 | const int L = 8; 44 | typedef float code_type; 45 | #endif 46 | 47 | typedef SIMD simd_type; 48 | 49 | std::random_device rd; 50 | typedef std::default_random_engine generator; 51 | typedef std::uniform_int_distribution distribution; 52 | auto data = std::bind(distribution(0, 1), generator(rd())); 53 | auto frozen = new uint32_t[N/32]; 54 | auto codeword = new code_type[N]; 55 | auto temp = new simd_type[N]; 56 | 57 | const int *reliability_sequence; 58 | double erasure_probability = 0.3; 59 | int K = (1 - erasure_probability) * N; 60 | double design_SNR = 10 * std::log10(-std::log(erasure_probability)); 61 | std::cerr << "design SNR: " << design_SNR << std::endl; 62 | if (0) { 63 | auto construct = new CODE::PolarSeqConst0; 64 | std::cerr << "sizeof(PolarSeqConst0) = " << sizeof(CODE::PolarSeqConst0) << std::endl; 65 | double better_SNR = design_SNR + 1.59175; 66 | std::cerr << "better SNR: " << better_SNR << std::endl; 67 | double probability = std::exp(-pow(10.0, better_SNR / 10)); 68 | std::cerr << "prob: " << probability << std::endl; 69 | auto rel_seq = new int[N]; 70 | (*construct)(rel_seq, M, probability); 71 | delete construct; 72 | reliability_sequence = rel_seq; 73 | } else { 74 | reliability_sequence = sequence; 75 | } 76 | for (int i = 0; i < N / 32; ++i) 77 | frozen[i] = 0; 78 | for (int i = 0; i < N - K; ++i) 79 | frozen[reliability_sequence[i]/32] |= 1 << (reliability_sequence[i]%32); 80 | int P = K / (S + 1); 81 | int F = K % (S + 1); 82 | if (!crc_aided) 83 | F += S; 84 | if (par_aided) 85 | K -= P; 86 | std::cerr << "Polar(" << N << ", " << K << ")" << std::endl; 87 | auto message = new code_type[K]; 88 | auto decoded = new simd_type[K]; 89 | std::cerr << "sizeof(PolarListDecoder) = " << sizeof(CODE::PolarListDecoder) << std::endl; 90 | auto decode = new CODE::PolarListDecoder; 91 | auto par_dec = new CODE::PolarParityDecoder; 92 | 93 | auto orig = new code_type[N]; 94 | auto noisy = new code_type[N]; 95 | auto symb = new double[N]; 96 | double low_SNR = std::floor(design_SNR-3); 97 | double high_SNR = std::ceil(design_SNR+5); 98 | double min_SNR = high_SNR, max_mbs = 0; 99 | int count = 0; 100 | std::cerr << "SNR BER Mbit/s Eb/N0" << std::endl; 101 | for (double SNR = low_SNR; count <= 3 && SNR <= high_SNR; SNR += 0.1, ++count) { 102 | //double mean_signal = 0; 103 | double sigma_signal = 1; 104 | double mean_noise = 0; 105 | double sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 106 | 107 | typedef std::normal_distribution normal; 108 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator(rd())); 109 | 110 | int64_t awgn_errors = 0; 111 | int64_t quantization_erasures = 0; 112 | int64_t uncorrected_errors = 0; 113 | int64_t ambiguity_erasures = 0; 114 | int64_t frame_errors = 0; 115 | double avg_mbs = 0; 116 | int64_t loops = 0; 117 | while (uncorrected_errors < 10000 && ++loops < 1000) { 118 | if (crc_aided) { 119 | crc.reset(); 120 | for (int i = 0; i < K-C; ++i) { 121 | bool bit = data(); 122 | crc(bit); 123 | message[i] = 1 - 2 * bit; 124 | } 125 | for (int i = 0; i < C; ++i) { 126 | bool bit = (crc() >> i) & 1; 127 | message[K-C+i] = 1 - 2 * bit; 128 | } 129 | } else { 130 | for (int i = 0; i < K; ++i) 131 | message[i] = 1 - 2 * data(); 132 | } 133 | 134 | if (systematic) { 135 | CODE::PolarSysEnc sysenc; 136 | sysenc(codeword, message, frozen, M); 137 | for (int i = 0, j = 0; i < N; ++i) 138 | if (!get_bit(frozen, i)) 139 | assert(codeword[i] == message[j++]); 140 | } else if (par_aided) { 141 | CODE::PolarParityEncoder encode; 142 | encode(codeword, message, frozen, M, S, F); 143 | } else { 144 | CODE::PolarEncoder encode; 145 | encode(codeword, message, frozen, M); 146 | } 147 | 148 | for (int i = 0; i < N; ++i) 149 | orig[i] = codeword[i]; 150 | 151 | for (int i = 0; i < N; ++i) 152 | symb[i] = codeword[i]; 153 | 154 | for (int i = 0; i < N; ++i) 155 | symb[i] += awgn(); 156 | 157 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 158 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 159 | double DIST = 2; // BPSK 160 | double fact = DIST / (sigma_noise * sigma_noise); 161 | for (int i = 0; i < N; ++i) 162 | codeword[i] = CODE::PolarHelper::quant(fact * symb[i]); 163 | 164 | for (int i = 0; i < N; ++i) 165 | noisy[i] = codeword[i]; 166 | 167 | int rank[L]; 168 | auto start = std::chrono::system_clock::now(); 169 | if (par_aided) 170 | (*par_dec)(rank, decoded, codeword, frozen, M, S, F); 171 | else 172 | (*decode)(rank, decoded, codeword, frozen, M); 173 | auto end = std::chrono::system_clock::now(); 174 | auto usec = std::chrono::duration_cast(end - start); 175 | double mbs = (double)K / usec.count(); 176 | avg_mbs += mbs; 177 | 178 | if (systematic) { 179 | CODE::PolarEncoder encode; 180 | encode(temp, decoded, frozen, M); 181 | for (int i = 0, j = 0; i < N; ++i) 182 | if (!get_bit(frozen, i)) 183 | decoded[j++] = temp[i]; 184 | } 185 | 186 | int best = 0; 187 | if (crc_aided) { 188 | bool error = true; 189 | for (int k = 0; k < L; ++k) { 190 | crc.reset(); 191 | for (int i = 0; i < K; ++i) 192 | crc(decoded[i].v[k] < 0); 193 | if (crc() == 0) { 194 | best = k; 195 | error = false; 196 | break; 197 | } 198 | } 199 | frame_errors += error; 200 | } else { 201 | bool error = rank[0] == rank[1]; 202 | for (int i = 0; i < K; ++i) 203 | error |= decoded[i].v[0] * message[i] <= 0; 204 | frame_errors += error; 205 | } 206 | 207 | for (int i = 0; i < N; ++i) 208 | awgn_errors += noisy[i] * (orig[i] < 0); 209 | for (int i = 0; i < N; ++i) 210 | quantization_erasures += !noisy[i]; 211 | for (int i = 0; i < K; ++i) 212 | uncorrected_errors += decoded[i].v[best] * message[i] <= 0; 213 | for (int i = 0; i < K; ++i) 214 | ambiguity_erasures += !decoded[i].v[best]; 215 | } 216 | 217 | avg_mbs /= loops; 218 | 219 | max_mbs = std::max(max_mbs, avg_mbs); 220 | double frame_error_rate = (double)frame_errors / (double)loops; 221 | double bit_error_rate = (double)uncorrected_errors / (double)(K * loops); 222 | if (!uncorrected_errors) 223 | min_SNR = std::min(min_SNR, SNR); 224 | else 225 | count = 0; 226 | 227 | int MOD_BITS = 1; // BPSK 228 | double code_rate = (double)K / (double)N; 229 | double spectral_efficiency = code_rate * MOD_BITS; 230 | double EbN0 = 10 * std::log10(sigma_signal * sigma_signal / (spectral_efficiency * 2 * sigma_noise * sigma_noise)); 231 | 232 | if (0) { 233 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 234 | std::cerr << EbN0 << " Eb/N0, using spectral efficiency of " << spectral_efficiency << " from " << code_rate << " code rate and " << MOD_BITS << " bits per symbol." << std::endl; 235 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 236 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 237 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 238 | std::cerr << ambiguity_erasures << " ambiguity erasures." << std::endl; 239 | std::cerr << frame_error_rate << " frame error rate." << std::endl; 240 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 241 | std::cerr << avg_mbs << " megabit per second." << std::endl; 242 | } else { 243 | std::cout << SNR << " " << frame_error_rate << " " << bit_error_rate << " " << avg_mbs << " " << EbN0 << std::endl; 244 | } 245 | } 246 | std::cerr << "QEF at: " << min_SNR << " SNR, speed: " << max_mbs << " Mb/s." << std::endl; 247 | double QEF_SNR = design_SNR + 0.5; 248 | assert(min_SNR < QEF_SNR); 249 | std::cerr << "Polar list regression test passed!" << std::endl; 250 | return 0; 251 | } 252 | -------------------------------------------------------------------------------- /tests/polar_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the Polar Encoder and Decoders 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "polar_helper.hh" 16 | #include "polar_decoder.hh" 17 | #include "polar_encoder.hh" 18 | #include "polar_freezer.hh" 19 | #include "polar_sequence.hh" 20 | 21 | bool get_bit(const uint32_t *bits, int idx) 22 | { 23 | return (bits[idx/32] >> (idx%32)) & 1; 24 | } 25 | 26 | int main() 27 | { 28 | const int M = 20; 29 | const int N = 1 << M; 30 | const bool systematic = true; 31 | #if 1 32 | typedef int8_t code_type; 33 | #else 34 | typedef float code_type; 35 | #endif 36 | 37 | std::random_device rd; 38 | typedef std::default_random_engine generator; 39 | typedef std::uniform_int_distribution distribution; 40 | auto data = std::bind(distribution(0, 1), generator(rd())); 41 | auto frozen = new uint32_t[N/32]; 42 | auto codeword = new code_type[N]; 43 | auto temp = new code_type[N]; 44 | 45 | double erasure_probability = 1. / 3.; 46 | int K = (1 - erasure_probability) * N; 47 | double design_SNR = 10 * std::log10(-std::log(erasure_probability)); 48 | std::cerr << "design SNR: " << design_SNR << std::endl; 49 | double better_SNR = design_SNR + 0.5;//1.59175; 50 | std::cerr << "better SNR: " << better_SNR << std::endl; 51 | double probability = std::exp(-pow(10.0, better_SNR / 10)); 52 | if (1) { 53 | auto freeze = new CODE::PolarCodeConst0; 54 | std::cerr << "sizeof(PolarCodeConst0) = " << sizeof(CODE::PolarCodeConst0) << std::endl; 55 | (*freeze)(frozen, M, K, probability); 56 | delete freeze; 57 | } else { 58 | auto sequence = new int[N]; 59 | auto construct = new CODE::PolarSeqConst0; 60 | std::cerr << "sizeof(PolarSeqConst0) = " << sizeof(CODE::PolarSeqConst0) << std::endl; 61 | (*construct)(sequence, M, probability); 62 | delete construct; 63 | for (int i = 0; i < N / 32; ++i) 64 | frozen[i] = 0; 65 | for (int i = 0; i < N - K; ++i) 66 | frozen[sequence[i]/32] |= 1 << (sequence[i]%32); 67 | delete[] sequence; 68 | } 69 | std::cerr << "Polar(" << N << ", " << K << ")" << std::endl; 70 | auto message = new code_type[K]; 71 | auto decoded = new code_type[K]; 72 | std::cerr << "sizeof(PolarDecoder) = " << sizeof(CODE::PolarDecoder) << std::endl; 73 | auto decode = new CODE::PolarDecoder; 74 | 75 | auto orig = new code_type[N]; 76 | auto noisy = new code_type[N]; 77 | auto symb = new double[N]; 78 | double low_SNR = std::floor(design_SNR-3); 79 | double high_SNR = std::ceil(design_SNR+5); 80 | double min_SNR = high_SNR, max_mbs = 0; 81 | int count = 0; 82 | std::cerr << "SNR BER Mbit/s Eb/N0" << std::endl; 83 | for (double SNR = low_SNR; count <= 3 && SNR <= high_SNR; SNR += 0.1, ++count) { 84 | //double mean_signal = 0; 85 | double sigma_signal = 1; 86 | double mean_noise = 0; 87 | double sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 88 | 89 | typedef std::normal_distribution normal; 90 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator(rd())); 91 | 92 | int64_t awgn_errors = 0; 93 | int64_t quantization_erasures = 0; 94 | int64_t uncorrected_errors = 0; 95 | int64_t ambiguity_erasures = 0; 96 | double avg_mbs = 0; 97 | int64_t loops = 0; 98 | while (uncorrected_errors < 1000 && ++loops < 100) { 99 | for (int i = 0; i < K; ++i) 100 | message[i] = 1 - 2 * data(); 101 | 102 | if (systematic) { 103 | CODE::PolarSysEnc sysenc; 104 | sysenc(codeword, message, frozen, M); 105 | for (int i = 0, j = 0; i < N; ++i) 106 | if (!get_bit(frozen, i)) 107 | assert(codeword[i] == message[j++]); 108 | } else { 109 | CODE::PolarEncoder encode; 110 | encode(codeword, message, frozen, M); 111 | } 112 | 113 | for (int i = 0; i < N; ++i) 114 | orig[i] = codeword[i]; 115 | 116 | for (int i = 0; i < N; ++i) 117 | symb[i] = codeword[i]; 118 | 119 | for (int i = 0; i < N; ++i) 120 | symb[i] += awgn(); 121 | 122 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 123 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 124 | double DIST = 2; // BPSK 125 | double fact = DIST / (sigma_noise * sigma_noise); 126 | for (int i = 0; i < N; ++i) 127 | codeword[i] = CODE::PolarHelper::quant(fact * symb[i]); 128 | 129 | for (int i = 0; i < N; ++i) 130 | noisy[i] = codeword[i]; 131 | 132 | auto start = std::chrono::system_clock::now(); 133 | (*decode)(decoded, codeword, frozen, M); 134 | auto end = std::chrono::system_clock::now(); 135 | auto usec = std::chrono::duration_cast(end - start); 136 | double mbs = (double)K / usec.count(); 137 | avg_mbs += mbs; 138 | 139 | if (systematic) { 140 | CODE::PolarEncoder encode; 141 | encode(temp, decoded, frozen, M); 142 | for (int i = 0, j = 0; i < N; ++i) 143 | if (!get_bit(frozen, i)) 144 | decoded[j++] = temp[i]; 145 | } 146 | 147 | for (int i = 0; i < N; ++i) 148 | awgn_errors += noisy[i] * (orig[i] < 0); 149 | for (int i = 0; i < N; ++i) 150 | quantization_erasures += !noisy[i]; 151 | for (int i = 0; i < K; ++i) 152 | uncorrected_errors += decoded[i] * message[i] <= 0; 153 | for (int i = 0; i < K; ++i) 154 | ambiguity_erasures += !decoded[i]; 155 | } 156 | 157 | avg_mbs /= loops; 158 | 159 | max_mbs = std::max(max_mbs, avg_mbs); 160 | double bit_error_rate = (double)uncorrected_errors / (double)(K * loops); 161 | if (!uncorrected_errors) 162 | min_SNR = std::min(min_SNR, SNR); 163 | else 164 | count = 0; 165 | 166 | int MOD_BITS = 1; // BPSK 167 | double code_rate = (double)K / (double)N; 168 | double spectral_efficiency = code_rate * MOD_BITS; 169 | double EbN0 = 10 * std::log10(sigma_signal * sigma_signal / (spectral_efficiency * 2 * sigma_noise * sigma_noise)); 170 | 171 | if (0) { 172 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 173 | std::cerr << EbN0 << " Eb/N0, using spectral efficiency of " << spectral_efficiency << " from " << code_rate << " code rate and " << MOD_BITS << " bits per symbol." << std::endl; 174 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 175 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 176 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 177 | std::cerr << ambiguity_erasures << " ambiguity erasures." << std::endl; 178 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 179 | std::cerr << avg_mbs << " megabit per second." << std::endl; 180 | } else { 181 | std::cout << SNR << " " << bit_error_rate << " " << avg_mbs << " " << EbN0 << std::endl; 182 | } 183 | } 184 | std::cerr << "QEF at: " << min_SNR << " SNR, speed: " << max_mbs << " Mb/s." << std::endl; 185 | double QEF_SNR = design_SNR + 0.2; 186 | assert(min_SNR < QEF_SNR); 187 | std::cerr << "Polar regression test passed!" << std::endl; 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /tests/rs_decoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Reed Solomon Decoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "galois_field.hh" 12 | #include "reed_solomon_decoder.hh" 13 | 14 | int main() 15 | { 16 | std::random_device rd; 17 | std::default_random_engine generator(rd()); 18 | if (1) { 19 | // BBC WHP031 RS(15, 11) T=2 20 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 21 | typedef CODE::ReedSolomonDecoder<4, 0, GF> RS; 22 | GF instance; 23 | RS decode; 24 | RS::value_type target[RS::N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 3, 3, 12, 12 }; 25 | RS::value_type code[RS::N]; 26 | for (int i = 0; i < RS::N; ++i) 27 | code[i] = target[i]; 28 | typedef std::uniform_int_distribution distribution; 29 | auto pos = std::bind(distribution(0, RS::N-1), generator); 30 | auto val = std::bind(distribution(0, RS::N), generator); 31 | for (int i = 0; i < RS::NR/2; ++i) 32 | code[pos()] = val(); 33 | decode(code, code + RS::K); 34 | for (int i = 0; i < RS::N; ++i) 35 | assert(code[i] == target[i]); 36 | } 37 | if (1) { 38 | // DVB-T RS(255, 239) T=8 39 | typedef CODE::GaloisField<8, 0b100011101, uint8_t> GF; 40 | typedef CODE::ReedSolomonDecoder<16, 0, GF> RS; 41 | GF instance; 42 | RS decode; 43 | RS::value_type target[RS::N]; 44 | for (int i = 0; i < RS::K; ++i) 45 | target[i] = i + 1; 46 | RS::value_type parity[RS::NP] = { 1, 126, 147, 48, 155, 224, 3, 157, 29, 226, 40, 114, 61, 30, 244, 75 }; 47 | for (int i = 0; i < RS::NP; ++i) 48 | target[RS::K+i] = parity[i]; 49 | RS::value_type code[RS::N]; 50 | for (int i = 0; i < RS::N; ++i) 51 | code[i] = target[i]; 52 | typedef std::uniform_int_distribution distribution; 53 | auto pos = std::bind(distribution(0, RS::N-1), generator); 54 | auto val = std::bind(distribution(0, RS::N), generator); 55 | for (int i = 0; i < RS::NR/2; ++i) 56 | code[pos()] = val(); 57 | decode(code, code + RS::K); 58 | for (int i = 0; i < RS::N; ++i) 59 | assert(code[i] == target[i]); 60 | } 61 | if (1) { 62 | // FUN RS(65535, 65471) T=32 63 | typedef CODE::GaloisField<16, 0b10001000000001011, uint16_t> GF; 64 | typedef CODE::ReedSolomonDecoder<64, 1, GF> RS; 65 | GF *instance = new GF(); 66 | RS *decode = new RS(); 67 | RS::value_type *target = new RS::value_type[RS::N]; 68 | for (int i = 0; i < RS::K; ++i) 69 | target[i] = i + 1; 70 | RS::value_type parity[RS::NP] = { 25271, 26303, 22052, 31318, 31233, 6076, 40148, 29468, 47507, 32655, 12404, 13265, 23901, 38403, 50967, 50433, 40818, 226, 62296, 23636, 56393, 12952, 11476, 44416, 518, 50014, 10037, 57582, 33421, 42654, 54025, 7157, 4826, 52148, 17167, 23294, 6427, 40953, 11168, 35305, 18209, 1868, 39971, 54928, 27566, 1424, 4846, 25347, 34710, 42190, 56452, 21859, 49805, 28028, 41657, 25756, 22014, 24479, 28758, 17438, 12976, 61743, 46735, 1557 }; 71 | for (int i = 0; i < RS::NP; ++i) 72 | target[RS::K+i] = parity[i]; 73 | RS::value_type *code = new RS::value_type[RS::N]; 74 | for (int i = 0; i < RS::N; ++i) 75 | code[i] = target[i]; 76 | typedef std::uniform_int_distribution distribution; 77 | auto pos = std::bind(distribution(0, RS::N-1), generator); 78 | auto val = std::bind(distribution(0, RS::N), generator); 79 | for (int i = 0; i < RS::NR/2; ++i) 80 | code[pos()] = val(); 81 | (*decode)(code, code + RS::K); 82 | for (int i = 0; i < RS::N; ++i) 83 | assert(code[i] == target[i]); 84 | delete[] target; 85 | delete[] code; 86 | delete decode; 87 | delete instance; 88 | } 89 | std::cerr << "Reed Solomon Decoder test passed!" << std::endl; 90 | return 0; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /tests/rs_encoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Reed Solomon Encoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "galois_field.hh" 11 | #include "reed_solomon_encoder.hh" 12 | 13 | int main() 14 | { 15 | if (1) { 16 | // BBC WHP031 RS(15, 11) T=2 17 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 18 | typedef CODE::ReedSolomonEncoder<4, 0, GF> RS; 19 | GF instance; 20 | RS encode; 21 | RS::value_type code[RS::N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; 22 | RS::value_type target[RS::N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 3, 3, 12, 12 }; 23 | encode(code, code + RS::K); 24 | for (int i = 0; i < RS::N; ++i) 25 | assert(code[i] == target[i]); 26 | } 27 | if (1) { 28 | // DVB-T RS(255, 239) T=8 29 | typedef CODE::GaloisField<8, 0b100011101, uint8_t> GF; 30 | typedef CODE::ReedSolomonEncoder<16, 0, GF> RS; 31 | GF instance; 32 | RS encode; 33 | RS::value_type code[RS::N], target[RS::N]; 34 | for (int i = 0; i < RS::K; ++i) 35 | target[i] = code[i] = i + 1; 36 | RS::value_type parity[RS::NP] = { 1, 126, 147, 48, 155, 224, 3, 157, 29, 226, 40, 114, 61, 30, 244, 75 }; 37 | for (int i = 0; i < RS::NP; ++i) 38 | target[RS::K+i] = parity[i]; 39 | encode(code, code + RS::K); 40 | for (int i = 0; i < RS::N; ++i) 41 | assert(code[i] == target[i]); 42 | } 43 | if (1) { 44 | // FUN RS(65535, 65471) T=32 45 | typedef CODE::GaloisField<16, 0b10001000000001011, uint16_t> GF; 46 | typedef CODE::ReedSolomonEncoder<64, 1, GF> RS; 47 | GF *instance = new GF(); 48 | RS *encode = new RS(); 49 | RS::value_type *code = new RS::value_type[RS::N]; 50 | RS::value_type *target = new RS::value_type[RS::N]; 51 | for (int i = 0; i < RS::K; ++i) 52 | target[i] = code[i] = i + 1; 53 | RS::value_type parity[RS::NP] = { 25271, 26303, 22052, 31318, 31233, 6076, 40148, 29468, 47507, 32655, 12404, 13265, 23901, 38403, 50967, 50433, 40818, 226, 62296, 23636, 56393, 12952, 11476, 44416, 518, 50014, 10037, 57582, 33421, 42654, 54025, 7157, 4826, 52148, 17167, 23294, 6427, 40953, 11168, 35305, 18209, 1868, 39971, 54928, 27566, 1424, 4846, 25347, 34710, 42190, 56452, 21859, 49805, 28028, 41657, 25756, 22014, 24479, 28758, 17438, 12976, 61743, 46735, 1557 }; 54 | for (int i = 0; i < RS::NP; ++i) 55 | target[RS::K+i] = parity[i]; 56 | (*encode)(code, code + RS::K); 57 | for (int i = 0; i < RS::N; ++i) 58 | assert(code[i] == target[i]); 59 | delete[] target; 60 | delete[] code; 61 | delete encode; 62 | delete instance; 63 | } 64 | std::cerr << "Reed Solomon Encoder test passed!" << std::endl; 65 | return 0; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /tests/rs_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the Reed Solomon Encoder and Decoder 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "galois_field.hh" 12 | #include "reed_solomon_encoder.hh" 13 | #include "reed_solomon_decoder.hh" 14 | 15 | template 16 | void rs_test(ENC *encode, DEC *decode, int trials) 17 | { 18 | std::random_device rd; 19 | std::default_random_engine generator(rd()); 20 | typedef std::uniform_int_distribution distribution; 21 | auto rnd_cnt = std::bind(distribution(0, ENC::NR), generator); 22 | auto rnd_len = std::bind(distribution(1, ENC::K), generator); 23 | auto rnd_val = std::bind(distribution(0, ENC::N), generator); 24 | while (--trials) { 25 | int data_len = rnd_len(); 26 | auto rnd_pos = std::bind(distribution(0, data_len + ENC::NP - 1), generator); 27 | typename ENC::value_type data[data_len], orig_data[data_len]; 28 | for (int i = 0; i < data_len; ++i) 29 | data[i] = orig_data[i] = rnd_val(); 30 | typename ENC::value_type parity[ENC::NP]; 31 | (*encode)(data, parity, data_len); 32 | for (int i = 0; i < data_len; ++i) 33 | assert(data[i] == orig_data[i]); 34 | typename ENC::value_type orig_parity[ENC::NP]; 35 | for (int i = 0; i < ENC::NP; ++i) 36 | orig_parity[i] = parity[i]; 37 | int error_count = rnd_cnt() / 2; 38 | typename ENC::value_type errors[ENC::NR]; 39 | for (int i = 0; i < error_count; ++i) { 40 | int pos = rnd_pos(); 41 | for (int j = 0; j < i; ++j) { 42 | if (errors[j] == pos) { 43 | pos = rnd_pos(); 44 | j = -1; 45 | } 46 | } 47 | errors[i] = pos; 48 | if (pos < data_len) 49 | data[pos] = rnd_val(); 50 | else 51 | parity[pos-data_len] = rnd_val(); 52 | } 53 | int erasures_count = ENC::NR - 2 * error_count; 54 | typename ENC::value_type erasures[erasures_count]; 55 | for (int i = 0; i < erasures_count; ++i) { 56 | int pos = rnd_pos(); 57 | for (int j = 0; j < error_count + i; ++j) { 58 | if (errors[j] == pos) { 59 | pos = rnd_pos(); 60 | j = -1; 61 | } 62 | } 63 | errors[error_count + i] = pos; 64 | erasures[i] = pos; 65 | if (pos < data_len) 66 | data[pos] = rnd_val(); 67 | else 68 | parity[pos-data_len] = rnd_val(); 69 | } 70 | int ret = (*decode)(data, parity, erasures, erasures_count, data_len); 71 | assert(ret >= 0); 72 | for (int i = 0; i < data_len; ++i) 73 | assert(data[i] == orig_data[i]); 74 | for (int i = 0; i < ENC::NP; ++i) 75 | assert(parity[i] == orig_parity[i]); 76 | } 77 | } 78 | 79 | int main() 80 | { 81 | if (1) { 82 | // BBC WHP031 RS(15, 11) T=2 83 | typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; 84 | GF instance; 85 | CODE::ReedSolomonEncoder<4, 0, GF> encoder; 86 | CODE::ReedSolomonDecoder<4, 0, GF> decoder; 87 | rs_test(&encoder, &decoder, 1000000); 88 | } 89 | if (1) { 90 | // DVB-T RS(255, 239) T=8 91 | typedef CODE::GaloisField<8, 0b100011101, uint8_t> GF; 92 | GF instance; 93 | CODE::ReedSolomonEncoder<16, 0, GF> encoder; 94 | CODE::ReedSolomonDecoder<16, 0, GF> decoder; 95 | rs_test(&encoder, &decoder, 100000); 96 | } 97 | if (1) { 98 | // FUN RS(65535, 65471) T=32 99 | typedef CODE::GaloisField<16, 0b10001000000001011, uint16_t> GF; 100 | GF instance; 101 | CODE::ReedSolomonEncoder<64, 1, GF> encoder; 102 | CODE::ReedSolomonDecoder<64, 1, GF> decoder; 103 | rs_test(&encoder, &decoder, 100); 104 | } 105 | std::cerr << "Reed Solomon regression test passed!" << std::endl; 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /tests/sequence.h: -------------------------------------------------------------------------------- 1 | static const int sequence[1024] = { 3, 17, 65, 32, 0, 4, 8, 128, 12, 34, 72, 64, 16, 2, 6, 10, 18, 20, 24, 36, 256, 40, 48, 66, 68, 1, 5, 9, 33, 80, 129, 130, 132, 96, 136, 512, 144, 7, 11, 13, 19, 14, 21, 22, 25, 35, 26, 37, 257, 38, 28, 258, 41, 42, 160, 260, 44, 49, 67, 50, 264, 69, 70, 52, 73, 74, 76, 56, 81, 272, 82, 84, 131, 192, 133, 134, 97, 137, 88, 98, 513, 138, 514, 100, 140, 288, 145, 516, 146, 15, 104, 148, 23, 520, 27, 39, 29, 259, 43, 30, 161, 261, 152, 45, 162, 262, 51, 46, 265, 112, 71, 53, 164, 266, 528, 75, 320, 54, 77, 57, 268, 273, 83, 78, 58, 168, 274, 85, 193, 86, 60, 135, 194, 276, 89, 99, 139, 515, 90, 544, 101, 141, 196, 289, 517, 176, 147, 102, 142, 290, 92, 518, 280, 105, 149, 384, 521, 106, 150, 200, 292, 522, 31, 153, 163, 263, 47, 108, 113, 154, 165, 267, 524, 529, 576, 321, 55, 114, 296, 166, 269, 530, 79, 322, 208, 156, 59, 169, 275, 270, 87, 116, 61, 195, 170, 532, 277, 324, 91, 62, 545, 197, 304, 278, 177, 103, 143, 172, 291, 93, 519, 546, 281, 120, 640, 198, 385, 224, 536, 328, 178, 107, 151, 201, 94, 293, 282, 523, 386, 548, 109, 202, 155, 294, 525, 180, 577, 284, 115, 297, 167, 388, 531, 110, 336, 323, 209, 157, 526, 204, 578, 552, 271, 768, 298, 117, 171, 184, 533, 210, 158, 325, 63, 392, 305, 118, 279, 580, 300, 534, 173, 326, 352, 547, 121, 560, 641, 199, 212, 225, 537, 329, 306, 179, 95, 283, 174, 387, 122, 400, 584, 642, 549, 226, 538, 203, 330, 295, 181, 216, 308, 285, 389, 550, 111, 124, 337, 644, 228, 527, 540, 592, 205, 579, 182, 553, 332, 416, 769, 299, 286, 312, 390, 185, 211, 159, 338, 206, 648, 393, 554, 232, 770, 119, 581, 608, 301, 535, 186, 448, 327, 353, 561, 340, 213, 394, 556, 307, 772, 582, 656, 240, 302, 175, 354, 188, 123, 401, 585, 562, 643, 214, 227, 344, 539, 396, 331, 776, 217, 309, 672, 356, 402, 586, 564, 551, 125, 645, 229, 218, 541, 310, 593, 784, 183, 333, 417, 704, 360, 404, 588, 287, 568, 313, 126, 391, 646, 230, 339, 542, 220, 594, 334, 418, 800, 207, 649, 555, 408, 368, 233, 771, 314, 609, 187, 596, 449, 420, 832, 650, 341, 234, 316, 395, 610, 557, 773, 583, 450, 600, 657, 424, 241, 896, 652, 303, 342, 236, 355, 189, 563, 612, 558, 215, 774, 452, 345, 658, 242, 432, 397, 190, 777, 616, 673, 456, 346, 660, 244, 357, 398, 403, 587, 565, 778, 624, 674, 348, 464, 664, 358, 248, 219, 566, 311, 785, 780, 705, 676, 361, 405, 589, 480, 569, 127, 786, 647, 231, 706, 680, 362, 406, 590, 543, 221, 595, 570, 335, 419, 801, 788, 409, 369, 708, 688, 364, 222, 315, 572, 802, 792, 410, 370, 712, 597, 421, 833, 804, 651, 235, 412, 372, 720, 598, 317, 422, 834, 808, 611, 451, 376, 601, 736, 318, 425, 897, 836, 653, 816, 343, 237, 602, 426, 898, 613, 840, 559, 654, 775, 238, 453, 659, 604, 243, 433, 428, 900, 614, 848, 191, 454, 434, 617, 904, 864, 457, 347, 661, 245, 436, 399, 618, 912, 458, 662, 779, 246, 440, 625, 620, 928, 675, 349, 465, 460, 665, 359, 249, 626, 960, 567, 350, 466, 666, 781, 250, 628, 677, 481, 468, 668, 782, 252, 632, 678, 787, 482, 472, 707, 681, 363, 407, 591, 484, 571, 682, 789, 488, 709, 689, 684, 365, 790, 496, 223, 573, 710, 803, 690, 366, 793, 574, 411, 371, 713, 692, 794, 714, 805, 696, 796, 413, 373, 721, 716, 806, 599, 423, 414, 374, 835, 722, 809, 377, 737, 724, 810, 319, 378, 837, 738, 728, 817, 812, 380, 603, 838, 740, 818, 427, 899, 841, 744, 655, 820, 239, 605, 842, 752, 824, 429, 901, 606, 615, 849, 844, 430, 455, 902, 850, 435, 905, 865, 852, 906, 866, 856, 437, 619, 913, 908, 868, 438, 459, 663, 914, 872, 247, 441, 621, 929, 916, 880, 442, 461, 622, 930, 920, 444, 462, 627, 961, 932, 351, 467, 667, 962, 936, 251, 629, 964, 944, 469, 669, 630, 783, 968, 253, 470, 670, 633, 976, 679, 254, 483, 473, 634, 992, 474, 636, 485, 476, 486, 683, 489, 490, 685, 791, 497, 492, 686, 498, 711, 691, 367, 500, 575, 504, 693, 795, 694, 715, 697, 797, 698, 798, 717, 807, 700, 718, 415, 375, 723, 725, 811, 726, 379, 739, 729, 813, 730, 814, 381, 839, 741, 732, 819, 382, 742, 745, 821, 746, 822, 843, 753, 748, 825, 754, 826, 607, 845, 756, 828, 431, 846, 760, 903, 851, 853, 854, 907, 867, 857, 858, 909, 869, 860, 439, 910, 870, 915, 873, 874, 917, 881, 876, 443, 918, 882, 623, 931, 921, 884, 445, 922, 888, 463, 446, 933, 924, 934, 963, 937, 938, 965, 945, 940, 966, 946, 631, 969, 948, 970, 952, 471, 671, 977, 972, 255, 978, 635, 993, 980, 994, 984, 475, 637, 996, 638, 1000, 477, 1008, 478, 487, 491, 493, 687, 494, 499, 501, 502, 505, 506, 508, 695, 699, 799, 701, 702, 719, 727, 731, 815, 733, 734, 383, 743, 747, 823, 749, 750, 755, 827, 757, 829, 758, 830, 847, 761, 762, 764, 855, 859, 861, 862, 911, 871, 875, 877, 878, 919, 883, 885, 886, 923, 889, 890, 447, 925, 892, 926, 935, 939, 941, 942, 967, 947, 949, 950, 971, 953, 954, 973, 956, 974, 979, 981, 982, 995, 985, 986, 997, 988, 998, 639, 1001, 1002, 1004, 1009, 479, 1010, 1012, 1016, 495, 503, 507, 509, 510, 703, 735, 751, 759, 831, 763, 765, 766, 863, 879, 887, 891, 893, 894, 927, 943, 951, 955, 957, 958, 975, 983, 987, 989, 990, 999, 1003, 1005, 1006, 1011, 1013, 1014, 1017, 1018, 1020, 511, 767, 895, 959, 991, 1007, 1015, 1019, 1021, 1022, 1023, }; 2 | -------------------------------------------------------------------------------- /tests/short_bch_code_decoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the short BCH codes decoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "short_bch_code_decoder.hh" 12 | 13 | int main() 14 | { 15 | std::random_device rd; 16 | std::default_random_engine generator(rd()); 17 | if (1) { 18 | // Perfect binary Golay code using x^11+x^9+x^7+x^6+x^5+x+1 19 | const int N = 23, K = 12, T = 3, POLY = 0b101011100011; 20 | CODE::ShortBCHCodeDecoder decode; 21 | int target = 0b10101101101111011111001; 22 | int damaged = target; 23 | typedef std::uniform_int_distribution distribution; 24 | auto epos = std::bind(distribution(0, N-1), generator); 25 | for (int i = 0; i < T; ++i) 26 | damaged ^= 1 << epos(); 27 | int recovered = decode(damaged); 28 | assert(recovered == target); 29 | } 30 | if (1) { 31 | // Perfect binary Golay code using x^11+x^10+x^6+x^5+x^4+x^2+1 32 | const int N = 23, K = 12, T = 3, POLY = 0b110001110101; 33 | CODE::ShortBCHCodeDecoder decode; 34 | int target = 0b10101101101100100010101; 35 | int damaged = target; 36 | typedef std::uniform_int_distribution distribution; 37 | auto epos = std::bind(distribution(0, N-1), generator); 38 | for (int i = 0; i < T; ++i) 39 | damaged ^= 1 << epos(); 40 | int recovered = decode(damaged); 41 | assert(recovered == target); 42 | } 43 | if (1) { 44 | // NASA INTRO BCH(15, 5) T=3 45 | const int N = 15, K = 5, T = 3, POLY = 0b10100110111; 46 | CODE::ShortBCHCodeDecoder decode; 47 | int target = 0b110010001111010; 48 | int damaged = target; 49 | typedef std::uniform_int_distribution distribution; 50 | auto epos = std::bind(distribution(0, N-1), generator); 51 | for (int i = 0; i < T; ++i) 52 | damaged ^= 1 << epos(); 53 | int recovered = decode(damaged); 54 | assert(recovered == target); 55 | } 56 | if (1) { 57 | // BCH(31, 16) T=3 58 | const int N = 31, K = 16, T = 3, POLY = 0b1000111110101111; 59 | CODE::ShortBCHCodeDecoder decode; 60 | int target = 970576025; 61 | int damaged = target; 62 | typedef std::uniform_int_distribution distribution; 63 | auto epos = std::bind(distribution(0, N-1), generator); 64 | for (int i = 0; i < T; ++i) 65 | damaged ^= 1 << epos(); 66 | int recovered = decode(damaged); 67 | assert(recovered == target); 68 | } 69 | std::cerr << "Short BCH code decoder test passed!" << std::endl; 70 | return 0; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /tests/short_bch_code_encoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the short BCH codes encoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include "short_bch_code_encoder.hh" 10 | 11 | template 12 | int popcnt(TYPE x) 13 | { 14 | int cnt = 0; 15 | while (x) { 16 | ++cnt; 17 | x &= x-1; 18 | } 19 | return cnt; 20 | } 21 | 22 | template 23 | void bch_test(int message, int target) 24 | { 25 | CODE::ShortBCHCodeEncoder encode; 26 | int hist[N+1] = { 0 }; 27 | for (int j = 0; j < (1 << K); ++j) 28 | for (int i = j + 1; i < (1 << K); ++i) 29 | ++hist[popcnt(encode(i)^encode(j))]; 30 | std::cout << "BCH(" << N << ", " << K << ") T=" << T << " hist:"; 31 | for (int i = 0; i < N + 1; ++i) 32 | if (hist[i]) 33 | std::cout << " " << i << ":" << hist[i]; 34 | std::cout << std::endl; 35 | int d = 0; 36 | while (!hist[d]) 37 | ++d; 38 | assert((d-1)/2 == T); 39 | int codeword = encode(message); 40 | assert(target == codeword); 41 | } 42 | 43 | int main() 44 | { 45 | if (1) { 46 | // Perfect binary Golay code using x^11+x^9+x^7+x^6+x^5+x+1 47 | bch_test<23, 12, 3, 0b101011100011>(0b101011011011, 0b10101101101111011111001); 48 | } 49 | if (1) { 50 | // Perfect binary Golay code using x^11+x^10+x^6+x^5+x^4+x^2+1 51 | bch_test<23, 12, 3, 0b110001110101>(0b101011011011, 0b10101101101100100010101); 52 | } 53 | if (1) { 54 | // NASA INTRO BCH(15, 5) T=3 55 | bch_test<15, 5, 3, 0b10100110111>(0b11001, 0b110010001111010); 56 | } 57 | if (1) { 58 | // BCH(31, 16) T=3 59 | bch_test<31, 16, 3, 0b1000111110101111>(29619, 970576025); 60 | } 61 | std::cerr << "Short BCH code encoder test passed!" << std::endl; 62 | return 0; 63 | } 64 | 65 | -------------------------------------------------------------------------------- /tests/short_bch_code_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the short BCH codes encoder and decoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "short_bch_code_decoder.hh" 12 | #include "short_bch_code_encoder.hh" 13 | 14 | template 15 | void bch_test(int trials) 16 | { 17 | CODE::ShortBCHCodeEncoder encode; 18 | CODE::ShortBCHCodeDecoder decode; 19 | std::random_device rd; 20 | typedef std::default_random_engine generator; 21 | typedef std::uniform_int_distribution distribution; 22 | auto data = std::bind(distribution(0, (1<>(N-K)) == message); 28 | int decoded = decode(codeword); 29 | assert(decoded == codeword); 30 | int damaged = codeword; 31 | for (int i = 0; i < T; ++i) 32 | damaged ^= 1 << epos(); 33 | int recovered = decode(damaged); 34 | assert(recovered == codeword); 35 | } 36 | } 37 | 38 | int main() 39 | { 40 | if (1) { 41 | // Perfect binary Golay code using x^11+x^9+x^7+x^6+x^5+x+1 42 | bch_test<23, 12, 3, 0b101011100011>(1000000); 43 | } 44 | if (1) { 45 | // Perfect binary Golay code using x^11+x^10+x^6+x^5+x^4+x^2+1 46 | bch_test<23, 12, 3, 0b110001110101>(1000000); 47 | } 48 | if (1) { 49 | // NASA INTRO BCH(15, 5) T=3 50 | bch_test<15, 5, 3, 0b10100110111>(1000000); 51 | } 52 | if (1) { 53 | // BCH(31, 16) T=3 54 | bch_test<31, 16, 3, 0b1000111110101111>(1000000); 55 | } 56 | std::cerr << "Short BCH code regression test passed!" << std::endl; 57 | return 0; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tests/simd_sort_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the SIMD sorting wrappers 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "simd.hh" 13 | #include "simd_sort.hh" 14 | 15 | int main() 16 | { 17 | const int WIDTH = 32; 18 | typedef int32_t TYPE; 19 | typedef SIMD::uint_type UINT; 20 | std::random_device rd; 21 | typedef std::default_random_engine generator; 22 | typedef std::uniform_int_distribution distribution; 23 | auto rand = std::bind(distribution(0, WIDTH), generator(rd())); 24 | for (int loop = 0; loop < 1000000; ++loop) { 25 | SIMD a, b, c; 26 | for (int i = 0; i < WIDTH; ++i) 27 | a.v[i] = rand(); 28 | b = vsort(a); 29 | c = a; 30 | std::sort(c.v, c.v+WIDTH); 31 | for (int i = 0; i < WIDTH; ++i) 32 | assert(b.v[i] == c.v[i]); 33 | SIMD d, e; 34 | d = vorder(a); 35 | for (int i = 0; i < WIDTH; ++i) 36 | e.v[i] = i; 37 | std::stable_sort(e.v, e.v+WIDTH, [a](int i, int j){ return a.v[i] < a.v[j]; }); 38 | for (int i = 0; i < WIDTH; ++i) 39 | assert(d.v[i] == e.v[i]); 40 | } 41 | std::cerr << "SIMD sort regression test passed!" << std::endl; 42 | return 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /tests/simplex_encoder_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Test for the Simplex codes encoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include "simplex_encoder.hh" 10 | 11 | template 12 | void simplex_test() 13 | { 14 | CODE::SimplexEncoder encode; 15 | const int W = 1 << K; 16 | const int N = (1 << K) - 1; 17 | const int D = 1 << (K - 1); 18 | int hist[N+1] = { 0 }; 19 | for (int j = 0; j < W; ++j) { 20 | int8_t cj[N]; 21 | encode(cj, j); 22 | for (int i = j + 1; i < W; ++i) { 23 | int8_t ci[N]; 24 | encode(ci, i); 25 | int d = 0; 26 | for (int n = 0; n < N; ++n) 27 | d += ci[n] != cj[n]; 28 | ++hist[d]; 29 | } 30 | } 31 | std::cout << "Simplex(" << N << ", " << K << ", " << D << ") hist:"; 32 | for (int i = 0; i < N + 1; ++i) 33 | if (hist[i]) 34 | std::cout << " " << i << ":" << hist[i]; 35 | std::cout << std::endl; 36 | int d = 0; 37 | while (!hist[d]) 38 | ++d; 39 | assert(d == D); 40 | } 41 | 42 | int main() 43 | { 44 | if (1) { 45 | simplex_test<2>(); 46 | } 47 | if (1) { 48 | simplex_test<3>(); 49 | } 50 | if (1) { 51 | simplex_test<4>(); 52 | } 53 | if (1) { 54 | simplex_test<5>(); 55 | } 56 | if (1) { 57 | simplex_test<6>(); 58 | } 59 | if (1) { 60 | simplex_test<7>(); 61 | } 62 | if (1) { 63 | simplex_test<8>(); 64 | } 65 | std::cerr << "Simplex encoder test passed!" << std::endl; 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tests/simplex_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the Simplex code Encoder and soft Decoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "simplex_encoder.hh" 14 | #include "simplex_decoder.hh" 15 | 16 | template 17 | int popcnt(TYPE x) 18 | { 19 | int cnt = 0; 20 | while (x) { 21 | ++cnt; 22 | x &= x-1; 23 | } 24 | return cnt; 25 | } 26 | 27 | #if 0 28 | const int LOOPS = 320000; 29 | const float QEF_SNR = 7.0; 30 | const int DATA_LEN = 2; 31 | #endif 32 | #if 0 33 | const int LOOPS = 160000; 34 | const float QEF_SNR = 4.5; 35 | const int DATA_LEN = 3; 36 | #endif 37 | #if 1 38 | const int LOOPS = 80000; 39 | const float QEF_SNR = 2.0; 40 | const int DATA_LEN = 4; 41 | #endif 42 | #if 0 43 | const int LOOPS = 40000; 44 | const float QEF_SNR = -1.0; 45 | const int DATA_LEN = 5; 46 | #endif 47 | #if 0 48 | const int LOOPS = 20000; 49 | const float QEF_SNR = -3.5; 50 | const int DATA_LEN = 6; 51 | #endif 52 | 53 | int main() 54 | { 55 | const int CODE_LEN = (1 << DATA_LEN) - 1; 56 | 57 | CODE::SimplexEncoder encode; 58 | CODE::SimplexDecoder decode; 59 | 60 | std::random_device rd; 61 | std::default_random_engine generator(rd()); 62 | typedef std::uniform_int_distribution uniform; 63 | typedef std::normal_distribution normal; 64 | 65 | float min_SNR = 20; 66 | 67 | for (float SNR = -10; SNR <= 10; SNR += 0.1) { 68 | //float mean_signal = 0; 69 | float sigma_signal = 1; 70 | float mean_noise = 0; 71 | float sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 72 | 73 | auto data = std::bind(uniform(0, (1 << DATA_LEN) - 1), generator); 74 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator); 75 | 76 | int awgn_errors = 0; 77 | int quantization_erasures = 0; 78 | int uncorrected_errors = 0; 79 | int ambiguity_errors = 0; 80 | for (int loop = 0; loop < LOOPS; ++loop) { 81 | int8_t code[CODE_LEN], orig[CODE_LEN], noisy[CODE_LEN]; 82 | float symb[CODE_LEN]; 83 | 84 | int dat = data(); 85 | encode(code, dat); 86 | 87 | for (int i = 0; i < CODE_LEN; ++i) 88 | orig[i] = code[i]; 89 | 90 | for (int i = 0; i < CODE_LEN; ++i) 91 | symb[i] = code[i]; 92 | 93 | for (int i = 0; i < CODE_LEN; ++i) 94 | symb[i] += awgn(); 95 | 96 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 97 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 98 | float DIST = 2; // BPSK 99 | float fact = DIST / (sigma_noise * sigma_noise); 100 | for (int i = 0; i < CODE_LEN; ++i) 101 | code[i] = std::min(std::max(std::nearbyint(fact * symb[i]), -127), 127); 102 | 103 | for (int i = 0; i < CODE_LEN; ++i) 104 | noisy[i] = code[i]; 105 | 106 | int dec = decode(code); 107 | 108 | for (int i = 0; i < CODE_LEN; ++i) 109 | awgn_errors += noisy[i] * orig[i] < 0; 110 | for (int i = 0; i < CODE_LEN; ++i) 111 | quantization_erasures += !noisy[i]; 112 | uncorrected_errors += dec < 0 ? DATA_LEN : popcnt(dat^dec); 113 | ambiguity_errors += dec < 0; 114 | } 115 | float bit_error_rate = (float)uncorrected_errors / (float)(DATA_LEN * LOOPS); 116 | if (bit_error_rate < 0.0001) 117 | min_SNR = std::min(min_SNR, SNR); 118 | 119 | if (0) { 120 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 121 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 122 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 123 | std::cerr << ambiguity_errors << " ambiguity errors." << std::endl; 124 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 125 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 126 | } else { 127 | std::cout << SNR << " " << bit_error_rate << std::endl; 128 | } 129 | } 130 | 131 | std::cerr << "QEF at: " << min_SNR << " SNR" << std::endl; 132 | assert(min_SNR < QEF_SNR); 133 | std::cerr << "Simplex code regression test passed!" << std::endl; 134 | return 0; 135 | } 136 | 137 | -------------------------------------------------------------------------------- /tests/soft_bch_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for the short BCH code encoder and soft decoder 3 | 4 | Copyright 2020 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "short_bch_code_decoder.hh" 14 | #include "short_bch_code_encoder.hh" 15 | 16 | template 17 | int popcnt(TYPE x) 18 | { 19 | int cnt = 0; 20 | while (x) { 21 | ++cnt; 22 | x &= x-1; 23 | } 24 | return cnt; 25 | } 26 | 27 | #if 1 28 | // Perfect binary Golay code using x^11+x^9+x^7+x^6+x^5+x+1 29 | const int LOOPS = 100000; 30 | const float QEF_SNR = 3.0; 31 | const int CODE_LEN = 23; 32 | const int DATA_LEN = 12; 33 | const int RADIUS_T = 3; 34 | const int GEN_POLY = 0b101011100011; 35 | #endif 36 | #if 0 37 | // Perfect binary Golay code using x^11+x^10+x^6+x^5+x^4+x^2+1 38 | const int LOOPS = 100000; 39 | const float QEF_SNR = 3.0; 40 | const int CODE_LEN = 23; 41 | const int DATA_LEN = 12; 42 | const int RADIUS_T = 3; 43 | const int GEN_POLY = 0b110001110101; 44 | #endif 45 | #if 0 46 | // NASA INTRO BCH(15, 5) T=3 47 | const int LOOPS = 100000; 48 | const float QEF_SNR = 3.0; 49 | const int CODE_LEN = 15; 50 | const int DATA_LEN = 5; 51 | const int RADIUS_T = 3; 52 | const int GEN_POLY = 0b10100110111; 53 | #endif 54 | #if 0 55 | // BCH(31, 16) T=3 56 | const int LOOPS = 50000; 57 | const float QEF_SNR = 6.5; 58 | const int CODE_LEN = 31; 59 | const int DATA_LEN = 16; 60 | const int RADIUS_T = 3; 61 | const int GEN_POLY = 0b1000111110101111; 62 | #endif 63 | 64 | int main() 65 | { 66 | CODE::ShortBCHCodeEncoder encode; 67 | CODE::ShortBCHCodeDecoder decode; 68 | 69 | std::random_device rd; 70 | std::default_random_engine generator(rd()); 71 | typedef std::uniform_int_distribution uniform; 72 | typedef std::normal_distribution normal; 73 | 74 | float min_SNR = 20; 75 | 76 | for (float SNR = -10; SNR <= 10; SNR += 0.1) { 77 | //float mean_signal = 0; 78 | float sigma_signal = 1; 79 | float mean_noise = 0; 80 | float sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10))); 81 | 82 | auto data = std::bind(uniform(0, (1 << DATA_LEN) - 1), generator); 83 | auto awgn = std::bind(normal(mean_noise, sigma_noise), generator); 84 | 85 | int awgn_errors = 0; 86 | int quantization_erasures = 0; 87 | int uncorrected_errors = 0; 88 | int ambiguity_errors = 0; 89 | for (int loop = 0; loop < LOOPS; ++loop) { 90 | int8_t code[CODE_LEN], orig[CODE_LEN], noisy[CODE_LEN]; 91 | float symb[CODE_LEN]; 92 | 93 | int dat = data(); 94 | int enc = encode(code, dat); 95 | 96 | for (int i = 0; i < CODE_LEN; ++i) 97 | orig[i] = code[i]; 98 | 99 | for (int i = 0; i < CODE_LEN; ++i) 100 | symb[i] = code[i]; 101 | 102 | for (int i = 0; i < CODE_LEN; ++i) 103 | symb[i] += awgn(); 104 | 105 | // $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$ 106 | // $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$ 107 | float DIST = 2; // BPSK 108 | float fact = DIST / (sigma_noise * sigma_noise); 109 | for (int i = 0; i < CODE_LEN; ++i) 110 | code[i] = std::min(std::max(std::nearbyint(fact * symb[i]), -127), 127); 111 | 112 | for (int i = 0; i < CODE_LEN; ++i) 113 | noisy[i] = code[i]; 114 | 115 | int dec = decode(code); 116 | 117 | for (int i = 0; i < CODE_LEN; ++i) 118 | awgn_errors += noisy[i] * orig[i] < 0; 119 | for (int i = 0; i < CODE_LEN; ++i) 120 | quantization_erasures += !noisy[i]; 121 | uncorrected_errors += dec < 0 ? CODE_LEN : popcnt(enc^dec); 122 | ambiguity_errors += dec < 0; 123 | } 124 | float bit_error_rate = (float)uncorrected_errors / (float)(CODE_LEN * LOOPS); 125 | if (bit_error_rate < 0.0001) 126 | min_SNR = std::min(min_SNR, SNR); 127 | 128 | if (0) { 129 | std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl; 130 | std::cerr << awgn_errors << " errors caused by AWGN." << std::endl; 131 | std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl; 132 | std::cerr << ambiguity_errors << " ambiguity errors." << std::endl; 133 | std::cerr << uncorrected_errors << " errors uncorrected." << std::endl; 134 | std::cerr << bit_error_rate << " bit error rate." << std::endl; 135 | } else { 136 | std::cout << SNR << " " << bit_error_rate << std::endl; 137 | } 138 | } 139 | 140 | std::cerr << "QEF at: " << min_SNR << " SNR" << std::endl; 141 | assert(min_SNR < QEF_SNR); 142 | std::cerr << "Soft BCH regression test passed!" << std::endl; 143 | return 0; 144 | } 145 | 146 | -------------------------------------------------------------------------------- /tests/sort_regression_test.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Regression Test for sorting 3 | 4 | Copyright 2024 Ahmet Inan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "sort.hh" 13 | 14 | int main() 15 | { 16 | const int MAX_N = 128; 17 | unsigned seed = 42; 18 | if (1) { 19 | std::random_device rd; 20 | seed = rd(); 21 | } 22 | typedef std::default_random_engine generator; 23 | typedef std::uniform_int_distribution distribution; 24 | auto rand = std::bind(distribution(1, MAX_N), generator(seed)); 25 | typedef std::pair Pair; 26 | Pair a[MAX_N], b[MAX_N], c[MAX_N]; 27 | int d[MAX_N], e[MAX_N]; 28 | auto comp = [](Pair a, Pair b){ return a.second < b.second; }; 29 | CODE::MergeSort merge_sort; 30 | for (int loop = 0; loop < 1000000; ++loop) { 31 | int size = rand(); 32 | for (int i = 0; i < size; ++i) 33 | a[i].first = b[i].first = c[i].first = e[i] = i; 34 | for (int i = 0; i < size; ++i) 35 | a[i].second = b[i].second = c[i].second = d[i] = rand(); 36 | std::stable_sort(a, a+size, comp); 37 | CODE::insertion_sort(b, size, comp); 38 | for (int i = 0; i < size; ++i) 39 | assert(a[i] == b[i]); 40 | merge_sort(c, size, comp); 41 | for (int i = 0; i < size; ++i) 42 | assert(a[i] == c[i]); 43 | CODE::insertion_sort(e, d, size); 44 | for (int i = 0; i < size; ++i) 45 | assert(a[i].first == e[i]); 46 | } 47 | std::cerr << "Sorting regression test passed!" << std::endl; 48 | return 0; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /xorshift.hh: -------------------------------------------------------------------------------- 1 | /* 2 | Class of pseudorandom number generators, discovered by George Marsaglia 3 | 4 | Copyright 2018 Ahmet Inan 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace CODE { 10 | 11 | class Xorshift32 12 | { 13 | static const uint32_t Y = 2463534242; 14 | uint32_t y_; 15 | public: 16 | typedef uint32_t result_type; 17 | static constexpr result_type min() 18 | { 19 | return 0; 20 | } 21 | static constexpr result_type max() 22 | { 23 | return UINT32_MAX; 24 | } 25 | Xorshift32(uint32_t y = Y) : y_(y) {} 26 | void reset(uint32_t y = Y) 27 | { 28 | y_ = y; 29 | } 30 | uint32_t operator()() 31 | { 32 | y_ ^= y_ << 13; 33 | y_ ^= y_ >> 17; 34 | y_ ^= y_ << 5; 35 | return y_; 36 | } 37 | }; 38 | 39 | class Xorshift64 40 | { 41 | static const uint64_t X = 88172645463325252; 42 | uint64_t x_; 43 | public: 44 | typedef uint64_t result_type; 45 | static constexpr result_type min() 46 | { 47 | return 0; 48 | } 49 | static constexpr result_type max() 50 | { 51 | return UINT64_MAX; 52 | } 53 | Xorshift64(uint64_t x = X) : x_(x) {} 54 | void reset(uint64_t x = X) 55 | { 56 | x_ = x; 57 | } 58 | uint64_t operator()() 59 | { 60 | x_ ^= x_ << 13; 61 | x_ ^= x_ >> 7; 62 | x_ ^= x_ << 17; 63 | return x_; 64 | } 65 | }; 66 | 67 | class Xorwow 68 | { 69 | static const uint32_t X = 123456789; 70 | static const uint32_t Y = 362436069; 71 | static const uint32_t Z = 521288629; 72 | static const uint32_t W = 88675123; 73 | static const uint32_t V = 5783321; 74 | static const uint32_t D = 6615241; 75 | uint32_t x_, y_, z_, w_, v_, d_; 76 | public: 77 | typedef uint32_t result_type; 78 | static constexpr result_type min() 79 | { 80 | return 0; 81 | } 82 | static constexpr result_type max() 83 | { 84 | return UINT32_MAX; 85 | } 86 | Xorwow(uint32_t x = X, uint32_t y = Y, 87 | uint32_t z = Z, uint32_t w = W, 88 | uint32_t v = V, uint32_t d = D) : 89 | x_(x), y_(y), z_(z), w_(w), v_(v), d_(d) {} 90 | void reset(uint32_t x = X, uint32_t y = Y, 91 | uint32_t z = Z, uint32_t w = W, 92 | uint32_t v = V, uint32_t d = D) 93 | { 94 | x_ = x; 95 | y_ = y; 96 | z_ = z; 97 | w_ = w; 98 | v_ = v; 99 | d_ = d; 100 | } 101 | uint32_t operator()() 102 | { 103 | uint32_t t = x_ ^ (x_ >> 2); 104 | x_ = y_; y_ = z_; z_ = w_; w_ = v_; 105 | v_ = (v_ ^ (v_ << 4)) ^ (t ^ (t << 1)); 106 | d_ += 362437; 107 | return d_ + v_; 108 | } 109 | }; 110 | 111 | class Xorshift128 112 | { 113 | static const uint32_t X = 123456789; 114 | static const uint32_t Y = 362436069; 115 | static const uint32_t Z = 521288629; 116 | static const uint32_t W = 88675123; 117 | uint32_t x_, y_, z_, w_; 118 | public: 119 | typedef uint32_t result_type; 120 | static constexpr result_type min() 121 | { 122 | return 0; 123 | } 124 | static constexpr result_type max() 125 | { 126 | return UINT32_MAX; 127 | } 128 | Xorshift128(uint32_t x = X, uint32_t y = Y, 129 | uint32_t z = Z, uint32_t w = W) : 130 | x_(x), y_(y), z_(z), w_(w) {} 131 | void reset(uint32_t x = X, uint32_t y = Y, 132 | uint32_t z = Z, uint32_t w = W) 133 | { 134 | x_ = x; 135 | y_ = y; 136 | z_ = z; 137 | w_ = w; 138 | } 139 | uint32_t operator()() 140 | { 141 | uint32_t t = (x_ ^ (x_ << 11)); 142 | x_ = y_; y_ = z_; z_ = w_; 143 | w_ = (w_ ^ (w_ >> 19)) ^ (t ^ (t >> 8)); 144 | return w_; 145 | } 146 | }; 147 | 148 | } 149 | 150 | --------------------------------------------------------------------------------