From 5cba1989ec536b93450169e09776f9c717a681d4 Mon Sep 17 00:00:00 2001 From: Christopher Taylor Date: Fri, 26 May 2017 19:51:30 -0700 Subject: [PATCH] WIP --- LeopardCommon.cpp | 981 ++++--------------- LeopardCommon.h | 95 +- LeopardDecoder.cpp | 1220 ------------------------ LeopardDecoder.h | 1220 ------------------------ LeopardEncoder.cpp | 1220 ------------------------ LeopardEncoder.h | 1220 ------------------------ LeopardFF16.cpp | 1606 ++++++++++++++------------------ LeopardFF16.h | 1228 ++---------------------- LeopardFF8.cpp | 678 ++++++++------ LeopardFF8.h | 79 +- docs/HighRateDecoder.pdf | Bin 0 -> 164713 bytes docs/LowRateDecoder.pdf | Bin 0 -> 349855 bytes leopard.cpp | 218 +++-- leopard.h | 26 +- proj/Leopard.sln | 14 +- proj/Leopard.vcxproj | 13 +- proj/Leopard.vcxproj.filters | 12 - tests/experiments.cpp | 615 ++++++++++++ tests/proj/Benchmark.vcxproj | 11 +- tests/proj/Experiments.filters | 22 + tests/proj/Experiments.vcxproj | 181 ++++ 21 files changed, 2458 insertions(+), 8201 deletions(-) delete mode 100644 LeopardDecoder.cpp delete mode 100644 LeopardDecoder.h delete mode 100644 LeopardEncoder.cpp delete mode 100644 LeopardEncoder.h create mode 100644 docs/HighRateDecoder.pdf create mode 100644 docs/LowRateDecoder.pdf create mode 100644 tests/experiments.cpp create mode 100644 tests/proj/Experiments.filters create mode 100644 tests/proj/Experiments.vcxproj diff --git a/LeopardCommon.cpp b/LeopardCommon.cpp index 82bdbcf..55850bc 100644 --- a/LeopardCommon.cpp +++ b/LeopardCommon.cpp @@ -139,818 +139,233 @@ void InitializeCPUArch() } - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; -} - - //------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. +// XOR Memory -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; - -// {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) +void xor_mem( + void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, + unsigned bytes) { - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); - a = sum; - b = dif; -} - -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) -{ - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; -} - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) -{ - unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - unsigned y = 0; - data[y] = t0; y += s; - data[y] = t1; y += s; - data[y] = t2; y += s; - data[y] = t3; -} - -static inline void FWHT_8(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; -} - -static inline void FWHT_16(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t8, t9); - FWHT_2(t10, t11); - FWHT_2(t12, t13); - FWHT_2(t14, t15); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t8, t10); - FWHT_2(t9, t11); - FWHT_2(t12, t14); - FWHT_2(t13, t15); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - FWHT_2(t8, t12); - FWHT_2(t9, t13); - FWHT_2(t10, t14); - FWHT_2(t11, t15); - FWHT_2(t0, t8); - FWHT_2(t1, t9); - FWHT_2(t2, t10); - FWHT_2(t3, t11); - FWHT_2(t4, t12); - FWHT_2(t5, t13); - FWHT_2(t6, t14); - FWHT_2(t7, t15); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; - data[8] = t8; - data[9] = t9; - data[10] = t10; - data[11] = t11; - data[12] = t12; - data[13] = t13; - data[14] = t14; - data[15] = t15; -} - -static void FWHT_SmallData(fwht_t* data, unsigned ldn) -{ - const unsigned n = (1UL << ldn); - - if (n <= 2) - { - if (n == 2) - FWHT_2(data[0], data[1]); - return; - } - - for (unsigned ldm = ldn; ldm > 3; ldm -= 2) - { - unsigned m = (1UL << ldm); - unsigned m4 = (m >> 2); - for (unsigned r = 0; r < n; r += m) - for (unsigned j = 0; j < m4; j++) - FWHT_4(data + j + r, m4); - } - - if (ldn & 1) - { - for (unsigned i0 = 0; i0 < n; i0 += 8) - FWHT_8(data + i0); - } - else - { - for (unsigned i0 = 0; i0 < n; i0 += 4) - FWHT_4(data + i0); - } -} - -// Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) -{ - if (ldn <= 13) - { - FWHT_SmallData(data, ldn); - return; - } - - FWHT_2(data[2], data[3]); - FWHT_4(data + 4); - FWHT_8(data + 8); - FWHT_16(data + 16); - for (unsigned ldm = 5; ldm < ldn; ++ldm) - FWHT(data + (unsigned)(1UL << ldm), ldm); - - for (unsigned ldm = 0; ldm < ldn; ++ldm) - { - const unsigned mh = (1UL << ldm); - for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2) - FWHT_2(data[t1], data[t2]); - } -} - -#endif - - -//------------------------------------------------------------------------------ -// Memory Buffer XOR - -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) -{ - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) - { - while (bytes >= 64) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; - } - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; - } - } - else -# endif // LEO_TRY_NEON - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); - } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) +#if defined(LEO_TRY_AVX2) if (CpuHasAVX2) { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); - - while (bytes >= 128) + LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(vx); + const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(vy); + do { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); - + const LEO_M256 x0 = _mm256_xor_si256(_mm256_loadu_si256(x32), _mm256_loadu_si256(y32)); + const LEO_M256 x1 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 1), _mm256_loadu_si256(y32 + 1)); + const LEO_M256 x2 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 2), _mm256_loadu_si256(y32 + 2)); + const LEO_M256 x3 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 3), _mm256_loadu_si256(y32 + 3)); _mm256_storeu_si256(x32, x0); _mm256_storeu_si256(x32 + 1, x1); _mm256_storeu_si256(x32 + 2, x2); _mm256_storeu_si256(x32 + 3, x3); - bytes -= 128, x32 += 4, y32 += 4; - } - - // Handle multiples of 32 bytes - while (bytes >= 32) + } while (bytes >= 128); + if (bytes > 0) { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; + const LEO_M256 x0 = _mm256_xor_si256(_mm256_loadu_si256(x32), _mm256_loadu_si256(y32)); + const LEO_M256 x1 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 1), _mm256_loadu_si256(y32 + 1)); + _mm256_storeu_si256(x32, x0); + _mm256_storeu_si256(x32 + 1, x1); } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); + return; } - else -# endif // LEO_TRY_AVX2 +#endif // LEO_TRY_AVX2 + LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); + const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); + do { - while (bytes >= 64) - { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); - - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - - bytes -= 64, x16 += 4, y16 += 4; - } - } -#endif // LEO_TARGET_MOBILE - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); - - bytes -= 16, ++x16, ++y16; - } - - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); - - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } - - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } - - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } + const LEO_M128 x0 = _mm_xor_si128(_mm_loadu_si128(x16), _mm_loadu_si128(y16)); + const LEO_M128 x1 = _mm_xor_si128(_mm_loadu_si128(x16 + 1), _mm_loadu_si128(y16 + 1)); + const LEO_M128 x2 = _mm_xor_si128(_mm_loadu_si128(x16 + 2), _mm_loadu_si128(y16 + 2)); + const LEO_M128 x3 = _mm_xor_si128(_mm_loadu_si128(x16 + 3), _mm_loadu_si128(y16 + 3)); + _mm_storeu_si128(x16, x0); + _mm_storeu_si128(x16 + 1, x1); + _mm_storeu_si128(x16 + 2, x2); + _mm_storeu_si128(x16 + 3, x3); + bytes -= 64, x16 += 4, y16 += 4; + } while (bytes > 0); } - -//------------------------------------------------------------------------------ -// Formal Derivative - -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) +void xor_mem2( + void * LEO_RESTRICT vx_0, const void * LEO_RESTRICT vy_0, + void * LEO_RESTRICT vx_1, const void * LEO_RESTRICT vy_1, + unsigned bytes) { - for (unsigned i = 1; i < size; ++i) +#if defined(LEO_TRY_AVX2) + if (CpuHasAVX2) { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; - - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; + LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast (vx_0); + const LEO_M256 * LEO_RESTRICT y32_0 = reinterpret_cast(vy_0); + LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast (vx_1); + const LEO_M256 * LEO_RESTRICT y32_1 = reinterpret_cast(vy_1); + do + { + const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); + const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); + const LEO_M256 x2_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 2), _mm256_loadu_si256(y32_0 + 2)); + const LEO_M256 x3_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 3), _mm256_loadu_si256(y32_0 + 3)); + const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); + const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); + const LEO_M256 x2_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 2), _mm256_loadu_si256(y32_1 + 2)); + const LEO_M256 x3_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 3), _mm256_loadu_si256(y32_1 + 3)); + _mm256_storeu_si256(x32_0, x0_0); + _mm256_storeu_si256(x32_0 + 1, x1_0); + _mm256_storeu_si256(x32_0 + 2, x2_0); + _mm256_storeu_si256(x32_0 + 3, x3_0); + _mm256_storeu_si256(x32_1, x0_1); + _mm256_storeu_si256(x32_1 + 1, x1_1); + _mm256_storeu_si256(x32_1 + 2, x2_1); + _mm256_storeu_si256(x32_1 + 3, x3_1); + x32_0 += 4, y32_0 += 4; + x32_1 += 4, y32_1 += 4; + bytes -= 128; + } while (bytes >= 128); + if (bytes > 0) + { + const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); + const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); + const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); + const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); + _mm256_storeu_si256(x32_0, x0_0); + _mm256_storeu_si256(x32_0 + 1, x1_0); + _mm256_storeu_si256(x32_1, x0_1); + _mm256_storeu_si256(x32_1 + 1, x1_1); + } + return; } - - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); +#endif // LEO_TRY_AVX2 + LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast (vx_0); + const LEO_M128 * LEO_RESTRICT y16_0 = reinterpret_cast(vy_0); + LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast (vx_1); + const LEO_M128 * LEO_RESTRICT y16_1 = reinterpret_cast(vy_1); + do + { + const LEO_M128 x0_0 = _mm_xor_si128(_mm_loadu_si128(x16_0), _mm_loadu_si128(y16_0)); + const LEO_M128 x1_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 1), _mm_loadu_si128(y16_0 + 1)); + const LEO_M128 x2_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 2), _mm_loadu_si128(y16_0 + 2)); + const LEO_M128 x3_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 3), _mm_loadu_si128(y16_0 + 3)); + const LEO_M128 x0_1 = _mm_xor_si128(_mm_loadu_si128(x16_1), _mm_loadu_si128(y16_1)); + const LEO_M128 x1_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 1), _mm_loadu_si128(y16_1 + 1)); + const LEO_M128 x2_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 2), _mm_loadu_si128(y16_1 + 2)); + const LEO_M128 x3_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 3), _mm_loadu_si128(y16_1 + 3)); + _mm_storeu_si128(x16_0, x0_0); + _mm_storeu_si128(x16_0 + 1, x1_0); + _mm_storeu_si128(x16_0 + 2, x2_0); + _mm_storeu_si128(x16_0 + 3, x3_0); + _mm_storeu_si128(x16_1, x0_1); + _mm_storeu_si128(x16_1 + 1, x1_1); + _mm_storeu_si128(x16_1 + 2, x2_1); + _mm_storeu_si128(x16_1 + 3, x3_1); + x16_0 += 4, y16_0 += 4; + x16_1 += 4, y16_1 += 4; + bytes -= 64; + } while (bytes > 0); } - -//------------------------------------------------------------------------------ -// Fast Fourier Transform - -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) +void xor_mem3( + void * LEO_RESTRICT vx_0, const void * LEO_RESTRICT vy_0, + void * LEO_RESTRICT vx_1, const void * LEO_RESTRICT vy_1, + void * LEO_RESTRICT vx_2, const void * LEO_RESTRICT vy_2, + unsigned bytes) { - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) +#if defined(LEO_TRY_AVX2) + if (CpuHasAVX2) { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) + LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast (vx_0); + const LEO_M256 * LEO_RESTRICT y32_0 = reinterpret_cast(vy_0); + LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast (vx_1); + const LEO_M256 * LEO_RESTRICT y32_1 = reinterpret_cast(vy_1); + LEO_M256 * LEO_RESTRICT x32_2 = reinterpret_cast (vx_2); + const LEO_M256 * LEO_RESTRICT y32_2 = reinterpret_cast(vy_2); + do { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } -} - -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) + const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); + const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); + const LEO_M256 x2_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 2), _mm256_loadu_si256(y32_0 + 2)); + const LEO_M256 x3_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 3), _mm256_loadu_si256(y32_0 + 3)); + const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); + const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); + const LEO_M256 x2_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 2), _mm256_loadu_si256(y32_1 + 2)); + const LEO_M256 x3_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 3), _mm256_loadu_si256(y32_1 + 3)); + const LEO_M256 x0_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2), _mm256_loadu_si256(y32_2)); + const LEO_M256 x1_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 1), _mm256_loadu_si256(y32_2 + 1)); + const LEO_M256 x2_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 2), _mm256_loadu_si256(y32_2 + 2)); + const LEO_M256 x3_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 3), _mm256_loadu_si256(y32_2 + 3)); + _mm256_storeu_si256(x32_0, x0_0); + _mm256_storeu_si256(x32_0 + 1, x1_0); + _mm256_storeu_si256(x32_0 + 2, x2_0); + _mm256_storeu_si256(x32_0 + 3, x3_0); + _mm256_storeu_si256(x32_1, x0_1); + _mm256_storeu_si256(x32_1 + 1, x1_1); + _mm256_storeu_si256(x32_1 + 2, x2_1); + _mm256_storeu_si256(x32_1 + 3, x3_1); + _mm256_storeu_si256(x32_2, x0_2); + _mm256_storeu_si256(x32_2 + 1, x1_2); + _mm256_storeu_si256(x32_2 + 2, x2_2); + _mm256_storeu_si256(x32_2 + 3, x3_2); + x32_0 += 4, y32_0 += 4; + x32_1 += 4, y32_1 += 4; + x32_2 += 4, y32_2 += 4; + bytes -= 128; + } while (bytes >= 128); + if (bytes > 0) { - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; + const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); + const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); + const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); + const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); + const LEO_M256 x0_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2), _mm256_loadu_si256(y32_2)); + const LEO_M256 x1_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 1), _mm256_loadu_si256(y32_2 + 1)); + _mm256_storeu_si256(x32_0, x0_0); + _mm256_storeu_si256(x32_0 + 1, x1_0); + _mm256_storeu_si256(x32_1, x0_1); + _mm256_storeu_si256(x32_1 + 1, x1_1); + _mm256_storeu_si256(x32_2, x0_2); + _mm256_storeu_si256(x32_2 + 1, x1_2); } + return; } -} - - -//------------------------------------------------------------------------------ -// FFT Initialization - -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial - -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() -{ - GFSymbol temp[kGFBits - 1]; - - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); - - for (unsigned m = 0; m < (kGFBits - 1); ++m) +#endif // LEO_TRY_AVX2 + LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast (vx_0); + const LEO_M128 * LEO_RESTRICT y16_0 = reinterpret_cast(vy_0); + LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast (vx_1); + const LEO_M128 * LEO_RESTRICT y16_1 = reinterpret_cast(vy_1); + LEO_M128 * LEO_RESTRICT x16_2 = reinterpret_cast (vx_2); + const LEO_M128 * LEO_RESTRICT y16_2 = reinterpret_cast(vy_2); + do { - const unsigned step = (unsigned)1 << (m + 1); - - skewVec[((unsigned)1 << m) - 1] = 0; - - for (unsigned i = m; i < (kGFBits - 1); ++i) - { - const unsigned s = ((unsigned)1 << (i + 1)); - - for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; - } - - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; - - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); - } - - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; - - temp[0] = kFieldModulus - temp[0]; - - for (unsigned i = 1; i < (kGFBits - 1); ++i) - temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); - - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); -} - - -//------------------------------------------------------------------------------ -// Encoder - -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) -{ - memcpy(codeword, data, sizeof(GFSymbol) * k); - - IFLT(codeword, k, 0); - - for (unsigned i = k; i < kFieldSize; i += k) - { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) - { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } - - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) - { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; - } - } - } - - //printf("Decoding is successful!\n"); -} - - -//------------------------------------------------------------------------------ -// Entrypoint - -int main(int argc, char **argv) -{ - // Initialize architecture-specific code - leo_architecture_init(); - - // Fill GFLog table and GFExp table - InitField(); - - // Compute factors used in erasure decoder - InitFieldOperations(); - - unsigned seed = (unsigned)time(NULL); - for (;;) - { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); - - ++seed; - } - - return 0; + const LEO_M128 x0_0 = _mm_xor_si128(_mm_loadu_si128(x16_0), _mm_loadu_si128(y16_0)); + const LEO_M128 x1_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 1), _mm_loadu_si128(y16_0 + 1)); + const LEO_M128 x2_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 2), _mm_loadu_si128(y16_0 + 2)); + const LEO_M128 x3_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 3), _mm_loadu_si128(y16_0 + 3)); + const LEO_M128 x0_1 = _mm_xor_si128(_mm_loadu_si128(x16_1), _mm_loadu_si128(y16_1)); + const LEO_M128 x1_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 1), _mm_loadu_si128(y16_1 + 1)); + const LEO_M128 x2_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 2), _mm_loadu_si128(y16_1 + 2)); + const LEO_M128 x3_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 3), _mm_loadu_si128(y16_1 + 3)); + const LEO_M128 x0_2 = _mm_xor_si128(_mm_loadu_si128(x16_2), _mm_loadu_si128(y16_2)); + const LEO_M128 x1_2 = _mm_xor_si128(_mm_loadu_si128(x16_2 + 1), _mm_loadu_si128(y16_2 + 1)); + const LEO_M128 x2_2 = _mm_xor_si128(_mm_loadu_si128(x16_2 + 2), _mm_loadu_si128(y16_2 + 2)); + const LEO_M128 x3_2 = _mm_xor_si128(_mm_loadu_si128(x16_2 + 3), _mm_loadu_si128(y16_2 + 3)); + _mm_storeu_si128(x16_0, x0_0); + _mm_storeu_si128(x16_0 + 1, x1_0); + _mm_storeu_si128(x16_0 + 2, x2_0); + _mm_storeu_si128(x16_0 + 3, x3_0); + _mm_storeu_si128(x16_1, x0_1); + _mm_storeu_si128(x16_1 + 1, x1_1); + _mm_storeu_si128(x16_1 + 2, x2_1); + _mm_storeu_si128(x16_1 + 3, x3_1); + _mm_storeu_si128(x16_2, x0_2); + _mm_storeu_si128(x16_2 + 1, x1_2); + _mm_storeu_si128(x16_2 + 2, x2_2); + _mm_storeu_si128(x16_2 + 3, x3_2); + x16_0 += 4, y16_0 += 4; + x16_1 += 4, y16_1 += 4; + x16_2 += 4, y16_2 += 4; + bytes -= 64; + } while (bytes > 0); } diff --git a/LeopardCommon.h b/LeopardCommon.h index 17425c0..a737304 100644 --- a/LeopardCommon.h +++ b/LeopardCommon.h @@ -30,42 +30,20 @@ /* TODO: - + Refactor software - + I think it should be split up into several C++ modules - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! + + Benchmarks for smaller data! + + New 16-bit Muladd inner loops + + Benchmarks for large data! + + Use parallel row ops + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation + + Write detailed comments for all the routines + + Final benchmarks! + Release version 1 + + Finish up documentation - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. + TBD: + + Look into getting EncodeL working so we can support smaller data (Ask Lin) + + Look into using FFT_m instead of FFT_n for decoder */ #include @@ -191,4 +169,57 @@ extern bool CpuHasSSSE3; #endif // LEO_TARGET_MOBILE +//------------------------------------------------------------------------------ +// Portable Intrinsics + +#ifdef _MSC_VER +#include +#endif + +// Returns highest bit index 0..31 where the first non-zero bit is found +// Precondition: x != 0 +LEO_FORCE_INLINE unsigned LastNonzeroBit32(unsigned x) +{ +#ifdef _MSC_VER + unsigned long index; + // Note: Ignoring result because x != 0 + _BitScanReverse(&index, (uint32_t)x); + return (unsigned)index; +#else + // Note: Ignoring return value of 0 because x != 0 + return 31 - (unsigned)__builtin_clzl(x); +#endif +} + +// Returns next power of two at or above given value +LEO_FORCE_INLINE unsigned NextPow2(unsigned n) +{ + return 2UL << LastNonzeroBit32(n - 1); +} + + +//------------------------------------------------------------------------------ +// XOR Memory +// +// This works for both 8-bit and 16-bit finite fields + +// x[] ^= y[] +void xor_mem( + void * LEO_RESTRICT x, const void * LEO_RESTRICT y, + unsigned bytes); + +// For i = {0, 1}: x_i[] ^= x_i[] +void xor_mem2( + void * LEO_RESTRICT x_0, const void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, const void * LEO_RESTRICT y_1, + unsigned bytes); + +// For i = {0, 1, 2}: x_i[] ^= x_i[] +void xor_mem3( + void * LEO_RESTRICT x_0, const void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, const void * LEO_RESTRICT y_1, + void * LEO_RESTRICT x_2, const void * LEO_RESTRICT y_2, + unsigned bytes); + + } // namespace leopard diff --git a/LeopardDecoder.cpp b/LeopardDecoder.cpp deleted file mode 100644 index 71d22e2..0000000 --- a/LeopardDecoder.cpp +++ /dev/null @@ -1,1220 +0,0 @@ -/* - Copyright (c) 2017 Christopher A. Taylor. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include -#include - - -/* - TODO: - + Write C API and unit tester - + Limit input to multiples of 64 bytes - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! - + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Refactor software - + Pick a name for the software better than LEO_RS - + I think it should be split up into several C++ modules - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! - + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation - + Release version 1 - - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. -*/ - - -//------------------------------------------------------------------------------ -// Debug - -// Some bugs only repro in release mode, so this can be helpful -//#define LEO_DEBUG_IN_RELEASE - -#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) - #define LEO_DEBUG - #ifdef _WIN32 - #define LEO_DEBUG_BREAK __debugbreak() - #else - #define LEO_DEBUG_BREAK __builtin_trap() - #endif - #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } -#else - #define LEO_DEBUG_BREAK ; - #define LEO_DEBUG_ASSERT(cond) ; -#endif - - -//------------------------------------------------------------------------------ -// Platform/Architecture - -#if defined(ANDROID) || defined(IOS) - #define LEO_TARGET_MOBILE -#endif // ANDROID - -#if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) - #define LEO_TRY_AVX2 /* 256-bit */ - #include - #define LEO_ALIGN_BYTES 32 -#else // __AVX2__ - #define LEO_ALIGN_BYTES 16 -#endif // __AVX2__ - -#if !defined(LEO_TARGET_MOBILE) - // Note: MSVC currently only supports SSSE3 but not AVX2 - #include // SSSE3: _mm_shuffle_epi8 - #include // SSE2 -#endif // LEO_TARGET_MOBILE - -#if defined(HAVE_ARM_NEON_H) - #include -#endif // HAVE_ARM_NEON_H - -#if defined(LEO_TARGET_MOBILE) - - #define LEO_ALIGNED_ACCESSES /* Inputs must be aligned to LEO_ALIGN_BYTES */ - -# if defined(HAVE_ARM_NEON_H) - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 uint8x16_t - #define LEO_TRY_NEON -#else - #define LEO_M128 uint64_t -# endif - -#else // LEO_TARGET_MOBILE - - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 __m128i - -#endif // LEO_TARGET_MOBILE - -#ifdef LEO_TRY_AVX2 - // Compiler-specific 256-bit SIMD register keyword - #define LEO_M256 __m256i -#endif - -// Compiler-specific C++11 restrict keyword -#define LEO_RESTRICT __restrict - -// Compiler-specific force inline keyword -#ifdef _MSC_VER - #define LEO_FORCE_INLINE inline __forceinline -#else - #define LEO_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -// Compiler-specific alignment keyword -// Note: Alignment only matters for ARM NEON where it should be 16 -#ifdef _MSC_VER - #define LEO_ALIGNED __declspec(align(LEO_ALIGN_BYTES)) -#else // _MSC_VER - #define LEO_ALIGNED __attribute__((aligned(LEO_ALIGN_BYTES))) -#endif // _MSC_VER - - -//------------------------------------------------------------------------------ -// Runtime CPU Architecture Check -// -// Feature checks stolen shamelessly from -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/runtime.c - -#if defined(HAVE_ANDROID_GETCPUFEATURES) - #include -#endif - -#if defined(LEO_TRY_NEON) -# if defined(IOS) && defined(__ARM_NEON__) - // Requires iPhone 5S or newer - static const bool CpuHasNeon = true; - static const bool CpuHasNeon64 = true; -# else - // Remember to add LOCAL_STATIC_LIBRARIES := cpufeatures - static bool CpuHasNeon = false; // V6 / V7 - static bool CpuHasNeon64 = false; // 64-bit -# endif -#endif - - -#if !defined(LEO_TARGET_MOBILE) - -#ifdef _MSC_VER - #include // __cpuid - #pragma warning(disable: 4752) // found Intel(R) Advanced Vector Extensions; consider using /arch:AVX -#endif - -#ifdef LEO_TRY_AVX2 -static bool CpuHasAVX2 = false; -#endif -static bool CpuHasSSSE3 = false; - -#define CPUID_EBX_AVX2 0x00000020 -#define CPUID_ECX_SSSE3 0x00000200 - -static void _cpuid(unsigned int cpu_info[4U], const unsigned int cpu_info_type) -{ -#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) - __cpuid((int *) cpu_info, cpu_info_type); -#else //if defined(HAVE_CPUID) - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; -# ifdef __i386__ - __asm__ __volatile__ ("pushfl; pushfl; " - "popl %0; " - "movl %0, %1; xorl %2, %0; " - "pushl %0; " - "popfl; pushfl; popl %0; popfl" : - "=&r" (cpu_info[0]), "=&r" (cpu_info[1]) : - "i" (0x200000)); - if (((cpu_info[0] ^ cpu_info[1]) & 0x200000) == 0) { - return; /* LCOV_EXCL_LINE */ - } -# endif -# ifdef __i386__ - __asm__ __volatile__ ("xchgl %%ebx, %k1; cpuid; xchgl %%ebx, %k1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# elif defined(__x86_64__) - __asm__ __volatile__ ("xchgq %%rbx, %q1; cpuid; xchgq %%rbx, %q1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# else - __asm__ __volatile__ ("cpuid" : - "=a" (cpu_info[0]), "=b" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# endif -#endif -} - -#endif // defined(LEO_TARGET_MOBILE) - - -static void leo_architecture_init() -{ -#if defined(LEO_TRY_NEON) && defined(HAVE_ANDROID_GETCPUFEATURES) - AndroidCpuFamily family = android_getCpuFamily(); - if (family == ANDROID_CPU_FAMILY_ARM) - { - if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) - CpuHasNeon = true; - } - else if (family == ANDROID_CPU_FAMILY_ARM64) - { - CpuHasNeon = true; - if (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) - CpuHasNeon64 = true; - } -#endif - -#if !defined(LEO_TARGET_MOBILE) - unsigned int cpu_info[4]; - - _cpuid(cpu_info, 1); - CpuHasSSSE3 = ((cpu_info[2] & CPUID_ECX_SSSE3) != 0); - -#if defined(LEO_TRY_AVX2) - _cpuid(cpu_info, 7); - CpuHasAVX2 = ((cpu_info[1] & CPUID_EBX_AVX2) != 0); -#endif // LEO_TRY_AVX2 - -#endif // LEO_TARGET_MOBILE -} - - -//------------------------------------------------------------------------------ -// SIMD-Safe Aligned Memory Allocations - -static const unsigned kAlignmentBytes = LEO_ALIGN_BYTES; - -LEO_FORCE_INLINE unsigned NextAlignedOffset(unsigned offset) -{ - return (offset + kAlignmentBytes - 1) & ~(kAlignmentBytes - 1); -} - -static LEO_FORCE_INLINE uint8_t* SIMDSafeAllocate(size_t size) -{ - uint8_t* data = (uint8_t*)calloc(1, kAlignmentBytes + size); - if (!data) - return nullptr; - unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); - data += kAlignmentBytes - offset; - data[-1] = (uint8_t)offset; - return data; -} - -static LEO_FORCE_INLINE void SIMDSafeFree(void* ptr) -{ - if (!ptr) - return; - uint8_t* data = (uint8_t*)ptr; - unsigned offset = data[-1]; - if (offset >= kAlignmentBytes) - { - LEO_DEBUG_BREAK; // Should never happen - return; - } - data -= kAlignmentBytes - offset; - free(data); -} - - -//------------------------------------------------------------------------------ -// Field - -//#define LEO_SHORT_FIELD - -#ifdef LEO_SHORT_FIELD -typedef uint8_t GFSymbol; -static const unsigned kGFBits = 8; -static const unsigned kGFPolynomial = 0x11D; -GFSymbol kGFBasis[kGFBits] = { - 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis -}; -#else -typedef uint16_t GFSymbol; -static const unsigned kGFBits = 16; -static const unsigned kGFPolynomial = 0x1002D; -GFSymbol kGFBasis[kGFBits] = { - 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis - 0xC582, 0xED2E, 0x914C, 0x4012, - 0x6C98, 0x10D8, 0x6A72, 0xB900, - 0xFDB8, 0xFB34, 0xFF38, 0x991E -}; -#endif - -/* - Cantor Basis introduced by: - D. G. Cantor, "On arithmetical algorithms over finite fields", - Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. -*/ - -static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size -static const unsigned kFieldModulus = kFieldSize - 1; - -static GFSymbol GFLog[kFieldSize]; -static GFSymbol GFExp[kFieldSize]; - -// Initialize GFLog[], GFExp[] -static void InitField() -{ - unsigned state = 1; - for (unsigned i = 0; i < kFieldModulus; ++i) - { - GFExp[state] = static_cast(i); - state <<= 1; - if (state >= kFieldSize) - state ^= kGFPolynomial; - } - GFExp[0] = kFieldModulus; - - // Conversion to chosen basis: - - GFLog[0] = 0; - for (unsigned i = 0; i < kGFBits; ++i) - { - const GFSymbol basis = kGFBasis[i]; - const unsigned width = (unsigned)(1UL << i); - - for (unsigned j = 0; j < width; ++j) - GFLog[j + width] = GFLog[j] ^ basis; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - GFLog[i] = GFExp[GFLog[i]]; - - for (unsigned i = 0; i < kFieldSize; ++i) - GFExp[GFLog[i]] = i; - - GFExp[kFieldModulus] = GFExp[0]; -} - - -//------------------------------------------------------------------------------ -// Mod Q Field Operations -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// z = x + y (mod Q) -static inline GFSymbol AddModQ(GFSymbol a, GFSymbol b) -{ - const unsigned sum = (unsigned)a + b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(sum + (sum >> kGFBits)); -} - -// z = x - y (mod Q) -static inline GFSymbol SubModQ(GFSymbol a, GFSymbol b) -{ - const unsigned dif = (unsigned)a - b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(dif + (dif >> kGFBits)); -} - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; -} - - -//------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; - -// {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) -{ - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); - a = sum; - b = dif; -} - -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) -{ - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; -} - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) -{ - unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - unsigned y = 0; - data[y] = t0; y += s; - data[y] = t1; y += s; - data[y] = t2; y += s; - data[y] = t3; -} - -static inline void FWHT_8(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; -} - -static inline void FWHT_16(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t8, t9); - FWHT_2(t10, t11); - FWHT_2(t12, t13); - FWHT_2(t14, t15); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t8, t10); - FWHT_2(t9, t11); - FWHT_2(t12, t14); - FWHT_2(t13, t15); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - FWHT_2(t8, t12); - FWHT_2(t9, t13); - FWHT_2(t10, t14); - FWHT_2(t11, t15); - FWHT_2(t0, t8); - FWHT_2(t1, t9); - FWHT_2(t2, t10); - FWHT_2(t3, t11); - FWHT_2(t4, t12); - FWHT_2(t5, t13); - FWHT_2(t6, t14); - FWHT_2(t7, t15); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; - data[8] = t8; - data[9] = t9; - data[10] = t10; - data[11] = t11; - data[12] = t12; - data[13] = t13; - data[14] = t14; - data[15] = t15; -} - -static void FWHT_SmallData(fwht_t* data, unsigned ldn) -{ - const unsigned n = (1UL << ldn); - - if (n <= 2) - { - if (n == 2) - FWHT_2(data[0], data[1]); - return; - } - - for (unsigned ldm = ldn; ldm > 3; ldm -= 2) - { - unsigned m = (1UL << ldm); - unsigned m4 = (m >> 2); - for (unsigned r = 0; r < n; r += m) - for (unsigned j = 0; j < m4; j++) - FWHT_4(data + j + r, m4); - } - - if (ldn & 1) - { - for (unsigned i0 = 0; i0 < n; i0 += 8) - FWHT_8(data + i0); - } - else - { - for (unsigned i0 = 0; i0 < n; i0 += 4) - FWHT_4(data + i0); - } -} - -// Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) -{ - if (ldn <= 13) - { - FWHT_SmallData(data, ldn); - return; - } - - FWHT_2(data[2], data[3]); - FWHT_4(data + 4); - FWHT_8(data + 8); - FWHT_16(data + 16); - for (unsigned ldm = 5; ldm < ldn; ++ldm) - FWHT(data + (unsigned)(1UL << ldm), ldm); - - for (unsigned ldm = 0; ldm < ldn; ++ldm) - { - const unsigned mh = (1UL << ldm); - for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2) - FWHT_2(data[t1], data[t2]); - } -} - -#endif - - -//------------------------------------------------------------------------------ -// Memory Buffer XOR - -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) -{ - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) - { - while (bytes >= 64) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; - } - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; - } - } - else -# endif // LEO_TRY_NEON - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); - } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) - { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); - - while (bytes >= 128) - { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); - - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - - bytes -= 128, x32 += 4, y32 += 4; - } - - // Handle multiples of 32 bytes - while (bytes >= 32) - { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; - } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); - } - else -# endif // LEO_TRY_AVX2 - { - while (bytes >= 64) - { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); - - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - - bytes -= 64, x16 += 4, y16 += 4; - } - } -#endif // LEO_TARGET_MOBILE - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); - - bytes -= 16, ++x16, ++y16; - } - - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); - - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } - - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } - - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } -} - - -//------------------------------------------------------------------------------ -// Formal Derivative - -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) -{ - for (unsigned i = 1; i < size; ++i) - { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; - - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; - } - - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); -} - - -//------------------------------------------------------------------------------ -// Fast Fourier Transform - -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } -} - -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - } - } -} - - -//------------------------------------------------------------------------------ -// FFT Initialization - -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial - -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() -{ - GFSymbol temp[kGFBits - 1]; - - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); - - for (unsigned m = 0; m < (kGFBits - 1); ++m) - { - const unsigned step = (unsigned)1 << (m + 1); - - skewVec[((unsigned)1 << m) - 1] = 0; - - for (unsigned i = m; i < (kGFBits - 1); ++i) - { - const unsigned s = ((unsigned)1 << (i + 1)); - - for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; - } - - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; - - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); - } - - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; - - temp[0] = kFieldModulus - temp[0]; - - for (unsigned i = 1; i < (kGFBits - 1); ++i) - temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); - - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); -} - - -//------------------------------------------------------------------------------ -// Encoder - -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) -{ - memcpy(codeword, data, sizeof(GFSymbol) * k); - - IFLT(codeword, k, 0); - - for (unsigned i = k; i < kFieldSize; i += k) - { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) - { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } - - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) - { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; - } - } - } - - //printf("Decoding is successful!\n"); -} - - -//------------------------------------------------------------------------------ -// Entrypoint - -int main(int argc, char **argv) -{ - // Initialize architecture-specific code - leo_architecture_init(); - - // Fill GFLog table and GFExp table - InitField(); - - // Compute factors used in erasure decoder - InitFieldOperations(); - - unsigned seed = (unsigned)time(NULL); - for (;;) - { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); - - ++seed; - } - - return 0; -} diff --git a/LeopardDecoder.h b/LeopardDecoder.h deleted file mode 100644 index 71d22e2..0000000 --- a/LeopardDecoder.h +++ /dev/null @@ -1,1220 +0,0 @@ -/* - Copyright (c) 2017 Christopher A. Taylor. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include -#include - - -/* - TODO: - + Write C API and unit tester - + Limit input to multiples of 64 bytes - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! - + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Refactor software - + Pick a name for the software better than LEO_RS - + I think it should be split up into several C++ modules - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! - + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation - + Release version 1 - - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. -*/ - - -//------------------------------------------------------------------------------ -// Debug - -// Some bugs only repro in release mode, so this can be helpful -//#define LEO_DEBUG_IN_RELEASE - -#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) - #define LEO_DEBUG - #ifdef _WIN32 - #define LEO_DEBUG_BREAK __debugbreak() - #else - #define LEO_DEBUG_BREAK __builtin_trap() - #endif - #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } -#else - #define LEO_DEBUG_BREAK ; - #define LEO_DEBUG_ASSERT(cond) ; -#endif - - -//------------------------------------------------------------------------------ -// Platform/Architecture - -#if defined(ANDROID) || defined(IOS) - #define LEO_TARGET_MOBILE -#endif // ANDROID - -#if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) - #define LEO_TRY_AVX2 /* 256-bit */ - #include - #define LEO_ALIGN_BYTES 32 -#else // __AVX2__ - #define LEO_ALIGN_BYTES 16 -#endif // __AVX2__ - -#if !defined(LEO_TARGET_MOBILE) - // Note: MSVC currently only supports SSSE3 but not AVX2 - #include // SSSE3: _mm_shuffle_epi8 - #include // SSE2 -#endif // LEO_TARGET_MOBILE - -#if defined(HAVE_ARM_NEON_H) - #include -#endif // HAVE_ARM_NEON_H - -#if defined(LEO_TARGET_MOBILE) - - #define LEO_ALIGNED_ACCESSES /* Inputs must be aligned to LEO_ALIGN_BYTES */ - -# if defined(HAVE_ARM_NEON_H) - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 uint8x16_t - #define LEO_TRY_NEON -#else - #define LEO_M128 uint64_t -# endif - -#else // LEO_TARGET_MOBILE - - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 __m128i - -#endif // LEO_TARGET_MOBILE - -#ifdef LEO_TRY_AVX2 - // Compiler-specific 256-bit SIMD register keyword - #define LEO_M256 __m256i -#endif - -// Compiler-specific C++11 restrict keyword -#define LEO_RESTRICT __restrict - -// Compiler-specific force inline keyword -#ifdef _MSC_VER - #define LEO_FORCE_INLINE inline __forceinline -#else - #define LEO_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -// Compiler-specific alignment keyword -// Note: Alignment only matters for ARM NEON where it should be 16 -#ifdef _MSC_VER - #define LEO_ALIGNED __declspec(align(LEO_ALIGN_BYTES)) -#else // _MSC_VER - #define LEO_ALIGNED __attribute__((aligned(LEO_ALIGN_BYTES))) -#endif // _MSC_VER - - -//------------------------------------------------------------------------------ -// Runtime CPU Architecture Check -// -// Feature checks stolen shamelessly from -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/runtime.c - -#if defined(HAVE_ANDROID_GETCPUFEATURES) - #include -#endif - -#if defined(LEO_TRY_NEON) -# if defined(IOS) && defined(__ARM_NEON__) - // Requires iPhone 5S or newer - static const bool CpuHasNeon = true; - static const bool CpuHasNeon64 = true; -# else - // Remember to add LOCAL_STATIC_LIBRARIES := cpufeatures - static bool CpuHasNeon = false; // V6 / V7 - static bool CpuHasNeon64 = false; // 64-bit -# endif -#endif - - -#if !defined(LEO_TARGET_MOBILE) - -#ifdef _MSC_VER - #include // __cpuid - #pragma warning(disable: 4752) // found Intel(R) Advanced Vector Extensions; consider using /arch:AVX -#endif - -#ifdef LEO_TRY_AVX2 -static bool CpuHasAVX2 = false; -#endif -static bool CpuHasSSSE3 = false; - -#define CPUID_EBX_AVX2 0x00000020 -#define CPUID_ECX_SSSE3 0x00000200 - -static void _cpuid(unsigned int cpu_info[4U], const unsigned int cpu_info_type) -{ -#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) - __cpuid((int *) cpu_info, cpu_info_type); -#else //if defined(HAVE_CPUID) - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; -# ifdef __i386__ - __asm__ __volatile__ ("pushfl; pushfl; " - "popl %0; " - "movl %0, %1; xorl %2, %0; " - "pushl %0; " - "popfl; pushfl; popl %0; popfl" : - "=&r" (cpu_info[0]), "=&r" (cpu_info[1]) : - "i" (0x200000)); - if (((cpu_info[0] ^ cpu_info[1]) & 0x200000) == 0) { - return; /* LCOV_EXCL_LINE */ - } -# endif -# ifdef __i386__ - __asm__ __volatile__ ("xchgl %%ebx, %k1; cpuid; xchgl %%ebx, %k1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# elif defined(__x86_64__) - __asm__ __volatile__ ("xchgq %%rbx, %q1; cpuid; xchgq %%rbx, %q1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# else - __asm__ __volatile__ ("cpuid" : - "=a" (cpu_info[0]), "=b" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# endif -#endif -} - -#endif // defined(LEO_TARGET_MOBILE) - - -static void leo_architecture_init() -{ -#if defined(LEO_TRY_NEON) && defined(HAVE_ANDROID_GETCPUFEATURES) - AndroidCpuFamily family = android_getCpuFamily(); - if (family == ANDROID_CPU_FAMILY_ARM) - { - if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) - CpuHasNeon = true; - } - else if (family == ANDROID_CPU_FAMILY_ARM64) - { - CpuHasNeon = true; - if (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) - CpuHasNeon64 = true; - } -#endif - -#if !defined(LEO_TARGET_MOBILE) - unsigned int cpu_info[4]; - - _cpuid(cpu_info, 1); - CpuHasSSSE3 = ((cpu_info[2] & CPUID_ECX_SSSE3) != 0); - -#if defined(LEO_TRY_AVX2) - _cpuid(cpu_info, 7); - CpuHasAVX2 = ((cpu_info[1] & CPUID_EBX_AVX2) != 0); -#endif // LEO_TRY_AVX2 - -#endif // LEO_TARGET_MOBILE -} - - -//------------------------------------------------------------------------------ -// SIMD-Safe Aligned Memory Allocations - -static const unsigned kAlignmentBytes = LEO_ALIGN_BYTES; - -LEO_FORCE_INLINE unsigned NextAlignedOffset(unsigned offset) -{ - return (offset + kAlignmentBytes - 1) & ~(kAlignmentBytes - 1); -} - -static LEO_FORCE_INLINE uint8_t* SIMDSafeAllocate(size_t size) -{ - uint8_t* data = (uint8_t*)calloc(1, kAlignmentBytes + size); - if (!data) - return nullptr; - unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); - data += kAlignmentBytes - offset; - data[-1] = (uint8_t)offset; - return data; -} - -static LEO_FORCE_INLINE void SIMDSafeFree(void* ptr) -{ - if (!ptr) - return; - uint8_t* data = (uint8_t*)ptr; - unsigned offset = data[-1]; - if (offset >= kAlignmentBytes) - { - LEO_DEBUG_BREAK; // Should never happen - return; - } - data -= kAlignmentBytes - offset; - free(data); -} - - -//------------------------------------------------------------------------------ -// Field - -//#define LEO_SHORT_FIELD - -#ifdef LEO_SHORT_FIELD -typedef uint8_t GFSymbol; -static const unsigned kGFBits = 8; -static const unsigned kGFPolynomial = 0x11D; -GFSymbol kGFBasis[kGFBits] = { - 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis -}; -#else -typedef uint16_t GFSymbol; -static const unsigned kGFBits = 16; -static const unsigned kGFPolynomial = 0x1002D; -GFSymbol kGFBasis[kGFBits] = { - 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis - 0xC582, 0xED2E, 0x914C, 0x4012, - 0x6C98, 0x10D8, 0x6A72, 0xB900, - 0xFDB8, 0xFB34, 0xFF38, 0x991E -}; -#endif - -/* - Cantor Basis introduced by: - D. G. Cantor, "On arithmetical algorithms over finite fields", - Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. -*/ - -static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size -static const unsigned kFieldModulus = kFieldSize - 1; - -static GFSymbol GFLog[kFieldSize]; -static GFSymbol GFExp[kFieldSize]; - -// Initialize GFLog[], GFExp[] -static void InitField() -{ - unsigned state = 1; - for (unsigned i = 0; i < kFieldModulus; ++i) - { - GFExp[state] = static_cast(i); - state <<= 1; - if (state >= kFieldSize) - state ^= kGFPolynomial; - } - GFExp[0] = kFieldModulus; - - // Conversion to chosen basis: - - GFLog[0] = 0; - for (unsigned i = 0; i < kGFBits; ++i) - { - const GFSymbol basis = kGFBasis[i]; - const unsigned width = (unsigned)(1UL << i); - - for (unsigned j = 0; j < width; ++j) - GFLog[j + width] = GFLog[j] ^ basis; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - GFLog[i] = GFExp[GFLog[i]]; - - for (unsigned i = 0; i < kFieldSize; ++i) - GFExp[GFLog[i]] = i; - - GFExp[kFieldModulus] = GFExp[0]; -} - - -//------------------------------------------------------------------------------ -// Mod Q Field Operations -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// z = x + y (mod Q) -static inline GFSymbol AddModQ(GFSymbol a, GFSymbol b) -{ - const unsigned sum = (unsigned)a + b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(sum + (sum >> kGFBits)); -} - -// z = x - y (mod Q) -static inline GFSymbol SubModQ(GFSymbol a, GFSymbol b) -{ - const unsigned dif = (unsigned)a - b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(dif + (dif >> kGFBits)); -} - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; -} - - -//------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; - -// {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) -{ - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); - a = sum; - b = dif; -} - -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) -{ - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; -} - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) -{ - unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - unsigned y = 0; - data[y] = t0; y += s; - data[y] = t1; y += s; - data[y] = t2; y += s; - data[y] = t3; -} - -static inline void FWHT_8(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; -} - -static inline void FWHT_16(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t8, t9); - FWHT_2(t10, t11); - FWHT_2(t12, t13); - FWHT_2(t14, t15); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t8, t10); - FWHT_2(t9, t11); - FWHT_2(t12, t14); - FWHT_2(t13, t15); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - FWHT_2(t8, t12); - FWHT_2(t9, t13); - FWHT_2(t10, t14); - FWHT_2(t11, t15); - FWHT_2(t0, t8); - FWHT_2(t1, t9); - FWHT_2(t2, t10); - FWHT_2(t3, t11); - FWHT_2(t4, t12); - FWHT_2(t5, t13); - FWHT_2(t6, t14); - FWHT_2(t7, t15); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; - data[8] = t8; - data[9] = t9; - data[10] = t10; - data[11] = t11; - data[12] = t12; - data[13] = t13; - data[14] = t14; - data[15] = t15; -} - -static void FWHT_SmallData(fwht_t* data, unsigned ldn) -{ - const unsigned n = (1UL << ldn); - - if (n <= 2) - { - if (n == 2) - FWHT_2(data[0], data[1]); - return; - } - - for (unsigned ldm = ldn; ldm > 3; ldm -= 2) - { - unsigned m = (1UL << ldm); - unsigned m4 = (m >> 2); - for (unsigned r = 0; r < n; r += m) - for (unsigned j = 0; j < m4; j++) - FWHT_4(data + j + r, m4); - } - - if (ldn & 1) - { - for (unsigned i0 = 0; i0 < n; i0 += 8) - FWHT_8(data + i0); - } - else - { - for (unsigned i0 = 0; i0 < n; i0 += 4) - FWHT_4(data + i0); - } -} - -// Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) -{ - if (ldn <= 13) - { - FWHT_SmallData(data, ldn); - return; - } - - FWHT_2(data[2], data[3]); - FWHT_4(data + 4); - FWHT_8(data + 8); - FWHT_16(data + 16); - for (unsigned ldm = 5; ldm < ldn; ++ldm) - FWHT(data + (unsigned)(1UL << ldm), ldm); - - for (unsigned ldm = 0; ldm < ldn; ++ldm) - { - const unsigned mh = (1UL << ldm); - for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2) - FWHT_2(data[t1], data[t2]); - } -} - -#endif - - -//------------------------------------------------------------------------------ -// Memory Buffer XOR - -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) -{ - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) - { - while (bytes >= 64) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; - } - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; - } - } - else -# endif // LEO_TRY_NEON - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); - } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) - { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); - - while (bytes >= 128) - { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); - - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - - bytes -= 128, x32 += 4, y32 += 4; - } - - // Handle multiples of 32 bytes - while (bytes >= 32) - { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; - } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); - } - else -# endif // LEO_TRY_AVX2 - { - while (bytes >= 64) - { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); - - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - - bytes -= 64, x16 += 4, y16 += 4; - } - } -#endif // LEO_TARGET_MOBILE - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); - - bytes -= 16, ++x16, ++y16; - } - - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); - - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } - - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } - - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } -} - - -//------------------------------------------------------------------------------ -// Formal Derivative - -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) -{ - for (unsigned i = 1; i < size; ++i) - { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; - - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; - } - - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); -} - - -//------------------------------------------------------------------------------ -// Fast Fourier Transform - -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } -} - -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - } - } -} - - -//------------------------------------------------------------------------------ -// FFT Initialization - -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial - -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() -{ - GFSymbol temp[kGFBits - 1]; - - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); - - for (unsigned m = 0; m < (kGFBits - 1); ++m) - { - const unsigned step = (unsigned)1 << (m + 1); - - skewVec[((unsigned)1 << m) - 1] = 0; - - for (unsigned i = m; i < (kGFBits - 1); ++i) - { - const unsigned s = ((unsigned)1 << (i + 1)); - - for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; - } - - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; - - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); - } - - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; - - temp[0] = kFieldModulus - temp[0]; - - for (unsigned i = 1; i < (kGFBits - 1); ++i) - temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); - - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); -} - - -//------------------------------------------------------------------------------ -// Encoder - -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) -{ - memcpy(codeword, data, sizeof(GFSymbol) * k); - - IFLT(codeword, k, 0); - - for (unsigned i = k; i < kFieldSize; i += k) - { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) - { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } - - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) - { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; - } - } - } - - //printf("Decoding is successful!\n"); -} - - -//------------------------------------------------------------------------------ -// Entrypoint - -int main(int argc, char **argv) -{ - // Initialize architecture-specific code - leo_architecture_init(); - - // Fill GFLog table and GFExp table - InitField(); - - // Compute factors used in erasure decoder - InitFieldOperations(); - - unsigned seed = (unsigned)time(NULL); - for (;;) - { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); - - ++seed; - } - - return 0; -} diff --git a/LeopardEncoder.cpp b/LeopardEncoder.cpp deleted file mode 100644 index 71d22e2..0000000 --- a/LeopardEncoder.cpp +++ /dev/null @@ -1,1220 +0,0 @@ -/* - Copyright (c) 2017 Christopher A. Taylor. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include -#include - - -/* - TODO: - + Write C API and unit tester - + Limit input to multiples of 64 bytes - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! - + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Refactor software - + Pick a name for the software better than LEO_RS - + I think it should be split up into several C++ modules - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! - + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation - + Release version 1 - - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. -*/ - - -//------------------------------------------------------------------------------ -// Debug - -// Some bugs only repro in release mode, so this can be helpful -//#define LEO_DEBUG_IN_RELEASE - -#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) - #define LEO_DEBUG - #ifdef _WIN32 - #define LEO_DEBUG_BREAK __debugbreak() - #else - #define LEO_DEBUG_BREAK __builtin_trap() - #endif - #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } -#else - #define LEO_DEBUG_BREAK ; - #define LEO_DEBUG_ASSERT(cond) ; -#endif - - -//------------------------------------------------------------------------------ -// Platform/Architecture - -#if defined(ANDROID) || defined(IOS) - #define LEO_TARGET_MOBILE -#endif // ANDROID - -#if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) - #define LEO_TRY_AVX2 /* 256-bit */ - #include - #define LEO_ALIGN_BYTES 32 -#else // __AVX2__ - #define LEO_ALIGN_BYTES 16 -#endif // __AVX2__ - -#if !defined(LEO_TARGET_MOBILE) - // Note: MSVC currently only supports SSSE3 but not AVX2 - #include // SSSE3: _mm_shuffle_epi8 - #include // SSE2 -#endif // LEO_TARGET_MOBILE - -#if defined(HAVE_ARM_NEON_H) - #include -#endif // HAVE_ARM_NEON_H - -#if defined(LEO_TARGET_MOBILE) - - #define LEO_ALIGNED_ACCESSES /* Inputs must be aligned to LEO_ALIGN_BYTES */ - -# if defined(HAVE_ARM_NEON_H) - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 uint8x16_t - #define LEO_TRY_NEON -#else - #define LEO_M128 uint64_t -# endif - -#else // LEO_TARGET_MOBILE - - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 __m128i - -#endif // LEO_TARGET_MOBILE - -#ifdef LEO_TRY_AVX2 - // Compiler-specific 256-bit SIMD register keyword - #define LEO_M256 __m256i -#endif - -// Compiler-specific C++11 restrict keyword -#define LEO_RESTRICT __restrict - -// Compiler-specific force inline keyword -#ifdef _MSC_VER - #define LEO_FORCE_INLINE inline __forceinline -#else - #define LEO_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -// Compiler-specific alignment keyword -// Note: Alignment only matters for ARM NEON where it should be 16 -#ifdef _MSC_VER - #define LEO_ALIGNED __declspec(align(LEO_ALIGN_BYTES)) -#else // _MSC_VER - #define LEO_ALIGNED __attribute__((aligned(LEO_ALIGN_BYTES))) -#endif // _MSC_VER - - -//------------------------------------------------------------------------------ -// Runtime CPU Architecture Check -// -// Feature checks stolen shamelessly from -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/runtime.c - -#if defined(HAVE_ANDROID_GETCPUFEATURES) - #include -#endif - -#if defined(LEO_TRY_NEON) -# if defined(IOS) && defined(__ARM_NEON__) - // Requires iPhone 5S or newer - static const bool CpuHasNeon = true; - static const bool CpuHasNeon64 = true; -# else - // Remember to add LOCAL_STATIC_LIBRARIES := cpufeatures - static bool CpuHasNeon = false; // V6 / V7 - static bool CpuHasNeon64 = false; // 64-bit -# endif -#endif - - -#if !defined(LEO_TARGET_MOBILE) - -#ifdef _MSC_VER - #include // __cpuid - #pragma warning(disable: 4752) // found Intel(R) Advanced Vector Extensions; consider using /arch:AVX -#endif - -#ifdef LEO_TRY_AVX2 -static bool CpuHasAVX2 = false; -#endif -static bool CpuHasSSSE3 = false; - -#define CPUID_EBX_AVX2 0x00000020 -#define CPUID_ECX_SSSE3 0x00000200 - -static void _cpuid(unsigned int cpu_info[4U], const unsigned int cpu_info_type) -{ -#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) - __cpuid((int *) cpu_info, cpu_info_type); -#else //if defined(HAVE_CPUID) - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; -# ifdef __i386__ - __asm__ __volatile__ ("pushfl; pushfl; " - "popl %0; " - "movl %0, %1; xorl %2, %0; " - "pushl %0; " - "popfl; pushfl; popl %0; popfl" : - "=&r" (cpu_info[0]), "=&r" (cpu_info[1]) : - "i" (0x200000)); - if (((cpu_info[0] ^ cpu_info[1]) & 0x200000) == 0) { - return; /* LCOV_EXCL_LINE */ - } -# endif -# ifdef __i386__ - __asm__ __volatile__ ("xchgl %%ebx, %k1; cpuid; xchgl %%ebx, %k1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# elif defined(__x86_64__) - __asm__ __volatile__ ("xchgq %%rbx, %q1; cpuid; xchgq %%rbx, %q1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# else - __asm__ __volatile__ ("cpuid" : - "=a" (cpu_info[0]), "=b" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# endif -#endif -} - -#endif // defined(LEO_TARGET_MOBILE) - - -static void leo_architecture_init() -{ -#if defined(LEO_TRY_NEON) && defined(HAVE_ANDROID_GETCPUFEATURES) - AndroidCpuFamily family = android_getCpuFamily(); - if (family == ANDROID_CPU_FAMILY_ARM) - { - if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) - CpuHasNeon = true; - } - else if (family == ANDROID_CPU_FAMILY_ARM64) - { - CpuHasNeon = true; - if (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) - CpuHasNeon64 = true; - } -#endif - -#if !defined(LEO_TARGET_MOBILE) - unsigned int cpu_info[4]; - - _cpuid(cpu_info, 1); - CpuHasSSSE3 = ((cpu_info[2] & CPUID_ECX_SSSE3) != 0); - -#if defined(LEO_TRY_AVX2) - _cpuid(cpu_info, 7); - CpuHasAVX2 = ((cpu_info[1] & CPUID_EBX_AVX2) != 0); -#endif // LEO_TRY_AVX2 - -#endif // LEO_TARGET_MOBILE -} - - -//------------------------------------------------------------------------------ -// SIMD-Safe Aligned Memory Allocations - -static const unsigned kAlignmentBytes = LEO_ALIGN_BYTES; - -LEO_FORCE_INLINE unsigned NextAlignedOffset(unsigned offset) -{ - return (offset + kAlignmentBytes - 1) & ~(kAlignmentBytes - 1); -} - -static LEO_FORCE_INLINE uint8_t* SIMDSafeAllocate(size_t size) -{ - uint8_t* data = (uint8_t*)calloc(1, kAlignmentBytes + size); - if (!data) - return nullptr; - unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); - data += kAlignmentBytes - offset; - data[-1] = (uint8_t)offset; - return data; -} - -static LEO_FORCE_INLINE void SIMDSafeFree(void* ptr) -{ - if (!ptr) - return; - uint8_t* data = (uint8_t*)ptr; - unsigned offset = data[-1]; - if (offset >= kAlignmentBytes) - { - LEO_DEBUG_BREAK; // Should never happen - return; - } - data -= kAlignmentBytes - offset; - free(data); -} - - -//------------------------------------------------------------------------------ -// Field - -//#define LEO_SHORT_FIELD - -#ifdef LEO_SHORT_FIELD -typedef uint8_t GFSymbol; -static const unsigned kGFBits = 8; -static const unsigned kGFPolynomial = 0x11D; -GFSymbol kGFBasis[kGFBits] = { - 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis -}; -#else -typedef uint16_t GFSymbol; -static const unsigned kGFBits = 16; -static const unsigned kGFPolynomial = 0x1002D; -GFSymbol kGFBasis[kGFBits] = { - 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis - 0xC582, 0xED2E, 0x914C, 0x4012, - 0x6C98, 0x10D8, 0x6A72, 0xB900, - 0xFDB8, 0xFB34, 0xFF38, 0x991E -}; -#endif - -/* - Cantor Basis introduced by: - D. G. Cantor, "On arithmetical algorithms over finite fields", - Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. -*/ - -static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size -static const unsigned kFieldModulus = kFieldSize - 1; - -static GFSymbol GFLog[kFieldSize]; -static GFSymbol GFExp[kFieldSize]; - -// Initialize GFLog[], GFExp[] -static void InitField() -{ - unsigned state = 1; - for (unsigned i = 0; i < kFieldModulus; ++i) - { - GFExp[state] = static_cast(i); - state <<= 1; - if (state >= kFieldSize) - state ^= kGFPolynomial; - } - GFExp[0] = kFieldModulus; - - // Conversion to chosen basis: - - GFLog[0] = 0; - for (unsigned i = 0; i < kGFBits; ++i) - { - const GFSymbol basis = kGFBasis[i]; - const unsigned width = (unsigned)(1UL << i); - - for (unsigned j = 0; j < width; ++j) - GFLog[j + width] = GFLog[j] ^ basis; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - GFLog[i] = GFExp[GFLog[i]]; - - for (unsigned i = 0; i < kFieldSize; ++i) - GFExp[GFLog[i]] = i; - - GFExp[kFieldModulus] = GFExp[0]; -} - - -//------------------------------------------------------------------------------ -// Mod Q Field Operations -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// z = x + y (mod Q) -static inline GFSymbol AddModQ(GFSymbol a, GFSymbol b) -{ - const unsigned sum = (unsigned)a + b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(sum + (sum >> kGFBits)); -} - -// z = x - y (mod Q) -static inline GFSymbol SubModQ(GFSymbol a, GFSymbol b) -{ - const unsigned dif = (unsigned)a - b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(dif + (dif >> kGFBits)); -} - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; -} - - -//------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; - -// {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) -{ - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); - a = sum; - b = dif; -} - -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) -{ - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; -} - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) -{ - unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - unsigned y = 0; - data[y] = t0; y += s; - data[y] = t1; y += s; - data[y] = t2; y += s; - data[y] = t3; -} - -static inline void FWHT_8(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; -} - -static inline void FWHT_16(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t8, t9); - FWHT_2(t10, t11); - FWHT_2(t12, t13); - FWHT_2(t14, t15); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t8, t10); - FWHT_2(t9, t11); - FWHT_2(t12, t14); - FWHT_2(t13, t15); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - FWHT_2(t8, t12); - FWHT_2(t9, t13); - FWHT_2(t10, t14); - FWHT_2(t11, t15); - FWHT_2(t0, t8); - FWHT_2(t1, t9); - FWHT_2(t2, t10); - FWHT_2(t3, t11); - FWHT_2(t4, t12); - FWHT_2(t5, t13); - FWHT_2(t6, t14); - FWHT_2(t7, t15); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; - data[8] = t8; - data[9] = t9; - data[10] = t10; - data[11] = t11; - data[12] = t12; - data[13] = t13; - data[14] = t14; - data[15] = t15; -} - -static void FWHT_SmallData(fwht_t* data, unsigned ldn) -{ - const unsigned n = (1UL << ldn); - - if (n <= 2) - { - if (n == 2) - FWHT_2(data[0], data[1]); - return; - } - - for (unsigned ldm = ldn; ldm > 3; ldm -= 2) - { - unsigned m = (1UL << ldm); - unsigned m4 = (m >> 2); - for (unsigned r = 0; r < n; r += m) - for (unsigned j = 0; j < m4; j++) - FWHT_4(data + j + r, m4); - } - - if (ldn & 1) - { - for (unsigned i0 = 0; i0 < n; i0 += 8) - FWHT_8(data + i0); - } - else - { - for (unsigned i0 = 0; i0 < n; i0 += 4) - FWHT_4(data + i0); - } -} - -// Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) -{ - if (ldn <= 13) - { - FWHT_SmallData(data, ldn); - return; - } - - FWHT_2(data[2], data[3]); - FWHT_4(data + 4); - FWHT_8(data + 8); - FWHT_16(data + 16); - for (unsigned ldm = 5; ldm < ldn; ++ldm) - FWHT(data + (unsigned)(1UL << ldm), ldm); - - for (unsigned ldm = 0; ldm < ldn; ++ldm) - { - const unsigned mh = (1UL << ldm); - for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2) - FWHT_2(data[t1], data[t2]); - } -} - -#endif - - -//------------------------------------------------------------------------------ -// Memory Buffer XOR - -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) -{ - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) - { - while (bytes >= 64) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; - } - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; - } - } - else -# endif // LEO_TRY_NEON - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); - } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) - { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); - - while (bytes >= 128) - { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); - - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - - bytes -= 128, x32 += 4, y32 += 4; - } - - // Handle multiples of 32 bytes - while (bytes >= 32) - { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; - } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); - } - else -# endif // LEO_TRY_AVX2 - { - while (bytes >= 64) - { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); - - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - - bytes -= 64, x16 += 4, y16 += 4; - } - } -#endif // LEO_TARGET_MOBILE - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); - - bytes -= 16, ++x16, ++y16; - } - - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); - - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } - - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } - - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } -} - - -//------------------------------------------------------------------------------ -// Formal Derivative - -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) -{ - for (unsigned i = 1; i < size; ++i) - { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; - - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; - } - - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); -} - - -//------------------------------------------------------------------------------ -// Fast Fourier Transform - -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } -} - -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - } - } -} - - -//------------------------------------------------------------------------------ -// FFT Initialization - -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial - -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() -{ - GFSymbol temp[kGFBits - 1]; - - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); - - for (unsigned m = 0; m < (kGFBits - 1); ++m) - { - const unsigned step = (unsigned)1 << (m + 1); - - skewVec[((unsigned)1 << m) - 1] = 0; - - for (unsigned i = m; i < (kGFBits - 1); ++i) - { - const unsigned s = ((unsigned)1 << (i + 1)); - - for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; - } - - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; - - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); - } - - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; - - temp[0] = kFieldModulus - temp[0]; - - for (unsigned i = 1; i < (kGFBits - 1); ++i) - temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); - - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); -} - - -//------------------------------------------------------------------------------ -// Encoder - -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) -{ - memcpy(codeword, data, sizeof(GFSymbol) * k); - - IFLT(codeword, k, 0); - - for (unsigned i = k; i < kFieldSize; i += k) - { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) - { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } - - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) - { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; - } - } - } - - //printf("Decoding is successful!\n"); -} - - -//------------------------------------------------------------------------------ -// Entrypoint - -int main(int argc, char **argv) -{ - // Initialize architecture-specific code - leo_architecture_init(); - - // Fill GFLog table and GFExp table - InitField(); - - // Compute factors used in erasure decoder - InitFieldOperations(); - - unsigned seed = (unsigned)time(NULL); - for (;;) - { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); - - ++seed; - } - - return 0; -} diff --git a/LeopardEncoder.h b/LeopardEncoder.h deleted file mode 100644 index 71d22e2..0000000 --- a/LeopardEncoder.h +++ /dev/null @@ -1,1220 +0,0 @@ -/* - Copyright (c) 2017 Christopher A. Taylor. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include -#include - - -/* - TODO: - + Write C API and unit tester - + Limit input to multiples of 64 bytes - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! - + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Refactor software - + Pick a name for the software better than LEO_RS - + I think it should be split up into several C++ modules - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! - + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation - + Release version 1 - - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. -*/ - - -//------------------------------------------------------------------------------ -// Debug - -// Some bugs only repro in release mode, so this can be helpful -//#define LEO_DEBUG_IN_RELEASE - -#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) - #define LEO_DEBUG - #ifdef _WIN32 - #define LEO_DEBUG_BREAK __debugbreak() - #else - #define LEO_DEBUG_BREAK __builtin_trap() - #endif - #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } -#else - #define LEO_DEBUG_BREAK ; - #define LEO_DEBUG_ASSERT(cond) ; -#endif - - -//------------------------------------------------------------------------------ -// Platform/Architecture - -#if defined(ANDROID) || defined(IOS) - #define LEO_TARGET_MOBILE -#endif // ANDROID - -#if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) - #define LEO_TRY_AVX2 /* 256-bit */ - #include - #define LEO_ALIGN_BYTES 32 -#else // __AVX2__ - #define LEO_ALIGN_BYTES 16 -#endif // __AVX2__ - -#if !defined(LEO_TARGET_MOBILE) - // Note: MSVC currently only supports SSSE3 but not AVX2 - #include // SSSE3: _mm_shuffle_epi8 - #include // SSE2 -#endif // LEO_TARGET_MOBILE - -#if defined(HAVE_ARM_NEON_H) - #include -#endif // HAVE_ARM_NEON_H - -#if defined(LEO_TARGET_MOBILE) - - #define LEO_ALIGNED_ACCESSES /* Inputs must be aligned to LEO_ALIGN_BYTES */ - -# if defined(HAVE_ARM_NEON_H) - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 uint8x16_t - #define LEO_TRY_NEON -#else - #define LEO_M128 uint64_t -# endif - -#else // LEO_TARGET_MOBILE - - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 __m128i - -#endif // LEO_TARGET_MOBILE - -#ifdef LEO_TRY_AVX2 - // Compiler-specific 256-bit SIMD register keyword - #define LEO_M256 __m256i -#endif - -// Compiler-specific C++11 restrict keyword -#define LEO_RESTRICT __restrict - -// Compiler-specific force inline keyword -#ifdef _MSC_VER - #define LEO_FORCE_INLINE inline __forceinline -#else - #define LEO_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -// Compiler-specific alignment keyword -// Note: Alignment only matters for ARM NEON where it should be 16 -#ifdef _MSC_VER - #define LEO_ALIGNED __declspec(align(LEO_ALIGN_BYTES)) -#else // _MSC_VER - #define LEO_ALIGNED __attribute__((aligned(LEO_ALIGN_BYTES))) -#endif // _MSC_VER - - -//------------------------------------------------------------------------------ -// Runtime CPU Architecture Check -// -// Feature checks stolen shamelessly from -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/runtime.c - -#if defined(HAVE_ANDROID_GETCPUFEATURES) - #include -#endif - -#if defined(LEO_TRY_NEON) -# if defined(IOS) && defined(__ARM_NEON__) - // Requires iPhone 5S or newer - static const bool CpuHasNeon = true; - static const bool CpuHasNeon64 = true; -# else - // Remember to add LOCAL_STATIC_LIBRARIES := cpufeatures - static bool CpuHasNeon = false; // V6 / V7 - static bool CpuHasNeon64 = false; // 64-bit -# endif -#endif - - -#if !defined(LEO_TARGET_MOBILE) - -#ifdef _MSC_VER - #include // __cpuid - #pragma warning(disable: 4752) // found Intel(R) Advanced Vector Extensions; consider using /arch:AVX -#endif - -#ifdef LEO_TRY_AVX2 -static bool CpuHasAVX2 = false; -#endif -static bool CpuHasSSSE3 = false; - -#define CPUID_EBX_AVX2 0x00000020 -#define CPUID_ECX_SSSE3 0x00000200 - -static void _cpuid(unsigned int cpu_info[4U], const unsigned int cpu_info_type) -{ -#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) - __cpuid((int *) cpu_info, cpu_info_type); -#else //if defined(HAVE_CPUID) - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; -# ifdef __i386__ - __asm__ __volatile__ ("pushfl; pushfl; " - "popl %0; " - "movl %0, %1; xorl %2, %0; " - "pushl %0; " - "popfl; pushfl; popl %0; popfl" : - "=&r" (cpu_info[0]), "=&r" (cpu_info[1]) : - "i" (0x200000)); - if (((cpu_info[0] ^ cpu_info[1]) & 0x200000) == 0) { - return; /* LCOV_EXCL_LINE */ - } -# endif -# ifdef __i386__ - __asm__ __volatile__ ("xchgl %%ebx, %k1; cpuid; xchgl %%ebx, %k1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# elif defined(__x86_64__) - __asm__ __volatile__ ("xchgq %%rbx, %q1; cpuid; xchgq %%rbx, %q1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# else - __asm__ __volatile__ ("cpuid" : - "=a" (cpu_info[0]), "=b" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# endif -#endif -} - -#endif // defined(LEO_TARGET_MOBILE) - - -static void leo_architecture_init() -{ -#if defined(LEO_TRY_NEON) && defined(HAVE_ANDROID_GETCPUFEATURES) - AndroidCpuFamily family = android_getCpuFamily(); - if (family == ANDROID_CPU_FAMILY_ARM) - { - if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) - CpuHasNeon = true; - } - else if (family == ANDROID_CPU_FAMILY_ARM64) - { - CpuHasNeon = true; - if (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) - CpuHasNeon64 = true; - } -#endif - -#if !defined(LEO_TARGET_MOBILE) - unsigned int cpu_info[4]; - - _cpuid(cpu_info, 1); - CpuHasSSSE3 = ((cpu_info[2] & CPUID_ECX_SSSE3) != 0); - -#if defined(LEO_TRY_AVX2) - _cpuid(cpu_info, 7); - CpuHasAVX2 = ((cpu_info[1] & CPUID_EBX_AVX2) != 0); -#endif // LEO_TRY_AVX2 - -#endif // LEO_TARGET_MOBILE -} - - -//------------------------------------------------------------------------------ -// SIMD-Safe Aligned Memory Allocations - -static const unsigned kAlignmentBytes = LEO_ALIGN_BYTES; - -LEO_FORCE_INLINE unsigned NextAlignedOffset(unsigned offset) -{ - return (offset + kAlignmentBytes - 1) & ~(kAlignmentBytes - 1); -} - -static LEO_FORCE_INLINE uint8_t* SIMDSafeAllocate(size_t size) -{ - uint8_t* data = (uint8_t*)calloc(1, kAlignmentBytes + size); - if (!data) - return nullptr; - unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); - data += kAlignmentBytes - offset; - data[-1] = (uint8_t)offset; - return data; -} - -static LEO_FORCE_INLINE void SIMDSafeFree(void* ptr) -{ - if (!ptr) - return; - uint8_t* data = (uint8_t*)ptr; - unsigned offset = data[-1]; - if (offset >= kAlignmentBytes) - { - LEO_DEBUG_BREAK; // Should never happen - return; - } - data -= kAlignmentBytes - offset; - free(data); -} - - -//------------------------------------------------------------------------------ -// Field - -//#define LEO_SHORT_FIELD - -#ifdef LEO_SHORT_FIELD -typedef uint8_t GFSymbol; -static const unsigned kGFBits = 8; -static const unsigned kGFPolynomial = 0x11D; -GFSymbol kGFBasis[kGFBits] = { - 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis -}; -#else -typedef uint16_t GFSymbol; -static const unsigned kGFBits = 16; -static const unsigned kGFPolynomial = 0x1002D; -GFSymbol kGFBasis[kGFBits] = { - 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis - 0xC582, 0xED2E, 0x914C, 0x4012, - 0x6C98, 0x10D8, 0x6A72, 0xB900, - 0xFDB8, 0xFB34, 0xFF38, 0x991E -}; -#endif - -/* - Cantor Basis introduced by: - D. G. Cantor, "On arithmetical algorithms over finite fields", - Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. -*/ - -static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size -static const unsigned kFieldModulus = kFieldSize - 1; - -static GFSymbol GFLog[kFieldSize]; -static GFSymbol GFExp[kFieldSize]; - -// Initialize GFLog[], GFExp[] -static void InitField() -{ - unsigned state = 1; - for (unsigned i = 0; i < kFieldModulus; ++i) - { - GFExp[state] = static_cast(i); - state <<= 1; - if (state >= kFieldSize) - state ^= kGFPolynomial; - } - GFExp[0] = kFieldModulus; - - // Conversion to chosen basis: - - GFLog[0] = 0; - for (unsigned i = 0; i < kGFBits; ++i) - { - const GFSymbol basis = kGFBasis[i]; - const unsigned width = (unsigned)(1UL << i); - - for (unsigned j = 0; j < width; ++j) - GFLog[j + width] = GFLog[j] ^ basis; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - GFLog[i] = GFExp[GFLog[i]]; - - for (unsigned i = 0; i < kFieldSize; ++i) - GFExp[GFLog[i]] = i; - - GFExp[kFieldModulus] = GFExp[0]; -} - - -//------------------------------------------------------------------------------ -// Mod Q Field Operations -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// z = x + y (mod Q) -static inline GFSymbol AddModQ(GFSymbol a, GFSymbol b) -{ - const unsigned sum = (unsigned)a + b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(sum + (sum >> kGFBits)); -} - -// z = x - y (mod Q) -static inline GFSymbol SubModQ(GFSymbol a, GFSymbol b) -{ - const unsigned dif = (unsigned)a - b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(dif + (dif >> kGFBits)); -} - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; -} - - -//------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. - -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; - -// {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) -{ - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); - a = sum; - b = dif; -} - -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) -{ - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; -} - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) -{ - unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - unsigned y = 0; - data[y] = t0; y += s; - data[y] = t1; y += s; - data[y] = t2; y += s; - data[y] = t3; -} - -static inline void FWHT_8(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; -} - -static inline void FWHT_16(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t8, t9); - FWHT_2(t10, t11); - FWHT_2(t12, t13); - FWHT_2(t14, t15); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t8, t10); - FWHT_2(t9, t11); - FWHT_2(t12, t14); - FWHT_2(t13, t15); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - FWHT_2(t8, t12); - FWHT_2(t9, t13); - FWHT_2(t10, t14); - FWHT_2(t11, t15); - FWHT_2(t0, t8); - FWHT_2(t1, t9); - FWHT_2(t2, t10); - FWHT_2(t3, t11); - FWHT_2(t4, t12); - FWHT_2(t5, t13); - FWHT_2(t6, t14); - FWHT_2(t7, t15); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; - data[8] = t8; - data[9] = t9; - data[10] = t10; - data[11] = t11; - data[12] = t12; - data[13] = t13; - data[14] = t14; - data[15] = t15; -} - -static void FWHT_SmallData(fwht_t* data, unsigned ldn) -{ - const unsigned n = (1UL << ldn); - - if (n <= 2) - { - if (n == 2) - FWHT_2(data[0], data[1]); - return; - } - - for (unsigned ldm = ldn; ldm > 3; ldm -= 2) - { - unsigned m = (1UL << ldm); - unsigned m4 = (m >> 2); - for (unsigned r = 0; r < n; r += m) - for (unsigned j = 0; j < m4; j++) - FWHT_4(data + j + r, m4); - } - - if (ldn & 1) - { - for (unsigned i0 = 0; i0 < n; i0 += 8) - FWHT_8(data + i0); - } - else - { - for (unsigned i0 = 0; i0 < n; i0 += 4) - FWHT_4(data + i0); - } -} - -// Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) -{ - if (ldn <= 13) - { - FWHT_SmallData(data, ldn); - return; - } - - FWHT_2(data[2], data[3]); - FWHT_4(data + 4); - FWHT_8(data + 8); - FWHT_16(data + 16); - for (unsigned ldm = 5; ldm < ldn; ++ldm) - FWHT(data + (unsigned)(1UL << ldm), ldm); - - for (unsigned ldm = 0; ldm < ldn; ++ldm) - { - const unsigned mh = (1UL << ldm); - for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2) - FWHT_2(data[t1], data[t2]); - } -} - -#endif - - -//------------------------------------------------------------------------------ -// Memory Buffer XOR - -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) -{ - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) - { - while (bytes >= 64) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; - } - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; - } - } - else -# endif // LEO_TRY_NEON - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); - } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) - { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); - - while (bytes >= 128) - { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); - - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - - bytes -= 128, x32 += 4, y32 += 4; - } - - // Handle multiples of 32 bytes - while (bytes >= 32) - { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; - } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); - } - else -# endif // LEO_TRY_AVX2 - { - while (bytes >= 64) - { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); - - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - - bytes -= 64, x16 += 4, y16 += 4; - } - } -#endif // LEO_TARGET_MOBILE - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); - - bytes -= 16, ++x16, ++y16; - } - - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); - - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } - - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } - - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } -} - - -//------------------------------------------------------------------------------ -// Formal Derivative - -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) -{ - for (unsigned i = 1; i < size; ++i) - { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; - - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; - } - - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); -} - - -//------------------------------------------------------------------------------ -// Fast Fourier Transform - -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } -} - -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - } - } -} - - -//------------------------------------------------------------------------------ -// FFT Initialization - -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial - -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() -{ - GFSymbol temp[kGFBits - 1]; - - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); - - for (unsigned m = 0; m < (kGFBits - 1); ++m) - { - const unsigned step = (unsigned)1 << (m + 1); - - skewVec[((unsigned)1 << m) - 1] = 0; - - for (unsigned i = m; i < (kGFBits - 1); ++i) - { - const unsigned s = ((unsigned)1 << (i + 1)); - - for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; - } - - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; - - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); - } - - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; - - temp[0] = kFieldModulus - temp[0]; - - for (unsigned i = 1; i < (kGFBits - 1); ++i) - temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); - - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); -} - - -//------------------------------------------------------------------------------ -// Encoder - -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) -{ - memcpy(codeword, data, sizeof(GFSymbol) * k); - - IFLT(codeword, k, 0); - - for (unsigned i = k; i < kFieldSize; i += k) - { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) - { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } - - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) - { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; - } - } - } - - //printf("Decoding is successful!\n"); -} - - -//------------------------------------------------------------------------------ -// Entrypoint - -int main(int argc, char **argv) -{ - // Initialize architecture-specific code - leo_architecture_init(); - - // Fill GFLog table and GFExp table - InitField(); - - // Compute factors used in erasure decoder - InitFieldOperations(); - - unsigned seed = (unsigned)time(NULL); - for (;;) - { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); - - ++seed; - } - - return 0; -} diff --git a/LeopardFF16.cpp b/LeopardFF16.cpp index 71d22e2..bd5c1cb 100644 --- a/LeopardFF16.cpp +++ b/LeopardFF16.cpp @@ -9,7 +9,7 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be + * Neither the name of Leopard-RS nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -26,494 +26,75 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "LeopardFF16.h" #include -#include -#include -#include -#include +// Define this to enable the optimized version of FWHT() +#define LEO_FF16_FWHT_OPTIMIZED -/* - TODO: - + Write C API and unit tester - + Limit input to multiples of 64 bytes - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! - + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Refactor software - + Pick a name for the software better than LEO_RS - + I think it should be split up into several C++ modules - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! - + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation - + Release version 1 - - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. -*/ +namespace leopard { namespace ff16 { //------------------------------------------------------------------------------ -// Debug +// Datatypes and Constants -// Some bugs only repro in release mode, so this can be helpful -//#define LEO_DEBUG_IN_RELEASE +// Modulus for field operations +static const ffe_t kModulus = 65535; -#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) - #define LEO_DEBUG - #ifdef _WIN32 - #define LEO_DEBUG_BREAK __debugbreak() - #else - #define LEO_DEBUG_BREAK __builtin_trap() - #endif - #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } -#else - #define LEO_DEBUG_BREAK ; - #define LEO_DEBUG_ASSERT(cond) ; -#endif +// LFSR Polynomial that generates the field elements +static const unsigned kPolynomial = 0x1002D; - -//------------------------------------------------------------------------------ -// Platform/Architecture - -#if defined(ANDROID) || defined(IOS) - #define LEO_TARGET_MOBILE -#endif // ANDROID - -#if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) - #define LEO_TRY_AVX2 /* 256-bit */ - #include - #define LEO_ALIGN_BYTES 32 -#else // __AVX2__ - #define LEO_ALIGN_BYTES 16 -#endif // __AVX2__ - -#if !defined(LEO_TARGET_MOBILE) - // Note: MSVC currently only supports SSSE3 but not AVX2 - #include // SSSE3: _mm_shuffle_epi8 - #include // SSE2 -#endif // LEO_TARGET_MOBILE - -#if defined(HAVE_ARM_NEON_H) - #include -#endif // HAVE_ARM_NEON_H - -#if defined(LEO_TARGET_MOBILE) - - #define LEO_ALIGNED_ACCESSES /* Inputs must be aligned to LEO_ALIGN_BYTES */ - -# if defined(HAVE_ARM_NEON_H) - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 uint8x16_t - #define LEO_TRY_NEON -#else - #define LEO_M128 uint64_t -# endif - -#else // LEO_TARGET_MOBILE - - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 __m128i - -#endif // LEO_TARGET_MOBILE - -#ifdef LEO_TRY_AVX2 - // Compiler-specific 256-bit SIMD register keyword - #define LEO_M256 __m256i -#endif - -// Compiler-specific C++11 restrict keyword -#define LEO_RESTRICT __restrict - -// Compiler-specific force inline keyword -#ifdef _MSC_VER - #define LEO_FORCE_INLINE inline __forceinline -#else - #define LEO_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -// Compiler-specific alignment keyword -// Note: Alignment only matters for ARM NEON where it should be 16 -#ifdef _MSC_VER - #define LEO_ALIGNED __declspec(align(LEO_ALIGN_BYTES)) -#else // _MSC_VER - #define LEO_ALIGNED __attribute__((aligned(LEO_ALIGN_BYTES))) -#endif // _MSC_VER - - -//------------------------------------------------------------------------------ -// Runtime CPU Architecture Check -// -// Feature checks stolen shamelessly from -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/runtime.c - -#if defined(HAVE_ANDROID_GETCPUFEATURES) - #include -#endif - -#if defined(LEO_TRY_NEON) -# if defined(IOS) && defined(__ARM_NEON__) - // Requires iPhone 5S or newer - static const bool CpuHasNeon = true; - static const bool CpuHasNeon64 = true; -# else - // Remember to add LOCAL_STATIC_LIBRARIES := cpufeatures - static bool CpuHasNeon = false; // V6 / V7 - static bool CpuHasNeon64 = false; // 64-bit -# endif -#endif - - -#if !defined(LEO_TARGET_MOBILE) - -#ifdef _MSC_VER - #include // __cpuid - #pragma warning(disable: 4752) // found Intel(R) Advanced Vector Extensions; consider using /arch:AVX -#endif - -#ifdef LEO_TRY_AVX2 -static bool CpuHasAVX2 = false; -#endif -static bool CpuHasSSSE3 = false; - -#define CPUID_EBX_AVX2 0x00000020 -#define CPUID_ECX_SSSE3 0x00000200 - -static void _cpuid(unsigned int cpu_info[4U], const unsigned int cpu_info_type) -{ -#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) - __cpuid((int *) cpu_info, cpu_info_type); -#else //if defined(HAVE_CPUID) - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; -# ifdef __i386__ - __asm__ __volatile__ ("pushfl; pushfl; " - "popl %0; " - "movl %0, %1; xorl %2, %0; " - "pushl %0; " - "popfl; pushfl; popl %0; popfl" : - "=&r" (cpu_info[0]), "=&r" (cpu_info[1]) : - "i" (0x200000)); - if (((cpu_info[0] ^ cpu_info[1]) & 0x200000) == 0) { - return; /* LCOV_EXCL_LINE */ - } -# endif -# ifdef __i386__ - __asm__ __volatile__ ("xchgl %%ebx, %k1; cpuid; xchgl %%ebx, %k1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# elif defined(__x86_64__) - __asm__ __volatile__ ("xchgq %%rbx, %q1; cpuid; xchgq %%rbx, %q1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# else - __asm__ __volatile__ ("cpuid" : - "=a" (cpu_info[0]), "=b" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# endif -#endif -} - -#endif // defined(LEO_TARGET_MOBILE) - - -static void leo_architecture_init() -{ -#if defined(LEO_TRY_NEON) && defined(HAVE_ANDROID_GETCPUFEATURES) - AndroidCpuFamily family = android_getCpuFamily(); - if (family == ANDROID_CPU_FAMILY_ARM) - { - if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) - CpuHasNeon = true; - } - else if (family == ANDROID_CPU_FAMILY_ARM64) - { - CpuHasNeon = true; - if (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) - CpuHasNeon64 = true; - } -#endif - -#if !defined(LEO_TARGET_MOBILE) - unsigned int cpu_info[4]; - - _cpuid(cpu_info, 1); - CpuHasSSSE3 = ((cpu_info[2] & CPUID_ECX_SSSE3) != 0); - -#if defined(LEO_TRY_AVX2) - _cpuid(cpu_info, 7); - CpuHasAVX2 = ((cpu_info[1] & CPUID_EBX_AVX2) != 0); -#endif // LEO_TRY_AVX2 - -#endif // LEO_TARGET_MOBILE -} - - -//------------------------------------------------------------------------------ -// SIMD-Safe Aligned Memory Allocations - -static const unsigned kAlignmentBytes = LEO_ALIGN_BYTES; - -LEO_FORCE_INLINE unsigned NextAlignedOffset(unsigned offset) -{ - return (offset + kAlignmentBytes - 1) & ~(kAlignmentBytes - 1); -} - -static LEO_FORCE_INLINE uint8_t* SIMDSafeAllocate(size_t size) -{ - uint8_t* data = (uint8_t*)calloc(1, kAlignmentBytes + size); - if (!data) - return nullptr; - unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); - data += kAlignmentBytes - offset; - data[-1] = (uint8_t)offset; - return data; -} - -static LEO_FORCE_INLINE void SIMDSafeFree(void* ptr) -{ - if (!ptr) - return; - uint8_t* data = (uint8_t*)ptr; - unsigned offset = data[-1]; - if (offset >= kAlignmentBytes) - { - LEO_DEBUG_BREAK; // Should never happen - return; - } - data -= kAlignmentBytes - offset; - free(data); -} - - -//------------------------------------------------------------------------------ -// Field - -//#define LEO_SHORT_FIELD - -#ifdef LEO_SHORT_FIELD -typedef uint8_t GFSymbol; -static const unsigned kGFBits = 8; -static const unsigned kGFPolynomial = 0x11D; -GFSymbol kGFBasis[kGFBits] = { - 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis -}; -#else -typedef uint16_t GFSymbol; -static const unsigned kGFBits = 16; -static const unsigned kGFPolynomial = 0x1002D; -GFSymbol kGFBasis[kGFBits] = { +// Basis used for generating logarithm tables +static const ffe_t kBasis[kBits] = { 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis 0xC582, 0xED2E, 0x914C, 0x4012, 0x6C98, 0x10D8, 0x6A72, 0xB900, 0xFDB8, 0xFB34, 0xFF38, 0x991E }; -#endif - -/* - Cantor Basis introduced by: - D. G. Cantor, "On arithmetical algorithms over finite fields", - Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. -*/ - -static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size -static const unsigned kFieldModulus = kFieldSize - 1; - -static GFSymbol GFLog[kFieldSize]; -static GFSymbol GFExp[kFieldSize]; - -// Initialize GFLog[], GFExp[] -static void InitField() -{ - unsigned state = 1; - for (unsigned i = 0; i < kFieldModulus; ++i) - { - GFExp[state] = static_cast(i); - state <<= 1; - if (state >= kFieldSize) - state ^= kGFPolynomial; - } - GFExp[0] = kFieldModulus; - - // Conversion to chosen basis: - - GFLog[0] = 0; - for (unsigned i = 0; i < kGFBits; ++i) - { - const GFSymbol basis = kGFBasis[i]; - const unsigned width = (unsigned)(1UL << i); - - for (unsigned j = 0; j < width; ++j) - GFLog[j + width] = GFLog[j] ^ basis; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - GFLog[i] = GFExp[GFLog[i]]; - - for (unsigned i = 0; i < kFieldSize; ++i) - GFExp[GFLog[i]] = i; - - GFExp[kFieldModulus] = GFExp[0]; -} //------------------------------------------------------------------------------ -// Mod Q Field Operations -// -// Q is the maximum symbol value, e.g. 255 or 65535. +// Field Operations -// z = x + y (mod Q) -static inline GFSymbol AddModQ(GFSymbol a, GFSymbol b) +// z = x + y (mod kModulus) +static inline ffe_t AddMod(const ffe_t a, const ffe_t b) { const unsigned sum = (unsigned)a + b; - // Partial reduction step, allowing for Q to be returned - return static_cast(sum + (sum >> kGFBits)); + // Partial reduction step, allowing for kModulus to be returned + return static_cast(sum + (sum >> kBits)); } -// z = x - y (mod Q) -static inline GFSymbol SubModQ(GFSymbol a, GFSymbol b) +// z = x - y (mod kModulus) +static inline ffe_t SubMod(const ffe_t a, const ffe_t b) { const unsigned dif = (unsigned)a - b; - // Partial reduction step, allowing for Q to be returned - return static_cast(dif + (dif >> kGFBits)); -} - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; + // Partial reduction step, allowing for kModulus to be returned + return static_cast(dif + (dif >> kBits)); } //------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. +// Fast Walsh-Hadamard Transform (FWHT) (mod kModulus) -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; +#if defined(LEO_FF16_FWHT_OPTIMIZED) // {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) +static LEO_FORCE_INLINE void FWHT_2(ffe_t& LEO_RESTRICT a, ffe_t& LEO_RESTRICT b) { - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); + const ffe_t sum = AddMod(a, b); + const ffe_t dif = SubMod(a, b); a = sum; b = dif; } -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) +static LEO_FORCE_INLINE void FWHT_4(ffe_t* data) { - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; + ffe_t t0 = data[0]; + ffe_t t1 = data[1]; + ffe_t t2 = data[2]; + ffe_t t3 = data[3]; FWHT_2(t0, t1); FWHT_2(t2, t3); FWHT_2(t0, t2); @@ -524,13 +105,13 @@ static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) data[3] = t3; } -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) +static LEO_FORCE_INLINE void FWHT_4(ffe_t* data, unsigned s) { unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; + ffe_t t0 = data[x]; x += s; + ffe_t t1 = data[x]; x += s; + ffe_t t2 = data[x]; x += s; + ffe_t t3 = data[x]; FWHT_2(t0, t1); FWHT_2(t2, t3); FWHT_2(t0, t2); @@ -542,16 +123,16 @@ static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) data[y] = t3; } -static inline void FWHT_8(fwht_t* data) +static inline void FWHT_8(ffe_t* data) { - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; + ffe_t t0 = data[0]; + ffe_t t1 = data[1]; + ffe_t t2 = data[2]; + ffe_t t3 = data[3]; + ffe_t t4 = data[4]; + ffe_t t5 = data[5]; + ffe_t t6 = data[6]; + ffe_t t7 = data[7]; FWHT_2(t0, t1); FWHT_2(t2, t3); FWHT_2(t4, t5); @@ -574,24 +155,24 @@ static inline void FWHT_8(fwht_t* data) data[7] = t7; } -static inline void FWHT_16(fwht_t* data) +static inline void FWHT_16(ffe_t* data) { - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; + ffe_t t0 = data[0]; + ffe_t t1 = data[1]; + ffe_t t2 = data[2]; + ffe_t t3 = data[3]; + ffe_t t4 = data[4]; + ffe_t t5 = data[5]; + ffe_t t6 = data[6]; + ffe_t t7 = data[7]; + ffe_t t8 = data[8]; + ffe_t t9 = data[9]; + ffe_t t10 = data[10]; + ffe_t t11 = data[11]; + ffe_t t12 = data[12]; + ffe_t t13 = data[13]; + ffe_t t14 = data[14]; + ffe_t t15 = data[15]; FWHT_2(t0, t1); FWHT_2(t2, t3); FWHT_2(t4, t5); @@ -642,7 +223,7 @@ static inline void FWHT_16(fwht_t* data) data[15] = t15; } -static void FWHT_SmallData(fwht_t* data, unsigned ldn) +static void FWHT_SmallData(ffe_t* data, unsigned ldn) { const unsigned n = (1UL << ldn); @@ -675,7 +256,7 @@ static void FWHT_SmallData(fwht_t* data, unsigned ldn) } // Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) +static void FWHT(ffe_t* data, const unsigned ldn) { if (ldn <= 13) { @@ -698,523 +279,774 @@ static void FWHT(fwht_t* data, const unsigned ldn) } } -#endif +#else // LEO_FF16_FWHT_OPTIMIZED + +// Reference implementation +void FWHT(ffe_t* data, const unsigned bits) +{ + const unsigned size = (unsigned)(1UL << bits); + for (unsigned width = 1; width < size; width <<= 1) + for (unsigned i = 0; i < size; i += (width << 1)) + for (unsigned j = i; j < (width + i); ++j) + FWHT_2(data[j], data[j + width]); +} + +#endif // LEO_FF16_FWHT_OPTIMIZED + +// Transform specialized for the finite field order +void FWHT(ffe_t data[kOrder]) +{ + FWHT(data, kBits); +} //------------------------------------------------------------------------------ -// Memory Buffer XOR +// Logarithm Tables -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) +static ffe_t LogLUT[kOrder]; +static ffe_t ExpLUT[kOrder]; + + +// Initialize LogLUT[], ExpLUT[] +static void InitializeLogarithmTables() { - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); + // LFSR table generation: -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) + unsigned state = 1; + for (unsigned i = 0; i < kModulus; ++i) { - while (bytes >= 64) + ExpLUT[state] = static_cast(i); + state <<= 1; + if (state >= kOrder) + state ^= kPolynomial; + } + ExpLUT[0] = kModulus; + + // Conversion to chosen basis: + + LogLUT[0] = 0; + for (unsigned i = 0; i < kBits; ++i) + { + const ffe_t basis = kBasis[i]; + const unsigned width = static_cast(1UL << i); + + for (unsigned j = 0; j < width; ++j) + LogLUT[j + width] = LogLUT[j] ^ basis; + } + + for (unsigned i = 0; i < kOrder; ++i) + LogLUT[i] = ExpLUT[LogLUT[i]]; + + for (unsigned i = 0; i < kOrder; ++i) + ExpLUT[LogLUT[i]] = i; + + ExpLUT[kModulus] = ExpLUT[0]; +} + +//------------------------------------------------------------------------------ +// Multiplies + +/* + Muladd implementation notes: + + Specialize for 1-3 rows at a time since often times we're multiplying by + the same (skew) value repeatedly, as the ISA-L library does here: + + https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 + + Except we should be doing it for 16-bit Galois Field. + To implement that use the ALTMAP trick from Jerasure: + + http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 + + Except we should also support AVX2 since that is a 40% perf boost, so put + the high and low bytes 32 bytes instead of 16 bytes apart. + + Also I think we should go ahead and precompute the multiply tables since + it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. +*/ + +// We require memory to be aligned since the SIMD instructions benefit from +// or require aligned accesses to the table data. +struct { + LEO_ALIGNED LEO_M128 LUT[65536][4]; +} static Multiply128LUT; +#if defined(LEO_TRY_AVX2) +struct { + LEO_ALIGNED LEO_M256 LUT[65536][4]; +} static Multiply256LUT; +#endif // LEO_TRY_AVX2 + +// Returns a * b +static ffe_t FFEMultiply(ffe_t a, ffe_t b) +{ + if (a == 0 || b == 0) + return 0; + return ExpLUT[AddMod(LogLUT[a], LogLUT[b])]; +} + +// Returns a * Log(b) +static ffe_t FFEMultiplyLog(ffe_t a, ffe_t log_b) +{ + if (a == 0) + return 0; + return ExpLUT[AddMod(LogLUT[a], b)]; +} + +bool InitializeMultiplyTables() +{ + for (int y = 0; y < 256; ++y) + { + uint8_t lo[16], hi[16]; + for (unsigned char x = 0; x < 16; ++x) { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; + lo[x] = FFEMultiply(x, static_cast(y)); + hi[x] = FFEMultiply(x << 4, static_cast(y)); } - // Handle multiples of 16 bytes - while (bytes >= 16) + const LEO_M128 table_lo = _mm_loadu_si128((LEO_M128*)lo); + const LEO_M128 table_hi = _mm_loadu_si128((LEO_M128*)hi); + + _mm_storeu_si128(Multiply128LUT.Lo + y, table_lo); + _mm_storeu_si128(Multiply128LUT.Hi + y, table_hi); + +#if defined(LEO_TRY_AVX2) + if (CpuHasAVX2) { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; + _mm256_storeu_si256(Multiply256LUT.Lo + y, + _mm256_broadcastsi128_si256(table_lo)); + _mm256_storeu_si256(Multiply256LUT.Hi + y, + _mm256_broadcastsi128_si256(table_hi)); } +#endif // LEO_TRY_AVX2 } - else -# endif // LEO_TRY_NEON + + return true; +} + +// vx[] = vy[] * m +void mul_mem_set( + void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, + ffe_t m, uint64_t bytes) +{ + if (m <= 1) { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); + if (m == 1) + memcpy(vx, vy, bytes); + else + memset(vx, 0, bytes); + return; } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) + +#if defined(LEO_TRY_AVX2) if (CpuHasAVX2) { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); + const LEO_M256 table_lo_y = _mm256_loadu_si256(Multiply256LUT.Lo + m); + const LEO_M256 table_hi_y = _mm256_loadu_si256(Multiply256LUT.Hi + m); - while (bytes >= 128) + const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f); + + LEO_M256 * LEO_RESTRICT z32 = reinterpret_cast(vx); + const LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(vy); + + const unsigned count = bytes / 64; + for (unsigned i = 0; i < count; ++i) { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); + LEO_M256 x0 = _mm256_loadu_si256(x32 + i * 2); + LEO_M256 l0 = _mm256_and_si256(x0, clr_mask); + x0 = _mm256_srli_epi64(x0, 4); + LEO_M256 h0 = _mm256_and_si256(x0, clr_mask); + l0 = _mm256_shuffle_epi8(table_lo_y, l0); + h0 = _mm256_shuffle_epi8(table_hi_y, h0); + _mm256_storeu_si256(z32 + i * 2, _mm256_xor_si256(l0, h0)); - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - - bytes -= 128, x32 += 4, y32 += 4; + LEO_M256 x1 = _mm256_loadu_si256(x32 + i * 2 + 1); + LEO_M256 l1 = _mm256_and_si256(x1, clr_mask); + x1 = _mm256_srli_epi64(x1, 4); + LEO_M256 h1 = _mm256_and_si256(x1, clr_mask); + l1 = _mm256_shuffle_epi8(table_lo_y, l1); + h1 = _mm256_shuffle_epi8(table_hi_y, h1); + _mm256_storeu_si256(z32 + i * 2 + 1, _mm256_xor_si256(l1, h1)); } + return; + } +#endif // LEO_TRY_AVX2 - // Handle multiples of 32 bytes - while (bytes >= 32) + const LEO_M128 table_lo_y = _mm_loadu_si128(Multiply128LUT.Lo + m); + const LEO_M128 table_hi_y = _mm_loadu_si128(Multiply128LUT.Hi + m); + + const LEO_M128 clr_mask = _mm_set1_epi8(0x0f); + + LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast (vx); + const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); + + do + { + LEO_M128 x3 = _mm_loadu_si128(y16 + 3); + LEO_M128 l3 = _mm_and_si128(x3, clr_mask); + x3 = _mm_srli_epi64(x3, 4); + LEO_M128 h3 = _mm_and_si128(x3, clr_mask); + l3 = _mm_shuffle_epi8(table_lo_y, l3); + h3 = _mm_shuffle_epi8(table_hi_y, h3); + + LEO_M128 x2 = _mm_loadu_si128(y16 + 2); + LEO_M128 l2 = _mm_and_si128(x2, clr_mask); + x2 = _mm_srli_epi64(x2, 4); + LEO_M128 h2 = _mm_and_si128(x2, clr_mask); + l2 = _mm_shuffle_epi8(table_lo_y, l2); + h2 = _mm_shuffle_epi8(table_hi_y, h2); + + LEO_M128 x1 = _mm_loadu_si128(y16 + 1); + LEO_M128 l1 = _mm_and_si128(x1, clr_mask); + x1 = _mm_srli_epi64(x1, 4); + LEO_M128 h1 = _mm_and_si128(x1, clr_mask); + l1 = _mm_shuffle_epi8(table_lo_y, l1); + h1 = _mm_shuffle_epi8(table_hi_y, h1); + + LEO_M128 x0 = _mm_loadu_si128(y16); + LEO_M128 l0 = _mm_and_si128(x0, clr_mask); + x0 = _mm_srli_epi64(x0, 4); + LEO_M128 h0 = _mm_and_si128(x0, clr_mask); + l0 = _mm_shuffle_epi8(table_lo_y, l0); + h0 = _mm_shuffle_epi8(table_hi_y, h0); + + _mm_storeu_si128(x16 + 3, _mm_xor_si128(l3, h3)); + _mm_storeu_si128(x16 + 2, _mm_xor_si128(l2, h2)); + _mm_storeu_si128(x16 + 1, _mm_xor_si128(l1, h1)); + _mm_storeu_si128(x16, _mm_xor_si128(l0, h0)); + + x16 += 4, y16 += 4; + bytes -= 64; + } while (bytes > 0); +} + +// vx0[] *= m, vx1[] *= m +void mul_mem2_inplace( + void * LEO_RESTRICT vx_0, + void * LEO_RESTRICT vx_1, + ffe_t m, uint64_t bytes) +{ + if (m <= 1) + { + if (m == 0) { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; + memset(vx_0, 0, bytes); + memset(vx_1, 0, bytes); } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); + return; } - else -# endif // LEO_TRY_AVX2 + +#if defined(LEO_TRY_AVX2) + if (CpuHasAVX2) { - while (bytes >= 64) + const LEO_M256 table_lo_y = _mm256_loadu_si256(Multiply256LUT.Lo + m); + const LEO_M256 table_hi_y = _mm256_loadu_si256(Multiply256LUT.Hi + m); + + const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f); + + LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast(vx_0); + LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast(vx_1); + + do { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); + LEO_M256 x0_0 = _mm256_loadu_si256(x32_0 + 1); + LEO_M256 l0_0 = _mm256_and_si256(x0_0, clr_mask); + x0_0 = _mm256_srli_epi64(x0_0, 4); + LEO_M256 h0_0 = _mm256_and_si256(x0_0, clr_mask); + l0_0 = _mm256_shuffle_epi8(table_lo_y, l0_0); + h0_0 = _mm256_shuffle_epi8(table_hi_y, h0_0); + l0_0 = _mm256_xor_si256(l0_0, h0_0); - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); + LEO_M256 x1_0 = _mm256_loadu_si256(x32_0); + LEO_M256 l1_0 = _mm256_and_si256(x1_0, clr_mask); + x1_0 = _mm256_srli_epi64(x1_0, 4); + LEO_M256 h1_0 = _mm256_and_si256(x1_0, clr_mask); + l1_0 = _mm256_shuffle_epi8(table_lo_y, l1_0); + h1_0 = _mm256_shuffle_epi8(table_hi_y, h1_0); + l1_0 = _mm256_xor_si256(l1_0, h1_0); - bytes -= 64, x16 += 4, y16 += 4; - } + LEO_M256 x0_1 = _mm256_loadu_si256(x32_1 + 1); + LEO_M256 l0_1 = _mm256_and_si256(x0_1, clr_mask); + x0_1 = _mm256_srli_epi64(x0_1, 4); + LEO_M256 h0_1 = _mm256_and_si256(x0_1, clr_mask); + l0_1 = _mm256_shuffle_epi8(table_lo_y, l0_1); + h0_1 = _mm256_shuffle_epi8(table_hi_y, h0_1); + l0_1 = _mm256_xor_si256(l0_1, h0_1); + + LEO_M256 x1_1 = _mm256_loadu_si256(x32_1); + LEO_M256 l1_1 = _mm256_and_si256(x1_1, clr_mask); + x1_1 = _mm256_srli_epi64(x1_1, 4); + LEO_M256 h1_1 = _mm256_and_si256(x1_1, clr_mask); + l1_1 = _mm256_shuffle_epi8(table_lo_y, l1_1); + h1_1 = _mm256_shuffle_epi8(table_hi_y, h1_1); + l1_1 = _mm256_xor_si256(l1_1, h1_1); + + _mm256_storeu_si256(x32_0 + 1, l0_0); + _mm256_storeu_si256(x32_0, l1_0); + _mm256_storeu_si256(x32_1 + 1, l0_1); + _mm256_storeu_si256(x32_1, l1_1); + + x32_0 += 2; + x32_1 += 2; + bytes -= 64; + } while (bytes > 0); + return; } -#endif // LEO_TARGET_MOBILE +#endif // LEO_TRY_AVX2 - // Handle multiples of 16 bytes - while (bytes >= 16) + const LEO_M128 table_lo_y = _mm_loadu_si128(Multiply128LUT.Lo + m); + const LEO_M128 table_hi_y = _mm_loadu_si128(Multiply128LUT.Hi + m); + + const LEO_M128 clr_mask = _mm_set1_epi8(0x0f); + + LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast(vx_0); + LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast(vx_1); + + do { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); + LEO_M128 x3 = _mm_loadu_si128(x16_0 + 3); + LEO_M128 l3 = _mm_and_si128(x3, clr_mask); + x3 = _mm_srli_epi64(x3, 4); + LEO_M128 h3 = _mm_and_si128(x3, clr_mask); + l3 = _mm_shuffle_epi8(table_lo_y, l3); + h3 = _mm_shuffle_epi8(table_hi_y, h3); - bytes -= 16, ++x16, ++y16; - } + LEO_M128 x2 = _mm_loadu_si128(x16_0 + 2); + LEO_M128 l2 = _mm_and_si128(x2, clr_mask); + x2 = _mm_srli_epi64(x2, 4); + LEO_M128 h2 = _mm_and_si128(x2, clr_mask); + l2 = _mm_shuffle_epi8(table_lo_y, l2); + h2 = _mm_shuffle_epi8(table_hi_y, h2); - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); + LEO_M128 x1 = _mm_loadu_si128(x16_0 + 1); + LEO_M128 l1 = _mm_and_si128(x1, clr_mask); + x1 = _mm_srli_epi64(x1, 4); + LEO_M128 h1 = _mm_and_si128(x1, clr_mask); + l1 = _mm_shuffle_epi8(table_lo_y, l1); + h1 = _mm_shuffle_epi8(table_hi_y, h1); - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } + LEO_M128 x0 = _mm_loadu_si128(x16_0); + LEO_M128 l0 = _mm_and_si128(x0, clr_mask); + x0 = _mm_srli_epi64(x0, 4); + LEO_M128 h0 = _mm_and_si128(x0, clr_mask); + l0 = _mm_shuffle_epi8(table_lo_y, l0); + h0 = _mm_shuffle_epi8(table_hi_y, h0); - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } + _mm_storeu_si128(x16_0 + 3, _mm_xor_si128(l3, h3)); + _mm_storeu_si128(x16_0 + 2, _mm_xor_si128(l2, h2)); + _mm_storeu_si128(x16_0 + 1, _mm_xor_si128(l1, h1)); + _mm_storeu_si128(x16_0, _mm_xor_si128(l0, h0)); - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } + // FIXME: Add second one here + + x16_0 += 4; + x16_1 += 4; + bytes -= 64; + } while (bytes > 0); } //------------------------------------------------------------------------------ -// Formal Derivative +// FFT Operations -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) +// x[] ^= y[] * m, y[] ^= x[] +void fft_butterfly( + void * LEO_RESTRICT x, void * LEO_RESTRICT y, + ffe_t m, uint64_t bytes) { - for (unsigned i = 1; i < size; ++i) - { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; - } +} + +// For i = {0, 1}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] +void fft_butterfly2( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + ffe_t m, uint64_t bytes) +{ + +} + +// For i = {0, 1, 2}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] +void fft_butterfly3( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, + ffe_t m, uint64_t bytes) +{ - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); } //------------------------------------------------------------------------------ -// Fast Fourier Transform +// IFFT Operations -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) +// y[] ^= x[], x[] ^= y[] * m +void ifft_butterfly( + void * LEO_RESTRICT x, void * LEO_RESTRICT y, + ffe_t m, uint64_t bytes) { - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } } -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) +// For i = {0, 1}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m +void ifft_butterfly2( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + ffe_t m, uint64_t bytes) { - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - const GFSymbol skew = skewVec[j + index - 1]; - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); +} + +// For i = {0, 1, 2}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m +void ifft_butterfly3( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, + ffe_t m, uint64_t bytes) +{ - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - } - } } //------------------------------------------------------------------------------ -// FFT Initialization +// FFT -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial +static ffe_t FFTSkew[kFieldModulus]; // twisted factors used in FFT +static ffe_t LogWalsh[kOrder]; // factors used in the evaluation of the error locator polynomial -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() +void FFTInitialize() { - GFSymbol temp[kGFBits - 1]; + ffe_t temp[kBits - 1]; - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); + for (unsigned i = 1; i < kBits; ++i) + temp[i - 1] = (ffe_t)((unsigned)1 << i); - for (unsigned m = 0; m < (kGFBits - 1); ++m) + for (unsigned m = 0; m < (kBits - 1); ++m) { const unsigned step = (unsigned)1 << (m + 1); - skewVec[((unsigned)1 << m) - 1] = 0; + FFTSkew[((unsigned)1 << m) - 1] = 0; - for (unsigned i = m; i < (kGFBits - 1); ++i) + for (unsigned i = m; i < (kBits - 1); ++i) { const unsigned s = ((unsigned)1 << (i + 1)); for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; + FFTSkew[j + s] = FFTSkew[j] ^ temp[i]; } - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; + // TBD: This can be cleaned up + temp[m] = kFieldModulus - LogLUT[FFEMultiply(temp[m], temp[m] ^ 1)]; - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); + for (unsigned i = m + 1; i < (kBits - 1); ++i) + temp[i] = FFEMultiplyLog(temp[i], (LogLUT[temp[i] ^ 1] + temp[m]) % kFieldModulus); } - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; + for (unsigned i = 0; i < kOrder; ++i) + FFTSkew[i] = LogLUT[FFTSkew[i]]; temp[0] = kFieldModulus - temp[0]; - for (unsigned i = 1; i < (kGFBits - 1); ++i) + for (unsigned i = 1; i < (kBits - 1); ++i) temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); + for (unsigned i = 0; i < kOrder; ++i) + LogWalsh[i] = LogLUT[i]; - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } + LogWalsh[0] = 0; - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); + FWHT(LogWalsh, kBits); } //------------------------------------------------------------------------------ -// Encoder +// Encode -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) +void Encode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, + void* const * const data, + void** work) { - memcpy(codeword, data, sizeof(GFSymbol) * k); + // work <- data - IFLT(codeword, k, 0); + // FIXME: Unroll first loop to eliminate this + for (unsigned i = 0; i < m; ++i) + memcpy(work[i], data[i], buffer_bytes); - for (unsigned i = k; i < kFieldSize; i += k) + // work <- IFFT(data, m, m) + + for (unsigned width = 1; width < m; width <<= 1) { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) + for (unsigned j = width; j < m; j += (width << 1)) { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } + const ffe_t skew = FFTSkew[j + m - 1]; - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) + if (skew != kFieldModulus) { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; + for (unsigned i = j - width; i < j; ++i) + ifft_butterfly(work[i], work[i + width], skew, buffer_bytes); + } + else + { + for (unsigned i = j - width; i < j; ++i) + xor_mem(work[i + width], work[i], buffer_bytes); } } } - //printf("Decoding is successful!\n"); + for (unsigned i = m; i + m <= original_count; i += m) + { + // temp <- data + i + + void** temp = work + m; + + // FIXME: Unroll first loop to eliminate this + for (unsigned j = 0; j < m; ++j) + memcpy(temp[j], data[j], buffer_bytes); + + // temp <- IFFT(temp, m, m + i) + + for (unsigned width = 1; width < m; width <<= 1) + { + for (unsigned j = width; j < m; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j + m + i - 1]; + + if (skew != kFieldModulus) + { + for (unsigned k = j - width; k < j; ++k) + ifft_butterfly(temp[k], temp[k + width], skew, buffer_bytes); + } + else + { + for (unsigned k = j - width; k < j; ++k) + xor_mem(temp[k + width], temp[k], buffer_bytes); + } + } + } + + // work <- work XOR temp + + // FIXME: Unroll last loop to eliminate this + for (unsigned j = 0; j < m; ++j) + xor_mem(work[j], temp[j], buffer_bytes); + } + + const unsigned last_count = original_count % m; + if (last_count != 0) + { + const unsigned i = original_count - last_count; + + // temp <- data + i + + void** temp = work + m; + + for (unsigned j = 0; j < last_count; ++j) + memcpy(temp[j], data[j], buffer_bytes); + for (unsigned j = last_count; j < m; ++j) + memset(temp[j], 0, buffer_bytes); + + // temp <- IFFT(temp, m, m + i) + + for (unsigned width = 1, shift = 1; width < m; width <<= 1, ++shift) + { + // Calculate stop considering that the right is all zeroes + const unsigned stop = ((last_count + width - 1) >> shift) << shift; + + for (unsigned j = width; j < stop; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j + m + i - 1]; + + if (skew != kFieldModulus) + { + for (unsigned k = j - width; k < j; ++k) + ifft_butterfly(temp[k], temp[k + width], skew, buffer_bytes); + } + else + { + for (unsigned k = j - width; k < j; ++k) + xor_mem(temp[k + width], temp[k], buffer_bytes); + } + } + } + + // work <- work XOR temp + + // FIXME: Unroll last loop to eliminate this + for (unsigned j = 0; j < m; ++j) + xor_mem(work[j], temp[j], buffer_bytes); + } + + // work <- FFT(work, m, 0) + + for (unsigned width = (m >> 1); width > 0; width >>= 1) + { + const ffe_t* skewLUT = FFTSkew + width - 1; + const unsigned range = width << 1; + + for (unsigned j = 0; j < m; j += range) + { + const ffe_t skew = skewLUT[j]; + + if (skew != kFieldModulus) + { + for (unsigned k = j, count = j + width; k < count; ++k) + fft_butterfly(data[k], data[k + width], skew, buffer_bytes); + } + else + { + for (unsigned k = j, count = j + width; k < count; ++k) + xor_mem(work[k + width], work[k], buffer_bytes); + } + } + } } //------------------------------------------------------------------------------ -// Entrypoint +// Decode -int main(int argc, char **argv) +void Decode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, // NextPow2(recovery_count) + unsigned n, // NextPow2(m + original_count) = work_count + void* const * const original, // original_count entries + void* const * const recovery, // recovery_count entries + void** work) // n entries { - // Initialize architecture-specific code - leo_architecture_init(); + // Fill in error locations - // Fill GFLog table and GFExp table - InitField(); + ffe_t ErrorLocations[kOrder]; + for (unsigned i = 0; i < recovery_count; ++i) + ErrorLocations[i] = recovery[i] ? 0 : 1; + for (unsigned i = recovery_count; i < m; ++i) + ErrorLocations[i] = 1; + for (unsigned i = 0; i < original_count; ++i) + ErrorLocations[i + m] = original[i] ? 0 : 1; + memset(ErrorLocations + m + original_count, 0, (n - original_count - m) * sizeof(ffe_t)); - // Compute factors used in erasure decoder - InitFieldOperations(); + // Evaluate error locator polynomial - unsigned seed = (unsigned)time(NULL); - for (;;) + FWHT(ErrorLocations, kBits); + + for (unsigned i = 0; i < kOrder; ++i) + ErrorLocations[i] = ((unsigned)ErrorLocations[i] * (unsigned)LogWalsh[i]) % kFieldModulus; + + FWHT(ErrorLocations, kBits); + + // work <- recovery data + + for (unsigned i = 0; i < recovery_count; ++i) { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); + if (recovery[i]) + mul_mem_set(work[i], recovery[i], ErrorLocations[i], buffer_bytes); + else + memset(work[i], 0, buffer_bytes); + } + for (unsigned i = recovery_count; i < m; ++i) + memset(work[i], 0, buffer_bytes); - ++seed; + // work <- original data + + for (unsigned i = 0; i < original_count; ++i) + { + if (original[i]) + mul_mem_set(work[m + i], original[i], ErrorLocations[m + i], buffer_bytes); + else + memset(work[m + i], 0, buffer_bytes); + } + for (unsigned i = m + original_count; i < n; ++i) + memset(work[i], 0, buffer_bytes); + + // work <- IFFT(work, n, 0) + + for (unsigned width = 1; width < n; width <<= 1) + { + for (unsigned j = width; j < n; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j - 1]; + + if (skew != kFieldModulus) + { + for (unsigned i = j - width; i < j; ++i) + ifft_butterfly(work[i], work[i + width], skew, buffer_bytes); + } + else + { + for (unsigned i = j - width; i < j; ++i) + xor_mem(work[i + width], work[i], buffer_bytes); + } + } } - return 0; + // work <- FormalDerivative(work, n) + + for (unsigned i = 1; i < n; ++i) + { + const unsigned width = ((i ^ (i - 1)) + 1) >> 1; + + // If a large number of values are being XORed: + for (unsigned j = i - width; j < i; ++j) + xor_mem(work[j], work[j + width], buffer_bytes); + } + + // work <- FFT(work, n, 0) truncated to m + original_count + + const unsigned output_count = m + original_count; + for (unsigned width = (n >> 1); width > 0; width >>= 1) + { + const ffe_t* skewLUT = FFTSkew + width - 1; + const unsigned range = width << 1; + + for (unsigned j = (m < range) ? 0 : m; j < output_count; j += range) + { + const ffe_t skew = skewLUT[j]; + + if (skew != kFieldModulus) + { + for (unsigned i = j; i < j + width; ++i) + fft_butterfly(work[i], work[i + width], skew, buffer_bytes); + } + else + { + for (unsigned i = j; i < j + width; ++i) + xor_mem(work[i + width], work[i], buffer_bytes); + } + } + } + + // Reveal erasures + + for (unsigned i = 0; i < original_count; ++i) + if (!original[i]) + mul_mem_set(work[i], work[i + m], kFieldModulus - ErrorLocations[i], buffer_bytes); } + + +//------------------------------------------------------------------------------ +// API + +static bool IsInitialized = false; + +bool Initialize() +{ + if (IsInitialized) + return true; + + if (!CpuHasSSSE3) + return false; + + InitializeLogarithmTables(); + FFTInitialize(); + + IsInitialized = true; + return true; +} + + +}} // namespace leopard::ff16 diff --git a/LeopardFF16.h b/LeopardFF16.h index 71d22e2..981b9a9 100644 --- a/LeopardFF16.h +++ b/LeopardFF16.h @@ -9,7 +9,7 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be + * Neither the name of Leopard-RS nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -26,1195 +26,133 @@ POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include -#include +#pragma once +#include "LeopardCommon.h" /* - TODO: - + Write C API and unit tester - + Limit input to multiples of 64 bytes - + Replace GFSymbol with a file data pointer - + New 16-bit Muladd inner loops - + Class to contain the (large) muladd tables - + Preliminary benchmarks for large data! - + New 8-bit Muladd inner loops - + Benchmarks for smaller data! - + Refactor software - + Pick a name for the software better than LEO_RS - + I think it should be split up into several C++ modules - + Write detailed comments for all the routines - + Look into getting EncodeL working so we can support smaller data (Ask Lin) - + Look into using k instead of k2 to speed up decoder (Ask Lin) - + Avoid performing FFT/IFFT intermediate calculations we're not going to use - + Benchmarks, fun! - + Add multi-threading to split up long parallelizable calculations - + Final benchmarks! - + Finish up documentation - + Release version 1 + 16-bit Finite Field Math - - Muladd implementation notes: - - Specialize for 1-3 rows at a time since often times we're multiplying by - the same (skew) value repeatedly, as the ISA-L library does here: - - https://github.com/01org/isa-l/blob/master/erasure_code/gf_3vect_mad_avx.asm#L258 - - Except we should be doing it for 16-bit Galois Field. - To implement that use the ALTMAP trick from Jerasure: - - http://lab.jerasure.org/jerasure/gf-complete/blob/master/src/gf_w16.c#L1140 - - Except we should also support AVX2 since that is a 40% perf boost, so put - the high and low bytes 32 bytes instead of 16 bytes apart. - - Also I think we should go ahead and precompute the multiply tables since - it avoids a bunch of memory lookups for each muladd, and only costs 8 MB. + This finite field contains 65536 elements and so each element is one byte. + This library is designed for data that is a multiple of 64 bytes in size. */ - -//------------------------------------------------------------------------------ -// Debug - -// Some bugs only repro in release mode, so this can be helpful -//#define LEO_DEBUG_IN_RELEASE - -#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) - #define LEO_DEBUG - #ifdef _WIN32 - #define LEO_DEBUG_BREAK __debugbreak() - #else - #define LEO_DEBUG_BREAK __builtin_trap() - #endif - #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } -#else - #define LEO_DEBUG_BREAK ; - #define LEO_DEBUG_ASSERT(cond) ; -#endif +namespace leopard { namespace ff16 { //------------------------------------------------------------------------------ -// Platform/Architecture +// Datatypes and Constants -#if defined(ANDROID) || defined(IOS) - #define LEO_TARGET_MOBILE -#endif // ANDROID +// Finite field element type +typedef uint16_t ffe_t; -#if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) - #define LEO_TRY_AVX2 /* 256-bit */ - #include - #define LEO_ALIGN_BYTES 32 -#else // __AVX2__ - #define LEO_ALIGN_BYTES 16 -#endif // __AVX2__ +// Number of bits per element +static const unsigned kBits = 16; -#if !defined(LEO_TARGET_MOBILE) - // Note: MSVC currently only supports SSSE3 but not AVX2 - #include // SSSE3: _mm_shuffle_epi8 - #include // SSE2 -#endif // LEO_TARGET_MOBILE - -#if defined(HAVE_ARM_NEON_H) - #include -#endif // HAVE_ARM_NEON_H - -#if defined(LEO_TARGET_MOBILE) - - #define LEO_ALIGNED_ACCESSES /* Inputs must be aligned to LEO_ALIGN_BYTES */ - -# if defined(HAVE_ARM_NEON_H) - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 uint8x16_t - #define LEO_TRY_NEON -#else - #define LEO_M128 uint64_t -# endif - -#else // LEO_TARGET_MOBILE - - // Compiler-specific 128-bit SIMD register keyword - #define LEO_M128 __m128i - -#endif // LEO_TARGET_MOBILE - -#ifdef LEO_TRY_AVX2 - // Compiler-specific 256-bit SIMD register keyword - #define LEO_M256 __m256i -#endif - -// Compiler-specific C++11 restrict keyword -#define LEO_RESTRICT __restrict - -// Compiler-specific force inline keyword -#ifdef _MSC_VER - #define LEO_FORCE_INLINE inline __forceinline -#else - #define LEO_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -// Compiler-specific alignment keyword -// Note: Alignment only matters for ARM NEON where it should be 16 -#ifdef _MSC_VER - #define LEO_ALIGNED __declspec(align(LEO_ALIGN_BYTES)) -#else // _MSC_VER - #define LEO_ALIGNED __attribute__((aligned(LEO_ALIGN_BYTES))) -#endif // _MSC_VER +// Finite field order: Number of elements in the field +static const unsigned kOrder = 65536; //------------------------------------------------------------------------------ -// Runtime CPU Architecture Check -// -// Feature checks stolen shamelessly from -// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/runtime.c +// Fast Walsh-Hadamard Transform (FWHT) (mod kModulus) -#if defined(HAVE_ANDROID_GETCPUFEATURES) - #include -#endif +// Transform for a variable number of bits (up to kOrder) +void FWHT(ffe_t* data, const unsigned bits); -#if defined(LEO_TRY_NEON) -# if defined(IOS) && defined(__ARM_NEON__) - // Requires iPhone 5S or newer - static const bool CpuHasNeon = true; - static const bool CpuHasNeon64 = true; -# else - // Remember to add LOCAL_STATIC_LIBRARIES := cpufeatures - static bool CpuHasNeon = false; // V6 / V7 - static bool CpuHasNeon64 = false; // 64-bit -# endif -#endif - - -#if !defined(LEO_TARGET_MOBILE) - -#ifdef _MSC_VER - #include // __cpuid - #pragma warning(disable: 4752) // found Intel(R) Advanced Vector Extensions; consider using /arch:AVX -#endif - -#ifdef LEO_TRY_AVX2 -static bool CpuHasAVX2 = false; -#endif -static bool CpuHasSSSE3 = false; - -#define CPUID_EBX_AVX2 0x00000020 -#define CPUID_ECX_SSSE3 0x00000200 - -static void _cpuid(unsigned int cpu_info[4U], const unsigned int cpu_info_type) -{ -#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) - __cpuid((int *) cpu_info, cpu_info_type); -#else //if defined(HAVE_CPUID) - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; -# ifdef __i386__ - __asm__ __volatile__ ("pushfl; pushfl; " - "popl %0; " - "movl %0, %1; xorl %2, %0; " - "pushl %0; " - "popfl; pushfl; popl %0; popfl" : - "=&r" (cpu_info[0]), "=&r" (cpu_info[1]) : - "i" (0x200000)); - if (((cpu_info[0] ^ cpu_info[1]) & 0x200000) == 0) { - return; /* LCOV_EXCL_LINE */ - } -# endif -# ifdef __i386__ - __asm__ __volatile__ ("xchgl %%ebx, %k1; cpuid; xchgl %%ebx, %k1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# elif defined(__x86_64__) - __asm__ __volatile__ ("xchgq %%rbx, %q1; cpuid; xchgq %%rbx, %q1" : - "=a" (cpu_info[0]), "=&r" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# else - __asm__ __volatile__ ("cpuid" : - "=a" (cpu_info[0]), "=b" (cpu_info[1]), - "=c" (cpu_info[2]), "=d" (cpu_info[3]) : - "0" (cpu_info_type), "2" (0U)); -# endif -#endif -} - -#endif // defined(LEO_TARGET_MOBILE) - - -static void leo_architecture_init() -{ -#if defined(LEO_TRY_NEON) && defined(HAVE_ANDROID_GETCPUFEATURES) - AndroidCpuFamily family = android_getCpuFamily(); - if (family == ANDROID_CPU_FAMILY_ARM) - { - if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) - CpuHasNeon = true; - } - else if (family == ANDROID_CPU_FAMILY_ARM64) - { - CpuHasNeon = true; - if (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) - CpuHasNeon64 = true; - } -#endif - -#if !defined(LEO_TARGET_MOBILE) - unsigned int cpu_info[4]; - - _cpuid(cpu_info, 1); - CpuHasSSSE3 = ((cpu_info[2] & CPUID_ECX_SSSE3) != 0); - -#if defined(LEO_TRY_AVX2) - _cpuid(cpu_info, 7); - CpuHasAVX2 = ((cpu_info[1] & CPUID_EBX_AVX2) != 0); -#endif // LEO_TRY_AVX2 - -#endif // LEO_TARGET_MOBILE -} +// Transform specialized for the finite field order +void FWHT(ffe_t data[kOrder]); //------------------------------------------------------------------------------ -// SIMD-Safe Aligned Memory Allocations +// Multiplies -static const unsigned kAlignmentBytes = LEO_ALIGN_BYTES; +// x[] = y[] * m +void mul_mem_set( + void * LEO_RESTRICT x, const void * LEO_RESTRICT y, + ffe_t m, uint64_t bytes); -LEO_FORCE_INLINE unsigned NextAlignedOffset(unsigned offset) -{ - return (offset + kAlignmentBytes - 1) & ~(kAlignmentBytes - 1); -} - -static LEO_FORCE_INLINE uint8_t* SIMDSafeAllocate(size_t size) -{ - uint8_t* data = (uint8_t*)calloc(1, kAlignmentBytes + size); - if (!data) - return nullptr; - unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); - data += kAlignmentBytes - offset; - data[-1] = (uint8_t)offset; - return data; -} - -static LEO_FORCE_INLINE void SIMDSafeFree(void* ptr) -{ - if (!ptr) - return; - uint8_t* data = (uint8_t*)ptr; - unsigned offset = data[-1]; - if (offset >= kAlignmentBytes) - { - LEO_DEBUG_BREAK; // Should never happen - return; - } - data -= kAlignmentBytes - offset; - free(data); -} +// For i = {0, 1}: x_i[] *= m +void mul_mem2_inplace( + void * LEO_RESTRICT x_0, + void * LEO_RESTRICT x_1, + ffe_t m, uint64_t bytes); //------------------------------------------------------------------------------ -// Field +// FFT Operations -//#define LEO_SHORT_FIELD +// x[] ^= y[] * m, y[] ^= x[] +void fft_butterfly( + void * LEO_RESTRICT x, void * LEO_RESTRICT y, + ffe_t m, uint64_t bytes); -#ifdef LEO_SHORT_FIELD -typedef uint8_t GFSymbol; -static const unsigned kGFBits = 8; -static const unsigned kGFPolynomial = 0x11D; -GFSymbol kGFBasis[kGFBits] = { - 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis -}; -#else -typedef uint16_t GFSymbol; -static const unsigned kGFBits = 16; -static const unsigned kGFPolynomial = 0x1002D; -GFSymbol kGFBasis[kGFBits] = { - 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis - 0xC582, 0xED2E, 0x914C, 0x4012, - 0x6C98, 0x10D8, 0x6A72, 0xB900, - 0xFDB8, 0xFB34, 0xFF38, 0x991E -}; -#endif +// For i = {0, 1}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] +void fft_butterfly2( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + ffe_t m, uint64_t bytes); -/* - Cantor Basis introduced by: - D. G. Cantor, "On arithmetical algorithms over finite fields", - Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. -*/ - -static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size -static const unsigned kFieldModulus = kFieldSize - 1; - -static GFSymbol GFLog[kFieldSize]; -static GFSymbol GFExp[kFieldSize]; - -// Initialize GFLog[], GFExp[] -static void InitField() -{ - unsigned state = 1; - for (unsigned i = 0; i < kFieldModulus; ++i) - { - GFExp[state] = static_cast(i); - state <<= 1; - if (state >= kFieldSize) - state ^= kGFPolynomial; - } - GFExp[0] = kFieldModulus; - - // Conversion to chosen basis: - - GFLog[0] = 0; - for (unsigned i = 0; i < kGFBits; ++i) - { - const GFSymbol basis = kGFBasis[i]; - const unsigned width = (unsigned)(1UL << i); - - for (unsigned j = 0; j < width; ++j) - GFLog[j + width] = GFLog[j] ^ basis; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - GFLog[i] = GFExp[GFLog[i]]; - - for (unsigned i = 0; i < kFieldSize; ++i) - GFExp[GFLog[i]] = i; - - GFExp[kFieldModulus] = GFExp[0]; -} +// For i = {0, 1, 2}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] +void fft_butterfly3( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, + ffe_t m, uint64_t bytes); //------------------------------------------------------------------------------ -// Mod Q Field Operations -// -// Q is the maximum symbol value, e.g. 255 or 65535. +// IFFT Operations -// z = x + y (mod Q) -static inline GFSymbol AddModQ(GFSymbol a, GFSymbol b) -{ - const unsigned sum = (unsigned)a + b; +// y[] ^= x[], x[] ^= y[] * m +void ifft_butterfly( + void * LEO_RESTRICT x, void * LEO_RESTRICT y, + ffe_t m, uint64_t bytes); - // Partial reduction step, allowing for Q to be returned - return static_cast(sum + (sum >> kGFBits)); -} +// For i = {0, 1}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m +void ifft_butterfly2( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + ffe_t m, uint64_t bytes); -// z = x - y (mod Q) -static inline GFSymbol SubModQ(GFSymbol a, GFSymbol b) -{ - const unsigned dif = (unsigned)a - b; - - // Partial reduction step, allowing for Q to be returned - return static_cast(dif + (dif >> kGFBits)); -} - -// vx[] += vy[] * z -static void muladd_mem(GFSymbol * LEO_RESTRICT vx, const GFSymbol * LEO_RESTRICT vy, GFSymbol z, unsigned symbolCount) -{ - for (unsigned i = 0; i < symbolCount; ++i) - { - const GFSymbol a = vy[i]; - if (a == 0) - continue; - - GFSymbol sum1 = static_cast(AddModQ(GFLog[a & 0x0f], z)); - GFSymbol value1 = GFExp[sum1]; - if ((a & 0x0f) == 0) - { - value1 = 0; - } - GFSymbol sum2 = static_cast(AddModQ(GFLog[a & 0xf0], z)); - GFSymbol value2 = GFExp[sum2]; - if ((a & 0xf0) == 0) - { - value2 = 0; - } - GFSymbol sum3 = static_cast(AddModQ(GFLog[a & 0x0f00], z)); - GFSymbol value3 = GFExp[sum3]; - if ((a & 0x0f00) == 0) - { - value3 = 0; - } - GFSymbol sum4 = static_cast(AddModQ(GFLog[a & 0xf000], z)); - GFSymbol value4 = GFExp[sum4]; - if ((a & 0xf000) == 0) - { - value4 = 0; - } - - vx[i] ^= value1; - vx[i] ^= value2; - vx[i] ^= value3; - vx[i] ^= value4; - } -} - -// return a*GFExp[b] over GF(2^r) -static GFSymbol mulE(GFSymbol a, GFSymbol b) -{ - if (a == 0) - return 0; - - const GFSymbol sum = static_cast(AddModQ(GFLog[a], b)); - return GFExp[sum]; -} +// For i = {0, 1, 2}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m +void ifft_butterfly3( + void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, + void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, + void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, + ffe_t m, uint64_t bytes); //------------------------------------------------------------------------------ -// Fast Walsh-Hadamard Transform (FWHT) Mod Q -// -// Q is the maximum symbol value, e.g. 255 or 65535. +// Encode -// Define this to enable the optimized version of FWHT() -#define LEO_FWHT_OPTIMIZED - -typedef GFSymbol fwht_t; - -// {a, b} = {a + b, a - b} (Mod Q) -static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) -{ - const fwht_t sum = AddModQ(a, b); - const fwht_t dif = SubModQ(a, b); - a = sum; - b = dif; -} - -/* - FWHT is a minor slice of the runtime and does not grow with data size, - but I did attempt a few additional optimizations that failed: - - I've attempted to vectorize (with partial reductions) FWHT_4(data, s), - which is 70% of the algorithm, but it was slower. Left in _attic_. - - I've attempted to avoid reductions in all or parts of the FWHT. - The final modular reduction ends up being slower than the savings. - Specifically I tried doing it for the whole FWHT and also I tried - doing it just for the FWHT_2 loop in the main routine, but both - approaches are slower than partial reductions. - - Replacing word reads with wider reads does speed up the operation, but - at too high a complexity cost relative to minor perf improvement. -*/ - -#ifndef LEO_FWHT_OPTIMIZED - -// Reference implementation -static void FWHT(fwht_t* data, const unsigned bits) -{ - const unsigned size = (unsigned)(1UL << bits); - for (unsigned width = 1; width < size; width <<= 1) - for (unsigned i = 0; i < size; i += (width << 1)) - for (unsigned j = i; j < (width + i); ++j) - FWHT_2(data[j], data[j + width]); -} - -#else - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; -} - -static LEO_FORCE_INLINE void FWHT_4(fwht_t* data, unsigned s) -{ - unsigned x = 0; - fwht_t t0 = data[x]; x += s; - fwht_t t1 = data[x]; x += s; - fwht_t t2 = data[x]; x += s; - fwht_t t3 = data[x]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - unsigned y = 0; - data[y] = t0; y += s; - data[y] = t1; y += s; - data[y] = t2; y += s; - data[y] = t3; -} - -static inline void FWHT_8(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; -} - -static inline void FWHT_16(fwht_t* data) -{ - fwht_t t0 = data[0]; - fwht_t t1 = data[1]; - fwht_t t2 = data[2]; - fwht_t t3 = data[3]; - fwht_t t4 = data[4]; - fwht_t t5 = data[5]; - fwht_t t6 = data[6]; - fwht_t t7 = data[7]; - fwht_t t8 = data[8]; - fwht_t t9 = data[9]; - fwht_t t10 = data[10]; - fwht_t t11 = data[11]; - fwht_t t12 = data[12]; - fwht_t t13 = data[13]; - fwht_t t14 = data[14]; - fwht_t t15 = data[15]; - FWHT_2(t0, t1); - FWHT_2(t2, t3); - FWHT_2(t4, t5); - FWHT_2(t6, t7); - FWHT_2(t8, t9); - FWHT_2(t10, t11); - FWHT_2(t12, t13); - FWHT_2(t14, t15); - FWHT_2(t0, t2); - FWHT_2(t1, t3); - FWHT_2(t4, t6); - FWHT_2(t5, t7); - FWHT_2(t8, t10); - FWHT_2(t9, t11); - FWHT_2(t12, t14); - FWHT_2(t13, t15); - FWHT_2(t0, t4); - FWHT_2(t1, t5); - FWHT_2(t2, t6); - FWHT_2(t3, t7); - FWHT_2(t8, t12); - FWHT_2(t9, t13); - FWHT_2(t10, t14); - FWHT_2(t11, t15); - FWHT_2(t0, t8); - FWHT_2(t1, t9); - FWHT_2(t2, t10); - FWHT_2(t3, t11); - FWHT_2(t4, t12); - FWHT_2(t5, t13); - FWHT_2(t6, t14); - FWHT_2(t7, t15); - data[0] = t0; - data[1] = t1; - data[2] = t2; - data[3] = t3; - data[4] = t4; - data[5] = t5; - data[6] = t6; - data[7] = t7; - data[8] = t8; - data[9] = t9; - data[10] = t10; - data[11] = t11; - data[12] = t12; - data[13] = t13; - data[14] = t14; - data[15] = t15; -} - -static void FWHT_SmallData(fwht_t* data, unsigned ldn) -{ - const unsigned n = (1UL << ldn); - - if (n <= 2) - { - if (n == 2) - FWHT_2(data[0], data[1]); - return; - } - - for (unsigned ldm = ldn; ldm > 3; ldm -= 2) - { - unsigned m = (1UL << ldm); - unsigned m4 = (m >> 2); - for (unsigned r = 0; r < n; r += m) - for (unsigned j = 0; j < m4; j++) - FWHT_4(data + j + r, m4); - } - - if (ldn & 1) - { - for (unsigned i0 = 0; i0 < n; i0 += 8) - FWHT_8(data + i0); - } - else - { - for (unsigned i0 = 0; i0 < n; i0 += 4) - FWHT_4(data + i0); - } -} - -// Decimation in time (DIT) version -static void FWHT(fwht_t* data, const unsigned ldn) -{ - if (ldn <= 13) - { - FWHT_SmallData(data, ldn); - return; - } - - FWHT_2(data[2], data[3]); - FWHT_4(data + 4); - FWHT_8(data + 8); - FWHT_16(data + 16); - for (unsigned ldm = 5; ldm < ldn; ++ldm) - FWHT(data + (unsigned)(1UL << ldm), ldm); - - for (unsigned ldm = 0; ldm < ldn; ++ldm) - { - const unsigned mh = (1UL << ldm); - for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2) - FWHT_2(data[t1], data[t2]); - } -} - -#endif +void Encode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, // = NextPow2(recovery_count) * 2 = work_count + void* const * const data, + void** work); // Size of GetEncodeWorkCount() //------------------------------------------------------------------------------ -// Memory Buffer XOR +// Decode -static void xor_mem(void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, unsigned bytes) -{ - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - -#if defined(LEO_TARGET_MOBILE) -# if defined(LEO_TRY_NEON) - // Handle multiples of 64 bytes - if (CpuHasNeon) - { - while (bytes >= 64) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 x1 = vld1q_u8(x16 + 1); - LEO_M128 x2 = vld1q_u8(x16 + 2); - LEO_M128 x3 = vld1q_u8(x16 + 3); - LEO_M128 y0 = vld1q_u8(y16); - LEO_M128 y1 = vld1q_u8(y16 + 1); - LEO_M128 y2 = vld1q_u8(y16 + 2); - LEO_M128 y3 = vld1q_u8(y16 + 3); - - vst1q_u8(x16, veorq_u8(x0, y0)); - vst1q_u8(x16 + 1, veorq_u8(x1, y1)); - vst1q_u8(x16 + 2, veorq_u8(x2, y2)); - vst1q_u8(x16 + 3, veorq_u8(x3, y3)); - - bytes -= 64, x16 += 4, y16 += 4; - } - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - LEO_M128 x0 = vld1q_u8(x16); - LEO_M128 y0 = vld1q_u8(y16); - - vst1q_u8(x16, veorq_u8(x0, y0)); - - bytes -= 16, ++x16, ++y16; - } - } - else -# endif // LEO_TRY_NEON - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x16); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y16); - - const unsigned count = (unsigned)bytes / 8; - for (unsigned ii = 0; ii < count; ++ii) - x8[ii] ^= y8[ii]; - - x16 = reinterpret_cast(x8 + count); - y16 = reinterpret_cast(y8 + count); - } -#else // LEO_TARGET_MOBILE -# if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) - { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(x16); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(y16); - - while (bytes >= 128) - { - LEO_M256 x0 = _mm256_loadu_si256(x32); - LEO_M256 y0 = _mm256_loadu_si256(y32); - x0 = _mm256_xor_si256(x0, y0); - LEO_M256 x1 = _mm256_loadu_si256(x32 + 1); - LEO_M256 y1 = _mm256_loadu_si256(y32 + 1); - x1 = _mm256_xor_si256(x1, y1); - LEO_M256 x2 = _mm256_loadu_si256(x32 + 2); - LEO_M256 y2 = _mm256_loadu_si256(y32 + 2); - x2 = _mm256_xor_si256(x2, y2); - LEO_M256 x3 = _mm256_loadu_si256(x32 + 3); - LEO_M256 y3 = _mm256_loadu_si256(y32 + 3); - x3 = _mm256_xor_si256(x3, y3); - - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - - bytes -= 128, x32 += 4, y32 += 4; - } - - // Handle multiples of 32 bytes - while (bytes >= 32) - { - // x[i] = x[i] xor y[i] - _mm256_storeu_si256(x32, - _mm256_xor_si256( - _mm256_loadu_si256(x32), - _mm256_loadu_si256(y32))); - - bytes -= 32, ++x32, ++y32; - } - - x16 = reinterpret_cast(x32); - y16 = reinterpret_cast(y32); - } - else -# endif // LEO_TRY_AVX2 - { - while (bytes >= 64) - { - LEO_M128 x0 = _mm_loadu_si128(x16); - LEO_M128 y0 = _mm_loadu_si128(y16); - x0 = _mm_xor_si128(x0, y0); - LEO_M128 x1 = _mm_loadu_si128(x16 + 1); - LEO_M128 y1 = _mm_loadu_si128(y16 + 1); - x1 = _mm_xor_si128(x1, y1); - LEO_M128 x2 = _mm_loadu_si128(x16 + 2); - LEO_M128 y2 = _mm_loadu_si128(y16 + 2); - x2 = _mm_xor_si128(x2, y2); - LEO_M128 x3 = _mm_loadu_si128(x16 + 3); - LEO_M128 y3 = _mm_loadu_si128(y16 + 3); - x3 = _mm_xor_si128(x3, y3); - - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - - bytes -= 64, x16 += 4, y16 += 4; - } - } -#endif // LEO_TARGET_MOBILE - - // Handle multiples of 16 bytes - while (bytes >= 16) - { - // x[i] = x[i] xor y[i] - _mm_storeu_si128(x16, - _mm_xor_si128( - _mm_loadu_si128(x16), - _mm_loadu_si128(y16))); - - bytes -= 16, ++x16, ++y16; - } - - uint8_t * LEO_RESTRICT x1 = reinterpret_cast(x16); - const uint8_t * LEO_RESTRICT y1 = reinterpret_cast(y16); - - // Handle a block of 8 bytes - const unsigned eight = bytes & 8; - if (eight) - { - uint64_t * LEO_RESTRICT x8 = reinterpret_cast(x1); - const uint64_t * LEO_RESTRICT y8 = reinterpret_cast(y1); - *x8 ^= *y8; - } - - // Handle a block of 4 bytes - const unsigned four = bytes & 4; - if (four) - { - uint32_t * LEO_RESTRICT x4 = reinterpret_cast(x1 + eight); - const uint32_t * LEO_RESTRICT y4 = reinterpret_cast(y1 + eight); - *x4 ^= *y4; - } - - // Handle final bytes - const unsigned offset = eight + four; - switch (bytes & 3) - { - case 3: x1[offset + 2] ^= y1[offset + 2]; - case 2: x1[offset + 1] ^= y1[offset + 1]; - case 1: x1[offset] ^= y1[offset]; - default: - break; - } -} +void Decode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, // = NextPow2(recovery_count) + unsigned n, // = NextPow2(m + original_count) = work_count + void* const * const original, // original_count entries + void* const * const recovery, // recovery_count entries + void** work); // n entries //------------------------------------------------------------------------------ -// Formal Derivative +// API -// Formal derivative of polynomial in the new basis -static void formal_derivative(GFSymbol* cos, const unsigned size) -{ - for (unsigned i = 1; i < size; ++i) - { - const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; +// Returns false if the self-test fails +bool Initialize(); - // If a large number of values are being XORed: - if (leng >= 8) - xor_mem(cos + i - leng, cos + i, leng * sizeof(GFSymbol)); - else - for (unsigned j = i - leng; j < i; j++) - cos[j] ^= cos[j + leng]; - } - for (unsigned i = size; i < kFieldSize; i <<= 1) - xor_mem(cos, cos + i, size * sizeof(GFSymbol)); -} - - -//------------------------------------------------------------------------------ -// Fast Fourier Transform - -static GFSymbol skewVec[kFieldModulus]; // twisted factors used in FFT - -// IFFT in the proposed basis -static void IFLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = 1; depart_no < size; depart_no <<= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - } - } -} - -// FFT in the proposed basis -static void FLT(GFSymbol* data, const unsigned size, const unsigned index) -{ - for (unsigned depart_no = (size >> 1); depart_no > 0; depart_no >>= 1) - { - for (unsigned j = depart_no; j < size; j += (depart_no << 1)) - { - const GFSymbol skew = skewVec[j + index - 1]; - - if (skew != kFieldModulus) - muladd_mem(data + j - depart_no, data + j, skew, depart_no); - - // If a large number of values are being XORed: - if (depart_no >= 8) - xor_mem(data + j, data + j - depart_no, depart_no * sizeof(GFSymbol)); - else - for (unsigned i = j - depart_no; i < j; ++i) - data[i + depart_no] ^= data[i]; - } - } -} - - -//------------------------------------------------------------------------------ -// FFT Initialization - -static GFSymbol B[kFieldSize >> 1]; // factors used in formal derivative -static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial - -// Initialize skewVec[], B[], log_walsh[] -static void InitFieldOperations() -{ - GFSymbol temp[kGFBits - 1]; - - for (unsigned i = 1; i < kGFBits; ++i) - temp[i - 1] = (GFSymbol)((unsigned)1 << i); - - for (unsigned m = 0; m < (kGFBits - 1); ++m) - { - const unsigned step = (unsigned)1 << (m + 1); - - skewVec[((unsigned)1 << m) - 1] = 0; - - for (unsigned i = m; i < (kGFBits - 1); ++i) - { - const unsigned s = ((unsigned)1 << (i + 1)); - - for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) - skewVec[j + s] = skewVec[j] ^ temp[i]; - } - - temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; - - for (unsigned i = m + 1; i < (kGFBits - 1); ++i) - temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); - } - - for (unsigned i = 0; i < kFieldSize; ++i) - skewVec[i] = GFLog[skewVec[i]]; - - temp[0] = kFieldModulus - temp[0]; - - for (unsigned i = 1; i < (kGFBits - 1); ++i) - temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; - - B[0] = 0; - for (unsigned i = 0; i < (kGFBits - 1); ++i) - { - const unsigned depart = ((unsigned)1 << i); - - for (unsigned j = 0; j < depart; ++j) - B[j + depart] = (B[j] + temp[i]) % kFieldModulus; - } - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh[i] = GFLog[i]; - - log_walsh[0] = 0; - - FWHT(log_walsh, kGFBits); -} - - -//------------------------------------------------------------------------------ -// Encoder - -// Encoding alg for k/n<0.5: message is a power of two -static void encodeL(GFSymbol* data, const unsigned k, GFSymbol* codeword) -{ - memcpy(codeword, data, sizeof(GFSymbol) * k); - - IFLT(codeword, k, 0); - - for (unsigned i = k; i < kFieldSize; i += k) - { - memcpy(&codeword[i], codeword, sizeof(GFSymbol) * k); - - FLT(&codeword[i], k, i); - } - - memcpy(codeword, data, sizeof(GFSymbol) * k); -} - -// Encoding alg for k/n>0.5: parity is a power of two. -// data: message array. parity: parity array. mem: buffer(size>= n-k) -static void encodeH(const GFSymbol* data, const unsigned k, GFSymbol* parity, GFSymbol* mem) -{ - const unsigned t = kFieldSize - k; - - memset(parity, 0, sizeof(GFSymbol) * t); - - for (unsigned i = t; i < kFieldSize; i += t) - { - memcpy(mem, &data[i - t], sizeof(GFSymbol) * t); - - IFLT(mem, t, i); - - xor_mem(parity, mem, t * sizeof(GFSymbol)); - } - - FLT(parity, t, 0); -} - - -//------------------------------------------------------------------------------ -// Decoder - -static void decode(GFSymbol* codeword, unsigned k, const bool* erasure) -{ - fwht_t log_walsh2[kFieldSize]; - - // Compute the evaluations of the error locator polynomial - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = erasure[i] ? 1 : 0; - - FWHT(log_walsh2, kGFBits); - - for (unsigned i = 0; i < kFieldSize; ++i) - log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; - - FWHT(log_walsh2, kGFBits); - - // k2 can be replaced with k - const unsigned k2 = kFieldSize; - //const unsigned k2 = k; // cannot actually be replaced with k. what else need to change? - - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i]) - { - codeword[i] = 0; - } - else - { - codeword[i] = mulE(codeword[i], log_walsh2[i]); - } - } - - IFLT(codeword, kFieldSize, 0); - - // formal derivative - for (unsigned i = 0; i < kFieldSize; i += 2) - { - codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); - } - - formal_derivative(codeword, k2); - - for (unsigned i = 0; i < k2; i += 2) - { - codeword[i] = mulE(codeword[i], B[i >> 1]); - codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); - } - - FLT(codeword, k2, 0); - - for (unsigned i = 0; i < k2; ++i) - { - if (erasure[i]) - { - codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); - } - } -} - - -//------------------------------------------------------------------------------ -// Test Application - -void test(unsigned k, unsigned seed) -{ - srand(seed); - - //-----------Generating message---------- - - // Message array - GFSymbol data[kFieldSize] = {0}; - - // Filled with random numbers - for (unsigned i = kFieldSize - k; i < kFieldSize; ++i) - data[i] = (GFSymbol)rand(); - - - //---------encoding---------- - - GFSymbol codeword[kFieldSize]; - encodeH(&data[kFieldSize - k], k, data, codeword); - //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? - - memcpy(codeword, data, sizeof(GFSymbol) * kFieldSize); - - - //--------erasure simulation--------- - - // Array indicating erasures - bool erasure[kFieldSize] = { - false - }; - - for (unsigned i = k; i < kFieldSize; ++i) - erasure[i] = true; - - // permuting the erasure array - for (unsigned i = kFieldSize - 1; i > 0; --i) - { - unsigned pos = rand() % (i + 1); - - if (i != pos) - { - bool tmp = erasure[i]; - erasure[i] = erasure[pos]; - erasure[pos] = tmp; - } - } - - // erasure codeword symbols - for (unsigned i = 0; i < kFieldSize; ++i) - if (erasure[i]) - codeword[i] = 0; - - - //---------main processing---------- - decode(codeword, k, erasure); - - // Check the correctness of the result - for (unsigned i = 0; i < kFieldSize; ++i) - { - if (erasure[i] == 1) - { - if (data[i] != codeword[i]) - { - printf("Decoding Error with seed = %d!\n", seed); - LEO_DEBUG_BREAK; - return; - } - } - } - - //printf("Decoding is successful!\n"); -} - - -//------------------------------------------------------------------------------ -// Entrypoint - -int main(int argc, char **argv) -{ - // Initialize architecture-specific code - leo_architecture_init(); - - // Fill GFLog table and GFExp table - InitField(); - - // Compute factors used in erasure decoder - InitFieldOperations(); - - unsigned seed = (unsigned)time(NULL); - for (;;) - { - // test(int k), k: message size - /* - EncodeH works for kFieldSize / 2 and kFieldSize * 3 / 4, etc, - s.t. the number of recovery pieces is a power of two - */ - test(kFieldSize / 2, seed); - - ++seed; - } - - return 0; -} +}} // namespace leopard::ff16 diff --git a/LeopardFF8.cpp b/LeopardFF8.cpp index 030a555..1e7d7cd 100644 --- a/LeopardFF8.cpp +++ b/LeopardFF8.cpp @@ -9,7 +9,7 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of LHC-RS nor the names of its contributors may be + * Neither the name of Leopard-RS nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -27,6 +27,10 @@ */ #include "LeopardFF8.h" +#include + +// Define this to enable the optimized version of FWHT() +#define LEO_FF8_FWHT_OPTIMIZED namespace leopard { namespace ff8 { @@ -34,6 +38,9 @@ namespace leopard { namespace ff8 { //------------------------------------------------------------------------------ // Datatypes and Constants +// Modulus for field operations +static const ffe_t kModulus = 255; + // LFSR Polynomial that generates the field elements static const unsigned kPolynomial = 0x11D; @@ -47,9 +54,6 @@ static const ffe_t kBasis[kBits] = { //------------------------------------------------------------------------------ // Field Operations -// Modulus for field operations -static const ffe_t kModulus = 255; - // z = x + y (mod kModulus) static inline ffe_t AddMod(const ffe_t a, const ffe_t b) { @@ -69,50 +73,6 @@ static inline ffe_t SubMod(const ffe_t a, const ffe_t b) } -//------------------------------------------------------------------------------ -// Logarithm Tables - -static ffe_t LogLUT[kOrder]; -static ffe_t ExpLUT[kOrder]; - - -// Initialize LogLUT[], ExpLUT[] -static void InitializeLogarithmTables() -{ - // LFSR table generation: - - unsigned state = 1; - for (unsigned i = 0; i < kModulus; ++i) - { - ExpLUT[state] = static_cast(i); - state <<= 1; - if (state >= kOrder) - state ^= kPolynomial; - } - ExpLUT[0] = kModulus; - - // Conversion to chosen basis: - - LogLUT[0] = 0; - for (unsigned i = 0; i < kBits; ++i) - { - const ffe_t basis = kBasis[i]; - const unsigned width = static_cast(1UL << i); - - for (unsigned j = 0; j < width; ++j) - LogLUT[j + width] = LogLUT[j] ^ basis; - } - - for (unsigned i = 0; i < kOrder; ++i) - LogLUT[i] = ExpLUT[LogLUT[i]]; - - for (unsigned i = 0; i < kOrder; ++i) - ExpLUT[LogLUT[i]] = i; - - ExpLUT[kModulus] = ExpLUT[0]; -} - - //------------------------------------------------------------------------------ // Fast Walsh-Hadamard Transform (FWHT) (mod kModulus) @@ -248,234 +208,47 @@ void FWHT(ffe_t data[kOrder]) //------------------------------------------------------------------------------ -// XOR Memory +// Logarithm Tables -void xor_mem( - void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, - unsigned bytes) +static ffe_t LogLUT[kOrder]; +static ffe_t ExpLUT[kOrder]; + + +// Initialize LogLUT[], ExpLUT[] +static void InitializeLogarithmTables() { -#if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) - { - LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast(vx); - const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast(vy); - do - { - const LEO_M256 x0 = _mm256_xor_si256(_mm256_loadu_si256(x32), _mm256_loadu_si256(y32)); - const LEO_M256 x1 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 1), _mm256_loadu_si256(y32 + 1)); - const LEO_M256 x2 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 2), _mm256_loadu_si256(y32 + 2)); - const LEO_M256 x3 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 3), _mm256_loadu_si256(y32 + 3)); - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - _mm256_storeu_si256(x32 + 2, x2); - _mm256_storeu_si256(x32 + 3, x3); - bytes -= 128, x32 += 4, y32 += 4; - } while (bytes >= 128); - if (bytes > 0) - { - const LEO_M256 x0 = _mm256_xor_si256(_mm256_loadu_si256(x32), _mm256_loadu_si256(y32)); - const LEO_M256 x1 = _mm256_xor_si256(_mm256_loadu_si256(x32 + 1), _mm256_loadu_si256(y32 + 1)); - _mm256_storeu_si256(x32, x0); - _mm256_storeu_si256(x32 + 1, x1); - } - return; - } -#endif // LEO_TRY_AVX2 - LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast(vx); - const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast(vy); - do - { - const LEO_M128 x0 = _mm_xor_si128(_mm_loadu_si128(x16), _mm_loadu_si128(y16)); - const LEO_M128 x1 = _mm_xor_si128(_mm_loadu_si128(x16 + 1), _mm_loadu_si128(y16 + 1)); - const LEO_M128 x2 = _mm_xor_si128(_mm_loadu_si128(x16 + 2), _mm_loadu_si128(y16 + 2)); - const LEO_M128 x3 = _mm_xor_si128(_mm_loadu_si128(x16 + 3), _mm_loadu_si128(y16 + 3)); - _mm_storeu_si128(x16, x0); - _mm_storeu_si128(x16 + 1, x1); - _mm_storeu_si128(x16 + 2, x2); - _mm_storeu_si128(x16 + 3, x3); - bytes -= 64, x16 += 4, y16 += 4; - } while (bytes > 0); -} + // LFSR table generation: -void xor_mem2( - void * LEO_RESTRICT vx_0, const void * LEO_RESTRICT vy_0, - void * LEO_RESTRICT vx_1, const void * LEO_RESTRICT vy_1, - unsigned bytes) -{ -#if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) + unsigned state = 1; + for (unsigned i = 0; i < kModulus; ++i) { - LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast (vx_0); - const LEO_M256 * LEO_RESTRICT y32_0 = reinterpret_cast(vy_0); - LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast (vx_1); - const LEO_M256 * LEO_RESTRICT y32_1 = reinterpret_cast(vy_1); - do - { - const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); - const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); - const LEO_M256 x2_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 2), _mm256_loadu_si256(y32_0 + 2)); - const LEO_M256 x3_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 3), _mm256_loadu_si256(y32_0 + 3)); - const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); - const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); - const LEO_M256 x2_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 2), _mm256_loadu_si256(y32_1 + 2)); - const LEO_M256 x3_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 3), _mm256_loadu_si256(y32_1 + 3)); - _mm256_storeu_si256(x32_0, x0_0); - _mm256_storeu_si256(x32_0 + 1, x1_0); - _mm256_storeu_si256(x32_0 + 2, x2_0); - _mm256_storeu_si256(x32_0 + 3, x3_0); - _mm256_storeu_si256(x32_1, x0_1); - _mm256_storeu_si256(x32_1 + 1, x1_1); - _mm256_storeu_si256(x32_1 + 2, x2_1); - _mm256_storeu_si256(x32_1 + 3, x3_1); - x32_0 += 4, y32_0 += 4; - x32_1 += 4, y32_1 += 4; - bytes -= 128; - } while (bytes >= 128); - if (bytes > 0) - { - const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); - const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); - const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); - const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); - _mm256_storeu_si256(x32_0, x0_0); - _mm256_storeu_si256(x32_0 + 1, x1_0); - _mm256_storeu_si256(x32_1, x0_1); - _mm256_storeu_si256(x32_1 + 1, x1_1); - } - return; + ExpLUT[state] = static_cast(i); + state <<= 1; + if (state >= kOrder) + state ^= kPolynomial; } -#endif // LEO_TRY_AVX2 - LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast (vx_0); - const LEO_M128 * LEO_RESTRICT y16_0 = reinterpret_cast(vy_0); - LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast (vx_1); - const LEO_M128 * LEO_RESTRICT y16_1 = reinterpret_cast(vy_1); - do - { - const LEO_M128 x0_0 = _mm_xor_si128(_mm_loadu_si128(x16_0), _mm_loadu_si128(y16_0)); - const LEO_M128 x1_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 1), _mm_loadu_si128(y16_0 + 1)); - const LEO_M128 x2_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 2), _mm_loadu_si128(y16_0 + 2)); - const LEO_M128 x3_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 3), _mm_loadu_si128(y16_0 + 3)); - const LEO_M128 x0_1 = _mm_xor_si128(_mm_loadu_si128(x16_1), _mm_loadu_si128(y16_1)); - const LEO_M128 x1_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 1), _mm_loadu_si128(y16_1 + 1)); - const LEO_M128 x2_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 2), _mm_loadu_si128(y16_1 + 2)); - const LEO_M128 x3_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 3), _mm_loadu_si128(y16_1 + 3)); - _mm_storeu_si128(x16_0, x0_0); - _mm_storeu_si128(x16_0 + 1, x1_0); - _mm_storeu_si128(x16_0 + 2, x2_0); - _mm_storeu_si128(x16_0 + 3, x3_0); - _mm_storeu_si128(x16_1, x0_1); - _mm_storeu_si128(x16_1 + 1, x1_1); - _mm_storeu_si128(x16_1 + 2, x2_1); - _mm_storeu_si128(x16_1 + 3, x3_1); - x16_0 += 4, y16_0 += 4; - x16_1 += 4, y16_1 += 4; - bytes -= 64; - } while (bytes > 0); -} + ExpLUT[0] = kModulus; -void xor_mem3( - void * LEO_RESTRICT vx_0, const void * LEO_RESTRICT vy_0, - void * LEO_RESTRICT vx_1, const void * LEO_RESTRICT vy_1, - void * LEO_RESTRICT vx_2, const void * LEO_RESTRICT vy_2, - unsigned bytes) -{ -#if defined(LEO_TRY_AVX2) - if (CpuHasAVX2) + // Conversion to chosen basis: + + LogLUT[0] = 0; + for (unsigned i = 0; i < kBits; ++i) { - LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast (vx_0); - const LEO_M256 * LEO_RESTRICT y32_0 = reinterpret_cast(vy_0); - LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast (vx_1); - const LEO_M256 * LEO_RESTRICT y32_1 = reinterpret_cast(vy_1); - LEO_M256 * LEO_RESTRICT x32_2 = reinterpret_cast (vx_2); - const LEO_M256 * LEO_RESTRICT y32_2 = reinterpret_cast(vy_2); - do - { - const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); - const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); - const LEO_M256 x2_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 2), _mm256_loadu_si256(y32_0 + 2)); - const LEO_M256 x3_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 3), _mm256_loadu_si256(y32_0 + 3)); - const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); - const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); - const LEO_M256 x2_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 2), _mm256_loadu_si256(y32_1 + 2)); - const LEO_M256 x3_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 3), _mm256_loadu_si256(y32_1 + 3)); - const LEO_M256 x0_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2), _mm256_loadu_si256(y32_2)); - const LEO_M256 x1_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 1), _mm256_loadu_si256(y32_2 + 1)); - const LEO_M256 x2_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 2), _mm256_loadu_si256(y32_2 + 2)); - const LEO_M256 x3_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 3), _mm256_loadu_si256(y32_2 + 3)); - _mm256_storeu_si256(x32_0, x0_0); - _mm256_storeu_si256(x32_0 + 1, x1_0); - _mm256_storeu_si256(x32_0 + 2, x2_0); - _mm256_storeu_si256(x32_0 + 3, x3_0); - _mm256_storeu_si256(x32_1, x0_1); - _mm256_storeu_si256(x32_1 + 1, x1_1); - _mm256_storeu_si256(x32_1 + 2, x2_1); - _mm256_storeu_si256(x32_1 + 3, x3_1); - _mm256_storeu_si256(x32_2, x0_2); - _mm256_storeu_si256(x32_2 + 1, x1_2); - _mm256_storeu_si256(x32_2 + 2, x2_2); - _mm256_storeu_si256(x32_2 + 3, x3_2); - x32_0 += 4, y32_0 += 4; - x32_1 += 4, y32_1 += 4; - x32_2 += 4, y32_2 += 4; - bytes -= 128; - } while (bytes >= 128); - if (bytes > 0) - { - const LEO_M256 x0_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0), _mm256_loadu_si256(y32_0)); - const LEO_M256 x1_0 = _mm256_xor_si256(_mm256_loadu_si256(x32_0 + 1), _mm256_loadu_si256(y32_0 + 1)); - const LEO_M256 x0_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1), _mm256_loadu_si256(y32_1)); - const LEO_M256 x1_1 = _mm256_xor_si256(_mm256_loadu_si256(x32_1 + 1), _mm256_loadu_si256(y32_1 + 1)); - const LEO_M256 x0_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2), _mm256_loadu_si256(y32_2)); - const LEO_M256 x1_2 = _mm256_xor_si256(_mm256_loadu_si256(x32_2 + 1), _mm256_loadu_si256(y32_2 + 1)); - _mm256_storeu_si256(x32_0, x0_0); - _mm256_storeu_si256(x32_0 + 1, x1_0); - _mm256_storeu_si256(x32_1, x0_1); - _mm256_storeu_si256(x32_1 + 1, x1_1); - _mm256_storeu_si256(x32_2, x0_2); - _mm256_storeu_si256(x32_2 + 1, x1_2); - } - return; + const ffe_t basis = kBasis[i]; + const unsigned width = static_cast(1UL << i); + + for (unsigned j = 0; j < width; ++j) + LogLUT[j + width] = LogLUT[j] ^ basis; } -#endif // LEO_TRY_AVX2 - LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast (vx_0); - const LEO_M128 * LEO_RESTRICT y16_0 = reinterpret_cast(vy_0); - LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast (vx_1); - const LEO_M128 * LEO_RESTRICT y16_1 = reinterpret_cast(vy_1); - LEO_M128 * LEO_RESTRICT x16_2 = reinterpret_cast (vx_2); - const LEO_M128 * LEO_RESTRICT y16_2 = reinterpret_cast(vy_2); - do - { - const LEO_M128 x0_0 = _mm_xor_si128(_mm_loadu_si128(x16_0), _mm_loadu_si128(y16_0)); - const LEO_M128 x1_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 1), _mm_loadu_si128(y16_0 + 1)); - const LEO_M128 x2_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 2), _mm_loadu_si128(y16_0 + 2)); - const LEO_M128 x3_0 = _mm_xor_si128(_mm_loadu_si128(x16_0 + 3), _mm_loadu_si128(y16_0 + 3)); - const LEO_M128 x0_1 = _mm_xor_si128(_mm_loadu_si128(x16_1), _mm_loadu_si128(y16_1)); - const LEO_M128 x1_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 1), _mm_loadu_si128(y16_1 + 1)); - const LEO_M128 x2_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 2), _mm_loadu_si128(y16_1 + 2)); - const LEO_M128 x3_1 = _mm_xor_si128(_mm_loadu_si128(x16_1 + 3), _mm_loadu_si128(y16_1 + 3)); - const LEO_M128 x0_2 = _mm_xor_si128(_mm_loadu_si128(x16_2), _mm_loadu_si128(y16_2)); - const LEO_M128 x1_2 = _mm_xor_si128(_mm_loadu_si128(x16_2 + 1), _mm_loadu_si128(y16_2 + 1)); - const LEO_M128 x2_2 = _mm_xor_si128(_mm_loadu_si128(x16_2 + 2), _mm_loadu_si128(y16_2 + 2)); - const LEO_M128 x3_2 = _mm_xor_si128(_mm_loadu_si128(x16_2 + 3), _mm_loadu_si128(y16_2 + 3)); - _mm_storeu_si128(x16_0, x0_0); - _mm_storeu_si128(x16_0 + 1, x1_0); - _mm_storeu_si128(x16_0 + 2, x2_0); - _mm_storeu_si128(x16_0 + 3, x3_0); - _mm_storeu_si128(x16_1, x0_1); - _mm_storeu_si128(x16_1 + 1, x1_1); - _mm_storeu_si128(x16_1 + 2, x2_1); - _mm_storeu_si128(x16_1 + 3, x3_1); - _mm_storeu_si128(x16_2, x0_2); - _mm_storeu_si128(x16_2 + 1, x1_2); - _mm_storeu_si128(x16_2 + 2, x2_2); - _mm_storeu_si128(x16_2 + 3, x3_2); - x16_0 += 4, y16_0 += 4; - x16_1 += 4, y16_1 += 4; - x16_2 += 4, y16_2 += 4; - bytes -= 64; - } while (bytes > 0); -} + for (unsigned i = 0; i < kOrder; ++i) + LogLUT[i] = ExpLUT[LogLUT[i]]; + + for (unsigned i = 0; i < kOrder; ++i) + ExpLUT[LogLUT[i]] = i; + + ExpLUT[kModulus] = ExpLUT[0]; +} //------------------------------------------------------------------------------ // Multiplies @@ -485,12 +258,12 @@ void xor_mem3( struct { LEO_ALIGNED LEO_M128 Lo[256]; LEO_ALIGNED LEO_M128 Hi[256]; -} Multiply128LUT; +} static Multiply128LUT; #if defined(LEO_TRY_AVX2) struct { LEO_ALIGNED LEO_M256 Lo[256]; LEO_ALIGNED LEO_M256 Hi[256]; -} Multiply256LUT; +} static Multiply256LUT; #endif // LEO_TRY_AVX2 // Returns a * b @@ -501,14 +274,19 @@ static ffe_t FFEMultiply(ffe_t a, ffe_t b) return ExpLUT[AddMod(LogLUT[a], LogLUT[b])]; } +// Returns a * Log(b) +static ffe_t FFEMultiplyLog(ffe_t a, ffe_t log_b) +{ + if (a == 0) + return 0; + return ExpLUT[AddMod(LogLUT[a], b)]; +} + bool InitializeMultiplyTables() { - // Reuse aligned self test buffers to load table data - uint8_t* lo = m_SelfTestBuffers.A; - uint8_t* hi = m_SelfTestBuffers.B; - for (int y = 0; y < 256; ++y) { + uint8_t lo[16], hi[16]; for (unsigned char x = 0; x < 16; ++x) { lo[x] = FFEMultiply(x, static_cast(y)); @@ -517,15 +295,17 @@ bool InitializeMultiplyTables() const LEO_M128 table_lo = _mm_loadu_si128((LEO_M128*)lo); const LEO_M128 table_hi = _mm_loadu_si128((LEO_M128*)hi); + _mm_storeu_si128(Multiply128LUT.Lo + y, table_lo); _mm_storeu_si128(Multiply128LUT.Hi + y, table_hi); + #if defined(LEO_TRY_AVX2) if (CpuHasAVX2) { - const LEO_M256 table_lo2 = _mm256_broadcastsi128_si256(table_lo); - const LEO_M256 table_hi2 = _mm256_broadcastsi128_si256(table_hi); - _mm256_storeu_si256(Multiply256LUT.Lo + y, table_lo2); - _mm256_storeu_si256(Multiply256LUT.Hi + y, table_hi2); + _mm256_storeu_si256(Multiply256LUT.Lo + y, + _mm256_broadcastsi128_si256(table_lo)); + _mm256_storeu_si256(Multiply256LUT.Hi + y, + _mm256_broadcastsi128_si256(table_hi)); } #endif // LEO_TRY_AVX2 } @@ -536,7 +316,7 @@ bool InitializeMultiplyTables() // vx[] = vy[] * m void mul_mem_set( void * LEO_RESTRICT vx, const void * LEO_RESTRICT vy, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { if (m <= 1) { @@ -633,7 +413,7 @@ void mul_mem_set( void mul_mem2_inplace( void * LEO_RESTRICT vx_0, void * LEO_RESTRICT vx_1, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { if (m <= 1) { @@ -759,28 +539,28 @@ void mul_mem2_inplace( // FFT Operations // x[] ^= y[] * m, y[] ^= x[] -void mul_fft( +void fft_butterfly( void * LEO_RESTRICT x, void * LEO_RESTRICT y, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { } // For i = {0, 1}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] -void mul_fft2( +void fft_butterfly2( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { } // For i = {0, 1, 2}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] -void mul_fft3( +void fft_butterfly3( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { } @@ -790,33 +570,348 @@ void mul_fft3( // IFFT Operations // y[] ^= x[], x[] ^= y[] * m -void mul_ifft( +void ifft_butterfly( void * LEO_RESTRICT x, void * LEO_RESTRICT y, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { } // For i = {0, 1}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m -void mul_ifft2( +void ifft_butterfly2( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { } // For i = {0, 1, 2}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m -void mul_ifft3( +void ifft_butterfly3( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, - ffe_t m, unsigned bytes) + ffe_t m, uint64_t bytes) { } +//------------------------------------------------------------------------------ +// FFT + +static ffe_t FFTSkew[kFieldModulus]; // twisted factors used in FFT +static ffe_t LogWalsh[kOrder]; // factors used in the evaluation of the error locator polynomial + +void FFTInitialize() +{ + ffe_t temp[kBits - 1]; + + for (unsigned i = 1; i < kBits; ++i) + temp[i - 1] = (ffe_t)((unsigned)1 << i); + + for (unsigned m = 0; m < (kBits - 1); ++m) + { + const unsigned step = (unsigned)1 << (m + 1); + + FFTSkew[((unsigned)1 << m) - 1] = 0; + + for (unsigned i = m; i < (kBits - 1); ++i) + { + const unsigned s = ((unsigned)1 << (i + 1)); + + for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) + FFTSkew[j + s] = FFTSkew[j] ^ temp[i]; + } + + // TBD: This can be cleaned up + temp[m] = kFieldModulus - LogLUT[FFEMultiply(temp[m], temp[m] ^ 1)]; + + for (unsigned i = m + 1; i < (kBits - 1); ++i) + temp[i] = FFEMultiplyLog(temp[i], (LogLUT[temp[i] ^ 1] + temp[m]) % kFieldModulus); + } + + for (unsigned i = 0; i < kOrder; ++i) + FFTSkew[i] = LogLUT[FFTSkew[i]]; + + // Precalculate FWHT(Log[i]): + + for (unsigned i = 0; i < kOrder; ++i) + LogWalsh[i] = LogLUT[i]; + LogWalsh[0] = 0; + FWHT(LogWalsh, kBits); +} + + +//------------------------------------------------------------------------------ +// Encode + +void Encode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, + void* const * const data, + void** work) +{ + // work <- data + + // FIXME: Unroll first loop to eliminate this + for (unsigned i = 0; i < m; ++i) + memcpy(work[i], data[i], buffer_bytes); + + // work <- IFFT(data, m, m) + + for (unsigned width = 1; width < m; width <<= 1) + { + for (unsigned j = width; j < m; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j + m - 1]; + + if (skew != kFieldModulus) + { + for (unsigned i = j - width; i < j; ++i) + ifft_butterfly(work[i], work[i + width], skew, buffer_bytes); + } + else + { + for (unsigned i = j - width; i < j; ++i) + xor_mem(work[i + width], work[i], buffer_bytes); + } + } + } + + for (unsigned i = m; i + m <= original_count; i += m) + { + // temp <- data + i + + void** temp = work + m; + + // FIXME: Unroll first loop to eliminate this + for (unsigned j = 0; j < m; ++j) + memcpy(temp[j], data[j], buffer_bytes); + + // temp <- IFFT(temp, m, m + i) + + for (unsigned width = 1; width < m; width <<= 1) + { + for (unsigned j = width; j < m; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j + m + i - 1]; + + if (skew != kFieldModulus) + { + for (unsigned k = j - width; k < j; ++k) + ifft_butterfly(temp[k], temp[k + width], skew, buffer_bytes); + } + else + { + for (unsigned k = j - width; k < j; ++k) + xor_mem(temp[k + width], temp[k], buffer_bytes); + } + } + } + + // work <- work XOR temp + + // FIXME: Unroll last loop to eliminate this + for (unsigned j = 0; j < m; ++j) + xor_mem(work[j], temp[j], buffer_bytes); + } + + const unsigned last_count = original_count % m; + if (last_count != 0) + { + const unsigned i = original_count - last_count; + + // temp <- data + i + + void** temp = work + m; + + for (unsigned j = 0; j < last_count; ++j) + memcpy(temp[j], data[j], buffer_bytes); + for (unsigned j = last_count; j < m; ++j) + memset(temp[j], 0, buffer_bytes); + + // temp <- IFFT(temp, m, m + i) + + for (unsigned width = 1, shift = 1; width < m; width <<= 1, ++shift) + { + // Calculate stop considering that the right is all zeroes + const unsigned stop = ((last_count + width - 1) >> shift) << shift; + + for (unsigned j = width; j < stop; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j + m + i - 1]; + + if (skew != kFieldModulus) + { + for (unsigned k = j - width; k < j; ++k) + ifft_butterfly(temp[k], temp[k + width], skew, buffer_bytes); + } + else + { + for (unsigned k = j - width; k < j; ++k) + xor_mem(temp[k + width], temp[k], buffer_bytes); + } + } + } + + // work <- work XOR temp + + // FIXME: Unroll last loop to eliminate this + for (unsigned j = 0; j < m; ++j) + xor_mem(work[j], temp[j], buffer_bytes); + } + + // work <- FFT(work, m, 0) + + for (unsigned width = (m >> 1); width > 0; width >>= 1) + { + const ffe_t* skewLUT = FFTSkew + width - 1; + const unsigned range = width << 1; + + for (unsigned j = 0; j < m; j += range) + { + const ffe_t skew = skewLUT[j]; + + if (skew != kFieldModulus) + { + for (unsigned k = j, count = j + width; k < count; ++k) + fft_butterfly(data[k], data[k + width], skew, buffer_bytes); + } + else + { + for (unsigned k = j, count = j + width; k < count; ++k) + xor_mem(work[k + width], work[k], buffer_bytes); + } + } + } +} + + +//------------------------------------------------------------------------------ +// Decode + +void Decode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, // NextPow2(recovery_count) + unsigned n, // NextPow2(m + original_count) = work_count + void* const * const original, // original_count entries + void* const * const recovery, // recovery_count entries + void** work) // n entries +{ + // Fill in error locations + + ffe_t ErrorLocations[kOrder]; + for (unsigned i = 0; i < recovery_count; ++i) + ErrorLocations[i] = recovery[i] ? 0 : 1; + for (unsigned i = recovery_count; i < m; ++i) + ErrorLocations[i] = 1; + for (unsigned i = 0; i < original_count; ++i) + ErrorLocations[i + m] = original[i] ? 0 : 1; + memset(ErrorLocations + m + original_count, 0, (n - original_count - m) * sizeof(ffe_t)); + + // Evaluate error locator polynomial + + FWHT(ErrorLocations, kBits); + + for (unsigned i = 0; i < kOrder; ++i) + ErrorLocations[i] = ((unsigned)ErrorLocations[i] * (unsigned)LogWalsh[i]) % kFieldModulus; + + FWHT(ErrorLocations, kBits); + + // work <- recovery data + + for (unsigned i = 0; i < recovery_count; ++i) + { + if (recovery[i]) + mul_mem_set(work[i], recovery[i], ErrorLocations[i], buffer_bytes); + else + memset(work[i], 0, buffer_bytes); + } + for (unsigned i = recovery_count; i < m; ++i) + memset(work[i], 0, buffer_bytes); + + // work <- original data + + for (unsigned i = 0; i < original_count; ++i) + { + if (original[i]) + mul_mem_set(work[m + i], original[i], ErrorLocations[m + i], buffer_bytes); + else + memset(work[m + i], 0, buffer_bytes); + } + for (unsigned i = m + original_count; i < n; ++i) + memset(work[i], 0, buffer_bytes); + + // work <- IFFT(work, n, 0) + + for (unsigned width = 1; width < n; width <<= 1) + { + for (unsigned j = width; j < n; j += (width << 1)) + { + const ffe_t skew = FFTSkew[j - 1]; + + if (skew != kFieldModulus) + { + for (unsigned i = j - width; i < j; ++i) + ifft_butterfly(work[i], work[i + width], skew, buffer_bytes); + } + else + { + for (unsigned i = j - width; i < j; ++i) + xor_mem(work[i + width], work[i], buffer_bytes); + } + } + } + + // work <- FormalDerivative(work, n) + + for (unsigned i = 1; i < n; ++i) + { + const unsigned width = ((i ^ (i - 1)) + 1) >> 1; + + // If a large number of values are being XORed: + for (unsigned j = i - width; j < i; ++j) + xor_mem(work[j], work[j + width], buffer_bytes); + } + + // work <- FFT(work, n, 0) truncated to m + original_count + + const unsigned output_count = m + original_count; + for (unsigned width = (n >> 1); width > 0; width >>= 1) + { + const ffe_t* skewLUT = FFTSkew + width - 1; + const unsigned range = width << 1; + + for (unsigned j = (m < range) ? 0 : m; j < output_count; j += range) + { + const ffe_t skew = skewLUT[j]; + + if (skew != kFieldModulus) + { + for (unsigned i = j; i < j + width; ++i) + fft_butterfly(work[i], work[i + width], skew, buffer_bytes); + } + else + { + for (unsigned i = j; i < j + width; ++i) + xor_mem(work[i + width], work[i], buffer_bytes); + } + } + } + + // Reveal erasures + + for (unsigned i = 0; i < original_count; ++i) + if (!original[i]) + mul_mem_set(work[i], work[i + m], kFieldModulus - ErrorLocations[i], buffer_bytes); +} + + //------------------------------------------------------------------------------ // API @@ -831,6 +926,7 @@ bool Initialize() return false; InitializeLogarithmTables(); + FFTInitialize(); IsInitialized = true; return true; diff --git a/LeopardFF8.h b/LeopardFF8.h index 1ef933b..88efa3f 100644 --- a/LeopardFF8.h +++ b/LeopardFF8.h @@ -56,9 +56,6 @@ static const unsigned kOrder = 256; //------------------------------------------------------------------------------ // Fast Walsh-Hadamard Transform (FWHT) (mod kModulus) -// Define this to enable the optimized version of FWHT() -#define LEO_FF8_FWHT_OPTIMIZED - // Transform for a variable number of bits (up to kOrder) void FWHT(ffe_t* data, const unsigned bits); @@ -66,85 +63,89 @@ void FWHT(ffe_t* data, const unsigned bits); void FWHT(ffe_t data[kOrder]); -//------------------------------------------------------------------------------ -// XOR Memory - -// x[] ^= y[] -void xor_mem( - void * LEO_RESTRICT x, const void * LEO_RESTRICT y, - unsigned bytes); - -// For i = {0, 1}: x_i[] ^= x_i[] -void xor_mem2( - void * LEO_RESTRICT x_0, const void * LEO_RESTRICT y_0, - void * LEO_RESTRICT x_1, const void * LEO_RESTRICT y_1, - unsigned bytes); - -// For i = {0, 1, 2}: x_i[] ^= x_i[] -void xor_mem3( - void * LEO_RESTRICT x_0, const void * LEO_RESTRICT y_0, - void * LEO_RESTRICT x_1, const void * LEO_RESTRICT y_1, - void * LEO_RESTRICT x_2, const void * LEO_RESTRICT y_2, - unsigned bytes); - - //------------------------------------------------------------------------------ // Multiplies // x[] = y[] * m void mul_mem_set( void * LEO_RESTRICT x, const void * LEO_RESTRICT y, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); // For i = {0, 1}: x_i[] *= m void mul_mem2_inplace( void * LEO_RESTRICT x_0, void * LEO_RESTRICT x_1, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); //------------------------------------------------------------------------------ // FFT Operations // x[] ^= y[] * m, y[] ^= x[] -void mul_fft( +void fft_butterfly( void * LEO_RESTRICT x, void * LEO_RESTRICT y, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); // For i = {0, 1}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] -void mul_fft2( +void fft_butterfly2( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); // For i = {0, 1, 2}: x_i[] ^= y_i[] * m, y_i[] ^= x_i[] -void mul_fft3( +void fft_butterfly3( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); //------------------------------------------------------------------------------ // IFFT Operations // y[] ^= x[], x[] ^= y[] * m -void mul_ifft( +void ifft_butterfly( void * LEO_RESTRICT x, void * LEO_RESTRICT y, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); // For i = {0, 1}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m -void mul_ifft2( +void ifft_butterfly2( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); // For i = {0, 1, 2}: y_i[] ^= x_i[], x_i[] ^= y_i[] * m -void mul_ifft3( +void ifft_butterfly3( void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0, void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1, void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2, - ffe_t m, unsigned bytes); + ffe_t m, uint64_t bytes); + + +//------------------------------------------------------------------------------ +// Encode + +void Encode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, // = NextPow2(recovery_count) * 2 = work_count + void* const * const data, + void** work); // Size of GetEncodeWorkCount() + + +//------------------------------------------------------------------------------ +// Decode + +void Decode( + uint64_t buffer_bytes, + unsigned original_count, + unsigned recovery_count, + unsigned m, // = NextPow2(recovery_count) + unsigned n, // = NextPow2(m + original_count) = work_count + void* const * const original, // original_count entries + void* const * const recovery, // recovery_count entries + void** work); // n entries //------------------------------------------------------------------------------ diff --git a/docs/HighRateDecoder.pdf b/docs/HighRateDecoder.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6ce50544a44a25044c7173c97a2ac8289df1b539 GIT binary patch literal 164713 zcmd431yogCyEcq;gLJd$X45Uw-3!Lb^jr8sS@@&-3Vc z-*e9UkMWIveC{Evy=um~=Umry&Fh9nMM8>&os|oXCNMNIHZe6L`$f^qir4jT-?ex5 z4~>5MGPk(8xqI;a{2C379mEE5FtJ7x5(0tM-Ar7)93ddE2Jn*|1eS8Jca?y+m^xcI zx;i+6`1ycy)j(h|V;9KXArM$pK}=RfoJm|!QI;PBmb5o@Ftf6^0D-lw>_zQetbX5N z=4$DJ_5{Ss&yQvR+}X<6#Z}zW*crsb_OrO3rGdZ-#(y6`6Bb5;*qhzej^npl%0R3v zEL}lB{lu(XT~r{>;tqC>4)zeBLcqSi+aPOaYymVy0b+0AYI%1=+X|?|-Ns$(KwuqZ z6KjYm(1`mJ6^&hNK(5Yikh?-%T%94tcF0h7rT^Uis~()c)#K-F{%NCxmAN^@8Deh= zxoao~2<&KS1p-4HU94;!?15sf99)e-TwEZqse_%JF^HXw69l#Ze({39HXyJq2y6#p zX9v!6Kr{HQ!T`_YZ72sQXDAyeRw!#=%LMrE2;~Z81^l*$y1PJ9>OKObKym-*Oue(OM)sCw-W_OntrUtQO#N0TSQQ@otW3>q9W z+a*(=YDNoWYE`IbOiWUOn5U6~Z$tuK3kJxK9U+Ej zi!MWxhUoBmLcAPnC{o?U(I$l5qBvC_*XG*g>8FZ%EgX~*PtD!N9ebCshYp_oro?(n zjI1$P<}D=~?QS&&8*yNr0Yz2Ql)~E{^Ct7^jW#mb4-Ltj>~B2=emH%#-$6SKNA4XF z@UF*g%zdraVHJ}vuLW;k7@%#NRaLwOkGMWW#8>@nr?G{;Fi%aOs`z5}ZJP2;^Kn8T zEnH(q2*XUHsanSZw>ml^o%Z#wr-Mj?M01IsSv)!zjzp#dM#ru8Z28o*@mYJ)TKuu&dW4Jbp3cs(kt@sj=*@`adun2iHy16C ziSqaynhKqC^{I`l+WNZ}31P@>h@c3#97YWl`-?3F1T)N7te^Xyw~vdGJ1;9Wxo_sXXi<6BtxJOTNInLOSe5)6)-oi z^49J!dhNsdIKpwY#(^V7~gdAWn+fA{`#LmLj;073hQ(gBIH(Uk@pFc)1U#1p|?h zF@x$*?t94AQ5LrauiOMKF3iX5b<0TETfQ2M6N(*%hn7@QdU`@dPM9*7m8B-HfvGtR zQe9a!7fa+RaIT3cj8>+D()@LrEVWldOX@Q@!7Vs``CA8@B0Wne3xS`a!Z zS_}wVf6QW$RP(`T8(BgU^*U}e?4?R}RML{68nzQ6x33KLQ=<2yC`QBxh$%?wVefjL z_t-CU17}l+nYfLHIj=TZkz&ywjL1ubp+{jtQ^l+;3H+EW+a8m9g^^GgRUyjO zOJt#DGFzU2*@*`^e-5<3-pm{h)SCxvN{`nzxRnLT5%fS{%=;C(J|naM5JXBR)Ke*jM_F zVsg9m{^(*f0_%M;n1bam#LFp3)kVK3Aa`zX@$@?H*C;#YdlPAI**=yr^A*XDUyPNQ zv*SQb8=_Fx=84@=-d9?MHj{XHiYp`C)#EWSgLwI3lCY`{tbQa)SpCs@<;izhF3De%dQWPjmjKTh&(f@m z83}!G0+;YIBZ$oEiX8H+;FlF86B;r%uoE@NKhk4I~qeI62z zaoxlzFwTVwK6ma%ob|VgkOy(V-kK*)ym8dXxW%XyKqN$sVdwNg(%bh&BI|Zg(E$YZe4@3zVrS!w4~ex82Ej?ZQb`0Y*I$C~}VJT3**6t6f&(LIYO zjER=oGrceD0Sk&hEr&~zu7mY@(7XNGm2=zBD^Yb$LA<@SCUf*~Q=M7pXGD&~?W7&7aA zrz-k-ewxhb!_~vhSWX_sDU3Nss4t|~GwfPD;uD8hmX2xWFILm!=HLjR;z%vc{*Td)Jz0_H4Gs2l)>)c{0yw_8li!4p7&EI^f6 zIJnsWbjQH~;^k($1HkI85IY$VC(mDK^uIvu`{P;wTK)}$-J$Qlkow)}Kl_8{E{Oi< z4*(sy{OkrhD|@#)aQ8p9kcaQj7P7E&vEDW8uV&tL#qVvDRS^T)sIChD{y!SY4`Six z<^vkZ#RKBy<^89TTma7fiIx9-Be^(#YvdjH{Hu|FL;1U_*~S0H@|>L9zYT{6P_WQY z4<0-~z3YEz(YyZVx%*CbPW<2;ANHIC70=@;o&>h+A{g#W4C74lVf9%X78LY@2X;>4 z4vt>;aRH(?WdO1B^Yb!;IM~?uL85jLXDd@7Zem7ZA)}64Ug{r;#@rQ%poo11^kt9Is(}XaWAL`x2Q<9$nz-W$*8FF*{Ep$ zIu6;{etRY=&JL!)umi#x43JFV!9qM;e^Ds0U%!C|bx)~e??OP*(^XpC6<`YY^g>#l z{f+_s2PZf8tb#PX9G&cR&2yqu6%7IGW-&Nd&AM~^A`%{0B%KKZ&+Hr#Tf7MskjvZix z_gBc;0XM%d=q`x=;aK;T{wwsw9o&H60Y>4^fujOYSr-sk-pcHbT5|oo6EM#1ZuqOx zy!YS!ppNXnv*!Pqw()b^)#MlNm5Hj*9tLO|Cj3lRo|v?~G3VS<=O9Bh*asvp=!l!B z`A5fROsG;AX{MdX^D(9^wtP9|b(0OaBJ^dxecT294e^p&)YaN6WNX&#cv$pkyZz{! z&N|Z@`M!I3TH)GSRkx}A@HsQT&ZiAYU91KV$|m~qR|NwX)1?JFlVO~0gwcCy2B8JB zqdL6BtXftriiJn5v68YWJgIJnhF0<`4_;Amv0GD|6t56^7rS>MKO&?41W}(`^eL9P zib`dC;!W64_-f!AX*UwQn^jDJ)|S3s(~YD1SZW%(w-l`_7um;@{f|-myj@zxPn_qP znz_u{6D$3NnX&Ypa^0;U_Uv2C5yE-#s(a>QC2tip{Q z#PUuJ%1BvgKfKdY-DO20ekL?e#l#oN(NvOalFK;#_;{SuMJK@$=d0O6{yLmaWqu=K zSocTh$MvfH%wFafUk@q#WOK=SF1iH^Y;^ zgp1HMhb9|O74#rU+R=gK(4I&OY^-}hZ(|HPMRTv7U@J%pNEmtxR*F6}rd;|`qvJ+T zVW{M0l<#QEHL%P7nU`z6wtQNK&R_nuIAP|5ijQEJfXd_xXX25`{>DvTn&`n7?POLw zo|!oqXFIq%bj6QkTL-pT9;;@z<|kws_i=ZH%o+>HNI>SUcn%}D3h(J0B|8+BS!EM9##a#$*U84>e4_>pZ>l^- zLuJ}hpy=wv8%r8sd(ni$S182krbrwWE9OuzY`OBKSZA*ok#9aK?5qr5z4D!e1!jv5 zVQ^7ZdN+sS(5MvDtUUp%2PG+XcRR#rhTj)j&qA3Z77gikgE~^uEm@ZqAz?W|yMMkjLi%7lY|`yeR@IUF zMH@rwA~D?7D6X!F5;a~wjm;}&#_1T2UY$J8f;zgX-S;rTYKO$B>r!4-LYa+xY}xP9 zv``$z-loqbzF+c>LfRUvho+f7#NpDHoHxTRj4tx03u)ZHNp7iq8~DCq*0_NPN7R#< ztOvvXh(4DIPC?E^AA3De^4aoHvBt-n?CQlE%EIex-$ESRj5g6CKU^1g?^ZbnYxts` zR@_9nVHtQBWf_%c2(^r=U%gD|;^Q0IEmqG-i51=#n11RB$smo$m5)E5*tMh6r5>^J zc{reGc#d42YFLU|J;7)ho+UCHA};vpWyh~raR~yw^~5^h69b>xC><=C=UdY! zf}W*gf~is*s4Lp4EKmE=%{#~1Njje z(dLRhMG@%Iq*4fq)SzE4ta#?7`cd6zknAkS;B8K$t**^*eI-M#1o7TL(`(*_R7oq2 z+Vq-H1$W!SWIaDd)&&Lx_+vbwPV@D?$D}-LV~5z*f}uVH7uTUPd*|IAQa4{qD|ncx z=es3#khwL6k4ovJv!2twq@8)((z?-^>%Z^s;Db!@!_+d=B5wX=p(}aU4@S4aU}}|- znj;L+3D85l_Dj+9Z^fD1=P@WoITt*k#H)WD9mDZOBUk*9;rlWB2N^+iy#N7~G>_*m`q1FD# z|3IG5&f7i_7{Cv|&*B0Oh6=W!FyIleC)oTnw+f^1uxwbW>@e_H4YN#dF=s6=IDW~P zDen0bOeRxRM{3=Dds?m7QSPGSmyYCi^S3NBGO(g`WWqO%ABf|T&$46ePphF|pw_M! zX#Ne%`dwT8Z=o;S?@aJ7=!^Ed4u6MzsefW$vL!j#TU6*f2yA~9f7y43faNjHlpdC! z72P9Xdzb&;5HPSp06<{iCjfz=NbfPQDu976>U3V5_vSrVx`2ndkU3AlO-)Eo${Pa^6VKL zx?BFaczSyJnM3}c`1Td@BuY^dD(b)K^naDt{|70|yfj9sJ3$}B!b+vM|_4*T`{UNOX&FT2=5ceOP&gBk$f+2Qh z#x9m%4X`B`@*BOqUr+d*%g1!GBm^=Uu{fk8N@ZWL7dm7Eb!@#5JBSO5n1ij^&x3!lYj%LZvTy*5ft`(w4FKHS|1eK>PM`w+6ZHg6_#J`&$9fFc z{bj%4^1UA8&;`Q$dV$MZvK`nFLq!oz8@-xO{%YbI!AEqp*oVC#w|*V&>TXc)Th8YF z#?<|)a^gfDcfKT!9o4mfR$3w95{r57*3L&zzCu>nMwW_@IW){6Nn$+}2ZGJdi^?|9 zS#(_5ZSsV^BtwPY@#fQ?p7N06syp)_8I`0Lkz@Pv(A!({MiC9s_2dUlb&T|)&Cu!R z!j1u^IMF6DQ5^kX`Z!qE^~chK!(;B3CD7BUl3o?m58n;HT?8XpxOA@RCTo~#*FD|W zk~?6GI(Kr>Y;j(3Uhz%n!}3O_*n2}}_my(QfmHb0wsc5i=NgZ?3LY#<;g?nC{*C3jpPAR2z1@(0fY_}bqz5nww1kUpS*zpwa< z^4%584dVKnB=Umz?nxBz^zW(Qzi`_Bg}eS9HFquei??YwXxdxdFNv|U{Ss#WoXUU3 z?r)2#T)$ZCAB(DTQY!b0s=$c(dpi2XB=1)-?^qxY&`K_#TRDLd#|=dO|EvEw___a| zt=s?oKKn0i1q269c3xms;{VwP?3{Oj_0Q@z$M5sgZ;RhRH2)rAzpG;ZBa!Flxxemj zBA=~2ki5c!6?ah6Dkg1YJEnuQyy?`kchr$fV54N}~KJW}l zP0g)^lpUW{gmUT`ig}$EBsPmBop=A!62zx^LlH9hnqTAc$MQy&!@}kB92PSB^~S1N zCoi5inzgx~=SQPv4L7yFB0R!ytFUeuRV+xeZ+nD;F3Q(EIOX<;)A^{)o2*>whe#h| z_37Rctdi1R!%25BjNmmmBF)w{a}#~R=&P3KqxuLcKRW5!d25SLFAUN?b%#YxVH&S3 z`mahy2#xK{8q#BAqB?Jj3SzW8M=C?-XGjbVlg)3Xo#;rUb>VR_)TStgI@7TsB0w6s z1XX=)q~;4I9xDs_azTqJ&cUiZacNA~_Vs4eUIcpbAZ)nL_-TK}WJ;oaR4mT$wvzFQ z)(B~3>sTx8qqY!_%I1kLm++{hq_=M>+zlQI_kD9XdW2)#by`6c5jaWtvhIQgTc*=`m1VCHKL^<8d{!Zgf~NGwM@eR$z6Nrt_h_aW&%fiwWnUlbpa(gTaR< zxi*g9`a)_?Df>nWT3A-4Fq+K5v_B_4khpCPo(|Wt_c?D5Dq5xX>M&>3{TMpH7@#!J z4}l(e0BzG_jd>$++iWWqSK*$Temy29`gKB&LYc)#X`)J!^hN zq=0oqbAV_GRUrpvkKBB<8=852it6HmRXP1k`=QU}s^0R4X7OvzSG^aQHFZq`lQM5S zw~KPQ={1tGl+=bw7!~70onN;Nb}(jUK(b(umH720HSz~Pl22KPDag9NGP1>5+5gTW z34L)OrH;0UyhTHW%6KW-JQfv6S*pb5XeI4XNG-jd-l@WSLi18((@r*>>H&jF^(wQ% zc$geSndEUQLvkp*1*$Xrcf>_$#Z24R-=GtxJ|LNRuCZd}6+^_ru3>D0xvbP4QG0}ewJ$Y8X(8ztp08-fS+1=x8e|v5dUN&6#vn>PXk-EW zGPx?V)D4Dn#cO7TMnwEAU;R8uxIF&ciLx1|#VRj4qJ>X=irRjy%g-d1T-~xS^x>)PMp2)Zto`&ejV~NM-a$Q~W&lM639x|%`SSIJi?}1F(m7P$b zKSv<5NUSBkk$${XLk4&HP#12U)p@Jyvpe5OWTWIPoK*ITSKCwcXUq&t#Dr`lijLzi zlC1M0Nna|v*o??EhLgRF+y;Z5vOaFx$^@gJhwUn(@&s*g#j*tIN%1tid%a?dc`f+% zdKshhWqHYXLr90dMzgzmvwQqpoz=E3y9Dv7AO=WQz&<^;H-K&^FUix1dghj$6g%#8 zPN~1mW-pNI^8)^lD`i!*7WuB=nf&jz&YuOJms?vOYi=D!@|U&x5>*pbvJPXpAK`}P zmZEVA9I0opJ|Rg>>$IBs_JktUqOW3cp*iU}ic~H12oZjR;byqICXOF09l11Hq?86^ zozfyBfM?L3=+ZmW8G10yBeH+l2Ysl`vaw^1ilEH-;}&;B@JRqxI&_PO`D8!OK|8UF ztY&=f_4`SchS?8rua}Va>fdkhzo;tjPw^s?UWt?oljWuD%6J`as%xeYA>4IR$ZVHk z@LZEtkagL98vSHtR&cFI)!Zr-=Z6G7p}yN-slJ<#WS8N5;%R_~II?DjmplWn;d}}8 z)y%wA^ix*?mprdi%GKV;*Wd{M=@V!VeT@pnC^+N~H92b3@J6QEwLx@x5~^yi&Cqc} zEt3^=D3?+OQuM8Cj{_8Q8T#5tg{b1-Haa-rrA;ktcU`eP7aM|d!%=GHbS+zJZ%&Gf z&-c%}kMq}RT}J5(s(UGpcmyZ3BCA_r2A-MuWcjo`19O6o+{yxUw+ zPht;=8ah*sR~Q~V?h~Bqi+Q@piSHL$|JdSYH$l7xoSl6#0PVD9J0uU{n;|Ngu&CKK zOH*x?snKa=WHZ|1P}V_N#<^Gc*>q!j&|hPloY3|QPNXa=4_BGBvv^|SodAd>%iRW9gkC3d|LTe zH+c16(VUm@7L#&aN4M%BNOK6sU9V{f8u7Y#yiFex;4!qjdl~z%+osPVD?~2oKz|PU zL2m?y@$UNFM?{)V%Ke7<4M%9@zUoMmwb1Oq*=+0^*W86{3dAfu>eV(Uxi@4rju+VS z7vOm#$%*8x>J)dgwC(7PI?O#Bi(bc4;&S4IPi%QNf#R<=GqP7W%&tB~m{fA1@ODp- z2Pz`Rw0q;}Iu*pY9JTSmmHEEKB+4GQY-xHW{aI!9=~|I!Hv-q3JE^cX|3Xx_&S7M6 zvyf)`Q-VIACj=NRN~f{3m%s7T)4mu)JD#w5aNA9jT~MkeTMCXzwG62vxHYz{--ips zQ#>;Zr%mmh`MRIS*CV*M)p%TMDm}wQY?*kB+Xcb?ELZQ@T8>J0B*h!o8E_@fVV>22 zZ;~mCHr*Zp&7fI#xvDT|UBmw|`|Zlx?pU!0Sa>T%v_8Csw9a}c!t+uT&@56{qk;_F z@`l1>-a@0jU+nm%4T@NX2b1=ivZq{kdPB;;a+QhZNy;9NTZ8AH2QJf1?)4mYWT}j` zpe8L0kDQZq>gN#F8KasfjgTu1r=5Ng;A_CTP{y}{5KiGd?Yer6+PRppas_sXlhNpY zxy2!?e?JQGb6uY!f+B;iZ%ke zD-B6@I7_8`i2${^ISrG3$W$gYfwZBPI!PF{{Vsj|vWR^9O6jvc1VO{+7R;YXoU`!x zhcv&o2~W?;rTA?^!oJ_Yt}5j=^rTwy7Jh?7*V?4DF_eckyIR%CK)Dc7c!!`w?!@nB7-*t7;TFgok5um#2IulDFL=Os21Th7sbNwo(Ng zP_=9_W~a#KZTNkA>pGCN=Cr;s^A2kCdxUWeKjHwL|FHLahya6b&yuv+^Yv6oiRQzb zb-iQDdS`D~1Tl$&p!AV6a{{iA%@1FzrVr4D_%MXa*iyJsX2|@@zGqZURizboQ<~GM zjkBpsWP0xMBj#voXc}0RutJ8t%`DobUE!cf@IS`O2*hJ3pQ`sI;E|a1C!pCRdKWVL zX4quBVaVY^vsaUz`n2i3CW}PwLVGu zTiXX7TKg|BrX9qnm8`{GzSM@{Aw9SXfH0FCv)KnfXIK=k{Wd8BC-+K*m)1fB?(?>F zxS1fWsGIbuWy043dyV$a#HTfCwZVA03aN&>&Q4plT*$FfEd&W%c)5?SZlY6ORWrHP z?Q>R&He;AAO}$-Nc}8ZiQxH{^V(y7JG`rA3oOQ9b%2GSOcDkwHWy;Sa|1~05Si+br zp*eR--@b*8LNGk5`lc?2(t+PPt+Y&iuPVl)kZ&h_uixSck-GIQXXvt-pH5@RF3X$O zRo6=XkT;9tF|GTd2dN3qtR<(zA}X2~5VFqHmhuP6sb;aO>`j}K^>DX1csLcUkd}=6 zSu3mA^3NQfz05z7=vl(E=!N1w`o8-(BOrg_aSKcNDtyG_M}*C1^m`Y+!OUk%khT|4 zh!o!sRL3z@!SBM#C9?4CN2%B4G{l!-EKmIFsYSJh?1+iUNp4q}?VK0aueb0}jbjR1@&Zrfo@?kt z;)lRb26){ zhl3FRR4=A(oO*eDVX!y75M^-hwpEHj_QvAliXfc`SS!1COZ4wPzL&~F9mqV38L?qI z_TV88e$0hXQgrt&ulxWwDE`GX4wxH$Q;6=(=YOj@;D2$AYgj@6pE%%yw=*#V{NjL0 z`nQqk*XB=w`Oa2zXL-6a6WnXEfOFg)upt2g?ww@z(+LIQ2e$4+Y8!~_y-W=l68`%B zOQ*iKoCES1pxe6sAx__09R3ov?&R2?hNF9A@^8dx(K`tl5SyI=$=MVTpltxL*$mLB zJpsMi?oW-F2asid8Lj@*h&cdj!yk4lmOJVEFDDXUxcWmS2G0FcC017XO(p*6+WL9Q zUs^HX?fIz{^Yin7_&L~rlZt_d^gm0*z}f$pH304ZpGW#{;n}ae173EvyUYHTigA)J z)UiZx`p7Y#XA)6aGnBlX_B6NJ@+@9AOV2DS$?!C%k}OO{C}4PrSU_os7(5pd7#M9~ zEug4A@#)hi*C|)>0O=1mwRH<#`&WKf)5D5NK@SAJzTw+pe*4VII=Oj6=Ulq_ZS*5j zVw4v2*@Qi~53_|qqK=~z*av(hIOb;1duk8Oa%F-M+-G^88J5=%7P0US4zk0NlfOBA zJ~bmZ_d4@b3V`#8f(Ce|QP z_>kA;pTT=yD#=H`;hfbb#JRa8ew!T{89X`??cPR@p$!xBFfEKQVAxOmej@b&zKL#P zc{ylv1L22jbAl3=yVG#d8~Jaiq)*3(;&{^3*cdxxw00VZvQ;?%D>y!0cX*0%{KyAP zclXe9FQ2Z==|xI%q;Qli!o`d%2*X9!R+iR8*ON5OFBz{L5l`vF5R(uM$;c9O6A_W; zRklBOUVp$1QL1~XdSo3F{UGNlmEl5C$qENS#Fyb=L-<2j8VX7*%r0IAc6NK7rEn>YAgb95ug5uh70x^oh zT{?NZ74s!pE1y|*l&(Gy5-ZX^CNXT@$8P-QyFYoFP?~_--%MMT+Arxnf##FkfAEsvrR`Q zmqyiP^=d51HuZh0$tS{5=8~%L@E@ua)GzRGg1z+ARyZs*)szCDFUgN&d6){Q1rg4N zPG9sJVaf2rL|IcjE-iisO+~&+`?OTQgUzbp{1zG&YV(vM;NQ$}zb~WRtD7KTq$=Gd z+x(S(!wZ-qe=fx+n^>#6+TE$3|B^`Z$51i*D2fuyM$mg$eKbFZzRhC*iNu_#1Tk*b zesC_A>Kf3GT4<%*9RG1~>v;~1mPet7l24fe6H5gh8iEH>;di3aC#5%DN-^YZ$4Za%_QqbF zd}D^;GDqyy+sl`c5fgclvXx4V5ai^|)nTpSAgrUn*_(XEqOS=C?7g?skLdnQul%*T z2bkq#?ad&bfGH4|%-pR^A>xX<@_@^Y4+Knwz~Tll@BWqO^dGN5@&3v3G?Ek@LP40} z`!~`2_7gBNye8Po##3B}qY0nv_7MxY%VaPyuJ%zZ-)ucN8e-e`75%9AHDe6M%cahG zO#Cs?p5t@vQcM97DHwj3M~rPxwzN{}MS|bHXC*7`{mzM)lyr9R*3%=bOI>Myi!@QY zbS%z1u#4vQ95vp(;gpz;i;P|a*K(KB*+2bMQVEfI>n8hIxuns+$FuRe-X6IyrOXGS zDq2CK@1VIZZ=Yz{MptP+9ft&yiBp@_S3u(vPxK2NQYGk1eYC-Qn>vsu9S;Xv>=udS zb{f0Od#hz{6DCZp>4eHS54SELdtvp=zN2RLZTIY|wHqrljh#RoSp*r{yl~e?o{t2| zr&R4wd6HrJ|K@r8u{!my(f3>O*j=Rl<~RiwbMH;6e^28Q$vz6|76iQ`TS0gRm5V|y zf_>R8Z05n*>qFVm_9C5lup#sX^jUB=4A7PJI%8_99JUw8N{NC+#er+f^oAldd<7p~ zrFa^nG~V_(=(2TVpAOhR9!c5onkSb%mqARXUNatBQZ-gZp)^@eZ~L6x zpS;XW?$mfqO3LbUVN%eJ-&xwV9+*4bO2~mZGMtkjALB;3D-NB%vn zXTH%V0<|!{G{dw8=?3x0Pv%6SZiA?42Lj{Ex6Ti#*~6$;VIMAvJHj6Fi*{p5FpDZ2 zYk}zZA#`LZPG$|EiC?Ha%0#xyMuf<@ieB~=$3Js&$1N7S+QR;~yAo&u~ zkQfP9RY0Uk-k6G)c6|OQk*<>{(xjaqeLo(}B?3Jy&2Ly^BVX)WKwY!VCvwB;0-;oG zeRF>O?pvZug1WcS9k1x$;b3>nwffP}^e8y#szOc*nJ^v01b@gAW3q(>iO^U&Q6grG z%>|YUENVTTYfM=l_m@)$dc@^f#6A47sVC6cYd0unoaj@9lLUggV+tz;?Q`1tA6f zfKO2LOjOeIa2@L3@E-lbc8uMu=@CTihAN;`vw<-FnfK`AurU&%K!D6C?LyHzQmd{5 z=N!ZGZDJm7FrZ}d3@PC9{JdEdPp3m~B$|!Q%7}Y`#%9qC`8<(jV&~Yf5c~0OuEL7N4fZu=x|J{Ac8};;K1%d~<@S_sF)n$c@LlQ5F%;{`lIctw z1H00?$lA0Qf~vi6#J-aKnWp#x@^kpL%2ikEt)}q~HIK6wC)plCIkq&fZo1f`HuCF0nUoNHiJ6m>&D3SzSx#nOuaP1^L58!@!8J;TOPf4(|$*b`ETw7Evb1{BUZguO41|D)0WSG^yO<%VgD8Ut>}C8T4NL#yttzvoHn< zmLMdA0V$&;yA1DdN|E;t3gCQB3vpKL{hwB`N)InP=3QY1it&cg4nN19;T@fv7PMT!4SuGyNoD+5 z5jmNzz*6Wouv*W(p@2RqPl$3hH=%&9p#leuRKV+9%j#PXbzKeN?KRwsoPtG4WuZWM z48s5^aq&PX*mX8KBWMJpfJ9iYt~$^K$~C`j5AkNs;Uthu^DPy!$UI?l?0VvC$MQ&V zp*EMy&KrAwMzs;bR5w}6!1-K#D>*i*qSF$eY8zB{6loBhrpfW~VUl>_ZGY+8()iZ3 zSBW2_ch)AlnK*kKFYgj&NO1F${>=>j$6EXU#-xmG9W4PSb)SZ7`;$(A9qb?$z`p-B zD;F?T|8m5Dei~x#6O`{#DnPtNoU8}p2S5){nhB8e3jzZ#f5=~1zhFxs9~cC-{wMVpY!7fS;7(2;urpBfy}1GG z4%mu-8+qRQ&B5M((zD+eh5r?;svuKih*%}gM64z6Rk`QQbW(cdAxBbXUPhayyH*zKQHnUSPex`}B!Ex@iuE|-1S#{GHhC2)BPFRBCFu>i!#1sq3iG3f)xVCZd$Y`6H2;5%qhEHN zKjTPL@;;7$VE>Qei0=*;|KIuW@ZK?x|K!61*u=PiTo5i^4q!<00;yMkfrt$lalH3N z9l$>WaC+|hnB?O8FA>f4H+Q+y8~zCB`*ioe0{YKc0|2G!I z!L7!o!p5z|RHw)W|2$)0zPmSO{cTACMm{%)pNs21^SJz;_64Nb z{V#gTpVj_txr>wScgu_AyhQ39G{-e<5igD)Fkoyg8r#!>3q+JgVa~RNjtZg`uW3Dtoty z^9}xu4+H#d3j@+v-|S&!lXGiiouX=(#FkU07+0&Y>O2FI1Ez8;diKpskIfofBA~|K zzc8cK@Xosy!CVr5n^L4wv5=cyI?kAqx?Mp6@pXzaOv%eCO@lK>7{D1DI~Ic^IeXPJ zYgLY~S}?_!4anK$%2j@94i?k3c5-i(D}M&ZIyhl1k0&9q>DugA>@DwEL+c@{@>1bh z^>9k1rk$!)tHxSCR&-2#t)^Uq-04E>(yg)_rWuPBwNT_%XvP7pFXGc+8oY<{kvNn& z2d;(CA!4i1{XKcr;LliCQzfCqs$$o2A88(XeXvV=LbWEvQkeS5eY@2sF!T)k2nye! zD-33}=TMXwbsD~pp%j^HA~8yQkt#d#*$Ue=#m7!L#6D-4KncVk`Y-Lv2%f3v-8UHd z4>E&%xK2@voaZ954k|8;NvIEy^->Zd;;kJ6_vlgAW<#;gk*0HuwQ4_Xi5`k=$jzu3 z7+56Qb57bT$;IGE@t_-<#7>(UJ$xiS=|5*LlTO zQ$29ut_FI?&LS^{t-{XfR!D&>(+X$*Cutw6im< z;XTJ`Cqgs@edFs6!uPCu`sRfIXI<%MeS>11ct7qEWmRP*4`qtv$Rjk8ZdIM0#f&&l!rUmo zFMDbIW@fK;Y>2Z0t_Xd>bsHU(a4)Z7<-4!l`St=iwUY7q=!H=m$46;w8$jg^sG5#% zQJTXl`wJ%WYmxChR_s+J3CFjXU#7umQ&)&Uw3uq^Ym>*(H+)FV79LqPOCLUW3?VBc z9B3JcuS~d+VlW_m!!H!WB=b`DU81gDHQ&Q7Rz5fJvmDn&_p22DDx0@U-@vFJdw4gq zab`1eIZ^Zbo;_#U)SG}p;beYuI#cv@>U$7g6+dndHM{(nbC(=Xy>F#=h(_?PZ}s~H zxRO;`IEx(r@1h!|5N%rNeCTJYJCIKok9ZrNrrK0!yoL+wBs->BbrceN#A*RY8Rqm9 zOegALWU~DH>&6_cS**2ftI+v;{?ls~DE`;tFahi!jaVZjylyFpl3eli#@ux5oXd~z zsue7sCB64HdU2NQ`u$VzX4np1F-;3PvI7^ z*cyy(#t~{i-$c`JE~%3D*cgH2_RL)RR>$eVrH%|LZa zWcw;Aip?_-zc?`Jgm4<5%j*3kdx=qs-(&R4W1nk?r6bQd3Jp9lcprqZ5aM8vi)@6A z=I>%?q`XTSE4v%FwL!7?Aa^7;7#zd90^S^oJ>{gyE+PNn8M`G8gK#y?8S$}Eu;@pr z9H(H@M}>N+p4Ecy7rq1p#BepfP-t~~o;=i6p)CcI zwJ&C@iZKozK0LjAfAr;~YpVKnNG4a(&ehCY=(xuB&9uU;Xwj$K?ubrWF_Cr&Q`oDI zO)##$FA{Qi&XAEfL_d^}Q`{tm$R^gnv?Cao39$gAD4>>Jd=>`r*s{ad@!nV5#vcl}UF?1}g|!Mx6D_ggJ_>}B7k%&qCmXVgL1P|%3-anT5 ze#kb4oMhS1)GtnbL`aGJFlt${z<>{n5=l6dp&3j@VaVc+uN~RaozUHes>?%d)bBOG z71gr3J&F-AgX$+&0iulRgQaYtM|m}AS+5$Xy3e*@{1knGOUV4Z_+h!!OX+LVq96U? zs&n*}HQk;qPP3NIuAehxO+T61$6xp{^EQ4qD~c){(EctY;5`@bG*y&RBwJFyg+8&+ z*AidNn`K?XjXgv)T+QJ^$bCijgrmbYxGOYbzp3?X(r2F!Ts7GFrBg?fB?vs#>#>_w`%3&+|eq z5Y6PmH4RSBlX4c$lf6rSY}2ctM)Gh|Sn(n-=p4D4ys3aiV;9q^E1I1l-5KF1Cgfef zb5z`{O!fB}UaNmb!|5d(YC&m1LSGR^O%0tifW2riV`>A%RZVS|pG#m$Q6!61nNR}@YqpvI4F)kLM0uEY& zuG<%)xf*3)$0(m84OtbE<`te5yAIs$q3xaL-L&W0Ec3`D)r-cvau(?q#?ZsF0RcG7iqCD*DkVUFrVY3CPZj$drJ}GiCgXGB; zzrFV9zcqv|^UKERwNjb6gvg}^_U1duMf$kQ!&tG!SCd5Z22)rK;#k3E%%(v)o7ljvG%=yO9MMzXG?`(7u+Fyb5Yna76;K3rW9 zOsbe0ayI0xCdQsUwzFyRE*m4uV*>R#k$41ye6_lsZqu;egq_|?FN8lo(D#0#o^P2> zY;ZR81h>6H=&dcD#h!OZ5E_QuL7i6_eMA%65z{%;5FQsl?&0@myzv^O{C%$H0Wn3y z>t2P2AG2AbUVe5H_V01$-LJ`cKkgq7cE{`FHNbC-$XIO8j<-}tXM*`nrd`gH5$*{G?~}Bg!8iV- z&txUKyL(8?lbE8q>C>yW*rwxRUVe#2g(r0-ohOw}~5OX@$v(<4fZDefF@K7Lfrl@UFDJK*WA#0e4*R`wm1OFO7zpt01ix!nl zHW+)}H6|uZ6U*3R!PV-qY&f}Nwg!Q6wiW%Wi)=V@tm%_B3Sqh>l?4P?sR;{xJLjdP z8T*5l_d66n;L$ra)Glpa;CWnDn0)@`GV`u`=~$ZN$NFScu&8{|2M6YGb`s(G zq6gu*O$;rO8T($BV@;W*In~c2p4fwyQy5xGnXS$?b;IB`Dj#n?%F8Ezgi9;0{3*}x z;HA}BRf0!z6TIU$$g!}`5Wx-vGeCkskEO~F4atG#S#WWpE|j_v0*Q&y7Ak1e(ySRL zD)D03v>!1;-6BS>nJLh1BT(fmC6?{-oQX*8?T}n*+<&IHAuX~Rw({q%_OQwX}-Bg-rJeh z&qFZ3=mu-uviM#An{7Kt)gw%G&o#mENd|rK$cwYWMje5gi0eUc*URetoW-r&nQIUo zS?nnG))FaC$FWX5QN*`L8Hf+q(DA=0eEE((EV4@HZh4;kgOJXx?0O?nwt~vkAnNGl zrZoay&6CmS1|cn_x#%h_7ktim(hn>g#LTg8k+8ju`7>c@Ejeq&<;Toc z;|M@WXqMy)ud14QiBU|_$WGl6^Fkxt{B~&ke7=vvlqEG8o+U}0&oPHeu>Kf3f|)N5 zfgTcHGafv^q2m9t#V{?bX<`40=TY8c_%6iJBvQ`E*nZy;$KGMip12foyh-w~2xb+P zxSR^w*riN4utU&C_kNqC9mT*+ILUpi7n>!Wt%u{_A_E7dxA~=oLuFDwm8=K|>kNVDh$5m5LqYj-2aoSZeaoB(UXa*YQm)bqPTqXXAl|^$WCTIZ3uMcnW zfMr9DkfZ2Hei6^SlqJuqPs&?`+8356W(_7xq8BfWb-F|)*Gwf`!jD;)ldfacF!z6m znd)#U@7NwkS&W`pxW?3PmMnd) z74Pyft*L-sN#CNqo|@hPua}UTNSEHUs_(t?2HTpBd3sQxh`@s6iY&lAjTq?Zwpf6*tGdB0O7|Ij=}+Q(HxRzV=9 z)+{6ZF0e;0Nmh6J>ytJ)6-{~%IhC}UaIq)!m#$)al`mpfPQ_TuM~Hy)=li+E~EOk!y3<0ja)$|~*cYi11>>VGB4f`vJ?B&(oB z!aGO*tT{`P?XHE0^<4J6K)LW)QeofjD~KaDE9FxgbJgP)2zLU)+&}fJ3Jy0nM2A++ zixd@IpV_>Q9w&E!yZCjqHFq!OvG)9Z>#L%d=<1bu{rHMI0~)FAX?@7%N=zI_g(lf* zPP52d99$?2aXV9d#^6af{3@R#y|fuYwl~u<*O^fE_8Bd zCu8S(l^kVyu(7t!LY;gNoc!=zp$@DPzwOVj&InI`2Icfp9$JE6S;qybzSsk`_sDm2l#ru+lPK}fpqZiu5$`tro+W5?E` zXX{Ws5~n68K`rF<*mr_d$dV5-kLsY4k1YSqp{Gh8PHRcYFyUOC4ITm=aVD(D8?6BXO_#rE^CXbUS2?QW|abi$j0-Fy`80Lc!GZA!D`^bxp8 zCnHdD2w~FDnlgrrXuO&`uFLCt8_aoJ@miZ7Qxmn?w+hG?szhAaVHMcNXfkaF4MZ=c zZEHA3-P(VenMK3NYq83A6db%hzZU3rH+;T6ifjCA^O%DJT8w)7WNztGRJx$$GiuS# zzAHb-$qRf{SE_^N4?deRnAmrme@c5oZ97htlfc~1!2I1t$y3P8c(wefZM_4ng*kL* zXnNAMaM>WU`O~{nN)$%zpV?~q`tal}D=Higu|}mytnyJa-%Ntbobl0%F(t)ie>!pC zXky=L^-I(S()X2S*UDmv&pF=?te323@rQB<$`aQs)1L42v|V&k7{rMn=t$r=ohqt& zBqIe47SUE`sB|ofd?S1Lh229L&M!UeCbasr8=`qU=UX(^yYl*szYfEhS-#>_-RL#} zf#pYq@+JKjWljFj5(1)gmZWog_9FMV`0~Mm^&lAYkW5N{cJVDP7s5*%u|jb{7x|W# z&K(f%bEn-;HS|rUn0eVuwWy@<(k;Gr7v0~30>U!4;Xo#SM zM|f;&W7_8=DIE@@ zrla@`D)Y?(A(;=IE}y^WLu^pcj2?OW#%mafo&Z}RE2C^eiiv7nh4J|g!4rupYv4X^muhl)E4 zAI`DMzj)L>DH(G7;PCmZhU14_>XqT-4?=QTK@9(2>t+U}6M`I60#u=Wv`pd@FCO6} zQB;namoQEBp;z$s7w5_}nIUAISS1nbD)^+5at%Y2*>;K>cZ)i`(3hVbQ}sLC&%B#~ z9jv^tBgklo_!z=Q+i6ghP=T5FN@7hXLHu!I=d0j~HrQjaOx*YD!@oX1g~Wz*nYC%$ zzmf=c3}Nikl3IJtRhYxp{NmTNI9l4yQ>OCghkp9-mk~VqA5gl)+Bh>NxciAY9@`C4vz4(&TR|$?t za5t`p%&oG-#B|r6zwwIrTwLK$jvCKdsv~tC?~1Kxuu*(R}U#zUjvy#%;DYlN8=1nrM^-4UD0+!-ReQiK?{s47-M zPQh=f?3Y+rG?JZP(N4ypEnJq}X?MJR0p6dnKQ2cp(b0dcN9~OmndcxDy|-1ZDJNiL z&xJKB1EMhXu%K(kkD8CD&%z>S?QLk1Au@hpfEkm~DK$~~c%fwEqmmQPoaJfq=5}!L zj}>*6yL>5ltq@^VE)rOO;yLG5$7GN>8QVXyv@GOZM<1P;Cv{w?XmBsti&0enG_I(v zFUqS3>ADvt*DJZ^nJ?1wP;Jbd|9JO^r1v{sxXY``wH=2(=fM1P3q75)NcirbNKBgL zhQ2@do{`>$AT0{h+3SgggxS=X{mcp(;pf}KzS%+d;xh5d?aJ>G@@X?$=Fm_Ft!WV( z5cG^=G21O&l6CWtlWyN4C+a`S5vch}ot~TN^EQt-IA%el4#wbcL_gz5JRBi((FveF z3lJ2>uyFojq^a6MKJ#fqslON-4R%a-dF5s&@Ur(p-Rm*zd2YkJ$AK`Hc69MQW8WVYWOB4Pt;XQq82qB@GQv1tWVFlb?cvJnhfWn+IVQM zCYnX_G&BARsGM*r*K)SEwpMU;Oc~8aWgCJAF5RHt#g-f4rYlj@gWj~cRNM5u91*^# z0W&2@A~ur6Q=nM)jY42Vr1X*{3gHSx3ZFdq^2cwjBYB<#P5Rlwn;PZ?aqE)SvUzSx zr4(T2gT<$U3+ebugW#Bk_W5d4`k#D>mNUx9iCg3MK>WEY?H46iaNx>?cac1;j$;IG zSGUVtDjprHZmH=lukS4{L36Zk-122fub3TgQ%&YmZm_Vi7xe+MAfsOTXuWyx(bkRm z)Zv?rP>=)VySAtAevOGYBqYCpMK5NMB)Z1pw31=p&r`NqJ{LM-JL#IL7^AoK#=s^t zy*jx(;aEkWa77T1Z`mrbcGY=TuFA!M%Hw|Tc{fxyTX<5a!tc27?2smMpI;ZlLTt;* z6mD<+!Y*TxztZ_!MWP&Sw8zedbz@qYY;pwNkBIqrpXdHO(u0X~|CiZkRq@3_T=t=c z3#S{%4XXI#kIw4zc4{`soJyj5GKuvT`b7>F9eiXgs4q&l28G=Z1XV4n3{V9tP)$qY zU&s#{K7WH{F?QSOWL+$$+{zN!t0*px9cCd^_3L^tePrQ<2$?Fq|Y9ytOBFt02sh8nc$OuyA@*fc$NWXbO$ePLqM@xxSylBo7Z zj{&F0oVcOK9C(R1<1-WCGfFY3c^jb#2Tm6eM719Vg@gRpvK}MZboKhBl~$+>CyqZ* zs~7v2+Q{czx!tI|#1A^FD^vFu{oHe2``B4G$r>EIA&AJ&Ck~w?9de$Zi>}om_ZmoMoc1{e3wR4=E z{hlXepQUP!$x{Mnr4t#21DwC&;q334;FFT3fre>pG59X?6!Pzbf7=4Vn#>QhfBNVMf6 ztBJ~rP>aBG6&X(amK%P`0*yvW&9md-Y%XI=3VS_f;LGqaYrMU)CxwHLDTqMewam9? zxZeq9D8$|~Ma09WY)jlHKXPDwto=A6Ir!NoAv-rgF~*MeeE@2Sc0DYImwr?4X_w+-{!qG@oCv2RCcRzw5Ec->4-x zZtsX`N<2@ClXv!R=%dwU>ME7jrzcHlzq*;=l6>ic2#%CH_jL#8 zLWQzM$sDwquaxt8KXDYjTPQ$ZddKyqmxo1Yi%>ihkxV1|P|O^i zLV5dRqKS>QEqiiScxo z7Zi11ElcV89uf3t=fmoMz_kWojsK<1@kia|Uy4Eh#o+0mc*TG_48ZRO;9TN2dNH6a zXJ>Bz0JO~J0O;)f_H6NhP({h+>R|0=?&J&r^0NIw94>)%%1)oy^GDn7|^==jpp@_^#2Zg7w5kzl>gI03Q!UJtrzoOAgce( z3JTDq`7dD8fOgRT0c;w`{cpBTvM&?>g&b8t)9xYqFZ(7yJ?9V7G$)|x^lwPh4}M4f zrIYtJ)xdusP4oR72|ZKj;s8+YA0X=oH7gzfYKa#>iv>_@6wJ-6jsIhcD&XnF zzpdx~Z*&G|@!uu0n8X8_0WHM8lNspFA2LG#PX9MD1Il#& zk7VZky8Tkbw+q)O2-l;x^tbQK z0o|Na@o1?s?M=A=^u4A`PVKhB6Tea1pBc|St%e0)BRo;gH1m4(+S5R*-8I zJJ{HHHQ-jwgp0leD852z67i1>NQsT5bsvqvY&HCFKTUc~+njO|wvnlk)7Ex>Vqx#F zziXFdlRP1;b+{#2ANI{lCvq-=ROr{xDc?fLQiNZr+-|mPulB!aCD@|{`V#~8inWQp~lD@R14!61RaQgE4o^@Se zoLzpqw~^reSn6`5!*G9gh_%(`KJVO|tG*uBMiKhJym_s`hA$izu~G?0mHF}0QaBu0l*IXo%DOEs8RwpuMa&37N7sP zO#Dv%YaIVktK~0iG(PUXPax^HX<7=ez!bv)UfIZ|5#Lk_jUbZ)17WM?_^6+URv(d0lk6C0fwc$BH&mP+3gt4Bo6t>c{)qWTp$A5#%)^K4W)K3@JA&W z*g^9?VM`SVc3RL`--`-UJezMLc8Z4Xv(+8!i$u%%4KWdeycZ>%?@{;ys{&tnd_Tt{ z8tD-Cd+ESFTP)ZrSyPZqg_DCz1(SH=W4L_&sJppn-ro>+Ir{9&2QryNRGfIa)Nk(? zJA}f=2Nsm!dVK4iIwXe%TJvkhu^9EODPmNb>s$WtXxN}jl6givx4eC1bLBR#DcrQC z%kf&kAH?-c73c9sx6)~2(DgwC?2%$(S+#VGUJV_ZZGV)PF)Zv!7UJ96JW-zw$IWA} zX%e_j1k{+=y8VR`mhVrb28isgKXdta8}NQaW5jQTl9&|5S``=DgXTFl$9NO<4~8_5 zUafTN`(3=B{)#TrB_7;Ut1Uw6QWu=g6$^)2XX1A+KyXG>P0>z!`Qy7po)w_`$k9#BF>z$gh0VK?JFfl zN$II^Lq>>a&(6@Uqld=;F=J%72vHAjq){$+QrnBS@QGPntxY%w30q zK6*-)FWXQ*MYhRkr-#;=Kxq>5zgI0(HspDaytwjT>YNTeOZujjVSPq6pwJ;;$DPiZ z(h~S=MX<)Yt)K^-9eNtZL(cGHbGzyJQ<~tBGu_YEZcFjkFN?pWuBLLwT(ipH(#q(H zYh_*5*+hyos(j2!xS+A-_7&3;A~rxOUHCaOc~3f#*vIKQ`Wm0q zEKQ3k`&U)hR%*^LLVu+5e5UX9!`d5U-^;9pXJf*Q2}v6LDy*=u`f6QcJmDs9U2v8W zKSLQC?(p0Wn~f1CddXP3ty*a8*fy4c@{>ZYcjZdc1jk{Xfp6S>R+)Chz9wr4WgKO4 zbGOsg2Tb8b|Jw@5Ju6>{{v9h(P9*nDY!SCQ4up)di{|eVV_AGNtqMai&)&Y(yw{*j z?d;v+-b;KZ&1Bp>ElJUpwp0Kwm{g?IL>9^om5hrdh3hVes`br zBD3r+Pu0T7Lsrr73&UCX`b%i0Cy?^96PX0w%U7#h6&(;vr+mkvNy430ZLJvEs5Y*r z-M7u6%d2u&b*daR8c2_cNf0I>8Gfx3E5aOp!R2m0;dz$*+(;_C9lx)RJd*=Y`JTSC zpoC_hsA%Y042yOxcX5`!9=>$Q$*N_IAa&L#mpXH?L3@(XK{M~|)aG1}`?a_m5~qts zy05%)cr8IFk7(E5 zm=NXOs6F}`8}hT30cjWB+FC54(L3Cmu*aWc`A(D`{A2d_i(zwDpp!X#Oxg(9e0)#rYtbQxSGxDNBH(@>V zb{Am)kabP>G0%TE%=}%KUqf6K5NagMEsO#DV*sJTkBAkqKt0=oYHWBu=c z8@@lH^1qDZ0N(z?#}7RCcS|BpzP}&H|9*k;KP7;(Kg>&jdH)Xw@&~Ax-|2t)_rFQt zfC<#!WYvF4;M{+X?tk;f^?&uK1t72doAmvE_c!$*f-A@X#r`IP|MRAU1CTTRT?GF( z>%+fI^S^F7e&Z(m!({8XBc8ujj=$i?AB?pAi)&c`WapnINdKE_Sq{DjX!C!03Hpaa zpufKKKON2fM~UQ*S6aS5K;-|#7uf$0=>?wkSXLlEU3>He4#~9X%Z8@-BWff6W27q5 zKRkB}kNy3p zjIWOnu)AF$e2MIv<+;ACY{){f#Rrvq21C~E^%Z5 z%0}S~q`gf~)OWOlFGBGl zfFHYHrLCjjU~wW)hA_B5xV8|11s~d3_>`yn5zHS4vdi#&^2(Yz#RUUB00#mO?J}bA z5fD{aFDl8J!3M#BjkwUE)nBJy#k$m4m)fE}6d{2J&-V3b5|kK-8zI8{Rr>eHA2K4s zv^s-pY5=7 znlDJl8vlLD#WaDBQw$TLYX|!oL_QSD^sNFjeIWFyO;lNnzVFH|5rzYoO`XoZsU>E5 z9rP}3uACR^Av2$3NV*tlqkx%B=J)?D|Xj+a> zB3XOv@WgUGrDN%8!2tWMyH~$Hy6x~ggke}Jz$*Ll!n0sflt;!O^~W%7{`!& zf_SxW2M=nZo<-*Vj^F9M`Lq`}X^O75tzjl$X77bvULMLm1z`hQ-vmv>%!nau)ilQv)GtM(5O5I*z%*1EE67=FnFyTN`J2=*d}VJDn4X%e7@v1lIc$g<)OXS&Yi)O zA?7iv{RFxq{gE&I=~?m2;E6D?Gdr#z_{6LIP`Zau-&K5#*2!QA)?_fXVPo%Fei;Hv$Nm~mrm_aFp2mo(p2)k}TXS+C-v5qC%_th~J8 zLgWD94$%=bbGU$RL?h(`nXngGcn6o;1C;0T&q>rPZ202IFz}`d%y^JS&Oe}IGNsxQ zqqQ~oLB-+eH}FZ%VLj>T?_Av7v z{#Xd1FEx$LxFKgihNwcQ%qE&v8F2+pVZ0c4?cgQIg*PGn+2E=(ML)qQ4ywNx0A?VM}_? zuDiEDmR+;_KETx;Tjk8TX65+LUOW@jl20tX2~0i&mc7$DW)go5cUPOfu{mh zq3zOh`NpqG`|QsJ+%TchLM5*zKrrpZul;n|Dd@Q`yUrjHJ$?FHJn50Kx{9`YK%0XK zw)0Gh+h!ox&->4P8yH?sTcUsTTG%1Dca>iX zy_>zh2EG=O82ogKI7(Nu zIXRwAnlik~yBnIb+;7iphOFKL)(+D(Ptd@94KEa&!aAitlTQ&cyumVTd^13p^tI`D z6p=Zm^;Ryx^x&z`D2a^kIYm@w_y$&SFk!1P#D?@S$|se*p9AwJeek&zZEnV9Um)@2 zZn%eu5=N11X^8Qarx%#IKD1T2njj>exiU`ge5qVhmoXeN{-Wz=>QZM3cv9TzNOi^n z{E;{f%zC?~H`E&<$}dfiTXnU*HQiD|?=C$}6BcLgNy%tx=@zQZM*H1dj`bGZv<5a%bobvc^Mi`H_PK96Q;?zcbkJZ?jbl!} z{TQ6DOSuBn-+~~!w;{0?P%)UUQbAN~DLi(0{`7U3Z@nn;Y@&XJ(<=fJsHD?WmVB7F zF%22t4|Yc^7Fg3!>Don5bjE$l8RYSuVPQaQZl$@s!AQYR3Z&2=h>4CvCE0U4TIfPo zLxVSr)o!o{SKHxW^we_2)X~0}^H~GlcjI*x8A5$sFLe+ynBX{(BIt6(bjruD45-)Y z>f2seEQMJLJhWWywTnLYG&^Z{?4k%c)hQVjbP!9g*;Y-MzPiPsveqBX2CJEO$M;Tx zwN#niL}TzhvEWiR8E{^xm17V^KI-TL$e`VK&=6DCHh}`R^z3;~a-b<~pzm$OhYsE6 zlCqX)?Q6G6U<_RmRJy(@f0qarKE{Y_o5O3n@oMEh6^(RF_?3Inx3U17-dv%=f$eK@ z>l>0k%_@%)8x7E>FeOlqvhAaOnXd92UR1vP=6j=kjz=cipwut4V*IBfgg@-ci{WU-lNLNjmq81 zpP7=;iDq##_aJ?})BRneH{7lvWSHY(etHsQNDZrQKkt{@`u%7*igU2_HUob>asMjD zc7K7;_v^^V%|J8D&6MUYruZTU>nYgR^NMwHxz3XfF|^B=#w}4@a@~sy_G0Xlt>`D? zyg~kop^rDb3{7+mo3@oux{(9{RvTt%FZ!j_U2u&3gE* z@9ZXabrReXt+`#O5!+%I(No0|^_0;3w=`k_-|0cslUy5$C;I;56UHBLW28ORhf!vCu*}h-2fx(--5yq)1?odOV5N03bJ4Dso1JeqoD&(9?$LlW@RqT$uX0U`!zbT&@hnga9aq14+dP>#!QOPX_pp(}0TmFIAJc z9s!#SKXgd%w4WE)q^OL#Il|OyPFyH~tq00@+EPo40c>$0fjFjaZ!wd&PceWqs5Gd0 zkbFZ(hzu2^5Eu*d<~?zti{BsB8><&E;!1lgamWE}MG+;d9sq$|=TaW@6&$7nj zY;4dWMqDyBy>+kS$pLbe7HSf?3yv*6hQ_7c7n_^ghOhAM`!8i{g!#=ULwnkuV~HS= z5K@3!;dfnnAHX0$h5Fy|njZlvk@H zK zUQBLwO*dVuS2$T^ySK>KI2+FDCm#m-X=!O{eWL<=jQVEr9~>DY(zq&!r!v)7Z@y$F zbon_4#<@2iw26HQjP2)V1sE_2x7{iN#9ptPv#=`ln}ZmE6-1#??f1vevx}0Au?jNd z_Qyel_2Pb`H2jGtGmgVzfrkX}l3HK5uxGlgA0tBvn}s<75Hr~qZr`(0Evly<0f;sc z@ZgKOO1ylcL~4MCsR15t6?5UP|Ez=p@L{-@i$o0D?qo3@7g5P5FVw{F9C5txSR{BT zaR4;1{bKWj+zj3mxLgSP1bfp^8pdROG@GP4pO!4+9NGb;uE{nc_G~FBcO%5H(Z|QlJ2IO*Kud#6nzf-WvVAs`qmhZ1YWeA?6Zba67X?GRnqyU-B%zJ3gzEO(OBuROP0u{1y@Kwu#EM|P*; zsGfzU(O9NmyjrPbfMiZrT=fnba_t4jJLLH~KLqpJerCkW^X&ai?p7a_6Jv4(rsexzZ)Zkoc$O{re4kB00jaTCN#KGFV=2cR36A;Mgw&?x^_@we`paqx9-Q253BB@ zhmLsvh-l{dvC`{p^pKMr>g$XcX}~3XyZgjiyPpe|T%OjrE)}BT>B1a||CiMO;Q7OdjJox| zr8Jw~{jLNtckbZ&axtL*IRyCXwlD!E&?aj)B@h5M2O^-f3}{&9(zXF;!~zyeHbHFZ zDGF4AY<#}$tqXkTY+_UV7p@I(4=oSl=St2!MFW1p@sxesCeiPDOuB<*NZMxmK@S76h>Q()F_=qp{~dT6i#a#!ny2^} zs6Z3cxTXF^@Nud3XY(R>an1+Ir=mgYN0oDK^r}B~_iwIruf|oikEuTt&FSk`YP)dU z`kC21UiSlkGL!+r^`|-G^{+|MpJQ(h?BQVGmQUJ3)9hRijGIktH08D#M}cKz_ACxQ zcfB4KTv`4sP(1gcp$PKaWo}nnUgoy|jm@Me@ji6M5enMPn93z(%HI>(Z7$euY$uJ%NDJB^2Vcsd%&d^`UCj}K?b~{{pjQ^Q` z1*QP|;L}2f4$Y_=S#sSJZ5z&a_|e%>0lG?t<2WM(glG#T2Wu>Uki{Fwvo*33df-zP zlPOlDT~>2t;Pe_h$<0OaFcf-rn9HxuZvRu3G_^kVCfRQ|k94GpZ2SwtRX*gN zM8!6OPL^Fa1h@U9nf!)HUg|$gxG*Y8J82Dmpy+un+4gOx>o^b(s;OfncR9MO7 zRt*E6>v_AC_lZyJ)X0g5&3;y~ao*Db)5hVGQ7bOVA6pEUFJ!V31R2_!g9-vvw{*Lkaej=6H8{sCHrd{9 z(B7gV*0;?kCFw(N$Vx7oa8acb_@c|IS{z-ng=mlz<<-b(TIvFGV~83vD!n{RbuIHX zd11CHpUQ{))U|9jw8v<<-H4WHv&H@U6nn#})oFc${=I$M+1Mj|qm!{L6x+k9u6{(N z2qaIytd<-}4kPSwr)glj#4I$(X-ku2*)h3{_s4`pP8DYophvmJ>#UA#qrbl42|Y6D9zWb$C@Qw!gYcfS^z?Xyf%U@7OxQOa&)(BKxo<_HPSQZ59a4d_QNI znd~cZ8y}X!&RC-c`dZi?aAihS`h9zNVxaAoZVMG(tldf|oyVY2r%4fO5hVgR^VPd) zmD+qfHyMYz@+>@Z#y{#zH9Cp)h?N-tUheWqIVF)_sl>G3Lk^bXB zdoITD)!J$Gc2Nm)Ts!5sqTXUs-3a1FT&4&#XL@;9 z%O?{D{t$#0Dc1xKWX!I?*pem?Ui>7JWu*eiN$8diA&wi>$7Mz{nVC`Z-bT#xAVSMu1vo-DXt%IC;pI5| zsPUIq4?qcRrx%JayPr5q@M^61$}*-Rm;_u4Z8RUB(rWETsin4rAMtWOz$oc* z&LOcY;jg@Yj-t#WoY?6FlL6Q9=8`3i@KJ@Lev-GXmwK7DWL_K7v#in|&)XYVMFJU8 zLn>eav6U4jS=!!R6Mq)aDyW*)GMjCAbNYt(yOfzCgN%iP@<#8&b z3Fg_yY~cp6gHMa|N)_~gYjylaw@cSG+=*H`lc~6`3+F}Zahp939~f>v&xjb|M@hUA zn5eZdHM>OZ!0>{~+qk?)ow0)HbH$EEjVMg!F!(@0=@i8XP>a$U&$x@P*6!0(T=}Eq zxl=zayYoh*4Xky6AnC)J0t8h9xDFaJpQ~NlK|JY$IZae8%QaIk*ss5cE93J+v(s?m zZGv^J7l_F4S0$rkIp;@EK`D7{o(6(D+*!bG()22(SLrDn*qP=+(%Mtp>&LVEz2#(fD8%8GzrX!$d z;e{?1erQiefIde9vFm(KPyPu@R?n(k1hH>*(m~*5pIdXlbV-FH&Y6_qW!H>@>72H` z6F!&SYYkyT0(-@aRpRPAax`C2Od`Ots+A|WE4F1RU@E0tu0H$92oPQMgv0)~8BdhC zu))3h+F+n8y*mXx2`m<&5R=4zqHbEqB9{$1_f+gQ!AGS$I{gK5{5aObO46OjTGHK0 zuP}OVEc0$^u_rh!NX5y^_7os6QSt+6-j}*6PGlg&d75;SYJ-a?B!^C_}+)meZmUPOf~1Irk;6wf+4mOXSt;KRNl^#WUQ#kujxFuiq~v`M}rF zGJX~hhfBqj$v0;-mTZ5T8qIto5m$>J;@*y$N-fhA?E3x5QD}SrCIwrOYP0qt1yWc$ zs(@ArNT_9nwk+rpnzQ`@Oea8Z2fUE6dNBYi2;z_Tti}fGoy`HUP276FU_Ff4SR0mCKQ^KyNr)V4Sem(j9j)n@$!pV#1&_J`ztp)SfL|ge8Lv~B& z(h1$a<3T>rs&&}KWmrQ_%wHoSa{eY~yrpuFNL>c#187!gm z#!b9%9v2PNO?I9<7?hh_ zz>%mNuuQOg2@C|t7Nw*@8(*+xNW-YH)rjj&35_n0PGp%oNY!{aeF5puaZG`y2vO{2 z8h2m0kWlnDF;37jEGv2f7g)5jx>?E^?)b>(0%L$;oL5VDDW>0N%|7tA$}pM%S2KZh z@9Ns5)Y(Ri*Z3iU@~Ug$ui_LpSt*{tC_kzfJH4}Uo>7E@QBEd^7LDr3^0>evDvg2r z#zDrsEhgHhJW!0(n`NAS#QCtZgg6Z1TWiNyUr@eBwXyZS&M$ok3umVE{v4aI1J8}az5PwQAL-@UT|0e6{lKFc7Y+S6w_kv z7aX#T)mZV#ck%-T!?FXRdgwHKB@sR<9x>Zyn9ZPUk9Qsn^Y7%;_@z7z)r-`2+J91G zS}D@1)o7Ti))@23^7&qen@27->h&Om0UkD0YqA>-kWM9uldLKOP%sswkn_z7ze*e) zV3Q5Y?GbRfbI{QW8o)pwovFzpfpi&*JhQ?moR4X^(;o_EUWlDly2;K=Qn4_drgu-V zud?p5UmC?mhZNAI$Jr;i(MZjxtluKx7R52)tIWg%jbuSu?3C=UkF8<;fDPb?Zo{kU5 zs}IE^ezsZ#kw=o}S+TzmAWY?ud@rBsGz!H~odQvjJyCd1-Rdqqxi>nMi^O0C`y90J zvlgj zAU-RUR%t)V%^BgLAzhkxoju$B1K8pos1F?_M9Fhx7tjEihjpL=wHQBtOayN2xmgyD z@v`IlodGc~8{))}kNogJFT`nQ+U--T$Jhs()(ELi|-& zM@g?mc-hY{sx<0>{+?k?D9`k3g38My4M#$!+0jiTyvX$iNb5MIx@f|H9&D23sX(0a zh=?K!1wzGO7kr|$)V?k17R+kRYQ2ZZkAYTN#Yo_C*Dp5Y_^$R@%&++QJzMeu=>(AUaE@%Is%kYE?b}b83J>2v@7;^;?ZF6j=X|xJ&nJh2gIh0iw%EodvC7XUTX@4W zN-Ca@ss9-J;(dSDE7;|GEBgtwlm5xZ;`3MjjNPHC7hYWU5eK z5P^-iZLJ>L+jrtSRipPMf@FF^9cp3nDAsxY4KCl!Cb%J_nBZcc9G}-sY=cUPllwU9 zhq8SRs^aX0&zL(xO`yzc+sdP}t z5s?^kVhB>gN8Ic7)t_sL^=qQ*+e44_EKR1w4jeNcY0fU`YMQI6)Bru{q-gc2052E! zor#=W%XSFKI@Rn9c#UsYgy`i+Ax%y-+@<90q}l5pZvE zB63YIJoFHcqp{xNs-H>-Bvwp*`347l*GQS-k8>swDUo`ii^7p%l}qcHpEA6lA%-hF zT?Rqrz@q^};z|a_mV7@FRIQQTau0Cre=A~9t+2_Bhv&edafZZ|`xHtj?)3`?@I2pg z9;7EDRBn7sj1mW!B$_+r+z5H8<5Lug=PzL4*4Q)n=+_^Ariy>gfeub25~MsF@xaaf zAaN6g`&v+)z*U@f^C%(OC?TsjX#GT3;)>==S?_0K8V%O`vzkvIme=;j3 z<0$UjL4C`5opoWoNN?MUGVj<8VlZBwhnkv^y=`@lmW`*-t`p?1iKKS9^ry70oHH6D zMw=zqjxPPx<|j{**SToA9wV{skSW2XKP^s#x)unsfK?Af!=Gkf;lPyBw5!CXz?l6r-*sk2(ghZDPIyx|$odz=JyMbiC6na*0(oKYditXA**rm8E?gWEUs#bgeL zcu6+yH$pLn*3*TV&&!7JRD?MvpnE&q(*dVkN!_1Nx+6U@f(a`5A~mGX4$JW_UDSpM_3Do%EsWZ3KBuNA zeIBLuH0?zJ?x7|KO)TjNp`?Cqp`{Y(ECW&Kx<^axx?cqdN(?HK(u6W;y3Or<+ZIw` zy3GOc7S<|QnM+MwLUB}Pv7<`;@I<-Ppi*fU2|p?E@*-d*+Wdixo_gA1-!pLo3Iap= zFHT8AW3p5_(=_e)%chwOZ8Gj~+=I{TAG^gXm%Uc4Ri44jjwAM>Fgu@VR6m_IU*WS+ z*{LVNdFCg)qG>#uKOM@t)pnMnmuJY6^GHM)eI*)gk0E!z!BW@-{J^bJzpI{+A&DZD z13&cxX997fvIP=cVCNfE8HbF$BXt$g#Rryghvx8ww+9FH^(hni_!ni(lx{z=qRK{- zo%4BUo7 zS>|<<=0?c7xssCq@)bddyy~iS5Qa<6tzJf6v4@cg+xX|b)^N~h9!Fl?ULss?9^~;x zy#6R5@&YB0cmZB)_Ejmv@K=G5r~MqE{GRqZyVKeO!#wtfnP(MW3np8V%q#eoL&K?6 zJA!D=w7A#9sbC^-a48DLFPP@d$9dJV zT)Jg0_eS?M+v~09xO3k2;&8IPt9IY|7%g;mc}_8B?+9DrU}FVz>um98SBw($f{#C4REmH~T$@IgJZB8u z=6Xu-$jhbT3TL~9kq^;Npv92G<>j6~Xw|n6sb8Goe;BP*+D_eSe*WqFER=T+Fo1bF{cauYaB%Jo0|iC?CzMU%mMf zc|xQ)AjcvROLNM2^wy{(%chAS*9M8{*=r-Jr_i+6s0khaK()kpL{B~uhFl7$% z6efqxF28-E_lI}d8)9K2j^_-|PfkESCuA-c-p5C;=&&7OY*otN>Xcv#V7^h$@Qa_M z37q|6+SW|;$rA{mis;3Nk)*ff1QD@YIF0F_!0WiLuSMK18Kj-`{nFY`Ig?AU*X z$x*DR*V>6uD9?sj=(%5FnUiw-wc8P+u-XMVW1wG(iIgx`HJ#D&W^Q)UdIb~vcwcxY zBj-*hey8$=n0ckLhYA}1<-EmvMKyu(=X{7O-sAs2P42*j=4V|}N0^sv6pSs`f# zZS|pro;2Z2rDv;pS`;&L6Ln(MoM{c!`dloL`pA`fEulQhOrCwK>9Yxbns?08c=6zF zdwlX>i;zyDtkcR4mgdRbdC&SIm1o%Z_ge_O@ZJ!rpAALx?R;3TVq%%qMDyuj2E3KB zyP0o->R;m_a>WZ&d$RAib$Xx4%qDnr9DniJ4*gmyqGL}a)1S<@vz^x1QFz&8!6hB; zMtwGTw@o_4#)VsP^1Y!oKl&zkX z2U%-eZ)^k{(-)EF-yf4nKT4YH@zOCKID0iq>PbWNhS=nLSy9cQFgpOoC0 z=v?qL_RzyGz~w;#1cDVlpdo2}G)V-NYsV(|A}6`mZ+6LM^j&!Ol$+c=zR=V)uU?#i zJ>dng_kmKiV&~W6(x^wL_gg$~3Yx72+1*VR?37bI^r{aG8HV_ek-!bwIKBZ}F){@j z`O8DL73GdRm*;PI8bh?*gEx9^^$msN&bzIxB17Zp6J=~HrT zF&_RlxI6IeRd&6|+HeTqz({gKCLEw$aeK(Zh|jQUpr6Mg=8)p8Rk49QDxp~D z!QFq)I0G+C2h4D&-7&lNQBxBNj24qF<4-JGQXHz9;DGb86O0Cq?sw9D)UTJ z8+9*0Sf9i@7Y1f9W`Op|z@0bn5oEetyC*pG__0BfWu$r{oD5<< zO&_@loIj^R>TNuZMowcV%?Aob_6_%6N;lW-e%?yYEl<&3wvWLSlNp(A(SfDWX2)fZ zY~fVtJoigBeybSPJUX$WQ+%fuuPIEG*Ojm3R5wAwaUJv4F3#S1MJPaKKz3;gZn;9EkH#a0<-fjZt7APF^*t0dv60;gJ+hVipoNU20scQ0mioF|~ zxt7ys^Lch~nLUzoXF=ak5@6Nd8&rL1!njVaQ9Cngi7u{h2L~F=E7okiRG3I4U7cJkASRe9M8*enXmxMQ3~shZRvc4oZwX*Q zgIUGPAQN*pE@Bn)7x!B9&j*k-HIii~WhfgTWQbUI)e&RdJMWAhM5mCbvwC-P>q!y2 z=u4~In|CtCY4qMJa+lv7WVA5sz83~GIJt}_b=+DNDOdikyx5whUa-i0qXG{Ke0*kB zIqErPUkl9J@WRYrK!Zia%eBdz;j#F&G!;Tq#04LltsdP18Y)P1dB*@FR{@J#tQ(3c zfucS1UA*!5Awv`Axg@Ivol-*W%sE&er%wrn*ntnFF+l*H1*j=cM-9GTW~Z4peP<$VH`Fo^-Ha=8i+ka?g$Rzlc!V zuM{$hP{&6#iX>0#joB09hO(g(d%2}(Aqe9T*01}n1H4r5rD0I!!{C#?aiyY&*i7#+ zJW=-01^v1PM%G|Bv9_^_7YE4drpp9#W(Ij*h_@tJP3g3%70*rmX2T{^n1DWj?!l2Z zz3#;95G$?!;tm|X5<7D@011+*+2JNs>?7MtE5XS3GFz3(-;&ijeTbUU7gbtC`<5zC z7)4&1oPdf9KxqJ$elp<2_`WFscHahkC6t1KkuF-j#MQmAFd_E&4+y3~ zbOy40L2x}XdR71`P*6(fp_tqi-Uqny8s3M21^k_JgBgJS{0{i}4O8`Zzz@fDWEI+sz>$-OfuAy zSv#lspmm!Jwq=y@rBLF!3gvMA7R>q*{8#_k6%G6C*K&(*70hx!cMB`9uDzdVcELfe zR@Kgc#VM;pj_PcCr=V92Ky}Op^_%%hFG`YKOawY&&5+~YEUXP=4wcO83rYzvy=m6E zH(|0`+arigW*nbeqV(*>8fF2WOG>%%St@Py-W3Pb=REp%J|th% z^ag^YLQji($HY_lDzJ8`ad%$9plPlfJoxym;X^_+TWoSar}06z_)wb^+nq3q%;Z=M zKRtK_i=rGgEZ82tt!fV}H9Qv>QG3HKydcCag&>yxyZCf|;0K#%)QGeJ%^zyhmY~Pb zYnV=Zh(g4~x?pHkKbsBMqxGp8BVqNbi^eN^X@tGlYdNcx3KGtFbJ|1SKX7CAjr@T# zM%E{NKN~AXUGWJN?<)D!=ns4G_ED*f&|f;o-ltHgZ#Og8Z>*?m)Af->bnWVrth`rg zmFx~ge=v2oIeHA7Kt^IcNZ6RInY`#esen^K_%X=3d5hNm_|Q!*QCHtenleZol%a12s=*F@ur34 zGdIJuFV3WY&LHez$E!3riv$ienUY3_FcO}kdz>L(nkZoDnea}S;qR73YnQ%RyL~6O zrHY%&q9WEbbdFWfYg`;|#EpzV^lVECa9P9$k~%w7zn#t|8&G{0KVADGgecsHT92D@ zwqqB~Y5gScRUq-EkUJXH05v>8g2RQQHYeuUCX?*jm_*~KqeO8>!qh@OGBHFc14G8c zAsS%>H37v$5*TcK%IA!$nC13oS*a{TDvgu~F%nRM_g|X78zr@xA5MLE_UPWe^ZDdK zeNOb6#7%JlIX}u8O6sgy^?8)EX=y_E`kar&H09n(aIUzc1bLgt9))OFF|f3}^Nnt9 zrs$@oA6m4n*yWeHl!bE_!LxVwsH9W5=~DBG-Rs@Vzu4~JtI*^s_)6nhW4u#TX3MZ8 zoX{GSK!#GAby>$otyCD5o6}01^L@H&YuU|Pt|ZfM$Z=e#!>PuJS^vuP?wL1&J;e^X z2NH#o{6iGN#rg-MoSEA3X(+)AN9)Zy+cW&J)FAlN^e@f4T(~BbXR7EQ|DkW z{#sEsyK-wteXnTXje^kK;*kND+H}qIlm7IM%-3*Yw`!mDb`M(~(D7`)QXsvh-xJYA z^ArhZ=}nX5)=8YYQ+T-I_WOwhm=e>|E&h|hE;cjA+Whw~S?8!Sh7S`6kO~ca9Ze^; ztRgp}h3VkkEBcsxI1RC1qJFXSiO$tXk23OaQy8+gYa(=0c@uuVOz_;_ZHuiU(<-V+ z+1x&LXGy>%GPh~(U1!kep&mTaQCAq&8E>nSOX$}Y+ji%gb3CNMez&um!_)hfg5Ry& zH{F$f40S^cZtn5lCv5Laj+OO=^Jq9aT@A_IXKx_4Z$2tdPW@V97YBPEc_5= z9Tf;O50J9(DhHyRy%CUu6wJg4LG3;=a|aTb0$}?e*$kP1X$GWVV*}86T`_b4;Nq)1 z=MZ2#7myAWQk{sAfsLUNfH}5uK!qfm0${RER@M$=V8|>AFo+pI=0#=*8xJtw|A_gwyx!BFBYJ%;JQA$#V5AZ|+pjZrpr-;Wkiyo$>=LF6Xl zZMB$5-k7*sWXz9Zpze@uo5uuDgvh~TY-W>L;odJuc-%yPUxZb>fmA$11`4BwY!61( z7sVEib`F_k2ST;MOcH6H@2A%*KC1OWShwV<_w*)K}Hp9vVj zK>Aw9hlz}n15(?!Up64g{9ivm&i&Y4FaP7%|NRzn*B`a80*rf*ec%|RU;Ki^1Kdvk z029xAm1*odRGaNOt;98w2VgmP1zZ0o!u?a?QVBUBK)8?80D8Bd!h~e6y{hl0P&oj; ztp9Kx{P#Hz{(qDe&_{mn)WF;QZQ%MZc^-gtslP^tKaPIim+4?Orr$=t&7`5IG$!=^ z9XF(bb{xyeyjC*Z`d1}BiuqwJrB;+>&FF^o`P*B(GKEm7ffr-oh#VIi zdGU4BY*wMl_L%gQYc-IU^z%rK4< zP+ny;l}4?>Jt==}p!9OHhuXwLZR{|6ExSJEaozjoLNQsI&;Ay5?qZTvx^GSNjtd^Q zDC=U_7dx6~YVKb)cG&Yzj_^>NP7H8CAyUz3@K`)+u1|litcp05q8i;Ef5_|65|fkL z7gtQ)SDxIHx|P?LLZb%dd#i>2ICfDeu`NmZg%P}ucewDlbQs|yme=d07Z(x{F}GQS z#9koa-=SFU^plFy8--mNpXuMK?u5m=agTQH51grN*C{`~b)BDM66kN7saG_YKt5w2 zSviDW?;FAC)$TWC5@5mu0Zf4o0ADA-qiW}*XZc(1aJK7g9+GB;5GorW88T$x`@P)t zeCxjtu)i|!vV9w-ziW_CH(+kwfP#WTg{bmBD0CnPXpV`Yj&Wf~79PS}qTD~`O(!X_NhJIvqJNwuR;|2_HF@{foo&&&ZXy#yHrRQjBYj0*{^h;HyF`xxV z1GzZvkpWXM7nz_HP|!fnnoJrfY6O(OQlTH&7?>G3x{=ZFnmRh#a)UrFE-s9ERt}6d z_9pza_sCq#98Jjpeo`ZQXCp(htJn0(264st!+7=g2GH(cs2c@0Zrp@sy82!>U~UxN zn1g~j4naGfEzde8$b>pdc?o?qaTJL?TUnKomyGZ>IpAX3?5mV|B z4QxhaXr2|JkE_3vU$j+(FKMKEoN%P$Dd8$!tZzt^AEBGKr@KpXP(m>25xo?biLsZx z55B9vU$85I`xDQMxXAE?WRfGIRgY6PSf{7{$;nUSvlD`g!xBkC(*s6BbB<7W@LckN zQiK^#QqwbiV*H{>(T-;8vX1RCp<0ikp^qAlEwSU%BjV!-N^Ej1jeIOUf=EvB&O~9u zLW8}-2z*L>yFw?Q4{B(8lblTe{TRkk03XPNq}^Z z3E!-vhL5SGP{;aP&_`8Ao!C(^IZZVs2=U29nazX=rQx3Wq;`Iuj!py?`2i_KMY%Oy z@_CYWGNB?eBwe0Lnfipf4%QByq)x`33V{TV6M_@cBI43wNG6}=R+JHz#8^AK`x*q; zkOtWL>IXRm*azAL*dqkk_^1UEIHf<%$b6cT8A}osUv)$un}dMnoOLXmn3Qy6n+12O zn+Q*mCG2(s>JkO90_g*CMKlV^-ya-1WMTDV=?Zy2fC=h~boXzZKUciTkTK{QgYtK3 zW&?k7&cFpsvCPZ>OBV;n)eQZ} z(a1`i3|Jyxo1Xt}G6Y{w>kbT|zvS?H2mWTFymmeF_r>FHYj_DcL3%}C0CQjvvavM0 zzSjIsd3udc>}ncdxzg-@ESw=*R#w)l53ma3U;_Asz8!&VSpe>#>npD7SA8pW{RjsO zM2iFrsKAz;odcL20QMxH2B0M8kEJ@0z#UTJ>gczUY(T3Zr6E@`GcjGS@W=jD6M$l1 z;9^K|$QIJ*AIBlhgp`GBIU&b^GC$5SeJc$#;OdI+XCO`d@x>su{kYS=x`j0O_q7?& zFRnfP>m*!k*Aa~{6vjOsrrSoZErf#J3=4y#&CU^b9|iH5E`pGy1X2_XI(Frl z_B|)Vch%Ex-mP0rFJy1kUA}edFIXvFN-uOEd2`}D?+Gs&7mto5YGPvJV>6RRYiwgvi8&?kH)K&`mJvR4CH1imrr7rL-N-=HurnJWPxc3Kb%+j3uIOd_hNJ zA6%p+f_8MJ%OmE7YSJo9^kYw1zr#x0_vGv?F+h5gs zLs})1#o%~khV0g&|EURUhDVP3>n`@r2P!7_XME<*)lsGE#`Q;-KJc9c^_(L+5clRN@Y5w+O`Ik$=*dF%~IMO2vkM19s?Z0yM?BkPZv$b#U zDy>f=DC>8JLr}v`p0(7)twEjyTkeh?*d`(3z51vij9*!OU&pY&jJ z_aI;>Ootz3;`AEM2$Z?6g0xNeyf;O)AIF)wwHhr!9X4P3-ZYwtfe&9$<+H}K$FemZ z{!E1Sa!+tqyLdZw)5M;nA>-Bd>35@88-yRtR4zn-u!J8-Ce%Ds3oh#%m^X$$o1Ze! zYoaB>4HyoN=CtE}gUxx~V9bM4;>00Gr-TZ97WCyAZ+b>w0TlvH^^11CBM?eD7vY_f z+B))<#p6)eKJrlMr~z!}WWSI5?tX<;jER+E+67q1q|A+aU)zH-zY1dWQf3*Ay-HOl zDNjzoU0e)%u{DVwau|WZ;qiccZMXVR4?9E%j(aPIvw9v@j2Zxer;g;HWDW{3JPxtmko@(DJ&6a(GFRcK1{l?z8Z$?Z7*WKMy#?Nu-9V` z5Hqy%;)OXk_}%aI83<%c(;(P4j>lGgeK%2UV5YbN8+!?f``y@rsnaonH*cyq|7|hq zi8&`KG@P!no5AxAZx&o!Q(IGcr7K=!WFu7a5ZDFpUW|?fV~aiX2*@h^)IdN#=C&v8 z)`C`81WM$B*Rxc=S#oFhtvf<0Mg3NIEc@}`l@hnSMf6qVAk{cOAw1c^0oui3qSC|s!@s<&>U)))j zEHp8C^D+MJN&ZGT-*K1H30*avaNGlgaA(O|AI?&Bd7qC?BhmYhP8vL2Cwr1B^mdJw z)!l?Y72CNWua~c~7Z>s3w(gDH!b;9=0cBPj5rZBNzH&V$s0X9#Kt2y(^ z`t*Cr|E}uK@)v4AMY&e%UNZE}lQ0I*L*F~G>D@U^wd6n=`CTf$pn88S=o(mHh1>i0yApUp^V5NNb@0>Ix^fSznG_pZq>&dYeX^hQByilqbzfIfA@?Mb2WW8 zpjLiyMm=WPg{IblmL@@3r8L&rcw~n;YkMwSr9Gzm^fQEuXa`F6` zsN{c5w*X9&SDGjnpjto$1BxgV7NjjOHV_Ttr1q4t;Ru{{+@A@xZ~_N?B7VGwoe-TA z`-I?$Z^GlypzuH+lCxK*d9Ypqfk8e5-bsN8!6e5oPIF-cQli5n3DaUz;-2_N`^J!- zl^-R;9;co~Vy9<@XJtEudy<~D!Ff7`J2_`UbiI?>Be#ZcH{m#P!_Igbo}J|aQ3%`M z++D(*ot}ngd{YRs;ZBK?ekm6XNDash=_n|`=MS3U)r#`x0_&%O!3ueY09V4VFoY{> z$CYkjVFctg_^xLF`&Y(}UnY=md*2RT89hMmM)o!!8*3wwl%AEoq285E1mp;WFF3l` z{4*^C@K9V2T>uu1kcB=ZdCA}P4{?Z*9j5?TQ!n=aL;u;Yb$qK9+YrI|2eWEsdOEShf@_jM`1V7bQ}+4Fk1k{p7MN z)oJ9^)sYe!61D|#JzhmH6XXt?z9%?3=HcB*Zf$$-G4HA)ZB1hQSjf(_Ln{o{k!Wjc zp-ca(W1ZS6_(Y?n04hD)Z*v^9a6h@H3+uu zEG*unwzRzecEZE^QDMV-j`d88&#pypUiLXEE2$y7dz@{2L1qe>P$_?`leZjI7~9sS z=beC^mYII@PKWp7##(>rcH_aBG1Q9Zy*CT}#S$VTdn;*Hr^5FzzDCI(=?>|;&Zs&L z&~=R>zfVVq!6Jd$hPm0q$t~n=NrWfb4R<&3Q&X`@C0QA$!@Bpf#V1cpR5+&b*5{8I zPt~C+<(?T8R?rz?lHXkt5(w0TD%=*v{A^p0%v?i^dFR;HGiT}Pd4fKH&^t5fAL5&+@pJTUSsqQkEkUUZdq`B` zBiz38HgnB1I#}{cv!Iv(g|`w_COJ1xPpTO0jQU$kBGm{|28W#S^$Y$uYxf#Mty~4N zhzHO3J08!KF!(#eyY0Hk(`{#|sE|IucV#e$rp%gS0AHRHSJ^llDsu;|RkYYl?r9<_ZE}vT5^ZLkcpvyB6Kf4Sh&0}r z3ogc5%lQnOjG8iMbN9>GS!nuVoulsPv}Qg<&#>6Al$NmIox68AVJ;AvZq#y%82o*z z1uPA}2A=l)Y{o>Bm){ zMjZb^J*aq;2!yyx(iQ~oxOHl?PuVTrWYw1jDm}-zWL-DP262=A@IbCxqOQ3>A;Tsx z=>NVN`nzrFYM}k=n4i+ln}@2WR}zk@x$<`DH6&V`Bf^R+Oje=7~9xk$le(i^(s5 z#uuM^>Ao4+iqv!!`c0^s1vC?cV8r7No2fWUwIk--lwq%_I0ng@mD3vrOmKcZM%`oR zWQZ@>X{ASrq@(kU<~f(@P7SFo8@IL#TTe63d09K>kB*6lJm&&nNHT|TE2nClM^g_3 zv>Tl^`tv0hsrM$Sk2jYvt31-~o>9^IW@PNE@|&C&IEYZg)BuEU1WgB@W7VP?`MWW9 zPeK4fiBb~Tvh*R)z0R;Q9HEh&@H9u_Gjou+|Bn~?q zcfhblv^a1jJZzvwWl$&P-az6i<<9GSL-$HwzZUl{ZW2#Dvp3f>{=yj~Ixpy}`dVmC zk;Dcm`UslDh9LTgGWrNBx{=BE2$SDN?dA>_iOm+wTu^9R24b6vTbmc}92AMqSHt61 zj!y%Ecb|SJM{nz$c`*}L72Zj$CjfF1I_w<@c=$A3rEh64cykwqmvBe&LfKoVJ1C$P z@d$PR2JTrg;xY^wm~va@%vnF0MtFZ%cvxclr9`}eghTwjN8Y@BjNS@oX<9 zJp%3Xu|$p7fgOF1vkX}1k8-EYk4H_}2oSpOe>laS!V1NabIFawTH0O6^p}XdFG3N~ zmp`l!&>cYAuv{)P#W?-h&v1;j6ukbiq1(5#Fb%45$?5Fy10m|{#~qsO-L={xVkvCK zRg#GSL2sTXqi;w(AKcJ}PmH_Ihh3)hNQv{vauEdJtuF|yWKrNgq#!+Ap|p>-dlCO$ zh;bn!@u*U3V^5tyn^;0YjF^%BMae56m(7jKrWvQ+PyVFyD6x!ayF<5-yF7HGdUJCT zh|SnzCeQqQUh+hY7nNJdtMp>_z;`7W=;k9MmhdSN1PWF>N3oyv&P@j=@(C9qlvhN& z3)}Bop*;~-mz;KsamAEc@9p(3>5mn?bAeup36L9-aj~CbH8PQ5E5Yz8qM%Z{n@;PA zmQe~%GL=uLnjqK(Pi?R=Dt9c(3QE&G%yo$c&zU(Aq>cN$WE=dT-uy(aoSi^PT&bKJ z-PVJTQOYw^(h9~xcym!K3uRYU$DrEcrt{K^QH=gi`L|Q;OY3oRUPWwuC<%PedUspG z`Ys2unymvBIw6yn?;RZ-GKE4@Rz#7Fy2&jRt9pHur4V(C=*K6lU9wxcjQ64Z+B(-B zp&jWGaeu9hvl@4r?R;>5gf9kV^|X5R`5c=L=P^pR^jF%t7mv)PJY^k_&$xPzTprgn zc7|B?XX^)=c~Hjo(ua$EHRu`+C^j z6kfXaAjMD2ky7ongc2_$eXNjMs=ti50on4#GS5O(c1DU&=rV54wBC(RYlCs4tyx{O zx%HH5!hg$_RN4)*-Q&|29zNIo{o70%(jro`YxAw38?C{V(R1T|<0}xML!jR@~iB?+N_!Q0Of8ohp=znAfGvMYdSn zr11y^n2zyZg}KjKS5D}v@O&O-$_iYxd^3GK$oNt6&;DFGCP+saFp#o{r$tp;h7yXOw6|O7FJju;TIc!w=Opr?g2-V8Nu~ zEDkeZS3|W1ZJFv;p{r6vs7Wj0bC*0!Fr!WBmuA~O~vBmFsM^|l1+6$KTL;8 zrE*F`Kj;2@diq4qWW=&D+Pq6k3~m~8ghG^F3hPl2t{P$*sUA&mIU1&bXnpkOnUhV* zLko%MnNMz{ku61VnL#W|q81@J-SvSIwWuY#Md!>9t#iZ`f`uIp5o#R+oT=!>o(V*W zwp&?QwT>o%XJB)`?kfh~6pLR-jF?&#)r<(Y6FT=kCVQ{4YdbeI+^}nVm)C|XmV-ow zk0i=0>jjy%YzqTjujL&Cw*EW0TJ!$ICOIS~qnNp1Y{w5f{x`ExQ59CGyFD=ivn5o9Wl;INcT;ldrRUy0o^$^m8iC!TJC*En`^J<>>SP(m zG(P+Ndz;vLFc_*~_5$Jw&((sWPc|5ENJ+?u16EHr3!=H})|F1^LsIT0ie@{YGq&Z< zOiztL=Rcl9a$`$?nwPK;y4@==Q?8BjTK_cAak1UTO^8j{!n0^%!D-sgdF}<4>+3=M z_LIhuhO^hx_M6Amc~eoxM~(@+rB6y4`*uI*qZ2%56{^}1=k+?+B2^W2CY`N3?Jd;ipd9OIc^pN5(*dfv#*41PUFc$HV|2pb1D>S6ZcIZ_;qe&|lUy~Pj zYkVH&RhnOH3Pl=~@<=hO+C4V&%`TY*-StxGG&yVTwGiO?@B6+C7_>#u1VUmcyG{nwDA@Uj&!JNQ<>QjmZlPx9EE6>Lg+6})XO*v@rYG36 zAiv_?pa$^R%Z|Gj$7l@URqywRqzcKuhubw(xjLV-#*)D3hJf%$fV=np5 z$P@V0O{GVMXD4;(oA2|cs=}jJJc7otS&zGD(CJLob;7@vMR9YW!Ks<-&`^{a;>F)O z;Ni9YsKE4Dt5TsVoNAy8>dpfBf~zeI;a96C!G~`JdkYH??rkx}syEbc3L2dR6Hg-@ z4@zjlCl9_D}w2p&qLejdLqp3aZ6&~)Y9!yw^r9kW#ph5bnYiCPa0VwG#ybNOWvJ= zs}kWpt4;H{8PB7xp8FuDM>#e#NHn5XpqcGm_xXK)Yc^{`WALt`ipv~r9S_Dj!iQ%hmqx1l zwz2Q}q4m>Bux3WzV&rgP8EejAHZ^AMHbw70UdDfe6ILC`kg)P94C$D1E&d4>R&g1| z7Jq`WR>XEGm&?8sJW&_*uH+VUyVuA1=@l^wv$`j?c3)JD$x1T?I8nfjF!u#&)G^i+ zT9jpz^xebzbM9xAf0AK_D;G?*DCtdd*g+mUeLcqY!Y{krMYksf2|&1BxDROz5lhk2 z(exUYQbsZLHMQqh6vBT>9e%31u)5{;UPSb6$DJKrXSmcf+}6?iY%moc7P)L`cfK~+ zY(~k!xU?ENr{=W;l@vcKO*Bk|qOF_&*Y)Rk6+Uv;S(aa4_d=_*&7?VLSVqCRM;nB`*qq=HeEf9+h`Md_Mc07Rp}T? zW5Tf#VZ+EBGrw5Q7b`Q-9&&qgJGWJmwrjWEe8ZC47ZH9+5(2%AI-!OAxa0EKV>wdm z?T=R3%|YamAz(v$_?sQ-Jma6Ok1-!i-fnnc%z~yu-GCW4GVL!Eu@> z6{T~*Qjwhcj)gLx$K>;#_GgNTC!Sc@U_Fn76VSF;Daupvc%DyaOTJ6cx?e<>)!i?V zHPEn#Ui+1vZ|_Qxo?>76FD!k|ncyK$2r$1n5*Igu;bx@-9>Fo|QI5VV;aUEuy%&SVUVrqR;$3B~kqL*qOgVGA%)YsxF z)U|h*y-azJBRV(W@jTw)ofu=!>1vOh)DTE*LzTNkR&W>_>J;X6VfI~BMur5OSLUS@ z)v69COK9G$QZ?${x@(N@it^qgwGFCG5D`2oFDqiMp|8os#h| zP4TTc4wp3-{8l|52fgGp8EyXH@$s}r`2qc!Y|09{$C&d?*3WG;&o+HZ(>mX5Ip{hc z-I)Ajis5uypmHt*@59#p@K|$&a>0h9{7kEGSoa>)QN*3*PR6==mi0N|BF&X8#A)(| z8`j7Q?6>T#BsXfc%i23~DKM~6Gq~N(z@M^ck^N7_Q~i#hkcFcgoLv@eh20^|tt?~2Hm zmhO$U3duwmd-_Kee5PjY-?Bt*Q8!B~YPbG4p_2u@UDn1TT-|{VS{11o6RJfyT&i}N zA(kd)EIZ`guRASzVMBaV;<3Q$X-2VTj^(WidJc;V+If!jEqFg-fN9fM{XzRDw_Op0}vwN_)O13_?2&`IhZ+6Y01<4#_`Eq+1Q)SDy zhlN*0LBq|TkwV$b4)Ljb;Y~;|yv4q}(dDi-S8?uxs#`=#sd?X;Xj_9LiNSd;VcgsH zo(6+G80s?5&D{mrHU&g`n{?7c~(&SF7;K`#uiLBMM{iG=(!SIq^8&Ea)Z~rk%8t zZHArc3V0WuTV67A-d4toXl>T-j=-gNv6>4E%Nzsy4Rn4H{_4FqB|sis?c}T0;I3kF zF8$#3sttZbkf@A`#oq2JH(Ml5D%~r|=kG}@LiZil8nlm5otyu7Y^5&t3NMIkqxi3qblPZrW z7z!-o7`2XMK#N=Ke|au#f`V_`>T|%e@ww7H=-E;x@+6TmrCTmIi-sOlE;kjajXF-V zmuyT?401r)&Tb#su3a-|oDt|ve|A~0caeSIn&(uum-NiHu7EgrjKe#qtrp+R<;=JN@Z`_ z=0ZOXrSgITPEz73DK7<&UHe)=Pngk(J4?`TCFq94sJ{=@;!C%UfxY2~Pf?7jw6@Rz z7Copcc*orpgmS63sBAPXtv;FI4%?s|?Ca`y;_w@VyO59EkKx92&Z??uA!1OOfK7B2 z(Z+ryti#z?RBq(NP&-D27Q6_twz)d0ti zIby=s>A|!`Noiiub;XkVK+>!h`8mcSmh&{K!RhKC7d zL1eaeqEEEe4!#QNamGp>$n+9S(7K*=Qu4jyUmst-`#=H1)FWXl;S7=$mB2fw^AEP$ z>yZWj$QTF!t*%+5fB3CYAfePk(hxH(8^mo*@+}JX7zls$1;Srp@gU)^Lbr~@ZdV>7 zT!p_{+5NA%s1?`%4>i!P8&Eeepst+LQf(0DG;et0aZ+O=5Fg#Y27OF^f{tAr5^hc+ zDRK%O?n3CUlMa{jIx4S^^ehD4GW_u!5;YyT?&#(4zP{z~ZWPK!HTXN>NhN^inKyjn zXyFKHyb)^k2=S=k5c+uGNC{gHF2vPW+QSzi(4QmRlkij(Zm?~v=Xou{%FFq_1A-&0 zQ`C`Dic>-wK3bJVzm$T~jJis@Ov@;eroz7BH-ww-_((z=>gb#eULKf5ydLOnpR&Vabh0V!Dm!ZrfLED4BN z9}uVsAa-Ry=z>3BnpxPIe|>w zY)y@>u;iGV+6ceKjIAlmIUCx ze=Kpgj_?bn^!GRf zj_YyIK;d8Rz<*a#T8N7gctZeuC?v4rDu@8`!T`+&{&xU?q2CL9Yvo^((q3U4e>nGl zj;X5BQa`?xw2`Bpp`N4OPj4bC+qV$k?{0ox0au`}0D&ZA`bH*Z)_mkUt)1j#W`=y^ zDs0kB(ze1zre@;q_C}A~WfTqEEeyB}$p!e4cwM<&t!%A;y2)IvEUg{5UHQoM3~ls{ zxPg7hVGudlRS`!EK5{|G2{Kh_Ie_ZH-iQp$$im3XV88^tJ!~wDtY8){F7_)J3nz$) z4aCF&JZf$*CpX~G`u;=Cj|A-T+8Y{kD~O1Fzaj7+ANe&{iwi3R)dB)AFwKkE7MoB=`!GDy98`Zi9EeB|Vi8~yzG_RS0pe%{B{$=>qn zE`|mmBTFMI$nya)GctpIRt{XtYiPi2Y-0~#aQO9XZ7l&Hjw5gvkgcIHFX-2)UpIo3 zcQgZHdal0Z&zoO;T|rC7-;YA-hqTwy3?c?@OFe57K5|zEz_b8KE>6z>7oHAY(6_?B zSMu%X?{#0b?YCxpD|Jm!KPtIuz_sK5On^W~`6)qb2ga*z!U*uFfL!%#0qHUQE-o1mA)s&m`3V6h zfG5QI`x62Va{pj|;r~bEFF&|nXn7$$|5y2cchiAKEiL`$#{cMER#v~a|LPKM5gP+1 z$O|kX!tdl{X2{LPWXJ+G2D32ebF%6)fDQDF81#({*%<&ZG_wJlJ`yAj2Gnb;#|cEmF#|L{U@*Wz!1|-! zYuEmla{Bh*fw2%6h(SL`;vetl&mrW0Warlx{vWCSTc!WG$-gAr|A^~9;`)~)@Glkq zPw)DVxc(&x{7Z%Z)4Tp{;zIhR%K-)hK5`d8Q~QGz?q7(dXJ+~%A&)2|UhfBEoCW~`az z9A$GbvZNn>dd@U>w23qDt?mS&IP6F5!5PNa<}o;UY!e&cCAk#mfE@8j9%7kV4Lui$ zKE6)q!%yIrImfEj0@6&%cj5K(oLMQp!fJl~?0SVJW;)A{yM*R5U9}ke5KbLyN!VnR z=^RHK5nyqtu%d5KWer~r#R+eksvl5kVbHrd^CtAFEmP|cyRLRS6hNDpEb@M8p4cl| z)1GhiJ;K$uk&==Wx=4#ZK8T#4>GcikWo~gjioi4>SKlpX!#PzswzUZ@>f=J9Kf52& z8$3+d<`qX=?$b*8G8!<$LUV*s|GfinUXN-1qmX~v5g;Z9=3jxDV1OO+H?cdaASynZ z7?+7?h7T5deL(kq(j)g=smvkcL+CBRa;4ky^6B@{(Q~Elz7CQn^QAN zQ>WLjA5$-__b)FuusHSP53Xpm;dLv$L_91XYn;2REPG$(v_0kFbIJpJMD7uA-lxO~ zU+K8;mW1}AhAr051wT4lG$79?ZOcMOrhr4r4UX!aXzkU$nOr zb8&1TR6h1m;=}nTcq`_G@R9aT33nf6NGYHC(kKxaHL=JOli*_$R?HPg%opsd^z~5} zP>IlbCDZgQkr(!rCerB_N=%o?isDABfN zTf1!RvTfV8wad0`+qP}nw!O=?ZNEBr_nz*)(fuzYGIRaPh+ONNV~#ne8fTg*^iVCv zq(qcX^Lq*l?+z*KLsN(nG<>NwR9`q>Ant@)AXJPXKKv;BPrm+~Xnu__9uCbGNi}j0 z2A!9$U9X~e>cPI>77!xXR>E|=vqA$Ix4r1%NmE3P`$!(3>D_f8XRMu&GBUx zMv&LbVp5~HrFx=xEOHymb;grCAM=5itxj~RBP`UUCS zXn>P1BE*5^;c5ia+CM2sQl-PEb@Ws3m-k>Hga>1nGB}tD+t5~22(~KJK%46W)6E8f zrO|ECp$6BoErZ-5vEW}p`UBoXKAubi+XJc4L3ucQNCMnFvCwq!vBXz84d?})l!slo z)EL-+2aM6xd%K*MAfXY^_na-OeTqqvhP~dc%t~e-HSC=638oz&nmy}T@f|cQgId@Q z-)Oe+z}LNfIvx8D62?D`Z4MeUX@ptaB?ZyeTS(Xl<71uDV^2}!`Y;=Dd^KJWi~;?t z-)m%EQezBO&Mp}%fY*JduCjov{8Lm@TH3LbMM#FCHj2O{IzcY3VuHMAeBNCp*2vj| zyNs^j0fJxA-(VuXcRa3rV6h<~{u3*=s$Z}y4h#W9JnX{Izjp8u0I4EHtrwiMTeG8U z(IbjFX8oR?!X<{}2T+)=b!Q>Hh@9!|G4#OW(R;O2eQ@mAQ#e|t9@=*orG6SR4QbumE-*$^uw z*y;yCr{j%!wBY3OE$e?m_oAQcz#U%c68A`G$Q=()8VaMVYWoN8AZfje_-RPlSaJ92 zY1T(SYIX-jRReqxw!wKX^BY&3KC8t=P-pWK7KyuZTg!$GFxe;r*{l^ARwQ4}-W3PQmHANK z&_4qh_s~CYc}4#O41}Ygil*`y-~;)6#O5q}Z$a{wl|{O(39l?32=IC!9syFB-uaYd zH^h>ukf-e=t$~VE_^usj)uK%gJXk5K*Pdr$-?B102`;O*W@1P5JC95cR^0VDBBEdg zjL}6G_yC=Wa+xZFK_sN{l{wfaAUc0OUO#593g32!7IchO2~m7lBXO2f<^`FEb}Q-U<(S&o;{xrN0JEblORmU-bJQJRze5*+?6uj?l{!0Wd)UquQ)+NOv^r=&2n}o<4|XQF z#=KOvv^wtjRTp*t?+Smc2V1a1H0WqjP zUL_Celju`mmd1WyIOB7&bX|~zGm0;4mZrt9NvRd*>M~{CO1|GpuIKonUNmLpu3Wp4 zU#$8N4@_T!=xoznJyvOXTag#jdOshPL6g6&L zu#)@|+XXhA%B$Syol??GsLn`MMhQ-`EEB9s6k+QT|5#&ZpXt zo!IMHAdPCS%5zFKr)174Mw(jQifD5bE&Z-i0i3M>qN4Lw0vAdoq@v}^=Mj$KM1s=f za7iaA&oaL?tG|AINz$NEOyhDXOkqb+R~W!C|8?G~-e}Ig7jz`R!3;hPby3Zc)pw`T zNv+Y^-;|cND^+{e-vQzO(f+YY;6qph74WR-c5wfzrB3-@ErYm#Yai*vq4q zp4GWZZ-1zMBDkXE$eB9be*_d>8qL`UIIF_ zLfp8ithOx-*?Kn%<8MU;rTWO;VX1tQdI;pU&#G8v7We$r(MS!_$Q zf9(wTB>}Y612?x1m2FN3p39uV+>@dccB`uJ-nA05)k`V3rj`5RAD}N;uYW&YlV+eB1Xa#a z-@uNvy7@W05%I;mH`PK}!7s6He&-GKTQX%dI0FP5i8IEPYp(xurwR6Y2(9tFinambDdO6b2uLqo*vZ>Z4P)ey*oIQ*^HD4u^ zN7v3`&+TR|M}LPR@guggkMfNDw%rfk}a1jJ1 zVkSU3A0TeKp&+Tni!$IfH9e53KewH1KOj1`Y~vNkLSzMcC*%#y$DC()YdZ9UqeSbnFUB0Ie)(~KT7E9$ z$9{fa(;GH|-)BuR|1ySe^8<+;1~Bq=ewR?f%ML%~cG$`>aRniOl|>5^<-t>*8aNT9 z^CctVJ!IvAyl5K6ijavJaHh05jf^Gpj@;z_1Ga(%&Rw!(93U%+?30q6D`O zv2~!gm8em)W3V>&nR`)@Gyy}jqS8**ilQdCl8K?TG^Uckd=#+*f1&G!+rA-o%@(cnv`kExq7YgP?%r-c?XluU`zIvTAB(&{lGL@%*>GVd%{B1?5nkHsR0x9Mg4D4*VfZuRUHOh!58 z5KngN0F`Iw9L;n3<&IZ!7IFrD>@{}iu56$kYGko}!ewc1VBKU^xw`3e)#V-Ye-!s* zz6$xh7+lp3kOd+Qz+PjBz-->yV$Jm!B%hFuae8rL&qR2=VDHXAC4;I4H~oKxahy_y z$FD*M^tmvURH@xtA*a0&C$qk?oQ~U9IQM~f_C+}c$8?)Y`bB^qX~?zPh4hUtX}>lX z`&q22CTUhXm5EVrX3bp5{3S+a*FYY#b`#@+urDlxw4=`l?q~MIqfKkOd0SXtMg!@RYINRA`%A zwS|dRl|C47*zq1m)MvYkmpNtm(AaEc%H*D6N+mMIr{9O(a0d7f(mpy>ba0XK%vRi(A;u%Fn2bi~S!Dg9K=;(3ZnsGpFxih$+X9zR%{*%J9#5tw zH`ls`unf)U59Ik1ziAOvG$Uj>gDo@k-Wf`9)SK4{-cATw$T;vPcrD{MWiLQUFqFjq z5NG^nNAQ1xNA&-6E&l_gFx9hH{J|T@X#Y!I@^2TP$v;L?7}@_zlkx9*{=f8n|0US? zfs{;4f82dP#dR}VOE+45J$qW~AH3)vCH;TFNKV#f&PMhQdY1pT{r%UYdiM4brKc-y&juL<9DaL;?KL6#t*zj1lCmM+> zS`Fnin5a1!nKV)%-^a<*;xQ$&uXG`{mpa2vvr2#i$!(V>k^?~jP^e0K{q>o<)!0+- zy+L@VoKk7-%hi%;0a;x*TfcrPXzb?J*F!Eon>~p#^;{io22u; z*jp8{qxj|>S9Ku1P%Uj9k{>{fb^C&ORTLMZOnx#t)&{K`!(!-c<|Misz$ptW~=&FRk2pt zQqk({tk&zph`L+*%8r&SQ0uyw%k^@`dXnR0)WoL8q~=0Q#Iw=hx=|Gn^}A^NED zFiDO>$@Pg<&1n@^4{?pcy?*ggdmT+p$CyX!sy21YL(p&s6NBdq%5Q{|xB6iTn`9dc ztJ3!#`GD$n&Bc}V!#BV0t%#RW3G14+79HdAxibmbXxZBPTCu}vkMh;iz(wy5z3u=|LOYEH*lq{|)x zHHF4e)sFS642HhChcd&XxbE7j2d|Y$F+&)*yYn}d*P@RlsX3Y;5d%rD{DH=X)~8v| z{ETx{p4YYr2F#?M*^pwyg$<22qG`-a&<65>w2YGrQx2oz>GymV#|N63;{`n+a~!6o z&GnQGDUMd^^e43*a2RSnoW)C9O7UazlKavx2F+wQDV8HqbGAh8b#V$R*rRVB5eI0R z;W%(yjr4(fP$z4!1P;9&FjkpYIv4j=2WFo1IQPae0r$xHFpc$91O_xSwwr(q{_?H( zz`?7~i*+c6H|@dzMTJ-)ZkMQsdzS?L_z;opkz+J<3NYJjq@@@ksU;icx4%TF>kd>; z^>2-7iOiDa0fE%c?Sj(-IVB_#Br|SrvyB!F=@*fXhqX0|LQIj*Z?pkUrQZWdQ!gv?(o9AX@SFLe|{zQca{~?xq5)!83o6yAs0@NMl9E z&rB!EbHCLG5%xJ?bR?&1K4e4}lPC3)pUVt;W17Q|F#D+RaxxI%nKb=oXO!7{ch~pD z#I6K3-PRr@H_s~|^p1&ei%mJ6W?JbY0y~6hJa)jdUL^bJN$kp%cv3SA#eW)}*Yk0% zu!DaWRyrEmB0CeS(oE5EvJ?HsB3PJ4mgVH(Y^W11SHdz4{zOjVn+H18plHDDzJh#t zL<-elak=Z1t#_?Q^?)YNm50eOV&{S%eVWOTS1cvQY{!*%rzSyu@!Wkh zT+A|C4fp&sSD4m(?-$BDcgtqEo=5P}&1mOpSx9`jBa}HSiSf*U>k6;db>)Juur$Oc z3J3u?i?}^VfUN8rw!HiZ-8=SsG-2^xl{=BMR|a5v_G@O6D-P+-i_oXk>5cD~#;baBWL(S+l6Xs zNTwoA<{XYn67_v-W`HvW>MUYy*3gxGu(9X1PP3eyBSGj6cN?Oy3JIs9P4?F@`{{@7 z&DU1#HnOi^XV@{4AA!Hth^%iP+7%-K+UhyMK0<*GUlXBxe<+PZphmQHa)xumTNt)VUYzH6iY9xT);up5vz{iZW9K{t8ueHv%%$L=iM zmhtFBr-Wl1#K_6z98C*aUy}R&5?YK?#j4wyDhn@|yNHiJ&rKH}vJ1yrF{q`WwPR$J zek%Dp%I(+<0t%<`tg)KX77@HzI&A8(hY$iiua_rI>2Ze68WtsL6IA?If-GApeksF6 zt}mK<1SZI)M+%Ecd}nHhHxoy7m^5js(j3ipB%|Dc&4T4|$!KWnUk>fgqdK&k_J-$# zKBc|ac{_n{qRJ7|oh4MK@$D7!@R3@*>- z$zrxF<6GoVsKx0=HT~1y>-->8hZh#jZ)?eKxi5-xK>Em=5P*9ee`RR977E-OKodvZ zK&pX&f{|j{b-~!0qI^AHa_G)HvM; z8s6UHEE@n0BQvu_@0sf1w!#aJ^IFzdpPf6iYtOyCG)xrlvb0ohnblC|NEMK+`bP(A z?Ce_f-fp@ahz+{QTHSUFT;Qgx92wpBEN=*hE^$oaJrtg`~;u3ms?wzi8D_5WzvUM)!{X#2r6Cwr0y+`j^+eZFT*HM*rJjL zx69)8-El>TlC`RCIE9X%`5wQOnw`e$n4%USFMqkc2*FOrlFD? zl72Dgs+nnnFXmvISaugO5_h%?#nX-VV3dr=1N7+N2>2OZWFVt%*>-S2(N_YS+PT=f zcfsPW+vm-To3KFD%l_R8p^L!Hk%3c0B3XsXyWjK5VrGw{fv89KkJ?St3BIe$3Y_;> z>*dvA`i1kOpmbbt@gabm(|s%6Df}M1KD$gkpoet3W%xvfeT$lWM437-_&K=!ei|h+ z7afy0c3T{pLQ8c*GuyOy8s?@kUJ0_~0J@3EpNZ16C2HNJInrWj1zeep%zj&6d*-f8eR|FGnBk}SvU>I&Z2X}p7A+ZHZ-E1p^NOra3kuS+#)W9KU(=J>VOYN; zPdueZRYz`Ky(1~(LXSjqupCiUeaPyBwqd0a$Q=v3I*_uE6@Vy_Jiw3~TPh>JR7nTQ zGg#=9^JUe7C{vRpXEB&91%Hzm0$q9xu<|DYpEq<;x!@k|X}N*dW;tTR1&2ikpC(l9 z-sU?3V45h9KpIy17$=TdPCo@r1!>YqU>f}06dCdpe%OW1*WT%DBwkmz>s~1QC3^kN z66hYDAdKIN@x)UGvD+&2HW>eHM{y*Zt?4t0WWSoQ3b`;%ahb$z1L8L)>TyRQLwO9& z;7f>^C#yUS%3jz9PmWJc1Egv`S=nsH;%lF8fEm1Z}o;q|Xl2Zq8 z6BvDOOw8Fx*nV1V`=y2Om?PjgXl#O8q;q_9g9s{H2j$@v(Di8(1#7_3f-d})^fQ=> za`d~i9>AE%av{Jf-n0JxvQla zbK>MswjPi)%{~s%sd1BU>~oQ>!15@KemMvW&*0$0bT&4^hUqrW2l*ismvlbu00t?q zKre44MqhHJV-oqCOT5UBBjL{HV^`Z*y*MlZ1{*3b+cGAgRE1`poZFGgk|DO`zX)!z zC{74L$#5$aVdT&v=0oe5;6^ns)^Hhv0oGFkS3M;hk9id|5mWOFvqX9yxvOz+X8;fN z>h#HC{J8kc7$XkEn^ycA3=vw?T!F~43)YuiA)YM@J#+w$4B4n#QhC+$fT!$jNpcR~ z+XglegL-?CD ze){P=ZI8(un@2C6TC7AyD2nMs@TGU7=l1kQ{L+XkuMY=|I|7{D<~Kj3knQGVEW=7jQ}087{DGD`^klStT&%RClN9B-8;m(vNs#=xqb)WY z(nVuogLO7ngB~@aagmAbHcu9g^yfx>B%{o0i0{^%OSg-M1lc9B8>ly6pbkZvA?Hkv zmrxZt&%y7=CYW!gV*4UL6w3N;y+{*8`y)WdoOo0R}}8p(Q*c+A_UaS8Aj=vPC90lmi=# zVXJl#Z=esGR7R?EbLOwKT;4!LRc!g-O^u*|KFltRRn0%NHKeyHtB-Tpo}5$qGW)-< zr8K-lYfXu(|E~7uEA7dhryU4e1jEk6qe?%4!5^93F<2344QL>_nB#h^^-Q&vEBde7 z){{l~izg0ZG?At^dgPC>B$;^9de%v&dWgR!KsnsEZX{I79v3+iom=atY;pZc*k=$( zHrpC`@s>(7HlGJW4g7MjS!nbKPo|&_bguY1c8$BP-_y>9TvY?;^ zdBo`Spa;Z6&!+}{3$6cr(99KH%GGdEV#02ImYY$r(yv`ne`I5RVZMm)ls7;(K}z-W zw>5jij*5UJw)P{M1Z2pGwP_>iSW2vYO zUUfRh>;{p*4btxp`E8(6RU1L*5FyNRa`9?9JWL2RBAs{9ciE+h1e^Cj1T45T*c~4s zb(Qd3&1%nBu}fCI5l*pOIUa%l1IggH;b&Vt%rvpb&=}{~APDfaO#iESVMrwbUqW|L z;QTZ_K`;?mZky@JvAQh(TY6t({>LxRl_w<;$8&c(ImQ;N`#g0>cel?5H=i?|FkT2) zRirS|`Mj|28bmh)JN(3y<*RUTkHUmm(NG9vHa}NdB3am59Uf4`={N6;mSYAu^3_HE zvEKLh%K`NQ+AGGGRJHoc3%E6QsM!MmwIzh}QZi2#!n2TNXuwpv5yDLnzpjRc)v9|# z;vt}7Bon0uxz}_>R%P59U{CaGi2W^OanJbi7z9i#8i?qNF+~8&exh0wTl?^Q-mY8Q zPUY2toH8F4mGyeQUy%OaY6p{QdE>hlPtHEbeq4|8QSJvgm9L>LuzPqKSauP!gd z$l`A_GKF6hDHF3j>lJn9rjvLn&Dd!&vjy#ZDxsph`d)8eI1oj1A&9<$J)Kl4G=J^r zith|~G-J^rIv_l5qBnX~RI%%akV0++;yj*HeLl=b8f2dyehv2lSXC1-Wcv|Tp-Edt z*(f%K5pXYF@9Bu1eohC{$m*sE1Jsw}IU7#?l!B~_qad(>6bQ*|n!SwAd$1w%l?SM8 za2qkF(?eoPMfn&YN3t*_X5)yKdT=9e7(UjoxU8uDs9onyxq}4MELDJE$*EMKJVt&* zL2?LqCxKzMX~w|DkP23CQe;hee5z#;!48idFr^_bM_?^S5{TeKp}iOeQSGEr;fc!B znS{jLcu?o~TIXi8QA%Iiy*WJ?=Pc+z^HVf#!O5IvXiMm1B<_0>+I1hR5in|>-YyLr zr}Fb8FT1GyF;<{Ht^<5=*v_NEFRDA6ocI>RCO+IYdhg&bJ&{$%9j!ptpGDQ_&K9{R ztS4m(vL7vU<2HnM-+vB(dFPrJQIE9x@?2ItYqn&-FJN*TqHV;##0ZJr5JDHV0X7f` z$>o_mp&K_2r4#cV7%k#snOM8baYE@Lq zC-Gv~NW1@|52sN0GTF=_KTDk((7ZMmp#^c?=1w0E#<7P9?q7!1SJ+TqZ^6=>>NuWQ zuQ#H9#Nyqet3cqP7HWOnEPNRUsiDn1erR9FK>Uk zOu*Md+m70~S1ErExhkgBV+Xec#L2v|()#hW%ZoD;d`=AEJC4+g-?Vko^{EN_<)r`$ zZd1}-OeVvfafxJvher6(MN#fY&n=Tn2?KKk7d)l*j*T+b*4Wk=#EJ_G|k zC^|I?R;Gi=qXAC(NAR_%)6mqAj@pBniLteb0M^BPIY~7=%w;Q0og-)Vf1Sxnt9cZo zKB#;PhDs7*pZBG5Rwb&7f(}1Nz19}T{JPQenZ=C)omJw1rYuu)YbS}e-5P*z!F(eo z%-B^>6o4!cMD05li40C2oES9?G~86oGCPTgmqOfNP^z)Wd^H5u1qu(}8!aIx&rLap`*7-;d1ll-1Fi}WO!bF7?YVZmrl+7}%TP*6WuHcp5S%mP%x357 z2=v8i*1EIiPI4blv0%mOq=VNX_9q>*+HGzgtL+FfSJD`rT$^lC*cTE{&hut*BmHXN zxG0NV4LHI@e+y;dC%i^q<$B17f;U~G$ReyGR8)tzhS`JQKJme=3XjM{KK&91<6*1q zg9z~9pWfKC?}+u4PKFXMOuN;!)9j=LIFW(nHTUC3Ce3l&`=ij^U-3F;FT!H&+@|L9 z9okPuL`!J$7{qa7oxnOjGdA`FCMKqdZ3N6Wq`oD<+W%RT5U-!hh9v|u) zFjlRRWI5ltVqqSqsMiG6&#j1(dir#7+aOBLnxwXs~tkHty722o#+ z?H_(wHI#RGa+cOwZ7G>+?I^YHB3I5hj|JIv?dL!Rc&AX zNs#+<>aCEf(DC)!N54{OW9#Z^*Z}V1bgLCbSJCvleFFW>cJRw(bphvSy?h}xPU8am z&7FKb@5MBp(3TiT0R$9F2!`xVGyr=DL-@{` zl|YD4M`C@LhqB7DmBs@?F~1xZp;(OUA_TFx(vKOsEYO_vu1J&dnKqw72*1>BIcw!F z-E|Im%Gb@PeeeV5hx`t39u}p@zA%9Z>cqM)I;Nn#pYir+TbZ3^s1+ZV|k#rJu z7ir6ecKV~K51u-@;Y9E0xEqqq;8#Fv-78!?7wv^iT@Q@+=M3)fB13qJ)>$IbXHb8V zSP*5jl7OjljOCv0s+g8r2i4^tLa=GQa_k7A+TJ|i0vGQ_MO-`q(97I@<;Yv1yIiQ(2jg^{^5d-AnQ=}IuIU}?$}dV$jtt2mM*ja^R$!j<38Cja1^iJPyuBYOn8`ipfG zk~MT6*usw{48uWnxN`LFUMK)rzSzP9R?QK=u{KFNvcOnYZ*>f83I;ZPfUm8P95yC3 z0jb6a{QOK}D+%n*qe0|s${x!z3;iQH?vF~IKVPW%aEMfp+^fY4u)HUrGobl}eJV&v~EY`UyZUXcS??ImiC;&t#V1hnc=Gd#8W%vXwc)~G2=(y?g2pDG^cC% z$ov_$7819Y{A|FRVUse_*E=ogs-S|l;+}N7>$*ZF3M|5~Xk&}F2I`U_HF%;_pc^Ai1-}sp(qWlAK z_%0Sr(|%y|WU~o@V_$%q4pycW;Io6nVzS3H8@yAwfo`Ly%Vr954}1=+1$mtONSE*h zDJwA5Qfftq;H5rRHo^#XNQ5%!#dLpXBI;kIHNc)EBP+5GWYl8x58o>MmeDJI9~bq^ z8KyEa$_JX~q|S}H(rhvb&BVIZJYL4FdusPgqE4Q?6~(#WPe+RcIsJf3<~5HIY9yQ! zx%p$^hdM{_%0e!ND@nJDHfQK2JU&x73Em_NQ|=Vsz#tKA<=Tp-i^lt2TYI!GVxB|U z8`s3*#uYw$6Ei)Dygeg*QGb6`drxd$*a41OvvXe*kBNeuH*nHf$kz$*h+IS2H=k^B zS8>iuPMvI~fUOBvi^gny>VU0*2S2Y^87xUge zoYY2mu~hkILCjL}?mMmB;8xRK1*6tXZ^0~yivr_g4+A$URB8qVXIDAxPEM7i&98MN zuo);t>&^ke0+)8hiXP;&$7ljmK)n?+1{KFHFK%dhb5yQQ#G{ojfm2W?Q0@SjZlfm< zI1daIEnY6g45%935hG04C~pQwp%2wzU~(ED!~!DsSs85}=5DGMsJNAE@zZt554$Ry z0ZDzW}F3VRKauIoox3s#=c}g8k+-0v+ozAa*$?Ctw#&F9g@_?M68{Kpi-G8stOz z9eZTzNcnp3S$kBe?ajKHXHZ(ayh~?N3MZ(#yCE?5xrxYUezo$h)ePy`iw&z{pP*J}ithA`Eym)WTeAl!5(v5|Q# z#R74lDV`r*tVfMC`GrZnyX@mV!xt;SIU8v9NVTsw`ZJLuu(S%E+uDNTM}qV>69dzZ z2{6%t+Th;BSTKlGkKkRB0sZV(GqQ^Z0R*$$$gqQmr*`DBi-w?&OW?wv^r(%o6v#ih zvCQi5L&-`7otp4@@s`&xlK!S*#k%Nf)&Bl#Lq%IeBQ#FVhg`P^v+Wj>LV#MeHRC5taJ)pCT3M!ieP^}710cS0Qi@bsChiPp;pY@wIeFIssW@@ zbewoK;LnNMJ?m-RSFYw>adKknv95)?6drkG(OS?VzYq)LL`-D$Z%09Y`y?iP-%?Ox3Cg zwJk25GY5_Y=(}t7x>hw!S)IUpJnvkI7J%nk7RdmpVDvnSSNgr7SjfVzJyOuo zEZAJwwA>tZ_`F*bzQ^-3)GNO_k1>Ur|7E8SE$iJms(n)&FRMb+)JEs%5h^&ZVs!|j ztb_gJL${Whb41-21Ijq;bjH%JLbfh$i!|NOOV=a`%Wo6#Oyrb)#+A#|1oS@HshH&F z&`X3>!iNz^0znww0E91jxogJ_OhA?no&g+w z>`-?BZ4$(oUJf38gNGnny|iYA)K;TI>vhJtc`K%GJhN`$efa`&*+pnn5O&TCUx>&7 zR{rczzw2zJpQoQ~i7L3!s=6R}rN>lh#yL&K;;smp}M7 zVU#Xp$Ddnk0nO@E2oxXA&C{yP^ergp$S^aq@*%e=%WFmIV>&q}EOmBk2W1;Y8^`}Q$j_eUuT?`2q7#uy{L@A4J!fZ3nwT1uypB*YV&0#Q14>X8#{ z82myMmjy89*u9xTzlPPvL2P2stHmlBwrw93R2Rh&yxDBtrdfI&gBMalft)QCL~f9{ zqboKYQM!hl$&nKPfmK)|B!z5eZOhfULiHKj07G;-DvK(y(g2CXui>=_+5W=LyAV1J z>MqB^U&G(Ak98Wej-#-!3bIg_PqtOalPsa!vCG*E zh=)(2!Lgra)Ke8q1AaJ+d^oVfQthO8j*;(O&nB!!0ACC(BxX>C|RofMxOOUb^b>fWBB)prp z2{+uA^lb3t)7&s|35XU{juXVxZwq4pKCJgMct96YIdSDq;wj0Q*mrv{YH z719kBC2t1SkY!cM7u`=&Rxz@NmfG{N!>5|?BSD9;>*TqqLVHmT*yo3k3&YA3cNEE( zR+^;luFl&S+OCgB@~;3MqTpbrUzhMpvTJBZ^)?*PC%>ivVHYogP*tVnK@%w8Y;#{Z z=&6uQ)W3_{xbV0&Rx|MSiLY232v_0lb9$BUN2G~r43DeZLUS!;xEWIVa;Nrz(jFM% zct1B0zDXvkUe}PK2eHFSs+AM#w)H%8&v4kF})bRJfSOr%CPG*yKQ9`*-^z4 z1--xr%iLR5)gkwTVc%EL?*Bajn81>(aUCQz^k6eD(aC-{rQ~Lxw5}LWI2D6?Hu(so z$T3#!++}YO5wZahLh92Gpg66dOdQ>|-$PAE21Y;SI$~vE=CDEY7i91pvTfbv+9CyC z8I{mNZa^Yzj8$QlC;VAUBF%ki=$%F=x9&%TTdv`ZPMrz`Pb)TZuGWZru^SpM1!LZ=V*Fd ziP%?C2yPw7simZ2c1G_c!dYXP_7Ut_4E+kA%CDr-fxYZ_FM|mzVXWk~eo9VqU%jE& zk#c6P`QGMda~NvPC!{TYvDQ)1*el%L0G`&dhPz=?bG77V!Ipp+$!ro*g2DaW zR;1|o$NGJMsjY~N!(okVF=Nd4Ne`KBftAC8E#usX*Ia++Pw~kyO>-ry#}N*6dJi)7 zX>kb3wd|*(Z-%_&68|pK)v65&w3e4yN1?yxQY2cfhAvAEv9;dsdR`2|y-4aB+TB{3*O*S_;%ZOL>4NX6d@-5;T_A8W5H zxsgNcR>8bXj@MeO_3-fq2xTox>zy?XnO(n?s~O)(dAqRbi^U~b#LEVH<=BV3q_GkpD(ZzNsV=4Oc@wl!u0dO!vg`zO;wV=hKyv(smHgR89GaGkPAr z&j{pdG%@fj03~~fX%DN{3HE#^Hb{M-Q;|kRLVgJU`K#u}p5B=7?w4_+f7)W6{zDKY z($J#xiRa47-jGZ@X|K`%T_corL9S4NOZU^zjF@SFs8 z2vX#&CvyoG)d{V&gVzJQ5|NM2_-*={Rl(%woAgzq`@$^zl;s%g%D!ox2!qL33|h24 z76%Q4cI16wKp}+_I3(QAK=kC=QnZnIO)SDe|7rz*`h}#c(yN1#lWcX;P2!YggJ<>g zRPOP%N9t7r#f3<=1tzTM?24n)$GuvG_>}J}TQ6~0UWQj9CQX4-v7!R3yKKUQwoDCD zWDz!L^^K?5ym7u>5H2VMui)S z6~G*M+4U)EE>B;Go~pLww@v3dZ&DHhQp+kfV`8i%l4cin1}yUW6#X25BZUk$+T9wy zD4C<71_}x3(Pfu3tuAjg(M&Hx7LR(mHU5Tdy*{lC6G%kFDn;P}kDjfKY7|y9p=vk& zR6@PUa=a?^C}|0oC$-aThduH2f0P|$fuTWYJmq@ph(0Nyzj(E*)GzA^sS_<=>MJW`a^a~6xmxpoP}#K$$XurG zHAv1H)HdJoKdmy4NMpW6=h+S6*c0Xhzt8_!-3{~P#cIuY+PD@mQ>EE%j5>(Ch2YVO`+E8um2q;uNA>k4_7GE43 z#w`d9G;XPMZ%RHp1=9mIWTj--g?UzC;{jQblZoyaz9x0+^?S<EZyK7(C@T{RY{mF<%B6k&&1s%l~MW4pn*UUGv zmt$7P?GO~9r;_#_(;I%LfXi?LV_|KVYQ>Tmy|Z@d7=AkuUMDo~Bfd}(=C2$2magG# z@2GRpLZF{s4I4WyXArIHyj*3$; z2&XvZ%jW;b*j)ximMjPx#@!otcXw#q-QC^Y-CY~k#@*emp>cP2hsIqRzM7f4bMNf# z7qR~!il}pvN-F9kC*P;3j<~pFfpFmIiNwE+3it(56?>{$c8|(W*PomGT9l2J=^c^A ziD$yk^?-ebN~P#cXXCIMzr0}=wFe|pwC8JoK0TQ5H@1uPWdo-P$7V2^O&B(>l4dYu zH6>v!)VQNOPh|>((k}#>y0*1k+r?RZo|e_%S&Ir|E;`Q(+YZw4u$GR4pEbpsj$~!u zIr6%5G{DbcXin`KjX04P-7_cgqj}2+ooq<4M2%BB8L#@P5AF?Tgv=Z|mA(pJGQl}o zVy*JSiHphQc}87hS8fpPx^5NcW?T~&rrt*%Oeg8K9kbRJm5REfiV#^a+6IW+j-|VT z9+Kp=?jmIb7Z5z_7eVPHD9oqVtkr+vob%?(OaiBe1EWv#W*0hb+x;rK^Ubh9Q+`_F zVbEYcFzQ>(3-eDJhuWPV1)ZVFbG1JNrUJfOw~Vb3Qc#{;a!B|RF1K`bC&_bwJog+A zIkv>2`@o{VR&3uHMgIyCI%>byJ}Pj+8;#Ab8A$~Z%d~{%KZvfOBtuNTsy*}xv3RiA}+&I}`mHx!jz@x3X4hLTe0`EN) zzf$qJ`f!wR2+o9y^OlUEhTYFnxYm(P*W!{uW4E~qRz~h-yDo333bwL_jWDC69#7qhgR?@ z+HQ1FgGq7<(@C*(h85v1olyt4#J;~!)O0x!spGsjgBlI-#>5*(C$ERf!3GscsBM`~ zS;UQhpi3PFESF+t1eV&xObCYNWD)FA;Rw&zv<5{oF;*urn;L^{=~)^@ zHPK?SD`|ji0)x9mV|6)4vI;?+?gzRBr)Ly0z}*vRbQ z;d~jHa+6SBhf2hoBpPR$ARM^A{y=S;0D-3$Ag9z7ZUX{=1Da;{{a>6&&i~9&|J#ZD zzcG<4e`JUNTgU!E2ulfy0CrX>0X84~9X(=V`PcLPT|MI9;`+bQiT_zW;`~R}+0x0` z1z-siGRP4Ej`goiO01lJXNu*Gtdw1B0lTgKr{w#OHqV5Ttou(&FY+G|KDRB1Sl)*|?U)@oPeu_oKn<*)5(zHc<$zcc6?G~t7a zG?va&vY>DI&69~`iv#4cM6A#=g?Avk?eQ-gipfW9uarM8js01l2Pv^SYz%LIPN8G3 z$9>n^+JVU22}i_!{HPtSnHWn^Wf5kmZ1%TnUY~;A17B2WzAF@*Ioor;qOogC1Cbj# z7eC(RAzvp-$}MrY^XjKWC=W@;N5S+F1lfmdA2|O0+!adTvI{GS>?IV|GJ~U=hute% z^Hvx>-A5cbbp5+z_|6e7hV6y@VL`b!ewz{TYlisSgz!>{o?k1(x}|~tvICZ5lv8z5ChQ(n z{!1183uBf?f|oR1YnR;L)0IKQ(##C7-w{wJbp}+inF$%3U2OrKV!&QT7gG;Flgq)$-o(|| z<&SURb^mkM-yb{q{wp#q_U8m;Wd`)RZE{Ogea*E#x2+tz-a6XgT^ ziQxVQA^I|+NF=JyD3H{4%_WIlVlK^=fdExIZgDJ?X4G;`-=C$(Vm#4?rc-xgkDd`V z{-Hl@cVfU?>`OJz_I$#yu)KA=_?mIg6*Sro$6eF(4G!Wc7>tKVj}Xx{=NBTFe2R1f zN(p%=XE9HquzQySa2-YGHCPav9HwnplaMsbJ0Ux;%TVI}qxuJhz1SG=oa7ZunEUB<8Z{(##836m*e>{%@EY~B@5ipck|kRRc}SU|p0j`*w>KC~+WmcxVRY958-p{#U#LAo99l)+`8fxb zOJ$x|GEe4e7cA!oa#t9O2WagMP|XgC+%AaNiw*jKwg#zaW;{_Y#WgB|A;OpRBE}%p zIxdZ$qRi8Yl5}VbOa}^ma7L7dz*rmosCs>vJwNbp2kzI6GADM)x`N3z`ec#g`6yB> z^HXhc?ohw7-UZZNLtpN)WDhuNn2e*0(bAV*gZJ!9T%zDwrcM*p0@*Y`8lEhYBHZ!cpHq+W&2z3W${3Z&;<%7)x8x}3RjHG37!3emi25cP9vDr=)cWjt?>+Q2cWn8KtopjKYr|PaDUPNxq?t;yj4d~O zV$DZIymquJznLkv`I@-9$ma2yyM}ch*Q%$cZp*Rgc|iE!Sm~)rkRB%i*`YW>TYmBq zVD5n3)Yb7P(q`oI_w%&JRKx=u8>qdfw z9rS0mw)`1TJ3ImFrc)PN&b(7^aKo->gMv_lHa9x3@$Cu6PE3lNrkFxdODUoeN)Q~& z7&g{kxVf+6_)%u@!`n|gZDsxkTn~`BZ~lA==DrpL=FygL(UvfpJe^p>-=>j{2sM+b z=6E42^#)JuQ%j9Djv^|61?68Mba+DvkEnntauOx1r+3hl7){#L^+*#odV-%Yt$#qu z(;QTCNLUYlLsG(A$|SydkAZW8aE@a8=7_GKKu1zIokNfqW3ttw&|8+6RDKwWPlViO&~)Zh29{dH=& z1ZU4n$4;zcr0`Ao6{;efMf_`j^{lhKhMnc1Rh63;dqGJ$-tg9;-((;AH6HzQM=-1) zB7K)YZ@0ydplva*a>zu!Wi3#!g5QLB+JpgTTQDHIZc`Kw+wm)rl+45Nr1+n7Aa~}# zHhDd-*_&dLg+4dc7NNe$QNL;Wd$uBKxMH|*U3h;%kAl4xBgn2L^&@ZIOAmU#-~J4d zpN)%<>4=a)aFJU+SmVN4KhKUqL>xgqcbD5ZdLfDZ(B}tRfA{M{c6=%84L|?o1r)a! zGprN=(RpG@fTa|({@M*ncH+JNEmnkEoozesiJh(o`Jp&d4@Fqvx61%gCbksE*^t`k zjr-}lfnEvks1s=fo(b$)Q}Bg!DozLXOcQbO(y2m>%$oRD-}SD~Vc$$%X!?!zErsr) zG?K$(O&wW7#iB-&ZdE=X_a<-s)=Az@LRd=-lO}T(c~Ku@j9)_#Q zG1d+Buw%bF^Q=~3D(ueb+d>P%+U{U$2!5R3oItuvh68cuyDv|+9mtG^Rff(1vxNED z8kq6cxJCInKKn_NVwSB5A3J7IhZ?s4-(CI>pkcK4uUc~F6Nz9vr?y#C#cg|vXT0caZY=BTl*q!1S$6b2C z>Q+S99Xiyj+H2N*q<3B4LYzsZYSqlEj*ofBoq5A+`lLyJz$UVD<*_hgd-BzuC~PjW zN`n8o!0YCrmo;LT6Z>Xc-y-r;srAaSWgVUDs(vsh4q~P77jO~#=fRtxZVL$l^63_( zk*z2%vRM3N)3~V2>crSmCVE51xmR&^^__hfjm$u~uYc+)1CG5+l!pi#s)I7AiZVI| zW&B-~-h46XPZDPIbo%2~$NFs3n}(ow^jUbOlvlt9fl;>}n1yvca4lsM)NK9Y@&ZTMD(6(10 z(9EEd!<0G?d3dUbgZKZcUT7cWubg|>37;2c`X=IQstvv_%+I=G*AiGAUGgE5n6K)| zR&+uY6HzItTsu=|ck!h0lrIT(g4Yhd>jZ0rbfLr1&TcwO?`fnfjXvNE>m?ngdpg|J zzh8gcoWHaT+HL0^V6HRSVkQ3D zNK6VEbo?v-KpL;#w2qXbYYfL(C6~7ut*!W@o`ZRpO`e@@l5oG*;v)05w&h$}^QU61 zbrJSh6RFcf-|#mrq7>Foc=L}tDUY#MN41fJ2`mI$s4>Rh;sF4K>oMTzf4MsP|F@6- zUAbjpVfl{>>HiYQ5dILz1Z4nA074o}jDH(ss9=8>WXb@83@qjkgG>VPNgBE7sqIgL z%-&Vl-of+F&I=)>F%=;Ir{VZhy5S-ev<0MPj1BDwWer^{O#iqDGd8p#RJJ#^GpA zU|=u{fI{{>A5i;zR14hpg!<(1gbMmx{4|gDE9xouE%Yrk>Me+hGUmYuAZ<3K5T~xmLqCTg+bo)h3tKswF+?4o-dD!g z76&09^;seLNiQ!Y72-`d@!w)t2h0-$Y-1uE+!I789DEEM!vFG${G<2`xDW*_YnZYS zviz|m0$73hcf98>(dutl%EiLT)Re*5(gOfmi2^Vxc>r^z3IMGH0f?0{0Ju{6mqw-b zH)sW5sYC#rl^wvXGP4AjR+az&%Z0(=4|VC!D$k!^{T1u62dG)L058iKAY9o1Y%B`~ z34oF13DC2|{>!?O0>D|W06NRa=}$cA?_dxBQu^yT>?{EXH2t$44Zvt={%L0Y%iD4`wYB`~aWfkR6MGvQK!8Y^ z!O_*;#T3vM`tz{=_03<$1>|r3G}0^q7y8bY&H&d->#raG^WlHwhS=EwO91~<;$mSX zWcUlw68s}r1b89-L*W7#TK`tK*#3KcNK#(zUkaC!y)9sJ|EZ(>8{y*o1L5KXAY1_3 ziIa=--wh(bYTbV%eg2~%^sffdU$yUlm3_GW(Xak{%5eVixBZVP<7Mh(|BnF!PWNBK z{o};{6yKQH0W26`ZSDUv;Qv0rf4>9#b8KwvfT00?9e}zL>py|FKUPToT`T&ZLt|q4 zvt<5XWBW(U>VK~A{k_@($XorDJNmm?wBWs&xG_|37ILVOJEMG!qS2^brjLr7537x0 zd%3m{HrVWz_9*IH63%ij^VDW8hUz-y3EaEWKJN+mXM7`!0PrR}D_6sQh*0(=gu+P0 z*jO$@@B6RT?J^4cOTOQe-w`C;ctGMThA#xA656`+#fW;zC`|e4;W;dZz%DV#Z(_U$^M=%{1R?-4K%GyIVNNY zJs~>gxpSZl+ipqPuUb^?Iy@;f+`8pWk?J4jA2QXhbn58oO@nQnDvgr&Y@eZI*) zeffANs(xE89{l-9y6^sl5q+9>+?-Vp0qY|J3d=rD`L|Nodx=g6HRhVTZbl|YMvd2n zCd2-48Gt`th|-&TT+wPd#;e{pZCAKIs+^nko{|bCkh#9U)Rh#j{d^=$b2~2+lPYCV zSXA9qFU*BkAwD6ehCMYX5g%HRjJJzBOBnI&ro&(j+?y0gO5+ZhN!!^m=$34hTPj)E zs&TLM&dHi^=xh%T5PzX{(F# zZM@;4{NL=`qx`3z+ATA4TGSj13F;(r)WnUCr|SEGvl=9GFBuLI zR!J@T8q+pEsMVjIFtADYd+3`>vIrcEa4g6_drOwnjh=Fglx1&nWP%r)*vurj3>irFy{p;yQz7UILlG#^d0)W`9VL3J(Lip8+&2E#7>a6lp zmvh?r28g17$=q(PTc_O92E7PTYc#+6>EQH8IaQP|T{T*EK^Ue+uSukA;XogXIR6{& zRL8D4Jg>HPS)!vthiasu2S&Ecpm4KDV+<_#WRjoOEmW=D!M+(*^0ZKv{B3zh z$s7T)&X4jHT6`3YJOd@dd|?|03g}3>gE!Nzsk^-2mL!zWsn0-rVem$mKolxMz^#Zl z?a%{U^PBF->X}h$ShR#Kua%@(fiM(A=9EA~0Kun3!7C zZnFw@?ivXJ1EOmKZ}!or`^5v0kp(fr*IQ|!wD`n?+!A3^_^Gsn+&g5-d{QqlOX}9_ zQzlR#HlFZ?3apwmbBmKt=NQd@g+UP0~iuQomr~@Z^;U41OY! zcJfnI3K4;&cs#((|6ZP;(^Dd>qkNsx9fH!D7iR+9fLv^!0Ll7%t>veo zdy^~cGQr8R6oU|ZPJkT;*eI{~LB36JARn`LRlCHb-$p<$pHj+U4>?pC`-8Cc%oBeS znw?aQar++ctxkKp?zxZCh8S?D>etFZeC4mtaKV^#~7~ z?E>Fw$rL|YW1W1XDWH`Qx8HUS@1%jmT-c^rFX1!w!R~>+u5NG-`OYU7i8&S(>|O*S zdsdPewqu7STly?;v;Lo|_+3ejuCv={y%P{gtPGY}OHPSfa*?$aGG3Y`H>f^2(zx&m z*fA2bLaZkCcRwCO#ZhCAtvNH8uvmO#bMDw(ez7e3JzunFDXFaZzs>v1j?A_vuc!ql zx0Zled^3y1uuZ4%Hk~{W4mSB-la7w^{3g&B)d-e;q2j5IzoguJb)TbR7BQ^NPtZH-X8-e{WTMhF36Ik-IFNq(J8{a62m6yE|p=t z4{F*pl5Js-P@h>Nh*tmA`mrOl1gUte5UNaR0a=9WK-QjM#5@$pe()JZ_W9`HF_Xba z5Q))wX#kVS7t)R{KQT#RRH_Vh`4I{9!b&H zACyy^=xy_o6Vs6n5$T2#QDTW11q8$5krzc|ty#;3t{txV-%*>%IKBFn$ml)#fXjeRk! zPfh~ey`aG`*BD{klRecHK>MIKD8{&U=B_?MZ58eGvzZj3!-K@1atEGl@y_t8KUpvTiW(hV_M%uUx2DUxE~C*$Bfj9Sy~I|i8X{nr;(6o{jtPq%F`)d%i)G?Oc}?E$&Gh3r`49t z5tl399$rvpsCW2b9s}UCBtDLXpg9N~UKSmg5^4bZtgy_wRxa%rMaBYrVM;7-W5$=@ zr79))D+FcmvZbLcrQR}W7t(;d)NaMKtZ~LBl#r^FFmINpr9vaOT~dHdb$G?(5=#ls zX9W4`6|OzmGRDgxKA;x|pbAG%{L}NR&<~P3=*%`{$fuh8UE;j1tdNh`shEDOwnwsr zj=+qU%1dNZjgYUeVC=3_8H=35_%Hj*r$oB7+4yK6Ob4pSBft!S3M&lGk47rDNpr7% zp+C$>#Kq>OXaOG_U1zd+aDkTEi$r98c;dSC0j^ipurKYD|?+{njbM%*CYi6Z6Dh{V7)t!ROol0^Scv_?c!y z-Al<eHwaIsbZvvLt>yF7Vu_oZP`oB?uu61Q ztHm*IbR$uA2F?d3r5KGG9uj5nc%Ed+9K}tqDR4C_t{b6zQjw}#AL0G@R z6x3wQ8blKrNUaH4axj3`#kj>Omo(NOyJ<^xeKF!HSNT!D=u`u~!8hcTs4Uc*W$_ zXyc+tSDwXPsZ|&oC_h0R9NX}Rck1qSXCLgi`fjdyI*stGre6eco@9OrbWiRe-q79K z$>s|hkd$`S793`b$@7{foz^9Y(fp5cc)A&nC}L&epjd%-=QRYY`j4fUcton1>07cQ z$p!jxgyFcV;KO*%hEa_~T&xTiX>qd_QI%%%$+Hrt1=1-fXKJ^S%}o3zwVq6EihR}< z?z#>6-tQ&pmKnPSX^X>A#cZK6-oojhPzM+(eG(YEQ zOY&0Gws?>)#|dxC!xQ2m*n|exRjM?*I%zRlG_J%1xZpQLm1V6tc)rXe+hq(_=jZoP z?6>0+?Y`#%H}_e$riBP6U`ze*mf&oz41rdk$67c=-`%K6H@-9L)YRPraPhjN{5^%T zikpRfFjo_Auf6X^#HwT&o)nXgU%2?^Dlls=BL|K7RnG4Fo-rf~BQvI2}aLSkoh!LL|hMEvy) z%{j8AYFOX3m{KOOQQvxL&~*5zK{nc(Xk;f%WDC>T%#DbqJNgb}8FW;_T+vfAp4}&4 z{HG5i73CF8UDV<~2WL=L@gavrP537DJJPG7OwopkB0_7>j@RETOt7_Jj3ET$BqzEz zAKwEj@vsfk>vC#aaQOBa?d$6TspWs}wAJNPPSqtm+BCb!NBEFO+iiDMJ;V%l9c&-Di7+&rcREf2F2>#A%{?6b?JZCM+Z`Sh%gWk@b#K3ZO?4GACcmB_(MPg>DeXo}{cbnM{8feJ zk#5Qm*=OLRgrOfu56C}Ph=lxFTiuzM@&_>a3xoeO_);GFa_NGrBD|-Og>g53O{_Ra zI|@OW%OwuJXKAddrYbj=GY9q&Rnp}?J9f2>;bkV?s(1XWpFFja@l`=M_A2HrdP`-w z*`)cT$ZFJEN`<7PE+izv}k0? zIFB?PVvyjflwa?9$^3xIeN95cN-*hxAt)ZS=|4Bbn}4QJz?!^5vFA6K0~Ml@^hQ+gc;^|Bf7YUi9MWko%}kUWKm0G4YJTt zy6{}nE)i6qR=nV;S$5vTh)TG?DZR?=gWlSETMD7$=4=8#mPRsM=!7 zU_Ku~znJZO|2ATQSQ7GA(N|6u3jsW^;&9`3h=tQ06Ug?RT65IXJi+c|e&Ruv{wQ9_ zv^9|^^U9~iHXO!Qi$YObYt&FL?v=z6w}&34p=f+FOOjs(8EHr!83nFu?o z>=`9{GkM`rUIZ~3cpDi*gt2E=GFC@Kz{MPz>&4EpC&5(pvydwI7(|AwuTUSIz^$sm zo!uQr1Y0&F%-Op?ID*QS>LnH*6Sv0>H(ONdcYhX#eJPZXxj$mg@xjwxVAH5o0)JE0 z@m$Xo2cMX22?>yJ^QM<&6o~g@tHM=lb`r-{6Iu+OA!cswVEAFLk9m&ud@D0$OU%_e z;9rp{q0g2<^1QsU@qqnZE$K(9Xi>f`C~O*=5k3M1E{hW@rRJ>I$2yU>cbR)De4!p% zE{zRd+tAaxVSZ4CmN$AMcI)T++Yhr^3qqS93d$-3x3=cd7r$6r_(5S!w5Pdoq*kr? z)(*QWzNM+}Oj~=;GNTYH9@C#a7#0dZ^5L~oEt`(>UOhLciwmBhFT1h1E-uZ)`{-g9 zt&2?f$UKFX-k%?aoqk0;Z2rrZ>NqfAbr<<(ExZLQ{sK(hZ6LQ=1Na-4QZUqgwYAZW z4==$!x}VhUswnIXGis`AmW3n~!7slo91sRE$h2Bp>%Mlh1@dtnQ72~1%S;Wcvqzvm zTwWQpgA5$EFj|%S9=B)yLq`Dx`jC`aK*A-Of3T8G&Nvev5lf z*eF{qcSQ)=p2u)+%sxeQtlFOHD}w7(rawdJe%sV+Wm4+O@GNI*=162o7Q1EiDN$YRO}n3MW0=vPsEt)}k6D}* zI50OF=h;(n?;6G(gub}r=i^B@*RO1TFyM~mwZw)47><#pMJ_0E(~(xfYg#Y z3aRwSXsU%IR3A?5Y`&qpkPGn8EY5M(LZldC`1T|;qO2TON@e@qzHG#Db3$1l)jcL} zIRT++oU9cG&$#ZQW=)&9IR7}U=-BcH!ADaGi_zlj{UBA~s*}Hj_C!U?Zo-Sjr(K0y zMbHgIW7O*)!l|UlIDrezSvq`dmKXQeqwj;sn^D&OhBT{sVW`g5%JiubR8g0dbt!dO z`?HzaS95#sqx3%OZg3S{pOV83{PR7){}OrmHLfx9TOtgb4lhG)I{OSfisa!aduV&g zW9BSqsF*Tuf>PJ_1SJ&>P`5S`eQO)2PcZ#;Hw2Y^G`u4E=6c>&9WZRS^Il#;-697Q zUk(}m>S>*mBLnLDFEY?&2%!umh=xxouOCsaNE7;m=dM4zJOk~4Ec@oOCJ^8hAe#I# z&x-DWzzDtMo;oU(85=`@B!+U*P^SskD+RKy3NUKWhfI8YTfn6yInXwV`3KXFA1$$u zW2Oi-U)XUIH~fN;PA3TiLkP@%-g3N^zvQrU4Awgpa1`KN0}D`1AyhgP~uLyyVswD=Bjora+l2Zc> z!rR4b%iGIfaWIU@l87$b>2_HbtC@>zfgyzbZsAst`X>BU23$p4U+3;g)p9Ryhq?ha zg&}5mN2L(Cc=v5B-($y|Ecr~-(i~M7$Ieo4yI_$5lOE@p2}revH*U7sa4JWAf*4u4 z^h>S)U5gk70|UV&=eiH|_SMU)sfh|i86~LZvmmhXR*FDVN8p|x;NFEhtrk%&t1P2o z4W1Ic#ZLsj?Vtm|S>e6vL zgs*P{mkYRX?P3lMvQWY}HKnD1lrPEFT?xs!lfyh_C^BAG7%YV<<|~^@mpnVfM5Jj$ z)r|R5j!jqiEU}BdYAh^s#C$@;+q--*bB?S4z9dZtp7mw9$py6Fs(Phr^U;kaN$>?)RYW&i zS;Mhsi)f*bsw=>CHOe#5znypy^-7A~%94~}G<&q{Lc6UU1j4zIJ|+4B;vEH(5sMCe z`phQ+2aGZQ=#e4IFZqqKAfI4`tm)eAin0huFbH%d1{==^_8d4fKerIDn}=S{E@OZ5 z=NKNvK#SPL1ueLY*2oI^kO6mMJ=20$tFin}0GWR{TfGv}!XW56wK{Z>9!XV^M!MY_ zeB)5>j=z6+n&D~vM%%XarHL@ovSZ3u^)#6aeU2{RK4o%m*fwjo8s2^s=K=;*q##p`D#j&>bf!Z7atW+;%|X#njxz7DdR~Ri@ms+ckwR zDJ893+S~oPp@e9#JifD&^}U|KHQ5@}p|5?S0=}pa8Pr{8W3#%oQkjJ>uqx0!^pDU^ zBQ_Pg%7o41J2$4=+hfvd^=Dc1!=-r#5HzkhhzYp)WAah0xNVeP{fUD>j#wd4JWX$G zIEbP&Qq~33ngW*!AQ6qot`M+e(&bYUn%Iu`-8uG&GYUq%(tZX47CX8Q`H~|QagTfJ z4XNVagsxvTq-oZ$ptg$eN)D;Gwe$rMvP1j6qeM-Da&~k2myNSkom)^S6m>^&U>aZ5 zIdxEP>2B&H#Bf9?jp5gC`IZQ!%s4fyxW}0D658}Zss;72<2v`5#JG6wRsJ#)?!4QV zyX>{^Y*!$|w6Ofvx>GcA6qfGvVnP`N7kRYnvHmvsV&^?@l@KSl5dyv!#$RMvAv<3{ zulaqbABCo23DKVZ@QEMv(M$pyL#=9ls$WCuGp6fBCrA5pM0u^L*SxUiNekzA9ydX> zr5`7Dz&pBupVh9hyM0fdrbTplM7^`t9h5OaCkW*dD~);$@3$Eetw)i!r$im1+MD0C zM(t85mm!bTTb#|bXOq3PLc%SZ=CNOEF>R)!tD1@&RI8;9Ceel!%x+6l;XSnzljhan zW^%|>;PF|vd|YqJ@kk{$A#%5SF&pnYw*3p+0og+yc46({5@od7~B$ zn?)gUsG_R4QB(d=OhQbEWcV^^R7?O3iU%eaJl`OF9C{Z-;+!C@>${|#JyGhn9Q)ju zxb!*5$6WGu!+~%?j&k9+&*aR`Z%e-4y${r*vs5AO@z^kWt zv1A)F>ED>qgFG#BTWm8V@v+g>z@}gS9Kr(gPw`#o=H+x&$MRF>a89^6{=(crO?&5? zp<^OWRdD-;{&KC2%H8F=T+O*pCy;g9deEW}?ASPsUa{X&Ck@BZf9F&vgMcgH!x);* zX3Qh*j(SEV>7A*^j!|Ey8lH?bxg$40m=s&N&tftsu+u9QkW~*Q`EAekEB;*jHJvSe zmWZ%th@rsbn9cWJ@1Ho7eBPh&(TqbawQTv#^;l$wv-??7D!XldO7uvj^9nCUKlSoG zTXc4f<<@Owvn;upqrW&^!52>jYtBcl^D=xzslT3e>_e=`jwp;5rET^DX$jx03C+^1 z--=9)dZW`ZjzDXc>V@T@ZJK^KRoUUdrXY z+|)gvQf1yc%>tK$)njIHos9Sfm&0duEpr0yf3Qh=?4J!|DVZH3=;SroTZ44Hi_R>~ z9u{&6oi|k>fMj9A$r-oN91Y4lvZ(8gb(B=tazEq&xkFf(z)<^a7JcX2lIT5!U*J-# z0`b*yMQ#aBv5pZS`GNC_#ls(XQ3DdtugR&B z+cwtAlVfl(@>6`-O~>%!qY~aX6L_G$Lzz)ZOs~z=gb`)Si4Nw3Dg}FKC}XjeDFwc;c&ILm zhG!MAotkmsR-6tSRJXEtcj<;KdHFo8j32aRHY%W|ZdcV{(T0r!nbmFSrby85Bm^kd zUanuoqC(Bg#f{Ny`!NMz21p}z$UzRuZ-epc9=?6-=wY#@4|+UR#9Tgi6`5l-3a%kV z5uDZvJiJ^TS3Vm;#nRFao{`6F?!pbMUu%{)Q06Por=}4TN=f5c*t%u1J?Hnq1}!NI z>rD#eDc4N;_%|GE%xz(9>xaIFs1}~$vxM;H{-nX9fKy#YWrfdvHq#8X$t8Ejy2m(^ z!SkcmV3h!y{X*Z0hCprmv;vb=Wg?vQVh*Njw|K(gx_QrG+NWjM5X6;d&&a{Sn zDMM%09;ed;;p2umX>#HVhcLMXOV7mhxB}DTEpw8U6}h;r;w>mz2V@Gh$VTnqEp<@) zG4$;wLf^JKr92D8xWk408#iCQamawE1)H!Vhts$c%px0$gG*N%O(h&%9FYAfV^XQN zbZ!YAI?Gx!Dq+SSKktbefPV19w0H=iYTR|E`s^1K< z_AWjwB3AYYl*CQOJ^LjbeT#LH0aYGcQx_Ux>7nk~-5-1S{^L|cZ2AC(p+C^NzNbBQ z4QMXIl=rrScU$*H!VW|GI%pWcqpxD?q9+$L(cZQD@Wi8pRr^>a;?3%NoCdINR6+2& z*mSBKKc>{tym900CVG<(F;3dymE+?EZq9=V7hQXDRZGG?Vst-jMEM&Lzdg6BF)xpBS?1>-UY1OgaYm*SaKxCao?N2|enA zf{h5OB`(JR@h;sizuu7-Fiae+&)44{AihOoIu3MO=zunT(><;4 zS9wQr7GxW&7+hL{GDXNCIb8aDike_SS3?mp==C9Et))}0gg8Ljh9aDr;Bw@glO{b0 zXgvu=Jv0nHuV)$(xa1|>uOd>|@g(w1sNmKW8Z53dpF*W7_ayYM$T!DC-mofj{RrKu zyW5B%_WDmTgML3MI6YhNq$~o`-Yw1715?aS+rLe@om~+hv+Z@6M&hpw!8bZ-A#NW! z)+wc1syb$X>ywa_q7z&x$F*A9cN*={2}Rtot}Sxew5#ro)!AklO2fXesUH^~#GhDN zCYdfDZK|ed6&c{RWjHTH+SN(P%(x1Zmkb-vJ?D{(VoS5>p-Hx(s@1cd^VA0_+ESWX913URM|4+ zUPFB<{}UO`xS)SzVZ_bCM`E#pHg23XRGMpOX%jcB|8;2n*1-D z`8_|sAk1tgmvD~-ymeNY;b+?YM$An+h$}7b^PFX^s9Adr%9#UoiRBRlEY70ID}GXS zV9p4vZT9de(~oL2lw|O2D_sXNf(X7Em(?>ENEbn5#E;)BFNu{^7ACRdh$x4ct%TEl{bhS^Uagq|Jgb}2$P z%D%G7sO2Z5=dgk(cO1!4=<1h1q*5To?Y|eb*hciW3vlfRQ0hsWGxV_7>~zo)lb`G5 z5mZGB@Dnxf#~!yOya6FER7?`kFN4BD$f3TQDC$kQGN^6<8sO9((j#M$VB@RA(B`T{?;b?NQe z?A-bBVck0BZDCl<9S6?j(m~6#UU>3uUIzvRZAyNIB4dKJB^yt~{?2K_C+%iU=~GXX z>P(NfzTbv|I|^e=r`fxbCh}Cc%*fd&y15EBo~6bO_6&ZLtmFA@;%?9t*5u6y9Xu4i%NVI zwO7AYek%=b80W{EO(@%3a2ys4F_PSLq*3%>Ld~d!F5t}yXJ-x;HH9}=m9IDn*1T(C z6@5RTWedj@ue^5Ng{M3ZnvlI7=EDZ&P*@zfoN%zv)z!+23o8wTHXx%<^WknH1iXJC z%^Zn5+SRZ_1JvbU*K}{Agq&f;Yp|qs;S1+g)m+- zlyYA}Z!(kazbySc22m_q=m)Px053(E#@8e60hA#|Q2!tg-xJ z#&F^ipftLdU6)h8SV1AwYg~(K$n+49z-dG1u=TV6KayY!jI%-U6}(T3ZX3X8ZCsBQ%TYI^Q77FM*gH#GvS!f_}08(8q!hwnG?d zN>B_F0H!Iz5v7UUj+>^m{yH%UImV@WN}S4<`Ga#z~}A@Aj%VVl$8 zhMZ*mABCtPE&(X_;`0b|tX^2JP57c*AgfYJZ7-REABbxU7pj(Aj?NG!%a^2UgH8WX>~)Mf;xs22g7hHH?uGjB9}Y`dFF_>pm*g+_|ntH zxOKnrY)}-28?u%O>j;Obw{{PKAKpBMtzVpnMKfHxK0BG6>ay>uS7-!?a$dez&k&$( zqc^nHUcY$ppN|zcx_#7f=>u!bHsQH2+MS}p0WX4|x`j)C>bj4@F;MdD+pb82cYYDm zy_N~>G^AFOZ77d29vs=5*`he2XyiN?y@RqIAhy36`xd-oKSarTWwU^wiJ8)71yO|M zbr+4bn{Ik9HcLG;<9P*rH*{sr^DCHi5WYaTrm{=DXI_xd-@qo2bs6>P$|R$uzuQ{F zZGLnMH#iLG4o)=pr!cD)x7RJ5tgg7((ygiG_nCh5vFjEfNqd+zlxwhE=tIacLvXk% z$G1)t0}>T3X=KT-F8jx7jY)d+cCC(gS6?LmAA4^dRn@odjf0?oARSv8H{F|Vq&D4+ zba#hHNhq~JHwY-Du;~<}Te^{!R;0T(A@W=3Irn_ex#zw2y<_~w`^P)pu?MjBVy?O7 znorJWuIKq+I-`s`j=lT2SfMx@jQYW*zaBz;)5o`k|J5o>_tBchB>k2KTA&_ncg;NB zra8M_+VMQ$n0U+L^m7k`M@+%BOrzE>+St@Ad7`zzJU_`xdP=$F97xK)ML2eAJ1O-n zVrNC+u`E^0EP5TXd`#kPck7P%;ZtO#Mz02b+w1&^mgmP}!HA`IunBVI7S`F%j$Pyax^1yZMW_yXS{O+LIR- zf~-vVYK}T9!&H+67rgd$n2m1tyxm(eT$f8PzR~HX3Px6aQk{!5m<#=Q?UlzgV)z~m z`hK?`%j9+cUG3PGz=(-~skXodACcg5!Z#kcl7j`EMvm1%pbpxM*rlZ;fp)U>d7jaL zZ+F%PNQ!yuc`s;%^o)Ml5_2`PQeT=d7I2F0-F&CKpJl~vtL!RmLB{;B$Y6nvU(+XW zFVpZSFkqS@Y)WWI)0LM=y7nb}D^)<&&io3abB#7Cl8H9IO7BE3>9w`E zx73!kvMrdzp83#gu>_0_T4|l-Y)TtjXbC7&bm*^XJt9iDcaMq!#P_L?l162ykjt*F zo9{4xAZSpjjQfichW3lYZUEVrh12bu9UJg`r!v+XvUwtmIbzFXG}5An-SUB1scEW0np!GEMJU!=_VciPqwb5X z)%+VXAXDnY>-5wAoR=f~KLC0LQXKwsxfDf>+j1$Yia>6~KMGLjJGe@@dO7^{gtfQ* zZ7~Xd!G9^70^~*fHE{w+e6aBHzD)-LaxDHSW8$wk&kDa2ZfN=Vfy5SR2X8M88&4TG z7k4*T8&_{ypgP5WAXY27SlHS8BbkDiR}i?H43LN8sqJoIWkU<$xqW^ho8(XGh5=B} z!p6$`-xgh=1@NnZhoBYW1>%+x5B?(`V<#;(gssPCDdn9w`d*i3G=^|zcvH%m?hXo{ zMw7zm2ciL`DbHn{1SetLYlW5Gid3hW7^g z!^sYf@XMF>kLkD#*fAPKk2Ad11|Qntg?;EcQGSY!|K>ReT`Cq61ib$kMr)2P_2d;? z`KsZy(s=V#xZJimGZSK|8BNRx6Qp<{dk<{(EkTLqjI1;~3%63Z3!ajHCzAsmhNEyF zz=^x)Ru^lur-JFQ!&ekEbjrTXNVoNNN3kt&BrVp)PJKp=e@uW5d&w;#9&qqlQO4Vq zq%=I7GV~E9=z)c|cS#idTW1%t3j;&=jvPKWc{_8G9_jkYPG@ZF{wqAAn*;m(Xfwvk zHjI$F^>km7H%1q%aQ@P<{%YK+ z(`wq)irbQ!EOX&O-MjiYYf}X$LV2;HdE;5L09X^b=C0mjwGo{jiR)fx&;_43^sGKD zoWRNlQeEm48T zqo(qW$9y`y(QneXWFuEgi;qCCOD!ouEO0kxC-X#vouD$si>oNNb>^h_%TVTDsT)!> zK}y~aGSKRM>F!y;I5`>Lf*-1j!e~d2b6(9LZPe4X)B_yP&riR$Ge4~>vBZI<2mfF< zI-O^S8RW5+)Z;yc)@W8sO19?Xp4m@~o*Q2aXR;6c@&gUOHqL0O=7ANh-N#s@xE!PS z(eg@&C&PFoF=xqfDP^};>6@|75(GNS{Hj9%#<>iC(J}JUf?llgIrHOXWPw1#9!6dc z?mfOO`#@I*9O&U$GRbgU*$~ND>%cp>&Je|Q(D_oC;Zk(XTKdyp-%#_g8``f!>k0A? zK@Z=@yXmye=}C!ov5tl3IU_8iJ;xq!;vXK`or?M(RsFWMy|)oB7cm7Ld~*(ks?K1G zjglXuC+5A2NEe&p;OgtO1_=9?IS=O6EIN5qoj223;`roiwdqaXTo)iC76(%;>FuCL z*Jm^Ec#gVD+mi=%bkHeOwC?ZRP*0QgIn)K%jkGHX6H#@_d=NAkRQ;?O>b=)oksY0u0pn zIk}~9PQjfoI$LOYH!{>>ve|myfEN}4VZE!wP!uOQ#4j-|jGfXRACIV452S7KN9`Ep zMRLG&Hc4TF_h_to1xUbCn*=5n%1b&f^g_#t!w}bucRW|$*GNfDa|4hUQ9gZ>1y&d; zuAdbaqI~B%DHXka1wHjj(dZu9O1^r$azeEBC7MZ1^k!&j40ISQYO{qK`8KXg!EMt^ zw9Wp$|2Vr*aZ3({X+2KiHKpcPQ9bO$?Jxp15xDE&_WP5c8@?x6TP;3j-$ONcKo&bX zZy1?=T_yIMd<)`%`3c!u0UeFj0MlAc^XC-BZEua2)71z9N5aNmK(T6KQ?Orlwi z2BS5vLV=35=Hh`C#Gw=oSEs%tHp|Fi*G)9&lQG%iZeTD@yY|b1z(rUVolQjtJNNCu zm>`blqquHBn>J=)Sf{KyWAJ{a9R}>VW&&t;BU!?{>ItZxcyCKJ7jgbU*BAuuX5~2y z=&3nDMVPcRFD4EIR$u55*pMW)DY$G7>`mQXp$s~-_e)e$*MDDXDGjPnQGV*%_~!fp z%oh`!sKI&6?7b+<+B~vjsRi1dzilrAdM8HzefGL$k(0SzBrX=qveQ5Y7)J~+4&a(k zs>qX_l|+lp&mD-mx6Dw{YTyxf@uB&4wiL_ZArEJBiMU9}O0{YFF3%bQus3JcqcTGr z-7wH$Q#6QXc`S>ZTJQizf1x7iof28|nd`g$#*evfaS(Eq7nyIcnpNU ze88tx@3Uoii4t%#bXBOqQywqurfmly?;b4rKJ!nUEl`3#t3AQ->W@k`KnL?~CMmyc z=uP}wFdY72YkqdJBTYc^EhRJ?jAv=RkZ0KReW0DWDP?>$5+q&75p=_vrcLSZ&${yn zYQ0%=Y&>%i80-YUJP?BE@TS$M-9t$0Eew}pN4%u4c$l|l*U14B5pa%Mo;!&%G-8Ds zw-=>EPh=PC8sCEs{xH03%UATIXJ&P?S=+LPy;Qb>5OVO9i+dt_ELPHN;yQA;8we0T zUW^Zpty;r!Oj6I1q4VtpR#x0G`p^^@JiC{jsF0>QM)@vol?U+9->&UfZ00 zSReUF(r7w>c}=MM;dsvp$J-l$T34FbUw(Yh8mC%5*b4tnPn7(xErV(P`gD)-%dol| zm)u@QM7Yz#cVd{AX3_1Ho%RQFQZU(+tGD(=uOt{QrM4W@t46|+RO8J{Gi1l>K?|X> zcw+qdKT|ITzm{JCH3mS@A>xOD3xm9tI=iuPIsthiUR5&u{u7TUzYygO(7{%|e7JfS zbbWK#73qlbit}=sW5CMwI?IQ>rQTzT+4$k<*MxN_FLNLH&5&JmykzYuD2bWPh#T~j z;r;+2!h9oL0v1T(cy#LAF0K=FropMr&7a=$$v!A1!>xq7l5wU)GQviQ*V4N*l;xfJ z<#kLu!ca^8)WkRyu}BMTucX|1b$GW(_(OxlRc1EZE7yf?W-&KD=wbf`Vd*FYC?p|RU^4y@sU4Cs{{9C(NnD!( zr!PI(K}%i@r*#34z^VzFnGUgk5rwhO(sKH%9xmag5=Ce9(B?@rJcje<9!6BK8yi`C zLIwqdln!3>0;BE_`6f$bpQ65F1g$gSqe!_Ee9$dnZw&3IgQbpE~^<-7^z;xl;5) z(R@Wi&t>SO3@A4X?GGKNDcyd>P2QB>@}!ok`Jl-IG}GGP1L9Zl&N9d*tAQK_IDj5< zElJ`)8vM+BK*a-H?)Z_f*d4wHr`z|sQ0q?&d;$E5tCssZG9(? z*)qbid;>qOE3qG#OOFj*)ju6=`Gt@89AFt#OHC82UVj%H%+&Xo?Q?H3csyK0^u?%; zilzf2tg4*mMYVO0Law?Pr0)IAn%j#h=AZ0WW1IUuG`qiiyi1N?FVQ|97sHA07gDjU z8PK{y?XNDaoQSCtYY(Ci`iDw;dX|s9hnkkuHrtzjZLZY(anj;U+U;34RfgUD#Z)wt zP}#(@{AU7LT0|~ZAmZl9<-M^&Xe&0B=R9 zNTpNHN$R^?qX*RD>Go8`K@*ay7-vEXcBu!gr2=PAdoSFB^4c%9xT%wqmN;hcFALPt zfV*)WWruJ8%h6^};IEsViU+%OEERkphr`pMqkLM*m+Qx;<)<^(4LYzrcjUH_MW;a) zbY@F{{ZZ*5qo@5g#PS`frUUj^`0aIcP{)Vq^SIn$&qt-Vk_+eWLh*slGL*B6hfHN} z?=cC2_|lGt3-}XYflv{XR-Iy*xt^#XsWyAMNt9# z+-L!f+@6TQD;*I>j^es|AV`gWz#vn#W|I_TJpGG=W5?5~yOc&UFtric;{viZY**yYn6Rq^aGv2!o=G0QCA z3WYuz6&oeiC8^KuZ$>(WWdiE)rKoL_`QXSip`f^TF;}Idp1XW zO^-s?0rzyG_0rmc9{-30YHSFe5gCg!m;VT5>rsGi4IA1gql+?S>Q)U0&~MLe@0IG6 z|9p}yxwBi^S|ZSpiGdiCg`F9`AIBJijV>ZqxCw{A!l_a4Lhn_VN=ggjZ- zcDI09tF~Yi+)?~_RTX0@IhZqFj$T~2E8csfWEohG7|7&dxCB!a`j4W=%Z^8QP_K49 zupss)`oans+g%>}#TWsSc4?{7}l(wNOqK6q7Zt9T#r>M7lmFZUs11Uk`hQ${Ux!!G`= zD1RRD4cqX!-nd5#Bq3~=Mo`r=xF^}n3T9|1(XV;fa@;rjt<66V@UcxDNd(W4G&79&~(@#G~RmA!%TE)i^ z0=fcb!~{E`_M-X2*sG7mjPfv00mg-ZAbQN&$>rZyPyqIS&>m$o<@HJ!;M=qa_@dRG zFC~%l!4w*cIXX-L8{FMsBNCkc+qFOe+_|Bwcx732J7T%jY6F#9S^5Fk=x=PY<-r~< z%$7D`Q^q#teC+$hW{7EtPn^V3bJJxn-y;j^HC zii+^j8JBH{_+z(u&N^WwmbAZ zlp%zUwT`q{N_W6ZJIaz|a;R#oK?U*MT^!vCy$}Gl$K=CZ?#sGL;ryiruAQ+KW)Fwt zlI3J8Vz|zI-Y0Y$dawutU3sse_7%+Kc9tje5t{FY^VIGdBS)QV zI3h>Jj>Hca@iu3AV+e;iEN|HIwZuYMBnX;ta>}NG4!h(|*t{Q^Z`W~ncX#~7fUM)_ zB0vHR{O3+5ixP&SsfdGk0UzS8{5f*%!Ti z*xy!m$)TREvRBx8G3$;xVo@1Q%0m8)VesY=5)cEux{&|$W&Kn{qJ9t2j4y27<6@!B zUb7l|@@rpXXku*_h>B`yoWf)T-_jr^B5~bF&D8drlXKHoTv%qk+kLk?h}=Fe^5jH6 z0#SbqLn^MyP+<&>h`-s@ zFaxsU_zviM{cz#%`(_f!o3>R!gNg-Nm}y0803iTlKG+>Ax!(MNbj+LT;H7rwrx{9E zLJ{$ZPjC#YX8Y5t=Yru{RUF&}zEmoy0_b4B?>fM05nnEdf-8qrt(M&Q1z%Dasigf6 z!%}|e_Z9n!c^z}U_ubA-Usy#RWVYfuuwsY@Lm)VzB~g>+#H$ljcMv2SHF0E6s{FPE zTIW1x_daYHVY#f=Bo*X93@J`>UE(omhtYsnVDLj*DSVUliy?Q=9YX@PcILrC(Q8c4 zEdl9aRJSgvX5+R-wV>x<4ex0XM9_OJ6CIkC6Vs9A0a|9Y_spjPre}{5F`>OiRalr- z&@jF##hFt`T6JFgBKMA*UzBFZJv39A^|9k2=GLI|)-zO8 z1Ad&X)iG`M+SY)!8)M}@>M+(VbJk*QUeX7iNmdJtk)RhgI$EJvAu6u@0`bGE5fdh$ za;pjBwg%dIdDw%zz-p}?dA&ShiV-D`xMy_|DpP1#EMUZ(aDqy(PrhAFHWqR(e{B7- zE!vKxbc_1;a`(&Sw#-tH+Ut|^ivgwscGq!)A%vVyaa792LnuSN6LZQQp|h_4Q8D@n z*ec=P0-wy_=wTnzs}Y&9_+{WGPZRo^vBhF!Au-cY(hn^ozF&Z(aR60!2Tb%)bTf^j zP@Ryn`L`Jb8hh)eGQ8%?aipx@8;XF7@^sAU(=b4uy;C5t1}5z6^@QgEv-Awx<8iB% z$Qz!O{QCBR@x3%-?@65*IrBHLJept03R*5=ifk<{jXm~td{_wg3!fEIEY6m%c!t;` z2R*y_C{VB3xvGw(%8$;qjGOs${ z)4%o#VB5#=(^-0U7B0KxWwdIMKo}BO+_az+*(bYzrZ{Y)zv$U#<5{G2yu!{nr#bZG zJqb9Vb@bs`r&s?o11($aSUSTnx@>8pMxykI z&MLM)S4Po%l2KpnO6XD2Q+E@rm=mVX>7j#a!sPlosc20@3Cn%+doaeG;JZ+4-Tc&v zM&=?;7EyE29vl6kdny8Xl&Asyc_#h$h)->JE7{FVPG=D%LixGmHHE`uiX~spDPqLGJTUZAC4L^BJYA^LekdM;FZy2`fvl907o)HiCZo^GV6lbK>XLD+w zHZzH)Y^dr?OtUl@_++U1FzQcxAsibE`kd@Bj{IW9qfPR%k`aM&PU5_CKiG_-YDx0R z$i110gvU=#-J*@QQgr>Zi$?U3T|>9!_3Q@VV6mwH zM_B=I3H70627mZN%Xxko4xi4J zZ-3b?MW++|`rDfBFVb4)=+yjvPthti-RR3_baR@Gs4EO<{Xn&%EhYY+*MN;Vb8Npp2%fH(9b1xy4R?h^vpfhJAX@Lz+x( zdAI82lki0cp;43%7~wV2deeEO@myuRO{``T3Rbz4lNQxUMRg|pzepcZ6go|*Wl$px zuVR1Yo`*Cn-%%c|u&yoY)nq2hnAz;VndF7}s)?!zlgYsd}UJt|fXwrWFgebqTyuMYf;BTdC4#%MtWN08yrC zPhv^7xAwftt5OXpfRcFXLl_u14Ux;;gv4lvLi2c3mRz?b1-A;c7RV2;x-SKQx-wmQ zbe<#a6)i<2n(De_cUS|lR#j4?YjIJnZCjg8<9^D~Gx!BLNgO8)nLQG=SJ4Sk%b=0W z?YQfTPWLhwFNE#$Sb#{`9!7j(+bY9CgW{s=oX2^}B13~GcoqCB9wW(;CHO$FLxXA z8YD~sd@HAuJ&LA>==x{7@OfI)F!3pOK-csv20egc36mLAPUI74VH={cg|ZywQS`o<6vUgWkiR zcXC|_BXGsw3#>cr`&4NMu&~h zjQwqTkVi*4fwJ%N(SX6$)u_L(Vg3AtrXw z)W}IS;UBtFDgJiXUSL52D|FZgMZ)nn6vGQNLCMKdXD{baRb>54ApWyvSd`JqLl@wL{by4%E-z_WM{cTPRT{oIN zetg|Ul7s?5kxSeI0?v$CX*w&vd^^@KKkqvP{vHb-0a*Xit&B(1b96HQ^P}5{5j`yM zVw0OSvgg_(%{r8zY8)ij_gMYerCW>Ap>Y;fH?wI-S1=eel?<%bh8afMVZT!&4ovO1 zm3O8>?$fd_xK_kA5MZSYH0ZX7qqWzBd6VKlq5b**_Q9lkV@mdRXBxN=h$XpTLd(DQ zKm@}+2y-h{YKiIFr$9VAe86gaAv+Wxsm8d`lvkUXvzhXdzvh)I+AwEbU@A{TV^hY} z|8zgA53*<_KpzUf6{{|%EnvYeb!>H#hXJFthXAppH`>T=S!@7kn;1Y~K3?dcKHW=X ze%E#uw-PV_+a3i^@BNm*>!ZWwE8zRuXJ+DrP+EI6*z$=^PM-ANHqp~!;WO;;q6d*= zb&PCDdE95_zbEtM3U01n{@8u6+4DrD;vC&Sv&nw*xoxc29Z3(Yov#K#jDOB9e-u@S zy!yfGAH~MSnY6gib_a+fbiMC>3Ho`g9X-1Fu2M_BXWf_JHcUYey%t#8P*uO(M*j-O z{7q+sCg{Q2x38A$?jv-Jg6IbHMnw+cC2r-77(jGE{*%>7e{bTsL>eL301Wt}RXRnr z9$UlJqX}vzI=7s?wSN-$)V32**U|HbMcxGk@HtynA@lz1c-*$BlsFoNH(-LBRXQ<# zGd?NX(*5yO(?DS3yLw|QvGTFr?eLhjC<`fOYsg-%{Cw&#MU#Sw`7`5e5_=9Yzbh((vV;1#xa3H5Z{H3iJ!7Rp(o5Cv+)n%g*l; zQeVPAfPRp|GXD<-^nT|1_HVq{DO5rL$iHS)>`%o(fE#^b%LjL)asJ-xn&nAM5TzHu ztr%d7xM8B-L)V2Zo)8w_K7=mJ^kGPHm5y!A8zt?!(RtQe$~~}}sErmotzuHpqWrBa zRz(bM#SuXOnxT6+E=aAEOb-F*pWVx#`FqxS&H3oQm2xll$13L{uYOPOxW!85z{DgF z3`XAs#3PmL#M}TANJn;CSp5LR~ z92OIw!%K-Kmij$7`!Qipy`1T|lk8&kNVMNAu(ZDKy(W`1)jsOk-9x*L9XA0~!vvv% z0gd=)=77WhpT8P$LL~?m8p~q}y$vd3Z*?zG=g31*kvti!Hz-U=64uv$#wK#K>ckqf zjP>@6K`-0-x0cNlJw8z}pebvr(=oyamP&x)%@dmxKU5o`7PB71&*1$tDb@TUWM7zX z!?6`NV)^7vb4_iDFP@KL=4PHnVEdItiyW3@`-~x&f~A#OfA&+OyM$9S#J~f38Lc`~ zWjWSo9Fxl+vQi(R;mbU4%x$H|vH+qOV!ZKLnJ+L!+@t0M%)n~#^Z8DEnb;zg7a&ga zcv^G54-?v#CR{{?a{9#}HhGuBOOxpl4N^R^JJ6ay7k|^>g5QLIOp)kzZa2#2_C!Qt zqk_{OQ3ZiNZEO`+(KdoM6I?sDi3Ig!&75owp3<2U$56!R%!WN?<$J6LnRqZM;^=zP zB=r8rp>&%)uK%?6>c?iwOvm>>J_3YGRrdKlJc=#58or@`q5kj=^poOOaQ6B$i0BMu zCJ#EyHu0@O60_Zf*MpU5K>66l+<%v{G24LvrsWPXb}2;=EI&{auUcGND( zzy$eu{|KSULYo3`K|*JVLfDZB@<~Vi<9E>L0^>F?5Y*gtOksJi+4uNF|MYt~*U`!{ zT&B%FjFnXhsn5Fk{nGa7b#*+0Atp#w=P*V|6(Yl`=OjC08gOEWD*%>i+aR^^%=v*c zI1)RiTg$tgBj$6kL1*rbE${zqlsHK-d+k6t<9?sTvHbNaT^dL zY(Ij@zaXkNxTUK#47MfZ6P2%m=Tr#{9~FTOQSK+Z7v1NrEUSPTIi*LIGF~z|{}4I| z&&!`DH%dBPpeDyQ()O>`q^l&cq*obQfXTTy<$gz`0~Y!GT$7N>Z(zm7eAt zuNSKsU%>aaajR~5%!|xk*1ptCS<{g6LHZ7a)s*?ExlUCa-ejIeQW^YV zu5goXP!i#1g|HlM5s_rKpPk!moG%Fyiz^MKetukSqU{%{)F>wq7?f5{nSLX>V!PzS z4Tc)~dla4on|WBkA}I9h@^-$AF7xU<2n`bsOD=x)Qn>wqZ~d#b2nFy00vH zN;rzQiNaF)rIOz2es%l&jRXa>u&Pr0Z;|0Y`zstDBuNEN0!IR$W(G4Z1)srEH?FPm z!V;z;7q9k+0^Dpzo>{eocq2g!=5b%;GP8AOD2jZ@UEvipqaiJf&MH7lRoZ8B?S{Oc zM0{eX0Kcp&0%BP*u3KCwVYSHL%=%Vj5Ieoyjg5i5TNX7PP|Vk{?viaB@&@IO5!+5;QQ(sU zk$HNU(wlL~0|Cu8Jm`G=`demYR3b+E`~22p4s@GF`i+{F39~jCvqK#?p9ji#*Cvig z8ZfoZlhd|#S4@z%1(#UdL5uP51JN_g$YHhs-IdFnq)b#A8aR+^@W%J}v< zuztC}Aqpi+dFNB+c9EjLqE9dOtA7kMiw-kjl#IucKk}S(!pZZr=1dLsJ>&sXFT}ej zAsr`-e_SSq_?wW|C%H^>H^>Da8>y#+YBo~>8iYzGhfPSv*LnwnO?xhh@j zYXiwb5Re2%!Dsp+CiW42->SUpvNoO`S}hi;Zf3BrCQ$AZmASo3nSQ-^O~n!JEC$ue z>{uWlJ(psVXY|-GpBBg!>?_q6dNO^P{L~+#k3M4$AF7n$drlv|^&!>|kk>^z}KrBAEPc^7b3!8H&EJ`IztX&$4Fr+fQ@+&W6O;U|BMg!mz zC*bdwQ$LTM!;@8Lmm3V~HhOC*p2@?E-wdEnINSN`%%OXU%*ksq;QU%kco$4>yp(E2 z%}st75$(w7$74qK44jCloPVEmm%0uQwx1jiph3a9Cu#es`9Is85H+Z*)n1pxE@>h}Df_F&K3os;a)~oyd~E70w5v(7XY30227>~R zM=r`m?*3#vyVKvE2XkN9G6@z^nfwENAo zSGxL1=-@mroK~_^agCIJetB<&RH{v(D1^eiB>u+%g^BYEQdTy0Vz-G>FBRdGNr9pR zfx97WYoEGI`hY&+QwIWVQvU7Ix?pU%2~BynbQ&Q>T`sFd~#ieJof;!-ZLCS|L`c|x3CABKx*h=Ldb zb`Etf*@=^gCAhg--~1$*Ln^3Pd3rH^g=-zx-Vnse!UVBkPt5bg>h=>EBx~y> z!^s_s^m{(aRmvg%BRLCNQ$arfw?1q-IQK^XQvBdaSv*VYY~p&$;rY7p%Q5lL@1phl z5ub`<9on}!Y!()Cw{PGot?J~;iZ*e<2hv*Io;KZn!NBSr*~KP#nEYy#{7G%!fJP!K zzP*WpajF!panpz&y?pD2U99!ncD4tUgZ-sCRJ)`S!Rp`pZr0MCG3xnEq!;Iv?aDAb z6Mx85K_ZgTXYT)@C?NXRI$lDSCrh^<>)?7%6k8;)id=HIosd|?$;n^$M#pij)Cs9V z4Ba)pLA&qhhNK1zEGyKMlHs)F#*<#f2d<8reV|DYswHcB!kJ=rT~>C|9P*9zs&75y zD)If#mBx@lSniLs*Yff~!DwJ!5ytkbmuQ{M&<3Fri9a; zEwAK1aanpQc1$=j``W32%ns5mzq%o5?XZ8oL4!HiHTsE#`#lA)mVTEt{;8#J6l}O; z>W7!AUEV}be$bWUc(vYha(=Vd%vtoaBcHHfuef~l$8mErQi7TF=f$THiK}M2_qvl3 zSC?i@*JpDf$oz6P6O-0oC1RJCjmQ>GodErabN{pFl4vt&*_yFyh}e^ z_;IK-Z+V)#x(e|R^{%Fe4d(Qo`?lGua_wJvp4>po6E%GLEH|$a-zpSO-dPu@q#ixw zUW&p;<4!djXR0bk8MqveCLhR&+dp~Bg@2&8INy7!Z(ap})t8KK2Avw5OmxC*#=89+ z0oGC|2QBm+v2nMbmPQ;ELd<9%DwMTe^07YLNOM}moBDZqa4xWSM5?HI8m%UFPfD~r z*ZIx!+knpBf+c>;DZ>x*G4Q&KL-qNocebf4`E@$wQ*9uTu2qiKb*^-nL`J*B$*z#vS=>koKa_P+ zRagYy|8f)8k0y=}T6FW|yN-9v)t9hT*I3X)r`@}7gRiaH%898wNZz?Mhi<_v?|bM8 zUl`10SnkPMJlbJwSG%;mB<(nOl^YaQd2&d=01d=rF@d&gA-@wB5SWBm1mkf}DV6IA zh2H@ZN=)Oc*>F%b-@4PuoX)EGeZUAxqw`y*Tz$1jEMy1naf=Wvr@MX<$F*)<;zz=# z_S)V<$xeaxYGj=)AB6uRI`}oxSDNt`jBe8Vd*rxpNX*nLxZV0S3K`;Oa({O>R$+

7Zox=_As;{-JbZ6(%wLZYE}`$Q>JrJyou#9mu~q`4;9#gz2Sw_WJiL;T+OJzG04 zFVOh|AFQutk_X2=DISjf?$@MfE{V7&)LPktV9MkQyzW zK*KMOyN@r~q4@YSvaHjEHQS5(N-!gcaqIUX)j+EXJqKM8B2n%TACCrc=3bq)Xpq!n z7^7@#sGV_+JG*0-`zh#Qu$@K5Qm)vKH9=BqSPZYw>%#B4zz88}QA7xyk*G#Z-j~}W zabuX&e9#|NTx`;~B$}2w8J|M~C5F-PP%GX7&BTILZ=d!Qbc!~DdnmGasAGLZ#^L+f zq({|~7=wE9A-;l)6Tcsk@#a_5$Ha^q8*9!~WkhwhIMZ$r_a_fOcC@vCyf`yGS}ea^ z*#0zc73o0T zYaoZ~_LhGK^5NnBJJ^jd?>~dx0KnP*CD@J7Z)B~%W8M5S?6tGEjVEv)XA5r|SsN=i z>%XRF@$mm8wMrp6M}NQ-EQsHiy ze*Il3hh@Mri24UNOjjp|rM6^luqN(Da9Nv#X(N7QW-JqAYsrEjC_R zF3npmUH4`upzm^|L>1f@t4ojY-(2Uzp2aAG<8aiG&a90PK0gXFM#fU%?Tq^lxYFMt zA)a=wNj(AJud93=5Z~Ys`t8@lB0#iGM={5xVWBdi57zBYi$(%QGp-D3e zFTf?IExcg=E1Cwz`y#r^@`1+FY|<~Pr|6@UVsBhW0GwFVFkTE_9-P6hm)0~tD5Rbw zBq%(_T3UJTw2WO-|JCw6A&vZ9N==u2RF^oEWWTH$p+~x?0YW$vn}!EQ$8Ih~i^%iZ z-Z++@UOZAZ2Pg?C>D56dAxm$)K{ez()pj?J_DM(KQ5!YU(L%UYb$M90L`O`A3RbOo z?@xSAKN@m1S{2!ItqN_;B~*wQcom4h%bTB1hQvyz)h2V>ZN4PW<;MKOoI)2-eKy}%LBScP-ejA8e8+mJSxqpwWw2y8O$fzk3Pre%1!Z#e zIq&>fO+`eV2JX|2B$gTuLcHi;uQzO`i+X4+w#e<(a<0acC-O{iKH9m3=}i4fWxjy44^{i)^I*ko#@*lm7&oYX(mNs;BQZC9# zqvT#;S)uGZAazTIkAbadRMB^wdi>UPYTb|cX6&jjw6zgbp~@)&H~5|kcv2`PDr?8i ziBZLmp^j%0Cw>dI!e*NL`tUNa-vzP+#Ym@uX$F!|$+7U@(rJ#=@6J5s*JXam_2QpD zb5{f+?xFWyh|-O%O=ix0^)pKEzpI>g!^TQJ&tvP|1J>8 zZpnR8L>SQ?6$w@0Or{-$?~Tnq-ZCzc8fKztAA=F7{30?(*|eOh{x_E7uL$vSE|xad z);89+*2D`)!o5v;{R{BB@Lz%7|D?kHBX{>dp~(XYx&Wx5mlJ?yXnzYP?*JeZLU@6+ zTNejcAFqExlo$Rh5aU108RRyZ7s$H(hdl#^<@MhgG~s{2Z{vXQadHFsytiO$w~4?1 z3gV`yA@e&m88BA=WY&cLX4bd``TxbNLH=vACiqVY$^V3C_+LP{@%;;c+fMRGd>S`? z_rCxAfo4+YuQ`ph=GFa0Av(D+4aF|y@$c~2x}n_!&Zec25?W?iFX`C0(dop5T%Sk7d$oVM)O?6_#HBd&aQkL)vkKp@73)8A5m211 z<0vL%mvLdyrK)AItv8k0`PInxxqz!;j(Ew3Y~#u@dTkaZ^)id*X}TI3ANF;z14wRv zG5hrxg|uCSX#ibZz91xb10(U>v zJbWYSp-(mPfV+4IF~xIr&^KecH7juj$*S%ZOdKZMkSG;)rpG6G-Is}-{&C>CKnAjg)`{T7i{xhHJ z*xPtp{ChM?cV{23KWLEuoge@_|G(?lLDg>^J3u!7DgnHIM=|6Ub%UaaBKP}4i=vO>hT@6hgu;p92pm}gKkg{rC=S41R}?_v z|0j)aaU^eHQ2)m_x%H{F{z)%Exc^%B0HD_Y(kbQnC!f@QQRlU)TL&%9%l)7CX+(D) zKTd6s>CBzXp{&tWtrfk?Bx8gkyz)B4?1N@L)yIP#t5yL?@1EgC^q;byPu`llFA@%0 z(1p8qjr!vgmhd_p7x5>?xAD%Gd$kuH%RH~K`I(;Rl}+O7w!B*Jf9IDXY2O8bFWKQ~ z2HG{@4=%M0BEApA29zMlrtR}ACUn#K)^9d(n3b276jbcPniTZA@)0%g`?LC`D9zXJ zt&Y@;F^&<@3v9W>0bQ&bzqBd_T}X0e40p1+*<}sw7h3vRq|W<#+fY84 zI=TKk=KgEt1#KToZ+~~dXTP-)kiUTG^8MD0f2)e{cQ9R?-(b481tPSBZ=rN)&nZ#Q z$G*^7*I3%TRR}A^3 z$O(shB!im+2Ch%mxlB6JH5s0D@5(aeAKE# zfi;!D-+*=hnO=X?z+b>g1#T4=V2GtHylieY1OS|R3xBK#P)yqp&>H{Y1prk#emDGG z1LIZzA>06}EI*+5{@>64#dE+5`9t5o|NS4(TZl*C|MABEV7_kU2$-{5BL{Wx{96qi zAcXVB9seVo|4|!<2l6{o^0x>Jg!``{mjBv+2VC4+U)#pR2f!i!UwG|+Z2#4t`LC>_ z0QX-aHh^{1P*v}w#cw`lTdV6v<8X|_`u>qW?c|&M&Q?Zym9iyE*2$L;t}hMUXa}}y zLllc&8vI>LupTd6=NRF9p(qv~9$zMrjtsBR;Q0c5A>Ln0o{L7FHs^yYBvAAIxtFx0 zr3Y^Ci;n1ux#fEsY112VRNve=C0i}CP13Kr30$7vi9m@H&pM6uF87rEP`}URZ%TD% zz@z?ECf~3c>&X}{W|R4yr<2%^i*KfTJElfYw;qYlyzMgi|JZvAs63XfTNDWvTml4l zch?ZyEx5b8LvVL@cemg!3GNmkxH|-Qdmq_5dnf#UHz)9g++$~A!lP$mV#8x)r~iABZonMZzcjjEjK!am zbTj|%1OAHeD98Kj&ES z;qjvS39R^J717G#j<(?xe-9hYw8I&789nFf;ECAU(P4`~n^kX;(UNm|(B&6jNM7U* zc1A~o-;&GRKYvEq3KkB5Q}tlQ8O=;Igx2oEFRMpT%8NU~0QnpH*8ZvfD=fg?&En>&YQ2_#B zsK2@Q>Y3z6W}1dT+~l)9F4cYlJsOqH^Nn}31AW|lOei6JR z{MB3Z{Cd|0%IW^_6dh!&g!oWPm60MS*r7qe21SK%K9edt*+bRtwbcTUieR2;=v5s< z|JTyA)Me6$d~_g4`jen(kdzjs8Cfy{>>j!0ZG*y%BtpQ_XR-*%S?%H6rmzb^QP5#E zMEf7EfR@Y>s(G|c(lhHBSn}HVPk9O}L@}DR>f!={wIsQZELjN1|{GWBz0Ne7bZrQIuM(y(b`-55l zkeSNEYL*oL1<3IH0%RP<(w(4gIKig$d_KE~vjzIYe0d|h!z=P>G;gGIMuG!m%(`O~ zs&Et@cs}7YGA#|c{F5s?P73!4YqiK|k);<2zZ{eR9KVq{~(ds$vwkC!_BuW;r6 zSC#YMtqa4S_5Zp4?|%P3IUjJZ{xkQCp8hxMNB=9?@Ne8R09h$vXRc!hU{=llT>qd| z@n{Wgb@c2_tSs?pb!`nC0T5hI$M$7Q-^%{Qqci#ONk{Jo6-sODU}>ad>tJE7;{X74 zRz?76L^C{ETN6toKtX_04b5Nvlb5Fa7sLa!*WXkde`R`p5Ltf_T>sR|{QmO%ZNFKV z8Gh?_xFR(~M$6l|FPn$XWYK zTk&(|@~XPFwIGvbzdUK9eDNkZP;@T0)a3qvgbnLlgpVPbMz5vmDEO2McbcaOlEWi( zGN)Ly>^c+4N7#~7#1S!N4Q**{;=!zAn4CUXVo36R_? z3+7)DbWvE*QS|n-687X}a$S~ikKtW{eK6BM(B=eg2|fZRT4E3RtgdU#|yC?j|;e>a4&HEtMf7i=V0`~13xELf*mh$^ls zxR&}?r3L6=IF17SW6~7gy6`$a5z%s|C1b12Xp^6?82g$6Ym(=$kL01NW~8ShZx?L$ z2{*Tpd=DkVbI5{X2hKk>_CYGY;enhIJLP_Li%Zy9&zI5YDgHdI#PyE*ZBh&_)zUOj z598SB>-|@5!MDnj)07e6Bg3!!-do$+Y>jN^f+4=jGSdc;l1UU?<41ZYB%&%(GD&iB z7NyHi&yM3Xu|V{Tws z!T|SeqYE$#S&T4qhuxfPqAhZCSBeC%>g`F1ZmV!X5cVa7p#r_BG?N94ubvJ5V9u>g z1UmB_`dQ$Rdvrr=2TPv~C#5>?_W8gC@>3-cD9{`_llwmzh+oN+7f<~ULYJNmkKs=n zAwhs80MIM_c@7gE%bzAd2w)iK8UEyAgy;cZ0F?E^I0ykc1$w}a zKQ;h`{`?4FT9|(pO3w_?IQ@A(3*c&hFq!Y(;nB+2TItCd*aMgtS{VT$fX8m&Z2$5~ z3p(2i%h>}4*MC%9SdJbK;N$}?^70Y_et#lHK*@hI*1x~I|6=$6mg`3?e>Z%8g$(=; zJCI*2APfBufblo5Wd<0`e?+!_43_*=&HogLGXBOt0)S}Dkfkpjg5b4V50#t)@b;Df zp9ri)KGmH}to?Bkz92;}gMM^JWgkD_AP0`t{jyF_>e9yO1!MQI;uUIYYbW` z!nBh`Oa$`Uwv0pt=L24qpvSdGhZLJ{$(DX4A?xrR>?7Y_mm1-JkL3(nQ4g(PHB!dz zKAl3lLNn{IdC$lX;s8tSD7)B7c3%SS$Ch?aiBo}!4{KJX97!#uXktH@hFC&Wo*h#H z&UEzLCZg}Q`E`$Wiy(nyPVs9-O{b)2nw`72y0tq$Rg+Yj={k`Gm(lQB8%^-hy$rW5 zi90?p8XPY18Ij3;=JS*!=1DWR8{+(jkzsCm@{h9fRrknEQLYi@f{3q8^ZVy$b3tz3 zn9E0*kaHzw*W~4W7vC@2AQKf*-*)NEhCu>;Z9?7H;;OeTt=42o%aEL9!|M3kB6k;@hE7JfPp?a zJV%3C9ebX~`^Lvx#aH4!Bn1LJ1@@fyHiULikgq$%aS20SM67p~Xnaw4^5VQ2PR6c2 zGZGX88eg)LDLt5NXsCZ1moG_6?sUAQrdjYXP@&-(f8WA%cSYjRQ2oKec*?#Q)?c!) zT6N)T1^or3xaYDV-L9tRr9$jLt=!Udja;eK()EYr$*-2iayZi$#usfVhFdy-Nj`2P zn#FS$wHKO1$v_08Lj`!=1TV*gM1bUI1q-M_a|q~89OU$M%1>nEfK|yo6y4YjnC#K! zUuTq55AvYyZlX;?t>QdG(;uspE8M8WoOu%}kB6+I!UWFNKNPC3e-Vkq#L_-mlO_4W z@F|Uw^!%ZN7Km$*M-fK)gMCPhOY)RHNOLkiHgdlpm*B11u7Q16(6uyL#(7%5bZow}m$jdA4^;rk63`uL6sHlo&(ETyH zsL8%rOP#N=zBBwXC;yQRM*=_FoDFmIRo}VirJ%ljP=sG+PXQw{AXN;T43&7VC=qJv z#CMp_0}ca zP$oHCZr!m0Z<7C+O0)zK9S&&Uy|Ubns7By+*;6m9rFcfHbXTU0mdGyMn=o%&mNa)I z1SQBI7i|3Xe7&xd!S1@1iucI8wO#x4$ZQ0j#zQveo}rfA{qDBMy}6_j^g|>cJd(~d zR^e`QsuxBL__2dx8~eapAVe2mDd6ZnrtvdTn2EH^^ z%AO^Mp^3Vo*#N3hp=f>2YJBw}GvH*FiL1>p5oQju6t+zHyE;bZYxUvIfu>#Ich;a& z8~aXO$9nE>C`5MB(BY?SIKui}baVMT_k%gV_US&IxPw_%H*93t46`t>d;x!(1O;xG zM1%~lbtZiy_kD-sh@m+ z6legt+OV*Vb&b2dBl;5=YKI%@APlC1`xR;-oXx&@cw-g+xB8{g z%ii_S=Sr|D0Xjr4a!=>aJovF4vO&&f8Jtl-PqOG#KEf!G`8M?@)Y?QLaiTkoAA%FJ z(Q(-fBT_DWcST7Lzt)KN`GZ#Dir&*r8b?Iw_4DK__$aJTE~wFe+7~-uAZ_map;00Da@0R7Jh@EQLT2>XBXoS#tq=lZ{U?f;ZYU}5_Ee`k8RH-C=0 zu&~kLu>&F;*y#Q~{?{=8qGe|Llac}4#}_J`jo}X}goW*YGO`IchY0`%m;lkW%m5CZ z2{6$5QYI5CV6+Rc0Q^uqCV*T2a*P2G{lvz~hR5<}eSVzB!~$^X0Zuu9#s66srl0j- z{bRHZQ09L~Gcf$X)_*z6zmn|#uKE25D}M9oMWv-FUfH^0AMPh)4@;4IupGBeon$oW^ z7RJkmY74el!nWbc_rY4;eU{Kuhs_QOX8iC(kRe|~@FcaBH2K}oVNyDi>^{SR6W|AW zcCM^P%F3*LYq+drlQvY49)m?U%@1FK1S1UE``)atV3^)I(?uIOz1(x@+eZqI^G?Cl z^my~gx1^{I%9Z#rl*fW>-*l{{1ae+B_0-q)c%4@^Vel{=W_NA#(Rb?bpmhg2oqNn( zrzW@6sEoX%Ec4nvyLy0KvnXd2F5~fOcW7*HAbTmdel6SHGeyZi@_9%G;*8XTXR!DP zs=4%w{)*vkYHVeYBXupM(zg&};+PDtiqD|?)@Qh(J#XTb#NHX*p9vPy=Chm*9Vgs7 zm$$j?=}e$*WEie&3-}j93)`#aTRlJ z;DK%bb@ok>>HPKv4-T#7+E{4qF|A#w|1)o|7FDP6g==$b&gD_mTITEMDjW5En${rBN7Fb@fFH68`8%?%&yCE{?l-}qKC!dNLOiGIl!<-{dyP_?xM7RdP=#xu7q7g%^ckznPA-> z&UTgSmyK4qcs^Uo55vqugmc?hV?W%l(lSx2&?wLVaAf7?@1-yMUqa zE$-rY!Ygma$dpnX+Xo!_-<_l$F`4S7KN4ZoRfI5%N5aLo!6g%&u%(OuF@1#fON%(i ziQcQG!~#zYPf=ep_iXg7Xr>Wsi?3PmE4c7yXlW?eXs!v8)vK8TXIJ|Av9iQMmex_Y z>YF~T(GJ)r=c$-j=kHvI*`1SIIk-=a77vP8P8sh_ln?|{6VEIP5-%mkc^%$Nw-nh+ zJXofOsFL0v8@Si*2A_hRFbLl5j-K`{#Hn5h9wxnG--(TQ7^zRr{+L+6D6|JAyF8eZ zK$;J_YC1(M7Sw*8chvf*I<;54+gAy5qk4U?!u_W`=|I2(^2vfVI zf_t!5t>4uC2se7wJ(mxn_Y49Dx{W=qka?N_;AUL2TF zNaxs6<180b-)g(3B(mg0EZg7o3M)Vpe;nv6E<1j`;V#|;P3pK)HM0wuwrlPll(%=; z!8 zXllqo&fM9K!6jNBaL#jmZ}1M=Ei_@!XYwZ@g=Ee)d4^05>bkpEoL}y~EHHC^vHC=4 z9PrUj9&Cu*@BYTgG5Ku zO`k4G(e2Z}V&xgkEQygHW7GxpHD&rNQE<Y`Wv}Dl3j;>sb`2&cH_l$drDr66kUd=n|PlGtpsfF82}B z{E-|}OGJM^UY<+FmKx7JB*>mt=A8D!IVyyYXxB4#&0)<}GFtRqChKl{^zp)mVP0BE zI={R}Dhi2lTA(FBV_Bf`&Fx`Xme{3rm z=MB0_E02NH;jJp5gI8b~ajg)--&jbUZ z?=0t7^qzL!H8Xwt0{6CzDJWOrTK4`RtxHNh7ul3=C1=FG7sa>6zoahI21HN4q0+AG z9+lZz!7XWsulLbqk^OMV!J@|HBMbELvUOvZ?WxED;`QD@-M6uOK}W>WNhZxyu)w27lM z;ReZ^>>m-(98I;0MoUj0z(j?g+?=zi2yrY)9`*v2ix(h}L+xYRsmpvA@*VZ*_zr)E zOzxWxTf5n2ip7qCC4CAbXGf(6x6=h}GMCAEqsU-4e(_ZyN;LP@)8pjAdhaqK#+=6R2&DVw`dt`t6mld-A!6ZbHkK6G{2It00~f_shU%Hc z4yK;Tdf>?La3#hjfMkmfYhw_t8CutL=QJ0AHe^3xXKr+!0l_f6!5=jLNPmEgS1x)* zE|mt;8tG$}HDae+1(@UNgqijTq#%i+(`~CdT7h&h(w_W;?r=s(H ztmre-&={_Y8|IQGrT4|il6h6V!BL-D`y+Z2g;d(y6VS~iHudRay%gbok?MP3?kJ&S|#udi_$b`RrS3y zQ$y6Gg;$>r*9&UcJ$ZB{mUPTQ=}jpA>65}~o*JyekDN92LM_2lpp=tNiE z-8;33Py=#M;5(Cvm)ybbfOYGpos)6xefG?(ZZxPRv?q0^3h2&Mf(gz#VELX#QC=sZ zUd2(COfr-6MLR|_EBh$jfk&Mm*DGViF0)2`L+ta2K>KnYVlaWNgupYBUy;m0#Ua0D zE5d0Urw{+TmPTOOFz``wSuPe%tBTD7?NQJ7-blO`vhFI{W}p)+c)avd8|OKJB=WAI z9SI7!*YOEQVciwxd@;l|k~DOYl6M@FNG((i6wD*{EELBxjcPFw;fA%8OFrZ*ni)3) zGPoRL^ZW$#*ONs8?OJe~mdtN;c0-qL1z`fnT8@NbCy5sV0;V>gJEo{$+buCE4tFx9 zEt1;lN@7H>k2B+_yd-1i?@5@MsV2Xd4SqcB3C#g?SL%z^OD>#y4)%VV$C(k z*SZ3Qs|yq`YOkApj|x9T^@Mcu@1^TqpQsh(Kd zlnwtNckC5jXN7s({**b>1CAk(u>uQGrh|vnBmOzAVIAakPV%1Z{`WBTX6v@UInt zEqXAzNb7q~Kw~4CpY-`Fb$gDPszOhU%dBNq%RUvj0_NU`b zlo64dx9&T?>+jvXbtx(h5Rg5Y7q}J{_6Eq1@R+yQgY|Y1VA$%p&C|7nSMtCOx7UkH7gU| zh@Xap;iO_O$`lc}!dhKA5e!`$E`9~dTRP>`bLC?f`r2PVuZlB|J2a-`)z|(_4r!8@ zB+Z7ik8Uj3f-TTixM#;*1K_EmhjcE`BE(;x%lnH+8RAt9(xq5mS(GxfGkxY;Mdv$# zdAiL^^eoY=U|o#&bbG!_+*%RCS0zK+*5a7j5R&qHpe4P_;jTZsQjE~j*Zg=N<;k;# zoH&d!YdhYPOKJ6)H&h1RE(#quOLJTMY!|nxOUkt5gEuG(?>CZYSlGMnLSbuxCgUyn zP+^xGXF%hu;K**Nh>;2;=~i30Tq3R!pzDD!K2fQnHt#cz z7r11}s4G}0ZcxNmFN4uMQzQJ@Ha(m7x+)zN4(hr@E@{|r>6?*t!+HucFg2BgG+4_nA9CylIH?W265dOV{rpPaf#EbC3_c|_{ zRm$66zT!POz3wbMG*6M|xJB{5Uhmvx^Rs_j^Oec`dSQC){+!d-bHJ=piC`6{w0ZwfYjr5zM*r;v$DB6S z2EkilOCZyUDy^s-8+s8kz@*QwAx7mmAwSkhWmiGJMn-04L`023CQ%FyrUVV^>9&E| zU-iQKBrixnygVRhuj`56wxCkz@1X>Ls{%b9t7C4;N6FuAPd*DQR+z)}ymo8nw`bZV z_G*MRz6TLY{_7h9)zd3{@&$&o=i5_Y;S>*P2L0-djUC#^2;Slxr^twmAi8D=j5e;A z4y{mctb1!TiS~+uXc)r8jP3>hnMAE!ve?Fh&w}Q!2*Hl238M!_d&srli%UTqp1y}+ z$K0Y3HSNzhZWwjI8Cl{8)}#(AS2Iu8Evp#7Cp-%69wv0O+Qia-bV8`NFJM!a-jtvC z%oJU(VtXYgkQnTj**sBRf?TF78^FPT^*LaZrY8|H@s2|*B4Umkir8)y)aag!6|1t` zeTGwS(H_Zi`!m~~TxcH27%kkpgnIv1wV<$wzLyMJWjv8pVh_@qWE&7JX)U;NEsu3dcU)pA_t;zvPXXMU18vQ*2Vugj<$IeKYEO=G~~V z^^pcC23B$IIY$~HoW6Zr99|4TEkkky;)njcl9oXOMR0~mP|l;NOL6!wVPL}tDoT^S{f=oGh?x@{Eoz7x*eTJ zqmd(nY-VeTnXIwcaY?f#iU~4#TmEt?H&*{9T(J1k=EQ7s=p&+53X9~f5{qY!7~4 z<%x~?mKzNKZ2J%1ujLL2ov0g`^uX zJRkaaxTLHQS;_~b01T`=B)wdLLU}?LzUM45YNs_!=(QO&FsB$UBD5Y4SkjRKZz`GoD(M z&Eow;@4G~BbiPJ&)*X)W9i*&oGgV} zg?CKkA`J2^jo-Fyr>wX(YE!TTbWg}mw=gu`c;YO$#(#R*VkJz|v`Uv<0Fp$e4YhJz zy@Z&LWXxP&fts%*h0-mFuW=sB#M@rv;MU`{S%+1g8yvKL2Z;BlCy^A%DQJ3S5W-Ui zD^R#$El@lGGwl`25q(5P0}KU7YRFH`G42ujal?K1sg~gHsWU1^MpJInRE9~o@Vjkq zziN?iL^{gYxu@nw$v310T172 zG;-@=_oUC!$otyM)l#lIZ8)d~9hKT)xqgmeJ)k)9i zZ4gW4PK;|e<5*6YPWuHvKZpRjTQR5Td?&o2n8EVkbH0T3%rb74{@L#lcCx^JX53#v zq;kxCf<9Rk<*T9q*R8qjy1+||xJPKo?2#r~FfUUXHCPf+faYrl@#G=Aff4CuEuwcr zgK{kQ>(ZTArUCb9bB-`FnR3D}6@wd2L@yIz~YMs++Ad^CycD*kKYM{opU8?4X zVx|c^L0}@LYs{Iom%5Szwiwi)$~5}w(vt2Tl|Ps>*3(aqfOGRLNPlqoYjC4rM4nKA zuoO#0N09~GGTyCnP^em;w=W~k(yRK!dHI%0sF|M04SUyxwjfy!+*Oq+@H5!d+TK~^ z4RD=q^u|qXighzgo-$p?vlH_BvB1inW)3dhcUawpt-}#&@bx~SlfE>H4tz;O*@gSC z_`pU}S|U+-@4+E%cN06oYn(ksCcZz-StH5G;NSO?H}Fq#69+0W4fl7z1bi^o?+Z z9dF@$FUTf?5rR#~@A%%uU+-)qWkYYs)+TZIrhljs0TpKTb8gz>n{~ngbQ_`+Ie;i- zyX!r{^q8*UR`1)J2;hbrZz*m9z+ekm$T6~?d3C|2=>|uAMmRv- zn+(PqM`BE6G=NY$T5qk+{A~;L`S-r)Z|fu;o7`$hj9*ogN8J=cF~(QlO~sze+s&oH zbAFbNk(LfqkA>QMF4+Se)B%+s%_P;@o(}uu$!7d<>@F8kAgV?=<)a{@Z(7n2!Q+Kn zc2Vab<50&n@l{T2)_P(6K7>UVwoi$~Aa*9mdsuKiIfSGQjREf~oPAq%u#Lu(Q~Kq& zL*D3w3#74VP5!1v;)Zv%k#FK3g8eZUpZPh*aTl~I$Fcf!+E$)agIZ_~Xq*LY+`#Z( z=fDLwHN@^Y_w#D!UBpoMd}`kvQ;`pvhd-M$fY4`J9wwZ*pt9Q}ABrDBfZp$+CNg3E zh`m@UugI8OgOA};l$TP|h8_kS1^viAI?O{@|J9Ed$J4xm)v|mD8d2v7lUuD8W1+n+lW;$OWlB&G6XUlj- z^9miwQP2$5l=!F+>k0Of`%_bn718L9s3iE16U6x`Rkl^Oij7Wu#necfs!E0nz z(r%eEBced>ZN_nnb!w1ne>swM_VAjp+m}sor1Q~`*G4xqTKZlEG*q_=_f1o;AgWf-$@b@{4Bze*orYbFdsaabqDEm(iMp=s zQR=XwSsKqoW?`}}f4k)4x*>FO4#|$tzlcpMjd5k5vE}5(J$_^JNPZ<$1!jqe9&+S| zzHNc+A`ZJ1fm-&JZBWw9lE?v*?ekG}-gOxnO^a!T>0%_ahAi?%q+85~LllNa3j-w` zqUll|wD9ekQLT;sdIM{|vIzCtWO*!;_p>*KlNe%ddbFxawjD=B$sK4H#WhX@X3Xf) zU2XX~OlY%&_0{Hj>}=tNzR_FI*cTY!jd0-3yQGAIif?WsyH#u=O;KLCjTCa}+a^}6 z>w;Ce-{f@JZWMkqZn7_yry4La*a3O?8p%$*uROmvn6{{$0SfwoRMbXEs0wO?Uf&C0 zrT6fZ+Jq?C^fO!}q&juiVt>@BJ7{dlJ{}PCZgTCOUEnrXxiLpmmx0&}e%M%yRo-BE zyHG1j62(rq*XzE)gRAWoHz5{~1&{ljV-NKqL(EJ9U2JoYQI@+ke1QQy*on3DGwhYB z#oWzjtD6zOXQSZ&(;JTMG(A}+%<)s{DpRKDog&U^E4226n7h|KA5+|^J&@T7-;P+a zkRa5OPueXxz9|gV;?Q~;SF9xD>6lpI$amx@QAx#S9SC4GS6+g$BzJVh!0R5YAvmX? zV1O8T3aO{lXxkJfe8R4Z$jA4U z-YLebmAJLm;9>ff1e{N{t$yGqYy(ZmEpzx94Xd=EPo62HLP0`X*_ONt#IMKqlBT=j zfxAiBQdU)D&`P7hJyAkx_9Y^=+Dlm;89 zSHvW>4ghTEG;tqOh+s&2o3t#o!8?wp4yfIg%V+wWp|DV0x!`A7t6O5Z>Zp7Qj?2B~ zc&Lw(b1nYS7B#Bs2gbN>$qJP#VD3dgQ`m0iGOg43$z7l|ubYU3P|QmZ91OgWoDnx> z7h$Xg#V0i@-d(V5vTVxvW#VRr&;)sk`0D!MaHK<-6eA~!#F4up3rmCgpnQX=6f9R& zENKm%viYiJ&S=toN1&_v?(TR4WGB(dIaz_L^D%i*pB-~_qw-=~(dV;7-GI>4XU-y% zIIpmjfu4sL-*%R{r_&F{Sh~w64BBpr-_CJo-6Rd%l&4okO{qgE7m6x3T>?yrZsHAC z$Trm`hs|z^#AoZiv}hVohE7*AdLbX8_nTuz$IN`}klc<5t|oCNpC*vX4y=%sddyO< z%gn&l7_n8dqakd^H@6>%6-2exaGyf~rN-|Gs5QlJ_^bu9v%-ozqYQqF{q2S>IqQ=+ zP=8+<*Wg-D)FV;cu1T=)q>oj<3kC`F&iO?ht!Y$tq-3V@@!$;gMg6)ev9{vbSsKdR zS=N-3v(CJz;W1RLY;oGisEY55d7F|DAN`Qy;&xBzrd7cwUq2M^iO%{mweGBjfrJ{B z9a;)}$RVP7#j14!d1-o<@6H7Hc%N%2Qdrf$oJx)w5br&r{Ju$)lmgBJvk^@znEMS_j7(P4nPO@i#wF=-&Tw;`qZYqV z|9sN-!YhcQ2GS$+KHnis$HFYw`6}I&`sof}56dgLYv*O1+sAvcAuC&7k$Y>VB#+&; zhZUnuT}jofXGkp&UXjxTR(c-b+c%_e>?29v(au8s+~}3|dTKY>gg`bYB-a@UT_EOM zcto>4kH8}aPkD8Two134g4yjMX~(3agX-)DJr`Z}Y=JYpEo80X9ZqEz6`K-opC*eh zEJ1>Rld_L)7YJ*m zxS5;4jZ?mTT%6eh3 z`a46~fyL7ZHiFAW3_8+6r^5d1M#`b4)H8EZ<_kBSLd>NF@snG=*IzLCz`-+Qx?m_0 z<3~And0W2uz!qBDRnuZHORhZL@JNe5AnCEhv5_)}_#-4G8*!IPm^5pq&ID|ve&C5a z`Wm0M73ZGz42Xc|I#O2qr@Qnsy9&K42Z|%R_F+dje|3GPlp81dDvcJOS?d9Cfkqyv0)SN8*=b+YPD(>oqU{Vpt zKJ6ODK|#vCLwysjr$>_CQkSp6I&Y~#s%B-;pmu+Cx1<(0jGFw3O`nf=0>-<0?8aM2 zvik-x{FL%8(DPQ)h4W&D+Jkue`v|wo^0kXc$9-EzAQ1&6WDF6r+O_#Yox{%jk?Zx* zigD7|SCLZDy=}3Rql!VE5{f*|Qbmpm9*?7jbw-qC`S5q`Q0^^<`D*c`3CQB2)prTg zm@r|&rs9S(qdl;Hdqgs0+Q2dFSl`u#o?uq*qjJJRhjS}}Bv+%Oa!B7`Nz;AA9krq5ei;^%3kNU-K%tp?;#6zC zWBRh5gW5%870t}tQ2l|EBm3eVi5_XG*0}Ct4$&)72}Ghw5;%lzxmOcH2&7Q!y&=Su zyYrGxvkf)a+x!A`WyINdZkw-dy&l@XXxa#7f2gGt=vjy#iRY0UDrz>AeMaKj!Qt*)YCgAVC zDZYZMVtxaV?iST+{Dce(kE$x~Ilx?dn}k3$BBSs8ENT(N z!1-Z3j~|_z4EP^RLEj&pC4jFezCSphOvbvc^F!%z&BHaO&j^W(501RKMSvHpT(c?R zp~HZodlImALl>!Bqk1#^ny%!{lnMO^>788j2RMIna@n)-J`!!3S?I`JF~&i{w(*T* zuRi-{ux3n+{66Gh{gi=|;KJA}rqF76TdzR;a@y4$w_-#a0aEO~PjOvQXE$BmIsxMM zJw-sVDtPIiEuMV|xxEz!M8K+@A>Da1S+%&_?t*Uj61pI$_nS-&JXhA)_c#sA7GhH& z-qJuL7ZXYJyni|@9y;#0o#0fG(&YF^g%}L!qZ-s%)CnmA39T&;DflJ2y_=$py6jeG z<#-wE$?9&ey8{6V6?8qoHMHE%pJZhBT>FmP?-I4C0bxhi8_E-0p|j8=UzJf#S~Ndtc- zHFe28L9*^-3A_zVtScL?qP`^*4}O)%=Hk=n=!+~aEPj4`bTOFSC^{zyW`xN5n)%bD zq~L)(V$g7scm}KB$z2;J&+@AGITm34wbkGFa7SpDkvwEfHe6D_e@x$pJRI3t?S9p0 zDD$YXMol(;x-NIIH&Fz;iVf|Q5-9Aj>wN&erNx|L*z|s%4PRwTt3gXP>#a!y4c9Xc zK`b6la1TyywG{$(%2(vHKhQVf-3f|@-TtJ!swBMAh zw4#p+sd5&>+yNLO0wi|^I_WV$q5`oa7I;4HeKA7xx|82owO?amV{}kt!;z&$pyBrp z)GKFP1iLI!4ltEX51cAD`V3u={Vj4=Ph5)dk4bUpBloX``mE`Hpj944g)#H&Q&C&JTSehc-4j zWX-uMx>aSt&KBBU-3m{``V;%-`x}*%Y&z)K==w8>Ka+O?iP2N%{*&=}2_yf1Wk*>5 zi!7n8zP668w!Z$R!-dV6?b(#$)#W7<7*x?yl;H#ZD8co3=oSzB6#QG9D%g&DePs<# zmMV|ywPzfv#&x4q1W;r`JdX}cLhPJvH zK32M-LZ+6wP*JhD2x*yV$*LcUvvgu*KYY;6uhi1itVm2!D@)Xhvn(vmmB~i>GpoV+Oyh+0OQCF3f-mN>p^mfi|xjv$WnHT-@eVc zLfU&A;e@uN_mq7SD`;=C)%xY3%%|+ZpBZ|lOu6ZUM6$xtk%Gq2n{r!x1smSz zNn+|6G@2wji!-j=JjpDAe;aEa6-xV(G-b@`uUl8isBRQy}k~tCz^VJ2PWV+|=CFu*b*S_WIhfK`=s2ygUWXote5pALR<~L=x!nphuOqL`J@@C z0bo9hf_l`!1{5Zey7&Zi(OOjI?gpL4D$lE03wJqEsU4WBZv)Jh`5u`;TS` zc2rsorjs0ddo3-cibJGd?MWR)6(ngjYut|3kCfs`gx|C{Ei?~d7s0C|5vM0BrGFsP zN>WB!CQ)E4bzM)(XeW-{lp5A-wFC7?9LwM|7?p2&8i-YrP8$2H)O-?nUTZSdzC){j zpUxJmVd*{Oi8FesF5NUMy}bquNe86LtlIWZFVe3Gv45v?07R^Qq-p%_jr~iCEZd*i zyT66gi}4Em5Z?JMn**S|_@ltTvN>L|99|X{mX{;U%m7VNz!pGK5|C8zXKeY;81(;t z{d;b~|4sMfe|Hkh|4sMee|KF0FVOF~82_P++K-q0FJt@-7+Vd;x36V~H7et6D3jTfxxhS`eJc&(APjYwPDnSQ*Q!Cfy56 zeNogyCFvFkOl?Jjj+$@Si&h`YDoRr}N=FiEZy9LMu5MG_P3W$+L8BBDNZPeL@WrB{ zSW7Fy3PHA!xl5Lw1*-yn`4&}tue7Ii>Kr}Q#qKBP#zFUL4D$+`2G#Q3{YXq9n{CS?U-M1KkLg*`%Hm@ zyHCuFAHBZ#EI+oOaDA|3s1noqo~3&lBA}be_-=K4ftva_v*D|JMr?Do(o`{4(ss!! ze+%P6ViDICgMz?DO^M^(`VzG$`LG@t`7yQk?j#OSki$yJ)x79w&Sl*WANg4o9=T2e zJ`;uY!nkP=NA9>xZ42_6X-AkN_+y?~RH?Dlrjt@LtuN76u-sC2fJ~YNvRlr8v3nIIGE05y{dM7j z#p&=k{1oSh0;5EbvX7fd0Y0BmrcDDSg8v`J-Z8kAx82uGW^5Z{#+ITNR9AKN=;{w$qn>{Hxv%?orI+dN`FXrV>&v`JWZ_r~ z`)?_4!Sa$w%P0Oi-Y=(|)!9~-)Pp|GdHi#AY_1y93ZgqL-Fbc{KtSzo)=+=}YWR0_ zAx|2w6!y)xmwG+=HSt_{W8ZJnsZ*l~^Rt~wRhKfTHP@_j?dN*lX=96_j=V3^N|-}W zk`!hO#_zgz{jsS5tV;pkoVEgngoq(M}jq1%o){!*oyMT$V$ZKS_Ze)@Jzk%GDkXhf1wz2^T-xnJ1gD#wZ{Hc9j})sxo-u>9d) zTqcJ##_@kWZS`#1#@ArI{5j+O`1+@M$jYpu(JCnO^rqhO&gV)DLeI`v@B$uoGSEg0 z>aoQ%8)g;8vDKCVV%VwGe6fBQeF{g-1-;rLj_E*Mde)EE=MNYLwP24PI7Lsq+>w*8 zf|`ue2^B@y`gjSZy*^7_54s}eO8B$`RnUFxRk|)sLF6*anXtmqnhH(}uo>H*e38y_ zhq6DfgNRbvJ=$t2J)H>&Nlk0lA2jt%OtyVw=t|a9Btf)~_9XPIjv(d8WqLIxKY1oz z#WrG`D}buOm-;Ey>f!o}h_+c_x$CA;7ExIX41=NDURNkXR$3qe-mbbU@M$a&jAP5o zO4a-{FMwUz>}?@JSG6mlsLxm^&LdSJJQp5+J|fpH0(H-W1;)#ZUkF>Z9zqZnvsyNf z#hI3su~wt^wic*pgLW5e!>Xbc)JXE6wVujDVnEg@l})H7L-oq+#j4jPS>k3p+H@t`9BzV5;qL%yp+-9DpC` zRA1J!Jg%HHgX5XN;K7-MZ^fwTiVV6q-IY~lCu_5p8Ec^AIiPSC?#Is*actU^ z1PLg*Wzo9=wWYgTHkxA__*j#O5D1NIc4AptHGPc2ZaFF38h@TQNx+4Q6(plISnrXP zoE6-gjfPSkDyx=6rM7}9+$_V792u(|k`VrHi550H@th)ucuhPj*TE<<&8^Lo8w}TZ zV%$SsX$~yo`Z|$3ZE0&^E40`(q*Z-c*+UM5Z;Rcuhzk#7W8y|Q_pf^p64|LY1_+H^ z`BUKwBk>%rf-7f7?Z1qLuCCH65tVRtB@|K+qmS*q(^e>*dOMaTZ7$;79(uy^BuHV^ zlEeKhkS^}x&NT9Uq%h5J6{m?B>4v%Cmr}{l);+h(UYk}8y1IvD>q$4rhiILfn{7tx z-z_m7)8t3YLG*Pt+f~>bl1tVb5vzm>wII#<1sTq-gUVsHV`bTiTNI0k^B{i6vuBzUVUfr*7?Zl;PG5fu(`S!Rg2#PvDpI`%!1!6x6WUfwW=Pq z>C@OnydLkB1E`CDUwR!~Qwvm@KSGp$WqeAUvicCK0Ov2^Xz*#NgGE%%8p6TcN=Swg zd^~8GF-!*W9h!@vT&WCUPg^hT*UtUY#$ku z`9fao`_sRSE2d{79Si~0kP0LS?sJhg0v`pt-KEcfRBfS9a&uS}a%I5|gK}@4e|zhY zH43$xHtXj}LUEB`hr=2=&w0BHv#y(IfDU$keVhdie+$K0iqnvDOnCW|E~^1=OaS6c zBLj3T$61@jb9ka;45$LBd6UFVV$z(c8q@Dl9XQ5uL@)>&p3KJ@_D4K8zn@ zKV9+MHcwC69d<6T)^&Y})1>DWpIVb|!@gJ&?%bv;_#CQ@RF7xw6$m==?puCWJN(s= z4`F^$LLm73!?RBSnI;R)8dA#JOqzDeYOa}Jz+82A8X;)=5&RVKPf2I@l}eQp@Mj+M zMI|ZQlRkb*oE3a&0*Jlf-D^wlRZ1AJ-i4$sMu9Q9@@Y(5PG=H)%i$K+bW0YSwY{@M zY3(CO1|iI?6z(;hg+?XsG_KQep5*}rbw<~fxOYWR0}S5@0f%n9G85UybSL<@40`<} z6U1a|j#o7=A;R_-SMt{uWXK}KS-$Np&YzWmGQZ@qv-cj#YL>I{Uow?z@d#bCMd2OS z!O^N@0A@m$s}if_`t#sYxg5AFfi}c;?$&8*t08dI<<6(eUbtLNGMsnsSfBh>EEq)c z-~g;#=SO=sTle4%CW;$GC8LF2Sf+|oQXd6$FwM!jQkCRBwr~`SG_?$%;1Laui(Tg> z^w*yV%Poo?)~a3kSVksNr`RODmS%DBd0vam_?6Uo*?O)Cpi?;zM^PI=sdFwEl`Iy& zTc>%kKQ7u3JrJ3fvF_oukY2@3huY{5KfDiYCLUpv{LaHW;3mw*$JEwhaP6^CYTtc_1y<=IG@viG;u zjBhNJc2cfq({(sz&2=93>(OJA(?06$j&-`vtH#ZQhKPM<|7*K;JXHE<8chJS7vVjw&G!5BpL?YMkmPnA3O!vMuh~^3=C@dc;*O z%D2ynlsd{#CEY*Ng;0A#BOG^l`k!A)A*@|;YT!OJl+DOaO!@Hv;&y_a-Mne{f4&F3 zex!g(%J2U&bd%?YrMMS3mqY~| zXb)^fSrt@DdPLzVhfPhTi@6udRkM8O3=X>M7P?!~oRZ=Dz2LI%Fh|;9*bX3~P`%keOiD3TP6~!!2ag%9>yOhyIYVzDc4^VN zPGYg@9HC_}pGosW%@JgdJ`Xxk>? z1ezw;@(gJcp-Q$Mt2C6iC@9|?iR4;x@XFNnXgE>vIhT=U+t(|#y?X9Fgk=ARShnfK z{*FE9SQuv@o@bw;t5IXy$XP`hjYtZvJD9)nTNJBujX@>jwfv(yMuq*WjS=|UQY``f z+e{x%iaWR0$A@~>c|dZWM6fG+@tQM@;uOs!tdab{+S(1aGo}S*uJ04A62U1=EtE9kDRf#OubSDh@fW5wKw0DT(S#ddLa|M1rnn7E!O7iQ<3fE!uO zwg9vLRtNCc8-$uR1h|vW=ZbuH`m9lT^e>k;^{y$HR}R_V#3DP(fAs7ENw&H_Jw%tk zUz(>s>bK&moe>^)q_SOpHe>U{ojyc*pYtxU3w{1EIOc?S9kTXKweQfcubTUiu?soF z(At%o6NK-f{NVZ26Z+Cz|4p~&vw`!<;R9l#N07y9^2?UR72A-@pAGqOHhS|2F8CO5 z4X%BJ2(pa#8gkk6D5ynYku6hoc||wOW^h}Cz|GzT4PVf?g5i5Nz_$==C9-6Z#$C&7 zhVCwTyzPQTjRg3-aq{x^zLAZ-4m&O*1bXG67cLF#wNmF^TaI4v%Cwm*wRzX%zQaP! zA>QrOpAlaM^KP4{4Qh-dxZzm49(I{{^s9~DUoJvHYHVdO9qHC|^k}naYE$uSD(+H# zpBb#xrsG4^>N#vO8ftX+!3*&1z=A2{^&!TQLY;(Nqp=*>)V1}>M}VE8dsbCCzUvLT z0$`~dQGEju{@Jv~7#MvwzXPuEgT@X1Y$BS5KIU#G&i#IO%@}k= zP48TpGPyUxn=I?Cdo6C{jv%d~TYIjpw||}XW_H?^pi>-K3OteBVMi$KQj7w4 zn8Xz;_6Tk}WSa55q+|dV)5`$V@rzy2&#Cv(95xtvH9$N0NZ1H}gnbWMHuI@ktF7g z@sh-s`!eRP^Zz6>m7C#1gnAdc&6IFYDHeJZ1qFP9_9Q9rQl1$r@Tv?Ydw~WuE3vty-&xllk<2^xI~Euz`qvF3L& z)EYGnp0*7NMM5Nc{&vIjkY_D%Q+@39PBp`D<+&4HOr=x$db6_p${=`8H1}DkwdGAV z)k2MCh*OPnp%;1_vQ>ILyYCZtwuli{M~xzlr;Y@{rXw38flFq965V7hzzQ9V-)8Y< zgeRL8q9%^8{zb-2dSVzCMOu?=7@-5%hZp6i#Qg!;92}Y{C6H|lT$sCif{7R0L+nO{ zSJx@XN3dsyPM?)8Pmd*BtAGinmHSofgl`ychbxox8`JoAT4XEd=@QuD zTbHrD^p7aGuL#veZz+=TWZOsHBu|j2uoinvIN@}1Tz4c3kRl$^+Q;%k%@RlTvQ z^IuO)Ne1bVUf13=lRu!IBCQ9r1nC?%y>0~0`sf|+p}3b{Bcf-?if8J)Z8L9QU8xmk z5C9hIqCxju=$`a$2U=4v4i79*`)$Flw}DyFYJByRn|dG9_04dG0^l|yRy_vnq~V9s z6(~o=fJB6v20De;FyaceS(q5Wz$`6tKs2n)D0od~1WI3i5|6_|eqN*zu6Xv2y?h&Q)64SlaN>>dn{1>s<=2jr{t3wZV%hAPtEDGT&C4$L$fp^(lXN6+LL}Y+6+tc6{FLN{&VeA-ECW{Ef0Fj`>k2=yZ*g+bntU6zNr+}VJF%`0;Y zOmz82aq9h(|AT{^tocNogKEGb_Jv36#F6CZkn55|<~{#LcAoUU$MOP8JPQs{R(1Qm z(T8Y{?p)bzmDB`A{aAJyMAS_zNwlWW~;%1hW3GfoFu_VG;0eK=ZhJ~qliMKXB9-i4T zj$}cEkFfz!T4wrmiwonem3@uQfHFwtri-jrWnyVu_2nDe7tsi3G^) zFgAO8+TWC|D6`4+F_F^F)PHlEIf`q#V`_Mcyyh%8oGx$MJiUvusah%J#rN3Ld{TAK zZWcN!osJ;L@_+C|P3H()DE6&e`t>aMhS?!{bJ0Pv$0E7^j+frL-l(OCmFvVp|5!Q2 z5avSYCg)Hc{pe+$aot|~j`LVvvqu+Np6%qe$erh){O1h9xa+0EgOjE!cyeVp`Jow% z=cv2WLBV`%*lLK&9NmP2V`xDWmE7B^CH#X3q`bgaku;AD3C*Ofs1i6YLDhn3XStf1ffFPl(zfk zk9@t)g#8hpWi%z}YzZK*L-W>)bGga5n%C@nyk~aMR!U-poy6F?ZqhQ>z{9sM-(;F0i)9J0u0H_x-+($potK{ST z4ptwXuP*pOt}7#E?vHFxIduCMNz>$jA0i5_(5XV1n2AOm2SU@NspnMnXEe%RS=7bH zJc!)(oBAWVaH@_G0*0vOi=D0X?S214Wl{ zqmQxMPN}UAz>|iWqq@Y|I$hStB}+zwOJp+;NRG`qn7ph8J+`h9Nn$STKDwhQd3FX@ zD19(XH^Yk%fG(j&l#ybxv($z~GT)h&vNepszxf`tqVsrXOUohnRiHCZ`%J=n{#MiR zMkGyfOIGoiS;U{qZbh13RTE^Zoz?9cHs|5=s9;85K8K+_N3`yNPc}Km%6L0w*%ii#W6K9uSwv_apxQR8Y-4?%wl0>fj z=FswP@eYogR6CR34oa)^9%8iJG}<~snr$%IKV%kNTy9SwB7Eihs^msu6ZL-@vkVwv zQS|)Hl`8&{uxSF%%gTAk%DBc=lwl&-Wx2(HWb2*Dk?4*$(kSvIV#8!ORX#~ko2otp zxW~+rjh?>vS{vN5#4(3&`!<;E=o(Jf%NCFxU~q143Bw*9Vffd(uUnalYcP_JBwIC< z&N6<*RYCZ~^(byCMKA23jQJK?r6SxKr?dH6WrBpV+Ti=5V{xmzgY+fAmy&TVt4K>` z;h(hi=*{pdvjiId&1MBymM&ON|FR021h!3^Ux=&s*Q#|A6_+^Uf3}9q5uK&?X7wl2 z_)7I5z`4B?rC8__r4m3o!_nvrANS;9gyxc_OOS`9B=pkR=ZN)jT7@5{#o7?MemtKq zcYZuCAhm(zsEk$#ur4h**A;Gl5U1lO6Lp%^mQWYjEjE&W7$onFc=nhWIUmqml-tTW zUBmO_$|pR;{Z0o{Yl^S+VWE9b;&i%?CBSC)TduM|k%rG84>7qHH+T~YbgH!W&fm7W z&&wy=Zw>+H#d2Q#NYqB|bc;peM5F2dAlF%r7IklXMQ`L~oT-|mh{_SO+ghgJ3zf-eP@R1(8j2!m|$azPfXV{Xx*-i^hO}}1@XqQAXcn%SUt|fDklx5i; zsrRPG#Mr}j@O1VTwh1V0sya{JRj-y_Mp4qDdwlSwWVQA5Pp#$pN`O9V;AM!Mt^3n(NnhHcmCqko}xPu<;pKvq9xSA6! zH46R#3gzF&Nch6Wa$>k|Y79?jwv*$TOnnBw15tG@>Jh9C_|ECic{1^b}+$WtWWC zasKt9H}XweV}{Sh@=${oKd?<&^|@`;QlL7bxl3fH?@Y1uIS!V!w|u5O)Dk-|SKkmk zslY!x%ymc4@S1m&@2mqD0ry${r|EoC;nUu1!VY`0LfTy>ADqSr125vOAL=VVLt8eK z*~XDtO9tN@Jhh&(+O;4kHwCii>q$*d2>*F!t*hH!_rl^5jS>Pc#;=RY zCXSkjzDRfSYJ~do_EA;`o=maFD#-Mx^f}dhLmdQN5v#HhiSmB;PX*aT+*sA{F13zV zV4ftn6_tOSw_=+lR&AYGF_0|qsed{!iOZ@vt_f_K@iNYSxW^ipTUpTPVt;#HrXD5d zCKS9*JW^m?g69dxu%_ZHW396}BtdA8G%q`v_1r|F zyj!H9#1%eV#FF3ET3t8FnHl$!hhx>o zR^T$f;x6a7norfk+rvsY`WhNJ7Y*`a`kK1jVS>;Af^)W|c!Kw%PS70nBKlg*ZK%JT z+h9wG(uo+(%S8W%GT-x(GVpD1irhJ`QUcIF5{yz7fk7*cUyf$=v1#Kt|HK}OS24?l z!7Osiya%Vv0e_G-Fv)$_D|0__&f+Yi8XKx5T2n_kaW$nAX6tO9>}8^AFZ9V6XpjE5 z&l_g!Tx2DfIo@H^*5i0D39G|Wt3(t zYKn@B62QTm1Crn$Et|Qhah&^LJMVDov@~<>G&5uO_#`U6ar0VX4l8dtdj|w&+e%fc=jt$ShuN1J|f+idX1& zAn)_kFx_gQW~FURp2Z+Iv|=b`&mUb-c|EU44zb7CVZ1c2D2l;-B{_XB60Ti-!Lj|3 zIy*L@1?j_|A^Qe?dJ-5E#dN1sJivVXA)CPX{c%Utx-ve*_k{-}$0KUt0aD*Dmczs3 zZjhdUn>@$35$D(frd37V$E#@Athl`sDNed5*=nIzSw_e9B*A+Sh);fUo)_4{4dmP7 zpXE<~ebm7s&plE0r2HqHF>ju2hk(sF_{dGPkID6e=|<~w_{cz#Z_Nx%9~V?p#pXE z6}7>Pp^zXO{fD%w=Mav$njVAQ=`^~7zT-BoLdAy-^ZSmiiowYz<5ZMn{@1~+>fPo^ zg14qi;r2_m0-ptdr+rH2rwH1Bi(@-V)13FMS<#2khFo-E$87EkJXiG-E%UFMyyZ@u zJmAx@7h2UR8yP9pq1fq)`o@O-b->OG2X^Nyd~|hmCl{fl%E1^V7b63MGR>6Qb4+Kr zs~I2(-;J%n6>j=t+VyXj3-6=1m{C}>J1&Hew9RD)(ozrN2A|(isNh^H z)oHlmI^VI!T+Bp#DAN`#-V5HwUIdnfwepR9uiK+%71GT^C5Z8TN2imnsCEv3x|ZO&W`3Cc^ZGtfA$|vh1J7VN<$E%Z!Do z`Ay)l#N{V^dSgGGSpx&gJ<*rhzA6{aL$){ua4a9_AV&Nh^_u$4XFvo$VwAj=Abcbr zGXntcjSKN=?uKT8FU$cC=MH9Q3**k9hEqgwj9@Q`Ql?~o=Y|`#Z|CD-;6u){lA%$sep$p*GwM<=XB#{HuXXAcKkn;g%a zN0wT{Z;EKwrAKVeD;+Z{bZcb~&ny$PM`##LSD;;{Ma zZw)I}WeS+hHUf_~|AtmDJBy9_SSz2`P(C1^)evippHSleB5Nr3>nj^oacc7>yqx5j z2>ly3&F;T>7I%$AD{VCYr)+WUtRxDmqrwtLVEg)?&Rz|aWocpF*4d`oM-)!4ZIto) z$+Kg4g4zOs-tR-D8IqAtyBGtL{LQTn4?|u2lQ zH)1OK^7*F@!@bCj@zBjbMQ{YYt^SKb^>xX^n}EmmSC8gn)=9qFj&YQ?N99j+x;u_J4Qkl`75)~A`b ztS(Iz(YOZv$|;;CLX;njRpfShlJ1ksVK6Je?lbG#(jL=x)RlRxTTA1Y5vDXXFx*-o zyp;W(;tM9SUqf}{O*Ed^5?dW(*xK?Y*nTp-0M)^aodkG^3ECv<{sO>o;B=1aJ}W`I z(=6YIZ0F+_1N(m+T~uydYLKd};InLc_Meh*N;+JgxfTFMlgd??jUFO@EhTWv2iliy zYGoP(2<)$e}{XO7=4BRX4q8$%}p$A9+!8xr>|4*Gusao_ET|1L!RKI0!~&FEWa z{k{8-#_1nx{C}ve1^+=Th1LE+vj6$vf3a}i&^67!h-(gxZ<_6YlW@%6$lU*lgk%1f zoAf^k%YP}H{tF-d|8_zC7xnhtG|BOA)SFmnz3Q6v-Y}Bz?cZqn`(-H=+{Z-X7>p{> z%?EwjZZ&cITDNJj`oexfxY*5=kHGg|pR`+X4y|dm#2zuA;(BV9tYzOH8RbmxyN!yh z&8K)9jCi&s_tN6A>wPhDS#CRMad+6gU+;pyj7;Q&%6 z9rR_Bc|@U}A%rHiGB5o3wikVdFURl(wR>q+riQ+A0AqOKsllN`+2(<`va-@+js%&3 zV>jg)4E5JF10Z-6*LZ8P>hP*7(`Pq|v&xRsDo+3|dwDz0W3Z+O61U)N9aQ#9udH`O zC7H&NeL45gCACCvn@?q}2#@{Dp~729{wm#q=yK>qz^H%Mk5Wmp3BNb~t(SCb`}jsu zBQ?0UAiuOLtI{s_dP-(meB42~_nL5cqTXJj9W~n=yf7{n?|@{+7;ly2{hj&Wj_P}C5}a6goCsRRSr7^II_W<1u|UA zp{>ILM!A6Tm6PRyinDkI3L!{f`57-$sHlRl?0Xy>j3-5ajtJf`^jRr@{Pz+*KkmUS zkVxIX@5k`p9QK~-gT<)M9Qd3uFj?jsJA!xwLi=f-R>NbNy%PDa_B&k!@~~Rk2YJVR z%$bs1kA?EIEY>>q@CTI#93-{MgV2Ec>lKVf$stcMc_Ob`1JIk(d)>m zsD$#+c=~uJc}9wVkJ;~2Z}3{em(ulTLCnNCq*baLCai^EY^RipgtOmr5m+hsd#AVj zSkz_&gsJuD6P%@5F`J}=E5YL2;Q3Fq9{!kMm(yn{`}cBz3>TZcW*qO2bSSmLNTZ(^j_yg zJenTY^oBJA)N+CDCp73u2!eIVby%bVAq>E*z+VxbgcBo@TevE;dqVZ`10BB7kQaes zbxmU-kt4`qM+2&=EpVLgTcvaj(G5@FiBa!?HAebG-yPTWXy3PR7gveK-51hCn?#Sr zx2e}frNJofEWWlbBMrDS5I90-|4ucUMdjziU`U88A&FUEtbw*{ooEyG9gubr2IFgP5Rp9vRj(NWkA)fxeUckC7G~WN7VN zH3z)jJrVa)6d~_2d2{ohB+2zpISjdRAc$Kk+UIqQjRS}T+PBQnHO9Hgc)TNjN!sBR z{-7NeU{43KVQE$wJZees`-MY;T-s~I4pQ|gwpkN~>ywvzm5icW z(h**}H<0&`X-|lYDUXlV@M?~Km*MI|IK)NvCdG+8|C6?GSD3Jtm#2r|M-ALzG*t2)jxbZtJ*-a4LT_4Py;887ZM*Y?|CS z?+VZao|;O}VnJmN+{6vi-kykgWh~ii(O4(P4>VcRR87>4i2uCl5jrHO;T#^F%@_1T zu-spBuBXs|Tcws#+t!8Rp1uMz?cx=lV9JnFll^0Cx8&7{{{*Tc?B=o!aaYJN4#Ht|Ci7#sl0%YC~`NXEitc8kHzbYTDhby zoZJgfrV7zSL?u!BQ1+j$O4|@{q5o;h@}DjJ+}z*ug#YPD{{PoN!NSbM{%?wRhbB;Q0081h~9`vy^e+NzQ9H0vW~wEqZ0B+V7_@jfBsxTL>7Y#SSKI`4T(X# zeByEyRBE@VP@7-G(P&s$T-><$yl`Ig3if_!`VwmUTH*QfVKm;e^;tdbW&uuTrGXZg3LWz#`GuaD(Y)s2gD-irg0*)Nyu(EnX<#Cg7PTot(S_}U zrXRxxmX^)O=^zq^V1t{jtJB0v!y}s3OvZoswR;)(DXBuO`&#?ddVb;*_|&?~^0iQ7 z#$flU>A+}YN0IsoT+%jIaAG!Vs;+fj_W4;BAOGRsL1y?0F=R9-ez3k#9Pp09^pXEz zG<&?V%L9Qb`p1jT(%$DwX6%9y);JP z6@Y*t0%{8kFD9n}RFHd*BoI<8+w6NPjZ*gT%J_P-m46X1L`2drz3{!>vN>+Ks&X42vfLW0{b!?bD z0=_$}fS>3tHxF+OSLW zP*B!)$SSmcxKfYS5p)<2w~W9HasXceygn1rXGNAHMZDD6#d(Kx@4NcW#AH*#m}^Mp z;?~0({qk=3eB6P@Dk{et`C2<-i z`Iot4+XUDb@K6#YT@OI%U^)EiDok+gZ-jQ^Wn@VnX+*rMRFyRPT+b* zoFBDSqsm2cqUCg6VIbk|2;|?IRt1X_Cur8SeY%sBSAp}?WZH)hnS(ZLeaG=B$$`JU zvZ-k>dn34BVd5VNqLYK|z4Uk(jTuY}TIIA0yd{3l?c(pXy>L1g{b5R4iM*pf}UvGy=*S?(;*@ie4tB`$U9+x zwRe6L>tO2`>=48H3A=EaW?d)EYD2d5JTZ#)m9QWY!xu5oAn`T{k|LFaZrym>yn2FW zT}wkQgEe5Wn?si!?v~NRMzSe0ALaLn!PjZxN$nr)?ZUad-Y7?AcU1-p(a=Q)D<4DpCv|s*ZZYKG=bFc^1TN40Uj`=p-cknC?m$hY zvV?dI=~`npo;4x~0m49DiK#%8OZ3Zz!BvDfBNFLH4eWq(iOL@Bdq;x>b}D^YJyu+# zEKH}rQY|yxUR?t;u#HsZABLG7jrE^T)jq~7pi3Sgy)s+&MZnSCnVB9^K7krX1Y)potre zQ*eO*+vChr%|{!aswJh{GFPPEWNuygIz6ac()Ozv+2phs%AYA?A%!i{V*l-Xf|_<_ zHVL(C6vk**4Pw3H@wmP#SWs3_+8zlW#G-FblCjTq^?5$*s0p` z6HQ0cwLd|k=t84Hd4?SN1CD9e$B^xK(kU^6^1#wu4r=PeYLJupfJ(N(hGtSm52>-L zzA-<3m;2_PJ9}!#`s;Cujir6d6$u;vW}&M7=@>!z8A*|62?=$ShZIj*OVIb2jh3!$ zWhF>Z)WD$~RbTlTSka#$Qwe<^V}H&R zU!w&JUC+I#ve56`oR-VHc;l}I$@}uYit5!XSc})p89BXcJ|P6HWE=wvo9qf&az+{E*LCa#q*j-L9#%9n(vW=s1zKvn*6lh!qEBkC-bTbyiElg#6nk3IAzWW_ZsU!H!zMmJZ{rALyFS zQDTHKP$5#4BW=oGdhr0j)W6<6fk))iD-Xnst$=1U9uX@f*}MKFyEPHcY&Xm_L*VwF zqWM5t-()08trZeyQcSjxQ8bVy;(UrIq`XA#i471~Qtf{#r_)NH_Q1N}uOp3mQaJM+JfZkv=-kk%l%$h zV-Q(C13o7QWm#ui^$NWbgz3HKf+f5mk_L>;-*Msl-NyoXzY5oS$Qq85Qj#0l-qS7V z;Yn4MFdZ_EY6*fY{;Seagj}cH3+CXPslBSk-r{;oq;K~ks!=NV+2#qp(?X?fLZ1O zsSaXTWgw)ILMcLE3QOp%o6?_`bTYfAy%+gSu&6idVsr2aIys0llQ;eP2LhIW&#n@C zP}_&{0qNzs_^@sG}5`&RmEVTMWRuG6w*GK4fY z#T1-TH=+MLL}pz?IG9zZqHbyBEVU6C_H+8V*u!OnXhizAR((~zvZ;*T(i-2_^@YnN zv_|`Pw}r`-GruzkfTnq((@k}H>{Swf&&D;$pv?(OtUY#gDLI)54PKIEC1XbkW5xrT z#?JUQe^$^f6IF_Rh=yX9^^kMD`%iST^Y4)0Y)8lL*0%(QZ1EveXlnI>BIb^=nxe`| z8J1@Bm-Am#pE3M&g9GG6z@3%B7Z6%-yay(2SKWEx;x3^O{d_Bi&bh?j>1lZIB!b+w z`(k(u^U%breuF;rF)PLT$VP1n@gwJ@?-Dc})Wj52DX<(JXwUR#lVrLxzw(6^%w|`n zPxCTqt z`{vEx66A^Px)R|MsJCj!e@d+IxE>5|i6E#J4$kUdFcfBx$bJsC9bf1xGPi>VBH*@K zvf_&v{?$W zuMk{Th;YJ4JUWPPDbTZ^;D9zy@)nn(l#R+(_ME zVYEF_0wfDgFeDvWs{q}EPfb_&UWF3G{GeSyjxQZ9u7AC)9Ub*2p*^%{Hfq!lAXDkm zLQ@^~&7xr&O|4h{%N{pgbI$F2IlK~$Fx_A8tTNSM%|hN5&uFv)1r<4ZXzgOMp;*TJ z$!gtq#n0z2sCea@0P?;}?M9}_Nft3{9`?kLC?mG`ORE{o20!m> zlJ+yR2U$5HnO~M+(V+1xv4CbPB@u&^u&9R{^V4SrU?xMce{NwdX6xYIwpZB zk{Ras*3Bo<)o#@xNsQECaK`>6G-z}_QFJ@eXdWbHzv2Iif16Y8B-^cN^K*uCk}u^< zZw~~+O>Gmi$&ywBH>-Dp3nEs>s;|kS5yO&d+cvngxmzViM;XWg<_f}YW4I@rknlwB z>;eEm5j+={^#M#d>Kf{?46pWZeY2LAJ9(xH$fLw-)v9cu*c8z|DOioOeO?cBAbT_Q zzD~ zoQSgSSYo1Ean$4uMWYM7P5PD#I&swctWdA_qBl*qKAZvuOm9T|!i&t{-m>r7H!0^c zSqHflJL`g<^@dKU$4w+Mqg>sYL;^t#ot>>E_mGkaSd%Om2Y(N2=d-vp4v4zC&9U46$@;GH6p5ZycJ;1qmymH?Yk?u}rVRMp0baJZ*` ziF6t?4y)TqzecC-%(}ffqw-rKHnjh=@bXhmNGUfCYYs*?`K^zt3W6h37mxEifsorn zqDN7$9H)V*pyY;`RLADEv~7OnX+3l37ChicJFUlT;U#%}%!Y9pnRN(0pir-iN5T^5 zy2Rm+=+uV?^~6c(CoRjlb|4Rfgh0|6^YdzdP*k-8H|za*NY;SL3tS!e21+W+8}ePt z|E(%KAWTh-VQn`S?Q*W zVbGdj3udY%4MF{Wu7@YmJbys1t5nk1|o^$xq z^?2iLCS8IXw!VG2K_?MGUq)ehJmCqIDyhTM6TFmW@;_1v z!j@i=4;x4wO)GFcRod^~e^I*;-?9uzHXq{~+NX2r?&mb5HR2g)2nZ>~OXiDC;56!H z^r=}xtamNsaf}s=AP(XvtiYU#;c<5)=&NAYsH8)bWu5M%pZl@?3e6sL*L#n?0~%(BPx)N1tjqq^7C< zHdPyWOUtI2L1*Ard+EdF@K)}@BWlC7Tm?y@xDUpPOB|Pl!yEoqz!7kp#F{OkSffA< zb?|t@AVQX@*^6_^nKLU#Tkpy^TvBRwUuk^*&z{9P7-F`ua>^&zt(=oe+}B)INy(8>*VKU^`(18dOfhqORqaPxH=7hsI=wY^6-g<7?fBEj^K2f zOUBiemg8@}W#PB4IW`wb8V;<@ut+Ie%B#wuO|I@iVCB6YW2wRBMh`nHTX>eTii(rL zVLrv=UbWwYe|u4$bbh5Y4C^l`g4d0heizOilN>nZwH1Nt?7a)7P*W8WAFt)DZCyS8bop~XNDNe#%ISQxQ+N2|LIgpGOW0K0~TQW)0a7GiPmF7BE zv4d=xjg*apMZhZ{3t3ERsvPWT)aN(m>3CLv24uwJdfYR!ZVM!wAD#$$gnhO0)1r|gz8TQ zaqFdLNJ9ghU-^5*VO44zNE%0LcPUTX9hNLAq#hdkOs#NJ@$qP-3th+#&6H7*lH!+= z1V$1iI0V`AA;u5rky=+=Qx$j;s=C z{g7Nbl=&!4gfU)h&(pT?wc;`?{x)3{>+wbf@el^1mu=T7f2=$dHvNqU`FLw9aIT=% ztuUpP~au+Wz2e_EOSCrI@_Ypnl$5%xbSy8jke z@@;GWSB3e%g>s3@%KR_KwStY+Z|i?oTuc8p_}4LEYXciYGi#H7b6o$ESo9y?f1K3+ zY~M*#tlzfifAe1dry2VHCf(?tbN|`?<0}78YtJ4J)%HCVl}Du{m(qm9EzCJ{&Y3d` zc@s)ek{*T`nY=PHUdeUodL`eKPJZmN+Y$!sjdk8{kPH z#pf$5oPei;rP$M9JVOUr1POTK0UV*9K-m){uO}s)NAUlEDIz}L7(;Y?+&o(z5hEIe zELuqeVWA}S4kype7PN=dhvb=BTfR?+~SM=6C7ZYd*e z|F4uf?Iv0&TCVyvEU3SFqLxhm_NlRUW!6vapg#4uw75Rn?g=wg3+B*gDCsLtz)cq>8#tE- zan7{uHJYHJ-7>pI-TL`c;fltbF7LpTq2X%&3L(q1^71d0WtnV#T;*7pxPH&0HxW87 zlfER$dByGlZB}Ibf zc6w>6!D7hczJ1%`=7-Y?n(G_mp5__MUphs9wfTH*q$74WRr^(FP*3Qlk2}vBxnGpq zt6O@-Wr$I?wADKqB)YbEe=PF2sovIYclT$OzFz*(`Ow`v5AIB|^RC(1l6G&`z5bxV z^KMxRhpQ7JLY^Hyt$HZ>Xv|7u^Mwt?Nt|_=COOSEg=yh|YeQv^^gq?8@zSra5O$UX zo#suS^hX!6+WC~Dt7~qmqGMW{TZXmO5!FVuNZP`}?Hc;fH?4iH5fM$VuQlm zbX+zoVZW87#m67D^{ywi9j{EapPS?tuVQ%KoRRGk(wQ9X5-{KGBikj$xM3LX9o&84 z?8d057AWSGNks1sNRX;LjfrUu*?zUEawyjAJYjQbWA!azSbI*5+HBcV=kB2!Cz}4c zpv|;)g-3JEoc5RHmZ6(U9{ZHmu`9FiiNb-(fD4Ar`@+7t?}(eXY`E8?$#Wj|_N$tg zj9Ylh_oBix#z#4`;&a);U_MRX&cWG*<`OIry87^W+l4e=UI3T2Js@-|FNn5|1{r}E zXx@=D7p{=Yr#bTYK7L!d0znX$4}81actTKCpBKp9%7u)$+XVpT4CIHy7P7$#N-Sx`0NU*xuX#0S)ZWV;}(y?0n*@5&5zLZ3hH{EuL@<6NgkI^&)mp zlgRsbkANl18^#c?+@tZ>?UVD|8~7qjwS92$g-Q8(yP4HCYTX>z`P8Q7a$Us@xq%nq zjQgbzoJ74{Mo;bwjAKSa?Rc{&ay#DOgCzbH0nk7bO8n15KAEKCA z_5Q(M1sdhs8w=EHJ3Bu+9n|(Ibvlvlr_*pCr$haPHs|Vn58Fcr-`3Z^cREtlc;8v= z`iqN>OE&(rqc~$h{5qS4nryjOh8NTNW;q7#WAO?~GFG};MbBHGvu4}dQ>*i1E&lzI z3-KP#?mgh_Li5&`AcZyHzBtFY6UE%5GXeMtJ z(v>=3W6Q1IApgR;s*CetTgm=M?-CE+_!M}!N@-12ZOrfCbIfaYciNOV9nB88|I;yt zZ2vu$Uw$b5x_ZLO`X{Kv!YLXNd5#L!nXNbOU)hlp+nU>CQJ*{u}FpQ6(^294D6roUq*#ZWWa{ zp}+H|*JYa*KU+)hF#1t7=ku?^UH+vVlan85Hmy+nGgWtD4>ulUT^2p<;fkJZmFd~- zxaL`0S#QhE_x*!LDLeCO(V(()bkF1a%8{=uGz@>s@b-lD(iUK*mrmYH*g3slTlD9b zOpOAID)*h}CLTUBgAseUnR7Pvdec=cj@+!rW+_|VZCm=>7Hg~X748j0x;$TmW^psE z%tAdX_wxfoPFJ+msI>K`edCJKwrKcBk%Eb;-Fr& z7g_lo=$}zIe^HOB&f>q;-dbBtw+_3St*I?PPkyPk_n$ewi4%LPRnI{MCP&qupRoUm zrmEaF)^s&ZRMvILnp8>mn|gcIqA$pBSqH6;#%kf z+3w>9qmFOta9VrfKrXiBb=+AcU14_2x-`>kGr7wYU^D^>yV6}47^~wyoLG4#An8XP z533U2Nm0xFDi8eeN;G?UzOAmgz15!hG)JYyGwWnjeo>13xg|-|(EYODovc#W%lo(_?&&?xQ_icTIVjc~vZaS<49lK1 zRo2^WnX(=~?wNPaT2?nXv@&kOO?;Z+X`gV__@DKy2f_bh1r73pAM09Z#=MzRlp0O! zEGse_<;xz3yhXfb=i@EO${+zt#mWTMkTVo{WQzO87@wlB<8MwI>=_y|9H`Fko8Z!* zVW#yiZbF^RC;MjZl|Qua>Kgy%sja1Mm5>!F^B}KHIP|F}VSjb}iZ$*R>;!R|Gt~z8 zP0k&`?w-lNc>d9k_-~ARa@uY$U~X-O`J1Ub5#e7lHHr2++RPG6nyN2r4S^idi zbBRz({qN!S2)~7#9D^S}Y|iypy7s8xP~~JjgWJa}Y6m~_YjYGllwX|mpwYQ!ewZ#Q zzPj1G(yW}D`FqRG+c!@f3-2k5e4rKMZoR4EY>4^lP`#^WGmZwI%24`ON8Zi6gYpis z1Mzngf=4)+AIf`iaebu9>70?KJS{bMesKoP(~HzUKeFrNg>4DZJIB zc1mo2{io2Z7}IU7?H9^FRAeqz*f;rdRZTMTILBlnrWE*<@e({Ras7ESEx zOf&#ARtOev(ZmJl5t1|w5nW2!ffyj7wWK-(>`ie#6ru`BL&mCuei>thm^56{HE0tS zBM;O7nz0K%*vFMC1d6t?i?uCKAApBAVPx4v2<=>jY#~=XdOKGLSe*p!gzhj-4@abY zy8yAk=FSDy?kL{>xT6R1f;gnneGRC(s_a0|K^Pjh%4oeZU`YpVC?_d zC*8skSZb~wXVFJpN zU`&GS(l7>{_`?7R2BTp9NiYnfw1?1%ltt3z5Qt8}1*2n>_ArFt4C%TU`kitt@Qj62 zjtLXbnxyM8Q9}5nVK_vgGen1&)kVmP0AqJB|0{|mb zYw2>|p%sLpgla8a4#f$fm4;z-!Z0Ag5JGNAFeYU#A&do93sN~Iok`|N2uFw@`BLq{ zC?Gxs#-i{fj6qCFU5r8D7Z@WXgVYC;j*@8rGXXY~attbr#h}O$%tAmOE$KcObcjMz z1|0_JwaDdAI(Zxj14YU71AeHKIY$sWvAz~ac>A~OgMQ{)Z7nH2gVIE%s?2n!*er%LsUg8%f8=>lLB9zr3APOXc; zHKA)HC4>)=ayaDt#I<+o~PF|aUHYwKo zq-nqc119&u!f=utfx2MvB$b1~5=+JzrsGr?gpy^I*e)sKfMJA!GYn&7`GsI0T9ken zU}GiYhtkPw7$5-Hq{!tk$~q1)z<~j|Jq8MsWd@XE09{X-uNWAMLQ@74Cr%)wXwLx5 zOQtCU%o}+=7%YT}3yZQoLts56ue$(-A!Hr`*$5eA8lX&wtRq1vlSNnqQaGbHox&R^ z5Ps4+1OXUX2LWP4C(b>j+XIJaWEucXl)T0Q(V|mmg+Yw*x{8p`24(?YS!_!<1o;Mo zW2_NF$iW&|9?KZsh%eU{hJ1l$<;y^DHk*smy-{B#hlBWXFvN_ujK${oLO9DCWde#L zT)Gb(Xv!SS8_bipw=YaLBQ$z<&=>?x3?Q+3&)dNzH1)9q0g&sFX1pdiSO^RwFp-4G zCeAPkDgmaz#b-;_TyR>+bz$=Z1!82af&)SWg9Jp5P8U9x!}B2?&qA=+bo%ad%#jD8 f>(>A~0hmbwA)7B08x4R=K>bw)e?wShtDyXUtg7GA literal 0 HcmV?d00001 diff --git a/docs/LowRateDecoder.pdf b/docs/LowRateDecoder.pdf new file mode 100644 index 0000000000000000000000000000000000000000..93ba65ecb4c1ac0d419bb9b8a7842733c829e26d GIT binary patch literal 349855 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

nfi!OZLztgnctZnpp^yTUAKYP}E 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

IqoA)NhkQ;Md&# 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 G=l>V_9L;+G literal 0 HcmV?d00001 diff --git a/leopard.cpp b/leopard.cpp index 5c694fd..51850f9 100644 --- a/leopard.cpp +++ b/leopard.cpp @@ -27,8 +27,8 @@ */ #include "leopard.h" -#include "FecalEncoder.h" -#include "FecalDecoder.h" +#include "LeopardFF8.h" +#include "LeopardFF16.h" extern "C" { @@ -38,134 +38,152 @@ extern "C" { static bool m_Initialized = false; -FECAL_EXPORT int fecal_init_(int version) +LEO_EXPORT int leo_init_(int version) { - if (version != FECAL_VERSION) - return Fecal_InvalidInput; + if (version != LEO_VERSION) + return Leopard_InvalidInput; - if (0 != gf256_init()) - return Fecal_Platform; + if (!leopard::ff8::Initialize()) + return Leopard_Platform; + + if (!leopard::ff16::Initialize()) + return Leopard_Platform; m_Initialized = true; - return Fecal_Success; + return Leopard_Success; } //------------------------------------------------------------------------------ // Encoder API -FECAL_EXPORT FecalEncoder fecal_encoder_create(unsigned input_count, void* const * const input_data, uint64_t total_bytes) +LEO_EXPORT unsigned leo_encode_work_count( + unsigned original_count, + unsigned recovery_count) { - if (input_count <= 0 || !input_data || total_bytes < input_count) - { - FECAL_DEBUG_BREAK; // Invalid input - return nullptr; - } - - FECAL_DEBUG_ASSERT(m_Initialized); // Must call fecal_init() first - if (!m_Initialized) - return nullptr; - - fecal::Encoder* encoder = new(std::nothrow) fecal::Encoder; - if (!encoder) - { - FECAL_DEBUG_BREAK; // Out of memory - return nullptr; - } - - if (Fecal_Success != encoder->Initialize(input_count, input_data, total_bytes)) - { - delete encoder; - return nullptr; - } - - return reinterpret_cast( encoder ); + return leopard::NextPow2(recovery_count) * 2; } -FECAL_EXPORT int fecal_encode(FecalEncoder encoder_v, FecalSymbol* symbol) +LEO_EXPORT LeopardResult leo_encode( + uint64_t buffer_bytes, // Number of bytes in each data buffer + unsigned original_count, // Number of original_data[] buffer pointers + unsigned recovery_count, // Number of recovery_data[] buffer pointers + unsigned work_count, // Number of work_data[] buffer pointers, from leo_encode_work_count() + void* const * const original_data, // Array of pointers to original data buffers + void** work_data, // Array of work buffers + unsigned flags) // Operation flags { - fecal::Encoder* encoder = reinterpret_cast( encoder_v ); - if (!encoder || !symbol) - return Fecal_InvalidInput; + if (buffer_bytes <= 0 || buffer_bytes % 64 != 0) + return Leopard_InvalidSize; - return encoder->Encode(*symbol); -} + if (recovery_count <= 0 || recovery_count > original_count) + return Leopard_InvalidCounts; -FECAL_EXPORT void fecal_free(void* codec_v) -{ - if (codec_v) + if (!original_data || !work_data) + return Leopard_InvalidInput; + + const unsigned m = leopard::NextPow2(recovery_count); + const unsigned n = leopard::NextPow2(m + original_count); + + if (work_count != m * 2) + return Leopard_InvalidCounts; + + const bool mt = (flags & LeopardFlags_Multithreaded) != 0; + + if (n <= leopard::ff8::kOrder) { - fecal::ICodec* icodec = reinterpret_cast( codec_v ); - delete icodec; + leopard::ff8::Encode( + buffer_bytes, + original_count, + recovery_count, + m, + original_data, + work_data); } + else if (n <= leopard::ff16::kOrder) + { + leopard::ff16::Encode( + buffer_bytes, + original_count, + recovery_count, + m, + original_data, + work_data); + } + else + return Leopard_TooMuchData; + + return Leopard_Success; } //------------------------------------------------------------------------------ // Decoder API -FECAL_EXPORT FecalDecoder fecal_decoder_create(unsigned input_count, uint64_t total_bytes) +LEO_EXPORT unsigned leo_decode_work_count( + unsigned original_count, + unsigned recovery_count) { - if (input_count <= 0 || total_bytes < input_count) + const unsigned m = leopard::NextPow2(recovery_count); + const unsigned n = leopard::NextPow2(m + original_count); + return n; +} + +LEO_EXPORT LeopardResult leo_decode( + uint64_t buffer_bytes, // Number of bytes in each data buffer + unsigned original_count, // Number of original_data[] buffer pointers + unsigned recovery_count, // Number of recovery_data[] buffer pointers + unsigned work_count, // Number of buffer pointers in work_data[] + void* const * const original_data, // Array of original data buffers + void* const * const recovery_data, // Array of recovery data buffers + void** work_data, // Array of work data buffers + unsigned flags) // Operation flags +{ + if (buffer_bytes <= 0 || buffer_bytes % 64 != 0) + return Leopard_InvalidSize; + + if (recovery_count <= 0 || recovery_count > original_count) + return Leopard_InvalidCounts; + + if (!original_data || !recovery_data || !work_data) + return Leopard_InvalidInput; + + const unsigned m = leopard::NextPow2(recovery_count); + const unsigned n = leopard::NextPow2(m + original_count); + + if (work_count != n) + return Leopard_InvalidCounts; + + const bool mt = (flags & LeopardFlags_Multithreaded) != 0; + + if (n <= leopard::ff8::kOrder) { - FECAL_DEBUG_BREAK; // Invalid input - return nullptr; + leopard::ff8::Decode( + buffer_bytes, + original_count, + recovery_count, + m, + n, + original_data, + recovery_data, + work_data); } - - FECAL_DEBUG_ASSERT(m_Initialized); // Must call fecal_init() first - if (!m_Initialized) - return nullptr; - - fecal::Decoder* decoder = new(std::nothrow) fecal::Decoder; - if (!decoder) + else if (n <= leopard::ff16::kOrder) { - FECAL_DEBUG_BREAK; // Out of memory - return nullptr; + leopard::ff16::Decode( + buffer_bytes, + original_count, + recovery_count, + m, + n, + original_data, + recovery_data, + work_data); } + else + return Leopard_TooMuchData; - if (Fecal_Success != decoder->Initialize(input_count, total_bytes)) - { - delete decoder; - return nullptr; - } - - return reinterpret_cast( decoder ); -} - -FECAL_EXPORT int fecal_decoder_add_original(FecalDecoder decoder_v, const FecalSymbol* symbol) -{ - fecal::Decoder* decoder = reinterpret_cast( decoder_v ); - if (!decoder || !symbol) - return Fecal_InvalidInput; - - return decoder->AddOriginal(*symbol); -} - -FECAL_EXPORT int fecal_decoder_add_recovery(FecalDecoder decoder_v, const FecalSymbol* symbol) -{ - fecal::Decoder* decoder = reinterpret_cast( decoder_v ); - if (!decoder || !symbol) - return Fecal_InvalidInput; - - return decoder->AddRecovery(*symbol); -} - -FECAL_EXPORT int fecal_decode(FecalDecoder decoder_v, RecoveredSymbols* symbols) -{ - fecal::Decoder* decoder = reinterpret_cast( decoder_v ); - if (!decoder || !symbols) - return Fecal_InvalidInput; - - return decoder->Decode(*symbols); -} - -FECAL_EXPORT int fecal_decoder_get(FecalDecoder decoder_v, unsigned input_index, FecalSymbol* symbol) -{ - fecal::Decoder* decoder = reinterpret_cast( decoder_v ); - if (!decoder || !symbol) - return Fecal_InvalidInput; - - return decoder->GetOriginal(input_index, *symbol); + return Leopard_Success; } diff --git a/leopard.h b/leopard.h index 8c0e85f..e8a6b4f 100644 --- a/leopard.h +++ b/leopard.h @@ -59,6 +59,7 @@ # endif #endif +#include #ifdef __cplusplus extern "C" { @@ -90,14 +91,13 @@ typedef enum LeopardResultT Leopard_Success = 0, // Operation succeeded Leopard_TooMuchData = -1, // Buffer counts are too high - Leopard_InvalidBlockSize = -2, // Buffer size must be a multiple of 64 bytes - Leopard_InvalidInput = -3, // A function parameter was invalid - Leopard_Platform = -4, // Platform is unsupported - Leopard_OutOfMemory = -5, // Out of memory error occurred - Leopard_Unexpected = -6, // Unexpected error - Software bug? + Leopard_InvalidSize = -2, // Buffer size must be a multiple of 64 bytes + Leopard_InvalidCounts = -3, // Invalid counts provided + Leopard_InvalidInput = -4, // A function parameter was invalid + Leopard_Platform = -5, // Platform is unsupported } LeopardResult; -// Results +// Flags typedef enum LeopardFlagsT { LeopardFlags_Defaults = 0, // Default settings @@ -119,7 +119,6 @@ typedef enum LeopardFlagsT Returns the work_count value to pass into leo_encode(). Returns 0 on invalid input. */ - LEO_EXPORT unsigned leo_encode_work_count( unsigned original_count, unsigned recovery_count); @@ -138,6 +137,8 @@ LEO_EXPORT unsigned leo_encode_work_count( flags: Flags for encoding e.g. LeopardFlag_Multithreaded The sum of original_count + recovery_count must not exceed 65536. + The recovery_count <= original_count. + The buffer_bytes must be a multiple of 64. Each buffer should have the same number of bytes. Even the last piece must be rounded up to the block size. @@ -153,15 +154,11 @@ LEO_EXPORT unsigned leo_encode_work_count( ((uint64_t)total_bytes + original_count - 1) / original_count); Returns Leopard_Success on success. - The first set of recovery_count buffers in work_data will be the result. - - Returns Leopard_TooMuchData if the data is too large. - Returns Leopard_InvalidBlockSize if the data is the wrong size. - Returns Leopard_InvalidInput on invalid input. + * The first set of recovery_count buffers in work_data will be the result. Returns other values on errors. */ LEO_EXPORT LeopardResult leo_encode( - unsigned buffer_bytes, // Number of bytes in each data buffer + uint64_t buffer_bytes, // Number of bytes in each data buffer unsigned original_count, // Number of original_data[] buffer pointers unsigned recovery_count, // Number of recovery_data[] buffer pointers unsigned work_count, // Number of work_data[] buffer pointers, from leo_encode_work_count() @@ -183,7 +180,6 @@ LEO_EXPORT LeopardResult leo_encode( Returns the work_count value to pass into leo_encode(). Returns 0 on invalid input. */ - LEO_EXPORT unsigned leo_decode_work_count( unsigned original_count, unsigned recovery_count); @@ -211,7 +207,7 @@ LEO_EXPORT unsigned leo_decode_work_count( Returns other values on errors. */ LEO_EXPORT LeopardResult leo_decode( - unsigned buffer_bytes, // Number of bytes in each data buffer + uint64_t buffer_bytes, // Number of bytes in each data buffer unsigned original_count, // Number of original_data[] buffer pointers unsigned recovery_count, // Number of recovery_data[] buffer pointers unsigned work_count, // Number of buffer pointers in work_data[] diff --git a/proj/Leopard.sln b/proj/Leopard.sln index bafad8e..daa9f58 100644 --- a/proj/Leopard.sln +++ b/proj/Leopard.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26127.3 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Leopard", "Leopard.vcxproj", "{32176592-2F30-4BD5-B645-EB11C8D3453E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LeopardBenchmark", "..\tests\proj\Benchmark.vcxproj", "{97FCA15F-EAF3-4F1A-AFF8-83E693DA9D45}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LeopardExperiments", "..\tests\proj\Experiments.vcxproj", "{97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -31,6 +33,14 @@ Global {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D45}.Release|Win32.Build.0 = Release|Win32 {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D45}.Release|x64.ActiveCfg = Release|x64 {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D45}.Release|x64.Build.0 = Release|x64 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Debug|Win32.ActiveCfg = Debug|Win32 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Debug|Win32.Build.0 = Debug|Win32 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Debug|x64.ActiveCfg = Debug|x64 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Debug|x64.Build.0 = Debug|x64 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Release|Win32.ActiveCfg = Release|Win32 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Release|Win32.Build.0 = Release|Win32 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Release|x64.ActiveCfg = Release|x64 + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/proj/Leopard.vcxproj b/proj/Leopard.vcxproj index da9a8ad..c5c69b5 100644 --- a/proj/Leopard.vcxproj +++ b/proj/Leopard.vcxproj @@ -21,16 +21,12 @@ - - - - @@ -38,34 +34,33 @@ {32176592-2F30-4BD5-B645-EB11C8D3453E} GF65536 Leopard - 10.0.14393.0 StaticLibrary true MultiByte - v141 + v140 StaticLibrary true MultiByte - v141 + v140 StaticLibrary false true MultiByte - v141 + v140 StaticLibrary false true MultiByte - v141 + v140 diff --git a/proj/Leopard.vcxproj.filters b/proj/Leopard.vcxproj.filters index 079edb1..df7d586 100644 --- a/proj/Leopard.vcxproj.filters +++ b/proj/Leopard.vcxproj.filters @@ -21,12 +21,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -35,12 +29,6 @@ - - Source Files - - - Source Files - Source Files diff --git a/tests/experiments.cpp b/tests/experiments.cpp new file mode 100644 index 0000000..f2c6a4e --- /dev/null +++ b/tests/experiments.cpp @@ -0,0 +1,615 @@ +/* + Copyright (c) 2017 Christopher A. Taylor. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of LHC-RS nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +// Debug + +// Some bugs only repro in release mode, so this can be helpful +//#define LEO_DEBUG_IN_RELEASE + +#if defined(_DEBUG) || defined(DEBUG) || defined(LEO_DEBUG_IN_RELEASE) + #define LEO_DEBUG + #ifdef _WIN32 + #define LEO_DEBUG_BREAK __debugbreak() + #else + #define LEO_DEBUG_BREAK __builtin_trap() + #endif + #define LEO_DEBUG_ASSERT(cond) { if (!(cond)) { LEO_DEBUG_BREAK; } } +#else + #define LEO_DEBUG_BREAK ; + #define LEO_DEBUG_ASSERT(cond) ; +#endif + + +//------------------------------------------------------------------------------ +// Platform/Architecture + +// Compiler-specific C++11 restrict keyword +#define LEO_RESTRICT __restrict + +// Compiler-specific force inline keyword +#ifdef _MSC_VER + #define LEO_FORCE_INLINE inline __forceinline +#else + #define LEO_FORCE_INLINE inline __attribute__((always_inline)) +#endif + + + + +//------------------------------------------------------------------------------ +// Field + +//#define LEO_SHORT_FIELD + +#ifdef LEO_SHORT_FIELD +typedef uint8_t ffe_t; +static const unsigned kGFBits = 8; +static const unsigned kGFPolynomial = 0x11D; +ffe_t kGFBasis[kGFBits] = { + 1, 214, 152, 146, 86, 200, 88, 230 // Cantor basis +}; +#else +typedef uint16_t ffe_t; +static const unsigned kGFBits = 16; +static const unsigned kGFPolynomial = 0x1002D; +ffe_t kGFBasis[kGFBits] = { + 0x0001, 0xACCA, 0x3C0E, 0x163E, // Cantor basis + 0xC582, 0xED2E, 0x914C, 0x4012, + 0x6C98, 0x10D8, 0x6A72, 0xB900, + 0xFDB8, 0xFB34, 0xFF38, 0x991E +}; +#endif + +/* + Cantor Basis introduced by: + D. G. Cantor, "On arithmetical algorithms over finite fields", + Journal of Combinatorial Theory, Series A, vol. 50, no. 2, pp. 285-300, 1989. +*/ + +static const unsigned kFieldSize = (unsigned)1 << kGFBits; //Field size +static const unsigned kFieldModulus = kFieldSize - 1; + +static ffe_t GFLog[kFieldSize]; +static ffe_t GFExp[kFieldSize]; + +// Initialize GFLog[], GFExp[] +static void InitField() +{ + unsigned state = 1; + for (unsigned i = 0; i < kFieldModulus; ++i) + { + GFExp[state] = static_cast(i); + state <<= 1; + if (state >= kFieldSize) + state ^= kGFPolynomial; + } + GFExp[0] = kFieldModulus; + + // Conversion to chosen basis: + + GFLog[0] = 0; + for (unsigned i = 0; i < kGFBits; ++i) + { + const ffe_t basis = kGFBasis[i]; + const unsigned width = (unsigned)(1UL << i); + + for (unsigned j = 0; j < width; ++j) + GFLog[j + width] = GFLog[j] ^ basis; + } + + for (unsigned i = 0; i < kFieldSize; ++i) + GFLog[i] = GFExp[GFLog[i]]; + + for (unsigned i = 0; i < kFieldSize; ++i) + GFExp[GFLog[i]] = i; + + GFExp[kFieldModulus] = GFExp[0]; +} + + +//------------------------------------------------------------------------------ +// Mod Q Field Operations +// +// Q is the maximum symbol value, e.g. 255 or 65535. + +// z = x + y (mod Q) +static inline ffe_t AddModQ(ffe_t a, ffe_t b) +{ + const unsigned sum = (unsigned)a + b; + + // Partial reduction step, allowing for Q to be returned + return static_cast(sum + (sum >> kGFBits)); +} + +// z = x - y (mod Q) +static inline ffe_t SubModQ(ffe_t a, ffe_t b) +{ + const unsigned dif = (unsigned)a - b; + + // Partial reduction step, allowing for Q to be returned + return static_cast(dif + (dif >> kGFBits)); +} + +// return a*GFExp[b] over GF(2^r) +static ffe_t mulE(ffe_t a, ffe_t b) +{ + if (a == 0) + return 0; + + const ffe_t sum = static_cast(AddModQ(GFLog[a], b)); + return GFExp[sum]; +} + + +//------------------------------------------------------------------------------ +// Fast Walsh-Hadamard Transform (FWHT) Mod Q +// +// Q is the maximum symbol value, e.g. 255 or 65535. + +// Define this to enable the optimized version of FWHT() +#define LEO_FWHT_OPTIMIZED + +typedef ffe_t fwht_t; + +// {a, b} = {a + b, a - b} (Mod Q) +static LEO_FORCE_INLINE void FWHT_2(fwht_t& LEO_RESTRICT a, fwht_t& LEO_RESTRICT b) +{ + const fwht_t sum = AddModQ(a, b); + const fwht_t dif = SubModQ(a, b); + a = sum; + b = dif; +} + +// Reference implementation +static void FWHT(fwht_t* data, const unsigned bits) +{ + const unsigned size = (unsigned)(1UL << bits); + for (unsigned width = 1; width < size; width <<= 1) + for (unsigned i = 0; i < size; i += (width << 1)) + for (unsigned j = i; j < (width + i); ++j) + FWHT_2(data[j], data[j + width]); +} + + +//------------------------------------------------------------------------------ +// Formal Derivative + +// Formal derivative of polynomial in the new basis +static void formal_derivative(ffe_t* cos, const unsigned size) +{ + /* + Left to right xoring data ahead into data behind. + + If the data ends in all zeroes, this can simply stop. + */ + for (unsigned i = 1; i < size; ++i) + { + const unsigned leng = ((i ^ (i - 1)) + 1) >> 1; + + // If a large number of values are being XORed: + for (unsigned j = i - leng; j < i; ++j) + cos[j] ^= cos[j + leng]; + } + + // Doesn't seem to be needed +#if 0 + /* + Same here - Zeroes on the right are preserved + */ + for (unsigned i = size; i < kFieldSize; i <<= 1) + { + for (unsigned j = 0; j < size; ++j) + cos[j] ^= cos[j + i]; + } +#endif +} + + +//------------------------------------------------------------------------------ +// Fast Fourier Transform + +static ffe_t skewVec[kFieldModulus]; // twisted factors used in FFT + +static LEO_FORCE_INLINE void ifft_butterfly(ffe_t& a, ffe_t& b, ffe_t skew) +{ + b ^= a; + a ^= mulE(b, skew); +} + +// IFFT in the proposed basis +static void IFLT(ffe_t* data, const unsigned size, const unsigned index) +{ + for (unsigned width = 1; width < size; width <<= 1) + { + for (unsigned j = width; j < size; j += (width << 1)) + { + const ffe_t skew = skewVec[j + index - 1]; + + if (skew != kFieldModulus) + { + for (unsigned i = j - width; i < j; ++i) + ifft_butterfly(data[i], data[i + width], skew); + } + else + { + for (unsigned i = j - width; i < j; ++i) + data[i + width] ^= data[i]; + } + } + } +} + +static LEO_FORCE_INLINE void fft_butterfly(ffe_t& a, ffe_t& b, ffe_t skew) +{ + a ^= mulE(b, skew); + b ^= a; +} + +// FFT in the proposed basis +static void FLT(ffe_t* data, const unsigned size, const unsigned skewIndex, const unsigned output_elements) +{ + for (unsigned width = (size >> 1); width > 0; width >>= 1) + { + const ffe_t* skewLUT = skewVec + width + skewIndex - 1; + + for (unsigned j = 0; j < output_elements; j += (width << 1)) + { + const ffe_t skew = skewLUT[j]; + + if (skew != kFieldModulus) + { + for (unsigned i = j; i < j + width; ++i) + fft_butterfly(data[i], data[i + width], skew); + } + else + { + for (unsigned i = j; i < j + width; ++i) + data[i + width] ^= data[i]; + } + } + } +} + + +//------------------------------------------------------------------------------ +// FFT Initialization + +//static ffe_t B[kFieldSize >> 1]; // factors used in formal derivative +static fwht_t log_walsh[kFieldSize]; // factors used in the evaluation of the error locator polynomial + +// Initialize skewVec[], B[], log_walsh[] +static void InitFieldOperations() +{ + ffe_t temp[kGFBits - 1]; + + for (unsigned i = 1; i < kGFBits; ++i) + temp[i - 1] = (ffe_t)((unsigned)1 << i); + + for (unsigned m = 0; m < (kGFBits - 1); ++m) + { + const unsigned step = (unsigned)1 << (m + 1); + + skewVec[((unsigned)1 << m) - 1] = 0; + + for (unsigned i = m; i < (kGFBits - 1); ++i) + { + const unsigned s = ((unsigned)1 << (i + 1)); + + for (unsigned j = ((unsigned)1 << m) - 1; j < s; j += step) + skewVec[j + s] = skewVec[j] ^ temp[i]; + } + + temp[m] = kFieldModulus - GFLog[mulE(temp[m], GFLog[temp[m] ^ 1])]; + + for (unsigned i = m + 1; i < (kGFBits - 1); ++i) + temp[i] = mulE(temp[i], (GFLog[temp[i] ^ 1] + temp[m]) % kFieldModulus); + } + + for (unsigned i = 0; i < kFieldSize; ++i) + skewVec[i] = GFLog[skewVec[i]]; + +#if 0 + temp[0] = kFieldModulus - temp[0]; + + for (unsigned i = 1; i < (kGFBits - 1); ++i) + temp[i] = (kFieldModulus - temp[i] + temp[i - 1]) % kFieldModulus; + + B[0] = 0; + for (unsigned i = 0; i < (kGFBits - 1); ++i) + { + const unsigned depart = ((unsigned)1 << i); + + for (unsigned j = 0; j < depart; ++j) + B[j + depart] = (B[j] + temp[i]) % kFieldModulus; + } +#endif + + for (unsigned i = 0; i < kFieldSize; ++i) + log_walsh[i] = GFLog[i]; + + log_walsh[0] = 0; + + FWHT(log_walsh, kGFBits); +} + + +//------------------------------------------------------------------------------ +// Encoder + +// Encoding alg for k/n<0.5: message is a power of two +static void encodeL(ffe_t* data, const unsigned k, ffe_t* codeword) +{ + memcpy(codeword, data, sizeof(ffe_t) * k); + + IFLT(codeword, k, 0); + + for (unsigned i = k; i < kFieldSize; i += k) + { + memcpy(&codeword[i], codeword, sizeof(ffe_t) * k); + + FLT(&codeword[i], k, i, k); + } + + memcpy(codeword, data, sizeof(ffe_t) * k); +} + +// Encoding alg for k/n>0.5: parity is a power of two. +// data: message array. parity: parity array. mem: buffer(size>= n-k) +static void encodeH(const ffe_t* data, const unsigned m, const unsigned original_count, ffe_t* parity, ffe_t* mem) +{ + // Note: Assumes data is padded with zeroes out to the next multiple of m + + memcpy(parity, data, m * sizeof(ffe_t)); + IFLT(parity, m, m); + + for (unsigned i = m; i < original_count; i += m) + { + memcpy(mem, data + i, m * sizeof(ffe_t)); + IFLT(mem, m, m + i); + for (unsigned j = 0; j < m; ++j) + parity[j] ^= mem[j]; + } + + FLT(parity, m, 0, m); +} + + +//------------------------------------------------------------------------------ +// Decoder + +static void decode(ffe_t* codeword, const unsigned m, const unsigned original_count, const unsigned n, const bool* erasure) +{ + fwht_t log_walsh2[kFieldSize]; + + // Compute the evaluations of the error locator polynomial + for (unsigned i = 0; i < kFieldSize; ++i) + log_walsh2[i] = erasure[i] ? 1 : 0; + + FWHT(log_walsh2, kGFBits); + + for (unsigned i = 0; i < kFieldSize; ++i) + log_walsh2[i] = ((unsigned)log_walsh2[i] * (unsigned)log_walsh[i]) % kFieldModulus; + + FWHT(log_walsh2, kGFBits); + + // k2 can be replaced with k + //const unsigned k2 = kFieldSize; + //const unsigned k2 = k; // cannot actually be replaced with k. maybe for encodeL() only? + + for (unsigned i = 0; i < m + original_count; ++i) + { + if (erasure[i]) + { + codeword[i] = 0; + } + else + { + codeword[i] = mulE(codeword[i], log_walsh2[i]); + } + } + for (unsigned i = m + original_count; i < n; ++i) + codeword[i] = 0; + + IFLT(codeword, n, 0); + + // Note: This is not needed to recover successfully... +#if 0 + // formal derivative + // Note: Preserves zeroes on the right + for (unsigned i = 0; i < m + original_count; i += 2) + { + codeword[i] = mulE(codeword[i], kFieldModulus - B[i >> 1]); + codeword[i + 1] = mulE(codeword[i + 1], kFieldModulus - B[i >> 1]); + } +#endif + + formal_derivative(codeword, n); + +#if 0 + // Note: Preserves zeroes on the right + for (unsigned i = 0; i < m + original_count; i += 2) + { + codeword[i] = mulE(codeword[i], B[i >> 1]); + codeword[i + 1] = mulE(codeword[i + 1], B[i >> 1]); + } +#endif + + FLT(codeword, n, 0, m + original_count); + + for (unsigned i = 0; i < kFieldSize; ++i) + { + if (erasure[i]) + { + codeword[i] = mulE(codeword[i], kFieldModulus - log_walsh2[i]); + } + } +} + + +#ifdef _MSC_VER +#include +#endif + +// Returns highest bit index 0..63 where the first non-zero bit is found +// Precondition: x != 0 +LEO_FORCE_INLINE unsigned LastNonzeroBit64(uint64_t x) +{ +#ifdef _MSC_VER +#ifdef _WIN64 + unsigned long index; + // Note: Ignoring result because x != 0 + _BitScanReverse64(&index, x); + return (unsigned)index; +#else + unsigned long index; + if (0 != _BitScanReverse(&index, (uint32_t)x)) + return (unsigned)index; + // Note: Ignoring result because x != 0 + _BitScanReverse(&index, (uint32_t)(x >> 32)); + return (unsigned)index + 32; +#endif +#else + // Note: Ignoring return value of 0 because x != 0 + return 63 - (unsigned)__builtin_clzll(x); +#endif +} + + +//------------------------------------------------------------------------------ +// Test Application + +void test(unsigned original_count, unsigned recovery_count, unsigned seed) +{ + unsigned m = 2UL << LastNonzeroBit64(recovery_count - 1); + unsigned n = 2UL << LastNonzeroBit64(m + original_count - 1); + + srand(seed); + + //-----------Generating message---------- + + // Message array + ffe_t data[kFieldSize] = {0}; + + // Filled with random numbers + for (unsigned i = m; i < m + original_count; ++i) + data[i] = (ffe_t)rand(); + + + //---------encoding---------- + + ffe_t codeword[kFieldSize] = {}; + // First m codewords are for the parity data + encodeH(data + m, m, original_count, data, codeword); + //encodeL(data, k, codeword); // does not seem to work with any input? what else needs to change? + + memcpy(codeword, data, sizeof(ffe_t) * kFieldSize); + + + //--------erasure simulation--------- + + // Array indicating erasures + bool erasure[kFieldSize] = { + false + }; + + // Tag the first "recovery_count" elements as erasures + for (unsigned i = m; i < m + recovery_count; ++i) + erasure[i] = true; + + // permuting the erasure array + for (unsigned i = m + original_count - 1; i > 0; --i) + { + unsigned pos = rand() % (i + 1); + + if (i != pos) + { + bool tmp = erasure[i]; + erasure[i] = erasure[pos]; + erasure[pos] = tmp; + } + } + + + //---------main processing---------- + decode(codeword, m, original_count, n, erasure); + + // Check the correctness of the result + for (unsigned i = 0; i < kFieldSize; ++i) + { + if (erasure[i]) + { + if (data[i] != codeword[i]) + { + printf("Decoding Error with seed = %d!\n", seed); + LEO_DEBUG_BREAK; + return; + } + } + } + + printf(":D "); +} + + +//------------------------------------------------------------------------------ +// Entrypoint + +int main(int argc, char **argv) +{ + // Fill GFLog table and GFExp table + InitField(); + + // Compute factors used in erasure decoder + InitFieldOperations(); + + unsigned seed = (unsigned)time(NULL); + for (;;) + { +#ifdef LEO_SHORT_FIELD + const unsigned input_count = 100; + const unsigned recovery_count = 20; +#else // LEO_SHORT_FIELD + const unsigned input_count = 10000; + const unsigned recovery_count = 2000; +#endif // LEO_SHORT_FIELD + + test(input_count, recovery_count, seed); + + ++seed; + } + + return 0; +} diff --git a/tests/proj/Benchmark.vcxproj b/tests/proj/Benchmark.vcxproj index 6c008f5..41583ff 100644 --- a/tests/proj/Benchmark.vcxproj +++ b/tests/proj/Benchmark.vcxproj @@ -20,36 +20,35 @@ {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D45} - Fecal + Leopard LeopardBenchmark - 10.0.14393.0 Application true MultiByte - v141 + v140 Application true MultiByte - v141 + v140 Application false true MultiByte - v141 + v140 Application false true MultiByte - v141 + v140 diff --git a/tests/proj/Experiments.filters b/tests/proj/Experiments.filters new file mode 100644 index 0000000..50a05dd --- /dev/null +++ b/tests/proj/Experiments.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tests/proj/Experiments.vcxproj b/tests/proj/Experiments.vcxproj new file mode 100644 index 0000000..187d804 --- /dev/null +++ b/tests/proj/Experiments.vcxproj @@ -0,0 +1,181 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {97FCA15F-EAF3-4F1A-AFF8-83E693DA9D65} + Leopard + LeopardExperiments + + + + Application + true + MultiByte + v140 + + + Application + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + + + + + + + + + + + + + + + + + Output/$(ProjectName)/$(Configuration)/$(Platform)/ + Obj/$(ProjectName)/$(Configuration)/$(Platform)/ + + + Output/$(ProjectName)/$(Configuration)/$(Platform)/ + Obj/$(ProjectName)/$(Configuration)/$(Platform)/ + + + Output/$(ProjectName)/$(Configuration)/$(Platform)/ + Obj/$(ProjectName)/$(Configuration)/$(Platform)/ + + + Output/$(ProjectName)/$(Configuration)/$(Platform)/ + Obj/$(ProjectName)/$(Configuration)/$(Platform)/ + + + + Level3 + Disabled + true + MultiThreadedDebug + _MBCS;%(PreprocessorDefinitions) + + + true + + + + + + + + + + + Level3 + Disabled + true + MultiThreadedDebug + _MBCS;%(PreprocessorDefinitions) + + + true + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + AnySuitable + Speed + false + MultiThreaded + true + _MBCS;%(PreprocessorDefinitions) + + + true + true + true + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + AnySuitable + Speed + false + MultiThreaded + true + _MBCS;%(PreprocessorDefinitions) + + + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file