diff --git a/include/armral.h b/include/armral.h index ab3a27dfccae925bf08372ddf45432480dcd81d1..7e77bf545bc68209a1d990f5766ea19e3bad0201 100644 --- a/include/armral.h +++ b/include/armral.h @@ -3721,7 +3721,7 @@ armral_status armral_ldpc_rate_recovery(armral_ldpc_graph_t bg, uint32_t z, * This function takes a pre-allocated buffer (`buffer`) to use internally. * This variant will not call any system memory allocators. * - * The buffer must be at least `((z* 66) + e ) * sizeof(uint8_t)` bytes. + * The buffer must be at least `e * sizeof(uint8_t)` bytes. * * @param[in] bg The type of base graph for which rate matching is * to be performed. diff --git a/src/UpperPHY/LDPC/ldpc_rate_matching.cpp b/src/UpperPHY/LDPC/ldpc_rate_matching.cpp index b14ea72a0a99839b75fcdf2a802174c9571d114b..40952c0dc059c62b1d92c9bb082f08a49a2046a8 100644 --- a/src/UpperPHY/LDPC/ldpc_rate_matching.cpp +++ b/src/UpperPHY/LDPC/ldpc_rate_matching.cpp @@ -11,6 +11,16 @@ namespace armral::ldpc { +void copy_bits(uint32_t src_bit, uint32_t start_idx, uint32_t len, uint32_t l, + const uint8_t *in_bits, uint8_t *out) { + for (uint32_t i = start_idx; i < len; i++) { + uint32_t bit = (in_bits[src_bit / 8] >> (7 - src_bit % 8)) & 1; + out[i / 8] |= (bit << (7 - (i % 8))); + src_bit++; + src_bit = src_bit % l; + } +} + static void bit_selection(uint32_t z, uint32_t n, uint32_t e, uint32_t len_filler_bits, uint32_t k, uint32_t k0, const uint8_t *in, uint8_t *out, @@ -49,12 +59,13 @@ static void bit_selection(uint32_t z, uint32_t n, uint32_t e, uint32_t num_bytes = (e + 7) / 8; out[num_bytes - 1] = 0; - uint32_t src_bit = k0; - for (uint32_t i = 0; i < e; ++i) { - uint32_t bit = (in_bits[src_bit / 8] >> (7 - src_bit % 8)) & 1; - out[i / 8] |= (bit << (7 - (i % 8))); - src_bit++; - src_bit = src_bit % n; + uint32_t len = ((n - len_filler_bits) <= e) ? (n - len_filler_bits) : e; + copy_bits(k0, 0, len, (n - len_filler_bits), in_bits, out); + + // repetition + if (len < e) { + copy_bits(k0, (n - len_filler_bits), e, (n - len_filler_bits), in_bits, + out); } } diff --git a/src/UpperPHY/LDPC/ldpc_rate_recovery.cpp b/src/UpperPHY/LDPC/ldpc_rate_recovery.cpp index 803e814d9540faefdb08eba1ce3198476adb79c5..400bd640cb26689a21d77a00ed2d6da0d1c8459f 100644 --- a/src/UpperPHY/LDPC/ldpc_rate_recovery.cpp +++ b/src/UpperPHY/LDPC/ldpc_rate_recovery.cpp @@ -11,45 +11,80 @@ namespace armral::ldpc { +void soft_combine(const int8_t *in, int8_t *out) { + int32_t llr = *out + *in; + if (llr < (int32_t)INT8_MIN) { + *out = INT8_MIN; + } else if (llr > (int32_t)INT8_MAX) { + *out = INT8_MAX; + } else { + *out = (int8_t)llr; + } +} + void undo_selection(uint32_t z, uint32_t n, uint32_t e, uint32_t len_filler_bits, uint32_t k, uint32_t k0, - const int8_t *in, int8_t *out, int8_t *scratch_llrs) { + const int8_t *in, int8_t *out) { // performs the inverse of the bit selection as specified by // section 5.4.2.1 in 3GPP TS 38.212 assert(k0 >= 0 && k0 < n); assert(e > 0); - int8_t *out_llrs; + // systematic bits len + uint32_t len_s_bits = k - len_filler_bits - (2 * z); + uint32_t k_idx = 0; + uint32_t k0_start = k0; + uint32_t soft_buff_len = n - len_filler_bits; - if (len_filler_bits > 0) { - out_llrs = scratch_llrs; - } else { - out_llrs = out; + // fill the data in output systematic region + if (k0 < len_s_bits) { + for (; (k0 < len_s_bits) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; + } + } + // offset k0 to skip filler bits + if (k0 >= len_s_bits && k0 < len_s_bits + len_filler_bits) { + k0 = len_s_bits + len_filler_bits; + } + // fill the data in output parity region + for (; (k0 < n) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; } - for (uint32_t i = 0; i < e; i++) { - int32_t sum = (int32_t)out_llrs[(k0 + i) % n] + (int32_t)in[i]; - if (sum < (int32_t)INT8_MIN) { - out_llrs[(k0 + i) % n] = INT8_MIN; - } else if (sum > (int32_t)INT8_MAX) { - out_llrs[(k0 + i) % n] = INT8_MAX; - } else { - out_llrs[(k0 + i) % n] = (int8_t)sum; + if (((n != e) && (k_idx != n)) || ((n == e) && (k0 != 0))) { + while (k_idx < e) { + // fill the data in output systematic region + for (k0 = 0; (k0 < len_s_bits) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; + } + // fill the data in output parity region, filler bits skipped + for (k0 = len_s_bits + len_filler_bits; (k0 < n) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; + } } } - //insert Filler bits - if (len_filler_bits > 0) { - uint32_t len_s_f_bits = k - z * 2; // length of systematic & filler bits - uint32_t len_s_bits = - len_s_f_bits - len_filler_bits; // length of systematic bits - uint32_t len_p_bits = n - len_s_f_bits; // length of parity bits - - memcpy(out, scratch_llrs, len_s_bits); - memset(&out[len_s_bits], 0, len_filler_bits); - memcpy(&out[len_s_bits + len_filler_bits], &scratch_llrs[len_s_bits], - len_p_bits); + // soft combining + if ((e > soft_buff_len) || ((e == n) && (len_filler_bits != 0))) { + k_idx = soft_buff_len; + k0 = k0_start; + // systematic region + if (k0 < len_s_bits) { + for (; (k0 < len_s_bits) && (k_idx < e); k0++) { + soft_combine(&in[k_idx++], &out[k0]); + } + } + + // offset k0 to skip filler bits + if (k0 >= len_s_bits && k0 < len_s_bits + len_filler_bits) { + k0 = len_s_bits + len_filler_bits; + } + + // parity region + for (; (k0 < n) && (k_idx < e); k0++) { + soft_combine(&in[k_idx++], &out[k0]); + } } } @@ -98,7 +133,6 @@ armral_status rate_recovery(armral_ldpc_graph_t bg, uint32_t z, uint32_t e, Allocator &allocator) { auto llrs = allocate_zeroed(allocator, e); uint32_t n = (bg == LDPC_BASE_GRAPH_2) ? 50 * z : 66 * z; - auto scratch_llrs = allocate_zeroed(allocator, n); uint32_t ncb = 0; if (nref != 0) { ncb = (n > nref) ? nref : n; @@ -108,8 +142,7 @@ armral_status rate_recovery(armral_ldpc_graph_t bg, uint32_t z, uint32_t e, uint32_t k0 = starting_position(bg, rv, n, ncb, z); uint32_t qm = 2 + (uint32_t)mod * 2; undo_interleave(e, qm, src, llrs.get()); - undo_selection(z, n, e, len_filler_bits, k, k0, llrs.get(), dst, - scratch_llrs.get()); + undo_selection(z, n, e, len_filler_bits, k, k0, llrs.get(), dst); return ARMRAL_SUCCESS; } diff --git a/test/LDPC/rate_matching/main.cpp b/test/LDPC/rate_matching/main.cpp index 52bc1280dc4f816972555bcec9fbb0dadc7892aa..e677b6f74923b0ade514ed02b3765fba03deaaed 100644 --- a/test/LDPC/rate_matching/main.cpp +++ b/test/LDPC/rate_matching/main.cpp @@ -12,6 +12,17 @@ #include namespace { + +void copy_bits(uint32_t src_bit, uint32_t start_idx, uint32_t len, uint32_t l, + const uint8_t *in_bits, uint8_t *out) { + for (uint32_t i = start_idx; i < len; i++) { + uint32_t bit = (in_bits[src_bit / 8] >> (7 - src_bit % 8)) & 1; + out[i / 8] |= (bit << (7 - (i % 8))); + src_bit++; + src_bit = src_bit % l; + } +} + void ref_bit_selection(uint32_t z, uint32_t n, uint32_t e, uint32_t len_filler_bits, uint32_t k, uint32_t k0, const uint8_t *in, uint8_t *out) { @@ -63,12 +74,13 @@ void ref_bit_selection(uint32_t z, uint32_t n, uint32_t e, out[i] = 0; } - int src_bit = k0; - for (uint32_t i = 0; i < e; ++i) { - uint32_t bit = (in_bits[src_bit / 8] >> (7 - src_bit % 8)) & 1; - out[i / 8] |= (bit << (7 - (i % 8))); - src_bit++; - src_bit = src_bit % n; + uint32_t len = ((n - len_filler_bits) <= e) ? (n - len_filler_bits) : e; + copy_bits(k0, 0, len, (n - len_filler_bits), in_bits, out); + + // repetition + if (len < e) { + copy_bits(k0, (n - len_filler_bits), e, (n - len_filler_bits), in_bits, + out); } } @@ -322,8 +334,9 @@ bool test_ldpc_rate_matching( const armral_ldpc_graph_t base_graph_list[] = {LDPC_BASE_GRAPH_1, LDPC_BASE_GRAPH_2}; // Arbitrary subset of lifting sizes given in Table 5.3.2-1. - const uint32_t lifting_size_list[] = {2, 3, 5, 11, 28, 32, 104}; + const uint32_t lifting_size_list[] = {2, 3, 8, 11, 28, 32, 104}; const uint32_t rv_list[] = {0, 1, 2, 3}; + const uint8_t rb_list[] = {1, 1, 1, 3, 4, 7, 24}; const uint32_t filler_bits_list[] = {0, 28, 32}; uint32_t lifting_size_list_len = sizeof(lifting_size_list) / sizeof(lifting_size_list[0]); @@ -338,8 +351,10 @@ bool test_ldpc_rate_matching( }; // Arbitrary subset of modulation schemes. - const armral_modulation_type mod_list[] = {ARMRAL_MOD_QPSK, ARMRAL_MOD_16QAM}; + const armral_modulation_type mod_list[] = {ARMRAL_MOD_QPSK, + ARMRAL_MOD_256QAM}; for (auto z : lifting_size_list) { + static uint8_t rb_idx = 0; for (auto bg : base_graph_list) { for (auto len_filler_bits : filler_bits_list) { if (z < 28) { @@ -347,9 +362,10 @@ bool test_ldpc_rate_matching( } // Default to base graph 1 uint32_t num_bits = src_length(bg, z); - uint32_t num_bytes = (num_bits + 7) / 8; - std::vector src_store = allocate_random_u8(num_bytes, 0U, 1U); - std::vector src = std::vector(num_bytes); + uint32_t num_bytes_enc_out = (num_bits + 7) / 8; + std::vector src_store = + allocate_random_u8(num_bytes_enc_out, 0U, 1U); + std::vector src = std::vector(num_bytes_enc_out); for (auto mod : mod_list) { uint32_t qm = num_bit_per_symbol(mod); // Choose G as number of coded bits. The specs define @@ -361,28 +377,33 @@ bool test_ldpc_rate_matching( if (bg == LDPC_BASE_GRAPH_2) { g = 10 * z; } - // Choose e based on E_r = N_L * qm * ceil(G / (N_L * qm * C)) - // where N_L = 1, and C = 1. - uint32_t e = qm * ((g + qm - 1) / qm); - num_bytes = (e + 7) / 8; + // cosider single layer, single CB . + uint32_t num_res = + qm * + (mod == ARMRAL_MOD_QPSK ? 144 : 32); // 12 symbols or 3 symbols + uint32_t e = rb_list[rb_idx] * num_res; + uint32_t num_bytes_ratematch_out = (e + 7) / 8; for (auto rv : rv_list) { for (auto nref : nref_list[bg]) { - std::vector ref = std::vector(num_bytes); - std::vector dst = std::vector(num_bytes); + std::vector ref = + std::vector(num_bytes_ratematch_out); + std::vector dst = + std::vector(num_bytes_ratematch_out); - memcpy(src.data(), src_store.data(), num_bytes); + memcpy(src.data(), src_store.data(), num_bytes_enc_out); ldpc_rate_matching_under_test(bg, z, e, nref, len_filler_bits, g, rv, mod, src.data(), dst.data()); - memcpy(src.data(), src_store.data(), num_bytes); + memcpy(src.data(), src_store.data(), num_bytes_enc_out); armral_ref_rate_matching(bg, z, e, nref, len_filler_bits, g, rv, mod, src.data(), ref.data()); passed &= check_results_u8(test_name, dst.data(), ref.data(), - num_bytes); + num_bytes_ratematch_out); } } } } } + rb_idx++; } return passed; } diff --git a/test/LDPC/rate_recovery/main.cpp b/test/LDPC/rate_recovery/main.cpp index 88a67093cf473a7e44734c7c84de32a0f4f97b2f..3a399b132b8cb8af16f0b7addf48fc01806247de 100644 --- a/test/LDPC/rate_recovery/main.cpp +++ b/test/LDPC/rate_recovery/main.cpp @@ -15,7 +15,7 @@ namespace { void ref_undo_selection(uint32_t z, uint32_t n, uint32_t e, uint32_t len_filler_bits, uint32_t k, uint32_t k0, - const int8_t *in, int8_t *out, int8_t *scratch_llrs) { + const int8_t *in, int8_t *out) { // performs the inverse of the bit selection as specified by // section 5.4.2.1 in 3GPP TS 38.212 assert(k0 >= 0 && k0 < n); @@ -26,37 +26,75 @@ void ref_undo_selection(uint32_t z, uint32_t n, uint32_t e, assert(out[i] == 0); } - int8_t *out_llrs; - - if (len_filler_bits > 0) { - out_llrs = scratch_llrs; - } else { - out_llrs = out; + // systematic bits len + uint32_t len_s_bits = k - len_filler_bits - (2 * z); + int32_t llr; + uint32_t k_idx = 0; + uint32_t k0_start = k0; + uint32_t soft_buff_len = n - len_filler_bits; + + // fill the data in output systematic region + if (k0 < len_s_bits) { + for (; (k0 < len_s_bits) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; + } + } + // offset k0 to skip filler bits + if (k0 >= len_s_bits && k0 < len_s_bits + len_filler_bits) { + k0 = len_s_bits + len_filler_bits; + } + // fill the data in output parity region + for (; (k0 < n) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; } - for (uint32_t i = 0; i < e; i++) { - int32_t sum = (int32_t)out_llrs[(k0 + i) % n] + (int32_t)in[i]; - if (sum < (int32_t)INT8_MIN) { - out_llrs[(k0 + i) % n] = INT8_MIN; - } else if (sum > (int32_t)INT8_MAX) { - out_llrs[(k0 + i) % n] = INT8_MAX; - } else { - out_llrs[(k0 + i) % n] = (int8_t)sum; + if (((n != e) && (k_idx != n)) || ((n == e) && (k0 != 0))) { + while (k_idx < e) { + // fill the data in output systematic region + for (k0 = 0; (k0 < len_s_bits) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; + } + // fill the data in output parity region, filler bits skipped + for (k0 = len_s_bits + len_filler_bits; (k0 < n) && (k_idx < e); k0++) { + out[k0] = in[k_idx++]; + } } } - // insert Filler bits - if (len_filler_bits > 0) { + // soft combining + if ((e > soft_buff_len) || ((e == n) && (len_filler_bits != 0))) { + k_idx = soft_buff_len; + k0 = k0_start; + // systematic region + if (k0 < len_s_bits) { + for (; (k0 < len_s_bits) && (k_idx < e); k0++) { + llr = out[k0] + in[k_idx++]; + if (llr < (int32_t)INT8_MIN) { + out[k0] = INT8_MIN; + } else if (llr > (int32_t)INT8_MAX) { + out[k0] = INT8_MAX; + } else { + out[k0] = (int8_t)llr; + } + } + } - uint32_t len_s_f_bits = k - z * 2; // length of systematic & filler bits - uint32_t len_s_bits = - len_s_f_bits - len_filler_bits; // length of systematic bits - uint32_t len_p_bits = n - len_s_f_bits; // length of parity bits + // offset k0 to skip filler bits + if (k0 >= len_s_bits && k0 < len_s_bits + len_filler_bits) { + k0 = len_s_bits + len_filler_bits; + } - memcpy(out, scratch_llrs, len_s_bits); - memset(&out[len_s_bits], 0, len_filler_bits); - memcpy(&out[len_s_bits + len_filler_bits], &scratch_llrs[len_s_bits], - len_p_bits); + // parity region + for (; (k0 < n) && (k_idx < e); k0++) { + llr = out[k0] + in[k_idx++]; + if (llr < (int32_t)INT8_MIN) { + out[k0] = INT8_MIN; + } else if (llr > (int32_t)INT8_MAX) { + out[k0] = INT8_MAX; + } else { + out[k0] = (int8_t)llr; + } + } } } @@ -151,73 +189,78 @@ void armral_ref_rate_recovery(armral_ldpc_graph_t bg, uint32_t z, uint32_t e, uint32_t k0 = starting_position(bg, rv, n, ncb, z); uint32_t qm = num_bit_per_symbol(mod); std::vector llrs(e); - std::vector scratch_llrs(n); ref_undo_interleave(e, qm, src, llrs.data()); - ref_undo_selection(z, n, e, len_filler_bits, k, k0, llrs.data(), dst, - scratch_llrs.data()); + ref_undo_selection(z, n, e, len_filler_bits, k, k0, llrs.data(), dst); } bool test_ref_rate_recovery() { bool passed = true; // Test ring behaviour of selection process. - uint32_t e = 32; - uint32_t n = 32; + uint32_t e = 100; + uint32_t n = 100; uint32_t k0 = 16; + uint32_t z = 2; + uint32_t k = 20; auto in = allocate_random_i8(e); std::vector out(n); - ref_undo_selection(0, n, e, 0, 0, k0, in.data(), out.data(), NULL); + ref_undo_selection(z, n, e, 0, k, k0, in.data(), out.data()); passed &= std::equal(out.begin() + k0, out.begin() + n, in.begin()); - passed &= std::equal(out.begin(), out.begin() + k0, in.begin() + k0); + passed &= std::equal(out.begin(), out.begin() + k0, in.begin() + (e - k0)); // Test selection process with shortening - e = 22; - n = 32; + + e = 80; + n = 100; k0 = 16; + k = 20; + in = allocate_random_i8(e); memset(out.data(), 0, n * sizeof(int8_t)); - ref_undo_selection(0, n, e, 0, 0, k0, in.data(), out.data(), NULL); - passed &= std::equal(out.begin() + k0, out.begin() + n, in.begin()); - passed &= std::equal(out.begin(), out.begin() + (e - k0), in.begin() + k0); - passed &= std::all_of(out.begin() + (e - k0), out.begin() + k0, + ref_undo_selection(z, n, e, 0, k, k0, in.data(), out.data()); + passed &= std::all_of(out.begin(), out.begin() + k0, + [](uint8_t i) { return i == 0; }); + passed &= std::equal(out.begin() + k0, out.begin() + k0 + e, in.begin()); + passed &= std::all_of(out.begin() + (e + k0), out.begin() + n, [](uint8_t i) { return i == 0; }); // Test summation of llrs - e = 32; + e = 100; // Every llr is transmitted twice - n = 16; + n = 50; k0 = 0; + k = 20; + in = allocate_random_i8(e); // The final llrs is the sum of the i-th and (i+16)-th llr. // Ensure that saturation is tested at least once. in[0] = INT8_MIN; - in[16] = -1; + in[50] = -1; in[1] = INT8_MAX; - in[17] = 1; + in[51] = 1; // Half the values so that saturation cannot be reached. - for (uint32_t i = 2; i < 16; i++) { + for (uint32_t i = 2; i < 50; i++) { in[i] = in[i] / 2; - in[16 + i] = in[16 + i] / 2; + in[50 + i] = in[50 + i] / 2; } + memset(out.data(), 0, n * sizeof(int8_t)); - ref_undo_selection(0, n, e, 0, 0, k0, in.data(), out.data(), NULL); + ref_undo_selection(z, n, e, 0, k, k0, in.data(), out.data()); passed &= (out[0] == INT8_MIN); passed &= (out[1] == INT8_MAX); - for (uint32_t i = 2; i < 16; i++) { - int32_t sol = (int32_t)in[i] + (int32_t)in[i + 16]; + for (uint32_t i = 2; i < 50; i++) { + int32_t sol = (int32_t)in[i] + (int32_t)in[i + 50]; passed &= (out[i] == (int8_t)sol); } // Test selection process with filler bits - e = 328; - n = 328; + e = 80; + n = 100; k0 = 0; - uint32_t z = 7; - uint32_t f = 16; + uint32_t f = 8; + k = 20; auto in_filler = allocate_random_i8(e); std::vector out_filler(n); - std::vector scratch_llrs(n); - ref_undo_selection(z, n, e, f, z * 10, k0, in_filler.data(), - out_filler.data(), scratch_llrs.data()); + out_filler.data()); for (uint16_t i = 0, j = 0; i < n; i++) { if (i < (z * 10 - 2 * z - f)) { @@ -225,9 +268,16 @@ bool test_ref_rate_recovery() { j++; } else if ((i >= (z * 10 - 2 * z - f)) && (i < (z * 10 - 2 * z))) { passed &= (0 == out_filler[i]); - } else { + } else if (n <= e) { passed &= (in_filler[j] == out_filler[i]); j++; + } else { + if (i < (e + f)) { + passed &= (in_filler[j] == out_filler[i]); + j++; + } else { + passed &= (0 == out_filler[i]); + } } } @@ -264,6 +314,7 @@ bool test_ldpc_rate_recovery( {0, (66 * lifting_size_min), (66 * lifting_size_max)}, //BG1 {0, (50 * lifting_size_min), (50 * lifting_size_max)}, //BG2 }; + const uint8_t rb_list[] = {1, 1, 1, 3, 4, 7, 24}; const uint32_t filler_bits_list[] = {0, 28, 36}; // Arbitrary subset of modulation schemes. const armral_modulation_type mod_list[] = {ARMRAL_MOD_QPSK, @@ -272,6 +323,7 @@ bool test_ldpc_rate_recovery( const uint32_t rv_list[] = {0, 1}; for (auto z : lifting_size_list) { + static uint8_t rb_idx = 0; for (auto bg : base_graph_list) { uint32_t n = (bg == LDPC_BASE_GRAPH_2) ? 50 * z : 66 * z; for (auto len_filler_bits : filler_bits_list) { @@ -281,7 +333,10 @@ bool test_ldpc_rate_recovery( for (auto mod : mod_list) { uint32_t qm = num_bit_per_symbol(mod); uint32_t g = (bg == LDPC_BASE_GRAPH_2) ? 10 * z : 22 * z; - uint32_t e = qm * ((g + qm - 1) / qm); + uint32_t num_res = + qm * + (mod == ARMRAL_MOD_QPSK ? 144 : 32); // 12 symbols or 3 symbols + uint32_t e = rb_list[rb_idx] * num_res; for (auto rv : rv_list) { for (auto nref : nref_list[bg]) { std::vector llrs_in = allocate_random_i8(e); @@ -298,6 +353,7 @@ bool test_ldpc_rate_recovery( } } } + rb_idx++; } if (!passed) { @@ -309,10 +365,8 @@ bool test_ldpc_rate_recovery( int main(int argc, char **argv) { bool passed = test_ref_rate_recovery(); - passed &= test_ldpc_rate_recovery("LDPCRateRecovery", armral_ldpc_rate_recovery); - passed &= test_ldpc_rate_recovery( "LDPCRateRecoveryNoAlloc", [](armral_ldpc_graph_t bg, uint32_t z, uint32_t e, auto... args) { @@ -320,6 +374,5 @@ int main(int argc, char **argv) { return armral_ldpc_rate_recovery_noalloc(bg, z, e, args..., buffer.data()); }); - exit(passed ? EXIT_SUCCESS : EXIT_FAILURE); }