From 883e410d9ebbdb543f4361039ef6fa930d076858 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 22 Dec 2025 17:24:14 +0300 Subject: [PATCH 01/77] Start v2.2.1 --- CMakeLists.txt | 2 +- include/bee2/info.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 662119dc..259d882e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5...3.31) project(BEE2 - VERSION 2.2.0 + VERSION 2.2.1 LANGUAGES C ) diff --git a/include/bee2/info.h b/include/bee2/info.h index aa6cf888..332516cb 100644 --- a/include/bee2/info.h +++ b/include/bee2/info.h @@ -22,18 +22,18 @@ #define __BEE2_INFO #define BEE2_NAME "Bee2" -#define BEE2_VERSION "2.2.0" +#define BEE2_VERSION "2.2.1" #define BEE2_VERSION_MAJOR "2" #define BEE2_VERSION_MINOR "2" -#define BEE2_VERSION_PATCH "0" +#define BEE2_VERSION_PATCH "1" #define BEE2_VERSION_NUM\ - 2, 2, 0 + 2, 2, 1 /*! ******************************************************************************* \mainpage Библиотека Bee2 -\version 2.2.0 +\version 2.2.1 \section toc Содержание From 0b96e4b47658ecc615ddcd315cb69c59470d2b30 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 23 Dec 2025 20:18:32 +0300 Subject: [PATCH 02/77] Upgrade rng/trng: retries and 64-bit values --- src/core/rng/rng_trng.c | 201 ++++++++++++++++++++++++++-------------- 1 file changed, 134 insertions(+), 67 deletions(-) diff --git a/src/core/rng/rng_trng.c b/src/core/rng/rng_trng.c index b9d5d1da..02d7d4bb 100644 --- a/src/core/rng/rng_trng.c +++ b/src/core/rng/rng_trng.c @@ -4,7 +4,7 @@ \brief Random number generation: physical entropy sources \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.10.08 +\version 2025.12.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -19,22 +19,33 @@ ******************************************************************************* Физические источники -Поддержан ГСЧ Intel +Поддержан ГСЧ Intel/AMD -Реализация: -- по материалам https://software.intel.com/en-us/articles/ - intel-digital-random-number-generator-drng-software-implementation-guide. +В соответствии с документами: +[1] Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: + Basic Architecture, September 2016. + https://www.intel.com/content/dam/www/public/us/en/documents/manuals/ + 64-ia-32-architectures-software-developer-vol-1-manual.pdf. +[2] Intel Digital Random Number Generator (DRNG) Software Implementation Guide. + Technical Paper. Revision 2.2, September 2025. + https://www.intel.com/content/www/us/en/content-details/864722/ + intel-digital-random-number-generator-software-implementation-guide.html. Используются инструкции: -- rdseed -- без криптографической постобработки, т.е. прямая работа с +- RDSEED -- без криптографической постобработки, т.е. прямая работа с источником случайности; -- rdrand -- с криптографической постобработкой. +- RDRAND -- с криптографической постобработкой. -Инструкция rdseed напрямую ("честнее") работает с источником случайности, -инструкция rdrand поддержана более широко. +Инструкция rdseed используется в основном источнике "trng", инструкция rdrand -- +во вспомогательном источнике "trng2". -Инструкция rdseed используется в основном источнике "trng", -инструкция rdrand -- во вспомогательном источнике "trng2". +Если инструкция RDRAND не выдала случайное число, то предпринимаются повторные +попытки, вплоть до 10. Если случайное число не выдала инструкция RDSEED, +то число попыток увеличивается до 100 и между попытками вызывается инструкция +PAUSE. Данная логика соответствует рекомендациям в [2]. + +\warning RDSEED Failure on AMD “Zen 5” Processors: +https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7055.html ******************************************************************************* */ @@ -43,85 +54,139 @@ #include #include -#define rngCPUID(info, id) __cpuidex((int*)info, id, 0) -#define rngRDStep(val) _rdseed32_step(val) -#define rngRDStep2(val) _rdrand32_step(val) +#if defined(_M_IX86) +typedef u32 trng_val_t; +#else +typedef u64 trng_val_t; +#endif -#elif defined(_MSC_VER) && defined(_M_IX86) +#define rngCPUID(info, id) __cpuid(info, id) -#define cpuid __asm _emit 0x0F __asm _emit 0xA2 -#define rdseed_eax __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF8 -#define rdrand_eax __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 +static int rngRDStep(trng_val_t* val) +{ + size_t retries = 100; + while (retries--) + { +#if defined(_M_IX86) + if (_rdseed32_step(val)) +#else + if (_rdseed64_step(val)) +#endif + return 1; + _mm_pause(); + } + return 0; +} -static void rngCPUID(u32 info[4], u32 id) +static int rngRDStep2(trng_val_t* val) { - u32 a, b, c, d; - __asm { - mov eax, id - xor ecx, ecx - cpuid - mov a, eax - mov b, ebx - mov c, ecx - mov d, edx + size_t retries = 10; + while (retries--) + { +#if defined(_M_IX86) + if (_rdrand32_step(val)) +#else + if (_rdrand64_step(val)) +#endif + return 1; } - info[0] = a, info[1] = b, info[2] = c, info[3] = d; + return 0; } -static int rngRDStep(u32* val) +#elif defined(_MSC_VER) && defined(_M_IX86) + +#include + +typedef u32 trng_val_t; + +#define rngCPUID(info, id) __cpuid(info, id) +#define pause __asm _emit 0xF3 __asm _emit 0x90 +#define rdseed_eax __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF8 +#define rdrand_eax __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 + +static int rngRDStep(trng_val_t* val) { - __asm { - xor eax, eax - xor edx, edx - rdseed_eax - jnc err - mov edx, val - mov [edx], eax + size_t retries = 100; + while (retries--) + { + __asm { + xor eax, eax + rdseed_eax + jnc err + mov edx, val + mov[edx], eax + } + return 1; + err: + _asm pause; } - return 1; -err: return 0; } -static int rngRDStep2(u32* val) +static int rngRDStep2(trng_val_t* val) { - __asm { - xor eax, eax - xor edx, edx - rdrand_eax - jnc err - mov edx, val - mov [edx], eax + size_t retries = 10; + while (retries--) + { + __asm { + xor eax, eax + rdrand_eax + jnc err + mov edx, val + mov[edx], eax + } + return 1; + err: + continue; } - return 1; -err: return 0; } -#elif (defined(__GNUC__) || defined(__clang__)) && \ +#elif (defined(__GNUC__) || defined(__clang__)) &&\ (defined(__i386__) || defined(__x86_64__)) #include -#define rngCPUID(info, id) \ +#if defined(__i386__) +typedef u32 trng_val_t; +#else +typedef u64 trng_val_t; +#endif + +#define rngCPUID(info, id)\ __cpuid_count(id, 0, info[0], info[1], info[2], info[3]) -static int rngRDStep(u32* val) +static int rngRDStep(trng_val_t* val) { - octet ok = 0; - asm ("rdseed %0; setc %1" : "=r" (*val), "=qm" (ok)); - return ok; + size_t retries = 100; + while (retries--) + { + octet ok = 0; + asm("rdseed %0; setc %1" : "=r" (*val), "=qm" (ok)); + if (ok) + return 1; + asm("pause"); + } + return 0; } -static int rngRDStep2(u32* val) +static int rngRDStep2(trng_val_t* val) { - octet ok = 0; - asm ("rdrand %0; setc %1" : "=r" (*val), "=qm" (ok)); - return ok; + size_t retries = 100; + while (retries--) + { + octet ok = 0; + asm("rdrand %0; setc %1" : "=r" (*val), "=qm" (ok)); + if (ok) + return 1; + } + return 0; } #else +typedef u32 trng_val_t; + #define rngCPUID(info, id) memSetZero(info, 16) #define rngRDStep(val) 0 #define rngRDStep2(val) 0 @@ -165,7 +230,7 @@ static bool_t rngTRNG2IsAvail() err_t rngTRNGRead(void* buf, size_t* read, size_t count) { - u32 rand; + trng_val_t rand; // pre ASSERT(memIsValid(read, O_PER_S)); ASSERT(memIsValid(buf, count)); @@ -174,15 +239,16 @@ err_t rngTRNGRead(void* buf, size_t* read, size_t count) if (!rngTRNGIsAvail()) return ERR_FILE_NOT_FOUND; // генерация - while (*read + 4 <= count) + while (*read + sizeof(trng_val_t) <= count) { if (!rngRDStep(&rand)) { CLEAN(rand); return ERR_BAD_ENTROPY; } - memCopy(buf, &rand, 4); - buf = (octet*)buf + 4, *read += 4; + memCopy(buf, &rand, sizeof(trng_val_t)); + buf = (octet*)buf + sizeof(trng_val_t); + *read += sizeof(trng_val_t); } // неполный блок if (*read < count) @@ -201,7 +267,7 @@ err_t rngTRNGRead(void* buf, size_t* read, size_t count) err_t rngTRNG2Read(void* buf, size_t* read, size_t count) { - u32 rand; + trng_val_t rand; // pre ASSERT(memIsValid(read, O_PER_S)); ASSERT(memIsValid(buf, count)); @@ -210,15 +276,16 @@ err_t rngTRNG2Read(void* buf, size_t* read, size_t count) if (!rngTRNG2IsAvail()) return ERR_FILE_NOT_FOUND; // генерация - while (*read + 4 <= count) + while (*read + sizeof(trng_val_t) <= count) { if (!rngRDStep2(&rand)) { CLEAN(rand); return ERR_BAD_ENTROPY; } - memCopy(buf, &rand, 4); - buf = (octet*)buf + 4, *read += 4; + memCopy(buf, &rand, sizeof(trng_val_t)); + buf = (octet*)buf + sizeof(trng_val_t); + *read += sizeof(trng_val_t); } // неполный блок if (*read < count) From 679cdbd2e56f16ca180ca0498ed3ff0893d1e50e Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 24 Dec 2025 14:44:29 +0300 Subject: [PATCH 03/77] Update comments in core/rng/trng --- src/core/rng/rng_trng.c | 48 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/core/rng/rng_trng.c b/src/core/rng/rng_trng.c index 02d7d4bb..64ba11d1 100644 --- a/src/core/rng/rng_trng.c +++ b/src/core/rng/rng_trng.c @@ -4,7 +4,7 @@ \brief Random number generation: physical entropy sources \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.12.23 +\version 2025.12.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -19,30 +19,38 @@ ******************************************************************************* Физические источники -Поддержан ГСЧ Intel/AMD +ГСЧ Intel/AMD -В соответствии с документами: -[1] Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: +Инструкции для работы с ГСЧ: +- RDSEED -- выдает данные от источника случайности после их + криптографической постобработки с помощью AES-CBC-MAC; +- RDRAND -- выдает данные генератора псевдослучайных чисел AES-CTR-DRBG. + Затравочное значение генератора (seed) строится по данным от источника + случайности и периодически обновляется (в соответствии с [1]: не реже + чем после 1022 обращений к RDRAND). + +Инструкция RDSEED используется в основном источнике "trng", инструкция +RDRAND -- во вспомогательном источнике "trng2". + +Архитектура ГСЧ / описание инструкций: +[1] Intel Digital Random Number Generator (DRNG) Software Implementation Guide. + Technical Paper. Revision 2.2, September 2025. + https://www.intel.com/content/www/us/en/content-details/864722/ + intel-digital-random-number-generator-software-implementation-guide.html. +[2] AMD Random Number Generator, 6/27/17. + https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/ + white-papers/amd-random-number-generator.pdf. +[3] Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: Basic Architecture, September 2016. https://www.intel.com/content/dam/www/public/us/en/documents/manuals/ 64-ia-32-architectures-software-developer-vol-1-manual.pdf. -[2] Intel Digital Random Number Generator (DRNG) Software Implementation Guide. - Technical Paper. Revision 2.2, September 2025. - https://www.intel.com/content/www/us/en/content-details/864722/ - intel-digital-random-number-generator-software-implementation-guide.html. - -Используются инструкции: -- RDSEED -- без криптографической постобработки, т.е. прямая работа с - источником случайности; -- RDRAND -- с криптографической постобработкой. - -Инструкция rdseed используется в основном источнике "trng", инструкция rdrand -- -во вспомогательном источнике "trng2". -Если инструкция RDRAND не выдала случайное число, то предпринимаются повторные -попытки, вплоть до 10. Если случайное число не выдала инструкция RDSEED, -то число попыток увеличивается до 100 и между попытками вызывается инструкция -PAUSE. Данная логика соответствует рекомендациям в [2]. +Работа с ГСЧ реализована в соответствии с рекомендациями в [1]. Детали +реализации: +1. Если инструкция RDRAND завершена с ошибкой, т.е. случайное число не было + сгенерировано, то предпринимаются повторные попытки генерации, вплоть до 10. +2. В случае ошибки при вызове RDSEED число попыток увеличивается до 100 и между + попытками вызывается инструкция PAUSE. \warning RDSEED Failure on AMD “Zen 5” Processors: https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7055.html From 2c32027462ff8b15ebf457af6685c7ccba6b20e7 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 24 Dec 2025 16:54:25 +0300 Subject: [PATCH 04/77] Simplify memWipe() --- src/core/mem.c | 51 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/core/mem.c b/src/core/mem.c index 92e4b96e..cf51e610 100644 --- a/src/core/mem.c +++ b/src/core/mem.c @@ -4,7 +4,7 @@ \brief Memory management \project bee2 [cryptographic library] \created 2012.12.18 -\version 2025.10.03 +\version 2025.12.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -266,32 +266,21 @@ int FAST(memCmpRev)(const void* buf1, const void* buf2, size_t count) ******************************************************************************* Очистка буфера памяти -\remark Функция memWipe() повторяет функцию OPENSSL_cleanse() -из библиотеки OpenSSL: -\code - unsigned char cleanse_ctr = 0; - void OPENSSL_cleanse(void *ptr, size_t len) - { - unsigned char *p = ptr; - size_t loop = len, ctr = cleanse_ctr; - while(loop--) - { - *(p++) = (unsigned char)ctr; - ctr += (17 + ((size_t)p & 0xF)); - } - p=memchr(ptr, (unsigned char)ctr, len); - if(p) - ctr += (63 + (size_t)p); - cleanse_ctr = (unsigned char)ctr; - } -\endcode +Методы очистки памяти с защитой от оптимизаторов: -\remark На платформе Windows есть функции SecureZeroMemory() -и RtlSecureZeroMemory(), которые, как и memWipe(), выполняют -гарантированную очистку памяти. -******************************************************************************* -*/ +[1] Yang Z., Johannesmeyer B., Trier Olesen A., Lerner S., Levchenko K. + Dead Store Elimination (Still) Considered Harmfuf. USENIX Security 2017: + 26th USENIX Security Symposium, pp. 1025–1040, 2017. + https://www.usenix.org/system/files/conference/usenixsecurity17/ + sec17-yang.pdf + +Реализован метод "Volatile Function Pointer". Он используется также в OpenSSL +версии 1.0.2 и выше. В [1] подтверждается безопасность метода при использовании +GCC, Clang и Microsoft Visual Studio. +\remark В предыдущей редакции memWipe() был реализован метод +"Complicated Computation": +\code void memWipe(void* buf, size_t count) { static octet wipe_ctr = 0; @@ -307,6 +296,18 @@ void memWipe(void* buf, size_t count) ctr += (63 + (size_t)p); wipe_ctr = (octet)ctr; } +\endcode +Заимствован код функции OPENSSL_cleanse() библиотеки OpenSSL версии < 1.0.2. +******************************************************************************* +*/ + +typedef void (*memset_i)(void*, octet, size_t); +static volatile memset_i memSet_volatile = memSet; + +void memWipe(void *buf, size_t count) +{ + memSet_volatile(buf, 0, count); +} /* ******************************************************************************* From 54c13666ad94b70411f8da44a7e19789780c6dd2 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 24 Dec 2025 20:41:59 +0300 Subject: [PATCH 05/77] Use BCryptGenRandom() as sys2 entropy source --- src/core/file.c | 6 +++--- src/core/mem.c | 2 +- src/core/rng/rng_sys.c | 33 ++++++++++++++++++++++++++++----- src/core/rng/rng_trng.c | 3 ++- src/math/pp/pp_mul.c | 6 +++--- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/core/file.c b/src/core/file.c index 018628c5..02d452f8 100644 --- a/src/core/file.c +++ b/src/core/file.c @@ -4,13 +4,13 @@ \brief File management \project bee2 [cryptographic library] \created 2025.04.11 -\version 2025.06.10 +\version 2025.12.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* */ -#ifdef _MSC_VER +#if defined(_MSC_VER) #include #else #include @@ -179,7 +179,7 @@ size_t fileSize(file_t file) bool_t fileTrunc(file_t file, size_t size) { ASSERT(fileIsValid(file)); -#ifdef _MSC_VER +#if defined(_MSC_VER) return (size_t)(__int64)size == size && _chsize_s(_fileno(file), (__int64)size) == 0; #else diff --git a/src/core/mem.c b/src/core/mem.c index cf51e610..18232a1a 100644 --- a/src/core/mem.c +++ b/src/core/mem.c @@ -527,7 +527,7 @@ void memRev(void* buf, size_t count) ******************************************************************************* */ -#if defined(_MSC_VER) && (_MSC_VER < 1800) +#if defined(_MSC_VER) && _MSC_VER < 1800 #define va_copy(dest, src) ((dest) = (src)) #endif diff --git a/src/core/rng/rng_sys.c b/src/core/rng/rng_sys.c index fc865906..147a2520 100644 --- a/src/core/rng/rng_sys.c +++ b/src/core/rng/rng_sys.c @@ -4,7 +4,7 @@ \brief Random number generation: system entropy sources \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.11.08 +\version 2025.12.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -69,7 +69,6 @@ CryptAcquireContextW() снижает риск ошибочного заверш #include #include -#include err_t rngSysRead(void* buf, size_t* read, size_t count) { @@ -82,7 +81,7 @@ err_t rngSysRead(void* buf, size_t* read, size_t count) if (!CryptAcquireContextW(&hprov, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return ERR_FILE_NOT_FOUND; - // получить данные + // получить случайные данные *read = 0; if (!CryptGenRandom(hprov, (DWORD)count, (octet*)buf)) { @@ -95,9 +94,32 @@ err_t rngSysRead(void* buf, size_t* read, size_t count) return ERR_OK; } +#if defined(_MSC_VER) && _MSC_VER > 1500 &&\ + defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 + +#include +#pragma comment (lib, "bcrypt.lib") + +err_t rngSys2Read(void* buf, size_t* read, size_t count) +{ + ASSERT(memIsValid(read, O_PER_S)); + ASSERT(memIsValid(buf, count)); + ASSERT((size_t)(ULONG)count == count); + // получить случайные данные + *read = 0; + if (BCryptGenRandom(NULL, buf, (ULONG)count, + BCRYPT_USE_SYSTEM_PREFERRED_RNG) != 0) + return ERR_BAD_ENTROPY; + *read = count; + return ERR_OK; +} + +#else + +#include + err_t rngSys2Read(void* buf, size_t* read, size_t count) { - // pre ASSERT(memIsValid(read, O_PER_S)); ASSERT(memIsValid(buf, count)); ASSERT((size_t)(ULONG)count == count); @@ -105,11 +127,12 @@ err_t rngSys2Read(void* buf, size_t* read, size_t count) *read = 0; if (!RtlGenRandom(buf, (ULONG)count)) return ERR_BAD_ENTROPY; - // завершение *read = count; return ERR_OK; } +#endif + #elif defined OS_UNIX err_t rngSysRead(void* buf, size_t* read, size_t count) diff --git a/src/core/rng/rng_trng.c b/src/core/rng/rng_trng.c index 64ba11d1..05aff22b 100644 --- a/src/core/rng/rng_trng.c +++ b/src/core/rng/rng_trng.c @@ -57,7 +57,8 @@ RDRAND -- во вспомогательном источнике "trng2". ******************************************************************************* */ -#if (_MSC_VER >= 1600) && (defined(_M_IX86) || defined(_M_X64)) +#if defined(_MSC_VER) && _MSC_VER >= 1600 &&\ + (defined(_M_IX86) || defined(_M_X64)) #include #include diff --git a/src/math/pp/pp_mul.c b/src/math/pp/pp_mul.c index 0501973a..7afb4ebe 100644 --- a/src/math/pp/pp_mul.c +++ b/src/math/pp/pp_mul.c @@ -4,7 +4,7 @@ \brief Binary polynomials: multiplicative operations \project bee2 [cryptographic library] \created 2012.03.01 -\version 2025.10.22 +\version 2025.12.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -18,7 +18,7 @@ [см. макросы _MUL_REPAIR_XX] */ -#if (_MSC_VER >= 1200) +#if defined(_MSC_VER) && _MSC_VER >= 1200 #pragma warning(push) #pragma warning(disable:4146) #endif @@ -1386,6 +1386,6 @@ size_t ppMod_deep(size_t n, size_t m) } /* снять подавление предупреждения C4146 */ -#if (_MSC_VER >= 1200) +#if defined(_MSC_VER) && _MSC_VER >= 1200 #pragma warning(pop) #endif From c1ee87f2a2e5d84c98d4505aaa3ea99154c20e14 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 29 Dec 2025 16:26:04 +0300 Subject: [PATCH 06/77] Fix typos in comments --- include/bee2/crypto/belt.h | 4 ++-- include/bee2/math/ec2.h | 2 +- include/bee2/math/ecp.h | 8 ++++---- src/math/ec.c | 8 ++++---- src/math/ecp.c | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/bee2/crypto/belt.h b/include/bee2/crypto/belt.h index 23596a64..975e43b6 100644 --- a/include/bee2/crypto/belt.h +++ b/include/bee2/crypto/belt.h @@ -4,7 +4,7 @@ \brief STB 34.101.31 (belt): data encryption and integrity algorithms \project bee2 [cryptographic library] \created 2012.12.18 -\version 2023.02.02 +\version 2025.12.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -375,7 +375,7 @@ void beltCompr( /*! \brief Сжатие форматированных данных со сложением Форматированный буфер h || X сжимается до форматированного буфера h. - Внутренняя переменная S belt-compress добавдяется поразрядно + Внутренняя переменная S belt-compress добавляется поразрядно по модулю 2 к буферу s. \pre Буферы s и h, X и s, X и h не пересекаются. \deep{stack} beltCompr_deep(). diff --git a/include/bee2/math/ec2.h b/include/bee2/math/ec2.h index 3f34ff97..e71985f8 100644 --- a/include/bee2/math/ec2.h +++ b/include/bee2/math/ec2.h @@ -36,7 +36,7 @@ extern "C" { E(GF(2^m)) -- множество аффинных точек E (решений E в GF(2^m)), O -- бесконечно удаленная точка. -Поддерживаются координаты Лопеса -- Дахаба (с помощью структуры ec_o) +Поддерживаются координаты Лопеса -- Дахаба (с помощью структуры ec_o) и аффинные координаты (прямые функции). \pre Все указатели, передаваемые в функции, действительны. diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 62f60e65..ab4bdb19 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2025.09.03 +\version 2025.12.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -36,7 +36,7 @@ extern "C" { E(GF(p)) -- множество аффинных точек E (решений E в GF(p)), O -- бесконечно удаленная точка. -Поддерживаются якобиановы координаты (с помощью структуры ec_o) +Поддерживаются якобиевы координаты (с помощью структуры ec_o) и аффинные координаты (прямые функции). \pre Все указатели, передаваемые в функции, действительны. @@ -53,9 +53,9 @@ O -- бесконечно удаленная точка. ******************************************************************************* */ -/*! \brief Создание эллиптической кривой в якобиановых координатах +/*! \brief Создание эллиптической кривой в якобиевых координатах - Создается описание ec эллиптической кривой в якобиановых координатах + Создается описание ec эллиптической кривой в якобиевых координатах над полем f с коэффициентами [f->no]A и [f->no]B. \return Признак успеха. \pre gfpIsOperable(f) == TRUE. diff --git a/src/math/ec.c b/src/math/ec.c index 4a69b110..5477554e 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2025.09.13 +\version 2025.12.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -131,9 +131,9 @@ Springer, 2004]. В практических диапазонах размерностей при использовании наиболее эффективных -координат (якобиановых для кривых над GF(p) и Лопеса -- Дахаба для кривых -над GF(2^m)) первые две стратегии являются проигрышными. Реализована только -третья стратегия. +координат (якобиевых для кривых над GF(p) и Лопеса -- Дахаба для кривых над +GF(2^m)) первые две стратегии являются проигрышными. Реализована только третья +стратегия. Оптимальная длина окна выбирается как решение следующей оптимизационной задачи: diff --git a/src/math/ecp.c b/src/math/ecp.c index 08b561e1..caf5224e 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2025.10.22 +\version 2025.12.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -56,7 +56,7 @@ /* ******************************************************************************* -Якобиановы координаты: +Якобиевы координаты: x = X / Z^2, y = Y / Z^3, -(X : Y : Z) = (X : -Y : Z). From 21b9f202c9d3c25ab4346c4561e9ea351f30063c Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 8 Jan 2026 12:29:00 +0300 Subject: [PATCH 07/77] Refine belsGenMid() --- src/crypto/bels.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/crypto/bels.c b/src/crypto/bels.c index 107bfa7c..36d3dc36 100644 --- a/src/crypto/bels.c +++ b/src/crypto/bels.c @@ -4,7 +4,7 @@ \brief STB 34.101.60 (bels): secret sharing algorithms \project bee2 [cryptographic library] \created 2013.05.14 -\version 2025.09.29 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -82,6 +82,7 @@ err_t belsValM(const octet m0[], size_t len) if ((len != 16 && len != 24 && len != 32) || !memIsValid(m0, len)) return ERR_BAD_INPUT; // создать состояние + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); state = blobCreate2( O_OF_W(n + 1), @@ -124,6 +125,7 @@ err_t belsGenM0(octet m0[], size_t len, gen_i ang, void* ang_state) !memIsValid(m0, len)) return ERR_BAD_INPUT; // создать состояние + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); state = blobCreate2( O_OF_W(n + 1), @@ -169,6 +171,7 @@ err_t belsGenMi(octet mi[], size_t len, const octet m0[], gen_i ang, return ERR_BAD_INPUT; EXPECT(belsValM(m0, len) == ERR_OK); // создать состояние + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); state = blobCreate2( O_OF_W(n + 1), @@ -215,7 +218,7 @@ err_t belsGenMid(octet mid[], size_t len, const octet m0[], const octet id[], void* state; word* f0; /* [n + 1] */ word* f; /* [n + 1] */ - word* u; /* [W_OF_O(32) + 1] */ + word* u; /* [n + 1] */ void* stack; // проверить входные данные if ((len != 16 && len != 24 && len != 32) || @@ -224,11 +227,12 @@ err_t belsGenMid(octet mid[], size_t len, const octet m0[], const octet id[], return ERR_BAD_INPUT; EXPECT(belsValM(m0, len) == ERR_OK); // создать состояние + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); state = blobCreate2( O_OF_W(n + 1), O_OF_W(n + 1), - O_OF_W(W_OF_O(32) + 1), + O_OF_W(n + 1), utilMax(2, beltHash_keep(), ppMinPolyMod_deep(n + 1)), @@ -242,8 +246,8 @@ err_t belsGenMid(octet mid[], size_t len, const octet m0[], const octet id[], // хэшировать beltHashStart(stack); beltHashStepH(id, id_len, stack); - beltHashStepG((octet*)u, stack); - wwFrom(u, u, 32); + beltHashStepG2((octet*)u, len, stack); + wwFrom(u, u, len); u[n] = 0; // попытки генерации for (reps = MAX2(3, B_PER_IMPOSSIBLE * 2 / len / 8); reps--;) @@ -346,6 +350,7 @@ err_t belsShare(octet si[], size_t count, size_t threshold, size_t len, return ERR_BAD_INPUT; EXPECT(belsValM(m0, len) == ERR_OK); // создать состояние + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); state = blobCreate2( O_OF_W(n + 1), @@ -402,6 +407,7 @@ err_t belsShare2(octet si[], size_t count, size_t threshold, size_t len, !memIsValid(s, len) || !memIsValid(si, count * (len + 1))) return ERR_BAD_INPUT; // создать состояние + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); state = blobCreate2( O_OF_W(n + 1), @@ -493,6 +499,7 @@ err_t belsRecover(octet s[], size_t count, size_t len, const octet si[], return ERR_BAD_INPUT; EXPECT(belsValM(m0, len) == ERR_OK); // расчет глубины стека + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); deep = utilMax(2, ppMul_deep(n, n), @@ -599,6 +606,7 @@ err_t belsRecover2(octet s[], size_t count, size_t len, const octet si[]) return ERR_BAD_PUBKEY; } // расчет глубины стека + ASSERT(len % O_PER_W == 0); n = W_OF_O(len); deep = utilMax(2, ppMul_deep(n, n), From 5ab060bb16313452a2faa855c1c1fb5f529e9008 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 9 Jan 2026 19:10:55 +0300 Subject: [PATCH 08/77] Provide interface for calculating small multiples in ec.h --- include/bee2/math/ec.h | 224 ++++++++++++++++++++++-------------- include/bee2/math/ec2.h | 5 +- include/bee2/math/ecp.h | 5 +- src/crypto/bake/bake_bmqv.c | 2 +- src/crypto/bake/bake_bsts.c | 6 +- src/crypto/bign/bign_ibs.c | 6 +- src/math/ec.c | 150 +++++++++++++++--------- src/math/ec2.c | 28 ++--- src/math/ecp.c | 34 +++--- test/math/ecp_test.c | 4 +- 10 files changed, 282 insertions(+), 182 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 359d7e8f..c94561ce 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2025.09.29 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -31,88 +31,86 @@ extern "C" { ******************************************************************************* \file ec.h -Определяются вычисления на эллиптических кривых над конечными полями. -Поле представляется как кольцо вычетов с помощью структуры -типа qr_o (см. заголовочный файл qr.h). Описание кривой соответствует -соглашениям ANSI X 9.62 (ECDSA) и последующих стандартов. +Реализованы операции с точками эллиптической кривой над конечным полем. Поле +представляется как кольцо вычетов с помощью структуры типа qr_o +(см. заголовочный файл qr.h). Описание кривой соответствует соглашениям +ANSI X 9.62 (ECDSA) и последующих стандартов. -Эллиптическая кривая описывается структурой типа ec_o. Пусть ec -- указатель -на такую структуру. Тогда ec->f указывает на описание базового поля, -ec->A и ec->B --- коэффициенты из базового поля, которые определяют -уравнение эллиптической кривой. +Эллиптическая кривая описывается структурой типа ec_o. Пусть ec -- указатель +на такую структуру. Тогда ec->f указывает на описание базового поля, ec->A +и ec->B --- коэффициенты из базового поля, которые определяют уравнение +эллиптической кривой. -В описание ec->f базового поля включается функции арифметики в этом поле. -Описание должно быть настроено вызывающей программой -(см. описание qrCreate() в qr.h). +Описание ec->f базового поля включает функции арифметики в этом поле. Описание +должно быть настроено вызывающей программой (см. qrCreate() в qr.h). Используется внутреннее (для ec->f) представление элементов базового поля. -Импорт элементов базового поля из строк октетов выполняется -с помощью функции ec->f->from(), экспорт -- с помощью функции ec->f->to(). -Можно также использовать макросы qrFrom, qrTo (см. заголовочный файл qr.h). - -Точка эллиптической кривой задается ec->d координатами из базового поля. -Каждая координата представляется ec->f->n машинными словами. Координаты -записываются друг за другом и в целом образуют массив pt из ec->d * ec->f->n -машинных слов. Первые три координаты именованные: X, Y, Z. Макросы -ecX, ecY, ecZ поддерживают извлечение именованных координат из буфера pt. - -Точки с двумя координатами называются аффинными, а точки -pt = (X : Y : Z :...) с тремя и более координатами -- проективными. -Бесконечно удаленную точку O нельзя представить аффинной, но можно проективной: -точке O соответствует точка pt, у которой Z == 0. Макросы ecSetO, ecIsO -выполняют присваивание pt = O и проверку pt == O. - -Для организации вычислений требуется, чтобы среди точек эллиптической -кривой имелась бесконечно удаленная. Поэтому должны обязательно использоваться -проективные координаты (проективные точки). Размерность ec->d >= 3. - -Проективную точку можно построить по аффинной точке (x, y) с помощью функции +Импорт элементов базового поля из строк октетов выполняется с помощью функции +ec->f->from(), экспорт -- с помощью функции ec->f->to(). Можно также +использовать макросы qrFrom, qrTo (см. заголовочный файл qr.h). + +Точка эллиптической кривой задается ec->d координатами из базового поля. Каждая +координата задается ec->f->n машинными словами. Координаты записываются друг за +другом и в целом образуют массив pt из ec->d * ec->f->n машинных слов. Первые +три координаты именованные: X, Y и Z. Макросы ecX, ecY, ecZ поддерживают +извлечение именованных координат из буфера pt. + +Точки с двумя координатами называются аффинными, а точки pt = (X : Y : Z :...) +с тремя и более координатами -- проективными. Бесконечно удаленную точку O +нельзя представить как аффинную, но можно как проективную: точке O +соответствует точка pt, у которой Z == 0. Макросы ecSetO, ecIsO выполняют +присваивание pt = O и проверку pt == O. + +Для организации вычислений на кривой требуется, чтобы среди ее точек имелась +бесконечно удаленная. Поэтому должны обязательно использоваться проективные +координаты (проективные точки). Как следствие, размерность ec->d >= 3. + +Проективную точку можно построить по аффинной точке (x, y) с помощью функции интерфейса ec_froma_i. Как правило, построение состоит в присваиваниях \code - X <- x, Y <- y, Z <- ec->f->unity,.... + X <- x, Y <- y, Z <- ec->f->unity, .... \endcode -Обратно, проективную точку можно преобразовать в аффинную с помощью -функции интерфейса ec_toa_i (при преобразовании бесконечно удаленной точки +Обратно, проективную точку можно преобразовать в аффинную с помощью +функции интерфейса ec_toa_i (при преобразовании бесконечно удаленной точки функция возвращает соответствующий признак). -Описание ec может быть использовано для организации вычислений в аффинных +Описание ec может быть использовано для организации вычислений в аффинных координатах, несмотря на заданные этим описанием проективные. -\remark Если используется ec->d == 3, то Z-координата точки pt всегда -равняется единице для pt != O и нулю для pt == O, то вычисления на кривой -фактически ведутся в аффинных координатах. - -В структуре типа ec_o предусмотрены поля для описания циклической группы -точек эллиптической кривой. Эта группа задается образующим ec->base. -Образующий представляется аффинной точкой, т.е. задается -2 * ec->f->n машинными словами. Порядок циклической группы -ec->order задается 2 * ec->f->n + 1 машинным словом. Кофактор -- -отношение числа всех ec->f-рациональных точек к ec->order -- -задается одним машинным словом ec->cofactor. +В структуре типа ec_o предусмотрены поля для описания группы точек +эллиптической кривой: +- в поле ec->base указывается образующий группы, +- в поле ec->order -- порядок группы, +- в поле ec->cofactor -- кофактор группы, т.е. отношение числа всех + ec->f-рациональных точек кривой к ec->order. +. +Образующий представляет собой аффинную точку, т.е. задается 2 * ec->f->n +машинными словами, порядок задается 2 * ec->f->n + 1 машинным словом, +кофактор -- одним машинным словом. -Если работа с группой точек не предусматривается, то поля base, order -и cofactor могут не задаваться. +Если работа с группой точек не предполагается, то поля ec->base, ec->order +и ec->cofactor можно не инициализировать. По адресу ec->params могут размещаться дополнительные данные, например, -определенные кратные базовой точки, с помощью которых можно ускорить -криптографические вычисления на эллиптической кривой. +данные, с помощью которых можно ускорить вычисления на эллиптической кривой. -Описание ec эллиптической кривой включает указатели на функции арифметики -в группе точек этой кривой. Функцию интерфейса ec_tpl_i -можно не поддерживать. Указатель на неподдерживаемую функцию -должен быть нулевым. +Описание эллиптической кривой включает указатели на функции арифметики +в группе точек этой кривой. Функции интерфейсов ec_tpl_i и ec_smult_i +можно не поддерживать. Указатель на неподдерживаемую функцию должен быть +нулевым. Описание кольца организовано как объект, и можно применять функции, описанные в заголовочном файле obj.h. Эллиптическая кривая должна создаваться специальной функцией ecCreate(), -которая является аналогом конструктора. В эту функцию должен передаваться +которая является аналогом конструктора и реализуется разными способами в +завсимости от типа кривой. В функцию ecCreate() должен передаваться указатель ec на структуру типа ec_o. По этому указателю будет размещаться описание (состояние) кривой. По адресу ec может быть зарезервировано больше -памяти, чем sizeof(ec_o). Дело в том, что коэффициенты A, B, порядок order, -базовая точка base могут размещаться в поле descr открытого размера. -Функция ecCreate() должна сопровождаться функцией ecCreate_keep(), -которая поддерживает расчет длины состояния (в октетах). +памяти, чем sizeof(ec_o). Дело в том, что коэффициенты A и B, +базовая точка base и ее порядок order могут размещаться в поле descr +открытого размера. Функция ecCreate() должна сопровождаться функцией +ecCreate_keep(), которая поддерживает расчет длины состояния (в октетах). Функция ecCreate_keep() должна оценивать длину состояния сверху. Оценка может быть неточной. Точная длина состояния фиксируется при @@ -128,8 +126,8 @@ ec->order задается 2 * ec->f->n + 1 машинным словом. Ко работы с функциями арифметики на кривой. Оценка глубины стека, определяемая функцией ecCreate_deep(), может быть -неточной. Точная глубина фиксируется при непосредственном выполнении ecCreate() -и сохраняется в поле deep описания кривой. +неточной. Точная глубина фиксируется при непосредственном выполнении +ecCreate() и сохраняется в поле deep описания кривой. В функцию ecCreate() могут не передаваться параметры base, order и cofactor, определяющие строение группы точек, хотя буферы для base и order могут @@ -147,8 +145,8 @@ struct ec_o; /*! \brief Импорт из аффинной точки - По аффинной точке [2 * ec->f->n]a эллиптической кривой ec строится - точка [ec->d * ec->f->n]b. + По аффинной точке [2 * ec->f->n]a эллиптической кривой ec строится точка + [ec->d * ec->f->n]b. \pre Описание ec работоспособно. \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. \pre Координаты a лежат в базовом поле. @@ -166,8 +164,8 @@ typedef bool_t (*ec_froma_i)( /*! \brief Экспорт в аффинную точку - По точке [ec->d * ec->f->n]a эллиптической кривой ec строится - аффинная точка [2 * ec->f->n]b. + По точке [ec->d * ec->f->n]a эллиптической кривой ec строится аффинная + точка [2 * ec->f->n]b. \pre Описание ec работоспособно. \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. \pre Координаты a лежат в базовом поле. @@ -185,7 +183,7 @@ typedef bool_t (*ec_toa_i)( /*! \brief Обратная точка - На эллиптической кривой ec определяется точка [ec->d * ec->f->n]b, + На эллиптической кривой ec определяется точка [ec->d * ec->f->n]b, обратная к точке [ec->d * ec->f->n]a: \code b <- -a. @@ -352,6 +350,29 @@ typedef void (*ec_tpl_i)( void* stack /*!< [in] вспомогательная память */ ); +/*! \brief Расчет малых нечетных кратных + + На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных + аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере + [2^{w-1} * ec->d * ec->f->n]b: + \code + b <- (a, 3a, ..., (2^w - 1)a). + \endcode + \pre w > 0. + \pre Описание ec работоспособно. + \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. +*/ +typedef void (*ec_smul_i)( + word b[], /*!< [out] малые кратные */ + const word a[], /*!< [in] первоначальная точка */ + size_t w, /*!< [in] ширина окна */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + /*! \brief Описание эллиптической кривой Описывается эллиптическая кривая, правила представления ее элементов, @@ -371,6 +392,8 @@ typedef struct ec_o void* params; /*!< дополнительные параметры */ // } size_t d; /*!< размерность */ + size_t max_w; /*!< максимальная ширина окна */ + size_t w; /*!< ширина окна */ word cofactor; /*!< кофактор группы точек */ ec_froma_i froma; /*!< функция импорта из аффинной точки */ ec_toa_i toa; /*!< функция экспорта в аффинную точку */ @@ -382,6 +405,7 @@ typedef struct ec_o ec_dbl_i dbl; /*!< функция удвоения */ ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ + ec_smul_i smul; /*!< функция расчета малых кратных */ size_t deep; /*!< максимальная глубина стека функций */ mem_align_t descr[]; /*!< память для размещения данных */ } ec_o; @@ -459,13 +483,13 @@ typedef struct ec_o - objKeep(ec) >= sizeof(ec_o); - objPCount(ec) == 6 && objOCount(ec) == 1; - ec->d >= 3; - - qrIsOperable(ec->f) == TRUE; - - буферы [ec->n]ec->A, [ec->n]ec->B корректны; - указатели ec->froma, r->toa, ..., r->dbla корректны; + - qrIsOperable(ec->f) == TRUE; + - буферы [ec->f->n]ec->A, [ec->f->n]ec->B корректны; - ec->deep >= ec->f->deep. . \return Признак корректности. - \remark Поля ec->base, ec->order и ec->cofactor, описывающие группу точек, + \remark Поля ec->base, ec->order и ec->cofactor, описывающие группу точек, не контролируются. */ bool_t ecIsOperable( @@ -480,11 +504,10 @@ bool_t ecIsOperable( - objKeep(ec) >= sizeof(ec_o); - objPCount(ec) == 6 && objOCount(ec) == 1; - ec->d >= 3; - - буферы [ec->n]ec->A, [ec->n]ec->B корректны; - - указатели ec->froma, r->toa, ..., r->dbla корректны. + - указатели ec->froma, r->toa, ..., r->dbla корректны; . \return Признак корректности. - \remark Поля ec->base, ec->order и ec->cofactor, описывающие группу точек, + \remark Поля ec->base, ec->order и ec->cofactor, описывающие группу точек, не контролируются. */ bool_t ecIsOperable2( @@ -502,9 +525,9 @@ bool_t ecIsOperable2( машинных слов. \expect{FALSE} cofactor != 0 && cofactor укладывается в машинное слово. \expect Описание ec корректно. - \return TRUE, если описание группы успешно задано, и FALSE в - противном случае. - \remark Любой из указателей xbase, ybase может быть нулевым. При нулевом + \return TRUE, если описание группы успешно создано, и FALSE в противном + случае. + \remark Любой из указателей xbase, basey может быть нулевым. При нулевом указателе соответствующая координата базовой точки устанавливается равной нулю. \deep{stack} ecGroupCreate_deep(f_deep). @@ -515,7 +538,7 @@ bool_t ecGroupCreate( const octet ybase[], /*!< [in] y-координата базовой точки */ const octet order[], /*!< [in] порядок группы точек */ size_t order_len, /*!< [in] длина order */ - u32 cofactor, /*!< [in] кофактор группы точек */ + size_t cofactor, /*!< [in] кофактор группы точек */ void* stack /*!< [in] вспомогательная память */ ); @@ -542,6 +565,33 @@ bool_t ecGroupIsOperable( ******************************************************************************* */ +/*! \brief Малые нечетные кратные + + На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных + аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере + [2^{w-1} * ec->d * ec->f->n]b: + \code + b <- (a, 3a, ..., (2^w - 1)a). + \endcode + \pre w > 0. + \pre Описание ec работоспособно. + \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. + \deep{stack} ecSmul_deep(ec->f->n, ec->d, ec->deep). + \remark Реализация интерфейса ec_smul_i. +*/ +void ecSmul( + word b[], /*!< [out] малые кратные */ + const word a[], /*!< [in] базовая точка */ + size_t w, /*!< [in] длина окна */ + const ec_o* ec, /*!< [in] описание кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep); + /*! \brief Кратная точка Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, @@ -553,20 +603,24 @@ bool_t ecGroupIsOperable( \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на ec. - \return TRUE, если кратная точка является аффинной, и FALSE в противном - случае (b == O). - \deep{stack} ecpMulA_deep(ec->f->n, ec->d, ec->f->deep, m). + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} ecMulA_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция нерегулярна. */ -bool_t ecMulA( +bool_t ecMulNAF( word b[], /*!< [out] кратная точка */ - const word a[], /*!<[in] базовая точка */ + const word a[], /*!< [in] базовая точка */ const ec_o* ec, /*!< [in] описание кривой */ const word d[], /*!< [in] кратность */ size_t m, /*!< [in] длина d в машинных словах */ void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + +#define ecMulA ecMulNAF +#define ecMulA_deep ecMulNAF_deep /*! \brief Имеет порядок? @@ -580,7 +634,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \expect Число q -- простое. Если q -- составное, то точка порядка q1 | q может быть ошибочно признана точкой порядка q. \return Признак успеха проверки. - \deep{stack} ecpHasOrderA_deep(ec->f->n, ec->d, ec->f->deep, m). + \deep{stack} ecHasOrderA_deep(ec->f->n, ec->d, ec->deep, m). */ bool_t ecHasOrderA( const word a[], /*!< [in] точка */ diff --git a/include/bee2/math/ec2.h b/include/bee2/math/ec2.h index e71985f8..44f8120c 100644 --- a/include/bee2/math/ec2.h +++ b/include/bee2/math/ec2.h @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.04.19 -\version 2025.09.03 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -110,7 +110,8 @@ size_t ec2IsValid_deep(size_t n); . \pre Описание ec работоспособно. \return Признак корректности. - \remark Не проверяется, что порядок ec->base равняется ec->order. + \remark Не проверяется, что порядок базовой точки в ec->base равняется + ec->order. \deep{stack} ec2GroupSeemsValid_deep(ec->f->n, ec->f->deep). */ bool_t ec2GroupSeemsValid( diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index ab4bdb19..4c1fc182 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2025.12.29 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -112,7 +112,8 @@ size_t ecpIsValid_deep(size_t n, size_t f_deep); . \pre Описание ec работоспособно. \return Признак корректности. - \remark Не проверяется, что порядок ec->base равняется ec->order. + \remark Не проверяется, что порядок базовой точки в ec->base равняется + ec->order. \deep{stack} ecpGroupSeemsValid_deep(ec->f->n, ec->f->deep). */ bool_t ecpGroupSeemsValid( diff --git a/src/crypto/bake/bake_bmqv.c b/src/crypto/bake/bake_bmqv.c index e9ce5be4..fe00446e 100644 --- a/src/crypto/bake/bake_bmqv.c +++ b/src/crypto/bake/bake_bmqv.c @@ -4,7 +4,7 @@ \brief STB 34.101.66 (bake): the BMQV protocol \project bee2 [cryptographic library] \created 2014.04.14 -\version 2025.09.26 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* diff --git a/src/crypto/bake/bake_bsts.c b/src/crypto/bake/bake_bsts.c index 7560195a..3d154863 100644 --- a/src/crypto/bake/bake_bsts.c +++ b/src/crypto/bake/bake_bsts.c @@ -4,7 +4,7 @@ \brief STB 34.101.66 (bake): the BSTS protocol \project bee2 [cryptographic library] \created 2014.04.14 -\version 2025.10.22 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -499,8 +499,8 @@ err_t bakeBSTSStep5(const octet in[], size_t in_len, bake_certval_i valb, ERR_CALL_CHECK(code); } // sb G + (2^l + t)Qa == Vb? - if (!ecAddMulA(Qb, s->ec, stack, 2, s->ec->base, sb, n, - Qb, s->t, n / 2 + 1)) + if (!ecAddMulA(Qb, s->ec, stack, 2, s->ec->base, sb, n, Qb, s->t, + n / 2 + 1)) return ERR_BAD_PARAMS; if (!wwEq(Qb, s->Vb, 2 * n)) return ERR_AUTH; diff --git a/src/crypto/bign/bign_ibs.c b/src/crypto/bign/bign_ibs.c index ae32a034..6c99750e 100644 --- a/src/crypto/bign/bign_ibs.c +++ b/src/crypto/bign/bign_ibs.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): identity-based signature \project bee2 [cryptographic library] \created 2012.04.27 -\version 2025.10.23 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -473,8 +473,8 @@ err_t bignIdVerifyEc(const ec_o* ec, const octet oid_der[], size_t oid_len, zzMod(t1, t1, n + 1, ec->order, n, stack); zzNegMod(t1, t1, ec->order, n); // V <- s1 G + (s0 + 2^l) R + t Q - if (!ecAddMulA(V, ec, stack, - 3, ec->base, s1, n, R, s0, n / 2 + 1, Q, t1, n)) + if (!ecAddMulA(V, ec, stack, 3, ec->base, s1, n, R, s0, n / 2 + 1, + Q, t1, n)) { blobClose(state); return ERR_BAD_SIG; diff --git a/src/math/ec.c b/src/math/ec.c index 5477554e..6319edf8 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2025.12.29 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -29,8 +29,6 @@ bool_t ecIsOperable2(const ec_o* ec) objKeep(ec) >= sizeof(ec_o) && objPCount(ec) == 6 && objOCount(ec) == 1 && - wwIsValid(ec->A, ec->f->n) && - wwIsValid(ec->B, ec->f->n) && ec->d >= 3 && ec->froma != 0 && ec->toa != 0 && @@ -47,11 +45,13 @@ bool_t ecIsOperable(const ec_o* ec) { return ecIsOperable2(ec) && qrIsOperable(ec->f) && + wwIsValid(ec->A, ec->f->n) && + wwIsValid(ec->B, ec->f->n) && ec->deep >= ec->f->deep; } bool_t ecGroupCreate(ec_o* ec, const octet xbase[], const octet ybase[], - const octet order[], size_t order_len, u32 cofactor, void* stack) + const octet order[], size_t order_len, size_t cofactor, void* stack) { ASSERT(ecIsOperable(ec)); ASSERT(memIsValid(order, order_len)); @@ -62,7 +62,7 @@ bool_t ecGroupCreate(ec_o* ec, const octet xbase[], const octet ybase[], if (order_len == 0 || W_OF_O(order_len) > ec->f->n + 1 || cofactor == 0 || - (u32)(word)cofactor != cofactor) + (size_t)(word)cofactor != cofactor) return FALSE; // установить базовую точку if (xbase == 0) @@ -73,6 +73,7 @@ bool_t ecGroupCreate(ec_o* ec, const octet xbase[], const octet ybase[], qrSetZero(ecY(ec->base, ec->f->n), ec->f); else if (!qrFrom(ecY(ec->base, ec->f->n), ybase, ec->f, stack)) return FALSE; + ec->w = 1; // установить порядок и кофактор wwFrom(ec->order, order, order_len); wwSetZero(ec->order + W_OF_O(order_len), @@ -90,7 +91,8 @@ size_t ecGroupCreate_deep(size_t f_deep) bool_t ecGroupIsOperable(const ec_o* ec) { ASSERT(ecIsOperable(ec)); - return wwIsValid(ec->base, 2 * ec->f->n) && + return ec->w >= 1 && + wwIsValid(ec->base, 2 * (SIZE_1 << (ec->w - 1)) * ec->f->n) && wwIsValid(ec->order, ec->f->n + 1) && !wwIsZero(ec->order, ec->f->n + 1) && ec->cofactor != 0; @@ -98,25 +100,75 @@ bool_t ecGroupIsOperable(const ec_o* ec) /* ******************************************************************************* -Кратная точка +Малые нечетные кратные +******************************************************************************* +*/ + +#define ecSmul_local(n, ec_d)\ +/* t */ O_OF_W(ec_d * n) + +void ecSmul(word b[], const word a[], size_t w, const ec_o* ec, void* stack) +{ + word* t; /* [ec->d * ec->f->n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(w > 0); + ASSERT(wwIsDisjoint2(b, ec->d * ec->f->n * (SIZE_1 << (w - 1)), + a, 2 * ec->f->n) || a == b); + // разметить стек + memSlice(stack, + ecSmul_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + // вычислить малые кратные + if (w > 1) + { + size_t i; + // t <- 2 a + ecDblA(t, a, ec, stack); + // b[1] <- t + a + ecAddA(b + ec->d * ec->f->n, t, a, ec, stack); + // b[i] <- t + b[i - 1] + for (i = 2; i < SIZE_1 << (w - 1); ++i) + ecAdd(b + i * ec->d * ec->f->n, t, b + (i - 1) * ec->d * ec->f->n, + ec, stack); + } + // b[0] <- a + ecFromA(b, a, ec, stack); +} + +size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecSmul_local(n, ec_d), + ec_deep, + SIZE_MAX); +} + +/* +******************************************************************************* +Кратная точка: NAF Для определения b = da (d-кратное точки a) используется оконный NAF с -длиной окна w. В функции ecMulWNAF() реализован алгоритм 3.35 из +длиной окна w. В функции ecMulNAF() реализован алгоритм 3.35 из [Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004]. -Предварительно рассчитываются малые кратные a: сначала 2a, а затем -точки a[i] = a[i - 1] + 2a, i = 1,\ldots, 2^{w - 1} - 2, где a[0] = a. +Предварительно рассчитываются малые нечетные кратные a: + b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. +Схема расчета по умолчанию: + t <- 2a, b[0] <- a, b[i] <- b[i-1] + t, i = 1, 2,... +Схема реализована в функции ecSmul(). При использовании проективных координат имеются три стратегии: 1) w = 2 и малые кратные вообще не рассчитываются; 2) w > 2 и малые кратные рассчитываются в аффинных координатах; 3) w > 2 и малые кратные рассчитываются в проективных координатах. -Средняя общая сложность нахождения кратной точки (l = wwBitSize(d)): +Средняя общая сложность нахождения кратной точки (l = wwBitSize(d), схема расчета +малых кратных по умолчанию): 1) c1(l) = l/3(P <- P + A); -2) c2(l, w) = 1(A <- 2A) + (2^{w-2} - 2)(A <- A + A) + l/(w + 1)(P <- P + A); -3) c3(l, w) = 1(P <- 2A) + (2^{w-2} - 2)(P <- P + P) + l/(w + 1)(P <- P + P), +2) c2(l, w) = 1(A <- 2A) + (2^{w-2} - 1)(A <- A + A) + l/(w + 1)(P <- P + A); +3) c3(l, w) = 1(P <- 2A) + (2^{w-2} - 1)(P <- P + P) + l/(w + 1)(P <- P + P), без учета общего во всех стратегиях слагаемого l(P <- 2P). Здесь @@ -129,7 +181,6 @@ Springer, 2004]. ----------------------------------------------------- * [или прямых вычислений в аффинных координатах] - В практических диапазонах размерностей при использовании наиболее эффективных координат (якобиевых для кривых над GF(p) и Лопеса -- Дахаба для кривых над GF(2^m)) первые две стратегии являются проигрышными. Реализована только третья @@ -137,7 +188,7 @@ GF(2^m)) первые две стратегии являются проигры Оптимальная длина окна выбирается как решение следующей оптимизационной задачи: - (2^{w - 2} - 2) + l / (w + 1) -> min. + (2^{w - 2} - 1) + l / (w + 1) -> min. \todo Усилить вторую стратегию. Рассчитать малые кратные в проективных координатах, а затем быстро перейти к аффинным координатам с помощью @@ -163,15 +214,14 @@ static size_t ecNAFWidth(size_t l) return 3; } -#define ecMulA_local(n, ec_d, m, naf_count)\ +#define ecMulNAF_local(n, ec_d, m, naf_count)\ /* naf */ O_OF_W(2 * m + 1),\ /* t */ O_OF_W(ec_d * n),\ /* pre */ O_OF_W(naf_count * ec_d * n) -bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], +bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], size_t m, void* stack) { - const size_t n = ec->f->n; const size_t naf_width = ecNAFWidth(B_OF_W(m)); const size_t naf_count = SIZE_1 << (naf_width - 2); const word naf_hi = WORD_1 << (naf_width - 1); @@ -179,13 +229,13 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], register size_t i; register word w; word* naf; /* [2 * m + 1] NAF */ - word* t; /* [ec->d * n] вспомогательная точка */ - word* pre; /* [naf_count * ec->d * n] pre[i] = (2i + 1)a */ + word* t; /* [ec->d * ec->f->n] вспомогательная точка */ + word* pre; /* [naf_count * ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); // разметить стек memSlice(stack, - ecMulA_local(n, ec->d, m, naf_count), SIZE_0, SIZE_MAX, + ecMulNAF_local(ec->f->n, ec->d, m, naf_count), SIZE_0, SIZE_MAX, &naf, &t, &pre, &stack); // расчет NAF ASSERT(naf_width >= 3); @@ -193,18 +243,15 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], // d == O => b <- O if (naf_size == 0) return FALSE; - // pre[0] <- a - ecFromA(pre, a, ec, stack); - // расчет pre[i]: t <- 2a, pre[i] <- t + pre[i - 1] - ASSERT(naf_count > 1); - ecDblA(t, pre, ec, stack); - ecAddA(pre + ec->d * n, t, pre, ec, stack); - for (i = 2; i < naf_count; ++i) - ecAdd(pre + i * ec->d * n, t, pre + (i - 1) * ec->d * n, ec, stack); + // малые кратные + if (ec->smul) + ec->smul(pre, a, naf_width - 1, ec, stack); + else + ecSmul(pre, a, naf_width - 1, ec, stack); // t <- a[naf[l - 1]] w = wwGetBits(naf, 0, naf_width); ASSERT((w & 1) == 1 && (w & naf_hi) == 0); - wwCopy(t, pre + (w >> 1) * ec->d * n, ec->d * n); + wwCopy(t, pre + (w >> 1) * ec->d * ec->f->n, ec->d * ec->f->n); // цикл по символам NAF i = naf_width; while (--naf_size) @@ -220,9 +267,10 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], else if (w == (naf_hi ^ 1)) ecSubA(t, t, pre, ec, stack); else if (w & naf_hi) - ecSub(t, t, pre + ((w ^ naf_hi) >> 1) * ec->d * n, ec, stack); + ecSub(t, t, pre + ((w ^ naf_hi) >> 1) * ec->d * ec->f->n, + ec, stack); else - ecAdd(t, t, pre + (w >> 1) * ec->d * n, ec, stack); + ecAdd(t, t, pre + (w >> 1) * ec->d * ec->f->n, ec, stack); // к следующему разряду naf i += naf_width; } @@ -235,12 +283,12 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], return ecToA(b, t, ec, stack); } -size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); const size_t naf_count = SIZE_1 << (naf_width - 2); return memSliceSize( - ecMulA_local(n, ec_d, m, naf_count), + ecMulNAF_local(n, ec_d, m, naf_count), ec_deep, SIZE_MAX); } @@ -257,11 +305,10 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) bool_t ecHasOrderA(const word a[], const ec_o* ec, const word q[], size_t m, void* stack) { - const size_t n = ec->f->n; - word* b; /* [ec->d * n] */ + word* b; /* [ec->d * ec->f->n] */ // разметить стек memSlice(stack, - ecHasOrderA_local(n, ec->d), SIZE_0, SIZE_MAX, + ecHasOrderA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &b, &stack); // q a == O? return !ecMulA(b, a, ec, q, m, stack); @@ -301,11 +348,10 @@ Elliptic Curve Cryptography, Springer, 2004] (interleaving with NAF). bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) { - const size_t n = ec->f->n; register word w; size_t i, naf_max_size = 0; va_list args; - word* t; /* [ec->d * n] проективная точка */ + word* t; /* [ec->d * ec->f->n] проективная точка */ size_t* m; /* [k] длины d[i] */ size_t* naf_width; /* [k] размеры NAF-окон */ size_t* naf_size; /* [k] длины NAF */ @@ -317,7 +363,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) ASSERT(k > 0); // разметить стек memSlice(stack, - ecAddMulA_local(n, ec->d), SIZE_0, SIZE_MAX, + ecAddMulA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &m, &naf_width, &naf_size, &naf_pos, &naf, &pre, &stack); // обработать параметры (a[i], d[i], m[i]) va_start(args, k); @@ -325,7 +371,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) { const word* a; const word* d; - size_t naf_count, j; + size_t naf_count; // a <- a[i] a = va_arg(args, const word*); // d <- d[i] @@ -338,7 +384,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) naf_width[i] = ecNAFWidth(B_OF_W(m[i])); naf_count = SIZE_1 << (naf_width[i] - 2); memSlice(stack, - O_OF_W(2 * m[i] + 1), O_OF_W(ec->d * n * naf_count), SIZE_0, + O_OF_W(2 * m[i] + 1), O_OF_W(ec->d * ec->f->n * naf_count), SIZE_0, SIZE_MAX, naf + i, pre + i, &stack); // расчет naf[i] @@ -346,15 +392,11 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) if (naf_size[i] > naf_max_size) naf_max_size = naf_size[i]; naf_pos[i] = 0; - // pre[i][0] <- a[i] - ecFromA(pre[i], a, ec, stack); - // расчет pre[i][j]: t <- 2a[i], pre[i][j] <- t + pre[i][j - 1] - ASSERT(naf_count > 1); - ecDblA(t, pre[i], ec, stack); - ecAddA(pre[i] + ec->d * n, t, pre[i], ec, stack); - for (j = 2; j < naf_count; ++j) - ecAdd(pre[i] + j * ec->d * n, t, pre[i] + (j - 1) * ec->d * n, ec, - stack); + // малые кратные + if (ec->smul) + ec->smul(pre[i], a, naf_width[i] - 1, ec, stack); + else + ecSmul(pre[i], a, naf_width[i] - 1, ec, stack); } va_end(args); // t <- O @@ -384,9 +426,11 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) ecSubA(t, t, pre[i], ec, stack); else if (w & naf_hi) w ^= naf_hi, - ecSub(t, t, pre[i] + (w >> 1) * ec->d * n, ec, stack); + ecSub(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, + ec, stack); else - ecAdd(t, t, pre[i] + (w >> 1) * ec->d * n, ec, stack); + ecAdd(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, + ec, stack); // к следующему символу naf[i] naf_pos[i] += naf_width[i]; } diff --git a/src/math/ec2.c b/src/math/ec2.c index 97e91b34..3d783c0a 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2025.10.22 +\version 2025.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -57,8 +57,9 @@ #define ec2SeemsOnA(a, ec)\ (gf2IsIn(ecX(a), (ec)->f) && gf2IsIn(ecY(a, (ec)->f->n), (ec)->f)) -#define ec2SeemsOn3(a, ec)\ - (ec2SeemsOnA(a, ec) && gf2IsIn(ecZ(a, (ec)->f->n), (ec)->f)) +#define ec2SeemsOnLD(a, ec)\ + (ecIsO(a, ec) ||\ + (ec2SeemsOnA(a, ec) && gf2IsIn(ecZ(a, (ec)->f->n), (ec)->f))) /* ******************************************************************************* @@ -145,7 +146,7 @@ static bool_t ec2ToALD(word b[], const word a[], const ec_o* ec, void* stack) word* t1; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(a == b || wwIsDisjoint2(a, 3 * n, b, 2 * n)); // разметить стек memSlice(stack, @@ -184,7 +185,7 @@ static void ec2NegLD(word b[], const word a[], const ec_o* ec, void* stack) word* t1; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // разметить стек memSlice(stack, @@ -217,7 +218,7 @@ static void ec2DblLD(word b[], const word a[], const ec_o* ec, void* stack) word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // разметить стек memSlice(stack, @@ -344,8 +345,8 @@ static void ec2AddLD(word c[], const word a[], const word b[], word* t6; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); - ASSERT(ec2SeemsOn3(b, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); + ASSERT(ec2SeemsOnLD(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); // разметить стек @@ -448,7 +449,7 @@ static void ec2AddALD(word c[], const word a[], const word b[], word* t4; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(ec2SeemsOnA(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); @@ -539,8 +540,8 @@ static void ec2SubLD(word c[], const word a[], const word b[], word* t; /* [3 * n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); - ASSERT(ec2SeemsOn3(b, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); + ASSERT(ec2SeemsOnLD(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); // разметить стек @@ -577,7 +578,7 @@ static void ec2SubALD(word c[], const word a[], const word b[], word* t; /* [2 * n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOn3(a, ec)); + ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(ec2SeemsOnA(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); @@ -716,8 +717,7 @@ bool_t ec2GroupSeemsValid(const ec_o* ec, void* stack) ec2GroupSeemsValid_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); // ecGroupIsOperable(ec)? base \in ec? - if (!ecGroupIsOperable(ec) || - !ec2IsOnA(ec->base, ec, stack)) + if (!ecGroupIsOperable(ec) || !ec2IsOnA(ec->base, ec, stack)) return FALSE; // [n + 1]t1 <- 2^m wwSetZero(t1, n + 1); diff --git a/src/math/ecp.c b/src/math/ecp.c index caf5224e..83366dad 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2025.12.29 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -51,8 +51,9 @@ #define ecpSeemsOnA(a, ec)\ (zmIsIn(ecX(a), (ec)->f) && zmIsIn(ecY(a, (ec)->f->n), (ec)->f)) -#define ecpSeemsOn3(a, ec)\ - (ecpSeemsOnA(a, ec) && zmIsIn(ecZ(a, (ec)->f->n), (ec)->f)) +#define ecpSeemsOnJ(a, ec)\ + (ecIsO(a, ec) ||\ + (ecpSeemsOnA(a, ec) && zmIsIn(ecZ(a, (ec)->f->n), (ec)->f))) /* ******************************************************************************* @@ -158,7 +159,7 @@ static bool_t ecpToAJ(word b[], const word a[], const ec_o* ec, void* stack) word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(a == b || wwIsDisjoint2(a, 3 * n, b, 2 * n)); // разметить стек memSlice(stack, @@ -195,7 +196,7 @@ static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) const size_t n = ec->f->n; // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // xb <- xa qrCopy(ecX(b), ecX(a), ec->f); @@ -217,7 +218,7 @@ static void ecpDblJ(word b[], const word a[], const ec_o* ec, void* stack) word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // разметить стек memSlice(stack, @@ -291,7 +292,7 @@ static void ecpDblJA3(word b[], const word a[], const ec_o* ec, void* stack) word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // разметить стек memSlice(stack, @@ -448,8 +449,8 @@ static void ecpAddJ(word c[], const word a[], const word b[], const ec_o* ec, word* t4; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); - ASSERT(ecpSeemsOn3(b, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnJ(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); // разметить стек @@ -566,7 +567,7 @@ static void ecpAddAJ(word c[], const word a[], const word b[], const ec_o* ec, word* t4; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(ecpSeemsOnA(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); @@ -652,8 +653,8 @@ static void ecpSubJ(word c[], const word a[], const word b[], const ec_o* ec, word* t; /* [3 * n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); - ASSERT(ecpSeemsOn3(b, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnJ(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); // разметить стек @@ -687,7 +688,7 @@ static void ecpSubAJ(word c[], const word a[], const word b[], const ec_o* ec, word* t; /* [2 * n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(ecpSeemsOnA(b, ec)); ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); @@ -734,7 +735,7 @@ static void ecpTplJ(word b[], const word a[], const ec_o* ec, void* stack) word* t7; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // разметить стек memSlice(stack, @@ -832,7 +833,7 @@ static void ecpTplJA3(word b[], const word a[], const ec_o* ec, void* stack) word* t7; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOn3(a, ec)); + ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); // разметить стек memSlice(stack, @@ -1086,8 +1087,7 @@ bool_t ecpGroupSeemsValid(const ec_o* ec, void* stack) ecpGroupSeemsValid_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); // ecGroupIsOperable(ec) == TRUE? base \in ec? - if (!ecGroupIsOperable(ec) || - !ecpIsOnA(ec->base, ec, stack)) + if (!ecGroupIsOperable(ec) || !ecpIsOnA(ec->base, ec, stack)) return FALSE; // [n + 2]t1 <- order * cofactor t1[n + 1] = zzMulW(t1, ec->order, n + 1, ec->cofactor); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 67ec0bce..97bb94ea 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2025.09.15 +\version 2026.01.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -38,7 +38,7 @@ static char xbase[] = "0000000000000000000000000000000000000000000000000000000000000000"; static char ybase[] = "B0E9804939D7C2E931D4CE052CCC6B6B692514CCADBA44940484EEA5F52D9268"; -static u32 cofactor = 1; +static size_t cofactor = 1; /* ******************************************************************************* From 1d8f3c311af3cc323d9305a9268062eb34163337 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 12 Jan 2026 18:53:47 +0300 Subject: [PATCH 09/77] Refactor NAF methods --- include/bee2/math/ec.h | 94 +++------------------ include/bee2/math/ww.h | 60 ++++++++------ src/math/ec.c | 182 +++++++++++++++++++---------------------- src/math/ec2.c | 84 +------------------ src/math/ecp.c | 81 +----------------- src/math/ww.c | 31 ++++--- test/math/ww_test.c | 8 +- 7 files changed, 161 insertions(+), 379 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index c94561ce..b14b6cc0 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.08 +\version 2026.01.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -104,7 +104,7 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). Эллиптическая кривая должна создаваться специальной функцией ecCreate(), которая является аналогом конструктора и реализуется разными способами в -завсимости от типа кривой. В функцию ecCreate() должен передаваться +зависимости от типа кривой. В функцию ecCreate() должен передаваться указатель ec на структуру типа ec_o. По этому указателю будет размещаться описание (состояние) кривой. По адресу ec может быть зарезервировано больше памяти, чем sizeof(ec_o). Дело в том, что коэффициенты A и B, @@ -152,8 +152,10 @@ struct ec_o; \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на кривой. - \return TRUE, если преобразование успешно выполнено, и FALSE в противном + \return TRUE, если преобразование успешно выполнено, и FALSE в противном случае. + \post Точка, составленная из двух первых координат b, совпадает с a и, + таким образом, является аффинной точкой кривой. */ typedef bool_t (*ec_froma_i)( word b[], /*!< [out] выходная точка */ @@ -245,50 +247,6 @@ typedef void (*ec_adda_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Вычитание точек - - На эллиптической кривой ec определяется разность [ec->d * ec->f->n]c точек - [ec->d * ec->f->n]a и [ec->d * ec->f->n]b: - \code - c <- a - b. - \endcode - \pre Описание ec работоспособно. - \pre Буфер с либо не пересекается с каждым из буферов a и b, либо - совпадает с некоторым из них. - \pre Координаты a и b лежат в базовом поле. - \expect Описание ec корректно. - \expect Точки a и b лежат на кривой. -*/ -typedef void (*ec_sub_i)( - word c[], /*!< [out] разность */ - const word a[], /*!< [in] уменьшаемое */ - const word b[], /*!< [in] вычитаемое */ - const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ - void* stack /*!< [in] вспомогательная память */ -); - -/*! \brief Вычитание аффинной точки - - На эллиптической кривой ec определяется разность [ec->d * ec->f->n]c - проективной точки [ec->d * ec->f->n]a и аффинной точки [2 * ec->f->n]b: - \code - c <- a - b. - \endcode - \pre Описание ec работоспособно. - \pre Буфер с либо не пересекается с каждым из буферов a и b, либо - указатель c совпадает с некоторым из указателей a или b. - \pre Координаты a и b лежат в базовом поле. - \expect Описание ec корректно. - \expect Точки a и b лежат на кривой. -*/ -typedef void (*ec_suba_i)( - word c[], /*!< [out] разность */ - const word a[], /*!< [in] уменьшаемое */ - const word b[], /*!< [in] вычитаемое */ - const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ - void* stack /*!< [in] вспомогательная память */ -); - /*! \brief Удвоение точки На эллиптической кривой ec определяется точка [ec->d * ec->f->n]b, @@ -356,14 +314,17 @@ typedef void (*ec_tpl_i)( аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере [2^{w-1} * ec->d * ec->f->n]b: \code - b <- (a, 3a, ..., (2^w - 1)a). + (b[0], ..., b[2^{w-1} - 1]) <- (a, 3a, ..., (2^w - 1)a). \endcode + Каждое кратное занимает ec->d * ec->f->n машинных слов. \pre w > 0. \pre Описание ec работоспособно. \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на кривой. + \post Точка, составленная из двух первых координат b[0], совпадает с a и, + таким образом, является аффинной точкой кривой. */ typedef void (*ec_smul_i)( word b[], /*!< [out] малые кратные */ @@ -400,8 +361,6 @@ typedef struct ec_o ec_neg_i neg; /*!< функция обращения */ ec_add_i add; /*!< функция сложения */ ec_adda_i adda; /*!< функция сложения с аффинной точкой */ - ec_sub_i sub; /*!< функция вычитания */ - ec_suba_i suba; /*!< функция вычитания аффинной точки */ ec_dbl_i dbl; /*!< функция удвоения */ ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ @@ -565,33 +524,6 @@ bool_t ecGroupIsOperable( ******************************************************************************* */ -/*! \brief Малые нечетные кратные - - На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных - аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере - [2^{w-1} * ec->d * ec->f->n]b: - \code - b <- (a, 3a, ..., (2^w - 1)a). - \endcode - \pre w > 0. - \pre Описание ec работоспособно. - \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. - \pre Координаты a лежат в базовом поле. - \expect Описание ec корректно. - \expect Точка a лежит на кривой. - \deep{stack} ecSmul_deep(ec->f->n, ec->d, ec->deep). - \remark Реализация интерфейса ec_smul_i. -*/ -void ecSmul( - word b[], /*!< [out] малые кратные */ - const word a[], /*!< [in] базовая точка */ - size_t w, /*!< [in] длина окна */ - const ec_o* ec, /*!< [in] описание кривой */ - void* stack /*!< [in] вспомогательная память */ -); - -size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep); - /*! \brief Кратная точка Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, @@ -608,7 +540,7 @@ size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep); \deep{stack} ecMulA_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция нерегулярна. */ -bool_t ecMulNAF( +bool_t ecMul0( word b[], /*!< [out] кратная точка */ const word a[], /*!< [in] базовая точка */ const ec_o* ec, /*!< [in] описание кривой */ @@ -617,10 +549,10 @@ bool_t ecMulNAF( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -#define ecMulA ecMulNAF -#define ecMulA_deep ecMulNAF_deep +#define ecMulA ecMul0 +#define ecMulA_deep ecMul0_deep /*! \brief Имеет порядок? diff --git a/include/bee2/math/ww.h b/include/bee2/math/ww.h index 5b49ad04..691d2a65 100644 --- a/include/bee2/math/ww.h +++ b/include/bee2/math/ww.h @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2025.09.23 +\version 2026.01.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -355,7 +355,7 @@ bool_t wwTestBit( \pre По адресу a зарезервировано W_OF_B(pos + width) машинных слов. \pre width <= B_PER_W. \return Машинное слово, составленное из разрядов a с номерами - pos (младший), pos + 1,..., pos + width -1 (старший). + pos (младший), pos + 1,..., pos + width - 1 (старший). */ word wwGetBits( const word a[], /*!< [in] слово */ @@ -439,39 +439,45 @@ size_t wwBitSize( /*! \brief Расчет NAF - В [2n + 1]naf помещается кодированное представление оптимальной знаковой - формы (NAF) слова [n]a. Рассчитывается оконная NAF с размером окна w. - \remark NAF(a, w) представляет собой последовательность символов - (a_0, a_1,..., a_{l-1}) такую, что + В [2n + 1]naf помещается кодированное представление оконной оптимальной + знаковой формы (NAF) слова [n]a. NAF представляет собой последовательность + цифр (a_0, a_1,..., a_{l-1}) такую, что - a_i \in {0, \pm 1, \pm 3, ..., \pm 2^{w-1} - 1}; - если a != 0, то a_{l-1} != 0; - a как число равняется \sum {i=0}^{l-1} a_i 2^i; - - среди любых w последовательных символов a_i только один ненулевой. + - среди любых w последовательных цифр a_i только одна ненулевая. . - \remark Известно, что l - длина NAF - не превосходит wwBitSize(a) + 1. - \remark Кодирование символов состоит в следующем - (\ -- двоичная запись числа b): + Подробнее см. [Hankerson D., Menezes A., Vanstone S. Guide to Elliptic + Curve Cryptography, Springer, 2004]. + \pre 2 <= w < B_PER_W. + \pre Буфер naf не пересекается с буфером a. + \return Длина naf (число цифр l). + \safe Функция нерегулярна. + \remark Длина NAF не превосходит wwBitSize(a) + 1. + \remark Кодирование цифр NAF состоит в следующем (\bin(b) -- двоичная + запись числа b): - нулевые a_i представляются одним двоичным символом 0; - - положительные a_i представляются w двоичными символами 0\; - - отрицательные a_i представляются w двоичными символами 1\<|a_i|>; + - положительные a_i представляются w битами 0\bin(a_i); + - отрицательные a_i представляются w битами 1\bin(|a_i|); - кодированное представление - это конкатенация кода a_{l-1} - (первые символы), ...., кода a_1, кода a_0 (последние символы). + (первые биты), ...., кода a_1, кода a_0 (последние биты). + . + \remark Для кодирования w последовательных цифр потребуется не + более (w - 1) * 1 + 1 * w = 2 * w - 1 битов. Поэтому при w < B_PER_W для + хранения всего кодового представления потребуется не более 2 * n + 1 слов. + \remark Декодирование NAF выполняется с помощью окна длины w: + - если слово в окне заканчивается нулевым битом, то декодируется цифра 0 + и окно смещается на 1 позицию вправо; + - в зависимости от того, начинается слово в окне нулевым или единичным + битом, декодируется положительная или отрицательная цифра и окно + смещается на w позиций вправо. . - \remark Для кодирования w последовательных элементов a_i потребуется - не более (w - 1) * 1 + 1 * w = 2 * w - 1 битов. Поэтому при w < B_PER_W - для хранения всего кодового представления потребуется - не более 2 * n + 1 слов. - \remark Если при расчете NAF получен суффикс \alpha, 0,..., 0, 1, - в котором w - 1 нулей и \alpha < 0, то этот суффикс заменяется на - \beta, 0,..., 0, 1, в котором w - 2 нулей и - \beta = 2^{w - 1} + \alpha > 0. - Суффиксы описывают одинаковые числа: + \remark Суффикс старших разрядов (\alpha, 0,..., 0, 1), в котором w - 1 нулей + и цифра \alpha < 0, заменяется на суффикс (\beta, 0,..., 0, 1), в котором + w - 2 нулей и цифра \beta = 2^{w - 1} + \alpha > 0. Замена корректна, так как + суффиксы описывают одинаковые числа: 2^w + \alpha = 2^{w - 1} + \beta. - Длина второго суффикса и соответствующей NAF на единицу меньше. - \pre 2 <= w < B_PER_W. - \pre Буфер naf не пересекается с буфером a. - \return Размер naf (число символов l). - \safe Функция нерегулярна. + При этом длина второго суффикса и соответствующей NAF-формы на единицу меньше. */ size_t wwNAF( word naf[], /*!< [out] знаковая форма */ diff --git a/src/math/ec.c b/src/math/ec.c index 6319edf8..3d34c0c6 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.08 +\version 2026.01.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -35,8 +35,6 @@ bool_t ecIsOperable2(const ec_o* ec) ec->neg != 0 && ec->add != 0 && ec->adda != 0 && - ec->sub != 0 && - ec->suba != 0 && ec->dbl != 0 && ec->dbla != 0; } @@ -101,13 +99,20 @@ bool_t ecGroupIsOperable(const ec_o* ec) /* ******************************************************************************* Малые нечетные кратные + +Простейшая реализация интерфейса ec_smul_i: + t <- 2a, b[0] <- a, b[i] <- t + b[i - 1], i = 1,..., 2^(w-1) - 1. + +Сложение t + b[0] выполняется по схеме P <- P + A. Остальные сложения -- по +схеме P <- P + P. ******************************************************************************* */ #define ecSmul_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -void ecSmul(word b[], const word a[], size_t w, const ec_o* ec, void* stack) +static void ecSmul(word b[], const word a[], size_t w, const ec_o* ec, + void* stack) { word* t; /* [ec->d * ec->f->n] */ // pre @@ -136,7 +141,7 @@ void ecSmul(word b[], const word a[], size_t w, const ec_o* ec, void* stack) ecFromA(b, a, ec, stack); } -size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( ecSmul_local(n, ec_d), @@ -149,57 +154,30 @@ size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) Кратная точка: NAF Для определения b = da (d-кратное точки a) используется оконный NAF с -длиной окна w. В функции ecMulNAF() реализован алгоритм 3.35 из -[Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, -Springer, 2004]. +длиной окна w > 2. В функции ecMul0() реализован алгоритм 3.36 из [1]. -Предварительно рассчитываются малые нечетные кратные a: +Используются малые нечетные кратные a: b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. -Схема расчета по умолчанию: - t <- 2a, b[0] <- a, b[i] <- b[i-1] + t, i = 1, 2,... -Схема реализована в функции ecSmul(). - -При использовании проективных координат имеются три стратегии: -1) w = 2 и малые кратные вообще не рассчитываются; -2) w > 2 и малые кратные рассчитываются в аффинных координатах; -3) w > 2 и малые кратные рассчитываются в проективных координатах. - -Средняя общая сложность нахождения кратной точки (l = wwBitSize(d), схема расчета -малых кратных по умолчанию): -1) c1(l) = l/3(P <- P + A); -2) c2(l, w) = 1(A <- 2A) + (2^{w-2} - 1)(A <- A + A) + l/(w + 1)(P <- P + A); -3) c3(l, w) = 1(P <- 2A) + (2^{w-2} - 1)(P <- P + P) + l/(w + 1)(P <- P + P), -без учета общего во всех стратегиях слагаемого l(P <- 2P). +Они вычисляются с помощью функции интерфейса ec_smul_i, указанной в описании +кривой. Если искомая функция не задана в описании, то используется функция +по умолчанию ecSmul(). + +Пусть l = wwBitSize(d). Если расчет малых кратных выполняется с помощью +функции ecSmul(), то средняя сложность нахождения кратной точки: + c(l, w) = 1(P <- 2A) + 1(P <- P + A) + (2^{w-2} - 2)(P <- P + P) + + l/(w + 1)(P <- P + P) + l(P <- 2P). Здесь -- (P <- P + A) -- время работы функций ec->adda / ec->suba; -- (A <- 2A) -- время работы каскада (ec->dbla, ec->toa)*; -- (A <- A + A) -- время работы каскада (ec->adda, ec->toa)*; - (P <- 2A) -- время работы функции ec->dbla; +- (P <- P + A) -- время работы функций ec->adda / ec->suba; - (P <- P + P) -- время работы функции ec->add / ec->sub; - (P <- 2P) -- время работы функции ec->dbl. ------------------------------------------------------ -* [или прямых вычислений в аффинных координатах] - -В практических диапазонах размерностей при использовании наиболее эффективных -координат (якобиевых для кривых над GF(p) и Лопеса -- Дахаба для кривых над -GF(2^m)) первые две стратегии являются проигрышными. Реализована только третья -стратегия. - -Оптимальная длина окна выбирается как решение следующей оптимизационной -задачи: - (2^{w - 2} - 1) + l / (w + 1) -> min. - -\todo Усилить вторую стратегию. Рассчитать малые кратные в проективных -координатах, а затем быстро перейти к аффинным координатам с помощью -трюка Монтгомери [Algorithm 11.15 Simultaneous inversion, CohenFrey, p. 209]: - U_1 <- Z_1 - for t = 2,..., T: U_t <- U_{t-1} Z_t - V <- U_T^{-1} - for t = T,..., 2: - Z_t^{-1} <- V U_{t-1} - V <- V Z_t - Z_1^{-1} <- V + +Длина окна w выбирается как решение следующей оптимизационной задачи: + (2^{w - 2} - 2) + l / (w + 1) -> min. + +[1] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, +Springer, 2004. ******************************************************************************* */ @@ -214,17 +192,17 @@ static size_t ecNAFWidth(size_t l) return 3; } -#define ecMulNAF_local(n, ec_d, m, naf_count)\ +#define ecMulNAF_local(n, ec_d, m, pre_count)\ /* naf */ O_OF_W(2 * m + 1),\ /* t */ O_OF_W(ec_d * n),\ -/* pre */ O_OF_W(naf_count * ec_d * n) +/* pre */ O_OF_W(pre_count * ec_d * n) -bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], +bool_t ecMul0(word b[], const word a[], const ec_o* ec, const word d[], size_t m, void* stack) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); - const size_t naf_count = SIZE_1 << (naf_width - 2); - const word naf_hi = WORD_1 << (naf_width - 1); + const word naf_hi = WORD_BIT_POS(naf_width - 1); + const size_t pre_count = SIZE_1 << (naf_width - 1); register size_t naf_size; register size_t i; register word w; @@ -235,7 +213,7 @@ bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], ASSERT(ecIsOperable(ec)); // разметить стек memSlice(stack, - ecMulNAF_local(ec->f->n, ec->d, m, naf_count), SIZE_0, SIZE_MAX, + ecMulNAF_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, &naf, &t, &pre, &stack); // расчет NAF ASSERT(naf_width >= 3); @@ -248,6 +226,10 @@ bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], ec->smul(pre, a, naf_width - 1, ec, stack); else ecSmul(pre, a, naf_width - 1, ec, stack); + // отрицательные малые кратные + for (i = 0; i < pre_count / 2; ++i) + ec->neg(pre + (pre_count / 2 + i) * ec->d * ec->f->n, + pre + i * ec->d * ec->f->n, ec, stack); // t <- a[naf[l - 1]] w = wwGetBits(naf, 0, naf_width); ASSERT((w & 1) == 1 && (w & naf_hi) == 0); @@ -256,26 +238,23 @@ bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], i = naf_width; while (--naf_size) { + // t <- 2 t + ecDbl(t, t, ec, stack); + // обработать цифру naf w = wwGetBits(naf, i, naf_width); if (w & 1) { - // t <- 2 t - ecDbl(t, t, ec, stack); - // t <- t \pm pre[naf[w]] - if (w == 1) - ecAddA(t, t, pre, ec, stack); - else if (w == (naf_hi ^ 1)) - ecSubA(t, t, pre, ec, stack); - else if (w & naf_hi) - ecSub(t, t, pre + ((w ^ naf_hi) >> 1) * ec->d * ec->f->n, - ec, stack); + // t <- t + pre[w / 2] + if (w == 1 || w == (naf_hi ^ 1)) + ecAddA(t, t, pre + (w >> 1) * ec->d * ec->f->n, ec, stack); else ecAdd(t, t, pre + (w >> 1) * ec->d * ec->f->n, ec, stack); - // к следующему разряду naf + // к следующей цифре i += naf_width; } else - ecDbl(t, t, ec, stack), ++i; + // к следующей цифре + ++i; } // очистка CLEAN2(w, i); @@ -283,13 +262,13 @@ bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], return ecToA(b, t, ec, stack); } -size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); - const size_t naf_count = SIZE_1 << (naf_width - 2); + const size_t pre_count = SIZE_1 << (naf_width - 1); return memSliceSize( - ecMulNAF_local(n, ec_d, m, naf_count), - ec_deep, + ecMulNAF_local(n, ec_d, m, pre_count), + ecSmul_deep(n, ec_d, ec_deep), SIZE_MAX); } @@ -326,8 +305,7 @@ size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ******************************************************************************* Сумма кратных точек -Реализован алгоритм 3.51 [Hankerson D., Menezes A., Vanstone S. Guide to -Elliptic Curve Cryptography, Springer, 2004] (interleaving with NAF). +Реализован алгоритм 3.51 (interleaving with NAF) из [1]. Для каждого d[i] строится naf[i] длиной l[i] с шириной окна w[i]. @@ -337,7 +315,7 @@ Elliptic Curve Cryptography, Springer, 2004] (interleaving with NAF). ******************************************************************************* */ -#define ecAddMulA_local(n, ec_d)\ +#define ecAddMulA_local(n, ec_d, k)\ /* t */ O_OF_W(ec_d * n),\ /* m */ O_PER_S * k,\ /* naf_width */ O_PER_S * k,\ @@ -348,8 +326,9 @@ Elliptic Curve Cryptography, Springer, 2004] (interleaving with NAF). bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) { + register size_t naf_max_size; register word w; - size_t i, naf_max_size = 0; + size_t i; va_list args; word* t; /* [ec->d * ec->f->n] проективная точка */ size_t* m; /* [k] длины d[i] */ @@ -363,15 +342,17 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) ASSERT(k > 0); // разметить стек memSlice(stack, - ecAddMulA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecAddMulA_local(ec->f->n, ec->d, k), SIZE_0, SIZE_MAX, &t, &m, &naf_width, &naf_size, &naf_pos, &naf, &pre, &stack); // обработать параметры (a[i], d[i], m[i]) va_start(args, k); + naf_max_size = 0; for (i = 0; i < k; ++i) { const word* a; const word* d; - size_t naf_count; + size_t pre_count; + size_t j; // a <- a[i] a = va_arg(args, const word*); // d <- d[i] @@ -382,9 +363,9 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) m[i] = wwWordSize(d, m[i]); // зарезервировать память для naf[i] и pre[i] naf_width[i] = ecNAFWidth(B_OF_W(m[i])); - naf_count = SIZE_1 << (naf_width[i] - 2); + pre_count = SIZE_1 << (naf_width[i] - 1); memSlice(stack, - O_OF_W(2 * m[i] + 1), O_OF_W(ec->d * ec->f->n * naf_count), SIZE_0, + O_OF_W(2 * m[i] + 1), O_OF_W(ec->d * ec->f->n * pre_count), SIZE_0, SIZE_MAX, naf + i, pre + i, &stack); // расчет naf[i] @@ -397,6 +378,10 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) ec->smul(pre[i], a, naf_width[i] - 1, ec, stack); else ecSmul(pre[i], a, naf_width[i] - 1, ec, stack); + // отрицательные малые кратные + for (j = 0; j < pre_count / 2; ++j) + ec->neg(pre[i] + (pre_count / 2 + j) * ec->d * ec->f->n, + pre[i] + j * ec->d * ec->f->n, ec, stack); } va_end(args); // t <- O @@ -410,36 +395,32 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) for (i = 0; i < k; ++i) { word naf_hi; - // символы naf[i] не начались? + // цифры naf[i] не начались? if (naf_size[i] < naf_max_size) continue; - // прочитать очередной символ naf[i] + // прочитать очередную цифру naf[i] w = wwGetBits(naf[i], naf_pos[i], naf_width[i]); - // обработать символ - naf_hi = WORD_1 << (naf_width[i] - 1); + // обработать цифру + naf_hi = WORD_BIT_POS(naf_width[i] - 1); if (w & 1) { - // t <- t \pm pre[i][naf[i][w]] - if (w == 1) - ecAddA(t, t, pre[i], ec, stack); - else if (w == (naf_hi ^ 1)) - ecSubA(t, t, pre[i], ec, stack); - else if (w & naf_hi) - w ^= naf_hi, - ecSub(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, - ec, stack); + // t <- t + pre[i][w / 2] + if (w == 1 || w == (naf_hi ^ 1)) + ecAddA(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, ec, + stack); else - ecAdd(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, - ec, stack); - // к следующему символу naf[i] + ecAdd(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, ec, + stack); + // к следующей цифре naf[i] naf_pos[i] += naf_width[i]; } else + // к следующей цифре ++naf_pos[i]; } } // очистка - CLEAN(w); + CLEAN2(naf_max_size, w); // к аффинным координатам return ecToA(b, t, ec, stack); } @@ -449,19 +430,20 @@ size_t ecAddMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t k, ...) size_t i, ret; va_list args; ret = memSliceSize( - ecAddMulA_local(n, ec_d), SIZE_0, SIZE_MAX); + ecAddMulA_local(n, ec_d, k), + ecSmul_deep(n, ec_d, ec_deep), + SIZE_MAX); va_start(args, k); for (i = 0; i < k; ++i) { size_t m = va_arg(args, size_t); size_t naf_width = ecNAFWidth(B_OF_W(m)); - size_t naf_count = SIZE_1 << (naf_width - 2); + size_t pre_count = SIZE_1 << (naf_width - 1); ret += memSliceSize( O_OF_W(2 * m + 1), - O_OF_W(ec_d * n * naf_count), + O_OF_W(ec_d * n * pre_count), SIZE_0, SIZE_MAX); } va_end(args); - ret += ec_deep; return ret; } diff --git a/src/math/ec2.c b/src/math/ec2.c index 3d783c0a..89a1bbb0 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2025.01.08 +\version 2025.01.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -529,78 +529,6 @@ static size_t ec2AddALD_deep(size_t n, size_t f_deep) SIZE_MAX); } -#define ec2SubLD_local(n)\ -/* t */ O_OF_W(3 * n) - -// [3n]c <- [3n]a - [3n]b (P <- P - P) -static void ec2SubLD(word c[], const word a[], const word b[], - const ec_o* ec, void* stack) -{ - const size_t n = ec->f->n; - word* t; /* [3 * n] */ - // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOnLD(a, ec)); - ASSERT(ec2SeemsOnLD(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); - // разметить стек - memSlice(stack, - ec2SubLD_local(n), SIZE_0, SIZE_MAX, - &t, &stack); - // t <- -b - qrMul(ecY(t, n), ecX(b), ecZ(b, n), ec->f, stack); - gf2Add2(ecY(t, n), ecY(b, n), ec->f); - qrCopy(ecX(t), ecX(b), ec->f); - qrCopy(ecZ(t, n), ecZ(b, n), ec->f); - // c <- a + t - ec2AddLD(c, a, t, ec, stack); -} - -static size_t ec2SubLD_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ec2SubLD_local(n), - utilMax(2, - f_deep, - ec2AddLD_deep(n, f_deep)), - SIZE_MAX); -} - -#define ec2SubALD_local(n)\ -/* t */ O_OF_W(2 * n) - -// [3n]c <- [3n]a - [2n]b (P <- P - A) -static void ec2SubALD(word c[], const word a[], const word b[], - const ec_o* ec, void* stack) -{ - const size_t n = ec->f->n; - word* t; /* [2 * n] */ - // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ec2SeemsOnLD(a, ec)); - ASSERT(ec2SeemsOnA(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); - // разметить стек - memSlice(stack, - ec2SubALD_local(n), SIZE_0, SIZE_MAX, - &t, &stack); - // t <- -b - wwCopy(t, b, 2 * n); - gf2Add2(ecY(t, n), ecX(t), ec->f); - // c <- a + t - ec2AddALD(c, a, t, ec, stack); -} - -static size_t ec2SubALD_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ec2SubALD_local(n), - ec2AddALD_deep(n, f_deep), - SIZE_MAX); -} - #define ec2CreateLD_state(n)\ /* A */ O_OF_W(n),\ /* B */ O_OF_W(n),\ @@ -634,17 +562,13 @@ bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->neg = ec2NegLD; ec->add = ec2AddLD; ec->adda = ec2AddALD; - ec->sub = ec2SubLD; - ec->suba = ec2SubALD; ec->dbl = ec2DblLD; ec->dbla = ec2DblALD; - ec->deep = utilMax(8, + ec->deep = utilMax(6, ec2ToALD_deep(f->n, f->deep), ec2NegLD_deep(f->n, f->deep), ec2AddLD_deep(f->n, f->deep), ec2AddALD_deep(f->n, f->deep), - ec2SubLD_deep(f->n, f->deep), - ec2SubALD_deep(f->n, f->deep), ec2DblLD_deep(f->n, f->deep), ec2DblALD_deep(f->n, f->deep)); // настроить заголовок @@ -664,13 +588,11 @@ size_t ec2CreateLD_keep(size_t n) size_t ec2CreateLD_deep(size_t n, size_t f_deep) { - return utilMax(8, + return utilMax(6, ec2ToALD_deep(n, f_deep), ec2NegLD_deep(n, f_deep), ec2AddLD_deep(n, f_deep), ec2AddALD_deep(n, f_deep), - ec2SubLD_deep(n, f_deep), - ec2SubALD_deep(n, f_deep), ec2DblLD_deep(n, f_deep), ec2DblALD_deep(n, f_deep)); } diff --git a/src/math/ecp.c b/src/math/ecp.c index 83366dad..0d8cbbeb 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.08 +\version 2026.01.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -642,75 +642,6 @@ static size_t ecpAddAJ_deep(size_t n, size_t f_deep) SIZE_MAX); } -#define ecpSubJ_local(n)\ -/* t */ O_OF_W(3 * n) - -// [3n]c <- [3n]a - [3n]b (P <- P - P) -static void ecpSubJ(word c[], const word a[], const word b[], const ec_o* ec, - void* stack) -{ - const size_t n = ec->f->n; - word* t; /* [3 * n] */ - // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(ecpSeemsOnJ(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); - // разметить стек - memSlice(stack, - ecpSubJ_local(n), SIZE_0, SIZE_MAX, - &t, &stack); - // t <- -b - qrCopy(ecX(t), ecX(b), ec->f); - zmNeg(ecY(t, n), ecY(b, n), ec->f); - qrCopy(ecZ(t, n), ecZ(b, n), ec->f); - // c <- a + t - ecpAddJ(c, a, t, ec, stack); -} - -static size_t ecpSubJ_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpSubJ_local(n), - ecpAddJ_deep(n, f_deep), - SIZE_MAX); -} - -#define ecpSubAJ_local(n)\ -/* t */ O_OF_W(2 * n) - -// [3n]c <- [3n]a - [2n]b (P <- P - A) -static void ecpSubAJ(word c[], const word a[], const word b[], const ec_o* ec, - void* stack) -{ - const size_t n = ec->f->n; - word* t; /* [2 * n] */ - // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(ecpSeemsOnA(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); - // разметить стек - memSlice(stack, - ecpSubAJ_local(n), SIZE_0, SIZE_MAX, - &t, &stack); - // t <- -b - qrCopy(ecX(t), ecX(b), ec->f); - zmNeg(ecY(t, n), ecY(b, n), ec->f); - // c <- a + t - ecpAddAJ(c, a, t, ec, stack); -} - -static size_t ecpSubAJ_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpSubAJ_local(n), - ecpAddAJ_deep(n, f_deep), - SIZE_MAX); -} - #define ecpTplJ_local(n)\ /* t0 */ O_OF_W(n),\ /* t1 */ O_OF_W(n),\ @@ -956,17 +887,13 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->neg = ecpNegJ; ec->add = ecpAddJ; ec->adda = ecpAddAJ; - ec->sub = ecpSubJ; - ec->suba = ecpSubAJ; ec->dbl = bA3 ? ecpDblJA3 : ecpDblJ; ec->dbla = ecpDblAJ; ec->tpl = bA3 ? ecpTplJA3 : ecpTplJ; - ec->deep = utilMax(8, + ec->deep = utilMax(6, ecpToAJ_deep(f->n, f->deep), ecpAddJ_deep(f->n, f->deep), ecpAddAJ_deep(f->n, f->deep), - ecpSubJ_deep(f->n, f->deep), - ecpSubAJ_deep(f->n, f->deep), bA3 ? ecpDblJA3_deep(f->n, f->deep) : ecpDblJ_deep(f->n, f->deep), ecpDblAJ_deep(f->n, f->deep), bA3 ? ecpTplJA3_deep(f->n, f->deep) : ecpTplJ_deep(f->n, f->deep)); @@ -990,13 +917,11 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) { return memSliceSize( ecpCreateJ_local(n), - utilMax(11, + utilMax(9, O_OF_W(n), ecpToAJ_deep(n, f_deep), ecpAddJ_deep(n, f_deep), ecpAddAJ_deep(n, f_deep), - ecpSubJ_deep(n, f_deep), - ecpSubAJ_deep(n, f_deep), ecpDblJ_deep(n, f_deep), ecpDblJA3_deep(n, f_deep), ecpDblAJ_deep(n, f_deep), diff --git a/src/math/ww.c b/src/math/ww.c index 9ec59450..e38b4790 100644 --- a/src/math/ww.c +++ b/src/math/ww.c @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2025.07.24 +\version 2026.01.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -315,16 +315,23 @@ bool_t wwTestBit(const word a[], size_t pos) word wwGetBits(const word a[], size_t pos, size_t width) { register word ret; - size_t n = pos / B_PER_W; + size_t n; + // pre ASSERT(wwIsValid(a, W_OF_B(pos + width))); + ASSERT(width <= B_PER_W); + // уточнить позицию + n = pos / B_PER_W; pos %= B_PER_W; - // биты a[n] - ret = a[n] >> pos; - // биты a[n + 1] - if (pos + width > B_PER_W) - ret |= a[n + 1] << (B_PER_W - pos); + // определить биты + if (pos) + { + ret = a[n] >> pos; + if (pos + width > B_PER_W) + ret |= a[n + 1] << (B_PER_W - pos); + } + else + ret = a[n]; // только width битов - ASSERT(width <= B_PER_W); if (width < B_PER_W) ret &= WORD_BIT_POS(width) - 1; return ret; @@ -409,21 +416,23 @@ size_t wwNAF(word naf[], const word a[], size_t n, size_t w) const word mask = hi_bit - 1; register word window; register word digit; - register size_t naf_len = 0; - register size_t naf_size = 0; - register size_t a_len = wwBitSize(a, n); + register size_t naf_len; + register size_t naf_size; + register size_t a_len; size_t i; // pre ASSERT(wwIsDisjoint2(a, n, naf, 2 * n + 1)); ASSERT(2 <= w && w < B_PER_W); // naf <- 0 wwSetZero(naf, 2 * n + 1); + naf_len = naf_size = 0; // a == 0? if (wwIsZero(a, n)) return 0; // window <- a mod 2^w window = wwGetBits(a, 0, w); // расчет NAF + a_len = wwBitSize(a, n); for (i = w; window || i < a_len; ++i) { // ненулевой символ? diff --git a/test/math/ww_test.c b/test/math/ww_test.c index bc6c9366..9186189f 100644 --- a/test/math/ww_test.c +++ b/test/math/ww_test.c @@ -1,6 +1,6 @@ /* ******************************************************************************* -\file u64_test.c +\file ww_test.c \brief Tests for operations on arbitrary length words \project bee2/test \created 2023.03.31 @@ -105,6 +105,12 @@ bool_t wwTest() (wwShLo(a + 2, 4, 2 * B_PER_W - 1), FALSE) || !wwIsRepW(a + 2, 2, 0x5C)) return FALSE; + // NAF + a[0] = 255; + if (wwNAF(b, a, 1, 2) != 9 || b[0] != 0x0601 || + wwNAF(b, a, 1, 3) != 9 || b[0] != 0x1401 || + wwNAF(b, a, 1, 4) != 9 || b[0] != 0x4801) + return FALSE; // все нормально return TRUE; } From 06746a0509b519d6b535517cfe0c6c3f3fcc7244 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 16 Jan 2026 18:04:07 +0300 Subject: [PATCH 10/77] Add ecMulSNZ() --- include/bee2/math/ec.h | 90 +++++++++-- include/bee2/math/zz.h | 27 +++- src/math/ec.c | 329 ++++++++++++++++++++++++++++++++++------- src/math/ww.c | 12 +- src/math/zz/zz_mod.c | 32 +++- test/math/ecp_bench.c | 31 +++- test/math/zz_test.c | 16 +- 7 files changed, 452 insertions(+), 85 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index b14b6cc0..20ec1dbc 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.12 +\version 2026.01.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -173,8 +173,7 @@ typedef bool_t (*ec_froma_i)( \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на кривой. - \return TRUE, если аффинная точка построена, и FALSE, если точке a - соответствует бесконечно удаленная точка. + \return TRUE, если аффинная точка построена, и FALSE, если нет (a == O). */ typedef bool_t (*ec_toa_i)( word b[], /*!< [out] аффинная точка */ @@ -308,7 +307,7 @@ typedef void (*ec_tpl_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Расчет малых нечетных кратных +/*! \brief Малые нечетные кратные На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере @@ -334,6 +333,66 @@ typedef void (*ec_smul_i)( void* stack /*!< [in] вспомогательная память */ ); +/*! \brief Сложение с изменением знака + + На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, + которая является суммой точек [ec->d * ec->f->n]a и [ec->d * ec->f->n]b + при neg == 0, либо обратной к этой сумме при neg == 1: + \code + c <- (-1)^neg (a + b). + \endcode + \pre Описание ec работоспособно. + \pre Буфер с либо не пересекается с каждым из буферов a и b, либо совпадает + с некоторым из них. + \pre Координаты a и b лежат в базовом поле. + \pre neg == 0 || neg == 1. + \expect Описание ec корректно. + \expect Точки a и b лежат на кривой. + \expect a + b != O. + \expect Используется полная (complete) формула сложения, которая одинаково + работает с a == b и a != b. + \remark Функции интерфейса ec_pmaadd_i используются в регулярных алгоритмах + вычисления кратной точки. +*/ +typedef void(*ec_pmaadd_i)( + word c[], /*!< [out] сумма */ + const word a[], /*!< [in] первое слагаемое */ + const word b[], /*!< [in] второе слагаемое */ + register word neg, /*!< [in] флаг изменения знака */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +/*! \brief Сложение с аффинной точкой с изменением знака + + На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, + которая является суммой точки [ec->d * ec->f->n]a и аффинной точки + [2 * ec->f->n]b при neg == 0, либо обратной к этой сумме при neg == 1: + \code + c <- (-1)^neg (a + b). + \endcode + \pre Описание ec работоспособно. + \pre Буфер с либо не пересекается с каждым из буферов a и b, либо указатель + c совпадает с некоторым из указателей a или b. + \pre Координаты a и b лежат в базовом поле. + \pre neg == 0 || neg == 1. + \expect Описание ec корректно. + \expect Точки a и b лежат на кривой. + \expect a + b != O. + \expect Используется полная (complete) формула сложения, которая одинаково + работает с a == b и a != b. + \remark Функции интерфейса ec_pmaadda_i используются в регулярных + алгоритмах вычисления кратной точки. +*/ +typedef void(*ec_pmaadda_i)( + word c[], /*!< [out] сумма */ + const word a[], /*!< [in] первое слагаемое */ + const word b[], /*!< [in] второе слагаемое */ + register word neg, /*!< [in] флаг изменения знака */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + /*! \brief Описание эллиптической кривой Описывается эллиптическая кривая, правила представления ее элементов, @@ -353,8 +412,6 @@ typedef struct ec_o void* params; /*!< дополнительные параметры */ // } size_t d; /*!< размерность */ - size_t max_w; /*!< максимальная ширина окна */ - size_t w; /*!< ширина окна */ word cofactor; /*!< кофактор группы точек */ ec_froma_i froma; /*!< функция импорта из аффинной точки */ ec_toa_i toa; /*!< функция экспорта в аффинную точку */ @@ -365,6 +422,8 @@ typedef struct ec_o ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ ec_smul_i smul; /*!< функция расчета малых кратных */ + ec_pmaadd_i pmaadd; /*!< функция сложения c настройкой знака */ + ec_pmaadda_i pmaadda; /*!< функция сложения с афф. т. с настр. знака */ size_t deep; /*!< максимальная глубина стека функций */ mem_align_t descr[]; /*!< память для размещения данных */ } ec_o; @@ -486,7 +545,7 @@ bool_t ecIsOperable2( \expect Описание ec корректно. \return TRUE, если описание группы успешно создано, и FALSE в противном случае. - \remark Любой из указателей xbase, basey может быть нулевым. При нулевом + \remark Любой из указателей xbase, ybase может быть нулевым. При нулевом указателе соответствующая координата базовой точки устанавливается равной нулю. \deep{stack} ecGroupCreate_deep(f_deep). @@ -538,9 +597,16 @@ bool_t ecGroupIsOperable( \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. \deep{stack} ecMulA_deep(ec->f->n, ec->d, ec->deep, m). - \safe Функция нерегулярна. + \safe Функция регулярна по [m]d при выполнении следующих условий: + - описание группы точек в ec работоспособно; + - ec->order --- нечетное число; + - длина ec->order в машинных словах равняется m; + - m > 1; + - 0 < d && d < ec->order; + - ec->pmaadd != 0; + - функции ec->add, ec->dbl и ec->pmaadd регулярны. */ -bool_t ecMul0( +bool_t ecMulA( word b[], /*!< [out] кратная точка */ const word a[], /*!< [in] базовая точка */ const ec_o* ec, /*!< [in] описание кривой */ @@ -549,10 +615,7 @@ bool_t ecMul0( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); - -#define ecMulA ecMul0 -#define ecMulA_deep ecMul0_deep +size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); /*! \brief Имеет порядок? @@ -567,6 +630,7 @@ size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); может быть ошибочно признана точкой порядка q. \return Признак успеха проверки. \deep{stack} ecHasOrderA_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция нерегулярна. */ bool_t ecHasOrderA( const word a[], /*!< [in] точка */ diff --git a/include/bee2/math/zz.h b/include/bee2/math/zz.h index 254de085..2a39b8b5 100644 --- a/include/bee2/math/zz.h +++ b/include/bee2/math/zz.h @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers \project bee2 [cryptographic library] \created 2012.04.22 -\version 2025.07.24 +\version 2026.01.15 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -733,7 +733,7 @@ void zzSubWMod( void FAST(zzSubWMod)(word b[], const word a[], register word w, const word mod[], size_t n); -/*! \brief Аддитивное обращение чисел по модулю +/*! \brief Аддитивное обращение числа по модулю Определяется число [n]b, аддитивно обратное к [n]a по модулю [n]mod: \code @@ -754,6 +754,29 @@ void zzNegMod( void FAST(zzNegMod)(word b[], const word a[], const word mod[], size_t n); +/*! \brief Настройка знака числа по модулю + + Определяется число [n]b, которое совпадает с [n]a при neg == 0 и аддитивно + обратно к [n]a по модулю [n]mod при neg == 1: + \code + b <- (-1)^neg a \mod mod. + \endcode + \pre n > 0 && mod[n - 1] != 0. + \pre a < mod. + \pre Буфер b либо не пересекается, либо совпадает с буфером a. + \pre Буфер b не пересекается с буфером mod. + \pre neg == 0 || neg == 1. + \remark Функция используется при регуляризации. + +*/ +void zzPmMod( + word b[], /*!< [in,out] +- число */ + const word a[], /*!< [in] число */ + const word mod[], /*!< [in] модуль */ + size_t n, /*!< [in] длина чисел в машинных словах */ + register word neg /*!< [in] флаг изменения знака */ +); + /*! \brief Умножение чисел по модулю Определяется произведение [n]c чисел [n]a и [n]b по модулю [n]mod: diff --git a/src/math/ec.c b/src/math/ec.c index 3d34c0c6..0bfe94e3 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.12 +\version 2026.01.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -16,6 +16,7 @@ #include "bee2/core/word.h" #include "bee2/math/ec.h" #include "bee2/math/ww.h" +#include "bee2/math/zz.h" /* ******************************************************************************* @@ -71,7 +72,6 @@ bool_t ecGroupCreate(ec_o* ec, const octet xbase[], const octet ybase[], qrSetZero(ecY(ec->base, ec->f->n), ec->f); else if (!qrFrom(ecY(ec->base, ec->f->n), ybase, ec->f, stack)) return FALSE; - ec->w = 1; // установить порядок и кофактор wwFrom(ec->order, order, order_len); wwSetZero(ec->order + W_OF_O(order_len), @@ -89,8 +89,7 @@ size_t ecGroupCreate_deep(size_t f_deep) bool_t ecGroupIsOperable(const ec_o* ec) { ASSERT(ecIsOperable(ec)); - return ec->w >= 1 && - wwIsValid(ec->base, 2 * (SIZE_1 << (ec->w - 1)) * ec->f->n) && + return wwIsValid(ec->base, 2 * ec->f->n) && wwIsValid(ec->order, ec->f->n + 1) && !wwIsZero(ec->order, ec->f->n + 1) && ec->cofactor != 0; @@ -100,11 +99,17 @@ bool_t ecGroupIsOperable(const ec_o* ec) ******************************************************************************* Малые нечетные кратные -Простейшая реализация интерфейса ec_smul_i: +Прямая реализация интерфейса ec_smul_i: t <- 2a, b[0] <- a, b[i] <- t + b[i - 1], i = 1,..., 2^(w-1) - 1. Сложение t + b[0] выполняется по схеме P <- P + A. Остальные сложения -- по -схеме P <- P + P. +схеме P <- P + P. Итоговая сложность: + 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). + +Здесь и далее +- (P <- 2A) -- время работы функции ec->dbla; +- (P <- P + A) -- время работы функций ec->adda; +- (P <- P + P) -- время работы функции ec->add. ******************************************************************************* */ @@ -151,16 +156,74 @@ static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) /* ******************************************************************************* -Кратная точка: NAF +Сложение с настройкой знака + +Прямые нерегулярные реализации интерфейсов ec_pmaadd_i и ec_pmaadda_i. +******************************************************************************* +*/ + +#define ecPmAAdd_local(n, ec_d)\ +/* t */ O_OF_W(ec_d * n) + +static void ecPmAAdd(word c[], const word a[], const word b[], + register word neg, const ec_o* ec, void* stack) +{ + word* t; /* [ec->d * ec->f->n] */ + ASSERT(ecIsOperable(ec)); + memSlice(stack, + ecPmAAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + ecAdd(t, a, b, ec, stack); + if (neg) + ecNeg(t, t, ec, stack); + (void)ecToA(c, t, ec, stack); +} + +static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecPmAAdd_local(n, ec_d), + ec_deep, + SIZE_MAX); +} -Для определения b = da (d-кратное точки a) используется оконный NAF с -длиной окна w > 2. В функции ecMul0() реализован алгоритм 3.36 из [1]. +#define ecPmAAddA_local(n, ec_d)\ +/* t */ O_OF_W(ec_d * n) + +/* static */ void ecPmAAddA(word c[], const word a[], const word b[], + register word neg, const ec_o* ec, void* stack) +{ + word* t; /* [ec->d * ec->f->n] */ + ASSERT(ecIsOperable(ec)); + memSlice(stack, + ecPmAAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + ecAddA(t, a, b, ec, stack); + if (neg) + ecNeg(t, t, ec, stack); + (void)ecToA(c, t, ec, stack); +} + +/* static */ size_t ecPmAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecPmAAddA_local(n, ec_d), + ec_deep, + SIZE_MAX); +} + +/* +******************************************************************************* +Кратная точка: NAF (Non-Adjacent Form) + +В функции ecMulNAF() для определения b = da (d-кратного точки a) применяется +оконный NAF с длиной окна w > 2. Реализован алгоритм 3.36 из [1]. Используются малые нечетные кратные a: b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. Они вычисляются с помощью функции интерфейса ec_smul_i, указанной в описании -кривой. Если искомая функция не задана в описании, то используется функция -по умолчанию ecSmul(). +кривой. Если функция не задана в описании, то используется функция по +умолчанию ecSmul(). Пусть l = wwBitSize(d). Если расчет малых кратных выполняется с помощью функции ecSmul(), то средняя сложность нахождения кратной точки: @@ -168,9 +231,6 @@ static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) l/(w + 1)(P <- P + P) + l(P <- 2P). Здесь -- (P <- 2A) -- время работы функции ec->dbla; -- (P <- P + A) -- время работы функций ec->adda / ec->suba; -- (P <- P + P) -- время работы функции ec->add / ec->sub; - (P <- 2P) -- время работы функции ec->dbl. Длина окна w выбирается как решение следующей оптимизационной задачи: @@ -197,18 +257,18 @@ static size_t ecNAFWidth(size_t l) /* t */ O_OF_W(ec_d * n),\ /* pre */ O_OF_W(pre_count * ec_d * n) -bool_t ecMul0(word b[], const word a[], const ec_o* ec, const word d[], - size_t m, void* stack) +bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, + const word d[], size_t m, void* stack) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); const word naf_hi = WORD_BIT_POS(naf_width - 1); const size_t pre_count = SIZE_1 << (naf_width - 1); register size_t naf_size; register size_t i; - register word w; + register word digit; word* naf; /* [2 * m + 1] NAF */ - word* t; /* [ec->d * ec->f->n] вспомогательная точка */ - word* pre; /* [naf_count * ec->d * ec->f->n] */ + word* t; /* [ec->d * ec->f->n] */ + word* pre; /* [pre_count * ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); // разметить стек @@ -218,37 +278,36 @@ bool_t ecMul0(word b[], const word a[], const ec_o* ec, const word d[], // расчет NAF ASSERT(naf_width >= 3); naf_size = wwNAF(naf, d, m, naf_width); - // d == O => b <- O - if (naf_size == 0) - return FALSE; - // малые кратные + ASSERT(naf_size > 0); + // малые кратные: a, 3a, ..., (2^w - 1)a if (ec->smul) ec->smul(pre, a, naf_width - 1, ec, stack); else ecSmul(pre, a, naf_width - 1, ec, stack); - // отрицательные малые кратные + // отрицательные малые кратные: -a, -3a, ..., -(2^w - 1)a for (i = 0; i < pre_count / 2; ++i) - ec->neg(pre + (pre_count / 2 + i) * ec->d * ec->f->n, + ecNeg(pre + (pre_count / 2 + i) * ec->d * ec->f->n, pre + i * ec->d * ec->f->n, ec, stack); - // t <- a[naf[l - 1]] - w = wwGetBits(naf, 0, naf_width); - ASSERT((w & 1) == 1 && (w & naf_hi) == 0); - wwCopy(t, pre + (w >> 1) * ec->d * ec->f->n, ec->d * ec->f->n); - // цикл по символам NAF + // старшая цифра NAF + digit = wwGetBits(naf, 0, naf_width); + ASSERT((digit & 1) == 1 && (digit & naf_hi) == 0); + // t <- pre[digit / 2] + wwCopy(t, pre + (digit >> 1) * ec->d * ec->f->n, ec->d * ec->f->n); + // цикл по цифрам NAF i = naf_width; while (--naf_size) { // t <- 2 t ecDbl(t, t, ec, stack); - // обработать цифру naf - w = wwGetBits(naf, i, naf_width); - if (w & 1) + // обработать цифру + digit = wwGetBits(naf, i, naf_width); + if (digit & 1) { - // t <- t + pre[w / 2] - if (w == 1 || w == (naf_hi ^ 1)) - ecAddA(t, t, pre + (w >> 1) * ec->d * ec->f->n, ec, stack); + // t <- t + pre[digit / 2] + if (digit == 1 || digit == (naf_hi ^ 1)) + ecAddA(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); else - ecAdd(t, t, pre + (w >> 1) * ec->d * ec->f->n, ec, stack); + ecAdd(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); // к следующей цифре i += naf_width; } @@ -257,12 +316,12 @@ bool_t ecMul0(word b[], const word a[], const ec_o* ec, const word d[], ++i; } // очистка - CLEAN2(w, i); + CLEAN2(digit, i); // к аффинным координатам return ecToA(b, t, ec, stack); } -size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +static size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); const size_t pre_count = SIZE_1 << (naf_width - 1); @@ -272,6 +331,172 @@ size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } +/* +******************************************************************************* +Кратная точка: SNZ (Signed Non-Zero) + +В функции ecMulSNZ() для определения b = da при нечетной кратности d +используется ее представление в виде + d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}, +где d_i \in {\pm 1, \pm 3, ..., \pm (2^w - 1)} -- ненулевые (нечетные) цифры +со знаком. Предполагается, что точка a лежит в группе нечетного порядка order, +(2^w)^{k-1} < order < (2^w)^k. Если кратность d четная, то она предварительно +меняется на order - d. + +Реализован следующий алгоритм (см. [2; p. 9-11, algorithm 1], а также [3]): +1. Для i = 0, 1, ..., (2^{w-1} - 1): + 1) pre[i] <- (2i - 1)a; // ec_smult_i + 2) pre[-i] <- -pre[i]. // ec_neg_i +2. t <- pre[d_{k-1} / 2] +3. Для i = k - 2, ..., 1: + 1) t <- t + pre[d_i / 2]; // ec_add_i + 2) t <- 2^w t. // ec_dbl_i +4. t <- t + pre[d_0]. // ec_aadd_i +5. Возвратить t. + +Реализация регулярна при условии, что на шаге 4 используется регулярная +функция интерфейса ec_aadd_i. + +\remark сложение t + pre[d_0] вынесено за рамки основного цикла на шаге 3, +потому что при этом (и только при этом) сложении может произойти исключительная +ситуация -- совпадение операндов t и pre[d_0] с переключением от сложения +к удвоению. Регулярность функция ec_aadd_i означает, что сложение и удвоение +будут выполняться по одним формулам. + +[2] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves +for Cryptography: An Efficiency and Security Analysis, 2014, +https://eprint.iacr.org/2014/130.pdf. + +[3] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication on +Weierstrass curves using division polynomials. Mat. Vopr. Kriptogr., +13:2, 2022, https://doi.org/10.4213/mvk406. +******************************************************************************* +*/ + +static size_t ecSNZWidth(size_t l) +{ + if (l <= 256) + return 5; + return 6; +} + +#define ecMulSNZ_local(n, ec_d, m, pre_count)\ +/* dd */ O_OF_W(m),\ +/* t */ O_OF_W(ec_d * n),\ +/* pre */ O_OF_W(pre_count * ec_d * n) + +bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, + const word d[], size_t m, void* stack) +{ + register word neg; + register word digit; + register word hi; + size_t mb; + size_t snz_width; + size_t pre_count; + size_t k; + size_t i; + word* dd; /* [m] */ + word* t; /* [ec->d * ec->f->n] */ + word* pre; /* [pre_count * ec->d * ec->f->n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); + ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(!wwIsZero(d, m) && wwCmp(d, ec->order, m) < 0); + // размерности + mb = wwBitSize(ec->order, m); + snz_width = ecSNZWidth(mb); + ASSERT(3 <= snz_width && snz_width < B_PER_W); + pre_count = SIZE_1 << snz_width; + // разметить стек + memSlice(stack, + ecMulSNZ_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, + &dd, &t, &pre, &stack); + // dd <- (d & 1) ? d : ec->order - d + neg = WORD_1 - (d[0] & 1); + zzPmMod(dd, d, ec->order, m, neg); + ASSERT(zzIsOdd(dd, m)); + // малые кратные: a, 3a, ..., (2^w - 1)a + if (ec->smul) + ec->smul(pre, a, snz_width, ec, stack); + else + ecSmul(pre, a, snz_width, ec, stack); + // отрицательные малые кратные: -(2^w - 1)a, ..., -3a, -a + for (i = 0; i < pre_count / 2; ++i) + ecNeg(pre + (pre_count - 1 - i) * ec->d * ec->f->n, + pre + i * ec->d * ec->f->n, ec, stack); + // число цифр SNZ + k = (mb + snz_width - 1) / snz_width; + ASSERT(k > 1); + // старшая цифра + --k; + digit = wwGetBits(dd, k * snz_width, mb - k * snz_width); + wwCopy(t, pre + (digit >> 1) * ec->d * ec->f->n, ec->d * ec->f->n); + hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + // обработать остальные цифры + while (--k) + { + for (i = snz_width; i--;) + ecDbl(t, t, ec, stack); + digit = wwGetBits(dd, k * snz_width, snz_width); + ecAdd(t, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, ec, stack); + hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + } + // завершающие удвоения и сложение с настройкой знака + for (i = snz_width; i--;) + ecDbl(t, t, ec, stack); + digit = wwGetBits(dd, 0, snz_width); + ASSERT(digit & 1); + if (ec->pmaadd) + ec->pmaadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, + neg, ec, stack); + else + ecPmAAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, + neg, ec, stack); + // очистка и возврат + CLEAN3(neg, digit, hi); + return TRUE; +} + +size_t ecMulSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + const size_t snz_width = ecSNZWidth(B_OF_W(m)); + const size_t pre_count = SIZE_1 << snz_width; + return memSliceSize( + ecMulSNZ_local(n, ec_d, m, pre_count), + utilMax(2, + ecSmul_deep(n, ec_d, ec_deep), + ecPmAAdd_deep(n, ec_d, ec_deep)), + SIZE_MAX); +} + +/* +******************************************************************************* +Кратная точка: общий случай +******************************************************************************* +*/ + +bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], + size_t m, void* stack) +{ + ASSERT(ecIsOperable(ec)); + if (ecGroupIsOperable(ec) && + wwWordSize(ec->order, ec->f->n + 1) == m && + zzIsOdd(ec->order, m) && m > 1 && + !wwIsZero(d, m) && wwCmp(d, ec->order, m) < 0) + return ecMulSNZ(b, a, ec, d, m, stack); + return ecMulNAF(b, a, ec, d, m, stack); +} + +size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return utilMax(2, + ecMulNAF_deep(n, ec_d, ec_deep, m), + ecMulSNZ_deep(n, ec_d, ec_deep, m)); +} + /* ******************************************************************************* Имеет порядок? @@ -279,25 +504,25 @@ size_t ecMul0_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) */ #define ecHasOrderA_local(n, ec_d)\ -/* b */ O_OF_W(ec_d * n) +/* t */ O_OF_W(ec_d * n) bool_t ecHasOrderA(const word a[], const ec_o* ec, const word q[], size_t m, void* stack) { - word* b; /* [ec->d * ec->f->n] */ + word* t; /* [ec->d * ec->f->n] */ // разметить стек memSlice(stack, ecHasOrderA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, - &b, &stack); + &t, &stack); // q a == O? - return !ecMulA(b, a, ec, q, m, stack); + return !ecMulNAF(t, a, ec, q, m, stack); } size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( ecHasOrderA_local(n, ec_d), - ecMulA_deep(n, ec_d, ec_deep, m), + ecMulNAF_deep(n, ec_d, ec_deep, m), SIZE_MAX); } @@ -327,7 +552,7 @@ size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) { register size_t naf_max_size; - register word w; + register word digit; size_t i; va_list args; word* t; /* [ec->d * ec->f->n] проективная точка */ @@ -399,17 +624,17 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) if (naf_size[i] < naf_max_size) continue; // прочитать очередную цифру naf[i] - w = wwGetBits(naf[i], naf_pos[i], naf_width[i]); + digit = wwGetBits(naf[i], naf_pos[i], naf_width[i]); // обработать цифру naf_hi = WORD_BIT_POS(naf_width[i] - 1); - if (w & 1) + if (digit & 1) { - // t <- t + pre[i][w / 2] - if (w == 1 || w == (naf_hi ^ 1)) - ecAddA(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, ec, + // t <- t + pre[i][digit / 2] + if (digit == 1 || digit == (naf_hi ^ 1)) + ecAddA(t, t, pre[i] + (digit / 2) * ec->d * ec->f->n, ec, stack); else - ecAdd(t, t, pre[i] + (w >> 1) * ec->d * ec->f->n, ec, + ecAdd(t, t, pre[i] + (digit / 2) * ec->d * ec->f->n, ec, stack); // к следующей цифре naf[i] naf_pos[i] += naf_width[i]; @@ -420,7 +645,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) } } // очистка - CLEAN2(naf_max_size, w); + CLEAN2(naf_max_size, digit); // к аффинным координатам return ecToA(b, t, ec, stack); } diff --git a/src/math/ww.c b/src/math/ww.c index e38b4790..ef325419 100644 --- a/src/math/ww.c +++ b/src/math/ww.c @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.01.12 +\version 2026.01.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -349,17 +349,19 @@ void wwSetBit(word a[], size_t pos, register bool_t val) void wwSetBits(word a[], size_t pos, size_t width, register word val) { - word mask = WORD_MAX; - size_t n = pos / B_PER_W; + word mask; + size_t n; ASSERT(wwIsValid(a, W_OF_B(pos + width))); ASSERT(width <= B_PER_W); // маска + mask = WORD_MAX; if (width < B_PER_W) { mask <<= B_PER_W - width; mask >>= B_PER_W - width; } // биты a[n] + n = pos / B_PER_W; pos %= B_PER_W; a[n] &= ~(mask << pos); a[n] ^= (val & mask) << pos; @@ -392,10 +394,10 @@ size_t wwLoZeroBits(const word a[], size_t n) size_t wwHiZeroBits(const word a[], size_t n) { - register size_t i = n; + register size_t i; ASSERT(wwIsValid(a, n)); // поиск старшего ненулевого машинного слова - while (i-- && a[i] == 0); + for (i = n; i-- && a[i] == 0;); // нулевое слово? if (i == SIZE_MAX) return n * B_PER_W; diff --git a/src/math/zz/zz_mod.c b/src/math/zz/zz_mod.c index b4792889..4e3841a8 100644 --- a/src/math/zz/zz_mod.c +++ b/src/math/zz/zz_mod.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: modular arithmetic \project bee2 [cryptographic library] \created 2012.04.22 -\version 2025.09.26 +\version 2026.01.15 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -34,7 +34,7 @@ void FAST(zzAddMod)(word c[], const word a[], const word b[], const word mod[], ASSERT(wwIsSameOrDisjoint(a, c, n)); ASSERT(wwIsSameOrDisjoint(b, c, n)); ASSERT(wwIsDisjoint(c, mod, n)); - ASSERT(wwCmp(a, mod, n) < 0 && wwCmp(b, mod, n) < 0); + ASSERT(FAST(wwCmp)(a, mod, n) < 0 && FAST(wwCmp)(b, mod, n) < 0); if (zzAdd(c, a, b, n) || FAST(wwCmp)(c, mod, n) >= 0) zzSub2(c, mod, n); } @@ -74,7 +74,7 @@ void FAST(zzAddWMod)(word b[], const word a[], register word w, { ASSERT(wwIsSameOrDisjoint(a, b, n)); ASSERT(wwIsDisjoint(b, mod, n)); - ASSERT(wwCmp(a, mod, n) < 0 && wwCmpW(mod, n, w) > 0); + ASSERT(FAST(wwCmp)(a, mod, n) < 0 && FAST(wwCmpW)(mod, n, w) > 0); // a + w >= mod => a + w - mod < mod if (zzAddW(b, a, n, w) || wwCmp(b, mod, n) > 0) zzSub2(b, mod, n); @@ -111,7 +111,7 @@ void FAST(zzSubMod)(word c[], const word a[], const word b[], const word mod[], ASSERT(wwIsSameOrDisjoint(a, c, n)); ASSERT(wwIsSameOrDisjoint(b, c, n)); ASSERT(wwIsDisjoint(c, mod, n)); - ASSERT(wwCmp(a, mod, n) < 0 && wwCmp(b, mod, n) < 0); + ASSERT(FAST(wwCmp)(a, mod, n) < 0 && FAST(wwCmp)(b, mod, n) < 0); // a < b => a - b + mod < mod if (zzSub(c, a, b, n)) zzAdd2(c, mod, n); @@ -136,7 +136,7 @@ void FAST(zzSubWMod)(word b[], const word a[], register word w, { ASSERT(wwIsSameOrDisjoint(a, b, n)); ASSERT(wwIsDisjoint(b, mod, n)); - ASSERT(wwCmp(a, mod, n) < 0 && wwCmpW(mod, n, w) > 0); + ASSERT(FAST(wwCmp)(a, mod, n) < 0 && FAST(wwCmpW)(mod, n, w) > 0); // a < w => a - w + mod < mod if (zzSubW(b, a, n, w)) zzAdd2(b, mod, n); @@ -183,6 +183,26 @@ void zzNegMod(word b[], const word a[], const word mod[], size_t n) CLEAN(mask); } +void zzPmMod(word b[], const word a[], const word mod[], size_t n, + register word neg) +{ + register word mask; + size_t i; + ASSERT(wwIsSameOrDisjoint(a, b, n)); + ASSERT(wwIsDisjoint(b, mod, n)); + ASSERT(wwCmp(a, mod, n) < 0); + ASSERT(neg == WORD_0 || neg == WORD_1); + // b <- neg ? -a : a + mask = WORD_0 - neg; + for (i = 0; i < n; ++i) + b[i] = a[i] ^ mask; + zzAddW2(b, n, neg); + // b <- (neg && b != 0) ? b + mod : b + mask &= (word)wwIsZero(b, n) - WORD_1; + zzAddAndW(b, mod, n, mask); + CLEAN2(neg, mask); +} + /* ******************************************************************************* Модулярная арифметика: мультипликативные операции @@ -351,7 +371,7 @@ void FAST(zzHalfMod)(word b[], const word a[], const word mod[], size_t n) ASSERT(wwIsSameOrDisjoint(a, b, n)); ASSERT(wwIsDisjoint(mod, b, n)); ASSERT(zzIsOdd(mod, n) && mod[n - 1] != 0); - ASSERT(wwCmp(a, mod, n) < 0); + ASSERT(FAST(wwCmp)(a, mod, n) < 0); // a -- нечетное? => b <- (a + mod) / 2 if (zzIsOdd(a, n)) { diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index d57b37c3..5e99d696 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2025.09.30 +\version 2026.01.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -18,6 +18,18 @@ #include #include +/* +******************************************************************************* +Композиционные элементы ecMulA() +******************************************************************************* +*/ + +extern bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], + size_t m, void* stack); + +extern bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, const word d[], + size_t m, void* stack); + /* ******************************************************************************* Потребности в стеке @@ -56,18 +68,27 @@ bool_t ecpBench() prngCOMBOStart(combo_state, utilNonce32()); // оценить число кратных точек в секунду { - const size_t reps = 1000; + const size_t reps = 500; size_t i; tm_ticks_t ticks; - // эксперимент + // эксперимент: ecMulNAF() + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecMulA(pt, ec->base, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf("ecpBench::ecMulNAF: %u cycles/mulpoint [%u mulpoints/sec]\n", + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + // эксперимент: ecMulSNZ() for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, ec->f->no, combo_state); ecMulA(pt, ec->base, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - // печать результатов - printf("ecpBench: %u cycles/mulpoint [%u mulpoints/sec]\n", + printf("ecpBench::ecMulSNZ: %u cycles/mulpoint [%u mulpoints/sec]\n", (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } diff --git a/test/math/zz_test.c b/test/math/zz_test.c index da83172e..da884542 100644 --- a/test/math/zz_test.c +++ b/test/math/zz_test.c @@ -4,7 +4,7 @@ \brief Tests for multiple-precision unsigned integers \project bee2/test \created 2014.07.15 -\version 2025.09.28 +\version 2026.01.15 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -287,12 +287,24 @@ static bool_t zzTestMod() FAST(zzSubWMod)(t1, t, b[0], mod, n); if (!FAST(wwEq)(t1, a, n)) return FALSE; - // zzNegMod + // zzNegMod / zzPmMod zzNegMod(t, a, mod, n); + zzPmMod(t1, a, mod, n, 1); + if (!wwEq(t, t1, n)) + return FALSE; zzAddMod(t1, t, a, mod, n); if (!wwIsZero(t1, n)) return FALSE; + zzPmMod(t, a, mod, n, 0); + if (!wwEq(t, a, n)) + return FALSE; zzNegMod(t1, t1, mod, n); + if (!wwIsZero(t1, n)) + return FALSE; + zzPmMod(t1, t1, mod, n, 0); + if (!wwIsZero(t1, n)) + return FALSE; + zzPmMod(t1, t1, mod, n, 1); if (!wwIsZero(t1, n)) return FALSE; // FAST(zzNegMod) From 432afb4b732332618510b0d1874fe0c740bfe803 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 16 Jan 2026 20:05:33 +0300 Subject: [PATCH 11/77] Start complete addition formulas in math/ecp --- include/bee2/math/ec.h | 6 +- src/math/ecp.c | 179 ++++++++++++++++++++++++++++++++++------- 2 files changed, 155 insertions(+), 30 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 20ec1dbc..13d374d1 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -333,7 +333,7 @@ typedef void (*ec_smul_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Сложение с изменением знака +/*! \brief Сложение с настройкой знака суммы На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, которая является суммой точек [ec->d * ec->f->n]a и [ec->d * ec->f->n]b @@ -363,7 +363,7 @@ typedef void(*ec_pmaadd_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Сложение с аффинной точкой с изменением знака +/*! \brief Сложение с аффинной точкой с настройкой знака суммы На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, которая является суммой точки [ec->d * ec->f->n]a и аффинной точки @@ -422,7 +422,7 @@ typedef struct ec_o ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ ec_smul_i smul; /*!< функция расчета малых кратных */ - ec_pmaadd_i pmaadd; /*!< функция сложения c настройкой знака */ + ec_pmaadd_i pmaadd; /*!< функция сложения c настройкой знака суммы */ ec_pmaadda_i pmaadda; /*!< функция сложения с афф. т. с настр. знака */ size_t deep; /*!< максимальная глубина стека функций */ mem_align_t descr[]; /*!< память для размещения данных */ diff --git a/src/math/ecp.c b/src/math/ecp.c index 0d8cbbeb..9de6d41b 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -57,7 +57,7 @@ /* ******************************************************************************* -Якобиевы координаты: +Якобиевы (Jacobian, J) координаты: x = X / Z^2, y = Y / Z^3, -(X : Y : Z) = (X : -Y : Z). @@ -92,6 +92,12 @@ add-2007-bl [Bernstein-Lange, 2007]. Сложность алгоритма: \todo Сравнить add-2007-bl с add-1998-cmo-2, сложность которого 12M + 4S + 6add + 1*2. +\todo При выполнении функции ecpAddAJ() может выясниться, что +складываются одинаковые точки. В этом случае вызывается функция +ecpDblAJ(). Подумать, как "помочь" этой функции, передав уже +рассчитанные значения, например, za^2. Аналогичное замечание касается +функции ecpAddJ(). + В функции ecpAddAJ() выполняется сложение P <- P + A. Реализован алгоритм madd-2004-hmv [Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004]. @@ -102,31 +108,13 @@ Guide to Elliptic Curve Cryptography, Springer, 2004]. tpl-2007-bl [Bernstein-Lange, 2007]. Сложность алгоритма 5M + 10S + 1*A + 15add + 2*4 + 1*6 + 1*8 + 1*16 + 1*3 \approx 15M. -В функции ecpTplJA3() выполняется утроение P <- 3P для случая A = -3. -Реализован алгоритм tpl-2007-bl-2 [Bernstein-Lange, 2007]. -Сложность алгоритма - 7M + 7S + 13add + 2*4 + 1*8 + 1*12 + 1*16 + 1*3 \approx 14M. - -Целевые функции ci(l), определенные в описании реализации ecMul() в ec.c, -принимают следующий вид: - c1(l) = l/3 11; - c2(l, w) = 103 + (2^{w-2} - 2)102 + l/(w + 1) 11; - c3(l, w) = 6 + (2^{w-2} - 2)16 + l/(w + 1) 16. - -Расчеты показывают, что - с1(l) <= min_w c3(l), l <= 81, - min_w c3(l, w) <= m in_w c2(l, w), l <= 899. -Поэтому для практически используемых размерностей l (192 <= l <= 571) -первые две стратегии являются проигрышными. Реализована только стратегия 3. - \todo Сравнить madd-2004-hmv с madd-2007-bl, сложность которого 7M + 4S + 9add + 1*4 + 3*2. -\todo При выполнении функции ecpAddAJ() может выясниться, что -складываются одинаковые точки. В этом случае вызывается функция -ecpDblAJ(). Подумать, как "помочь" этой функции, передав уже -рассчитанные значения, например, za^2. Аналогичное замечание касается -функции ecpAddJ(). +В функции ecpTplJA3() выполняется утроение P <- 3P для случая A = -3. +Реализован алгоритм tpl-2007-bl-2 [Bernstein-Lange, 2007]. +Сложность алгоритма + 7M + 7S + 13add + 2*4 + 1*8 + 1*12 + 1*16 + 1*3 \approx 14M. ******************************************************************************* */ @@ -836,6 +824,140 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) SIZE_MAX); } +/* +******************************************************************************* +Малые кратные +******************************************************************************* +*/ + +/* +******************************************************************************* +Сложение с настройкой знака суммы + +Использованы полные (complete) формулы сложения / удвоения из работы + +[RCB15] Renes J., Costello C., Batina L. Complete addition formulas for prime +order elliptic curves. 2015. https://eprint.iacr.org/2015/1060.pdf. + +В формулах используются однородные (Homogenious, H) координаты: + x = X / Z, y = Y / Z. + +Преобразование H <- J: + (X Z : Y : Z^3) <- (X : Y : Z). +******************************************************************************* +*/ + +#define ecpPmAAdd_local(n)\ +/* pt1 */ O_OF_W(3 * n),\ +/* pt2 */ O_OF_W(3 * n),\ +/* pt3 */ O_OF_W(3 * n),\ +/* B3 */ O_OF_W(n),\ +/* t0 */ O_OF_W(n),\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n),\ +/* t3 */ O_OF_W(n),\ +/* t4 */ O_OF_W(n),\ +/* t5 */ O_OF_W(n) + +// [2n]с <- (-1)^neg ([3n]a + [3n]b) (A <- J + J) +static void ecpPmAAdd(word c[], const word a[], const word b[], + register word neg, const ec_o* ec, void* stack) +{ + const size_t n = ec->f->n; + word* pt1; /* [3 * n] */ + word* pt2; /* [3 * n] */ + word* pt3; /* [3 * n] */ + word* B3; /* [n] */ + word* t0; /* [n] */ + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + word* t4; /* [n] */ + word* t5; /* [n] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnJ(b, ec)); + // разметить стек + memSlice(stack, + ecpPmAAdd_local(n), SIZE_0, SIZE_MAX, + &pt1, &pt2, &pt3, &B3, &t0, &t1, &t2, &t3, &t4, &t5, &stack); + // pt1 <- a (H <- J) + qrMul(ecX(pt1), ecX(a), ecZ(a, n), ec->f, stack); + wwCopy(ecY(pt1, n), ecY(a, n), n); + qrSqr(ecZ(pt1, n), ecZ(a, n), ec->f, stack); + qrMul(ecZ(pt1, n), ecZ(pt1, n), ecZ(a, n), ec->f, stack); + // pt2 <- b (H <- J) + qrMul(ecX(pt2), ecX(b), ecZ(b, n), ec->f, stack); + wwCopy(ecY(pt2, n), ecY(b, n), n); + qrSqr(ecZ(pt2, n), ecZ(b, n), ec->f, stack); + qrMul(ecZ(pt2, n), ecZ(pt2, n), ecZ(b, n), ec->f, stack); + // B3 <- 3B + qrAdd(B3, ec->B, ec->B, ec->f); + qrAdd(B3, B3, ec->B, ec->f); + // [RCB15; algorithm 1] + qrMul(t0, ecX(pt1), ecX(pt2), ec->f, stack); + qrMul(t1, ecY(pt1, n), ecY(pt2, n), ec->f, stack); + qrMul(t2, ecZ(pt1, n), ecZ(pt2, n), ec->f, stack); + qrAdd(t3, ecX(pt1), ecY(pt1, n), ec->f); + qrAdd(t4, ecX(pt2), ecY(pt2, n), ec->f); + qrMul(t3, t3, t4, ec->f, stack); + qrAdd(t4, t0, t1, ec->f); + qrSub(t3, t3, t4, ec->f); + qrAdd(t4, ecX(pt1), ecZ(pt1, n), ec->f); + qrAdd(t5, ecX(pt2), ecZ(pt2, n), ec->f); + qrMul(t4, t4, t5, ec->f, stack); + qrAdd(t5, t0, t2, ec->f); + qrSub(t4, t4, t5, ec->f); + qrAdd(t5, ecY(pt1, n), ecZ(pt1, n), ec->f); + qrAdd(ecX(pt3), ecY(pt2, n), ecZ(pt2, n), ec->f); + qrMul(t5, t5, ecX(pt3), ec->f, stack); + qrAdd(ecX(pt3), t1, t2, ec->f); + qrSub(t5, t5, ecX(pt3), ec->f); + qrMul(ecZ(pt3, n), ec->A, t4, ec->f, stack); + qrMul(ecX(pt3), B3, t2, ec->f, stack); + qrAdd(ecZ(pt3, n), ecX(pt3), ecZ(pt3, n), ec->f); + qrSub(ecX(pt3), t1, ecZ(pt3, n), ec->f); + qrAdd(ecZ(pt3, n), t1, ecZ(pt3, n), ec->f); + qrMul(ecY(pt3, n), ecX(pt3), ecZ(pt3, n), ec->f, stack); + qrAdd(t1, t0, t0, ec->f); + qrAdd(t1, t1, t0, ec->f); + qrMul(t2, ec->A, t2, ec->f, stack); + qrMul(t4, B3, t4, ec->f, stack); + qrAdd(t1, t1, t2, ec->f); + qrSub(t2, t0, t2, ec->f); + qrMul(t2, ec->A, t2, ec->f, stack); + qrAdd(t4, t4, t2, ec->f); + qrMul(t0, t1, t4, ec->f, stack); + qrAdd(ecY(pt3, n), ecY(pt3, n), t0, ec->f); + qrMul(t0, t5, t4, ec->f, stack); + qrMul(ecX(pt3), t3, ecX(pt3), ec->f, stack); + qrSub(ecX(pt3), ecX(pt3), t0, ec->f); + qrMul(t0, t3, t1, ec->f, stack); + qrMul(ecZ(pt3, n), t5, ecZ(pt3, n), ec->f, stack); + qrAdd(ecZ(pt3, n), ecZ(pt3, n), t0, ec->f); + // c <- pt3 (A <- H) + qrInv(t0, ecZ(pt3, n), ec->f, stack); + qrMul(ecX(c), ecX(pt3), t0, ec->f, stack); + qrMul(ecY(c, n), ecY(pt3, n), t0, ec->f, stack); + // c <- (-1)^neg c + zzPmMod(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); +} + +static size_t ecpPmAAdd_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpPmAAdd_local(n), + f_deep, + SIZE_MAX); +} + +/* +******************************************************************************* +Создание кривой +******************************************************************************* +*/ + #define ecpCreateJ_state(n)\ /* A */ O_OF_W(n),\ /* B */ O_OF_W(n),\ @@ -890,13 +1012,15 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->dbl = bA3 ? ecpDblJA3 : ecpDblJ; ec->dbla = ecpDblAJ; ec->tpl = bA3 ? ecpTplJA3 : ecpTplJ; - ec->deep = utilMax(6, + ec->pmaadd = ecpPmAAdd; + ec->deep = utilMax(7, ecpToAJ_deep(f->n, f->deep), ecpAddJ_deep(f->n, f->deep), ecpAddAJ_deep(f->n, f->deep), bA3 ? ecpDblJA3_deep(f->n, f->deep) : ecpDblJ_deep(f->n, f->deep), ecpDblAJ_deep(f->n, f->deep), - bA3 ? ecpTplJA3_deep(f->n, f->deep) : ecpTplJ_deep(f->n, f->deep)); + bA3 ? ecpTplJA3_deep(f->n, f->deep) : ecpTplJ_deep(f->n, f->deep), + ecpPmAAdd_deep(f->n, f->deep)); // настроить ec->hdr.keep = sizeof(ec_o) + memSliceSize(ecpCreateJ_state(f->n), SIZE_MAX); @@ -917,7 +1041,7 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) { return memSliceSize( ecpCreateJ_local(n), - utilMax(9, + utilMax(10, O_OF_W(n), ecpToAJ_deep(n, f_deep), ecpAddJ_deep(n, f_deep), @@ -926,7 +1050,8 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) ecpDblJA3_deep(n, f_deep), ecpDblAJ_deep(n, f_deep), ecpTplJ_deep(n, f_deep), - ecpTplJA3_deep(n, f_deep)), + ecpTplJA3_deep(n, f_deep), + ecpPmAAdd_deep(n, f_deep)), SIZE_MAX); } From 3b3f3b46a1fab04af654b5e5b1eaefc5cb231dac Mon Sep 17 00:00:00 2001 From: agievich Date: Sun, 18 Jan 2026 19:47:13 +0300 Subject: [PATCH 12/77] Update interfaces in math/ec --- include/bee2/math/ec.h | 59 +++++++++++++---- include/bee2/math/zz.h | 5 +- src/math/ec.c | 138 +++++++++++++++++++-------------------- src/math/ecp.c | 142 +++++++++++++++++++++++------------------ src/math/zm.c | 6 +- src/math/zz/zz_gcd.c | 4 +- test/math/ecp_bench.c | 24 ++----- test/math/ecp_test.c | 41 +++++++++--- 8 files changed, 244 insertions(+), 175 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 13d374d1..1eb9bc4d 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.16 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -348,13 +348,14 @@ typedef void (*ec_smul_i)( \pre neg == 0 || neg == 1. \expect Описание ec корректно. \expect Точки a и b лежат на кривой. - \expect a + b != O. - \expect Используется полная (complete) формула сложения, которая одинаково - работает с a == b и a != b. - \remark Функции интерфейса ec_pmaadd_i используются в регулярных алгоритмах - вычисления кратной точки. + \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно + удаленной (a + b == O). + \remark Функции интерфейса ec_pmaadd_i предназначены для использования + в регулярных алгоритмах вычисления кратной точки. Предполагается, что + сложение a + b выполняется по полным (complete) формулам, одними и теми же + для случаев a == b, a == -b и a != \pm b. */ -typedef void(*ec_pmaadd_i)( +typedef bool_t(*ec_pmaadd_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ @@ -378,13 +379,16 @@ typedef void(*ec_pmaadd_i)( \pre neg == 0 || neg == 1. \expect Описание ec корректно. \expect Точки a и b лежат на кривой. - \expect a + b != O. \expect Используется полная (complete) формула сложения, которая одинаково работает с a == b и a != b. - \remark Функции интерфейса ec_pmaadda_i используются в регулярных - алгоритмах вычисления кратной точки. + \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно + удаленной (a + b == O). + \remark Функции интерфейса ec_pmaadda_i предназначены для использования + в регулярных алгоритмах вычисления кратной точки. Предполагается, что + сложение a + b выполняется по полным (complete) формулам, одними и теми же + для случаев a == b, a == -b и a != \pm b. */ -typedef void(*ec_pmaadda_i)( +typedef bool_t(*ec_pmaadda_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ @@ -617,6 +621,39 @@ bool_t ecMulA( size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +/*! \brief Кратная точка с регуляризацией + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [m]d-кратной аффинной точки [2 * ec->f->n]a: + \code + b <- d a. + \endcode + \pre Описание ec работоспособно. + \pre Описание группы точек в ec работоспособно. + \pre Длина ec->order в машинных словах равняется m. + \pre m > 1. + \pre d < ec->order. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} ecMulA2_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция регулярна по [m]d при выполнении следующих условий: + - ec->pmaadd != 0; + - функции ec->add, ec->dbl и ec->pmaadd регулярны. +*/ +bool_t ecMulA2( + word b[], /*!< [out] кратная точка */ + const word a[], /*!< [in] базовая точка */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + /*! \brief Имеет порядок? Проверяется, что аффинная точка [2 * ec->f->n]a имеет порядок [m]q diff --git a/include/bee2/math/zz.h b/include/bee2/math/zz.h index 2a39b8b5..894c1868 100644 --- a/include/bee2/math/zz.h +++ b/include/bee2/math/zz.h @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.15 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -871,7 +871,8 @@ size_t zzInvMod_deep(size_t n); b <- divident * a^{-1} \mod mod. \endcode \pre mod -- нечетное && mod[n - 1] != 0. - \pre a, divident < mod. + \pre divident < mod. + \pre 0 < a && a < mod. \pre Буфер b не пересекается с буфером mod. \expect \gcd(a, mod) = 1. \remark Если \gcd(a, mod) != 1, то b <- 0. diff --git a/src/math/ec.c b/src/math/ec.c index 0bfe94e3..7ed9fa56 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.16 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -17,6 +17,7 @@ #include "bee2/math/ec.h" #include "bee2/math/ww.h" #include "bee2/math/zz.h" +#include "math/zz/zz_lcl.h" /* ******************************************************************************* @@ -165,7 +166,7 @@ static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) #define ecPmAAdd_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static void ecPmAAdd(word c[], const word a[], const word b[], +static bool_t ecPmAAdd(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ @@ -176,7 +177,7 @@ static void ecPmAAdd(word c[], const word a[], const word b[], ecAdd(t, a, b, ec, stack); if (neg) ecNeg(t, t, ec, stack); - (void)ecToA(c, t, ec, stack); + return ecToA(c, t, ec, stack); } static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) @@ -190,7 +191,7 @@ static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) #define ecPmAAddA_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -/* static */ void ecPmAAddA(word c[], const word a[], const word b[], +static bool_t ecPmAAddA(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ @@ -201,10 +202,10 @@ static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) ecAddA(t, a, b, ec, stack); if (neg) ecNeg(t, t, ec, stack); - (void)ecToA(c, t, ec, stack); + return ecToA(c, t, ec, stack); } -/* static */ size_t ecPmAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecPmAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( ecPmAAddA_local(n, ec_d), @@ -216,8 +217,8 @@ static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) ******************************************************************************* Кратная точка: NAF (Non-Adjacent Form) -В функции ecMulNAF() для определения b = da (d-кратного точки a) применяется -оконный NAF с длиной окна w > 2. Реализован алгоритм 3.36 из [1]. +В функции ecMulA() для определения b = da (d-кратного точки a) применяется +оконный NAF с длиной окна w > 2. Реализован алгоритм 3.36 из [HMV04]. Используются малые нечетные кратные a: b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. @@ -236,8 +237,8 @@ static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) Длина окна w выбирается как решение следующей оптимизационной задачи: (2^{w - 2} - 2) + l / (w + 1) -> min. -[1] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, -Springer, 2004. +[HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve + Cryptography, Springer, 2004. ******************************************************************************* */ @@ -252,12 +253,12 @@ static size_t ecNAFWidth(size_t l) return 3; } -#define ecMulNAF_local(n, ec_d, m, pre_count)\ +#define ecMulA_local(n, ec_d, m, pre_count)\ /* naf */ O_OF_W(2 * m + 1),\ /* t */ O_OF_W(ec_d * n),\ /* pre */ O_OF_W(pre_count * ec_d * n) -bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, +bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], size_t m, void* stack) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); @@ -273,12 +274,14 @@ bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, ASSERT(ecIsOperable(ec)); // разметить стек memSlice(stack, - ecMulNAF_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, + ecMulA_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, &naf, &t, &pre, &stack); // расчет NAF ASSERT(naf_width >= 3); naf_size = wwNAF(naf, d, m, naf_width); - ASSERT(naf_size > 0); + // d == O => b <- O + if (naf_size == 0) + return FALSE; // малые кратные: a, 3a, ..., (2^w - 1)a if (ec->smul) ec->smul(pre, a, naf_width - 1, ec, stack); @@ -321,12 +324,12 @@ bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, return ecToA(b, t, ec, stack); } -static size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); const size_t pre_count = SIZE_1 << (naf_width - 1); return memSliceSize( - ecMulNAF_local(n, ec_d, m, pre_count), + ecMulA_local(n, ec_d, m, pre_count), ecSmul_deep(n, ec_d, ec_deep), SIZE_MAX); } @@ -335,15 +338,19 @@ static size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ******************************************************************************* Кратная точка: SNZ (Signed Non-Zero) -В функции ecMulSNZ() для определения b = da при нечетной кратности d -используется ее представление в виде - d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}, -где d_i \in {\pm 1, \pm 3, ..., \pm (2^w - 1)} -- ненулевые (нечетные) цифры +В функции ecMulA2() для определения кратной точки b = da при нечетной +кратности d используется ее представление в виде + d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}. +Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w - 1)} -- ненулевые (нечетные) цифры со знаком. Предполагается, что точка a лежит в группе нечетного порядка order, -(2^w)^{k-1} < order < (2^w)^k. Если кратность d четная, то она предварительно -меняется на order - d. +(2^w)^{k-1} < order < (2^w)^k. + +Если кратность d четная, то она предварительно меняется на order - d. +Для этого используется функция zzPmMod2(). Она отличается от zzPmMod() только +тем, что d = 0 меняется на order. -Реализован следующий алгоритм (см. [2; p. 9-11, algorithm 1], а также [3]): +Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], +а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): 1) pre[i] <- (2i - 1)a; // ec_smult_i 2) pre[-i] <- -pre[i]. // ec_neg_i @@ -363,16 +370,34 @@ static size_t ecMulNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) к удвоению. Регулярность функция ec_aadd_i означает, что сложение и удвоение будут выполняться по одним формулам. -[2] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves -for Cryptography: An Efficiency and Security Analysis, 2014, -https://eprint.iacr.org/2014/130.pdf. - -[3] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication on -Weierstrass curves using division polynomials. Mat. Vopr. Kriptogr., -13:2, 2022, https://doi.org/10.4213/mvk406. +[BCL+14] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves + for Cryptography: An Efficiency and Security Analysis, 2014, + https://eprint.iacr.org/2014/130.pdf. +[APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication on + Weierstrass curves using division polynomials. Mat. Vopr. Kriptogr., + 13:2, 2022, https://doi.org/10.4213/mvk406. ******************************************************************************* */ +static zzPmMod2(word b[], const word a[], const word mod[], size_t n, + register word neg) +{ + register word mask; + size_t i; + ASSERT(wwIsSameOrDisjoint(a, b, n)); + ASSERT(wwIsDisjoint(b, mod, n)); + ASSERT(wwCmp(a, mod, n) < 0); + ASSERT(neg == WORD_0 || neg == WORD_1); + // b <- neg ? -a : a + mask = WORD_0 - neg; + for (i = 0; i < n; ++i) + b[i] = a[i] ^ mask; + zzAddW2(b, n, neg); + // b <- neg ? b + mod : b + zzAddAndW(b, mod, n, mask); + CLEAN2(neg, mask); +} + static size_t ecSNZWidth(size_t l) { if (l <= 256) @@ -380,17 +405,18 @@ static size_t ecSNZWidth(size_t l) return 6; } -#define ecMulSNZ_local(n, ec_d, m, pre_count)\ +#define ecMulA2_local(n, ec_d, m, pre_count)\ /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pre */ O_OF_W(pre_count * ec_d * n) -bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, +bool_t ecMulA2(word b[], const word a[], const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; register word digit; register word hi; + register bool_t ret; size_t mb; size_t snz_width; size_t pre_count; @@ -404,7 +430,7 @@ bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, ASSERT(ecGroupIsOperable(ec)); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); - ASSERT(!wwIsZero(d, m) && wwCmp(d, ec->order, m) < 0); + ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); snz_width = ecSNZWidth(mb); @@ -412,11 +438,11 @@ bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, pre_count = SIZE_1 << snz_width; // разметить стек memSlice(stack, - ecMulSNZ_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, + ecMulA2_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, &dd, &t, &pre, &stack); // dd <- (d & 1) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); - zzPmMod(dd, d, ec->order, m, neg); + zzPmMod2(dd, d, ec->order, m, neg); ASSERT(zzIsOdd(dd, m)); // малые кратные: a, 3a, ..., (2^w - 1)a if (ec->smul) @@ -449,54 +475,28 @@ bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, snz_width); ASSERT(digit & 1); - if (ec->pmaadd) + ret = (ec->pmaadd) ? ec->pmaadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, - neg, ec, stack); - else + neg, ec, stack) : ecPmAAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); - return TRUE; + return ret; } -size_t ecMulSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t snz_width = ecSNZWidth(B_OF_W(m)); const size_t pre_count = SIZE_1 << snz_width; return memSliceSize( - ecMulSNZ_local(n, ec_d, m, pre_count), + ecMulA2_local(n, ec_d, m, pre_count), utilMax(2, ecSmul_deep(n, ec_d, ec_deep), ecPmAAdd_deep(n, ec_d, ec_deep)), SIZE_MAX); } -/* -******************************************************************************* -Кратная точка: общий случай -******************************************************************************* -*/ - -bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[], - size_t m, void* stack) -{ - ASSERT(ecIsOperable(ec)); - if (ecGroupIsOperable(ec) && - wwWordSize(ec->order, ec->f->n + 1) == m && - zzIsOdd(ec->order, m) && m > 1 && - !wwIsZero(d, m) && wwCmp(d, ec->order, m) < 0) - return ecMulSNZ(b, a, ec, d, m, stack); - return ecMulNAF(b, a, ec, d, m, stack); -} - -size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) -{ - return utilMax(2, - ecMulNAF_deep(n, ec_d, ec_deep, m), - ecMulSNZ_deep(n, ec_d, ec_deep, m)); -} - /* ******************************************************************************* Имеет порядок? @@ -515,14 +515,14 @@ bool_t ecHasOrderA(const word a[], const ec_o* ec, const word q[], size_t m, ecHasOrderA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); // q a == O? - return !ecMulNAF(t, a, ec, q, m, stack); + return !ecMulA(t, a, ec, q, m, stack); } size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( ecHasOrderA_local(n, ec_d), - ecMulNAF_deep(n, ec_d, ec_deep, m), + ecMulA_deep(n, ec_d, ec_deep, m), SIZE_MAX); } @@ -530,7 +530,7 @@ size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ******************************************************************************* Сумма кратных точек -Реализован алгоритм 3.51 (interleaving with NAF) из [1]. +Реализован алгоритм 3.51 (interleaving with NAF) из [HMV04]. Для каждого d[i] строится naf[i] длиной l[i] с шириной окна w[i]. diff --git a/src/math/ecp.c b/src/math/ecp.c index 9de6d41b..ee98572e 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.12 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -62,31 +62,25 @@ -(X : Y : Z) = (X : -Y : Z). В функции ecpDblJ() выполняется удвоение P <- 2P. Реализован -алгоритм dbl-1998-hnm [Hasegawa T., Nakajima J., Matsui M. A Practical -Implementation of Elliptic Curve Cryptosystems over GF(p) on a -16-bit Microcomputer. Public Key Cryptography, 1998]. -Сложность алгоритма: +алгоритм dbl-1998-hnm из [HNM98]. Сложность алгоритма: 3M + 6S + 1*A + 1half + 6add + 3*2 \approx 9M. \todo Сравнить dbl-1998-hnm с dbl-2007-bl, сложность которого 1M + 8S + 1A + 10add + 1*8 + 2*2 + 1*3. В функции ecpDblJA3() выполняется удвоение P <- 2P для случая A = -3. -Реализован алгоритм dbl-1998-hnm2 [Hasegawa T., Nakajima J., Matsui M. -A Practical Implementation of Elliptic Curve Cryptosystems over GF(p) -on a 16-bit Microcomputer. Public Key Cryptography, 1998]. -Сложность алгоритма: +Реализован алгоритм dbl-1998-hnm2 из [HNM98]. Сложность алгоритма: 4M + 4S + 1*half + 7add + 3*2 \approx 8M. \todo Сравнить dbl-1998-hnm2 с dbl-2001-b, сложность которого 3M +5S + 8add + 1*4 + 2*8 + 1*3. В функции ecpDblAJ() выполняется удвоение P <- 2A. Реализован алгоритм -mdbl-2007-bl [Bernstein-Lange, 2007]. Сложность алгоритма: +mdbl-2007-bl из [BerLan07]. Сложность алгоритма: 1M + 5S + 7add + 1*8 + 3*2 + 1*3 \approx 6M. В функции ecpAddJ() выполняется сложение P <- P + P. Реализован алгоритм -add-2007-bl [Bernstein-Lange, 2007]. Сложность алгоритма: +add-2007-bl из [BerLan07]. Сложность алгоритма: 11M + 5S + 9add + 4*2 \approx 16M. \todo Сравнить add-2007-bl с add-1998-cmo-2, сложность которого @@ -99,39 +93,43 @@ ecpDblAJ(). Подумать, как "помочь" этой функции, п функции ecpAddJ(). В функции ecpAddAJ() выполняется сложение P <- P + A. -Реализован алгоритм madd-2004-hmv [Hankerson D., Menezes A., Vanstone S. -Guide to Elliptic Curve Cryptography, Springer, 2004]. -Сложность алгоритма: +Реализован алгоритм madd-2004-hmv из [HMV04]. Сложность алгоритма: 8M + 3S + 6add + 1*2 \approx 11M. В функции ecpTplJ() выполняется утроение P <- 3P. Реализован алгоритм -tpl-2007-bl [Bernstein-Lange, 2007]. Сложность алгоритма +tpl-2007-bl из [BerLan07]. Сложность алгоритма: 5M + 10S + 1*A + 15add + 2*4 + 1*6 + 1*8 + 1*16 + 1*3 \approx 15M. \todo Сравнить madd-2004-hmv с madd-2007-bl, сложность которого 7M + 4S + 9add + 1*4 + 3*2. В функции ecpTplJA3() выполняется утроение P <- 3P для случая A = -3. -Реализован алгоритм tpl-2007-bl-2 [Bernstein-Lange, 2007]. -Сложность алгоритма +Реализован алгоритм tpl-2007-bl-2 из [BerLan07]. Сложность алгоритма: 7M + 7S + 13add + 2*4 + 1*8 + 1*12 + 1*16 + 1*3 \approx 14M. + +[BerLan07] Bernstein D., Lange T. Faster addition and doubling on elliptic + curves. Advances in Cryptology -- ASIACRYPT 2007. + https://eprint.iacr.org/2007/286.pdf. +[HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve + Cryptography, Springer, 2004. +[HNM98] Hasegawa T., Nakajima J., Matsui M. A Practical Implementation + of Elliptic Curve Cryptosystems over GF(p) on a 16-bit + Microcomputer. Public Key Cryptography, 1998. ******************************************************************************* */ // [3n]b <- [2n]a (P <- A) static bool_t ecpFromAJ(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; - // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(a == b || wwIsDisjoint2(a, 2 * n, b, 3 * n)); + ASSERT(a == b || wwIsDisjoint2(a, 2 * ec->f->n, b, 3 * ec->f->n)); // xb <- xa qrCopy(ecX(b), ecX(a), ec->f); // yb <- ya - qrCopy(ecY(b, n), ecY(a, n), ec->f); + qrCopy(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); // zb <- unity - qrSetUnity(ecZ(b, n), ec->f); + qrSetUnity(ecZ(b, ec->f->n), ec->f); return TRUE; } @@ -142,14 +140,15 @@ static bool_t ecpFromAJ(word b[], const word a[], const ec_o* ec, void* stack) // [2n]b <- [3n]a (A <- P) static bool_t ecpToAJ(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(a == b || wwIsDisjoint2(a, 3 * n, b, 2 * n)); + ASSERT(a == b || wwIsDisjoint2(a, 3 * ec->f->n, b, 2 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpToAJ_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -181,17 +180,15 @@ static size_t ecpToAJ_deep(size_t n, size_t f_deep) // [3n]b <- -[3n]a (P <- -P) static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; - // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // xb <- xa qrCopy(ecX(b), ecX(a), ec->f); // yb <- -ya - zmNeg(ecY(b, n), ecY(a, n), ec->f); + zmNeg(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); // zb <- za - qrCopy(ecZ(b, n), ecZ(a, n), ec->f); + qrCopy(ecZ(b, ec->f->n), ecZ(a, ec->f->n), ec->f); } #define ecpDblJ_local(n)\ @@ -201,14 +198,15 @@ static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) // [3n]b <- 2[3n]a (P <- 2P) static void ecpDblJ(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpDblJ_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -275,14 +273,15 @@ static size_t ecpDblJ_deep(size_t n, size_t f_deep) // [3n]b <- 2[3n]a (P <- 2P, A = -3) static void ecpDblJA3(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpDblJA3_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -349,7 +348,7 @@ static size_t ecpDblJA3_deep(size_t n, size_t f_deep) // [3n]b <- 2[2n]a (P <- 2A) static void ecpDblAJ(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -357,8 +356,9 @@ static void ecpDblAJ(word b[], const word a[], const ec_o* ec, void* stack) // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(a == b || wwIsDisjoint2(a, 2 * n, b, 3 * n)); + ASSERT(a == b || wwIsDisjoint2(a, 2 * ec->f->n, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpDblAJ_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &t4, &stack); @@ -430,7 +430,7 @@ static size_t ecpDblAJ_deep(size_t n, size_t f_deep) static void ecpAddJ(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -439,9 +439,10 @@ static void ecpAddJ(word c[], const word a[], const word b[], const ec_o* ec, ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(ecpSeemsOnJ(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(wwIsSameOrDisjoint(b, c, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpAddJ_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &t4, &stack); @@ -548,7 +549,7 @@ static size_t ecpAddJ_deep(size_t n, size_t f_deep) static void ecpAddAJ(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -557,9 +558,10 @@ static void ecpAddAJ(word c[], const word a[], const word b[], const ec_o* ec, ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(ecpSeemsOnA(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(b == c || wwIsDisjoint2(b, 2 * ec->f->n, c, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpAddAJ_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &t4, &stack); @@ -643,7 +645,7 @@ static size_t ecpAddAJ_deep(size_t n, size_t f_deep) // [3n]b <- 3[3n]a (P <- 3P) static void ecpTplJ(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t0; /* [n] */ word* t1; /* [n] */ word* t2; /* [n] */ @@ -655,8 +657,9 @@ static void ecpTplJ(word b[], const word a[], const ec_o* ec, void* stack) // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpTplJ_local(n), SIZE_0, SIZE_MAX, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &stack); @@ -742,7 +745,7 @@ static size_t ecpTplJ_deep(size_t n, size_t f_deep) // [3n]b <- 3[3n]a (P <- 3P) static void ecpTplJA3(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -753,8 +756,9 @@ static void ecpTplJA3(word b[], const word a[], const ec_o* ec, void* stack) // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ecpTplJA3_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &stack); @@ -834,16 +838,16 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) ******************************************************************************* Сложение с настройкой знака суммы -Использованы полные (complete) формулы сложения / удвоения из работы - -[RCB15] Renes J., Costello C., Batina L. Complete addition formulas for prime -order elliptic curves. 2015. https://eprint.iacr.org/2015/1060.pdf. +Использованы полные (complete) формулы сложения / удвоения из работы [RCB15]. В формулах используются однородные (Homogenious, H) координаты: x = X / Z, y = Y / Z. Преобразование H <- J: (X Z : Y : Z^3) <- (X : Y : Z). + +[RCB15] Renes J., Costello C., Batina L. Complete addition formulas for prime + order elliptic curves. 2015. https://eprint.iacr.org/2015/1060.pdf. ******************************************************************************* */ @@ -860,10 +864,11 @@ order elliptic curves. 2015. https://eprint.iacr.org/2015/1060.pdf. /* t5 */ O_OF_W(n) // [2n]с <- (-1)^neg ([3n]a + [3n]b) (A <- J + J) -static void ecpPmAAdd(word c[], const word a[], const word b[], +static bool_t ecpPmAAdd(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; + register bool_t ret; word* pt1; /* [3 * n] */ word* pt2; /* [3 * n] */ word* pt3; /* [3 * n] */ @@ -879,6 +884,7 @@ static void ecpPmAAdd(word c[], const word a[], const word b[], ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(ecpSeemsOnJ(b, ec)); // разметить стек + n = ec->f->n; memSlice(stack, ecpPmAAdd_local(n), SIZE_0, SIZE_MAX, &pt1, &pt2, &pt3, &B3, &t0, &t1, &t2, &t3, &t4, &t5, &stack); @@ -937,11 +943,13 @@ static void ecpPmAAdd(word c[], const word a[], const word b[], qrMul(ecZ(pt3, n), t5, ecZ(pt3, n), ec->f, stack); qrAdd(ecZ(pt3, n), ecZ(pt3, n), t0, ec->f); // c <- pt3 (A <- H) + ret = !wwIsZero(ecZ(pt3, n), n); qrInv(t0, ecZ(pt3, n), ec->f, stack); qrMul(ecX(c), ecX(pt3), t0, ec->f, stack); qrMul(ecY(c, n), ecY(pt3, n), t0, ec->f, stack); // c <- (-1)^neg c zzPmMod(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); + return ret; } static size_t ecpPmAAdd_deep(size_t n, size_t f_deep) @@ -983,7 +991,7 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], return FALSE; // обнулить memSetZero(ec, sizeof(ec_o)); - // зафиксировать размерности + // зафиксировать размерность d ec->d = 3; // запомнить базовое поле ec->f = f; @@ -1068,22 +1076,24 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) bool_t ecpIsValid(const ec_o* ec, void* stack) { - size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ + // кривая работоспособна? + if (!ecIsOperable2(ec)) + return FALSE; // разметить стек + n = ec->f->n; memSlice(stack, ecpIsValid_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); - // кривая работоспособна? // поле ec->f корректно? // f->mod > 3? // ec->deep >= ec->f->deep? // A, B \in ec->f? - if (!ecIsOperable2(ec) || - !gfpIsValid(ec->f, stack) || - wwCmpW(ec->f->mod, ec->f->n, 3) <= 0 || + if (!gfpIsValid(ec->f, stack) || + wwCmpW(ec->f->mod, n, 3) <= 0 || ec->deep < ec->f->deep || !zmIsIn(ec->A, ec->f) || !zmIsIn(ec->B, ec->f)) @@ -1124,7 +1134,7 @@ size_t ecpIsValid_deep(size_t n, size_t f_deep) bool_t ecpGroupSeemsValid(const ec_o* ec, void* stack) { - size_t n = ec->f->n; + size_t n; int cmp; word w; word* t1; /* [n + 1] */ @@ -1133,6 +1143,7 @@ bool_t ecpGroupSeemsValid(const ec_o* ec, void* stack) // pre ASSERT(ecIsOperable(ec)); // разметить стек + n = ec->f->n; memSlice(stack, ecpGroupSeemsValid_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); @@ -1181,13 +1192,14 @@ size_t ecpGroupSeemsValid_deep(size_t n, size_t f_deep) bool_t ecpGroupIsSafe(const ec_o* ec, size_t mov_threshold, void* stack) { - size_t n1 = ec->f->n + 1; + size_t n1; word* t1; /* [n1] */ word* t2; /* [n1] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); // разметить стек + n1 = ec->f->n + 1; memSlice(stack, ecpGroupIsSafe_local(n1), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -1246,12 +1258,13 @@ size_t ecpGroupIsSafe_deep(size_t n) bool_t ecpIsOnA(const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec)); // разметить стек + n = ec->f->n; memSlice(stack, ecpIsOnA_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -1297,7 +1310,7 @@ void ecpNegA(word b[], const word a[], const ec_o* ec) bool_t ecpAddAA(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -1306,6 +1319,7 @@ bool_t ecpAddAA(word c[], const word a[], const word b[], const ec_o* ec, ASSERT(ecpSeemsOnA(a, ec)); ASSERT(ecpSeemsOnA(b, ec)); // разметить стек + n = ec->f->n; memSlice(stack, ecpAddAA_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); @@ -1364,7 +1378,7 @@ size_t ecpAddAA_deep(size_t n, size_t f_deep) bool_t ecpSubAA(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -1373,6 +1387,7 @@ bool_t ecpSubAA(word c[], const word a[], const word b[], const ec_o* ec, ASSERT(ecpSeemsOnA(a, ec)); ASSERT(ecpSeemsOnA(b, ec)); // разметить стек + n = ec->f->n; memSlice(stack, ecpSubAA_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); @@ -1440,7 +1455,7 @@ size_t ecpSubAA_deep(size_t n, size_t f_deep) void ecpSWU(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; register size_t mask; word* t; /* [n] */ word* x1; /* [n] */ @@ -1453,6 +1468,7 @@ void ecpSWU(word b[], const word a[], const ec_o* ec, void* stack) ASSERT(wwGetBits(ec->f->mod, 0, 2) == 3); ASSERT(!qrIsZero(ec->A, ec->f) && !qrIsZero(ec->B, ec->f)); // разметить стек + n = ec->f->n; memSlice(stack, ecpSWU_local(n), SIZE_0, SIZE_MAX, &t, &x1, &x2, &y, &s, &stack); diff --git a/src/math/zm.c b/src/math/zm.c index 73efa7a9..eba7e8c0 100644 --- a/src/math/zm.c +++ b/src/math/zm.c @@ -118,7 +118,8 @@ static void zmInv(word b[], const word a[], const qr_o* r, void* stack) { ASSERT(zmIsOperable(r)); ASSERT(zmIsIn(a, r)); - zzInvMod(b, a, r->mod, r->n, stack); + wwIsZero(a, r->n) ? wwSetZero(b, r->n) : + zzInvMod(b, a, r->mod, r->n, stack); } static size_t zmInv_deep(size_t n) @@ -132,7 +133,8 @@ static void zmDiv(word b[], const word divident[], const word a[], ASSERT(zmIsOperable(r)); ASSERT(zmIsIn(divident, r)); ASSERT(zmIsIn(a, r)); - zzDivMod(b, divident, a, r->mod, r->n, stack); + wwIsZero(a, r->n) ? wwSetZero(b, r->n) : + zzDivMod(b, divident, a, r->mod, r->n, stack); } static size_t zmDiv_deep(size_t n) diff --git a/src/math/zz/zz_gcd.c b/src/math/zz/zz_gcd.c index 62e9fec1..3c657315 100644 --- a/src/math/zz/zz_gcd.c +++ b/src/math/zz/zz_gcd.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: Euclidian gcd algorithms \project bee2 [cryptographic library] \created 2012.04.22 -\version 2025.09.23 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -364,7 +364,7 @@ void zzDivMod(word b[], const word divident[], const word a[], word* da; /* [n] */ word* da1; /* [n] */ // pre - ASSERT(wwCmp(a, mod, n) < 0); + ASSERT(!wwIsZero(a, n) && wwCmp(a, mod, n) < 0); ASSERT(wwCmp(divident, mod, n) < 0); ASSERT(wwIsDisjoint(b, mod, n)); ASSERT(zzIsOdd(mod, n) && mod[n - 1] != 0); diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 5e99d696..e8c5dd22 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.01.16 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -18,18 +18,6 @@ #include #include -/* -******************************************************************************* -Композиционные элементы ecMulA() -******************************************************************************* -*/ - -extern bool_t ecMulNAF(word b[], const word a[], const ec_o* ec, const word d[], - size_t m, void* stack); - -extern bool_t ecMulSNZ(word b[], const word a[], const ec_o* ec, const word d[], - size_t m, void* stack); - /* ******************************************************************************* Потребности в стеке @@ -56,7 +44,9 @@ bool_t ecpBench() prngCOMBO_keep(), O_OF_W(2 * ec->f->n), O_OF_W(ec->f->n), - ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), + utilMax(2, + ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), + ecMulA2_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), SIZE_MAX, &combo_state, &pt, &d, &stack); if (state == 0) @@ -78,17 +68,17 @@ bool_t ecpBench() ecMulA(pt, ec->base, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf("ecpBench::ecMulNAF: %u cycles/mulpoint [%u mulpoints/sec]\n", + printf("ecpBench::ecMulA: %u cycles/mulpoint [%u mulpoints/sec]\n", (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); // эксперимент: ecMulSNZ() for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulA(pt, ec->base, ec, d, ec->f->n, stack); + ecMulA2(pt, ec->base, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf("ecpBench::ecMulSNZ: %u cycles/mulpoint [%u mulpoints/sec]\n", + printf("ecpBench::ecMulA2: %u cycles/mulpoint [%u mulpoints/sec]\n", (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 97bb94ea..5ec72f95 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.08 +\version 2026.01.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -18,6 +18,7 @@ #include #include #include +#include /* ******************************************************************************* @@ -59,13 +60,17 @@ bool_t ecpTest() ec_o* ec; /* [ec_keep] */ qr_o* f; /* [f_keep] */ octet* t; /* [5 * no] */ + word* pts; /* [4 * n] */ + word* d; /* [n] */ void* stack; // создать состояние state = blobCreate2( ec_keep, f_keep, 5 * no, - utilMax(12, + O_OF_W(4 * n), + O_OF_W(n), + utilMax(13, gfpCreate_deep(no), ecpCreateJ_deep(n, f_deep), ecGroupCreate_deep(f_deep), @@ -77,9 +82,10 @@ bool_t ecpTest() ecpIsOnA_deep(n, f_deep), ecpAddAA_deep(n, f_deep), ecpSubAA_deep(n, f_deep), - ecMulA_deep(n, 3, ec_deep, 1)), + ecMulA_deep(n, 3, ec_deep, n), + ecMulA2_deep(n, 3, ec_deep, n)), SIZE_MAX, - &ec, &f, &t, &stack); + &ec, &f, &t, &pts, &d, &stack); if (state == 0) return FALSE; // создать f = GF(p) @@ -116,8 +122,8 @@ bool_t ecpTest() } // утроить базовую точку разными способами { - word* pts = (word*)t; - word d = 3; + // d <- 3 + d[0] = 3; // удвоить и сложить if (!ecpIsOnA(ec->base, ec, stack) || !ecpAddAA(pts, ec->base, ec->base, ec, stack) || @@ -130,7 +136,7 @@ bool_t ecpTest() (ecpNegA(pts + 2 * n, pts + 2 * n, ec), ecpAddAA(pts + 2 * n, pts, pts + 2 * n, ec, stack)) || // вычислить кратную точку - !ecMulA(pts + 2 * n, ec->base, ec, &d, 1, stack) || + !ecMulA(pts + 2 * n, ec->base, ec, d, 1, stack) || !memEq(pts, pts + 2 * n, 2 * n) || // утроить напрямую !ec->froma || !ec->tpl || !ec->toa || @@ -143,6 +149,22 @@ bool_t ecpTest() return FALSE; } } + // ecMulA() vs ecMulA2() + { + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + while (!wwIsZero(d, n)) + { + if (!ecMulA2(pts, ec->base, ec, d, n, stack) || + !ecMulA(pts + 2 * n, ec->base, ec, d, n, stack) || + !wwEq(pts, pts + 2 * n, 2 * n)) + return FALSE; + wwShLo(d, n, 1); + } + if (ecMulA(pts, ec->base, ec, d, n, stack) || + ecMulA2(pts + 2 * n, ec->base, ec, d, n, stack)) + return FALSE; + } // вывести f = GF(p) за пределы ec objCopy(f, objPtr(ec, 0, qr_o)); // создать ec = EC_{a-1, b}(f) @@ -161,7 +183,8 @@ bool_t ecpTest() // утроить базовую точку разными способами { word* pts = (word*)t; - word d = 3; + // d <- 3 + d[0] = 3; // удвоить и сложить if (!ecpIsOnA(ec->base, ec, stack) || !ecpAddAA(pts, ec->base, ec->base, ec, stack) || @@ -174,7 +197,7 @@ bool_t ecpTest() (ecpNegA(pts + 2 * n, pts + 2 * n, ec), ecpAddAA(pts + 2 * n, pts, pts + 2 * n, ec, stack)) || // вычислить кратную точку - !ecMulA(pts + 2 * n, ec->base, ec, &d, 1, stack) || + !ecMulA(pts + 2 * n, ec->base, ec, d, 1, stack) || !memEq(pts, pts + 2 * n, 2 * n) || (ec->froma(pts + 2 * n, ec->base, ec, stack), ec->tpl(pts + 2 * n, pts + 2 * n, ec, stack), From 0c012b929fac998a524c69156b133ec23259f2ef Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 20 Jan 2026 19:19:17 +0300 Subject: [PATCH 13/77] Clarify bibitems --- include/bee2/core/word.h | 6 +- include/bee2/math/ec.h | 14 ++--- include/bee2/math/pp.h | 15 +++-- include/bee2/math/zz.h | 43 +++++++++---- src/core/mem.c | 16 ++--- src/core/rng/rng_timer.c | 51 ++++++++------- src/core/u32.c | 25 ++++---- src/math/ec.c | 132 ++++++++++++++++++++++----------------- src/math/ec2.c | 118 +++++++++++++++++++--------------- src/math/ecp.c | 31 ++++++--- src/math/pp/pp_etc.c | 40 +++++++----- src/math/pp/pp_mod.c | 9 ++- src/math/pri.c | 25 ++++---- src/math/zm.c | 5 +- src/math/zz/zz_add.c | 23 ++++++- src/math/zz/zz_etc.c | 10 +-- src/math/zz/zz_gcd.c | 13 ++-- src/math/zz/zz_mod.c | 70 ++++++++++++--------- src/math/zz/zz_mul.c | 8 ++- test/math/zz_test.c | 45 +++++++++---- 20 files changed, 420 insertions(+), 279 deletions(-) diff --git a/include/bee2/core/word.h b/include/bee2/core/word.h index b0624418..80a73364 100644 --- a/include/bee2/core/word.h +++ b/include/bee2/core/word.h @@ -103,7 +103,7 @@ extern "C" { реализованные макросы являются псевдонимами этих сравнений. Если обычные сравнения все таки не регулярны, то можно использовать следующие -универсальные (но медленные) макросы [1, с. 35]: +универсальные (но медленные) макросы [War03; с. 35]: \code #define wordLess(a, b)\ ((~(a) & (b) | ((~(a) | (b)) & (a) - (b))) >> (B_PER_W - 1)) @@ -121,8 +121,8 @@ extern "C" { Макросы с суффиксом 0M меняют WORD_1 на WORD_MAX. Возвращаемые значения можно использовать как маски при организации регулярных вычислений. -[1] Уоррен Генри Мл. Алгоритмические трюки для программистов, - М.: Издательский дом <<Вильямс>>, 2003. +[War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, + М.: Издательский дом <<Вильямс>>, 2003. \def wordEq \brief Машинные слова a и b равны? diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 1eb9bc4d..034d4bb0 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.18 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -350,12 +350,12 @@ typedef void (*ec_smul_i)( \expect Точки a и b лежат на кривой. \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно удаленной (a + b == O). - \remark Функции интерфейса ec_pmaadd_i предназначены для использования + \remark Функции интерфейса ec_negaadd_i предназначены для использования в регулярных алгоритмах вычисления кратной точки. Предполагается, что сложение a + b выполняется по полным (complete) формулам, одними и теми же для случаев a == b, a == -b и a != \pm b. */ -typedef bool_t(*ec_pmaadd_i)( +typedef bool_t(*ec_negaadd_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ @@ -383,12 +383,12 @@ typedef bool_t(*ec_pmaadd_i)( работает с a == b и a != b. \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно удаленной (a + b == O). - \remark Функции интерфейса ec_pmaadda_i предназначены для использования + \remark Функции интерфейса ec_negaadda_i предназначены для использования в регулярных алгоритмах вычисления кратной точки. Предполагается, что сложение a + b выполняется по полным (complete) формулам, одними и теми же для случаев a == b, a == -b и a != \pm b. */ -typedef bool_t(*ec_pmaadda_i)( +typedef bool_t(*ec_negaadda_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ @@ -426,8 +426,8 @@ typedef struct ec_o ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ ec_smul_i smul; /*!< функция расчета малых кратных */ - ec_pmaadd_i pmaadd; /*!< функция сложения c настройкой знака суммы */ - ec_pmaadda_i pmaadda; /*!< функция сложения с афф. т. с настр. знака */ + ec_negaadd_i negaadd; /*!< функция сложения c настройкой знака суммы */ + ec_negaadda_i negaadda; /*!< функция сложения с афф. т. с настр. знака */ size_t deep; /*!< максимальная глубина стека функций */ mem_align_t descr[]; /*!< память для размещения данных */ } ec_o; diff --git a/include/bee2/math/pp.h b/include/bee2/math/pp.h index b0f27eca..9147c8d2 100644 --- a/include/bee2/math/pp.h +++ b/include/bee2/math/pp.h @@ -4,7 +4,7 @@ \brief Binary polynomials \project bee2 [cryptographic library] \created 2012.03.01 -\version 2023.02.02 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -321,15 +321,17 @@ size_t ppSqrMod_deep(size_t n); /*! \brief Обращение по модулю - Определяется многочлен [n]b, мультипликативно обратный к [n]a по модулю [n]mod: + Определяется многочлен [n]b, мультипликативно обратный к [n]a по модулю + [n]mod: \code b <- a^{-1} \mod mod. \endcode - \pre n > 0 && mod[n - 1] != 0 && mod -- нечетное. + \pre n > 0 && mod[n - 1] != 0 && mod имеет свободный член. \pre a < mod. \expect \gcd(a, mod) == 1. \remark Если \gcd(a, mod) != 1, то b <- 0. \deep{stack} ppInvMod_deep(n). + \safe Функция нерегулярна. */ void ppInvMod( word b[], /*!< [out] обратный многочлен */ @@ -350,9 +352,9 @@ size_t ppInvMod_deep(size_t n); \endcode \pre n > 0 && mod[n - 1] != 0 && mod имеет свободный член. \pre a, divident < mod. - \expect \gcd(a, mod) == 1. - \remark Если \gcd(a, mod) != 1, то b <- 0. + \remark Если a == 0 или \gcd(a, mod) != 1, то b <- 0. \deep{stack} ppDivMod_deep(n). + \safe Функция нерегулярна. */ void ppDivMod( word b[], /*!< [out] частное */ @@ -535,9 +537,6 @@ size_t ppMinPoly_deep(size_t l); \remark Если \deg(mod) кратно B_PER_W, то a умещается в n - 1 машинное слово. Тем не менее, все равно используется дополнительное нулевое старшее слово. - \remark Теория изложена в [Shoup V. A Computational Introduction - to Number Theory and Algebra, http://www.shoup.net/ntb/, 2008] - (версия 2, глава 18). \remark Если mod --- неприводим и a != 0, то b будет неприводимым. Этот факт можно использовать для построения одного неприводимого многочлена по другому. diff --git a/include/bee2/math/zz.h b/include/bee2/math/zz.h index 894c1868..e3ecff46 100644 --- a/include/bee2/math/zz.h +++ b/include/bee2/math/zz.h @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.18 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -264,7 +264,7 @@ word zzSubW( register word w /*!< [in] вычитаемое */ ); -/*! \brief Уменьшение числа на слова +/*! \brief Уменьшение числа на слово Число [n]a уменьшается на слово w: \code @@ -278,6 +278,27 @@ word zzSubW2( register word w /*!< [in] вычитаемое */ ); +/*! \brief Условное вычитание + + Определяется число [n]с, которое представляет собой разность + [n]a - [n]b при sub == 1 и совпадает с [n]b при sub == 0: + \code + c <- sub ? a - b : b. + \endcode + \pre a >= b. + \pre Буфер c либо не пересекается, либо совпадает с буфером a. + \pre Буфер c не пересекается с буфером a. + \pre sub == 0 || sub == 1. + \remark Функция используется при регуляризации. +*/ +void zzSubIf( + word с[], /*!< [out] результат */ + const word a[], /*!< [in] потенциальное уменьшаемое */ + const word b[], /*!< [in] потенциальное вычитаемое */ + size_t n, /*!< [in] длина чисел в машинных словах */ + register word neg /*!< [in] флаг вычитания */ +); + /*! \brief Минус Определяется число [n]b, отрицательное к [n]a по модулю B^n: @@ -754,7 +775,7 @@ void zzNegMod( void FAST(zzNegMod)(word b[], const word a[], const word mod[], size_t n); -/*! \brief Настройка знака числа по модулю +/*! \brief Условное аддитивное обращение числа по модулю Определяется число [n]b, которое совпадает с [n]a при neg == 0 и аддитивно обратно к [n]a по модулю [n]mod при neg == 1: @@ -767,10 +788,9 @@ void FAST(zzNegMod)(word b[], const word a[], const word mod[], size_t n); \pre Буфер b не пересекается с буфером mod. \pre neg == 0 || neg == 1. \remark Функция используется при регуляризации. - */ -void zzPmMod( - word b[], /*!< [in,out] +- число */ +void zzNegModIf( + word b[], /*!< [in,out] результат */ const word a[], /*!< [in] число */ const word mod[], /*!< [in] модуль */ size_t n, /*!< [in] длина чисел в машинных словах */ @@ -848,8 +868,7 @@ size_t zzSqrMod_deep(size_t n); \pre mod -- нечетное && mod[n - 1] != 0. \pre a < mod. \pre Буфер b не пересекается с буфером mod. - \expect \gcd(a, mod) == 1. - \remark Если \gcd(a, mod) != 1, то b <- 0. + \remark Если a == 0 или \gcd(a, mod) != 1, то b <- 0. \deep{stack} zzInvMod_deep(n). \safe Функция нерегулярна. */ @@ -870,12 +889,10 @@ size_t zzInvMod_deep(size_t n); \code b <- divident * a^{-1} \mod mod. \endcode - \pre mod -- нечетное && mod[n - 1] != 0. - \pre divident < mod. - \pre 0 < a && a < mod. + \pre n > 0 && mod[n - 1] != 0 && mod -- нечетное число. + \pre a, divident < mod. \pre Буфер b не пересекается с буфером mod. - \expect \gcd(a, mod) = 1. - \remark Если \gcd(a, mod) != 1, то b <- 0. + \remark Если a == 0 или \gcd(a, mod) != 1, то b <- 0. \deep{stack} zzDivMod_deep(n). \safe Функция нерегулярна. */ diff --git a/src/core/mem.c b/src/core/mem.c index 18232a1a..491a02cc 100644 --- a/src/core/mem.c +++ b/src/core/mem.c @@ -4,7 +4,7 @@ \brief Memory management \project bee2 [cryptographic library] \created 2012.12.18 -\version 2025.12.24 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -268,15 +268,15 @@ int FAST(memCmpRev)(const void* buf1, const void* buf2, size_t count) Методы очистки памяти с защитой от оптимизаторов: -[1] Yang Z., Johannesmeyer B., Trier Olesen A., Lerner S., Levchenko K. - Dead Store Elimination (Still) Considered Harmfuf. USENIX Security 2017: - 26th USENIX Security Symposium, pp. 1025–1040, 2017. - https://www.usenix.org/system/files/conference/usenixsecurity17/ - sec17-yang.pdf +[YJO+17] Yang Z., Johannesmeyer B., Olesen A., Lerner S., Levchenko K. + Dead Store Elimination (Still) Considered Harmful. USENIX Security + 2017: 26th USENIX Security Symposium, pp. 1025–1040, 2017. + https://www.usenix.org/system/files/conference/usenixsecurity17/ + sec17-yang.pdf Реализован метод "Volatile Function Pointer". Он используется также в OpenSSL -версии 1.0.2 и выше. В [1] подтверждается безопасность метода при использовании -GCC, Clang и Microsoft Visual Studio. +версии 1.0.2 и выше. В [YJO+17] подтверждается безопасность метода при +использовании GCC, Clang и Microsoft Visual Studio. \remark В предыдущей редакции memWipe() был реализован метод "Complicated Computation": diff --git a/src/core/rng/rng_timer.c b/src/core/rng/rng_timer.c index ed78092a..793266ea 100644 --- a/src/core/rng/rng_timer.c +++ b/src/core/rng/rng_timer.c @@ -4,7 +4,7 @@ \brief Random number generation: entropy sources based on timers \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.11.08 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -23,13 +23,11 @@ ******************************************************************************* Источник-таймер -Реализовано предложение [Jessie Walker, Seeding Random Number Generator]: -наблюдением является разность между показаниями высокоточного таймера -(например, регистра RDTSC) при приостановке потока на 0 мс, т.е. при передаче -управления ядру. +Реализовано предложение [Wal13]: наблюдением является разность между +показаниями высокоточного таймера (например, регистра RDTSC) при приостановке +потока на 0 мс, т.е. при передаче управления ядру. -Оценка энтропии при использовании RDTSC [Jessie Walker]: -4.9 битов / наблюдение. +Оценка энтропии при использовании RDTSC [Wal13]: 4.9 битов / наблюдение. Реализация: - таймер может быть источником случайности, если он обновляется не реже @@ -44,7 +42,11 @@ статистическое качество выборок катастрофически плохое. К источнику следует относиться с большой осторожностью, использовать его как вспомогательный. -\warning [Jessie Walker]: наблюдения зависимы, модель AR(1). +[Wal13] Walker J. Seeding Random NumberGenerators. Workshop on Real-World + Cryptography, Stanford University, Jan., 9-11, 2013, + https://crypto.stanford.edu/RealWorldCrypto/slides/jesse.pdf. + +\warning [Wal13]: наблюдения зависимы, модель AR(1). \todo Остановка на Windows, если параллельно запущено несколько ресурсоемких процессов. @@ -231,9 +233,9 @@ static bool_t tmCtrStart() ******************************************************************************* Источник-джиттер (экспериментальный) -Реализована идея работ [1, 2]: наблюдением является разность между показаниями -таймера-счетчика до и после выполнения определенного набора инструкций -процессора. Время выполнения набора предположительно флуктуирует. +Реализована идея работ [Mul22, SezSen03]: наблюдением является разность между +показаниями таймера-счетчика до и после выполнения определенного набора +инструкций процессора. Время выполнения набора предположительно флуктуирует. Реализация: - целевой набор инструкций оформлен в виде функции rngJitterSleep(); @@ -243,28 +245,33 @@ static bool_t tmCtrStart() n in {16, 32, 64, 128}. Преобразование LFSR -- регистра t -- выполняется по формуле t = (t >> 1) ^ (~((t & 1) - 1) & 0x0175) ^ (t << (n - 1)) - (в соответствии с [1, стр. 155]); + (в соответствии с [KhaAgi01; стр. 155]); - LFSR накапливает разности между показаниями таймера: разность складывается с регистром по правилу XOR, после этого выполняется преобразование LFSR; - для формирования одного выходного бита используется сумма битов четности LFSR после обработки 128-и разностей; - после формирования выходного бита LFSR не обнуляется. -[1] Харин Ю.С., Агиевич С.В. Компьютерный практикум по математическим методам - защиты информации. Мн.: БГУ, 2001. - -\warning Пока функция rngJitterSleep() и способ постобработки разностей носят -экспериментальный характер. - -[1] https://www.chronox.de/jent/CPU-Jitter-NPTRNG-v2.2.0.pdf; -[2] https://www.irisa.fr/caps/projects/hipsor/publications/havege-tomacs.pdf. - -\remark Дополнительная информация / реализации: +[Mul22] Muller S. CPU Time Jitter Based Non-Physical True Random Number + Generator, 2014-2022, + https://www.chronox.de/jent/CPU-Jitter-NPTRNG-v2.2.0.pdf. +[SezSen03] Sezner A., Sendrier N. HAVEGE: A user-level software heuristic + for generating empirically strong random numbers. ACM Transactions + on Modeling and Computer Simulation (TOMACS), 13 (4), p. 334-346, + https://doi.org/10.1145/945511.945516. +[KhaAgi01] Харин Ю.С., Агиевич С.В. Компьютерный практикум по математическим + методам защиты информации. Мн.: БГУ, 2001. + +\remark Дополнительные ссылки / информация / реализации: +* https://www.irisa.fr/caps/projects/hipsor/publications/havege-tomacs.pdf; * https://static.lwn.net/images/conf/rtlws11/random-hardware.pdf * https://www.issihosts.com/haveged/; * https://github.com/smuellerDD/jitterentropy-library; * https://github.com/jirka-h/haveged. +\warning Пока функция rngJitterSleep() и способ постобработки разностей носят +экспериментальный характер. + \todo Уточнить rngJitterSleep() и способ постобработки. ******************************************************************************* */ diff --git a/src/core/u32.c b/src/core/u32.c index f3476af8..94e2ebe9 100644 --- a/src/core/u32.c +++ b/src/core/u32.c @@ -4,7 +4,7 @@ \brief 32-bit unsigned words \project bee2 [cryptographic library] \created 2015.10.28 -\version 2025.10.24 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -17,14 +17,14 @@ /* ******************************************************************************* Использованы алгоритмы из следующих источников: -[1] Уоррен Генри Мл. Алгоритмические трюки для программистов, - М.: Издательский дом <<Вильямс>>, 2003. -[2] Andersen S.A. Bit Twidding Hacks. Avail. at: - http://graphics.stanford.edu/~seander/bithacks.html, 1997-2005. +[War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, + М.: Издательский дом <<Вильямс>>, 2003. +[And05] Andersen S.A. Bit Twidding Hacks, 1997-2005. + http://graphics.stanford.edu/~seander/bithacks.html. Слово может интерпретироваться как вычет по модулю 2^32. -\remark Вторая редакция [1], дополнительные материалы: +\remark Вторая редакция [War03], дополнительные материалы: http://www.hackersdelight.org/. ******************************************************************************* */ @@ -51,10 +51,11 @@ void u32Rev2(u32 buf[], size_t count) ******************************************************************************* Реверс битов -Реализован алгоритм, представленнный в [2] (Reverse an N-bit quantity +Реализован алгоритм, представленнный в [And05] (Reverse an N-bit quantity in parallel in 5 * lg(N) operations). Первоисточник: -* Freed, Edwin E. 1983. "Binary Magic Numbers," Dr. Dobb's Journal - Vol. 78 (April), pp. 24-37. + +[Fre83] Freed E. Binary Magic Numbers, Dr. Dobb's Journal, 1983, + Vol. 78 (April), pp. 24-37. ******************************************************************************* */ @@ -72,7 +73,7 @@ u32 u32Bitrev(register u32 w) ******************************************************************************* Вес -Реализованы алгоритмы из [1] (п.п. 5.1, 5.2). +Реализованы алгоритмы из [War03] (п.п. 5.1, 5.2). ******************************************************************************* */ @@ -100,7 +101,7 @@ bool_t u32Parity(register u32 w) ******************************************************************************* Число нулей -Реализованы алгоритмы из [1]: +Реализованы алгоритмы из [War03]: - u32CTZ_safe(): п. 5.4, второй абзац (стр. 92); - u32CTZ_fast(): листинг 5.13 (стр. 93); - u32CLZ_safe(): листинг 5.10 (стр. 89); @@ -166,7 +167,7 @@ size_t FAST(u32CLZ)(register u32 w) ******************************************************************************* Группировка четных и нечетных битов -Реализованы алгоритмы из [1]: п. 7.2 (с. 112). +Реализованы алгоритмы из [War03]: п. 7.2 (с. 112). ******************************************************************************* */ diff --git a/src/math/ec.c b/src/math/ec.c index 7ed9fa56..23498a9e 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.18 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -17,7 +17,6 @@ #include "bee2/math/ec.h" #include "bee2/math/ww.h" #include "bee2/math/zz.h" -#include "math/zz/zz_lcl.h" /* ******************************************************************************* @@ -159,20 +158,20 @@ static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) ******************************************************************************* Сложение с настройкой знака -Прямые нерегулярные реализации интерфейсов ec_pmaadd_i и ec_pmaadda_i. +Прямые нерегулярные реализации интерфейсов ec_negaadd_i и ec_negaadda_i. ******************************************************************************* */ -#define ecPmAAdd_local(n, ec_d)\ +#define ecNegAAdd_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static bool_t ecPmAAdd(word c[], const word a[], const word b[], +static bool_t ecNegAAdd(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ ASSERT(ecIsOperable(ec)); memSlice(stack, - ecPmAAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecNegAAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); ecAdd(t, a, b, ec, stack); if (neg) @@ -180,24 +179,24 @@ static bool_t ecPmAAdd(word c[], const word a[], const word b[], return ecToA(c, t, ec, stack); } -static size_t ecPmAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecNegAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecPmAAdd_local(n, ec_d), + ecNegAAdd_local(n, ec_d), ec_deep, SIZE_MAX); } -#define ecPmAAddA_local(n, ec_d)\ +#define ecNegAAddA_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static bool_t ecPmAAddA(word c[], const word a[], const word b[], +static bool_t ecNegAAddA(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ ASSERT(ecIsOperable(ec)); memSlice(stack, - ecPmAAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecNegAAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); ecAddA(t, a, b, ec, stack); if (neg) @@ -205,10 +204,10 @@ static bool_t ecPmAAddA(word c[], const word a[], const word b[], return ecToA(c, t, ec, stack); } -static size_t ecPmAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecNegAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecPmAAddA_local(n, ec_d), + ecNegAAddA_local(n, ec_d), ec_deep, SIZE_MAX); } @@ -222,22 +221,54 @@ static size_t ecPmAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) Используются малые нечетные кратные a: b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. -Они вычисляются с помощью функции интерфейса ec_smul_i, указанной в описании -кривой. Если функция не задана в описании, то используется функция по -умолчанию ecSmul(). -Пусть l = wwBitSize(d). Если расчет малых кратных выполняется с помощью -функции ecSmul(), то средняя сложность нахождения кратной точки: - c(l, w) = 1(P <- 2A) + 1(P <- P + A) + (2^{w-2} - 2)(P <- P + P) + - l/(w + 1)(P <- P + P) + l(P <- 2P). - -Здесь +Имеются три стратегии: +1) w = 2 и малые кратные вообще не рассчитываются; +2) w > 2 и малые кратные рассчитываются в аффинных координатах; +3) w > 2 и малые кратные рассчитываются в проективных координатах. + +Если малые кратные рассчитываются по схеме "последовательно складывать +с удвоенной исходной точкой", то средняя общая сложность нахождения +кратной точки (l = wwBitSize(d)): +1) c1(l) = l/3(P <- P + A); +2) c2(l, w) = 1(A <- 2A) + 1(P <- P + A) + + (2^{w-2} - 2)(A <- A + A) + l/(w + 1)(P <- P + A); +3) c3(l, w) = 1(P <- 2A) + 1(P <- P + A) + + (2^{w-2} - 2)(P <- P + P) + l/(w + 1)(P <- P + P), +без учета общего во всех стратегиях слагаемого l(P <- 2P). + +Здесь +- (A <- 2A) -- время работы каскада (ec->dbla, ec->toa)*; +- (A <- A + A) -- время работы каскада (ec->adda, ec->toa)*; - (P <- 2P) -- время работы функции ec->dbl. +----------------------------------------------------- +* [или прямых вычислений в аффинных координатах] + +Реализована третья стратегия. Она является выигрышной для кривых над GF(p) +в практических диапазонах размерностей при использовании наиболее эффективных +(якобиевых) координат. Длина окна w выбирается как решение следующей оптимизационной задачи: (2^{w - 2} - 2) + l / (w + 1) -> min. -[HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve +Малые кратные вычисляются с помощью функции интерфейса ec_smul_i, указанной +в описании кривой. Если функция не задана в описании, то используется функция +по умолчанию ecSmul(). + +\todo Усилить вторую стратегию. Рассчитать малые кратные в проективных +координатах, а затем быстро перейти к аффинным координатам с помощью +трюка Монтгомери [Doc05; algorithm 11.15 -- simultaneous inversion, p. 209]: + U_1 <- Z_1 + for t = 2,..., T: U_t <- U_{t-1} Z_t + V <- U_T^{-1} + for t = T,..., 2: + Z_t^{-1} <- V U_{t-1} + V <- V Z_t + Z_1^{-1} <- V + +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. +[HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004. ******************************************************************************* */ @@ -339,15 +370,15 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Кратная точка: SNZ (Signed Non-Zero) В функции ecMulA2() для определения кратной точки b = da при нечетной -кратности d используется ее представление в виде +кратности d используется ее представление в форме d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}. Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w - 1)} -- ненулевые (нечетные) цифры -со знаком. Предполагается, что точка a лежит в группе нечетного порядка order, -(2^w)^{k-1} < order < (2^w)^k. +со знаком (signed non-zero). Предполагается, что точка a лежит в группе +нечетного порядка order, (2^w)^{k-1} < order < (2^w)^k. Представление SNZ +предложено в [OkeTak03]. Если кратность d четная, то она предварительно меняется на order - d. -Для этого используется функция zzPmMod2(). Она отличается от zzPmMod() только -тем, что d = 0 меняется на order. +Для этого используется функция zzSubIf(). Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): @@ -370,34 +401,19 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) к удвоению. Регулярность функция ec_aadd_i означает, что сложение и удвоение будут выполняться по одним формулам. -[BCL+14] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves - for Cryptography: An Efficiency and Security Analysis, 2014, - https://eprint.iacr.org/2014/130.pdf. -[APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication on - Weierstrass curves using division polynomials. Mat. Vopr. Kriptogr., - 13:2, 2022, https://doi.org/10.4213/mvk406. +[OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory and + fast elliptic scalar multiplications secure against side channel + attacks. In Cryptographers’ Track at the RSA Conference, 2003, + pp. 328-343. Springer Berlin Heidelberg. +[BCL+14] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic + Curves for Cryptography: An Efficiency and Security Analysis, 2014, + https://eprint.iacr.org/2014/130.pdf. +[APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication + on Weierstrass curves using division polynomials. Mat. Vopr. + Kriptogr., 13:2, 2022, https://doi.org/10.4213/mvk406. ******************************************************************************* */ -static zzPmMod2(word b[], const word a[], const word mod[], size_t n, - register word neg) -{ - register word mask; - size_t i; - ASSERT(wwIsSameOrDisjoint(a, b, n)); - ASSERT(wwIsDisjoint(b, mod, n)); - ASSERT(wwCmp(a, mod, n) < 0); - ASSERT(neg == WORD_0 || neg == WORD_1); - // b <- neg ? -a : a - mask = WORD_0 - neg; - for (i = 0; i < n; ++i) - b[i] = a[i] ^ mask; - zzAddW2(b, n, neg); - // b <- neg ? b + mod : b - zzAddAndW(b, mod, n, mask); - CLEAN2(neg, mask); -} - static size_t ecSNZWidth(size_t l) { if (l <= 256) @@ -442,7 +458,7 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, &dd, &t, &pre, &stack); // dd <- (d & 1) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); - zzPmMod2(dd, d, ec->order, m, neg); + zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); // малые кратные: a, 3a, ..., (2^w - 1)a if (ec->smul) @@ -475,10 +491,10 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, snz_width); ASSERT(digit & 1); - ret = (ec->pmaadd) ? - ec->pmaadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, + ret = (ec->negaadd) ? + ec->negaadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, neg, ec, stack) : - ecPmAAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, + ecNegAAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); @@ -493,7 +509,7 @@ size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ecMulA2_local(n, ec_d, m, pre_count), utilMax(2, ecSmul_deep(n, ec_d, ec_deep), - ecPmAAdd_deep(n, ec_d, ec_deep)), + ecNegAAdd_deep(n, ec_d, ec_deep)), SIZE_MAX); } diff --git a/src/math/ec2.c b/src/math/ec2.c index 89a1bbb0..fe98a605 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2025.01.12 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -41,8 +41,7 @@ \warning Соотношение 1D = 24M получено экспериментальным путем на платформе x86 и возможно требует пересмотра. На упомянутом сайте http://www.hyperelliptic.org/efd используется другое -соотношение: 1D = 10M. Считаем его слишком оптимистичным -(по отношению к D). +соотношение: 1D = 10M. Считаем его слишком оптимистичным (по отношению к D). Используются обозначения: A <- A + A -- сложение аффинных точек, @@ -63,58 +62,67 @@ /* ******************************************************************************* -Кривая в проективных координатах Лопеса -- Дахаба (LD): +Кривая в проективных координатах Лопеса -- Дахаба (LD, [LopDah98]): x = X / Z, y = Y / Z^2, O = (1 : 0 : 0), -(X : Y : Z) = (X : ZX + Y : Z). -\warning Ошибка в книге [Hankerson D., Menezes A., Vanstone S. Guide to -Elliptic Curve Cryptography, Springer, 2004] при определении обратной -точки в LD-координатах. +\warning Ошибка в книге [HMV04] при определении обратной точки +в LD-координатах. В функции ec2DblLD() выполняется удвоение P <- 2P. Реализован алгоритм -dbl-2005-l [Lange, 2005]. Сложность алгоритма: +dbl-2005-dl [DocLan05;LopDah98]. Сложность алгоритма: 4M + 4S + 1*A + 5add \approx 5M, причем умножение на A не выполняется, если A \in {0, 1}. \todo Ссравнить с алгоритмом dbl-2005-dl. -В функции ec2DblALD() выполняется удвоение P <- 2A (Z-координата -проективной точки равняется 1). Реализован алгоритм -mdbl-2005-dl [Doche-Lange, 2005]. Сложность алгоритма: - 1M + 3S + 1*A + 1*B + 4add \approx 3M. +В функции ec2DblALD() выполняется удвоение P <- 2A (Z-координата проективной +точки равняется 1). Реализован алгоритм mdbl-2005-dl [DocLan05]. Сложность +алгоритма: + 1M + 3S + 1*A + 1*B + 4add \approx 3M, причем умножение на A не выполняется, если A \in {0, 1}. -В функции ec2AddLD() выполняется сложение P <- P + P. -Реализован алгоритм add-2005-dl [Doche-Lange-Takagi, 2005]. -Сложность алгоритма: +В функции ec2AddLD() выполняется сложение P <- P + P. Реализован алгоритм +add-2005-dl [DocLan05; HigTak00]). Сложность алгоритма: 13M + 4S + 9add \approx 13M. В функции ec2AddALD() выполняется сложение P <- P + A (Z-координата -второго слагаемого равняется 1). -Реализован алгоритм madd-2005-dl [Doche, Lange, Al-Daoude, 2005]. -Сложность алгоритма: +второго слагаемого равняется 1). Реализован алгоритм madd-2005-dl +[DocLan05;AMR+02]). Сложность алгоритма: 8M + 5S + 1*A + 9add \approx 9M, причем умножение на A не выполняется, если A \in {0, 1}. -Целевые функции ci(l), определенные в описании реализации ecMul() в ec.c, -принимают следующий вид (считаем, что коэффициент A \in {0, 1}): +\remark Целевые функции ci(l), определенные в описании реализации ecMulA() +в ec.c, принимают следующий вид (считаем, что коэффициент A \in {0, 1}): c1(l) = l/3 8; c2(l, w) = 26 + (2^{w-2} - 2)26 + l/(w + 1) 8; c3(l, w) = 2 + (2^{w-2} - 2)13 + l/(w + 1) 13. - Расчеты показывают, что с1(l) <= min_w c2(l), l <= 39, min_w c2(l, w) <= min_w c3(l, w). Поэтому для практически используемых размерностей l (39 <= l) первая и третья стратегии являются проигрышными. Реализована только стратегия 2. -\todo Реализовать быстрые формулы для особенных B: -B = 1 (кривые Коблица), известен \sqrt{B}. +\todo Реализовать быстрые формулы для особенных B: B = 1 (кривые Коблица), +известен \sqrt{B}. \todo Реализовать редакции функций с A \in {0, 1}. \todo Исследовать расширенные LD-координаты (дополнительно поддерживается Z^2). + +[AMR+02] A new addition formula for elliptic curves over GF(2n), IEEE Trans. + on Computers 51 No 8 (2002), 972–975. +[DocLan05] Doche C., Lange T. Arithmetic of Elliptic Curves. In: Handbook of + Elliptic and Hyperelliptic Curve Cryptography, Chapman & Hall/CRC, + 2005. +[HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve + Cryptography, Springer, 2004. +[HigTak00] Higuchi A., Takagi N. A fast addition algorithm for elliptic curve + arithmetic in GF(2^n) using projective coordinates, Inform. + Process. Lett. 76 (2000), 101–103. +[LopDah98] Lopez J., Dahab R. Improved algorithms for elliptic curve arithmetic + in GF(2^n), Tech. Report IC-98-39, Relatorio Tecnico, October 1998. ******************************************************************************* */ @@ -122,17 +130,16 @@ B = 1 (кривые Коблица), известен \sqrt{B}. static bool_t ec2FromALD(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnA(a, ec)); - ASSERT(a == b || wwIsDisjoint2(a, 2 * n, b, 3 * n)); + ASSERT(a == b || wwIsDisjoint2(a, 2 * ec->f->n, b, 3 * ec->f->n)); // xb <- xa qrCopy(ecX(b), ecX(a), ec->f); // yb <- ya - qrCopy(ecY(b, n), ecY(a, n), ec->f); + qrCopy(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); // zb <- 1 - qrSetUnity(ecZ(b, n), ec->f); + qrSetUnity(ecZ(b, ec->f->n), ec->f); return TRUE; } @@ -142,13 +149,14 @@ static bool_t ec2FromALD(word b[], const word a[], const ec_o* ec, // [2n]b <- [3n]a (A <- P) static bool_t ec2ToALD(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnLD(a, ec)); - ASSERT(a == b || wwIsDisjoint2(a, 3 * n, b, 2 * n)); + ASSERT(a == b || wwIsDisjoint2(a, 3 * ec->f->n, b, 2 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2ToALD_local(n), SIZE_0, SIZE_MAX, &t1, &stack); @@ -181,13 +189,14 @@ static size_t ec2ToALD_deep(size_t n, size_t f_deep) // [3n]b <- -[3n]a (P <- -P) static void ec2NegLD(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnLD(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2NegLD_local(n), SIZE_0, SIZE_MAX, &t1, &stack); @@ -213,14 +222,15 @@ static size_t ec2NegLD_deep(size_t n, size_t f_deep) // [3n]b <- 2[3n]a (P <- 2P) static void ec2DblLD(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnLD(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2DblLD_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -276,13 +286,14 @@ static size_t ec2DblLD_deep(size_t n, size_t f_deep) // [3n]b <- 2[2n]a (P <- 2A) static void ec2DblALD(word b[], const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnA(a, ec)); - ASSERT(a == b || wwIsDisjoint2(a, 2 * n, b, 3 * n)); + ASSERT(a == b || wwIsDisjoint2(a, 2 * ec->f->n, b, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2DblALD_local(n), SIZE_0, SIZE_MAX, &t1, &stack); @@ -336,7 +347,7 @@ static size_t ec2DblALD_deep(size_t n, size_t f_deep) static void ec2AddLD(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -347,9 +358,10 @@ static void ec2AddLD(word c[], const word a[], const word b[], ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(ec2SeemsOnLD(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(wwIsSameOrDisjoint(b, c, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(wwIsSameOrDisjoint(b, c, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2AddLD_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &t4, &t5, &t6, &stack); @@ -442,7 +454,7 @@ static size_t ec2AddLD_deep(size_t n, size_t f_deep) static void ec2AddALD(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -451,9 +463,10 @@ static void ec2AddALD(word c[], const word a[], const word b[], ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ec2SeemsOnLD(a, ec)); ASSERT(ec2SeemsOnA(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * n)); - ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(b == c || wwIsDisjoint2(b, 2 * ec->f->n, c, 3 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2AddALD_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &t4, &stack); @@ -628,13 +641,14 @@ size_t ec2IsValid_deep(size_t n) bool_t ec2GroupSeemsValid(const ec_o* ec, void* stack) { - size_t n = ec->f->n; + size_t n; word* t1; /* [n + 1] */ word* t2; /* [n + 2] */ word* t3; /* [2 * n] */ // pre ASSERT(ecIsOperable(ec)); // разметить стек + n = ec->f->n; memSlice(stack, ec2GroupSeemsValid_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); @@ -683,13 +697,14 @@ size_t ec2GroupSeemsValid_deep(size_t n, size_t f_deep) bool_t ec2GroupIsSafe(const ec_o* ec, size_t mov_threshold, void* stack) { - size_t n1 = ec->f->n + 1; + size_t n1; word* t1; /* [n1] */ word* t2; /* [n1] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); // разметить стек + n1 = ec->f->n + 1; memSlice(stack, ec2GroupIsSafe_local(n1), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -751,12 +766,13 @@ size_t ec2GroupIsSafe_deep(size_t n) bool_t ec2IsOnA(const word a[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ // pre ASSERT(ecIsOperable(ec)); // разметить стек + n = ec->f->n; memSlice(stack, ec2IsOnA_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &stack); @@ -785,14 +801,12 @@ size_t ec2IsOnA_deep(size_t n, size_t f_deep) void ec2NegA(word b[], const word a[], const ec_o* ec) { - const size_t n = ec->f->n; - // pre ASSERT(ecIsOperable(ec)); ASSERT(ec2SeemsOnA(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); // b <- (xa, ya + xa) qrCopy(ecX(b), ecX(a), ec->f); - gf2Add(ecY(b, n), ecX(a), ecY(a, n), ec->f); + gf2Add(ecY(b, ec->f->n), ecX(a), ecY(a, ec->f->n), ec->f); } #define ec2AddAA_local(n)\ @@ -803,7 +817,7 @@ void ec2NegA(word b[], const word a[], const ec_o* ec) bool_t ec2AddAA(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t1; /* [n] */ word* t2; /* [n] */ word* t3; /* [n] */ @@ -811,8 +825,9 @@ bool_t ec2AddAA(word c[], const word a[], const word b[], const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(ec2SeemsOnA(a, ec)); ASSERT(ec2SeemsOnA(b, ec)); - ASSERT(wwIsDisjoint(a, c, 2 * n)); + ASSERT(wwIsDisjoint(a, c, 2 * ec->f->n)); // разметить стек + n = ec->f->n; memSlice(stack, ec2AddAA_local(n), SIZE_0, SIZE_MAX, &t1, &t2, &t3, &stack); @@ -882,9 +897,10 @@ size_t ec2AddAA_deep(size_t n, size_t f_deep) bool_t ec2SubAA(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { - const size_t n = ec->f->n; + size_t n; word* t; /* [2 * n] */ // разметить стек + n = ec->f->n; memSlice(stack, ec2SubAA_local(n), SIZE_0, SIZE_MAX, &t, &stack); diff --git a/src/math/ecp.c b/src/math/ecp.c index ee98572e..7f9cbcae 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.18 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -107,6 +107,17 @@ tpl-2007-bl из [BerLan07]. Сложность алгоритма: Реализован алгоритм tpl-2007-bl-2 из [BerLan07]. Сложность алгоритма: 7M + 7S + 13add + 2*4 + 1*8 + 1*12 + 1*16 + 1*3 \approx 14M. +\remark Целевые функции ci(l), определенные в описании реализации ecMulA() +в ec.c, принимают следующий вид: + c1(l) = l/3 11; + c2(l, w) = 103 + (2^{w-2} - 2)102 + l/(w + 1) 11; + c3(l, w) = 6 + (2^{w-2} - 2)16 + l/(w + 1) 16. +Расчеты показывают, что + с1(l) <= min_w c3(l), l <= 81, + min_w c3(l, w) <= min_w c2(l, w), l <= 899. +Поэтому для практически используемых размерностей l (192 <= l <= 571) +первые две стратегии являются проигрышными. + [BerLan07] Bernstein D., Lange T. Faster addition and doubling on elliptic curves. Advances in Cryptology -- ASIACRYPT 2007. https://eprint.iacr.org/2007/286.pdf. @@ -851,7 +862,7 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) ******************************************************************************* */ -#define ecpPmAAdd_local(n)\ +#define ecpNegAAdd_local(n)\ /* pt1 */ O_OF_W(3 * n),\ /* pt2 */ O_OF_W(3 * n),\ /* pt3 */ O_OF_W(3 * n),\ @@ -864,7 +875,7 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) /* t5 */ O_OF_W(n) // [2n]с <- (-1)^neg ([3n]a + [3n]b) (A <- J + J) -static bool_t ecpPmAAdd(word c[], const word a[], const word b[], +static bool_t ecpNegAAdd(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { size_t n; @@ -886,7 +897,7 @@ static bool_t ecpPmAAdd(word c[], const word a[], const word b[], // разметить стек n = ec->f->n; memSlice(stack, - ecpPmAAdd_local(n), SIZE_0, SIZE_MAX, + ecpNegAAdd_local(n), SIZE_0, SIZE_MAX, &pt1, &pt2, &pt3, &B3, &t0, &t1, &t2, &t3, &t4, &t5, &stack); // pt1 <- a (H <- J) qrMul(ecX(pt1), ecX(a), ecZ(a, n), ec->f, stack); @@ -948,14 +959,14 @@ static bool_t ecpPmAAdd(word c[], const word a[], const word b[], qrMul(ecX(c), ecX(pt3), t0, ec->f, stack); qrMul(ecY(c, n), ecY(pt3, n), t0, ec->f, stack); // c <- (-1)^neg c - zzPmMod(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); + zzNegModIf(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); return ret; } -static size_t ecpPmAAdd_deep(size_t n, size_t f_deep) +static size_t ecpNegAAdd_deep(size_t n, size_t f_deep) { return memSliceSize( - ecpPmAAdd_local(n), + ecpNegAAdd_local(n), f_deep, SIZE_MAX); } @@ -1020,7 +1031,7 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->dbl = bA3 ? ecpDblJA3 : ecpDblJ; ec->dbla = ecpDblAJ; ec->tpl = bA3 ? ecpTplJA3 : ecpTplJ; - ec->pmaadd = ecpPmAAdd; + ec->negaadd = ecpNegAAdd; ec->deep = utilMax(7, ecpToAJ_deep(f->n, f->deep), ecpAddJ_deep(f->n, f->deep), @@ -1028,7 +1039,7 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], bA3 ? ecpDblJA3_deep(f->n, f->deep) : ecpDblJ_deep(f->n, f->deep), ecpDblAJ_deep(f->n, f->deep), bA3 ? ecpTplJA3_deep(f->n, f->deep) : ecpTplJ_deep(f->n, f->deep), - ecpPmAAdd_deep(f->n, f->deep)); + ecpNegAAdd_deep(f->n, f->deep)); // настроить ec->hdr.keep = sizeof(ec_o) + memSliceSize(ecpCreateJ_state(f->n), SIZE_MAX); @@ -1059,7 +1070,7 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) ecpDblAJ_deep(n, f_deep), ecpTplJ_deep(n, f_deep), ecpTplJA3_deep(n, f_deep), - ecpPmAAdd_deep(n, f_deep)), + ecpNegAAdd_deep(n, f_deep)), SIZE_MAX); } diff --git a/src/math/pp/pp_etc.c b/src/math/pp/pp_etc.c index 0cd9d539..f49d7694 100644 --- a/src/math/pp/pp_etc.c +++ b/src/math/pp/pp_etc.c @@ -4,7 +4,7 @@ \brief Binary polynomials: other functions \project bee2 [cryptographic library] \created 2012.03.01 -\version 2025.09.26 +\version 2026.01.19 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -29,13 +29,9 @@ size_t ppDeg(const word a[], size_t n) ******************************************************************************* Неприводимость -Реализован алгоритм Бен-Ора [Ben-Or M. Probabilistic algorithms in -finite fields. In Proc. 22nd IEEE Symp. Foundations Computer Science, -1981, 394--398]. По оценкам [Gao S., Panario D. Test and Construction of -Irreducible Polynomials over Finite Fields] этот алгоритм обрабатывает -случайные многочлены значительно быстрее, чем алгоритм Рабина -[Rabin M. Probabilistic algorithms in finite fields. SIAM J. Comp. 9, -1980, 273--280]. +Реализован алгоритм Бен-Ора [Ben81]. По оценкам [GaoPan97] этот алгоритм +обрабатывает случайные многочлены значительно быстрее, чем алгоритм Рабина +[Rab80]. Алгоритм Рабина (m = deg(a)): для (i = 1,..., m) @@ -50,6 +46,15 @@ Irreducible Polynomials over Finite Fields] этот алгоритм обраб если (a, x^{2^i} - x) != 1 возвратить 0 возвратить 1 + +[Ben81] Ben-Or M. Probabilistic algorithms in finite fields. In Proc. 22nd + IEEE Symp. Foundations Computer Science, 1981, 394--398. +[GaoPan97] Gao S., Panario D. Test and Construction of Irreducible Polynomials + over Finite Fields. In: Cucker, F., Shub, M. (eds) Foundations + of Computational Mathematics. Springer, 1997. + https://doi.org/10.1007/978-3-642-60539-0_27. +[Rab80] Probabilistic algorithms in finite fields. SIAM J. Comp. 9, 1980, + 273--280. ******************************************************************************* */ @@ -105,7 +110,7 @@ size_t ppIsIrred_deep(size_t n) ******************************************************************************* Минимальные многочлены -Реализован следующий алгоритм определения минимального многочлена +В ppMinPoly() реализован следующий алгоритм определения минимального многочлена последовательности: aa <- a bb <- x^{2l} @@ -118,12 +123,17 @@ size_t ppIsIrred_deep(size_t n) (bb, aa) <- (aa, r) } вернуть da -Алгоритм формально определен в [Atti N.B., Diaz-Toca G.M., Lombardi H. -The Berlekamp-Massey Algorithm Revisited, AAECC (2006) 17: 75–82] и -неформально в [Shoup V. A Computational Introduction to Number Theory -and Algebra]. В последней работе можно найти обоснование алгоритма: -теорема 17.8, п. 17.5.1, рассуждения после теоремы 18.2. -Из этого обоснования, в частности, следует, что \deg da, \deg db <= l. +Алгоритм формально определен в [ADL06] и неформально в [Sho08]. В последней +работе можно найти обоснование алгоритма: теорема 17.8, п. 17.5.1, рассуждения +после теоремы 18.2. Из этого обоснования, в частности, следует, что +\deg da, \deg db <= l. + +В ppMinPoly() использована теория, изложенная в [Sho08; версия 2, глава 18]. + +[ADL06] Atti N.B., Diaz-Toca G.M., Lombardi H. The Berlekamp-Massey Algorithm + Revisited, AAECC (2006) 17: 75–82. +[Sho08] Теория изложена в [Shoup V. A Computational Introduction to Number + Theory and Algebra, http://www.shoup.net/ntb/, 2008. ******************************************************************************* */ diff --git a/src/math/pp/pp_mod.c b/src/math/pp/pp_mod.c index 5c838922..0df50a30 100644 --- a/src/math/pp/pp_mod.c +++ b/src/math/pp/pp_mod.c @@ -111,6 +111,12 @@ void ppDivMod(word b[], const word divident[], const word a[], ASSERT(wwCmp(divident, mod, n) < 0); ASSERT(n > 0 && mod[n - 1] != 0 && wwTestBit(mod, 0)); ASSERT(wwIsValid(b, n)); + // деление на 0? + if (wwIsZero(a, n)) + { + wwSetZero(b, n); + return; + } // разметить стек memSlice(stack, ppDivMod_local(n), SIZE_MAX, @@ -120,8 +126,8 @@ void ppDivMod(word b[], const word divident[], const word a[], wwSetZero(da, n); // u <- a, v <- mod wwCopy(u, a, n); - nu = wwWordSize(u, n); wwCopy(v, mod, n); + nu = wwWordSize(u, n); nv = n; // итерации со следующими инвариантами: // da0 * a \equiv divident * u \mod mod @@ -162,7 +168,6 @@ void ppDivMod(word b[], const word divident[], const word a[], } } // здесь v == \gcd(a, mod) - EXPECT(wwIsW(v, nv, 1)); // \gcd(a, mod) == 1 ? b <- da : b <- 0 if (wwIsW(v, nv, 1)) wwCopy(b, da, n); diff --git a/src/math/pri.c b/src/math/pri.c index 040cd0e7..c6a1db9a 100644 --- a/src/math/pri.c +++ b/src/math/pri.c @@ -4,7 +4,7 @@ \brief Prime numbers \project bee2 [cryptographic library] \created 2012.08.13 -\version 2025.10.22 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -510,18 +510,19 @@ size_t priIsSmooth_deep(size_t n) Применяется тест Рабина -- Миллера со специально подобранными основаниями. Успешное завершение теста на всех основаниях из списка _base16 гарантирует -простоту чисел вплоть до 1373653 [1], из списка _base32 --- вплоть -до 4759123141 [2], из списка _base64 --- для всех 64-разрядных чисел -(Sinclair, unpublished, see [3] and [4]). +простоту чисел вплоть до 1373653 [PSW80], из списка _base32 --- вплоть +до 4759123141 [Jae93], из списка _base64 --- для всех 64-разрядных чисел +(Sinclair, unpublished, см. [ForJan15] и [Izy24]). -[1] Pomerance C., Selfridge J., Wagstaff S. Jr. The pseudoprimes to 25 * 10^9. - Mathematics of Computation. 35(151): 1003–1026. -[2] Jaeschke G. On strong pseudoprimes to several bases. Mathematics - of Computation. 61(204): 915-925. -[3] Forisek M., Jancina, J. Fast Primality Testing for Integers That Fit into - a Machine Word. 2015. https://ceur-ws.org/Vol-1326/020-Forisek.pdf. -[4] Izykowski, W.: The best known SPRP bases sets. 2024-02-28. - https://miller-rabin.appspot.com/. +[PSW80] Pomerance C., Selfridge J., Wagstaff S. Jr. The pseudoprimes + to 25 * 10^9. Mathematics of Computation. 1980, 35(151): 1003–1026. +[Jae93] Jaeschke G. On strong pseudoprimes to several bases. Mathematics + of Computation. 1993, 61(204): 915-925. +[ForJan15] Forisek M., Jancina, J. Fast Primality Testing for Integers That + Fit into a Machine Word. 2015. + https://ceur-ws.org/Vol-1326/020-Forisek.pdf. +[Izy24] Izykowski, W.: The best known SPRP bases sets. 2024-02-28. + https://miller-rabin.appspot.com/. ******************************************************************************* */ diff --git a/src/math/zm.c b/src/math/zm.c index eba7e8c0..3fcbd7d5 100644 --- a/src/math/zm.c +++ b/src/math/zm.c @@ -4,7 +4,7 @@ \brief Quotient rings of integers modulo m \project bee2 [cryptographic library] \created 2013.09.14 -\version 2025.10.02 +\version 2026.01.19 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -118,8 +118,7 @@ static void zmInv(word b[], const word a[], const qr_o* r, void* stack) { ASSERT(zmIsOperable(r)); ASSERT(zmIsIn(a, r)); - wwIsZero(a, r->n) ? wwSetZero(b, r->n) : - zzInvMod(b, a, r->mod, r->n, stack); + zzInvMod(b, a, r->mod, r->n, stack); } static size_t zmInv_deep(size_t n) diff --git a/src/math/zz/zz_add.c b/src/math/zz/zz_add.c index 47baa50d..c4a3c2a3 100644 --- a/src/math/zz/zz_add.c +++ b/src/math/zz/zz_add.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: additive operations \project bee2 [cryptographic library] \created 2012.04.22 -\version 2025.07.24 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -14,6 +14,7 @@ #include "bee2/core/word.h" #include "bee2/math/ww.h" #include "bee2/math/zz.h" +#include "zz_lcl.h" /* ******************************************************************************* @@ -288,6 +289,25 @@ word zzSubW2(word a[], size_t n, register word w) return w; } +void zzSubIf(word c[], const word a[], const word b[], size_t n, + register word sub) +{ + register word mask; + size_t i; + ASSERT(wwIsDisjoint(c, a, n)); + ASSERT(wwIsSameOrDisjoint(c, b, n)); + ASSERT(wwCmp(a, b, n) >= 0); + ASSERT(sub == WORD_0 || sub == WORD_1); + // c <- sub ? -b : b + mask = WORD_0 - sub; + for (i = 0; i < n; ++i) + c[i] = b[i] ^ mask; + zzAddW2(c, n, sub); + // c <- sub ? a + c : c + zzAddAndW(c, a, n, mask); + CLEAN2(sub, mask); +} + void zzNeg(word b[], const word a[], size_t n) { size_t i; @@ -296,4 +316,3 @@ void zzNeg(word b[], const word a[], size_t n) b[i] = ~a[i]; zzAddW2(b, n, 1); } - diff --git a/src/math/zz/zz_etc.c b/src/math/zz/zz_etc.c index eddd1af0..c1b2b156 100644 --- a/src/math/zz/zz_etc.c +++ b/src/math/zz/zz_etc.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: other functions \project bee2 [cryptographic library] \created 2012.04.22 -\version 2025.09.13 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -86,10 +86,12 @@ word zzSubAndW(word b[], const word a[], size_t n, register word w) ******************************************************************************* Квадратичные вычеты -Реализован алгоритм 2.148 из [Menezes A., van Oorschot P., Vanstone S. -Handbook of Applied Cryptography] в редакции CТБ 34.101.45 (приложение Ж). +Реализован алгоритм 2.148 из [MOV96] в редакции CТБ 34.101.45 (приложение Ж). -В некоторых приложениях область определения символа Якоби расширяется +[MOV96] Menezes A., van Oorschot P., Vanstone S. Handbook of Applied + Cryptography. CRC Press, 1996. + +\remark В некоторых приложениях область определения символа Якоби расширяется до любых b по следующим правилам: (a / 2) = 0, если a четное, (a / 2) = (-1)^{(a^2 - 1) / 8}, если a нечетное. diff --git a/src/math/zz/zz_gcd.c b/src/math/zz/zz_gcd.c index 3c657315..baf3a872 100644 --- a/src/math/zz/zz_gcd.c +++ b/src/math/zz/zz_gcd.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: Euclidian gcd algorithms \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.18 +\version 2026.01.19 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -364,10 +364,16 @@ void zzDivMod(word b[], const word divident[], const word a[], word* da; /* [n] */ word* da1; /* [n] */ // pre - ASSERT(!wwIsZero(a, n) && wwCmp(a, mod, n) < 0); + ASSERT(wwCmp(a, mod, n) < 0); ASSERT(wwCmp(divident, mod, n) < 0); ASSERT(wwIsDisjoint(b, mod, n)); - ASSERT(zzIsOdd(mod, n) && mod[n - 1] != 0); + ASSERT(n > 0 && mod[n - 1] != 0 && zzIsOdd(mod, n)); + // деление на 0? + if (wwIsZero(a, n)) + { + wwSetZero(b, n); + return; + } // разметить стек memSlice(stack, zzDivMod_local(n), SIZE_MAX, @@ -421,7 +427,6 @@ void zzDivMod(word b[], const word divident[], const word a[], } } // здесь u == gcd(a, mod) - EXPECT(wwIsW(u, nu, 1)); // gcd(a, mod) != 1? b <- 0 if (!wwIsW(u, nu, 1)) wwSetZero(b, n); diff --git a/src/math/zz/zz_mod.c b/src/math/zz/zz_mod.c index 4e3841a8..8ba8eae8 100644 --- a/src/math/zz/zz_mod.c +++ b/src/math/zz/zz_mod.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: modular arithmetic \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.15 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -183,7 +183,7 @@ void zzNegMod(word b[], const word a[], const word mod[], size_t n) CLEAN(mask); } -void zzPmMod(word b[], const word a[], const word mod[], size_t n, +void zzNegModIf(word b[], const word a[], const word mod[], size_t n, register word neg) { register word mask; @@ -205,9 +205,7 @@ void zzPmMod(word b[], const word a[], const word mod[], size_t n, /* ******************************************************************************* -Модулярная арифметика: мультипликативные операции - -\remark Функции zzDivMod(), zzAlmostDivMod() реализованы в zz_gcd.c. +Модулярная арифметика: умножение ******************************************************************************* */ @@ -292,28 +290,6 @@ size_t zzSqrMod_deep(size_t n) SIZE_MAX); } -#define zzInvMod_local(n)\ -/* divident */ O_OF_W(n) - -void zzInvMod(word b[], const word a[], const word mod[], size_t n, - void* stack) -{ - word* divident; /* [n] */ - memSlice(stack, - zzInvMod_local(n), SIZE_0, SIZE_MAX, - ÷nt, &stack); - wwSetW(divident, n, 1); - zzDivMod(b, divident, a, mod, n, stack); -} - -size_t zzInvMod_deep(size_t n) -{ - return memSliceSize( - zzInvMod_local(n), - zzDivMod_deep(n), - SIZE_MAX); -} - void FAST(zzDoubleMod)(word b[], const word a[], const word mod[], size_t n) { register word carry = 0; @@ -409,7 +385,7 @@ void zzHalfMod(word b[], const word a[], const word mod[], size_t n) b[0] = a[0] + w; carry = wordLess01(b[0], w); b[0] >>= 1; - for(i = 1; i < n; ++i) + for (i = 1; i < n; ++i) { b[i] = a[i]; b[i] += carry; @@ -425,7 +401,43 @@ void zzHalfMod(word b[], const word a[], const word mod[], size_t n) CLEAN3(carry, mask, w); } -bool_t zzRandMod(word a[], const word mod[], size_t n, gen_i rng, +/* +******************************************************************************* +Модулярная арифметика: обращение + +Функция zzDivMod() реализована в модуле zz_gcd.c. +******************************************************************************* +*/ + +#define zzInvMod_local(n)\ +/* divident */ O_OF_W(n) + +void zzInvMod(word b[], const word a[], const word mod[], size_t n, + void* stack) +{ + word* divident; /* [n] */ + memSlice(stack, + zzInvMod_local(n), SIZE_0, SIZE_MAX, + ÷nt, &stack); + wwSetW(divident, n, 1); + zzDivMod(b, divident, a, mod, n, stack); +} + +size_t zzInvMod_deep(size_t n) +{ + return memSliceSize( + zzInvMod_local(n), + zzDivMod_deep(n), + SIZE_MAX); +} + +/* +******************************************************************************* +Модулярная арифметика: генерация вычетов +******************************************************************************* +*/ + +bool_t zzRandMod(word a[], const word mod[], size_t n, gen_i rng, void* rng_state) { register size_t l; diff --git a/src/math/zz/zz_mul.c b/src/math/zz/zz_mul.c index b73dda33..dcdcc843 100644 --- a/src/math/zz/zz_mul.c +++ b/src/math/zz/zz_mul.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: multiplicative operations \project bee2 [cryptographic library] \created 2012.04.22 -\version 2025.09.13 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -286,8 +286,7 @@ b = b[m - 1]...b[0] предварительно нормализуются: \opt Сокращать длину a при нулевом a[n]. Экономится одно деление 2by1. -Деление выполняется по алгоритму 14.20 из [Menezes A., van Oorschot P., -Vanstone S. Handbook of Applied Cryptography]: +Деление выполняется по алгоритму 14.20 из [MOV96]: for i = n, n - 1, ...., m: if a[i] == b[m - 1] (#) q[i - m] <- B - 1 @@ -304,6 +303,9 @@ Vanstone S. Handbook of Applied Cryptography]: заменить на while (q[i - m] * b[m - 2] > d * B + a[i - 2]) q[i - m]--, d += b[m - 1] + +[MOV96] Menezes A., van Oorschot P., Vanstone S. Handbook of Applied + Cryptography. CRC Press, 1996. ******************************************************************************* */ diff --git a/test/math/zz_test.c b/test/math/zz_test.c index da884542..04d14798 100644 --- a/test/math/zz_test.c +++ b/test/math/zz_test.c @@ -4,7 +4,7 @@ \brief Tests for multiple-precision unsigned integers \project bee2/test \created 2014.07.15 -\version 2026.01.15 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -95,6 +95,13 @@ static bool_t zzTestAdd() if (zzAdd(c, a, b, n) != 1 || !wwIsZero(c, n)) return FALSE; + // zzSub / zzSubIf + if (wwCmp(a, b, n) < 0) + wwSwap(a, b, n); + zzSubIf(c, a, b, n, 1); + if (zzSub(c1, a, b, n) || + !wwEq(c, c1, n)) + return FALSE; } // все нормально return TRUE; @@ -287,24 +294,24 @@ static bool_t zzTestMod() FAST(zzSubWMod)(t1, t, b[0], mod, n); if (!FAST(wwEq)(t1, a, n)) return FALSE; - // zzNegMod / zzPmMod + // zzNegMod / zzNegModIf zzNegMod(t, a, mod, n); - zzPmMod(t1, a, mod, n, 1); + zzNegModIf(t1, a, mod, n, 1); if (!wwEq(t, t1, n)) return FALSE; zzAddMod(t1, t, a, mod, n); if (!wwIsZero(t1, n)) return FALSE; - zzPmMod(t, a, mod, n, 0); + zzNegModIf(t, a, mod, n, 0); if (!wwEq(t, a, n)) return FALSE; zzNegMod(t1, t1, mod, n); if (!wwIsZero(t1, n)) return FALSE; - zzPmMod(t1, t1, mod, n, 0); + zzNegModIf(t1, t1, mod, n, 0); if (!wwIsZero(t1, n)) return FALSE; - zzPmMod(t1, t1, mod, n, 1); + zzNegModIf(t1, t1, mod, n, 1); if (!wwIsZero(t1, n)) return FALSE; // FAST(zzNegMod) @@ -333,7 +340,15 @@ static bool_t zzTestMod() return FALSE; if (zzJacobi(t1, n, mod, n, stack) == -1) return FALSE; + // zzMulWMod / zzMulMod + wwSetZero(b + 1, n - 1); + zzMulWMod(t, a, b[0], mod, n, stack); + zzMulMod(t1, a, b, mod, n, stack); + if (!wwEq(t, t1, n)) + return FALSE; // zzMulMod / zzDivMod / zzInvMod + if (wwIsZero(a, n)) + continue; zzGCD(t, a, n, mod, n, stack); if (wwCmpW(t, n, 1) != 0) continue; @@ -347,12 +362,6 @@ static bool_t zzTestMod() zzMulMod(t1, t1, a, mod, n, stack); if (!wwEq(t1, b, n)) return FALSE; - // zzMulWMod / zzMulMod - wwSetZero(b + 1, n - 1); - zzMulWMod(t, a, b[0], mod, n, stack); - zzMulMod(t1, a, b, mod, n, stack); - if (!wwEq(t, t1, n)) - return FALSE; // zzAlmostInvMod k = zzAlmostInvMod(t, a, mod, n, stack); while (k--) @@ -360,7 +369,17 @@ static bool_t zzTestMod() zzInvMod(t1, a, mod, n, stack); if (!wwEq(t, t1, n)) return FALSE; - + // zzInvMod, zzDivMod: исключительные случаи + wwSetZero(b, n); + zzInvMod(t, b, mod, n, stack); + if (!wwIsZero(t, n)) + return FALSE; + zzDivMod(t, b, a, mod, n, stack); + if (!wwIsZero(t, n)) + return FALSE; + zzDivMod(t, b, b, mod, n, stack); + if (!wwIsZero(t, n)) + return FALSE; } // все нормально return TRUE; From f29911d4e92c8b0462509aff7095bfe01e5f8e3a Mon Sep 17 00:00:00 2001 From: agievich Date: Tue, 20 Jan 2026 23:20:32 +0300 Subject: [PATCH 14/77] Update test/math/ecp and pp --- include/bee2/math/ec.h | 2 +- test/math/ecp_test.c | 57 ++++++++++++++++++++++++++++------ test/math/pp_test.c | 69 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 12 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 034d4bb0..600fe031 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -95,7 +95,7 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). данные, с помощью которых можно ускорить вычисления на эллиптической кривой. Описание эллиптической кривой включает указатели на функции арифметики -в группе точек этой кривой. Функции интерфейсов ec_tpl_i и ec_smult_i +в группе точек этой кривой. Функции интерфейсов ec_tpl_i и ec_smul_i можно не поддерживать. Указатель на неподдерживаемую функцию должен быть нулевым. diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 5ec72f95..a6afd84b 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -60,7 +60,7 @@ bool_t ecpTest() ec_o* ec; /* [ec_keep] */ qr_o* f; /* [f_keep] */ octet* t; /* [5 * no] */ - word* pts; /* [4 * n] */ + word* pts; /* [4 * 3 * n] */ word* d; /* [n] */ void* stack; // создать состояние @@ -68,7 +68,7 @@ bool_t ecpTest() ec_keep, f_keep, 5 * no, - O_OF_W(4 * n), + O_OF_W(4 * 3 * n) | SIZE_HI, O_OF_W(n), utilMax(13, gfpCreate_deep(no), @@ -132,18 +132,18 @@ bool_t ecpTest() !ecpAddAA(pts + 2 * n, ec->base, ec->base, ec, stack) || !ecpAddAA(pts + 2 * n, pts + 2 * n, pts + 2 * n, ec, stack) || !ecpSubAA(pts + 2 * n, pts + 2 * n, ec->base, ec, stack) || - !memEq(pts, pts + 2 * n, 2 * n) || + !wwEq(pts, pts + 2 * n, 2 * n) || (ecpNegA(pts + 2 * n, pts + 2 * n, ec), ecpAddAA(pts + 2 * n, pts, pts + 2 * n, ec, stack)) || // вычислить кратную точку !ecMulA(pts + 2 * n, ec->base, ec, d, 1, stack) || - !memEq(pts, pts + 2 * n, 2 * n) || + !wwEq(pts, pts + 2 * n, 2 * n) || // утроить напрямую !ec->froma || !ec->tpl || !ec->toa || (ec->froma(pts + 2 * n, ec->base, ec, stack), ec->tpl(pts + 2 * n, pts + 2 * n, ec, stack), ec->toa(pts + 2 * n, pts + 2 * n, ec, stack), - !memEq(pts, pts + 2 * n, 2 * n))) + !wwEq(pts, pts + 2 * n, 2 * n))) { blobClose(state); return FALSE; @@ -182,7 +182,6 @@ bool_t ecpTest() ASSERT(wwIsZero(ec->base, n)); // утроить базовую точку разными способами { - word* pts = (word*)t; // d <- 3 d[0] = 3; // удвоить и сложить @@ -193,21 +192,61 @@ bool_t ecpTest() !ecpAddAA(pts + 2 * n, ec->base, ec->base, ec, stack) || !ecpAddAA(pts + 2 * n, pts + 2 * n, pts + 2 * n, ec, stack) || !ecpSubAA(pts + 2 * n, pts + 2 * n, ec->base, ec, stack) || - !memEq(pts, pts + 2 * n, 2 * n) || + !wwEq(pts, pts + 2 * n, 2 * n) || (ecpNegA(pts + 2 * n, pts + 2 * n, ec), ecpAddAA(pts + 2 * n, pts, pts + 2 * n, ec, stack)) || // вычислить кратную точку !ecMulA(pts + 2 * n, ec->base, ec, d, 1, stack) || - !memEq(pts, pts + 2 * n, 2 * n) || + !wwEq(pts, pts + 2 * n, 2 * n) || (ec->froma(pts + 2 * n, ec->base, ec, stack), ec->tpl(pts + 2 * n, pts + 2 * n, ec, stack), ec->toa(pts + 2 * n, pts + 2 * n, ec, stack), - !memEq(pts, pts + 2 * n, 2 * n))) + !wwEq(pts, pts + 2 * n, 2 * n))) { blobClose(state); return FALSE; } } + // малые кратные разными способами + if (ec->smul) + { + // (pts[0], ..., pts[3]) <- (base, 3 base, 5 base, 7 base) + ec->smul(pts, ec->base, 3, ec, stack); + // pts[2] <- 7 base + d[0] = 7; + ecMulA(pts + 2 * 3 * n, ec->base, ec, d, 1, stack); + // pts[2] ==? pts[3] + ec->toa(pts + 3 * 3 * n, pts + 3 * 3 * n, ec, stack); + if (!wwEq(pts + 2 * 3 * n, pts + 3 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + if (ec->negaadd) + { + // pts[2] <- 4 base + d[0] = 4; + ecMulA(pts + 2 * 3 * n, ec->base, ec, d, 1, stack); + // pts[3] <- pts[0] + prs[1] = 4 base + ec->negaadd(pts + 3 * 3 * n, pts, pts + 1 * 3 * n, 0, ec, stack); + // pts[2] ==? pts[3] + if (!wwEq(pts + 2 * 3 * n, pts + 3 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[2] <- -4 base + ec->neg(pts + 2 * 3 * n, pts + 2 * 3 * n, ec, stack); + // pts[3] <- -(pts[0] + prs[1]) = -4 base + ec->negaadd(pts + 3 * 3 * n, pts, pts + 1 * 3 * n, 1, ec, stack); + // pts[2] ==? pts[3] + if (!wwEq(pts + 2 * 3 * n, pts + 3 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + } // все хорошо blobClose(state); return TRUE; diff --git a/test/math/pp_test.c b/test/math/pp_test.c index e6870b18..bbdf664d 100644 --- a/test/math/pp_test.c +++ b/test/math/pp_test.c @@ -4,7 +4,7 @@ \brief Tests for the arithmetic of binary polynomials \project bee2/test \created 2023.11.09 -\version 2025.09.28 +\version 2026.01.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +115,70 @@ static bool_t ppTestExps16() return TRUE; } +static bool_t ppTestMod() +{ + enum { n = 8 }; + size_t reps = 500; + word a[n]; + word b[n]; + word t[n]; + word t1[n]; + word mod[n]; + mem_align_t state[64 / sizeof(mem_align_t)]; + mem_align_t stack[2048 / sizeof(mem_align_t)]; + // подготовить память + if (sizeof(state) < prngCOMBO_keep() || + sizeof(stack) < utilMax(6, + ppMulMod_deep(n), + ppSqrMod_deep(n), + ppMod_deep(n, n), + ppGCD_deep(n, n), + ppDivMod_deep(n), + ppInvMod_deep(n))) + return FALSE; + // инициализировать генератор COMBO + prngCOMBOStart(state, utilNonce32()); + // сложение / вычитание + while (reps--) + { + // генерация + prngCOMBOStepR(mod, O_OF_W(n), state); + prngCOMBOStepR(a, O_OF_W(n), state); + prngCOMBOStepR(b, O_OF_W(n), state); + if (mod[n - 1] == 0) + mod[n - 1] = WORD_MAX; + ppMod(a, a, n, mod, n, stack); + ppMod(b, b, n, mod, n, stack); + // zzMulMod / zzDivMod / zzInvMod + if (wwIsZero(a, n)) + continue; + ppGCD(t, a, n, mod, n, stack); + if (wwCmpW(t, n, 1) != 0) + continue; + ppInvMod(t, a, mod, n, stack); + ppMulMod(t, t, b, mod, n, stack); + ppDivMod(t1, b, a, mod, n, stack); + if (!wwEq(t, t1, n)) + return FALSE; + ppMulMod(t1, t1, a, mod, n, stack); + if (!wwEq(t1, b, n)) + return FALSE; + // ppInvMod, ppDivMod: исключительные случаи + wwSetZero(b, n); + ppInvMod(t, b, mod, n, stack); + if (!wwIsZero(t, n)) + return FALSE; + ppDivMod(t, b, a, mod, n, stack); + if (!wwIsZero(t, n)) + return FALSE; + ppDivMod(t, b, b, mod, n, stack); + if (!wwIsZero(t, n)) + return FALSE; + } + // все нормально + return TRUE; +} + /* ******************************************************************************* Тест редукций @@ -222,5 +287,5 @@ static bool_t ppTestRed() bool_t ppTest() { - return ppTestExps16() && ppTestRed(); + return ppTestExps16() && ppTestMod() && ppTestRed(); } From 741cea6e4d2dc5211621635b19878fcb83fda224 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 21 Jan 2026 20:31:16 +0300 Subject: [PATCH 15/77] Add ecpDblAddA() and ecpFinAddA() --- include/bee2/math/ec.h | 132 ++++++++------ src/math/ec.c | 107 ++++++------ src/math/ec2.c | 36 ++-- src/math/ecp.c | 389 +++++++++++++++++++++++++++++++++++------ test/math/ecp_test.c | 174 +++++++++++++----- test/math/pp_test.c | 3 +- 6 files changed, 614 insertions(+), 227 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 600fe031..b5950632 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.20 +\version 2026.01.21 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -95,9 +95,9 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). данные, с помощью которых можно ускорить вычисления на эллиптической кривой. Описание эллиптической кривой включает указатели на функции арифметики -в группе точек этой кривой. Функции интерфейсов ec_tpl_i и ec_smul_i -можно не поддерживать. Указатель на неподдерживаемую функцию должен быть -нулевым. +в группе точек этой кривой. Функции интерфейсов ec_dbladd_i, ec_dbladda_i, +ec_tpl_i, ec_finadd_i, ec_finadda_i и ec_smul_i можно не поддерживать. +Указатель на неподдерживаемую функцию должен быть нулевым. Описание кольца организовано как объект, и можно применять функции, описанные в заголовочном файле obj.h. @@ -287,53 +287,54 @@ typedef void (*ec_dbla_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Утроение точки +/*! \brief Удвоение и сложение с аффинной точкой - На эллиптической кривой ec определяется точка [ec->d * ec->f->n]b, - полученная утроением точки [ec->d * ec->f->n]a: + На эллиптической кривой ec определяется точка [ec->d * ec->f->n]c, + которая является суммой удвоенной точки [ec->d * ec->f->n]a и + аффинной точки [ec->d * ec->f->n]b: \code - b <- 3 a. + с <- 2 a + b. \endcode \pre Описание ec работоспособно. - \pre Буфер b либо не пересекается, либо совпадает с буфером a. - \pre Координаты a лежат в базовом поле. + \pre Буфер с либо не пересекается с буфером a, либо совпадает с ним. + \pre Буфер с не пересекается с буфером b. + \pre Координаты a и b лежат в базовом поле. \expect Описание ec корректно. - \expect Точка a лежит на кривой. + \expect Точки a и b лежат на кривой. + \remark Функция интерфейса ec_dbladda_i может использоваться в алгоритмах + вычисления кратной точки. Функцию следует реализовать, если она дает + выигрыш по сравнению с каскадом функций интерфейсов ec_dbl_i (удвоение) + и ec_adda_i (сложение с аффинной точкой). */ -typedef void (*ec_tpl_i)( - word b[], /*!< [out] утроенная точка */ - const word a[], /*!< [in] первоначальная точка */ +typedef void(*ec_dbladda_i)( + word c[], /*!< [out] сумма */ + const word a[], /*!< [in] удваиваемая точка */ + const word b[], /*!< [in] второе слагаемое */ const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Малые нечетные кратные +/*! \brief Утроение точки - На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных - аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере - [2^{w-1} * ec->d * ec->f->n]b: + На эллиптической кривой ec определяется точка [ec->d * ec->f->n]b, + полученная утроением точки [ec->d * ec->f->n]a: \code - (b[0], ..., b[2^{w-1} - 1]) <- (a, 3a, ..., (2^w - 1)a). + b <- 3 a. \endcode - Каждое кратное занимает ec->d * ec->f->n машинных слов. - \pre w > 0. \pre Описание ec работоспособно. - \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. + \pre Буфер b либо не пересекается, либо совпадает с буфером a. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на кривой. - \post Точка, составленная из двух первых координат b[0], совпадает с a и, - таким образом, является аффинной точкой кривой. */ -typedef void (*ec_smul_i)( - word b[], /*!< [out] малые кратные */ +typedef void (*ec_tpl_i)( + word b[], /*!< [out] утроенная точка */ const word a[], /*!< [in] первоначальная точка */ - size_t w, /*!< [in] ширина окна */ const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Сложение с настройкой знака суммы +/*! \brief Финишное сложение На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, которая является суммой точек [ec->d * ec->f->n]a и [ec->d * ec->f->n]b @@ -350,12 +351,12 @@ typedef void (*ec_smul_i)( \expect Точки a и b лежат на кривой. \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно удаленной (a + b == O). - \remark Функции интерфейса ec_negaadd_i предназначены для использования + \remark Функции интерфейса ec_finadd_i предназначены для использования в регулярных алгоритмах вычисления кратной точки. Предполагается, что сложение a + b выполняется по полным (complete) формулам, одними и теми же для случаев a == b, a == -b и a != \pm b. */ -typedef bool_t(*ec_negaadd_i)( +typedef bool_t(*ec_finadd_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ @@ -364,7 +365,7 @@ typedef bool_t(*ec_negaadd_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Сложение с аффинной точкой с настройкой знака суммы +/*! \brief Финишное сложение с аффинной точкой На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, которая является суммой точки [ec->d * ec->f->n]a и аффинной точки @@ -383,12 +384,12 @@ typedef bool_t(*ec_negaadd_i)( работает с a == b и a != b. \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно удаленной (a + b == O). - \remark Функции интерфейса ec_negaadda_i предназначены для использования + \remark Функции интерфейса ec_finadda_i предназначены для использования в регулярных алгоритмах вычисления кратной точки. Предполагается, что сложение a + b выполняется по полным (complete) формулам, одними и теми же для случаев a == b, a == -b и a != \pm b. */ -typedef bool_t(*ec_negaadda_i)( +typedef bool_t(*ec_finadda_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ @@ -397,6 +398,32 @@ typedef bool_t(*ec_negaadda_i)( void* stack /*!< [in] вспомогательная память */ ); +/*! \brief Малые нечетные кратные + + На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных + аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере + [2^{w-1} * ec->d * ec->f->n]b: + \code + (b[0], ..., b[2^{w-1} - 1]) <- (a, 3a, ..., (2^w - 1)a). + \endcode + Каждое кратное занимает ec->d * ec->f->n машинных слов. + \pre w > 0. + \pre Описание ec работоспособно. + \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. + \post Точка, составленная из двух первых координат b[0], совпадает с a и, + таким образом, является аффинной точкой кривой. +*/ +typedef void(*ec_smul_i)( + word b[], /*!< [out] малые кратные */ + const word a[], /*!< [in] первоначальная точка */ + size_t w, /*!< [in] ширина окна */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ + ); + /*! \brief Описание эллиптической кривой Описывается эллиптическая кривая, правила представления ее элементов, @@ -425,9 +452,10 @@ typedef struct ec_o ec_dbl_i dbl; /*!< функция удвоения */ ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ + ec_dbladda_i dbladda; /*!< функция удвоения и сложения с афф. точкой */ + ec_finadd_i finadd; /*!< функция финишного сложения */ + ec_finadda_i finadda; /*!< функция финишного сложения с афф. точкой */ ec_smul_i smul; /*!< функция расчета малых кратных */ - ec_negaadd_i negaadd; /*!< функция сложения c настройкой знака суммы */ - ec_negaadda_i negaadda; /*!< функция сложения с афф. т. с настр. знака */ size_t deep; /*!< максимальная глубина стека функций */ mem_align_t descr[]; /*!< память для размещения данных */ } ec_o; @@ -473,18 +501,22 @@ typedef struct ec_o #define ecAddA(c, a, b, ec, stack)\ (ec)->adda(c, a, b, ec, stack) -#define ecSub(c, a, b, ec, stack)\ - (ec)->sub(c, a, b, ec, stack) - -#define ecSubA(c, a, b, ec, stack)\ - (ec)->suba(c, a, b, ec, stack) - #define ecDbl(b, a, ec, stack)\ (ec)->dbl(b, a, ec, stack) #define ecDblA(b, a, ec, stack)\ (ec)->dbla(b, a, ec, stack) +#define ecDblAdd(c, a, b, ec, stack)\ + ((ec)->dbladd ?\ + (ec)->dbladd(c, a, b, ec, stack) :\ + (ecDbl(c, a, ec, stack), ecAdd(c, c, b, ec, stack))) + +#define ecDblAddA(c, a, b, ec, stack)\ + ((ec)->dbladda ?\ + (ec)->dbladda(c, a, b, ec, stack) :\ + (ecDbl(c, a, ec, stack), ecAddA(c, c, b, ec, stack))) + #define ecSetO(a, ec)\ wwSetZero(ecZ(a, (ec)->f->n), (ec)->f->n) @@ -601,14 +633,7 @@ bool_t ecGroupIsOperable( \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. \deep{stack} ecMulA_deep(ec->f->n, ec->d, ec->deep, m). - \safe Функция регулярна по [m]d при выполнении следующих условий: - - описание группы точек в ec работоспособно; - - ec->order --- нечетное число; - - длина ec->order в машинных словах равняется m; - - m > 1; - - 0 < d && d < ec->order; - - ec->pmaadd != 0; - - функции ec->add, ec->dbl и ec->pmaadd регулярны. + \safe Функция нерегулярна. */ bool_t ecMulA( word b[], /*!< [out] кратная точка */ @@ -640,8 +665,13 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); случае. \deep{stack} ecMulA2_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - - ec->pmaadd != 0; - - функции ec->add, ec->dbl и ec->pmaadd регулярны. + - ec->pmaadd != 0 и функция ec->pmaadd регулярна: сложение и удвоение + выполняются по одним и тем же формулам без условных переходов; + - функция ec->dbl регулярна: удвоение точек, отличных от O, выполняется + без условных переходов; + - функция ec->add (ec->dbladd == 0) или ec->dbladd (ec->dbladd != 0) + регулярна: сложение различных и не взаимно обратных точек выполняется + без условных переходов. */ bool_t ecMulA2( word b[], /*!< [out] кратная точка */ diff --git a/src/math/ec.c b/src/math/ec.c index 23498a9e..6d84f4fc 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.20 +\version 2026.01.21 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -162,16 +162,16 @@ static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) ******************************************************************************* */ -#define ecNegAAdd_local(n, ec_d)\ +#define ecFinAdd_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static bool_t ecNegAAdd(word c[], const word a[], const word b[], +static bool_t ecFinAdd(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ ASSERT(ecIsOperable(ec)); memSlice(stack, - ecNegAAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecFinAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); ecAdd(t, a, b, ec, stack); if (neg) @@ -179,24 +179,24 @@ static bool_t ecNegAAdd(word c[], const word a[], const word b[], return ecToA(c, t, ec, stack); } -static size_t ecNegAAdd_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecFinAdd_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecNegAAdd_local(n, ec_d), + ecFinAdd_local(n, ec_d), ec_deep, SIZE_MAX); } -#define ecNegAAddA_local(n, ec_d)\ +#define ecFinAddA_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static bool_t ecNegAAddA(word c[], const word a[], const word b[], +static bool_t ecFinAddA(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ ASSERT(ecIsOperable(ec)); memSlice(stack, - ecNegAAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecFinAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); ecAddA(t, a, b, ec, stack); if (neg) @@ -204,10 +204,10 @@ static bool_t ecNegAAddA(word c[], const word a[], const word b[], return ecToA(c, t, ec, stack); } -static size_t ecNegAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecNegAAddA_local(n, ec_d), + ecFinAddA_local(n, ec_d), ec_deep, SIZE_MAX); } @@ -222,27 +222,27 @@ static size_t ecNegAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) Используются малые нечетные кратные a: b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. -Имеются три стратегии: -1) w = 2 и малые кратные вообще не рассчитываются; -2) w > 2 и малые кратные рассчитываются в аффинных координатах; -3) w > 2 и малые кратные рассчитываются в проективных координатах. +Имеются три стратегии: +1) w = 2 и малые кратные вообще не рассчитываются; +2) w > 2 и малые кратные рассчитываются в аффинных координатах; +3) w > 2 и малые кратные рассчитываются в проективных координатах. Если малые кратные рассчитываются по схеме "последовательно складывать с удвоенной исходной точкой", то средняя общая сложность нахождения -кратной точки (l = wwBitSize(d)): -1) c1(l) = l/3(P <- P + A); +кратной точки (l = wwBitSize(d)): +1) c1(l) = l/3(P <- P + A); 2) c2(l, w) = 1(A <- 2A) + 1(P <- P + A) + - (2^{w-2} - 2)(A <- A + A) + l/(w + 1)(P <- P + A); + (2^{w-2} - 2)(A <- A + A) + l/(w + 1)(P <- P + A); 3) c3(l, w) = 1(P <- 2A) + 1(P <- P + A) + - (2^{w-2} - 2)(P <- P + P) + l/(w + 1)(P <- P + P), + (2^{w-2} - 2)(P <- P + P) + l/(w + 1)(P <- P + P), без учета общего во всех стратегиях слагаемого l(P <- 2P). Здесь -- (A <- 2A) -- время работы каскада (ec->dbla, ec->toa)*; -- (A <- A + A) -- время работы каскада (ec->adda, ec->toa)*; +- (A <- 2A) -- время работы каскада (ec->dbla, ec->toa)*; +- (A <- A + A) -- время работы каскада (ec->adda, ec->toa)*; - (P <- 2P) -- время работы функции ec->dbl. ------------------------------------------------------ -* [или прямых вычислений в аффинных координатах] +----------------------------------------------------- +* [или прямых вычислений в аффинных координатах] Реализована третья стратегия. Она является выигрышной для кривых над GF(p) в практических диапазонах размерностей при использовании наиболее эффективных @@ -255,18 +255,18 @@ static size_t ecNegAAddA_deep(size_t n, size_t ec_d, size_t ec_deep) в описании кривой. Если функция не задана в описании, то используется функция по умолчанию ecSmul(). -\todo Усилить вторую стратегию. Рассчитать малые кратные в проективных -координатах, а затем быстро перейти к аффинным координатам с помощью -трюка Монтгомери [Doc05; algorithm 11.15 -- simultaneous inversion, p. 209]: - U_1 <- Z_1 - for t = 2,..., T: U_t <- U_{t-1} Z_t - V <- U_T^{-1} - for t = T,..., 2: - Z_t^{-1} <- V U_{t-1} - V <- V Z_t - Z_1^{-1} <- V - -[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and +\todo Усилить вторую стратегию. Рассчитать малые кратные в проективных +координатах, а затем быстро перейти к аффинным координатам с помощью +трюка Монтгомери [Doc05; algorithm 11.15 -- simultaneous inversion, p. 209]: + U_1 <- Z_1 + for t = 2,..., T: U_t <- U_{t-1} Z_t + V <- U_T^{-1} + for t = T,..., 2: + Z_t^{-1} <- V U_{t-1} + V <- V Z_t + Z_1^{-1} <- V + +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. [HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004. @@ -310,9 +310,9 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, // расчет NAF ASSERT(naf_width >= 3); naf_size = wwNAF(naf, d, m, naf_width); - // d == O => b <- O - if (naf_size == 0) - return FALSE; + // d == O => b <- O + if (naf_size == 0) + return FALSE; // малые кратные: a, 3a, ..., (2^w - 1)a if (ec->smul) ec->smul(pre, a, naf_width - 1, ec, stack); @@ -331,23 +331,20 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, i = naf_width; while (--naf_size) { - // t <- 2 t - ecDbl(t, t, ec, stack); - // обработать цифру digit = wwGetBits(naf, i, naf_width); - if (digit & 1) + // t <- 2t + pre[digit / 2] + if (digit == 1 || digit == (naf_hi ^ 1)) + ecDblAddA(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); + else { - // t <- t + pre[digit / 2] - if (digit == 1 || digit == (naf_hi ^ 1)) - ecAddA(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); - else + // t <- 2t + ecDbl(t, t, ec, stack); + if (digit & 1) + // t <- t + pre[digit / 2] ecAdd(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); - // к следующей цифре - i += naf_width; } - else - // к следующей цифре - ++i; + // к следующей цифре + i += (digit & 1) ? naf_width : 1; } // очистка CLEAN2(digit, i); @@ -491,10 +488,10 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, snz_width); ASSERT(digit & 1); - ret = (ec->negaadd) ? - ec->negaadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, + ret = (ec->finadd) ? + ec->finadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, neg, ec, stack) : - ecNegAAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, + ecFinAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); @@ -509,7 +506,7 @@ size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ecMulA2_local(n, ec_d, m, pre_count), utilMax(2, ecSmul_deep(n, ec_d, ec_deep), - ecNegAAdd_deep(n, ec_d, ec_deep)), + ecFinAdd_deep(n, ec_d, ec_deep)), SIZE_MAX); } diff --git a/src/math/ec2.c b/src/math/ec2.c index fe98a605..20cf0e45 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.20 +\version 2026.01.21 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -22,10 +22,14 @@ ******************************************************************************* Общие положения -Ссылки на все реализованные алгоритмы имеются на сайте -http://www.hyperelliptic.org/efd. Там же можно найти соглашения по -обозначению сложности алгоритмов. В этих обозначениях фигурируют -следующие формальные выражения: +Используются проективные координаты Лопеса -- Дахаба (LD, [LopDah98]): + x = X / Z, y = Y / Z^2, + O = (1 : 0 : 0), + -(X : Y : Z) = (X : ZX + Y : Z). + +Основные алгоритмы представлены на сайте http://www.hyperelliptic.org/efd. +Там же можно найти соглашения по обозначению сложности алгоритмов. В этих +обозначениях фигурируют следующие формальные выражения: add -- сложение или вычитание в GF(2^m), c -- умножение на малую константу c в GF(2^m), *A -- умножение на коэффициент A в GF(2^m), @@ -39,9 +43,9 @@ распространенных на практике случаях A \in {0, 1} и 1*A = 0M. \warning Соотношение 1D = 24M получено экспериментальным путем -на платформе x86 и возможно требует пересмотра. -На упомянутом сайте http://www.hyperelliptic.org/efd используется другое -соотношение: 1D = 10M. Считаем его слишком оптимистичным (по отношению к D). +на платформе x86 и возможно требует пересмотра. На сайте +http://www.hyperelliptic.org/efd используется другое соотношение: 1D = 10M. +Считаем его слишком оптимистичным (по отношению к D). Используются обозначения: A <- A + A -- сложение аффинных точек, @@ -50,6 +54,9 @@ P <- P + A -- добавление к проективной точке аффинной; P <- 2P -- удвоение проективной точки; P <- 2A -- удвоение аффинной точки с переходом к проективным координатам. + +[LopDah98] Lopez J., Dahab R. Improved algorithms for elliptic curve arithmetic + in GF(2^n), Tech. Report IC-98-39, Relatorio Tecnico, October 1998. ******************************************************************************* */ @@ -62,13 +69,7 @@ /* ******************************************************************************* -Кривая в проективных координатах Лопеса -- Дахаба (LD, [LopDah98]): - x = X / Z, y = Y / Z^2, - O = (1 : 0 : 0), - -(X : Y : Z) = (X : ZX + Y : Z). - -\warning Ошибка в книге [HMV04] при определении обратной точки -в LD-координатах. +Базовые операции В функции ec2DblLD() выполняется удвоение P <- 2P. Реализован алгоритм dbl-2005-dl [DocLan05;LopDah98]. Сложность алгоритма: @@ -104,6 +105,9 @@ add-2005-dl [DocLan05; HigTak00]). Сложность алгоритма: Поэтому для практически используемых размерностей l (39 <= l) первая и третья стратегии являются проигрышными. Реализована только стратегия 2. +\warning Ошибка в книге [HMV04] при определении обратной точки +в LD-координатах. + \todo Реализовать быстрые формулы для особенных B: B = 1 (кривые Коблица), известен \sqrt{B}. @@ -121,8 +125,6 @@ add-2005-dl [DocLan05; HigTak00]). Сложность алгоритма: [HigTak00] Higuchi A., Takagi N. A fast addition algorithm for elliptic curve arithmetic in GF(2^n) using projective coordinates, Inform. Process. Lett. 76 (2000), 101–103. -[LopDah98] Lopez J., Dahab R. Improved algorithms for elliptic curve arithmetic - in GF(2^n), Tech. Report IC-98-39, Relatorio Tecnico, October 1998. ******************************************************************************* */ diff --git a/src/math/ecp.c b/src/math/ecp.c index 7f9cbcae..015e76bf 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.20 +\version 2026.01.21 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -23,10 +23,13 @@ ******************************************************************************* Общие положения -Ссылки на все реализованные алгоритмы имеются на сайте -http://www.hyperelliptic.org/efd. Там же можно найти соглашения по -обозначению сложности алгоритмов. В этих обозначениях фигурируют -следующие формальные выражения: +Используются якобиевы (Jacobian, J) проективные координаты: + x = X / Z^2, y = Y / Z^3, + -(X : Y : Z) = (X : -Y : Z). + +Основные алгоритмы представлены на сайте http://www.hyperelliptic.org/efd. +Там же можно найти соглашения по обозначению сложности алгоритмов. В этих +обозначениях фигурируют следующие формальные выражения: add -- сложение или вычитание \mod p, c -- умножение на малую константу c (2,3,...) \mod p, half -- умножение на 2^{-1} \mod p, @@ -41,10 +44,10 @@ Используются обозначения: A <- A + A -- сложение аффинных точек, A <- 2A -- удвоение аффинной точки; - P <- P + P -- сложение проективных точек; - P <- P + A -- добавление к проективной точке аффинной; - P <- 2P -- удвоение проективной точки; - P <- 2A -- удвоение аффинной точки с переходом к проективным координатам. + J <- J + J -- сложение якобиевых точек; + J <- J + A -- добавление к якобиевой точке аффинной; + J <- 2J -- удвоение якобиевой точки; + J <- 2A -- удвоение аффинной точки с переходом к якобиевым координатам. ******************************************************************************* */ @@ -57,55 +60,49 @@ /* ******************************************************************************* -Якобиевы (Jacobian, J) координаты: - x = X / Z^2, y = Y / Z^3, - -(X : Y : Z) = (X : -Y : Z). +Базовые операции -В функции ecpDblJ() выполняется удвоение P <- 2P. Реализован -алгоритм dbl-1998-hnm из [HNM98]. Сложность алгоритма: +В функции ecpDblJ() выполняется удвоение J <- 2J. Реализован алгоритм +dbl-1998-hnm из [HNM98]. Сложность алгоритма: 3M + 6S + 1*A + 1half + 6add + 3*2 \approx 9M. \todo Сравнить dbl-1998-hnm с dbl-2007-bl, сложность которого 1M + 8S + 1A + 10add + 1*8 + 2*2 + 1*3. -В функции ecpDblJA3() выполняется удвоение P <- 2P для случая A = -3. +В функции ecpDblJA3() выполняется удвоение J <- 2J для случая A = -3. Реализован алгоритм dbl-1998-hnm2 из [HNM98]. Сложность алгоритма: 4M + 4S + 1*half + 7add + 3*2 \approx 8M. \todo Сравнить dbl-1998-hnm2 с dbl-2001-b, сложность которого 3M +5S + 8add + 1*4 + 2*8 + 1*3. -В функции ecpDblAJ() выполняется удвоение P <- 2A. Реализован алгоритм +В функции ecpDblAJ() выполняется удвоение J <- 2A. Реализован алгоритм mdbl-2007-bl из [BerLan07]. Сложность алгоритма: 1M + 5S + 7add + 1*8 + 3*2 + 1*3 \approx 6M. -В функции ecpAddJ() выполняется сложение P <- P + P. Реализован алгоритм +В функции ecpAddJ() выполняется сложение J <- J + J. Реализован алгоритм add-2007-bl из [BerLan07]. Сложность алгоритма: 11M + 5S + 9add + 4*2 \approx 16M. \todo Сравнить add-2007-bl с add-1998-cmo-2, сложность которого 12M + 4S + 6add + 1*2. -\todo При выполнении функции ecpAddAJ() может выясниться, что -складываются одинаковые точки. В этом случае вызывается функция -ecpDblAJ(). Подумать, как "помочь" этой функции, передав уже -рассчитанные значения, например, za^2. Аналогичное замечание касается -функции ecpAddJ(). - -В функции ecpAddAJ() выполняется сложение P <- P + A. +В функции ecpAddAJ() выполняется сложение J <- J + A. Реализован алгоритм madd-2004-hmv из [HMV04]. Сложность алгоритма: 8M + 3S + 6add + 1*2 \approx 11M. -В функции ecpTplJ() выполняется утроение P <- 3P. Реализован алгоритм -tpl-2007-bl из [BerLan07]. Сложность алгоритма: - 5M + 10S + 1*A + 15add + 2*4 + 1*6 + 1*8 + 1*16 + 1*3 \approx 15M. - \todo Сравнить madd-2004-hmv с madd-2007-bl, сложность которого 7M + 4S + 9add + 1*4 + 3*2. -В функции ecpTplJA3() выполняется утроение P <- 3P для случая A = -3. -Реализован алгоритм tpl-2007-bl-2 из [BerLan07]. Сложность алгоритма: - 7M + 7S + 13add + 2*4 + 1*8 + 1*12 + 1*16 + 1*3 \approx 14M. +\todo При выполнении функции ecpAddAJ() может выясниться, что +складываются одинаковые точки. В этом случае вызывается функция +ecpDblAJ(). Подумать, как "помочь" этой функции, передав уже +рассчитанные значения, например, za^2. Аналогичное замечание касается +функции ecpAddJ(). + +В функции ecpDblAddAJ() выполняется удвоение и сложение J <- 2J + A. +Реализован алгоритм из [LonMir08; приложение А.3]. Время работы алгоритма: + 11M + 7S + 27add. \remark Целевые функции ci(l), определенные в описании реализации ecMulA() в ec.c, принимают следующий вид: @@ -129,7 +126,7 @@ tpl-2007-bl из [BerLan07]. Сложность алгоритма: ******************************************************************************* */ -// [3n]b <- [2n]a (P <- A) +// [3n]b <- [2n]a (J <- A) static bool_t ecpFromAJ(word b[], const word a[], const ec_o* ec, void* stack) { ASSERT(ecIsOperable(ec) && ec->d == 3); @@ -148,7 +145,7 @@ static bool_t ecpFromAJ(word b[], const word a[], const ec_o* ec, void* stack) /* t1 */ O_OF_W(n),\ /* t2 */ O_OF_W(n) -// [2n]b <- [3n]a (A <- P) +// [2n]b <- [3n]a (A <- J) static bool_t ecpToAJ(word b[], const word a[], const ec_o* ec, void* stack) { size_t n; @@ -188,7 +185,7 @@ static size_t ecpToAJ_deep(size_t n, size_t f_deep) SIZE_MAX); } -// [3n]b <- -[3n]a (P <- -P) +// [3n]b <- -[3n]a (J <- -J) static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) { ASSERT(ecIsOperable(ec) && ec->d == 3); @@ -206,7 +203,7 @@ static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) /* t1 */ O_OF_W(n),\ /* t2 */ O_OF_W(n) -// [3n]b <- 2[3n]a (P <- 2P) +// [3n]b <- 2[3n]a (J <- 2J) static void ecpDblJ(word b[], const word a[], const ec_o* ec, void* stack) { size_t n; @@ -281,7 +278,7 @@ static size_t ecpDblJ_deep(size_t n, size_t f_deep) /* t1 */ O_OF_W(n),\ /* t2 */ O_OF_W(n) -// [3n]b <- 2[3n]a (P <- 2P, A = -3) +// [3n]b <- 2[3n]a (J <- 2J, A = -3) static void ecpDblJA3(word b[], const word a[], const ec_o* ec, void* stack) { size_t n; @@ -356,7 +353,7 @@ static size_t ecpDblJA3_deep(size_t n, size_t f_deep) /* t3 */ O_OF_W(n),\ /* t4 */ O_OF_W(n) -// [3n]b <- 2[2n]a (P <- 2A) +// [3n]b <- 2[2n]a (J <- 2A) static void ecpDblAJ(word b[], const word a[], const ec_o* ec, void* stack) { size_t n; @@ -437,7 +434,7 @@ static size_t ecpDblAJ_deep(size_t n, size_t f_deep) /* t3 */ O_OF_W(n),\ /* t4 */ O_OF_W(n) -// [3n]c <- [3n]a + [3n]b (P <- P + P) +// [3n]c <- [3n]a + [3n]b (J <- J + J) static void ecpAddJ(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { @@ -556,7 +553,7 @@ static size_t ecpAddJ_deep(size_t n, size_t f_deep) /* t3 */ O_OF_W(n),\ /* t4 */ O_OF_W(n) -// [3n]c <- [3n]a + [2n]b (P <- P + A) +// [3n]c <- [3n]a + [2n]b (J <- J + A) static void ecpAddAJ(word c[], const word a[], const word b[], const ec_o* ec, void* stack) { @@ -643,6 +640,187 @@ static size_t ecpAddAJ_deep(size_t n, size_t f_deep) SIZE_MAX); } +/* +******************************************************************************* +Удвоение и сложение + +В функции ecpDblAddAJ() выполняется удвоение и сложение J <- 2J + A. +Реализован алгоритм из [LonMir08; приложение А.3]. Время работы алгоритма: + 11M + 7S + 27add. + +\warning В [LonMir08] вычисление 2a + b выполняется за два сложения: + c <- a + b, c <- c + a. +При первом сложении не обрабатывается исключение a + b == O, что может привести +к неверному результату. + +\warning В [LonMir08] имеется неточность: шаг 36 необходимо выполнять после +шага 38, так как на шаге 38 значение переменной t5 предполагается равным +theta^3. Это значение устанавливается на шаге 33 и перезаписывается на +шаге 36. + +[LonMir08] Longa P., Miri A. New Multibase Non-Adjacent Form Scalar + Multiplication and its Application to Elliptic Curve Cryptosystems + (extended version). Cryptology ePrint Archive, Report 2008/052. + https://eprint.iacr.org/2008/052. +****************************************************************************** +*/ + +#define ecpDblAddAJ_local(n)\ +/* pt */ O_OF_W(3 * n),\ +/* t4 */ O_OF_W(n),\ +/* t5 */ O_OF_W(n),\ +/* t6 */ O_OF_W(n) + +// [3n]c <- 2[3n]a + [2n]b (J <- 2J + A) +void ecpDblAddAJ(word c[], const word a[], const word b[], const ec_o* ec, + void* stack) +{ + size_t n; + word* pt; /* [3 * n] */ + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + word* t4; /* [n] */ + word* t5; /* [n] */ + word* t6; /* [n] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnA(b, ec)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(wwIsDisjoint2(b, 2 * ec->f->n, c, 3 * ec->f->n)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpDblAddAJ_local(n), SIZE_0, SIZE_MAX, + &pt, &t4, &t5, &t6, &stack); + t1 = pt, t2 = t1 + n, t3 = t2 + n; + // a == O => c <- b + if (qrIsZero(ecZ(a, n), ec->f)) + { + ecFromA(c, b, ec, stack); + return; + } + // pt = (t1 : t2 : t3) <- a + wwCopy(pt, a, 3 * n); + // 3: t4 <- t3^2 + qrSqr(t4, t3, ec->f, stack); + // 4: t5 <- xb * t4 + qrMul(t5, ecX(b), t4, ec->f, stack); + // 5: t5 <- t5 - t1 [beta] + qrSub(t5, t5, t1, ec->f); + // a + b == O? + if (qrIsZero(t5, ec->f)) + { + wwCopy(c, a, 3 * n); + return; + } + // 6: t6 <- t3 + t5 + qrAdd(t6, t3, t5, ec->f); + // 7: t6 <- t6^2 + qrSqr(t6, t6, ec->f, stack); + // 8: t6 <- t6 - t4 + qrSub(t6, t6, t4, ec->f); + // 9: t4 <- t3 * t4 + qrMul(t4, t3, t4, ec->f, stack); + // 10: t4 <- yb * t4 + qrMul(t4, ecY(b, n), t4, ec->f, stack); + // 11: t4 <- t4 - t2 + qrSub(t4, t4, t2, ec->f); + // 12: t3 <- t5^2 + qrSqr(t3, t5, ec->f, stack); + // 13: t6 <- t6 - t3 + qrSub(t6, t6, t3, ec->f); + // 14: t1 <- t1 * t3 + qrMul(t1, t1, t3, ec->f, stack); + // 15: t1 <- 4 t1 + gfpDouble(t1, t1, ec->f); + gfpDouble(t1, t1, ec->f); + // 16: t3 <- t3 * t5 + qrMul(t3, t3, t5, ec->f, stack); + // 17: t2 <- t2 * t3 + qrMul(t2, t2, t3, ec->f, stack); + // 18: t2 <- 8 t2 + gfpDouble(t2, t2, ec->f); + gfpDouble(t2, t2, ec->f); + gfpDouble(t2, t2, ec->f); + // 19: t5 <- t4^2 + qrSqr(t5, t4, ec->f, stack); + // 20: t3 <- t5 - t3 + qrSub(t3, t5, t3, ec->f); + // 21: t3 <- 4 t3 + gfpDouble(t3, t3, ec->f); + gfpDouble(t3, t3, ec->f); + // 22: t3 <- t3 - t1 + qrSub(t3, t3, t1, ec->f); + // 23: t3 <- t3 - t1 + qrSub(t3, t3, t1, ec->f); + // 24: t3 <- t3 - t1 + qrSub(t3, t3, t1, ec->f); + // 25: t4 <- t3 + t4 + qrAdd(t4, t3, t4, ec->f); + // 26: t4 <- t4^2 + qrSqr(t4, t4, ec->f, stack); + // 27: t4 <- t5 - t4 + qrSub(t4, t5, t4, ec->f); + // 28: t4 <- t4 - t2 + qrSub(t4, t4, t2, ec->f); + // 29: t4 <- t4 - t2 + qrSub(t4, t4, t2, ec->f); + // 30: t5 <- t3^2 + qrSqr(t5, t3, ec->f, stack); + // 31: t4 <- t4 + t5 + qrAdd(t4, t4, t5, ec->f); + // 32: t1 <- t1 * t5 + qrMul(t1, t1, t5, ec->f, stack); + // 33: t5 <- t3 * t5 + qrMul(t5, t3, t5, ec->f, stack); + // 34: t3 <- t3 * t6 + qrMul(t3, t3, t6, ec->f, stack); + // 35: t2 <- t2 * t5 + qrMul(t2, t2, t5, ec->f, stack); + // шаг 36 будет после шага 38 + // 37: t6 <- t4^2 + qrSqr(t6, t4, ec->f, stack); + // 38: t6 <- t6 - t5 + qrSub(t6, t6, t5, ec->f); + // 36: t5 <- 3 t1 + gfpDouble(t5, t1, ec->f); + qrAdd(t5, t5, t1, ec->f); + // 39: t5 <- t5 - t6 + qrSub(t5, t5, t6, ec->f); + // 40: t4 <- t4 * t5 + qrMul(t4, t4, t5, ec->f, stack); + // 41: t2 <- t4 - t2 + qrSub(t2, t4, t2, ec->f); + // 42: t1 <- t1 - t5 + qrSub(t1, t1, t5, ec->f); + // c <- pt = (t1 : t2 : t3) + wwCopy(c, pt, 3 * n); +} + +size_t ecpDblAddAJ_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpDblAddAJ_local(n), + f_deep, + SIZE_MAX); +} + +/* +******************************************************************************* +Утроение + +В функции ecpTplJ() выполняется утроение J <- 3J. Реализован алгоритм +tpl-2007-bl из [BerLan07]. Сложность алгоритма: + 5M + 10S + 1*A + 15add + 2*4 + 1*6 + 1*8 + 1*16 + 1*3 \approx 15M. + +В функции ecpTplJA3() выполняется утроение J <- 3J для случая A = -3. +Реализован алгоритм tpl-2007-bl-2 из [BerLan07]. Сложность алгоритма: + 7M + 7S + 13add + 2*4 + 1*8 + 1*12 + 1*16 + 1*3 \approx 14M. +****************************************************************************** +*/ + #define ecpTplJ_local(n)\ /* t0 */ O_OF_W(n),\ /* t1 */ O_OF_W(n),\ @@ -653,7 +831,7 @@ static size_t ecpAddAJ_deep(size_t n, size_t f_deep) /* t6 */ O_OF_W(n),\ /* t7 */ O_OF_W(n) -// [3n]b <- 3[3n]a (P <- 3P) +// [3n]b <- 3[3n]a (J <- 3J) static void ecpTplJ(word b[], const word a[], const ec_o* ec, void* stack) { size_t n; @@ -753,7 +931,7 @@ static size_t ecpTplJ_deep(size_t n, size_t f_deep) /* t6 */ O_OF_W(n),\ /* t7 */ O_OF_W(n) -// [3n]b <- 3[3n]a (P <- 3P) +// [3n]b <- 3[3n]a (J <- 3J) static void ecpTplJA3(word b[], const word a[], const ec_o* ec, void* stack) { size_t n; @@ -841,13 +1019,7 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) /* ******************************************************************************* -Малые кратные -******************************************************************************* -*/ - -/* -******************************************************************************* -Сложение с настройкой знака суммы +Финишное сложение Использованы полные (complete) формулы сложения / удвоения из работы [RCB15]. @@ -862,7 +1034,7 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) ******************************************************************************* */ -#define ecpNegAAdd_local(n)\ +#define ecpFinAdd_local(n)\ /* pt1 */ O_OF_W(3 * n),\ /* pt2 */ O_OF_W(3 * n),\ /* pt3 */ O_OF_W(3 * n),\ @@ -875,7 +1047,7 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) /* t5 */ O_OF_W(n) // [2n]с <- (-1)^neg ([3n]a + [3n]b) (A <- J + J) -static bool_t ecpNegAAdd(word c[], const word a[], const word b[], +static bool_t ecpFinAdd(word c[], const word a[], const word b[], register word neg, const ec_o* ec, void* stack) { size_t n; @@ -897,7 +1069,7 @@ static bool_t ecpNegAAdd(word c[], const word a[], const word b[], // разметить стек n = ec->f->n; memSlice(stack, - ecpNegAAdd_local(n), SIZE_0, SIZE_MAX, + ecpFinAdd_local(n), SIZE_0, SIZE_MAX, &pt1, &pt2, &pt3, &B3, &t0, &t1, &t2, &t3, &t4, &t5, &stack); // pt1 <- a (H <- J) qrMul(ecX(pt1), ecX(a), ecZ(a, n), ec->f, stack); @@ -963,10 +1135,105 @@ static bool_t ecpNegAAdd(word c[], const word a[], const word b[], return ret; } -static size_t ecpNegAAdd_deep(size_t n, size_t f_deep) +static size_t ecpFinAdd_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpFinAdd_local(n), + f_deep, + SIZE_MAX); +} + +#define ecpFinAddA_local(n)\ +/* pt1 */ O_OF_W(3 * n),\ +/* pt3 */ O_OF_W(3 * n),\ +/* B3 */ O_OF_W(n),\ +/* t0 */ O_OF_W(n),\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n),\ +/* t3 */ O_OF_W(n),\ +/* t4 */ O_OF_W(n),\ +/* t5 */ O_OF_W(n) + +// [2n]с <- (-1)^neg ([3n]a + [2n]b) (A <- J + A) +static bool_t ecpFinAddA(word c[], const word a[], const word b[], + register word neg, const ec_o* ec, void* stack) +{ + size_t n; + register bool_t ret; + word* pt1; /* [3 * n] */ + word* pt3; /* [3 * n] */ + word* B3; /* [n] */ + word* t0; /* [n] */ + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + word* t4; /* [n] */ + word* t5; /* [n] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnA(b, ec)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpFinAddA_local(n), SIZE_0, SIZE_MAX, + &pt1, &pt3, &B3, &t0, &t1, &t2, &t3, &t4, &t5, &stack); + // pt1 <- a (H <- J) + qrMul(ecX(pt1), ecX(a), ecZ(a, n), ec->f, stack); + wwCopy(ecY(pt1, n), ecY(a, n), n); + qrSqr(ecZ(pt1, n), ecZ(a, n), ec->f, stack); + qrMul(ecZ(pt1, n), ecZ(pt1, n), ecZ(a, n), ec->f, stack); + // B3 <- 3B + qrAdd(B3, ec->B, ec->B, ec->f); + qrAdd(B3, B3, ec->B, ec->f); + // [RCB15; algorithm 2] + qrMul(t0, ecX(pt1), ecX(b), ec->f, stack); + qrMul(t1, ecY(pt1, n), ecY(b, n), ec->f, stack); + qrAdd(t3, ecX(b), ecY(b, n), ec->f); + qrAdd(t4, ecX(pt1), ecY(pt1, n), ec->f); + qrMul(t3, t3, t4, ec->f, stack); + qrAdd(t4, t0, t1, ec->f); + qrSub(t3, t3, t4, ec->f); + qrMul(t4, ecX(b), ecZ(pt1, n), ec->f, stack); + qrAdd(t4, t4, ecX(pt1), ec->f); + qrMul(t5, ecY(b, n), ecZ(pt1, n), ec->f, stack); + qrAdd(t5, t5, ecY(pt1, n), ec->f); + qrMul(ecZ(pt3, n), ec->A, t4, ec->f, stack); + qrMul(ecX(pt3), B3, ecZ(pt1, n), ec->f, stack); + qrAdd(ecZ(pt3, n), ecX(pt3), ecZ(pt3, n), ec->f); + qrSub(ecX(pt3), t1, ecZ(pt3, n), ec->f); + qrAdd(ecZ(pt3, n), t1, ecZ(pt3, n), ec->f); + qrMul(ecY(pt3, n), ecX(pt3), ecZ(pt3, n), ec->f, stack); + qrAdd(t1, t0, t0, ec->f); + qrAdd(t1, t1, t0, ec->f); + qrMul(t2, ec->A, ecZ(pt1, n), ec->f, stack); + qrMul(t4, B3, t4, ec->f, stack); + qrAdd(t1, t1, t2, ec->f); + qrSub(t2, t0, t2, ec->f); + qrMul(t2, ec->A, t2, ec->f, stack); + qrAdd(t4, t4, t2, ec->f); + qrMul(t0, t1, t4, ec->f, stack); + qrAdd(ecY(pt3, n), ecY(pt3, n), t0, ec->f); + qrMul(t0, t5, t4, ec->f, stack); + qrMul(ecX(pt3), t3, ecX(pt3), ec->f, stack); + qrSub(ecX(pt3), ecX(pt3), t0, ec->f); + qrMul(t0, t3, t1, ec->f, stack); + qrMul(ecZ(pt3, n), t5, ecZ(pt3, n), ec->f, stack); + qrAdd(ecZ(pt3, n), ecZ(pt3, n), t0, ec->f); + // c <- pt3 (A <- H) + ret = !wwIsZero(ecZ(pt3, n), n); + qrInv(t0, ecZ(pt3, n), ec->f, stack); + qrMul(ecX(c), ecX(pt3), t0, ec->f, stack); + qrMul(ecY(c, n), ecY(pt3, n), t0, ec->f, stack); + // c <- (-1)^neg c + zzNegModIf(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); + return ret; +} + +static size_t ecpFinAddA_deep(size_t n, size_t f_deep) { return memSliceSize( - ecpNegAAdd_local(n), + ecpFinAddA_local(n), f_deep, SIZE_MAX); } @@ -1030,16 +1297,20 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->adda = ecpAddAJ; ec->dbl = bA3 ? ecpDblJA3 : ecpDblJ; ec->dbla = ecpDblAJ; + ec->dbladda = ecpDblAddAJ; ec->tpl = bA3 ? ecpTplJA3 : ecpTplJ; - ec->negaadd = ecpNegAAdd; - ec->deep = utilMax(7, + ec->finadd = ecpFinAdd; + ec->finadda = ecpFinAddA; + ec->deep = utilMax(9, ecpToAJ_deep(f->n, f->deep), ecpAddJ_deep(f->n, f->deep), ecpAddAJ_deep(f->n, f->deep), bA3 ? ecpDblJA3_deep(f->n, f->deep) : ecpDblJ_deep(f->n, f->deep), ecpDblAJ_deep(f->n, f->deep), + ecpDblAddAJ_deep(f->n, f->deep), bA3 ? ecpTplJA3_deep(f->n, f->deep) : ecpTplJ_deep(f->n, f->deep), - ecpNegAAdd_deep(f->n, f->deep)); + ecpFinAdd_deep(f->n, f->deep), + ecpFinAddA_deep(f->n, f->deep)); // настроить ec->hdr.keep = sizeof(ec_o) + memSliceSize(ecpCreateJ_state(f->n), SIZE_MAX); @@ -1060,7 +1331,7 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) { return memSliceSize( ecpCreateJ_local(n), - utilMax(10, + utilMax(12, O_OF_W(n), ecpToAJ_deep(n, f_deep), ecpAddJ_deep(n, f_deep), @@ -1068,9 +1339,11 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) ecpDblJ_deep(n, f_deep), ecpDblJA3_deep(n, f_deep), ecpDblAJ_deep(n, f_deep), + ecpDblAddAJ_deep(n, f_deep), ecpTplJ_deep(n, f_deep), ecpTplJA3_deep(n, f_deep), - ecpNegAAdd_deep(n, f_deep)), + ecpFinAdd_deep(n, f_deep), + ecpFinAddA_deep(n, f_deep)), SIZE_MAX); } diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index a6afd84b..d43c4368 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.18 +\version 2026.01.21 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -59,16 +59,16 @@ bool_t ecpTest() void* state; ec_o* ec; /* [ec_keep] */ qr_o* f; /* [f_keep] */ - octet* t; /* [5 * no] */ - word* pts; /* [4 * 3 * n] */ + octet* t; /* [3 * no] */ + word* pts; /* [6 * 3 * n] */ word* d; /* [n] */ void* stack; // создать состояние state = blobCreate2( ec_keep, f_keep, - 5 * no, - O_OF_W(4 * 3 * n) | SIZE_HI, + 3 * no, + O_OF_W(6 * 3 * n) | SIZE_HI, O_OF_W(n), utilMax(13, gfpCreate_deep(no), @@ -165,6 +165,130 @@ bool_t ecpTest() ecMulA2(pts + 2 * n, ec->base, ec, d, n, stack)) return FALSE; } + // финишное сложение + if (ec->finadd) + { + // (pts[1], pts[2], pts[3], pts[4]) <- (base, 2 base, 3 base, 4 base) + ecFromA(pts + 1 * 3 * n, ec->base, ec, stack); + ecAddA(pts + 2 * 3 * n, pts + 1 * 3 * n, ec->base, ec, stack); + ecAddA(pts + 3 * 3 * n, pts + 2 * 3 * n, ec->base, ec, stack); + ecAddA(pts + 4 * 3 * n, pts + 3 * 3 * n, ec->base, ec, stack); + // pts[0] <- pts[1] + pts[2] = 3 base, pts[0] ==? pts[3] + ec->finadd(pts, pts + 1 * 3 * n, pts + 2 * 3 * n, 0, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 3 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] <- -(pts[1] + pts[3]) == -4 base, pts[0] ==? -pts[4] + ec->finadd(pts, pts + 1 * 3 * n, pts + 3 * 3 * n, 1, ec, stack); + ecNeg(pts + 5 * 3 * n, pts + 4 * 3 * n, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 5 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] <- -(pts[2] + pts[2]) == -4 base, pts[0] ==? -pts[4] + ec->finadd(pts, pts + 2 * 3 * n, pts + 2 * 3 * n, 1, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] <- pts[1] + pts[1] == 2 base, pts[0] ==? pts[2] + ec->finadd(pts, pts + 1 * 3 * n, pts + 1 * 3 * n, 0, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // финишное сложение с аффинной точкой + if (ec->finadda) + { + // (pts[1], pts[2], pts[3], pts[4]) <- (base, 2 base, 3 base, 4 base) + ecFromA(pts + 1 * 3 * n, ec->base, ec, stack); + ecAddA(pts + 2 * 3 * n, pts + 1 * 3 * n, ec->base, ec, stack); + ecAddA(pts + 3 * 3 * n, pts + 2 * 3 * n, ec->base, ec, stack); + ecAddA(pts + 4 * 3 * n, pts + 3 * 3 * n, ec->base, ec, stack); + // pts[0] <- pts[1] + pts[2] = 3 base, pts[0] ==? pts[3] + ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); + ec->finadda(pts, pts + 1 * 3 * n, pts + 5 * 3 * n, 0, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 3 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] <- -(pts[1] + pts[3]) == -4 base, pts[0] ==? -pts[4] + ecToA(pts + 5 * 3 * n, pts + 3 * 3 * n, ec, stack); + ec->finadda(pts, pts + 1 * 3 * n, pts + 5 * 3 * n, 1, ec, stack); + ecNeg(pts + 5 * 3 * n, pts + 4 * 3 * n, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 5 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] <- -(pts[2] + pts[2]) == -4 base, pts[0] ==? -pts[4] + ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); + ec->finadda(pts, pts + 2 * 3 * n, pts + 5 * 3 * n, 1, ec, stack); + ecNeg(pts + 5 * 3 * n, pts + 4 * 3 * n, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 5 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] <- pts[1] + pts[1] == 2 base, pts[0] ==? pts[2] + ec->finadda(pts, pts + 1 * 3 * n, ec->base, 0, ec, stack); + ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); + if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // малые кратные + if (ec->smul) + { + // (pts[0], ..., pts[3]) <- (base, 3 base, 5 base, 7 base) + ec->smul(pts, ec->base, 3, ec, stack); + // pts[0] ==? base? + ecToA(pts + 4 * 3 * n, pts + 0 * 3 * n, ec, stack); + if (!wwEq(pts + 4 * 3 * n, ec->base, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] ==? 3 base? + d[0] = 3, ecMulA(pts + 5 * 3 * n, ec->base, ec, d, 1, stack); + ecToA(pts + 4 * 3 * n, pts + 1 * 3 * n, ec, stack); + if (!wwEq(pts + 4 * 3 * n, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] ==? 5 base? + d[0] = 5, ecMulA(pts + 5 * 3 * n, ec->base, ec, d, 1, stack); + ecToA(pts + 4 * 3 * n, pts + 2 * 3 * n, ec, stack); + if (!wwEq(pts + 4 * 3 * n, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pts[0] ==? 7 base? + d[0] = 7, ecMulA(pts + 5 * 3 * n, ec->base, ec, d, 1, stack); + ecToA(pts + 4 * 3 * n, pts + 3 * 3 * n, ec, stack); + if (!wwEq(pts + 4 * 3 * n, pts + 5 * 3 * n, 2 * n)) + { + blobClose(state); + return FALSE; + } + } // вывести f = GF(p) за пределы ec objCopy(f, objPtr(ec, 0, qr_o)); // создать ec = EC_{a-1, b}(f) @@ -207,46 +331,6 @@ bool_t ecpTest() return FALSE; } } - // малые кратные разными способами - if (ec->smul) - { - // (pts[0], ..., pts[3]) <- (base, 3 base, 5 base, 7 base) - ec->smul(pts, ec->base, 3, ec, stack); - // pts[2] <- 7 base - d[0] = 7; - ecMulA(pts + 2 * 3 * n, ec->base, ec, d, 1, stack); - // pts[2] ==? pts[3] - ec->toa(pts + 3 * 3 * n, pts + 3 * 3 * n, ec, stack); - if (!wwEq(pts + 2 * 3 * n, pts + 3 * 3 * n, 2 * n)) - { - blobClose(state); - return FALSE; - } - if (ec->negaadd) - { - // pts[2] <- 4 base - d[0] = 4; - ecMulA(pts + 2 * 3 * n, ec->base, ec, d, 1, stack); - // pts[3] <- pts[0] + prs[1] = 4 base - ec->negaadd(pts + 3 * 3 * n, pts, pts + 1 * 3 * n, 0, ec, stack); - // pts[2] ==? pts[3] - if (!wwEq(pts + 2 * 3 * n, pts + 3 * 3 * n, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pts[2] <- -4 base - ec->neg(pts + 2 * 3 * n, pts + 2 * 3 * n, ec, stack); - // pts[3] <- -(pts[0] + prs[1]) = -4 base - ec->negaadd(pts + 3 * 3 * n, pts, pts + 1 * 3 * n, 1, ec, stack); - // pts[2] ==? pts[3] - if (!wwEq(pts + 2 * 3 * n, pts + 3 * 3 * n, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - } // все хорошо blobClose(state); return TRUE; diff --git a/test/math/pp_test.c b/test/math/pp_test.c index bbdf664d..25e7fd76 100644 --- a/test/math/pp_test.c +++ b/test/math/pp_test.c @@ -4,7 +4,7 @@ \brief Tests for the arithmetic of binary polynomials \project bee2/test \created 2023.11.09 -\version 2026.01.20 +\version 2026.01.21 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -147,6 +147,7 @@ static bool_t ppTestMod() prngCOMBOStepR(b, O_OF_W(n), state); if (mod[n - 1] == 0) mod[n - 1] = WORD_MAX; + mod[0] |= 1; ppMod(a, a, n, mod, n, stack); ppMod(b, b, n, mod, n, stack); // zzMulMod / zzDivMod / zzInvMod From 248bd274f2c1b3efeed6bd7c3767366c43444364 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 22 Jan 2026 13:00:21 +0300 Subject: [PATCH 16/77] Clarify ecpDblAddA() --- src/math/ec.c | 50 +++--- src/math/ecp.c | 343 +++++++++++++++++++++--------------------- src/math/zz/zz_gcd.c | 4 +- test/math/ecp_bench.c | 4 +- test/math/ecp_test.c | 248 ++++++++++++++++++------------ 5 files changed, 355 insertions(+), 294 deletions(-) diff --git a/src/math/ec.c b/src/math/ec.c index 6d84f4fc..ca71b1a4 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -18,6 +18,14 @@ #include "bee2/math/ww.h" #include "bee2/math/zz.h" +/* +******************************************************************************* +Вспомогательные определения +******************************************************************************* +*/ + +#define ecPt(pts, pos, ec) ((pts) + (pos) * (ec)->d * (ec)->f->n) + /* ******************************************************************************* Управление описанием кривой @@ -320,13 +328,12 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, ecSmul(pre, a, naf_width - 1, ec, stack); // отрицательные малые кратные: -a, -3a, ..., -(2^w - 1)a for (i = 0; i < pre_count / 2; ++i) - ecNeg(pre + (pre_count / 2 + i) * ec->d * ec->f->n, - pre + i * ec->d * ec->f->n, ec, stack); + ecNeg(ecPt(pre, pre_count / 2 + i, ec), ecPt(pre, i, ec), ec, stack); // старшая цифра NAF digit = wwGetBits(naf, 0, naf_width); ASSERT((digit & 1) == 1 && (digit & naf_hi) == 0); // t <- pre[digit / 2] - wwCopy(t, pre + (digit >> 1) * ec->d * ec->f->n, ec->d * ec->f->n); + wwCopy(t, ecPt(pre, digit >> 1, ec), ec->d * ec->f->n); // цикл по цифрам NAF i = naf_width; while (--naf_size) @@ -334,14 +341,14 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, digit = wwGetBits(naf, i, naf_width); // t <- 2t + pre[digit / 2] if (digit == 1 || digit == (naf_hi ^ 1)) - ecDblAddA(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); + ecDblAddA(t, t, ecPt(pre, digit >> 1, ec), ec, stack); else { // t <- 2t ecDbl(t, t, ec, stack); if (digit & 1) // t <- t + pre[digit / 2] - ecAdd(t, t, pre + (digit >> 1) * ec->d * ec->f->n, ec, stack); + ecAdd(t, t, ecPt(pre, digit >> 1, ec), ec, stack); } // к следующей цифре i += (digit & 1) ? naf_width : 1; @@ -380,23 +387,21 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pre[i] <- (2i - 1)a; // ec_smult_i + 1) pre[i] <- (2i - 1)a; // ec_smul_i 2) pre[-i] <- -pre[i]. // ec_neg_i 2. t <- pre[d_{k-1} / 2] 3. Для i = k - 2, ..., 1: - 1) t <- t + pre[d_i / 2]; // ec_add_i - 2) t <- 2^w t. // ec_dbl_i -4. t <- t + pre[d_0]. // ec_aadd_i -5. Возвратить t. - -Реализация регулярна при условии, что на шаге 4 используется регулярная -функция интерфейса ec_aadd_i. + 1) t <- 2^w t; // ec_dbl_i + 1) t <- t + pre[d_i / 2]. // ec_add_i +4. t <- 2^w t. +5. t <- t + pre[d_0]. // ec_finadd_i +6. Возвратить t. \remark сложение t + pre[d_0] вынесено за рамки основного цикла на шаге 3, потому что при этом (и только при этом) сложении может произойти исключительная ситуация -- совпадение операндов t и pre[d_0] с переключением от сложения -к удвоению. Регулярность функция ec_aadd_i означает, что сложение и удвоение -будут выполняться по одним формулам. +к удвоению. Предполагается, что функция ec_finadd_i регулярна, и тогда сложение +и удвоение будут выполняться по одним и тем же формулам. [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory and fast elliptic scalar multiplications secure against side channel @@ -464,15 +469,14 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, ecSmul(pre, a, snz_width, ec, stack); // отрицательные малые кратные: -(2^w - 1)a, ..., -3a, -a for (i = 0; i < pre_count / 2; ++i) - ecNeg(pre + (pre_count - 1 - i) * ec->d * ec->f->n, - pre + i * ec->d * ec->f->n, ec, stack); + ecNeg(ecPt(pre, pre_count - 1 - i, ec), ecPt(pre, i, ec), ec, stack); // число цифр SNZ k = (mb + snz_width - 1) / snz_width; ASSERT(k > 1); // старшая цифра --k; digit = wwGetBits(dd, k * snz_width, mb - k * snz_width); - wwCopy(t, pre + (digit >> 1) * ec->d * ec->f->n, ec->d * ec->f->n); + wwCopy(t, ecPt(pre, digit >> 1, ec), ec->d * ec->f->n); hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; // обработать остальные цифры while (--k) @@ -480,19 +484,17 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, for (i = snz_width; i--;) ecDbl(t, t, ec, stack); digit = wwGetBits(dd, k * snz_width, snz_width); - ecAdd(t, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, ec, stack); + ecAdd(t, t, ecPt(pre, hi | (digit >> 1), ec), ec, stack); hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; } - // завершающие удвоения и сложение с настройкой знака + // завершающие удвоения и финишное сложение for (i = snz_width; i--;) ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, snz_width); ASSERT(digit & 1); ret = (ec->finadd) ? - ec->finadd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, - neg, ec, stack) : - ecFinAdd(b, t, pre + (hi | (digit >> 1)) * ec->d * ec->f->n, - neg, ec, stack); + ec->finadd(b, t, ecPt(pre, hi | (digit >> 1), ec), neg, ec, stack) : + ecFinAdd(b, t, ecPt(pre, hi | (digit >> 1), ec), neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); return ret; diff --git a/src/math/ecp.c b/src/math/ecp.c index 015e76bf..abc60592 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.21 +\version 2026.01.22 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -640,173 +640,6 @@ static size_t ecpAddAJ_deep(size_t n, size_t f_deep) SIZE_MAX); } -/* -******************************************************************************* -Удвоение и сложение - -В функции ecpDblAddAJ() выполняется удвоение и сложение J <- 2J + A. -Реализован алгоритм из [LonMir08; приложение А.3]. Время работы алгоритма: - 11M + 7S + 27add. - -\warning В [LonMir08] вычисление 2a + b выполняется за два сложения: - c <- a + b, c <- c + a. -При первом сложении не обрабатывается исключение a + b == O, что может привести -к неверному результату. - -\warning В [LonMir08] имеется неточность: шаг 36 необходимо выполнять после -шага 38, так как на шаге 38 значение переменной t5 предполагается равным -theta^3. Это значение устанавливается на шаге 33 и перезаписывается на -шаге 36. - -[LonMir08] Longa P., Miri A. New Multibase Non-Adjacent Form Scalar - Multiplication and its Application to Elliptic Curve Cryptosystems - (extended version). Cryptology ePrint Archive, Report 2008/052. - https://eprint.iacr.org/2008/052. -****************************************************************************** -*/ - -#define ecpDblAddAJ_local(n)\ -/* pt */ O_OF_W(3 * n),\ -/* t4 */ O_OF_W(n),\ -/* t5 */ O_OF_W(n),\ -/* t6 */ O_OF_W(n) - -// [3n]c <- 2[3n]a + [2n]b (J <- 2J + A) -void ecpDblAddAJ(word c[], const word a[], const word b[], const ec_o* ec, - void* stack) -{ - size_t n; - word* pt; /* [3 * n] */ - word* t1; /* [n] */ - word* t2; /* [n] */ - word* t3; /* [n] */ - word* t4; /* [n] */ - word* t5; /* [n] */ - word* t6; /* [n] */ - // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOnJ(a, ec)); - ASSERT(ecpSeemsOnA(b, ec)); - ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); - ASSERT(wwIsDisjoint2(b, 2 * ec->f->n, c, 3 * ec->f->n)); - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpDblAddAJ_local(n), SIZE_0, SIZE_MAX, - &pt, &t4, &t5, &t6, &stack); - t1 = pt, t2 = t1 + n, t3 = t2 + n; - // a == O => c <- b - if (qrIsZero(ecZ(a, n), ec->f)) - { - ecFromA(c, b, ec, stack); - return; - } - // pt = (t1 : t2 : t3) <- a - wwCopy(pt, a, 3 * n); - // 3: t4 <- t3^2 - qrSqr(t4, t3, ec->f, stack); - // 4: t5 <- xb * t4 - qrMul(t5, ecX(b), t4, ec->f, stack); - // 5: t5 <- t5 - t1 [beta] - qrSub(t5, t5, t1, ec->f); - // a + b == O? - if (qrIsZero(t5, ec->f)) - { - wwCopy(c, a, 3 * n); - return; - } - // 6: t6 <- t3 + t5 - qrAdd(t6, t3, t5, ec->f); - // 7: t6 <- t6^2 - qrSqr(t6, t6, ec->f, stack); - // 8: t6 <- t6 - t4 - qrSub(t6, t6, t4, ec->f); - // 9: t4 <- t3 * t4 - qrMul(t4, t3, t4, ec->f, stack); - // 10: t4 <- yb * t4 - qrMul(t4, ecY(b, n), t4, ec->f, stack); - // 11: t4 <- t4 - t2 - qrSub(t4, t4, t2, ec->f); - // 12: t3 <- t5^2 - qrSqr(t3, t5, ec->f, stack); - // 13: t6 <- t6 - t3 - qrSub(t6, t6, t3, ec->f); - // 14: t1 <- t1 * t3 - qrMul(t1, t1, t3, ec->f, stack); - // 15: t1 <- 4 t1 - gfpDouble(t1, t1, ec->f); - gfpDouble(t1, t1, ec->f); - // 16: t3 <- t3 * t5 - qrMul(t3, t3, t5, ec->f, stack); - // 17: t2 <- t2 * t3 - qrMul(t2, t2, t3, ec->f, stack); - // 18: t2 <- 8 t2 - gfpDouble(t2, t2, ec->f); - gfpDouble(t2, t2, ec->f); - gfpDouble(t2, t2, ec->f); - // 19: t5 <- t4^2 - qrSqr(t5, t4, ec->f, stack); - // 20: t3 <- t5 - t3 - qrSub(t3, t5, t3, ec->f); - // 21: t3 <- 4 t3 - gfpDouble(t3, t3, ec->f); - gfpDouble(t3, t3, ec->f); - // 22: t3 <- t3 - t1 - qrSub(t3, t3, t1, ec->f); - // 23: t3 <- t3 - t1 - qrSub(t3, t3, t1, ec->f); - // 24: t3 <- t3 - t1 - qrSub(t3, t3, t1, ec->f); - // 25: t4 <- t3 + t4 - qrAdd(t4, t3, t4, ec->f); - // 26: t4 <- t4^2 - qrSqr(t4, t4, ec->f, stack); - // 27: t4 <- t5 - t4 - qrSub(t4, t5, t4, ec->f); - // 28: t4 <- t4 - t2 - qrSub(t4, t4, t2, ec->f); - // 29: t4 <- t4 - t2 - qrSub(t4, t4, t2, ec->f); - // 30: t5 <- t3^2 - qrSqr(t5, t3, ec->f, stack); - // 31: t4 <- t4 + t5 - qrAdd(t4, t4, t5, ec->f); - // 32: t1 <- t1 * t5 - qrMul(t1, t1, t5, ec->f, stack); - // 33: t5 <- t3 * t5 - qrMul(t5, t3, t5, ec->f, stack); - // 34: t3 <- t3 * t6 - qrMul(t3, t3, t6, ec->f, stack); - // 35: t2 <- t2 * t5 - qrMul(t2, t2, t5, ec->f, stack); - // шаг 36 будет после шага 38 - // 37: t6 <- t4^2 - qrSqr(t6, t4, ec->f, stack); - // 38: t6 <- t6 - t5 - qrSub(t6, t6, t5, ec->f); - // 36: t5 <- 3 t1 - gfpDouble(t5, t1, ec->f); - qrAdd(t5, t5, t1, ec->f); - // 39: t5 <- t5 - t6 - qrSub(t5, t5, t6, ec->f); - // 40: t4 <- t4 * t5 - qrMul(t4, t4, t5, ec->f, stack); - // 41: t2 <- t4 - t2 - qrSub(t2, t4, t2, ec->f); - // 42: t1 <- t1 - t5 - qrSub(t1, t1, t5, ec->f); - // c <- pt = (t1 : t2 : t3) - wwCopy(c, pt, 3 * n); -} - -size_t ecpDblAddAJ_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpDblAddAJ_local(n), - f_deep, - SIZE_MAX); -} - /* ******************************************************************************* Утроение @@ -1017,6 +850,180 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) SIZE_MAX); } +/* +******************************************************************************* +Удвоение и сложение + +В функции ecpDblAddAJ() выполняется удвоение и сложение J <- 2J + A. +Реализован алгоритм из [LonMir08; приложение А.3]. Время работы алгоритма: + 11M + 7S + 27add. + +\warning В [LonMir08] вычисление 2a + b структурно выполняется за два сложения: + c <- a + b, c <- c + a. +При первом сложении не обрабатываются исключения a == \pm b, что может привести +к неверному результату. + +\warning В [LonMir08] имеется неточность: шаг 36 необходимо выполнять после +шага 38, так как на шаге 38 значение переменной t5 предполагается равным +theta^3. Это значение устанавливается на шаге 33 и перезаписывается на +шаге 36. + +[LonMir08] Longa P., Miri A. New Multibase Non-Adjacent Form Scalar + Multiplication and its Application to Elliptic Curve Cryptosystems + (extended version). Cryptology ePrint Archive, Report 2008/052. + https://eprint.iacr.org/2008/052. +****************************************************************************** +*/ + +#define ecpDblAddAJ_local(n)\ +/* pt */ O_OF_W(3 * n),\ +/* t4 */ O_OF_W(n),\ +/* t5 */ O_OF_W(n),\ +/* t6 */ O_OF_W(n) + +// [3n]c <- 2[3n]a + [2n]b (J <- 2J + A) +void ecpDblAddAJ(word c[], const word a[], const word b[], const ec_o* ec, + void* stack) +{ + size_t n; + word* pt; /* [3 * n] */ + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + word* t4; /* [n] */ + word* t5; /* [n] */ + word* t6; /* [n] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnA(b, ec)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(wwIsDisjoint2(b, 2 * ec->f->n, c, 3 * ec->f->n)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpDblAddAJ_local(n), SIZE_0, SIZE_MAX, + &pt, &t4, &t5, &t6, &stack); + t1 = pt, t2 = t1 + n, t3 = t2 + n; + // a == O => c <- b + if (qrIsZero(ecZ(a, n), ec->f)) + { + ecFromA(c, b, ec, stack); + return; + } + // pt = (t1 : t2 : t3) <- a + wwCopy(pt, a, 3 * n); + // 3: t4 <- t3^2 + qrSqr(t4, t3, ec->f, stack); + // 4: t5 <- xb * t4 + qrMul(t5, ecX(b), t4, ec->f, stack); + // 5: t5 <- t5 - t1 [beta] + qrSub(t5, t5, t1, ec->f); + // 6: t6 <- t3 + t5 + qrAdd(t6, t3, t5, ec->f); + // 7: t6 <- t6^2 + qrSqr(t6, t6, ec->f, stack); + // 8: t6 <- t6 - t4 + qrSub(t6, t6, t4, ec->f); + // 9: t4 <- t3 * t4 + qrMul(t4, t3, t4, ec->f, stack); + // 10: t4 <- yb * t4 + qrMul(t4, ecY(b, n), t4, ec->f, stack); + // 11: t4 <- t4 - t2 [alpha] + qrSub(t4, t4, t2, ec->f); + // beta == 0 => a == \pm b + // alpha == 0 => a == b => 2a + b == 3a + // alpha != 0 => a == -b => 2a + b == a + if (qrIsZero(t5, ec->f)) + { + if (qrIsZero(t4, ec->f)) + ecpTplJ(c, a, ec, stack); + else + wwCopy(c, a, 3 * n); + return; + } + // 12: t3 <- t5^2 + qrSqr(t3, t5, ec->f, stack); + // 13: t6 <- t6 - t3 + qrSub(t6, t6, t3, ec->f); + // 14: t1 <- t1 * t3 + qrMul(t1, t1, t3, ec->f, stack); + // 15: t1 <- 4 t1 + gfpDouble(t1, t1, ec->f); + gfpDouble(t1, t1, ec->f); + // 16: t3 <- t3 * t5 + qrMul(t3, t3, t5, ec->f, stack); + // 17: t2 <- t2 * t3 + qrMul(t2, t2, t3, ec->f, stack); + // 18: t2 <- 8 t2 + gfpDouble(t2, t2, ec->f); + gfpDouble(t2, t2, ec->f); + gfpDouble(t2, t2, ec->f); + // 19: t5 <- t4^2 + qrSqr(t5, t4, ec->f, stack); + // 20: t3 <- t5 - t3 + qrSub(t3, t5, t3, ec->f); + // 21: t3 <- 4 t3 + gfpDouble(t3, t3, ec->f); + gfpDouble(t3, t3, ec->f); + // 22: t3 <- t3 - t1 + qrSub(t3, t3, t1, ec->f); + // 23: t3 <- t3 - t1 + qrSub(t3, t3, t1, ec->f); + // 24: t3 <- t3 - t1 + qrSub(t3, t3, t1, ec->f); + // 25: t4 <- t3 + t4 + qrAdd(t4, t3, t4, ec->f); + // 26: t4 <- t4^2 + qrSqr(t4, t4, ec->f, stack); + // 27: t4 <- t5 - t4 + qrSub(t4, t5, t4, ec->f); + // 28: t4 <- t4 - t2 + qrSub(t4, t4, t2, ec->f); + // 29: t4 <- t4 - t2 + qrSub(t4, t4, t2, ec->f); + // 30: t5 <- t3^2 + qrSqr(t5, t3, ec->f, stack); + // 31: t4 <- t4 + t5 + qrAdd(t4, t4, t5, ec->f); + // 32: t1 <- t1 * t5 + qrMul(t1, t1, t5, ec->f, stack); + // 33: t5 <- t3 * t5 + qrMul(t5, t3, t5, ec->f, stack); + // 34: t3 <- t3 * t6 + qrMul(t3, t3, t6, ec->f, stack); + // 35: t2 <- t2 * t5 + qrMul(t2, t2, t5, ec->f, stack); + // шаг 36 будет после шага 38 + // 37: t6 <- t4^2 + qrSqr(t6, t4, ec->f, stack); + // 38: t6 <- t6 - t5 + qrSub(t6, t6, t5, ec->f); + // 36: t5 <- 3 t1 + gfpDouble(t5, t1, ec->f); + qrAdd(t5, t5, t1, ec->f); + // 39: t5 <- t5 - t6 + qrSub(t5, t5, t6, ec->f); + // 40: t4 <- t4 * t5 + qrMul(t4, t4, t5, ec->f, stack); + // 41: t2 <- t4 - t2 + qrSub(t2, t4, t2, ec->f); + // 42: t1 <- t1 - t5 + qrSub(t1, t1, t5, ec->f); + // c <- pt = (t1 : t2 : t3) + wwCopy(c, pt, 3 * n); +} + +size_t ecpDblAddAJ_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpDblAddAJ_local(n), + utilMax(2, + f_deep, + ecpTplJ_deep(n, f_deep)), + SIZE_MAX); +} + /* ******************************************************************************* Финишное сложение diff --git a/src/math/zz/zz_gcd.c b/src/math/zz/zz_gcd.c index baf3a872..e068d1a6 100644 --- a/src/math/zz/zz_gcd.c +++ b/src/math/zz/zz_gcd.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: Euclidian gcd algorithms \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.19 +\version 2026.01.22 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -367,7 +367,7 @@ void zzDivMod(word b[], const word divident[], const word a[], ASSERT(wwCmp(a, mod, n) < 0); ASSERT(wwCmp(divident, mod, n) < 0); ASSERT(wwIsDisjoint(b, mod, n)); - ASSERT(n > 0 && mod[n - 1] != 0 && zzIsOdd(mod, n)); + ASSERT(zzIsOdd(mod, n) && mod[n - 1] != 0); // деление на 0? if (wwIsZero(a, n)) { diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index e8c5dd22..a9b93ea8 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -61,7 +61,7 @@ bool_t ecpBench() const size_t reps = 500; size_t i; tm_ticks_t ticks; - // эксперимент: ecMulNAF() + // эксперимент: ecMulA() for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, ec->f->no, combo_state); @@ -71,7 +71,7 @@ bool_t ecpBench() printf("ecpBench::ecMulA: %u cycles/mulpoint [%u mulpoints/sec]\n", (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); - // эксперимент: ecMulSNZ() + // эксперимент: ecMulA2() for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, ec->f->no, combo_state); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index d43c4368..6887bf86 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.21 +\version 2026.01.22 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -61,6 +61,12 @@ bool_t ecpTest() qr_o* f; /* [f_keep] */ octet* t; /* [3 * no] */ word* pts; /* [6 * 3 * n] */ + word* pt0; /* [3 * n] */ + word* pt1; /* [3 * n] */ + word* pt2; /* [3 * n] */ + word* pt3; /* [3 * n] */ + word* pt4; /* [3 * n] */ + word* pt5; /* [3 * n] */ word* d; /* [n] */ void* stack; // создать состояние @@ -88,6 +94,8 @@ bool_t ecpTest() &ec, &f, &t, &pts, &d, &stack); if (state == 0) return FALSE; + pt0 = pts, pt1 = pt0 + 3 * n, pt2 = pt1 + 3 * n, pt3 = pt2 + 3 * n, + pt4 = pt3 + 3 * n, pt5 = pt4 + 3 * n; // создать f = GF(p) hexToRev(t, p); if (!gfpCreate(f, t, no, stack)) @@ -111,7 +119,7 @@ bool_t ecpTest() // корректная кривая? // корректная группа? // надежная группа? - // базовая точнка имеет порядок q? + // базовая точка имеет порядок q? if (!ecpIsValid(ec, stack) || !ecpGroupSeemsValid(ec, stack) || !ecpGroupIsSafe(ec, 40, stack) || @@ -126,24 +134,23 @@ bool_t ecpTest() d[0] = 3; // удвоить и сложить if (!ecpIsOnA(ec->base, ec, stack) || - !ecpAddAA(pts, ec->base, ec->base, ec, stack) || - !ecpAddAA(pts, pts, ec->base, ec, stack) || + !ecpAddAA(pt0, ec->base, ec->base, ec, stack) || + !ecpAddAA(pt0, pt0, ec->base, ec, stack) || // дважды удвоить и вычесть - !ecpAddAA(pts + 2 * n, ec->base, ec->base, ec, stack) || - !ecpAddAA(pts + 2 * n, pts + 2 * n, pts + 2 * n, ec, stack) || - !ecpSubAA(pts + 2 * n, pts + 2 * n, ec->base, ec, stack) || - !wwEq(pts, pts + 2 * n, 2 * n) || - (ecpNegA(pts + 2 * n, pts + 2 * n, ec), - ecpAddAA(pts + 2 * n, pts, pts + 2 * n, ec, stack)) || + !ecpAddAA(pt1, ec->base, ec->base, ec, stack) || + !ecpAddAA(pt1, pt1, pt1, ec, stack) || + !ecpSubAA(pt1, pt1, ec->base, ec, stack) || + !wwEq(pt0, pt1, 2 * n) || + (ecpNegA(pt1, pt1, ec), ecpAddAA(pt1, pt0, pt1, ec, stack)) || // вычислить кратную точку - !ecMulA(pts + 2 * n, ec->base, ec, d, 1, stack) || - !wwEq(pts, pts + 2 * n, 2 * n) || + !ecMulA(pt1, ec->base, ec, d, 1, stack) || + !wwEq(pt0, pt1, 2 * n) || // утроить напрямую !ec->froma || !ec->tpl || !ec->toa || - (ec->froma(pts + 2 * n, ec->base, ec, stack), - ec->tpl(pts + 2 * n, pts + 2 * n, ec, stack), - ec->toa(pts + 2 * n, pts + 2 * n, ec, stack), - !wwEq(pts, pts + 2 * n, 2 * n))) + (ec->froma(pt1, ec->base, ec, stack), + ec->tpl(pt1, pt1, ec, stack), + ec->toa(pt1, pt1, ec, stack), + !wwEq(pt0, pt1, 2 * n))) { blobClose(state); return FALSE; @@ -155,52 +162,52 @@ bool_t ecpTest() zzSubW2(d, n, 1); while (!wwIsZero(d, n)) { - if (!ecMulA2(pts, ec->base, ec, d, n, stack) || - !ecMulA(pts + 2 * n, ec->base, ec, d, n, stack) || - !wwEq(pts, pts + 2 * n, 2 * n)) + if (!ecMulA2(pt0, ec->base, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) return FALSE; wwShLo(d, n, 1); } - if (ecMulA(pts, ec->base, ec, d, n, stack) || - ecMulA2(pts + 2 * n, ec->base, ec, d, n, stack)) + if (ecMulA(pt0, ec->base, ec, d, n, stack) || + ecMulA2(pt1, ec->base, ec, d, n, stack)) return FALSE; } // финишное сложение if (ec->finadd) { - // (pts[1], pts[2], pts[3], pts[4]) <- (base, 2 base, 3 base, 4 base) - ecFromA(pts + 1 * 3 * n, ec->base, ec, stack); - ecAddA(pts + 2 * 3 * n, pts + 1 * 3 * n, ec->base, ec, stack); - ecAddA(pts + 3 * 3 * n, pts + 2 * 3 * n, ec->base, ec, stack); - ecAddA(pts + 4 * 3 * n, pts + 3 * 3 * n, ec->base, ec, stack); - // pts[0] <- pts[1] + pts[2] = 3 base, pts[0] ==? pts[3] - ec->finadd(pts, pts + 1 * 3 * n, pts + 2 * 3 * n, 0, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 3 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // (pt1, pt2, pt3, pt4) <- (base, 2 base, 3 base, 4 base) + ecFromA(pt1, ec->base, ec, stack); + ecAddA(pt2, pt1, ec->base, ec, stack); + ecAddA(pt3, pt2, ec->base, ec, stack); + ecAddA(pt4, pt3, ec->base, ec, stack); + // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? + ec->finadd(pt0, pt1, pt2, 0, ec, stack); + ecToA(pt5, pt3, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] <- -(pts[1] + pts[3]) == -4 base, pts[0] ==? -pts[4] - ec->finadd(pts, pts + 1 * 3 * n, pts + 3 * 3 * n, 1, ec, stack); - ecNeg(pts + 5 * 3 * n, pts + 4 * 3 * n, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 5 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // pt0 <- -(pt1 + pt3) == -4 base, pt0 == -pt4? + ec->finadd(pt0, pt1, pt3, 1, ec, stack); + ecNeg(pt5, pt4, ec, stack); + ecToA(pt5, pt5, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] <- -(pts[2] + pts[2]) == -4 base, pts[0] ==? -pts[4] - ec->finadd(pts, pts + 2 * 3 * n, pts + 2 * 3 * n, 1, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // pt0 <- -(pt2 + pt2) == -4 base, pt0 == -pt4? + ec->finadd(pt0, pt2, pt2, 1, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] <- pts[1] + pts[1] == 2 base, pts[0] ==? pts[2] - ec->finadd(pts, pts + 1 * 3 * n, pts + 1 * 3 * n, 0, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // pt0 <- pt1 + pt1 == 2 base, pt0 == pt2? + ec->finadd(pt0, pt1, pt1, 0, ec, stack); + ecToA(pt5, pt2, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; @@ -209,44 +216,90 @@ bool_t ecpTest() // финишное сложение с аффинной точкой if (ec->finadda) { - // (pts[1], pts[2], pts[3], pts[4]) <- (base, 2 base, 3 base, 4 base) - ecFromA(pts + 1 * 3 * n, ec->base, ec, stack); - ecAddA(pts + 2 * 3 * n, pts + 1 * 3 * n, ec->base, ec, stack); - ecAddA(pts + 3 * 3 * n, pts + 2 * 3 * n, ec->base, ec, stack); - ecAddA(pts + 4 * 3 * n, pts + 3 * 3 * n, ec->base, ec, stack); - // pts[0] <- pts[1] + pts[2] = 3 base, pts[0] ==? pts[3] - ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); - ec->finadda(pts, pts + 1 * 3 * n, pts + 5 * 3 * n, 0, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 3 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // (pt1, pt2, pt3, pt4) <- (base, 2 base, 3 base, 4 base) + ecFromA(pt1, ec->base, ec, stack); + ecAddA(pt2, pt1, ec->base, ec, stack); + ecAddA(pt3, pt2, ec->base, ec, stack); + ecAddA(pt4, pt3, ec->base, ec, stack); + // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? + ecToA(pt5, pt2, ec, stack); + ec->finadda(pt0, pt1, pt5, 0, ec, stack); + ecToA(pt5, pt3, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] <- -(pts[1] + pts[3]) == -4 base, pts[0] ==? -pts[4] - ecToA(pts + 5 * 3 * n, pts + 3 * 3 * n, ec, stack); - ec->finadda(pts, pts + 1 * 3 * n, pts + 5 * 3 * n, 1, ec, stack); - ecNeg(pts + 5 * 3 * n, pts + 4 * 3 * n, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 5 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // pt0 <- -(pt1 + pt3) == -4 base, pt0 == -pt4? + ecToA(pt5, pt3, ec, stack); + ec->finadda(pt0, pt1, pt5, 1, ec, stack); + ecNeg(pt5, pt4, ec, stack); + ecToA(pt5, pt5, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] <- -(pts[2] + pts[2]) == -4 base, pts[0] ==? -pts[4] - ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); - ec->finadda(pts, pts + 2 * 3 * n, pts + 5 * 3 * n, 1, ec, stack); - ecNeg(pts + 5 * 3 * n, pts + 4 * 3 * n, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 5 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // pt0 <- -(pt2 + pt2) == -4 base, pt0 == -pt4? + ecToA(pt5, pt2, ec, stack); + ec->finadda(pt0, pt2, pt5, 1, ec, stack); + ecNeg(pt5, pt4, ec, stack); + ecToA(pt5, pt5, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] <- pts[1] + pts[1] == 2 base, pts[0] ==? pts[2] - ec->finadda(pts, pts + 1 * 3 * n, ec->base, 0, ec, stack); - ecToA(pts + 5 * 3 * n, pts + 2 * 3 * n, ec, stack); - if (!wwEq(pts, pts + 5 * 3 * n, 2 * n)) + // pt0 <- pt1 + pt1 == 2 base, pt0 ==? pt2 + ec->finadda(pt0, pt1, ec->base, 0, ec, stack); + ecToA(pt5, pt2, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // удвоение и сложение с аффинной точкой + if (ec->dbladda) + { + // pt0 <- 2 base + base + ecFromA(pt1, ec->base, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt3 <- 3 base + ecpAddAA(pt3, ec->base, ec->base, ec, stack); + ecpAddAA(pt3, pt3, ec->base, ec, stack); + // pt0 == pt3? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2(-base) + base + ecNeg(pt1, pt1, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt3 <- -base + ecpNegA(pt3, ec->base, ec); + // pt0 == pt3? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2O + base + ecSetO(pt1, ec); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt0 == base? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, ec->base, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2(2base) + base + ecDblA(pt1, ec->base, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt0 == 5 base? + d[0] = 5, ecMulA(pt3, ec->base, ec, d, 1, stack); + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) { blobClose(state); return FALSE; @@ -255,35 +308,35 @@ bool_t ecpTest() // малые кратные if (ec->smul) { - // (pts[0], ..., pts[3]) <- (base, 3 base, 5 base, 7 base) + // (pt0, pt1, pt2, pt3) <- (base, 3 base, 5 base, 7 base) ec->smul(pts, ec->base, 3, ec, stack); - // pts[0] ==? base? - ecToA(pts + 4 * 3 * n, pts + 0 * 3 * n, ec, stack); - if (!wwEq(pts + 4 * 3 * n, ec->base, 2 * n)) + // pt0 == base? + ecToA(pt4, pt0, ec, stack); + if (!wwEq(pt5, ec->base, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] ==? 3 base? - d[0] = 3, ecMulA(pts + 5 * 3 * n, ec->base, ec, d, 1, stack); - ecToA(pts + 4 * 3 * n, pts + 1 * 3 * n, ec, stack); - if (!wwEq(pts + 4 * 3 * n, pts + 5 * 3 * n, 2 * n)) + // pt1 == 3 base? + d[0] = 3, ecMulA(pt5, ec->base, ec, d, 1, stack); + ecToA(pt4, pt1, ec, stack); + if (!wwEq(pt4, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] ==? 5 base? - d[0] = 5, ecMulA(pts + 5 * 3 * n, ec->base, ec, d, 1, stack); - ecToA(pts + 4 * 3 * n, pts + 2 * 3 * n, ec, stack); - if (!wwEq(pts + 4 * 3 * n, pts + 5 * 3 * n, 2 * n)) + // pt2 == 5 base? + d[0] = 5, ecMulA(pt5, ec->base, ec, d, 1, stack); + ecToA(pt4, pt2, ec, stack); + if (!wwEq(pt4, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pts[0] ==? 7 base? - d[0] = 7, ecMulA(pts + 5 * 3 * n, ec->base, ec, d, 1, stack); - ecToA(pts + 4 * 3 * n, pts + 3 * 3 * n, ec, stack); - if (!wwEq(pts + 4 * 3 * n, pts + 5 * 3 * n, 2 * n)) + // pt3 == 7 base? + d[0] = 7, ecMulA(pt5, ec->base, ec, d, 1, stack); + ecToA(pt4, pt3, ec, stack); + if (!wwEq(pt4, pt5, 2 * n)) { blobClose(state); return FALSE; @@ -310,22 +363,21 @@ bool_t ecpTest() d[0] = 3; // удвоить и сложить if (!ecpIsOnA(ec->base, ec, stack) || - !ecpAddAA(pts, ec->base, ec->base, ec, stack) || - !ecpAddAA(pts, pts, ec->base, ec, stack) || + !ecpAddAA(pt0, ec->base, ec->base, ec, stack) || + !ecpAddAA(pt0, pt0, ec->base, ec, stack) || // дважды удвоить и вычесть - !ecpAddAA(pts + 2 * n, ec->base, ec->base, ec, stack) || - !ecpAddAA(pts + 2 * n, pts + 2 * n, pts + 2 * n, ec, stack) || - !ecpSubAA(pts + 2 * n, pts + 2 * n, ec->base, ec, stack) || - !wwEq(pts, pts + 2 * n, 2 * n) || - (ecpNegA(pts + 2 * n, pts + 2 * n, ec), - ecpAddAA(pts + 2 * n, pts, pts + 2 * n, ec, stack)) || + !ecpAddAA(pt1, ec->base, ec->base, ec, stack) || + !ecpAddAA(pt1, pt1, pt1, ec, stack) || + !ecpSubAA(pt1, pt1, ec->base, ec, stack) || + !wwEq(pt0, pt1, 2 * n) || + (ecpNegA(pt1, pt1, ec), ecpAddAA(pt1, pt0, pt1, ec, stack)) || // вычислить кратную точку - !ecMulA(pts + 2 * n, ec->base, ec, d, 1, stack) || - !wwEq(pts, pts + 2 * n, 2 * n) || - (ec->froma(pts + 2 * n, ec->base, ec, stack), - ec->tpl(pts + 2 * n, pts + 2 * n, ec, stack), - ec->toa(pts + 2 * n, pts + 2 * n, ec, stack), - !wwEq(pts, pts + 2 * n, 2 * n))) + !ecMulA(pt1, ec->base, ec, d, 1, stack) || + !wwEq(pt0, pt1, 2 * n) || + (ec->froma(pt1, ec->base, ec, stack), + ec->tpl(pt1, pt1, ec, stack), + ec->toa(pt1, pt1, ec, stack), + !wwEq(pt0, pt1, 2 * n))) { blobClose(state); return FALSE; From ade2cc36c388f965841345d63a3a9ccc840c75cb Mon Sep 17 00:00:00 2001 From: agievich Date: Mon, 26 Jan 2026 23:21:42 +0300 Subject: [PATCH 17/77] Start math/ec/pre --- include/bee2/math/ec.h | 63 +++++++++++++++++++++-------------------- include/bee2/math/ecp.h | 45 ++++++++++++++++++++++++++++- src/math/ec.c | 33 +++++++-------------- src/math/ec2.c | 6 ++-- src/math/ecp.c | 6 ++-- 5 files changed, 94 insertions(+), 59 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index b5950632..9db4bdf1 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.21 +\version 2026.01.26 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -91,8 +91,9 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). Если работа с группой точек не предполагается, то поля ec->base, ec->order и ec->cofactor можно не инициализировать. -По адресу ec->params могут размещаться дополнительные данные, например, -данные, с помощью которых можно ускорить вычисления на эллиптической кривой. +По адресу ec->pre могут размещаться предвычисленные точки, построеннные +по базовой точке base. С их помощью можно ускорить вычисления на эллиптической +кривой. Описание эллиптической кривой включает указатели на функции арифметики в группе точек этой кривой. Функции интерфейсов ec_dbladd_i, ec_dbladda_i, @@ -398,38 +399,41 @@ typedef bool_t(*ec_finadda_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Малые нечетные кратные - - На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных - аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере - [2^{w-1} * ec->d * ec->f->n]b: - \code - (b[0], ..., b[2^{w-1} - 1]) <- (a, 3a, ..., (2^w - 1)a). - \endcode - Каждое кратное занимает ec->d * ec->f->n машинных слов. - \pre w > 0. - \pre Описание ec работоспособно. - \pre Буферы a и b либо не пересекаются, либо указатели a и b совпадают. - \pre Координаты a лежат в базовом поле. - \expect Описание ec корректно. - \expect Точка a лежит на кривой. - \post Точка, составленная из двух первых координат b[0], совпадает с a и, - таким образом, является аффинной точкой кривой. +/*! \brief Схема предвычислений */ +typedef enum +{ + ec_pre_snz = 1, /*!< схема SNZ */ + ec_pre_snza, /*!< схема SNZA */ +} ec_pre_type; + +/*! \brief Предвычисленные точки + + В поле pts размещаются точки эллиптической кривой, построенные + по некоторой точке a в соответствии с определенной схемой + предвычислений. Число предвычисленных точек определяется + шириной окна w. + Поддерживаются следующие схемы: + - SNZ (Signed Non-Zero): вычисляется проективные точки + -(2^w-1)a, ..., -3a, a, a, 3a, ..., (2^w-1)a + (2^w точек); + - SNZA: вычисляются аффинные точки + -(2^w-1)a, ..., -3a, a, a, 3a, ..., (2^w-1)a + (2^w точек). */ -typedef void(*ec_smul_i)( - word b[], /*!< [out] малые кратные */ - const word a[], /*!< [in] первоначальная точка */ - size_t w, /*!< [in] ширина окна */ - const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ - void* stack /*!< [in] вспомогательная память */ - ); +typedef struct ec_pre_t +{ + ec_pre_type type; /*!< схема предвычислений */ + size_t width; /*!< ширина окна */ + word pts[]; /*!< точки */ +} ec_pre_t; + /*! \brief Описание эллиптической кривой Описывается эллиптическая кривая, правила представления ее элементов, группа точек и функции, реализующие операции в группе. \remark В таблицу указателей описания кривой как объекта входят поля - f, A, B, base, order, params. Поле f является указателем на объект. + f, A, B, base, order, pre. Поле f является указателем на объект. */ typedef struct ec_o { @@ -440,7 +444,7 @@ typedef struct ec_o word* B; /*!< коэффициент B */ word* base; /*!< базовая точка */ word* order; /*!< порядок группы точек */ - void* params; /*!< дополнительные параметры */ + ec_pre_t* pre; /*!< предвычисленные точки */ // } size_t d; /*!< размерность */ word cofactor; /*!< кофактор группы точек */ @@ -455,7 +459,6 @@ typedef struct ec_o ec_dbladda_i dbladda; /*!< функция удвоения и сложения с афф. точкой */ ec_finadd_i finadd; /*!< функция финишного сложения */ ec_finadda_i finadda; /*!< функция финишного сложения с афф. точкой */ - ec_smul_i smul; /*!< функция расчета малых кратных */ size_t deep; /*!< максимальная глубина стека функций */ mem_align_t descr[]; /*!< память для размещения данных */ } ec_o; diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 4c1fc182..1427ed21 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.01.08 +\version 2026.01.26 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -144,6 +144,7 @@ bool_t ecpGroupIsSafe( size_t ecpGroupIsSafe_deep(size_t n); + /* ******************************************************************************* Арифметика аффинных точек @@ -231,6 +232,12 @@ bool_t ecpSubAA( size_t ecpSubAA_deep(size_t n, size_t f_deep); +/* +******************************************************************************* +Хэширование на кривую +******************************************************************************* +*/ + /*! \brief Преобразование элемента поля в аффинную точку Элемент [ec->f->n]a поля ec->f преобразуется в аффинную точку @@ -252,6 +259,42 @@ void ecpSWU( size_t ecpSWU_deep(size_t n, size_t f_deep); +/* +******************************************************************************* +Предвычисления +******************************************************************************* +*/ + +/*! \brief Предвычисления по схеме SNZ + + На эллиптической кривой ec вычисляются 2^w малых нечетных кратных + аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере + [2^w * ec->d * ec->f->n]pre: + \code + pre[0:2^w) <- (-(2^w - 1)a, ..., -3a, a, 3a, ..., (2^w - 1)a). + \endcode + Каждое кратное является проективной точкой и занимает ec->d * ec->f->n + машинных слов. + \pre w > 0. + \pre Описание ec работоспособно. + \pre Буферы a и pre либо не пересекаются, либо указатели a и pre совпадают. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. + \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->f->deep, ec->d, ec->deep, w). +*/ +void ecpPreSNZ( + word pre[], /*!< [out] малые кратные */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t ec_d, size_t ec_deep, + size_t w); + + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/math/ec.c b/src/math/ec.c index ca71b1a4..2a05b47b 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.21 +\version 2026.01.26 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -105,12 +105,12 @@ bool_t ecGroupIsOperable(const ec_o* ec) /* ******************************************************************************* -Малые нечетные кратные +Предвычисления -Прямая реализация интерфейса ec_smul_i: - t <- 2a, b[0] <- a, b[i] <- t + b[i - 1], i = 1,..., 2^(w-1) - 1. +Прямая реализация схемы SNZ: + t <- 2a, pre[0] <- a, pre[i] <- t + pre[i - 1], i = 1,..., 2^(w-1) - 1. -Сложение t + b[0] выполняется по схеме P <- P + A. Остальные сложения -- по +Сложение t + pre[0] выполняется по схеме P <- P + A. Остальные сложения -- по схеме P <- P + P. Итоговая сложность: 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). @@ -166,7 +166,7 @@ static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) ******************************************************************************* Сложение с настройкой знака -Прямые нерегулярные реализации интерфейсов ec_negaadd_i и ec_negaadda_i. +Прямые нерегулярные реализации интерфейсов ec_finadd_i и ec_finadda_i. ******************************************************************************* */ @@ -259,9 +259,7 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) Длина окна w выбирается как решение следующей оптимизационной задачи: (2^{w - 2} - 2) + l / (w + 1) -> min. -Малые кратные вычисляются с помощью функции интерфейса ec_smul_i, указанной -в описании кривой. Если функция не задана в описании, то используется функция -по умолчанию ecSmul(). +Предвычисления выполняются с помощью функции ecSmul(). \todo Усилить вторую стратегию. Рассчитать малые кратные в проективных координатах, а затем быстро перейти к аффинным координатам с помощью @@ -322,10 +320,7 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, if (naf_size == 0) return FALSE; // малые кратные: a, 3a, ..., (2^w - 1)a - if (ec->smul) - ec->smul(pre, a, naf_width - 1, ec, stack); - else - ecSmul(pre, a, naf_width - 1, ec, stack); + ecSmul(pre, a, naf_width - 1, ec, stack); // отрицательные малые кратные: -a, -3a, ..., -(2^w - 1)a for (i = 0; i < pre_count / 2; ++i) ecNeg(ecPt(pre, pre_count / 2 + i, ec), ecPt(pre, i, ec), ec, stack); @@ -387,7 +382,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pre[i] <- (2i - 1)a; // ec_smul_i + 1) pre[i] <- (2i - 1)a; // ecSmul 2) pre[-i] <- -pre[i]. // ec_neg_i 2. t <- pre[d_{k-1} / 2] 3. Для i = k - 2, ..., 1: @@ -463,10 +458,7 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); // малые кратные: a, 3a, ..., (2^w - 1)a - if (ec->smul) - ec->smul(pre, a, snz_width, ec, stack); - else - ecSmul(pre, a, snz_width, ec, stack); + ecSmul(pre, a, snz_width, ec, stack); // отрицательные малые кратные: -(2^w - 1)a, ..., -3a, -a for (i = 0; i < pre_count / 2; ++i) ecNeg(ecPt(pre, pre_count - 1 - i, ec), ecPt(pre, i, ec), ec, stack); @@ -614,10 +606,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) naf_max_size = naf_size[i]; naf_pos[i] = 0; // малые кратные - if (ec->smul) - ec->smul(pre[i], a, naf_width[i] - 1, ec, stack); - else - ecSmul(pre[i], a, naf_width[i] - 1, ec, stack); + ecSmul(pre[i], a, naf_width[i] - 1, ec, stack); // отрицательные малые кратные for (j = 0; j < pre_count / 2; ++j) ec->neg(pre[i] + (pre_count / 2 + j) * ec->d * ec->f->n, diff --git a/src/math/ec2.c b/src/math/ec2.c index 20cf0e45..a5d0191a 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.21 +\version 2026.01.26 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -549,7 +549,7 @@ static size_t ec2AddALD_deep(size_t n, size_t f_deep) /* B */ O_OF_W(n),\ /* base */ O_OF_W(2 * n),\ /* order */ O_OF_W(n + 1),\ -/* params */ SIZE_0 +/* pre */ SIZE_0 bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[], void* stack) @@ -567,7 +567,7 @@ bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[], // разметить состояние memSlice(ec->descr, ec2CreateLD_state(f->n), SIZE_MAX, - &ec->A, &ec->B, &ec->base, &ec->order, &ec->params); + &ec->A, &ec->B, &ec->base, &ec->order, &ec->pre); // сохранить коэффициенты if (!qrFrom(ec->A, A, ec->f, stack) || !qrFrom(ec->B, B, ec->f, stack)) return FALSE; diff --git a/src/math/ecp.c b/src/math/ecp.c index abc60592..f1ecc6f3 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.22 +\version 2026.01.26 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -1256,7 +1256,7 @@ static size_t ecpFinAddA_deep(size_t n, size_t f_deep) /* B */ O_OF_W(n),\ /* base */ O_OF_W(2 * n),\ /* order */ O_OF_W(n + 1),\ -/* params */ SIZE_0 +/* pre */ SIZE_0 #define ecpCreateJ_local(n)\ /* t */ O_OF_W(n) @@ -1283,7 +1283,7 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], // разметить состояние и стек memSlice(ec->descr, ecpCreateJ_state(f->n), SIZE_MAX, - &ec->A, &ec->B, &ec->base, &ec->order, &ec->params); + &ec->A, &ec->B, &ec->base, &ec->order, &ec->pre); memSlice(stack, ecpCreateJ_local(f->n), SIZE_0, SIZE_MAX, &t, &stack); From 2b253b0a3c24dbe173ec010a0c8081e24479fc2d Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 27 Jan 2026 18:52:28 +0300 Subject: [PATCH 18/77] Start ec/pre/SNZ --- include/bee2/math/ec.h | 53 ++++---- include/bee2/math/ww.h | 33 ++--- src/math/ec.c | 291 +++++++++++++++++++++++++---------------- src/math/ecp.c | 11 ++ src/math/ww.c | 80 +++++------ test/math/ecp_test.c | 6 +- 6 files changed, 273 insertions(+), 201 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 9db4bdf1..780b0b9a 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.26 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -91,14 +91,17 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). Если работа с группой точек не предполагается, то поля ec->base, ec->order и ec->cofactor можно не инициализировать. -По адресу ec->pre могут размещаться предвычисленные точки, построеннные +По адресу ec->pre могут размещаться точки кривой, предварительно построеннные по базовой точке base. С их помощью можно ускорить вычисления на эллиптической -кривой. +кривой. Предвычисления могут выполняться по разным схемам. Схема +предвычислений указывается вместе с вычисленными точками. Всего вычисляется +2^w точек. Параметр w, который называется длиной окна, также указывается +вместе с точками. Описание эллиптической кривой включает указатели на функции арифметики -в группе точек этой кривой. Функции интерфейсов ec_dbladd_i, ec_dbladda_i, -ec_tpl_i, ec_finadd_i, ec_finadda_i и ec_smul_i можно не поддерживать. -Указатель на неподдерживаемую функцию должен быть нулевым. +в группе точек этой кривой. Функции интерфейсов ec_dbladda_i, ec_tpl_i, +ec_finadd_i и ec_finadda_i можно не поддерживать. Указатель на +неподдерживаемую функцию должен быть нулевым. Описание кольца организовано как объект, и можно применять функции, описанные в заголовочном файле obj.h. @@ -132,8 +135,8 @@ ecCreate() и сохраняется в поле deep описания крив В функцию ecCreate() могут не передаваться параметры base, order и cofactor, определяющие строение группы точек, хотя буферы для base и order могут -подготавливаться в ecCreate(). Параметры могут устанавливаться позднее -с помощью функции ecGroupCreate(). +готовиться в ecCreate(). Параметры могут устанавливаться позднее с помощью +функции ecGroupCreate(). \pre Все указатели действительны. @@ -402,8 +405,9 @@ typedef bool_t(*ec_finadda_i)( /*! \brief Схема предвычислений */ typedef enum { - ec_pre_snz = 1, /*!< схема SNZ */ + ec_pre_snz = 1, /*!< схема SNZ */ ec_pre_snza, /*!< схема SNZA */ + ec_pre_comb, /*!< схема Comb */ } ec_pre_type; /*! \brief Предвычисленные точки @@ -411,19 +415,22 @@ typedef enum В поле pts размещаются точки эллиптической кривой, построенные по некоторой точке a в соответствии с определенной схемой предвычислений. Число предвычисленных точек определяется - шириной окна w. - Поддерживаются следующие схемы: - - SNZ (Signed Non-Zero): вычисляется проективные точки - -(2^w-1)a, ..., -3a, a, a, 3a, ..., (2^w-1)a - (2^w точек); - - SNZA: вычисляются аффинные точки - -(2^w-1)a, ..., -3a, a, a, 3a, ..., (2^w-1)a - (2^w точек). + шириной окна w и равняется 2^w. Поддерживаются следующие схемы: + - SNZ (Signed Non-Zero): вычисляется проективные точки + a, 3a, ..., (2^w-1)a, -(2^w-1)a, ..., -3a, -a; + - SNZA: вычисляются аффинные точки + a, 3a, ..., (2^w-1)a, -(2^w-1)a, ..., -3a, -a; + - Comb: вычисляются аффинные точки + \pm a + \pm h a + \pm h^2 a + ... + \pm h^{w-1} a. + . + \pre 0 < w && w < B_PER_W. + \pre h > 0 <=> type == ec_pre_comb. */ typedef struct ec_pre_t { ec_pre_type type; /*!< схема предвычислений */ - size_t width; /*!< ширина окна */ + size_t w; /*!< ширина окна */ + size_t h; /*!< высота окна */ word pts[]; /*!< точки */ } ec_pre_t; @@ -510,16 +517,6 @@ typedef struct ec_o #define ecDblA(b, a, ec, stack)\ (ec)->dbla(b, a, ec, stack) -#define ecDblAdd(c, a, b, ec, stack)\ - ((ec)->dbladd ?\ - (ec)->dbladd(c, a, b, ec, stack) :\ - (ecDbl(c, a, ec, stack), ecAdd(c, c, b, ec, stack))) - -#define ecDblAddA(c, a, b, ec, stack)\ - ((ec)->dbladda ?\ - (ec)->dbladda(c, a, b, ec, stack) :\ - (ecDbl(c, a, ec, stack), ecAddA(c, c, b, ec, stack))) - #define ecSetO(a, ec)\ wwSetZero(ecZ(a, (ec)->f->n), (ec)->f->n) diff --git a/include/bee2/math/ww.h b/include/bee2/math/ww.h index 691d2a65..c08b7234 100644 --- a/include/bee2/math/ww.h +++ b/include/bee2/math/ww.h @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.01.12 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -439,9 +439,9 @@ size_t wwBitSize( /*! \brief Расчет NAF - В [2n + 1]naf помещается кодированное представление оконной оптимальной - знаковой формы (NAF) слова [n]a. NAF представляет собой последовательность - цифр (a_0, a_1,..., a_{l-1}) такую, что + В [2n + 1]naf формируется оконная оптимальная знаковая форма (Non-Adjacent + Form, NAF) слова [n]a. NAF представляет собой последовательность цифр + (a_{l-1}, ..., a_1, a_0) такую, что - a_i \in {0, \pm 1, \pm 3, ..., \pm 2^{w-1} - 1}; - если a != 0, то a_{l-1} != 0; - a как число равняется \sum {i=0}^{l-1} a_i 2^i; @@ -449,35 +449,26 @@ size_t wwBitSize( . Подробнее см. [Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004]. + Нулевая цифра a_i кодируется единственным нулевым битом. Ненулевая цифра + кодирируется w битами стандартного двоичного представления. Например, + цифра -1 кодируется битами 11...11, цифра -(2^{w-1} - 1) -- битами 10..01. + Код a_{l-1} записывается в младшие биты naf, код a_0 -- в старшие. \pre 2 <= w < B_PER_W. \pre Буфер naf не пересекается с буфером a. \return Длина naf (число цифр l). \safe Функция нерегулярна. \remark Длина NAF не превосходит wwBitSize(a) + 1. - \remark Кодирование цифр NAF состоит в следующем (\bin(b) -- двоичная - запись числа b): - - нулевые a_i представляются одним двоичным символом 0; - - положительные a_i представляются w битами 0\bin(a_i); - - отрицательные a_i представляются w битами 1\bin(|a_i|); - - кодированное представление - это конкатенация кода a_{l-1} - (первые биты), ...., кода a_1, кода a_0 (последние биты). - . \remark Для кодирования w последовательных цифр потребуется не более (w - 1) * 1 + 1 * w = 2 * w - 1 битов. Поэтому при w < B_PER_W для хранения всего кодового представления потребуется не более 2 * n + 1 слов. - \remark Декодирование NAF выполняется с помощью окна длины w: + \remark Декодирование NAF выполняется с помощью окна длины w, которое + смещается от младших битов naf к старшим по следующим правилам: - если слово в окне заканчивается нулевым битом, то декодируется цифра 0 - и окно смещается на 1 позицию вправо; + и окно смещается на 1 позицию; - в зависимости от того, начинается слово в окне нулевым или единичным битом, декодируется положительная или отрицательная цифра и окно - смещается на w позиций вправо. + смещается на w позиций. . - \remark Суффикс старших разрядов (\alpha, 0,..., 0, 1), в котором w - 1 нулей - и цифра \alpha < 0, заменяется на суффикс (\beta, 0,..., 0, 1), в котором - w - 2 нулей и цифра \beta = 2^{w - 1} + \alpha > 0. Замена корректна, так как - суффиксы описывают одинаковые числа: - 2^w + \alpha = 2^{w - 1} + \beta. - При этом длина второго суффикса и соответствующей NAF-формы на единицу меньше. */ size_t wwNAF( word naf[], /*!< [out] знаковая форма */ diff --git a/src/math/ec.c b/src/math/ec.c index 2a05b47b..c58d73cb 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.26 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -20,11 +20,21 @@ /* ******************************************************************************* -Вспомогательные определения +Вспомогательные функции и определения ******************************************************************************* */ -#define ecPt(pts, pos, ec) ((pts) + (pos) * (ec)->d * (ec)->f->n) +static ecPreIsValid(const ec_pre_t* pre) +{ + return memIsValid(pre, sizeof(pre)) && + (pre->type == ec_pre_snz || pre->type == ec_pre_snza || + pre->type == ec_pre_comb) && + pre->w > 0 && pre->w < B_PER_W && + (pre->type == ec_pre_comb && pre->h > 0 || + pre->type != ec_pre_comb && pre->h == 0); +} + +#define ecPrePt(pre, pos, ec) ((pre)->pts + (pos) * (ec)->d * (ec)->f->n) /* ******************************************************************************* @@ -108,63 +118,72 @@ bool_t ecGroupIsOperable(const ec_o* ec) Предвычисления Прямая реализация схемы SNZ: - t <- 2a, pre[0] <- a, pre[i] <- t + pre[i - 1], i = 1,..., 2^(w-1) - 1. +- t <- 2a; +- pt[0] <- a; +- pt[i] <- t + pt[i - 1], i = 1, 2, ..., 2^(w-1) - 1; +- pt[2^w - 1 - i] = -pt[i], i = 0, 1, ..., 2^(w-1) - 1. -Сложение t + pre[0] выполняется по схеме P <- P + A. Остальные сложения -- по +Сложение t + pt[0] выполняется по схеме P <- P + A. Остальные сложения -- по схеме P <- P + P. Итоговая сложность: 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). -Здесь и далее +Здесь и далее: - (P <- 2A) -- время работы функции ec->dbla; - (P <- P + A) -- время работы функций ec->adda; - (P <- P + P) -- время работы функции ec->add. ******************************************************************************* */ -#define ecSmul_local(n, ec_d)\ +#define ecPreSNZ_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static void ecSmul(word b[], const word a[], size_t w, const ec_o* ec, +static void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ + size_t i; // pre ASSERT(ecIsOperable(ec)); ASSERT(w > 0); - ASSERT(wwIsDisjoint2(b, ec->d * ec->f->n * (SIZE_1 << (w - 1)), - a, 2 * ec->f->n) || a == b); + ASSERT(memIsDisjoint2(a, O_OF_W(2 * ec->f->n), + pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << w)))); // разметить стек memSlice(stack, - ecSmul_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecPreSNZ_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); + // pt[0] <- a + ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); // вычислить малые кратные if (w > 1) { - size_t i; // t <- 2 a ecDblA(t, a, ec, stack); - // b[1] <- t + a - ecAddA(b + ec->d * ec->f->n, t, a, ec, stack); - // b[i] <- t + b[i - 1] + // pt[1] <- t + a + ecAddA(ecPrePt(pre, 1, ec), t, a, ec, stack); + // pt[i] <- t + pt[i - 1] for (i = 2; i < SIZE_1 << (w - 1); ++i) - ecAdd(b + i * ec->d * ec->f->n, t, b + (i - 1) * ec->d * ec->f->n, - ec, stack); + ecAdd(ecPrePt(pre, i, ec), t, ecPrePt(pre, i - 1, ec), ec, stack); } - // b[0] <- a - ecFromA(b, a, ec, stack); + // pt[pre_count - i] <- -pt[i] + for (i = 0; i < SIZE_1 << (w - 1); ++i) + ecNeg(ecPrePt(pre, (SIZE_1 << w) - 1 - i, ec), ecPrePt(pre, i, ec), + ec, stack); + // заполнить остальные поля + pre->type = ec_pre_snz; + pre->w = w, pre->h = 0; } -static size_t ecSmul_deep(size_t n, size_t ec_d, size_t ec_deep) +static size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecSmul_local(n, ec_d), + ecPreSNZ_local(n, ec_d), ec_deep, SIZE_MAX); } /* ******************************************************************************* -Сложение с настройкой знака +Финишные сложения Прямые нерегулярные реализации интерфейсов ec_finadd_i и ec_finadda_i. ******************************************************************************* @@ -222,13 +241,13 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) /* ******************************************************************************* -Кратная точка: NAF (Non-Adjacent Form) +Кратная точка: метод NAF (Non-Adjacent Form) -В функции ecMulA() для определения b = da (d-кратного точки a) применяется -оконный NAF с длиной окна w > 2. Реализован алгоритм 3.36 из [HMV04]. +В функции ecMulPreNAF() для определения b = da (d-кратного точки a) применяется +оконный NAF с длиной окна w >= 2. Реализован алгоритм 3.36 из [HMV04]. -Используются малые нечетные кратные a: - b[i] = (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. +Используются малые нечетные кратные a вида: + \pm (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. Имеются три стратегии: 1) w = 2 и малые кратные вообще не рассчитываются; @@ -272,10 +291,10 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) V <- V Z_t Z_1^{-1} <- V -[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and - Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. [HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004. +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. ******************************************************************************* */ @@ -290,77 +309,106 @@ static size_t ecNAFWidth(size_t l) return 3; } -#define ecMulA_local(n, ec_d, m, pre_count)\ +#define ecMulPreNAF_local(n, ec_d, m)\ /* naf */ O_OF_W(2 * m + 1),\ -/* t */ O_OF_W(ec_d * n),\ -/* pre */ O_OF_W(pre_count * ec_d * n) +/* t */ O_OF_W(ec_d * n) -bool_t ecMulA(word b[], const word a[], const ec_o* ec, +static bool_t ecMulPreNAF(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { - const size_t naf_width = ecNAFWidth(B_OF_W(m)); - const word naf_hi = WORD_BIT_POS(naf_width - 1); - const size_t pre_count = SIZE_1 << (naf_width - 1); + register size_t naf_width; register size_t naf_size; register size_t i; register word digit; word* naf; /* [2 * m + 1] NAF */ word* t; /* [ec->d * ec->f->n] */ - word* pre; /* [pre_count * ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); + ASSERT(ecPreIsValid(pre)); + ASSERT(pre->type == ec_pre_snz); // разметить стек memSlice(stack, - ecMulA_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, - &naf, &t, &pre, &stack); + ecMulPreNAF_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &naf, &t, &stack); // расчет NAF - ASSERT(naf_width >= 3); + naf_width = pre->w + 1; naf_size = wwNAF(naf, d, m, naf_width); // d == O => b <- O if (naf_size == 0) return FALSE; - // малые кратные: a, 3a, ..., (2^w - 1)a - ecSmul(pre, a, naf_width - 1, ec, stack); - // отрицательные малые кратные: -a, -3a, ..., -(2^w - 1)a - for (i = 0; i < pre_count / 2; ++i) - ecNeg(ecPt(pre, pre_count / 2 + i, ec), ecPt(pre, i, ec), ec, stack); // старшая цифра NAF digit = wwGetBits(naf, 0, naf_width); - ASSERT((digit & 1) == 1 && (digit & naf_hi) == 0); - // t <- pre[digit / 2] - wwCopy(t, ecPt(pre, digit >> 1, ec), ec->d * ec->f->n); + ASSERT((digit & 1) == 1 && (digit >> (naf_width - 1)) == 0); + // t <- pt[digit / 2] + wwCopy(t, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); // цикл по цифрам NAF i = naf_width; while (--naf_size) { + // t <- 2t + ecDbl(t, t, ec, stack); + // обработать цифру digit = wwGetBits(naf, i, naf_width); - // t <- 2t + pre[digit / 2] - if (digit == 1 || digit == (naf_hi ^ 1)) - ecDblAddA(t, t, ecPt(pre, digit >> 1, ec), ec, stack); - else + if (digit & 1) { - // t <- 2t - ecDbl(t, t, ec, stack); - if (digit & 1) - // t <- t + pre[digit / 2] - ecAdd(t, t, ecPt(pre, digit >> 1, ec), ec, stack); + // t <- t + pt[digit / 2] + ecAdd(t, t, ecPrePt(pre, digit >> 1, ec), ec, stack); + // к следующей цифре + i += naf_width - 1; } - // к следующей цифре - i += (digit & 1) ? naf_width : 1; + ++i; } // очистка - CLEAN2(digit, i); + CLEAN3(naf_width, digit, i); // к аффинным координатам return ecToA(b, t, ec, stack); } +size_t ecMulPreNAF_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreNAF_local(n, ec_d, m), + ec_deep, + SIZE_MAX); +} + +#define ecMulA_local(n, ec_d, pre_count)\ +/* pre */ sizeof(ec_pre_t) + O_OF_W(pre_count * ec_d * n) + +bool_t ecMulA(word b[], const word a[], const ec_o* ec, + const word d[], size_t m, void* stack) +{ + size_t naf_width; + size_t pre_count; + ec_pre_t* pre; /* [pre_count проективных точек] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(wwIsValid(d, m)); + // размерности + m = wwWordSize(d, m); + if (m == 0) + return FALSE; + naf_width = ecNAFWidth(B_OF_W(m)); + pre_count = SIZE_1 << (naf_width - 1); + // разметить стек + memSlice(stack, + ecMulA_local(ec->f->n, ec->d, pre_count), SIZE_0, SIZE_MAX, + &pre, &stack); + // предвычисления + ecPreSNZ(pre, a, naf_width - 1, ec, stack); + // кратная точка по методу NAF + return ecMulPreNAF(b, pre, ec, d, m, stack); +} + size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); const size_t pre_count = SIZE_1 << (naf_width - 1); return memSliceSize( - ecMulA_local(n, ec_d, m, pre_count), - ecSmul_deep(n, ec_d, ec_deep), + ecMulA_local(n, ec_d, pre_count), + utilMax(2, + ecPreSNZ_deep(m, ec_d, ec_deep), + ecMulPreNAF_deep(n, ec_d, ec_deep, m)), SIZE_MAX); } @@ -382,19 +430,18 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pre[i] <- (2i - 1)a; // ecSmul - 2) pre[-i] <- -pre[i]. // ec_neg_i -2. t <- pre[d_{k-1} / 2] + 1) pt[i] <- (2i - 1)a, pt[-i] <- -pt[i]. // предвычисления +2. t <- p[d_{k-1} / 2]. 3. Для i = k - 2, ..., 1: - 1) t <- 2^w t; // ec_dbl_i - 1) t <- t + pre[d_i / 2]. // ec_add_i + 1) t <- 2^w t; // ec_dbl_i + 1) t <- t + pt[d_i / 2]. // ec_add_i 4. t <- 2^w t. -5. t <- t + pre[d_0]. // ec_finadd_i +5. t <- t + pt[d_0]. // ec_finadd_i 6. Возвратить t. -\remark сложение t + pre[d_0] вынесено за рамки основного цикла на шаге 3, +\remark сложение t + pt[d_0] вынесено за рамки основного цикла на шаге 3, потому что при этом (и только при этом) сложении может произойти исключительная -ситуация -- совпадение операндов t и pre[d_0] с переключением от сложения +ситуация -- совпадение операндов t и pt[d_0] с переключением от сложения к удвоению. Предполагается, что функция ec_finadd_i регулярна, и тогда сложение и удвоение будут выполняться по одним и тем же формулам. @@ -418,12 +465,11 @@ static size_t ecSNZWidth(size_t l) return 6; } -#define ecMulA2_local(n, ec_d, m, pre_count)\ +#define ecMulPreSNZ_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ -/* t */ O_OF_W(ec_d * n),\ -/* pre */ O_OF_W(pre_count * ec_d * n) +/* t */ O_OF_W(ec_d * n)\ -bool_t ecMulA2(word b[], const word a[], const ec_o* ec, +static bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -432,43 +478,36 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, register bool_t ret; size_t mb; size_t snz_width; - size_t pre_count; size_t k; size_t i; word* dd; /* [m] */ word* t; /* [ec->d * ec->f->n] */ - word* pre; /* [pre_count * ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); + ASSERT(ecPreIsValid(pre)); + ASSERT(pre->w == ec_pre_snz); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); - snz_width = ecSNZWidth(mb); - ASSERT(3 <= snz_width && snz_width < B_PER_W); - pre_count = SIZE_1 << snz_width; + snz_width = pre->w; // разметить стек memSlice(stack, - ecMulA2_local(ec->f->n, ec->d, m, pre_count), SIZE_0, SIZE_MAX, - &dd, &t, &pre, &stack); + ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &dd, &t, &stack); // dd <- (d & 1) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); - // малые кратные: a, 3a, ..., (2^w - 1)a - ecSmul(pre, a, snz_width, ec, stack); - // отрицательные малые кратные: -(2^w - 1)a, ..., -3a, -a - for (i = 0; i < pre_count / 2; ++i) - ecNeg(ecPt(pre, pre_count - 1 - i, ec), ecPt(pre, i, ec), ec, stack); // число цифр SNZ k = (mb + snz_width - 1) / snz_width; ASSERT(k > 1); // старшая цифра --k; digit = wwGetBits(dd, k * snz_width, mb - k * snz_width); - wwCopy(t, ecPt(pre, digit >> 1, ec), ec->d * ec->f->n); + wwCopy(t, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; // обработать остальные цифры while (--k) @@ -476,7 +515,7 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, for (i = snz_width; i--;) ecDbl(t, t, ec, stack); digit = wwGetBits(dd, k * snz_width, snz_width); - ecAdd(t, t, ecPt(pre, hi | (digit >> 1), ec), ec, stack); + ecAdd(t, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; } // завершающие удвоения и финишное сложение @@ -485,21 +524,59 @@ bool_t ecMulA2(word b[], const word a[], const ec_o* ec, digit = wwGetBits(dd, 0, snz_width); ASSERT(digit & 1); ret = (ec->finadd) ? - ec->finadd(b, t, ecPt(pre, hi | (digit >> 1), ec), neg, ec, stack) : - ecFinAdd(b, t, ecPt(pre, hi | (digit >> 1), ec), neg, ec, stack); + ec->finadd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), neg, ec, stack) : + ecFinAdd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); return ret; } +size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreSNZ_local(n, ec_d, m), + ecFinAdd_deep(n, ec_d, ec_deep), + SIZE_MAX); +} + +#define ecMulA2_local(n, ec_d, pre_count)\ +/* pre */ sizeof(ec_pre_t) + O_OF_W(pre_count * ec_d * n) + +bool_t ecMulA2(word b[], const word a[], const ec_o* ec, + const word d[], size_t m, void* stack) +{ + size_t mb; + size_t snz_width; + size_t pre_count; + ec_pre_t* pre; /* [pre_count проективных точек] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); + ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(wwCmp(d, ec->order, m) < 0); + // размерности + mb = wwBitSize(ec->order, m); + snz_width = ecSNZWidth(mb); + pre_count = SIZE_1 << snz_width; + // разметить стек + memSlice(stack, + ecMulA2_local(ec->f->n, ec->d, pre_count), SIZE_0, SIZE_MAX, + &pre, &stack); + // предвычисления + ecPreSNZ(pre, a, snz_width, ec, stack); + // кратная точка по методу SNZ + return ecMulPreSNZ(b, pre, ec, d, m, stack); +} + size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t snz_width = ecSNZWidth(B_OF_W(m)); const size_t pre_count = SIZE_1 << snz_width; return memSliceSize( - ecMulA2_local(n, ec_d, m, pre_count), + ecMulA2_local(n, ec_d, pre_count), utilMax(2, - ecSmul_deep(n, ec_d, ec_deep), + ecPreSNZ_deep(n, ec_d, ec_deep), ecFinAdd_deep(n, ec_d, ec_deep)), SIZE_MAX); } @@ -554,7 +631,7 @@ size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) /* naf_size */ O_PER_S * k,\ /* naf_pos */ O_PER_S * k,\ /* naf */ sizeof(word*) * k,\ -/* pre */ sizeof(word*) * k +/* pre */ sizeof(ec_pre_t*) * k bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) { @@ -568,7 +645,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) size_t* naf_size; /* [k] длины NAF */ size_t* naf_pos; /* [k] позиция в NAF-представлении */ word** naf; /* [k] NAF */ - word** pre; /* [k] предвычисленные точки */ + ec_pre_t** pre; /* [k] предвычисленные точки */ // pre ASSERT(ecIsOperable(ec)); ASSERT(k > 0); @@ -584,7 +661,6 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) const word* a; const word* d; size_t pre_count; - size_t j; // a <- a[i] a = va_arg(args, const word*); // d <- d[i] @@ -597,7 +673,9 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) naf_width[i] = ecNAFWidth(B_OF_W(m[i])); pre_count = SIZE_1 << (naf_width[i] - 1); memSlice(stack, - O_OF_W(2 * m[i] + 1), O_OF_W(ec->d * ec->f->n * pre_count), SIZE_0, + O_OF_W(2 * m[i] + 1), + sizeof(ec_pre_t) + O_OF_W(pre_count + ec->d * ec->f->n), + SIZE_0, SIZE_MAX, naf + i, pre + i, &stack); // расчет naf[i] @@ -605,12 +683,8 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) if (naf_size[i] > naf_max_size) naf_max_size = naf_size[i]; naf_pos[i] = 0; - // малые кратные - ecSmul(pre[i], a, naf_width[i] - 1, ec, stack); - // отрицательные малые кратные - for (j = 0; j < pre_count / 2; ++j) - ec->neg(pre[i] + (pre_count / 2 + j) * ec->d * ec->f->n, - pre[i] + j * ec->d * ec->f->n, ec, stack); + // предвычисления + ecPreSNZ(pre[i], a, naf_width[i] - 1, ec, stack); } va_end(args); // t <- O @@ -633,13 +707,8 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) naf_hi = WORD_BIT_POS(naf_width[i] - 1); if (digit & 1) { - // t <- t + pre[i][digit / 2] - if (digit == 1 || digit == (naf_hi ^ 1)) - ecAddA(t, t, pre[i] + (digit / 2) * ec->d * ec->f->n, ec, - stack); - else - ecAdd(t, t, pre[i] + (digit / 2) * ec->d * ec->f->n, ec, - stack); + // t <- t + pre[i].pt[digit / 2] + ecAdd(t, t, ecPrePt(pre[i], digit / 2, ec), ec, stack); // к следующей цифре naf[i] naf_pos[i] += naf_width[i]; } @@ -660,7 +729,7 @@ size_t ecAddMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t k, ...) va_list args; ret = memSliceSize( ecAddMulA_local(n, ec_d, k), - ecSmul_deep(n, ec_d, ec_deep), + ecPreSNZ_deep(n, ec_d, ec_deep), SIZE_MAX); va_start(args, k); for (i = 0; i < k; ++i) @@ -670,7 +739,7 @@ size_t ecAddMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t k, ...) size_t pre_count = SIZE_1 << (naf_width - 1); ret += memSliceSize( O_OF_W(2 * m + 1), - O_OF_W(ec_d * n * pre_count), + sizeof(ec_pre_t) + O_OF_W(ec_d * n * pre_count), SIZE_0, SIZE_MAX); } va_end(args); diff --git a/src/math/ecp.c b/src/math/ecp.c index f1ecc6f3..156e3d73 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -1815,3 +1815,14 @@ size_t ecpSWU_deep(size_t n, size_t f_deep) qrPower_deep(n, n, f_deep)), SIZE_MAX); } + +/* +******************************************************************************* +Предвычисления +******************************************************************************* +*/ + +void ecpPreSNZ(word pre[], const word a[], size_t w, const struct ec_o* ec, + void* stack) +{ +} diff --git a/src/math/ww.c b/src/math/ww.c index ef325419..0cb28a5f 100644 --- a/src/math/ww.c +++ b/src/math/ww.c @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.01.13 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -297,7 +297,7 @@ size_t wwOctetSize(const word a[], size_t n) /* ******************************************************************************* -Операции с отдельными битами, кодирование +Операции с отдельными битами \remark В wwSetBit() использован трюк из работы Andersen S.A. Bit Twidding Hacks. Avail. at: @@ -411,6 +411,21 @@ size_t wwBitSize(const word a[], size_t n) return n * B_PER_W - wwHiZeroBits(a, n); } +/* +******************************************************************************* +NAF + +\remark Если старшие цифры NAF имеют вид + 1, 0, ..., 0, \alpha, .... +где w - 1 нулей и цифра \alpha < 0, то выполняется замена этих цифр на + 1, 0, ..., 0, \beta, .... +где w - 2 нулей и \beta = 2^{w - 1} + \alpha > 0. +Замена корректна, так как: + 2^w + \alpha = 2^{w - 1} + \beta. +Число цифр сокращается на единицу, NAF становится более экономичной. +******************************************************************************* +*/ + size_t wwNAF(word naf[], const word a[], size_t n, size_t w) { const word next_bit = WORD_BIT_POS(w); @@ -418,67 +433,56 @@ size_t wwNAF(word naf[], const word a[], size_t n, size_t w) const word mask = hi_bit - 1; register word window; register word digit; - register size_t naf_len; - register size_t naf_size; - register size_t a_len; + register size_t len; + register size_t size; + register size_t nb; size_t i; // pre ASSERT(wwIsDisjoint2(a, n, naf, 2 * n + 1)); ASSERT(2 <= w && w < B_PER_W); // naf <- 0 wwSetZero(naf, 2 * n + 1); - naf_len = naf_size = 0; + len = size = 0; // a == 0? - if (wwIsZero(a, n)) + nb = wwBitSize(a, n); + if (nb == 0) return 0; - // window <- a mod 2^w - window = wwGetBits(a, 0, w); // расчет NAF - a_len = wwBitSize(a, n); - for (i = w; window || i < a_len; ++i) + window = wwGetBits(a, 0, w); + for (i = w; window || i < nb; ++i) { - // ненулевой символ? + // ненулевая цифра? if (window & 1) { - // кодирование отрицательного символа + // отрицательная цифра? if (window & hi_bit) { - // модифицировать отрицательный символ суффикса NAF... - if (i >= a_len) - // ...сделать его положительным - digit = window & mask, - // window <- window - digit - window = hi_bit; + // среди старших цифр? + if (i >= nb) + // сделать цифру положительной + digit = window & mask, window = hi_bit; else - // digit <- |window| - digit = (0 - window) & mask, - // digit <- 1||digit - digit ^= hi_bit, - // window <- window - digit - window = next_bit; + digit = window, window = next_bit; } else - // кодирование положительного символа - digit = window, - // window <- window - digit - window = 0; - // запись ненулевого символа - wwShHi(naf, W_OF_B(naf_len + w), w); + digit = window, window = 0; + // записать цифру + wwShHi(naf, W_OF_B(len + w), w); wwSetBits(naf, 0, w, digit); - naf_len += w; + len += w; } else - // кодирование нулевого символа - wwShHi(naf, W_OF_B(++naf_len), 1); + // записать нулевую цифру + wwShHi(naf, W_OF_B(++len), 1); // увеличить размер naf - ++naf_size; + ++size; // сдвиг окна window >>= 1; - if (i < a_len) + if (i < nb) window += hi_bit * wwTestBit(a, i); } - CLEAN2(digit, window), CLEAN2(naf_len, a_len); - return naf_size; + CLEAN2(digit, window), CLEAN2(len, nb); + return size; } /* diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 6887bf86..9ed5fc44 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.22 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -306,10 +306,10 @@ bool_t ecpTest() } } // малые кратные - if (ec->smul) + if (FALSE) { // (pt0, pt1, pt2, pt3) <- (base, 3 base, 5 base, 7 base) - ec->smul(pts, ec->base, 3, ec, stack); + ecpPreSNZ(pts, ec->base, 3, ec, stack); // pt0 == base? ecToA(pt4, pt0, ec, stack); if (!wwEq(pt5, ec->base, 2 * n)) From 6dd8e92ae16b559ddc63b4bc17bd1b691e9b3e05 Mon Sep 17 00:00:00 2001 From: agievich Date: Wed, 28 Jan 2026 01:42:28 +0300 Subject: [PATCH 19/77] Start math/ec/SNZA --- include/bee2/math/ec.h | 87 +++++++++++++++++++ src/math/ec.c | 193 +++++++++++++++++++++++++++-------------- src/math/ec2.c | 11 ++- src/math/ecp.c | 11 ++- test/math/ecp_test.c | 2 +- test/math/ww_test.c | 6 +- 6 files changed, 237 insertions(+), 73 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 780b0b9a..b69ee034 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -206,6 +206,26 @@ typedef void (*ec_neg_i)( void* stack /*!< [in] вспомогательная память */ ); +/*! \brief Обратная аффинная точка + + На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]b, + обратная к аффинной точке [2 * ec->f->n]a: + \code + b <- -a. + \endcode + \pre Описание ec работоспособно. + \pre Буфер b либо не пересекается, либо совпадает с буфером a. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. +*/ +typedef void (*ec_nega_i)( + word b[], /*!< [out] обратная точка */ + const word a[], /*!< [in] обращаемая точка */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + /*! \brief Сложение точек На эллиптической кривой ec определяется сумма [ec->d * ec->f->n]c точек @@ -458,6 +478,7 @@ typedef struct ec_o ec_froma_i froma; /*!< функция импорта из аффинной точки */ ec_toa_i toa; /*!< функция экспорта в аффинную точку */ ec_neg_i neg; /*!< функция обращения */ + ec_neg_i nega; /*!< функция обращения аффинной точки */ ec_add_i add; /*!< функция сложения */ ec_adda_i adda; /*!< функция сложения с аффинной точкой */ ec_dbl_i dbl; /*!< функция удвоения */ @@ -505,6 +526,9 @@ typedef struct ec_o #define ecNeg(b, a, ec, stack)\ (ec)->neg(b, a, ec, stack) +#define ecNegA(b, a, ec, stack)\ + (ec)->nega(b, a, ec, stack) + #define ecAdd(c, a, b, ec, stack)\ (ec)->add(c, a, b, ec, stack) @@ -613,6 +637,69 @@ bool_t ecGroupIsOperable( const ec_o* ec /*!< [in] описание кривой */ ); +/* +******************************************************************************* +Предвычмсления +******************************************************************************* +*/ + +/*! \brief Контейнер с предвычисленными точками работоспособен? + + Проверяется работоспособность контейнера pre с предвычисленными точками. + Проверяются следующие условия: + - буфер [sizeof(ec_pre_t)]pre корректен; + - pre->type \in ec_pre_type; + - pre->w > 0; + - pre->h > 0 <=> pre->type == ec_pre_comb. + . + \return Признак корректности. +*/ +bool_t ecPreIsOperable( + const ec_pre_t* pre /*!< [in] предвычисленные точки */ +); + +/*! \brief Предвычисления по схеме SNZ + + На эллиптической кривой ec выполняются предвычисления по схеме SNZ + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w > 1. + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + случае. +*/ +void ecPreSNZ( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + const ec_o* ec, /*!< [in] описание кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +/*! \brief Предвычисления по схеме SNZA + + На эллиптической кривой ec выполняются предвычисления по схеме SNZA + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w > 1. + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + случае. +*/ +bool_t ecPreSNZA( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + const ec_o* ec, /*!< [in] описание кривой */ + void* stack /*!< [in] вспомогательная память */ +); + /* ******************************************************************************* Макрооперации с аффинными точками diff --git a/src/math/ec.c b/src/math/ec.c index c58d73cb..fdefa654 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -15,6 +15,7 @@ #include "bee2/core/util.h" #include "bee2/core/word.h" #include "bee2/math/ec.h" +#include "bee2/math/qr.h" #include "bee2/math/ww.h" #include "bee2/math/zz.h" @@ -24,18 +25,6 @@ ******************************************************************************* */ -static ecPreIsValid(const ec_pre_t* pre) -{ - return memIsValid(pre, sizeof(pre)) && - (pre->type == ec_pre_snz || pre->type == ec_pre_snza || - pre->type == ec_pre_comb) && - pre->w > 0 && pre->w < B_PER_W && - (pre->type == ec_pre_comb && pre->h > 0 || - pre->type != ec_pre_comb && pre->h == 0); -} - -#define ecPrePt(pre, pos, ec) ((pre)->pts + (pos) * (ec)->d * (ec)->f->n) - /* ******************************************************************************* Управление описанием кривой @@ -52,6 +41,7 @@ bool_t ecIsOperable2(const ec_o* ec) ec->froma != 0 && ec->toa != 0 && ec->neg != 0 && + ec->nega != 0 && ec->add != 0 && ec->adda != 0 && ec->dbl != 0 && @@ -117,27 +107,67 @@ bool_t ecGroupIsOperable(const ec_o* ec) ******************************************************************************* Предвычисления -Прямая реализация схемы SNZ: -- t <- 2a; +В функции ecPreSNZ() реализованы предвычисления по схеме SNZ. Используется +следуюший алгоритм: +- t <- 2a; // P <- 2A - pt[0] <- a; -- pt[i] <- t + pt[i - 1], i = 1, 2, ..., 2^(w-1) - 1; -- pt[2^w - 1 - i] = -pt[i], i = 0, 1, ..., 2^(w-1) - 1. +- pt[1] <- t + pt[0]; // P <- P + A +- pt[i] <- t + pt[i - 1], i = 2, 3, ..., 2^{w-1} - 1; // P <- P + P +- pt[2^w - 1 - i] = -pt[i], i = 0, 1, ..., 2^{w-1} - 1. -Сложение t + pt[0] выполняется по схеме P <- P + A. Остальные сложения -- по -схеме P <- P + P. Итоговая сложность: +Итоговая сложность: 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). -Здесь и далее: +В функции ecPreSNZA() реализованы предвычисления по схеме SNZA. Используется +тот же алгоритм, только операция P <- 2A в нем меняется на A <- 2A, а операции +P <- P + A и P <- P + P меняются на A <- A + A. + +Итоговая сложность: + 1(P <- 2A) + (2^{w-1} - 1)(A <- A + A). + +Перечень операций (здесь и далее): - (P <- 2A) -- время работы функции ec->dbla; - (P <- P + A) -- время работы функций ec->adda; - (P <- P + P) -- время работы функции ec->add. +- (A <- 2A) -- время работы каскада (ec->dbla, ec->toa)*; +- (A <- A + A) -- время работы каскада (ec->adda, ec->toa)*; +- (P <- 2P) -- время работы функции ec->dbl. +----------------------------------------------------- +* [или прямых вычислений в аффинных координатах] + +\remark Функция ecPreSNZ() и особенно ecPreSNZA() не оптимальны. Например, для +ускорения второй функции можно рассчитать малые кратные в проективных +координатах, а затем быстро перейти к аффинным координатам с помощью трюка +Монтгомери [Doc05; algorithm 11.15 -- simultaneous inversion, p. 209]: + U_1 <- Z_1 + for t = 2,..., T: U_t <- U_{t-1} Z_t + V <- U_T^{-1} + for t = T,..., 2: + Z_t^{-1} <- V U_{t-1} + V <- V Z_t + Z_1^{-1} <- V + +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. ******************************************************************************* */ +bool_t ecPreIsOperable(const ec_pre_t* pre) +{ + return memIsValid(pre, sizeof(pre)) && + (pre->type == ec_pre_snz || pre->type == ec_pre_snza || + pre->type == ec_pre_comb) && + pre->w > 0 && pre->w < B_PER_W && + (pre->type == ec_pre_comb && pre->h > 0 || + pre->type != ec_pre_comb && pre->h == 0); +} + +#define ecPrePt(pre, pos, ec) ((pre)->pts + (pos) * (ec)->d * (ec)->f->n) + #define ecPreSNZ_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -static void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, +void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ @@ -173,7 +203,7 @@ static void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, pre->w = w, pre->h = 0; } -static size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep) +size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( ecPreSNZ_local(n, ec_d), @@ -181,6 +211,58 @@ static size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } +#define ecPreSNZA_local(n, ec_d)\ +/* t1 */ O_OF_W(ec_d * n),\ +/* t2 */ O_OF_W(ec_d * n) + +bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, + void* stack) +{ + word* t1; /* [ec->d * ec->f->n] */ + word* t2; /* [ec->d * ec->f->n] */ + size_t i; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(w > 0); + ASSERT(memIsDisjoint2(a, O_OF_W(2 * ec->f->n), + pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * (SIZE_1 << w)))); + // разметить стек + memSlice(stack, + ecPreSNZA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t1, &t2, &stack); + // pt[0] <- a + wwCopy(ecPrePt(pre, 0, ec), a, 2 * ec->f->n); + // вычислить малые кратные + if (w > 1) + { + // t1 <- 2 a + ecDblA(t1, a, ec, stack); + // pt[i] <- t1 + pt[i - 1] + for (i = 1; i < SIZE_1 << (w - 1); ++i) + { + ecAddA(t2, t1, ecPrePt(pre, i - 1, ec), ec, stack); + if (!ecToA(ecPrePt(pre, i, ec), t2, ec, stack)) + return FALSE; + } + } + // pt[pre_count - i] <- -pt[i] + for (i = 0; i < SIZE_1 << (w - 1); ++i) + ecNegA(ecPrePt(pre, (SIZE_1 << w) - 1 - i, ec), ecPrePt(pre, i, ec), + ec, stack); + // заполнить остальные поля + pre->type = ec_pre_snza; + pre->w = w, pre->h = 0; + return TRUE; +} + +size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecPreSNZA_local(n, ec_d), + ec_deep, + SIZE_MAX); +} + /* ******************************************************************************* Финишные сложения @@ -251,26 +333,19 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) Имеются три стратегии: 1) w = 2 и малые кратные вообще не рассчитываются; -2) w > 2 и малые кратные рассчитываются в аффинных координатах; -3) w > 2 и малые кратные рассчитываются в проективных координатах. +2) w > 2 и малые кратные рассчитываются в аффинных координатах (схема SNZA); +3) w > 2 и малые кратные рассчитываются в проективных координатах (схема SNZ). Если малые кратные рассчитываются по схеме "последовательно складывать с удвоенной исходной точкой", то средняя общая сложность нахождения кратной точки (l = wwBitSize(d)): 1) c1(l) = l/3(P <- P + A); -2) c2(l, w) = 1(A <- 2A) + 1(P <- P + A) + - (2^{w-2} - 2)(A <- A + A) + l/(w + 1)(P <- P + A); -3) c3(l, w) = 1(P <- 2A) + 1(P <- P + A) + - (2^{w-2} - 2)(P <- P + P) + l/(w + 1)(P <- P + P), +2) c2(l, w) = 1(A <- 2A) + (2^{w-2} - 1)(A <- A + A) + + l/(w + 1)(P <- P + A); +3) c3(l, w) = 1(P <- 2A) + 1(P <- P + A) + (2^{w-2} - 2)(P <- P + P) + + l/(w + 1)(P <- P + P), без учета общего во всех стратегиях слагаемого l(P <- 2P). -Здесь -- (A <- 2A) -- время работы каскада (ec->dbla, ec->toa)*; -- (A <- A + A) -- время работы каскада (ec->adda, ec->toa)*; -- (P <- 2P) -- время работы функции ec->dbl. ------------------------------------------------------ -* [или прямых вычислений в аффинных координатах] - Реализована третья стратегия. Она является выигрышной для кривых над GF(p) в практических диапазонах размерностей при использовании наиболее эффективных (якобиевых) координат. @@ -278,23 +353,11 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) Длина окна w выбирается как решение следующей оптимизационной задачи: (2^{w - 2} - 2) + l / (w + 1) -> min. -Предвычисления выполняются с помощью функции ecSmul(). - -\todo Усилить вторую стратегию. Рассчитать малые кратные в проективных -координатах, а затем быстро перейти к аффинным координатам с помощью -трюка Монтгомери [Doc05; algorithm 11.15 -- simultaneous inversion, p. 209]: - U_1 <- Z_1 - for t = 2,..., T: U_t <- U_{t-1} Z_t - V <- U_T^{-1} - for t = T,..., 2: - Z_t^{-1} <- V U_{t-1} - V <- V Z_t - Z_1^{-1} <- V +В функции ecMulA() реализована композиция функций ecPreSNZ() (предвычисления) +и ecMulPreSNZ(). [HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004. -[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and - Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. ******************************************************************************* */ @@ -324,7 +387,7 @@ static bool_t ecMulPreNAF(word b[], const ec_pre_t* pre, const ec_o* ec, word* t; /* [ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); - ASSERT(ecPreIsValid(pre)); + ASSERT(ecPreIsOperable(pre)); ASSERT(pre->type == ec_pre_snz); // разметить стек memSlice(stack, @@ -414,9 +477,9 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) /* ******************************************************************************* -Кратная точка: SNZ (Signed Non-Zero) +Кратная точка: метод SNZ (Signed Non-Zero) -В функции ecMulA2() для определения кратной точки b = da при нечетной +В функции ecMulPreSNZ() для определения кратной точки b = da при нечетной кратности d используется ее представление в форме d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}. Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w - 1)} -- ненулевые (нечетные) цифры @@ -430,20 +493,22 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pt[i] <- (2i - 1)a, pt[-i] <- -pt[i]. // предвычисления + 1) pt[i] <- (2i - 1)a, pt[-i] <- -pt[i]. // предвычисления, схема SNZ 2. t <- p[d_{k-1} / 2]. 3. Для i = k - 2, ..., 1: - 1) t <- 2^w t; // ec_dbl_i - 1) t <- t + pt[d_i / 2]. // ec_add_i + 1) t <- 2^w t; // P <- 2P + 1) t <- t + pt[d_i / 2]. // P <- P + P 4. t <- 2^w t. -5. t <- t + pt[d_0]. // ec_finadd_i +5. t <- t + pt[d_0]. // A <- P + P 6. Возвратить t. -\remark сложение t + pt[d_0] вынесено за рамки основного цикла на шаге 3, +\remark Сложение t + pt[d_0] вынесено за рамки основного цикла на шаге 3, потому что при этом (и только при этом) сложении может произойти исключительная ситуация -- совпадение операндов t и pt[d_0] с переключением от сложения -к удвоению. Предполагается, что функция ec_finadd_i регулярна, и тогда сложение -и удвоение будут выполняться по одним и тем же формулам. +к удвоению. Завершающее сложение выполняется с помощью функции интерфейса +ec_finadd_i, если таковая указана в описании кривой. Предполагается, что функция +ec_finadd_i регулярна, и тогда сложение и удвоение будут выполняться по одним и +тем же формулам. [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory and fast elliptic scalar multiplications secure against side channel @@ -469,7 +534,7 @@ static size_t ecSNZWidth(size_t l) /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n)\ -static bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, +bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -485,8 +550,8 @@ static bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); - ASSERT(ecPreIsValid(pre)); - ASSERT(pre->w == ec_pre_snz); + ASSERT(ecPreIsOperable(pre)); + ASSERT(pre->type == ec_pre_snz); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); @@ -576,7 +641,7 @@ size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) return memSliceSize( ecMulA2_local(n, ec_d, pre_count), utilMax(2, - ecPreSNZ_deep(n, ec_d, ec_deep), + ecMulPreSNZ_deep(n, ec_d, ec_deep, m), ecFinAdd_deep(n, ec_d, ec_deep)), SIZE_MAX); } @@ -674,7 +739,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) pre_count = SIZE_1 << (naf_width[i] - 1); memSlice(stack, O_OF_W(2 * m[i] + 1), - sizeof(ec_pre_t) + O_OF_W(pre_count + ec->d * ec->f->n), + sizeof(ec_pre_t) + O_OF_W(pre_count * ec->d * ec->f->n), SIZE_0, SIZE_MAX, naf + i, pre + i, &stack); @@ -697,14 +762,12 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) // цикл по (a[i], naf[i]) for (i = 0; i < k; ++i) { - word naf_hi; // цифры naf[i] не начались? if (naf_size[i] < naf_max_size) continue; // прочитать очередную цифру naf[i] digit = wwGetBits(naf[i], naf_pos[i], naf_width[i]); // обработать цифру - naf_hi = WORD_BIT_POS(naf_width[i] - 1); if (digit & 1) { // t <- t + pre[i].pt[digit / 2] diff --git a/src/math/ec2.c b/src/math/ec2.c index a5d0191a..2b632911 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.26 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -217,6 +217,12 @@ static size_t ec2NegLD_deep(size_t n, size_t f_deep) SIZE_MAX); } +// [2n]b <- -[2n]a (A <- -A) +static void ec2NegALD(word b[], const word a[], const ec_o* ec, void* stack) +{ + ec2NegA(b, a, ec); +} + #define ec2DblLD_local(n)\ /* t1 */ O_OF_W(n),\ /* t2 */ O_OF_W(n) @@ -575,6 +581,7 @@ bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->froma = ec2FromALD; ec->toa = ec2ToALD; ec->neg = ec2NegLD; + ec->nega = ec2NegALD; ec->add = ec2AddLD; ec->adda = ec2AddALD; ec->dbl = ec2DblLD; @@ -805,7 +812,7 @@ void ec2NegA(word b[], const word a[], const ec_o* ec) { ASSERT(ecIsOperable(ec)); ASSERT(ec2SeemsOnA(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); + ASSERT(wwIsSameOrDisjoint(a, b, 2 * ec->f->n)); // b <- (xa, ya + xa) qrCopy(ecX(b), ecX(a), ec->f); gf2Add(ecY(b, ec->f->n), ecX(a), ecY(a, ec->f->n), ec->f); diff --git a/src/math/ecp.c b/src/math/ecp.c index 156e3d73..21864247 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.26 +\version 2026.01.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -199,6 +199,12 @@ static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) qrCopy(ecZ(b, ec->f->n), ecZ(a, ec->f->n), ec->f); } +// [2n]b <- -[2n]a (A <- -A) +static void ecpNegAJ(word b[], const word a[], const ec_o* ec, void* stack) +{ + ecpNegA(b, a, ec); +} + #define ecpDblJ_local(n)\ /* t1 */ O_OF_W(n),\ /* t2 */ O_OF_W(n) @@ -1300,6 +1306,7 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->froma = ecpFromAJ; ec->toa = ecpToAJ; ec->neg = ecpNegJ; + ec->nega = ecpNegAJ; ec->add = ecpAddJ; ec->adda = ecpAddAJ; ec->dbl = bA3 ? ecpDblJA3 : ecpDblJ; @@ -1587,7 +1594,7 @@ void ecpNegA(word b[], const word a[], const ec_o* ec) // pre ASSERT(ecIsOperable(ec)); ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 3 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 2 * n)); // (xb, yb) <- (xa, -ya) qrCopy(ecX(b), ecX(a), ec->f); zmNeg(ecY(b, n), ecY(a, n), ec->f); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 9ed5fc44..18d566db 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -305,7 +305,7 @@ bool_t ecpTest() return FALSE; } } - // малые кратные + // предвычисления по схеме SNZ if (FALSE) { // (pt0, pt1, pt2, pt3) <- (base, 3 base, 5 base, 7 base) diff --git a/test/math/ww_test.c b/test/math/ww_test.c index 9186189f..950da459 100644 --- a/test/math/ww_test.c +++ b/test/math/ww_test.c @@ -4,7 +4,7 @@ \brief Tests for operations on arbitrary length words \project bee2/test \created 2023.03.31 -\version 2025.07.24 +\version 2025.07.27 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -108,8 +108,8 @@ bool_t wwTest() // NAF a[0] = 255; if (wwNAF(b, a, 1, 2) != 9 || b[0] != 0x0601 || - wwNAF(b, a, 1, 3) != 9 || b[0] != 0x1401 || - wwNAF(b, a, 1, 4) != 9 || b[0] != 0x4801) + wwNAF(b, a, 1, 3) != 9 || b[0] != 0x1C01 || + wwNAF(b, a, 1, 4) != 9 || b[0] != 0x7801) return FALSE; // все нормально return TRUE; From a91a4bb567c7cfeea6b6f843e63dc6f8afd7888b Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 28 Jan 2026 20:09:39 +0300 Subject: [PATCH 20/77] Finish ecMulPreSNZ() --- include/bee2/math/ec.h | 135 +++++++++++++++++++++++++++++-------- src/math/ec.c | 148 ++++++++++++++++++++++++++++++++--------- test/math/ecp_bench.c | 101 ++++++++++++++++++++++------ test/math/ecp_test.c | 11 ++- 4 files changed, 310 insertions(+), 85 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index b69ee034..28d74cf2 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.27 +\version 2026.01.28 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -136,7 +136,7 @@ ecCreate() и сохраняется в поле deep описания крив В функцию ecCreate() могут не передаваться параметры base, order и cofactor, определяющие строение группы точек, хотя буферы для base и order могут готовиться в ecCreate(). Параметры могут устанавливаться позднее с помощью -функции ecGroupCreate(). +ecGroupCreate() или подобных функций. \pre Все указатели действительны. @@ -422,7 +422,8 @@ typedef bool_t(*ec_finadda_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Схема предвычислений */ +/*! \brief Схема предвычислений +*/ typedef enum { ec_pre_snz = 1, /*!< схема SNZ */ @@ -441,10 +442,25 @@ typedef enum - SNZA: вычисляются аффинные точки a, 3a, ..., (2^w-1)a, -(2^w-1)a, ..., -3a, -a; - Comb: вычисляются аффинные точки - \pm a + \pm h a + \pm h^2 a + ... + \pm h^{w-1} a. + [b_{w-1} ... b_1 b_0]a = b_{w-1} h^{w-1} a + ... + b_1 h a + b_0 a, + где b_i in {-1, 1}. Точка [b_{w-1} ... b_1 b_0]a размещаются + в массиве pts по индексу, двоичная запись которого есть + с_{w-1}...c_1 c_0, + где c_i = 1, если b_i = 1, и c_i = 0, если b_i = -1. . \pre 0 < w && w < B_PER_W. \pre h > 0 <=> type == ec_pre_comb. + \remark Схему SNZ было предложено в использовать в работе [OkeTak03], + схему Comb -- в работе [HPB05]. + . + [HPB05] Hedabou M., Pinel P., Beneteau L. Countermeasures for preventing + comb method against SCA attacks. In: International Conference + on Information Security Practice and Experience, 2005, pp. 85-96, + Springer, Berlin Heidelberg. + [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory + and fast elliptic scalar multiplications secure against side + channel attacks. In Cryptographers’ Track at the RSA Conference, + 2003, pp. 328-343. Springer, Berlin Heidelberg. */ typedef struct ec_pre_t { @@ -454,7 +470,6 @@ typedef struct ec_pre_t word pts[]; /*!< точки */ } ec_pre_t; - /*! \brief Описание эллиптической кривой Описывается эллиптическая кривая, правила представления ее элементов, @@ -541,12 +556,23 @@ typedef struct ec_o #define ecDblA(b, a, ec, stack)\ (ec)->dbla(b, a, ec, stack) +#define ecDblAddA(c, a, b, ec, stack)\ + ((ec)->dbladda ?\ + (ec)->dbladda(c, a, b, ec, stack) :\ + (ecDbl(c, a, ec, stack), ecAddA(c, c, b, ec, stack))) + #define ecSetO(a, ec)\ wwSetZero(ecZ(a, (ec)->f->n), (ec)->f->n) #define ecIsO(a, ec)\ wwIsZero(ecZ(a, (ec)->f->n), (ec)->f->n) +#define ecPrePt(pre, pos, ec)\ + ((pre)->pts + (pos) * (ec)->d * (ec)->f->n) + +#define ecPrePtA(pre, pos, ec)\ + ((pre)->pts + (pos) * 2 * (ec)->f->n) + /* ******************************************************************************* Управление описанием кривой @@ -662,14 +688,13 @@ bool_t ecPreIsOperable( На эллиптической кривой ec выполняются предвычисления по схеме SNZ с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется - в контейнере pre. + в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. \pre w > 1. \expect Описание ec корректно. \expect Точка a лежит на ec. - \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном - случае. + \deep{stack} ecPreSNZ_deep(ec->f->n, ec->d, ec->deep). */ void ecPreSNZ( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -679,6 +704,8 @@ void ecPreSNZ( void* stack /*!< [in] вспомогательная память */ ); +size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep); + /*! \brief Предвычисления по схеме SNZA На эллиптической кривой ec выполняются предвычисления по схеме SNZA @@ -689,8 +716,11 @@ void ecPreSNZ( \pre w > 1. \expect Описание ec корректно. \expect Точка a лежит на ec. - \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном случае. + \remark При обнаружении точки O предвычисления прерываются с неопределенным + результатом в pre->pts. + \deep{stack} ecPreSNZA_deep(ec->f->n, ec->d, ec->deep). */ bool_t ecPreSNZA( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -700,6 +730,8 @@ bool_t ecPreSNZA( void* stack /*!< [in] вспомогательная память */ ); +size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep); + /* ******************************************************************************* Макрооперации с аффинными точками @@ -733,43 +765,88 @@ bool_t ecMulA( size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с регуляризацией +/*! \brief Кратная точка с предвычислениями по схеме SNZ Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, - которая является [m]d-кратной аффинной точки [2 * ec->f->n]a: + которая является [m]d-кратной точки a, по которой построен контейнер pre + с предвычисленными по схеме SNZ точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. + \pre Контейнер pre работоспособен. + \pre pre->type == ec_pre_snz. \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. - \expect Точка a лежит на ec. + \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulA2_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - - ec->pmaadd != 0 и функция ec->pmaadd регулярна: сложение и удвоение - выполняются по одним и тем же формулам без условных переходов; - - функция ec->dbl регулярна: удвоение точек, отличных от O, выполняется - без условных переходов; - - функция ec->add (ec->dbladd == 0) или ec->dbladd (ec->dbladd != 0) - регулярна: сложение различных и не взаимно обратных точек выполняется - без условных переходов. -*/ -bool_t ecMulA2( - word b[], /*!< [out] кратная точка */ - const word a[], /*!< [in] базовая точка */ - const ec_o* ec, /*!< [in] описание кривой */ - const word d[], /*!< [in] кратность */ - size_t m, /*!< [in] длина d в машинных словах */ - void* stack /*!< [in] вспомогательная память */ + - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без + условных переходов; + - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, + выполняется без условных переходов; + - ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение + выполняются по одним и тем же формулам без условных переходов. +*/ +bool_t ecMulPreSNZ( + word b[], /*!< [out] кратная точка */ + const ec_pre_t* pre, /*!< [in] предвычисленные точки */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + +/*! \brief Кратная точка с предвычислениями по схеме SNZA + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [m]d-кратной точки a, по которой построен контейнер pre + с предвычисленными по схеме SNZA точками: + \code + b <- d a. + \endcode + \pre Описание ec работоспособно. + \pre Описание группы точек в ec работоспособно. + \pre Контейнер pre работоспособен. + \pre pre->type == ec_pre_snza. + \pre Длина ec->order в машинных словах равняется m. + \pre m > 1. + \pre d < ec->order. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точки контейнера pre корректно рассчитаны по a. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция регулярна по [m]d при выполнении следующих условий: + - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без + условных переходов; + - функция ec->adda регулярна: сложение a + b, a != \pm b, a != O, b != O, + выполняется без условных переходов; + - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление + 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных + переходов; + - ec->finadda != 0 и функция ec->finadda регулярна: сложение и удвоение + выполняются по одним и тем же формулам без условных переходов. +*/ +bool_t ecMulPreSNZA( + word b[], /*!< [out] кратная точка */ + const ec_pre_t* pre, /*!< [in] предвычисленные точки */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); /*! \brief Имеет порядок? diff --git a/src/math/ec.c b/src/math/ec.c index fdefa654..9198f7f4 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.27 +\version 2026.01.28 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -135,10 +135,11 @@ P <- P + A и P <- P + P меняются на A <- A + A. ----------------------------------------------------- * [или прямых вычислений в аффинных координатах] -\remark Функция ecPreSNZ() и особенно ecPreSNZA() не оптимальны. Например, для +\remark Функция ecPreSNZ() и особенно ecPreSNZA() не оптимальны. Для ускорения второй функции можно рассчитать малые кратные в проективных -координатах, а затем быстро перейти к аффинным координатам с помощью трюка -Монтгомери [Doc05; algorithm 11.15 -- simultaneous inversion, p. 209]: +координатах, а затем быстро перейти к аффинным координатам с помощью +одновременного обращения сразу нескольких элементов поля. Одновременное +обращение известно как трюк Монтгомери [Doc05; algorithm 11.15, p. 209]: U_1 <- Z_1 for t = 2,..., T: U_t <- U_{t-1} Z_t V <- U_T^{-1} @@ -146,6 +147,8 @@ P <- P + A и P <- P + P меняются на A <- A + A. Z_t^{-1} <- V U_{t-1} V <- V Z_t Z_1^{-1} <- V +Трюк не применяется, поскольку на данном уровне описания кривой структура +проективных координат не определена. [Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. @@ -162,8 +165,6 @@ bool_t ecPreIsOperable(const ec_pre_t* pre) pre->type != ec_pre_comb && pre->h == 0); } -#define ecPrePt(pre, pos, ec) ((pre)->pts + (pos) * (ec)->d * (ec)->f->n) - #define ecPreSNZ_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) @@ -175,8 +176,9 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // pre ASSERT(ecIsOperable(ec)); ASSERT(w > 0); - ASSERT(memIsDisjoint2(a, O_OF_W(2 * ec->f->n), - pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << w)))); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << w)), + a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, ecPreSNZ_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, @@ -224,14 +226,15 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // pre ASSERT(ecIsOperable(ec)); ASSERT(w > 0); - ASSERT(memIsDisjoint2(a, O_OF_W(2 * ec->f->n), - pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * (SIZE_1 << w)))); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * (SIZE_1 << w)), + a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, ecPreSNZA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t1, &t2, &stack); // pt[0] <- a - wwCopy(ecPrePt(pre, 0, ec), a, 2 * ec->f->n); + wwCopy(ecPrePtA(pre, 0, ec), a, 2 * ec->f->n); // вычислить малые кратные if (w > 1) { @@ -240,8 +243,8 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // pt[i] <- t1 + pt[i - 1] for (i = 1; i < SIZE_1 << (w - 1); ++i) { - ecAddA(t2, t1, ecPrePt(pre, i - 1, ec), ec, stack); - if (!ecToA(ecPrePt(pre, i, ec), t2, ec, stack)) + ecAddA(t2, t1, ecPrePtA(pre, i - 1, ec), ec, stack); + if (!ecToA(ecPrePtA(pre, i, ec), t2, ec, stack)) return FALSE; } } @@ -328,7 +331,7 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) В функции ecMulPreNAF() для определения b = da (d-кратного точки a) применяется оконный NAF с длиной окна w >= 2. Реализован алгоритм 3.36 из [HMV04]. -Используются малые нечетные кратные a вида: +Используются малые нечетные кратные a вида \pm (2i + 1)a, i = 0, 1, ..., 2^{w-2} - 1. Имеются три стратегии: @@ -354,7 +357,7 @@ static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) (2^{w - 2} - 2) + l / (w + 1) -> min. В функции ecMulA() реализована композиция функций ecPreSNZ() (предвычисления) -и ecMulPreSNZ(). +и ecMulPreNAF(). [HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004. @@ -477,7 +480,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) /* ******************************************************************************* -Кратная точка: метод SNZ (Signed Non-Zero) +Кратная точка: метод SNZ В функции ecMulPreSNZ() для определения кратной точки b = da при нечетной кратности d используется ее представление в форме @@ -493,7 +496,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pt[i] <- (2i - 1)a, pt[-i] <- -pt[i]. // предвычисления, схема SNZ + 1) pt[i] <- (2i + 1)a, pt[-i] <- -pt[i]. // предвычисления, схема SNZ 2. t <- p[d_{k-1} / 2]. 3. Для i = k - 2, ..., 1: 1) t <- 2^w t; // P <- 2P @@ -506,14 +509,14 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) потому что при этом (и только при этом) сложении может произойти исключительная ситуация -- совпадение операндов t и pt[d_0] с переключением от сложения к удвоению. Завершающее сложение выполняется с помощью функции интерфейса -ec_finadd_i, если таковая указана в описании кривой. Предполагается, что функция -ec_finadd_i регулярна, и тогда сложение и удвоение будут выполняться по одним и -тем же формулам. - -[OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory and - fast elliptic scalar multiplications secure against side channel - attacks. In Cryptographers’ Track at the RSA Conference, 2003, - pp. 328-343. Springer Berlin Heidelberg. +ec_finadd_i, если таковая указана в описании кривой. Предполагается, что +функция ec_finadd_i регулярна, и тогда сложение и удвоение будут выполняться +по одним и тем же формулам. + +В функции ecMulPreSNZA() предварительно рассчитанные точки являются аффинными, +а не проективными. Это позволяет заменить регулярные сложения P <- P + P +на P <- P + A и финишное сложение A <- P + P на A <- P + A. + [BCL+14] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves for Cryptography: An Efficiency and Security Analysis, 2014, https://eprint.iacr.org/2014/130.pdf. @@ -523,13 +526,6 @@ ec_finadd_i регулярна, и тогда сложение и удвоени ******************************************************************************* */ -static size_t ecSNZWidth(size_t l) -{ - if (l <= 256) - return 5; - return 6; -} - #define ecMulPreSNZ_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n)\ @@ -604,6 +600,94 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } +#define ecMulPreSNZA_local(n, ec_d, m)\ +/* dd */ O_OF_W(m),\ +/* t */ O_OF_W(ec_d * n)\ + +bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, + const word d[], size_t m, void* stack) +{ + register word neg; + register word digit; + register word hi; + register bool_t ret; + size_t mb; + size_t snz_width; + size_t k; + size_t i; + word* dd; /* [m] */ + word* t; /* [ec->d * ec->f->n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(ecPreIsOperable(pre)); + ASSERT(pre->type == ec_pre_snza); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); + ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(wwCmp(d, ec->order, m) < 0); + // размерности + mb = wwBitSize(ec->order, m); + snz_width = pre->w; + // разметить стек + memSlice(stack, + ecMulPreSNZA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &dd, &t, &stack); + // dd <- (d & 1) ? d : ec->order - d + neg = WORD_1 - (d[0] & 1); + zzSubIf(dd, ec->order, d, m, neg); + ASSERT(zzIsOdd(dd, m)); + // число цифр SNZ + k = (mb + snz_width - 1) / snz_width; + ASSERT(k > 1); + // старшая цифра + --k; + digit = wwGetBits(dd, k * snz_width, mb - k * snz_width); + wwCopy(t, ecPrePtA(pre, digit >> 1, ec), ec->d * ec->f->n); + hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + // обработать остальные цифры + while (--k) + { + for (i = snz_width; --i;) + ecDbl(t, t, ec, stack); + digit = wwGetBits(dd, k * snz_width, snz_width); + ecDblAddA(t, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, stack); + hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + } + // завершающие удвоения и финишное сложение + for (i = snz_width; i--;) + ecDbl(t, t, ec, stack); + digit = wwGetBits(dd, 0, snz_width); + ASSERT(digit & 1); + ret = (ec->finadda) ? + ec->finadda(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), neg, ec, + stack) : + ecFinAddA(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), neg, ec, stack); + // очистка и возврат + CLEAN3(neg, digit, hi); + return ret; +} + +size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreSNZA_local(n, ec_d, m), + ecFinAddA_deep(n, ec_d, ec_deep), + SIZE_MAX); +} + +/* +******************************************************************************* +На удаление +******************************************************************************* +*/ + +static size_t ecSNZWidth(size_t l) +{ + if (l <= 256) + return 5; + return 6; +} + #define ecMulA2_local(n, ec_d, pre_count)\ /* pre */ sizeof(ec_pre_t) + O_OF_W(pre_count * ec_d * n) diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index a9b93ea8..d428019a 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.01.18 +\version 2026.01.28 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -32,6 +32,7 @@ bool_t ecpBench() octet* combo_state; /* [prngCOMBO_keep()] */ word* pt; /* [2 * n] */ word* d; /* [n] */ + ec_pre_t* pre; /* [64 якобиевы точки] */ void* stack; // загрузить параметры if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || @@ -44,11 +45,15 @@ bool_t ecpBench() prngCOMBO_keep(), O_OF_W(2 * ec->f->n), O_OF_W(ec->f->n), - utilMax(2, + sizeof(ec_pre_t) + O_OF_W(64 * 3 * ec->f->n), + utilMax(5, ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), - ecMulA2_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), + ecPreSNZ_deep(ec->f->n, ec->d, ec->deep), + ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, ec->f->n), + ecPreSNZA_deep(ec->f->n, ec->d, ec->deep), + ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), SIZE_MAX, - &combo_state, &pt, &d, &stack); + &combo_state, &pt, &d, &pre, &stack); if (state == 0) { bignEcClose(ec); @@ -57,30 +62,84 @@ bool_t ecpBench() // создать генератор COMBO prngCOMBOStart(combo_state, utilNonce32()); // оценить число кратных точек в секунду + printf("ecpBench::\n"); { - const size_t reps = 500; + const size_t reps = 200; + size_t w; size_t i; tm_ticks_t ticks; - // эксперимент: ecMulA() - for (i = 0, ticks = tmTicks(); i < reps; ++i) + // ecMulA { - prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulA(pt, ec->base, ec, d, ec->f->n, stack); + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecMulA(pt, ec->base, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); } - ticks = tmTicks() - ticks; - printf("ecpBench::ecMulA: %u cycles/mulpoint [%u mulpoints/sec]\n", - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); - // эксперимент: ecMulA2() - for (i = 0, ticks = tmTicks(); i < reps; ++i) + // ecPre+MulPre[SNZ] + for (w = 4; w <= 6; ++w) { - prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulA2(pt, ec->base, ec, d, ec->f->n, stack); + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecPreSNZ(pre, ec->base, w, ec, stack); + ecMulPreSNZ(pt, pre, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecPre+ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } + // ecPre+MulPre[SNZA] + for (w = 4; w <= 6; ++w) + { + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecPreSNZA(pre, ec->base, w, ec, stack); + ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } + // ecMulPre[SNZ] + for (w = 4; w <= 6; ++w) + { + ecPreSNZ(pre, ec->base, w, ec, stack); + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecMulPreSNZ(pt, pre, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } + // ecMulPre[SNZA] + for (w = 4; w <= 6; ++w) + { + ecPreSNZA(pre, ec->base, w, ec, stack); + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); } - ticks = tmTicks() - ticks; - printf("ecpBench::ecMulA2: %u cycles/mulpoint [%u mulpoints/sec]\n", - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); } // завершение blobClose(state); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 18d566db..93499284 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.27 +\version 2026.01.28 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -76,7 +76,7 @@ bool_t ecpTest() 3 * no, O_OF_W(6 * 3 * n) | SIZE_HI, O_OF_W(n), - utilMax(13, + utilMax(16, gfpCreate_deep(no), ecpCreateJ_deep(n, f_deep), ecGroupCreate_deep(f_deep), @@ -89,7 +89,10 @@ bool_t ecpTest() ecpAddAA_deep(n, f_deep), ecpSubAA_deep(n, f_deep), ecMulA_deep(n, 3, ec_deep, n), - ecMulA2_deep(n, 3, ec_deep, n)), + ecPreSNZ_deep(n, 3, ec_deep), + ecMulPreSNZ_deep(n, 3, ec_deep, n), + ecPreSNZA_deep(n, 3, ec_deep), + ecMulPreSNZA_deep(n, 3, ec_deep, n)), SIZE_MAX, &ec, &f, &t, &pts, &d, &stack); if (state == 0) @@ -157,6 +160,7 @@ bool_t ecpTest() } } // ecMulA() vs ecMulA2() +/* { wwCopy(d, ec->order, n); zzSubW2(d, n, 1); @@ -172,6 +176,7 @@ bool_t ecpTest() ecMulA2(pt1, ec->base, ec, d, n, stack)) return FALSE; } +*/ // финишное сложение if (ec->finadd) { From 84e4adfd8af051e511ab4523d7bba89f73f3e537 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 29 Jan 2026 20:35:15 +0300 Subject: [PATCH 21/77] Update test/ecp --- include/bee2/math/ec.h | 24 +++-- src/math/ec.c | 102 +++++-------------- test/math/ecp_bench.c | 8 +- test/math/ecp_test.c | 219 +++++++++++++++++++++++++++++++---------- 4 files changed, 211 insertions(+), 142 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 28d74cf2..413d11ed 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.28 +\version 2026.01.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -567,11 +567,17 @@ typedef struct ec_o #define ecIsO(a, ec)\ wwIsZero(ecZ(a, (ec)->f->n), (ec)->f->n) +#define ecPt(pts, pos, ec)\ + ((pts) + (pos) * (ec)->d * (ec)->f->n) + #define ecPrePt(pre, pos, ec)\ - ((pre)->pts + (pos) * (ec)->d * (ec)->f->n) + ecPt(pre->pts, pos, ec) + +#define ecPtA(pts, pos, ec)\ + ((pts) + (pos) * 2 * (ec)->f->n) #define ecPrePtA(pre, pos, ec)\ - ((pre)->pts + (pos) * 2 * (ec)->f->n) + ecPtA(pre->pts, pos, ec) /* ******************************************************************************* @@ -791,8 +797,10 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); условных переходов; - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, выполняется без условных переходов; - - ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение - выполняются по одним и тем же формулам без условных переходов. + - справедливо, по крайней мере, одно из условий: + a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; + b) ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение + выполняются по одним и тем же формулам без условных переходов. */ bool_t ecMulPreSNZ( word b[], /*!< [out] кратная точка */ @@ -834,8 +842,10 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных переходов; - - ec->finadda != 0 и функция ec->finadda регулярна: сложение и удвоение - выполняются по одним и тем же формулам без условных переходов. + - справедливо, по крайней мере, одно из условий: + a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; + b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и + удвоение выполняются по одним и тем же формулам без условных переходов. */ bool_t ecMulPreSNZA( word b[], /*!< [out] кратная точка */ diff --git a/src/math/ec.c b/src/math/ec.c index 9198f7f4..d96262ec 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.28 +\version 2026.01.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -139,7 +139,8 @@ P <- P + A и P <- P + P меняются на A <- A + A. ускорения второй функции можно рассчитать малые кратные в проективных координатах, а затем быстро перейти к аффинным координатам с помощью одновременного обращения сразу нескольких элементов поля. Одновременное -обращение известно как трюк Монтгомери [Doc05; algorithm 11.15, p. 209]: +обращение известно как трюк Монтгомери [Mon87] (см. также +[Doc05; algorithm 11.15, p. 209]): U_1 <- Z_1 for t = 2,..., T: U_t <- U_{t-1} Z_t V <- U_T^{-1} @@ -150,6 +151,8 @@ P <- P + A и P <- P + P меняются на A <- A + A. Трюк не применяется, поскольку на данном уровне описания кривой структура проективных координат не определена. +[Mon87] Montogomery P, Speeding the Pollard and elliptic curve method of + factorization. Mathematics of Computation, 48 (177), 1987, 243--264. [Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. ******************************************************************************* @@ -250,7 +253,7 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, } // pt[pre_count - i] <- -pt[i] for (i = 0; i < SIZE_1 << (w - 1); ++i) - ecNegA(ecPrePt(pre, (SIZE_1 << w) - 1 - i, ec), ecPrePt(pre, i, ec), + ecNegA(ecPrePtA(pre, (SIZE_1 << w) - 1 - i, ec), ecPrePtA(pre, i, ec), ec, stack); // заполнить остальные поля pre->type = ec_pre_snza; @@ -538,7 +541,6 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, register word hi; register bool_t ret; size_t mb; - size_t snz_width; size_t k; size_t i; word* dd; /* [m] */ @@ -553,7 +555,6 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); - snz_width = pre->w; // разметить стек memSlice(stack, ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, @@ -563,26 +564,26 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); // число цифр SNZ - k = (mb + snz_width - 1) / snz_width; + k = (mb + pre->w - 1) / pre->w; ASSERT(k > 1); // старшая цифра --k; - digit = wwGetBits(dd, k * snz_width, mb - k * snz_width); + digit = wwGetBits(dd, k * pre->w, mb - k * pre->w); wwCopy(t, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); - hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; // обработать остальные цифры while (--k) { - for (i = snz_width; i--;) + for (i = pre->w; i--;) ecDbl(t, t, ec, stack); - digit = wwGetBits(dd, k * snz_width, snz_width); + digit = wwGetBits(dd, k * pre->w, pre->w); ecAdd(t, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); - hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; } // завершающие удвоения и финишное сложение - for (i = snz_width; i--;) + for (i = pre->w; i--;) ecDbl(t, t, ec, stack); - digit = wwGetBits(dd, 0, snz_width); + digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); ret = (ec->finadd) ? ec->finadd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), neg, ec, stack) : @@ -612,7 +613,6 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, register word hi; register bool_t ret; size_t mb; - size_t snz_width; size_t k; size_t i; word* dd; /* [m] */ @@ -627,7 +627,6 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); - snz_width = pre->w; // разметить стек memSlice(stack, ecMulPreSNZA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, @@ -637,26 +636,26 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); // число цифр SNZ - k = (mb + snz_width - 1) / snz_width; + k = (mb + pre->w - 1) / pre->w; ASSERT(k > 1); // старшая цифра --k; - digit = wwGetBits(dd, k * snz_width, mb - k * snz_width); - wwCopy(t, ecPrePtA(pre, digit >> 1, ec), ec->d * ec->f->n); - hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + digit = wwGetBits(dd, k * pre->w, mb - k * pre->w); + ecFromA(t, ecPrePtA(pre, digit >> 1, ec), ec, stack); + hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; // обработать остальные цифры while (--k) { - for (i = snz_width; --i;) + for (i = pre->w; --i;) ecDbl(t, t, ec, stack); - digit = wwGetBits(dd, k * snz_width, snz_width); + digit = wwGetBits(dd, k * pre->w, pre->w); ecDblAddA(t, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, stack); - hi = WORD_1 - (digit & 1), hi <<= snz_width - 1; + hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; } // завершающие удвоения и финишное сложение - for (i = snz_width; i--;) + for (i = pre->w; i--;) ecDbl(t, t, ec, stack); - digit = wwGetBits(dd, 0, snz_width); + digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); ret = (ec->finadda) ? ec->finadda(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), neg, ec, @@ -675,61 +674,6 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } -/* -******************************************************************************* -На удаление -******************************************************************************* -*/ - -static size_t ecSNZWidth(size_t l) -{ - if (l <= 256) - return 5; - return 6; -} - -#define ecMulA2_local(n, ec_d, pre_count)\ -/* pre */ sizeof(ec_pre_t) + O_OF_W(pre_count * ec_d * n) - -bool_t ecMulA2(word b[], const word a[], const ec_o* ec, - const word d[], size_t m, void* stack) -{ - size_t mb; - size_t snz_width; - size_t pre_count; - ec_pre_t* pre; /* [pre_count проективных точек] */ - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(ecGroupIsOperable(ec)); - ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); - ASSERT(zzIsOdd(ec->order, m) && m > 1); - ASSERT(wwCmp(d, ec->order, m) < 0); - // размерности - mb = wwBitSize(ec->order, m); - snz_width = ecSNZWidth(mb); - pre_count = SIZE_1 << snz_width; - // разметить стек - memSlice(stack, - ecMulA2_local(ec->f->n, ec->d, pre_count), SIZE_0, SIZE_MAX, - &pre, &stack); - // предвычисления - ecPreSNZ(pre, a, snz_width, ec, stack); - // кратная точка по методу SNZ - return ecMulPreSNZ(b, pre, ec, d, m, stack); -} - -size_t ecMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) -{ - const size_t snz_width = ecSNZWidth(B_OF_W(m)); - const size_t pre_count = SIZE_1 << snz_width; - return memSliceSize( - ecMulA2_local(n, ec_d, pre_count), - utilMax(2, - ecMulPreSNZ_deep(n, ec_d, ec_deep, m), - ecFinAdd_deep(n, ec_d, ec_deep)), - SIZE_MAX); -} - /* ******************************************************************************* Имеет порядок? diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index d428019a..b784e70d 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.01.28 +\version 2026.01.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -81,7 +81,7 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecPre+MulPre[SNZ] - for (w = 4; w <= 6; ++w) + for (w = 3; w <= 6; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { @@ -111,7 +111,7 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecMulPre[SNZ] - for (w = 4; w <= 6; ++w) + for (w = 3; w <= 6; ++w) { ecPreSNZ(pre, ec->base, w, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) @@ -126,7 +126,7 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecMulPre[SNZA] - for (w = 4; w <= 6; ++w) + for (w = 3; w <= 6; ++w) { ecPreSNZA(pre, ec->base, w, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 93499284..f1c3538a 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.28 +\version 2026.01.29 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -60,23 +60,29 @@ bool_t ecpTest() ec_o* ec; /* [ec_keep] */ qr_o* f; /* [f_keep] */ octet* t; /* [3 * no] */ - word* pts; /* [6 * 3 * n] */ - word* pt0; /* [3 * n] */ - word* pt1; /* [3 * n] */ - word* pt2; /* [3 * n] */ - word* pt3; /* [3 * n] */ - word* pt4; /* [3 * n] */ - word* pt5; /* [3 * n] */ - word* d; /* [n] */ + ec_pre_t* pre; /* [64 якобиевы точки] */ + word* pt0; /* [3 * n] */ + word* pt1; /* [3 * n] */ + word* pt2; /* [3 * n] */ + word* pt3; /* [3 * n] */ + word* pt4; /* [3 * n] */ + word* pt5; /* [3 * n] */ + word* d; /* [n + 1] */ void* stack; // создать состояние state = blobCreate2( ec_keep, f_keep, 3 * no, - O_OF_W(6 * 3 * n) | SIZE_HI, - O_OF_W(n), - utilMax(16, + (sizeof(ec_pre_t) + O_OF_W(64 * 3 * n)) | SIZE_HI, + O_OF_W(3 * n), + O_OF_W(3 * n), + O_OF_W(3 * n), + O_OF_W(3 * n), + O_OF_W(3 * n), + O_OF_W(3 * n), + O_OF_W(n + 1), + utilMax(17, gfpCreate_deep(no), ecpCreateJ_deep(n, f_deep), ecGroupCreate_deep(f_deep), @@ -92,17 +98,19 @@ bool_t ecpTest() ecPreSNZ_deep(n, 3, ec_deep), ecMulPreSNZ_deep(n, 3, ec_deep, n), ecPreSNZA_deep(n, 3, ec_deep), - ecMulPreSNZA_deep(n, 3, ec_deep, n)), + ecMulPreSNZA_deep(n, 3, ec_deep, n), + ecAddMulA_deep(n, 3, ec_deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, - &ec, &f, &t, &pts, &d, &stack); + &ec, &f, &t, &pre, &pt0, &pt1, &pt2, &pt3, &pt4, &pt5, &d, &stack); if (state == 0) return FALSE; - pt0 = pts, pt1 = pt0 + 3 * n, pt2 = pt1 + 3 * n, pt3 = pt2 + 3 * n, - pt4 = pt3 + 3 * n, pt5 = pt4 + 3 * n; // создать f = GF(p) hexToRev(t, p); if (!gfpCreate(f, t, no, stack)) + { + blobClose(state); return FALSE; + } // создать ec = EC_{ab}(f) hexToRev(t, a), hexToRev(t + no, b); if (!ecpCreateJ(ec, f, t, t + no, stack) || ec->d != 3) @@ -159,24 +167,6 @@ bool_t ecpTest() return FALSE; } } - // ecMulA() vs ecMulA2() -/* - { - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - while (!wwIsZero(d, n)) - { - if (!ecMulA2(pt0, ec->base, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - return FALSE; - wwShLo(d, n, 1); - } - if (ecMulA(pt0, ec->base, ec, d, n, stack) || - ecMulA2(pt1, ec->base, ec, d, n, stack)) - return FALSE; - } -*/ // финишное сложение if (ec->finadd) { @@ -311,37 +301,162 @@ bool_t ecpTest() } } // предвычисления по схеме SNZ - if (FALSE) { - // (pt0, pt1, pt2, pt3) <- (base, 3 base, 5 base, 7 base) - ecpPreSNZ(pts, ec->base, 3, ec, stack); - // pt0 == base? - ecToA(pt4, pt0, ec, stack); - if (!wwEq(pt5, ec->base, 2 * n)) + size_t i; + // pt[0..64) <- (1, 3, ..., 31, -31, ..., -3, -1)base + ecPreSNZ(pre, ec->base, 6, ec, stack); + // pt[i] == -pt[63 - i]? + for (i = 0; i < 32; ++i) + { + ecNeg(pt0, ecPrePt(pre, 63 - i, ec), ec, stack); + ecNeg(pt0, pt0, ec, stack); + ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); + if (ecToA(pt0, pt0, ec, stack)) + { + blobClose(state); + return FALSE; + } + } + // pt0 <- \sum_{i = 0}^31 2^{31 - i} pt[i] + wwCopy(pt0, ecPrePt(pre, 0, ec), 3 * n); + for (i = 1; i < 32; ++i) + { + ecDbl(pt0, pt0, ec, stack); + ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); + } + // pt0 == \sum_{i = 0}^31 (2i + 1) 2^{31 - i} base? + wwSetW(d, n, 1); + for (i = 1; i < 32; ++i) + { + wwShHi(d, n, 1); + zzAddW2(d, n, (word)(2 * i + 1)); + } + if (!ecToA(pt0, pt0, ec, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } - // pt1 == 3 base? - d[0] = 3, ecMulA(pt5, ec->base, ec, d, 1, stack); - ecToA(pt4, pt1, ec, stack); - if (!wwEq(pt4, pt5, 2 * n)) + } + // предвычисления по схеме SNZA + { + size_t i; + // pt[0..64) <- (1, 3, ..., 31, -31, ..., -3, -1)base + if (!ecPreSNZA(pre, ec->base, 6, ec, stack)) { blobClose(state); return FALSE; } - // pt2 == 5 base? - d[0] = 5, ecMulA(pt5, ec->base, ec, d, 1, stack); - ecToA(pt4, pt2, ec, stack); - if (!wwEq(pt4, pt5, 2 * n)) + // pt[i] == -pt[63 - i]? + for (i = 0; i < 32; ++i) + { + ecNegA(pt0, ecPrePtA(pre, 63 - i, ec), ec, stack); + ecNegA(pt0, pt0, ec, stack); + if (ecpAddAA(pt0, pt0, ecPrePtA(pre, i, ec), ec, stack)) + { + blobClose(state); + return FALSE; + } + } + // pt0 <- pt[1] - pt[0] + ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); + if (!ecpAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) { blobClose(state); return FALSE; } - // pt3 == 7 base? - d[0] = 7, ecMulA(pt5, ec->base, ec, d, 1, stack); - ecToA(pt4, pt3, ec, stack); - if (!wwEq(pt4, pt5, 2 * n)) + // pt[i+1] - pt[i] == pt0? + for (i = 1; i + 1 < 32; ++i) + { + ecNegA(pt1, ecPrePtA(pre, i, ec), ec, stack); + if (!ecpAddAA(pt1, pt1, ecPrePtA(pre, i + 1, ec), ec, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // pt0 == 2 base? + if (!ecpAddAA(pt1, ec->base, ec->base, ec, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // кратная точка: ecMulA vs ecMulPreSNZ + { + size_t w; + for (w = 5; w <= 6; ++w) + { + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + ecPreSNZ(pre, ec->base, w, ec, stack); + while (!wwIsZero(d, n)) + { + if (!ecMulPreSNZ(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, n, 11); + } + if (ecMulPreSNZ(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; + } + } + } + // кратная точка: ecMulA vs ecMulPreSNZA + { + size_t w; + for (w = 5; w <= 6; ++w) + { + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + ecPreSNZA(pre, ec->base, w, ec, stack); + while (!wwIsZero(d, n)) + { + if (!ecMulPreSNZA(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, n, 23); + } + if (ecMulPreSNZA(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; + } + } + } + // сумма кратных точек: ecMulA vs ecAddMulA + if (n >= 4) + { + if (!ecAddMulA(pt0, ec, stack, 4, + ec->base, ec->order, (size_t)1, + ec->base, ec->order, (size_t)2, + ec->base, ec->order, (size_t)3, + ec->base, ec->order, (size_t)4)) + { + blobClose(state); + return FALSE; + } + wwCopy(d, ec->order, n); + d[4] = zzAddW2(d + 3, 1, zzAdd2(d, ec->order, 3)); + d[4] += zzAddW2(d + 2, 2, zzAdd2(d, ec->order, 2)); + d[4] += zzAddW2(d + 1, 3, zzAdd2(d, ec->order, 1)); + if (!ecMulA(pt1, ec->base, ec, d, 5, stack) || + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; From 7635c9234108eb72f9887644475589c10f3383a4 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 2 Feb 2026 17:34:03 +0300 Subject: [PATCH 22/77] Fix comments in math/ec --- include/bee2/math/ec.h | 12 ++++++------ src/math/ec.c | 11 +++++++++++ src/math/ecp.c | 6 +----- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 413d11ed..1734f638 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.01.29 +\version 2026.02.02 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -450,17 +450,17 @@ typedef enum . \pre 0 < w && w < B_PER_W. \pre h > 0 <=> type == ec_pre_comb. - \remark Схему SNZ было предложено в использовать в работе [OkeTak03], + \remark Схему SNZ предложено в использовать в работе [OkeTak03], схему Comb -- в работе [HPB05]. . + [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory + and fast elliptic scalar multiplications secure against side + channel attacks. In Cryptographers’ Track at the RSA Conference, + 2003, pp. 328-343. Springer, Berlin Heidelberg. [HPB05] Hedabou M., Pinel P., Beneteau L. Countermeasures for preventing comb method against SCA attacks. In: International Conference on Information Security Practice and Experience, 2005, pp. 85-96, Springer, Berlin Heidelberg. - [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory - and fast elliptic scalar multiplications secure against side - channel attacks. In Cryptographers’ Track at the RSA Conference, - 2003, pp. 328-343. Springer, Berlin Heidelberg. */ typedef struct ec_pre_t { diff --git a/src/math/ec.c b/src/math/ec.c index d96262ec..afdc4c04 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -674,6 +674,17 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } +/* +******************************************************************************* +Кратная точка: метод Comb + +[LimLee94] Lim, C.H., Lee, P.J. More Flexible Exponentiation with + Precomputation. In: Advances in Cryptology -- CRYPTO ’94. 1994. + Lecture Notes in Computer Science, vol 839. Springer, Berlin, + Heidelberg. https://doi.org/10.1007/3-540-48658-5_11. +******************************************************************************* +*/ + /* ******************************************************************************* Имеет порядок? diff --git a/src/math/ecp.c b/src/math/ecp.c index 21864247..9bd0465c 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -100,10 +100,6 @@ ecpDblAJ(). Подумать, как "помочь" этой функции, п рассчитанные значения, например, za^2. Аналогичное замечание касается функции ecpAddJ(). -В функции ecpDblAddAJ() выполняется удвоение и сложение J <- 2J + A. -Реализован алгоритм из [LonMir08; приложение А.3]. Время работы алгоритма: - 11M + 7S + 27add. - \remark Целевые функции ci(l), определенные в описании реализации ecMulA() в ec.c, принимают следующий вид: c1(l) = l/3 11; @@ -862,7 +858,7 @@ static size_t ecpTplJA3_deep(size_t n, size_t f_deep) В функции ecpDblAddAJ() выполняется удвоение и сложение J <- 2J + A. Реализован алгоритм из [LonMir08; приложение А.3]. Время работы алгоритма: - 11M + 7S + 27add. + 11M + 7S + 25add. \warning В [LonMir08] вычисление 2a + b структурно выполняется за два сложения: c <- a + b, c <- c + a. From acc0d51ddafd3339ccdec3a2436736051ee06741 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 3 Feb 2026 18:54:18 +0300 Subject: [PATCH 23/77] Add ec_sgn_i and ec_sgna_i interfaces --- include/bee2/math/ec.h | 145 ++++++++++++++++++++++++++++++----------- src/math/ec.c | 34 ++++++---- src/math/ec2.c | 2 +- src/math/ecp.c | 56 +++++++++++----- test/math/ecp_test.c | 128 ++++++++++++++++++------------------ 5 files changed, 230 insertions(+), 135 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 1734f638..6e0ff599 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.02 +\version 2026.02.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -226,6 +226,26 @@ typedef void (*ec_nega_i)( void* stack /*!< [in] вспомогательная память */ ); +/*! \brief Обратная точка + + На эллиптической кривой ec определяется точка [ec->d * ec->f->n]b, + обратная к точке [ec->d * ec->f->n]a: + \code + b <- -a. + \endcode + \pre Описание ec работоспособно. + \pre Буфер b либо не пересекается, либо совпадает с буфером a. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. +*/ +typedef void(*ec_neg_i)( + word b[], /*!< [out] обратная точка */ + const word a[], /*!< [in] обращаемая точка */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + /*! \brief Сложение точек На эллиптической кривой ec определяется сумма [ec->d * ec->f->n]c точек @@ -311,29 +331,46 @@ typedef void (*ec_dbla_i)( void* stack /*!< [in] вспомогательная память */ ); -/*! \brief Удвоение и сложение с аффинной точкой +/*! \brief Установка знака точки - На эллиптической кривой ec определяется точка [ec->d * ec->f->n]c, - которая является суммой удвоенной точки [ec->d * ec->f->n]a и - аффинной точки [ec->d * ec->f->n]b: + На эллиптической кривой ec точка [ec->d * ec->f->n]a меняется на + обратную при neg == 1 и остается неизменной при neg == 0: \code - с <- 2 a + b. + a <- (-1)^neg a. \endcode \pre Описание ec работоспособно. - \pre Буфер с либо не пересекается с буфером a, либо совпадает с ним. - \pre Буфер с не пересекается с буфером b. - \pre Координаты a и b лежат в базовом поле. + \pre Координаты a лежат в базовом поле. + \pre neg == 0 || neg == 1. \expect Описание ec корректно. - \expect Точки a и b лежат на кривой. - \remark Функция интерфейса ec_dbladda_i может использоваться в алгоритмах - вычисления кратной точки. Функцию следует реализовать, если она дает - выигрыш по сравнению с каскадом функций интерфейсов ec_dbl_i (удвоение) - и ec_adda_i (сложение с аффинной точкой). + \expect Точка a лежит на кривой. + \remark Функции интерфейса ec_sgn_i предназначены для использования + в регулярных алгоритмах вычисления кратной точки. */ -typedef void(*ec_dbladda_i)( - word c[], /*!< [out] сумма */ - const word a[], /*!< [in] удваиваемая точка */ - const word b[], /*!< [in] второе слагаемое */ +typedef void(*ec_sgn_i)( + word a[], /*!< [in,out] точка */ + register word neg, /*!< [in] флаг изменения знака */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +/*! \brief Установка знака аффинной точки + + На эллиптической кривой ec аффинная точка [2 * ec->f->n]a меняется на + обратную при neg == 1 и остается неизменной при neg == 0: + \code + a <- (-1)^neg a. + \endcode + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre neg == 0 || neg == 1. + \expect Описание ec корректно. + \expect Точка a лежит на кривой. + \remark Функции интерфейса ec_sgna_i предназначены для использования + в регулярных алгоритмах вычисления кратной точки. +*/ +typedef void(*ec_sgna_i)( + word a[], /*!< [in,out] точка */ + register word neg, /*!< [in] флаг изменения знака */ const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ void* stack /*!< [in] вспомогательная память */ ); @@ -358,33 +395,57 @@ typedef void (*ec_tpl_i)( void* stack /*!< [in] вспомогательная память */ ); +/*! \brief Удвоение со сложением с аффинной точкой + + На эллиптической кривой ec определяется точка [ec->d * ec->f->n]c, + которая является суммой удвоенной точки [ec->d * ec->f->n]a и + аффинной точки [ec->d * ec->f->n]b: + \code + с <- 2 a + b. + \endcode + \pre Описание ec работоспособно. + \pre Буфер с либо не пересекается с буфером a, либо совпадает с ним. + \pre Буфер с не пересекается с буфером b. + \pre Координаты a и b лежат в базовом поле. + \expect Описание ec корректно. + \expect Точки a и b лежат на кривой. + \remark Функция интерфейса ec_dbladda_i может использоваться в алгоритмах + вычисления кратной точки. Функцию следует реализовать, если она дает + выигрыш по сравнению с каскадом функций интерфейсов ec_dbl_i (удвоение) + и ec_adda_i (сложение с аффинной точкой). +*/ +typedef void(*ec_dbladda_i)( + word c[], /*!< [out] сумма */ + const word a[], /*!< [in] удваиваемая точка */ + const word b[], /*!< [in] второе слагаемое */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + /*! \brief Финишное сложение На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, - которая является суммой точек [ec->d * ec->f->n]a и [ec->d * ec->f->n]b - при neg == 0, либо обратной к этой сумме при neg == 1: + которая является суммой точек [ec->d * ec->f->n]a и [ec->d * ec->f->n]b: \code - c <- (-1)^neg (a + b). + c <- a + b. \endcode \pre Описание ec работоспособно. \pre Буфер с либо не пересекается с каждым из буферов a и b, либо совпадает с некоторым из них. \pre Координаты a и b лежат в базовом поле. - \pre neg == 0 || neg == 1. \expect Описание ec корректно. \expect Точки a и b лежат на кривой. + \expect Сложение выполняется по полным (complete) формулам, одними и теми + же для случаев a == b, a == -b и a != \pm b. \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно удаленной (a + b == O). - \remark Функции интерфейса ec_finadd_i предназначены для использования - в регулярных алгоритмах вычисления кратной точки. Предполагается, что - сложение a + b выполняется по полным (complete) формулам, одними и теми же - для случаев a == b, a == -b и a != \pm b. + \remark Функции интерфейса ec_finadd_i предназначены для использования + в регулярных алгоритмах вычисления кратной точки. */ typedef bool_t(*ec_finadd_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ - register word neg, /*!< [in] флаг изменения знака */ const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ void* stack /*!< [in] вспомогательная память */ ); @@ -393,31 +454,27 @@ typedef bool_t(*ec_finadd_i)( На эллиптической кривой ec определяется аффинная точка [2 * ec->f->n]c, которая является суммой точки [ec->d * ec->f->n]a и аффинной точки - [2 * ec->f->n]b при neg == 0, либо обратной к этой сумме при neg == 1: + [2 * ec->f->n]b: \code - c <- (-1)^neg (a + b). + c <- a + b. \endcode \pre Описание ec работоспособно. \pre Буфер с либо не пересекается с каждым из буферов a и b, либо указатель c совпадает с некоторым из указателей a или b. \pre Координаты a и b лежат в базовом поле. - \pre neg == 0 || neg == 1. \expect Описание ec корректно. \expect Точки a и b лежат на кривой. - \expect Используется полная (complete) формула сложения, которая одинаково - работает с a == b и a != b. + \expect Сложение выполняется по полным (complete) формулам, одними и теми + же для случаев a == b, a == -b и a != \pm b. \return TRUE, если сумма является аффинной точкой, и FALSE, если бесконечно удаленной (a + b == O). \remark Функции интерфейса ec_finadda_i предназначены для использования - в регулярных алгоритмах вычисления кратной точки. Предполагается, что - сложение a + b выполняется по полным (complete) формулам, одними и теми же - для случаев a == b, a == -b и a != \pm b. + в регулярных алгоритмах вычисления кратной точки. */ typedef bool_t(*ec_finadda_i)( word c[], /*!< [out] сумма */ const word a[], /*!< [in] первое слагаемое */ const word b[], /*!< [in] второе слагаемое */ - register word neg, /*!< [in] флаг изменения знака */ const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ void* stack /*!< [in] вспомогательная память */ ); @@ -484,7 +541,7 @@ typedef struct ec_o const qr_o* f; /*!< базовое поле */ word* A; /*!< коэффициент A */ word* B; /*!< коэффициент B */ - word* base; /*!< базовая точка */ + word* base; /*!< базовая точустановке знаковка */ word* order; /*!< порядок группы точек */ ec_pre_t* pre; /*!< предвычисленные точки */ // } @@ -498,8 +555,10 @@ typedef struct ec_o ec_adda_i adda; /*!< функция сложения с аффинной точкой */ ec_dbl_i dbl; /*!< функция удвоения */ ec_dbla_i dbla; /*!< функция удвоения аффинной точки */ + ec_sgn_i sgn; /*!< функция установки знака */ + ec_sgna_i sgna; /*!< функция установки знака аффинной точки */ ec_tpl_i tpl; /*!< функция утроения */ - ec_dbladda_i dbladda; /*!< функция удвоения и сложения с афф. точкой */ + ec_dbladda_i dbladda; /*!< функция удвоения со сложением с афф. точкой */ ec_finadd_i finadd; /*!< функция финишного сложения */ ec_finadda_i finadda; /*!< функция финишного сложения с афф. точкой */ size_t deep; /*!< максимальная глубина стека функций */ @@ -561,6 +620,16 @@ typedef struct ec_o (ec)->dbladda(c, a, b, ec, stack) :\ (ecDbl(c, a, ec, stack), ecAddA(c, c, b, ec, stack))) +#define ecSgn(a, neg, ec, stack)\ + ((ec)->sgn ?\ + (ec)->sgn(a, neg, ec, stack) :\ + (neg ? ecNeg(a, a, ec, stack) : 0)) + +#define ecSgnA(a, neg, ec, stack)\ + ((ec)->sgna ?\ + (ec)->sgna(a, neg, ec, stack) :\ + (neg ? ecNegA(a, a, ec, stack) : 0)) + #define ecSetO(a, ec)\ wwSetZero(ecZ(a, (ec)->f->n), (ec)->f->n) diff --git a/src/math/ec.c b/src/math/ec.c index afdc4c04..f03e64ac 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -281,7 +281,7 @@ size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep) /* t */ O_OF_W(ec_d * n) static bool_t ecFinAdd(word c[], const word a[], const word b[], - register word neg, const ec_o* ec, void* stack) + const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ ASSERT(ecIsOperable(ec)); @@ -289,8 +289,6 @@ static bool_t ecFinAdd(word c[], const word a[], const word b[], ecFinAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); ecAdd(t, a, b, ec, stack); - if (neg) - ecNeg(t, t, ec, stack); return ecToA(c, t, ec, stack); } @@ -306,7 +304,7 @@ static size_t ecFinAdd_deep(size_t n, size_t ec_d, size_t ec_deep) /* t */ O_OF_W(ec_d * n) static bool_t ecFinAddA(word c[], const word a[], const word b[], - register word neg, const ec_o* ec, void* stack) + const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ ASSERT(ecIsOperable(ec)); @@ -314,8 +312,6 @@ static bool_t ecFinAddA(word c[], const word a[], const word b[], ecFinAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); ecAddA(t, a, b, ec, stack); - if (neg) - ecNeg(t, t, ec, stack); return ecToA(c, t, ec, stack); } @@ -585,9 +581,15 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); - ret = (ec->finadd) ? - ec->finadd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), neg, ec, stack) : - ecFinAdd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), neg, ec, stack); + if (ec->finadd) + ret = ec->finadd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); + else + { + ecAdd(t, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); + ret = ecToA(b, t, ec, stack); + } + // настройка знака + ecSgnA(b, neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); return ret; @@ -657,10 +659,16 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); - ret = (ec->finadda) ? - ec->finadda(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), neg, ec, - stack) : - ecFinAddA(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), neg, ec, stack); + if (ec->finadd) + ret = ec->finadda(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, + stack); + else + { + ecAddA(t, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, stack); + ret = ecToA(b, t, ec, stack); + } + // настройка знака + ecSgnA(b, neg, ec, stack); // очистка и возврат CLEAN3(neg, digit, hi); return ret; diff --git a/src/math/ec2.c b/src/math/ec2.c index 2b632911..77a217d2 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.27 +\version 2026.02.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* diff --git a/src/math/ecp.c b/src/math/ecp.c index 9bd0465c..0a3cda83 100644 --- a/src/math/ecp.c +++ b/src/math/ecp.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.01.27 +\version 2026.02.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -187,11 +187,9 @@ static void ecpNegJ(word b[], const word a[], const ec_o* ec, void* stack) ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(ecpSeemsOnJ(a, ec)); ASSERT(wwIsSameOrDisjoint(a, b, 3 * ec->f->n)); - // xb <- xa + // b <- (xa, -ya, za) qrCopy(ecX(b), ecX(a), ec->f); - // yb <- -ya zmNeg(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); - // zb <- za qrCopy(ecZ(b, ec->f->n), ecZ(a, ec->f->n), ec->f); } @@ -1026,6 +1024,32 @@ size_t ecpDblAddAJ_deep(size_t n, size_t f_deep) SIZE_MAX); } +/* +******************************************************************************* +Изменение знака +****************************************************************************** +*/ + +// [3n]b <- (-1)^neg [3n]a (J <- \pm J) +static void ecpSgnJ(word a[], register word neg, const ec_o* ec, void* stack) +{ + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(neg == 0 || neg == 1); + // a <- (xa, (-1)^neg ya, za) + zzNegModIf(ecY(a, ec->f->n), ecY(a, ec->f->n), ec->f->mod, ec->f->n, neg); +} + +// [2n]b <- (-1)^neg [2n]a (A <- \pm A) +static void ecpSgnAJ(word a[], register word neg, const ec_o* ec, void* stack) +{ + ASSERT(ecIsOperable(ec)); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(neg == 0 || neg == 1); + // a <- (xa, (-1)^neg ya) + zzNegModIf(ecY(a, ec->f->n), ecY(a, ec->f->n), ec->f->mod, ec->f->n, neg); +} + /* ******************************************************************************* Финишное сложение @@ -1055,9 +1079,9 @@ size_t ecpDblAddAJ_deep(size_t n, size_t f_deep) /* t4 */ O_OF_W(n),\ /* t5 */ O_OF_W(n) -// [2n]с <- (-1)^neg ([3n]a + [3n]b) (A <- J + J) +// [2n]с <- [3n]a + [3n]b (A <- J + J) static bool_t ecpFinAdd(word c[], const word a[], const word b[], - register word neg, const ec_o* ec, void* stack) + const ec_o* ec, void* stack) { size_t n; register bool_t ret; @@ -1139,8 +1163,6 @@ static bool_t ecpFinAdd(word c[], const word a[], const word b[], qrInv(t0, ecZ(pt3, n), ec->f, stack); qrMul(ecX(c), ecX(pt3), t0, ec->f, stack); qrMul(ecY(c, n), ecY(pt3, n), t0, ec->f, stack); - // c <- (-1)^neg c - zzNegModIf(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); return ret; } @@ -1163,9 +1185,9 @@ static size_t ecpFinAdd_deep(size_t n, size_t f_deep) /* t4 */ O_OF_W(n),\ /* t5 */ O_OF_W(n) -// [2n]с <- (-1)^neg ([3n]a + [2n]b) (A <- J + A) +// [2n]с <- [3n]a + [2n]b (A <- J + A) static bool_t ecpFinAddA(word c[], const word a[], const word b[], - register word neg, const ec_o* ec, void* stack) + const ec_o* ec, void* stack) { size_t n; register bool_t ret; @@ -1234,8 +1256,6 @@ static bool_t ecpFinAddA(word c[], const word a[], const word b[], qrInv(t0, ecZ(pt3, n), ec->f, stack); qrMul(ecX(c), ecX(pt3), t0, ec->f, stack); qrMul(ecY(c, n), ecY(pt3, n), t0, ec->f, stack); - // c <- (-1)^neg c - zzNegModIf(ecY(c, n), ecY(c, n), ec->f->mod, n, neg); return ret; } @@ -1307,8 +1327,10 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->adda = ecpAddAJ; ec->dbl = bA3 ? ecpDblJA3 : ecpDblJ; ec->dbla = ecpDblAJ; - ec->dbladda = ecpDblAddAJ; ec->tpl = bA3 ? ecpTplJA3 : ecpTplJ; + ec->dbladda = ecpDblAddAJ; + ec->sgn = ecpSgnJ; + ec->sgna = ecpSgnAJ; ec->finadd = ecpFinAdd; ec->finadda = ecpFinAddA; ec->deep = utilMax(9, @@ -1349,9 +1371,9 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) ecpDblJ_deep(n, f_deep), ecpDblJA3_deep(n, f_deep), ecpDblAJ_deep(n, f_deep), - ecpDblAddAJ_deep(n, f_deep), ecpTplJ_deep(n, f_deep), ecpTplJA3_deep(n, f_deep), + ecpDblAddAJ_deep(n, f_deep), ecpFinAdd_deep(n, f_deep), ecpFinAddA_deep(n, f_deep)), SIZE_MAX); @@ -1586,14 +1608,12 @@ size_t ecpIsOnA_deep(size_t n, size_t f_deep) void ecpNegA(word b[], const word a[], const ec_o* ec) { - const size_t n = ec->f->n; - // pre ASSERT(ecIsOperable(ec)); ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 2 * n)); + ASSERT(wwIsSameOrDisjoint(a, b, 2 * ec->f->n)); // (xb, yb) <- (xa, -ya) qrCopy(ecX(b), ecX(a), ec->f); - zmNeg(ecY(b, n), ecY(a, n), ec->f); + zmNeg(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); } #define ecpAddAA_local(n)\ diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index f1c3538a..a92d5d72 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -167,6 +167,52 @@ bool_t ecpTest() return FALSE; } } + // удвоение со сложением с аффинной точкой + if (ec->dbladda) + { + // pt0 <- 2 base + base + ecFromA(pt1, ec->base, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt3 <- 3 base + ecpAddAA(pt3, ec->base, ec->base, ec, stack); + ecpAddAA(pt3, pt3, ec->base, ec, stack); + // pt0 == pt3? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2(-base) + base + ecNeg(pt1, pt1, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt3 <- -base + ecpNegA(pt3, ec->base, ec); + // pt0 == pt3? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2O + base + ecSetO(pt1, ec); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt0 == base? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, ec->base, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2(2base) + base + ecDblA(pt1, ec->base, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt0 == 5 base? + d[0] = 5, ecMulA(pt3, ec->base, ec, d, 1, stack); + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + } // финишное сложение if (ec->finadd) { @@ -176,33 +222,33 @@ bool_t ecpTest() ecAddA(pt3, pt2, ec->base, ec, stack); ecAddA(pt4, pt3, ec->base, ec, stack); // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? - ec->finadd(pt0, pt1, pt2, 0, ec, stack); + ec->finadd(pt0, pt1, pt2, ec, stack); ecToA(pt5, pt3, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pt0 <- -(pt1 + pt3) == -4 base, pt0 == -pt4? - ec->finadd(pt0, pt1, pt3, 1, ec, stack); - ecNeg(pt5, pt4, ec, stack); - ecToA(pt5, pt5, ec, stack); + // pt0 <- pt1 + pt3 == 4 base, pt0 == pt4? + ec->finadd(pt0, pt1, pt3, ec, stack); + ecToA(pt5, pt4, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pt0 <- -(pt2 + pt2) == -4 base, pt0 == -pt4? - ec->finadd(pt0, pt2, pt2, 1, ec, stack); + // pt0 <- pt2 + pt2 == 4 base, pt0 == pt4? + ec->finadd(pt0, pt2, pt2, ec, stack); + ecToA(pt5, pt4, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } // pt0 <- pt1 + pt1 == 2 base, pt0 == pt2? - ec->finadd(pt0, pt1, pt1, 0, ec, stack); + ec->finadd(pt0, pt1, pt1, ec, stack); ecToA(pt5, pt2, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) + if (!wwEq(pt0, pt2, 2 * n)) { blobClose(state); return FALSE; @@ -218,35 +264,33 @@ bool_t ecpTest() ecAddA(pt4, pt3, ec->base, ec, stack); // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? ecToA(pt5, pt2, ec, stack); - ec->finadda(pt0, pt1, pt5, 0, ec, stack); + ec->finadda(pt0, pt1, pt5, ec, stack); ecToA(pt5, pt3, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pt0 <- -(pt1 + pt3) == -4 base, pt0 == -pt4? + // pt0 <- pt1 + pt3 == 4 base, pt0 == pt4? ecToA(pt5, pt3, ec, stack); - ec->finadda(pt0, pt1, pt5, 1, ec, stack); - ecNeg(pt5, pt4, ec, stack); - ecToA(pt5, pt5, ec, stack); + ec->finadda(pt0, pt1, pt5, ec, stack); + ecToA(pt5, pt4, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } - // pt0 <- -(pt2 + pt2) == -4 base, pt0 == -pt4? + // pt0 <- pt2 + pt2 == 4 base, pt0 == pt4? ecToA(pt5, pt2, ec, stack); - ec->finadda(pt0, pt2, pt5, 1, ec, stack); - ecNeg(pt5, pt4, ec, stack); - ecToA(pt5, pt5, ec, stack); + ec->finadda(pt0, pt2, pt5, ec, stack); + ecToA(pt5, pt4, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; } // pt0 <- pt1 + pt1 == 2 base, pt0 ==? pt2 - ec->finadda(pt0, pt1, ec->base, 0, ec, stack); + ec->finadda(pt0, pt1, ec->base, ec, stack); ecToA(pt5, pt2, ec, stack); if (!wwEq(pt0, pt5, 2 * n)) { @@ -254,52 +298,6 @@ bool_t ecpTest() return FALSE; } } - // удвоение и сложение с аффинной точкой - if (ec->dbladda) - { - // pt0 <- 2 base + base - ecFromA(pt1, ec->base, ec, stack); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt3 <- 3 base - ecpAddAA(pt3, ec->base, ec->base, ec, stack); - ecpAddAA(pt3, pt3, ec->base, ec, stack); - // pt0 == pt3? - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- 2(-base) + base - ecNeg(pt1, pt1, ec, stack); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt3 <- -base - ecpNegA(pt3, ec->base, ec); - // pt0 == pt3? - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- 2O + base - ecSetO(pt1, ec); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt0 == base? - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, ec->base, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- 2(2base) + base - ecDblA(pt1, ec->base, ec, stack); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt0 == 5 base? - d[0] = 5, ecMulA(pt3, ec->base, ec, d, 1, stack); - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) - { - blobClose(state); - return FALSE; - } - } // предвычисления по схеме SNZ { size_t i; From 0b569a6904306bfedad09d6f1c986754437e6f35 Mon Sep 17 00:00:00 2001 From: agievich Date: Tue, 3 Feb 2026 23:24:38 +0300 Subject: [PATCH 24/77] Update test/math/ecp --- src/math/ec.c | 94 +++++++++++++------------------------------- test/math/ecp_test.c | 39 +++++++++++++++++- 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/src/math/ec.c b/src/math/ec.c index f03e64ac..ab710c1f 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.01.29 +\version 2026.02.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -269,60 +269,6 @@ size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } -/* -******************************************************************************* -Финишные сложения - -Прямые нерегулярные реализации интерфейсов ec_finadd_i и ec_finadda_i. -******************************************************************************* -*/ - -#define ecFinAdd_local(n, ec_d)\ -/* t */ O_OF_W(ec_d * n) - -static bool_t ecFinAdd(word c[], const word a[], const word b[], - const ec_o* ec, void* stack) -{ - word* t; /* [ec->d * ec->f->n] */ - ASSERT(ecIsOperable(ec)); - memSlice(stack, - ecFinAdd_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, - &t, &stack); - ecAdd(t, a, b, ec, stack); - return ecToA(c, t, ec, stack); -} - -static size_t ecFinAdd_deep(size_t n, size_t ec_d, size_t ec_deep) -{ - return memSliceSize( - ecFinAdd_local(n, ec_d), - ec_deep, - SIZE_MAX); -} - -#define ecFinAddA_local(n, ec_d)\ -/* t */ O_OF_W(ec_d * n) - -static bool_t ecFinAddA(word c[], const word a[], const word b[], - const ec_o* ec, void* stack) -{ - word* t; /* [ec->d * ec->f->n] */ - ASSERT(ecIsOperable(ec)); - memSlice(stack, - ecFinAddA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, - &t, &stack); - ecAddA(t, a, b, ec, stack); - return ecToA(c, t, ec, stack); -} - -static size_t ecFinAddA_deep(size_t n, size_t ec_d, size_t ec_deep) -{ - return memSliceSize( - ecFinAddA_local(n, ec_d), - ec_deep, - SIZE_MAX); -} - /* ******************************************************************************* Кратная точка: метод NAF (Non-Adjacent Form) @@ -446,6 +392,7 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, size_t naf_width; size_t pre_count; ec_pre_t* pre; /* [pre_count проективных точек] */ + size_t i; // pre ASSERT(ecIsOperable(ec)); ASSERT(wwIsValid(d, m)); @@ -459,8 +406,12 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, memSlice(stack, ecMulA_local(ec->f->n, ec->d, pre_count), SIZE_0, SIZE_MAX, &pre, &stack); - // предвычисления + // предвычисления: pre[i] <- (2i + 1)a ecPreSNZ(pre, a, naf_width - 1, ec, stack); + // предвычисления: pre[pre_count - 1 - i] <- -pre[i] + for (i = 0; i < pre_count / 2; ++i) + ecNeg(ecPrePt(pre, pre_count - 1 - i, ec), + ecPrePt(pre, i, ec), ec, stack); // кратная точка по методу NAF return ecMulPreNAF(b, pre, ec, d, m, stack); } @@ -495,13 +446,14 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pt[i] <- (2i + 1)a, pt[-i] <- -pt[i]. // предвычисления, схема SNZ + 1) pt[i] <- (2i + 1)a; // предвычисления: SNZ + 2) pt[-i] = =pt[i]. 2. t <- p[d_{k-1} / 2]. 3. Для i = k - 2, ..., 1: - 1) t <- 2^w t; // P <- 2P - 1) t <- t + pt[d_i / 2]. // P <- P + P + 1) t <- 2^w t; // P <- 2P + 1) t <- t + pt[d_i / 2]. // P <- P + P 4. t <- 2^w t. -5. t <- t + pt[d_0]. // A <- P + P +5. t <- t + pt[d_0 / 2]. // A <- P + P 6. Возвратить t. \remark Сложение t + pt[d_0] вынесено за рамки основного цикла на шаге 3, @@ -527,7 +479,8 @@ ec_finadd_i, если таковая указана в описании крив #define ecMulPreSNZ_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ -/* t */ O_OF_W(ec_d * n)\ +/* t */ O_OF_W(ec_d * n),\ +/* pt */ O_OF_W(ec_d * n) bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) @@ -541,6 +494,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, size_t i; word* dd; /* [m] */ word* t; /* [ec->d * ec->f->n] */ + word* pt; /* [ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); @@ -554,7 +508,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, // разметить стек memSlice(stack, ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &dd, &t, &stack); + &dd, &t, &pt, &stack); // dd <- (d & 1) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); zzSubIf(dd, ec->order, d, m, neg); @@ -573,7 +527,8 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, for (i = pre->w; i--;) ecDbl(t, t, ec, stack); digit = wwGetBits(dd, k * pre->w, pre->w); - ecAdd(t, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); + wwCopy(pt, ecPrePt(pre, hi | (digit >> 1), ec), ec->d * ec->f->n); + ecAdd(t, t, pt, ec, stack); hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; } // завершающие удвоения и финишное сложение @@ -581,11 +536,12 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); + wwCopy(pt, ecPrePt(pre, hi | (digit >> 1), ec), ec->d * ec->f->n); if (ec->finadd) - ret = ec->finadd(b, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); + ret = ec->finadd(b, t, pt, ec, stack); else { - ecAdd(t, t, ecPrePt(pre, hi | (digit >> 1), ec), ec, stack); + ecAdd(t, t, pt, ec, stack); ret = ecToA(b, t, ec, stack); } // настройка знака @@ -599,7 +555,7 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( ecMulPreSNZ_local(n, ec_d, m), - ecFinAdd_deep(n, ec_d, ec_deep), + ec_deep, SIZE_MAX); } @@ -678,7 +634,7 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( ecMulPreSNZA_local(n, ec_d, m), - ecFinAddA_deep(n, ec_d, ec_deep), + ec_deep, SIZE_MAX); } @@ -773,6 +729,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) const word* a; const word* d; size_t pre_count; + size_t j; // a <- a[i] a = va_arg(args, const word*); // d <- d[i] @@ -797,6 +754,9 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) naf_pos[i] = 0; // предвычисления ecPreSNZ(pre[i], a, naf_width[i] - 1, ec, stack); + for (j = 0; j < pre_count / 2; ++j) + ecNeg(ecPrePt(pre[i], pre_count - 1 - j, ec), + ecPrePt(pre[i], j, ec), ec, stack); } va_end(args); // t <- O diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index a92d5d72..94dc9d22 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -213,6 +213,43 @@ bool_t ecpTest() return FALSE; } } + // установка знака + if (ec->sgn) + { + ecFromA(pt0, ec->base, ec, stack); + wwCopy(pt1, pt0, 3 * n); + ecSgn(pt0, 0, ec, stack); + if (!wwEq(pt0, pt1, 3 * n)) + { + blobClose(state); + return FALSE; + } + ecSgn(pt0, 1, ec, stack); + ecNeg(pt1, pt1, ec, stack); + if (!wwEq(pt0, pt1, 3 * n)) + { + blobClose(state); + return FALSE; + } + } + // установка знака аффинной точки + if (ec->sgna) + { + wwCopy(pt0, ec->base, 2 * n); + ecSgnA(pt0, 0, ec, stack); + if (!wwEq(pt0, ec->base, 2 * n)) + { + blobClose(state); + return FALSE; + } + ecSgnA(pt0, 1, ec, stack); + ecNegA(pt1, ec->base, ec, stack); + if (!wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } // финишное сложение if (ec->finadd) { @@ -248,7 +285,7 @@ bool_t ecpTest() // pt0 <- pt1 + pt1 == 2 base, pt0 == pt2? ec->finadd(pt0, pt1, pt1, ec, stack); ecToA(pt5, pt2, ec, stack); - if (!wwEq(pt0, pt2, 2 * n)) + if (!wwEq(pt0, pt5, 2 * n)) { blobClose(state); return FALSE; From c3632fb3a97b1cf7c98b38e2742e52682c340667 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 4 Feb 2026 15:13:33 +0300 Subject: [PATCH 25/77] Update SNZ and SNZA schemas in math/ec --- include/bee2/math/ec.h | 40 +++++----- src/math/ec.c | 172 ++++++++++++++++++++++------------------- test/math/ecp_test.c | 31 +------- 3 files changed, 116 insertions(+), 127 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 6e0ff599..9188f7e0 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.03 +\version 2026.02.04 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -99,9 +99,9 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). вместе с точками. Описание эллиптической кривой включает указатели на функции арифметики -в группе точек этой кривой. Функции интерфейсов ec_dbladda_i, ec_tpl_i, -ec_finadd_i и ec_finadda_i можно не поддерживать. Указатель на -неподдерживаемую функцию должен быть нулевым. +в группе точек этой кривой. Функции интерфейсов ec_sgn_i, ec_sgna_i, +ec_tpl_i, ec_dbladda_i, ec_finadd_i и ec_finadda_i можно не поддерживать. +Указатель на неподдерживаемую функцию должен быть нулевым. Описание кольца организовано как объект, и можно применять функции, описанные в заголовочном файле obj.h. @@ -485,30 +485,30 @@ typedef enum { ec_pre_snz = 1, /*!< схема SNZ */ ec_pre_snza, /*!< схема SNZA */ - ec_pre_comb, /*!< схема Comb */ + ec_pre_hpb, /*!< схема HPB */ } ec_pre_type; /*! \brief Предвычисленные точки - В поле pts размещаются точки эллиптической кривой, построенные - по некоторой точке a в соответствии с определенной схемой - предвычислений. Число предвычисленных точек определяется - шириной окна w и равняется 2^w. Поддерживаются следующие схемы: + В поле pts размещаются точки эллиптической кривой, построенные по + определенной точке a в соответствии с определенной схемой предвычислений. + Число предвычисленных точек определяется шириной окна w и обычно равняется + 2^{w-1}. Поддерживаются следующие схемы: - SNZ (Signed Non-Zero): вычисляется проективные точки - a, 3a, ..., (2^w-1)a, -(2^w-1)a, ..., -3a, -a; + a, 3a, ..., (2^w-1)a; - SNZA: вычисляются аффинные точки - a, 3a, ..., (2^w-1)a, -(2^w-1)a, ..., -3a, -a; - - Comb: вычисляются аффинные точки - [b_{w-1} ... b_1 b_0]a = b_{w-1} h^{w-1} a + ... + b_1 h a + b_0 a, - где b_i in {-1, 1}. Точка [b_{w-1} ... b_1 b_0]a размещаются - в массиве pts по индексу, двоичная запись которого есть + a, 3a, ..., (2^w-1)a; + - HPB: вычисляются аффинные точки + [b_{w-1} ... b_1 b_0]a = \sum_{i=0}^{w-1} b_i 2^{h i} a, + где b_i in {-1, 1}, b_{w-1} == 1. Точка [b_{w-1} ... b_1 b_0]a + размещаются в массиве pts по индексу, двоичная запись которого есть с_{w-1}...c_1 c_0, - где c_i = 1, если b_i = 1, и c_i = 0, если b_i = -1. + где c_i = 0, если b_i = 1, и c_i = 1, если b_i = -1. . \pre 0 < w && w < B_PER_W. - \pre h > 0 <=> type == ec_pre_comb. + \pre h > 0 <=> type == ec_pre_hpb. \remark Схему SNZ предложено в использовать в работе [OkeTak03], - схему Comb -- в работе [HPB05]. + схему HPB -- в работе [HPB05]. . [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory and fast elliptic scalar multiplications secure against side @@ -751,9 +751,9 @@ bool_t ecGroupIsOperable( - буфер [sizeof(ec_pre_t)]pre корректен; - pre->type \in ec_pre_type; - pre->w > 0; - - pre->h > 0 <=> pre->type == ec_pre_comb. + - pre->h > 0 <=> pre->type == ec_pre_hpb. . - \return Признак корректности. + \return Признак работоспособности. */ bool_t ecPreIsOperable( const ec_pre_t* pre /*!< [in] предвычисленные точки */ diff --git a/src/math/ec.c b/src/math/ec.c index ab710c1f..e447b91b 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.03 +\version 2026.02.04 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -112,8 +112,7 @@ bool_t ecGroupIsOperable(const ec_o* ec) - t <- 2a; // P <- 2A - pt[0] <- a; - pt[1] <- t + pt[0]; // P <- P + A -- pt[i] <- t + pt[i - 1], i = 2, 3, ..., 2^{w-1} - 1; // P <- P + P -- pt[2^w - 1 - i] = -pt[i], i = 0, 1, ..., 2^{w-1} - 1. +- pt[i] <- t + pt[i - 1], i = 2, 3, ..., 2^{w-1} - 1. // P <- P + P Итоговая сложность: 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). @@ -162,10 +161,10 @@ bool_t ecPreIsOperable(const ec_pre_t* pre) { return memIsValid(pre, sizeof(pre)) && (pre->type == ec_pre_snz || pre->type == ec_pre_snza || - pre->type == ec_pre_comb) && + pre->type == ec_pre_hpb) && pre->w > 0 && pre->w < B_PER_W && - (pre->type == ec_pre_comb && pre->h > 0 || - pre->type != ec_pre_comb && pre->h == 0); + (pre->type == ec_pre_hpb && pre->h > 0 || + pre->type != ec_pre_hpb && pre->h == 0); } #define ecPreSNZ_local(n, ec_d)\ @@ -199,10 +198,6 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, for (i = 2; i < SIZE_1 << (w - 1); ++i) ecAdd(ecPrePt(pre, i, ec), t, ecPrePt(pre, i - 1, ec), ec, stack); } - // pt[pre_count - i] <- -pt[i] - for (i = 0; i < SIZE_1 << (w - 1); ++i) - ecNeg(ecPrePt(pre, (SIZE_1 << w) - 1 - i, ec), ecPrePt(pre, i, ec), - ec, stack); // заполнить остальные поля pre->type = ec_pre_snz; pre->w = w, pre->h = 0; @@ -251,10 +246,6 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, return FALSE; } } - // pt[pre_count - i] <- -pt[i] - for (i = 0; i < SIZE_1 << (w - 1); ++i) - ecNegA(ecPrePtA(pre, (SIZE_1 << w) - 1 - i, ec), ecPrePtA(pre, i, ec), - ec, stack); // заполнить остальные поля pre->type = ec_pre_snza; pre->w = w, pre->h = 0; @@ -446,23 +437,38 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], а также [APS22]): 1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pt[i] <- (2i + 1)a; // предвычисления: SNZ - 2) pt[-i] = =pt[i]. -2. t <- p[d_{k-1} / 2]. + 1) pt[i] <- (2i + 1)a. // предвычисления: SNZ +2. t <- pt[d_{k-1} / 2]. 3. Для i = k - 2, ..., 1: - 1) t <- 2^w t; // P <- 2P - 1) t <- t + pt[d_i / 2]. // P <- P + P + 1) t <- 2^w t; // P <- 2P + 1) t <- t + sgn(d_i) pt[|d_i| / 2]. // P <- P + P 4. t <- 2^w t. -5. t <- t + pt[d_0 / 2]. // A <- P + P +5. t <- t + sgn(d_0) pt[|d_0| / 2]. // A <- P + P 6. Возвратить t. -\remark Сложение t + pt[d_0] вынесено за рамки основного цикла на шаге 3, -потому что при этом (и только при этом) сложении может произойти исключительная -ситуация -- совпадение операндов t и pt[d_0] с переключением от сложения -к удвоению. Завершающее сложение выполняется с помощью функции интерфейса -ec_finadd_i, если таковая указана в описании кривой. Предполагается, что -функция ec_finadd_i регулярна, и тогда сложение и удвоение будут выполняться -по одним и тем же формулам. +\remark Сложение t + sgn(d_0) pt[|d_0| / 2] вынесено за рамки основного цикла +на шаге 3, потому что при этом (и только при этом) сложении может произойти +исключительная ситуация -- совпадение операндов t и sgn(d_0) pt[|d_0| / 2] +с переключением от сложения к удвоению. Завершающее сложение выполняется +с помощью функции интерфейса ec_finadd_i, если таковая указана в описании +кривой. Предполагается, что функция ec_finadd_i регулярна, и тогда сложение +и удвоение будут выполняться по одним и тем же формулам. + +\remark Расчет цифр d_i (см. [APS22]): +1. Записать + d = d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}, + где d_i \in {0, 1, ..., 2^w - 1}. +2. (d_{k-1}, borrow) <- (d_{k-1} + (d_{k-1} % 2), d_{k-1} % 2). +3. Для i = k - 2, ...., 0: + 1) (d_i, borrow) <- (d_i - borrow * 2^w + (d_i % 2), d_i % 2). + +\remark Выражение + borrow ? d_i : d_i - 2^w, +по которому определяется индекс в таблице pt, вычисляется следующим образом: + mask <- 2^w - 2, d_i <- d_i ^ ((0 - borrow) & mask). +Поскольку младший разряд mask нулевой, сохраняется четность цифры d_i и эту +четность можно использовать для пересчета borrow: + borrow <- d_i % 2. В функции ecMulPreSNZA() предварительно рассчитанные точки являются аффинными, а не проективными. Это позволяет заменить регулярные сложения P <- P + P @@ -487,10 +493,11 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, { register word neg; register word digit; - register word hi; + register word borrow; register bool_t ret; size_t mb; - size_t k; + size_t mask; + size_t pos; size_t i; word* dd; /* [m] */ word* t; /* [ec->d * ec->f->n] */ @@ -505,49 +512,49 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); + ASSERT(mb >= 2 * pre->w); + mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, &dd, &t, &pt, &stack); - // dd <- (d & 1) ? d : ec->order - d + // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); - // число цифр SNZ - k = (mb + pre->w - 1) / pre->w; - ASSERT(k > 1); - // старшая цифра - --k; - digit = wwGetBits(dd, k * pre->w, mb - k * pre->w); + // обработать старшую цифру + pos = (mb - 1) / pre->w, pos *= pre->w; + digit = wwGetBits(dd, pos, mb - pos); wwCopy(t, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); - hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; + borrow = WORD_1 - (digit & 1); // обработать остальные цифры - while (--k) + while (pos -= pre->w) { for (i = pre->w; i--;) ecDbl(t, t, ec, stack); - digit = wwGetBits(dd, k * pre->w, pre->w); - wwCopy(pt, ecPrePt(pre, hi | (digit >> 1), ec), ec->d * ec->f->n); + digit = wwGetBits(dd, pos, pre->w); + digit ^= (WORD_0 - borrow) & mask; + wwCopy(pt, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); + ecSgn(pt, borrow, ec, stack); ecAdd(t, t, pt, ec, stack); - hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; + borrow = WORD_1 - (digit & 1); } - // завершающие удвоения и финишное сложение + // завершающие удвоения for (i = pre->w; i--;) ecDbl(t, t, ec, stack); + // финишное сложение digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); - wwCopy(pt, ecPrePt(pre, hi | (digit >> 1), ec), ec->d * ec->f->n); - if (ec->finadd) - ret = ec->finadd(b, t, pt, ec, stack); - else - { - ecAdd(t, t, pt, ec, stack); - ret = ecToA(b, t, ec, stack); - } + digit ^= (WORD_0 - borrow) & mask; + wwCopy(pt, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); + ecSgn(pt, borrow, ec, stack); + ret = (ec->finadd) ? + ec->finadd(b, t, pt, ec, stack) : + (ecAdd(t, t, pt, ec, stack), ecToA(b, t, ec, stack)); // настройка знака ecSgnA(b, neg, ec, stack); // очистка и возврат - CLEAN3(neg, digit, hi); + CLEAN3(neg, digit, borrow); return ret; } @@ -561,72 +568,77 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) #define ecMulPreSNZA_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ -/* t */ O_OF_W(ec_d * n)\ +/* t */ O_OF_W(ec_d * n),\ +/* pt */ O_OF_W(ec_d * n) bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; register word digit; - register word hi; + register word borrow; register bool_t ret; size_t mb; - size_t k; + size_t mask; + size_t pos; size_t i; word* dd; /* [m] */ word* t; /* [ec->d * ec->f->n] */ + word* pt; /* [ec->d * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_snza); + ASSERT(pre->type == ec_pre_snz); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); + ASSERT(mb >= 2 * pre->w); + mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, - ecMulPreSNZA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &dd, &t, &stack); - // dd <- (d & 1) ? d : ec->order - d + ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &dd, &t, &pt, &stack); + // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); - // число цифр SNZ - k = (mb + pre->w - 1) / pre->w; - ASSERT(k > 1); - // старшая цифра - --k; - digit = wwGetBits(dd, k * pre->w, mb - k * pre->w); - ecFromA(t, ecPrePtA(pre, digit >> 1, ec), ec, stack); - hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; + // обработать старшую цифру + pos = (mb - 1) / pre->w, pos *= pre->w; + digit = wwGetBits(dd, pos, mb - pos); + wwCopy(pt, ecPrePtA(pre, digit >> 1, ec), 2 * ec->f->n); + ecFromA(t, pt, ec, stack); + borrow = WORD_1 - (digit & 1); // обработать остальные цифры - while (--k) + while (pos -= pre->w) { for (i = pre->w; --i;) ecDbl(t, t, ec, stack); - digit = wwGetBits(dd, k * pre->w, pre->w); - ecDblAddA(t, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, stack); - hi = WORD_1 - (digit & 1), hi <<= pre->w - 1; + digit = wwGetBits(dd, pos, pre->w); + digit ^= (WORD_0 - borrow) & mask; + wwCopy(pt, ecPrePtA(pre, digit >> 1, ec), 2 * ec->f->n); + ecSgnA(pt, borrow, ec, stack); + ecDblAddA(t, t, pt, ec, stack); + borrow = WORD_1 - (digit & 1); } - // завершающие удвоения и финишное сложение + // завершающие удвоения for (i = pre->w; i--;) ecDbl(t, t, ec, stack); + // финишное сложение digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); - if (ec->finadd) - ret = ec->finadda(b, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, - stack); - else - { - ecAddA(t, t, ecPrePtA(pre, hi | (digit >> 1), ec), ec, stack); - ret = ecToA(b, t, ec, stack); - } + digit ^= (WORD_0 - borrow) & mask; + wwCopy(pt, ecPrePtA(pre, digit >> 1, ec), 2 * ec->f->n); + ecSgnA(pt, borrow, ec, stack); + ret = (ec->finadda) ? + ec->finadda(b, t, pt, ec, stack) : + (ecAddA(t, t, pt, ec, stack), ecToA(b, t, ec, stack)); // настройка знака ecSgnA(b, neg, ec, stack); // очистка и возврат - CLEAN3(neg, digit, hi); + CLEAN3(neg, digit, borrow); return ret; } diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 94dc9d22..a658a252 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.01.29 +\version 2026.02.04 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -74,7 +74,7 @@ bool_t ecpTest() ec_keep, f_keep, 3 * no, - (sizeof(ec_pre_t) + O_OF_W(64 * 3 * n)) | SIZE_HI, + (sizeof(ec_pre_t) + O_OF_W(32 * 3 * n)) | SIZE_HI, O_OF_W(3 * n), O_OF_W(3 * n), O_OF_W(3 * n), @@ -338,20 +338,8 @@ bool_t ecpTest() // предвычисления по схеме SNZ { size_t i; - // pt[0..64) <- (1, 3, ..., 31, -31, ..., -3, -1)base + // pt[0..32) <- (1, 3, ..., 31)base ecPreSNZ(pre, ec->base, 6, ec, stack); - // pt[i] == -pt[63 - i]? - for (i = 0; i < 32; ++i) - { - ecNeg(pt0, ecPrePt(pre, 63 - i, ec), ec, stack); - ecNeg(pt0, pt0, ec, stack); - ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); - if (ecToA(pt0, pt0, ec, stack)) - { - blobClose(state); - return FALSE; - } - } // pt0 <- \sum_{i = 0}^31 2^{31 - i} pt[i] wwCopy(pt0, ecPrePt(pre, 0, ec), 3 * n); for (i = 1; i < 32; ++i) @@ -377,23 +365,12 @@ bool_t ecpTest() // предвычисления по схеме SNZA { size_t i; - // pt[0..64) <- (1, 3, ..., 31, -31, ..., -3, -1)base + // pt[0..32) <- (1, 3, ..., 31)base if (!ecPreSNZA(pre, ec->base, 6, ec, stack)) { blobClose(state); return FALSE; } - // pt[i] == -pt[63 - i]? - for (i = 0; i < 32; ++i) - { - ecNegA(pt0, ecPrePtA(pre, 63 - i, ec), ec, stack); - ecNegA(pt0, pt0, ec, stack); - if (ecpAddAA(pt0, pt0, ecPrePtA(pre, i, ec), ec, stack)) - { - blobClose(state); - return FALSE; - } - } // pt0 <- pt[1] - pt[0] ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); if (!ecpAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) From ac1b6d2b4bfa7f9ed207858b464827268ba0bc9e Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 4 Feb 2026 17:06:08 +0300 Subject: [PATCH 26/77] Add wwSel() --- include/bee2/math/ww.h | 17 +++++++++++++++++ src/math/ec.c | 32 +++++++++++++++++--------------- src/math/ww.c | 16 +++++++++++++++- test/math/ww_test.c | 11 +++++++++-- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/include/bee2/math/ww.h b/include/bee2/math/ww.h index c08b7234..af10a80a 100644 --- a/include/bee2/math/ww.h +++ b/include/bee2/math/ww.h @@ -332,6 +332,23 @@ size_t wwOctetSize( size_t n /*!< [in] длина a в машинных словах */ ); +/*! \brief Выбор фрагмента + + В слове [count * m]a, которое интерпретируется как объединение count + фрагментов a[0], a[1], ..., a[n - 1] по m слов в каждом, выбирается + и размещается в буфере [m]b фрагмент под номером pos: + b <- a[pos]. + \pre Буферы a и b не пересекаются. + \remark Функция используется при регуляризации. +*/ +void wwSel( + word b[], /*!< [out] фрагмент */ + const word a[], /*!< [in] слово */ + size_t count, /*!< [in] число фрагментов */ + size_t m, /*!< [in] длина фрагмента в машинных словах */ + size_t pos /*!< [in] номер фрагмента */ +); + /* ******************************************************************************* Операции с битами, кодирование diff --git a/src/math/ec.c b/src/math/ec.c index e447b91b..52fc24f3 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -19,12 +19,6 @@ #include "bee2/math/ww.h" #include "bee2/math/zz.h" -/* -******************************************************************************* -Вспомогательные функции и определения -******************************************************************************* -*/ - /* ******************************************************************************* Управление описанием кривой @@ -496,6 +490,8 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, register word borrow; register bool_t ret; size_t mb; + size_t pre_count; + size_t pt_size; size_t mask; size_t pos; size_t i; @@ -513,6 +509,8 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, // размерности mb = wwBitSize(ec->order, m); ASSERT(mb >= 2 * pre->w); + pre_count = SIZE_1 << (pre->w - 1); + pt_size = ec->d * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, @@ -525,7 +523,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, // обработать старшую цифру pos = (mb - 1) / pre->w, pos *= pre->w; digit = wwGetBits(dd, pos, mb - pos); - wwCopy(t, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); + wwSel(t, pre->pts, pre_count, pt_size, digit >> 1); borrow = WORD_1 - (digit & 1); // обработать остальные цифры while (pos -= pre->w) @@ -534,7 +532,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; - wwCopy(pt, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); + wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgn(pt, borrow, ec, stack); ecAdd(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); @@ -546,7 +544,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; - wwCopy(pt, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); + wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgn(pt, borrow, ec, stack); ret = (ec->finadd) ? ec->finadd(b, t, pt, ec, stack) : @@ -569,7 +567,7 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) #define ecMulPreSNZA_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ -/* pt */ O_OF_W(ec_d * n) +/* pt */ O_OF_W(2 * n) bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) @@ -579,23 +577,27 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, register word borrow; register bool_t ret; size_t mb; + size_t pre_count; + size_t pt_size; size_t mask; size_t pos; size_t i; word* dd; /* [m] */ word* t; /* [ec->d * ec->f->n] */ - word* pt; /* [ec->d * ec->f->n] */ + word* pt; /* [2 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_snz); + ASSERT(pre->type == ec_pre_snza); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); ASSERT(mb >= 2 * pre->w); + pre_count = SIZE_1 << (pre->w - 1); + pt_size = 2 * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, @@ -608,7 +610,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, // обработать старшую цифру pos = (mb - 1) / pre->w, pos *= pre->w; digit = wwGetBits(dd, pos, mb - pos); - wwCopy(pt, ecPrePtA(pre, digit >> 1, ec), 2 * ec->f->n); + wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecFromA(t, pt, ec, stack); borrow = WORD_1 - (digit & 1); // обработать остальные цифры @@ -618,7 +620,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, ecDbl(t, t, ec, stack); digit = wwGetBits(dd, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; - wwCopy(pt, ecPrePtA(pre, digit >> 1, ec), 2 * ec->f->n); + wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgnA(pt, borrow, ec, stack); ecDblAddA(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); @@ -630,7 +632,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; - wwCopy(pt, ecPrePtA(pre, digit >> 1, ec), 2 * ec->f->n); + wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgnA(pt, borrow, ec, stack); ret = (ec->finadda) ? ec->finadda(b, t, pt, ec, stack) : diff --git a/src/math/ww.c b/src/math/ww.c index 0cb28a5f..ac276d4f 100644 --- a/src/math/ww.c +++ b/src/math/ww.c @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.01.27 +\version 2026.02.04 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -295,6 +295,20 @@ size_t wwOctetSize(const word a[], size_t n) } } +void wwSel(word b[], const word a[], size_t count, size_t m, size_t pos) +{ + size_t i, j; + ASSERT(wwIsDisjoint2(b, m, a, count * m)); + ASSERT(pos < count); + wwSetZero(b, m); + for (i = 0; i < count; ++i) + { + register word mask = wordEq0M(i, pos); + for (j = 0; j < m; ++j) + b[j] |= a[i * m + j] & mask; + } +} + /* ******************************************************************************* Операции с отдельными битами diff --git a/test/math/ww_test.c b/test/math/ww_test.c index 950da459..843fe7b5 100644 --- a/test/math/ww_test.c +++ b/test/math/ww_test.c @@ -4,7 +4,7 @@ \brief Tests for operations on arbitrary length words \project bee2/test \created 2023.03.31 -\version 2025.07.27 +\version 2026.02.04 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -51,8 +51,15 @@ bool_t wwTest() wwCmp(a, b, 8) <= 0 || FAST(wwCmp)(a, b, 8) <= 0 || wwCmp2(a, 7, b, 8) >= 0 || FAST(wwCmp2)(a, 7, b, 8) >= 0) return FALSE; + // выбор фрагмента + wwSetZero(b, 8); + b[3] = 2, b[4] = 4, b[5] = 5; + wwSetZero(c, 8); + wwSel(c, b, 2, 3, 1); + if (!wwEq(c, b + 3, 3) || !wwIsZero(c + 3, 8 - 3)) + return FALSE; // операции с битами - ASSERT(wwIsRepW(b, 8, 0x36)); + wwRepW(b, 8, 0x36); if (wwTestBit(b, 0) || !wwTestBit(b, 1) || wwTestBit(b, 0 + B_PER_W) || !wwTestBit(b, 1 + B_PER_W) || wwTestBit(b, B_PER_W - 1) || wwTestBit(b, 2 * B_PER_W - 1) || From a8331a5c91cdaf3d313b589574bedc9f8cfcadec Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 4 Feb 2026 18:08:37 +0300 Subject: [PATCH 27/77] Split math/ecp into parts --- include/bee2/math/ec.h | 4 + src/CMakeLists.txt | 4 +- src/math/ecp/ecp_a.c | 215 ++++++++++++++ src/math/{ecp.c => ecp/ecp_j.c} | 490 +------------------------------ src/math/ecp/ecp_lcl.h | 85 ++++++ src/math/ecp/ecp_misc.c | 283 ++++++++++++++++++ win/vs09/libbee2.vcproj | 24 +- win/vs15/libbee2.vcxproj | 5 +- win/vs15/libbee2.vcxproj.filters | 18 +- 9 files changed, 634 insertions(+), 494 deletions(-) create mode 100644 src/math/ecp/ecp_a.c rename src/math/{ecp.c => ecp/ecp_j.c} (77%) create mode 100644 src/math/ecp/ecp_lcl.h create mode 100644 src/math/ecp/ecp_misc.c diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 9188f7e0..fe2b0b95 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -866,6 +866,8 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); условных переходов; - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, выполняется без условных переходов; + - ec->sgn != 0 и функция ec->sgn регулярна: знак точки устанавливается + без условных переходов; - справедливо, по крайней мере, одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; b) ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение @@ -911,6 +913,8 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных переходов; + - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается + без условных переходов; - справедливо, по крайней мере, одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cf0bc60..bdebe179 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,7 +72,9 @@ set(src crypto/pfok.c math/ec.c math/ec2.c - math/ecp.c + math/ecp/ecp_a.c + math/ecp/ecp_j.c + math/ecp/ecp_misc.c math/gf2.c math/gfp.c math/pp/pp_etc.c diff --git a/src/math/ecp/ecp_a.c b/src/math/ecp/ecp_a.c new file mode 100644 index 00000000..95017718 --- /dev/null +++ b/src/math/ecp/ecp_a.c @@ -0,0 +1,215 @@ +/* +******************************************************************************* +\file ecp_a.c +\brief Elliptic curves over prime fields: affine coordinates +\project bee2 [cryptographic library] +\created 2012.06.26 +\version 2026.02.04 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include "bee2/core/util.h" +#include "bee2/math/ecp.h" +#include "bee2/math/gfp.h" +#include "bee2/math/ww.h" +#include "bee2/math/zz.h" +#include "ecp_lcl.h" + +/* +******************************************************************************* +Арифметика аффинных точек + +Сложение A <- A + A: + 1D + 1S + 1M + 6add \approx 102M + +Удвоение A <- 2A: + 1D + 2S + 1M + 5add + 1*3 + 1*2 \approx 103M +******************************************************************************* +*/ + +#define ecpIsOnA_local(n)\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n) + +bool_t ecpIsOnA(const word a[], const ec_o* ec, void* stack) +{ + size_t n; + word* t1; /* [n] */ + word* t2; /* [n] */ + // pre + ASSERT(ecIsOperable(ec)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpIsOnA_local(n), SIZE_0, SIZE_MAX, + &t1, &t2, &stack); + // xa, ya \in ec->f? + if (!zmIsIn(ecX(a), ec->f) || !zmIsIn(ecY(a, n), ec->f)) + return FALSE; + // t1 <- (xa^2 + A)xa + B + qrSqr(t1, ecX(a), ec->f, stack); + zmAdd(t1, t1, ec->A, ec->f); + qrMul(t1, t1, ecX(a), ec->f, stack); + zmAdd(t1, t1, ec->B, ec->f); + // t2 <- ya^2 + qrSqr(t2, ecY(a, n), ec->f, stack); + // t1 == t2? + return qrCmp(t1, t2, ec->f) == 0; +} + +size_t ecpIsOnA_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpIsOnA_local(n), + f_deep, + SIZE_MAX); +} + +void ecpNegA(word b[], const word a[], const ec_o* ec) +{ + ASSERT(ecIsOperable(ec)); + ASSERT(ecpSeemsOnA(a, ec)); + ASSERT(wwIsSameOrDisjoint(a, b, 2 * ec->f->n)); + // (xb, yb) <- (xa, -ya) + qrCopy(ecX(b), ecX(a), ec->f); + zmNeg(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); +} + +#define ecpAddAA_local(n)\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n),\ +/* t3 */ O_OF_W(n) + +bool_t ecpAddAA(word c[], const word a[], const word b[], const ec_o* ec, + void* stack) +{ + size_t n; + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecpSeemsOnA(a, ec)); + ASSERT(ecpSeemsOnA(b, ec)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpAddAA_local(n), SIZE_0, SIZE_MAX, + &t1, &t2, &t3, &stack); + // xa != xb? + if (qrCmp(ecX(a), ecX(b), ec->f) != 0) + { + // t1 <- xa - xb + zmSub(t1, ecX(a), ecX(b), ec->f); + // t2 <- ya - yb + zmSub(t2, ecY(a, n), ecY(b, n), ec->f); + } + else + { + // ya != yb или yb == 0 => a == -b => a + b == O + if (qrCmp(ecY(a, n), ecY(b, n), ec->f) != 0 || + qrIsZero(ecY(b, n), ec->f)) + return FALSE; + // t2 <- 3 xa^2 + A + qrSqr(t1, ecX(a), ec->f, stack); + gfpDouble(t2, t1, ec->f); + zmAdd(t2, t2, t1, ec->f); + zmAdd(t2, t2, ec->A, ec->f); + // t1 <- 2 ya + gfpDouble(t1, ecY(a, n), ec->f); + } + // t2 <- t2 / t1 = \lambda + qrDiv(t2, t2, t1, ec->f, stack); + // t1 <- \lambda^2 - xa - xb = xc + qrSqr(t1, t2, ec->f, stack); + zmSub(t1, t1, ecX(a), ec->f); + zmSub(t1, t1, ecX(b), ec->f); + // t3 <- xa - xc + zmSub(t3, ecX(a), t1, ec->f); + // t2 <- \lambda(xa - xc) - ya + qrMul(t2, t2, t3, ec->f, stack); + zmSub(t2, t2, ecY(a, n), ec->f); + // выгрузить результат + qrCopy(ecX(c), t1, ec->f); + qrCopy(ecY(c, n), t2, ec->f); + return TRUE; +} + +size_t ecpAddAA_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpAddAA_local(n), + f_deep, + SIZE_MAX); +} + +#define ecpSubAA_local(n)\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n),\ +/* t3 */ O_OF_W(n) + +bool_t ecpSubAA(word c[], const word a[], const word b[], const ec_o* ec, + void* stack) +{ + size_t n; + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecpSeemsOnA(a, ec)); + ASSERT(ecpSeemsOnA(b, ec)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpSubAA_local(n), SIZE_0, SIZE_MAX, + &t1, &t2, &t3, &stack); + // xa != xb? + if (qrCmp(ecX(a), ecX(b), ec->f) != 0) + { + // t1 <- xa - xb + zmSub(t1, ecX(a), ecX(b), ec->f); + // t2 <- ya + yb + zmAdd(t2, ecY(a, n), ecY(b, n), ec->f); + } + else + { + // ya == yb => a == b => a - b == O + if (qrCmp(ecY(a, n), ecY(b, n), ec->f) == 0) + return FALSE; + // здесь должно быть a == -b => a - b = 2a + // t2 <- 3 xa^2 + A + qrSqr(t1, ecX(a), ec->f, stack); + gfpDouble(t2, t1, ec->f); + zmAdd(t2, t2, t1, ec->f); + zmAdd(t2, t2, ec->A, ec->f); + // t1 <- 2 ya + gfpDouble(t1, ecY(a, n), ec->f); + } + // t2 <- t2 / t1 = \lambda + qrDiv(t2, t2, t1, ec->f, stack); + // t1 <- \lambda^2 - xa - xb = xc + qrSqr(t1, t2, ec->f, stack); + zmSub(t1, t1, ecX(a), ec->f); + zmSub(t1, t1, ecX(b), ec->f); + // t3 <- xa - xc + zmSub(t3, ecX(a), t1, ec->f); + // t2 <- \lambda(xa - xc) - ya + qrMul(t2, t2, t3, ec->f, stack); + zmSub(t2, t2, ecY(a, n), ec->f); + // выгрузить результат + qrCopy(ecX(c), t1, ec->f); + qrCopy(ecY(c, n), t2, ec->f); + return TRUE; +} + +size_t ecpSubAA_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpSubAA_local(n), + f_deep, + SIZE_MAX); +} + diff --git a/src/math/ecp.c b/src/math/ecp/ecp_j.c similarity index 77% rename from src/math/ecp.c rename to src/math/ecp/ecp_j.c index 0a3cda83..d78a444a 100644 --- a/src/math/ecp.c +++ b/src/math/ecp/ecp_j.c @@ -1,27 +1,25 @@ /* ******************************************************************************* -\file ecp.c -\brief Elliptic curves over prime fields +\file ecp_j.c +\brief Elliptic curves over prime fields: Jacobian coordinates \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.02.03 +\version 2026.02.04 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* */ -#include "bee2/core/mem.h" #include "bee2/core/util.h" #include "bee2/math/ecp.h" #include "bee2/math/gfp.h" -#include "bee2/math/pri.h" #include "bee2/math/ww.h" -#include "bee2/math/zm.h" #include "bee2/math/zz.h" +#include "ecp_lcl.h" /* ******************************************************************************* -Общие положения +Арифметика якобиевых точек Используются якобиевы (Jacobian, J) проективные координаты: x = X / Z^2, y = Y / Z^3, @@ -51,13 +49,6 @@ ******************************************************************************* */ -#define ecpSeemsOnA(a, ec)\ - (zmIsIn(ecX(a), (ec)->f) && zmIsIn(ecY(a, (ec)->f->n), (ec)->f)) - -#define ecpSeemsOnJ(a, ec)\ - (ecIsO(a, ec) ||\ - (ecpSeemsOnA(a, ec) && zmIsIn(ecZ(a, (ec)->f->n), (ec)->f))) - /* ******************************************************************************* Базовые операции @@ -1378,474 +1369,3 @@ size_t ecpCreateJ_deep(size_t n, size_t f_deep) ecpFinAddA_deep(n, f_deep)), SIZE_MAX); } - -/* -******************************************************************************* -Свойства кривой -******************************************************************************* -*/ - -#define ecpIsValid_local(n)\ -/* t1 */ O_OF_W(n),\ -/* t2 */ O_OF_W(n),\ -/* t3 */ O_OF_W(n) - -bool_t ecpIsValid(const ec_o* ec, void* stack) -{ - size_t n; - word* t1; /* [n] */ - word* t2; /* [n] */ - word* t3; /* [n] */ - // кривая работоспособна? - if (!ecIsOperable2(ec)) - return FALSE; - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpIsValid_local(n), SIZE_0, SIZE_MAX, - &t1, &t2, &t3, &stack); - // поле ec->f корректно? - // f->mod > 3? - // ec->deep >= ec->f->deep? - // A, B \in ec->f? - if (!gfpIsValid(ec->f, stack) || - wwCmpW(ec->f->mod, n, 3) <= 0 || - ec->deep < ec->f->deep || - !zmIsIn(ec->A, ec->f) || - !zmIsIn(ec->B, ec->f)) - return FALSE; - // t1 <- 4 A^3 - qrSqr(t1, ec->A, ec->f, stack); - qrMul(t1, t1, ec->A, ec->f, stack); - gfpDouble(t1, t1, ec->f); - gfpDouble(t1, t1, ec->f); - // t2 <- 27 B^2 - qrSqr(t2, ec->B, ec->f, stack); - gfpDouble(t3, t2, ec->f); - zmAdd(t2, t2, t3, ec->f); - gfpDouble(t3, t2, ec->f); - zmAdd(t2, t2, t3, ec->f); - gfpDouble(t3, t2, ec->f); - zmAdd(t2, t2, t3, ec->f); - // t1 <- t1 + t2 [4 A^3 + 27 B^2 -- дискриминант] - zmAdd(t1, t1, t2, ec->f); - // t1 == 0 => сингулярная кривая - return !qrIsZero(t1, ec->f); -} - -size_t ecpIsValid_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpIsValid_local(n), - utilMax(2, - f_deep, - gfpIsValid_deep(n)), - SIZE_MAX); -} - -#define ecpGroupSeemsValid_local(n)\ -/* t1 */ O_OF_W(n + 1),\ -/* t2 */ O_OF_W(n + 2),\ -/* t3 */ O_OF_W(2 * n) - -bool_t ecpGroupSeemsValid(const ec_o* ec, void* stack) -{ - size_t n; - int cmp; - word w; - word* t1; /* [n + 1] */ - word* t2; /* [n + 2] */ - word* t3; /* [2 * n] */ - // pre - ASSERT(ecIsOperable(ec)); - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpGroupSeemsValid_local(n), SIZE_0, SIZE_MAX, - &t1, &t2, &t3, &stack); - // ecGroupIsOperable(ec) == TRUE? base \in ec? - if (!ecGroupIsOperable(ec) || !ecpIsOnA(ec->base, ec, stack)) - return FALSE; - // [n + 2]t1 <- order * cofactor - t1[n + 1] = zzMulW(t1, ec->order, n + 1, ec->cofactor); - // t1 <- |t1 - (p + 1)| - if (zzSubW2(t1, n + 2, 1)) - return FALSE; - if (wwCmp2(t1, n + 2, ec->f->mod, n) >= 0) - zzSubW2(t1 + n, 2, zzSub2(t1, ec->f->mod, n)); - else - zzSub(t1, ec->f->mod, t1, n); - // n <- длина t1 - n = wwWordSize(t1, n + 2); - // n > ec->f->n => t1^2 > 4 p - if (n > ec->f->n) - return FALSE; - // [2n]t2 <- ([n]t1)^2 - zzSqr(t2, t1, n, stack); - // условие Хассе: t2 <= 4 p? - w = wwGetBits(t2, 0, 2); - wwShLo(t2, 2 * n, 2); - cmp = wwCmp2(t2, 2 * n, ec->f->mod, ec->f->n); - if (cmp > 0 || cmp == 0 && w != 0) - return FALSE; - // все нормально - return TRUE; -} - -size_t ecpGroupSeemsValid_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpGroupSeemsValid_local(n), - utilMax(2, - ecpIsOnA_deep(n, f_deep), - zzSqr_deep(n)), - SIZE_MAX); -} - -#define ecpGroupIsSafe_local(n1)\ -/* t1 */ O_OF_W(n1),\ -/* t2 */ O_OF_W(n1) - -bool_t ecpGroupIsSafe(const ec_o* ec, size_t mov_threshold, void* stack) -{ - size_t n1; - word* t1; /* [n1] */ - word* t2; /* [n1] */ - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(ecGroupIsOperable(ec)); - // разметить стек - n1 = ec->f->n + 1; - memSlice(stack, - ecpGroupIsSafe_local(n1), SIZE_0, SIZE_MAX, - &t1, &t2, &stack); - // order -- простое? - n1 = wwWordSize(ec->order, n1); - if (!priIsPrime(ec->order, n1, stack)) - return FALSE; - // order == p? - if (wwCmp2(ec->f->mod, ec->f->n, ec->order, n1) == 0) - return FALSE; - // проверка MOV - if (mov_threshold) - { - zzMod(t1, ec->f->mod, ec->f->n, ec->order, n1, stack); - wwCopy(t2, t1, n1); - if (wwCmpW(t2, n1, 1) == 0) - return FALSE; - while (--mov_threshold) - { - zzMulMod(t2, t2, t1, ec->order, n1, stack); - if (wwCmpW(t2, n1, 1) == 0) - return FALSE; - } - } - // все нормально - return TRUE; -} - -size_t ecpGroupIsSafe_deep(size_t n) -{ - const size_t n1 = n + 1; - return memSliceSize( - ecpGroupIsSafe_local(n1), - utilMax(2, - priIsPrime_deep(n1), - zzMod_deep(n, n1), - zzMulMod_deep(n1)), - SIZE_MAX); -} - -/* -******************************************************************************* -Арифметика аффинных точек - -Сложение A <- A + A: - 1D + 1S + 1M + 6add \approx 102M - -Удвоение A <- 2A: - 1D + 2S + 1M + 5add + 1*3 + 1*2 \approx 103M -******************************************************************************* -*/ - -#define ecpIsOnA_local(n)\ -/* t1 */ O_OF_W(n),\ -/* t2 */ O_OF_W(n) - -bool_t ecpIsOnA(const word a[], const ec_o* ec, void* stack) -{ - size_t n; - word* t1; /* [n] */ - word* t2; /* [n] */ - // pre - ASSERT(ecIsOperable(ec)); - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpIsOnA_local(n), SIZE_0, SIZE_MAX, - &t1, &t2, &stack); - // xa, ya \in ec->f? - if (!zmIsIn(ecX(a), ec->f) || !zmIsIn(ecY(a, n), ec->f)) - return FALSE; - // t1 <- (xa^2 + A)xa + B - qrSqr(t1, ecX(a), ec->f, stack); - zmAdd(t1, t1, ec->A, ec->f); - qrMul(t1, t1, ecX(a), ec->f, stack); - zmAdd(t1, t1, ec->B, ec->f); - // t2 <- ya^2 - qrSqr(t2, ecY(a, n), ec->f, stack); - // t1 == t2? - return qrCmp(t1, t2, ec->f) == 0; -} - -size_t ecpIsOnA_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpIsOnA_local(n), - f_deep, - SIZE_MAX); -} - -void ecpNegA(word b[], const word a[], const ec_o* ec) -{ - ASSERT(ecIsOperable(ec)); - ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(wwIsSameOrDisjoint(a, b, 2 * ec->f->n)); - // (xb, yb) <- (xa, -ya) - qrCopy(ecX(b), ecX(a), ec->f); - zmNeg(ecY(b, ec->f->n), ecY(a, ec->f->n), ec->f); -} - -#define ecpAddAA_local(n)\ -/* t1 */ O_OF_W(n),\ -/* t2 */ O_OF_W(n),\ -/* t3 */ O_OF_W(n) - -bool_t ecpAddAA(word c[], const word a[], const word b[], const ec_o* ec, - void* stack) -{ - size_t n; - word* t1; /* [n] */ - word* t2; /* [n] */ - word* t3; /* [n] */ - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(ecpSeemsOnA(b, ec)); - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpAddAA_local(n), SIZE_0, SIZE_MAX, - &t1, &t2, &t3, &stack); - // xa != xb? - if (qrCmp(ecX(a), ecX(b), ec->f) != 0) - { - // t1 <- xa - xb - zmSub(t1, ecX(a), ecX(b), ec->f); - // t2 <- ya - yb - zmSub(t2, ecY(a, n), ecY(b, n), ec->f); - } - else - { - // ya != yb или yb == 0 => a == -b => a + b == O - if (qrCmp(ecY(a, n), ecY(b, n), ec->f) != 0 || - qrIsZero(ecY(b, n), ec->f)) - return FALSE; - // t2 <- 3 xa^2 + A - qrSqr(t1, ecX(a), ec->f, stack); - gfpDouble(t2, t1, ec->f); - zmAdd(t2, t2, t1, ec->f); - zmAdd(t2, t2, ec->A, ec->f); - // t1 <- 2 ya - gfpDouble(t1, ecY(a, n), ec->f); - } - // t2 <- t2 / t1 = \lambda - qrDiv(t2, t2, t1, ec->f, stack); - // t1 <- \lambda^2 - xa - xb = xc - qrSqr(t1, t2, ec->f, stack); - zmSub(t1, t1, ecX(a), ec->f); - zmSub(t1, t1, ecX(b), ec->f); - // t3 <- xa - xc - zmSub(t3, ecX(a), t1, ec->f); - // t2 <- \lambda(xa - xc) - ya - qrMul(t2, t2, t3, ec->f, stack); - zmSub(t2, t2, ecY(a, n), ec->f); - // выгрузить результат - qrCopy(ecX(c), t1, ec->f); - qrCopy(ecY(c, n), t2, ec->f); - return TRUE; -} - -size_t ecpAddAA_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpAddAA_local(n), - f_deep, - SIZE_MAX); -} - -#define ecpSubAA_local(n)\ -/* t1 */ O_OF_W(n),\ -/* t2 */ O_OF_W(n),\ -/* t3 */ O_OF_W(n) - -bool_t ecpSubAA(word c[], const word a[], const word b[], const ec_o* ec, - void* stack) -{ - size_t n; - word* t1; /* [n] */ - word* t2; /* [n] */ - word* t3; /* [n] */ - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(ecpSeemsOnA(b, ec)); - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpSubAA_local(n), SIZE_0, SIZE_MAX, - &t1, &t2, &t3, &stack); - // xa != xb? - if (qrCmp(ecX(a), ecX(b), ec->f) != 0) - { - // t1 <- xa - xb - zmSub(t1, ecX(a), ecX(b), ec->f); - // t2 <- ya + yb - zmAdd(t2, ecY(a, n), ecY(b, n), ec->f); - } - else - { - // ya == yb => a == b => a - b == O - if (qrCmp(ecY(a, n), ecY(b, n), ec->f) == 0) - return FALSE; - // здесь должно быть a == -b => a - b = 2a - // t2 <- 3 xa^2 + A - qrSqr(t1, ecX(a), ec->f, stack); - gfpDouble(t2, t1, ec->f); - zmAdd(t2, t2, t1, ec->f); - zmAdd(t2, t2, ec->A, ec->f); - // t1 <- 2 ya - gfpDouble(t1, ecY(a, n), ec->f); - } - // t2 <- t2 / t1 = \lambda - qrDiv(t2, t2, t1, ec->f, stack); - // t1 <- \lambda^2 - xa - xb = xc - qrSqr(t1, t2, ec->f, stack); - zmSub(t1, t1, ecX(a), ec->f); - zmSub(t1, t1, ecX(b), ec->f); - // t3 <- xa - xc - zmSub(t3, ecX(a), t1, ec->f); - // t2 <- \lambda(xa - xc) - ya - qrMul(t2, t2, t3, ec->f, stack); - zmSub(t2, t2, ecY(a, n), ec->f); - // выгрузить результат - qrCopy(ecX(c), t1, ec->f); - qrCopy(ecY(c, n), t2, ec->f); - return TRUE; -} - -size_t ecpSubAA_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpSubAA_local(n), - f_deep, - SIZE_MAX); -} - -/* -******************************************************************************* -Алгоритм SWU - -\todo Регуляризировать (qrIsUnitySafe). -******************************************************************************* -*/ - -#define ecpSWU_local(n)\ -/* t */ O_OF_W(n),\ -/* x1 */ O_OF_W(n),\ -/* x2 */ O_OF_W(n),\ -/* y */ O_OF_W(n),\ -/* s */ O_OF_W(n) - -void ecpSWU(word b[], const word a[], const ec_o* ec, void* stack) -{ - size_t n; - register size_t mask; - word* t; /* [n] */ - word* x1; /* [n] */ - word* x2; /* [n] */ - word* y; /* [n] */ - word* s; /* [n] */ - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(zmIsIn(a, ec->f)); - ASSERT(wwGetBits(ec->f->mod, 0, 2) == 3); - ASSERT(!qrIsZero(ec->A, ec->f) && !qrIsZero(ec->B, ec->f)); - // разметить стек - n = ec->f->n; - memSlice(stack, - ecpSWU_local(n), SIZE_0, SIZE_MAX, - &t, &x1, &x2, &y, &s, &stack); - // t <- -a^2 - qrSqr(t, a, ec->f, stack); - zmNeg(t, t, ec->f); - // s <- p - 2 - wwCopy(s, ec->f->mod, n); - zzSubW2(s, n, 2); - // x1 <- -B(1 + t + t^2)(A(t + t^2))^{p - 2} - qrSqr(x2, t, ec->f, stack); - qrAdd(x2, x2, t, ec->f); - qrMul(x1, x2, ec->A, ec->f, stack); - qrPower(x1, x1, s, n, ec->f, stack); - qrAddUnity(x2, x2, ec->f); - qrMul(x1, x1, x2, ec->f, stack); - qrMul(x1, x1, ec->B, ec->f, stack); - zmNeg(x1, x1, ec->f); - // y <- (x1)^3 + A x1 + B - qrSqr(x2, x1, ec->f, stack); - qrMul(x2, x2, x1, ec->f, stack); - qrMul(y, x1, ec->A, ec->f, stack); - qrAdd(y, y, x2, ec->f); - qrAdd(y, y, ec->B, ec->f); - // x2 <- x1 t - qrMul(x2, x1, t, ec->f, stack); - // t <- y^{(p - 1) - (p + 1) / 4} = y^{s - (p - 3) / 4} - wwCopy(t, ec->f->mod, n); - wwShLo(t, n, 2); - zzSub(s, s, t, n); - qrPower(t, y, s, n, ec->f, stack); - // s <- a^3 y - qrSqr(s, a, ec->f, stack); - qrMul(s, s, a, ec->f, stack); - qrMul(s, s, y, ec->f, stack); - // mask <- t^2 y == 1 ? 0 : -1; - qrSqr(b, t, ec->f, stack); - qrMul(b, b, y, ec->f, stack); - mask = qrIsUnity(b, ec->f) - SIZE_1; - // b <- mask == 0 ? (x1, t y) : (x2, t s) - qrCopy(ecX(b), x1 + (mask & n), ec->f); - qrMul(ecY(b, n), t, y + (mask & n), ec->f, stack); - // очистка - CLEAN(mask); -} - -size_t ecpSWU_deep(size_t n, size_t f_deep) -{ - return memSliceSize( - ecpSWU_local(n), - utilMax(2, - f_deep, - qrPower_deep(n, n, f_deep)), - SIZE_MAX); -} - -/* -******************************************************************************* -Предвычисления -******************************************************************************* -*/ - -void ecpPreSNZ(word pre[], const word a[], size_t w, const struct ec_o* ec, - void* stack) -{ -} diff --git a/src/math/ecp/ecp_lcl.h b/src/math/ecp/ecp_lcl.h new file mode 100644 index 00000000..c5411739 --- /dev/null +++ b/src/math/ecp/ecp_lcl.h @@ -0,0 +1,85 @@ +/* +******************************************************************************* +\file ecp_lcl.h +\brief Elliptic curves over prime fields: local definitions +\project bee2 [cryptographic library] +\created 2012.06.26 +\version 2026.02.04 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#ifndef __ECP_LCL_H +#define __ECP_LCL_H + +#include "bee2/math/ecp.h" +#include "bee2/math/zm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +******************************************************************************* +Общие положения + +Реализованы операции с аффинными (A) точками (x, y) и якобиевыми (Jacobian, J) +проективными точками (X : Y : Z): + x = X / Z^2, y = Y / Z^3, + -(X : Y : Z) = (X : -Y : Z). + +Якобиева точка с нулевой Z-координатой считается бесконечно удаленной. + +Реализованные арифметические операции с точками (см. модули ecp_a.c, ecp_j.c) +представлены на сайте http://www.hyperelliptic.org/EFD. Там же можно найти +соглашения по обозначению сложности операций. В этих обозначениях фигурируют +следующие формальные выражения: + add -- сложение или вычитание \mod p, + c -- умножение на малую константу c (2,3,...) \mod p, + half -- умножение на 2^{-1} \mod p, + *A -- умножение на коэффициент A \mod p, + S -- возведение в квадрат \mod p, + M -- умножение \mod p, + D -- деление \mod p. + +При общей оценке сложности считается, что 1D = 100M и 1S = 1M. Аддитивные +операции в базовом поле игнорируются. + +Основные операции с точками: + A <- A + A -- сложение аффинных точек, + A <- 2A -- удвоение аффинной точки; + J <- J + J -- сложение якобиевых точек; + J <- J + A -- добавление к якобиевой точке аффинной; + J <- 2J -- удвоение якобиевой точки; + J <- 2A -- удвоение аффинной точки с переходом к якобиевым координатам; + J <- 3J -- утроение якобиевой точки; + J <- 2J + A -- удвоение якобиевой точки со сложением с аффинной точкой. +******************************************************************************* +*/ + +/* +******************************************************************************* +Макросы + +В макросе ecpSeemsOnA() проверяется, что две координаты входной (аффинной) +точки лежат в базовом поле. + +В макросе ecpSeemsOnJ() проверяется, что три координаты входной (проективной) +точки лежат в базовом поле. Если точка является бесконечно удаленной, то +первые две координаты не проверяются. +******************************************************************************* +*/ + +#define ecpSeemsOnA(a, ec)\ + (zmIsIn(ecX(a), (ec)->f) && zmIsIn(ecY(a, (ec)->f->n), (ec)->f)) + +#define ecpSeemsOnJ(a, ec)\ + (ecIsO(a, ec) ||\ + (ecpSeemsOnA(a, ec) && zmIsIn(ecZ(a, (ec)->f->n), (ec)->f))) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __ECP_LCL_H */ diff --git a/src/math/ecp/ecp_misc.c b/src/math/ecp/ecp_misc.c new file mode 100644 index 00000000..38c44691 --- /dev/null +++ b/src/math/ecp/ecp_misc.c @@ -0,0 +1,283 @@ +/* +******************************************************************************* +\file ecp_misc.c +\brief Elliptic curves over prime fields: miscellaneous functions +\project bee2 [cryptographic library] +\created 2012.06.26 +\version 2026.02.04 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include "bee2/core/util.h" +#include "bee2/math/ecp.h" +#include "bee2/math/gfp.h" +#include "bee2/math/pri.h" +#include "bee2/math/ww.h" +#include "bee2/math/zm.h" +#include "bee2/math/zz.h" + +/* +******************************************************************************* +Свойства кривой +******************************************************************************* +*/ + +#define ecpIsValid_local(n)\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n),\ +/* t3 */ O_OF_W(n) + +bool_t ecpIsValid(const ec_o* ec, void* stack) +{ + size_t n; + word* t1; /* [n] */ + word* t2; /* [n] */ + word* t3; /* [n] */ + // кривая работоспособна? + if (!ecIsOperable2(ec)) + return FALSE; + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpIsValid_local(n), SIZE_0, SIZE_MAX, + &t1, &t2, &t3, &stack); + // поле ec->f корректно? + // f->mod > 3? + // ec->deep >= ec->f->deep? + // A, B \in ec->f? + if (!gfpIsValid(ec->f, stack) || + wwCmpW(ec->f->mod, n, 3) <= 0 || + ec->deep < ec->f->deep || + !zmIsIn(ec->A, ec->f) || + !zmIsIn(ec->B, ec->f)) + return FALSE; + // t1 <- 4 A^3 + qrSqr(t1, ec->A, ec->f, stack); + qrMul(t1, t1, ec->A, ec->f, stack); + gfpDouble(t1, t1, ec->f); + gfpDouble(t1, t1, ec->f); + // t2 <- 27 B^2 + qrSqr(t2, ec->B, ec->f, stack); + gfpDouble(t3, t2, ec->f); + zmAdd(t2, t2, t3, ec->f); + gfpDouble(t3, t2, ec->f); + zmAdd(t2, t2, t3, ec->f); + gfpDouble(t3, t2, ec->f); + zmAdd(t2, t2, t3, ec->f); + // t1 <- t1 + t2 [4 A^3 + 27 B^2 -- дискриминант] + zmAdd(t1, t1, t2, ec->f); + // t1 == 0 => сингулярная кривая + return !qrIsZero(t1, ec->f); +} + +size_t ecpIsValid_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpIsValid_local(n), + utilMax(2, + f_deep, + gfpIsValid_deep(n)), + SIZE_MAX); +} + +#define ecpGroupSeemsValid_local(n)\ +/* t1 */ O_OF_W(n + 1),\ +/* t2 */ O_OF_W(n + 2),\ +/* t3 */ O_OF_W(2 * n) + +bool_t ecpGroupSeemsValid(const ec_o* ec, void* stack) +{ + size_t n; + int cmp; + word w; + word* t1; /* [n + 1] */ + word* t2; /* [n + 2] */ + word* t3; /* [2 * n] */ + // pre + ASSERT(ecIsOperable(ec)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpGroupSeemsValid_local(n), SIZE_0, SIZE_MAX, + &t1, &t2, &t3, &stack); + // ecGroupIsOperable(ec) == TRUE? base \in ec? + if (!ecGroupIsOperable(ec) || !ecpIsOnA(ec->base, ec, stack)) + return FALSE; + // [n + 2]t1 <- order * cofactor + t1[n + 1] = zzMulW(t1, ec->order, n + 1, ec->cofactor); + // t1 <- |t1 - (p + 1)| + if (zzSubW2(t1, n + 2, 1)) + return FALSE; + if (wwCmp2(t1, n + 2, ec->f->mod, n) >= 0) + zzSubW2(t1 + n, 2, zzSub2(t1, ec->f->mod, n)); + else + zzSub(t1, ec->f->mod, t1, n); + // n <- длина t1 + n = wwWordSize(t1, n + 2); + // n > ec->f->n => t1^2 > 4 p + if (n > ec->f->n) + return FALSE; + // [2n]t2 <- ([n]t1)^2 + zzSqr(t2, t1, n, stack); + // условие Хассе: t2 <= 4 p? + w = wwGetBits(t2, 0, 2); + wwShLo(t2, 2 * n, 2); + cmp = wwCmp2(t2, 2 * n, ec->f->mod, ec->f->n); + if (cmp > 0 || cmp == 0 && w != 0) + return FALSE; + // все нормально + return TRUE; +} + +size_t ecpGroupSeemsValid_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpGroupSeemsValid_local(n), + utilMax(2, + ecpIsOnA_deep(n, f_deep), + zzSqr_deep(n)), + SIZE_MAX); +} + +#define ecpGroupIsSafe_local(n1)\ +/* t1 */ O_OF_W(n1),\ +/* t2 */ O_OF_W(n1) + +bool_t ecpGroupIsSafe(const ec_o* ec, size_t mov_threshold, void* stack) +{ + size_t n1; + word* t1; /* [n1] */ + word* t2; /* [n1] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + // разметить стек + n1 = ec->f->n + 1; + memSlice(stack, + ecpGroupIsSafe_local(n1), SIZE_0, SIZE_MAX, + &t1, &t2, &stack); + // order -- простое? + n1 = wwWordSize(ec->order, n1); + if (!priIsPrime(ec->order, n1, stack)) + return FALSE; + // order == p? + if (wwCmp2(ec->f->mod, ec->f->n, ec->order, n1) == 0) + return FALSE; + // проверка MOV + if (mov_threshold) + { + zzMod(t1, ec->f->mod, ec->f->n, ec->order, n1, stack); + wwCopy(t2, t1, n1); + if (wwCmpW(t2, n1, 1) == 0) + return FALSE; + while (--mov_threshold) + { + zzMulMod(t2, t2, t1, ec->order, n1, stack); + if (wwCmpW(t2, n1, 1) == 0) + return FALSE; + } + } + // все нормально + return TRUE; +} + +size_t ecpGroupIsSafe_deep(size_t n) +{ + const size_t n1 = n + 1; + return memSliceSize( + ecpGroupIsSafe_local(n1), + utilMax(2, + priIsPrime_deep(n1), + zzMod_deep(n, n1), + zzMulMod_deep(n1)), + SIZE_MAX); +} + +/* +******************************************************************************* +Алгоритм SWU + +\todo Регуляризировать (qrIsUnitySafe). +******************************************************************************* +*/ + +#define ecpSWU_local(n)\ +/* t */ O_OF_W(n),\ +/* x1 */ O_OF_W(n),\ +/* x2 */ O_OF_W(n),\ +/* y */ O_OF_W(n),\ +/* s */ O_OF_W(n) + +void ecpSWU(word b[], const word a[], const ec_o* ec, void* stack) +{ + size_t n; + register size_t mask; + word* t; /* [n] */ + word* x1; /* [n] */ + word* x2; /* [n] */ + word* y; /* [n] */ + word* s; /* [n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(zmIsIn(a, ec->f)); + ASSERT(wwGetBits(ec->f->mod, 0, 2) == 3); + ASSERT(!qrIsZero(ec->A, ec->f) && !qrIsZero(ec->B, ec->f)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpSWU_local(n), SIZE_0, SIZE_MAX, + &t, &x1, &x2, &y, &s, &stack); + // t <- -a^2 + qrSqr(t, a, ec->f, stack); + zmNeg(t, t, ec->f); + // s <- p - 2 + wwCopy(s, ec->f->mod, n); + zzSubW2(s, n, 2); + // x1 <- -B(1 + t + t^2)(A(t + t^2))^{p - 2} + qrSqr(x2, t, ec->f, stack); + qrAdd(x2, x2, t, ec->f); + qrMul(x1, x2, ec->A, ec->f, stack); + qrPower(x1, x1, s, n, ec->f, stack); + qrAddUnity(x2, x2, ec->f); + qrMul(x1, x1, x2, ec->f, stack); + qrMul(x1, x1, ec->B, ec->f, stack); + zmNeg(x1, x1, ec->f); + // y <- (x1)^3 + A x1 + B + qrSqr(x2, x1, ec->f, stack); + qrMul(x2, x2, x1, ec->f, stack); + qrMul(y, x1, ec->A, ec->f, stack); + qrAdd(y, y, x2, ec->f); + qrAdd(y, y, ec->B, ec->f); + // x2 <- x1 t + qrMul(x2, x1, t, ec->f, stack); + // t <- y^{(p - 1) - (p + 1) / 4} = y^{s - (p - 3) / 4} + wwCopy(t, ec->f->mod, n); + wwShLo(t, n, 2); + zzSub(s, s, t, n); + qrPower(t, y, s, n, ec->f, stack); + // s <- a^3 y + qrSqr(s, a, ec->f, stack); + qrMul(s, s, a, ec->f, stack); + qrMul(s, s, y, ec->f, stack); + // mask <- t^2 y == 1 ? 0 : -1; + qrSqr(b, t, ec->f, stack); + qrMul(b, b, y, ec->f, stack); + mask = qrIsUnity(b, ec->f) - SIZE_1; + // b <- mask == 0 ? (x1, t y) : (x2, t s) + qrCopy(ecX(b), x1 + (mask & n), ec->f); + qrMul(ecY(b, n), t, y + (mask & n), ec->f, stack); + // очистка + CLEAN(mask); +} + +size_t ecpSWU_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpSWU_local(n), + utilMax(2, + f_deep, + qrPower_deep(n, n, f_deep)), + SIZE_MAX); +} diff --git a/win/vs09/libbee2.vcproj b/win/vs09/libbee2.vcproj index cad055a0..174e0006 100644 --- a/win/vs09/libbee2.vcproj +++ b/win/vs09/libbee2.vcproj @@ -496,10 +496,6 @@ RelativePath="..\..\src\math\ec2.c" > - - @@ -584,6 +580,26 @@ > + + + + + + + + + + - + + + @@ -498,6 +500,7 @@ + diff --git a/win/vs15/libbee2.vcxproj.filters b/win/vs15/libbee2.vcxproj.filters index b4ef29d5..f44e4fb6 100644 --- a/win/vs15/libbee2.vcxproj.filters +++ b/win/vs15/libbee2.vcxproj.filters @@ -55,11 +55,11 @@ {d79259c8-57bc-4d08-8356-012f773be234} + + {b675af6f-3f48-4922-87c1-ad79ce813caa} + - - Source Files\math - Source Files\math @@ -333,6 +333,15 @@ Source Files\core\rng + + Source Files\math\ecp + + + Source Files\math\ecp + + + Source Files\math\ecp + @@ -497,5 +506,8 @@ Header Files\core + + Source Files\math\ecp + \ No newline at end of file From ed2092f1c4d6f9bfeee9f7afb9657708dfb2bea8 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 4 Feb 2026 18:58:42 +0300 Subject: [PATCH 28/77] Start math/ecp/pre --- include/bee2/math/ecp.h | 14 +- src/CMakeLists.txt | 1 + src/math/ec.c | 4 +- src/math/ecp/ecp_j.c | 32 --- src/math/ecp/ecp_pre.c | 396 +++++++++++++++++++++++++++++++ test/math/ecp_test.c | 7 +- win/vs09/libbee2.vcproj | 4 + win/vs15/libbee2.vcxproj | 1 + win/vs15/libbee2.vcxproj.filters | 3 + 9 files changed, 417 insertions(+), 45 deletions(-) create mode 100644 src/math/ecp/ecp_pre.c diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 1427ed21..fb5b354e 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -267,11 +267,11 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); /*! \brief Предвычисления по схеме SNZ - На эллиптической кривой ec вычисляются 2^w малых нечетных кратных + На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере - [2^w * ec->d * ec->f->n]pre: + [2^{w-1} * ec->d * ec->f->n]pre: \code - pre[0:2^w) <- (-(2^w - 1)a, ..., -3a, a, 3a, ..., (2^w - 1)a). + pre[0:2^{w-1}) <- (a, 3a, ..., (2^w - 1)a). \endcode Каждое кратное является проективной точкой и занимает ec->d * ec->f->n машинных слов. @@ -281,19 +281,17 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на кривой. - \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->f->deep, ec->d, ec->deep, w). + \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->f->deep, w). */ void ecpPreSNZ( - word pre[], /*!< [out] малые кратные */ + ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ void* stack /*!< [in] вспомогательная память */ ); -size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t ec_d, size_t ec_deep, - size_t w); - +size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bdebe179..baee4e84 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,7 @@ set(src math/ecp/ecp_a.c math/ecp/ecp_j.c math/ecp/ecp_misc.c + math/ecp/ecp_pre.c math/gf2.c math/gfp.c math/pp/pp_etc.c diff --git a/src/math/ec.c b/src/math/ec.c index 52fc24f3..74fc8bba 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -173,7 +173,7 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(w > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << w)), + pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << (w - 1))), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -219,7 +219,7 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(w > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * (SIZE_1 << w)), + pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * (SIZE_1 << (w - 1))), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, diff --git a/src/math/ecp/ecp_j.c b/src/math/ecp/ecp_j.c index d78a444a..4d2b5a1a 100644 --- a/src/math/ecp/ecp_j.c +++ b/src/math/ecp/ecp_j.c @@ -21,38 +21,6 @@ ******************************************************************************* Арифметика якобиевых точек -Используются якобиевы (Jacobian, J) проективные координаты: - x = X / Z^2, y = Y / Z^3, - -(X : Y : Z) = (X : -Y : Z). - -Основные алгоритмы представлены на сайте http://www.hyperelliptic.org/efd. -Там же можно найти соглашения по обозначению сложности алгоритмов. В этих -обозначениях фигурируют следующие формальные выражения: - add -- сложение или вычитание \mod p, - c -- умножение на малую константу c (2,3,...) \mod p, - half -- умножение на 2^{-1} \mod p, - *A -- умножение на коэффициент A \mod p, - S -- возведение в квадрат \mod p, - M -- умножение \mod p, - D -- деление \mod p. - -При общей оценке сложности считается, что 1D = 100M и 1S = 1M. -Аддитивные операции игнорируются. - -Используются обозначения: - A <- A + A -- сложение аффинных точек, - A <- 2A -- удвоение аффинной точки; - J <- J + J -- сложение якобиевых точек; - J <- J + A -- добавление к якобиевой точке аффинной; - J <- 2J -- удвоение якобиевой точки; - J <- 2A -- удвоение аффинной точки с переходом к якобиевым координатам. -******************************************************************************* -*/ - -/* -******************************************************************************* -Базовые операции - В функции ecpDblJ() выполняется удвоение J <- 2J. Реализован алгоритм dbl-1998-hnm из [HNM98]. Сложность алгоритма: 3M + 6S + 1*A + 1half + 6add + 3*2 \approx 9M. diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c new file mode 100644 index 00000000..35b5ca14 --- /dev/null +++ b/src/math/ecp/ecp_pre.c @@ -0,0 +1,396 @@ +/* +******************************************************************************* +\file ecp_pre.c +\brief Elliptic curves over prime fields: precomputations +\project bee2 [cryptographic library] +\created 2021.07.18 +\version 2026.02.04 +\license This program is released under the GNU General Public License +version 3. See Copyright Notices in bee2/info.h. +******************************************************************************* +*/ + +#include "bee2/core/util.h" +#include "bee2/math/ecp.h" +#include "bee2/math/gfp.h" +#include "bee2/math/ww.h" +#include "bee2/math/zz.h" + +/* +******************************************************************************* +Вспомогательные макросы +******************************************************************************* +*/ + +#define gfpMul2(c, a, b, a2, b2, f, stack)\ + do {\ + qrAdd(c, a, b, f);\ + qrSqr(c, c, f, stack);\ + qrSub(c, c, a2, f);\ + qrSub(c, c, b2, f);\ + gfpHalf(c, c, f);\ + } while(0) + +/* +******************************************************************************* +Предвычисления: схема SNZ + +Реализован алгоритм, предложенный в [APS22]. Алгоритм основан на многочленах +деления и требует примерно 11/2 M + 7/2 S операций для каждой кратной точки. + +[APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication + on Weierstrass curves using division polynomials. Mat. Vopr. + Kriptogr., 13:2, 2022, https://doi.org/10.4213/mvk406. +******************************************************************************* +*/ + +#define stack_walloc(p, k) \ + do { \ + (p) = (word*)(stack); \ + stack = (word*)(stack) + (k); \ + } while(0) +#define stack_wfree(p) + +void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, + void* stack) +{ + // размер координаты + const size_t n = ec->f->n; + // размер якобиевой точки + const size_t nj = n * ec->d; + // координата x базовой точки + const word* x = ecX(a); + // координата y базовой точки + const word* y = ecY(a, n); + + size_t i; + + // Основные этапы алгоритма: + // 0) Wᵢ, i=3,4,5 + // 1) for i=3,4..2ʷ⁻¹ + // 3*) for i=3,5..2ʷ⁻¹-1 + // 3) for i=3,5..2ʷ⁻¹+1 + // 4) for i=2ʷ⁻¹+1,2ʷ⁻¹+3..2ʷ-1 + // 4*) for i=2ʷ⁻¹+3,2ʷ⁻¹+5..2ʷ-1 + // + // Таблица. Подвыражения на этапах. + // Подвыражение | 0) | 1) | 3) | 4) | + // ---------------------------------------------------------------------------------- + // Wᵢ | W[3,4,5] | R[i,i+2,2i+1] | R[*2i] | R[*i-1,i,*i+1,i+2] | + // | | W[2i,2i+1] | | | + // Wᵢ² | W[3,4,5] | R[i-1,i,i+1,'i+2] | | R[i-1,'i,i+1,'i+2] | + // | | W[2i,2i+1] | | | + // Wᵢ₋₁ Wᵢ₊₁ | W[2,3] | W[i+1] | | R[i-1], W[i+1] | + // | | R[i-1,i(e),i+1] | | | + // (2y)² Wᵢ₋₁ Wᵢ₊₁ | W[3] | W[i(e)+1] | R[i] | | + // (2y)⁴ Wᵢ₋₁ Wᵢ₊₁ | W[3] | R[i(o)] | | | + // | | WR[i(e)+1] | | | + // + + // Выделяемая память + word* tmp; + word* tmp2; + // 2y + word* dy; + // (2y)² + word* dy2; + // полиномы деления: Wᵢ, i=3,4..(2ʷ+1) + // этапы: 0), 1), 3), 4) + // память: 2ʷ-1 + // Значения расчитываются на этапе 1) по индексам 2i и 2i+1. + // На этапе 1) значения считываются последовательно, по индексам i, i+2. + // На этапе 3) значения считываются по чётным индексам 2i. + // На этапе 4) значения считываются последовательно, по индексам i-1,i,i+1,i+2. + // Упростить кэширование не получается - необходимо выделять память под все значения. + word* pW; +#define W(i) (pW + ((i)-3) * n) + // квадраты: Wᵢ², i=3,4..2ʷ + // этапы: 2), 3), 4) + // память: 2ʷ-2[+1] + // Квадраты с нечётными индексами сгруппированы вместе для упрощения их обращения. + // Квадраты по чётным индексам: W₂ᵢ², i=2,3..2ʷ⁻¹, - выделяются в pW2[0]. + // Квадраты по нечётным индексам: W₂ᵢ₋₁², i=2,3..2ʷ⁻¹, - выделяются в pw2[1]. + word* pW2[2]; +#define W2(i) (pW2[(i)&1] + (((i)-3)>>1) * n) + // произведения: Wᵢ₋₁ Wᵢ₊₁, i=2,3..2ʷ⁻¹+1 + // этапы: 0), 1), 4) + // память: 3 + // На этапе 1) значения формируются и используются последовательно с индексами i-1,i,i+1, + // поэтому можно выделять память лишь под 3 текущие значения. + // На этапе 4) происходит чтение по индексу i-1, и запись по индексу i+1, поэтому + // память можно выделять только под 1 значение. + // Макрос WW(i) имеет вид (i+D)%3, где D - константа. + // D выбрано как 2ʷ, чтобы WW(2ʷ⁻¹)=[(2ʷ⁻¹(1+2))%3]=0. + // Макрос WW переопределен перед этапом 4) так, чтобы WW(i)=0. + word* pWW; +#define WW(i) (pWW + (((i) + (SIZE_1 << w))%3) * n) + // произведения: (2y)² Wᵢ₋₁ Wᵢ₊₁, i=3,5..2ʷ⁻¹+1 + // этапы: 0), 1), 3) + // память: 2ʷ⁻² + // Значения формируются на этапах 0), 1), чтение - на этапе 3). Кэшировать нужно все значения. + word* pWW2; +#define WWy2(i) (pWW2 + (((i)-3) >> 1) * n) + // текущее произведение: (2y)⁴ Wᵢ₋₁ Wᵢ₊₁, i=3,5..(2ʷ⁻¹-1) + // этапы: 0), 1) + // память: 1 + // Запись на пред. шаге, чтение на текущем. Кэшировать можно только одно текущее значение. + word* pWW4; +#define WWy4(i) (pWW4) + + // выходной буфер + word* c; + + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(w > 0); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << (w - 1))), + a, O_OF_W(2 * ec->f->n))); + + // разметить стек + stack_walloc(dy2, n); + stack_walloc(tmp, n); + stack_walloc(tmp2, n); + stack_walloc(pW, n * ((SIZE_1 << w) - 1)); + stack_walloc(pW2[0], n * ((SIZE_1 << (w-1)) - 1)); + stack_walloc(pW2[1], n * ((SIZE_1 << (w-1)) - 1)); + stack_walloc(pWW, n * 3); + stack_walloc(pWW2, n * (SIZE_1 << (w-2))); + stack_walloc(pWW4, n); + dy = dy2; + + // Этап 0) + + // [(2y)²] + gfpDouble(dy, y, ec->f); + qrSqr(dy2, dy, ec->f, stack); + + { + // вспомогательные значения + word* xx; + word* bx; + word* aa; + + xx = pWW; + bx = xx + n; + aa = bx + n; + + qrSqr(xx, x, ec->f, stack); // x ² + qrMul(bx, ec->B, x, ec->f, stack); // b x + qrSqr(aa, ec->A, ec->f, stack); // a ² + + // [W₃] + { + qrAdd(tmp, xx, ec->A, ec->f); // x² + a + qrSqr(tmp, tmp, ec->f, stack); // (x²+a) ² + gfpDouble(tmp2, tmp, ec->f); // 2 (x²+a)² + qrAdd(W(3), tmp, tmp2, ec->f); // 3 (x²+a)² + + gfpDouble(tmp, bx, ec->f); // 2 bx + qrAdd(tmp, tmp, bx, ec->f); // 3 bx + qrSub(tmp, aa, tmp, ec->f); // a² − 3bx + gfpDouble(tmp, tmp, ec->f); // 2 (a²−3bx) + gfpDouble(tmp, tmp, ec->f); // 4 (a²−3bx) + + qrSub(W(3), W(3), tmp, ec->f); // W₃ = 3(x²+a)² − 4(a²−3bx) + } + + // [W₄] + { + word* u = W(4); + gfpDouble(tmp, xx, ec->f); // 2 x² + gfpDouble(tmp, tmp, ec->f); // 4 x² + qrAdd(tmp, tmp, xx, ec->f); // 5 x² + qrSub(tmp, tmp, ec->A, ec->f); // 5x² - a + qrMul(tmp, bx, tmp, ec->f, stack); // bx (5x²-a) + gfpDouble(tmp, tmp, ec->f); // 2 bx(5x²-a) + gfpDouble(W(4), tmp, ec->f); // 4 bx(5x²-a) + + qrMul(tmp, xx, x, ec->f, stack); // x³ + qrSqr(tmp2, tmp, ec->f, stack); // x⁶ + qrAdd(W(4), tmp2, W(4), ec->f); // x⁶ + 4bx(5x²-a) + + qrMul(tmp2, ec->A, x, ec->f, stack); // a x + qrSub(tmp, tmp, tmp2, ec->f); // x³ - ax + qrMul(tmp, tmp, tmp2, ec->f, stack); // ax (x³-ax) + gfpDouble(tmp2, tmp, ec->f); // 2 ax(x³-ax) + gfpDouble(tmp2, tmp2, ec->f); // 4 ax(x³-ax) + qrAdd(tmp, tmp, tmp2, ec->f); // 5 ax(x³-ax) + qrAdd(W(4), W(4), tmp, ec->f); // x⁶+4bx(5x²-a) + 5ax(x³-ax) + + qrSqr(tmp, ec->B, ec->f, stack); // b ² + gfpDouble(tmp, tmp, ec->f); // 2 b² + gfpDouble(tmp, tmp, ec->f); // 4 b² + gfpDouble(tmp, tmp, ec->f); // 8 b² + qrSub(W(4), W(4), tmp, ec->f); // x⁶+4bx(5x²-a)+5ax(x³-ax) - 8b² + + qrMul(tmp2, aa, ec->A, ec->f, stack); // a² a + qrSub(u, W(4), tmp2, ec->f); // W₄/2 = x⁶+4bx(5x²-a)+5ax(x³-ax)-8b² - a³ + + gfpDouble(W(4), u, ec->f); // W₄ = 2 (x⁶+4bx(5x²-a)+5ax(x³-ax)-8b²-a³) + } + } + // [W₃²], [W₁W₃], [W₄²], [W₂W₄], [(2y)²W₂W₄], [(2y)⁴W₂W₄] + qrSqr(W2(3), W(3), ec->f, stack); // W₃² + qrCopy(WW(2), W(3), ec->f); // W₁W₃ = W₃ + qrSqr(W2(4), W(4), ec->f, stack); // W₄² + qrCopy(WW(3), W(4), ec->f); // W₂W₄ = W₄ + qrMul(WWy2(3), dy2, WW(3), ec->f, stack); // (2y)² W₂W₄ + qrMul(WWy4(3), dy2, WWy2(3), ec->f, stack); // (2y)² (2y)²W₂W₄ + + // [W₅], [W₅²] + { + qrMul(tmp, WW(2), W2(3), ec->f, stack); // W₁W₃ W₃² + qrSub(W(5), WWy4(3), tmp, ec->f); // W₅ = (2y)⁴W₂W₄ − W₁W₃W₃² + if (w > 2) + qrSqr(W2(5), W(5), ec->f, stack); // W₅ ² + } + + // Этап 1) + // для i=3,4..2ʷ⁻¹: [W₂ᵢ], [W₂ᵢ₊₁], [Wᵢ Wᵢ₊₂] + for (i = 3; i <= SIZE_1 << (w - 1); ++i) + { + // [WᵢWᵢ₊₂] = ((Wᵢ + Wᵢ₊₂) ² - Wᵢ² - Wᵢ₊₂²) / 2 + gfpMul2(WW(i+1), W(i), W(i+2), W2(i), W2(i+2), ec->f, stack); + + // [W₂ᵢ] + if (i == 3) + { + qrMul(tmp, WW(i-1), W2(i+1), ec->f, stack); // (W₁W₃) W₄² + qrSub(W(2*i), WW(i+1), tmp, ec->f); // W₆ = W₃W₅ - W₁W₃W₄² + } + else + { + qrMul(tmp, WW(i-1), W2(i+1), ec->f, stack); // (Wᵢ₋₂Wᵢ) Wᵢ₊₁² + qrMul(W(2*i), WW(i+1), W2(i-1), ec->f, stack); // (WᵢWᵢ₊₂) Wᵢ₋₁² + qrSub(W(2*i), W(2*i), tmp, ec->f); // W₂ᵢ = (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² + } + + // [W₂ᵢ²] + qrSqr(W2(2*i), W(2*i), ec->f, stack); // W₂ᵢ ² + + // [W₂ᵢ₊₁] + if ((i & 1) == 1) + { + qrMul(tmp, WWy4(i), W2(i+1), ec->f, stack); // (2y)⁴Wᵢ₋₁Wᵢ₊₁ Wᵢ₊₁² + qrMul(W(2*i+1), WW(i+1), W2(i), ec->f, stack); // WᵢWᵢ₊₂ Wᵢ² + qrSub(W(2*i+1), W(2*i+1), tmp, ec->f); // W₂ᵢ₊₁ = WᵢWᵢ₊₂Wᵢ² - (2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + } + else + { + // [(2y)²WᵢWᵢ₊₂] + qrMul(WWy2(i+1), dy2, WW(i+1), ec->f, stack); // (2y)² WᵢWᵢ₊₂ + // [(2y)⁴WᵢWᵢ₊₂] + qrMul(WWy4(i+1), dy2, WWy2(i+1), ec->f, stack); // (2y)² (2y)²WᵢWᵢ₊₂ + qrMul(tmp, WW(i), W2(i+1), ec->f, stack); // Wᵢ₋₁Wᵢ₊₁ Wᵢ₊₁² + qrMul(W(2*i+1), WWy4(i+1), W2(i), ec->f, stack); // (2y)⁴WᵢWᵢ₊₂ Wᵢ² + qrSub(W(2*i+1), W(2*i+1), tmp, ec->f); // W₂ᵢ₊₁ = (2y)⁴WᵢWᵢ₊₂Wᵢ² - Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + } + + if (i != SIZE_1 << (w - 1)) + { + // [W₂ᵢ₊₁²] + qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); // W₂ᵢ₊₁ ² + } + } + + // [1]P + c = pre->pts; + ecFromA(c, a, ec, stack); + c += nj; + + // Этап 3) + for (i = 3;;) + { + // i=3,5..2ʷ⁻¹+1: [Xᵢ] = x Wᵢ² − (2y)²Wᵢ₋₁Wᵢ₊₁ + qrMul(ecX(c), x, W2(i), ec->f, stack); + qrSub(ecX(c), ecX(c), WWy2(i), ec->f); + + if (i == (SIZE_1 << (w - 1)) + 1) break; + + // i=3,5..2ʷ⁻¹-1: [Yᵢ] = y (Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²) + if (i < 4) + wwCopy(tmp, W(i+2), n); + else + qrMul(tmp, W(i+2), W2(i-1), ec->f, stack); + if (i < 5) + wwCopy(ecY(c, n), W2(i+1), n); + else + qrMul(ecY(c, n), W(i-2), W2(i+1), ec->f, stack); + qrSub(ecY(c, n), tmp, ecY(c, n), ec->f); + qrMul(ecY(c, n), y, ecY(c, n), ec->f, stack); + // [Zᵢ] = Wᵢ + wwCopy(ecZ(c, n), W(i), n); + + i += 2, c += nj; + } + // Этап 4) +#undef WW +#define WW(i) (pWW) + for (; i <= (SIZE_1 << w) - 1;) + { + // i=2ʷ⁻¹+1,2ʷ⁻¹+3..2ʷ-1: [Yᵢ] = y (Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²) + if (i < 4) + wwCopy(tmp, W(i+2), n); + else + qrMul(tmp, W(i+2), W2(i-1), ec->f, stack); + if (i < 5) + wwCopy(ecY(c, n), W2(i+1), n); + else + qrMul(ecY(c, n), W(i-2), W2(i+1), ec->f, stack); + + qrSub(ecY(c, n), tmp, ecY(c, n), ec->f); + qrMul(ecY(c, n), y, ecY(c, n), ec->f, stack); + // [Zᵢ] = Wᵢ + wwCopy(ecZ(c, n), W(i), n); + + if (i == (SIZE_1 << w) - 1) break; + i += 2, c += nj; + + // i=2ʷ⁻¹+3,2ʷ⁻¹+5..2ʷ-1: [Xᵢ] = x Wᵢ² − (2y)² Wᵢ₋₁ Wᵢ₊₁ + gfpMul2(tmp, W(i-1), W(i+1), W2(i-1), W2(i+1), ec->f, stack); // Wᵢ₋₁ Wᵢ₊₁ + qrMul(tmp, dy2, tmp, ec->f, stack); // (2y)² Wᵢ₋₁Wᵢ₊₁ + qrMul(ecX(c), x, W2(i), ec->f, stack); // x Wᵢ² + qrSub(ecX(c), ecX(c), tmp, ec->f); // x Wᵢ² − (2y)²Wᵢ₋₁Wᵢ₊₁ + } + + stack_wfree(pWW4); + stack_wfree(pWW2); + stack_wfree(pWW); + stack_wfree(pW2[1]); + stack_wfree(pW2[0]); + stack_wfree(pW); + stack_wfree(tmp2); + stack_wfree(tmp); + stack_wfree(dy2); + +#undef W +#undef W2 +#undef WW +#undef WWy2 +#undef WWy4 + + // заполнить остальные поля + pre->type = ec_pre_snza; + pre->w = w, pre->h = 0; +} + +size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) +{ + size_t const ww = SIZE_1 << w; + size_t r = n * (0 + + 1 // dy2 + + 1 // tmp + + 1 // tmp2 + + (ww - 1) // pW + + (ww/2 - 1) // pW2[0] + + (ww/2 - 1) // pW2[1] + + 3 // pWW + + ww/4 // pWW2 + + 1 // pWW4 + ); + return O_OF_W(r) + f_deep; +} + +#undef stack_walloc +#undef stack_wfree diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index a658a252..c1fdc125 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -60,7 +60,7 @@ bool_t ecpTest() ec_o* ec; /* [ec_keep] */ qr_o* f; /* [f_keep] */ octet* t; /* [3 * no] */ - ec_pre_t* pre; /* [64 якобиевы точки] */ + ec_pre_t* pre; /* [32 якобиевы точки] */ word* pt0; /* [3 * n] */ word* pt1; /* [3 * n] */ word* pt2; /* [3 * n] */ @@ -82,7 +82,7 @@ bool_t ecpTest() O_OF_W(3 * n), O_OF_W(3 * n), O_OF_W(n + 1), - utilMax(17, + utilMax(18, gfpCreate_deep(no), ecpCreateJ_deep(n, f_deep), ecGroupCreate_deep(f_deep), @@ -96,6 +96,7 @@ bool_t ecpTest() ecpSubAA_deep(n, f_deep), ecMulA_deep(n, 3, ec_deep, n), ecPreSNZ_deep(n, 3, ec_deep), + ecpPreSNZ_deep(n, f_deep, 6), ecMulPreSNZ_deep(n, 3, ec_deep, n), ecPreSNZA_deep(n, 3, ec_deep), ecMulPreSNZA_deep(n, 3, ec_deep, n), @@ -339,7 +340,7 @@ bool_t ecpTest() { size_t i; // pt[0..32) <- (1, 3, ..., 31)base - ecPreSNZ(pre, ec->base, 6, ec, stack); + ecpPreSNZ(pre, ec->base, 6, ec, stack); // pt0 <- \sum_{i = 0}^31 2^{31 - i} pt[i] wwCopy(pt0, ecPrePt(pre, 0, ec), 3 * n); for (i = 1; i < 32; ++i) diff --git a/win/vs09/libbee2.vcproj b/win/vs09/libbee2.vcproj index 174e0006..f93ddfaa 100644 --- a/win/vs09/libbee2.vcproj +++ b/win/vs09/libbee2.vcproj @@ -599,6 +599,10 @@ RelativePath="..\..\src\math\ecp\ecp_misc.c" > + + + diff --git a/win/vs15/libbee2.vcxproj.filters b/win/vs15/libbee2.vcxproj.filters index f44e4fb6..80de3a13 100644 --- a/win/vs15/libbee2.vcxproj.filters +++ b/win/vs15/libbee2.vcxproj.filters @@ -342,6 +342,9 @@ Source Files\math\ecp + + Source Files\math\ecp + From feb0558afdeee956ec4fd730e3e7e98e3759c3fa Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 5 Feb 2026 20:41:43 +0300 Subject: [PATCH 29/77] Update ecpPreSNZ() --- include/bee2/defs.h | 5 +- include/bee2/math/ecp.h | 2 +- src/core/rng/rng_timer.c | 4 +- src/math/ec.c | 26 +- src/math/ecp/ecp_pre.c | 526 +++++++++++++++++---------------------- src/math/qr.c | 6 +- test/math/ecp_bench.c | 28 ++- 7 files changed, 282 insertions(+), 315 deletions(-) diff --git a/include/bee2/defs.h b/include/bee2/defs.h index bc001cfa..306eb56e 100644 --- a/include/bee2/defs.h +++ b/include/bee2/defs.h @@ -4,7 +4,7 @@ \brief Basic definitions \project bee2 [cryptographic library] \created 2012.04.01 -\version 2025.09.28 +\version 2026.02.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -404,7 +404,8 @@ T == octet. #error "Unsupported size_t size" #endif -#define SIZE_HI (SIZE_1 << (B_PER_S - 1)) +#define SIZE_BIT_POS(pos) (SIZE_1 << (pos)) +#define SIZE_HI SIZE_BIT_POS(B_PER_S - 1) /* ******************************************************************************* diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index fb5b354e..9b63ccb9 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -275,7 +275,7 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); \endcode Каждое кратное является проективной точкой и занимает ec->d * ec->f->n машинных слов. - \pre w > 0. + \pre w >= 3. \pre Описание ec работоспособно. \pre Буферы a и pre либо не пересекаются, либо указатели a и pre совпадают. \pre Координаты a лежат в базовом поле. diff --git a/src/core/rng/rng_timer.c b/src/core/rng/rng_timer.c index 793266ea..6e5168cc 100644 --- a/src/core/rng/rng_timer.c +++ b/src/core/rng/rng_timer.c @@ -4,7 +4,7 @@ \brief Random number generation: entropy sources based on timers \project bee2 [cryptographic library] \created 2014.10.13 -\version 2026.01.20 +\version 2026.02.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -201,7 +201,7 @@ static bool_t tmCtrIsValid() static tm_ticks_t tmCtrTicks() { - register size_t reps = SIZE_1 << 20; + register size_t reps = SIZE_BIT_POS(20); ASSERT(tmCtrIsValid()); while (reps--) if (_tm_ctr_ticks != _tm_ctr_ticks2) diff --git a/src/math/ec.c b/src/math/ec.c index 74fc8bba..b04e410c 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.04 +\version 2026.02.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -173,7 +173,7 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(w > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << (w - 1))), + pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * SIZE_BIT_POS(w - 1)), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -189,10 +189,10 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // pt[1] <- t + a ecAddA(ecPrePt(pre, 1, ec), t, a, ec, stack); // pt[i] <- t + pt[i - 1] - for (i = 2; i < SIZE_1 << (w - 1); ++i) + for (i = 2; i < SIZE_BIT_POS(w - 1); ++i) ecAdd(ecPrePt(pre, i, ec), t, ecPrePt(pre, i - 1, ec), ec, stack); } - // заполнить остальные поля + // заполнить служебные поля pre->type = ec_pre_snz; pre->w = w, pre->h = 0; } @@ -219,7 +219,7 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(w > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * (SIZE_1 << (w - 1))), + pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * SIZE_BIT_POS(w - 1)), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -233,14 +233,14 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // t1 <- 2 a ecDblA(t1, a, ec, stack); // pt[i] <- t1 + pt[i - 1] - for (i = 1; i < SIZE_1 << (w - 1); ++i) + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { ecAddA(t2, t1, ecPrePtA(pre, i - 1, ec), ec, stack); if (!ecToA(ecPrePtA(pre, i, ec), t2, ec, stack)) return FALSE; } } - // заполнить остальные поля + // заполнить служебные поля pre->type = ec_pre_snza; pre->w = w, pre->h = 0; return TRUE; @@ -386,7 +386,7 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, if (m == 0) return FALSE; naf_width = ecNAFWidth(B_OF_W(m)); - pre_count = SIZE_1 << (naf_width - 1); + pre_count = SIZE_BIT_POS(naf_width - 1); // разметить стек memSlice(stack, ecMulA_local(ec->f->n, ec->d, pre_count), SIZE_0, SIZE_MAX, @@ -404,7 +404,7 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { const size_t naf_width = ecNAFWidth(B_OF_W(m)); - const size_t pre_count = SIZE_1 << (naf_width - 1); + const size_t pre_count = SIZE_BIT_POS(naf_width - 1); return memSliceSize( ecMulA_local(n, ec_d, pre_count), utilMax(2, @@ -509,7 +509,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, // размерности mb = wwBitSize(ec->order, m); ASSERT(mb >= 2 * pre->w); - pre_count = SIZE_1 << (pre->w - 1); + pre_count = SIZE_BIT_POS(pre->w - 1); pt_size = ec->d * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; // разметить стек @@ -596,7 +596,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, // размерности mb = wwBitSize(ec->order, m); ASSERT(mb >= 2 * pre->w); - pre_count = SIZE_1 << (pre->w - 1); + pre_count = SIZE_BIT_POS(pre->w - 1); pt_size = 2 * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; // разметить стек @@ -754,7 +754,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) m[i] = wwWordSize(d, m[i]); // зарезервировать память для naf[i] и pre[i] naf_width[i] = ecNAFWidth(B_OF_W(m[i])); - pre_count = SIZE_1 << (naf_width[i] - 1); + pre_count = SIZE_BIT_POS(naf_width[i] - 1); memSlice(stack, O_OF_W(2 * m[i] + 1), sizeof(ec_pre_t) + O_OF_W(pre_count * ec->d * ec->f->n), @@ -820,7 +820,7 @@ size_t ecAddMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t k, ...) { size_t m = va_arg(args, size_t); size_t naf_width = ecNAFWidth(B_OF_W(m)); - size_t pre_count = SIZE_1 << (naf_width - 1); + size_t pre_count = SIZE_BIT_POS(naf_width - 1); ret += memSliceSize( O_OF_W(2 * m + 1), sizeof(ec_pre_t) + O_OF_W(ec_d * n * pre_count), diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index 35b5ca14..a5adba62 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.04 +\version 2026.02.05 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -35,362 +35,304 @@ version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* Предвычисления: схема SNZ -Реализован алгоритм, предложенный в [APS22]. Алгоритм основан на многочленах -деления и требует примерно 11/2 M + 7/2 S операций для каждой кратной точки. +Реализован алгоритм SmallMultJ, предложенный в [APS22]. Алгоритм основан на +многочленах деления и требует примерно 19/2 M + 7/2 S операций на каждую +кратную точку. + +Алгоритм выполняется в 4 этапа. Этапам соответствуют следующие шаги алгоритма: +- этап 1 -- шаги 1 -- 13; +- этап 2 -- шаги 14 -- 16; +- этап 3 -- шаги 17, 19; +- этап 4 -- шаги 18, 20. + +Для повышения читабельности используются макросы, которые покрывают следующие +переменные алгоритма: +1. W(i) -- значения многочленов Wᵢ, i=3,4,...,2ʷ+1: + * этапы: 0), 1), 3), 4); + * память: 2ʷ-1 (элементов поля); + * значения рассчитываются на этапе 2 по индексам 2i и 2i+1. + * на этапе 1) значения считываются последовательно, по индексам i, i+2; + * на этапе 3) значения считываются по чётным индексам 2i; + * на этапе 4) значения считываются последовательно, по индексам + i-1,i,i+1,i+2; + * упростить кэширование не получается -- необходимо выделять память под + все значения. +2. W2(i) -- квадраты Wᵢ², i=3,4...,2ʷ: + * этапы: 2), 3), 4); + * память: 2ʷ-2. +3. WW(i) -- произведения Wᵢ₋₁ Wᵢ₊₁, i=2,3...,2ʷ⁻¹+1: + * этапы: 0), 1), 4); + * память: 3; + * на этапе 1) значения формируются и используются последовательно + с индексами i-1,i,i+1 и поэтому можно выделять память лишь под 3 текущие + значения; + * на этапе 4) происходит чтение по индексу i-1 и запись по индексу i+1, + поэтому память можно выделять только под 1 значение. +4. WWy2(i) -- произведения (2y)² Wᵢ₋₁ Wᵢ₊₁, i=3,5,...,2ʷ⁻¹+1: + * этапы: 0), 1), 3); + * память: 2ʷ⁻²; + * значения формируются на этапах 0), 1), чтение -- на этапе 3). Кэшировать + * нужно все значения. +5. WWy4(i) -- текущее произведение (2y)⁴ Wᵢ₋₁ Wᵢ₊₁, i=3,5,...,2ʷ⁻¹-1: + * этапы: 0), 1); + * память: 1; + * запись на предыдущем шаге, чтение на текущем. Кэшируется только текущее + значение. [APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication on Weierstrass curves using division polynomials. Mat. Vopr. Kriptogr., 13:2, 2022, https://doi.org/10.4213/mvk406. + +\todo Разобраться с шагом 15 SmallMultJ: на этом шаге значение W6 еще не +вычислено. ******************************************************************************* */ -#define stack_walloc(p, k) \ - do { \ - (p) = (word*)(stack); \ - stack = (word*)(stack) + (k); \ - } while(0) -#define stack_wfree(p) +#define ecpPreSNZ_local(n, w)\ +/* t1 */ O_OF_W(n),\ +/* t2 */ O_OF_W(n),\ +/* dy2 */ O_OF_W(n),\ +/* pW */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ +/* pW2 */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ +/* pWW */ O_OF_W(3 * n),\ +/* pWW2 */ O_OF_W(SIZE_BIT_POS(w - 2) * n),\ +/* pWW4 */ O_OF_W(n) void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { - // размер координаты - const size_t n = ec->f->n; - // размер якобиевой точки - const size_t nj = n * ec->d; - // координата x базовой точки - const word* x = ecX(a); - // координата y базовой точки - const word* y = ecY(a, n); - + size_t n; + word* t1; /* [n] */ + word* t2; /* [n] */ + word* dy2; /* [n] (2y)² */ + word* pW; /* [(2^w - 1) * n] Wᵢ */ + word* pW2; /* [2 * (2^{w-1} - 1) * n] Wᵢ² */ + word* pWW; /* [3 * n] Wᵢ₋₁ Wᵢ₊₁ */ + word* pWWy2; /* [2^{w-2} * n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ + word* pWWy4; /* [n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ size_t i; - - // Основные этапы алгоритма: - // 0) Wᵢ, i=3,4,5 - // 1) for i=3,4..2ʷ⁻¹ - // 3*) for i=3,5..2ʷ⁻¹-1 - // 3) for i=3,5..2ʷ⁻¹+1 - // 4) for i=2ʷ⁻¹+1,2ʷ⁻¹+3..2ʷ-1 - // 4*) for i=2ʷ⁻¹+3,2ʷ⁻¹+5..2ʷ-1 - // - // Таблица. Подвыражения на этапах. - // Подвыражение | 0) | 1) | 3) | 4) | - // ---------------------------------------------------------------------------------- - // Wᵢ | W[3,4,5] | R[i,i+2,2i+1] | R[*2i] | R[*i-1,i,*i+1,i+2] | - // | | W[2i,2i+1] | | | - // Wᵢ² | W[3,4,5] | R[i-1,i,i+1,'i+2] | | R[i-1,'i,i+1,'i+2] | - // | | W[2i,2i+1] | | | - // Wᵢ₋₁ Wᵢ₊₁ | W[2,3] | W[i+1] | | R[i-1], W[i+1] | - // | | R[i-1,i(e),i+1] | | | - // (2y)² Wᵢ₋₁ Wᵢ₊₁ | W[3] | W[i(e)+1] | R[i] | | - // (2y)⁴ Wᵢ₋₁ Wᵢ₊₁ | W[3] | R[i(o)] | | | - // | | WR[i(e)+1] | | | - // - - // Выделяемая память - word* tmp; - word* tmp2; - // 2y - word* dy; - // (2y)² - word* dy2; - // полиномы деления: Wᵢ, i=3,4..(2ʷ+1) - // этапы: 0), 1), 3), 4) - // память: 2ʷ-1 - // Значения расчитываются на этапе 1) по индексам 2i и 2i+1. - // На этапе 1) значения считываются последовательно, по индексам i, i+2. - // На этапе 3) значения считываются по чётным индексам 2i. - // На этапе 4) значения считываются последовательно, по индексам i-1,i,i+1,i+2. - // Упростить кэширование не получается - необходимо выделять память под все значения. - word* pW; -#define W(i) (pW + ((i)-3) * n) - // квадраты: Wᵢ², i=3,4..2ʷ - // этапы: 2), 3), 4) - // память: 2ʷ-2[+1] - // Квадраты с нечётными индексами сгруппированы вместе для упрощения их обращения. - // Квадраты по чётным индексам: W₂ᵢ², i=2,3..2ʷ⁻¹, - выделяются в pW2[0]. - // Квадраты по нечётным индексам: W₂ᵢ₋₁², i=2,3..2ʷ⁻¹, - выделяются в pw2[1]. - word* pW2[2]; -#define W2(i) (pW2[(i)&1] + (((i)-3)>>1) * n) - // произведения: Wᵢ₋₁ Wᵢ₊₁, i=2,3..2ʷ⁻¹+1 - // этапы: 0), 1), 4) - // память: 3 - // На этапе 1) значения формируются и используются последовательно с индексами i-1,i,i+1, - // поэтому можно выделять память лишь под 3 текущие значения. - // На этапе 4) происходит чтение по индексу i-1, и запись по индексу i+1, поэтому - // память можно выделять только под 1 значение. - // Макрос WW(i) имеет вид (i+D)%3, где D - константа. - // D выбрано как 2ʷ, чтобы WW(2ʷ⁻¹)=[(2ʷ⁻¹(1+2))%3]=0. - // Макрос WW переопределен перед этапом 4) так, чтобы WW(i)=0. - word* pWW; -#define WW(i) (pWW + (((i) + (SIZE_1 << w))%3) * n) - // произведения: (2y)² Wᵢ₋₁ Wᵢ₊₁, i=3,5..2ʷ⁻¹+1 - // этапы: 0), 1), 3) - // память: 2ʷ⁻² - // Значения формируются на этапах 0), 1), чтение - на этапе 3). Кэшировать нужно все значения. - word* pWW2; -#define WWy2(i) (pWW2 + (((i)-3) >> 1) * n) - // текущее произведение: (2y)⁴ Wᵢ₋₁ Wᵢ₊₁, i=3,5..(2ʷ⁻¹-1) - // этапы: 0), 1) - // память: 1 - // Запись на пред. шаге, чтение на текущем. Кэшировать можно только одно текущее значение. - word* pWW4; -#define WWy4(i) (pWW4) - - // выходной буфер - word* c; - + word* pt; // pre - ASSERT(ecIsOperable(ec)); - ASSERT(w > 0); + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(w >= 3); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * (SIZE_1 << (w - 1))), + pre, sizeof(ec_pre_t) + O_OF_W(3 * ec->f->n * SIZE_BIT_POS(w - 1)), a, O_OF_W(2 * ec->f->n))); - + // размерности + n = ec->f->n; // разметить стек - stack_walloc(dy2, n); - stack_walloc(tmp, n); - stack_walloc(tmp2, n); - stack_walloc(pW, n * ((SIZE_1 << w) - 1)); - stack_walloc(pW2[0], n * ((SIZE_1 << (w-1)) - 1)); - stack_walloc(pW2[1], n * ((SIZE_1 << (w-1)) - 1)); - stack_walloc(pWW, n * 3); - stack_walloc(pWW2, n * (SIZE_1 << (w-2))); - stack_walloc(pWW4, n); - dy = dy2; - - // Этап 0) - - // [(2y)²] - gfpDouble(dy, y, ec->f); - qrSqr(dy2, dy, ec->f, stack); - + memSlice(stack, + ecpPreSNZ_local(n, w), SIZE_0, SIZE_MAX, + &t1, &t2, &dy2, &pW, &pW2, &pWW, &pWWy2, &pWWy4, &stack); + // первая точка + pt = pre->pts; + ecFromA(pt, a, ec, stack); + pt += 3 * n; + +/*** SmallMultJ: этап 1 ***/ + +#define W(i) (pW + ((i) - 3) * n) +#define W2(i) (pW2 + ((i) - 3) * n) +#define WW(i) (pWW + ((i) % 3) * n) +#define WWy2(i) (pWWy2 + (((i) - 3) >> 1) * n) +#define WWy4(i) (pWWy4) + + // dy2 <- (2y)² + gfpDouble(dy2, ecY(a, n), ec->f); + qrSqr(dy2, dy2, ec->f, stack); + // [W₃], [W₄] { - // вспомогательные значения - word* xx; - word* bx; - word* aa; - - xx = pWW; - bx = xx + n; - aa = bx + n; - - qrSqr(xx, x, ec->f, stack); // x ² - qrMul(bx, ec->B, x, ec->f, stack); // b x - qrSqr(aa, ec->A, ec->f, stack); // a ² - + word* x2 = pWW; + word* bx = x2 + n; + word* a2 = bx + n; + // (x2, bx, a2) <- (x², b x, A²) + qrSqr(x2, ecX(a), ec->f, stack); + qrMul(bx, ec->B, ecX(a), ec->f, stack); + qrSqr(a2, ec->A, ec->f, stack); // [W₃] { - qrAdd(tmp, xx, ec->A, ec->f); // x² + a - qrSqr(tmp, tmp, ec->f, stack); // (x²+a) ² - gfpDouble(tmp2, tmp, ec->f); // 2 (x²+a)² - qrAdd(W(3), tmp, tmp2, ec->f); // 3 (x²+a)² - - gfpDouble(tmp, bx, ec->f); // 2 bx - qrAdd(tmp, tmp, bx, ec->f); // 3 bx - qrSub(tmp, aa, tmp, ec->f); // a² − 3bx - gfpDouble(tmp, tmp, ec->f); // 2 (a²−3bx) - gfpDouble(tmp, tmp, ec->f); // 4 (a²−3bx) - - qrSub(W(3), W(3), tmp, ec->f); // W₃ = 3(x²+a)² − 4(a²−3bx) + // [W₃] <- 3(x²+A)² + qrAdd(t1, x2, ec->A, ec->f); // x²+A + qrSqr(t1, t1, ec->f, stack); // (x²+A)² + gfpDouble(t2, t1, ec->f); // 2(x²+A)² + qrAdd(W(3), t1, t2, ec->f); // 3(x²+A)² + // [W₃] <- 3(x²+A)² − 4(a²−3Bx) + gfpDouble(t1, bx, ec->f); // 2Bx + qrAdd(t1, t1, bx, ec->f); // 3Bx + qrSub(t1, a2, t1, ec->f); // A²−3Bx + gfpDouble(t1, t1, ec->f); // 2(A²−3Bx) + gfpDouble(t1, t1, ec->f); // 4(A²−3Bx) + qrSub(W(3), W(3), t1, ec->f); } - // [W₄] { - word* u = W(4); - gfpDouble(tmp, xx, ec->f); // 2 x² - gfpDouble(tmp, tmp, ec->f); // 4 x² - qrAdd(tmp, tmp, xx, ec->f); // 5 x² - qrSub(tmp, tmp, ec->A, ec->f); // 5x² - a - qrMul(tmp, bx, tmp, ec->f, stack); // bx (5x²-a) - gfpDouble(tmp, tmp, ec->f); // 2 bx(5x²-a) - gfpDouble(W(4), tmp, ec->f); // 4 bx(5x²-a) - - qrMul(tmp, xx, x, ec->f, stack); // x³ - qrSqr(tmp2, tmp, ec->f, stack); // x⁶ - qrAdd(W(4), tmp2, W(4), ec->f); // x⁶ + 4bx(5x²-a) - - qrMul(tmp2, ec->A, x, ec->f, stack); // a x - qrSub(tmp, tmp, tmp2, ec->f); // x³ - ax - qrMul(tmp, tmp, tmp2, ec->f, stack); // ax (x³-ax) - gfpDouble(tmp2, tmp, ec->f); // 2 ax(x³-ax) - gfpDouble(tmp2, tmp2, ec->f); // 4 ax(x³-ax) - qrAdd(tmp, tmp, tmp2, ec->f); // 5 ax(x³-ax) - qrAdd(W(4), W(4), tmp, ec->f); // x⁶+4bx(5x²-a) + 5ax(x³-ax) - - qrSqr(tmp, ec->B, ec->f, stack); // b ² - gfpDouble(tmp, tmp, ec->f); // 2 b² - gfpDouble(tmp, tmp, ec->f); // 4 b² - gfpDouble(tmp, tmp, ec->f); // 8 b² - qrSub(W(4), W(4), tmp, ec->f); // x⁶+4bx(5x²-a)+5ax(x³-ax) - 8b² - - qrMul(tmp2, aa, ec->A, ec->f, stack); // a² a - qrSub(u, W(4), tmp2, ec->f); // W₄/2 = x⁶+4bx(5x²-a)+5ax(x³-ax)-8b² - a³ - - gfpDouble(W(4), u, ec->f); // W₄ = 2 (x⁶+4bx(5x²-a)+5ax(x³-ax)-8b²-a³) + // [W₄] <- 4Bx(5x²-A) + gfpDouble(t1, x2, ec->f); // 2x² + gfpDouble(t1, t1, ec->f); // 4x² + qrAdd(t1, t1, x2, ec->f); // 5x² + qrSub(t1, t1, ec->A, ec->f); // 5x²-A + qrMul(t1, bx, t1, ec->f, stack); // Bx(5x²-A) + gfpDouble(t1, t1, ec->f); // 2Bx(5x²-A) + gfpDouble(W(4), t1, ec->f); // 4Bx(5x²-A) + // [W₄] <- x⁶ + 4Bx(5x²-A) + qrMul(t1, x2, ecX(a), ec->f, stack); + qrSqr(t2, t1, ec->f, stack); + qrAdd(W(4), t2, W(4), ec->f); + // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) + qrMul(t2, ec->A, ecX(a), ec->f, stack); // Ax + qrSub(t1, t1, t2, ec->f); // x³-Ax + qrMul(t1, t1, t2, ec->f, stack); // Ax(x³-Ax) + gfpDouble(t2, t1, ec->f); // 2Ax(x³-Ax) + gfpDouble(t2, t2, ec->f); // 4Ax(x³-Ax) + qrAdd(t1, t1, t2, ec->f); // 5Ax(x³-Ax) + qrAdd(W(4), W(4), t1, ec->f); + // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² + qrSqr(t1, ec->B, ec->f, stack); // B² + gfpDouble(t1, t1, ec->f); // 2B² + gfpDouble(t1, t1, ec->f); // 4B² + gfpDouble(t1, t1, ec->f); // 8B² + qrSub(W(4), W(4), t1, ec->f); + // [W₄] <- 2(x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² - A³) + qrMul(t2, a2, ec->A, ec->f, stack); // A³ + qrSub(W(4), W(4), t2, ec->f); + gfpDouble(W(4), W(4), ec->f); } } - // [W₃²], [W₁W₃], [W₄²], [W₂W₄], [(2y)²W₂W₄], [(2y)⁴W₂W₄] - qrSqr(W2(3), W(3), ec->f, stack); // W₃² - qrCopy(WW(2), W(3), ec->f); // W₁W₃ = W₃ - qrSqr(W2(4), W(4), ec->f, stack); // W₄² - qrCopy(WW(3), W(4), ec->f); // W₂W₄ = W₄ - qrMul(WWy2(3), dy2, WW(3), ec->f, stack); // (2y)² W₂W₄ - qrMul(WWy4(3), dy2, WWy2(3), ec->f, stack); // (2y)² (2y)²W₂W₄ - - // [W₅], [W₅²] - { - qrMul(tmp, WW(2), W2(3), ec->f, stack); // W₁W₃ W₃² - qrSub(W(5), WWy4(3), tmp, ec->f); // W₅ = (2y)⁴W₂W₄ − W₁W₃W₃² - if (w > 2) - qrSqr(W2(5), W(5), ec->f, stack); // W₅ ² - } - - // Этап 1) - // для i=3,4..2ʷ⁻¹: [W₂ᵢ], [W₂ᵢ₊₁], [Wᵢ Wᵢ₊₂] - for (i = 3; i <= SIZE_1 << (w - 1); ++i) + // [W₃²] <- W₃² + qrSqr(W2(3), W(3), ec->f, stack); + // [W₁W₃] <- W₃ + qrCopy(WW(2), W(3), ec->f); + // [W₄²] <- W₄² + qrSqr(W2(4), W(4), ec->f, stack); + // [W₂W₄] <- W₄ + qrCopy(WW(3), W(4), ec->f); + // [(2y)²W₂W₄] <- (2y)²W₂W₄ + qrMul(WWy2(3), dy2, WW(3), ec->f, stack); + // [(2y)⁴W₂W₄] <- (2y)²(2y)²W₂W₄ + qrMul(WWy4(3), dy2, WWy2(3), ec->f, stack); + // [W₅] <- (2y)⁴W₂W₄ − W₁W₃W₃² + qrMul(t1, WW(2), W2(3), ec->f, stack); // W₁W₃W₃² + qrSub(W(5), WWy4(3), t1, ec->f); + // [W₅²] <- W₅² + qrSqr(W2(5), W(5), ec->f, stack); + +/* Этап 2 */ + + // [W₂ᵢ], [W₂ᵢ₊₁], [Wᵢ Wᵢ₊₂], i=3,4...,2ʷ⁻¹ + for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) { - // [WᵢWᵢ₊₂] = ((Wᵢ + Wᵢ₊₂) ² - Wᵢ² - Wᵢ₊₂²) / 2 + // [WᵢWᵢ₊₂] <- ((Wᵢ + Wᵢ₊₂)² - Wᵢ² - Wᵢ₊₂²) / 2 gfpMul2(WW(i+1), W(i), W(i+2), W2(i), W2(i+2), ec->f, stack); - - // [W₂ᵢ] + // [W₆] <- W₃W₅ - W₁W₃W₄² if (i == 3) { - qrMul(tmp, WW(i-1), W2(i+1), ec->f, stack); // (W₁W₃) W₄² - qrSub(W(2*i), WW(i+1), tmp, ec->f); // W₆ = W₃W₅ - W₁W₃W₄² + qrMul(t1, WW(i-1), W2(i+1), ec->f, stack); // (W₁W₃) W₄² + qrSub(W(2*i), WW(i+1), t1, ec->f); } + // [W₂ᵢ] <- (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² else { - qrMul(tmp, WW(i-1), W2(i+1), ec->f, stack); // (Wᵢ₋₂Wᵢ) Wᵢ₊₁² - qrMul(W(2*i), WW(i+1), W2(i-1), ec->f, stack); // (WᵢWᵢ₊₂) Wᵢ₋₁² - qrSub(W(2*i), W(2*i), tmp, ec->f); // W₂ᵢ = (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² + qrMul(t1, WW(i-1), W2(i+1), ec->f, stack); // (Wᵢ₋₂Wᵢ)Wᵢ₊₁² + qrMul(W(2*i), WW(i+1), W2(i-1), ec->f, stack); // (WᵢWᵢ₊₂)Wᵢ₋₁² + qrSub(W(2*i), W(2*i), t1, ec->f); } - - // [W₂ᵢ²] - qrSqr(W2(2*i), W(2*i), ec->f, stack); // W₂ᵢ ² - + // [W₂ᵢ²] <- W₂ᵢ² + qrSqr(W2(2*i), W(2*i), ec->f, stack); // [W₂ᵢ₊₁] - if ((i & 1) == 1) + if (i & 1) { - qrMul(tmp, WWy4(i), W2(i+1), ec->f, stack); // (2y)⁴Wᵢ₋₁Wᵢ₊₁ Wᵢ₊₁² - qrMul(W(2*i+1), WW(i+1), W2(i), ec->f, stack); // WᵢWᵢ₊₂ Wᵢ² - qrSub(W(2*i+1), W(2*i+1), tmp, ec->f); // W₂ᵢ₊₁ = WᵢWᵢ₊₂Wᵢ² - (2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + // [W₂ᵢ₊₁] <- WᵢWᵢ₊₂Wᵢ² - (2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + qrMul(t1, WWy4(i), W2(i+1), ec->f, stack); // (2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + qrMul(W(2*i+1), WW(i+1), W2(i), ec->f, stack); // WᵢWᵢ₊₂Wᵢ² + qrSub(W(2*i+1), W(2*i+1), t1, ec->f); } else { - // [(2y)²WᵢWᵢ₊₂] - qrMul(WWy2(i+1), dy2, WW(i+1), ec->f, stack); // (2y)² WᵢWᵢ₊₂ - // [(2y)⁴WᵢWᵢ₊₂] - qrMul(WWy4(i+1), dy2, WWy2(i+1), ec->f, stack); // (2y)² (2y)²WᵢWᵢ₊₂ - qrMul(tmp, WW(i), W2(i+1), ec->f, stack); // Wᵢ₋₁Wᵢ₊₁ Wᵢ₊₁² - qrMul(W(2*i+1), WWy4(i+1), W2(i), ec->f, stack); // (2y)⁴WᵢWᵢ₊₂ Wᵢ² - qrSub(W(2*i+1), W(2*i+1), tmp, ec->f); // W₂ᵢ₊₁ = (2y)⁴WᵢWᵢ₊₂Wᵢ² - Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² - } - - if (i != SIZE_1 << (w - 1)) - { - // [W₂ᵢ₊₁²] - qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); // W₂ᵢ₊₁ ² + // [W₂ᵢ₊₁] <- (2y)² WᵢWᵢ₊₂ + qrMul(WWy2(i+1), dy2, WW(i+1), ec->f, stack); + // [(2y)⁴WᵢWᵢ₊₂] <- (2y)² (2y)²WᵢWᵢ₊₂ + qrMul(WWy4(i+1), dy2, WWy2(i+1), ec->f, stack); + // [W₂ᵢ₊₁] <- 2y)⁴WᵢWᵢ₊₂Wᵢ² - Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + qrMul(t1, WW(i), W2(i+1), ec->f, stack); // Wᵢ₋₁Wᵢ₊₁ Wᵢ₊₁² + qrMul(W(2*i+1), WWy4(i+1), W2(i), ec->f, stack); + qrSub(W(2*i+1), W(2*i+1), t1, ec->f); } + // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² + if (i != SIZE_BIT_POS(w - 1)) + qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); } - // [1]P - c = pre->pts; - ecFromA(c, a, ec, stack); - c += nj; +/* SmallMultJ: этап 3 */ - // Этап 3) for (i = 3;;) { - // i=3,5..2ʷ⁻¹+1: [Xᵢ] = x Wᵢ² − (2y)²Wᵢ₋₁Wᵢ₊₁ - qrMul(ecX(c), x, W2(i), ec->f, stack); - qrSub(ecX(c), ecX(c), WWy2(i), ec->f); - - if (i == (SIZE_1 << (w - 1)) + 1) break; - - // i=3,5..2ʷ⁻¹-1: [Yᵢ] = y (Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²) + // [Xᵢ] <- x Wᵢ² − (2y)²Wᵢ₋₁Wᵢ₊₁, i=3,5,...,2ʷ⁻¹+1 + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); + qrSub(ecX(pt), ecX(pt), WWy2(i), ec->f); + if (i == SIZE_BIT_POS(w - 1) + 1) + break; + // [Yᵢ] <- y(Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²), i=3,5,...,2ʷ⁻¹-1 if (i < 4) - wwCopy(tmp, W(i+2), n); + wwCopy(t1, W(i + 2), n); else - qrMul(tmp, W(i+2), W2(i-1), ec->f, stack); + qrMul(t1, W(i + 2), W2(i - 1), ec->f, stack); if (i < 5) - wwCopy(ecY(c, n), W2(i+1), n); + wwCopy(ecY(pt, n), W2(i + 1), n); else - qrMul(ecY(c, n), W(i-2), W2(i+1), ec->f, stack); - qrSub(ecY(c, n), tmp, ecY(c, n), ec->f); - qrMul(ecY(c, n), y, ecY(c, n), ec->f, stack); - // [Zᵢ] = Wᵢ - wwCopy(ecZ(c, n), W(i), n); - - i += 2, c += nj; + qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); + qrSub(ecY(pt, n), t1, ecY(pt, n), ec->f); + qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); + // [Zᵢ] <- Wᵢ + wwCopy(ecZ(pt, n), W(i), n); + // к следующей точке + i += 2, pt += 3 * n; } - // Этап 4) -#undef WW -#define WW(i) (pWW) - for (; i <= (SIZE_1 << w) - 1;) + +/* SmallMultJ: этап 4 */ + + for (; i <= SIZE_BIT_POS(w) - 1;) { - // i=2ʷ⁻¹+1,2ʷ⁻¹+3..2ʷ-1: [Yᵢ] = y (Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²) + // [Yᵢ] <- y(Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²), i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 if (i < 4) - wwCopy(tmp, W(i+2), n); + wwCopy(t1, W(i + 2), n); else - qrMul(tmp, W(i+2), W2(i-1), ec->f, stack); + qrMul(t1, W(i + 2), W2(i - 1), ec->f, stack); if (i < 5) - wwCopy(ecY(c, n), W2(i+1), n); + wwCopy(ecY(pt, n), W2(i + 1), n); else - qrMul(ecY(c, n), W(i-2), W2(i+1), ec->f, stack); - - qrSub(ecY(c, n), tmp, ecY(c, n), ec->f); - qrMul(ecY(c, n), y, ecY(c, n), ec->f, stack); - // [Zᵢ] = Wᵢ - wwCopy(ecZ(c, n), W(i), n); - - if (i == (SIZE_1 << w) - 1) break; - i += 2, c += nj; - - // i=2ʷ⁻¹+3,2ʷ⁻¹+5..2ʷ-1: [Xᵢ] = x Wᵢ² − (2y)² Wᵢ₋₁ Wᵢ₊₁ - gfpMul2(tmp, W(i-1), W(i+1), W2(i-1), W2(i+1), ec->f, stack); // Wᵢ₋₁ Wᵢ₊₁ - qrMul(tmp, dy2, tmp, ec->f, stack); // (2y)² Wᵢ₋₁Wᵢ₊₁ - qrMul(ecX(c), x, W2(i), ec->f, stack); // x Wᵢ² - qrSub(ecX(c), ecX(c), tmp, ec->f); // x Wᵢ² − (2y)²Wᵢ₋₁Wᵢ₊₁ + qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); + qrSub(ecY(pt, n), t1, ecY(pt, n), ec->f); + qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); + // [Zᵢ] <- Wᵢ + wwCopy(ecZ(pt, n), W(i), n); + // к следующей точке + if (i == SIZE_BIT_POS(w) - 1) + break; + i += 2, pt += 3 * n; + // [Xᵢ] <- x Wᵢ² − (2y)² Wᵢ₋₁ Wᵢ₊₁, i=2ʷ⁻¹+3,2ʷ⁻¹+5..2ʷ-1 + gfpMul2(t1, W(i-1), W(i+1), W2(i-1), W2(i+1), ec->f, stack); + qrMul(t1, dy2, t1, ec->f, stack); // (2y)² Wᵢ₋₁Wᵢ₊₁ + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); // x Wᵢ² + qrSub(ecX(pt), ecX(pt), t1, ec->f); } - stack_wfree(pWW4); - stack_wfree(pWW2); - stack_wfree(pWW); - stack_wfree(pW2[1]); - stack_wfree(pW2[0]); - stack_wfree(pW); - stack_wfree(tmp2); - stack_wfree(tmp); - stack_wfree(dy2); - #undef W #undef W2 #undef WW #undef WWy2 #undef WWy4 - // заполнить остальные поля +/* SmallMultJ: end */ + + // заполнить служебные поля pre->type = ec_pre_snza; pre->w = w, pre->h = 0; } size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) { - size_t const ww = SIZE_1 << w; - size_t r = n * (0 - + 1 // dy2 - + 1 // tmp - + 1 // tmp2 - + (ww - 1) // pW - + (ww/2 - 1) // pW2[0] - + (ww/2 - 1) // pW2[1] - + 3 // pWW - + ww/4 // pWW2 - + 1 // pWW4 - ); - return O_OF_W(r) + f_deep; + return memSliceSize( + ecpPreSNZ_local(n, w), + f_deep, + SIZE_MAX); } - -#undef stack_walloc -#undef stack_wfree diff --git a/src/math/qr.c b/src/math/qr.c index 0271e6d1..508b4a1d 100644 --- a/src/math/qr.c +++ b/src/math/qr.c @@ -4,7 +4,7 @@ \brief Quotient rings \project bee2 [cryptographic library] \created 2013.09.14 -\version 2025.09.29 +\version 2026.02.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -103,7 +103,7 @@ void qrPower(word c[], const word a[], const word b[], size_t m, const qr_o* r, void* stack) { const size_t w = qrCalcSlideWidth(m); - const size_t powers_count = SIZE_1 << (w - 1); + const size_t powers_count = SIZE_BIT_POS(w - 1); register word slide; register size_t slide_size; size_t pos; @@ -182,7 +182,7 @@ void qrPower(word c[], const word a[], const word b[], size_t m, size_t qrPower_deep(size_t n, size_t m, size_t r_deep) { - const size_t powers_count = SIZE_1 << (qrCalcSlideWidth(m) - 1); + const size_t powers_count = SIZE_BIT_POS(qrCalcSlideWidth(m) - 1); return memSliceSize( qrPower_local(n, powers_count), r_deep, diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index b784e70d..27b563b1 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.01.29 +\version 2026.02.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -46,9 +47,10 @@ bool_t ecpBench() O_OF_W(2 * ec->f->n), O_OF_W(ec->f->n), sizeof(ec_pre_t) + O_OF_W(64 * 3 * ec->f->n), - utilMax(5, + utilMax(6, ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), ecPreSNZ_deep(ec->f->n, ec->d, ec->deep), + ecpPreSNZ_deep(ec->f->n, ec->f->deep, 6), ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, ec->f->n), ecPreSNZA_deep(ec->f->n, ec->d, ec->deep), ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), @@ -140,6 +142,28 @@ bool_t ecpBench() (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } + // ecPre[SNZ] + for (w = 3; w <= 6; ++w) + { + for (i = 0, ticks = tmTicks(); i < reps; ++i) + ecPreSNZ(pre, ec->base, w, ec, stack); + ticks = tmTicks() - ticks; + printf(" ecPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } + // ecpPre[SNZ] + for (w = 3; w <= 6; ++w) + { + for (i = 0, ticks = tmTicks(); i < reps; ++i) + ecpPreSNZ(pre, ec->base, w, ec, stack); + ticks = tmTicks() - ticks; + printf(" ecpPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } } // завершение blobClose(state); From bda29ec6bf680542c010fe10cc77f2506a21e56e Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 6 Feb 2026 17:25:00 +0300 Subject: [PATCH 30/77] Update math/ecp/pre --- src/math/ecp/ecp_pre.c | 171 ++++++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 80 deletions(-) diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index a5adba62..d67a4cc2 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -92,11 +92,12 @@ version 3. See Copyright Notices in bee2/info.h. /* t1 */ O_OF_W(n),\ /* t2 */ O_OF_W(n),\ /* dy2 */ O_OF_W(n),\ -/* pW */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ -/* pW2 */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ -/* pWW */ O_OF_W(3 * n),\ -/* pWW2 */ O_OF_W(SIZE_BIT_POS(w - 2) * n),\ -/* pWW4 */ O_OF_W(n) +/* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ +/* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ +/* WWs */ O_OF_W(3 * n),\ +/* WWy2s */ O_OF_W(SIZE_BIT_POS(w - 2) * n),\ +/* WWy4s */ O_OF_W(n),\ +/* WWWs */ O_OF_W((SIZE_BIT_POS(w - 1) - 2) * n) void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) @@ -105,11 +106,12 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* t1; /* [n] */ word* t2; /* [n] */ word* dy2; /* [n] (2y)² */ - word* pW; /* [(2^w - 1) * n] Wᵢ */ - word* pW2; /* [2 * (2^{w-1} - 1) * n] Wᵢ² */ - word* pWW; /* [3 * n] Wᵢ₋₁ Wᵢ₊₁ */ - word* pWWy2; /* [2^{w-2} * n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ - word* pWWy4; /* [n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ + word* Ws; /* [(2^w - 1) * n] Wᵢ */ + word* W2s; /* [(2^w - 2) * n] Wᵢ² */ + word* WWs; /* [3 * n] Wᵢ₋₁ Wᵢ₊₁ */ + word* WWy2s; /* [2^{w-2} * n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ + word* WWy4s; /* [n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ + word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁² */ size_t i; word* pt; // pre @@ -123,7 +125,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // разметить стек memSlice(stack, ecpPreSNZ_local(n, w), SIZE_0, SIZE_MAX, - &t1, &t2, &dy2, &pW, &pW2, &pWW, &pWWy2, &pWWy4, &stack); + &t1, &t2, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &WWWs, &stack); // первая точка pt = pre->pts; ecFromA(pt, a, ec, stack); @@ -131,72 +133,66 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, /*** SmallMultJ: этап 1 ***/ -#define W(i) (pW + ((i) - 3) * n) -#define W2(i) (pW2 + ((i) - 3) * n) -#define WW(i) (pWW + ((i) % 3) * n) -#define WWy2(i) (pWWy2 + (((i) - 3) >> 1) * n) -#define WWy4(i) (pWWy4) +#define W(i) (Ws + ((i) - 3) * n) +#define W2(i) (W2s + ((i) - 3) * n) +#define WW(i) (WWs + ((i) % 3) * n) +#define WWy2(i) (WWy2s + (((i) - 3) >> 1) * n) +#define WWy4(i) (WWy4s) +#define WWW(i) (WWWs + ((i) - 3) * n) // dy2 <- (2y)² gfpDouble(dy2, ecY(a, n), ec->f); qrSqr(dy2, dy2, ec->f, stack); - // [W₃], [W₄] + // W₃, W₄ { - word* x2 = pWW; + word* x2 = WWs; word* bx = x2 + n; word* a2 = bx + n; // (x2, bx, a2) <- (x², b x, A²) qrSqr(x2, ecX(a), ec->f, stack); qrMul(bx, ec->B, ecX(a), ec->f, stack); qrSqr(a2, ec->A, ec->f, stack); - // [W₃] - { - // [W₃] <- 3(x²+A)² - qrAdd(t1, x2, ec->A, ec->f); // x²+A - qrSqr(t1, t1, ec->f, stack); // (x²+A)² - gfpDouble(t2, t1, ec->f); // 2(x²+A)² - qrAdd(W(3), t1, t2, ec->f); // 3(x²+A)² - // [W₃] <- 3(x²+A)² − 4(a²−3Bx) - gfpDouble(t1, bx, ec->f); // 2Bx - qrAdd(t1, t1, bx, ec->f); // 3Bx - qrSub(t1, a2, t1, ec->f); // A²−3Bx - gfpDouble(t1, t1, ec->f); // 2(A²−3Bx) - gfpDouble(t1, t1, ec->f); // 4(A²−3Bx) - qrSub(W(3), W(3), t1, ec->f); - } - // [W₄] - { - // [W₄] <- 4Bx(5x²-A) - gfpDouble(t1, x2, ec->f); // 2x² - gfpDouble(t1, t1, ec->f); // 4x² - qrAdd(t1, t1, x2, ec->f); // 5x² - qrSub(t1, t1, ec->A, ec->f); // 5x²-A - qrMul(t1, bx, t1, ec->f, stack); // Bx(5x²-A) - gfpDouble(t1, t1, ec->f); // 2Bx(5x²-A) - gfpDouble(W(4), t1, ec->f); // 4Bx(5x²-A) - // [W₄] <- x⁶ + 4Bx(5x²-A) - qrMul(t1, x2, ecX(a), ec->f, stack); - qrSqr(t2, t1, ec->f, stack); - qrAdd(W(4), t2, W(4), ec->f); - // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - qrMul(t2, ec->A, ecX(a), ec->f, stack); // Ax - qrSub(t1, t1, t2, ec->f); // x³-Ax - qrMul(t1, t1, t2, ec->f, stack); // Ax(x³-Ax) - gfpDouble(t2, t1, ec->f); // 2Ax(x³-Ax) - gfpDouble(t2, t2, ec->f); // 4Ax(x³-Ax) - qrAdd(t1, t1, t2, ec->f); // 5Ax(x³-Ax) - qrAdd(W(4), W(4), t1, ec->f); - // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² - qrSqr(t1, ec->B, ec->f, stack); // B² - gfpDouble(t1, t1, ec->f); // 2B² - gfpDouble(t1, t1, ec->f); // 4B² - gfpDouble(t1, t1, ec->f); // 8B² - qrSub(W(4), W(4), t1, ec->f); - // [W₄] <- 2(x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² - A³) - qrMul(t2, a2, ec->A, ec->f, stack); // A³ - qrSub(W(4), W(4), t2, ec->f); - gfpDouble(W(4), W(4), ec->f); - } + // [W₃] <- 3(x²+A)² − 4(a²−3Bx) + qrAdd(t1, x2, ec->A, ec->f); // x²+A + qrSqr(t1, t1, ec->f, stack); // (x²+A)² + gfpDouble(t2, t1, ec->f); // 2(x²+A)² + qrAdd(W(3), t1, t2, ec->f); // 3(x²+A)² + gfpDouble(t1, bx, ec->f); // 2Bx + qrAdd(t1, t1, bx, ec->f); // 3Bx + qrSub(t1, a2, t1, ec->f); // A²−3Bx + gfpDouble(t1, t1, ec->f); // 2(A²−3Bx) + gfpDouble(t1, t1, ec->f); // 4(A²−3Bx) + qrSub(W(3), W(3), t1, ec->f); + // [W₄] <- 4Bx(5x²-A) + gfpDouble(t1, x2, ec->f); // 2x² + gfpDouble(t1, t1, ec->f); // 4x² + qrAdd(t1, t1, x2, ec->f); // 5x² + qrSub(t1, t1, ec->A, ec->f); // 5x²-A + qrMul(t1, bx, t1, ec->f, stack); // Bx(5x²-A) + gfpDouble(t1, t1, ec->f); // 2Bx(5x²-A) + gfpDouble(W(4), t1, ec->f); // 4Bx(5x²-A) + // [W₄] <- x⁶ + 4Bx(5x²-A) + qrMul(t1, x2, ecX(a), ec->f, stack); + qrSqr(t2, t1, ec->f, stack); + qrAdd(W(4), t2, W(4), ec->f); + // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) + qrMul(t2, ec->A, ecX(a), ec->f, stack); // Ax + qrSub(t1, t1, t2, ec->f); // x³-Ax + qrMul(t1, t1, t2, ec->f, stack); // Ax(x³-Ax) + gfpDouble(t2, t1, ec->f); // 2Ax(x³-Ax) + gfpDouble(t2, t2, ec->f); // 4Ax(x³-Ax) + qrAdd(t1, t1, t2, ec->f); // 5Ax(x³-Ax) + qrAdd(W(4), W(4), t1, ec->f); + // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² + qrSqr(t1, ec->B, ec->f, stack); // B² + gfpDouble(t1, t1, ec->f); // 2B² + gfpDouble(t1, t1, ec->f); // 4B² + gfpDouble(t1, t1, ec->f); // 8B² + qrSub(W(4), W(4), t1, ec->f); + // [W₄] <- 2(x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² - A³) + qrMul(t2, a2, ec->A, ec->f, stack); // A³ + qrSub(W(4), W(4), t2, ec->f); + gfpDouble(W(4), W(4), ec->f); } // [W₃²] <- W₃² qrSqr(W2(3), W(3), ec->f, stack); @@ -206,35 +202,50 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrSqr(W2(4), W(4), ec->f, stack); // [W₂W₄] <- W₄ qrCopy(WW(3), W(4), ec->f); - // [(2y)²W₂W₄] <- (2y)²W₂W₄ + // [(2y)²W₂W₄] <- (2y)² W₂W₄ qrMul(WWy2(3), dy2, WW(3), ec->f, stack); - // [(2y)⁴W₂W₄] <- (2y)²(2y)²W₂W₄ + // [(2y)⁴W₂W₄] <- (2y)² (2y)²W₂W₄ qrMul(WWy4(3), dy2, WWy2(3), ec->f, stack); - // [W₅] <- (2y)⁴W₂W₄ − W₁W₃W₃² - qrMul(t1, WW(2), W2(3), ec->f, stack); // W₁W₃W₃² + // [W₅] <- (2y)⁴W₂W₄ − W₁W₃ W₃² + qrMul(t1, WW(2), W2(3), ec->f, stack); qrSub(W(5), WWy4(3), t1, ec->f); // [W₅²] <- W₅² qrSqr(W2(5), W(5), ec->f, stack); + // [W₅W₂² − W₁W₄²] <- W₅ - W₄² + qrSub(WWW(3), W(5), W2(4), ec->f); + // [W₆] <- W₃(W₅W₂² − W₁W₄²) + qrMul(W(6), W(3), WWW(3), ec->f, stack); + // [W₆W₃² − W₂W₅²] <- W₆W₃² - W₅² + qrMul(WWW(4), W(6), W2(3), ec->f, stack); + qrSub(WWW(4), WWW(4), W2(5), ec->f); /* Этап 2 */ - // [W₂ᵢ], [W₂ᵢ₊₁], [Wᵢ Wᵢ₊₂], i=3,4...,2ʷ⁻¹ + // i=3,4...,2ʷ⁻¹: W₂ᵢ, W₂ᵢ₊₁, Wᵢ Wᵢ₊₂ for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) { // [WᵢWᵢ₊₂] <- ((Wᵢ + Wᵢ₊₂)² - Wᵢ² - Wᵢ₊₂²) / 2 - gfpMul2(WW(i+1), W(i), W(i+2), W2(i), W2(i+2), ec->f, stack); - // [W₆] <- W₃W₅ - W₁W₃W₄² - if (i == 3) + gfpMul2(WW(i + 1), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + +/* + // [Wᵢ₊₂Wᵢ² − Wᵢ₋₂Wᵢ₊₁²] <- Wᵢ₊₂Wᵢ² − Wᵢ₋₂Wᵢ₊₁² + if (i >= 5) { - qrMul(t1, WW(i-1), W2(i+1), ec->f, stack); // (W₁W₃) W₄² - qrSub(W(2*i), WW(i+1), t1, ec->f); + qrMul(WWW(i), W(i + 2), W2(i), ec->f, stack); + qrMul(t1, W(i - 2), W2(i + 1), ec->f, stack); + qrSub(WWW(i), WWW(i), t1, ec->f); } + // [W₂ᵢ] <- Wᵢ(Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁²) + if (i >= 4) + qrMul(W(2*i), W(i), WWW(i), ec->f, stack); +*/ + // [W₂ᵢ] <- (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² - else + if (i >= 4) { - qrMul(t1, WW(i-1), W2(i+1), ec->f, stack); // (Wᵢ₋₂Wᵢ)Wᵢ₊₁² - qrMul(W(2*i), WW(i+1), W2(i-1), ec->f, stack); // (WᵢWᵢ₊₂)Wᵢ₋₁² - qrSub(W(2*i), W(2*i), t1, ec->f); + qrMul(t1, WW(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i), WW(i + 1), W2(i - 1), ec->f, stack); + qrSub(W(2 * i), W(2 * i), t1, ec->f); } // [W₂ᵢ²] <- W₂ᵢ² qrSqr(W2(2*i), W(2*i), ec->f, stack); From b54e9b8ca8b6dbe43f166969b63c2c12004e9b84 Mon Sep 17 00:00:00 2001 From: agievich Date: Fri, 6 Feb 2026 23:29:12 +0300 Subject: [PATCH 31/77] Finish ecpPreSNZ() --- src/math/ecp/ecp_pre.c | 235 +++++++++++++++++------------------------ test/math/ecp_bench.c | 20 ++-- 2 files changed, 109 insertions(+), 146 deletions(-) diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index d67a4cc2..e0484065 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.05 +\version 2026.02.06 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -18,7 +18,10 @@ version 3. See Copyright Notices in bee2/info.h. /* ******************************************************************************* -Вспомогательные макросы +Ускорители + +В gfpMul2 выражение a * b при известных a^2 и b^2 вычисляется как + ((a + b)^2 - a^2 - b^2) / 2 ******************************************************************************* */ @@ -39,52 +42,39 @@ version 3. See Copyright Notices in bee2/info.h. многочленах деления и требует примерно 19/2 M + 7/2 S операций на каждую кратную точку. -Алгоритм выполняется в 4 этапа. Этапам соответствуют следующие шаги алгоритма: -- этап 1 -- шаги 1 -- 13; -- этап 2 -- шаги 14 -- 16; -- этап 3 -- шаги 17, 19; -- этап 4 -- шаги 18, 20. +Алгоритм выполняется в 3 этапа. Этапам соответствуют следующие шаги алгоритма: +- этап 1 -- шаги 1 -- 16; +- этап 2 -- шаги 17, 18, 20; +- этап 3 -- шаги 19, 21. Для повышения читабельности используются макросы, которые покрывают следующие переменные алгоритма: 1. W(i) -- значения многочленов Wᵢ, i=3,4,...,2ʷ+1: - * этапы: 0), 1), 3), 4); * память: 2ʷ-1 (элементов поля); - * значения рассчитываются на этапе 2 по индексам 2i и 2i+1. - * на этапе 1) значения считываются последовательно, по индексам i, i+2; - * на этапе 3) значения считываются по чётным индексам 2i; - * на этапе 4) значения считываются последовательно, по индексам - i-1,i,i+1,i+2; - * упростить кэширование не получается -- необходимо выделять память под - все значения. + * вычисляются на этапах 1 и 2, используются на этапах 1--3; + * кэшируются все значения. 2. W2(i) -- квадраты Wᵢ², i=3,4...,2ʷ: - * этапы: 2), 3), 4); * память: 2ʷ-2. -3. WW(i) -- произведения Wᵢ₋₁ Wᵢ₊₁, i=2,3...,2ʷ⁻¹+1: - * этапы: 0), 1), 4); - * память: 3; - * на этапе 1) значения формируются и используются последовательно - с индексами i-1,i,i+1 и поэтому можно выделять память лишь под 3 текущие - значения; - * на этапе 4) происходит чтение по индексу i-1 и запись по индексу i+1, - поэтому память можно выделять только под 1 значение. -4. WWy2(i) -- произведения (2y)² Wᵢ₋₁ Wᵢ₊₁, i=3,5,...,2ʷ⁻¹+1: - * этапы: 0), 1), 3); - * память: 2ʷ⁻²; - * значения формируются на этапах 0), 1), чтение -- на этапе 3). Кэшировать - * нужно все значения. -5. WWy4(i) -- текущее произведение (2y)⁴ Wᵢ₋₁ Wᵢ₊₁, i=3,5,...,2ʷ⁻¹-1: - * этапы: 0), 1); +3. WW(i) -- произведения WᵢWᵢ₊₂, i=1,3...,2ʷ⁻¹: + * память: 2; + * вычисляются на этапах 1 и 2, используются на этапах 1--3; + * кэшируются все значения. +4. WWy2(i) -- произведения (2y)²WᵢWᵢ₊₂, i=3,5,...,2ʷ⁻¹+1: + * память: 2; + * вычисляются и используются на этапах 1 и 2; + * можно хранить только 2 последовательных значения. +5. WWy4(i) -- произведения (2y)⁴WᵢWᵢ₊₂, i=3,5,...,2ʷ⁻¹-1: * память: 1; - * запись на предыдущем шаге, чтение на текущем. Кэшируется только текущее - значение. + * вычисляются и используются на этапах 1 и 2; + * можно хранить только текущее значение. +6. WWW(i) -- выражения Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁², i=3,4,...,2ʷ⁻¹: + * память: 2ʷ⁻¹-2; + * вычисляются на этапах 1 и 2, используются на этапах 1--3; + * кэшируются все значения. [APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication on Weierstrass curves using division polynomials. Mat. Vopr. Kriptogr., 13:2, 2022, https://doi.org/10.4213/mvk406. - -\todo Разобраться с шагом 15 SmallMultJ: на этом шаге значение W6 еще не -вычислено. ******************************************************************************* */ @@ -94,8 +84,8 @@ version 3. See Copyright Notices in bee2/info.h. /* dy2 */ O_OF_W(n),\ /* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ /* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ -/* WWs */ O_OF_W(3 * n),\ -/* WWy2s */ O_OF_W(SIZE_BIT_POS(w - 2) * n),\ +/* WWs */ O_OF_W(2 * n),\ +/* WWy2s */ O_OF_W(2 * n),\ /* WWy4s */ O_OF_W(n),\ /* WWWs */ O_OF_W((SIZE_BIT_POS(w - 1) - 2) * n) @@ -108,10 +98,10 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* dy2; /* [n] (2y)² */ word* Ws; /* [(2^w - 1) * n] Wᵢ */ word* W2s; /* [(2^w - 2) * n] Wᵢ² */ - word* WWs; /* [3 * n] Wᵢ₋₁ Wᵢ₊₁ */ - word* WWy2s; /* [2^{w-2} * n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ - word* WWy4s; /* [n] (2y)² Wᵢ₋₁ Wᵢ₊₁ */ - word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁² */ + word* WWs; /* [2 * n] WᵢWᵢ₊₂ */ + word* WWy2s; /* [2 * n] (2y)²WᵢWᵢ₊₂ */ + word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ + word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁² */ size_t i; word* pt; // pre @@ -131,15 +121,16 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ecFromA(pt, a, ec, stack); pt += 3 * n; -/*** SmallMultJ: этап 1 ***/ - +/*** SmallMultJ: begin ***/ #define W(i) (Ws + ((i) - 3) * n) #define W2(i) (W2s + ((i) - 3) * n) -#define WW(i) (WWs + ((i) % 3) * n) -#define WWy2(i) (WWy2s + (((i) - 3) >> 1) * n) +#define WW(i) (WWs + ((i) % 2) * n) +#define WWy2(i) (WWy2s + ((i) % 2) * n) #define WWy4(i) (WWy4s) #define WWW(i) (WWWs + ((i) - 3) * n) +/* SmallMultJ: этап 1 */ + // dy2 <- (2y)² gfpDouble(dy2, ecY(a, n), ec->f); qrSqr(dy2, dy2, ec->f, stack); @@ -171,11 +162,11 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrMul(t1, bx, t1, ec->f, stack); // Bx(5x²-A) gfpDouble(t1, t1, ec->f); // 2Bx(5x²-A) gfpDouble(W(4), t1, ec->f); // 4Bx(5x²-A) - // [W₄] <- x⁶ + 4Bx(5x²-A) + // [W₄] <- x⁶+4Bx(5x²-A) qrMul(t1, x2, ecX(a), ec->f, stack); qrSqr(t2, t1, ec->f, stack); qrAdd(W(4), t2, W(4), ec->f); - // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) qrMul(t2, ec->A, ecX(a), ec->f, stack); // Ax qrSub(t1, t1, t2, ec->f); // x³-Ax qrMul(t1, t1, t2, ec->f, stack); // Ax(x³-Ax) @@ -183,13 +174,13 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, gfpDouble(t2, t2, ec->f); // 4Ax(x³-Ax) qrAdd(t1, t1, t2, ec->f); // 5Ax(x³-Ax) qrAdd(W(4), W(4), t1, ec->f); - // [W₄] <- x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² qrSqr(t1, ec->B, ec->f, stack); // B² gfpDouble(t1, t1, ec->f); // 2B² gfpDouble(t1, t1, ec->f); // 4B² gfpDouble(t1, t1, ec->f); // 8B² qrSub(W(4), W(4), t1, ec->f); - // [W₄] <- 2(x⁶ + 4Bx(5x²-A) + 5Ax(x³-Ax) - 8B² - A³) + // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) qrMul(t2, a2, ec->A, ec->f, stack); // A³ qrSub(W(4), W(4), t2, ec->f); gfpDouble(W(4), W(4), ec->f); @@ -197,133 +188,106 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // [W₃²] <- W₃² qrSqr(W2(3), W(3), ec->f, stack); // [W₁W₃] <- W₃ - qrCopy(WW(2), W(3), ec->f); + qrCopy(WW(1), W(3), ec->f); // [W₄²] <- W₄² qrSqr(W2(4), W(4), ec->f, stack); // [W₂W₄] <- W₄ - qrCopy(WW(3), W(4), ec->f); + qrCopy(WW(2), W(4), ec->f); // [(2y)²W₂W₄] <- (2y)² W₂W₄ - qrMul(WWy2(3), dy2, WW(3), ec->f, stack); + qrMul(WWy2(2), dy2, WW(2), ec->f, stack); // [(2y)⁴W₂W₄] <- (2y)² (2y)²W₂W₄ - qrMul(WWy4(3), dy2, WWy2(3), ec->f, stack); - // [W₅] <- (2y)⁴W₂W₄ − W₁W₃ W₃² - qrMul(t1, WW(2), W2(3), ec->f, stack); - qrSub(W(5), WWy4(3), t1, ec->f); + qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); + // [W₅] <- (2y)⁴W₂W₄−W₁W₃W₃² + qrMul(t1, WW(1), W2(3), ec->f, stack); + qrSub(W(5), WWy4(2), t1, ec->f); // [W₅²] <- W₅² qrSqr(W2(5), W(5), ec->f, stack); - // [W₅W₂² − W₁W₄²] <- W₅ - W₄² + // [W₅W₂²−W₁W₄²] <- W₅-W₄² qrSub(WWW(3), W(5), W2(4), ec->f); - // [W₆] <- W₃(W₅W₂² − W₁W₄²) + // [W₆] <- W₃(W₅W₂²−W₁W₄²) qrMul(W(6), W(3), WWW(3), ec->f, stack); - // [W₆W₃² − W₂W₅²] <- W₆W₃² - W₅² + // [W₆W₃²−W₂W₅²] <- W₆W₃²-W₅² qrMul(WWW(4), W(6), W2(3), ec->f, stack); qrSub(WWW(4), WWW(4), W2(5), ec->f); -/* Этап 2 */ +/* SmallMultJ: этап 2 */ - // i=3,4...,2ʷ⁻¹: W₂ᵢ, W₂ᵢ₊₁, Wᵢ Wᵢ₊₂ + // W₂ᵢ, W₂ᵢ₊₁, Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁², WᵢWᵢ₊₂ для i=3,4...,2ʷ⁻¹ + // (Xᵢ:Yᵢ:Zᵢ) для нечетных i for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) { - // [WᵢWᵢ₊₂] <- ((Wᵢ + Wᵢ₊₂)² - Wᵢ² - Wᵢ₊₂²) / 2 - gfpMul2(WW(i + 1), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); - -/* - // [Wᵢ₊₂Wᵢ² − Wᵢ₋₂Wᵢ₊₁²] <- Wᵢ₊₂Wᵢ² − Wᵢ₋₂Wᵢ₊₁² + // [Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁²] <- Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁² if (i >= 5) { - qrMul(WWW(i), W(i + 2), W2(i), ec->f, stack); + qrMul(WWW(i), W(i + 2), W2(i - 1), ec->f, stack); qrMul(t1, W(i - 2), W2(i + 1), ec->f, stack); qrSub(WWW(i), WWW(i), t1, ec->f); } - // [W₂ᵢ] <- Wᵢ(Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁²) - if (i >= 4) - qrMul(W(2*i), W(i), WWW(i), ec->f, stack); -*/ - - // [W₂ᵢ] <- (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² + // [W₂ᵢ] <- Wᵢ(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) if (i >= 4) - { - qrMul(t1, WW(i - 1), W2(i + 1), ec->f, stack); - qrMul(W(2 * i), WW(i + 1), W2(i - 1), ec->f, stack); - qrSub(W(2 * i), W(2 * i), t1, ec->f); - } + qrMul(W(2 * i), W(i), WWW(i), ec->f, stack); // [W₂ᵢ²] <- W₂ᵢ² - qrSqr(W2(2*i), W(2*i), ec->f, stack); - // [W₂ᵢ₊₁] + qrSqr(W2(2 * i), W(2 * i), ec->f, stack); + // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 + gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + // W₂ᵢ₊₁, (Xᵢ:Yᵢ:Zᵢ) при нечетном i if (i & 1) { - // [W₂ᵢ₊₁] <- WᵢWᵢ₊₂Wᵢ² - (2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² - qrMul(t1, WWy4(i), W2(i+1), ec->f, stack); // (2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² - qrMul(W(2*i+1), WW(i+1), W2(i), ec->f, stack); // WᵢWᵢ₊₂Wᵢ² - qrSub(W(2*i+1), W(2*i+1), t1, ec->f); + // [W₂ᵢ₊₁] <- WᵢWᵢ₊₂Wᵢ²-(2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + qrMul(t1, WWy4(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t1, ec->f); + // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); + qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); + // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) + qrMul(ecY(pt, n), ecY(a, n), WWW(i), ec->f, stack); + // [Zᵢ] <- Wᵢ + wwCopy(ecZ(pt, n), W(i), n); + // к следующей точке + pt += 3 * n; } else { - // [W₂ᵢ₊₁] <- (2y)² WᵢWᵢ₊₂ - qrMul(WWy2(i+1), dy2, WW(i+1), ec->f, stack); - // [(2y)⁴WᵢWᵢ₊₂] <- (2y)² (2y)²WᵢWᵢ₊₂ - qrMul(WWy4(i+1), dy2, WWy2(i+1), ec->f, stack); - // [W₂ᵢ₊₁] <- 2y)⁴WᵢWᵢ₊₂Wᵢ² - Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² - qrMul(t1, WW(i), W2(i+1), ec->f, stack); // Wᵢ₋₁Wᵢ₊₁ Wᵢ₊₁² - qrMul(W(2*i+1), WWy4(i+1), W2(i), ec->f, stack); - qrSub(W(2*i+1), W(2*i+1), t1, ec->f); + // [(2y)²WᵢWᵢ₊₂] <- (2y)²WᵢWᵢ₊₂ + qrMul(WWy2(i), dy2, WW(i), ec->f, stack); + // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²(2y)²WᵢWᵢ₊₂ + qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); + // [W₂ᵢ₊₁] <- (2y)⁴WᵢWᵢ₊₂Wᵢ²-Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² + qrMul(t1, WW(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t1, ec->f); } // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² if (i != SIZE_BIT_POS(w - 1)) qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); } + // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ для i = 2ʷ⁻¹ + 1 + ASSERT(i == SIZE_BIT_POS(w - 1) + 1); + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); + qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); /* SmallMultJ: этап 3 */ - for (i = 3;;) + // (Xᵢ:Yᵢ:Zᵢ) для i=2ʷ⁻¹+1,...,2ʷ-1 + while (1) { - // [Xᵢ] <- x Wᵢ² − (2y)²Wᵢ₋₁Wᵢ₊₁, i=3,5,...,2ʷ⁻¹+1 - qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); - qrSub(ecX(pt), ecX(pt), WWy2(i), ec->f); - if (i == SIZE_BIT_POS(w - 1) + 1) - break; - // [Yᵢ] <- y(Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²), i=3,5,...,2ʷ⁻¹-1 - if (i < 4) - wwCopy(t1, W(i + 2), n); - else - qrMul(t1, W(i + 2), W2(i - 1), ec->f, stack); - if (i < 5) - wwCopy(ecY(pt, n), W2(i + 1), n); - else - qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); + // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) + qrMul(t1, W(i + 2), W2(i - 1), ec->f, stack); + qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); qrSub(ecY(pt, n), t1, ecY(pt, n), ec->f); qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); // [Zᵢ] <- Wᵢ wwCopy(ecZ(pt, n), W(i), n); - // к следующей точке - i += 2, pt += 3 * n; - } - -/* SmallMultJ: этап 4 */ - - for (; i <= SIZE_BIT_POS(w) - 1;) - { - // [Yᵢ] <- y(Wᵢ₊₂ Wᵢ₋₁² - Wᵢ₋₂ Wᵢ₊₁²), i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 - if (i < 4) - wwCopy(t1, W(i + 2), n); - else - qrMul(t1, W(i + 2), W2(i - 1), ec->f, stack); - if (i < 5) - wwCopy(ecY(pt, n), W2(i + 1), n); - else - qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); - qrSub(ecY(pt, n), t1, ecY(pt, n), ec->f); - qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); - // [Zᵢ] <- Wᵢ - wwCopy(ecZ(pt, n), W(i), n); - // к следующей точке + // последняя точка? if (i == SIZE_BIT_POS(w) - 1) break; + // к следующей точке i += 2, pt += 3 * n; - // [Xᵢ] <- x Wᵢ² − (2y)² Wᵢ₋₁ Wᵢ₊₁, i=2ʷ⁻¹+3,2ʷ⁻¹+5..2ʷ-1 - gfpMul2(t1, W(i-1), W(i+1), W2(i-1), W2(i+1), ec->f, stack); - qrMul(t1, dy2, t1, ec->f, stack); // (2y)² Wᵢ₋₁Wᵢ₊₁ - qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); // x Wᵢ² + // [Xᵢ] <- x Wᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ + gfpMul2(t1, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); + qrMul(t1, dy2, t1, ec->f, stack); + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); qrSub(ecX(pt), ecX(pt), t1, ec->f); } @@ -332,11 +296,10 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, #undef WW #undef WWy2 #undef WWy4 - /* SmallMultJ: end */ // заполнить служебные поля - pre->type = ec_pre_snza; + pre->type = ec_pre_snz; pre->w = w, pre->h = 0; } diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 27b563b1..f3fa23d3 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.05 +\version 2026.02.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -78,21 +78,21 @@ bool_t ecpBench() ecMulA(pt, ec->base, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecPre+MulPre[SNZ] + // ecpPre+MulPre[SNZ] for (w = 3; w <= 6; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, ec->f->no, combo_state); - ecPreSNZ(pre, ec->base, w, ec, stack); + ecpPreSNZ(pre, ec->base, w, ec, stack); ecMulPreSNZ(pt, pre, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf(" ecPre+ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecpPre+ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); @@ -107,7 +107,7 @@ bool_t ecpBench() ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf(" ecPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); @@ -122,7 +122,7 @@ bool_t ecpBench() ecMulPreSNZ(pt, pre, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); @@ -137,7 +137,7 @@ bool_t ecpBench() ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); @@ -148,7 +148,7 @@ bool_t ecpBench() for (i = 0, ticks = tmTicks(); i < reps; ++i) ecPreSNZ(pre, ec->base, w, ec, stack); ticks = tmTicks() - ticks; - printf(" ecPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", + printf(" ecPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); @@ -159,7 +159,7 @@ bool_t ecpBench() for (i = 0, ticks = tmTicks(); i < reps; ++i) ecpPreSNZ(pre, ec->base, w, ec, stack); ticks = tmTicks() - ticks; - printf(" ecpPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", + printf(" ecpPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); From 6bef02f34b0f5f871958a5e2b08d533d284ec3dd Mon Sep 17 00:00:00 2001 From: agievich Date: Sun, 8 Feb 2026 00:48:56 +0300 Subject: [PATCH 32/77] Start ecpPreSNZA() --- include/bee2/math/ecp.h | 46 ++-- src/math/ecp/ecp_pre.c | 474 +++++++++++++++++++++++++++++++++------- test/math/ecp_test.c | 9 +- 3 files changed, 438 insertions(+), 91 deletions(-) diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 9b63ccb9..06caa37d 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.01.26 +\version 2026.02.07 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -267,21 +267,15 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); /*! \brief Предвычисления по схеме SNZ - На эллиптической кривой ec вычисляются 2^{w-1} малых нечетных кратных - аффинной точки [2 * ec->f->n]a. Кратные размещаются в буфере - [2^{w-1} * ec->d * ec->f->n]pre: - \code - pre[0:2^{w-1}) <- (a, 3a, ..., (2^w - 1)a). - \endcode - Каждое кратное является проективной точкой и занимает ec->d * ec->f->n - машинных слов. - \pre w >= 3. + На эллиптической кривой ec выполняются предвычисления по схеме SNZ + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. \pre Описание ec работоспособно. - \pre Буферы a и pre либо не пересекаются, либо указатели a и pre совпадают. \pre Координаты a лежат в базовом поле. + \pre w >= 3. \expect Описание ec корректно. - \expect Точка a лежит на кривой. - \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->f->deep, w). + \expect Точка a лежит на ec. + \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->d, ec->deep, w). */ void ecpPreSNZ( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -293,6 +287,32 @@ void ecpPreSNZ( size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w); +/*! \brief Предвычисления по схеме SNZA + + На эллиптической кривой ec выполняются предвычисления по схеме SNZA + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w >= 3. + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + случае. + \remark При обнаружении точки O предвычисления прерываются с неопределенным + результатом в pre->pts. + \deep{stack} ecPreSNZA_deep(ec->f->n, ec->d, ec->deep). +*/ +bool_t ecpPreSNZA( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecpPreSNZA_deep(size_t n, size_t f_deep, size_t w); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index e0484065..ff134fc8 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.06 +\version 2026.02.07 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -43,12 +43,12 @@ version 3. See Copyright Notices in bee2/info.h. кратную точку. Алгоритм выполняется в 3 этапа. Этапам соответствуют следующие шаги алгоритма: -- этап 1 -- шаги 1 -- 16; -- этап 2 -- шаги 17, 18, 20; -- этап 3 -- шаги 19, 21. +- этап 1 -- шаги 1 -- 16 (расчет начальных выражений); +- этап 2 -- шаги 17, 18, 20 (расчет основных выражений); +- этап 3 -- шаги 19, 21 (формирование точек). Для повышения читабельности используются макросы, которые покрывают следующие -переменные алгоритма: +выражения: 1. W(i) -- значения многочленов Wᵢ, i=3,4,...,2ʷ+1: * память: 2ʷ-1 (элементов поля); * вычисляются на этапах 1 и 2, используются на этапах 1--3; @@ -62,11 +62,11 @@ version 3. See Copyright Notices in bee2/info.h. 4. WWy2(i) -- произведения (2y)²WᵢWᵢ₊₂, i=3,5,...,2ʷ⁻¹+1: * память: 2; * вычисляются и используются на этапах 1 и 2; - * можно хранить только 2 последовательных значения. + * кэшируются 2 последовательных значения: текущее и предыдущее. 5. WWy4(i) -- произведения (2y)⁴WᵢWᵢ₊₂, i=3,5,...,2ʷ⁻¹-1: * память: 1; * вычисляются и используются на этапах 1 и 2; - * можно хранить только текущее значение. + * кэшируется только текущее значение. 6. WWW(i) -- выражения Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁², i=3,4,...,2ʷ⁻¹: * память: 2ʷ⁻¹-2; * вычисляются на этапах 1 и 2, используются на этапах 1--3; @@ -78,9 +78,13 @@ version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* */ +#if defined(W) || defined(W2) || defined(WW) || defined(WWy2) ||\ + defined(WWy4) || defined(WWW) + #error "Conflicting preprocessor definitions" +#endif + #define ecpPreSNZ_local(n, w)\ -/* t1 */ O_OF_W(n),\ -/* t2 */ O_OF_W(n),\ +/* t */ O_OF_W(n),\ /* dy2 */ O_OF_W(n),\ /* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ /* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ @@ -93,8 +97,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { size_t n; - word* t1; /* [n] */ - word* t2; /* [n] */ + word* t; /* [n] */ word* dy2; /* [n] (2y)² */ word* Ws; /* [(2^w - 1) * n] Wᵢ */ word* W2s; /* [(2^w - 2) * n] Wᵢ² */ @@ -115,7 +118,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // разметить стек memSlice(stack, ecpPreSNZ_local(n, w), SIZE_0, SIZE_MAX, - &t1, &t2, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &WWWs, &stack); + &t, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &WWWs, &stack); // первая точка pt = pre->pts; ecFromA(pt, a, ec, stack); @@ -136,7 +139,8 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrSqr(dy2, dy2, ec->f, stack); // W₃, W₄ { - word* x2 = WWs; + word* t1 = W2s; + word* x2 = t1 + n; word* bx = x2 + n; word* a2 = bx + n; // (x2, bx, a2) <- (x², b x, A²) @@ -144,69 +148,69 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrMul(bx, ec->B, ecX(a), ec->f, stack); qrSqr(a2, ec->A, ec->f, stack); // [W₃] <- 3(x²+A)² − 4(a²−3Bx) - qrAdd(t1, x2, ec->A, ec->f); // x²+A - qrSqr(t1, t1, ec->f, stack); // (x²+A)² - gfpDouble(t2, t1, ec->f); // 2(x²+A)² - qrAdd(W(3), t1, t2, ec->f); // 3(x²+A)² - gfpDouble(t1, bx, ec->f); // 2Bx - qrAdd(t1, t1, bx, ec->f); // 3Bx - qrSub(t1, a2, t1, ec->f); // A²−3Bx - gfpDouble(t1, t1, ec->f); // 2(A²−3Bx) - gfpDouble(t1, t1, ec->f); // 4(A²−3Bx) - qrSub(W(3), W(3), t1, ec->f); + qrAdd(t, x2, ec->A, ec->f); // x²+A + qrSqr(t, t, ec->f, stack); // (x²+A)² + gfpDouble(t1, t, ec->f); // 2(x²+A)² + qrAdd(W(3), t, t1, ec->f); // 3(x²+A)² + gfpDouble(t, bx, ec->f); // 2Bx + qrAdd(t, t, bx, ec->f); // 3Bx + qrSub(t, a2, t, ec->f); // A²−3Bx + gfpDouble(t, t, ec->f); // 2(A²−3Bx) + gfpDouble(t, t, ec->f); // 4(A²−3Bx) + qrSub(W(3), W(3), t, ec->f); // [W₄] <- 4Bx(5x²-A) - gfpDouble(t1, x2, ec->f); // 2x² - gfpDouble(t1, t1, ec->f); // 4x² - qrAdd(t1, t1, x2, ec->f); // 5x² - qrSub(t1, t1, ec->A, ec->f); // 5x²-A - qrMul(t1, bx, t1, ec->f, stack); // Bx(5x²-A) - gfpDouble(t1, t1, ec->f); // 2Bx(5x²-A) - gfpDouble(W(4), t1, ec->f); // 4Bx(5x²-A) + gfpDouble(t, x2, ec->f); // 2x² + gfpDouble(t, t, ec->f); // 4x² + qrAdd(t, t, x2, ec->f); // 5x² + qrSub(t, t, ec->A, ec->f); // 5x²-A + qrMul(t, bx, t, ec->f, stack); // Bx(5x²-A) + gfpDouble(t, t, ec->f); // 2Bx(5x²-A) + gfpDouble(W(4), t, ec->f); // 4Bx(5x²-A) // [W₄] <- x⁶+4Bx(5x²-A) - qrMul(t1, x2, ecX(a), ec->f, stack); - qrSqr(t2, t1, ec->f, stack); - qrAdd(W(4), t2, W(4), ec->f); + qrMul(t, x2, ecX(a), ec->f, stack); + qrSqr(t1, t, ec->f, stack); + qrAdd(W(4), t1, W(4), ec->f); // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) - qrMul(t2, ec->A, ecX(a), ec->f, stack); // Ax - qrSub(t1, t1, t2, ec->f); // x³-Ax - qrMul(t1, t1, t2, ec->f, stack); // Ax(x³-Ax) - gfpDouble(t2, t1, ec->f); // 2Ax(x³-Ax) - gfpDouble(t2, t2, ec->f); // 4Ax(x³-Ax) - qrAdd(t1, t1, t2, ec->f); // 5Ax(x³-Ax) - qrAdd(W(4), W(4), t1, ec->f); + qrMul(t1, ec->A, ecX(a), ec->f, stack); // Ax + qrSub(t, t, t1, ec->f); // x³-Ax + qrMul(t, t, t1, ec->f, stack); // Ax(x³-Ax) + gfpDouble(t1, t, ec->f); // 2Ax(x³-Ax) + gfpDouble(t1, t1, ec->f); // 4Ax(x³-Ax) + qrAdd(t, t, t1, ec->f); // 5Ax(x³-Ax) + qrAdd(W(4), W(4), t, ec->f); // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² - qrSqr(t1, ec->B, ec->f, stack); // B² - gfpDouble(t1, t1, ec->f); // 2B² - gfpDouble(t1, t1, ec->f); // 4B² - gfpDouble(t1, t1, ec->f); // 8B² - qrSub(W(4), W(4), t1, ec->f); + qrSqr(t, ec->B, ec->f, stack); // B² + gfpDouble(t, t, ec->f); // 2B² + gfpDouble(t, t, ec->f); // 4B² + gfpDouble(t, t, ec->f); // 8B² + qrSub(W(4), W(4), t, ec->f); // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) - qrMul(t2, a2, ec->A, ec->f, stack); // A³ - qrSub(W(4), W(4), t2, ec->f); + qrMul(t1, a2, ec->A, ec->f, stack); // A³ + qrSub(W(4), W(4), t1, ec->f); gfpDouble(W(4), W(4), ec->f); } // [W₃²] <- W₃² qrSqr(W2(3), W(3), ec->f, stack); - // [W₁W₃] <- W₃ - qrCopy(WW(1), W(3), ec->f); // [W₄²] <- W₄² qrSqr(W2(4), W(4), ec->f, stack); + // [W₁W₃] <- W₃ + qrCopy(WW(1), W(3), ec->f); // [W₂W₄] <- W₄ qrCopy(WW(2), W(4), ec->f); - // [(2y)²W₂W₄] <- (2y)² W₂W₄ + // [(2y)²W₂W₄] <- (2y)²(W₂W₄) qrMul(WWy2(2), dy2, WW(2), ec->f, stack); - // [(2y)⁴W₂W₄] <- (2y)² (2y)²W₂W₄ + // [(2y)⁴W₂W₄] <- (2y)²((2y)²W₂W₄) qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); - // [W₅] <- (2y)⁴W₂W₄−W₁W₃W₃² - qrMul(t1, WW(1), W2(3), ec->f, stack); - qrSub(W(5), WWy4(2), t1, ec->f); + // [W₅] <- (2y)⁴W₂W₄−(W₁W₃)W₃² + qrMul(t, WW(1), W2(3), ec->f, stack); + qrSub(W(5), WWy4(2), t, ec->f); // [W₅²] <- W₅² qrSqr(W2(5), W(5), ec->f, stack); // [W₅W₂²−W₁W₄²] <- W₅-W₄² qrSub(WWW(3), W(5), W2(4), ec->f); // [W₆] <- W₃(W₅W₂²−W₁W₄²) qrMul(W(6), W(3), WWW(3), ec->f, stack); - // [W₆W₃²−W₂W₅²] <- W₆W₃²-W₅² + // [W₆W₃²−W₂W₅²] <- (W₆W₃)W₃-W₅² qrMul(WWW(4), W(6), W2(3), ec->f, stack); qrSub(WWW(4), WWW(4), W2(5), ec->f); @@ -220,8 +224,8 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, if (i >= 5) { qrMul(WWW(i), W(i + 2), W2(i - 1), ec->f, stack); - qrMul(t1, W(i - 2), W2(i + 1), ec->f, stack); - qrSub(WWW(i), WWW(i), t1, ec->f); + qrMul(t, W(i - 2), W2(i + 1), ec->f, stack); + qrSub(WWW(i), WWW(i), t, ec->f); } // [W₂ᵢ] <- Wᵢ(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) if (i >= 4) @@ -230,13 +234,14 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrSqr(W2(2 * i), W(2 * i), ec->f, stack); // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); - // W₂ᵢ₊₁, (Xᵢ:Yᵢ:Zᵢ) при нечетном i + // W₂ᵢ₊₁ + //(Xᵢ:Yᵢ:Zᵢ) при нечетном i if (i & 1) { - // [W₂ᵢ₊₁] <- WᵢWᵢ₊₂Wᵢ²-(2y)⁴Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² - qrMul(t1, WWy4(i - 1), W2(i + 1), ec->f, stack); + // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); - qrSub(W(2 * i + 1), W(2 * i + 1), t1, ec->f); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); @@ -251,12 +256,12 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, { // [(2y)²WᵢWᵢ₊₂] <- (2y)²WᵢWᵢ₊₂ qrMul(WWy2(i), dy2, WW(i), ec->f, stack); - // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²(2y)²WᵢWᵢ₊₂ + // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²((2y)²WᵢWᵢ₊₂) qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); - // [W₂ᵢ₊₁] <- (2y)⁴WᵢWᵢ₊₂Wᵢ²-Wᵢ₋₁Wᵢ₊₁Wᵢ₊₁² - qrMul(t1, WW(i - 1), W2(i + 1), ec->f, stack); + // [W₂ᵢ₊₁] <- ((2y)⁴WᵢWᵢ₊₂)Wᵢ²-(Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WW(i - 1), W2(i + 1), ec->f, stack); qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); - qrSub(W(2 * i + 1), W(2 * i + 1), t1, ec->f); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); } // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² if (i != SIZE_BIT_POS(w - 1)) @@ -273,9 +278,9 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, while (1) { // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) - qrMul(t1, W(i + 2), W2(i - 1), ec->f, stack); + qrMul(t, W(i + 2), W2(i - 1), ec->f, stack); qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); - qrSub(ecY(pt, n), t1, ecY(pt, n), ec->f); + qrSub(ecY(pt, n), t, ecY(pt, n), ec->f); qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); // [Zᵢ] <- Wᵢ wwCopy(ecZ(pt, n), W(i), n); @@ -285,17 +290,18 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // к следующей точке i += 2, pt += 3 * n; // [Xᵢ] <- x Wᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ - gfpMul2(t1, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); - qrMul(t1, dy2, t1, ec->f, stack); + gfpMul2(t, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); + qrMul(t, dy2, t, ec->f, stack); qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); - qrSub(ecX(pt), ecX(pt), t1, ec->f); + qrSub(ecX(pt), ecX(pt), t, ec->f); } -#undef W -#undef W2 -#undef WW -#undef WWy2 +#undef WWW #undef WWy4 +#undef WWy2 +#undef WW +#undef W2 +#undef W /* SmallMultJ: end */ // заполнить служебные поля @@ -310,3 +316,323 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) f_deep, SIZE_MAX); } + +/* +******************************************************************************* +Предвычисления: схема SNZA + +Реализован алгоритм SmallMultA, предложенный в [APS22]. Алгоритм основан на +многочленах деления и требует примерно 25/2 M + 5 S операций на каждую +кратную точку и дополнительно операцию 1I для одновременнного обращения +нескольких элементов поля. + +Алгоритм выполняется в 4 этапа. Этапам соответствуют следующие шаги алгоритма: +- этап 1 -- шаги 1 -- 13 (расчет начальных выражений); +- этап 2 -- шаг 14, частично шаги 16 и 18 (расчет основных выражений); +- этап 3 -- шаг 15 (обращение элементов поля); +- этап 4 -- шаги 16 -- 20 (формирование точек). + +В целом кэшируются те же выражения, что и в ecpPreSNZ(). Отличия: +- не кэшируется выражение Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁². Соответственно макрос + WWW не используется; +- кэшируется не 2, а 3 последовательных значения WᵢWᵢ₊₂. Это связано + с тем, что на итерациях этапа 2 используются пары выражений + Wᵢ₋₁Wᵢ₊₁ и WᵢWᵢ₊₂, а на итерациях этапа 4 -- пары Wᵢ₋₂Wᵢ и WᵢWᵢ₊₂. + +Дополнительный макрос: +7. W2I(i) -- выражения Wᵢ⁻², i=3,5,...,2ʷ-1: + * память: 2ʷ⁻¹-1; + * на этапе 2 кэшируются значения (∏ᵥ Wᵥ²: v = 3,5,...,i); + * на этапе 3 вычисляются актуальные значения Wᵢ⁻², которые используются на + этапе 4. + +Точки (xᵢ,yᵢ), i=3,5,..,2ʷ⁻¹-1, начинают формироваться на этапе 2 и завершают +на этапе 4, после обращения Wᵢ². +******************************************************************************* +*/ + +#if defined(W) || defined(W2) || defined(WW) || defined(WWy2) ||\ + defined(WWy4) || defined(W2I) + #error "Conflicting preprocessor definitions" +#endif + +#define ecpPreSNZA_local(n, w)\ +/* t */ O_OF_W(n),\ +/* dy2 */ O_OF_W(n),\ +/* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ +/* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ +/* WWs */ O_OF_W(3 * n),\ +/* WWy2s */ O_OF_W(2 * n),\ +/* WWy4s */ O_OF_W(n),\ +/* W2Is */ O_OF_W((SIZE_BIT_POS(w - 1) - 1) * n) + +bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, + void* stack) +{ + size_t n; + word* t; /* [n] */ + word* dy2; /* [n] (2y)² */ + word* Ws; /* [(2^w - 1) * n] Wᵢ */ + word* W2s; /* [(2^w - 2) * n] Wᵢ² */ + word* WWs; /* [3 * n] WᵢWᵢ₊₂ */ + word* WWy2s; /* [2 * n] (2y)²WᵢWᵢ₊₂ */ + word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ + word* W2Is; /* [(2^{w-1} - 1) * n] Wᵢ⁻² */ + size_t i; + word* pt; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(w >= 3); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * SIZE_BIT_POS(w - 1)), + a, O_OF_W(2 * ec->f->n))); + // размерности + n = ec->f->n; + // разметить стек + memSlice(stack, + ecpPreSNZA_local(n, w), SIZE_0, SIZE_MAX, + &t, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &W2Is, &stack); + // первая точка + pt = pre->pts; + wwCopy(pt, a, 2 * n); + pt += 2 * n; + +/*** SmallMultA: begin ***/ +#define W(i) (Ws + ((i) - 3) * n) +#define W2(i) (W2s + ((i) - 3) * n) +#define WW(i) (WWs + ((i) % 3) * n) +#define WWy2(i) (WWy2s + ((i) % 2) * n) +#define WWy4(i) (WWy4s) +#define W2I(i) (W2Is + ((i) >> 1) * n) + +/* SmallMultA: этап 1 */ + + // dy2 <- (2y)² + gfpDouble(dy2, ecY(a, n), ec->f); + qrSqr(dy2, dy2, ec->f, stack); + // W₃, W₄ + { + word* t1 = W2s; + word* x2 = t1 + n; + word* bx = x2 + n; + word* a2 = bx + n; + // (x2, bx, a2) <- (x², b x, A²) + qrSqr(x2, ecX(a), ec->f, stack); + qrMul(bx, ec->B, ecX(a), ec->f, stack); + qrSqr(a2, ec->A, ec->f, stack); + // [W₃] <- 3(x²+A)² − 4(a²−3Bx) + qrAdd(t, x2, ec->A, ec->f); // x²+A + qrSqr(t, t, ec->f, stack); // (x²+A)² + gfpDouble(t1, t, ec->f); // 2(x²+A)² + qrAdd(W(3), t, t1, ec->f); // 3(x²+A)² + gfpDouble(t, bx, ec->f); // 2Bx + qrAdd(t, t, bx, ec->f); // 3Bx + qrSub(t, a2, t, ec->f); // A²−3Bx + gfpDouble(t, t, ec->f); // 2(A²−3Bx) + gfpDouble(t, t, ec->f); // 4(A²−3Bx) + qrSub(W(3), W(3), t, ec->f); + // [W₄] <- 4Bx(5x²-A) + gfpDouble(t, x2, ec->f); // 2x² + gfpDouble(t, t, ec->f); // 4x² + qrAdd(t, t, x2, ec->f); // 5x² + qrSub(t, t, ec->A, ec->f); // 5x²-A + qrMul(t, bx, t, ec->f, stack); // Bx(5x²-A) + gfpDouble(t, t, ec->f); // 2Bx(5x²-A) + gfpDouble(W(4), t, ec->f); // 4Bx(5x²-A) + // [W₄] <- x⁶+4Bx(5x²-A) + qrMul(t, x2, ecX(a), ec->f, stack); + qrSqr(t1, t, ec->f, stack); + qrAdd(W(4), t1, W(4), ec->f); + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) + qrMul(t1, ec->A, ecX(a), ec->f, stack); // Ax + qrSub(t, t, t1, ec->f); // x³-Ax + qrMul(t, t, t1, ec->f, stack); // Ax(x³-Ax) + gfpDouble(t1, t, ec->f); // 2Ax(x³-Ax) + gfpDouble(t1, t1, ec->f); // 4Ax(x³-Ax) + qrAdd(t, t, t1, ec->f); // 5Ax(x³-Ax) + qrAdd(W(4), W(4), t, ec->f); + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² + qrSqr(t, ec->B, ec->f, stack); // B² + gfpDouble(t, t, ec->f); // 2B² + gfpDouble(t, t, ec->f); // 4B² + gfpDouble(t, t, ec->f); // 8B² + qrSub(W(4), W(4), t, ec->f); + // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) + qrMul(t1, a2, ec->A, ec->f, stack); // A³ + qrSub(W(4), W(4), t1, ec->f); + gfpDouble(W(4), W(4), ec->f); + } + // [W₃²] <- W₃² + qrSqr(W2(3), W(3), ec->f, stack); + // [W₄²] <- W₄² + qrSqr(W2(4), W(4), ec->f, stack); + // [W₁W₃] <- W₃ + qrCopy(WW(1), W(3), ec->f); + // [W₂W₄] <- W₄ + qrCopy(WW(2), W(4), ec->f); + // [(2y)²W₂W₄] <- (2y)² W₂W₄ + qrMul(WWy2(2), dy2, WW(2), ec->f, stack); + // [(2y)⁴W₂W₄] <- (2y)² (2y)²W₂W₄ + qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); + // [W₅] <- (2y)⁴W₂W₄−W₁W₃W₃² + qrMul(t, WW(1), W2(3), ec->f, stack); + qrSub(W(5), WWy4(2), t, ec->f); + // [W₅²] <- W₅² + qrSqr(W2(5), W(5), ec->f, stack); + // [W₆] <- W₃(W₅W₂²−W₁W₄²) + qrSqr(W2(5), W(5), ec->f, stack); + +/* SmallMultA: этап 2 */ + + // [W₃⁻²] <- W₃ + wwCopy(W2I(3), W(3), n); + // W₂ᵢ, W₂ᵢ₊₁, WᵢWᵢ₊₂ для i=3,4...,2ʷ⁻¹ + // Vᵢ и предварительные точки (xᵢ,yᵢ) для нечетных i + for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) + { + // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 + gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + // W₂ᵢ + if (i == 3) + { + // [W₆] <- W₃W₅-(W₁W₃)W₄² + qrMul(t, WW(1), W2(4), ec->f, stack); + qrSub(W(6), WW(3), t, ec->f); + } + else + { + // [W₂ᵢ] <- (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² + qrMul(t, WW(i - 2), W2(i + 1), ec->f, stack); + qrMul(W(2 * i), WW(i), W2(i - 1), ec->f, stack); + qrSub(W(2 * i), W(2 * i), t, ec->f); + } + // [W₂ᵢ²] <- W₂ᵢ² + qrSqr(W2(2 * i), W(2 * i), ec->f, stack); + // W₂ᵢ₊₁ + // Vᵢ и предварительные (xᵢ,yᵢ) при нечетном i + if (i & 1) + { + // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + // [xᵢ] <- (2y)²Wᵢ₋₁Wᵢ₊₁ + wwCopy(ecX(pt), WWy4(i - 1), n); + // [yᵢ] <- yW₂ᵢ + qrMul(ecY(pt, n), ecY(a, n), W2(2 * i), ec->f, stack); + // [Wᵢ⁻²] <- [Wᵢ₋₂⁻²]Wᵢ² + if (i >= 5) + qrMul(W2I(i), W2I(i - 2), W2(i), ec->f, stack); + // к следующей точке + pt += 2 * n; + } + else + { + // [(2y)²WᵢWᵢ₊₂] <- (2y)²(WᵢWᵢ₊₂) + qrMul(WWy2(i), dy2, WW(i), ec->f, stack); + // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²((2y)²WᵢWᵢ₊₂) + qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); + // [W₂ᵢ₊₁] <- ((2y)⁴WᵢWᵢ₊₂)Wᵢ²-(Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WW(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + } + // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² + if (i != SIZE_BIT_POS(w - 1)) + qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); + } + // [xᵢ] <- (2y)²Wᵢ₋₁Wᵢ₊₁ для i = 2ʷ⁻¹ + 1 + ASSERT(i == SIZE_BIT_POS(w - 1) + 1); + wwCopy(ecX(pt), WWy2(i - 1), n); + +/* SmallMultA: этап 3 */ + + // обращение + i = SIZE_BIT_POS(w) - 1; + // (∏ᵥ Wᵥ²: v = 3,5,...,2ʷ-1) == 0? + if (qrIsZero(W2I(i), ec->f)) + return FALSE; + { + word* t1 = WWy4s; + // t <- (∏ᵥ Wᵥ²: v <= 2ʷ-1)⁻¹ + qrInv(t, W2I(i), ec->f, stack); + // Wᵢ⁻² + for (; i > 3; i -= 2) + { + // t1 <- (∏ᵥ Wᵥ²: v < i)⁻¹ + qrMul(t1, t, W2I(i), ec->f, stack); + // [Wᵢ⁻²] <- (∏ᵥ Wᵥ²: v <= i)⁻¹ (∏ᵥ Wᵥ²: v < i) + qrMul(W2I(i), t, W2I(i - 2), ec->f, stack); + // t <- t1 + wwCopy(t, t1, n); + } + // [W₃⁻²] <- (∏ᵥ Wᵥ²: v <= 3)⁻¹ + wwCopy(W2I(3), t, n); + } + +/* SmallMultA: этап 4 */ + + // (xᵢ,yᵢ) для i=3,5,..,2ʷ⁻¹-1 + ASSERT(i == 3); + for (pt = pre->pts + 2 * n; i < SIZE_BIT_POS(w - 1); i += 2, pt += 2 * n) + { + // [xᵢ] <- x-[xᵢ]Wᵢ⁻² = x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² + qrMul(ecX(pt), ecX(pt), W2I(i), ec->f, stack); + qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); + // [yᵢ] <- [yᵢ](Wᵢ⁻²)² = (yW₂ᵢ)(Wᵢ⁻²)² + qrSqr(t, W2I(i), ec->f, stack); + qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); + } + // [xᵢ] <- x-[xᵢ]Wᵢ⁻² = x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² для i=2ʷ⁻¹+1 + ASSERT(i == SIZE_BIT_POS(w - 1) + 1); + qrMul(ecX(pt), ecX(pt), W2I(i), ec->f, stack); + qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); + // (xᵢ,yᵢ) для i=2ʷ⁻¹+1,...,2ʷ-1 + while (1) + { + if (i == SIZE_BIT_POS(w) - 1) + // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 + gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + else + // [WᵢWᵢ₊₂] <- WᵢWᵢ₊₂ + qrMul(WW(i), W(i), W(i + 2), ec->f, stack); + // [yᵢ] <- y((WᵢWᵢ₊₂)Wᵢ₋₁²-(Wᵢ₋₂Wᵢ)Wᵢ₊₁²)(Wᵢ⁻²)² + qrMul(t, WW(i), W2(i - 1), ec->f, stack); + qrMul(ecY(pt, n), WW(i - 2), W2(i + 1), ec->f, stack); + qrSub(ecY(pt, n), t, ecY(pt, n), ec->f); + qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); + qrSqr(t, W2I(i), ec->f, stack); + qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); + // последняя точка? + if (i == SIZE_BIT_POS(w) - 1) + break; + // к следующей точке + i += 2, pt += 2 * n; + // [xᵢ] <- x−(2y)²(Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² + gfpMul2(t, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); + qrMul(t, dy2, t, ec->f, stack); + qrMul(t, t, W2I(i), ec->f, stack); + qrSub(ecX(pt), ecX(a), t, ec->f); + } + +#undef W2I +#undef WWy4 +#undef WWy2 +#undef WW +#undef W2 +#undef W +/* SmallMultA: end */ + + // заполнить служебные поля + pre->type = ec_pre_snza; + pre->w = w, pre->h = 0; + return TRUE; +} + +size_t ecpPreSNZA_deep(size_t n, size_t f_deep, size_t w) +{ + return memSliceSize( + ecpPreSNZA_local(n, w), + f_deep, + SIZE_MAX); +} diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index c1fdc125..a94ef5ae 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.04 +\version 2026.02.07 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -82,7 +82,7 @@ bool_t ecpTest() O_OF_W(3 * n), O_OF_W(3 * n), O_OF_W(n + 1), - utilMax(18, + utilMax(19, gfpCreate_deep(no), ecpCreateJ_deep(n, f_deep), ecGroupCreate_deep(f_deep), @@ -96,9 +96,10 @@ bool_t ecpTest() ecpSubAA_deep(n, f_deep), ecMulA_deep(n, 3, ec_deep, n), ecPreSNZ_deep(n, 3, ec_deep), + ecPreSNZA_deep(n, 3, ec_deep), ecpPreSNZ_deep(n, f_deep, 6), + ecpPreSNZA_deep(n, f_deep, 6), ecMulPreSNZ_deep(n, 3, ec_deep, n), - ecPreSNZA_deep(n, 3, ec_deep), ecMulPreSNZA_deep(n, 3, ec_deep, n), ecAddMulA_deep(n, 3, ec_deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, @@ -367,7 +368,7 @@ bool_t ecpTest() { size_t i; // pt[0..32) <- (1, 3, ..., 31)base - if (!ecPreSNZA(pre, ec->base, 6, ec, stack)) + if (!ecpPreSNZA(pre, ec->base, 6, ec, stack)) { blobClose(state); return FALSE; From ac88c90836878456b04887c521fca8c4cfa1f252 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 9 Feb 2026 17:17:16 +0300 Subject: [PATCH 33/77] Finish ecpPreSNZA() --- include/bee2/math/ec.h | 4 ++ src/math/ec.c | 27 ++------ src/math/ecp/ecp_pre.c | 136 +++++++++++++++++++++-------------------- test/math/ecp_bench.c | 15 ++--- test/math/ecp_test.c | 30 ++++++--- 5 files changed, 109 insertions(+), 103 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index fe2b0b95..7deef99c 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -770,6 +770,8 @@ bool_t ecPreIsOperable( \expect Описание ec корректно. \expect Точка a лежит на ec. \deep{stack} ecPreSNZ_deep(ec->f->n, ec->d, ec->deep). + \remark Функция ecPreSNZ() не оптимальна. Ускоренный алгоритм вычисления + малых нечетных кратных для кривых над GF(p) реализован в ecpPreSNZ(). */ void ecPreSNZ( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -796,6 +798,8 @@ size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep); \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. \deep{stack} ecPreSNZA_deep(ec->f->n, ec->d, ec->deep). + \remark Функция ecPreSNZA() не оптимальна. Ускоренный алгоритм вычисления + малых нечетных кратных для кривых над GF(p) реализован в ecpPreSNZA(). */ bool_t ecPreSNZA( ec_pre_t* pre, /*!< [out] предвычисленные точки */ diff --git a/src/math/ec.c b/src/math/ec.c index b04e410c..e711c1be 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.05 +\version 2026.02.09 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -128,26 +128,9 @@ P <- P + A и P <- P + P меняются на A <- A + A. ----------------------------------------------------- * [или прямых вычислений в аффинных координатах] -\remark Функция ecPreSNZ() и особенно ecPreSNZA() не оптимальны. Для -ускорения второй функции можно рассчитать малые кратные в проективных -координатах, а затем быстро перейти к аффинным координатам с помощью -одновременного обращения сразу нескольких элементов поля. Одновременное -обращение известно как трюк Монтгомери [Mon87] (см. также -[Doc05; algorithm 11.15, p. 209]): - U_1 <- Z_1 - for t = 2,..., T: U_t <- U_{t-1} Z_t - V <- U_T^{-1} - for t = T,..., 2: - Z_t^{-1} <- V U_{t-1} - V <- V Z_t - Z_1^{-1} <- V -Трюк не применяется, поскольку на данном уровне описания кривой структура -проективных координат не определена. - -[Mon87] Montogomery P, Speeding the Pollard and elliptic curve method of - factorization. Mathematics of Computation, 48 (177), 1987, 243--264. -[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and - Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. +\remark Функции ecPreSNZ() и особенно ecPreSNZA() не оптимальны. Ускоренные +алгоритмы предвычислений для кривых над GF(p) реализованы в функциях +ecPreSNZ() и ecpPreSNZA(). ******************************************************************************* */ @@ -287,7 +270,7 @@ size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep) (2^{w - 2} - 2) + l / (w + 1) -> min. В функции ecMulA() реализована композиция функций ecPreSNZ() (предвычисления) -и ecMulPreNAF(). +и ecMulPreNAF(). [HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve Cryptography, Springer, 2004. diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index ff134fc8..f56f3ce0 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.07 +\version 2026.02.09 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -44,31 +44,32 @@ version 3. See Copyright Notices in bee2/info.h. Алгоритм выполняется в 3 этапа. Этапам соответствуют следующие шаги алгоритма: - этап 1 -- шаги 1 -- 16 (расчет начальных выражений); -- этап 2 -- шаги 17, 18, 20 (расчет основных выражений); -- этап 3 -- шаги 19, 21 (формирование точек). +- этап 2 -- шаги 17, 18, 20 (расчет основных выражений, формирование первой + половины точек); +- этап 3 -- шаги 19, 21 (формирование оставшихся точек). Для повышения читабельности используются макросы, которые покрывают следующие выражения: -1. W(i) -- значения многочленов Wᵢ, i=3,4,...,2ʷ+1: - * память: 2ʷ-1 (элементов поля); +1. W(i) -- значения многочленов Wᵢ, i = 3, 4, ..., 2ʷ + 1: + * память: 2ʷ - 1 (элементов поля); * вычисляются на этапах 1 и 2, используются на этапах 1--3; * кэшируются все значения. -2. W2(i) -- квадраты Wᵢ², i=3,4...,2ʷ: - * память: 2ʷ-2. -3. WW(i) -- произведения WᵢWᵢ₊₂, i=1,3...,2ʷ⁻¹: +2. W2(i) -- квадраты Wᵢ², i = 3, 4, ..., 2ʷ: + * память: 2ʷ - 2. +3. WW(i) -- произведения WᵢWᵢ₊₂, i = 1, 2, ..., 2ʷ⁻¹: * память: 2; * вычисляются на этапах 1 и 2, используются на этапах 1--3; * кэшируются все значения. -4. WWy2(i) -- произведения (2y)²WᵢWᵢ₊₂, i=3,5,...,2ʷ⁻¹+1: +4. WWy2(i) -- произведения (2y)²WᵢWᵢ₊₂, i = 2, 4, ..., 2ʷ⁻¹: * память: 2; * вычисляются и используются на этапах 1 и 2; * кэшируются 2 последовательных значения: текущее и предыдущее. -5. WWy4(i) -- произведения (2y)⁴WᵢWᵢ₊₂, i=3,5,...,2ʷ⁻¹-1: +5. WWy4(i) -- произведения (2y)⁴WᵢWᵢ₊₂, i = 2, 4, ..., 2ʷ⁻¹: * память: 1; * вычисляются и используются на этапах 1 и 2; * кэшируется только текущее значение. -6. WWW(i) -- выражения Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁², i=3,4,...,2ʷ⁻¹: - * память: 2ʷ⁻¹-2; +6. WWW(i) -- выражения Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁², i = 3, 4, ..., 2ʷ⁻¹: + * память: 2ʷ⁻¹ - 2; * вычисляются на этапах 1 и 2, используются на этапах 1--3; * кэшируются все значения. @@ -104,7 +105,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* WWs; /* [2 * n] WᵢWᵢ₊₂ */ word* WWy2s; /* [2 * n] (2y)²WᵢWᵢ₊₂ */ word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ - word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁² */ + word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁² */ size_t i; word* pt; // pre @@ -147,7 +148,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrSqr(x2, ecX(a), ec->f, stack); qrMul(bx, ec->B, ecX(a), ec->f, stack); qrSqr(a2, ec->A, ec->f, stack); - // [W₃] <- 3(x²+A)² − 4(a²−3Bx) + // [W₃] <- 3(x²+A)²−4(a²−3Bx) qrAdd(t, x2, ec->A, ec->f); // x²+A qrSqr(t, t, ec->f, stack); // (x²+A)² gfpDouble(t1, t, ec->f); // 2(x²+A)² @@ -206,7 +207,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrSub(W(5), WWy4(2), t, ec->f); // [W₅²] <- W₅² qrSqr(W2(5), W(5), ec->f, stack); - // [W₅W₂²−W₁W₄²] <- W₅-W₄² + // [W₅W₂² − W₁W₄²] <- W₅-W₄² qrSub(WWW(3), W(5), W2(4), ec->f); // [W₆] <- W₃(W₅W₂²−W₁W₄²) qrMul(W(6), W(3), WWW(3), ec->f, stack); @@ -216,7 +217,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, /* SmallMultJ: этап 2 */ - // W₂ᵢ, W₂ᵢ₊₁, Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁², WᵢWᵢ₊₂ для i=3,4...,2ʷ⁻¹ + // W₂ᵢ, W₂ᵢ₊₁, Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁², WᵢWᵢ₊₂ для i=3,4,...,2ʷ⁻¹ // (Xᵢ:Yᵢ:Zᵢ) для нечетных i for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) { @@ -274,7 +275,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, /* SmallMultJ: этап 3 */ - // (Xᵢ:Yᵢ:Zᵢ) для i=2ʷ⁻¹+1,...,2ʷ-1 + // (Xᵢ:Yᵢ:Zᵢ) для i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 while (1) { // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) @@ -328,26 +329,43 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) Алгоритм выполняется в 4 этапа. Этапам соответствуют следующие шаги алгоритма: - этап 1 -- шаги 1 -- 13 (расчет начальных выражений); -- этап 2 -- шаг 14, частично шаги 16 и 18 (расчет основных выражений); +- этап 2 -- шаг 14 (расчет основных выражений); - этап 3 -- шаг 15 (обращение элементов поля); - этап 4 -- шаги 16 -- 20 (формирование точек). В целом кэшируются те же выражения, что и в ecpPreSNZ(). Отличия: -- не кэшируется выражение Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁². Соответственно макрос +- не кэшируется выражение Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁². Соответственно макрос WWW не используется; +- выражения (2y)²WᵢWᵢ₊₂ сохраняются в координатах выходных точек. + В частности, при четном i выражение (2y)²WᵢWᵢ₊₂ сохраняется в x-координате + (i + 1)-й точки и используется на этапе 4 для вычисления xᵢ₊₁; - кэшируется не 2, а 3 последовательных значения WᵢWᵢ₊₂. Это связано с тем, что на итерациях этапа 2 используются пары выражений Wᵢ₋₁Wᵢ₊₁ и WᵢWᵢ₊₂, а на итерациях этапа 4 -- пары Wᵢ₋₂Wᵢ и WᵢWᵢ₊₂. Дополнительный макрос: -7. W2I(i) -- выражения Wᵢ⁻², i=3,5,...,2ʷ-1: - * память: 2ʷ⁻¹-1; - * на этапе 2 кэшируются значения (∏ᵥ Wᵥ²: v = 3,5,...,i); - * на этапе 3 вычисляются актуальные значения Wᵢ⁻², которые используются на - этапе 4. - -Точки (xᵢ,yᵢ), i=3,5,..,2ʷ⁻¹-1, начинают формироваться на этапе 2 и завершают -на этапе 4, после обращения Wᵢ². +7. W2I(i) -- выражения Wᵢ⁻², i = 3, 5, ..., 2ʷ - 1: + * память: 2ʷ⁻¹ - 1; + * вычисляются на этапе 3, используются на этапе 4; + * предварительно в ячейках памяти для выражений размещаются произведения + (∏ᵥ Wᵥ²: v <= i). + +\remark Произведения (∏ᵥ Wᵥ²: v <= i) используются для одновременного +обращения (Wᵢ²) с помощью следующего алгоритма: +1. V₃ <- W₃². +2. Для i = 5, 7, ..., m = 2ʷ - 1: + 1) Vᵢ <- Vᵢ₋₂Wᵢ² == (∏ᵥ Wᵥ²: v <= i). +3. t <- Vₘ⁻¹ = (∏ᵥ Wᵥ²: v <= m)⁻¹. +4. Для i = m, m - 2, ..., 5: + 1) (Wᵢ⁻², t) <- (t Vᵢ₋₂, t Wᵢ²). +5. W₃⁻² <- t. +Алгоритм предложен в [Mon87] (см. также [Doc05; algorithm 11.15, p. 209]) +и известен как трюк Монтгомери. + +[Mon87] Montogomery P, Speeding the Pollard and elliptic curve method of + factorization. Mathematics of Computation, 48 (177), 1987, 243--264. +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. ******************************************************************************* */ @@ -362,7 +380,6 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) /* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ /* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ /* WWs */ O_OF_W(3 * n),\ -/* WWy2s */ O_OF_W(2 * n),\ /* WWy4s */ O_OF_W(n),\ /* W2Is */ O_OF_W((SIZE_BIT_POS(w - 1) - 1) * n) @@ -375,7 +392,6 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* Ws; /* [(2^w - 1) * n] Wᵢ */ word* W2s; /* [(2^w - 2) * n] Wᵢ² */ word* WWs; /* [3 * n] WᵢWᵢ₊₂ */ - word* WWy2s; /* [2 * n] (2y)²WᵢWᵢ₊₂ */ word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ word* W2Is; /* [(2^{w-1} - 1) * n] Wᵢ⁻² */ size_t i; @@ -391,7 +407,7 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // разметить стек memSlice(stack, ecpPreSNZA_local(n, w), SIZE_0, SIZE_MAX, - &t, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &W2Is, &stack); + &t, &dy2, &Ws, &W2s, &WWs, &WWy4s, &W2Is, &stack); // первая точка pt = pre->pts; wwCopy(pt, a, 2 * n); @@ -401,9 +417,9 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, #define W(i) (Ws + ((i) - 3) * n) #define W2(i) (W2s + ((i) - 3) * n) #define WW(i) (WWs + ((i) % 3) * n) -#define WWy2(i) (WWy2s + ((i) % 2) * n) +#define WWy2(i) (pre->pts + (i) * n) #define WWy4(i) (WWy4s) -#define W2I(i) (W2Is + ((i) >> 1) * n) +#define W2I(i) (W2Is + (((i) - 3) >> 1) * n) /* SmallMultA: этап 1 */ @@ -484,10 +500,7 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, /* SmallMultA: этап 2 */ - // [W₃⁻²] <- W₃ - wwCopy(W2I(3), W(3), n); // W₂ᵢ, W₂ᵢ₊₁, WᵢWᵢ₊₂ для i=3,4...,2ʷ⁻¹ - // Vᵢ и предварительные точки (xᵢ,yᵢ) для нечетных i for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) { // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 @@ -509,22 +522,12 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // [W₂ᵢ²] <- W₂ᵢ² qrSqr(W2(2 * i), W(2 * i), ec->f, stack); // W₂ᵢ₊₁ - // Vᵢ и предварительные (xᵢ,yᵢ) при нечетном i if (i & 1) { // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); - // [xᵢ] <- (2y)²Wᵢ₋₁Wᵢ₊₁ - wwCopy(ecX(pt), WWy4(i - 1), n); - // [yᵢ] <- yW₂ᵢ - qrMul(ecY(pt, n), ecY(a, n), W2(2 * i), ec->f, stack); - // [Wᵢ⁻²] <- [Wᵢ₋₂⁻²]Wᵢ² - if (i >= 5) - qrMul(W2I(i), W2I(i - 2), W2(i), ec->f, stack); - // к следующей точке - pt += 2 * n; } else { @@ -541,32 +544,31 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, if (i != SIZE_BIT_POS(w - 1)) qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); } - // [xᵢ] <- (2y)²Wᵢ₋₁Wᵢ₊₁ для i = 2ʷ⁻¹ + 1 - ASSERT(i == SIZE_BIT_POS(w - 1) + 1); - wwCopy(ecX(pt), WWy2(i - 1), n); /* SmallMultA: этап 3 */ - // обращение - i = SIZE_BIT_POS(w) - 1; - // (∏ᵥ Wᵥ²: v = 3,5,...,2ʷ-1) == 0? + // (∏ᵥ Wᵥ²: v <= i) + wwCopy(W2I(3), W2(3), n); + for (i = 3; i + 2 < SIZE_BIT_POS(w); i += 2) + qrMul(W2I(i + 2), W2I(i), W2(i + 2), ec->f, stack); + // t <- (∏ᵥ Wᵥ²: v <= 2ʷ-1)⁻¹ + ASSERT(i == SIZE_BIT_POS(w) - 1); if (qrIsZero(W2I(i), ec->f)) return FALSE; + qrInv(t, W2I(i), ec->f, stack); + // Wᵢ⁻² { word* t1 = WWy4s; - // t <- (∏ᵥ Wᵥ²: v <= 2ʷ-1)⁻¹ - qrInv(t, W2I(i), ec->f, stack); - // Wᵢ⁻² for (; i > 3; i -= 2) { - // t1 <- (∏ᵥ Wᵥ²: v < i)⁻¹ - qrMul(t1, t, W2I(i), ec->f, stack); - // [Wᵢ⁻²] <- (∏ᵥ Wᵥ²: v <= i)⁻¹ (∏ᵥ Wᵥ²: v < i) + // t1 <- (∏ᵥ Wᵥ²: v <= i)⁻¹ Wᵢ² == (∏ᵥ Wᵥ²: v < i)⁻¹ + qrMul(t1, t, W2(i), ec->f, stack); + // [Wᵢ⁻²] <- (∏ᵥ Wᵥ²: v <= i)⁻¹ (∏ᵥ Wᵥ²: v < i) == Wᵥ⁻² qrMul(W2I(i), t, W2I(i - 2), ec->f, stack); // t <- t1 wwCopy(t, t1, n); } - // [W₃⁻²] <- (∏ᵥ Wᵥ²: v <= 3)⁻¹ + // [W₃⁻²] <- (∏ᵥ Wᵥ²: v = 3)⁻¹ wwCopy(W2I(3), t, n); } @@ -574,23 +576,25 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // (xᵢ,yᵢ) для i=3,5,..,2ʷ⁻¹-1 ASSERT(i == 3); - for (pt = pre->pts + 2 * n; i < SIZE_BIT_POS(w - 1); i += 2, pt += 2 * n) + for (; i < SIZE_BIT_POS(w - 1); i += 2, pt += 2 * n) { - // [xᵢ] <- x-[xᵢ]Wᵢ⁻² = x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² - qrMul(ecX(pt), ecX(pt), W2I(i), ec->f, stack); + // [xᵢ] <- x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² + qrMul(ecX(pt), WWy2(i - 1), W2I(i), ec->f, stack); qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); - // [yᵢ] <- [yᵢ](Wᵢ⁻²)² = (yW₂ᵢ)(Wᵢ⁻²)² + // [yᵢ] <- yW₂ᵢ(Wᵢ⁻²)² qrSqr(t, W2I(i), ec->f, stack); + qrMul(ecY(pt, n), ecY(a, n), W(2 * i), ec->f, stack); qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); } - // [xᵢ] <- x-[xᵢ]Wᵢ⁻² = x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² для i=2ʷ⁻¹+1 + // [xᵢ] <- x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² для i=2ʷ⁻¹+1 ASSERT(i == SIZE_BIT_POS(w - 1) + 1); - qrMul(ecX(pt), ecX(pt), W2I(i), ec->f, stack); + qrMul(ecX(pt), WWy2(i - 1), W2I(i), ec->f, stack); qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); - // (xᵢ,yᵢ) для i=2ʷ⁻¹+1,...,2ʷ-1 + + // (xᵢ,yᵢ) для i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 while (1) { - if (i == SIZE_BIT_POS(w) - 1) + if (i != SIZE_BIT_POS(w) - 1) // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); else diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index f3fa23d3..2f0f8ea0 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.06 +\version 2026.02.09 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -47,12 +47,13 @@ bool_t ecpBench() O_OF_W(2 * ec->f->n), O_OF_W(ec->f->n), sizeof(ec_pre_t) + O_OF_W(64 * 3 * ec->f->n), - utilMax(6, + utilMax(7, ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), ecPreSNZ_deep(ec->f->n, ec->d, ec->deep), + ecPreSNZA_deep(ec->f->n, ec->d, ec->deep), ecpPreSNZ_deep(ec->f->n, ec->f->deep, 6), + ecpPreSNZA_deep(ec->f->n, ec->f->deep, 6), ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, ec->f->n), - ecPreSNZA_deep(ec->f->n, ec->d, ec->deep), ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); @@ -82,7 +83,7 @@ bool_t ecpBench() (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecpPre+MulPre[SNZ] + // ecpPre+ecMulPre[SNZ] for (w = 3; w <= 6; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) @@ -97,17 +98,17 @@ bool_t ecpBench() (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecPre+MulPre[SNZA] + // ecpPre+ecMulPre[SNZA] for (w = 4; w <= 6; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, ec->f->no, combo_state); - ecPreSNZA(pre, ec->base, w, ec, stack); + ecpPreSNZA(pre, ec->base, w, ec, stack); ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); } ticks = tmTicks() - ticks; - printf(" ecPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecpPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index a94ef5ae..923cb2e1 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.07 +\version 2026.02.09 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -47,8 +47,17 @@ static size_t cofactor = 1; ******************************************************************************* */ +typedef void (*ec_pre_snz_i)(ec_pre_t* pre, const word a[], size_t w, + const struct ec_o* ec, void* stack); + +typedef bool_t (*ec_pre_snza_i)(ec_pre_t* pre, const word a[], size_t w, + const struct ec_o* ec, void* stack); + bool_t ecpTest() { + // функции предвычислений + const ec_pre_snz_i pre_snz_fn[] = { ecPreSNZ, ecpPreSNZ }; + const ec_pre_snza_i pre_snza_fn[] = { ecPreSNZA, ecpPreSNZA }; // размерности const size_t n = W_OF_O(no); const size_t f_keep = gfpCreate_keep(no); @@ -69,6 +78,8 @@ bool_t ecpTest() word* pt5; /* [3 * n] */ word* d; /* [n + 1] */ void* stack; + // другие переменные + size_t pos; // создать состояние state = blobCreate2( ec_keep, @@ -101,7 +112,8 @@ bool_t ecpTest() ecpPreSNZA_deep(n, f_deep, 6), ecMulPreSNZ_deep(n, 3, ec_deep, n), ecMulPreSNZA_deep(n, 3, ec_deep, n), - ecAddMulA_deep(n, 3, ec_deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), + ecAddMulA_deep(n, 3, ec_deep, 4, + (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, &ec, &f, &t, &pre, &pt0, &pt1, &pt2, &pt3, &pt4, &pt5, &d, &stack); if (state == 0) @@ -338,10 +350,11 @@ bool_t ecpTest() } } // предвычисления по схеме SNZ + for (pos = 0; pos < COUNT_OF(pre_snz_fn); ++pos) { size_t i; // pt[0..32) <- (1, 3, ..., 31)base - ecpPreSNZ(pre, ec->base, 6, ec, stack); + pre_snz_fn[pos](pre, ec->base, 6, ec, stack); // pt0 <- \sum_{i = 0}^31 2^{31 - i} pt[i] wwCopy(pt0, ecPrePt(pre, 0, ec), 3 * n); for (i = 1; i < 32; ++i) @@ -365,10 +378,11 @@ bool_t ecpTest() } } // предвычисления по схеме SNZA + for (pos = 0; pos < COUNT_OF(pre_snza_fn); ++pos) { size_t i; // pt[0..32) <- (1, 3, ..., 31)base - if (!ecpPreSNZA(pre, ec->base, 6, ec, stack)) + if (!pre_snza_fn[pos](pre, ec->base, 6, ec, stack)) { blobClose(state); return FALSE; @@ -402,11 +416,11 @@ bool_t ecpTest() // кратная точка: ecMulA vs ecMulPreSNZ { size_t w; - for (w = 5; w <= 6; ++w) + for (w = 3; w <= 6; ++w) { wwCopy(d, ec->order, n); zzSubW2(d, n, 1); - ecPreSNZ(pre, ec->base, w, ec, stack); + ecpPreSNZ(pre, ec->base, w, ec, stack); while (!wwIsZero(d, n)) { if (!ecMulPreSNZ(pt0, pre, ec, d, n, stack) || @@ -429,11 +443,11 @@ bool_t ecpTest() // кратная точка: ecMulA vs ecMulPreSNZA { size_t w; - for (w = 5; w <= 6; ++w) + for (w = 3; w <= 6; ++w) { wwCopy(d, ec->order, n); zzSubW2(d, n, 1); - ecPreSNZA(pre, ec->base, w, ec, stack); + ecpPreSNZA(pre, ec->base, w, ec, stack); while (!wwIsZero(d, n)) { if (!ecMulPreSNZA(pt0, pre, ec, d, n, stack) || From fac6b4f97b5e740c97313e3e161582e072f07ea2 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 9 Feb 2026 19:33:55 +0300 Subject: [PATCH 34/77] Start math/ec/pre/snzh --- include/bee2/math/ec.h | 110 ++++++++++++++++++++++++++-- include/bee2/math/ecp.h | 4 ++ src/math/ec.c | 156 +++++++++++++++++++++++++++++++++++++--- test/math/ecp_bench.c | 24 ++++++- 4 files changed, 274 insertions(+), 20 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 7deef99c..16979a71 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -136,7 +136,7 @@ ecCreate() и сохраняется в поле deep описания крив В функцию ecCreate() могут не передаваться параметры base, order и cofactor, определяющие строение группы точек, хотя буферы для base и order могут готовиться в ecCreate(). Параметры могут устанавливаться позднее с помощью -ecGroupCreate() или подобных функций. +ecGroupCreate(). \pre Все указатели действительны. @@ -485,6 +485,7 @@ typedef enum { ec_pre_snz = 1, /*!< схема SNZ */ ec_pre_snza, /*!< схема SNZA */ + ec_pre_snzh, /*!< схема SNZH */ ec_pre_hpb, /*!< схема HPB */ } ec_pre_type; @@ -495,18 +496,34 @@ typedef enum Число предвычисленных точек определяется шириной окна w и обычно равняется 2^{w-1}. Поддерживаются следующие схемы: - SNZ (Signed Non-Zero): вычисляется проективные точки + \code a, 3a, ..., (2^w-1)a; + \endcode - SNZA: вычисляются аффинные точки + \code a, 3a, ..., (2^w-1)a; + \endcode + - SNZH: вычисляются аффинные точки + \code + 2^{0} (a, 3a, ..., (2^w-1)a), + 2^{w} (a, 3a, ..., (2^w-1)a), + 2^{2w} (a, 3a, ..., (2^w-1)a), + ... + 2^{(h-1)w} (a, 3a, ..., (2^w-1)a); + \endcode - HPB: вычисляются аффинные точки + \code [b_{w-1} ... b_1 b_0]a = \sum_{i=0}^{w-1} b_i 2^{h i} a, + \endcode где b_i in {-1, 1}, b_{w-1} == 1. Точка [b_{w-1} ... b_1 b_0]a размещаются в массиве pts по индексу, двоичная запись которого есть + \code с_{w-1}...c_1 c_0, + \endcode где c_i = 0, если b_i = 1, и c_i = 1, если b_i = -1. . \pre 0 < w && w < B_PER_W. - \pre h > 0 <=> type == ec_pre_hpb. + \pre h > 0 <=> type \in { ec_pre_snzh, ec_pre_hpb }. \remark Схему SNZ предложено в использовать в работе [OkeTak03], схему HPB -- в работе [HPB05]. . @@ -751,7 +768,7 @@ bool_t ecGroupIsOperable( - буфер [sizeof(ec_pre_t)]pre корректен; - pre->type \in ec_pre_type; - pre->w > 0; - - pre->h > 0 <=> pre->type == ec_pre_hpb. + - pre->h > 0 <=> pre->type \in { ec_pre_snzh, ec_pre_hpb }. . \return Признак работоспособности. */ @@ -767,11 +784,13 @@ bool_t ecPreIsOperable( \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. \pre w > 1. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. \deep{stack} ecPreSNZ_deep(ec->f->n, ec->d, ec->deep). - \remark Функция ecPreSNZ() не оптимальна. Ускоренный алгоритм вычисления - малых нечетных кратных для кривых над GF(p) реализован в ecpPreSNZ(). + \remark Ускоренной редакцией ecPreSNZ() для кривых над GF(p) является + функция ecpPreSNZ(). */ void ecPreSNZ( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -791,6 +810,8 @@ size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep); \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. \pre w > 1. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном @@ -798,8 +819,8 @@ size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep); \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. \deep{stack} ecPreSNZA_deep(ec->f->n, ec->d, ec->deep). - \remark Функция ecPreSNZA() не оптимальна. Ускоренный алгоритм вычисления - малых нечетных кратных для кривых над GF(p) реализован в ecpPreSNZA(). + \remark Ускоренной редакцией ecPreSNZA() для кривых над GF(p) является + функция ecpPreSNZA(). */ bool_t ecPreSNZA( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -811,6 +832,35 @@ bool_t ecPreSNZA( size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep); +/*! \brief Предвычисления по схеме SNZH + + На эллиптической кривой ec выполняются предвычисления по схеме SNZH + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w > 1 && h > 0. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n)). + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + случае. + \remark При обнаружении точки O предвычисления прерываются с неопределенным + результатом в pre->pts. + \deep{stack} ecPreSNZH_deep(ec->f->n, ec->d, ec->deep). +*/ +bool_t ecPreSNZH( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + size_t h, /*!< [in] высота окна */ + const ec_o* ec, /*!< [in] описание кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep); + /* ******************************************************************************* Макрооперации с аффинными точками @@ -935,6 +985,52 @@ bool_t ecMulPreSNZA( size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +/*! \brief Кратная точка с предвычислениями по схеме SNZH + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [m]d-кратной точки a, по которой построен контейнер pre + с предвычисленными по схеме SNZH точками: + \code + b <- d a. + \endcode + \pre Описание ec работоспособно. + \pre Описание группы точек в ec работоспособно. + \pre Контейнер pre работоспособен. + \pre pre->type == ec_pre_snzh. + \pre pre->w * pre->h >= wwBitSize(ec->order, m). + \pre Длина ec->order в машинных словах равняется m. + \pre m > 1. + \pre d < ec->order. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точки контейнера pre корректно рассчитаны по a. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} ecMulPreSNZH_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция регулярна по [m]d при выполнении следующих условий: + - если ec->dbladda == 0, то функция ec->adda регулярна: сложение a + b, + a != \pm b, a != O, b != O, выполняется без условных переходов; + - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление + 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных + переходов; + - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается + без условных переходов; + - справедливо, по крайней мере, одно из условий: + a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; + b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и + удвоение выполняются по одним и тем же формулам без условных переходов. +*/ +bool_t ecMulPreSNZH( + word b[], /*!< [out] кратная точка */ + const ec_pre_t* pre, /*!< [in] предвычисленные точки */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + /*! \brief Имеет порядок? Проверяется, что аффинная точка [2 * ec->f->n]a имеет порядок [m]q diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 06caa37d..dd6d49d3 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -273,6 +273,8 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. \pre w >= 3. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->d, ec->deep, w). @@ -295,6 +297,8 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w); \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. \pre w >= 3. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном diff --git a/src/math/ec.c b/src/math/ec.c index e711c1be..a7a671ee 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -127,10 +127,6 @@ P <- P + A и P <- P + P меняются на A <- A + A. - (P <- 2P) -- время работы функции ec->dbl. ----------------------------------------------------- * [или прямых вычислений в аффинных координатах] - -\remark Функции ecPreSNZ() и особенно ecPreSNZA() не оптимальны. Ускоренные -алгоритмы предвычислений для кривых над GF(p) реализованы в функциях -ecPreSNZ() и ecpPreSNZA(). ******************************************************************************* */ @@ -138,10 +134,9 @@ bool_t ecPreIsOperable(const ec_pre_t* pre) { return memIsValid(pre, sizeof(pre)) && (pre->type == ec_pre_snz || pre->type == ec_pre_snza || - pre->type == ec_pre_hpb) && + pre->type == ec_pre_snzh || pre->type == ec_pre_hpb) && pre->w > 0 && pre->w < B_PER_W && - (pre->type == ec_pre_hpb && pre->h > 0 || - pre->type != ec_pre_hpb && pre->h == 0); + ((pre->type != ec_pre_snzh && pre->type != ec_pre_hpb) ^ (pre->h > 0)); } #define ecPreSNZ_local(n, ec_d)\ @@ -156,7 +151,7 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(w > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(ec->d * ec->f->n * SIZE_BIT_POS(w - 1)), + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -202,7 +197,7 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(w > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * SIZE_BIT_POS(w - 1)), + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -237,6 +232,59 @@ size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } +#define ecPreSNZH_local(n, ec_d)\ +/* t */ O_OF_W(ec_d * n) + +bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, + const ec_o* ec, void* stack) +{ + word* t; /* [ec->d * ec->f->n] */ + size_t i; + word* prev; + word* cur; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(w > 0 && h > 0); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n), + a, O_OF_W(2 * ec->f->n))); + // разметить стек + memSlice(stack, + ecPreSNZH_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + // первая строка + if (!ecPreSNZA(pre, a, w, ec, stack)) + return FALSE; + // остальные строки + prev = pre->pts, cur = prev + SIZE_BIT_POS(w - 1) * 2 * ec->f->n; + for (i = 0; i < h; ++i) + { + word* pt; + for (pt = cur; prev != cur; pt += 2 * ec->f->n, prev += 2 * ec->f->n) + { + size_t j = SIZE_BIT_POS(w); + ecDblA(t, prev, ec, stack); + while (--j) + ecDbl(t, t, ec, stack); + if (!ecToA(pt, t, ec, stack)) + return FALSE; + } + cur = pt; + } + // заполнить служебные поля + pre->type = ec_pre_snzh; + pre->w = w, pre->h = h; + return TRUE; +} + +size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecPreSNZH_local(n, ec_d), + ecPreSNZA_deep(n, ec_d, ec_deep), + SIZE_MAX); +} + /* ******************************************************************************* Кратная точка: метод NAF (Non-Adjacent Form) @@ -584,7 +632,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, - ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + ecMulPreSNZA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, &dd, &t, &pt, &stack); // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); @@ -635,6 +683,94 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } +#define ecMulPreSNZH_local(n, ec_d, m)\ +/* dd */ O_OF_W(m),\ +/* t */ O_OF_W(ec_d * n),\ +/* pt */ O_OF_W(2 * n) + +bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, + const word d[], size_t m, void* stack) +{ + register word neg; + register word digit; + register word borrow; + register bool_t ret; + size_t mb; + size_t row_count; + size_t pt_size; + size_t mask; + size_t pos; + const word* row; + word* dd; /* [m] */ + word* t; /* [ec->d * ec->f->n] */ + word* pt; /* [2 * ec->f->n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(ecPreIsOperable(pre)); + ASSERT(pre->type == ec_pre_snzh); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); + ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(wwCmp(d, ec->order, m) < 0); + // размерности + mb = wwBitSize(ec->order, m); + ASSERT(2 * pre->w <= mb && mb <= pre->w * pre->h); + row_count = SIZE_BIT_POS(pre->w - 1); + pt_size = 2 * ec->f->n; + mask = WORD_BIT_POS(pre->w) - 2; + // разметить стек + memSlice(stack, + ecMulPreSNZH_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &dd, &t, &pt, &stack); + // dd <- (d % 2) ? d : ec->order - d + neg = WORD_1 - (d[0] & 1); + zzSubIf(dd, ec->order, d, m, neg); + ASSERT(zzIsOdd(dd, m)); + // позиция старшей цифры, старшая строка pre + pos = (mb - 1) / pre->w; + row = pre->pts + pos * pt_size; + pos *= pre->w; + // обработать старшую цифру + digit = wwGetBits(dd, pos, mb - pos); + wwSel(pt, row, row_count, pt_size, digit >> 1); + ecFromA(t, pt, ec, stack); + borrow = WORD_1 - (digit & 1); + // обработать остальные цифры + while (pos -= pre->w) + { + row -= row_count * pt_size; + digit = wwGetBits(dd, pos, pre->w); + digit ^= (WORD_0 - borrow) & mask; + wwSel(pt, row, row_count, pt_size, digit >> 1); + ecSgnA(pt, borrow, ec, stack); + ecDblAddA(t, t, pt, ec, stack); + borrow = WORD_1 - (digit & 1); + } + // финишное сложение + row -= row_count * pt_size; + digit = wwGetBits(dd, 0, pre->w); + ASSERT(digit & 1); + digit ^= (WORD_0 - borrow) & mask; + wwSel(pt, pre->pts, row_count, pt_size, digit >> 1); + ecSgnA(pt, borrow, ec, stack); + ret = (ec->finadda) ? + ec->finadda(b, t, pt, ec, stack) : + (ecAddA(t, t, pt, ec, stack), ecToA(b, t, ec, stack)); + // настройка знака + ecSgnA(b, neg, ec, stack); + // очистка и возврат + CLEAN3(neg, digit, borrow); + return ret; +} + +size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreSNZH_local(n, ec_d, m), + ec_deep, + SIZE_MAX); +} + /* ******************************************************************************* Кратная точка: метод Comb diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 2f0f8ea0..2dea1d21 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -46,15 +46,17 @@ bool_t ecpBench() prngCOMBO_keep(), O_OF_W(2 * ec->f->n), O_OF_W(ec->f->n), - sizeof(ec_pre_t) + O_OF_W(64 * 3 * ec->f->n), - utilMax(7, + sizeof(ec_pre_t) + O_OF_W(43 * 32 * 3 * ec->f->n), + utilMax(9, ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), ecPreSNZ_deep(ec->f->n, ec->d, ec->deep), ecPreSNZA_deep(ec->f->n, ec->d, ec->deep), + ecPreSNZH_deep(ec->f->n, ec->d, ec->deep), ecpPreSNZ_deep(ec->f->n, ec->f->deep, 6), ecpPreSNZA_deep(ec->f->n, ec->f->deep, 6), ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, ec->f->n), - ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), + ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), + ecMulPreSNZH_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); if (state == 0) @@ -143,6 +145,22 @@ bool_t ecpBench() (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } + // ecMulPre[SNZH] + for (w = 3; w <= 6; ++w) + { + size_t h = (B_OF_W(ec->f->n) + w - 1) / w; + ecPreSNZH(pre, ec->base, w, h, ec, stack); + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, ec->f->no, combo_state); + ecMulPreSNZH(pt, pre, ec, d, ec->f->n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecMulPre[SNZH,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } // ecPre[SNZ] for (w = 3; w <= 6; ++w) { From 865343654d34531f3373e5e09a5cc2042a245172 Mon Sep 17 00:00:00 2001 From: agievich Date: Tue, 10 Feb 2026 01:04:17 +0300 Subject: [PATCH 35/77] Finish ecMulPreSNZH() --- src/math/ec.c | 15 +++++++-------- test/math/ecp_test.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/math/ec.c b/src/math/ec.c index a7a671ee..cb4546c3 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -257,14 +257,14 @@ bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, return FALSE; // остальные строки prev = pre->pts, cur = prev + SIZE_BIT_POS(w - 1) * 2 * ec->f->n; - for (i = 0; i < h; ++i) + for (i = 1; i < h; ++i) { word* pt; for (pt = cur; prev != cur; pt += 2 * ec->f->n, prev += 2 * ec->f->n) { - size_t j = SIZE_BIT_POS(w); + size_t j; ecDblA(t, prev, ec, stack); - while (--j) + for (j = 1; j < w; ++j) ecDbl(t, t, ec, stack); if (!ecToA(pt, t, ec, stack)) return FALSE; @@ -273,7 +273,7 @@ bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, } // заполнить служебные поля pre->type = ec_pre_snzh; - pre->w = w, pre->h = h; + pre->h = h; return TRUE; } @@ -728,7 +728,7 @@ bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(zzIsOdd(dd, m)); // позиция старшей цифры, старшая строка pre pos = (mb - 1) / pre->w; - row = pre->pts + pos * pt_size; + row = pre->pts + pos * row_count * pt_size; pos *= pre->w; // обработать старшую цифру digit = wwGetBits(dd, pos, mb - pos); @@ -738,16 +738,15 @@ bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, // обработать остальные цифры while (pos -= pre->w) { - row -= row_count * pt_size; digit = wwGetBits(dd, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; + row -= row_count * pt_size; wwSel(pt, row, row_count, pt_size, digit >> 1); ecSgnA(pt, borrow, ec, stack); - ecDblAddA(t, t, pt, ec, stack); + ecAddA(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); } // финишное сложение - row -= row_count * pt_size; digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 923cb2e1..45a9db8c 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -53,6 +53,8 @@ typedef void (*ec_pre_snz_i)(ec_pre_t* pre, const word a[], size_t w, typedef bool_t (*ec_pre_snza_i)(ec_pre_t* pre, const word a[], size_t w, const struct ec_o* ec, void* stack); +const size_t max_pre_count = (B_OF_O(no) + 5) / 6 * 32; + bool_t ecpTest() { // функции предвычислений @@ -85,7 +87,7 @@ bool_t ecpTest() ec_keep, f_keep, 3 * no, - (sizeof(ec_pre_t) + O_OF_W(32 * 3 * n)) | SIZE_HI, + (sizeof(ec_pre_t) + O_OF_W(max_pre_count * 3 * n)) | SIZE_HI, O_OF_W(3 * n), O_OF_W(3 * n), O_OF_W(3 * n), @@ -108,10 +110,12 @@ bool_t ecpTest() ecMulA_deep(n, 3, ec_deep, n), ecPreSNZ_deep(n, 3, ec_deep), ecPreSNZA_deep(n, 3, ec_deep), + ecPreSNZH_deep(n, 3, ec_deep), ecpPreSNZ_deep(n, f_deep, 6), ecpPreSNZA_deep(n, f_deep, 6), ecMulPreSNZ_deep(n, 3, ec_deep, n), ecMulPreSNZA_deep(n, 3, ec_deep, n), + ecMulPreSNZH_deep(n, 3, ec_deep, n), ecAddMulA_deep(n, 3, ec_deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, @@ -447,7 +451,11 @@ bool_t ecpTest() { wwCopy(d, ec->order, n); zzSubW2(d, n, 1); - ecpPreSNZA(pre, ec->base, w, ec, stack); + if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) + { + blobClose(state); + return FALSE; + } while (!wwIsZero(d, n)) { if (!ecMulPreSNZA(pt0, pre, ec, d, n, stack) || @@ -467,6 +475,38 @@ bool_t ecpTest() } } } + // кратная точка: ecMulA vs ecMulPreSNZH + { + size_t w; + for (w = 3; w <= 6; ++w) + { + size_t h = (B_OF_W(ec->f->n) + w - 1) / w; + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) + { + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, n)) + { + if (!ecMulPreSNZH(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, n, 13); + } + if (ecMulPreSNZH(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; + } + } + } // сумма кратных точек: ecMulA vs ecAddMulA if (n >= 4) { From 9bad13ba69a51778520c9f4be1d368d2c0ad4f8f Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 11 Feb 2026 20:29:09 +0300 Subject: [PATCH 36/77] Start math/ec/preHPB --- include/bee2/math/ec.h | 92 +++++++++++-- include/bee2/math/ww.h | 6 +- src/math/ec.c | 303 +++++++++++++++++++++++++++++++++++------ src/math/ecp/ecp_pre.c | 2 +- test/math/ecp_test.c | 106 +++++++++++--- 5 files changed, 441 insertions(+), 68 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 16979a71..7407a54d 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.04 +\version 2026.02.11 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -513,14 +513,11 @@ typedef enum \endcode - HPB: вычисляются аффинные точки \code - [b_{w-1} ... b_1 b_0]a = \sum_{i=0}^{w-1} b_i 2^{h i} a, + [c_{w-1}... c_1 c_0]a = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a, \endcode - где b_i in {-1, 1}, b_{w-1} == 1. Точка [b_{w-1} ... b_1 b_0]a - размещаются в массиве pts по индексу, двоичная запись которого есть - \code - с_{w-1}...c_1 c_0, - \endcode - где c_i = 0, если b_i = 1, и c_i = 1, если b_i = -1. + где c_i in {0, 1}, c_{w-1} == 0. Точка [c_{w-1}... c_1 c_0]a + размещается в pts по индексу, двоичная запись которого -- + c_{w-1}... c_1 c_0. . \pre 0 < w && w < B_PER_W. \pre h > 0 <=> type \in { ec_pre_snzh, ec_pre_hpb }. @@ -861,6 +858,35 @@ bool_t ecPreSNZH( size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep); +/*! \brief Предвычисления по схеме HPB + + На эллиптической кривой ec выполняются предвычисления по схеме HPB + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w > 1 && h > 0. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + случае. + \remark При обнаружении точки O предвычисления прерываются с неопределенным + результатом в pre->pts. + \deep{stack} ecPreHPB_deep(ec->f->n, ec->d, ec->deep, h). +*/ +bool_t ecPreHPB( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + size_t h, /*!< [in] высота окна */ + const ec_o* ec, /*!< [in] описание кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h); + /* ******************************************************************************* Макрооперации с аффинными точками @@ -997,10 +1023,10 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_snzh. - \pre pre->w * pre->h >= wwBitSize(ec->order, m). \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. + \pre pre->w * pre->h >= wwBitSize(ec->order, m). \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. @@ -1031,6 +1057,54 @@ bool_t ecMulPreSNZH( size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +/*! \brief Кратная точка с предвычислениями по схеме SNZA + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [m]d-кратной точки a, по которой построен контейнер pre + с предвычисленными по схеме HPB точками: + \code + b <- d a. + \endcode + \pre Описание ec работоспособно. + \pre Описание группы точек в ec работоспособно. + \pre Контейнер pre работоспособен. + \pre pre->type == ec_pre_hpb. + \pre Длина ec->order в машинных словах равняется m. + \pre m > 1. + \pre d < ec->order. + \pre wwBitSize(ec->order, m) <= pre->w * pre->h && + pre->w * pre->h < wwBitSize(ec->order, m) + B_PER_W. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точки контейнера pre корректно рассчитаны по a. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} ecMulPreHPB_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция регулярна по [m]d при выполнении следующих условий: + - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без + условных переходов; + - функция ec->adda регулярна: сложение a + b, a != \pm b, a != O, b != O, + выполняется без условных переходов; + - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление + 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных + переходов; + - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается + без условных переходов; + - ec->finadda != 0 и функция ec->finadda регулярна: сложение и удвоение + выполняются по одним и тем же формулам без условных переходов; + - pre->w * pre->h == wwBitSize(ec->order, m). +*/ +bool_t ecMulPreHPB( + word b[], /*!< [out] кратная точка */ + const ec_pre_t* pre, /*!< [in] предвычисленные точки */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecMulPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + /*! \brief Имеет порядок? Проверяется, что аффинная точка [2 * ec->f->n]a имеет порядок [m]q diff --git a/include/bee2/math/ww.h b/include/bee2/math/ww.h index af10a80a..0c6ad2ac 100644 --- a/include/bee2/math/ww.h +++ b/include/bee2/math/ww.h @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.01.27 +\version 2026.02.11 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -95,6 +95,10 @@ extern "C" { #define wwIsDisjoint3(a, n, b, m, c, k)\ memIsDisjoint3(a, O_OF_W(n), b, O_OF_W(m), c, O_OF_W(k)) +/*! Биты словв [n]a инвертируются */ +#define wwNeg(a, n)\ + memNeg(a, O_OF_W(n)) + /* ******************************************************************************* Копирование, логические операции diff --git a/src/math/ec.c b/src/math/ec.c index cb4546c3..197a56de 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.09 +\version 2026.02.11 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -101,19 +101,19 @@ bool_t ecGroupIsOperable(const ec_o* ec) ******************************************************************************* Предвычисления -В функции ecPreSNZ() реализованы предвычисления по схеме SNZ. Используется -следуюший алгоритм: -- t <- 2a; // P <- 2A -- pt[0] <- a; -- pt[1] <- t + pt[0]; // P <- P + A -- pt[i] <- t + pt[i - 1], i = 2, 3, ..., 2^{w-1} - 1. // P <- P + P +В функции ecPreSNZ() реализован следуюший алгоритм: +1. t <- 2a. // P <- 2A +2. pre[0] <- a. +3. pre[1] <- t + pre[0]. // P <- P + A +4. Для i = 2, 3, ..., 2^{w-1} - 1: + 1) pre[i] <- t + pre[i - 1]. // P <- P + P Итоговая сложность: 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). -В функции ecPreSNZA() реализованы предвычисления по схеме SNZA. Используется -тот же алгоритм, только операция P <- 2A в нем меняется на A <- 2A, а операции -P <- P + A и P <- P + P меняются на A <- A + A. +В функции ecPreSNZA() реализован тот тот же алгоритм, только операция P <- 2A +в нем меняется на A <- 2A, а операции P <- P + A и P <- P + P меняются +на A <- A + A. Итоговая сложность: 1(P <- 2A) + (2^{w-1} - 1)(A <- A + A). @@ -127,6 +127,29 @@ P <- P + A и P <- P + P меняются на A <- A + A. - (P <- 2P) -- время работы функции ec->dbl. ----------------------------------------------------- * [или прямых вычислений в аффинных координатах] + +В функции ecPreHPB() вычисляются точки + [b_{w-1} ... b_1 b_0]a = \sum_{i=0}^{w-1} b_i 2^{h i} a, +где b_i in {-1, 1}, b_{w-1} == 1. Точка [b_{w-1} ... b_1 b_0]a +размещается в массиве предвычисленных точек по индексу, двоичная запись +которого есть + с_{w-1}...c_1 c_0, +где c_i = 0, если b_i = 1, и c_i = 1, если b_i = -1. + +Слова code = с_{w-1}...c_1 c_0 просматриваются как слова кода Грея: +соседние слова отличаются только в одном бите. Если это бит номер i, +то новая точка получается из [b_{w-1} ... b_1 b_0]a: +- добавлением точки (2(2^h)^i)a при c_i = 1; +- вычитанием точки (2(2^h)^i)a при c_i = 0. + +Определение следующего за code слова выполняется следующим образом +(см. [War03; глава 13], нумерация битов справа налево от 0): +1. Если code содержит четное число единиц, то pos <- 0. Иначе + pos <- (позиция первой справа единицы) + 1. +2. Инвертировать бит code номер pos. + +[War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, + М.: Издательский дом <<Вильямс>>, 2003. ******************************************************************************* */ @@ -149,7 +172,7 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, size_t i; // pre ASSERT(ecIsOperable(ec)); - ASSERT(w > 0); + ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), a, O_OF_W(2 * ec->f->n))); @@ -157,16 +180,16 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, memSlice(stack, ecPreSNZ_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); - // pt[0] <- a + // pre[0] <- a ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); // вычислить малые кратные if (w > 1) { // t <- 2 a ecDblA(t, a, ec, stack); - // pt[1] <- t + a + // pre[1] <- t + a ecAddA(ecPrePt(pre, 1, ec), t, a, ec, stack); - // pt[i] <- t + pt[i - 1] + // pre[i] <- t + pre[i - 1] for (i = 2; i < SIZE_BIT_POS(w - 1); ++i) ecAdd(ecPrePt(pre, i, ec), t, ecPrePt(pre, i - 1, ec), ec, stack); } @@ -195,7 +218,7 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, size_t i; // pre ASSERT(ecIsOperable(ec)); - ASSERT(w > 0); + ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); @@ -203,14 +226,14 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, memSlice(stack, ecPreSNZA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t1, &t2, &stack); - // pt[0] <- a + // pre[0] <- a wwCopy(ecPrePtA(pre, 0, ec), a, 2 * ec->f->n); // вычислить малые кратные if (w > 1) { // t1 <- 2 a ecDblA(t1, a, ec, stack); - // pt[i] <- t1 + pt[i - 1] + // pre[i] <- t1 + pre[i - 1] for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { ecAddA(t2, t1, ecPrePtA(pre, i - 1, ec), ec, stack); @@ -244,7 +267,7 @@ bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, word* cur; // pre ASSERT(ecIsOperable(ec)); - ASSERT(w > 0 && h > 0); + ASSERT(0 < w && w < B_PER_W && h > 0); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); @@ -285,6 +308,73 @@ size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } +#define ecPreHPB_local(n, ec_d, h)\ +/* t1 */ O_OF_W(ec_d * n),\ +/* t2 */ O_OF_W(ec_d * n),\ +/* dbls */ O_OF_W(h * ec_d * n) + +bool_t ecPreHPB(ec_pre_t* pre, const word a[], size_t w, size_t h, + const ec_o* ec, void* stack) +{ + word* t1; /* [ec->d * ec->f->n] */ + word* t2; /* [ec->d * ec->f->n] */ + word* dbls; /* [h * ec->d * ec->f->n] */ + size_t i; + word code; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(0 < w && w < B_PER_W && h > 0); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), + a, O_OF_W(2 * ec->f->n))); + // разметить стек + memSlice(stack, + ecPreHPB_local(ec->f->n, ec->d, h), SIZE_0, SIZE_MAX, + &t1, &t2, &dbls, &stack); + // dbls[i] <- 2(2^h)^i a, t2 <- \sum_{i=0}^{w-1} (2^h)^i a + ecFromA(t1, a, ec, stack); + wwCopy(t2, t1, ec->d * ec->f->n); + for (i = 0; i < w; ++i) + { + ecDbl(t1, t1, ec, stack); + wwCopy(ecPt(dbls, i, ec), t1, ec->d * ec->f->n); + if (i != w - 1) + { + size_t j; + for (j = 1; j < h; ++j) + ecDbl(t1, t1, ec, stack); + ecAdd(t2, t2, t1, ec, stack); + } + } + // pre[0] <- \sum_{i=0}^{w-1} dbls[i] + if (!ecToA(ecPrePtA(pre, 0, ec), t2, ec, stack)) + return FALSE; + // pre[next] <- pre[cur] \pm dbls[diff(next, cur)] + for (i = 1, code = 0; i < WORD_BIT_POS(w - 1); ++i) + { + size_t pos; + pos = wordParity(code) ? wordCTZ(code) + 1 : 0; + wwCopy(t1, ecPt(dbls, pos, ec), ec->d * ec->f->n); + ecSgn(t1, (~code >> pos) & WORD_1, ec, stack); + code ^= WORD_BIT_POS(pos); + ecAdd(t2, t2, t1, ec, stack); + if (!ecToA(ecPrePtA(pre, (size_t)code, ec), t2, ec, stack)) + return FALSE; + } + // заполнить служебные поля + pre->type = ec_pre_hpb; + pre->w = w, pre->h = h; + return TRUE; +} + +size_t ecPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h) +{ + return memSliceSize( + ecPreHPB_local(n, ec_d, h), + ec_deep, + SIZE_MAX); +} + /* ******************************************************************************* Кратная точка: метод NAF (Non-Adjacent Form) @@ -366,7 +456,7 @@ static bool_t ecMulPreNAF(word b[], const ec_pre_t* pre, const ec_o* ec, // старшая цифра NAF digit = wwGetBits(naf, 0, naf_width); ASSERT((digit & 1) == 1 && (digit >> (naf_width - 1)) == 0); - // t <- pt[digit / 2] + // t <- pre[digit / 2] wwCopy(t, ecPrePt(pre, digit >> 1, ec), ec->d * ec->f->n); // цикл по цифрам NAF i = naf_width; @@ -378,7 +468,7 @@ static bool_t ecMulPreNAF(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(naf, i, naf_width); if (digit & 1) { - // t <- t + pt[digit / 2] + // t <- t + pre[digit / 2] ecAdd(t, t, ecPrePt(pre, digit >> 1, ec), ec, stack); // к следующей цифре i += naf_width - 1; @@ -446,34 +536,33 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) /* ******************************************************************************* -Кратная точка: метод SNZ +Кратная точка: метод SNZ и его модификации В функции ecMulPreSNZ() для определения кратной точки b = da при нечетной кратности d используется ее представление в форме - d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}. -Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w - 1)} -- ненулевые (нечетные) цифры + d_{k-1} (2^w)^{k-1} + ... + d_1 (2^w) + d_0. +Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w-1)} -- ненулевые (нечетные) цифры со знаком (signed non-zero). Предполагается, что точка a лежит в группе нечетного порядка order, (2^w)^{k-1} < order < (2^w)^k. Представление SNZ предложено в [OkeTak03]. -Если кратность d четная, то она предварительно меняется на order - d. -Для этого используется функция zzSubIf(). +Если кратность d четная, то она предварительно меняется на order-d. Для этого +используется функция zzSubIf(). -Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], -а также [APS22]): -1. Для i = 0, 1, ..., (2^{w-1} - 1): - 1) pt[i] <- (2i + 1)a. // предвычисления: SNZ -2. t <- pt[d_{k-1} / 2]. -3. Для i = k - 2, ..., 1: +Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], [APS22]): +1. Для i = 0, 1, ..., 2^{w-1}-1: + 1) pre[i] <- (2i + 1)a. // предвычисления: SNZ +2. t <- pre[d_{k-1} / 2]. +3. Для i = k-2, ..., 2, 1: 1) t <- 2^w t; // P <- 2P - 1) t <- t + sgn(d_i) pt[|d_i| / 2]. // P <- P + P + 1) t <- t + sgn(d_i) pre[|d_i|/2]. // P <- P + P 4. t <- 2^w t. -5. t <- t + sgn(d_0) pt[|d_0| / 2]. // A <- P + P +5. t <- t + sgn(d_0) pre[|d_0|/2]. // A <- P + P 6. Возвратить t. -\remark Сложение t + sgn(d_0) pt[|d_0| / 2] вынесено за рамки основного цикла +\remark Сложение t + sgn(d_0) pre[|d_0|/2] вынесено за рамки основного цикла на шаге 3, потому что при этом (и только при этом) сложении может произойти -исключительная ситуация -- совпадение операндов t и sgn(d_0) pt[|d_0| / 2] +исключительная ситуация -- совпадение операндов t и sgn(d_0) pre[|d_0|/2] с переключением от сложения к удвоению. Завершающее сложение выполняется с помощью функции интерфейса ec_finadd_i, если таковая указана в описании кривой. Предполагается, что функция ec_finadd_i регулярна, и тогда сложение @@ -481,10 +570,10 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) \remark Расчет цифр d_i (см. [APS22]): 1. Записать - d = d_0 + d_1 (2^w) + ... + d_{k-1} (2^w)^{k-1}, - где d_i \in {0, 1, ..., 2^w - 1}. -2. (d_{k-1}, borrow) <- (d_{k-1} + (d_{k-1} % 2), d_{k-1} % 2). -3. Для i = k - 2, ...., 0: + d = d_{k-1} (2^w)^{k-1} + d_1 (2^w) + d_0, + где d_i \in {0, 1, ..., 2^w-1}. +2. (d_{h-1}, borrow) <- (d_{h-1} + (d_{h-1} % 2), d_{k-1} % 2). +3. Для i = k-2, ...., 1, 0: 1) (d_i, borrow) <- (d_i - borrow * 2^w + (d_i % 2), d_i % 2). \remark Выражение @@ -499,6 +588,17 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) а не проективными. Это позволяет заменить регулярные сложения P <- P + P на P <- P + A и финишное сложение A <- P + P на A <- P + A. +В функции ecMulPreSNZH() предполагается, что аффинные точки предварительно +рассчитаны для каждой цифры обрабатываемого скаляра d. Это позволяет +избавиться от удвоений: +1. Для i = 0, 1, ..., k-1 и j = 0, 1, ..., 2^{w-1}-1: + 1) pre[i][j] <- (2^w)^i(2j + 1)a. // предвычисления: SNZH +2. t <- pre[k-1][d_{k-1}/2]. +3. Для i = k-2, ..., 2, 1: + 1) t <- t + sgn(d_i) pre[i]t[|d_i|/2]. // P <- P + P +4. t <- t + sgn(d_0) pre[0][|d_0|/2]. // A <- P + P +5. Возвратить t. + [BCL+14] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves for Cryptography: An Efficiency and Security Analysis, 2014, https://eprint.iacr.org/2014/130.pdf. @@ -772,8 +872,35 @@ size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) /* ******************************************************************************* -Кратная точка: метод Comb +Кратная точка: метод HPB + +В функции ecMulPreHPB() вычисление кратной точки выполняется по методу HPB, +предложенному в [HPB05]. Метод HPB является модификацией метода Comb, +предложенного в [LimLee94] (см. также [HMV04; algorithm 3.44]). + +Для определения кратной точки b = da при нечетной кратности d используется +ее представление в форме + d_{hw-1} 2^{hw-1} + d_{hw-2} 2^{hw-2} + ... + d_1 2^1 + d_0, +где d_i \in {\pm 1}. + +Как обычно, если кратность d четная, то она предварительно меняется на +order-d. + +Реализован следующий алгоритм: +1. Для с_{w-1} = 0 и с_{w-2}...c_0 \in {0,1}^{w-1}: + 1) pre[с_{w-1}с_{w-2}...c_0] = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a. +2. t <- pre[d_{hw-1}d_{(h(w-1)-1}... d_{h-1}]. +3. Для i = 2, 3, ..., h-1: + 1) t <- 2 t; + 1) t <- t + sgn(d_{hw-i})pt[0 d_{h(w-1)-i}...d_{h-i}]. +4. t <- 2 t. +5. t <- t + sgn(hw-h) pt[0 d_{h(w-1)-h)}...d_0]. +6. Возвратить t. +[HPB05] Hedabou M., Pinel P., Beneteau L. Countermeasures for preventing + comb method against SCA attacks. In: International Conference + on Information Security Practice and Experience, 2005, pp. 85-96, + Springer, Berlin Heidelberg. [LimLee94] Lim, C.H., Lee, P.J. More Flexible Exponentiation with Precomputation. In: Advances in Cryptology -- CRYPTO ’94. 1994. Lecture Notes in Computer Science, vol 839. Springer, Berlin, @@ -781,6 +908,102 @@ size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ******************************************************************************* */ +#define ecMulPreHPB_local(n, ec_d, m)\ +/* dd */ O_OF_W(m + 1),\ +/* t */ O_OF_W(ec_d * n),\ +/* pt */ O_OF_W(2 * n) + +bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, + const word d[], size_t m, void* stack) +{ + register word neg; + register word digit; + register bool_t ret; + size_t mb; + size_t pre_count; + size_t pt_size; + size_t mask; + size_t pos; + size_t i; + word* dd; /* [W_OF_B(pre->h * pre->w)] */ + word* t; /* [ec->d * ec->f->n] */ + word* pt; /* [2 * ec->f->n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(ecPreIsOperable(pre)); + ASSERT(pre->type == ec_pre_hpb); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); + ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(wwCmp(d, ec->order, m) < 0); + // размерности + mb = pre->w * pre->h; + ASSERT(wwBitSize(ec->order, m) <= mb); + ASSERT(mb < wwBitSize(ec->order, m) + B_PER_W); + pre_count = SIZE_BIT_POS(pre->w - 1); + pt_size = 2 * ec->f->n; + mask = WORD_BIT_POS(pre->w - 1) - 1; + // разметить стек + memSlice(stack, + ecMulPreHPB_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &dd, &t, &pt, &stack); + // dd <- (d % 2) ? d : ec->order - d + neg = WORD_1 - (d[0] & 1); + zzSubIf(dd, ec->order, d, m, neg); + ASSERT(zzIsOdd(dd, m)); + // перекодировать dd + m = W_OF_B(mb); + wwNeg(dd, m); + if (i = mb % B_PER_W) + dd[m - 1] <<= (B_PER_W - i), dd[m - 1] >>= (B_PER_W - i); + wwShLo(dd, m, 1); + // сформировать старшую цифру + digit = 0; + for (i = 1; i < pre->w; ++i) + digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - 1); + ASSERT((digit & mask) == digit); + // обработать старшую цифру + wwSel(pt, pre->pts, pre_count, pt_size, digit); + ecFromA(t, pt, ec, stack); + // обработать остальные цифры + for (pos = 1; pos < pre->h; ++pos) + { + // сформировать цифру + digit = (word)wwTestBit(dd, mb - pos); + for (i = 1; i < pre->w; ++i) + digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - pos); + // удвоить и сложить + wwSel(pt, pre->pts, pre_count, pt_size, digit & mask); + ecSgnA(pt, digit >> (pre->w - 1), ec, stack); + ecDblAddA(t, t, pt, ec, stack); + } + // удвоить + ecDbl(t, t, ec, stack); + // сформировать последнюю цифру + digit = (word)wwTestBit(dd, mb - pos); + for (i = 1; i < pre->w; ++i) + digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - pos); + // финишное сложение + wwSel(pt, pre->pts, pre_count, pt_size, digit & mask); + ecSgnA(pt, digit >> (pre->w - 1), ec, stack); + ret = (ec->finadda) ? + ec->finadda(b, t, pt, ec, stack) : + (ecAddA(t, t, pt, ec, stack), ecToA(b, t, ec, stack)); + // настройка знака + ecSgnA(b, neg, ec, stack); + // очистка и возврат + CLEAN2(neg, digit); + return ret; +} + +size_t ecMulPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreHPB_local(n, ec_d, m), + ec_deep, + SIZE_MAX); +} + /* ******************************************************************************* Имеет порядок? diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index f56f3ce0..e5994e44 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -46,7 +46,7 @@ version 3. See Copyright Notices in bee2/info.h. - этап 1 -- шаги 1 -- 16 (расчет начальных выражений); - этап 2 -- шаги 17, 18, 20 (расчет основных выражений, формирование первой половины точек); -- этап 3 -- шаги 19, 21 (формирование оставшихся точек). +- этап 3 -- шаги 19, 21, 22 (формирование оставшихся точек). Для повышения читабельности используются макросы, которые покрывают следующие выражения: diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 45a9db8c..318a515f 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.09 +\version 2026.02.11 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -53,8 +53,6 @@ typedef void (*ec_pre_snz_i)(ec_pre_t* pre, const word a[], size_t w, typedef bool_t (*ec_pre_snza_i)(ec_pre_t* pre, const word a[], size_t w, const struct ec_o* ec, void* stack); -const size_t max_pre_count = (B_OF_O(no) + 5) / 6 * 32; - bool_t ecpTest() { // функции предвычислений @@ -66,12 +64,17 @@ bool_t ecpTest() const size_t f_deep = gfpCreate_deep(no); const size_t ec_keep = ecpCreateJ_keep(n); const size_t ec_deep = ecpCreateJ_deep(n, f_deep); + const size_t min_w = 3; + const size_t max_w = 6; + const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; + const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; + const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); // состояние void* state; ec_o* ec; /* [ec_keep] */ qr_o* f; /* [f_keep] */ octet* t; /* [3 * no] */ - ec_pre_t* pre; /* [32 якобиевы точки] */ + ec_pre_t* pre; /* [max_pre_count якобиевых точек] */ word* pt0; /* [3 * n] */ word* pt1; /* [3 * n] */ word* pt2; /* [3 * n] */ @@ -95,7 +98,7 @@ bool_t ecpTest() O_OF_W(3 * n), O_OF_W(3 * n), O_OF_W(n + 1), - utilMax(19, + utilMax(23, gfpCreate_deep(no), ecpCreateJ_deep(n, f_deep), ecGroupCreate_deep(f_deep), @@ -111,11 +114,13 @@ bool_t ecpTest() ecPreSNZ_deep(n, 3, ec_deep), ecPreSNZA_deep(n, 3, ec_deep), ecPreSNZH_deep(n, 3, ec_deep), - ecpPreSNZ_deep(n, f_deep, 6), - ecpPreSNZA_deep(n, f_deep, 6), + ecPreHPB_deep(n, 3, max_h, ec_deep), + ecpPreSNZ_deep(n, f_deep, max_w), + ecpPreSNZA_deep(n, f_deep, max_w), ecMulPreSNZ_deep(n, 3, ec_deep, n), ecMulPreSNZA_deep(n, 3, ec_deep, n), ecMulPreSNZH_deep(n, 3, ec_deep, n), + ecMulPreHPB_deep(n, 3, ec_deep, n), ecAddMulA_deep(n, 3, ec_deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, @@ -357,9 +362,10 @@ bool_t ecpTest() for (pos = 0; pos < COUNT_OF(pre_snz_fn); ++pos) { size_t i; - // pt[0..32) <- (1, 3, ..., 31)base + // pre[0..32) <- (1, 3, ..., 31)base + ASSERT(6 <= max_w); pre_snz_fn[pos](pre, ec->base, 6, ec, stack); - // pt0 <- \sum_{i = 0}^31 2^{31 - i} pt[i] + // pt0 <- \sum_{i = 0}^31 2^{31 - i} pre[i] wwCopy(pt0, ecPrePt(pre, 0, ec), 3 * n); for (i = 1; i < 32; ++i) { @@ -385,20 +391,21 @@ bool_t ecpTest() for (pos = 0; pos < COUNT_OF(pre_snza_fn); ++pos) { size_t i; - // pt[0..32) <- (1, 3, ..., 31)base + // pre[0..32) <- (1, 3, ..., 31)base + ASSERT(6 <= max_w); if (!pre_snza_fn[pos](pre, ec->base, 6, ec, stack)) { blobClose(state); return FALSE; } - // pt0 <- pt[1] - pt[0] + // pt0 <- pre[1] - pre[0] ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); if (!ecpAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) { blobClose(state); return FALSE; } - // pt[i+1] - pt[i] == pt0? + // pre[i+1] - pre[i] == pt0? for (i = 1; i + 1 < 32; ++i) { ecNegA(pt1, ecPrePtA(pre, i, ec), ec, stack); @@ -417,10 +424,43 @@ bool_t ecpTest() return FALSE; } } + // предвычисления по схеме HPB + { + const size_t w = max_w; + const size_t h = min_h; + size_t i; + // pre[0..2^{w-1}) <- (1, 3, ..., 2^{w-1}-1)base + ecPreHPB(pre, ec->base, w, h, ec, stack); + // pre[0] == \sum_{i=0}^{w-1} (2^h)^i base? + wwSetZero(d, n + 1); + for (i = 0; i < w; ++i) + wwSetBit(d, h * i, TRUE); + if (!ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(ecPrePt(pre, 0, ec), pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- \sum_{i = 0}^{2^{w-1}-1} pre[i] + ecFromA(pt0, ecPrePtA(pre, 0, ec), ec, stack); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + ecAddA(pt0, pt0, ecPrePtA(pre, i, ec), ec, stack); + // pt0 == (2^{h(w-1)} * 2^{w-1}) base? + ASSERT((h + 1) * (w - 1) < B_OF_W(n + 1)); + wwSetZero(d, n + 1); + wwSetBit(d, (h + 1) * (w - 1), TRUE); + if (!ecToA(pt0, pt0, ec, stack) || + !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } // кратная точка: ecMulA vs ecMulPreSNZ { size_t w; - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { wwCopy(d, ec->order, n); zzSubW2(d, n, 1); @@ -428,7 +468,7 @@ bool_t ecpTest() while (!wwIsZero(d, n)) { if (!ecMulPreSNZ(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || !wwEq(pt0, pt1, 2 * n)) { blobClose(state); @@ -447,7 +487,7 @@ bool_t ecpTest() // кратная точка: ecMulA vs ecMulPreSNZA { size_t w; - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { wwCopy(d, ec->order, n); zzSubW2(d, n, 1); @@ -478,9 +518,9 @@ bool_t ecpTest() // кратная точка: ecMulA vs ecMulPreSNZH { size_t w; - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { - size_t h = (B_OF_W(ec->f->n) + w - 1) / w; + const size_t h = (B_OF_W(ec->f->n) + w - 1) / w; wwCopy(d, ec->order, n); zzSubW2(d, n, 1); if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) @@ -507,6 +547,38 @@ bool_t ecpTest() } } } + // кратная точка: ecMulA vs ecMulPreHPB + { + size_t w; + for (w = min_w; w <= max_w; ++w) + { + const size_t h = (B_OF_W(ec->f->n) + w - 1) / w; + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + if (!ecPreHPB(pre, ec->base, w, h, ec, stack)) + { + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, n)) + { + if (!ecMulPreHPB(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, n, 15); + } + if (ecMulPreHPB(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; + } + } + } // сумма кратных точек: ecMulA vs ecAddMulA if (n >= 4) { From da7f3e90ab17167f49c29cb254c1ff7d3a8f0ea3 Mon Sep 17 00:00:00 2001 From: agievich Date: Thu, 12 Feb 2026 01:01:00 +0300 Subject: [PATCH 37/77] Finish ecMulPreHPB() --- src/math/ec.c | 32 ++++---- test/core/json_test.c | 12 +-- test/math/ecp_bench.c | 136 +++++++++++++++++++++------------- test/math/ecp_test.c | 165 ++++++++++++++++++++---------------------- 4 files changed, 188 insertions(+), 157 deletions(-) diff --git a/src/math/ec.c b/src/math/ec.c index 197a56de..cdc28817 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -892,9 +892,11 @@ order-d. 2. t <- pre[d_{hw-1}d_{(h(w-1)-1}... d_{h-1}]. 3. Для i = 2, 3, ..., h-1: 1) t <- 2 t; - 1) t <- t + sgn(d_{hw-i})pt[0 d_{h(w-1)-i}...d_{h-i}]. + 2) t <- t + sgn(d_{hw-i}) + pt[d_{hw-i}d_{h(w-1)-i}...d_{h-i} ^ d_{hw-i}...d_{hw-i}]. 4. t <- 2 t. -5. t <- t + sgn(hw-h) pt[0 d_{h(w-1)-h)}...d_0]. +5. t <- t + sgn(d_{hw-h}) + pt[d_{hw-h}d_{h(w-1)-h)}...d_0 ^ d_{hw-h}...d_{hw-h}]. 6. Возвратить t. [HPB05] Hedabou M., Pinel P., Beneteau L. Countermeasures for preventing @@ -918,6 +920,7 @@ bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, { register word neg; register word digit; + register word hi; register bool_t ret; size_t mb; size_t pre_count; @@ -949,6 +952,7 @@ bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, &dd, &t, &pt, &stack); // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); + dd[m] = 0; zzSubIf(dd, ec->order, d, m, neg); ASSERT(zzIsOdd(dd, m)); // перекодировать dd @@ -966,33 +970,35 @@ bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, wwSel(pt, pre->pts, pre_count, pt_size, digit); ecFromA(t, pt, ec, stack); // обработать остальные цифры - for (pos = 1; pos < pre->h; ++pos) + for (pos = 2; pos < pre->h; ++pos) { // сформировать цифру - digit = (word)wwTestBit(dd, mb - pos); - for (i = 1; i < pre->w; ++i) + hi = (word)wwTestBit(dd, mb - pos); + for (digit = 0, i = 1; i < pre->w; ++i) digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - pos); + digit ^= (WORD_0 - hi) & mask; // удвоить и сложить - wwSel(pt, pre->pts, pre_count, pt_size, digit & mask); - ecSgnA(pt, digit >> (pre->w - 1), ec, stack); + wwSel(pt, pre->pts, pre_count, pt_size, digit); + ecSgnA(pt, hi, ec, stack); ecDblAddA(t, t, pt, ec, stack); } // удвоить ecDbl(t, t, ec, stack); // сформировать последнюю цифру - digit = (word)wwTestBit(dd, mb - pos); - for (i = 1; i < pre->w; ++i) + hi = (word)wwTestBit(dd, mb - pos); + for (digit = 0, i = 1; i < pre->w; ++i) digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - pos); + digit ^= (WORD_0 - hi) & mask; // финишное сложение - wwSel(pt, pre->pts, pre_count, pt_size, digit & mask); - ecSgnA(pt, digit >> (pre->w - 1), ec, stack); + wwSel(pt, pre->pts, pre_count, pt_size, digit); + ecSgnA(pt, hi, ec, stack); ret = (ec->finadda) ? ec->finadda(b, t, pt, ec, stack) : (ecAddA(t, t, pt, ec, stack), ecToA(b, t, ec, stack)); // настройка знака ecSgnA(b, neg, ec, stack); // очистка и возврат - CLEAN2(neg, digit); + CLEAN3(neg, digit, hi); return ret; } @@ -1039,7 +1045,7 @@ size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован алгоритм 3.51 (interleaving with NAF) из [HMV04]. -Для каждого d[i] строится naf[i] длиной l[i] с шириной окна w[i]. +Для каждого d[i] строится naf[i] длиной l[i] и окном шириной w[i]. Сложность алгоритма: max l[i](P <- 2P) + \sum {i=1}^k diff --git a/test/core/json_test.c b/test/core/json_test.c index 569d3648..e5482750 100644 --- a/test/core/json_test.c +++ b/test/core/json_test.c @@ -4,7 +4,7 @@ \brief Tests for JSON \project bee2/test \created 2025.05.08 -\version 2025.05.10 +\version 2026.02.11 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -21,7 +21,7 @@ ******************************************************************************* */ -static err_t jsonTestStr() +static bool_t jsonTestStr() { const char* jsons[] = { @@ -61,7 +61,7 @@ static err_t jsonTestStr() ******************************************************************************* */ -static err_t jsonTestSize() +static bool_t jsonTestSize() { const char* jsons[] = { @@ -123,7 +123,7 @@ static err_t jsonTestSize() ******************************************************************************* */ -static err_t jsonTestObj() +static bool_t jsonTestObj() { const char* names[] = { "a", "b", "c" }; const char* jsons[] = @@ -175,7 +175,7 @@ static err_t jsonTestObj() ******************************************************************************* */ -static err_t jsonTestArr() +static bool_t jsonTestArr() { const char* jsons[] = { @@ -216,7 +216,7 @@ static err_t jsonTestArr() #include -static err_t jsonTestEnc() +static bool_t jsonTestEnc() { const char* fmts[] = { diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 2dea1d21..9ff838cb 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.09 +\version 2026.02.11 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -21,55 +22,53 @@ /* ******************************************************************************* -Потребности в стеке +Оценка производительности на заданной кривой ******************************************************************************* */ -bool_t ecpBench() +bool_t ecpBench_internal(const ec_o* ec) { - bign_params params[1]; - ec_o* ec; + const size_t n = ec->f->n; + const size_t no = ec->f->no; + const size_t min_w = 3; + const size_t max_w = 6; + const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; + const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; + const size_t max_pre_count = SIZE_BIT_POS(max_w - 1) * min_h; + const size_t reps = 200; void* state; octet* combo_state; /* [prngCOMBO_keep()] */ word* pt; /* [2 * n] */ word* d; /* [n] */ - ec_pre_t* pre; /* [64 якобиевы точки] */ + ec_pre_t* pre; /* [SIZE_BIT_POS(max_w - 1) якобиевы точки] */ void* stack; - // загрузить параметры - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || - bignEcCreate(&ec, params) != ERR_OK) - return FALSE; - // заблокировать утроение точек - ec->tpl = 0; // создать состояние state = blobCreate2( prngCOMBO_keep(), - O_OF_W(2 * ec->f->n), - O_OF_W(ec->f->n), - sizeof(ec_pre_t) + O_OF_W(43 * 32 * 3 * ec->f->n), - utilMax(9, - ecMulA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), - ecPreSNZ_deep(ec->f->n, ec->d, ec->deep), - ecPreSNZA_deep(ec->f->n, ec->d, ec->deep), - ecPreSNZH_deep(ec->f->n, ec->d, ec->deep), - ecpPreSNZ_deep(ec->f->n, ec->f->deep, 6), - ecpPreSNZA_deep(ec->f->n, ec->f->deep, 6), - ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, ec->f->n), - ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, ec->f->n), - ecMulPreSNZH_deep(ec->f->n, ec->d, ec->deep, ec->f->n)), + O_OF_W(2 * n), + O_OF_W(n), + sizeof(ec_pre_t) + O_OF_W(max_pre_count * 3 * n), + utilMax(11, + ecMulA_deep(n, ec->d, ec->deep, n), + ecPreSNZ_deep(n, ec->d, ec->deep), + ecPreSNZA_deep(n, ec->d, ec->deep), + ecPreSNZH_deep(n, ec->d, ec->deep), + ecPreHPB_deep(n, ec->d, ec->deep, max_h), + ecpPreSNZ_deep(n, ec->f->deep, max_w), + ecpPreSNZA_deep(n, ec->f->deep, max_w), + ecMulPreSNZ_deep(n, ec->d, ec->deep, n), + ecMulPreSNZA_deep(n, ec->d, ec->deep, n), + ecMulPreSNZH_deep(n, ec->d, ec->deep, n), + ecMulPreHPB_deep(n, ec->d, ec->deep, n)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); if (state == 0) - { - bignEcClose(ec); return FALSE; - } // создать генератор COMBO prngCOMBOStart(combo_state, utilNonce32()); // оценить число кратных точек в секунду printf("ecpBench::\n"); { - const size_t reps = 200; size_t w; size_t i; tm_ticks_t ticks; @@ -77,8 +76,8 @@ bool_t ecpBench() { for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulA(pt, ec->base, ec, d, ec->f->n, stack); + prngCOMBOStepR(d, no, combo_state); + ecMulA(pt, ec->base, ec, d, n, stack); } ticks = tmTicks() - ticks; printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", @@ -86,13 +85,13 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecpPre+ecMulPre[SNZ] - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, ec->f->no, combo_state); + prngCOMBOStepR(d, no, combo_state); ecpPreSNZ(pre, ec->base, w, ec, stack); - ecMulPreSNZ(pt, pre, ec, d, ec->f->n, stack); + ecMulPreSNZ(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; printf(" ecpPre+ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", @@ -101,13 +100,13 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecpPre+ecMulPre[SNZA] - for (w = 4; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, ec->f->no, combo_state); + prngCOMBOStepR(d, no, combo_state); ecpPreSNZA(pre, ec->base, w, ec, stack); - ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); + ecMulPreSNZA(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; printf(" ecpPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", @@ -116,13 +115,13 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecMulPre[SNZ] - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { ecPreSNZ(pre, ec->base, w, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulPreSNZ(pt, pre, ec, d, ec->f->n, stack); + prngCOMBOStepR(d, no, combo_state); + ecMulPreSNZ(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; printf(" ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", @@ -131,13 +130,13 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecMulPre[SNZA] - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { ecPreSNZA(pre, ec->base, w, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulPreSNZA(pt, pre, ec, d, ec->f->n, stack); + prngCOMBOStepR(d, no, combo_state); + ecMulPreSNZA(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; printf(" ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", @@ -146,14 +145,14 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecMulPre[SNZH] - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { - size_t h = (B_OF_W(ec->f->n) + w - 1) / w; + size_t h = (B_OF_O(no) + w - 1) / w; ecPreSNZH(pre, ec->base, w, h, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, ec->f->no, combo_state); - ecMulPreSNZH(pt, pre, ec, d, ec->f->n, stack); + prngCOMBOStepR(d, no, combo_state); + ecMulPreSNZH(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; printf(" ecMulPre[SNZH,w=%u]: %u cycles/pt [%u pts/sec]\n", @@ -161,8 +160,24 @@ bool_t ecpBench() (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } + // ecMulPre[HPB] + for (w = min_w; w <= max_w; ++w) + { + size_t h = (B_OF_O(no) + w - 1) / w; + ecPreHPB(pre, ec->base, w, h, ec, stack); + for (i = 0, ticks = tmTicks(); i < reps; ++i) + { + prngCOMBOStepR(d, no, combo_state); + ecMulPreHPB(pt, pre, ec, d, n, stack); + } + ticks = tmTicks() - ticks; + printf(" ecMulPre[HPB,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + } // ecPre[SNZ] - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) ecPreSNZ(pre, ec->base, w, ec, stack); @@ -173,7 +188,7 @@ bool_t ecpBench() (unsigned)tmSpeed(reps, ticks)); } // ecpPre[SNZ] - for (w = 3; w <= 6; ++w) + for (w = min_w; w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) ecpPreSNZ(pre, ec->base, w, ec, stack); @@ -186,6 +201,27 @@ bool_t ecpBench() } // завершение blobClose(state); - bignEcClose(ec); return TRUE; } + +/* +******************************************************************************* +Оценка производительности на кривой bign-curve256v1 +******************************************************************************* +*/ + +bool_t ecpBench() +{ + bool_t ret; + bign_params params[1]; + ec_o* ec; + // создать кривую + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || + bignEcCreate(&ec, params) != ERR_OK) + return FALSE; + // оценка + ret = ecpBench_internal(ec); + // завершение + bignEcClose(ec); + return ret; +} diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 318a515f..9a445116 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -85,6 +85,7 @@ bool_t ecpTest() void* stack; // другие переменные size_t pos; + size_t w; // создать состояние state = blobCreate2( ec_keep, @@ -425,9 +426,9 @@ bool_t ecpTest() } } // предвычисления по схеме HPB + for (w = min_w; w <= max_w; ++w) { - const size_t w = max_w; - const size_t h = min_h; + const size_t h = (B_OF_O(no) + w - 1) / w; size_t i; // pre[0..2^{w-1}) <- (1, 3, ..., 2^{w-1}-1)base ecPreHPB(pre, ec->base, w, h, ec, stack); @@ -458,125 +459,113 @@ bool_t ecpTest() } } // кратная точка: ecMulA vs ecMulPreSNZ + for (w = min_w; w <= max_w; ++w) { - size_t w; - for (w = min_w; w <= max_w; ++w) + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + ecpPreSNZ(pre, ec->base, w, ec, stack); + while (!wwIsZero(d, n)) { - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - ecpPreSNZ(pre, ec->base, w, ec, stack); - while (!wwIsZero(d, n)) - { - if (!ecMulPreSNZ(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 11); - } - if (ecMulPreSNZ(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) + if (!ecMulPreSNZ(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } + wwShLo(d, n, 11); + } + if (ecMulPreSNZ(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; } } // кратная точка: ecMulA vs ecMulPreSNZA + for (w = min_w; w <= max_w; ++w) { - size_t w; - for (w = min_w; w <= max_w; ++w) + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) { - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) - { - blobClose(state); - return FALSE; - } - while (!wwIsZero(d, n)) - { - if (!ecMulPreSNZA(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 23); - } - if (ecMulPreSNZA(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, n)) + { + if (!ecMulPreSNZA(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } + wwShLo(d, n, 23); + } + if (ecMulPreSNZA(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; } } // кратная точка: ecMulA vs ecMulPreSNZH + for (w = min_w; w <= max_w; ++w) { - size_t w; - for (w = min_w; w <= max_w; ++w) + const size_t h = (B_OF_O(no) + w - 1) / w; + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) { - const size_t h = (B_OF_W(ec->f->n) + w - 1) / w; - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) - { - blobClose(state); - return FALSE; - } - while (!wwIsZero(d, n)) - { - if (!ecMulPreSNZH(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 13); - } - if (ecMulPreSNZH(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, n)) + { + if (!ecMulPreSNZH(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } + wwShLo(d, n, 13); + } + if (ecMulPreSNZH(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; } } // кратная точка: ecMulA vs ecMulPreHPB + for (w = min_w; w <= max_w; ++w) { - size_t w; - for (w = min_w; w <= max_w; ++w) + const size_t h = (B_OF_O(no) + w - 1) / w; + wwCopy(d, ec->order, n); + zzSubW2(d, n, 1); + if (!ecPreHPB(pre, ec->base, w, h, ec, stack)) { - const size_t h = (B_OF_W(ec->f->n) + w - 1) / w; - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - if (!ecPreHPB(pre, ec->base, w, h, ec, stack)) - { - blobClose(state); - return FALSE; - } - while (!wwIsZero(d, n)) - { - if (!ecMulPreHPB(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 15); - } - if (ecMulPreHPB(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, n)) + { + if (!ecMulPreHPB(pt0, pre, ec, d, n, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } + wwShLo(d, n, 15); + } + if (ecMulPreHPB(pt0, pre, ec, d, n, stack) || + ecMulA(pt1, ec->base, ec, d, n, stack)) + { + blobClose(state); + return FALSE; } } // сумма кратных точек: ecMulA vs ecAddMulA From a05a088bb89f14b728922879644bb6159ae3b561 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 12 Feb 2026 17:39:33 +0300 Subject: [PATCH 38/77] Add test/math/ec --- src/crypto/bign/bign_lcl.h | 36 +- src/math/ec.c | 7 +- test/CMakeLists.txt | 1 + test/math/ec_test.c | 748 ++++++++++++++++++++++++++++++++++ test/math/ecp_bench.c | 10 +- test/math/ecp_test.c | 575 +++----------------------- test/test.c | 4 +- win/vs09/test.vcproj | 4 + win/vs15/test.vcxproj | 1 + win/vs15/test.vcxproj.filters | 3 + 10 files changed, 829 insertions(+), 560 deletions(-) create mode 100644 test/math/ec_test.c diff --git a/src/crypto/bign/bign_lcl.h b/src/crypto/bign/bign_lcl.h index 4b7809b3..39d079a2 100644 --- a/src/crypto/bign/bign_lcl.h +++ b/src/crypto/bign/bign_lcl.h @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): local declarations \project bee2 [cryptographic library] \created 2014.04.03 -\version 2025.09.24 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -14,6 +14,7 @@ #define __BEE2_BIGN_LCL_H #include "bee2/crypto/bign.h" +#include "bee2/math/ec.h" #ifdef __cplusplus extern "C" { @@ -188,39 +189,6 @@ typedef size_t(*bign_deep_i)( size_t ec_deep /*!< [in] глубина стека эллиптической кривой */ ); -/*! \brief Длина состояния - - Возвращается длина состояния (в октетах) высокоуровневой функции, - которая работает на уровне стойкости l и имеет потребности в стеке deep. - \pre l == 128 || l == 192 || l == 256. - \return Длина состояния. - \remark Состояние включает как локальные переменные, так и стек. -*/ -size_t bignStart_keep( - size_t l, /*!< [in] уровень стойкости */ - bign_deep_i deep /*!< [in] потребности в стековой памяти */ -); - -/*! \brief Начало работы с параметрами - - По долговременным параметрам params по адресу state формируется описание - эллиптической кривой. - \pre bignParamsCheck(params) == ERR_OK. - \return ERR_OK, если описание успешно создано, и код ошибки в противном - случае. - \remark Высокоуровневый механизм должен работать по следующей схеме: - - описать потребности в стеке с помощью функции интерфейса bign_deep_i; - - определить длину состояния, вызвав bignStart_keep(); - - определить начало стека как память в state сразу после описания - эллиптической кривой. - . -*/ -err_t bignStart( - void* state, /*!< [out] состояние */ - const bign_params* params /*!< [in] долговременные параметры */ -); - - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/math/ec.c b/src/math/ec.c index cdc28817..8d9ebfa7 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.11 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -76,8 +76,7 @@ bool_t ecGroupCreate(ec_o* ec, const octet xbase[], const octet ybase[], return FALSE; // установить порядок и кофактор wwFrom(ec->order, order, order_len); - wwSetZero(ec->order + W_OF_O(order_len), - ec->f->n + 1 - W_OF_O(order_len)); + wwSetZero(ec->order + W_OF_O(order_len), ec->f->n + 1 - W_OF_O(order_len)); ec->cofactor = (word)cofactor; // все нормально return TRUE; @@ -350,7 +349,7 @@ bool_t ecPreHPB(ec_pre_t* pre, const word a[], size_t w, size_t h, if (!ecToA(ecPrePtA(pre, 0, ec), t2, ec, stack)) return FALSE; // pre[next] <- pre[cur] \pm dbls[diff(next, cur)] - for (i = 1, code = 0; i < WORD_BIT_POS(w - 1); ++i) + for (i = 1, code = 0; i < (size_t)WORD_BIT_POS(w - 1); ++i) { size_t pos; pos = wordParity(code) ? wordCTZ(code) + 1 : 0; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da50b5fe..982990c2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(testbee2 crypto/g12s_test.c crypto/pfok_test.c crypto/stb99_test.c + math/ec_test.c math/ecp_test.c math/ecp_bench.c math/pp_test.c diff --git a/test/math/ec_test.c b/test/math/ec_test.c new file mode 100644 index 00000000..d481794d --- /dev/null +++ b/test/math/ec_test.c @@ -0,0 +1,748 @@ +/* +******************************************************************************* +\file ec_test.c +\brief Tests for elliptic curves +\project bee2/test +\created 2026.02.12 +\version 2026.02.12 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +******************************************************************************* +Сложение / вычитание афффинных точек +******************************************************************************* +*/ + +#define ecAddAA_local(n, ec_d)\ +/* t */ O_OF_W(ec_d * n) + +static bool_t ecAddAA(word c[], const word a[], const word b[], + const ec_o* ec, void* stack) +{ + word* t; /* [ec->d * ec->f->n] */ + memSlice(stack, + ecAddAA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + ecFromA(t, b, ec, stack); + ecAddA(t, t, a, ec, stack); + return ecToA(c, t, ec, stack); +} + +static size_t ecAddAA_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecAddAA_local(n, ec_d), + ec_deep, + SIZE_MAX); +} + +#define ecSubAA_local ecAddAA_local + +static bool_t ecSubAA(word c[], const word a[], const word b[], + const ec_o* ec, void* stack) +{ + word* t; /* [ec->d * ec->f->n] */ + memSlice(stack, + ecSubAA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + ecNegA(t, b, ec, stack); + ecFromA(t, t, ec, stack); + ecAddA(t, t, a, ec, stack); + return ecToA(c, t, ec, stack); +} + +#define ecSubAA_deep ecAddAA_deep + +/* +******************************************************************************* +Тестирование на заданной кривой +******************************************************************************* +*/ + +bool_t ecTestEc(const ec_o* ec) +{ + // размерности + const size_t n = ec->f->n; + const size_t no = ec->f->no; + const size_t min_w = 3; + const size_t max_w = 6; + const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; + const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; + const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); + // состояние + void* state; + ec_pre_t* pre; /* [max_pre_count проективных точек] */ + word* pt0; /* [ec->d * n] */ + word* pt1; /* [ec->d * n] */ + word* pt2; /* [ec->d * n] */ + word* pt3; /* [ec->d * n] */ + word* pt4; /* [ec->d * n] */ + word* pt5; /* [ec->d * n] */ + word* d; /* [n + 1] */ + void* stack; + // другие переменные + size_t w; + size_t i; + // создать состояние + state = blobCreate2( + sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(n + 1), + utilMax(14, + ec->deep, + ecHasOrderA_deep(n, ec->d, ec->deep, n), + ecAddAA_deep(n, ec->d, ec->deep), + ecSubAA_deep(n, ec->d, ec->deep), + ecMulA_deep(n, ec->d, ec->deep, n), + ecPreSNZ_deep(n, ec->d, ec->deep), + ecPreSNZA_deep(n, ec->d, ec->deep), + ecPreSNZH_deep(n, ec->d, ec->deep), + ecPreHPB_deep(n, ec->d, max_h, ec->deep), + ecMulPreSNZ_deep(n, ec->d, ec->deep, n), + ecMulPreSNZA_deep(n, ec->d, ec->deep, n), + ecMulPreSNZH_deep(n, ec->d, ec->deep, n), + ecMulPreHPB_deep(n, ec->d, ec->deep, n), + ecAddMulA_deep(n, ec->d, ec->deep, 4, + (size_t)1, (size_t)2, (size_t)3, (size_t)4)), + SIZE_MAX, + &pre, &pt0, &pt1, &pt2, &pt3, &pt4, &pt5, &d, &stack); + if (state == 0) + return FALSE; + // работоспособная кривая? + // работоспособная группа? + // надежная группа? + // базовая точка имеет порядок q? + if (!ecIsOperable(ec) || + !ecIsOperable2(ec) || + !ecGroupIsOperable(ec) || + !ecHasOrderA(ec->base, ec, ec->order, n + 1, stack)) + { + blobClose(state); + return FALSE; + } + // утроить базовую точку разными способами + { + // d <- 3 + d[0] = 3; + // удвоить и сложить + if (!ecAddAA(pt0, ec->base, ec->base, ec, stack) || + !ecAddAA(pt0, pt0, ec->base, ec, stack) || + // дважды удвоить и вычесть + !ecAddAA(pt1, ec->base, ec->base, ec, stack) || + !ecAddAA(pt1, pt1, pt1, ec, stack) || + !ecSubAA(pt1, pt1, ec->base, ec, stack) || + !wwEq(pt0, pt1, 2 * n) || + (ecNegA(pt1, pt1, ec, stack), ecAddAA(pt1, pt0, pt1, ec, stack)) || + // вычислить кратную точку + !ecMulA(pt1, ec->base, ec, d, 1, stack) || + !wwEq(pt0, pt1, 2 * n) || + // утроить напрямую + !ec->froma || !ec->tpl || !ec->toa || + (ec->froma(pt1, ec->base, ec, stack), + ec->tpl(pt1, pt1, ec, stack), + ec->toa(pt1, pt1, ec, stack), + !wwEq(pt0, pt1, 2 * n))) + { + blobClose(state); + return FALSE; + } + } + // удвоение со сложением с аффинной точкой + if (ec->dbladda) + { + // pt0 <- 2 base + base + ecFromA(pt1, ec->base, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt3 <- 3 base + ecAddAA(pt3, ec->base, ec->base, ec, stack); + ecAddAA(pt3, pt3, ec->base, ec, stack); + // pt0 == pt3? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2(-base) + base + ecNeg(pt1, pt1, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt3 <- -base + ecNegA(pt3, ec->base, ec, stack); + // pt0 == pt3? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2O + base + ecSetO(pt1, ec); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt0 == base? + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, ec->base, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- 2(2base) + base + ecDblA(pt1, ec->base, ec, stack); + ec->dbladda(pt0, pt1, ec->base, ec, stack); + // pt0 == 5 base? + d[0] = 5, ecMulA(pt3, ec->base, ec, d, 1, stack); + if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // установка знака + if (ec->sgn) + { + ecFromA(pt0, ec->base, ec, stack); + wwCopy(pt1, pt0, 3 * n); + ecSgn(pt0, 0, ec, stack); + if (!wwEq(pt0, pt1, 3 * n)) + { + blobClose(state); + return FALSE; + } + ecSgn(pt0, 1, ec, stack); + ecNeg(pt1, pt1, ec, stack); + if (!wwEq(pt0, pt1, 3 * n)) + { + blobClose(state); + return FALSE; + } + } + // установка знака аффинной точки + if (ec->sgna) + { + wwCopy(pt0, ec->base, 2 * n); + ecSgnA(pt0, 0, ec, stack); + if (!wwEq(pt0, ec->base, 2 * n)) + { + blobClose(state); + return FALSE; + } + ecSgnA(pt0, 1, ec, stack); + ecNegA(pt1, ec->base, ec, stack); + if (!wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // финишное сложение + if (ec->finadd) + { + // (pt1, pt2, pt3, pt4) <- (base, 2 base, 3 base, 4 base) + ecFromA(pt1, ec->base, ec, stack); + ecAddA(pt2, pt1, ec->base, ec, stack); + ecAddA(pt3, pt2, ec->base, ec, stack); + ecAddA(pt4, pt3, ec->base, ec, stack); + // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? + ec->finadd(pt0, pt1, pt2, ec, stack); + ecToA(pt5, pt3, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pt1 + pt3 == 4 base, pt0 == pt4? + ec->finadd(pt0, pt1, pt3, ec, stack); + ecToA(pt5, pt4, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pt2 + pt2 == 4 base, pt0 == pt4? + ec->finadd(pt0, pt2, pt2, ec, stack); + ecToA(pt5, pt4, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pt1 + pt1 == 2 base, pt0 == pt2? + ec->finadd(pt0, pt1, pt1, ec, stack); + ecToA(pt5, pt2, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // финишное сложение с аффинной точкой + if (ec->finadda) + { + // (pt1, pt2, pt3, pt4) <- (base, 2 base, 3 base, 4 base) + ecFromA(pt1, ec->base, ec, stack); + ecAddA(pt2, pt1, ec->base, ec, stack); + ecAddA(pt3, pt2, ec->base, ec, stack); + ecAddA(pt4, pt3, ec->base, ec, stack); + // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? + ecToA(pt5, pt2, ec, stack); + ec->finadda(pt0, pt1, pt5, ec, stack); + ecToA(pt5, pt3, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pt1 + pt3 == 4 base, pt0 == pt4? + ecToA(pt5, pt3, ec, stack); + ec->finadda(pt0, pt1, pt5, ec, stack); + ecToA(pt5, pt4, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pt2 + pt2 == 4 base, pt0 == pt4? + ecToA(pt5, pt2, ec, stack); + ec->finadda(pt0, pt2, pt5, ec, stack); + ecToA(pt5, pt4, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pt1 + pt1 == 2 base, pt0 ==? pt2 + ec->finadda(pt0, pt1, ec->base, ec, stack); + ecToA(pt5, pt2, ec, stack); + if (!wwEq(pt0, pt5, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // предвычисления по схеме SNZ + for (w = min_w; w <= max_w; ++w) + { + // pre[0..2^w) <- (1, 3, ..., 2^w-1)base + ecPreSNZ(pre, ec->base, w, ec, stack); + // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] + wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + { + ecDbl(pt0, pt0, ec, stack); + ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); + } + // pt0 == \sum_{i = 0}^{2^w-1} (2i + 1) 2^{2^{w-1}-1-i} base? + wwSetW(d, n, 1); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + { + wwShHi(d, n, 1); + zzAddW2(d, n, (word)(2 * i + 1)); + } + if (!ecToA(pt0, pt0, ec, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // предвычисления по схеме SNZA + for (w = min_w; w <= max_w; ++w) + { + size_t i; + // pre[0..2^w) <- (1, 3, ..., 2^w-1)base + if (!ecPreSNZA(pre, ec->base, w, ec, stack)) + { + blobClose(state); + return FALSE; + } + // pt0 <- pre[1] - pre[0] + ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); + if (!ecAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) + { + blobClose(state); + return FALSE; + } + // pre[i+1] - pre[i] == pt0? + for (i = 1; i + 1 < SIZE_BIT_POS(w - 1); ++i) + { + ecNegA(pt1, ecPrePtA(pre, i, ec), ec, stack); + if (!ecAddAA(pt1, pt1, ecPrePtA(pre, i + 1, ec), ec, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // pt0 == 2 base? + if (!ecAddAA(pt1, ec->base, ec->base, ec, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // предвычисления по схеме HPB + for (w = min_w; w <= max_w; ++w) + { + const size_t h = (B_OF_O(no) + w - 1) / w; + size_t i; + // pre[0..2^{w-1}) <- (1, 3, ..., 2^{w-1}-1)base + ecPreHPB(pre, ec->base, w, h, ec, stack); + // pre[0] == \sum_{i=0}^{w-1} (2^h)^i base? + wwSetZero(d, n + 1); + for (i = 0; i < w; ++i) + wwSetBit(d, h * i, TRUE); + if (!ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(ecPrePt(pre, 0, ec), pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- \sum_{i = 0}^{2^{w-1}-1} pre[i] + ecFromA(pt0, ecPrePtA(pre, 0, ec), ec, stack); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + ecAddA(pt0, pt0, ecPrePtA(pre, i, ec), ec, stack); + // pt0 == (2^{h(w-1)} * 2^{w-1}) base? + ASSERT((h + 1) * (w - 1) < B_OF_W(n + 1)); + wwSetZero(d, n + 1); + wwSetBit(d, (h + 1) * (w - 1), TRUE); + if (!ecToA(pt0, pt0, ec, stack) || + !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // сумма кратных точек: ecMulA vs ecAddMulA + if (n >= 4) + { + if (!ecAddMulA(pt0, ec, stack, 4, + ec->base, ec->order, (size_t)1, + ec->base, ec->order, (size_t)2, + ec->base, ec->order, (size_t)3, + ec->base, ec->order, (size_t)4)) + { + blobClose(state); + return FALSE; + } + wwCopy(d, ec->order, n); + d[4] = zzAddW2(d + 3, 1, zzAdd2(d, ec->order, 3)); + d[4] += zzAddW2(d + 2, 2, zzAdd2(d, ec->order, 2)); + d[4] += zzAddW2(d + 1, 3, zzAdd2(d, ec->order, 1)); + if (!ecMulA(pt1, ec->base, ec, d, 5, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } + // четный порядок? + if (ec->order[0] % 2 == 0) + { + blobClose(state); + return TRUE; + } + // кратная точка: ecMulA vs ecMulPreSNZ + for (w = min_w; w <= max_w; ++w) + { + const size_t m = wwWordSize(ec->order, n + 1); + wwCopy(d, ec->order, m); + zzSubW2(d, m, 1); + ecPreSNZ(pre, ec->base, w, ec, stack); + while (!wwIsZero(d, m)) + { + if (!ecMulPreSNZ(pt0, pre, ec, d, m, stack) || + !ecMulA(pt1, ec->base, ec, d, m, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, m, 31); + } + if (ecMulPreSNZ(pt0, pre, ec, d, m, stack) || + ecMulA(pt1, ec->base, ec, d, m, stack)) + { + blobClose(state); + return FALSE; + } + } + // кратная точка: ecMulA vs ecMulPreSNZA + for (w = min_w; w <= max_w; ++w) + { + const size_t m = wwWordSize(ec->order, n + 1); + wwCopy(d, ec->order, m); + zzSubW2(d, m, 1); + if (!ecPreSNZA(pre, ec->base, w, ec, stack)) + { + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, m)) + { + if (!ecMulPreSNZA(pt0, pre, ec, d, m, stack) || + !ecMulA(pt1, ec->base, ec, d, m, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, m, 23); + } + if (ecMulPreSNZA(pt0, pre, ec, d, m, stack) || + ecMulA(pt1, ec->base, ec, d, m, stack)) + { + blobClose(state); + return FALSE; + } + } + // кратная точка: ecMulA vs ecMulPreSNZH + for (w = min_w; w <= max_w; ++w) + { + const size_t m = wwWordSize(ec->order, n + 1); + const size_t h = (B_OF_O(no) + w - 1) / w; + wwCopy(d, ec->order, m); + zzSubW2(d, m, 1); + if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) + { + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, m)) + { + if (!ecMulPreSNZH(pt0, pre, ec, d, m, stack) || + !ecMulA(pt1, ec->base, ec, d, m, stack) || + !wwEq(pt0, pt1, 2 * m)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, m, 34); + } + if (ecMulPreSNZH(pt0, pre, ec, d, m, stack) || + ecMulA(pt1, ec->base, ec, d, m, stack)) + { + blobClose(state); + return FALSE; + } + } + // кратная точка: ecMulA vs ecMulPreHPB + for (w = min_w; w <= max_w; ++w) + { + const size_t m = wwWordSize(ec->order, n + 1); + const size_t h = (B_OF_O(no) + w - 1) / w; + wwCopy(d, ec->order, m); + zzSubW2(d, m, 1); + if (!ecPreHPB(pre, ec->base, w, h, ec, stack)) + { + blobClose(state); + return FALSE; + } + while (!wwIsZero(d, m)) + { + if (!ecMulPreHPB(pt0, pre, ec, d, m, stack) || + !ecMulA(pt1, ec->base, ec, d, m, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, m, 15); + } + if (ecMulPreHPB(pt0, pre, ec, d, m, stack) || + ecMulA(pt1, ec->base, ec, d, m, stack)) + { + blobClose(state); + return FALSE; + } + } + // все хорошо + blobClose(state); + return TRUE; +} + +/* +******************************************************************************* +Проверочная кривая: +- p -- простое, p = 3 (mod 4); +- a = p - 3; +- q = |E_{a,b}(FF_p)| -- простое число; +- ybase = b^((p + 1) / 4) mod p. + +\remark Вычисление порядка в PARI/GP: +\code + p = 2^256 - 189 + a = p - 3 + b = 5304 + E = ellinit([a, b], p) + ellcard(E) +\endcode + +При a = p - 5 порядок группы точек становится равным 2 * q1, где + q1 = 2^2 * 3 * 7 * 277847 * 282129349161734992975487682047 * + 17585131690472517417114517434596060246881. +Точка (xbase = 0, ybase) остается лежать на кривой, имея порядок q1. + +\remark +Определение порядка точки в PARI/GP: +\code + y = lift(Mod(b, p)^((p + 1)/4)) + ellorder(E, [0, y]) +\endcode +******************************************************************************* +*/ + +static const size_t no = 32; +static char p[] = + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43"; +static char a[] = + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40"; +static char b[] = + "00000000000000000000000000000000000000000000000000000000000014B8"; +static char q[] = + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D1229165911507C328526818EC4E11D"; +static char xbase[] = + "0000000000000000000000000000000000000000000000000000000000000000"; +static char ybase[] = + "B0E9804939D7C2E931D4CE052CCC6B6B692514CCADBA44940484EEA5F52D9268"; +static size_t cofactor = 1; + +static char q1[] = + "010000000000000000000000000000000063d339dd2fc92a1634b0ce9864ab0ef4"; + +/* +******************************************************************************* +Тестирование +******************************************************************************* +*/ + +bool_t ecTest() +{ + // размерности + const size_t n = W_OF_O(no); + const size_t f_keep = gfpCreate_keep(no); + const size_t f_deep = gfpCreate_deep(no); + const size_t ec_keep = ecpCreateJ_keep(n); + const size_t ec_deep = ecpCreateJ_deep(n, f_deep); + // состояние + void* state; + ec_o* ec; /* [ec_keep] */ + qr_o* f; /* [f_keep] */ + octet* t; /* [3 * no] */ + void* stack; + // создать состояние + state = blobCreate2( + ec_keep, + f_keep, + 3 * no + 1, + utilMax(7, + gfpCreate_deep(no), + ecpCreateJ_deep(n, f_deep), + ecGroupCreate_deep(f_deep), + ecpIsValid_deep(n, f_deep), + ecpGroupSeemsValid_deep(n, f_deep), + ecpGroupIsSafe_deep(n), + ec_deep), + SIZE_MAX, + &ec, &f, &t, &stack); + if (state == 0) + return FALSE; + // создать f = GF(p) + hexToRev(t, p); + if (!gfpCreate(f, t, no, stack)) + { + blobClose(state); + return FALSE; + } + // создать ec = EC_{ab}(f) + hexToRev(t, a), hexToRev(t + no, b); + if (!ecpCreateJ(ec, f, t, t + no, stack) || ec->d != 3) + { + blobClose(state); + return FALSE; + } + // создать группу точек ec + hexToRev(t, xbase), hexToRev(t + no, ybase), hexToRev(t + 2 * no, q); + if (!ecGroupCreate(ec, t, t + no, t + 2 * no, no, cofactor, stack)) + { + blobClose(state); + return FALSE; + } + // присоединить f к ec + objAppend(ec, f, 0); + // корректная кривая? + // корректная группа? + // надежная группа? + if (!ecpIsValid(ec, stack) || + !ecpGroupSeemsValid(ec, stack) || + !ecpGroupIsSafe(ec, 40, stack)) + { + blobClose(state); + return FALSE; + } + // выполнить тесты + if (!ecTestEc(ec)) + { + blobClose(state); + return FALSE; + } + // вывести f = GF(p) за пределы ec + objCopy(f, objPtr(ec, 0, qr_o)); + // создать ec = EC_{a-2, b}(f) + hexToRev(t, a), t[0] -= 2, hexToRev(t + no, b); + if (!ecpCreateJ(ec, f, t, t + no, stack)) + { + blobClose(state); + return FALSE; + } + // заново присоединить f к ec + objAppend(ec, f, 0); + // заново создать группу точек ec: + // - точка (xbase = 0, ybase) лежит на ec; + // - порядок группы меняется на q1. + ASSERT(strLen(q1) == 2 * no + 2); + hexToRev(t, xbase), hexToRev(t + no, ybase), hexToRev(t + 2 * no, q1); + if (!ecGroupCreate(ec, t, t + no, t + 2 * no, no + 1, cofactor, stack)) + { + blobClose(state); + return FALSE; + } + // корректная кривая? + // корректная группа? + // группа перестала быть надежной? + if (!ecpIsValid(ec, stack) || + !ecpGroupSeemsValid(ec, stack) || + ecpGroupIsSafe(ec, 40, stack)) + { + blobClose(state); + return FALSE; + } + // заблокировать функции интерфейсов ec_finadd_i, ec_finadda_i + ec->finadd = 0, ec->finadda = 0; + // выполнить тесты + if (!ecTestEc(ec)) + { + blobClose(state); + return FALSE; + } + // все хорошо + blobClose(state); + return TRUE; +} diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 9ff838cb..bee03696 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.11 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -12,11 +12,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -26,7 +24,7 @@ ******************************************************************************* */ -bool_t ecpBench_internal(const ec_o* ec) +static bool_t ecpBenchEc(const ec_o* ec) { const size_t n = ec->f->n; const size_t no = ec->f->no; @@ -47,7 +45,7 @@ bool_t ecpBench_internal(const ec_o* ec) prngCOMBO_keep(), O_OF_W(2 * n), O_OF_W(n), - sizeof(ec_pre_t) + O_OF_W(max_pre_count * 3 * n), + sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), utilMax(11, ecMulA_deep(n, ec->d, ec->deep, n), ecPreSNZ_deep(n, ec->d, ec->deep), @@ -220,7 +218,7 @@ bool_t ecpBench() bignEcCreate(&ec, params) != ERR_OK) return FALSE; // оценка - ret = ecpBench_internal(ec); + ret = ecpBenchEc(ec); // завершение bignEcClose(ec); return ret; diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 9a445116..a31af395 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,161 +4,69 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.11 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* */ #include -#include -#include -#include #include -#include #include #include #include +#include /* ******************************************************************************* -Проверочная кривая +Тестирование на заданной кривой ******************************************************************************* */ -static const size_t no = 32; -static char p[] = - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43"; -static char a[] = - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40"; -static char b[] = - "00000000000000000000000000000000000000000000000000000000000014B8"; -static char q[] = - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D1229165911507C328526818EC4E11D"; -static char xbase[] = - "0000000000000000000000000000000000000000000000000000000000000000"; -static char ybase[] = - "B0E9804939D7C2E931D4CE052CCC6B6B692514CCADBA44940484EEA5F52D9268"; -static size_t cofactor = 1; - -/* -******************************************************************************* -Тестирование -******************************************************************************* -*/ - -typedef void (*ec_pre_snz_i)(ec_pre_t* pre, const word a[], size_t w, - const struct ec_o* ec, void* stack); - -typedef bool_t (*ec_pre_snza_i)(ec_pre_t* pre, const word a[], size_t w, - const struct ec_o* ec, void* stack); - -bool_t ecpTest() +static bool_t ecpTestEc(const ec_o* ec) { - // функции предвычислений - const ec_pre_snz_i pre_snz_fn[] = { ecPreSNZ, ecpPreSNZ }; - const ec_pre_snza_i pre_snza_fn[] = { ecPreSNZA, ecpPreSNZA }; // размерности - const size_t n = W_OF_O(no); - const size_t f_keep = gfpCreate_keep(no); - const size_t f_deep = gfpCreate_deep(no); - const size_t ec_keep = ecpCreateJ_keep(n); - const size_t ec_deep = ecpCreateJ_deep(n, f_deep); + const size_t n = ec->f->n; const size_t min_w = 3; const size_t max_w = 6; - const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; - const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; - const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); + const size_t max_pre_count = SIZE_BIT_POS(max_w - 1); // состояние void* state; - ec_o* ec; /* [ec_keep] */ - qr_o* f; /* [f_keep] */ - octet* t; /* [3 * no] */ - ec_pre_t* pre; /* [max_pre_count якобиевых точек] */ - word* pt0; /* [3 * n] */ - word* pt1; /* [3 * n] */ - word* pt2; /* [3 * n] */ - word* pt3; /* [3 * n] */ - word* pt4; /* [3 * n] */ - word* pt5; /* [3 * n] */ + ec_pre_t* pre; /* [max_pre_count проективных точек] */ + word* pt0; /* [ec->d * n] */ + word* pt1; /* [ec->d * n] */ word* d; /* [n + 1] */ void* stack; // другие переменные - size_t pos; size_t w; + size_t i; // создать состояние state = blobCreate2( - ec_keep, - f_keep, - 3 * no, - (sizeof(ec_pre_t) + O_OF_W(max_pre_count * 3 * n)) | SIZE_HI, - O_OF_W(3 * n), - O_OF_W(3 * n), - O_OF_W(3 * n), - O_OF_W(3 * n), - O_OF_W(3 * n), - O_OF_W(3 * n), + sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), + O_OF_W(ec->d * n), + O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(23, - gfpCreate_deep(no), - ecpCreateJ_deep(n, f_deep), - ecGroupCreate_deep(f_deep), - ecpIsValid_deep(n, f_deep), - ecpGroupSeemsValid_deep(n, f_deep), + utilMax(10, + ecpIsValid_deep(n, ec->f->deep), + ecpGroupSeemsValid_deep(n, ec->f->deep), ecpGroupIsSafe_deep(n), - ecHasOrderA_deep(n, 3, ec_deep, n), - ec_deep, - ecpIsOnA_deep(n, f_deep), - ecpAddAA_deep(n, f_deep), - ecpSubAA_deep(n, f_deep), - ecMulA_deep(n, 3, ec_deep, n), - ecPreSNZ_deep(n, 3, ec_deep), - ecPreSNZA_deep(n, 3, ec_deep), - ecPreSNZH_deep(n, 3, ec_deep), - ecPreHPB_deep(n, 3, max_h, ec_deep), - ecpPreSNZ_deep(n, f_deep, max_w), - ecpPreSNZA_deep(n, f_deep, max_w), - ecMulPreSNZ_deep(n, 3, ec_deep, n), - ecMulPreSNZA_deep(n, 3, ec_deep, n), - ecMulPreSNZH_deep(n, 3, ec_deep, n), - ecMulPreHPB_deep(n, 3, ec_deep, n), - ecAddMulA_deep(n, 3, ec_deep, 4, - (size_t)1, (size_t)2, (size_t)3, (size_t)4)), + ec->deep, + ecpIsOnA_deep(n, ec->f->deep), + ecpAddAA_deep(n, ec->f->deep), + ecpSubAA_deep(n, ec->f->deep), + ecMulA_deep(n, ec->d, ec->deep, n), + ecpPreSNZ_deep(n, ec->f->deep, max_w), + ecpPreSNZA_deep(n, ec->f->deep, max_w)), SIZE_MAX, - &ec, &f, &t, &pre, &pt0, &pt1, &pt2, &pt3, &pt4, &pt5, &d, &stack); + &pre, &pt0, &pt1, &d, &stack); if (state == 0) return FALSE; - // создать f = GF(p) - hexToRev(t, p); - if (!gfpCreate(f, t, no, stack)) - { - blobClose(state); - return FALSE; - } - // создать ec = EC_{ab}(f) - hexToRev(t, a), hexToRev(t + no, b); - if (!ecpCreateJ(ec, f, t, t + no, stack) || ec->d != 3) - { - blobClose(state); - return FALSE; - } - // создать группу точек ec - hexToRev(t, xbase), hexToRev(t + no, ybase), hexToRev(t + 2 * no, q); - if (!ecGroupCreate(ec, t, t + no, t + 2 * no, no, cofactor, stack)) - { - blobClose(state); - return FALSE; - } - // присоединить f к ec - objAppend(ec, f, 0); // корректная кривая? // корректная группа? // надежная группа? - // базовая точка имеет порядок q? if (!ecpIsValid(ec, stack) || !ecpGroupSeemsValid(ec, stack) || - !ecpGroupIsSafe(ec, 40, stack) || - !ecHasOrderA(ec->base, ec, ec->order, n, stack)) + !ecpGroupIsSafe(ec, 40, stack)) { blobClose(state); return FALSE; @@ -179,203 +87,27 @@ bool_t ecpTest() (ecpNegA(pt1, pt1, ec), ecpAddAA(pt1, pt0, pt1, ec, stack)) || // вычислить кратную точку !ecMulA(pt1, ec->base, ec, d, 1, stack) || - !wwEq(pt0, pt1, 2 * n) || - // утроить напрямую - !ec->froma || !ec->tpl || !ec->toa || - (ec->froma(pt1, ec->base, ec, stack), - ec->tpl(pt1, pt1, ec, stack), - ec->toa(pt1, pt1, ec, stack), - !wwEq(pt0, pt1, 2 * n))) - { - blobClose(state); - return FALSE; - } - } - // удвоение со сложением с аффинной точкой - if (ec->dbladda) - { - // pt0 <- 2 base + base - ecFromA(pt1, ec->base, ec, stack); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt3 <- 3 base - ecpAddAA(pt3, ec->base, ec->base, ec, stack); - ecpAddAA(pt3, pt3, ec->base, ec, stack); - // pt0 == pt3? - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- 2(-base) + base - ecNeg(pt1, pt1, ec, stack); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt3 <- -base - ecpNegA(pt3, ec->base, ec); - // pt0 == pt3? - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- 2O + base - ecSetO(pt1, ec); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt0 == base? - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, ec->base, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- 2(2base) + base - ecDblA(pt1, ec->base, ec, stack); - ec->dbladda(pt0, pt1, ec->base, ec, stack); - // pt0 == 5 base? - d[0] = 5, ecMulA(pt3, ec->base, ec, d, 1, stack); - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - // установка знака - if (ec->sgn) - { - ecFromA(pt0, ec->base, ec, stack); - wwCopy(pt1, pt0, 3 * n); - ecSgn(pt0, 0, ec, stack); - if (!wwEq(pt0, pt1, 3 * n)) - { - blobClose(state); - return FALSE; - } - ecSgn(pt0, 1, ec, stack); - ecNeg(pt1, pt1, ec, stack); - if (!wwEq(pt0, pt1, 3 * n)) - { - blobClose(state); - return FALSE; - } - } - // установка знака аффинной точки - if (ec->sgna) - { - wwCopy(pt0, ec->base, 2 * n); - ecSgnA(pt0, 0, ec, stack); - if (!wwEq(pt0, ec->base, 2 * n)) - { - blobClose(state); - return FALSE; - } - ecSgnA(pt0, 1, ec, stack); - ecNegA(pt1, ec->base, ec, stack); - if (!wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - // финишное сложение - if (ec->finadd) - { - // (pt1, pt2, pt3, pt4) <- (base, 2 base, 3 base, 4 base) - ecFromA(pt1, ec->base, ec, stack); - ecAddA(pt2, pt1, ec->base, ec, stack); - ecAddA(pt3, pt2, ec->base, ec, stack); - ecAddA(pt4, pt3, ec->base, ec, stack); - // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? - ec->finadd(pt0, pt1, pt2, ec, stack); - ecToA(pt5, pt3, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- pt1 + pt3 == 4 base, pt0 == pt4? - ec->finadd(pt0, pt1, pt3, ec, stack); - ecToA(pt5, pt4, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- pt2 + pt2 == 4 base, pt0 == pt4? - ec->finadd(pt0, pt2, pt2, ec, stack); - ecToA(pt5, pt4, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- pt1 + pt1 == 2 base, pt0 == pt2? - ec->finadd(pt0, pt1, pt1, ec, stack); - ecToA(pt5, pt2, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - // финишное сложение с аффинной точкой - if (ec->finadda) - { - // (pt1, pt2, pt3, pt4) <- (base, 2 base, 3 base, 4 base) - ecFromA(pt1, ec->base, ec, stack); - ecAddA(pt2, pt1, ec->base, ec, stack); - ecAddA(pt3, pt2, ec->base, ec, stack); - ecAddA(pt4, pt3, ec->base, ec, stack); - // pt0 <- pt1 + pt2 = 3 base, pt0 == pt3? - ecToA(pt5, pt2, ec, stack); - ec->finadda(pt0, pt1, pt5, ec, stack); - ecToA(pt5, pt3, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- pt1 + pt3 == 4 base, pt0 == pt4? - ecToA(pt5, pt3, ec, stack); - ec->finadda(pt0, pt1, pt5, ec, stack); - ecToA(pt5, pt4, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- pt2 + pt2 == 4 base, pt0 == pt4? - ecToA(pt5, pt2, ec, stack); - ec->finadda(pt0, pt2, pt5, ec, stack); - ecToA(pt5, pt4, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- pt1 + pt1 == 2 base, pt0 ==? pt2 - ec->finadda(pt0, pt1, ec->base, ec, stack); - ecToA(pt5, pt2, ec, stack); - if (!wwEq(pt0, pt5, 2 * n)) + !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } } // предвычисления по схеме SNZ - for (pos = 0; pos < COUNT_OF(pre_snz_fn); ++pos) + for (w = min_w; w <= max_w; ++w) { - size_t i; - // pre[0..32) <- (1, 3, ..., 31)base - ASSERT(6 <= max_w); - pre_snz_fn[pos](pre, ec->base, 6, ec, stack); - // pt0 <- \sum_{i = 0}^31 2^{31 - i} pre[i] - wwCopy(pt0, ecPrePt(pre, 0, ec), 3 * n); - for (i = 1; i < 32; ++i) + // pre[0..2^w) <- (1, 3, ..., 2^w-1)base + ecpPreSNZ(pre, ec->base, w, ec, stack); + // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] + wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { ecDbl(pt0, pt0, ec, stack); ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); } - // pt0 == \sum_{i = 0}^31 (2i + 1) 2^{31 - i} base? + // pt0 == \sum_{i = 0}^{2^w-1} (2i + 1) 2^{2^{w-1}-1-i} base? wwSetW(d, n, 1); - for (i = 1; i < 32; ++i) + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { wwShHi(d, n, 1); zzAddW2(d, n, (word)(2 * i + 1)); @@ -389,12 +121,10 @@ bool_t ecpTest() } } // предвычисления по схеме SNZA - for (pos = 0; pos < COUNT_OF(pre_snza_fn); ++pos) + for (w = min_w; w <= max_w; ++w) { - size_t i; - // pre[0..32) <- (1, 3, ..., 31)base - ASSERT(6 <= max_w); - if (!pre_snza_fn[pos](pre, ec->base, 6, ec, stack)) + // pre[0..2^w) <- (1, 3, ..., 2^w-1)base + if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) { blobClose(state); return FALSE; @@ -407,7 +137,7 @@ bool_t ecpTest() return FALSE; } // pre[i+1] - pre[i] == pt0? - for (i = 1; i + 1 < 32; ++i) + for (i = 1; i + 1 < SIZE_BIT_POS(w - 1); ++i) { ecNegA(pt1, ecPrePtA(pre, i, ec), ec, stack); if (!ecpAddAA(pt1, pt1, ecPrePtA(pre, i + 1, ec), ec, stack) || @@ -425,214 +155,29 @@ bool_t ecpTest() return FALSE; } } - // предвычисления по схеме HPB - for (w = min_w; w <= max_w; ++w) - { - const size_t h = (B_OF_O(no) + w - 1) / w; - size_t i; - // pre[0..2^{w-1}) <- (1, 3, ..., 2^{w-1}-1)base - ecPreHPB(pre, ec->base, w, h, ec, stack); - // pre[0] == \sum_{i=0}^{w-1} (2^h)^i base? - wwSetZero(d, n + 1); - for (i = 0; i < w; ++i) - wwSetBit(d, h * i, TRUE); - if (!ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(ecPrePt(pre, 0, ec), pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- \sum_{i = 0}^{2^{w-1}-1} pre[i] - ecFromA(pt0, ecPrePtA(pre, 0, ec), ec, stack); - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) - ecAddA(pt0, pt0, ecPrePtA(pre, i, ec), ec, stack); - // pt0 == (2^{h(w-1)} * 2^{w-1}) base? - ASSERT((h + 1) * (w - 1) < B_OF_W(n + 1)); - wwSetZero(d, n + 1); - wwSetBit(d, (h + 1) * (w - 1), TRUE); - if (!ecToA(pt0, pt0, ec, stack) || - !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - // кратная точка: ecMulA vs ecMulPreSNZ - for (w = min_w; w <= max_w; ++w) - { - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - ecpPreSNZ(pre, ec->base, w, ec, stack); - while (!wwIsZero(d, n)) - { - if (!ecMulPreSNZ(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n + 1, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 11); - } - if (ecMulPreSNZ(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) - { - blobClose(state); - return FALSE; - } - } - // кратная точка: ecMulA vs ecMulPreSNZA - for (w = min_w; w <= max_w; ++w) - { - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) - { - blobClose(state); - return FALSE; - } - while (!wwIsZero(d, n)) - { - if (!ecMulPreSNZA(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 23); - } - if (ecMulPreSNZA(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) - { - blobClose(state); - return FALSE; - } - } - // кратная точка: ecMulA vs ecMulPreSNZH - for (w = min_w; w <= max_w; ++w) - { - const size_t h = (B_OF_O(no) + w - 1) / w; - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) - { - blobClose(state); - return FALSE; - } - while (!wwIsZero(d, n)) - { - if (!ecMulPreSNZH(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 13); - } - if (ecMulPreSNZH(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) - { - blobClose(state); - return FALSE; - } - } - // кратная точка: ecMulA vs ecMulPreHPB - for (w = min_w; w <= max_w; ++w) - { - const size_t h = (B_OF_O(no) + w - 1) / w; - wwCopy(d, ec->order, n); - zzSubW2(d, n, 1); - if (!ecPreHPB(pre, ec->base, w, h, ec, stack)) - { - blobClose(state); - return FALSE; - } - while (!wwIsZero(d, n)) - { - if (!ecMulPreHPB(pt0, pre, ec, d, n, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, n, 15); - } - if (ecMulPreHPB(pt0, pre, ec, d, n, stack) || - ecMulA(pt1, ec->base, ec, d, n, stack)) - { - blobClose(state); - return FALSE; - } - } - // сумма кратных точек: ecMulA vs ecAddMulA - if (n >= 4) - { - if (!ecAddMulA(pt0, ec, stack, 4, - ec->base, ec->order, (size_t)1, - ec->base, ec->order, (size_t)2, - ec->base, ec->order, (size_t)3, - ec->base, ec->order, (size_t)4)) - { - blobClose(state); - return FALSE; - } - wwCopy(d, ec->order, n); - d[4] = zzAddW2(d + 3, 1, zzAdd2(d, ec->order, 3)); - d[4] += zzAddW2(d + 2, 2, zzAdd2(d, ec->order, 2)); - d[4] += zzAddW2(d + 1, 3, zzAdd2(d, ec->order, 1)); - if (!ecMulA(pt1, ec->base, ec, d, 5, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - // вывести f = GF(p) за пределы ec - objCopy(f, objPtr(ec, 0, qr_o)); - // создать ec = EC_{a-1, b}(f) - hexToRev(t, a), --t[0], hexToRev(t + no, b); - if (!ecpCreateJ(ec, f, t, t + no, stack)) - { - blobClose(state); - return FALSE; - } - // заново присоединить f к ec - objAppend(ec, f, 0); - // точка (xbase = 0, ybase) все еще лежит на ec: ybase^2 = b - hexToRev(t, xbase), hexToRev(t + no, ybase); - wwFrom(ec->base, t, no), wwFrom(ec->base + n, t + no, no); - ASSERT(wwIsZero(ec->base, n)); - // утроить базовую точку разными способами - { - // d <- 3 - d[0] = 3; - // удвоить и сложить - if (!ecpIsOnA(ec->base, ec, stack) || - !ecpAddAA(pt0, ec->base, ec->base, ec, stack) || - !ecpAddAA(pt0, pt0, ec->base, ec, stack) || - // дважды удвоить и вычесть - !ecpAddAA(pt1, ec->base, ec->base, ec, stack) || - !ecpAddAA(pt1, pt1, pt1, ec, stack) || - !ecpSubAA(pt1, pt1, ec->base, ec, stack) || - !wwEq(pt0, pt1, 2 * n) || - (ecpNegA(pt1, pt1, ec), ecpAddAA(pt1, pt0, pt1, ec, stack)) || - // вычислить кратную точку - !ecMulA(pt1, ec->base, ec, d, 1, stack) || - !wwEq(pt0, pt1, 2 * n) || - (ec->froma(pt1, ec->base, ec, stack), - ec->tpl(pt1, pt1, ec, stack), - ec->toa(pt1, pt1, ec, stack), - !wwEq(pt0, pt1, 2 * n))) - { - blobClose(state); - return FALSE; - } - } // все хорошо blobClose(state); return TRUE; } + +/* +******************************************************************************* +Тестирование на кривой bign-curve256v1 +******************************************************************************* +*/ + +bool_t ecpTest() +{ + bool_t ret; + bign_params params[1]; + ec_o* ec; + // создать кривую + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || + bignEcCreate(&ec, params) != ERR_OK) + return FALSE; + // оценка + ret = ecpTestEc(ec); + // завершение + bignEcClose(ec); + return ret; +} diff --git a/test/test.c b/test/test.c index 4b312ebc..76bef5b5 100644 --- a/test/test.c +++ b/test/test.c @@ -4,7 +4,7 @@ \brief Bee2 testing \project bee2/test \created 2014.04.02 -\version 2025.05.10 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -78,6 +78,7 @@ extern bool_t wwTest(); extern bool_t zzTest(); extern bool_t ppTest(); extern bool_t priTest(); +extern bool_t ecTest(); extern bool_t ecpTest(); extern bool_t ecpBench(); @@ -90,6 +91,7 @@ static int testMath() printf("zzTest: %s\n", (code = zzTest()) ? "OK" : "Err"), ret |= !code; printf("ppTest: %s\n", (code = ppTest()) ? "OK" : "Err"), ret |= !code; printf("priTest: %s\n", (code = priTest()) ? "OK" : "Err"), ret |= !code; + printf("ecTest: %s\n", (code = ecTest()) ? "OK" : "Err"), ret |= !code; printf("ecpTest: %s\n", (code = ecpTest()) ? "OK" : "Err"), ret |= !code; code = ecpBench(), ret |= !code; return ret; diff --git a/win/vs09/test.vcproj b/win/vs09/test.vcproj index 22007aad..a23386f5 100644 --- a/win/vs09/test.vcproj +++ b/win/vs09/test.vcproj @@ -256,6 +256,10 @@ + + diff --git a/win/vs15/test.vcxproj b/win/vs15/test.vcxproj index fefdabef..6ef8b5a0 100644 --- a/win/vs15/test.vcxproj +++ b/win/vs15/test.vcxproj @@ -392,6 +392,7 @@ + diff --git a/win/vs15/test.vcxproj.filters b/win/vs15/test.vcxproj.filters index 9ff61b84..35ddcc39 100644 --- a/win/vs15/test.vcxproj.filters +++ b/win/vs15/test.vcxproj.filters @@ -148,5 +148,8 @@ Source Files\core + + Source Files\math + \ No newline at end of file From 009e5d33817b181793c8a9b679add9de52673633 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 12 Feb 2026 18:48:15 +0300 Subject: [PATCH 39/77] Add odd_order to the inputs of ecpCreateJ() --- include/bee2/math/ecp.h | 9 +++++++-- src/crypto/bake/bake_misc.c | 4 ++-- src/crypto/bign/bign_ec.c | 4 ++-- src/crypto/g12s.c | 2 +- src/math/ecp/ecp_j.c | 32 +++++++++++++++++++++++--------- test/math/ec_test.c | 14 +++++++------- test/math/ecp_bench.c | 2 +- 7 files changed, 43 insertions(+), 24 deletions(-) diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index dd6d49d3..0c08b8f9 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -55,8 +55,10 @@ O -- бесконечно удаленная точка. /*! \brief Создание эллиптической кривой в якобиевых координатах - Создается описание ec эллиптической кривой в якобиевых координатах - над полем f с коэффициентами [f->no]A и [f->no]B. + Создается описание ec эллиптической кривой в якобиевых координатах над + полем f с коэффициентами [f->no]A и [f->no]B. При формировании описания + учитывается флаг odd_order, в котором указывается признак нечетности + порядка группы точек кривой. \return Признак успеха. \pre gfpIsOperable(f) == TRUE. \expect{FALSE} f->mod > 3. @@ -64,12 +66,15 @@ O -- бесконечно удаленная точка. \post Буферы ec->order и ec->base подготовлены для ecGroupCreate(). \keep{ec} ecpCreateJ_keep(f->n). \deep{stack} ecpCreateJ_deep(f->n, f->deep). + \remark Передача odd_order = FALSE означает отсутствие гарантий нечетности + порядка и не означает, что порядок обязательно является нечетным. */ bool_t ecpCreateJ( ec_o* ec, /*!< [in] описание кривой */ const qr_o* f, /*!< [in] базовое поле */ const octet A[], /*!< [in] коэффициент A */ const octet B[], /*!< [in] коэффициент B */ + bool_t odd_order, /*!< [in] признак нечетности порядка группы */ void* stack /*!< [in] вспомогательная память */ ); diff --git a/src/crypto/bake/bake_misc.c b/src/crypto/bake/bake_misc.c index 79274018..bcc45554 100644 --- a/src/crypto/bake/bake_misc.c +++ b/src/crypto/bake/bake_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.66 (bake): miscellaneous (curves, KDF) \project bee2 [cryptographic library] \created 2014.04.14 -\version 2025.09.25 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -62,7 +62,7 @@ err_t bakeEcStart(void* state, const bign_params* params) ASSERT(wwBitSize(f->mod, n) == params->l * 2); ASSERT(wwGetBits(f->mod, 0, 2) == 3); // создать кривую и группу - if (!ecpCreateJ(ec, f, params->a, params->b, stack) || + if (!ecpCreateJ(ec, f, params->a, params->b, TRUE, stack) || !ecGroupCreate(ec, 0, params->yG, params->q, no, 1, stack)) return ERR_BAD_PARAMS; ASSERT(wwBitSize(ec->order, n) == params->l * 2); diff --git a/src/crypto/bign/bign_ec.c b/src/crypto/bign/bign_ec.c index b6c35aef..f22ed76b 100644 --- a/src/crypto/bign/bign_ec.c +++ b/src/crypto/bign/bign_ec.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): curves \project bee2 [cryptographic library] \created 2012.04.27 -\version 2025.09.15 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -62,7 +62,7 @@ err_t bignEcCreate(ec_o** pec, const bign_params* params) } // создать поле, кривую и группу if (!gfpCreate(f, params->p, no, stack) || - !ecpCreateJ(ec, f, params->a, params->b, stack) || + !ecpCreateJ(ec, f, params->a, params->b, TRUE, stack) || !ecGroupCreate(ec, 0, params->yG, params->q, no, 1, stack)) { blobClose(state); diff --git a/src/crypto/g12s.c b/src/crypto/g12s.c index 3d8bbac7..7f54aeeb 100644 --- a/src/crypto/g12s.c +++ b/src/crypto/g12s.c @@ -630,7 +630,7 @@ static err_t g12sEcCreate( return ERR_BAD_PARAMS; } // создать кривую и группу - if (!ecpCreateJ(ec, f, params->a, params->b, stack) || + if (!ecpCreateJ(ec, f, params->a, params->b, FALSE, stack) || !ecGroupCreate(ec, params->xP, params->yP, params->q, params->l / 8, params->n, stack)) { diff --git a/src/math/ecp/ecp_j.c b/src/math/ecp/ecp_j.c index 4d2b5a1a..7383ba1b 100644 --- a/src/math/ecp/ecp_j.c +++ b/src/math/ecp/ecp_j.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: Jacobian coordinates \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.02.04 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -1013,16 +1013,26 @@ static void ecpSgnAJ(word a[], register word neg, const ec_o* ec, void* stack) ******************************************************************************* Финишное сложение -Использованы полные (complete) формулы сложения / удвоения из работы [RCB15]. +Использованы полные (complete) формулы сложения / удвоения из работы [RCB15], +в свою очередь основанные на формулах работы [BosLen95]. В формулах используются однородные (Homogenious, H) координаты: x = X / Z, y = Y / Z. - Преобразование H <- J: (X Z : Y : Z^3) <- (X : Y : Z). -[RCB15] Renes J., Costello C., Batina L. Complete addition formulas for prime - order elliptic curves. 2015. https://eprint.iacr.org/2015/1060.pdf. +\remark Хотя в названии работы [RCB15] фигурируют слова "prime order", формулы +остаются корректными для групп нечетного порядка. Цитата: + ``...we point that the word "prime" in our title can be relaxed to "odd"; + the completeness of the Bosma–Lenstra formulas only requires the + non-existence of rational two-torsion points (see Sections 2 and 3), i.e., + that the group order #E(Fq) is not even.'' + +[RCB15] Renes J., Costello C., Batina L. Complete addition formulas for + prime order elliptic curves. 2015. + https://eprint.iacr.org/2015/1060.pdf. +[BosLen95] Bosma W., Lenstra H.W. Complete systems of two addition laws + for elliptic curves. Journal of Number theory, 53(2):229–240, 1995. ******************************************************************************* */ @@ -1242,8 +1252,8 @@ static size_t ecpFinAddA_deep(size_t n, size_t f_deep) #define ecpCreateJ_local(n)\ /* t */ O_OF_W(n) -bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], - void* stack) +bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], + bool_t odd_order, void* stack) { register bool_t bA3; word* t; /* [n] */ @@ -1290,8 +1300,12 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], ec->dbladda = ecpDblAddAJ; ec->sgn = ecpSgnJ; ec->sgna = ecpSgnAJ; - ec->finadd = ecpFinAdd; - ec->finadda = ecpFinAddA; + if (odd_order) + { + ec->finadd = ecpFinAdd; + ec->finadda = ecpFinAddA; + } + // для простоты учитываем в том числе глубину ecpFinAdd/ecpFinAddA ec->deep = utilMax(9, ecpToAJ_deep(f->n, f->deep), ecpAddJ_deep(f->n, f->deep), diff --git a/test/math/ec_test.c b/test/math/ec_test.c index d481794d..2d027b3c 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -122,7 +122,7 @@ bool_t ecTestEc(const ec_o* ec) ecMulPreSNZH_deep(n, ec->d, ec->deep, n), ecMulPreHPB_deep(n, ec->d, ec->deep, n), ecAddMulA_deep(n, ec->d, ec->deep, 4, - (size_t)1, (size_t)2, (size_t)3, (size_t)4)), + (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, &pre, &pt0, &pt1, &pt2, &pt3, &pt4, &pt5, &d, &stack); if (state == 0) @@ -583,10 +583,10 @@ bool_t ecTestEc(const ec_o* ec) Проверочная кривая: - p -- простое, p = 3 (mod 4); - a = p - 3; -- q = |E_{a,b}(FF_p)| -- простое число; -- ybase = b^((p + 1) / 4) mod p. +- q, порядок группы точек, -- простое число; +- xbase = 0, ybase = b^((p + 1) / 4) mod p (ybase^2 = b mod p). -\remark Вычисление порядка в PARI/GP: +\remark Вычисление порядка группы в PARI/GP: \code p = 2^256 - 189 a = p - 3 @@ -671,9 +671,9 @@ bool_t ecTest() blobClose(state); return FALSE; } - // создать ec = EC_{ab}(f) + // создать ec = EC_{a,b}(f) hexToRev(t, a), hexToRev(t + no, b); - if (!ecpCreateJ(ec, f, t, t + no, stack) || ec->d != 3) + if (!ecpCreateJ(ec, f, t, t + no, TRUE, stack) || ec->d != 3) { blobClose(state); return FALSE; @@ -707,7 +707,7 @@ bool_t ecTest() objCopy(f, objPtr(ec, 0, qr_o)); // создать ec = EC_{a-2, b}(f) hexToRev(t, a), t[0] -= 2, hexToRev(t + no, b); - if (!ecpCreateJ(ec, f, t, t + no, stack)) + if (!ecpCreateJ(ec, f, t, t + no, FALSE, stack)) { blobClose(state); return FALSE; diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index bee03696..65f4802b 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -214,7 +214,7 @@ bool_t ecpBench() bign_params params[1]; ec_o* ec; // создать кривую - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2") != ERR_OK || bignEcCreate(&ec, params) != ERR_OK) return FALSE; // оценка From 09fada55db91812be64cdccec42900073f0a8d3b Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 12 Feb 2026 19:03:07 +0300 Subject: [PATCH 40/77] Update test/math/ec --- test/math/ec_test.c | 10 +++++++--- test/math/ecp_bench.c | 8 ++++---- test/math/ecp_test.c | 6 +++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/test/math/ec_test.c b/test/math/ec_test.c index 2d027b3c..36b0af1b 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -78,8 +78,8 @@ bool_t ecTestEc(const ec_o* ec) // размерности const size_t n = ec->f->n; const size_t no = ec->f->no; - const size_t min_w = 3; - const size_t max_w = 6; + const size_t min_w = 1; + const size_t max_w = 4; const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); @@ -366,11 +366,15 @@ bool_t ecTestEc(const ec_o* ec) { size_t i; // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - if (!ecPreSNZA(pre, ec->base, w, ec, stack)) + if (!ecPreSNZA(pre, ec->base, w, ec, stack) || + !wwEq(ecPrePtA(pre, 0, ec), ec->base, 2 * n)) { blobClose(state); return FALSE; } + // пропустить проверки при w = 1 + if (w == 1) + continue; // pt0 <- pre[1] - pre[0] ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); if (!ecAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 65f4802b..5ceff75a 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -83,7 +83,7 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)tmSpeed(reps, ticks)); } // ecpPre+ecMulPre[SNZ] - for (w = min_w; w <= max_w; ++w) + for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { @@ -98,7 +98,7 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)tmSpeed(reps, ticks)); } // ecpPre+ecMulPre[SNZA] - for (w = min_w; w <= max_w; ++w) + for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { @@ -186,7 +186,7 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)tmSpeed(reps, ticks)); } // ecpPre[SNZ] - for (w = min_w; w <= max_w; ++w) + for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) ecpPreSNZ(pre, ec->base, w, ec, stack); @@ -214,7 +214,7 @@ bool_t ecpBench() bign_params params[1]; ec_o* ec; // создать кривую - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2") != ERR_OK || + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || bignEcCreate(&ec, params) != ERR_OK) return FALSE; // оценка diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index a31af395..bffa7897 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -27,7 +27,7 @@ static bool_t ecpTestEc(const ec_o* ec) { // размерности const size_t n = ec->f->n; - const size_t min_w = 3; + const size_t min_w = 1; const size_t max_w = 6; const size_t max_pre_count = SIZE_BIT_POS(max_w - 1); // состояние @@ -94,7 +94,7 @@ static bool_t ecpTestEc(const ec_o* ec) } } // предвычисления по схеме SNZ - for (w = min_w; w <= max_w; ++w) + for (w = MAX2(3, min_w); w <= max_w; ++w) { // pre[0..2^w) <- (1, 3, ..., 2^w-1)base ecpPreSNZ(pre, ec->base, w, ec, stack); @@ -121,7 +121,7 @@ static bool_t ecpTestEc(const ec_o* ec) } } // предвычисления по схеме SNZA - for (w = min_w; w <= max_w; ++w) + for (w = MAX2(3, min_w); w <= max_w; ++w) { // pre[0..2^w) <- (1, 3, ..., 2^w-1)base if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) From cf486cddaf7de3c556a88d8ad29daa328c19ee4c Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 13 Feb 2026 16:37:46 +0300 Subject: [PATCH 41/77] Make math/gfp/inv constant-time --- include/bee2/math/zm.h | 3 +- src/math/gfp.c | 88 ++++++++++++++++++++++++++++++++++++++---- src/math/zz/zz_gcd.c | 27 ++++++++----- 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/include/bee2/math/zm.h b/include/bee2/math/zm.h index 3f563a43..88f10f76 100644 --- a/include/bee2/math/zm.h +++ b/include/bee2/math/zm.h @@ -4,7 +4,7 @@ \brief Quotient rings of integers modulo m \project bee2 [cryptographic library] \created 2013.09.14 -\version 2025.09.28 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -20,6 +20,7 @@ #ifndef __ZM_H #include "bee2/math/qr.h" +#include "bee2/math/ww.h" #ifdef __cplusplus extern "C" { diff --git a/src/math/gfp.c b/src/math/gfp.c index 5960ea5a..63933b05 100644 --- a/src/math/gfp.c +++ b/src/math/gfp.c @@ -4,7 +4,7 @@ \brief Prime fields \project bee2 [cryptographic library] \created 2012.07.11 -\version 2025.04.25 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -15,6 +15,70 @@ #include "bee2/math/gfp.h" #include "bee2/math/pri.h" #include "bee2/math/zm.h" +#include "bee2/math/zz.h" + +/* +******************************************************************************* +Обращение и деление + +Обращение выполняется по малой теореме Ферма: + a^{-1} <- a^{p-2} mod p. +Достигается регулярность по обращаемому элементу. +******************************************************************************* +*/ + +#define gfpInv_local(n)\ +/* t */ O_OF_W(n) + +static void gfpInv(word b[], const word a[], const qr_o* r, void* stack) +{ + word* t; /* [n] */ + ASSERT(gfpIsOperable(r)); + ASSERT(zmIsIn(a, r)); + memSlice(stack, + gfpInv_local(r->n), SIZE_0, SIZE_MAX, + &t, &stack); + wwCopy(t, r->mod, r->n); + zzSubW2(t, r->n, 2); + qrPower(b, a, t, r->n, r, stack); +} + +size_t gfpInv_deep(size_t n, size_t r_deep) +{ + return memSliceSize( + gfpInv_local(n), + qrPower_deep(n, n, r_deep), + SIZE_MAX); +} + +#define gfpDiv_local(n)\ +/* t */ O_OF_W(n) + +static void gfpDiv(word c[], const word a[], const word b[], const qr_o* r, + void* stack) +{ + word* t; /* [n] */ + ASSERT(gfpIsOperable(r)); + ASSERT(zmIsIn(a, r)); + ASSERT(zmIsIn(b, r)); + memSlice(stack, + gfpDiv_local(r->n), SIZE_0, SIZE_MAX, + &t, &stack); + wwCopy(t, r->mod, r->n); + zzSubW2(t, r->n, 2); + qrPower(t, b, t, r->n, r, stack); + qrMul(c, a, t, r, stack); +} + +size_t gfpDiv_deep(size_t n, size_t r_deep) +{ + return memSliceSize( + gfpDiv_local(n), + utilMax(2, + r_deep, + qrPower_deep(n, n, r_deep)), + SIZE_MAX); +} /* ******************************************************************************* @@ -23,17 +87,20 @@ \todo Поддержать функции редукции для простых Солинаса из NIST: P192, P224, P256, P384, P521. -\todo Реализовать алгоритм обращения a^{-1} mod p, p -- простое, предложенный в -[J.J. Thomas, J.M.Keller, G.N.Larsen. The calculation of multiplicative -inverses over GF(p) efficiently where p is a Mersenne prime, -IEEE Trans. on Computers 35 No5 (1986), 478–482] и цитируемый в соответствии -с [Algorithm 11.9 Prime field inversion, CohenFrey, p. 207]: +\todo Реализовать алгоритм обращения a^{-1} mod p, p -- простое, предложенный +в [TKL86] и цитируемый в [Doc05; Algorithm 11.9 -- prime field inversion]: t <- a mod p, b <- 1 while t != 1 q <- - (p div t) t <- p + q t b <- (q b) mod p return b + +[TKL86] Thomas J.J., Keller J.M., Larsen G.N. The calculation of multiplicative + inverses over GF(p) efficiently where p is a Mersenne prime, + IEEE Trans. on Computers 35 No5 (1986), 478–482. +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography, Chapman & Hall/CRC, 2005. ******************************************************************************* */ @@ -47,6 +114,11 @@ bool_t gfpCreate(qr_o* r, const octet p[], size_t no, void* stack) return FALSE; // создать GF(p) как ZZ / (p) zmCreate(r, p, no, stack); + // перегрузить функции обращения и деления + r->inv = gfpInv, r->div = gfpDiv; + r->deep = MAX3(r->deep, gfpInv_deep(r->n, r->deep), + gfpDiv_deep(r->n, r->deep)); + // все хорошо return TRUE; } @@ -57,7 +129,9 @@ size_t gfpCreate_keep(size_t no) size_t gfpCreate_deep(size_t no) { - return zmCreate_deep(no); + const size_t r_deep = zmCreate_deep(no); + return MAX3(r_deep, gfpInv_deep(W_OF_O(no), r_deep), + gfpDiv_deep(W_OF_O(no), r_deep)); } bool_t gfpIsOperable(const qr_o* f) diff --git a/src/math/zz/zz_gcd.c b/src/math/zz/zz_gcd.c index e068d1a6..7c3efccf 100644 --- a/src/math/zz/zz_gcd.c +++ b/src/math/zz/zz_gcd.c @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers: Euclidian gcd algorithms \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.22 +\version 2026.02.12 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -73,7 +73,15 @@ 1) бинарный алгоритм опережает обычный (полные деления) примерно в 2 раза и 2) опережает смешанный (полные деления и деления на 2) примерно в 1.5 раза. -\todo Регуляризация? +\todo Регуляризация: [BerYan19], [Por20]. + +[BerYan19] Bernstein D., Yang B.-Y. Fast constant-time gcd computation and + modular inversion. IACR Transactions on Cryptographic Hardware and + Embedded Systems, 2019(3), 340-398. + https://eprint.iacr.org/2019/266. +[Por20] Pornin T. Optimized Binary GCD for Modular Inversion. Cryptology + ePrint Archive, 2020, paper 2020/972. + https://eprint.iacr.org/2020/972. ******************************************************************************* */ @@ -447,9 +455,7 @@ size_t zzDivMod_deep(size_t n) ******************************************************************************* Почти обращение по модулю -В zzAlmostDivMod() реализован алгоритм Калиски [B.S.Kaliski Jr. The Montgomery -inverse and its applications. IEEE Transactions on Computers, 44(8):1064–1065, -1995]: +В zzAlmostDivMod() реализован алгоритм Калиски [Kal95]: u <- a da0 <- 1 v <- mod @@ -480,16 +486,19 @@ inverse and its applications. IEEE Transactions on Computers, 44(8):1064–1065, - числа da, da0 лежат в интервале [0, 2 * mod - 1]; - wwBitSize(mod) <= k <= 2 * wwBitSize(mod). -\remark В [E. Savas, K. Koc. The Montgomery Modular Inverse -- Revisited. -IEEE Transactions on Computers, 49(7):763–766, 2000] рассмотрен случай, когда -a и mod < 2^m, причем условие a < mod может нарушаться. Доказано, что в этом -случае +\remark В [SavKoc00] рассмотрен случай, когда a и mod < 2^m, причем условие +a < mod может нарушаться. Доказано, что в этом случае: - числа da, da0 лежат в интервале [0, 2 * mod - 1]; - wwBitSize(mod) <= k <= m + wwBitSize(mod). \todo В перечисленных статьях предполагается, что mod -- простое число. Проверить, что результаты можно распространить на случай произвольного нечетного mod. Можно ли сузить интервал [0, 2 * mod - 1]? + +[Kal95] Kaliski B.S., Jr. The Montgomery inverse and its applications. + IEEE Transactions on Computers, 44(8):1064–1065, 1995. +[SavKoc00] Savas E., Koc K. The Montgomery Modular Inverse -- Revisited. + IEEE Transactions on Computers, 49(7):763–766, 2000. ******************************************************************************* */ From 880a743f06d8ce31fe27d80d6b42b5b6ef3ddb50 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 13 Feb 2026 18:07:11 +0300 Subject: [PATCH 42/77] Make Bign algorithms constant-time --- src/crypto/bign/bign_ibs.c | 8 ++-- src/crypto/bign/bign_keyt.c | 15 +++--- src/crypto/bign/bign_lcl.h | 81 ++++++++++++++++++++++--------- src/crypto/bign/bign_misc.c | 95 +++++++++++++++++++++++++++++++++---- src/crypto/bign/bign_sign.c | 10 ++-- src/math/ec2.c | 7 ++- src/math/ecp/ecp_j.c | 7 ++- 7 files changed, 167 insertions(+), 56 deletions(-) diff --git a/src/crypto/bign/bign_ibs.c b/src/crypto/bign/bign_ibs.c index 6c99750e..887da0f5 100644 --- a/src/crypto/bign/bign_ibs.c +++ b/src/crypto/bign/bign_ibs.c @@ -173,7 +173,7 @@ err_t bignIdSignEc(octet id_sig[], const ec_o* ec, const octet oid_der[], O_OF_W(n / 2), utilMax(4, beltHash_keep(), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n)), SIZE_MAX, @@ -194,7 +194,7 @@ err_t bignIdSignEc(octet id_sig[], const ec_o* ec, const octet oid_der[], return ERR_BAD_RNG; } // V <- k G - if (!ecMulA(V, ec->base, ec, k, n, stack)) + if (!bignMulBase(V, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -284,7 +284,7 @@ err_t bignIdSign2Ec(octet id_sig[], const ec_o* ec, const octet oid_der[], beltHash_keep(), 32, beltWBL_keep(), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n)), SIZE_MAX, @@ -324,7 +324,7 @@ err_t bignIdSign2Ec(octet id_sig[], const ec_o* ec, const octet oid_der[], } } // V <- k G - if (!ecMulA(V, ec->base, ec, k, n, stack)) + if (!bignMulBase(V, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; diff --git a/src/crypto/bign/bign_keyt.c b/src/crypto/bign/bign_keyt.c index 73ec256b..791a42a8 100644 --- a/src/crypto/bign/bign_keyt.c +++ b/src/crypto/bign/bign_keyt.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): key transport \project bee2 [cryptographic library] \created 2012.04.27 -\version 2025.10.23 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -56,8 +56,9 @@ err_t bignKeyWrapEc(octet token[], const ec_o* ec, const octet key[], O_OF_W(n), O_OF_W(2 * n), (size_t)32, - utilMax(2, - ecMulA_deep(n, ec->d, ec->deep, n), + utilMax(3, + bignMulA_deep(n, ec->f->deep, ec->deep), + bignMulBase_deep(n, ec->f->deep, ec->deep), beltKWP_keep()), SIZE_MAX, &k, &R, &theta, &stack); @@ -76,7 +77,7 @@ err_t bignKeyWrapEc(octet token[], const ec_o* ec, const octet key[], blobClose(state); return ERR_BAD_PUBKEY; } - if (!ecMulA(R, R, ec, k, n, stack)) + if (!bignMulA(R, R, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -84,7 +85,7 @@ err_t bignKeyWrapEc(octet token[], const ec_o* ec, const octet key[], // theta <- _{256} qrTo(theta, ecX(R), ec->f, stack); // R <- k G - if (!ecMulA(R, ec->base, ec, k, n, stack)) + if (!bignMulBase(R, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -162,7 +163,7 @@ err_t bignKeyUnwrapEc(octet key[], const ec_o* ec, const octet token[], utilMax(3, beltKWP_keep(), qrPower_deep(n, n, ec->f->deep), - ecMulA_deep(n, ec->d, ec->deep, n)), + bignMulA_deep(n, ec->f->deep, ec->deep)), SIZE_MAX, &d, &R, &t1, &t2, &theta, &header2, &stack); if (state == 0) @@ -199,7 +200,7 @@ err_t bignKeyUnwrapEc(octet key[], const ec_o* ec, const octet token[], return ERR_BAD_KEYTOKEN; } // R <- d R - if (!ecMulA(R, R, ec, d, n, stack)) + if (!bignMulA(R, R, ec, d, stack)) { blobClose(state); return ERR_BAD_PARAMS; diff --git a/src/crypto/bign/bign_lcl.h b/src/crypto/bign/bign_lcl.h index 39d079a2..0a50e1b8 100644 --- a/src/crypto/bign/bign_lcl.h +++ b/src/crypto/bign/bign_lcl.h @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): local declarations \project bee2 [cryptographic library] \created 2014.04.03 -\version 2026.02.12 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -108,6 +108,63 @@ void bignEcClose( ec_o* ec /*!< [in] эллиптическая кривая */ ); +/* +******************************************************************************* +Кратная точка + +Регулярные функции вычисления кратной точки. +******************************************************************************* +*/ + +/*! \brief Кратная точка + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [ec->f->n]d-кратной аффинной точки [2 * ec->f->n]a: + \code + b <- d a. + \endcode + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \expect Кривая ec удовлетворяет соглашениям Bign. + \expect В ec используются якобиевы координаты. + \expect Точка a лежит на ec. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} bignMulA_deep(ec->f->n, ec->d, ec->deep). +*/ +bool_t bignMulA( + word b[], /*!< [out] кратная точка */ + const word a[], /*!< [in] базовая точка */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t bignMulA_deep(size_t n, size_t ec_d, size_t ec_deep); + +/*! \brief Кратная базовая точка + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [ec->f->n]d-кратной точки ec->base: + \code + a <- d (ec->base). + \endcode + \pre Описание ec работоспособно. + \expect Кривая ec удовлетворяет соглашениям Bign. + \expect В ec используются якобиевы координаты. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} bignMulBase_deep(ec->f->n, ec->d, ec->deep). +*/ +bool_t bignMulBase( + word a[], /*!< [out] кратная точка */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t bignMulBase_deep(size_t n, size_t ec_d, size_t ec_deep); + /* ******************************************************************************* ЕС-функции @@ -167,28 +224,6 @@ err_t bignIdVerifyEc(const ec_o* ec, const octet oid_der[], size_t oid_len, const octet id_hash[], const octet hash[], const octet id_sig[], const octet id_pubkey[], const octet pubkey[]); -/* -******************************************************************************* -К удалению -******************************************************************************* -*/ - -/*! \brief Потребности в стеке - - Определяется глубина стека, который требуется высокоуровневой функции - для работы с эллиптической кривой, описываемой объясненными ниже - размерностями n, f_deep, ec_d, ec_deep. - \remark При расчете глубины не следует учитывать память для - размещения описаний базового поля и эллиптической кривой. -*/ - -typedef size_t(*bign_deep_i)( - size_t n, /*!< [in] число слов для хранения элемента поля */ - size_t f_deep, /*!< [in] глубина стека базового поля */ - size_t ec_d, /*!< [in] число проективных координат */ - size_t ec_deep /*!< [in] глубина стека эллиптической кривой */ - ); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index ca0dfe6b..7387aa0e 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2025.09.26 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -22,6 +22,83 @@ #include "bee2/math/zz.h" #include "bign_lcl.h" +/* +******************************************************************************* +Кратная точка +******************************************************************************* +*/ + +static size_t bignMulAWidth(size_t l) +{ + return l <= 128 ? 5 : 6; +} + +#define bignMulA_local(n, pre_count)\ +/* pre */ sizeof(ec_pre_t) + O_OF_W(pre_count * 3 * n) + +bool_t bignMulA(word b[], const word a[], const ec_o* ec, const word d[], + void* stack) +{ + size_t w; + size_t pre_count; + ec_pre_t* pre; /* [pre_count проективных точек] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + // размерности + w = bignMulAWidth(B_OF_W(ec->f->n)); + pre_count = SIZE_BIT_POS(w - 1); + // разметить стек + memSlice(stack, + bignMulA_local(ec->f->n, pre_count), SIZE_0, SIZE_MAX, + &pre, &stack); + // метод SNZ + ecpPreSNZ(pre, a, w, ec, stack); + return ecMulPreSNZ(b, pre, ec, d, ec->f->n, stack); +} + +size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) +{ + const size_t w = bignMulAWidth(B_OF_W(n)); + const size_t pre_count = SIZE_BIT_POS(w - 1); + return memSliceSize( + bignMulA_local(n, pre_count), + utilMax(2, + ecpPreSNZ_deep(n, f_deep, w), + ecMulPreSNZ_deep(n, 3, ec_deep, n)), + SIZE_MAX); +} + +bool_t bignMulBase(word a[], const ec_o* ec, const word d[], void* stack) +{ + ASSERT(ecIsOperable(ec) && ec->d == 3); + if (ec->pre) + { + ASSERT(ecPreIsOperable(ec->pre)); + switch (ec->pre->type) + { + case ec_pre_snzh: + return ecMulPreSNZH(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_hpb: + return ecMulPreHPB(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_snza: + return ecMulPreSNZA(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_snz: + return ecMulPreSNZ(a, ec->pre, ec, d, ec->f->n, stack); + } + } + return bignMulA(a, ec->base, ec, d, stack); +} + +size_t bignMulBase_deep(size_t n, size_t f_deep, size_t ec_deep) +{ + return utilMax(4, + bignMulA_deep(n, f_deep, ec_deep), + ecMulPreSNZH_deep(n, 3, ec_deep, n), + ecMulPreHPB_deep(n, 3, ec_deep, n), + ecMulPreSNZA_deep(n, 3, ec_deep, n), + ecMulPreSNZ_deep(n, 3, ec_deep, n)); +} + /* ******************************************************************************* Идентификатор объекта @@ -76,7 +153,7 @@ err_t bignKeypairGenEc(octet privkey[], octet pubkey[], const ec_o* ec, state = blobCreate2( O_OF_W(n), O_OF_W(2 * n), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), SIZE_MAX, &d, &Q, &stack); if (state == 0) @@ -88,7 +165,7 @@ err_t bignKeypairGenEc(octet privkey[], octet pubkey[], const ec_o* ec, return ERR_BAD_RNG; } // Q <- d G - if (!ecMulA(Q, ec->base, ec, d, n, stack)) + if (!bignMulBase(Q, ec, d, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -141,7 +218,7 @@ err_t bignKeypairValEc(const ec_o* ec, const octet privkey[], state = blobCreate2( O_OF_W(n), O_OF_W(2 * n), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), SIZE_MAX, &d, &Q, &stack); if (state == 0) @@ -155,7 +232,7 @@ err_t bignKeypairValEc(const ec_o* ec, const octet privkey[], return ERR_BAD_PRIVKEY; } // Q <- d G - if (!ecMulA(Q, ec->base, ec, d, n, stack)) + if (!bignMulBase(Q, ec, d, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -264,7 +341,7 @@ err_t bignPubkeyCalcEc(octet pubkey[], const ec_o* ec, const octet privkey[]) state = blobCreate2( O_OF_W(n), O_OF_W(2 * n), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), SIZE_MAX, &d, &Q, &stack); if (state == 0) @@ -277,7 +354,7 @@ err_t bignPubkeyCalcEc(octet pubkey[], const ec_o* ec, const octet privkey[]) return ERR_BAD_PRIVKEY; } // Q <- d G - if (!ecMulA(Q, ec->base, ec, d, n, stack)) + if (!bignMulBase(Q, ec, d, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -336,7 +413,7 @@ err_t bignDHEc(octet key[], const ec_o* ec, const octet privkey[], O_OF_W(2 * n), utilMax(2, ecpIsOnA_deep(n, ec->f->deep), - ecMulA_deep(n, ec->d, ec->deep, n)), + bignMulA_deep(n, ec->f->deep, ec->deep)), SIZE_MAX, &u, &K, &V, &stack); if (state == 0) @@ -357,7 +434,7 @@ err_t bignDHEc(octet key[], const ec_o* ec, const octet privkey[], return ERR_BAD_PUBKEY; } // V <- u V - if (!ecMulA(V, V, ec, u, n, stack)) + if (!bignMulA(V, V, ec, u, stack)) { blobClose(state); return ERR_BAD_PARAMS; diff --git a/src/crypto/bign/bign_sign.c b/src/crypto/bign/bign_sign.c index b83e7b8c..53d6b503 100644 --- a/src/crypto/bign/bign_sign.c +++ b/src/crypto/bign/bign_sign.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): digital signature \project bee2 [cryptographic library] \created 2012.04.27 -\version 2025.10.22 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -65,7 +65,7 @@ err_t bignSignEc(octet sig[], const ec_o* ec, const octet oid_der[], O_OF_W(n / 2), utilMax(4, beltHash_keep(), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n)), SIZE_MAX, @@ -86,7 +86,7 @@ err_t bignSignEc(octet sig[], const ec_o* ec, const octet oid_der[], return ERR_BAD_RNG; } // R <- k G - if (!ecMulA(R, ec->base, ec, k, n, stack)) + if (!bignMulBase(R, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -177,7 +177,7 @@ err_t bignSign2Ec(octet sig[], const ec_o* ec, const octet oid_der[], beltHash_keep(), (size_t)32, beltWBL_keep(), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->f->deep, ec->deep), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n)), SIZE_MAX, @@ -217,7 +217,7 @@ err_t bignSign2Ec(octet sig[], const ec_o* ec, const octet oid_der[], } } // R <- k G - if (!ecMulA(R, ec->base, ec, k, n, stack)) + if (!bignMulBase(R, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; diff --git a/src/math/ec2.c b/src/math/ec2.c index 77a217d2..e493a970 100644 --- a/src/math/ec2.c +++ b/src/math/ec2.c @@ -4,7 +4,7 @@ \brief Elliptic curves over binary fields \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.02.03 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -554,8 +554,7 @@ static size_t ec2AddALD_deep(size_t n, size_t f_deep) /* A */ O_OF_W(n),\ /* B */ O_OF_W(n),\ /* base */ O_OF_W(2 * n),\ -/* order */ O_OF_W(n + 1),\ -/* pre */ SIZE_0 +/* order */ O_OF_W(n + 1) bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[], void* stack) @@ -573,7 +572,7 @@ bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[], // разметить состояние memSlice(ec->descr, ec2CreateLD_state(f->n), SIZE_MAX, - &ec->A, &ec->B, &ec->base, &ec->order, &ec->pre); + &ec->A, &ec->B, &ec->base, &ec->order); // сохранить коэффициенты if (!qrFrom(ec->A, A, ec->f, stack) || !qrFrom(ec->B, B, ec->f, stack)) return FALSE; diff --git a/src/math/ecp/ecp_j.c b/src/math/ecp/ecp_j.c index 7383ba1b..d62e1c21 100644 --- a/src/math/ecp/ecp_j.c +++ b/src/math/ecp/ecp_j.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: Jacobian coordinates \project bee2 [cryptographic library] \created 2012.06.26 -\version 2026.02.12 +\version 2026.02.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -1246,8 +1246,7 @@ static size_t ecpFinAddA_deep(size_t n, size_t f_deep) /* A */ O_OF_W(n),\ /* B */ O_OF_W(n),\ /* base */ O_OF_W(2 * n),\ -/* order */ O_OF_W(n + 1),\ -/* pre */ SIZE_0 +/* order */ O_OF_W(n + 1) #define ecpCreateJ_local(n)\ /* t */ O_OF_W(n) @@ -1274,7 +1273,7 @@ bool_t ecpCreateJ(ec_o* ec, const qr_o* f, const octet A[], const octet B[], // разметить состояние и стек memSlice(ec->descr, ecpCreateJ_state(f->n), SIZE_MAX, - &ec->A, &ec->B, &ec->base, &ec->order, &ec->pre); + &ec->A, &ec->B, &ec->base, &ec->order); memSlice(stack, ecpCreateJ_local(f->n), SIZE_0, SIZE_MAX, &t, &stack); From 4c31c19e73d7c7ae630fc9ded55c35507764f7e4 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 16 Feb 2026 18:56:24 +0300 Subject: [PATCH 43/77] Rename math/ec/pre schemas --- include/bee2/core/word.h | 4 +- include/bee2/math/ec.h | 135 ++++++++++++------------- include/bee2/math/ecp.h | 22 ++-- src/core/u32.c | 4 +- src/crypto/bign/bign_misc.c | 36 +++---- src/math/ec.c | 194 +++++++++++++++++++----------------- src/math/ecp/ecp_pre.c | 32 +++--- test/math/ec_test.c | 62 ++++++------ test/math/ecp_bench.c | 84 ++++++++-------- test/math/ecp_test.c | 12 +-- 10 files changed, 295 insertions(+), 290 deletions(-) diff --git a/include/bee2/core/word.h b/include/bee2/core/word.h index 80a73364..3a0c18cb 100644 --- a/include/bee2/core/word.h +++ b/include/bee2/core/word.h @@ -4,7 +4,7 @@ \brief Machine words \project bee2 [cryptographic library] \created 2014.07.18 -\version 2025.10.09 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -122,7 +122,7 @@ extern "C" { можно использовать как маски при организации регулярных вычислений. [War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, - М.: Издательский дом <<Вильямс>>, 2003. + М.: Издательский дом "Вильямс", 2003. \def wordEq \brief Машинные слова a и b равны? diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 7407a54d..9ba4e860 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.11 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -483,10 +483,10 @@ typedef bool_t(*ec_finadda_i)( */ typedef enum { - ec_pre_snz = 1, /*!< схема SNZ */ - ec_pre_snza, /*!< схема SNZA */ - ec_pre_snzh, /*!< схема SNZH */ - ec_pre_hpb, /*!< схема HPB */ + ec_pre_so = 1, /*!< схема SO */ + ec_pre_soa, /*!< схема SOA */ + ec_pre_soh, /*!< схема SOH */ + ec_pre_si, /*!< схема SI */ } ec_pre_type; /*! \brief Предвычисленные точки @@ -495,15 +495,15 @@ typedef enum определенной точке a в соответствии с определенной схемой предвычислений. Число предвычисленных точек определяется шириной окна w и обычно равняется 2^{w-1}. Поддерживаются следующие схемы: - - SNZ (Signed Non-Zero): вычисляется проективные точки + - SO (Small Odd): вычисляется проективные точки \code a, 3a, ..., (2^w-1)a; \endcode - - SNZA: вычисляются аффинные точки + - SOA: вычисляются аффинные точки \code a, 3a, ..., (2^w-1)a; \endcode - - SNZH: вычисляются аффинные точки + - SOH: вычисляются аффинные точки \code 2^{0} (a, 3a, ..., (2^w-1)a), 2^{w} (a, 3a, ..., (2^w-1)a), @@ -511,7 +511,7 @@ typedef enum ... 2^{(h-1)w} (a, 3a, ..., (2^w-1)a); \endcode - - HPB: вычисляются аффинные точки + - SI (Signed Interleaved): вычисляются аффинные точки \code [c_{w-1}... c_1 c_0]a = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a, \endcode @@ -520,18 +520,7 @@ typedef enum c_{w-1}... c_1 c_0. . \pre 0 < w && w < B_PER_W. - \pre h > 0 <=> type \in { ec_pre_snzh, ec_pre_hpb }. - \remark Схему SNZ предложено в использовать в работе [OkeTak03], - схему HPB -- в работе [HPB05]. - . - [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory - and fast elliptic scalar multiplications secure against side - channel attacks. In Cryptographers’ Track at the RSA Conference, - 2003, pp. 328-343. Springer, Berlin Heidelberg. - [HPB05] Hedabou M., Pinel P., Beneteau L. Countermeasures for preventing - comb method against SCA attacks. In: International Conference - on Information Security Practice and Experience, 2005, pp. 85-96, - Springer, Berlin Heidelberg. + \pre h > 0 <=> type \in { ec_pre_soh, ec_pre_si }. */ typedef struct ec_pre_t { @@ -765,7 +754,7 @@ bool_t ecGroupIsOperable( - буфер [sizeof(ec_pre_t)]pre корректен; - pre->type \in ec_pre_type; - pre->w > 0; - - pre->h > 0 <=> pre->type \in { ec_pre_snzh, ec_pre_hpb }. + - pre->h > 0 <=> pre->type \in { ec_pre_soh, ec_pre_si }. . \return Признак работоспособности. */ @@ -773,9 +762,9 @@ bool_t ecPreIsOperable( const ec_pre_t* pre /*!< [in] предвычисленные точки */ ); -/*! \brief Предвычисления по схеме SNZ +/*! \brief Предвычисления по схеме SO - На эллиптической кривой ec выполняются предвычисления по схеме SNZ + На эллиптической кривой ec выполняются предвычисления по схеме SO с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -785,11 +774,11 @@ bool_t ecPreIsOperable( O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. - \deep{stack} ecPreSNZ_deep(ec->f->n, ec->d, ec->deep). - \remark Ускоренной редакцией ecPreSNZ() для кривых над GF(p) является - функция ecpPreSNZ(). + \deep{stack} ecPreSO_deep(ec->f->n, ec->d, ec->deep). + \remark Ускоренной редакцией ecPreSO() для кривых над GF(p) является + функция ecpPreSO(). */ -void ecPreSNZ( +void ecPreSO( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -797,11 +786,11 @@ void ecPreSNZ( void* stack /*!< [in] вспомогательная память */ ); -size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep); +size_t ecPreSO_deep(size_t n, size_t ec_d, size_t ec_deep); -/*! \brief Предвычисления по схеме SNZA +/*! \brief Предвычисления по схеме SOA - На эллиптической кривой ec выполняются предвычисления по схеме SNZA + На эллиптической кривой ec выполняются предвычисления по схеме SOA с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -815,11 +804,11 @@ size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep); случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. - \deep{stack} ecPreSNZA_deep(ec->f->n, ec->d, ec->deep). - \remark Ускоренной редакцией ecPreSNZA() для кривых над GF(p) является - функция ecpPreSNZA(). + \deep{stack} ecPreSOA_deep(ec->f->n, ec->d, ec->deep). + \remark Ускоренной редакцией ecPreSOA() для кривых над GF(p) является + функция ecpPreSOA(). */ -bool_t ecPreSNZA( +bool_t ecPreSOA( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -827,11 +816,11 @@ bool_t ecPreSNZA( void* stack /*!< [in] вспомогательная память */ ); -size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep); +size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); -/*! \brief Предвычисления по схеме SNZH +/*! \brief Предвычисления по схеме SOH - На эллиптической кривой ec выполняются предвычисления по схеме SNZH + На эллиптической кривой ec выполняются предвычисления по схеме SOH с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -845,9 +834,9 @@ size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep); случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. - \deep{stack} ecPreSNZH_deep(ec->f->n, ec->d, ec->deep). + \deep{stack} ecPreSOH_deep(ec->f->n, ec->d, ec->deep). */ -bool_t ecPreSNZH( +bool_t ecPreSOH( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -856,11 +845,11 @@ bool_t ecPreSNZH( void* stack /*!< [in] вспомогательная память */ ); -size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep); +size_t ecPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep); -/*! \brief Предвычисления по схеме HPB +/*! \brief Предвычисления по схеме SI - На эллиптической кривой ec выполняются предвычисления по схеме HPB + На эллиптической кривой ec выполняются предвычисления по схеме SI с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -874,9 +863,9 @@ size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep); случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. - \deep{stack} ecPreHPB_deep(ec->f->n, ec->d, ec->deep, h). + \deep{stack} ecPreSI_deep(ec->f->n, ec->d, ec->deep, h). */ -bool_t ecPreHPB( +bool_t ecPreSI( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -885,7 +874,7 @@ bool_t ecPreHPB( void* stack /*!< [in] вспомогательная память */ ); -size_t ecPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h); +size_t ecPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h); /* ******************************************************************************* @@ -920,18 +909,18 @@ bool_t ecMulA( size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SNZ +/*! \brief Кратная точка с предвычислениями по схеме SO Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SNZ точками: + с предвычисленными по схеме SO точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_snz. + \pre pre->type == ec_pre_so. \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. @@ -940,7 +929,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreSNZ_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreSO_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без условных переходов; @@ -953,7 +942,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); b) ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение выполняются по одним и тем же формулам без условных переходов. */ -bool_t ecMulPreSNZ( +bool_t ecMulPreSO( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -962,20 +951,20 @@ bool_t ecMulPreSNZ( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SNZA +/*! \brief Кратная точка с предвычислениями по схеме SOA Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SNZA точками: + с предвычисленными по схеме SOA точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_snza. + \pre pre->type == ec_pre_soa. \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. @@ -984,7 +973,7 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreSNZA_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreSOA_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без условных переходов; @@ -998,9 +987,10 @@ size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); - справедливо, по крайней мере, одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и - удвоение выполняются по одним и тем же формулам без условных переходов. + удвоение выполняются по одним и тем же формулам без условных + переходов. */ -bool_t ecMulPreSNZA( +bool_t ecMulPreSOA( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -1009,20 +999,20 @@ bool_t ecMulPreSNZA( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SNZH +/*! \brief Кратная точка с предвычислениями по схеме SOH Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SNZH точками: + с предвычисленными по схеме SOH точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_snzh. + \pre pre->type == ec_pre_soh. \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. @@ -1032,7 +1022,7 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreSNZH_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreSOH_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - если ec->dbladda == 0, то функция ec->adda регулярна: сложение a + b, a != \pm b, a != O, b != O, выполняется без условных переходов; @@ -1044,9 +1034,10 @@ size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); - справедливо, по крайней мере, одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и - удвоение выполняются по одним и тем же формулам без условных переходов. + удвоение выполняются по одним и тем же формулам без условных + переходов. */ -bool_t ecMulPreSNZH( +bool_t ecMulPreSOH( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -1055,20 +1046,20 @@ bool_t ecMulPreSNZH( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SNZA +/*! \brief Кратная точка с предвычислениями по схеме SOA Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме HPB точками: + с предвычисленными по схеме SI точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_hpb. + \pre pre->type == ec_pre_si. \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. @@ -1079,7 +1070,7 @@ size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreHPB_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreSI_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без условных переходов; @@ -1094,7 +1085,7 @@ size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); выполняются по одним и тем же формулам без условных переходов; - pre->w * pre->h == wwBitSize(ec->order, m). */ -bool_t ecMulPreHPB( +bool_t ecMulPreSI( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -1103,7 +1094,7 @@ bool_t ecMulPreHPB( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); /*! \brief Имеет порядок? diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 0c08b8f9..d6f4a066 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.02.07 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -270,9 +270,9 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); ******************************************************************************* */ -/*! \brief Предвычисления по схеме SNZ +/*! \brief Предвычисления по схеме SO - На эллиптической кривой ec выполняются предвычисления по схеме SNZ + На эллиптической кривой ec выполняются предвычисления по схеме SO с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -282,9 +282,9 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. - \deep{stack} ecpPreSNZ_deep(ec->f->n, ec->d, ec->deep, w). + \deep{stack} ecpPreSO_deep(ec->f->n, ec->d, ec->deep, w). */ -void ecpPreSNZ( +void ecpPreSO( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -292,11 +292,11 @@ void ecpPreSNZ( void* stack /*!< [in] вспомогательная память */ ); -size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w); +size_t ecpPreSO_deep(size_t n, size_t f_deep, size_t w); -/*! \brief Предвычисления по схеме SNZA +/*! \brief Предвычисления по схеме SOA - На эллиптической кривой ec выполняются предвычисления по схеме SNZA + На эллиптической кривой ec выполняются предвычисления по схеме SOA с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -310,9 +310,9 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w); случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. - \deep{stack} ecPreSNZA_deep(ec->f->n, ec->d, ec->deep). + \deep{stack} ecPreSOA_deep(ec->f->n, ec->d, ec->deep). */ -bool_t ecpPreSNZA( +bool_t ecpPreSOA( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -320,7 +320,7 @@ bool_t ecpPreSNZA( void* stack /*!< [in] вспомогательная память */ ); -size_t ecpPreSNZA_deep(size_t n, size_t f_deep, size_t w); +size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/core/u32.c b/src/core/u32.c index 94e2ebe9..d216c9fa 100644 --- a/src/core/u32.c +++ b/src/core/u32.c @@ -4,7 +4,7 @@ \brief 32-bit unsigned words \project bee2 [cryptographic library] \created 2015.10.28 -\version 2026.01.20 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -18,7 +18,7 @@ ******************************************************************************* Использованы алгоритмы из следующих источников: [War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, - М.: Издательский дом <<Вильямс>>, 2003. + М.: Издательский дом "Вильямс", 2003. [And05] Andersen S.A. Bit Twidding Hacks, 1997-2005. http://graphics.stanford.edu/~seander/bithacks.html. diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index 7387aa0e..f59143cb 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.02.13 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -51,9 +51,9 @@ bool_t bignMulA(word b[], const word a[], const ec_o* ec, const word d[], memSlice(stack, bignMulA_local(ec->f->n, pre_count), SIZE_0, SIZE_MAX, &pre, &stack); - // метод SNZ - ecpPreSNZ(pre, a, w, ec, stack); - return ecMulPreSNZ(b, pre, ec, d, ec->f->n, stack); + // метод SO + ecpPreSO(pre, a, w, ec, stack); + return ecMulPreSO(b, pre, ec, d, ec->f->n, stack); } size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) @@ -63,8 +63,8 @@ size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) return memSliceSize( bignMulA_local(n, pre_count), utilMax(2, - ecpPreSNZ_deep(n, f_deep, w), - ecMulPreSNZ_deep(n, 3, ec_deep, n)), + ecpPreSO_deep(n, f_deep, w), + ecMulPreSO_deep(n, 3, ec_deep, n)), SIZE_MAX); } @@ -76,14 +76,14 @@ bool_t bignMulBase(word a[], const ec_o* ec, const word d[], void* stack) ASSERT(ecPreIsOperable(ec->pre)); switch (ec->pre->type) { - case ec_pre_snzh: - return ecMulPreSNZH(a, ec->pre, ec, d, ec->f->n, stack); - case ec_pre_hpb: - return ecMulPreHPB(a, ec->pre, ec, d, ec->f->n, stack); - case ec_pre_snza: - return ecMulPreSNZA(a, ec->pre, ec, d, ec->f->n, stack); - case ec_pre_snz: - return ecMulPreSNZ(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_soh: + return ecMulPreSOH(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_si: + return ecMulPreSI(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_soa: + return ecMulPreSOA(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_so: + return ecMulPreSO(a, ec->pre, ec, d, ec->f->n, stack); } } return bignMulA(a, ec->base, ec, d, stack); @@ -93,10 +93,10 @@ size_t bignMulBase_deep(size_t n, size_t f_deep, size_t ec_deep) { return utilMax(4, bignMulA_deep(n, f_deep, ec_deep), - ecMulPreSNZH_deep(n, 3, ec_deep, n), - ecMulPreHPB_deep(n, 3, ec_deep, n), - ecMulPreSNZA_deep(n, 3, ec_deep, n), - ecMulPreSNZ_deep(n, 3, ec_deep, n)); + ecMulPreSOH_deep(n, 3, ec_deep, n), + ecMulPreSI_deep(n, 3, ec_deep, n), + ecMulPreSOA_deep(n, 3, ec_deep, n), + ecMulPreSO_deep(n, 3, ec_deep, n)); } /* diff --git a/src/math/ec.c b/src/math/ec.c index 8d9ebfa7..6337d536 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.12 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -100,7 +100,7 @@ bool_t ecGroupIsOperable(const ec_o* ec) ******************************************************************************* Предвычисления -В функции ecPreSNZ() реализован следуюший алгоритм: +В функции ecPreSO() реализован следуюший алгоритм: 1. t <- 2a. // P <- 2A 2. pre[0] <- a. 3. pre[1] <- t + pre[0]. // P <- P + A @@ -110,7 +110,7 @@ bool_t ecGroupIsOperable(const ec_o* ec) Итоговая сложность: 1(P <- 2A) + 1(P <- P + A) + (2^{w-1} - 2)(P <- P + P). -В функции ecPreSNZA() реализован тот тот же алгоритм, только операция P <- 2A +В функции ecPreSOA() реализован тот тот же алгоритм, только операция P <- 2A в нем меняется на A <- 2A, а операции P <- P + A и P <- P + P меняются на A <- A + A. @@ -127,44 +127,41 @@ bool_t ecGroupIsOperable(const ec_o* ec) ----------------------------------------------------- * [или прямых вычислений в аффинных координатах] -В функции ecPreHPB() вычисляются точки - [b_{w-1} ... b_1 b_0]a = \sum_{i=0}^{w-1} b_i 2^{h i} a, -где b_i in {-1, 1}, b_{w-1} == 1. Точка [b_{w-1} ... b_1 b_0]a -размещается в массиве предвычисленных точек по индексу, двоичная запись -которого есть - с_{w-1}...c_1 c_0, -где c_i = 0, если b_i = 1, и c_i = 1, если b_i = -1. - -Слова code = с_{w-1}...c_1 c_0 просматриваются как слова кода Грея: -соседние слова отличаются только в одном бите. Если это бит номер i, -то новая точка получается из [b_{w-1} ... b_1 b_0]a: +В функции ecPreSI() вычисляются точки + [c_{w-1} ... c_1 c_0]a = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a, +где c_i in {0, 1}, c_{w-1} == 1. Точка [c_{w-1}...c_1 c_0]a +размещается в массиве предвычисленных точек по индексу c_{w-1}...c_1 c_0. + +Слова с_{w-1}...c_1 c_0 просматриваются как слова кода Грея: соседние +слова отличаются только в одном бите. Если это бит номер i, то новая точка +получается из [с_{w-1}...с_1 с_0]a: - добавлением точки (2(2^h)^i)a при c_i = 1; - вычитанием точки (2(2^h)^i)a при c_i = 0. -Определение следующего за code слова выполняется следующим образом -(см. [War03; глава 13], нумерация битов справа налево от 0): +Определение следующего за code = с_{w-1}...c_1 c_0 слова выполняется +следующим образом (см. [War03; глава 13], нумерация битов справа налево от 0): 1. Если code содержит четное число единиц, то pos <- 0. Иначе pos <- (позиция первой справа единицы) + 1. 2. Инвертировать бит code номер pos. [War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, - М.: Издательский дом <<Вильямс>>, 2003. + М.: Издательский дом "Вильямс", 2003. ******************************************************************************* */ bool_t ecPreIsOperable(const ec_pre_t* pre) { return memIsValid(pre, sizeof(pre)) && - (pre->type == ec_pre_snz || pre->type == ec_pre_snza || - pre->type == ec_pre_snzh || pre->type == ec_pre_hpb) && + (pre->type == ec_pre_so || pre->type == ec_pre_soa || + pre->type == ec_pre_soh || pre->type == ec_pre_si) && pre->w > 0 && pre->w < B_PER_W && - ((pre->type != ec_pre_snzh && pre->type != ec_pre_hpb) ^ (pre->h > 0)); + ((pre->type != ec_pre_soh && pre->type != ec_pre_si) ^ (pre->h > 0)); } -#define ecPreSNZ_local(n, ec_d)\ +#define ecPreSO_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, +void ecPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ @@ -177,7 +174,7 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecPreSNZ_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecPreSO_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); // pre[0] <- a ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); @@ -193,23 +190,23 @@ void ecPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ecAdd(ecPrePt(pre, i, ec), t, ecPrePt(pre, i - 1, ec), ec, stack); } // заполнить служебные поля - pre->type = ec_pre_snz; + pre->type = ec_pre_so; pre->w = w, pre->h = 0; } -size_t ecPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep) +size_t ecPreSO_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecPreSNZ_local(n, ec_d), + ecPreSO_local(n, ec_d), ec_deep, SIZE_MAX); } -#define ecPreSNZA_local(n, ec_d)\ +#define ecPreSOA_local(n, ec_d)\ /* t1 */ O_OF_W(ec_d * n),\ /* t2 */ O_OF_W(ec_d * n) -bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, +bool_t ecPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t1; /* [ec->d * ec->f->n] */ @@ -223,7 +220,7 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecPreSNZA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecPreSOA_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t1, &t2, &stack); // pre[0] <- a wwCopy(ecPrePtA(pre, 0, ec), a, 2 * ec->f->n); @@ -241,23 +238,23 @@ bool_t ecPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, } } // заполнить служебные поля - pre->type = ec_pre_snza; + pre->type = ec_pre_soa; pre->w = w, pre->h = 0; return TRUE; } -size_t ecPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep) +size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecPreSNZA_local(n, ec_d), + ecPreSOA_local(n, ec_d), ec_deep, SIZE_MAX); } -#define ecPreSNZH_local(n, ec_d)\ +#define ecPreSOH_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, +bool_t ecPreSOH(ec_pre_t* pre, const word a[], size_t w, size_t h, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ @@ -272,10 +269,10 @@ bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecPreSNZH_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecPreSOH_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); // первая строка - if (!ecPreSNZA(pre, a, w, ec, stack)) + if (!ecPreSOA(pre, a, w, ec, stack)) return FALSE; // остальные строки prev = pre->pts, cur = prev + SIZE_BIT_POS(w - 1) * 2 * ec->f->n; @@ -294,25 +291,25 @@ bool_t ecPreSNZH(ec_pre_t* pre, const word a[], size_t w, size_t h, cur = pt; } // заполнить служебные поля - pre->type = ec_pre_snzh; + pre->type = ec_pre_soh; pre->h = h; return TRUE; } -size_t ecPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep) +size_t ecPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecPreSNZH_local(n, ec_d), - ecPreSNZA_deep(n, ec_d, ec_deep), + ecPreSOH_local(n, ec_d), + ecPreSOA_deep(n, ec_d, ec_deep), SIZE_MAX); } -#define ecPreHPB_local(n, ec_d, h)\ +#define ecPreSI_local(n, ec_d, h)\ /* t1 */ O_OF_W(ec_d * n),\ /* t2 */ O_OF_W(ec_d * n),\ /* dbls */ O_OF_W(h * ec_d * n) -bool_t ecPreHPB(ec_pre_t* pre, const word a[], size_t w, size_t h, +bool_t ecPreSI(ec_pre_t* pre, const word a[], size_t w, size_t h, const ec_o* ec, void* stack) { word* t1; /* [ec->d * ec->f->n] */ @@ -328,7 +325,7 @@ bool_t ecPreHPB(ec_pre_t* pre, const word a[], size_t w, size_t h, a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecPreHPB_local(ec->f->n, ec->d, h), SIZE_0, SIZE_MAX, + ecPreSI_local(ec->f->n, ec->d, h), SIZE_0, SIZE_MAX, &t1, &t2, &dbls, &stack); // dbls[i] <- 2(2^h)^i a, t2 <- \sum_{i=0}^{w-1} (2^h)^i a ecFromA(t1, a, ec, stack); @@ -361,15 +358,15 @@ bool_t ecPreHPB(ec_pre_t* pre, const word a[], size_t w, size_t h, return FALSE; } // заполнить служебные поля - pre->type = ec_pre_hpb; + pre->type = ec_pre_si; pre->w = w, pre->h = h; return TRUE; } -size_t ecPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h) +size_t ecPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h) { return memSliceSize( - ecPreHPB_local(n, ec_d, h), + ecPreSI_local(n, ec_d, h), ec_deep, SIZE_MAX); } @@ -386,8 +383,8 @@ size_t ecPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h) Имеются три стратегии: 1) w = 2 и малые кратные вообще не рассчитываются; -2) w > 2 и малые кратные рассчитываются в аффинных координатах (схема SNZA); -3) w > 2 и малые кратные рассчитываются в проективных координатах (схема SNZ). +2) w > 2 и малые кратные рассчитываются в аффинных координатах (схема SOA); +3) w > 2 и малые кратные рассчитываются в проективных координатах (схема SO). Если малые кратные рассчитываются по схеме "последовательно складывать с удвоенной исходной точкой", то средняя общая сложность нахождения @@ -406,7 +403,7 @@ size_t ecPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h) Длина окна w выбирается как решение следующей оптимизационной задачи: (2^{w - 2} - 2) + l / (w + 1) -> min. -В функции ecMulA() реализована композиция функций ecPreSNZ() (предвычисления) +В функции ecMulA() реализована композиция функций ecPreSO() (предвычисления) и ecMulPreNAF(). [HMV04] Hankerson D., Menezes A., Vanstone S. Guide to Elliptic Curve @@ -441,7 +438,7 @@ static bool_t ecMulPreNAF(word b[], const ec_pre_t* pre, const ec_o* ec, // pre ASSERT(ecIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_snz); + ASSERT(pre->type == ec_pre_so); // разметить стек memSlice(stack, ecMulPreNAF_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, @@ -512,7 +509,7 @@ bool_t ecMulA(word b[], const word a[], const ec_o* ec, ecMulA_local(ec->f->n, ec->d, pre_count), SIZE_0, SIZE_MAX, &pre, &stack); // предвычисления: pre[i] <- (2i + 1)a - ecPreSNZ(pre, a, naf_width - 1, ec, stack); + ecPreSO(pre, a, naf_width - 1, ec, stack); // предвычисления: pre[pre_count - 1 - i] <- -pre[i] for (i = 0; i < pre_count / 2; ++i) ecNeg(ecPrePt(pre, pre_count - 1 - i, ec), @@ -528,21 +525,21 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) return memSliceSize( ecMulA_local(n, ec_d, pre_count), utilMax(2, - ecPreSNZ_deep(m, ec_d, ec_deep), + ecPreSO_deep(m, ec_d, ec_deep), ecMulPreNAF_deep(n, ec_d, ec_deep, m)), SIZE_MAX); } /* ******************************************************************************* -Кратная точка: метод SNZ и его модификации +Кратная точка: метод SO и его модификации -В функции ecMulPreSNZ() для определения кратной точки b = da при нечетной +В функции ecMulPreSO() для определения кратной точки b = da при нечетной кратности d используется ее представление в форме d_{k-1} (2^w)^{k-1} + ... + d_1 (2^w) + d_0. Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w-1)} -- ненулевые (нечетные) цифры со знаком (signed non-zero). Предполагается, что точка a лежит в группе -нечетного порядка order, (2^w)^{k-1} < order < (2^w)^k. Представление SNZ +нечетного порядка order, (2^w)^{k-1} < order < (2^w)^k. Представление SO предложено в [OkeTak03]. Если кратность d четная, то она предварительно меняется на order-d. Для этого @@ -550,7 +547,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], [APS22]): 1. Для i = 0, 1, ..., 2^{w-1}-1: - 1) pre[i] <- (2i + 1)a. // предвычисления: SNZ + 1) pre[i] <- (2i + 1)a. // предвычисления: SO 2. t <- pre[d_{k-1} / 2]. 3. Для i = k-2, ..., 2, 1: 1) t <- 2^w t; // P <- 2P @@ -583,21 +580,25 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) четность можно использовать для пересчета borrow: borrow <- d_i % 2. -В функции ecMulPreSNZA() предварительно рассчитанные точки являются аффинными, +В функции ecMulPreSOA() предварительно рассчитанные точки являются аффинными, а не проективными. Это позволяет заменить регулярные сложения P <- P + P на P <- P + A и финишное сложение A <- P + P на A <- P + A. -В функции ecMulPreSNZH() предполагается, что аффинные точки предварительно +В функции ecMulPreSOH() предполагается, что аффинные точки предварительно рассчитаны для каждой цифры обрабатываемого скаляра d. Это позволяет избавиться от удвоений: 1. Для i = 0, 1, ..., k-1 и j = 0, 1, ..., 2^{w-1}-1: - 1) pre[i][j] <- (2^w)^i(2j + 1)a. // предвычисления: SNZH + 1) pre[i][j] <- (2^w)^i(2j + 1)a. // предвычисления: SOH 2. t <- pre[k-1][d_{k-1}/2]. 3. Для i = k-2, ..., 2, 1: 1) t <- t + sgn(d_i) pre[i]t[|d_i|/2]. // P <- P + P 4. t <- t + sgn(d_0) pre[0][|d_0|/2]. // A <- P + P 5. Возвратить t. +[OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory + and fast elliptic scalar multiplications secure against side + channel attacks. In Cryptographers’ Track at the RSA Conference, + 2003, pp. 328-343. Springer, Berlin Heidelberg. [BCL+14] Bos J.W., Costello C., Longa P., Naehrig M. Selecting Elliptic Curves for Cryptography: An Efficiency and Security Analysis, 2014, https://eprint.iacr.org/2014/130.pdf. @@ -607,12 +608,12 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) ******************************************************************************* */ -#define ecMulPreSNZ_local(n, ec_d, m)\ +#define ecMulPreSO_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(ec_d * n) -bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, +bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -632,7 +633,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_snz); + ASSERT(pre->type == ec_pre_so); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); @@ -644,7 +645,7 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, - ecMulPreSNZ_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + ecMulPreSO_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, &dd, &t, &pt, &stack); // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); @@ -686,20 +687,20 @@ bool_t ecMulPreSNZ(word b[], const ec_pre_t* pre, const ec_o* ec, return ret; } -size_t ecMulPreSNZ_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( - ecMulPreSNZ_local(n, ec_d, m), + ecMulPreSO_local(n, ec_d, m), ec_deep, SIZE_MAX); } -#define ecMulPreSNZA_local(n, ec_d, m)\ +#define ecMulPreSOA_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(2 * n) -bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, +bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -719,7 +720,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_snza); + ASSERT(pre->type == ec_pre_soa); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); @@ -731,7 +732,7 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, - ecMulPreSNZA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + ecMulPreSOA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, &dd, &t, &pt, &stack); // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); @@ -774,20 +775,20 @@ bool_t ecMulPreSNZA(word b[], const ec_pre_t* pre, const ec_o* ec, return ret; } -size_t ecMulPreSNZA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( - ecMulPreSNZA_local(n, ec_d, m), + ecMulPreSOA_local(n, ec_d, m), ec_deep, SIZE_MAX); } -#define ecMulPreSNZH_local(n, ec_d, m)\ +#define ecMulPreSOH_local(n, ec_d, m)\ /* dd */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(2 * n) -bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, +bool_t ecMulPreSOH(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -807,7 +808,7 @@ bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_snzh); + ASSERT(pre->type == ec_pre_soh); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); @@ -819,7 +820,7 @@ bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, - ecMulPreSNZH_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + ecMulPreSOH_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, &dd, &t, &pt, &stack); // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); @@ -861,21 +862,21 @@ bool_t ecMulPreSNZH(word b[], const ec_pre_t* pre, const ec_o* ec, return ret; } -size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( - ecMulPreSNZH_local(n, ec_d, m), + ecMulPreSOH_local(n, ec_d, m), ec_deep, SIZE_MAX); } /* ******************************************************************************* -Кратная точка: метод HPB +Кратная точка: метод SI -В функции ecMulPreHPB() вычисление кратной точки выполняется по методу HPB, -предложенному в [HPB05]. Метод HPB является модификацией метода Comb, -предложенного в [LimLee94] (см. также [HMV04; algorithm 3.44]). +В функции ecMulPreSI() вычисление кратной точки выполняется по методу, +предложенному в [HPB05]. Это модификация метода Comb, введенного в [LimLee94] +(см. также [HMV04; algorithm 3.44]). Для определения кратной точки b = da при нечетной кратности d используется ее представление в форме @@ -883,7 +884,7 @@ size_t ecMulPreSNZH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) где d_i \in {\pm 1}. Как обычно, если кратность d четная, то она предварительно меняется на -order-d. +order - d. Реализован следующий алгоритм: 1. Для с_{w-1} = 0 и с_{w-2}...c_0 \in {0,1}^{w-1}: @@ -898,6 +899,19 @@ order-d. pt[d_{hw-h}d_{h(w-1)-h)}...d_0 ^ d_{hw-h}...d_{hw-h}]. 6. Возвратить t. +Переход от двоичного представления + d = \sum_{i=0}^{hw-1} d_i 2^i, d_i \in {0,1}, d_0 = 1, +к \pm 1 представлению + d = \sum_{i=0}^{hw-1}(1-2 e_i) 2^i, e_i \in {0,1}, +реализуется следующим образом: + (e_{hw-1}, ..., e_1, e_0) <- (0, d_{hw-1} ^ 1, ..., d_1 ^ 1). + +\remark Обоснование: + \sum_{i=0}^{hw-1}(1-2 e_i) 2^i = + 2^{hw-1} + \sum_{i=0}^{hw-2}(2 d_{i+1}-1)2^i = + 2^{hw-1} - \sum_{i=0}^{hw-2}2^i + \sum_{i=1}^{hw-1}d_i 2^i = + 1 + \sum_{i=1}^{hw-1}d_i 2^i = d. + [HPB05] Hedabou M., Pinel P., Beneteau L. Countermeasures for preventing comb method against SCA attacks. In: International Conference on Information Security Practice and Experience, 2005, pp. 85-96, @@ -909,12 +923,12 @@ order-d. ******************************************************************************* */ -#define ecMulPreHPB_local(n, ec_d, m)\ +#define ecMulPreSI_local(n, ec_d, m)\ /* dd */ O_OF_W(m + 1),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(2 * n) -bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, +bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -934,7 +948,7 @@ bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_hpb); + ASSERT(pre->type == ec_pre_si); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); @@ -947,7 +961,7 @@ bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, mask = WORD_BIT_POS(pre->w - 1) - 1; // разметить стек memSlice(stack, - ecMulPreHPB_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + ecMulPreSI_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, &dd, &t, &pt, &stack); // dd <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); @@ -1001,10 +1015,10 @@ bool_t ecMulPreHPB(word b[], const ec_pre_t* pre, const ec_o* ec, return ret; } -size_t ecMulPreHPB_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( - ecMulPreHPB_local(n, ec_d, m), + ecMulPreSI_local(n, ec_d, m), ec_deep, SIZE_MAX); } @@ -1113,7 +1127,7 @@ bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...) naf_max_size = naf_size[i]; naf_pos[i] = 0; // предвычисления - ecPreSNZ(pre[i], a, naf_width[i] - 1, ec, stack); + ecPreSO(pre[i], a, naf_width[i] - 1, ec, stack); for (j = 0; j < pre_count / 2; ++j) ecNeg(ecPrePt(pre[i], pre_count - 1 - j, ec), ecPrePt(pre[i], j, ec), ec, stack); @@ -1159,7 +1173,7 @@ size_t ecAddMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t k, ...) va_list args; ret = memSliceSize( ecAddMulA_local(n, ec_d, k), - ecPreSNZ_deep(n, ec_d, ec_deep), + ecPreSO_deep(n, ec_d, ec_deep), SIZE_MAX); va_start(args, k); for (i = 0; i < k; ++i) diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index e5994e44..cb03302f 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.09 +\version 2026.02.16 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -36,7 +36,7 @@ version 3. See Copyright Notices in bee2/info.h. /* ******************************************************************************* -Предвычисления: схема SNZ +Предвычисления: схема SO Реализован алгоритм SmallMultJ, предложенный в [APS22]. Алгоритм основан на многочленах деления и требует примерно 19/2 M + 7/2 S операций на каждую @@ -84,7 +84,7 @@ version 3. See Copyright Notices in bee2/info.h. #error "Conflicting preprocessor definitions" #endif -#define ecpPreSNZ_local(n, w)\ +#define ecpPreSO_local(n, w)\ /* t */ O_OF_W(n),\ /* dy2 */ O_OF_W(n),\ /* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ @@ -94,7 +94,7 @@ version 3. See Copyright Notices in bee2/info.h. /* WWy4s */ O_OF_W(n),\ /* WWWs */ O_OF_W((SIZE_BIT_POS(w - 1) - 2) * n) -void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, +void ecpPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { size_t n; @@ -118,7 +118,7 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, n = ec->f->n; // разметить стек memSlice(stack, - ecpPreSNZ_local(n, w), SIZE_0, SIZE_MAX, + ecpPreSO_local(n, w), SIZE_0, SIZE_MAX, &t, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &WWWs, &stack); // первая точка pt = pre->pts; @@ -306,21 +306,21 @@ void ecpPreSNZ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, /* SmallMultJ: end */ // заполнить служебные поля - pre->type = ec_pre_snz; + pre->type = ec_pre_so; pre->w = w, pre->h = 0; } -size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) +size_t ecpPreSO_deep(size_t n, size_t f_deep, size_t w) { return memSliceSize( - ecpPreSNZ_local(n, w), + ecpPreSO_local(n, w), f_deep, SIZE_MAX); } /* ******************************************************************************* -Предвычисления: схема SNZA +Предвычисления: схема SOA Реализован алгоритм SmallMultA, предложенный в [APS22]. Алгоритм основан на многочленах деления и требует примерно 25/2 M + 5 S операций на каждую @@ -333,7 +333,7 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) - этап 3 -- шаг 15 (обращение элементов поля); - этап 4 -- шаги 16 -- 20 (формирование точек). -В целом кэшируются те же выражения, что и в ecpPreSNZ(). Отличия: +В целом кэшируются те же выражения, что и в ecpPreSO(). Отличия: - не кэшируется выражение Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁². Соответственно макрос WWW не используется; - выражения (2y)²WᵢWᵢ₊₂ сохраняются в координатах выходных точек. @@ -374,7 +374,7 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) #error "Conflicting preprocessor definitions" #endif -#define ecpPreSNZA_local(n, w)\ +#define ecpPreSOA_local(n, w)\ /* t */ O_OF_W(n),\ /* dy2 */ O_OF_W(n),\ /* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ @@ -383,7 +383,7 @@ size_t ecpPreSNZ_deep(size_t n, size_t f_deep, size_t w) /* WWy4s */ O_OF_W(n),\ /* W2Is */ O_OF_W((SIZE_BIT_POS(w - 1) - 1) * n) -bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, +bool_t ecpPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { size_t n; @@ -406,7 +406,7 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, n = ec->f->n; // разметить стек memSlice(stack, - ecpPreSNZA_local(n, w), SIZE_0, SIZE_MAX, + ecpPreSOA_local(n, w), SIZE_0, SIZE_MAX, &t, &dy2, &Ws, &W2s, &WWs, &WWy4s, &W2Is, &stack); // первая точка pt = pre->pts; @@ -628,15 +628,15 @@ bool_t ecpPreSNZA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, /* SmallMultA: end */ // заполнить служебные поля - pre->type = ec_pre_snza; + pre->type = ec_pre_soa; pre->w = w, pre->h = 0; return TRUE; } -size_t ecpPreSNZA_deep(size_t n, size_t f_deep, size_t w) +size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) { return memSliceSize( - ecpPreSNZA_local(n, w), + ecpPreSOA_local(n, w), f_deep, SIZE_MAX); } diff --git a/test/math/ec_test.c b/test/math/ec_test.c index 36b0af1b..69a8aa04 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves \project bee2/test \created 2026.02.12 -\version 2026.02.12 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -113,14 +113,14 @@ bool_t ecTestEc(const ec_o* ec) ecAddAA_deep(n, ec->d, ec->deep), ecSubAA_deep(n, ec->d, ec->deep), ecMulA_deep(n, ec->d, ec->deep, n), - ecPreSNZ_deep(n, ec->d, ec->deep), - ecPreSNZA_deep(n, ec->d, ec->deep), - ecPreSNZH_deep(n, ec->d, ec->deep), - ecPreHPB_deep(n, ec->d, max_h, ec->deep), - ecMulPreSNZ_deep(n, ec->d, ec->deep, n), - ecMulPreSNZA_deep(n, ec->d, ec->deep, n), - ecMulPreSNZH_deep(n, ec->d, ec->deep, n), - ecMulPreHPB_deep(n, ec->d, ec->deep, n), + ecPreSO_deep(n, ec->d, ec->deep), + ecPreSOA_deep(n, ec->d, ec->deep), + ecPreSOH_deep(n, ec->d, ec->deep), + ecPreSI_deep(n, ec->d, max_h, ec->deep), + ecMulPreSO_deep(n, ec->d, ec->deep, n), + ecMulPreSOA_deep(n, ec->d, ec->deep, n), + ecMulPreSOH_deep(n, ec->d, ec->deep, n), + ecMulPreSI_deep(n, ec->d, ec->deep, n), ecAddMulA_deep(n, ec->d, ec->deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, @@ -334,11 +334,11 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SNZ + // предвычисления по схеме SO for (w = min_w; w <= max_w; ++w) { // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - ecPreSNZ(pre, ec->base, w, ec, stack); + ecPreSO(pre, ec->base, w, ec, stack); // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) @@ -361,12 +361,12 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SNZA + // предвычисления по схеме SOA for (w = min_w; w <= max_w; ++w) { size_t i; // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - if (!ecPreSNZA(pre, ec->base, w, ec, stack) || + if (!ecPreSOA(pre, ec->base, w, ec, stack) || !wwEq(ecPrePtA(pre, 0, ec), ec->base, 2 * n)) { blobClose(state); @@ -401,13 +401,13 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме HPB + // предвычисления по схеме SI for (w = min_w; w <= max_w; ++w) { const size_t h = (B_OF_O(no) + w - 1) / w; size_t i; // pre[0..2^{w-1}) <- (1, 3, ..., 2^{w-1}-1)base - ecPreHPB(pre, ec->base, w, h, ec, stack); + ecPreSI(pre, ec->base, w, h, ec, stack); // pre[0] == \sum_{i=0}^{w-1} (2^h)^i base? wwSetZero(d, n + 1); for (i = 0; i < w; ++i) @@ -463,16 +463,16 @@ bool_t ecTestEc(const ec_o* ec) blobClose(state); return TRUE; } - // кратная точка: ecMulA vs ecMulPreSNZ + // кратная точка: ecMulA vs ecMulPreSO for (w = min_w; w <= max_w; ++w) { const size_t m = wwWordSize(ec->order, n + 1); wwCopy(d, ec->order, m); zzSubW2(d, m, 1); - ecPreSNZ(pre, ec->base, w, ec, stack); + ecPreSO(pre, ec->base, w, ec, stack); while (!wwIsZero(d, m)) { - if (!ecMulPreSNZ(pt0, pre, ec, d, m, stack) || + if (!ecMulPreSO(pt0, pre, ec, d, m, stack) || !ecMulA(pt1, ec->base, ec, d, m, stack) || !wwEq(pt0, pt1, 2 * n)) { @@ -481,27 +481,27 @@ bool_t ecTestEc(const ec_o* ec) } wwShLo(d, m, 31); } - if (ecMulPreSNZ(pt0, pre, ec, d, m, stack) || + if (ecMulPreSO(pt0, pre, ec, d, m, stack) || ecMulA(pt1, ec->base, ec, d, m, stack)) { blobClose(state); return FALSE; } } - // кратная точка: ecMulA vs ecMulPreSNZA + // кратная точка: ecMulA vs ecMulPreSOA for (w = min_w; w <= max_w; ++w) { const size_t m = wwWordSize(ec->order, n + 1); wwCopy(d, ec->order, m); zzSubW2(d, m, 1); - if (!ecPreSNZA(pre, ec->base, w, ec, stack)) + if (!ecPreSOA(pre, ec->base, w, ec, stack)) { blobClose(state); return FALSE; } while (!wwIsZero(d, m)) { - if (!ecMulPreSNZA(pt0, pre, ec, d, m, stack) || + if (!ecMulPreSOA(pt0, pre, ec, d, m, stack) || !ecMulA(pt1, ec->base, ec, d, m, stack) || !wwEq(pt0, pt1, 2 * n)) { @@ -510,28 +510,28 @@ bool_t ecTestEc(const ec_o* ec) } wwShLo(d, m, 23); } - if (ecMulPreSNZA(pt0, pre, ec, d, m, stack) || + if (ecMulPreSOA(pt0, pre, ec, d, m, stack) || ecMulA(pt1, ec->base, ec, d, m, stack)) { blobClose(state); return FALSE; } } - // кратная точка: ecMulA vs ecMulPreSNZH + // кратная точка: ecMulA vs ecMulPreSOH for (w = min_w; w <= max_w; ++w) { const size_t m = wwWordSize(ec->order, n + 1); const size_t h = (B_OF_O(no) + w - 1) / w; wwCopy(d, ec->order, m); zzSubW2(d, m, 1); - if (!ecPreSNZH(pre, ec->base, w, h, ec, stack)) + if (!ecPreSOH(pre, ec->base, w, h, ec, stack)) { blobClose(state); return FALSE; } while (!wwIsZero(d, m)) { - if (!ecMulPreSNZH(pt0, pre, ec, d, m, stack) || + if (!ecMulPreSOH(pt0, pre, ec, d, m, stack) || !ecMulA(pt1, ec->base, ec, d, m, stack) || !wwEq(pt0, pt1, 2 * m)) { @@ -540,28 +540,28 @@ bool_t ecTestEc(const ec_o* ec) } wwShLo(d, m, 34); } - if (ecMulPreSNZH(pt0, pre, ec, d, m, stack) || + if (ecMulPreSOH(pt0, pre, ec, d, m, stack) || ecMulA(pt1, ec->base, ec, d, m, stack)) { blobClose(state); return FALSE; } } - // кратная точка: ecMulA vs ecMulPreHPB + // кратная точка: ecMulA vs ecMulPreSI for (w = min_w; w <= max_w; ++w) { const size_t m = wwWordSize(ec->order, n + 1); const size_t h = (B_OF_O(no) + w - 1) / w; wwCopy(d, ec->order, m); zzSubW2(d, m, 1); - if (!ecPreHPB(pre, ec->base, w, h, ec, stack)) + if (!ecPreSI(pre, ec->base, w, h, ec, stack)) { blobClose(state); return FALSE; } while (!wwIsZero(d, m)) { - if (!ecMulPreHPB(pt0, pre, ec, d, m, stack) || + if (!ecMulPreSI(pt0, pre, ec, d, m, stack) || !ecMulA(pt1, ec->base, ec, d, m, stack) || !wwEq(pt0, pt1, 2 * n)) { @@ -570,7 +570,7 @@ bool_t ecTestEc(const ec_o* ec) } wwShLo(d, m, 15); } - if (ecMulPreHPB(pt0, pre, ec, d, m, stack) || + if (ecMulPreSI(pt0, pre, ec, d, m, stack) || ecMulA(pt1, ec->base, ec, d, m, stack)) { blobClose(state); diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 5ceff75a..2a7fd3e1 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.12 +\version 2026.02.16 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -48,16 +48,16 @@ static bool_t ecpBenchEc(const ec_o* ec) sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), utilMax(11, ecMulA_deep(n, ec->d, ec->deep, n), - ecPreSNZ_deep(n, ec->d, ec->deep), - ecPreSNZA_deep(n, ec->d, ec->deep), - ecPreSNZH_deep(n, ec->d, ec->deep), - ecPreHPB_deep(n, ec->d, ec->deep, max_h), - ecpPreSNZ_deep(n, ec->f->deep, max_w), - ecpPreSNZA_deep(n, ec->f->deep, max_w), - ecMulPreSNZ_deep(n, ec->d, ec->deep, n), - ecMulPreSNZA_deep(n, ec->d, ec->deep, n), - ecMulPreSNZH_deep(n, ec->d, ec->deep, n), - ecMulPreHPB_deep(n, ec->d, ec->deep, n)), + ecPreSO_deep(n, ec->d, ec->deep), + ecPreSOA_deep(n, ec->d, ec->deep), + ecPreSOH_deep(n, ec->d, ec->deep), + ecPreSI_deep(n, ec->d, ec->deep, max_h), + ecpPreSO_deep(n, ec->f->deep, max_w), + ecpPreSOA_deep(n, ec->f->deep, max_w), + ecMulPreSO_deep(n, ec->d, ec->deep, n), + ecMulPreSOA_deep(n, ec->d, ec->deep, n), + ecMulPreSOH_deep(n, ec->d, ec->deep, n), + ecMulPreSI_deep(n, ec->d, ec->deep, n)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); if (state == 0) @@ -78,120 +78,120 @@ static bool_t ecpBenchEc(const ec_o* ec) ecMulA(pt, ec->base, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecpPre+ecMulPre[SNZ] + // ecpPre+ecMulPre[SO] for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecpPreSNZ(pre, ec->base, w, ec, stack); - ecMulPreSNZ(pt, pre, ec, d, n, stack); + ecpPreSO(pre, ec->base, w, ec, stack); + ecMulPreSO(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecpPre+ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecpPre+ecMulPre[SO,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecpPre+ecMulPre[SNZA] + // ecpPre+ecMulPre[SOA] for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecpPreSNZA(pre, ec->base, w, ec, stack); - ecMulPreSNZA(pt, pre, ec, d, n, stack); + ecpPreSOA(pre, ec->base, w, ec, stack); + ecMulPreSOA(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecpPre+ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecpPre+ecMulPre[SOA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecMulPre[SNZ] + // ecMulPre[SO] for (w = min_w; w <= max_w; ++w) { - ecPreSNZ(pre, ec->base, w, ec, stack); + ecPreSO(pre, ec->base, w, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecMulPreSNZ(pt, pre, ec, d, n, stack); + ecMulPreSO(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[SNZ,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[SO,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecMulPre[SNZA] + // ecMulPre[SOA] for (w = min_w; w <= max_w; ++w) { - ecPreSNZA(pre, ec->base, w, ec, stack); + ecPreSOA(pre, ec->base, w, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecMulPreSNZA(pt, pre, ec, d, n, stack); + ecMulPreSOA(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[SNZA,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[SOA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecMulPre[SNZH] + // ecMulPre[SOH] for (w = min_w; w <= max_w; ++w) { size_t h = (B_OF_O(no) + w - 1) / w; - ecPreSNZH(pre, ec->base, w, h, ec, stack); + ecPreSOH(pre, ec->base, w, h, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecMulPreSNZH(pt, pre, ec, d, n, stack); + ecMulPreSOH(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[SNZH,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[SOH,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecMulPre[HPB] + // ecMulPre[SI] for (w = min_w; w <= max_w; ++w) { size_t h = (B_OF_O(no) + w - 1) / w; - ecPreHPB(pre, ec->base, w, h, ec, stack); + ecPreSI(pre, ec->base, w, h, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecMulPreHPB(pt, pre, ec, d, n, stack); + ecMulPreSI(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[HPB,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[SI,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecPre[SNZ] + // ecPre[SO] for (w = min_w; w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) - ecPreSNZ(pre, ec->base, w, ec, stack); + ecPreSO(pre, ec->base, w, ec, stack); ticks = tmTicks() - ticks; - printf(" ecPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", + printf(" ecPre[SO,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecpPre[SNZ] + // ecpPre[SO] for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) - ecpPreSNZ(pre, ec->base, w, ec, stack); + ecpPreSO(pre, ec->base, w, ec, stack); ticks = tmTicks() - ticks; - printf(" ecpPre[SNZ,w=%u]: %u cycles/pre [%u pre/sec]\n", + printf(" ecpPre[SO,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index bffa7897..bff9dacc 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -55,8 +55,8 @@ static bool_t ecpTestEc(const ec_o* ec) ecpAddAA_deep(n, ec->f->deep), ecpSubAA_deep(n, ec->f->deep), ecMulA_deep(n, ec->d, ec->deep, n), - ecpPreSNZ_deep(n, ec->f->deep, max_w), - ecpPreSNZA_deep(n, ec->f->deep, max_w)), + ecpPreSO_deep(n, ec->f->deep, max_w), + ecpPreSOA_deep(n, ec->f->deep, max_w)), SIZE_MAX, &pre, &pt0, &pt1, &d, &stack); if (state == 0) @@ -93,11 +93,11 @@ static bool_t ecpTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SNZ + // предвычисления по схеме SO for (w = MAX2(3, min_w); w <= max_w; ++w) { // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - ecpPreSNZ(pre, ec->base, w, ec, stack); + ecpPreSO(pre, ec->base, w, ec, stack); // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) @@ -120,11 +120,11 @@ static bool_t ecpTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SNZA + // предвычисления по схеме SOA for (w = MAX2(3, min_w); w <= max_w; ++w) { // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - if (!ecpPreSNZA(pre, ec->base, w, ec, stack)) + if (!ecpPreSOA(pre, ec->base, w, ec, stack)) { blobClose(state); return FALSE; From 641ea301db27fba25ec5d411732168b43ae1f5b6 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 18 Feb 2026 15:52:19 +0300 Subject: [PATCH 44/77] Slightly optimize ecMulPreSOA() --- src/math/ec.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/math/ec.c b/src/math/ec.c index 6337d536..3ca6ec0f 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.16 +\version 2026.02.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -656,21 +656,23 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(dd, pos, mb - pos); wwSel(t, pre->pts, pre_count, pt_size, digit >> 1); borrow = WORD_1 - (digit & 1); + // t <- 2^w t + for (i = 0; i < pre->w; ++i) + ecDbl(t, t, ec, stack); // обработать остальные цифры while (pos -= pre->w) { - for (i = pre->w; i--;) - ecDbl(t, t, ec, stack); + // сложить digit = wwGetBits(dd, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgn(pt, borrow, ec, stack); ecAdd(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); + // удвоить + for (i = 0; i < pre->w; ++i) + ecDbl(t, t, ec, stack); } - // завершающие удвоения - for (i = pre->w; i--;) - ecDbl(t, t, ec, stack); // финишное сложение digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); @@ -742,23 +744,30 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, pos = (mb - 1) / pre->w, pos *= pre->w; digit = wwGetBits(dd, pos, mb - pos); wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); - ecFromA(t, pt, ec, stack); borrow = WORD_1 - (digit & 1); + // t <- 2^{w-1} pt + if (pre->w > 1) + ecDblA(t, pt, ec, stack); + else + ecFromA(t, pt, ec, stack); + for (i = 2; i < pre->w; ++i) + ecDbl(t, t, ec, stack); // обработать остальные цифры while (pos -= pre->w) { - for (i = pre->w; --i;) - ecDbl(t, t, ec, stack); + // удвоить со сложением digit = wwGetBits(dd, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgnA(pt, borrow, ec, stack); ecDblAddA(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); + // t <- 2^{w-1} t + for (i = 1; i < pre->w; ++i) + ecDbl(t, t, ec, stack); } - // завершающие удвоения - for (i = pre->w; i--;) - ecDbl(t, t, ec, stack); + // завершающее удвоение + ecDbl(t, t, ec, stack); // финишное сложение digit = wwGetBits(dd, 0, pre->w); ASSERT(digit & 1); From 0d8547c6a923b423036fb5e2d3c8b2aa742f67e0 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 20 Feb 2026 20:06:51 +0300 Subject: [PATCH 45/77] Start Co-Z formulas in math/ecp --- include/bee2/math/ec.h | 39 ++- include/bee2/math/ecp.h | 8 +- src/crypto/bign/bign_misc.c | 10 +- src/math/ec.c | 119 +++---- src/math/ecp/ecp_pre.c | 472 ++++++++++++------------- test/math/ec_test.c | 14 +- test/math/ecp_bench.c | 16 +- test/math/ecp_test.c | 664 +++++++++++++++++++++++++++++++++++- 8 files changed, 976 insertions(+), 366 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 9ba4e860..b6819ddf 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.16 +\version 2026.02.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -485,7 +485,8 @@ typedef enum { ec_pre_so = 1, /*!< схема SO */ ec_pre_soa, /*!< схема SOA */ - ec_pre_soh, /*!< схема SOH */ + ec_pre_sh, /*!< схема SH */ + ec_pre_od, /*!< схема OD */ ec_pre_si, /*!< схема SI */ } ec_pre_type; @@ -503,7 +504,11 @@ typedef enum \code a, 3a, ..., (2^w-1)a; \endcode - - SOH: вычисляются аффинные точки + - SH (Small Hi): вычисляются проективные точки + \code + (2^{w-1})a, (2^{w-1}+1)a, ..., (2^w-1)a; + \endcode + - OD (Odd Doubled): вычисляются аффинные точки \code 2^{0} (a, 3a, ..., (2^w-1)a), 2^{w} (a, 3a, ..., (2^w-1)a), @@ -520,7 +525,7 @@ typedef enum c_{w-1}... c_1 c_0. . \pre 0 < w && w < B_PER_W. - \pre h > 0 <=> type \in { ec_pre_soh, ec_pre_si }. + \pre h > 0 <=> type \in { ec_pre_od, ec_pre_si }. */ typedef struct ec_pre_t { @@ -754,7 +759,7 @@ bool_t ecGroupIsOperable( - буфер [sizeof(ec_pre_t)]pre корректен; - pre->type \in ec_pre_type; - pre->w > 0; - - pre->h > 0 <=> pre->type \in { ec_pre_soh, ec_pre_si }. + - pre->h > 0 <=> pre->type \in { ec_pre_od, ec_pre_si }. . \return Признак работоспособности. */ @@ -818,9 +823,9 @@ bool_t ecPreSOA( size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); -/*! \brief Предвычисления по схеме SOH +/*! \brief Предвычисления по схеме OD - На эллиптической кривой ec выполняются предвычисления по схеме SOH + На эллиптической кривой ec выполняются предвычисления по схеме OD с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. @@ -834,9 +839,9 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным результатом в pre->pts. - \deep{stack} ecPreSOH_deep(ec->f->n, ec->d, ec->deep). + \deep{stack} ecPreOD_deep(ec->f->n, ec->d, ec->deep). */ -bool_t ecPreSOH( +bool_t ecPreOD( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -845,7 +850,7 @@ bool_t ecPreSOH( void* stack /*!< [in] вспомогательная память */ ); -size_t ecPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep); +size_t ecPreOD_deep(size_t n, size_t ec_d, size_t ec_deep); /*! \brief Предвычисления по схеме SI @@ -1001,18 +1006,18 @@ bool_t ecMulPreSOA( size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SOH +/*! \brief Кратная точка с предвычислениями по схеме OD Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SOH точками: + с предвычисленными по схеме OD точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_soh. + \pre pre->type == ec_pre_od. \pre Длина ec->order в машинных словах равняется m. \pre m > 1. \pre d < ec->order. @@ -1022,7 +1027,7 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreSOH_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreOD_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - если ec->dbladda == 0, то функция ec->adda регулярна: сложение a + b, a != \pm b, a != O, b != O, выполняется без условных переходов; @@ -1037,7 +1042,7 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); удвоение выполняются по одним и тем же формулам без условных переходов. */ -bool_t ecMulPreSOH( +bool_t ecMulPreOD( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -1046,9 +1051,9 @@ bool_t ecMulPreSOH( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SOA +/*! \brief Кратная точка с предвычислениями по схеме SI Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index d6f4a066..3fae5ea7 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.02.16 +\version 2026.02.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -282,7 +282,7 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. - \deep{stack} ecpPreSO_deep(ec->f->n, ec->d, ec->deep, w). + \deep{stack} ecpPreSO_deep(ec->f->n, ec->d, ec->f->deep). */ void ecpPreSO( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -292,7 +292,7 @@ void ecpPreSO( void* stack /*!< [in] вспомогательная память */ ); -size_t ecpPreSO_deep(size_t n, size_t f_deep, size_t w); +size_t ecpPreSO_deep(size_t n, size_t ec_d, size_t f_deep); /*! \brief Предвычисления по схеме SOA @@ -302,7 +302,7 @@ size_t ecpPreSO_deep(size_t n, size_t f_deep, size_t w); \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. \pre w >= 3. - \pre memIsValid(pre, sizeof(ec_pre_t) + + \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index f59143cb..c33a1dce 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.02.16 +\version 2026.02.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -63,7 +63,7 @@ size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) return memSliceSize( bignMulA_local(n, pre_count), utilMax(2, - ecpPreSO_deep(n, f_deep, w), + ecpPreSO_deep(n, 3, f_deep), ecMulPreSO_deep(n, 3, ec_deep, n)), SIZE_MAX); } @@ -76,8 +76,8 @@ bool_t bignMulBase(word a[], const ec_o* ec, const word d[], void* stack) ASSERT(ecPreIsOperable(ec->pre)); switch (ec->pre->type) { - case ec_pre_soh: - return ecMulPreSOH(a, ec->pre, ec, d, ec->f->n, stack); + case ec_pre_od: + return ecMulPreOD(a, ec->pre, ec, d, ec->f->n, stack); case ec_pre_si: return ecMulPreSI(a, ec->pre, ec, d, ec->f->n, stack); case ec_pre_soa: @@ -93,7 +93,7 @@ size_t bignMulBase_deep(size_t n, size_t f_deep, size_t ec_deep) { return utilMax(4, bignMulA_deep(n, f_deep, ec_deep), - ecMulPreSOH_deep(n, 3, ec_deep, n), + ecMulPreOD_deep(n, 3, ec_deep, n), ecMulPreSI_deep(n, 3, ec_deep, n), ecMulPreSOA_deep(n, 3, ec_deep, n), ecMulPreSO_deep(n, 3, ec_deep, n)); diff --git a/src/math/ec.c b/src/math/ec.c index 3ca6ec0f..0f15a210 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -153,9 +153,10 @@ bool_t ecPreIsOperable(const ec_pre_t* pre) { return memIsValid(pre, sizeof(pre)) && (pre->type == ec_pre_so || pre->type == ec_pre_soa || - pre->type == ec_pre_soh || pre->type == ec_pre_si) && + pre->type == ec_pre_sh || pre->type == ec_pre_od || + pre->type == ec_pre_si) && pre->w > 0 && pre->w < B_PER_W && - ((pre->type != ec_pre_soh && pre->type != ec_pre_si) ^ (pre->h > 0)); + ((pre->type != ec_pre_od && pre->type != ec_pre_si) ^ (pre->h > 0)); } #define ecPreSO_local(n, ec_d)\ @@ -251,10 +252,10 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } -#define ecPreSOH_local(n, ec_d)\ +#define ecPreOD_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) -bool_t ecPreSOH(ec_pre_t* pre, const word a[], size_t w, size_t h, +bool_t ecPreOD(ec_pre_t* pre, const word a[], size_t w, size_t h, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ @@ -269,7 +270,7 @@ bool_t ecPreSOH(ec_pre_t* pre, const word a[], size_t w, size_t h, a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecPreSOH_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecPreOD_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, &t, &stack); // первая строка if (!ecPreSOA(pre, a, w, ec, stack)) @@ -291,15 +292,15 @@ bool_t ecPreSOH(ec_pre_t* pre, const word a[], size_t w, size_t h, cur = pt; } // заполнить служебные поля - pre->type = ec_pre_soh; + pre->type = ec_pre_od; pre->h = h; return TRUE; } -size_t ecPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep) +size_t ecPreOD_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( - ecPreSOH_local(n, ec_d), + ecPreOD_local(n, ec_d), ecPreSOA_deep(n, ec_d, ec_deep), SIZE_MAX); } @@ -584,11 +585,11 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) а не проективными. Это позволяет заменить регулярные сложения P <- P + P на P <- P + A и финишное сложение A <- P + P на A <- P + A. -В функции ecMulPreSOH() предполагается, что аффинные точки предварительно +В функции ecMulPreOD() предполагается, что аффинные точки предварительно рассчитаны для каждой цифры обрабатываемого скаляра d. Это позволяет избавиться от удвоений: 1. Для i = 0, 1, ..., k-1 и j = 0, 1, ..., 2^{w-1}-1: - 1) pre[i][j] <- (2^w)^i(2j + 1)a. // предвычисления: SOH + 1) pre[i][j] <- (2^w)^i(2j + 1)a. // предвычисления: OD 2. t <- pre[k-1][d_{k-1}/2]. 3. Для i = k-2, ..., 2, 1: 1) t <- t + sgn(d_i) pre[i]t[|d_i|/2]. // P <- P + P @@ -609,7 +610,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) */ #define ecMulPreSO_local(n, ec_d, m)\ -/* dd */ O_OF_W(m),\ +/* e */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(ec_d * n) @@ -626,7 +627,7 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, size_t mask; size_t pos; size_t i; - word* dd; /* [m] */ + word* e; /* [m] */ word* t; /* [ec->d * ec->f->n] */ word* pt; /* [ec->d * ec->f->n] */ // pre @@ -646,14 +647,14 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, // разметить стек memSlice(stack, ecMulPreSO_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &dd, &t, &pt, &stack); - // dd <- (d % 2) ? d : ec->order - d + &e, &t, &pt, &stack); + // e <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); - zzSubIf(dd, ec->order, d, m, neg); - ASSERT(zzIsOdd(dd, m)); + zzSubIf(e, ec->order, d, m, neg); + ASSERT(zzIsOdd(e, m)); // обработать старшую цифру pos = (mb - 1) / pre->w, pos *= pre->w; - digit = wwGetBits(dd, pos, mb - pos); + digit = wwGetBits(e, pos, mb - pos); wwSel(t, pre->pts, pre_count, pt_size, digit >> 1); borrow = WORD_1 - (digit & 1); // t <- 2^w t @@ -663,7 +664,7 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, while (pos -= pre->w) { // сложить - digit = wwGetBits(dd, pos, pre->w); + digit = wwGetBits(e, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgn(pt, borrow, ec, stack); @@ -674,7 +675,7 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, ecDbl(t, t, ec, stack); } // финишное сложение - digit = wwGetBits(dd, 0, pre->w); + digit = wwGetBits(e, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); @@ -698,7 +699,7 @@ size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) } #define ecMulPreSOA_local(n, ec_d, m)\ -/* dd */ O_OF_W(m),\ +/* e */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(2 * n) @@ -715,7 +716,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, size_t mask; size_t pos; size_t i; - word* dd; /* [m] */ + word* e; /* [m] */ word* t; /* [ec->d * ec->f->n] */ word* pt; /* [2 * ec->f->n] */ // pre @@ -735,14 +736,14 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, // разметить стек memSlice(stack, ecMulPreSOA_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &dd, &t, &pt, &stack); - // dd <- (d % 2) ? d : ec->order - d + &e, &t, &pt, &stack); + // e <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); - zzSubIf(dd, ec->order, d, m, neg); - ASSERT(zzIsOdd(dd, m)); + zzSubIf(e, ec->order, d, m, neg); + ASSERT(zzIsOdd(e, m)); // обработать старшую цифру pos = (mb - 1) / pre->w, pos *= pre->w; - digit = wwGetBits(dd, pos, mb - pos); + digit = wwGetBits(e, pos, mb - pos); wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); borrow = WORD_1 - (digit & 1); // t <- 2^{w-1} pt @@ -756,7 +757,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, while (pos -= pre->w) { // удвоить со сложением - digit = wwGetBits(dd, pos, pre->w); + digit = wwGetBits(e, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); ecSgnA(pt, borrow, ec, stack); @@ -769,7 +770,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, // завершающее удвоение ecDbl(t, t, ec, stack); // финишное сложение - digit = wwGetBits(dd, 0, pre->w); + digit = wwGetBits(e, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); @@ -792,12 +793,12 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } -#define ecMulPreSOH_local(n, ec_d, m)\ -/* dd */ O_OF_W(m),\ +#define ecMulPreOD_local(n, ec_d, m)\ +/* e */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(2 * n) -bool_t ecMulPreSOH(word b[], const ec_pre_t* pre, const ec_o* ec, +bool_t ecMulPreOD(word b[], const ec_pre_t* pre, const ec_o* ec, const word d[], size_t m, void* stack) { register word neg; @@ -810,14 +811,14 @@ bool_t ecMulPreSOH(word b[], const ec_pre_t* pre, const ec_o* ec, size_t mask; size_t pos; const word* row; - word* dd; /* [m] */ + word* e; /* [m] */ word* t; /* [ec->d * ec->f->n] */ word* pt; /* [2 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec)); ASSERT(ecGroupIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_soh); + ASSERT(pre->type == ec_pre_od); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); ASSERT(zzIsOdd(ec->order, m) && m > 1); ASSERT(wwCmp(d, ec->order, m) < 0); @@ -829,25 +830,25 @@ bool_t ecMulPreSOH(word b[], const ec_pre_t* pre, const ec_o* ec, mask = WORD_BIT_POS(pre->w) - 2; // разметить стек memSlice(stack, - ecMulPreSOH_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &dd, &t, &pt, &stack); - // dd <- (d % 2) ? d : ec->order - d + ecMulPreOD_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &e, &t, &pt, &stack); + // e <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); - zzSubIf(dd, ec->order, d, m, neg); - ASSERT(zzIsOdd(dd, m)); + zzSubIf(e, ec->order, d, m, neg); + ASSERT(zzIsOdd(e, m)); // позиция старшей цифры, старшая строка pre pos = (mb - 1) / pre->w; row = pre->pts + pos * row_count * pt_size; pos *= pre->w; // обработать старшую цифру - digit = wwGetBits(dd, pos, mb - pos); + digit = wwGetBits(e, pos, mb - pos); wwSel(pt, row, row_count, pt_size, digit >> 1); ecFromA(t, pt, ec, stack); borrow = WORD_1 - (digit & 1); // обработать остальные цифры while (pos -= pre->w) { - digit = wwGetBits(dd, pos, pre->w); + digit = wwGetBits(e, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; row -= row_count * pt_size; wwSel(pt, row, row_count, pt_size, digit >> 1); @@ -856,7 +857,7 @@ bool_t ecMulPreSOH(word b[], const ec_pre_t* pre, const ec_o* ec, borrow = WORD_1 - (digit & 1); } // финишное сложение - digit = wwGetBits(dd, 0, pre->w); + digit = wwGetBits(e, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; wwSel(pt, pre->pts, row_count, pt_size, digit >> 1); @@ -871,10 +872,10 @@ bool_t ecMulPreSOH(word b[], const ec_pre_t* pre, const ec_o* ec, return ret; } -size_t ecMulPreSOH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) { return memSliceSize( - ecMulPreSOH_local(n, ec_d, m), + ecMulPreOD_local(n, ec_d, m), ec_deep, SIZE_MAX); } @@ -933,7 +934,7 @@ order - d. */ #define ecMulPreSI_local(n, ec_d, m)\ -/* dd */ O_OF_W(m + 1),\ +/* e */ O_OF_W(m + 1),\ /* t */ O_OF_W(ec_d * n),\ /* pt */ O_OF_W(2 * n) @@ -950,7 +951,7 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, size_t mask; size_t pos; size_t i; - word* dd; /* [W_OF_B(pre->h * pre->w)] */ + word* e; /* [W_OF_B(pre->h * pre->w)] */ word* t; /* [ec->d * ec->f->n] */ word* pt; /* [2 * ec->f->n] */ // pre @@ -971,22 +972,22 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, // разметить стек memSlice(stack, ecMulPreSI_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &dd, &t, &pt, &stack); - // dd <- (d % 2) ? d : ec->order - d + &e, &t, &pt, &stack); + // e <- (d % 2) ? d : ec->order - d neg = WORD_1 - (d[0] & 1); - dd[m] = 0; - zzSubIf(dd, ec->order, d, m, neg); - ASSERT(zzIsOdd(dd, m)); - // перекодировать dd + e[m] = 0; + zzSubIf(e, ec->order, d, m, neg); + ASSERT(zzIsOdd(e, m)); + // перекодировать e m = W_OF_B(mb); - wwNeg(dd, m); + wwNeg(e, m); if (i = mb % B_PER_W) - dd[m - 1] <<= (B_PER_W - i), dd[m - 1] >>= (B_PER_W - i); - wwShLo(dd, m, 1); + e[m - 1] <<= (B_PER_W - i), e[m - 1] >>= (B_PER_W - i); + wwShLo(e, m, 1); // сформировать старшую цифру digit = 0; for (i = 1; i < pre->w; ++i) - digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - 1); + digit <<= 1, digit ^= (word)wwTestBit(e, mb - i * pre->h - 1); ASSERT((digit & mask) == digit); // обработать старшую цифру wwSel(pt, pre->pts, pre_count, pt_size, digit); @@ -995,9 +996,9 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, for (pos = 2; pos < pre->h; ++pos) { // сформировать цифру - hi = (word)wwTestBit(dd, mb - pos); + hi = (word)wwTestBit(e, mb - pos); for (digit = 0, i = 1; i < pre->w; ++i) - digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - pos); + digit <<= 1, digit ^= (word)wwTestBit(e, mb - i * pre->h - pos); digit ^= (WORD_0 - hi) & mask; // удвоить и сложить wwSel(pt, pre->pts, pre_count, pt_size, digit); @@ -1007,9 +1008,9 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, // удвоить ecDbl(t, t, ec, stack); // сформировать последнюю цифру - hi = (word)wwTestBit(dd, mb - pos); + hi = (word)wwTestBit(e, mb - pos); for (digit = 0, i = 1; i < pre->w; ++i) - digit <<= 1, digit ^= (word)wwTestBit(dd, mb - i * pre->h - pos); + digit <<= 1, digit ^= (word)wwTestBit(e, mb - i * pre->h - pos); digit ^= (WORD_0 - hi) & mask; // финишное сложение wwSel(pt, pre->pts, pre_count, pt_size, digit); diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index cb03302f..587e15ac 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.16 +\version 2026.02.20 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -15,6 +15,197 @@ version 3. See Copyright Notices in bee2/info.h. #include "bee2/math/gfp.h" #include "bee2/math/ww.h" #include "bee2/math/zz.h" +#include "ecp_lcl.h" + +/* +******************************************************************************* +Арифметика Co-Z + +Aрифметика Co-Z --- это формулы сложения якобиевых точек с совпадающими +Z-координатами. Арифметика предложена в [Mel06]. Детальные и дополнительные +алгоритмы представлены в [Riv11]. + +В функции ecpIDblAJ() выполняется удвоение (J, J) <- 2A. Результатом удвоения +аффинной точки a являются якобиевы точки a1 и b такие, что: +- a1 == a; +- b == 2a; +- ecZ(a1, n) == ecZ(b, n). +Реализован модифицированный алгоритм XYCZ-IDBL из [Riv11]. Модификация состоит +в сокращении числа переменных и отказе от двух удвоений взамен на одно +уполовинивание. Сложность алгоритма: + 2M + 4S + 6add + 1half + 2*2 \approx 6M. + +В функции ecpReaddJ() выполняется сложение (J, J) <- J + J. Предполагается, +что это сложение выполняется сразу после предыдущего такого же сложения +или после операции (J, J) <- 2A и тогда у слагаемых совпадают z-координаты. +Результатом сложения якобиевых точек a и b с одинаковыми z-координатами +являются якобиевы точки a1 и c такие, что: +- a1 == a; +- c == a + b; +- ecZ(a1, n) == ecZ(c, n). +Реализован модифицированный алгоритм XYCZ-ADD из [Riv11]. Модификация состоит +в сокращении числа переменных и дополнительном умножении для вычисления +выходной z-координаты. Сложность алгоритма: + 5M + 2S + 7add \approx 7M. + +\pre В ecpReaddJ() у точек a и b совпадают z-координаты. + +\expect В ecpIDblAJ() у точки a ненулевая y-координата, т.е. в результате +удвоения не может быть получена точка O. + +\expect В ecpReaddJ() точки a и b отличаются. + +\post В ecpIDblAJ() у точек b и a1 совпадают z-координаты. + +\post В ecpReaddJ() у точек c и a1 совпадают z-координаты. + +[Mel07] Meloni N. New Point Addition Formulae for ECC Applications. + In: Arithmetic of Finite Fields, First International Workshop -- + WAIFI 2007, volume 4547 of Lecture Notes in Computer Science, + pp. 189–201. Springer, 2007. +[Riv11] Rivain M. Fast and Regular Algorithms for Scalar Multiplication + over Elliptic Curves. Cryptology ePrint Archive, Report 2011/338. + https://eprint.iacr.org/2011/338. +******************************************************************************* +*/ + +#define ecpIDblAJ_local(n)\ +/* t3 */ O_OF_W(n),\ +/* t4 */ O_OF_W(n) + +// ([3n]b, [3n]a1) <- (2[2n]a, [3n]a) ((J, J) <- 2A) +static void ecpIDblAJ(word b[], word a1[], const word a[], const ec_o* ec, + void* stack) +{ + size_t n; + word* t3; /* [n] */ + word* t4; /* [n] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnA(a, ec)); + ASSERT(a == b || wwIsDisjoint2(a, 2 * ec->f->n, b, 3 * ec->f->n)); + ASSERT(a == a1 || wwIsDisjoint2(a, 2 * ec->f->n, a1, 3 * ec->f->n)); + ASSERT(wwIsDisjoint2(a1, 3 * ec->f->n, b, 3 * ec->f->n)); + EXPECT(!wwIsZero(ecY(a, ec->f->n), ec->f->n)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpIDblAJ_local(n), SIZE_0, SIZE_MAX, + &t3, &t4, &stack); + // t3 <- xa^2 [x1^2] + qrSqr(t3, ecX(a), ec->f, stack); + // t4 <- 2 t3 [2x1^2] + gfpDouble(t4, t3, ec->f); + // t3 <- t3 + t4 [3x1^2] + zmAdd(t3, t3, t4, ec->f); + // t3 <- t3 + A [3x1^2 + A = BB] + zmAdd(t3, t3, ec->A, ec->f); + // zb <- 2 ya [2y1 = z2], za1 <- zb + gfpDouble(ecZ(b, n), ecY(a, n), ec->f); + wwCopy(ecZ(a1, n), ecZ(b, n), n); + // t4 <- zb^2 [2y1^2] + qrSqr(t4, ecZ(b, n), ec->f, stack); + // t4 <- t4^2 [4y1^2] + qrSqr(t4, t4, ec->f, stack); + // xa1 <- t4 xa [4x1y1^2 = AA] + qrMul(ecX(a1), t4, ecX(a), ec->f, stack); + // xb <- t3^2 [BB^2] + qrSqr(ecX(b), t3, ec->f, stack); + // xb <- xb - xa1 [BB^2 - AA] + zmSub(ecX(b), ecX(b), ecX(a1), ec->f); + // xb <- xb - xa1 [BB^2 - 2AA = x2] + zmSub(ecX(b), ecX(b), ecX(a1), ec->f); + // yb <- xa1 - xb [AA - x2] + zmSub(ecY(b, n), ecX(a1), ecX(b), ec->f); + // yb <- yb t3 [BB(AA - x2)] + qrMul(ecY(b, n), ecY(b, n), t3, ec->f, stack); + // ya1 <- t4^2 [16y1^4] + qrSqr(ecY(a1, n), t4, ec->f, stack); + // ya1 <- ya1/2 [8y1^4 = y1'] + gfpHalf(ecY(a1, n), ecY(a1, n), ec->f); + // yb <- yb - ya1 [BB(AA - x2) - 8y1^4 = y2] + zmSub(ecY(b, n), ecY(b, n), ecY(a1, n), ec->f); + // post + ASSERT(wwEq(ecZ(b, n), ecZ(a1, n), n)); +} + +static size_t ecpIDblAJ_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpIDblAJ_local(n), + f_deep, + SIZE_MAX); +} + +#define ecpReaddJ_local(n)\ +/* t3 */ O_OF_W(n),\ +/* t4 */ O_OF_W(n),\ +/* t5 */ O_OF_W(n) + +// ([3n]c, [3n]a1) <- ([3n]a + [3n]b, [3n]a) ((J, J) <- J + J) +static void ecpReaddJ(word c[], word a1[], const word a[], const word b[], + const ec_o* ec, void* stack) +{ + size_t n; + word* t3; /* [n] */ + word* t4; /* [n] */ + word* t5; /* [n] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnJ(a, ec)); + ASSERT(ecpSeemsOnJ(b, ec)); + ASSERT(wwIsSameOrDisjoint(a, c, 3 * ec->f->n)); + ASSERT(wwIsSameOrDisjoint(b, c, 3 * ec->f->n)); + ASSERT(wwIsSameOrDisjoint(a, a1, 3 * ec->f->n)); + ASSERT(wwIsSameOrDisjoint(b, a1, 3 * ec->f->n)); + ASSERT(wwIsDisjoint(c, a1, 3 * ec->f->n)); + ASSERT(wwEq(ecZ(a, ec->f->n), ecZ(b, ec->f->n), ec->f->n)); + EXPECT(!wwEq(a, b, 2 * ec->f->n)); + // разметить стек + n = ec->f->n; + memSlice(stack, + ecpReaddJ_local(n), SIZE_0, SIZE_MAX, + &t3, &t4, &t5, &stack); + // t5 <- xb - xa [x2 - x1] + zmSub(t3, t3, t4, ec->f); + // zc <- t5 za [z(x2 - x1) = z3], za1 <- zc + qrMul(ecZ(c, n), t5, ecZ(a, n), ec->f, stack); + wwCopy(ecZ(a1, n), ecZ(c, n), n); + // t5 <- t5^2 [(x2 - x1)^2 = AA] + qrSqr(t3, ecX(a), ec->f, stack); + // t3 <- t5 xb [x2 AA = CC] + qrMul(t3, t5, ecX(b), ec->f, stack); + // xa1 <- t5 xa [x1 AA = BB] + qrMul(ecX(a1), t5, ecX(a), ec->f, stack); + // t4 <- yb - ya [y2 - y1] + zmSub(t4, ecY(b, n), ecY(a, n), ec->f); + // t5 <- t4^2 [(y2 - y1)^2 = DD] + qrSqr(t5, t4, ec->f, stack); + // t5 <- t5 - xa1 [DD - BB] + zmSub(t5, t5, ecX(a1), ec->f); + // xc <- t5 - t3 [DD - BB - CC = x3] + zmSub(ecX(c), t5, t3, ec->f); + // t3 <- t3 - xa1 [CC - BB] + zmSub(t3, t3, ecX(a1), ec->f); + // ya1 <- ya t3 [y1(CC - BB)] + qrMul(ecY(a1, n), ecY(a, n), t3, ec->f, stack); + // t3 <- xa1 - xc [BB - x3] + zmSub(t3, ecX(a1), ecX(c), ec->f); + // t4 <- t4 t3 [(y2 - y1)(BB - x3)] + qrMul(t4, t4, t3, ec->f, stack); + // yc <- t4 - ya1 [(y2 - y1)(BB - x3) - y1(CC - BB) = y3] + zmSub(ecY(c, n), t4, ecY(a1, n), ec->f); + // post + ASSERT(wwEq(ecZ(c, n), ecZ(a1, n), n)); +} + +static size_t ecpReaddJ_deep(size_t n, size_t f_deep) +{ + return memSliceSize( + ecpReaddJ_local(n), + f_deep, + SIZE_MAX); +} /* ******************************************************************************* @@ -37,284 +228,51 @@ version 3. See Copyright Notices in bee2/info.h. /* ******************************************************************************* Предвычисления: схема SO - -Реализован алгоритм SmallMultJ, предложенный в [APS22]. Алгоритм основан на -многочленах деления и требует примерно 19/2 M + 7/2 S операций на каждую -кратную точку. - -Алгоритм выполняется в 3 этапа. Этапам соответствуют следующие шаги алгоритма: -- этап 1 -- шаги 1 -- 16 (расчет начальных выражений); -- этап 2 -- шаги 17, 18, 20 (расчет основных выражений, формирование первой - половины точек); -- этап 3 -- шаги 19, 21, 22 (формирование оставшихся точек). - -Для повышения читабельности используются макросы, которые покрывают следующие -выражения: -1. W(i) -- значения многочленов Wᵢ, i = 3, 4, ..., 2ʷ + 1: - * память: 2ʷ - 1 (элементов поля); - * вычисляются на этапах 1 и 2, используются на этапах 1--3; - * кэшируются все значения. -2. W2(i) -- квадраты Wᵢ², i = 3, 4, ..., 2ʷ: - * память: 2ʷ - 2. -3. WW(i) -- произведения WᵢWᵢ₊₂, i = 1, 2, ..., 2ʷ⁻¹: - * память: 2; - * вычисляются на этапах 1 и 2, используются на этапах 1--3; - * кэшируются все значения. -4. WWy2(i) -- произведения (2y)²WᵢWᵢ₊₂, i = 2, 4, ..., 2ʷ⁻¹: - * память: 2; - * вычисляются и используются на этапах 1 и 2; - * кэшируются 2 последовательных значения: текущее и предыдущее. -5. WWy4(i) -- произведения (2y)⁴WᵢWᵢ₊₂, i = 2, 4, ..., 2ʷ⁻¹: - * память: 1; - * вычисляются и используются на этапах 1 и 2; - * кэшируется только текущее значение. -6. WWW(i) -- выражения Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁², i = 3, 4, ..., 2ʷ⁻¹: - * память: 2ʷ⁻¹ - 2; - * вычисляются на этапах 1 и 2, используются на этапах 1--3; - * кэшируются все значения. - -[APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication - on Weierstrass curves using division polynomials. Mat. Vopr. - Kriptogr., 13:2, 2022, https://doi.org/10.4213/mvk406. ******************************************************************************* */ -#if defined(W) || defined(W2) || defined(WW) || defined(WWy2) ||\ - defined(WWy4) || defined(WWW) - #error "Conflicting preprocessor definitions" -#endif - -#define ecpPreSO_local(n, w)\ -/* t */ O_OF_W(n),\ -/* dy2 */ O_OF_W(n),\ -/* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ -/* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ -/* WWs */ O_OF_W(2 * n),\ -/* WWy2s */ O_OF_W(2 * n),\ -/* WWy4s */ O_OF_W(n),\ -/* WWWs */ O_OF_W((SIZE_BIT_POS(w - 1) - 2) * n) +#define ecpPreSO_local(n, ec_d)\ +/* t */ O_OF_W(ec_d * n) void ecpPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { - size_t n; - word* t; /* [n] */ - word* dy2; /* [n] (2y)² */ - word* Ws; /* [(2^w - 1) * n] Wᵢ */ - word* W2s; /* [(2^w - 2) * n] Wᵢ² */ - word* WWs; /* [2 * n] WᵢWᵢ₊₂ */ - word* WWy2s; /* [2 * n] (2y)²WᵢWᵢ₊₂ */ - word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ - word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁² */ + word* t; /* [ec->d * ec->f->n] */ size_t i; - word* pt; // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(w >= 3); + ASSERT(ecIsOperable(ec)); + ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(3 * ec->f->n * SIZE_BIT_POS(w - 1)), + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), a, O_OF_W(2 * ec->f->n))); - // размерности - n = ec->f->n; // разметить стек memSlice(stack, - ecpPreSO_local(n, w), SIZE_0, SIZE_MAX, - &t, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &WWWs, &stack); - // первая точка - pt = pre->pts; - ecFromA(pt, a, ec, stack); - pt += 3 * n; - -/*** SmallMultJ: begin ***/ -#define W(i) (Ws + ((i) - 3) * n) -#define W2(i) (W2s + ((i) - 3) * n) -#define WW(i) (WWs + ((i) % 2) * n) -#define WWy2(i) (WWy2s + ((i) % 2) * n) -#define WWy4(i) (WWy4s) -#define WWW(i) (WWWs + ((i) - 3) * n) - -/* SmallMultJ: этап 1 */ - - // dy2 <- (2y)² - gfpDouble(dy2, ecY(a, n), ec->f); - qrSqr(dy2, dy2, ec->f, stack); - // W₃, W₄ - { - word* t1 = W2s; - word* x2 = t1 + n; - word* bx = x2 + n; - word* a2 = bx + n; - // (x2, bx, a2) <- (x², b x, A²) - qrSqr(x2, ecX(a), ec->f, stack); - qrMul(bx, ec->B, ecX(a), ec->f, stack); - qrSqr(a2, ec->A, ec->f, stack); - // [W₃] <- 3(x²+A)²−4(a²−3Bx) - qrAdd(t, x2, ec->A, ec->f); // x²+A - qrSqr(t, t, ec->f, stack); // (x²+A)² - gfpDouble(t1, t, ec->f); // 2(x²+A)² - qrAdd(W(3), t, t1, ec->f); // 3(x²+A)² - gfpDouble(t, bx, ec->f); // 2Bx - qrAdd(t, t, bx, ec->f); // 3Bx - qrSub(t, a2, t, ec->f); // A²−3Bx - gfpDouble(t, t, ec->f); // 2(A²−3Bx) - gfpDouble(t, t, ec->f); // 4(A²−3Bx) - qrSub(W(3), W(3), t, ec->f); - // [W₄] <- 4Bx(5x²-A) - gfpDouble(t, x2, ec->f); // 2x² - gfpDouble(t, t, ec->f); // 4x² - qrAdd(t, t, x2, ec->f); // 5x² - qrSub(t, t, ec->A, ec->f); // 5x²-A - qrMul(t, bx, t, ec->f, stack); // Bx(5x²-A) - gfpDouble(t, t, ec->f); // 2Bx(5x²-A) - gfpDouble(W(4), t, ec->f); // 4Bx(5x²-A) - // [W₄] <- x⁶+4Bx(5x²-A) - qrMul(t, x2, ecX(a), ec->f, stack); - qrSqr(t1, t, ec->f, stack); - qrAdd(W(4), t1, W(4), ec->f); - // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) - qrMul(t1, ec->A, ecX(a), ec->f, stack); // Ax - qrSub(t, t, t1, ec->f); // x³-Ax - qrMul(t, t, t1, ec->f, stack); // Ax(x³-Ax) - gfpDouble(t1, t, ec->f); // 2Ax(x³-Ax) - gfpDouble(t1, t1, ec->f); // 4Ax(x³-Ax) - qrAdd(t, t, t1, ec->f); // 5Ax(x³-Ax) - qrAdd(W(4), W(4), t, ec->f); - // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² - qrSqr(t, ec->B, ec->f, stack); // B² - gfpDouble(t, t, ec->f); // 2B² - gfpDouble(t, t, ec->f); // 4B² - gfpDouble(t, t, ec->f); // 8B² - qrSub(W(4), W(4), t, ec->f); - // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) - qrMul(t1, a2, ec->A, ec->f, stack); // A³ - qrSub(W(4), W(4), t1, ec->f); - gfpDouble(W(4), W(4), ec->f); - } - // [W₃²] <- W₃² - qrSqr(W2(3), W(3), ec->f, stack); - // [W₄²] <- W₄² - qrSqr(W2(4), W(4), ec->f, stack); - // [W₁W₃] <- W₃ - qrCopy(WW(1), W(3), ec->f); - // [W₂W₄] <- W₄ - qrCopy(WW(2), W(4), ec->f); - // [(2y)²W₂W₄] <- (2y)²(W₂W₄) - qrMul(WWy2(2), dy2, WW(2), ec->f, stack); - // [(2y)⁴W₂W₄] <- (2y)²((2y)²W₂W₄) - qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); - // [W₅] <- (2y)⁴W₂W₄−(W₁W₃)W₃² - qrMul(t, WW(1), W2(3), ec->f, stack); - qrSub(W(5), WWy4(2), t, ec->f); - // [W₅²] <- W₅² - qrSqr(W2(5), W(5), ec->f, stack); - // [W₅W₂² − W₁W₄²] <- W₅-W₄² - qrSub(WWW(3), W(5), W2(4), ec->f); - // [W₆] <- W₃(W₅W₂²−W₁W₄²) - qrMul(W(6), W(3), WWW(3), ec->f, stack); - // [W₆W₃²−W₂W₅²] <- (W₆W₃)W₃-W₅² - qrMul(WWW(4), W(6), W2(3), ec->f, stack); - qrSub(WWW(4), WWW(4), W2(5), ec->f); - -/* SmallMultJ: этап 2 */ - - // W₂ᵢ, W₂ᵢ₊₁, Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁², WᵢWᵢ₊₂ для i=3,4,...,2ʷ⁻¹ - // (Xᵢ:Yᵢ:Zᵢ) для нечетных i - for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) - { - // [Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁²] <- Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁² - if (i >= 5) - { - qrMul(WWW(i), W(i + 2), W2(i - 1), ec->f, stack); - qrMul(t, W(i - 2), W2(i + 1), ec->f, stack); - qrSub(WWW(i), WWW(i), t, ec->f); - } - // [W₂ᵢ] <- Wᵢ(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) - if (i >= 4) - qrMul(W(2 * i), W(i), WWW(i), ec->f, stack); - // [W₂ᵢ²] <- W₂ᵢ² - qrSqr(W2(2 * i), W(2 * i), ec->f, stack); - // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 - gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); - // W₂ᵢ₊₁ - //(Xᵢ:Yᵢ:Zᵢ) при нечетном i - if (i & 1) - { - // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² - qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); - qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); - qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); - // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ - qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); - qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); - // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) - qrMul(ecY(pt, n), ecY(a, n), WWW(i), ec->f, stack); - // [Zᵢ] <- Wᵢ - wwCopy(ecZ(pt, n), W(i), n); - // к следующей точке - pt += 3 * n; - } - else - { - // [(2y)²WᵢWᵢ₊₂] <- (2y)²WᵢWᵢ₊₂ - qrMul(WWy2(i), dy2, WW(i), ec->f, stack); - // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²((2y)²WᵢWᵢ₊₂) - qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); - // [W₂ᵢ₊₁] <- ((2y)⁴WᵢWᵢ₊₂)Wᵢ²-(Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² - qrMul(t, WW(i - 1), W2(i + 1), ec->f, stack); - qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); - qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); - } - // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² - if (i != SIZE_BIT_POS(w - 1)) - qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); - } - // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ для i = 2ʷ⁻¹ + 1 - ASSERT(i == SIZE_BIT_POS(w - 1) + 1); - qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); - qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); - -/* SmallMultJ: этап 3 */ - - // (Xᵢ:Yᵢ:Zᵢ) для i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 - while (1) + ecpPreSO_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &t, &stack); + // вычислить малые кратные + if (w > 1) { - // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) - qrMul(t, W(i + 2), W2(i - 1), ec->f, stack); - qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); - qrSub(ecY(pt, n), t, ecY(pt, n), ec->f); - qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); - // [Zᵢ] <- Wᵢ - wwCopy(ecZ(pt, n), W(i), n); - // последняя точка? - if (i == SIZE_BIT_POS(w) - 1) - break; - // к следующей точке - i += 2, pt += 3 * n; - // [Xᵢ] <- x Wᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ - gfpMul2(t, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); - qrMul(t, dy2, t, ec->f, stack); - qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); - qrSub(ecX(pt), ecX(pt), t, ec->f); + // (t, pre[0]) <- (2a, a) + ecpIDblAJ(t, ecPrePt(pre, 0, ec), a, ec, stack); + // pre[i] <- t + pre[i - 1] (с обновлением t) + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + ecpReaddJ(ecPrePt(pre, i, ec), t, t, ecPrePt(pre, i - 1, ec), + ec, stack); } - -#undef WWW -#undef WWy4 -#undef WWy2 -#undef WW -#undef W2 -#undef W -/* SmallMultJ: end */ - + else + ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); // заполнить служебные поля pre->type = ec_pre_so; pre->w = w, pre->h = 0; } -size_t ecpPreSO_deep(size_t n, size_t f_deep, size_t w) +size_t ecpPreSO_deep(size_t n, size_t ec_d, size_t f_deep) { return memSliceSize( - ecpPreSO_local(n, w), - f_deep, + ecpPreSO_local(n, ec_d), + utilMax(2, + ecpIDblAJ_deep(n, f_deep), + ecpReaddJ_deep(n, f_deep)), SIZE_MAX); } diff --git a/test/math/ec_test.c b/test/math/ec_test.c index 69a8aa04..66416edd 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves \project bee2/test \created 2026.02.12 -\version 2026.02.16 +\version 2026.02.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -115,11 +115,11 @@ bool_t ecTestEc(const ec_o* ec) ecMulA_deep(n, ec->d, ec->deep, n), ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), - ecPreSOH_deep(n, ec->d, ec->deep), + ecPreOD_deep(n, ec->d, ec->deep), ecPreSI_deep(n, ec->d, max_h, ec->deep), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), - ecMulPreSOH_deep(n, ec->d, ec->deep, n), + ecMulPreOD_deep(n, ec->d, ec->deep, n), ecMulPreSI_deep(n, ec->d, ec->deep, n), ecAddMulA_deep(n, ec->d, ec->deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), @@ -517,21 +517,21 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } - // кратная точка: ecMulA vs ecMulPreSOH + // кратная точка: ecMulA vs ecMulPreOD for (w = min_w; w <= max_w; ++w) { const size_t m = wwWordSize(ec->order, n + 1); const size_t h = (B_OF_O(no) + w - 1) / w; wwCopy(d, ec->order, m); zzSubW2(d, m, 1); - if (!ecPreSOH(pre, ec->base, w, h, ec, stack)) + if (!ecPreOD(pre, ec->base, w, h, ec, stack)) { blobClose(state); return FALSE; } while (!wwIsZero(d, m)) { - if (!ecMulPreSOH(pt0, pre, ec, d, m, stack) || + if (!ecMulPreOD(pt0, pre, ec, d, m, stack) || !ecMulA(pt1, ec->base, ec, d, m, stack) || !wwEq(pt0, pt1, 2 * m)) { @@ -540,7 +540,7 @@ bool_t ecTestEc(const ec_o* ec) } wwShLo(d, m, 34); } - if (ecMulPreSOH(pt0, pre, ec, d, m, stack) || + if (ecMulPreOD(pt0, pre, ec, d, m, stack) || ecMulA(pt1, ec->base, ec, d, m, stack)) { blobClose(state); diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 2a7fd3e1..339d96a9 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.16 +\version 2026.02.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -50,13 +50,13 @@ static bool_t ecpBenchEc(const ec_o* ec) ecMulA_deep(n, ec->d, ec->deep, n), ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), - ecPreSOH_deep(n, ec->d, ec->deep), + ecPreOD_deep(n, ec->d, ec->deep), ecPreSI_deep(n, ec->d, ec->deep, max_h), - ecpPreSO_deep(n, ec->f->deep, max_w), + ecpPreSO_deep(n, ec->d, ec->f->deep), ecpPreSOA_deep(n, ec->f->deep, max_w), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), - ecMulPreSOH_deep(n, ec->d, ec->deep, n), + ecMulPreOD_deep(n, ec->d, ec->deep, n), ecMulPreSI_deep(n, ec->d, ec->deep, n)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); @@ -142,18 +142,18 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); } - // ecMulPre[SOH] + // ecMulPre[OD] for (w = min_w; w <= max_w; ++w) { size_t h = (B_OF_O(no) + w - 1) / w; - ecPreSOH(pre, ec->base, w, h, ec, stack); + ecPreOD(pre, ec->base, w, h, ec, stack); for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecMulPreSOH(pt, pre, ec, d, n, stack); + ecMulPreOD(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; - printf(" ecMulPre[SOH,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecMulPre[OD,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)(ticks / reps), (unsigned)tmSpeed(reps, ticks)); diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index bff9dacc..89bb7c89 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.12 +\version 2026.02.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -13,18 +13,653 @@ #include #include #include +#include #include #include #include +/* +******************************************************************************* +Ускорители + +В gfpMul2 выражение a * b при известных a^2 и b^2 вычисляется как + ((a + b)^2 - a^2 - b^2) / 2 +******************************************************************************* +*/ + +#define gfpMul2(c, a, b, a2, b2, f, stack)\ + do {\ + qrAdd(c, a, b, f);\ + qrSqr(c, c, f, stack);\ + qrSub(c, c, a2, f);\ + qrSub(c, c, b2, f);\ + gfpHalf(c, c, f);\ + } while(0) + +/* +******************************************************************************* +Предвычисления: SmallMultJ + +Реализован алгоритм SmallMultJ, предложенный в [APS22]. Алгоритм основан на +многочленах деления и требует примерно 19/2 M + 7/2 S операций на каждую +кратную точку. + +Алгоритм выполняется в 3 этапа. Этапам соответствуют следующие шаги алгоритма: +- этап 1 -- шаги 1 -- 16 (расчет начальных выражений); +- этап 2 -- шаги 17, 18, 20 (расчет основных выражений, формирование первой + половины точек); +- этап 3 -- шаги 19, 21, 22 (формирование оставшихся точек). + +Для повышения читабельности используются макросы, которые покрывают следующие +выражения: +1. W(i) -- значения многочленов Wᵢ, i = 3, 4, ..., 2ʷ + 1: + * память: 2ʷ - 1 (элементов поля); + * вычисляются на этапах 1 и 2, используются на этапах 1--3; + * кэшируются все значения. +2. W2(i) -- квадраты Wᵢ², i = 3, 4, ..., 2ʷ: + * память: 2ʷ - 2. +3. WW(i) -- произведения WᵢWᵢ₊₂, i = 1, 2, ..., 2ʷ⁻¹: + * память: 2; + * вычисляются на этапах 1 и 2, используются на этапах 1--3; + * кэшируются все значения. +4. WWy2(i) -- произведения (2y)²WᵢWᵢ₊₂, i = 2, 4, ..., 2ʷ⁻¹: + * память: 2; + * вычисляются и используются на этапах 1 и 2; + * кэшируются 2 последовательных значения: текущее и предыдущее. +5. WWy4(i) -- произведения (2y)⁴WᵢWᵢ₊₂, i = 2, 4, ..., 2ʷ⁻¹: + * память: 1; + * вычисляются и используются на этапах 1 и 2; + * кэшируется только текущее значение. +6. WWW(i) -- выражения Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁², i = 3, 4, ..., 2ʷ⁻¹: + * память: 2ʷ⁻¹ - 2; + * вычисляются на этапах 1 и 2, используются на этапах 1--3; + * кэшируются все значения. + +[APS22] Agievich S., Poruchnik S., Semenov V. Small scalar multiplication + on Weierstrass curves using division polynomials. Mat. Vopr. + Kriptogr., 13:2, 2022, https://doi.org/10.4213/mvk406. +******************************************************************************* +*/ + +#if defined(W) || defined(W2) || defined(WW) || defined(WWy2) ||\ + defined(WWy4) || defined(WWW) +#error "Conflicting preprocessor definitions" +#endif + +#define ecpSmallMultJ_local(n, w)\ +/* t */ O_OF_W(n),\ +/* dy2 */ O_OF_W(n),\ +/* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ +/* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ +/* WWs */ O_OF_W(2 * n),\ +/* WWy2s */ O_OF_W(2 * n),\ +/* WWy4s */ O_OF_W(n),\ +/* WWWs */ O_OF_W((SIZE_BIT_POS(w - 1) - 2) * n) + +static void ecpSmallMultJ(ec_pre_t* pre, const word a[], size_t w, + const ec_o* ec, void* stack) +{ + size_t n; + word* t; /* [n] */ + word* dy2; /* [n] (2y)² */ + word* Ws; /* [(2^w - 1) * n] Wᵢ */ + word* W2s; /* [(2^w - 2) * n] Wᵢ² */ + word* WWs; /* [2 * n] WᵢWᵢ₊₂ */ + word* WWy2s; /* [2 * n] (2y)²WᵢWᵢ₊₂ */ + word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ + word* WWWs; /* [(2^{w-1} - 2) * n] Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁² */ + size_t i; + word* pt; + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(w >= 3); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(3 * ec->f->n * SIZE_BIT_POS(w - 1)), + a, O_OF_W(2 * ec->f->n))); + // размерности + n = ec->f->n; + // разметить стек + memSlice(stack, + ecpSmallMultJ_local(n, w), SIZE_0, SIZE_MAX, + &t, &dy2, &Ws, &W2s, &WWs, &WWy2s, &WWy4s, &WWWs, &stack); + // первая точка + pt = pre->pts; + ecFromA(pt, a, ec, stack); + pt += 3 * n; + +/*** SmallMultJ: begin ***/ +#define W(i) (Ws + ((i) - 3) * n) +#define W2(i) (W2s + ((i) - 3) * n) +#define WW(i) (WWs + ((i) % 2) * n) +#define WWy2(i) (WWy2s + ((i) % 2) * n) +#define WWy4(i) (WWy4s) +#define WWW(i) (WWWs + ((i) - 3) * n) + +/* SmallMultJ: этап 1 */ + + // dy2 <- (2y)² + gfpDouble(dy2, ecY(a, n), ec->f); + qrSqr(dy2, dy2, ec->f, stack); + // W₃, W₄ + { + word* t1 = W2s; + word* x2 = t1 + n; + word* bx = x2 + n; + word* a2 = bx + n; + // (x2, bx, a2) <- (x², b x, A²) + qrSqr(x2, ecX(a), ec->f, stack); + qrMul(bx, ec->B, ecX(a), ec->f, stack); + qrSqr(a2, ec->A, ec->f, stack); + // [W₃] <- 3(x²+A)²−4(a²−3Bx) + qrAdd(t, x2, ec->A, ec->f); // x²+A + qrSqr(t, t, ec->f, stack); // (x²+A)² + gfpDouble(t1, t, ec->f); // 2(x²+A)² + qrAdd(W(3), t, t1, ec->f); // 3(x²+A)² + gfpDouble(t, bx, ec->f); // 2Bx + qrAdd(t, t, bx, ec->f); // 3Bx + qrSub(t, a2, t, ec->f); // A²−3Bx + gfpDouble(t, t, ec->f); // 2(A²−3Bx) + gfpDouble(t, t, ec->f); // 4(A²−3Bx) + qrSub(W(3), W(3), t, ec->f); + // [W₄] <- 4Bx(5x²-A) + gfpDouble(t, x2, ec->f); // 2x² + gfpDouble(t, t, ec->f); // 4x² + qrAdd(t, t, x2, ec->f); // 5x² + qrSub(t, t, ec->A, ec->f); // 5x²-A + qrMul(t, bx, t, ec->f, stack); // Bx(5x²-A) + gfpDouble(t, t, ec->f); // 2Bx(5x²-A) + gfpDouble(W(4), t, ec->f); // 4Bx(5x²-A) + // [W₄] <- x⁶+4Bx(5x²-A) + qrMul(t, x2, ecX(a), ec->f, stack); + qrSqr(t1, t, ec->f, stack); + qrAdd(W(4), t1, W(4), ec->f); + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) + qrMul(t1, ec->A, ecX(a), ec->f, stack); // Ax + qrSub(t, t, t1, ec->f); // x³-Ax + qrMul(t, t, t1, ec->f, stack); // Ax(x³-Ax) + gfpDouble(t1, t, ec->f); // 2Ax(x³-Ax) + gfpDouble(t1, t1, ec->f); // 4Ax(x³-Ax) + qrAdd(t, t, t1, ec->f); // 5Ax(x³-Ax) + qrAdd(W(4), W(4), t, ec->f); + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² + qrSqr(t, ec->B, ec->f, stack); // B² + gfpDouble(t, t, ec->f); // 2B² + gfpDouble(t, t, ec->f); // 4B² + gfpDouble(t, t, ec->f); // 8B² + qrSub(W(4), W(4), t, ec->f); + // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) + qrMul(t1, a2, ec->A, ec->f, stack); // A³ + qrSub(W(4), W(4), t1, ec->f); + gfpDouble(W(4), W(4), ec->f); + } + // [W₃²] <- W₃² + qrSqr(W2(3), W(3), ec->f, stack); + // [W₄²] <- W₄² + qrSqr(W2(4), W(4), ec->f, stack); + // [W₁W₃] <- W₃ + qrCopy(WW(1), W(3), ec->f); + // [W₂W₄] <- W₄ + qrCopy(WW(2), W(4), ec->f); + // [(2y)²W₂W₄] <- (2y)²(W₂W₄) + qrMul(WWy2(2), dy2, WW(2), ec->f, stack); + // [(2y)⁴W₂W₄] <- (2y)²((2y)²W₂W₄) + qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); + // [W₅] <- (2y)⁴W₂W₄−(W₁W₃)W₃² + qrMul(t, WW(1), W2(3), ec->f, stack); + qrSub(W(5), WWy4(2), t, ec->f); + // [W₅²] <- W₅² + qrSqr(W2(5), W(5), ec->f, stack); + // [W₅W₂² − W₁W₄²] <- W₅-W₄² + qrSub(WWW(3), W(5), W2(4), ec->f); + // [W₆] <- W₃(W₅W₂²−W₁W₄²) + qrMul(W(6), W(3), WWW(3), ec->f, stack); + // [W₆W₃²−W₂W₅²] <- (W₆W₃)W₃-W₅² + qrMul(WWW(4), W(6), W2(3), ec->f, stack); + qrSub(WWW(4), WWW(4), W2(5), ec->f); + +/* SmallMultJ: этап 2 */ + + // W₂ᵢ, W₂ᵢ₊₁, Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁², WᵢWᵢ₊₂ для i=3,4,...,2ʷ⁻¹ + // (Xᵢ:Yᵢ:Zᵢ) для нечетных i + for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) + { + // [Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁²] <- Wᵢ₊₂Wᵢ²−Wᵢ₋₂Wᵢ₊₁² + if (i >= 5) + { + qrMul(WWW(i), W(i + 2), W2(i - 1), ec->f, stack); + qrMul(t, W(i - 2), W2(i + 1), ec->f, stack); + qrSub(WWW(i), WWW(i), t, ec->f); + } + // [W₂ᵢ] <- Wᵢ(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) + if (i >= 4) + qrMul(W(2 * i), W(i), WWW(i), ec->f, stack); + // [W₂ᵢ²] <- W₂ᵢ² + qrSqr(W2(2 * i), W(2 * i), ec->f, stack); + // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 + gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + // W₂ᵢ₊₁ + //(Xᵢ:Yᵢ:Zᵢ) при нечетном i + if (i & 1) + { + // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); + qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); + // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) + qrMul(ecY(pt, n), ecY(a, n), WWW(i), ec->f, stack); + // [Zᵢ] <- Wᵢ + wwCopy(ecZ(pt, n), W(i), n); + // к следующей точке + pt += 3 * n; + } + else + { + // [(2y)²WᵢWᵢ₊₂] <- (2y)²WᵢWᵢ₊₂ + qrMul(WWy2(i), dy2, WW(i), ec->f, stack); + // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²((2y)²WᵢWᵢ₊₂) + qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); + // [W₂ᵢ₊₁] <- ((2y)⁴WᵢWᵢ₊₂)Wᵢ²-(Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WW(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + } + // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² + if (i != SIZE_BIT_POS(w - 1)) + qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); + } + // [Xᵢ] <- xWᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ для i = 2ʷ⁻¹ + 1 + ASSERT(i == SIZE_BIT_POS(w - 1) + 1); + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); + qrSub(ecX(pt), ecX(pt), WWy2(i - 1), ec->f); + +/* SmallMultJ: этап 3 */ + + // (Xᵢ:Yᵢ:Zᵢ) для i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 + while (1) + { + // [Yᵢ] <- y(Wᵢ₊₂Wᵢ₋₁²-Wᵢ₋₂Wᵢ₊₁²) + qrMul(t, W(i + 2), W2(i - 1), ec->f, stack); + qrMul(ecY(pt, n), W(i - 2), W2(i + 1), ec->f, stack); + qrSub(ecY(pt, n), t, ecY(pt, n), ec->f); + qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); + // [Zᵢ] <- Wᵢ + wwCopy(ecZ(pt, n), W(i), n); + // последняя точка? + if (i == SIZE_BIT_POS(w) - 1) + break; + // к следующей точке + i += 2, pt += 3 * n; + // [Xᵢ] <- x Wᵢ²−(2y)²Wᵢ₋₁Wᵢ₊₁ + gfpMul2(t, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); + qrMul(t, dy2, t, ec->f, stack); + qrMul(ecX(pt), ecX(a), W2(i), ec->f, stack); + qrSub(ecX(pt), ecX(pt), t, ec->f); + } + +#undef WWW +#undef WWy4 +#undef WWy2 +#undef WW +#undef W2 +#undef W +/* SmallMultJ: end */ + + // заполнить служебные поля + pre->type = ec_pre_so; + pre->w = w, pre->h = 0; +} + +static size_t ecpSmallMultJ_deep(size_t n, size_t f_deep, size_t w) +{ + return memSliceSize( + ecpSmallMultJ_local(n, w), + f_deep, + SIZE_MAX); +} + +/* +******************************************************************************* +Предвычисления: SmallMultA + +Реализован алгоритм SmallMultA, предложенный в [APS22]. Алгоритм основан на +многочленах деления и требует примерно 25/2 M + 5 S операций на каждую +кратную точку и дополнительно операцию 1I для одновременнного обращения +нескольких элементов поля. + +Алгоритм выполняется в 4 этапа. Этапам соответствуют следующие шаги алгоритма: +- этап 1 -- шаги 1 -- 13 (расчет начальных выражений); +- этап 2 -- шаг 14 (расчет основных выражений); +- этап 3 -- шаг 15 (обращение элементов поля); +- этап 4 -- шаги 16 -- 20 (формирование точек). + +В целом кэшируются те же выражения, что и в ecpPreSO(). Отличия: +- не кэшируется выражение Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁². Соответственно макрос + WWW не используется; +- выражения (2y)²WᵢWᵢ₊₂ сохраняются в координатах выходных точек. + В частности, при четном i выражение (2y)²WᵢWᵢ₊₂ сохраняется в x-координате + (i + 1)-й точки и используется на этапе 4 для вычисления xᵢ₊₁; +- кэшируется не 2, а 3 последовательных значения WᵢWᵢ₊₂. Это связано + с тем, что на итерациях этапа 2 используются пары выражений + Wᵢ₋₁Wᵢ₊₁ и WᵢWᵢ₊₂, а на итерациях этапа 4 -- пары Wᵢ₋₂Wᵢ и WᵢWᵢ₊₂. + +Дополнительный макрос: +7. W2I(i) -- выражения Wᵢ⁻², i = 3, 5, ..., 2ʷ - 1: + * память: 2ʷ⁻¹ - 1; + * вычисляются на этапе 3, используются на этапе 4; + * предварительно в ячейках памяти для выражений размещаются произведения + (∏ᵥ Wᵥ²: v <= i). + +\remark Произведения (∏ᵥ Wᵥ²: v <= i) используются для одновременного +обращения (Wᵢ²) с помощью следующего алгоритма: +1. V₃ <- W₃². +2. Для i = 5, 7, ..., m = 2ʷ - 1: + 1) Vᵢ <- Vᵢ₋₂Wᵢ² == (∏ᵥ Wᵥ²: v <= i). +3. t <- Vₘ⁻¹ = (∏ᵥ Wᵥ²: v <= m)⁻¹. +4. Для i = m, m - 2, ..., 5: + 1) (Wᵢ⁻², t) <- (t Vᵢ₋₂, t Wᵢ²). +5. W₃⁻² <- t. +Алгоритм предложен в [Mon87] (см. также [Doc05; algorithm 11.15, p. 209]) +и известен как трюк Монтгомери. + +[Mon87] Montogomery P, Speeding the Pollard and elliptic curve method of + factorization. Mathematics of Computation, 48 (177), 1987, 243--264. +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. +******************************************************************************* +*/ + +#if defined(W) || defined(W2) || defined(WW) || defined(WWy2) ||\ + defined(WWy4) || defined(W2I) +#error "Conflicting preprocessor definitions" +#endif + +#define ecpSmallMultA_local(n, w)\ +/* t */ O_OF_W(n),\ +/* dy2 */ O_OF_W(n),\ +/* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ +/* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ +/* WWs */ O_OF_W(3 * n),\ +/* WWy4s */ O_OF_W(n),\ +/* W2Is */ O_OF_W((SIZE_BIT_POS(w - 1) - 1) * n) + +static bool_t ecpSmallMultA(ec_pre_t* pre, const word a[], size_t w, + const ec_o* ec, void* stack) +{ + size_t n; + word* t; /* [n] */ + word* dy2; /* [n] (2y)² */ + word* Ws; /* [(2^w - 1) * n] Wᵢ */ + word* W2s; /* [(2^w - 2) * n] Wᵢ² */ + word* WWs; /* [3 * n] WᵢWᵢ₊₂ */ + word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ + word* W2Is; /* [(2^{w-1} - 1) * n] Wᵢ⁻² */ + size_t i; + word* pt; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(w >= 3); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * SIZE_BIT_POS(w - 1)), + a, O_OF_W(2 * ec->f->n))); + // размерности + n = ec->f->n; + // разметить стек + memSlice(stack, + ecpSmallMultA_local(n, w), SIZE_0, SIZE_MAX, + &t, &dy2, &Ws, &W2s, &WWs, &WWy4s, &W2Is, &stack); + // первая точка + pt = pre->pts; + wwCopy(pt, a, 2 * n); + pt += 2 * n; + +/*** SmallMultA: begin ***/ +#define W(i) (Ws + ((i) - 3) * n) +#define W2(i) (W2s + ((i) - 3) * n) +#define WW(i) (WWs + ((i) % 3) * n) +#define WWy2(i) (pre->pts + (i) * n) +#define WWy4(i) (WWy4s) +#define W2I(i) (W2Is + (((i) - 3) >> 1) * n) + +/* SmallMultA: этап 1 */ + + // dy2 <- (2y)² + gfpDouble(dy2, ecY(a, n), ec->f); + qrSqr(dy2, dy2, ec->f, stack); + // W₃, W₄ + { + word* t1 = W2s; + word* x2 = t1 + n; + word* bx = x2 + n; + word* a2 = bx + n; + // (x2, bx, a2) <- (x², b x, A²) + qrSqr(x2, ecX(a), ec->f, stack); + qrMul(bx, ec->B, ecX(a), ec->f, stack); + qrSqr(a2, ec->A, ec->f, stack); + // [W₃] <- 3(x²+A)² − 4(a²−3Bx) + qrAdd(t, x2, ec->A, ec->f); // x²+A + qrSqr(t, t, ec->f, stack); // (x²+A)² + gfpDouble(t1, t, ec->f); // 2(x²+A)² + qrAdd(W(3), t, t1, ec->f); // 3(x²+A)² + gfpDouble(t, bx, ec->f); // 2Bx + qrAdd(t, t, bx, ec->f); // 3Bx + qrSub(t, a2, t, ec->f); // A²−3Bx + gfpDouble(t, t, ec->f); // 2(A²−3Bx) + gfpDouble(t, t, ec->f); // 4(A²−3Bx) + qrSub(W(3), W(3), t, ec->f); + // [W₄] <- 4Bx(5x²-A) + gfpDouble(t, x2, ec->f); // 2x² + gfpDouble(t, t, ec->f); // 4x² + qrAdd(t, t, x2, ec->f); // 5x² + qrSub(t, t, ec->A, ec->f); // 5x²-A + qrMul(t, bx, t, ec->f, stack); // Bx(5x²-A) + gfpDouble(t, t, ec->f); // 2Bx(5x²-A) + gfpDouble(W(4), t, ec->f); // 4Bx(5x²-A) + // [W₄] <- x⁶+4Bx(5x²-A) + qrMul(t, x2, ecX(a), ec->f, stack); + qrSqr(t1, t, ec->f, stack); + qrAdd(W(4), t1, W(4), ec->f); + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) + qrMul(t1, ec->A, ecX(a), ec->f, stack); // Ax + qrSub(t, t, t1, ec->f); // x³-Ax + qrMul(t, t, t1, ec->f, stack); // Ax(x³-Ax) + gfpDouble(t1, t, ec->f); // 2Ax(x³-Ax) + gfpDouble(t1, t1, ec->f); // 4Ax(x³-Ax) + qrAdd(t, t, t1, ec->f); // 5Ax(x³-Ax) + qrAdd(W(4), W(4), t, ec->f); + // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² + qrSqr(t, ec->B, ec->f, stack); // B² + gfpDouble(t, t, ec->f); // 2B² + gfpDouble(t, t, ec->f); // 4B² + gfpDouble(t, t, ec->f); // 8B² + qrSub(W(4), W(4), t, ec->f); + // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) + qrMul(t1, a2, ec->A, ec->f, stack); // A³ + qrSub(W(4), W(4), t1, ec->f); + gfpDouble(W(4), W(4), ec->f); + } + // [W₃²] <- W₃² + qrSqr(W2(3), W(3), ec->f, stack); + // [W₄²] <- W₄² + qrSqr(W2(4), W(4), ec->f, stack); + // [W₁W₃] <- W₃ + qrCopy(WW(1), W(3), ec->f); + // [W₂W₄] <- W₄ + qrCopy(WW(2), W(4), ec->f); + // [(2y)²W₂W₄] <- (2y)² W₂W₄ + qrMul(WWy2(2), dy2, WW(2), ec->f, stack); + // [(2y)⁴W₂W₄] <- (2y)² (2y)²W₂W₄ + qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); + // [W₅] <- (2y)⁴W₂W₄−W₁W₃W₃² + qrMul(t, WW(1), W2(3), ec->f, stack); + qrSub(W(5), WWy4(2), t, ec->f); + // [W₅²] <- W₅² + qrSqr(W2(5), W(5), ec->f, stack); + // [W₆] <- W₃(W₅W₂²−W₁W₄²) + qrSqr(W2(5), W(5), ec->f, stack); + +/* SmallMultA: этап 2 */ + + // W₂ᵢ, W₂ᵢ₊₁, WᵢWᵢ₊₂ для i=3,4...,2ʷ⁻¹ + for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) + { + // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 + gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + // W₂ᵢ + if (i == 3) + { + // [W₆] <- W₃W₅-(W₁W₃)W₄² + qrMul(t, WW(1), W2(4), ec->f, stack); + qrSub(W(6), WW(3), t, ec->f); + } + else + { + // [W₂ᵢ] <- (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² + qrMul(t, WW(i - 2), W2(i + 1), ec->f, stack); + qrMul(W(2 * i), WW(i), W2(i - 1), ec->f, stack); + qrSub(W(2 * i), W(2 * i), t, ec->f); + } + // [W₂ᵢ²] <- W₂ᵢ² + qrSqr(W2(2 * i), W(2 * i), ec->f, stack); + // W₂ᵢ₊₁ + if (i & 1) + { + // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + } + else + { + // [(2y)²WᵢWᵢ₊₂] <- (2y)²(WᵢWᵢ₊₂) + qrMul(WWy2(i), dy2, WW(i), ec->f, stack); + // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²((2y)²WᵢWᵢ₊₂) + qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); + // [W₂ᵢ₊₁] <- ((2y)⁴WᵢWᵢ₊₂)Wᵢ²-(Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² + qrMul(t, WW(i - 1), W2(i + 1), ec->f, stack); + qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); + qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + } + // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² + if (i != SIZE_BIT_POS(w - 1)) + qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); + } + +/* SmallMultA: этап 3 */ + + // (∏ᵥ Wᵥ²: v <= i) + wwCopy(W2I(3), W2(3), n); + for (i = 3; i + 2 < SIZE_BIT_POS(w); i += 2) + qrMul(W2I(i + 2), W2I(i), W2(i + 2), ec->f, stack); + // t <- (∏ᵥ Wᵥ²: v <= 2ʷ-1)⁻¹ + ASSERT(i == SIZE_BIT_POS(w) - 1); + if (qrIsZero(W2I(i), ec->f)) + return FALSE; + qrInv(t, W2I(i), ec->f, stack); + // Wᵢ⁻² + { + word* t1 = WWy4s; + for (; i > 3; i -= 2) + { + // t1 <- (∏ᵥ Wᵥ²: v <= i)⁻¹ Wᵢ² == (∏ᵥ Wᵥ²: v < i)⁻¹ + qrMul(t1, t, W2(i), ec->f, stack); + // [Wᵢ⁻²] <- (∏ᵥ Wᵥ²: v <= i)⁻¹ (∏ᵥ Wᵥ²: v < i) == Wᵥ⁻² + qrMul(W2I(i), t, W2I(i - 2), ec->f, stack); + // t <- t1 + wwCopy(t, t1, n); + } + // [W₃⁻²] <- (∏ᵥ Wᵥ²: v = 3)⁻¹ + wwCopy(W2I(3), t, n); + } + +/* SmallMultA: этап 4 */ + + // (xᵢ,yᵢ) для i=3,5,..,2ʷ⁻¹-1 + ASSERT(i == 3); + for (; i < SIZE_BIT_POS(w - 1); i += 2, pt += 2 * n) + { + // [xᵢ] <- x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² + qrMul(ecX(pt), WWy2(i - 1), W2I(i), ec->f, stack); + qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); + // [yᵢ] <- yW₂ᵢ(Wᵢ⁻²)² + qrSqr(t, W2I(i), ec->f, stack); + qrMul(ecY(pt, n), ecY(a, n), W(2 * i), ec->f, stack); + qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); + } + // [xᵢ] <- x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² для i=2ʷ⁻¹+1 + ASSERT(i == SIZE_BIT_POS(w - 1) + 1); + qrMul(ecX(pt), WWy2(i - 1), W2I(i), ec->f, stack); + qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); + + // (xᵢ,yᵢ) для i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 + while (1) + { + if (i != SIZE_BIT_POS(w) - 1) + // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 + gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); + else + // [WᵢWᵢ₊₂] <- WᵢWᵢ₊₂ + qrMul(WW(i), W(i), W(i + 2), ec->f, stack); + // [yᵢ] <- y((WᵢWᵢ₊₂)Wᵢ₋₁²-(Wᵢ₋₂Wᵢ)Wᵢ₊₁²)(Wᵢ⁻²)² + qrMul(t, WW(i), W2(i - 1), ec->f, stack); + qrMul(ecY(pt, n), WW(i - 2), W2(i + 1), ec->f, stack); + qrSub(ecY(pt, n), t, ecY(pt, n), ec->f); + qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); + qrSqr(t, W2I(i), ec->f, stack); + qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); + // последняя точка? + if (i == SIZE_BIT_POS(w) - 1) + break; + // к следующей точке + i += 2, pt += 2 * n; + // [xᵢ] <- x−(2y)²(Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² + gfpMul2(t, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); + qrMul(t, dy2, t, ec->f, stack); + qrMul(t, t, W2I(i), ec->f, stack); + qrSub(ecX(pt), ecX(a), t, ec->f); + } + +#undef W2I +#undef WWy4 +#undef WWy2 +#undef WW +#undef W2 +#undef W +/* SmallMultA: end */ + + // заполнить служебные поля + pre->type = ec_pre_soa; + pre->w = w, pre->h = 0; + return TRUE; +} + +static size_t ecpSmallMultA_deep(size_t n, size_t f_deep, size_t w) +{ + return memSliceSize( + ecpSmallMultA_local(n, w), + f_deep, + SIZE_MAX); +} + /* ******************************************************************************* Тестирование на заданной кривой ******************************************************************************* */ +typedef void(*ec_pre_so_i)(ec_pre_t* pre, const word a[], size_t w, + const struct ec_o* ec, void* stack); + +typedef bool_t(*ec_pre_soa_i)(ec_pre_t* pre, const word a[], size_t w, + const struct ec_o* ec, void* stack); + static bool_t ecpTestEc(const ec_o* ec) { + // функции предвычислений + const ec_pre_so_i pre_so_fn[] = { ecpPreSO, ecpSmallMultJ }; + const ec_pre_soa_i pre_soa_fn[] = { ecpPreSOA, ecpSmallMultA }; // размерности const size_t n = ec->f->n; const size_t min_w = 1; @@ -38,6 +673,7 @@ static bool_t ecpTestEc(const ec_o* ec) word* d; /* [n + 1] */ void* stack; // другие переменные + size_t pos; size_t w; size_t i; // создать состояние @@ -46,7 +682,7 @@ static bool_t ecpTestEc(const ec_o* ec) O_OF_W(ec->d * n), O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(10, + utilMax(12, ecpIsValid_deep(n, ec->f->deep), ecpGroupSeemsValid_deep(n, ec->f->deep), ecpGroupIsSafe_deep(n), @@ -55,7 +691,9 @@ static bool_t ecpTestEc(const ec_o* ec) ecpAddAA_deep(n, ec->f->deep), ecpSubAA_deep(n, ec->f->deep), ecMulA_deep(n, ec->d, ec->deep, n), - ecpPreSO_deep(n, ec->f->deep, max_w), + ecpSmallMultJ_deep(n, ec->f->deep, max_w), + ecpSmallMultA_deep(n, ec->f->deep, max_w), + ecpPreSO_deep(n, ec->d, ec->f->deep), ecpPreSOA_deep(n, ec->f->deep, max_w)), SIZE_MAX, &pre, &pt0, &pt1, &d, &stack); @@ -93,11 +731,15 @@ static bool_t ecpTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SO - for (w = MAX2(3, min_w); w <= max_w; ++w) + // предвычисления: схема SO + for (pos = 0; pos < COUNT_OF(pre_so_fn); ++pos) + for (w = min_w; w <= max_w; ++w) { + // ecpSmallMultJ() нельзя вызывать с w < 3 + if (/*pre_so_fn[pos] == ecpSmallMultJ &&*/ w < 3) + continue; // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - ecpPreSO(pre, ec->base, w, ec, stack); + pre_so_fn[pos](pre, ec->base, w, ec, stack); // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) @@ -120,11 +762,15 @@ static bool_t ecpTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SOA - for (w = MAX2(3, min_w); w <= max_w; ++w) + // предвычисления: схема SOA + for (pos = 0; pos < COUNT_OF(pre_soa_fn); ++pos) + for (w = min_w; w <= max_w; ++w) { + // ecpSmallMultA() нельзя вызывать с w < 3 + if (/*pre_so_fn[pos] == ecpSmallMultA &&*/ w < 3) + continue; // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - if (!ecpPreSOA(pre, ec->base, w, ec, stack)) + if (!pre_soa_fn[pos](pre, ec->base, w, ec, stack)) { blobClose(state); return FALSE; From f6973c4c53d9bf075b486f1defb5f82383c74c2c Mon Sep 17 00:00:00 2001 From: agievich Date: Sat, 21 Feb 2026 14:05:28 +0300 Subject: [PATCH 46/77] Switch math/ecp/pre to use Co-Z formulas --- include/bee2/math/ecp.h | 2 +- src/crypto/bign/bign_misc.c | 4 +- src/math/ecp/ecp_pre.c | 470 +++++++++++------------------------- test/math/ecp_bench.c | 4 +- test/math/ecp_test.c | 16 +- 5 files changed, 162 insertions(+), 334 deletions(-) diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 3fae5ea7..3f008dd8 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -292,7 +292,7 @@ void ecpPreSO( void* stack /*!< [in] вспомогательная память */ ); -size_t ecpPreSO_deep(size_t n, size_t ec_d, size_t f_deep); +size_t ecpPreSO_deep(size_t n, size_t f_deep); /*! \brief Предвычисления по схеме SOA diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index c33a1dce..bcb6dbbe 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.02.18 +\version 2026.02.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -63,7 +63,7 @@ size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) return memSliceSize( bignMulA_local(n, pre_count), utilMax(2, - ecpPreSO_deep(n, 3, f_deep), + ecpPreSO_deep(n, f_deep), ecMulPreSO_deep(n, 3, ec_deep, n)), SIZE_MAX); } diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index 587e15ac..1a95edab 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.20 +\version 2026.02.21 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -17,6 +17,72 @@ version 3. See Copyright Notices in bee2/info.h. #include "bee2/math/zz.h" #include "ecp_lcl.h" +/* +******************************************************************************* +Одновременное обращение элементов кольца вычетов + +Реализован алгоритм Монтгомери, предложенный в [Mon87] (см. также +[Doc05; algorithm 11.15, p. 209]). Сложность обращения count элементов: + 1I + 3(count - 1)M. + +[Mon87] Montogomery P, Speeding the Pollard and elliptic curve method of + factorization. Mathematics of Computation, 48 (177), 1987, 243--264. +[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and + Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. +******************************************************************************* +*/ + +#define qrInvBatch_local(n, count)\ +/* t */ O_OF_W(n),\ +/* vs */ O_OF_W(count * n) + +static bool_t qrInvBatch(word* bs, const word* as, size_t count, + const qr_o* r, void* stack) +{ + size_t n; + word* t; /* [n] */ + word* vs; /* [count * n] */ + size_t i; + // pre + ASSERT(qrIsOperable(r)); + ASSERT(wwIsSameOrDisjoint(as, bs, count * r->n)); + ASSERT(count > 0); + // разметить стек + n = r->n; + memSlice(stack, + qrInvBatch_local(n, count), SIZE_0, SIZE_MAX, + &t, &vs, &stack); + // v[i] <- (prod_j a[j]: j <= i) + wwCopy(vs + 0 * n, as + 0 * n, n); + for (i = 1; i < count; ++i) + qrMul(vs + i * n, as + i * n, vs + (i - 1) * n, r, stack); + // t <- 1 / (prod_j a[j]: j < count) + if (qrIsZero(vs + (count - 1) * n, r)) + return FALSE; + qrInv(t, vs + (count - 1) * n, r, stack); + // одновременное обрашение + for (--i; i >= 1; --i) + { + // v[i] <- (prod_j a[j]: j < i) / (prod_j a[j]: j <= i) == 1/a[i] + qrMul(vs + i * n, t, vs + (i - 1) * n, r, stack); + // t <- a[i] / (prod_j a[j]: j <= i) == (prod_j a[j]: j < i) + qrMul(t, t, as + i * n, r, stack); + // b[i] <- 1/a[i] + wwCopy(bs + i * n, vs + i * n, n); + } + // b[0] <- 1/a[0] + wwCopy(bs + 0 * n, t, n); + return TRUE; +} + +static size_t qrInvBatch_deep(size_t n, size_t f_deep, size_t count) +{ + return memSliceSize( + qrInvBatch_local(n, count), + f_deep, + SIZE_MAX); +} + /* ******************************************************************************* Арифметика Co-Z @@ -25,7 +91,7 @@ Aрифметика Co-Z --- это формулы сложения якобие Z-координатами. Арифметика предложена в [Mel06]. Детальные и дополнительные алгоритмы представлены в [Riv11]. -В функции ecpIDblAJ() выполняется удвоение (J, J) <- 2A. Результатом удвоения +В функции ecpIDblAJ() выполняется удвоение JJ <- 2A. Результатом удвоения аффинной точки a являются якобиевы точки a1 и b такие, что: - a1 == a; - b == 2a; @@ -35,9 +101,9 @@ Z-координатами. Арифметика предложена в [Mel06] уполовинивание. Сложность алгоритма: 2M + 4S + 6add + 1half + 2*2 \approx 6M. -В функции ecpReaddJ() выполняется сложение (J, J) <- J + J. Предполагается, +В функции ecpReaddJ() выполняется сложение JJ <- J + J. Предполагается, что это сложение выполняется сразу после предыдущего такого же сложения -или после операции (J, J) <- 2A и тогда у слагаемых совпадают z-координаты. +или после операции JJ <- 2A и тогда у слагаемых совпадают z-координаты. Результатом сложения якобиевых точек a и b с одинаковыми z-координатами являются якобиевы точки a1 и c такие, что: - a1 == a; @@ -73,7 +139,7 @@ Z-координатами. Арифметика предложена в [Mel06] /* t3 */ O_OF_W(n),\ /* t4 */ O_OF_W(n) -// ([3n]b, [3n]a1) <- (2[2n]a, [3n]a) ((J, J) <- 2A) +// ([3n]b, [3n]a1) <- (2[2n]a, [3n]a) (JJ <- 2A) static void ecpIDblAJ(word b[], word a1[], const word a[], const ec_o* ec, void* stack) { @@ -103,10 +169,8 @@ static void ecpIDblAJ(word b[], word a1[], const word a[], const ec_o* ec, // zb <- 2 ya [2y1 = z2], za1 <- zb gfpDouble(ecZ(b, n), ecY(a, n), ec->f); wwCopy(ecZ(a1, n), ecZ(b, n), n); - // t4 <- zb^2 [2y1^2] + // t4 <- zb^2 [4y1^2] qrSqr(t4, ecZ(b, n), ec->f, stack); - // t4 <- t4^2 [4y1^2] - qrSqr(t4, t4, ec->f, stack); // xa1 <- t4 xa [4x1y1^2 = AA] qrMul(ecX(a1), t4, ecX(a), ec->f, stack); // xb <- t3^2 [BB^2] @@ -142,7 +206,7 @@ static size_t ecpIDblAJ_deep(size_t n, size_t f_deep) /* t4 */ O_OF_W(n),\ /* t5 */ O_OF_W(n) -// ([3n]c, [3n]a1) <- ([3n]a + [3n]b, [3n]a) ((J, J) <- J + J) +// ([3n]c, [3n]a1) <- ([3n]a + [3n]b, [3n]a) (JJ <- J + J) static void ecpReaddJ(word c[], word a1[], const word a[], const word b[], const ec_o* ec, void* stack) { @@ -167,12 +231,12 @@ static void ecpReaddJ(word c[], word a1[], const word a[], const word b[], ecpReaddJ_local(n), SIZE_0, SIZE_MAX, &t3, &t4, &t5, &stack); // t5 <- xb - xa [x2 - x1] - zmSub(t3, t3, t4, ec->f); + zmSub(t5, ecX(b), ecX(a), ec->f); // zc <- t5 za [z(x2 - x1) = z3], za1 <- zc qrMul(ecZ(c, n), t5, ecZ(a, n), ec->f, stack); wwCopy(ecZ(a1, n), ecZ(c, n), n); // t5 <- t5^2 [(x2 - x1)^2 = AA] - qrSqr(t3, ecX(a), ec->f, stack); + qrSqr(t5, t5, ec->f, stack); // t3 <- t5 xb [x2 AA = CC] qrMul(t3, t5, ecX(b), ec->f, stack); // xa1 <- t5 xa [x1 AA = BB] @@ -187,8 +251,8 @@ static void ecpReaddJ(word c[], word a1[], const word a[], const word b[], zmSub(ecX(c), t5, t3, ec->f); // t3 <- t3 - xa1 [CC - BB] zmSub(t3, t3, ecX(a1), ec->f); - // ya1 <- ya t3 [y1(CC - BB)] - qrMul(ecY(a1, n), ecY(a, n), t3, ec->f, stack); + // ya1 <- ya1 t3 [y1(CC - BB)] + qrMul(ecY(a1, n), ecY(a1, n), t3, ec->f, stack); // t3 <- xa1 - xc [BB - x3] zmSub(t3, ecX(a1), ecX(c), ec->f); // t4 <- t4 t3 [(y2 - y1)(BB - x3)] @@ -207,51 +271,39 @@ static size_t ecpReaddJ_deep(size_t n, size_t f_deep) SIZE_MAX); } -/* -******************************************************************************* -Ускорители - -В gfpMul2 выражение a * b при известных a^2 и b^2 вычисляется как - ((a + b)^2 - a^2 - b^2) / 2 -******************************************************************************* -*/ - -#define gfpMul2(c, a, b, a2, b2, f, stack)\ - do {\ - qrAdd(c, a, b, f);\ - qrSqr(c, c, f, stack);\ - qrSub(c, c, a2, f);\ - qrSub(c, c, b2, f);\ - gfpHalf(c, c, f);\ - } while(0) - /* ******************************************************************************* Предвычисления: схема SO + +Алгоритм (см. [Mel07], [Riv11]): +1. t <- 2a. // JJ <- 2A, с обновлением a +2. pre[i] <- a. +3. Для i = 2, 3, ..., 2^{i-1}: + 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t ******************************************************************************* */ -#define ecpPreSO_local(n, ec_d)\ -/* t */ O_OF_W(ec_d * n) +#define ecpPreSO_local(n)\ +/* t */ O_OF_W(3 * n) void ecpPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { - word* t; /* [ec->d * ec->f->n] */ - size_t i; + word* t; /* [3 * ec->f->n] */ // pre - ASSERT(ecIsOperable(ec)); + ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecpPreSO_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + ecpPreSO_local(ec->f->n), SIZE_0, SIZE_MAX, &t, &stack); // вычислить малые кратные if (w > 1) { + size_t i; // (t, pre[0]) <- (2a, a) ecpIDblAJ(t, ecPrePt(pre, 0, ec), a, ec, stack); // pre[i] <- t + pre[i - 1] (с обновлением t) @@ -266,10 +318,10 @@ void ecpPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, pre->w = w, pre->h = 0; } -size_t ecpPreSO_deep(size_t n, size_t ec_d, size_t f_deep) +size_t ecpPreSO_deep(size_t n, size_t f_deep) { return memSliceSize( - ecpPreSO_local(n, ec_d), + ecpPreSO_local(n), utilMax(2, ecpIDblAJ_deep(n, f_deep), ecpReaddJ_deep(n, f_deep)), @@ -280,311 +332,80 @@ size_t ecpPreSO_deep(size_t n, size_t ec_d, size_t f_deep) ******************************************************************************* Предвычисления: схема SOA -Реализован алгоритм SmallMultA, предложенный в [APS22]. Алгоритм основан на -многочленах деления и требует примерно 25/2 M + 5 S операций на каждую -кратную точку и дополнительно операцию 1I для одновременнного обращения -нескольких элементов поля. - -Алгоритм выполняется в 4 этапа. Этапам соответствуют следующие шаги алгоритма: -- этап 1 -- шаги 1 -- 13 (расчет начальных выражений); -- этап 2 -- шаг 14 (расчет основных выражений); -- этап 3 -- шаг 15 (обращение элементов поля); -- этап 4 -- шаги 16 -- 20 (формирование точек). - -В целом кэшируются те же выражения, что и в ecpPreSO(). Отличия: -- не кэшируется выражение Wᵢ₊₂Wᵢ₋₁² - Wᵢ₋₂Wᵢ₊₁². Соответственно макрос - WWW не используется; -- выражения (2y)²WᵢWᵢ₊₂ сохраняются в координатах выходных точек. - В частности, при четном i выражение (2y)²WᵢWᵢ₊₂ сохраняется в x-координате - (i + 1)-й точки и используется на этапе 4 для вычисления xᵢ₊₁; -- кэшируется не 2, а 3 последовательных значения WᵢWᵢ₊₂. Это связано - с тем, что на итерациях этапа 2 используются пары выражений - Wᵢ₋₁Wᵢ₊₁ и WᵢWᵢ₊₂, а на итерациях этапа 4 -- пары Wᵢ₋₂Wᵢ и WᵢWᵢ₊₂. - -Дополнительный макрос: -7. W2I(i) -- выражения Wᵢ⁻², i = 3, 5, ..., 2ʷ - 1: - * память: 2ʷ⁻¹ - 1; - * вычисляются на этапе 3, используются на этапе 4; - * предварительно в ячейках памяти для выражений размещаются произведения - (∏ᵥ Wᵥ²: v <= i). - -\remark Произведения (∏ᵥ Wᵥ²: v <= i) используются для одновременного -обращения (Wᵢ²) с помощью следующего алгоритма: -1. V₃ <- W₃². -2. Для i = 5, 7, ..., m = 2ʷ - 1: - 1) Vᵢ <- Vᵢ₋₂Wᵢ² == (∏ᵥ Wᵥ²: v <= i). -3. t <- Vₘ⁻¹ = (∏ᵥ Wᵥ²: v <= m)⁻¹. -4. Для i = m, m - 2, ..., 5: - 1) (Wᵢ⁻², t) <- (t Vᵢ₋₂, t Wᵢ²). -5. W₃⁻² <- t. -Алгоритм предложен в [Mon87] (см. также [Doc05; algorithm 11.15, p. 209]) -и известен как трюк Монтгомери. +Реализован алгоритм схемы SO с дополнением -- преобразованием якобиевых точек +в аффинные. -[Mon87] Montogomery P, Speeding the Pollard and elliptic curve method of - factorization. Mathematics of Computation, 48 (177), 1987, 243--264. -[Doc05] Doche C. Finite Field Arithmetic. In: Handbook of Elliptic and - Hyperelliptic Curve Cryptography. Chapman & Hall/CRC, 2005. +Требуется преобразовать count = 2^{w-1} - 1 точек. Для мульпликативного +обращения z-координат используется алгоритм Монтгомери. Затем для каждой точки +выполняется стандартное преобрвзование + (X : Y : Z) -> (X * (1/Z)^2, Y * (1/Z)^3) +со сложостью 1S + 3M. Общая сложность преобразования count точек: + 1I + (6 count - 1)M + count S. ******************************************************************************* */ -#if defined(W) || defined(W2) || defined(WW) || defined(WWy2) ||\ - defined(WWy4) || defined(W2I) - #error "Conflicting preprocessor definitions" -#endif - #define ecpPreSOA_local(n, w)\ -/* t */ O_OF_W(n),\ -/* dy2 */ O_OF_W(n),\ -/* Ws */ O_OF_W((SIZE_BIT_POS(w) - 1) * n),\ -/* W2s */ O_OF_W((SIZE_BIT_POS(w) - 2) * n),\ -/* WWs */ O_OF_W(3 * n),\ -/* WWy4s */ O_OF_W(n),\ -/* W2Is */ O_OF_W((SIZE_BIT_POS(w - 1) - 1) * n) +/* t1 */ O_OF_W(3 * n),\ +/* t2 */ O_OF_W(3 * n),\ +/* zs */ O_OF_W((SIZE_BIT_POS(w - 1) - 1) * n) bool_t ecpPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { size_t n; - word* t; /* [n] */ - word* dy2; /* [n] (2y)² */ - word* Ws; /* [(2^w - 1) * n] Wᵢ */ - word* W2s; /* [(2^w - 2) * n] Wᵢ² */ - word* WWs; /* [3 * n] WᵢWᵢ₊₂ */ - word* WWy4s; /* [n] (2y)⁴WᵢWᵢ₊₂ */ - word* W2Is; /* [(2^{w-1} - 1) * n] Wᵢ⁻² */ + word* t1; /* [3 * n] */ + word* t2; /* [3 * n] */ + word* zs; /* [(SIZE_BIT_POS(w - 1) - 1) * n] */ size_t i; - word* pt; // pre - ASSERT(ecIsOperable(ec)); - ASSERT(w >= 3); + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(2 * ec->f->n * SIZE_BIT_POS(w - 1)), + pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); - // размерности - n = ec->f->n; // разметить стек + n = ec->f->n; memSlice(stack, ecpPreSOA_local(n, w), SIZE_0, SIZE_MAX, - &t, &dy2, &Ws, &W2s, &WWs, &WWy4s, &W2Is, &stack); - // первая точка - pt = pre->pts; - wwCopy(pt, a, 2 * n); - pt += 2 * n; - -/*** SmallMultA: begin ***/ -#define W(i) (Ws + ((i) - 3) * n) -#define W2(i) (W2s + ((i) - 3) * n) -#define WW(i) (WWs + ((i) % 3) * n) -#define WWy2(i) (pre->pts + (i) * n) -#define WWy4(i) (WWy4s) -#define W2I(i) (W2Is + (((i) - 3) >> 1) * n) - -/* SmallMultA: этап 1 */ - - // dy2 <- (2y)² - gfpDouble(dy2, ecY(a, n), ec->f); - qrSqr(dy2, dy2, ec->f, stack); - // W₃, W₄ - { - word* t1 = W2s; - word* x2 = t1 + n; - word* bx = x2 + n; - word* a2 = bx + n; - // (x2, bx, a2) <- (x², b x, A²) - qrSqr(x2, ecX(a), ec->f, stack); - qrMul(bx, ec->B, ecX(a), ec->f, stack); - qrSqr(a2, ec->A, ec->f, stack); - // [W₃] <- 3(x²+A)² − 4(a²−3Bx) - qrAdd(t, x2, ec->A, ec->f); // x²+A - qrSqr(t, t, ec->f, stack); // (x²+A)² - gfpDouble(t1, t, ec->f); // 2(x²+A)² - qrAdd(W(3), t, t1, ec->f); // 3(x²+A)² - gfpDouble(t, bx, ec->f); // 2Bx - qrAdd(t, t, bx, ec->f); // 3Bx - qrSub(t, a2, t, ec->f); // A²−3Bx - gfpDouble(t, t, ec->f); // 2(A²−3Bx) - gfpDouble(t, t, ec->f); // 4(A²−3Bx) - qrSub(W(3), W(3), t, ec->f); - // [W₄] <- 4Bx(5x²-A) - gfpDouble(t, x2, ec->f); // 2x² - gfpDouble(t, t, ec->f); // 4x² - qrAdd(t, t, x2, ec->f); // 5x² - qrSub(t, t, ec->A, ec->f); // 5x²-A - qrMul(t, bx, t, ec->f, stack); // Bx(5x²-A) - gfpDouble(t, t, ec->f); // 2Bx(5x²-A) - gfpDouble(W(4), t, ec->f); // 4Bx(5x²-A) - // [W₄] <- x⁶+4Bx(5x²-A) - qrMul(t, x2, ecX(a), ec->f, stack); - qrSqr(t1, t, ec->f, stack); - qrAdd(W(4), t1, W(4), ec->f); - // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax) - qrMul(t1, ec->A, ecX(a), ec->f, stack); // Ax - qrSub(t, t, t1, ec->f); // x³-Ax - qrMul(t, t, t1, ec->f, stack); // Ax(x³-Ax) - gfpDouble(t1, t, ec->f); // 2Ax(x³-Ax) - gfpDouble(t1, t1, ec->f); // 4Ax(x³-Ax) - qrAdd(t, t, t1, ec->f); // 5Ax(x³-Ax) - qrAdd(W(4), W(4), t, ec->f); - // [W₄] <- x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B² - qrSqr(t, ec->B, ec->f, stack); // B² - gfpDouble(t, t, ec->f); // 2B² - gfpDouble(t, t, ec->f); // 4B² - gfpDouble(t, t, ec->f); // 8B² - qrSub(W(4), W(4), t, ec->f); - // [W₄] <- 2(x⁶+4Bx(5x²-A)+5Ax(x³-Ax)-8B²-A³) - qrMul(t1, a2, ec->A, ec->f, stack); // A³ - qrSub(W(4), W(4), t1, ec->f); - gfpDouble(W(4), W(4), ec->f); - } - // [W₃²] <- W₃² - qrSqr(W2(3), W(3), ec->f, stack); - // [W₄²] <- W₄² - qrSqr(W2(4), W(4), ec->f, stack); - // [W₁W₃] <- W₃ - qrCopy(WW(1), W(3), ec->f); - // [W₂W₄] <- W₄ - qrCopy(WW(2), W(4), ec->f); - // [(2y)²W₂W₄] <- (2y)² W₂W₄ - qrMul(WWy2(2), dy2, WW(2), ec->f, stack); - // [(2y)⁴W₂W₄] <- (2y)² (2y)²W₂W₄ - qrMul(WWy4(2), dy2, WWy2(2), ec->f, stack); - // [W₅] <- (2y)⁴W₂W₄−W₁W₃W₃² - qrMul(t, WW(1), W2(3), ec->f, stack); - qrSub(W(5), WWy4(2), t, ec->f); - // [W₅²] <- W₅² - qrSqr(W2(5), W(5), ec->f, stack); - // [W₆] <- W₃(W₅W₂²−W₁W₄²) - qrSqr(W2(5), W(5), ec->f, stack); - -/* SmallMultA: этап 2 */ - - // W₂ᵢ, W₂ᵢ₊₁, WᵢWᵢ₊₂ для i=3,4...,2ʷ⁻¹ - for (i = 3; i <= SIZE_BIT_POS(w - 1); ++i) + &t1, &t2, &zs, &stack); + // pre[0] <- a + wwCopy(ecPrePtA(pre, 0, ec), a, 2 * n); + // остальные кратные точки + if (w > 1) { - // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 - gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); - // W₂ᵢ - if (i == 3) - { - // [W₆] <- W₃W₅-(W₁W₃)W₄² - qrMul(t, WW(1), W2(4), ec->f, stack); - qrSub(W(6), WW(3), t, ec->f); - } - else - { - // [W₂ᵢ] <- (WᵢWᵢ₊₂)Wᵢ₋₁² - (Wᵢ₋₂Wᵢ)Wᵢ₊₁² - qrMul(t, WW(i - 2), W2(i + 1), ec->f, stack); - qrMul(W(2 * i), WW(i), W2(i - 1), ec->f, stack); - qrSub(W(2 * i), W(2 * i), t, ec->f); - } - // [W₂ᵢ²] <- W₂ᵢ² - qrSqr(W2(2 * i), W(2 * i), ec->f, stack); - // W₂ᵢ₊₁ - if (i & 1) - { - // [W₂ᵢ₊₁] <- (WᵢWᵢ₊₂)Wᵢ²-((2y)⁴Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² - qrMul(t, WWy4(i - 1), W2(i + 1), ec->f, stack); - qrMul(W(2 * i + 1), WW(i), W2(i), ec->f, stack); - qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); - } - else + // (t2, t1) <- (2a, a) + ecpIDblAJ(t2, t1, a, ec, stack); + // pre[1] <- t2 + t1 (с обновлением t2) + ecpReaddJ(t1, t2, t2, t1, ec, stack); + wwCopy(ecPrePtA(pre, 1, ec), t1, 2 * n); + // z[0] <- z-координата pre[1] + wwCopy(zs + 0 * n, ecZ(t1, n), n); + // остальные кратные точки + for (i = 2; i < SIZE_BIT_POS(w - 1); ++i) { - // [(2y)²WᵢWᵢ₊₂] <- (2y)²(WᵢWᵢ₊₂) - qrMul(WWy2(i), dy2, WW(i), ec->f, stack); - // [(2y)⁴WᵢWᵢ₊₂] <- (2y)²((2y)²WᵢWᵢ₊₂) - qrMul(WWy4(i), dy2, WWy2(i), ec->f, stack); - // [W₂ᵢ₊₁] <- ((2y)⁴WᵢWᵢ₊₂)Wᵢ²-(Wᵢ₋₁Wᵢ₊₁)Wᵢ₊₁² - qrMul(t, WW(i - 1), W2(i + 1), ec->f, stack); - qrMul(W(2 * i + 1), WWy4(i), W2(i), ec->f, stack); - qrSub(W(2 * i + 1), W(2 * i + 1), t, ec->f); + // pre[i] <- t2 + pre[i-1] (с обновлением t2) + ecpReaddJ(t1, t2, t2, t1, ec, stack); + wwCopy(ecPrePtA(pre, i, ec), t1, 2 * n); + // z[i-1] <- z-координата pre[i] + wwCopy(zs + (i - 1) * n, ecZ(t1, n), n); } - // [W₂ᵢ₊₁²] <- W₂ᵢ₊₁² - if (i != SIZE_BIT_POS(w - 1)) - qrSqr(W2(2 * i + 1), W(2 * i + 1), ec->f, stack); - } - -/* SmallMultA: этап 3 */ - - // (∏ᵥ Wᵥ²: v <= i) - wwCopy(W2I(3), W2(3), n); - for (i = 3; i + 2 < SIZE_BIT_POS(w); i += 2) - qrMul(W2I(i + 2), W2I(i), W2(i + 2), ec->f, stack); - // t <- (∏ᵥ Wᵥ²: v <= 2ʷ-1)⁻¹ - ASSERT(i == SIZE_BIT_POS(w) - 1); - if (qrIsZero(W2I(i), ec->f)) - return FALSE; - qrInv(t, W2I(i), ec->f, stack); - // Wᵢ⁻² - { - word* t1 = WWy4s; - for (; i > 3; i -= 2) + // обратить z-координаты + if (!qrInvBatch(zs, zs, SIZE_BIT_POS(w - 1) - 1, ec->f, stack)) + return FALSE; + // построить аффинные точки + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { - // t1 <- (∏ᵥ Wᵥ²: v <= i)⁻¹ Wᵢ² == (∏ᵥ Wᵥ²: v < i)⁻¹ - qrMul(t1, t, W2(i), ec->f, stack); - // [Wᵢ⁻²] <- (∏ᵥ Wᵥ²: v <= i)⁻¹ (∏ᵥ Wᵥ²: v < i) == Wᵥ⁻² - qrMul(W2I(i), t, W2I(i - 2), ec->f, stack); - // t <- t1 - wwCopy(t, t1, n); + word* pt = ecPrePtA(pre, i, ec); + // t2 <- (1/z[i-1])^2 + qrSqr(t2, zs + (i - 1) * n, ec->f, stack); + // нормировать x-координату pre[i] + qrMul(ecX(pt), ecX(pt), t2, ec->f, stack); + // t2 <- (1/z[i-1])^3 + qrMul(t2, zs + (i - 1) * n, t2, ec->f, stack); + // нормировать y-координату pre[i] + qrMul(ecY(pt, n), ecY(pt, n), t2, ec->f, stack); } - // [W₃⁻²] <- (∏ᵥ Wᵥ²: v = 3)⁻¹ - wwCopy(W2I(3), t, n); - } - -/* SmallMultA: этап 4 */ - - // (xᵢ,yᵢ) для i=3,5,..,2ʷ⁻¹-1 - ASSERT(i == 3); - for (; i < SIZE_BIT_POS(w - 1); i += 2, pt += 2 * n) - { - // [xᵢ] <- x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² - qrMul(ecX(pt), WWy2(i - 1), W2I(i), ec->f, stack); - qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); - // [yᵢ] <- yW₂ᵢ(Wᵢ⁻²)² - qrSqr(t, W2I(i), ec->f, stack); - qrMul(ecY(pt, n), ecY(a, n), W(2 * i), ec->f, stack); - qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); } - // [xᵢ] <- x-((2y)²Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² для i=2ʷ⁻¹+1 - ASSERT(i == SIZE_BIT_POS(w - 1) + 1); - qrMul(ecX(pt), WWy2(i - 1), W2I(i), ec->f, stack); - qrSub(ecX(pt), ecX(a), ecX(pt), ec->f); - - // (xᵢ,yᵢ) для i=2ʷ⁻¹+1,2ʷ⁻¹+3,...,2ʷ-1 - while (1) - { - if (i != SIZE_BIT_POS(w) - 1) - // [WᵢWᵢ₊₂] <- ((Wᵢ+Wᵢ₊₂)²-Wᵢ²-Wᵢ₊₂²)/2 - gfpMul2(WW(i), W(i), W(i + 2), W2(i), W2(i + 2), ec->f, stack); - else - // [WᵢWᵢ₊₂] <- WᵢWᵢ₊₂ - qrMul(WW(i), W(i), W(i + 2), ec->f, stack); - // [yᵢ] <- y((WᵢWᵢ₊₂)Wᵢ₋₁²-(Wᵢ₋₂Wᵢ)Wᵢ₊₁²)(Wᵢ⁻²)² - qrMul(t, WW(i), W2(i - 1), ec->f, stack); - qrMul(ecY(pt, n), WW(i - 2), W2(i + 1), ec->f, stack); - qrSub(ecY(pt, n), t, ecY(pt, n), ec->f); - qrMul(ecY(pt, n), ecY(a, n), ecY(pt, n), ec->f, stack); - qrSqr(t, W2I(i), ec->f, stack); - qrMul(ecY(pt, n), ecY(pt, n), t, ec->f, stack); - // последняя точка? - if (i == SIZE_BIT_POS(w) - 1) - break; - // к следующей точке - i += 2, pt += 2 * n; - // [xᵢ] <- x−(2y)²(Wᵢ₋₁Wᵢ₊₁)Wᵢ⁻² - gfpMul2(t, W(i - 1), W(i + 1), W2(i - 1), W2(i + 1), ec->f, stack); - qrMul(t, dy2, t, ec->f, stack); - qrMul(t, t, W2I(i), ec->f, stack); - qrSub(ecX(pt), ecX(a), t, ec->f); - } - -#undef W2I -#undef WWy4 -#undef WWy2 -#undef WW -#undef W2 -#undef W -/* SmallMultA: end */ - // заполнить служебные поля pre->type = ec_pre_soa; pre->w = w, pre->h = 0; @@ -595,6 +416,9 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) { return memSliceSize( ecpPreSOA_local(n, w), - f_deep, + utilMax(3, + ecpIDblAJ_deep(n, f_deep), + ecpReaddJ_deep(n, f_deep), + qrInvBatch_deep(n, f_deep, SIZE_BIT_POS(w - 1) - 1)), SIZE_MAX); } diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 339d96a9..4670feb3 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.18 +\version 2026.02.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -52,7 +52,7 @@ static bool_t ecpBenchEc(const ec_o* ec) ecPreSOA_deep(n, ec->d, ec->deep), ecPreOD_deep(n, ec->d, ec->deep), ecPreSI_deep(n, ec->d, ec->deep, max_h), - ecpPreSO_deep(n, ec->d, ec->f->deep), + ecpPreSO_deep(n, ec->f->deep), ecpPreSOA_deep(n, ec->f->deep, max_w), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 89bb7c89..ae595795 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -691,10 +691,10 @@ static bool_t ecpTestEc(const ec_o* ec) ecpAddAA_deep(n, ec->f->deep), ecpSubAA_deep(n, ec->f->deep), ecMulA_deep(n, ec->d, ec->deep, n), + ecpPreSO_deep(n, ec->f->deep), + ecpPreSOA_deep(n, ec->f->deep, max_w), ecpSmallMultJ_deep(n, ec->f->deep, max_w), - ecpSmallMultA_deep(n, ec->f->deep, max_w), - ecpPreSO_deep(n, ec->d, ec->f->deep), - ecpPreSOA_deep(n, ec->f->deep, max_w)), + ecpSmallMultA_deep(n, ec->f->deep, max_w)), SIZE_MAX, &pre, &pt0, &pt1, &d, &stack); if (state == 0) @@ -736,7 +736,7 @@ static bool_t ecpTestEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { // ecpSmallMultJ() нельзя вызывать с w < 3 - if (/*pre_so_fn[pos] == ecpSmallMultJ &&*/ w < 3) + if (pre_so_fn[pos] == ecpSmallMultJ && w < 3) continue; // pre[0..2^w) <- (1, 3, ..., 2^w-1)base pre_so_fn[pos](pre, ec->base, w, ec, stack); @@ -767,14 +767,18 @@ static bool_t ecpTestEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { // ecpSmallMultA() нельзя вызывать с w < 3 - if (/*pre_so_fn[pos] == ecpSmallMultA &&*/ w < 3) + if (pre_soa_fn[pos] == ecpSmallMultA && w < 3) continue; // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - if (!pre_soa_fn[pos](pre, ec->base, w, ec, stack)) + if (!pre_soa_fn[pos](pre, ec->base, w, ec, stack) || + !wwEq(ecPrePtA(pre, 0, ec), ec->base, n)) { blobClose(state); return FALSE; } + // одна кратная точка? + if (w == 1) + continue; // pt0 <- pre[1] - pre[0] ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); if (!ecpAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) From fd3b59ed6199f2da31a2ea948d0ca05d3ddd9b2a Mon Sep 17 00:00:00 2001 From: agievich Date: Mon, 23 Feb 2026 23:09:03 +0300 Subject: [PATCH 47/77] Add SH schema to math/ec --- include/bee2/math/ec.h | 58 +++++++---- include/bee2/math/ecp.h | 43 ++++++-- src/crypto/bign/bign_misc.c | 8 +- src/math/ec.c | 54 +++++++++- src/math/ecp/ecp_pre.c | 105 ++++++++++++++++--- test/math/ec_test.c | 47 ++++++++- test/math/ecp_bench.c | 8 +- test/math/ecp_test.c | 203 ++++++++++++++++++++++++------------ 8 files changed, 403 insertions(+), 123 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index b6819ddf..d74ff56a 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.18 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -494,21 +494,21 @@ typedef enum В поле pts размещаются точки эллиптической кривой, построенные по определенной точке a в соответствии с определенной схемой предвычислений. - Число предвычисленных точек определяется шириной окна w и обычно равняется - 2^{w-1}. Поддерживаются следующие схемы: - - SO (Small Odd): вычисляется проективные точки + Число предвычисленных точек определяется шириной и высотой окна. + Поддерживаются следующие схемы: + - SO (Small Odd): вычисляется 2^{w-1} проективных точек \code a, 3a, ..., (2^w-1)a; \endcode - - SOA: вычисляются аффинные точки + - SOA (Small Odd Affine): вычисляется 2^{w-1} аффинных точек \code a, 3a, ..., (2^w-1)a; \endcode - - SH (Small Hi): вычисляются проективные точки + - SH (Small Hi): вычисляется 2^{w-1}+1 проективных точек \code - (2^{w-1})a, (2^{w-1}+1)a, ..., (2^w-1)a; + (2^{w-1})a, (2^{w-1}+1)a, ..., (2^w-1)a, 2a; \endcode - - OD (Odd Doubled): вычисляются аффинные точки + - OD (Odd Doubled): вычисляется h*2^{w-1} аффинных точек \code 2^{0} (a, 3a, ..., (2^w-1)a), 2^{w} (a, 3a, ..., (2^w-1)a), @@ -516,7 +516,7 @@ typedef enum ... 2^{(h-1)w} (a, 3a, ..., (2^w-1)a); \endcode - - SI (Signed Interleaved): вычисляются аффинные точки + - SI (Signed Interleaved): вычисляется 2^{w-1} аффинных точек \code [c_{w-1}... c_1 c_0]a = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a, \endcode @@ -774,14 +774,12 @@ bool_t ecPreIsOperable( в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 1. + \pre w > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. \deep{stack} ecPreSO_deep(ec->f->n, ec->d, ec->deep). - \remark Ускоренной редакцией ecPreSO() для кривых над GF(p) является - функция ecpPreSO(). */ void ecPreSO( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -800,7 +798,7 @@ size_t ecPreSO_deep(size_t n, size_t ec_d, size_t ec_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 1. + \pre w > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. @@ -808,10 +806,8 @@ size_t ecPreSO_deep(size_t n, size_t ec_d, size_t ec_deep); \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным - результатом в pre->pts. + результатом. \deep{stack} ecPreSOA_deep(ec->f->n, ec->d, ec->deep). - \remark Ускоренной редакцией ecPreSOA() для кривых над GF(p) является - функция ecpPreSOA(). */ bool_t ecPreSOA( ec_pre_t* pre, /*!< [out] предвычисленные точки */ @@ -823,6 +819,32 @@ bool_t ecPreSOA( size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); +/*! \brief Предвычисления по схеме SH + + На эллиптической кривой ec выполняются предвычисления по схеме SH + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется + в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w > 0. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). + \expect Описание ec корректно. + \expect Точка a лежит на ec. + \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном + случае. + \deep{stack} ecPreSH_deep(ec->deep). +*/ +void ecPreSH( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + const ec_o* ec, /*!< [in] описание кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecPreSH_deep(size_t ec_deep); + /*! \brief Предвычисления по схеме OD На эллиптической кривой ec выполняются предвычисления по схеме OD @@ -838,7 +860,7 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным - результатом в pre->pts. + результатом. \deep{stack} ecPreOD_deep(ec->f->n, ec->d, ec->deep). */ bool_t ecPreOD( @@ -867,7 +889,7 @@ size_t ecPreOD_deep(size_t n, size_t ec_d, size_t ec_deep); \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном случае. \remark При обнаружении точки O предвычисления прерываются с неопределенным - результатом в pre->pts. + результатом. \deep{stack} ecPreSI_deep(ec->f->n, ec->d, ec->deep, h). */ bool_t ecPreSI( diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 3f008dd8..056fd63a 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.02.20 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -273,18 +273,18 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); /*! \brief Предвычисления по схеме SO На эллиптической кривой ec выполняются предвычисления по схеме SO - с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется - в контейнере pre. + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Используются + якобиевы координаты. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w >= 3. + \pre w > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + - O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n)). \expect Описание ec корректно. \expect Точка a лежит на ec. - \deep{stack} ecpPreSO_deep(ec->f->n, ec->d, ec->f->deep). + \deep{stack} ecpPreSOJ_deep(ec->f->n, ec->f->deep). */ -void ecpPreSO( +void ecpPreSOJ( ec_pre_t* pre, /*!< [out] предвычисленные точки */ const word a[], /*!< [in] исходная точка */ size_t w, /*!< [in] ширина окна */ @@ -292,7 +292,7 @@ void ecpPreSO( void* stack /*!< [in] вспомогательная память */ ); -size_t ecpPreSO_deep(size_t n, size_t f_deep); +size_t ecpPreSOJ_deep(size_t n, size_t f_deep); /*! \brief Предвычисления по схеме SOA @@ -301,7 +301,7 @@ size_t ecpPreSO_deep(size_t n, size_t f_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w >= 3. + \pre w > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. @@ -322,6 +322,31 @@ bool_t ecpPreSOA( size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w); +/*! \brief Предвычисления по схеме SH + + На эллиптической кривой ec выполняются предвычисления по схеме SH + с аффинной точкой [2 * ec->f->n]a и окном шириной w. Используются + якобиевы координаты. Результат сохраняется в контейнере pre. + \pre Описание ec работоспособно. + \pre Координаты a лежат в базовом поле. + \pre w > 0. + \pre memIsValid(pre, sizeof(ec_pre_t) + + O_OF_W((SIZE_BIT_POS(w - 1) + 1) * 3 * ec->f->n)). + \expect Описание ec корректно. + \expect В ec установлены якобиевы координаты. + \expect Точка a лежит на ec. + \deep{stack} ecPreSHJ_deep(ec->f->n, ec->d, ec->deep). +*/ +void ecpPreSHJ( + ec_pre_t* pre, /*!< [out] предвычисленные точки */ + const word a[], /*!< [in] исходная точка */ + size_t w, /*!< [in] ширина окна */ + const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecpPreSHJ_deep(size_t n, size_t f_deep, size_t ec_deep); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index bcb6dbbe..72a7c8e9 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.02.20 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -52,7 +52,7 @@ bool_t bignMulA(word b[], const word a[], const ec_o* ec, const word d[], bignMulA_local(ec->f->n, pre_count), SIZE_0, SIZE_MAX, &pre, &stack); // метод SO - ecpPreSO(pre, a, w, ec, stack); + ecpPreSOJ(pre, a, w, ec, stack); return ecMulPreSO(b, pre, ec, d, ec->f->n, stack); } @@ -63,7 +63,7 @@ size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) return memSliceSize( bignMulA_local(n, pre_count), utilMax(2, - ecpPreSO_deep(n, f_deep), + ecpPreSOJ_deep(n, f_deep), ecMulPreSO_deep(n, 3, ec_deep, n)), SIZE_MAX); } @@ -84,6 +84,8 @@ bool_t bignMulBase(word a[], const ec_o* ec, const word d[], void* stack) return ecMulPreSOA(a, ec->pre, ec, d, ec->f->n, stack); case ec_pre_so: return ecMulPreSO(a, ec->pre, ec, d, ec->f->n, stack); + default: + break; } } return bignMulA(a, ec->base, ec, d, stack); diff --git a/src/math/ec.c b/src/math/ec.c index 0f15a210..f52fbb06 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.18 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -171,7 +171,8 @@ void ecPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), + pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -217,7 +218,8 @@ bool_t ecPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ASSERT(ecIsOperable(ec)); ASSERT(0 < w && w < B_PER_W); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), + pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -252,6 +254,46 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } +void ecPreSH(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, + void* stack) +{ + size_t i; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(0 < w && w < B_PER_W); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + + O_OF_W((SIZE_BIT_POS(w - 1) + 1) * ec->d * ec->f->n), + a, O_OF_W(2 * ec->f->n))); + // pre[2^{w-1}] <- 2a + ecDblA(ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), a, ec, stack); + // вычислить малые кратные + if (w > 1) + { + // pre[0] <- 2^{w-1}a + wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), + ec->d * ec->f->n); + for (i = 2; i < w; ++i) + ecDbl(ecPrePt(pre, 0, ec), ecPrePt(pre, 0, ec), ec, stack); + // pre[i] <- pre[i - 1] + a + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + ecAddA(ecPrePt(pre, i, ec), ecPrePt(pre, i - 1, ec), a, ec, stack); + } + else + // pre[0] <- a + ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); + // заполнить служебные поля + pre->type = ec_pre_sh; + pre->w = w, pre->h = 0; +} + +size_t ecPreSH_deep(size_t ec_deep) +{ + return memSliceSize( + ec_deep, + SIZE_MAX); +} + #define ecPreOD_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) @@ -266,7 +308,8 @@ bool_t ecPreOD(ec_pre_t* pre, const word a[], size_t w, size_t h, ASSERT(ecIsOperable(ec)); ASSERT(0 < w && w < B_PER_W && h > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n), + pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, @@ -322,7 +365,8 @@ bool_t ecPreSI(ec_pre_t* pre, const word a[], size_t w, size_t h, ASSERT(ecIsOperable(ec)); ASSERT(0 < w && w < B_PER_W && h > 0); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), + pre, sizeof(ec_pre_t) + + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index 1a95edab..d0de5e4a 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.21 +\version 2026.02.23 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -273,32 +273,32 @@ static size_t ecpReaddJ_deep(size_t n, size_t f_deep) /* ******************************************************************************* -Предвычисления: схема SO +Предвычисления: схема SO, якобиевы координаты Алгоритм (см. [Mel07], [Riv11]): 1. t <- 2a. // JJ <- 2A, с обновлением a -2. pre[i] <- a. -3. Для i = 2, 3, ..., 2^{i-1}: +2. pre[0] <- a. +3. Для i = 1, 2, ..., 2^{w-1}: 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t ******************************************************************************* */ -#define ecpPreSO_local(n)\ +#define ecpPreSOJ_local(n)\ /* t */ O_OF_W(3 * n) -void ecpPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, +void ecpPreSOJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t; /* [3 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(0 < w && w < B_PER_W); - ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n), + ASSERT(memIsDisjoint2(pre, + sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, - ecpPreSO_local(ec->f->n), SIZE_0, SIZE_MAX, + ecpPreSOJ_local(ec->f->n), SIZE_0, SIZE_MAX, &t, &stack); // вычислить малые кратные if (w > 1) @@ -312,16 +312,19 @@ void ecpPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, ec, stack); } else - ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); + { + wwCopy(ecPrePt(pre, 0, ec), a, 2 * ec->f->n); + qrSetUnity(ecZ(ecPrePt(pre, 0, ec), ec->f->n), ec->f); + } // заполнить служебные поля pre->type = ec_pre_so; pre->w = w, pre->h = 0; } -size_t ecpPreSO_deep(size_t n, size_t f_deep) +size_t ecpPreSOJ_deep(size_t n, size_t f_deep) { return memSliceSize( - ecpPreSO_local(n), + ecpPreSOJ_local(n), utilMax(2, ecpIDblAJ_deep(n, f_deep), ecpReaddJ_deep(n, f_deep)), @@ -360,8 +363,8 @@ bool_t ecpPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(0 < w && w < B_PER_W); - ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), + ASSERT(memIsDisjoint2(pre, + sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек n = ec->f->n; @@ -422,3 +425,77 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) qrInvBatch_deep(n, f_deep, SIZE_BIT_POS(w - 1) - 1)), SIZE_MAX); } + + +/* +******************************************************************************* +Предвычисления: схема SH, якобиевы координаты + +Алгоритм (w > 1): +1. b <- 2a. // J <- 2A +2. pre[0] <- 2^{w-2}b. // J <- 2J +3. t <- a: t -- якобиева точка, Z(t) = Z(pre[0]). +4. Для i = 1, 2, ..., 2^{w-1}: + 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t +******************************************************************************* +*/ + +#define ecpPreSHJ_local(n)\ +/* t */ O_OF_W(3 * n) + +void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, + void* stack) +{ + word* t; /* [3 * ec->f->n] */ + size_t i; + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(0 < w && w < B_PER_W); + ASSERT(memIsDisjoint2( + pre, sizeof(ec_pre_t) + + O_OF_W((SIZE_BIT_POS(w - 1) + 1) * 3 * ec->f->n), + a, O_OF_W(2 * ec->f->n))); + // разметить стек + memSlice(stack, + ecpPreSHJ_local(ec->f->n), SIZE_0, SIZE_MAX, + &t, &stack); + // pre[2^{w-1}] <- 2a + ecDblA(ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), a, ec, stack); + // вычислить малые кратные + if (w > 1) + { + const size_t n = ec->f->n; + // pre[0] <- 2^{w-1}a + wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), + 3 * ec->f->n); + for (i = 2; i < w; ++i) + ecDbl(ecPrePt(pre, 0, ec), ecPrePt(pre, 0, ec), ec, stack); + // t <- a: z(t) = z(pre[0]) + wwCopy(ecZ(t, n), ecZ(ecPrePt(pre, 0, ec), n), n); + qrSqr(ecX(t), ecZ(t, n), ec->f, stack); + qrMul(ecY(t, n), ecX(t), ecZ(t, n), ec->f, stack); + qrMul(ecX(t), ecX(t), ecX(a), ec->f, stack); + qrMul(ecY(t, n), ecY(t, n), ecY(a, n), ec->f, stack); + // pre[i] <- pre[i - 1] + a + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + ecpReaddJ(ecPrePt(pre, i, ec), t, t, ecPrePt(pre, i - 1, ec), ec, + stack); + } + else + // pre[0] <- a + ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); + // заполнить служебные поля + pre->type = ec_pre_sh; + pre->w = w, pre->h = 0; +} + +size_t ecpPreSHJ_deep(size_t n, size_t f_deep, size_t ec_deep) +{ + return memSliceSize( + ecpPreSHJ_local(n), + utilMax(3, + ecpIDblAJ_deep(n, f_deep), + ecpReaddJ_deep(n, f_deep), + ec_deep), + SIZE_MAX); +} diff --git a/test/math/ec_test.c b/test/math/ec_test.c index 66416edd..a28cd4e1 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves \project bee2/test \created 2026.02.12 -\version 2026.02.18 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +83,8 @@ bool_t ecTestEc(const ec_o* ec) const size_t max_w = 4; const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; - const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); + const size_t max_pre_count = + MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 1); // состояние void* state; ec_pre_t* pre; /* [max_pre_count проективных точек] */ @@ -115,7 +117,7 @@ bool_t ecTestEc(const ec_o* ec) ecMulA_deep(n, ec->d, ec->deep, n), ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), - ecPreOD_deep(n, ec->d, ec->deep), + ecPreSH_deep(ec->deep), ecPreSI_deep(n, ec->d, max_h, ec->deep), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), @@ -339,14 +341,14 @@ bool_t ecTestEc(const ec_o* ec) { // pre[0..2^w) <- (1, 3, ..., 2^w-1)base ecPreSO(pre, ec->base, w, ec, stack); - // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] + // pt0 <- \sum_{i=0}^{2^{w-1}-1} 2^{2^{w-1}-1-i} pre[i] wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { ecDbl(pt0, pt0, ec, stack); ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); } - // pt0 == \sum_{i = 0}^{2^w-1} (2i + 1) 2^{2^{w-1}-1-i} base? + // pt0 == \sum_{i=0}^{2^{w-1}-1} (2i+1)2^{2^{w-1}-1-i} base? wwSetW(d, n, 1); for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) { @@ -401,6 +403,41 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } + // предвычисления по схеме SH + for (w = min_w; w <= max_w; ++w) + { + // pre[0..2^w] <- (2^{w-1}, 2^{w-1}+1, ..., 2^w-1, 2)base + ecPreSH(pre, ec->base, w, ec, stack); + // pt0 == 2base? + if (!ecToA(pt0, ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), ec, stack) || + !ecAddAA(pt1, ec->base, ec->base, ec, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + // pt0 <- \sum_{i=0}^{2^{w-1}-1} 2^{2^{w-1}-1-i} pre[i] + wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + { + ecDbl(pt0, pt0, ec, stack); + ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); + } + // pt0 == \sum_{i=0}^{2^{w-1}-1} (2^{w-1}+i)2^{2^{w-1}-1-i} base? + wwSetW(d, n, WORD_BIT_POS(w - 1)); + for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + { + wwShHi(d, n, 1); + zzAddW2(d, n, WORD_BIT_POS(w - 1) + (word)i); + } + if (!ecToA(pt0, pt0, ec, stack) || + !ecMulA(pt1, ec->base, ec, d, n, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + } // предвычисления по схеме SI for (w = min_w; w <= max_w; ++w) { diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 4670feb3..b1eb30a3 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.20 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -52,7 +52,7 @@ static bool_t ecpBenchEc(const ec_o* ec) ecPreSOA_deep(n, ec->d, ec->deep), ecPreOD_deep(n, ec->d, ec->deep), ecPreSI_deep(n, ec->d, ec->deep, max_h), - ecpPreSO_deep(n, ec->f->deep), + ecpPreSOJ_deep(n, ec->f->deep), ecpPreSOA_deep(n, ec->f->deep, max_w), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), @@ -88,7 +88,7 @@ static bool_t ecpBenchEc(const ec_o* ec) for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, no, combo_state); - ecpPreSO(pre, ec->base, w, ec, stack); + ecpPreSOJ(pre, ec->base, w, ec, stack); ecMulPreSO(pt, pre, ec, d, n, stack); } ticks = tmTicks() - ticks; @@ -189,7 +189,7 @@ static bool_t ecpBenchEc(const ec_o* ec) for (w = MAX2(3, min_w); w <= max_w; ++w) { for (i = 0, ticks = tmTicks(); i < reps; ++i) - ecpPreSO(pre, ec->base, w, ec, stack); + ecpPreSOJ(pre, ec->base, w, ec, stack); ticks = tmTicks() - ticks; printf(" ecpPre[SO,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index ae595795..1564f297 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,13 +4,14 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.20 +\version 2026.02.23 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* */ #include +#include #include #include #include @@ -36,6 +37,7 @@ gfpHalf(c, c, f);\ } while(0) + /* ******************************************************************************* Предвычисления: SmallMultJ @@ -645,26 +647,97 @@ static size_t ecpSmallMultA_deep(size_t n, size_t f_deep, size_t w) /* ******************************************************************************* -Тестирование на заданной кривой +Контрольная сумма + +Для предвычисленных точек pre[0..count) вычисляется контрольная сумма + pre[0] \pm pre[1] \pm ... \pm pre[count-1], +где знаки определяются псевдослучайным образом с помощью генератора COMBO. +Генератор инициализируется затравочным значением seed. ******************************************************************************* */ -typedef void(*ec_pre_so_i)(ec_pre_t* pre, const word a[], size_t w, - const struct ec_o* ec, void* stack); +#define ecPreChecksum_local(n, ec_d)\ +/* state */ prngCOMBO_keep(),\ +/* t1 */ O_OF_W(ec_d * n),\ +/* t2 */ O_OF_W(ec_d * n),\ +/* r */ 1 + +static bool_t ecPreChecksum(word a[], const ec_pre_t* pre, u32 seed, + const ec_o* ec, void* stack) +{ + octet* state; /* [prngCOMBO_keep()] */ + word* t1; /* [ec->d * ec->f->n] */ + word* t2; /* [ec->d * ec->f->n] */ + octet* r; /* [1] */ + size_t count; + size_t i; + // pre + ASSERT(sizeof(state) >= prngCOMBO_keep()); + ASSERT(ecIsOperable(ec)); + ASSERT(ecPreIsOperable(pre)); + // разметить стек + memSlice(stack, + ecPreChecksum_local(ec->f->n, ec->d), SIZE_0, SIZE_MAX, + &state, &t1, &t2, &r, &stack); + // инициализировать генератор COMBO + prngCOMBOStart(state, seed); + // число предвычисленных точек + count = SIZE_BIT_POS(pre->w - 1); + if (pre->type == ec_pre_sh) + ++count; + else if (pre->type == ec_pre_so) + count *= pre->h; + // проективные предвычисленные точки? + if (pre->type == ec_pre_so || pre->type == ec_pre_sh) + { + wwCopy(t1, ecPrePt(pre, 0, ec), ec->d * ec->f->n); + for (i = 1; i < count; ++i) + { + prngCOMBOStepR(r, 1, state); + if (r[0] & 1) + ecNeg(t2, ecPrePt(pre, i, ec), ec, stack); + else + wwCopy(t2, ecPrePt(pre, i, ec), ec->d * ec->f->n); + ecAdd(t1, t1, t2, ec, stack); + } + } + else + { + ecFromA(t1, ecPrePtA(pre, 0, ec), ec, stack); + for (i = 1; i < count; ++i) + { + prngCOMBOStepR(r, 1, state); + if (r[0] & 1) + ecNegA(t2, ecPrePtA(pre, i, ec), ec, stack); + else + wwCopy(t2, ecPrePtA(pre, i, ec), 2 * ec->f->n); + ecAddA(t1, t1, t2, ec, stack); + } + } + return ecToA(a, t1, ec, stack); +} + +static size_t ecPreChecksum_deep(size_t n, size_t ec_d, size_t ec_deep) +{ + return memSliceSize( + ecPreChecksum_local(n, ec_d), + ec_deep, + SIZE_MAX); +} -typedef bool_t(*ec_pre_soa_i)(ec_pre_t* pre, const word a[], size_t w, - const struct ec_o* ec, void* stack); +/* +******************************************************************************* +Тестирование на заданной кривой +******************************************************************************* +*/ static bool_t ecpTestEc(const ec_o* ec) { - // функции предвычислений - const ec_pre_so_i pre_so_fn[] = { ecpPreSO, ecpSmallMultJ }; - const ec_pre_soa_i pre_soa_fn[] = { ecpPreSOA, ecpSmallMultA }; // размерности const size_t n = ec->f->n; const size_t min_w = 1; const size_t max_w = 6; - const size_t max_pre_count = SIZE_BIT_POS(max_w - 1); + const size_t max_pre_count = SIZE_BIT_POS(max_w - 1) + 1; // состояние void* state; ec_pre_t* pre; /* [max_pre_count проективных точек] */ @@ -673,28 +746,31 @@ static bool_t ecpTestEc(const ec_o* ec) word* d; /* [n + 1] */ void* stack; // другие переменные - size_t pos; size_t w; - size_t i; // создать состояние state = blobCreate2( sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), O_OF_W(ec->d * n), O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(12, + utilMax(17, + ec->deep, ecpIsValid_deep(n, ec->f->deep), ecpGroupSeemsValid_deep(n, ec->f->deep), ecpGroupIsSafe_deep(n), - ec->deep, ecpIsOnA_deep(n, ec->f->deep), ecpAddAA_deep(n, ec->f->deep), ecpSubAA_deep(n, ec->f->deep), ecMulA_deep(n, ec->d, ec->deep, n), - ecpPreSO_deep(n, ec->f->deep), - ecpPreSOA_deep(n, ec->f->deep, max_w), + ecPreSO_deep(n, ec->d, ec->deep), + ecpPreSOJ_deep(n, ec->f->deep), ecpSmallMultJ_deep(n, ec->f->deep, max_w), - ecpSmallMultA_deep(n, ec->f->deep, max_w)), + ecPreSOA_deep(n, ec->d, ec->deep), + ecpPreSOA_deep(n, ec->f->deep, max_w), + ecpSmallMultA_deep(n, ec->f->deep, max_w), + ecPreSH_deep(ec->deep), + ecpPreSHJ_deep(n, ec->f->deep, ec->deep), + ecPreChecksum_deep(n, ec->d, ec->f->deep)), SIZE_MAX, &pre, &pt0, &pt1, &d, &stack); if (state == 0) @@ -732,74 +808,71 @@ static bool_t ecpTestEc(const ec_o* ec) } } // предвычисления: схема SO - for (pos = 0; pos < COUNT_OF(pre_so_fn); ++pos) for (w = min_w; w <= max_w; ++w) { - // ecpSmallMultJ() нельзя вызывать с w < 3 - if (pre_so_fn[pos] == ecpSmallMultJ && w < 3) - continue; - // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - pre_so_fn[pos](pre, ec->base, w, ec, stack); - // pt0 <- \sum_{i = 0}^{2^w-1} 2^{2^{w-1}-1-i} pre[i] - wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) - { - ecDbl(pt0, pt0, ec, stack); - ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); - } - // pt0 == \sum_{i = 0}^{2^w-1} (2i + 1) 2^{2^{w-1}-1-i} base? - wwSetW(d, n, 1); - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + const u32 seed = 23; + bool_t is_zero; + // эталонная контрольная сумма + ecPreSO(pre, ec->base, w, ec, stack); + is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); + // проверить ecpPreSO() + ecpPreSOJ(pre, ec->base, w, ec, stack); + if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || + !is_zero && !wwEq(pt0, pt1, 2 * n)) { - wwShHi(d, n, 1); - zzAddW2(d, n, (word)(2 * i + 1)); + blobClose(state); + return FALSE; } - if (!ecToA(pt0, pt0, ec, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) + // проверить ecpSmallMultJ() + if (w < 3) + continue; + ecpSmallMultJ(pre, ec->base, w, ec, stack); + if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || + !is_zero && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } } // предвычисления: схема SOA - for (pos = 0; pos < COUNT_OF(pre_soa_fn); ++pos) for (w = min_w; w <= max_w; ++w) { - // ecpSmallMultA() нельзя вызывать с w < 3 - if (pre_soa_fn[pos] == ecpSmallMultA && w < 3) - continue; - // pre[0..2^w) <- (1, 3, ..., 2^w-1)base - if (!pre_soa_fn[pos](pre, ec->base, w, ec, stack) || - !wwEq(ecPrePtA(pre, 0, ec), ec->base, n)) + const u32 seed = 34; + bool_t is_zero; + // эталонная контрольная сумма + ecPreSOA(pre, ec->base, w, ec, stack); + is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); + // проверить ecpPreSOA() + ecpPreSOA(pre, ec->base, w, ec, stack); + if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || + !is_zero && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } - // одна кратная точка? - if (w == 1) - continue; - // pt0 <- pre[1] - pre[0] - ecNegA(pt0, ecPrePtA(pre, 0, ec), ec, stack); - if (!ecpAddAA(pt0, pt0, ecPrePtA(pre, 1, ec), ec, stack)) + // проверить ecpSmallMultA() + if (w < 3) + continue; + ecpSmallMultA(pre, ec->base, w, ec, stack); + if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || + !is_zero && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } - // pre[i+1] - pre[i] == pt0? - for (i = 1; i + 1 < SIZE_BIT_POS(w - 1); ++i) - { - ecNegA(pt1, ecPrePtA(pre, i, ec), ec, stack); - if (!ecpAddAA(pt1, pt1, ecPrePtA(pre, i + 1, ec), ec, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - } - // pt0 == 2 base? - if (!ecpAddAA(pt1, ec->base, ec->base, ec, stack) || - !wwEq(pt0, pt1, 2 * n)) + } + // предвычисления: схема SH + for (w = min_w; w <= max_w; ++w) + { + const u32 seed = 43; + bool_t is_zero; + // эталонная контрольная сумма + ecPreSH(pre, ec->base, w, ec, stack); + is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); + // проверить ecpPreSOA() + ecpPreSHJ(pre, ec->base, w, ec, stack); + if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || + !is_zero && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; From c375a4f20fabfc14d1382e639e40b748e4518e5e Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 24 Feb 2026 12:39:36 +0300 Subject: [PATCH 48/77] Clarify comments in math/ec --- include/bee2/math/ec.h | 20 ++++++++++---------- include/bee2/math/ecp.h | 8 ++++---- src/math/ec.c | 24 +++++++++++++----------- src/math/ecp/ecp_pre.c | 19 +++++++++---------- test/math/ec_test.c | 5 +++-- test/math/ecp_bench.c | 5 +++-- test/math/ecp_test.c | 7 +++---- 7 files changed, 45 insertions(+), 43 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index d74ff56a..01315e4e 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.23 +\version 2026.02.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -103,8 +103,8 @@ ec->f->from(), экспорт -- с помощью функции ec->f->to(). ec_tpl_i, ec_dbladda_i, ec_finadd_i и ec_finadda_i можно не поддерживать. Указатель на неподдерживаемую функцию должен быть нулевым. -Описание кольца организовано как объект, и можно применять функции, -описанные в заголовочном файле obj.h. +Описание кольца организовано как объект. Можно применять функции, описанные +в заголовочном файле obj.h. Эллиптическая кривая должна создаваться специальной функцией ecCreate(), которая является аналогом конструктора и реализуется разными способами в @@ -136,7 +136,7 @@ ecCreate() и сохраняется в поле deep описания крив В функцию ecCreate() могут не передаваться параметры base, order и cofactor, определяющие строение группы точек, хотя буферы для base и order могут готовиться в ecCreate(). Параметры могут устанавливаться позднее с помощью -ecGroupCreate(). +функции ecGroupCreate(). \pre Все указатели действительны. @@ -758,7 +758,7 @@ bool_t ecGroupIsOperable( Проверяются следующие условия: - буфер [sizeof(ec_pre_t)]pre корректен; - pre->type \in ec_pre_type; - - pre->w > 0; + - 0 < pre->w && pre->w < MIN2(B_PER_W, B_PER_S); - pre->h > 0 <=> pre->type \in { ec_pre_od, ec_pre_si }. . \return Признак работоспособности. @@ -774,7 +774,7 @@ bool_t ecPreIsOperable( в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). \expect Описание ec корректно. @@ -798,7 +798,7 @@ size_t ecPreSO_deep(size_t n, size_t ec_d, size_t ec_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. @@ -826,7 +826,7 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. @@ -852,7 +852,7 @@ size_t ecPreSH_deep(size_t ec_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 1 && h > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S) && h > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n)). \expect Описание ec корректно. @@ -881,7 +881,7 @@ size_t ecPreOD_deep(size_t n, size_t ec_d, size_t ec_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 1 && h > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S) && h > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 056fd63a..8fae1edb 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.02.23 +\version 2026.02.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -277,7 +277,7 @@ size_t ecpSWU_deep(size_t n, size_t f_deep); якобиевы координаты. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n)). \expect Описание ec корректно. @@ -301,7 +301,7 @@ size_t ecpPreSOJ_deep(size_t n, size_t f_deep); в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). \expect Описание ec корректно. @@ -329,7 +329,7 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w); якобиевы координаты. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. \pre Координаты a лежат в базовом поле. - \pre w > 0. + \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W((SIZE_BIT_POS(w - 1) + 1) * 3 * ec->f->n)). \expect Описание ec корректно. diff --git a/src/math/ec.c b/src/math/ec.c index f52fbb06..d0b36375 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.23 +\version 2026.02.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -155,7 +155,7 @@ bool_t ecPreIsOperable(const ec_pre_t* pre) (pre->type == ec_pre_so || pre->type == ec_pre_soa || pre->type == ec_pre_sh || pre->type == ec_pre_od || pre->type == ec_pre_si) && - pre->w > 0 && pre->w < B_PER_W && + 0 < pre->w && pre->w < MIN2(B_PER_W, B_PER_S) && ((pre->type != ec_pre_od && pre->type != ec_pre_si) ^ (pre->h > 0)); } @@ -166,10 +166,9 @@ void ecPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t; /* [ec->d * ec->f->n] */ - size_t i; // pre ASSERT(ecIsOperable(ec)); - ASSERT(0 < w && w < B_PER_W); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n), @@ -183,6 +182,7 @@ void ecPreSO(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // вычислить малые кратные if (w > 1) { + size_t i; // t <- 2 a ecDblA(t, a, ec, stack); // pre[1] <- t + a @@ -213,10 +213,9 @@ bool_t ecPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, { word* t1; /* [ec->d * ec->f->n] */ word* t2; /* [ec->d * ec->f->n] */ - size_t i; // pre ASSERT(ecIsOperable(ec)); - ASSERT(0 < w && w < B_PER_W); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), @@ -230,6 +229,7 @@ bool_t ecPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // вычислить малые кратные if (w > 1) { + size_t i; // t1 <- 2 a ecDblA(t1, a, ec, stack); // pre[i] <- t1 + pre[i - 1] @@ -257,10 +257,9 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep) void ecPreSH(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { - size_t i; // pre ASSERT(ecIsOperable(ec)); - ASSERT(0 < w && w < B_PER_W); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W((SIZE_BIT_POS(w - 1) + 1) * ec->d * ec->f->n), @@ -270,6 +269,7 @@ void ecPreSH(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // вычислить малые кратные if (w > 1) { + size_t i; // pre[0] <- 2^{w-1}a wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), ec->d * ec->f->n); @@ -306,7 +306,7 @@ bool_t ecPreOD(ec_pre_t* pre, const word a[], size_t w, size_t h, word* cur; // pre ASSERT(ecIsOperable(ec)); - ASSERT(0 < w && w < B_PER_W && h > 0); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S) && h > 0); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n), @@ -344,7 +344,9 @@ size_t ecPreOD_deep(size_t n, size_t ec_d, size_t ec_deep) { return memSliceSize( ecPreOD_local(n, ec_d), - ecPreSOA_deep(n, ec_d, ec_deep), + utilMax(2, + ec_deep, + ecPreSOA_deep(n, ec_d, ec_deep)), SIZE_MAX); } @@ -363,7 +365,7 @@ bool_t ecPreSI(ec_pre_t* pre, const word a[], size_t w, size_t h, word code; // pre ASSERT(ecIsOperable(ec)); - ASSERT(0 < w && w < B_PER_W && h > 0); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S) && h > 0); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index d0de5e4a..82b4008a 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.23 +\version 2026.02.24 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -292,7 +292,7 @@ void ecpPreSOJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* t; /* [3 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(0 < w && w < B_PER_W); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n), a, O_OF_W(2 * ec->f->n))); @@ -359,10 +359,9 @@ bool_t ecpPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* t1; /* [3 * n] */ word* t2; /* [3 * n] */ word* zs; /* [(SIZE_BIT_POS(w - 1) - 1) * n] */ - size_t i; // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(0 < w && w < B_PER_W); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), a, O_OF_W(2 * ec->f->n))); @@ -376,6 +375,7 @@ bool_t ecpPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, // остальные кратные точки if (w > 1) { + size_t i; // (t2, t1) <- (2a, a) ecpIDblAJ(t2, t1, a, ec, stack); // pre[1] <- t2 + t1 (с обновлением t2) @@ -426,17 +426,16 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) SIZE_MAX); } - /* ******************************************************************************* Предвычисления: схема SH, якобиевы координаты Алгоритм (w > 1): -1. b <- 2a. // J <- 2A -2. pre[0] <- 2^{w-2}b. // J <- 2J +1. pre[2^{w-1}] <- 2a. // J <- 2A +2. pre[0] <- 2^{w-2}pre[2^{w-1}]. // J <- 2J 3. t <- a: t -- якобиева точка, Z(t) = Z(pre[0]). 4. Для i = 1, 2, ..., 2^{w-1}: - 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t + 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t ******************************************************************************* */ @@ -447,10 +446,9 @@ void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { word* t; /* [3 * ec->f->n] */ - size_t i; // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(0 < w && w < B_PER_W); + ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + O_OF_W((SIZE_BIT_POS(w - 1) + 1) * 3 * ec->f->n), @@ -465,6 +463,7 @@ void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, if (w > 1) { const size_t n = ec->f->n; + size_t i; // pre[0] <- 2^{w-1}a wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), 3 * ec->f->n); diff --git a/test/math/ec_test.c b/test/math/ec_test.c index a28cd4e1..7bbcd897 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves \project bee2/test \created 2026.02.12 -\version 2026.02.23 +\version 2026.02.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -109,7 +109,7 @@ bool_t ecTestEc(const ec_o* ec) O_OF_W(ec->d * n), O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(14, + utilMax(15, ec->deep, ecHasOrderA_deep(n, ec->d, ec->deep, n), ecAddAA_deep(n, ec->d, ec->deep), @@ -119,6 +119,7 @@ bool_t ecTestEc(const ec_o* ec) ecPreSOA_deep(n, ec->d, ec->deep), ecPreSH_deep(ec->deep), ecPreSI_deep(n, ec->d, max_h, ec->deep), + ecPreOD_deep(n, ec->d, ec->deep), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), ecMulPreOD_deep(n, ec->d, ec->deep, n), diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index b1eb30a3..13dc771f 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -32,13 +32,14 @@ static bool_t ecpBenchEc(const ec_o* ec) const size_t max_w = 6; const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; - const size_t max_pre_count = SIZE_BIT_POS(max_w - 1) * min_h; + const size_t max_pre_count = + MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 1); const size_t reps = 200; void* state; octet* combo_state; /* [prngCOMBO_keep()] */ word* pt; /* [2 * n] */ word* d; /* [n] */ - ec_pre_t* pre; /* [SIZE_BIT_POS(max_w - 1) якобиевы точки] */ + ec_pre_t* pre; /* [max_pre_count проективных точек] */ void* stack; // создать состояние state = blobCreate2( diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 1564f297..ca0b94f1 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.23 +\version 2026.02.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -672,7 +672,6 @@ static bool_t ecPreChecksum(word a[], const ec_pre_t* pre, u32 seed, size_t count; size_t i; // pre - ASSERT(sizeof(state) >= prngCOMBO_keep()); ASSERT(ecIsOperable(ec)); ASSERT(ecPreIsOperable(pre)); // разметить стек @@ -815,7 +814,7 @@ static bool_t ecpTestEc(const ec_o* ec) // эталонная контрольная сумма ecPreSO(pre, ec->base, w, ec, stack); is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); - // проверить ecpPreSO() + // проверить ecpPreSOJ() ecpPreSOJ(pre, ec->base, w, ec, stack); if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || !is_zero && !wwEq(pt0, pt1, 2 * n)) @@ -869,7 +868,7 @@ static bool_t ecpTestEc(const ec_o* ec) // эталонная контрольная сумма ecPreSH(pre, ec->base, w, ec, stack); is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); - // проверить ecpPreSOA() + // проверить ecpPreSHJ() ecpPreSHJ(pre, ec->base, w, ec, stack); if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || !is_zero && !wwEq(pt0, pt1, 2 * n)) From 3b67e1395bb415d8979a824a09b9bfdfa42c40b9 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 24 Feb 2026 18:52:41 +0300 Subject: [PATCH 49/77] Clarify preconditions in math/ec --- include/bee2/math/ec.h | 60 +++++++++++++++++++++++++++++++++++------- src/math/ec.c | 18 ++++++------- test/math/ec_test.c | 2 +- test/math/ecp_test.c | 4 +-- 4 files changed, 62 insertions(+), 22 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 01315e4e..e52f7572 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -948,8 +948,8 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_so. - \pre Длина ec->order в машинных словах равняется m. - \pre m > 1. + \pre wwWordSize(ec->order, ec->f->n + 1) == m. + \pre wwBitSize(ec->order, m) > pre->w. \pre d < ec->order. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. @@ -992,8 +992,8 @@ size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_soa. - \pre Длина ec->order в машинных словах равняется m. - \pre m > 1. + \pre wwWordSize(ec->order, ec->f->n + 1) == m. + \pre wwBitSize(ec->order, m) > pre->w. \pre d < ec->order. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. @@ -1028,6 +1028,47 @@ bool_t ecMulPreSOA( size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +/*! \brief Кратная точка с предвычислениями по схеме SH + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [m]d-кратной точки a, по которой построен контейнер pre + с предвычисленными по схеме SO точками: + \code + b <- d a. + \endcode + \pre Описание ec работоспособно. + \pre Описание группы точек в ec работоспособно. + \pre Контейнер pre работоспособен. + \pre pre->type == ec_pre_sh. + \pre 0 < d && d < ec->order. + \pre wwBitSize(d, m) делится на pre->w. + \pre Координаты a лежат в базовом поле. + \expect Описание ec корректно. + \expect Точки контейнера pre корректно рассчитаны по a. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} ecMulPreSH_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция регулярна по [m]d при выполнении следующих условий: + - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без + условных переходов; + - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, + выполняется без условных переходов; + - справедливо, по крайней мере, одно из условий: + a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; + b) ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение + выполняются по одним и тем же формулам без условных переходов. +*/ +bool_t ecMulPreSH( + word b[], /*!< [out] кратная точка */ + const ec_pre_t* pre, /*!< [in] предвычисленные точки */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t ecMulPreSH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + /*! \brief Кратная точка с предвычислениями по схеме OD Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, @@ -1040,8 +1081,8 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_od. - \pre Длина ec->order в машинных словах равняется m. - \pre m > 1. + \pre wwWordSize(ec->order, ec->f->n + 1) == m. + \pre wwBitSize(ec->order, m) > pre->w. \pre d < ec->order. \pre pre->w * pre->h >= wwBitSize(ec->order, m). \pre Координаты a лежат в базовом поле. @@ -1087,11 +1128,10 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_si. - \pre Длина ec->order в машинных словах равняется m. - \pre m > 1. + \pre wwWordSize(ec->order, ec->f->n + 1) == m. + \pre pre->w < wwBitSize(ec->order, m). + \pre wwBitSize(ec->order, m) <= pre->w * pre->h. \pre d < ec->order. - \pre wwBitSize(ec->order, m) <= pre->w * pre->h && - pre->w * pre->h < wwBitSize(ec->order, m) + B_PER_W. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. diff --git a/src/math/ec.c b/src/math/ec.c index d0b36375..7d50433c 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -682,11 +682,11 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecPreIsOperable(pre)); ASSERT(pre->type == ec_pre_so); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); - ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(zzIsOdd(ec->order, m)); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); - ASSERT(mb >= 2 * pre->w); + ASSERT(mb > pre->w); pre_count = SIZE_BIT_POS(pre->w - 1); pt_size = ec->d * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; @@ -771,11 +771,11 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecPreIsOperable(pre)); ASSERT(pre->type == ec_pre_soa); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); - ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(zzIsOdd(ec->order, m)); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); - ASSERT(mb >= 2 * pre->w); + ASSERT(mb > pre->w); pre_count = SIZE_BIT_POS(pre->w - 1); pt_size = 2 * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; @@ -813,7 +813,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, for (i = 1; i < pre->w; ++i) ecDbl(t, t, ec, stack); } - // завершающее удвоение + // t <- 2t ecDbl(t, t, ec, stack); // финишное сложение digit = wwGetBits(e, 0, pre->w); @@ -866,11 +866,11 @@ bool_t ecMulPreOD(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecPreIsOperable(pre)); ASSERT(pre->type == ec_pre_od); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); - ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(zzIsOdd(ec->order, m)); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = wwBitSize(ec->order, m); - ASSERT(2 * pre->w <= mb && mb <= pre->w * pre->h); + ASSERT(pre->w < mb && mb <= pre->w * pre->h); row_count = SIZE_BIT_POS(pre->w - 1); pt_size = 2 * ec->f->n; mask = WORD_BIT_POS(pre->w) - 2; @@ -1006,12 +1006,12 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, ASSERT(ecPreIsOperable(pre)); ASSERT(pre->type == ec_pre_si); ASSERT(wwWordSize(ec->order, ec->f->n + 1) == m); - ASSERT(zzIsOdd(ec->order, m) && m > 1); + ASSERT(zzIsOdd(ec->order, m)); ASSERT(wwCmp(d, ec->order, m) < 0); // размерности mb = pre->w * pre->h; + ASSERT(pre->w < wwBitSize(ec->order, m)); ASSERT(wwBitSize(ec->order, m) <= mb); - ASSERT(mb < wwBitSize(ec->order, m) + B_PER_W); pre_count = SIZE_BIT_POS(pre->w - 1); pt_size = 2 * ec->f->n; mask = WORD_BIT_POS(pre->w - 1) - 1; diff --git a/test/math/ec_test.c b/test/math/ec_test.c index 7bbcd897..a16e6493 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -118,7 +118,7 @@ bool_t ecTestEc(const ec_o* ec) ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), ecPreSH_deep(ec->deep), - ecPreSI_deep(n, ec->d, max_h, ec->deep), + ecPreSI_deep(n, ec->d, ec->deep, max_h), ecPreOD_deep(n, ec->d, ec->deep), ecMulPreSO_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index ca0b94f1..96868fdf 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -660,7 +660,7 @@ static size_t ecpSmallMultA_deep(size_t n, size_t f_deep, size_t w) /* state */ prngCOMBO_keep(),\ /* t1 */ O_OF_W(ec_d * n),\ /* t2 */ O_OF_W(ec_d * n),\ -/* r */ 1 +/* r */ (size_t)1 static bool_t ecPreChecksum(word a[], const ec_pre_t* pre, u32 seed, const ec_o* ec, void* stack) @@ -769,7 +769,7 @@ static bool_t ecpTestEc(const ec_o* ec) ecpSmallMultA_deep(n, ec->f->deep, max_w), ecPreSH_deep(ec->deep), ecpPreSHJ_deep(n, ec->f->deep, ec->deep), - ecPreChecksum_deep(n, ec->d, ec->f->deep)), + ecPreChecksum_deep(n, ec->d, ec->deep)), SIZE_MAX, &pre, &pt0, &pt1, &d, &stack); if (state == 0) From 57e435024f129e96fb04f8064987033090622dfe Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 3 Mar 2026 18:36:58 +0300 Subject: [PATCH 50/77] Add ecMulPreSH() --- include/bee2/math/ec.h | 18 ++- src/math/ec.c | 246 +++++++++++++++++++++++++++++++---------- src/math/ecp/ecp_pre.c | 31 +++--- test/math/ec_test.c | 47 ++++++-- test/math/ecp_bench.c | 171 ++++++++++++++++++---------- test/math/ecp_test.c | 38 +++---- 6 files changed, 376 insertions(+), 175 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index e52f7572..3303ec2e 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.02.24 +\version 2026.03.02 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -504,9 +504,9 @@ typedef enum \code a, 3a, ..., (2^w-1)a; \endcode - - SH (Small Hi): вычисляется 2^{w-1}+1 проективных точек + - SH (Small Hi): вычисляется 2^{w-1}+2 проективных точек \code - (2^{w-1})a, (2^{w-1}+1)a, ..., (2^w-1)a, 2a; + (2^{w-1})a, (2^{w-1}+1)a, ..., (2^w-1)a, (2^w)a, 2a; \endcode - OD (Odd Doubled): вычисляется h*2^{w-1} аффинных точек \code @@ -1032,7 +1032,7 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SO точками: + с предвычисленными по схеме SH точками: \code b <- d a. \endcode @@ -1040,8 +1040,7 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_sh. - \pre 0 < d && d < ec->order. - \pre wwBitSize(d, m) делится на pre->w. + \pre 0 < wwBitSize(d, m) && wwBitSize(d, m) - 1 делится на pre->w. \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. @@ -1049,14 +1048,11 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); случае. \deep{stack} ecMulPreSH_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: + - wwBitSize(d, m) < wwBitSize(ec->order, ec->f->n + 1); - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без условных переходов; - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, - выполняется без условных переходов; - - справедливо, по крайней мере, одно из условий: - a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение - выполняются по одним и тем же формулам без условных переходов. + выполняется без условных переходов. */ bool_t ecMulPreSH( word b[], /*!< [out] кратная точка */ diff --git a/src/math/ec.c b/src/math/ec.c index 7d50433c..3dfad6c3 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.02.24 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -128,21 +128,21 @@ bool_t ecGroupIsOperable(const ec_o* ec) * [или прямых вычислений в аффинных координатах] В функции ecPreSI() вычисляются точки - [c_{w-1} ... c_1 c_0]a = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a, -где c_i in {0, 1}, c_{w-1} == 1. Точка [c_{w-1}...c_1 c_0]a -размещается в массиве предвычисленных точек по индексу c_{w-1}...c_1 c_0. + [v_{w-1}, v_{w-2}, ..., v_0]a = \sum_{j=0}^{w-1}(1-2v_i)(2^h)^j a, +где v_i in {0, 1}, v_{w-1} == 1. Точка [v_{w-1},...,v_0]a +размещается в массиве предвычисленных точек по индексу v_{w-1}...v_0. -Слова с_{w-1}...c_1 c_0 просматриваются как слова кода Грея: соседние -слова отличаются только в одном бите. Если это бит номер i, то новая точка -получается из [с_{w-1}...с_1 с_0]a: -- добавлением точки (2(2^h)^i)a при c_i = 1; -- вычитанием точки (2(2^h)^i)a при c_i = 0. +Слова v_{w-1}...v_1 v_0 просматриваются как слова кода Грея: соседние +слова отличаются только в одном бите. Если это бит номер j, то новая точка +получается из [v_{w-1}...v_1 v_0]a: +- добавлением точки (2(2^h)^j)a при v_j = 1; +- вычитанием точки (2(2^h)^j)a при v_j = 0. -Определение следующего за code = с_{w-1}...c_1 c_0 слова выполняется +Определение следующего за v = v_{w-1}...v_1 v_0 слова выполняется следующим образом (см. [War03; глава 13], нумерация битов справа налево от 0): -1. Если code содержит четное число единиц, то pos <- 0. Иначе - pos <- (позиция первой справа единицы) + 1. -2. Инвертировать бит code номер pos. +1. Если v содержит четное число единиц, то j <- 0. Иначе + j <- (позиция первой справа единицы) + 1. +2. Инвертировать бит code номер j. [War03] Уоррен Генри Мл. Алгоритмические трюки для программистов, М.: Издательский дом "Вильямс", 2003. @@ -257,31 +257,35 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep) void ecPreSH(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { + const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; // pre ASSERT(ecIsOperable(ec)); ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + - O_OF_W((SIZE_BIT_POS(w - 1) + 1) * ec->d * ec->f->n), + pre, sizeof(ec_pre_t) + O_OF_W(pre_count * ec->d * ec->f->n), a, O_OF_W(2 * ec->f->n))); - // pre[2^{w-1}] <- 2a - ecDblA(ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), a, ec, stack); + // pre[-2, -1] <- (a, 2a) + ecFromA(ecPrePt(pre, pre_count - 2, ec), a, ec, stack); + ecDblA(ecPrePt(pre, pre_count - 1, ec), a, ec, stack); // вычислить малые кратные if (w > 1) { size_t i; // pre[0] <- 2^{w-1}a - wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), + wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 1, ec), ec->d * ec->f->n); for (i = 2; i < w; ++i) ecDbl(ecPrePt(pre, 0, ec), ecPrePt(pre, 0, ec), ec, stack); // pre[i] <- pre[i - 1] + a - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + for (i = 1; i < pre_count - 3; ++i) ecAddA(ecPrePt(pre, i, ec), ecPrePt(pre, i - 1, ec), a, ec, stack); + // pre[-3] <- 2 pre[0] + ecDbl(ecPrePt(pre, pre_count - 3, ec), ecPrePt(pre, 0, ec), ec, stack); } else - // pre[0] <- a - ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); + // pre[0, 1] <- pre[-1, -2] + wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 2, ec), + 2 * ec->d * ec->f->n); // заполнить служебные поля pre->type = ec_pre_sh; pre->w = w, pre->h = 0; @@ -583,11 +587,11 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) В функции ecMulPreSO() для определения кратной точки b = da при нечетной кратности d используется ее представление в форме - d_{k-1} (2^w)^{k-1} + ... + d_1 (2^w) + d_0. -Здесь d_i \in {\pm 1, \pm 3, ..., \pm (2^w-1)} -- ненулевые (нечетные) цифры -со знаком (signed non-zero). Предполагается, что точка a лежит в группе -нечетного порядка order, (2^w)^{k-1} < order < (2^w)^k. Представление SO -предложено в [OkeTak03]. + e_{h-1} (2^w)^{h-1} + ... + e_1 (2^w) + e_0. +Здесь e_i \in {\pm 1, \pm 3, ..., \pm (2^w-1)} -- ненулевые нечетные цифры +со знаком. Предполагается, что точка a лежит в группе нечетного порядка order, +(2^w)^{h-1} < order < (2^w)^h. Представление SO предложено использовать +в [OkeTak03]. Если кратность d четная, то она предварительно меняется на order-d. Для этого используется функция zzSubIf(). @@ -595,37 +599,37 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) Реализован следующий алгоритм (см. [BCL+14; p. 9-11, algorithm 1], [APS22]): 1. Для i = 0, 1, ..., 2^{w-1}-1: 1) pre[i] <- (2i + 1)a. // предвычисления: SO -2. t <- pre[d_{k-1} / 2]. -3. Для i = k-2, ..., 2, 1: +2. t <- pre[e_{h-1} / 2]. +3. Для i = h-2, ..., 2, 1: 1) t <- 2^w t; // P <- 2P - 1) t <- t + sgn(d_i) pre[|d_i|/2]. // P <- P + P + 2) t <- t + sgn(e_i) pre[|e_i|/2]. // P <- P + P 4. t <- 2^w t. -5. t <- t + sgn(d_0) pre[|d_0|/2]. // A <- P + P +5. t <- t + sgn(e_0) pre[|e_0|/2]. // A <- P + P 6. Возвратить t. -\remark Сложение t + sgn(d_0) pre[|d_0|/2] вынесено за рамки основного цикла +\remark Сложение t + sgn(e_0) pre[|e_0|/2] вынесено за рамки основного цикла на шаге 3, потому что при этом (и только при этом) сложении может произойти -исключительная ситуация -- совпадение операндов t и sgn(d_0) pre[|d_0|/2] +исключительная ситуация -- совпадение операндов t и sgn(e_0) pre[|e_0|/2] с переключением от сложения к удвоению. Завершающее сложение выполняется с помощью функции интерфейса ec_finadd_i, если таковая указана в описании кривой. Предполагается, что функция ec_finadd_i регулярна, и тогда сложение и удвоение будут выполняться по одним и тем же формулам. -\remark Расчет цифр d_i (см. [APS22]): +\remark Расчет цифр e_i (см. [APS22]): 1. Записать - d = d_{k-1} (2^w)^{k-1} + d_1 (2^w) + d_0, + d = d_{h-1} (2^w)^{h-1} + d_1 (2^w) + d_0, где d_i \in {0, 1, ..., 2^w-1}. -2. (d_{h-1}, borrow) <- (d_{h-1} + (d_{h-1} % 2), d_{k-1} % 2). -3. Для i = k-2, ...., 1, 0: - 1) (d_i, borrow) <- (d_i - borrow * 2^w + (d_i % 2), d_i % 2). +2. (e_{h-1}, borrow) <- (d_{h-1} + (d_{h-1} % 2), d_{h-1} % 2). +3. Для i = h-2, ...., 1, 0: + 1) (e_i, borrow) <- (d_i - borrow * 2^w + (d_i % 2), d_i % 2). \remark Выражение - borrow ? d_i : d_i - 2^w, + e_i = borrow ? d_i : d_i - 2^w, по которому определяется индекс в таблице pt, вычисляется следующим образом: - mask <- 2^w - 2, d_i <- d_i ^ ((0 - borrow) & mask). -Поскольку младший разряд mask нулевой, сохраняется четность цифры d_i и эту + mask <- 2^w - 2, e_i <- d_i ^ ((0 - borrow) & mask). +Поскольку младший разряд mask нулевой, в e_i переносится четность d_i, и эту четность можно использовать для пересчета borrow: - borrow <- d_i % 2. + borrow <- e_i % 2. В функции ecMulPreSOA() предварительно рассчитанные точки являются аффинными, а не проективными. Это позволяет заменить регулярные сложения P <- P + P @@ -634,12 +638,12 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) В функции ecMulPreOD() предполагается, что аффинные точки предварительно рассчитаны для каждой цифры обрабатываемого скаляра d. Это позволяет избавиться от удвоений: -1. Для i = 0, 1, ..., k-1 и j = 0, 1, ..., 2^{w-1}-1: +1. Для i = 0, 1, ..., h-1 и j = 0, 1, ..., 2^{w-1}-1: 1) pre[i][j] <- (2^w)^i(2j + 1)a. // предвычисления: OD -2. t <- pre[k-1][d_{k-1}/2]. -3. Для i = k-2, ..., 2, 1: - 1) t <- t + sgn(d_i) pre[i]t[|d_i|/2]. // P <- P + P -4. t <- t + sgn(d_0) pre[0][|d_0|/2]. // A <- P + P +2. t <- pre[h-1][e_{h-1}/2]. +3. Для i = h-2, ..., 2, 1: + 1) t <- t + sgn(e_i) pre[i]t[|e_i|/2]. // P <- P + P +4. t <- t + sgn(e_0) pre[0][|e_0|/2]. // A <- P + P 5. Возвратить t. [OkeTak03] Okeya K., Takagi T. The width-w NAF method provides small memory @@ -935,24 +939,28 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) (см. также [HMV04; algorithm 3.44]). Для определения кратной точки b = da при нечетной кратности d используется -ее представление в форме - d_{hw-1} 2^{hw-1} + d_{hw-2} 2^{hw-2} + ... + d_1 2^1 + d_0, -где d_i \in {\pm 1}. +ее представление в виде + e_{hw-1} 2^{hw-1} + e_{hw-2} 2^{hw-2} + ... + e_1 2^1 + e_0, +где e_i \in {\pm 1}. Как обычно, если кратность d четная, то она предварительно меняется на -order - d. +order-d. + +Обозначения: +* если e = (e_{hw-1},e_{hw-2},...,e_0), то + e_{h,i} = (e_{(w-1)h+i},e_{(w-2)h+i},...,e_i), i = 0,1,...,h-1; +* если v = (v_{w-1},v_{w-2},...,v_0) -- двоичный вектор, то + sgn(v) = 1-2*v_{w-1}, |v| = sgn(v) > 0 ? v : ~v. Реализован следующий алгоритм: -1. Для с_{w-1} = 0 и с_{w-2}...c_0 \in {0,1}^{w-1}: - 1) pre[с_{w-1}с_{w-2}...c_0] = \sum_{i=0}^{w-1}(1-2c_i)(2^h)^i a. -2. t <- pre[d_{hw-1}d_{(h(w-1)-1}... d_{h-1}]. -3. Для i = 2, 3, ..., h-1: - 1) t <- 2 t; - 2) t <- t + sgn(d_{hw-i}) - pt[d_{hw-i}d_{h(w-1)-i}...d_{h-i} ^ d_{hw-i}...d_{hw-i}]. -4. t <- 2 t. -5. t <- t + sgn(d_{hw-h}) - pt[d_{hw-h}d_{h(w-1)-h)}...d_0 ^ d_{hw-h}...d_{hw-h}]. +1. Для v_{w-1} = 0 и v_{w-2},..., v_0 \in {0,1}: + 1) pre[v_{w-1},v_{w-2},...,v_0] = \sum_{j=0}^{w-1}(1-2v_j)(2^h)^j a. +2. t <- pre[e_{h,h-1}]. +3. t <- 2 t. +4. Для i = h-2, h-3, ..., 1: + 1) t <- t + sgn(e_{h,i})pt[|e_{h,i}|]. + 2) t <- 2 t. +5. t <- t + sgn(e_{h,0})pt[|e_{h,0}|]. 6. Возвратить t. Переход от двоичного представления @@ -1079,6 +1087,124 @@ size_t ecMulPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } +/* +******************************************************************************* +Кратная точка: метод SH + +В функции ecMulPreSH() определяется кратная точка b = (2^l + d)a. Используется +окно ширины w. Предполагается, что d < 2^l, l < bitlen(order) и w | l. Пусть +h = l/w + 1. + +Скаляр 2^l+d записывается в виде + e_{h-1} (2^w)^{h-1} + ... + e_1 (2^w) + e_0. +Здесь e_i \in {2^{w-1}, \pm (2^{w-1}+1),...,\pm 2^w}, i = 0, 1,..., h-2, -- +ненулевые цифры с установленным старшим разрядом и e_{h-1} \in {1, 2}. + +Реализован следующий алгоритм: +1. Для i = 0, 1, ..., 2^{w-1}: + 1) pre[i] <- (2^{w-1} + i)a; // предвычисления: SH + 2) pre[2^{w-1} + 1] <- a; + 3) pre[2^{w-1} + 2] <- 2a. +2. t <- pre[2^{w-1}+e_{h-1}]. +3. Для i = h-2, ..., 1, 0: + 1) t <- 2^w t; // P <- 2P + 2) t <- t + sgn(e_i) pre[|e_i|-2^{w-1}]. // P <- P + P +4. Возвратить t. + +\remark Расчет цифр e_i: +1. Записать + 2^l + d = d_{h-1} (2^w)^{h-1} + d_1 (2^w) + d_0, + где d_i \in {0, 1, ..., 2^w-1}, d_{h-1} = 1. +2. e_0 <- d_0. +3. Для i = 0, 1, ...., h-1: + 1) (e_i, e_{i+1}) <- e_i < 2^{w-1} ? (e_i-2^w, d_{i+1}+1) : (e_i, d_{i+1}). + +\remark Цифра e_i хранится в виде (w+1)-разрядного слова: +* старший бит -- признак отрицательности; +* младшие w+1 битов -- |e_i|-2^{w-1}. +******************************************************************************* +*/ + +#define ecMulPreSH_local(n, ec_d, m)\ +/* e */ O_OF_W(2 * m),\ +/* t */ O_OF_W(ec_d * n),\ +/* pt */ O_OF_W(ec_d * n) + +bool_t ecMulPreSH(word b[], const ec_pre_t* pre, const ec_o* ec, + const word d[], size_t m, void* stack) +{ + register word digit; + register word carry; + size_t mb; + size_t pre_count; + size_t pt_size; + word mask; + word hi; + size_t i; + word* e; /* [2 * m] */ + word* t; /* [ec->d * ec->f->n] */ + word* pt; /* [ec->d * ec->f->n] */ + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(ecPreIsOperable(pre)); + ASSERT(pre->type == ec_pre_sh); + // размерности + mb = wwBitSize(d, m); + ASSERT(0 < mb && (mb - 1) % pre->w == 0); + pre_count = SIZE_BIT_POS(pre->w - 1) + 3; + pt_size = ec->d * ec->f->n; + mask = ~WORD_BIT_POS(pre->w); + hi = WORD_BIT_POS(pre->w - 1); + // разметить стек + memSlice(stack, + ecMulPreSH_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &e, &t, &pt, &stack); + // перекодирование + for (i = 0, carry = 0; i < (mb - 1) / pre->w; ++i) + { + // digit <- d_i + carry + digit = wwGetBits(d, i * pre->w, pre->w); + digit += carry; + // carry <- (digit < 2^{w-1}) + carry = wordLess01(digit, hi); + // digit <- carry ? digit - 2^w : digit + digit ^= SIZE_0 - carry; + digit += carry; + // \post w-й бит digit: признак отрицательности + // \post младшие w-1 битов digit: |digit - 2^w| + // сохранить digit в виде (признак, |digit - 2^w| - 2^{w-1}) + wwSetBits(e, i * (pre->w + 1), pre->w + 1, digit - hi); + } + // обработать старшую цифру + ASSERT(wwTestBit(d, i * pre->w)); + wwSel(t, ecPrePt(pre, pre_count - 2, ec), 2, pt_size, carry); + // обработать остальные цифры + while (i--) + { + size_t j; + // t <- 2^w t + for (j = 0; j < pre->w; ++j) + ecDbl(t, t, ec, stack); + // сложить + digit = wwGetBits(e, i * (pre->w + 1), pre->w + 1); + wwSel(pt, pre->pts, pre_count - 2, pt_size, digit & mask); + ecSgn(pt, digit >> pre->w, ec, stack); + ecAdd(t, t, pt, ec, stack); + } + // очистка и возврат + CLEAN2(digit, carry); + return ecToA(b, t, ec, stack); +} + +size_t ecMulPreSH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreSH_local(n, ec_d, m), + ec_deep, + SIZE_MAX); +} + /* ******************************************************************************* Имеет порядок? diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index 82b4008a..c4bfc8fc 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.02.24 +\version 2026.03.03 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -431,10 +431,11 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) Предвычисления: схема SH, якобиевы координаты Алгоритм (w > 1): -1. pre[2^{w-1}] <- 2a. // J <- 2A -2. pre[0] <- 2^{w-2}pre[2^{w-1}]. // J <- 2J -3. t <- a: t -- якобиева точка, Z(t) = Z(pre[0]). -4. Для i = 1, 2, ..., 2^{w-1}: +1. pre[2^{w-1}+1] <- a. // J <- A +2. pre[2^{w-1}+2] <- 2a. // J <- 2A +3. pre[0] <- 2^{w-2}pre[2^{w-1}]. // J <- 2J +4. t <- a: t -- якобиева точка, Z(t) = Z(pre[0]). +5. Для i = 1, 2, ..., 2^{w-1}: 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t ******************************************************************************* */ @@ -445,28 +446,29 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, void* stack) { + const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; word* t; /* [3 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + - O_OF_W((SIZE_BIT_POS(w - 1) + 1) * 3 * ec->f->n), - a, O_OF_W(2 * ec->f->n))); + O_OF_W(pre_count * 3 * ec->f->n), a, O_OF_W(2 * ec->f->n))); // разметить стек memSlice(stack, ecpPreSHJ_local(ec->f->n), SIZE_0, SIZE_MAX, &t, &stack); - // pre[2^{w-1}] <- 2a - ecDblA(ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), a, ec, stack); + // pre[-2, -1] <- (a, 2a) + ecFromA(ecPrePt(pre, pre_count - 2, ec), a, ec, stack); + ecDblA(ecPrePt(pre, pre_count - 1, ec), a, ec, stack); // вычислить малые кратные if (w > 1) { const size_t n = ec->f->n; size_t i; // pre[0] <- 2^{w-1}a - wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), - 3 * ec->f->n); + wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 1, ec), + ec->d * ec->f->n); for (i = 2; i < w; ++i) ecDbl(ecPrePt(pre, 0, ec), ecPrePt(pre, 0, ec), ec, stack); // t <- a: z(t) = z(pre[0]) @@ -476,13 +478,14 @@ void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, qrMul(ecX(t), ecX(t), ecX(a), ec->f, stack); qrMul(ecY(t, n), ecY(t, n), ecY(a, n), ec->f, stack); // pre[i] <- pre[i - 1] + a - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + for (i = 1; i <= pre_count - 3; ++i) ecpReaddJ(ecPrePt(pre, i, ec), t, t, ecPrePt(pre, i - 1, ec), ec, stack); } else - // pre[0] <- a - ecFromA(ecPrePt(pre, 0, ec), a, ec, stack); + // pre[0, 1] <- pre[-1, -2] + wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 2, ec), + 2 * 3 * ec->f->n); // заполнить служебные поля pre->type = ec_pre_sh; pre->w = w, pre->h = 0; diff --git a/test/math/ec_test.c b/test/math/ec_test.c index a16e6493..b78209f1 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves \project bee2/test \created 2026.02.12 -\version 2026.02.24 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -84,7 +84,7 @@ bool_t ecTestEc(const ec_o* ec) const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; const size_t max_pre_count = - MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 1); + MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 3); // состояние void* state; ec_pre_t* pre; /* [max_pre_count проективных точек] */ @@ -109,7 +109,7 @@ bool_t ecTestEc(const ec_o* ec) O_OF_W(ec->d * n), O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(15, + utilMax(16, ec->deep, ecHasOrderA_deep(n, ec->d, ec->deep, n), ecAddAA_deep(n, ec->d, ec->deep), @@ -124,6 +124,7 @@ bool_t ecTestEc(const ec_o* ec) ecMulPreSOA_deep(n, ec->d, ec->deep, n), ecMulPreOD_deep(n, ec->d, ec->deep, n), ecMulPreSI_deep(n, ec->d, ec->deep, n), + ecMulPreSH_deep(n, ec->d, ec->deep, n), ecAddMulA_deep(n, ec->d, ec->deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, @@ -407,26 +408,29 @@ bool_t ecTestEc(const ec_o* ec) // предвычисления по схеме SH for (w = min_w; w <= max_w; ++w) { - // pre[0..2^w] <- (2^{w-1}, 2^{w-1}+1, ..., 2^w-1, 2)base + const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; + // pre[0..pre_count) <- (2^{w-1}, 2^{w-1}+1, ..., 2^w-1, 2^w, 1, 2)base ecPreSH(pre, ec->base, w, ec, stack); - // pt0 == 2base? - if (!ecToA(pt0, ecPrePt(pre, SIZE_BIT_POS(w - 1), ec), ec, stack) || + // pre[-2..) == (1, 2)base? + if (!ecToA(pt0, ecPrePt(pre, pre_count - 2, ec), ec, stack) || + !wwEq(pt0, ec->base, 2 * n) || + !ecToA(pt0, ecPrePt(pre, pre_count - 1, ec), ec, stack) || !ecAddAA(pt1, ec->base, ec->base, ec, stack) || !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; } - // pt0 <- \sum_{i=0}^{2^{w-1}-1} 2^{2^{w-1}-1-i} pre[i] + // pt0 <- \sum_{i=0}^{2^{w-1}} 2^{2^{w-1}-i} pre[i] wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + for (i = 1; i <= SIZE_BIT_POS(w - 1); ++i) { ecDbl(pt0, pt0, ec, stack); ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); } - // pt0 == \sum_{i=0}^{2^{w-1}-1} (2^{w-1}+i)2^{2^{w-1}-1-i} base? + // pt0 == \sum_{i=0}^{2^{w-1}} (2^{w-1}+i)2^{2^{w-1}-i} base? wwSetW(d, n, WORD_BIT_POS(w - 1)); - for (i = 1; i < SIZE_BIT_POS(w - 1); ++i) + for (i = 1; i <= SIZE_BIT_POS(w - 1); ++i) { wwShHi(d, n, 1); zzAddW2(d, n, WORD_BIT_POS(w - 1) + (word)i); @@ -615,6 +619,29 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } + // кратная точка: ecMulA vs ecMulPreSH + for (w = min_w; w <= max_w; ++w) + { + const size_t m = wwWordSize(ec->order, n + 1); + const size_t mb = (wwBitSize(ec->order, n + 1) / 3 / w) * w; + if (mb < w) + continue; + wwCopy(d, ec->order, m); + wwTrimHi(d, m, mb); + wwSetBit(d, mb, TRUE); + ecPreSH(pre, ec->base, w, ec, stack); + while (!wwIsZero(d, m)) + { + if (!ecMulPreSH(pt0, pre, ec, d, m, stack) || + !ecMulA(pt1, ec->base, ec, d, m, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, m, w); + } + } // все хорошо blobClose(state); return TRUE; diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 13dc771f..9c095f91 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.02.23 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -16,49 +16,57 @@ #include #include #include +#include #include /* ******************************************************************************* Оценка производительности на заданной кривой + +\expect Функции ecMulPreSO(), ecMulPreSOA(), ecMulPreOD(), ecMulPreSI() и +ecMulPreSH() регулярны на кривой ec. Поэтому скаляр d не рандомизируется, +а вместо среднего времени замеряется минимальное. ******************************************************************************* */ static bool_t ecpBenchEc(const ec_o* ec) { const size_t n = ec->f->n; - const size_t no = ec->f->no; + const size_t m = wwWordSize(ec->order, n + 1); + const size_t mo = wwOctetSize(ec->order, m); const size_t min_w = 3; const size_t max_w = 6; - const size_t min_h = (B_OF_O(no) + max_w - 1) / max_w; - const size_t max_h = (B_OF_O(no) + min_w - 1) / min_w; + const size_t min_h = (B_OF_O(mo) + max_w - 1) / max_w; + const size_t max_h = (B_OF_O(mo) + min_w - 1) / min_w; const size_t max_pre_count = - MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 1); + MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 3); const size_t reps = 200; void* state; octet* combo_state; /* [prngCOMBO_keep()] */ word* pt; /* [2 * n] */ - word* d; /* [n] */ + word* d; /* [m] */ ec_pre_t* pre; /* [max_pre_count проективных точек] */ void* stack; // создать состояние state = blobCreate2( prngCOMBO_keep(), O_OF_W(2 * n), - O_OF_W(n), + O_OF_W(m), sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), - utilMax(11, - ecMulA_deep(n, ec->d, ec->deep, n), + utilMax(13, + ecMulA_deep(n, ec->d, ec->deep, m), ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), ecPreOD_deep(n, ec->d, ec->deep), ecPreSI_deep(n, ec->d, ec->deep, max_h), + ecPreSH_deep(ec->deep), ecpPreSOJ_deep(n, ec->f->deep), ecpPreSOA_deep(n, ec->f->deep, max_w), - ecMulPreSO_deep(n, ec->d, ec->deep, n), - ecMulPreSOA_deep(n, ec->d, ec->deep, n), - ecMulPreOD_deep(n, ec->d, ec->deep, n), - ecMulPreSI_deep(n, ec->d, ec->deep, n)), + ecMulPreSO_deep(n, ec->d, ec->deep, m), + ecMulPreSOA_deep(n, ec->d, ec->deep, m), + ecMulPreOD_deep(n, ec->d, ec->deep, m), + ecMulPreSI_deep(n, ec->d, ec->deep, m), + ecMulPreSH_deep(n, ec->d, ec->deep, m)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); if (state == 0) @@ -75,8 +83,8 @@ static bool_t ecpBenchEc(const ec_o* ec) { for (i = 0, ticks = tmTicks(); i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); - ecMulA(pt, ec->base, ec, d, n, stack); + prngCOMBOStepR(d, mo, combo_state); + ecMulA(pt, ec->base, ec, d, m, stack); } ticks = tmTicks() - ticks; printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", @@ -86,116 +94,157 @@ static bool_t ecpBenchEc(const ec_o* ec) // ecpPre+ecMulPre[SO] for (w = MAX2(3, min_w); w <= max_w; ++w) { - for (i = 0, ticks = tmTicks(); i < reps; ++i) + wwSetW(d, m, 1); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); + tm_ticks_t t = tmTicks(); ecpPreSOJ(pre, ec->base, w, ec, stack); - ecMulPreSO(pt, pre, ec, d, n, stack); + ecMulPreSO(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; } - ticks = tmTicks() - ticks; printf(" ecpPre+ecMulPre[SO,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecpPre+ecMulPre[SOA] for (w = MAX2(3, min_w); w <= max_w; ++w) { - for (i = 0, ticks = tmTicks(); i < reps; ++i) + wwSetW(d, m, 1); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); + tm_ticks_t t = tmTicks(); ecpPreSOA(pre, ec->base, w, ec, stack); - ecMulPreSOA(pt, pre, ec, d, n, stack); + ecMulPreSOA(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; } - ticks = tmTicks() - ticks; printf(" ecpPre+ecMulPre[SOA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecMulPre[SO] for (w = min_w; w <= max_w; ++w) { ecPreSO(pre, ec->base, w, ec, stack); - for (i = 0, ticks = tmTicks(); i < reps; ++i) + wwSetW(d, m, 1); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); - ecMulPreSO(pt, pre, ec, d, n, stack); + tm_ticks_t t = tmTicks(); + ecMulPreSO(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; } - ticks = tmTicks() - ticks; printf(" ecMulPre[SO,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecMulPre[SOA] for (w = min_w; w <= max_w; ++w) { ecPreSOA(pre, ec->base, w, ec, stack); - for (i = 0, ticks = tmTicks(); i < reps; ++i) + wwSetW(d, m, 1); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); - ecMulPreSOA(pt, pre, ec, d, n, stack); + tm_ticks_t t = tmTicks(); + ecMulPreSOA(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; } - ticks = tmTicks() - ticks; printf(" ecMulPre[SOA,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecMulPre[OD] for (w = min_w; w <= max_w; ++w) { - size_t h = (B_OF_O(no) + w - 1) / w; + size_t h = (B_OF_O(mo) + w - 1) / w; ecPreOD(pre, ec->base, w, h, ec, stack); - for (i = 0, ticks = tmTicks(); i < reps; ++i) + wwSetW(d, m, 1); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); - ecMulPreOD(pt, pre, ec, d, n, stack); + tm_ticks_t t = tmTicks(); + ecMulPreOD(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; } - ticks = tmTicks() - ticks; printf(" ecMulPre[OD,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecMulPre[SI] for (w = min_w; w <= max_w; ++w) { - size_t h = (B_OF_O(no) + w - 1) / w; + size_t h = (B_OF_O(mo) + w - 1) / w; ecPreSI(pre, ec->base, w, h, ec, stack); - for (i = 0, ticks = tmTicks(); i < reps; ++i) + wwSetW(d, m, 1); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { - prngCOMBOStepR(d, no, combo_state); - ecMulPreSI(pt, pre, ec, d, n, stack); + tm_ticks_t t = tmTicks(); + ecMulPreSI(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; } - ticks = tmTicks() - ticks; printf(" ecMulPre[SI,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + } + // ecMulPre[SH] + for (w = min_w; w <= max_w; ++w) + { + const size_t mb = (B_OF_O(mo) / 2 / w) * w; + if (mb < w) + continue; + ecPreSH(pre, ec->base, w, ec, stack); + wwSetW(d, m, 1); + wwSetBit(d, mb, TRUE); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + ecMulPreSH(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf(" ecMulPre[SH,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecPre[SO] for (w = min_w; w <= max_w; ++w) { - for (i = 0, ticks = tmTicks(); i < reps; ++i) + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); ecPreSO(pre, ec->base, w, ec, stack); - ticks = tmTicks() - ticks; + if ((t = tmTicks() - t) < ticks) + ticks = t; + } printf(" ecPre[SO,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } // ecpPre[SO] for (w = MAX2(3, min_w); w <= max_w; ++w) { - for (i = 0, ticks = tmTicks(); i < reps; ++i) + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); ecpPreSOJ(pre, ec->base, w, ec, stack); - ticks = tmTicks() - ticks; + if ((t = tmTicks() - t) < ticks) + ticks = t; + } printf(" ecpPre[SO,w=%u]: %u cycles/pre [%u pre/sec]\n", (unsigned)w, - (unsigned)(ticks / reps), - (unsigned)tmSpeed(reps, ticks)); + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); } } // завершение diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 96868fdf..33deb3ea 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.02.24 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -683,7 +683,7 @@ static bool_t ecPreChecksum(word a[], const ec_pre_t* pre, u32 seed, // число предвычисленных точек count = SIZE_BIT_POS(pre->w - 1); if (pre->type == ec_pre_sh) - ++count; + count += 3; else if (pre->type == ec_pre_so) count *= pre->h; // проективные предвычисленные точки? @@ -736,7 +736,7 @@ static bool_t ecpTestEc(const ec_o* ec) const size_t n = ec->f->n; const size_t min_w = 1; const size_t max_w = 6; - const size_t max_pre_count = SIZE_BIT_POS(max_w - 1) + 1; + const size_t max_pre_count = SIZE_BIT_POS(max_w - 1) + 3; // состояние void* state; ec_pre_t* pre; /* [max_pre_count проективных точек] */ @@ -810,14 +810,14 @@ static bool_t ecpTestEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { const u32 seed = 23; - bool_t is_zero; + bool_t nz; // эталонная контрольная сумма ecPreSO(pre, ec->base, w, ec, stack); - is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); + nz = ecPreChecksum(pt0, pre, seed, ec, stack); // проверить ecpPreSOJ() ecpPreSOJ(pre, ec->base, w, ec, stack); - if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || - !is_zero && !wwEq(pt0, pt1, 2 * n)) + if (ecPreChecksum(pt1, pre, seed, ec, stack) != nz || + nz && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; @@ -826,8 +826,8 @@ static bool_t ecpTestEc(const ec_o* ec) if (w < 3) continue; ecpSmallMultJ(pre, ec->base, w, ec, stack); - if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || - !is_zero && !wwEq(pt0, pt1, 2 * n)) + if (ecPreChecksum(pt1, pre, seed, ec, stack) != nz || + nz && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; @@ -837,14 +837,14 @@ static bool_t ecpTestEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { const u32 seed = 34; - bool_t is_zero; + bool_t nz; // эталонная контрольная сумма ecPreSOA(pre, ec->base, w, ec, stack); - is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); + nz = ecPreChecksum(pt0, pre, seed, ec, stack); // проверить ecpPreSOA() ecpPreSOA(pre, ec->base, w, ec, stack); - if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || - !is_zero && !wwEq(pt0, pt1, 2 * n)) + if (ecPreChecksum(pt1, pre, seed, ec, stack) != nz || + nz && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; @@ -853,8 +853,8 @@ static bool_t ecpTestEc(const ec_o* ec) if (w < 3) continue; ecpSmallMultA(pre, ec->base, w, ec, stack); - if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || - !is_zero && !wwEq(pt0, pt1, 2 * n)) + if (ecPreChecksum(pt1, pre, seed, ec, stack) != nz || + nz && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; @@ -864,14 +864,14 @@ static bool_t ecpTestEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { const u32 seed = 43; - bool_t is_zero; + bool_t nz; // эталонная контрольная сумма ecPreSH(pre, ec->base, w, ec, stack); - is_zero = ecPreChecksum(pt0, pre, seed, ec, stack); + nz = ecPreChecksum(pt0, pre, seed, ec, stack); // проверить ecpPreSHJ() ecpPreSHJ(pre, ec->base, w, ec, stack); - if (ecPreChecksum(pt1, pre, seed, ec, stack) != is_zero || - !is_zero && !wwEq(pt0, pt1, 2 * n)) + if (ecPreChecksum(pt1, pre, seed, ec, stack) != nz || + nz && !wwEq(pt0, pt1, 2 * n)) { blobClose(state); return FALSE; From ca3a6e1dbb778ca05c1cf5f478718290bee26ae7 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 3 Mar 2026 21:09:40 +0300 Subject: [PATCH 51/77] Make Bake protocols constant-time --- include/bee2/math/ec.h | 12 ---------- src/crypto/bake/bake_bmqv.c | 30 +++++++++++++---------- src/crypto/bake/bake_bpace.c | 16 ++++++------- src/crypto/bake/bake_bsts.c | 25 +++++++++++++------- src/crypto/bign/bign_lcl.h | 42 ++++++++++++++++++++++++++++---- src/crypto/bign/bign_misc.c | 46 +++++++++++++++++++++++++++++++++++- src/math/ecp/ecp_pre.c | 3 +++ 7 files changed, 127 insertions(+), 47 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 3303ec2e..81c533d5 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -773,7 +773,6 @@ bool_t ecPreIsOperable( с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * ec->d * ec->f->n)). @@ -797,7 +796,6 @@ size_t ecPreSO_deep(size_t n, size_t ec_d, size_t ec_deep); с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). @@ -825,7 +823,6 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). @@ -851,7 +848,6 @@ size_t ecPreSH_deep(size_t ec_deep); с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \pre 0 < w && w < MIN2(B_PER_W, B_PER_S) && h > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * h * 2 * ec->f->n)). @@ -880,7 +876,6 @@ size_t ecPreOD_deep(size_t n, size_t ec_d, size_t ec_deep); с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется в контейнере pre. \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \pre 0 < w && w < MIN2(B_PER_W, B_PER_S) && h > 0. \pre memIsValid(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). @@ -917,7 +912,6 @@ size_t ecPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t h); b <- d a. \endcode \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точка a лежит на ec. \return TRUE, если кратная точка отличается от O, и FALSE в противном @@ -951,7 +945,6 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre wwWordSize(ec->order, ec->f->n + 1) == m. \pre wwBitSize(ec->order, m) > pre->w. \pre d < ec->order. - \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном @@ -995,7 +988,6 @@ size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre wwWordSize(ec->order, ec->f->n + 1) == m. \pre wwBitSize(ec->order, m) > pre->w. \pre d < ec->order. - \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном @@ -1041,7 +1033,6 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre Контейнер pre работоспособен. \pre pre->type == ec_pre_sh. \pre 0 < wwBitSize(d, m) && wwBitSize(d, m) - 1 делится на pre->w. - \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном @@ -1081,7 +1072,6 @@ size_t ecMulPreSH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre wwBitSize(ec->order, m) > pre->w. \pre d < ec->order. \pre pre->w * pre->h >= wwBitSize(ec->order, m). - \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном @@ -1128,7 +1118,6 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre pre->w < wwBitSize(ec->order, m). \pre wwBitSize(ec->order, m) <= pre->w * pre->h. \pre d < ec->order. - \pre Координаты a лежат в базовом поле. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном @@ -1164,7 +1153,6 @@ size_t ecMulPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); Проверяется, что аффинная точка [2 * ec->f->n]a имеет порядок [m]q в группе точек кривой ec. \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. \pre q > 0. \expect Описание ec корректно. \expect Точка a лежит на ec. diff --git a/src/crypto/bake/bake_bmqv.c b/src/crypto/bake/bake_bmqv.c index fe00446e..b96f1ed2 100644 --- a/src/crypto/bake/bake_bmqv.c +++ b/src/crypto/bake/bake_bmqv.c @@ -4,7 +4,7 @@ \brief STB 34.101.66 (bake): the BMQV protocol \project bee2 [cryptographic library] \created 2014.04.14 -\version 2026.01.08 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -164,7 +164,7 @@ err_t bakeBMQVStep2(octet out[], void* state) s->settings->rng_state)) return ERR_BAD_RNG; // Vb <- ub G - if (!ecMulA(Vb, s->ec->base, s->ec, s->u, n, stack)) + if (!bignMulBase(Vb, s->ec, s->u, stack)) return ERR_BAD_PARAMS; // out <- qrTo(out, ecX(Vb), s->ec->f, stack); @@ -182,7 +182,7 @@ static size_t bakeBMQVStep2_deep(size_t n, size_t f_deep, size_t ec_d, bakeBMQVStep2_local(n), utilMax(2, f_deep, - ecMulA_deep(n, ec_d, ec_deep, n)), + bignMulBase_deep(n, ec_d, ec_deep)), SIZE_MAX); } @@ -221,6 +221,8 @@ err_t bakeBMQVStep3(octet out[], const octet in[], const bake_cert* certb, !memIsValid(certb->data, certb->len) || certb->val == 0) return ERR_BAD_INPUT; + if (n % 2) + return ERR_NOT_IMPLEMENTED; // разметить стек memSlice(s->stack, bakeBMQVStep3_local(n, no), SIZE_0, SIZE_MAX, @@ -242,7 +244,7 @@ err_t bakeBMQVStep3(octet out[], const octet in[], const bake_cert* certb, s->settings->rng_state)) return ERR_BAD_RNG; // Va <- ua G - if (!ecMulA(Va, s->ec->base, s->ec, s->u, n, stack)) + if (!bignMulBase(Va, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo((octet*)Va, ecX(Va), s->ec->f, stack); qrTo((octet*)Va + no, ecY(Va, n), s->ec->f, stack); @@ -261,13 +263,13 @@ err_t bakeBMQVStep3(octet out[], const octet in[], const bake_cert* certb, zzSubMod(sa, s->u, sa, s->ec->order, n); // K <- sa(Vb - (2^l + t)Qb), K == O => K <- G t[n / 2] = 1; - if (!ecMulA(Qb, Qb, s->ec, t, n / 2 + 1, stack)) + if (!bignMulA2(Qb, Qb, s->ec, t, n / 2 + 1, stack)) return ERR_BAD_PARAMS; if (!ecpSubAA(Vb, Vb, Qb, s->ec, stack)) qrTo(K, s->ec->base, s->ec->f, stack); else { - if (!ecMulA(Vb, Vb, s->ec, sa, n, stack)) + if (!bignMulA(Vb, Vb, s->ec, sa, stack)) return ERR_BAD_PARAMS; qrTo(K, ecX(Vb), s->ec->f, stack); } @@ -309,10 +311,11 @@ static size_t bakeBMQVStep3_deep(size_t n, size_t f_deep, size_t ec_d, { return memSliceSize( bakeBMQVStep3_local(n, O_OF_W(n)), - utilMax(9, + utilMax(10, f_deep, ecpIsOnA_deep(n, f_deep), - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA2_deep(n, ec_d, ec_deep, n / 2 + 1), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n), @@ -355,6 +358,8 @@ err_t bakeBMQVStep4(octet out[], const octet in[], const bake_cert* certa, !memIsValid(certa->data, certa->len) || certa->val == 0) return ERR_BAD_INPUT; + if (n % 2) + return ERR_NOT_IMPLEMENTED; // разметить стек memSlice(s->stack, bakeBMQVStep4_local(n, no), SIZE_0, SIZE_MAX, @@ -384,13 +389,13 @@ err_t bakeBMQVStep4(octet out[], const octet in[], const bake_cert* certa, zzSubMod(sb, s->u, sb, s->ec->order, n); // K <- sb(Va - (2^l + t)Qa), K == O => K <- G t[n / 2] = 1; - if (!ecMulA(Qa, Qa, s->ec, t, n / 2 + 1, stack)) + if (!bignMulA2(Qa, Qa, s->ec, t, n / 2 + 1, stack)) return ERR_BAD_PARAMS; if (!ecpSubAA(Va, Va, Qa, s->ec, stack)) qrTo(K, s->ec->base, s->ec->f, stack); else { - if (!ecMulA(Va, Va, s->ec, sb, n, stack)) + if (!bignMulA(Va, Va, s->ec, sb, stack)) return ERR_BAD_PARAMS; qrTo(K, ecX(Va), s->ec->f, stack); } @@ -440,10 +445,11 @@ static size_t bakeBMQVStep4_deep(size_t n, size_t f_deep, size_t ec_d, { return memSliceSize( bakeBMQVStep4_local(n, O_OF_W(n)), - utilMax(9, + utilMax(10, f_deep, ecpIsOnA_deep(n, f_deep), - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA2_deep(n, ec_d, ec_deep, n / 2 + 1), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n), diff --git a/src/crypto/bake/bake_bpace.c b/src/crypto/bake/bake_bpace.c index ad24992d..ef7f891c 100644 --- a/src/crypto/bake/bake_bpace.c +++ b/src/crypto/bake/bake_bpace.c @@ -4,7 +4,7 @@ \brief STB 34.101.66 (bake): the BPACE protocol \project bee2 [cryptographic library] \created 2014.04.14 -\version 2025.10.22 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -254,7 +254,7 @@ err_t bakeBPACEStep3(octet out[], const octet in[], void* state) s->settings->rng_state)) return ERR_BAD_RNG; // Va <- ua W - if (!ecMulA(Va, s->W, s->ec, s->u, n, stack)) + if (!bignMulA(Va, s->W, s->ec, s->u, stack)) return ERR_BAD_PARAMS; // ...|| out <- qrTo(out + no / 2, ecX(Va), s->ec->f, stack); @@ -273,7 +273,7 @@ static size_t bakeBPACEStep3_deep(size_t n, size_t f_deep, size_t ec_d, utilMax(4, beltECB_keep(), bakeSWUEc_deep(n, f_deep), - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA_deep(n, ec_d, ec_deep), f_deep), SIZE_MAX); } @@ -324,11 +324,11 @@ err_t bakeBPACEStep4(octet out[], const octet in[], void* state) s->settings->rng_state)) return ERR_BAD_RNG; // K <- ub Va - if (!ecMulA(K, Va, s->ec, s->u, n, stack)) + if (!bignMulA(K, Va, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo((octet*)K, ecX(K), s->ec->f, stack); // Vb <- ub W - if (!ecMulA(Vb, s->W, s->ec, s->u, n, stack)) + if (!bignMulA(Vb, s->W, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo((octet*)ecX(Vb), ecX(Vb), s->ec->f, stack); qrTo((octet*)ecY(Vb, n), ecY(Vb, n), s->ec->f, stack); @@ -376,7 +376,7 @@ static size_t bakeBPACEStep4_deep(size_t n, size_t f_deep, size_t ec_d, f_deep, beltECB_keep(), bakeSWUEc_deep(n, f_deep), - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), beltKRP_keep(), beltMAC_keep()), @@ -417,7 +417,7 @@ err_t bakeBPACEStep5(octet out[], const octet in[], void* state) !ecpIsOnA(Vb, s->ec, stack)) return ERR_BAD_POINT; // K <- ua Vb - if (!ecMulA(K, Vb, s->ec, s->u, n, stack)) + if (!bignMulA(K, Vb, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo((octet*)K, ecX(K), s->ec->f, stack); qrTo((octet*)Vb, ecX(Vb), s->ec->f, stack); @@ -470,7 +470,7 @@ static size_t bakeBPACEStep5_deep(size_t n, size_t f_deep, size_t ec_d, bakeBPACEStep5_local(n), utilMax(5, f_deep, - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), beltKRP_keep(), beltMAC_keep()), diff --git a/src/crypto/bake/bake_bsts.c b/src/crypto/bake/bake_bsts.c index 3d154863..0a71531a 100644 --- a/src/crypto/bake/bake_bsts.c +++ b/src/crypto/bake/bake_bsts.c @@ -4,7 +4,7 @@ \brief STB 34.101.66 (bake): the BSTS protocol \project bee2 [cryptographic library] \created 2014.04.14 -\version 2026.01.08 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -154,7 +154,7 @@ err_t bakeBSTSStep2(octet out[], void* state) s->settings->rng_state)) return ERR_BAD_RNG; // Vb <- ub G - if (!ecMulA(s->Vb, s->ec->base, s->ec, s->u, n, stack)) + if (!bignMulBase(s->Vb, s->ec, s->u, stack)) return ERR_BAD_PARAMS; // out <- qrTo(out, ecX(s->Vb), s->ec->f, stack); @@ -168,7 +168,7 @@ static size_t bakeBSTSStep2_deep(size_t n, size_t f_deep, size_t ec_d, { return utilMax(2, f_deep, - ecMulA_deep(n, ec_d, ec_deep, n)); + bignMulBase_deep(n, ec_d, ec_deep)); } #define bakeBSTSStep3_local(n, no)\ @@ -197,6 +197,8 @@ err_t bakeBSTSStep3(octet out[], const octet in[], void* state) if (!memIsValid(in, 2 * no) || !memIsValid(out, 3 * no + s->cert->len + 8)) return ERR_BAD_INPUT; + if (n % 2) + return ERR_NOT_IMPLEMENTED; // разметить стек memSlice(s->stack, bakeBSTSStep3_local(n, no), SIZE_0, SIZE_MAX, @@ -211,7 +213,7 @@ err_t bakeBSTSStep3(octet out[], const octet in[], void* state) s->settings->rng_state)) return ERR_BAD_RNG; // Va <- ua G - if (!ecMulA(Va, s->ec->base, s->ec, s->u, n, stack)) + if (!bignMulBase(Va, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo((octet*)Va, ecX(Va), s->ec->f, stack); qrTo((octet*)Va + no, ecY(Va, n), s->ec->f, stack); @@ -232,7 +234,7 @@ err_t bakeBSTSStep3(octet out[], const octet in[], void* state) wwTo(out + 2 * no, no, sa); memCopy(out + 3 * no, s->cert->data, s->cert->len); // K <- beltHash(_2l || helloa || hellob) - if (!ecMulA(Va, s->Vb, s->ec, s->u, n, stack)) + if (!bignMulA(Va, s->Vb, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo(K, ecX(Va), s->ec->f, stack); beltHashStart(stack); @@ -274,10 +276,11 @@ static size_t bakeBSTSStep3_deep(size_t n, size_t f_deep, size_t ec_d, { return memSliceSize( bakeBSTSStep3_local(n, O_OF_W(n)), - utilMax(9, + utilMax(10, f_deep, ecpIsOnA_deep(n, f_deep), - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulBase_deep(n, ec_d, ec_deep), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n), @@ -321,6 +324,8 @@ err_t bakeBSTSStep4(octet out[], const octet in[], size_t in_len, vala == 0 || !memIsValid(out, no + s->cert->len + 8)) return ERR_BAD_INPUT; + if (n % 2) + return ERR_NOT_IMPLEMENTED; // разметить стек memSlice(s->stack, bakeBSTSStep4_local(n, no), SIZE_0, SIZE_MAX, @@ -331,7 +336,7 @@ err_t bakeBSTSStep4(octet out[], const octet in[], size_t in_len, !ecpIsOnA(Va, s->ec, stack)) return ERR_BAD_POINT; // K <- beltHash(_2l || helloa || hellob) - if (!ecMulA(Qa, Va, s->ec, s->u, n, stack)) + if (!bignMulA(Qa, Va, s->ec, s->u, stack)) return ERR_BAD_PARAMS; qrTo(K, ecX(Qa), s->ec->f, stack); beltHashStart(stack); @@ -426,7 +431,7 @@ static size_t bakeBSTSStep4_deep(size_t n, size_t f_deep, size_t ec_d, utilMax(10, f_deep, ecpIsOnA_deep(n, f_deep), - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), zzMul_deep(n / 2, n), zzMod_deep(n + n / 2 + 1, n), @@ -460,6 +465,8 @@ err_t bakeBSTSStep5(const octet in[], size_t in_len, bake_certval_i valb, !memIsValid(in, in_len) || valb == 0) return ERR_BAD_INPUT; + if (n % 2) + return ERR_NOT_IMPLEMENTED; // разметить стек memSlice(s->stack, bakeBSTSStep5_local(n), SIZE_0, SIZE_MAX, diff --git a/src/crypto/bign/bign_lcl.h b/src/crypto/bign/bign_lcl.h index 0a50e1b8..a385a8fc 100644 --- a/src/crypto/bign/bign_lcl.h +++ b/src/crypto/bign/bign_lcl.h @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): local declarations \project bee2 [cryptographic library] \created 2014.04.03 -\version 2026.02.13 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -116,15 +116,15 @@ void bignEcClose( ******************************************************************************* */ -/*! \brief Кратная точка +/*! \brief Кратная точка на кривой Bign - Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, - которая является [ec->f->n]d-кратной аффинной точки [2 * ec->f->n]a: + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая + является [ec->f->n]d-кратной аффинной точки [2 * ec->f->n]a: \code b <- d a. \endcode \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. + \pre d < ec->order. \expect Кривая ec удовлетворяет соглашениям Bign. \expect В ec используются якобиевы координаты. \expect Точка a лежит на ec. @@ -142,6 +142,38 @@ bool_t bignMulA( size_t bignMulA_deep(size_t n, size_t ec_d, size_t ec_deep); +/*! \brief Кратная точка на кривой Bign для скаляра специального вида + + Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, + которая является [m]d-кратной аффинной точки [2 * ec->f->n]a: + \code + b <- d a. + \endcode + Скаляр d имеет специальный вид: + - 0 < wwBitSize(d, m); + - wwBitSize(d, m) < wwBitSize(ec->order, ec->f->n + 1); + - wwBitSize(d, m) - 1 делится на 8. + . + \pre Описание ec работоспособно. + \pre Соблюдаются ограничения на d. + \expect Кривая ec удовлетворяет соглашениям Bign. + \expect В ec используются якобиевы координаты. + \expect Точка a лежит на ec. + \return TRUE, если кратная точка отличается от O, и FALSE в противном + случае. + \deep{stack} bignMulA2_deep(ec->f->n, ec->d, ec->deep). +*/ +bool_t bignMulA2( + word b[], /*!< [out] кратная точка */ + const word a[], /*!< [in] базовая точка */ + const ec_o* ec, /*!< [in] описание кривой */ + const word d[], /*!< [in] кратность */ + size_t m, /*!< [in] длина d в машинных словах */ + void* stack /*!< [in] вспомогательная память */ +); + +size_t bignMulA2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); + /*! \brief Кратная базовая точка Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index 72a7c8e9..bbc6d6fc 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.02.23 +\version 2026.03.03 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -44,6 +44,7 @@ bool_t bignMulA(word b[], const word a[], const ec_o* ec, const word d[], ec_pre_t* pre; /* [pre_count проективных точек] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(wwCmp(d, ec->order, ec->f->n) < 0); // размерности w = bignMulAWidth(B_OF_W(ec->f->n)); pre_count = SIZE_BIT_POS(w - 1); @@ -68,6 +69,49 @@ size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) SIZE_MAX); } +static size_t bignMulA2Width(size_t l) +{ + return l <= 128 ? 4 : 8; +} + +#define bignMulA2_local(n, pre_count)\ +/* pre */ sizeof(ec_pre_t) + O_OF_W(pre_count * 3 * n) + +bool_t bignMulA2(word b[], const word a[], const ec_o* ec, const word d[], + size_t m, void* stack) +{ + size_t w; + size_t pre_count; + ec_pre_t* pre; /* [pre_count проективных точек] */ + // pre + ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(0 < wwBitSize(d, m)); + ASSERT(wwBitSize(d, m) < wwBitSize(ec->order, ec->f->n + 1)); + ASSERT((wwBitSize(d, m) - 1) % 8 == 0); + // размерности + w = bignMulA2Width(B_OF_W(ec->f->n)); + pre_count = SIZE_BIT_POS(w - 1) + 3; + // разметить стек + memSlice(stack, + bignMulA2_local(ec->f->n, pre_count), SIZE_0, SIZE_MAX, + &pre, &stack); + // метод SH + ecpPreSHJ(pre, a, w, ec, stack); + return ecMulPreSH(b, pre, ec, d, m, stack); +} + +size_t bignMulA2_deep(size_t n, size_t f_deep, size_t ec_deep, size_t m) +{ + const size_t w = bignMulA2Width(B_OF_W(m)); + const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; + return memSliceSize( + bignMulA2_local(n, pre_count), + utilMax(2, + ecpPreSHJ_deep(n, f_deep, ec_deep), + ecMulPreSH_deep(n, 3, ec_deep, m)), + SIZE_MAX); +} + bool_t bignMulBase(word a[], const ec_o* ec, const word d[], void* stack) { ASSERT(ecIsOperable(ec) && ec->d == 3); diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index c4bfc8fc..da96fd89 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -292,6 +292,7 @@ void ecpPreSOJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* t; /* [3 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnA(a, ec)); ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 3 * ec->f->n), @@ -361,6 +362,7 @@ bool_t ecpPreSOA(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* zs; /* [(SIZE_BIT_POS(w - 1) - 1) * n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnA(a, ec)); ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2(pre, sizeof(ec_pre_t) + O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n), @@ -450,6 +452,7 @@ void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, word* t; /* [3 * ec->f->n] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(ecpSeemsOnA(a, ec)); ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); ASSERT(memIsDisjoint2( pre, sizeof(ec_pre_t) + From 8d0983a04607610e20db4b23de2ea3506db9a926 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 3 Mar 2026 21:32:30 +0300 Subject: [PATCH 52/77] Simplify bench/ecp --- test/math/ecp_bench.c | 49 ++++++++----------------------------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 9c095f91..66f0769f 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -23,9 +23,12 @@ ******************************************************************************* Оценка производительности на заданной кривой -\expect Функции ecMulPreSO(), ecMulPreSOA(), ecMulPreOD(), ecMulPreSI() и -ecMulPreSH() регулярны на кривой ec. Поэтому скаляр d не рандомизируется, -а вместо среднего времени замеряется минимальное. +\expect Функции ecMulPreXX() регулярны на кривой ec. Поэтому скаляр d +не рандомизируется, а вместо среднего времени замеряется минимальное. + +\remark Битовая длина скаляра функции ecMulPreSH() примерно в два раза меньше +длин скаляров других функций. Такая логика соответствует применению +ecMulPreSH() в реализации протоколов Bake. ******************************************************************************* */ @@ -125,40 +128,6 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)ticks, (unsigned)tmSpeed(1, ticks)); } - // ecMulPre[SO] - for (w = min_w; w <= max_w; ++w) - { - ecPreSO(pre, ec->base, w, ec, stack); - wwSetW(d, m, 1); - for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) - { - tm_ticks_t t = tmTicks(); - ecMulPreSO(pt, pre, ec, d, m, stack); - if ((t = tmTicks() - t) < ticks) - ticks = t; - } - printf(" ecMulPre[SO,w=%u]: %u cycles/pt [%u pts/sec]\n", - (unsigned)w, - (unsigned)ticks, - (unsigned)tmSpeed(1, ticks)); - } - // ecMulPre[SOA] - for (w = min_w; w <= max_w; ++w) - { - ecPreSOA(pre, ec->base, w, ec, stack); - wwSetW(d, m, 1); - for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) - { - tm_ticks_t t = tmTicks(); - ecMulPreSOA(pt, pre, ec, d, m, stack); - if ((t = tmTicks() - t) < ticks) - ticks = t; - } - printf(" ecMulPre[SOA,w=%u]: %u cycles/pt [%u pts/sec]\n", - (unsigned)w, - (unsigned)ticks, - (unsigned)tmSpeed(1, ticks)); - } // ecMulPre[OD] for (w = min_w; w <= max_w; ++w) { @@ -195,23 +164,23 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)ticks, (unsigned)tmSpeed(1, ticks)); } - // ecMulPre[SH] + // ecPre+MulPre[SH] for (w = min_w; w <= max_w; ++w) { const size_t mb = (B_OF_O(mo) / 2 / w) * w; if (mb < w) continue; - ecPreSH(pre, ec->base, w, ec, stack); wwSetW(d, m, 1); wwSetBit(d, mb, TRUE); for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { tm_ticks_t t = tmTicks(); + ecpPreSHJ(pre, ec->base, w, ec, stack); ecMulPreSH(pt, pre, ec, d, m, stack); if ((t = tmTicks() - t) < ticks) ticks = t; } - printf(" ecMulPre[SH,w=%u]: %u cycles/pt [%u pts/sec]\n", + printf(" ecpPre+ecMulPre[SH,w=%u]: %u cycles/pt [%u pts/sec]\n", (unsigned)w, (unsigned)ticks, (unsigned)tmSpeed(1, ticks)); From 3a47fb358d2ed03e9300903d66a814c3ab57fa71 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Thu, 5 Mar 2026 14:36:29 +0300 Subject: [PATCH 53/77] Replace ecMulSH() with ecMulSO2() in math/ec --- include/bee2/math/ec.h | 107 ++++++-------- include/bee2/math/ecp.h | 27 +--- src/crypto/bign/bign_misc.c | 23 +-- src/math/ec.c | 277 +++++++++++++----------------------- src/math/ecp/ecp_pre.c | 79 +--------- test/math/ec_test.c | 91 ++++-------- test/math/ecp_bench.c | 58 ++++---- test/math/ecp_test.c | 31 +--- 8 files changed, 216 insertions(+), 477 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 81c533d5..3a948e40 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.03.02 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -485,7 +485,6 @@ typedef enum { ec_pre_so = 1, /*!< схема SO */ ec_pre_soa, /*!< схема SOA */ - ec_pre_sh, /*!< схема SH */ ec_pre_od, /*!< схема OD */ ec_pre_si, /*!< схема SI */ } ec_pre_type; @@ -504,10 +503,6 @@ typedef enum \code a, 3a, ..., (2^w-1)a; \endcode - - SH (Small Hi): вычисляется 2^{w-1}+2 проективных точек - \code - (2^{w-1})a, (2^{w-1}+1)a, ..., (2^w-1)a, (2^w)a, 2a; - \endcode - OD (Odd Doubled): вычисляется h*2^{w-1} аффинных точек \code 2^{0} (a, 3a, ..., (2^w-1)a), @@ -817,31 +812,6 @@ bool_t ecPreSOA( size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep); -/*! \brief Предвычисления по схеме SH - - На эллиптической кривой ec выполняются предвычисления по схеме SH - с аффинной точкой [2 * ec->f->n]a и окном шириной w. Результат сохраняется - в контейнере pre. - \pre Описание ec работоспособно. - \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). - \pre memIsValid(pre, sizeof(ec_pre_t) + - O_OF_W(SIZE_BIT_POS(w - 1) * 2 * ec->f->n)). - \expect Описание ec корректно. - \expect Точка a лежит на ec. - \return TRUE, если среди предвычисленных точек нет O, и FALSE в противном - случае. - \deep{stack} ecPreSH_deep(ec->deep). -*/ -void ecPreSH( - ec_pre_t* pre, /*!< [out] предвычисленные точки */ - const word a[], /*!< [in] исходная точка */ - size_t w, /*!< [in] ширина окна */ - const ec_o* ec, /*!< [in] описание кривой */ - void* stack /*!< [in] вспомогательная память */ -); - -size_t ecPreSH_deep(size_t ec_deep); - /*! \brief Предвычисления по схеме OD На эллиптической кривой ec выполняются предвычисления по схеме OD @@ -930,7 +900,7 @@ bool_t ecMulA( size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SO +/*! \brief Кратная точка по методу SO Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre @@ -944,6 +914,7 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre pre->type == ec_pre_so. \pre wwWordSize(ec->order, ec->f->n + 1) == m. \pre wwBitSize(ec->order, m) > pre->w. + \pre zzIsOdd(ec->order, m). \pre d < ec->order. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. @@ -973,43 +944,37 @@ bool_t ecMulPreSO( size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SOA +/*! \brief Кратная точка по методу SO2 Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SOA точками: + с предвычисленными по схеме SO точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_soa. - \pre wwWordSize(ec->order, ec->f->n + 1) == m. - \pre wwBitSize(ec->order, m) > pre->w. - \pre d < ec->order. + \pre pre->type == ec_pre_so. + \pre wwBitSize(d, m) > pre->w. + \pre 2 <= d. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreSOA_deep(ec->f->n, ec->d, ec->deep, m). - \safe Функция регулярна по [m]d при выполнении следующих условий: + \deep{stack} ecMulPreSO2_deep(ec->f->n, ec->d, ec->deep, m). + \safe Функция регулярна по [m]d при фиксированной битовой длине d, если + выполнены следующие условия: - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без условных переходов; - - функция ec->adda регулярна: сложение a + b, a != \pm b, a != O, b != O, + - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, выполняется без условных переходов; - - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление - 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных - переходов; - - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается + - функция ec->toa регулярна: преобразование в аффинную точку выполняется без условных переходов; - - справедливо, по крайней мере, одно из условий: - a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и - удвоение выполняются по одним и тем же формулам без условных - переходов. + - zzIsOdd(ec->order, m); + - 2^{wwBitSize(d, m) + 1} < (ec->order + 3)/2 - 2^{pre->w}. */ -bool_t ecMulPreSOA( +bool_t ecMulPreSO2( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -1018,34 +983,46 @@ bool_t ecMulPreSOA( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSO2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SH +/*! \brief Кратная точка по методу SOA Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre - с предвычисленными по схеме SH точками: + с предвычисленными по схеме SOA точками: \code b <- d a. \endcode \pre Описание ec работоспособно. \pre Описание группы точек в ec работоспособно. \pre Контейнер pre работоспособен. - \pre pre->type == ec_pre_sh. - \pre 0 < wwBitSize(d, m) && wwBitSize(d, m) - 1 делится на pre->w. + \pre pre->type == ec_pre_soa. + \pre wwWordSize(ec->order, ec->f->n + 1) == m. + \pre wwBitSize(ec->order, m) > pre->w. + \pre zzIsOdd(ec->order, m). + \pre d < ec->order. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. \return TRUE, если кратная точка отличается от O, и FALSE в противном случае. - \deep{stack} ecMulPreSH_deep(ec->f->n, ec->d, ec->deep, m). + \deep{stack} ecMulPreSOA_deep(ec->f->n, ec->d, ec->deep, m). \safe Функция регулярна по [m]d при выполнении следующих условий: - - wwBitSize(d, m) < wwBitSize(ec->order, ec->f->n + 1); - функция ec->dbl регулярна: удвоение 2 a, a != O, выполняется без условных переходов; - - функция ec->add регулярна: сложение a + b, a != \pm b, a != O, b != O, - выполняется без условных переходов. + - функция ec->adda регулярна: сложение a + b, a != \pm b, a != O, b != O, + выполняется без условных переходов; + - если ec->dbladda != 0, то функция ec->dbladda регулярна: вычисление + 2a + b, 2a != \pm b, a != O, b != O, выполняется без условных + переходов; + - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается + без условных переходов; + - справедливо, по крайней мере, одно из условий: + a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; + b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и + удвоение выполняются по одним и тем же формулам без условных + переходов. */ -bool_t ecMulPreSH( +bool_t ecMulPreSOA( word b[], /*!< [out] кратная точка */ const ec_pre_t* pre, /*!< [in] предвычисленные точки */ const ec_o* ec, /*!< [in] описание кривой */ @@ -1054,9 +1031,9 @@ bool_t ecMulPreSH( void* stack /*!< [in] вспомогательная память */ ); -size_t ecMulPreSH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); +size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме OD +/*! \brief Кратная точка по методу OD Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre @@ -1070,6 +1047,7 @@ size_t ecMulPreSH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre pre->type == ec_pre_od. \pre wwWordSize(ec->order, ec->f->n + 1) == m. \pre wwBitSize(ec->order, m) > pre->w. + \pre zzIsOdd(ec->order, m). \pre d < ec->order. \pre pre->w * pre->h >= wwBitSize(ec->order, m). \expect Описание ec корректно. @@ -1102,7 +1080,7 @@ bool_t ecMulPreOD( size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); -/*! \brief Кратная точка с предвычислениями по схеме SI +/*! \brief Кратная точка по методу SI Определяется аффинная точка [2 * ec->f->n]b эллиптической кривой ec, которая является [m]d-кратной точки a, по которой построен контейнер pre @@ -1117,6 +1095,7 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); \pre wwWordSize(ec->order, ec->f->n + 1) == m. \pre pre->w < wwBitSize(ec->order, m). \pre wwBitSize(ec->order, m) <= pre->w * pre->h. + \pre zzIsOdd(ec->order, m). \pre d < ec->order. \expect Описание ec корректно. \expect Точки контейнера pre корректно рассчитаны по a. diff --git a/include/bee2/math/ecp.h b/include/bee2/math/ecp.h index 8fae1edb..1981444e 100644 --- a/include/bee2/math/ecp.h +++ b/include/bee2/math/ecp.h @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields \project bee2 [cryptographic library] \created 2012.06.24 -\version 2026.02.24 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -322,31 +322,6 @@ bool_t ecpPreSOA( size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w); -/*! \brief Предвычисления по схеме SH - - На эллиптической кривой ec выполняются предвычисления по схеме SH - с аффинной точкой [2 * ec->f->n]a и окном шириной w. Используются - якобиевы координаты. Результат сохраняется в контейнере pre. - \pre Описание ec работоспособно. - \pre Координаты a лежат в базовом поле. - \pre 0 < w && w < MIN2(B_PER_W, B_PER_S). - \pre memIsValid(pre, sizeof(ec_pre_t) + - O_OF_W((SIZE_BIT_POS(w - 1) + 1) * 3 * ec->f->n)). - \expect Описание ec корректно. - \expect В ec установлены якобиевы координаты. - \expect Точка a лежит на ec. - \deep{stack} ecPreSHJ_deep(ec->f->n, ec->d, ec->deep). -*/ -void ecpPreSHJ( - ec_pre_t* pre, /*!< [out] предвычисленные точки */ - const word a[], /*!< [in] исходная точка */ - size_t w, /*!< [in] ширина окна */ - const struct ec_o* ec, /*!< [in] описание эллиптической кривой */ - void* stack /*!< [in] вспомогательная память */ -); - -size_t ecpPreSHJ_deep(size_t n, size_t f_deep, size_t ec_deep); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/crypto/bign/bign_misc.c b/src/crypto/bign/bign_misc.c index bbc6d6fc..312960de 100644 --- a/src/crypto/bign/bign_misc.c +++ b/src/crypto/bign/bign_misc.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): miscellaneous (OIDs, keys, DH) \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.03.03 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -44,6 +44,7 @@ bool_t bignMulA(word b[], const word a[], const ec_o* ec, const word d[], ec_pre_t* pre; /* [pre_count проективных точек] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == ec->f->n); ASSERT(wwCmp(d, ec->order, ec->f->n) < 0); // размерности w = bignMulAWidth(B_OF_W(ec->f->n)); @@ -71,7 +72,7 @@ size_t bignMulA_deep(size_t n, size_t f_deep, size_t ec_deep) static size_t bignMulA2Width(size_t l) { - return l <= 128 ? 4 : 8; + return l < 128 ? 5 : 6; } #define bignMulA2_local(n, pre_count)\ @@ -85,19 +86,18 @@ bool_t bignMulA2(word b[], const word a[], const ec_o* ec, const word d[], ec_pre_t* pre; /* [pre_count проективных точек] */ // pre ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(0 < wwBitSize(d, m)); - ASSERT(wwBitSize(d, m) < wwBitSize(ec->order, ec->f->n + 1)); - ASSERT((wwBitSize(d, m) - 1) % 8 == 0); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == ec->f->n); + ASSERT(0 <= wwCmpW(d, m, 2) && wwCmp2(d, m, ec->order, ec->f->n) < 0); // размерности - w = bignMulA2Width(B_OF_W(ec->f->n)); + w = bignMulA2Width(B_OF_W(m)); pre_count = SIZE_BIT_POS(w - 1) + 3; // разметить стек memSlice(stack, bignMulA2_local(ec->f->n, pre_count), SIZE_0, SIZE_MAX, &pre, &stack); - // метод SH - ecpPreSHJ(pre, a, w, ec, stack); - return ecMulPreSH(b, pre, ec, d, m, stack); + // метод SO2 + ecpPreSOJ(pre, a, w, ec, stack); + return ecMulPreSO2(b, pre, ec, d, m, stack); } size_t bignMulA2_deep(size_t n, size_t f_deep, size_t ec_deep, size_t m) @@ -107,14 +107,15 @@ size_t bignMulA2_deep(size_t n, size_t f_deep, size_t ec_deep, size_t m) return memSliceSize( bignMulA2_local(n, pre_count), utilMax(2, - ecpPreSHJ_deep(n, f_deep, ec_deep), - ecMulPreSH_deep(n, 3, ec_deep, m)), + ecpPreSOJ_deep(n, f_deep), + ecMulPreSO2_deep(n, 3, ec_deep, m)), SIZE_MAX); } bool_t bignMulBase(word a[], const ec_o* ec, const word d[], void* stack) { ASSERT(ecIsOperable(ec) && ec->d == 3); + ASSERT(wwWordSize(ec->order, ec->f->n + 1) == ec->f->n); if (ec->pre) { ASSERT(ecPreIsOperable(ec->pre)); diff --git a/src/math/ec.c b/src/math/ec.c index 3dfad6c3..25469b29 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.03.03 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -153,8 +153,7 @@ bool_t ecPreIsOperable(const ec_pre_t* pre) { return memIsValid(pre, sizeof(pre)) && (pre->type == ec_pre_so || pre->type == ec_pre_soa || - pre->type == ec_pre_sh || pre->type == ec_pre_od || - pre->type == ec_pre_si) && + pre->type == ec_pre_od || pre->type == ec_pre_si) && 0 < pre->w && pre->w < MIN2(B_PER_W, B_PER_S) && ((pre->type != ec_pre_od && pre->type != ec_pre_si) ^ (pre->h > 0)); } @@ -254,50 +253,6 @@ size_t ecPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep) SIZE_MAX); } -void ecPreSH(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, - void* stack) -{ - const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); - ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + O_OF_W(pre_count * ec->d * ec->f->n), - a, O_OF_W(2 * ec->f->n))); - // pre[-2, -1] <- (a, 2a) - ecFromA(ecPrePt(pre, pre_count - 2, ec), a, ec, stack); - ecDblA(ecPrePt(pre, pre_count - 1, ec), a, ec, stack); - // вычислить малые кратные - if (w > 1) - { - size_t i; - // pre[0] <- 2^{w-1}a - wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 1, ec), - ec->d * ec->f->n); - for (i = 2; i < w; ++i) - ecDbl(ecPrePt(pre, 0, ec), ecPrePt(pre, 0, ec), ec, stack); - // pre[i] <- pre[i - 1] + a - for (i = 1; i < pre_count - 3; ++i) - ecAddA(ecPrePt(pre, i, ec), ecPrePt(pre, i - 1, ec), a, ec, stack); - // pre[-3] <- 2 pre[0] - ecDbl(ecPrePt(pre, pre_count - 3, ec), ecPrePt(pre, 0, ec), ec, stack); - } - else - // pre[0, 1] <- pre[-1, -2] - wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 2, ec), - 2 * ec->d * ec->f->n); - // заполнить служебные поля - pre->type = ec_pre_sh; - pre->w = w, pre->h = 0; -} - -size_t ecPreSH_deep(size_t ec_deep) -{ - return memSliceSize( - ec_deep, - SIZE_MAX); -} - #define ecPreOD_local(n, ec_d)\ /* t */ O_OF_W(ec_d * n) @@ -631,6 +586,10 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) четность можно использовать для пересчета borrow: borrow <- e_i % 2. +В функции ecMulSO2() скаляр d делается нечетным не через замену на order-d, +а через добавление 1. Соответственно по окончании вычислений из +точки-результата вычитается базовая точка a. + В функции ecMulPreSOA() предварительно рассчитанные точки являются аффинными, а не проективными. Это позволяет заменить регулярные сложения P <- P + P на P <- P + A и финишное сложение A <- P + P на A <- P + A. @@ -705,7 +664,7 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, // обработать старшую цифру pos = (mb - 1) / pre->w, pos *= pre->w; digit = wwGetBits(e, pos, mb - pos); - wwSel(t, pre->pts, pre_count, pt_size, digit >> 1); + wwSel(t, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); borrow = WORD_1 - (digit & 1); // t <- 2^w t for (i = 0; i < pre->w; ++i) @@ -716,7 +675,7 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, // сложить digit = wwGetBits(e, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; - wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); ecSgn(pt, borrow, ec, stack); ecAdd(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); @@ -728,7 +687,7 @@ bool_t ecMulPreSO(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(e, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; - wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); ecSgn(pt, borrow, ec, stack); ret = (ec->finadd) ? ec->finadd(b, t, pt, ec, stack) : @@ -748,6 +707,88 @@ size_t ecMulPreSO_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } +#define ecMulPreSO2_local(n, ec_d, m)\ +/* e */ O_OF_W(m),\ +/* t */ O_OF_W(ec_d * n),\ +/* pts */ O_OF_W(2 * ec_d * n) + +bool_t ecMulPreSO2(word b[], const ec_pre_t* pre, const ec_o* ec, + const word d[], size_t m, void* stack) +{ + register word even; + register word digit; + register word borrow; + size_t mb; + size_t pre_count; + size_t pt_size; + size_t mask; + size_t pos; + size_t i; + word* e; /* [m] */ + word* t; /* [ec->d * ec->f->n] */ + word* pts; /* [2 * ec->d * ec->f->n] */ + word* pt; + // pre + ASSERT(ecIsOperable(ec)); + ASSERT(ecGroupIsOperable(ec)); + ASSERT(ecPreIsOperable(pre)); + ASSERT(pre->type == ec_pre_so); + ASSERT(0 <= wwCmpW(d, m, 2) && wwCmp2(d, m, ec->order, ec->f->n + 1) < 0); + // размерности + mb = wwBitSize(d, m); + ASSERT(mb > pre->w); + pre_count = SIZE_BIT_POS(pre->w - 1); + pt_size = ec->d * ec->f->n; + mask = WORD_BIT_POS(pre->w) - 2; + // разметить стек + memSlice(stack, + ecMulPreSO2_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, + &e, &t, &pts, &stack); + pt = pts; + // e <- (d % 2) ? d : d + 1 + wwCopy(e, d, m); + even = WORD_1 - (e[0] & 1); + e[0] |= 1; + ASSERT(zzIsOdd(e, m)); + // обработать старшую цифру + pos = (mb - 1) / pre->w, pos *= pre->w; + digit = wwGetBits(e, pos, mb - pos); + wwSel(t, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); + borrow = WORD_1 - (digit & 1); + // обработать остальные цифры + while (pos) + { + pos -= pre->w; + // t <- 2^w t + for (i = 0; i < pre->w; ++i) + ecDbl(t, t, ec, stack); + // сложить + digit = wwGetBits(e, pos, pre->w); + digit ^= (WORD_0 - borrow) & mask; + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); + ecSgn(pt, borrow, ec, stack); + ecAdd(t, t, pt, ec, stack); + borrow = WORD_1 - (digit & 1); + } + // pts[0, 1] <- (t, t - a) + wwCopy(ecPt(pts, 0, ec), t, pt_size); + ecNeg(ecPt(pts, 1, ec), ecPrePt(pre, 0, ec), ec, stack); + ecAdd(ecPt(pts, 1, ec), t, ecPt(pts, 1, ec), ec, stack); + // t <- pts[even] + wwSel(t, pts, 2, pt_size, (size_t)even); + // очистка и возврат + CLEAN3(even, digit, borrow); + return ecToA(b, t, ec, stack); +} + +size_t ecMulPreSO2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) +{ + return memSliceSize( + ecMulPreSO2_local(n, ec_d, m), + ec_deep, + SIZE_MAX); +} + #define ecMulPreSOA_local(n, ec_d, m)\ /* e */ O_OF_W(m),\ /* t */ O_OF_W(ec_d * n),\ @@ -794,7 +835,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, // обработать старшую цифру pos = (mb - 1) / pre->w, pos *= pre->w; digit = wwGetBits(e, pos, mb - pos); - wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); borrow = WORD_1 - (digit & 1); // t <- 2^{w-1} pt if (pre->w > 1) @@ -809,7 +850,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, // удвоить со сложением digit = wwGetBits(e, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; - wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); ecSgnA(pt, borrow, ec, stack); ecDblAddA(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); @@ -823,7 +864,7 @@ bool_t ecMulPreSOA(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(e, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; - wwSel(pt, pre->pts, pre_count, pt_size, digit >> 1); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)(digit >> 1)); ecSgnA(pt, borrow, ec, stack); ret = (ec->finadda) ? ec->finadda(b, t, pt, ec, stack) : @@ -892,7 +933,7 @@ bool_t ecMulPreOD(word b[], const ec_pre_t* pre, const ec_o* ec, pos *= pre->w; // обработать старшую цифру digit = wwGetBits(e, pos, mb - pos); - wwSel(pt, row, row_count, pt_size, digit >> 1); + wwSel(pt, row, row_count, pt_size, (size_t)(digit >> 1)); ecFromA(t, pt, ec, stack); borrow = WORD_1 - (digit & 1); // обработать остальные цифры @@ -901,7 +942,7 @@ bool_t ecMulPreOD(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(e, pos, pre->w); digit ^= (WORD_0 - borrow) & mask; row -= row_count * pt_size; - wwSel(pt, row, row_count, pt_size, digit >> 1); + wwSel(pt, row, row_count, pt_size, (size_t)(digit >> 1)); ecSgnA(pt, borrow, ec, stack); ecAddA(t, t, pt, ec, stack); borrow = WORD_1 - (digit & 1); @@ -910,7 +951,7 @@ bool_t ecMulPreOD(word b[], const ec_pre_t* pre, const ec_o* ec, digit = wwGetBits(e, 0, pre->w); ASSERT(digit & 1); digit ^= (WORD_0 - borrow) & mask; - wwSel(pt, pre->pts, row_count, pt_size, digit >> 1); + wwSel(pt, pre->pts, row_count, pt_size, (size_t)(digit >> 1)); ecSgnA(pt, borrow, ec, stack); ret = (ec->finadda) ? ec->finadda(b, t, pt, ec, stack) : @@ -1044,7 +1085,7 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, digit <<= 1, digit ^= (word)wwTestBit(e, mb - i * pre->h - 1); ASSERT((digit & mask) == digit); // обработать старшую цифру - wwSel(pt, pre->pts, pre_count, pt_size, digit); + wwSel(pt, pre->pts, pre_count, pt_size,( size_t)digit); ecFromA(t, pt, ec, stack); // обработать остальные цифры for (pos = 2; pos < pre->h; ++pos) @@ -1055,7 +1096,7 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, digit <<= 1, digit ^= (word)wwTestBit(e, mb - i * pre->h - pos); digit ^= (WORD_0 - hi) & mask; // удвоить и сложить - wwSel(pt, pre->pts, pre_count, pt_size, digit); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)digit); ecSgnA(pt, hi, ec, stack); ecDblAddA(t, t, pt, ec, stack); } @@ -1067,7 +1108,7 @@ bool_t ecMulPreSI(word b[], const ec_pre_t* pre, const ec_o* ec, digit <<= 1, digit ^= (word)wwTestBit(e, mb - i * pre->h - pos); digit ^= (WORD_0 - hi) & mask; // финишное сложение - wwSel(pt, pre->pts, pre_count, pt_size, digit); + wwSel(pt, pre->pts, pre_count, pt_size, (size_t)digit); ecSgnA(pt, hi, ec, stack); ret = (ec->finadda) ? ec->finadda(b, t, pt, ec, stack) : @@ -1087,124 +1128,6 @@ size_t ecMulPreSI_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) SIZE_MAX); } -/* -******************************************************************************* -Кратная точка: метод SH - -В функции ecMulPreSH() определяется кратная точка b = (2^l + d)a. Используется -окно ширины w. Предполагается, что d < 2^l, l < bitlen(order) и w | l. Пусть -h = l/w + 1. - -Скаляр 2^l+d записывается в виде - e_{h-1} (2^w)^{h-1} + ... + e_1 (2^w) + e_0. -Здесь e_i \in {2^{w-1}, \pm (2^{w-1}+1),...,\pm 2^w}, i = 0, 1,..., h-2, -- -ненулевые цифры с установленным старшим разрядом и e_{h-1} \in {1, 2}. - -Реализован следующий алгоритм: -1. Для i = 0, 1, ..., 2^{w-1}: - 1) pre[i] <- (2^{w-1} + i)a; // предвычисления: SH - 2) pre[2^{w-1} + 1] <- a; - 3) pre[2^{w-1} + 2] <- 2a. -2. t <- pre[2^{w-1}+e_{h-1}]. -3. Для i = h-2, ..., 1, 0: - 1) t <- 2^w t; // P <- 2P - 2) t <- t + sgn(e_i) pre[|e_i|-2^{w-1}]. // P <- P + P -4. Возвратить t. - -\remark Расчет цифр e_i: -1. Записать - 2^l + d = d_{h-1} (2^w)^{h-1} + d_1 (2^w) + d_0, - где d_i \in {0, 1, ..., 2^w-1}, d_{h-1} = 1. -2. e_0 <- d_0. -3. Для i = 0, 1, ...., h-1: - 1) (e_i, e_{i+1}) <- e_i < 2^{w-1} ? (e_i-2^w, d_{i+1}+1) : (e_i, d_{i+1}). - -\remark Цифра e_i хранится в виде (w+1)-разрядного слова: -* старший бит -- признак отрицательности; -* младшие w+1 битов -- |e_i|-2^{w-1}. -******************************************************************************* -*/ - -#define ecMulPreSH_local(n, ec_d, m)\ -/* e */ O_OF_W(2 * m),\ -/* t */ O_OF_W(ec_d * n),\ -/* pt */ O_OF_W(ec_d * n) - -bool_t ecMulPreSH(word b[], const ec_pre_t* pre, const ec_o* ec, - const word d[], size_t m, void* stack) -{ - register word digit; - register word carry; - size_t mb; - size_t pre_count; - size_t pt_size; - word mask; - word hi; - size_t i; - word* e; /* [2 * m] */ - word* t; /* [ec->d * ec->f->n] */ - word* pt; /* [ec->d * ec->f->n] */ - // pre - ASSERT(ecIsOperable(ec)); - ASSERT(ecGroupIsOperable(ec)); - ASSERT(ecPreIsOperable(pre)); - ASSERT(pre->type == ec_pre_sh); - // размерности - mb = wwBitSize(d, m); - ASSERT(0 < mb && (mb - 1) % pre->w == 0); - pre_count = SIZE_BIT_POS(pre->w - 1) + 3; - pt_size = ec->d * ec->f->n; - mask = ~WORD_BIT_POS(pre->w); - hi = WORD_BIT_POS(pre->w - 1); - // разметить стек - memSlice(stack, - ecMulPreSH_local(ec->f->n, ec->d, m), SIZE_0, SIZE_MAX, - &e, &t, &pt, &stack); - // перекодирование - for (i = 0, carry = 0; i < (mb - 1) / pre->w; ++i) - { - // digit <- d_i + carry - digit = wwGetBits(d, i * pre->w, pre->w); - digit += carry; - // carry <- (digit < 2^{w-1}) - carry = wordLess01(digit, hi); - // digit <- carry ? digit - 2^w : digit - digit ^= SIZE_0 - carry; - digit += carry; - // \post w-й бит digit: признак отрицательности - // \post младшие w-1 битов digit: |digit - 2^w| - // сохранить digit в виде (признак, |digit - 2^w| - 2^{w-1}) - wwSetBits(e, i * (pre->w + 1), pre->w + 1, digit - hi); - } - // обработать старшую цифру - ASSERT(wwTestBit(d, i * pre->w)); - wwSel(t, ecPrePt(pre, pre_count - 2, ec), 2, pt_size, carry); - // обработать остальные цифры - while (i--) - { - size_t j; - // t <- 2^w t - for (j = 0; j < pre->w; ++j) - ecDbl(t, t, ec, stack); - // сложить - digit = wwGetBits(e, i * (pre->w + 1), pre->w + 1); - wwSel(pt, pre->pts, pre_count - 2, pt_size, digit & mask); - ecSgn(pt, digit >> pre->w, ec, stack); - ecAdd(t, t, pt, ec, stack); - } - // очистка и возврат - CLEAN2(digit, carry); - return ecToA(b, t, ec, stack); -} - -size_t ecMulPreSH_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m) -{ - return memSliceSize( - ecMulPreSH_local(n, ec_d, m), - ec_deep, - SIZE_MAX); -} - /* ******************************************************************************* Имеет порядок? diff --git a/src/math/ecp/ecp_pre.c b/src/math/ecp/ecp_pre.c index da96fd89..a8752a3e 100644 --- a/src/math/ecp/ecp_pre.c +++ b/src/math/ecp/ecp_pre.c @@ -4,7 +4,7 @@ \brief Elliptic curves over prime fields: precomputations \project bee2 [cryptographic library] \created 2021.07.18 -\version 2026.03.03 +\version 2026.03.05 \license This program is released under the GNU General Public License version 3. See Copyright Notices in bee2/info.h. ******************************************************************************* @@ -427,80 +427,3 @@ size_t ecpPreSOA_deep(size_t n, size_t f_deep, size_t w) qrInvBatch_deep(n, f_deep, SIZE_BIT_POS(w - 1) - 1)), SIZE_MAX); } - -/* -******************************************************************************* -Предвычисления: схема SH, якобиевы координаты - -Алгоритм (w > 1): -1. pre[2^{w-1}+1] <- a. // J <- A -2. pre[2^{w-1}+2] <- 2a. // J <- 2A -3. pre[0] <- 2^{w-2}pre[2^{w-1}]. // J <- 2J -4. t <- a: t -- якобиева точка, Z(t) = Z(pre[0]). -5. Для i = 1, 2, ..., 2^{w-1}: - 1) pre[i] <- t + pre[i-1]. // JJ <- J + J, с обновлением t -******************************************************************************* -*/ - -#define ecpPreSHJ_local(n)\ -/* t */ O_OF_W(3 * n) - -void ecpPreSHJ(ec_pre_t* pre, const word a[], size_t w, const ec_o* ec, - void* stack) -{ - const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; - word* t; /* [3 * ec->f->n] */ - // pre - ASSERT(ecIsOperable(ec) && ec->d == 3); - ASSERT(ecpSeemsOnA(a, ec)); - ASSERT(0 < w && w < MIN2(B_PER_W, B_PER_S)); - ASSERT(memIsDisjoint2( - pre, sizeof(ec_pre_t) + - O_OF_W(pre_count * 3 * ec->f->n), a, O_OF_W(2 * ec->f->n))); - // разметить стек - memSlice(stack, - ecpPreSHJ_local(ec->f->n), SIZE_0, SIZE_MAX, - &t, &stack); - // pre[-2, -1] <- (a, 2a) - ecFromA(ecPrePt(pre, pre_count - 2, ec), a, ec, stack); - ecDblA(ecPrePt(pre, pre_count - 1, ec), a, ec, stack); - // вычислить малые кратные - if (w > 1) - { - const size_t n = ec->f->n; - size_t i; - // pre[0] <- 2^{w-1}a - wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 1, ec), - ec->d * ec->f->n); - for (i = 2; i < w; ++i) - ecDbl(ecPrePt(pre, 0, ec), ecPrePt(pre, 0, ec), ec, stack); - // t <- a: z(t) = z(pre[0]) - wwCopy(ecZ(t, n), ecZ(ecPrePt(pre, 0, ec), n), n); - qrSqr(ecX(t), ecZ(t, n), ec->f, stack); - qrMul(ecY(t, n), ecX(t), ecZ(t, n), ec->f, stack); - qrMul(ecX(t), ecX(t), ecX(a), ec->f, stack); - qrMul(ecY(t, n), ecY(t, n), ecY(a, n), ec->f, stack); - // pre[i] <- pre[i - 1] + a - for (i = 1; i <= pre_count - 3; ++i) - ecpReaddJ(ecPrePt(pre, i, ec), t, t, ecPrePt(pre, i - 1, ec), ec, - stack); - } - else - // pre[0, 1] <- pre[-1, -2] - wwCopy(ecPrePt(pre, 0, ec), ecPrePt(pre, pre_count - 2, ec), - 2 * 3 * ec->f->n); - // заполнить служебные поля - pre->type = ec_pre_sh; - pre->w = w, pre->h = 0; -} - -size_t ecpPreSHJ_deep(size_t n, size_t f_deep, size_t ec_deep) -{ - return memSliceSize( - ecpPreSHJ_local(n), - utilMax(3, - ecpIDblAJ_deep(n, f_deep), - ecpReaddJ_deep(n, f_deep), - ec_deep), - SIZE_MAX); -} diff --git a/test/math/ec_test.c b/test/math/ec_test.c index b78209f1..2abb882d 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves \project bee2/test \created 2026.02.12 -\version 2026.03.03 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -109,7 +109,7 @@ bool_t ecTestEc(const ec_o* ec) O_OF_W(ec->d * n), O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(16, + utilMax(15, ec->deep, ecHasOrderA_deep(n, ec->d, ec->deep, n), ecAddAA_deep(n, ec->d, ec->deep), @@ -117,14 +117,13 @@ bool_t ecTestEc(const ec_o* ec) ecMulA_deep(n, ec->d, ec->deep, n), ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), - ecPreSH_deep(ec->deep), ecPreSI_deep(n, ec->d, ec->deep, max_h), ecPreOD_deep(n, ec->d, ec->deep), ecMulPreSO_deep(n, ec->d, ec->deep, n), + ecMulPreSO2_deep(n, ec->d, ec->deep, n), ecMulPreSOA_deep(n, ec->d, ec->deep, n), ecMulPreOD_deep(n, ec->d, ec->deep, n), ecMulPreSI_deep(n, ec->d, ec->deep, n), - ecMulPreSH_deep(n, ec->d, ec->deep, n), ecAddMulA_deep(n, ec->d, ec->deep, 4, (size_t)1, (size_t)2, (size_t)3, (size_t)4)), SIZE_MAX, @@ -405,44 +404,6 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } - // предвычисления по схеме SH - for (w = min_w; w <= max_w; ++w) - { - const size_t pre_count = SIZE_BIT_POS(w - 1) + 3; - // pre[0..pre_count) <- (2^{w-1}, 2^{w-1}+1, ..., 2^w-1, 2^w, 1, 2)base - ecPreSH(pre, ec->base, w, ec, stack); - // pre[-2..) == (1, 2)base? - if (!ecToA(pt0, ecPrePt(pre, pre_count - 2, ec), ec, stack) || - !wwEq(pt0, ec->base, 2 * n) || - !ecToA(pt0, ecPrePt(pre, pre_count - 1, ec), ec, stack) || - !ecAddAA(pt1, ec->base, ec->base, ec, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - // pt0 <- \sum_{i=0}^{2^{w-1}} 2^{2^{w-1}-i} pre[i] - wwCopy(pt0, ecPrePt(pre, 0, ec), ec->d * n); - for (i = 1; i <= SIZE_BIT_POS(w - 1); ++i) - { - ecDbl(pt0, pt0, ec, stack); - ecAdd(pt0, pt0, ecPrePt(pre, i, ec), ec, stack); - } - // pt0 == \sum_{i=0}^{2^{w-1}} (2^{w-1}+i)2^{2^{w-1}-i} base? - wwSetW(d, n, WORD_BIT_POS(w - 1)); - for (i = 1; i <= SIZE_BIT_POS(w - 1); ++i) - { - wwShHi(d, n, 1); - zzAddW2(d, n, WORD_BIT_POS(w - 1) + (word)i); - } - if (!ecToA(pt0, pt0, ec, stack) || - !ecMulA(pt1, ec->base, ec, d, n, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - } // предвычисления по схеме SI for (w = min_w; w <= max_w; ++w) { @@ -530,6 +491,29 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } + // кратная точка: ecMulA vs ecMulPreSO2 + for (w = min_w; w <= max_w; ++w) + { + const size_t m = wwWordSize(ec->order, n + 1); + const size_t mb = (wwBitSize(ec->order, m) / 3 / w) * w; + if (mb <= w) + continue; + wwCopy(d, ec->order, m); + wwTrimHi(d, m, mb); + wwSetBit(d, mb, TRUE); + ecPreSO(pre, ec->base, w, ec, stack); + while (0 <= wwCmpW(d, m, 2)) + { + if (!ecMulPreSO2(pt0, pre, ec, d, m, stack) || + !ecMulA(pt1, ec->base, ec, d, m, stack) || + !wwEq(pt0, pt1, 2 * n)) + { + blobClose(state); + return FALSE; + } + wwShLo(d, m, 11); + } + } // кратная точка: ecMulA vs ecMulPreSOA for (w = min_w; w <= max_w; ++w) { @@ -619,29 +603,6 @@ bool_t ecTestEc(const ec_o* ec) return FALSE; } } - // кратная точка: ecMulA vs ecMulPreSH - for (w = min_w; w <= max_w; ++w) - { - const size_t m = wwWordSize(ec->order, n + 1); - const size_t mb = (wwBitSize(ec->order, n + 1) / 3 / w) * w; - if (mb < w) - continue; - wwCopy(d, ec->order, m); - wwTrimHi(d, m, mb); - wwSetBit(d, mb, TRUE); - ecPreSH(pre, ec->base, w, ec, stack); - while (!wwIsZero(d, m)) - { - if (!ecMulPreSH(pt0, pre, ec, d, m, stack) || - !ecMulA(pt1, ec->base, ec, d, m, stack) || - !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - wwShLo(d, m, w); - } - } // все хорошо blobClose(state); return TRUE; diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 66f0769f..22513c97 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.03.03 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -26,9 +26,9 @@ \expect Функции ecMulPreXX() регулярны на кривой ec. Поэтому скаляр d не рандомизируется, а вместо среднего времени замеряется минимальное. -\remark Битовая длина скаляра функции ecMulPreSH() примерно в два раза меньше +\remark Битовая длина скаляра функции ecMulPreSO2() примерно в два раза меньше длин скаляров других функций. Такая логика соответствует применению -ecMulPreSH() в реализации протоколов Bake. +ecMulPreSO2() в реализации протоколов Bake. ******************************************************************************* */ @@ -41,8 +41,7 @@ static bool_t ecpBenchEc(const ec_o* ec) const size_t max_w = 6; const size_t min_h = (B_OF_O(mo) + max_w - 1) / max_w; const size_t max_h = (B_OF_O(mo) + min_w - 1) / min_w; - const size_t max_pre_count = - MAX2(min_h * SIZE_BIT_POS(max_w - 1), SIZE_BIT_POS(max_w - 1) + 3); + const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); const size_t reps = 200; void* state; octet* combo_state; /* [prngCOMBO_keep()] */ @@ -56,20 +55,19 @@ static bool_t ecpBenchEc(const ec_o* ec) O_OF_W(2 * n), O_OF_W(m), sizeof(ec_pre_t) + O_OF_W(max_pre_count * ec->d * n), - utilMax(13, + utilMax(12, ecMulA_deep(n, ec->d, ec->deep, m), ecPreSO_deep(n, ec->d, ec->deep), ecPreSOA_deep(n, ec->d, ec->deep), ecPreOD_deep(n, ec->d, ec->deep), ecPreSI_deep(n, ec->d, ec->deep, max_h), - ecPreSH_deep(ec->deep), ecpPreSOJ_deep(n, ec->f->deep), ecpPreSOA_deep(n, ec->f->deep, max_w), ecMulPreSO_deep(n, ec->d, ec->deep, m), + ecMulPreSO2_deep(n, ec->d, ec->deep, m), ecMulPreSOA_deep(n, ec->d, ec->deep, m), ecMulPreOD_deep(n, ec->d, ec->deep, m), - ecMulPreSI_deep(n, ec->d, ec->deep, m), - ecMulPreSH_deep(n, ec->d, ec->deep, m)), + ecMulPreSI_deep(n, ec->d, ec->deep, m)), SIZE_MAX, &combo_state, &pt, &d, &pre, &stack); if (state == 0) @@ -111,6 +109,27 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)ticks, (unsigned)tmSpeed(1, ticks)); } + // ecPre+MulPre[SO2] + for (w = min_w; w <= max_w; ++w) + { + const size_t mb = (B_OF_O(mo) / 2 / w) * w; + if (mb < w) + continue; + wwSetW(d, m, 1); + wwSetBit(d, mb, TRUE); + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + ecpPreSOJ(pre, ec->base, w, ec, stack); + ecMulPreSO2(pt, pre, ec, d, m, stack); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf(" ecpPre+ecMulPre[SO2,w=%u]: %u cycles/pt [%u pts/sec]\n", + (unsigned)w, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + } // ecpPre+ecMulPre[SOA] for (w = MAX2(3, min_w); w <= max_w; ++w) { @@ -164,27 +183,6 @@ static bool_t ecpBenchEc(const ec_o* ec) (unsigned)ticks, (unsigned)tmSpeed(1, ticks)); } - // ecPre+MulPre[SH] - for (w = min_w; w <= max_w; ++w) - { - const size_t mb = (B_OF_O(mo) / 2 / w) * w; - if (mb < w) - continue; - wwSetW(d, m, 1); - wwSetBit(d, mb, TRUE); - for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) - { - tm_ticks_t t = tmTicks(); - ecpPreSHJ(pre, ec->base, w, ec, stack); - ecMulPreSH(pt, pre, ec, d, m, stack); - if ((t = tmTicks() - t) < ticks) - ticks = t; - } - printf(" ecpPre+ecMulPre[SH,w=%u]: %u cycles/pt [%u pts/sec]\n", - (unsigned)w, - (unsigned)ticks, - (unsigned)tmSpeed(1, ticks)); - } // ecPre[SO] for (w = min_w; w <= max_w; ++w) { diff --git a/test/math/ecp_test.c b/test/math/ecp_test.c index 33deb3ea..1c802692 100644 --- a/test/math/ecp_test.c +++ b/test/math/ecp_test.c @@ -4,7 +4,7 @@ \brief Tests for elliptic curves over prime fields \project bee2/test \created 2017.05.29 -\version 2026.03.03 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -682,12 +682,10 @@ static bool_t ecPreChecksum(word a[], const ec_pre_t* pre, u32 seed, prngCOMBOStart(state, seed); // число предвычисленных точек count = SIZE_BIT_POS(pre->w - 1); - if (pre->type == ec_pre_sh) - count += 3; - else if (pre->type == ec_pre_so) + if (pre->type == ec_pre_so) count *= pre->h; // проективные предвычисленные точки? - if (pre->type == ec_pre_so || pre->type == ec_pre_sh) + if (pre->type == ec_pre_so) { wwCopy(t1, ecPrePt(pre, 0, ec), ec->d * ec->f->n); for (i = 1; i < count; ++i) @@ -736,7 +734,7 @@ static bool_t ecpTestEc(const ec_o* ec) const size_t n = ec->f->n; const size_t min_w = 1; const size_t max_w = 6; - const size_t max_pre_count = SIZE_BIT_POS(max_w - 1) + 3; + const size_t max_pre_count = SIZE_BIT_POS(max_w - 1); // состояние void* state; ec_pre_t* pre; /* [max_pre_count проективных точек] */ @@ -752,7 +750,7 @@ static bool_t ecpTestEc(const ec_o* ec) O_OF_W(ec->d * n), O_OF_W(ec->d * n), O_OF_W(n + 1), - utilMax(17, + utilMax(15, ec->deep, ecpIsValid_deep(n, ec->f->deep), ecpGroupSeemsValid_deep(n, ec->f->deep), @@ -767,8 +765,6 @@ static bool_t ecpTestEc(const ec_o* ec) ecPreSOA_deep(n, ec->d, ec->deep), ecpPreSOA_deep(n, ec->f->deep, max_w), ecpSmallMultA_deep(n, ec->f->deep, max_w), - ecPreSH_deep(ec->deep), - ecpPreSHJ_deep(n, ec->f->deep, ec->deep), ecPreChecksum_deep(n, ec->d, ec->deep)), SIZE_MAX, &pre, &pt0, &pt1, &d, &stack); @@ -860,23 +856,6 @@ static bool_t ecpTestEc(const ec_o* ec) return FALSE; } } - // предвычисления: схема SH - for (w = min_w; w <= max_w; ++w) - { - const u32 seed = 43; - bool_t nz; - // эталонная контрольная сумма - ecPreSH(pre, ec->base, w, ec, stack); - nz = ecPreChecksum(pt0, pre, seed, ec, stack); - // проверить ecpPreSHJ() - ecpPreSHJ(pre, ec->base, w, ec, stack); - if (ecPreChecksum(pt1, pre, seed, ec, stack) != nz || - nz && !wwEq(pt0, pt1, 2 * n)) - { - blobClose(state); - return FALSE; - } - } // все хорошо blobClose(state); return TRUE; From 255955055278ab6171cc20921d2e7358e5dc67d8 Mon Sep 17 00:00:00 2001 From: agievich Date: Thu, 5 Mar 2026 21:04:46 +0300 Subject: [PATCH 54/77] Add bignPrePrint() --- include/bee2/math/ww.h | 28 ++++++++- test/crypto/bign_test.c | 127 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/include/bee2/math/ww.h b/include/bee2/math/ww.h index 0c6ad2ac..571bb5e8 100644 --- a/include/bee2/math/ww.h +++ b/include/bee2/math/ww.h @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.02.11 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -99,6 +99,32 @@ extern "C" { #define wwNeg(a, n)\ memNeg(a, O_OF_W(n)) +/* +******************************************************************************* +\def wwH8 +\brief Массив машинных слов по 8 октетам + +\def wwH16 +\brief Массив машинных слов по 16 октетам +******************************************************************************* +*/ + +#if (B_PER_W == 16) + #define wwH8(a, b, c, d, e, f, g, h)\ + 0x##b##a, 0x##d##c, 0x##f##e, 0x##h##g +#elif (B_PER_W == 32) + #define wwH8(a, b, c, d, e, f, g, h)\ + 0x##d##c##b##a, 0x##h##g##f##e +#elif (B_PER_W == 64) + #define wwH8(a, b, c, d, e, f, g, h)\ + 0x##h##g##f##e##d##c##b##a +#else + #error "Unsupported word size" +#endif /* B_PER_W */ + +#define wordH16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)\ + wordH8(a, b, c, d, e, f, g, h), wordH8(i, j, k, l, m, n, o, p) + /* ******************************************************************************* Копирование, логические операции diff --git a/test/crypto/bign_test.c b/test/crypto/bign_test.c index 85d67b7c..566d0a29 100644 --- a/test/crypto/bign_test.c +++ b/test/crypto/bign_test.c @@ -4,22 +4,26 @@ \brief Tests for STB 34.101.45 (bign) \project bee2/test \created 2012.08.27 -\version 2025.09.29 +\version 2026.03.05 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* */ +#include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include /* ******************************************************************************* @@ -100,13 +104,132 @@ static err_t _calc_q(bign_params* params, void* state) return ERR_OK; } +/* +******************************************************************************* +Предвычисления с печатью +******************************************************************************* +*/ + +static const char* ecPreTypename(ec_pre_type type) +{ + switch (type) + { + case ec_pre_so: + return "ec_pre_so"; + case ec_pre_soa: + return "ec_pre_soa"; + case ec_pre_od: + return "ec_pre_od"; + case ec_pre_si: + return "ec_pre_si"; + default: + return "unknown"; + } +} + +static err_t bignPrePrintEc(const ec_o* ec, ec_pre_type type, size_t w) +{ + err_t code; + size_t mb; + size_t coord_size; + size_t pre_count; + size_t h; + void* state; + ec_pre_t* pre; /* [pre_count аффинных точек] */ + void* stack; + size_t pos; + // размерности + mb = wwBitSize(ec->order, ec->f->n + 1); + coord_size = O_OF_W(ec->f->n); + if (w == 0 || (type == ec_pre_si && mb % w) || coord_size % 16) + return ERR_BAD_INPUT; + h = (type != ec_pre_soa) ? h = (mb + w - 1) / w : 0; + pre_count = SIZE_BIT_POS(w - 1); + if (type == ec_pre_od) + pre_count *= h; + // создать состояние + state = blobCreate2( + sizeof(ec_pre_t) + pre_count * 2 * coord_size, + utilMax(3, + ecPreSOA_deep(ec->f->n, ec->d, ec->deep), + ecPreOD_deep(ec->f->n, ec->d, ec->deep), + ecPreSI_deep(ec->f->n, ec->d, ec->deep, h)), + SIZE_MAX, + &pre, &stack); + if (state == 0) + return ERR_OUTOFMEMORY; + // предвычисления + switch (type) + { + case ec_pre_soa: + code = ecPreSOA(pre, ec->base, w, ec, stack) ? + ERR_OK : ERR_BAD_PARAMS; + break; + case ec_pre_od: + code = ecPreOD(pre, ec->base, w, h, ec, stack) ? + ERR_OK : ERR_BAD_PARAMS; + break; + case ec_pre_si: + code = ecPreSI(pre, ec->base, w, h, ec, stack) ? + ERR_OK : ERR_BAD_PARAMS; + break; + default: + code = ERR_NOT_IMPLEMENTED; + } + ERR_CALL_HANDLE(code, blobClose(state)); + // начало печати + printf( + "ec_pre_t _pre = {\n" + "\t%s, %u, %u,\n", + ecPreTypename(type), (unsigned)w, (unsigned)h); + // цикл по точкам + for (pos = 0; pos < pre_count; ++pos) + { + octet* ptr; + size_t i; + // печать точки + ptr = (octet*)ecPrePtA(pre, pos, ec); + wwTo(ptr, 2 * coord_size, ecPrePtA(pre, pos, ec)); + for (i = 0; i < 2 * coord_size; ++i) + { + if (i % 16 == 0) + printf("\twwFromH16(%02X,", ptr[i]); + else if ((i + 1) % 16 == 0) + printf("%02X),\n", ptr[i]); + else + printf("%02X,", ptr[i]); + } + } + // завершение печати + printf("};\n"); + // завершение + blobClose(state); + return code; +} + +err_t bignPrePrint(const char* name, ec_pre_type type, size_t w) +{ + err_t code; + bign_params params[1]; + ec_o* ec; + code = bignParamsStd(params, name); + ERR_CALL_CHECK(code); + code = bignParamsCheck(params); + ERR_CALL_CHECK(code); + code = bignEcCreate(&ec, params); + ERR_CALL_CHECK(code); + code = bignPrePrintEc(ec, type, w); + bignEcClose(ec); + return code; +} + /* ******************************************************************************* Самотестирование -# Выполняются тесты из приложения к СТБ 34.101.45. -# Номера тестов соответствуют номерам таблиц приложения. --# Дополнительные тесты покрывают ошибки, выявленные в результате испытаний. +-# Дополнительные тесты покрывают ошибки, выявленные при испытаниях. ******************************************************************************* */ From 3f5c964327dd75423459ddaca407ae433c527ad6 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 6 Mar 2026 19:02:55 +0300 Subject: [PATCH 55/77] Add Bign128, Bign192 and Bign256 --- include/bee2/crypto/bign128.h | 234 + include/bee2/crypto/bign192.h | 237 + include/bee2/crypto/bign256.h | 234 + include/bee2/math/ec.h | 4 +- include/bee2/math/ww.h | 12 +- src/CMakeLists.txt | 3 + src/crypto/bign/bign128.c | 233 + src/crypto/bign/bign192.c | 233 + src/crypto/bign/bign256.c | 234 + src/crypto/bign/pre/bign128_pre_od7.c | 9488 +++++++++++++++++++++++++ src/crypto/bign/pre/bign192_pre_si8.c | 784 ++ src/crypto/bign/pre/bign256_pre_si8.c | 1040 +++ test/CMakeLists.txt | 3 + test/crypto/bign128_test.c | 179 + test/crypto/bign192_test.c | 70 + test/crypto/bign256_test.c | 70 + test/crypto/bign_test.c | 40 +- test/crypto/dstu_test.c | 11 +- test/math/ecp_bench.c | 6 +- test/test.c | 11 +- win/bee2.def | 33 + win/vs09/libbee2.vcproj | 12 + win/vs09/test.vcproj | 12 + win/vs15/libbee2.vcxproj | 3 + win/vs15/libbee2.vcxproj.filters | 9 + win/vs15/test.vcxproj | 3 + win/vs15/test.vcxproj.filters | 9 + 27 files changed, 13168 insertions(+), 39 deletions(-) create mode 100644 include/bee2/crypto/bign128.h create mode 100644 include/bee2/crypto/bign192.h create mode 100644 include/bee2/crypto/bign256.h create mode 100644 src/crypto/bign/bign128.c create mode 100644 src/crypto/bign/bign192.c create mode 100644 src/crypto/bign/bign256.c create mode 100644 src/crypto/bign/pre/bign128_pre_od7.c create mode 100644 src/crypto/bign/pre/bign192_pre_si8.c create mode 100644 src/crypto/bign/pre/bign256_pre_si8.c create mode 100644 test/crypto/bign128_test.c create mode 100644 test/crypto/bign192_test.c create mode 100644 test/crypto/bign256_test.c diff --git a/include/bee2/crypto/bign128.h b/include/bee2/crypto/bign128.h new file mode 100644 index 00000000..06355270 --- /dev/null +++ b/include/bee2/crypto/bign128.h @@ -0,0 +1,234 @@ +/* +******************************************************************************* +\file bign128.h +\brief STB 34.101.45 (bign): Bign algorithms with bign-curve256v1 and belt-hash +\project bee2 [cryptographic library] +\created 2025.03.05 +\version 2025.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +/*! +******************************************************************************* +\file bign128.h +\brief Алгоритмы Bign уровня 128 +******************************************************************************* +*/ + +#ifndef __BIGN128_H +#define __BIGN128_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bee2/crypto/bign.h" + +/*! +******************************************************************************* +\file bign128.h + +Алгоритмы Bign128 -- это алгоритмы Bign со следующими уточнениями: +- уровень стойкости l = 128; +- используется эллиптическая кривая bign-curve256v1; +- при выработке и проверке ЭЦП предварительное хэширование выполняется с + помощью алгоритма belt-hash. + +Повторяются соглашения и интерфейсы модуля bee2/crypto/bign. Интерфейсы +упрощаются: из входных и выходных данных исключаются долговременные параметры +и идентификатор алгоритма предварительного хэширования. + +\expect{ERR_BAD_INPUT} Все входные указатели корректны. + +\warning Идентификационная ЭЦП не поддерживается. +******************************************************************************* +*/ + +/* +******************************************************************************* +Управление ключами +******************************************************************************* +*/ + +/*! \brief Генерация пары ключей + + Генерируются личный privkey и открытый pubkey ключи. При генерации + используется генератор rng и его состояние rng_state. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Используется криптографически стойкий генератор rng. + \return ERR_OK, если ключи успешно сгенерированы, и код ошибки + в противном случае. +*/ +err_t bign128KeypairGen( + octet privkey[32], /*!< [out] личный ключ */ + octet pubkey[64], /*!< [out] открытый ключ */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in/out] состояние генератора */ +); + +/*! \brief Проверка пары ключей + + Проверяется корректность личного ключа privkey и соответствие ему открытого + ключа pubkey. + \return ERR_OK, если пара корректна, и код ошибки в противном случае. +*/ +err_t bign128KeypairVal( + const octet privkey[32], /*!< [in] личный ключ */ + const octet pubkey[64] /*!< [in] открытый ключ */ +); + +/*! \brief Проверка открытого ключа + + Проверяется корректность открытого ключа pubkey. + \return ERR_OK, если ключ корректен, и код ошибки в противном случае. +*/ +err_t bign128PubkeyVal( + const octet pubkey[64] /*!< [in] проверяемый ключ */ +); + +/*! \brief Построение открытого ключа по личному + + По личному ключу privkey строится открытый ключ pubkey. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если открытый ключ успешно построен, и код ошибки + в противном случае. +*/ +err_t bign128PubkeyCalc( + octet pubkey[64], /*!< [out] открытый ключ */ + const octet privkey[32] /*!< [in] личный ключ */ +); + +/*! \brief Построение общего ключа Диффи -- Хеллмана + + По личному ключу privkey и открытому ключу pubkey противоположной стороны + строится общий ключ [key_len]key. Общий ключ определяется как + privkey-кратное ключа pubkey, что соответствует протоколу + Диффи -- Хеллмана. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \expect{ERR_BAD_SHAREDKEY} key_len <= 64. + \return ERR_OK, если общий ключ успешно построен, и код ошибки + в противном случае. +*/ +err_t bign128DH( + octet key[], /*!< [out] общий ключ */ + const octet privkey[32], /*!< [in] личный ключ */ + const octet pubkey[64], /*!< [in] открытый ключ (другой стороны) */ + size_t key_len /*!< [in] длина key в октетах */ +); + +/* +******************************************************************************* +Электронная цифровая подпись (ЭЦП) +******************************************************************************* +*/ + +/*! \brief Выработка ЭЦП + + Вырабатывается подпись sig сообщения с хэш-значением hash. + Подпись вырабатывается на личном ключе privkey. При выработке ЭЦП + используется генератор rng с состоянием rng_state. + \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Генератор rng является криптографически стойким. + \return ERR_OK, если подпись выработана, и код ошибки в противном + случае. +*/ +err_t bign128Sign( + octet sig[48], /*!< [out] подпись */ + const octet hash[32], /*!< [in] хэш-значение */ + const octet privkey[32], /*!< [in] личный ключ */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in/out] состояние генератора */ +); + +/*! \brief Детерминированная выработка ЭЦП + + Вырабатывается подпись sig сообщения с хэш-значением hash. + Подпись вырабатывается на личном ключе privkey. Одноразовый личный ключ + генерируется с использованием дополнительных данных [t_len]t. + Если t == 0, то дополнительные данные не используются. + \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если подпись выработана, и код ошибки в противном случае. +*/ +err_t bign128Sign2( + octet sig[48], /*!< [out] подпись */ + const octet hash[32], /*!< [in] хэш-значение */ + const octet privkey[32], /*!< [in] личный ключ */ + const void* t, /*!< [in] дополнительные данные */ + size_t t_len /*!< [in] размер дополнительных данных */ +); + +/*! \brief Проверка ЭЦП + + Проверяется ЭЦП sig сообщения с хэш-значением hash. При проверке + используется открытый ключ pubkey. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \return ERR_OK, если подпись корректна, и код ошибки в противном случае. + \remark При нарушении ограничений на ЭЦП возвращается код ERR_BAD_SIG. +*/ +err_t bign128Verify( + const octet hash[32], /*!< [in] хэш-значение */ + const octet sig[48], /*!< [in] подпись */ + const octet pubkey[64] /*!< [in] открытый ключ */ +); + +/* +******************************************************************************* +Транспорт ключа +******************************************************************************* +*/ + +/*! \brief Создание токена ключа + + Создается токен [48 + len]token ключа [len]key с заголовком [16]header. + При создании токена используются открытый ключ получателя pubkey + и генератор rng с состоянием rng_state. + \expect{ERR_BAD_INPUT} len >= 16. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Используется криптографически стойкий генератор rng. + \return ERR_OK, если токен успешно создан, и код ошибки в противном случае. + \remark Может передаваться нулевой указатель header. В этом случае будет + использоваться заголовок из всех нулей. +*/ +err_t bign128KeyWrap( + octet token[], /*!< [out] токен ключа */ + const octet key[], /*!< [in] транспортируемый ключ */ + size_t len, /*!< [in] длина ключа в октетах */ + const octet header[16], /*!< [in] заголовок ключа */ + const octet pubkey[64], /*!< [in] открытый ключ получателя */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in,out] состояние генератора */ +); + +/*! \brief Разбор токена ключа + + Определяется ключ [len - 48]key, который имеет заголовок [16]header + и содержится в токене [len]token. При разборе токена используется личный + ключ privkey. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если токен успешно разобран, и код ошибки в противном + случае. + \remark Может передаваться нулевой указатель header. В этом случае будет + использоваться заголовок из всех нулей. + \remark При нарушении целостности токена возвращается код ERR_BAD_KEYTOKEN. + Этот код будет возвращен, если len < 64. +*/ +err_t bign128KeyUnwrap( + octet key[], /*!< [out] ключ */ + const octet token[], /*!< [in] токен ключа */ + size_t len, /*!< [in] длина токена в октетах */ + const octet header[16], /*!< [in] заголовок ключа */ + const octet privkey[32] /*!< [in] личный ключ получателя */ +); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __BIGN128_H */ diff --git a/include/bee2/crypto/bign192.h b/include/bee2/crypto/bign192.h new file mode 100644 index 00000000..bc6e71b3 --- /dev/null +++ b/include/bee2/crypto/bign192.h @@ -0,0 +1,237 @@ +/* +******************************************************************************* +\file bign192.h +\brief STB 34.101.45 (bign): Bign algorithms with bign-curve384v1 and bash384 +\project bee2 [cryptographic library] +\created 2025.03.06 +\version 2025.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +/*! +******************************************************************************* +\file bign192.h +\brief Алгоритмы Bign уровня 192 +******************************************************************************* +*/ + +#ifndef __BIGN192_H +#define __BIGN192_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bee2/crypto/bign.h" + +/*! +******************************************************************************* +\file bign192.h + +Алгоритмы Bign192 -- это алгоритмы Bign со следующими уточнениями: +- уровень стойкости l = 192; +- используется эллиптическая кривая bign-curve384v1; +- при выработке и проверке ЭЦП предварительное хэширование выполняется с + помощью алгоритма bash384. + +Повторяются соглашения и интерфейсы модуля bee2/crypto/bign. Интерфейсы +упрощаются: из входных и выходных данных исключаются долговременные параметры +и идентификатор алгоритма предварительного хэширования. + +\expect{ERR_BAD_INPUT} Все входные указатели корректны. + +\warning Идентификационная ЭЦП не поддерживается. +******************************************************************************* +*/ + +/* +******************************************************************************* +Управление ключами +******************************************************************************* +*/ + +/*! \brief Генерация пары ключей + + Генерируются личный privkey и открытый pubkey ключи. При генерации + используется генератор rng и его состояние rng_state. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Используется криптографически стойкий генератор rng. + \return ERR_OK, если ключи успешно сгенерированы, и код ошибки + в противном случае. +*/ +err_t bign192KeypairGen( + octet privkey[48], /*!< [out] личный ключ */ + octet pubkey[96], /*!< [out] открытый ключ */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in/out] состояние генератора */ +); + +/*! \brief Проверка пары ключей + + Проверяется корректность личного ключа privkey и соответствие ему открытого + ключа pubkey. + \return ERR_OK, если пара корректна, и код ошибки в противном случае. +*/ +err_t bign192KeypairVal( + const octet privkey[48], /*!< [in] личный ключ */ + const octet pubkey[96] /*!< [in] открытый ключ */ +); + +/*! \brief Проверка открытого ключа + + Проверяется корректность открытого ключа pubkey. + \return ERR_OK, если ключ корректен, и код ошибки в противном случае. +*/ +err_t bign192PubkeyVal( + const octet pubkey[96] /*!< [in] проверяемый ключ */ +); + +/*! \brief Построение открытого ключа по личному + + По личному ключу privkey строится открытый ключ pubkey. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если открытый ключ успешно построен, и код ошибки + в противном случае. +*/ +err_t bign192PubkeyCalc( + octet pubkey[96], /*!< [out] открытый ключ */ + const octet privkey[48] /*!< [in] личный ключ */ +); + +/*! \brief Построение общего ключа Диффи -- Хеллмана + + По личному ключу privkey и открытому ключу pubkey противоположной стороны + строится общий ключ [key_len]key. Общий ключ определяется как + privkey-кратное ключа pubkey, что соответствует протоколу + Диффи -- Хеллмана. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \expect{ERR_BAD_SHAREDKEY} key_len <= 64. + \return ERR_OK, если общий ключ успешно построен, и код ошибки + в противном случае. +*/ +err_t bign192DH( + octet key[], /*!< [out] общий ключ */ + const octet privkey[48], /*!< [in] личный ключ */ + const octet pubkey[96], /*!< [in] открытый ключ (другой стороны) */ + size_t key_len /*!< [in] длина key в октетах */ +); + +/* +******************************************************************************* +Электронная цифровая подпись (ЭЦП) +******************************************************************************* +*/ + +/*! \brief Выработка ЭЦП + + Вырабатывается подпись sig сообщения с хэш-значением hash. + Подпись вырабатывается на личном ключе privkey. При выработке ЭЦП + используется генератор rng с состоянием rng_state. + \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Генератор rng является криптографически стойким. + \return ERR_OK, если подпись выработана, и код ошибки в противном + случае. +*/ +err_t bign192Sign( + octet sig[72], /*!< [out] подпись */ + const octet hash[48], /*!< [in] хэш-значение */ + const octet privkey[48], /*!< [in] личный ключ */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in/out] состояние генератора */ +); + +/*! \brief Детерминированная выработка ЭЦП + + Вырабатывается подпись sig сообщения с хэш-значением hash. + Подпись вырабатывается на личном ключе privkey. Одноразовый личный ключ + генерируется с использованием дополнительных данных [t_len]t. + Если t == 0, то дополнительные данные не используются. + \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если подпись выработана, и код ошибки в противном + случае. +*/ +err_t bign192Sign2( + octet sig[72], /*!< [out] подпись */ + const octet hash[48], /*!< [in] хэш-значение */ + const octet privkey[48], /*!< [in] личный ключ */ + const void* t, /*!< [in] дополнительные данные */ + size_t t_len /*!< [in] размер дополнительных данных */ +); + +/*! \brief Проверка ЭЦП + + Проверяется ЭЦП sig сообщения с хэш-значением hash. При проверке + используется открытый ключ pubkey. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \return ERR_OK, если подпись корректна, и код ошибки в противном + случае. + \remark При нарушении ограничений на ЭЦП возвращается код ERR_BAD_SIG. +*/ +err_t bign192Verify( + const octet hash[48], /*!< [in] хэш-значение */ + const octet sig[72], /*!< [in] подпись */ + const octet pubkey[96] /*!< [in] открытый ключ */ +); + +/* +******************************************************************************* +Транспорт ключа +******************************************************************************* +*/ + +/*! \brief Создание токена ключа + + Создается токен [64 + len]token ключа [len]key с заголовком [16]header. + При создании токена используются открытый ключ получателя pubkey и + генератор rng с состоянием rng_state. + \expect{ERR_BAD_INPUT} len >= 16. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Используется криптографически стойкий генератор rng. + \return ERR_OK, если токен успешно создан, и код ошибки в противном + случае. + \remark Может передаваться нулевой указатель header. В этом случае будет + использоваться заголовок из всех нулей. +*/ +err_t bign192KeyWrap( + octet token[], /*!< [out] токен ключа */ + const octet key[], /*!< [in] транспортируемый ключ */ + size_t len, /*!< [in] длина ключа в октетах */ + const octet header[16], /*!< [in] заголовок ключа */ + const octet pubkey[96], /*!< [in] открытый ключ получателя */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in,out] состояние генератора */ +); + +/*! \brief Разбор токена ключа + + Определяется ключ [len - 64]key, который имеет заголовок + [16]header и содержится в токене [len]token. При разборе токена + используется личный ключ privkey. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если токен успешно разобран, и код ошибки в противном + случае. + \remark Может передаваться нулевой указатель header. В этом случае будет + использоваться заголовок из всех нулей. + \remark При нарушении целостности токена возвращается код ERR_BAD_KEYTOKEN. + Этот код будет возвращен, если len < 80. +*/ +err_t bign192KeyUnwrap( + octet key[], /*!< [out] ключ */ + const octet token[], /*!< [in] токен ключа */ + size_t len, /*!< [in] длина токена в октетах */ + const octet header[16], /*!< [in] заголовок ключа */ + const octet privkey[48] /*!< [in] личный ключ получателя */ +); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __BIGN192_H */ diff --git a/include/bee2/crypto/bign256.h b/include/bee2/crypto/bign256.h new file mode 100644 index 00000000..62a644ee --- /dev/null +++ b/include/bee2/crypto/bign256.h @@ -0,0 +1,234 @@ +/* +******************************************************************************* +\file bign256.h +\brief STB 34.101.45 (bign): Bign algorithms with bign-curve512v1 and bash512 +\project bee2 [cryptographic library] +\created 2025.03.06 +\version 2025.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +/*! +******************************************************************************* +\file bign256.h +\brief Алгоритмы Bign уровня 256 +******************************************************************************* +*/ + +#ifndef __BIGN256_H +#define __BIGN256_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bee2/crypto/bign.h" + +/*! +******************************************************************************* +\file bign256.h + +Алгоритмы bign256 -- это алгоритмы Bign со следующими уточнениями: +- уровень стойкости l = 256; +- используется эллиптическая кривая bign-curve512v1; +- при выработке и проверке ЭЦП предварительное хэширование выполняется с + помощью алгоритма bash512. + +Повторяются соглашения и интерфейсы модуля bee2/crypto/bign. Интерфейсы +упрощаются: из входных и выходных данных исключаются долговременные параметры +и идентификатор алгоритма предварительного хэширования. + +\expect{ERR_BAD_INPUT} Все входные указатели корректны. + +\warning Идентификационная ЭЦП не поддерживается. +******************************************************************************* +*/ + +/* +******************************************************************************* +Управление ключами +******************************************************************************* +*/ + +/*! \brief Генерация пары ключей + + Генерируются личный privkey и открытый pubkey ключи. При генерации + используется генератор rng и его состояние rng_state. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Используется криптографически стойкий генератор rng. + \return ERR_OK, если ключи успешно сгенерированы, и код ошибки + в противном случае. +*/ +err_t bign256KeypairGen( + octet privkey[64], /*!< [out] личный ключ */ + octet pubkey[128], /*!< [out] открытый ключ */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in/out] состояние генератора */ +); + +/*! \brief Проверка пары ключей + + Проверяется корректность личного ключа privkey и соответствие ему открытого + ключа pubkey. + \return ERR_OK, если пара корректна, и код ошибки в противном случае. +*/ +err_t bign256KeypairVal( + const octet privkey[64], /*!< [in] личный ключ */ + const octet pubkey[128] /*!< [in] открытый ключ */ +); + +/*! \brief Проверка открытого ключа + + Проверяется корректность открытого ключа pubkey. + \return ERR_OK, если ключ корректен, и код ошибки в противном случае. +*/ +err_t bign256PubkeyVal( + const octet pubkey[128] /*!< [in] проверяемый ключ */ +); + +/*! \brief Построение открытого ключа по личному + + По личному ключу privkey строится открытый ключ pubkey. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если открытый ключ успешно построен, и код ошибки + в противном случае. +*/ +err_t bign256PubkeyCalc( + octet pubkey[128], /*!< [out] открытый ключ */ + const octet privkey[64] /*!< [in] личный ключ */ +); + +/*! \brief Построение общего ключа Диффи -- Хеллмана + + По личному ключу privkey и открытому ключу pubkey противоположной стороны + строится общий ключ [key_len]key. Общий ключ определяется как + privkey-кратное ключа pubkey, что соответствует протоколу + Диффи -- Хеллмана. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \expect{ERR_BAD_SHAREDKEY} key_len <= 64. + \return ERR_OK, если общий ключ успешно построен, и код ошибки + в противном случае. +*/ +err_t bign256DH( + octet key[], /*!< [out] общий ключ */ + const octet privkey[64], /*!< [in] личный ключ */ + const octet pubkey[128], /*!< [in] открытый ключ (другой стороны) */ + size_t key_len /*!< [in] длина key в октетах */ +); + +/* +******************************************************************************* +Электронная цифровая подпись (ЭЦП) +******************************************************************************* +*/ + +/*! \brief Выработка ЭЦП + + Вырабатывается подпись sig сообщения с хэш-значением hash. + Подпись вырабатывается на личном ключе privkey. При выработке ЭЦП + используется генератор rng с состоянием rng_state. + \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Генератор rng является криптографически стойким. + \return ERR_OK, если подпись выработана, и код ошибки в противном + случае. +*/ +err_t bign256Sign( + octet sig[96], /*!< [out] подпись */ + const octet hash[64], /*!< [in] хэш-значение */ + const octet privkey[64], /*!< [in] личный ключ */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in/out] состояние генератора */ +); + +/*! \brief Детерминированная выработка ЭЦП + + Вырабатывается подпись sig сообщения с хэш-значением hash. + Подпись вырабатывается на личном ключе privkey. Одноразовый личный ключ + генерируется с использованием дополнительных данных [t_len]t. + Если t == 0, то дополнительные данные не используются. + \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если подпись выработана, и код ошибки в противном случае. +*/ +err_t bign256Sign2( + octet sig[96], /*!< [out] подпись */ + const octet hash[64], /*!< [in] хэш-значение */ + const octet privkey[64], /*!< [in] личный ключ */ + const void* t, /*!< [in] дополнительные данные */ + size_t t_len /*!< [in] размер дополнительных данных */ +); + +/*! \brief Проверка ЭЦП + + Проверяется ЭЦП sig сообщения с хэш-значением hash. При проверке + используется открытый ключ pubkey. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \return ERR_OK, если подпись корректна, и код ошибки в противном случае. + \remark При нарушении ограничений на ЭЦП возвращается код ERR_BAD_SIG. +*/ +err_t bign256Verify( + const octet hash[64], /*!< [in] хэш-значение */ + const octet sig[96], /*!< [in] подпись */ + const octet pubkey[128] /*!< [in] открытый ключ */ +); + +/* +******************************************************************************* +Транспорт ключа +******************************************************************************* +*/ + +/*! \brief Создание токена ключа + + Создается токен [48 + len]token ключа [len]key с заголовком [16]header. + При создании токена используются открытый ключ получателя pubkey + и генератор rng с состоянием rng_state. + \expect{ERR_BAD_INPUT} len >= 16. + \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. + \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. + \expect Используется криптографически стойкий генератор rng. + \return ERR_OK, если токен успешно создан, и код ошибки в противном случае. + \remark Может передаваться нулевой указатель header. В этом случае будет + использоваться заголовок из всех нулей. +*/ +err_t bign256KeyWrap( + octet token[], /*!< [out] токен ключа */ + const octet key[], /*!< [in] транспортируемый ключ */ + size_t len, /*!< [in] длина ключа в октетах */ + const octet header[16], /*!< [in] заголовок ключа */ + const octet pubkey[128], /*!< [in] открытый ключ получателя */ + gen_i rng, /*!< [in] генератор случайных чисел */ + void* rng_state /*!< [in,out] состояние генератора */ +); + +/*! \brief Разбор токена ключа + + Определяется ключ [len - 80]key, который имеет заголовок [16]header + и содержится в токене [len]token. При разборе токена используется личный + ключ privkey. + \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. + \return ERR_OK, если токен успешно разобран, и код ошибки в противном + случае. + \remark Может передаваться нулевой указатель header. В этом случае будет + использоваться заголовок из всех нулей. + \remark При нарушении целостности токена возвращается код ERR_BAD_KEYTOKEN. + Этот код будет возвращен, если len < 96. +*/ +err_t bign256KeyUnwrap( + octet key[], /*!< [out] ключ */ + const octet token[], /*!< [in] токен ключа */ + size_t len, /*!< [in] длина токена в октетах */ + const octet header[16], /*!< [in] заголовок ключа */ + const octet privkey[64] /*!< [in] личный ключ получателя */ +); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __BIGN256_H */ diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index 3a948e40..fab78cfe 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.03.05 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -546,7 +546,7 @@ typedef struct ec_o word* B; /*!< коэффициент B */ word* base; /*!< базовая точустановке знаковка */ word* order; /*!< порядок группы точек */ - ec_pre_t* pre; /*!< предвычисленные точки */ + const ec_pre_t* pre; /*!< предвычисленные точки */ // } size_t d; /*!< размерность */ word cofactor; /*!< кофактор группы точек */ diff --git a/include/bee2/math/ww.h b/include/bee2/math/ww.h index 571bb5e8..c264c04a 100644 --- a/include/bee2/math/ww.h +++ b/include/bee2/math/ww.h @@ -4,7 +4,7 @@ \brief Arbitrary length words \project bee2 [cryptographic library] \created 2012.04.18 -\version 2026.03.05 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -110,20 +110,20 @@ extern "C" { */ #if (B_PER_W == 16) - #define wwH8(a, b, c, d, e, f, g, h)\ + #define wwH8(a,b,c,d,e,f,g,h)\ 0x##b##a, 0x##d##c, 0x##f##e, 0x##h##g #elif (B_PER_W == 32) - #define wwH8(a, b, c, d, e, f, g, h)\ + #define wwH8(a,b,c,d,e,f,g,h)\ 0x##d##c##b##a, 0x##h##g##f##e #elif (B_PER_W == 64) - #define wwH8(a, b, c, d, e, f, g, h)\ + #define wwH8(a,b,c,d,e,f,g,h)\ 0x##h##g##f##e##d##c##b##a #else #error "Unsupported word size" #endif /* B_PER_W */ -#define wordH16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)\ - wordH8(a, b, c, d, e, f, g, h), wordH8(i, j, k, l, m, n, o, p) +#define wwH16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)\ + wwH8(a,b,c,d,e,f,g,h), wwH8(i,j,k,l,m,n,o,p) /* ******************************************************************************* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index baee4e84..bf8fb702 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,6 +58,9 @@ set(src crypto/bign/bign_misc.c crypto/bign/bign_params.c crypto/bign/bign_sign.c + crypto/bign/bign128.c + crypto/bign/bign192.c + crypto/bign/bign256.c crypto/bign96.c crypto/bpki.c crypto/botp.c diff --git a/src/crypto/bign/bign128.c b/src/crypto/bign/bign128.c new file mode 100644 index 00000000..7b58434e --- /dev/null +++ b/src/crypto/bign/bign128.c @@ -0,0 +1,233 @@ +/* +******************************************************************************* +\file bign128.c +\brief STB 34.101.45 (bign): Bign algorithms with bign-curve256v1 and belt-hash +\project bee2 [cryptographic library] +\created 2026.03.05 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include "bee2/core/err.h" +#include "bee2/core/mt.h" +#include "bee2/core/util.h" +#include "bee2/crypto/bign128.h" +#include "bee2/math/ww.h" +#include "bign_lcl.h" + +/* +******************************************************************************* +Предвычисленные точки +******************************************************************************* +*/ + +#include "pre/bign128_pre_od7.c" + +/* +******************************************************************************* +Кривая +******************************************************************************* +*/ + +static size_t _once; /*< триггер однократности */ +static mt_mtx_t _mtx[1]; /*< мьютекс */ +static bool_t _inited; /*< мьютекс создан? */ +static ec_o* _ec; /*< кривая */ + +static void bign128EcDestroy() +{ + mtMtxLock(_mtx); + bignEcClose(_ec), _ec = 0; + mtMtxUnlock(_mtx); + mtMtxClose(_mtx); +} + +static void bign128EcInit() +{ + ASSERT(!_inited); + // создать мьютекс + if (!mtMtxCreate(_mtx)) + return; + // зарегистрировать деструктор + if (!utilOnExit(bign128EcDestroy)) + { + mtMtxClose(_mtx); + return; + } + _inited = TRUE; +} + +static err_t bign128EcLock(const ec_o** pec) +{ + ASSERT(memIsValid(pec, sizeof(const ec_o*))); + // инициализировать однократно + if (!mtCallOnce(&_once, bign128EcInit) || !_inited) + return ERR_FILE_CREATE; + // заблокировать мьютекс + mtMtxLock(_mtx); + // кривая не создана? + if (_ec == 0) + { + err_t code; + bign_params params[1]; + // загрузить параметры + code = bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1"); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // создать кривую + code = bignEcCreate(&_ec, params); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // настроить ec->pre + _ec->pre = &_pre; + } + // возвратить кривую + *pec = _ec; + return ERR_OK; +} + +static void bign128EcUnlock() +{ + mtMtxUnlock(_mtx); +} + +/* +******************************************************************************* +Управление ключами +******************************************************************************* +*/ + +err_t bign128KeypairGen(octet privkey[32], octet pubkey[64], gen_i rng, + void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); + bign128EcUnlock(ec); + return code; +} + +err_t bign128KeypairVal(const octet privkey[32], const octet pubkey[64]) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeypairValEc(ec, privkey, pubkey); + bign128EcUnlock(ec); + return code; +} + +err_t bign128PubkeyVal(const octet pubkey[64]) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignPubkeyValEc(ec, pubkey); + bign128EcUnlock(ec); + return code; +} + +err_t bign128PubkeyCalc(octet pubkey[64], const octet privkey[32]) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignPubkeyCalcEc(pubkey, ec, privkey); + bign128EcUnlock(ec); + return code; +} + +err_t bign128DH(octet key[], const octet privkey[32], const octet pubkey[64], + size_t key_len) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignDHEc(key, ec, privkey, pubkey, key_len); + bign128EcUnlock(ec); + return code; +} + +/* +******************************************************************************* +ЭЦП +******************************************************************************* +*/ + +static const octet _oid_der[] = { + 0x06, 0x09, 0x2A, 0x70, 0x00, 0x02, 0x00, 0x22, 0x65, 0x1F, 0x51, +}; + +err_t bign128Sign(octet sig[48], const octet hash[32], + const octet privkey[32], gen_i rng, void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, + rng_state); + bign128EcUnlock(ec); + return code; +} + +err_t bign128Sign2(octet sig[48], const octet hash[32], + const octet privkey[32], const void* t, size_t t_len) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, + t_len); + bign128EcUnlock(ec); + return code; +} + +err_t bign128Verify(const octet hash[32], const octet sig[48], + const octet pubkey[64]) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); + bign128EcUnlock(ec); + return code; +} + +/* +******************************************************************************* +Транспорт ключа +******************************************************************************* +*/ + +err_t bign128KeyWrap(octet token[], const octet key[], size_t len, + const octet header[16], const octet pubkey[64], gen_i rng, void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); + bign128EcUnlock(ec); + return code; +} + +err_t bign128KeyUnwrap(octet key[], const octet token[], size_t len, + const octet header[16], const octet privkey[32]) +{ + err_t code; + const ec_o* ec; + code = bign128EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); + bign128EcUnlock(ec); + return code; +} diff --git a/src/crypto/bign/bign192.c b/src/crypto/bign/bign192.c new file mode 100644 index 00000000..c8a64c8e --- /dev/null +++ b/src/crypto/bign/bign192.c @@ -0,0 +1,233 @@ +/* +******************************************************************************* +\file bign192.c +\brief STB 34.101.45 (bign): Bign algorithms with bign-curve256v1 and bash384 +\project bee2 [cryptographic library] +\created 2026.03.06 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include "bee2/core/err.h" +#include "bee2/core/mt.h" +#include "bee2/core/util.h" +#include "bee2/crypto/bign192.h" +#include "bee2/math/ww.h" +#include "bign_lcl.h" + +/* +******************************************************************************* +Предвычисленные точки +******************************************************************************* +*/ + +#include "pre/bign192_pre_si8.c" + +/* +******************************************************************************* +Кривая +******************************************************************************* +*/ + +static size_t _once; /*< триггер однократности */ +static mt_mtx_t _mtx[1]; /*< мьютекс */ +static bool_t _inited; /*< мьютекс создан? */ +static ec_o* _ec; /*< кривая */ + +static void bign192EcDestroy() +{ + mtMtxLock(_mtx); + bignEcClose(_ec), _ec = 0; + mtMtxUnlock(_mtx); + mtMtxClose(_mtx); +} + +static void bign192EcInit() +{ + ASSERT(!_inited); + // создать мьютекс + if (!mtMtxCreate(_mtx)) + return; + // зарегистрировать деструктор + if (!utilOnExit(bign192EcDestroy)) + { + mtMtxClose(_mtx); + return; + } + _inited = TRUE; +} + +static err_t bign192EcLock(const ec_o** pec) +{ + ASSERT(memIsValid(pec, sizeof(const ec_o*))); + // инициализировать однократно + if (!mtCallOnce(&_once, bign192EcInit) || !_inited) + return ERR_FILE_CREATE; + // заблокировать мьютекс + mtMtxLock(_mtx); + // кривая не создана? + if (_ec == 0) + { + err_t code; + bign_params params[1]; + // загрузить параметры + code = bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2"); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // создать кривую + code = bignEcCreate(&_ec, params); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // настроить ec->pre + _ec->pre = &_pre; + } + // возвратить кривую + *pec = _ec; + return ERR_OK; +} + +static void bign192EcUnlock() +{ + mtMtxUnlock(_mtx); +} + +/* +******************************************************************************* +Управление ключами +******************************************************************************* +*/ + +err_t bign192KeypairGen(octet privkey[48], octet pubkey[96], gen_i rng, + void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); + bign192EcUnlock(ec); + return code; +} + +err_t bign192KeypairVal(const octet privkey[48], const octet pubkey[96]) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeypairValEc(ec, privkey, pubkey); + bign192EcUnlock(ec); + return code; +} + +err_t bign192PubkeyVal(const octet pubkey[96]) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignPubkeyValEc(ec, pubkey); + bign192EcUnlock(ec); + return code; +} + +err_t bign192PubkeyCalc(octet pubkey[96], const octet privkey[48]) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignPubkeyCalcEc(pubkey, ec, privkey); + bign192EcUnlock(ec); + return code; +} + +err_t bign192DH(octet key[], const octet privkey[48], const octet pubkey[96], + size_t key_len) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignDHEc(key, ec, privkey, pubkey, key_len); + bign192EcUnlock(ec); + return code; +} + +/* +******************************************************************************* +ЭЦП +******************************************************************************* +*/ + +static const octet _oid_der[] = { + 0x06, 0x09, 0x2A, 0x70, 0x00, 0x02, 0x00, 0x22, 0x65, 0x4D, 0x0C, +}; + +err_t bign192Sign(octet sig[72], const octet hash[48], + const octet privkey[48], gen_i rng, void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, + rng_state); + bign192EcUnlock(ec); + return code; +} + +err_t bign192Sign2(octet sig[72], const octet hash[48], + const octet privkey[48], const void* t, size_t t_len) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, + t_len); + bign192EcUnlock(ec); + return code; +} + +err_t bign192Verify(const octet hash[48], const octet sig[72], + const octet pubkey[96]) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); + bign192EcUnlock(ec); + return code; +} + +/* +******************************************************************************* +Транспорт ключа +******************************************************************************* +*/ + +err_t bign192KeyWrap(octet token[], const octet key[], size_t len, + const octet header[16], const octet pubkey[96], gen_i rng, void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); + bign192EcUnlock(ec); + return code; +} + +err_t bign192KeyUnwrap(octet key[], const octet token[], size_t len, + const octet header[16], const octet privkey[48]) +{ + err_t code; + const ec_o* ec; + code = bign192EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); + bign192EcUnlock(ec); + return code; +} diff --git a/src/crypto/bign/bign256.c b/src/crypto/bign/bign256.c new file mode 100644 index 00000000..795dcfd1 --- /dev/null +++ b/src/crypto/bign/bign256.c @@ -0,0 +1,234 @@ +/* +******************************************************************************* +\file bign256.c +\brief STB 34.101.45 (bign): Bign algorithms with bign-curve512v1 and bash512 +\project bee2 [cryptographic library] +\created 2026.03.06 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include "bee2/core/err.h" +#include "bee2/core/mt.h" +#include "bee2/core/util.h" +#include "bee2/crypto/bign256.h" +#include "bee2/math/ww.h" +#include "bign_lcl.h" + +/* +******************************************************************************* +Предвычисленные точки +******************************************************************************* +*/ + +#include "pre/bign256_pre_si8.c" + +/* +******************************************************************************* +Кривая +******************************************************************************* +*/ + +static size_t _once; /*< триггер однократности */ +static mt_mtx_t _mtx[1]; /*< мьютекс */ +static bool_t _inited; /*< мьютекс создан? */ +static ec_o* _ec; /*< кривая */ + +static void bign256EcDestroy() +{ + mtMtxLock(_mtx); + bignEcClose(_ec), _ec = 0; + mtMtxUnlock(_mtx); + mtMtxClose(_mtx); +} + +static void bign256EcInit() +{ + ASSERT(!_inited); + // создать мьютекс + if (!mtMtxCreate(_mtx)) + return; + // зарегистрировать деструктор + if (!utilOnExit(bign256EcDestroy)) + { + mtMtxClose(_mtx); + return; + } + _inited = TRUE; +} + +static err_t bign256EcLock(const ec_o** pec) +{ + ASSERT(memIsValid(pec, sizeof(const ec_o*))); + // инициализировать однократно + if (!mtCallOnce(&_once, bign256EcInit) || !_inited) + return ERR_FILE_CREATE; + // заблокировать мьютекс + mtMtxLock(_mtx); + // кривая не создана? + if (_ec == 0) + { + err_t code; + bign_params params[1]; + // загрузить параметры + code = bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.3"); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // создать кривую + code = bignEcCreate(&_ec, params); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // настроить ec->pre + _ec->pre = &_pre; + } + // возвратить кривую + *pec = _ec; + return ERR_OK; +} + +static void bign256EcUnlock() +{ + mtMtxUnlock(_mtx); +} + +/* +******************************************************************************* +Управление ключами +******************************************************************************* +*/ + +err_t bign256KeypairGen(octet privkey[64], octet pubkey[128], gen_i rng, + void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); + bign256EcUnlock(ec); + return code; +} + +err_t bign256KeypairVal(const octet privkey[64], const octet pubkey[128]) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeypairValEc(ec, privkey, pubkey); + bign256EcUnlock(ec); + return code; +} + +err_t bign256PubkeyVal(const octet pubkey[128]) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignPubkeyValEc(ec, pubkey); + bign256EcUnlock(ec); + return code; +} + +err_t bign256PubkeyCalc(octet pubkey[128], const octet privkey[64]) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignPubkeyCalcEc(pubkey, ec, privkey); + bign256EcUnlock(ec); + return code; +} + +err_t bign256DH(octet key[], const octet privkey[64], const octet pubkey[128], + size_t key_len) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignDHEc(key, ec, privkey, pubkey, key_len); + bign256EcUnlock(ec); + return code; +} + +/* +******************************************************************************* +ЭЦП +******************************************************************************* +*/ + +static const octet _oid_der[] = { + 0x06, 0x09, 0x2A, 0x70, 0x00, 0x02, 0x00, 0x22, 0x65, 0x4D, 0x0D, +}; + +err_t bign256Sign(octet sig[96], const octet hash[64], + const octet privkey[64], gen_i rng, void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, + rng_state); + bign256EcUnlock(ec); + return code; +} + +err_t bign256Sign2(octet sig[96], const octet hash[64], + const octet privkey[64], const void* t, size_t t_len) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, + t_len); + bign256EcUnlock(ec); + return code; +} + +err_t bign256Verify(const octet hash[64], const octet sig[96], + const octet pubkey[128]) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); + bign256EcUnlock(ec); + return code; +} + +/* +******************************************************************************* +Транспорт ключа +******************************************************************************* +*/ + +err_t bign256KeyWrap(octet token[], const octet key[], size_t len, + const octet header[16], const octet pubkey[128], gen_i rng, + void* rng_state) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); + bign256EcUnlock(ec); + return code; +} + +err_t bign256KeyUnwrap(octet key[], const octet token[], size_t len, + const octet header[16], const octet privkey[64]) +{ + err_t code; + const ec_o* ec; + code = bign256EcLock(&ec); + ERR_CALL_CHECK(code); + code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); + bign256EcUnlock(ec); + return code; +} diff --git a/src/crypto/bign/pre/bign128_pre_od7.c b/src/crypto/bign/pre/bign128_pre_od7.c new file mode 100644 index 00000000..8787c8c8 --- /dev/null +++ b/src/crypto/bign/pre/bign128_pre_od7.c @@ -0,0 +1,9488 @@ +/* +******************************************************************************* +\file bign128_pre_od7.c +\brief STB 34.101.45 (bign): precomputed tables (l=128, type=od, w=7) +\project bee2 [cryptographic library] +\created 2026.03.05 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +\remark Built with bignPrePrint("1.2.112.0.2.0.34.101.45.3.1", ec_pre_od, 7). +******************************************************************************* +*/ + +static const ec_pre_t _pre = { + ec_pre_od, 7, 37, { + wwH16(00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00), + wwH16(00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00), + wwH16(93,6A,51,04,18,CF,29,1E,52,F6,08,C4,66,39,91,78), + wwH16(5D,83,D6,51,A3,C9,E4,5C,9F,D6,16,FB,3C,FC,F7,6B), + wwH16(4E,82,89,49,85,10,9D,CC,4B,23,1C,2B,0C,06,12,E0), + wwH16(F5,92,2E,66,F0,2C,27,78,FB,EF,F4,CD,D5,8E,C5,37), + wwH16(84,3E,28,32,7B,17,EF,F0,E3,7A,1E,7A,75,69,C1,02), + wwH16(27,31,5F,D3,12,E1,60,41,B9,85,CB,DA,B3,C9,9B,FD), + wwH16(46,F7,12,36,7A,9C,6E,1B,3E,0D,12,11,1F,11,73,CC), + wwH16(BB,FF,9F,00,49,77,F2,B1,C7,F3,6E,1F,54,8F,A0,5E), + wwH16(51,A7,F3,C4,EC,9E,23,30,21,48,3D,28,76,59,82,C7), + wwH16(BE,EE,70,9D,BC,A0,F8,78,F3,69,D3,1B,E3,AB,AA,D1), + wwH16(44,98,5F,25,A8,39,F7,65,4B,6C,5F,25,F2,44,B6,B9), + wwH16(54,7B,34,C2,F9,8A,B0,72,69,20,59,A4,4A,CD,BB,0B), + wwH16(EC,35,91,33,F5,DB,86,76,26,9A,06,05,85,64,CF,30), + wwH16(02,EC,9A,AB,89,9F,F3,42,8B,C1,AD,A1,E3,7E,71,BB), + wwH16(0E,C8,56,F3,9F,1B,47,F8,C9,99,FF,F8,64,73,27,3F), + wwH16(13,69,85,C8,63,2B,22,FE,42,A3,08,48,78,1C,F1,79), + wwH16(6B,51,8D,8E,AE,33,48,D6,D1,D7,48,01,E5,B9,49,19), + wwH16(D2,BC,48,6D,F9,B5,1C,0C,43,DA,A5,56,3F,4B,5D,50), + wwH16(28,37,05,83,A6,0E,8A,22,BC,DE,8D,B6,55,7A,6E,E6), + wwH16(E6,BA,34,11,8A,62,09,46,13,A6,2F,CC,2F,84,4A,11), + wwH16(1B,7C,D7,60,B7,20,C8,FD,41,A3,6B,92,9F,7B,0B,D9), + wwH16(57,3E,BC,FF,E0,7B,47,8A,B1,8A,41,12,D3,93,A5,92), + wwH16(19,47,9D,A8,49,DB,0C,46,2D,9B,04,B8,E1,78,4C,2B), + wwH16(90,14,73,0A,AF,DD,1D,B8,BA,0D,48,43,4C,2C,C2,30), + wwH16(BC,6B,A3,71,D5,C6,AA,33,11,AC,B6,FF,74,CC,42,44), + wwH16(EC,C6,D3,B9,8A,12,30,DA,A9,67,CC,77,BE,23,28,6C), + wwH16(94,B6,73,B1,6E,E8,62,21,9A,86,BE,9D,85,87,1E,75), + wwH16(09,6A,23,0B,F9,0F,A2,46,76,4C,C3,BB,96,B8,8D,84), + wwH16(52,1E,F4,9D,45,72,48,18,51,09,8E,77,1F,60,95,47), + wwH16(9C,A4,75,57,9A,24,59,21,55,22,52,A8,51,8E,87,7B), + wwH16(12,08,B6,12,7A,52,2C,34,05,BE,70,44,B0,57,DA,B2), + wwH16(5D,C2,BE,7B,55,67,7A,86,33,B6,AD,16,47,E6,73,4E), + wwH16(82,91,E1,9C,7A,09,B0,2D,41,BC,F9,D1,78,B7,CB,76), + wwH16(2B,3A,96,19,76,EE,42,B4,04,FD,2C,8B,28,9A,ED,3F), + wwH16(0B,E7,EE,75,D3,FD,F4,E7,36,7E,99,98,FD,18,A7,24), + wwH16(5F,ED,8B,1F,10,60,A9,44,B5,E6,33,AB,87,FE,E8,F5), + wwH16(BD,91,4B,71,C1,B5,5A,B7,A3,BE,54,8B,4E,A9,99,A5), + wwH16(F8,0D,A3,85,6E,A4,34,91,C8,CD,B9,9F,E2,DC,CF,FD), + wwH16(70,26,DD,24,A4,BB,F3,97,9A,9F,5F,C7,AD,D3,94,76), + wwH16(06,A8,AC,6C,29,5A,68,C2,DB,22,F6,79,49,71,6A,00), + wwH16(21,F1,97,80,E8,53,72,DB,79,D7,B5,D2,13,4F,18,35), + wwH16(FC,81,D2,FF,B0,D0,3D,A2,C4,B1,1F,F2,ED,78,FD,F4), + wwH16(AA,01,2F,4A,36,73,DD,EA,06,44,05,B9,31,E7,D3,2F), + wwH16(45,95,27,B8,55,29,8E,EF,C7,DA,B7,11,38,C1,2C,23), + wwH16(C3,21,63,2B,13,B1,05,31,72,F4,66,6F,9D,07,0F,57), + wwH16(A6,66,69,5C,EB,69,CE,47,99,2A,AA,4D,AB,C2,27,B5), + wwH16(D3,18,EA,51,D6,68,28,78,C7,55,FD,0E,99,8A,EB,A1), + wwH16(3B,FF,4B,75,A7,D9,6E,2D,1B,5C,76,12,FA,19,D6,BB), + wwH16(5E,D0,4A,FC,02,05,F8,F2,A8,46,A7,24,AC,83,BC,E5), + wwH16(28,7C,6D,86,E7,42,FD,D8,85,92,CB,CA,13,6A,EB,77), + wwH16(C2,05,2B,D0,BA,02,3D,E2,2E,04,F1,2E,DE,B1,94,EC), + wwH16(64,E8,50,81,89,F2,8E,00,E4,EB,53,C7,4B,B4,95,6C), + wwH16(D5,F4,8C,D0,BB,4A,56,9F,4E,ED,7F,4F,6E,DD,C6,99), + wwH16(14,FD,DF,71,76,35,31,8F,CE,61,98,EB,FB,96,B1,F2), + wwH16(A5,E4,E3,A0,E0,37,6F,45,5C,48,4B,9F,8F,94,B0,5B), + wwH16(FE,70,5D,82,15,02,2B,3A,94,69,D4,79,4D,78,11,AE), + wwH16(84,D9,15,13,1E,C4,8A,1E,D1,0F,40,74,59,46,E5,F4), + wwH16(27,7A,45,9E,C6,6E,2F,8F,0A,DB,D5,FB,EF,3A,E4,CE), + wwH16(72,18,27,AF,C6,67,5C,BD,3B,C9,7B,1B,46,B2,40,BC), + wwH16(2F,9D,09,C2,DA,6B,9F,0F,63,FD,E3,92,7C,03,F8,62), + wwH16(A2,D0,82,BE,6E,1D,99,CC,9F,8F,0D,C5,8D,4C,72,BA), + wwH16(24,D4,D8,CF,41,AB,0E,1E,B4,E5,AD,B4,ED,0F,63,20), + wwH16(33,04,C1,88,2F,8F,55,9C,6A,96,EA,74,4F,2C,FC,5D), + wwH16(21,31,E8,BF,CC,06,E2,0A,08,B4,25,32,E9,69,A6,44), + wwH16(93,DD,23,D1,54,80,EF,77,02,BA,9E,D7,06,D2,AF,27), + wwH16(29,47,DC,C0,C1,AC,DA,99,1E,DE,64,DE,EE,DF,ED,2A), + wwH16(C5,63,05,21,2D,DA,7C,67,BB,B0,E1,2A,D7,82,5C,8C), + wwH16(9D,65,CB,82,FF,73,98,BD,FB,E7,1F,86,9F,B1,B2,0F), + wwH16(75,C4,50,4D,C1,80,DA,BF,95,EA,D4,08,A4,E0,E8,76), + wwH16(CD,2C,5A,1C,F7,0F,E7,62,75,72,B9,A3,EB,77,84,C1), + wwH16(3F,20,7E,18,F9,A9,B2,EA,D8,37,8C,EE,11,16,2F,25), + wwH16(9F,08,89,72,2E,25,77,02,EA,0B,E2,58,40,8C,1C,5F), + wwH16(A2,6B,45,7A,63,50,D8,D2,E2,2B,52,29,5B,B4,01,D3), + wwH16(66,4A,34,1D,D1,5A,60,73,87,4D,76,9F,6D,FD,FB,27), + wwH16(BF,0B,99,3F,EA,3F,40,87,B1,DF,CC,D3,14,19,6C,AA), + wwH16(FD,DA,55,D8,F3,B9,95,E9,4A,63,F6,AD,4B,B4,BB,04), + wwH16(FC,57,AE,60,2F,23,63,EE,4A,13,F9,47,B6,DB,AE,83), + wwH16(1D,EB,A7,5D,78,3F,E3,D0,59,81,BC,44,DE,6E,2B,83), + wwH16(CC,08,FA,E2,8C,EB,CB,68,30,B1,BD,4C,02,AC,6E,4E), + wwH16(C1,C5,37,01,E0,81,A2,4A,D3,45,00,AF,16,E5,42,81), + wwH16(03,19,24,81,14,5E,C6,89,6E,08,55,C4,36,A2,D0,37), + wwH16(48,49,2A,55,90,71,27,ED,14,DE,B7,61,56,A1,FC,37), + wwH16(6F,3E,EE,3F,78,43,C5,5B,21,1F,1F,96,C1,21,CD,62), + wwH16(F7,50,A2,F3,65,4B,1F,B4,C9,C3,55,22,23,87,7A,F1), + wwH16(6A,7E,8C,C3,13,3E,3B,9E,5C,5A,09,81,41,92,A3,F2), + wwH16(6F,9C,15,22,8B,F5,75,41,DF,F5,5B,A2,88,D7,42,2E), + wwH16(76,2A,A1,F4,68,EF,97,9C,B9,E3,5E,1C,06,26,9E,25), + wwH16(56,98,4F,DB,4C,25,BB,64,1D,46,17,08,5C,A3,80,08), + wwH16(4E,30,B0,69,2E,0F,25,86,5E,6F,2F,CF,4F,7D,D4,91), + wwH16(67,6A,68,D7,18,BB,D6,08,80,01,7D,61,D1,6C,7B,4D), + wwH16(42,06,6F,2F,C1,D6,D8,9B,1E,0F,A0,27,1C,3E,C7,2A), + wwH16(3C,A5,DB,45,78,18,20,A1,57,7C,86,39,C1,DC,57,65), + wwH16(02,5F,75,B3,D8,C2,D7,AB,9B,DF,5F,C2,CC,E6,EC,7E), + wwH16(E8,E5,E4,FD,C0,B2,1D,7A,0F,37,F6,24,1D,05,3D,26), + wwH16(F5,F2,AB,2D,79,07,FC,A1,EB,E5,3A,36,38,4D,FE,0F), + wwH16(32,4D,19,45,05,C6,5A,D0,FD,0A,C8,71,BF,B9,97,82), + wwH16(64,4C,1A,17,BE,E0,8B,F6,F8,B5,CC,00,F2,0A,0D,39), + wwH16(C8,B8,0D,F5,52,08,1B,8A,C1,B1,D4,BB,5C,6B,AC,FE), + wwH16(82,C3,D2,BC,4C,AE,D1,26,B3,60,71,26,BF,06,E6,8D), + wwH16(3F,8A,8E,DE,4F,E3,92,64,1E,55,EE,FA,88,22,FD,EE), + wwH16(64,F0,73,7F,73,56,80,4F,20,91,7A,35,5E,A2,AC,C6), + wwH16(BF,9B,9F,A0,3B,CD,9B,7A,4F,7A,5A,DD,6F,75,EB,2E), + wwH16(BB,8F,4F,42,55,8C,69,C5,83,4C,52,56,01,39,1E,A9), + wwH16(0A,CB,35,73,7E,FB,23,FD,E0,F4,42,01,59,3A,0B,8F), + wwH16(A6,3C,73,53,BD,40,BD,A5,AE,E7,9E,DA,C3,F4,CA,3C), + wwH16(23,6D,5D,A4,D2,BB,0F,AD,3D,A3,D7,F8,F2,FA,36,E5), + wwH16(EA,0F,F5,87,9B,8F,CB,98,83,AB,64,34,7C,59,F3,FA), + wwH16(AF,C4,5F,CC,F4,47,2B,21,32,5E,3D,8D,28,DF,67,58), + wwH16(5B,32,B9,D5,70,41,2D,91,39,F7,56,B1,C2,D6,26,BF), + wwH16(E5,AF,81,B6,52,B3,54,CE,E1,5E,D3,12,37,32,64,77), + wwH16(86,53,63,7D,2D,39,57,49,F6,89,68,4A,D0,BB,D1,42), + wwH16(1D,1E,62,73,04,C7,A7,1B,D7,E1,A3,7D,27,07,F2,E9), + wwH16(70,20,D7,77,53,F9,23,EE,B1,10,30,61,57,9A,D6,82), + wwH16(7E,DE,4B,12,3B,B4,C5,74,AD,10,D2,53,05,C9,5F,87), + wwH16(A0,4E,FE,C4,37,6B,19,1D,37,A7,C7,B7,BB,B8,16,47), + wwH16(61,F1,6F,04,6D,54,A5,6D,C0,0B,1C,AA,B4,10,A5,72), + wwH16(7F,86,36,C5,3A,BB,95,9D,FF,22,F1,78,8E,7A,52,26), + wwH16(AD,5D,33,9E,C5,E7,F1,5C,D9,D3,8B,B4,16,54,74,48), + wwH16(E5,4F,30,13,36,F9,A0,D4,E2,36,2B,A5,08,39,8C,B8), + wwH16(5D,99,9A,1D,87,2A,8F,B9,E1,12,0C,85,31,6E,8B,D2), + wwH16(DE,EE,66,CC,40,39,D5,03,F7,66,D0,79,E7,4A,71,20), + wwH16(C4,C9,7B,03,92,88,D9,B7,A9,B6,50,C9,BD,8B,F0,8A), + wwH16(54,B8,EB,A1,FA,FE,F9,00,0B,A1,61,50,43,3F,05,68), + wwH16(89,F5,61,4E,6C,F2,B6,CB,DC,20,44,7A,68,B0,35,6A), + wwH16(9C,EA,97,F9,A6,DF,EC,62,6B,CC,1A,01,A9,DF,02,B7), + wwH16(E1,9E,8A,3C,C7,76,9B,67,C6,30,23,4A,34,BD,9F,68), + wwH16(58,C2,E3,54,80,F1,CB,C7,6C,A0,E5,CD,C9,45,87,55), + wwH16(60,5C,1A,3C,28,95,D8,C3,F4,E3,1F,05,BC,72,B6,46), + wwH16(53,CE,67,DD,8F,6B,2E,6E,40,35,9E,52,37,42,65,2A), + wwH16(64,8B,CC,5B,77,FB,D6,25,29,6B,CB,3A,2B,64,07,E0), + wwH16(BE,AA,91,39,7B,B0,9C,9C,93,38,AF,40,73,39,10,3A), + wwH16(1B,CE,07,49,6F,52,05,98,D0,5E,84,AD,BB,37,D0,7C), + wwH16(84,EC,6D,6F,7F,FB,03,07,FE,BE,4D,26,6A,3D,5B,1A), + wwH16(C1,D7,78,11,34,7D,6C,FC,3A,F2,2F,FB,1C,C6,47,75), + wwH16(A6,32,DB,CB,16,76,D4,E6,2A,EE,97,8F,5C,3A,38,58), + wwH16(47,0C,E6,FD,82,9E,E0,DF,55,58,12,F4,EF,13,3E,EE), + wwH16(4C,51,32,C2,63,C2,92,AE,DC,4E,50,08,BD,62,1E,17), + wwH16(83,45,58,FF,94,53,3A,BE,CD,62,46,C8,53,EB,4B,82), + wwH16(9E,C6,E7,0F,31,E5,44,F3,74,53,68,E0,F2,C3,AB,CD), + wwH16(6F,EA,D8,78,A9,48,EC,8F,F8,BD,BD,B6,96,2F,CF,77), + wwH16(2D,59,7D,99,96,96,03,88,AA,1C,37,AB,14,81,7F,3B), + wwH16(A5,7A,F1,DF,65,8C,59,14,BA,E4,34,2A,6A,16,35,18), + wwH16(A6,3A,F5,53,F1,B9,7D,69,96,0D,04,2E,B0,2E,3C,11), + wwH16(C3,AA,D4,C5,18,02,7A,0C,54,C1,F3,E0,66,D9,BB,E1), + wwH16(BF,60,BD,75,B7,A9,D9,02,6A,B0,6A,66,23,C5,5E,72), + wwH16(12,74,CC,E2,50,82,BF,5E,E7,15,60,7F,9A,A1,D8,DF), + wwH16(84,B5,F7,4E,81,32,3A,8A,32,28,3E,68,FA,2E,04,8D), + wwH16(41,FA,C3,D1,CD,8A,A5,C4,49,C9,F2,0E,EF,B1,86,9E), + wwH16(77,CB,29,F6,71,92,71,C0,74,CE,58,C4,51,1E,26,B6), + wwH16(EF,5A,B2,8C,AC,79,4F,6E,16,D8,65,DC,F9,0B,FF,CD), + wwH16(AE,FB,6C,FC,A4,25,71,21,45,B2,68,00,3B,45,67,45), + wwH16(E2,C5,F8,73,5D,4C,76,C0,7A,F6,CE,C1,61,14,D0,16), + wwH16(06,6E,7E,0A,16,A9,A4,C8,EC,E8,A4,20,7D,8A,77,0E), + wwH16(B6,F1,D9,E6,BC,E7,F4,3D,81,11,FB,05,98,18,46,52), + wwH16(45,74,7A,7C,F2,55,B2,08,C3,DD,AC,BD,EF,52,DF,FE), + wwH16(5F,AC,9D,D3,52,EE,18,52,A3,B3,03,48,D3,53,3A,04), + wwH16(0A,78,BE,70,C7,66,EB,D9,FA,20,9D,A9,DC,7B,A8,CF), + wwH16(96,84,5D,4B,EF,78,02,F7,82,E6,E1,11,00,58,E6,08), + wwH16(49,05,66,4B,CC,BF,5E,3A,C8,CB,B4,E8,7E,E6,9E,C4), + wwH16(08,93,A0,15,73,69,99,F6,AC,98,C0,61,ED,0B,22,BB), + wwH16(65,97,D1,AA,7D,05,4C,F2,C2,4E,8A,79,1F,48,29,89), + wwH16(43,21,27,6D,1B,4B,95,93,67,A8,6E,A8,A2,53,5B,71), + wwH16(7F,B6,70,5B,0E,5C,F8,88,81,F9,F7,72,23,74,11,C1), + wwH16(4C,F8,58,74,E8,41,B5,74,FF,5B,9F,62,88,0B,DF,1D), + wwH16(33,23,06,BA,41,8C,D2,75,1B,8F,3B,78,E0,7E,C5,3D), + wwH16(DB,84,76,19,84,5E,61,C5,55,F9,98,45,F3,F8,26,47), + wwH16(A6,BE,4A,BB,30,00,DD,A4,1E,79,2E,C8,6D,5D,B7,F5), + wwH16(AA,3B,51,28,21,14,F4,F4,81,47,46,73,F0,92,04,8A), + wwH16(16,CE,98,E7,F4,6B,D5,17,5F,AD,2C,B8,93,2A,1C,73), + wwH16(2B,AD,E3,96,40,46,94,73,27,94,15,B9,C3,15,6C,01), + wwH16(BF,B8,04,08,A3,58,00,28,2C,91,92,7D,CF,CA,66,FD), + wwH16(B3,12,16,AF,F5,94,E5,84,7A,B1,24,30,D4,31,D2,4A), + wwH16(3A,12,01,4F,3C,24,B8,D3,D1,89,4A,C7,03,3F,03,13), + wwH16(B8,2F,32,A7,A7,C8,CE,48,0D,C5,E8,B5,9D,2C,91,EC), + wwH16(E8,06,43,CA,CE,81,F5,99,5B,9B,D2,3E,B3,05,A4,56), + wwH16(95,72,3E,C2,9B,64,74,37,64,65,9A,55,9E,01,AD,E9), + wwH16(57,9F,C1,5F,15,E6,8A,A2,9C,54,35,59,68,A9,52,D0), + wwH16(48,40,CD,FE,57,90,A2,70,EB,A4,6E,E1,52,BC,1C,26), + wwH16(3C,EB,BC,DA,D7,98,85,F8,E8,67,CB,EF,AB,97,FD,17), + wwH16(EF,90,69,01,60,9D,D0,96,D3,0C,A3,A3,8C,E2,15,91), + wwH16(C7,63,67,62,4B,EA,8F,CD,B4,D9,E0,26,4A,59,AE,67), + wwH16(62,2C,7D,92,83,91,73,E8,DA,D3,80,94,8F,3F,43,2D), + wwH16(1B,DB,99,02,46,68,DE,FD,E8,C7,BA,17,EA,91,77,4B), + wwH16(61,E8,AF,90,5D,81,B1,DF,E3,25,A8,36,77,3D,A7,69), + wwH16(F9,D5,33,8B,70,49,5F,B2,8B,84,E4,D2,60,0F,A2,01), + wwH16(EA,3C,D5,84,85,90,AA,23,90,DA,85,E6,79,4B,9C,41), + wwH16(77,2B,EB,BB,4C,6A,FA,4B,AF,28,37,F7,04,3A,48,EF), + wwH16(18,BC,F7,8B,22,46,52,FC,0D,FD,BA,D9,14,D2,88,31), + wwH16(C4,48,27,80,97,3A,F9,6F,F1,C9,07,41,70,7A,24,83), + wwH16(B4,A6,FE,E4,92,DF,7F,1F,59,74,42,B4,9B,43,0B,85), + wwH16(C6,F9,3E,BB,D1,BB,8C,14,D5,13,1B,CF,01,B6,AE,BA), + wwH16(3A,E3,51,B9,73,F0,4D,7B,F2,58,25,F9,B2,06,AF,24), + wwH16(5B,34,62,2E,ED,02,2E,57,9B,04,A4,50,F7,95,5C,F6), + wwH16(DF,35,45,FA,DF,08,E8,ED,80,3A,C4,4B,AF,EE,D7,3D), + wwH16(63,A5,44,AF,9F,28,F5,68,FB,F3,8B,77,19,F1,77,11), + wwH16(AA,F4,DF,80,20,76,86,F8,D8,90,77,6F,2F,CC,35,AD), + wwH16(FB,47,F5,3D,28,7E,7D,78,C1,54,F0,E6,C5,5E,57,57), + wwH16(87,04,8B,E4,88,04,EC,42,55,76,BB,69,83,2C,9C,F9), + wwH16(54,7E,10,2A,FE,CF,BA,89,E8,8C,C3,0F,53,51,E7,31), + wwH16(C1,22,78,49,D0,B7,E5,53,99,EB,7E,0A,71,64,13,48), + wwH16(BA,4E,97,D6,90,75,CA,91,A2,A5,70,0E,8D,ED,90,4E), + wwH16(AC,62,72,FE,7B,0F,19,49,34,FA,40,BB,D3,44,6C,2B), + wwH16(13,A0,7E,AF,D3,8E,4B,E7,48,DD,6A,95,40,F7,23,DC), + wwH16(CC,5E,4A,6B,E3,89,48,43,5D,D4,2A,3A,6C,0F,69,7B), + wwH16(DC,73,F3,1F,72,FA,AD,55,27,E9,D6,73,E6,99,C6,45), + wwH16(45,3C,73,CD,78,EF,BA,3E,BD,7E,A2,43,F5,26,5B,73), + wwH16(B3,1B,E5,70,9E,99,87,6E,11,87,B3,E5,0C,17,60,AD), + wwH16(76,EC,57,F1,5C,50,6A,69,E5,4D,D0,6B,A6,8F,73,E5), + wwH16(28,B4,48,2C,75,1E,4A,F5,AA,00,28,53,51,80,CA,7B), + wwH16(D0,3A,A4,E4,54,61,24,41,BF,F5,3F,2F,91,DE,69,D6), + wwH16(29,DB,A2,32,74,45,03,19,0F,27,EA,CB,65,0F,15,63), + wwH16(1A,9E,D9,91,04,1E,AF,DC,6D,53,E2,BA,84,AD,62,40), + wwH16(D3,57,0B,EA,79,37,88,1A,47,45,EC,54,CB,D1,26,24), + wwH16(68,D8,D5,A0,20,6B,6D,21,D6,C5,CC,E9,2F,32,B3,99), + wwH16(E7,C7,E3,B2,11,11,7D,13,59,CD,2D,5D,2B,27,78,3F), + wwH16(5D,AA,24,17,9F,CE,36,08,5D,C5,92,1B,4D,A6,9D,23), + wwH16(CB,2B,65,10,5C,C5,F2,68,E8,00,E6,FD,48,31,BD,77), + wwH16(DB,E3,AB,93,A7,7D,CB,00,B0,49,8F,C7,C0,B3,71,A4), + wwH16(62,2B,07,A7,51,2A,65,18,94,4E,A5,00,56,22,51,EF), + wwH16(7F,53,16,57,EE,E1,D6,CB,DB,CF,86,33,35,3C,B8,39), + wwH16(E2,61,06,F2,53,AE,82,3D,19,64,47,05,DD,A2,79,B9), + wwH16(DC,8A,1A,81,5A,1C,45,F5,B1,2A,3A,62,08,E3,96,72), + wwH16(75,C7,6D,E6,2A,DB,CE,A0,F4,71,C5,6E,10,CD,BC,0A), + wwH16(F2,5D,67,A9,BB,32,F1,0B,D6,F9,BE,4C,1B,6E,90,0F), + wwH16(D4,CE,50,48,13,FF,08,9E,AF,4F,1F,89,C6,0D,3A,57), + wwH16(4F,5F,A3,81,8B,3C,57,63,78,87,9F,69,3F,1F,9C,09), + wwH16(DF,14,EF,FD,B4,70,76,48,05,F8,41,09,01,C3,4F,90), + wwH16(37,06,8B,30,2C,F5,DE,04,B7,3B,1B,1F,02,21,30,AE), + wwH16(32,A0,D8,11,6D,5F,80,56,E2,3C,F2,02,F6,28,22,CA), + wwH16(70,69,3C,04,AA,2A,BB,84,44,11,04,F1,25,6B,08,D7), + wwH16(91,4C,57,5F,FF,F9,07,6C,12,94,B0,A1,95,F4,DD,B0), + wwH16(59,F2,FD,7F,FE,2D,A6,66,40,DB,A6,AA,D8,17,8A,84), + wwH16(CE,83,3E,72,AC,2F,A2,37,E6,2F,F1,25,F3,33,2A,D4), + wwH16(B8,D3,09,9F,FC,49,BF,CE,54,05,96,F5,DC,DE,2C,FA), + wwH16(C5,CB,FA,80,CE,69,EC,3F,FB,C9,17,99,AE,53,CC,80), + wwH16(32,23,C3,EC,A6,B2,CE,FA,84,C7,05,59,C9,46,4D,28), + wwH16(34,E9,72,1E,82,E5,A2,61,0A,3C,41,3B,0B,FC,FF,6D), + wwH16(06,66,D8,F1,B4,26,12,0B,C5,40,EF,51,0B,91,A0,07), + wwH16(B6,1A,DA,D7,1A,84,4C,EB,0D,0A,09,FB,8D,46,AE,B7), + wwH16(24,2F,3A,02,EF,68,7B,46,0F,83,14,35,B3,E2,AF,0D), + wwH16(1B,AC,FF,F1,33,24,51,B2,59,96,95,B1,95,29,B3,CA), + wwH16(4D,B8,68,F9,D1,67,3A,EA,48,7F,A3,44,52,9B,EB,B2), + wwH16(D6,94,B0,23,3E,EF,07,44,B7,C5,82,0C,76,40,AC,86), + wwH16(AB,62,27,8C,6E,BC,B3,2A,1B,27,84,E2,47,41,F7,D7), + wwH16(A0,C5,43,06,94,64,1A,A9,FE,00,0A,5F,2E,D9,62,81), + wwH16(E9,CC,73,9A,37,4D,B1,92,29,2C,0E,DC,7A,5E,8E,3C), + wwH16(84,A9,48,C0,0D,0B,F2,A5,BC,9B,3D,0B,D6,87,B0,8D), + wwH16(CD,C3,E9,D7,64,74,B2,D2,26,1F,2B,8E,A7,1B,80,2A), + wwH16(6B,9C,A2,A0,52,86,D2,D2,A2,70,10,60,2A,11,FB,FE), + wwH16(C9,26,8B,0B,4C,D6,77,56,F4,5F,F1,AA,DB,4E,0D,6F), + wwH16(2C,9D,E6,52,9D,BD,49,AD,04,43,31,09,FD,6A,EC,3E), + wwH16(2C,3E,12,34,76,34,AE,E3,E7,C7,DA,32,4F,09,94,FE), + wwH16(85,0F,A3,30,C0,19,0E,6A,D9,6D,B2,11,B9,26,5C,6E), + wwH16(EA,9E,63,CE,3A,C4,57,39,91,4E,D6,55,03,B0,62,EF), + wwH16(37,EC,9E,6B,43,05,21,BF,BE,0C,B2,43,32,6E,5B,7B), + wwH16(29,2C,D7,68,CF,64,DA,64,F1,5A,9E,CA,2E,12,F1,58), + wwH16(67,99,E6,00,72,40,B6,1C,4B,D3,A4,25,AA,1D,8A,71), + wwH16(A5,17,EA,95,C3,6E,05,1B,B4,33,42,08,0C,AC,5D,94), + wwH16(BC,80,A1,6E,D3,64,40,87,32,DF,4D,76,66,E6,E0,1C), + wwH16(A4,FB,1C,3A,B0,39,C9,7C,8D,54,22,31,CA,40,EF,E5), + wwH16(9D,E3,BA,45,BB,56,65,B3,18,2B,96,21,FE,05,3A,1A), + wwH16(19,D0,CE,3A,52,B4,A4,26,FA,28,14,89,F2,1A,37,71), + wwH16(53,2A,79,26,D9,C1,AE,2C,84,89,EF,3D,44,81,84,B4), + wwH16(6C,DE,9E,E8,51,2A,A4,5B,69,44,C1,CE,2B,1F,42,53), + wwH16(5D,65,68,69,5F,38,47,49,16,09,58,98,B1,C7,00,C4), + wwH16(1D,DB,D0,2C,E2,6F,E2,7D,D0,77,10,F2,51,99,64,F8), + wwH16(66,76,86,CF,74,E2,3E,9C,09,D4,25,AF,0A,59,CC,FD), + wwH16(11,14,D9,1C,78,A3,41,B7,FE,9C,F5,44,25,CE,E6,3B), + wwH16(CD,E8,D7,2B,48,AB,62,12,81,10,63,2E,AF,49,7E,40), + wwH16(50,74,C4,F7,0F,F6,68,17,4F,9B,3C,11,3E,FA,95,27), + wwH16(8D,40,72,46,3D,FB,FC,6F,B3,7D,9A,7C,E9,DF,88,17), + wwH16(0E,AE,F9,67,51,A0,00,EE,04,95,90,2C,00,76,CB,CC), + wwH16(0D,8D,BB,C9,2A,20,35,41,70,8A,07,EF,4E,6E,A9,35), + wwH16(95,07,3E,F3,09,68,88,53,CC,51,1B,9D,DC,CB,52,D6), + wwH16(91,8C,D8,DC,98,6B,3C,22,1F,79,1D,12,11,FD,CE,3E), + wwH16(49,A1,50,1C,5A,B8,F8,BD,BA,56,0D,81,6E,84,F0,46), + wwH16(8B,B1,E4,35,73,AC,17,0E,6D,6B,E6,7E,42,F8,C6,3C), + wwH16(A9,74,9E,D0,4F,A2,C0,67,1F,61,A7,96,2F,61,9B,9C), + wwH16(8E,03,E4,3F,0A,C3,94,E6,82,86,FC,52,8D,81,C3,78), + wwH16(42,29,53,42,F7,5D,4B,D2,75,66,E8,D8,C6,88,50,4D), + wwH16(DE,3F,D0,DF,9D,52,38,EE,31,9C,B0,EC,64,43,57,07), + wwH16(FA,21,37,4E,3B,6E,C2,A1,96,E2,1F,95,5A,07,52,27), + wwH16(00,B9,C3,86,3C,FF,7A,ED,F0,22,59,94,1F,F2,3B,42), + wwH16(64,D3,B0,97,DC,1A,31,6C,BC,A8,E0,48,3F,9E,B3,07), + wwH16(23,81,DA,E0,63,74,CB,70,EC,B0,6E,4E,B4,85,48,7F), + wwH16(D6,5E,EA,F9,7E,DC,24,29,88,15,87,4E,47,35,4D,97), + wwH16(AB,A2,0C,18,EC,7F,0B,1C,07,2F,A2,E6,C0,7F,59,AE), + wwH16(06,68,3C,F1,41,CC,3F,34,78,A3,4C,C9,67,7F,0A,40), + wwH16(C5,F3,AE,09,61,32,50,4F,5B,B3,6E,DD,F3,BD,B6,AA), + wwH16(06,9B,81,3B,A1,9B,DC,00,19,34,AC,5F,42,79,C3,0E), + wwH16(08,7D,F4,2A,DD,FD,0B,C9,43,A7,9C,C1,72,32,AA,C3), + wwH16(66,D6,18,EB,22,EF,87,EA,02,23,49,FA,16,F7,4E,2D), + wwH16(25,D9,CE,49,CF,C1,5E,78,B1,56,10,66,59,3A,7E,23), + wwH16(4B,7A,BC,41,AF,8F,09,97,C2,75,06,1D,94,C9,14,03), + wwH16(35,5F,C2,0F,26,8B,01,7E,B5,83,2B,B5,3D,F5,16,79), + wwH16(15,68,FA,3C,9A,92,DF,95,E2,A9,39,4C,4A,BD,5C,FA), + wwH16(81,8F,9F,EC,E0,1C,61,7F,6E,08,9E,24,8C,8E,C1,95), + wwH16(93,0B,47,19,D6,02,E3,91,3A,FA,DB,13,45,7B,68,A6), + wwH16(26,7E,39,EC,B0,5D,78,C8,D0,CD,28,8B,85,47,C2,53), + wwH16(64,0A,9D,B7,24,E0,A1,F6,D1,B8,BE,1C,86,99,BA,9B), + wwH16(C3,BA,D0,F6,0E,9D,45,21,EE,FF,AB,5B,85,1F,7A,41), + wwH16(6F,CF,24,79,D0,C3,AD,58,F9,E2,23,13,14,23,B3,83), + wwH16(67,FE,E4,92,F0,F8,A0,86,50,2D,0B,18,B7,6C,5E,08), + wwH16(9F,89,41,1D,38,4A,C7,77,5E,5E,E7,07,6A,65,82,F0), + wwH16(C3,C1,17,51,D8,7F,39,95,44,39,58,8E,89,4A,09,38), + wwH16(84,AC,4B,77,DB,49,BB,14,8B,80,21,13,9B,4A,31,41), + wwH16(31,90,EE,90,D5,A8,C4,32,D2,7A,16,D8,D0,5E,44,A2), + wwH16(4A,C4,14,95,7F,A1,98,3F,03,90,93,F3,9A,43,3F,FA), + wwH16(D0,43,F8,67,D8,B3,5D,68,28,27,5F,75,7C,BF,A6,8D), + wwH16(0F,B7,66,1E,4D,76,39,9F,C8,85,5B,71,D9,E3,AE,53), + wwH16(AF,84,59,E4,88,00,E3,B2,99,A1,EB,3E,9D,F9,48,64), + wwH16(E1,3A,5E,12,54,F4,1D,65,BC,9B,65,7F,58,22,D7,93), + wwH16(81,A1,CD,5F,5F,FA,D4,FB,E4,2A,40,1A,FF,9A,CE,CF), + wwH16(DC,8E,C4,69,4A,26,18,68,0B,2F,D7,F9,B3,E6,81,4C), + wwH16(0B,9B,79,BB,27,41,83,97,F4,F3,33,7C,66,FC,F8,B9), + wwH16(52,DA,ED,DB,B6,4A,04,25,BE,25,BF,CD,2B,50,82,61), + wwH16(C3,EA,DC,9F,69,04,43,B1,B1,2E,7B,62,AA,B5,AA,6D), + wwH16(70,56,95,18,FE,59,8A,29,23,91,0A,63,F5,1B,EA,E8), + wwH16(4F,EB,88,63,CD,4F,2E,72,2E,F4,F0,BE,2A,05,D7,CF), + wwH16(4C,02,7E,39,33,B4,6B,B2,15,C5,9A,03,F1,F0,40,76), + wwH16(DA,F7,16,40,6A,30,6E,43,75,C4,D8,66,EC,BA,7C,59), + wwH16(49,D5,4F,40,A0,71,ED,B6,5B,A3,4D,DD,4F,4E,91,06), + wwH16(73,3E,62,A8,17,9A,E6,35,A7,22,1C,5A,7E,F5,AD,2D), + wwH16(CA,93,08,D9,BE,65,86,1B,F9,E0,4C,13,09,66,75,54), + wwH16(BA,08,56,4B,11,75,31,68,0D,9D,E2,3A,78,72,2E,BE), + wwH16(DD,B3,3A,3C,AB,2F,0A,0B,1D,49,D1,F6,13,2B,B4,3D), + wwH16(73,B9,37,38,D3,62,B5,A3,6D,1B,91,7D,56,36,0C,B8), + wwH16(BB,7B,A6,4A,18,C2,BC,87,59,6A,D4,77,41,EA,1A,4A), + wwH16(63,AE,FB,33,A1,79,FA,2B,97,07,98,82,F3,8F,C7,06), + wwH16(62,43,49,52,EF,05,55,98,0D,D6,68,E4,E3,F0,64,30), + wwH16(32,C1,87,EB,B3,7B,74,04,C2,0B,C4,71,A7,84,24,6C), + wwH16(F9,92,17,F0,18,57,B6,B9,E6,74,F0,14,33,9A,B6,05), + wwH16(6E,CC,50,7B,8A,58,44,BD,BE,EE,2F,A6,5F,E2,76,F4), + wwH16(EF,57,A8,55,15,BA,80,24,9C,F2,CF,B6,1C,DA,96,42), + wwH16(D7,E1,DB,70,8C,CB,70,16,D3,09,85,33,AB,97,EF,7A), + wwH16(9E,1E,18,4C,26,59,A9,07,F6,3B,A1,24,A5,F9,F1,C6), + wwH16(9B,25,69,27,4D,07,AD,33,12,9F,F6,06,74,32,F8,A1), + wwH16(8E,4A,0F,75,16,A9,A5,89,10,D3,07,DC,69,55,B5,1A), + wwH16(09,23,64,CE,77,70,51,ED,65,0D,1C,C1,60,58,F5,89), + wwH16(D0,F1,62,2A,DE,19,6E,A1,37,9D,97,43,57,DF,77,FF), + wwH16(9F,05,B5,94,41,5E,78,4F,C9,57,7E,6C,0C,D9,5B,C8), + wwH16(43,9C,FE,07,1D,B6,2A,7F,AD,CB,BD,A5,A3,68,55,68), + wwH16(36,A2,5D,92,6D,64,43,36,A8,B3,4D,78,FE,43,BD,F1), + wwH16(A0,C9,4A,0C,47,CC,8A,D0,7E,B5,A3,2B,6F,E3,8C,02), + wwH16(FC,B8,B2,D4,81,2A,A7,7E,5A,A2,60,62,C6,4D,0D,07), + wwH16(77,3E,7C,F1,3E,E5,E3,BA,56,1F,B9,11,18,03,73,3C), + wwH16(9D,02,94,26,DF,18,07,36,53,47,E4,C6,55,14,CA,B7), + wwH16(DD,87,B0,3C,AC,B4,9F,B0,23,C3,5E,99,CD,F1,71,D2), + wwH16(06,B2,C0,33,B4,21,11,3E,74,40,BB,4F,D3,96,0D,B4), + wwH16(2B,6A,C9,2E,E8,ED,D1,A3,1D,E1,94,17,DB,2B,28,4A), + wwH16(05,14,13,A0,2F,10,01,51,92,8E,42,2E,59,13,C6,1D), + wwH16(A8,98,58,AE,E6,62,A3,C8,E2,F8,1D,48,68,65,FE,6C), + wwH16(26,F4,9E,FC,A9,C3,6F,B6,CA,93,68,78,45,A4,88,55), + wwH16(25,30,77,16,17,76,B0,9E,CD,1A,BA,53,C6,CC,38,32), + wwH16(CC,7D,27,5F,AE,9A,74,93,6A,E9,62,C9,D6,74,E1,60), + wwH16(6B,6E,31,67,FF,D6,4B,D8,5F,83,7D,9A,74,D5,BE,30), + wwH16(84,5E,29,50,65,AE,1E,B8,28,D4,6C,73,38,28,EB,C6), + wwH16(39,A5,A7,50,6F,A3,EE,97,07,06,C3,59,9C,A2,C3,AC), + wwH16(98,72,BD,1F,12,85,9E,E6,39,03,35,F1,D9,0C,2C,90), + wwH16(E5,ED,65,9C,63,33,B3,CC,8E,10,97,0D,D6,C3,B7,1B), + wwH16(60,3A,75,21,B4,06,EB,C7,53,65,EB,D9,0F,6F,27,A3), + wwH16(C5,E5,8F,DB,3A,31,BD,67,4D,63,8B,83,66,2C,D9,37), + wwH16(2D,55,F5,13,60,E9,1E,27,26,18,AC,3C,C0,DE,95,B0), + wwH16(EF,D8,59,5E,83,C6,54,76,18,9E,22,0F,AA,D2,90,FC), + wwH16(15,45,9F,26,57,F8,E4,59,71,B5,B9,A0,31,A5,E5,6D), + wwH16(2D,85,37,7F,EB,40,DD,A8,4B,86,72,28,70,DB,DE,BB), + wwH16(D5,C0,D1,4A,C5,17,8F,56,66,29,91,36,D2,33,2F,D0), + wwH16(D1,A8,98,AE,77,C5,D7,73,B7,E6,2E,C4,01,74,08,D1), + wwH16(BB,74,71,6D,2E,00,B4,7D,BB,95,56,CB,76,0B,58,A7), + wwH16(62,33,C2,E1,DD,50,1F,B9,05,0E,63,3A,5B,CC,C2,19), + wwH16(B5,16,BA,EA,E7,A6,3A,32,DA,6B,9B,92,19,CA,D9,06), + wwH16(A1,EC,CF,01,1D,DD,46,82,B4,49,97,EB,32,A6,CB,1D), + wwH16(78,36,D6,88,A3,0F,D4,D6,37,A1,41,7D,53,47,FF,5D), + wwH16(C4,26,60,57,4F,09,95,1D,68,7F,7D,94,92,93,4C,0C), + wwH16(B9,4A,06,E2,40,49,0E,AE,39,18,EE,0D,AA,9C,26,50), + wwH16(71,00,9D,93,66,E3,19,3B,A1,A4,11,E3,E5,BC,A5,ED), + wwH16(A6,D2,44,64,21,5D,37,B3,F7,BB,EC,AF,6D,74,1D,27), + wwH16(47,DB,9B,90,C2,28,5D,8D,CC,2D,9F,F1,D0,F1,3F,0E), + wwH16(98,37,97,3F,D0,EE,23,5E,2F,27,F1,8E,6B,94,7D,EC), + wwH16(33,B5,CE,85,F2,81,62,01,DE,1D,7A,91,5F,91,99,83), + wwH16(8E,12,A0,E9,90,BE,90,F8,3D,0B,F9,0D,B0,65,08,50), + wwH16(70,5A,00,B2,DD,9B,69,CA,6B,15,7D,3E,7B,7F,83,EC), + wwH16(42,15,0A,5B,5A,43,B5,68,49,DD,63,D3,A1,BD,BC,81), + wwH16(39,40,13,E6,94,CE,E3,FB,B7,8F,6F,7D,CE,0F,C8,92), + wwH16(47,68,FB,AE,69,9E,1E,FC,2D,53,90,1D,43,74,BD,D5), + wwH16(44,07,00,B4,87,37,79,3A,5B,0B,76,02,95,71,19,D7), + wwH16(22,A5,18,6B,D7,4A,4E,EC,37,2A,C3,06,40,58,17,DB), + wwH16(F1,48,51,F9,71,4F,ED,AA,A1,2C,70,92,E1,5B,3C,05), + wwH16(CE,62,A9,0A,1B,97,B4,75,0E,32,D8,CA,C7,2A,9D,85), + wwH16(DE,3F,72,05,A9,FA,A0,82,DD,46,0E,38,DD,7E,FF,44), + wwH16(C1,E9,2E,C7,16,E2,BF,AD,6E,4C,84,52,B3,54,69,65), + wwH16(AE,E9,32,2F,B7,6B,03,F5,F4,DE,33,BF,02,CC,74,37), + wwH16(F7,27,6E,62,E0,BC,9D,02,F1,A7,7F,B5,5A,49,F2,5F), + wwH16(76,B4,6F,1A,E6,13,12,63,67,EB,37,B5,67,A2,4B,4E), + wwH16(9C,6E,B2,FD,F1,B4,37,C8,22,71,D9,8E,90,CB,58,DD), + wwH16(26,BD,10,E8,CE,1D,D8,CD,45,C8,9B,8E,08,C9,28,2B), + wwH16(BD,8B,C7,AF,B1,37,F2,73,FD,69,C2,1F,21,CB,07,8E), + wwH16(93,E6,37,37,0E,4B,0B,14,04,30,86,3A,D6,0B,1B,AD), + wwH16(88,5B,E6,CB,0D,19,45,42,83,49,B7,62,B6,19,8F,59), + wwH16(3A,19,09,83,99,5A,A9,CE,44,38,13,12,EC,7B,D0,82), + wwH16(A6,53,9D,2F,1A,FB,F2,F6,44,B3,65,83,CF,31,CA,40), + wwH16(95,56,EE,C0,AF,F9,D9,5E,13,AB,1E,CD,34,44,2A,FA), + wwH16(70,D4,30,07,18,61,6D,2D,25,F2,6E,01,60,69,D4,83), + wwH16(C8,4B,F0,AE,97,BF,4B,0E,2B,6A,AE,4D,48,89,B9,48), + wwH16(06,77,24,68,46,27,BB,DB,CD,B8,6D,A2,EA,E1,62,26), + wwH16(C4,6C,91,62,72,7F,AF,97,2A,21,E2,37,8E,19,52,77), + wwH16(3D,F6,E9,25,AA,BE,0D,95,D0,93,BE,04,AB,4D,4C,2F), + wwH16(8A,15,A3,59,0F,87,35,77,E5,57,F0,9B,54,97,54,DA), + wwH16(A2,C8,C9,0B,96,64,39,88,24,C3,90,03,CB,86,6B,74), + wwH16(CC,CC,92,70,67,09,E4,32,EA,1D,92,72,84,61,A6,34), + wwH16(C3,37,DB,F0,85,3C,5B,9C,E7,C2,68,4F,48,60,55,E6), + wwH16(CD,D7,CD,9E,9E,F6,68,6C,59,7F,F8,21,0E,08,A2,70), + wwH16(50,26,B0,FA,95,06,FE,44,A7,09,37,02,16,FE,54,EB), + wwH16(E0,19,A2,85,FE,7C,2C,A0,DD,39,2D,66,B8,8A,7B,BE), + wwH16(8D,0A,D7,9B,86,F2,01,44,DB,34,35,C8,3C,FB,47,FE), + wwH16(26,AD,15,13,1C,4F,C8,AF,42,90,6F,5A,40,CE,39,50), + wwH16(11,9C,B4,40,AF,CD,1B,C3,0A,A5,7B,E6,51,6C,F8,C2), + wwH16(9C,CF,69,DC,E2,C6,C1,7D,F7,37,B0,7F,27,D8,89,4A), + wwH16(36,54,67,D1,AA,CC,6D,C5,76,B1,94,A1,BD,7B,93,3B), + wwH16(E4,38,1A,74,5C,9B,8F,00,2B,8D,BA,3C,6F,77,6C,3C), + wwH16(9B,5D,6D,78,22,BA,29,58,51,01,7C,57,32,66,B0,F5), + wwH16(3F,48,EC,12,18,88,FC,E7,89,05,EE,6A,94,8B,A9,74), + wwH16(F5,CF,1D,3B,68,E2,21,94,CE,82,32,65,2A,B8,90,38), + wwH16(1A,7A,7C,F3,1C,91,2C,57,59,07,5C,94,F8,7F,7A,51), + wwH16(D9,AE,CD,2D,CF,50,9C,F9,71,88,95,B2,FC,09,A5,EC), + wwH16(7D,C3,46,E6,CA,F9,87,E6,48,8A,80,34,0F,8A,29,98), + wwH16(86,DC,BA,58,00,17,87,B8,AB,5C,3A,83,9E,23,5B,E7), + wwH16(ED,CF,AE,BD,81,9D,9E,A5,2C,97,49,43,CF,1E,1E,6B), + wwH16(09,00,A6,99,E0,97,E7,4B,8D,87,DF,49,08,7E,40,A6), + wwH16(4D,3F,71,B7,57,8C,51,B8,B4,8B,24,D4,2C,79,7E,35), + wwH16(01,68,8D,20,B6,08,D0,EE,FC,3F,B8,78,3D,F4,7D,E4), + wwH16(DC,8E,75,63,2A,FB,49,AF,CB,F4,A6,E2,14,35,37,46), + wwH16(1F,D9,8A,BC,F0,D2,A9,54,2B,CB,F6,40,AF,B6,96,00), + wwH16(03,CA,38,4D,CD,2F,57,F6,A8,78,76,5C,DF,C9,F1,77), + wwH16(85,09,9B,0A,74,A6,C3,3C,8E,75,78,F1,43,A3,6B,51), + wwH16(98,CC,F9,14,D9,36,30,CA,8F,D2,A8,FD,7D,D3,0C,C4), + wwH16(4A,84,9E,4C,8B,51,5F,D1,12,0E,0B,F7,F3,2F,94,AC), + wwH16(B0,2D,34,A2,B2,08,58,6C,DB,B5,DD,45,1B,E3,3E,C6), + wwH16(B4,50,52,CA,60,C2,91,04,87,91,E6,67,C6,D8,C9,7F), + wwH16(32,3B,7F,CE,87,4B,2F,2D,B2,63,30,10,3E,D5,53,F5), + wwH16(33,04,9F,8C,70,80,0E,8D,07,E4,C2,27,8D,88,67,FC), + wwH16(EB,0D,0D,B6,AC,0A,EB,48,77,E6,D6,21,4F,A3,0B,B0), + wwH16(31,A0,A1,F6,8D,20,52,E7,EF,8C,74,97,CB,ED,72,50), + wwH16(EF,57,7C,FA,DE,8A,A6,7E,CF,7A,3D,C6,5B,A0,E0,58), + wwH16(6B,48,BB,93,28,08,22,37,65,31,0F,CF,55,B6,B5,16), + wwH16(E8,B8,E8,26,FB,80,53,5F,4C,95,A1,65,6A,3C,7E,39), + wwH16(64,E5,10,84,CA,AB,25,ED,60,F5,C0,E9,FE,77,87,65), + wwH16(42,AF,0D,E2,EB,46,B2,AC,21,C8,EE,96,C6,9E,2D,0C), + wwH16(FC,9A,BA,E1,75,A0,BD,C4,BD,79,24,8F,18,0F,0E,41), + wwH16(9B,52,DE,34,C3,C5,27,42,D3,A7,69,D3,F3,3F,EB,DC), + wwH16(56,C5,6B,A1,07,7A,CA,10,CC,8A,34,51,1D,4C,CA,D9), + wwH16(D0,64,60,ED,18,92,31,9F,94,4E,C9,50,D2,A8,DF,0E), + wwH16(86,7B,6E,C0,47,55,5F,2A,2B,37,20,BC,DC,CA,AD,50), + wwH16(5E,12,A7,93,21,F3,23,98,89,C5,DA,37,25,49,BD,25), + wwH16(15,D9,DF,E6,6D,D6,B0,37,B1,70,F1,EE,51,E8,5D,2A), + wwH16(FB,8A,C5,C0,C6,0D,F2,95,0A,CA,90,E6,FE,8A,B3,94), + wwH16(AC,FA,73,91,8B,B9,38,19,F8,86,DD,08,EC,4C,11,38), + wwH16(B6,72,6D,1F,36,42,27,B3,D0,6F,07,1F,03,F4,29,62), + wwH16(FA,18,7C,15,EC,77,97,91,DE,1A,19,52,85,DD,4B,84), + wwH16(AB,50,41,94,03,3E,9E,0A,CC,8F,99,E1,09,B8,F2,46), + wwH16(4D,F6,D1,28,70,AF,AC,ED,6A,B9,DA,A4,76,F1,AB,6D), + wwH16(05,5A,DB,2E,E2,AC,FF,25,B8,1B,AD,BE,01,1A,20,66), + wwH16(BE,FF,F0,96,31,EF,08,99,34,06,F1,DC,B0,4A,E6,AD), + wwH16(61,5D,15,BF,A5,96,4E,E5,A4,1F,21,87,76,1A,EE,56), + wwH16(44,75,99,A0,B5,5A,5B,7B,A5,D5,AC,44,B7,BB,8A,A2), + wwH16(93,61,57,B1,CB,45,F9,ED,CE,5A,41,F1,EC,DD,E5,5F), + wwH16(F8,52,2B,E5,6B,63,A4,17,90,15,F0,BA,2C,EB,1A,47), + wwH16(C7,03,06,6D,6A,2F,38,BF,68,48,CC,48,08,54,70,83), + wwH16(CA,E3,A4,F5,2C,80,FE,3D,CE,B1,E4,1D,42,E9,1E,86), + wwH16(B6,83,88,1D,ED,FD,45,44,2A,70,2A,8F,13,18,4F,56), + wwH16(B1,63,B0,FE,9F,25,46,6B,2D,DE,A7,6C,0E,49,54,1A), + wwH16(0C,70,05,0F,30,76,FD,68,37,B0,82,5D,EB,53,FE,A1), + wwH16(A5,8B,DE,34,B4,37,13,80,ED,72,1C,77,9D,FB,7F,51), + wwH16(ED,4B,69,CF,2C,D7,FB,33,5A,50,8C,1F,CA,84,D0,7D), + wwH16(3E,AD,D6,EE,03,E7,06,2D,20,16,7C,3A,87,16,82,4F), + wwH16(55,9B,29,90,82,9C,F1,B6,84,11,F5,3D,CD,F6,4A,63), + wwH16(E7,88,40,A8,29,BC,73,F5,AD,F8,1D,EE,8C,53,EF,10), + wwH16(E6,C2,4D,71,07,17,75,C2,4F,4F,F7,18,E0,A7,5E,F0), + wwH16(20,5A,60,A1,35,CF,94,62,F6,88,C3,B0,A0,4B,56,D3), + wwH16(4F,D4,DF,E0,4B,1E,C6,2C,34,3C,D0,EB,C5,2B,4F,44), + wwH16(32,59,02,21,BA,D5,EB,B3,7B,E1,0F,CD,CF,B6,E5,97), + wwH16(05,7B,E0,9C,49,F1,8F,76,0D,0D,AA,63,D6,AF,AA,5A), + wwH16(CA,08,97,A5,21,66,BD,A3,54,6A,FF,44,26,E3,91,C2), + wwH16(4A,BC,93,E2,27,4C,76,EE,02,04,54,B5,35,A2,4B,41), + wwH16(94,D0,74,4E,9C,AF,E9,88,AD,98,7C,50,7D,4C,E6,2B), + wwH16(4A,6F,AE,96,CB,8C,55,FE,4A,5C,0A,70,FC,15,70,50), + wwH16(55,9A,5C,78,FF,3F,41,B7,68,73,7F,B8,05,CE,EE,8A), + wwH16(40,95,7B,48,A6,CA,D4,20,E8,BF,17,FD,36,2C,1B,28), + wwH16(C5,15,3D,F4,0F,25,E1,9B,3D,98,70,01,6B,BA,32,42), + wwH16(55,AF,7B,EE,3B,E5,D9,F9,51,8C,7D,C1,B3,EE,D8,61), + wwH16(C9,6E,4D,87,21,5E,2A,DF,0B,B5,33,1F,F0,4C,25,91), + wwH16(98,93,8A,2D,90,8B,36,48,AE,B0,B7,7F,AE,5F,A3,00), + wwH16(C3,00,6D,9C,8B,95,58,5D,92,24,5E,FB,89,96,BB,A3), + wwH16(DD,18,7F,4E,CD,BD,04,61,33,CC,FF,FE,E6,9D,A2,D3), + wwH16(EF,81,B9,B8,AF,0C,3B,B3,59,60,ED,BF,BA,82,6B,AD), + wwH16(C4,B2,C6,99,F5,B6,D6,BB,A0,A8,3F,02,FA,A8,8D,01), + wwH16(48,EF,91,3C,CF,36,1A,EC,78,25,6C,C2,A9,99,C0,22), + wwH16(29,45,C6,BD,DA,7D,08,78,82,D5,D3,4E,15,80,B9,02), + wwH16(69,18,ED,71,C1,D0,73,F8,F0,F2,59,BF,EF,EA,23,26), + wwH16(4B,51,16,C3,27,BF,6B,DA,89,65,BD,69,4E,CD,DA,64), + wwH16(22,E6,F0,D1,D8,BA,91,93,2F,23,5B,97,70,D3,11,3E), + wwH16(A2,36,DF,D4,90,F3,8E,40,99,B6,60,22,9E,59,F9,44), + wwH16(B4,06,A5,21,43,C3,00,4D,75,D6,EF,D2,AC,53,D7,B8), + wwH16(3B,B6,2D,9A,DA,58,79,48,FD,49,AF,14,A7,32,C9,99), + wwH16(23,5F,0A,55,64,BC,28,EF,2E,E0,7D,E3,95,99,A3,99), + wwH16(AB,2B,19,3A,0E,BB,24,FE,A4,F1,A0,89,FD,4A,6D,5A), + wwH16(EC,03,DE,29,9C,99,F8,F5,CB,8F,07,82,12,64,FA,C5), + wwH16(45,74,5B,E4,2A,2F,5E,9A,5A,20,86,FA,9E,B8,74,49), + wwH16(6A,52,8E,1F,02,1B,51,9E,53,7C,57,E2,04,34,62,1B), + wwH16(A4,0D,D8,2A,F2,28,62,52,53,59,2E,45,26,B2,C0,E7), + wwH16(5A,D7,77,5B,39,6D,AB,4B,4A,71,48,3C,2D,FB,AA,D1), + wwH16(D4,4E,D5,3C,72,1A,A2,0A,6D,CE,D7,E4,68,18,5E,B9), + wwH16(DD,90,AA,B5,B3,5F,28,8A,AD,B5,A4,F3,0A,D6,BD,08), + wwH16(C9,D0,65,E4,1B,C1,0C,86,CB,84,39,4B,20,30,0F,12), + wwH16(1A,64,84,41,18,4E,FC,ED,C4,AA,71,9F,C9,E6,3C,1C), + wwH16(40,AA,46,1C,9E,4D,18,38,53,88,3E,85,48,B1,58,5F), + wwH16(AD,A3,02,DB,5F,84,A0,F1,9E,C3,DE,3D,3D,F5,CD,29), + wwH16(54,5A,FA,A1,B0,BF,DF,0E,74,F4,41,CF,8D,45,04,01), + wwH16(49,6D,D0,CD,0D,57,83,FC,8E,C2,7A,B2,44,EA,36,63), + wwH16(A4,1E,10,66,70,F6,19,0E,D3,C8,21,76,70,BA,66,FB), + wwH16(DC,D1,6C,25,F0,BD,8C,13,44,D3,A7,6D,C9,0E,DF,E1), + wwH16(31,36,D1,0B,B6,45,C0,F4,BE,B0,F8,8D,4F,6A,5D,E5), + wwH16(67,6E,F6,DE,69,E7,1D,32,E5,1F,9F,77,4A,83,A2,7C), + wwH16(60,A1,B7,45,59,C7,F8,0A,80,B8,9F,68,A1,29,30,49), + wwH16(7A,7F,21,FE,2A,E3,EC,8E,3C,49,22,A1,9F,E0,85,95), + wwH16(96,A7,13,6F,BB,8B,56,35,D7,D0,BD,FF,1A,49,21,FB), + wwH16(B7,7E,0E,EF,58,92,B7,58,44,DC,30,F3,20,8D,8A,8D), + wwH16(C4,3B,C4,36,D7,48,8F,FD,E7,26,00,1D,C3,4A,44,95), + wwH16(45,32,AB,5F,AF,E8,40,9F,2E,D5,E9,4A,D6,26,F3,7E), + wwH16(39,8A,71,00,B8,E1,BD,75,64,6D,18,B1,62,5E,33,E0), + wwH16(55,B7,24,10,62,32,41,C4,B9,09,10,60,34,ED,03,D2), + wwH16(2F,55,8E,1F,A0,7C,49,94,B4,4C,15,6A,7D,07,A4,C6), + wwH16(0B,91,C8,A1,5A,B7,6D,39,B1,BF,0B,EA,53,1D,DC,ED), + wwH16(CA,3F,1B,C4,45,CF,1E,F6,E3,0C,B2,24,D1,EE,B3,B0), + wwH16(22,78,83,2F,FB,BF,4C,EC,D5,AE,72,75,DD,72,95,76), + wwH16(8D,F4,A0,44,25,7B,3C,A6,F2,D9,59,E9,96,BF,32,FF), + wwH16(01,26,2E,D1,56,99,09,07,E6,EA,1C,55,B6,83,C2,9E), + wwH16(73,F2,A6,39,D4,4E,0E,A4,F9,DB,24,B4,E9,96,7D,55), + wwH16(5F,F8,44,E3,9F,0E,EC,FF,DD,4A,50,85,21,60,E6,D1), + wwH16(69,F8,28,0A,2E,23,7B,A5,3A,95,87,A8,61,30,44,C4), + wwH16(2E,90,83,E8,91,46,A7,65,8E,E3,9B,36,3F,8B,1F,2A), + wwH16(04,1D,49,49,29,0C,E2,E7,36,D3,D3,96,97,4C,D2,37), + wwH16(27,77,48,60,0C,C1,86,19,E6,20,EE,8E,0A,B4,23,C4), + wwH16(D8,A9,67,B1,F8,5D,0E,33,8A,DD,8F,86,3D,9C,08,D0), + wwH16(65,B0,C1,7E,DE,F5,6D,C1,E8,01,D5,E0,DF,61,A9,3C), + wwH16(83,BC,28,35,80,55,52,8F,57,DF,9E,CA,FE,CA,05,E6), + wwH16(FC,5C,A3,57,2D,B8,91,43,F5,D4,69,C4,86,B1,21,06), + wwH16(64,B0,5A,92,96,E4,12,EA,0B,CE,AC,DB,71,F2,B4,57), + wwH16(7A,06,E7,BF,7F,72,70,64,C9,38,80,69,11,AF,34,EA), + wwH16(A4,85,3B,48,3E,60,FE,20,19,89,CA,49,97,CD,F5,A8), + wwH16(B9,2C,64,ED,3E,D1,1E,A2,31,5A,2E,58,03,1E,1D,B7), + wwH16(51,CA,7B,E1,D6,FF,42,E3,32,60,81,CA,BF,43,AD,32), + wwH16(96,19,62,16,05,6C,92,F3,73,F0,69,2E,1A,57,C9,43), + wwH16(7B,9C,5A,B9,26,20,A4,02,09,35,AA,E4,BA,3D,30,E5), + wwH16(B6,57,47,F6,E1,30,4A,64,8F,C5,84,B6,FA,C2,FF,98), + wwH16(80,37,13,00,B0,43,7C,96,74,1F,D4,CE,6B,08,E4,B9), + wwH16(50,E1,14,D9,44,0F,C8,CF,EA,7E,0E,7A,23,FD,04,E4), + wwH16(A4,13,B8,B8,0F,34,83,EE,FA,58,C7,42,F9,B6,68,52), + wwH16(E8,6D,D4,DC,7A,43,83,33,51,EC,B8,67,5D,D1,F2,B6), + wwH16(11,6A,C5,7A,BE,BB,30,00,E2,5D,6F,E1,68,2E,FE,87), + wwH16(A6,3F,27,5D,8C,2E,D4,88,AC,34,58,64,CA,2D,C1,96), + wwH16(B2,93,B4,62,A2,AD,20,AC,A2,41,6E,8A,F4,41,66,AB), + wwH16(FC,5B,66,0C,72,B8,7C,08,A8,67,BD,FF,AB,F8,3F,CD), + wwH16(0D,30,9F,24,79,0B,41,54,88,15,36,02,84,C8,6D,B7), + wwH16(B9,5E,9C,A1,B1,FE,A0,E7,EA,67,42,1A,13,B8,B3,2A), + wwH16(E7,6F,F8,6F,BF,07,72,32,86,12,9F,22,2A,D1,8B,0D), + wwH16(4A,7C,27,E4,BB,66,F1,E8,AA,50,0A,50,B7,B5,CE,6E), + wwH16(45,4C,4C,31,B1,81,A6,38,19,9A,8E,4F,4F,66,27,9D), + wwH16(27,20,00,06,CB,F4,83,0A,3F,47,67,A1,79,F1,A4,DF), + wwH16(BE,FA,EB,CB,6E,F6,D0,7A,EB,4C,50,9D,7E,6F,3C,8C), + wwH16(3D,A3,3B,EB,8E,A7,D0,A2,C9,1E,51,FA,D7,55,6F,2D), + wwH16(93,12,E7,FA,BE,D2,7B,A0,F7,74,B2,A8,FE,AD,13,C6), + wwH16(4F,E3,EB,C7,28,CB,FC,E8,20,AF,D7,0F,19,41,C5,2E), + wwH16(4B,17,8D,EE,6F,95,24,30,92,76,22,64,62,21,90,DF), + wwH16(4C,EC,E5,9E,E6,BF,2D,CE,A4,60,2E,42,7B,7B,EC,D9), + wwH16(09,11,70,D6,BA,E3,AB,C9,33,F1,CB,3A,F3,30,F2,BE), + wwH16(43,1B,F4,2D,7D,4D,36,55,83,33,F4,10,EB,49,6A,1B), + wwH16(15,06,F4,DB,04,04,47,DC,DB,3B,1D,E0,63,4A,8B,82), + wwH16(31,9D,5D,33,E4,4A,AF,D6,39,52,69,E8,C4,D5,09,FE), + wwH16(A0,BA,0D,72,6D,68,22,FF,85,33,E6,D8,3B,B8,09,4A), + wwH16(40,B9,95,BD,37,DD,59,D3,B4,3F,BB,16,C3,B1,4C,24), + wwH16(BC,B8,3E,B9,DF,8E,8D,5A,51,54,37,82,9C,AE,1B,DD), + wwH16(BE,F8,45,2E,B1,D2,0B,BC,13,15,C6,1C,32,8C,FB,2F), + wwH16(D8,10,63,08,BC,BB,1E,02,50,33,47,A3,63,1E,30,0C), + wwH16(E6,57,50,81,A7,F3,A1,41,44,EC,79,19,16,06,36,89), + wwH16(0E,90,F9,65,F6,B6,1B,99,A0,CC,1C,BB,A1,F1,A4,4C), + wwH16(C7,76,7E,89,3A,86,6B,74,4A,2F,CB,11,9F,C3,98,C4), + wwH16(D9,DE,D7,7A,EC,7F,71,F6,D2,21,4C,C9,0A,41,4F,62), + wwH16(11,E1,86,98,26,A5,DF,EB,14,BA,79,03,35,AF,42,96), + wwH16(1C,E8,FB,47,2C,95,85,B8,E1,11,A0,0D,61,23,D0,29), + wwH16(70,A8,13,31,86,7D,DF,DD,F4,1C,38,8A,F5,E0,0B,86), + wwH16(7D,A5,1A,4D,6B,D8,39,65,F9,41,EA,86,FB,3E,9D,B8), + wwH16(B1,31,AC,7A,1F,3F,F2,C2,AE,E8,CA,1B,3D,6D,B9,90), + wwH16(AF,85,DE,01,2E,67,FD,F4,45,2B,77,6F,85,06,56,64), + wwH16(6B,C8,D6,35,59,EB,62,F0,3D,CA,8F,81,75,DA,51,15), + wwH16(7B,A4,63,95,C7,23,2E,8C,62,D0,A4,F9,A5,80,D7,EE), + wwH16(CB,29,59,07,43,90,66,49,69,1F,51,5E,2F,85,7C,1E), + wwH16(D1,58,2F,34,6E,3E,AA,4C,CD,F6,AD,34,EA,E8,63,B1), + wwH16(59,EB,F9,C5,74,5B,3F,71,73,82,14,F2,04,F2,0C,12), + wwH16(F7,8D,22,13,26,58,B6,FB,37,F4,C1,DC,E0,64,A3,66), + wwH16(13,3C,EB,45,F1,17,B5,5A,01,27,4B,52,1D,D9,A9,BF), + wwH16(7A,A4,7C,65,35,21,29,D6,CE,E2,40,36,F3,47,E3,E2), + wwH16(1A,31,42,C7,54,E3,DB,B6,07,8F,CB,97,14,8F,D7,C3), + wwH16(B1,F0,09,39,5A,BD,00,49,97,A4,72,7C,84,3E,D7,E4), + wwH16(17,34,FE,6A,FE,37,45,BE,4C,2F,9B,51,52,4B,D5,44), + wwH16(CD,C0,8A,38,88,A8,0D,58,28,D3,E2,0F,E6,70,A1,55), + wwH16(25,2E,82,3D,81,60,48,C7,46,7B,29,6D,53,74,A3,A2), + wwH16(E0,61,69,4D,7B,CD,01,05,A1,02,AE,A5,4B,A0,F0,B0), + wwH16(24,F2,94,0A,3F,B2,A9,08,11,E7,DF,47,14,60,32,43), + wwH16(73,08,0B,36,21,0D,C2,37,F9,3C,55,5F,1F,27,0F,31), + wwH16(B3,49,31,FB,89,EC,39,7F,B6,B7,DF,07,CC,EB,5E,59), + wwH16(F7,36,F9,EE,DC,17,F9,BF,EE,76,2D,FE,C9,A0,EF,EB), + wwH16(8D,74,2D,3A,E4,74,51,ED,E6,24,73,D4,D1,AF,61,22), + wwH16(3C,45,04,BE,C7,84,C7,7A,97,00,B0,D1,D3,28,05,4A), + wwH16(B5,A7,AE,B3,6F,0F,E0,AE,9A,4D,78,EA,63,AA,A8,9E), + wwH16(34,BB,C6,CC,03,30,8A,2B,A5,91,F7,31,10,F7,5A,63), + wwH16(BE,50,4D,51,18,04,BD,29,BD,86,52,1A,E2,65,21,60), + wwH16(DE,5D,C8,0E,7E,99,E2,EC,CB,21,5D,07,0A,A5,ED,CA), + wwH16(BB,2B,4B,CB,1A,EA,76,08,49,96,A7,01,F8,A9,61,38), + wwH16(E6,2C,77,D2,F5,78,C0,E9,BD,DC,B9,A5,61,8A,E0,46), + wwH16(F0,2A,FB,29,D0,D3,2A,32,6B,A8,17,59,19,93,BB,8E), + wwH16(B4,09,12,F4,34,2C,97,D5,64,0E,57,16,26,2A,1D,E4), + wwH16(F0,E3,88,3F,D9,D7,EB,92,4B,FF,23,04,79,8F,A2,47), + wwH16(70,8C,3D,EE,D9,8E,63,80,81,87,79,3D,43,F6,DC,3F), + wwH16(5A,F2,80,F3,1F,A2,A0,96,C8,1A,1A,6F,D3,12,FA,8A), + wwH16(CA,95,22,8B,A2,F5,3C,0F,43,19,E5,D8,68,CE,8C,9B), + wwH16(73,44,F3,8E,06,4B,D5,05,15,5B,A0,68,96,E9,4D,82), + wwH16(C0,CF,7A,0D,8D,7C,14,D4,CD,CE,D6,05,CC,C0,77,50), + wwH16(95,A0,F7,B9,0F,07,40,2B,A3,C8,12,06,3E,7C,6D,57), + wwH16(10,53,23,0A,C1,7A,D4,4C,48,60,A3,F9,26,9A,68,64), + wwH16(01,B4,8B,76,0F,97,DF,70,31,4F,B9,AF,E6,19,D0,49), + wwH16(DF,17,79,E2,A4,CD,70,33,F7,8B,33,5D,3F,43,86,9E), + wwH16(FD,CB,9E,E3,C9,2A,A4,25,95,00,0E,0C,ED,43,82,06), + wwH16(F3,5F,6E,D2,F5,D9,8F,EC,D8,C0,3C,C9,42,D2,33,B9), + wwH16(5B,17,14,5A,AC,D1,A1,67,DB,98,DC,90,25,14,0E,4E), + wwH16(E4,13,67,C2,7E,56,00,A5,A0,80,84,08,7F,1E,28,ED), + wwH16(89,E9,F9,02,53,93,13,70,9A,E6,8C,F3,2A,49,58,BA), + wwH16(93,A5,04,B3,D1,05,0B,FD,E8,71,31,AE,BC,07,CD,86), + wwH16(51,30,D6,FB,1D,A8,9C,20,7F,35,FB,F4,69,B5,66,1D), + wwH16(AE,06,1D,BD,FF,A7,08,7D,45,FC,0B,0C,11,AA,65,B8), + wwH16(24,BB,29,E4,DE,77,F1,0B,8F,45,D0,3C,63,77,56,58), + wwH16(F6,21,46,80,03,1C,11,07,C1,32,4A,BD,FE,80,FC,47), + wwH16(D9,D1,8C,70,19,DE,72,37,C9,57,08,91,70,09,2B,4B), + wwH16(6A,6A,4A,C2,77,98,BF,F2,D8,55,BC,C3,D4,3D,06,32), + wwH16(82,DF,8F,4F,8F,E0,89,0F,3D,3D,FB,31,3A,1F,CB,62), + wwH16(BA,8E,49,6E,D4,79,F5,B5,3F,B3,34,4F,43,62,68,CB), + wwH16(04,5D,8E,42,8C,73,4A,30,CC,96,36,24,C5,46,3A,1F), + wwH16(1C,C6,78,C8,A9,F9,74,AF,3A,7B,31,28,04,F2,7E,20), + wwH16(94,C8,31,6C,2A,56,E7,1D,16,B2,1E,4C,75,98,87,39), + wwH16(DC,17,CA,ED,44,A6,10,BD,E4,B4,DA,67,C7,95,27,64), + wwH16(89,28,F4,F2,73,2A,10,C1,79,22,2B,BE,45,AB,D9,BB), + wwH16(56,BE,E9,FB,75,54,04,73,47,47,92,11,48,3D,8C,E6), + wwH16(DC,62,DA,42,08,F6,6C,8F,7E,BD,D5,FB,4E,32,03,80), + wwH16(D7,AD,2B,AB,85,3D,74,8A,90,4F,72,6B,6D,44,CC,A1), + wwH16(AE,F3,A9,86,E8,EE,57,5F,BA,1C,2A,BC,3F,D9,17,D9), + wwH16(2B,D1,9C,58,C1,90,6F,A3,6A,1E,C2,89,A2,E3,3A,87), + wwH16(80,8F,F9,AF,96,B9,89,4E,5C,C8,4B,F1,9E,70,3C,23), + wwH16(CE,D4,D4,09,42,BA,35,AE,0F,A9,29,1C,73,25,D3,83), + wwH16(8F,3A,A7,D7,5B,1D,55,99,3C,8A,A1,BC,04,99,25,61), + wwH16(31,C1,13,57,1D,4E,D6,DF,56,18,8E,F5,4D,B0,F5,A0), + wwH16(56,34,57,39,5E,7E,69,E3,3B,02,06,26,85,93,CF,93), + wwH16(B9,F1,36,3C,36,1E,35,DD,AB,3B,04,DB,EB,87,64,1E), + wwH16(01,87,06,2C,FA,F0,08,60,5B,79,8C,33,60,0F,D3,89), + wwH16(8F,23,6A,34,F5,D8,12,22,B2,B5,72,41,BF,8E,82,DB), + wwH16(ED,1C,53,B0,8A,86,BA,10,93,95,57,80,46,F3,6E,69), + wwH16(CA,A1,42,DE,0D,36,F2,92,7C,97,26,3B,8C,33,E4,C3), + wwH16(48,5F,DB,5E,96,88,89,92,59,83,CF,36,5A,36,52,0C), + wwH16(72,AB,AC,7C,5B,A8,6C,8B,F6,6C,F9,88,68,92,87,BA), + wwH16(5B,76,90,34,12,15,52,0D,7B,C0,91,08,FC,16,E2,2B), + wwH16(41,3D,65,FE,AC,EB,ED,7C,E7,D6,88,6D,E2,59,56,4A), + wwH16(08,AF,B7,25,E9,A7,CA,A9,54,E8,C4,0B,98,AC,B7,19), + wwH16(EB,D9,A1,C2,46,3E,23,42,C6,E3,2A,B4,19,2A,83,01), + wwH16(48,DE,DB,75,49,DE,5D,C6,E1,74,51,44,0C,67,FA,8C), + wwH16(44,50,94,F3,24,BC,C7,92,45,5C,74,8D,55,74,40,9A), + wwH16(1F,DF,02,F9,6F,28,C8,12,F1,D4,3C,48,E0,1A,F9,89), + wwH16(FB,60,0D,8B,E9,1F,D3,3A,E0,69,0C,B3,93,2E,79,59), + wwH16(5D,9F,42,60,4A,46,A7,F7,0F,E5,38,94,40,89,7E,5D), + wwH16(A1,20,84,86,D0,45,3A,56,86,06,2C,5D,81,E9,F2,CC), + wwH16(81,D8,56,21,38,30,70,F6,7B,73,B5,6E,A3,F9,4B,A8), + wwH16(02,1B,C5,81,ED,90,3C,A9,5D,9E,02,89,80,05,40,F0), + wwH16(DA,08,F7,7F,60,54,05,B0,48,E2,31,11,49,EB,26,68), + wwH16(AD,15,78,D8,EF,8D,38,8E,9E,96,F4,8F,7E,52,CA,F9), + wwH16(B8,79,2B,95,B5,BA,7B,F5,80,3F,1A,84,C6,05,8C,47), + wwH16(35,6E,B7,E8,3D,88,F2,35,AF,CA,B5,8C,F3,01,D9,1A), + wwH16(C3,51,D3,2C,C0,4F,37,9E,3E,1C,B4,32,1E,08,90,35), + wwH16(4D,8A,E0,F6,9E,63,68,47,E3,AE,C2,6B,D9,81,AF,80), + wwH16(17,AC,77,FC,52,37,1E,73,6D,4F,42,FF,C4,E8,86,08), + wwH16(D6,A8,99,15,42,F3,31,25,19,2C,B6,41,A1,81,46,E2), + wwH16(C5,6B,EB,14,0B,27,92,19,22,B7,A8,F5,9F,BB,13,56), + wwH16(F1,1C,2C,0F,85,6C,C4,8C,C8,1D,70,C1,FD,98,27,5E), + wwH16(8D,18,E2,91,EC,41,EB,8E,1A,3E,7C,71,63,1B,9E,F4), + wwH16(A2,06,5E,71,44,9A,FF,C5,84,C3,D7,0B,BF,02,3E,EF), + wwH16(B9,6E,31,89,F2,2F,E6,CC,14,BE,23,15,C5,EB,09,CB), + wwH16(02,B0,F0,D5,AD,B0,98,B7,63,F9,08,CD,AA,DD,51,FF), + wwH16(98,48,D5,A8,1C,BF,6B,9A,21,1A,13,A9,54,7A,33,25), + wwH16(41,27,32,A9,30,18,A5,68,51,03,7D,85,C9,83,96,AB), + wwH16(E8,E2,30,8B,FC,91,ED,BB,31,98,CD,7D,AE,0F,92,C7), + wwH16(8B,C2,E3,3F,E6,61,BD,4B,9B,78,F0,06,09,0D,FE,1F), + wwH16(A6,60,78,FB,C9,91,BC,F7,E1,7D,73,E9,4A,21,42,BB), + wwH16(D5,38,4B,A3,C2,B1,A5,FE,49,50,AC,FC,AA,F7,45,90), + wwH16(C7,11,64,65,BE,96,33,FB,2A,97,0E,53,99,94,F1,EA), + wwH16(F7,52,1E,6D,D6,61,AE,43,2F,A2,16,5B,73,55,F5,3E), + wwH16(39,7E,B7,8A,E5,68,73,9E,B2,C0,D1,EF,6F,0A,7C,6E), + wwH16(71,74,F3,09,02,23,1F,33,96,7E,D1,E9,25,B4,64,1F), + wwH16(AA,16,C9,88,D7,BA,C1,16,F4,A0,9B,AB,F7,FB,D8,71), + wwH16(A3,FF,9F,CD,50,F4,F0,E2,53,FA,D4,6C,5A,D8,CC,D8), + wwH16(40,5F,72,62,70,DD,73,FF,B9,EE,72,A4,32,54,0C,49), + wwH16(FD,6D,E3,05,8E,2E,A9,DE,64,1D,72,92,54,1A,FD,0C), + wwH16(8C,F3,09,27,66,3E,42,6E,69,7D,BC,45,13,A3,F1,67), + wwH16(18,9C,99,75,5E,52,6A,CD,6E,8D,E1,38,EB,01,8E,F9), + wwH16(07,33,B8,18,49,8E,9D,47,D6,C6,48,44,24,47,EA,58), + wwH16(49,C6,42,A5,EE,55,5E,C6,A0,1A,C0,8C,32,44,86,A8), + wwH16(CD,44,4E,B5,36,85,B1,BB,5D,2F,63,AD,EF,52,1B,29), + wwH16(1A,FA,4D,F4,07,C1,9C,08,4C,FC,C2,46,A8,97,1F,E1), + wwH16(F5,D7,22,B8,66,48,CE,1B,17,31,09,5F,C0,C0,42,FD), + wwH16(FD,E1,98,41,4C,C2,D5,E3,E8,19,74,A2,62,EE,A6,7B), + wwH16(CE,EF,C4,CE,FE,79,A6,49,FC,BD,AB,00,D8,FA,50,E2), + wwH16(72,2F,81,71,34,D4,59,02,DE,DC,1E,61,E4,68,B9,07), + wwH16(5C,AD,42,31,66,18,73,7F,B4,DE,F0,D4,52,0F,98,3A), + wwH16(B0,C1,79,84,E8,A8,A1,88,45,D2,F2,33,82,FD,CD,E3), + wwH16(F1,64,B3,93,FF,CC,CA,5D,ED,11,AB,AC,6C,78,1F,01), + wwH16(A3,72,ED,6F,11,A5,CA,63,B1,9A,9E,EB,44,9C,2C,0C), + wwH16(32,F8,51,37,97,70,CF,B7,2C,FA,14,F5,66,72,EE,85), + wwH16(D5,C8,F1,49,27,13,34,84,37,1B,4A,8D,DE,A2,D7,17), + wwH16(C9,8F,49,A0,13,57,33,80,8A,C0,A4,CD,21,F9,A9,BB), + wwH16(15,57,42,9F,F8,69,DC,41,AF,33,2C,78,AA,9B,BF,CA), + wwH16(51,11,7A,9E,47,79,4F,F7,B8,83,3D,22,E9,D0,62,0F), + wwH16(E0,37,47,DB,28,2A,32,FA,B6,57,DF,71,E8,A4,99,95), + wwH16(C8,FB,5F,E4,D1,8C,18,C2,2E,BD,6D,6F,72,B2,41,1A), + wwH16(77,3B,B0,23,13,7A,60,06,F2,59,2D,35,93,89,C3,8F), + wwH16(5E,38,67,CD,14,5F,4E,32,DE,10,A5,EC,38,08,89,F8), + wwH16(34,59,4E,09,A1,87,0D,DC,19,82,A5,31,13,37,5D,47), + wwH16(8E,9B,D5,04,14,E5,5E,D2,5E,27,B6,DC,34,9D,F2,44), + wwH16(81,0F,07,41,6F,BF,D0,A4,97,8E,3E,0A,F6,B6,3F,71), + wwH16(52,21,9C,3E,FC,91,FB,53,69,B8,46,B7,EA,A7,BA,0E), + wwH16(9C,C7,FA,F5,2E,93,10,72,C0,43,50,A8,5B,F9,38,21), + wwH16(56,EF,9F,D4,F1,A2,54,2C,51,08,D1,35,5D,DB,11,D4), + wwH16(18,74,42,44,E9,7C,78,14,F2,79,05,2A,A5,E9,3D,3E), + wwH16(A0,2B,6D,2F,4E,65,1B,C4,DA,BA,E2,62,13,0A,64,C0), + wwH16(E2,92,CA,CD,9F,77,5B,87,8F,B9,58,5A,55,EA,66,09), + wwH16(33,34,9F,1D,7B,01,6C,09,A4,24,31,BF,C9,41,E8,4F), + wwH16(11,8D,8A,D6,21,76,56,7B,EB,2C,00,65,8D,AA,8D,7F), + wwH16(FC,F6,D4,19,D5,9C,25,5E,A3,C1,69,9A,57,C0,85,70), + wwH16(38,D3,90,11,CC,E3,EB,D1,D5,90,F6,4A,A5,B4,F4,83), + wwH16(FE,BD,35,4C,7C,73,62,98,8E,2E,B0,1E,EB,F3,B0,34), + wwH16(66,39,9F,9F,55,4B,3F,A9,AD,F0,3F,F0,82,32,D3,3E), + wwH16(CE,16,42,CF,56,5B,E9,D5,AE,73,2F,F6,31,7F,C0,5A), + wwH16(EF,A5,0E,27,11,77,8D,85,80,EC,71,D5,7B,50,4D,A7), + wwH16(96,E3,AA,B8,6B,9E,3F,7B,E0,B1,DD,7F,08,BD,10,2A), + wwH16(AE,97,4E,E3,DC,39,1D,EB,1A,91,43,CE,AF,E6,F5,BB), + wwH16(A2,10,4E,E4,2D,6E,0B,EC,1F,D3,CC,54,A2,79,AA,13), + wwH16(C8,BB,32,24,49,CA,15,3D,FA,59,9C,7B,6B,95,79,C2), + wwH16(3A,68,0E,B8,60,5C,38,AC,CE,A9,D7,69,A0,94,C7,3B), + wwH16(55,8F,26,C1,0D,02,2E,92,E2,7F,3A,F4,07,05,A5,F9), + wwH16(8E,77,06,C4,A1,46,A7,53,50,5C,11,D9,4D,8E,CB,63), + wwH16(27,29,BE,57,A5,16,53,47,FD,03,BE,39,AC,4A,37,A8), + wwH16(D3,FE,9B,20,8C,E5,ED,CF,56,F9,28,FF,10,AB,33,2D), + wwH16(1A,46,EE,97,D1,D2,6D,D2,E1,E4,D2,C1,5E,DD,53,97), + wwH16(72,A8,6E,77,D5,5E,11,EF,C3,9E,64,CE,EC,D8,72,01), + wwH16(E2,D7,C0,06,4A,07,0D,DA,C3,D5,34,B2,29,6B,82,2A), + wwH16(DD,63,86,E4,B8,2D,7B,4A,62,59,0C,ED,76,19,64,44), + wwH16(7D,8A,6F,1F,47,E8,40,FE,24,E2,8A,DD,B7,D8,0F,31), + wwH16(A9,14,67,65,12,D4,4D,BB,5D,04,58,C6,31,4B,7C,CD), + wwH16(4C,CB,40,A4,8A,34,F9,85,7A,6E,8B,2A,2B,38,76,70), + wwH16(40,5F,2D,15,BB,75,56,9D,EF,86,08,DC,E5,80,12,B8), + wwH16(B3,03,B2,87,3C,1B,98,68,D7,FD,D0,C9,EA,E5,24,43), + wwH16(BF,30,23,44,40,19,29,68,49,5C,90,FB,F4,9E,15,1B), + wwH16(B5,77,DD,D8,0A,FC,1B,9B,BB,F0,F5,79,6C,DD,96,1D), + wwH16(4E,E9,D2,CE,A6,59,D4,93,6A,A9,98,AF,C8,BB,F8,5F), + wwH16(F4,2C,EC,B1,E9,03,9B,99,F2,AC,0B,BC,AD,84,C2,EB), + wwH16(BC,84,99,F1,0C,FF,33,D7,3D,1A,24,38,6B,9C,C7,04), + wwH16(7F,E9,30,12,37,4D,C4,0E,F1,26,EB,6C,3F,EF,2A,2F), + wwH16(60,31,22,5B,71,93,A3,74,D2,6B,F3,7F,2E,2D,96,97), + wwH16(AE,9F,A0,82,DB,25,51,E3,79,61,B7,E1,45,5E,DB,D1), + wwH16(41,C2,5E,DF,EE,82,8C,20,2D,20,6B,20,89,F1,FE,7B), + wwH16(32,15,80,B1,4C,32,80,03,07,83,04,44,31,8E,E7,68), + wwH16(63,FF,55,44,71,71,B6,B9,32,0C,2A,76,3F,03,31,DB), + wwH16(B3,2D,D2,94,6F,84,80,7E,ED,B4,5B,77,B3,34,80,F1), + wwH16(04,06,3A,E9,F8,A0,40,A3,9C,C5,07,70,8E,32,C2,24), + wwH16(42,71,3B,F6,CB,D7,F9,19,99,6D,D2,0F,2E,B4,97,A7), + wwH16(BC,03,6E,77,8B,94,E2,48,5F,DC,99,22,63,27,05,19), + wwH16(1A,55,F7,70,0B,0F,DC,48,0E,7B,33,5C,EC,7C,B8,7F), + wwH16(15,FF,E9,71,51,65,14,CF,B1,8D,07,57,8E,09,7F,2F), + wwH16(4F,7F,0B,44,E7,01,BA,99,18,D0,51,86,96,39,39,46), + wwH16(49,5A,78,C1,72,3A,99,95,F5,33,C3,8B,48,01,6A,F4), + wwH16(4D,61,BA,AE,59,79,1C,67,64,9A,E4,DD,5D,B9,BF,70), + wwH16(2F,05,F1,91,89,B1,94,CB,34,72,04,FD,92,AF,31,57), + wwH16(55,FC,EA,95,A2,9C,AA,F1,7F,64,80,0C,6D,83,F7,24), + wwH16(40,87,9A,95,84,FB,9D,F0,42,ED,7B,06,8C,9E,39,71), + wwH16(59,DC,B9,A6,C3,0E,48,89,06,A8,21,D6,A8,FE,60,C8), + wwH16(3A,02,5C,29,2D,E3,8F,38,4D,69,A2,09,BC,75,49,9E), + wwH16(AC,07,EF,BD,34,01,A1,57,9E,9A,99,2C,E1,54,6A,CD), + wwH16(05,65,2E,9A,EF,65,54,23,E2,3C,8F,4F,4B,F2,73,15), + wwH16(48,D2,85,6A,BC,AF,34,FB,02,94,BF,77,52,1B,EE,00), + wwH16(B8,5C,13,63,9E,2A,E3,77,A4,4F,C7,32,49,3D,00,A2), + wwH16(6F,18,1D,B2,8F,37,28,BC,D6,F9,83,6E,A0,E9,12,4D), + wwH16(7B,A1,C2,F7,0E,90,3E,30,45,C0,64,64,BC,A6,51,04), + wwH16(E5,81,2D,CB,77,F8,74,40,53,E1,2D,AE,B8,1B,84,95), + wwH16(59,AE,77,5C,62,F8,F0,D3,19,DB,6C,F7,A0,99,02,FC), + wwH16(4A,2B,2E,C5,16,9A,03,CD,88,06,5E,27,BD,E1,03,C4), + wwH16(6D,56,DF,A0,10,CA,40,EE,7E,75,21,6D,26,49,8B,CE), + wwH16(2D,93,37,8D,56,35,C7,FA,5F,50,74,56,FE,04,E0,CA), + wwH16(B8,97,C7,12,5A,65,FB,48,F5,B3,DB,AF,4E,D7,0D,73), + wwH16(A5,63,FA,DA,FD,EB,03,68,AA,29,B4,96,33,CF,32,08), + wwH16(C6,19,D6,DF,3D,0A,1B,A6,29,68,C9,88,92,35,08,6C), + wwH16(10,AD,6A,FD,FE,53,A1,63,68,EB,03,BA,B8,CC,37,D0), + wwH16(1E,D4,18,27,BC,E8,FC,C8,41,6F,EA,AD,9E,04,D5,30), + wwH16(E2,E7,BA,0C,A0,D3,61,EB,61,66,79,3F,3F,90,5C,91), + wwH16(CC,D4,CE,DB,B9,5D,E8,B6,06,AA,E4,5B,C0,9E,0D,B8), + wwH16(70,A2,51,23,30,3B,D3,F2,0F,E3,71,D4,E4,8D,EE,41), + wwH16(17,21,5B,A7,40,1F,33,08,65,9B,A0,B3,6D,4A,02,56), + wwH16(80,E8,05,F1,A8,EF,FD,16,02,E9,E3,EF,62,B4,AC,F4), + wwH16(70,76,BB,B4,C7,99,21,B9,A5,BF,C9,25,3B,84,C2,80), + wwH16(72,5D,41,BC,9B,81,8A,60,92,C6,EC,35,68,44,4D,1A), + wwH16(CA,28,47,34,12,01,82,E7,B1,6C,65,D5,68,D8,D7,00), + wwH16(7B,20,D5,F8,E9,C7,BA,93,CE,B6,5E,8F,11,07,AB,E0), + wwH16(EE,DC,1A,5F,E8,EF,5F,A8,24,70,F1,AE,D2,EC,8E,B2), + wwH16(96,F9,E1,3D,CD,92,56,2F,C9,14,ED,39,94,69,48,93), + wwH16(73,99,23,85,49,9D,C8,F3,07,22,57,B2,69,D1,F3,89), + wwH16(E2,E8,74,7B,A2,6F,E0,06,25,4D,B6,CB,B1,56,56,7A), + wwH16(80,C3,9C,BB,61,06,8D,41,E1,B6,83,4B,8C,1F,36,7D), + wwH16(0A,62,12,AE,CE,D8,94,BF,AE,4D,27,2D,4B,DB,4D,88), + wwH16(C3,60,5E,5F,DE,08,C9,70,41,7C,97,82,EB,FA,69,AA), + wwH16(12,0F,53,30,92,DC,0B,FA,63,B3,54,B9,83,4D,34,28), + wwH16(B4,8A,66,D2,6F,BC,57,A2,92,85,18,0A,51,0B,89,F9), + wwH16(B9,8A,72,F9,96,9F,89,9E,DE,5F,91,80,81,52,51,D3), + wwH16(8E,0B,03,FB,21,3D,1D,85,3B,AD,95,DE,AC,1D,66,90), + wwH16(DB,8A,F4,9E,AA,4D,41,8C,88,92,65,A2,3A,73,38,61), + wwH16(81,CE,C4,27,22,B8,F4,5C,04,FC,89,F6,04,E0,5C,97), + wwH16(A4,05,16,91,07,CB,38,3A,67,CC,2D,A3,AC,7F,FE,DA), + wwH16(E4,7D,D2,A8,F5,23,F6,1E,08,DB,7B,FA,AB,BD,E0,14), + wwH16(C2,BE,5C,D5,66,7C,87,AC,05,0C,7E,C6,12,3E,E6,B5), + wwH16(E8,7A,BF,20,E6,F4,DC,34,A3,71,9A,C2,6C,A4,62,5B), + wwH16(FF,87,0A,24,6D,AE,70,C7,5D,69,31,44,0E,5F,5B,C9), + wwH16(2E,30,1C,48,2B,C8,41,8D,F9,93,B0,84,B8,AC,2B,F9), + wwH16(8B,A8,0C,A3,8C,AF,3B,E7,71,2E,B4,65,B3,F0,C5,D7), + wwH16(A4,78,AB,C5,19,77,DE,B5,1A,54,06,97,ED,AD,79,5F), + wwH16(EF,B7,5C,FF,1B,E8,0E,EF,56,38,7B,E4,26,A4,DE,9E), + wwH16(2F,92,3B,58,85,06,B6,F5,2B,7E,88,F4,C5,D6,CA,8C), + wwH16(E9,98,01,F7,95,59,22,44,1E,D3,6D,9B,BD,92,9E,A6), + wwH16(53,CB,15,4A,59,A4,BA,04,AE,58,B5,24,D0,D3,C9,D6), + wwH16(0D,2E,22,C4,15,6C,FC,6D,F8,DB,88,52,23,B3,1C,0C), + wwH16(97,B4,A2,6E,FE,61,36,BC,12,CE,3D,8F,34,CF,59,B6), + wwH16(9F,BD,99,78,7E,60,20,EF,17,6D,B5,AC,BA,EC,74,11), + wwH16(7D,F2,73,9A,BE,7A,AE,13,2F,66,69,FA,0D,8D,48,CB), + wwH16(42,30,AF,E3,A6,7A,FF,0B,8C,E4,D8,8D,22,C5,20,2F), + wwH16(EB,6D,F9,8F,F9,D4,EE,34,FC,94,B0,63,49,21,B6,DA), + wwH16(F3,7A,51,2E,6B,CD,39,96,F0,66,B3,E9,FE,A7,9F,22), + wwH16(5C,50,6F,43,BB,42,07,F3,01,C7,6B,C2,3A,1B,D8,88), + wwH16(81,1C,E6,3B,C8,B6,AB,7E,03,EA,34,3B,6A,AE,49,A5), + wwH16(79,74,FB,86,0B,05,0A,11,9F,BF,0C,1B,08,FC,EE,EF), + wwH16(60,1D,9A,1B,C3,AC,8E,AA,E4,25,63,69,8D,BA,92,3D), + wwH16(AD,E6,AE,D8,DC,8C,7E,CD,AA,95,A8,9E,8D,8D,04,1D), + wwH16(FF,29,A8,73,52,D3,30,66,24,CB,C2,F7,F6,2B,D8,AD), + wwH16(1D,38,9F,6F,3F,06,E5,D0,D5,B6,04,3B,30,FF,AA,7F), + wwH16(66,8F,21,0D,55,2B,B2,F7,5F,5D,4E,B4,88,2D,74,3A), + wwH16(88,4F,C7,23,EC,7A,19,DD,D4,9C,EA,FE,60,07,41,6E), + wwH16(25,B4,94,B4,5C,F9,29,77,DC,F0,4F,8E,28,6A,BA,B9), + wwH16(42,77,FC,D8,DD,0F,BC,F7,0B,C5,D6,3A,4D,B9,F4,04), + wwH16(46,CA,03,75,4E,63,F6,B6,B8,5E,E4,13,59,1B,89,50), + wwH16(E4,8D,A9,7D,5D,99,A1,AE,DD,0E,A4,B1,71,46,78,2F), + wwH16(B6,DB,C9,D8,D2,6A,76,F7,8D,34,91,21,F1,94,10,90), + wwH16(2E,44,16,3B,A1,26,9B,77,58,6D,7E,3B,59,0F,DF,E4), + wwH16(E6,BF,24,BF,06,98,DC,8C,CB,F4,DD,AF,8C,5D,BF,07), + wwH16(B8,10,09,9C,88,C2,F8,B1,EE,FC,33,CE,3D,42,BF,F1), + wwH16(74,2E,E4,E7,6D,2F,59,07,14,3B,3D,E4,D4,0A,86,9B), + wwH16(13,E8,A9,E8,1E,40,91,CF,80,C0,A9,CA,5C,6F,FC,A6), + wwH16(04,5D,50,3C,EE,EB,63,BE,77,47,3C,9E,51,43,30,3F), + wwH16(88,52,A8,80,09,F4,84,F9,37,5E,F9,3A,62,39,8B,63), + wwH16(92,BE,F8,1C,BC,5B,C1,B4,AF,5A,A8,21,24,85,02,CA), + wwH16(78,13,4A,81,2F,D5,8C,6D,2A,C9,09,F2,34,83,B9,7C), + wwH16(88,2D,A6,1A,6F,02,39,DE,F8,5C,E0,CC,8E,30,4E,20), + wwH16(6C,63,80,CF,BC,83,37,63,CF,6D,DF,80,03,38,C9,D1), + wwH16(FD,C2,21,06,F0,6B,E1,98,3F,42,1A,99,84,26,91,DC), + wwH16(C9,08,94,38,A8,9F,ED,E8,02,57,70,C8,68,40,09,4C), + wwH16(BF,F4,74,2E,D6,AF,1F,7E,09,97,F0,CB,37,92,AD,4C), + wwH16(5D,DA,6E,D2,8E,68,CE,B2,8F,62,47,81,3E,0B,0E,28), + wwH16(57,77,66,2C,5B,C7,91,82,A8,FA,1F,CF,D6,77,E2,37), + wwH16(B8,91,3F,19,D4,74,C6,75,51,03,26,D0,4D,D9,06,A1), + wwH16(65,29,81,DF,63,DA,85,04,B9,68,14,97,CE,92,74,2A), + wwH16(52,C6,87,61,3C,92,65,25,1C,88,A7,01,E1,DF,E1,9A), + wwH16(16,CD,2B,3D,3A,32,C0,C4,E1,48,B8,FE,03,D8,0B,FE), + wwH16(1D,3B,BF,5C,23,0B,5F,92,A8,E9,13,29,7D,C7,F6,13), + wwH16(1C,9A,3F,34,73,BB,06,0B,4F,62,5E,68,CA,47,88,BC), + wwH16(DD,4F,B0,FD,92,4E,BA,FE,24,32,3C,68,DF,8F,E5,EA), + wwH16(FC,52,12,2D,BF,F4,7C,67,F8,07,88,B1,CD,17,A9,90), + wwH16(62,F4,C1,47,85,DC,AD,CE,0C,0E,11,A9,DD,4B,B3,B4), + wwH16(7B,EF,29,34,86,9F,AE,E4,1C,97,C9,37,80,F8,8B,9D), + wwH16(01,68,45,8B,81,2C,AF,F9,19,DD,06,6E,55,AD,1B,40), + wwH16(E5,E9,9C,DF,DE,4E,27,3D,57,60,DE,73,43,F4,B6,C5), + wwH16(C6,8A,47,17,D6,44,C8,B2,96,BB,C0,60,44,7C,CB,AC), + wwH16(3C,00,27,02,03,B6,D5,1C,46,3D,5E,A0,43,F8,A7,74), + wwH16(20,92,E9,A6,1A,ED,D6,90,09,6D,15,FE,D8,19,AA,EC), + wwH16(F2,8B,A3,7B,2C,C1,15,38,C4,42,62,95,E0,FD,A7,F7), + wwH16(82,E8,D2,65,35,ED,A1,48,1E,C0,E5,5E,CB,D3,01,0F), + wwH16(17,7B,A8,A5,3A,34,24,42,72,34,04,03,A7,E1,26,D6), + wwH16(23,AE,D9,E0,FF,24,88,9B,68,C2,62,58,08,0F,88,56), + wwH16(54,D2,56,E2,47,5A,30,6F,5C,7E,61,F7,9F,13,91,10), + wwH16(31,42,40,F9,E5,BE,75,33,40,87,DB,D5,48,24,4C,33), + wwH16(23,5F,DF,0C,39,3A,7E,21,BC,22,2F,6D,74,9E,19,8D), + wwH16(E2,34,A3,D8,29,0B,AD,77,C5,F4,39,A0,B5,70,EA,71), + wwH16(29,4C,1C,72,47,9A,99,F9,89,67,F5,CB,5C,E4,50,40), + wwH16(A5,F2,3E,01,A6,2A,A7,D9,7A,5F,E8,D0,49,A6,E2,CE), + wwH16(E3,5A,E9,75,E5,A4,EF,24,9F,CD,55,54,95,E7,10,8C), + wwH16(D3,6B,2F,57,99,BD,68,1D,75,A7,B2,0A,AA,27,01,E0), + wwH16(96,68,75,55,13,D7,9D,C8,91,B1,1A,C5,C4,24,61,82), + wwH16(81,85,2D,B8,66,CB,72,1B,31,24,AD,33,D5,D5,48,FF), + wwH16(42,13,3B,15,50,5B,37,C7,59,30,88,1E,33,3B,81,C3), + wwH16(22,06,82,E7,A7,BB,0A,C4,8B,13,F4,A9,2F,3D,82,3A), + wwH16(FD,38,DE,BF,22,47,D4,06,BA,F9,47,29,09,DA,81,C7), + wwH16(0D,71,8D,A6,01,07,34,DC,67,F2,C8,C2,9E,A5,E8,C8), + wwH16(74,E6,C4,4B,FA,1E,02,BF,65,02,DE,33,C5,F5,6A,60), + wwH16(0E,5A,3E,06,C3,A9,CE,EF,01,65,85,00,8F,83,73,FE), + wwH16(2B,F1,D8,16,33,29,FF,67,BB,A2,54,86,11,9A,0D,3C), + wwH16(EB,66,79,E0,A5,98,2E,ED,98,ED,5C,19,07,31,69,89), + wwH16(E4,87,F7,F1,22,AA,E1,C1,9A,8B,A5,3C,C0,BA,B3,6A), + wwH16(9E,0D,3B,7E,21,64,3E,E5,46,D7,2A,B7,7A,68,BC,93), + wwH16(02,0C,06,3A,1C,9E,9F,15,4E,55,B1,F3,0E,66,AE,FE), + wwH16(99,78,A6,F8,98,82,20,B4,58,1D,A8,60,AE,D3,BF,D7), + wwH16(AC,6F,61,C9,94,17,6D,ED,80,51,B7,A0,17,BA,FB,D0), + wwH16(42,7E,F6,31,28,B7,92,81,D3,4C,10,EA,0F,9F,FC,8F), + wwH16(1E,03,7F,E9,34,FB,8B,C6,DC,41,75,7E,0D,A4,25,60), + wwH16(FC,BD,FB,19,A7,01,D1,EA,D7,CF,08,0C,9A,FF,F3,36), + wwH16(AA,B8,F7,9B,97,45,F1,0F,D9,3A,CC,CD,0B,89,DD,EB), + wwH16(11,1C,6F,7D,6C,D6,E8,C1,1F,F4,BD,16,58,81,C5,CA), + wwH16(03,B0,91,4A,13,8D,1D,33,6D,04,A3,42,D5,4B,BB,6F), + wwH16(80,B2,26,15,57,72,79,8B,F8,0D,55,B0,C9,25,84,4C), + wwH16(4B,81,02,2E,4D,27,79,0A,CD,D2,0D,A2,12,15,7C,88), + wwH16(2E,6E,20,A2,7A,69,D9,E4,0F,15,3F,D9,96,34,66,4B), + wwH16(26,45,FD,01,B6,D3,2F,A1,E3,F4,4B,EE,7C,A1,78,28), + wwH16(86,48,71,62,D8,9B,8D,7A,D8,81,8F,57,69,06,0F,AB), + wwH16(0E,E9,6D,C7,87,A5,0F,8F,C8,A3,A5,BC,D6,F5,65,41), + wwH16(95,1F,58,B3,B6,48,34,D3,07,BA,39,B8,EB,47,75,5E), + wwH16(90,CE,54,CA,62,12,FA,28,C4,0A,09,3C,32,F4,88,E1), + wwH16(5C,AC,E2,AB,6D,62,51,45,23,72,44,61,B7,BB,F5,78), + wwH16(D7,93,D2,7B,41,0C,FD,62,A7,F6,0E,19,89,FB,80,26), + wwH16(B5,44,B8,FE,0F,4B,FF,21,7D,E2,C2,99,BB,3E,1E,49), + wwH16(90,0C,7E,EA,29,A5,61,7E,45,21,89,32,E1,58,62,77), + wwH16(92,E3,D5,56,AD,1B,14,93,7F,6E,C9,F4,D9,D0,1A,C6), + wwH16(B6,C7,A3,B0,AB,E8,0B,B8,35,CD,FB,CB,EF,6A,98,7E), + wwH16(05,F8,9D,AA,31,23,69,97,10,68,0E,C4,BF,F0,3D,1F), + wwH16(46,F9,57,04,7E,43,DB,D7,D6,B3,FF,09,8A,56,0E,74), + wwH16(1C,E6,41,80,54,A2,C0,24,AB,01,B6,94,9D,D5,51,6E), + wwH16(D3,44,6A,7A,C0,C9,9E,6D,20,11,4F,BE,E6,54,35,E6), + wwH16(75,2B,57,C3,62,32,BB,2C,73,74,7B,CE,5E,CE,EB,50), + wwH16(FB,C2,9C,AC,5E,52,64,85,76,F0,F9,2F,FE,B2,E4,95), + wwH16(33,05,EB,26,C4,02,AD,35,50,34,CE,F9,34,7F,D3,0F), + wwH16(A1,B2,75,46,AE,AA,86,34,9D,B3,0E,8B,EF,9C,2E,0C), + wwH16(E7,70,7F,09,D7,73,95,CE,F2,54,96,8F,43,EB,93,32), + wwH16(77,06,58,7C,14,9B,67,51,58,7B,D2,02,18,6D,F9,09), + wwH16(86,ED,6F,E5,84,EB,73,8C,54,02,97,22,C9,70,B7,3C), + wwH16(76,4C,35,EE,E5,5B,70,06,17,E0,5A,4E,E9,88,59,56), + wwH16(5D,47,09,57,8D,42,7C,FF,25,A0,D8,D4,8E,BD,38,49), + wwH16(97,23,3E,4E,21,2F,91,29,EF,58,28,58,2F,7C,9B,F0), + wwH16(E2,D3,EA,F5,3B,E3,EA,6E,9B,44,E3,5F,80,6C,9A,67), + wwH16(FB,6A,99,9A,BA,A7,C7,37,40,D3,E2,43,73,7E,F8,FA), + wwH16(E9,B9,8F,F9,90,FE,BF,9D,DA,44,E3,9E,B2,83,FA,05), + wwH16(BD,56,B6,F2,EA,DD,A3,90,A8,27,08,2B,8F,23,0B,64), + wwH16(1E,F2,03,FC,D1,19,27,B3,B8,E6,C2,EA,70,BF,80,AC), + wwH16(AD,F9,F9,09,3E,D4,9F,C4,BE,A6,BC,70,8A,84,6D,69), + wwH16(05,1F,DF,A7,3A,41,0B,EF,50,DF,8B,41,94,A7,1A,99), + wwH16(B0,81,89,BB,B2,6E,E4,0F,97,2A,6D,14,68,84,5A,C7), + wwH16(EA,55,ED,BA,82,47,60,33,2E,43,FF,57,1C,7E,5F,A9), + wwH16(7E,4F,7D,04,69,F9,FB,64,3B,EE,5E,78,4B,F2,6F,68), + wwH16(B5,2B,C6,06,0E,84,75,E1,2E,CE,56,93,12,DC,AA,EF), + wwH16(41,BC,4F,14,B6,D7,67,2E,7D,36,00,C2,74,73,81,EB), + wwH16(B0,C8,5D,00,D4,69,E2,B0,58,89,A5,32,3C,45,B2,06), + wwH16(7A,C7,DB,FE,90,B3,C3,95,55,A6,02,2E,18,CC,90,11), + wwH16(5A,E8,A8,AD,02,C0,95,9B,7E,68,7B,DF,BC,D5,57,59), + wwH16(A9,CE,CF,6C,32,66,B2,E4,F5,17,28,FC,45,43,03,6F), + wwH16(DB,76,1D,BD,74,D4,BF,11,24,53,55,17,D8,0A,8A,FE), + wwH16(0F,49,11,C2,8A,1A,0D,A7,89,B6,62,45,16,48,D0,44), + wwH16(10,64,2F,47,B6,79,7B,53,A8,A3,DD,82,36,DD,C4,00), + wwH16(B3,F2,6E,9A,23,3F,17,A3,B2,02,49,5F,D1,CF,ED,77), + wwH16(12,70,76,ED,A3,B5,23,CD,DD,41,F6,9A,83,C5,AC,59), + wwH16(F0,D9,17,70,A3,37,DB,9F,4B,E2,37,C7,2C,09,CC,CC), + wwH16(BC,83,D0,B1,BE,E0,A1,A9,D1,92,97,F6,36,95,F2,98), + wwH16(32,74,4F,66,58,E0,EE,05,FC,BB,83,BE,07,87,1F,10), + wwH16(CF,87,20,39,F5,A3,EB,01,FD,F7,FD,26,2A,F3,51,CD), + wwH16(4F,0E,32,A5,FE,72,3F,03,0F,80,CE,DC,9D,33,E6,FD), + wwH16(12,5A,98,C9,36,DD,9F,3C,65,BA,64,11,47,46,BE,43), + wwH16(04,F1,BD,F2,4E,AB,A2,AF,2F,7F,DA,55,4D,3D,01,E3), + wwH16(10,42,AA,09,C1,78,DF,B4,38,59,96,ED,0B,6F,E6,B3), + wwH16(A1,C1,15,5E,51,EB,8D,46,94,5E,C5,12,04,FB,25,AE), + wwH16(43,37,EF,CA,FA,93,2C,6A,5D,F6,D1,9E,32,75,E2,1E), + wwH16(5C,D7,FB,1E,F0,7B,34,DB,F2,95,BE,4A,8B,CB,66,B6), + wwH16(7C,60,58,62,3A,25,71,12,01,77,B8,57,A6,D0,32,31), + wwH16(C2,CB,8D,5A,E3,18,00,43,C1,36,3F,1E,5C,71,AD,EC), + wwH16(09,8C,3D,CF,F6,47,7D,AF,E9,11,63,0A,95,41,46,67), + wwH16(15,34,52,C5,60,E9,4D,33,27,5E,CF,51,65,9C,02,58), + wwH16(AB,97,42,81,7F,67,7B,04,F6,FE,9D,91,C8,13,21,70), + wwH16(C5,89,BF,75,87,9D,A1,C0,45,2B,DB,7D,D0,18,18,66), + wwH16(3B,B2,14,9B,E5,F0,A6,85,E7,91,FA,D4,86,75,FB,2F), + wwH16(A8,F6,DC,92,FB,B4,39,FF,2F,64,E1,C9,A0,52,9B,AF), + wwH16(E4,CF,49,BE,53,AD,C9,4D,F2,5E,8B,AD,CF,F2,03,75), + wwH16(54,B2,05,A2,D4,85,48,27,61,3D,B6,05,A6,34,BD,CC), + wwH16(C9,52,57,59,93,AA,2B,12,BA,05,DB,C0,82,A0,13,6C), + wwH16(A1,8A,73,D6,2D,7C,63,3C,82,82,21,B4,85,30,24,D1), + wwH16(37,D3,9D,03,23,D8,E8,BE,0B,D5,44,03,EF,FD,96,AD), + wwH16(AA,84,52,C8,97,47,7D,CD,74,49,8B,13,58,67,E4,F0), + wwH16(0A,03,45,57,3E,7C,64,52,9D,CD,EF,66,61,AC,9D,A0), + wwH16(00,E2,F0,B3,61,70,F1,EE,79,BE,83,18,9C,89,B1,7F), + wwH16(37,0C,DB,1B,B5,10,12,5D,26,F5,68,C5,76,91,E4,56), + wwH16(E3,A5,7D,F5,DB,29,FF,94,94,C6,20,1C,35,72,45,76), + wwH16(13,3B,E9,5C,C8,BB,5D,BA,D0,1B,3A,44,31,65,7B,1E), + wwH16(37,B5,28,63,65,CE,A6,D0,AE,9E,5A,A2,95,7E,3E,8A), + wwH16(EE,AA,12,F5,95,31,DE,D7,E0,05,6D,14,61,A9,88,31), + wwH16(DD,DA,03,67,7E,B0,BC,1F,B5,83,93,F1,D2,18,4B,16), + wwH16(7C,5A,0A,2D,8F,30,14,A8,78,C8,B8,14,13,44,6E,14), + wwH16(82,2A,1A,6A,56,63,D6,42,6B,01,B5,89,4F,45,A8,29), + wwH16(E5,C8,7A,44,9C,A7,B0,F0,42,1C,06,AE,4B,B2,82,05), + wwH16(F6,DE,55,1B,F1,C2,A9,A5,DA,58,6B,91,04,95,5D,D9), + wwH16(16,8B,28,D4,5D,8A,FB,D1,40,3A,44,5B,D5,09,12,A4), + wwH16(65,B1,EE,09,6C,29,FD,8A,FD,8C,64,83,AC,62,A8,73), + wwH16(A0,4A,0C,BC,71,98,49,92,D6,B5,53,D2,4B,1D,15,C8), + wwH16(A6,F3,F3,67,22,9A,45,CE,B4,9D,50,1E,00,6E,78,5D), + wwH16(FE,47,BA,6A,44,37,E8,33,A9,DF,03,CF,07,55,37,1B), + wwH16(6B,BE,C8,B9,28,D3,C9,94,78,B8,27,63,AE,DC,06,93), + wwH16(2B,92,BD,70,59,46,5D,CD,9B,56,50,91,FB,30,F5,0A), + wwH16(9F,26,E0,EC,B1,D7,DC,B0,CE,4F,1E,42,29,CF,1F,BA), + wwH16(B7,8F,D8,3F,15,9D,52,D9,5F,54,D3,B1,AD,64,03,F6), + wwH16(14,C2,10,BC,56,02,20,54,DF,06,7E,59,AC,88,1D,0D), + wwH16(6F,DD,01,8D,8B,84,DE,86,D2,6A,EF,E3,83,75,81,AC), + wwH16(48,FF,DF,8D,E1,55,43,F9,10,D5,FB,81,53,CB,09,06), + wwH16(07,CD,64,71,DA,56,64,BD,39,0D,E3,14,EE,8A,13,7E), + wwH16(84,A8,99,1C,94,0C,AE,F1,B2,69,80,AC,29,85,AD,C3), + wwH16(15,66,40,1F,24,DC,E5,E1,C7,ED,3B,03,8A,FF,92,10), + wwH16(85,1E,95,22,87,CA,49,CA,0A,DC,19,68,66,FF,1F,A5), + wwH16(E2,7C,91,D9,B9,31,05,4A,E4,13,75,03,E0,17,01,D2), + wwH16(7D,15,4C,3D,D6,45,7C,45,DF,F4,BF,C7,41,17,62,64), + wwH16(0E,70,23,FB,C2,80,33,42,D6,3D,F0,B2,45,D4,BB,F3), + wwH16(8B,99,88,4B,BD,79,77,D5,FE,F8,43,B2,93,04,07,88), + wwH16(C3,3B,1F,79,E7,CD,41,E7,63,1E,3D,36,23,13,79,B3), + wwH16(8E,07,9E,29,7B,78,AA,FB,C1,FC,29,A1,5F,DD,10,EE), + wwH16(2B,5B,90,90,0B,59,8D,8D,77,66,17,DA,1F,C0,92,6B), + wwH16(80,21,F8,77,23,31,0B,AE,E1,6B,4F,61,51,3A,32,18), + wwH16(5B,F8,96,22,EA,88,41,02,66,7B,C7,3E,6C,2F,AF,85), + wwH16(99,E0,69,47,69,D0,CE,49,C9,31,92,FD,C4,B6,E0,24), + wwH16(2E,39,29,87,BD,18,41,6D,74,D2,59,25,64,DE,D7,AD), + wwH16(F7,83,FA,E5,35,A9,A1,44,41,99,BB,4F,8C,94,CC,F1), + wwH16(5D,70,C7,47,2D,F3,67,44,A7,27,44,9D,85,4A,8C,7C), + wwH16(E0,DD,80,24,1E,9D,44,48,DE,0B,D6,49,8B,14,EB,81), + wwH16(01,CC,7C,16,18,D7,E1,F2,FD,6C,92,6E,64,27,CC,B0), + wwH16(7F,6E,D6,CC,02,BE,2B,1A,46,25,43,65,EE,50,37,CE), + wwH16(D7,DA,09,DD,23,E9,BC,07,3D,10,01,73,6E,CF,39,5F), + wwH16(EC,AB,F5,4D,43,CF,CE,97,7C,95,25,DA,F5,D2,15,35), + wwH16(10,2A,42,DF,31,34,33,47,65,AA,52,FE,67,45,23,09), + wwH16(AD,80,36,60,FD,98,30,36,D8,0A,C5,8F,BC,17,98,A4), + wwH16(3A,70,1B,EC,C3,F3,54,0A,DD,EF,78,48,2C,B4,32,49), + wwH16(CC,12,69,98,A2,3A,F2,EF,47,94,CC,AC,EC,70,3F,74), + wwH16(BE,AE,A2,54,8E,18,33,73,31,67,1C,D2,ED,26,03,AF), + wwH16(A3,CD,C4,70,2F,23,EE,0A,0C,6C,6D,A6,CF,53,4F,8F), + wwH16(B3,6A,10,47,58,3F,B2,B4,FA,D9,92,8D,1C,C3,0D,18), + wwH16(71,3F,E2,27,22,0F,96,A0,9B,1B,E5,1A,51,B8,F3,76), + wwH16(30,5D,75,CB,54,F8,7C,B4,14,E6,0D,8A,C8,57,F9,E2), + wwH16(7F,E6,A0,11,48,B9,A9,A8,25,7E,73,89,F7,36,0E,4A), + wwH16(47,4E,1C,B2,78,01,5D,28,86,CB,C0,00,25,A1,36,98), + wwH16(5F,9B,A6,02,80,CC,82,BB,01,C1,D7,7F,5A,0B,64,5C), + wwH16(CC,B6,0F,EF,E0,D2,1D,D1,DD,4C,38,95,86,B8,AE,53), + wwH16(57,51,86,68,AC,C5,1C,96,53,57,1F,26,DC,55,76,75), + wwH16(25,E4,64,20,DF,E1,98,4B,CF,20,40,24,DE,3F,58,2A), + wwH16(18,B0,DE,58,6F,74,2B,CB,30,15,5C,62,95,A9,36,F4), + wwH16(D0,0C,55,FF,16,C2,BE,15,22,36,43,35,80,8A,F8,E2), + wwH16(ED,40,67,A0,BC,6A,D0,7C,25,E9,D0,CC,57,B5,4B,EF), + wwH16(7D,0F,41,EF,AB,36,83,76,EA,1A,C9,D0,6B,FA,3F,AB), + wwH16(09,06,E2,F5,97,05,CC,EF,0C,40,99,10,92,B8,10,4C), + wwH16(CA,3D,83,ED,AA,12,F7,B2,F8,9C,6A,73,90,66,EB,DD), + wwH16(BC,13,13,2B,E1,EF,CB,31,51,AB,DB,3B,16,4B,0D,C2), + wwH16(F7,CC,7E,28,DC,C2,93,31,52,A4,75,0D,F0,6B,68,AA), + wwH16(5E,92,BE,E0,1D,6B,0E,2D,DD,C6,4E,B1,E0,F8,79,61), + wwH16(2A,05,DA,D5,04,92,6C,67,EE,77,94,0D,94,C3,81,D1), + wwH16(16,BF,3E,3C,BC,B6,AB,86,D1,32,B5,A4,EE,39,74,EB), + wwH16(CB,8A,90,2E,2C,F4,DF,3A,93,6D,89,AF,E6,14,A6,B1), + wwH16(DA,CD,D4,D6,F9,E0,54,A9,47,B4,49,A2,60,B4,F1,FB), + wwH16(A5,00,AB,A9,23,00,07,C4,4B,2C,F9,C8,8F,6F,1F,9E), + wwH16(03,EE,57,84,B8,00,23,5F,34,DA,B3,72,E1,01,D7,21), + wwH16(A8,D8,25,47,65,42,C7,C3,B0,42,C6,AA,87,9A,77,1C), + wwH16(03,E1,60,77,C0,3A,46,62,EC,7B,05,70,93,E9,81,33), + wwH16(3A,2D,A3,F8,66,E4,FD,33,B3,46,36,37,C7,2C,07,31), + wwH16(4C,74,3D,E0,70,1F,41,4D,51,7D,A8,9E,17,AB,2C,2E), + wwH16(D1,FD,81,23,7B,71,94,A3,AE,1A,99,BA,5E,06,DB,A5), + wwH16(EB,1D,BC,C0,D0,1F,B1,72,79,03,43,49,F8,4E,E7,11), + wwH16(97,5F,16,33,54,59,96,F3,D4,F6,4C,80,98,2E,EE,CA), + wwH16(78,BA,8F,0F,6E,0B,E5,2B,6B,DC,D7,B6,28,26,7F,91), + wwH16(17,91,6B,8B,EF,3A,89,1B,5C,88,05,D8,AF,DD,81,31), + wwH16(52,64,1B,1F,B6,B4,24,2B,F4,74,B9,79,DB,E8,6D,53), + wwH16(4B,0C,45,7F,17,98,16,41,5C,81,CE,4A,87,0B,FC,E2), + wwH16(33,22,E9,20,FD,48,81,86,D7,66,51,7D,BE,29,5B,33), + wwH16(47,1B,D6,1F,E8,21,D8,16,21,29,91,36,74,4D,BE,31), + wwH16(5A,21,E6,53,1E,D7,7D,EB,FF,DE,22,BE,03,70,C5,44), + wwH16(43,BC,BF,5E,4A,F2,7C,0B,95,2E,61,09,1E,57,1C,A4), + wwH16(20,47,0C,A2,EB,46,AD,BF,52,AE,A8,6A,AB,35,1B,90), + wwH16(C4,4A,B0,2C,D8,61,B2,7D,EE,47,85,6D,02,C9,00,81), + wwH16(7F,25,7B,4B,43,D1,72,6D,71,03,06,15,00,25,44,29), + wwH16(34,A6,EF,14,A0,82,59,38,6A,29,AA,EA,B2,A7,92,AB), + wwH16(95,8A,70,20,36,CC,75,E6,05,59,2E,30,76,86,16,1E), + wwH16(EA,7F,55,E1,4B,E9,CB,50,66,22,B0,25,E7,FF,96,65), + wwH16(9A,CE,F8,BB,2C,B1,51,BC,25,D6,C4,0E,06,D4,57,1F), + wwH16(23,E6,1F,BB,2A,DF,79,5B,54,61,96,54,15,A2,B7,7D), + wwH16(9D,36,B0,85,7B,E1,66,C4,36,85,DE,10,F9,B9,4A,6F), + wwH16(24,42,ED,AE,20,87,AB,72,42,6F,54,86,E5,F3,D6,7F), + wwH16(0F,09,6B,E4,A0,7D,56,7D,89,43,A6,0E,C6,34,90,CA), + wwH16(E1,32,7C,2C,27,7C,13,C4,C5,94,07,2F,39,17,99,C3), + wwH16(CA,DE,76,45,5A,55,75,79,5F,D2,C8,63,6C,E6,78,86), + wwH16(52,A1,93,D7,69,17,C5,C6,DA,29,CC,9D,A6,D1,3D,8C), + wwH16(84,1B,53,D3,9E,F3,73,B3,3D,5A,E6,25,DA,B2,1E,AC), + wwH16(5D,7C,EA,45,5E,E4,1D,7D,41,A8,A4,82,3C,9A,E9,C2), + wwH16(EF,72,BE,FC,EC,38,C3,46,5D,C1,5C,5E,71,67,78,CF), + wwH16(2E,6F,2F,12,3B,C7,51,5A,C4,34,9B,5A,DA,E7,E2,5E), + wwH16(1C,A6,1B,AC,29,63,96,7A,76,C1,63,00,41,5C,2A,8A), + wwH16(51,9A,56,EC,46,AB,56,B2,80,69,72,14,99,33,4E,18), + wwH16(27,D7,8E,B0,52,65,7D,E1,3E,45,80,0E,49,6D,83,C1), + wwH16(F2,6C,88,C0,76,29,5A,70,4D,21,1C,96,2E,2E,43,4F), + wwH16(DB,22,44,F3,0C,80,83,7D,5F,78,72,A7,21,F8,49,52), + wwH16(B8,C8,9E,D7,93,01,61,5E,AF,40,D2,84,FF,72,D4,D6), + wwH16(B5,89,16,0E,79,8E,FB,14,D2,9E,8F,07,90,26,AC,B5), + wwH16(34,99,26,E8,FF,B8,61,D8,24,71,59,48,0F,D9,AC,90), + wwH16(DD,21,61,11,1A,F4,C1,7D,FD,3C,C8,CA,52,01,2B,AB), + wwH16(D2,B5,3E,38,0B,B5,B9,47,AD,24,3A,52,E9,23,71,C0), + wwH16(56,3D,0F,1D,65,04,5B,C9,A8,72,B4,80,16,E2,A2,5A), + wwH16(38,31,EF,4A,6B,2F,2E,64,52,3B,6B,23,4A,C4,34,C4), + wwH16(17,57,64,EA,3B,4F,36,F4,E5,B7,56,D2,32,CF,91,A1), + wwH16(63,1F,79,B8,27,25,9E,6E,B7,10,66,AA,82,4F,42,66), + wwH16(C0,0F,C5,0C,D4,7F,EB,B9,67,41,A9,6F,1A,88,1E,2F), + wwH16(AD,E9,80,C9,A6,3A,BC,82,14,67,57,3B,BF,76,F6,F9), + wwH16(AA,74,92,AD,1D,B5,CA,EC,04,10,4C,03,34,2F,EB,0D), + wwH16(04,38,66,26,23,2F,51,FE,42,7B,88,6D,9C,C4,2F,C1), + wwH16(0A,DA,08,F4,5B,41,AF,80,8F,65,8D,4C,18,B2,14,C6), + wwH16(86,6E,8F,86,D0,4A,3A,F4,8B,93,89,41,3D,70,E0,CE), + wwH16(1A,E8,6B,9D,C8,4D,48,7D,93,30,B4,14,82,A7,C7,0C), + wwH16(B6,D5,D7,71,C4,51,7C,B8,BB,7E,0F,86,BE,A6,24,4D), + wwH16(7A,70,06,39,66,FB,12,35,60,01,B2,98,0C,07,76,61), + wwH16(61,D7,27,8A,BC,5C,8C,D2,69,AE,15,2C,E9,2C,BD,D2), + wwH16(73,12,5B,11,40,EE,2C,C9,F6,58,C4,91,67,BF,7F,46), + wwH16(59,86,5E,79,BB,BB,E6,7C,5C,D4,D4,10,9D,0D,0E,D4), + wwH16(92,B7,53,07,2E,80,FC,12,31,F8,01,F0,28,38,7F,5C), + wwH16(D4,56,3E,52,5B,81,0D,41,9F,5E,50,7F,89,56,F0,37), + wwH16(63,E4,E6,AF,7A,C3,12,41,1B,64,63,EA,85,12,5D,DE), + wwH16(32,47,F6,6F,ED,7B,A8,4D,E0,39,CE,60,A5,F2,13,50), + wwH16(F6,09,D6,CA,F0,01,57,9E,88,08,68,23,29,AC,8B,0B), + wwH16(19,52,E9,0B,D4,2B,E6,8F,A4,6D,A6,9C,6E,67,F9,C4), + wwH16(E8,45,60,85,51,68,48,62,14,7C,CD,13,75,4C,86,10), + wwH16(6F,38,B2,77,E4,F1,E4,13,D7,52,AD,DC,A7,B7,0B,E8), + wwH16(51,43,EC,43,A8,57,AE,5D,54,20,99,7E,EE,85,12,E3), + wwH16(5D,37,FE,38,5E,7D,84,CF,96,BF,FD,C6,25,10,47,F0), + wwH16(9B,91,8D,0F,49,7E,82,75,A2,8C,03,28,44,53,7C,73), + wwH16(57,B6,AB,5D,53,AB,F0,41,D2,BD,0B,CE,C9,56,2E,E1), + wwH16(BC,4D,62,F7,F5,B8,BA,CC,51,04,9A,91,96,06,16,DC), + wwH16(03,E7,6E,0F,F1,C0,08,14,AD,3C,82,4F,FD,72,44,CC), + wwH16(30,15,36,B5,2F,0B,59,F1,AE,80,65,16,2A,8D,23,2B), + wwH16(DF,85,B2,9B,FD,F0,E5,48,D5,88,69,57,CF,90,CC,59), + wwH16(A3,4B,0D,65,AA,9B,74,B2,1B,89,20,80,BF,F3,9D,7F), + wwH16(34,74,15,21,5F,C6,5C,E4,11,44,75,F2,8A,B9,D7,CA), + wwH16(91,00,3B,B6,06,8C,C1,81,FA,14,54,97,24,26,F3,E5), + wwH16(F5,9C,9F,F1,9F,58,F6,2E,0C,B0,08,EE,11,9E,A2,D2), + wwH16(CD,01,1E,20,7F,57,7D,90,32,52,2C,F8,E7,A7,05,E6), + wwH16(E8,BE,BD,BB,72,60,E9,C1,AC,F1,B9,AC,0C,08,57,10), + wwH16(5E,BF,50,BD,9A,AC,87,AE,A3,A1,C5,B5,D0,CC,1C,97), + wwH16(C8,DD,E3,98,FD,41,E7,D5,B2,17,3A,F8,6B,04,89,95), + wwH16(56,13,BF,4F,5C,BC,EC,01,48,5C,D8,F6,A5,E9,D2,57), + wwH16(A3,84,D8,7C,A9,26,9B,CC,98,D5,C0,DD,2A,AB,CB,66), + wwH16(77,C4,CA,DF,F7,95,E2,6D,FE,26,BA,8A,6E,70,AA,99), + wwH16(ED,BD,E6,63,73,CA,41,89,CC,D1,1E,D7,F0,53,67,99), + wwH16(9E,F7,56,97,A8,D6,CC,18,47,5A,97,0B,1F,BB,D7,09), + wwH16(E1,E6,58,B6,CB,AF,79,A6,E0,0A,79,22,A1,93,9E,DC), + wwH16(39,30,4C,D9,71,8C,CD,49,D5,EB,1B,69,E3,AD,AB,0D), + wwH16(A3,F3,97,FB,77,30,ED,1F,E4,31,10,18,C6,1A,9F,61), + wwH16(B8,C6,24,00,A9,15,6E,45,AB,D5,AC,54,31,27,C4,F6), + wwH16(C7,5D,0B,EF,A0,90,DC,10,A7,CA,5C,CD,68,74,47,55), + wwH16(F2,49,67,AA,C2,F4,F8,EE,B3,50,A1,C8,7E,09,D3,42), + wwH16(70,17,51,D1,4D,C4,0F,1C,03,7B,67,59,C2,BF,B1,9C), + wwH16(C1,CF,75,F6,55,BB,6A,D8,0B,0B,ED,5B,7F,BF,BD,3C), + wwH16(64,AC,C9,B5,AB,DB,C0,13,3E,0E,B9,84,E3,76,DE,8D), + wwH16(AB,9E,09,0C,11,C1,E9,D6,BA,F8,CC,72,0B,47,44,C1), + wwH16(16,E3,34,72,0B,93,5A,40,56,24,74,0F,5C,A4,C2,F4), + wwH16(0D,EC,3F,71,B9,04,5D,AA,03,C4,10,CB,6E,3D,79,85), + wwH16(C6,DD,6D,DC,91,1F,5E,EB,C9,CD,60,56,AF,FB,20,A5), + wwH16(BB,0C,7C,DD,66,A8,4A,B1,7C,3C,9B,52,CF,E1,4D,A0), + wwH16(EC,B0,D6,D7,47,40,5A,53,06,D8,33,8B,10,5E,ED,85), + wwH16(61,BE,A6,FE,D4,34,07,73,76,41,D7,68,BE,1E,A2,E1), + wwH16(67,57,78,AD,7F,E7,15,1E,61,B3,D0,60,39,62,98,7C), + wwH16(84,96,22,2E,9F,2E,D1,16,61,6D,BD,71,3D,BD,26,71), + wwH16(09,62,48,D0,19,1D,5D,1F,65,4D,8A,0C,F8,05,46,53), + wwH16(DA,13,2A,A0,9A,CE,78,A2,6D,EF,9F,2F,AB,40,2D,84), + wwH16(98,80,5F,7D,7A,B1,A3,7E,24,8E,4A,22,EF,1D,73,48), + wwH16(E5,D8,7A,11,50,CF,EF,CF,AB,32,4B,B7,6D,C8,39,56), + wwH16(12,1E,C2,87,B5,65,E3,AE,BF,EE,65,56,80,CE,84,35), + wwH16(7D,1C,91,1F,FB,83,3B,CC,AC,4F,E4,3B,9C,91,F0,FC), + wwH16(A4,A8,55,64,69,3E,F0,23,D8,25,13,2F,E2,50,9F,0C), + wwH16(1F,81,54,3C,8E,D3,51,D8,19,53,3B,56,AC,62,7F,2E), + wwH16(CB,F6,A5,41,73,95,85,BF,F1,BD,6A,1E,6C,02,A4,C4), + wwH16(6C,EB,1A,17,AB,09,81,B0,DF,03,45,C5,3D,2D,5C,40), + wwH16(70,BF,E9,62,1C,D2,E3,AD,AE,BF,05,1E,97,2F,D5,61), + wwH16(10,83,D1,D1,10,49,D0,B7,01,31,6E,C3,08,63,84,17), + wwH16(99,D6,43,24,B1,33,74,3C,39,AD,87,C1,F3,E2,23,E7), + wwH16(58,A9,13,37,D0,06,2B,9B,9D,37,35,B7,EB,47,AF,9C), + wwH16(13,8D,57,4F,BD,87,61,80,15,13,91,05,CD,8E,F1,45), + wwH16(C7,17,C1,07,C0,62,57,38,F3,02,D0,24,37,DE,68,5C), + wwH16(40,56,6C,39,D1,F6,BE,E3,93,E2,D6,8D,2E,2C,69,A1), + wwH16(3B,28,D5,43,C1,01,81,0F,F7,3B,AE,0E,68,F9,7A,13), + wwH16(01,EA,6D,BA,5C,31,B2,88,1C,EC,3F,E2,5E,9D,B0,41), + wwH16(53,3E,D2,0F,8D,79,55,B3,2C,C5,52,83,01,B5,CE,27), + wwH16(3D,E9,D7,A9,84,03,CC,2F,9A,6E,16,33,EA,77,AA,D9), + wwH16(71,BD,86,EC,E2,40,F6,84,77,48,6D,EC,24,6F,D2,1D), + wwH16(EA,67,A8,82,DE,3F,9D,0B,DD,5A,DC,14,88,DE,1A,08), + wwH16(A1,83,AF,D6,6C,39,69,A1,64,F5,47,7B,B5,EE,58,F0), + wwH16(BA,93,83,2E,E4,4C,E0,3B,59,32,97,38,1F,98,5F,47), + wwH16(55,A8,28,42,B8,DD,AD,F4,CF,0F,48,77,65,C7,85,67), + wwH16(D3,32,1F,E7,10,E2,08,A3,09,09,71,3C,74,65,54,68), + wwH16(EC,38,3D,8E,33,DA,40,E9,7E,69,A4,DD,96,EC,E3,8A), + wwH16(E7,33,14,39,B2,86,17,BD,5B,07,EB,99,D7,CD,19,FA), + wwH16(7E,52,01,E8,C7,17,DC,D0,78,16,D2,8A,1B,4D,2C,D5), + wwH16(F8,95,D4,26,F9,8B,AD,50,6A,59,3B,D2,5C,18,0D,05), + wwH16(ED,55,3A,9E,AA,B6,2D,A0,91,57,38,CB,69,7F,C1,48), + wwH16(AE,8F,B2,31,EE,9F,AB,72,75,BE,93,84,98,02,95,5B), + wwH16(21,90,DB,02,3E,BA,50,4A,DB,EC,60,8D,78,75,4E,C5), + wwH16(D4,E5,DF,88,9E,7E,59,69,F6,6F,1D,1F,11,BC,75,44), + wwH16(E4,D6,E7,F9,CC,D9,C9,E0,CB,30,EA,6C,67,2F,89,D1), + wwH16(14,A6,5F,B1,76,D1,1A,F4,33,2B,DF,FF,9B,CA,1E,87), + wwH16(2B,3A,84,D2,A5,CD,13,29,6A,75,44,E4,DA,65,61,56), + wwH16(E7,35,F9,A3,4A,58,5E,12,A8,93,D4,DF,48,17,EB,07), + wwH16(B4,7D,E5,A1,A0,FA,60,90,9B,67,9F,A9,DE,5E,73,47), + wwH16(00,42,8A,F0,73,B6,47,B1,0B,05,6A,FD,6D,55,52,C4), + wwH16(E2,53,00,F3,05,C6,DD,FD,BB,94,86,DE,5D,A3,FA,D7), + wwH16(76,BB,C6,B9,98,81,4B,4B,14,F4,EF,AB,D0,AA,4F,1F), + wwH16(2C,B1,A7,26,77,80,64,05,8D,DB,F0,43,E5,BE,DF,91), + wwH16(4E,80,46,42,94,D2,A1,8E,8F,0A,47,98,93,DA,28,E2), + wwH16(AD,13,2F,50,31,A0,F1,D4,5C,5E,82,8E,15,DF,45,15), + wwH16(03,3A,4B,F1,13,65,16,DB,A6,9C,4C,D4,0F,15,DE,47), + wwH16(E4,AC,A1,1F,D1,F3,88,B1,C7,55,46,91,E2,68,F8,17), + wwH16(37,07,8B,22,BF,08,AE,83,6A,AA,AE,A1,13,15,88,EE), + wwH16(8F,09,A6,E9,61,D0,8D,53,9A,85,31,EA,F4,5F,A3,CF), + wwH16(7F,B1,BD,AB,5C,E8,03,8B,F3,48,CC,A3,E4,8C,1F,DD), + wwH16(04,8F,29,94,24,C8,0F,0C,B3,E7,D2,1D,16,8E,59,15), + wwH16(48,F5,7E,6A,A3,89,3C,4F,11,BC,57,2E,4B,EA,B1,15), + wwH16(C1,AD,8F,03,72,44,7D,5A,A2,7B,A3,7C,BE,D3,F8,B7), + wwH16(53,50,3F,B1,3B,2B,46,3E,8D,2E,F6,D9,DD,65,DF,61), + wwH16(C2,D3,D4,59,74,1B,78,53,2D,42,9F,83,DC,4E,0E,9B), + wwH16(AE,EB,BA,D5,FD,FA,DE,58,9E,10,62,F0,AD,24,08,32), + wwH16(53,83,9E,FA,FC,1C,56,B3,B9,26,96,FD,EA,10,C6,58), + wwH16(01,28,17,24,B4,33,C1,83,E2,9A,02,3D,7F,15,3F,25), + wwH16(E8,C3,8F,AA,62,80,35,D3,10,83,1E,80,FB,80,16,89), + wwH16(14,F9,B7,89,A4,88,F5,34,C2,F3,94,BA,04,42,E7,5E), + wwH16(93,61,56,AB,1B,72,86,F2,DD,85,C6,11,EE,26,0D,12), + wwH16(D4,98,C3,10,D8,17,90,79,09,A6,01,00,CF,3D,95,72), + wwH16(52,66,ED,8C,D1,E3,49,B8,1A,D0,E0,76,1F,F9,8A,57), + wwH16(3A,9F,18,8C,E0,6C,01,98,9F,E5,20,5D,52,2A,2B,AF), + wwH16(0E,A6,C5,89,BD,EE,B3,E4,F6,68,B5,3A,EB,D3,D9,74), + wwH16(D7,ED,FA,02,84,75,A7,EC,E4,E5,8C,12,36,A9,89,50), + wwH16(89,60,8F,EA,EB,90,C0,69,24,59,B3,57,99,BB,EE,11), + wwH16(CD,62,2C,C2,42,54,59,5E,8B,3F,A8,BF,86,CF,C5,8C), + wwH16(EB,C8,34,93,5A,FC,72,31,74,54,DB,EA,17,ED,8D,C7), + wwH16(6E,F2,34,E2,B1,EF,FE,1C,88,AB,75,AE,A8,9F,07,95), + wwH16(1B,E2,2A,99,2E,5A,17,9D,BB,FA,CF,06,FE,7D,C9,CA), + wwH16(B8,93,A9,BB,19,25,28,27,72,E5,4A,F5,9D,FB,75,E9), + wwH16(0E,0C,FD,DA,08,C1,2F,E2,57,B4,E4,09,3B,0C,7A,0A), + wwH16(8B,EE,E1,36,EF,9B,BD,EA,FB,5F,2F,3C,11,A3,CF,20), + wwH16(FB,54,00,2D,5F,27,94,AA,D6,BC,FF,68,D2,1B,A5,F9), + wwH16(2B,3E,83,71,D6,50,CA,CC,61,9B,21,E3,31,96,9E,52), + wwH16(AE,EA,87,07,CE,9C,2A,D7,61,85,A4,DB,24,51,2B,02), + wwH16(66,8D,72,BF,2D,74,24,AD,95,B9,4D,B7,A9,62,78,DC), + wwH16(56,31,1B,B2,01,2F,B7,1F,FF,03,C1,02,C8,57,A8,5A), + wwH16(C1,F6,B8,E6,E3,AA,6E,62,59,8A,7E,24,30,BB,8A,24), + wwH16(63,9E,07,BE,38,A5,11,CA,1D,D6,2B,61,52,81,35,91), + wwH16(B2,95,A1,57,20,8D,A7,FC,FF,12,DD,6C,C6,57,31,4F), + wwH16(09,AC,36,1A,43,29,98,62,D5,2B,4D,27,FB,07,F3,1A), + wwH16(FA,A3,E7,32,F4,3B,CA,2E,88,7C,FC,EF,3C,5E,51,71), + wwH16(AB,9C,F6,90,59,7C,14,D7,D6,25,2B,04,50,72,8E,9C), + wwH16(02,0C,1A,7D,87,F8,E9,DC,EF,38,75,CD,77,33,C6,11), + wwH16(6E,D4,6B,25,B7,2E,5C,55,6D,A0,DD,65,65,1A,17,D3), + wwH16(53,59,2A,24,0C,84,BC,DA,B1,E2,8D,B3,CD,A4,1E,A6), + wwH16(81,A4,93,CF,14,6A,9C,BD,97,4A,60,12,C6,14,79,ED), + wwH16(4C,AB,C0,B2,84,06,75,F3,7A,7C,D9,E5,B1,AA,CB,7A), + wwH16(07,98,20,B0,5E,CE,F2,13,A3,1C,FC,FA,54,74,00,9B), + wwH16(47,A7,35,E0,35,99,C7,36,6D,B1,FF,F5,5D,1C,33,1E), + wwH16(C7,AE,50,15,76,12,89,93,03,F1,DD,01,CB,7A,86,C5), + wwH16(2B,C5,2F,92,5E,3F,CF,96,77,EA,81,D0,4B,80,30,87), + wwH16(4C,CE,EF,BE,AC,19,DD,64,B9,E9,1C,B5,C7,24,BA,46), + wwH16(25,78,3D,9C,D9,E1,53,AB,5F,A1,95,9A,12,8A,0C,1B), + wwH16(60,E9,D7,0C,B4,4B,F3,2F,7B,72,32,37,3A,99,FF,AB), + wwH16(4B,24,36,BF,D7,89,48,DF,F8,E2,46,81,B9,7D,4D,46), + wwH16(43,33,BE,E9,3A,35,D1,0F,8B,AE,5C,18,C3,48,5B,5B), + wwH16(62,13,91,DC,59,20,25,14,4B,02,72,B0,5A,98,52,B3), + wwH16(BB,BB,3E,A2,B0,C5,7C,7F,7C,56,38,98,67,79,D1,5E), + wwH16(61,3C,54,8C,79,B0,6E,20,89,CB,46,75,CA,6F,A1,B9), + wwH16(C2,CD,7A,7C,C5,3B,FE,B4,87,81,90,1F,4D,8E,46,D9), + wwH16(DE,05,29,2A,E9,93,81,DD,4A,7A,6C,EA,90,8D,25,07), + wwH16(83,E0,21,B2,D8,CF,1A,01,4A,CD,C6,EA,5F,BE,22,68), + wwH16(04,05,40,02,E3,5E,21,B4,A1,19,B6,83,C3,97,18,3D), + wwH16(28,F7,6A,50,11,48,D5,6E,BE,1F,42,D9,0C,9E,85,7F), + wwH16(B7,13,E3,43,50,E9,DE,C9,67,BD,4C,30,BF,05,6E,20), + wwH16(CB,26,3E,07,C1,B2,0E,9C,60,B8,DF,F3,7A,53,48,BB), + wwH16(6E,0A,51,97,5A,7A,BC,24,31,A5,61,30,06,7F,A9,AC), + wwH16(55,37,F2,F3,D4,D0,37,F6,64,7C,17,5F,A3,97,C2,50), + wwH16(8C,0D,D3,BF,F1,4A,AD,3E,36,4D,13,74,D4,3B,F2,3F), + wwH16(96,9E,C7,B2,88,51,99,9C,B4,E2,98,BF,51,D4,B4,28), + wwH16(95,7B,66,0D,17,C5,A4,2E,C8,46,E9,AA,92,DC,19,11), + wwH16(78,B3,EA,54,F9,90,3F,00,FE,35,40,33,7D,34,B2,11), + wwH16(60,AE,21,20,40,35,E3,0A,00,66,D0,84,91,53,9F,6E), + wwH16(02,A0,E4,AE,56,04,07,C4,0A,D7,8D,29,41,82,AC,E8), + wwH16(7B,D1,E9,64,BD,2C,5B,42,C7,67,D8,EB,B8,6E,B1,82), + wwH16(5B,77,F7,41,7C,B2,1C,96,58,27,80,C4,03,4C,CF,2D), + wwH16(73,E6,A5,FF,18,3C,6D,52,77,52,47,D4,63,57,98,71), + wwH16(BB,54,A5,C9,06,31,3C,75,47,F0,44,0D,F2,ED,85,32), + wwH16(E3,34,CA,B8,26,E8,1F,C4,D3,AF,DC,25,43,BA,3F,8F), + wwH16(15,6C,F6,7B,DF,83,30,EA,B9,69,AD,F4,A3,D9,B1,7B), + wwH16(73,02,24,26,76,D1,4A,11,B6,70,C6,02,A2,B3,94,64), + wwH16(C2,7C,76,72,C9,50,65,85,0E,8C,6F,3F,1C,C2,7B,BE), + wwH16(F7,C1,F8,66,4F,BF,43,ED,76,AF,C9,A6,37,81,17,E9), + wwH16(77,AB,56,D8,AA,31,67,39,08,05,90,4C,E3,A1,76,30), + wwH16(15,21,79,99,F8,54,20,2C,B6,37,F0,CA,92,04,8C,A7), + wwH16(07,75,F8,90,A3,CA,B6,49,C1,3B,09,94,8F,F6,19,80), + wwH16(F6,17,E8,96,85,01,7A,E5,E6,B0,60,58,DD,F7,F5,C8), + wwH16(91,B0,91,FE,06,F1,B6,B3,D8,42,B0,7D,E8,9A,FD,94), + wwH16(88,39,B8,CD,EC,FC,74,F0,98,45,1C,13,3A,F6,99,28), + wwH16(2C,B8,A6,2C,B0,74,51,5D,16,F2,BD,72,E6,CE,A0,C8), + wwH16(60,7C,37,3B,1A,45,56,EB,F0,42,6E,94,E0,5A,D9,4D), + wwH16(22,F6,64,55,43,DA,8F,40,30,EB,53,B1,C4,25,41,BF), + wwH16(BD,7B,52,04,85,71,91,C7,45,2B,28,59,EC,18,F1,1B), + wwH16(C3,BD,FB,C7,BF,60,ED,E5,77,E4,08,51,C9,8F,CF,E4), + wwH16(E1,92,B9,35,07,71,A5,C5,38,56,32,2A,1F,F4,11,DE), + wwH16(11,2A,E9,04,FF,C9,CA,A4,E5,A3,F6,7F,BC,0C,83,01), + wwH16(58,71,AB,F9,FE,01,F7,19,67,C6,FC,7A,C7,BB,E4,98), + wwH16(08,DA,6D,79,9E,99,61,5C,FC,FA,DE,F9,9A,F9,3D,A5), + wwH16(40,35,22,68,40,DF,29,67,77,3A,5A,11,68,F8,8A,C6), + wwH16(12,14,C8,C1,B3,AE,27,0D,2D,72,AE,5A,D9,F7,A9,1B), + wwH16(37,2C,DB,12,53,F1,05,C3,AB,D4,F7,6B,1C,D8,86,7C), + wwH16(7B,80,0A,18,50,94,C0,9A,63,CB,40,1C,D7,C7,08,91), + wwH16(60,A8,20,A2,74,44,9D,54,3E,54,83,8F,64,C7,CE,EB), + wwH16(19,E7,01,CE,BE,12,78,3B,C6,0B,95,53,7E,4A,CE,5E), + wwH16(50,D9,A7,B9,40,0E,1E,F7,74,CE,58,8F,DB,1F,13,D8), + wwH16(D1,34,A6,CF,E6,94,EE,D4,DD,91,C1,E7,02,7B,58,2A), + wwH16(07,8C,2A,48,80,DE,20,E3,A1,79,B1,18,18,12,F0,55), + wwH16(62,6C,85,52,9D,08,0B,F9,67,4B,0E,D8,EA,4B,21,63), + wwH16(1B,08,46,53,7D,3E,F7,57,83,AE,EF,9F,A5,BB,FF,A0), + wwH16(BA,35,47,56,C4,6B,6F,C5,34,CE,C1,BE,87,A6,D3,6D), + wwH16(9B,F3,6A,E2,5B,8D,F1,2A,71,29,64,42,B2,A4,2A,6E), + wwH16(A8,17,1A,BC,84,20,6C,9D,0A,1B,73,B6,EE,71,F4,48), + wwH16(AC,38,66,84,CD,F9,A9,23,96,13,F7,53,42,A9,57,41), + wwH16(8C,A4,CD,67,80,E9,AB,FC,F6,D4,EF,3C,83,D7,FE,6A), + wwH16(4A,5B,DA,54,18,D2,45,96,6A,87,61,0A,47,04,FE,9E), + wwH16(7F,22,64,78,B6,1F,FC,A5,0E,6B,8E,CE,3B,94,3D,5D), + wwH16(63,CF,7A,0B,1C,6D,A0,9A,71,0F,F4,E6,25,4C,E8,6A), + wwH16(32,DE,49,50,AE,6A,57,31,04,D0,4B,56,80,00,19,25), + wwH16(A8,F8,31,77,15,F9,D9,A8,03,13,2E,90,BB,41,65,47), + wwH16(BE,EA,BB,7D,68,88,A6,26,21,1C,B5,38,44,E5,F7,F4), + wwH16(3D,B7,72,40,DF,1D,D0,88,F8,54,0D,38,BA,0C,CF,15), + wwH16(89,86,0F,E1,0D,89,3B,F1,E8,D9,A7,24,D9,C8,63,3E), + wwH16(7D,2D,AE,0E,77,D5,0F,1D,D3,4A,DB,53,D3,BE,4E,A2), + wwH16(21,B2,2F,BC,D4,45,0D,C7,ED,BF,17,1B,4F,37,DC,A1), + wwH16(0F,95,53,35,7A,2D,B6,BE,82,85,81,39,D6,5F,7D,D1), + wwH16(20,B0,EC,2E,2B,31,39,17,44,73,A6,02,8D,F5,18,34), + wwH16(BE,D6,D8,64,E7,E5,2B,B3,9E,B9,A0,0E,C3,14,04,B3), + wwH16(05,DE,32,BD,16,39,FD,0D,0D,1C,14,44,26,80,44,3D), + wwH16(1D,C8,DA,CF,48,DB,B1,96,DA,3B,DF,03,93,DF,92,6A), + wwH16(40,24,5B,25,65,D5,C8,9C,22,16,4E,B9,19,0E,9D,AD), + wwH16(CD,62,8A,62,8B,48,23,B9,19,BA,CE,0B,5E,0D,9E,2B), + wwH16(0B,3B,FD,7F,A4,10,1A,76,5C,7D,26,B0,54,79,D5,A4), + wwH16(B1,EF,A7,80,17,E7,50,88,E0,21,AD,19,7B,E9,08,30), + wwH16(9B,C3,DB,AD,38,94,2B,FE,87,69,70,31,92,FF,D6,BF), + wwH16(97,BA,83,F9,15,73,1A,42,59,B9,8A,0C,5B,77,02,2F), + wwH16(9E,0D,9F,7F,F9,65,4B,62,97,37,B4,CF,1F,12,A3,62), + wwH16(F2,97,EC,CB,87,CC,EC,3F,8B,59,DA,E6,58,B4,D4,25), + wwH16(E4,18,9C,7D,63,59,BD,A8,B3,7C,F8,80,75,F9,3C,9A), + wwH16(EC,36,F3,F4,4A,C5,40,AA,8B,11,98,11,5D,38,0B,67), + wwH16(6E,68,2B,C7,AF,B6,47,C2,99,40,8B,AD,83,06,47,A9), + wwH16(F9,C6,15,96,4F,54,F9,E7,B4,DD,6F,15,C7,FB,E6,30), + wwH16(FD,27,A1,DF,63,37,5A,43,70,0A,C4,D5,48,1E,B2,74), + wwH16(1B,C4,32,13,71,DB,AA,C3,C3,21,9C,E9,53,94,26,10), + wwH16(CD,A5,F7,BE,CD,03,D0,70,91,06,9C,07,61,A9,3B,28), + wwH16(2D,2D,03,5C,46,2B,4A,46,FC,43,61,17,BF,8F,90,01), + wwH16(34,13,D3,62,B1,C1,4F,4D,80,9D,99,8D,CA,3D,F1,3D), + wwH16(6C,69,4B,2F,F0,73,C3,B0,82,42,BA,E4,A2,BC,AE,C3), + wwH16(2A,FE,8A,CC,2F,B0,B5,E9,DF,04,05,AF,0F,77,A8,57), + wwH16(F8,44,03,FF,A6,EC,05,93,F2,2C,7F,BF,72,C5,CC,22), + wwH16(30,D0,D5,8C,39,C7,14,6B,A6,17,52,B7,EC,68,28,36), + wwH16(68,59,B4,3B,A3,9A,0E,A9,F6,78,B3,75,BD,92,65,B9), + wwH16(84,BE,30,17,0C,F3,D7,54,07,54,14,29,CB,E4,8A,B7), + wwH16(5C,A8,1A,BE,29,45,8E,8C,8F,66,48,44,70,FC,30,CF), + wwH16(9C,B0,75,80,0D,6B,33,E2,FE,93,C7,85,B2,52,22,A4), + wwH16(38,E8,28,A8,5C,50,D9,6C,5B,11,37,A4,72,63,4D,43), + wwH16(3B,94,B8,3E,E9,FF,99,B2,D4,C2,32,FA,68,42,B0,31), + wwH16(02,66,44,25,9E,14,D7,9D,47,31,58,3E,FF,2A,26,0E), + wwH16(9D,A6,1B,CA,E1,49,89,BA,71,A0,A8,1C,9F,74,8A,CE), + wwH16(D8,D1,7C,EA,12,2E,68,DA,20,A6,79,CF,77,78,8B,5F), + wwH16(9D,33,A6,65,3E,24,E3,75,F0,33,19,A5,23,C2,CC,EC), + wwH16(AA,58,FA,A4,37,A5,3E,29,4C,4B,1F,37,CE,7E,B6,C0), + wwH16(56,6B,4B,75,9A,AD,29,C2,7B,78,7C,C1,38,40,88,23), + wwH16(3A,77,34,2E,87,28,6C,DD,42,20,40,C0,4D,06,81,24), + wwH16(F1,B1,DF,E4,DD,0D,61,AD,5A,08,F1,EF,19,A5,7D,79), + wwH16(EA,54,4A,CF,75,44,62,B7,F0,A1,C1,F0,9D,01,EE,3D), + wwH16(00,2E,DA,23,6B,4F,61,47,8D,57,35,94,4C,93,A6,A4), + wwH16(2D,6B,E8,D6,4E,96,CF,71,BB,D9,9F,D9,B8,21,54,C0), + wwH16(3C,9F,CB,A1,15,06,9D,63,6A,5C,69,85,3E,00,08,AE), + wwH16(55,E7,4F,A5,26,FA,AE,1D,FE,A7,84,17,95,32,9E,7C), + wwH16(1B,4C,2A,5B,80,6E,E3,5B,1E,8E,EF,A3,44,06,EA,83), + wwH16(0D,72,09,F6,DF,0B,08,8D,69,8B,C2,7A,B1,AB,27,C0), + wwH16(AB,C2,EE,C3,98,F8,92,88,F5,F1,E0,B2,FA,A4,D8,85), + wwH16(F1,3B,01,7F,E9,3E,32,B0,FD,6F,1D,F8,D9,BF,BA,A3), + wwH16(95,17,97,38,15,B3,6A,BE,1D,4C,42,D5,79,D1,67,0C), + wwH16(38,04,84,A7,A5,55,07,B8,E2,BB,09,96,A1,51,C2,82), + wwH16(B2,C1,1F,21,98,E6,F7,9E,37,D9,A9,FC,BC,A8,2F,21), + wwH16(C7,24,CF,66,0F,70,7C,D3,C8,B3,ED,AA,AE,DC,DF,63), + wwH16(10,90,A3,3F,8C,F9,17,E6,96,CE,79,56,82,4F,E8,A3), + wwH16(B3,78,01,46,18,26,7F,12,49,B6,29,87,D1,47,FE,69), + wwH16(38,D8,F6,F6,38,B1,B1,F5,44,B4,9C,E9,23,15,99,26), + wwH16(D8,5B,F5,51,C7,16,E8,94,F5,CC,1D,42,7D,09,54,86), + wwH16(C7,CC,24,CB,1B,D2,5B,95,5C,3D,64,40,D7,39,27,90), + wwH16(05,7F,D9,1B,63,DD,80,18,C1,AF,80,03,2A,C9,DD,EA), + wwH16(89,F1,A8,33,71,FF,04,36,4D,A4,3C,1B,B3,F5,35,E9), + wwH16(52,2F,AC,B3,9A,4B,CC,F7,B3,78,F3,44,95,5B,37,2A), + wwH16(67,D0,8D,85,9C,D7,9F,D4,44,54,A6,33,0F,76,2D,90), + wwH16(C0,F5,0B,97,4A,15,1D,9A,A0,A5,33,73,4D,28,2B,8E), + wwH16(CB,A3,71,0C,8B,E1,EB,03,C1,38,E8,7E,13,BE,2A,0A), + wwH16(D9,7B,87,33,A2,B9,1D,BE,29,10,51,03,77,E7,B5,DE), + wwH16(A1,B4,8E,75,A6,C9,3A,0E,7F,D3,75,96,58,81,7C,08), + wwH16(79,AB,E3,0F,4C,92,CB,0D,21,3C,09,0B,ED,48,AF,C2), + wwH16(94,54,CD,1D,2A,3F,DB,07,62,C3,FD,A4,D0,95,AB,F2), + wwH16(18,9F,A0,9A,42,E8,38,D5,38,A6,45,46,A0,0B,69,10), + wwH16(A5,B3,D6,2A,16,15,5C,A9,95,A9,3C,9B,7E,95,22,90), + wwH16(EC,EB,33,BF,54,4B,36,FB,C9,58,DC,C4,27,CF,1E,03), + wwH16(99,D9,C5,F5,FE,7E,5A,E7,21,5E,0B,F8,10,AA,3E,30), + wwH16(85,69,07,CC,7D,3D,88,C1,A0,E5,BF,C8,B3,56,1A,25), + wwH16(B5,43,6C,F6,30,0C,F3,A2,58,F6,59,F0,00,C4,57,B2), + wwH16(F8,B2,9B,F9,52,7A,EE,3C,0C,B7,AC,7A,7C,4B,02,76), + wwH16(8A,AE,1D,38,C8,E1,FE,1A,4B,45,B9,0F,FC,5B,05,F8), + wwH16(B3,9E,B9,2E,8C,EC,4D,A0,38,7B,15,EA,8A,9E,E7,84), + wwH16(BF,34,4B,C4,27,8F,30,FD,49,74,7C,35,68,B2,08,03), + wwH16(EF,D4,D5,D0,A0,C8,35,B2,73,11,A3,B7,8C,41,C1,A8), + wwH16(F4,B5,88,14,57,50,D5,73,DF,C2,0F,7D,27,A6,15,7A), + wwH16(47,CA,29,D0,93,44,E5,A3,18,2B,C1,69,A1,C6,FE,30), + wwH16(24,F9,8C,1B,53,C8,6C,E5,9F,D3,70,B8,68,86,1B,57), + wwH16(A3,8B,26,5E,E6,DE,36,A6,E9,71,EF,A1,4A,A8,93,F5), + wwH16(F8,1E,31,1D,DA,E2,B4,50,7D,EA,D1,6F,1B,E3,0F,98), + wwH16(60,50,A9,EC,2B,43,79,39,88,CE,2F,16,B6,17,4D,A1), + wwH16(B8,D8,9D,4E,A6,DF,2D,96,9A,A3,6A,19,DF,6F,8A,6F), + wwH16(24,0D,91,EC,DD,7D,30,A3,80,08,A6,AF,42,A1,3B,26), + wwH16(AD,75,AB,82,FB,DA,A5,BF,E0,64,95,E2,36,C9,7D,EC), + wwH16(55,DE,92,3A,84,5B,D6,5E,98,E8,BB,84,0A,DC,F8,6C), + wwH16(A7,43,90,B6,9A,EC,11,C4,B4,04,99,DD,1F,CD,CF,0D), + wwH16(8B,88,E5,2F,3E,8E,B4,F0,C8,76,AE,8C,3B,78,2D,CC), + wwH16(E1,D6,4F,E4,6A,C8,20,06,F7,A8,05,47,70,80,40,BA), + wwH16(BB,CE,09,F2,F1,D2,6A,5E,95,01,7C,5F,FC,30,DD,6B), + wwH16(3E,CC,DF,49,7B,3D,29,13,0C,1C,25,97,86,09,DF,BF), + wwH16(C0,55,38,C2,23,7C,8A,A6,8E,10,A5,BD,50,9A,CB,0C), + wwH16(A9,45,61,05,69,A4,DB,73,B5,2C,1A,8F,51,A1,55,73), + wwH16(D3,82,8D,47,A4,7D,16,96,BE,FC,FC,51,07,4C,97,7D), + wwH16(FD,C7,6B,D3,63,B4,4C,67,3D,79,03,97,86,AD,1E,28), + wwH16(EE,50,F5,41,FD,1F,07,56,18,E7,C5,F1,77,82,C1,70), + wwH16(06,28,08,AA,C6,2B,6A,26,EA,45,88,68,30,CC,DC,5F), + wwH16(EB,06,99,29,BD,86,0F,D8,E7,72,99,6B,BD,D1,4B,A6), + wwH16(F3,C0,5F,A4,2F,31,7F,92,B9,5D,77,30,DE,8D,D6,10), + wwH16(58,10,B0,2B,5C,8C,2E,AA,56,E0,CD,E1,6C,8B,DF,38), + wwH16(BA,4E,2F,09,A7,5B,8B,68,A7,B5,BE,26,19,4A,BA,83), + wwH16(BE,2D,BC,78,4E,AE,1B,E3,A3,09,E1,19,A3,15,98,6C), + wwH16(11,09,86,E3,4C,2E,A5,46,BE,63,B2,0E,3D,32,17,F6), + wwH16(21,C1,0F,36,0F,07,D7,76,DC,45,F8,F0,93,99,F2,04), + wwH16(49,0D,88,4C,99,B7,57,96,82,AD,FE,D2,45,4F,33,3C), + wwH16(89,22,E8,8F,54,8B,BA,4F,F4,A1,1D,7C,14,60,4A,05), + wwH16(82,3B,E6,F3,A9,EC,71,84,9A,D3,21,82,17,75,32,17), + wwH16(31,8F,98,1A,DB,94,51,A7,32,91,01,D2,0A,D4,33,D7), + wwH16(25,91,2B,34,79,BC,9B,06,92,25,41,EB,9E,13,C1,76), + wwH16(11,C3,D5,18,2A,29,D9,D7,5E,37,08,30,D6,02,AE,DF), + wwH16(78,B1,A8,33,66,2B,D3,35,3F,3E,F6,37,69,A8,95,CC), + wwH16(D4,65,61,E8,A4,0E,30,33,E1,03,A2,8E,2B,22,38,A0), + wwH16(76,6F,1D,0D,A0,91,2B,B4,80,3A,0C,81,0E,75,3F,28), + wwH16(87,8F,05,4D,35,61,0B,37,D7,A6,C2,FE,B4,C6,E6,AB), + wwH16(52,EE,CC,76,26,92,89,F0,E0,86,48,41,A3,29,64,10), + wwH16(57,31,CB,C5,77,C3,22,F0,2E,9E,56,B2,A4,60,A0,2B), + wwH16(D5,05,C2,55,CD,81,8A,44,9B,AF,D5,C5,16,0D,53,EC), + wwH16(5B,75,FA,3C,F0,E9,5A,50,76,3A,8C,F8,4B,E7,51,BD), + wwH16(A3,53,25,FA,EF,3A,4C,96,77,91,BE,FA,B6,AE,49,61), + wwH16(96,06,57,60,84,25,FA,CF,62,F0,DB,1E,1F,5B,DC,5C), + wwH16(BA,74,FB,4B,38,FE,A1,53,3D,6F,F2,D1,9F,F9,22,AC), + wwH16(8B,A6,7D,65,81,4C,A2,6F,A4,E3,4E,76,BB,BD,B1,D4), + wwH16(39,C6,42,98,DD,3E,FE,99,30,B7,17,5F,77,F4,56,87), + wwH16(D2,7C,90,56,37,1F,FF,83,54,3F,D8,DD,EF,FF,AB,56), + wwH16(A9,7E,5F,49,C6,0B,D7,04,CE,AC,DE,FE,93,AF,2A,B4), + wwH16(58,F8,D6,CC,7F,91,11,49,07,B5,E2,70,2C,C9,48,AB), + wwH16(2D,C6,68,12,1D,78,A7,94,46,66,CF,8A,16,B9,56,49), + wwH16(85,88,05,11,FD,C9,BC,64,33,15,46,DA,B6,98,E7,0A), + wwH16(AC,40,F8,EA,E2,08,33,DD,C9,92,C8,6E,91,F1,FC,B0), + wwH16(DF,E8,4A,A1,48,FE,CB,8D,61,11,2D,B5,AB,BF,73,9D), + wwH16(91,C8,B5,98,AC,28,54,97,E4,95,D1,68,61,F0,5D,BC), + wwH16(FB,2A,0B,D0,2A,D5,EF,3E,C0,35,11,EA,FD,84,F5,21), + wwH16(F5,DE,0D,35,4F,2B,67,3D,A6,08,01,10,55,7F,52,E0), + wwH16(A1,1E,9E,6C,B3,F1,1F,32,8E,3E,2E,48,5A,41,00,8E), + wwH16(84,6C,F1,2A,48,F7,1D,DD,3B,38,74,18,4C,68,97,DA), + wwH16(40,F7,42,58,20,A8,F1,29,E2,5D,16,02,9A,AD,26,DF), + wwH16(76,DB,8B,49,5B,D4,BB,C6,F8,1D,27,64,13,61,99,00), + wwH16(B2,69,A2,1A,7D,13,B9,4A,50,CF,07,EA,6E,2D,A4,29), + wwH16(BF,AC,C1,F7,46,80,0B,89,B8,BD,32,D5,12,65,44,7A), + wwH16(BA,71,90,5E,FD,08,B5,B3,50,0E,23,25,05,6E,16,17), + wwH16(06,59,D9,33,E8,C3,4F,1D,3B,61,50,A4,60,55,FC,A4), + wwH16(EB,E6,0D,88,81,12,4F,57,6C,B0,9A,F1,84,9F,31,04), + wwH16(C2,EF,82,DB,9D,E5,A4,48,01,E9,2D,C6,06,C2,DC,3A), + wwH16(4C,96,99,31,F3,34,E8,ED,7A,17,78,17,37,F0,6A,2E), + wwH16(C3,17,50,AE,22,61,26,FC,75,5C,0D,27,F9,A6,03,26), + wwH16(3E,4C,4E,97,05,99,45,A3,8C,E6,7D,E7,D8,1E,54,3D), + wwH16(13,88,54,67,3F,33,B5,54,25,53,6D,3A,49,78,0A,01), + wwH16(E9,BA,34,7D,B9,79,EA,D6,88,AD,81,2E,4A,26,2F,37), + wwH16(D4,A6,E5,82,FB,1C,70,6F,79,05,0E,E4,DE,D1,3E,35), + wwH16(2C,07,08,98,5E,A4,E0,DC,F3,F7,CC,A1,D8,C4,FD,44), + wwH16(83,62,08,E1,34,35,27,9B,B3,D1,78,B2,0E,5B,FE,86), + wwH16(B9,01,ED,60,33,C0,98,05,98,DD,F6,45,55,61,C7,E5), + wwH16(8A,D4,B1,2A,63,3B,BF,52,5C,72,B2,40,45,81,BB,4E), + wwH16(03,08,A3,F8,B3,BD,49,D4,45,5A,71,57,6E,E5,39,BC), + wwH16(5B,BE,F0,19,29,5D,AB,B5,9F,C4,D9,05,43,FA,25,EC), + wwH16(8E,58,3C,3B,2E,B1,EA,70,D1,15,1D,2C,72,19,41,43), + wwH16(4E,2F,1D,A2,F1,A7,55,82,56,57,DF,8A,88,0E,69,08), + wwH16(B9,31,19,58,C6,A4,1B,8E,D9,DE,9E,55,52,89,4A,06), + wwH16(82,40,41,B6,70,33,58,BA,F0,52,77,50,CE,2A,69,9C), + wwH16(EC,EA,FC,5A,27,3C,E3,CB,63,83,28,B9,F7,99,CE,54), + wwH16(A8,33,15,2A,59,48,D9,25,09,5D,4E,07,8D,3C,AA,F8), + wwH16(70,CD,4E,01,C9,B2,C9,D8,FE,67,6F,50,85,DB,7B,2E), + wwH16(3A,F7,44,DB,25,1B,36,C2,C6,19,E6,6E,CD,0E,96,B2), + wwH16(76,D6,9A,B4,E6,05,EC,08,81,BF,60,6D,21,67,6E,DC), + wwH16(09,1C,CE,18,E9,DB,DD,12,D9,F9,B2,0A,F3,FD,C6,26), + wwH16(15,FA,CF,23,74,C9,64,24,18,92,88,8D,18,2C,F8,55), + wwH16(8D,CD,78,62,E3,FF,C6,37,B7,1B,FD,3B,F7,84,A5,FA), + wwH16(9F,9A,F3,60,57,24,92,31,01,4B,92,D7,3A,C5,BE,DB), + wwH16(15,D3,A5,C0,29,4A,F5,B8,E4,06,DC,43,D7,B3,AD,A4), + wwH16(12,5B,A5,91,46,B1,4B,D6,FC,4E,82,D6,A8,5A,B3,9B), + wwH16(15,20,E5,02,1B,1F,7E,19,B2,CF,E0,46,09,21,D5,74), + wwH16(7F,BB,AB,9A,D2,2E,7D,40,24,5C,ED,4E,C6,87,E5,76), + wwH16(55,00,50,EE,DE,FF,FD,A0,A6,B1,66,3E,41,E8,F1,0E), + wwH16(1D,9F,0B,AC,D5,A2,33,F0,BB,BC,0A,01,5C,B8,95,E7), + wwH16(01,26,CD,55,46,43,11,C4,8B,11,78,6E,4B,3E,7D,2F), + wwH16(C6,B1,BE,AC,99,55,65,D7,E4,D4,3C,85,6B,28,8C,B0), + wwH16(BC,5F,82,F1,5C,C7,4E,57,0C,76,5B,D2,51,11,82,C7), + wwH16(19,49,57,21,41,8E,B0,07,19,39,3E,1B,E9,C2,09,2D), + wwH16(20,E0,83,57,E8,CC,99,12,45,7D,4D,4F,EB,21,FA,4D), + wwH16(8E,2A,AF,58,E3,46,A1,8D,46,6F,9F,E0,7D,4C,D9,00), + wwH16(78,90,0C,94,99,23,81,1D,EA,40,1E,92,5F,27,70,22), + wwH16(51,58,A7,B5,C7,6C,10,B8,D3,F1,C5,05,7C,47,9B,3D), + wwH16(00,9B,DC,3F,01,47,68,1F,71,3E,D5,16,F2,A7,FA,AC), + wwH16(59,A5,51,37,72,1C,D1,16,C9,3F,EC,56,8F,2F,53,D8), + wwH16(A2,B5,07,96,8E,20,62,27,AB,83,A5,78,3F,90,58,A4), + wwH16(4F,25,EE,2B,19,B7,0A,EA,5D,DB,89,39,45,BE,BC,A3), + wwH16(F9,94,6C,90,D1,5A,EA,02,8C,05,35,60,6C,D3,D3,C5), + wwH16(67,B4,29,42,71,1F,FC,87,8C,E0,3D,A7,70,4A,98,7F), + wwH16(2E,14,7B,E5,77,35,C9,9E,5F,07,42,46,92,74,78,95), + wwH16(B6,AB,F5,88,3C,A7,C4,4B,BF,6B,FC,55,95,5A,DE,E2), + wwH16(F5,6C,AA,84,73,4F,97,B8,36,62,37,43,00,2D,2C,26), + wwH16(E3,53,DB,78,0F,F9,12,90,15,51,9A,40,ED,CB,A7,C1), + wwH16(EB,FC,EF,20,35,F5,73,4D,1B,2C,EC,C0,BC,F2,34,4D), + wwH16(4C,7A,19,6C,FC,BC,14,00,D5,A4,48,19,7B,A8,F1,83), + wwH16(BC,F1,64,58,AC,BB,C9,3D,88,78,85,06,4B,24,EB,E2), + wwH16(AA,C1,06,93,A9,FE,24,06,CD,17,FB,DA,3B,7F,12,BB), + wwH16(DE,6A,21,E3,B9,2F,E2,90,13,E8,C7,8F,BB,C3,C2,DD), + wwH16(6B,50,40,A8,58,57,38,04,C7,E4,FA,7D,27,F3,C6,7E), + wwH16(49,6F,0B,10,1C,1D,A5,E9,65,55,B5,BF,36,FD,79,B0), + wwH16(A7,82,CA,8A,F6,85,93,83,4F,A7,69,6A,89,AE,19,5D), + wwH16(52,C8,6E,0C,CB,3B,0B,51,15,EB,01,F3,FB,70,41,3B), + wwH16(17,C1,91,C0,35,7F,ED,FC,E5,B4,C8,22,50,6D,16,CA), + wwH16(F1,4A,54,40,72,C5,E2,37,4D,3C,A0,28,98,22,B1,31), + wwH16(4D,5B,D6,CD,FD,8D,8E,BD,13,7C,42,84,66,1E,9E,DA), + wwH16(4D,C6,63,05,0F,4C,65,DC,FF,9C,1F,12,F3,C9,1D,8E), + wwH16(30,82,47,41,5F,8D,B5,10,86,7C,BD,47,15,9C,B3,02), + wwH16(CE,85,21,70,A8,3B,56,39,43,1C,14,19,68,4A,EB,F9), + wwH16(69,FD,BB,9A,8A,A5,17,50,56,63,E2,C1,7A,E4,D2,A8), + wwH16(50,72,C8,05,B9,A2,FF,F6,8D,95,31,3D,A9,F7,98,3C), + wwH16(97,B2,D2,75,2A,A9,6F,31,B5,47,63,8B,07,77,1C,4C), + wwH16(93,72,F4,21,18,9A,05,C1,EF,81,F2,65,B4,A4,BB,F9), + wwH16(F3,90,38,20,34,98,39,B8,A3,E9,AD,51,D6,EC,9F,C2), + wwH16(3A,06,C2,2A,3D,A1,76,C1,F9,27,AA,7D,EE,F4,FD,7A), + wwH16(DE,28,E9,70,75,28,3B,54,61,C0,23,E8,F0,F9,68,6A), + wwH16(78,5F,03,A1,C9,D7,8D,78,0D,39,CF,A1,FF,8A,D8,F1), + wwH16(EF,F8,28,A3,C9,59,AD,1E,B0,EA,A5,0F,E2,7A,6F,3D), + wwH16(91,DA,86,44,38,5B,50,B2,E2,D3,81,F9,9F,AD,6A,D3), + wwH16(D6,9F,5C,72,F0,0C,AB,10,E9,7E,5C,B1,40,82,81,23), + wwH16(9F,94,A9,0C,5C,FC,59,F3,EB,D9,F2,A0,A5,92,20,8D), + wwH16(14,91,10,8D,59,25,FD,60,B5,7B,5E,37,5A,F6,AD,0A), + wwH16(80,F9,38,FA,B6,DB,34,BE,27,E2,3C,B8,23,0D,A7,EF), + wwH16(6A,E4,3E,65,1F,A5,73,ED,37,F3,90,72,2E,23,B6,34), + wwH16(D9,19,19,3C,5F,BB,01,D5,2D,12,B7,83,AC,54,7A,A9), + wwH16(0A,A8,BB,60,18,04,12,FA,A1,5C,B1,5B,55,30,9E,F9), + wwH16(6E,52,58,D5,F5,9B,7E,7D,C6,DC,53,FF,EA,97,BA,27), + wwH16(03,A4,2A,7C,01,5C,F0,13,EA,7D,F5,F9,2C,F9,BF,FF), + wwH16(5A,43,A6,86,4E,C5,70,E1,75,06,76,47,87,5A,B9,A5), + wwH16(F0,0B,AD,4B,DC,B1,D7,B2,20,BD,D4,30,5C,F0,FE,53), + wwH16(31,01,FC,EB,CE,35,BD,CC,16,E4,0A,61,C3,B2,B4,5C), + wwH16(C2,3B,3B,86,D7,6C,A1,93,07,01,B5,2F,09,9A,6F,86), + wwH16(C8,3E,81,3F,12,D7,18,84,72,5D,F0,D7,C9,5F,27,23), + wwH16(51,58,52,F7,96,3F,DD,96,60,07,3D,23,EA,22,D3,18), + wwH16(4C,69,59,45,84,7F,F9,0F,08,86,B2,3F,A1,72,0C,CB), + wwH16(97,1D,CF,AF,24,E8,A6,83,69,CB,0D,AC,96,E3,19,82), + wwH16(8D,53,99,B9,BD,5A,95,02,6A,04,86,B9,41,ED,D1,6F), + wwH16(52,2A,C1,96,9B,B6,D1,9E,A0,B2,51,59,04,E8,BB,E5), + wwH16(11,56,B2,95,13,F0,8C,88,A3,0F,36,10,BC,58,F9,7E), + wwH16(B2,A1,4F,77,6D,B1,1E,D1,D5,E9,A2,F2,51,51,CC,00), + wwH16(11,A7,82,C7,D4,D2,11,76,1E,A2,A6,AC,FB,46,16,89), + wwH16(A5,B1,08,C7,A7,76,B2,AE,21,95,AA,94,32,CF,26,34), + wwH16(AB,88,53,86,E4,46,17,8C,AF,4C,02,62,FD,05,28,D3), + wwH16(3B,CE,5A,60,7B,44,98,71,79,7F,85,DD,BA,B1,0B,46), + wwH16(52,81,54,4C,01,6D,05,E3,23,3E,6D,07,1F,0F,CD,F6), + wwH16(2E,4B,40,86,F6,D7,0F,3C,65,8E,06,D8,77,E6,93,09), + wwH16(48,54,25,21,F0,57,76,9C,E0,11,2A,01,69,34,AC,EA), + wwH16(5C,30,9A,F1,25,5D,48,11,F1,B7,E2,16,45,4F,24,F7), + wwH16(F1,FC,72,B0,48,C4,A4,51,F2,2A,35,36,DF,A9,BD,B9), + wwH16(9D,03,08,E7,58,0C,E1,25,24,81,41,BE,0F,D6,16,2B), + wwH16(59,35,78,3E,34,DE,AA,87,7F,39,C8,B2,84,A2,35,D9), + wwH16(7A,9C,C4,44,D2,82,B4,DE,7D,5B,61,46,50,A9,0E,86), + wwH16(62,AF,22,5C,2B,AF,B5,BE,BD,2E,0D,C5,53,47,27,1B), + wwH16(F4,72,29,E6,E0,08,EA,A9,B2,B1,FD,CB,F1,E1,FA,56), + wwH16(1E,59,91,A8,4D,4B,A2,49,FB,3D,3F,0B,CB,87,60,D9), + wwH16(5E,72,B6,67,4B,A0,C1,5C,C8,C5,CC,8F,45,24,D7,E7), + wwH16(FF,EE,9A,19,8B,A3,B4,3D,32,18,85,A9,20,CA,86,67), + wwH16(2D,17,70,69,FD,03,1E,13,7A,19,57,DD,DA,BE,36,67), + wwH16(FD,EB,96,01,D4,EA,FC,3D,88,CF,12,54,07,92,EB,A0), + wwH16(CD,CE,95,A7,60,05,11,7D,97,1C,FD,B9,AE,0B,2F,27), + wwH16(05,6D,33,6B,95,24,AE,A1,6C,B6,BC,EA,13,9B,DF,E2), + wwH16(17,1C,7B,06,80,A3,21,03,52,50,A4,9D,51,25,B6,F5), + wwH16(F8,3A,7A,C1,86,C4,4F,75,DD,76,C5,B9,AA,65,2F,12), + wwH16(83,4B,5D,75,04,23,A2,9F,72,12,CB,2E,B3,6B,84,0F), + wwH16(CD,D4,B1,1F,67,55,18,4F,5D,8D,46,E4,EF,5B,C9,C3), + wwH16(E6,D7,1A,C7,05,53,07,6F,83,0B,AC,60,17,34,C8,60), + wwH16(66,0B,1A,FC,44,CF,28,91,1A,48,40,C4,40,F1,D1,4F), + wwH16(9C,FD,9E,33,88,92,83,96,E6,FE,ED,37,D6,51,E8,FF), + wwH16(F3,B2,E4,7E,2A,9D,95,DD,71,A2,81,1D,59,78,6A,7A), + wwH16(C4,C8,F8,A9,3E,6E,44,85,DA,28,BE,E6,A2,62,30,4B), + wwH16(5D,9E,B1,0C,37,C5,81,06,13,1F,E5,9A,36,FC,99,23), + wwH16(C6,E4,44,E0,64,F8,C3,3E,F3,74,2D,1F,17,82,2A,40), + wwH16(45,9A,EF,4B,D1,B6,14,CE,DF,6B,00,DF,7B,63,A3,A2), + wwH16(1A,DC,7C,F2,D7,B0,BF,E8,13,58,ED,03,8F,D4,D8,CE), + wwH16(E6,51,E3,A3,70,48,1C,AC,46,50,0B,90,94,94,73,F9), + wwH16(A9,96,66,F4,67,72,F4,F6,A0,1F,65,D2,9F,89,40,46), + wwH16(EB,5B,AE,BF,FC,7E,DA,92,34,29,7D,BA,B0,BC,F0,8B), + wwH16(AA,3B,E1,41,6D,70,5A,A2,4C,EF,2A,B8,6A,71,78,CD), + wwH16(E9,42,46,D5,A1,AA,50,F0,57,A0,9B,3E,E8,9E,48,74), + wwH16(32,91,94,43,12,0B,14,D7,FA,F3,68,43,AE,D9,54,D6), + wwH16(7A,7A,D4,EA,A2,28,56,FC,3C,AF,9A,21,6B,65,65,80), + wwH16(1A,B4,C7,0A,E5,F1,C4,A2,F9,29,FF,F8,4A,28,99,5F), + wwH16(97,D8,D1,C1,0D,28,55,51,2A,22,86,BC,93,CE,6B,F7), + wwH16(68,F4,79,65,18,E2,7A,18,EB,74,2E,D6,5B,60,61,A8), + wwH16(35,D0,3C,AC,42,74,FB,77,62,09,3D,54,1B,C1,80,20), + wwH16(4F,3A,46,F2,1F,76,63,FB,64,94,69,20,ED,5B,5D,ED), + wwH16(82,CE,EA,4C,36,FA,06,CA,A4,7C,8F,4E,C6,CF,B5,2E), + wwH16(5D,9F,76,D5,F4,08,B3,78,4B,7E,59,57,51,A3,5C,C9), + wwH16(74,95,D2,68,9D,A0,7A,29,C0,E9,BE,61,09,B5,A4,A6), + wwH16(DC,F1,F7,FF,64,6B,A3,E6,A1,8D,03,32,6E,DB,6C,76), + wwH16(AE,70,5B,65,CB,F5,AB,08,5D,48,2D,1E,9B,46,7E,87), + wwH16(28,B7,3D,60,7A,95,54,57,40,86,D3,45,87,C7,7E,DD), + wwH16(E5,A3,14,B5,C3,9A,B5,EF,C1,F9,05,E8,B5,E0,7D,42), + wwH16(1E,57,75,D4,84,B1,0B,3E,14,8B,85,2A,EF,AC,D7,FD), + wwH16(EE,BF,49,AE,5F,97,98,5C,99,9C,AF,01,AF,1F,FF,C7), + wwH16(70,C5,AB,8D,66,72,FB,BC,5F,7E,AE,4E,E9,27,08,E9), + wwH16(96,67,48,89,DC,EA,08,C7,ED,4C,E2,1D,72,B7,DA,E7), + wwH16(C7,C4,C8,36,59,C0,BB,BF,6B,D3,8F,9F,D2,22,FD,07), + wwH16(97,14,A4,7C,3C,8B,7C,AA,3F,BB,5B,6D,C0,6B,AC,D0), + wwH16(63,5E,4C,61,77,00,85,AC,BB,C6,3F,A2,2A,A2,EE,1F), + wwH16(EC,37,1A,5B,21,6D,CD,95,25,FE,12,46,45,6B,BC,E1), + wwH16(06,62,C1,59,29,C6,BE,36,D6,F3,5E,33,94,BC,94,0D), + wwH16(CC,0A,38,4C,FC,8C,5C,0B,61,EF,78,94,07,04,69,85), + wwH16(1F,F6,D4,21,67,B3,E0,2F,10,1D,22,99,BE,8C,23,0E), + wwH16(F5,FA,56,43,9D,3D,1D,23,15,DD,54,84,65,E7,A5,18), + wwH16(69,5A,11,E0,E5,AB,83,B7,AD,4D,DC,B1,E1,48,54,AE), + wwH16(2A,2B,50,4E,1A,F2,CD,1F,18,C9,36,4E,3B,5D,C9,64), + wwH16(5C,ED,D8,64,2D,F1,FB,39,F6,11,F6,24,43,41,29,1C), + wwH16(50,ED,DA,40,DE,72,7A,B9,E5,9C,3A,F6,0A,15,91,08), + wwH16(8A,4B,74,0A,85,1B,F9,70,E5,B6,5D,08,BE,2A,13,EA), + wwH16(D0,04,32,FF,92,BA,77,21,62,17,88,BA,61,A8,24,81), + wwH16(AC,A3,05,FE,AC,30,0E,01,DC,3E,61,AC,91,2C,18,5E), + wwH16(9C,FC,9D,CC,70,F0,B0,D7,FA,D8,FF,73,B1,5D,7E,3A), + wwH16(03,66,9E,39,E0,E7,AA,1A,BF,25,59,BF,1F,7B,FC,EF), + wwH16(79,03,22,99,E1,57,A8,5E,B9,C9,F6,5A,0E,A6,FD,1C), + wwH16(BA,97,26,40,82,F2,FB,6C,5A,68,CF,66,25,DE,A6,B2), + wwH16(46,F4,01,50,EC,3B,F0,54,66,14,FA,35,48,4A,BC,03), + wwH16(DA,A4,88,9E,36,44,27,33,92,C1,35,E4,8D,D4,F1,FC), + wwH16(6C,E5,D5,A8,58,31,2D,39,74,7C,24,5E,7E,82,F2,14), + wwH16(34,F1,34,46,A0,DB,6B,E9,59,CB,85,BA,95,31,41,84), + wwH16(0F,42,98,B3,70,94,54,3A,E3,64,16,C6,75,30,8B,B9), + wwH16(09,57,E8,69,34,62,EE,AD,60,50,70,70,C5,2A,44,39), + wwH16(74,3B,18,09,78,DF,11,D4,2A,EA,2D,0C,76,23,18,AF), + wwH16(03,F2,B5,01,8F,DC,70,99,EF,C7,81,4D,CF,F6,BE,D9), + wwH16(13,1D,DB,D7,D2,59,E6,25,9F,68,98,7A,1A,82,8A,77), + wwH16(6D,9F,3B,64,D9,32,11,CC,C1,21,AB,F1,5F,38,AA,4A), + wwH16(C1,2E,41,BC,11,58,80,D3,87,7B,D1,D4,7D,88,52,2F), + wwH16(D8,EE,09,95,08,69,AC,E8,3C,33,7A,7D,80,CD,2F,77), + wwH16(1A,3B,80,ED,30,EC,7C,FD,CE,E7,B6,40,83,28,E1,4D), + wwH16(FA,9B,60,09,0E,94,D7,68,69,CA,BF,4D,F6,DB,61,B2), + wwH16(B0,9A,D1,E9,82,CD,7D,8C,CD,60,8F,6E,5F,75,75,06), + wwH16(56,E4,A7,BA,24,F4,14,37,12,FB,96,5B,AC,93,0C,69), + wwH16(C0,8E,CD,7B,B2,D1,3E,A4,A7,F8,4F,62,7E,60,A1,B1), + wwH16(00,60,2D,AC,84,5D,59,56,E3,F5,EE,8B,A9,8F,FA,E0), + wwH16(C3,A8,4F,D8,88,C6,92,35,AA,BA,79,58,14,C8,A6,A1), + wwH16(6B,11,5B,4D,63,C3,0E,69,30,5C,85,E2,E5,27,99,2E), + wwH16(1A,BC,A1,EB,2C,11,B0,C0,EE,D1,27,DD,F9,4B,AF,98), + wwH16(88,D7,39,44,38,E0,71,28,5B,01,D2,5B,0A,0A,D9,39), + wwH16(3E,78,73,33,51,66,AF,C2,C2,28,E4,77,B1,61,63,F6), + wwH16(CE,F5,06,75,5F,F3,88,22,A9,30,7A,2C,00,43,75,21), + wwH16(EE,51,EA,05,13,C1,C5,9A,0C,9F,A8,F0,8D,1C,B2,2B), + wwH16(EC,8C,6C,5F,75,7B,9E,16,09,FA,74,22,36,20,DA,D2), + wwH16(B3,05,FF,EB,03,A3,64,0B,D4,4A,76,E4,B7,33,64,D9), + wwH16(C3,09,FF,E2,AE,2E,16,A5,C9,54,F4,03,3B,DD,33,18), + wwH16(B7,85,5F,34,0C,56,8C,E3,C7,37,99,B6,3D,C0,57,BE), + wwH16(F5,2A,E6,3A,E6,BD,85,5B,14,2B,ED,71,1E,1B,9F,8E), + wwH16(6E,83,55,88,07,BA,AB,C9,11,C4,FB,83,D1,2D,12,5B), + wwH16(07,34,64,36,87,6F,33,03,58,D8,0E,69,77,03,C3,FB), + wwH16(CD,5B,D7,47,B5,10,88,F6,81,FF,1F,DD,6E,7C,F8,BD), + wwH16(4A,02,C7,5A,FA,A3,3C,74,83,29,ED,46,35,12,0A,A8), + wwH16(B8,2C,FE,39,ED,12,A7,78,B0,FB,7B,DA,22,66,11,98), + wwH16(AA,A6,D7,77,DE,37,60,2F,13,DE,9B,C0,7A,73,E7,FC), + wwH16(9B,54,90,11,13,7B,90,C0,EC,A3,03,03,5D,CB,DD,BF), + wwH16(A4,3E,C9,3C,EB,F5,EA,76,30,F3,15,22,34,75,63,7D), + wwH16(F4,8D,60,69,35,81,A2,72,7F,04,0D,BB,DA,95,C4,FB), + wwH16(50,F4,6D,D7,BE,5D,98,69,1B,7F,62,29,75,12,16,68), + wwH16(E7,26,D5,8F,EB,33,54,BC,81,60,04,D4,9E,50,B9,97), + wwH16(4E,D7,7A,24,C4,5B,E3,31,A8,27,29,88,1A,3F,AF,18), + wwH16(38,A7,E9,48,B7,31,58,07,AC,49,04,85,8B,9A,56,60), + wwH16(50,55,53,C1,1E,4B,F8,B0,D2,5A,9C,9F,3D,FF,52,D8), + wwH16(53,48,74,E2,C3,F8,44,A0,B6,E5,06,48,20,CC,DC,E0), + wwH16(CF,19,AD,1B,40,4F,0B,EF,7A,94,9D,8E,C2,75,7E,8B), + wwH16(E1,91,98,E4,66,57,3F,2A,AE,1E,2D,11,97,4A,1B,94), + wwH16(C5,AB,AF,5C,1F,E9,0C,BE,44,34,7D,4B,23,05,2E,D5), + wwH16(86,E2,C0,70,23,77,61,44,21,73,0D,6C,AD,06,49,CB), + wwH16(43,14,2B,7D,14,65,7C,5B,36,A1,BF,C1,9A,E1,6C,D8), + wwH16(47,03,C8,1C,4A,05,E5,BA,9A,52,63,25,62,2F,38,BB), + wwH16(5E,56,F2,DB,91,85,05,17,0E,58,2C,E3,3C,0B,A1,25), + wwH16(24,C4,7D,CB,42,3C,59,67,82,A5,43,55,E0,EB,E9,61), + wwH16(9A,03,7A,7F,50,C9,09,2F,16,E6,D9,16,F0,22,81,BE), + wwH16(0A,8A,88,8C,CA,29,B1,E1,5D,D8,EB,E6,EA,A2,D0,E6), + wwH16(3C,F5,8A,B9,EE,5E,B6,7F,4B,9E,E4,03,40,3D,20,C4), + wwH16(42,F6,02,9A,47,41,EA,A6,C8,2E,30,3C,FE,50,D1,FE), + wwH16(2B,7A,47,A4,A0,1F,92,82,27,10,12,95,69,3B,BC,CB), + wwH16(A4,9F,06,2A,5F,BB,29,D4,8B,4D,F9,3E,04,6C,20,C6), + wwH16(7A,7E,D2,6C,F7,E0,4C,CE,5A,AE,77,31,1E,D4,C7,CC), + wwH16(06,D3,97,04,8A,25,ED,5A,D8,5D,A5,90,0E,C2,6C,2D), + wwH16(1E,E0,49,9A,E9,16,E1,59,5F,31,F4,CC,6E,4E,A1,4B), + wwH16(9D,51,EE,28,3C,D9,2B,B6,68,14,6F,39,DF,16,E6,66), + wwH16(BB,E1,31,DB,4C,0E,60,05,64,E4,53,AA,28,F4,29,FC), + wwH16(96,BC,65,56,FA,79,68,96,B6,D2,7E,75,27,E4,D4,17), + wwH16(E8,37,1E,98,4E,82,97,1E,F4,F0,07,55,8F,94,9A,A7), + wwH16(45,01,94,3A,F4,D3,84,F5,77,93,58,F9,CD,59,57,73), + wwH16(CD,A5,39,18,49,FD,69,48,37,D7,A4,63,D3,83,26,13), + wwH16(2D,FA,B0,B6,1A,A1,1B,68,5D,6F,1B,22,3F,5D,DC,EB), + wwH16(9B,B1,87,20,BC,89,C4,1D,26,49,E0,EE,E4,4E,8D,89), + wwH16(8A,A7,80,C5,ED,B8,D4,77,77,7A,C0,A9,2A,02,FC,F3), + wwH16(F5,72,15,CF,A3,06,3D,F8,79,C5,6C,87,3B,15,1D,9B), + wwH16(7A,FE,FD,42,F3,09,05,14,FF,42,64,2C,B3,4C,F0,9C), + wwH16(A1,29,28,E4,FC,AB,FD,00,F3,2F,49,94,DF,2C,BC,67), + wwH16(61,11,69,1D,10,CB,D4,D1,24,A4,92,5C,67,8A,95,0C), + wwH16(04,CB,44,9E,33,68,6D,AB,BB,CF,59,D4,1E,28,C5,C5), + wwH16(7B,06,BD,F0,7A,E3,EB,A1,A8,83,6A,F2,F2,65,E8,0C), + wwH16(AB,58,12,1D,36,3D,64,08,09,E0,00,05,6D,29,F8,EF), + wwH16(EA,57,1D,85,9B,B5,BF,32,A6,78,BB,71,C8,C3,57,E9), + wwH16(F7,2C,14,05,25,72,3E,9F,43,1A,39,3F,00,E4,7A,DD), + wwH16(A0,B0,18,B5,5D,56,83,29,EC,88,12,2A,FC,D0,AA,62), + wwH16(C8,9A,42,66,EF,FF,19,7C,FC,AB,39,5C,38,5C,02,A8), + wwH16(7F,A7,73,C7,44,33,82,95,0B,02,92,59,87,7F,02,93), + wwH16(38,E7,0E,80,C9,B3,99,6C,83,82,F7,D5,8A,D3,15,80), + wwH16(26,E4,A0,33,D6,4A,F7,C7,85,E6,75,6D,A0,F0,87,3F), + wwH16(29,78,7D,02,15,C8,36,1C,0B,D5,F9,CC,5B,A0,79,45), + wwH16(EC,B4,48,DB,9E,2A,72,9E,25,DA,A3,98,D3,C3,1A,60), + wwH16(14,C1,E0,AE,4B,8F,34,B2,82,6A,40,87,83,3D,DB,D2), + wwH16(A5,BF,71,D6,20,3A,72,44,E8,70,93,13,54,5E,3C,BB), + wwH16(03,90,3E,D3,0A,FC,0A,84,95,21,BE,6E,32,5A,55,A8), + wwH16(ED,18,F4,10,4A,C0,20,8B,A2,41,E7,14,48,9D,78,B6), + wwH16(ED,DF,C6,26,A5,26,D4,74,95,36,4E,C6,65,A1,40,54), + wwH16(66,8F,B0,B3,A7,4C,9B,02,68,2B,89,68,88,28,27,12), + wwH16(AD,36,41,BE,44,8F,DC,50,74,62,76,43,B8,8A,A5,3F), + wwH16(E4,5F,09,05,DD,84,EE,31,66,4D,AC,99,CF,26,4E,8C), + wwH16(57,BC,8D,15,E7,9A,A1,68,4D,1F,F2,FA,F2,BD,4A,6C), + wwH16(AE,F6,6A,F8,57,1B,48,1A,03,02,74,CF,8E,95,2E,DA), + wwH16(74,16,4A,02,DD,49,9C,68,E5,3A,50,D1,BE,C9,E8,F8), + wwH16(12,69,02,7F,86,66,75,A4,48,91,61,56,B3,FF,CC,D4), + wwH16(8B,0B,DA,AC,33,40,44,22,34,AF,44,C4,5E,7E,65,35), + wwH16(BA,B2,FF,01,07,DC,94,91,BE,19,62,03,6F,48,C0,EC), + wwH16(EB,51,1C,A3,A8,D1,D9,F4,13,0E,85,9C,1C,36,E3,AE), + wwH16(BD,4C,FD,7B,C0,B4,C6,85,31,52,3A,BE,41,35,2D,54), + wwH16(3A,EE,C9,D4,D8,39,CE,EE,4E,EC,D5,D7,56,1C,B5,FA), + wwH16(0C,04,B2,34,57,A4,1C,ED,D2,79,7F,25,A3,23,C9,5D), + wwH16(CF,84,50,C6,DF,7D,5D,65,2E,6D,16,6C,22,0D,D1,A2), + wwH16(DF,80,8E,03,E6,A8,01,A4,B3,82,AB,55,79,A4,9D,2A), + wwH16(30,B3,83,E7,3F,C1,35,3D,C5,99,DA,D1,EC,58,75,DD), + wwH16(10,62,54,6C,8D,A2,F2,66,99,71,28,A3,F3,4C,D1,A0), + wwH16(13,41,96,54,87,B7,8C,37,02,C2,23,DD,04,5D,D1,C0), + wwH16(2B,E9,ED,E1,B2,BF,0F,DC,31,DF,8B,1B,5D,F3,F9,58), + wwH16(64,5F,11,85,B4,04,10,78,DC,65,F5,AC,AF,FE,5A,73), + wwH16(79,77,9C,7D,B5,59,91,4D,1B,4D,07,C0,AD,C6,8E,30), + wwH16(C8,3F,8C,71,D6,D6,87,49,E4,ED,69,A4,0E,D6,A7,A7), + wwH16(CA,4A,EF,7B,48,B7,67,1F,9F,AD,11,19,69,BA,0E,8B), + wwH16(B3,CE,74,C9,DC,BD,0D,86,62,A2,22,EE,F7,C2,63,09), + wwH16(7D,7D,FD,09,73,4D,54,8E,6E,F2,F4,02,D4,A9,74,5D), + wwH16(D1,CB,08,95,ED,E4,2E,2C,74,61,93,BC,32,11,29,27), + wwH16(D3,8D,88,CB,BB,99,D7,7B,97,C0,09,33,19,2D,4C,91), + wwH16(00,11,BA,85,52,97,84,4B,1C,66,A2,A8,5E,6F,D3,D2), + wwH16(E3,53,9C,02,77,A5,CC,5A,51,67,1B,0B,D8,97,46,B8), + wwH16(9A,4F,50,33,19,81,D7,4D,24,E5,A8,5C,DC,E4,E6,44), + wwH16(38,39,5D,CB,FC,EA,FE,C0,E4,94,1D,45,97,E8,F5,7C), + wwH16(39,91,9B,28,33,86,B6,08,A8,93,CB,F9,2B,FB,50,62), + wwH16(48,5E,7D,57,D1,7A,3B,8B,D1,BA,2D,94,C3,0B,2C,4C), + wwH16(92,82,5A,11,6F,BB,C7,B3,26,B7,DF,2F,2D,35,34,8D), + wwH16(07,F1,69,3E,0B,BA,35,31,6F,C1,24,68,2A,AE,39,12), + wwH16(7C,4C,BD,82,9E,B8,0D,6F,4D,80,48,F2,E7,7A,B9,F4), + wwH16(6F,A8,01,21,F5,8D,EF,EC,5A,C8,2D,51,3A,8B,34,06), + wwH16(06,55,AE,22,2E,79,BE,87,25,CD,77,ED,7C,92,3A,9C), + wwH16(8C,C1,10,A5,05,26,9D,45,92,F3,72,98,9E,A8,9C,04), + wwH16(F1,02,BD,9B,21,30,DF,DF,E7,F2,D9,A5,FB,E9,10,FA), + wwH16(D1,EF,45,9C,0A,46,7F,88,84,B0,E3,C7,2F,0F,02,CE), + wwH16(07,8A,89,07,EF,4E,05,57,9D,87,5E,67,E4,02,10,29), + wwH16(17,91,5C,73,C4,F1,10,33,99,FE,00,80,C8,D7,EF,4B), + wwH16(82,75,C1,28,2D,B0,A4,61,B8,02,5F,06,D2,AE,18,A4), + wwH16(28,14,FC,2D,9A,12,B2,4C,2C,86,F9,AC,41,A6,89,D4), + wwH16(6B,96,A9,68,94,77,59,B9,A1,39,47,2E,2F,C6,A1,CA), + wwH16(6A,02,E8,F6,4B,A7,69,7D,6A,27,71,62,A0,3F,A1,BF), + wwH16(D3,E9,DF,63,B1,4D,A8,BE,A0,28,AB,B8,95,F9,6D,A5), + wwH16(6E,E4,27,41,81,7C,52,18,75,21,C4,38,FC,96,5C,21), + wwH16(94,9C,49,85,8E,A6,47,7D,44,6F,CC,2A,03,57,55,7D), + wwH16(91,A7,F0,EA,16,AF,BB,62,41,C4,5E,FA,03,01,61,D5), + wwH16(4B,09,56,F8,A6,C2,26,AD,BE,B8,80,B2,1C,9C,5D,AF), + wwH16(3A,64,7B,65,32,7C,8A,AB,F3,4B,38,6F,32,AB,E2,CA), + wwH16(8F,36,36,A5,FB,DD,C6,FA,AC,EE,31,04,18,62,C6,45), + wwH16(6D,59,06,D2,49,2A,05,D1,95,B0,95,AD,D2,1B,F1,78), + wwH16(A0,09,2A,31,0E,30,CD,6E,87,F9,1B,B9,7B,82,07,4C), + wwH16(60,F1,4D,7B,37,AC,41,52,A3,1F,3A,DD,70,AC,3D,73), + wwH16(52,A2,70,B7,C8,62,7E,FE,AA,CF,12,CB,76,C9,BE,E3), + wwH16(0B,D6,29,E0,8A,20,E4,7B,E7,E6,8E,8B,4F,97,E6,54), + wwH16(19,78,6F,F7,58,48,31,83,86,F7,89,09,7D,2C,3D,CC), + wwH16(41,CA,68,0A,CC,94,00,BD,EC,14,55,07,1D,80,DD,0E), + wwH16(C2,EF,56,B3,93,4D,0C,52,66,99,EF,43,AE,C6,66,8A), + wwH16(20,FC,84,77,44,36,C9,5C,3F,7C,9D,8F,48,54,A5,F5), + wwH16(28,B0,82,8F,C1,1E,CF,02,C1,60,14,B7,C6,37,64,B0), + wwH16(30,81,90,8E,B6,77,72,F9,06,40,BB,EE,31,3A,2B,40), + wwH16(F2,E3,09,86,78,BF,C8,B8,06,2A,16,51,1A,AC,90,21), + wwH16(9E,E5,E8,7F,30,5A,83,80,AA,ED,BC,91,23,C5,5A,C3), + wwH16(C0,C7,D1,2C,6A,4C,13,E9,B8,C5,17,58,AD,F1,03,84), + wwH16(79,C6,B1,13,65,DA,59,7E,60,5D,BD,87,A3,A1,A9,F7), + wwH16(F9,B6,40,99,B5,B5,11,84,96,D4,2F,16,60,51,F5,56), + wwH16(A0,E1,39,1C,AB,74,BE,EF,32,1B,16,7D,E5,B9,EC,9A), + wwH16(F5,3E,32,2A,61,5E,90,0C,00,48,65,F7,16,66,4A,FD), + wwH16(A5,66,E6,89,E2,2E,55,26,0D,3C,B4,C9,C6,50,F6,49), + wwH16(7E,1C,77,57,45,51,BF,B5,A2,F6,8B,8E,24,5F,9A,B2), + wwH16(CF,6F,4D,41,D7,1E,7F,68,A0,DA,17,54,9E,50,C0,20), + wwH16(5F,9A,3B,2A,A3,CC,30,78,11,6E,E2,B5,AF,81,2F,7E), + wwH16(12,72,D1,C4,40,F3,7A,ED,78,C0,C5,4E,39,DC,21,F9), + wwH16(50,BB,D4,6D,F1,BA,CC,15,91,16,57,9C,6B,46,9C,F9), + wwH16(E9,9D,03,E2,BB,51,26,70,D6,34,F0,1C,81,06,DE,BD), + wwH16(AA,6D,90,C9,40,CF,48,79,2F,9E,37,C0,3D,C4,02,47), + wwH16(EF,B9,00,F0,B6,6C,07,98,E0,1D,E8,DF,F0,8C,5A,36), + wwH16(A0,20,D5,7E,BF,39,05,58,29,C9,52,C3,BF,29,82,B7), + wwH16(95,12,82,4B,09,6B,70,E7,84,5D,FE,09,DB,A4,B6,6D), + wwH16(4D,3C,36,E7,B1,F4,0F,13,30,60,F9,95,AD,F4,A6,F7), + wwH16(AB,55,7B,A4,3C,DA,D3,4E,B3,59,40,AD,F9,03,57,A7), + wwH16(10,C1,9A,6D,A6,8B,61,28,CE,68,C6,1D,77,60,6C,E2), + wwH16(C4,35,E2,E8,09,6B,36,9C,7E,7C,C0,97,EF,78,C0,E9), + wwH16(6D,8D,60,7B,44,05,82,2D,2F,DD,54,37,37,72,ED,DE), + wwH16(EB,C4,EF,C1,11,CD,FE,9F,37,FC,4F,34,E9,83,7E,38), + wwH16(2A,5E,28,95,BD,6D,E1,97,61,1F,F7,C6,3C,01,8E,00), + wwH16(00,15,2E,1F,9F,17,01,C3,F0,EB,95,61,6D,DE,EA,37), + wwH16(28,57,EC,3A,98,8E,36,C7,41,FF,19,55,93,2C,5B,7A), + wwH16(5B,12,C0,9F,BE,79,FA,9C,16,74,52,07,E1,63,0D,7E), + wwH16(AC,CB,91,0B,50,78,EC,39,D0,24,FC,05,4D,03,72,AB), + wwH16(16,2C,88,B3,A8,8E,5D,B6,66,B4,0C,DE,C6,66,8F,4A), + wwH16(06,6C,19,C0,8F,7B,F0,07,BB,70,64,58,B1,FE,82,54), + wwH16(AD,17,0E,19,42,99,E6,F6,C6,E6,54,6C,AF,87,86,AC), + wwH16(DE,4E,2F,55,1D,71,C2,2B,D9,59,DD,2E,21,72,D5,D9), + wwH16(BF,FD,45,6A,FA,B8,9E,27,71,4A,27,5B,46,02,EB,EF), + wwH16(92,00,EA,78,C1,D2,06,D4,B7,16,EC,8B,1A,60,C0,01), + wwH16(01,E0,85,26,69,8F,09,4B,77,E2,DC,8D,AF,49,57,2A), + wwH16(48,C8,AD,4D,F9,84,99,18,E8,11,DE,FD,59,16,27,95), + wwH16(EE,DB,36,0A,FA,5C,9C,B7,A9,B9,48,DB,36,CF,F5,F7), + wwH16(59,C0,24,EC,65,8C,B6,DA,51,E4,84,4A,3A,C8,0E,B2), + wwH16(8D,D1,39,51,C8,4D,03,1B,C8,AA,BA,A2,48,DE,A9,11), + wwH16(44,7B,9D,40,FF,AE,59,4A,B5,07,5E,2B,5A,34,7C,60), + wwH16(0F,17,BE,E4,7C,17,B5,B1,14,69,0A,C7,A4,88,5B,7A), + wwH16(D6,21,73,23,FC,64,E4,E4,63,BE,69,A5,41,A3,4D,B7), + wwH16(67,DB,12,54,AE,30,5F,4C,2D,4F,81,7A,8F,E9,D7,73), + wwH16(14,80,E6,A0,B7,6B,35,13,44,D7,03,39,C0,DD,86,4E), + wwH16(A7,D8,1B,45,E7,0B,7D,3E,06,9C,7B,A3,5A,5B,B0,CA), + wwH16(B7,E5,D9,20,41,29,83,2A,29,1B,9A,48,34,5F,41,31), + wwH16(2E,90,0A,11,CE,3E,E7,A4,92,3E,EE,EB,6C,38,A7,F8), + wwH16(D7,BD,3D,C5,DB,6F,C2,C4,F9,97,A5,2C,A3,8D,CA,DB), + wwH16(25,5B,96,67,85,A0,10,FF,9D,A3,F9,4B,97,D4,5D,3E), + wwH16(5F,3E,75,32,47,2C,E8,62,C8,17,3D,43,D8,32,92,8B), + wwH16(28,B8,6A,9D,BB,1A,BA,B5,B1,20,63,14,A6,5E,FF,F3), + wwH16(B2,82,16,98,EA,B3,F2,E2,9B,47,56,4E,8A,B3,01,3F), + wwH16(5C,64,99,55,6F,B2,D4,0B,F6,71,AA,9E,AA,2D,3B,F0), + wwH16(36,06,EB,73,49,A1,54,03,78,57,82,AF,BC,5B,37,73), + wwH16(54,F3,15,D7,49,51,B2,C2,35,58,63,71,32,F9,5B,34), + wwH16(70,8A,BB,56,D2,09,68,91,6E,05,23,3A,4E,BE,A1,4F), + wwH16(EB,39,81,A9,78,20,E8,9C,99,00,8E,A2,6B,44,15,15), + wwH16(20,5A,30,B9,E4,E1,1B,56,85,8C,72,99,EF,39,CF,BE), + wwH16(6D,3E,06,14,9E,DA,B4,B7,F6,45,97,9B,0F,A9,C5,F1), + wwH16(09,75,A2,1B,95,00,95,20,D6,F7,A1,52,EC,F3,01,17), + wwH16(AD,6A,03,A2,F3,A4,FB,33,02,81,02,33,7E,36,53,18), + wwH16(28,37,43,09,B2,13,F5,84,67,FF,80,6D,7E,DB,C8,97), + wwH16(76,93,60,F4,D5,86,30,F9,D2,A8,05,11,C1,5F,48,0A), + wwH16(2D,0B,8C,24,B8,11,DA,3A,6A,0E,E0,C0,94,78,69,DE), + wwH16(F7,6C,B6,7D,56,7E,1B,9B,34,13,A1,87,F1,87,B2,87), + wwH16(63,A1,E2,9B,96,5C,2B,CE,96,D9,55,4C,B2,0C,0B,84), + wwH16(28,A8,37,D0,F8,06,5B,78,3C,2C,01,69,49,BB,5B,55), + wwH16(1E,31,10,54,6F,A8,B5,42,C8,58,43,13,42,89,96,77), + wwH16(BE,E1,B5,B9,08,04,31,EB,CD,9D,EC,33,18,11,8F,79), + wwH16(B7,ED,FD,BF,47,84,DB,F5,B8,B2,43,B8,93,B7,24,F5), + wwH16(77,2B,2C,2E,E0,BC,AF,80,C3,9C,68,1B,FE,BF,26,CE), + wwH16(31,A8,CA,DF,B2,56,0E,E6,38,1A,6B,70,42,5F,E8,56), + wwH16(1C,83,B1,70,3A,BC,33,14,0E,5F,51,3E,7F,95,2E,32), + wwH16(08,31,49,7D,13,FE,52,15,78,27,74,5F,45,77,3E,CF), + wwH16(36,00,28,A8,F0,F8,70,7C,3A,CC,C8,9C,26,98,63,D3), + wwH16(94,B7,D4,90,85,3E,67,04,96,AB,C7,DD,4E,3C,CE,A8), + wwH16(40,0A,AE,66,FB,6F,C0,D1,89,D1,27,0A,21,D7,1F,48), + wwH16(1F,6E,B9,36,BD,19,66,16,40,1F,FF,53,3C,88,A9,3E), + wwH16(D4,F3,06,75,43,2F,2A,DA,B0,76,2B,F2,11,3D,02,79), + wwH16(32,EF,A4,20,F1,58,E7,D4,E6,3A,06,73,28,5E,C0,27), + wwH16(2C,EC,A6,A8,FE,F6,8C,1B,9C,FA,FA,16,42,B8,ED,5F), + wwH16(C3,42,2F,EB,E8,35,1E,04,31,75,4D,EC,A8,68,8C,87), + wwH16(E7,00,7A,47,F7,A2,99,CB,65,EE,BE,B6,5C,C2,FA,E5), + wwH16(1F,23,A2,3D,83,79,61,5F,EB,8E,01,F2,C6,C9,48,86), + wwH16(27,28,9C,49,FC,63,2B,60,A7,49,53,67,71,BA,71,DC), + wwH16(C7,6C,82,5E,68,60,49,33,94,A5,EE,49,86,0C,32,72), + wwH16(D8,8E,88,91,4F,67,A5,4B,CE,DA,CD,2D,06,BF,D0,62), + wwH16(1D,6F,35,6E,06,80,B7,A8,AC,7E,08,EE,BF,9A,81,AE), + wwH16(4F,97,4C,BE,12,CB,97,82,5A,EA,F7,9A,CF,E3,CE,02), + wwH16(16,67,54,13,15,53,2A,DE,F2,3A,30,65,C0,05,3B,E2), + wwH16(05,68,79,1E,66,55,D1,ED,8A,7A,FE,F9,88,2C,EA,F0), + wwH16(A4,8B,23,5C,19,2B,BD,80,5A,E4,72,23,0E,C3,3A,96), + wwH16(68,0F,13,33,E2,A2,D8,8E,EC,64,0E,DA,F0,D1,5E,36), + wwH16(B0,B0,6B,59,E6,F6,1A,25,51,BB,30,36,4C,E2,0D,3E), + wwH16(73,E0,A9,A4,4E,E6,96,F7,F9,76,F6,D7,E5,E8,C4,D3), + wwH16(5D,5F,B0,FE,C7,17,6C,DB,B4,B4,11,7F,16,8A,FE,49), + wwH16(FE,FB,B5,4E,B5,25,D8,67,64,03,AE,B3,E1,8C,43,58), + wwH16(E6,83,4F,66,8C,67,81,44,58,9B,E6,64,06,C4,90,EF), + wwH16(05,66,BB,B0,84,14,0A,64,50,03,3F,E9,DD,81,E5,20), + wwH16(91,EF,6C,7C,43,59,5E,D7,C2,CA,94,7A,7F,7B,75,F4), + wwH16(2B,1D,8F,44,2D,DB,77,F9,C9,07,82,77,03,E1,68,AF), + wwH16(C5,28,92,22,89,8B,C5,38,0F,90,6C,45,7C,9C,25,F2), + wwH16(E9,7A,C2,D7,31,F3,93,F0,59,F3,B7,E8,66,66,EE,81), + wwH16(DD,80,56,81,9F,74,AC,66,12,BF,80,7F,E0,69,1B,46), + wwH16(1B,10,D8,18,B3,48,6D,B3,DD,45,8D,F5,9C,20,8B,38), + wwH16(1C,E0,C3,79,7D,4F,73,C8,3D,CD,42,03,D1,80,67,41), + wwH16(C4,D7,92,A9,64,41,E6,F9,64,8E,EA,6D,29,9F,81,31), + wwH16(B0,79,FB,27,14,7F,D2,20,5F,F3,6B,6D,05,FB,7C,97), + wwH16(ED,FF,EB,81,DD,C5,A2,AD,EB,87,B2,3D,E2,5D,33,05), + wwH16(3B,90,20,F6,3F,E4,DB,C1,D7,30,3A,A9,63,2E,89,6C), + wwH16(F5,7D,38,E1,DF,54,3F,24,C8,96,D6,0D,EC,CE,08,85), + wwH16(4F,F9,00,69,05,07,CA,93,AC,95,C2,33,16,DD,95,84), + wwH16(7F,0F,D7,21,9F,2E,EE,2E,F8,90,DE,F5,EA,52,AB,9C), + wwH16(EB,A6,B8,A5,12,6F,16,0F,D9,63,35,DE,AF,43,75,9B), + wwH16(EB,33,D4,67,CD,8A,63,BE,7B,26,FA,ED,5B,D6,70,83), + wwH16(0D,E7,D7,FB,51,13,60,E0,F8,88,B6,BD,E4,AA,3B,CC), + wwH16(C1,70,62,6F,3F,6C,6F,77,29,4A,A7,5B,6C,18,88,4B), + wwH16(92,BF,CD,FD,F7,E4,DF,E8,FA,9D,39,5A,A2,2F,57,BD), + wwH16(9B,7D,B9,9B,E0,12,1A,96,48,02,7F,A6,62,1C,E3,2A), + wwH16(19,9B,45,4C,7A,11,EF,BE,6B,A4,84,8B,FB,C2,D5,09), + wwH16(F1,F1,B8,57,DC,E9,09,94,AF,A5,77,BF,05,AD,51,37), + wwH16(46,B7,15,3D,3D,E6,73,AA,C3,51,8C,75,1A,C4,C2,48), + wwH16(2F,DD,C3,A7,6F,E8,30,DF,EC,9A,00,2F,42,ED,F6,05), + wwH16(80,45,DC,AC,76,64,F4,4C,04,46,F1,F5,46,0B,99,B1), + wwH16(A4,19,60,70,70,A4,33,CD,C8,CA,AD,91,41,02,1D,79), + wwH16(EE,72,18,0E,C0,E2,49,9E,FB,04,64,6D,B1,A9,FB,CD), + wwH16(28,5D,0D,25,B4,31,49,C2,6B,97,A8,9C,A9,BE,D8,B7), + wwH16(74,CC,C4,E2,0F,CF,00,31,53,BB,B4,77,B2,39,AB,87), + wwH16(69,61,4C,B2,17,82,FC,DF,2A,E0,4F,DF,02,69,6A,70), + wwH16(1F,01,BE,83,64,A0,EC,9B,EF,AE,FB,AE,FF,DC,A5,F5), + wwH16(47,68,E1,BC,2D,D9,62,CC,0A,BF,9F,BC,CD,8D,4C,F1), + wwH16(F5,1C,A9,F8,E2,32,47,85,90,4D,51,ED,50,80,A6,7D), + wwH16(6D,80,55,8C,09,1E,69,10,68,8F,F9,5E,CF,C6,B4,2D), + wwH16(F2,27,96,59,28,FA,61,78,8E,9B,C0,31,46,35,D6,8E), + wwH16(2E,9B,18,13,12,B6,69,BE,C6,23,4D,24,D8,4F,81,C5), + wwH16(D6,77,86,23,BE,56,C4,B1,97,98,52,2A,CC,46,7B,9A), + wwH16(01,10,12,5A,8E,9C,D8,E4,43,34,44,C7,7E,A2,04,24), + wwH16(08,D0,0D,CA,6D,23,06,0A,8F,D2,CA,78,B4,90,B0,D9), + wwH16(0B,8B,BF,E6,20,0F,7B,E1,14,DE,EA,A1,0F,24,6D,4D), + wwH16(84,7D,7E,3D,92,BA,1C,9A,1D,71,EF,72,18,3D,3F,F0), + wwH16(29,13,45,DA,7D,27,AE,C7,50,50,4C,A7,C1,C0,8D,49), + wwH16(F4,10,75,79,06,AA,BD,BD,D7,85,74,4A,3E,2C,38,C4), + wwH16(2F,6E,77,CC,02,C9,24,79,04,2F,96,0D,DE,CA,EB,CA), + wwH16(B0,B9,B8,12,09,5F,73,09,D6,47,1E,84,AC,20,E2,42), + wwH16(C9,0C,41,92,3A,A4,83,17,E7,BC,BC,B3,04,8B,0A,46), + wwH16(43,0F,E3,F0,C8,BA,B6,EC,3A,EF,2D,33,DF,35,F5,0B), + wwH16(50,CC,33,AC,76,5A,FF,61,9A,5C,96,D3,15,51,4D,DB), + wwH16(F9,0D,8A,CA,68,90,6C,CF,D7,CC,03,4F,2A,DB,1C,A4), + wwH16(92,20,B0,5E,4A,ED,0A,97,92,C8,20,B8,7D,11,B1,21), + wwH16(06,28,0B,4B,39,EF,7B,EC,BD,66,C0,C7,07,09,E5,E1), + wwH16(6C,12,AC,6A,A6,EB,82,7B,07,19,F5,BE,7A,12,21,5D), + wwH16(8E,5A,F1,A7,74,34,3A,74,71,CC,60,62,6C,37,41,1B), + wwH16(73,70,9A,D5,F6,B3,E0,E9,36,98,51,EE,03,4A,71,E7), + wwH16(36,65,CC,7B,70,CE,B1,36,C7,E4,36,15,AD,CC,29,E1), + wwH16(8F,22,AF,25,27,CA,E9,0C,4F,68,1E,2E,9D,C5,98,30), + wwH16(2B,71,4E,0C,FD,83,24,2F,97,18,33,F9,F7,BE,8D,C1), + wwH16(1E,AA,7A,80,1D,0C,C0,E2,1E,0E,75,DC,91,28,EF,10), + wwH16(1F,57,5D,E2,C1,B5,C3,7E,B9,D3,A1,AC,F0,DE,71,93), + wwH16(A1,57,75,1F,78,91,F2,9A,D0,E9,75,EB,1A,CA,74,52), + wwH16(D9,27,BE,0B,72,C2,1B,29,11,04,69,72,16,BC,D9,EB), + wwH16(A1,EA,75,8F,F1,70,88,18,87,48,58,0F,55,5B,68,6B), + wwH16(59,98,2A,C9,FF,3C,9A,CA,4B,19,36,B7,C0,42,2A,12), + wwH16(D4,35,6C,BC,1E,5E,74,A8,BD,5E,59,63,65,D9,D1,21), + wwH16(62,94,65,65,00,DC,4E,65,19,81,61,75,72,23,3A,87), + wwH16(4A,D8,34,69,00,F5,E3,4D,F3,93,5A,C0,75,F9,1D,8B), + wwH16(B5,EB,3A,2D,24,05,AA,C6,A0,74,E1,E3,E4,E9,06,E0), + wwH16(43,14,6D,15,58,2A,D1,BF,E0,F2,A5,72,6B,C6,3A,F3), + wwH16(EC,6B,FC,5E,8C,36,56,34,83,95,6D,31,A6,B3,9A,11), + wwH16(DD,94,49,58,89,A0,F1,FB,28,39,2E,32,BB,86,6B,4A), + wwH16(C9,B5,4D,90,D8,7D,06,AB,98,1B,DA,28,38,F5,DF,94), + wwH16(3E,40,FE,DD,FF,F5,DE,1A,0C,74,5F,F2,12,1C,22,EE), + wwH16(2A,34,21,46,D4,53,F9,1A,EF,83,C9,6D,B5,C9,DB,47), + wwH16(B9,C1,53,0A,05,44,B4,1B,22,3C,55,FD,CD,41,8D,05), + wwH16(D4,8B,D8,44,22,0E,BC,0A,8E,9C,41,02,A1,37,3C,50), + wwH16(BB,13,29,27,C9,48,48,13,27,F0,27,A0,BF,62,53,EF), + wwH16(75,93,B1,6B,90,7D,80,7B,73,09,04,3E,DD,DD,89,DB), + wwH16(BB,38,38,00,50,A8,CA,96,C7,3B,A5,28,16,75,00,54), + wwH16(75,BC,10,00,BE,02,72,A1,F2,67,B6,FE,30,0E,E2,8A), + wwH16(80,60,EB,A1,BE,56,41,18,D9,9B,0C,D8,84,69,0C,B9), + wwH16(63,0E,D2,A9,0B,45,64,27,39,2B,8D,91,03,4A,93,28), + wwH16(9C,13,27,DC,DA,79,F4,8E,F8,09,1E,6E,C4,D9,14,CB), + wwH16(C8,C7,FA,D3,F4,56,A0,60,92,04,B9,41,F9,25,3F,1F), + wwH16(AE,97,81,C7,80,DA,40,70,68,E2,B4,DA,2E,80,DB,18), + wwH16(EE,36,76,25,D2,EF,A1,4D,11,42,22,CA,9C,44,04,C0), + wwH16(E7,6F,46,8B,B8,43,50,48,43,A7,14,C0,BC,62,DC,D1), + wwH16(B9,EA,36,0F,E2,66,F7,D7,F2,A6,E1,38,7F,2E,FB,19), + wwH16(6C,6C,4F,4E,86,C8,B8,1E,39,66,5C,D3,AF,C0,26,CF), + wwH16(87,BD,EF,F7,25,DB,20,8D,63,9C,8A,44,03,AF,79,41), + wwH16(B1,AB,6A,6D,74,DF,EA,81,51,F8,B2,78,25,C5,FD,FC), + wwH16(4B,CE,68,3B,8F,2B,95,61,E7,A3,35,AF,C3,B3,74,05), + wwH16(B4,77,C2,A7,5C,11,73,59,77,18,52,3B,CD,E0,F9,FB), + wwH16(5F,8D,CF,B0,88,51,07,42,11,DF,E9,09,4F,28,B3,1B), + wwH16(01,50,E4,DA,8D,9A,BA,58,8B,1D,C7,FB,A1,5E,01,3E), + wwH16(72,34,FA,C4,F2,11,EC,AD,0B,C0,3D,F2,B7,08,CB,61), + wwH16(28,6D,85,AD,35,F7,F2,38,EB,CE,0A,BA,A9,FF,A1,CE), + wwH16(BC,6B,B0,63,5A,18,93,3C,B3,14,0F,16,3E,5F,06,75), + wwH16(74,AF,53,50,DB,D5,82,7D,A2,29,6B,D6,12,FB,6C,A3), + wwH16(A7,90,42,B4,41,80,71,92,FE,91,3B,2B,3C,85,3E,CB), + wwH16(C0,9E,03,DD,75,9B,7A,4E,E1,80,41,89,31,72,02,24), + wwH16(6D,A1,DC,DD,9B,F1,9C,BA,B6,77,3B,80,B5,5E,71,62), + wwH16(4B,4C,B0,67,ED,29,FC,B3,5B,FC,CA,7A,40,69,EC,EF), + wwH16(0B,64,95,05,7A,5F,19,A1,C5,94,DB,E1,8E,F1,55,32), + wwH16(4A,1C,BC,F3,EA,B0,9E,E1,E8,08,2D,B4,16,92,3D,9C), + wwH16(8C,D5,DE,16,26,B6,4A,FA,83,66,83,F3,2F,D6,75,4D), + wwH16(7F,9B,4B,01,87,75,86,56,E4,EF,8D,CE,56,C8,C8,0F), + wwH16(02,D9,E5,A3,57,0A,C1,78,AF,1B,27,0B,9A,63,EC,D2), + wwH16(9C,9B,FA,FF,D2,E1,62,B2,50,D8,02,3A,F0,A5,51,BB), + wwH16(B7,38,FF,07,BB,35,97,6A,0D,6B,D9,6C,50,90,8B,A1), + wwH16(EE,11,91,57,D7,06,47,A3,99,55,92,D9,14,03,5C,92), + wwH16(98,83,DC,FC,02,64,D7,0C,F6,D5,8A,75,6A,7D,D9,0F), + wwH16(53,A2,A8,31,38,21,58,E0,71,12,CE,A3,7C,31,61,A9), + wwH16(63,E1,7D,5D,C5,F6,57,D6,65,CC,73,64,B0,7B,48,3F), + wwH16(76,86,75,3A,37,4C,0F,E1,5A,E4,3C,5B,10,DC,22,E1), + wwH16(E3,A5,2D,16,63,E5,0A,23,00,42,5F,B8,18,25,6B,57), + wwH16(37,3C,A9,92,D1,3C,9B,AA,60,C5,E9,36,28,61,A9,60), + wwH16(24,56,4D,7E,26,E5,FB,F2,FC,E9,D2,86,BD,D7,74,1F), + wwH16(C1,8A,95,10,C1,5B,4A,2A,C3,A9,19,42,03,31,63,5D), + wwH16(80,B0,F5,E4,91,A0,56,CD,79,D2,A0,C6,5C,6B,8A,23), + wwH16(7E,92,F7,75,62,35,19,6E,E0,49,8A,A8,24,A8,5E,6A), + wwH16(C1,7E,5E,D3,23,69,F5,4B,4F,32,FC,60,C0,CC,24,ED), + wwH16(B1,69,25,D6,44,04,33,23,C1,E4,23,0F,EC,0A,26,D9), + wwH16(38,68,4E,AB,E5,9D,00,AB,E4,9D,24,86,04,9F,1E,E7), + wwH16(0C,4E,18,E8,2C,6C,4F,81,5A,DD,25,CB,B2,CD,71,5B), + wwH16(82,A3,5C,41,A4,3D,B0,4E,A7,FB,84,84,D6,8A,52,0C), + wwH16(1A,EB,15,18,05,3C,BC,EE,10,15,9E,7C,AF,05,34,7A), + wwH16(9B,A1,6B,BF,9B,69,FC,BC,05,F5,97,CB,E4,1F,7F,07), + wwH16(51,B9,7F,00,D4,7E,F4,07,40,65,CC,A6,27,28,8D,98), + wwH16(10,9C,0D,E3,B5,D5,A7,37,D2,E3,D6,9C,87,1B,89,AC), + wwH16(32,B9,DA,EE,75,85,5E,EC,19,12,8A,1E,E7,8E,C1,3B), + wwH16(57,4C,3E,AA,D8,1E,48,92,D6,38,68,04,60,17,22,8E), + wwH16(1F,00,89,76,85,1D,7B,F4,41,0E,26,DD,B6,5E,5E,62), + wwH16(90,7F,94,D7,6B,E8,96,13,36,F7,93,CB,EC,25,7B,03), + wwH16(9A,AF,87,2E,12,4F,AD,C0,2A,1E,59,D7,29,A8,D6,13), + wwH16(48,FD,B4,BB,FB,A0,ED,CD,36,8D,D9,83,67,56,A4,B9), + wwH16(E4,31,F5,7E,51,95,4C,29,35,B1,4B,D2,B9,1A,59,73), + wwH16(C5,DC,1D,91,D3,3A,86,B0,1B,26,E3,AF,C5,F4,F0,05), + wwH16(56,85,52,AC,12,ED,2F,65,F8,1A,27,06,28,DA,D3,6C), + wwH16(7C,19,CA,7D,88,52,C9,E8,59,6E,06,22,EA,D0,56,E0), + wwH16(3A,B5,49,25,F7,64,2B,AC,8F,E6,E1,E2,C3,1A,46,23), + wwH16(7D,53,36,DE,D1,B8,E6,70,58,4D,C5,24,36,3C,01,F7), + wwH16(7B,A3,54,D6,75,22,4C,F6,7B,8C,2F,BC,8D,06,E6,52), + wwH16(40,3A,1F,75,BA,AC,19,FE,E1,FA,82,4D,A7,41,D0,1C), + wwH16(0A,F9,58,3B,59,D4,86,C1,11,19,49,5A,BD,3F,D1,55), + wwH16(FF,DC,CE,67,89,38,F8,A0,BB,E5,66,F6,4B,FA,B5,11), + wwH16(56,F5,EC,C6,12,63,77,EE,5E,39,7E,82,5A,9B,F7,58), + wwH16(7F,95,5A,52,41,08,64,E8,E8,76,A7,AA,49,41,1D,57), + wwH16(8F,1B,88,19,07,3D,1D,DD,CB,14,60,80,90,1E,0B,B5), + wwH16(15,4B,B9,56,BC,9F,BD,A3,BB,3E,35,FB,D9,0D,55,1D), + wwH16(DA,F4,53,B8,4A,53,A7,DE,00,C9,A9,D6,3C,48,7D,78), + wwH16(6E,A9,6C,C8,1D,6D,7D,D9,7B,96,3B,3D,BD,5C,C8,13), + wwH16(14,D4,60,40,E5,89,55,85,A2,5A,73,28,E0,41,0C,DB), + wwH16(49,AB,0C,F9,61,F8,C2,00,43,2A,7C,FB,20,15,D2,8C), + wwH16(A9,2F,52,F8,7A,60,48,29,44,91,56,6E,9F,BC,D4,2E), + wwH16(6F,2E,80,E8,8E,6C,C5,84,2B,34,2D,F5,60,25,21,FE), + wwH16(14,07,16,42,A0,17,03,57,66,DA,CE,20,7C,8C,B7,D2), + wwH16(EF,39,13,B8,D7,4E,B9,60,CB,32,DE,B4,AF,89,D5,54), + wwH16(79,C9,17,B8,3A,30,A2,15,5F,4A,CE,8D,82,80,EC,82), + wwH16(38,80,9B,B6,15,E7,70,76,EA,79,71,11,8F,BF,E4,FC), + wwH16(1D,38,3F,9A,F6,EB,42,01,BA,52,6D,C5,58,AA,E7,FB), + wwH16(9C,0C,4C,24,44,C8,02,0A,1D,7E,1A,40,09,C3,A6,D2), + wwH16(DB,06,CB,10,DE,53,F6,B8,F4,1D,3E,24,EE,33,41,1F), + wwH16(C1,2F,D4,D8,C3,97,E0,4B,8A,0A,27,2F,76,FB,11,77), + wwH16(01,E5,60,74,BC,FB,6F,40,E8,A8,D6,95,D7,55,16,5F), + wwH16(56,7A,98,C8,3E,7B,2C,F7,9E,28,93,53,FB,B6,A9,82), + wwH16(C6,24,00,48,F7,E7,43,1C,44,25,03,CD,95,12,25,28), + wwH16(4A,40,EF,38,30,6F,17,B5,43,74,30,3B,79,BC,B1,F3), + wwH16(C4,DF,DB,96,ED,00,9C,E0,E4,7C,89,A7,16,0A,42,96), + wwH16(99,6C,F7,9E,0B,0A,01,8A,2D,A0,FA,E9,C6,F2,5A,E8), + wwH16(0A,A7,03,A1,40,CB,F6,3F,8A,B5,E9,04,0A,2D,55,1F), + wwH16(AF,03,1B,12,71,AD,70,7D,6E,D7,D2,66,06,42,4D,FF), + wwH16(14,2A,67,E4,43,C8,DB,B8,AD,20,98,9C,4C,64,7A,6E), + wwH16(1F,2B,88,E8,26,9D,4D,0E,A9,27,53,87,A8,28,F0,94), + wwH16(5D,F8,7D,28,ED,8B,FD,24,29,36,E9,71,52,56,7B,1C), + wwH16(00,A1,9E,5A,F1,17,A7,C9,0B,B9,94,AE,CE,70,FF,CD), + wwH16(75,FD,2E,64,9F,EE,B4,F9,82,7B,2D,C0,C7,4A,22,88), + wwH16(83,95,F8,77,29,09,2C,20,BE,EA,D5,1B,42,A1,C8,90), + wwH16(77,72,C1,0C,27,8B,A1,4A,CE,7D,87,7D,3C,59,8A,70), + wwH16(76,F9,03,FF,52,54,87,86,6E,B9,91,A2,32,EC,78,9B), + wwH16(9F,70,1C,AA,D0,39,68,75,D5,82,E8,3B,E8,68,B2,9E), + wwH16(21,DE,A8,25,D0,FA,14,1D,37,18,A5,7E,94,95,4E,BA), + wwH16(83,0E,5E,D9,EE,83,38,6E,F6,43,65,67,6D,CC,1E,54), + wwH16(8B,00,03,1B,79,B0,78,41,24,1C,01,BE,19,B9,0B,15), + wwH16(29,0F,2D,4D,75,9D,DF,DE,4E,D4,5F,6D,00,A5,C0,D6), + wwH16(1C,21,67,C6,B4,FE,88,94,43,BB,D9,46,6B,26,EE,2D), + wwH16(BF,D3,1D,2B,B4,53,60,43,03,29,05,03,A8,94,CB,32), + wwH16(C2,D4,91,03,79,1D,7B,5A,CC,6E,80,FC,10,E7,C6,B4), + wwH16(DC,CA,4A,79,F2,AA,D3,59,33,4C,7F,68,68,00,F2,3A), + wwH16(F7,BB,3E,1B,81,FE,E2,EB,3E,DB,84,3C,BE,C4,32,8E), + wwH16(F4,A0,60,7F,C3,15,BC,72,67,B5,EB,39,22,2B,5D,C3), + wwH16(A4,6E,46,52,D2,ED,D6,DE,22,79,C9,F7,21,70,89,11), + wwH16(3C,25,24,A5,49,91,0F,32,F1,2C,A5,C8,C7,72,7E,C8), + wwH16(85,26,30,D3,69,7E,1E,1F,7A,9B,69,56,85,54,08,03), + wwH16(EF,5C,27,95,12,BB,7A,9F,B2,96,E4,6E,E0,B7,E4,0A), + wwH16(6A,DF,0B,22,51,92,1D,0A,26,BC,C3,7B,C8,37,5E,10), + wwH16(07,A6,2F,66,59,79,B2,42,CB,D2,4F,BE,AC,03,67,F4), + wwH16(44,C2,E8,8C,CA,2B,03,A6,1C,4C,E7,FE,27,95,CE,47), + wwH16(D6,B4,27,CC,03,75,6B,BF,EA,00,59,35,83,C8,9C,D6), + wwH16(93,0D,F3,4A,BC,C8,FD,72,44,B4,E5,FF,1A,EC,D9,6B), + wwH16(C7,FD,27,51,F9,68,2C,BD,56,FB,24,3D,EF,A4,EF,A0), + wwH16(D6,90,70,0F,B1,CE,F4,53,29,4D,D9,34,BA,46,11,37), + wwH16(89,59,10,F3,3A,C5,E2,BE,8F,02,1B,99,EC,1C,2E,77), + wwH16(70,17,C8,FD,05,6A,F8,8A,37,82,CD,70,DE,D3,A8,F1), + wwH16(21,3F,D8,DA,8A,D6,33,52,53,39,DC,09,97,94,EE,40), + wwH16(EC,01,3B,FA,47,CE,0C,32,B1,06,60,D0,CF,11,AA,83), + wwH16(01,E7,F7,B7,1C,E9,71,0F,62,4B,55,55,7F,2B,30,2C), + wwH16(4D,B2,9B,38,BC,47,7C,8C,01,B6,78,5F,CF,B3,F4,42), + wwH16(1D,92,BE,C7,41,A6,CE,B2,E1,9E,CB,7E,45,2B,D4,58), + wwH16(22,A3,4D,CB,D4,9D,BA,E4,5C,EE,5C,30,BD,DB,EC,7C), + wwH16(63,5E,31,05,A7,D6,C0,DF,BB,E7,64,6D,8C,99,63,A4), + wwH16(95,06,6F,E6,ED,CD,07,40,62,55,0F,8A,B1,24,3B,39), + wwH16(27,1E,82,99,A5,19,30,45,30,DC,DF,C2,BE,B4,56,B2), + wwH16(EA,43,9C,51,9C,8D,07,EE,50,35,C8,72,16,92,A3,76), + wwH16(AB,E8,3E,35,5F,14,B2,15,B7,05,CB,0F,45,6B,59,B3), + wwH16(29,04,CD,E2,C3,A9,53,AF,AA,DB,AA,F6,A5,20,43,AC), + wwH16(55,4B,08,F2,D2,F9,B4,42,0A,F6,B2,40,35,27,B8,6D), + wwH16(AB,0B,7C,D4,BD,CD,E7,FA,D8,BE,81,1E,BE,58,98,50), + wwH16(B2,49,0B,39,14,23,23,BE,85,3D,71,4B,26,27,52,64), + wwH16(2F,CE,F7,36,56,6E,CC,27,8C,5D,12,FE,64,19,51,03), + wwH16(EC,32,CC,4B,2E,E5,06,CA,AE,31,D0,8A,16,89,27,5E), + wwH16(30,99,6C,1A,F9,A3,7B,B5,C2,9D,79,4F,BB,D7,EB,1F), + wwH16(81,10,70,85,A3,6C,6D,EB,4E,0C,62,EC,CD,D4,10,1C), + wwH16(72,BB,85,73,06,9F,03,A0,85,E7,46,8E,1B,E4,6E,B3), + wwH16(A0,94,36,52,68,BF,85,19,16,D9,A3,11,C2,4D,B1,4C), + wwH16(9A,06,37,E4,98,CE,7B,86,90,A3,73,47,08,16,4F,82), + wwH16(89,DB,66,51,0E,7B,4C,18,E0,AF,C1,68,3A,8C,5A,64), + wwH16(48,7E,C1,C6,D9,09,75,39,00,01,99,FA,75,6B,AE,05), + wwH16(AF,E8,13,5B,17,67,7F,8C,21,E2,86,D3,EA,85,AA,0B), + wwH16(B7,19,48,51,49,BC,D1,CD,86,75,CF,12,9F,55,BD,50), + wwH16(CE,19,2D,DE,D0,65,F4,45,29,41,91,50,96,B4,21,10), + wwH16(F0,71,AB,B6,BD,C9,CF,F6,88,E6,FB,65,BB,4F,73,E6), + wwH16(B8,FF,65,A2,18,0F,A5,82,A8,80,78,64,65,93,16,2D), + wwH16(FF,7D,21,74,9E,6A,F2,72,BB,EE,AB,64,7A,C9,F2,FF), + wwH16(D4,F6,9B,79,16,0E,68,70,C9,97,88,F5,B3,54,D7,D3), + wwH16(DF,72,9C,9A,BF,13,53,F0,20,11,BE,96,AC,45,21,B1), + wwH16(56,02,B0,08,B8,20,AC,40,93,03,01,0A,B5,09,8E,F4), + wwH16(6D,FA,3A,5B,E0,DA,38,2A,B4,80,2C,A2,77,8C,61,7D), + wwH16(EB,0A,FA,1C,50,0E,2A,E6,7C,09,F1,57,7B,4F,EA,13), + wwH16(AF,A1,9D,2A,07,D4,03,79,3E,D7,1F,BB,DE,85,B3,4A), + wwH16(91,53,86,57,17,BB,2F,76,36,0E,91,AC,24,21,16,33), + wwH16(BB,AF,31,D2,F1,A0,C5,B3,1D,F4,D4,F2,C0,C7,32,D0), + wwH16(24,D3,D2,84,7E,6F,36,5A,C4,B7,1A,57,E1,31,6E,B0), + wwH16(FF,90,C2,6C,C1,6A,EA,85,D7,ED,22,0D,BC,13,05,D1), + wwH16(22,2C,FB,52,1A,A3,41,1C,4E,42,C5,39,E6,A3,AA,AF), + wwH16(C8,94,E5,72,18,1E,B3,B4,39,36,C3,56,F8,27,F4,B7), + wwH16(A1,E4,09,20,3F,A7,E8,75,48,93,30,7A,87,DB,C0,12), + wwH16(1E,81,3F,26,17,47,C9,4F,98,11,3A,46,3F,B9,5C,79), + wwH16(C4,CE,6D,F0,60,A3,69,A8,05,D9,47,14,4B,E3,6B,32), + wwH16(FC,C4,8A,C7,78,3C,49,B8,2C,15,36,46,82,20,A6,CA), + wwH16(28,EE,1A,95,40,CD,BB,DB,F2,18,79,63,4F,10,19,87), + wwH16(65,F7,8C,FB,18,3F,14,C7,98,9E,9A,C7,2E,CF,18,D8), + wwH16(0C,4C,E7,86,BD,90,03,8F,33,67,EB,EA,09,50,30,C3), + wwH16(00,0B,DD,06,C7,09,0A,49,E3,8E,44,C2,57,7D,48,B5), + wwH16(C4,CE,A1,DD,52,B9,A1,42,A7,38,98,4E,68,2A,95,41), + wwH16(95,16,A9,25,CE,F6,EE,3A,5C,57,DD,1A,49,79,90,E2), + wwH16(15,95,FE,B1,78,06,E6,65,96,CF,F2,67,6B,93,8A,0A), + wwH16(7C,F9,7A,C6,AA,C2,5C,1B,99,C8,D0,18,FC,01,D1,54), + wwH16(C2,1A,79,70,C7,DF,A9,70,17,EB,A3,FC,E2,9D,B3,1B), + wwH16(17,C4,EB,37,D0,79,29,2A,DF,C9,E9,79,D4,9C,F3,99), + wwH16(EB,5D,12,97,DD,EB,34,B9,7B,0C,A8,C5,30,6A,4A,E0), + wwH16(96,91,B8,53,97,13,17,D2,35,10,D1,0C,C9,AD,87,82), + wwH16(A4,2D,88,F1,5A,98,0C,16,21,04,CF,2A,67,6F,C9,55), + wwH16(53,39,9D,7E,78,54,FA,A9,C4,59,D0,6F,DE,7B,6A,DD), + wwH16(E7,6B,D8,01,26,01,BA,3B,68,1E,F9,03,45,29,AA,5A), + wwH16(3E,00,9C,89,C8,D7,A1,20,2E,AE,08,EA,18,D5,1B,9B), + wwH16(C9,4B,0D,C7,32,80,EA,15,D7,51,90,72,05,6F,19,4B), + wwH16(61,22,17,3E,92,F9,F2,56,0C,D6,A1,A1,B0,89,F4,5F), + wwH16(09,92,5E,89,B8,A8,CB,26,1B,02,5E,22,DA,D8,F2,6C), + wwH16(66,13,95,7A,71,9F,EE,83,D6,F8,56,F8,B6,48,5D,E0), + wwH16(84,38,C8,6D,CB,CD,A3,B9,B1,06,7B,B7,14,F1,31,30), + wwH16(7B,72,47,53,97,88,8A,33,41,A3,9E,8A,D4,B1,D6,C4), + wwH16(D2,91,41,9B,B8,A2,B1,53,AA,0F,95,FD,20,85,57,92), + wwH16(E1,1D,9C,68,34,B4,D8,45,93,0A,AD,14,BF,BB,BD,68), + wwH16(1F,69,54,06,F2,13,12,82,14,EE,26,E7,38,0B,B8,AD), + wwH16(4C,77,89,26,61,77,64,8B,87,F0,9E,65,CF,13,C5,0F), + wwH16(4D,2F,A0,0E,9D,6E,22,CC,33,5D,1C,41,4F,14,47,63), + wwH16(3E,CE,62,0C,76,5D,AC,AD,4E,CB,D6,F3,A0,03,E8,77), + wwH16(33,01,B6,36,F0,A7,91,81,8C,7F,EC,FC,1E,FA,1C,7C), + wwH16(9C,DE,53,9D,AA,06,0C,C7,CF,40,1D,23,90,40,6C,4D), + wwH16(D7,17,70,33,85,D5,8A,88,D9,26,72,3F,CA,1B,BB,D9), + wwH16(06,79,79,78,C4,58,F3,90,50,CA,CA,E1,16,25,3A,22), + wwH16(5B,17,4B,A9,75,42,56,4D,FA,0F,1A,CA,13,CB,AD,D1), + wwH16(6D,71,0D,B5,B4,F6,7E,12,56,F2,6E,ED,03,E4,10,BC), + wwH16(17,02,9F,3E,FF,68,67,01,5A,E7,7D,6A,9D,DC,AF,B2), + wwH16(FA,3B,C5,93,69,04,AC,EA,95,CE,46,09,64,0B,0E,9F), + wwH16(43,06,DA,86,BB,85,CE,09,34,11,C2,DB,BE,1F,45,29), + wwH16(FF,3C,37,F6,12,59,C0,24,F2,73,D9,D8,3F,52,1E,DD), + wwH16(E7,C2,EB,DA,03,AF,91,9E,42,BE,FA,22,31,48,58,81), + wwH16(B8,F0,3E,54,BF,69,B1,FE,AA,31,71,59,F1,72,D2,F1), + wwH16(37,F8,6E,29,02,50,7A,06,4B,D4,B0,C2,3D,6D,AF,93), + wwH16(41,5B,BD,72,C5,DE,84,3C,8F,20,66,F4,00,3E,C4,54), + wwH16(1F,5B,4F,E5,AA,75,55,0E,FF,51,38,60,9E,F2,B0,4F), + wwH16(8B,B9,68,88,2F,99,34,89,8A,BA,B5,B1,B4,D7,4D,C0), + wwH16(4B,2B,41,CD,E1,96,EA,F7,79,2A,1F,43,1A,FA,67,AA), + wwH16(0C,E5,EA,10,CD,B8,8D,3D,51,8D,54,2B,8F,8D,84,F6), + wwH16(48,FB,43,45,06,14,95,06,A3,17,A1,60,A3,C0,AE,DC), + wwH16(AC,89,6F,77,33,C4,DB,69,95,29,0E,95,A4,33,CA,55), + wwH16(51,1E,E1,FC,51,34,6E,6A,3D,CA,94,06,75,AB,A0,87), + wwH16(A0,FA,3B,4C,0F,DD,E1,0D,F7,DC,A0,82,5C,59,FB,78), + wwH16(0F,B4,60,DD,88,5F,00,1B,2C,18,79,9D,1E,A6,7B,3F), + wwH16(98,30,14,10,A5,95,B0,B6,08,A3,2D,E6,2A,1E,D9,F3), + wwH16(D0,2E,C6,19,EC,C1,29,FD,E0,CE,D6,88,87,1E,0B,86), + wwH16(92,8D,97,DF,3A,52,22,25,32,0F,8F,90,8C,E6,0C,CA), + wwH16(F1,85,7D,86,10,41,AA,3F,8F,41,F0,81,49,00,93,9E), + wwH16(AD,83,D4,EC,2E,3E,D0,7F,AC,39,CF,9A,37,0B,C7,7C), + wwH16(70,E1,A8,D6,E5,B5,A5,9A,90,DF,E2,F5,9D,B2,F4,6E), + wwH16(C4,A6,C7,90,5F,E3,35,CE,5F,93,E9,3A,FB,0B,C0,AF), + wwH16(9F,89,41,48,F6,F2,94,C3,CD,25,74,FB,46,79,D0,67), + wwH16(AA,5C,E1,E3,36,CB,28,6E,0D,DB,01,72,6B,0D,50,97), + wwH16(53,4C,52,C7,A5,5C,F5,6F,2D,DE,EE,57,7B,FC,AF,A3), + wwH16(77,A6,9B,14,7B,7E,DF,80,9F,EE,4D,AB,9B,72,BC,D7), + wwH16(8F,05,5E,31,94,ED,DB,D1,8C,FB,0F,96,83,1D,EC,17), + wwH16(C6,C8,D0,17,04,D1,D0,0D,BF,7E,6A,15,16,65,5C,8F), + wwH16(74,C7,28,09,BE,3B,15,5B,4D,3B,D7,0D,CC,74,28,39), + wwH16(87,2B,AE,92,E4,DF,B2,16,5C,8F,BF,01,86,34,4B,24), + wwH16(40,4F,C4,E6,27,C4,B3,E2,B6,6C,88,12,FF,6A,CE,32), + wwH16(D4,0D,49,D9,44,24,53,D2,A3,78,D9,BF,54,DD,E2,58), + wwH16(85,C2,F7,6A,C2,C3,AC,CB,3D,F6,37,73,11,C7,70,9F), + wwH16(05,EE,39,41,2F,A3,32,45,59,0F,8F,C5,5D,08,9D,40), + wwH16(C5,5B,EB,11,5D,8D,D0,7C,0A,71,B3,81,C6,B2,66,81), + wwH16(EC,B9,EB,BC,73,CF,B1,C6,3E,50,16,FF,0C,A6,59,16), + wwH16(B1,77,92,CF,E2,FC,62,FD,46,69,2A,15,23,0C,67,AB), + wwH16(CE,FE,6E,03,B8,31,D1,47,55,74,69,50,78,E2,53,65), + wwH16(35,73,A1,20,F5,CA,18,FC,E8,CB,D5,11,1B,27,1A,23), + wwH16(8E,18,02,C1,90,FA,EA,26,AE,D8,3F,37,CD,12,D2,81), + wwH16(7C,18,3B,94,50,6C,69,EF,8E,EA,E8,DF,D0,69,64,49), + wwH16(12,9A,8A,E1,B9,25,4E,F7,30,EC,4F,C3,58,06,BD,ED), + wwH16(CA,08,FE,7A,38,19,EF,C0,81,E1,3D,28,3D,EE,C6,60), + wwH16(BF,A0,D3,CA,CF,C2,43,95,5C,6C,BB,C5,B7,0D,BA,F8), + wwH16(03,57,89,07,D4,72,0D,F9,19,EE,7D,82,A3,8B,09,59), + wwH16(8F,94,C1,0E,00,86,3E,FA,4D,91,D2,C2,6D,1F,2D,9F), + wwH16(EA,AA,9A,68,DE,87,DD,A7,98,49,56,50,BD,C3,54,4C), + wwH16(2C,6A,79,5C,40,E5,9A,D6,F8,33,31,8B,D5,7B,4B,16), + wwH16(99,B6,95,19,4B,B8,75,8C,C5,3A,AF,88,D3,0A,5D,64), + wwH16(AD,F9,3E,A7,3C,7F,52,A5,1C,86,25,F0,F7,56,27,00), + wwH16(1C,4D,0D,B9,12,0E,9F,9A,51,18,11,6D,1D,18,34,82), + wwH16(02,72,BB,30,8E,DE,90,8F,B8,50,97,17,71,E3,66,78), + wwH16(CF,94,B5,05,A9,88,6E,51,EB,39,9C,E4,00,B4,54,3D), + wwH16(26,5D,20,5E,06,FD,C9,0A,F5,61,43,6A,E0,BD,3D,4C), + wwH16(3C,8F,40,56,1D,6D,CF,DA,15,85,AE,9F,EE,77,A0,EC), + wwH16(F8,4B,AE,33,DA,68,54,84,3F,22,19,60,6D,40,B8,E1), + wwH16(A6,73,A4,8D,A9,E0,1B,87,4B,3C,A1,F9,37,DA,A6,1B), + wwH16(69,19,4D,7C,77,B9,1F,98,EC,B6,8C,E0,C8,34,74,76), + wwH16(ED,65,45,EA,36,28,EC,CC,E5,6B,9E,70,87,D3,81,4D), + wwH16(73,45,F0,81,01,24,C4,47,68,8B,DF,57,12,A7,E5,0B), + wwH16(D3,81,90,5A,AD,60,93,E0,F4,19,FE,97,0B,54,C6,26), + wwH16(81,E1,88,FD,BE,75,EB,1B,95,7F,2E,F0,1A,9B,1D,09), + wwH16(5C,2F,8E,90,C9,F8,B8,E8,53,79,9B,BC,6F,6B,63,84), + wwH16(BA,70,0B,93,2A,D3,49,45,33,B0,D1,8A,D2,DF,5B,2C), + wwH16(50,A0,B2,55,D0,57,B8,2A,35,CC,BD,4A,29,C0,49,7E), + wwH16(46,9B,68,0D,76,9E,D0,68,7A,24,4C,86,4C,B0,C9,85), + wwH16(63,FE,58,70,AB,EF,B1,FB,FF,4F,15,3C,14,29,DC,8A), + wwH16(3B,8B,73,F8,1B,20,B8,B6,F6,D2,1C,48,5A,FE,B0,BA), + wwH16(9B,45,EB,DF,0F,92,EC,EA,B0,7B,C1,3B,4A,06,19,91), + wwH16(07,1A,37,9A,8E,CE,1C,AB,80,34,89,D3,47,9D,1D,54), + wwH16(78,62,88,9A,27,BB,66,72,0D,BC,76,66,91,C6,B4,F3), + wwH16(3E,43,DB,B1,CE,0F,D2,74,1B,74,21,C4,42,94,5F,72), + wwH16(38,13,F0,AA,D4,9D,4D,D1,FB,A6,BA,3E,A8,2C,70,99), + wwH16(3A,36,81,81,00,0D,3B,A0,67,D6,F7,EB,2A,97,FF,CA), + wwH16(29,95,5A,90,F3,B2,D4,45,CB,BE,B5,0E,07,6F,0B,37), + wwH16(18,CF,6D,92,E5,F0,9F,FE,BA,86,E0,01,22,A4,3C,7F), + wwH16(F8,DB,6A,08,D7,4B,B3,88,1D,77,88,2F,A2,EC,58,24), + wwH16(41,5A,73,86,60,42,5A,1F,77,CC,BC,31,5B,34,8E,52), + wwH16(D3,13,45,91,4D,C3,70,38,D5,5D,39,9E,A3,F2,BD,12), + wwH16(99,0B,EB,DD,BD,67,B6,00,6E,E8,58,AF,69,7A,2A,D7), + wwH16(D4,4C,0F,C6,56,03,7C,98,5E,67,A7,A9,9C,F9,38,9F), + wwH16(6D,87,44,18,17,48,65,26,8C,24,CB,6D,9B,0D,0B,49), + wwH16(41,F8,54,CA,DE,BC,B4,A3,28,3F,86,66,19,0A,26,C7), + wwH16(48,E6,00,50,20,95,3C,4C,6E,AD,70,90,DF,FD,AE,96), + wwH16(07,A5,93,6B,B8,9F,03,51,31,44,72,6C,D1,3A,D4,12), + wwH16(C8,D3,F0,3E,27,77,33,BF,18,8D,D5,36,0E,7B,44,88), + wwH16(DB,44,1F,12,41,01,B0,14,E6,82,2F,1E,36,00,D4,FE), + wwH16(9F,1B,C5,DB,78,B3,D4,AE,1A,35,57,31,D7,8B,65,23), + wwH16(7C,17,0A,6B,02,3B,69,30,7E,33,9C,97,3B,E1,E3,29), + wwH16(A9,DD,B3,D6,6F,75,B0,99,78,C7,01,42,D8,79,E3,34), + wwH16(A3,7F,4C,DE,4F,4D,F7,51,75,7E,2A,28,CA,B4,1A,64), + wwH16(C8,CE,0A,02,E4,1A,4A,AF,98,66,A7,6F,F8,71,B1,58), + wwH16(1C,1A,2C,38,F1,52,EA,D2,2A,DF,7C,EF,AF,8F,1E,53), + wwH16(EE,D9,58,5D,81,84,83,1D,D1,09,EC,38,5B,23,F9,AA), + wwH16(4C,5D,68,AF,34,47,B2,95,04,8E,29,91,51,58,8D,31), + wwH16(A1,59,A3,A1,94,9B,5D,84,8F,C1,DA,69,36,B1,63,02), + wwH16(BC,63,9C,34,53,35,CA,1E,BE,1E,34,AD,FF,59,AA,AB), + wwH16(85,90,09,EB,67,86,C2,97,E1,C8,AC,21,C5,BA,0E,2C), + wwH16(99,FE,3B,BB,8A,B9,3C,B7,84,AA,80,9F,DE,3F,F1,69), + wwH16(A4,01,39,21,F1,B8,79,57,22,F5,4A,A7,98,2E,92,CC), + wwH16(ED,05,57,71,B4,A1,81,8D,67,FF,7D,D5,D1,78,D7,E1), + wwH16(47,C7,79,33,44,38,66,29,12,A7,1C,39,DC,64,DA,59), + wwH16(A0,27,B0,CE,FA,71,A6,1F,4A,8F,6F,CF,13,97,AA,EF), + wwH16(CD,02,70,BB,BB,B6,67,BC,A0,D9,56,B2,D7,C4,5D,14), + wwH16(3B,32,23,D5,74,5A,81,2F,85,0F,7A,F0,88,FF,30,B8), + wwH16(A6,8B,CF,DA,0F,AC,34,11,B0,4A,6F,95,28,5B,AC,49), + wwH16(15,B4,3C,74,5D,0B,D6,22,0B,78,3D,7D,78,28,46,7C), + wwH16(D9,DB,89,9D,47,A9,27,5F,B2,CB,97,4B,AC,9E,0A,8A), + wwH16(6A,6D,A0,64,EC,E9,26,16,AD,B2,2E,AA,6E,16,BB,A5), + wwH16(D8,12,26,2F,7F,A4,90,3F,66,9A,5D,1F,5F,90,BE,0F), + wwH16(88,39,45,0A,40,36,FE,E8,EA,A7,20,20,0E,4F,DB,96), + wwH16(E7,EF,BE,F6,ED,CD,4F,3A,41,5F,2A,4F,1F,2A,F7,83), + wwH16(28,81,A7,2A,42,5A,E8,FF,12,00,C5,31,0F,FB,F0,B0), + wwH16(44,3A,45,35,07,4A,65,8B,E9,90,B4,F7,A0,1A,5B,84), + wwH16(46,4D,BF,08,4D,9A,60,BB,5A,01,98,9E,7B,1D,ED,2C), + wwH16(27,3F,10,35,6E,27,F9,66,70,03,B4,E9,00,3D,F1,1A), + wwH16(72,22,CF,A7,7B,6C,88,11,C2,C9,0E,3B,80,88,7E,C1), + wwH16(61,E9,3F,4F,A1,EA,45,50,7F,44,98,50,B8,B3,24,20), + wwH16(29,CD,56,04,00,07,5B,74,63,08,D0,EA,A4,7F,87,BF), + wwH16(A2,90,29,DE,AD,B2,EF,CF,20,6B,15,3E,F6,32,D8,90), + wwH16(5C,4A,AE,A8,9D,3D,0C,30,51,D9,81,8F,58,D0,DC,64), + wwH16(CC,E9,BB,40,B4,F9,1B,18,4E,57,4C,7E,4C,1B,42,CA), + wwH16(F2,8F,56,30,FD,0F,0E,30,E1,FE,CC,3C,58,EB,43,C5), + wwH16(11,40,58,E9,8A,33,A1,B2,E9,88,E0,AE,52,CC,E1,3D), + wwH16(01,30,A6,F1,ED,E0,84,86,84,BA,11,9C,44,ED,7B,5C), + wwH16(4E,25,DD,9D,EC,1B,AF,91,6E,B1,89,A6,CE,43,79,94), + wwH16(55,3A,08,25,77,2B,85,89,15,FA,FA,14,B5,AF,51,05), + wwH16(61,79,4D,E4,77,0B,2D,3D,82,CC,8A,2E,5D,15,9B,1F), + wwH16(FC,C1,B0,EC,34,7D,30,FB,F0,83,34,FD,15,BF,CE,2D), + wwH16(DA,E7,E5,10,26,3E,51,9C,A3,94,BE,E1,D4,5B,6B,E3), + wwH16(32,67,FC,21,61,28,31,6A,3E,25,D5,84,46,CA,03,26), + wwH16(08,38,5D,89,D8,1C,3C,85,20,A9,C5,58,08,10,B9,90), + wwH16(3A,C5,52,2D,4C,D1,2D,2F,20,26,C6,88,BA,56,9A,16), + wwH16(32,D9,C1,91,24,DC,76,0B,EA,34,09,5C,C2,32,F2,80), + wwH16(B3,09,8C,F8,54,CF,A1,6C,24,7F,E0,9A,92,71,6C,42), + wwH16(0A,C2,C4,97,E4,AC,5F,37,B4,1E,CA,6B,46,AB,79,E7), + wwH16(1D,35,43,A6,82,1D,93,F4,4C,D6,CE,04,CD,34,19,A0), + wwH16(A0,6B,3F,05,66,14,8E,B1,2E,52,11,48,DB,F6,70,50), + wwH16(F3,C2,8B,17,6D,D2,66,BE,A7,47,37,AA,87,DF,93,4B), + wwH16(67,B5,F5,AA,65,3E,7F,FB,47,50,65,2C,B4,98,6F,5F), + wwH16(71,DF,77,B4,5E,62,E0,61,A9,A2,C9,C6,02,57,C6,CC), + wwH16(64,C4,D9,BE,33,26,42,D5,31,9B,EF,C5,59,D6,2B,3E), + wwH16(81,38,38,F2,FE,C9,74,23,D2,C8,F5,E0,C5,57,2F,03), + wwH16(A7,11,86,7E,F6,C0,D0,A6,A0,08,A5,1F,34,A4,F5,C7), + wwH16(86,42,A4,6A,6E,9A,54,E9,08,94,EC,6D,B7,35,68,59), + wwH16(5C,62,0D,FD,4F,11,23,F5,CA,C1,FA,50,55,5C,A3,BA), + wwH16(34,29,D3,14,38,CA,31,04,48,C0,CD,D1,E8,3C,8A,1B), + wwH16(9C,BA,6E,1F,4F,38,47,BA,A2,A8,43,DF,54,C9,9F,A1), + wwH16(E7,21,DD,97,AB,8F,40,07,9A,53,D8,76,FB,9C,12,21), + wwH16(78,03,B9,44,16,6C,FB,C7,B2,08,0D,4B,78,63,29,63), + wwH16(3E,8C,05,28,E3,15,E8,4B,1A,C0,AA,60,0C,CD,69,95), + wwH16(30,A3,B7,25,C0,4A,43,4C,CF,31,91,7C,9B,79,0E,F8), + wwH16(C5,A3,E4,C6,27,DA,A8,FB,6C,2F,70,4E,4F,33,BB,6D), + wwH16(A0,DC,AF,68,76,8D,BE,41,71,DE,CF,4A,99,28,2C,94), + wwH16(F7,20,8F,1A,36,03,B3,98,83,83,CE,40,A1,26,FC,28), + wwH16(AD,7B,2C,5F,9C,39,5B,A6,9D,B6,DF,FC,CB,9A,B1,0C), + wwH16(F3,E5,1F,40,4B,12,04,F6,7D,E3,1D,E4,B2,07,38,DF), + wwH16(5B,60,FF,35,21,49,0F,9A,0F,3C,4C,22,44,2B,E3,E3), + wwH16(91,1B,D5,78,91,9E,90,50,85,48,F6,73,E4,CF,92,10), + wwH16(D8,15,F7,5E,07,8E,B1,1F,3E,03,E8,17,BF,A6,D8,50), + wwH16(A8,5B,CC,86,71,32,BB,F8,1B,CE,E4,B7,6A,0D,0C,B9), + wwH16(B3,59,36,44,7C,A9,71,D3,E1,91,28,83,D9,6A,3E,8B), + wwH16(8F,6D,F6,CD,75,49,19,95,39,E2,CD,26,D9,BE,F3,5B), + wwH16(33,B8,7D,F4,A3,9A,AB,EE,D2,53,BA,D5,AE,8B,67,B7), + wwH16(E6,09,C3,F1,98,D1,CE,47,91,3E,E9,0E,79,69,FE,D4), + wwH16(BA,9A,0C,C1,17,C8,52,59,18,38,2F,D5,83,B0,7E,A4), + wwH16(62,BC,30,9E,5E,CD,5B,31,19,62,2D,B3,8B,53,79,E3), + wwH16(CD,BC,DE,C7,99,D9,43,C9,75,F8,35,A3,4F,E2,C0,C3), + wwH16(BD,48,9F,DE,FD,F1,D4,52,2D,FA,65,1E,5F,5B,CB,40), + wwH16(52,23,C0,21,E4,75,AC,F6,39,99,3B,1A,3A,6A,E1,A6), + wwH16(C9,46,0F,7D,11,B0,B4,A9,18,95,E8,74,23,0E,7C,E3), + wwH16(E9,D1,29,11,E7,55,CB,6B,61,D7,BC,92,39,F7,69,B2), + wwH16(55,7D,15,4F,1E,82,45,0D,63,89,2F,E7,42,8E,D5,86), + wwH16(D5,92,4D,D3,12,D3,74,8B,89,1D,3E,55,17,5E,1F,2B), + wwH16(C2,26,7E,76,FE,15,F8,84,A0,7A,92,CF,72,39,45,D6), + wwH16(79,59,4E,48,9B,19,56,0E,A0,70,E1,80,FD,FE,F6,FF), + wwH16(66,7F,88,AA,4F,D5,64,DA,51,5C,AF,17,D2,0E,C9,2E), + wwH16(D8,F6,83,D5,08,54,D0,94,59,6A,A6,5C,E9,76,C0,25), + wwH16(51,29,5B,DF,1F,30,EB,EC,F2,1A,16,0E,79,5D,83,5D), + wwH16(DB,1D,50,41,8B,9A,2C,82,6D,04,12,B0,90,8B,F0,7A), + wwH16(94,3C,62,D5,43,A6,CE,8E,45,C0,C4,93,E5,E5,C2,B6), + wwH16(38,B8,58,EB,72,7B,BA,F9,5A,82,6E,ED,F2,2B,C7,DC), + wwH16(BB,9D,58,5A,6D,3B,15,C9,72,08,A0,44,0E,11,F5,62), + wwH16(D1,8C,C3,FD,C1,50,2A,6F,54,D0,A4,E4,07,B0,E8,CF), + wwH16(AF,3D,30,05,65,C4,B7,B4,01,F3,94,3E,8C,FB,B6,77), + wwH16(30,A6,17,4E,48,CB,73,47,21,CA,93,50,68,E1,6C,68), + wwH16(A0,EE,1B,7E,AE,7B,CD,31,D6,17,E3,09,C5,76,66,96), + wwH16(3C,2C,8F,56,2C,54,6E,4A,B0,CD,00,D5,7F,BA,1C,37), + wwH16(6B,92,D4,3E,21,C3,BB,3E,3F,01,5C,67,79,11,30,97), + wwH16(03,C8,06,74,DC,38,7E,AA,11,53,4E,9B,D2,59,EE,D7), + wwH16(70,43,6B,6F,85,D8,36,B7,A2,76,43,31,6A,B2,5B,71), + wwH16(D1,D2,D4,A1,CF,C4,08,54,91,98,0E,68,7E,AC,DE,54), + wwH16(25,77,DE,BD,F6,1B,5B,BE,29,16,AF,EF,5F,23,63,EC), + wwH16(F4,9C,F0,DE,EA,4F,D1,80,CE,07,77,F3,E1,45,64,6C), + wwH16(B3,02,D8,43,BF,3B,4E,A3,BB,6F,6F,C5,49,82,43,F4), + wwH16(C5,9C,7B,EB,F7,8B,E7,3A,DC,E7,36,2E,3B,BA,20,E2), + wwH16(68,73,C1,95,FC,34,46,AF,DB,83,09,40,0C,75,E1,52), + wwH16(A9,8C,68,82,D3,42,31,06,E0,CB,A7,64,13,8A,B1,2A), + wwH16(5B,BF,05,DB,C9,90,7A,71,C4,96,13,54,47,CD,74,FD), + wwH16(83,DB,91,79,91,49,44,5E,29,3D,85,6B,3C,54,D3,CE), + wwH16(95,DF,24,D2,7C,AD,B9,15,BF,62,C3,67,A8,0F,EA,7B), + wwH16(B5,61,8A,6F,A7,7F,C1,43,67,40,5A,B0,9D,33,83,52), + wwH16(3F,4E,F4,73,DA,3A,A8,C8,FD,4F,F9,7F,3A,0E,64,4A), + wwH16(66,65,21,E4,A1,4D,AA,62,E7,B2,82,D5,58,8C,82,8F), + wwH16(C1,92,56,36,B3,70,AD,31,4D,80,0D,45,3F,C1,3A,A6), + wwH16(3B,83,AE,4E,18,D4,E5,2E,EF,6D,73,26,8C,61,27,49), + wwH16(87,BE,D3,7B,C7,D2,50,B9,75,0B,FF,CB,86,08,4E,D9), + wwH16(22,76,F1,6E,00,97,DE,88,4F,C8,06,E2,85,48,AB,69), + wwH16(74,65,23,1C,91,FE,59,6E,5B,6F,B3,20,0A,0E,36,30), + wwH16(2E,90,06,85,51,3E,A1,19,EF,F2,19,AB,B6,3A,24,34), + wwH16(CA,F4,93,29,05,5E,C6,12,F6,6C,9E,B8,59,D5,D4,92), + wwH16(D7,F1,CC,3E,F2,AF,E5,02,A1,DE,EC,49,FD,24,D3,70), + wwH16(CF,D6,F5,DC,C7,7E,E3,78,CB,D8,AD,9E,17,0C,58,8C), + wwH16(76,43,AA,37,24,03,3E,E4,75,89,79,6B,0F,60,EF,E1), + wwH16(57,D2,4C,D6,64,9B,DF,A1,7C,D7,2E,B5,0D,09,17,5C), + wwH16(13,A1,8C,04,3B,3D,65,F8,2D,38,2B,A1,32,FE,7E,75), + wwH16(5E,6E,54,7D,68,E2,D1,E2,1D,5A,D7,FE,74,39,D2,6E), + wwH16(5C,57,E8,FD,56,15,31,A6,B7,A4,C6,C3,4A,B5,1E,31), + wwH16(1F,4E,25,92,ED,E0,AB,85,7C,A8,BB,44,39,B4,3C,42), + wwH16(6C,6D,04,E3,D2,F0,BD,E8,8A,51,40,35,4C,B4,5C,03), + wwH16(11,DB,3C,93,6E,E6,62,E3,EE,15,2D,D8,AE,7A,D7,67), + wwH16(E7,29,7C,E0,D5,93,61,81,CA,31,99,0C,57,93,AB,70), + wwH16(03,2C,5A,CF,79,39,EE,85,BC,57,3B,4D,EC,63,A8,4D), + wwH16(A7,FB,35,2D,39,21,F2,BE,00,B8,9A,29,30,61,28,E6), + wwH16(D4,DA,FE,C6,59,75,53,A0,35,D2,B9,9E,24,13,22,9D), + wwH16(7D,14,C8,52,80,00,A7,4D,24,0D,A4,A1,BE,16,32,5A), + wwH16(F5,5D,78,48,A2,D9,F1,B2,16,F5,ED,3C,E1,AD,9D,9D), + wwH16(07,57,18,7F,DF,93,4B,3F,60,49,F8,65,DF,D6,C5,04), + wwH16(81,A5,10,B2,BD,FF,E8,78,43,F9,1E,A1,52,9B,B0,0C), + wwH16(B4,FD,CD,FA,2A,89,C1,8E,53,E1,15,9D,29,71,A6,74), + wwH16(BB,5D,B9,51,C2,E9,9E,B8,70,DA,DF,DD,D0,50,2A,02), + wwH16(AF,68,33,F5,72,3A,83,40,B3,30,20,4E,E8,54,94,C7), + wwH16(DD,97,89,65,9A,57,3B,04,8D,B2,45,07,52,44,EE,3F), + wwH16(B7,FC,9F,86,51,F3,87,4D,6E,4E,22,EA,D0,A1,4A,F8), + wwH16(F1,B9,B5,22,76,7E,92,0E,34,F5,09,A5,B4,E6,60,D7), + wwH16(39,48,A4,2A,0F,74,91,45,DA,0B,BC,F8,A9,B9,DA,C0), + wwH16(1A,D3,1B,F6,C7,17,76,1B,93,C8,DE,CA,8C,52,E7,D6), + wwH16(2C,55,81,B9,58,3A,8F,78,10,5E,1A,84,E9,C3,89,FD), + wwH16(2B,36,7B,95,84,D8,FC,3D,C4,2C,26,A3,A4,79,10,8E), + wwH16(64,E6,C9,01,9B,85,62,23,6C,BC,CD,D6,2C,F6,2E,BA), + wwH16(DE,08,1C,B7,9B,AD,2A,65,DA,F8,A3,AB,50,B1,8E,D0), + wwH16(69,0A,DA,92,F4,1D,9F,3E,0D,6A,A8,F1,84,0F,5B,92), + wwH16(38,6D,E7,20,58,38,DE,35,C2,23,6D,ED,87,D5,EB,4B), + wwH16(32,40,6A,7B,0F,EF,8F,2D,5C,0F,98,F2,CD,58,EC,A7), + wwH16(A7,42,0E,34,04,4F,BC,62,99,CC,2D,21,15,2B,0B,D9), + wwH16(E4,EE,61,E9,6E,0A,F9,B0,44,2C,D7,40,77,49,36,9B), + wwH16(A0,18,F5,BD,0A,CC,B6,7E,A0,43,F7,E6,EF,F4,AA,B4), + wwH16(16,50,5C,72,4D,7A,72,0D,3D,D3,F7,40,BF,3A,07,19), + wwH16(82,DA,9C,7D,FE,E5,9B,0C,81,39,A1,0A,63,AB,9C,42), + wwH16(3F,C4,C2,E2,F2,7F,3B,78,91,E4,52,08,8A,17,B2,5C), + wwH16(CC,06,D5,6A,C0,29,54,A9,1D,EB,63,C5,F7,00,E1,86), + wwH16(BB,F1,48,ED,0B,76,8A,81,1C,8C,7B,EC,CB,B9,30,FE), + wwH16(A2,9C,E7,92,AB,7F,9F,A8,CE,C3,BB,10,98,E2,ED,2C), + wwH16(5C,FE,B1,BF,90,EF,7D,36,48,B2,40,8F,B8,44,5D,7F), + wwH16(DA,54,C5,03,84,A4,0E,2A,52,B4,FE,45,96,C3,C7,F8), + wwH16(3A,53,F5,0C,AF,E9,E4,4A,98,3E,2A,6C,8A,2E,10,F7), + wwH16(E8,3A,CA,02,E8,B4,4E,4F,0C,8C,80,5C,81,4D,FA,BE), + wwH16(F3,C8,0E,98,6F,A1,3A,58,D9,DB,30,59,58,59,00,54), + wwH16(39,9F,59,EC,36,8C,E7,23,E9,2E,AC,C3,30,4E,DE,93), + wwH16(2E,BF,BA,EE,34,D9,B4,C5,5B,CA,E1,4D,1B,5D,78,31), + wwH16(AF,69,25,D3,BD,04,15,9A,11,0D,44,D6,CA,77,0C,98), + wwH16(8E,92,04,81,21,F6,FC,5D,17,53,C4,C8,62,93,2C,0A), + wwH16(5C,28,84,C2,54,BD,CA,1B,F0,70,7C,A8,83,D8,D4,B1), + wwH16(AB,24,E8,4B,83,D4,11,01,E9,88,2B,15,10,9F,43,AD), + wwH16(C4,A9,A3,1E,59,63,F1,1C,DF,86,F5,24,DB,B1,EC,0A), + wwH16(3F,0C,11,AB,4D,BA,5C,0A,17,B2,F0,31,7E,00,DE,6E), + wwH16(17,75,29,08,50,D1,32,64,36,BA,7C,23,BB,29,07,05), + wwH16(BA,F3,A4,13,78,9F,8B,CA,F0,8B,E1,82,1F,B4,4B,97), + wwH16(F0,B4,83,BE,09,D9,2C,D5,27,67,C5,2E,BF,0A,8E,F3), + wwH16(3E,6E,EC,F8,66,21,A1,3A,C8,D6,17,3D,C5,89,71,61), + wwH16(9C,B8,B1,41,33,5B,65,2C,98,25,55,2A,E1,BE,BD,35), + wwH16(20,B7,F1,3C,84,D6,3C,75,E0,D3,B8,EA,B8,AE,A9,C4), + wwH16(7C,25,77,F3,28,1B,B8,08,A6,1B,C5,80,26,3C,0A,EB), + wwH16(AF,39,FF,5E,13,CA,76,80,3F,1C,23,AF,A9,B6,55,93), + wwH16(24,A5,2E,69,22,9A,5A,CC,53,D7,4B,05,1A,AA,97,E3), + wwH16(FA,FE,C9,3C,7C,1B,82,52,8B,7E,E7,90,D9,AB,EA,E0), + wwH16(FB,34,96,DC,F8,C5,DA,70,13,BC,A0,A8,E7,E2,8D,25), + wwH16(76,19,3E,3B,6F,AD,8F,BB,8C,F2,B1,E1,C9,6B,97,40), + wwH16(5F,CA,CD,60,4D,0D,D7,B6,5B,BF,B1,08,88,64,BF,D9), + wwH16(D9,0A,73,6D,B9,EF,E1,CB,57,52,CD,D1,42,39,24,FE), + wwH16(7A,3A,07,56,33,37,94,8A,3B,2F,F6,30,C9,72,A8,4C), + wwH16(F4,FC,92,A5,95,8C,24,C4,08,46,35,0D,94,FC,D2,DA), + wwH16(14,0B,22,17,80,FA,8D,D5,D3,13,69,60,27,EC,D1,44), + wwH16(86,91,AF,7C,B7,77,0D,B0,67,53,28,16,52,13,DE,A3), + wwH16(81,26,FF,73,6D,6E,C0,F8,02,3B,61,57,59,30,A2,14), + wwH16(C1,EB,C3,1B,2B,28,D2,0A,0A,21,7B,16,1C,15,08,D3), + wwH16(BC,6F,50,9B,BD,3A,FF,44,8A,95,B4,A0,16,67,D3,86), + wwH16(FD,D1,FF,33,97,BC,05,CB,65,DA,61,6D,00,CB,BA,C9), + wwH16(9E,20,63,19,C9,99,A3,3E,08,26,B9,4B,24,B7,9C,11), + wwH16(1C,9E,11,5F,6E,92,B6,49,DE,7C,67,1F,7B,91,41,9D), + wwH16(7B,B5,8A,65,36,D3,9F,13,44,58,00,97,B0,E2,ED,4D), + wwH16(F7,84,42,79,CB,1E,21,47,F1,70,20,2F,E1,86,A4,94), + wwH16(3D,27,96,9F,2E,D7,A2,EB,84,8E,3A,7F,06,41,DC,24), + wwH16(7A,ED,A9,58,43,06,A7,01,D4,B5,99,DC,A5,44,89,49), + wwH16(D2,1F,C9,B4,7E,56,DD,60,DE,A9,76,90,5D,E7,5A,B7), + wwH16(54,27,91,53,D6,38,F7,90,29,96,C9,54,B1,9D,DD,6C), + wwH16(8A,8B,A7,19,30,4A,6D,C0,DF,2A,FF,54,2A,AB,B2,41), + wwH16(B4,E1,A0,49,82,78,B0,A0,B6,7E,E1,F1,0B,05,D8,D2), + wwH16(3A,1F,42,0F,1E,46,20,0D,B2,B8,B1,9F,DC,37,66,CB), + wwH16(C6,B8,8F,D9,75,66,C0,65,62,45,FF,BA,21,E4,8B,90), + wwH16(75,EA,75,45,CD,90,D8,E6,85,13,C5,80,FC,D7,0D,C4), + wwH16(A5,C4,9E,4F,16,74,D4,E2,6F,45,49,4E,0B,5E,9D,A2), + wwH16(BF,93,50,4D,FA,6F,21,5D,23,C3,9A,C0,25,62,03,AA), + wwH16(C7,D3,D6,D8,94,99,07,25,7C,A7,2C,E2,D5,B3,5B,E9), + wwH16(4C,46,03,31,3E,99,07,C8,CC,8A,23,AE,D2,5D,94,B5), + wwH16(1A,49,E2,F5,29,B6,F2,4B,43,EF,78,28,F2,45,93,8C), + wwH16(39,0A,2E,16,33,5B,35,EE,4C,F0,55,11,D9,A4,A7,1F), + wwH16(15,D3,B8,1C,B4,53,ED,80,DC,C1,A5,E7,8F,91,4A,61), + wwH16(9B,47,F0,20,08,53,63,4F,6F,32,19,85,19,DB,71,B2), + wwH16(8B,B0,48,82,27,68,E8,EF,EC,D9,CC,FE,B5,D0,53,75), + wwH16(9F,73,4E,9C,8B,99,8C,DA,79,2D,DD,EC,2C,00,19,5D), + wwH16(30,D3,5D,15,94,93,44,75,B1,4F,C7,C5,7E,02,EC,59), + wwH16(22,E3,F2,66,19,C0,9B,D3,2A,F9,BB,40,F6,F7,AA,45), + wwH16(45,79,53,58,59,B8,99,8B,70,3A,AC,A2,1A,E4,18,87), + wwH16(47,75,E2,B9,09,14,2A,D4,75,99,03,1D,D6,A5,8A,91), + wwH16(45,06,AA,ED,09,49,40,6B,2D,E4,FC,F5,2F,7E,82,A9), + wwH16(A9,59,4A,50,64,6A,C1,FB,BC,3B,57,31,9F,F4,7D,D2), + wwH16(D3,6D,52,ED,54,DE,03,CC,17,99,FE,0E,F6,4A,5A,21), + wwH16(0D,80,A1,D3,03,19,5E,61,0E,2B,68,C1,82,93,48,46), + wwH16(6F,7C,5B,5B,D0,F0,AC,92,B6,7B,4B,37,4B,67,90,E8), + wwH16(F8,F2,0D,19,91,D0,2A,18,6B,63,5D,4E,27,FE,E4,DA), + wwH16(58,38,81,4C,6D,50,87,55,AC,AD,58,AB,B9,DE,81,02), + wwH16(10,70,5C,B3,BD,C2,E4,C3,74,A8,BE,8D,1B,64,E4,F5), + wwH16(EF,18,7E,C0,FD,F4,FA,B5,3C,49,90,6C,27,D8,91,99), + wwH16(F1,50,51,31,68,41,C7,C1,76,4C,B3,D1,A3,D1,FA,F8), + wwH16(C6,54,30,74,B4,D9,5B,BA,36,2A,54,4D,E8,C8,A8,F3), + wwH16(90,B8,7F,75,82,21,5F,8E,1A,F1,57,6F,1D,77,62,45), + wwH16(B5,0C,AF,1D,70,CA,AB,0D,FE,66,1F,66,32,A7,66,9A), + wwH16(42,6F,15,C8,83,C4,1F,46,1C,A0,BE,E1,F9,71,0A,D9), + wwH16(37,19,C3,BE,6E,DD,F3,40,79,37,56,14,15,D7,EC,F2), + wwH16(CF,54,0B,4C,2F,DD,5E,39,F4,D7,23,5D,30,7C,3B,24), + wwH16(7C,44,AD,9B,06,2F,B8,E6,9A,B2,F8,3B,0B,FA,70,2A), + wwH16(35,28,40,2F,7F,F3,6B,97,FC,E7,63,91,F9,82,00,63), + wwH16(D5,84,18,C3,92,EB,29,BC,E9,93,E5,E6,DD,3D,02,57), + wwH16(05,9A,9D,E7,9E,51,21,2C,79,6B,30,05,4D,8F,69,B3), + wwH16(DE,F4,F7,6A,85,B4,B4,23,24,7A,41,5A,80,5C,FC,17), + wwH16(DF,B0,1D,EB,57,FD,50,D4,7E,27,80,68,9F,85,45,CD), + wwH16(0B,53,DF,D2,C8,CE,F0,2D,77,7F,2A,26,54,97,79,46), + wwH16(A2,73,C2,31,AE,3D,FF,9F,9C,95,56,9F,56,2F,D6,25), + wwH16(5C,0E,CA,50,29,57,99,8B,DB,D4,C4,E8,C9,F0,22,BC), + wwH16(C5,62,9C,BC,61,00,87,1C,53,DA,10,7A,39,85,73,72), + wwH16(C0,8C,C8,32,A5,99,43,0D,F2,90,02,EA,7E,24,E6,70), + wwH16(DB,1D,60,34,AE,40,97,85,A7,34,AB,B7,56,1B,10,90), + wwH16(A1,69,57,FB,87,01,A2,B9,9F,ED,10,6A,3E,77,AE,80), + wwH16(BF,27,B6,76,89,5C,FA,C3,8A,CD,CE,95,83,7F,6E,AD), + wwH16(78,81,F0,0B,50,61,5A,1E,89,02,D9,58,6D,1B,3C,8C), + wwH16(6E,6C,94,A6,0F,5E,38,59,39,CA,5E,BC,9E,B9,57,E0), + wwH16(2E,87,25,88,27,9F,8B,57,10,8E,CE,58,C2,E3,E2,E5), + wwH16(DC,01,2F,BF,AE,7F,14,4A,21,24,64,12,F8,D5,2C,48), + wwH16(A8,78,D0,0C,58,7D,AA,5B,F3,5D,0F,58,E1,25,14,56), + wwH16(32,CE,AB,80,42,FE,55,0A,A8,5A,52,04,05,97,E2,A6), + wwH16(22,59,E4,74,DB,0B,15,F6,AB,7F,C4,0F,44,DC,92,0C), + wwH16(48,D5,FF,10,C4,E8,D6,4D,B5,2A,C6,5D,AE,55,70,41), + wwH16(C1,0F,93,B8,23,F3,E6,EF,A7,36,29,31,5E,E2,77,A6), + wwH16(52,E7,2E,EC,FE,1A,13,75,AC,FA,8C,98,D3,8E,5C,51), + wwH16(24,66,2B,1B,36,53,C1,CC,3D,38,1B,61,E5,0D,9A,E9), + wwH16(7E,76,6B,33,2F,79,B6,C4,98,74,61,60,59,A1,CD,52), + wwH16(5E,4C,53,D5,B4,4D,E5,E7,85,8D,49,B6,90,26,43,EC), + wwH16(C5,AD,C3,4A,AA,13,8E,20,7F,8D,01,9C,60,8B,96,1D), + wwH16(22,7B,53,A1,60,FC,E2,6E,B1,C1,0D,14,7D,12,EF,F9), + wwH16(52,08,45,C3,6A,D2,3F,30,BF,82,DE,66,84,07,95,08), + wwH16(6F,33,C1,C9,93,23,97,D3,D6,8B,CC,3E,7E,5D,8E,CB), + wwH16(0D,5A,F8,C5,DE,D5,B5,35,4C,CD,54,44,06,C3,6A,CD), + wwH16(D9,63,AC,7C,5C,D8,11,DB,0B,E6,DB,4A,C2,39,25,F4), + wwH16(52,3F,72,5F,66,8C,E5,6C,59,2A,22,FD,99,A1,9C,C4), + wwH16(7E,78,7A,FD,F8,8E,F2,41,4C,1C,DA,37,A4,F6,B9,00), + wwH16(B3,80,CF,9E,83,9C,83,AD,AE,BB,9D,B4,9A,C4,C7,26), + wwH16(CF,40,1B,3C,A8,2C,7D,05,F0,28,EB,F8,E4,D5,4C,26), + wwH16(07,47,06,21,68,4B,FB,D0,8E,B8,EE,37,BD,06,71,5B), + wwH16(7D,48,5C,A2,B8,27,EA,BF,C4,4F,0F,BF,FC,8D,13,BD), + wwH16(AD,61,70,70,E5,89,54,F4,45,A7,D3,02,9F,47,CA,CF), + wwH16(4B,CB,86,7F,2F,5A,83,0D,A5,8B,AD,5C,8D,0D,2B,E4), + wwH16(2B,90,64,E0,EC,61,CC,CA,4F,5B,E3,7A,3E,06,FF,FA), + wwH16(75,04,39,F7,A7,A8,E3,C8,68,09,37,73,FA,BA,A2,66), + wwH16(25,9F,28,91,4C,A4,E5,DE,18,3E,A9,89,8C,9D,26,00), + wwH16(AC,75,7D,07,44,62,CB,73,F3,FB,33,C0,21,61,75,56), + wwH16(AA,88,1A,6F,64,BC,DF,79,D3,89,38,06,10,75,E2,BA), + wwH16(97,B2,45,7D,F2,E3,11,1B,28,BE,6E,71,91,4C,90,98), + wwH16(9D,F0,F3,CF,81,5E,EA,64,E7,D3,E5,32,22,4B,CD,9E), + wwH16(62,F3,34,10,22,2D,67,16,E9,80,88,3F,85,4B,98,F6), + wwH16(FE,E6,D1,4F,DC,A2,DE,C4,54,E4,96,E6,2C,FC,64,C9), + wwH16(52,60,82,B1,1C,BE,8C,A1,F5,10,89,66,73,81,80,08), + wwH16(EF,C7,A6,76,AC,93,61,24,8A,3A,70,40,D9,66,E2,21), + wwH16(70,D4,03,0E,EC,34,D8,19,BF,79,15,C6,41,B9,CF,D4), + wwH16(AB,59,D4,00,4C,66,40,FE,18,5D,0A,74,4E,C7,F9,A6), + wwH16(C6,56,2E,BE,38,67,DE,19,C7,73,96,1C,3A,C9,70,B5), + wwH16(86,06,7B,8B,E9,4B,A1,9C,02,09,60,53,59,4E,FE,71), + wwH16(B5,79,8A,BD,EB,35,FC,58,CD,52,66,93,CB,8C,DA,A4), + wwH16(06,B0,6A,D6,9D,5E,7D,CF,72,0E,B4,D8,4A,4C,F1,42), + wwH16(E2,5F,3D,4B,45,21,2F,D6,FB,B8,31,EF,5E,D2,F9,05), + wwH16(07,30,DB,5C,DB,E0,5C,03,4C,EB,68,B7,5D,15,19,ED), + wwH16(0C,3F,89,BB,AB,5B,6B,13,D5,B1,AA,F0,21,29,3C,C9), + wwH16(53,06,57,8A,07,32,9E,62,F1,D5,E4,20,32,60,85,4E), + wwH16(F2,1E,A3,77,AB,DF,A7,74,DE,AF,B6,18,47,8E,9C,58), + wwH16(56,EF,E0,6F,3C,F8,BC,BF,7E,D2,39,20,FC,CD,B9,61), + wwH16(49,4E,D4,C7,4A,5C,0B,5E,D2,27,C6,B4,12,04,35,8B), + wwH16(F1,70,0E,11,F2,5F,A9,2A,83,96,20,07,21,92,CB,CF), + wwH16(2F,A7,87,93,22,2B,B1,21,66,35,26,3B,52,72,CB,9F), + wwH16(45,19,B4,75,16,BA,FC,D2,92,31,94,4E,BA,D6,30,9E), + wwH16(AA,BA,43,2D,5F,35,C4,F1,61,2A,88,C4,3A,9A,C7,D8), + wwH16(28,5C,37,4D,D2,9A,D2,61,B2,B8,8C,6A,D3,D3,D8,C5), + wwH16(A2,1B,89,75,80,F6,4F,29,CC,56,E6,F3,3E,79,C2,91), + wwH16(6A,D8,48,C2,19,F7,37,63,9B,A1,6A,1D,3A,0C,A1,B4), + wwH16(F7,3F,19,84,A3,6C,AC,B6,2E,11,F7,C5,45,7A,79,97), + wwH16(B5,A5,41,5B,AC,F8,F4,14,D6,84,A0,28,EB,18,4E,3E), + wwH16(BE,ED,7C,90,C6,7A,F2,58,12,B3,88,11,9F,76,F5,A8), + wwH16(C0,FB,E1,91,20,8F,8C,41,79,56,33,9B,8A,54,8C,BF), + wwH16(7D,65,4C,84,59,C5,95,70,BF,0E,C3,E9,34,93,95,19), + wwH16(35,01,BB,31,0D,FF,75,68,16,07,E9,9A,13,B7,D8,A7), + wwH16(B2,BC,1C,DB,09,0D,FC,67,3C,E1,AA,64,8F,B7,7D,05), + wwH16(0F,E2,32,9E,48,C4,18,AF,D3,24,6B,A6,E0,D4,3F,E8), + wwH16(C3,E8,D4,97,80,B5,11,95,4D,2C,95,B4,54,2B,E4,F4), + wwH16(71,C1,72,1A,CA,AD,11,F9,3B,0F,83,B7,E8,B9,08,8F), + wwH16(9E,FA,20,3E,C6,F5,D5,48,BA,A0,EB,32,43,F9,37,5B), + wwH16(31,F9,B9,31,8D,19,1D,94,BA,82,EE,35,BD,FD,B3,C4), + wwH16(9A,C1,76,AD,16,E6,34,76,6C,37,17,4E,42,AF,59,93), + wwH16(2E,87,CD,05,3D,F9,A0,9E,6D,9D,80,08,BC,40,99,AD), + wwH16(AD,94,7E,4E,86,67,D9,8B,A5,D6,FC,82,48,16,45,58), + wwH16(10,30,F1,B6,7B,72,6C,45,75,32,B2,8A,12,2B,CC,22), + wwH16(02,E2,8D,8C,45,9B,99,78,EB,4F,90,BE,17,98,A8,3D), + wwH16(2F,6E,63,58,CA,E9,8A,AB,C0,5F,D6,45,70,2C,8B,4E), + wwH16(CD,C4,2E,76,89,80,ED,F8,FC,01,F3,80,17,56,37,49), + wwH16(27,8D,79,53,1D,79,25,51,A1,4F,63,76,CC,D7,88,D1), + wwH16(28,47,E2,CB,B9,E4,4B,40,2C,89,12,3E,FF,E4,97,CF), + wwH16(E7,9E,B7,6E,60,6A,12,12,76,9C,A2,76,52,E5,A0,6D), + wwH16(DE,6B,C2,6D,2A,48,98,9D,1C,8B,DE,5B,07,DA,86,8B), + wwH16(85,8C,F0,5E,DA,2D,23,44,5E,35,DD,98,B3,23,D2,69), + wwH16(FA,C6,B8,EB,EB,7D,FF,7C,D4,F5,32,F9,68,3E,BE,10), + wwH16(03,E5,50,5B,8C,E6,ED,E3,11,97,D4,7F,8A,95,E9,6D), + wwH16(7E,4A,5B,F1,BE,E9,6D,08,E2,08,BD,92,96,24,92,DC), + wwH16(86,0C,0D,E8,EA,72,37,82,00,BC,B3,A5,ED,1E,8C,A5), + wwH16(E4,62,F5,FB,79,82,6F,89,74,A5,24,4B,35,FD,3A,1E), + wwH16(31,DC,E6,34,0C,70,DB,0C,C1,EE,7B,64,FD,2B,A9,25), + wwH16(BD,78,C7,9C,A4,68,E4,85,AF,B3,FA,62,6C,4A,D0,E2), + wwH16(1E,15,34,39,18,E8,C2,CB,9E,E9,4E,8C,6E,1A,68,5B), + wwH16(0B,76,81,D7,CD,FC,69,CD,01,25,DC,49,03,F4,62,77), + wwH16(6B,10,64,D4,30,E1,CD,60,88,7A,1A,67,24,BB,53,22), + wwH16(9F,76,3F,4B,10,06,D9,6E,BA,C7,5A,F6,FC,7C,EC,27), + wwH16(DB,BD,71,74,79,B7,81,DE,F3,F7,D1,D8,CE,E2,23,8E), + wwH16(C2,84,D2,C9,DE,60,3A,0B,B4,AB,8E,3E,45,AC,A2,10), + wwH16(40,30,89,91,38,0F,73,A4,29,3F,4B,B9,35,14,3D,DA), + wwH16(44,1F,59,94,AB,3E,10,00,42,A3,33,59,45,52,7B,47), + wwH16(2B,11,A5,C0,30,26,84,B2,7D,62,0C,1C,CD,33,13,F4), + wwH16(1F,E0,90,59,E2,4D,D4,3C,21,EA,36,DC,CF,E1,5F,F8), + wwH16(9C,B7,DB,8F,A3,87,55,F6,43,71,E7,69,4D,38,FB,E1), + wwH16(B5,46,92,7C,EC,86,BD,F9,8E,66,34,CB,4E,6E,D2,C1), + wwH16(8C,5F,DE,A0,19,05,8B,F3,69,CC,C7,3D,D6,A9,7C,46), + wwH16(84,75,B4,6D,62,20,92,94,70,33,10,C2,20,D3,76,D6), + wwH16(24,47,E7,2E,D6,3C,F2,E1,29,9B,84,4A,05,B6,A2,8A), + wwH16(D8,3E,8F,8D,18,6D,26,94,36,70,76,5C,05,16,E7,09), + wwH16(FA,3A,B1,2E,7D,FD,0D,B4,13,6F,2D,1C,B4,B9,2F,90), + wwH16(F1,E3,FF,34,96,B9,96,A8,76,FE,A6,58,44,67,32,F0), + wwH16(2B,9E,11,ED,93,80,38,81,0B,EA,35,0B,75,C3,8F,C3), + wwH16(93,95,42,1E,3E,2E,41,6F,4A,32,4E,14,D5,B8,1B,C0), + wwH16(89,50,72,F9,76,9A,82,42,30,5F,B6,EE,9D,88,78,8D), + wwH16(E9,15,44,D1,85,66,7D,82,84,6B,41,3E,31,A2,6E,99), + wwH16(0C,56,3D,1D,09,6E,32,D7,15,E5,0D,80,56,06,5C,4F), + wwH16(AD,05,07,CC,B3,B9,32,C7,F1,0C,43,72,D0,75,ED,C0), + wwH16(D3,21,B0,4B,6E,8B,36,4D,3E,0A,D2,37,D5,53,92,EC), + wwH16(31,94,01,3F,59,A3,AB,C6,77,7D,8D,39,2A,17,E3,C2), + wwH16(0E,44,4E,36,DA,AC,E5,9A,3F,A8,9D,36,90,C7,59,32), + wwH16(FC,E6,31,37,FF,94,EC,2D,01,CF,C9,8E,92,0E,96,7F), + wwH16(4A,00,0F,59,98,9C,05,2A,8F,73,95,AB,97,DD,6A,63), + wwH16(CA,95,9D,8C,5A,77,73,B0,4D,03,63,D6,2C,AD,8B,01), + wwH16(4C,CF,65,08,BA,D0,C4,8B,5B,3E,B5,62,44,39,9A,72), + wwH16(63,7E,B0,1D,2B,D6,37,C4,A3,4B,7A,8B,9C,4F,03,79), + wwH16(E6,36,88,F3,B1,FA,AE,5B,15,84,12,CF,E4,94,14,9A), + wwH16(01,4E,CA,E0,57,89,22,86,1C,92,38,DA,F4,62,41,0F), + wwH16(38,E0,35,16,90,38,94,0C,D1,F1,A2,BB,72,2E,38,A4), + wwH16(D0,3C,53,0D,29,B1,7B,45,22,13,35,14,B3,A3,56,2E), + wwH16(39,3D,F9,40,96,3D,D0,FE,0E,B9,43,2B,95,FB,60,23), + wwH16(6D,B9,33,39,55,2A,71,8A,01,11,3B,AB,7D,A4,FB,3B), + wwH16(70,2C,FF,8C,71,8F,61,3C,70,CD,43,97,80,58,B4,4B), + wwH16(DC,5E,CB,6A,90,EC,AC,42,58,0B,29,E9,D4,5C,08,B1), + wwH16(27,6B,1D,A5,58,A9,E7,1D,70,5A,21,52,26,C4,7E,9C), + wwH16(63,91,A6,D4,FF,B4,A2,39,24,EF,E7,2E,50,AF,13,14), + wwH16(99,37,BC,4D,A3,AA,60,6B,EB,60,D7,12,22,32,36,85), + wwH16(98,24,DB,41,B3,A9,62,41,F9,45,A9,FF,33,C8,8E,B9), + wwH16(95,4F,71,B9,8C,0E,A4,65,9D,16,03,9D,22,8A,BF,CE), + wwH16(1D,B7,18,6A,C3,27,20,FE,05,5B,C7,3B,83,A4,23,A3), + wwH16(7C,32,53,D0,B9,DB,79,9D,17,EB,B9,9B,E0,B9,0F,74), + wwH16(65,7D,FB,CC,BB,1A,82,45,5D,D9,4B,58,4A,E9,84,9B), + wwH16(DA,39,A2,07,FE,87,BA,73,0D,C8,E5,73,E4,DD,54,0D), + wwH16(2C,AA,58,60,67,32,51,2B,08,98,0C,1D,85,FF,16,B4), + wwH16(41,BD,C2,A1,15,91,24,6A,81,81,FC,19,24,67,CB,0D), + wwH16(3B,85,7D,9E,04,2C,F9,C9,45,DE,FC,45,61,B4,B2,A0), + wwH16(E6,6A,F8,AE,7F,8B,BB,2F,96,62,5D,DD,64,26,74,FB), + wwH16(B5,C9,3C,30,C8,12,6E,7F,21,49,4E,16,E2,E4,39,BF), + wwH16(67,B2,41,30,F6,3B,1B,DA,C1,64,05,C3,19,4B,39,FF), + wwH16(C9,45,89,EE,17,30,60,1A,25,C3,34,CD,E7,B3,E9,FF), + wwH16(BE,B8,87,5B,BD,EE,F8,C2,DB,D9,4E,83,57,83,0A,0C), + wwH16(76,1D,9C,0E,46,30,97,F8,C3,F7,3D,56,19,58,70,32), + wwH16(55,D9,60,72,F6,4A,9B,44,20,31,3D,90,F5,2D,7D,1A), + wwH16(8B,6A,2E,59,66,87,C6,65,61,2C,94,A1,D1,F4,EC,DD), + wwH16(95,6C,54,48,26,EB,42,20,E3,85,5F,76,CA,00,EC,E2), + wwH16(C6,76,6B,3F,F8,69,1A,0B,82,68,8A,37,04,33,FE,01), + wwH16(08,27,69,85,C2,A7,37,1F,CA,5F,70,A2,65,3B,03,81), + wwH16(2C,E7,D2,FC,7B,ED,D2,9F,3A,B4,87,34,21,0F,6A,36), + wwH16(B6,4E,91,CD,0E,AE,27,3A,02,E3,65,FD,79,06,E3,34), + wwH16(C1,2D,71,D6,52,53,2B,BA,99,41,85,CA,0C,20,20,F2), + wwH16(1D,FE,A5,A2,C8,5F,DB,C3,A7,13,D5,6C,A1,7D,9F,33), + wwH16(C0,56,84,0E,BB,03,16,6C,3D,27,50,EB,9D,71,96,C5), + wwH16(4B,F9,3E,01,65,F1,5E,90,2B,0D,96,6A,63,3D,65,96), + wwH16(37,61,05,22,A1,00,9C,52,34,A1,5A,1F,1E,66,C7,0B), + wwH16(67,E5,A9,DF,E3,56,17,86,B0,A6,4C,57,71,93,16,CF), + wwH16(D4,D0,83,46,9C,1E,11,49,34,93,61,9D,A4,A1,22,41), + wwH16(52,6E,04,9E,E2,62,1E,B4,E5,0E,B6,39,7E,AB,A9,83), + wwH16(F4,EA,15,54,4B,E6,CC,0D,46,D3,B4,20,2F,3C,95,9B), + wwH16(69,97,7D,9C,60,C5,43,CA,5F,87,0F,F6,9B,6D,57,9D), + wwH16(24,14,2C,63,2B,6E,B6,48,1F,55,4F,DD,96,02,63,5F), + wwH16(F1,02,0E,34,0E,94,1F,6B,ED,FE,43,AA,E6,FD,CD,EB), + wwH16(1B,79,30,E7,65,E3,66,59,F6,66,AE,2F,78,98,55,1A), + wwH16(F1,F0,04,84,FA,BD,08,7A,01,E8,73,33,C3,B0,64,F5), + wwH16(D5,80,7E,1A,4A,FA,4E,11,14,B8,84,C9,E0,56,01,4A), + wwH16(70,93,4F,07,47,09,33,C0,AD,33,04,2F,03,04,98,7D), + wwH16(6D,7B,B7,08,74,C1,24,DA,C0,CB,F0,70,CE,3D,87,1F), + wwH16(E2,6C,10,DE,82,48,11,C7,76,DD,BF,D5,B3,FB,53,21), + wwH16(B1,47,0E,2D,CE,B6,6F,3F,42,EE,D1,C7,94,26,57,28), + wwH16(76,A1,FF,A1,53,CC,1B,81,2D,D6,8B,0B,8D,78,DB,7A), + wwH16(4F,71,4E,5A,74,BE,2C,C1,8A,E1,2D,EC,13,49,E8,B9), + wwH16(4E,89,52,4A,98,79,51,63,12,D5,13,27,54,89,74,EB), + wwH16(3C,F3,3B,A1,96,54,6D,0A,04,34,69,C8,9B,87,52,9B), + wwH16(9B,80,7E,5D,34,C7,AF,21,88,03,B2,F7,7A,58,D4,7F), + wwH16(1C,6E,F2,5B,C7,CF,78,AB,35,90,92,00,7B,1B,6F,85), + wwH16(67,54,85,1F,C4,18,6A,E2,E7,31,69,A3,98,CC,24,3F), + wwH16(7A,E7,35,BC,64,E2,5E,37,90,4A,48,26,A1,F6,01,74), + wwH16(56,DE,C2,1F,37,79,A9,B1,36,C4,D3,15,4E,48,5C,8A), + wwH16(30,1A,04,97,45,CB,1C,14,3C,50,EC,28,BB,C1,00,DB), + wwH16(4F,A2,8F,DE,C5,AC,67,6E,AD,88,E6,0D,87,33,63,07), + wwH16(85,F2,F8,83,30,A0,77,EB,EC,56,07,08,86,12,90,EB), + wwH16(9D,DE,9A,AC,6B,C1,30,2B,95,CA,EE,3C,46,AB,B4,53), + wwH16(98,02,98,C3,05,F9,22,05,29,4C,1E,7F,6D,20,B5,66), + wwH16(AA,5B,76,0F,CA,FD,E2,DC,1E,03,D1,BE,F3,F5,7B,18), + wwH16(18,AF,A9,5B,17,65,E5,B2,05,F8,30,2D,5A,D7,F4,56), + wwH16(6F,9A,F5,AC,7A,34,79,B1,EF,21,67,B3,8A,D2,2C,96), + wwH16(11,A7,5C,56,19,14,6B,87,B0,57,63,F0,BA,BB,FE,5F), + wwH16(07,77,A3,69,63,EF,02,BE,C4,94,6B,F4,79,CF,86,7B), + wwH16(CB,E0,C4,CF,37,D6,A6,2A,24,5E,3C,6F,FC,0D,CE,E2), + wwH16(11,1F,88,4B,CA,45,11,1B,DD,0C,C3,20,24,4E,9A,FD), + wwH16(C2,F4,4E,CB,8C,AA,5E,D5,49,F2,31,8C,1C,5B,28,57), + wwH16(0A,5F,18,FB,A2,58,D5,E7,B7,1D,D9,BD,61,A0,1D,92), + wwH16(29,8F,70,BC,A5,5A,40,04,04,EB,A4,D2,E4,CE,CD,40), + wwH16(82,A8,6A,C2,64,7C,2F,B4,42,4F,5A,1E,47,CB,AD,A2), + wwH16(BD,16,E5,4E,A6,F4,3B,61,8D,C0,9C,4E,F3,72,02,D2), + wwH16(58,40,9F,FD,11,CF,5A,02,E7,CA,1B,3B,3E,68,5D,D2), + wwH16(B1,90,FB,21,E3,1E,35,ED,AA,53,93,23,CC,DE,71,A7), + wwH16(D3,0C,A3,36,94,56,27,BF,78,F6,E9,DE,BD,A9,C0,9D), + wwH16(34,30,4F,95,CB,98,74,E3,EF,AC,D8,9F,78,1B,34,24), + wwH16(9E,6F,FE,EA,53,78,FD,44,66,73,14,39,8A,EB,E9,4C), + wwH16(B0,93,4A,50,9C,71,87,9D,58,DF,BC,AF,01,31,61,B8), + wwH16(6B,75,9F,E1,36,C2,7D,70,08,65,B0,C2,3F,26,CB,9B), + wwH16(CE,3E,0D,A4,35,85,0D,93,DD,A2,84,5E,D0,62,EB,62), + wwH16(9F,F5,C8,67,FE,39,B6,5A,9A,AD,A5,F8,D1,DE,F4,76), + wwH16(C4,EF,C7,B1,A8,34,34,DD,79,13,41,4E,AB,17,C1,DE), + wwH16(7D,19,4D,E4,F9,62,B4,F4,FB,BD,DD,04,9C,77,6E,AD), + wwH16(4C,95,DF,E1,70,24,83,E2,C0,CC,04,74,AD,27,45,F6), + wwH16(6B,2A,89,D8,2C,F4,DC,08,4C,3C,92,B5,17,BE,B7,89), + wwH16(BD,87,06,58,51,1F,5C,72,0F,CC,E0,AA,A1,CC,EA,13), + wwH16(37,D6,7E,E7,69,5F,51,9D,DF,78,63,C8,3F,CE,FC,68), + wwH16(A4,91,AC,2A,44,EF,2B,B5,A8,06,B5,36,68,90,07,87), + wwH16(43,2A,1C,68,6A,7E,57,7C,28,D9,3A,71,2E,E4,23,0A), + wwH16(53,C5,9A,DE,80,7B,1B,5D,0C,DE,43,48,F5,64,BE,AD), + wwH16(EE,72,6C,DE,67,F7,5E,55,D7,6E,4E,6B,57,9C,ED,2C), + wwH16(76,59,25,03,A5,7F,6F,18,FD,FB,0B,3B,21,E3,1D,B0), + wwH16(48,A0,78,E5,FC,C2,F6,79,84,88,32,2B,81,6D,A2,36), + wwH16(AF,4A,4C,B7,12,60,DF,44,21,72,14,A7,FA,93,DB,5D), + wwH16(D2,2F,D9,52,83,C6,BF,1D,DC,DE,8A,5F,7F,50,BC,88), + wwH16(61,56,33,21,1A,5C,41,DE,ED,BC,E2,96,74,FB,B0,DD), + wwH16(5A,96,32,38,C3,24,36,B7,4B,85,C5,C4,F2,9B,D0,17), + wwH16(8C,7D,F0,52,21,32,8F,EC,30,1E,B2,18,1E,0C,F3,AA), + wwH16(71,DE,BA,87,FC,D7,65,F3,7D,3F,16,B3,64,D2,A8,A3), + wwH16(1E,D8,26,57,9A,EF,B7,C9,DE,1E,7B,C0,1E,A4,AD,73), + wwH16(48,22,14,B5,25,CA,96,B2,AF,B8,1B,42,AF,88,81,6F), + wwH16(33,14,01,FF,1A,22,33,FA,F8,64,D9,88,31,DE,D3,CE), + wwH16(0C,BF,13,18,91,04,B9,DC,54,59,DC,D3,65,E2,98,8D), + wwH16(9D,87,09,DB,D8,86,23,E3,7E,82,3E,F3,4F,4A,DD,BF), + wwH16(78,24,C0,C3,17,C2,C0,9D,F0,11,98,FE,8C,D8,7B,2D), + wwH16(48,AF,5F,AA,44,F0,EF,15,15,08,D8,4C,E8,A0,8A,72), + wwH16(61,2A,C1,B4,B1,4A,49,30,85,CD,44,91,FA,6C,62,36), + wwH16(74,20,3C,96,AD,14,EF,F5,51,C7,A1,8F,A8,35,79,A7), + wwH16(54,68,5F,61,D5,42,6A,D3,05,1C,FB,83,6B,8F,31,30), + wwH16(49,D8,A9,CE,6B,4D,64,4A,DE,47,EA,2D,1A,86,83,94), + wwH16(30,7F,64,CD,FC,23,68,79,72,5D,9D,E9,81,57,01,55), + wwH16(0D,BA,5A,20,50,8C,14,84,32,11,5A,9B,75,D2,18,54), + wwH16(7A,19,00,0B,86,87,99,81,0E,94,45,00,21,38,56,BA), + wwH16(7E,21,2D,28,89,1D,6A,86,C1,B1,D6,0E,88,1E,E1,97), + wwH16(C0,F7,7B,37,1B,04,06,43,02,C8,CF,67,9F,53,03,81), + wwH16(21,AC,56,5F,6B,93,FF,A3,ED,49,44,8B,D1,F5,2E,30), + wwH16(60,17,40,4F,9F,36,83,20,8F,02,1B,8B,6F,B4,F3,5A), + wwH16(27,E3,9A,FE,9D,40,DE,76,75,0C,D1,7B,70,63,5A,91), + wwH16(69,51,B0,08,F8,FA,D2,88,1F,F5,A3,00,1B,7A,38,B5), + wwH16(ED,C9,23,43,5B,9D,0A,BF,FB,0E,A8,3C,83,D2,4F,21), + wwH16(87,34,38,DD,B0,01,75,50,8C,50,A2,17,7D,3A,A5,0D), + wwH16(C8,7E,9B,9C,18,F5,53,D1,80,84,C2,C6,5C,99,41,EF), + wwH16(86,F5,19,E8,C4,72,90,8A,7D,6E,8F,ED,AC,56,F8,D0), + wwH16(F7,45,C0,AC,05,81,35,B0,16,28,AD,9E,14,2D,AE,34), + wwH16(C2,36,3C,B8,2A,33,30,77,3D,C2,AC,F2,12,CE,B5,35), + wwH16(E1,D4,27,74,9B,01,07,FC,4C,12,B9,F6,EC,AD,61,C6), + wwH16(F8,4E,7B,B5,D0,44,2D,DC,E7,DD,E5,49,64,C5,CD,02), + wwH16(D8,53,70,41,CA,E7,18,D9,C6,17,E9,F8,DD,B1,96,BF), + wwH16(AE,58,CC,92,BB,55,FC,7B,4C,65,88,2A,8D,BA,24,94), + wwH16(68,50,77,0C,72,6B,1D,49,05,DC,B7,4E,6E,F4,BF,47), + wwH16(CA,72,33,BC,31,D3,5A,59,3A,A4,CC,FA,53,88,4D,D8), + wwH16(32,FC,E0,E5,9F,27,95,DD,AB,FD,A1,B5,24,E9,BB,2D), + wwH16(69,0F,F7,8B,99,17,5C,88,FF,AD,36,60,9D,7E,29,A4), + wwH16(CC,37,FE,2B,12,EA,59,4F,8F,BE,81,4D,43,EB,59,20), + wwH16(3D,E7,83,A0,B4,D8,7B,C5,2A,04,39,2A,5B,A8,9D,B2), + wwH16(E5,C2,D8,75,5E,AF,46,B8,07,39,FF,4E,68,25,83,AC), + wwH16(9B,01,C3,5A,23,6D,B9,41,E7,F9,2B,89,F8,BA,82,93), + wwH16(05,0B,2B,DB,3D,BA,3A,30,3E,79,8D,06,4C,D5,D6,72), + wwH16(D8,70,96,DA,C7,BB,68,38,5F,74,58,8F,ED,76,0B,52), + wwH16(B2,94,D8,FE,15,8D,47,78,F7,68,AC,46,3C,F8,74,66), + wwH16(96,2B,21,6F,9D,07,69,47,35,55,9D,91,B7,99,2F,C7), + wwH16(E5,7B,79,95,8B,0A,2F,CA,29,61,77,6A,C4,37,7C,36), + wwH16(59,D9,C8,85,E2,8C,73,41,31,8A,8D,CB,EA,06,D3,B8), + wwH16(B7,95,D9,AD,ED,04,37,30,D5,4D,EC,02,2F,67,57,5C), + wwH16(44,0F,03,C4,2F,C7,D2,B7,82,E8,F3,82,EA,B1,BC,CF), + wwH16(D6,D7,26,69,47,F6,7F,FA,81,E2,32,6F,A3,E3,64,54), + wwH16(22,0F,02,6D,01,09,05,15,CD,5C,08,E2,B5,4C,C5,01), + wwH16(FC,EB,F8,0B,67,A7,E1,A8,8D,21,43,F2,3D,1C,98,CF), + wwH16(63,F0,4C,4D,82,64,F5,4C,7B,5B,32,11,1B,C2,2C,C4), + wwH16(55,5B,36,0A,27,98,6D,A1,B9,C2,BC,4B,60,C7,2F,6E), + wwH16(BF,4D,FE,ED,84,56,5A,34,7B,7F,0A,4C,37,14,46,5B), + wwH16(0C,63,8B,FD,7E,8D,B0,EF,0B,00,71,F6,65,C8,9E,A0), + wwH16(D9,FA,FE,CB,02,C9,B7,6B,47,F1,83,1A,99,90,C5,3B), + wwH16(5B,E7,D1,88,EA,AB,BD,5D,B0,0B,C3,20,69,DA,AF,AE), + wwH16(15,26,DB,EC,37,97,FA,21,56,E4,BA,81,D1,4F,E1,21), + wwH16(0B,18,D8,C0,E6,88,6C,F2,06,89,EE,F7,B4,4A,6C,FE), + wwH16(5A,7D,12,E9,6D,7C,E2,E1,58,98,EB,8F,D5,12,66,E4), + wwH16(18,CA,96,2C,F0,62,DA,CA,DD,B9,A0,65,DE,4C,78,67), + wwH16(30,72,AA,1E,C1,FC,CD,C6,14,6D,03,F9,E0,19,24,8D), + wwH16(D0,D9,AA,F2,FA,BC,87,B4,62,64,23,04,C0,A6,03,8D), + wwH16(18,36,81,99,74,CA,6C,E0,B2,B5,CF,73,80,8E,9D,9C), + wwH16(BA,34,48,7F,E6,D4,44,93,B9,98,58,25,9C,B9,5A,4A), + wwH16(E7,8E,2A,DD,6C,59,6F,57,77,64,39,AA,00,F9,31,E8), + wwH16(CE,D7,CC,23,E6,E5,19,7B,34,9F,C7,95,29,A9,1A,93), + wwH16(4E,D2,6D,0F,10,CC,D3,7B,59,13,47,9C,31,AF,9F,B4), + wwH16(3A,A1,98,B8,D1,8A,1A,5A,4B,D2,E9,C0,04,45,B7,1E), + wwH16(6B,D4,5E,D5,43,15,69,20,BE,96,8C,1C,64,85,5A,68), + wwH16(CB,4A,00,86,17,85,C6,95,EC,B0,1B,8B,97,7B,97,78), + wwH16(03,B1,BF,D6,3C,95,E9,AC,C0,B2,0E,DA,A1,C9,03,57), + wwH16(79,02,16,01,F4,46,7E,8C,20,36,99,19,47,52,00,B3), + wwH16(D4,C8,2A,7B,33,61,26,A4,53,BE,C3,D3,92,C3,49,0E), + wwH16(76,EC,D3,6C,6F,6E,BE,17,3D,35,11,32,AC,7F,CE,82), + wwH16(E7,40,66,FD,AB,0C,E3,CC,01,81,BF,6A,E1,30,49,E9), + wwH16(82,D9,DE,F1,62,99,93,4A,DC,3F,6A,0E,A3,E4,8D,0C), + wwH16(E0,E0,BC,1A,D0,7A,DA,52,CA,90,F3,DC,65,42,16,92), + wwH16(76,33,D3,8D,15,29,A9,B5,DB,83,7F,8F,C9,FA,D1,79), + wwH16(88,B4,88,8F,B6,73,CD,78,27,D2,6E,E4,13,5D,FA,3B), + wwH16(99,A3,D5,C5,6B,2A,EF,57,A9,1E,71,11,E8,A2,19,C3), + wwH16(4D,DA,3C,C1,BB,77,02,22,15,0B,F0,E0,55,E7,A7,37), + wwH16(AA,96,0F,DB,74,7B,F3,36,1C,17,6B,58,C9,C5,D8,EE), + wwH16(F7,76,96,D5,91,BD,EF,C4,2E,86,69,03,CE,ED,0A,CB), + wwH16(CD,BE,D8,FA,E3,C8,9D,B1,8A,98,38,8F,A7,89,B7,29), + wwH16(1A,6E,41,14,D1,A2,45,95,97,1A,60,57,FA,CF,9B,6D), + wwH16(F0,6E,BE,73,BD,A4,25,22,9F,BA,0B,64,DA,7A,2B,D8), + wwH16(10,15,8E,02,24,72,5F,5A,94,C7,DE,46,51,BF,CE,A5), + wwH16(00,E8,73,4B,1C,6E,5C,05,BA,FC,7F,20,79,1A,8C,DB), + wwH16(DD,66,D2,60,79,53,C2,6F,3B,B8,49,E0,A2,20,95,F3), + wwH16(1E,14,F3,42,4D,83,70,C1,BB,B7,7A,1F,6F,08,9F,DF), + wwH16(3D,88,7C,14,09,59,7D,BC,91,DA,FD,86,C2,DE,A5,E7), + wwH16(19,DC,76,15,2E,6E,B0,48,D3,85,D5,20,B3,CC,2A,BB), + wwH16(93,2A,5E,49,8F,D5,89,90,FF,A0,F9,0F,1E,4B,B3,95), + wwH16(9B,21,7A,48,6A,4A,91,A4,83,29,72,10,BB,92,D9,89), + wwH16(7C,CA,DF,9B,EE,60,18,A1,FB,DC,FD,B3,26,C0,21,94), + wwH16(25,81,63,2C,3C,01,AA,D9,1D,A6,B8,10,27,9F,80,30), + wwH16(F4,ED,65,ED,FF,67,BE,BC,1C,ED,93,6A,CB,D7,60,98), + wwH16(5A,37,94,4A,3E,30,89,2B,47,EB,1A,57,23,20,25,99), + wwH16(B5,76,B1,3E,5C,FA,08,4D,85,D7,01,E2,AF,F8,EC,C9), + wwH16(72,24,4F,A1,8E,9B,FC,61,79,C1,CC,9A,47,FE,14,D3), + wwH16(5C,21,E5,4C,A5,43,E2,69,D8,3B,F6,0C,19,B0,E9,B7), + wwH16(95,AE,73,6F,80,47,B6,7D,CE,44,81,48,85,27,B4,29), + wwH16(E7,D9,C1,A8,65,A9,3C,BE,CA,45,9A,60,60,E6,C6,74), + wwH16(4B,03,6D,B2,52,62,33,1D,93,91,B1,DB,2C,0A,C6,5D), + wwH16(2D,B8,B6,00,3C,B7,06,00,05,8F,CD,5C,92,FD,0F,6C), + wwH16(CD,C1,43,98,6A,C5,AF,1F,35,E8,56,39,86,7D,91,05), + wwH16(80,D7,BC,4B,3D,3B,73,20,85,0B,22,34,58,3A,83,27), + wwH16(F1,C4,39,B4,2D,B0,E8,12,64,0F,D1,97,B8,58,91,5D), + wwH16(33,06,3C,E5,E9,21,61,E4,69,CE,40,27,99,09,BB,F4), + wwH16(CC,7A,B6,90,FC,37,45,14,2C,EB,FE,5A,90,16,F1,2A), + wwH16(E5,8C,EE,03,96,01,41,FB,BB,0D,B5,4C,D5,C5,DA,9F), + wwH16(6F,2B,E1,81,90,26,1D,07,2B,FD,B1,78,30,96,5D,2C), + wwH16(4B,71,4B,0C,6B,B8,82,D4,6D,CB,67,78,A3,F8,3F,08), + wwH16(5D,80,8A,23,84,F9,BC,2A,0E,F1,B3,64,47,8D,31,FF), + wwH16(FA,AB,4A,EE,AD,D7,9A,88,03,63,2A,1B,56,A3,CD,B5), + wwH16(84,51,FD,76,51,3A,80,9F,1B,AD,72,F3,6A,A5,DD,3E), + wwH16(ED,6F,A3,3D,4E,A5,7B,55,2D,8A,7B,87,86,DD,2B,CB), + wwH16(7C,B3,39,A2,05,AD,2B,90,D0,F6,21,52,FA,D2,31,B3), + wwH16(96,B1,66,CD,4D,01,2C,D7,9F,C6,3B,DE,85,32,42,E9), + wwH16(F7,F1,46,50,98,F9,62,0D,7B,82,E1,7B,DF,BA,C7,F4), + wwH16(38,FC,21,FC,F8,53,BF,62,4B,92,00,3A,98,89,40,D1), + wwH16(43,8E,19,E8,5A,DD,05,AD,4E,DC,D1,55,4E,45,F5,57), + wwH16(C9,F1,09,13,EE,BB,C3,93,0A,F2,87,02,43,E4,5E,55), + wwH16(05,3F,67,4A,50,8A,A1,E6,81,F6,7E,C8,31,55,91,CD), + wwH16(89,8E,85,0C,F4,38,BD,7A,DC,85,F5,49,11,09,46,F8), + wwH16(BB,99,79,A4,6C,4A,B4,0B,7E,B6,5F,FC,A1,9A,89,B6), + wwH16(C6,51,22,11,1B,55,66,A4,E3,B7,10,83,AF,0F,AB,D6), + wwH16(49,1B,26,91,0E,AD,5B,5D,CF,87,6A,37,89,55,22,4D), + wwH16(33,C5,EF,0C,1A,89,D5,61,D6,40,16,3C,25,34,DB,59), + wwH16(67,52,8E,08,46,B0,55,10,72,B1,18,B2,EA,F1,2B,2E), + wwH16(8B,C7,A8,18,4C,B5,D8,85,11,4A,5F,CB,97,52,9D,55), + wwH16(03,9F,B6,CF,27,76,23,C6,7E,5E,9C,5B,79,E7,86,1B), + wwH16(77,93,4C,0E,25,F5,10,4E,1D,07,9B,20,71,3F,E4,E1), + wwH16(44,19,6A,C4,9D,7C,DC,42,B6,30,1D,F0,D9,89,CC,44), + wwH16(22,40,B9,81,08,E8,CC,EB,31,D8,2B,F4,CC,EA,55,B3), + wwH16(C6,69,20,A8,09,53,71,DE,20,47,9E,05,C4,4E,B5,7A), + wwH16(A7,7F,56,03,2C,B7,28,88,27,FD,01,D5,9F,C7,4A,CD), + wwH16(A1,28,9E,03,E0,FE,D6,BB,39,10,12,11,B1,09,90,46), + wwH16(FE,06,EF,21,32,51,64,27,24,92,B7,FF,C9,5F,58,59), + wwH16(B8,77,72,EB,C0,36,CF,1A,1A,62,B1,ED,D9,3B,0C,89), + wwH16(B3,4D,95,63,38,7C,C9,2B,5F,41,25,BC,8A,07,C3,26), + wwH16(32,61,18,CA,BC,53,5C,FA,40,E6,54,6E,FB,5E,A8,10), + wwH16(97,ED,0E,A6,3A,CD,17,D9,82,4C,F6,FF,AA,6C,A5,2B), + wwH16(47,3D,F8,DA,74,D0,E0,4E,1B,4C,6D,6B,AC,EB,7A,83), + wwH16(E6,91,01,91,EE,77,80,B2,AC,4E,29,05,E5,6C,37,C4), + wwH16(3B,AA,5B,C1,1D,5A,4A,9F,60,23,1E,E5,0B,21,84,6F), + wwH16(BC,87,BF,0D,4D,95,12,E3,24,79,40,53,D5,40,DD,62), + wwH16(B4,B2,6E,C5,D2,C6,85,39,2A,E8,EF,3C,97,BF,F4,68), + wwH16(8A,C3,4D,B4,76,17,52,6D,43,92,55,09,62,92,DB,54), + wwH16(E5,31,2E,57,14,92,07,A1,B6,15,F3,86,5C,91,3C,E0), + wwH16(13,93,2F,02,28,6C,38,06,CC,6A,6A,D1,F1,00,5E,4A), + wwH16(CD,3B,31,FD,C4,F6,09,76,B6,59,9F,4F,6A,DC,36,96), + wwH16(F9,51,53,51,EB,BF,E1,27,FF,F9,C9,E6,BE,8D,F1,D2), + wwH16(C4,0C,82,CE,EA,82,53,5C,E4,71,53,70,37,89,42,EC), + wwH16(35,D5,9A,1F,A6,B6,F9,82,B6,62,85,D0,31,CC,DE,29), + wwH16(26,6B,1D,C6,73,01,C8,41,10,86,66,A4,8F,0D,A8,44), + wwH16(5F,7D,9A,6D,C7,4E,E6,3C,82,87,81,03,D8,96,19,FD), + wwH16(F6,0C,87,32,B5,B7,F2,AA,2F,CC,FF,F3,20,9B,A1,7D), + wwH16(33,11,3A,07,2A,18,01,6A,32,66,89,CF,5F,45,4D,88), + wwH16(47,E4,B8,57,6D,CF,C1,B6,C7,14,0D,4F,89,2F,D4,91), + wwH16(67,42,96,ED,D2,1A,2A,E9,D9,A4,6D,D1,D7,44,A5,E5), + wwH16(8E,80,F2,A5,B7,C9,0C,4F,B6,29,4E,CB,1B,78,FC,E7), + wwH16(FE,2F,AE,70,DC,6A,2D,66,1E,D2,4D,FC,59,50,AF,0E), + wwH16(9C,FF,3A,FB,A2,BD,38,8D,77,56,0C,89,54,BD,9C,9A), + wwH16(2B,BF,87,CC,D1,85,A2,DE,90,F2,2C,B9,B0,F7,DC,BD), + wwH16(69,4C,EF,3D,A8,D2,DC,90,E9,F5,11,20,A3,90,BB,DA), + wwH16(9A,07,B4,F1,5B,BB,3A,DC,84,91,25,24,4C,2C,1C,A2), + wwH16(B1,07,9C,A8,61,70,FE,34,42,FC,82,F5,5F,D1,20,7D), + wwH16(09,6A,FF,CF,E6,CD,69,87,41,65,4F,DA,F9,27,82,E8), + wwH16(28,F8,BB,58,08,9E,40,45,D3,7B,56,A0,D5,67,82,0A), + wwH16(4E,55,E7,38,C3,EE,8D,C8,25,28,F0,B1,3A,03,27,55), + wwH16(C3,9E,34,8A,09,07,AD,D5,A4,47,C9,E1,52,51,CF,C0), + wwH16(B3,30,BD,C3,16,BF,F5,2A,A9,6A,33,BF,59,1B,86,51), + wwH16(BE,29,57,A3,82,F5,9F,48,46,44,73,24,23,8C,A7,03), + wwH16(5A,BB,74,28,6C,0F,C8,B2,C9,7F,34,59,1D,8B,8D,FC), + wwH16(B6,69,40,19,76,59,F9,A4,CB,40,0D,72,45,40,B5,DA), + wwH16(7D,EB,93,D5,2D,DB,C2,95,09,B9,5A,49,63,9F,DB,C4), + wwH16(08,FB,D9,89,22,5B,26,63,3F,55,B6,8E,EC,30,6D,18), + wwH16(74,A0,AF,5F,F7,C3,4C,98,FE,69,27,19,BD,B2,97,F3), + wwH16(4F,D0,7F,84,D9,1C,C5,A5,5A,18,92,E0,C2,3F,D5,8B), + wwH16(48,CE,00,4F,71,C5,ED,CC,DA,B8,66,FA,1F,17,CA,78), + wwH16(F9,43,A1,FE,84,6A,82,7D,7F,92,8C,4D,61,0E,15,1A), + wwH16(B3,95,27,D5,1E,A7,0C,E5,E3,53,D7,BB,C8,0B,6E,D2), + wwH16(E9,4C,F1,B4,C3,A8,69,68,C9,2A,A3,BB,16,53,82,E3), + wwH16(5D,60,E7,49,CB,D5,EF,EE,A5,5E,0E,4B,E4,FF,95,FF), + wwH16(B1,22,E9,E1,E4,58,13,D4,D0,1B,C3,C5,43,26,4A,AB), + wwH16(02,87,40,5B,AE,7E,44,7E,98,52,3E,AA,EE,B9,83,82), + wwH16(1B,E0,F2,AD,64,54,3E,6C,70,98,B8,FB,D2,58,E9,18), + wwH16(4F,54,C8,25,39,77,C2,8E,AA,A2,65,13,C6,49,95,17), + wwH16(7B,EE,BA,8D,9E,7E,31,48,C3,F8,B5,2F,66,B1,28,C4), + wwH16(5D,13,31,9C,F8,7B,43,64,D0,46,B4,A5,9C,E4,F4,BB), + wwH16(D2,DC,57,FD,94,F4,F4,C5,65,DA,A8,D9,88,D0,B4,BA), + wwH16(FB,E2,76,6E,13,FC,F7,7A,10,BA,A6,28,18,91,61,3B), + wwH16(5C,94,92,D5,6B,CA,79,38,CA,CA,5A,9C,2E,E2,1E,6F), + wwH16(7D,5A,C4,5A,18,27,11,01,0E,DF,B3,6C,96,95,91,A0), + wwH16(C3,FF,9C,8F,D3,BA,EC,B2,C9,0C,85,F2,78,E4,FC,8F), + wwH16(E9,6A,20,E8,93,CB,14,8E,2D,A2,D2,BE,47,BD,26,32), + wwH16(51,8F,A0,44,66,39,C3,C2,2B,3B,27,F2,C4,B1,D6,2C), + wwH16(CA,ED,7F,8C,40,6C,10,4C,2C,E3,C1,4B,5E,89,3C,A3), + wwH16(38,D7,8F,71,C4,05,3E,F2,9A,E9,2B,25,6C,76,31,7D), + wwH16(67,2C,4C,93,1D,1E,73,25,11,58,26,61,B0,EF,3F,7A), + wwH16(FC,1F,8B,36,81,69,22,53,C2,29,1A,34,06,F7,1D,E5), + wwH16(AA,86,99,95,60,AA,AF,96,40,12,D7,5C,B8,3C,35,85), + wwH16(E7,5A,BF,B6,37,63,58,98,03,40,42,4C,1D,17,AC,FF), + wwH16(EF,71,5F,D3,4D,1D,D3,59,DF,B8,7C,29,A9,CA,35,45), + wwH16(CF,C1,81,CC,95,D4,68,52,E8,B3,ED,B9,AE,F8,0C,37), + wwH16(41,E0,67,FD,76,B7,54,DE,DC,9F,C0,F8,91,22,3C,35), + wwH16(67,59,71,04,A3,35,71,1A,E1,39,5B,EB,09,58,94,C9), + wwH16(AA,CE,61,70,BC,39,66,59,4D,A9,E1,8A,55,27,75,8F), + wwH16(21,3F,32,98,1B,78,80,99,CF,F1,A0,23,A8,00,60,4E), + wwH16(C5,40,54,0B,49,2E,CD,30,DC,C9,13,09,CE,61,BD,43), + wwH16(19,4E,AA,DD,83,F3,E4,1D,6E,3E,1B,E7,FB,78,85,7B), + wwH16(50,94,9A,98,CF,2A,96,9A,00,38,45,00,27,2C,1D,86), + wwH16(D4,7D,D3,8B,13,B2,43,83,01,1F,5C,33,AB,6D,45,F4), + wwH16(39,06,8A,6A,BC,4B,74,1C,77,A2,88,CC,89,2B,37,5F), + wwH16(9E,2A,50,40,F8,02,45,17,FC,78,69,46,49,25,8C,74), + wwH16(93,91,B9,9D,6F,1E,0B,CD,BD,6A,7D,95,6A,8E,8A,48), + wwH16(51,AD,85,7E,ED,9B,CE,D1,1A,B2,0A,A8,80,A5,23,D9), + wwH16(61,72,9F,92,15,56,2D,2A,7A,78,ED,08,CF,F0,6B,A8), + wwH16(69,9E,9A,CC,21,85,CB,1A,6A,66,FF,AA,B8,51,DA,8A), + wwH16(EE,51,55,68,3E,BB,A7,86,64,C2,A7,D5,64,95,A5,AD), + wwH16(9A,7E,D6,20,3A,31,21,D0,59,CB,01,D6,22,E8,31,1D), + wwH16(5F,BB,A4,B1,E3,13,70,37,45,E1,D9,0F,B4,97,D7,14), + wwH16(A5,99,60,76,20,1D,C2,62,C0,B6,4E,34,AE,0F,7E,86), + wwH16(9F,55,C7,35,40,68,D0,52,D4,B8,47,76,D9,C5,02,2E), + wwH16(46,C9,3B,2B,65,46,6E,D8,93,F7,E7,8F,13,CD,1F,52), + wwH16(D8,D8,7A,71,31,D0,81,2A,CE,5D,E1,D0,1A,CA,98,67), + wwH16(10,97,D0,6B,58,1D,27,CE,75,DD,DB,82,06,99,62,9D), + wwH16(ED,4C,3F,44,75,A5,78,D3,1B,0E,83,C3,25,C6,32,5E), + wwH16(3D,B7,A9,A2,E9,F5,04,C1,75,91,A4,C6,C5,D7,A6,D0), + wwH16(0D,DA,78,57,DD,80,0E,13,9E,B9,52,34,5B,4D,20,87), + wwH16(51,0E,6A,16,C2,0B,FE,68,A0,10,79,5C,C6,4D,D5,D8), + wwH16(08,BF,25,08,0F,25,7B,56,1B,6E,94,E8,62,7E,94,EE), + wwH16(6C,49,92,57,DC,56,75,85,C1,16,AA,E4,CC,74,16,C8), + wwH16(19,AC,7C,C2,D4,15,DF,46,23,DD,32,E3,E7,61,9F,B1), + wwH16(C6,85,41,CF,67,33,2B,50,E6,E0,C7,19,97,DA,02,36), + wwH16(12,A9,77,BF,F9,D8,DE,D6,66,06,86,FD,91,47,C7,CF), + wwH16(CB,15,3D,CD,E3,62,E9,87,68,EE,69,B2,D3,00,C5,7B), + wwH16(FF,B5,36,CD,EB,A1,28,F8,9D,50,DE,AD,31,48,E7,78), + wwH16(B1,52,66,66,AC,86,2B,B5,35,8E,75,87,43,64,A9,BF), + wwH16(B9,35,CD,6D,7D,30,B8,5B,F2,11,40,0A,D5,1D,31,9C), + wwH16(2A,72,F6,24,F4,47,91,C4,26,46,DF,0A,6D,8D,75,BF), + wwH16(06,48,C7,E4,AA,C3,11,C8,F8,08,DA,45,30,C5,07,BC), + wwH16(A7,61,DD,DE,D9,1A,F5,BB,AF,E3,E1,AD,A9,A5,EE,CB), + wwH16(4E,AA,93,08,5F,E5,D0,F8,FF,46,EA,DC,60,93,E2,0D), + wwH16(F2,AE,9B,27,43,68,6A,14,A9,E2,5B,D4,58,DC,07,9C), + wwH16(8E,96,4A,01,93,80,3F,25,FE,C4,CB,9D,8B,A0,87,58), + wwH16(1E,4D,79,6D,8A,B7,A2,C2,A4,24,9A,86,8B,5D,23,7A), + wwH16(95,2A,91,60,AE,0A,E9,75,6C,DF,96,9D,3A,17,04,6E), + wwH16(E9,97,F7,C2,2C,36,92,7D,2A,0F,BD,EC,BD,72,A8,28), + wwH16(25,CB,4B,6B,16,52,B3,16,08,13,AD,AA,57,41,C0,DF), + wwH16(42,E7,17,E2,A5,08,8E,EB,49,1B,BD,C9,3F,53,D0,DA), + wwH16(EC,48,D9,B6,36,0C,8B,E9,8A,1D,72,D0,24,EF,6A,27), + wwH16(19,36,76,EB,DD,4C,5A,EA,BD,8F,D1,23,F8,43,11,5C), + wwH16(3A,1B,5A,E4,D3,BC,17,54,D5,1F,CF,B2,CD,AD,A2,9E), + wwH16(21,37,92,C4,D7,A1,E0,9F,75,FC,51,CD,C4,0E,D0,92), + wwH16(56,95,D5,E8,19,D1,39,4B,62,64,3E,7A,12,02,20,CB), + wwH16(B0,5F,75,A4,F7,73,17,B6,CE,2E,F9,6C,82,05,CF,F2), + wwH16(1C,70,00,7E,75,37,E9,57,BE,BA,4F,D9,A7,D5,6E,ED), + wwH16(C0,2E,86,4C,49,9D,96,6B,BA,43,57,D9,CA,92,85,B5), + wwH16(3F,30,D5,46,FD,D3,C8,DF,07,FD,7E,F6,D2,DE,3F,CD), + wwH16(D4,4B,68,50,0C,12,38,5C,26,68,DF,3A,46,B8,01,C6), + wwH16(4A,05,C9,D3,5E,64,EC,93,DA,8B,2C,D7,7D,7D,B2,7F), + wwH16(43,08,26,C4,AB,D2,57,21,CE,23,42,58,C1,CA,93,71), + wwH16(E4,DC,2A,C6,E0,38,0C,BD,D8,9F,FD,F9,75,A4,B9,33), + wwH16(1C,14,00,92,44,70,A4,B7,5F,A2,98,A6,7E,04,44,46), + wwH16(0F,4A,9E,19,6E,BD,14,D1,D6,48,6D,68,C7,C8,9F,52), + wwH16(7F,53,44,E6,BE,8F,16,D2,26,2D,48,AB,24,87,7E,88), + wwH16(9A,E5,13,CF,09,DE,7B,4D,74,54,91,E5,A0,C5,8C,01), + wwH16(88,01,5A,45,5F,89,76,54,EC,9A,C3,D4,B6,A2,B1,43), + wwH16(17,50,82,7F,7D,A2,8D,B5,49,97,9E,F0,63,48,A6,00), + wwH16(A1,6C,AA,20,50,AC,A0,10,19,B4,A5,35,FF,81,DB,A7), + wwH16(EF,A0,68,96,FF,65,B6,AF,DC,53,A3,58,71,7A,05,C0), + wwH16(0B,53,B9,D5,B3,5C,04,77,50,8D,6C,F2,EE,D2,0F,1C), + wwH16(BA,50,34,71,B5,87,A7,94,66,2D,CF,11,9B,F4,C5,A9), + wwH16(4E,D5,60,0D,C9,5A,9B,80,B9,85,6A,3D,FA,75,B7,51), + wwH16(CB,D0,05,DF,C6,16,A6,CA,CC,D8,28,13,60,12,45,6A), + wwH16(43,0B,AA,A5,BC,CB,38,D9,F1,28,A3,F1,04,AF,1E,69), + wwH16(0A,FF,6F,CE,4C,9C,93,10,60,EC,D3,F9,14,48,DD,13), + wwH16(90,2E,93,B5,C5,81,4B,AB,1A,ED,36,FB,36,1D,52,BA), + wwH16(40,9E,DB,39,67,9D,98,49,EB,3D,3F,CF,62,07,57,D3), + wwH16(A8,D2,02,94,D4,48,31,B3,8C,E6,39,58,F5,38,C7,D9), + wwH16(8D,9E,8A,9C,D6,0B,E2,02,88,87,2D,35,FE,17,D2,59), + wwH16(72,BC,56,8A,9A,04,31,42,26,FF,FF,DF,C4,1D,54,AC), + wwH16(E0,98,74,DD,5E,D7,55,A0,3B,46,E1,20,C7,82,9F,18), + wwH16(2E,CB,3C,B3,F8,89,A4,FA,56,0D,5F,8E,73,51,32,ED), + wwH16(9F,3D,4F,6C,D1,F0,76,83,FA,F3,AA,BE,92,93,D6,1C), + wwH16(39,E5,E6,93,99,B7,3D,6E,52,FA,F7,C9,65,0D,11,1E), + wwH16(F2,42,47,D5,AF,65,FF,78,A1,6D,64,77,F8,56,11,76), + wwH16(C7,66,91,31,9D,34,C9,54,5E,5E,BC,41,84,08,B3,BB), + wwH16(15,DD,FD,FF,9E,49,32,0C,8A,A6,5C,69,FF,5D,35,E3), + wwH16(FD,B5,7D,11,9A,B8,F0,AD,DD,91,B5,60,2D,BF,66,96), + wwH16(02,31,BA,A7,67,D7,53,1B,DA,AE,F5,96,2D,55,E8,B9), + wwH16(14,E3,D9,AB,3F,83,7E,7D,6D,54,B9,29,4F,C1,22,22), + wwH16(C2,EC,1E,EB,68,75,25,80,15,7C,4E,79,0F,11,4A,94), + wwH16(0D,33,8D,C8,6A,BD,19,E0,02,30,CB,E8,75,54,D5,F8), + wwH16(9E,58,73,8F,59,11,91,A5,1A,C0,25,0E,70,98,09,4F), + wwH16(37,1F,80,D3,95,9C,2A,1D,23,1E,B6,0B,D7,EC,B2,F8), + wwH16(64,D4,78,98,8F,DF,E9,5C,CC,31,6C,09,BF,A3,B3,97), + wwH16(41,34,CD,3E,C6,51,88,BA,F3,71,C3,D7,A7,66,EF,D6), + wwH16(B6,D2,F7,17,D7,9B,03,16,34,FA,D1,8D,95,49,40,2F), + wwH16(DF,63,05,81,41,79,65,34,FF,80,7E,88,57,EF,B7,94), + wwH16(32,F3,9B,B9,2E,80,73,07,D6,B7,D2,C9,C2,A3,91,31), + wwH16(0C,12,E8,8C,2A,67,EF,03,F7,B2,7F,D6,36,09,F5,E2), + wwH16(9E,8F,08,48,36,FA,57,33,69,30,23,89,7A,AD,88,66), + wwH16(6A,5D,B7,85,6B,4E,3A,D5,74,47,D6,7A,BD,02,8E,50), + wwH16(66,01,FD,11,66,FF,74,5E,95,1B,0E,A6,77,CA,67,98), + wwH16(95,9C,96,64,92,5B,03,E5,A2,5D,05,BC,58,29,98,DB), + wwH16(44,FD,C4,A1,DF,9B,D3,9B,9A,99,7D,07,E7,98,5E,86), + wwH16(A6,A7,CD,76,26,8E,E3,42,54,6F,DF,55,5F,3A,88,A0), + wwH16(22,86,56,C8,92,E8,E4,E9,E8,81,A3,F4,AD,04,83,78), + wwH16(E3,16,38,24,CD,C2,21,B9,04,15,46,E3,9F,C3,CF,97), + wwH16(55,70,FF,CB,17,89,C1,A3,B7,6F,BF,AB,18,3C,B7,A9), + wwH16(3B,C3,E9,35,3F,0E,11,49,61,A2,C6,E8,B8,3A,D1,CB), + wwH16(D9,19,37,D4,86,CA,8F,13,91,CE,BC,FD,66,B3,DD,0E), + wwH16(FE,8D,D1,D4,61,23,D7,FB,BB,07,E6,A5,97,BA,96,C4), + wwH16(47,3C,52,5F,2D,ED,63,E9,D0,C7,A4,6C,87,53,DD,8F), + wwH16(9E,1C,BC,05,5F,D5,AB,56,2E,0D,F2,42,8F,B3,E1,E4), + wwH16(A0,9C,ED,74,1D,75,FC,95,D2,A2,07,E9,C9,13,B4,21), + wwH16(C1,AC,FF,A7,C8,08,5E,5E,CB,E6,43,ED,B7,C0,48,91), + wwH16(0B,16,DF,A7,AA,39,A6,16,92,17,AE,74,6A,8A,0E,89), + wwH16(CE,3B,3F,55,6E,AA,90,4D,9E,CE,3D,96,EB,2A,A2,F1), + wwH16(20,7E,B7,F6,F3,32,89,29,0E,63,CC,58,24,CE,E2,55), + wwH16(37,C7,93,52,E1,7D,6D,46,31,73,C5,6D,17,3E,7E,54), + wwH16(2A,79,AD,1C,5A,CF,53,C1,CF,DF,EC,D9,B6,3A,70,7F), + wwH16(EB,4D,95,24,83,D3,00,0D,E2,43,A5,48,4C,CF,FB,E0), + wwH16(18,8A,A6,9B,38,05,E2,C3,82,53,95,8F,F3,01,C5,79), + wwH16(D6,CC,21,A7,21,D4,42,B9,50,F4,2F,30,CE,59,19,D7), + wwH16(5D,3C,2A,D5,D2,F1,7A,27,AD,6B,DD,0E,67,10,6D,86), + wwH16(DB,30,87,6F,9C,10,0D,CA,C6,2D,59,E5,0A,87,76,85), + wwH16(E6,71,D3,1A,CA,F4,2E,C8,41,D4,53,CA,44,00,C1,C5), + wwH16(18,A6,E4,55,52,D0,BA,EE,10,82,AF,24,9D,7F,6C,02), + wwH16(82,01,33,C1,81,48,51,1F,6F,18,43,4C,11,4F,3B,23), + wwH16(2F,D4,79,40,56,60,6C,DF,8E,4F,2C,48,1B,B7,F0,69), + wwH16(9C,92,BB,71,EE,E4,69,50,69,11,95,85,BE,C0,CC,81), + wwH16(64,F0,B1,03,67,B3,E8,DA,A8,FF,C3,98,85,D8,4C,3F), + wwH16(E7,EE,A4,5C,D1,B8,73,C5,6F,E0,7C,26,94,E9,52,12), + wwH16(C1,DF,DD,E6,E5,E8,FF,24,BD,F0,1D,89,58,C4,58,E0), + wwH16(E2,5A,B0,AB,87,17,81,36,EA,57,C9,AF,31,C7,5B,56), + wwH16(8F,4E,2A,0F,1B,B4,72,7F,2D,DF,2F,65,90,A0,36,6A), + wwH16(51,16,E8,42,89,4E,D0,B4,56,DF,A7,1E,B3,13,B2,2B), + wwH16(43,05,54,16,B6,E0,4A,F7,74,ED,35,D4,08,79,40,1B), + wwH16(C2,0B,A9,F6,FB,F2,A0,2E,0B,CA,F7,BE,1A,A2,2E,15), + wwH16(28,D5,78,14,D8,08,31,B5,DA,F6,0C,5F,0A,8F,61,83), + wwH16(F3,67,AF,53,87,25,1D,61,D1,A2,0C,17,AC,5D,A7,B9), + wwH16(CC,4B,F4,F5,B2,D1,8A,31,07,58,CC,8B,02,EB,23,A8), + wwH16(46,BD,CB,AB,FE,23,1B,97,9D,67,3B,D5,29,C9,6E,31), + wwH16(59,2D,14,5F,E1,5C,82,58,92,74,5A,C7,7C,AF,9B,0E), + wwH16(6F,65,90,B2,EC,4E,4F,3E,3B,31,7B,4D,34,D5,19,DB), + wwH16(F3,C8,65,AE,08,C1,26,02,00,08,EE,A9,2F,F8,60,6C), + wwH16(32,B7,BA,DE,B7,1B,AE,91,11,5D,84,5B,F1,C9,91,92), + wwH16(77,93,99,2E,C3,F8,7C,27,DE,27,BD,F7,35,99,44,CF), + wwH16(FC,AF,50,42,48,D0,9C,51,56,CA,E2,E0,3C,DC,16,39), + wwH16(FD,E4,C0,63,DA,42,12,73,0C,79,2F,13,57,C9,6A,30), + wwH16(68,D2,C5,9A,D3,87,A3,DA,91,6E,C5,83,23,24,4B,3D), + wwH16(F2,F0,58,27,29,9A,30,B8,15,E2,83,27,EB,C6,6C,26), + wwH16(12,5D,76,FD,5A,2C,57,38,E5,27,E1,9A,52,31,9A,74), + wwH16(5E,2C,CB,D6,3A,75,20,5D,43,EC,22,49,68,7B,0D,A2), + wwH16(63,95,4D,FA,84,37,27,A2,CD,95,B1,7F,BB,DC,5A,D6), + wwH16(C6,6B,B4,C2,B8,2F,D8,E6,1C,AC,61,B9,62,E1,99,AF), + wwH16(07,BB,AC,65,FB,6C,D6,5B,79,1F,AB,1E,BF,5C,2B,60), + wwH16(B1,5F,6D,71,8B,D3,59,99,23,B8,B1,64,6D,36,11,1D), + wwH16(32,82,CD,5A,1F,0E,B1,8B,77,AA,CE,DC,CC,BE,6A,5B), + wwH16(68,20,D5,E8,0D,DB,B3,6A,23,DF,F4,50,27,D8,8E,EA), + wwH16(77,07,07,6C,32,EA,4E,DA,B2,31,49,D5,F6,13,97,14), + wwH16(BC,5D,87,34,6C,DB,A6,63,79,C1,F5,9E,FE,76,DE,F1), + wwH16(D2,7B,93,B2,4E,92,BA,77,B5,64,84,41,57,C0,9A,C3), + wwH16(89,02,9B,D7,A2,DC,83,74,79,EF,02,5C,2C,BD,18,01), + wwH16(65,E9,44,FF,09,71,8F,B9,56,2E,94,B4,4B,4A,FD,07), + wwH16(C4,C6,52,3D,33,4D,0C,5A,A7,84,7A,33,5A,8A,29,C5), + wwH16(7A,B9,95,4F,5A,5D,E3,6F,4F,20,F6,8B,AB,CE,77,92), + wwH16(9E,DE,E5,35,53,6A,73,06,F0,06,8E,31,6F,0A,D6,24), + wwH16(00,31,78,58,8C,88,D3,38,4F,D1,01,55,84,60,7B,39), + wwH16(E5,1B,E5,52,C6,47,15,0A,3B,26,6B,9B,CA,D2,29,07), + wwH16(69,EE,FA,5C,B2,41,D3,5F,92,67,49,D6,BC,DC,94,80), + wwH16(99,CF,F5,9C,A4,70,5A,51,C0,DD,35,66,5F,20,1B,31), + wwH16(B6,2D,A3,0A,DA,84,EE,52,B1,75,3B,FF,E4,C6,04,25), + wwH16(F7,BB,8B,B1,54,33,D7,AC,BF,96,43,BD,32,C8,74,AD), + wwH16(DA,38,C9,D5,02,35,31,F3,FC,2D,36,8F,8E,58,8F,60), + wwH16(D6,3E,48,D1,62,4A,0A,1F,37,D8,B1,1D,34,CF,30,C7), + wwH16(DA,CB,58,D4,92,87,DB,49,C3,2B,CB,F5,77,A8,D1,50), + wwH16(8E,DA,F2,A4,54,B7,00,22,F0,E5,56,A8,46,D2,D0,7C), + wwH16(19,B6,B8,E3,A5,A0,75,CB,BB,74,84,30,07,F2,40,9F), + wwH16(14,21,68,1F,24,16,19,6E,7F,6F,12,58,48,DC,26,7B), + wwH16(41,58,79,1F,F1,CC,5C,D3,23,E2,AE,FB,A7,A5,D0,32), + wwH16(84,2C,D0,E6,86,40,9E,0D,B8,57,55,0F,CD,2F,E9,47), + wwH16(B7,C2,E5,C1,BC,A1,C4,96,3A,E6,BC,DE,6F,AE,71,47), + wwH16(46,B3,D1,B1,C1,06,86,E8,F4,C1,D3,13,37,97,FE,98), + wwH16(68,F9,68,0D,7E,5A,0C,DD,E3,69,78,2C,4F,84,98,1D), + wwH16(08,9F,E4,BD,15,6E,D6,CD,48,25,68,01,8F,B2,69,21), + wwH16(EF,E4,81,A5,50,87,D6,DE,40,7A,0F,AE,7C,6A,63,C3), + wwH16(8C,7D,0E,C0,84,8C,A5,50,FC,25,62,E1,46,F8,34,AD), + wwH16(55,67,D3,84,8D,01,A8,1F,06,C0,09,B8,52,30,B1,AC), + wwH16(F8,E8,97,C7,D4,EC,C8,2F,E0,80,54,5A,E6,DD,8C,11), + wwH16(98,79,74,50,D5,88,BC,DE,27,9E,7F,A1,CA,B8,33,56), + wwH16(69,E5,E6,8D,F2,27,D1,F1,3A,3F,6E,5F,E1,00,32,D3), + wwH16(16,12,E2,36,12,FF,68,15,4D,81,03,C6,BE,78,2C,44), + wwH16(F5,12,BA,04,F4,2D,DD,78,E1,87,7A,69,DB,AC,DA,AE), + wwH16(7E,33,2A,BC,7B,F5,23,A0,71,F2,F6,73,85,17,1D,D7), + wwH16(CC,EF,80,31,34,B4,8A,E8,40,D1,CC,14,7D,75,46,A1), + wwH16(7A,15,D8,E1,B1,15,08,38,F1,04,2F,BA,BC,B0,26,C6), + wwH16(2E,2F,41,5D,A9,A5,0B,B8,38,E0,A2,A5,56,62,B3,D3), + wwH16(88,5C,49,A0,E4,53,6C,91,52,7E,11,1A,44,0B,6B,49), + wwH16(5A,02,95,05,A8,47,DA,69,15,A7,B2,3A,E7,FE,11,1A), + wwH16(3F,8D,AD,57,36,94,5A,AC,6E,1A,C0,A4,0F,21,7B,78), + wwH16(45,74,94,8A,B0,4F,BE,0E,99,E4,AC,B0,0F,E8,C7,5D), + wwH16(DE,3C,E3,32,22,BC,51,5E,4A,3B,44,E7,0C,C7,73,6B), + wwH16(98,77,C7,CF,AF,9F,BC,EF,C7,F6,68,5F,AA,6B,DD,18), + wwH16(45,22,4D,B9,00,FD,CF,4F,06,E3,CF,8C,0B,58,AC,39), + wwH16(AF,C2,1B,4D,16,36,E1,EA,23,BA,14,D5,A8,22,D2,B7), + wwH16(22,1F,10,88,EF,8F,17,1D,83,62,52,FA,44,5B,FA,AD), + wwH16(78,5F,26,C2,06,E1,61,EB,B9,02,6A,5E,7A,6B,40,BA), + wwH16(85,23,63,D5,95,D5,E4,4D,46,97,E9,E3,8D,E4,92,AF), + wwH16(06,23,70,8D,42,6C,62,D8,70,88,0C,72,36,B8,56,87), + wwH16(2E,D3,D4,7D,4C,D6,AA,CA,C0,83,F0,F2,72,CF,B9,C0), + wwH16(46,56,ED,0E,17,F4,82,F9,28,3E,D8,77,07,BF,DF,43), + wwH16(35,09,DB,9E,36,53,F2,F9,69,7F,3B,53,D5,5D,15,A9), + wwH16(A7,73,6D,44,E0,91,BB,1B,80,1E,DA,70,7A,6E,F9,A0), + wwH16(94,51,B2,96,4D,93,D6,8C,3B,AC,E9,1A,48,4D,1E,2B), + wwH16(2A,CF,A7,B9,48,DE,F6,29,00,79,4E,26,98,0F,DA,43), + wwH16(CB,CC,EC,1A,41,1B,9B,88,B8,28,9F,55,08,63,82,C7), + wwH16(12,CA,F7,51,01,C6,29,FA,48,B8,39,62,C2,53,BF,EA), + wwH16(B8,70,30,B0,51,C4,70,99,D7,8A,CB,A0,6A,45,28,C1), + wwH16(8B,F7,28,AB,CC,3F,FD,0D,1B,33,60,23,3D,93,E8,5F), + wwH16(9C,72,28,B9,EA,45,DF,BA,DA,58,56,C7,E9,6F,5D,89), + wwH16(2C,78,DD,22,19,AD,79,4B,F9,2B,1D,CC,59,E2,BF,6A), + wwH16(3C,6E,9A,CA,07,94,01,10,75,E5,62,C9,18,D5,46,EB), + wwH16(77,46,0D,CC,FC,C0,81,D0,B1,E9,77,45,73,F4,38,CD), + wwH16(73,F0,08,FF,3C,9A,95,66,3A,FF,27,5F,40,3B,2D,1F), + wwH16(B0,36,E5,D4,32,93,EF,6C,06,D4,4E,96,43,97,52,D0), + wwH16(50,94,A7,16,74,3D,60,FD,44,6F,9B,74,1A,AC,E0,85), + wwH16(E0,72,C2,42,99,7C,62,16,D6,2C,A0,8B,BB,5C,E5,C8), + wwH16(62,2F,1B,7A,8F,A6,94,1A,D8,1D,49,FE,FB,0C,51,7C), + wwH16(CD,9B,26,FB,82,99,66,04,AD,65,3E,71,08,56,1C,BF), + wwH16(CF,E7,DF,17,9F,A9,06,48,B7,16,A0,FC,90,62,8C,EB), + wwH16(FD,36,DE,62,62,A9,12,67,0B,AE,24,88,E9,37,21,3E), + wwH16(71,A4,60,35,7D,33,3B,87,84,19,CE,F7,D3,BC,21,37), + wwH16(C8,D8,1B,0C,C0,C9,25,6F,70,69,66,67,D3,DE,45,B5), + wwH16(9E,94,D0,F5,15,86,3A,3E,4D,C5,EE,CD,DF,09,8E,AE), + wwH16(CA,10,8F,78,15,C1,C1,58,CD,A9,5A,BD,D4,D7,5C,1F), + wwH16(27,CC,58,93,FC,21,7A,C9,D6,98,84,9E,83,75,86,60), + wwH16(AA,58,AE,B4,37,26,DE,1A,41,14,C0,26,5B,0E,F4,BE), + wwH16(1C,8C,89,F4,4C,35,21,60,78,EC,84,06,43,F9,89,00), + wwH16(DF,9E,D2,4A,8D,D6,80,9C,F6,E2,C0,A6,E6,DD,C0,0B), + wwH16(92,1E,38,D2,EF,08,A0,9E,CD,BB,F0,FA,4D,57,4F,2B), + wwH16(B3,B7,0D,AB,8A,25,3E,32,6B,F1,D3,1C,98,80,9C,2B), + wwH16(1F,2F,9A,00,10,C8,61,44,85,F0,17,FA,6B,E7,63,FB), + wwH16(E8,05,89,5B,DD,9E,B3,2E,42,D6,D7,E3,55,93,0B,EF), + wwH16(3D,6B,17,C7,B9,AB,DC,1C,02,CB,F4,82,73,5B,F0,46), + wwH16(6F,FE,C6,95,A0,20,F9,06,24,6D,96,D0,63,69,12,78), + wwH16(83,E8,18,AB,5F,AA,B4,30,41,66,9E,49,7E,5F,54,5D), + wwH16(58,D4,54,55,92,80,1B,B8,05,5D,0D,74,9F,C3,1C,A1), + wwH16(13,06,B8,4A,00,44,F6,21,A5,13,0F,0E,C2,FC,EB,40), + wwH16(D2,2B,99,B7,50,65,7E,93,40,6B,EC,58,01,2F,6E,92), + wwH16(B7,0A,0B,E1,31,CF,29,10,57,51,BC,EE,70,A1,27,25), + wwH16(62,A1,5D,3F,44,4D,5E,D6,83,6D,80,21,46,4F,47,05), + wwH16(7B,E5,B9,1B,8E,57,6B,71,A8,6E,FE,3B,DF,FB,F6,3E), + wwH16(3B,59,A7,F6,2B,B5,BF,59,9C,11,9D,7F,F6,11,71,F4), + wwH16(CF,A4,C6,00,27,B4,26,66,3F,C3,21,68,01,8D,9A,13), + wwH16(09,AE,72,06,A2,23,6D,31,00,0B,44,59,B5,C4,86,3E), + wwH16(D6,E5,3E,6F,5F,31,03,C5,05,81,9F,5B,3B,75,F2,F0), + wwH16(0A,1B,DE,71,93,C2,90,3E,6D,D0,45,2A,D8,75,E8,AF), + wwH16(C5,7F,43,A4,BE,DB,B6,61,E1,7B,55,CE,C5,B7,B2,84), + wwH16(F8,4D,DF,F4,EB,A3,92,D2,D5,C2,02,CD,BE,1F,3D,02), + wwH16(D0,DD,C5,76,F8,83,EE,A6,56,17,58,D9,09,10,B2,E0), + wwH16(BC,B5,90,12,3E,D7,13,B4,58,EF,8A,67,9C,6A,A1,73), + wwH16(D7,45,E2,1E,67,F4,27,14,CE,0A,3F,26,E6,37,EF,71), + wwH16(0E,80,A6,12,CC,DA,3A,DB,E6,F3,81,0C,51,16,9C,AE), + wwH16(AC,C7,C4,BD,CF,1D,55,68,6A,F0,E7,D9,26,3F,3A,E3), + wwH16(AC,07,F3,F8,FA,5D,B0,95,56,A8,03,7A,07,D2,B8,39), + wwH16(B3,D8,C2,D9,94,CD,1F,B7,F3,0F,2A,E7,59,4B,8A,E7), + wwH16(7A,37,05,1D,E6,84,93,36,2C,83,B9,BD,9A,B6,51,52), + wwH16(C7,AD,72,3B,31,6D,3B,5C,99,95,58,16,13,BE,3C,21), + wwH16(44,C1,6E,9C,55,35,7A,B6,F7,11,A0,82,69,BC,5F,63), + wwH16(77,FD,57,2E,F3,85,B9,E0,9B,04,8A,A5,6D,39,9D,97), + wwH16(A4,6E,17,20,22,05,7B,E9,2B,1E,AD,B0,8D,BE,65,C8), + wwH16(B8,5C,30,39,41,CF,F5,55,49,49,CD,0E,C9,28,23,A3), + wwH16(11,D2,41,A3,D8,DD,31,AA,71,4E,C9,49,01,1E,46,A2), + wwH16(B3,26,66,FA,CA,86,7B,6C,00,37,2C,C5,77,A1,5B,ED), + wwH16(80,72,C3,EE,BC,D9,75,5E,6B,34,82,23,3B,FD,6E,7A), + wwH16(DC,8D,6B,EB,79,38,DE,A5,16,77,83,15,D4,21,11,33), + wwH16(D5,FC,BC,C7,54,DA,72,72,8A,0C,FA,C7,E5,A1,F2,A2), + wwH16(7F,EB,73,20,6D,D2,DB,0B,E5,F1,73,3A,B0,D6,A6,97), + wwH16(25,AE,3C,51,C1,61,53,7F,DD,46,F4,DA,1A,5A,B6,57), + wwH16(03,D9,C4,CA,AF,47,85,69,12,95,EC,CA,B0,D2,5B,36), + wwH16(AF,26,3D,29,0C,BF,2F,27,95,8E,5E,5A,58,76,7E,CB), + wwH16(6D,0E,F9,CC,C1,38,FD,3A,45,AC,73,1D,8E,1F,EA,9F), + wwH16(0F,AF,90,38,58,1D,A4,54,3B,4D,60,D1,FA,36,1E,3D), + wwH16(F1,44,C2,33,12,F9,E8,A7,C6,3C,34,4F,37,DA,0E,CB), + wwH16(13,75,0A,38,76,CC,82,3B,BD,04,F3,22,B3,23,C9,FB), + wwH16(E5,64,2F,A7,5F,67,3D,F4,05,FB,1C,D9,AC,3A,34,95), + wwH16(0A,A7,4E,B9,67,5A,B4,25,1D,EC,AD,79,64,84,00,39), + wwH16(AA,24,0B,DF,B9,E0,A4,9E,4A,ED,13,64,38,A3,A0,59), + wwH16(30,FB,6C,2D,BE,EA,4F,62,D8,55,22,7D,96,4F,4C,95), + wwH16(20,BD,4B,83,74,81,1F,20,E2,4E,66,F0,25,26,A8,6E), + wwH16(F0,F3,34,B5,9C,CC,9D,07,5F,68,7D,C8,8B,9B,1E,8D), + wwH16(23,35,BC,44,72,3F,EC,3B,A1,81,AF,29,00,5E,A5,0E), + wwH16(E6,B6,D3,67,AF,9C,45,C0,42,0A,70,38,05,4C,57,78), + wwH16(D7,32,B2,22,19,4C,60,F5,AB,A5,3F,57,A9,08,62,5F), + wwH16(AB,01,E4,D3,BC,34,7A,A1,D5,BE,26,3D,E2,70,23,EF), + wwH16(84,D6,FD,42,6D,24,3A,E6,0C,0F,C1,FC,2E,06,E3,27), + wwH16(14,33,12,D2,19,43,35,0D,D5,1B,34,5C,E8,64,E0,0B), + wwH16(3F,B5,5B,96,16,F2,34,17,ED,DF,94,F6,BC,93,A2,A2), + wwH16(9E,83,42,F9,73,8B,7E,E4,26,B9,80,3C,AB,55,CA,1B), + wwH16(0C,9C,07,C0,3B,CD,47,D4,0E,9B,A8,A6,95,0D,69,91), + wwH16(1C,35,E2,79,D8,61,7A,24,37,1B,5F,CC,A4,DA,CB,02), + wwH16(06,2B,59,79,70,77,25,57,6A,E5,6B,22,B3,43,86,9B), + wwH16(3B,FB,6D,E9,04,59,02,08,3D,80,DC,A7,AD,9C,F5,A2), + wwH16(B1,62,ED,40,C7,F2,7E,C2,95,58,E9,B7,C5,BD,AF,E6), + wwH16(2B,03,03,51,E7,05,2C,84,BE,94,3D,F1,8B,10,6E,84), + wwH16(6A,0E,BF,84,1A,08,91,AD,A2,65,55,5E,E9,80,FC,62), + wwH16(07,A7,A9,EF,19,BD,85,27,65,0A,46,42,C8,BD,91,34), + wwH16(54,8D,98,AC,04,CA,74,98,44,1D,5C,92,B0,83,63,2A), + wwH16(87,40,62,71,8D,96,51,61,CE,8D,F2,4F,36,FF,DD,3D), + wwH16(81,5E,DE,EA,D4,FB,53,4A,29,F6,8F,9A,AB,CC,F0,A7), + wwH16(CF,24,51,FB,E1,7F,8D,16,25,58,43,03,17,85,58,EC), + wwH16(A8,FD,1A,86,8D,60,15,36,B7,B2,63,20,65,95,24,51), + wwH16(D8,64,37,B7,44,DF,40,75,E1,F2,C2,78,CC,AF,73,F8), + wwH16(FD,65,1F,56,A9,E2,E8,F5,59,50,D6,E7,96,18,70,C7), + wwH16(0D,23,C9,87,80,22,F7,FF,8D,BF,74,64,FB,D5,48,43), + wwH16(DA,A2,7F,78,88,A9,F3,A3,76,2B,17,6B,8E,E6,FF,87), + wwH16(BE,A5,AA,AA,85,61,3D,D7,E1,3F,95,F7,B9,2E,B9,17), + wwH16(5F,45,A2,39,6A,17,79,E4,A7,A6,C2,3F,C5,39,48,4B), + wwH16(4B,DC,D8,D6,F7,FF,12,5F,06,E5,6E,08,75,D4,2A,F3), + wwH16(AA,73,B9,14,C4,5F,10,BB,49,15,95,50,F4,E2,32,4C), + wwH16(7D,91,F7,67,80,FB,89,59,5A,12,A5,2C,38,17,D2,FB), + wwH16(95,5E,0F,72,04,42,7E,2A,80,BE,A7,E1,B3,6B,63,6A), + wwH16(1A,2C,07,8E,2F,38,96,25,2C,E0,57,54,07,2E,E2,17), + wwH16(0A,1C,48,92,EF,BF,06,E6,92,6A,0F,D3,EC,C5,31,18), + wwH16(E1,2F,A0,7C,5B,77,04,33,C7,8F,59,2E,2B,DA,9B,28), + wwH16(93,15,CD,FA,48,BB,D7,51,39,A4,EC,E4,F1,15,6B,07), + wwH16(25,6E,2C,A9,45,A9,27,CD,BF,57,0F,4B,3C,4F,5C,90), + wwH16(19,E9,01,62,2E,18,F9,84,55,BB,99,5F,B4,D1,84,B3), + wwH16(F6,53,41,7B,EC,1C,64,C3,2F,6F,95,0B,2D,B5,0B,EB), + wwH16(57,D5,27,63,F1,66,3B,5F,41,C5,2C,AD,EC,4D,E7,F9), + wwH16(D1,86,85,57,74,AB,E3,B1,1A,38,61,43,B7,48,BF,62), + wwH16(C7,21,04,79,E3,54,C5,95,BD,52,A9,6F,D0,0A,51,E1), + wwH16(28,2B,7F,20,E3,F1,7F,87,17,41,5E,E6,2F,62,AD,40), + wwH16(94,2B,94,FE,84,33,42,11,C5,F0,DB,3C,F6,61,0A,EE), + wwH16(FA,4A,11,02,04,D7,2A,74,30,C2,75,43,09,4C,DA,F9), + wwH16(7F,F1,32,19,4B,34,C8,E9,C7,BA,62,63,86,03,0F,20), + wwH16(FC,DA,D0,04,92,71,0F,3F,60,45,6D,F2,F6,CC,CE,80), + wwH16(D6,DB,EF,32,5D,D8,AF,73,45,46,9B,41,44,77,3A,F2), + wwH16(7B,E6,94,B9,CD,C1,FF,01,A6,81,E1,1C,91,94,D4,69), + wwH16(4F,9E,0A,DA,66,D7,19,9F,C3,8F,0B,17,BA,31,BC,A3), + wwH16(5B,6B,80,0B,00,BA,2F,13,5D,CF,F5,6B,92,F5,DD,A4), + wwH16(40,C6,B9,60,AE,5D,B0,FE,DE,66,FC,8B,10,D5,E1,E1), + wwH16(E8,50,EB,79,A0,A8,FF,1F,EB,EA,71,18,BB,EA,BF,4F), + wwH16(77,8E,D5,D6,7F,37,75,2A,BC,27,F8,54,FF,78,B8,D6), + wwH16(43,3A,A6,0A,92,D4,E7,32,88,D1,E1,AC,6C,74,A2,33), + wwH16(3C,BF,B4,22,BC,2B,92,AB,41,AA,9B,BE,67,EB,84,EA), + wwH16(41,3A,D4,3A,FB,74,B6,7F,74,35,D8,A0,B8,05,67,FC), + wwH16(21,70,9E,7E,D9,7D,07,6E,54,C4,0A,30,48,AD,70,F8), + wwH16(91,A5,5E,A5,68,60,A8,96,A2,E1,9B,F9,CA,82,5B,99), + wwH16(41,3A,BA,45,9D,95,ED,6E,9B,29,3C,FF,DB,E2,09,BC), + wwH16(AD,2F,07,52,59,8C,C2,51,2D,5F,66,FD,42,3A,D0,3F), + wwH16(6A,48,E0,8D,71,FE,46,30,3A,56,A5,9A,A5,3A,2C,6C), + wwH16(9C,FC,3F,CF,EF,B5,FC,3E,3E,EA,BF,2D,F3,96,D3,12), + wwH16(02,0B,6B,9B,7E,16,FF,D2,63,35,9D,52,E9,3C,2C,2C), + wwH16(A4,3C,6D,B2,51,89,25,FF,D1,6C,19,9E,B5,8C,5A,39), + wwH16(68,BE,1A,F0,1A,B3,AA,95,DD,8A,BF,1F,96,AD,01,A4), + wwH16(62,04,B5,01,8D,8A,96,ED,41,21,F1,D8,EC,1D,6E,EF), + wwH16(C3,8B,00,41,91,DC,BF,C3,7C,61,30,2F,A2,BC,CF,E0), + wwH16(7A,FB,0E,11,02,BC,5D,2E,80,45,C7,53,16,C2,CD,B8), + wwH16(D2,A5,4C,4B,B3,6D,4B,A1,FC,47,AF,F3,41,10,6B,F6), + wwH16(F3,39,B8,36,69,02,F7,D5,5D,9F,E8,97,91,D2,B1,61), + wwH16(3D,A9,1E,F3,0A,B4,CA,E2,71,3B,67,71,D2,8B,97,52), + wwH16(F3,09,F2,15,88,A4,C6,9D,BF,F4,82,55,E1,3E,33,9D), + wwH16(82,C9,61,B6,B4,4B,2C,D1,92,AA,2A,D3,0B,32,7F,E4), + wwH16(E3,8D,25,EE,A7,3A,EF,9D,A5,8D,EF,5E,13,80,CC,46), + wwH16(D3,81,6D,55,D8,A4,12,9A,54,75,FA,01,5F,22,12,38), + wwH16(D4,C6,8E,70,5D,F4,C4,55,42,0C,B8,C3,C0,8B,1C,5C), + wwH16(3F,67,6D,BD,58,5E,1C,25,AB,C9,A7,5D,3F,86,22,C5), + wwH16(46,8D,57,A5,54,C3,42,AA,3C,32,21,C9,3F,DB,5E,04), + wwH16(DF,56,1F,36,DB,02,60,61,5A,94,22,50,E8,8B,51,E1), + wwH16(F8,C0,F7,B6,71,07,3D,6D,7B,DD,8D,15,FD,2A,5B,34), + wwH16(C5,39,B2,2C,FF,53,C0,16,05,C0,94,36,E3,69,07,4A), + wwH16(2D,E5,0C,1A,DB,FA,4D,EF,C3,AE,EA,98,5F,52,10,58), + wwH16(A6,37,2A,2E,77,74,FB,9C,53,74,58,86,BE,D2,63,68), + wwH16(BD,D6,01,D8,52,8A,A7,98,B0,BE,DC,8D,D6,9F,1C,C7), + wwH16(22,6B,08,27,A6,8E,E7,24,10,67,F1,A3,E2,29,40,65), + wwH16(9A,57,14,20,65,F4,AC,93,91,2C,8D,E0,1F,D5,AE,7D), + wwH16(31,03,C5,3C,2D,0C,0C,A7,0E,E1,21,C6,42,4C,E9,10), + wwH16(50,B2,6F,7A,66,96,26,C7,53,DF,CF,3C,65,B9,CD,B8), + wwH16(CC,FA,36,2A,D2,36,79,5C,73,F2,8C,5D,2B,36,25,F3), + wwH16(34,29,60,3E,56,A5,08,1D,C6,28,47,6C,07,5D,3D,96), + wwH16(35,6E,32,C7,5D,3D,0D,14,5C,BC,66,AC,7D,BB,5F,CA), + wwH16(F3,33,F5,F7,04,95,27,DB,4C,9C,A1,F4,49,C0,12,36), + wwH16(55,22,92,44,96,E1,D3,EF,6E,24,B3,08,EA,72,10,5E), + wwH16(C2,E2,81,E8,D5,7C,09,9B,BE,AD,48,3A,17,7E,4A,F8), + wwH16(56,D6,FE,59,F5,F9,51,AB,B8,5F,88,D4,D7,D4,24,D1), + wwH16(CE,69,72,D5,F4,6F,EC,3E,3D,A2,EF,DD,15,5B,9E,D1), + wwH16(CD,1E,68,B9,61,AF,AA,19,F6,FB,F0,22,E6,DB,FB,E0), + wwH16(D8,7A,1D,91,15,00,D7,4F,3C,00,3A,49,73,84,BC,10), + wwH16(DD,2C,4D,5B,24,21,4F,E7,5A,73,95,A8,B0,FF,8A,F0), + wwH16(0E,3D,38,88,BE,71,E3,2C,32,A1,80,05,0E,F2,E0,01), + wwH16(D3,EF,FC,B4,67,FD,3F,CF,BD,3F,52,46,B1,15,30,01), + wwH16(C2,89,79,8D,E5,E3,C1,15,DC,D3,32,E0,4D,79,CE,F4), + wwH16(57,2E,90,E1,92,D5,B7,96,45,C1,5A,8C,09,EB,0A,1E), + wwH16(F9,8C,11,08,22,26,C9,39,E4,FB,2D,99,A7,87,D9,10), + wwH16(03,F8,53,CB,FE,65,D0,66,73,95,56,D5,C2,BF,1C,45), + wwH16(1B,A4,88,25,E6,87,56,7C,39,E1,EC,A3,38,53,46,39), + wwH16(DA,03,4A,FF,71,27,09,73,9B,C5,D4,39,6B,8F,9B,20), + wwH16(F5,5C,B9,03,75,5D,5D,A9,D8,C0,86,FC,92,5E,B3,9E), + wwH16(6C,D5,B9,A3,0A,D4,B8,40,A0,07,EA,9A,BB,39,6C,CE), + wwH16(A8,81,DD,20,5B,84,9B,FB,F8,72,64,33,A9,43,E2,78), + wwH16(B9,3E,B1,3D,58,7B,83,5C,F5,8D,9A,81,19,55,59,79), + wwH16(88,4C,C9,F8,B2,AB,45,9A,7C,FB,DA,61,47,D0,34,58), + wwH16(4B,E4,0D,91,14,01,DC,AE,BC,3A,E5,0C,2C,23,2C,3F), + wwH16(7E,66,A1,2D,A2,A4,56,5D,56,39,E8,A8,DC,2E,EB,36), + wwH16(04,75,C3,90,A3,78,6B,91,20,36,21,21,C5,0A,98,C7), + wwH16(EF,E0,B0,58,FC,4C,B4,A4,A6,E2,A7,FE,C0,91,03,5B), + wwH16(E9,18,1D,33,0D,60,2A,EE,59,65,AE,A0,E5,B9,30,6E), + wwH16(23,CE,4B,59,B6,22,E1,EA,7C,95,3E,DE,8A,A1,64,BE), + wwH16(E2,B8,D7,4E,CA,41,5E,A3,C3,C0,4A,7C,90,EE,85,BD), + wwH16(00,FD,93,B7,55,AA,28,BD,3A,0C,CC,1A,68,12,A2,43), + wwH16(95,1D,F8,74,AB,CA,3E,43,5F,BB,E4,53,74,D3,CB,04), + wwH16(EF,14,65,88,7C,04,32,1C,85,C9,8E,BF,BF,B2,6E,F4), + wwH16(23,05,DA,97,9D,E2,79,A6,21,95,ED,7C,E7,D2,A6,04), + wwH16(25,DB,33,EB,30,64,A1,9E,3D,16,37,70,45,23,5F,FA), + wwH16(49,59,CA,0D,6F,A4,E4,2D,D7,8C,CA,C7,D4,3B,84,59), + wwH16(3F,C3,54,32,78,A3,15,D0,59,C0,50,DD,5D,2A,3F,A7), + wwH16(44,63,4F,82,B4,31,78,AD,9C,C9,FB,34,A4,CE,D9,0E), + wwH16(1B,F4,F0,55,E1,87,A1,AF,53,38,DF,82,F9,A8,62,69), + wwH16(6C,7A,89,C7,A4,1B,65,59,E9,AF,67,DB,17,CC,1B,61), + wwH16(69,CE,F5,F3,75,7E,15,D7,4D,3C,DF,2E,3B,0B,D7,B0), + wwH16(F3,3E,49,1F,66,DE,BC,9B,22,8D,06,EB,F8,1C,0C,26), + wwH16(C7,A2,7B,4A,4A,26,29,40,DB,CB,98,7D,6D,5A,66,A5), + wwH16(81,F3,D2,86,B4,72,22,F9,A8,7F,AC,5A,B5,A8,9D,77), + wwH16(A7,D0,36,21,4A,82,F3,A6,FF,15,90,5F,A6,31,2B,98), + wwH16(10,65,42,75,EA,B0,27,7C,7F,75,5B,6A,28,8F,89,EA), + wwH16(63,0A,24,2B,A4,DF,92,55,3C,5B,74,B9,EB,7F,B7,A4), + wwH16(4C,70,69,C3,7B,14,2B,6D,DB,4A,A0,5D,62,DB,9B,AA), + wwH16(69,8D,A9,06,4D,EC,3E,A7,81,47,8C,DA,ED,BC,AF,29), + wwH16(25,F4,C6,72,67,E1,B5,F9,EB,B9,31,5C,DF,13,59,B6), + wwH16(6D,A1,6B,98,93,67,A7,60,38,B8,6D,57,2C,C0,40,07), + wwH16(B0,1F,BC,90,D7,4F,7F,B2,5E,30,16,DA,3B,46,D1,43), + wwH16(C6,D1,5A,74,EA,14,0F,D7,D1,53,29,D0,FD,34,B9,99), + wwH16(39,AC,63,AA,B8,1A,12,B0,7E,5A,73,A6,05,55,12,26), + wwH16(12,EE,8A,21,F0,11,38,8A,66,30,72,70,F6,47,C4,F7), + wwH16(F9,A6,A5,CF,EF,44,E0,4D,88,DD,91,78,8E,54,A8,C8), + wwH16(DE,BE,A8,85,C4,AA,CE,F0,71,E2,A1,97,C4,66,28,9B), + wwH16(CC,B3,87,3E,22,90,A1,D8,D7,51,43,AA,EA,35,3F,0F), + wwH16(49,ED,1A,48,18,4F,19,29,51,8C,3A,E5,87,26,B1,10), + wwH16(2C,DF,C6,C5,08,F2,5C,0D,F7,E4,8F,A9,DD,C7,7B,BD), + wwH16(75,81,2C,94,B6,24,17,D2,34,12,C6,90,04,A3,F3,72), + wwH16(58,BC,3D,4F,B1,C0,38,73,F3,40,C4,17,C1,7B,E0,E7), + wwH16(7D,CA,93,06,1F,89,32,CF,7E,D1,03,35,19,D6,14,99), + wwH16(EC,8B,3F,6E,67,65,5C,27,F0,A2,DB,E5,B7,44,54,5D), + wwH16(7B,3B,31,0C,9B,77,2E,30,0A,E4,9C,FF,E5,48,58,86), + wwH16(FD,C6,D2,85,6F,F4,A9,4A,80,F1,51,DA,4C,35,3C,6D), + wwH16(59,46,5D,E5,CC,FE,76,24,F0,1B,42,DF,39,47,D9,BB), + wwH16(7D,9E,FA,69,6D,BC,C9,12,2A,1A,1E,31,41,CE,C1,83), + wwH16(32,EA,CB,59,AF,F7,DF,9B,58,AC,78,6E,21,E3,C6,08), + wwH16(1B,C6,54,2A,27,1E,F7,B8,77,84,F6,B8,EC,87,07,D5), + wwH16(13,C2,23,72,87,33,6D,74,FA,02,F1,9B,00,31,0F,CB), + wwH16(B2,F7,F1,CE,B6,DE,A9,D9,3E,86,4E,32,A0,0D,BD,78), + wwH16(B6,F5,B9,A6,98,57,9C,D8,22,FE,FA,8B,CC,F3,B8,62), + wwH16(6F,EC,C9,8C,A0,75,42,B0,B3,BC,48,04,A7,61,B7,A3), + wwH16(F1,36,0C,CA,47,8B,60,21,E4,1C,87,AD,20,5A,38,47), + wwH16(28,16,6D,38,40,8F,85,7A,C4,A0,5E,42,37,F9,8D,A5), + wwH16(7A,4B,39,41,DC,19,57,05,1C,FE,E6,22,C5,E1,01,1B), + wwH16(72,7F,3B,EC,26,1B,B9,18,0E,2A,41,90,B8,1A,0D,5D), + wwH16(46,F7,87,E4,23,51,3A,3D,E6,FB,A3,7D,47,D0,7E,F8), + wwH16(51,D3,76,A7,FB,1F,1F,D5,D5,45,9B,41,57,72,16,B0), + wwH16(4F,D5,D3,76,97,86,2C,7A,2A,E4,8F,AB,FC,BB,9F,97), + wwH16(74,EF,FA,7E,06,E1,84,DC,5E,00,B9,B4,BE,31,6B,61), + wwH16(19,62,46,AE,BB,FA,41,AD,76,23,2B,B8,2F,0F,5E,32), + wwH16(16,53,CF,D4,05,86,A7,BD,B8,20,7C,A4,80,C0,E5,6D), + wwH16(9F,99,38,49,4C,2D,C5,FD,D0,CE,2A,4E,56,89,40,38), + wwH16(A1,83,1F,F0,A0,7E,13,A2,D9,30,34,73,D4,48,CB,FF), + wwH16(3D,24,43,64,30,48,D0,F6,D5,34,26,A6,3F,00,47,33), + wwH16(F4,ED,43,DE,D4,18,E1,69,FE,98,FD,80,21,2B,23,8E), + wwH16(9E,AB,BA,3C,3E,BD,A8,C2,42,4A,FC,6E,84,D8,5C,D2), + wwH16(E2,95,DC,B7,73,75,02,B6,2E,63,F2,19,D6,66,7F,A7), + wwH16(F8,29,54,73,58,63,DC,6C,F3,B4,F8,D0,0E,9D,9A,B9), + wwH16(86,00,CB,61,91,99,9D,AF,6F,4F,18,01,B0,7D,E1,00), + wwH16(9C,5A,32,47,2B,0A,94,43,83,3C,83,D8,84,61,42,5A), + wwH16(7F,86,37,F4,F3,97,02,CF,C0,C4,FC,64,CB,C7,F6,91), + wwH16(3B,6E,73,3E,B0,31,E8,8F,F2,56,BC,CD,E0,8D,A5,1B), + wwH16(11,ED,9B,A5,72,49,AD,1B,48,AB,2D,A1,F2,E8,FE,E5), + wwH16(0E,C8,BA,50,84,A2,0D,2E,20,D2,72,B2,4E,18,D8,B3), + wwH16(DE,DD,FC,31,03,1F,AB,10,30,8A,9C,34,54,47,DF,7C), + wwH16(27,79,3B,7D,58,CC,69,A5,76,F3,EF,B5,7A,F2,37,CA), + wwH16(A2,C2,B9,EE,2C,69,0B,79,EB,D4,EB,A7,86,0B,F7,7D), + wwH16(FE,C8,62,C0,B5,34,BD,EB,79,91,83,D4,9D,BF,5A,44), + wwH16(10,28,68,39,09,3B,84,BF,7C,4C,C6,F8,51,43,46,57), + wwH16(1A,AE,30,25,3A,95,BB,F9,9F,D6,D7,8D,5C,56,2A,97), + wwH16(47,8A,92,29,CF,30,30,E1,9C,FA,47,7F,B7,A4,B3,07), + wwH16(52,5D,32,AE,F8,54,7B,9A,80,05,E4,47,FC,9E,21,1C), + wwH16(F8,C2,4E,0F,F6,9E,DD,89,6B,40,88,B6,50,E9,81,48), + wwH16(54,1B,E2,19,A6,57,52,2D,61,33,DF,CB,68,D5,65,B1), + wwH16(35,39,D8,51,DC,69,41,85,5C,24,94,76,51,74,FB,EE), + wwH16(9C,23,E1,78,1B,0B,7B,17,92,D4,12,9F,E7,07,05,2D), + wwH16(6D,47,D4,04,8D,43,8B,E8,E6,42,BB,54,31,2E,DC,89), + wwH16(7A,62,86,D9,87,65,D4,8D,12,A1,B7,06,AC,8E,44,B2), + wwH16(CB,23,0E,89,08,55,93,1C,3C,3B,F3,F0,D1,22,D2,80), + wwH16(C1,9E,99,2C,74,D9,DF,AC,D2,49,D8,FF,48,7E,0E,77), + wwH16(7E,02,63,F6,58,2F,88,6D,A0,B7,8F,82,AF,96,4F,B2), + wwH16(B3,6E,2D,A6,90,A6,95,C4,45,77,46,BF,09,15,60,F9), + wwH16(E3,59,85,00,D0,3C,EC,B6,A9,A2,54,A9,88,2A,1C,ED), + wwH16(87,32,86,0F,CC,9A,04,52,B5,E2,AB,28,C5,34,BB,70), + wwH16(AE,C2,32,35,3F,89,D1,76,AA,65,1D,99,C8,08,2C,50), + wwH16(FE,4F,FC,9A,2C,81,CF,E1,C7,9F,9A,4A,40,84,1C,95), + wwH16(39,BC,D4,FF,3D,A9,49,F6,3D,BD,A9,8F,C6,41,9E,F8), + wwH16(FC,83,DF,AA,CF,8C,81,A1,90,4E,04,92,AB,50,6E,C6), + wwH16(B5,A1,E5,96,67,F7,BD,22,2D,70,F6,66,36,B9,3F,40), + wwH16(30,25,F1,CC,0A,E5,1C,17,F4,11,8B,DC,7D,C2,02,01), + wwH16(0E,CF,E7,06,2A,16,63,B6,AF,E8,B9,9D,0F,01,2B,D8), + wwH16(4C,68,45,EA,B2,4C,AF,89,07,74,D9,72,E9,D0,05,E2), + wwH16(6D,2D,CA,D4,91,EC,31,F8,ED,74,C6,DF,EC,A1,2F,25), + wwH16(52,B6,A3,5C,F6,AF,64,72,07,CF,BB,55,9C,7F,66,58), + wwH16(04,6F,19,C6,13,4E,6C,34,6C,7F,7F,58,50,49,30,AD), + wwH16(14,24,5D,9A,21,8C,53,26,BE,EB,33,C0,98,1A,7A,D9), + wwH16(D6,36,67,1A,42,E7,62,9C,D4,02,B1,AB,17,C5,AF,DA), + wwH16(DC,6A,C5,70,05,4D,D6,20,26,92,C6,4F,67,60,CA,08), + wwH16(AB,4E,1A,B4,EB,EE,0C,86,2C,25,38,32,67,16,98,CA), + wwH16(9F,9F,45,BC,EA,F8,86,2A,CB,4A,49,7E,48,05,17,D6), + wwH16(D0,0E,6B,F8,22,C5,EA,86,F4,7F,2F,95,F0,AB,D0,C9), + wwH16(57,F0,6F,75,B0,8D,45,68,90,8F,5A,31,EC,24,D2,1B), + wwH16(F1,64,E8,C0,5B,38,3A,FF,4C,8C,67,55,D3,AF,1F,DA), + wwH16(5A,97,40,E8,22,82,4D,75,22,D2,25,0B,D0,39,49,F9), + wwH16(9C,4E,8F,C9,48,88,34,BC,42,21,71,78,08,9B,88,36), + wwH16(C3,FF,3B,CE,FE,DC,EE,C6,F3,7E,1C,E3,8E,00,6E,E7), + wwH16(2E,F2,11,C4,3C,17,61,B2,47,A5,35,B3,70,8E,17,B3), + wwH16(3F,52,1D,84,91,2F,87,BD,36,15,3E,CE,44,BD,DD,94), + wwH16(E2,C8,D2,6A,CE,2A,20,50,2C,18,20,E6,E9,D1,78,C4), + wwH16(18,9F,96,AC,E5,C4,AF,F6,42,46,3E,06,8F,01,1F,DD), + wwH16(69,FE,96,13,B1,E4,17,DE,51,E4,E1,EE,C8,EA,2A,56), + wwH16(8D,A7,ED,65,F4,11,76,F2,50,01,98,BF,52,46,46,5E), + wwH16(28,70,2E,16,DD,11,28,99,7E,C6,0D,D7,46,B8,72,4D), + wwH16(9D,D7,A8,05,93,40,59,FA,CE,AF,DC,18,95,59,4C,BA), + wwH16(A3,C9,A6,52,61,1D,35,41,C0,8E,40,9E,70,32,DB,BC), + wwH16(19,DE,81,36,12,58,DB,36,96,41,F6,9F,DE,05,5E,92), + wwH16(F4,7A,73,0F,44,ED,5A,E3,F3,24,F4,F4,77,E8,3F,13), + wwH16(54,EE,53,7E,85,2D,CA,02,F3,C3,30,6A,07,E2,98,ED), + wwH16(5E,BD,61,D0,E1,6E,E7,2C,15,1B,56,DB,4F,32,2F,B4), + wwH16(99,55,B5,35,E2,4B,63,CA,75,B8,28,1B,16,0E,00,D1), + wwH16(3A,C4,86,BC,DD,A9,DC,E8,55,3E,9C,AA,01,A8,C5,95), + wwH16(D6,05,15,84,9D,E7,F0,91,75,4D,82,46,2D,A6,72,30), + wwH16(20,39,86,F7,24,D2,94,EE,35,63,CF,F4,57,E0,4F,C6), + wwH16(A0,32,6A,50,59,A8,07,CE,24,16,A8,BF,65,7B,DA,D0), + wwH16(C8,FF,BE,AD,D6,14,F4,5A,5C,6A,57,0E,A5,15,33,92), + wwH16(34,AF,CA,5E,9E,B5,53,0D,6B,8A,9A,9F,4B,C2,43,EA), + wwH16(CB,26,02,A7,FE,9E,E2,EF,0A,53,E1,36,AF,37,99,BA), + wwH16(27,97,7C,14,53,CD,04,07,2D,CD,B6,03,A5,73,C0,60), + wwH16(4F,3E,DE,E1,CF,10,3D,92,F0,03,89,E6,AB,8A,D6,79), + wwH16(A8,02,58,8C,B8,A8,BE,F9,EF,9F,CA,CC,F4,63,13,FB), + wwH16(9B,18,76,12,F7,B9,DC,1E,61,7E,5C,B8,8D,C3,D0,FC), + wwH16(FE,52,C5,9A,14,97,28,AE,F5,70,0C,DE,04,0D,23,6D), + wwH16(21,D1,21,CE,86,42,B6,97,49,A0,02,58,E1,0F,D6,5D), + wwH16(70,61,0B,A1,FA,6B,D9,99,BB,B1,A0,63,A8,1F,A5,1B), + wwH16(E9,FA,58,A7,A5,C4,60,03,27,F3,D6,A5,FE,50,A6,EF), + wwH16(9C,A6,9D,1A,A2,96,E8,F1,AD,37,05,04,23,CC,DF,AC), + wwH16(3B,85,40,98,99,5C,4C,E9,78,29,AF,5B,A4,7F,B8,BB), + wwH16(A9,35,6C,FD,42,46,B7,ED,1C,51,87,42,D3,B7,BB,28), + wwH16(0C,0F,8A,FD,8D,F2,D3,10,C1,11,B8,7A,47,F5,45,9C), + wwH16(8C,0E,05,42,61,63,D9,99,01,2A,6C,8E,84,1F,AE,59), + wwH16(86,F6,DA,0D,1B,D3,2A,5F,DF,AC,67,20,EB,A7,8A,05), + wwH16(FA,F3,CE,EC,52,32,8C,AA,FB,0D,91,2A,A8,F1,C9,BA), + wwH16(4F,EE,AB,C0,7C,1F,C8,AD,90,F0,A4,FE,20,3F,55,AC), + wwH16(DE,CD,22,4E,E5,C2,C3,D3,32,34,61,C4,0B,1A,CB,90), + wwH16(56,71,62,54,09,D0,59,70,80,D9,EE,43,CB,92,5C,C5), + wwH16(4E,16,FB,80,C3,87,13,C6,16,F8,60,CA,8B,A5,EF,D8), + wwH16(56,AC,FE,55,B6,64,FB,A6,A9,4C,8E,35,A0,19,93,6D), + wwH16(17,38,81,14,C4,65,7C,3E,C8,84,EA,2C,19,5C,A2,75), + wwH16(11,1D,8A,D4,A2,8C,83,2C,99,25,66,EE,23,68,76,7B), + wwH16(EB,FC,B2,CC,DF,46,9F,67,5F,A8,96,6F,15,9C,7A,B0), + wwH16(8D,C8,AA,9A,8C,8A,F2,90,17,50,67,7B,7E,50,2C,21), + wwH16(05,58,78,8C,84,5C,81,DA,11,D6,9D,71,22,22,34,9D), + wwH16(ED,66,8A,17,92,83,1B,F9,2D,54,B3,A9,25,A5,22,90), + wwH16(B4,10,F1,BC,29,C4,0A,A5,4F,27,99,BA,26,D4,BE,2C), + wwH16(EA,81,2B,85,00,CF,47,28,98,33,A3,68,AE,3B,77,B5), + wwH16(7D,05,C5,3C,87,BA,0B,C8,0D,2D,7E,BF,EA,25,E6,96), + wwH16(7A,A0,E6,86,EA,C4,61,DB,AA,72,DA,AE,60,67,C5,94), + wwH16(F7,06,AC,00,40,85,15,DE,05,27,A9,65,4E,AE,F6,CF), + wwH16(95,5F,39,DC,91,2C,CA,7D,8C,28,34,43,CB,7A,EA,D7), + wwH16(BD,27,B8,81,56,67,8C,04,F0,1F,BF,45,AA,A1,FF,18), + wwH16(33,04,CD,5D,72,AF,BE,94,6F,31,E6,3D,DC,B3,7A,BA), + wwH16(81,6D,99,D2,F5,52,5E,DE,42,70,F2,3F,51,AC,B4,91), + wwH16(4C,45,A9,25,F7,09,40,03,43,35,5B,1D,D2,65,99,E7), + wwH16(F9,9F,FC,C2,48,8B,2C,66,97,B6,1F,5B,FD,78,6B,F0), + wwH16(2D,CA,2B,CB,1A,2B,45,0A,D0,0F,27,D3,7E,67,71,17), + wwH16(36,4F,14,74,89,CF,96,48,56,28,09,6D,15,58,1C,6F), + wwH16(87,16,B3,50,2B,B5,BD,4E,99,81,24,CE,8B,B7,71,76), + wwH16(F4,A5,E3,98,F8,F8,4E,79,D6,1C,C2,BB,FD,8B,42,7A), + wwH16(9B,26,79,2E,66,66,F1,E3,79,0A,AC,ED,C6,DC,7F,26), + wwH16(F7,8F,EE,48,B5,EC,6E,0B,7C,16,9A,75,D1,B3,F3,7D), + wwH16(05,B4,23,68,32,DE,0A,2D,57,94,9E,53,7A,27,79,C5), + wwH16(DD,76,28,CB,F4,C0,8C,60,42,D3,86,D0,9C,9D,07,ED), + wwH16(B9,78,6A,58,01,A9,9E,C4,07,DD,90,40,33,B5,2D,D7), + wwH16(2F,FB,FF,90,74,F6,A4,66,8E,B7,A3,C8,93,DF,D2,64), + wwH16(ED,81,07,19,27,EF,E7,FE,B2,03,AE,8A,F3,94,F5,26), + wwH16(AD,55,94,E1,F2,73,88,A6,1B,D2,9D,DD,2C,F1,D8,DE), + wwH16(C4,80,42,1D,AF,B3,D2,AB,7B,74,E2,A2,B4,67,1B,87), + wwH16(0E,E6,9B,2C,17,F6,44,EA,63,95,C1,D4,C8,89,0B,32), + wwH16(36,67,47,6A,3A,D5,F5,D6,BF,96,B0,F7,4C,15,75,57), + wwH16(8A,D0,01,93,1E,14,45,C1,AD,BD,A7,3C,62,1D,93,20), + wwH16(26,A7,FB,1E,24,41,00,A2,BC,9F,16,F6,08,52,88,7B), + wwH16(F6,1F,99,05,FA,50,43,B2,89,5D,05,74,B8,64,00,0C), + wwH16(70,6B,BE,36,2D,4D,6D,A0,71,12,D3,2A,C0,15,7F,99), + wwH16(45,3E,1E,E7,B6,0E,A9,DF,6D,72,78,6F,DC,53,4A,81), + wwH16(1C,77,FD,89,EC,EC,3E,EA,30,A0,6D,65,4B,2F,AF,E0), + wwH16(16,BC,0A,BC,48,F5,E6,33,50,91,A6,D6,11,68,18,2E), + wwH16(A8,D1,C5,36,90,B1,DC,9B,5A,D6,5F,E1,0B,A9,0B,6A), + wwH16(9D,E2,34,25,3F,F6,D6,9E,FA,26,E7,5D,AF,C3,76,AA), + wwH16(65,A0,54,1B,A0,BA,AB,D9,0A,71,EA,91,61,B0,BC,7B), + wwH16(F5,4D,A2,EE,53,47,C5,1D,5C,DC,50,1C,FD,9A,01,F7), + wwH16(AF,84,0F,D4,A4,AB,30,7A,B8,A0,E6,DF,17,7C,04,20), + wwH16(BE,E7,13,48,9A,3D,61,DC,77,C5,20,EB,FE,DB,BF,CE), + wwH16(19,41,58,A9,69,65,99,19,E2,AC,D7,FC,BD,09,97,C7), + wwH16(67,C0,7B,06,64,A5,A7,80,28,F8,BF,F4,6A,94,29,3D), + wwH16(20,F0,CA,BE,7E,01,58,9F,EA,B0,E1,B8,FC,26,89,4D), + wwH16(24,86,CA,CE,BF,3D,F0,96,50,CC,9B,45,91,19,6C,AA), + wwH16(92,9E,D7,5F,08,BC,C7,43,3E,3F,AF,2E,13,75,B6,59), + wwH16(34,31,B4,B9,D0,64,57,BD,F9,DB,6C,59,1E,65,2B,7D), + wwH16(46,99,F2,6A,39,F4,F4,C4,A2,39,89,37,F6,5A,DE,E6), + wwH16(D0,F0,F5,64,13,3A,36,82,86,81,DE,C3,8C,FC,FB,03), + wwH16(CF,30,78,F7,AF,52,01,C7,BF,9F,C4,72,DD,7F,99,F0), + wwH16(CE,9B,33,0C,E1,32,E5,AD,09,B4,E4,38,20,09,60,A0), + wwH16(FD,BD,6A,1E,CB,AA,12,C4,CB,1D,CE,D1,C0,63,AE,54), + wwH16(96,BF,7E,7D,03,97,41,C4,C1,29,61,AE,B0,9F,BA,1C), + wwH16(48,B0,A7,71,49,5A,34,CF,62,29,74,09,90,29,27,C5), + wwH16(48,32,5C,A7,F0,BE,55,31,3D,16,31,60,67,BA,CB,FC), + wwH16(5E,00,51,67,9B,EC,01,97,81,4A,54,CF,93,7E,A1,64), + wwH16(F1,C3,D0,E5,D4,57,07,AE,8A,36,90,36,E4,A9,B3,3E), + wwH16(BF,36,63,F7,3D,3C,10,13,CF,54,04,DB,AC,C0,95,26), + wwH16(5D,4F,70,D2,4A,23,45,A4,20,0F,D8,EA,A0,44,7B,83), + wwH16(F7,9F,45,EC,33,4D,42,F1,95,33,7B,E0,68,6C,96,2E), + wwH16(04,1A,4D,61,1D,B5,17,25,F6,9A,B2,8A,1A,4B,DA,C5), + wwH16(B4,9E,6A,94,E5,B6,13,FA,03,B8,26,86,67,6F,AC,7C), + wwH16(75,0A,F6,83,AC,4C,34,CA,C8,33,5A,1B,71,04,46,D6), + wwH16(E9,31,90,A3,60,47,8E,AF,8E,8E,BB,50,EE,0A,0C,9A), + wwH16(A0,19,40,96,FC,54,BB,C2,12,41,3D,C3,64,17,ED,C3), + wwH16(0C,DD,82,D7,47,42,04,FF,BF,14,54,4C,8E,2C,B3,57), + wwH16(88,45,78,95,89,A7,93,B9,BD,A8,A9,43,6C,3F,65,48), + wwH16(D8,C1,79,DA,B0,9B,E9,72,49,3B,62,AD,8B,45,D5,FC), + wwH16(C3,E7,0E,5A,B2,85,AA,0C,35,A5,65,7C,BA,0C,B3,78), + wwH16(25,A5,65,F4,1C,E6,C5,9D,E6,04,1D,1D,F9,49,FE,0C), + wwH16(5D,E4,5E,7C,68,AA,68,33,F2,0C,33,15,D4,34,F0,D3), + wwH16(CC,1D,70,53,A3,DE,E1,EF,16,0A,18,CA,63,E1,6A,FA), + wwH16(50,99,EE,F3,57,E5,27,4C,22,0F,AA,C4,EC,3E,54,9F), + wwH16(1A,62,E0,05,63,19,12,5F,B3,C3,B9,DE,72,D5,FE,83), + wwH16(01,76,9B,B0,04,2C,6E,98,64,13,F4,25,F9,39,F7,28), + wwH16(1E,C7,EA,E0,F6,91,F8,13,DA,71,00,4F,88,80,4C,D6), + wwH16(7E,3E,3F,CC,F3,88,BB,DE,E9,91,C8,82,A1,58,7B,D8), + wwH16(76,BB,4E,5D,28,D4,9F,A0,BE,26,7F,0A,78,54,82,0E), + wwH16(ED,AC,BD,6D,1B,38,86,0B,D0,41,74,38,BD,B8,E0,E9), + wwH16(1C,16,4B,EF,63,63,6B,EA,1A,63,30,C6,AE,35,40,8C), + wwH16(AF,1D,7F,E8,4B,85,54,1F,2C,D8,62,44,E0,85,7E,93), + wwH16(CB,87,B7,D9,16,CF,44,0F,8B,FB,40,5A,C9,DE,1B,3F), + wwH16(0F,6D,80,A5,1B,73,72,47,CF,6C,B4,60,C2,2B,21,4A), + wwH16(42,EE,19,05,99,FA,2E,A9,D1,A5,A9,7C,09,38,17,FE), + wwH16(F1,7A,76,6B,CF,C6,A9,7A,B9,90,07,D7,92,CB,C3,FA), + wwH16(CF,1B,C0,A6,8A,A9,2F,83,2F,42,16,70,F4,AD,0B,0A), + wwH16(5E,5F,A0,73,84,75,53,5F,F0,63,A3,5D,4C,0A,75,C1), + wwH16(30,93,39,94,D5,38,CC,7C,6B,37,C1,7F,CC,F4,DC,79), + wwH16(47,5E,01,44,79,FF,7F,38,3E,1C,9C,76,96,52,42,D3), + wwH16(5F,06,A5,CD,D9,22,EC,D2,95,5B,CF,88,28,2A,67,E1), + wwH16(EA,E0,F4,11,70,3C,06,69,46,A9,7C,CC,1F,76,A5,1B), + wwH16(7F,C6,4C,44,1F,CA,4D,2B,63,8E,AB,B7,F1,E1,44,5B), + wwH16(DA,A4,1D,8E,EE,24,85,ED,5E,B7,78,4C,CD,40,4F,AC), + wwH16(56,E2,FB,F1,97,5B,95,94,83,BA,CA,3B,D0,87,A2,FE), + wwH16(8D,57,6C,00,0A,73,A5,D6,F7,A4,4F,6A,C0,9B,8D,99), + wwH16(AB,65,81,72,F5,A2,64,56,18,26,37,6E,68,2D,50,F0), + wwH16(19,60,FF,83,43,06,FC,B9,F3,EC,D2,7C,78,77,AE,67), + wwH16(35,46,7B,11,C0,CF,13,BF,FA,46,5C,A0,EC,6A,7C,0E), + wwH16(27,14,7B,D0,A8,D0,28,65,DF,DB,12,D2,30,2E,08,73), + wwH16(BB,A5,5C,52,FB,EB,B7,E0,DD,EB,E3,EA,A4,91,8C,48), + wwH16(12,4E,98,A8,70,B0,6E,07,0F,84,BA,E8,FF,E8,B0,59), + wwH16(71,FE,62,2A,54,B1,79,55,DF,D4,09,F0,22,F5,7C,94), + wwH16(72,93,38,0A,A6,7F,48,F0,85,39,F2,D7,45,11,7F,F7), + wwH16(67,36,76,F7,38,EE,83,CE,CD,42,61,62,AA,C4,92,ED), + wwH16(30,C9,C8,34,75,A4,E9,4D,67,84,55,64,13,11,2C,75), + wwH16(D2,1D,09,1B,E5,4C,AE,09,43,94,61,9E,8E,52,A1,25), + wwH16(DF,A2,A6,53,B4,78,6C,B8,7A,13,9E,5F,EB,13,2F,AC), + wwH16(DA,0D,88,44,1C,DF,62,06,AB,DE,18,FC,B6,21,87,20), + wwH16(9C,2D,95,AA,01,70,EC,0D,79,1B,78,3C,2F,98,33,09), + wwH16(3A,9B,9C,01,3D,A9,EF,AE,E1,19,82,7C,19,B8,E5,F0), + wwH16(5C,F7,02,AE,41,AA,2E,19,C4,80,EE,EC,7F,D0,17,36), + wwH16(99,97,9E,42,D6,48,AF,B3,B2,37,8A,A1,E3,C1,6C,63), + wwH16(BF,A3,D9,39,73,DF,4B,08,29,71,4A,22,5F,45,2A,FC), + wwH16(D0,0A,B6,2A,90,4F,32,91,12,B4,62,52,8A,B7,3E,68), + wwH16(9F,88,CC,AE,5D,4E,B6,16,7E,10,6A,0C,68,CA,E7,BC), + wwH16(07,93,27,96,13,FF,40,F8,E2,B0,13,FF,F5,86,A3,3E), + wwH16(F5,6E,AD,95,D7,E7,0F,EC,6D,EF,BB,FB,36,40,1F,93), + wwH16(73,D0,79,CA,C6,B8,97,C4,95,67,76,CE,DC,DF,A5,15), + wwH16(B0,7A,20,33,8F,43,68,AE,B0,13,0B,20,92,87,43,E1), + wwH16(82,07,2A,AC,05,73,F3,67,B2,DD,6A,82,7F,C8,B4,10), + wwH16(F1,C5,A6,D4,78,8A,4C,E2,3C,86,95,DC,6C,3D,18,66), + wwH16(AC,99,2B,34,29,67,E2,05,DC,E3,5E,D5,E1,D7,3C,AE), + wwH16(4F,18,8D,49,D4,C9,96,8A,C4,A7,51,3C,69,6C,8D,7D), + wwH16(5D,3B,B9,5E,F5,34,4C,08,B2,19,47,76,0F,B6,A4,70), + wwH16(41,E0,40,FE,F5,44,5C,87,85,21,6F,93,3B,EE,D6,66), + wwH16(B2,34,AF,DA,1F,A5,DA,49,DC,AD,EA,65,BC,88,82,45), + wwH16(87,80,4D,67,5A,B8,D3,E4,67,DC,52,C1,3F,75,92,39), + wwH16(10,14,8B,4B,8B,D1,1E,38,80,A4,94,C6,8A,1B,2A,A6), + wwH16(DC,8D,A4,41,05,13,AE,C7,CE,44,96,78,0F,DE,7E,B7), + wwH16(E2,FB,FE,44,CD,FE,DE,C3,A2,EE,02,14,FB,87,95,CD), + wwH16(F3,83,36,E0,56,B2,0C,70,A0,9E,CC,24,1A,DF,BD,73), + wwH16(8D,6F,3E,09,61,51,8B,DE,11,E8,48,24,62,FA,6F,C8), + wwH16(73,0D,F8,19,03,D4,80,3D,3B,57,21,08,E4,57,D2,25), + wwH16(8A,D9,82,92,ED,01,C6,D0,38,A2,52,EB,DC,15,B4,12), + wwH16(BC,2C,5A,2D,0F,20,F5,CE,07,48,18,28,C9,A1,74,22), + wwH16(15,A2,F7,E7,F7,D3,56,1A,55,18,E2,B4,A5,06,09,94), + wwH16(05,CB,A3,EC,3A,C2,61,D9,B2,54,6E,9E,36,78,BB,4D), + wwH16(A3,04,00,93,62,28,38,92,94,4F,0D,41,01,B3,E5,DE), + wwH16(BE,57,8F,2F,77,B2,C2,A7,C4,1A,1E,95,1D,01,52,10), + wwH16(D6,A5,61,24,3A,E2,C4,D3,7E,C3,35,41,93,7F,E6,0A), + wwH16(37,BE,11,D6,25,20,92,8A,D2,59,07,F1,D8,FA,00,14), + wwH16(5E,83,00,6C,7F,5C,95,FD,1C,A1,23,12,45,52,76,44), + wwH16(1E,2B,3A,78,79,E1,B8,AB,5E,3F,AB,85,17,8C,AC,AA), + wwH16(5D,60,FA,05,7B,3C,06,42,41,F7,2C,A0,EF,EB,41,93), + wwH16(3F,DE,6F,85,2C,43,89,43,E1,1F,52,3F,29,C5,03,9B), + wwH16(81,70,1C,FE,CA,EA,92,37,0C,5C,C9,4A,D9,1D,7B,7A), + wwH16(61,7F,BE,E8,EA,2A,39,96,79,62,4E,4D,BB,A8,23,D2), + wwH16(3A,07,FC,49,27,EF,F1,38,EE,2A,DC,00,A9,EF,51,1A), + wwH16(40,B7,50,C4,3B,EB,6B,64,D1,80,32,EE,D3,49,ED,E8), + wwH16(9A,B0,18,47,B9,29,C1,5E,4E,AD,77,32,7C,EE,8C,B2), + wwH16(9D,81,C5,F4,7D,10,DC,D8,19,67,82,40,31,4B,4D,9A), + wwH16(5F,B7,8E,99,D1,25,39,14,69,3A,BE,7F,84,2D,C0,8D), + wwH16(93,6A,C0,C6,57,60,E0,A5,38,36,BF,B4,DA,94,8E,D7), + wwH16(4C,4D,E9,D2,E3,FD,1A,C0,8E,C2,C4,9E,B9,FF,59,2E), + wwH16(7B,2E,89,DB,C4,E7,37,41,6E,3C,A7,29,37,0D,66,02), + wwH16(53,DE,0B,2E,FA,2C,6F,9D,F0,A9,40,94,11,F6,33,04), + wwH16(76,95,CB,43,13,BB,33,E0,21,3C,6A,D6,0F,7C,7A,8F), + wwH16(BD,A9,B8,E9,8E,5D,F0,36,7E,0D,E8,1F,EF,79,15,64), + wwH16(7C,46,6A,66,DE,98,24,B0,60,70,DB,DB,01,8F,17,67), + wwH16(C2,AC,DD,7B,98,82,7A,90,1D,8F,DB,F2,36,82,26,50), + wwH16(71,F6,B6,75,6E,DA,53,65,4D,C7,85,EA,61,00,33,45), + wwH16(A3,15,B1,A9,59,43,1F,EA,8B,3F,31,38,97,A1,F8,14), + wwH16(A9,53,A3,8A,CB,12,CA,3F,FE,8E,7D,AF,8C,B5,1E,D5), + wwH16(41,B7,B0,21,FE,D5,0B,FE,B9,EE,85,83,9F,97,DA,4D), + wwH16(10,44,CB,B1,AE,2B,5B,15,CC,80,C3,4A,AE,D1,48,67), + wwH16(93,0A,3B,5D,9D,C0,A8,0F,FE,D0,75,DF,53,E9,69,B6), + wwH16(15,33,BA,26,16,AA,B5,A4,44,9D,F1,81,B0,1E,6E,E7), + wwH16(2C,88,49,DC,EC,04,D8,29,CE,66,A1,3F,60,52,EA,2A), + wwH16(82,91,78,CA,EB,56,B1,F2,5F,0F,AF,5C,98,47,FA,3F), + wwH16(D6,2D,81,89,6C,1F,9D,B7,EB,77,A3,D2,AA,A3,58,99), + wwH16(89,BD,A2,E2,3D,91,84,19,1C,2D,0F,17,A2,4B,2F,55), + wwH16(1C,45,D6,74,3B,8C,0C,AF,65,6C,6E,10,67,A2,AD,C2), + wwH16(28,14,23,69,F6,D9,BB,4C,CF,03,56,D6,68,BE,C5,95), + wwH16(95,2E,FB,9F,53,0D,19,D6,20,1C,F1,26,EB,48,D3,6C), + wwH16(A9,69,7C,5F,82,46,CC,8D,C5,E1,DF,0E,A5,90,5D,B6), + wwH16(F2,80,86,AC,C4,4C,0B,3E,FC,C4,37,65,54,6C,B0,1C), + wwH16(F7,B5,B9,8C,17,FA,14,78,05,30,B7,31,6B,D3,DB,9E), + wwH16(B3,5D,A9,5F,AA,28,F6,29,BB,3E,A0,39,09,25,C2,73), + wwH16(E3,A6,D0,23,0C,CE,C3,17,49,63,31,B2,C3,80,A4,5D), + wwH16(E2,75,6B,EC,20,6F,A6,22,29,5B,C1,B3,CE,AD,02,63), + wwH16(E3,E7,7A,8B,11,D4,3B,34,0A,D3,B8,9D,82,4C,46,41), + wwH16(AE,5B,5A,3D,38,3B,AD,B8,CA,CD,EB,E8,05,CF,E6,44), + wwH16(29,63,9F,EC,35,C6,16,BC,61,5F,9E,51,B2,00,C8,54), + wwH16(E1,B5,33,6A,EB,3F,64,AC,44,10,42,2D,7A,8F,AE,E6), + wwH16(18,F9,39,E8,9F,7D,43,E0,21,4C,BB,3C,BA,AC,09,05), + wwH16(78,0C,84,1A,DE,75,21,E1,A7,8A,7B,5F,45,60,A2,90), + wwH16(16,B1,20,24,86,75,C8,02,35,AE,6B,14,4C,BD,EB,4B), + wwH16(38,97,3C,65,8B,FF,9C,72,B4,94,0A,B0,B2,CB,B3,B8), + wwH16(C0,DE,28,F1,DE,00,02,C9,EC,90,81,0D,B6,EC,72,EA), + wwH16(30,E9,22,40,90,6D,FD,1D,FF,CA,37,1E,BB,FE,C8,A0), + wwH16(91,F4,BC,E8,97,31,95,01,FB,08,5B,03,A0,23,D0,41), + wwH16(0B,7B,12,51,49,64,04,87,83,77,0E,50,61,97,DF,FC), + wwH16(CD,23,F7,D5,46,D4,0D,CD,57,AD,A2,B7,E5,BD,07,47), + wwH16(C7,34,11,FF,05,43,FE,65,26,F3,D4,86,74,5F,AA,6A), + wwH16(CA,1B,98,90,60,5B,F3,44,F9,E1,BA,B4,2C,E2,E9,AF), + wwH16(B6,DD,5C,81,40,75,AB,AC,3C,5C,AC,2C,F8,46,54,34), + wwH16(1F,34,04,32,36,92,6D,65,0B,E9,34,66,F3,98,44,2C), + wwH16(26,62,85,89,3C,41,16,4C,FF,C2,26,A5,94,E1,5B,42), + wwH16(90,59,B6,D9,3C,8B,77,F7,F5,FC,E9,B5,32,78,D4,CF), + wwH16(77,A6,E0,59,1F,AD,8A,EF,E6,00,D1,4F,DF,97,6A,CE), + wwH16(A5,D4,11,5A,DB,6C,0D,1F,DA,53,FB,EC,FF,90,27,57), + wwH16(00,AC,F4,85,33,49,2E,45,9A,F6,3C,47,68,9D,1C,0B), + wwH16(6C,4F,46,ED,07,A8,AE,71,A3,0E,93,AC,DE,9F,DC,CC), + wwH16(A0,31,19,B2,F5,23,8F,16,DA,A9,DA,E9,0C,9A,4C,B1), + wwH16(59,5D,83,9C,73,A5,64,02,76,BD,FF,58,4D,90,00,40), + wwH16(4D,BE,A2,71,D4,06,00,07,3B,BA,8C,67,11,31,A8,1E), + wwH16(F3,CE,4B,75,8A,10,96,64,D1,51,08,C9,BC,D6,33,A3), + wwH16(39,E5,E1,EF,06,7F,18,70,63,F3,3A,66,04,97,DD,F2), + wwH16(62,EC,46,F2,9E,46,D8,9A,4A,A7,14,5F,6C,BD,9B,55), + wwH16(A8,7F,FF,B9,B2,20,4D,6C,D8,09,ED,70,88,37,14,B1), + wwH16(68,59,5F,15,5D,EF,3E,55,EA,5F,B2,4E,10,4F,A2,D5), + wwH16(03,00,A4,5E,E2,6E,BB,63,A9,1D,41,14,8B,25,B3,EE), + wwH16(D5,6D,DA,75,F3,52,C6,23,D9,9C,31,27,76,25,59,AA), + wwH16(92,6A,18,E2,45,E4,DD,09,19,7E,30,73,DE,B2,79,1F), + wwH16(51,13,68,9A,6A,84,92,8F,53,0E,DA,B2,53,AD,2B,D9), + wwH16(14,98,DE,65,EA,34,1E,60,6C,9C,B8,0C,AD,3A,E9,B9), + wwH16(1E,74,38,3F,7B,5F,C2,F8,F0,41,83,9C,92,D8,96,1D), + wwH16(01,78,F6,07,73,83,75,20,26,80,4C,19,A7,E6,29,8E), + wwH16(AF,64,98,3E,9E,9E,29,D6,98,CD,59,F8,14,5B,9C,4C), + wwH16(75,A0,AE,8C,BC,76,19,56,E4,62,05,48,E5,9D,19,02), + wwH16(D8,43,A9,1A,E8,3C,46,C3,1C,2C,81,99,B4,2D,A7,5E), + wwH16(CB,D5,68,42,E6,FD,3B,A8,AE,68,50,91,27,AC,1C,AF), + wwH16(84,C5,4E,C1,43,99,AF,65,35,FB,53,2A,32,42,87,A2), + wwH16(C5,35,EA,93,05,C5,03,A0,C1,A6,93,E2,93,1C,B1,B7), + wwH16(F4,26,81,B5,D3,84,5C,D3,0D,C4,12,6F,45,3C,64,CE), + wwH16(A6,5F,04,9B,F5,56,02,A9,13,32,67,EF,87,F4,13,85), + wwH16(A7,74,2A,1D,5D,DD,A8,B3,7E,6F,8F,28,B9,2B,25,63), + wwH16(4A,5D,95,0A,1A,97,2E,33,5B,E2,3E,A2,C3,79,24,33), + wwH16(01,05,2A,C7,13,0F,1C,F8,29,5B,7D,3D,5A,B9,B7,E0), + wwH16(F7,B4,F5,C1,99,57,7C,2D,B9,68,4B,BA,23,D7,10,C7), + wwH16(E1,3F,DD,95,F3,50,41,17,A3,B2,E4,E0,73,A0,2C,3E), + wwH16(E6,9E,C8,44,B1,9D,FF,6D,C1,17,12,B6,20,E6,AC,FE), + wwH16(C5,EB,15,89,9D,8B,CA,ED,76,4B,11,63,28,94,56,BD), + wwH16(19,7A,D9,3F,3B,AB,AB,85,2B,D1,39,47,0C,D4,A1,93), + wwH16(FC,23,D2,74,1F,48,93,71,68,DE,6B,13,30,A8,94,B0), + wwH16(49,7C,B5,AD,CB,63,5E,8F,4A,F8,2C,DA,2E,EB,30,F0), + wwH16(64,5C,74,D4,5E,60,89,F0,9D,B1,2C,96,EA,78,59,BE), + wwH16(78,E6,BE,76,45,84,73,80,AB,94,8D,8F,38,92,7F,48), + wwH16(08,14,91,43,4D,F3,EB,85,6B,80,80,77,45,E7,C9,B9), + wwH16(0C,A1,45,5D,BC,77,63,23,66,4E,30,BF,3F,16,55,0D), + wwH16(D2,E7,A0,8F,EB,1D,E4,11,01,C0,00,F5,55,FC,55,1B), + wwH16(BB,F3,2B,1B,90,5C,B8,F1,54,3A,EC,2F,3C,7A,70,C7), + wwH16(C7,C5,C9,4B,B4,2F,FF,B1,7E,88,CE,89,22,F3,07,31), + wwH16(4E,9C,12,92,06,9D,76,7F,D2,F3,39,94,98,73,19,CF), + wwH16(0D,9B,1B,1B,96,3C,2F,1C,84,FD,7D,26,7F,5C,12,81), + wwH16(C4,26,DE,0A,B2,4C,81,16,04,D6,6E,48,1C,48,81,A1), + wwH16(52,49,30,78,E8,98,61,CB,8D,EE,96,9A,D7,1E,2C,39), + wwH16(2C,A0,74,AA,F2,EB,D7,41,42,26,0D,F3,09,73,4C,28), + wwH16(5C,5A,3B,7E,1D,9A,27,B0,83,5E,9F,F8,CD,57,0F,9F), + wwH16(A0,19,C0,47,D4,A9,F5,8E,B4,0F,3E,91,50,E0,49,65), + wwH16(D4,87,37,10,3B,D3,0D,FC,1F,E7,6B,9C,99,0E,F9,BE), + wwH16(5F,5A,8B,EE,AE,15,E0,AE,6F,1A,D2,D7,1A,0A,73,00), + wwH16(A9,83,91,56,69,F4,E3,42,6B,46,FF,A7,FB,E0,38,0A), + wwH16(0D,C2,20,6C,6C,46,49,93,74,B9,32,5F,6D,4E,1F,C4), + wwH16(35,9A,93,C0,51,EE,39,08,4B,C6,1F,51,08,A9,A6,43), + wwH16(2B,4D,D7,87,7D,AC,C2,08,D5,44,CB,84,45,3E,E3,EA), + wwH16(23,50,8B,9C,3B,04,1F,6B,56,0B,11,6E,EB,7E,36,7C), + wwH16(F5,53,DE,62,14,7C,7A,3B,24,E4,66,2B,C7,21,A7,15), + wwH16(C0,88,7E,88,CC,97,34,4A,3B,60,E3,AF,8C,4A,BC,74), + wwH16(A0,F9,FA,6C,DC,5E,CD,5D,95,E6,DF,5A,14,E2,C4,6C), + wwH16(E4,EA,9B,0A,2D,41,F1,05,D4,09,40,73,01,79,A9,06), + wwH16(BE,83,4D,2A,A6,27,73,F6,C8,DC,62,35,6B,8B,2A,5B), + wwH16(11,60,D7,3A,35,F7,A1,71,CA,8C,13,76,19,36,EE,16), + wwH16(78,6E,41,CB,1B,BB,A0,99,83,40,90,A4,A6,19,C7,F0), + wwH16(52,ED,59,26,CE,EA,FA,D1,FE,53,AF,5B,79,72,17,13), + wwH16(28,46,B8,6B,3A,CC,88,21,20,17,5D,FF,FF,68,43,D4), + wwH16(7A,05,33,78,E7,3B,5C,87,9F,D4,15,EB,99,3D,71,1D), + wwH16(1E,73,2B,12,D2,93,2D,A7,61,95,0D,2D,8F,30,EE,2E), + wwH16(35,EF,B1,39,82,C4,55,ED,FE,4D,3E,5C,3E,1D,82,9F), + wwH16(7F,2D,B3,57,EE,0A,EF,D3,9B,C5,D6,EC,82,D8,FF,7A), + wwH16(CD,E4,E3,B2,28,3F,0F,8D,13,18,C8,21,FB,D4,78,9C), + wwH16(D9,17,C6,51,44,C0,AF,1E,6A,94,72,44,94,6D,7E,B4), + wwH16(23,3F,E7,13,B3,A9,E0,3D,61,6C,E3,87,80,20,4F,43), + wwH16(38,D7,53,E1,6A,51,E9,B0,4D,78,45,3D,24,FF,35,85), + wwH16(64,C0,6F,C9,8F,71,93,6A,54,61,B1,B3,92,4C,60,0C), + wwH16(E2,3D,64,63,09,48,A7,8E,6A,BB,16,30,9F,A8,8E,E6), + wwH16(13,DB,44,1E,21,B8,93,00,1E,E0,C0,57,10,1C,00,3A), + wwH16(6C,37,E0,F4,D4,38,CD,54,6B,B6,CD,D4,6D,DC,F3,89), + wwH16(ED,80,AD,AC,2B,E4,3D,B9,78,F2,D2,B6,2D,1B,45,63), + wwH16(89,58,E2,8C,EC,CB,2C,45,F8,A7,A1,E3,99,35,B9,24), + wwH16(0F,08,17,B1,F1,31,26,26,0D,DC,89,3B,2F,80,80,06), + wwH16(ED,0E,3A,53,78,B7,34,53,D8,82,71,F0,B0,5B,4E,85), + wwH16(F0,FB,68,80,B5,C0,64,92,9E,BF,F9,BD,10,F4,3C,49), + wwH16(9F,84,95,F5,C5,38,1A,96,E8,61,43,46,3F,EB,61,C7), + wwH16(C8,B3,B5,6B,B7,2F,CF,DB,60,04,2E,22,D5,C8,7D,85), + wwH16(3A,E3,FB,94,57,26,AF,15,B0,BB,CF,C7,8F,80,28,FC), + wwH16(B1,74,A4,30,5F,4B,90,26,63,4D,33,43,22,FB,0D,34), + wwH16(10,8A,A3,86,E6,0C,07,7E,6B,C2,55,FB,E4,6B,3B,34), + wwH16(7F,2B,06,08,86,C3,91,CA,BB,3E,2C,1A,BD,7B,B2,5A), + wwH16(2E,CF,CC,91,DB,F3,B9,2F,FF,F4,31,DB,81,23,9B,D5), + wwH16(63,2A,1F,74,1C,38,8F,2E,B9,CA,30,C4,39,51,DC,B6), + wwH16(58,22,59,7B,C0,16,C3,DE,B1,40,FF,9E,44,D9,9D,62), + wwH16(C4,27,03,03,CE,73,91,F1,EA,4A,0B,CB,8B,84,EF,75), + wwH16(70,91,A7,55,B0,91,94,A6,CF,43,3B,A3,BA,46,58,CF), + wwH16(DD,EC,00,B7,79,A0,2E,2E,FD,13,39,DE,15,FB,DE,6E), + wwH16(B0,2B,C3,39,F7,F0,74,A9,0A,96,FE,0E,3D,48,2F,C4), + wwH16(37,55,50,CD,05,5D,A1,7B,A5,13,FF,F9,DF,5B,F7,49), + wwH16(60,16,1B,9C,9D,FD,96,8E,51,D4,86,0B,71,DA,29,44), + wwH16(92,2E,5A,2B,30,2B,EC,E2,74,1F,F8,42,46,A9,A8,BC), + wwH16(56,59,A4,24,D4,71,64,7B,6B,0B,9E,1B,A8,AC,F7,F6), + wwH16(93,AD,CE,E7,BA,57,74,C7,64,D9,EF,E9,B3,88,94,67), + wwH16(63,39,A1,4B,43,AD,D2,15,F2,0B,6D,F0,93,2B,6C,66), + wwH16(10,CF,4C,5F,FA,44,8A,F0,78,F5,7E,B4,C8,68,A2,B4), + wwH16(81,F1,FD,5D,6B,78,E3,04,F9,A1,1B,29,74,3E,6E,4E), + wwH16(11,AE,BC,68,3F,FA,CF,CA,81,24,BA,C9,04,60,F7,2E), + wwH16(13,39,D4,CA,CE,84,84,57,07,F9,61,F9,EF,62,9E,35), + wwH16(A4,C8,A2,C5,CF,29,65,3B,79,76,A3,24,D1,97,11,56), + wwH16(59,CD,C0,D7,3F,29,9F,E0,56,BB,86,0B,02,1A,93,3F), + wwH16(63,B1,0E,18,2F,90,99,E3,A9,0A,20,E1,CF,19,2E,31), + wwH16(89,9B,F8,5C,12,00,8B,43,F9,A8,93,DB,F5,F2,A3,DF), + wwH16(B9,18,3F,73,0E,B9,93,06,00,0D,98,71,86,89,1A,C5), + wwH16(09,FB,B0,C9,F9,6C,51,38,D8,06,E2,B3,BE,69,35,55), + wwH16(02,48,0C,54,34,7B,14,13,B4,C6,27,8B,D5,E1,5A,63), + wwH16(88,7A,AA,2B,A7,18,AF,04,56,7C,07,81,5E,BF,97,A3), + wwH16(81,D9,D7,AD,CE,F0,3D,0E,9B,DE,E7,64,4E,79,9D,58), + wwH16(3A,87,64,F7,22,00,99,2B,DE,3C,25,AF,31,58,E0,C1), + wwH16(F7,53,B8,46,03,D0,2D,8D,28,45,D0,F1,CF,FD,C8,0A), + wwH16(B2,72,FC,23,81,02,0F,E4,12,61,89,8F,B6,D8,0D,22), + wwH16(AD,98,7E,7A,46,68,BE,2B,E2,2C,4A,C7,9C,4C,CF,F2), + wwH16(34,59,07,7B,AE,E8,26,C4,32,6A,B5,06,56,9D,F0,1E), + wwH16(F2,39,4B,07,88,A7,DB,FA,B1,06,8A,21,0B,4F,52,F6), + wwH16(CE,65,84,D9,ED,1A,B2,B6,03,4C,E6,D5,F7,4B,95,55), + wwH16(77,46,AF,10,C9,56,FB,F7,DC,D6,C8,1C,6C,B6,07,F7), + wwH16(38,76,52,12,8F,C8,0D,EC,06,87,0B,AA,B9,14,9C,5F), + wwH16(14,ED,EE,60,63,55,54,FB,75,11,73,DC,8A,AC,E4,3D), + wwH16(73,CD,BD,11,4A,11,24,CF,6C,D6,EF,C2,F1,03,8B,C6), + wwH16(D2,30,8F,B6,BE,93,6A,47,BB,D7,35,DD,53,10,51,16), + wwH16(1C,A1,A6,FE,43,F2,B4,9D,EA,BF,8E,0B,C3,2D,BF,88), + wwH16(D5,9A,1F,4B,F7,C1,45,D4,44,8C,EB,35,B9,B6,98,47), + wwH16(A4,32,94,71,E0,E1,ED,0B,16,91,07,A7,1D,22,F5,16), + wwH16(4A,E3,92,1C,66,A3,5F,F4,62,98,4A,36,1B,5C,2B,EF), + wwH16(1F,06,7B,BA,61,86,D0,6D,B5,7A,16,95,CD,68,CF,B6), + wwH16(B0,6A,9A,B2,BC,7F,07,7E,F3,5F,9E,C7,34,41,9C,32), + wwH16(0E,15,CD,56,4B,66,1E,2B,A6,85,8C,7A,52,F9,8C,17), + wwH16(C0,95,22,04,00,F6,50,18,55,D0,A4,93,D1,12,B9,4F), + wwH16(F0,AE,CD,92,20,28,9E,39,F0,84,39,11,DA,19,05,7E), + wwH16(C3,ED,2A,5D,7C,AD,44,BE,39,46,41,06,C9,82,34,14), + wwH16(3C,92,8F,75,8F,46,FE,2A,2A,C6,BE,1A,2B,2F,50,90), + wwH16(3C,28,31,AE,28,ED,19,F7,D5,58,E1,7A,98,D0,B2,F0), + wwH16(93,54,30,3B,31,CA,51,DE,B9,14,57,37,40,DB,8E,33), + wwH16(E3,FC,C8,99,7C,AC,F4,D8,66,25,40,F0,9F,F3,BF,D5), + wwH16(77,41,48,E9,2B,F3,12,11,A2,E7,45,8E,67,3F,92,E3), + wwH16(94,6B,86,BC,2E,4A,25,97,E1,CE,76,27,A9,EC,6D,8A), + wwH16(36,32,8F,68,1F,78,12,CB,69,85,AF,DE,41,2A,60,4B), + wwH16(D4,74,3F,AB,82,80,19,C4,5C,19,E8,9E,6A,43,06,89), + wwH16(5E,C0,BA,38,8C,BE,4F,6F,B0,04,EC,B1,DC,B0,D9,CA), + wwH16(49,A2,54,29,8A,87,73,1C,2B,38,8C,AA,02,9A,F5,60), + wwH16(1B,D4,6A,B9,FD,6C,BB,F7,1E,31,7E,6C,0B,75,2C,EB), + wwH16(47,50,1E,CB,00,B9,3F,CA,01,3E,3D,8E,79,E9,BA,9B), + wwH16(CB,CC,00,C8,2A,5B,3A,B1,E7,E5,73,C6,41,9A,87,12), + wwH16(B8,57,71,99,B6,87,DE,23,5B,D1,D0,D7,6A,D0,4F,AC), + wwH16(99,DD,11,66,FB,EB,D0,0B,81,BB,73,91,AB,83,DB,D2), + wwH16(05,B8,EB,86,C5,C3,48,43,89,E4,CE,7D,36,2A,16,00), + wwH16(8F,6A,A5,6C,DF,5C,9D,01,A4,1E,68,E7,95,D9,77,FA), + wwH16(92,D2,D3,A6,7D,30,D7,89,26,67,B5,33,22,19,37,5E), + wwH16(94,53,4C,7A,50,B4,5B,34,0A,DC,AD,38,5C,CF,4B,94), + wwH16(0E,3F,D5,CA,A2,21,D2,06,FB,A5,8C,A8,FE,A6,6C,76), + wwH16(51,70,E5,EB,93,9A,AD,8C,27,23,16,04,05,17,3A,7E), + wwH16(B7,4B,43,4F,11,90,00,86,AE,C3,61,79,C9,AB,EE,6D), + wwH16(75,2D,49,79,71,51,63,A9,39,A9,1E,23,83,3D,D3,37), + wwH16(45,34,71,F2,C5,BF,2F,20,E7,C1,C0,18,F0,11,70,B1), + wwH16(EE,9F,1A,EF,AD,1F,D9,DD,D1,AF,7A,61,E7,21,D5,4D), + wwH16(C9,66,0B,E3,F6,6F,0D,99,BE,2A,BB,B6,70,33,26,0E), + wwH16(B1,33,2F,32,CF,E8,7B,E9,36,64,CB,20,54,BD,78,2B), + wwH16(2E,C7,E4,7A,97,D0,5B,B2,E7,CB,5A,76,87,EE,29,99), + wwH16(A4,C2,BF,AB,38,58,75,2F,92,00,45,94,5A,D1,18,3C), + wwH16(BF,E1,84,87,94,45,47,C8,7D,66,96,C3,96,22,93,1E), + wwH16(12,A8,0F,93,FD,15,CA,A1,4B,45,A9,0B,67,0A,6D,08), + wwH16(97,E9,97,31,A9,A0,C7,E4,1E,5E,28,DC,51,92,0C,E8), + wwH16(29,59,ED,66,C7,6A,80,1A,E0,88,F0,3E,ED,E6,CE,E9), + wwH16(75,27,F9,1C,94,17,8E,E6,E5,13,67,2C,20,E2,E1,DF), + wwH16(8B,09,9D,59,CF,72,BB,BB,B3,EC,48,21,A0,98,3F,03), + wwH16(12,DD,5C,7C,27,CD,BE,C8,0C,D3,70,3C,A6,D2,B0,B1), + wwH16(C8,8D,39,A2,8B,95,FD,BC,3B,D1,CA,B5,D5,83,57,E6), + wwH16(13,5C,AA,34,11,9E,28,CF,45,0D,4B,2F,86,2E,FF,71), + wwH16(24,BF,FB,9A,BA,ED,76,AD,2B,78,C1,21,1A,68,A7,B5), + wwH16(BA,66,21,16,AE,2E,72,E3,A2,2A,D9,C7,0F,E3,2A,2E), + wwH16(78,F7,E4,13,38,A4,A3,50,19,68,9F,28,AF,42,2F,FC), + wwH16(AD,B9,CB,03,2E,BD,48,F4,3D,C3,A4,C5,70,B4,7E,AF), + wwH16(72,72,9D,86,31,B8,95,07,49,C1,27,18,C5,D6,AA,89), + wwH16(B1,7E,78,5B,9F,93,09,05,34,D9,4D,62,3E,EE,B8,68), + wwH16(77,E4,54,3F,3E,30,83,CB,EA,1E,BA,A1,AE,B9,C8,64), + wwH16(04,EA,DA,EF,D1,25,E2,C3,BA,30,38,B4,25,65,B8,48), + wwH16(E0,B2,96,42,D2,85,9E,DC,13,3D,98,96,D8,B7,C7,FE), + wwH16(E7,99,7A,07,02,4B,BE,F1,D6,47,0A,59,A6,E1,CE,CC), + wwH16(E5,C7,83,C0,1C,6F,62,4B,07,FB,1E,8C,2A,A5,E7,6E), + wwH16(BE,6A,56,A3,8D,6F,CD,90,E8,04,57,5E,E9,5D,E9,43), + wwH16(08,8D,AE,3C,C2,F4,96,6A,7F,ED,A9,A2,E3,E4,8F,B3), + wwH16(71,7C,20,02,E1,7E,93,68,F7,12,06,AF,CE,DF,E1,13), + wwH16(71,8F,EE,3F,CD,C2,9F,F7,0D,9B,81,33,8C,50,B9,B8), + wwH16(70,D9,09,B2,66,90,A9,68,5C,DB,12,5B,48,71,9A,82), + wwH16(65,F9,0E,13,87,FA,8B,61,D2,8B,43,C9,8C,76,CF,4B), + wwH16(D4,4C,98,71,32,34,3B,1B,5C,68,A9,5A,EA,6E,67,DF), + wwH16(BD,A6,E8,7D,CC,D2,18,1B,A7,E1,82,BC,22,D9,AB,5E), + wwH16(C2,C2,AA,A4,76,F8,E0,75,17,8B,3C,23,D1,E4,19,AB), + wwH16(C8,C6,E3,8C,92,6D,56,2D,99,97,D4,40,C1,30,86,C1), + wwH16(03,65,7D,B0,DE,2C,0F,8D,00,10,93,D2,6D,C8,FD,77), + wwH16(BE,1B,44,38,DF,1A,CA,78,64,EB,8C,3C,18,FC,05,F5), + wwH16(06,05,D2,49,B2,0C,B4,B0,3D,A5,CF,29,89,C7,9B,F2), + wwH16(0A,69,90,C3,83,D1,6C,82,69,B3,0B,D7,75,1E,71,C3), + wwH16(53,8D,31,27,37,28,36,E0,A1,8F,C6,1C,C7,1F,D7,5C), + wwH16(0C,90,E6,4F,3F,E1,CD,8B,80,16,7C,87,6B,1F,F3,F2), + wwH16(49,00,80,37,AE,C1,DC,89,C5,EE,4D,90,C1,66,78,C5), + wwH16(4D,9F,B1,0D,6C,1C,86,73,61,84,A8,F5,CE,15,0E,DB), + wwH16(92,BF,BE,57,18,30,15,26,66,2C,DD,13,7B,69,EA,E3), + wwH16(8C,54,60,3C,70,FE,04,FF,63,28,BF,92,EC,56,D9,82), + wwH16(B3,3E,88,42,76,E4,B8,71,FA,84,18,11,06,A5,93,35), + wwH16(FD,52,34,02,2B,35,49,A0,D1,4B,74,A5,6D,9D,00,F1), + wwH16(98,32,45,09,B3,BF,80,BB,22,F3,57,4C,61,16,47,75), + wwH16(59,EE,8F,1D,C2,45,D2,EE,0E,0C,F5,A5,05,88,CE,30), + wwH16(A8,6E,73,17,B5,5E,E0,00,EC,50,EA,15,88,8E,4A,35), + wwH16(F5,F6,BF,2B,59,AC,B6,29,27,D8,2F,F7,A9,1C,06,54), + wwH16(4F,84,CC,BA,63,1D,42,DA,BE,F4,BD,B7,14,99,28,CF), + wwH16(60,86,A8,9A,8F,BE,87,31,44,7A,32,59,F1,CB,1F,CA), + wwH16(4B,C5,3C,32,BE,AA,69,6B,36,A4,69,7E,0B,F5,73,51), + wwH16(25,51,C5,CF,03,07,96,1F,0C,93,DC,AA,57,32,04,49), + wwH16(2D,6E,AD,95,AF,5B,34,CF,A3,4D,D6,B9,2B,31,E0,77), + wwH16(95,F7,D9,D8,9A,77,44,A8,C1,C5,42,A6,AC,DB,BA,DC), + wwH16(FD,17,4A,A4,1F,26,1E,8D,A6,4A,41,25,18,26,16,0B), + wwH16(F1,59,28,EA,BE,13,C3,B7,00,BD,8D,39,72,D5,60,F5), + wwH16(75,B0,9C,23,24,55,51,F7,1B,AB,16,7E,35,43,DE,F1), + wwH16(BB,F1,F6,6C,13,B1,78,1B,25,D3,E1,9D,3B,30,59,18), + wwH16(A8,84,A0,F8,3C,F1,70,6D,0D,5B,DE,3F,A4,B9,11,A6), + wwH16(E0,F7,0A,CB,17,F5,FD,E1,99,7C,6B,AE,69,F2,F2,2E), + wwH16(21,2D,EF,D0,4B,98,3C,6B,1B,98,6F,A3,D1,EC,77,C7), + wwH16(8F,C8,84,46,A8,29,D7,F3,EB,72,69,3C,3F,AA,FD,12), + wwH16(96,F1,3D,15,28,D2,E3,6E,D6,6F,5F,62,29,B0,9B,D5), + wwH16(6D,2C,2A,B3,8D,32,89,93,52,43,7B,7C,56,7F,BE,2B), + wwH16(CB,E0,72,2C,29,50,A2,3E,8F,8A,72,63,4B,46,BE,AD), + wwH16(88,28,DC,7E,C5,7E,6B,F3,78,04,DA,AA,4C,F3,77,F1), + wwH16(DB,57,74,6E,B1,B5,50,60,0D,0F,75,9C,12,3D,E5,C9), + wwH16(94,CA,F3,0B,93,1D,8F,46,DB,D6,36,09,88,B1,84,EC), + wwH16(48,F5,52,93,7F,26,8E,B3,8C,A0,A8,4D,AE,63,A2,EA), + wwH16(FA,11,C1,E0,74,CB,55,B6,FA,4E,06,70,C4,B4,01,E3), + wwH16(51,8A,03,36,49,2E,E0,1B,CC,BF,80,B9,FB,24,73,0A), + wwH16(A0,70,77,85,8D,26,50,89,33,72,E3,2C,F0,D4,18,E7), + wwH16(5A,41,CA,14,1F,14,66,D4,A5,C1,08,7F,86,F1,AB,34), + wwH16(CB,65,20,D7,EE,DA,29,F1,3A,76,94,D7,CC,54,C5,E7), + wwH16(6C,4A,2A,3B,F3,DF,D6,44,02,B5,8A,37,22,52,E1,9A), + wwH16(C7,D1,90,36,BC,31,29,02,52,7B,A6,7B,4F,74,DB,9B), + wwH16(DB,8E,57,57,7E,77,40,62,FC,28,5C,C2,40,9D,16,8F), + wwH16(2F,E9,50,E2,50,64,D7,C7,0A,8B,AA,52,12,E5,2F,4D), + wwH16(7F,FD,34,E0,9F,42,5D,81,87,0C,C9,6B,26,72,53,25), + wwH16(B2,3D,ED,DC,6D,BA,B4,BB,20,2E,A4,FE,7F,72,A4,71), + wwH16(0C,5A,16,E1,6F,4F,DF,61,10,56,58,CB,BD,F3,04,AA), + wwH16(B4,67,EB,7A,07,C3,BC,1D,91,B6,BB,24,9D,25,CF,EA), + wwH16(A6,20,C2,4B,1B,33,E7,2D,0F,1F,4B,08,8E,FD,C1,0F), + wwH16(B1,DA,51,80,C5,F3,AD,30,42,5A,E1,84,C6,86,3E,FF), + wwH16(A8,51,51,1C,E9,25,D4,2E,00,02,32,F2,37,B3,BD,19), + wwH16(86,DF,6C,90,5A,30,18,F7,BC,98,C8,73,F0,EA,A7,69), + wwH16(94,5B,71,62,B2,0D,14,62,95,82,EE,BF,5F,50,6F,50), + wwH16(77,1A,CE,3C,C9,C8,1F,05,21,F5,B2,87,36,54,B6,D1), + wwH16(E1,EC,37,45,40,C9,F2,24,1A,46,3D,33,DA,16,53,7F), + wwH16(8E,DB,89,80,79,66,6E,00,47,23,6F,C7,88,5F,9C,A0), + wwH16(F0,9D,0F,7D,68,21,AA,03,0B,5E,21,FD,E3,13,26,F0), + wwH16(AF,55,A8,C7,BD,27,6C,D4,D7,4C,69,21,80,29,EE,F5), + wwH16(89,4B,A7,BE,C5,96,10,A7,2C,39,AF,D4,52,01,48,61), + wwH16(E8,53,89,12,B8,AE,73,80,D3,88,A0,70,97,12,AF,AF), + wwH16(B7,B9,2B,39,0E,6F,64,BC,A5,51,91,D1,EA,9E,44,6F), + wwH16(01,03,5E,95,F4,E5,B5,89,7B,1A,97,94,86,F1,BA,E4), + wwH16(B0,EC,DB,2B,76,F2,32,15,7C,D3,50,FF,26,0F,57,22), + wwH16(D6,89,89,A4,23,95,3D,96,CE,D2,57,B7,C1,29,94,CC), + wwH16(22,DD,E6,B8,22,15,64,59,B0,52,BC,79,71,B4,9E,3D), + wwH16(D5,C2,4A,ED,F0,3F,4A,DB,BB,92,A3,88,CF,08,DB,AD), + wwH16(F5,7D,7D,59,76,29,32,CA,A4,E3,10,74,23,55,35,7A), + wwH16(99,AB,59,E0,74,8F,59,08,6D,DB,68,28,A1,CF,3E,CF), + wwH16(E5,81,01,B5,5B,6B,F8,15,63,CD,A7,04,11,23,C5,2E), + wwH16(7A,56,C9,72,F1,3B,70,62,31,EB,13,64,D1,DC,B0,1A), + wwH16(03,EA,61,A3,7A,03,EB,EB,AE,3F,7B,24,04,56,56,66), + wwH16(A8,7F,3A,9F,B7,8A,34,C9,E6,36,4B,5E,8C,27,6C,61), + wwH16(04,DA,75,37,84,74,4B,B5,02,8C,73,F4,86,2A,B7,B0), + wwH16(0D,83,FE,A2,A6,7F,B9,5B,40,50,24,6A,DE,CA,53,E1), + wwH16(A4,E9,6C,F6,58,67,96,8B,9F,F2,B6,65,EA,A9,53,3F), + wwH16(00,1B,9F,1E,7D,F5,66,CC,1D,5A,18,F5,B0,B3,A5,13), + wwH16(DD,5E,49,C2,5D,36,9B,E8,AC,AA,04,CB,7A,18,C8,61), + wwH16(8A,3D,D5,53,59,9B,63,A1,29,66,37,96,D0,02,8C,C8), + wwH16(4D,E0,24,E9,64,AD,EF,A5,16,60,E7,4F,1C,E7,59,12), + wwH16(8B,66,25,BF,7E,03,F6,34,B2,A1,F7,A9,0A,23,99,73), + wwH16(DA,9F,79,5A,46,47,61,D4,1A,70,B5,16,17,4D,B8,DB), + wwH16(78,B8,DB,9F,AE,A2,C6,91,D6,D2,6A,29,AC,5D,EE,35), + wwH16(04,D3,3F,02,B9,16,71,74,B0,E6,2F,7E,82,A3,5D,55), + wwH16(21,A3,0D,B0,C1,B5,DC,34,DB,9D,7A,16,E9,D3,3E,0A), + wwH16(37,93,C6,BE,DF,5A,5F,DE,81,D7,10,F4,CA,F1,3C,BE), + wwH16(F0,74,CE,65,08,F3,BD,76,8E,57,BF,C7,62,A0,DD,65), + wwH16(BC,76,67,A0,AD,FB,8E,94,51,10,EF,48,9F,06,1E,7B), + wwH16(5E,60,CD,68,7D,0F,57,E2,AE,2B,5B,A1,3C,00,71,AD), + wwH16(5F,5B,C6,DD,A3,0D,E5,9C,48,D3,C5,26,2E,69,F7,36), + wwH16(5A,C2,15,C8,0F,DE,15,69,2B,06,18,1B,F2,A1,00,F9), + wwH16(55,C4,60,DF,12,86,7D,4F,FA,3C,03,BF,E9,BE,18,E3), + wwH16(97,9E,BB,A6,20,93,41,A4,00,49,29,31,C6,D7,70,5E), + wwH16(FE,97,79,88,2D,B2,89,CF,11,06,1D,AD,EA,6C,D5,EA), + wwH16(F8,48,3B,63,C7,B6,6A,D2,94,36,62,97,78,D7,AE,ED), + wwH16(38,D2,FC,AF,F4,4B,71,05,3E,FD,65,F3,1D,DF,7F,AB), + wwH16(24,B7,06,FB,1C,CD,84,29,11,62,EC,3F,69,63,57,29), + wwH16(8E,36,BC,AB,68,ED,AB,65,DF,3B,3E,F8,15,25,9E,2E), + wwH16(A8,AB,2E,53,65,E2,34,A5,EA,47,21,73,4D,D2,86,6C), + wwH16(26,C7,46,1E,16,D2,94,A7,08,C8,16,E6,09,88,28,71), + wwH16(D3,66,41,34,EB,70,27,E8,64,86,F8,22,F6,87,CC,E0), + wwH16(48,16,1C,2A,11,1B,F7,34,52,26,97,F8,B4,80,71,25), + wwH16(F6,B3,E5,B2,C8,7D,ED,6C,5B,B8,A3,B5,CC,53,F0,D3), + wwH16(C0,35,5C,36,5E,C6,96,6A,26,25,64,F6,59,29,E4,4E), + wwH16(25,AD,35,A5,FF,13,FD,AB,14,69,80,B1,53,7F,01,EE), + wwH16(AC,83,6C,B0,9F,0F,BB,C0,6B,2F,90,BC,AA,B2,1C,BA), + wwH16(E1,00,73,62,B9,D7,57,AC,90,29,8A,7F,C5,9E,AB,A9), + wwH16(47,78,BA,12,2D,54,76,08,74,6C,32,9C,2D,EE,0A,A1), + wwH16(29,CF,05,E3,7C,C3,6D,AA,52,5F,91,D7,0D,DE,52,0F), + wwH16(2D,92,7A,20,BA,21,83,7F,81,28,1B,6B,4C,27,C8,63), + wwH16(0F,2F,CF,C2,DE,50,5E,D1,79,7B,07,AD,5D,B7,05,89), + wwH16(1E,ED,CB,60,89,21,06,2A,11,B8,8B,CF,73,FA,4F,15), + wwH16(05,59,65,29,24,A4,20,4D,0E,43,86,C5,6F,56,F8,3C), + wwH16(26,AC,D8,76,78,75,0E,11,58,42,05,B6,BE,6D,4B,28), + wwH16(B2,AC,DC,73,3D,FF,2B,08,B8,D9,54,88,47,83,C5,73), + wwH16(3B,6D,16,74,0C,53,B2,A6,46,4A,CA,BE,A0,60,2F,AA), + wwH16(FF,7D,B2,49,62,1E,70,A2,C7,44,C9,1D,7B,8E,F2,CC), + wwH16(28,A6,47,34,09,2F,96,4B,C4,00,95,AB,7F,B9,88,15), + wwH16(5C,6E,8A,FA,81,79,32,8C,EB,4E,41,37,94,75,51,BC), + wwH16(6C,79,CD,82,DE,11,C4,AF,B5,87,36,DC,4C,6E,5B,F2), + wwH16(C5,9B,1E,3D,61,5C,FE,E1,52,1E,86,49,60,55,D6,3F), + wwH16(43,B7,E3,33,32,8E,32,D7,06,DF,48,B9,73,79,8C,33), + wwH16(F7,02,F7,27,BE,85,67,CC,83,3B,42,78,61,4C,25,D5), + wwH16(13,2E,58,6A,8C,3B,23,3A,D2,61,5D,FC,56,3A,69,94), + wwH16(5A,02,81,30,FE,87,8D,43,98,4D,C4,B1,EF,0F,5D,78), + wwH16(FA,DF,02,BD,D6,EB,2C,04,24,EE,B0,3E,14,E2,9E,8F), + wwH16(5A,72,7C,5A,23,E2,1F,9B,40,4D,42,D7,B0,13,3E,90), + wwH16(C3,56,93,8F,47,0D,AF,A7,BD,8B,93,D9,73,C7,C7,78), + wwH16(10,0B,35,8E,71,7C,A7,4E,E7,75,EC,43,CB,E8,15,D0), + wwH16(51,ED,52,F8,02,92,A0,63,A7,56,77,7B,2E,68,9B,43), + wwH16(07,03,D6,5B,F8,1C,8B,DF,70,7B,D1,93,F8,DA,75,F2), + wwH16(5F,70,16,58,8E,FC,56,A4,3C,A5,77,9D,DF,E0,A3,0A), + wwH16(AA,80,95,AF,07,71,EA,EE,47,24,E1,7C,1D,86,33,7F), + wwH16(34,02,E3,26,8E,0D,1E,73,CD,48,5B,28,4A,01,62,46), + wwH16(7C,CA,0B,80,FA,A5,BD,8B,63,6A,37,62,7D,C5,D3,DF), + wwH16(C0,2A,CA,D6,70,1A,89,C4,62,C7,D7,85,B3,1F,C0,40), + wwH16(8B,8C,41,8F,BF,79,6B,73,B7,F4,73,FF,45,9B,DC,6E), + wwH16(CC,1A,7B,77,BC,CB,BF,20,08,46,E0,08,CE,D9,BE,FD), + wwH16(36,20,C1,41,F3,52,AA,28,4F,45,4D,64,85,3B,B5,8C), + wwH16(38,7D,DD,15,DB,24,BD,BE,BF,50,7A,6B,9C,E1,E2,39), + wwH16(D0,00,7C,2C,9C,78,67,8A,E3,EE,7E,44,A6,E8,C4,B9), + wwH16(72,A0,44,CC,19,E9,10,B8,76,D1,32,A2,E1,4A,57,38), + wwH16(FB,39,1A,40,07,E0,B6,94,9F,B5,59,0B,57,7C,F9,9E), + wwH16(AC,74,63,90,61,BE,45,C1,67,F1,33,DD,2C,BA,9E,69), + wwH16(9C,69,65,3B,6F,93,D1,59,49,40,90,4F,4A,78,5F,47), + wwH16(F0,0E,23,BB,89,12,5D,98,E0,6C,BD,D3,0E,A1,66,D2), + wwH16(F3,A3,F1,03,B7,EA,80,BB,30,FB,35,68,2C,59,D1,BF), + wwH16(54,9B,E6,FA,18,21,A0,52,AF,DA,F6,65,1A,04,43,03), + wwH16(90,86,2F,F0,85,C3,33,96,34,85,01,75,BC,B6,68,C2), + wwH16(F4,59,AD,57,92,6E,57,FF,8E,BB,8E,6E,D5,58,37,15), + wwH16(5F,C4,95,A5,9D,49,D9,D2,31,4E,30,2B,0F,4E,CC,B4), + wwH16(3A,0F,77,9A,D1,95,78,CE,30,07,98,7D,32,72,A2,B0), + wwH16(F4,DF,04,F5,06,F1,8F,34,65,E2,BD,F3,6B,15,1F,8C), + wwH16(C8,C3,70,4E,28,15,49,BE,01,E0,88,F5,55,48,FB,7F), + wwH16(A2,1C,29,6D,7A,BE,84,29,FB,ED,32,82,49,6E,CA,D5), + wwH16(A9,91,CA,16,1F,03,F3,5D,AB,5D,32,88,B7,6E,41,52), + wwH16(7D,0C,B9,1B,C5,2D,37,17,4B,39,C1,E6,A8,4E,EC,58), + wwH16(22,D5,C4,9F,19,E4,6C,25,2F,0A,2B,AC,AC,52,FA,6F), + wwH16(B2,AD,E5,37,0C,83,59,47,66,2F,5E,45,AC,62,AD,63), + wwH16(8E,4E,F0,07,0F,10,6D,B4,20,D8,00,AB,75,72,85,94), + wwH16(24,CC,ED,B8,09,60,9F,72,1D,CF,93,86,1B,51,5D,27), + wwH16(F0,DB,41,A3,B2,2E,AD,50,19,E2,10,1F,98,E7,00,6B), + wwH16(98,8B,B2,64,33,21,D6,B7,93,A5,82,12,2A,CA,64,97), + wwH16(E6,22,E2,05,98,99,B9,FF,A0,8B,FA,13,49,5C,54,17), + wwH16(79,BB,95,E3,66,CC,2A,99,FE,98,51,BB,2E,F5,4A,20), + wwH16(E7,07,F4,C7,0A,B7,F0,3A,4D,72,4E,EC,E2,F5,D8,57), + wwH16(6D,48,E1,6A,7E,F4,25,EC,3B,21,43,32,39,A3,EE,20), + wwH16(C9,1C,0A,5B,3B,8A,70,B5,A7,16,2D,DD,35,9E,5F,EA), + wwH16(95,13,FD,3C,40,6A,E8,18,89,63,D1,B5,E1,48,38,01), + wwH16(52,61,1D,DC,CD,F2,60,1C,5A,E0,0E,CE,83,AA,00,F2), + wwH16(61,FE,1C,B5,47,56,6A,38,51,38,B4,55,25,A0,11,5D), + wwH16(08,2C,80,02,5F,1B,4C,23,71,18,68,FE,B8,57,55,2E), + wwH16(56,8E,02,F3,F6,5A,F7,2A,26,74,AE,8C,F8,B5,EE,13), + wwH16(E9,6F,74,A1,B3,08,22,AC,62,6F,7C,08,24,71,74,F3), + wwH16(52,76,2C,24,20,C4,CF,8C,CA,7B,10,0B,54,BA,15,FC), + wwH16(0B,46,4D,43,03,B8,20,A8,3B,D8,78,15,19,07,D3,7E), + wwH16(84,6F,8C,B8,38,92,5C,28,57,31,3E,FC,8B,53,F4,4F), + wwH16(D5,95,2F,D0,F4,21,DB,8B,04,03,82,40,38,71,3B,36), + wwH16(D2,C5,BA,50,0D,4D,0E,50,C3,39,B9,3C,F9,51,B6,4C), + wwH16(9D,3A,43,A3,56,56,CC,1B,03,D7,8D,2D,8E,E3,6A,78), + wwH16(81,9B,27,5F,E5,14,D3,AA,5A,AF,67,57,33,AE,86,EC), + wwH16(DA,7A,8F,36,EA,C0,06,7B,8C,30,C0,EE,82,43,B7,86), + wwH16(B2,15,84,B9,D1,CE,2A,E9,40,88,3A,99,92,39,18,C8), + wwH16(DB,DC,65,E6,EB,45,DE,EE,75,BB,6E,10,9A,53,01,69), + wwH16(2E,84,FC,12,AE,8C,C2,71,AC,B3,D6,FA,02,63,C9,53), + wwH16(E1,9E,26,C8,AE,A9,D9,2A,B5,F4,9D,51,3C,15,BD,72), + wwH16(68,BB,07,2B,E7,1C,FD,59,A9,32,08,E8,65,31,8D,22), + wwH16(98,6A,5C,88,10,68,BD,75,11,65,1E,89,82,49,D2,FF), + wwH16(8C,13,60,CF,13,15,29,C0,28,DA,F3,68,C9,A1,CF,B7), + wwH16(7C,49,46,9A,E1,64,95,B4,02,10,3C,F1,90,64,D7,2D), + wwH16(A9,E1,F2,CE,4A,B4,46,86,BF,A3,97,29,00,3B,41,8E), + wwH16(82,E2,58,D7,64,EF,95,F8,AC,A2,F5,65,BF,40,E0,A9), + wwH16(2C,C0,47,C7,E5,05,FB,BB,F4,75,45,FD,F8,40,75,21), + wwH16(B2,82,D2,2D,A2,9A,B3,C5,28,25,4E,2D,8F,EE,5C,6F), + wwH16(9F,C9,CF,F8,22,AA,3E,97,11,11,24,EB,52,4F,4A,E2), + wwH16(D2,69,D5,2A,31,B9,1F,1F,B9,82,12,E2,B4,75,33,5E), + wwH16(C8,BF,83,F5,D5,1B,DC,6D,97,39,C5,70,1E,6E,3D,0C), + wwH16(2A,A1,E8,B1,F2,04,E1,AD,4E,25,E9,2A,CA,6D,1D,F5), + wwH16(C9,48,5E,FC,BB,65,90,9F,6E,DC,BD,38,FA,A1,A1,CA), + wwH16(C3,1D,01,8F,4C,9F,1E,94,4F,8F,45,8D,8B,34,B6,4C), + wwH16(14,A4,52,75,D4,9D,C9,B3,53,F5,2F,7F,30,99,F7,95), + wwH16(EB,81,F9,EB,2E,5B,D0,FC,15,88,FF,3A,B1,71,59,90), + wwH16(26,4B,FB,A6,78,6B,47,16,63,9A,C9,73,74,45,C7,2E), + wwH16(04,1B,81,5D,0F,1F,8A,04,6A,4E,52,64,E6,CF,F6,16), + wwH16(26,D2,4F,3B,1B,54,56,EB,FC,4B,63,22,D2,4F,63,CC), + wwH16(DC,C3,CD,94,DC,E1,33,8B,02,03,F5,F2,4E,F7,16,56), + wwH16(21,64,F2,F9,C8,9B,15,EA,50,6E,BB,13,14,9C,8E,D0), + wwH16(CD,90,32,5B,8D,6C,D9,DC,83,ED,8C,46,4F,30,D3,89), + wwH16(03,6A,F0,AE,06,BC,22,76,AE,C0,94,2F,50,6D,93,1B), + wwH16(A5,A8,E2,EE,00,BE,9B,54,AC,FA,95,C8,E2,51,11,0D), + wwH16(4F,6A,B1,3B,72,CC,BA,0E,A9,29,EB,A7,07,D3,6E,EA), + wwH16(8E,7E,B3,30,CA,1A,70,B3,E3,9B,DE,CB,10,FE,A8,62), + wwH16(ED,5F,9E,26,36,40,AF,F8,87,48,C7,4F,50,EC,7C,25), + wwH16(46,15,51,E8,DF,F2,9D,D7,10,C6,BC,F9,75,63,4A,F2), + wwH16(E8,E8,07,FD,28,BF,E0,09,87,C1,E5,09,92,E2,3F,5A), + wwH16(B1,C4,DB,3C,AA,08,E3,CF,3F,0D,DF,26,A6,0A,B5,EB), + wwH16(7D,84,6B,17,B6,53,41,FD,31,92,EB,00,E2,E3,73,1C), + wwH16(85,3D,CB,94,5A,8B,17,9B,BF,B0,29,FC,EF,9C,5B,D5), + wwH16(3B,A2,BE,9C,F4,03,77,A7,78,65,EE,71,07,7C,AE,2D), + wwH16(6E,5D,99,B2,36,73,8B,D3,8A,B0,25,20,EB,71,2A,41), + wwH16(A2,2C,9A,25,40,61,F9,DC,FA,54,E9,AB,AE,BA,BE,C5), + wwH16(74,E8,BB,F2,94,64,FC,34,9B,05,FF,F5,BC,42,ED,91), + wwH16(FC,CA,01,47,C2,7E,47,C6,04,2E,2C,F8,A4,B5,7B,B9), + wwH16(E6,42,1E,13,AD,85,81,52,32,9B,36,2F,66,1F,F7,4F), + wwH16(1F,07,D0,63,98,47,48,A0,F8,B3,06,15,54,C3,8A,F4), + wwH16(86,B2,22,60,E7,5E,1F,DA,3D,60,EA,92,DD,C0,75,1A), + wwH16(45,0B,0B,1D,86,21,49,2F,F5,57,F6,F5,87,31,F8,A9), + wwH16(C8,6E,B0,71,7C,02,D6,C0,D6,BA,BB,0B,FA,9F,C3,8B), + wwH16(8A,3B,66,97,BD,F8,1A,55,E8,6B,56,15,5A,28,20,2E), + wwH16(89,20,D5,28,A6,50,E1,7C,7C,1A,BC,5B,C3,4F,B5,3F), + wwH16(F5,46,61,63,BE,06,16,34,57,C8,A6,B7,42,79,67,C9), + wwH16(33,DD,83,5E,60,29,BE,C4,6E,87,79,68,53,C9,08,1F), + wwH16(E8,2C,E0,3B,0B,97,7C,5E,6D,35,35,39,D4,C4,EB,8D), + wwH16(45,25,DE,A7,3A,EE,56,AF,44,2D,2B,26,D2,E7,24,10), + wwH16(AA,3C,98,E4,82,5A,A5,23,9F,D0,2B,C5,A0,29,41,02), + wwH16(1E,22,F1,BB,15,D2,7A,25,1A,BD,80,CB,FA,BA,05,02), + wwH16(B1,D3,9A,40,B7,3E,7E,49,7F,23,28,4C,A5,4F,C6,94), + wwH16(BA,12,1D,5B,D5,C8,F0,7F,75,FC,A4,1E,5B,B6,97,46), + wwH16(2F,E5,05,17,26,4B,8F,5A,97,28,85,EA,36,77,DF,70), + wwH16(CD,CD,75,77,AD,BC,1B,E3,00,38,D8,0E,60,9E,50,4E), + wwH16(9F,94,88,CD,0D,0E,F1,84,6F,0E,23,C4,5B,CD,4C,D1), + wwH16(47,80,1E,DA,2A,65,9D,20,9C,F5,5D,EE,4C,B2,DB,0E), + wwH16(DA,C7,10,46,A4,E7,95,3F,97,7E,13,74,A7,9B,51,17), + wwH16(0B,E6,63,62,35,FA,D8,BD,4E,8F,37,EA,F1,0E,EB,A4), + wwH16(6E,EE,52,B1,91,63,75,1C,86,C5,CF,D7,B8,86,B6,38), + wwH16(AC,8D,EB,C5,CB,2E,7A,CE,3F,4C,88,E5,74,D4,E2,47), + wwH16(F0,7A,1F,73,0C,37,9A,E0,23,91,F3,E9,2B,4E,9A,91), + wwH16(25,D2,52,27,23,7E,4B,79,69,CE,C1,FC,82,C2,69,76), + wwH16(40,F1,91,F0,19,28,3D,05,03,F3,29,6D,B0,65,59,35), + wwH16(1D,46,AC,34,56,31,59,64,D8,CF,98,11,93,46,91,A3), + wwH16(DF,EA,73,5B,9A,65,71,39,32,6F,84,7E,08,7B,C1,57), + wwH16(10,52,8C,E6,80,FC,6B,98,5C,6B,1F,80,BD,50,22,A5), + wwH16(3B,34,C0,8D,46,9D,BB,90,0E,A8,F3,0B,4A,CC,32,9E), + wwH16(06,49,6B,22,5D,C8,3D,61,51,BF,17,16,94,F1,9F,33), + wwH16(29,02,F6,1D,FE,93,74,D1,3B,F8,BA,85,8F,FE,A1,25), + wwH16(2F,B0,50,AC,8D,FC,80,BF,1A,FF,D4,7B,72,EB,B4,2B), + wwH16(2F,17,7F,76,CB,0C,6F,C7,3A,16,F5,4F,99,54,F9,32), + wwH16(4C,F7,56,CD,DD,30,BB,DB,14,0A,05,5F,F3,0D,B7,59), + wwH16(05,23,9C,92,7E,9D,90,7C,EF,00,E2,1C,5B,C0,F3,34), + wwH16(FE,2F,9F,64,8C,75,EB,7C,1F,86,5F,0F,FC,17,A7,1D), + wwH16(67,CC,42,C6,6C,47,E0,85,10,82,EB,29,B1,AF,C5,BA), + wwH16(A2,5A,D2,62,6F,63,58,EF,96,6E,B8,6B,EC,4A,A1,C0), + wwH16(86,0B,66,57,B0,DE,E6,BD,58,78,4B,C5,D5,6A,24,A0), + wwH16(66,11,71,A6,19,FD,33,39,DF,E8,83,32,EC,D9,E3,CC), + wwH16(B8,2B,74,5C,10,47,4B,52,FD,D8,68,B1,F1,28,8D,0D), + wwH16(ED,07,3F,D8,46,00,3C,E0,15,A8,AA,8D,B9,61,85,E6), + wwH16(35,DB,14,7D,A7,79,A4,36,E1,5E,C0,DB,0F,82,73,35), + wwH16(86,FD,99,4C,9D,DA,DF,41,0F,A9,64,9E,9C,2E,73,41), + wwH16(E3,63,80,E0,B7,EA,CF,07,83,1C,53,BC,44,A4,EE,DF), + wwH16(39,AB,68,2A,A4,63,F1,2D,B8,AF,0B,15,1F,49,6E,01), + wwH16(3E,52,5B,9B,EE,C4,DB,C6,6A,14,97,AD,43,34,0F,AF), + wwH16(1A,31,DC,27,CE,64,20,ED,B6,2F,F3,72,E9,BB,62,4B), + wwH16(75,0D,6A,34,EA,E1,CF,65,F8,3A,E4,13,4B,79,2B,95), + wwH16(EE,FE,CC,C0,65,A1,D9,01,9D,22,AB,25,99,77,10,2F), + wwH16(34,32,C1,D5,B4,70,12,04,D0,6F,B7,6C,39,6E,BF,9A), + wwH16(3C,30,A1,C4,F5,F4,84,5B,54,B1,96,66,DA,79,6E,0D), + wwH16(D3,94,0D,30,79,61,86,19,AF,43,74,BF,65,DF,F4,91), + wwH16(CE,72,85,6E,62,9D,6F,5C,73,2B,DD,EF,31,10,5E,B0), + wwH16(8D,AF,56,04,DD,7F,F0,9E,90,65,4A,88,7D,4C,E9,5B), + wwH16(59,2F,69,8B,42,76,5D,5D,2C,58,BD,4C,1B,80,16,22), + wwH16(09,5C,FB,CF,2D,B4,38,F8,C8,E7,E1,48,D4,7F,4F,39), + wwH16(A4,4C,C6,C8,58,89,A0,E4,84,5E,6F,65,0D,0F,A4,7A), + wwH16(4B,18,57,53,7A,28,23,C9,34,29,F7,FF,77,7E,97,1D), + wwH16(EF,EC,7B,B7,A6,5D,67,50,D5,E4,BE,B9,F9,DA,64,10), + wwH16(E9,A6,30,3B,B3,3F,DB,32,2A,FD,D8,6A,3C,44,DA,6C), + wwH16(41,4D,91,43,82,0A,EC,D2,06,13,0F,6C,9D,5C,BF,B5), + wwH16(CD,DD,75,23,1E,D7,6D,3D,7C,12,FF,EE,29,D0,15,FC), + wwH16(23,EF,8D,42,8B,C1,AC,79,38,C2,80,F5,0D,B8,A5,01), + wwH16(0D,51,73,BB,9D,47,7C,44,4C,70,19,FF,86,B4,91,C1), + wwH16(FE,55,60,C1,33,A9,1D,C3,0A,C0,7F,D7,75,74,C8,CB), + wwH16(35,31,97,01,EB,B5,44,5F,76,3D,1E,51,48,80,70,6C), + wwH16(FB,A2,E4,96,69,50,8A,53,C2,F7,87,2A,DA,0B,67,F0), + wwH16(49,73,79,69,A0,D8,54,90,D2,8A,EE,47,D0,2F,DB,D7), + wwH16(A3,1F,4B,EF,2B,8F,33,06,3A,61,0F,E1,28,1B,05,3F), + wwH16(DE,82,26,82,F5,A2,55,EE,8E,24,C1,FA,1C,48,D4,6B), + wwH16(AC,8F,E4,26,E1,2F,14,20,71,60,DD,B2,2A,56,78,E7), + wwH16(06,F3,D5,D1,B1,61,34,E3,83,38,2F,D6,53,6A,24,5C), + wwH16(7A,0A,BD,E4,31,D5,2F,CC,5F,6B,88,8E,B2,51,F0,C6), + wwH16(7F,1D,55,CE,01,92,8F,5B,A6,50,1F,5C,3F,ED,BA,64), + wwH16(B9,95,DB,6F,88,AC,DA,1C,26,28,39,90,13,4F,3C,BE), + wwH16(29,2A,25,26,29,BA,44,A3,93,F6,64,72,FA,70,25,16), + wwH16(55,F0,3F,1E,96,4B,A6,A7,ED,32,CE,97,07,A5,AA,D9), + wwH16(41,3D,B4,84,04,9B,E5,BD,89,6F,07,36,FB,48,5C,87), + wwH16(0E,22,7E,EA,2E,1A,CA,C4,88,5F,85,F8,BA,C7,72,AE), + wwH16(EC,39,19,F2,8D,16,EA,EB,94,33,B8,11,6A,BE,BF,88), + wwH16(CA,80,3B,95,51,54,34,4B,9A,3F,59,A9,52,30,70,9F), + wwH16(0F,B1,49,E6,DE,BC,C9,60,8D,8E,9F,DD,E1,7A,3A,9D), + wwH16(92,E2,C0,2C,2A,63,8E,C4,6F,E7,9F,F8,2C,53,FF,3A), + wwH16(B9,D8,02,6F,5B,23,89,63,88,D4,36,AB,62,3B,DE,88), + wwH16(29,7B,72,45,6C,F0,17,05,20,FC,8D,3E,FE,AE,31,36), + wwH16(41,6F,B0,6E,F2,3D,CB,22,95,20,E0,05,52,4D,5B,EF), + wwH16(E9,98,BB,BC,E4,A4,21,6B,F0,2C,10,61,7C,5C,B1,1E), + wwH16(01,31,AF,F7,79,58,73,DA,81,0D,D0,4C,80,F9,38,74), + wwH16(06,54,F4,0C,5D,14,CD,8F,FB,4B,DF,B6,14,5A,87,12), + wwH16(2A,1B,44,F2,AB,09,38,D2,D7,1E,3E,6C,7A,05,D5,09), + wwH16(BF,F4,01,AB,B1,24,31,1C,AA,E7,74,D7,51,FF,8D,CD), + wwH16(AD,2E,CE,AC,34,32,65,4D,07,23,3B,EF,AE,70,79,4B), + wwH16(B1,7C,D0,10,03,A6,67,B1,12,03,B2,74,37,F3,09,A9), + wwH16(0B,72,88,33,12,72,9A,8C,32,1F,2C,66,C5,3D,A3,86), + wwH16(B1,15,2B,84,53,19,B2,AA,3A,38,B5,D2,E2,3F,F9,49), + wwH16(99,B5,E4,06,F8,DA,24,C5,C9,E1,5F,A7,F3,CB,A1,C9), + wwH16(E9,10,F8,96,3B,31,02,D9,21,41,4A,C5,F5,00,2A,0B), + wwH16(87,EF,CE,25,6E,0C,B2,22,1A,0A,9B,C0,C8,8C,A5,0F), + wwH16(4D,D2,09,E8,88,6D,9E,9C,11,99,6B,A4,90,8B,B7,E3), + wwH16(A5,AB,1C,7A,4A,E8,60,44,13,24,FE,EC,0E,17,16,43), + wwH16(0A,67,D1,AB,6F,F5,E6,86,5C,BD,F0,24,2C,EE,2E,80), + wwH16(0F,1C,2D,CF,BB,76,23,CA,C3,23,C4,19,F1,26,AE,49), + wwH16(19,BB,31,1A,C1,77,FC,BF,15,5D,25,7B,0D,BA,58,A5), + wwH16(24,E6,2A,ED,F5,4F,0C,A0,2F,81,46,D7,02,C1,8E,41), + wwH16(33,B8,3F,FC,54,25,FB,A9,D3,2F,27,A8,2A,92,0F,15), + wwH16(65,BF,3A,DD,EA,C5,01,3D,9F,89,A9,43,20,98,EE,7C), + wwH16(9A,1B,7E,70,3F,72,39,AC,C1,F5,67,EC,1A,21,7C,39), + wwH16(F3,F3,C2,96,5A,B5,92,7A,23,F5,39,BA,2B,C7,2F,97), + wwH16(57,B5,5B,B2,87,91,A1,F5,BC,57,A6,4B,99,FD,1A,1F), + wwH16(62,67,5C,20,ED,52,CF,87,13,BC,F0,03,21,02,9D,94), + wwH16(47,0C,CA,3E,0C,15,77,2C,DD,FB,93,93,DE,C8,98,C9), + wwH16(D7,84,5F,C5,CF,03,EE,90,73,47,75,1A,3A,AA,B6,46), + wwH16(07,3F,05,84,A0,B6,6C,30,62,B9,7B,36,38,6B,1A,72), + wwH16(38,FC,85,B9,B0,71,87,63,2E,05,59,95,29,47,C8,B8), + wwH16(AD,80,59,87,D1,99,98,BD,D9,B8,AF,32,A7,D8,69,4F), + wwH16(45,90,F0,B7,9E,AD,5C,A8,01,FF,6C,15,79,13,61,5A), + wwH16(4D,01,6C,91,93,6F,9F,12,72,F5,9C,F9,52,47,A7,AC), + wwH16(B7,CB,7E,36,89,A6,A5,F1,35,9C,0B,F7,0A,B6,33,03), + wwH16(C3,4A,C8,AA,D6,92,DB,39,62,34,83,07,73,84,46,05), + wwH16(12,FA,61,4F,EC,84,F9,5B,ED,26,94,3E,C9,0E,EF,62), + wwH16(0B,03,D3,15,E0,2C,CD,74,E6,E1,F8,A2,3A,CA,B3,16), + wwH16(87,83,87,87,4B,0A,7A,8C,5E,41,28,47,8D,87,BF,7B), + wwH16(C7,59,D6,AD,D9,47,33,79,67,18,42,35,AF,0F,88,58), + wwH16(30,7A,A4,C3,B8,71,C1,51,92,8A,6D,9F,FB,F1,68,63), + wwH16(06,2D,2D,59,8E,4F,2D,00,07,E8,5F,6B,F2,8E,AE,CC), + wwH16(71,07,91,D5,DC,2F,8A,32,06,63,88,53,97,65,59,01), + wwH16(86,88,21,3A,C8,BA,1D,57,01,3C,81,FE,F3,A2,A5,97), + wwH16(C6,88,51,AD,32,35,44,38,4A,37,2A,A3,F7,FF,38,41), + wwH16(8C,2F,C3,D9,E4,B0,34,83,58,E7,EC,04,A2,73,51,70), + wwH16(13,33,08,52,EA,08,8C,F4,09,AF,8C,B5,8E,28,5A,A6), + wwH16(77,7F,43,03,6F,FA,88,E3,52,31,B5,50,6C,FC,70,AF), + wwH16(80,7C,93,03,FC,DF,4A,56,80,8A,62,04,97,15,67,98), + wwH16(13,D3,AF,6F,0A,99,D0,98,1D,BB,51,53,56,83,85,56), + wwH16(E1,26,EA,CE,B1,63,BE,24,DC,73,E9,DB,C1,61,8B,96), + wwH16(91,BF,B4,5B,B5,88,6A,F8,70,ED,79,F1,EB,35,96,DB), + wwH16(6B,60,F4,AF,03,1F,9D,65,95,CA,69,3F,E8,29,CD,E2), + wwH16(19,BA,95,CB,52,48,4A,D7,9D,EB,C2,F1,EB,83,20,38), + wwH16(CB,FA,5C,13,0F,77,A9,BA,F7,8E,39,EE,C6,94,27,77), + wwH16(B6,4E,A9,67,33,A0,96,FE,B7,6D,B1,0A,4B,A0,62,3D), + wwH16(B2,02,AC,FC,19,58,6A,D3,48,59,EF,EB,39,6C,3D,17), + wwH16(E8,8F,F3,2B,79,EA,2D,2D,E0,F6,EE,75,B2,39,87,CB), + wwH16(E8,98,DD,0F,5B,BA,73,E8,21,2F,7D,B1,E2,F4,3F,12), + wwH16(08,1E,7E,94,3D,3B,D6,12,2C,0D,E8,21,B2,9A,07,44), + wwH16(24,0A,66,C2,CE,93,27,1B,99,46,CA,DC,EF,00,64,9E), + wwH16(FF,5E,1B,18,0D,84,D9,A1,42,CF,C3,E7,30,3B,0E,3E), + wwH16(4E,12,0C,20,67,2A,EB,CF,9F,E3,B7,E2,A4,64,B9,E6), + wwH16(CC,2B,E4,0F,5C,6C,CC,4E,BA,7A,36,B6,32,5F,DA,F6), + wwH16(26,93,1F,4F,6A,F1,E6,79,24,76,53,11,DD,25,14,2B), + wwH16(59,A7,1B,E9,BF,18,1E,51,1E,3B,EA,78,1C,68,2C,D8), + wwH16(76,50,41,77,A5,2B,C5,7F,CD,F1,04,3D,1F,80,89,25), + wwH16(31,9E,DF,0D,8F,DE,B7,13,61,51,21,6F,43,71,80,25), + wwH16(6E,F5,C5,7F,85,19,C0,6C,4E,F7,94,51,96,DA,09,AE), + wwH16(94,EF,23,82,B7,5C,B0,9D,52,6B,CB,86,36,AA,11,94), + wwH16(CB,5F,0A,0D,CC,9C,DD,A4,3C,7F,81,A6,8D,8C,53,E4), + wwH16(DF,AF,89,69,99,2F,46,5B,71,95,B1,C9,A3,79,B4,F1), + wwH16(B5,C2,7E,39,8C,44,FC,8D,AC,63,C5,D0,9C,21,DD,F0), + wwH16(A5,E0,E1,B4,4A,D7,6F,09,F8,69,04,81,7F,C1,4A,5E), + wwH16(45,13,A9,44,FC,A0,DF,A5,CA,3D,2C,C3,7C,69,15,6A), + wwH16(76,89,C4,FB,4E,37,A2,4E,B4,15,A5,ED,0E,A3,31,7C), + wwH16(D6,8F,8B,7F,D0,DD,46,42,79,7F,74,18,1E,23,84,AA), + wwH16(C8,1D,2B,50,C8,14,60,6A,38,A8,7B,D8,26,1E,3B,A7), + wwH16(79,81,D6,C4,79,0F,CC,25,C4,BA,18,42,DF,68,D5,F1), + wwH16(45,23,B8,F4,9D,BC,72,B6,34,3D,15,FF,04,C2,EA,D0), + wwH16(81,90,49,73,2F,28,45,EF,DE,77,99,E4,32,CA,A5,64), + wwH16(BF,62,AB,2C,80,02,C7,28,58,A6,96,29,D2,58,60,91), + wwH16(E4,95,2D,3A,82,83,A0,B2,9B,C9,35,BA,52,62,A1,AE), + wwH16(44,60,5B,ED,FE,37,41,FE,7A,3E,9F,EA,A4,A0,0E,ED), + wwH16(CD,22,92,75,7C,81,A2,3C,D6,3B,56,B7,8D,CF,68,19), + wwH16(45,78,76,14,2C,3C,95,32,ED,A1,54,21,B3,AD,EC,44), + wwH16(74,40,A8,42,8B,DF,73,3D,EA,6F,FA,12,1D,72,AB,DC), + wwH16(50,EE,C1,04,51,83,42,BB,6D,B4,46,6A,17,0C,FE,5E), + wwH16(5C,6C,41,CF,BC,E3,FF,68,59,0B,66,F1,CD,A2,FB,AF), + wwH16(CD,2E,63,D7,2D,C2,10,D6,DE,FA,B1,F9,BB,FC,9F,63), + wwH16(88,5F,16,2C,36,D3,F4,FF,7C,F3,BF,79,84,72,4D,55), + wwH16(9B,C0,A9,1D,A4,3C,4C,61,EB,A3,72,C5,EA,C9,E1,A5), + wwH16(4D,75,BF,76,59,2C,9C,71,66,5C,A6,89,67,81,3A,73), + wwH16(59,22,63,E3,29,57,BD,45,0D,FA,CB,E1,C4,9B,3E,29), + wwH16(1D,C7,2F,63,97,10,60,E3,20,B4,CD,C4,CB,17,29,08), + wwH16(98,67,B7,99,62,D3,11,F4,89,5D,B6,A1,28,05,7E,44), + wwH16(15,0A,B1,65,9F,E3,2E,42,7F,F5,6A,76,14,D3,B7,F7), + wwH16(29,91,26,30,DE,5D,00,F2,2A,C2,4B,29,0A,AD,4D,5C), + wwH16(C8,06,8A,12,AF,2A,FB,FA,08,A9,10,2F,04,06,E1,7C), + wwH16(AD,53,A3,03,8A,71,44,18,0C,57,C1,8F,09,57,47,DB), + wwH16(BD,B8,87,87,77,F9,8B,D0,7B,16,4A,33,9F,D8,FB,23), + wwH16(70,06,34,85,01,42,6D,40,8E,AC,00,94,99,1C,F6,6F), + wwH16(46,E4,32,15,7B,92,B3,FE,54,E0,AF,BF,25,0B,56,9F), + wwH16(58,8C,17,D1,7A,2E,AE,B0,88,A6,CB,26,72,38,7F,1C), + wwH16(AF,2D,0A,80,5B,63,19,0B,19,97,33,6F,DE,D3,57,65), + wwH16(BC,19,04,06,74,94,3A,58,75,95,60,8E,23,F5,08,04), + wwH16(78,6A,A0,65,1C,79,38,98,1E,4D,49,FE,7F,93,BF,A2), + wwH16(9E,41,C8,0B,33,B6,CD,5A,2D,1D,1E,DE,48,92,7B,AE), + wwH16(5F,2F,03,50,60,FC,B4,21,72,09,2F,BE,ED,10,17,79), + wwH16(CA,01,EC,73,28,89,AF,D6,C8,BC,EC,B3,AD,07,2E,02), + wwH16(D6,C0,C2,43,B8,97,5D,44,85,52,2A,7C,86,D3,10,D4), + wwH16(C4,8D,AF,4C,67,3A,2D,46,A1,02,D8,79,C7,1F,35,C4), + wwH16(1D,F2,FE,AC,4F,D0,E6,B4,07,19,85,4F,B8,D7,21,E3), + wwH16(75,2A,2E,03,5F,7A,E9,6C,FC,2D,90,3C,2C,95,5D,D3), + wwH16(D7,49,B1,24,AF,78,35,A1,E4,1E,4C,5C,5A,39,DA,52), + wwH16(06,7F,13,24,BF,B2,18,A3,9C,90,9B,B5,E8,B4,D4,11), + wwH16(BF,F4,AA,C6,3C,08,80,82,96,77,89,7A,0B,B0,64,44), + wwH16(A2,3B,D5,F2,A1,E9,A2,4C,D1,A4,D9,67,5E,73,33,15), + wwH16(21,7F,8E,4F,63,18,8D,BB,E1,C2,45,10,C1,C1,3C,3A), + wwH16(31,A0,4D,79,F4,6B,DF,8C,AA,C2,12,8A,2D,72,29,D6), + wwH16(F1,D9,34,BF,5C,10,8E,9A,C1,83,75,A1,F9,40,3A,49), + wwH16(DF,A1,64,B0,0B,4A,52,67,0F,4F,1B,1C,A7,41,8D,A8), + wwH16(CD,7E,C4,1D,90,8A,18,4A,4D,B0,15,A9,B7,BD,58,14), + wwH16(98,8A,2F,23,2F,BB,2A,30,24,38,EE,0E,62,96,62,9F), + wwH16(D0,83,F1,B7,A4,19,93,6C,23,B9,7B,8B,E5,12,A1,6B), + wwH16(27,55,91,F5,93,12,79,32,E1,3C,C5,3E,7E,B9,28,64), + wwH16(EF,5F,97,4B,8A,AD,35,90,B1,22,7E,DF,AC,E1,10,08), + wwH16(FD,BF,49,F4,15,F9,F4,E5,4E,4A,69,0F,A3,28,56,CE), + wwH16(6C,81,16,D3,C8,3C,E8,A8,E0,99,71,42,56,27,6A,63), + wwH16(46,73,B7,07,D8,EC,FF,07,C5,51,71,40,2E,B5,B2,99), + wwH16(50,60,25,19,D7,10,20,BC,9A,3A,65,E7,64,68,6C,88), + wwH16(00,42,90,DB,BB,08,7E,3F,25,58,BD,CD,FC,DE,3E,ED), + wwH16(B6,38,5F,8E,DD,80,F7,2B,22,A4,9E,95,74,2E,A6,3B), + wwH16(86,9E,E4,E8,9A,21,DB,06,BA,61,CD,EF,8C,CC,C1,9D), + wwH16(6E,E0,DD,E1,10,AD,0F,D2,6D,D0,5D,E7,19,4D,F7,C7), + wwH16(37,74,80,E4,5F,48,7E,7A,20,A5,B8,E9,B2,66,55,F0), + wwH16(BC,66,A0,B1,06,49,0C,D7,9F,EE,66,E8,66,74,6A,63), + wwH16(46,67,0A,E6,BE,9E,87,41,30,0C,85,09,12,F4,66,9A), + wwH16(11,C4,B4,EC,14,59,6C,C4,A0,7D,97,E4,1B,18,8B,80), + wwH16(A5,E2,2D,00,59,6B,B1,A6,4A,63,75,67,A1,61,91,73), + wwH16(37,9B,BC,9B,C4,1F,EF,6B,E5,3F,53,38,91,1C,8F,2D), + wwH16(FF,76,F2,6F,53,CC,36,C3,D3,E3,E8,25,24,6C,D9,17), + wwH16(84,8C,58,0B,2C,F9,BC,2B,2E,45,06,CE,19,3F,C3,54), + wwH16(1E,66,86,86,83,82,E5,97,B5,11,19,F3,BD,72,3E,0D), + wwH16(04,DE,51,91,D8,7D,6C,40,8E,56,D0,29,EA,A2,DC,A0), + wwH16(60,33,87,02,3E,26,63,58,D0,64,25,D9,B4,40,32,8C), + wwH16(F9,F5,C5,A9,B0,37,41,C4,40,1C,45,45,44,2C,9B,14), + wwH16(2D,CB,1F,A0,62,AD,B8,84,FB,C1,0C,63,DA,6F,83,12), + wwH16(7D,0B,89,79,A5,6D,36,5D,32,9B,F8,9C,9E,6A,78,F6), + wwH16(A8,68,5E,F5,5E,24,AF,B1,CC,E2,19,71,63,CC,47,D6), + wwH16(0C,56,D6,80,46,F3,00,22,DE,01,68,EB,67,A4,DE,16), + wwH16(A3,91,29,CB,23,E4,E2,00,CE,3B,2A,C6,BE,9E,26,BB), + wwH16(C9,C8,32,B6,6A,72,16,56,0E,BC,47,D7,11,8C,6D,E5), + wwH16(48,C3,2B,92,B2,74,24,87,87,B3,F4,20,94,F3,54,E9), + wwH16(20,D1,8C,3B,C7,A9,B1,2F,DA,7F,8D,B2,15,DE,8F,CE), + wwH16(B0,E4,E2,3B,CC,A1,B8,1B,53,32,8D,FA,9D,24,37,34), + wwH16(69,3E,7D,B6,DD,B6,A1,0A,09,39,52,5A,01,1B,74,44), + wwH16(F9,90,62,57,DA,C3,70,EF,E5,DA,87,98,49,3D,13,F6), + wwH16(DF,25,41,A3,EF,A6,66,1A,8F,79,F3,9A,9D,8D,C7,1B), + wwH16(A1,BA,19,D6,A2,F5,60,33,E0,41,04,89,EE,C1,AC,80), + wwH16(66,57,85,67,60,09,9A,03,9D,7C,E6,8B,55,CE,8D,B9), + wwH16(1B,00,EE,4F,17,66,3F,A1,DA,8C,5E,35,7E,5D,EE,2D), + wwH16(9D,E1,5B,F8,CF,AE,7F,67,94,33,5E,8F,0C,BD,A2,70), + wwH16(88,96,3A,E7,59,E7,DB,CA,01,15,E6,71,88,1B,B7,90), + wwH16(F7,B1,F4,98,86,D9,39,CD,19,63,0C,C4,ED,53,E5,D8), + wwH16(4F,EB,C4,DC,94,91,37,17,0C,9E,A6,BA,9C,6E,10,DE), + wwH16(E8,E2,A1,FD,F3,51,BA,83,AE,1E,85,DF,BE,02,AD,22), + wwH16(E5,D3,A7,E1,19,F4,F9,07,1D,63,A7,C8,7A,EE,70,EF), + wwH16(5A,F1,93,B0,1D,1C,30,56,4D,FA,93,C4,8A,8C,07,05), + wwH16(EB,B0,56,40,A0,75,00,D7,8B,47,DD,8B,84,31,E8,C7), + wwH16(B9,D1,CA,78,CF,8E,A2,2D,4A,C8,2D,5C,E8,CA,84,77), + wwH16(07,29,B3,C8,66,B7,19,74,F8,15,C3,6F,91,7C,C5,0C), + wwH16(B4,33,1D,AE,86,24,12,1C,99,4D,C8,E1,D9,6E,D7,32), + wwH16(D1,51,24,9C,A7,06,63,D3,DE,23,28,29,47,08,9F,65), + wwH16(A0,AA,75,91,3B,F2,03,F8,85,FE,A6,B8,31,B5,02,41), + wwH16(AF,D0,1A,22,28,01,BE,17,9F,D6,F4,23,83,14,9F,27), + wwH16(2D,E5,48,7A,6E,20,AB,F0,9E,9F,28,51,E4,94,49,52), + wwH16(C2,56,17,F5,11,5F,94,33,C9,7A,AC,EE,03,FF,B1,1F), + wwH16(6C,3D,80,F8,B0,26,4B,7F,B0,29,5E,94,FF,78,9F,8F), + wwH16(BB,F5,29,75,BB,FE,B4,27,39,08,FA,BE,F8,86,40,7B), + wwH16(89,BC,19,B0,7A,3D,33,EC,AB,24,DC,81,53,D8,27,E5), + wwH16(EF,17,C9,88,2D,E5,8D,BA,80,C2,DC,A1,9C,B5,3C,6B), + wwH16(48,3D,E8,E3,D6,35,1B,F5,86,F6,86,D8,42,C4,92,8D), + wwH16(30,52,E2,4A,F0,50,9C,0A,FC,E4,7D,4E,02,AA,11,9D), + wwH16(1A,DA,48,AA,8F,03,41,F0,1D,57,5E,00,90,BE,E5,B9), + wwH16(90,CE,71,55,14,F1,FF,2B,73,3C,06,1C,86,5D,1F,66), + wwH16(40,4E,E2,12,64,FF,4F,47,BD,A7,BE,9E,B0,A4,E7,13), + wwH16(40,51,56,44,8A,5B,7B,B8,5F,99,83,E0,76,21,DC,5E), + wwH16(31,8D,C7,41,75,B2,B0,99,07,C4,11,29,E8,37,BA,EB), + wwH16(B5,8D,5B,37,A0,A7,81,F5,98,26,E6,AD,67,80,02,10), + wwH16(64,8A,21,EE,3D,B6,F4,90,5F,1A,3C,EB,51,56,C3,B8), + wwH16(9F,26,95,AF,28,2B,13,33,EE,39,77,B8,66,25,F6,48), + wwH16(97,E0,44,0C,16,C4,19,76,3A,9C,53,2E,B2,76,5E,84), + wwH16(2B,B2,0B,01,FD,38,88,8D,A4,04,F9,DC,B4,C3,1B,CD), + wwH16(C2,49,A8,C4,AD,49,2F,39,2D,1A,7F,AC,21,1B,01,3A), + wwH16(80,1D,1A,03,19,5F,9B,1D,FB,5B,8A,C3,4C,78,FE,3A), + wwH16(19,59,1E,BD,29,45,25,F6,E0,14,2F,73,AF,9A,B7,21), + wwH16(53,B5,12,46,59,6B,1D,32,75,78,37,67,D9,27,D9,E1), + wwH16(A1,EC,0D,C3,46,9E,A8,4D,DF,E4,6F,91,56,97,F0,2A), + wwH16(DF,D4,B9,4C,C1,66,2D,09,F8,BF,2F,A2,B6,66,72,26), + wwH16(32,00,81,3C,B6,30,6E,6A,EF,57,89,65,34,63,4A,49), + wwH16(9D,F8,AC,A7,EC,6A,CD,4D,66,18,6D,52,7D,67,61,FA), + wwH16(13,B7,1C,EA,D9,7F,2B,BB,48,58,9A,24,F2,2A,01,EC), + wwH16(18,48,79,B4,84,15,0A,C6,87,1C,6B,D5,07,2A,68,B1), + wwH16(9D,0B,9C,7E,70,8D,F8,F6,8B,3A,0A,26,EC,2F,B2,65), + wwH16(4D,6A,25,D9,34,F7,EB,35,4B,B7,AC,E6,48,47,AF,B8), + wwH16(76,68,14,5B,BC,8F,1A,8B,B2,24,5C,9B,D8,F5,64,E1), + wwH16(A8,49,6C,ED,42,9F,1E,F7,E1,7E,7F,08,A2,83,88,88), + wwH16(11,0F,DD,43,33,21,FC,05,66,B7,E0,E0,1D,A9,4C,F8), + wwH16(42,91,BA,3C,84,C7,37,81,D0,81,41,D6,7E,49,ED,A8), + wwH16(F2,11,3F,0C,5A,D1,2D,E1,E8,9F,F3,15,A3,35,9A,BF), + wwH16(6D,0D,98,9F,C7,D8,65,87,D0,6F,A0,FF,55,84,DF,28), + wwH16(77,2A,14,7B,88,2D,60,F2,D5,10,7F,3E,AD,C7,F4,36), + wwH16(C3,A9,65,18,6E,B3,E7,20,AC,E1,E0,1B,E4,1F,2D,E8), + wwH16(D2,1E,7D,72,71,0B,AC,70,79,37,D5,A3,62,D8,C6,6A), + wwH16(0B,76,B5,6C,AA,B2,E3,62,9D,94,06,2D,A7,13,EA,67), + wwH16(93,32,10,D0,8D,84,7B,F1,A0,A6,1E,B3,8E,9D,35,D7), + wwH16(98,D1,9D,2C,FB,48,EE,DF,77,7C,10,88,D8,87,2D,7D), + wwH16(7A,39,B4,40,FE,11,DB,7D,FC,EF,B0,DF,0F,D3,AC,26), + wwH16(00,0E,8D,19,A3,70,F2,C0,7E,77,7E,66,AD,1D,16,AA), + wwH16(69,9F,84,35,7C,D7,37,7C,86,A1,CD,2B,8A,71,C8,C2), + wwH16(4F,32,D7,58,76,DB,56,E5,0B,7E,A8,19,CC,39,82,16), + wwH16(EB,1F,4D,B1,94,F0,91,E6,E6,0A,CB,A6,26,86,25,08), + wwH16(4B,94,0F,CA,AE,5A,8C,77,E6,88,41,9B,5E,A4,D0,30), + wwH16(02,28,16,A8,7C,2B,AF,4B,A2,05,81,F2,4E,65,59,EA), + wwH16(A5,3C,19,79,69,F1,BF,5D,29,5A,6C,DD,33,F3,44,1F), + wwH16(C9,B0,A2,64,A2,F1,F0,46,A4,4E,40,F7,7A,EC,4F,FB), + wwH16(66,0C,B2,B2,0E,C0,00,56,A1,76,DC,DF,D2,C1,A7,55), + wwH16(BA,05,EA,4E,35,99,D0,7A,FC,28,9A,53,11,A5,21,83), + wwH16(F2,DB,2C,17,B8,C7,38,EC,2B,05,9C,4A,BA,F0,04,54), + wwH16(44,71,FD,72,62,C1,6C,C0,6F,FB,A4,3E,EB,88,39,41), + wwH16(08,5E,D2,37,CE,CE,EF,BE,4E,5B,39,C9,6F,04,D0,40), + wwH16(16,3C,93,40,B1,45,20,60,35,F7,48,A8,E5,56,C7,8F), + wwH16(60,6C,08,9C,45,CE,C3,19,40,88,5E,9B,B8,2D,22,DF), + wwH16(B4,84,43,B3,FC,9C,AD,15,21,1F,13,EF,15,84,9F,28), + wwH16(1C,9D,6C,13,AC,67,35,B5,A2,25,CB,05,A3,AA,F7,CD), + wwH16(9E,5A,30,9E,F2,D9,56,47,61,BC,A8,93,D9,F7,5C,63), + wwH16(1C,61,78,6D,8D,77,14,09,69,EB,32,72,F6,DA,59,11), + wwH16(A0,9D,F4,2B,E8,3B,55,5C,B9,CD,66,C3,6E,32,80,FB), + wwH16(15,46,40,8C,21,5D,FF,33,B2,1B,0E,AF,C2,12,96,FC), + wwH16(E1,D1,AB,2D,6C,81,D7,C3,B1,BC,92,0F,C5,E9,9E,81), + wwH16(5B,D6,E3,03,C8,BD,EB,31,EB,90,A6,A7,C1,E5,74,56), + wwH16(A1,68,29,7C,EA,53,26,2D,F8,8C,92,2E,A7,5D,7E,2E), + wwH16(75,F1,CC,0A,53,AE,EC,8A,3A,9C,F2,A2,29,94,F0,C3), + wwH16(28,FD,AE,91,E8,49,84,94,AB,8E,CF,CB,B2,0F,F7,0F), + wwH16(1B,2B,22,E7,D1,9C,39,89,34,9F,10,D2,BB,EA,7B,F4), + wwH16(E6,C2,A4,ED,48,0C,F9,78,96,76,24,6C,96,08,3B,74), + wwH16(AA,CA,39,FA,ED,01,85,C0,42,64,10,F2,CA,63,37,73), + wwH16(DA,34,BD,E8,F6,FC,D2,E0,47,05,ED,F2,BD,CA,29,AC), + wwH16(C4,92,01,78,53,CC,52,4F,72,0C,85,CF,99,0D,52,01), + wwH16(35,52,04,6B,94,96,FC,7B,5C,71,5B,F3,72,1A,40,12), + wwH16(45,50,0C,06,E3,1B,7A,DB,46,A4,C3,15,C4,0C,DC,F9), + wwH16(7D,18,07,A2,36,25,20,75,75,A2,0E,73,6A,41,1F,22), + wwH16(7D,EE,B5,91,46,52,B5,E2,D0,C2,2A,42,BA,37,E4,1B), + wwH16(D7,B9,73,FA,CC,0E,A1,D7,46,FF,92,5D,BF,5F,8B,C9), + wwH16(26,40,90,3F,27,42,FB,18,83,79,31,B9,88,DD,F6,A7), + wwH16(53,62,66,79,C4,4B,31,84,48,2D,58,57,61,F1,3F,22), + wwH16(9A,E2,C0,53,F6,2E,CF,4F,B0,0C,BF,97,B3,B3,45,ED), + wwH16(BC,66,D5,BA,95,DF,FD,34,92,99,A1,C7,CB,D6,1D,B8), + wwH16(3A,84,17,8B,1F,7B,74,8B,D7,AB,FB,A9,F4,8F,3C,46), + wwH16(A1,D4,70,98,5F,CB,A1,5A,8E,09,F9,E6,81,22,C7,D7), + wwH16(C7,96,47,6F,59,B9,20,79,3B,0C,13,77,B6,59,2C,E3), + wwH16(7B,51,AE,D0,49,93,10,48,61,B4,7B,DA,79,A9,31,B3), + wwH16(0D,68,50,E8,0D,6D,E0,FA,F2,01,03,AE,E1,D4,99,F6), + wwH16(08,F9,E7,5C,B7,1C,DB,C0,23,B9,96,62,05,05,83,F6), + wwH16(52,45,4E,03,D0,F3,6E,C9,BA,BE,77,D2,63,05,E1,F4), + wwH16(27,CC,55,DC,D4,DC,64,84,87,9C,07,E2,90,9A,B8,D0), + wwH16(37,83,D9,E5,B6,BA,39,89,5F,58,4D,12,7B,78,E6,54), + wwH16(86,70,0D,69,22,F9,D9,60,0A,ED,2A,6B,83,21,6C,5E), + wwH16(5C,56,9B,76,66,BA,DA,AE,06,BE,D7,ED,F3,09,44,26), + wwH16(9E,DD,2A,71,83,E8,37,5E,D3,2F,36,CD,6E,E4,1C,AF), + wwH16(86,BF,FB,08,EE,EB,EF,15,C5,D3,AC,98,10,16,76,63), + wwH16(CD,01,C0,5C,49,40,36,46,82,0B,ED,8C,92,48,62,C1), + wwH16(D6,94,26,9C,CD,AF,7C,4F,B5,0D,9E,F8,1C,FD,A0,68), + wwH16(29,23,67,92,95,4D,03,3C,71,4A,F0,C2,CD,B3,30,16), + wwH16(3E,BC,7C,14,3A,C7,32,6B,B1,74,76,0F,6A,F6,F5,F2), + wwH16(CD,CC,76,A3,33,47,88,B3,6B,6E,CB,68,97,11,0A,96), + wwH16(EC,04,EC,DF,C1,D0,44,8A,F3,02,9F,86,BE,8D,78,34), + wwH16(50,67,89,49,95,55,52,DF,CA,D8,4F,E7,79,6E,56,A2), + wwH16(0A,B4,8A,8D,B2,D1,53,87,79,47,18,88,75,D2,CE,AA), + wwH16(FE,4D,E2,69,72,B6,9E,17,B7,4C,93,B6,CB,2E,CB,92), + wwH16(18,AC,B5,0C,83,ED,55,D5,1B,08,79,A5,CA,5F,A5,72), + wwH16(B7,41,71,EE,7E,B9,F3,E8,2A,3C,C8,77,32,CE,55,BD), + wwH16(94,D6,AE,8E,84,1E,29,5C,F1,5D,4F,1F,7A,57,F7,9D), + wwH16(37,44,81,3C,D3,7B,5E,56,16,3D,E9,64,BB,83,0B,7D), + wwH16(74,8D,FB,A9,00,8A,9B,1B,4F,0C,79,D3,51,41,FF,03), + wwH16(B1,98,CD,9C,DF,63,D1,7A,C7,97,CC,DC,13,06,72,5C), + wwH16(50,EA,1A,73,69,AE,4E,0E,FD,90,F4,0E,7A,98,24,B5), + wwH16(46,16,48,1E,42,C2,74,45,1B,DA,D2,64,EC,BC,EB,CD), + wwH16(1F,01,D0,DB,FC,97,B8,18,4D,1C,8D,40,A7,4D,4B,93), + wwH16(C2,95,F4,23,1E,17,94,20,DC,ED,0B,17,7B,D0,24,7E), + wwH16(03,F5,34,F0,E3,79,14,60,4A,3A,42,3D,E6,9B,62,95), + wwH16(66,C5,D9,1A,3F,14,C2,FA,70,2F,47,A4,CA,5F,FA,23), + wwH16(4E,13,82,97,23,04,7C,8A,3D,5E,DB,D4,1A,35,5F,0D), + wwH16(E5,AE,51,93,5F,D0,19,ED,E9,3B,AE,1A,CE,83,82,9E), + wwH16(8F,81,F2,C4,40,2D,5E,68,E1,ED,07,B4,DC,90,6B,88), + wwH16(1D,E0,23,6A,E6,14,C8,1D,D3,D1,A3,44,D4,74,59,63), + wwH16(A1,88,83,37,AB,59,06,4C,A3,4E,1D,D5,E4,5F,53,43), + wwH16(FE,F8,4D,4A,F5,77,49,CD,A0,E6,B3,69,F3,04,4C,FB), + wwH16(FE,52,8C,BB,A2,3D,06,C0,16,B3,07,A6,14,AE,C4,80), + wwH16(55,18,BB,BD,AF,EA,22,E0,32,67,2A,D2,43,C4,DB,20), + wwH16(85,B6,E2,AD,80,9D,6B,1B,AA,46,AE,1E,DC,11,6C,F3), + wwH16(5F,87,98,7E,BA,56,AA,AB,B3,61,4B,37,8E,99,CD,16), + wwH16(F1,7C,77,AB,90,73,8F,05,8E,BE,A0,37,39,50,1A,2A), + wwH16(64,B9,0E,5A,BB,21,A0,1D,7D,54,30,1D,EA,1B,80,3A), + wwH16(A6,B5,E2,00,C0,92,DF,74,53,63,BD,25,87,B8,EE,88), + wwH16(57,7E,2F,21,A3,51,4A,91,3C,66,78,2B,79,3F,F6,3A), + wwH16(AA,9B,75,71,A2,54,E5,BC,91,57,09,E4,C0,28,53,8E), + wwH16(52,CD,56,89,F0,68,21,43,86,BA,42,2B,AC,A6,C8,93), + wwH16(B7,80,D7,53,7F,FB,40,A4,FE,48,40,D2,56,DF,76,FB), + wwH16(3A,F8,2C,41,6E,45,79,56,87,D6,27,EA,33,E8,9A,A9), + wwH16(3D,87,F4,E4,B4,A6,DA,12,BB,FD,4A,2B,8F,D2,EA,3E), + wwH16(DF,12,1D,1F,78,64,45,89,87,B2,24,16,B4,95,96,09), + wwH16(7A,A3,1F,D3,81,83,33,9C,DF,96,78,DA,12,FF,DB,CF), + wwH16(9A,14,5E,60,55,F9,4F,7A,BA,8B,4A,12,DA,53,22,A8), + wwH16(67,F4,FE,FB,94,1F,93,C7,4B,C8,07,F9,DF,13,6F,E6), + wwH16(6E,8E,B7,D9,92,6A,74,7B,3D,94,65,13,27,18,FE,7D), + wwH16(53,E6,8F,77,7C,13,06,61,2B,A9,69,D1,19,52,94,DA), + wwH16(1A,F7,AA,E8,3F,8A,44,21,CB,57,F5,C7,49,3A,8B,E9), + wwH16(50,62,59,C6,75,A5,9D,19,8D,8D,1D,0C,E0,75,2B,01), + wwH16(63,86,6E,CD,95,D8,03,CE,00,C5,F9,16,8D,4B,1B,80), + wwH16(04,81,21,96,4D,F6,C2,94,DF,7C,5E,6A,9F,33,AA,95), + wwH16(59,46,B8,24,7B,88,7E,38,F4,53,C2,0D,CE,1F,BC,2A), + wwH16(50,A3,41,DC,2C,FB,45,60,B5,7F,AE,95,61,D2,DE,2D), + wwH16(70,7E,99,7E,16,E8,09,31,A3,82,97,56,E9,8D,E0,BA), + wwH16(3F,6D,E8,D9,A1,FC,85,08,53,96,27,8F,11,6C,30,3F), + wwH16(E9,02,30,C4,3A,02,0D,60,29,84,9E,50,BB,1A,66,BB), + wwH16(84,EB,67,B2,82,84,0F,01,0F,46,CB,79,62,CB,E0,48), + wwH16(45,3D,2E,0F,80,BF,5E,6B,43,06,F2,55,9E,42,F7,96), + wwH16(EC,79,11,48,2C,05,59,48,32,3B,02,29,A3,14,24,EB), + wwH16(48,20,08,EE,AE,6E,F8,B5,83,97,F9,D7,4F,CC,AF,B1), + wwH16(7E,96,C6,80,70,9B,2E,AF,B5,17,C4,CD,01,50,C4,6A), + wwH16(3A,BA,C1,9E,3E,C6,60,FD,1D,43,A9,E8,4D,F4,AE,2E), + wwH16(00,C1,64,5A,48,17,C3,33,2F,A5,40,DB,6A,F3,24,1D), + wwH16(F0,65,E5,8C,DF,1F,CB,AA,43,64,58,66,33,6A,B6,1C), + wwH16(32,7A,68,EF,9E,86,58,1B,DA,48,64,17,75,60,1C,FD), + wwH16(46,0B,FC,AF,F5,1F,12,40,53,65,76,6A,CD,D6,F0,B2), + wwH16(56,BA,31,16,63,5A,68,E4,62,33,DF,81,A5,14,00,71), + wwH16(65,42,18,1D,3B,6E,AD,6A,48,D3,C4,3D,D1,92,C8,0E), + wwH16(CE,83,B4,9F,D5,1C,72,8D,B2,F8,E3,02,BA,EB,AE,7F), + wwH16(2E,1F,2D,2B,4B,92,E8,20,54,D9,8C,B0,87,62,BB,8B), + wwH16(A4,E4,9C,0F,78,1D,9C,A8,04,A8,1A,8A,82,A6,2B,D9), + wwH16(F9,C0,9F,64,38,B5,A5,45,6D,60,54,20,C1,82,78,D1), + wwH16(38,57,36,95,67,D0,F4,50,C6,03,FE,56,EF,70,DE,5F), + wwH16(13,7D,D8,9C,9A,C9,50,B5,7A,9E,E2,27,06,AD,53,5F), + wwH16(F0,7A,11,38,E8,CA,CE,5A,71,A5,0C,D4,21,45,89,C6), + wwH16(01,D4,F8,44,39,02,E1,93,A6,26,67,9E,CC,2E,11,01), + wwH16(A8,93,05,D3,15,E3,7D,8B,7D,7D,83,E0,05,FA,4D,58), + wwH16(23,80,06,74,A7,9B,5F,69,E8,2A,4B,EA,43,39,A6,E5), + wwH16(A1,DB,B5,4B,93,F6,FE,8D,36,4F,49,9A,B1,CB,F0,B2), + wwH16(EA,26,26,02,28,BE,01,1C,DA,08,3F,4A,2B,FB,88,90), + wwH16(18,E2,A8,07,89,98,1B,4F,06,AB,94,2C,FB,EE,C4,2E), + wwH16(43,C1,6D,E5,08,8A,9E,3B,FE,47,CB,A6,AF,6F,D8,7B), + wwH16(F9,CB,B1,C2,17,52,29,B7,24,C8,1B,76,30,0C,16,B2), + wwH16(09,84,01,C9,B9,1A,5F,97,EF,F4,F3,1D,B6,8A,48,F6), + wwH16(37,97,7A,CD,4B,EA,B5,D9,B7,83,AE,28,B6,87,90,FA), + wwH16(B3,A6,04,96,63,DC,B9,6E,BB,65,6F,2B,AD,11,FF,D4), + wwH16(1A,F3,FE,7A,11,DE,D8,55,1A,85,5F,29,2C,67,55,DB), + wwH16(84,F3,D0,7F,F3,15,B0,83,88,0C,B5,36,3D,33,B3,2A), + wwH16(27,34,52,30,D3,A9,A7,19,94,DE,9B,75,89,F0,4C,69), + wwH16(70,97,11,46,F0,63,E2,B2,99,B4,6C,E8,56,60,03,E5), + wwH16(1D,15,4B,4F,B3,DB,A1,54,5F,FA,73,85,49,B2,EB,90), + wwH16(68,C6,A6,63,18,BB,0C,00,F5,45,28,85,8D,A7,1A,0A), + wwH16(9A,6F,58,60,95,12,51,4A,7D,3E,02,4E,5D,7B,1D,B9), + wwH16(9F,C0,FD,2E,49,EB,FB,77,9B,C6,C8,F4,9F,46,C2,1A), + wwH16(2F,FA,A7,7A,A0,FC,EA,A4,D2,1E,63,91,00,91,D1,18), + wwH16(49,E1,B1,18,FE,AC,4A,2D,46,9A,66,D6,49,0D,3E,FB), + wwH16(BE,8E,BA,A2,A9,47,90,DE,51,02,12,CE,31,44,86,7B), + wwH16(AD,8F,6D,B7,18,AD,70,80,E5,F7,F2,3A,EC,C0,66,DB), + wwH16(94,58,59,2B,9B,E1,BB,97,B8,23,8D,25,46,98,DD,8A), + wwH16(A7,B5,B8,21,DD,57,73,65,0A,72,AA,51,8E,B0,E9,11), + wwH16(B1,13,D2,DE,BB,D8,0E,24,59,2F,E7,1C,AB,F9,7E,94), + wwH16(1D,55,B5,1C,03,9D,D5,5B,DB,38,32,73,9C,CB,27,13), + wwH16(E1,C2,4E,57,71,1C,DD,20,DF,C9,F8,DA,09,8C,75,E5), + wwH16(82,C9,EF,6F,BA,69,AA,25,9D,38,74,E9,4F,B9,1B,7E), + wwH16(7F,45,BA,8B,37,D1,AA,4E,7A,5C,B4,73,7D,6E,A1,F3), + wwH16(B6,C5,E2,54,D3,97,A4,09,DF,09,05,50,8C,44,15,66), + wwH16(88,28,41,5F,62,4A,3E,CB,D4,01,4B,30,D2,82,C7,DC), + wwH16(5D,37,CD,32,24,5E,D2,33,21,74,A7,8D,3F,08,D2,8C), + wwH16(10,66,BB,45,A2,C1,3B,9D,42,62,EF,3B,EA,CA,47,FF), + wwH16(38,96,F0,12,3D,CC,2F,54,8F,A9,98,CC,0F,62,E8,23), + wwH16(4F,BC,77,AD,B0,9B,07,47,8D,B4,71,FE,59,66,43,6F), + wwH16(B4,42,29,D0,A5,8A,FE,7C,B4,E7,7E,58,4B,DC,71,FE), + wwH16(A2,1C,96,82,32,35,10,8B,5D,8B,FD,F4,75,CB,79,FC), + wwH16(DB,44,20,31,80,24,E6,37,C2,DC,CC,21,25,D9,14,2F), + wwH16(46,A5,8C,9C,EC,59,04,83,EE,BA,0F,55,8B,00,F0,5A), + wwH16(3C,2F,D4,E0,4D,94,BB,36,72,FA,EC,F8,0A,2A,78,09), + wwH16(E3,80,2F,F1,3C,F1,76,FF,A5,0F,37,48,DC,C1,C4,FF), + wwH16(7D,EA,1B,62,4F,C2,B0,A6,F7,A1,F8,B9,F6,3E,17,15), + wwH16(42,3E,C9,5C,04,14,15,8C,17,C9,83,D5,63,E5,56,96), + wwH16(1D,9D,F1,D6,A9,82,2D,F1,4F,A3,3E,34,D7,74,DE,29), + wwH16(00,B8,9F,28,E7,1F,92,72,EE,65,61,D2,06,BC,65,74), + wwH16(48,C9,94,49,61,0A,7B,1B,A7,29,B1,F2,84,C6,D6,64), + wwH16(33,A2,A5,75,4D,A1,AA,9E,E1,50,A2,FC,82,5A,36,89), + wwH16(FC,60,BD,AF,96,0A,CD,DD,6B,23,99,49,36,F5,6A,44), + wwH16(E4,64,9B,D1,CF,46,1F,51,EC,33,89,DA,19,1A,47,1E), + wwH16(AF,C3,1A,D9,1C,4C,CA,B2,FE,ED,8C,A9,08,57,B6,D5), + wwH16(51,E7,B3,1D,86,C5,B7,B2,AB,D2,61,24,B6,1B,24,21), + wwH16(94,E7,3B,EE,11,45,57,25,F7,C9,3B,D3,16,4B,E9,73), + wwH16(41,A5,45,76,E5,5E,4A,45,C5,BB,BC,65,AE,57,86,41), + wwH16(12,5C,E3,53,35,BF,89,08,D7,DB,63,0F,21,89,85,5B), + wwH16(76,09,D3,A1,B3,0B,7D,97,4E,58,4B,04,85,2E,26,5A), + wwH16(4D,9D,C7,CC,55,35,A4,8B,34,3B,33,B7,EB,72,CF,EF), + wwH16(79,33,1D,6E,DD,0C,D1,9A,19,F3,91,B4,5C,55,D0,12), + wwH16(B6,75,14,C9,3F,4E,C3,FD,3E,F1,95,4C,D7,5F,A0,B0), + wwH16(E0,E8,A6,B7,35,D1,D0,FA,00,24,4B,02,84,E5,9F,50), + wwH16(95,2F,AC,B5,57,3C,D5,79,A5,DA,15,AB,0B,8F,5B,1A), + wwH16(BB,79,16,F2,96,AC,0C,D2,02,CB,2B,7E,51,5B,8B,2F), + wwH16(79,48,BA,88,06,B6,A4,03,51,D9,37,F7,AF,A7,7B,41), + wwH16(EE,00,32,E2,EF,E6,C1,94,E5,2A,92,C4,6B,B9,AA,B6), + wwH16(73,BB,17,07,4A,9B,C2,F3,3E,DA,83,C7,96,E4,61,FA), + wwH16(42,95,DD,52,0F,03,EF,2A,0A,4F,0E,45,55,0B,A1,D2), + wwH16(73,B7,22,2E,88,85,F6,18,67,5C,5C,0D,73,A4,03,0D), + wwH16(D0,7C,5E,DE,59,4E,FC,20,B1,8B,2D,06,7B,FA,E9,8E), + wwH16(C2,96,D5,3D,96,E4,98,51,02,19,6F,03,D4,47,CD,9D), + wwH16(FC,AB,92,DA,7C,4C,15,B6,08,40,2C,67,2A,B2,23,AE), + wwH16(F7,AB,49,BC,54,1A,44,31,6A,6E,53,04,CC,EC,E8,5B), + wwH16(A6,AA,0F,1D,FB,A9,5B,72,C7,F4,37,D8,97,06,6F,98), + wwH16(E0,6A,1C,A1,FC,D1,F8,97,A0,BE,5F,2C,38,08,1F,60), + wwH16(EA,D8,A8,99,98,E9,0B,2F,B6,AF,12,C7,95,AA,50,EC), + wwH16(FD,50,0E,A4,56,09,9A,1A,29,D2,FF,02,B0,D8,EB,7D), + wwH16(39,19,83,98,CC,0B,EC,71,A0,C4,55,BF,5C,E0,EA,53), + wwH16(9B,74,9F,89,A2,5D,10,CB,65,72,19,96,16,74,A1,A0), + wwH16(F9,32,C2,A8,4E,0A,68,50,07,45,8C,59,7E,AF,FA,71), + wwH16(43,2D,C0,F1,65,3F,6C,9C,4E,4E,20,9B,31,03,99,7A), + wwH16(8B,8C,44,F9,0D,EF,48,99,40,18,FD,3D,9E,BC,67,5D), + wwH16(C9,D1,16,01,BE,04,D4,0E,25,01,9A,66,77,5F,21,10), + wwH16(85,87,A6,21,7D,3A,3C,15,20,69,52,95,E2,37,B8,64), + wwH16(77,E9,3B,8D,16,51,57,07,9A,B5,AF,85,BC,E7,6A,3A), + wwH16(89,2B,49,3E,A0,11,A2,76,19,AD,CF,3C,0E,1B,9C,95), + wwH16(0B,09,54,96,E6,EA,41,75,9F,6B,68,6A,13,C1,61,DD), + wwH16(9C,82,DA,9D,6D,A1,07,7B,B0,58,73,D3,8B,9D,4B,D1), + wwH16(66,EE,1A,6A,B6,A6,5A,1E,D9,E6,37,82,EC,0C,85,10), + wwH16(6E,0F,5E,35,A7,B7,8A,CC,71,97,A8,02,8D,67,FB,60), + wwH16(F2,03,F0,D4,8F,CE,B8,40,1A,CC,E0,9A,13,E0,CC,09), + wwH16(B6,BF,87,D2,B4,A5,86,00,15,11,EF,74,B6,06,36,96), + wwH16(0C,E3,45,EE,06,99,DA,A5,6A,12,B6,15,04,3E,1E,3F), + wwH16(CE,29,E2,9F,10,78,95,0A,23,89,B3,B9,2C,71,6F,DD), + wwH16(00,30,3E,2A,4D,CD,E6,8F,B1,8E,0D,F0,5F,F1,B4,E5), + wwH16(66,9D,7F,2C,6C,8C,E7,69,39,6A,8E,78,07,36,D3,44), + wwH16(4C,43,8F,6A,B0,B0,2F,B7,74,63,E8,6E,49,31,75,6D), + wwH16(25,69,7C,D4,76,09,1C,DD,6A,4C,4C,94,9E,0E,A2,D7), + wwH16(18,98,AE,5A,41,5B,43,31,5A,F1,D0,5C,42,20,CE,5B), + wwH16(85,36,BC,05,76,B0,A9,A9,D2,31,90,DC,4B,D7,6A,71), + wwH16(90,14,F4,D8,29,B9,BD,82,71,5C,EF,26,B0,95,D7,71), + wwH16(73,BE,4F,08,13,CB,67,DC,13,9F,19,05,0F,1C,5A,CA), + wwH16(2E,3F,C3,99,8A,F2,91,FA,35,3D,62,7B,F9,82,4D,FE), + wwH16(95,D6,8C,7C,76,CD,15,8E,D6,B4,52,D3,46,9D,A8,E4), + wwH16(B6,95,3F,23,35,93,53,9D,FE,DD,6E,D8,1B,1F,07,BC), + wwH16(9F,55,C0,96,67,A8,52,22,0D,DE,95,F7,78,6F,CD,F4), + wwH16(3C,BB,2F,32,1D,B8,32,DC,56,F8,EE,5E,F9,26,1E,8D), + wwH16(A0,CF,DE,24,9C,58,07,13,2D,75,B4,08,0B,A1,1D,BE), + wwH16(87,2D,15,EF,EE,8D,DD,5D,BB,41,45,01,90,B3,33,7E), + wwH16(9D,82,C5,1D,93,74,DA,DD,67,C9,20,11,81,A0,7B,2C), + wwH16(2E,AA,04,D5,69,97,35,A8,B4,E5,E7,EA,6E,BC,01,92), + wwH16(2C,A7,82,EF,78,EC,CD,C2,4A,B1,7B,AB,5C,9B,AB,A1), + wwH16(C3,79,50,8A,40,21,46,97,40,90,C5,60,83,F9,0F,B8), + wwH16(04,B7,98,CF,37,F3,F7,B5,99,D2,88,B5,EC,D5,43,A6), + wwH16(E7,4A,5D,E5,84,A1,A5,28,5D,C0,A3,6D,55,73,CF,B0), + wwH16(C4,1C,65,16,85,50,1C,6B,22,67,99,BF,7A,98,5F,61), + wwH16(14,45,45,59,50,62,B8,08,5D,5F,57,FF,A0,AE,2C,51), + wwH16(31,FD,02,EF,ED,2E,28,A5,12,0D,D0,E9,24,BA,2D,6D), + wwH16(CF,2E,D5,0B,6D,A4,0E,BE,08,94,B5,C3,E3,5A,90,86), + wwH16(DD,1D,7C,85,6A,C8,80,BC,FC,82,8E,0E,5B,53,B0,67), + wwH16(ED,03,E8,F8,94,62,47,A6,6A,27,E3,FB,1C,8A,B7,E7), + wwH16(75,7D,8D,42,35,59,AC,59,D1,EA,0A,BC,65,9C,B3,B9), + wwH16(EC,81,15,C7,2E,21,B9,05,C4,F4,9D,AB,B1,E6,FE,E1), + wwH16(9F,E1,2F,D2,12,1C,4E,0D,5A,B8,94,56,12,72,F3,5E), + wwH16(FF,A6,68,4A,BD,3E,40,2D,C0,CC,FD,76,21,0D,30,65), + wwH16(67,D1,2B,40,0B,F3,40,82,C6,53,90,7D,C3,C7,E7,6F), + wwH16(7E,5E,24,56,90,81,4C,41,40,CD,C9,63,90,62,F8,C2), + wwH16(7D,43,34,DA,1F,5A,92,B4,BD,5C,B1,2A,86,F5,FD,68), + wwH16(72,7A,D4,12,6F,B9,61,C1,AA,C4,BD,E7,C2,E8,82,E9), + wwH16(36,D2,BE,20,87,9E,49,1D,38,44,6B,A5,97,6D,DE,9C), + wwH16(84,C9,FE,F3,B8,75,98,9D,60,61,16,D9,A0,A4,B4,5E), + wwH16(5C,0B,30,47,85,F2,C0,3B,66,61,06,69,43,E6,BA,8C), + wwH16(1F,73,E3,F6,BC,DD,F9,9F,84,FF,63,85,A2,8B,92,DC), + wwH16(AB,D9,EB,0F,EE,98,02,26,08,C7,12,28,55,58,17,D7), + wwH16(34,10,24,86,8C,B6,32,EA,4F,A6,AF,40,03,B4,2D,98), + wwH16(FE,A4,6C,FC,83,29,CE,FA,04,28,80,8D,E4,7B,53,79), + wwH16(87,DC,56,8C,8D,12,44,29,98,22,09,A2,D7,87,4D,07), + wwH16(F2,38,D4,69,9D,37,CD,44,7E,E6,2F,6A,7A,FA,1C,77), + wwH16(63,92,EC,98,12,2C,2A,09,38,38,33,CD,2A,6F,EE,85), + wwH16(A3,FF,95,49,D6,FD,C9,AA,B7,C7,08,33,7F,7D,28,44), + wwH16(88,CF,DA,4E,92,37,05,B5,83,E9,EC,12,F9,AB,D0,F6), + wwH16(49,F2,9E,D0,0B,73,02,23,7E,E1,A2,B7,BD,7A,E1,87), + wwH16(9C,72,5A,F8,39,AD,67,B1,A1,70,98,35,54,E4,92,DE), + wwH16(EC,ED,F4,67,E9,5B,2C,A7,67,F4,5D,C4,F9,A0,1D,D9), + wwH16(71,A6,3A,67,FA,63,E8,E4,D4,E2,56,1C,97,1F,2B,65), + wwH16(E7,C4,ED,52,F2,20,EA,FB,64,2F,7F,93,B6,FD,1B,BE), + wwH16(D2,CD,CC,3C,96,2F,07,41,90,00,9E,27,DE,11,F9,DC), + wwH16(B9,5E,73,38,B8,67,FC,A3,04,58,EF,3E,AE,17,75,1C), + wwH16(B6,29,D7,8F,05,5C,C1,82,D3,B1,1B,83,31,D6,52,93), + wwH16(47,07,9D,A5,A9,1D,C3,5A,C4,0C,E0,CB,C4,74,AE,BF), + wwH16(B4,A9,8F,FC,6C,9B,4F,DC,90,D9,36,75,83,4E,AB,D6), + wwH16(A6,6B,5D,C2,F0,F6,C3,56,9E,43,2E,55,19,66,46,21), + wwH16(4E,5E,66,5E,3A,99,C5,26,16,88,0D,5C,34,99,6B,1A), + wwH16(56,27,3D,78,66,89,8D,92,34,B9,0D,05,0E,CD,18,B7), + wwH16(8E,0A,3B,50,7A,D2,11,B6,C1,14,E7,8C,62,C3,8C,BA), + wwH16(38,5F,5A,7E,29,CC,B0,64,3E,4A,5F,33,DC,36,90,63), + wwH16(42,D5,13,3A,2F,64,F3,0E,14,AE,62,D0,8C,F2,20,B1), + wwH16(3D,1F,B4,63,07,B8,B5,97,98,32,26,C3,20,88,AB,0A), + wwH16(48,35,E7,E4,43,71,C7,CA,7B,6A,E2,17,79,43,59,73), + wwH16(17,A1,3F,FD,D9,CE,95,6B,4A,42,0B,AC,FC,1B,C7,9A), + wwH16(54,C7,2D,6E,19,99,F6,4B,C7,4A,75,AF,43,9C,AD,F9), + wwH16(04,F1,85,65,55,B4,CF,C3,9B,96,00,EC,6F,E9,D0,09), + wwH16(3A,43,CA,BB,07,8C,AD,82,A9,C1,AF,D3,1E,75,92,9E), + wwH16(7E,FC,A3,71,A4,01,A8,32,55,B9,3E,35,99,C3,FE,03), + wwH16(D9,33,D2,26,83,CB,23,56,8B,AB,1E,11,E6,46,BF,82), + wwH16(77,D3,C7,53,83,B7,80,E6,43,D6,9A,C4,1D,89,E8,04), + wwH16(86,6D,6D,A1,DD,FB,91,92,DF,9C,7A,84,43,23,66,56), + wwH16(7C,19,44,F7,9B,88,27,0A,C9,0D,03,5B,39,F1,69,44), + wwH16(CA,AE,1E,2A,1D,A3,42,43,0F,65,43,96,9E,0C,CC,E1), + wwH16(ED,2E,20,8E,45,D7,F9,0F,C0,5B,11,B8,C9,34,BB,32), + wwH16(2E,23,66,E8,F5,31,17,D7,7D,1A,BE,13,7D,7F,76,56), + wwH16(39,30,4A,7D,E1,9F,14,A2,C6,DE,67,D2,18,F6,36,20), + wwH16(C7,16,FC,55,11,DE,14,AC,4F,0F,D5,A9,94,13,7A,CF), + wwH16(12,97,5A,55,08,E6,EC,57,92,5E,41,D3,63,F8,13,DB), + wwH16(AF,0C,18,59,26,C9,F6,83,12,F4,34,BD,E9,E7,28,A5), + wwH16(E1,3E,F1,85,C9,AF,9A,27,48,1C,F1,DC,4E,48,9B,6B), + wwH16(74,DD,24,CD,C8,28,79,B0,30,49,6C,FE,D6,F9,A1,F2), + wwH16(B0,26,6C,68,48,8A,81,F7,39,83,D7,CF,42,05,2F,2A), + wwH16(FA,00,C8,69,ED,FE,47,2F,63,42,91,17,0C,D3,DE,AF), + wwH16(81,78,F6,6E,E8,B3,2C,5C,40,5E,7B,72,07,C5,C9,3D), + wwH16(85,84,8C,CF,AF,0A,9E,9E,8F,17,66,98,C6,80,C7,A2), + wwH16(A0,80,0D,D2,42,77,9F,58,90,96,F2,1E,55,BB,DD,23), + wwH16(A6,77,9A,3D,5D,D7,B7,07,9E,7C,CC,4B,A0,50,94,09), + wwH16(3D,A4,8D,D7,94,A8,0C,83,AD,22,56,36,F8,1B,C3,E8), + wwH16(96,CD,72,E9,7F,22,51,BA,83,FF,B0,21,68,E8,38,EA), + wwH16(15,A5,DD,49,2C,F5,D7,A5,35,8B,87,27,E6,0F,5A,B3), + wwH16(78,60,E1,09,F9,B9,DD,27,63,AA,37,65,47,52,24,1A), + wwH16(CF,17,26,BD,A4,9C,F1,87,76,AD,C9,0E,C5,98,46,B8), + wwH16(BB,76,F3,FC,27,CB,C8,60,CF,5E,07,0D,82,B1,E9,06), + wwH16(83,6D,13,E4,D5,2D,5D,78,BE,18,5B,F2,72,24,16,76), + wwH16(B2,63,7A,83,6B,D8,E0,81,4B,23,3C,B9,29,D9,DE,61), + wwH16(B1,15,1A,0A,04,7B,73,F5,3A,7E,39,4A,87,33,86,82), + wwH16(D1,11,F5,5B,1A,D9,B9,E0,68,BC,12,85,A0,0A,65,17), + wwH16(BF,BB,A1,AD,62,8B,B7,A1,E6,FE,DB,97,91,C7,0A,E0), + wwH16(D2,15,BF,13,C5,48,0F,49,95,1F,73,D1,4F,96,B4,93), + wwH16(43,19,D2,34,54,AA,85,C6,69,CB,26,9F,98,CF,C6,29), + wwH16(AD,F4,BA,2C,17,26,F0,28,CF,49,F1,46,0E,42,97,5E), + wwH16(31,51,0B,F0,4E,4F,72,17,42,2F,4D,08,90,AE,A6,C3), + wwH16(42,59,8C,D9,BD,BB,FB,20,33,3D,EA,1A,56,58,BE,E2), + wwH16(53,09,7C,D6,50,D0,5D,FA,65,CD,9D,4D,3A,C5,CC,44), + wwH16(D7,13,2A,B2,87,0B,CD,5E,B5,E0,7D,74,0A,E2,73,62), + wwH16(5E,18,38,69,DA,9A,81,AC,A9,F4,19,E1,7E,2F,85,4E), + wwH16(EA,24,9D,81,A6,0D,33,38,BE,2B,57,F5,D5,3A,05,26), + wwH16(E8,53,48,EC,E1,37,51,BD,50,71,24,6F,49,63,A9,C2), + wwH16(BF,47,D0,8E,81,95,29,39,E8,05,AC,F3,80,B6,80,4C), + wwH16(06,9C,F4,31,44,CB,61,A2,10,21,33,87,32,DF,B9,75), + wwH16(C1,22,F9,8C,A9,E8,95,B7,ED,86,31,D3,D8,4C,A7,0A), + wwH16(9A,88,4E,CF,2E,7A,5F,0A,7C,7C,8C,33,F4,1B,BD,C0), + wwH16(53,32,E7,55,4A,01,A0,68,30,44,A7,CB,22,E6,6D,F8), + wwH16(18,4A,55,1D,96,FF,05,49,C5,BF,7A,8F,08,55,94,78), + wwH16(7D,0C,DB,4C,07,EA,4C,9A,F5,6E,37,B2,78,4C,B6,4B), + wwH16(E0,C8,22,D9,94,41,07,A9,91,B8,6D,CB,8A,5A,BC,00), + wwH16(FD,79,00,09,75,A6,C5,36,FD,E4,F4,B4,15,15,67,13), + wwH16(F1,6E,EB,C8,C7,04,F9,35,4A,3D,19,5C,3E,1A,F1,7A), + wwH16(C0,19,6D,6A,65,F3,0A,29,E9,04,1A,37,0A,DA,68,37), + wwH16(3D,4B,88,41,4F,01,CF,C7,FF,B9,4B,DE,05,30,17,B8), + wwH16(DF,CD,99,77,EF,FB,95,E8,36,CF,B2,4F,92,FB,81,8F), + wwH16(F6,A5,29,96,C2,A4,EC,2B,88,E7,05,CA,40,13,9C,2B), + wwH16(C6,D4,5A,B1,A2,45,E7,3C,FC,65,21,10,03,29,B3,20), + wwH16(E9,FD,FE,29,EC,7E,66,D1,5A,6A,39,D0,3E,E5,4E,41), + wwH16(52,29,52,F4,12,6C,A3,B4,84,F9,95,1C,D2,DC,9E,15), + wwH16(9D,44,D8,7A,1C,B2,98,F5,6F,90,65,C0,84,0C,F3,9C), + wwH16(83,52,73,FA,C9,BB,62,4A,A5,E4,25,19,3D,9E,0D,B2), + wwH16(62,13,12,AD,FE,9A,1F,06,40,B3,8C,47,35,1E,DB,21), + wwH16(F0,EB,28,04,F0,E6,51,8A,45,5A,02,5D,6A,D8,64,61), + wwH16(4B,40,C5,6E,15,65,0E,13,76,03,F4,65,A8,F4,C3,06), + wwH16(6E,71,ED,F4,32,6B,24,D1,80,E4,4C,D7,0F,A9,2C,A6), + wwH16(58,86,79,8B,72,04,26,49,D8,62,63,A4,99,EF,A8,52), + wwH16(6E,C2,88,C9,D7,AD,38,A6,F9,DC,C3,6D,90,5D,D7,0F), + wwH16(82,41,10,65,AA,A9,FA,DA,B3,25,CD,DF,D8,F0,D9,03), + wwH16(BB,73,39,33,2E,D4,54,C7,92,C0,FC,A7,D0,1E,EF,3E), + wwH16(2F,2E,C8,35,09,44,83,DB,2A,59,1B,05,60,E2,FE,58), + wwH16(8E,DC,AD,5E,4B,64,A8,C3,EF,1B,FD,22,4F,49,0E,B3), + wwH16(C4,5B,C5,04,D5,58,81,B2,6E,71,BE,B7,EC,4D,BF,7A), + wwH16(BA,91,42,3E,AF,9C,44,53,FF,BF,C7,33,30,4A,6B,E7), + wwH16(E0,91,E6,DC,F7,B5,E8,74,54,0D,84,71,E4,4F,0E,BC), + wwH16(FC,4D,10,1A,7D,61,EA,E4,FC,7F,6D,10,A9,D4,BF,46), + wwH16(40,2E,DA,8C,D9,72,44,7C,80,C8,DE,5B,6A,B3,53,FF), + wwH16(90,47,68,3B,61,8C,71,77,D9,73,4A,37,91,BE,D4,C7), + wwH16(4B,CC,C4,26,62,D6,95,B7,95,29,D2,A1,32,F0,41,5E), + wwH16(32,11,7A,9A,CC,2B,90,73,6C,3E,42,61,D0,A0,9F,C4), + wwH16(EF,6B,FB,77,61,85,AE,00,D1,69,35,F3,94,EB,A3,57), + wwH16(BF,9D,42,B2,80,E5,5B,22,38,6A,6B,44,97,DA,62,E8), + wwH16(C0,DE,11,80,F5,C1,71,CA,96,75,66,5F,B7,34,8A,24), + wwH16(C6,4C,80,3F,D3,0C,21,34,67,BC,F0,41,37,E4,7C,CC), + wwH16(8B,51,86,74,F9,B5,D6,0C,4E,02,30,E0,DC,EA,AB,89), + wwH16(FF,D0,B6,D7,11,9A,65,E7,3E,FE,BE,C4,EE,3F,E0,6B), + wwH16(6B,12,E0,5D,FA,4F,84,1E,46,EE,FD,9B,4F,40,0D,75), + wwH16(BF,8B,18,C7,AA,63,73,1B,64,3F,16,0F,D1,1E,BF,8B), + wwH16(05,E1,41,DB,F9,0A,FD,27,FC,6A,EE,89,BA,8C,94,88), + wwH16(20,CE,B3,03,1A,2B,6D,BB,84,76,FF,E4,99,75,06,EE), + wwH16(95,9A,9A,73,9D,A4,2E,FA,6B,ED,B7,4D,4A,FA,9A,34), + wwH16(C4,45,70,09,C1,EA,3E,4A,BD,84,A3,36,02,57,51,AE), + wwH16(84,28,8A,CA,A7,F5,29,E0,F1,F8,52,5D,10,D6,26,05), + wwH16(90,AA,A3,F0,50,BD,3E,E3,A1,98,3E,A0,DD,03,0A,43), + wwH16(9F,0A,88,F5,B6,DC,0B,FB,BF,CB,48,CB,A4,34,08,ED), + wwH16(60,B1,F0,1D,D8,E9,2E,A1,4C,D6,4F,CF,D4,3D,28,00), + wwH16(3E,1D,38,05,50,88,FC,C6,B2,AE,B5,D5,1A,FE,66,4D), + wwH16(A1,F6,D9,BE,F3,84,6E,DF,01,77,AA,F3,34,27,B4,76), + wwH16(A8,BD,BA,93,B1,E4,92,20,57,FF,7F,30,C7,34,FD,74), + wwH16(F3,1E,11,29,C7,B1,A8,41,19,A6,BF,0F,78,96,34,FD), + wwH16(7F,E2,A7,3F,C8,9D,52,03,C1,17,E3,78,44,DE,F7,5E), + wwH16(08,3A,BD,A1,5B,F8,B3,2E,FF,1F,C7,77,1F,D7,B8,B0), + wwH16(13,93,3E,CD,00,E7,0A,04,41,10,90,EA,F9,67,5D,A0), + wwH16(69,5D,45,F6,12,95,BF,44,A9,AA,4F,DC,BD,4D,DA,84), + wwH16(93,38,01,3E,75,21,E6,B6,F4,AC,02,55,3B,DC,FA,26), + wwH16(03,46,6B,21,E8,CA,47,F1,1B,C9,B8,B8,B8,BE,F7,F6), + wwH16(76,F0,01,E2,56,DC,85,AC,58,45,E8,8B,AB,CC,F3,8D), + wwH16(96,8F,75,03,2D,BD,A0,16,2E,72,72,48,5E,F3,FF,D6), + wwH16(B3,23,2A,86,4C,56,4A,6C,29,FF,AD,BC,AD,BD,A8,5B), + wwH16(A9,CB,BD,29,1E,A3,C4,E4,2F,94,61,9A,BE,1D,65,4F), + wwH16(B3,EE,78,E5,4E,1F,5A,6F,DF,53,38,46,8E,56,AF,55), + wwH16(D5,3D,D5,9F,68,97,DF,9F,EF,71,63,F3,70,53,88,56), + wwH16(69,61,FE,EA,45,6D,65,88,9C,E3,29,21,11,DF,F8,BD), + wwH16(E2,66,5F,23,18,E3,A7,72,47,E7,09,E6,0C,4A,0A,96), + wwH16(83,3C,B0,65,66,F3,FC,92,D2,4D,EE,AF,51,F8,22,2C), + wwH16(42,32,70,D0,4A,DB,D7,A1,B2,52,59,5F,F5,6F,D5,52), + wwH16(C0,50,51,5C,F8,1D,4C,06,81,18,39,67,8E,F0,2F,A3), + wwH16(F4,59,8E,84,DB,64,F4,46,E6,99,8F,DE,02,07,43,E6), + wwH16(F3,5E,19,B1,76,6D,BA,57,D6,30,2D,92,F5,E4,34,D5), + wwH16(9E,08,D6,0B,28,E2,A9,67,6A,36,F1,9E,2D,37,4C,E4), + wwH16(FF,D3,F7,F3,E9,61,87,3C,19,F9,57,1F,1C,95,DC,4B), + wwH16(84,29,79,A3,C5,84,AB,B3,6E,C0,3F,51,F7,10,4E,DA), + wwH16(B7,F4,0E,FB,1A,47,CE,B1,73,43,D0,D8,D2,16,95,3A), + wwH16(70,78,18,EB,37,06,65,44,45,49,72,A2,99,2B,94,29), + wwH16(94,56,FD,95,3C,30,04,BF,74,45,2F,D0,F6,EA,15,07), + wwH16(A2,9F,2E,5B,53,50,E2,0B,FA,41,6D,53,77,87,88,66), + wwH16(DE,83,8F,DC,9A,6F,4C,9A,82,EF,CD,C3,C8,44,89,75), + wwH16(95,33,92,0F,1F,84,DA,5E,C7,7B,49,01,6B,96,58,7D), + wwH16(41,38,75,74,5C,3B,E3,4A,FE,E8,CC,97,A8,28,AC,78), + wwH16(FF,62,E0,EF,3B,09,A5,B3,64,CE,EF,8B,9F,4A,05,34), + wwH16(89,E3,05,C5,5B,6B,06,E1,D7,97,05,0F,FE,F1,69,AA), + wwH16(46,9E,FC,E8,74,BD,56,71,C5,04,9C,4C,C6,FE,D3,D8), + wwH16(4A,2C,36,F5,83,9B,DD,D3,CE,C9,9A,02,A7,FC,C9,EF), + wwH16(4D,38,B9,04,F8,0D,A1,E5,FE,05,96,74,CE,CA,DD,D9), + wwH16(B5,13,27,48,99,DD,7E,92,9E,00,1B,56,99,20,39,FC), + wwH16(D1,9C,45,67,45,47,A0,AD,44,F9,84,45,D0,E2,AA,7D), + wwH16(12,5B,F0,BE,57,95,C9,C7,BF,AE,54,C3,3B,07,27,CC), + wwH16(17,A4,17,4F,57,0D,F3,89,73,07,F1,E8,76,70,26,9B), + wwH16(68,11,87,C1,F9,F9,29,24,22,0A,57,56,F5,57,4F,FD), + wwH16(F7,B3,9C,DE,CA,CE,93,A3,6C,50,D5,84,6D,B3,D2,88), + wwH16(0B,F0,C3,80,75,0A,35,51,52,DC,7C,77,36,A3,E9,93), + wwH16(94,0D,AE,3B,64,70,8C,87,56,68,4C,F5,35,68,4F,A1), + wwH16(EE,F5,95,F2,F5,BE,A5,B6,D1,AB,91,A3,4B,49,1E,2C), + wwH16(90,AC,AE,A5,AA,CC,FF,81,35,58,54,FD,B4,5E,3D,45), + wwH16(8A,EF,92,4E,6C,1C,FF,21,91,62,DE,8B,DA,22,7C,DE), + wwH16(DD,3B,7B,54,65,83,68,6C,38,06,EB,25,00,C6,04,3C), + wwH16(06,12,BD,C9,E8,B2,13,86,4E,AE,05,AB,B5,10,53,AC), + wwH16(F5,37,86,9D,F0,2D,33,F5,51,31,64,F2,D7,D1,E6,12), + wwH16(0B,8A,94,61,1D,37,B8,8B,8F,6D,5E,89,E6,A0,A5,EE), + wwH16(E1,3D,75,29,BE,16,68,DB,6B,83,49,78,F0,17,25,F5), + wwH16(2D,9A,61,8A,12,CF,DE,C8,4D,BB,20,2D,25,85,D7,F7), + wwH16(34,54,4F,96,DA,2B,97,22,AE,AD,7F,B0,C1,D0,EE,2E), + wwH16(D6,4C,33,76,97,7C,72,18,6B,7A,5E,0A,24,6A,90,D3), + wwH16(A1,DF,36,22,62,94,A6,61,CC,54,07,C3,12,2F,89,BC), + wwH16(58,B7,50,4B,2E,9B,8D,80,34,76,C5,77,95,4C,BA,55), + wwH16(56,ED,B5,1E,5F,0B,1C,66,22,49,45,91,D3,2D,22,DC), + wwH16(E0,CD,06,F0,97,D8,34,EF,6D,3F,97,57,AF,3C,7C,42), + wwH16(A7,A2,85,7B,A9,43,22,0E,A6,C4,77,86,EB,1C,97,9F), + wwH16(D9,0F,05,1A,FC,02,D8,68,C9,AE,10,55,65,14,6E,81), + wwH16(87,80,7F,1A,E9,15,AB,E3,96,8A,FD,C5,CA,5F,B3,41), + wwH16(3F,98,00,9F,93,C0,2A,83,F4,48,E3,2B,AE,64,7B,D6), + wwH16(AE,12,2E,FB,A1,E9,B1,BB,5B,BD,59,A3,05,02,8E,AA), + wwH16(8B,C2,DA,D5,73,2D,E8,38,F0,1E,CB,F7,04,EF,49,76), + wwH16(01,05,81,6B,A6,3E,39,B6,37,E0,9A,85,7E,FD,D6,F1), + wwH16(2A,C5,B1,04,79,61,4D,27,3E,7E,2E,2D,B0,35,B7,7C), + wwH16(04,6B,D3,F3,46,E1,6F,3D,A8,41,38,A0,7C,D6,03,8E), + wwH16(4A,E9,FD,85,A6,6E,00,70,F4,59,F0,5F,89,E4,24,EC), + wwH16(A9,81,FE,25,DB,23,89,B9,D3,C2,B5,88,A7,85,9D,39), + wwH16(7B,A0,CE,5D,37,30,C3,3A,8C,1F,F1,A0,53,1A,DE,5D), + wwH16(17,FC,BB,8A,D5,6A,8F,E6,24,08,DC,4A,93,2E,9F,F4), + wwH16(06,87,14,89,A4,15,5F,6A,86,76,61,05,AB,04,55,8F), + wwH16(24,74,2D,06,2A,2D,5E,AC,B8,3D,09,B8,F7,A0,99,E9), + wwH16(36,F4,E4,82,3A,D5,AD,72,1C,EF,F4,ED,C0,DA,4F,76), + wwH16(F8,0D,33,51,AC,93,31,6B,AA,6A,D3,C3,CF,ED,26,D9), + wwH16(D2,A3,81,DB,B9,02,D4,B1,27,54,84,7F,A3,9A,7D,55), + wwH16(57,61,BC,27,17,ED,AD,F1,44,23,8F,F6,78,78,5B,02), + wwH16(A5,84,E0,96,A4,17,15,C9,63,0F,87,04,BD,29,BE,22), + wwH16(A1,C9,46,82,C6,46,E0,66,35,5D,C1,7B,22,37,17,28), + wwH16(85,D7,B6,CF,16,D5,A9,48,D3,DE,7B,49,47,C9,EC,B0), + wwH16(2C,03,BE,C4,7A,1B,26,FD,82,F6,A0,81,95,22,5A,34), + wwH16(AF,F8,38,2B,3A,15,C2,6B,2B,6D,B5,15,14,77,70,64), + wwH16(C6,C1,FE,C0,45,08,DA,41,D0,B5,CE,E8,90,F5,46,89), + wwH16(32,78,F2,FE,6C,E0,5D,FE,B5,35,3D,89,A4,4D,AF,62), + wwH16(AD,B5,14,D7,C3,31,BC,D6,7A,1E,84,39,BB,57,80,E9), + wwH16(F0,15,E2,F1,82,B7,DE,83,EC,94,9C,C8,6B,BF,26,51), + wwH16(02,24,73,0F,85,E8,47,D0,0F,F0,43,E1,84,D2,11,29), + wwH16(A4,12,88,53,70,01,F2,CA,47,9F,CF,B7,C8,68,03,0F), + wwH16(5D,84,C1,38,4D,AF,43,04,30,40,30,C0,93,91,7F,C6), + wwH16(B6,94,24,D6,03,FA,B2,56,C1,2D,21,B8,9C,4A,5A,13), + wwH16(32,C5,08,75,CD,4C,CA,B7,F6,17,86,85,D8,6F,64,9F), + wwH16(4F,06,F0,BF,95,CC,CC,0B,F5,59,0E,9D,4B,A3,33,25), + wwH16(BE,70,C4,0D,2D,FA,54,5C,07,DC,B2,C0,B8,A4,A8,0D), + wwH16(61,BB,BF,77,41,57,AF,88,B8,8B,1B,58,8D,92,F8,CF), + wwH16(B2,1D,D9,8A,6B,83,1D,0B,91,BE,6A,67,65,03,15,68), + wwH16(1D,D5,F7,5E,84,0A,96,AE,9B,A1,05,95,97,4F,80,1E), + wwH16(F2,4B,26,BE,77,3A,1A,95,6E,01,18,DB,C5,2A,ED,80), + wwH16(22,DA,7B,28,3B,7B,82,86,86,67,AA,B4,FB,7F,AE,C9), + wwH16(40,CF,E2,12,5C,5F,8F,14,B0,D5,93,7E,13,CE,3D,7F), + wwH16(21,10,06,7E,76,74,6D,B6,1A,45,45,9E,56,98,03,94), + wwH16(2C,C3,12,89,29,63,A7,51,C0,BD,D5,4D,37,0A,5A,8D), + wwH16(0F,A7,FF,35,96,40,65,B2,EA,0B,90,A0,75,B6,78,F9), + wwH16(40,0D,47,78,BD,E0,36,C7,1C,F7,07,87,F2,34,4B,30), + wwH16(BD,A1,9E,C5,22,25,AF,01,C3,B1,1C,74,A1,3E,79,61), + wwH16(14,F9,85,8D,15,9F,7E,E1,4A,DF,86,5C,6A,E2,E9,08), + wwH16(B4,2E,9B,9E,A1,B5,F9,90,46,B2,78,49,0F,60,62,E0), + wwH16(EC,6B,B8,E0,9D,A8,94,85,D8,B4,6B,82,13,08,12,49), + wwH16(17,66,DB,DA,8D,54,27,45,2F,27,42,50,58,1F,9C,8F), + wwH16(59,AA,E0,6E,12,61,21,64,CF,D8,F5,D4,7E,1C,47,75), + wwH16(5E,B6,41,06,37,EB,A0,C4,FF,75,8E,BD,10,AF,8D,19), + wwH16(E4,62,FA,12,AB,30,64,6C,A0,4C,C9,AA,FB,1B,8B,5C), + wwH16(09,84,D2,03,66,4D,2B,76,13,91,48,BF,CD,AA,07,56), + wwH16(90,D9,55,31,E4,80,3B,71,B1,AA,8E,11,57,59,C0,67), + wwH16(D7,3E,6E,90,06,C5,9C,57,7D,8C,BB,15,8A,66,40,E0), + wwH16(F6,47,F9,D0,75,E9,CC,03,CE,C8,10,65,47,38,CD,6C), + wwH16(4B,5E,D1,1A,0C,B0,24,52,49,F3,68,69,D3,46,45,55), + wwH16(56,B8,2B,47,50,15,37,2E,87,7E,73,E1,9D,C4,7E,BD), + wwH16(A8,2C,A6,C1,CB,6F,73,5A,11,18,14,B1,EE,4F,18,63), + wwH16(EF,6C,ED,00,B2,D7,6A,12,58,C8,1B,BB,05,59,54,B6), + wwH16(F7,BA,AC,EB,ED,70,5C,51,88,61,A7,BF,90,76,B2,C1), + wwH16(91,C3,4A,E9,DD,8E,29,44,56,DF,9A,0D,C8,95,A7,48), + wwH16(B2,65,D1,60,43,15,B7,16,4C,1F,78,96,F5,20,9F,0B), + wwH16(AE,5E,93,23,35,1E,20,99,22,56,46,5C,BD,3E,F4,88), + wwH16(10,D1,B5,58,CB,6B,F3,C2,F2,03,8A,89,64,0A,28,44), + wwH16(E4,19,8D,61,05,98,82,89,24,4B,F6,17,CE,70,F1,A2), + wwH16(19,BC,84,A0,AD,60,41,8D,7B,3A,55,3E,FE,EB,C1,13), + wwH16(79,FC,8E,9E,ED,E3,87,C8,9A,EC,11,FC,B5,6F,24,7C), + wwH16(43,D6,42,CC,8B,57,DE,DB,30,4A,F2,BC,C0,32,D8,66), + wwH16(E5,F1,F7,6D,5B,F1,1F,15,30,E5,62,D4,4E,30,D0,49), + wwH16(69,CF,90,C5,64,5C,31,04,E3,B1,62,7C,FC,6F,87,A0), + wwH16(FF,45,95,62,70,FB,97,57,0F,28,EE,4E,E8,1C,26,A4), + wwH16(71,3F,03,B5,13,25,B6,8D,8A,7E,70,9B,C0,D7,A2,50), + wwH16(CE,E6,C7,12,6C,93,A8,8A,E2,C5,ED,F0,BF,36,36,03), + wwH16(E9,65,79,18,D7,9C,61,E7,E8,3D,0A,DB,42,6C,6E,2A), + wwH16(32,58,87,62,64,C0,30,7C,94,79,C6,92,D8,62,51,93), + wwH16(AB,BB,E6,00,A6,AE,9E,CB,89,77,7F,D3,2A,B3,7F,E4), + wwH16(84,CA,40,49,F3,4E,DA,C1,D1,E6,5A,66,49,D9,52,0C), + wwH16(01,26,2A,C9,E2,2F,96,A5,6E,2B,C6,6B,BB,75,78,4E), + wwH16(F5,EC,F4,F4,66,7B,E2,D1,02,1C,C1,D9,9C,E7,A7,7B), + wwH16(8C,A4,25,1D,6B,A7,B6,C5,3A,C2,4D,A6,D0,00,36,07), + wwH16(3F,F8,74,0A,D1,87,F6,B1,18,74,54,51,8A,B4,11,F4), + wwH16(36,E7,57,0F,A2,71,AC,9A,92,10,60,40,D5,9E,3C,A6), + wwH16(5B,8F,64,5F,86,ED,6B,2A,5E,7F,A5,8E,8D,2E,43,62), + wwH16(87,7A,2C,E2,E6,15,8C,BC,22,DF,CB,66,F7,32,BD,0A), + wwH16(E8,74,6C,38,FC,43,C6,0C,75,3D,1C,54,97,3C,16,BE), + wwH16(E4,FE,9B,BA,FA,A6,53,0D,F6,A5,2B,B9,75,AE,B4,02), + wwH16(C0,71,8F,4D,17,1D,51,F1,85,B0,F7,D6,E3,DA,FE,6E), + wwH16(C5,B5,A4,3D,57,23,B4,8B,55,50,99,4F,22,DB,FA,C2), + wwH16(C3,FA,21,B9,E4,77,9A,B7,19,81,99,A7,8E,D8,BE,51), + wwH16(E2,1A,29,59,AA,8C,FB,A9,98,CA,6B,70,6D,B7,6D,4F), + wwH16(2D,A8,F4,D2,23,E3,4F,DF,9A,3B,1A,5C,CA,E6,66,80), + wwH16(8F,2C,D0,00,3F,31,91,BB,89,80,F8,EF,6D,E4,9A,F0), + wwH16(31,A5,80,A4,0B,80,56,4A,2A,1C,AB,C7,61,47,6C,E4), + wwH16(66,86,13,31,FE,FA,0D,BD,F4,03,B2,04,2C,C0,94,00), + wwH16(FB,54,EB,8D,24,5D,54,F4,85,1E,9E,BE,19,3A,73,B1), + wwH16(00,F2,CD,71,A8,DE,CC,26,62,12,9F,9A,2E,B6,84,FE), + wwH16(A5,EA,C1,32,F1,5F,E4,56,07,FE,F7,54,A3,78,59,37), + wwH16(49,DD,4F,C7,D3,5F,43,57,2B,BC,A1,71,06,A9,AC,01), + wwH16(03,C9,BE,4B,C2,A7,A8,C3,4A,D9,3D,D3,BC,CD,5C,AD), + wwH16(A6,34,FD,C4,99,E7,C0,ED,80,9D,86,68,4B,9D,96,44), + wwH16(84,A5,80,0C,E8,34,EE,6D,AC,95,50,80,CB,BD,F2,E1), + wwH16(75,2D,02,20,E5,2C,9D,25,2A,01,56,75,51,26,5C,D3), + wwH16(CD,35,35,CC,49,D5,CA,35,CF,FE,D1,4C,E3,4A,6F,D7), + wwH16(52,F0,7F,4B,84,81,38,21,47,A5,B0,2A,12,9D,59,67), + wwH16(3D,21,86,13,8A,F3,8F,A8,B7,FB,31,BB,79,F4,9C,55), + wwH16(AE,82,CF,4C,93,47,E4,20,F6,6D,3A,0D,09,55,A4,BF), + wwH16(D6,51,29,07,20,B1,94,08,34,E4,3C,FA,32,1A,75,98), + wwH16(5A,1F,B1,B3,C9,F7,AF,91,B8,7A,93,E3,FE,30,C8,02), + wwH16(16,AC,7E,46,32,0A,97,14,02,3C,7C,C6,2A,13,01,DF), + wwH16(00,28,4C,EB,FD,8F,E3,7E,4F,D9,FC,DD,51,20,8F,D4), + wwH16(1F,8B,95,53,04,0B,E2,2A,1B,6F,4D,E2,A7,11,20,C5), + wwH16(A1,C7,E9,12,37,EA,25,5C,AD,4F,50,0F,66,3D,3D,AC), + wwH16(D8,8E,6C,2B,C1,A1,DD,1E,DB,50,2F,DC,85,C4,56,D4), + wwH16(21,36,9D,89,78,B3,D7,16,98,2A,80,26,6D,E6,3C,37), + wwH16(C1,7C,52,75,77,08,46,D5,2A,48,09,C3,24,D4,6B,69), + wwH16(75,98,D2,25,1E,D4,D0,E0,D7,6B,D2,E2,4F,21,08,E6), + wwH16(C6,A0,97,12,4A,98,B3,4F,30,96,97,DD,BB,E2,C0,C1), + wwH16(5F,E3,A0,26,A3,0C,3D,4C,F4,38,A0,58,B0,EC,A8,56), + wwH16(65,67,9D,0C,3F,49,21,70,55,E2,10,5A,DF,DD,8C,3F), + wwH16(A1,2D,35,7C,6D,C9,FC,5E,47,BD,B1,FA,25,7A,E7,84), + wwH16(E2,2A,36,10,C4,43,AE,FF,73,DA,62,D3,8B,52,EC,DB), + wwH16(60,4D,76,9C,13,AE,E4,18,2C,80,28,85,8E,10,A0,F0), + wwH16(3D,F7,EC,B1,01,16,0D,38,9E,E4,EB,9E,F2,42,37,63), + wwH16(4D,69,82,1E,58,D8,8B,86,C9,22,D1,9E,F0,6D,34,BC), + wwH16(DC,56,F8,95,1F,55,91,78,A5,FE,2D,F4,0E,94,C1,73), + wwH16(AA,19,F5,51,AF,DB,E8,5F,16,84,D7,8D,0A,1C,61,6E), + wwH16(2A,A8,6E,DE,62,9D,54,34,32,62,88,B1,74,14,C3,BD), + wwH16(2B,02,90,84,6A,4E,37,87,9F,72,CD,39,A1,1F,AE,43), + wwH16(B5,65,C6,A2,55,69,ED,3E,FD,11,08,78,5E,05,8E,16), + wwH16(5E,CC,1F,C5,28,E8,4A,0F,8C,F8,24,B5,43,AF,34,A9), + wwH16(68,C1,5A,6A,37,5D,5C,DA,16,AA,EF,8B,86,7B,EF,F4), + wwH16(6C,E8,5D,42,61,51,74,CE,B1,33,88,9F,BD,B4,3E,F4), + wwH16(2F,78,58,02,21,4E,07,20,67,4F,3B,AC,E7,B4,74,3D), + wwH16(D1,D2,61,B5,31,F4,33,40,95,E7,C7,83,C6,52,DF,A0), + wwH16(FD,03,89,24,C5,58,F3,A3,12,28,77,ED,2D,BC,A9,C1), + wwH16(6D,21,07,38,ED,FB,B5,AD,0F,F8,13,AE,28,DA,1C,7D), + wwH16(0B,C9,A0,DB,EE,6B,B8,5E,43,C9,4E,1A,0A,35,7C,53), + wwH16(F0,76,DC,1F,E7,09,E4,FA,43,78,7F,E4,29,E3,48,B9), + wwH16(B2,5A,7B,2B,01,12,4F,86,8E,0E,88,98,04,8D,07,49), + wwH16(BA,05,38,FE,81,D4,39,F8,6E,2C,75,76,4E,30,7E,CF), + wwH16(96,9E,04,4A,8D,01,D3,04,F7,6C,47,71,EB,11,63,FC), + wwH16(C8,32,2A,34,FD,00,95,53,1A,6A,C0,2D,10,E8,3F,14), + wwH16(DA,F9,1F,B3,13,AE,1B,98,2D,5F,97,FA,0C,0D,4F,F2), + wwH16(BE,5F,9F,88,A0,56,EE,AC,B9,0C,FF,FB,46,AD,98,BF), + wwH16(03,66,9A,D4,5E,97,E7,18,E0,01,B6,B4,22,A4,4A,59), + wwH16(42,FF,5B,66,A6,0C,92,BA,77,3A,B6,86,EF,9D,3F,0E), + wwH16(8D,D0,15,A6,AE,E7,A0,11,35,B8,79,1B,4E,03,92,6A), + wwH16(D4,75,F8,14,D5,4C,C9,AD,D6,2E,FB,0B,C5,DC,77,8D), + wwH16(DB,C8,8C,98,B2,3D,EC,6A,24,31,5B,20,0B,96,6D,05), + wwH16(D2,A1,51,1C,AC,E3,F0,64,6C,3E,B6,39,16,50,49,A9), + wwH16(08,AE,C9,EA,C2,D3,8B,3C,50,9E,38,A0,82,66,6E,C8), + wwH16(D3,08,B5,B2,1C,50,E4,3E,35,6A,8A,EA,8E,76,F6,07), + wwH16(94,23,67,BE,2D,69,D4,CA,DE,9E,FC,EC,CE,16,AB,DB), + wwH16(CF,51,19,22,CB,38,C7,4E,37,11,12,EE,92,9B,DB,39), + wwH16(77,65,EA,69,E4,F2,C8,03,68,61,4A,49,41,23,E3,D1), + wwH16(F0,80,CA,4F,03,F1,AD,DC,EC,25,FE,28,B7,94,01,88), + wwH16(2C,8A,07,A6,74,AE,7F,7F,7C,79,E3,A2,E1,83,69,86), + wwH16(AF,38,67,35,6F,F4,53,30,A8,FE,52,19,25,62,BC,E9), + wwH16(C2,5C,0E,D0,FA,9F,37,0E,8F,45,73,59,17,D7,78,12), + wwH16(96,B9,47,CD,8A,78,2F,79,36,51,01,22,04,85,EF,C7), + wwH16(45,DA,61,FF,EF,40,EE,A4,62,90,F4,50,03,0E,DD,D2), + wwH16(D8,0F,09,7A,F3,50,22,86,90,49,E2,D4,2E,F5,B2,35), + wwH16(E1,AD,D2,12,84,A8,F2,B5,91,FA,30,90,5F,15,52,55), + wwH16(4D,7D,B9,AD,02,FC,DD,57,91,A7,45,58,58,35,9E,5B), + wwH16(6A,04,20,58,1A,46,F1,60,6C,9B,67,83,F0,26,94,9E), + wwH16(1C,C0,21,46,26,FF,D5,E4,2A,65,7B,73,D8,76,DE,90), + wwH16(DB,57,69,7E,05,11,08,08,E6,57,51,A1,2D,7E,84,3F), + wwH16(1F,0F,1E,64,F1,3E,24,CF,62,3F,30,3A,B0,F1,6E,37), + wwH16(C9,D3,69,D8,4A,95,C9,50,A1,07,11,F1,EB,2B,E1,6A), + wwH16(FD,B3,B5,47,23,21,E2,50,60,43,6C,4D,6D,11,E7,07), + wwH16(9A,81,CA,23,29,6A,E5,89,D2,92,49,2D,D8,22,DA,03), + wwH16(88,5C,49,44,EA,E1,55,C8,8D,BC,FB,BE,31,97,CB,88), + wwH16(E1,F6,F5,09,8E,61,84,54,EC,FB,3F,6A,AC,A7,66,13), + wwH16(7D,23,5C,98,27,7A,AC,49,23,D4,28,FA,44,91,DE,AC), + wwH16(31,52,E6,81,B2,26,91,0C,A5,06,79,CF,89,4B,A6,5B), + wwH16(0C,AF,90,08,07,81,40,93,79,F2,68,AF,6B,4F,EA,49), + wwH16(59,75,87,0D,9B,95,45,3A,40,10,4D,17,53,F9,ED,33), + wwH16(E6,B1,93,E6,B1,5F,75,23,70,6A,DE,39,89,24,4B,3C), + wwH16(EE,78,7A,64,F6,E1,3F,17,B5,6B,38,20,00,9B,57,4F), + wwH16(20,D2,4A,B7,72,F3,1D,F4,02,93,E7,67,69,77,D0,C1), + wwH16(3F,8D,59,E2,E0,BA,B6,0D,7E,C9,4A,CD,02,8E,D3,93), + wwH16(80,AF,C8,3B,62,9E,5D,CD,8B,58,02,BE,62,43,A0,40), + wwH16(99,EE,72,1C,21,F1,90,33,DC,AC,64,DE,3B,CA,F1,A9), + wwH16(BD,73,67,1C,1B,DA,E9,3B,27,7C,B8,09,CF,58,7B,49), + wwH16(4D,20,6E,47,FD,DA,54,F8,F2,F8,11,30,38,00,97,69), + wwH16(97,79,F8,69,D8,EA,16,74,37,15,5E,A1,4A,90,2D,53), + wwH16(3B,79,96,9D,CB,41,D8,E8,7E,63,E9,CC,6C,DE,19,D5), + wwH16(E8,64,77,99,A4,C9,73,2C,E9,91,6B,38,2B,F6,AA,B7), + wwH16(05,34,51,C8,4D,28,99,9A,C2,2F,F8,57,3D,9A,5A,D4), + wwH16(91,E9,9D,69,0E,24,D8,45,FF,73,C8,0B,EE,10,4F,EC), + wwH16(D9,49,5A,D7,76,CC,81,79,9A,F0,E1,60,DB,F2,B0,46), + wwH16(7A,96,F7,3F,98,50,14,DF,07,FE,06,0D,FF,A2,D7,EF), + wwH16(D0,80,88,9D,38,68,D8,0C,20,6A,48,D7,B6,7F,F1,42), + wwH16(23,DD,14,45,C8,18,15,0E,3C,9F,8C,74,5C,70,C0,12), + wwH16(43,FF,E9,78,E6,C4,AA,0F,60,B7,9C,E6,3F,FD,7E,E2), + wwH16(B2,50,1B,A5,43,04,E1,B4,8C,34,2E,F1,F5,65,A6,FD), + wwH16(7E,9E,B9,8A,05,7E,EA,9C,DF,32,F9,13,15,0D,0E,68), + wwH16(66,A5,78,C6,7F,22,CA,8B,F9,71,F1,85,5F,DE,42,32), + wwH16(C5,99,F0,D0,B4,14,54,C1,BC,21,C6,8F,FB,39,37,F6), + wwH16(7F,99,54,0D,70,6D,D1,21,F8,7D,96,C1,A5,25,0C,A6), + wwH16(FF,68,7D,BC,F7,7E,9D,D3,D0,EB,E2,97,5E,D5,0E,A3), + wwH16(78,6C,E1,0D,BC,01,20,3D,9D,83,21,04,78,06,D6,70), + wwH16(0D,A6,5B,88,28,EE,CC,54,BB,A3,D8,69,F0,0C,E2,59), + wwH16(42,AA,DF,AD,0A,6C,8A,66,D7,5F,5F,E8,EE,A9,C5,90), + wwH16(15,35,A0,AF,25,D9,88,69,D1,FF,71,40,86,A8,09,EA), + wwH16(2A,72,20,B6,1F,69,7A,AD,70,C9,22,77,D3,B0,D3,0F), + wwH16(25,D6,79,DE,9B,FB,09,D4,ED,FF,93,F2,60,D4,AD,CC), + wwH16(E3,C9,22,B5,C3,B7,F0,5B,57,6C,4C,6C,A4,3A,7E,B4), + wwH16(DC,32,D0,48,2B,12,E6,A6,49,8E,FD,DE,D5,2C,45,06), + wwH16(71,D0,40,15,98,CC,CB,88,EA,83,7A,5C,D0,93,B1,54), + wwH16(6E,62,E8,EA,2B,23,20,28,28,4B,41,FB,F9,9E,33,9C), + wwH16(D7,DE,51,50,ED,79,56,92,6B,6E,54,24,8D,9B,B2,CE), + wwH16(02,54,77,26,31,69,AA,0E,D1,D7,42,FB,5F,45,B7,D9), + wwH16(D6,54,58,29,30,99,F6,81,ED,0A,F8,EE,A8,A0,AD,16), + wwH16(FF,FF,67,69,10,44,CD,DA,5F,92,41,72,AA,0A,C6,24), + wwH16(FC,1E,0B,CD,58,FC,7A,92,F0,B0,8F,C8,04,C6,1C,B7), + wwH16(51,E4,C3,05,FF,26,A2,BD,80,5C,7C,1F,34,14,DC,DF), + wwH16(1F,72,D2,14,32,D1,77,CB,96,F8,39,32,49,EB,F0,8E), + wwH16(C9,3A,C7,90,2B,36,6F,A5,66,E3,21,9C,8C,A0,CE,99), + wwH16(B1,62,BB,2D,E9,AD,70,19,83,B4,46,93,37,DE,F4,5F), + wwH16(FC,34,33,76,0B,01,80,D5,5E,DE,45,B1,4E,DA,11,CB), + wwH16(7F,88,53,52,AB,C1,5E,EB,4B,44,E1,8B,00,CC,3B,C9), + wwH16(CB,CD,2A,90,AA,39,70,6F,B8,91,2C,10,27,7D,F9,84), + wwH16(3F,94,94,F8,8E,94,8D,1D,53,51,CD,F1,BA,C8,EB,67), + wwH16(1D,78,E1,DB,17,22,84,22,5B,AA,A7,0B,8E,40,D0,28), + wwH16(83,1C,CA,67,1E,C7,E8,D0,38,93,83,12,0E,9A,0C,F4), + wwH16(60,ED,50,27,30,BD,CA,93,44,84,4A,C0,A7,99,3B,20), + wwH16(FE,F6,B3,B0,B5,06,38,4C,21,BC,DA,3C,99,00,11,E6), + wwH16(70,74,8C,E7,A9,8F,EA,27,BC,1D,54,00,B9,6A,5B,C7), + wwH16(1A,5F,4C,DF,F6,04,41,B4,BC,14,46,61,48,4D,B7,08), + wwH16(0C,7F,51,27,99,29,D7,41,EB,7C,1E,CE,91,65,65,0E), + wwH16(07,21,C8,70,92,53,A4,B4,FE,81,0A,C7,0B,E2,19,59), + wwH16(EF,19,09,D3,CF,D3,91,F3,81,DF,2B,13,6D,AC,03,93), + wwH16(0F,51,27,2F,D2,18,D4,2F,7B,B3,C1,53,7E,5A,90,4B), + wwH16(74,3B,F0,3E,1E,CC,F3,5A,D6,73,B5,78,C9,C8,CD,B6), + wwH16(17,90,28,B3,9F,A7,23,E1,7F,D3,E9,CA,84,59,1B,2D), + wwH16(CA,70,F9,F6,8F,B8,8E,63,D0,B2,6B,40,B2,0D,4E,89), + wwH16(87,05,3F,DD,6A,BC,F8,ED,C8,8A,39,57,08,3F,CD,19), + wwH16(72,84,AD,81,6A,94,DA,84,49,F5,C8,EA,A5,8E,3A,6A), + wwH16(EF,04,4A,E2,DF,20,8E,56,F9,B0,1F,B9,4A,0A,52,BA), + wwH16(B2,C1,9A,9A,89,3D,7C,C5,CA,86,24,97,3A,DB,26,AF), + wwH16(2C,36,F8,4D,29,8B,BA,9B,B5,72,AA,E6,8D,7F,AF,71), + wwH16(37,94,03,57,F1,3A,65,02,CC,95,CA,0F,D2,35,76,91), + wwH16(44,8F,B1,AE,95,34,49,4F,0F,35,FA,24,32,69,7B,AB), + wwH16(B0,A3,62,2F,B3,ED,F0,1C,1F,DC,C8,CC,D8,B5,A8,94), + wwH16(C4,8C,E6,F3,F9,2F,93,58,A7,0A,30,69,69,B5,9F,53), + wwH16(19,3F,99,DD,FD,9E,D9,CF,FA,17,37,82,FE,59,74,FD), + wwH16(96,CC,3D,8A,29,55,B9,48,5A,39,42,27,34,7A,53,5C), + wwH16(8D,4B,31,B3,8C,01,4B,83,96,51,30,B7,53,5D,E1,D8), + wwH16(A0,6B,4B,50,F3,51,6A,3A,8D,8E,1D,FC,9A,A0,EC,48), + wwH16(6F,DC,AF,81,CB,A5,21,34,45,43,DC,9E,19,5B,28,20), + wwH16(2C,92,EA,8E,70,4E,F4,3D,E4,48,CC,B5,98,BF,33,70), + wwH16(45,19,23,C8,F1,1F,53,73,F3,DF,82,73,DC,C7,EE,8C), + wwH16(88,58,0D,B7,67,1D,B6,8F,0C,E5,4E,3C,F2,F5,34,23), + wwH16(6B,DE,56,2B,53,AE,22,D5,04,96,27,9D,64,A7,9B,92), + wwH16(AB,98,20,A4,0F,07,C9,30,E3,BB,A7,04,01,F0,22,4E), + wwH16(03,D2,FD,27,62,27,D1,5B,2C,A4,02,42,AE,D1,FB,6B), + wwH16(B3,83,9C,75,C6,25,28,22,2D,97,1B,98,1B,57,C2,CB), + wwH16(36,55,AD,32,43,46,53,E1,C1,2D,4C,18,64,37,C6,EC), + wwH16(96,6F,71,E6,B7,16,B2,45,4F,5D,65,A7,28,62,B6,2C), + wwH16(CB,3E,90,C2,DD,2C,D8,CA,86,A4,CC,BB,8F,EE,E0,17), + wwH16(7E,23,40,E3,9B,62,5F,2A,A4,3C,92,16,3C,AA,E5,B5), + wwH16(20,B9,3B,A0,D6,6B,DA,F7,AF,EC,92,9D,AD,1E,CA,35), + wwH16(FE,57,22,AB,A3,B0,06,9A,E7,AA,84,1C,3A,27,CC,46), + wwH16(73,48,67,CD,EC,F1,16,B9,9E,DD,F9,80,C6,29,3A,9C), + wwH16(03,FA,44,1A,3E,4C,5B,1E,96,8B,88,90,6E,B4,B5,CB), + wwH16(45,C8,0E,22,07,B3,1C,0B,F6,28,17,84,DC,EF,8B,32), + wwH16(A5,E2,CA,AC,97,FD,6D,A4,E3,DF,E3,B8,86,35,8B,95), + wwH16(DD,A3,D2,74,B3,E7,CE,A0,94,85,D5,F3,C5,99,0E,90), + wwH16(0D,DB,1C,97,5B,8B,DC,07,9D,5E,45,23,20,EB,95,47), + wwH16(2A,E4,D5,12,EF,AD,06,8E,AC,BB,F8,72,4B,30,3C,CD), + wwH16(C4,D7,24,9E,73,A6,C3,95,DA,05,AB,EE,35,A1,5D,8C), + wwH16(94,EF,FE,CF,3F,0D,F9,9E,31,A6,67,E3,BB,4F,D8,3C), + wwH16(27,C9,89,4F,D5,FB,C1,40,7A,82,AC,98,6D,F2,53,E1), + wwH16(BA,20,FF,9E,FD,F3,95,2E,3F,D3,75,75,24,EA,05,A1), + wwH16(69,24,C9,FC,F2,A9,85,D7,17,E1,07,1F,83,43,A0,D1), + wwH16(DD,2D,32,91,B6,D4,29,8C,7E,8A,68,1D,96,D9,4F,F2), + wwH16(FD,DC,54,34,B0,C6,E2,3F,26,74,75,74,22,DB,34,A1), + wwH16(1C,35,0B,1D,73,9B,DB,01,EF,99,DC,DD,76,F9,8E,C8), + wwH16(FB,80,11,C3,5F,55,4F,64,54,81,49,CE,EB,DB,00,A4), + wwH16(77,B1,09,77,FD,2C,D1,44,36,07,3C,0B,27,7B,F3,40), + wwH16(DF,D5,3E,CA,EE,B0,59,83,7B,FD,46,4D,A3,D1,6B,4E), + wwH16(34,C6,76,6A,F6,97,E2,86,A0,6A,B9,E2,61,E5,0F,96), + wwH16(59,73,9A,B0,C2,1A,4A,A2,BC,B6,64,BF,62,FA,C9,5D), + wwH16(82,D2,59,1C,ED,80,8C,5F,1C,33,BF,85,20,74,FE,75), + wwH16(84,FA,D8,BF,80,DF,E1,D0,6A,76,01,66,0B,F1,09,12), + wwH16(5F,02,9F,86,56,CF,0E,60,F1,1D,9C,14,C4,F9,F0,72), + wwH16(41,20,AA,62,27,97,34,5D,88,9D,D6,69,FF,AF,B3,8E), + wwH16(91,DF,80,8D,94,90,65,CF,D2,1D,A3,0E,5F,E2,8C,5F), + wwH16(8B,6D,BB,1B,B5,E5,29,A6,B8,D1,73,DC,EA,16,38,9B), + wwH16(E9,8E,C6,CF,8C,C2,B1,3C,46,27,98,59,E4,90,94,51), + wwH16(CD,3C,7E,0C,90,EE,C5,2C,2B,77,CE,D0,C5,6D,DF,0A), + wwH16(11,7E,ED,30,81,90,6A,14,9A,D0,D2,79,2A,58,CE,99), + wwH16(A2,51,5D,0B,D0,D3,EF,99,6A,9B,8C,51,77,CA,F5,6A), + wwH16(14,D5,1E,2E,2D,BA,90,7F,C2,C4,9C,89,F2,E9,27,A1), + wwH16(FC,15,82,E3,94,3A,32,47,B7,AE,0B,F0,91,55,0F,53), + wwH16(89,8E,8E,4E,FD,39,1E,44,5B,81,6C,D5,53,FC,2D,49), + wwH16(45,19,F3,1D,37,68,CA,60,C0,71,7A,8F,32,2D,48,4C), + wwH16(20,0C,89,50,AA,69,81,C5,37,FA,31,EE,04,DF,4C,D7), + wwH16(59,58,52,0A,FB,B7,B2,7F,D6,7D,79,26,23,21,B5,BC), + wwH16(90,68,14,A0,55,A6,10,C1,75,40,75,1F,95,E2,C3,06), + wwH16(6C,0F,A5,59,F0,7C,30,8A,11,BE,0B,9B,83,35,88,6E), + wwH16(50,20,98,7B,0D,3F,DE,37,81,93,7B,65,3B,A3,9F,AC), + wwH16(49,03,9D,A1,05,B7,00,31,14,49,52,7C,79,4E,8A,83), + wwH16(DA,59,CE,59,C5,03,EF,1B,3A,D2,DF,E0,93,6B,F7,DE), + wwH16(1A,42,46,DB,01,8B,DB,A4,31,ED,CE,24,13,9E,91,71), + wwH16(A4,6F,43,67,B4,98,90,D5,57,F7,57,38,68,DD,EC,C9), + wwH16(AA,A2,F6,FD,AF,6E,57,05,D4,58,21,7B,F9,C0,F6,93), + wwH16(D5,A2,B7,FD,D4,CF,93,52,0C,30,14,62,39,12,88,77), + wwH16(45,DC,56,AF,D2,20,4A,E4,E0,7C,7F,B6,1E,C8,72,27), + wwH16(FC,89,BE,10,23,50,93,28,D4,A2,5C,FE,63,37,24,02), + wwH16(EB,19,F3,EA,AA,92,BB,DB,AF,5C,79,FB,51,35,48,5C), + wwH16(B6,FC,93,CF,0A,DA,82,7B,DD,FD,10,BE,5D,F2,7F,5D), + wwH16(2F,51,47,94,57,E9,84,54,79,D6,33,B6,9A,DD,77,00), + wwH16(4B,DC,FE,78,3C,76,BE,ED,F1,79,D8,2C,4C,CF,A5,4C), + wwH16(1A,67,BA,C0,88,66,8F,78,0C,FC,3D,BF,7C,A7,2D,95), + wwH16(32,DF,E8,80,7B,F2,D7,D3,5C,51,13,EA,FD,7B,63,FD), + wwH16(BC,55,5C,C9,A9,F7,4A,68,DE,81,B7,4C,C4,F1,EE,FA), + wwH16(79,72,90,13,C9,9E,63,82,25,D5,01,D1,CE,A3,81,B6), + wwH16(CF,08,12,B3,DF,9B,12,FA,4F,17,FA,91,DE,D9,02,43), + wwH16(92,1D,E0,BB,8B,1E,A7,CF,B4,A4,FE,5D,ED,36,17,DC), + wwH16(BB,E8,F2,8D,CF,3D,36,79,47,DA,2D,02,70,C7,56,4F), + wwH16(58,1B,F4,F8,9E,1B,C3,67,13,AC,9C,50,48,88,17,4B), + wwH16(20,70,27,C4,33,B9,2A,EC,C9,E0,C1,79,66,87,07,57), + wwH16(B9,3F,95,6F,5F,32,EB,73,52,E3,ED,F9,5C,13,B6,74), + wwH16(47,A8,FF,9A,7F,8F,A8,EB,77,DC,D2,CC,0B,75,FA,FB), + wwH16(33,CF,58,92,0B,5F,8F,71,6A,9B,EC,DD,07,EA,51,05), + wwH16(F5,8E,D8,96,1F,85,DF,AA,1E,D2,CF,5F,7D,38,9C,A1), + wwH16(48,35,2B,F9,4D,17,95,83,F5,47,19,50,DD,D5,F2,2D), + wwH16(FE,42,98,63,35,40,72,F9,81,7C,AC,AD,73,17,24,44), + wwH16(6A,18,B5,0D,FB,E4,B6,0D,C0,B4,FB,28,DD,32,BC,2E), + wwH16(3C,7C,77,9F,1B,40,FE,FA,24,C4,19,4E,3E,E8,D3,BB), + wwH16(23,A1,6B,25,EE,26,E1,F6,E2,D2,82,8F,EA,9C,AE,21), + wwH16(75,BB,A2,2C,3D,04,07,22,C9,D6,D1,FE,B8,78,ED,B4), + wwH16(69,32,8D,40,EC,03,7B,BA,12,8A,D3,61,2F,8C,C9,92), + wwH16(E9,07,85,F3,9C,A1,C3,36,C9,61,44,76,6F,A9,D8,2A), + wwH16(32,88,2C,94,38,EF,BD,E7,F5,DD,ED,AD,F5,95,31,1E), + wwH16(EB,88,01,CA,64,41,94,C9,6D,78,BD,B9,58,F5,4B,55), + wwH16(18,3D,E1,97,29,DD,E9,EA,96,22,C1,B0,20,9E,7B,17), + wwH16(D5,B7,C9,41,3A,9F,56,7A,39,E0,92,36,FE,E9,2F,D5), + wwH16(15,2A,DD,8C,4F,9E,91,39,80,F2,D0,F3,AE,C5,B9,5C), + wwH16(BD,FB,FD,29,6B,58,E7,4E,48,31,1D,45,39,5C,79,48), + wwH16(9A,4F,C5,14,24,89,7A,57,3A,23,49,B6,C1,8F,09,F4), + wwH16(6D,59,54,1C,C9,F8,21,F7,06,BC,9A,4C,E0,D0,A2,F8), + wwH16(EB,5E,12,A3,8B,3D,89,BC,46,D8,B6,B2,79,D7,DF,9A), + wwH16(60,F4,B6,BC,39,13,21,37,89,3A,C5,B4,5E,04,33,18), + wwH16(D2,81,F8,EB,A5,ED,CC,18,95,78,EA,8C,15,A4,02,9F), + wwH16(48,3D,15,9E,14,06,67,3C,2E,4D,7E,6F,B4,53,B0,3A), + wwH16(BE,5E,54,D8,A1,03,5D,3D,95,65,A4,AB,CC,37,C7,AB), + wwH16(E0,83,2D,AD,85,45,A4,1C,3B,A9,6B,8A,D8,53,F5,A1), + wwH16(AF,E5,97,CA,47,C7,E2,74,4D,89,D6,D0,F0,76,36,BF), + wwH16(40,B1,11,69,00,1C,76,6F,9B,DB,55,DD,7B,96,EE,53), + wwH16(37,11,46,98,E2,B8,73,C3,E1,6A,BE,98,C0,61,77,02), + wwH16(24,DB,AC,0D,EA,D4,9A,C6,B4,41,E1,81,C3,CD,1C,72), + wwH16(C3,4B,B0,A4,AE,06,07,1C,F8,2E,F9,69,13,C0,D2,7E), + wwH16(14,3E,89,B3,8B,FD,45,6D,A2,C8,43,C3,AE,02,F6,17), + wwH16(9B,75,2E,4F,B9,E7,30,57,D1,60,96,FB,27,AB,E8,24), + wwH16(C2,FE,D8,09,DE,5B,2A,CE,DD,6D,1A,38,61,F3,8B,73), + wwH16(23,BC,D0,A8,BD,56,5B,79,4C,55,25,1C,28,27,85,A3), + wwH16(59,DA,93,70,E4,80,6F,25,89,2D,E6,5B,04,77,9E,B1), + wwH16(39,30,91,C9,17,FD,74,E1,CB,31,28,38,7D,DE,EA,44), + wwH16(EB,D9,51,A3,FD,A5,A0,7B,47,EC,54,6C,0B,96,44,1A), + wwH16(92,CE,9F,ED,80,FE,FB,39,88,8D,31,B6,4B,F9,F6,65), + wwH16(C1,00,61,26,AD,C4,F4,91,1F,F9,4D,9F,40,F1,44,2E), + wwH16(46,B8,22,14,7C,7B,94,23,F0,F5,83,C3,16,FC,5B,A2), + wwH16(58,38,60,99,EF,E4,78,BF,6C,A8,98,32,54,73,39,37), + wwH16(04,CE,F2,8B,13,50,34,9D,9B,DE,24,F3,C9,CE,37,B5), + wwH16(1B,12,11,ED,95,E6,16,A5,71,69,94,9B,B1,70,DC,DF), + wwH16(13,F6,7A,E5,60,97,B8,77,8E,0B,FA,45,35,9B,56,4B), + wwH16(38,AF,58,98,D9,D9,4B,D5,6F,E2,41,13,F0,7D,78,35), + wwH16(8E,32,38,A0,5B,96,D1,1E,E6,DD,E3,44,92,48,0D,2C), + wwH16(FF,AA,F3,12,98,D6,2B,23,EA,42,DE,B2,9F,5F,CD,E3), + wwH16(62,3B,5E,07,7C,D0,F5,54,55,B6,18,D4,EF,91,AA,86), + wwH16(16,04,B7,FF,8D,D8,C6,F3,80,85,39,1A,B5,82,16,29), + wwH16(2A,ED,E9,58,52,5C,00,0A,7C,C0,5E,A4,CC,5E,0F,C4), + wwH16(97,03,04,6E,3B,41,1A,68,0D,D4,4A,B8,D0,57,E6,42), + wwH16(1C,3B,BD,1B,6F,FE,04,98,1B,24,FD,2F,C1,B9,84,29), + wwH16(47,2D,E6,B2,29,14,BE,D5,4F,9D,E0,01,70,F1,E4,FA), + wwH16(73,5F,A5,4A,2D,D2,25,7C,6A,23,DE,D6,E4,55,B7,5A), + wwH16(08,33,62,F7,77,B9,CA,69,5F,67,77,A3,FD,0A,C2,70), + wwH16(D1,7C,D8,EA,37,8D,FD,C4,9A,58,1F,55,8A,66,EE,59), + wwH16(FC,D5,A8,25,02,56,79,C8,72,E9,ED,AE,67,53,04,34), + wwH16(F8,75,71,18,2E,F3,12,EC,55,9C,4F,98,69,32,85,54), + wwH16(12,21,05,F9,51,9D,EB,92,F0,43,25,B7,5E,22,9D,6A), + wwH16(23,3A,DD,BD,57,78,9B,54,8D,A3,56,A8,DA,69,1A,CE), + wwH16(1E,2A,AA,A7,6A,8E,06,12,D9,55,BF,F3,C0,17,50,01), + wwH16(5F,AC,0C,CA,1A,DC,E8,1F,B5,39,1F,63,63,79,14,92), + wwH16(D2,4C,5B,38,52,4F,89,AD,0E,E5,FE,0A,AA,F9,58,2B), + wwH16(B7,73,F7,35,6D,9E,A4,A1,93,D9,F1,F8,C3,F1,CA,29), + wwH16(22,8A,40,26,BC,7F,51,96,43,42,67,BF,A0,D4,7C,C1), + wwH16(89,4D,25,25,5F,41,1B,B4,74,10,1A,3E,8C,EB,8B,C4), + wwH16(9E,9D,55,89,98,D3,13,FE,C1,40,E4,C3,DA,DF,8A,72), + wwH16(AE,49,FE,F4,C0,4D,7E,28,C5,C4,11,1D,CC,E6,70,74), + wwH16(73,D8,00,5B,75,83,5C,F4,F4,2C,00,C8,52,C0,8E,54), + wwH16(66,55,9C,03,7A,D1,77,E0,66,96,3D,3B,1D,7F,35,B6), + wwH16(8B,9D,78,6F,E5,F6,7A,DF,A7,5B,01,32,57,E3,73,38), + wwH16(6D,A1,13,95,46,79,86,0D,75,17,43,9D,A3,F1,3A,D6), + wwH16(57,28,ED,15,A4,AB,2E,76,D7,3D,F8,DC,C9,F3,1A,B6), + wwH16(39,BF,88,B8,C6,18,0E,E5,62,89,01,D0,07,4C,BC,25), + wwH16(4E,2F,6C,A6,4D,2B,EA,18,73,07,45,28,45,0B,17,70), + wwH16(AF,8F,AA,F4,67,C1,0B,AA,5C,11,4B,68,E0,52,DF,B7), + wwH16(18,CD,A3,97,AA,58,B7,56,DC,81,DC,17,A7,5D,95,34), + wwH16(43,18,7B,32,4D,AF,C1,91,B2,20,E1,FC,A1,B9,C5,72), + wwH16(94,ED,45,49,F0,71,A3,B1,DC,41,C0,36,E8,71,DA,20), + wwH16(51,AC,D3,D8,33,08,0F,E8,10,CF,40,07,08,A8,10,A1), + wwH16(F7,4E,8F,B4,F8,8D,1C,94,77,7C,21,61,D8,2E,E6,3C), + wwH16(E0,E1,E6,3D,A5,00,D8,29,09,D9,23,40,61,89,49,1C), + wwH16(C9,D7,A6,8D,A8,8A,F0,14,34,EB,F6,B1,BC,3D,F2,4B), + wwH16(27,2C,78,A0,65,BF,B5,20,23,B5,C8,A3,F2,2B,9C,DF), + wwH16(D1,EC,A5,06,D8,43,DF,3F,50,3B,E8,25,9B,15,4A,61), + wwH16(04,C6,48,BD,E2,C6,74,76,08,20,2D,40,14,8F,D5,08), + wwH16(24,71,EC,E5,5C,32,13,F1,CF,E7,D0,B7,54,5C,7B,5D), + wwH16(57,3C,83,6C,7B,BB,24,A4,56,8C,D4,58,D2,9A,4E,D3), + wwH16(A3,42,4E,90,24,2A,70,51,F1,19,82,60,48,16,AB,F4), + wwH16(B6,39,40,6E,0A,A7,6A,23,81,8D,36,CA,27,B3,3D,A0), + wwH16(0E,70,63,FE,89,7C,15,F7,05,82,D8,44,17,3D,BC,75), + wwH16(A9,CF,78,70,95,A5,75,C7,A7,E3,CA,67,EF,6E,93,8F), + wwH16(A5,BC,44,20,9C,DB,87,88,08,A8,75,03,C4,0D,4C,54), + wwH16(95,47,C5,1D,9C,D6,DC,31,EE,2E,AD,A4,8D,BC,02,9D), + wwH16(FF,75,F1,8B,D5,FD,B9,B7,8D,BF,F1,5D,75,E8,52,C2), + wwH16(6B,F6,51,B2,A5,06,83,02,AD,9E,28,31,6F,86,66,E9), + wwH16(AF,CD,40,6F,B6,2B,04,E9,62,06,28,A2,A9,9D,9A,15), + wwH16(BC,BB,2B,5C,1F,31,C9,7F,00,C5,72,78,B6,CA,68,3E), + wwH16(0A,77,E9,B6,BB,3C,0A,01,37,BC,CA,90,24,6B,19,97), + wwH16(D4,F2,73,82,DF,9F,3E,4E,1A,3B,E1,7B,AE,14,40,4F), + wwH16(09,93,49,A5,40,D0,28,23,34,D1,27,57,C0,04,00,F2), + wwH16(93,43,44,29,1D,F0,80,4F,F3,9A,6C,B0,41,B0,01,E1), + wwH16(DD,F5,1B,52,C9,23,C3,A7,31,A5,7B,4E,0F,F2,E9,11), + wwH16(10,40,AC,6E,08,B0,FF,9A,E5,04,FE,5D,CE,03,8E,7F), + wwH16(22,CE,4F,BF,75,4C,60,EF,6B,6F,F6,86,A9,2E,58,0F), + wwH16(F9,B8,9B,DB,21,F1,97,BF,14,05,CB,6C,4B,49,D0,CC), + wwH16(27,3E,74,1A,BA,78,0A,DC,36,8E,63,CE,58,36,88,7A), + wwH16(5C,55,D4,4F,94,1C,18,72,8C,16,60,E4,FE,76,5C,3D), + wwH16(E7,AA,8C,A6,40,87,8C,F1,11,6A,E5,CE,3C,83,52,B8), + wwH16(2D,F1,99,11,57,AE,62,23,9E,2E,3E,C5,4D,10,F4,8D), + wwH16(3D,1B,AD,30,08,D1,D8,32,BE,60,71,44,36,CA,E3,B8), + wwH16(44,C0,67,DB,F7,DB,F7,A3,50,2C,1D,E9,68,B6,E3,B0), + wwH16(4B,E6,89,D7,00,E3,41,73,82,F5,37,96,71,66,E2,F9), + wwH16(C8,7C,29,40,2C,16,C4,B8,70,CB,A4,25,ED,5E,DC,FF), + wwH16(7B,A8,FF,90,D2,B0,CE,8E,44,8B,9A,A8,CF,B1,8A,BD), + wwH16(E4,71,16,48,86,BD,34,7B,EF,3A,13,FB,39,5A,2D,A2), + wwH16(D0,D4,56,25,05,7A,4B,12,A1,97,BA,CF,54,01,F6,85), + wwH16(22,C2,43,85,2B,03,2E,40,4B,D4,C1,77,4A,02,3B,B9), + wwH16(A7,DD,4F,9F,36,5A,B4,D1,49,D2,9C,E3,5F,C9,90,67), + wwH16(3F,BD,CE,BD,44,C3,69,ED,38,3A,07,14,17,5B,93,87), + wwH16(22,E1,86,AB,60,56,2D,28,0E,7E,BD,15,20,FB,B9,D5), + wwH16(BE,DD,20,19,C2,D0,73,E8,BB,D0,89,E2,C6,70,19,C6), + wwH16(9C,4F,3C,0F,61,38,D2,8F,E7,21,43,C4,8C,CE,D2,02), + wwH16(66,95,5B,DC,C3,EB,DF,3D,1B,88,16,E4,7D,CA,F0,8B), + wwH16(09,B7,3A,AA,28,70,BD,C3,A0,BF,52,27,8E,91,8B,4E), + wwH16(FE,C1,0C,8F,82,82,B3,ED,D3,DA,C3,6A,19,30,0A,B3), + wwH16(F3,5D,3A,6F,CE,9E,CD,74,68,CC,F0,0D,82,BA,08,9B), + wwH16(95,10,1A,B0,C5,7E,3E,3F,8F,C6,2A,E0,03,2D,5C,94), + wwH16(18,75,58,0A,A7,B2,72,10,AE,D1,35,26,64,2C,70,D2), + wwH16(02,67,1C,26,F5,A6,EE,59,EC,87,7B,62,93,8A,7B,1B), + wwH16(25,49,57,7F,A4,0B,1E,37,D6,21,F7,7B,D7,4E,77,7E), + wwH16(EF,2F,A2,BA,64,DF,F2,4F,8F,28,32,C4,43,21,C6,D3), + wwH16(9A,9E,FB,F4,E6,91,FA,8E,04,01,6A,A0,42,B9,FD,18), + wwH16(12,32,7D,78,75,C1,79,07,AA,35,8B,74,8B,AD,93,02), + wwH16(DC,79,02,A9,A0,D3,2E,56,3B,96,46,B9,F3,34,24,66), + wwH16(A3,D4,08,E9,7B,4C,3B,DA,5B,76,DF,F6,2D,BF,54,BB), + wwH16(7D,C8,CC,B4,1E,74,A2,78,04,67,F6,E5,72,6B,51,14), + wwH16(32,F4,9B,44,8F,78,98,2A,C1,E9,E3,90,4F,3E,D5,E5), + wwH16(EF,2F,8D,18,41,60,D2,48,DF,CA,C9,9C,BB,8A,DE,56), + wwH16(29,74,4B,35,CC,83,0C,D6,DF,D6,7A,24,1C,91,A3,F4), + wwH16(61,92,62,7E,61,99,1F,9B,CC,D7,12,75,47,92,D3,57), + wwH16(C5,B9,38,88,0B,CC,5F,C1,B4,0B,83,C2,ED,E6,2B,E4), + wwH16(EF,0A,7D,2E,CF,1A,AB,4F,7D,B0,8E,A2,50,19,EE,1E), + wwH16(83,3A,49,1F,9F,E0,46,FA,49,0F,9E,85,95,6A,A7,E2), + wwH16(B6,E8,D7,24,45,7D,92,F1,51,CB,2E,F7,0F,07,C6,96), + wwH16(4E,49,9B,DD,AA,3B,64,7D,93,EA,B1,35,9B,CA,CD,92), + wwH16(EA,04,FA,B1,98,C3,17,AD,4C,38,B7,B9,CF,D9,8C,8C), + wwH16(C1,61,F2,71,54,39,2C,5E,27,95,3D,A5,42,A4,41,0E), + wwH16(9D,A0,F8,B9,D7,66,06,98,59,85,A3,3B,F7,68,43,16), + wwH16(52,18,80,0B,1B,F5,E9,A2,6A,B3,5C,18,05,B7,20,49), + wwH16(64,26,1B,81,10,E9,EC,19,16,50,21,1C,05,F1,7B,80), + wwH16(A0,36,F1,74,92,7E,B2,60,30,1E,D5,0B,1A,C3,5E,43), + wwH16(B6,6D,F0,6B,1D,41,9B,E6,93,EF,70,FD,12,61,83,EE), + wwH16(84,DC,02,96,AE,71,56,CB,79,53,8C,A9,54,31,8B,D3), + wwH16(E6,BA,CE,7B,F0,14,74,73,5E,E8,B3,0E,52,66,1D,BF), + wwH16(81,0C,40,A6,E5,B9,34,BD,64,4B,E3,A0,FD,C5,0B,0B), + wwH16(74,E0,8E,67,20,5F,C7,70,36,58,2F,32,E4,9C,97,88), + wwH16(DB,6C,C9,1D,F3,D5,D4,DA,D0,96,53,A2,7E,47,FA,FD), + wwH16(AB,A4,E7,DC,84,B6,72,2A,2A,93,DA,BA,18,9E,2F,9E), + wwH16(58,C8,EC,4F,8E,A3,1F,17,AC,57,D1,FD,97,43,1D,1A), + wwH16(1E,D7,37,A3,38,92,D4,C0,50,1C,9A,12,D9,57,6D,24), + wwH16(CC,E6,4A,89,C1,37,B7,09,49,C5,EB,55,72,B0,0C,F3), + wwH16(88,59,3E,7B,6C,C6,3D,CD,71,8D,00,BC,B3,85,AA,AF), + wwH16(9F,20,FC,E5,8D,C7,41,1B,91,76,B6,F4,81,0D,4E,31), + wwH16(F4,FC,D6,33,34,C6,11,49,77,6D,D7,2A,26,7E,0A,A4), + wwH16(9D,73,FD,0B,01,9B,BF,3B,46,D6,78,AE,A6,4E,3E,A2), + wwH16(DC,AE,D1,9F,C8,DF,39,36,7C,92,0F,BE,DE,D9,A2,1E), + wwH16(D4,03,6E,C7,E3,16,2D,29,DA,F4,7A,9B,7C,C4,20,69), + wwH16(55,B0,29,6A,42,90,F2,B7,1D,FD,48,DA,15,84,65,9D), + wwH16(D1,37,8E,0A,69,3C,9B,09,60,B2,B3,9D,74,1F,6A,3E), + wwH16(F1,D4,1A,7B,71,3F,A6,EC,FF,6F,D3,EA,21,4B,17,76), + wwH16(7F,1F,2E,EB,5B,64,65,29,E9,05,2A,28,7A,DB,78,66), + wwH16(0B,C7,FC,2B,3D,E6,A6,F7,8D,07,75,B8,52,93,F3,52), + wwH16(4E,FE,17,FD,D1,45,07,B3,71,D5,B9,6C,9F,48,97,99), + wwH16(CA,5B,DA,E2,2B,25,04,8F,15,BD,75,10,FF,02,1C,35), + wwH16(4E,18,BE,DE,48,BE,7B,85,A1,56,9E,7B,41,8D,C9,CC), + wwH16(68,01,D6,4E,EE,04,FC,FA,C1,40,85,64,A3,B8,D7,35), + wwH16(45,FC,E9,4F,27,2B,0D,ED,6B,9D,CC,85,D4,01,5A,E5), + wwH16(D4,F8,14,C5,8F,30,7A,C9,5C,DB,75,E9,6A,41,3C,77), + wwH16(C2,E9,AD,43,2D,05,B7,23,DF,3A,89,14,D3,9F,D6,9D), + wwH16(96,53,F2,8D,70,13,07,E7,50,BE,F6,19,6B,45,BF,3F), + wwH16(38,A3,A1,CE,43,F8,F8,1C,53,47,2A,C3,43,B1,AF,35), + wwH16(69,7F,FB,CA,E9,8A,C5,30,C0,D6,44,FC,0B,BA,0F,BA), + wwH16(E2,C5,48,28,06,E4,96,4B,70,3B,12,3E,7A,71,D1,15), + wwH16(C5,82,E5,EB,9F,90,0B,82,B7,B1,6B,F9,CB,2C,6B,2D), + wwH16(2B,C9,84,CD,21,D2,FD,47,A7,14,63,BA,69,81,BE,FA), + wwH16(77,C3,EC,65,3C,35,75,E5,91,A4,1F,98,BF,FF,B0,83), + wwH16(B0,E6,B1,12,2C,7A,88,83,5D,8F,73,D9,AC,E7,29,9F), + wwH16(F1,48,3E,4F,F1,B3,1D,58,7D,79,F1,8D,7D,70,6B,6B), + wwH16(A6,24,B1,97,64,52,53,D1,35,47,18,47,DE,41,C4,0A), + wwH16(56,D5,3A,30,2D,FE,4A,27,83,5C,F7,4F,60,E0,D6,A7), + wwH16(73,31,AC,9A,84,2B,02,B4,2F,BA,3A,AF,C0,14,0E,2A), + wwH16(11,4D,9F,E7,C0,AF,C8,63,E8,3F,6C,F6,F3,B7,DB,D0), + wwH16(09,93,F5,42,B5,CF,98,88,21,3E,99,50,7F,3A,AC,C8), + wwH16(64,43,98,8E,C1,B5,77,59,C1,31,13,8C,AF,C7,0D,5A), + wwH16(DB,84,88,24,EC,A0,50,90,75,DD,6B,5C,92,3D,89,8F), + wwH16(08,43,63,43,BC,B7,21,58,83,AA,4D,51,92,3C,01,20), + wwH16(CA,67,8A,DF,F3,A0,63,FD,F0,DD,AD,13,A2,43,B8,FA), + wwH16(EE,44,5A,FA,C4,BF,5C,06,7A,5B,BA,BF,37,C8,A2,09), + wwH16(ED,7C,73,54,16,A5,F2,DD,B9,AB,71,22,3B,6D,99,E5), + wwH16(3D,B1,A9,06,88,E6,AA,4F,1B,C1,93,C4,E2,54,C4,2F), + wwH16(12,95,5F,79,E6,3B,8E,A4,BF,71,0E,38,5D,C9,5B,91), + wwH16(29,FA,00,09,44,30,E1,4F,A1,74,F6,83,96,60,D8,93), + wwH16(FF,66,1B,EF,E0,5B,06,B0,A1,9D,8F,5C,E2,72,B2,F0), + wwH16(7B,83,F1,6F,12,FB,51,06,1C,B8,BE,BD,39,5B,BA,B3), + wwH16(DB,13,71,96,E9,CC,30,73,09,12,E3,B8,3C,1A,29,0C), + wwH16(42,52,7D,61,25,6E,C8,CF,58,81,AE,39,6C,1B,45,50), + wwH16(16,72,82,FE,F0,06,F4,9C,EB,4C,B1,C6,B2,7F,0B,90), + wwH16(88,07,BB,24,8E,0F,2D,8B,80,A6,31,2F,D9,45,61,E7), + wwH16(7A,D3,EC,93,C7,72,26,09,07,CB,15,A2,E4,06,78,1D), + wwH16(E1,F2,FD,27,6A,94,1B,62,F2,95,AB,CF,2B,DF,53,8D), + wwH16(96,43,39,67,19,06,3B,62,A2,7D,35,A6,31,FF,C7,A3), + wwH16(83,D6,8E,4E,A0,47,EA,D8,94,E0,22,E5,80,B5,5B,34), + wwH16(4B,93,3A,53,2D,E8,CB,C9,B2,EC,85,B7,6D,6E,DC,FB), + wwH16(74,28,2C,7C,49,AB,DD,2C,D9,77,BB,6B,66,69,95,6B), + wwH16(35,D4,6F,98,6D,3C,92,58,A9,5C,E6,C2,05,A8,C5,DD), + wwH16(B8,9C,FE,1F,45,58,AD,A0,C4,76,E3,40,7E,43,18,18), + wwH16(D1,CD,C4,90,FD,3A,76,F7,13,0B,62,71,E2,FD,2A,D8), + wwH16(D1,1C,AB,B2,20,0A,FF,AA,4C,6C,5F,C3,E3,DA,F9,91), + wwH16(C4,ED,6D,5C,B5,12,17,79,D2,E7,50,3F,1E,47,8A,DC), + wwH16(0D,2F,92,90,E0,E6,EB,A4,2C,E0,A7,FB,98,EF,3D,C9), + wwH16(60,E6,77,4A,30,23,12,61,F1,D3,EB,7D,80,A7,CC,5E), + wwH16(1C,F3,5E,F2,8F,77,E7,98,09,E2,36,85,31,FD,B4,24), + wwH16(EF,6D,61,6D,7A,63,68,E6,8D,EE,46,5D,B8,1A,D0,6D), + wwH16(5C,7D,A0,BC,FC,41,6C,1C,E1,30,5B,20,F5,58,17,5A), + wwH16(E7,15,1B,CF,5A,94,FE,76,A1,33,A2,B1,80,BD,3B,95), + wwH16(16,6F,20,1D,48,6E,2C,82,18,D8,A2,D7,C2,8B,C9,5D), + wwH16(A0,D1,BC,AF,1E,41,DD,02,38,58,28,AF,66,0C,7A,88), + wwH16(F8,D4,17,8B,C2,75,45,88,CF,94,37,60,E7,D8,73,6B), + wwH16(03,54,ED,CB,57,9D,D7,36,F9,42,B4,0A,DF,21,A0,74), + wwH16(7C,A9,1B,6A,F3,55,F3,54,67,C9,D1,9A,67,D4,32,E0), + wwH16(BB,DA,F0,CA,7B,F0,AC,EE,8E,B9,7E,30,04,0E,DF,E6), + wwH16(0F,4C,CB,93,77,D4,DA,6A,E9,DC,76,E9,55,0F,A0,F2), + wwH16(5E,E7,16,56,CC,0F,EE,A7,32,A4,DB,8B,FC,1C,8A,51), + wwH16(CE,4E,51,5E,12,37,C3,46,92,DE,97,B5,25,E1,CF,7D), + wwH16(6E,73,BF,74,BF,F7,A1,1B,89,26,FF,19,E6,DB,D1,C0), + wwH16(94,D5,EC,02,33,CD,F1,70,5A,95,94,88,2E,57,94,14), + wwH16(91,67,94,15,FD,78,8E,D5,78,C9,F9,8B,E0,4E,10,BD), + wwH16(A4,AC,27,4A,1A,B4,6B,9A,F1,13,53,7C,09,89,AF,4C), + wwH16(06,7D,7D,D6,DC,13,48,6B,CC,3C,4D,B7,2D,3F,97,E8), + wwH16(FB,14,7D,13,6A,92,BC,89,69,70,D1,61,6C,ED,61,2D), + wwH16(D5,B7,65,7A,1B,40,D2,18,E9,7C,F0,56,D6,B3,34,CC), + wwH16(68,0A,B5,05,66,B6,83,02,35,5E,7C,F0,85,67,2B,4A), + wwH16(25,14,13,2C,7F,0D,75,CC,C2,BA,65,47,BC,F5,67,2A), + wwH16(EC,9A,96,61,E7,D0,D1,AF,ED,69,6E,D2,F7,4F,C8,02), + wwH16(A2,60,10,C2,9B,13,5E,8C,D8,04,A0,2E,C5,2C,22,9C), + wwH16(32,BD,40,6A,73,67,CD,7E,54,67,F8,A1,A1,F6,16,19), + wwH16(49,39,A9,19,E8,A9,76,33,B0,5E,74,42,76,C8,6F,BB), + wwH16(E3,86,62,02,F5,BE,33,38,57,28,E2,7D,2B,8B,64,19), + wwH16(86,E1,1C,E5,C6,AB,C9,E5,A4,03,BF,B8,60,55,2E,4F), + wwH16(C9,39,58,6A,CD,7B,57,8E,1C,A3,C6,E6,07,42,29,C1), + wwH16(F3,F6,88,28,44,9F,E6,0F,63,D6,FD,0A,3B,E4,BD,43), + wwH16(ED,DC,25,9C,47,CB,55,03,0E,E5,97,63,C7,00,84,21), + wwH16(59,71,54,14,CB,96,8F,B3,DB,6B,96,5B,CE,C4,15,54), + wwH16(69,C1,40,CE,F4,2B,5B,ED,ED,E9,B7,80,34,0F,43,88), + wwH16(3B,C0,3B,80,AB,E5,1E,3E,A3,3F,AE,68,D8,13,4D,9E), + wwH16(FB,80,83,8C,26,F7,E3,E5,66,90,DC,52,9D,07,E1,96), + wwH16(C6,D0,84,2E,4C,2B,FF,44,7E,6A,27,A1,0E,D9,4C,93), + wwH16(0A,3E,8C,B6,86,EE,FB,D6,81,AC,7D,CE,7C,55,E2,9A), + wwH16(E5,B1,4C,60,6B,9C,FD,BC,96,46,CF,2B,22,C5,7D,78), + wwH16(00,19,56,11,D9,07,D1,EE,24,BF,1A,65,D9,2D,B3,4C), + wwH16(E5,BD,51,80,3C,40,F6,23,88,3F,F3,14,64,F0,00,06), + wwH16(CB,6C,0F,7F,3E,7D,06,B3,9D,2B,6C,6C,F1,64,3C,72), + wwH16(49,B1,6A,06,D1,2C,76,19,82,36,9C,00,8B,11,B9,48), + wwH16(85,66,7E,6E,4D,DE,D4,8E,42,74,FB,79,77,96,E7,EA), + wwH16(2D,A3,C4,E1,BA,7A,59,23,2C,7A,15,F6,44,B5,B0,1F), + wwH16(E1,19,7E,44,89,4B,4F,0B,57,81,54,C9,88,46,11,16), + wwH16(B9,3F,02,94,2E,F1,80,40,54,B7,2F,AA,95,EA,73,D9), + wwH16(FF,EF,A4,09,FC,93,4B,78,D6,F8,4E,10,A5,8E,F2,F7), + wwH16(C2,92,77,5C,83,D0,29,49,13,13,A1,7E,40,A1,D2,FF), + wwH16(09,B5,A1,9D,5A,55,81,E5,A0,8D,D4,DC,16,C6,09,03), + wwH16(BC,99,87,92,B7,E0,7F,8B,8B,5E,5E,94,0E,E7,AC,FB), + wwH16(49,50,C3,52,58,75,3C,CA,98,53,88,BE,94,7D,DB,72), + wwH16(EB,B6,B4,81,A7,45,2C,4B,F8,4F,E9,27,AF,80,4F,8B), + wwH16(A0,7D,BC,7F,A0,1B,0A,89,AD,AB,A7,9D,D3,C8,76,1E), + wwH16(DB,3C,6A,12,3C,EF,B4,6D,91,7F,D1,6D,2D,9E,95,BE), + wwH16(2D,9B,72,67,C4,5F,3E,CC,59,BD,7A,71,D1,A0,74,4E), + wwH16(DB,74,12,9B,04,D7,48,EC,EB,03,5A,E2,26,30,4B,99), + wwH16(69,76,BC,E3,9E,EE,14,E3,76,87,C0,DB,CD,56,F6,6F), + wwH16(53,E9,01,48,E0,C7,B7,0F,81,7B,B0,7D,40,87,E4,07), + wwH16(1B,1B,0A,27,84,D3,F3,64,04,B2,8D,88,F3,8C,04,F3), + wwH16(EA,67,E4,58,8E,CC,8D,DB,C5,69,6A,A8,A8,10,8A,47), + wwH16(4F,2C,87,84,7C,4A,F0,5A,10,1D,97,B0,C4,B3,57,01), + wwH16(7C,81,C5,95,E7,69,7A,41,C4,5A,A3,A4,F5,79,F9,D3), + wwH16(67,DE,BF,70,71,72,CC,AF,90,7E,86,F8,5B,FA,1C,E1), + wwH16(BC,A9,0F,EC,DF,8D,66,A9,0A,6F,FF,B7,0E,5F,3F,02), + wwH16(B8,D1,30,64,A3,E2,A7,8D,84,82,A4,C7,51,12,95,4D), + wwH16(54,26,87,B0,AF,BC,71,E4,F1,7E,B7,48,EC,8A,27,21), + wwH16(3B,2D,C4,A4,15,DA,32,58,99,40,E8,39,0A,47,91,09), + wwH16(33,EC,FE,AC,EE,91,2A,21,D1,3E,30,A7,78,DB,EE,2F), + wwH16(17,FF,AC,21,25,B9,77,AA,AD,A4,B8,FB,F5,A0,88,C3), + wwH16(E8,63,67,58,FA,E1,70,6A,BC,8F,13,48,16,1D,DD,2C), + wwH16(E6,92,36,F0,13,7C,72,D9,7A,D9,1C,D5,6F,A0,86,78), + wwH16(65,93,0E,7F,97,24,1D,40,0A,D2,35,17,12,41,A4,81), + wwH16(E4,87,6D,CE,11,0A,5C,9F,CD,2D,30,F0,69,7F,A8,B0), + wwH16(59,CA,34,74,A5,69,17,07,9C,EA,D1,60,2C,73,E1,1C), + wwH16(31,03,6E,60,0A,83,59,08,5F,B2,A1,7C,98,65,63,E7), + wwH16(17,88,95,57,BF,30,93,D3,CF,A3,35,7F,69,D9,11,1E), + wwH16(0A,AF,7B,9D,75,38,16,CC,1A,51,36,2D,6D,6C,E0,E8), + wwH16(EF,0E,10,E7,50,90,63,EA,CB,AF,89,80,DD,03,59,B2), + wwH16(DB,E9,F6,77,0D,EC,E5,31,CF,37,07,16,0E,DB,A4,19), + wwH16(43,E9,BA,48,41,E5,7E,93,C7,75,DC,4C,D0,75,A7,61), + wwH16(02,3A,32,58,2E,6A,74,B4,30,96,58,5E,FB,AF,C8,69), + wwH16(96,FE,F3,23,E8,CE,CB,5D,CF,82,60,29,82,66,D5,91), + wwH16(DD,52,F0,6D,E8,17,FE,57,43,F5,12,28,D6,03,C2,E2), + wwH16(1B,C0,6B,97,0A,F0,52,9D,7D,21,56,53,82,68,B9,19), + wwH16(1C,2A,01,B4,1B,41,D1,0D,12,CC,BF,A6,E2,14,30,80), + wwH16(33,E4,71,9C,33,0B,10,8E,7E,41,3A,FF,59,79,A7,63), + wwH16(51,0F,CD,64,8D,DF,14,9C,DC,C5,E5,55,69,84,DA,51), + wwH16(E5,31,C4,F4,9D,60,19,DE,FC,F1,AA,5D,A9,0A,E5,0F), + wwH16(AC,08,E0,D7,81,42,D5,50,09,B7,D9,69,B1,0B,51,A6), + wwH16(E3,F1,12,EC,62,23,35,19,86,5A,F3,C1,2E,E0,30,BF), + wwH16(35,43,B6,28,7F,AC,4D,01,71,74,A1,6D,BD,8F,67,D5), + wwH16(33,5D,2A,51,91,56,27,B6,27,75,0C,D4,7B,D8,44,E3), + wwH16(7A,8D,0E,FD,6E,35,45,6D,5E,65,53,C8,64,66,47,94), + wwH16(C7,4D,67,EE,4C,85,7D,13,B3,E2,2E,F5,4E,16,44,69), + wwH16(5E,5C,50,2F,6F,AD,EE,58,B4,03,15,F7,5D,AC,AB,26), + wwH16(11,3D,F0,58,7F,4E,C0,6C,95,8A,A9,18,14,28,44,BA), + wwH16(BF,22,99,76,1F,78,E3,29,D1,D4,B9,AF,8C,C3,B3,BC), + wwH16(DA,B7,F2,A1,65,DD,06,A5,37,7D,FC,9C,EB,24,96,65), + wwH16(0C,BF,93,E5,10,AF,6B,F1,9E,AE,C4,17,23,70,B1,8A), + wwH16(86,3B,9D,54,62,6E,35,DC,C6,99,18,79,C1,FD,44,10), + wwH16(AE,C8,41,7C,22,D7,AC,60,8B,42,7B,76,E6,EC,D5,8F), + wwH16(96,86,A9,1D,72,EB,E2,4F,BB,46,B2,9B,D2,4A,05,57), + wwH16(B5,E7,5A,0B,71,BE,D3,5D,56,95,82,FB,8F,1A,51,4E), + wwH16(AA,FF,80,00,75,ED,17,87,F3,45,C9,92,7A,DE,39,06), + wwH16(8A,DC,97,3B,14,D8,32,84,C2,BD,28,B3,EB,CE,0E,7F), + wwH16(39,61,04,3E,6D,BD,3B,94,36,DE,97,FB,68,A4,22,63), + wwH16(15,90,BE,86,16,1D,65,84,85,E2,52,8C,B5,A8,39,0E), + wwH16(F8,A1,00,58,46,57,1A,3C,E1,89,7D,DE,6F,65,3D,E2), + wwH16(2F,D5,FC,70,BB,AF,D9,27,9B,9C,0D,A2,CF,AF,BD,F3), + wwH16(EA,B9,56,07,49,06,53,79,BE,95,88,89,5A,2D,4C,90), + wwH16(6A,C1,27,5D,A7,D5,95,EF,A1,0A,13,DD,58,26,E2,03), + wwH16(2B,BE,74,CB,D1,CF,55,14,A2,F1,C2,A0,EC,48,9D,49), + wwH16(46,EE,DF,D7,77,9B,45,BC,A4,A6,46,C1,7E,C7,F7,4C), + wwH16(6A,10,81,1A,32,37,FA,9F,EE,D9,F6,BA,D3,CB,4B,FB), + wwH16(45,8C,04,58,59,10,70,7D,1C,F8,DB,B7,14,B9,9A,8D), + wwH16(CF,1F,E5,7F,CA,F3,C9,15,AD,8A,8F,9A,89,06,12,39), + wwH16(B3,B9,67,C3,F2,AE,A6,6B,82,29,2C,49,76,F4,F3,18), + wwH16(D4,10,F5,36,FE,6C,0E,F1,DD,35,01,1D,2C,1D,CE,7E), + wwH16(0E,96,71,F2,B9,10,A9,2F,42,CF,FD,24,E1,7E,50,D5), + wwH16(5A,63,44,42,1C,18,4B,BA,9F,14,D9,5D,3B,00,1B,E8), + wwH16(0A,00,4E,37,7A,DC,84,2E,9E,7C,E7,F7,A5,00,8A,26), + wwH16(F1,5E,D8,00,99,48,B0,33,0F,02,F8,CF,1A,F3,85,30), + wwH16(D2,2D,9C,26,AF,11,DE,9E,A6,31,59,03,45,13,3F,56), + wwH16(61,62,44,73,1A,90,22,59,DC,55,1E,27,36,11,C1,58), + wwH16(87,AB,DD,68,CF,93,C2,82,F2,EB,98,41,22,A2,38,12), + wwH16(2F,4A,7D,FA,D0,E9,9D,AC,EE,9F,29,88,85,D0,27,D3), + wwH16(D8,01,95,0C,A7,2F,3D,69,BC,E3,C7,1A,BE,9B,DF,EA), + wwH16(C0,DB,9D,BF,C4,97,60,03,01,8E,C4,DE,2E,2E,79,8B), + wwH16(57,31,B2,16,72,C3,82,53,56,B1,C7,7B,D6,FC,21,8F), + wwH16(A1,8F,34,61,A6,A3,62,5E,D1,70,1F,0B,E6,60,13,FA), + wwH16(5D,66,B9,F1,C4,86,10,2A,21,0F,7C,B9,0F,8A,D6,E1), + wwH16(3C,38,5A,A5,64,44,4F,0E,84,66,AF,96,B1,6D,97,34), + wwH16(92,27,C9,9C,AE,19,F9,D1,95,EF,DF,1A,93,37,61,E8), + wwH16(6C,B9,8A,DB,9A,30,6A,60,0E,56,C2,8B,A1,58,74,84), + wwH16(38,4C,3C,29,89,71,4A,53,9E,AB,45,BE,66,5A,4B,D6), + wwH16(F8,B0,BF,0D,EC,59,E1,19,A9,D4,D1,AC,96,88,67,74), + wwH16(95,80,40,FC,E8,DA,A5,EB,8D,9B,3B,60,E1,B2,42,F8), + wwH16(74,39,08,5F,B3,00,80,6C,5F,97,48,76,56,8C,1E,5E), + wwH16(9A,4D,E7,42,69,64,C7,84,FF,76,4E,99,01,4F,96,FE), + wwH16(12,C9,D9,50,A4,87,E1,53,30,CA,D4,4F,E7,48,AB,B8), + wwH16(98,DD,52,03,D6,A3,70,67,F3,D4,5B,EE,9A,C1,50,37), + wwH16(AE,90,33,9B,A7,2E,1F,25,7F,3D,B6,E9,D3,5C,0D,B0), + wwH16(5E,DE,51,BE,B0,0D,9A,22,02,7C,E9,E9,E7,CC,0D,AA), + wwH16(67,1C,9A,35,53,E1,84,4E,DE,A8,71,48,68,9A,5B,44), + wwH16(62,B0,8D,9E,07,B3,B2,72,67,BD,25,5C,92,87,33,8A), + wwH16(14,D0,06,F7,B2,61,2F,25,EC,C2,13,4E,39,0C,0F,05), + wwH16(77,5B,73,59,49,BD,63,88,B7,1E,B1,39,CD,2F,8A,F4), + wwH16(3D,4A,D1,37,98,FF,A2,47,2B,BA,68,AE,7C,27,30,44), + wwH16(90,7B,2D,AE,FC,8D,50,53,4A,01,77,B1,CA,D9,BC,CC), + wwH16(B4,05,3F,F5,81,B7,A9,56,9D,F9,26,E4,0F,D4,D6,DC), + wwH16(D7,58,E6,E2,AA,0C,F2,FD,BB,04,BB,6D,86,A0,35,A4), + wwH16(8F,27,8D,E3,29,47,FB,EE,4B,45,08,D6,34,D6,E1,5B), + wwH16(73,25,64,18,8E,DB,26,BC,B0,CB,89,EE,91,F9,19,00), + wwH16(EB,40,CD,20,78,2B,CD,36,D5,66,E8,17,76,37,9D,7F), + wwH16(69,8D,FD,94,A1,7C,C6,3C,85,CC,BD,CE,30,C7,A6,54), + wwH16(4C,09,A4,A5,39,BE,6C,2B,45,60,B6,61,E4,92,CA,F3), + wwH16(5B,D4,ED,23,5F,73,06,CD,01,B6,BB,3C,43,34,92,E5), + wwH16(9E,E9,FE,41,AE,FC,5C,42,A4,A2,5B,73,28,51,78,87), + wwH16(C1,82,29,B2,C0,D0,4B,BD,91,F3,3B,EE,22,A4,04,11), + wwH16(0D,C3,8B,98,B2,6B,C4,57,0B,F3,85,64,64,E7,9E,60), + wwH16(36,5B,47,E1,EC,F7,73,BB,89,65,7D,C1,BF,5B,7D,29), + wwH16(4D,54,81,C4,06,F9,5B,7B,E3,14,6E,55,CB,24,5C,59), + wwH16(85,2B,7E,55,E9,6D,87,E2,9D,73,F7,53,E7,52,BA,B8), + wwH16(A8,1B,92,C8,C5,53,47,53,C3,99,CE,D8,D3,D4,26,F2), + wwH16(30,12,77,3A,66,05,61,FA,59,BC,15,D9,EC,06,0E,F4), + wwH16(61,E5,4E,3E,3B,B4,AB,03,EA,AC,AD,BB,43,8C,5B,9C), + wwH16(3C,A6,E4,B5,68,29,D2,71,C0,2E,15,4B,20,16,84,50), + wwH16(25,43,95,4A,A2,7E,5A,35,D6,38,CD,65,60,35,1E,83), + wwH16(D0,82,8E,3F,FD,42,69,12,25,82,30,AD,76,B6,DD,FC), + wwH16(42,71,1A,AF,E0,01,92,74,80,1D,03,46,86,70,F1,20), + wwH16(FB,EC,A8,8C,51,57,6C,2A,D3,F9,A0,13,FB,58,46,67), + wwH16(CB,1B,C1,30,C4,96,80,12,62,04,2E,55,05,D4,CD,5B), + wwH16(DF,70,A6,73,47,63,E2,C6,D8,13,63,1B,0C,46,FA,8A), + wwH16(0C,85,04,F4,96,2E,E7,B8,9D,B1,63,56,82,5C,78,32), + wwH16(A9,B8,EC,D1,0C,61,BB,92,EA,81,C5,FF,C7,A7,CD,7F), + wwH16(F1,F8,AB,3B,7D,42,23,B8,E8,72,60,B9,CF,5D,9A,7F), + wwH16(11,65,3B,94,42,94,D2,81,92,99,7B,B6,3F,E6,5B,CB), + wwH16(85,2E,C3,BA,E1,27,FE,48,36,D4,02,E0,AF,97,E8,7D), + wwH16(47,90,8C,1E,B5,DE,03,39,50,A8,6C,C7,E6,EE,1D,95), + wwH16(8C,BB,F7,51,7A,D8,CB,D7,D8,84,7E,F1,29,1A,E4,15), + wwH16(94,92,83,4B,AF,EE,7B,3C,4E,98,DB,F0,51,A7,CE,C7), + wwH16(67,BF,BD,1E,7A,52,AF,45,F7,BD,A2,55,4F,6C,ED,03), + wwH16(E3,AF,A3,DD,2C,68,CC,47,4C,08,C1,88,4F,0F,D4,FF), + wwH16(AF,71,C9,25,19,DF,A5,71,63,1B,80,44,A2,36,98,3A), + wwH16(12,F9,35,B3,F6,C1,E6,3C,A1,D9,0F,45,DE,F8,B3,1A), + wwH16(D6,AC,A7,7B,33,1E,48,44,AD,E1,C8,FC,9D,DD,D1,07), + wwH16(F1,92,47,85,B1,E1,64,E7,04,67,03,46,00,84,2F,DE), + wwH16(22,47,F6,78,D1,F3,C5,16,63,A4,13,11,12,41,37,10), + wwH16(B8,E7,46,79,AD,47,60,23,9E,71,8B,18,61,7B,6F,34), + wwH16(12,38,19,A8,6E,4D,45,AD,D5,D9,B8,F7,3D,34,97,48), + wwH16(A0,0F,2C,44,92,68,6C,3F,AD,C4,2D,CB,53,DD,9E,69), + wwH16(38,D9,08,A8,F6,16,26,F8,66,AD,B6,4F,08,04,98,E9), + wwH16(C0,70,3B,41,4B,33,CB,73,E9,B2,26,B9,46,AC,DB,74), + wwH16(50,E1,35,05,69,D1,32,AA,D3,79,B3,51,69,CA,13,D6), + wwH16(8B,CA,DD,98,0B,50,20,DE,20,F5,A4,EB,1F,28,47,5D), + wwH16(9A,CF,1D,5C,07,FC,E1,F2,8F,95,45,69,A1,67,09,73), + wwH16(14,C5,BF,74,54,68,A4,13,87,C9,78,5C,27,03,4E,ED), + wwH16(0E,50,AE,34,8A,7C,44,7D,E7,61,8C,1B,F3,88,79,49), + wwH16(1B,34,24,98,F0,99,3E,C3,91,D0,B9,55,26,6A,53,D7), + wwH16(D8,FD,B8,65,7F,3D,E5,B2,93,49,86,6F,43,3C,75,5C), + wwH16(BD,92,C2,07,1B,B1,6D,27,53,28,D5,68,43,15,A7,1D), + wwH16(2F,F1,7E,CF,3D,3B,8E,A9,46,E1,6D,D0,E8,AA,C1,9B), + wwH16(71,6E,6E,8B,84,BD,67,3C,E5,FC,A2,A7,8C,22,EA,26), + wwH16(FA,3A,37,3A,4D,A2,81,D2,A3,51,B5,DF,02,A0,23,8A), + wwH16(82,29,47,05,32,21,B9,B4,30,5C,69,29,EF,F1,AA,09), + wwH16(E9,C4,EB,65,FF,D4,A3,B7,39,31,AD,9C,4F,CA,20,1D), + wwH16(24,09,62,02,76,F1,9F,9C,6C,AB,82,4A,D1,A5,75,4C), + wwH16(13,F8,B4,C1,6D,D6,7F,4F,26,D3,4E,1D,76,19,26,12), + wwH16(DD,B0,F6,4A,3B,9D,20,3F,B2,C3,8B,EB,DA,DA,7C,31), + wwH16(D8,2F,51,F3,F9,75,53,C1,63,28,6A,C7,4F,BC,73,BD), + wwH16(8B,E2,87,C0,2B,4B,A2,D7,AF,DB,83,45,DD,9F,55,43), + wwH16(A8,6F,A4,29,91,E1,A0,1C,85,3F,5D,52,CD,17,7E,EE), + wwH16(A6,21,EC,96,35,F3,EE,26,CC,09,6E,08,E4,80,18,F0), + wwH16(70,84,A3,9D,AB,66,78,15,7B,91,A1,C3,D3,C0,82,7B), + wwH16(F2,E9,D9,60,AC,0C,BD,9A,72,67,B6,D1,41,05,C1,6E), + wwH16(CC,19,0A,31,51,DB,8E,28,5F,F6,1D,F3,06,A2,9E,9D), + wwH16(34,FA,1A,29,4C,06,39,99,B4,2B,07,AA,96,9A,4E,45), + wwH16(5F,19,F6,31,10,A2,EA,AF,6A,57,A8,03,0C,F5,59,FD), + wwH16(6D,55,34,DE,02,E4,4B,31,35,6C,2B,C3,A1,3D,08,E0), + wwH16(F0,A5,C5,D2,11,02,21,C4,C9,C8,CD,AD,8A,12,2D,9A), + wwH16(D2,53,42,64,71,FF,24,7C,1F,9F,05,8C,FF,19,35,26), + wwH16(3F,5A,9C,A2,D4,86,62,DB,D4,DE,09,EA,35,5E,9B,72), + wwH16(30,21,A6,5B,39,05,DD,19,3B,C4,FA,C2,BB,94,AF,7F), + wwH16(18,E1,17,5B,09,42,AD,58,1B,22,14,69,3B,FE,97,43), + wwH16(43,84,46,A8,D2,A4,2A,59,62,3B,80,8A,AB,99,36,24), + wwH16(49,3F,9F,9A,10,C7,5F,94,86,B7,CE,51,5E,4E,F9,2F), + wwH16(84,6A,C4,77,22,BA,BD,2B,01,89,2F,A5,DA,7D,DC,52), + wwH16(31,60,21,C4,B5,89,05,F8,46,F2,A6,4D,78,A6,F4,C6), + wwH16(33,A5,6A,33,F7,7E,59,85,A7,64,5F,F6,13,D5,25,99), + wwH16(53,87,BB,53,0A,52,EA,25,DB,6D,DE,D0,F7,B3,2D,E0), + wwH16(AE,98,E7,03,0A,D0,57,54,CA,83,87,46,D2,A8,C6,94), + wwH16(13,C8,E2,D0,81,F4,BE,5E,DA,BC,F7,00,11,FF,62,D3), + wwH16(73,B2,8B,B2,8F,5C,36,50,78,A1,A1,88,7B,98,AA,70), + wwH16(A4,EC,2A,AC,E8,BA,62,03,68,4E,43,D4,E3,16,41,37), + wwH16(C0,DD,70,A7,FF,1D,A5,80,14,40,14,7E,05,72,BA,EE), + wwH16(FC,BA,EF,2A,7B,E7,B6,8D,09,B6,FA,1A,7A,58,CA,22), + wwH16(47,56,63,F1,BD,36,D9,52,3B,49,9D,AC,09,FA,D3,94), + wwH16(5E,C3,03,AB,25,FB,74,08,F8,9F,1C,04,08,01,02,EA), + wwH16(2C,D5,FB,CB,46,ED,11,DC,DB,D4,5B,B0,96,23,D4,11), + wwH16(BA,21,35,06,CD,44,09,E5,74,F8,1D,39,8B,0A,06,86), + wwH16(6A,A0,2E,D1,67,38,84,D0,3F,84,6B,A0,93,C8,7A,60), + wwH16(2A,41,23,03,D1,32,57,CA,B0,4A,4A,A1,A5,33,32,F2), + wwH16(2D,61,3C,29,A6,57,A9,98,E9,14,3A,A8,19,88,6C,51), + wwH16(C3,71,84,29,D0,D0,CF,EC,E9,8A,57,C0,D2,BB,20,11), + wwH16(57,B5,82,B0,C1,72,49,24,44,16,87,68,A9,E1,52,B6), + wwH16(28,B8,95,A9,DF,6B,5F,61,43,CD,17,72,0B,53,42,58), + wwH16(A5,DC,CF,A0,BE,7D,00,6B,13,99,3C,25,A3,43,8D,A3), + wwH16(7F,6F,72,C7,6D,DC,9A,B9,3C,A3,08,9C,0D,88,A4,A8), + wwH16(0B,00,A5,A6,9D,24,1C,CC,1D,CA,98,16,BC,92,01,89), + wwH16(4C,00,C0,6B,64,7D,15,49,F6,56,FF,34,FC,FC,B8,E6), + wwH16(56,0C,AF,30,7D,56,4C,23,BF,49,C9,02,9A,37,CE,78), + wwH16(54,F1,23,A1,4A,84,D9,33,CA,06,FE,2E,6F,8D,E1,B8), + wwH16(14,4C,01,75,E4,EE,5C,43,73,43,9C,1F,4A,53,5D,2C), + wwH16(45,A3,9D,C1,8D,B2,1E,29,2F,54,09,7D,34,7C,6B,B9), + wwH16(14,93,E6,79,DD,F9,B9,07,E2,D3,7D,7F,37,3F,A7,7A), + wwH16(F2,DB,DB,5B,82,80,3C,E5,92,FA,70,E3,32,E5,E2,33), + wwH16(EE,5A,B2,C3,CA,7C,C7,77,8D,64,92,BC,C2,AA,38,D0), + wwH16(D3,45,6C,34,A9,D9,D3,75,3F,74,31,CE,E8,34,9E,06), + wwH16(28,0F,28,55,07,8D,86,02,07,6B,A7,05,76,08,AA,C4), + wwH16(E8,35,C9,04,85,30,74,E3,FE,34,FF,E8,97,60,AE,50), + wwH16(8B,42,51,D4,88,64,DD,1D,F9,28,A8,63,7B,0B,2E,CD), + wwH16(15,87,B3,F4,1C,16,01,89,96,71,E4,A9,82,EB,B6,D7), + wwH16(CF,23,07,47,1C,FE,E5,FF,95,1C,76,87,ED,DD,58,C4), + wwH16(2B,6C,44,AE,0F,01,B4,80,5D,96,29,DF,FA,5F,C9,E8), + wwH16(FB,51,6C,FB,A4,E2,2B,A9,6D,E7,A5,6C,F7,A7,29,57), + wwH16(A9,69,0F,73,1F,C0,08,F9,A8,BC,CF,F1,76,21,7E,46), + wwH16(65,9B,30,AB,EE,F7,C8,CC,C1,D2,12,2A,1B,17,50,54), + wwH16(58,BC,AE,8E,B0,91,AF,00,87,89,CA,D1,B6,AE,70,04), + wwH16(71,3E,83,98,99,BF,5E,6B,4E,FD,36,56,B7,AC,D3,C6), + wwH16(1E,8F,D4,4E,FF,19,08,17,5D,76,63,91,42,D2,AE,3C), + wwH16(97,59,86,73,FE,FF,5B,4A,B1,D4,2E,D1,98,DA,95,EA), + wwH16(76,98,EE,BC,EB,AE,AD,C1,85,96,DE,43,57,D9,AB,3C), + wwH16(ED,DB,19,2A,B1,93,F7,4D,E8,25,B9,1C,38,F4,B2,1A), + wwH16(AB,A1,92,45,1D,02,44,2D,3E,0C,C0,4C,4C,67,03,62), + wwH16(87,4C,E1,DB,C1,05,A7,BF,7E,49,07,F2,FA,9D,56,68), + wwH16(57,C1,C1,A1,B5,0C,6C,C2,2D,1B,FB,29,C6,DC,7C,83), + wwH16(2E,81,90,93,6A,64,D3,21,68,9B,49,1B,26,AF,57,B4), + wwH16(D2,6A,12,E7,85,C7,E3,15,85,5D,0E,1F,31,58,71,DC), + wwH16(01,F1,AC,65,32,98,CB,4E,20,28,4F,C3,08,61,0C,96), + wwH16(97,71,6F,63,8B,FD,65,FD,C2,64,9E,A4,40,37,B4,71), + wwH16(A9,8C,1B,66,58,AE,6F,C8,79,F1,74,F8,C6,19,DC,9F), + wwH16(1F,5E,46,73,39,D5,1D,41,F1,16,8A,7C,37,D4,80,21), + wwH16(BE,41,CE,0B,46,8C,D0,5D,17,38,B7,DA,C2,4F,E9,5F), + wwH16(86,58,94,E2,A4,51,D5,B1,24,75,38,45,05,B5,E4,15), + wwH16(C2,E6,99,5F,8F,64,30,E3,0F,70,FD,DC,64,C0,47,93), + wwH16(19,E5,5B,E3,77,9B,08,3E,B1,09,09,E5,D8,5F,97,DB), + wwH16(2F,24,05,B9,99,8B,C5,65,D9,31,21,C2,05,5A,C6,A3), + wwH16(C0,DC,16,23,37,5E,93,03,64,FD,73,00,84,FF,92,FC), + wwH16(B0,B9,8F,C8,9C,A3,03,FB,D3,BE,BA,5D,1F,4A,06,E0), + wwH16(82,69,B2,26,A8,AE,6D,BE,9A,94,AC,24,4F,E5,92,B5), + wwH16(28,5D,6D,E9,EF,FC,37,85,4F,30,64,0E,3A,0E,23,21), + wwH16(12,C7,22,D3,69,3E,D3,27,45,D8,94,16,CA,5F,8F,B6), + wwH16(3C,CF,08,F4,09,B5,B4,49,38,3C,24,5F,27,38,10,08), + wwH16(D6,89,07,72,C3,90,FA,99,08,21,EF,20,53,BB,12,C8), + wwH16(6B,30,3E,4C,44,47,A1,0E,91,37,5A,3A,02,80,6A,85), + wwH16(26,63,33,34,7D,82,1E,DE,53,31,87,80,AC,27,51,FB), + wwH16(9D,90,40,C5,D6,13,9F,81,14,26,B3,52,C0,16,DD,FB), + wwH16(2B,63,79,01,86,2B,F0,19,89,14,89,5B,BF,93,4D,C3), + wwH16(28,DF,11,2F,BD,0C,DA,73,65,EC,7E,4F,90,42,3F,A7), + wwH16(4E,DA,2E,A1,15,A9,B1,7D,D2,FB,FD,37,27,C6,FE,BA), + wwH16(90,02,BA,CB,23,D2,8B,E5,D6,3E,6E,32,9A,F2,AA,2C), + wwH16(2C,C4,30,70,DD,6D,04,2E,8D,2B,E3,1A,B1,19,84,A6), + wwH16(10,42,9B,4D,A0,2E,9B,F5,73,77,C8,BA,4E,9A,CB,3A), + wwH16(42,4F,7B,3E,BB,9E,AE,3D,04,E2,79,59,60,45,F7,40), + wwH16(08,65,AF,4D,BF,2C,31,C3,1F,61,63,D1,D3,B0,FD,CE), + wwH16(D6,60,B0,4E,27,09,C0,3F,F3,38,F3,DD,34,9E,32,31), + wwH16(E6,62,94,5F,D9,E0,23,0F,80,51,EC,F7,50,45,99,86), + wwH16(BC,62,D2,42,61,2F,88,D7,06,14,F5,09,60,8B,02,5F), + wwH16(90,6C,60,D4,B2,05,95,C2,32,A0,74,2F,FD,2C,4C,DA), + wwH16(AB,DA,E0,D4,76,B6,60,11,9B,26,1C,48,07,FC,DE,15), + wwH16(61,73,D7,42,C5,4F,4F,4A,45,46,04,6F,C2,DD,CD,B9), + wwH16(2C,26,1A,6F,B1,C1,59,C8,4D,35,FC,E6,5B,D3,AA,BC), + wwH16(5D,47,0D,2B,A2,CD,3C,B3,7A,30,1F,86,51,25,A7,D7), + wwH16(84,C4,2E,97,AE,D6,9D,D5,9A,60,B2,66,8C,08,21,AA), + wwH16(70,2C,89,68,A5,FF,57,39,4D,01,F0,6C,23,08,45,92), + wwH16(F0,A9,F8,FB,F4,82,0C,F4,10,E7,C9,A6,4C,C9,BF,D7), + wwH16(E1,A9,5B,C0,03,0E,07,0B,17,F8,EE,99,D6,D2,EE,C0), + wwH16(65,3F,A2,AE,55,ED,32,32,79,E0,90,69,78,99,34,E6), + wwH16(82,44,42,13,48,C0,07,84,5A,B9,56,F2,21,80,1C,FC), + wwH16(ED,86,20,87,59,DA,49,76,1F,2D,EB,DC,8E,52,09,46), + wwH16(86,63,D1,0B,2B,9F,92,62,82,40,76,7B,8D,A4,D6,71), + wwH16(36,91,EE,1A,1C,B6,9C,0F,38,3A,5C,1D,96,0C,03,73), + wwH16(10,28,C5,1B,46,AE,60,98,A4,F8,59,15,D6,88,44,D4), + wwH16(22,5F,13,1B,1A,E2,2C,B4,9F,2E,4B,92,CC,EA,DC,56), + wwH16(F4,D3,0B,AA,DB,94,DD,18,5B,99,A3,9E,B1,0A,76,26), + wwH16(1B,4B,E0,77,70,29,1B,75,D2,72,07,82,76,5C,25,7F), + wwH16(0F,EB,4E,C4,A6,D1,42,BC,A2,23,85,A8,9C,14,0C,86), + wwH16(77,7D,A0,B6,85,FF,11,BB,65,4E,06,44,16,9B,4F,62), + wwH16(FC,67,4E,0F,D9,E8,D1,28,29,F7,A3,7D,AC,29,AD,A5), + wwH16(BD,8A,49,1B,02,C2,E2,A7,D7,A3,31,DF,1A,3D,06,0A), + wwH16(D1,ED,5A,44,38,E4,A6,EB,39,43,2E,51,10,3F,DB,A7), + wwH16(0C,59,69,99,7E,0D,10,CB,DE,3A,84,9A,15,6C,E2,6B), + wwH16(57,85,BB,E1,D1,89,32,10,D8,AD,0C,1C,14,EA,C8,B3), + wwH16(2C,7E,5A,BC,16,D6,B4,D5,63,AE,41,14,00,69,80,2F), + wwH16(AC,8F,B1,E8,EA,C7,F3,C1,8B,F1,07,64,22,5E,02,C5), + wwH16(5F,66,92,5F,75,1C,66,75,7D,88,C9,C4,21,40,D2,7F), + wwH16(46,AE,7C,62,56,2D,DF,A1,A5,9F,E3,FD,29,D9,6E,38), + wwH16(6A,62,19,CA,FF,C2,14,42,30,D6,55,7F,CB,6E,5A,79), + wwH16(ED,7B,C3,02,DE,43,E3,48,BB,1F,E7,E2,2B,A4,39,7B), + wwH16(5C,08,27,46,24,C0,D7,B7,8C,B3,34,67,09,D9,39,35), + wwH16(11,17,A3,5C,69,4C,F9,77,AE,4F,A4,CA,06,6C,F6,EF), + wwH16(E6,81,2D,31,21,CD,2C,7F,28,9B,AA,85,9B,55,FA,58), + wwH16(AE,D2,11,6B,89,02,5B,A7,60,25,BD,5A,DD,0F,39,87), + wwH16(87,31,C5,18,4D,03,4C,83,43,3D,F5,63,94,40,0D,61), + wwH16(3D,4E,91,17,A7,21,D7,79,EF,E7,81,CA,75,D1,B6,10), + wwH16(C9,C0,C6,DC,8B,E0,D0,17,EC,FF,6B,24,F0,CD,BB,D0), + wwH16(86,65,50,C3,8D,F1,C9,F6,7F,3F,F6,7F,DA,2A,BD,D5), + wwH16(3D,1E,12,47,14,7F,ED,40,5D,13,5D,BE,2B,F0,CD,03), + wwH16(B4,47,27,52,B1,D0,E1,94,51,5E,DE,7B,8E,E8,E7,83), + wwH16(D5,86,2B,39,81,95,54,6F,64,FC,6B,75,06,29,C5,A4), + wwH16(EE,70,3B,A1,C2,0B,E4,B7,77,73,7D,92,FD,9C,2B,10), + wwH16(B3,12,7D,50,C6,D6,31,C0,81,7D,58,EF,6E,43,14,90), + wwH16(DF,6F,46,14,CD,30,D6,ED,DF,46,4B,C3,56,B5,A3,3B), + wwH16(27,61,0E,E3,A1,2E,DA,4A,CD,CA,17,8D,6E,63,3C,5C), + wwH16(E7,3D,37,85,A1,4C,A7,73,88,93,6D,47,56,D8,4E,BF), + wwH16(96,69,82,9C,08,27,1E,BB,3B,8A,FC,D8,AB,B5,AB,07), + wwH16(1F,9D,D3,41,1E,A4,9C,13,99,53,50,63,20,5E,48,5E), + wwH16(25,BB,3D,24,52,DA,7D,81,DB,10,85,BE,CA,22,18,02), + wwH16(20,5D,80,C3,3C,16,A9,44,69,2A,6F,21,AC,46,99,68), + wwH16(A4,57,91,49,B1,34,95,FE,15,17,9C,8D,D3,38,93,5F), + wwH16(F3,46,27,D1,C3,06,20,20,5B,ED,0C,73,AE,BB,48,8A), + wwH16(DA,02,8C,BB,E0,4A,B8,33,C5,14,CB,2B,AE,00,D1,08), + wwH16(05,79,4C,94,A5,82,2A,7F,30,AB,47,75,0E,9A,50,4E), + wwH16(4A,61,6B,40,48,48,96,4C,CD,57,8F,AF,53,33,EF,ED), + wwH16(AE,E4,7F,56,29,06,ED,4F,F6,2A,1D,F3,4A,79,53,C8), + wwH16(59,19,39,6A,5F,EE,48,71,CC,68,35,DD,87,17,B4,4C), + wwH16(56,27,2B,A0,49,B5,B5,BA,8D,31,86,EF,88,FB,B6,96), + wwH16(E0,0B,EF,1F,8A,04,B4,BE,B1,26,E0,2B,D8,13,58,23), + wwH16(A0,81,5D,84,2E,4D,0A,36,3E,53,F1,4F,6B,0D,86,53), + wwH16(88,15,D4,35,30,83,4B,86,EF,79,3D,94,A2,F2,B6,C4), + wwH16(B1,97,21,4D,D2,EB,8E,A9,F7,99,D6,88,8F,F9,8B,A9), + wwH16(BB,E5,1A,25,F0,0A,6D,DA,77,F2,41,D8,C0,6E,C6,40), + wwH16(4A,DC,3F,2E,D6,F0,3D,BE,80,28,94,FA,27,EA,80,11), + wwH16(CD,78,46,1B,73,2C,86,4F,93,8E,77,CF,8B,5F,B4,8C), + wwH16(01,B3,53,52,60,A6,24,B5,A8,FE,94,35,C0,16,B5,48), + wwH16(79,51,F9,02,09,FC,D6,49,44,E8,8A,F9,FF,77,CC,D2), + wwH16(D2,3C,61,D6,77,EE,C6,C5,5C,DD,C4,8C,56,F3,99,BE), + wwH16(6B,7A,18,A7,96,F0,76,2C,6B,67,AF,FA,38,34,3E,CE), + wwH16(63,A3,04,24,84,D9,6C,74,EA,5F,78,F1,29,1C,00,7B), + wwH16(DD,0A,EA,83,49,CF,FA,21,21,AA,20,EC,AF,0C,29,A8), + wwH16(27,8D,CE,36,E8,C1,4B,D9,6E,1B,0F,7A,CD,81,8E,69), + wwH16(B0,C8,71,36,D0,BF,12,94,3D,35,22,88,70,1D,65,7D), + wwH16(64,50,C0,CA,DB,28,AD,EC,72,E8,B1,B5,F4,B2,AD,AB), + wwH16(16,1B,50,EF,7E,34,2E,F4,6D,5B,9E,CA,CA,DA,28,D2), + wwH16(49,52,57,A9,07,F0,FE,60,D7,43,80,0A,18,BF,FC,2E), + wwH16(6E,93,F3,DA,C7,54,D5,4E,11,39,48,E2,53,67,81,73), + wwH16(A3,48,8D,13,E6,92,EA,03,39,5D,24,95,7E,7C,22,89), + wwH16(E6,45,07,35,D9,CB,8D,6D,74,9E,95,A0,37,02,C3,74), + wwH16(FE,1A,EB,B7,51,6A,79,96,61,26,14,EC,F2,70,85,F4), + wwH16(D0,53,BA,78,F1,4B,F0,3E,6B,01,D8,17,9E,AA,C3,95), + wwH16(D1,48,AB,7E,B9,F3,38,7B,45,E3,D2,2E,3D,1A,7D,A5), + wwH16(C0,51,4E,C0,D3,D2,87,54,7E,C7,F6,16,8E,6F,B7,B7), + wwH16(82,D8,AE,11,30,05,3C,83,41,FF,1C,98,9F,14,B6,48), + wwH16(69,88,B4,DF,27,EB,A9,07,9E,37,D4,10,20,0A,97,22), + wwH16(B0,95,95,08,63,A1,09,7A,38,24,19,42,96,5D,AF,74), + wwH16(D5,F8,6E,52,F6,FD,86,DF,3D,B0,7A,EF,8A,2B,F5,85), + wwH16(E4,6A,52,CF,08,8E,91,07,45,B7,7F,27,5C,B7,18,FC), + wwH16(5E,AE,8C,D7,F0,16,C9,40,62,8B,1A,4B,64,A9,92,D8), + wwH16(04,28,45,11,AC,EF,27,01,7F,D5,06,D1,A7,8A,CF,28), + wwH16(AE,0E,14,2F,A3,B5,39,18,8F,17,FF,9E,94,F0,05,44), + wwH16(C0,05,7F,98,63,35,8B,04,5E,D0,AF,8D,3F,F0,8C,38), + wwH16(E7,D1,B0,73,FC,16,68,94,A0,63,62,7E,F4,3F,70,E0), + wwH16(B0,16,C4,92,86,06,29,21,A1,3C,B5,D2,82,10,95,F0), + wwH16(D1,4C,0E,72,EC,3E,33,FF,18,E7,05,5C,32,8B,30,35), + wwH16(FD,12,13,2D,74,79,05,99,E2,2F,EB,4D,DA,3B,D7,42), + wwH16(EB,D1,D7,93,4A,91,67,36,DB,83,B8,7C,32,5C,7C,6B), + wwH16(70,49,59,81,88,91,70,5D,18,97,E5,7E,85,13,D6,B8), + wwH16(1B,05,57,D9,9E,81,86,77,FD,BA,84,A6,AC,AE,EE,18), + wwH16(E6,C7,A7,85,F9,23,AF,24,82,93,95,D7,DB,74,31,0C), + wwH16(AA,D0,63,92,8A,97,E2,30,D5,DA,09,CE,7D,42,AD,AA), + wwH16(29,07,CD,D1,4B,62,4C,D5,2F,6E,9C,9F,FB,28,A7,4D), + wwH16(80,12,A2,D5,62,49,1C,20,C9,8F,C0,4B,83,AD,B4,B4), + wwH16(DE,8A,06,FC,A9,01,A7,F2,D6,80,17,BB,B8,24,99,2A), + wwH16(6A,8A,26,99,3E,61,53,CE,1E,04,BA,54,36,E2,BA,EB), + wwH16(CF,44,CC,E3,76,84,C5,07,4F,23,5A,BE,CD,3D,61,6A), + wwH16(BC,48,87,A8,65,E6,C2,DE,4A,75,8D,2E,CA,9F,15,13), + wwH16(DC,54,83,AC,AE,19,66,06,64,B6,D0,14,AC,B2,A3,EC), + wwH16(56,2E,77,D9,B7,5C,D0,21,5F,E0,F9,43,C8,2F,AC,CE), + wwH16(13,79,C5,F3,D6,55,8C,36,4E,F6,EB,73,7F,86,7C,6E), + wwH16(72,6B,7C,4F,40,91,12,22,65,C3,42,A0,A0,89,90,FC), + wwH16(9B,F3,83,26,20,8D,BF,D3,0C,9A,A7,97,4F,E1,96,E5), + wwH16(EF,41,EC,C2,B8,09,E3,CB,B6,CB,2A,F2,0E,5B,DD,FC), + wwH16(29,1D,42,81,8E,DD,25,F9,73,9A,66,90,C7,E8,3C,CC), + wwH16(89,F6,CA,4F,58,FF,6F,C4,F0,25,5D,F2,E9,A4,A2,69), + wwH16(CD,D3,56,C1,9C,D5,1F,65,2A,CC,37,27,3D,52,AB,51), + wwH16(DC,D8,F3,B6,90,74,19,ED,22,17,64,0E,79,F2,C6,5A), + wwH16(0F,84,4F,CA,26,B2,36,98,04,67,28,1E,6C,88,77,64), + wwH16(B3,DF,25,0E,3F,36,D1,74,D7,FC,51,1A,30,E6,54,53), + wwH16(29,C9,6C,18,7D,49,65,AB,97,F0,54,53,BF,53,F2,4E), + wwH16(15,8B,29,19,20,BE,A0,A8,CB,DF,F0,7E,4C,E7,67,92), + wwH16(47,E0,BA,E4,C6,6F,36,C0,03,C8,66,77,AD,C6,65,22), + wwH16(99,00,AD,7A,BE,98,26,4C,BF,F0,67,97,26,F2,65,95), + wwH16(BC,5F,F6,3E,1F,0E,42,4F,E0,20,C8,C9,01,39,00,73), + wwH16(3B,ED,DE,F5,91,75,E8,20,E2,E8,DA,9A,93,32,C2,88), + wwH16(B8,E9,8F,60,DD,5C,4E,BC,43,F4,0C,C7,77,43,C0,DF), + wwH16(BF,5C,B9,E2,19,C4,61,A2,69,CA,D2,6F,65,DB,20,D5), + wwH16(10,37,34,C0,7E,B6,E5,9E,BA,8E,1C,FD,21,FF,24,79), + wwH16(A8,CB,C1,96,77,46,76,77,EE,8E,06,BD,0F,C9,7A,F4), + wwH16(39,1A,3D,C0,A1,B7,97,A6,2F,31,6A,1D,F3,E5,FA,DA), + wwH16(D1,64,20,CF,4B,D7,1A,5D,7C,3E,08,E8,04,B4,43,F0), + wwH16(B9,4B,B0,68,51,49,0B,C9,53,32,EE,5C,E7,03,C7,4B), + wwH16(59,F7,19,3F,0E,8D,E5,0B,0A,79,13,0A,0A,7D,15,65), + wwH16(65,57,CA,E9,34,CD,C4,E2,40,79,90,AD,A0,D9,5F,BA), + wwH16(CD,59,CE,DD,42,07,8E,45,E2,B0,D3,FD,4E,DE,62,70), + wwH16(D6,EF,E5,55,10,D1,1D,DA,4B,CE,BD,E8,A9,6E,DA,19), + wwH16(74,5D,17,7D,40,9E,F5,BC,96,68,69,DD,4E,50,F2,27), + wwH16(1E,10,C1,6D,25,BB,A8,E3,2E,3A,58,5D,25,71,B9,C5), + wwH16(A3,7D,AC,E0,5E,BD,61,9A,C5,74,FC,DC,1B,13,30,54), + wwH16(DA,64,61,34,6C,0C,14,10,5E,DE,3C,8E,A2,60,4E,C0), + wwH16(35,E7,6A,F8,4E,5F,22,80,C3,8D,AD,80,CE,99,9F,8A), + wwH16(FB,E1,A1,C0,75,C1,85,B3,50,7B,20,79,EF,43,84,43), + wwH16(BF,E3,70,B7,2C,CD,52,71,E7,0E,49,ED,28,2E,9D,C3), + wwH16(96,D5,F9,C0,96,3D,67,FC,AA,81,6D,25,B6,67,AD,E5), + wwH16(69,B5,60,91,A1,EB,91,94,61,08,42,B5,7A,77,7D,98), + wwH16(9E,35,5B,75,AF,90,48,30,01,F9,68,1C,75,FA,D1,1F), + wwH16(AA,EF,C4,D8,79,75,9D,B3,BF,54,7C,C3,95,CB,1E,68), + wwH16(68,01,10,F8,CF,20,90,5C,E6,75,54,7D,26,25,E7,D6), + wwH16(2E,1A,A3,EF,09,E3,61,34,9C,50,8F,F6,C0,56,6E,D8), + wwH16(C1,54,48,BB,CC,94,FB,BE,F3,BC,C8,59,40,C3,F2,02), + wwH16(EC,88,86,93,35,EB,B3,49,A0,23,D1,F1,05,67,E1,83), + wwH16(98,17,7A,AD,C6,2C,50,8F,34,D6,0B,63,C6,86,B1,D6), + wwH16(BF,01,51,3D,07,EA,B8,31,B3,CF,A1,D4,E5,09,77,4D), + wwH16(F1,DE,FF,E6,3B,3C,F0,66,A7,62,78,48,0D,47,9A,70), + wwH16(3A,9B,FE,78,24,AA,19,37,06,D7,46,11,77,2E,76,39), + wwH16(53,A2,B8,1C,B0,3A,53,06,EC,88,B5,21,5C,65,1F,22), + wwH16(02,15,6F,AA,11,4D,4C,F2,81,E0,17,DC,BD,C4,B0,68), + wwH16(19,16,0B,D7,BA,CD,CD,CB,B8,6A,F9,F7,D2,5C,2E,24), + wwH16(52,38,96,C7,21,F6,5F,C8,29,AC,25,99,50,73,DC,D1), + wwH16(02,7C,E6,E5,3A,39,59,E1,16,4A,76,EA,75,07,29,E1), + wwH16(4D,15,98,E4,56,73,FF,31,0E,86,29,61,CB,B5,DA,F2), + wwH16(5F,00,DB,2A,D4,6D,D3,7A,80,49,66,DC,66,85,1D,F9), + wwH16(9E,BF,AD,77,27,21,3A,DA,D6,30,58,EE,8A,83,BA,11), + wwH16(3E,68,6E,88,04,55,51,15,35,77,A1,AB,CD,D0,03,79), + wwH16(14,5D,FA,58,EA,6E,5E,86,B3,36,FD,33,10,02,FC,0E), + wwH16(F9,F5,DE,F9,AF,BB,75,BF,BC,19,4A,2D,37,BA,A2,E1), + wwH16(47,F3,16,9B,D4,61,14,E1,9E,6C,5D,2E,74,9E,26,EB), + wwH16(EA,84,F4,76,B4,C2,D0,1E,3D,EA,F9,CF,B8,5B,B8,82), + wwH16(4B,29,97,B4,B6,6B,E8,09,5B,45,7A,C9,A1,39,62,19), + wwH16(1D,1D,4F,04,87,98,86,FA,87,66,A4,0F,46,50,70,AC), + wwH16(E6,6A,F6,1C,45,89,4F,47,47,28,AE,D0,A8,59,54,41), + wwH16(F8,C8,3B,A6,4D,36,7D,8B,05,FA,35,42,2A,40,49,B9), + wwH16(89,05,E1,03,44,31,B5,FD,66,33,05,C1,62,8D,38,8D), + wwH16(09,DB,1F,99,38,61,70,AE,D8,75,C9,B6,56,05,E5,19), + wwH16(47,A7,89,E3,5F,D0,A5,20,F8,D3,23,2F,69,99,BD,F7), + wwH16(E1,1E,4A,3E,40,CC,BB,77,C4,5F,7A,84,43,34,F4,B6), + wwH16(98,E1,04,98,01,7B,A7,61,A6,3B,7C,A0,E2,47,F6,41), + wwH16(98,BC,9C,0B,8B,4B,61,3C,B7,13,7B,36,AC,40,11,C1), + wwH16(46,EB,57,DE,2A,B5,5A,39,1C,F9,F8,FC,A8,55,3A,83), + wwH16(22,F2,0D,83,52,1C,C8,6B,DD,CD,51,22,C4,BD,7F,91), + wwH16(10,09,7A,44,E1,D7,C5,D9,66,C7,69,34,DB,86,53,03), + wwH16(A2,B4,6C,DD,48,7C,A2,4D,88,67,5F,3B,BC,65,5B,4E), + wwH16(38,1F,D9,D7,A0,F7,C5,E4,BF,1F,F2,B2,B3,81,22,CE), + wwH16(92,84,39,16,90,4D,A6,CB,D1,8A,52,5D,11,EF,7B,94), + wwH16(72,39,BC,BC,C6,F2,08,90,3B,36,48,61,76,CD,FE,50), + wwH16(45,B7,12,14,62,66,4B,59,C2,49,D1,1D,77,AA,97,7F), + wwH16(E7,67,B1,C9,61,D0,0C,A6,6C,22,31,43,83,38,CA,6D), + wwH16(86,3E,75,AC,A0,C2,7D,96,81,A1,F6,9A,85,1D,CB,45), + wwH16(D1,8A,6C,08,12,58,00,24,11,72,8B,FD,97,B2,FF,DF), + wwH16(47,DB,1A,F7,5B,B7,D9,D8,3A,29,EC,EA,16,2A,60,3D), + wwH16(CD,23,32,7C,43,3F,9B,B1,13,B3,EF,78,57,D9,33,C0), + wwH16(2A,7E,F5,4D,FE,40,BF,7F,32,35,F7,DD,9F,1A,5A,89), + wwH16(19,B1,9E,25,7B,B8,69,45,7C,6E,6C,56,0D,F3,F0,E7), + wwH16(A7,B3,D4,68,49,BE,31,92,B4,2E,74,7D,2F,51,42,32), + wwH16(1F,22,E2,4A,94,56,82,E9,F5,83,37,2C,E0,D6,D6,67), + wwH16(A0,1C,FA,CE,92,A3,99,C2,02,74,C8,B5,27,EC,E9,27), + wwH16(E9,04,5B,13,C0,F1,C3,18,32,2E,8F,8F,9A,05,F1,EA), + wwH16(FE,A9,E7,52,7F,C5,81,6F,ED,50,35,82,1C,BC,64,34), + wwH16(DC,42,07,EA,FD,80,BF,90,B9,22,EB,1B,E0,E6,BB,4F), + wwH16(82,0A,CA,DF,96,D2,62,40,C2,5C,A0,D7,79,C4,6D,BF), + wwH16(BB,53,7A,BD,A4,4A,09,1A,94,4C,92,49,78,93,4C,77), + wwH16(9F,4C,EB,04,29,A5,3F,4D,7D,A2,28,45,D3,8D,6B,39), + wwH16(61,97,D4,CF,B1,0C,32,A5,D2,75,4F,0C,47,90,63,6E), + wwH16(FE,C5,8D,21,79,B3,48,E2,7F,FA,0C,E2,C5,89,ED,B5), + wwH16(58,93,5B,18,CC,24,6E,FD,38,20,34,F1,AC,98,6E,62), + wwH16(67,A1,62,05,BA,72,87,8D,8C,83,0F,D7,03,97,71,6F), + wwH16(32,02,21,5F,D0,8C,7B,71,53,C6,F9,DD,65,45,8E,C0), + wwH16(63,50,B5,C3,7C,01,23,DD,9B,DF,C8,1D,82,F9,F6,07), + wwH16(3C,C4,D6,E4,5A,3A,B3,6A,BE,DE,70,CA,4D,1A,D3,B6), + wwH16(CB,F0,CB,8D,8B,3D,AF,95,AA,3E,BB,C9,32,DB,15,01), + wwH16(E9,B3,07,89,67,FE,29,0C,70,E6,90,6B,74,43,BF,2E), + wwH16(E1,02,D4,6F,9E,F2,35,83,8F,3C,A3,5B,61,61,63,7D), + wwH16(2E,DF,62,57,57,DE,AF,80,F6,A7,88,A7,E2,B2,29,7C), + wwH16(4E,45,E4,3F,89,F5,55,28,13,7C,BE,38,86,D5,D8,87), + wwH16(29,1E,E5,C1,C7,66,A6,BE,06,AF,D3,B2,CF,21,41,4A), + wwH16(3A,E7,08,64,82,99,5E,F7,74,63,CC,3C,6F,64,71,D8), + wwH16(6A,73,5F,5C,CE,D1,DA,A0,C2,C8,8C,AA,E7,55,9D,BA), + wwH16(6A,B4,4A,30,C0,30,B6,FB,BD,CA,39,EA,AC,39,B6,DC), + wwH16(73,4F,88,27,35,E2,C0,9F,0A,6B,00,1E,35,00,86,98), + wwH16(22,72,A1,00,4D,CD,8A,9F,DD,55,30,E8,87,00,53,7D), + wwH16(7C,54,54,57,11,92,BC,70,18,AD,A0,01,7F,89,20,7B), + wwH16(BE,E9,B2,5B,7B,DE,34,D4,15,F4,B3,FB,95,A6,B4,5C), + wwH16(63,58,8C,85,BC,84,89,48,AE,49,BF,A9,48,04,89,64), + wwH16(67,5C,0A,81,CE,01,6D,F9,97,C4,E0,7A,A0,9B,6C,7E), + wwH16(B2,5D,B1,8A,5A,80,1A,A9,8B,53,86,F4,B6,1D,4E,95), + wwH16(DF,A0,1D,72,DF,F8,10,89,10,08,AE,13,DC,A8,FB,2F), + wwH16(FC,10,32,3A,02,CE,D4,9D,BF,E8,B9,4E,21,14,EB,A8), + wwH16(6B,D3,B4,74,B2,BC,86,2F,D3,AF,A9,5A,CD,7C,D0,C5), + wwH16(69,22,D7,A1,A1,63,E3,12,F8,D6,DF,02,82,3E,FA,8D), + wwH16(C3,63,BB,4F,40,3F,DB,FD,2F,BB,17,4F,32,8B,BA,EF), + wwH16(08,5E,57,0A,1D,4B,04,E6,9D,97,EC,0F,B7,CA,51,6E), + wwH16(59,8A,B9,D1,09,32,18,75,D9,43,63,BE,B1,89,8B,FE), + wwH16(B7,6F,0F,2F,07,57,65,94,A5,EC,DB,40,AF,D8,22,3E), + wwH16(B6,DB,DD,F8,DF,25,CB,61,4A,3A,40,05,5C,11,75,7B), + wwH16(92,DC,3D,17,95,F2,71,B5,68,97,E4,47,CC,5E,42,9B), + wwH16(C8,A5,1B,D4,86,8B,18,E6,BF,9F,56,87,53,15,02,FB), + wwH16(8A,DD,C8,C9,9B,BA,CD,90,65,B1,95,F8,0E,4B,FF,A4), + wwH16(7E,94,F3,97,D4,36,26,B6,BE,F2,8B,7B,F3,45,D9,90), + wwH16(54,80,BC,0C,13,97,F4,BC,4E,A8,77,7E,3F,CD,A2,85), + wwH16(F2,AD,41,19,2C,12,4F,E2,3D,71,83,D9,A6,2B,58,BA), + wwH16(10,4F,A7,F7,CE,4A,02,69,B2,64,0F,48,13,DB,43,53), + wwH16(F2,77,A6,7D,AB,23,48,97,0A,97,7A,77,70,C0,33,6B), + wwH16(EE,69,FB,12,75,76,34,12,7A,BE,F8,CA,49,95,0A,E9), + wwH16(5D,91,67,6A,57,B1,5A,88,43,46,8F,C2,17,FC,D1,01), + wwH16(CC,DB,3E,6E,FB,4F,29,89,CE,E7,12,2C,F8,58,B8,FC), + wwH16(4C,3F,E6,26,E7,C8,E4,F8,50,FF,ED,D6,8A,A7,20,B0), + wwH16(69,BC,3B,44,5C,4D,B0,9A,FD,60,52,4B,43,E8,65,FA), + wwH16(74,B5,0C,13,3D,63,4F,7B,F0,CB,EB,1F,3F,E3,84,FD), + wwH16(F8,35,2F,1A,1A,4F,28,CE,8B,E2,06,F3,05,47,55,39), + wwH16(02,11,6B,C6,02,A5,9B,3A,42,49,E0,79,3B,D8,7C,6B), + wwH16(D6,8C,3F,C8,36,56,1D,80,41,F9,30,E3,CE,01,1F,82), + wwH16(A3,40,B2,BD,7D,6C,0B,3D,43,6E,89,8A,F7,44,02,1A), + wwH16(9E,FE,25,3A,E6,B8,CA,4F,31,BA,05,72,C1,5A,5E,58), + wwH16(61,6F,8B,98,DB,E5,1F,E8,D5,D6,75,10,24,88,76,15), + wwH16(6D,31,04,75,1B,A2,2B,D6,D9,BD,74,C3,53,39,4B,46), + wwH16(72,79,6B,EA,ED,D7,CB,AB,D0,B4,F1,E8,F1,6F,FA,1E), + wwH16(AB,FC,AF,01,84,9C,FA,BE,E5,2A,C7,42,7A,9F,92,FF), + wwH16(1A,E5,BC,64,06,EF,EF,FF,23,92,CC,D6,B5,D9,38,B2), + wwH16(1A,8B,F3,19,E8,EE,EF,B1,62,76,5D,4C,45,BF,20,D2), + wwH16(0A,4E,12,B9,0E,52,C1,4F,8A,B7,4D,18,05,DC,7E,FF), + wwH16(73,17,31,B9,B2,78,2D,A5,FA,5D,5F,95,2D,79,40,8E), + wwH16(93,CE,C0,C6,4C,75,41,30,20,43,72,2C,D8,89,F0,5B), + wwH16(47,15,E0,65,4D,9E,D3,41,73,0A,9B,A0,6E,6B,08,A9), + wwH16(CC,7A,E7,C8,F0,74,4B,E1,52,E2,9C,E1,A7,6F,92,A3), + wwH16(95,C5,3A,E4,C4,75,51,D5,02,40,0E,98,09,6F,7F,35), + wwH16(91,38,88,60,D3,F5,77,D9,57,D5,6A,3D,E7,95,78,C5), + wwH16(2D,02,C0,18,08,23,89,A5,A5,C2,FD,7D,52,FA,83,33), + wwH16(81,04,9A,FE,CF,46,D7,2F,BB,F3,B0,7C,5E,F8,B8,97), + wwH16(C9,BF,03,BD,B6,78,C3,95,BF,30,4D,D8,12,C7,BF,6C), + wwH16(69,04,4B,0A,9D,45,A9,8D,5B,1C,E4,E3,F7,D2,10,11), + wwH16(43,81,2F,02,E0,F8,F3,62,A9,DD,38,91,E8,24,9C,53), + wwH16(79,EF,77,3B,77,D9,8B,D5,2E,A8,52,82,F4,B7,31,26), + wwH16(26,06,15,56,50,C6,26,8A,13,5D,8C,CD,07,D9,85,98), + wwH16(8B,1F,07,BC,98,A7,68,59,61,07,A3,92,C8,4E,14,C6), + wwH16(53,6C,8D,36,01,7B,A5,DF,EC,28,3B,ED,37,0C,A3,59), + wwH16(D3,E9,DA,8E,56,BF,AF,C9,75,10,DE,D7,D0,1A,5B,9F), + wwH16(EF,0F,90,CD,A5,60,91,B6,7B,56,97,1A,DA,81,CE,32), + wwH16(4C,3E,8A,89,2A,E8,A7,49,FD,C4,BE,C8,32,B2,06,DB), + wwH16(E0,93,85,34,B1,77,34,34,6A,7E,19,F6,B7,C7,83,4E), + wwH16(77,CC,66,CB,4A,DB,15,58,97,57,84,4E,F8,EB,BE,F2), + wwH16(30,C6,9B,1A,72,FD,F9,31,65,AE,A5,3E,C1,D7,64,A8), + wwH16(47,C2,30,B7,3B,E5,F4,7C,5A,CD,8E,26,50,FB,AB,FB), + wwH16(2D,70,AE,89,E3,20,D7,32,D1,8F,8A,CD,DF,B1,26,C8), + wwH16(B7,15,40,B5,CB,E2,CA,65,7C,22,D0,A9,C5,17,54,89), + wwH16(81,87,7C,E7,0F,46,0A,4E,30,1C,2D,38,81,D5,AF,E6), + wwH16(7E,AC,EC,15,DC,E6,CB,8F,0B,3E,19,72,4D,6E,45,D8), + wwH16(68,F5,3C,24,C5,B1,D2,8C,2D,C7,32,14,88,4B,A8,BE), + wwH16(78,F2,C3,67,EF,55,4E,16,1C,9E,F9,9A,05,B1,BF,79), + wwH16(75,9E,CB,A3,DC,39,87,5D,69,DF,AB,72,2D,00,DA,04), + wwH16(F5,1A,0F,99,96,5D,5F,6E,25,C2,8C,73,7D,17,02,7A), + wwH16(1A,F8,23,4B,D4,2F,41,35,4B,C7,E4,B2,7D,72,7E,52), + wwH16(17,43,2A,89,1C,64,39,0B,BD,52,5F,01,60,76,3E,3D), + wwH16(A1,DE,55,92,22,6E,48,98,CB,42,67,64,D1,A6,2C,E9), + wwH16(8F,3B,5F,23,84,44,F9,87,0A,13,AB,5B,1F,59,1F,6D), + wwH16(DC,D2,3F,91,7B,0B,24,D1,F6,81,6E,83,C1,15,A2,8C), + wwH16(DC,45,3B,2A,80,43,85,53,B0,A7,23,40,9F,B7,35,A1), + wwH16(F7,6D,3D,F9,B1,7D,F3,BA,25,51,65,1E,AE,07,EC,AC), + wwH16(8F,51,CB,AA,72,94,10,6A,B5,4C,6A,D4,4A,4B,21,04), + wwH16(14,0A,58,4E,59,E6,54,74,A2,D0,CA,7E,A0,68,D2,29), + wwH16(15,F8,6C,B5,0A,FE,2C,58,D8,51,94,9F,C3,F0,3E,C4), + wwH16(13,FA,EB,4E,C4,00,BD,CE,E0,FB,BA,FB,EB,0A,CB,A3), + wwH16(9A,69,D4,F2,CD,37,CF,DB,57,7F,9F,1F,6C,C6,0E,4B), + wwH16(9F,41,DD,EE,A1,2C,43,2B,0C,7D,2D,36,1F,32,66,67), + wwH16(E0,BC,B0,CA,87,22,E2,14,B7,D1,EB,E9,79,3E,9A,AE), + wwH16(2C,45,CD,62,26,73,68,38,27,D8,9E,D0,3C,B9,33,14), + wwH16(3F,47,88,69,50,B4,16,EB,F3,A9,03,32,23,80,9B,B3), + wwH16(B2,70,72,87,B1,E2,2F,05,D1,4D,D2,ED,4F,7B,4E,4A), + wwH16(8A,D9,5B,28,49,6A,D7,95,0D,8B,57,03,FC,AC,BE,E4), + wwH16(22,43,F2,F0,17,FA,D6,EB,49,45,A8,70,E3,FE,D3,95), + wwH16(0A,47,CA,07,4D,B9,BC,A6,00,AE,D9,41,37,99,90,60), + wwH16(32,CA,41,01,44,27,45,54,1C,69,B7,29,94,CA,6A,5F), + wwH16(B1,79,F5,E3,51,12,89,2F,91,95,B5,A9,48,0B,9D,8F), + wwH16(51,FF,26,7A,40,AB,7A,B2,96,9B,51,80,ED,2B,F6,41), + wwH16(13,77,21,7C,59,B0,01,73,C7,1E,7C,DF,FE,91,3A,DD), + wwH16(FA,F3,0D,F3,79,9A,BD,CC,AD,71,94,9E,47,0D,6C,80), + wwH16(ED,21,EE,2F,3C,C1,C1,40,E7,58,ED,37,14,57,5A,4E), + wwH16(AE,B2,F3,14,01,AE,7A,83,3F,99,D5,78,85,60,3D,75), + wwH16(6E,10,28,83,72,D1,DA,33,09,0B,EB,C4,3D,A1,E6,4E), + wwH16(BF,1D,F0,7C,0C,8E,04,A7,E7,42,33,F2,D5,70,87,E3), + wwH16(8D,5F,5F,86,A7,A4,58,F4,32,37,D6,78,6A,18,E9,64), + wwH16(FA,B2,42,D6,D9,34,CB,B6,7F,E8,89,2D,FB,4B,25,B2), + wwH16(B9,7C,F6,78,DD,A5,7C,F8,44,92,D6,F7,E9,6A,2A,0C), + wwH16(D7,FC,58,4B,FC,B3,F6,B4,41,57,F8,49,D8,6B,69,D8), + wwH16(5A,11,DE,92,2B,20,15,A0,24,AC,65,44,35,93,8F,F7), + wwH16(89,76,EF,80,C2,19,56,3C,47,83,93,E9,0B,EA,EC,50), + wwH16(2F,E3,A2,58,3C,A9,A7,63,A7,60,E4,E6,E6,60,26,57), + wwH16(30,59,31,2C,0D,2C,C9,55,9D,96,8C,BE,17,DD,66,10), + wwH16(FD,DE,C6,91,1D,8C,9F,CA,49,7A,B9,7F,25,E4,3A,BF), + wwH16(ED,E8,58,27,60,00,51,B8,DB,AA,97,47,00,0F,AF,B6), + wwH16(E7,09,08,08,45,84,91,FA,D8,76,E0,57,66,C1,30,A7), + wwH16(6A,A8,77,39,14,7F,4F,F7,95,2B,AF,1E,02,48,0F,90), + wwH16(61,79,81,D2,EB,67,FA,58,60,C6,F2,D2,75,7B,12,45), + wwH16(FC,83,DA,03,58,0B,E5,19,11,F4,81,FA,EE,FD,6E,FF), + wwH16(94,93,ED,F1,46,6B,E3,5A,36,98,F6,47,A9,8E,B9,33), + wwH16(D1,08,FC,45,B8,04,BE,DE,AA,40,A9,27,9D,19,C5,0F), + wwH16(92,E4,96,3E,39,2C,95,B2,39,FE,15,22,38,49,13,73), + wwH16(E1,66,B2,08,89,F3,7E,C5,6D,C6,9C,E6,8C,0E,87,BC), + wwH16(D1,FC,C4,F6,D8,85,01,7C,AB,98,76,B3,95,47,DD,9C), + wwH16(48,0F,15,F6,3D,5E,F6,8F,C1,34,41,56,C2,CF,EC,7D), + wwH16(64,6A,70,BD,02,38,88,20,DF,52,A3,CC,54,D9,57,A3), + wwH16(1B,07,D6,DC,3F,3B,98,F5,08,0E,3A,3F,08,24,6B,D8), + wwH16(6A,9A,70,23,E3,B1,1F,9E,91,9E,20,1C,2D,A6,0B,0F), + wwH16(16,18,51,CB,93,C4,52,02,29,41,2A,C6,25,08,7B,04), + wwH16(F4,28,35,60,D4,10,9B,64,80,59,9F,BB,50,FD,64,D0), + wwH16(E9,6C,8C,E8,DA,65,E8,6A,DB,E2,49,56,EA,38,EE,0D), + wwH16(52,CF,61,1F,5F,1B,D9,91,65,D9,FA,3D,CC,6F,A2,71), + wwH16(61,D1,3D,BE,E8,04,C9,3B,04,1E,4E,2E,4E,E0,8F,1A), + wwH16(A1,25,F0,D0,76,5F,B2,CF,FA,63,2B,6B,05,28,7F,11), + wwH16(4B,CD,66,B8,A3,11,97,22,00,BB,B3,8C,4C,05,29,06), + wwH16(F6,28,B3,C0,C3,D5,C6,E3,33,1B,61,EF,24,BE,FA,38), + wwH16(57,6E,67,0E,BA,EC,F4,CB,7A,D4,9D,F6,FE,D9,4D,3F), + wwH16(9D,9E,D6,A2,39,ED,A9,93,FD,D7,47,39,9C,91,8F,FA), + wwH16(30,94,41,7E,B7,6E,4F,F5,C5,A2,F9,34,AA,6C,9C,E6), + wwH16(A0,F8,0F,4D,41,1B,86,11,55,31,03,4A,09,1C,CD,93), + wwH16(DC,1A,E1,55,87,CB,D4,84,75,96,C0,52,B7,31,C7,D6), + wwH16(52,79,DB,5D,79,01,0E,32,87,EE,AA,8D,15,54,34,BE), + wwH16(8C,A0,20,B9,B9,8D,24,C2,F4,73,A6,44,43,3F,E2,D6), + wwH16(27,4A,E3,0A,2B,3E,9E,01,7D,18,7C,12,8C,C1,35,BC), + wwH16(FF,84,07,3C,CA,5C,11,10,16,08,2F,40,04,B2,C6,E4), + wwH16(DB,EB,58,5A,D7,AF,85,33,CD,70,30,8F,B4,5C,9B,7F), + wwH16(28,69,BE,D8,06,16,30,CE,F8,F4,86,6A,B3,EC,F1,A4), + wwH16(61,9D,13,F8,7E,16,B9,54,21,04,E9,1A,46,25,27,26), + wwH16(A8,24,D4,8E,AC,A9,34,65,75,EA,EA,0B,4F,07,CB,F4), + wwH16(0C,47,5A,D3,AC,51,DC,22,12,79,2F,60,96,9A,09,41), + wwH16(9E,07,BB,FE,11,FF,E2,88,49,85,B4,6B,71,08,5F,FD), + wwH16(6E,E2,42,DB,62,D9,94,DE,85,A9,78,76,83,85,22,7D), + wwH16(C8,C7,98,80,D9,DD,E0,A8,18,D7,68,0A,4E,03,A8,4E), + wwH16(E8,19,6F,DD,B4,02,D8,F2,82,68,B7,BA,CA,AB,86,3F), + wwH16(E4,E6,6F,90,92,1D,F5,20,09,22,C0,50,8F,EC,34,0A), + wwH16(21,E8,A1,DB,BD,B4,E9,C4,5C,E5,D5,15,88,A6,9E,AB), + wwH16(9A,B1,1C,58,8A,76,BB,CB,D6,6A,1A,93,E2,9C,17,3D), + wwH16(31,A9,D4,61,33,C4,22,77,82,0C,D2,BF,97,3D,BC,09), + wwH16(7C,4D,9C,1B,5A,97,34,88,A7,DB,7B,1F,95,90,55,49), + wwH16(FA,ED,80,1A,FE,A4,52,0B,A8,21,9D,B6,18,6A,AF,E3), + wwH16(99,48,61,54,FA,19,39,24,6A,C2,B2,D7,4E,8E,C3,83), + wwH16(40,D6,F3,24,15,EA,60,15,FC,85,24,08,CE,A3,E8,DB), + wwH16(18,AF,49,6B,C7,6E,CB,85,9C,8A,5C,18,16,10,18,31), + wwH16(B0,33,8A,52,3E,3D,EF,26,DE,61,30,8F,A0,70,8C,74), + wwH16(95,95,A0,05,4D,1E,E7,65,CB,38,8E,55,6A,D9,23,FD), + wwH16(98,C2,FC,78,EF,69,60,61,E7,56,AE,75,D4,6E,6D,B9), + wwH16(5B,55,F1,98,50,69,19,06,B9,09,E2,E0,BE,A2,2C,7C), + wwH16(71,35,ED,A7,99,9F,D5,5D,B7,E2,47,F5,E8,0D,D2,68), + wwH16(28,E6,E1,5E,8C,92,77,39,CC,1D,01,1F,9F,1C,BE,84), + wwH16(03,AE,CD,0E,63,D7,C4,15,04,9E,8B,7D,3F,5F,BD,98), + wwH16(AA,E5,CF,8E,F6,E6,22,B3,6E,E6,8B,56,20,1C,D3,FF), + wwH16(65,5B,47,66,7C,94,A1,D5,83,A3,EF,82,F5,25,AE,ED), + wwH16(35,D9,A8,2B,17,46,00,5C,97,28,B2,91,3E,E7,90,73), + wwH16(49,1A,06,68,2B,77,42,5E,BC,ED,4D,3A,F5,A2,B9,96), + wwH16(C2,68,21,BD,90,65,7E,19,E8,47,C7,85,A6,0B,02,50), + wwH16(9C,A2,EE,D2,99,8B,FA,57,3D,6B,45,2B,CF,46,DA,51), + wwH16(09,7F,55,53,91,F6,3D,2F,35,A2,59,BF,82,3B,CC,75), + wwH16(78,85,94,07,8A,20,B7,22,3C,AF,8E,C1,EC,EE,A7,33), + wwH16(EC,3E,F9,46,22,FF,C2,F5,3C,61,46,09,6F,F9,EF,66), + wwH16(91,65,0E,77,00,F7,6F,92,05,87,29,C7,7C,F0,DA,43), + wwH16(12,E0,53,0F,BA,36,30,23,DC,3B,9B,C9,E0,C2,5B,A3), + wwH16(5E,CA,4E,33,20,05,5D,E9,3E,44,06,D3,79,C8,E9,5D), + wwH16(99,F1,98,5D,D6,EE,B7,E5,47,23,73,A2,B0,F9,10,60), + wwH16(58,07,FF,D4,64,75,05,B4,50,A9,13,C4,AE,66,BD,E9), + wwH16(A2,B6,90,25,FF,39,D1,74,BB,59,11,D3,3B,A8,4C,6D), + wwH16(6D,F1,88,E0,3B,8C,8D,86,6E,B0,A4,88,AC,DE,77,03), + wwH16(E4,55,4A,F5,FE,88,7D,67,43,07,A4,31,62,32,1D,38), + wwH16(C7,A5,A9,A6,5A,4B,CD,63,DB,79,BB,17,E3,EC,77,09), + wwH16(D2,D7,BE,E3,5C,DC,D2,4C,9C,68,64,47,47,81,81,A6), + wwH16(B5,5F,F8,AB,56,75,55,E3,13,62,6B,71,CA,62,59,16), + wwH16(8C,0C,55,31,57,AB,88,4D,D4,63,78,2C,8D,C1,B2,86), + wwH16(7D,D0,C6,B3,2F,B2,50,40,B0,C9,BB,EB,AF,56,06,A6), + wwH16(79,29,45,48,C5,F6,1C,EE,AE,87,E4,B3,C7,64,05,86), + wwH16(A8,44,1A,6B,6A,F1,CF,4D,EB,46,2E,71,4B,45,38,CF), + wwH16(55,EF,1B,8C,8B,BD,D1,35,39,5D,6D,CE,B7,70,88,06), + wwH16(1A,2B,E6,98,63,86,76,28,E8,07,5D,1E,DB,70,17,CC), + wwH16(D1,70,57,9B,E2,B9,01,F7,3B,95,F5,11,A9,70,E2,68), + wwH16(5A,7B,E1,50,35,D1,D5,5C,05,B4,46,2E,1F,C6,F5,84), + wwH16(C2,72,8C,1D,0D,87,88,17,7C,5B,C7,3B,15,A0,01,07), + wwH16(50,1C,44,B0,46,52,C8,93,36,AB,99,43,EF,8C,5A,5E), + wwH16(B1,EF,65,23,B0,7D,14,57,8F,63,77,6D,ED,DA,23,9F), + wwH16(7C,D5,6A,0B,3C,5B,E7,90,8D,19,FA,C7,09,68,43,B2), + wwH16(A4,E6,7E,46,24,09,04,49,C8,09,A8,55,8D,B4,8A,52), + wwH16(3C,E4,16,4D,C9,0F,1C,5B,7C,23,40,7F,F2,A7,94,DE), + wwH16(CD,32,76,E4,AE,D2,1C,27,6E,79,54,FF,B2,DB,76,7A), + wwH16(E2,3B,82,0D,7F,BC,12,06,69,77,BB,5E,C2,2E,31,34), + wwH16(49,A2,39,C4,37,95,9E,B8,AD,34,76,5D,CD,0A,34,2D), + wwH16(AB,27,09,2E,4E,25,17,AF,2F,14,76,2B,37,74,3B,9D), + wwH16(7B,C4,33,CB,E9,89,A2,F3,C7,AD,1A,B5,2E,44,20,44), + wwH16(D5,E2,DA,8F,2A,2D,2E,D4,D2,6E,2F,34,37,F4,29,4B), + wwH16(7E,25,45,73,41,0B,42,7F,4B,9E,42,73,82,1A,6B,EF), + wwH16(B7,01,C0,D1,D5,9C,80,E5,1F,AE,79,82,CA,2E,B7,E7), + wwH16(2B,BD,CE,B2,AF,24,D2,BC,1A,5D,24,D1,BF,CF,80,8A), + wwH16(20,77,39,30,B6,AD,A9,59,BF,24,9C,BD,17,A2,B4,85), + wwH16(A6,69,AE,81,C1,65,BC,E5,11,9B,25,DC,49,14,31,AC), + wwH16(8B,5A,44,EB,BC,65,BC,41,A6,49,C2,18,F7,33,53,EB), + wwH16(5B,93,50,AB,9A,09,35,47,B2,EF,7B,CF,E0,0C,C0,67), + wwH16(20,E7,2E,1F,40,47,45,45,8D,3C,E9,E4,BA,79,88,5F), + wwH16(23,78,BE,49,59,30,AF,11,EF,1C,C3,DC,E3,D1,28,A7), + wwH16(AB,E6,47,02,A9,55,6E,F9,31,FE,B9,93,AF,C2,E6,31), + wwH16(20,56,F3,41,18,40,63,31,1B,31,37,63,49,12,D0,74), + wwH16(E9,E8,13,78,D8,FB,03,20,A9,A0,FC,49,3F,DA,ED,C5), + wwH16(15,FD,66,FB,7E,AC,90,46,C9,3E,B5,7E,79,7A,5B,6B), + wwH16(DD,66,B6,C9,89,1E,3D,F1,10,C7,1A,EB,1D,2F,A3,C1), + wwH16(38,44,E4,24,9D,14,A7,A1,F3,9E,ED,D6,ED,02,A0,13), + wwH16(27,6D,CB,01,18,33,8C,D4,64,A9,33,BD,4B,25,6B,D4), + wwH16(6A,03,8C,0D,F5,16,DD,B4,C8,0F,A0,9D,23,0C,6E,C6), + wwH16(97,6B,9C,29,FE,67,A8,93,B2,C0,C5,48,9D,EB,55,25), + wwH16(B7,A4,80,5D,4C,C2,37,94,94,B9,BF,FA,FA,B0,64,6C), + wwH16(4A,8C,75,5C,92,0D,00,D2,C4,0E,65,D3,4A,06,21,95), + wwH16(01,86,DD,21,94,F0,63,D0,F3,25,08,B1,7C,21,91,99), + wwH16(17,F9,EA,4E,39,76,6A,24,42,73,63,E3,25,21,17,48), + wwH16(71,7E,31,9E,1E,F7,39,87,02,E6,97,19,9C,19,0E,06), + wwH16(76,CD,AF,17,E7,2C,A8,A3,C4,31,E6,3C,14,37,AE,F0), + wwH16(9E,0B,F7,24,36,31,2B,B3,F9,96,17,B2,3F,F0,23,AF), + wwH16(E8,98,31,C5,43,9A,AC,1D,3C,C4,FC,4A,A5,78,EF,2D), + wwH16(71,4F,55,EB,26,59,A2,9A,89,0E,3E,D5,41,D0,18,20), + wwH16(9A,26,3A,B8,47,80,36,60,FF,6F,F6,93,14,0C,56,BE), + wwH16(82,D6,75,6B,2A,5F,BD,0E,53,9D,1F,F1,F0,DB,4F,4E), + wwH16(B3,86,70,09,88,F8,FF,94,65,FE,98,D6,66,E2,D1,E3), + wwH16(C7,54,CB,59,DC,C0,21,B4,2B,3A,94,EE,89,8F,A4,D7), + wwH16(F3,11,E4,7D,B0,6E,4A,80,E2,4B,42,84,82,D1,80,BC), + wwH16(F7,1B,17,0A,CE,DD,93,01,BC,24,1C,93,00,8C,D0,4C), + wwH16(C5,9B,13,62,8B,5B,86,50,2E,6F,D3,69,58,07,B5,10), + wwH16(99,42,B5,CE,97,5E,1F,BC,FC,05,23,44,6C,B2,3D,1E), + wwH16(D7,03,FD,F2,79,A7,7B,BD,FB,6C,51,AE,45,27,29,54), + wwH16(F4,A5,11,06,06,1D,4D,77,DD,46,D2,5B,45,8C,29,BE), + wwH16(BE,16,4B,B8,91,7E,92,3D,DE,B2,5D,5D,D6,18,2D,EB), + wwH16(5B,06,95,A4,7F,57,72,38,9C,BC,72,A2,21,D2,01,BB), + wwH16(47,3E,45,B8,18,97,FD,A2,C5,7F,C7,6D,65,22,10,B7), + wwH16(47,4F,36,06,0C,FF,7B,32,F3,B9,43,2D,13,F5,7A,BA), + wwH16(85,99,3C,F9,3F,D7,0D,0E,21,20,83,B7,A2,5C,60,64), + wwH16(45,D7,1F,A3,B5,74,22,D6,C1,60,CC,AE,95,2D,46,A0), + wwH16(8B,9A,63,58,C8,66,2C,BA,FC,35,B1,C0,33,8D,2C,68), + wwH16(DA,EC,65,32,64,F1,D9,9D,00,61,BE,10,28,2E,34,49), + wwH16(3C,99,FB,A7,BE,4D,FC,F3,DF,18,18,4D,FE,42,BD,69), + wwH16(D6,31,02,A1,C9,11,FD,37,82,70,61,20,6F,8A,23,69), + wwH16(5F,EA,25,E4,F6,80,3C,F3,8C,49,0D,DC,E7,F5,CC,E0), + wwH16(5C,25,E4,4C,DA,29,59,3D,DF,7C,B0,DB,E7,D2,38,B6), + wwH16(C3,85,3D,DA,84,A2,EE,61,CE,B2,97,24,BA,32,0D,5F), + wwH16(21,A6,12,40,0D,F0,42,8F,35,96,E8,46,92,30,23,37), + wwH16(25,4A,ED,6D,86,0C,6A,00,32,05,0D,B5,1B,E2,59,D5), + wwH16(67,C4,53,84,DD,C3,78,6F,D5,E5,F3,DA,99,BE,DE,C3), + wwH16(D8,BA,3A,17,A3,EB,A8,52,93,35,43,00,CC,99,FA,60), + wwH16(76,70,EC,F1,82,9B,64,1B,7B,BD,32,89,90,7A,94,F4), + wwH16(28,7B,BF,17,7D,7A,DB,BA,8A,58,73,57,18,62,F8,FF), + wwH16(4C,94,F7,74,1E,40,75,CD,C8,86,05,72,3C,02,B6,28), + wwH16(B4,CD,AD,10,62,F3,26,7E,95,02,78,E7,5A,FD,FD,B4), + wwH16(2C,F2,D9,0F,1D,C4,68,BE,28,36,BF,32,73,A3,63,E8), + wwH16(9C,68,AC,27,48,24,83,D2,25,AF,88,E5,9B,1E,CF,3C), + wwH16(46,F0,46,78,F6,D5,01,22,AE,57,6A,EC,CB,21,60,B7), + wwH16(EC,2A,EC,98,0E,5D,E6,F2,D8,2B,AF,86,DA,76,68,14), + wwH16(74,C5,98,2E,50,92,B5,9F,D0,8A,F0,DD,62,D0,F7,4F), + wwH16(FB,F7,84,4E,AC,53,D0,16,8A,0F,57,BD,2D,48,1C,36), + wwH16(6C,B7,95,FB,97,E0,ED,45,FA,A0,C2,BE,6D,47,4D,3C), + wwH16(15,CC,A5,4F,FC,BD,62,DA,81,C2,BD,00,DE,E6,63,DC), + wwH16(D0,DE,97,E1,5E,9E,42,0A,D7,28,CA,EB,02,41,9D,5E), + wwH16(05,54,B4,ED,07,93,03,66,10,6E,1F,4A,56,E9,3B,96), + wwH16(FC,95,16,E1,8C,DB,00,05,4D,30,5B,AF,10,C8,F4,79), + wwH16(CF,88,D7,14,40,06,1F,EB,D3,3D,FA,49,21,48,32,1D), + wwH16(2C,64,A9,50,0E,24,26,0E,23,9C,7F,62,1F,66,F3,48), + wwH16(76,12,A2,5D,B8,08,6F,EC,53,31,76,7E,59,AE,90,A1), + wwH16(AA,E7,A5,F8,99,C5,90,0B,C0,82,2A,42,E8,6C,64,81), + wwH16(15,8E,95,CC,A9,AB,43,B4,24,6F,82,5A,F8,68,C1,5B), + wwH16(2A,3F,15,2C,9C,3D,CC,C6,C6,25,CC,1A,50,3B,46,ED), + wwH16(D5,6B,46,F6,65,55,44,18,58,45,00,94,1F,CA,1C,2C), + wwH16(C2,6B,87,78,4A,CE,FE,84,72,88,22,ED,62,83,10,F3), + wwH16(38,36,33,75,13,9D,E1,5B,A8,4D,EB,DA,93,82,29,AE), + wwH16(E0,8B,3B,13,F8,31,A4,65,80,8B,7A,3B,4D,D0,E7,33), + wwH16(52,C0,B2,FB,94,AA,03,99,F1,62,82,31,5D,20,59,75), + wwH16(57,31,10,68,D7,1B,54,C8,91,05,15,64,7B,89,8A,ED), + wwH16(22,55,93,C4,1A,B9,DE,9E,9C,BE,0A,68,25,B4,87,51), + wwH16(0E,62,65,8E,AD,7E,18,04,35,9E,A3,F7,7E,78,15,7B), + wwH16(81,16,76,34,A8,C8,DD,E3,04,60,67,56,DB,A1,D5,03), + wwH16(A4,27,E2,1A,E7,B6,73,45,79,C5,27,6F,6A,5B,6B,D4), + wwH16(68,E2,A9,66,6B,A8,5E,39,10,5F,2B,BC,E0,DD,9F,34), + wwH16(77,A5,55,2F,79,63,EE,6C,77,96,F4,96,0A,22,02,88), + wwH16(96,8B,1A,23,25,E2,7C,11,BB,C4,5F,91,37,9A,01,DA), + wwH16(59,D0,EF,C9,B6,69,40,98,EE,CB,4E,A6,AC,61,3B,79), + wwH16(5C,DF,59,AA,D9,B6,D0,D5,1D,73,9E,B1,47,9C,E0,77), + wwH16(31,51,39,8C,5E,48,F7,DD,58,8A,91,58,7D,BE,F0,4A), + wwH16(98,33,B4,30,6E,1B,06,3C,0F,31,D9,D0,2F,0C,23,D1), + wwH16(5C,E3,BA,15,DD,02,7B,06,DA,5B,60,45,EF,09,43,70), + wwH16(EA,6B,8C,44,B4,11,9D,89,EE,69,98,46,E8,15,1C,EE), + wwH16(4E,A9,CB,6F,E7,57,FA,AD,10,B7,ED,C2,4D,0E,86,73), + wwH16(DD,62,F8,0C,B9,F3,CE,C9,A0,6C,32,35,67,38,B2,4E), + wwH16(3B,F3,5A,7D,20,B0,88,FB,1F,50,62,DF,51,81,55,9B), + wwH16(78,F1,1B,A9,4E,85,1F,AB,C0,70,BA,69,28,93,1C,DC), + wwH16(04,9E,5D,C0,D2,62,09,5A,4B,9D,EE,4C,13,80,7B,20), + wwH16(A6,E8,83,EB,DF,46,B5,13,74,80,55,0E,CD,A0,68,0B), + wwH16(FB,00,B1,85,CF,CE,F3,9F,0A,91,1F,AF,BA,13,67,E4), + wwH16(16,A9,CB,EF,96,66,1C,82,1B,22,56,9D,F4,2A,57,8D), + wwH16(7A,EE,81,AD,D1,4E,48,E1,89,88,7A,2A,E2,12,43,17), + wwH16(AA,EE,D5,99,EE,FF,75,A8,6A,6E,AA,AA,48,CE,E2,E6), + wwH16(22,4C,C2,E5,4B,CD,C5,7F,AC,13,9C,1D,05,40,06,B2), + wwH16(8B,FF,C7,3A,3D,DD,0B,2B,43,B9,9A,EC,85,EB,17,13), + wwH16(9C,AA,2A,53,44,82,E3,42,75,7F,1B,D8,8F,25,F5,5E), + wwH16(3A,F6,7C,C6,BE,C8,9F,E5,7F,82,C1,BC,8D,5E,97,51), + wwH16(05,3D,D9,DD,C2,CE,28,06,48,54,4D,A8,F1,16,35,63), + wwH16(F0,6D,12,58,68,A4,81,90,AC,78,4C,D3,91,60,FD,F4), + wwH16(AA,15,87,5E,14,69,13,90,8C,7C,28,DA,81,D3,49,6F), + wwH16(57,01,96,50,09,7D,64,41,F6,43,4E,BF,8C,40,D8,00), + wwH16(ED,E3,0E,78,01,1E,04,DF,F8,D2,82,90,9C,FB,A1,B5), + wwH16(AE,B8,C3,75,77,F0,6B,9E,95,1B,05,1A,95,2F,F4,20), + wwH16(9C,C7,1B,D2,67,76,21,E7,C1,99,E8,2A,9B,C0,F1,A2), + wwH16(38,BD,A7,E2,B0,E0,74,59,8A,C1,C5,6C,4A,20,B8,FD), + wwH16(2A,AD,BE,79,83,0E,FB,C4,16,5F,01,74,8A,BB,54,FC), + wwH16(F1,49,A5,89,8F,5C,9D,F9,3A,1C,C0,29,FA,3E,6C,2A), + wwH16(0A,1A,0B,77,E6,06,66,1C,C4,11,D4,9F,06,79,69,9D), + wwH16(F9,7B,21,64,CE,2D,79,24,72,7A,BC,71,BC,49,56,F1), + wwH16(FC,B5,FE,5E,61,E7,AD,D8,9A,34,E0,53,05,75,00,4F), + wwH16(41,B9,CD,A1,86,2B,EC,24,3E,9A,49,33,58,E2,AB,D2), + wwH16(9E,2C,67,B3,B2,E9,6E,6F,A1,F0,63,82,0B,A9,B1,37), + wwH16(14,02,EB,75,26,EC,22,EC,CC,CE,27,EA,F3,70,16,0A), + wwH16(0C,EB,98,6E,8E,02,6F,E0,A2,2F,A8,EF,2C,64,03,49), + wwH16(7C,4A,43,8B,D8,69,1A,B0,05,35,59,E2,F4,8E,FC,6C), + wwH16(5A,68,B4,DF,69,61,B0,05,45,4B,80,80,44,B7,99,BC), + wwH16(FF,30,27,43,43,E7,7C,61,57,AC,7F,40,7C,BC,70,96), + wwH16(A5,C9,C2,A3,05,98,9C,F7,48,1E,86,0A,50,B9,12,C9), + wwH16(61,A6,E5,63,C7,2E,37,7E,FF,AE,41,10,FA,20,40,EC), + wwH16(EA,06,FE,10,4C,3F,B9,42,F9,F3,D5,79,11,A8,10,58), + wwH16(40,01,20,E5,2D,39,CD,B3,86,84,22,A2,14,2D,BA,99), + wwH16(4D,0A,E3,50,1B,DA,40,7D,EE,D9,64,BA,B2,9D,4A,C1), + wwH16(C9,CC,2F,BA,C9,6C,19,4A,90,F0,8E,D6,42,47,4D,4A), + wwH16(2E,D7,34,87,D3,FD,F9,0A,C1,68,FA,40,50,49,42,8E), + wwH16(4A,B6,A2,38,38,66,6B,92,F7,89,29,E9,6C,DC,D0,47), + wwH16(F6,57,B4,05,75,D5,E8,3F,6A,D2,69,21,02,E9,C1,9C), + wwH16(E2,A4,EF,2B,31,3E,E5,31,9B,CA,66,1E,55,49,F4,DD), + wwH16(92,4E,AB,56,F6,3E,A7,0C,93,D3,54,FA,16,2B,12,42), + wwH16(17,19,97,67,37,31,DE,21,38,B4,65,B4,5E,6F,62,EF), + wwH16(CB,84,C1,BE,D2,16,8C,0C,2E,47,42,11,06,F8,BB,F5), + wwH16(A8,32,EF,89,27,0C,57,1A,78,2B,D2,33,3F,33,D6,35), + wwH16(A9,DD,87,16,78,21,31,92,D4,9A,80,4A,BC,E6,53,C7), + wwH16(DC,6F,C7,A1,16,2A,5D,65,17,8C,13,66,FE,C2,96,4E), + wwH16(AC,B0,D7,44,52,62,51,36,35,1E,10,8F,6F,FE,2C,AA), + wwH16(1D,B4,05,CA,5D,85,42,B2,9F,1D,8C,11,E4,AE,6F,2F), + wwH16(00,F5,C3,ED,25,A7,6A,BE,98,2D,E7,0B,6F,5B,60,84), + wwH16(CF,B4,7D,F9,4B,17,34,00,76,FC,3E,B0,AB,3A,6A,55), + wwH16(A2,B0,32,76,5A,DD,19,42,61,BF,23,9A,9F,BA,28,93), + wwH16(E2,32,D2,9E,AE,87,96,C5,45,EA,A8,3F,3D,68,A2,91), + wwH16(03,56,A9,1C,11,06,25,D6,33,49,DC,2F,DF,4C,A7,F1), + wwH16(A8,A3,D7,1E,3C,85,62,40,C7,8C,C5,87,71,F0,6F,F0), + wwH16(FA,60,17,81,E5,35,86,D7,35,2A,83,E9,CA,10,E1,CA), + wwH16(91,41,93,BA,BD,9E,38,40,AD,6E,72,88,9F,19,03,CE), + wwH16(43,35,A8,C9,FB,6A,A9,F0,0E,6F,5A,D4,26,8A,11,5B), + wwH16(85,3B,DE,85,52,D0,75,E8,45,80,E0,5D,D6,5A,7E,68), + wwH16(40,1A,E0,A6,0C,F5,E1,6F,D4,D9,46,FA,03,7C,84,BE), + wwH16(5F,4F,27,1C,F1,49,5E,2E,D8,4F,B6,FD,D5,AA,31,13), + wwH16(4F,01,4F,9A,31,3C,13,C1,FB,FB,9D,BB,D9,76,46,BA), + wwH16(C5,2D,B5,CE,85,72,3B,EA,F9,E8,8A,8F,49,E7,05,03), + wwH16(A0,C2,0F,7B,44,75,EC,F2,C1,DA,77,57,66,6A,1E,5F), + wwH16(85,F8,07,7B,1B,A9,14,08,C4,B2,3A,12,6E,FD,AF,96), + wwH16(B7,C5,94,01,88,A6,0A,7D,88,BF,45,D9,02,3A,B5,58), + wwH16(21,34,16,A3,2F,16,63,F4,D7,ED,5B,58,E5,4F,5E,5F), + wwH16(62,30,5B,18,A6,7D,F0,7B,09,00,86,10,4A,D5,34,72), + wwH16(B0,FC,4B,09,76,B6,3A,3B,AF,8D,0A,EB,B5,41,EC,AF), + wwH16(59,91,90,45,4B,9B,6D,4D,FD,5C,6D,E3,81,A6,75,ED), + wwH16(86,1D,BE,96,CF,A9,52,A8,03,FC,29,AA,E8,F5,25,51), + wwH16(22,E7,AF,17,35,FC,FC,1C,8E,FD,54,82,EA,CA,A0,C9), + wwH16(55,A3,14,79,D2,EC,8A,2F,4E,97,C7,65,7B,CB,2F,8B), + wwH16(7E,B5,EE,B5,08,EA,CB,14,69,C9,E7,7D,5C,BE,73,3B), + wwH16(CB,2F,65,7B,9C,8C,FB,D5,04,3B,5C,FB,03,0D,C9,C9), + wwH16(B6,86,04,A7,45,01,41,CD,96,43,36,F7,49,1F,FD,18), + wwH16(4C,54,7B,DE,85,B6,50,40,39,01,F9,FF,4B,28,C3,4E), + wwH16(A8,0F,13,C6,F0,38,E8,71,7E,29,32,EB,E2,9B,2E,82), + wwH16(93,D9,C4,FE,56,BA,8C,F7,F0,DB,EE,8F,2F,EB,8D,AC), + wwH16(39,54,8B,37,C5,31,18,13,3D,A2,4A,44,B6,8F,CE,42), + wwH16(EF,F0,86,7E,15,73,D0,40,49,B1,BB,5F,B7,22,4F,8A), + wwH16(C2,21,F9,46,22,F4,E2,21,C0,8D,81,E9,29,F3,3C,78), + wwH16(07,64,81,71,3D,6B,B3,01,0A,42,F5,FF,66,9F,8D,E3), + wwH16(45,E5,7C,F3,EB,BE,9D,71,3B,D3,17,9D,33,90,2E,E1), + wwH16(90,E3,DD,5B,9E,30,42,8B,AB,78,0A,FA,15,A6,9C,53), + wwH16(6E,60,62,BB,D4,9B,6B,2A,9F,9F,B2,8C,16,7A,6D,37), + wwH16(43,4C,6D,1E,E9,80,5E,1D,E6,C9,F5,6E,E2,F8,3B,D6), + wwH16(20,1B,24,DE,FD,62,DA,2C,25,ED,6A,F8,8B,3E,74,90), + wwH16(E1,70,2B,B5,91,06,23,EB,65,05,EB,A2,40,E4,3B,3B), + wwH16(7C,14,9F,BB,2D,9B,97,A5,C4,3C,89,8D,30,41,0B,39), + wwH16(CD,4E,F5,E9,F4,1D,2E,33,03,E1,4F,77,96,ED,27,1B), + wwH16(5F,2B,BA,D1,5C,50,72,A7,4D,C1,CB,4A,04,33,53,19), + wwH16(77,46,4D,26,B2,C0,F9,6E,1C,83,2C,A8,E9,0E,22,C7), + wwH16(CD,64,C7,C3,0E,12,93,1F,CC,84,CA,B7,23,1C,C0,E5), + wwH16(F0,AA,9E,28,CD,2B,78,E3,67,9D,BF,FB,BE,9F,7B,82), + wwH16(39,76,C6,63,40,51,C3,25,75,5A,32,74,CE,2F,EE,50), + wwH16(54,32,6D,3B,FC,FA,08,B6,40,0B,B1,48,03,53,2F,0A), + wwH16(A7,1D,2D,19,EA,E0,40,BA,E1,0C,1A,8F,A2,49,67,38), + wwH16(8F,52,4C,C0,6C,C7,75,DC,D8,B4,A6,93,D6,AC,10,0E), + wwH16(2C,5B,72,B0,E2,6E,0B,F5,B5,ED,9E,B2,94,95,11,BE), + wwH16(22,E1,37,B2,0F,C7,13,77,79,5C,83,38,05,FA,7D,B1), + wwH16(46,4C,33,77,90,F7,6C,A8,4F,5C,4F,BD,97,99,E9,C0), + wwH16(38,A1,27,D2,F2,71,E6,ED,83,34,4C,3E,D1,81,AA,73), + wwH16(35,6F,12,3B,39,A3,D1,5A,AC,EF,3A,58,98,36,6E,43), + wwH16(9F,B3,C3,CC,7C,DB,21,54,10,B1,BE,9A,78,55,39,08), + wwH16(84,E8,E7,72,24,0A,E8,8C,D5,57,80,ED,76,8B,8D,CE), + wwH16(5F,4C,DE,17,A8,B2,71,7D,2D,F8,D5,D4,7C,AA,A6,C7), + wwH16(81,41,AE,68,E9,D2,CC,EE,3C,62,F5,ED,7C,01,D2,B9), + wwH16(6A,CB,1D,A7,94,F8,28,68,E6,47,51,4E,E1,89,A9,D4), + wwH16(69,9F,6A,F5,40,2E,86,25,E5,F0,4E,7E,55,0A,0F,33), + wwH16(93,E3,5D,C1,CD,19,42,EE,1F,07,12,14,5D,6C,68,0A), + wwH16(2D,47,83,A0,E7,56,1B,19,FB,09,7C,BD,64,2D,93,43), + wwH16(F9,46,E1,B9,80,98,D9,C7,D1,73,30,00,BC,25,35,5A), + wwH16(99,AB,0B,EF,E4,54,E7,06,E0,04,5F,1C,66,5B,1B,85), + wwH16(7C,7D,AA,4B,BD,9A,A9,FE,2C,97,54,EA,B6,C4,7C,FF), + wwH16(CC,9E,73,96,3F,56,96,95,7C,9D,28,74,5D,79,B3,FA), + wwH16(A9,56,E9,F6,AA,27,3B,50,1B,8C,58,B3,41,96,15,4E), + wwH16(89,8F,69,74,4E,13,8C,64,EF,73,B9,1E,7B,C2,F1,C2), + wwH16(1D,1D,5B,FF,57,66,45,63,61,31,5A,DB,8C,94,1C,4E), + wwH16(A5,46,DC,82,C4,D7,E8,A6,9B,2A,33,AA,F9,3D,2D,F1), + wwH16(58,D6,A3,AD,DC,02,19,2E,41,2B,0D,C1,62,4C,F1,BB), + wwH16(95,CC,45,04,17,17,7D,0B,64,96,46,95,1D,44,58,92), + wwH16(BF,32,CE,32,C0,B6,64,DD,7B,B4,F0,E7,F4,C4,4B,E5), + wwH16(A8,C7,86,7C,8A,94,E6,10,F8,F0,B9,63,2A,6A,44,36), + wwH16(1C,EA,5E,AB,69,F0,53,82,BA,99,97,F7,BB,DE,9B,66), + wwH16(4A,10,57,81,67,A7,D6,B6,2A,85,50,AB,F3,F3,DA,1A), + wwH16(4F,9E,62,5B,CD,C1,BB,75,D0,61,F9,8D,36,87,9F,A9), + wwH16(9E,E4,36,8E,B7,39,50,D8,0B,4A,A9,EC,08,EA,07,C8), + wwH16(AA,BE,9B,DB,1B,2E,8E,23,D6,5F,71,FF,F3,AE,56,E6), + wwH16(0F,51,1B,B2,77,6D,A4,E1,56,DA,AC,A1,FD,27,5F,63), + wwH16(CE,B7,F4,63,E6,8B,24,67,06,B9,6C,E1,5A,F8,83,A9), + wwH16(B7,45,2A,38,ED,3F,B0,60,92,E7,4B,16,4D,15,EA,7C), + wwH16(3D,4F,EF,BB,18,56,63,40,8D,0C,8A,38,64,4F,14,51), + wwH16(68,10,F7,32,95,49,6F,6A,E6,24,DB,19,D9,8F,45,AD), + wwH16(82,A4,E4,83,93,DC,1C,7B,1D,26,35,69,7F,2C,37,B8), + wwH16(EE,C5,0D,4E,EE,0B,D7,50,D3,CB,59,37,C2,AC,57,3B), + wwH16(09,22,5B,EF,2E,D2,9F,3E,8F,DB,64,70,65,9B,A7,E2), + wwH16(F5,51,3D,42,D4,25,8E,26,D9,E7,08,D7,E7,37,A7,90), + wwH16(E0,DA,3D,59,FA,83,5B,3A,DB,8E,47,45,85,26,6A,0A), + wwH16(66,C5,99,D0,C7,60,9E,8A,8C,A6,20,5F,AB,30,9A,00), + wwH16(04,B5,33,60,A1,82,8C,55,4D,86,46,7D,85,96,58,29), + wwH16(E3,86,5A,FC,63,D6,F2,47,42,01,61,4B,25,B6,1B,2A), + wwH16(F5,E0,D5,B6,47,67,F4,22,9C,50,DB,A3,7D,B0,04,22), + wwH16(CA,B4,C2,3D,64,0A,81,A1,D8,ED,98,72,98,F4,75,79), + wwH16(97,2B,A5,F0,D8,F4,0D,0A,AF,F4,70,D4,DC,76,8F,A1), + wwH16(E4,AE,24,AF,38,71,89,40,15,00,2B,28,69,7A,CA,96), + wwH16(19,54,D8,BD,70,38,AB,3F,02,C4,4B,7A,6A,39,50,A7), + wwH16(DE,14,61,7E,3E,A9,2B,3F,CB,4C,C5,22,8E,AE,D2,CA), + wwH16(0B,C9,8A,EB,B5,E6,D8,3E,56,0C,80,63,A9,BE,6B,58), + wwH16(E5,23,78,58,0B,EE,72,F6,24,66,1C,E3,9C,D9,71,1F), + wwH16(88,41,07,45,58,E8,A2,54,7D,6E,C4,EA,67,C9,C5,76), + wwH16(EF,68,54,F6,DE,7F,28,FA,7E,09,26,CA,CF,E6,B9,7F), + wwH16(BF,FB,9E,BD,9F,B5,87,19,E3,7F,89,10,D8,2C,D8,72), + wwH16(54,89,4F,A0,DF,61,C1,A7,8E,CA,F2,E4,94,9E,6D,99), + wwH16(5A,C5,9D,BF,83,65,57,98,36,41,FF,34,45,CA,4C,9C), + wwH16(DA,87,7C,22,C8,30,63,1B,28,A0,E4,F1,B1,D0,96,48), + wwH16(53,B6,9E,80,DC,E2,FD,7F,B2,56,11,A7,18,3A,E9,8C), + wwH16(64,92,8B,9F,E6,8F,72,05,0A,E4,FD,35,43,11,66,FF), + wwH16(D1,9B,BF,82,76,0E,9A,38,97,03,8C,20,90,BB,BB,E7), + wwH16(78,8B,26,CC,4D,0D,47,C7,69,9E,06,1F,FD,94,B7,D2), + wwH16(59,C3,6E,9F,7B,CB,D1,05,29,21,65,F9,00,0C,67,63), + wwH16(2B,85,1D,02,0C,F7,4E,14,56,0F,F8,A8,09,66,09,CD), + wwH16(B3,58,62,1A,77,CB,7E,10,50,6D,12,88,41,56,3E,98), + wwH16(3D,0D,AA,2C,0A,8D,E7,47,DE,5C,79,4E,B9,73,9F,10), + wwH16(22,5E,BA,1A,4E,B7,9C,65,EB,33,D3,13,12,FC,0B,18), + wwH16(52,B5,FD,23,6C,E3,DC,6B,D2,AB,07,7F,C5,76,6B,15), + wwH16(32,BE,C6,56,DE,44,17,F4,52,41,53,27,A7,9A,71,E8), + wwH16(29,95,6A,D6,18,DB,63,21,67,DB,53,3B,C6,B1,51,DE), + wwH16(6A,97,A2,49,DF,45,2A,75,CD,24,59,A1,19,84,71,09), + wwH16(42,B4,AD,D9,73,E6,6F,CD,48,94,5D,DF,7D,61,AC,69), + wwH16(24,DC,42,90,8A,D6,5D,28,77,18,95,F8,4D,A3,B7,FA), + wwH16(C6,E6,42,9D,06,2D,77,FD,AC,2A,AB,25,E0,FD,A0,6A), + wwH16(75,3F,66,24,DF,19,F2,DC,58,D4,25,54,62,61,57,32), + wwH16(41,94,68,12,85,98,14,96,A3,D5,06,54,F0,0B,2D,AA), + wwH16(BD,49,B1,70,23,39,A4,E7,C6,CD,1A,90,72,3A,BB,37), + wwH16(5A,1F,45,28,4B,49,21,9A,9C,19,BD,AF,24,12,56,80), + wwH16(F9,E4,DD,E9,8B,5F,B3,BB,C8,49,97,9E,A7,C0,3D,29), + wwH16(F0,34,0B,D1,62,14,8F,1B,2A,50,52,FB,0E,0F,9C,BC), + wwH16(DF,3A,8E,A4,F8,82,5C,28,43,B5,55,1E,68,DF,6A,19), + wwH16(9E,3F,F9,7F,40,03,C4,D3,6C,CE,B0,2D,8E,F8,7E,E5), + wwH16(07,81,C5,D3,EE,1D,CE,53,5D,70,31,E5,2C,CC,66,EF), + wwH16(77,F7,85,C6,E4,25,35,42,2D,F5,A8,A0,08,99,08,EB), + wwH16(ED,97,36,A8,EF,B9,0F,9C,3B,CB,2F,3A,ED,73,B0,E2), + wwH16(95,C0,EF,E8,3B,40,41,2C,95,E1,1D,8A,81,AE,5E,20), + wwH16(62,96,EA,A0,68,7E,55,06,5D,9D,6F,81,6A,DD,4B,C9), + wwH16(DA,17,14,F2,CC,D3,BE,6C,00,2B,DD,AA,D3,BE,1C,54), + wwH16(D7,E5,FD,55,EE,A5,50,14,69,57,FF,EC,38,BE,BF,D9), + wwH16(29,DF,40,60,C3,FD,43,57,CB,F7,29,65,DB,4F,F6,F9), + wwH16(F4,F7,13,60,3A,7D,71,FE,6E,EC,A8,AB,A4,D9,4C,F0), + wwH16(B8,F8,36,98,AA,F2,0D,FB,F7,82,21,E3,52,D0,B4,3E), + wwH16(0C,9B,87,CE,66,26,97,51,83,35,C7,AC,17,33,CF,5A), + wwH16(9C,61,62,75,45,F2,BC,0E,46,BA,5A,3A,82,72,9E,CF), + wwH16(1F,A9,E4,C6,EF,DA,AA,EB,E6,89,F7,CC,98,C6,4E,F2), + wwH16(62,79,BD,3C,68,EC,99,B1,B4,9C,BC,C3,61,5D,9A,72), + wwH16(A5,F8,F4,54,68,C6,31,7F,02,E2,2D,FF,36,C3,A8,E4), + wwH16(CF,1D,57,9D,89,BB,18,07,B9,69,40,67,8F,BC,60,21), + wwH16(68,EF,B7,7E,58,26,95,16,87,3E,F9,75,C3,3D,C9,1A), + wwH16(9F,61,A2,A3,C9,6D,99,B6,F8,F6,CB,FB,45,E8,22,9E), + wwH16(96,A0,76,DC,6A,EB,54,A5,39,F2,E9,DB,7F,3D,89,B8), + wwH16(43,1F,7C,DD,31,8A,72,68,8F,60,3D,5D,F8,99,E0,AF), + wwH16(97,98,29,F3,41,D0,42,01,82,1B,B6,83,B8,29,9D,DF), + wwH16(35,12,67,E9,73,09,F0,4E,69,2E,50,DA,89,02,98,16), + wwH16(C1,E5,20,79,12,FA,54,92,F7,16,8D,A1,D6,AA,01,44), + wwH16(42,58,7D,1C,9F,CF,7E,D7,B2,E5,16,63,19,37,B8,A7), + wwH16(AE,15,F9,99,8E,90,EE,21,BB,D8,48,58,23,AD,92,A2), + wwH16(CC,E0,EA,32,7B,73,50,59,A6,C4,80,A9,42,FB,A5,AC), + wwH16(CC,C8,94,4E,3E,83,49,5D,0C,F1,70,3B,F2,70,F6,C5), + wwH16(34,F3,9C,3E,23,F1,9F,1F,7D,C7,C5,1A,CB,6C,3B,FF), + wwH16(E4,7C,72,A1,E7,CD,AF,9F,45,49,E5,B7,69,03,52,74), + wwH16(97,C0,1C,01,C3,ED,4C,02,8A,45,41,6F,E2,E6,E6,76), + wwH16(BF,D1,F7,7D,03,85,50,04,D8,F1,25,A1,65,05,CD,F7), + wwH16(B9,65,48,87,CD,E1,38,CE,D2,C4,18,E8,21,2B,6C,23), + wwH16(4A,92,AD,E2,F5,CB,9A,08,91,3A,52,3E,5F,94,34,89), + wwH16(73,DD,A4,09,BA,D0,BC,27,68,28,92,31,DC,66,B6,2B), + wwH16(BE,71,92,80,17,11,33,D0,24,20,29,37,EA,B5,71,46), + wwH16(2E,A9,27,2C,37,AA,F4,AD,9D,7B,7F,4C,5E,2F,F2,9A), + wwH16(49,66,D9,85,84,90,9E,8F,95,DB,1A,3B,D8,81,65,0E), + wwH16(C7,B6,1B,F2,4E,CB,B4,1D,DD,CE,A4,D5,FA,7D,A1,32), + wwH16(FE,E8,1C,75,93,CF,39,01,1A,AD,89,2F,28,C3,E5,D8), + wwH16(D4,23,44,E4,72,88,F0,FD,95,AE,29,7A,DB,ED,57,F4), + wwH16(00,9E,66,D8,40,E3,4A,56,2B,76,E0,CC,19,17,F7,18), + wwH16(98,BB,FA,C3,DF,BF,2A,44,71,64,FF,1F,0E,92,89,06), + wwH16(C5,EA,08,13,96,A4,82,C1,0B,F9,DF,51,71,52,E3,29), + wwH16(7D,AE,74,97,18,56,DC,3A,6B,D4,8A,CF,48,3A,23,E3), + wwH16(65,93,E6,F1,97,13,D3,47,19,78,90,E1,B1,F4,5D,CA), + wwH16(20,2B,83,12,8B,C4,BB,40,78,6B,51,8C,07,13,0C,90), + wwH16(22,99,F4,BA,C6,ED,B7,7E,A6,07,0B,A9,F9,27,43,22), + wwH16(14,D1,A3,14,23,55,DA,6B,30,C1,3D,8A,AE,F3,F7,73), + wwH16(A3,95,2B,DD,66,5D,4D,58,11,17,99,A3,EC,C9,44,96), + wwH16(DC,68,49,D1,14,07,7F,83,E7,F3,C7,F4,27,58,3F,55), + wwH16(14,E5,7D,BD,81,06,BC,7D,C1,A5,FA,A1,BE,76,3D,31), + wwH16(30,17,13,13,DF,64,18,30,22,CA,A8,30,07,31,56,6F), + wwH16(D2,E2,FD,6D,1D,6A,AF,1F,3D,7A,6D,05,CD,C6,5C,21), + wwH16(02,E1,76,F0,E6,64,A4,D1,B0,C9,B0,08,D7,99,CE,BF), + wwH16(78,20,57,5B,70,46,43,0C,32,00,F5,90,4F,A3,D4,7A), + wwH16(FD,E2,9F,3B,33,1D,7C,A0,42,24,E3,41,FE,9E,01,C8), + wwH16(42,62,C3,7F,8D,58,74,62,DE,D8,04,51,87,8A,9A,3F), + wwH16(CF,C5,93,1E,16,5B,4E,A2,1F,1B,71,03,A1,C8,E8,A2), + wwH16(32,7B,8C,45,78,B8,5F,46,0D,C9,B7,DC,A3,B1,38,58), + wwH16(22,90,ED,24,8A,58,24,09,EE,3A,EC,92,45,0D,B8,A3), + wwH16(B8,3F,76,50,BC,A3,25,EC,0E,5A,D3,49,9B,47,D9,FC), + wwH16(16,04,60,14,07,43,CB,99,8A,2E,84,77,14,4D,71,48), + wwH16(CC,5A,63,66,35,15,98,23,BB,A0,03,1C,DF,24,EF,0A), + wwH16(2C,B1,D2,F1,F8,AB,B3,10,88,8B,DB,9C,90,80,90,62), + wwH16(DE,34,95,97,54,E9,70,31,73,0B,8E,87,EA,59,A2,D1), + wwH16(86,E0,2B,06,09,7E,5F,B2,AE,FA,CD,67,A2,81,99,7D), + wwH16(4F,3C,DD,AF,59,14,E9,03,64,15,8D,A0,02,DE,E7,A8), + wwH16(56,31,1D,EB,DB,DD,34,A7,BC,3B,FF,DC,76,08,74,28), + wwH16(B4,44,80,3A,ED,F8,8E,18,94,30,17,AF,A0,90,FC,FB), + wwH16(9C,67,BA,19,BF,E1,B1,E6,5B,38,50,87,6D,18,CD,E7), + wwH16(F3,75,CB,CD,2C,4B,B4,84,F3,90,FE,A3,2D,5D,29,22), + wwH16(1E,E4,8C,53,AD,3A,46,C5,DF,5A,1A,35,E4,B6,BE,A1), + wwH16(1A,F2,CE,01,67,BC,2D,6C,9F,33,4E,F6,76,8A,25,DB), + wwH16(44,46,D9,B5,F2,DA,E7,3C,BC,64,5A,A9,BA,E2,2D,3C), + wwH16(22,35,F2,5C,E4,97,9A,46,B4,5D,3E,40,A5,A9,CB,46), + wwH16(25,81,DB,55,E4,2F,33,ED,F6,75,51,D1,1D,72,3C,4B), + wwH16(35,B8,48,F2,FF,90,47,9C,C3,23,7C,9F,AE,3C,BD,EB), + wwH16(30,EE,1F,FB,DF,74,0E,C8,E3,E1,D2,5D,BA,40,0F,79), + wwH16(5B,55,B4,58,74,63,AF,75,C9,48,31,E9,54,69,71,6A), + wwH16(13,57,00,E4,8D,F4,CA,5A,AE,B2,29,D6,C5,37,EB,61), + wwH16(D6,39,F9,3F,4B,87,A6,C1,4A,37,84,C1,9A,4E,B4,B6), + wwH16(BB,F0,68,31,F2,7C,4D,1A,A8,97,70,CA,4A,5A,E3,44), + wwH16(7D,2C,5D,0D,D5,28,9A,3B,CD,7A,5C,F6,56,40,82,CF), + wwH16(6E,E9,5B,46,AD,22,63,15,A9,F1,9B,9D,2A,60,31,08), + wwH16(C5,C4,7B,C0,9D,09,AD,FD,C2,D7,C3,54,12,DE,9B,3D), + wwH16(A0,33,F1,0C,6F,DD,52,A2,00,A9,D2,31,0A,F9,84,7C), + wwH16(7F,2C,16,6E,60,3C,4F,97,50,49,87,69,08,E6,5B,17), + wwH16(38,5E,5C,E8,35,32,2E,C1,1F,9E,94,37,D1,63,CF,26), + wwH16(C7,3A,BB,03,D6,BD,7D,1A,FA,27,37,15,67,BC,DA,4F), + wwH16(EA,5E,0D,98,93,BD,25,0B,B9,E7,D3,6E,3A,3A,24,AB), + wwH16(A1,76,03,7A,D4,DB,73,FC,72,75,D0,5E,D5,E6,80,D1), + wwH16(06,A1,69,7B,04,2C,3F,7B,80,A9,F0,35,B1,E4,50,6F), + wwH16(8D,0D,77,DF,A3,F7,CB,9E,6E,9D,ED,C2,AF,21,E4,6A), + wwH16(2E,87,90,4C,DE,8B,94,CF,C7,5D,6E,90,31,AA,BB,37), + wwH16(EF,3B,0A,39,F8,82,04,B9,EE,A2,6E,69,A8,0A,EE,6D), + wwH16(80,09,46,E9,16,0C,0E,B6,CA,32,AF,57,36,33,ED,A5), + wwH16(21,2C,4E,51,98,8F,82,A1,E1,C9,91,5C,D5,CC,2E,D7), + wwH16(B6,85,3D,60,AA,4B,48,EA,09,21,E8,72,2A,24,E6,BD), + wwH16(61,9B,F1,45,72,58,BD,2B,6C,2F,11,3E,5D,D5,AC,24), + wwH16(F6,E3,79,06,99,0B,5B,88,36,7D,3E,96,2D,DE,B6,20), + wwH16(A7,D7,BC,6A,20,39,3D,D3,E2,17,4C,AF,8E,03,86,E1), + wwH16(65,4B,2F,FE,60,43,BD,C9,C5,F4,50,AF,63,10,A1,02), + wwH16(87,D1,17,56,D3,F0,90,E2,31,48,DE,E7,96,25,09,9F), + wwH16(8D,32,E0,B6,95,A2,AD,5C,EE,3E,02,C5,EB,56,C6,46), + wwH16(59,00,5F,78,19,B2,55,A2,30,CF,5A,43,85,11,F9,30), + wwH16(AF,A1,51,AE,3B,D3,4A,A7,21,A1,10,54,89,8A,29,F5), + wwH16(9B,08,20,6D,63,CE,1E,72,28,DA,0D,E2,ED,17,C9,6A), + wwH16(03,04,28,3E,13,E5,24,4D,A0,08,6D,9C,30,11,8E,21), + wwH16(C6,96,B6,74,57,54,19,AA,4F,73,CE,26,67,D9,E8,B6), + wwH16(F6,FA,04,05,1A,AD,8C,C6,6E,70,35,7B,14,95,C9,A1), + wwH16(AB,2E,4E,1D,85,B8,39,51,D9,09,E5,67,25,CD,72,94), + wwH16(AC,BA,1B,2B,96,23,E3,56,CB,3E,AB,48,62,C4,AD,DA), + wwH16(DE,2C,3F,29,1C,38,A6,28,B5,89,6F,11,7B,52,F4,DE), + wwH16(63,E2,F6,69,A7,90,F5,5E,CE,5C,12,18,27,24,F7,01), + wwH16(FB,41,E2,D0,AA,10,D3,6B,D9,3D,31,93,20,B8,E5,C4), + wwH16(61,B1,81,7C,4C,0C,F9,8E,5E,51,55,CB,4D,FB,C2,51), + wwH16(63,2C,BA,1C,70,C9,C8,96,63,F4,73,54,20,13,E9,71), + wwH16(61,FC,29,17,98,A2,26,D3,29,76,12,9C,D3,06,B7,6E), + wwH16(D8,08,C7,24,EF,51,D0,BF,64,89,1D,88,E5,03,68,9F), + wwH16(86,08,F7,1E,F9,1B,7B,71,DF,54,9C,38,8D,F6,FA,F8), + wwH16(02,E9,2C,25,86,26,35,B4,CD,61,48,C0,2F,2E,19,C2), + wwH16(1B,23,0B,7C,54,B3,BB,FA,E7,7B,76,8C,15,0A,3E,EC), + wwH16(E8,0E,ED,68,E6,04,1A,92,21,36,DD,2F,12,4B,01,64), + wwH16(5C,CD,F5,C5,93,3D,0D,81,92,4D,CF,F9,C1,D9,E1,75), + wwH16(12,BB,1E,21,C5,8E,27,37,7B,42,6D,21,13,CF,B5,B2), + wwH16(B4,7F,2E,45,EF,52,77,EF,90,30,B9,BB,01,D1,CC,03), + wwH16(0B,BB,E0,1E,1E,17,86,03,55,0A,38,FB,09,C6,11,EE), + wwH16(0E,F4,BF,0D,E4,BE,29,7C,3B,7F,A9,98,54,0A,18,C9), + wwH16(A8,34,20,A8,56,80,98,F4,28,B1,06,C0,61,96,04,9A), + wwH16(BE,C9,E6,1A,D5,83,DA,05,57,8E,51,47,D4,50,CD,14), + wwH16(E2,42,BC,03,5A,67,BF,83,42,AD,48,91,0F,73,63,E7), + wwH16(44,2C,AA,53,F6,8D,17,3E,70,A4,82,34,4F,19,46,F0), + wwH16(7B,11,54,1E,95,66,25,83,AA,31,23,81,D8,30,5D,87), + wwH16(55,EC,7C,29,D3,8A,DF,F4,A4,79,58,83,42,B0,39,18), + wwH16(1B,BF,B3,DC,D6,83,8F,02,54,8F,6C,1A,D4,C9,38,75), + wwH16(57,0E,10,96,DE,04,AA,1C,7F,17,02,ED,1C,60,F4,FC), + wwH16(1F,2F,3E,47,87,91,E1,40,EA,04,24,BA,38,B4,8A,63), + wwH16(B2,5A,64,8E,9C,3C,2A,96,D8,EF,3D,79,6E,91,E4,B5), + wwH16(FC,BD,13,BD,D4,43,2F,38,6A,08,0C,0C,18,D7,D3,B4), + wwH16(7A,BD,9B,B4,B1,95,BD,34,60,6D,E7,CF,25,21,7A,B7), + wwH16(32,34,AF,D1,FB,76,4C,0B,1D,7E,35,6C,21,AD,CD,CC), + wwH16(BB,C6,F2,8A,6D,CE,71,F0,C6,28,72,FC,B4,D2,BD,55), + wwH16(2E,D8,9C,9B,19,AD,7E,5B,1E,BB,3F,DE,8D,94,7F,9B), + wwH16(01,F7,AF,CF,D6,73,FB,2B,B2,CB,05,F6,56,2E,3E,43), + wwH16(ED,B9,AD,9C,25,81,FC,4E,75,E7,8E,EC,34,23,99,1D), + wwH16(89,ED,4E,C2,BB,79,A8,53,C3,D7,EF,2B,72,44,14,F9), + wwH16(A7,40,FF,B4,CC,01,3B,CD,C0,4D,99,02,F8,9A,E7,82), + wwH16(FD,9F,B6,FA,D1,3E,D1,C7,0C,5A,A9,8F,DD,71,D6,4E), + wwH16(EF,5F,64,1C,08,D0,46,AD,5D,A3,5A,CE,0C,1E,AB,3A), + wwH16(B6,B1,51,04,A2,F6,F2,F2,A2,6B,9E,EE,36,DB,13,F5), + wwH16(07,49,83,6B,D4,45,B4,FF,3F,11,72,E0,52,1F,EC,43), + wwH16(8D,1C,FB,0A,7D,1F,25,45,FB,1D,C2,24,1B,83,94,4B), + wwH16(6C,91,E5,F5,6B,13,24,E9,A2,F0,46,EB,9A,11,70,B1), + wwH16(BE,A2,D9,34,5E,EC,70,05,C9,C3,43,08,B9,E3,5C,49), + wwH16(B9,3C,39,71,9F,82,4D,86,37,FD,A2,BE,0C,24,83,5C), + wwH16(B2,B7,E7,15,13,BA,6D,51,98,24,6D,78,3A,E6,EB,6C), + wwH16(93,4C,41,74,E1,05,CE,17,91,3D,2A,41,BE,12,B2,4F), + wwH16(F3,85,9A,41,81,3C,48,E8,AE,7F,0B,81,8A,7E,16,31), + wwH16(BB,C1,BC,B8,53,6C,54,61,50,00,E4,B0,87,9E,DE,24), + wwH16(92,C4,36,ED,37,9B,A2,FF,89,6E,F1,DB,3F,EB,29,18), + wwH16(E8,4A,A4,A4,E9,88,7F,AF,A8,24,8E,D7,61,B4,B4,E7), + wwH16(89,0B,DD,15,5A,49,12,59,6B,52,E0,75,D7,FD,97,8E), + wwH16(A2,7C,54,C5,05,E6,E7,35,84,54,02,E4,CB,4A,60,86), + wwH16(60,2B,49,87,66,00,59,D3,D7,1F,8B,11,42,FE,36,03), + wwH16(8A,FA,50,5A,FB,B0,C7,9E,D9,71,E9,99,ED,A2,0A,DC), + wwH16(56,8E,41,67,A8,36,B9,F5,D5,77,21,AE,C4,1B,02,CF), + wwH16(A6,77,6D,04,7A,04,33,E0,7B,58,CF,73,49,5C,14,28), + wwH16(17,08,C6,FB,C5,A1,8C,0B,A3,26,F8,C7,10,48,52,9D), + wwH16(6E,69,9E,0D,CC,C0,4F,E0,F6,B3,4E,B7,3E,64,B3,AD), + wwH16(A8,64,DF,C5,80,06,26,13,00,A1,EB,CF,EA,E3,35,41), + wwH16(2F,54,CF,AF,FA,40,99,C0,5F,CA,96,7D,8E,9E,75,08), + wwH16(15,B7,88,4E,16,7F,61,26,BA,81,0F,AA,54,A8,60,DE), + wwH16(34,48,FD,18,32,7C,00,5A,5B,62,4D,EF,C6,BB,E7,A3), + wwH16(9E,A0,97,26,F8,7F,21,30,21,BD,55,03,F9,70,11,8A), + wwH16(C7,73,A3,27,27,87,DB,35,30,2F,9E,E2,71,9C,30,98), + wwH16(A1,1F,06,17,1E,0D,EF,EF,79,F4,06,C0,D4,C1,A0,D8), + wwH16(00,2D,E5,BC,7B,A9,44,7F,CF,03,24,FB,77,BD,87,C2), + wwH16(7D,54,0D,1C,12,45,10,90,CA,40,44,8D,C1,B6,13,5F), + wwH16(72,5B,86,44,CF,D8,A3,19,16,AF,41,FA,BD,4F,F6,82), + wwH16(98,1B,4A,28,18,3C,9F,A4,C4,E7,FE,31,18,75,06,D7), + wwH16(E3,F4,7B,08,E4,CD,62,DD,7D,17,FB,0D,4D,C5,03,54), + wwH16(D6,40,75,14,E9,88,5F,AC,94,E3,EE,7E,92,F4,0D,E6), + wwH16(79,8B,D0,96,66,3F,41,9E,C8,8A,DF,AF,59,6F,A8,AE), + wwH16(62,1C,08,74,77,D1,18,20,F7,5F,F0,10,CE,26,BD,F4), + wwH16(DA,12,53,55,64,7F,68,C2,60,86,10,C2,40,6A,8D,5D), + wwH16(D2,8A,EF,D7,6C,94,62,5C,B7,79,75,35,41,8A,BB,68), + wwH16(69,41,10,97,71,8F,95,FC,A4,74,A3,73,A4,59,5B,F5), + wwH16(11,C0,1E,F1,04,93,D5,F9,2A,5F,D7,55,13,DD,4E,04), + wwH16(E3,D4,CE,C6,D3,28,CE,13,E5,E9,5F,28,7B,37,A4,26), + wwH16(8C,FE,CA,53,70,51,73,1A,31,A0,E2,48,46,17,97,31), + wwH16(A8,B0,58,C7,09,34,E8,DF,50,46,4B,65,D7,E1,24,2F), + wwH16(D9,43,39,EC,34,5F,E9,E6,5F,5C,7F,D8,2E,7A,37,E2), + wwH16(32,BC,80,4A,9C,BA,21,07,34,69,82,31,73,73,29,64), + wwH16(07,E2,11,4F,29,EE,2B,A7,CE,9E,31,78,1A,58,5A,04), + wwH16(D3,32,78,0A,FE,0A,83,F0,11,53,86,E8,14,0C,DA,06), + wwH16(0F,09,B0,4C,A2,9D,73,D7,EC,CF,42,92,40,2C,34,8F), + wwH16(3F,B0,9B,FA,C6,A6,F3,BE,7F,8A,37,93,2B,22,DE,98), + wwH16(4D,E7,C9,F3,74,06,02,40,3F,A7,20,2F,92,3A,BE,81), + wwH16(A4,2D,EB,89,75,DE,47,36,93,C4,49,43,FF,01,8D,2B), + wwH16(D5,26,9E,89,92,6C,C1,B0,1C,32,46,FB,A6,A3,E9,40), + wwH16(5D,96,12,1A,B6,70,B9,1C,4E,B4,84,BC,29,5A,AA,2A), + wwH16(B7,A8,CD,A3,C8,8B,B1,9B,B9,49,03,51,04,40,ED,FA), + wwH16(A6,1D,FB,96,F9,2A,F0,F3,A6,9B,F9,35,B7,05,8C,7A), + wwH16(2A,9B,AE,9A,F5,CA,A5,8D,C9,8F,06,E1,A6,F9,74,4B), + wwH16(69,99,C2,2C,9F,D1,F7,98,B8,A4,4F,5A,C3,46,65,26), + wwH16(BA,28,50,4D,30,F7,2F,71,44,E7,60,7F,9A,42,C5,42), + wwH16(41,4A,D7,4F,84,88,A1,D7,1B,40,4E,B5,19,12,6C,17), + wwH16(1B,62,E8,97,89,4B,06,63,4F,AF,87,43,26,31,D8,92), + wwH16(0E,93,F7,85,98,01,9E,12,5C,98,DA,79,52,38,83,70), + wwH16(E3,8B,3B,BE,71,F7,5E,CF,DD,39,41,13,CE,85,33,ED), + wwH16(3E,AC,78,89,6E,0D,C1,14,5F,FF,C3,23,F9,78,A7,78), + wwH16(42,27,60,9A,EC,C7,52,5F,A2,AA,33,41,80,BB,70,57), + wwH16(CB,9A,26,A0,AE,2D,02,4B,72,CA,9B,ED,16,EA,A1,CA), + wwH16(C1,2D,E3,98,AE,71,09,79,DB,62,3A,1D,52,20,85,64), + wwH16(70,7C,9F,86,88,F9,B0,1B,55,42,A7,69,B5,4B,6E,DD), + wwH16(98,C6,CD,2B,D6,B1,42,45,36,FD,65,4F,57,45,D7,A7), + wwH16(5B,6F,36,D1,50,F0,C3,DB,52,DD,75,81,36,B9,24,93), + wwH16(8B,1A,CE,CE,A1,6C,6B,2D,DF,5A,B3,24,38,73,6A,EF), + wwH16(50,77,C2,96,C5,D0,3D,4F,50,35,EB,24,FA,C0,47,B0), + wwH16(C0,33,19,F4,8C,20,2C,7F,EA,6D,10,18,A6,F8,8E,10), + wwH16(2A,E3,CF,E8,F9,03,75,66,69,E4,11,44,19,A2,24,82), + wwH16(77,B2,40,A6,8F,3C,CD,E1,0E,0C,2A,64,3E,F1,9B,91), + wwH16(D3,C5,17,FB,B8,25,DF,89,04,52,73,F5,A0,36,81,E2), + wwH16(53,F5,39,60,E2,50,2D,CE,36,F6,F9,DB,AA,6F,21,90), + wwH16(36,81,C7,29,28,32,41,91,B1,E5,CF,54,9A,7C,48,DC), + wwH16(23,16,EC,0B,0E,67,2F,0B,75,A6,FF,93,D7,23,B5,CD), + wwH16(54,74,41,BF,28,28,76,9D,AE,C2,D3,52,D4,69,F0,21), + wwH16(DF,ED,4A,5C,37,10,05,BB,88,6F,6B,55,E5,23,A1,0A), + wwH16(F2,A4,73,A6,97,7D,EC,FC,03,9E,0E,A8,5C,BD,57,C8), + wwH16(0E,63,19,83,4E,6F,EE,06,00,47,04,B7,59,88,EC,74), + wwH16(CC,98,A1,24,E6,79,DB,33,E0,9F,4E,95,15,0A,4F,50), + wwH16(FE,79,0F,04,71,0F,81,94,2D,62,97,C1,AC,18,F9,98), + wwH16(2A,05,99,D9,5A,EA,7E,4D,57,8E,69,D8,56,FC,A4,E4), + wwH16(08,93,E9,00,F6,8D,C7,E3,0E,E8,7A,0D,18,EC,87,52), + wwH16(C4,E9,9C,83,A4,18,97,8E,D4,CC,D5,A3,C3,1E,D1,64), + wwH16(C9,CA,26,4A,E6,BA,F5,8D,C3,9A,60,97,BC,8B,2F,40), + wwH16(D9,78,42,C0,DF,95,C3,D5,F5,BB,37,EE,45,4A,7A,56), + wwH16(D3,1C,99,25,F8,90,E3,56,9C,F9,5F,AA,9A,65,CE,9C), + wwH16(1F,FF,85,90,5B,7E,0A,EC,1A,4D,12,77,98,21,E4,CC), + wwH16(0F,2A,A2,1E,E7,80,5C,B7,05,3A,BA,36,2A,A7,BC,0E), + wwH16(0A,42,51,09,C6,34,6A,49,AE,55,F3,D1,CD,60,A5,B6), + wwH16(30,3D,13,A9,A9,B3,78,7C,50,2E,76,F5,E8,D7,96,41), + wwH16(6A,B9,3F,07,D1,DC,7D,2E,9C,1E,F1,63,C4,9E,12,04), + wwH16(E0,2F,D3,7D,F1,27,FD,FB,81,32,ED,9E,25,CF,5B,88), + wwH16(4A,3A,B3,F0,51,33,E0,52,35,51,A2,D1,65,F5,3B,12), + wwH16(A9,80,CA,DD,01,CB,0C,99,DA,37,A4,AA,18,6E,02,B8), + wwH16(1D,20,E6,33,6A,39,29,4A,DA,DA,43,C3,87,67,CC,FA), + wwH16(2D,8A,46,3D,0E,C0,86,F7,2C,DA,BC,19,6D,86,96,D6), + wwH16(4F,44,B8,33,0E,15,0B,AB,41,26,68,3F,AD,BE,FF,29), + wwH16(87,C5,E4,69,C2,D4,20,ED,15,09,0F,FE,BF,B1,DC,58), + wwH16(9F,23,1F,57,AD,B9,14,10,44,6E,F0,B1,E7,7C,7A,F8), + wwH16(5A,44,89,6B,09,78,C9,33,8B,C9,08,18,22,BF,AB,8E), + wwH16(F3,E7,30,D6,C1,7E,70,1C,17,93,8F,C8,75,46,9B,F0), + wwH16(14,8C,D5,CD,CC,4C,85,01,DB,93,DD,88,53,E5,D7,02), + wwH16(E1,1E,21,8D,D9,58,5E,64,F7,52,40,3F,22,85,03,5D), + wwH16(09,57,E5,0B,A7,D9,98,27,62,1C,48,9A,73,84,26,F6), + wwH16(3E,C4,00,5E,C6,E7,E7,41,AF,4B,7A,4B,01,5E,78,92), + wwH16(43,64,45,89,23,CD,CD,25,59,60,ED,46,93,C6,40,43), + wwH16(73,15,3C,24,D5,51,BF,A6,02,22,DE,4A,BB,03,6E,35), + wwH16(03,56,D9,14,E7,40,4D,40,EC,AF,50,B2,AF,BA,90,DD), + wwH16(D4,D1,AB,53,2D,C0,A3,64,D2,31,78,33,55,F5,F9,CE), + wwH16(E5,50,FE,17,A3,67,9B,D0,6A,93,2D,B9,16,28,74,5E), + wwH16(3E,98,B1,5E,2C,EC,CB,1E,8F,26,89,07,97,1F,E8,C7), + wwH16(64,C0,30,A2,CC,DE,A6,4D,92,C2,D0,B8,96,9B,73,A3), + wwH16(89,4B,D9,62,90,0A,F5,2D,80,22,F3,D4,10,72,9D,E5), + wwH16(FC,BC,C9,07,AB,0D,D2,1D,9E,F7,75,B4,53,BD,3D,A5), + wwH16(75,E2,0B,4F,60,A5,6B,2B,C0,4B,0A,DF,43,57,13,D5), + wwH16(03,8B,D0,B0,4D,FB,90,1E,C1,62,09,35,A5,4C,D4,99), + wwH16(28,F4,BA,1E,40,8B,89,98,12,90,00,8E,1F,5E,01,D8), + wwH16(54,58,E4,3D,2D,DE,B3,67,D8,8B,43,D6,0A,3A,11,59), + wwH16(2F,7A,2C,5C,46,B8,EE,05,AE,FB,4F,06,25,84,8F,C3), + wwH16(DD,8D,47,58,90,F0,A5,42,23,70,C6,B3,D0,C3,75,1A), + wwH16(8A,8F,AE,C4,85,B4,13,3A,3E,E5,5D,40,4D,CE,05,1B), + wwH16(52,95,B0,28,B7,A2,9A,9E,A6,CF,9A,BE,CD,3A,EB,98), + wwH16(92,82,28,CA,80,87,88,AA,AD,73,00,5F,8D,BF,09,07), + wwH16(09,96,DC,5C,43,50,8F,42,AE,FC,FD,21,C4,24,53,E9), + wwH16(AE,DA,42,AA,16,18,27,5C,BC,9E,41,A5,C1,CE,87,55), + wwH16(22,69,28,58,D2,85,8B,D3,41,F4,CE,DE,D0,18,22,30), + wwH16(2F,A1,5A,71,1D,08,9F,69,3A,19,6A,5E,38,B5,CC,E2), + wwH16(33,11,3E,96,7C,3F,9F,23,42,D0,91,A5,FB,8F,ED,C5), + wwH16(6C,F3,C6,27,F4,F9,14,0F,A4,37,0A,06,E5,BD,0C,E9), + wwH16(CB,F2,51,50,23,C3,DE,D3,01,17,54,5D,A9,55,38,08), + wwH16(0C,BE,BB,BB,E9,FF,1E,55,88,C4,52,57,57,F4,A0,73), + wwH16(60,33,17,7A,5F,7A,1D,A2,F2,4E,F1,DD,8D,DD,5D,55), + wwH16(9F,68,A6,83,75,99,88,0F,E1,28,93,62,EA,AB,6E,B8), + wwH16(29,4E,B6,BB,AD,82,03,27,60,01,F9,BB,07,5C,AA,C7), + wwH16(C7,AA,B3,C8,C9,F6,3B,EA,CE,DB,D8,FD,AF,80,7C,BF), + wwH16(D8,A1,1B,A3,88,93,45,38,E5,C5,FD,AD,73,C6,C5,C3), + wwH16(EE,8D,4E,98,80,8D,81,19,EA,DA,C1,1E,FE,75,12,D5), + wwH16(33,D4,CA,15,5D,0B,F5,0F,DB,68,19,CE,60,3C,72,F2), + wwH16(A3,3E,74,E6,AC,0F,D9,81,C5,7A,A5,2E,13,CE,64,26), + wwH16(9C,B5,8B,E0,ED,83,81,2C,DF,5F,46,1C,AB,E3,1F,6A), + wwH16(27,7F,33,58,ED,21,E9,E2,CB,33,08,AB,9B,B9,CD,6F), + wwH16(0B,47,AF,53,95,E7,CF,CD,17,D3,F2,44,94,F7,97,4C), + wwH16(40,D7,AE,CD,55,D7,14,1C,A4,41,A0,C3,3A,CB,22,98), + wwH16(AB,A8,25,C6,46,01,D5,A9,86,4B,1A,F3,80,99,A7,57), + wwH16(65,EC,46,47,21,7E,BC,D0,6F,42,D2,D1,C5,CF,6C,6A), + wwH16(B5,AB,1F,BC,2C,7B,18,08,57,DD,6B,76,69,BD,6C,24), + wwH16(CD,AC,DC,4B,0D,1A,6C,87,8A,CD,79,DE,66,0C,2E,3F), + wwH16(B5,0B,92,18,04,A9,23,03,57,15,43,7D,C1,17,35,C9), + wwH16(C8,02,EB,38,2C,89,CE,65,85,28,1B,F9,5B,9F,97,0B), + wwH16(19,0F,DF,A0,B4,EB,6D,98,DA,59,2F,4D,C0,53,4F,40), + wwH16(0B,9F,C3,DC,35,50,91,67,51,77,BF,C1,C9,7D,AA,40), + wwH16(A7,57,05,0F,E7,50,F4,6E,5D,61,FD,03,1B,05,B7,3C), + wwH16(21,EB,BF,E0,1D,86,48,89,FA,1D,3E,C7,15,EB,32,AA), + wwH16(E5,05,79,04,7E,A1,0A,3C,85,40,93,A9,F5,47,BB,EC), + wwH16(B3,5C,6C,ED,34,7C,8A,94,84,2F,05,05,4B,08,B9,32), + wwH16(8E,DA,37,CE,D1,DE,EC,19,51,E4,21,E2,EB,D8,9A,4A), + wwH16(82,8F,81,99,28,A5,F8,6E,56,B4,77,C7,01,BF,A3,73), + wwH16(7F,35,FE,0B,85,68,6E,6E,7A,E1,65,CE,DC,C1,5D,73), + wwH16(EC,EC,9B,FF,27,0C,96,24,2D,C6,EE,58,0B,9C,62,31), + wwH16(D3,9A,94,C4,9E,C6,7C,02,F8,E6,F8,2F,09,C8,7C,97), + wwH16(D5,B0,8F,22,AB,CB,0F,17,EE,47,E6,77,CE,94,7A,E2), + wwH16(D0,28,7D,8A,E0,05,E4,C2,B5,C6,FB,42,7F,67,D1,27), + wwH16(E0,90,AD,C8,CC,AE,C9,D4,FE,6B,03,E5,F3,7F,50,CA), + wwH16(50,EC,82,15,A5,95,B8,AE,E5,9C,2E,4A,9F,DF,0A,76), + wwH16(9A,44,9E,BD,FB,A9,64,DF,7D,9B,F8,5E,DC,0F,7B,F9), + wwH16(58,72,BA,B1,8A,A1,4F,E5,4E,DE,1F,9C,26,3C,B4,F1), + wwH16(9D,16,AE,26,75,1C,0F,50,FA,15,1B,88,B0,D3,BF,2B), + wwH16(F2,FB,55,60,1F,AF,B1,A0,64,B8,D2,FB,56,5B,FD,A6), + wwH16(EB,61,56,BD,EC,A2,64,18,57,11,7A,8F,0B,DF,D5,A2), + wwH16(D8,41,2F,08,E4,53,57,F0,FE,54,38,DB,B6,1A,48,CE), + wwH16(47,5B,66,07,EC,F8,D7,8A,83,CF,E4,73,E6,FD,A8,71), + wwH16(25,95,74,A7,8E,6C,8C,60,EB,0A,F6,B5,A0,46,09,5D), + wwH16(AC,6A,D1,FE,F5,B7,7C,58,79,DE,1F,06,02,F2,6A,44), + wwH16(4E,C4,32,BE,6E,4D,48,4D,96,FF,C1,BB,C4,87,77,75), + wwH16(A0,92,C6,B6,00,58,55,E9,51,86,77,51,A7,45,3D,92), + wwH16(60,39,33,32,20,0C,9A,D8,EE,28,C3,96,FA,8A,3A,ED), + wwH16(E9,29,41,76,28,AB,9D,F2,12,4F,D0,F2,57,3C,39,3E), + wwH16(71,41,03,9B,D9,D8,77,3B,05,4A,F6,4B,5F,24,84,D0), + wwH16(71,B5,D5,D2,48,76,2A,0D,8A,A9,59,CF,86,80,82,29), + wwH16(BA,A9,2F,30,E6,63,E2,FC,3F,EE,9B,A1,0C,97,8B,93), + wwH16(20,0B,DE,F6,10,42,F8,28,D9,81,F1,6A,4B,F1,9D,38), + wwH16(AF,E8,43,A5,3A,14,EB,51,92,33,EA,29,95,9D,1E,FE), + wwH16(4B,DC,46,A3,94,C3,5B,1E,7B,D2,D2,3D,75,F2,B2,73), + wwH16(88,7D,1F,FC,A9,ED,1B,E4,FD,D2,F2,47,E0,B5,1D,0A), + wwH16(1F,4D,56,D1,11,8D,B8,3B,E3,D0,4E,1C,97,BA,E4,C2), + wwH16(5E,A4,95,3D,43,A5,DD,B6,6A,CC,A6,D8,58,DA,3B,32), + wwH16(24,70,4F,E1,E8,A7,C3,43,01,C7,2A,E8,28,74,3E,B8), + wwH16(54,8E,80,CB,3A,C1,9E,D9,0B,E8,98,97,A3,F1,39,D4), + wwH16(28,C1,BB,B3,4B,87,81,07,E8,7F,A4,D7,BA,B7,36,46), + wwH16(2E,34,42,1C,E6,05,50,51,05,3A,A0,8A,98,6E,F3,99), + wwH16(C7,C1,D1,F2,53,55,0A,18,B6,8C,05,80,EA,C5,5E,FB), + wwH16(04,19,78,83,01,57,87,DF,EA,6E,2F,87,49,B7,D4,D3), + wwH16(A4,F2,FA,A3,B9,57,A6,06,88,65,C7,4C,63,BD,41,7A), + wwH16(49,DB,5B,0D,5C,E6,58,6D,FF,14,45,F7,F4,4C,04,04), + wwH16(DD,25,68,F0,A9,16,BC,2F,5B,43,BC,76,3E,CD,0A,56), + wwH16(CD,1F,00,7D,32,BA,84,B5,6C,66,38,5C,58,D4,8E,44), + wwH16(5D,80,0A,78,53,9A,3D,98,14,F3,8C,4A,63,B0,E9,A5), + wwH16(19,7D,9F,92,D1,79,59,F2,0C,62,81,E4,25,57,CF,88), + wwH16(26,84,4D,9D,1A,CA,AC,76,2C,52,60,AF,B5,AE,EC,D5), + wwH16(BB,72,92,86,19,BB,9B,10,9E,0B,73,4D,AB,1E,77,53), + wwH16(BC,E3,47,86,BC,36,18,41,1F,2F,F2,A7,AD,B3,D3,47), + wwH16(68,AD,8F,23,AC,EB,84,67,A8,7C,03,55,4D,D1,A3,D5), + wwH16(D3,4A,D2,F0,D3,8F,12,83,89,A5,9F,43,2D,1E,4A,0A), + wwH16(6A,86,43,67,62,71,BA,30,78,5A,2F,0D,28,F3,6E,CD), + wwH16(39,42,EE,6E,E7,08,2A,C8,17,E1,10,24,62,77,11,8E), + wwH16(B7,D2,2D,C1,74,D0,1D,05,8F,0E,C4,E0,38,68,A0,0E), + wwH16(9E,A5,8F,86,5D,33,23,06,57,81,58,AF,0B,EB,AE,B1), + wwH16(E9,46,57,1B,54,AF,4F,B9,CC,85,86,6B,58,BF,71,32), + wwH16(EA,D6,D4,68,71,07,AA,DB,69,D3,EF,B1,1B,8F,45,EC), + wwH16(18,79,24,8F,9E,7A,FA,E0,23,EF,FC,35,04,4A,BF,60), + wwH16(79,BF,F9,CF,9B,6E,0D,7C,22,1E,6B,AF,C1,43,FF,01), + wwH16(22,26,D0,74,AA,31,22,04,FE,19,0A,16,F6,AD,8C,C8), + wwH16(97,24,F0,C4,FB,E0,E9,6D,05,ED,49,73,19,3E,C3,B6), + wwH16(22,B3,E2,23,EB,58,DC,75,39,1E,ED,FD,5F,C7,BD,45), + wwH16(21,5F,7E,AB,70,FA,59,B8,0C,7E,E3,88,6A,3B,3A,9C), + wwH16(D3,13,C6,96,B8,AE,0A,DF,BF,84,2E,12,7A,AF,75,99), + wwH16(B5,F3,86,16,F0,32,82,AE,41,3D,CB,7A,81,C3,50,A7), + wwH16(DE,66,A5,3D,13,92,7D,E7,2C,50,0E,CE,DB,B0,5D,0D), + wwH16(08,D3,13,51,6C,8D,B8,74,F7,CA,37,52,8D,F5,D3,8C), + wwH16(77,97,91,AF,37,62,E6,73,D1,81,E3,D5,A3,BA,F5,29), + wwH16(DC,D3,A0,B7,82,72,6B,F6,A9,51,6D,CA,12,C5,51,6D), + wwH16(DC,95,1B,60,21,40,D5,6E,6C,25,8E,3B,DD,74,D7,9A), + wwH16(D1,B6,F0,85,30,39,80,AD,DA,49,8C,DC,75,73,A0,9F), + wwH16(C3,C7,D1,85,BD,1D,19,57,7A,CE,9A,97,63,EF,3E,E4), + wwH16(7D,BE,9A,F0,3A,E7,F2,14,08,47,7C,61,5A,70,36,C5), + wwH16(F0,D4,6E,07,77,C1,AD,BB,1A,A4,F6,ED,29,64,81,CF), + wwH16(40,AA,69,B6,83,36,9A,44,54,EC,22,02,4E,E9,EF,18), + wwH16(32,82,89,6B,57,6D,E5,F8,E0,FF,DD,DD,94,CA,B9,D2), + wwH16(C0,37,C8,55,52,B4,2E,EF,4D,06,86,AD,98,94,8A,F9), + wwH16(2A,87,DB,82,31,4E,78,2B,69,37,50,74,82,6B,C7,35), + wwH16(63,61,30,03,5F,90,0D,9F,13,C2,86,AD,21,43,B5,0F), + wwH16(97,5A,DC,3D,0B,27,42,8B,0E,D3,99,A1,83,6B,B7,56), + wwH16(6C,F5,38,52,BD,8A,8C,45,9C,1E,77,16,1F,CB,72,DA), + wwH16(B6,31,64,40,52,5F,A4,1B,9A,48,56,F6,EA,DF,A3,34), + wwH16(47,AD,25,97,78,41,34,38,6A,C4,BE,A4,FC,A5,00,78), + wwH16(B9,B6,E2,2B,66,50,F0,C2,AC,CA,05,59,67,3E,3C,08), + wwH16(A4,E7,4E,3E,B5,78,5C,28,D9,26,4E,45,10,B9,FE,06), + wwH16(60,03,B0,9E,9D,1E,80,F4,76,99,E0,DB,E6,EB,0A,A1), + wwH16(CD,0A,E7,96,27,36,D9,CE,06,78,C4,2B,CD,71,69,A3), + wwH16(D3,E0,77,D7,79,33,07,B4,6B,0E,63,D3,8A,E3,87,2E), + wwH16(F3,F6,45,BA,A5,87,70,EE,78,89,DA,49,2B,36,F5,D3), + wwH16(B1,C1,39,CE,03,57,16,0B,CF,81,77,C6,2C,93,BE,4F), + wwH16(40,9A,EB,93,AF,75,F1,D4,37,0E,4D,3C,27,09,4A,C5), + wwH16(E7,7C,B7,1C,1D,4E,6A,9F,95,12,59,8D,DA,FB,1F,79), + wwH16(F3,2C,C4,F6,1A,7D,B1,7E,60,66,0C,53,34,C0,FC,11), + wwH16(A9,D5,45,36,63,CF,92,CB,2F,3C,89,30,6B,C6,BA,AF), + wwH16(CB,BE,AE,F1,8A,53,C1,43,78,44,E8,19,6A,77,98,DE), + wwH16(35,15,5F,26,20,29,C0,EE,36,5F,B1,C7,FD,D4,D1,4D), + wwH16(70,AC,98,22,8D,AE,51,BA,E6,97,54,E2,C5,C4,86,BF), + wwH16(B7,A1,C6,71,5D,AA,DF,75,7A,B7,59,52,1C,F2,AF,12), + wwH16(8C,E8,B5,E8,6E,40,46,88,09,29,21,DC,22,18,05,4D), + wwH16(06,ED,C4,86,A8,C3,84,58,40,9C,39,A0,A2,E8,35,44), + wwH16(DA,10,43,90,6D,BD,D5,A7,18,32,BF,2E,06,2C,7B,3C), + wwH16(43,51,EF,5A,F3,83,80,A0,54,C3,9F,6E,B4,4D,FF,B5), + wwH16(92,AF,FD,49,A0,83,9B,F8,16,81,34,CC,E9,2A,50,D9), + wwH16(E3,BC,0E,74,A4,48,F1,44,7C,AC,6A,B7,B2,89,9E,12), + wwH16(11,B2,56,CD,EC,45,91,44,10,D5,68,69,41,D3,03,94), + wwH16(59,3C,CF,43,07,5F,50,5A,DC,AF,96,F5,94,55,DE,ED), + wwH16(EF,14,EA,30,24,AF,BB,39,92,DE,0E,F9,A7,E5,64,36), + wwH16(78,3B,15,96,1C,2A,3E,67,29,35,8A,A7,EF,E8,03,47), + wwH16(14,F1,8C,9A,82,21,92,22,15,A0,1D,02,32,08,D9,27), + wwH16(96,22,4B,EC,4B,49,64,18,77,B5,EF,FE,B3,93,75,23), + wwH16(C2,AE,C2,29,85,66,18,70,CE,FE,89,E5,81,18,00,5A), + wwH16(24,E8,77,D1,65,9D,99,4E,58,EE,17,90,E2,A4,94,FB), + wwH16(E6,36,D6,74,E9,98,2D,3D,90,55,4F,4E,E8,85,AC,D3), + wwH16(BE,BF,BC,23,6D,A8,FD,A4,C9,BD,BF,80,B9,4D,CC,F4), + wwH16(66,DF,40,B0,E3,E5,E5,4A,CA,B9,3A,79,88,BD,C9,B7), + wwH16(7B,82,6E,FC,64,11,59,EF,5E,41,F5,FB,BE,3B,90,7A), + wwH16(D3,10,AF,7A,B9,7A,78,90,61,3C,B6,3E,BC,16,6E,35), + wwH16(E7,76,B6,83,B4,12,97,D7,9F,FE,FD,FF,81,9A,09,59), + wwH16(71,1F,8A,A5,61,ED,7A,89,E8,01,20,08,79,43,66,D5), + wwH16(9E,6D,5F,54,55,49,EC,53,E9,13,4E,2B,E7,6D,87,DC), + wwH16(F4,44,EF,DB,36,77,EE,15,06,E2,15,B3,74,E8,8F,3B), + wwH16(4C,9C,4F,20,07,D0,AA,13,03,E2,4D,BB,6E,61,32,08), + wwH16(C2,38,46,95,01,4F,09,7D,29,8B,4E,C1,33,B1,4C,5F), + wwH16(69,FA,ED,AD,3E,82,FB,45,82,38,28,19,3B,50,74,6C), + wwH16(83,9B,79,48,6E,5D,A6,BD,FD,4F,DF,95,8B,C6,D7,BB), + wwH16(48,C9,D9,A3,44,D2,24,FC,21,E1,63,45,1B,F3,E4,3A), + wwH16(21,71,04,48,5D,7A,6E,0E,9C,FF,18,4A,F2,FF,31,85), + wwH16(D3,1B,5B,67,F6,F4,CF,CD,2E,D8,A4,ED,3B,E9,7D,D7), + wwH16(14,C6,6D,0A,C8,FB,87,DD,6B,B5,E3,7B,9D,00,85,4F), + wwH16(3F,2F,3A,EE,E1,A5,F2,B0,66,C6,D4,12,EA,F8,06,88), + wwH16(E0,23,7C,83,5A,D5,2A,A7,0A,97,33,25,F3,70,90,82), + wwH16(B7,09,87,C0,7D,86,C2,15,18,BE,4B,7B,CC,8C,CC,85), + wwH16(E6,CB,65,04,1C,7F,3C,94,70,7B,6B,69,93,9D,63,33), + wwH16(84,C8,A2,43,C0,8F,07,51,C2,02,B8,E5,FC,2D,37,3A), + wwH16(16,91,9C,6D,57,45,C4,28,BC,24,19,9C,E7,47,05,BD), + wwH16(CF,EF,64,9A,84,5F,F6,9F,DE,29,E0,9C,AE,A2,6F,B3), + wwH16(1B,A2,15,BF,09,2E,21,C2,77,83,2E,02,6E,37,85,E7), + wwH16(74,F7,7D,7D,E8,BE,98,80,B2,D1,40,F5,66,11,65,49), + wwH16(47,9E,DE,C0,AF,B4,F2,FE,20,09,F7,48,1D,D4,CC,AB), + wwH16(9F,63,AD,C0,84,49,A4,32,38,51,19,C2,53,5D,5B,E4), + wwH16(86,9D,88,BB,16,DB,38,3B,A8,47,14,2E,B2,84,0D,9A), + wwH16(D6,54,C4,F9,AE,7E,BD,95,2D,4B,8F,4C,52,51,CB,C4), + wwH16(29,18,8F,83,DA,2F,DB,0E,BB,82,16,8B,CB,C6,57,20), + wwH16(AF,1E,BB,28,E0,3D,B9,C4,3A,C6,E6,F0,29,CB,CA,57), + wwH16(46,C4,90,C1,59,8B,09,64,A6,D7,30,A2,66,E2,BB,62), + wwH16(33,8E,60,89,33,BA,DC,84,95,0D,0B,F3,89,A1,27,C1), + wwH16(D1,A2,5B,2C,C1,DD,44,27,23,F2,A6,B6,92,26,E0,F3), + wwH16(A6,0D,87,42,7E,93,61,03,BA,5A,FD,C2,1F,C4,D4,FA), + wwH16(A8,C8,E3,1E,DF,0C,D4,C9,73,26,31,18,A2,77,72,4C), + wwH16(93,BB,32,40,20,05,FA,53,EF,18,B8,8E,AE,E1,79,C6), + wwH16(E3,2C,2C,77,80,7E,49,F6,4F,2E,C4,7C,54,AA,7C,56), + wwH16(C0,40,68,3F,12,FC,80,CD,F7,E7,D6,4B,51,B4,12,5D), + wwH16(CC,B8,B4,CE,E1,B8,24,57,B8,18,76,0F,68,40,9E,32), + wwH16(85,A9,79,33,11,9E,9E,C2,DD,0C,D5,49,09,E9,14,CB), + wwH16(1A,2E,5B,D5,7F,74,D6,4C,57,C9,5B,04,39,41,86,7C), + wwH16(EE,1F,27,4E,DE,E0,72,4B,19,18,35,E2,B2,DC,47,1C), + wwH16(B8,EC,68,D2,91,27,04,E5,7D,69,4A,CC,E4,40,92,39), + wwH16(EB,86,C5,B8,CF,FE,AB,30,69,ED,38,68,57,74,68,AA), + wwH16(58,D1,1F,63,61,03,0F,71,1E,0B,17,5F,14,41,C8,43), + wwH16(C7,EB,C9,66,2B,BC,AB,5B,96,69,FB,8F,2F,B0,4B,FE), + wwH16(A1,EF,64,B7,EC,E1,40,19,47,43,45,4D,FD,66,BA,7F), + wwH16(01,B4,8E,CF,37,20,D5,54,C0,9D,5C,87,DB,BC,C6,ED), + wwH16(69,D3,77,00,8B,12,D7,0D,7D,96,EF,96,AD,FA,BC,07), + wwH16(68,B8,50,F8,AB,42,66,10,0F,46,4E,9A,AE,CC,B4,BB), + wwH16(89,6D,6F,50,78,92,07,85,47,06,3B,3F,F2,A6,7D,4F), + wwH16(AF,66,42,86,CD,0A,EF,1A,FC,C4,D7,BB,26,5F,C5,0D), + wwH16(C8,F7,BF,38,9D,54,4F,FF,69,D9,E7,3A,0B,31,D6,47), + wwH16(0D,CA,6D,F2,BE,65,FB,FC,78,9C,0E,75,B8,0D,8C,C7), + wwH16(C0,F9,81,B9,A7,EB,9A,CE,EE,BF,2B,29,BB,BE,EE,31), + wwH16(C7,8E,39,7A,D2,85,E0,1C,48,35,F3,15,14,A1,1F,CB), + wwH16(C4,AA,B2,8D,8A,25,84,FF,96,0F,A0,14,4C,09,FE,2D), + wwH16(C0,B5,70,A3,E6,64,96,51,73,66,4F,58,B6,7F,43,5F), + wwH16(BC,1A,B5,61,6B,7B,9D,4B,49,3C,59,B2,CC,5A,7C,B6), + wwH16(54,FB,8A,3D,18,D7,9E,C8,65,44,45,14,B8,04,6C,A6), + wwH16(4F,EA,B5,CE,C4,F0,BC,71,40,1A,53,82,EF,85,7C,E8), + wwH16(1A,BA,D9,3C,D9,5E,CB,C0,40,A6,6C,66,29,6E,8B,32), + wwH16(34,84,C9,AC,B3,A3,36,90,06,A1,BF,48,F4,B3,09,6D), + wwH16(41,69,48,2E,D8,9E,72,B1,D2,26,2F,E7,04,DA,4F,9F), + wwH16(F7,16,49,68,1B,13,46,36,37,78,0D,0B,8C,0B,35,74), + wwH16(54,32,B3,9E,09,7F,34,EA,E6,18,D5,78,49,E2,EA,76), + wwH16(F3,62,B1,6C,4E,61,33,B3,94,92,7D,C7,0C,AD,13,60), + wwH16(19,22,41,FD,4A,28,C6,88,02,C3,3F,F2,46,BF,0E,7C), + wwH16(B4,89,48,F0,92,00,BB,F9,6D,41,3B,3C,A3,22,16,AB), + wwH16(A6,A5,EB,52,46,5A,A9,FA,DC,6F,D7,20,F5,F1,B1,D1), + wwH16(CA,A2,E9,42,A5,4E,28,BA,CA,2E,51,5D,A7,24,98,D0), + wwH16(64,F2,C6,44,59,F5,D5,BD,6F,34,F1,C0,BC,A3,88,89), + wwH16(9A,32,7C,86,6B,B5,5F,2B,79,B8,75,1C,56,C1,ED,C3), + wwH16(03,41,84,31,7B,54,A5,93,67,F5,A1,9C,87,94,91,EF), + wwH16(1F,81,82,D8,81,EE,A8,7E,DE,E2,A9,7E,B4,28,46,3C), + wwH16(6D,19,A1,BB,A2,AB,39,99,0D,53,A4,5A,67,2C,E9,3E), + wwH16(25,F3,39,18,CE,E5,7C,6A,25,54,69,7D,68,4E,6B,DA), + wwH16(FD,0D,98,A7,5D,28,F5,23,4D,14,35,6F,19,71,9B,AF), + wwH16(D7,FE,B5,C9,E0,7A,8F,7A,5F,47,57,B9,2B,A6,C4,B7), + wwH16(00,4A,9C,12,0F,06,E4,46,67,F4,B7,E2,04,A1,C6,69), + wwH16(C6,7C,B7,C6,BC,D3,7E,20,E6,D1,E3,19,C6,C9,93,AC), + wwH16(A3,EA,89,78,5B,89,0A,37,35,9E,DD,2B,CD,46,FD,40), + wwH16(25,A4,D9,C5,F6,26,FE,E9,8C,1E,FB,64,56,6C,93,25), + wwH16(9A,88,FB,61,15,9E,57,9B,86,D8,83,F4,23,7D,F1,89), + wwH16(97,09,4C,96,FD,16,90,01,42,A8,57,23,37,B4,75,BB), + wwH16(A1,7F,22,E6,BF,59,82,7C,0D,10,72,50,1B,76,D1,FC), + wwH16(9B,8B,BD,08,EE,DF,9B,FE,9D,64,D4,06,68,59,0F,21), + wwH16(6A,32,CA,5D,27,80,F6,57,AB,B9,D5,B3,40,C4,61,79), + wwH16(5E,FB,0D,62,60,B8,3D,9B,D7,34,F0,03,B9,5A,BC,8C), + wwH16(A8,26,AC,8B,1D,BF,60,9A,4D,AE,6D,07,CE,5B,CF,DE), + wwH16(CD,AB,4A,84,D6,A9,61,AA,11,BD,40,07,5C,81,A5,AA), + wwH16(25,DE,5E,5C,42,23,FC,26,89,D8,68,BD,CE,DE,7E,34), + wwH16(FB,6B,8F,E0,2E,2E,99,D9,8E,15,31,46,C8,13,E7,C0), + wwH16(97,B9,58,27,36,53,21,E0,3B,3D,85,EE,8B,D0,B2,67), + wwH16(85,C8,32,F6,C3,13,17,D2,A7,90,3A,47,E7,20,D3,9A), + wwH16(F2,56,B7,74,FF,C6,D6,80,F7,83,0D,D0,59,47,41,E9), + wwH16(56,8B,1E,31,BD,FA,FE,43,9A,CC,09,A9,FA,9A,DB,EB), + wwH16(51,4F,EB,44,1D,92,40,8F,BD,FC,18,B5,B7,39,E0,5F), + wwH16(71,62,BB,E4,19,72,72,15,BA,29,9D,38,39,E6,AC,A5), + wwH16(41,F0,CA,ED,95,CD,53,18,E2,22,48,D5,AB,71,EF,A4), + wwH16(F1,19,E5,7C,19,9F,EF,68,DF,FE,50,EA,31,EA,94,FE), + wwH16(12,EC,72,F6,CD,31,32,0D,6B,54,7F,94,33,B2,C7,87), + wwH16(92,E0,15,20,D4,55,AF,05,FD,F4,B5,B8,B6,5F,AE,80), + wwH16(9A,AA,62,16,6C,F3,68,B7,5F,B3,1E,D2,8F,83,38,09), + wwH16(12,C7,B1,C5,4A,90,A0,E6,25,24,F3,15,35,C4,B1,1E), + wwH16(B7,AC,72,13,2B,0F,2A,86,61,36,7F,FA,53,66,49,C6), + wwH16(D7,59,4F,22,63,F9,C9,EA,37,0E,ED,F1,FA,9A,FF,FC), + wwH16(3E,C0,32,00,F0,BB,62,03,AF,ED,D5,72,B1,62,3D,95), + wwH16(53,A7,EC,64,BA,5A,52,B1,85,E5,E1,14,40,EF,A5,71), + wwH16(3E,35,D1,B8,FF,99,01,E8,7D,A9,1F,58,6B,0E,AE,3E), + wwH16(78,DE,40,79,B8,9C,31,E5,AC,64,A0,FB,B2,AB,C1,FA), + wwH16(32,49,D1,8A,81,07,DB,14,1A,A6,D3,C3,0A,ED,E1,07), + wwH16(83,5E,03,0F,60,EB,CC,4B,F0,0D,C5,DC,BF,1E,86,D8), + wwH16(FB,21,E3,22,1E,D6,4D,1B,A4,EE,33,3A,71,9C,98,A1), + wwH16(4A,2D,E4,6A,5F,CD,D7,AB,54,DC,E9,5C,92,3F,59,B9), + wwH16(D6,B3,6F,A8,4A,06,16,68,C6,40,88,D5,B4,11,AF,88), + wwH16(E4,7F,95,72,2B,C4,A4,B1,23,08,AC,9D,A9,0A,55,50), + wwH16(FD,6C,9B,A2,4C,8C,EC,73,FB,CE,9E,8E,59,A4,7F,3E), + wwH16(E5,48,79,6B,DD,A8,F6,97,81,38,9A,01,EF,FA,32,4A), + wwH16(92,55,AC,38,DC,D5,A9,AF,01,8F,A9,82,DE,B6,83,55), + wwH16(6E,AD,4F,C9,16,8B,4E,B9,C7,17,E7,F4,F1,39,BE,91), + wwH16(10,F0,50,85,A9,62,4B,FB,FA,ED,9B,24,25,4B,BC,8E), + wwH16(E3,9B,DB,64,E8,AD,42,CD,AE,4B,C1,7E,33,B2,94,39), + wwH16(C1,05,16,5A,74,E7,E9,EB,DF,7D,EA,DB,FC,E2,36,99), + wwH16(32,E3,2D,B5,D6,1A,17,F9,55,D1,F6,5C,EC,C5,3C,D3), + wwH16(C7,C8,DE,28,F5,C1,7D,DE,88,62,6A,0F,11,04,9D,6E), + wwH16(ED,82,E1,9D,39,8D,98,82,6E,24,9E,36,1B,EC,19,8A), + wwH16(B5,35,E0,D4,30,C5,9D,C7,68,2C,6C,25,FE,AD,8F,46), + wwH16(5C,03,0D,A0,29,DE,44,74,D5,B6,20,8C,01,CE,84,32), + wwH16(CB,77,AD,97,53,5B,4F,6E,9C,25,25,CA,FE,19,59,E3), + wwH16(B1,D5,94,71,41,4E,30,FE,28,52,55,11,56,3F,B8,F9), + wwH16(FB,24,3C,A9,74,47,01,D1,A8,81,DA,AC,B6,99,B8,CF), + wwH16(78,D5,A5,A8,54,78,31,A0,9E,40,A0,63,3D,5C,E6,A4), + wwH16(2C,C5,61,A9,D4,56,3E,9A,95,3E,57,12,6D,D5,D4,31), + wwH16(55,34,69,DC,17,50,15,7B,21,3F,F6,2D,6D,86,61,3E), + wwH16(77,5F,AF,36,0D,FF,DF,6F,E8,C2,0F,E8,6F,86,B4,51), + wwH16(9D,22,E6,EA,FE,F2,EB,D9,C3,45,1F,24,02,2E,EF,13), + wwH16(E5,EC,83,74,E3,D9,67,0F,1D,6C,A9,70,43,CD,AF,20), + wwH16(B1,20,4B,BF,F6,49,AC,2B,60,00,0F,C1,8C,AB,82,C3), + wwH16(3B,B6,60,22,6F,BF,46,03,31,52,B4,42,B2,5F,D8,C5), + wwH16(CA,7C,8B,E0,3D,9F,A3,6A,BC,EF,5F,FC,32,AE,10,F9), + wwH16(34,71,1C,48,C6,23,1C,DB,9D,97,7C,59,22,D1,F3,0C), + wwH16(88,9D,3F,46,C0,5B,54,5B,5F,0A,57,69,05,BC,26,D4), + wwH16(70,14,C1,EF,65,52,81,BA,6D,46,45,61,F4,00,6C,F7), + wwH16(7F,91,7F,16,17,F6,C9,06,E2,60,0E,E2,43,94,4B,06), + wwH16(61,0C,02,3F,4D,B4,DE,48,12,2E,E0,59,25,E6,13,AD), + wwH16(86,9B,AF,09,58,CE,0F,77,68,93,24,70,92,72,BB,CD), + wwH16(B9,0F,9A,D1,CF,07,94,BE,B0,D0,03,41,EA,95,8C,59), + wwH16(21,F0,3A,A8,5D,85,B1,04,BB,85,03,95,7C,31,C0,18), + wwH16(27,51,A7,DD,F0,7E,FA,B9,7D,26,EC,2E,A1,72,BC,62), + wwH16(FC,AC,86,81,2D,69,D1,47,45,6D,97,1B,87,D8,6E,5D), + wwH16(67,E8,4E,66,4F,5D,88,7F,6A,E0,6C,69,75,F3,35,80), + wwH16(9A,B4,6E,04,CB,32,B8,56,25,CF,D7,4E,BD,F0,2D,1B), + wwH16(A9,6C,08,87,36,E3,69,D1,85,61,19,D1,F5,77,26,EC), + wwH16(18,22,53,E1,2F,92,F1,9E,31,DC,4E,A7,0B,A0,C0,08), + wwH16(35,BA,44,63,7B,5B,45,59,FC,34,B1,36,60,ED,0C,25), + wwH16(7C,09,66,D3,54,7B,62,DA,28,FC,BA,58,6C,0D,86,52), + wwH16(32,C7,72,B8,6D,08,75,39,B1,4A,33,B2,E3,BA,81,68), + wwH16(49,99,08,89,D7,35,41,76,20,5C,77,E4,2A,40,A7,67), + wwH16(05,84,45,3A,BC,A6,24,D6,3F,6B,B1,21,35,9F,04,4E), + wwH16(80,15,77,16,37,A6,49,A1,19,E5,A4,18,5B,21,E4,5F), + wwH16(3F,36,55,51,8D,9E,3F,A3,6F,0B,24,C7,A1,96,53,DD), + wwH16(F2,9C,9F,60,77,49,61,25,06,EC,1A,E2,58,9C,C8,0C), + wwH16(EC,10,34,02,90,5D,84,6F,1B,F5,DE,E8,F6,FD,AD,6D), + wwH16(75,CB,C8,90,D0,3C,74,7C,2C,ED,0D,83,36,35,6E,53), + wwH16(59,03,DB,DF,DE,09,32,DF,F1,20,D1,AF,2E,EF,7D,26), + wwH16(27,4C,FC,5B,9C,49,CE,88,52,26,54,AF,A5,FF,C7,18), + wwH16(34,0C,B6,04,E2,FB,4D,0A,22,B9,F4,2E,61,54,F6,77), + wwH16(AF,7C,E7,06,7C,BF,17,85,AD,2D,61,87,A6,A5,A4,1E), + wwH16(71,F4,AE,2C,76,7F,0C,03,BD,BF,B9,75,ED,F9,C7,DC), + wwH16(70,3A,81,A6,A1,3F,83,52,AB,09,1C,76,36,51,7F,1C), + wwH16(4A,0C,32,01,12,38,BF,41,83,7E,B3,A7,F5,BF,91,3F), + wwH16(65,EC,D4,5B,02,F1,21,A0,92,29,48,9F,F5,91,36,64), + wwH16(8D,11,65,E6,2B,5F,CB,26,F7,F1,08,19,24,90,7A,AF), + wwH16(93,32,92,5C,99,C7,50,5C,6E,11,0B,C4,68,33,F2,ED), + wwH16(F2,5D,6C,F1,62,FD,48,7E,A9,5B,59,CA,40,5A,3C,5F), + wwH16(B3,B7,29,B5,D6,68,AF,51,BE,DB,BD,23,62,F1,88,12), + wwH16(35,9B,30,3E,89,49,0B,2D,53,6B,7A,39,91,C7,AA,62), + wwH16(0C,1D,26,F4,6F,91,4E,1E,5B,D7,84,7E,BA,6A,A6,A6), + wwH16(A4,92,49,C6,97,88,AB,B9,00,AF,D4,58,F2,0B,9D,5E), + wwH16(EB,E8,4E,4B,B6,01,AE,8C,42,1C,FE,D7,AC,A1,5D,B1), + wwH16(A9,51,80,F4,20,CC,9A,F7,56,B0,04,A3,6E,8D,49,75), + wwH16(6B,60,4F,A9,F2,C3,F9,E1,2F,B7,D1,EE,36,7E,90,B6), + wwH16(28,FF,F7,06,42,39,AD,6B,40,1A,10,A2,CB,28,85,48), + wwH16(0C,9C,68,11,81,7A,3E,07,E5,C9,D1,C5,52,83,5E,7E), + wwH16(78,8D,1A,34,80,B8,48,88,1A,97,10,C9,D4,DA,D0,38), + wwH16(C3,0F,1F,12,23,E1,03,78,3F,35,E9,62,84,88,40,C4), + wwH16(6E,8B,22,5E,E2,B2,75,C0,10,E3,A2,B8,EA,8D,09,7B), + wwH16(CF,5F,7F,78,33,6B,06,FA,47,DF,05,80,43,83,9B,5F), + wwH16(73,8F,1C,F2,85,94,64,85,FC,8F,2B,4E,3F,A9,84,76), + wwH16(FC,B1,DC,FB,B0,77,7C,CC,DD,77,D8,7B,E2,47,A4,09), + wwH16(8D,9C,74,70,E8,6D,10,9B,7C,6F,0D,1C,12,02,B2,D0), + wwH16(CA,56,6C,1E,AD,C8,F0,1C,8A,E6,D5,6F,F2,A3,47,1B), + wwH16(B5,D0,13,FC,72,69,4A,83,7D,B9,54,86,BE,1D,39,31), + wwH16(EA,E3,99,5E,66,13,AA,1C,15,65,C3,48,38,0A,74,A9), + wwH16(F6,E3,83,EA,58,48,52,7C,A0,69,FA,8B,9C,77,4A,E4), + wwH16(CF,6A,1D,00,AF,AE,09,A6,BB,50,73,6C,B6,CA,F0,E6), + wwH16(3A,B4,BB,BF,8E,9D,52,08,0A,E0,EE,9D,D5,3A,F2,B1), + wwH16(64,54,7B,F9,CB,98,4C,3D,7A,8D,39,03,DD,57,FF,8D), + wwH16(BB,09,73,14,7D,2A,74,45,CF,C1,52,91,D2,62,54,34), + wwH16(B7,D4,18,2F,31,3D,98,3E,BF,96,81,64,92,19,61,69), + wwH16(97,7C,87,1E,C8,A2,56,C6,18,CE,54,2A,35,28,96,0B), + wwH16(63,7A,AE,D9,F9,B2,6D,62,00,E9,12,03,42,A9,12,BA), + wwH16(5E,F6,7A,56,54,F5,08,F5,2A,A9,36,33,69,72,71,3D), + wwH16(8A,65,C9,D8,C0,9B,D6,78,AA,B9,10,6C,69,BE,91,BD), + wwH16(29,22,FD,83,D9,0E,F8,1F,F2,EE,B1,F5,C7,B4,F6,20), + wwH16(85,C6,F7,69,D8,7B,FA,7D,31,EC,C2,01,A8,EF,D0,E7), + wwH16(78,AA,BD,74,D8,E4,6D,C8,81,47,08,AE,51,DE,2B,40), + wwH16(65,47,6D,7B,04,E4,BB,E7,D8,74,86,E1,86,2E,01,A1), + wwH16(CD,53,4A,C7,1C,53,88,72,89,BA,7C,86,8D,9A,E9,A0), + wwH16(9E,91,29,94,A5,3F,8A,E9,BF,0A,0C,F1,93,B7,81,74), + wwH16(31,D3,CC,0C,8A,1A,EC,C4,E2,2C,F5,E4,FE,02,62,9D), + wwH16(DF,28,9D,23,1C,1E,E2,BF,66,FB,42,A3,BB,0C,1B,69), + wwH16(EC,BD,4F,99,86,F8,3C,91,DC,23,C9,AE,00,C3,C6,DB), + wwH16(F1,D8,4A,02,21,7C,C6,9C,33,C4,47,5C,83,A8,76,FC), + wwH16(8C,30,0A,B6,1C,2E,DD,A4,9D,71,AA,D6,C0,46,91,69), + wwH16(65,6A,48,DF,BC,CE,EC,4F,D5,BA,2A,C5,19,98,74,FE), + wwH16(0C,CB,A4,C2,D3,74,9D,1B,D3,E1,7E,E9,A8,68,3F,1E), + wwH16(1D,8B,07,77,63,F7,F5,B6,42,DB,80,43,BD,BF,59,64), + wwH16(79,3B,7E,2C,92,C0,49,1E,D3,E1,A8,FC,C7,67,17,21), + wwH16(73,4D,73,94,8B,27,1A,7E,89,07,26,0B,27,75,56,E4), + wwH16(58,A0,A2,EB,3D,F3,F7,23,35,0E,54,C9,72,80,A4,20), + wwH16(60,07,14,FA,FC,A4,AB,86,69,F3,D5,A5,BD,5F,8C,A2), + wwH16(E9,62,95,68,67,DF,BA,B0,D9,CA,8B,7E,6F,C2,20,3A), + wwH16(B5,CF,EA,6C,64,4E,D1,53,AB,0C,58,49,88,66,1E,E8), + wwH16(63,5F,B9,7E,2C,C6,D2,0C,0D,3C,D7,6B,14,CF,53,2A), + wwH16(71,5E,15,DE,AA,41,C4,6C,A8,C2,82,01,F0,48,48,BE), + wwH16(66,E3,02,DA,20,2C,54,BF,60,BB,2D,4C,AA,22,84,E2), + wwH16(70,38,8B,0A,D6,5B,9C,03,CA,7C,87,F9,71,12,6C,18), + wwH16(6A,CD,21,F6,65,9C,11,72,AF,DF,30,96,80,67,91,21), + wwH16(EE,34,10,91,BE,39,69,75,A1,93,9C,60,78,91,B2,B4), + wwH16(0E,D5,38,6E,AD,63,F7,FC,63,EA,1F,BF,FD,FD,D6,66), + wwH16(FA,63,3F,CD,52,96,15,EF,0F,E2,E6,64,35,02,C9,4E), + wwH16(7A,48,D4,6F,A8,B4,E9,EF,A9,F9,98,B9,5D,B3,8C,9D), + wwH16(EA,EB,EC,7C,E4,22,CA,2C,44,28,E4,86,7B,5F,FD,A6), + wwH16(BD,07,AF,37,89,53,39,6E,C3,50,71,A3,95,55,11,4B), + wwH16(2E,57,D0,CE,E5,50,01,77,06,61,28,70,92,33,1D,8A), + wwH16(27,3F,26,63,F2,CD,8D,EF,8B,AF,94,E3,E0,FE,43,3D), + wwH16(16,94,9A,2B,3D,78,CA,6D,CF,5C,45,4F,42,7B,F9,58), + wwH16(29,39,04,C9,38,3F,03,7E,2E,5C,33,97,4A,1A,C7,C0), + wwH16(D5,71,4D,82,93,65,8D,DB,B6,75,5F,E5,E3,ED,65,82), + wwH16(3D,43,06,FB,32,35,76,60,DF,CC,6C,F0,01,52,9C,DE), + wwH16(25,30,73,75,F1,D5,18,56,CE,4A,1F,4F,51,B8,4E,F1), + wwH16(14,62,D0,1B,9F,BB,4F,61,B9,14,B9,13,1B,C9,49,07), + wwH16(96,81,C0,81,B1,A4,15,0F,CA,F5,7A,E0,E0,14,08,6E), + wwH16(4B,BE,0E,D8,E0,5A,B9,D7,D3,20,96,24,BB,96,44,58), + wwH16(29,FC,7F,0B,0F,C2,2B,75,E2,D3,17,5B,76,19,FD,F5), + wwH16(D1,BA,6F,4B,DE,24,97,14,63,52,54,E9,A9,D0,94,CB), + wwH16(A5,62,16,D2,4A,DE,57,54,08,EF,F7,35,F4,EA,D2,04), + wwH16(BC,73,84,93,7A,D2,0E,EF,CF,BD,8C,16,C7,40,85,51), + wwH16(01,83,45,F4,B2,57,9F,52,62,F8,23,D0,E8,F2,2E,5D), + wwH16(B9,A5,44,AD,A4,74,42,1E,24,A8,31,50,3B,DE,1D,39), + wwH16(A0,0C,BE,3E,8C,2B,A9,15,3A,E8,86,73,38,D9,D6,0D), + wwH16(E2,D0,9C,42,2E,45,5C,B9,CE,9A,33,BE,4E,3B,48,90), + wwH16(1C,04,DB,F0,A8,E3,FD,55,2A,0E,5D,25,B4,6A,51,4B), + wwH16(35,4A,A4,A8,B4,DE,96,ED,5B,36,BE,09,45,0C,CF,CD), + wwH16(62,FB,69,C7,23,0D,1C,57,39,6E,28,45,27,6A,07,F5), + wwH16(79,9A,09,5C,A4,2C,ED,F5,C3,55,BC,23,CB,8C,01,E0), + wwH16(59,B0,E5,8F,C8,BE,9E,C4,89,22,60,43,77,3B,85,91), + wwH16(AA,F3,5D,F3,90,6A,3C,1A,24,6D,A1,7E,9D,8C,89,8D), + wwH16(90,70,93,F7,7E,EC,CE,6A,02,75,3D,BE,77,4B,DC,92), + wwH16(4A,70,56,F7,50,F4,17,91,F9,00,81,FD,83,15,31,FE), + wwH16(E5,A1,E1,B5,23,04,F7,AF,A0,66,57,45,8F,38,8D,3F), + wwH16(C2,6E,30,04,BB,A5,24,F2,59,A3,26,51,65,46,4B,4A), + wwH16(C2,BF,0D,80,C2,33,9E,9B,CC,58,1A,DF,07,10,D7,86), + wwH16(66,4C,C2,56,43,87,2C,EA,40,E5,28,D1,32,3A,B0,03), + wwH16(F2,B9,E2,61,84,FD,3D,6A,69,1D,E0,A9,1C,43,34,EA), + wwH16(ED,86,46,3A,B6,20,66,3D,D0,07,74,BF,D1,73,BF,07), + wwH16(8C,A6,2C,90,37,5D,B9,18,74,CA,AD,C0,03,05,A4,87), + wwH16(24,89,51,56,43,C8,8D,E1,AB,BE,7C,8E,AE,E5,E7,2F), + wwH16(83,78,88,85,5B,6F,45,5C,35,CD,0C,96,E9,18,69,E9), + wwH16(CE,CA,77,48,56,89,71,BD,CA,9C,7E,87,53,B0,CF,1F), + wwH16(60,C6,A1,70,16,B7,67,65,85,38,0E,13,B4,33,DB,80), + wwH16(09,3C,3B,DE,95,E5,A7,00,99,81,47,F5,72,8F,D3,BF), + wwH16(57,F8,36,79,CC,EE,18,CA,57,F3,66,11,CB,B8,92,43), + wwH16(48,13,CA,36,40,75,0F,8C,E4,FF,99,0B,DF,64,F3,D6), + wwH16(B6,C6,68,EF,55,8D,9D,4B,61,0B,73,38,A1,E2,D3,FF), + wwH16(7E,B6,57,09,8E,DC,3C,EF,41,42,F7,F5,8C,90,58,B3), + wwH16(38,F5,52,3E,1F,F5,5A,3B,7C,F9,D9,58,3E,2A,42,3B), + wwH16(C1,29,EC,9A,50,86,40,F1,D6,DE,1A,CF,3A,8A,7E,6B), + wwH16(B3,84,7E,FC,F8,B6,5D,C4,B1,E4,BE,F5,4A,27,7D,3B), + wwH16(32,75,34,BD,DD,5F,53,CC,49,B2,C8,2E,68,AB,E4,37), + wwH16(94,85,05,E4,B0,AB,98,DF,A0,5B,65,29,07,83,37,B9), + wwH16(C5,51,41,52,6E,AD,19,12,6C,45,BE,E2,7D,2F,88,1E), + wwH16(68,DA,CB,D1,91,01,BC,75,18,2A,FC,63,41,44,82,80), + wwH16(F8,69,05,7E,EF,7C,54,DE,83,B4,3B,27,CA,7D,6E,60), + wwH16(73,1F,88,8E,B9,76,F4,86,7F,7A,D5,38,1D,8A,1E,B9), + wwH16(AE,D8,93,22,E1,90,DF,13,96,8A,82,08,A0,6B,21,07), + wwH16(23,D0,C1,C7,37,DE,47,5D,21,CE,79,A1,16,F7,73,7D), + wwH16(38,D8,DB,00,E1,98,2D,CC,B5,B2,E6,51,BA,68,56,46), + wwH16(C2,CC,30,07,98,27,FF,A2,C8,FF,3E,E0,FD,FD,C6,44), + wwH16(4D,3E,C7,E8,0D,29,FE,91,E2,66,29,EC,C2,31,5A,AB), + wwH16(3A,3F,D8,14,48,66,81,7C,B5,55,AD,91,91,10,2D,CD), + wwH16(E9,0A,29,FC,57,15,F1,D3,CF,56,39,7E,77,A2,BA,45), + wwH16(08,01,56,07,01,89,EB,60,2E,BA,C5,16,53,CE,C5,30), + wwH16(52,6E,12,6E,3F,8C,92,F2,E8,CF,D4,3D,8B,B6,73,F8), + wwH16(E0,7F,A8,87,1F,9E,D2,69,10,B2,0D,66,29,F6,F6,B8), + wwH16(17,5B,3D,D5,77,B0,62,25,21,7D,BF,E3,71,1D,83,3A), + wwH16(4F,F7,51,B9,AE,C6,98,E1,53,46,F7,AE,66,47,86,1F), + wwH16(CC,CF,E2,AE,10,85,3C,81,0B,CB,FB,37,3F,E7,BC,8E), + wwH16(9C,47,01,E6,BE,B1,B9,C0,10,36,04,17,18,B3,D2,9C), + wwH16(94,98,13,30,3F,C1,1C,C4,A7,C3,5C,72,DB,FE,B0,AE), + wwH16(37,12,CA,6F,F5,F4,C3,04,57,4A,1C,49,D0,9C,50,B4), + wwH16(C9,90,7F,64,CC,3B,CB,E3,1C,E1,93,7D,3B,1F,7F,F4), + wwH16(93,01,3A,59,96,D3,6F,7A,91,18,B8,4E,9B,D3,DF,F3), + wwH16(7F,00,C7,37,F7,B7,9D,A5,54,FC,4A,22,28,8D,B2,C7), + wwH16(46,84,C3,4A,B4,5C,0E,BC,9C,75,6C,A4,45,8D,88,45), + wwH16(D1,10,C1,96,09,87,04,D4,92,52,7A,16,FE,B9,F5,9F), + wwH16(D5,D3,1F,D1,78,58,F5,01,AF,B5,B7,D5,7E,B7,00,7B), + wwH16(38,BA,07,C3,70,25,AF,3E,95,59,24,C7,4E,2C,08,C3), + wwH16(56,78,BB,35,95,26,8A,A1,70,05,8E,42,E9,96,28,DF), + wwH16(67,59,A9,FF,6F,CB,1D,1E,01,84,1C,6C,7F,5C,83,C9), + wwH16(0E,92,23,F5,B9,41,CA,F3,BF,5F,68,60,77,C0,4C,0D), + wwH16(11,1C,77,C2,33,89,E6,DB,CB,01,DF,DA,1F,07,EB,EC), + wwH16(19,9C,47,AB,E0,BA,E8,50,C3,9E,BC,A0,61,77,89,62), + wwH16(F0,7A,CB,B5,24,BB,64,64,E6,BC,33,60,0C,41,98,F9), + wwH16(90,1B,B9,A5,5E,F1,76,31,48,51,51,DC,1D,AE,E5,0F), + wwH16(46,28,90,56,BF,C0,74,AC,58,EB,42,AF,D3,B6,B2,41), + wwH16(BD,E7,2E,49,27,D1,49,4F,8A,6D,32,23,60,D3,4D,2F), + wwH16(AE,8C,99,90,19,FA,22,A5,A6,D4,E3,EA,0E,CF,93,27), + wwH16(71,31,0F,37,7B,5A,29,0C,8C,54,60,FC,F4,17,CF,17), + wwH16(0E,02,12,9E,08,02,21,CC,98,15,AA,9F,20,AC,F9,2E), + wwH16(11,C2,71,7B,67,7D,0D,43,8A,42,50,AF,AC,A0,F9,B0), + wwH16(27,55,5C,C5,60,6E,95,33,36,AB,33,F8,0B,D3,87,0E), + wwH16(1A,59,66,6E,15,1A,60,D4,F1,BB,55,CD,F8,05,7F,52), + wwH16(2D,E3,CC,04,9A,85,4B,82,7F,56,7E,52,59,AF,E4,97), + wwH16(A5,87,B1,90,A4,B3,3E,80,FD,BC,CD,C8,80,5F,91,89), + wwH16(5A,85,E4,07,7C,D0,E9,68,44,77,2B,E3,6E,FF,4D,3D), + wwH16(31,66,2E,9F,8D,D4,B1,8F,2B,5C,B0,C1,06,C1,36,65), + wwH16(6D,F6,53,2D,65,F4,6A,2B,3C,B5,BB,FB,60,CD,15,BE), + wwH16(00,03,3B,73,72,89,48,C1,F4,CE,19,FC,0A,4A,7B,CB), + wwH16(86,5C,D3,94,EF,7F,2B,6E,D0,42,41,EA,D6,99,69,DC), + wwH16(7A,64,59,58,59,AD,16,9E,62,B7,74,AA,15,70,AA,3A), + wwH16(E3,E8,73,77,F1,3E,66,D3,B1,BF,DC,95,E3,01,45,31), + wwH16(38,B7,79,81,C1,C6,C8,00,1E,ED,11,36,3D,49,D4,0D), + wwH16(48,DE,35,B5,B9,DE,04,47,58,10,8F,64,40,BE,AA,36), + wwH16(37,A8,A1,20,A3,61,CD,31,B3,16,D8,71,87,51,EB,5D), + wwH16(09,60,07,0E,12,AB,A0,DD,0A,B3,B1,BC,C6,44,56,B7), + wwH16(40,B4,D9,ED,AF,D2,6E,8E,38,28,9F,DA,22,7B,94,68), + wwH16(67,7C,02,44,94,1B,96,4C,8D,CF,34,B6,CF,1F,9F,1E), + wwH16(C8,0F,DB,B2,26,01,F3,11,77,9A,EE,06,28,ED,10,9E), + wwH16(71,D4,D0,F8,A0,4D,87,74,66,89,9A,8F,52,CF,4A,98), + wwH16(94,8E,F9,75,20,89,46,25,7F,A4,90,C8,92,50,20,95), + wwH16(35,AA,CE,5F,1C,C0,3F,B8,9E,E5,6C,D2,8F,52,4C,C6), + wwH16(0E,23,B9,0F,E6,59,B0,DF,4B,8A,C4,79,25,5C,F9,70), + wwH16(85,A0,C6,89,EF,23,1D,73,56,9E,38,D3,59,ED,2C,E2), + wwH16(34,18,12,DD,EE,03,DF,4E,60,99,22,55,3C,D0,65,01), + wwH16(09,40,DB,BC,C6,37,BD,ED,38,38,02,F2,C5,E8,94,0D), + wwH16(18,BA,3D,99,CF,DA,41,77,64,E1,67,43,39,BB,64,77), + wwH16(64,16,77,4E,6F,60,E7,BD,94,E8,31,11,58,C3,84,2F), + wwH16(73,56,1C,6F,30,5E,E7,C0,00,EC,67,7F,88,9E,11,66), + wwH16(DC,4C,F0,EC,E6,2D,4D,B8,A7,23,1B,ED,25,80,0F,35), + wwH16(6B,38,31,27,50,4E,0E,D4,FA,33,86,6C,1A,88,21,F1), + wwH16(A5,00,F6,7A,C2,79,07,9D,CA,09,7E,F8,33,52,9D,6B), + wwH16(78,AE,7E,94,46,7C,3B,12,6B,44,82,8E,1A,B5,4B,CC), + wwH16(F0,79,AB,7F,60,11,B6,28,DF,0F,E7,31,BD,93,9F,EC), + wwH16(48,CD,DC,35,73,86,B3,E4,CC,6B,51,46,31,33,22,32), + wwH16(03,2A,14,32,5F,A0,91,A1,33,5B,64,E4,D2,F9,C4,60), + wwH16(E9,58,6D,19,DA,43,7C,28,41,6D,37,3E,22,43,BF,D4), + wwH16(CD,1F,B0,68,57,A2,94,46,03,33,DB,7F,18,E8,95,AE), + wwH16(D4,F1,DE,0B,DA,A4,8C,14,1B,C6,C9,1A,5E,81,86,10), + wwH16(A8,92,01,49,4C,9C,03,EF,83,A9,EF,FA,FB,9C,32,B0), + wwH16(5D,6D,44,25,BF,E2,5F,13,20,E9,96,6B,3C,3B,43,49), + wwH16(40,8C,08,0C,E8,BC,42,35,BA,51,60,C8,A3,7E,A3,10), + wwH16(CD,2D,AE,FB,27,63,47,3B,6B,52,9D,01,47,78,C8,2E), + wwH16(D5,C3,8A,3A,D4,1D,0A,BF,6F,20,04,A1,05,9A,5B,56), + wwH16(E6,13,90,6F,5D,75,1B,7B,C4,DF,B6,F3,C4,94,47,62), + wwH16(46,2C,28,9A,E9,6E,E4,17,31,25,D1,44,88,25,00,1E), + wwH16(C8,B9,BD,E4,CD,23,99,85,78,10,14,C2,6D,B4,B1,4D), + wwH16(01,49,4D,62,37,10,3B,E6,C5,CC,AA,E5,3E,40,B1,56), + wwH16(EB,5F,1F,26,61,8D,08,A0,61,3C,E6,1F,E6,14,10,D9), + wwH16(85,15,1A,B9,98,AE,31,7D,59,CC,98,74,3F,4F,DD,01), + wwH16(5B,5E,C9,96,9A,14,8D,3F,B3,B2,C7,25,48,28,03,A3), + wwH16(48,63,59,00,02,F2,2F,1D,4D,BB,A3,85,91,B9,8C,22), + wwH16(20,43,20,58,82,FB,42,EB,A7,6F,33,EC,6B,63,99,1C), + wwH16(C3,20,CD,28,D3,B2,4B,B8,91,A3,D6,8F,23,0D,CC,31), + wwH16(AF,BE,B9,3E,2B,B6,FE,02,1D,DF,7A,B4,E6,5A,81,B6), + wwH16(CC,94,F0,2C,96,75,E0,B9,F0,D2,52,70,D7,22,E0,9F), + wwH16(F0,60,EB,BA,19,6B,BA,D6,BA,33,49,E7,E5,A8,46,E9), + wwH16(6B,5F,3D,08,07,19,27,AC,7C,7E,7B,A4,43,EF,BD,49), + wwH16(AF,30,DF,37,72,A9,85,6F,AF,5D,FC,5B,B2,4B,E3,69), + wwH16(6E,EC,60,AD,ED,49,1C,63,E4,2A,97,EC,A5,7B,05,AA), + wwH16(07,27,28,9F,58,5E,50,1E,2F,CC,76,72,D8,CF,DF,2D), + wwH16(E1,C9,7F,9B,05,48,B7,8C,45,02,0E,80,D9,49,78,CB), + wwH16(B4,33,AE,CF,01,2B,56,DE,26,4D,78,D2,BB,0E,91,41), + wwH16(74,9C,D4,19,5A,4B,9C,71,DF,8B,D7,55,E3,42,84,6B), + wwH16(3E,3D,49,2F,E2,01,A0,47,CE,45,54,8F,18,42,8E,BB), + wwH16(87,FB,9B,02,0A,F5,0E,D5,90,82,88,D9,C9,D5,FB,6C), + wwH16(A3,D0,3C,71,3A,31,7B,70,D0,F0,CC,85,60,8F,48,53), + wwH16(9A,11,54,9E,51,FE,26,91,D3,93,BC,A7,0A,3D,1E,F8), + wwH16(52,8D,CB,D0,02,EB,45,9A,C3,C7,DC,30,84,ED,A9,7A), + wwH16(E2,E0,92,78,B2,23,0E,D0,5D,02,15,8D,A3,C8,75,47), + wwH16(E6,82,03,F7,B6,A2,BE,8F,38,E4,E0,2F,58,44,89,7E), + wwH16(52,71,EB,B7,01,50,D3,97,6B,22,E5,F9,3C,9F,F4,7F), + wwH16(A2,79,8B,E5,E5,86,BC,16,12,D4,75,A4,7C,AD,F4,03), + wwH16(40,95,69,CE,B4,BC,66,F5,33,D1,E0,39,BA,AD,BB,3B), + wwH16(87,FB,A5,AB,95,45,A5,17,B8,F4,69,28,68,09,DE,71), + wwH16(0C,19,01,35,EF,A1,83,16,5B,55,86,E0,06,D8,8A,3B), + wwH16(80,11,3D,C7,71,F2,B8,61,68,E7,78,DA,D3,AE,A3,B2), + wwH16(BE,D3,9C,F6,C1,2B,C7,37,E4,FB,41,42,6A,33,63,3A), + wwH16(EA,41,77,BF,58,7F,D6,1B,53,58,74,4A,45,39,A9,96), + wwH16(65,FE,35,BD,FE,CC,85,72,5B,AA,9B,83,1F,7A,05,19), + wwH16(36,D0,D8,1D,EC,DA,8A,5E,D0,4A,44,8E,C6,66,A5,A8), + wwH16(23,97,75,8F,51,F7,32,D8,57,02,D4,31,5C,C2,4B,CB), + wwH16(F8,F1,D8,B2,86,93,E0,CD,91,EA,A1,AA,78,54,60,B4), + wwH16(95,48,D3,91,CF,BE,52,1F,A0,D9,C7,F2,7B,8D,B6,07), + wwH16(4F,DD,D5,E7,CC,D3,34,52,19,6C,D8,98,92,02,92,5D), + wwH16(90,79,76,61,D5,DB,58,9F,56,32,12,29,3A,99,80,78), + wwH16(2F,1A,59,80,F5,33,A8,29,58,D6,3C,77,E6,50,5F,A2), + wwH16(72,82,12,4E,C3,7F,04,C3,75,1D,BA,06,B1,48,16,46), + wwH16(4B,94,7C,D8,37,56,35,53,3B,42,0B,D1,B7,B6,15,55), + wwH16(74,61,7F,B2,D7,61,48,0A,F6,AB,19,9E,2D,05,6D,22), + wwH16(5C,DD,C3,28,F3,82,AB,56,28,D8,67,03,FF,DF,72,4C), + wwH16(9B,FE,26,60,8A,64,AD,BC,83,0A,CE,1A,39,9E,32,53), + wwH16(25,8C,30,8E,75,7B,8C,6A,E3,2C,5A,5C,C5,D1,91,EE), + wwH16(B8,F3,D0,E2,2A,EE,87,5C,CF,32,DD,89,BA,37,55,EC), + wwH16(3E,14,71,03,AD,25,81,7E,EF,49,CE,1A,8C,63,93,05), + wwH16(7A,1D,8D,FE,4E,77,BA,CB,E8,33,F2,DF,13,C7,B5,CE), + wwH16(8A,FD,F7,11,F9,E7,49,28,AE,39,F2,BF,2F,A8,03,86), + wwH16(78,4E,7D,A0,1A,48,B6,85,06,72,44,10,07,24,D9,BC), + wwH16(98,DF,9F,7F,C7,20,9D,55,CF,38,7D,A4,DD,AF,D4,B3), + wwH16(F0,DF,39,5A,B3,68,FA,FF,05,66,C7,C0,66,8C,ED,C3), + wwH16(F6,20,36,40,3D,C5,51,44,55,F9,D9,68,8C,9E,E1,D0), + wwH16(30,49,74,4E,DF,24,AB,9C,B5,3B,26,EA,E3,61,54,5B), + wwH16(62,B1,B1,2C,51,26,29,C3,62,93,FF,F1,73,B3,7F,EB), + wwH16(EC,F9,4A,F5,60,CC,C2,BD,74,29,7C,12,08,0E,D3,DD), + wwH16(AA,73,D4,DF,D3,A6,53,07,C7,A1,22,4B,5D,A7,22,B3), + wwH16(C5,85,65,63,9A,6D,1E,97,02,97,CB,C4,F0,03,F9,DE), + wwH16(01,2B,02,E7,9F,A0,16,A8,F5,95,1F,C5,4F,17,80,EB), + wwH16(D6,F1,18,5B,2A,3E,63,35,4D,C4,64,AA,97,C1,05,D0), + wwH16(34,B7,00,85,5E,24,8D,11,9A,B0,89,2E,63,4E,5B,FC), + wwH16(7F,89,A1,A4,DE,20,CC,30,51,88,A5,FD,7E,47,DE,BA), + wwH16(96,24,D4,EA,6D,58,51,4E,F6,34,E9,41,FB,8D,FC,69), + wwH16(2C,8C,FE,D1,40,3E,40,B0,EB,D7,C8,F2,AA,5B,A9,CA), + wwH16(39,C9,B2,F1,95,93,4A,75,47,F1,89,C5,36,FD,C6,B5), + wwH16(CE,4F,99,9C,FD,B4,E1,76,CC,0D,23,DA,52,C9,6B,DA), + wwH16(7F,A3,44,66,26,5A,B6,AB,A2,48,84,10,81,FE,72,6F), + wwH16(97,35,C6,25,A6,C4,0C,E3,9A,CF,45,5F,CD,3A,F6,47), + wwH16(D3,45,9A,C1,5D,AA,39,C4,1F,18,EC,9C,9E,4F,7A,82), + wwH16(93,AB,B9,86,4C,41,FD,C1,5C,EA,DF,CC,9A,96,D1,8E), + wwH16(C0,0A,7D,1F,E6,E6,AC,CE,C4,45,1C,7D,02,A6,7F,76), + wwH16(A6,40,C9,9E,E6,49,A3,91,1A,17,45,A0,21,E7,F7,C1), + wwH16(53,53,8B,09,E5,E1,92,9B,43,E0,91,AD,0F,0B,7C,46), + wwH16(C7,45,AB,A4,47,88,F9,40,C6,F6,A3,53,65,EA,EF,AF), + wwH16(4B,BA,03,B7,05,2F,8E,EB,05,B2,0B,63,C2,E8,B3,63), + wwH16(D4,AA,80,0C,CB,CE,B7,64,61,EB,DB,73,79,B2,A5,6B), + wwH16(A4,C2,52,09,5F,15,14,D7,48,30,A5,E1,1E,83,1A,A2), + wwH16(2F,C4,37,4F,99,05,8A,7E,83,C8,91,34,C8,2A,B9,01), + wwH16(3F,E5,03,BA,C2,79,BF,8F,FE,90,1A,A2,E8,4F,D6,8F), + wwH16(EE,D5,69,79,25,D4,83,0C,51,6A,95,1F,E1,C8,AF,E9), + wwH16(17,8A,4B,84,1D,82,CE,B9,EE,FB,4B,A2,C5,BF,6D,86), + wwH16(A9,EF,78,DA,E0,B2,A3,C0,2F,C8,43,38,48,6E,84,7E), + wwH16(90,B7,A7,06,47,FC,29,BB,11,5D,95,B2,DC,82,61,A1), + wwH16(AF,16,46,55,35,D0,68,20,92,7A,00,1A,82,E2,92,F5), + wwH16(0A,BA,42,12,39,7C,65,63,3A,AA,D2,07,E2,0D,41,10), + wwH16(CC,0F,70,99,9A,E5,C1,A7,61,45,6E,FC,AA,E2,00,EB), + wwH16(3E,EE,99,C0,4F,63,22,83,76,58,BE,C0,CA,16,35,CA), + wwH16(EE,F6,07,61,8A,18,64,BF,54,24,16,12,B9,7F,E8,3D), + wwH16(2A,20,71,A4,1E,F9,FD,E3,1F,5F,F5,9F,3A,42,C7,3B), + wwH16(44,57,CD,FA,09,01,77,62,8F,19,FA,B0,F6,F8,5C,F4), + wwH16(E3,E4,E3,3B,CA,1B,E3,BC,C9,F5,94,B9,D8,54,12,1A), + wwH16(5A,9A,6B,41,31,26,65,89,2E,B6,71,42,B1,28,C3,92), + wwH16(9D,99,AC,EE,88,8F,0E,89,72,50,E7,61,6E,4D,72,A3), + wwH16(F2,5D,37,0F,56,98,14,1B,C5,99,72,7F,83,0D,9D,1D), + wwH16(72,CD,79,E5,64,65,F2,59,92,B0,24,A9,D8,14,27,48), + wwH16(B6,19,E3,6A,4B,50,3D,AF,C4,F2,F6,22,51,57,32,35), + wwH16(DA,06,03,12,70,10,17,CA,6B,1F,3B,C2,E4,8B,2D,8B), + wwH16(18,16,02,DA,4A,24,45,41,22,C7,7F,FA,1F,35,46,A4), + wwH16(46,0E,42,9D,ED,D8,B0,81,7D,0B,27,DF,4A,A3,91,48), + wwH16(C8,D7,C9,F1,2D,E3,9F,AD,22,9C,28,EB,31,E9,CE,A5), + wwH16(6C,FB,AA,0F,92,B7,DB,B5,59,27,92,D7,1E,5D,A8,D6), + wwH16(0B,E2,90,09,76,62,0B,2A,AA,D8,40,D2,C8,D8,05,D0), + wwH16(94,A5,18,F7,41,F2,A6,0D,00,1B,05,31,18,47,AD,ED), + wwH16(31,C5,28,80,EA,41,9D,05,65,2B,5B,9D,FA,C7,43,88), + wwH16(92,6F,9C,35,77,34,9F,1E,9D,CD,4E,25,8C,7E,1D,04), + wwH16(C4,93,2C,E8,A4,EF,56,49,5A,DE,4C,95,0E,96,8D,47), + wwH16(CD,3E,E8,79,11,27,48,51,BD,F3,D6,9B,0E,9F,23,F9), + wwH16(FC,D9,1F,4D,A8,AB,8F,87,C8,72,49,7A,AA,DD,C2,97), + wwH16(2F,0D,72,34,42,0A,D5,60,31,2C,B9,BB,7B,57,BD,58), + wwH16(11,07,14,1F,6E,68,41,87,2B,E6,AF,A0,55,70,50,65), + wwH16(8D,CE,61,E1,00,93,B5,1A,7F,82,8E,CD,EF,9C,55,76), + wwH16(F7,A8,DD,AB,7F,BA,E4,1D,F0,F9,07,01,35,85,01,14), + wwH16(2F,11,61,CB,61,C4,7E,EA,94,F0,44,92,C6,29,0B,79), + wwH16(BC,81,94,B7,59,94,8F,5D,4A,50,D7,0E,25,9D,66,0D), + wwH16(1B,03,09,34,7B,07,1C,02,5F,AE,D5,8E,73,FC,5A,6F), + wwH16(CC,76,5D,8A,61,01,72,56,58,04,7B,5A,41,CD,7F,35), + wwH16(60,31,E0,12,25,FD,4B,23,85,53,9C,CC,14,78,84,9B), + wwH16(7D,95,DE,EC,DF,EF,01,DD,06,D3,BE,79,19,8E,4E,39), + wwH16(5F,2A,D2,D2,8C,62,3A,C7,6C,D6,66,39,FC,41,A2,E8), + wwH16(A7,F3,97,D8,61,3F,F6,57,81,6B,69,28,EB,94,36,4C), + wwH16(1F,31,98,98,1A,BF,18,72,D1,0C,B0,32,AB,A7,61,63), + wwH16(85,3F,28,C4,EF,A0,F3,D2,42,18,83,53,B6,80,16,69), + wwH16(5B,DB,CF,92,BD,0D,A3,6B,C9,A0,8D,B1,C5,86,16,E9), + wwH16(69,36,7D,08,3E,6B,13,22,BC,57,2E,73,6B,DB,2C,58), + wwH16(F1,41,A1,A2,09,AD,CC,95,CF,98,CA,A7,B2,3B,1E,27), + wwH16(6C,F4,47,6B,56,A2,C5,48,D1,20,0B,4D,5B,E8,76,3D), + wwH16(73,4C,55,9B,CA,DA,6D,42,C2,B4,C8,BB,0F,CF,DC,82), + wwH16(E0,2E,A0,3E,A5,99,B2,A1,24,41,81,92,E4,7A,6C,66), + wwH16(89,65,14,EA,A6,7D,E3,39,E3,48,DA,16,5A,55,0A,DB), + wwH16(BE,02,8D,AE,87,44,3D,6D,14,F5,33,61,26,FE,B8,62), + wwH16(44,D3,5E,8C,97,5A,75,FF,50,9F,84,70,B0,F0,55,EC), + wwH16(27,AE,E8,2C,67,78,38,72,0E,F9,7A,9C,A3,5A,95,E4), + wwH16(B6,DE,D1,59,DC,66,92,D3,09,D7,34,17,29,74,74,E0), + wwH16(98,FC,DD,CE,4D,42,44,B6,46,2B,27,9D,CD,76,24,4E), + wwH16(E8,A5,ED,0D,69,78,41,20,35,12,6A,9C,9D,08,FA,BD), + wwH16(9E,5B,1D,86,6E,1F,08,88,D0,A7,68,7D,D7,DD,AC,04), + wwH16(CC,36,AD,10,23,2C,38,C7,E3,C3,A9,5E,A0,71,32,C6), + wwH16(F2,C7,1C,92,0B,F9,47,56,78,8D,66,BB,04,D3,05,39), + wwH16(83,F6,CF,29,EA,A8,74,D8,A6,68,58,80,AA,80,F0,07), + wwH16(8E,EC,FC,3E,2D,89,6A,66,C3,75,58,D3,90,44,AD,3B), + wwH16(A8,A4,B4,6E,13,89,BC,FF,CC,C4,B9,02,B9,1B,C9,C5), + wwH16(1E,E5,51,E9,DA,D1,E9,CC,2C,AF,4B,5D,1B,50,48,A4), + wwH16(B1,0A,D2,C6,9E,82,7B,88,72,BE,5F,94,9D,25,56,B8), + wwH16(01,9E,82,D0,0C,3C,A6,DA,78,82,07,E4,6D,46,98,2A), + wwH16(A5,7A,C8,F1,0F,D7,22,AA,F4,5F,22,43,91,5F,1E,3E), + wwH16(7E,D2,3A,02,38,F8,7C,56,72,7D,BC,85,93,B0,C2,21), + wwH16(CD,26,5C,E7,97,8F,5F,DC,F6,CC,47,02,07,50,2E,76), + wwH16(88,A3,C4,A9,29,85,8A,08,B8,90,1A,87,25,D1,53,E1), + wwH16(54,8F,E2,70,EE,35,D8,AD,84,94,34,90,9D,D5,D1,EB), + wwH16(4B,6D,53,0F,8F,87,03,B4,DE,0C,8D,92,DF,08,24,12), + wwH16(A6,C4,CC,92,DF,04,43,0D,62,15,4B,FF,3C,26,CC,EE), + wwH16(FA,53,D7,F1,76,DA,2C,06,5E,11,D2,39,88,BD,EA,62), + wwH16(D3,85,14,C9,12,FE,A5,A4,1D,75,29,BA,93,E0,CE,09), + wwH16(D9,AD,3C,DB,1E,F3,90,0A,93,09,DA,9C,E1,DC,1C,82), + wwH16(66,99,67,CC,19,85,FE,A4,66,49,0D,F9,0A,32,12,6E), + wwH16(7D,0A,E8,9A,0B,BC,56,97,84,77,24,52,8C,13,79,66), + wwH16(C7,FF,CF,F7,18,16,B4,BC,D3,FD,1E,83,50,FA,91,81), + wwH16(C6,E4,C5,DA,C6,5B,7D,F7,81,9D,A5,AD,E3,95,D9,43), + wwH16(3D,36,45,54,E5,15,9B,AD,BB,72,D4,BA,37,DC,26,F2), + wwH16(61,F9,BE,D2,05,81,CE,80,73,DB,7D,4D,51,1C,11,00), + wwH16(7C,43,EB,29,D7,48,77,91,DA,A7,B2,4C,B2,02,F3,B3), + wwH16(A5,70,C3,85,C0,1C,8F,14,3B,1D,9F,6E,8D,C3,FB,5B), + wwH16(D8,85,0F,62,8D,1C,F2,10,EA,41,27,81,E0,B4,56,76), + wwH16(8F,82,8C,E5,E2,C4,E5,1A,B4,73,27,FA,B8,9B,57,2D), + wwH16(CB,37,6E,69,65,85,F5,A8,38,BB,76,56,BE,6A,92,B6), + wwH16(95,94,BE,C9,A3,37,1F,FB,01,49,84,A2,A8,A7,2B,BC), + wwH16(40,ED,D6,1D,EA,CE,C2,D8,B5,12,26,8B,2A,ED,58,B3), + wwH16(73,D6,4F,A5,49,38,AC,6F,59,40,CB,30,20,BF,C4,7D), + wwH16(44,3A,E4,DB,2B,A8,B0,08,0F,81,A7,A7,5C,E3,32,C1), + wwH16(2B,04,0F,01,17,98,64,E4,7F,B6,57,61,7D,6C,07,A1), + wwH16(FD,15,38,E5,BB,26,5E,F8,7F,B1,CB,FF,EF,6B,07,08), + wwH16(79,88,4B,E3,FA,3D,C3,C1,2F,C7,9C,20,7E,DF,A2,ED), + wwH16(73,17,4B,34,51,4C,EA,E5,6E,E1,86,BA,32,D4,40,87), + wwH16(4C,62,47,52,46,92,08,BC,A9,CD,FD,51,C7,79,FC,43), + wwH16(B5,20,03,3C,D2,2B,29,63,C9,75,7A,63,FE,9B,B0,43), + wwH16(AA,BC,58,0F,35,F2,0A,80,39,EB,11,F0,01,43,59,B2), + wwH16(54,FF,83,AB,1F,E1,63,1A,5C,FF,44,8B,A6,48,D6,75), + wwH16(E5,BB,41,B5,9A,7B,39,FE,D9,90,25,C2,12,79,80,2E), + wwH16(51,B3,75,D6,F4,53,9F,B5,A7,41,54,D8,D9,B1,97,16), + wwH16(0F,62,AC,E0,96,3A,D3,BA,88,E8,16,44,25,ED,05,CA), + wwH16(4A,DC,D7,BB,64,74,7A,BD,32,84,63,87,CE,AF,B8,31), + wwH16(0D,DE,A3,E4,5D,6A,2E,5B,DE,CD,AD,5E,89,2F,06,A7), + wwH16(D7,84,B7,B0,0B,08,87,5D,FE,16,A1,9D,31,F1,0C,F7), + wwH16(5A,17,21,14,CB,4B,F5,F9,52,21,8F,17,8A,07,11,2A), + wwH16(81,CA,7C,42,0E,0D,05,D7,16,C9,F4,2F,DC,80,E9,3C), + wwH16(E4,45,1C,B2,30,F0,AF,63,66,5B,21,E1,83,42,EC,33), + wwH16(26,93,ED,8C,53,6D,AF,59,B5,FB,3E,D4,8B,86,66,BE), + wwH16(44,C7,89,4C,09,F5,1B,8A,72,59,E9,77,DA,C7,D5,AB), + wwH16(58,E2,2D,F2,EA,90,8A,CF,1C,F8,ED,DE,C1,82,F2,6E), + wwH16(0E,66,C0,D1,95,83,F8,2E,D4,22,83,FD,19,C7,83,22), + wwH16(07,F5,00,C5,57,2F,85,F1,0A,22,0D,2C,43,4A,72,69), + wwH16(B9,5C,51,C0,1F,44,69,61,7C,46,89,47,71,9B,C6,47), + wwH16(53,A0,E3,AD,36,DC,31,AC,B4,AC,E8,53,06,14,42,21), + wwH16(6D,CD,D2,29,1D,92,26,44,34,95,2F,2D,AC,F5,60,89), + wwH16(87,01,1D,4E,FC,5F,4C,7C,5C,9A,AD,7F,BF,D1,09,C7), + wwH16(1A,05,50,BA,CB,98,B5,06,20,4B,68,77,39,F5,DA,4D), + wwH16(52,2B,45,5B,A7,29,BB,2A,8F,65,E5,94,4A,6C,32,64), + wwH16(D2,A5,04,1A,8C,F0,72,30,6A,AD,75,CA,73,9F,BD,43), + wwH16(FF,33,FA,42,92,71,36,D7,A1,EE,E7,45,BD,8E,15,5F), + wwH16(76,8A,69,ED,3C,60,85,74,3D,7C,55,F5,08,BE,03,CD), + wwH16(E7,78,A9,48,15,87,63,29,C8,84,E5,A3,44,32,CE,06), + wwH16(8E,8C,0C,73,F0,62,A8,EE,19,B4,FC,3B,4D,B1,CF,74), + wwH16(92,95,4E,D9,E4,FF,68,7A,44,99,BD,2F,CC,76,A4,8D), + wwH16(FC,A2,F1,BE,56,85,32,5B,5C,86,14,2B,37,12,D4,F1), + wwH16(8B,9F,C5,C4,98,2B,72,34,D9,FD,C8,23,AC,C2,8D,7F), + wwH16(4C,87,31,9F,89,9E,DF,F5,62,12,22,5A,33,92,A7,65), + wwH16(80,A7,0F,3C,8B,03,55,EC,64,03,02,13,AC,57,94,69), + wwH16(CA,CE,83,DE,E3,FA,55,01,D4,DD,3C,9A,80,05,12,D1), + wwH16(04,4F,9A,73,5C,5A,33,2C,21,DD,2F,56,62,F9,DE,9E), + wwH16(46,98,27,A5,76,52,CF,98,1F,D2,57,EB,A5,38,49,5F), + wwH16(9E,96,7C,FF,57,60,33,62,61,FB,42,B4,02,0A,A0,DA), + wwH16(EC,A9,88,9F,44,64,AA,33,F5,00,44,B8,51,79,ED,98), + wwH16(B9,77,67,50,9E,8F,BA,68,9F,28,FB,88,AA,90,20,43), + wwH16(41,28,0A,40,85,45,BF,00,57,7E,AD,A2,8B,55,C9,FC), + wwH16(29,9E,EF,56,41,4F,D1,22,D0,3F,46,E7,36,D8,6A,76), + wwH16(EC,E9,E7,78,5D,B0,DB,22,85,B6,80,E3,DA,FB,35,BD), + wwH16(FA,D9,E2,C2,F0,33,32,1F,86,14,8E,2D,E0,D1,DC,D4), + wwH16(CB,4A,DB,F8,7E,BC,97,15,DC,8E,99,46,B4,35,D1,AA), + wwH16(44,02,1D,69,6C,94,C5,62,C4,3B,15,50,41,6F,70,D3), + wwH16(40,04,4E,FD,5B,36,1C,42,11,4D,61,E2,95,25,AB,BE), + wwH16(69,C4,93,E8,05,E7,0B,8D,A1,A9,20,C5,3B,DC,65,DB), + wwH16(30,EE,15,3E,DF,BF,B1,6E,2C,7A,58,9B,8E,A3,E6,7C), + wwH16(6F,37,96,FF,A0,78,79,E4,13,11,48,B8,45,29,7C,4D), + wwH16(0C,AE,2E,F5,B4,D6,39,5E,0C,2F,FB,52,C7,61,C8,61), + wwH16(CC,5C,84,FC,40,1E,00,99,DB,D7,57,6A,67,E8,A4,81), + wwH16(96,E9,00,24,80,9E,6A,24,03,61,65,DD,49,97,9A,46), + wwH16(13,AB,B6,AC,64,1C,A3,19,2A,0E,18,24,4A,69,B2,3B), + wwH16(30,61,63,88,D4,6D,B1,13,64,AA,AE,1F,8B,8B,02,11), + wwH16(52,25,E8,1C,BD,AF,6E,7A,F9,93,D5,DC,EF,38,28,F1), + wwH16(DF,21,2E,9A,FB,9C,9F,E0,56,3F,49,37,AA,1C,0C,2E), + wwH16(3A,87,AE,1C,0B,A1,98,2B,6D,76,B5,6B,9B,0A,87,55), + wwH16(A7,35,AE,87,61,4C,AE,B5,D2,68,2B,51,B6,87,63,17), + wwH16(42,AA,C5,59,BD,35,34,B2,FD,0B,BA,F1,BA,13,66,75), + wwH16(EC,D7,1A,80,51,6F,07,5B,7D,C9,C9,00,C6,6C,67,78), + wwH16(A7,7D,60,A6,FC,05,2F,2C,36,15,A7,28,81,A3,B6,B2), + wwH16(F4,BA,64,5C,8B,E1,8E,0C,94,D6,74,95,60,CC,D4,B6), + wwH16(48,95,6E,A7,5F,8B,6F,B7,35,8F,6B,5D,08,BA,F6,4D), + wwH16(01,4B,2C,98,C4,9C,B4,67,C9,9B,B7,56,53,0C,55,D7), + wwH16(36,0F,78,06,25,69,E0,3A,55,BF,C3,2E,9E,28,30,E0), + wwH16(F0,19,71,99,AE,D6,CD,DE,C7,C3,FB,07,22,B9,99,41), + wwH16(9B,4B,7A,5C,A6,AB,D5,D0,41,B1,41,63,8E,BE,37,B8), + wwH16(2A,68,F9,05,C7,8E,CF,B1,98,EF,E8,97,39,3F,77,FA), + wwH16(78,98,79,E1,63,77,97,20,0D,0C,4B,3D,FF,E1,49,8F), + wwH16(E3,DE,4F,61,5E,FE,22,DD,6D,49,E0,0D,81,49,FF,74), + wwH16(81,C7,98,7D,5A,8E,E4,30,D8,3D,F4,77,B5,22,FE,8D), + wwH16(9A,05,35,4B,20,9F,AD,A5,C8,A5,F5,84,1E,3C,E9,16), + wwH16(0D,11,5E,AB,89,C1,F2,57,AD,71,88,DB,FA,B8,45,9A), + wwH16(1B,BC,6D,EA,39,97,5F,C1,4F,87,8A,03,CA,5B,4E,3C), + wwH16(27,DA,60,0A,81,1B,0F,A7,7C,41,E3,24,FA,3D,65,EC), + wwH16(06,92,D2,21,C8,7C,86,0D,F9,A4,F9,1D,E5,63,7B,60), + wwH16(F4,D6,00,76,F6,E1,85,3C,31,0F,75,D9,DB,3E,CE,01), + wwH16(7A,E9,2C,B8,B5,CE,9A,8C,5B,19,36,9B,D9,A9,AF,0E), + wwH16(83,38,4C,1A,35,2A,A9,DC,D2,F5,25,0B,B4,D0,E5,7C), + wwH16(9F,DA,D0,3D,E1,DA,75,1B,F6,A0,A4,A3,C9,83,33,5F), + wwH16(88,CE,31,2D,86,46,4D,43,7E,7D,AA,20,CD,26,A0,85), + wwH16(F4,D9,3B,A0,7B,68,7C,37,C8,DE,6F,2F,77,6B,92,D1), + wwH16(19,42,FB,10,58,3E,17,85,02,86,7A,0E,D4,AC,4E,E4), + wwH16(54,C1,C1,78,FB,70,82,E1,D3,B7,18,D2,56,36,E5,BB), + wwH16(E5,B4,08,A9,9A,82,03,8F,58,05,04,A9,88,D3,7D,62), + wwH16(12,A5,A3,A2,C1,D6,D6,D7,45,2E,B5,26,91,DC,11,11), + wwH16(3E,D5,72,CB,B8,13,19,B6,BD,F2,0D,13,87,1F,A0,61), + wwH16(B6,EB,9D,E3,FA,DE,32,2C,DD,E5,C7,D3,EF,AC,1E,AA), + wwH16(3F,E5,D6,A7,9C,E3,5D,16,64,F1,34,9D,B5,59,21,82), + wwH16(45,D4,E0,CF,8C,65,E8,6B,30,56,E7,35,37,27,E2,38), + wwH16(24,C1,DA,DE,76,66,A5,1B,C6,22,F0,60,D3,1F,55,AE), + wwH16(90,B0,70,4A,46,48,F5,F1,99,78,D3,EB,12,98,75,79), + wwH16(19,6C,68,85,22,F1,40,AA,ED,1C,15,71,EA,61,9C,CF), + wwH16(3C,31,D4,9C,FE,44,32,92,C5,AE,93,7E,8A,29,2B,E6), + wwH16(03,DD,F3,7E,9D,F1,50,EA,04,41,D9,FC,23,82,36,84), + wwH16(89,F1,7C,86,1A,16,CA,1E,01,35,77,9D,B6,2D,6D,7B), + wwH16(BB,CB,B7,0C,34,25,98,86,A6,2B,1A,A4,42,DF,7A,32), + wwH16(21,F3,0A,23,3F,67,D9,BB,C6,24,15,35,11,2E,9F,55), + wwH16(D0,FF,A9,B9,45,C7,DF,DF,0F,86,A6,23,E4,FF,BE,4C), + wwH16(EF,34,36,F9,9D,35,9F,21,28,08,FE,19,38,C4,02,F8), + wwH16(67,BF,EC,3C,6B,A0,6E,F3,BE,1E,21,20,75,F1,43,BD), + wwH16(78,47,AA,54,8C,63,24,5C,5E,EC,63,11,F4,F3,00,4F), + wwH16(3C,1D,11,19,C3,99,75,CB,49,3B,39,20,A6,B4,7C,D3), + wwH16(59,7B,BE,BF,6E,C1,93,15,92,24,4B,98,58,92,29,73), + wwH16(09,1E,1B,04,B0,86,39,96,35,B4,4E,86,02,19,5A,E8), + wwH16(F6,EC,17,59,95,66,83,F4,4C,5B,87,4C,5B,CB,C7,80), + wwH16(4C,E8,69,65,EC,4D,FE,B3,CA,18,A1,F2,19,2A,EC,6C), + wwH16(3A,56,BE,E1,51,8C,D5,00,DC,53,4F,AE,1C,D1,0D,6E), + wwH16(B7,64,FC,5A,CF,E2,52,B8,4F,47,A9,0D,C1,D0,6F,26), + wwH16(47,2B,63,38,0D,33,7F,92,B1,85,50,69,C3,58,03,A0), + wwH16(44,86,9B,16,36,CC,B3,64,5F,E0,5B,31,8F,69,AD,8D), + wwH16(78,C9,68,1A,73,92,D1,73,3E,33,0B,80,B4,A7,AA,DC), + wwH16(A2,32,B6,EF,A1,49,1B,FB,A2,65,AE,47,9A,0F,F6,1F), + wwH16(3C,FC,A8,76,2F,DD,EE,59,BC,32,D0,A4,EE,3A,CD,58), + wwH16(B8,3E,FD,48,B3,EA,B8,80,A3,70,F6,94,19,EE,BC,42), + wwH16(93,58,57,C2,A8,BD,EB,79,21,3B,DD,C2,CB,E5,C7,8C), + wwH16(E5,88,B9,55,F9,F0,77,A6,47,14,15,9A,08,7F,86,8C), + wwH16(B7,27,94,AE,70,36,B7,88,95,CE,EE,AB,E0,75,40,FE), + wwH16(E4,22,8F,EB,16,C9,86,98,11,80,B2,A6,C6,AE,EC,6D), + wwH16(24,D8,DD,32,C0,66,1E,8E,8C,DE,F3,E7,E2,CD,19,6A), + wwH16(BB,CA,D9,57,04,4D,75,1D,48,CC,4C,26,92,97,79,05), + wwH16(FC,43,4A,02,2B,A3,75,2E,3E,61,78,4C,0F,01,2C,D7), + wwH16(A8,38,4C,55,48,0C,6B,C0,D8,77,0B,B8,0E,93,E8,21), + wwH16(FE,4B,DA,AC,F9,B0,B5,1A,02,41,3D,91,E0,E2,AF,82), + wwH16(0E,F6,F0,72,6C,1F,87,19,2C,C2,DC,0F,BE,68,63,D8), + wwH16(24,FC,BB,49,35,4D,FA,E8,A3,EC,42,BA,B6,61,00,7C), + wwH16(96,5A,29,30,65,AF,A4,19,B7,5B,58,CB,1D,13,A2,DD), + wwH16(32,60,95,35,51,80,54,A1,06,34,3E,53,86,45,96,F4), + wwH16(E0,E5,75,7E,12,7E,D0,AF,ED,24,4A,97,F6,91,4F,DF), + wwH16(51,A8,06,99,39,6B,BB,6A,5B,EE,BD,67,2A,71,BF,B1), + wwH16(D4,D6,13,09,82,02,E8,0B,EB,F0,F0,0A,C7,97,EE,B9), + wwH16(CD,3D,11,1C,57,24,39,54,62,7F,09,70,40,2D,7B,E6), + wwH16(C7,77,FF,18,28,F0,73,4A,84,DA,F0,0F,32,4F,A3,04), + wwH16(56,4C,2C,F4,58,A9,2A,D3,40,D6,E6,02,B2,F3,5A,C3), + wwH16(84,53,09,E5,63,D9,DB,CC,49,9A,41,32,79,CF,DD,B9), + wwH16(2B,C9,CB,6E,02,3D,B4,FB,56,15,B0,E6,1F,04,AC,69), + wwH16(E9,91,95,2A,70,77,42,B9,15,CE,8A,C3,A3,39,E7,6A), + wwH16(EF,2E,49,4B,3F,49,C1,89,4F,56,29,25,F2,06,8C,CB), + wwH16(F8,27,5E,53,DE,A8,F6,82,5B,3C,51,02,ED,45,26,E9), + wwH16(14,00,E0,AD,60,3B,0E,39,F1,8C,5B,20,E0,93,8A,FE), + wwH16(F8,E6,C5,10,92,2F,C9,83,4A,8D,38,63,44,D2,50,6C), + wwH16(A7,62,BD,98,7E,93,72,C8,85,7E,9B,32,A2,11,50,A0), + wwH16(46,F4,4E,8B,5C,9A,E2,69,9E,DE,8A,AE,88,F8,42,3F), + wwH16(98,7B,93,D7,D2,BA,1F,10,45,10,3B,28,1A,AA,A1,EC), + wwH16(4B,52,30,EA,88,C0,59,B9,89,2A,EA,53,3B,5B,AC,C7), + wwH16(52,68,2B,2D,E5,14,00,70,E8,57,1B,20,85,9F,94,E9), + wwH16(F8,2F,41,AC,52,E9,E4,6B,46,C1,39,6C,62,03,20,2E), + wwH16(E0,94,0B,6A,93,6E,44,AD,41,F7,99,DA,C4,9D,3F,55), + wwH16(D3,C8,87,DB,85,15,C0,AC,49,E8,D9,09,EA,12,C0,16), + wwH16(72,77,E8,2F,08,B7,68,F6,52,5A,83,98,22,E2,3F,BE), + wwH16(69,A8,47,39,4F,36,87,EC,F3,4C,AF,49,5F,5B,C6,90), + wwH16(E7,8B,8F,27,0F,E2,A9,68,8C,C5,64,D5,FD,9D,9C,07), + wwH16(83,CE,82,47,90,17,43,2B,05,BA,81,84,27,72,71,36), + wwH16(04,D3,75,91,AF,E4,F5,42,18,9A,E1,5B,83,89,F6,0F), + wwH16(65,C6,2D,42,46,EE,90,F3,91,11,93,82,6C,9E,55,20), + wwH16(DB,3B,58,0D,A4,DC,A6,7E,B5,C3,F0,FE,7A,9D,D4,D4), + wwH16(9A,57,01,1C,45,36,7B,7E,82,54,1C,C8,D3,11,AB,BD), + wwH16(B4,7B,71,19,A9,A1,16,49,4A,85,BC,14,5F,2D,20,B7), + wwH16(EA,A3,A6,95,50,D5,D3,46,93,F1,FA,6D,4D,48,51,B1), + wwH16(57,6D,46,C7,8B,2A,52,A9,79,50,A0,D8,45,53,CB,2B), + wwH16(CC,AB,A3,DB,41,9D,24,16,F7,E7,84,49,6B,0B,D3,CB), + wwH16(32,6E,E3,2D,0B,11,0A,C7,DE,24,BC,D5,F5,20,1E,A8), + wwH16(D6,30,89,59,0D,F7,9B,C0,74,D9,5A,33,B2,D9,BD,12), + wwH16(65,6B,FA,F4,03,93,67,CB,2E,6C,11,66,1A,C6,D5,C0), + wwH16(84,A7,F7,89,40,BD,FA,DA,CE,C1,08,4F,75,FA,8A,9D), + wwH16(EC,8E,90,38,62,DC,2B,9A,62,A3,5B,F9,53,3B,96,5F), + wwH16(F3,3D,1C,63,71,FC,07,83,2C,82,2F,E4,76,98,4C,EA), + wwH16(18,E7,65,CD,A8,33,55,CB,A2,9D,5C,11,AA,1C,79,BE), + wwH16(06,D9,8A,2D,3D,E5,98,86,0B,BC,EA,92,33,8D,1C,8D), + wwH16(17,3B,5B,20,03,CC,26,39,F0,94,84,DD,32,EC,2A,1E), + wwH16(C1,FB,89,C1,4B,F8,F1,80,DF,01,95,05,36,71,3A,91), + wwH16(C9,DB,AA,6F,9C,72,12,30,D0,41,40,BF,14,1D,68,9A), + wwH16(12,1E,61,3C,A9,B3,44,4A,91,02,45,73,A8,F2,EF,44), + wwH16(58,AA,08,D2,10,1A,CB,C8,E1,73,59,F3,9A,79,FA,63), + wwH16(0B,0C,88,A8,5B,C6,A1,7E,05,88,61,E1,69,F2,FB,36), + wwH16(4A,DA,1D,C1,54,FF,68,37,73,CC,30,B1,B8,0D,0A,C5), + wwH16(AC,A5,F7,8F,AA,77,D9,8F,17,2C,BC,54,E3,42,24,F1), + wwH16(97,0C,96,9D,9F,44,0F,69,A1,29,D3,41,21,6A,7E,75), + wwH16(71,AE,06,DB,C0,A0,76,07,69,4A,A0,FE,A7,36,6C,74), + wwH16(0C,E9,B3,A6,FF,D6,E7,C7,73,04,3B,58,AB,46,EA,DE), + wwH16(88,45,91,04,1B,9D,75,76,DC,4B,43,DA,92,47,F9,A4), + wwH16(48,F5,DD,19,BC,61,19,A2,BC,E9,08,22,CD,62,F3,EF), + wwH16(5F,30,A1,03,A0,9F,C3,F8,0A,62,DC,88,A7,49,71,55), + wwH16(AD,8B,14,2F,4F,81,6F,C1,2C,F0,6B,0E,8F,9A,EF,8D), + wwH16(31,F4,AC,F9,6F,52,7A,91,B7,F9,52,AF,13,3E,D6,35), + wwH16(D1,8F,E9,E0,80,3C,5B,8E,1B,DD,A1,AE,41,4D,89,92), + wwH16(7B,FD,3C,FD,84,85,3A,D6,93,7C,34,9C,9D,4C,CA,44), + wwH16(26,3D,3D,3E,87,BF,E2,05,99,55,2E,A9,AF,5D,D6,6E), + wwH16(D7,7E,91,EC,2D,89,AD,1F,BA,52,B7,75,6B,30,39,A3), + wwH16(58,66,3F,B3,26,63,59,4C,B6,1C,36,73,B7,3D,A3,4B), + wwH16(4B,7C,77,F3,EA,1A,9F,30,DD,3C,48,F7,E1,E7,9F,D6), + wwH16(E4,6B,B9,22,63,78,16,BF,F1,C0,DF,72,45,1C,BD,B3), + wwH16(63,FC,D9,35,4B,B0,E1,80,19,33,B4,20,B5,86,72,D0), + wwH16(AA,93,EA,9A,13,E3,2E,28,FA,EA,40,5B,2D,68,EE,48), + wwH16(17,3C,80,8E,83,99,5F,2D,96,52,DF,40,14,BB,1A,51), + wwH16(C4,A0,33,DA,76,B2,E3,F5,11,02,56,BD,5C,8C,F8,7E), + wwH16(A3,23,22,40,DC,F4,9E,20,D4,FF,FE,65,55,E8,1E,16), + wwH16(75,12,7B,18,45,08,50,72,A8,BF,86,44,44,2F,49,9F), + wwH16(10,F1,9F,6D,67,E2,D6,AF,19,25,B1,B9,9A,96,60,28), + wwH16(B5,FE,8B,14,12,08,39,D3,9B,D4,CF,F3,0B,8C,A2,B0), + wwH16(41,70,A0,06,62,41,2C,DB,EB,57,2A,6D,3A,02,DB,A2), + wwH16(5A,48,F7,9D,90,D7,3B,CC,7E,FF,A2,04,31,CA,A9,CE), + wwH16(AE,F2,B8,B0,F3,C8,75,C4,38,9A,BF,5F,70,F9,64,28), + wwH16(2D,99,B5,11,D0,B1,DB,B7,76,39,46,6D,0C,00,74,A3), + wwH16(70,B4,BA,24,33,D5,B6,80,48,29,09,AA,78,6E,2D,3E), + wwH16(5A,16,07,6A,4A,EB,4D,6F,26,53,E2,38,8A,D9,EE,E7), + wwH16(3A,DA,CF,51,73,A1,23,B1,A3,18,11,61,8A,FC,65,A6), + wwH16(0E,BE,76,09,7B,FA,FC,DE,E8,4E,50,26,0E,75,A2,77), + wwH16(FF,BA,9C,1C,18,23,FE,55,06,89,D7,40,6B,24,C6,DB), + wwH16(9E,94,A4,CE,84,F7,EC,FF,D3,CA,73,56,3B,6C,E4,99), + wwH16(7B,6A,BD,3E,49,A1,BC,C4,FF,0F,D8,68,09,73,D5,2D), + wwH16(A2,E0,DF,C3,34,00,11,00,56,EC,BC,E3,3C,8F,79,32), + wwH16(60,01,9B,E1,65,8F,48,04,BC,76,F9,C0,84,B0,64,F7), + wwH16(47,CB,80,24,8D,8B,83,A2,4D,8A,BB,9F,77,C1,02,A2), + wwH16(A8,45,1C,0C,15,29,B9,68,63,84,F6,F0,24,4C,87,8F), + wwH16(59,26,17,A2,1B,AD,D7,85,AD,02,87,65,CC,AE,6B,C6), + wwH16(BA,FE,28,CF,C2,09,FC,E0,D1,58,FC,7B,07,00,24,F3), + wwH16(F4,C5,00,F1,6B,ED,D8,0B,0F,8F,70,3E,7E,EC,D2,F2), + wwH16(36,96,CE,F2,1B,FD,C3,A2,7E,64,99,A1,D9,B4,A3,69), + wwH16(0C,D9,09,42,1C,71,78,40,B9,50,94,63,41,BE,46,74), + wwH16(AE,88,E6,13,53,31,45,30,D1,FF,50,B1,22,F4,53,6A), + wwH16(7C,47,D5,07,B7,2F,BB,E3,69,43,28,C7,CC,EB,66,76), + wwH16(9D,37,A5,57,BA,C3,A5,77,6C,C9,AB,C4,7A,61,5E,E9), + wwH16(17,62,65,D3,6C,F7,AE,B3,06,F1,05,C4,02,16,84,B3), + wwH16(BC,5F,D9,51,21,64,23,B2,4C,DB,09,B4,15,B8,8A,75), + wwH16(B3,78,7F,A5,6C,7D,2C,B6,C5,AB,94,24,9D,F0,1A,F2), + wwH16(B0,D2,82,0D,40,87,CE,BC,A6,EC,57,B0,2C,77,7F,B9), + wwH16(E6,40,25,55,8D,6D,7C,EC,66,A8,DA,F6,B1,E5,36,7A), + wwH16(86,E6,C4,36,F1,86,E5,6B,DA,5C,6E,8B,89,4C,BF,51), + wwH16(75,23,30,19,FD,CD,C1,75,9C,38,32,1E,4F,D7,85,91), + wwH16(B1,E2,34,C4,98,37,56,FE,FD,F0,74,08,46,E6,3B,D6), + wwH16(99,B2,49,00,90,A0,56,54,2F,9F,09,8C,C6,35,D2,4A), + wwH16(93,64,24,CD,E2,3C,E8,FF,68,2E,B2,7B,D2,08,5A,22), + wwH16(72,DD,FF,7D,83,A4,17,D9,FD,E6,94,0C,61,8A,71,1C), + wwH16(18,78,08,26,BC,07,BC,85,8D,79,EB,2F,8E,0B,A6,CE), + wwH16(AD,10,B8,4A,D8,6E,DD,D4,C1,6D,A9,77,95,B0,AC,52), + wwH16(D0,1E,8C,10,99,46,34,C2,2E,B8,DE,02,49,83,0A,2D), + wwH16(63,08,04,42,65,D7,A4,49,1A,62,C5,E2,14,34,45,EB), + wwH16(38,D2,4C,12,8A,B6,0A,0B,D2,15,0A,A2,71,72,36,AB), + wwH16(78,57,BA,1F,9B,18,B9,C5,06,EB,2F,9D,8B,61,9F,44), + wwH16(5A,73,73,BC,F3,07,86,2C,71,6F,36,E8,76,05,CE,E4), + wwH16(71,DC,5E,4F,D9,EF,82,F0,00,8E,A7,59,B2,F6,DB,A2), + wwH16(EC,85,DE,1A,9C,26,22,70,F7,07,44,8E,37,F8,6C,17), + wwH16(23,D2,B9,4D,4D,36,9C,CB,5A,84,C2,E4,80,3C,FE,B5), + wwH16(F5,2D,2A,0D,BB,1D,F2,F2,26,70,14,C4,7F,42,7C,41), + wwH16(1F,2E,BF,27,8F,7D,FB,01,16,A9,64,88,9D,A5,A3,2F), + wwH16(87,CA,56,D5,7D,12,E8,F8,98,21,7A,FF,B3,20,4E,2F), + wwH16(16,DC,E7,9F,9E,E3,D7,80,FB,CB,31,F8,FA,A3,36,78), + wwH16(0F,3E,15,56,DB,72,64,8C,15,E8,6C,31,8B,1B,18,0F), + wwH16(64,47,F3,33,F2,B2,67,3F,33,53,69,95,C1,C6,1C,57), + wwH16(2D,6C,A4,28,67,3A,48,30,72,EF,C9,33,EB,E6,55,13), + wwH16(D3,B8,C7,A1,D7,C1,46,6E,52,AE,1E,AF,67,E7,B6,0B), + wwH16(2C,B9,C5,37,DE,0A,C0,6E,93,08,51,73,9A,74,A2,5C), + wwH16(A6,45,09,97,96,C7,AF,7B,89,F7,BB,91,A8,1D,4E,0B), + wwH16(E5,C9,5F,B2,7B,51,CF,33,AC,1D,8F,83,A9,6D,B4,DA), + wwH16(CF,90,3D,C6,C0,93,B5,23,98,1E,4E,4F,E5,05,D1,32), + wwH16(56,0F,0A,37,8C,A2,90,87,C9,D9,8E,24,DC,BA,41,AE), + wwH16(61,F4,7D,36,99,DB,01,7C,40,20,5E,47,7F,19,C2,57), + wwH16(E3,4C,7E,18,F4,9B,F3,8B,B6,9E,22,08,68,61,75,02), + wwH16(00,82,D4,C4,A5,8C,28,0F,C9,99,A8,3F,53,F6,89,1E), + wwH16(75,3E,A2,CA,C4,95,3A,BE,D4,13,52,AA,63,93,64,2D), + wwH16(36,36,5F,FF,26,ED,EC,F6,63,C0,9D,D2,F6,60,C5,C8), + wwH16(B3,D9,40,11,06,B4,BF,8C,4F,1A,F3,B6,0B,3E,A9,05), + wwH16(E0,F6,21,62,14,4C,8B,79,CA,6A,CF,9E,88,A1,C3,CD), + wwH16(4E,B7,0A,66,A2,AD,FF,F7,67,2F,49,AE,DC,76,E3,6A), + wwH16(E7,F5,3A,9A,CD,04,68,F8,C0,C6,6F,BA,E1,2B,37,C4), + wwH16(B0,33,5C,52,C8,43,EF,A8,34,C6,AF,99,94,A4,D9,AD), + wwH16(1D,5D,BF,15,8E,DA,B6,16,C5,89,42,EE,09,49,F8,88), + wwH16(ED,06,63,3A,A9,34,A2,81,5D,25,5D,15,72,83,73,9B), + wwH16(31,E9,00,A0,FA,BE,FB,FD,7D,15,7F,BD,07,C6,66,51), + wwH16(8F,24,B3,92,08,1A,7C,0C,26,65,F4,AE,68,2F,2A,10), + wwH16(F9,EF,70,78,A6,B3,6B,F9,D7,0C,ED,23,7B,4C,98,DF), + wwH16(97,FD,E4,A5,4B,68,5C,72,AC,C9,03,01,32,93,B5,F6), + wwH16(8F,F7,9B,25,66,96,B6,AA,4C,24,DC,60,83,F5,55,07), + wwH16(33,9E,65,50,D2,87,5B,67,B4,C2,41,08,D1,46,41,CC), + wwH16(E2,E5,90,6A,36,FE,60,AA,D7,E0,D7,B5,9B,41,4B,EE), + wwH16(CF,DC,F8,48,DE,46,6F,2C,B3,01,12,B5,AD,20,BE,A8), + wwH16(76,B7,7E,DA,A3,B0,8E,C6,4B,A1,DF,55,4E,47,B2,55), + wwH16(51,5B,78,34,37,0C,1F,CD,18,AF,74,71,C9,09,04,7E), + wwH16(84,E7,40,A6,A8,C5,C3,C9,D4,57,D8,B2,68,DC,9A,A2), + wwH16(2E,E2,83,6A,E2,B2,E4,18,5C,E5,48,74,BA,01,12,85), + wwH16(63,42,36,22,E0,2E,24,0B,C9,C7,6E,FB,D4,F5,46,89), + wwH16(2E,18,CD,F0,9F,36,38,D7,A0,65,DC,32,0F,AD,CC,AB), + wwH16(36,92,5A,BF,09,5F,23,BC,31,5C,3F,14,CF,6E,B4,77), + wwH16(56,D8,4F,CD,33,CD,F9,24,2C,C3,84,1C,2F,CE,CE,32), + wwH16(7C,7F,12,BB,FE,90,9A,F7,22,F7,ED,4D,A3,AF,76,76), + wwH16(82,66,C0,B4,BA,FE,8B,64,9F,B7,D6,04,EF,2D,EC,D8), + wwH16(2B,8E,8B,B0,78,5E,D1,B9,E7,A7,25,D2,DB,D8,3C,68), + wwH16(AB,C8,45,57,18,19,88,E8,AB,C3,4A,39,C5,C4,76,5F), + wwH16(57,46,0A,D6,44,C4,A1,E3,99,85,C2,5D,0B,C1,92,6B), + wwH16(7F,DC,7F,25,EB,DE,04,48,D0,2D,83,0D,AA,EC,66,56), + wwH16(11,A6,C3,36,66,C1,0E,3B,3C,5A,0C,10,B9,2E,69,91), + wwH16(D6,63,C6,B4,10,D8,AE,F4,ED,88,B0,5A,B0,99,09,4A), + wwH16(F9,22,41,96,D7,14,E2,21,DE,7A,46,90,49,9A,19,DE), + wwH16(85,31,70,91,8C,26,B1,34,A1,27,D5,EC,28,D6,F9,33), + wwH16(EB,2C,84,8A,4E,88,E7,FE,60,09,C1,2D,92,A1,8C,4F), + wwH16(62,CB,1D,09,B0,ED,62,CF,4D,D8,8F,66,72,91,E1,6C), + wwH16(0B,F6,BA,3D,43,54,31,F8,4C,6F,F7,FD,53,B2,B6,E0), + wwH16(0A,15,46,2F,F4,35,1D,9E,29,67,82,C9,90,03,E5,42), + wwH16(62,C4,4B,BA,BE,CF,FC,CC,0A,35,50,47,8A,17,5C,CF), + wwH16(EA,A6,2E,DD,7F,5B,65,4D,3C,AA,49,8F,F8,A2,9A,C3), + wwH16(5B,32,E3,11,80,8E,BB,31,FF,E8,37,89,5D,0C,CD,93), + wwH16(61,53,74,0B,96,84,FB,40,4A,48,7E,1B,0C,E6,6A,88), + wwH16(7D,79,9B,35,99,B5,75,AA,EC,B6,FC,FD,9A,8A,C6,C0), + wwH16(97,83,39,2B,D8,A2,5B,9D,87,7C,63,78,E9,40,CC,E3), + wwH16(AA,47,E0,0A,67,15,22,27,2A,CA,4D,8A,0A,EC,80,FC), + wwH16(45,86,D6,CE,BA,15,3A,71,56,0C,DE,4E,D8,1A,B6,1D), + wwH16(7B,7E,F3,D0,72,6C,C6,5B,64,04,05,E7,5D,D5,F3,1F), + wwH16(13,E5,FE,F1,10,17,FE,8F,06,A5,75,80,4E,0C,07,03), + wwH16(0C,34,09,3E,F2,90,4A,41,41,E1,C0,F8,8B,37,C3,59), + wwH16(0F,63,C4,9C,24,97,FB,21,F8,E5,96,80,C9,AB,06,56), + wwH16(83,E6,5F,34,03,01,53,7B,ED,7B,45,78,5A,3E,7C,55), + wwH16(20,AE,6B,4E,8D,3C,E8,25,F9,5F,DB,7A,4C,FE,AB,BF), + wwH16(8A,FA,E4,C4,97,4E,25,F8,FE,48,BA,DF,DE,55,91,1E), + wwH16(1F,5D,E3,E5,A4,01,D9,EB,E6,FE,25,BC,D8,D2,93,5D), + wwH16(6A,36,E0,60,8E,E2,DC,2F,27,E1,3A,09,27,53,6E,BD), + wwH16(BF,A8,FA,48,DF,1A,8E,1C,74,37,19,77,B1,CB,EF,83), + wwH16(09,AE,9B,0F,ED,9C,21,D3,26,E1,39,F7,35,43,87,3E), + wwH16(B5,21,5C,07,69,FD,72,2D,B3,DB,DB,02,E1,CD,22,D5), + wwH16(EF,75,C5,7C,52,3F,1E,06,1E,82,6F,CC,A2,69,C6,4D), + wwH16(CC,BD,6D,75,52,29,09,1C,64,EF,6F,0A,1D,B0,AC,0C), + wwH16(69,33,8F,C3,03,B7,38,94,E6,D8,CD,F7,C3,67,DF,5D), + wwH16(2E,75,9A,B4,0D,63,58,61,1B,B8,12,3B,B2,0F,4D,46), + wwH16(04,A2,27,00,FB,E7,D6,D9,86,D9,37,21,25,0E,90,C4), + wwH16(B2,82,F4,C4,B8,D9,BB,C5,BC,5E,CD,0A,AE,50,68,06), + wwH16(0A,35,22,8A,77,87,52,34,7C,9D,93,7B,08,5F,23,30), + wwH16(AF,54,57,B7,0B,60,01,8A,E1,E9,CA,C8,0B,B7,1F,E4), + wwH16(AB,C3,55,44,A9,70,A1,F0,8C,84,66,B3,17,6E,DA,8F), + wwH16(27,26,7F,31,E6,5C,BF,99,F8,45,30,03,35,3D,94,85), + wwH16(3F,84,D5,AD,DC,BC,4B,B0,82,7A,5F,E0,55,CE,A6,9C), + wwH16(BB,9F,53,E7,69,29,CD,6B,34,0F,40,E5,9D,51,71,3A), + wwH16(F2,9D,FF,F0,AD,CB,A2,55,08,81,DA,2F,DC,FE,D0,B5), + wwH16(EB,DB,B8,BC,CD,88,19,99,23,E2,02,19,4A,FC,81,1B), + wwH16(19,BC,AB,8C,AE,25,F4,76,46,12,FE,92,A2,B8,FF,A6), + wwH16(BA,C0,E7,97,F3,42,F3,C1,CD,49,4B,04,99,C1,B9,CF), + wwH16(E0,E7,F6,08,DD,F9,A4,76,8A,0B,DD,FE,91,06,82,87), + wwH16(07,FF,87,4B,E9,13,3C,00,97,7F,4B,A7,61,41,F3,46), + wwH16(2A,21,F2,13,9E,6A,2D,A2,E2,A8,86,41,85,96,09,8A), + wwH16(F7,80,C6,69,D0,82,2F,E0,86,B3,E8,F1,D2,3D,5E,39), + wwH16(4C,B2,55,D6,00,67,CE,B7,F7,83,28,10,54,78,19,F4), + wwH16(0B,BC,83,12,8C,4F,6F,63,37,1F,D6,90,75,C9,18,6D), + wwH16(60,6D,01,BD,ED,E7,80,57,AB,3C,C8,4A,19,08,EF,18), + wwH16(13,89,BC,31,92,0E,FD,5D,4A,DC,DB,99,58,88,3C,EA), + wwH16(96,34,09,7D,FE,6D,FB,CE,CC,5A,97,0D,4B,18,24,FB), + wwH16(32,52,CC,EF,07,51,DD,3A,DA,DF,D0,56,42,BC,E2,63), + wwH16(AF,59,BE,A3,E7,2F,32,E9,7A,16,9F,82,FF,D1,4B,05), + wwH16(40,45,1C,48,09,D6,84,75,FA,26,C4,A9,BE,87,16,11), + wwH16(0D,AB,14,E7,21,71,51,69,6F,6B,A3,7B,BD,5C,FB,7D), + wwH16(EA,B8,F4,BC,B6,BF,92,9F,A3,28,07,88,08,F3,39,47), + wwH16(59,6F,5C,7C,E9,B0,1B,1D,43,EE,ED,2E,A2,02,AD,A0), + wwH16(76,11,E8,6A,81,08,DE,1E,A2,28,1A,55,0D,41,1E,41), + wwH16(4D,A9,3E,EA,C8,3D,97,B3,EC,F9,A9,44,C6,9A,8F,8D), + wwH16(23,B9,5A,88,C9,5A,38,C9,AC,44,9B,A4,E0,EF,43,14), + wwH16(4F,F6,91,19,A0,D0,BF,95,A1,50,8B,6F,65,5D,FB,2B), + wwH16(38,C6,DF,B1,B1,C2,E8,D7,62,07,9A,2C,A6,9E,40,D6), + wwH16(5F,97,DC,E6,F3,0F,8A,D8,21,9D,E0,75,17,FA,7E,FB), + wwH16(27,21,CC,B7,60,8F,6A,13,1F,D0,1A,78,9B,1A,C6,E1), + wwH16(CC,4B,8B,D5,E3,7E,0D,5D,37,0B,82,9E,7D,4B,57,24), + wwH16(3B,F0,C0,26,3B,8C,FC,C3,CD,53,DA,38,74,18,4C,A5), + wwH16(14,7C,3D,F7,14,61,7D,D3,3E,58,2E,C6,1F,A1,ED,AB), + wwH16(F5,6C,E8,60,1A,D1,DF,23,FD,08,38,EB,A4,63,A6,3A), + wwH16(F1,2F,EE,FE,E4,ED,5D,A1,1F,34,6B,5B,CB,F4,1F,96), + wwH16(77,98,C8,0C,24,D9,E1,52,1B,BC,75,CE,26,E4,3A,1E), + wwH16(11,75,DA,7E,95,B0,C2,DD,7D,7E,FB,F0,BD,07,E1,2B), + wwH16(D2,4E,0E,58,76,6B,13,E2,9A,82,25,66,B3,0F,13,CA), + wwH16(F1,5B,20,E0,EB,B4,10,EA,4B,82,4B,91,E3,6E,46,A4), + wwH16(9B,E9,E6,0A,9A,70,B5,AA,E9,6A,97,C9,80,9A,ED,1D), + wwH16(C8,55,23,43,D4,49,68,8C,B5,BC,29,D3,85,F9,8E,26), + wwH16(2E,F5,DD,C0,95,A2,5B,8A,22,F6,DE,46,00,45,B8,8A), + wwH16(21,F2,D7,55,31,CC,62,45,AB,2E,75,65,4D,56,BF,62), + wwH16(23,A8,49,5C,03,EC,B9,D5,F2,CB,2C,40,78,07,1A,FB), + wwH16(80,08,1F,38,4F,1B,B2,A6,29,BA,A4,30,6E,95,76,55), + wwH16(0C,9D,26,E5,E7,02,FD,38,4A,24,9B,BF,12,41,E7,7F), + wwH16(B8,E7,5E,EA,EF,C6,8B,14,53,78,96,70,BF,CF,4F,E5), + wwH16(8D,EA,43,F2,A6,53,6D,95,FC,FF,FF,CE,B1,24,A3,7D), + wwH16(E7,49,CF,B6,47,95,18,2D,4C,7B,28,E1,85,6B,B9,79), + wwH16(A9,F9,A6,A9,68,DE,22,46,62,B4,C5,39,AE,37,BF,DB), + wwH16(86,A3,76,CA,4C,65,14,97,B1,D2,A2,6C,3D,03,9E,84), + wwH16(EF,7F,D6,B0,B9,A5,89,39,C7,EB,81,C2,02,D0,D6,8C), + wwH16(4E,12,35,3A,D9,D4,50,32,DE,B8,DC,FD,53,FB,F8,8F), + wwH16(84,A0,EE,56,2B,E6,DF,9E,BF,6F,7E,C2,34,65,05,D2), + wwH16(17,5B,F2,2F,7D,ED,B6,05,49,0B,59,DD,98,84,3D,31), + wwH16(36,BA,73,78,DC,32,5E,61,CE,C7,9C,67,39,49,36,63), + wwH16(FF,63,95,0C,5E,B6,93,D1,07,E5,18,2E,F5,83,9E,12), + wwH16(59,81,23,8D,FE,7B,C6,30,F7,6C,BA,45,9D,88,BE,87), + wwH16(F0,63,F6,25,AB,76,94,AF,E5,7B,F1,79,BA,6B,B1,98), + wwH16(37,5E,6F,D1,53,C9,8A,E0,87,63,E4,B5,91,7A,28,15), + wwH16(6E,CE,01,40,9E,F3,7C,5C,42,A7,96,0D,FE,FD,54,B1), + wwH16(99,83,4F,E4,3F,9C,64,3F,B2,1E,6C,4D,23,7A,AF,97), + wwH16(4D,F6,1A,F0,37,97,18,4D,4D,91,D9,28,22,76,90,EE), + wwH16(20,7D,C1,17,5E,02,F4,4F,F3,D0,0A,25,0C,CA,68,27), + wwH16(C4,56,52,B5,5A,EA,8D,CB,F0,09,BB,98,FE,CA,77,03), + wwH16(60,DE,0D,D4,19,1B,6C,EF,8F,71,AA,3A,9A,F3,20,F8), + wwH16(94,62,05,8F,83,F3,73,16,E1,39,2E,66,DF,CF,56,0D), + wwH16(EA,05,2E,D0,09,F8,5E,E0,C7,72,8D,9B,00,20,25,D6), + wwH16(64,6C,B0,6B,65,47,D7,A6,AB,F9,4A,2A,E9,21,1A,AB), + wwH16(BD,61,C3,89,28,5F,18,1C,07,CE,8C,6C,75,DF,C2,16), + wwH16(E9,AE,24,DB,C8,87,A9,2B,54,50,69,CD,87,AE,86,2B), + wwH16(6B,EC,C5,83,43,B7,BB,F6,AD,D4,EB,93,A1,AE,17,5C), + wwH16(21,3D,DA,A5,F7,DD,96,27,DA,F4,5D,C1,B4,3C,95,B7), + wwH16(B3,41,3B,BB,66,2E,A8,B3,49,9B,77,68,77,18,10,0C), + wwH16(0E,04,D6,6B,97,15,24,19,05,F8,1A,0B,A6,EE,79,FD), + wwH16(C9,B4,8C,DE,88,BD,DE,6D,6C,58,E7,BA,25,C3,40,0E), + wwH16(D6,E5,C7,8B,C8,B0,F9,F3,BA,C1,0A,27,7B,4D,AF,4D), + wwH16(0B,B5,41,49,8C,98,95,38,E6,09,13,1C,82,EA,02,88), + wwH16(54,C1,45,9E,0F,64,26,87,43,12,26,E8,41,F5,70,19), + wwH16(13,41,ED,C2,35,18,C4,35,99,33,9A,23,46,2F,C8,F6), + wwH16(E8,EF,D9,8E,30,3C,1F,31,66,A3,8A,D5,27,11,16,14), + wwH16(6C,D0,AF,EF,E8,DE,B5,42,D0,3E,D9,52,55,1E,43,16), + wwH16(C9,BD,F7,7D,A0,EB,8C,5B,AC,B9,B2,24,01,15,41,D9), + wwH16(49,4E,60,70,D4,F5,DE,E2,22,C3,01,21,B0,48,5E,F2), + wwH16(F1,DD,73,7C,23,26,5A,83,6C,63,CF,D3,8E,20,DA,2C), + wwH16(E0,CC,58,4B,EA,92,04,7C,78,2E,FE,83,DC,60,88,75), + wwH16(D1,6B,FE,9D,12,09,06,D9,29,40,46,40,E5,8C,E2,6B), + wwH16(46,C2,06,31,14,31,E2,25,F7,DD,7E,0D,BA,A1,CA,24), + wwH16(1E,D7,3A,EB,B3,D1,57,08,16,B0,72,3E,9D,91,6B,7E), + wwH16(7D,DD,4B,30,C2,61,A3,06,9A,ED,2E,90,C3,BC,EE,B8), + wwH16(49,FC,4C,38,A1,7A,27,E2,60,E1,88,50,40,75,5A,76), + wwH16(F8,C6,EB,31,0F,5C,6F,44,68,26,9E,37,87,48,C6,1C), + wwH16(7C,53,4C,8D,75,21,27,0B,38,BF,AB,61,CB,36,B9,2A), + wwH16(8A,8F,EE,79,1A,CC,D4,1B,FF,7E,35,28,84,1A,57,67), + wwH16(76,9D,E6,E6,8B,A1,39,86,0F,7A,45,75,E6,CE,6A,44), + wwH16(90,AD,4D,D0,C7,2B,59,C9,E4,14,BB,0A,E6,B2,0F,0F), + wwH16(E4,C3,71,E6,2F,73,6A,D9,78,B4,5F,F8,E9,1C,5F,32), + wwH16(7B,3F,09,D6,95,0F,CB,CA,CE,03,AC,E0,E7,CC,36,78), + wwH16(E8,DA,63,81,BC,DF,AC,0A,54,8F,1C,3C,1D,07,E4,5F), + wwH16(0A,12,57,81,A1,0B,24,C1,8B,B5,6B,B4,C3,CB,C9,DA), + wwH16(07,3A,DA,72,4A,B7,E7,EF,D4,CC,66,68,1B,CE,AE,F9), + wwH16(AB,9D,5F,40,93,B9,2A,29,87,B6,92,34,8E,60,CC,C9), + wwH16(7E,E1,FE,05,F9,B6,18,83,F6,83,D3,C2,DC,DC,BA,4B), + wwH16(FC,EC,3B,FA,70,2C,C8,D7,FB,CE,7F,97,B8,02,38,6B), + wwH16(FC,C0,E1,DE,87,76,CA,AD,40,B2,E7,32,D5,E3,9C,B6), + wwH16(25,92,B9,01,AF,E4,F8,A3,D8,D9,6C,40,E0,F9,02,A1), + wwH16(E7,6F,86,9F,92,51,BF,A9,97,2C,DC,69,82,26,5D,55), + wwH16(31,4F,77,1F,92,93,CE,9A,10,24,33,A6,01,BD,DB,66), + wwH16(B7,DE,F3,4C,B1,73,C1,61,DA,1A,EF,7F,8D,27,3F,AA), + wwH16(32,1D,B4,5C,BF,75,11,17,43,D3,A8,FA,C2,30,83,A7), + wwH16(B1,07,33,FD,A3,62,46,94,A9,ED,C7,8D,68,CD,80,66), + wwH16(4A,4B,EF,E3,A6,A7,70,53,F7,73,C4,55,46,F1,94,FA), + wwH16(80,4F,0E,33,B6,4E,DA,2E,38,C4,FE,87,66,D3,A2,5F), + wwH16(A5,B3,19,11,2D,78,52,39,87,A5,D3,49,BE,B6,4F,7A), + wwH16(89,E4,CC,DD,AD,C3,CE,68,DE,11,6C,A8,9D,F9,93,9F), + wwH16(E8,F2,7F,48,73,F5,72,4F,54,E7,1E,D5,DB,B5,55,ED), + wwH16(58,50,5F,02,AF,26,78,82,AF,CE,C2,43,3C,81,0A,7C), + wwH16(F0,C9,5B,F5,85,EC,9F,D0,E1,B1,4D,B2,91,36,14,8F), + wwH16(42,D3,D6,2C,12,73,BB,7F,60,C9,D1,D8,C3,8A,44,61), + wwH16(EA,AF,49,74,37,35,9E,E5,DC,1D,CF,A0,77,BE,48,BC), + wwH16(DB,EC,29,B0,9E,9C,EA,87,82,BE,67,EA,9D,51,C3,85), + wwH16(67,79,9A,EC,4F,4D,E3,CC,C6,C1,86,B4,4D,02,E3,E4), + wwH16(63,A0,6D,F7,A4,1D,E1,8E,9C,B0,10,01,94,42,32,F4), + wwH16(C9,5A,E6,CA,7B,28,EF,0C,FA,85,9D,65,B3,B3,5B,BE), + wwH16(C2,0E,15,C2,3B,DA,35,A4,1F,44,5F,2C,8D,0E,B2,2F), + wwH16(7C,45,4E,B2,8D,E7,26,83,94,93,4B,B9,57,1A,89,6E), + wwH16(DB,25,A6,59,D4,32,54,F2,16,C1,48,70,D0,6D,89,15), + wwH16(94,61,F8,95,C4,78,C1,1E,AA,C3,9F,01,41,EA,D4,5A), + wwH16(13,BA,AD,FA,E1,4F,81,4D,83,56,85,2D,7C,03,A5,6F), + wwH16(96,70,D0,6E,5D,CE,6A,EE,30,BE,E2,E8,B5,75,C5,01), + wwH16(56,B9,59,42,8F,AD,01,2C,A9,00,C2,68,FF,75,93,60), + wwH16(08,C6,38,5D,33,68,69,06,DB,36,D5,7D,B9,36,47,82), + wwH16(6D,1A,3A,CA,0D,4E,0F,68,52,C7,8C,46,52,8E,06,39), + wwH16(CD,0D,3E,84,4F,5A,7E,F5,59,F1,A3,29,81,F7,27,01), + wwH16(3C,18,B7,22,AC,B6,40,A3,1A,22,34,09,94,BA,CA,32), + wwH16(59,92,0F,74,68,C5,D7,86,30,45,38,56,B0,BE,2C,47), + wwH16(DA,A3,EA,B1,BC,FA,9D,52,66,ED,71,EC,3A,BD,33,4D), + wwH16(D9,A3,94,FB,B9,EF,B6,34,DD,86,FB,B0,AC,57,22,F3), + wwH16(0E,B7,D4,B9,81,1A,D3,8A,40,6D,C6,F9,01,AD,97,BE), + wwH16(C3,DA,BC,9A,5C,EB,DB,D3,6A,06,D5,4D,6B,41,80,96), + wwH16(B7,74,75,8B,88,4A,87,9B,74,7C,6C,5F,95,C2,1A,82), + wwH16(58,7B,23,62,E7,34,20,C1,9B,DB,A2,80,C7,78,3D,58), + wwH16(E8,B1,36,09,3D,59,DB,59,61,10,BB,E0,51,F6,5C,0B), + wwH16(5E,00,54,BA,9E,BA,AB,D9,70,12,DF,F1,9E,8D,7E,B1), + wwH16(1A,49,24,4F,EA,6B,14,09,2D,53,46,EA,68,E8,8D,6C), + wwH16(C4,F8,2B,A2,7A,F8,0C,AA,41,5E,D6,0C,03,12,EC,06), + wwH16(F5,54,96,2C,1D,B1,83,1B,C4,76,E7,7C,36,60,62,6A), + wwH16(58,CE,A3,29,4A,20,23,48,7B,0C,38,6D,67,C8,8B,76), + wwH16(7D,D3,25,FE,FE,5F,CF,23,29,B0,E3,CB,69,94,56,CF), + wwH16(01,23,19,14,B1,C3,72,B0,96,7D,C0,29,F1,D6,F1,61), + wwH16(69,0F,22,43,B8,FA,4C,BB,0B,2D,50,36,ED,E6,DB,DD), + wwH16(61,DE,A3,FF,ED,B7,C8,BC,B0,58,6E,A2,C8,71,33,AC), + wwH16(34,69,94,20,22,1C,9C,E1,A1,F4,90,59,19,2C,19,EF), + wwH16(EB,40,B0,B8,6E,E2,D2,5F,5E,21,31,CD,1A,1A,B6,E4), + wwH16(48,4A,2A,A6,40,C2,47,DB,02,F1,5E,A0,8D,7E,69,71), + wwH16(F8,C6,CC,40,07,41,01,96,7A,A9,33,F1,89,54,98,43), + wwH16(55,92,7A,1A,2F,93,8E,DA,8D,04,C1,57,EB,B4,D4,FF), + wwH16(02,5E,AB,20,AF,BA,46,DD,E4,59,BA,B5,29,14,C6,18), + wwH16(0E,F6,25,CD,10,BD,88,A3,C1,17,77,19,1F,BF,63,3F), + wwH16(0A,A4,60,A6,2B,A0,EB,6D,EB,2F,EF,D0,2F,6B,CF,B5), + wwH16(A5,D1,DF,7F,DF,6E,F3,82,12,FD,CB,6D,D4,5F,1A,16), + wwH16(D4,FE,B2,2E,23,E5,66,5E,C0,28,6B,63,57,AD,74,77), + wwH16(CB,D4,29,D1,1F,5C,4C,5F,D2,DE,63,9D,B7,FF,90,26), + wwH16(05,35,31,B2,48,95,C4,53,30,CD,80,BD,28,A4,5E,85), + wwH16(85,D5,24,8C,07,94,27,08,B4,16,F5,BF,DD,06,E2,6D), + wwH16(2C,3A,2D,78,F6,D2,D4,35,17,D8,A4,23,B8,06,8B,01), + wwH16(24,3A,90,84,F8,51,CF,35,F9,48,4F,F8,D4,94,5F,05), + wwH16(80,B4,14,E7,CD,78,3A,01,8D,B0,DB,F6,EB,75,E6,31), + wwH16(B7,7F,3F,71,C2,C4,93,27,0C,99,04,89,3C,36,38,C1), + wwH16(7E,09,BF,80,90,03,E3,EF,96,B5,38,1B,DF,3B,0E,08), + wwH16(13,CC,41,E5,80,C3,EF,CC,4F,23,C2,3D,07,73,3D,FA), + wwH16(BE,D8,15,13,E2,95,B6,3D,68,72,68,0D,D5,46,92,1C), + wwH16(F7,7C,00,4C,EC,24,36,A6,A0,BF,83,45,80,C0,65,E5), + wwH16(36,0B,CA,B5,29,08,57,12,91,8B,F8,73,75,F5,B9,85), + wwH16(37,1F,45,1E,5E,FD,99,66,6C,37,98,34,5C,4C,0D,E2), + wwH16(98,E9,7A,91,56,BA,B8,3A,2A,A6,22,37,FF,B4,A3,58), + wwH16(96,70,05,A7,FB,25,C4,45,40,44,B9,8E,E2,1C,F8,43), + wwH16(10,AD,6A,7F,3D,36,49,F5,D6,9D,80,00,BF,C0,64,5B), + wwH16(BF,CB,DF,E1,EA,0D,8F,60,EB,B6,18,CD,5B,2E,4B,D3), + wwH16(67,B9,C5,24,3C,71,0C,51,F8,A3,C6,29,14,7A,EA,67), + wwH16(C9,05,5C,D5,14,D0,67,43,4E,E6,B4,C2,17,D5,96,08), + wwH16(3B,90,34,A1,94,D9,87,BE,F6,F1,ED,56,8E,03,D3,39), + wwH16(2F,DA,66,C0,B1,D0,22,F6,D0,09,22,3F,AA,58,69,DD), + wwH16(2D,16,5A,ED,1B,21,4F,AD,4F,C0,10,AF,4A,7D,08,C8), + wwH16(36,CA,EE,94,17,C1,2D,60,E8,0E,0D,8D,D3,B2,B4,B5), + wwH16(1C,A3,AE,B1,F2,A2,41,3F,3F,6D,F3,4A,BD,45,9A,CD), + wwH16(20,99,2A,38,4D,23,21,41,99,5B,78,7E,6D,C4,5B,05), + wwH16(CC,B6,1B,43,FF,F2,F1,7A,44,2A,D0,78,F6,81,2C,2B), + wwH16(C6,CE,CE,48,EA,FA,F5,C6,63,69,0C,41,52,F3,6E,DE), + wwH16(48,F6,38,AE,02,9A,33,12,63,F2,45,96,2B,7D,7F,93), + wwH16(8C,63,E7,1D,86,7B,6E,41,20,B3,DD,6E,94,98,54,C8), + wwH16(2C,24,70,73,66,6F,C4,D4,92,1A,43,86,02,C5,52,7C), + wwH16(36,E4,75,56,68,F0,C7,FA,0F,41,1E,26,CD,18,28,40), + wwH16(B3,71,CA,80,34,BD,18,4C,41,C4,4B,AA,F9,6B,2D,3D), + wwH16(C3,B5,59,C6,F2,75,DC,C7,37,65,2D,5D,89,12,E6,D1), + wwH16(4A,9E,58,D3,67,0D,90,3D,15,0B,80,25,46,41,7A,4A), + wwH16(16,14,AF,F1,D6,4C,BD,78,B4,C0,1A,01,AF,26,2E,48), + wwH16(B7,B5,E2,19,96,72,40,D0,01,3F,87,36,D3,13,05,98), + wwH16(47,6E,6C,F9,88,98,F9,53,E6,CE,34,E0,D3,D9,4E,DB), + wwH16(DD,99,B6,71,C4,02,A5,99,A8,58,52,ED,C0,24,9E,D4), + wwH16(FD,91,3E,40,A0,77,27,87,EB,AD,7A,2B,D6,DE,6C,B5), + wwH16(82,8F,F0,FD,64,1E,73,A0,BB,F3,AA,C0,9F,CA,1D,EF), + wwH16(A4,AC,DD,6B,F7,A3,51,36,F0,77,D0,59,C0,A7,9F,AB), + wwH16(89,43,1E,CD,BF,17,68,4C,85,F6,4A,FC,54,E0,11,92), + wwH16(9E,C5,07,1F,CF,F3,8D,45,7D,BF,E3,92,B1,E8,4D,02), + wwH16(A0,A3,C8,37,2D,FE,A4,65,48,36,8D,6D,16,95,BB,D6), + wwH16(D3,D1,9F,1A,68,4D,FA,55,B5,98,39,CB,B5,57,21,97), + wwH16(DD,D2,88,1E,4F,F9,90,B2,B7,F6,DA,F4,64,65,08,5C), + wwH16(3A,9D,9A,87,A2,DB,97,D2,4D,4B,32,A7,19,60,0E,A5), + wwH16(1E,6E,AF,C2,79,7F,8A,B0,19,CD,45,F1,D4,B5,F9,AC), + wwH16(35,6E,75,76,E6,F2,3F,55,F6,18,66,E5,68,B2,15,65), + wwH16(B5,57,E9,41,20,B7,E7,FD,EC,ED,6A,3A,09,E8,C2,AD), + wwH16(DA,E4,A7,B2,3E,8E,45,AA,F0,E2,31,84,C2,75,6B,6F), + wwH16(0B,13,E7,B9,74,FA,5F,7D,2F,0B,7A,77,70,B8,75,CD), + wwH16(EC,33,95,4C,A3,31,38,DC,11,28,D7,BF,4C,53,C5,98), + wwH16(83,46,80,B1,F7,03,AE,18,39,0C,ED,46,2C,72,6A,E2), + wwH16(FC,15,EF,D9,E3,11,18,97,6C,DC,CC,FA,1E,B8,FB,A1), + wwH16(C7,B4,10,C0,50,5F,1D,63,FC,EA,F3,2D,08,0F,19,35), + wwH16(82,0E,F1,56,D4,23,C4,AF,99,D8,72,EF,D5,14,62,9C), + wwH16(10,28,BB,45,4B,24,D5,75,AE,50,E3,27,BC,E5,2D,94), + wwH16(61,DB,62,79,6B,0A,13,1D,3E,70,84,B7,25,EB,C4,F5), + wwH16(5A,E1,49,57,45,A6,D2,5E,2B,0B,66,83,DA,C3,26,B7), + wwH16(AB,13,72,EC,BF,E1,3A,BB,1B,7D,3D,1A,1E,F0,6C,B5), + wwH16(FE,A5,63,14,8F,F6,BA,32,6A,A1,4B,62,DA,19,1F,E1), + wwH16(26,4A,A4,DD,E9,DA,2D,39,38,35,93,6E,B3,D4,86,DB), + wwH16(30,75,E0,B8,BC,1A,27,4F,54,B5,5F,28,A6,EC,8A,6E), + wwH16(D2,10,A0,EE,76,2F,5E,2C,9A,B5,0D,11,64,9E,64,84), + wwH16(1C,00,AF,4F,A3,0F,FC,4D,18,10,CF,6A,23,86,3E,BF), + wwH16(B9,E5,19,C7,82,45,5A,4C,FE,C8,10,2F,6B,BA,99,AA), + wwH16(77,70,28,1D,B9,F5,63,5D,A7,E2,61,1D,2B,9A,98,2B), + wwH16(CF,04,C8,F3,D7,05,D8,F9,75,B7,22,00,BA,CB,D3,E5), + wwH16(75,F4,01,7B,B6,9D,DC,81,93,53,AA,3C,85,FF,D2,59), + wwH16(2E,32,04,50,73,63,48,89,C2,7A,BE,37,E3,E3,BB,33), + wwH16(CF,23,BE,3E,6A,B8,B3,55,9A,99,58,28,6B,F2,8C,06), + wwH16(2D,69,2C,29,41,1D,19,6B,0A,39,D0,A2,CD,13,95,58), + wwH16(39,97,A3,A8,C1,6A,88,F0,8D,F7,9B,98,7D,76,53,B6), + wwH16(55,A4,59,34,F8,B6,F4,7E,FE,31,AE,01,DC,45,02,91), + wwH16(E8,04,94,34,E3,19,3C,25,75,49,21,ED,C2,F1,97,E7), + wwH16(76,CF,D3,8B,50,71,2E,33,FF,9E,38,4E,B2,10,55,2F), + wwH16(0B,0A,8E,40,17,61,56,CB,9E,6C,0E,92,4F,8B,E6,28), + wwH16(D6,45,F9,1D,17,9B,09,EF,51,31,09,D1,27,D3,0D,09), + wwH16(EF,58,1D,3C,CC,45,CB,07,84,C1,1F,EC,3C,90,95,02), + wwH16(14,4A,0E,24,7F,D6,54,8B,AA,EE,B6,88,5C,62,6A,6E), + wwH16(DF,F7,FC,47,2E,AA,B7,0F,C4,28,F2,64,BA,0A,F1,1A), + wwH16(CF,BA,B2,F6,CA,F0,62,48,F8,72,E9,43,0F,42,43,36), + wwH16(A8,D1,CD,6E,5F,75,9A,E1,80,CF,16,4B,41,18,9F,84), + wwH16(91,3F,33,D8,D3,94,F4,BD,4D,FD,72,88,CB,D9,15,FC), + wwH16(21,07,18,D3,7D,52,94,F3,F7,B3,68,80,EA,21,B7,A3), + wwH16(07,90,DE,79,E5,59,75,55,93,A6,A9,6F,FB,D6,9F,CE), + wwH16(35,50,67,BA,E7,A0,B1,9D,77,A4,F2,88,20,C3,CE,13), + wwH16(AC,FF,32,68,CB,43,30,D2,60,40,86,69,C5,B8,F5,AF), + wwH16(83,F2,A5,B1,90,C2,32,61,4C,89,E1,0D,20,A4,62,4A), + wwH16(0A,0E,FC,95,99,30,F4,EC,53,7B,E9,F7,69,6D,15,38), + wwH16(E1,58,A8,ED,D2,F5,2D,5E,93,11,82,C2,1D,E6,57,83), + wwH16(56,A7,1C,C3,17,D9,BA,F8,7C,F2,55,FC,6D,F0,B8,F4), + wwH16(4C,A8,CE,98,6D,AC,A6,AA,39,A7,6C,5D,63,F8,CA,7D), + wwH16(B4,E2,F6,4E,AC,D5,72,9F,75,A9,4F,BB,9C,78,D4,06), + wwH16(FA,82,3E,A6,C7,ED,71,0C,79,DB,A1,45,AC,10,3A,A8), + wwH16(EA,83,B8,14,C5,98,0B,BB,E2,29,EB,1D,4D,D3,8F,4A), + wwH16(7C,C8,66,10,1D,52,6E,71,7A,43,7F,D4,57,8F,48,A3), + wwH16(A2,90,9C,A3,7E,F0,BB,0C,82,F5,10,3B,58,20,B4,55), + wwH16(4C,ED,7D,85,5B,0A,9E,DF,10,83,0D,E4,A1,3F,D4,A4), + wwH16(1E,A0,85,9C,19,D4,40,1B,BB,27,44,E5,17,58,BE,0B), + wwH16(18,1C,1E,BE,4A,2C,C5,FA,CB,AC,EE,5C,2D,AC,F3,DE), + wwH16(0E,5B,FF,57,0D,AC,7A,00,4A,4E,B0,44,7A,22,48,68), + wwH16(1D,ED,57,F7,7B,B3,25,82,A2,A8,22,DA,65,66,E4,DA), + wwH16(47,8A,57,F2,63,39,72,DB,B2,D7,85,6C,70,6A,4C,9A), + wwH16(A0,B7,38,5E,7E,9A,70,8D,1E,B6,83,87,60,F8,3C,C1), + wwH16(B6,C3,E5,C8,44,0D,08,33,15,AF,3C,FD,A5,63,04,83), + wwH16(C4,FD,22,FF,82,4A,D1,E5,9C,2E,19,0F,4A,C5,2D,4F), + wwH16(7F,58,78,8A,96,67,00,6F,60,AE,E5,B5,70,69,D5,B3), + wwH16(68,CD,3C,9D,9C,E5,B8,7D,2B,41,26,64,F9,27,1E,B2), + wwH16(79,45,F5,D8,A2,34,66,DE,C7,44,A1,76,33,56,59,E3), + wwH16(A9,7D,E6,BA,BB,43,AC,FB,62,96,20,A0,3F,21,51,47), + wwH16(AA,60,AD,BE,DB,28,C7,1F,7D,03,A7,CA,B8,74,63,8E), + wwH16(08,8E,B7,F5,BF,B6,B9,EB,48,CC,C7,01,EA,65,50,CA), + wwH16(57,FF,F9,0E,E0,50,CB,15,96,5E,12,3F,89,C6,67,65), + wwH16(7C,B4,08,E9,B4,54,B0,3D,D3,2D,EB,E0,CE,24,33,79), + wwH16(6E,19,C3,F3,B0,15,71,AF,97,6B,2D,7A,AD,C0,68,66), + wwH16(EF,B6,3B,44,60,8B,66,53,51,C2,31,C1,5A,43,DB,0F), + wwH16(8F,B1,F0,FA,F0,7A,6C,F9,0A,40,9D,6F,3F,69,C0,3F), + wwH16(9A,11,85,AC,29,AC,F0,AA,ED,54,55,17,3C,55,69,69), + wwH16(2E,F2,AD,10,90,92,09,48,5E,10,B1,16,CD,F8,4E,E2), + wwH16(4F,84,A4,AB,3F,64,CC,31,E8,85,91,8A,E2,94,76,C5), + wwH16(A1,50,DE,85,14,6D,79,68,DF,65,51,9F,BE,7C,8E,0B), + wwH16(F8,E6,4A,EB,61,3D,40,CC,AC,65,B7,C8,74,F2,F8,8B), + wwH16(58,91,1F,C8,D7,87,99,EA,63,7B,B6,55,0C,A2,8F,2F), + wwH16(FC,DB,47,8C,07,42,BA,53,7B,AE,66,09,55,39,F0,10), + wwH16(F2,74,8C,A4,18,7A,2C,16,DA,93,14,AA,3F,C6,70,32), + wwH16(68,65,1C,15,DA,09,BD,25,AB,7D,0A,90,C5,E6,27,68), + wwH16(26,DC,4F,7C,6E,53,65,29,CB,4B,D6,17,4B,CD,53,45), + wwH16(49,E3,02,50,A5,CD,36,D4,2A,9A,E2,16,7F,7E,AC,9F), + wwH16(7D,E6,15,9A,AF,61,65,64,4B,0E,14,7D,D3,59,55,C5), + wwH16(43,AB,78,0C,CF,12,D9,5A,02,94,78,93,DF,FA,F9,97), + wwH16(52,A7,3E,69,6E,9E,B9,40,D1,48,F0,6E,38,3C,DE,32), + wwH16(08,E4,95,7A,56,08,87,FE,C2,16,FD,BF,5B,00,3A,A2), + wwH16(31,E6,B2,F1,2C,1B,82,75,7B,C5,90,06,7A,A3,FD,BA), + wwH16(D2,20,FB,8C,AB,81,8B,1A,B6,38,8A,1F,65,47,8A,D6), + wwH16(E1,D6,FF,1A,D5,A6,E5,AB,9B,3C,76,A5,D9,64,00,B9), + wwH16(2E,21,87,BC,09,33,5A,2D,B0,8E,4C,C1,65,8A,DD,C3), + wwH16(B7,60,3B,A9,D5,E4,F6,38,8F,4F,5E,79,C8,FC,5C,5E), + wwH16(00,F7,39,7C,F4,DD,C2,40,79,5A,66,F3,26,1D,B7,76), + wwH16(04,29,62,2C,ED,A0,48,FD,B2,CE,AC,EE,40,B9,AB,68), + wwH16(21,FC,CE,04,61,98,64,E7,3F,EF,14,E2,12,94,84,D7), + wwH16(91,51,09,2F,06,76,46,1A,70,B6,CC,9A,6C,64,37,D0), + wwH16(19,E9,7B,83,25,A8,D2,22,0D,EE,B6,9A,0A,13,0C,FA), + wwH16(E8,1A,2E,07,5E,9B,0C,55,A6,4C,B4,EA,4F,C3,10,66), + wwH16(E9,D1,D2,A3,7F,3F,85,4F,A7,4D,53,F6,C9,AE,29,9E), + wwH16(9F,45,F9,06,30,B4,BB,89,D2,76,24,7A,56,F5,80,2F), + wwH16(7A,2A,A4,24,A9,87,0E,06,BB,81,D1,1C,05,1A,BC,92), + wwH16(08,51,47,91,F9,49,2C,4B,64,A1,40,81,F1,F6,56,7B), + wwH16(48,CD,75,37,CC,D0,75,56,D6,0C,C0,81,C0,22,3D,42), + wwH16(F9,FE,B0,95,48,35,94,F7,69,F5,28,63,67,31,62,B7), + wwH16(37,C4,92,79,9B,1B,F7,74,17,75,43,D6,15,E4,AD,D2), + wwH16(3B,17,8A,6A,AB,46,B4,93,BD,6D,35,2E,A3,B9,85,BA), + wwH16(24,B5,2D,86,86,D7,2C,D8,44,53,7A,DA,C2,5B,C0,CC), + wwH16(5E,C2,DA,D1,9A,9C,13,5B,FA,23,74,84,FD,28,75,CD), + wwH16(F1,D3,15,FB,1C,BC,88,6F,4B,35,4C,2A,C8,F2,EE,B5), + wwH16(3D,7C,30,DE,3A,0F,06,20,78,6D,76,58,A5,06,4C,4D), + wwH16(36,76,DB,E2,D0,C7,23,81,74,2F,E3,01,DF,22,D7,29), + wwH16(50,4E,97,AB,07,4B,E5,2F,9B,86,01,73,BB,A4,B6,70), + wwH16(BA,28,A3,63,59,1F,E6,C8,AE,33,37,FF,FC,51,6C,6B), + wwH16(96,5E,85,9D,2C,4C,D7,D6,90,30,27,AE,A2,9F,7C,70), + wwH16(8F,AB,28,94,93,EF,BC,C2,94,88,1E,AB,97,4B,A0,C3), + wwH16(75,22,C3,5A,C0,03,4D,7D,0F,61,1C,9A,1E,88,6D,9E), + wwH16(8E,96,34,D9,F5,75,48,78,08,22,A1,8E,75,BB,57,3F), + wwH16(46,3B,AB,B0,0E,6A,79,25,C1,C4,A8,D6,01,FA,CD,38), + wwH16(C9,DA,8E,A7,AD,6F,24,14,41,09,2B,E5,48,BC,AA,38), + wwH16(B7,D7,31,78,19,B5,02,44,43,76,F0,08,FD,4B,D8,07), + wwH16(0C,1C,2A,E5,2D,06,5F,11,07,5E,E3,A1,CD,0E,64,2D), + wwH16(40,AA,05,C7,78,EE,1E,9B,13,5C,3E,A3,CB,53,E1,4D), + wwH16(6C,30,4B,10,42,A8,0D,39,07,89,67,90,2C,6F,CE,0C), + wwH16(57,18,4D,82,03,86,08,DE,D2,AA,59,95,53,32,32,EB), + wwH16(07,29,14,78,BC,F8,02,FE,9D,39,8B,54,39,B4,B8,1C), + wwH16(3C,BA,4A,2E,35,0F,1D,B4,0C,6C,A7,24,D8,A5,CB,E4), + wwH16(23,BE,8B,0E,04,C4,07,BA,31,4F,E9,19,90,48,4E,9D), + wwH16(5D,B3,B0,D7,6E,82,F6,71,85,AB,D0,93,EC,B2,1D,63), + wwH16(08,CC,0A,0B,72,17,75,2D,8E,CD,73,BD,7B,0D,3C,37), + wwH16(FA,EA,5C,B8,CA,ED,23,A3,9A,C5,FD,B7,6C,38,E7,6B), + wwH16(E1,AC,3A,CC,FA,C7,ED,F8,5F,88,1F,43,A7,F1,3F,0D), + wwH16(14,93,4B,4F,9D,18,60,86,78,CF,A8,D8,16,BE,AF,1E), + wwH16(87,B1,DA,1B,AE,B4,5A,B7,25,E1,1A,3D,91,7F,FB,2C), + wwH16(87,2B,38,E8,76,13,B2,F4,0F,AF,9A,74,FB,ED,29,E8), + wwH16(76,95,AD,05,DE,5F,C0,04,08,43,AC,86,8B,74,DC,C3), + wwH16(69,B8,C9,90,50,39,FF,B2,9C,81,E9,46,3B,A5,A0,E6), + wwH16(E0,DA,81,C9,23,CA,23,C3,BC,0F,38,E5,D3,C0,FC,D4), + wwH16(EA,B6,01,67,C5,14,66,A3,52,9A,DF,19,B4,40,17,D9), + wwH16(95,E6,FD,A8,BF,4D,F7,49,95,F4,63,72,0B,9F,19,CF), + wwH16(1C,08,76,48,18,F1,4B,D9,DD,F1,A1,31,6B,82,A3,F7), + wwH16(5E,67,B0,13,7F,16,1F,19,7A,E8,18,68,14,47,A2,5F), + wwH16(D9,55,5D,6E,60,DF,B0,C5,68,E7,17,F6,9C,89,93,F4), + wwH16(ED,E1,BF,DE,3A,43,1A,24,9C,82,0C,14,A6,5C,C8,61), + wwH16(50,39,78,59,7C,89,71,82,96,2C,54,53,12,91,65,F4), + wwH16(B8,21,20,F9,1F,3C,33,87,F6,B4,CE,1C,20,E3,75,E8), + wwH16(AF,90,4B,74,1D,7C,0E,CE,9A,4E,BC,B1,42,03,54,8F), + wwH16(05,1E,FD,5A,A7,00,8B,03,AC,0C,F1,47,72,9D,9A,A9), + wwH16(47,8C,E5,86,CB,19,B7,6F,B5,31,5F,D9,3D,38,76,D6), + wwH16(15,3A,22,8C,DA,05,C3,A2,6C,82,5A,DA,A3,15,69,2E), + wwH16(11,A9,19,BD,5F,AB,EA,1A,7C,C1,70,BD,2B,81,9F,65), + wwH16(D1,E4,3B,B0,EE,FC,73,31,07,0B,DF,65,78,3A,55,8E), + wwH16(74,3D,B5,28,66,FB,DB,83,F6,C1,D8,04,69,5B,99,53), + wwH16(22,A3,8A,F8,D0,DC,21,B2,E3,8D,B2,CD,2C,1A,30,EE), + wwH16(1E,CC,85,10,BB,33,92,0B,74,4F,AE,7D,85,15,16,85), + wwH16(D4,A8,A2,84,1B,85,4F,43,7A,1A,B7,53,95,B1,19,62), + wwH16(BA,F0,09,47,7E,65,E6,31,2B,13,C0,22,50,F9,07,77), + wwH16(54,13,18,46,F3,9F,0F,74,48,C3,00,EA,42,A4,11,A3), + wwH16(6D,71,D5,01,F3,98,E6,1A,FB,C5,44,DE,89,1F,80,91), + wwH16(C9,06,31,C6,EE,DA,5D,8D,1B,42,AE,38,8F,13,47,5A), + wwH16(B1,92,C4,55,4C,73,B4,48,3B,D6,83,7A,1C,40,4F,51), + wwH16(BA,1A,3A,8A,30,72,66,B5,E2,67,66,48,03,EB,67,E5), + wwH16(C7,F4,9B,B9,77,30,19,D7,BA,EE,89,03,E3,1E,6B,68), + wwH16(30,55,C4,07,4B,19,44,1F,DC,7B,55,CA,43,B0,02,F9), + wwH16(04,FD,D0,FF,A1,02,ED,F7,95,63,8D,0D,5F,C9,40,6C), + wwH16(37,42,7C,C3,38,DA,3A,85,72,22,03,4B,D3,12,BE,D1), + wwH16(A1,4F,23,2E,6D,25,94,0F,7F,DC,09,09,6A,C7,FA,6B), + wwH16(E0,FE,15,69,71,B9,13,73,DE,9C,3A,11,59,FC,16,5B), + wwH16(AC,DC,4E,E7,84,6C,8F,BD,52,E7,4B,D7,28,3A,90,DE), + wwH16(66,4C,F5,67,51,D1,6A,24,1C,2F,A5,3B,0A,08,56,2C), + wwH16(FB,CC,FE,D0,47,2B,50,77,25,14,E8,B5,30,5A,2C,53), + wwH16(2F,C6,43,00,43,35,F0,CC,2D,DA,97,59,EC,CD,47,B1), + wwH16(8C,E9,6D,6D,C0,55,96,27,7A,9C,3D,87,3D,CE,92,43), + wwH16(40,3E,41,53,3F,2C,D4,4A,50,78,CC,04,C2,48,3D,89), + wwH16(47,EE,BC,0F,5E,C5,52,F8,D7,72,BE,31,5A,61,77,2D), + wwH16(EC,31,EE,CD,D0,81,41,72,55,4B,46,DB,3B,ED,19,CB), + wwH16(9A,52,D8,D3,15,01,E4,D8,99,DF,B3,3A,B0,FC,5A,F1), + wwH16(A1,98,FA,F7,55,0C,1E,68,39,56,A7,16,A5,24,CB,0B), + wwH16(2B,C6,4F,0B,98,4D,3A,54,82,7A,D0,35,6D,5F,A0,8A), + wwH16(06,EC,9F,61,CC,E9,D3,5F,B7,2A,60,DA,CE,ED,10,F8), + wwH16(B9,AF,8A,87,0B,D4,16,6A,AE,AD,4B,FF,94,85,AB,91), + wwH16(2E,2F,DA,D6,66,7C,67,4D,DF,17,4E,C2,A2,71,79,11), + wwH16(C3,D1,16,9B,67,AF,10,9C,56,C9,80,07,91,57,40,B4), + wwH16(E7,A1,51,8E,C8,60,5E,47,5E,85,D0,80,46,98,4E,66), + wwH16(05,AC,F6,54,9C,68,12,5F,C5,68,2A,2A,10,84,64,C0), + wwH16(16,63,F6,55,2F,65,1E,89,F9,72,91,23,83,9A,35,F5), + wwH16(CB,1D,57,20,6C,0C,8E,EA,88,1D,D9,EA,49,95,31,A2), + wwH16(A1,87,B1,C8,04,5A,28,C8,D7,38,0D,E8,A5,48,AA,A6), + wwH16(19,96,80,A6,C0,A5,A2,05,31,99,80,FA,D8,3E,DB,66), + wwH16(21,DE,A6,49,AF,82,6B,5E,55,A7,6E,D0,F5,89,0B,5B), + wwH16(45,3A,80,1E,94,D4,17,9C,75,14,42,93,E7,D5,AE,1A), + wwH16(E8,AC,9C,F3,43,A5,0B,0B,E3,9D,01,C1,B0,9C,B3,7B), + wwH16(82,D5,04,DE,99,8F,97,BF,34,EE,35,00,9C,56,C0,23), + wwH16(C5,4B,91,CF,40,D0,D2,06,6E,9C,11,52,E3,7F,00,BE), + wwH16(C8,91,1B,9D,35,C4,D0,4F,3F,F2,3A,54,96,84,E3,5F), + wwH16(0D,7D,F3,EE,46,54,F3,CB,48,77,9D,38,6E,59,14,17), + wwH16(9B,38,37,DB,DB,33,8D,9B,6C,48,4F,1A,10,04,ED,C0), + wwH16(F7,CC,29,1E,B5,1E,03,E7,50,F6,D4,F4,A4,D9,4B,60), + wwH16(06,07,51,2E,A3,CC,CB,35,B3,E3,73,F3,D2,69,74,20), + wwH16(04,BC,BF,96,37,83,4A,B8,15,1E,0F,D4,E9,05,67,29), + wwH16(63,F5,B2,D5,89,5A,5D,78,81,57,63,05,EA,DD,A0,30), + wwH16(53,78,91,57,B5,24,BE,13,09,A8,D9,DA,30,5B,B7,C5), + wwH16(22,9E,06,F3,AF,50,20,CD,02,F2,61,74,6C,54,64,39), + wwH16(22,01,ED,1C,B0,DB,E1,49,6D,BA,22,D9,CF,25,3C,92), + wwH16(BE,19,48,30,2B,01,B1,BE,A3,AC,1F,01,58,DC,BA,B2), + wwH16(73,E7,11,D0,87,2E,5F,73,41,22,A0,3A,DA,66,42,90), + wwH16(36,E8,2B,A1,FD,92,2E,A1,F9,0F,DF,FF,4E,5C,34,30), + wwH16(1B,8E,BC,E2,BE,DD,13,65,CC,9A,3E,41,5F,C6,73,4C), + wwH16(1A,C6,C4,62,D5,00,B9,AF,31,4A,9B,BE,D1,78,71,4D), + wwH16(1C,E2,71,EC,EC,4B,F7,2E,63,84,BC,84,DC,B2,0B,A5), + wwH16(D4,1E,4E,EE,B9,1A,CA,EE,97,F7,BF,D2,E0,AD,6F,65), + wwH16(A8,96,A5,AA,BA,80,1C,D3,0E,96,F2,B7,4A,EB,A5,FD), + wwH16(8C,4D,C7,9B,B1,CA,AE,00,93,45,50,00,BB,62,28,90), + wwH16(78,D4,FC,3D,B0,C5,87,99,2F,6A,F5,06,0E,87,12,9B), + wwH16(84,D9,BB,2A,58,CD,D0,B5,41,CF,D6,C6,A5,3F,13,6B), + wwH16(8E,C8,E5,0F,40,C6,5A,8E,E3,C3,80,6E,4D,0E,25,77), + wwH16(57,B2,74,0F,62,20,D7,0A,A6,3D,51,A7,79,13,A9,64), + wwH16(A1,5A,7F,79,8E,C3,F9,43,D1,FE,D3,11,96,82,41,39), + wwH16(5C,4A,1E,60,B0,80,97,99,6F,F2,CD,A2,41,E7,69,BB), + wwH16(1C,31,88,02,17,7A,55,A9,2E,67,56,E2,B3,0E,53,9E), + wwH16(E7,E1,7A,75,A2,8B,F6,76,83,A7,F3,FA,88,AC,C7,8B), + wwH16(27,6E,3E,B3,ED,76,07,6D,1D,A7,10,63,61,01,82,37), + wwH16(88,0A,6C,86,14,67,B6,E4,8C,76,F4,B6,22,8B,07,7F), + wwH16(CB,0E,EC,65,8D,D1,98,60,20,4B,0F,40,24,F9,BD,2E), + wwH16(0C,9B,7C,5C,42,20,04,F4,54,51,00,1B,D2,86,BE,E9), + wwH16(CA,AA,CE,B9,20,8F,F0,E3,16,5E,7F,ED,03,43,3A,18), + wwH16(3D,89,EC,E1,62,8C,79,99,8A,59,77,22,4F,D5,7A,DB), + wwH16(4D,62,9B,DA,F7,A8,AA,C5,F3,44,79,04,B4,FD,22,62), + wwH16(51,48,CF,B6,7B,B8,FF,55,FA,D9,B6,7D,D0,EB,0B,6D), + wwH16(AE,86,F4,9D,E0,C4,40,FC,DD,AE,41,CF,13,32,11,4E), + wwH16(82,DD,4D,20,87,D8,F5,04,4A,98,52,D5,AA,71,71,4B), + wwH16(EA,32,5E,CE,77,12,1E,EB,09,B2,E0,73,2C,73,BE,02), + wwH16(3A,02,B8,4B,21,C5,48,8D,23,8D,80,8D,AF,AE,3B,81), + wwH16(AD,EB,C9,8A,9B,98,C5,28,71,3A,A8,42,25,15,81,71), + wwH16(B0,E9,B4,14,96,BF,03,13,21,28,7D,70,BD,DB,FF,7A), + wwH16(7D,37,90,8D,53,DD,DC,2F,E7,41,2C,F4,71,31,B1,A6), + wwH16(BC,43,B3,FB,F8,0A,54,C3,CF,99,E1,7B,28,54,60,43), + wwH16(94,6F,1D,85,A8,41,76,F0,BF,DE,96,E5,3F,2F,CC,F0), + wwH16(02,85,78,1B,FE,B8,55,B8,78,5B,C1,99,46,C0,8A,D2), + wwH16(D2,E6,F6,F3,2A,99,C6,0E,4C,CB,3D,2D,64,DD,25,43), + wwH16(6F,07,EE,D6,A6,F6,5F,78,80,04,48,D5,3B,78,A0,92), + wwH16(63,EF,10,CA,98,97,93,BF,80,70,DC,A1,B7,77,51,33), + wwH16(60,45,54,4C,85,B7,4E,89,E7,AA,88,60,88,58,C1,9A), + wwH16(79,4E,2D,6D,E1,49,FD,31,97,C9,97,F8,B9,62,DE,8D), + wwH16(71,1B,73,0C,90,CA,67,AB,F2,44,80,79,75,69,AF,87), + wwH16(AD,8F,E5,49,F7,2F,C3,46,1A,48,70,8A,EF,D8,BD,7A), + wwH16(03,46,9F,3B,F1,C2,BF,6B,5A,A2,54,23,74,CF,B2,50), + wwH16(21,1E,8C,9E,B6,C3,52,09,F8,81,49,C0,38,C5,9C,77), + wwH16(71,ED,4C,56,76,9F,15,62,D2,4B,D6,84,77,6F,C3,CD), + wwH16(95,E2,81,95,2A,30,4C,35,A8,9D,CD,41,FB,43,1A,2A), + wwH16(B7,2C,39,A1,B7,58,1B,A4,90,54,56,B0,70,FD,E1,85), + wwH16(D7,10,33,0D,40,BB,9E,78,24,14,A6,40,A6,F3,88,DA), + wwH16(A3,9E,A9,C8,11,78,1E,01,01,39,88,85,60,87,2F,3C), + wwH16(C1,32,1B,FF,60,1A,E3,52,AE,06,3E,01,06,A2,E5,D2), + wwH16(5C,EF,5F,1F,9F,F3,AC,5C,D7,59,CD,B6,8E,32,18,F8), + wwH16(70,5B,31,BE,FD,69,5D,A4,2A,CA,80,F5,29,32,DA,41), + wwH16(70,A7,F7,8E,4C,2C,31,34,65,C9,66,8F,E1,17,B3,CF), + wwH16(8F,2F,CE,79,E8,81,88,86,F0,F5,62,16,CF,34,26,0A), + wwH16(99,DC,57,84,6D,88,BC,69,64,CD,19,10,55,71,73,F9), + wwH16(BC,7E,57,13,AF,00,70,A0,BB,6F,1A,11,B3,DB,C8,B6), + wwH16(4D,F4,DF,8D,BD,82,E8,74,25,DA,E0,8A,CF,E1,0A,FE), + wwH16(EF,09,4F,AF,2D,98,24,8E,84,93,C8,A0,2E,45,95,83), + wwH16(0F,DF,2D,1F,89,31,47,68,A3,7B,83,4E,EC,70,C4,B1), + wwH16(D8,B4,A8,E5,D8,F5,83,E3,A4,E0,41,46,8A,04,06,3D), + wwH16(80,84,B7,BD,F2,A9,7B,DC,C2,17,A8,2F,63,00,4F,56), + wwH16(2A,6E,A9,7E,AD,AA,BE,ED,BD,65,4F,FD,28,A1,81,2B), + wwH16(F8,8A,98,29,D6,EB,34,21,AB,E6,A5,CE,62,FE,25,E0), + wwH16(13,4E,C2,12,F5,C4,87,A0,56,6A,08,6A,08,05,8F,9C), + wwH16(C7,84,E9,E4,B4,2C,DB,DD,35,B1,17,6F,DA,9F,BC,DE), + wwH16(B6,48,7C,BB,98,EB,75,B9,03,4B,03,F7,9E,E7,0B,14), + wwH16(B8,94,95,3F,8A,CC,BD,C7,EB,83,45,90,DF,CA,1B,1F), + wwH16(2F,75,5D,A3,64,F3,22,F3,DF,97,45,6F,E9,4B,02,28), + wwH16(2E,9D,81,6F,03,E3,E1,25,31,4F,47,DF,56,16,E1,FF), + wwH16(C0,90,01,48,36,27,45,68,6B,61,A7,E8,52,31,00,55), + wwH16(1E,CA,CC,98,89,BF,E0,CE,C3,4A,5F,62,D7,4F,80,E5), + wwH16(F3,74,47,20,2E,32,B0,D1,CE,FC,8A,E6,1C,BA,7D,14), + wwH16(81,88,F9,58,75,43,85,5C,06,70,FD,4E,0B,F5,6B,BD), + wwH16(A6,D7,4F,9F,52,8F,3A,B2,F6,F2,5E,CB,EA,DA,EC,49), + wwH16(84,91,B9,FB,EF,56,D6,3D,44,24,02,06,2D,A5,8E,46), + wwH16(C1,98,26,9E,0C,D3,59,99,7A,0A,B2,66,8D,B5,96,4B), + wwH16(BF,9D,DF,EF,10,E5,F3,8C,4A,7A,50,B2,46,08,68,E0), + wwH16(D3,BE,39,BF,C8,0E,42,07,A5,C7,BF,EC,82,23,BC,2D), + wwH16(0F,BB,66,F1,B1,57,41,77,EF,32,39,7C,EC,1E,4B,A2), + wwH16(4F,EF,C8,13,EB,00,BC,54,7B,A7,3E,2C,4C,1D,1C,2E), + wwH16(3B,4B,7E,9C,91,4E,6E,CF,47,FA,99,88,7B,82,E8,D0), + wwH16(29,29,AD,23,82,F1,30,46,DB,6E,49,65,3A,5D,71,02), + wwH16(7F,70,13,26,CC,2E,87,EC,AF,18,5E,D6,77,AF,F3,33), + wwH16(18,67,9F,F1,81,61,35,9B,E2,B5,74,75,7E,20,AB,F1), + wwH16(38,F3,46,06,BF,21,F8,68,B2,51,8F,3D,7D,19,34,99), + wwH16(F3,B5,EF,E3,F5,1A,5D,D4,C9,BB,BC,E9,29,0E,2A,01), + wwH16(29,52,41,9C,8A,69,1A,02,6E,16,B5,B2,15,46,3D,D2), + wwH16(A0,8C,9B,F5,C9,25,96,4D,C7,A2,57,8E,90,2D,9C,11), + wwH16(84,22,22,C5,33,11,4F,1C,6C,AC,30,A6,FB,B2,2D,9F), + wwH16(B3,D8,E5,EC,21,55,BE,C1,0F,23,F1,9E,B7,8A,8B,42), + wwH16(33,BB,66,5A,7A,FE,BA,85,72,34,CA,07,D5,59,35,82), + wwH16(87,73,0D,A4,30,A9,07,29,10,E7,7D,DD,DC,F5,DC,48), + wwH16(65,C9,8A,EE,EE,E8,4F,0E,70,62,4F,C1,FE,4B,7C,27), + wwH16(BC,EA,0E,6E,BE,C3,60,AD,FB,42,F8,A9,6E,8D,24,60), + wwH16(FF,18,B3,CA,C4,4D,E3,7A,15,8B,9D,68,6B,00,D2,BD), + wwH16(8D,A4,F1,E6,E9,2F,2B,CA,39,4E,1E,68,DA,98,D0,13), + wwH16(10,48,5E,D1,A5,EC,88,A4,71,1E,AE,7F,39,BF,B9,8A), + wwH16(46,07,C0,ED,7D,42,B2,EC,54,F6,FE,38,E4,02,4B,2E), + wwH16(66,D1,3A,11,D7,A1,EF,9C,39,73,F7,EA,BD,22,F8,B2), + wwH16(C6,70,B9,D3,FD,2E,8F,97,47,8D,6A,6B,5A,28,BD,ED), + wwH16(2B,30,4A,EC,A4,C1,18,80,E2,00,9C,7E,1C,24,12,1D), + wwH16(85,47,56,11,26,E7,0E,FA,3D,69,26,CD,D6,BE,E3,72), + wwH16(DD,74,59,68,EC,A7,3C,94,45,53,19,7F,14,4C,F7,A9), + wwH16(0A,F4,C9,D7,2F,9C,C7,D4,AE,82,E4,DD,6B,AF,22,38), + wwH16(53,3B,1C,1D,A2,6C,40,3D,E8,A5,B8,B7,B5,59,01,E8), + wwH16(99,EC,B1,4C,35,CC,C6,7E,AE,AF,75,B3,A2,43,73,C2), + wwH16(1D,30,14,DE,E8,A5,AC,C8,C5,5E,57,17,2C,11,1B,47), + wwH16(09,58,E5,0B,D2,9E,39,C1,2F,9C,29,96,5E,9A,10,8E), + wwH16(F1,86,35,37,7D,68,34,54,83,BF,FB,E7,27,4E,08,6B), + wwH16(C1,C0,73,EF,46,93,6B,24,66,42,31,4E,AC,F7,C1,6B), + wwH16(B4,DA,CC,75,69,A6,5B,F8,65,23,DF,A4,7C,D7,54,5C), + wwH16(71,DD,62,19,54,CB,30,CE,AE,49,F5,14,49,7D,52,2D), + wwH16(AF,04,B3,A5,BA,DE,93,74,F4,BB,F3,AB,7B,C6,8A,54), + wwH16(E6,6A,A8,79,18,37,51,29,E6,88,D4,52,45,DB,FB,40), + wwH16(26,E4,FD,D4,D6,EB,33,F3,0D,49,0F,27,10,19,22,7E), + wwH16(CF,58,61,90,BF,E1,FB,27,AF,C5,0E,CB,09,1E,EA,87), + wwH16(72,50,78,56,16,B2,E5,4D,E2,9B,FE,8F,DC,54,8F,38), + wwH16(C0,55,20,76,D9,E2,3E,5C,F5,13,BE,FA,37,F9,85,4E), + wwH16(B1,68,A6,6D,7A,01,14,31,AD,B3,1D,9B,77,8D,46,0E), + wwH16(F1,55,88,86,FE,91,65,B2,80,E7,C0,D3,AA,32,B0,F4), + wwH16(0A,5B,4F,C7,71,2D,8D,B8,92,3C,6F,92,31,A1,18,5E), + wwH16(68,84,8F,FD,E1,74,A9,63,07,17,1E,0D,F2,49,BE,F6), + wwH16(BD,C9,C5,23,56,12,6C,77,0B,95,55,EA,33,C2,D9,C3), + wwH16(2D,0A,D5,70,FE,20,EF,E3,E5,D4,57,EB,BE,C8,5E,46), + wwH16(03,AE,3B,C5,B6,A7,37,3D,3D,67,1B,7D,81,91,E0,EC), + wwH16(68,4B,EF,36,E0,57,4A,89,CF,02,EB,66,6A,B1,E9,8F), + wwH16(B6,66,3E,76,14,79,67,63,DD,61,4D,12,39,6C,BF,1D), + wwH16(82,9F,94,C9,9E,34,1A,C0,2D,A1,AE,34,89,68,D4,74), + wwH16(1B,33,19,39,6E,E6,BF,07,19,F0,6C,AE,A5,B6,54,20), + wwH16(F9,2D,37,44,FB,B9,62,93,A2,EF,1C,66,E0,78,05,E3), + wwH16(9A,35,EE,D8,28,5C,E3,C9,90,C4,B0,CD,15,C4,AB,37), + wwH16(84,B8,56,EA,91,83,71,1A,24,EB,8E,2F,45,6C,32,B1), + wwH16(F5,A9,BF,C5,46,4B,B0,1D,56,AB,64,4E,56,AD,84,6F), + wwH16(9A,82,97,AE,35,20,12,1F,BE,DA,C3,DD,BE,0C,B3,65), + wwH16(83,F0,31,5D,57,1B,0E,D7,BB,04,7D,37,53,0B,71,A9), + wwH16(9F,89,39,E4,39,79,CF,0E,EF,C6,BB,49,3B,C4,71,A3), + wwH16(5E,3A,E8,82,B6,A9,81,06,95,57,43,67,9F,21,82,E0), + wwH16(51,7A,98,69,C2,E5,96,F1,20,F9,A8,70,10,35,89,ED), + wwH16(45,5F,17,B7,89,EA,A4,66,8C,90,F7,42,E4,6A,2F,2F), + wwH16(8A,89,92,2E,76,7D,F1,82,AE,1C,86,01,AE,70,B1,DF), + wwH16(60,45,43,11,DB,4E,19,7F,59,99,7F,C3,93,19,89,9E), + wwH16(89,A7,77,02,C8,5E,38,E9,0C,1A,A2,75,30,9A,FF,F9), + wwH16(CF,94,C3,EE,C8,9F,5A,B6,A5,A4,6D,A4,92,8A,8E,48), + wwH16(68,51,67,BF,99,F3,58,62,99,1A,93,2D,B8,6E,8F,C8), + wwH16(0E,91,4E,25,5B,8A,32,79,63,BE,EE,BC,C8,62,BE,5A), + wwH16(41,38,52,EB,16,8E,A9,CB,9D,93,F5,ED,74,0F,73,87), + wwH16(1D,8A,68,79,AB,12,FD,15,43,A6,00,40,8A,C8,92,10), + wwH16(95,BB,7F,54,7D,A3,C1,7F,E1,2F,12,80,76,7D,47,A9), + wwH16(83,E0,30,B1,81,5A,20,1D,B0,47,4A,B1,A6,F7,D7,F9), + wwH16(BE,38,CB,9F,52,6C,EF,A5,35,9F,8E,E9,5B,33,FB,CD), + wwH16(6C,45,2F,50,70,7C,BE,45,E9,04,44,DE,E0,CC,F3,25), + wwH16(7A,73,0D,9D,3B,BD,85,F5,5F,45,6E,B8,4E,A0,A5,2D), + wwH16(5E,F1,7B,A3,A8,12,F7,78,64,29,92,CE,C8,6D,02,B9), + wwH16(BF,FE,F5,07,AC,C8,A2,6A,A0,8D,1D,D9,07,EC,F1,B2), + wwH16(5D,01,22,CE,9E,B0,50,E2,F6,81,AB,B9,C9,9B,F3,47), + wwH16(8F,98,B4,2A,EF,1A,86,CC,73,B2,1F,45,AA,9F,DD,F8), + wwH16(3D,25,D0,11,FE,21,A0,F8,68,F0,6E,C4,83,DC,C0,A9), + wwH16(A6,F2,10,8A,22,3A,60,62,C7,7D,44,EE,34,8F,6D,5E), + wwH16(5F,77,8A,62,8B,B7,69,2B,2E,3F,BE,71,A6,77,87,22), + wwH16(FE,C8,B1,B0,67,3B,68,96,78,04,11,94,ED,DF,67,12), + wwH16(B1,F3,41,3E,9B,10,1A,5F,1D,31,1A,EE,49,73,CB,63), + wwH16(22,D5,C0,F6,E4,F9,47,7F,A0,22,56,8C,7F,91,28,6D), + wwH16(D6,F9,92,1A,0F,B2,11,78,BA,8B,04,C8,84,20,E2,42), + wwH16(2C,D4,F5,95,7B,C9,2F,AB,E3,21,C9,2B,7A,A2,DA,90), + wwH16(BF,A0,84,4F,E8,78,AD,FC,15,AE,DA,0D,F0,00,9F,39), + wwH16(10,8D,8F,5B,6C,E5,CE,34,DB,9E,71,96,6F,83,75,7E), + wwH16(C0,95,BD,43,02,A6,AB,2B,89,A1,FA,C3,75,C7,BD,82), + wwH16(CD,B8,83,0F,A3,CA,B1,10,CC,A5,03,D5,FB,13,99,6B), + wwH16(86,A9,EB,48,0F,67,A0,14,79,F5,4C,CE,55,4E,0F,81), + wwH16(DE,E3,F6,5E,1A,54,D7,84,CD,E2,2A,35,71,EA,F3,54), + wwH16(3B,B0,82,D4,F4,33,DE,15,C4,63,15,F5,3D,A3,76,83), + wwH16(F3,3A,6A,9E,3E,4D,4F,C6,9A,B0,31,30,E5,54,B4,19), + wwH16(3A,8C,EA,6D,76,2E,B7,7C,BE,DA,BB,19,87,26,45,AB), + wwH16(EE,9B,E9,A4,43,4F,1D,0F,95,C4,F0,A8,0C,1A,44,92), + wwH16(CF,B0,0E,3B,0C,61,C1,1D,A8,0D,8D,07,3C,EA,73,3E), + wwH16(77,C7,B4,BC,0A,52,AE,DB,0C,64,39,DE,37,52,3B,2C), + wwH16(76,36,00,4A,4F,13,12,A3,72,CE,8C,94,B3,0E,12,02), + wwH16(F1,D4,67,07,E5,A2,59,BE,7E,86,69,38,31,92,BF,CC), + wwH16(1D,66,ED,30,0D,46,3F,D0,89,5A,05,36,5A,9A,97,1A), + wwH16(B0,8A,5F,D8,7B,5F,31,1C,65,0C,DA,E6,4A,22,DE,C9), + wwH16(F3,1D,CB,1D,7B,A1,07,39,3C,09,49,C8,0E,08,37,2A), + wwH16(78,9B,67,BB,17,16,F7,17,92,07,BC,71,D8,7C,1D,55), + wwH16(56,C5,73,4E,AE,79,5A,91,DC,FE,60,FD,BE,E5,66,B9), + wwH16(14,35,F2,6A,6D,43,93,49,05,68,5F,85,78,31,2C,85), + wwH16(1F,63,86,BA,EC,B7,2C,B9,0A,16,CF,7B,2A,EC,C9,78), + wwH16(A5,61,4F,FE,F5,9E,FF,A4,48,60,64,7A,16,44,86,16), + wwH16(2A,21,D1,73,29,47,68,C2,59,90,0D,D0,61,0B,E2,64), + wwH16(C2,46,C7,18,48,42,3B,59,85,DE,38,53,F0,36,4D,D0), + wwH16(86,69,74,A2,52,64,3B,84,05,93,DC,BE,2C,8D,EE,C6), + wwH16(AD,BA,25,20,13,9A,1B,59,B7,06,DD,9F,3A,79,DF,A8), + wwH16(73,2A,E8,49,81,BC,E4,55,38,C1,4C,4F,0A,0A,BF,94), + wwH16(DA,89,FF,C8,83,C0,DE,EC,54,72,80,90,56,CB,C8,F1), + wwH16(61,2F,29,C2,42,98,1D,94,EB,3D,97,3E,00,42,6E,80), + wwH16(47,82,77,29,E0,F6,DB,0B,BB,EE,83,95,59,56,5A,31), + wwH16(E1,DE,9C,44,33,FC,1F,46,53,B2,CC,91,D3,EB,83,59), + wwH16(99,87,E2,AC,40,59,A4,8E,62,58,1D,B1,58,8B,88,F8), + wwH16(71,E2,F3,95,4D,1B,79,EF,0E,C3,37,4F,24,C6,E8,D8), + wwH16(36,68,D7,B0,9F,2D,4F,6F,20,5B,AE,79,27,D2,08,D2), + wwH16(A6,15,AD,56,A5,48,1F,21,00,DC,50,B9,E8,AD,1B,0A), + wwH16(30,07,84,19,55,3C,63,E7,4C,56,74,C5,F7,4B,1A,4B), + wwH16(3C,8C,77,C2,D0,C1,CE,51,6C,76,1A,80,E9,A4,70,B1), + wwH16(EF,14,7A,39,55,C3,9D,B2,14,E1,F6,E4,6F,D7,89,1D), + wwH16(75,3F,9D,F2,85,56,CD,76,C5,79,D5,FB,91,60,19,DA), + wwH16(C5,3F,CF,4F,B1,A1,39,33,D7,40,7B,76,50,A6,B2,E0), + wwH16(7A,57,68,F7,77,6F,E3,DF,6D,64,3C,2F,7E,18,D9,FF), + wwH16(1E,87,9B,4C,81,48,E7,80,F1,BB,67,85,65,57,02,50), + wwH16(36,BB,75,EC,5F,59,39,14,D4,4E,5E,8B,43,C2,B8,26), + wwH16(E0,C1,0F,23,4C,13,03,DE,DA,33,95,56,31,9F,7B,06), + wwH16(66,0E,43,33,B7,AD,42,DF,71,E1,4A,B1,80,CB,CE,65), + wwH16(A8,9A,54,05,9E,51,60,F9,2A,1C,B9,49,FB,81,4A,95), + wwH16(00,D5,B8,81,AB,0C,4D,BB,DA,27,4B,93,7B,11,12,DA), + wwH16(8B,D8,32,E6,77,A6,5D,AB,EC,F2,95,12,2B,1B,87,46), + wwH16(98,C8,C4,12,C4,55,ED,59,13,5E,66,3B,FA,C7,46,36), + wwH16(80,86,E7,AD,A0,84,17,18,2B,EC,24,B5,86,19,31,F5), + wwH16(E8,8F,0F,CC,A0,04,69,64,C8,E7,62,08,3C,96,79,AF), + wwH16(91,61,51,0F,0C,1C,9E,C4,BC,8B,5C,D3,31,8A,72,12), + wwH16(AB,AE,06,EF,48,2E,BD,20,03,77,B7,57,53,2A,73,AB), + wwH16(5C,A7,1F,E0,6F,74,C5,33,D2,63,44,7C,50,10,4F,08), + wwH16(38,E3,E9,56,45,19,06,29,F6,05,9F,98,E7,DC,AF,E1), + wwH16(25,22,C9,8A,A6,65,A6,0F,15,71,4F,13,AD,42,99,58), + wwH16(77,D6,A6,43,CA,D8,2F,A9,82,8D,62,BB,A8,D1,40,85), + wwH16(2F,67,2B,55,7C,C5,3D,AB,99,F3,1F,C2,51,DA,DF,89), + wwH16(22,8D,81,8F,34,7A,60,B7,54,99,97,A5,E3,BB,FF,03), + wwH16(A5,14,7B,21,6F,7F,95,CB,3F,1B,66,C0,00,9E,8F,7D), + wwH16(A5,99,88,4A,33,07,A6,C8,0A,6E,F8,25,8B,7A,64,76), + wwH16(AD,13,E9,4D,60,11,81,DA,9B,C5,9E,0B,3D,72,D7,44), + wwH16(79,2A,B4,F8,20,AA,26,74,5C,BE,D4,AC,82,DE,B0,DB), + wwH16(36,EC,9D,8F,E8,A2,7A,5E,2A,04,09,0C,A0,BA,F9,6B), + wwH16(CD,61,0D,2F,0C,7B,DB,32,F9,1C,32,6B,88,B5,A8,66), + wwH16(C8,47,E2,86,D6,B2,7F,32,B8,4C,95,78,C0,07,63,B4), + wwH16(69,C1,66,8F,5B,DC,DE,9F,64,5C,39,75,98,F7,D2,05), + wwH16(5C,EC,54,96,CA,38,8F,33,A6,4F,49,C4,DA,4F,4E,00), + wwH16(C5,50,55,F5,67,81,EC,20,E1,08,17,EA,79,AD,73,F7), + wwH16(E7,FA,AB,28,2C,A5,31,1E,C0,54,8F,DD,84,8C,F1,CA), + wwH16(CA,1A,6A,90,EA,9C,CB,52,AF,DA,48,FA,F2,47,FB,DE), + wwH16(D8,3A,CA,DF,1E,AF,D4,7D,15,7C,ED,9F,8D,5E,BF,4C), + wwH16(15,ED,5E,9A,F0,CF,45,20,C9,05,1E,29,BD,12,DE,0B), + wwH16(8F,D4,D9,CD,21,FC,29,FB,8A,22,FA,A4,0D,E0,10,B3), + wwH16(AB,2F,43,44,44,51,ED,62,32,16,F1,AC,5E,82,FE,1A), + wwH16(24,13,89,F7,C9,7F,E5,29,2F,CE,FD,7C,D2,57,43,8A), + wwH16(9A,B5,BA,5F,9F,C1,73,8A,F6,8E,C8,C1,FC,47,C4,9B), + wwH16(6B,A5,E7,EA,39,4B,67,A7,2C,4A,96,94,D9,D2,F7,1C), + wwH16(7B,12,BD,A8,32,96,1C,A5,48,F6,F2,7B,D3,95,69,B2), + wwH16(00,E0,5F,F9,0A,0A,33,66,C9,78,1B,8D,7E,81,F1,74), + wwH16(F2,F9,01,F9,C2,35,88,D9,A6,00,11,7E,12,7A,34,38), + wwH16(F7,5E,1C,10,F9,45,6B,01,32,E1,D6,A9,38,17,43,A4), + wwH16(C2,C9,37,10,E5,E9,24,A4,17,C5,AD,DC,96,6E,9A,6C), + wwH16(D2,A4,A0,52,8E,6D,0E,84,3A,2F,63,AD,F3,F7,F7,C0), + wwH16(C6,CF,81,1B,42,09,F0,A8,21,A8,B9,69,35,46,6F,EE), + wwH16(9B,CE,0E,71,2B,F9,A1,2D,3D,E7,76,A6,82,AD,A3,DD), + wwH16(A1,EF,B9,E5,CA,7F,44,09,10,C9,A4,94,72,93,97,2B), + wwH16(74,4D,64,65,20,48,9B,7A,A0,97,AF,F1,96,13,8F,5B), + wwH16(8E,F2,7C,63,74,4F,F9,96,89,AF,B4,CF,CF,85,DD,B5), + wwH16(AB,92,C1,79,08,75,C5,3C,8E,88,B1,64,0E,E2,F9,26), + wwH16(E1,ED,9B,17,C2,4D,14,BC,C6,BC,B1,2F,03,2C,AE,D2), + wwH16(FC,C8,30,1A,B8,55,1C,77,65,35,F3,AD,A4,46,8F,38), + wwH16(D2,B8,0D,EF,D4,AB,41,BA,8B,CD,BC,74,42,49,0D,00), + wwH16(97,FD,47,FE,27,EE,42,F0,C5,91,98,8F,42,06,62,03), + wwH16(04,63,8C,97,6B,BD,2C,09,B4,66,F5,4D,7A,69,3D,28), + wwH16(5C,A5,C8,BB,48,A6,73,86,CD,9D,DC,58,C5,65,56,72), + wwH16(AA,8A,85,EA,8A,BB,E6,F0,5A,88,36,03,F1,CA,52,4B), + wwH16(17,7B,4E,10,4D,76,5F,9C,73,2D,B6,B5,4F,A9,B3,CF), + wwH16(38,57,1B,64,80,2D,08,D8,C0,42,CB,20,3E,32,F6,F4), + wwH16(8A,2E,8F,91,84,2B,8B,6C,09,14,FB,AA,3A,F0,E3,E8), + wwH16(05,9A,C8,9D,05,FC,F3,2D,4A,AC,7F,A3,3A,03,09,6B), + wwH16(86,E3,B8,4D,25,A8,E7,62,B6,09,8D,78,44,CA,82,E6), + wwH16(96,0B,4C,D1,02,FB,07,D1,8C,8A,19,2F,8E,88,A5,ED), + wwH16(22,48,22,25,BE,72,86,25,BD,7B,F0,48,81,86,1F,8B), + wwH16(31,01,ED,29,F0,37,38,95,B5,5D,44,2C,E4,7B,50,A6), + wwH16(C6,C2,30,57,E8,3B,85,6B,9D,0A,15,A2,71,6F,C0,A0), + wwH16(99,D8,40,FD,2A,1A,66,D0,80,59,FC,35,5E,DC,79,75), + wwH16(BF,48,DD,15,47,E1,BF,2A,77,1A,20,8F,F3,B2,52,6F), + wwH16(26,DD,9F,A0,4C,58,82,2B,82,70,C5,E5,70,99,BB,AB), + wwH16(01,48,81,E7,C1,BC,BB,60,B3,F1,58,27,46,C6,9A,90), + wwH16(53,52,E0,42,DB,F7,25,C9,4C,92,73,80,51,4A,EE,43), + wwH16(8C,7E,D9,16,5A,7B,49,98,08,DC,7B,BF,34,A4,C3,39), + wwH16(B2,F8,11,77,D1,9A,BD,05,61,9A,93,EA,AE,3D,57,3F), + wwH16(0F,5F,76,3E,DC,9E,29,5C,2B,9B,57,7F,35,07,C8,23), + wwH16(CE,E1,07,4F,B5,6C,EA,6E,49,25,1A,5D,2D,40,73,B4), + wwH16(8A,5C,46,17,F2,A6,B8,40,35,48,BC,12,85,32,F9,82), + wwH16(97,67,CE,64,75,5B,22,A6,0F,A7,9E,65,85,57,A7,B5), + wwH16(2A,91,77,9A,87,5F,20,D4,00,EF,4D,8F,05,C4,46,FD), + wwH16(67,FD,68,4F,0E,7D,62,B8,26,3E,B9,C3,25,09,CE,74), + wwH16(E6,23,A4,CA,F0,52,8C,DB,D1,A9,D5,2A,9F,48,31,D3), + wwH16(DF,27,6D,93,44,49,B6,BF,A2,12,F7,45,94,FC,1B,CD), + wwH16(76,97,1A,F3,13,8E,85,C1,50,88,F8,0C,06,68,A4,CD), + wwH16(7C,E1,7F,6C,8A,3F,FE,7E,A3,FF,A5,E5,8B,D2,20,F2), + wwH16(5E,3B,44,7C,A7,95,84,03,82,02,2E,65,E3,C5,35,5B), + wwH16(33,53,10,A3,CA,E4,8F,E0,9F,BD,A7,57,FB,F2,95,85), + wwH16(AC,91,18,9B,84,DB,98,44,73,B1,0E,49,E0,A2,4A,51), + wwH16(5B,28,B8,EC,B2,CC,02,04,74,31,CC,48,58,AF,D4,D7), + wwH16(4B,18,7A,9F,B4,4B,56,63,E9,FD,74,6F,52,55,52,7B), + wwH16(F8,EF,DE,45,41,7F,B0,B1,D5,30,00,7E,F9,76,CA,B9), + wwH16(74,53,B6,2F,6F,EE,49,EF,D8,3E,87,EE,52,59,56,6B), + wwH16(26,73,8C,96,A5,DC,29,43,E7,37,93,A4,EB,39,12,DA), + wwH16(26,D8,7B,06,D3,77,5C,A7,D8,ED,AF,8C,6E,20,D7,E2), + wwH16(B4,24,43,4B,2C,BE,FF,9A,A1,03,75,27,E1,0E,D1,20), + wwH16(19,A7,CD,1C,E7,CF,9E,1F,53,D7,C6,5C,36,7E,1A,42), + wwH16(11,07,63,B4,B1,AB,90,61,11,EB,F1,9E,DF,BE,D8,4A), + wwH16(C8,73,E1,5A,5C,4C,D1,49,9D,71,1A,62,0A,AF,D3,CB), + wwH16(BE,B6,4B,15,40,41,14,07,BD,4F,22,BC,B3,A1,F1,68), + wwH16(70,A7,91,C5,2F,A2,AB,92,24,45,1B,21,9A,19,C0,75), + wwH16(7C,87,07,23,4D,23,BF,A3,F2,D7,D4,DE,F9,B8,D5,06), + wwH16(BF,64,5A,4C,4C,1A,01,A5,96,5A,BA,50,38,F5,42,28), + wwH16(E9,99,14,D8,9E,5D,54,BD,0C,55,AF,E4,74,BB,A7,4A), + wwH16(45,E8,48,54,54,D6,D4,B3,A3,4B,0D,EC,1C,4A,15,07), + wwH16(81,85,6D,D3,5F,09,DD,29,73,C0,15,36,12,65,61,EC), + wwH16(9F,36,40,BC,1E,41,59,5E,98,CC,FF,63,AD,61,E6,03), + wwH16(AE,01,A2,1A,B2,10,03,05,33,78,CB,80,18,63,9B,EF), + wwH16(39,0C,89,95,6E,A3,E3,F3,F9,7F,EC,D3,51,F9,7F,5B), + wwH16(D8,03,AD,BB,4B,56,80,03,1B,81,89,E1,EA,D3,A2,7C), + wwH16(5E,9D,35,BE,9C,68,80,B8,E0,98,67,6F,C7,D3,C3,43), + wwH16(30,38,32,BC,34,36,9E,2B,D5,D9,11,39,66,CF,8D,27), + wwH16(8F,4B,D4,D8,04,A2,CE,2F,C6,77,F0,18,63,5C,9C,B8), + wwH16(C5,92,85,15,6D,F6,F9,58,D8,52,1C,A5,B3,FE,CE,AA), + wwH16(21,37,30,44,78,67,0F,94,0E,DD,4E,82,15,59,94,76), + wwH16(C1,BB,47,69,44,D8,FB,A9,DE,2B,EC,FE,65,34,5E,16), + wwH16(8D,FE,B2,E3,03,D9,7E,EF,7B,04,2A,98,DA,DA,31,47), + wwH16(C9,76,B3,47,A9,01,16,15,E5,D3,A5,39,A7,18,9D,09), + wwH16(A9,AD,D5,9A,85,46,D2,9B,B9,81,6F,1E,7F,D4,FB,6E), + wwH16(ED,A0,21,F6,F2,62,81,EA,15,A9,1E,B7,AC,5D,A1,D8), + wwH16(15,EC,DB,78,18,43,2E,56,3F,CE,07,A9,20,E7,DD,3F), + wwH16(86,B5,36,4E,0A,0A,FD,7A,2D,EA,88,01,27,E6,AD,7A), + wwH16(E5,0C,C3,16,C6,1D,80,BB,5C,6C,66,0B,6F,4F,91,DA), + wwH16(62,25,6E,6E,A6,36,F6,87,DB,11,C6,32,19,A8,73,31), + wwH16(FD,9C,F5,11,F5,4D,B4,4C,FE,D6,38,27,33,63,77,E9), + wwH16(20,96,0E,FD,58,D6,3C,D8,D5,9D,B9,6A,D9,0C,A7,CC), + wwH16(7D,C1,82,8C,3A,3B,54,E6,16,D2,E8,90,E3,4F,22,F9), + wwH16(2C,CC,F1,19,40,40,85,39,37,81,2A,78,D1,6A,9A,C9), + wwH16(34,CE,40,7C,91,E8,62,DC,F2,1A,C7,33,87,13,19,E1), + wwH16(56,AF,66,45,8F,59,F7,7C,2D,3B,B3,F0,E1,93,D5,24), + wwH16(F4,A8,EA,98,BC,9A,CA,BC,2E,DF,41,4A,74,FE,71,A2), + wwH16(2E,67,68,05,C2,25,92,29,E8,B3,1C,14,75,83,5B,AC), + wwH16(8E,42,A6,5B,05,EF,08,82,60,F2,9C,F2,23,3A,B7,E5), + wwH16(BF,1A,6C,48,D0,85,64,E5,5A,3D,70,6E,04,2F,94,F7), + wwH16(93,5B,08,48,68,05,B3,96,87,4B,4A,30,A1,C3,26,6D), + wwH16(52,DF,48,97,22,0C,29,36,FB,ED,8B,71,15,D0,BB,09), + wwH16(95,D0,E4,6D,EE,E9,94,91,D1,14,5E,65,35,72,92,59), + wwH16(7C,94,30,82,B7,87,41,34,56,9C,70,99,AB,09,1E,C7), + wwH16(03,EE,60,19,4C,88,85,10,F2,7F,0E,37,42,0E,F6,03), + wwH16(86,88,F1,B1,E9,4B,3A,42,F8,43,53,97,47,A6,6A,54), + wwH16(3C,09,52,97,98,62,6A,FD,7F,74,1E,8C,5F,26,A3,AC), + wwH16(22,AC,68,FE,56,78,7C,18,EF,24,CD,F3,98,48,32,39), + wwH16(5D,42,A3,CA,F4,10,21,F7,E5,A3,D7,9B,3C,28,E7,45), + wwH16(A6,BB,F1,54,CF,D1,0B,29,4B,11,EC,18,F3,DB,DE,BC), + wwH16(30,3C,BC,24,45,FC,C6,C4,31,E9,98,CF,51,48,60,02), + wwH16(AA,E1,FD,9F,8E,46,1A,77,D3,ED,07,C8,7C,A7,65,68), + wwH16(44,AE,62,B0,D7,A7,F7,00,9C,72,E1,36,A4,21,75,70), + wwH16(35,A6,38,1A,4B,2B,44,72,A2,45,73,01,12,2B,77,9D), + wwH16(1B,76,7E,BD,53,61,CB,B6,A4,55,2A,C3,20,23,F3,72), + wwH16(64,A7,9C,4D,BC,4D,0A,F5,E4,0E,5E,E4,26,20,38,D4), + wwH16(7D,35,AD,6C,32,AE,25,56,6E,26,1E,B7,1B,A5,15,D6), + wwH16(B7,E3,39,22,D0,67,BC,6D,C5,A9,B1,70,C1,B6,EA,00), + wwH16(10,A0,C3,BC,A4,9E,E5,E3,02,00,8A,4C,35,83,44,B1), + wwH16(41,C7,60,A6,A9,D8,C2,05,7F,0C,2C,87,C2,0A,DA,E3), + wwH16(83,6E,83,62,A7,7E,4F,BF,AC,0F,36,EE,8C,92,50,19), + wwH16(7D,9D,5A,3D,D8,5F,A3,15,76,83,39,10,E1,58,3E,4D), + wwH16(7C,A5,71,96,BF,F1,1E,CF,7D,BC,E6,EF,82,DC,DA,5C), + wwH16(BF,39,E1,E4,FC,EB,26,7B,CF,88,91,6D,DD,9C,86,95), + wwH16(CB,96,16,0B,62,78,3D,A5,6A,51,5F,28,3A,2D,B4,83), + wwH16(1B,CD,03,17,1F,42,E1,42,44,D1,42,80,32,D7,AB,73), + wwH16(E1,A0,15,B9,E0,BF,CC,74,75,61,E5,D6,7A,97,D9,5D), + wwH16(E1,F0,80,0F,23,B6,03,AB,B9,8D,37,A0,89,8F,51,A5), + wwH16(29,D9,9E,F5,9D,EC,AE,49,95,DE,D9,57,C7,75,9F,6F), + wwH16(2A,6D,11,7F,1A,69,2A,B3,13,BC,F8,BB,52,AC,76,C5), + wwH16(BB,42,1D,14,0E,23,98,19,05,40,07,52,5C,D3,AC,0F), + wwH16(55,0D,02,45,6D,66,2B,5B,9C,AC,F7,97,71,D3,3E,34), + wwH16(96,13,0E,3E,F7,71,FD,84,7C,1C,A2,F2,49,87,C1,72), + wwH16(6C,9A,70,D2,41,19,B2,C0,4E,2E,E8,F8,A3,F7,C4,24), + wwH16(2A,E3,70,5C,7C,93,19,37,CD,E4,BB,AF,3A,51,71,23), + wwH16(2A,4F,F3,11,1D,82,85,06,31,2F,9B,2D,5D,9B,CB,72), + wwH16(1F,BE,C2,33,18,1A,87,5B,0B,1B,0E,69,0C,13,16,B5), + wwH16(EB,5A,7C,F0,14,ED,FD,16,D6,60,DE,10,B5,34,9F,85), + wwH16(E4,DF,9E,42,E7,9A,B0,1A,90,1A,28,8E,67,04,42,9B), + wwH16(ED,3B,B6,DA,8C,04,EA,95,1E,7B,85,EB,C7,EE,1A,95), + wwH16(1A,C4,1A,EE,9C,FC,72,CD,FC,7B,70,A0,4A,B9,34,15), + wwH16(43,0E,1C,58,9A,81,15,AA,C7,5E,6B,E2,AF,8A,A5,71), + wwH16(73,02,B0,C6,52,5F,8F,F1,C9,AE,44,FC,A6,C2,53,98), + wwH16(C7,B9,50,7C,5E,CD,44,BE,C1,E9,3D,58,18,FD,AD,68), + wwH16(00,7A,BD,40,5E,B3,B9,9C,66,A6,18,5C,63,BB,39,DF), + wwH16(6E,11,CB,E6,C2,A7,F8,B4,EF,3C,E6,8D,8C,49,AC,6E), + wwH16(46,F9,23,8F,40,DB,28,57,30,5B,50,31,F4,A4,B5,E4), + wwH16(F5,04,E5,4D,FB,0D,FC,41,44,C4,0F,B5,8A,7E,0A,DA), + wwH16(9D,C0,46,11,20,68,E9,67,80,5B,CA,5F,03,C6,5B,96), + wwH16(30,29,91,89,D4,65,6A,09,02,A5,0A,0E,52,FA,E6,2F), + wwH16(46,F6,72,80,05,45,5E,09,8E,D1,B5,57,B0,7B,0F,21), + wwH16(5E,34,9D,92,FD,CF,15,47,91,8B,6E,A1,7E,56,7A,27), + wwH16(07,24,24,05,23,8C,F6,55,12,22,CE,F2,2E,16,06,F5), + wwH16(67,AA,85,C8,E9,26,DA,04,D0,54,AA,F0,B8,A3,2A,93), + wwH16(1E,CC,13,A1,13,32,6E,09,69,01,F5,9B,CE,48,F2,2A), + wwH16(53,83,41,AC,12,52,1D,AE,52,D1,47,E1,98,15,C4,E2), + wwH16(D8,13,B1,A8,08,EC,C0,1D,82,2F,0F,42,11,9F,9B,96), + wwH16(EA,32,73,D5,E0,09,39,54,24,E7,DE,50,80,43,8F,00), + wwH16(9B,89,43,79,28,BA,56,24,F1,8F,1E,C0,92,B2,60,26), + wwH16(F8,41,CA,17,DA,B7,0E,34,BC,C5,07,30,38,39,67,58), + wwH16(74,52,3A,5B,A1,88,99,F0,1C,E3,F7,F2,A1,DD,BD,5E), + wwH16(70,B6,BF,37,59,BA,E7,39,D5,74,6D,E6,15,C4,35,C7), + wwH16(14,14,25,E8,11,F0,12,37,E3,FE,40,45,09,3F,D5,8A), + wwH16(2F,E7,35,04,12,C4,76,89,E1,79,FD,FC,19,C6,52,F0), + wwH16(B9,6E,33,D1,52,BF,D4,4F,9C,47,95,B3,27,89,9F,3A), + wwH16(39,00,03,06,78,E3,C7,A2,1F,B3,B0,9B,5F,74,63,7C), + wwH16(C3,C8,83,21,14,77,67,12,23,58,C0,D0,09,28,4F,8B), + wwH16(43,FC,46,3F,D4,84,C8,CC,65,5F,1D,05,3A,E4,D3,C9), + wwH16(E6,47,28,EC,CD,1A,23,7A,29,75,B4,BF,E8,22,F9,28), + wwH16(1C,A6,C1,C1,BC,A1,C5,14,F0,B3,6B,BA,1B,E9,13,59), + wwH16(BC,42,01,0A,53,9D,A0,D0,38,E9,70,A8,4D,14,6B,F2), + wwH16(8E,64,A0,DC,04,CF,5D,89,1B,F3,1A,E0,DC,D2,77,E1), + wwH16(37,15,B0,83,74,8A,9B,03,58,C5,7B,91,ED,5D,CC,83), + wwH16(5E,14,EF,8F,97,48,BA,94,86,1D,82,86,AF,59,FC,82), + wwH16(00,36,C4,CF,3B,ED,33,DB,47,DB,69,B2,A4,65,C3,DF), + wwH16(14,AF,99,17,A3,6F,81,EE,B5,C7,09,D0,18,63,97,7D), + wwH16(A0,D1,58,A9,07,89,E4,07,82,8E,9E,EE,C9,5A,92,0C), + wwH16(12,A1,65,4F,8A,6B,8F,38,1D,BC,92,4F,12,A6,67,D6), + wwH16(FB,06,D0,EF,77,64,46,A1,34,E5,39,3B,6C,E3,10,DC), + wwH16(B9,71,F5,D8,25,0B,15,27,42,96,C0,88,8A,2C,F5,35), + wwH16(D1,EA,92,A7,26,2F,36,68,B7,31,41,8C,97,16,CB,1E), + wwH16(2F,58,08,19,96,DE,7F,DF,95,2E,3C,7B,52,72,23,3E), + wwH16(68,20,7E,31,19,08,A8,F8,8F,CF,89,1D,CB,E6,F7,37), + wwH16(D6,26,94,78,80,98,E9,48,E1,84,E3,F3,4A,40,C8,57), + wwH16(FA,9D,1D,15,74,1C,20,9B,75,FF,E4,5F,68,7D,49,76), + wwH16(8C,1A,4A,07,6D,22,B0,B6,45,F2,0C,6C,75,F9,D3,B3), + wwH16(AA,04,6A,08,C9,F3,9F,44,94,AF,23,69,9B,F4,BE,0A), + wwH16(4C,BE,B1,DC,E6,82,FF,B5,5B,89,77,B7,62,95,57,F1), + wwH16(67,2A,E3,9C,F2,FD,AF,E4,A2,75,11,EF,3B,1A,E7,1C), + wwH16(C4,37,B9,BE,75,5E,FD,2D,08,59,E3,2F,AA,BF,E0,95), + wwH16(6C,16,C3,61,E2,2D,60,43,40,5C,AE,D6,7B,C6,22,61), + wwH16(8E,E6,A5,79,12,26,0E,7E,4A,56,55,FE,F5,66,D8,77), + wwH16(A0,54,AA,9E,5C,38,56,47,96,A9,A4,08,8D,ED,85,B0), + wwH16(1B,77,25,ED,89,13,FC,18,20,66,D3,A3,D7,02,CC,55), + wwH16(3E,5B,FE,CF,1B,E3,F6,42,2D,8B,4B,19,55,C1,67,D3), + wwH16(05,50,27,27,C5,D7,8D,0F,19,2F,C9,EA,DF,B7,06,E3), + wwH16(3F,D9,C6,24,8C,70,51,4C,AE,9B,3D,B8,E9,8A,4C,17), + wwH16(F7,3A,37,57,11,15,F8,E5,0C,C2,36,4F,39,7F,F8,FF), + wwH16(B0,3F,BC,95,72,BC,E6,46,63,6F,38,F9,DD,8D,B5,9D), + wwH16(B7,73,CC,94,FA,63,81,CF,5D,9E,0B,61,24,5C,24,55), + wwH16(95,1F,26,7D,D9,7B,BD,F7,AD,7A,27,FB,98,73,BA,CD), + wwH16(5B,EC,B7,72,29,5C,52,39,05,49,28,F1,5D,58,BD,6B), + wwH16(ED,2A,75,A0,FC,FD,D9,63,9E,56,07,8C,D3,67,E7,77), + wwH16(A8,6A,76,5D,C7,F5,CD,AE,E2,90,19,C4,2D,24,91,DB), + wwH16(31,5A,D9,35,D9,9C,7A,F8,C9,5D,F2,CA,13,45,BD,E1), + wwH16(49,0A,97,44,E9,EC,B0,5C,DA,77,17,CA,6F,45,4C,B0), + wwH16(43,BC,77,9B,0C,CF,79,03,68,66,FD,52,18,6D,3C,A1), + wwH16(65,97,ED,DB,28,D9,57,00,47,5D,3C,90,0F,2B,4D,C8), + wwH16(59,04,4D,0C,B3,91,92,34,70,9E,2D,A5,7A,DF,10,EF), + wwH16(C2,87,1C,FE,F9,1B,72,53,9E,07,47,3C,A2,5A,19,FD), + wwH16(DB,AE,B5,1B,15,D6,22,EB,31,4B,EF,98,DE,F4,55,C7), + wwH16(5A,13,64,29,5C,B9,C4,1D,FD,97,D1,BC,0F,02,AB,B9), + wwH16(D9,D5,11,D3,5A,1F,7F,61,3B,4F,5E,C6,F4,86,79,2E), + wwH16(05,E9,37,80,A0,6A,3E,AA,2A,EE,39,1C,07,33,AA,64), + wwH16(69,5D,67,98,A1,6C,F5,70,9F,58,87,36,91,90,15,02), + wwH16(D7,E8,E6,65,B0,FC,B5,8D,AB,3D,8E,2F,1C,59,24,2B), + wwH16(C7,B3,35,3C,68,55,18,AF,09,15,0C,F3,3B,F7,C1,10), + wwH16(78,32,50,F3,63,0D,57,DA,78,2B,19,14,91,69,54,50), + wwH16(D4,58,C0,0A,31,4A,1D,F9,3C,CA,9A,7B,00,43,F2,A3), + wwH16(25,0A,6B,0A,7B,4A,3D,F3,58,48,57,41,EA,78,71,9D), + wwH16(71,0E,F4,4F,C4,6E,0E,41,3F,D3,F4,36,FE,0B,CC,0F), + wwH16(AE,C1,96,EE,35,DB,0F,4F,8A,B4,7E,A1,35,F1,70,D6), + wwH16(32,F6,30,FE,76,7A,78,E0,13,D7,FB,B2,D3,2A,CF,48), + wwH16(1C,11,AF,3A,2E,FD,88,52,19,A9,B4,23,6B,AE,63,48), + wwH16(D7,D5,19,2B,C8,E2,F9,9C,F3,E5,76,33,66,7F,B8,BA), + wwH16(AC,7D,8D,5F,7F,15,AD,15,85,93,47,C2,D4,07,05,2B), + wwH16(43,6A,B9,E9,65,73,7B,E3,8F,F1,9C,10,38,6E,C0,6F), + wwH16(A0,2D,72,F0,57,9A,14,1F,B7,ED,B0,17,DD,D5,90,99), + wwH16(62,A8,DB,47,71,47,DD,22,1D,55,F0,68,A4,6E,E6,47), + wwH16(AB,FD,8F,61,49,B0,CB,91,A2,18,1C,78,D7,6A,70,75), + wwH16(A3,CF,17,C2,16,69,F8,CD,E9,0A,A4,FB,F4,F5,A2,66), + wwH16(31,50,48,87,3A,8D,9C,F9,D4,91,A9,F9,6F,C2,CA,DB), + wwH16(98,9F,49,67,41,CD,B7,4A,2F,2E,B8,2C,19,99,6D,CF), + wwH16(EE,94,50,C1,15,FB,14,BC,D9,19,9A,4F,29,28,1F,42), + wwH16(B8,A2,7C,96,58,A9,15,1C,14,D7,71,C3,1F,53,BD,54), + wwH16(B4,7D,FE,43,59,DF,63,CB,10,10,C8,8D,AF,C1,FA,63), + wwH16(61,DD,FE,79,40,20,85,50,42,5E,AF,0F,3F,96,98,83), + wwH16(35,66,76,3B,15,BB,8B,6B,A8,CA,26,38,34,0B,A1,F2), + wwH16(D7,7D,9D,9B,07,DB,1D,FB,B1,F7,38,7B,51,C6,A7,A3), + wwH16(F0,FA,BD,F9,AD,7F,7A,DC,89,99,33,F7,90,7B,A5,6B), + wwH16(29,6D,9A,F4,85,3D,B0,FF,73,96,CB,F5,E4,10,EA,46), + wwH16(9E,A2,08,32,9B,3D,54,29,4C,BD,84,AB,BC,D2,64,6A), + wwH16(9B,C1,1C,69,29,3F,A9,9B,52,F9,E3,25,CD,FB,43,3C), + wwH16(CB,15,FC,1F,59,38,C7,6F,7A,39,BC,C8,10,FF,1C,1E), + wwH16(94,B4,98,41,44,78,EE,13,CE,85,F1,BF,CE,3C,D9,FB), + wwH16(6F,5E,42,7D,1E,95,B2,73,F4,63,85,12,F5,0E,EA,3F), + wwH16(E3,69,03,1B,E2,8B,B4,49,87,CC,25,F1,86,A9,D3,15), + wwH16(C0,66,9D,2F,CB,02,74,E3,A5,88,79,B8,47,DE,51,E6), + wwH16(9D,04,66,1E,C8,CF,40,47,FE,1E,AE,41,CC,3C,01,5A), + wwH16(61,BD,2F,47,C9,6D,84,E9,49,AF,9E,5B,54,42,6B,D4), + wwH16(95,BE,42,78,30,7B,C7,25,8C,D1,1A,FA,2B,DF,B5,55), + wwH16(92,C1,54,BA,E3,7B,24,98,C8,7D,D2,FB,9D,1F,48,C3), + wwH16(68,C4,73,D0,DF,34,8D,FC,C8,BD,D9,48,4D,15,1C,F3), + wwH16(62,70,B1,60,B2,14,D7,18,A7,88,69,DD,6E,B4,43,8E), + wwH16(4A,95,8F,16,74,CF,A1,B3,0A,4D,C1,FE,D8,E1,9A,BB), + wwH16(AC,60,52,4A,B9,02,06,D3,10,DB,05,E5,A9,25,C5,EC), + wwH16(C2,40,9F,ED,C4,C1,68,92,99,A8,8D,3B,0A,82,99,67), + wwH16(70,DE,6D,8A,41,68,C7,DB,B2,0B,B7,A2,80,36,3E,7F), + wwH16(87,E7,70,C2,CA,0F,0C,D1,EE,99,9F,7D,12,CE,1F,E4), + wwH16(B4,25,A3,CF,74,43,D1,76,59,4D,69,55,FE,A0,32,29), + wwH16(88,A2,0E,01,D5,72,C0,39,6E,09,59,D5,58,15,11,D9), + wwH16(FF,45,74,9D,FB,CC,29,75,B1,9D,F0,3A,73,1D,52,69), + wwH16(E7,A3,83,FA,47,DF,5C,81,57,70,5A,50,F5,FB,D9,2B), + wwH16(96,EF,22,12,5D,7B,AA,F0,BF,CB,46,47,C3,59,07,58), + wwH16(2D,64,35,6A,05,F5,4A,EF,02,7D,1F,50,B5,04,AF,0E), + wwH16(13,9C,96,9D,7C,88,26,A8,12,B1,19,E4,85,18,0A,C8), + wwH16(62,54,56,2E,1A,CB,4B,A7,F5,34,C9,B8,C3,A0,25,0D), + wwH16(F9,22,97,C1,38,F1,83,77,B4,A4,38,1E,CA,DF,48,2A), + wwH16(46,B8,B1,31,68,B2,92,21,76,FC,EA,30,15,B1,3F,26), + wwH16(C4,59,70,6D,58,FF,A5,D5,23,BC,E3,8E,A0,58,08,5E), + wwH16(D5,7E,89,D3,40,4C,EF,CD,9C,C1,92,42,69,26,B0,7C), + wwH16(19,33,30,B3,2F,92,B9,C3,61,4C,25,44,AB,98,42,99), + wwH16(5F,94,8F,51,8A,9C,D0,E7,EE,B1,A8,94,B5,F2,86,34), + wwH16(A4,B7,F8,02,2A,EA,DE,0F,72,C2,B1,98,5A,31,10,24), + wwH16(B6,DB,A8,F3,F1,69,01,4F,4B,DF,D4,83,84,4D,1F,E1), + wwH16(BA,23,10,60,08,D2,A1,A6,F1,D8,21,84,97,E0,69,51), + wwH16(55,7D,89,84,B4,D6,52,02,FE,1B,5C,2B,A0,5E,30,A5), + wwH16(B3,52,AA,67,E0,8B,3E,A5,31,D5,4B,53,21,D1,45,7A), + wwH16(F0,0F,36,DF,13,14,CD,52,8D,3E,A4,D5,2A,2E,31,2C), + wwH16(54,BE,ED,45,68,06,5B,CA,93,E0,58,33,27,BD,C0,60), + wwH16(F6,CB,41,E7,DD,FB,41,06,BC,A5,98,8E,F0,BC,AF,D9), + wwH16(ED,34,29,45,74,43,64,4C,96,B5,9C,88,FF,CF,96,AA), + wwH16(42,1C,43,76,D0,44,E5,ED,3B,1B,58,D5,90,D3,41,2B), + wwH16(24,35,2B,5E,8C,74,7F,0E,24,A9,6F,BC,00,F8,CF,46), + wwH16(0D,8F,E1,B9,46,49,14,45,7B,72,32,7C,B6,1F,65,0E), + wwH16(02,E6,57,9B,16,A0,E9,42,74,4C,65,57,05,22,95,5D), + wwH16(D9,43,C1,6B,BC,C4,11,5B,96,26,1F,FB,94,44,00,5D), + wwH16(0B,AD,13,01,1A,47,EE,4B,21,C6,14,C6,B8,29,D8,45), + wwH16(DE,2C,B5,3A,C0,C3,DD,62,5E,AD,67,DE,02,17,58,65), + wwH16(CA,C3,62,AD,55,0F,08,35,41,98,FE,2F,DB,AA,C7,3D), + wwH16(1C,1E,72,91,CD,E8,26,4A,07,7E,B2,85,D3,F3,F5,46), + wwH16(9F,BF,98,01,2C,32,48,46,EA,7D,EE,99,37,58,25,B0), + wwH16(92,FF,ED,BA,3E,A6,37,2F,93,68,49,2A,84,18,86,FA), + wwH16(5A,61,E3,DD,1E,5E,70,72,83,A2,A9,E9,C9,CA,E6,DB), + wwH16(16,F0,BA,84,9B,75,B5,3A,C0,DE,10,17,51,2C,18,A2), + wwH16(C6,27,FB,51,B7,A1,66,7B,41,18,16,AA,89,D1,98,07), + wwH16(BB,8B,B3,6B,CA,6D,90,A3,1F,83,04,B3,D2,C8,07,73), + wwH16(B4,08,7A,89,35,AC,FC,F1,24,35,D4,48,E4,63,CF,9E), + wwH16(1F,6B,F7,2F,3E,A6,66,40,83,59,D2,A8,78,30,57,EB), + wwH16(C7,D4,98,AC,BE,3F,18,15,61,D8,4C,BD,10,74,4B,AD), + wwH16(26,0A,CD,B7,BC,06,8C,84,68,22,15,38,87,9E,2F,88), + wwH16(5A,30,2A,F7,45,F3,BC,CC,10,18,C6,00,B5,B7,78,A6), + wwH16(41,AE,A5,42,9E,AD,8B,8B,E1,31,A5,63,1D,45,ED,C7), + wwH16(36,58,A0,12,C0,3A,5F,74,55,70,3A,55,0E,F5,73,74), + wwH16(3B,BC,B0,1A,A4,16,CD,8C,A1,6B,4D,2C,4E,CC,59,52), + wwH16(0E,D9,69,6F,EB,D1,61,62,6F,6D,1D,44,16,7A,3F,54), + wwH16(AE,73,60,C4,6F,CD,45,45,54,CD,EE,34,E7,67,DF,6F), + wwH16(20,10,EE,67,D6,D0,5D,19,83,8C,D9,94,E7,65,65,07), + wwH16(06,DB,D6,ED,B4,6B,85,77,EC,57,AB,A5,E7,65,14,20), + wwH16(AD,10,CD,DF,7E,C2,5E,2B,18,8B,9D,A5,AA,C4,1C,3F), + wwH16(4E,ED,FC,59,53,2A,22,5B,2E,DE,23,61,34,19,E7,4E), + wwH16(9B,37,1D,7A,19,A9,F1,09,06,8A,7D,78,65,B9,F8,46), + wwH16(65,57,1A,9C,CA,C4,34,D4,4F,AB,F3,EA,91,07,51,0F), + wwH16(06,D3,C8,7F,48,51,41,45,9F,87,62,2E,F9,C3,33,49), + wwH16(8F,20,2B,E3,D8,38,3A,3C,66,A7,E4,A6,F2,F7,83,C3), + wwH16(22,8F,FE,0B,D3,7F,E9,98,48,BE,9D,0F,73,F5,45,13), + wwH16(03,9F,26,10,94,37,9E,E2,9A,A3,52,A7,98,12,5B,AF), + wwH16(74,39,C7,A0,52,65,0D,ED,1F,43,E8,B4,46,12,49,8D), + wwH16(2E,E3,99,67,B0,AB,FD,92,60,73,1A,95,1C,72,5C,1F), + wwH16(A7,B6,0C,74,BB,FC,EA,FC,F8,E6,1F,60,FA,3C,54,8F), + wwH16(11,DB,3B,8D,E8,51,D1,71,0B,4F,A4,87,1E,AA,63,4E), + wwH16(99,69,99,D8,37,38,6D,4F,97,7D,11,DB,D0,7E,2B,B5), + wwH16(4B,55,6B,67,84,F0,80,F9,11,8A,C5,FC,95,EF,26,7C), + wwH16(98,4A,22,47,D3,70,62,DE,7B,4C,B5,C3,35,FA,7E,16), + wwH16(FC,41,60,B7,3C,B5,06,39,40,70,4C,8D,62,B6,06,9E), + wwH16(A3,39,8B,C2,A2,2F,47,5F,E0,FD,F1,AF,25,70,8A,72), + wwH16(EA,0D,9E,00,33,1F,D2,E0,A3,F4,A4,5F,A1,23,FA,52), + wwH16(48,49,DF,2D,DF,03,CE,5A,C3,1B,93,94,D2,F3,78,2D), + wwH16(63,88,DA,99,1C,A0,0D,51,34,B3,D5,55,8B,86,90,3E), + wwH16(E5,5F,A1,8C,BA,44,AB,02,E2,27,8F,0A,12,FF,C5,2F), + wwH16(3E,1D,B4,DE,2D,2E,19,FE,B1,E0,30,C0,EF,77,22,22), + wwH16(69,F8,F2,0D,1B,7D,DD,F1,0E,BD,DF,2F,16,9C,20,9C), + wwH16(36,E1,DC,E8,95,CA,9C,5F,A3,6D,0A,DA,CE,E3,A8,C7), + wwH16(16,2A,4F,FA,DE,BF,F2,32,E6,E5,C0,79,31,24,B7,4E), + wwH16(C3,01,90,6D,6B,4A,BD,C0,D3,8F,29,44,A5,CB,BF,12), + wwH16(AF,AF,8D,F1,93,1E,51,BA,93,7E,3D,2B,9C,A5,60,08), + wwH16(76,CD,6F,8B,47,8F,9B,56,C9,6B,F3,DA,DF,E9,46,62), + wwH16(42,36,B5,97,13,2A,1C,10,E0,9D,44,C2,14,02,C3,36), + wwH16(7C,EB,19,69,A5,06,52,9B,B9,84,5A,67,13,93,35,DC), + wwH16(8D,4C,91,40,2C,D6,D5,13,27,E5,D0,50,35,4F,92,E3), + wwH16(14,88,F2,93,9D,D3,69,A0,BD,7B,A4,DE,6F,8A,28,FF), + wwH16(10,A6,DD,F6,1C,21,BA,73,E3,7E,0D,8E,89,20,82,74), + wwH16(D8,9F,05,F0,FA,2F,5F,44,9D,59,02,02,4B,97,2A,D9), + wwH16(32,AC,A6,0E,4A,BE,D7,1E,1B,D2,A8,DF,C0,B5,62,FF), + wwH16(1F,7E,1D,15,B5,A0,FA,37,D5,48,8A,FF,76,CB,1E,98), + wwH16(8A,8B,9A,23,B5,13,1F,0B,F0,8D,E2,5D,C9,94,35,AE), + wwH16(DF,94,E3,1B,35,15,77,C3,71,7E,57,F6,0C,FA,55,D0), + wwH16(75,D4,A3,CC,44,BD,13,23,A1,8E,B9,D6,EC,0D,43,B3), + wwH16(01,D4,81,C8,A5,42,71,1E,4C,F1,29,50,A7,DA,4A,22), + wwH16(2A,D4,DC,33,72,5B,5A,5D,68,F9,C8,46,3A,A0,D2,D6), + wwH16(CE,9C,E7,17,A6,B3,FF,B9,DA,07,F6,B5,2F,C3,FA,12), + wwH16(6B,30,63,8E,E2,33,47,AA,23,BA,91,39,FE,86,0F,6E), + wwH16(71,44,C8,E3,24,7B,9E,D7,82,D2,60,6D,9C,D7,7D,E9), + wwH16(4C,B1,4B,DD,5A,A7,52,7A,C8,4C,80,99,45,8F,3B,53), + wwH16(6F,C7,98,34,A4,E8,22,31,20,17,BD,8C,8E,B6,A9,1B), + wwH16(C4,B9,65,D6,02,D1,BF,80,65,B2,D9,06,D8,0B,38,5B), + wwH16(3F,56,09,59,72,2E,14,AB,69,C1,03,72,F3,43,9E,71), + wwH16(6F,5A,FE,28,72,55,B8,70,08,92,20,5A,03,A2,B5,02), + wwH16(A8,EC,91,38,28,A8,0B,95,24,95,E3,DE,06,6F,C9,54), + wwH16(04,DD,BB,A1,1D,FD,D5,40,E2,30,35,AD,23,C9,A9,69), + wwH16(4D,EA,95,47,66,BB,8C,F8,1B,35,D4,D7,34,D2,90,AD), + wwH16(7A,D8,E5,AE,9E,98,CB,D2,64,8A,59,BA,9B,C2,08,B5), + wwH16(12,C2,68,34,45,6F,98,6E,A8,31,EA,00,0A,48,97,56), + wwH16(01,6E,38,EB,54,74,8C,1A,25,CA,5F,45,27,16,D6,B4), + wwH16(45,84,75,BA,4F,CD,2D,3C,1E,1B,81,14,1A,E3,2F,25), + wwH16(C3,FF,3E,E2,3E,52,8D,4C,17,6A,9C,8D,20,58,3F,46), + wwH16(F2,7F,A5,1F,A9,6B,92,9F,E5,D8,1C,85,A3,60,0C,AC), + wwH16(4C,44,B4,5D,C3,9D,D2,BD,0F,43,F2,D2,CF,1B,BA,28), + wwH16(86,38,12,C6,09,67,B5,25,D1,C0,63,CF,29,39,06,FB), + wwH16(3B,66,0B,57,CC,7B,A6,9C,FD,5E,25,67,61,1C,61,F4), + wwH16(A0,D2,F4,8B,3C,49,EB,0E,D5,4E,10,5C,01,80,C7,46), + wwH16(D8,FD,26,09,F6,0A,7B,17,9E,B4,A5,91,FD,3E,8E,4D), + wwH16(B6,84,BF,1F,49,81,7B,0D,6F,49,40,3D,55,48,E3,DC), + wwH16(40,4B,15,86,64,67,98,4B,FF,50,51,65,6B,D9,4D,16), + wwH16(69,98,ED,62,AF,81,6C,EC,66,18,40,78,84,46,15,0C), + wwH16(43,43,E4,CF,DC,A7,43,0F,19,52,5B,53,BE,29,5D,66), + wwH16(75,6F,95,02,68,57,75,56,D6,0B,46,28,90,AF,16,66), + wwH16(F5,23,A7,5D,C8,46,FB,C5,49,F2,20,B0,4A,A6,F9,BD), + wwH16(BC,C7,F8,B7,4A,F2,7C,6B,DF,81,D1,7A,59,0D,F0,47), + wwH16(4A,7C,36,54,C3,F3,E5,20,DD,0A,49,35,91,E4,B3,E0), + wwH16(65,F6,DA,E6,7E,39,08,6D,10,7B,04,43,A8,4C,AE,60), + wwH16(27,5A,8E,30,ED,A9,05,66,3D,77,51,1C,1E,19,4D,3B), + wwH16(96,45,69,B1,37,41,D6,24,D0,0A,F2,6A,93,17,59,DA), + wwH16(98,9E,37,EB,61,47,FC,4B,5A,93,BC,73,B8,A0,C9,05), + wwH16(93,3C,5C,72,11,FF,C4,D9,E1,1B,AB,E5,FB,B9,C6,30), + wwH16(AE,BB,E7,98,53,51,6B,FC,8A,B8,AF,33,B2,27,09,13), + wwH16(C1,8F,15,A6,EA,21,E2,D7,41,E8,F8,03,92,08,B8,A2), + wwH16(AA,B1,1F,1B,39,DC,02,1B,77,AF,25,67,26,96,02,5A), + wwH16(AC,B5,FB,30,6E,6F,5D,84,B5,E6,7E,F5,65,B0,8B,DD), + wwH16(4E,1F,7A,73,16,AE,F7,59,ED,D7,E8,08,1A,D4,13,9A), + wwH16(9F,42,3E,79,69,46,0A,BD,70,F7,10,BA,8A,0B,5A,49), + wwH16(F9,B0,A5,0C,92,4D,17,C7,9D,2E,C4,C3,BB,29,86,3B), + wwH16(42,B7,D3,BE,A6,EB,6A,97,2B,E1,2E,A7,69,5D,E3,91), + wwH16(4E,77,FD,FB,B9,17,54,24,6B,A7,4C,ED,32,7E,45,67), + wwH16(38,C3,DD,F9,43,8C,DA,DE,C3,F0,7E,A1,40,DE,2C,41), + wwH16(CB,60,22,8F,DC,15,47,02,04,0E,88,48,FD,3B,84,9B), + wwH16(52,05,2C,5E,82,8E,6F,7F,B8,08,B8,F2,53,07,92,F1), + wwH16(66,D0,CD,50,98,BB,A3,47,A7,35,B7,43,CC,50,FB,42), + wwH16(76,AB,86,AA,53,BC,23,2C,32,E0,84,B4,04,42,8D,C1), + wwH16(4B,F2,84,EB,02,F0,5E,28,49,D2,3D,71,4B,8B,E8,AE), + wwH16(E3,EA,20,5A,F6,77,72,D2,0B,6D,35,C9,3C,33,82,3D), + wwH16(39,20,60,44,5A,87,6D,D6,D4,E9,E1,BE,B5,3B,6F,1A), + wwH16(CF,C5,E7,78,09,78,7D,B1,1A,AC,36,8E,F7,0D,DF,72), + wwH16(3B,E4,D2,49,8A,11,4E,4E,8A,42,AF,C2,50,70,3B,46), + wwH16(58,D6,55,9D,76,B9,9D,91,E7,9C,A3,56,C4,8F,D9,30), + wwH16(AF,EF,8D,40,B6,AF,07,45,E6,35,08,A2,E2,CC,53,E7), + wwH16(53,5D,45,28,22,A1,70,EB,BB,8A,91,FB,BA,5F,D0,3A), + wwH16(BC,61,19,0B,F2,2D,35,6C,5E,D7,9B,BD,61,92,D7,A1), + wwH16(D8,CA,64,82,CA,82,31,85,63,D4,B8,0E,3C,2A,2B,C8), + wwH16(2E,F4,C8,55,FE,92,F7,84,3A,E8,80,87,B8,17,56,FB), + wwH16(04,88,97,AA,B1,7A,AD,A5,45,BB,87,AC,54,3E,4E,9A), + wwH16(A8,08,31,32,DB,EE,F6,BD,0A,EB,8F,27,A7,77,C6,C3), + wwH16(FC,FA,AF,02,4A,13,0C,0E,C1,A6,A1,99,38,63,89,98), + wwH16(2B,D6,5C,66,48,47,65,B7,D4,0C,43,3F,0F,A7,16,8C), + wwH16(2D,11,1B,80,6A,CF,92,1B,15,DF,97,43,E9,56,D7,23), + wwH16(D5,FD,3A,58,BB,A7,3C,3D,81,58,F9,F4,DD,50,7B,9E), + wwH16(AB,73,A5,11,A0,37,84,0A,A5,AF,B7,CE,6A,BA,55,AC), + wwH16(18,98,6F,CC,DA,32,50,24,76,21,74,4B,D5,CA,51,B8), + wwH16(5B,70,D8,2C,0A,C5,58,91,5E,68,DF,A9,D6,E1,EA,AC), + wwH16(E3,4B,15,3B,BA,36,E3,3A,C8,5B,98,0D,D1,B9,77,4C), + wwH16(03,96,1B,3E,4B,7B,27,DE,7F,C5,7A,EB,8F,24,57,C6), + wwH16(EE,79,E8,DB,F2,4F,A1,39,C2,18,E8,02,EA,38,98,35), + wwH16(4B,D0,EE,E7,C4,DC,05,8D,E8,63,C6,35,8C,6B,AC,45), + wwH16(70,B0,54,78,8C,5D,CE,02,2F,E2,24,30,C6,79,97,64), + wwH16(C2,53,25,8F,12,9B,1D,60,D0,B5,D5,8B,49,56,C8,1F), + wwH16(8E,8C,D7,D3,18,D4,55,F3,32,27,B3,84,C8,36,D4,12), + wwH16(C8,3F,53,9C,68,BD,89,0F,24,69,5A,8E,79,43,B3,EF), + wwH16(1B,E8,E0,27,C4,DE,CD,B0,58,E7,05,21,6B,72,FD,25), + wwH16(11,17,B7,6D,B1,5B,2A,41,6D,20,B6,F6,CC,D6,F7,4E), + wwH16(1F,43,A9,89,CB,52,73,1B,EA,99,56,B2,01,94,38,96), + wwH16(CE,3C,A3,DF,E1,D2,0D,F2,B9,31,CC,FF,01,1A,47,53), + wwH16(8C,10,D3,5E,62,C2,99,4B,32,7C,83,81,B0,52,44,71), + wwH16(FE,BF,4A,6C,D2,8B,9C,D0,7D,87,75,12,29,40,90,9D), + wwH16(66,00,A6,8A,38,E2,D1,CB,FD,0F,84,0D,B2,9D,6E,F7), + wwH16(C9,26,8B,D9,F5,BD,E9,90,77,4B,53,D5,13,C4,29,80), + wwH16(1A,D2,DA,AA,5B,4D,C0,57,25,BF,E6,48,E2,87,3B,21), + wwH16(E1,39,37,15,45,26,9E,2A,73,7C,29,51,17,88,D7,7F), + wwH16(53,47,7E,BD,2F,1D,87,82,84,52,91,61,37,E7,2D,1C), + wwH16(36,84,1A,E6,33,1E,9F,62,A2,F5,39,BB,F6,2A,79,6C), + wwH16(A3,B2,28,5D,96,1B,10,70,FC,5E,CC,86,5A,29,0A,D8), + wwH16(38,06,B8,6E,37,49,55,54,E0,5C,CE,7C,CB,20,CF,06), + wwH16(0D,3D,9D,FD,3D,28,DA,33,EA,20,00,3E,F0,D1,F1,37), + wwH16(9D,BB,72,67,41,B4,50,3E,41,59,F8,DE,65,89,B5,8A), + wwH16(D4,DF,88,B0,1F,D2,1C,15,F9,70,A4,B0,B3,C3,A2,D3), + wwH16(33,44,25,EA,8B,88,C1,2D,C6,84,C5,EF,6D,21,0E,1E), + wwH16(84,4F,41,D7,A6,0E,C5,D8,62,E3,4F,EB,AF,34,A9,92), + wwH16(30,5B,76,F7,28,9D,9A,BF,DF,6A,81,C6,49,4E,1D,3E), + wwH16(39,27,49,DB,DB,BB,C9,D2,8A,0A,AE,C4,E1,75,13,BA), + wwH16(08,C9,57,FC,80,16,15,05,C2,54,14,E2,DE,F9,78,DA), + wwH16(A3,7D,D5,33,54,88,2B,6E,8D,87,CF,66,66,AA,13,40), + wwH16(FF,18,2A,B9,FA,9F,4C,12,1E,6E,B2,B7,28,F7,B8,BF), + wwH16(B6,74,97,02,89,15,41,B2,08,9C,B5,BE,8E,03,1C,95), + wwH16(DF,61,14,90,46,0C,86,D6,49,DA,01,C8,7F,1C,9A,12), + wwH16(94,03,C7,62,1B,1A,22,B6,E9,8D,09,A3,68,09,36,D7), + wwH16(BF,02,29,C0,CB,69,E1,F6,FC,0D,80,A2,24,AC,7C,B5), + wwH16(8B,CF,50,DF,3D,D8,14,CB,27,1F,F7,1B,49,1A,A6,A8), + wwH16(01,AE,CB,ED,97,C5,0B,5A,8D,6F,1E,11,C2,4B,CA,5E), + wwH16(C7,78,AD,63,5B,31,DA,8D,75,9C,0D,72,58,D9,5E,CE), + wwH16(F0,2D,26,90,65,7B,A7,D9,66,F4,62,EF,8B,67,C2,9F), + wwH16(30,9E,E6,D6,E1,2B,3B,54,C4,B6,68,A8,B5,4C,04,8B), + wwH16(F7,37,7B,85,B2,8E,EB,C3,E0,A7,4F,CB,F0,F2,37,38), + wwH16(6F,24,2E,CE,AB,E4,F8,60,D3,1F,0C,85,DC,19,EE,E6), + wwH16(2E,56,66,DA,E6,F9,89,9B,EA,7B,6C,04,E8,B0,38,D5), + wwH16(35,F0,0F,5C,0A,F2,5A,CF,40,22,29,20,59,AC,C1,F4), + wwH16(A0,5E,C7,23,C9,9D,F1,73,67,28,A1,FE,BF,C1,60,C3), + wwH16(76,48,1F,E3,44,B6,AF,3C,F0,87,C2,00,DD,E9,3A,98), + wwH16(4C,12,F8,EF,B3,DA,C8,39,09,08,17,6A,B1,2C,31,74), + wwH16(12,00,BF,D4,B1,64,AD,C5,B8,03,4B,91,01,58,2F,46), + wwH16(C4,DB,F8,FA,F0,18,10,80,5B,22,77,91,E2,46,D9,14), + wwH16(4E,1B,93,00,CC,20,C7,E9,FC,8C,8C,98,9F,85,BD,22), + wwH16(70,AF,40,19,18,B9,FA,F9,81,01,20,F3,AB,FA,1D,03), + wwH16(65,B7,66,0C,98,55,BD,CD,9A,5A,1F,36,63,23,34,EB), + wwH16(9F,8D,AC,25,67,7B,18,F8,7E,42,E5,1E,82,4B,31,8C), + wwH16(E0,1E,0B,90,36,79,5D,6A,A2,5D,ED,21,7E,F0,00,76), + wwH16(87,BB,CC,C7,8C,09,37,02,7A,98,EA,A3,7B,65,48,79), + wwH16(DF,3C,23,FE,76,71,EF,11,ED,D7,43,1C,32,E3,EA,BF), + wwH16(A1,1D,DC,EB,F0,D2,4F,CD,B3,D2,61,CE,27,50,BC,2F), + wwH16(EB,A1,5C,36,54,1E,51,9B,D4,68,14,C2,29,EC,44,90), + wwH16(A9,55,3B,25,F6,8B,69,E8,F8,91,5B,90,A9,5B,40,EF), + wwH16(94,8B,4D,A9,EF,C3,59,78,D5,BA,4E,89,F8,AF,F7,94), + wwH16(BC,3C,C2,88,FA,56,E9,99,7D,B7,08,32,BD,65,B1,E5), + wwH16(EF,4B,F7,83,D8,74,C1,ED,A9,49,77,1A,9F,83,40,91), + wwH16(1B,60,68,6F,84,01,36,02,85,19,B1,68,D8,0B,10,0B), + wwH16(DA,DE,8A,7E,3E,67,68,2B,B2,B2,21,E8,51,96,63,D1), + wwH16(7D,D7,1C,9F,64,EC,62,94,CA,B0,2D,6A,F6,49,C4,8A), + wwH16(D6,45,58,C6,70,E8,21,D1,4C,D6,20,AB,95,B2,F2,51), + wwH16(E1,1D,2C,C3,14,FD,A7,E0,BF,10,FE,28,0D,4A,E4,4E), + wwH16(5C,0F,A0,35,F6,1E,FF,A2,3A,B8,C4,16,59,1E,FF,8B), + wwH16(63,52,E0,AA,FF,11,2C,C2,25,EF,DB,DC,C8,A8,BF,0E), + wwH16(32,A9,34,5B,33,5F,EF,73,AB,AB,F1,E2,32,2F,6E,38), + wwH16(00,EE,1F,84,62,8D,EE,86,66,EC,96,D9,DD,70,28,83), + wwH16(35,D7,5F,64,1F,4F,BE,0C,A0,4B,26,2D,8E,4D,C6,AD), + wwH16(41,59,1A,6C,6A,26,EA,A5,C1,62,04,16,15,6C,56,7C), + wwH16(2C,0D,02,50,E6,E4,5F,89,70,7F,BC,8F,19,4D,77,00), + wwH16(BF,32,82,D8,C0,D5,33,41,3F,B4,D1,68,86,27,EA,AC), + wwH16(61,F5,E6,8D,B1,48,C5,F3,CE,3C,50,F5,1C,D2,18,23), + wwH16(B4,60,E1,B3,FF,6F,F9,CF,92,CE,A8,FE,1B,0C,B3,6E), + wwH16(6C,84,D6,B7,CF,F4,08,1F,A4,D5,A0,09,D6,4B,11,BF), + wwH16(E3,64,23,A4,F6,B3,E4,B5,FB,05,49,A8,C4,7C,CC,E0), + wwH16(8D,AA,14,20,2E,BB,16,6E,1F,21,CC,20,55,0C,30,04), + wwH16(A1,6C,92,22,C3,E3,C2,FC,66,A1,A4,B7,EA,7E,25,74), + wwH16(08,5A,0C,65,F3,83,11,70,6A,D0,B3,D1,02,84,C2,F4), + wwH16(B8,63,33,5F,9A,6A,D5,DB,1C,0B,3D,48,8A,74,BC,95), + wwH16(0A,4A,57,74,BB,87,9D,3E,6B,39,E1,7C,03,85,82,05), + wwH16(0C,86,BC,F5,FE,0E,C1,D0,F8,79,FE,00,72,AE,64,6D), + wwH16(B3,61,4E,6E,FC,5A,EE,65,AC,D3,08,98,03,11,F6,3C), + wwH16(5A,F9,D5,04,6E,00,9F,14,3A,6B,FA,63,1D,36,7A,E7), + wwH16(CD,54,BD,0F,7A,5F,CF,6F,75,0A,F5,6F,9D,9E,00,4F), + wwH16(7E,CD,1C,01,B4,81,0C,09,CD,12,AF,A4,B2,51,DE,16), + wwH16(C1,AB,CB,BA,5E,82,F5,A9,F6,6E,0E,5C,B9,6B,ED,F0), + wwH16(46,99,B8,BE,87,21,A2,71,B9,47,3D,8C,91,35,38,77), + wwH16(15,78,82,E4,61,D7,35,A3,15,31,58,18,E6,27,F5,0B), + wwH16(7A,AE,14,7C,37,83,D2,FC,A6,11,91,13,B7,DA,D3,0D), + wwH16(15,79,E1,E9,D7,92,5F,F6,3A,16,22,E5,12,0F,59,F9), + wwH16(08,40,9D,4F,39,F5,AC,BE,95,2E,67,4A,5B,0C,65,B0), + wwH16(EC,E4,2D,74,89,D8,CB,E6,32,C5,3E,11,88,42,01,D6), + wwH16(BF,81,99,17,F2,0D,EE,B3,87,7F,DE,C3,60,B5,90,DA), + wwH16(14,FA,D7,46,6A,3F,98,49,7A,7E,3C,2A,0A,6A,8D,9B), + wwH16(9D,61,4A,FE,CD,34,02,82,FC,9A,35,82,87,5F,D9,A6), + wwH16(6D,6E,68,D2,4A,1A,96,3A,80,5E,8E,B5,A6,FA,26,5A), + wwH16(B2,47,12,92,A2,50,20,CD,CE,78,E9,96,11,6D,23,3C), + wwH16(2A,38,C9,88,11,5E,1B,8A,CA,C8,B1,40,D0,13,25,FE), + wwH16(59,1D,5A,8A,42,B9,51,28,20,D0,B4,B5,A4,90,28,6F), + wwH16(A2,7E,DF,62,D5,FC,80,E9,1C,BD,89,15,E6,79,DD,6C), + wwH16(E2,96,45,2E,32,4F,34,AB,65,61,99,EA,FA,2C,5A,3C), + wwH16(75,80,C3,62,82,35,B1,66,A8,34,1C,FF,2E,11,F4,4D), + wwH16(CF,26,42,EC,76,9A,E8,BA,6A,CA,15,82,0F,3F,D9,B5), + wwH16(BB,FB,9D,CF,16,5D,9E,5F,5A,A5,D6,C2,62,43,BE,CB), + wwH16(67,0E,55,0F,97,EE,37,55,D6,BF,22,93,02,51,00,73), + wwH16(EC,5F,D4,3B,12,F8,2D,12,A8,E4,66,2F,A0,31,44,42), + wwH16(CB,29,9E,A9,31,BB,7C,05,21,FE,AB,C0,29,61,53,B6), + wwH16(7A,96,1D,F7,72,27,E7,57,82,05,95,6F,14,D9,5B,81), + wwH16(4D,77,1A,37,91,3D,20,D8,8C,9E,24,9E,58,15,85,0E), + wwH16(34,DD,29,4A,AA,CB,D5,86,7B,A1,CB,80,87,0D,05,0D), + wwH16(D7,D4,7A,09,06,50,89,80,F5,17,C6,C6,14,CA,5F,7B), + wwH16(6B,36,F9,2A,EF,6C,9F,6E,4E,24,C9,8D,3C,98,27,59), + wwH16(E8,6A,7B,34,F6,90,FB,A2,73,7C,20,54,EC,6A,2E,15), + wwH16(7A,84,B6,79,1C,D5,1E,2A,F0,CE,24,61,FA,36,3E,35), + wwH16(D8,08,15,F5,78,F8,E9,5B,5E,29,1E,E2,08,6A,0C,B4), + wwH16(C2,7B,92,32,A8,C2,40,C6,DE,E0,7A,1D,AD,46,06,CF), + wwH16(D5,3E,5A,B0,4B,AE,D6,B2,81,97,96,C3,B0,4C,29,29), + wwH16(A6,AD,77,99,EA,35,67,B7,6E,D6,13,BC,B9,BC,F8,0D), + wwH16(05,BF,3E,56,4D,28,B4,9D,A6,10,88,83,EB,21,20,74), + wwH16(25,7A,34,1D,56,9A,96,10,1E,84,47,FC,05,6E,EF,2F), + wwH16(C5,8D,D0,62,98,6E,D9,BC,63,CB,CC,9E,12,14,94,3E), + wwH16(EE,47,3C,84,B2,7E,DE,8D,36,30,AE,86,51,37,31,3C), + wwH16(BD,C2,1A,E3,9C,55,0B,E3,E3,B5,AE,62,FB,FA,10,DC), + wwH16(17,25,C1,EC,6E,AC,D5,55,83,E6,BC,9C,E4,A4,13,D7), + wwH16(38,54,7B,30,F7,C8,50,32,A1,30,41,AC,21,11,BA,60), + wwH16(7C,2F,DE,95,90,88,2E,3F,38,A8,9C,07,86,A3,50,A7), + wwH16(C5,2D,52,0A,16,D6,7A,26,DD,3F,4E,F5,EA,97,FE,44), + wwH16(37,3B,D0,2E,0E,02,E0,38,0B,78,89,33,61,D1,14,10), + wwH16(05,7D,2C,B6,F1,55,5A,1E,CE,1C,34,84,B1,24,83,6E), + wwH16(49,A6,72,CE,A4,5D,6E,CE,05,4B,26,00,56,5C,00,DA), + wwH16(11,AD,15,A5,F2,19,5C,B7,1C,80,E7,A8,A1,5B,07,6C), + wwH16(6E,A3,52,41,50,DE,C3,14,96,84,B4,C6,03,C1,D7,A8), + wwH16(3E,35,6D,3E,B0,CE,3C,40,47,6E,9F,DB,64,22,F2,7A), + wwH16(C1,BB,45,0D,BA,32,C8,AA,7D,15,D2,72,F4,B1,91,A9), + wwH16(1B,5E,6C,E2,39,C0,DC,A4,CD,60,D9,CB,21,08,21,5E), + wwH16(AF,E9,1A,9E,BE,C2,EA,13,13,DE,B0,77,D3,C5,6A,69), + wwH16(CB,E3,D2,8C,30,8A,A5,44,E9,12,F5,88,B0,2D,CA,E4), + wwH16(F5,7E,F8,5E,E4,88,17,98,89,F0,6C,D1,AE,EE,4C,A3), + wwH16(EC,64,D4,B6,3D,46,48,FB,4D,26,36,0B,DA,4F,94,04), + wwH16(21,85,10,7F,08,8E,A4,FC,E7,CA,55,DB,60,5B,CD,D1), + wwH16(D4,94,1A,8D,FB,29,FB,A3,2E,8C,74,3A,2D,14,F8,3A), + wwH16(F4,F9,19,E6,A6,6C,BC,7E,27,27,73,83,FF,C4,E7,46), + wwH16(46,2F,4D,A2,09,C0,49,D0,6F,A0,2E,67,0E,D3,43,75), + wwH16(79,BA,40,93,CD,24,69,AF,C9,92,F2,45,C5,5A,7F,83), + wwH16(E0,C5,FB,92,25,FA,95,74,4B,D7,F2,E1,01,A1,40,38), + wwH16(44,9C,32,59,9E,BA,87,17,C0,04,D6,3B,3C,FF,90,80), + wwH16(1F,20,D1,C3,63,46,0A,79,96,F8,3A,7C,48,ED,3B,62), + wwH16(98,F7,13,A3,66,E7,69,99,E9,B2,10,64,3D,CF,2F,12), + wwH16(72,B2,A6,03,B8,56,F7,E5,D9,18,22,E1,59,7A,46,D9), + wwH16(3D,99,90,0D,15,6C,E4,0F,C2,00,1B,BC,A5,89,D0,D5), + wwH16(51,DE,96,11,C4,B5,4B,CD,5D,3B,41,1B,02,F2,21,50), + wwH16(B6,5F,6B,9E,05,32,AF,76,8C,91,1A,B1,63,A7,74,E1), + wwH16(80,94,DD,36,F3,00,CB,7A,42,50,96,A7,A2,F4,16,41), + wwH16(8B,05,DE,AF,11,20,C0,58,4C,E0,99,87,DE,AA,12,9C), + wwH16(DB,3E,CE,4A,EB,26,8F,08,A2,0F,4B,BC,87,75,E0,F8), + wwH16(BD,2B,EF,4E,C7,6D,6C,C1,6A,9A,36,18,50,94,80,A0), + wwH16(4A,A3,2C,09,9E,A0,49,53,8B,35,D8,F0,CC,71,17,C0), + wwH16(C8,D1,63,A8,61,5F,26,16,DA,8D,7C,41,97,CF,C3,BD), + wwH16(F9,B4,AC,EF,4C,B7,ED,4E,50,8A,E7,5D,7E,E2,E3,37), + wwH16(CC,2E,E3,39,A8,A4,C2,7C,1E,DE,D4,47,D1,BC,BB,E5), + wwH16(DB,12,13,D9,72,44,72,BA,D5,EA,2A,91,B3,10,B0,B9), + wwH16(D8,4D,3D,D4,62,BA,EB,DD,75,1D,FB,5D,CC,42,18,87), + wwH16(66,6E,34,BE,73,20,72,29,8B,B7,AE,A3,C5,3C,F3,9B), + wwH16(03,AD,39,21,14,F6,8F,CC,37,16,44,81,91,C0,C9,91), + wwH16(2E,1F,20,23,FA,A9,3D,63,2A,62,D2,C6,AE,B8,B9,D8), + wwH16(BE,A1,01,23,1B,37,E5,ED,DA,8E,EC,33,CB,D4,F8,A3), + wwH16(88,C4,2A,27,30,5B,89,3D,47,7D,66,A0,FA,9B,19,A2), + wwH16(35,AB,66,CC,7C,01,E3,6A,D9,4A,F7,4E,A3,DF,F8,C5), + wwH16(A1,F4,C6,91,E6,09,EB,F5,CD,F1,D3,84,2A,0F,BF,BB), + wwH16(52,B9,C1,F1,40,27,4D,74,F9,69,83,CE,41,B8,51,94), + wwH16(08,C9,A7,F5,25,31,5B,68,F7,89,96,F9,A3,66,C4,FD), + wwH16(5F,34,1E,5F,BD,34,22,46,71,33,86,87,2B,35,65,65), + wwH16(AF,6E,F5,F9,C6,CB,4D,C4,F5,49,D3,F0,B1,93,A6,3F), + wwH16(E4,C1,A2,25,12,33,5B,1F,CF,6D,7A,93,26,1B,63,04), + wwH16(A5,A3,E7,F4,08,18,56,E3,C7,7F,F6,2D,17,97,99,FA), + wwH16(BE,52,A0,EF,55,C3,B4,E6,53,2E,9F,14,79,A0,A0,CF), + wwH16(1E,3F,34,85,1F,4F,61,B3,F8,4A,56,99,91,15,C5,50), + wwH16(B4,A6,45,DC,F6,CB,2D,1B,4B,27,F8,2E,B3,37,39,EA), + wwH16(BA,DF,70,69,50,2E,B9,E2,51,46,AF,11,B4,8C,41,14), + wwH16(EF,01,B6,6C,8E,08,B9,59,04,70,18,B8,F4,05,69,D7), + wwH16(9A,8E,C3,CD,F2,75,63,B1,AC,4E,9D,5E,F9,C3,21,81), + wwH16(C7,84,40,66,73,13,7A,EE,E3,29,D5,3D,56,F3,1F,63), + wwH16(F9,7B,F1,19,84,1D,70,C5,3E,80,04,5B,28,D6,04,CD), + wwH16(69,53,57,72,D7,FB,9B,8F,1B,8E,68,89,50,F2,26,DC), + wwH16(8F,A8,32,15,90,A3,FC,4D,F4,C2,BF,12,61,56,90,B5), + wwH16(B0,18,17,E4,AF,37,7E,FF,73,66,24,CA,53,10,21,31), + wwH16(C5,98,95,A4,08,F1,F0,96,69,BA,29,99,72,5F,D2,33), + wwH16(60,C2,47,75,69,13,A7,D5,A9,E5,85,F6,BA,37,07,30), + wwH16(68,49,B8,C4,6F,4A,F9,D7,DA,42,43,64,C3,8F,66,0B), + wwH16(93,AB,93,DF,19,29,E4,F8,6D,B5,6E,9D,4A,29,B1,09), + wwH16(28,C9,1F,68,C2,01,D1,75,0D,84,87,3F,F7,70,D4,65), + wwH16(C0,CA,ED,73,E3,BF,1E,FF,B2,8C,B8,64,07,63,DC,DD), + wwH16(2A,86,E2,69,03,01,EE,E0,32,DB,12,22,68,4E,0E,AB), + wwH16(00,53,D9,CD,6F,27,49,CB,F1,BA,D4,1C,6D,9A,62,D0), + wwH16(E2,5A,56,11,07,AA,76,51,7C,25,8F,51,90,E1,48,E9), + wwH16(C7,4F,23,D6,09,65,B6,58,A0,9C,6D,B4,B1,53,B4,90), + wwH16(0A,A3,59,23,8A,DB,8B,2F,11,AE,A0,CB,5F,2B,18,94), + wwH16(44,5E,C8,A6,B2,3E,EB,7C,78,33,78,B2,3F,D5,19,84), + wwH16(D3,08,B4,C1,53,B0,15,BF,0A,69,B8,7B,60,4B,0D,1F), + wwH16(86,EE,E6,69,E4,7C,65,32,71,17,3F,BE,F3,BA,F3,A0), + wwH16(05,0E,9A,AE,31,17,D5,99,02,FC,9F,0B,10,5D,92,B1), + wwH16(0A,86,09,E4,6E,34,AE,CA,F2,A2,DB,E9,D8,78,9E,DA), + wwH16(35,69,7D,79,FB,D1,92,40,46,B0,46,A6,42,5C,65,8F), + wwH16(35,05,F2,3D,EB,C3,63,C6,D7,86,C8,5F,B0,98,7C,89), + wwH16(8A,4A,BB,78,BA,EA,42,4C,DF,8E,68,AD,70,D9,37,64), + wwH16(A7,A9,FD,69,70,E9,6F,0A,08,30,0B,4F,48,3F,44,0B), + wwH16(DC,3F,20,B9,0F,B7,3D,3F,7A,91,41,9A,6A,F0,49,71), + wwH16(B2,D6,98,EC,E9,B3,9F,32,6C,BC,18,B2,A3,16,4E,89), + wwH16(A2,ED,7D,E1,AC,FF,2F,08,C3,56,85,3D,CD,F0,87,2F), + wwH16(3A,AC,4E,6D,27,D8,D0,4A,D1,05,88,0A,C7,41,9F,3E), + wwH16(52,3F,B1,D7,2C,24,91,29,A2,6C,36,E7,3F,D5,7D,A4), + wwH16(D4,19,4A,04,AD,59,3C,33,53,ED,3E,57,93,F0,32,51), + wwH16(BF,57,3C,CA,61,2B,8F,BA,50,0B,A3,85,FF,A5,63,8E), + wwH16(D5,A6,FA,0B,16,A4,31,03,E9,4D,09,33,3F,32,88,B6), + wwH16(B7,78,C3,86,6E,30,33,6E,26,B3,90,DF,5B,55,2D,1A), + wwH16(FE,E0,B9,4C,0F,00,20,1B,0D,70,44,CE,03,FE,04,54), + wwH16(BD,56,89,7A,FA,AB,DB,CD,EE,A0,26,E1,BE,66,C7,0E), + wwH16(59,71,9A,DB,1C,88,AF,7B,6A,43,01,3A,7B,15,6E,18), + wwH16(6C,1B,1A,E9,C7,7D,66,66,1B,24,B4,2A,C4,C7,AE,36), + wwH16(8C,19,25,5F,59,8D,6C,4B,6E,69,8A,1C,D1,F2,E5,62), + wwH16(5B,6D,50,6E,EE,3B,EF,9C,01,93,86,23,F2,2B,26,7A), + wwH16(C7,B1,80,AF,08,73,E8,4C,CB,C2,5B,24,56,9D,0E,F0), + wwH16(0D,2A,1F,62,5F,18,D3,AD,02,1B,66,0E,3F,CB,DC,A4), + wwH16(18,CB,DC,FC,03,16,B9,FB,70,B9,A1,43,FA,B6,9F,45), + wwH16(D1,82,BE,0D,64,38,BA,46,FF,60,9F,1E,04,74,9F,C9), + wwH16(4B,56,98,9C,6D,D9,4F,2B,9E,5E,BC,B0,41,75,5F,15), + wwH16(4C,48,0E,2B,72,F4,03,EF,EC,8B,9B,CF,69,9C,A4,D1), + wwH16(B7,23,E5,BB,6B,7A,13,73,3F,A0,2F,7F,DE,82,97,EE), + wwH16(24,3B,6B,DC,36,74,85,F8,01,91,B1,4D,3B,CB,3F,AE), + wwH16(AC,54,59,8C,85,39,D0,45,35,76,EC,9B,D4,3C,73,33), + wwH16(03,7C,E6,DD,86,79,9A,A7,AB,0A,27,02,FC,39,0E,6E), + wwH16(6D,B1,C9,F6,15,FA,DA,71,D6,CC,1C,A6,1F,7F,43,3F), + wwH16(4C,36,50,0F,3D,A2,E7,D3,7B,36,D8,A3,07,32,BB,A7), + wwH16(96,1D,4E,8F,7C,1B,70,2F,29,6A,03,1A,BB,F5,89,E5), + wwH16(17,67,8A,2E,DB,74,DF,0D,6E,F4,34,CB,95,40,21,1F), + wwH16(A6,AC,12,14,86,61,F8,02,96,56,EF,F8,B2,3A,32,E0), + wwH16(55,13,EE,74,0B,53,AB,7B,88,1A,DA,A7,50,8F,5B,CB), + wwH16(05,F6,F8,C8,51,EB,52,C9,44,DB,80,44,01,A1,11,46), + wwH16(8B,7F,75,BE,E1,B6,EA,8A,64,4E,06,FD,DB,51,1A,24), + wwH16(01,FF,48,39,62,E9,01,9B,38,AD,77,8E,10,5E,DB,7D), + wwH16(3D,CA,64,F4,02,9E,20,74,C4,39,40,D7,CC,7F,08,89), + wwH16(D0,E1,21,E0,F4,E4,A3,6F,FE,D1,AC,92,61,51,AF,20), + wwH16(F2,3C,70,52,8C,43,D7,17,2E,FD,81,45,9E,00,DB,3C), + wwH16(56,47,14,90,E0,50,C6,DA,FF,71,FE,5F,0C,65,B1,09), + wwH16(B6,18,06,68,09,4C,8A,21,1C,5F,50,7D,B1,5C,B0,4D), + wwH16(98,00,A9,85,45,CB,D7,E0,E8,47,BE,FC,D2,C5,2B,CB), + wwH16(AB,02,08,61,4C,C2,F1,ED,FC,69,F8,69,4B,7B,91,72), + wwH16(8B,CA,FF,E0,15,D6,8A,86,E1,DE,2A,A7,CA,EE,2E,73), + wwH16(A6,A9,D5,60,08,67,7B,71,58,3A,52,0B,25,A1,66,EB), + wwH16(7F,4A,D3,0A,0D,01,11,2B,10,57,DB,90,EA,68,AC,22), + wwH16(C7,0D,42,9C,7B,C8,C1,39,33,78,1F,A2,C8,8D,80,EE), + wwH16(DA,CD,F1,27,0B,96,BB,FA,67,93,A6,4B,C7,49,B0,64), + wwH16(D5,62,68,B6,E8,30,D6,82,E1,3F,B1,ED,F3,8F,57,9E), + wwH16(54,2C,C7,E3,39,92,99,4B,F0,A0,BD,AF,9A,50,CA,04), + wwH16(9E,08,6B,72,AB,CE,02,88,EE,6A,B5,3C,E1,D3,AB,94), + wwH16(6C,6E,0C,B3,F3,F7,40,D9,C8,63,2F,1E,AA,95,1D,57), + wwH16(4B,74,CD,38,E6,22,3A,12,40,B0,D7,5F,7F,75,1F,22), + wwH16(EB,C1,D2,1F,24,88,47,41,79,CF,52,9D,48,06,55,E9), + wwH16(A7,CF,C6,F5,34,FB,35,23,32,B5,E2,B9,69,A9,91,77), + wwH16(86,30,30,7F,6D,FA,2B,09,8F,4E,13,C6,EF,A2,F7,CD), + wwH16(7E,E7,34,9E,CA,DF,0C,A6,85,28,DB,14,85,46,54,7B), + wwH16(6F,E3,48,76,2A,34,F3,25,B6,00,53,DE,92,B5,95,3D), + wwH16(F6,B4,E7,58,36,6B,7B,04,A7,D6,C5,33,FF,12,CB,A9), + wwH16(06,F1,49,4B,40,F9,F8,06,A0,CF,44,4F,3B,E6,B1,93), + wwH16(0B,24,8F,FA,D6,3E,25,EB,04,3A,56,72,F9,32,27,FE), + wwH16(9B,3B,88,A0,06,98,44,9A,7B,DB,46,6F,13,26,9D,3F), + wwH16(FA,79,13,48,EC,2D,85,96,05,49,CB,A2,0B,4B,E9,60), + wwH16(D4,F3,0F,72,3D,B9,19,F4,E9,36,6D,E6,BC,C1,5D,6E), + wwH16(3A,59,3C,7C,0B,C6,2D,04,51,2E,4B,4A,13,21,73,AC), + wwH16(B2,50,17,B3,25,6F,4B,47,73,30,1A,72,E4,19,1B,23), + wwH16(6D,88,60,F1,73,8E,29,FD,08,59,3D,94,1B,8E,33,2F), + wwH16(64,73,D6,34,97,FF,B1,6B,87,9D,35,5F,1F,DA,76,A8), + wwH16(0B,23,08,9A,24,51,E2,DF,2A,D5,1E,35,7B,5A,25,F4), + wwH16(B5,9F,9C,89,AD,A8,01,E8,2D,57,73,17,A1,55,91,E7), + wwH16(12,EE,C5,FC,62,42,DE,D0,2E,53,36,FD,8E,82,6A,46), + wwH16(25,28,B2,DD,E2,60,AB,5E,B6,3B,32,56,FB,24,05,AA), + wwH16(36,17,1B,2B,8C,F8,BB,E1,58,C2,3A,29,EE,17,DA,39), + wwH16(5E,D9,43,9A,55,40,2D,A0,38,7C,9C,63,29,E2,2D,AB), + wwH16(8C,69,B1,B0,FF,EF,69,49,B9,72,FB,9D,58,E5,EF,AD), + wwH16(A9,6D,F4,58,BE,FE,4B,33,81,B5,1E,21,0D,7D,54,9A), + wwH16(40,8F,D3,6B,BA,AC,7A,9A,DB,B1,46,4D,1D,C7,21,86), + wwH16(C2,25,20,17,5F,57,11,14,84,93,DE,A6,08,FB,7C,17), + wwH16(1C,25,14,02,18,91,AA,42,8F,25,76,06,20,98,B2,80), + wwH16(E0,99,6B,3C,15,2D,69,B6,AE,96,CC,12,19,79,64,8C), + wwH16(82,7B,52,53,66,D1,5A,0B,55,E9,92,E7,6C,F4,02,07), + wwH16(E3,BB,A2,94,A4,53,62,6B,55,D3,EA,84,BF,3E,33,44), + wwH16(F4,51,7D,76,F2,8D,27,44,0C,3C,E9,86,5C,C9,F6,3E), + wwH16(C7,CD,1C,86,57,99,98,6D,5C,81,02,3C,A9,7D,66,14), + wwH16(8D,96,08,72,D6,CB,41,76,E9,97,74,9F,1C,01,01,28), + wwH16(AD,54,B5,50,A2,CF,B9,6F,F9,7C,15,63,B5,2A,38,16), + wwH16(08,21,2A,B1,77,8A,B3,B0,59,C5,F8,0B,DF,7D,59,D6), + wwH16(F8,09,D5,C2,2A,2F,9C,33,50,48,5D,B1,22,50,84,6D), + wwH16(DD,0C,43,63,A0,34,64,DD,9B,ED,6D,C1,B2,3B,EA,9C), + wwH16(F1,AE,9D,0E,E5,43,3F,34,5F,AE,71,54,D1,FC,0C,80), + wwH16(17,9E,C8,A1,2B,55,ED,5A,F4,CD,4F,1D,2B,61,52,DF), + wwH16(DC,FE,A0,08,B0,5C,BD,D7,1E,8A,1A,DA,0F,E6,D5,53), + wwH16(9B,22,8B,F5,A0,63,37,D5,98,E9,C7,37,46,19,D4,59), + wwH16(47,CC,AD,BD,E3,C4,CA,73,C3,7B,70,8B,C4,E0,94,87), + wwH16(6E,BA,91,65,C7,16,0C,35,0C,F1,BD,58,30,EC,0F,37), + wwH16(E1,1E,97,C8,0A,63,22,9E,B1,BA,F5,EE,89,8B,74,B9), + wwH16(75,0F,A8,F9,9F,41,B2,83,CA,71,8B,F5,83,6B,F5,78), + wwH16(44,FE,74,00,B8,29,80,7A,3A,74,61,11,4C,42,14,33), + wwH16(59,AE,D8,C3,BB,8C,48,52,31,09,5F,D9,96,9E,A0,14), + wwH16(32,29,2A,91,22,73,F9,4C,C2,B1,41,55,68,14,05,2B), + wwH16(12,BE,6B,A6,D3,34,0A,45,38,FA,2B,61,1B,4F,F7,7A), + wwH16(32,A4,6B,2D,8E,6F,7C,CB,48,3C,4D,FF,E2,6D,7F,86), + wwH16(E7,9A,DA,F3,BB,8D,2E,6C,16,31,72,D8,BB,D4,40,6C), + wwH16(D7,C2,3B,B5,96,E7,3C,79,A1,D7,07,67,29,9C,83,52), + wwH16(8B,53,16,09,3C,BA,89,94,6A,C2,84,01,8E,EF,6D,3B), + wwH16(15,3C,F3,EE,41,07,E2,37,0A,0F,3A,7C,10,19,83,CD), + wwH16(58,80,6A,FF,C2,2D,18,2C,03,2A,95,C0,84,7E,E7,38), + wwH16(BD,66,ED,A8,C6,AD,7A,2D,D9,83,06,0D,AE,8E,FF,28), + wwH16(27,32,C2,D6,28,D4,76,C5,18,3F,A5,91,FA,0C,15,28), + wwH16(E7,C9,23,E2,C6,56,87,23,08,AD,BF,1B,16,B0,11,BB), + wwH16(BF,F3,D4,24,30,35,BC,60,11,29,C3,77,86,91,DC,A4), + wwH16(08,D5,6A,41,F4,AC,A8,38,44,45,BE,0A,0B,C3,9D,18), + wwH16(56,1C,C8,F3,0C,39,BC,A2,B5,1D,D0,8B,0F,83,2D,10), + wwH16(9C,14,EF,6C,10,12,25,AF,59,ED,73,44,54,AF,3C,C1), + wwH16(91,67,AA,BD,5B,CC,AB,E8,FB,8F,9D,13,56,45,D8,02), + wwH16(E1,E6,61,1B,FD,97,C0,74,54,ED,DB,2D,BA,E1,61,92), + wwH16(8D,3D,93,74,0D,B2,37,DC,35,65,D7,EA,97,70,F4,46), + wwH16(92,8B,28,B1,CD,46,AA,73,77,06,47,D2,68,4A,72,3D), + wwH16(59,65,01,1C,B0,65,E6,85,A4,71,2B,3F,82,AC,52,98), + wwH16(46,15,A4,22,62,A7,5E,D3,7E,D7,26,74,CA,4A,EE,E4), + wwH16(2D,7C,84,31,F7,76,50,DE,53,96,BC,25,C4,BF,86,DC), + wwH16(1B,DC,04,33,85,A2,5C,91,E5,EA,A1,B8,DF,78,D2,81), + wwH16(FB,21,FF,2B,AE,C9,ED,CD,45,44,E3,78,47,7E,C6,4B), + wwH16(84,CD,D5,2E,9F,02,7B,EB,86,48,65,91,36,45,D5,FC), + wwH16(95,F3,E5,87,90,8E,D9,A9,B8,1F,3E,65,25,39,56,AA), + wwH16(CF,6D,9F,8D,87,3C,3B,A1,AE,38,0C,BF,AA,1F,10,20), + wwH16(22,20,85,A3,42,14,BF,E2,2C,8B,85,A8,9A,B4,0B,D1), + wwH16(6C,A9,49,C9,36,1D,E6,8E,17,BB,51,50,C3,B4,60,7C), + wwH16(23,08,49,D9,23,E7,2F,1F,17,0A,4D,CA,9D,A5,86,95), + wwH16(67,9D,6B,59,A8,4E,18,E7,FE,74,56,58,52,62,13,71), + wwH16(21,3A,9E,8A,F0,B2,14,B3,7B,44,25,59,34,29,D4,1E), + wwH16(2D,04,01,E2,5E,B1,57,0D,6C,EB,8C,17,48,73,20,04), + wwH16(84,B0,A6,4D,AA,26,FB,08,41,02,29,13,19,09,C0,A5), + wwH16(04,0A,A5,DC,2D,E3,CA,A7,AB,AB,A9,8D,1B,C8,8D,6D), + wwH16(81,C1,7C,7D,0F,7F,6E,B3,81,8C,11,05,54,79,A7,E4), + wwH16(95,2E,CE,13,02,41,71,6B,94,CF,00,C7,DF,45,9A,29), + wwH16(F4,12,6D,90,E9,36,F6,89,11,33,64,CE,0E,D0,6D,DE), + wwH16(43,BC,DF,75,AD,E0,26,F4,AE,60,D1,60,18,EB,FC,DF), + wwH16(B6,43,85,02,6E,19,B5,DC,DF,FD,71,84,8C,16,4E,59), + wwH16(72,5C,11,53,E2,5D,52,B3,CF,77,FE,D5,C3,2F,61,B5), + wwH16(91,20,FF,B8,36,E4,E0,D4,E3,26,2B,4A,39,11,2D,EC), + wwH16(10,FE,FC,3B,A0,37,0E,82,64,C0,3F,AA,93,82,FA,2E), + wwH16(F3,07,45,BE,7C,88,F7,03,50,14,62,CD,65,41,42,F6), + wwH16(61,55,7B,EA,20,8F,F1,05,FB,92,9B,19,5E,FC,78,0A), + wwH16(48,15,E2,FC,2B,FC,2F,26,D5,26,12,60,B9,1E,E7,3D), + wwH16(53,D5,C3,0E,DD,72,05,58,1A,EB,46,5F,34,06,B0,58), + wwH16(06,EB,1E,C8,AC,0F,5D,C5,B3,66,96,66,01,EC,13,A4), + wwH16(AB,55,74,4C,4D,D2,FC,CB,D0,69,3A,91,15,BF,70,F0), + wwH16(18,F7,E2,A5,C6,96,FB,34,43,13,D2,7C,8E,D0,8A,61), + wwH16(E4,DD,08,CA,AC,69,15,29,45,69,D4,23,0E,4D,FF,D5), + wwH16(4E,D5,0E,E7,62,61,54,CA,1D,22,B9,BB,BA,26,28,8B), + wwH16(45,1D,65,1B,C9,1A,91,A6,2C,C0,DF,55,1E,1A,38,9B), + wwH16(32,C2,3B,07,80,14,E8,00,11,E8,9D,71,E9,D5,84,08), + wwH16(27,83,94,CB,B5,40,80,A5,27,4F,B2,27,9F,26,E2,31), + wwH16(3E,89,98,DE,94,4C,AB,FF,08,FB,E9,AE,A0,C0,16,99), + wwH16(4D,D2,4B,45,45,B7,61,42,03,23,5D,2D,B3,CF,59,08), + wwH16(D4,23,54,E4,99,F3,55,8B,A1,F0,C8,C0,B9,E4,52,F0), + wwH16(0C,41,F1,D8,0F,1D,D7,7A,F3,65,80,8C,46,56,19,7A), + wwH16(8B,BD,85,1D,4E,6B,A0,5A,E1,6B,32,3B,6F,06,CB,68), + wwH16(04,F7,FF,E9,2A,7F,C7,2B,1F,1E,FC,E0,D8,3A,27,02), + wwH16(43,2A,F5,0A,FA,16,76,B0,48,F7,35,2C,43,E2,7B,55), + wwH16(A0,77,96,1F,28,5B,7E,B8,D4,CC,89,93,61,35,F1,03), + wwH16(89,7B,51,18,A8,B3,7D,6C,E5,40,B7,1F,B2,B7,99,67), + wwH16(81,5F,EE,59,E4,2E,24,F8,D2,B4,D7,F5,70,A0,63,AA), + wwH16(1A,4F,6C,2A,BE,B3,71,CC,9B,78,1F,AE,C6,F7,36,53), + wwH16(A5,8B,15,BF,6E,B0,4D,F0,D7,83,AB,06,67,C7,3A,17), + wwH16(F7,BC,9D,CB,72,F7,9E,08,C2,B2,0A,F4,E9,89,B6,07), + wwH16(CC,10,0F,02,A3,18,01,D6,E5,81,D4,B8,5C,B5,AA,C1), + wwH16(01,90,E8,D4,08,C2,00,BE,C3,4D,0B,53,BC,97,B2,EB), + wwH16(3A,33,57,AC,F3,12,F2,98,24,9E,8F,F8,7E,CE,25,E7), + wwH16(5E,0C,A1,E6,5D,67,5A,DE,17,FE,6A,D9,4C,5A,11,B8), + wwH16(6F,FC,D8,03,1C,4A,D9,A4,24,B3,BF,77,3C,BA,7D,7C), + wwH16(B9,1A,E8,27,4F,75,56,D2,C6,82,27,F4,BD,AC,B1,16), + wwH16(BB,31,CB,F2,A9,CB,76,A6,C3,F4,74,BB,BB,81,27,92), + wwH16(3F,28,28,34,C0,32,ED,56,2F,F3,E9,00,B7,21,3A,D5), + wwH16(F1,56,DE,57,CD,3B,84,60,7F,C1,B7,EC,9D,DE,19,AD), + wwH16(9C,FA,8C,7E,E9,B4,3C,1F,71,B1,A5,C7,F1,D3,0A,98), + wwH16(72,BA,64,B6,6B,48,66,CF,5D,8C,5E,29,B9,78,DB,17), + wwH16(ED,1B,5C,F1,90,B3,3C,79,A6,74,5E,EA,9F,95,88,AE), + wwH16(2C,70,07,8A,8B,E5,7E,0D,C6,AA,2F,8D,47,34,32,EF), + wwH16(46,20,FD,79,B5,3F,46,80,23,27,32,35,9E,22,48,72), + wwH16(67,65,33,15,15,42,C2,06,1B,89,EE,F0,3D,0A,EF,CE), + wwH16(26,E5,AC,29,82,E9,59,2F,4C,FC,78,60,91,B4,FE,92), + wwH16(04,04,50,0F,F7,1F,E2,6A,D5,F6,28,F3,10,93,58,25), + wwH16(5F,32,08,C7,C6,7A,E3,B0,14,E4,22,F2,A8,A9,58,D4), + wwH16(18,BB,2D,5B,25,37,99,60,9F,4A,88,47,DE,61,E7,8D), + wwH16(74,A7,4F,E1,C1,BF,1F,AC,04,E9,A4,12,BC,4F,0B,8B), + wwH16(85,35,30,A7,22,05,24,14,31,1E,30,2B,3C,C5,15,1A), + wwH16(90,F7,D3,2F,16,57,E4,BC,48,18,94,69,17,E5,12,91), + wwH16(50,7D,F1,EA,E7,E0,5A,77,5B,29,E6,79,7D,42,B2,0C), + wwH16(66,50,89,33,D2,FE,8D,37,E1,D5,2A,A6,62,A9,C5,44), + wwH16(3F,05,B3,30,E6,B3,D1,D5,15,75,32,95,23,2F,C4,E4), + wwH16(EB,7B,93,F6,E0,4A,09,D6,11,BC,BA,CF,78,22,A9,48), + wwH16(B6,1E,5C,93,BD,5D,EC,7E,19,CD,2E,60,1C,2A,E3,43), + wwH16(31,BB,A4,B2,C5,75,1B,8D,1C,CA,2B,72,14,B4,6D,92), + wwH16(34,1D,18,BF,D4,D9,C8,CA,15,6E,B1,B0,DC,4C,77,D1), + wwH16(4E,87,00,2F,49,7F,41,59,6B,AE,F6,FB,E3,25,34,AC), + wwH16(9E,9A,54,0A,2E,56,F9,12,38,5A,90,9E,53,2C,78,63), + wwH16(85,19,4D,66,31,7C,C2,76,B4,A9,5E,A1,04,D2,B8,9C), + wwH16(D6,50,E1,CD,AA,88,BD,AE,32,E6,4D,C7,50,E7,91,87), + wwH16(16,9A,3C,47,2D,A9,59,76,BA,AC,7B,CF,73,0F,92,46), + wwH16(E6,91,38,CD,3B,E2,F6,EF,23,6D,9E,A7,BC,17,03,D1), + wwH16(D3,53,CB,7F,13,2D,18,93,E2,CB,80,41,C0,59,6A,0B), + wwH16(80,14,40,9C,50,6C,F8,46,6A,0E,33,C8,45,47,48,C0), + wwH16(FA,87,30,17,FE,10,20,C8,69,B8,4E,0D,14,44,E5,40), + wwH16(0C,78,D6,F4,1D,98,68,4E,7C,E9,4F,18,1F,34,20,EF), + wwH16(DE,39,5C,AA,51,66,2B,99,7B,AE,BB,9A,3A,87,28,58), + wwH16(AB,64,6D,AC,DB,5C,3F,DA,9A,83,8D,1B,29,85,14,72), + wwH16(A6,9C,4E,D5,C0,06,F8,A9,6E,E6,89,D8,50,60,78,E9), + wwH16(27,50,C2,7E,A5,85,3B,23,1F,A9,81,53,AA,C4,B5,AF), + wwH16(06,46,D8,73,68,B7,9E,D4,AA,3F,05,24,E3,18,25,5E), + wwH16(30,E9,A4,57,89,2A,4C,48,1C,7A,9D,EE,7C,37,5E,77), + wwH16(DB,E8,D0,91,C5,BB,EC,8D,17,2A,55,AF,08,DF,4C,38), + wwH16(FE,15,2E,AA,FC,9F,83,D7,F4,BB,F9,D4,B3,33,9F,02), + wwH16(38,01,52,3F,16,D3,AC,38,F9,B5,98,59,D8,CE,7F,80), + wwH16(4B,3B,E1,29,7F,F0,BB,97,A1,02,AB,60,89,EF,FE,D0), + wwH16(B9,01,A9,C3,39,D5,F3,6B,A4,2D,01,E5,68,1F,9C,BC), + wwH16(15,AF,02,FA,DD,62,4E,7B,B4,98,24,9A,72,2C,A2,21), + wwH16(4D,2E,9C,34,4D,0D,1E,C6,43,73,69,A9,99,C9,ED,14), + wwH16(A5,15,BC,78,55,7C,9F,83,16,91,3F,10,5F,3F,DF,14), + wwH16(A8,19,57,E6,1D,30,6C,EE,3D,4A,FA,93,27,48,A8,9E), + wwH16(EA,85,9F,EC,30,41,61,6B,0B,40,C8,26,D5,76,7D,31), + wwH16(58,A1,40,0A,0B,8D,A2,BB,D1,08,DE,A5,84,82,51,BD), + wwH16(D1,59,87,E4,8E,E8,39,CB,B2,A2,32,0C,36,35,64,54), + wwH16(1A,83,13,1B,31,2D,65,65,28,72,A4,D7,39,D1,13,13), + wwH16(5F,0D,CA,2B,D6,B2,AA,8D,BB,B2,57,13,E1,C2,43,8A), + wwH16(BB,FE,17,87,62,E3,53,09,7C,80,85,BA,26,DA,C2,DB), + wwH16(9D,A9,18,D0,34,61,FC,18,23,3D,29,4E,F4,C8,06,63), + wwH16(C3,5B,A5,08,85,2A,29,4F,DF,26,8A,40,81,88,8C,2B), + wwH16(AE,19,CE,3E,C0,73,85,B5,95,88,7A,7C,97,4B,F3,9D), + wwH16(02,43,11,73,B7,FD,C3,97,08,7A,7A,92,05,63,C3,53), + wwH16(92,19,18,BD,99,44,CB,83,47,12,D2,B2,38,0C,68,9D), + wwH16(1F,95,50,F9,42,28,9A,E3,25,BB,A2,48,04,00,FA,48), + wwH16(37,CA,5D,75,3D,E4,87,0B,FB,3C,F8,12,AB,CE,3E,FD), + wwH16(D5,7B,1E,A6,8B,1F,89,31,52,3D,1A,9C,F2,7B,79,0B), + wwH16(E0,EF,D6,45,4A,D1,04,C9,05,64,7B,C6,CD,AC,45,C3), + wwH16(EE,DF,47,92,E0,61,F7,C0,82,B4,CD,62,6C,6B,B6,A6), + wwH16(1C,CA,27,CF,F7,F8,76,EF,B7,82,C9,14,B5,59,0B,48), + wwH16(44,BB,CF,9A,B0,5E,1B,27,4F,F8,7A,69,E4,68,22,7A), + wwH16(7F,83,81,AC,42,C7,31,91,46,9C,00,50,5A,B0,23,5D), + wwH16(64,21,C6,D9,9F,AE,20,A2,CC,05,0D,63,77,37,F5,44), + wwH16(AE,C1,6C,74,DB,3B,9B,8C,4A,DF,73,AE,47,F4,AF,A1), + wwH16(A4,F0,C5,2F,5B,08,DE,CF,8C,85,83,06,AC,6D,10,2E), + wwH16(28,75,62,21,45,CD,0E,9D,49,D4,07,AC,6A,70,E6,7E), + wwH16(98,FC,FB,01,91,28,EE,21,DE,D5,5A,5E,B7,F2,FF,B1), + wwH16(8E,6F,8A,27,53,70,F5,32,92,AD,E0,42,AE,6A,A3,35), + wwH16(F1,96,75,44,B8,DB,78,C3,3B,67,E1,46,43,2D,E4,11), + wwH16(4A,07,A6,DF,41,5A,BF,84,96,33,4B,B9,1D,91,A0,AF), + wwH16(2F,3D,F0,4B,AC,69,D5,A0,FD,48,7D,0F,DC,5E,41,28), + wwH16(DD,77,2E,D6,2F,47,9B,B4,B6,6D,1C,7E,BA,B5,5B,0E), + wwH16(BC,84,48,AC,42,3B,67,37,06,80,D5,F2,FB,D7,E1,D6), + wwH16(21,47,A9,7F,22,D9,E2,98,39,26,55,4F,BF,62,A4,69), + wwH16(18,21,03,66,ED,7E,D9,5F,98,B6,70,53,95,34,DD,1B), + wwH16(E5,22,2A,75,FA,16,2E,13,39,6D,86,74,F2,AB,68,BF), + wwH16(BA,58,C6,8D,4D,CF,38,A1,CB,A2,DA,B6,63,71,32,D9), + wwH16(6E,49,E4,E5,8E,6C,B8,02,DC,90,6A,D4,81,77,48,7C), + wwH16(22,0C,5F,17,42,7A,FD,DC,E8,76,EF,C4,1D,5C,FF,92), + wwH16(7D,C7,71,C0,CE,94,70,A9,17,66,22,50,40,F4,3F,83), + wwH16(17,95,6E,22,8A,D4,F8,E3,37,3D,A6,D4,23,B6,6C,40), + wwH16(77,77,29,35,F8,B1,20,94,1B,5F,4C,CB,D6,03,2B,6C), + wwH16(7D,B7,C8,6C,D7,BD,D4,69,81,7C,BF,7C,CC,14,53,0A), + wwH16(58,FF,B1,27,23,92,1A,D5,15,A5,BF,9B,BF,B8,D5,E1), + wwH16(E1,D1,0E,FE,49,2D,76,84,EB,0E,67,8A,CC,57,5C,69), + wwH16(46,9A,6E,0D,D3,E4,9D,D8,0E,EA,D6,B9,BD,EB,58,D3), + wwH16(65,74,24,02,4F,F7,92,89,47,95,E0,A5,BE,C7,66,E5), + wwH16(84,25,8F,F6,18,DE,38,FF,E5,92,13,8D,0C,EC,2C,D3), + wwH16(93,CB,AF,05,BA,A1,B9,4D,52,BD,58,A6,EB,91,CC,B8), + wwH16(12,4C,08,92,F6,E4,46,66,8B,48,68,46,7A,40,A5,16), + wwH16(F5,4C,C5,3D,07,71,D0,17,48,97,0E,EE,D6,89,1A,BB), + wwH16(64,7B,2D,85,E4,D9,13,28,51,9B,23,26,0D,F0,5C,7C), + wwH16(8A,B3,45,F4,58,56,B7,40,A7,2A,48,3C,8C,8C,9B,83), + wwH16(A0,A3,4F,C2,23,38,1B,21,C6,0D,F2,47,EF,00,1C,E5), + wwH16(6C,86,CA,11,B7,B0,0A,4B,93,56,B0,16,98,16,2E,40), + wwH16(67,C0,23,8E,A4,64,7C,18,71,51,2D,FF,46,F7,38,7E), + wwH16(A3,B4,19,75,D9,D1,FD,57,90,92,F3,A4,AB,EE,AD,F5), + wwH16(9F,BE,2F,EE,0E,2E,EA,50,76,A4,D5,B7,58,C1,03,3B), + wwH16(00,17,BB,01,51,BD,0B,89,A7,3D,29,2A,F2,E3,19,E8), + wwH16(EC,8E,DD,3F,13,AB,31,FD,FA,BE,AD,C4,A3,C9,CB,56), + wwH16(69,9C,99,BB,FA,F5,0E,3C,68,35,C7,09,3A,0B,A8,AB), + wwH16(DE,AB,C9,3B,0F,D0,41,EE,30,B6,77,AF,54,4A,10,48), + wwH16(2B,A9,95,C0,E2,E3,30,A7,66,D7,9D,FD,30,7B,DC,70), + wwH16(F8,6D,FF,D7,C6,33,24,38,F2,B5,A9,CA,A2,AF,BB,EA), + wwH16(56,71,03,E4,C4,EA,46,95,4E,88,AC,25,53,26,B9,44), + wwH16(51,59,4A,2A,7C,D2,D2,0B,55,45,E8,32,21,F1,56,CA), + wwH16(F1,0C,50,53,8D,5C,2D,0B,27,7B,50,61,16,39,83,3D), + wwH16(43,ED,D0,94,BA,A7,C2,28,A1,07,1D,C5,76,8B,88,B6), + wwH16(AE,96,8C,95,17,6A,82,47,5B,D3,07,9E,92,EF,1E,03), + wwH16(B4,FE,33,04,EE,B0,EB,12,4F,98,65,F3,EC,2F,9B,0D), + wwH16(49,F8,F1,09,9D,4F,3D,72,9A,63,65,30,51,F0,E6,E3), + wwH16(21,3F,9D,2C,4B,35,D6,C9,6B,81,D7,28,27,02,3F,D2), + wwH16(22,8F,A9,96,CF,96,38,80,8B,D1,98,7D,70,16,FB,30), + wwH16(EA,43,BE,94,85,8C,DA,7A,56,E5,91,BD,EA,FD,EC,18), + wwH16(7C,6C,2D,E9,61,4D,D6,E0,42,B5,96,FD,C5,17,B2,5C), + wwH16(E9,20,54,15,D7,9D,5B,6F,72,7D,90,CA,19,F4,9F,73), + wwH16(37,09,CD,32,99,BF,3F,8D,65,00,66,FF,C0,E2,B4,15), + wwH16(8B,A5,CF,76,C4,F5,A3,99,BD,86,AF,D8,23,5A,64,F3), + wwH16(A7,BC,59,A4,38,E4,A3,37,EE,AC,77,CD,19,90,9E,B5), + wwH16(A8,18,62,35,15,E9,8B,F3,BD,4D,7F,AE,88,6F,03,34), + wwH16(EC,3E,DD,3A,66,43,F1,FF,B0,30,86,0D,99,4B,F2,02), + wwH16(2B,C4,A4,CE,57,86,8F,24,2A,0B,BD,66,4D,84,DB,3A), + wwH16(BF,E8,CF,5C,90,BB,99,EE,CB,82,B2,2C,0A,1D,33,37), + wwH16(ED,87,09,3A,49,EA,0B,74,2C,66,8D,A8,CF,C2,BE,0E), + wwH16(68,57,A8,78,1C,DE,C8,58,60,EA,D7,7F,0F,DA,F7,2B), + wwH16(1D,24,F3,C1,E9,32,19,CB,00,DF,BE,26,A8,F9,78,98), + wwH16(97,AC,46,B0,2A,98,37,15,EE,A6,22,CE,08,15,48,D9), + wwH16(6B,E0,81,6B,48,0D,43,82,00,B4,21,40,49,87,40,5B), + wwH16(D4,4B,FD,BE,A0,07,20,E8,38,E5,94,93,51,3E,73,B9), + wwH16(7C,B3,39,7D,D9,59,28,C7,8C,10,AF,B6,69,02,A7,9A), + wwH16(03,2F,6F,20,C1,AE,60,B4,01,CA,39,FA,CA,4B,F7,44), + wwH16(F5,45,5E,3F,9B,77,3D,3B,54,ED,5F,D9,4F,AA,6C,1E), + wwH16(CB,66,DB,0A,27,DA,9D,A7,BB,CE,F7,46,A4,EF,6F,BE), + wwH16(54,E6,86,CC,04,64,D9,24,14,89,52,AA,1F,30,2B,67), + wwH16(0C,13,CC,FD,CB,53,08,21,A1,43,6C,33,BB,AF,C2,70), + wwH16(43,E5,77,80,7E,62,9A,15,3C,77,B6,2A,06,B2,0D,D7), + wwH16(CB,CF,09,1A,69,D9,11,96,F5,7D,9D,F6,3E,B9,47,1B), + wwH16(0C,A1,D8,A0,4E,EC,1E,77,AD,6B,7D,6D,46,88,FA,D1), + wwH16(B0,60,B6,B0,F3,BC,E4,EF,CE,82,1F,43,8B,64,30,77), + wwH16(37,CD,CE,3F,2B,1B,EB,5E,7D,D5,A4,18,B0,1C,B0,41), + wwH16(AC,28,A5,2E,6E,44,F6,94,D3,D3,C4,45,0D,E0,ED,14), + wwH16(ED,8A,75,A6,40,F9,2A,23,6F,65,8F,47,8F,A9,DA,CB), + wwH16(88,F7,BF,B8,9C,C2,43,37,00,45,C8,E3,D4,CA,1D,11), + wwH16(E4,46,E5,B9,4D,AA,EA,9C,1D,16,88,10,F7,83,3D,27), + wwH16(F9,24,0A,2B,EE,DC,10,15,60,9D,9E,F7,9C,F3,83,DB), + wwH16(44,12,66,D7,43,FB,25,B9,FC,04,D8,21,82,BB,18,22), + wwH16(22,CB,72,C1,77,72,E0,B9,D9,BB,62,39,FD,66,0B,EE), + wwH16(2F,9B,45,38,DC,97,CE,77,E2,07,34,49,C3,FE,C0,6A), + wwH16(9E,F1,43,77,93,62,2C,75,EC,33,D2,DD,A0,B2,14,72), + wwH16(0E,75,15,D4,7A,E8,16,7F,DE,E3,BF,01,11,22,CC,2F), + wwH16(12,B4,39,89,68,F3,C2,81,6C,5C,49,87,7E,EA,9C,3C), + wwH16(E7,53,11,7C,D0,F8,0F,BA,53,BC,B3,C2,D5,64,17,5B), + wwH16(6A,06,BA,66,8F,9E,81,C2,1A,D2,AC,95,48,F6,2E,3B), + wwH16(5F,68,D9,CD,8E,8F,80,57,5D,23,66,CB,89,F3,38,61), + wwH16(01,29,FB,04,99,97,D5,1F,4C,27,F1,69,44,E1,57,7C), + wwH16(66,7D,64,F4,C6,AC,DE,70,34,65,B7,70,B0,C0,87,B0), + wwH16(EE,8D,3F,6F,3F,39,FB,B7,F8,92,58,38,8F,6F,4E,23), + wwH16(0F,E6,64,0C,97,7F,C4,9B,29,04,AB,4C,AD,5B,02,91), + wwH16(54,0A,86,D6,7C,5F,36,51,18,ED,78,FC,B9,20,B3,E4), + wwH16(1D,E8,B3,D2,D7,94,09,DC,C0,81,A5,65,5E,13,18,D8), + wwH16(AC,73,38,EB,81,F5,65,86,50,B0,BD,7A,ED,A8,3C,62), + wwH16(2A,87,71,A1,FE,39,93,53,A2,38,2B,92,D3,64,D2,89), + wwH16(F6,97,D0,54,68,AC,05,BA,B4,1B,03,5E,25,FA,64,45), + wwH16(52,AA,D3,15,02,13,5D,26,38,53,22,BE,6A,9C,B6,94), + wwH16(6A,59,29,C7,E5,05,EC,00,9B,21,C6,C9,02,6E,75,5D), + wwH16(5E,10,C5,3C,CF,84,F1,97,E6,62,C2,D8,38,78,BE,7F), + wwH16(5B,7A,AE,B1,85,76,D5,6E,3A,EE,FA,7D,0F,E1,29,41), + wwH16(AE,EF,47,69,98,5B,41,36,1A,A3,D0,66,0A,35,A5,5E), + wwH16(8D,CC,A2,30,0E,D7,2C,9E,1B,FD,5E,C9,66,A7,F6,12), + wwH16(1D,E3,AF,80,76,80,3A,E3,4A,E9,0F,09,CB,C0,03,4E), + wwH16(90,B6,7F,EB,00,10,8B,7D,78,E6,A0,CA,FB,38,7A,3F), + wwH16(AC,C2,1A,0C,42,B3,5B,7C,EC,B0,E4,BA,75,04,7E,3A), + wwH16(5B,D4,CE,50,16,31,6F,F3,9F,DA,B4,53,AC,0B,12,DD), + wwH16(7E,B0,43,74,48,97,4F,49,F2,A5,31,1A,DA,E5,AB,76), + wwH16(C0,F8,D7,D4,74,FA,EB,E1,C3,47,B9,A2,DF,B0,0F,59), + wwH16(06,73,E6,B3,DE,E3,E7,32,B8,F1,CF,E8,01,3D,77,80), + wwH16(51,BA,C9,1A,CB,5C,31,15,6B,D3,EC,0E,B3,1C,53,D8), + wwH16(CF,E2,3D,52,8F,4A,01,E0,46,BB,10,C0,FF,01,16,21), + wwH16(41,B7,6B,B9,CF,81,47,D6,9F,16,70,65,FD,56,1E,A3), + wwH16(1C,38,81,4F,D4,D2,1B,56,2D,82,B8,0A,B8,AC,98,D7), + wwH16(B5,98,53,9C,83,5F,27,A6,43,EA,A5,36,D3,E8,5E,C3), + wwH16(F8,47,0F,57,3C,5D,15,B9,0E,FE,E6,BC,16,6A,83,B2), + wwH16(FA,CF,D4,0B,AB,D0,65,27,94,02,2B,63,3F,F4,E3,BF), + wwH16(A5,44,24,CD,8B,4C,7B,33,1E,A2,89,CC,43,C8,E5,37), + wwH16(17,F0,4A,4F,B8,20,B7,82,17,F2,26,37,7C,14,17,80), + wwH16(04,8D,53,EC,D9,90,52,F4,48,D5,CA,9D,B9,AA,E3,7D), + wwH16(7A,18,01,B4,A1,77,91,8E,C2,B4,6F,7F,61,82,C7,B1), + wwH16(29,D2,11,65,A2,F9,A8,9E,AC,04,D7,A7,7B,CD,34,F9), + wwH16(3E,E5,C3,C5,38,93,5A,46,D5,A5,E4,24,31,5A,86,33), + wwH16(D1,7C,CF,64,E4,F7,19,9A,95,45,01,B1,40,6A,09,FD), + wwH16(8A,D0,E1,74,08,77,46,23,8D,90,F5,F7,30,88,0D,F7), + wwH16(32,AC,66,57,B7,99,CD,E1,5C,7C,D7,D9,02,93,77,9C), + wwH16(5A,1C,FF,0B,87,AE,17,7F,AE,74,F7,80,B7,05,DF,F7), + wwH16(89,4F,3E,C3,E5,30,A7,1B,36,97,F0,8B,4A,4B,AB,6F), + wwH16(3E,58,3F,3C,0B,33,6E,E9,4B,3F,8E,33,94,DF,B0,68), + wwH16(54,12,60,58,70,7E,C9,E8,A4,CE,E5,81,CA,E4,13,67), + wwH16(F6,C5,0F,A6,8B,D3,41,3E,09,72,AD,79,11,5A,E0,A3), + wwH16(32,9E,A7,87,23,25,CC,3A,64,F5,85,AE,47,D6,D5,AD), + wwH16(E4,45,FB,7A,77,39,3A,E7,63,FB,D3,58,1E,7A,E2,F7), + wwH16(E1,D2,3D,4C,6B,DA,8B,EC,67,CE,A7,50,BD,65,94,47), + wwH16(FC,48,9B,01,3F,F8,09,73,73,48,CF,3E,1E,B3,4E,BA), + wwH16(83,EB,63,80,3E,63,38,18,6E,7F,DB,47,C5,71,A5,4E), + wwH16(27,DD,84,9C,F3,5A,72,0E,F2,52,0A,1A,D2,4B,A4,CE), + wwH16(6C,93,6E,DF,DF,93,46,7D,54,23,56,3E,10,31,69,0D), + wwH16(2C,83,1C,47,C9,10,26,8E,FE,AB,3A,A4,65,C0,15,CC), + wwH16(52,B9,4C,A3,1C,77,A0,20,26,97,BC,42,60,CC,F4,A9), + wwH16(C0,8D,1E,69,74,92,DC,97,DB,3F,62,13,E6,19,58,70), + wwH16(1D,C0,CB,C1,E2,63,6E,6F,A7,E4,53,53,6E,9F,14,07), + wwH16(CB,8E,30,A3,F3,40,5E,B5,3D,1E,CC,8A,E0,18,9A,F8), + wwH16(6C,C4,91,C6,4C,18,55,3B,75,C4,41,D1,AE,7A,A5,28), + wwH16(65,DF,E1,8C,75,DA,93,D5,3D,9D,A8,E6,31,8E,BD,E3), + wwH16(94,73,C9,68,A2,7C,96,17,72,08,97,2F,78,A3,F3,4D), + wwH16(92,6F,93,6A,FF,86,D8,F9,1C,EE,02,83,54,F7,BD,2F), + wwH16(09,CA,60,05,1C,07,EC,53,5E,5B,76,55,12,00,C6,B4), + wwH16(B1,C7,32,43,C3,BE,3D,75,8A,F8,AF,08,C3,9A,DB,FC), + wwH16(77,BA,95,29,B0,78,99,E0,4B,2C,16,5A,41,08,49,DD), + wwH16(C6,8B,FC,03,83,59,96,4F,D1,30,3F,AD,B8,24,18,9E), + wwH16(F2,54,15,2F,C0,7B,2F,32,4F,32,CA,44,54,92,21,0F), + wwH16(9B,D5,41,7F,7A,07,F2,7F,19,D0,B7,34,59,1F,D8,78), + wwH16(AE,D5,47,F6,12,5C,92,54,A7,C9,F2,34,41,FF,E6,A2), + wwH16(EA,D9,6C,93,F2,0D,08,B3,FF,3C,10,69,17,FA,D8,62), + wwH16(DD,1E,4B,52,46,80,4E,D9,9D,51,27,EC,8A,80,D1,21), + wwH16(24,A8,12,FD,7E,D5,04,7E,16,ED,51,ED,87,FF,66,EB), + wwH16(82,6B,16,A3,1D,F5,4A,A2,65,28,E6,F0,D6,B3,87,A2), + wwH16(BC,A9,C3,EF,F9,A1,E9,78,31,D3,5B,3A,AD,E5,26,62), + wwH16(CB,DA,24,4F,88,C3,77,B6,F0,75,99,F9,79,49,C5,58), + wwH16(5E,5B,3A,13,00,AC,B5,89,7E,8E,04,70,08,6D,80,2F), + wwH16(EC,7C,44,A4,0B,00,77,EE,23,33,B2,2B,43,6D,71,D7), + wwH16(95,50,BE,86,4E,76,92,19,9B,BC,58,62,AB,D4,DB,BA), + wwH16(D7,30,AF,10,51,EB,D6,65,18,7B,85,0D,C5,17,FD,E5), + wwH16(BF,CE,96,DE,BC,C1,CB,E4,5A,02,63,98,96,BA,1E,81), + wwH16(D0,59,3F,38,FC,4C,A6,99,74,A8,0E,1E,AB,C9,18,88), + wwH16(28,8A,71,51,83,24,DF,D7,65,1B,D2,43,E0,37,3A,BC), + wwH16(31,DE,8C,2F,FA,4F,B8,95,BE,98,22,8F,24,FB,24,D0), + wwH16(75,C4,47,8B,4D,4F,49,E2,3D,6D,C5,7D,18,AF,90,DE), + wwH16(E4,10,A8,75,BA,84,0C,C1,2F,46,28,C9,7B,23,DB,4A), + wwH16(D0,C7,C1,5B,4F,09,AF,A6,DF,E8,89,06,F8,90,B9,DB), + wwH16(1B,CE,6D,AE,DB,E9,86,B3,A4,20,A2,5F,BA,18,8C,8D), + wwH16(BA,EF,2C,3D,2E,AA,ED,8A,F1,38,AE,AD,11,F7,78,75), + wwH16(04,77,53,E1,DD,2F,60,25,F9,11,C4,BA,A0,07,E3,54), + wwH16(DC,BB,84,18,3A,1B,59,5C,46,F1,39,11,75,0C,8E,82), + wwH16(C9,56,D7,71,60,CE,53,76,A8,A5,88,C9,64,0A,24,B8), + wwH16(F6,6D,88,29,31,7F,48,07,F1,E0,9B,B9,1D,E3,31,76), + wwH16(88,C8,89,9C,75,0C,BA,DD,B6,6E,E7,25,CB,7E,B3,01), + wwH16(AA,42,47,0C,D7,4C,E5,E0,E9,45,78,7E,CD,11,DC,2C), + wwH16(9A,C4,65,AA,62,83,42,BC,61,0A,44,38,39,7D,0F,77), + wwH16(0E,51,AF,40,9F,8D,19,8E,4A,99,D0,0B,E4,5B,7A,5B), + wwH16(D2,98,6C,F3,E2,E4,AE,12,90,3D,D7,52,1E,CB,57,91), + wwH16(43,F7,28,58,13,29,E2,AD,26,80,1A,2E,31,8F,91,F4), + wwH16(F1,D2,76,2D,DE,6D,24,A0,BE,5D,B6,E1,48,3A,72,E6), + wwH16(DE,74,01,CF,26,CB,CD,FD,23,FC,70,A8,89,04,46,E3), + wwH16(2F,B9,C3,A5,98,19,26,E1,FD,CC,A2,68,CA,FE,30,0A), + wwH16(56,1D,2B,91,1A,11,23,ED,E2,94,C4,87,C8,37,96,D6), + wwH16(E0,1A,FB,B2,72,F5,E6,12,57,D2,35,93,45,C0,1C,7E), +}}; diff --git a/src/crypto/bign/pre/bign192_pre_si8.c b/src/crypto/bign/pre/bign192_pre_si8.c new file mode 100644 index 00000000..b050d6a0 --- /dev/null +++ b/src/crypto/bign/pre/bign192_pre_si8.c @@ -0,0 +1,784 @@ +/* +******************************************************************************* +\file bign192_pre_si8.c +\brief STB 34.101.45 (bign): precomputed tables (l=192, type=si, w=8) +\project bee2 [cryptographic library] +\created 2026.03.05 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +\remark Built with bignPrePrint("1.2.112.0.2.0.34.101.45.3.2", ec_pre_si, 8). +******************************************************************************* +*/ + +static const ec_pre_t _pre = { + ec_pre_si, 8, 48, { + wwH16(0B,1C,8A,9E,69,68,4E,CB,E0,00,CE,75,B8,79,BB,7D), + wwH16(D9,6F,79,B5,E5,54,54,19,45,5B,37,E9,98,52,A0,63), + wwH16(22,47,DC,DF,4A,C3,64,79,6B,7D,0C,C0,C3,42,A1,E0), + wwH16(36,D2,9C,5D,BE,D5,90,56,CF,76,78,6B,F6,BE,8B,FF), + wwH16(DD,4B,45,E5,72,17,23,2A,76,74,7B,E7,23,7B,BB,5E), + wwH16(69,70,D6,BA,AF,14,E7,9E,89,48,97,21,0C,D2,87,E6), + wwH16(E9,80,40,AE,EC,CC,DC,8F,9A,A9,23,A1,DB,7F,59,80), + wwH16(8D,71,2B,3D,DE,46,C5,70,15,8D,39,18,A7,DE,FE,14), + wwH16(A4,B7,3C,28,CF,48,BE,0A,1B,6B,C8,00,FE,0A,A4,4D), + wwH16(78,10,31,5D,BE,FB,39,B8,3B,34,DA,C3,54,66,A9,51), + wwH16(AB,A0,8B,4F,D6,40,B4,69,72,3F,D3,40,D3,D8,20,02), + wwH16(36,E3,A1,6D,05,31,58,5E,D5,D6,B4,FA,EF,4D,D1,3D), + wwH16(C8,C9,71,12,20,61,45,7D,1D,45,F4,11,A0,4F,07,E6), + wwH16(4F,22,63,76,C1,FB,69,1E,31,EF,40,FB,6A,74,36,8F), + wwH16(04,3D,AA,98,17,9D,63,1A,0F,1A,B2,81,CF,43,2A,ED), + wwH16(86,16,49,45,83,0C,76,5D,AC,63,9C,AB,5D,4F,8A,FD), + wwH16(B8,EC,71,A8,05,3B,48,88,ED,C6,9B,27,C5,32,05,60), + wwH16(0B,14,DB,FD,21,1F,DD,6E,12,5F,A2,FA,43,DF,2A,8C), + wwH16(F2,FD,73,3E,14,59,07,9D,1A,FD,B7,73,F2,8E,F8,3B), + wwH16(2F,31,BB,7C,35,C4,9A,0C,32,B3,D6,E6,C7,3B,AE,34), + wwH16(4E,43,72,7B,FA,B6,67,26,EC,B9,4A,5D,85,A2,A5,C2), + wwH16(F1,4F,BB,F4,56,96,C8,F2,A7,08,59,2B,55,66,49,10), + wwH16(81,75,B8,B5,1D,60,71,41,D2,E8,64,55,18,ED,69,FC), + wwH16(16,73,03,CF,3A,14,83,FA,88,F1,D1,71,A5,1E,A0,E5), + wwH16(B1,58,55,E4,AB,F0,1D,E8,55,EA,2C,0C,C7,63,75,25), + wwH16(E2,04,9D,90,24,E0,E7,C2,73,24,7F,2C,BA,4C,4D,39), + wwH16(F6,A8,55,76,08,70,AB,36,B9,5C,B7,59,C7,6C,AE,D6), + wwH16(D9,9C,F2,BD,25,DD,BB,F0,AE,7D,B4,6B,5A,BC,C5,4B), + wwH16(14,4B,E3,73,7C,EA,78,3E,CA,53,42,5B,A5,F6,8E,6B), + wwH16(1B,6E,0E,E3,32,11,AB,3D,93,92,47,F5,64,8B,36,8B), + wwH16(54,01,2F,B2,29,A8,A7,99,30,3F,EF,DF,47,EC,5C,D8), + wwH16(23,E8,1A,A8,24,DC,B9,22,12,4E,F1,DA,84,2C,F1,D5), + wwH16(03,AE,95,06,F7,C8,6B,DF,96,26,C2,71,1F,7C,AC,D9), + wwH16(85,90,B0,EE,78,B8,B7,D4,79,5F,05,49,D7,B5,87,FE), + wwH16(9F,35,75,24,E0,E4,68,10,12,08,8A,23,C4,7B,74,67), + wwH16(8D,08,B4,12,97,B2,A3,0C,DB,49,FD,BC,81,59,BF,4B), + wwH16(F6,79,1B,53,51,B3,A3,08,98,61,2E,C2,2D,CB,3B,EA), + wwH16(6E,0A,14,3A,00,0C,68,64,D8,7A,AD,DC,2B,B3,D0,1A), + wwH16(29,AE,CA,1B,4C,5C,D3,44,A6,9D,3A,8A,49,E6,F8,65), + wwH16(05,5B,4E,0F,61,BE,58,35,26,8B,5A,18,DA,54,5C,93), + wwH16(92,71,6D,65,16,E5,53,2E,48,09,AA,A8,A3,A8,E5,E0), + wwH16(47,14,DF,D8,F3,80,D9,7C,F7,27,F5,05,61,DB,FF,08), + wwH16(A0,27,89,D2,93,61,D4,76,65,F7,8E,63,5D,2B,EB,17), + wwH16(9C,55,75,66,F9,CB,A2,57,EF,D8,DD,9B,FE,3D,85,6B), + wwH16(81,4C,BF,AD,B2,E7,F7,4D,80,D0,C0,74,A4,E9,1F,C8), + wwH16(5D,91,09,02,20,A7,9F,CC,83,FE,DF,39,01,59,88,3E), + wwH16(2F,39,02,F5,98,8D,98,00,75,75,61,14,56,94,01,2D), + wwH16(C5,17,52,58,A4,E0,54,33,12,0A,06,B2,8C,23,FB,F3), + wwH16(44,D5,F4,26,6C,F1,95,EC,4F,83,1F,88,5D,2D,C2,C5), + wwH16(B2,4B,D7,48,11,DC,A9,28,06,57,D5,4D,DD,0F,2E,3D), + wwH16(BA,31,8A,E5,DC,2B,62,58,62,43,0B,45,BF,7A,E3,FF), + wwH16(69,00,83,34,5D,15,FE,07,63,66,1A,38,C9,A4,81,F2), + wwH16(73,BA,06,77,E3,9F,F7,90,D4,4A,87,3A,68,53,3F,07), + wwH16(40,11,A4,D8,E1,9D,31,F5,1A,89,3A,6E,5E,AB,32,61), + wwH16(0D,AE,83,E7,07,C9,9C,C8,66,91,CF,9A,57,52,34,1D), + wwH16(87,2D,63,6F,70,FE,5C,43,2A,3B,8D,00,C9,95,4E,54), + wwH16(FA,86,27,E4,A4,0C,BF,93,8F,74,94,BF,CC,C9,3A,50), + wwH16(75,6A,24,8D,16,CC,09,BA,0D,5D,FA,77,5D,F1,DD,A4), + wwH16(C1,73,A9,09,C9,36,A9,A6,E6,84,C6,EE,4A,5B,8B,6B), + wwH16(E2,80,D3,48,11,94,B1,C2,DF,AF,07,FB,93,A4,65,0A), + wwH16(D5,0B,C3,12,83,6C,9A,98,69,5B,59,04,25,01,DD,82), + wwH16(29,B6,4F,97,CC,DC,D6,A8,91,D8,B6,CF,4C,F8,C6,E0), + wwH16(F5,B6,A9,4E,3E,93,59,1C,24,0B,60,0B,9C,9C,5A,E2), + wwH16(60,7E,BF,9E,9D,74,62,32,E2,BB,32,73,F9,B8,E1,82), + wwH16(90,5F,48,61,D3,EC,4F,0E,50,12,2A,9B,5F,42,73,2C), + wwH16(66,68,D4,CE,95,32,C0,B2,DD,75,A3,8D,7C,41,5B,5F), + wwH16(F5,C0,86,A6,87,24,98,C5,37,67,BA,47,74,45,EC,71), + wwH16(59,0D,3B,90,E9,A3,C0,D7,A9,52,AC,26,63,A5,C9,DC), + wwH16(2F,2A,D7,D8,BD,6D,6A,E0,E7,6F,F1,65,02,BD,8A,95), + wwH16(9F,E9,84,F1,BB,C8,E7,1B,CD,A4,28,8F,41,3D,93,51), + wwH16(6C,30,29,B7,B9,BF,78,EB,48,48,16,26,AD,30,2C,2F), + wwH16(45,77,89,5E,3E,3E,5C,E8,AD,D2,EC,30,9D,5B,54,13), + wwH16(84,BD,07,B6,F0,0B,3A,CF,DF,77,76,88,FE,DD,D8,0F), + wwH16(D8,D0,53,40,F7,14,09,D3,61,83,38,23,30,60,0A,AD), + wwH16(05,22,8E,98,05,16,F1,03,B9,B7,56,E5,A8,DF,12,23), + wwH16(AD,AD,85,BA,DE,62,16,D3,27,70,71,47,33,56,0C,47), + wwH16(17,B6,D1,F6,88,ED,B9,C9,97,77,15,4C,9A,D0,D7,B5), + wwH16(FB,10,5C,BD,F8,82,47,CF,3E,51,7C,0E,F7,AE,72,73), + wwH16(9F,7E,A1,55,BB,BE,01,5A,E4,96,AE,25,DB,AB,37,84), + wwH16(6A,B4,0F,16,DC,6E,08,1F,33,9C,05,A9,21,CF,AC,CD), + wwH16(5C,D0,6C,D5,AB,46,D9,EA,20,1C,78,A8,76,87,20,A1), + wwH16(E7,94,45,3A,02,B7,5E,11,0A,8C,39,AC,6B,D9,48,D4), + wwH16(05,CA,7D,D0,DA,C0,AF,89,6B,83,7B,DC,42,E5,C0,BD), + wwH16(2E,56,BA,25,ED,F6,6B,F0,66,CE,47,3C,CD,C3,92,E1), + wwH16(2F,85,FE,AE,0C,48,62,31,60,09,A6,0B,28,49,A2,4A), + wwH16(90,F9,4B,CF,6A,0C,B4,F1,45,21,D0,33,3A,08,B6,4D), + wwH16(AD,E9,A6,29,A8,99,14,DA,7E,06,D7,80,B2,5B,07,E4), + wwH16(26,3D,75,A6,25,D9,BC,DF,07,1E,32,D6,4E,03,AB,5E), + wwH16(8D,D2,08,D9,EF,2A,63,83,0D,4A,8A,24,CF,D2,6D,8F), + wwH16(AA,BD,79,08,8F,54,A0,8B,02,F4,94,3F,F8,0A,68,EB), + wwH16(78,D9,D8,D0,F8,9B,F3,99,41,88,A3,D4,16,0A,74,85), + wwH16(A5,73,AE,90,F2,BE,4B,1E,A3,9F,C2,1F,97,03,86,26), + wwH16(3C,46,A0,CC,75,20,C9,0A,F1,48,A1,AC,60,0C,E5,8D), + wwH16(93,7C,23,71,9B,62,2E,E9,E7,C5,EB,A0,D8,FA,77,19), + wwH16(B9,6A,0A,53,66,89,72,47,11,4B,2C,C5,DA,15,7B,A1), + wwH16(06,B4,1E,04,17,39,08,41,E0,9B,06,20,5B,8D,CD,52), + wwH16(BF,AF,FF,E2,38,6D,EC,3D,A3,60,F2,D7,62,1A,7E,9C), + wwH16(28,EC,9D,A0,25,A0,A6,02,C1,5D,8C,69,11,04,FC,21), + wwH16(73,40,06,62,B7,41,AC,DA,F7,1D,96,94,4D,5F,11,57), + wwH16(52,11,FA,D0,EB,13,0B,14,CD,74,1B,2B,BA,C4,13,79), + wwH16(DC,95,97,3D,32,BD,4F,96,73,BE,13,6F,56,1F,01,32), + wwH16(0E,26,2A,9B,19,6C,D4,6F,E5,63,22,0A,B4,71,94,30), + wwH16(B9,2B,0B,7E,AB,C5,DA,3D,A4,0D,E4,37,2E,DD,A2,50), + wwH16(C3,20,24,77,DC,EF,0B,45,CC,3B,97,BA,AC,F3,8C,5F), + wwH16(00,81,23,D0,AC,06,21,FC,79,EC,0B,E7,23,E0,40,CB), + wwH16(AC,2A,56,6C,2F,D3,64,A9,AE,DF,B1,80,E4,12,1C,CE), + wwH16(03,0A,C3,21,CD,97,92,C4,F6,F6,52,AB,43,B0,10,4C), + wwH16(43,7B,18,AD,7C,26,37,D8,8C,C2,B7,1E,18,F2,E3,02), + wwH16(FF,61,F0,7C,2A,64,F7,24,5A,38,21,6F,0F,9F,BE,A3), + wwH16(06,5A,67,C7,60,56,57,24,22,FC,2D,BD,CC,2D,A3,F1), + wwH16(4C,CC,97,B1,03,8B,29,D4,59,4F,13,96,65,65,AE,6F), + wwH16(4C,B6,9C,2C,C0,45,11,26,58,59,1C,FE,6B,57,AA,FB), + wwH16(8C,60,0D,78,23,CC,A9,E1,EC,29,CF,13,3A,F3,43,8F), + wwH16(BD,11,F5,79,55,DA,D4,9A,C3,44,0D,1F,EA,BC,D5,23), + wwH16(31,F9,25,FD,F8,E3,23,54,C8,AA,BD,BF,37,E1,51,71), + wwH16(7C,84,3E,9C,AF,B2,46,BA,22,95,D6,EA,66,57,7A,53), + wwH16(13,05,EF,06,04,C4,50,F6,A1,6E,D9,7B,EE,52,D9,3D), + wwH16(36,BA,D0,9B,F6,BC,1D,5C,0C,0C,A0,2F,1A,0C,01,36), + wwH16(81,B7,3A,62,2E,02,5A,DA,96,29,0B,72,0B,B3,99,5F), + wwH16(5B,DC,79,16,7F,46,62,49,62,12,D5,49,23,8C,0B,25), + wwH16(49,93,5C,B7,2E,97,1A,E1,82,F7,B1,B6,4C,99,5D,15), + wwH16(E5,75,9B,67,1E,ED,05,D4,62,5E,EB,6A,01,C2,3F,D6), + wwH16(D9,72,1E,63,C8,D7,99,9A,9B,91,58,2E,94,39,75,27), + wwH16(3B,7B,29,6F,19,26,56,8B,E7,74,63,00,07,78,6F,3D), + wwH16(44,23,30,6A,0A,F9,D9,69,92,1F,C2,66,51,EE,CD,82), + wwH16(AD,8B,C0,7D,48,A9,52,60,AF,2B,3C,3B,C3,88,2C,A4), + wwH16(54,DD,48,2A,A1,4E,75,4F,DC,CD,68,C2,3C,C2,78,9C), + wwH16(FE,7A,44,B4,CE,08,E6,8B,D3,F4,31,6A,63,EE,D7,74), + wwH16(79,CC,AE,06,AB,8B,7D,DF,BF,87,D2,69,00,BF,5A,44), + wwH16(0F,52,56,70,4C,7F,D1,2A,9C,82,A9,2C,2A,8A,F4,22), + wwH16(DD,1D,16,EC,6C,24,E2,01,E9,6C,72,BB,B8,78,81,A6), + wwH16(B6,C2,47,ED,29,A8,3D,11,0C,A8,D5,58,9D,C9,82,9C), + wwH16(BE,DE,C8,DD,3E,F2,07,63,14,21,E7,F0,0A,14,3C,C0), + wwH16(24,7F,A8,9B,A1,BD,D4,BB,72,05,BA,FE,83,79,21,32), + wwH16(07,BA,EF,3D,5A,E7,7E,D1,BA,9E,C9,AA,91,D9,0A,49), + wwH16(F5,9A,B4,C7,1B,36,D7,9C,F7,FD,FE,F5,9F,60,96,96), + wwH16(80,51,5F,37,B9,BE,4A,A3,E0,9D,77,88,39,D7,73,FC), + wwH16(DD,AB,8F,75,ED,87,2D,FA,C8,79,4F,17,FC,9F,57,6C), + wwH16(80,40,67,9B,E0,D5,73,1A,EC,A3,3A,B6,31,D7,95,16), + wwH16(14,8C,04,49,CC,1F,B3,88,88,2A,98,C6,EE,C6,52,3C), + wwH16(10,5F,BF,52,89,1F,21,35,27,27,CE,99,78,6F,0B,46), + wwH16(AB,3D,2F,84,F7,1F,08,D6,CD,97,33,A7,FB,97,5F,09), + wwH16(AF,76,C7,CA,AB,5E,8A,5F,3D,84,47,EF,E7,4C,C5,74), + wwH16(40,E8,48,BF,8D,41,6F,C8,72,5E,75,B6,B7,65,93,0C), + wwH16(5D,9B,B4,2D,DA,03,5C,A8,48,FC,7A,9F,BB,F1,40,0B), + wwH16(53,89,60,2E,22,71,E5,32,85,B3,40,B3,32,4A,43,E3), + wwH16(1E,79,E2,18,86,09,D9,DD,3A,6E,B4,AF,79,DA,E5,F0), + wwH16(CC,15,21,2F,A3,A8,E9,5D,BC,58,EB,92,92,84,73,F6), + wwH16(57,C7,F9,49,38,26,90,B2,DD,D0,CF,B6,EB,A9,66,D4), + wwH16(A3,7D,6C,37,8C,87,5D,00,EC,6D,0D,AF,26,CD,22,87), + wwH16(36,84,83,DD,E7,F6,B2,C2,BB,91,E6,B9,CD,B5,A6,45), + wwH16(5D,2F,94,93,CF,E5,6F,F2,E0,59,AC,E3,65,CE,AA,3B), + wwH16(F6,13,D0,B8,1E,FA,03,23,8B,97,5F,38,09,E0,A2,F8), + wwH16(11,5F,F4,27,FA,84,90,F3,96,DB,16,BD,47,E4,9C,71), + wwH16(8E,34,49,2F,69,AD,3E,33,9C,CC,2A,BB,01,42,02,C4), + wwH16(7F,6A,52,6E,D1,FB,47,EE,EF,24,92,23,4A,AA,8C,0C), + wwH16(17,51,07,EB,6D,DD,9F,F5,C5,D9,D7,B9,4B,A8,D0,EB), + wwH16(26,A0,56,61,C0,6C,A4,61,3C,A0,05,1F,94,C9,F6,CB), + wwH16(48,D9,5A,A3,C0,C8,2B,18,AB,0C,CB,B2,97,BA,F8,F5), + wwH16(63,6D,03,B2,5C,96,23,64,75,AC,2A,FE,ED,9F,52,9A), + wwH16(8A,FA,72,A3,48,95,B7,93,C6,27,D2,8A,65,EE,69,21), + wwH16(FD,1D,A5,45,8D,DE,40,C2,D7,61,46,EA,C9,59,76,48), + wwH16(56,70,91,1D,C4,34,F7,F0,F5,9F,08,62,2C,1D,BF,32), + wwH16(B1,8E,F4,D6,59,AD,87,67,2C,42,77,3D,E1,75,DF,AE), + wwH16(B8,74,30,D4,7D,1C,EC,22,1A,10,C8,89,48,C2,C8,45), + wwH16(39,6E,A8,36,35,5E,80,31,F9,99,BE,CF,F0,FE,4B,21), + wwH16(9D,C2,15,90,2F,BE,41,0E,E0,9E,41,80,5A,4C,78,77), + wwH16(48,06,7B,12,00,13,98,E7,9E,98,E2,A7,EF,C2,2E,44), + wwH16(47,7E,EB,45,BD,B8,F0,E2,E6,C7,F2,17,41,E5,8C,75), + wwH16(CB,25,57,4E,85,62,9A,F4,EB,B3,47,96,A3,D0,4E,4E), + wwH16(55,97,87,66,DF,05,1E,55,BB,70,90,40,F6,64,D8,23), + wwH16(E6,B2,7D,FA,53,30,CE,56,D6,A9,21,7D,97,BC,A1,E2), + wwH16(83,24,F2,4F,5A,74,F4,C9,84,47,19,7E,69,71,C3,07), + wwH16(2D,40,77,9F,C1,06,2A,55,FB,28,A5,61,B8,7F,98,F8), + wwH16(1D,F4,91,1C,53,C2,43,EA,48,AD,EF,52,09,BE,CE,50), + wwH16(DF,A9,BD,DD,3D,51,4A,B3,2A,10,D7,E6,BA,4E,98,17), + wwH16(38,2E,FA,3A,11,13,5F,38,F6,87,02,B1,52,F3,FA,D3), + wwH16(C1,58,3E,0E,7D,57,8C,13,7A,CF,2D,16,A6,72,31,0B), + wwH16(72,F6,39,51,FE,0E,A4,13,EB,65,31,68,FD,05,63,24), + wwH16(06,02,06,06,30,F0,89,CC,E5,45,84,4A,10,52,FC,A1), + wwH16(82,85,9D,37,4E,8C,9F,01,DC,B3,89,32,C0,35,D6,A6), + wwH16(78,7B,EE,8B,22,9E,B4,6D,F7,21,45,D0,DD,F1,0F,45), + wwH16(4C,C4,52,95,FA,25,A2,14,64,A6,9C,7E,F4,83,56,50), + wwH16(82,90,01,15,C5,C3,16,27,43,28,4A,BB,55,6D,54,AF), + wwH16(23,BD,FA,38,6D,96,C3,62,6C,83,6F,49,14,5F,66,2D), + wwH16(83,99,EE,F3,2D,01,6D,6B,E1,C8,C9,2D,C8,61,26,97), + wwH16(1E,27,0D,DD,05,42,EF,E4,92,DD,B4,E6,08,26,ED,1A), + wwH16(A2,E4,3A,99,A1,CF,4E,48,AA,C6,DF,91,D4,52,56,56), + wwH16(A1,D9,54,16,36,31,99,68,F6,46,B0,73,29,34,B6,87), + wwH16(97,AC,36,19,B7,E0,60,FE,AD,79,FB,93,32,52,5A,6C), + wwH16(DD,26,A7,50,EB,A5,A5,F8,C7,24,95,B7,71,B8,F8,73), + wwH16(80,2F,74,57,BD,84,5D,47,D1,4E,31,46,FB,D5,B2,75), + wwH16(19,16,C8,F8,37,9B,1D,9F,BE,C9,E6,C2,FD,7C,6A,47), + wwH16(DA,40,07,17,A5,9C,56,AC,F2,58,C4,FD,6B,66,2F,19), + wwH16(DB,09,85,86,D0,60,9B,85,6E,56,19,B0,76,50,9B,89), + wwH16(12,49,14,02,77,C8,20,8C,71,08,58,BE,EF,30,BE,E7), + wwH16(AF,72,0B,0E,AA,92,3B,0D,5F,39,CF,0F,AC,4F,05,3E), + wwH16(32,BA,CE,FC,4C,BD,9B,22,86,8F,A2,D4,6F,3A,20,1C), + wwH16(42,9F,A5,6D,72,F4,92,74,ED,09,80,05,3A,F0,21,89), + wwH16(67,2D,F8,D8,43,CF,48,FA,EE,B8,26,AA,9E,C0,61,60), + wwH16(6D,01,77,DC,F0,57,E3,17,E7,DF,B6,9A,BF,F0,A2,6F), + wwH16(FF,54,FB,4D,E7,B4,69,5D,35,E2,6A,2F,34,8C,51,B6), + wwH16(68,A6,E0,CB,8B,CF,DF,D1,99,42,6E,B8,12,E0,F1,53), + wwH16(46,2C,20,34,2A,C7,38,B8,47,C5,9A,A2,DF,50,B7,92), + wwH16(9F,8D,07,8C,96,9B,42,4F,E2,55,15,61,61,01,DC,D5), + wwH16(76,3C,F0,E0,C8,6F,E5,F0,27,8A,D2,53,AB,1E,64,09), + wwH16(52,2D,44,B2,5D,21,99,F0,20,E6,C8,D2,D6,40,A0,8A), + wwH16(F8,0F,53,61,0F,3C,D7,05,1F,56,83,66,A9,11,75,38), + wwH16(2A,E7,7C,B5,88,86,A7,44,D5,5F,B5,81,41,E1,13,ED), + wwH16(E4,8F,1A,E3,E3,C3,1D,38,7E,C7,80,12,2E,E2,67,61), + wwH16(31,29,2B,EC,C1,D3,AA,A6,09,65,F8,5B,82,FE,73,D4), + wwH16(18,60,6A,89,29,FE,74,C8,C0,60,62,69,8E,72,6C,6C), + wwH16(A6,62,CD,95,11,5E,03,FC,7B,48,CB,D4,15,CA,A7,70), + wwH16(62,A9,A7,6A,37,CA,FC,CD,F4,28,EE,A2,B8,76,17,63), + wwH16(9D,33,87,23,C3,54,FA,3A,2C,5F,83,A3,52,F9,46,30), + wwH16(59,A9,E9,D9,BB,2B,54,2E,1A,2E,9E,5A,45,48,4F,FC), + wwH16(12,70,7B,56,0E,66,FB,8D,80,7C,D7,93,36,FD,7C,42), + wwH16(6F,47,23,64,D6,A5,A8,11,AB,79,2A,09,F4,D0,29,56), + wwH16(FF,7A,E4,B5,CF,DA,08,D9,06,1F,A1,3C,D6,6C,45,89), + wwH16(21,BF,9D,6F,3D,D9,0C,BA,DC,D1,63,C4,0B,0D,2E,01), + wwH16(AE,FE,25,D4,A4,E4,60,B8,23,40,65,A2,DB,0F,16,E5), + wwH16(40,CF,51,64,F6,1E,B2,0C,C6,96,E0,AA,A8,C3,49,1F), + wwH16(33,FE,94,21,C8,9C,D3,00,A8,DE,E1,9A,E6,A4,EE,C4), + wwH16(80,D5,64,4F,CA,55,9E,A5,08,A4,86,84,AB,94,DC,1B), + wwH16(A3,91,4D,4B,A3,1E,8D,F2,28,4F,BC,BA,B5,95,09,41), + wwH16(B9,44,60,9A,AE,9B,48,17,D3,54,B0,73,EC,A9,B3,3E), + wwH16(35,F5,A9,17,CB,5B,F9,1B,91,7C,1F,65,FB,49,62,83), + wwH16(54,98,41,C2,D4,41,6E,5B,12,89,DF,F2,42,0C,9F,1B), + wwH16(C4,09,69,5B,1B,10,12,D8,72,21,9C,DA,45,EF,3E,05), + wwH16(1D,8C,4C,33,9F,A3,86,FB,C0,6B,74,D3,35,8D,B0,D7), + wwH16(BB,0B,5E,84,E2,39,6D,BA,87,EC,ED,EF,95,0D,12,D9), + wwH16(35,86,A4,C5,99,B2,09,E3,5D,6E,7D,2A,D7,B1,FB,3E), + wwH16(87,EA,03,84,F2,F6,EE,09,8A,E6,49,0F,A5,4B,17,15), + wwH16(C8,58,FB,99,9A,FD,80,95,44,00,A9,97,6D,C9,CB,11), + wwH16(6C,D1,64,05,93,41,E8,3A,1A,2C,EC,BE,56,C2,06,A2), + wwH16(C4,7E,35,07,69,74,A7,5E,BB,43,64,B5,5F,E8,15,43), + wwH16(83,4F,2A,4F,DD,33,85,4B,B9,F3,78,08,F1,B8,DA,1A), + wwH16(BC,58,1B,3F,10,72,CB,CB,FA,96,FD,AE,85,28,42,14), + wwH16(57,F8,6B,1F,3E,52,4F,0F,78,35,13,11,7C,97,CE,FF), + wwH16(DF,7D,F4,E9,31,31,60,7C,3E,D1,9D,98,88,38,9B,96), + wwH16(BF,3B,9A,F8,06,90,5C,F6,9E,40,BC,77,D8,5F,80,E4), + wwH16(86,97,D4,47,51,DB,50,6A,57,03,A0,95,68,BE,6F,B6), + wwH16(3E,55,AE,E3,0F,2C,A6,AA,DC,77,DC,A8,F0,B4,F2,C8), + wwH16(CF,0C,05,63,1E,7D,94,1F,61,7F,CC,47,96,24,CD,EF), + wwH16(AD,B4,B0,46,1F,E8,CF,FB,7E,69,B2,A5,B7,9A,2D,F7), + wwH16(75,30,13,DC,B8,7F,BA,3B,0E,FD,F7,3C,73,20,C6,E0), + wwH16(D5,DA,07,90,A8,7D,51,49,24,00,3B,BA,79,BA,04,15), + wwH16(EC,3F,B9,F7,AF,C3,77,2D,B5,07,8A,CA,C4,27,78,77), + wwH16(7D,29,F6,84,22,BD,F9,C3,7D,D6,C9,9A,5C,37,8D,42), + wwH16(45,F5,1F,FB,42,E4,8B,F4,5D,6F,77,4D,C0,C3,B9,F5), + wwH16(2A,F6,09,09,D5,AC,8E,5C,9A,DE,4B,50,AC,5B,14,53), + wwH16(84,59,1E,58,52,70,45,DF,16,8D,BE,BE,93,0A,55,57), + wwH16(FE,5C,D9,B2,18,BB,4A,5A,DF,B4,9F,CF,5B,5D,43,65), + wwH16(87,49,CF,86,6F,45,D4,71,34,BF,BF,C2,86,1A,30,E7), + wwH16(C8,00,AF,B5,9A,E7,E1,A4,CC,A8,54,2F,01,7D,29,F9), + wwH16(17,F8,E2,CE,E7,00,56,37,3D,69,B2,CF,1A,9F,37,90), + wwH16(20,9E,52,40,BB,39,80,00,47,C5,39,A1,74,0E,93,64), + wwH16(E7,23,9D,05,55,30,5C,5D,5D,5E,76,C3,C0,45,71,5E), + wwH16(E7,D1,99,07,11,88,98,29,63,EA,FB,92,82,24,A1,B4), + wwH16(83,FF,81,3C,EB,FC,17,08,C6,6D,86,86,9C,A5,32,2F), + wwH16(54,3F,31,AD,2B,31,C1,4E,7E,83,20,B9,F5,26,3F,1A), + wwH16(E4,50,B9,37,51,C1,66,26,B5,56,3C,52,35,57,1B,4F), + wwH16(AB,13,72,A6,AC,7F,BD,EA,1D,C1,9D,BF,0A,1F,BD,7C), + wwH16(ED,44,60,00,09,15,CB,8F,B7,6F,0D,D2,D4,FA,A8,34), + wwH16(DF,70,4A,51,D9,A6,00,AD,17,C8,84,A9,C7,1B,CE,7F), + wwH16(20,A2,79,AD,C2,C7,86,E7,67,3E,54,2D,C5,17,1A,FF), + wwH16(D8,4C,6B,70,14,49,7E,8F,89,87,94,1E,FE,0E,A8,F5), + wwH16(34,06,4A,08,0D,D6,42,FA,19,E8,18,92,B1,E5,06,70), + wwH16(15,2A,60,6D,AD,FB,28,BB,F1,BB,BD,02,5A,9B,58,13), + wwH16(DB,C9,75,AD,BD,CC,D0,7F,23,BF,B0,64,E9,6F,A0,87), + wwH16(28,F1,D9,DF,BF,AB,68,75,EF,88,75,FA,1D,05,67,C5), + wwH16(16,46,4F,70,D5,5D,A7,A3,09,74,B2,65,C2,C5,33,8C), + wwH16(C0,A3,1E,5B,BA,0F,79,81,49,F5,8C,86,42,B1,6F,98), + wwH16(F6,D8,2A,E3,D4,2F,9F,30,B6,6D,5F,7B,EA,62,8C,49), + wwH16(82,9B,88,0C,B9,95,29,8B,0E,4B,BD,3F,61,8C,C3,20), + wwH16(A5,D8,48,94,FF,3E,C1,40,73,3E,63,47,D1,D2,37,DD), + wwH16(0A,73,6B,9A,87,56,21,A6,12,4F,FD,78,0C,A5,E1,A5), + wwH16(A5,15,C1,1F,D2,82,8D,6F,12,91,E1,72,2E,65,0D,DB), + wwH16(89,D2,9E,59,6A,53,12,A6,0F,26,0D,E5,04,93,7D,82), + wwH16(F9,C6,13,86,13,0F,69,F8,55,3D,99,FE,00,13,7D,74), + wwH16(6A,7A,49,FD,44,E0,91,48,13,89,4E,4F,D8,53,7E,B3), + wwH16(DA,01,C4,FD,64,BC,68,73,AB,1C,8E,9B,D4,EE,D8,F3), + wwH16(C2,EA,72,D9,49,DC,BC,14,FB,BA,95,89,77,6E,05,90), + wwH16(68,8F,35,86,36,72,78,46,59,49,A5,20,72,89,95,9D), + wwH16(A8,CE,EB,E5,85,0C,E1,EE,DC,74,03,3D,59,0C,DC,45), + wwH16(2C,CC,EE,DD,73,54,05,7D,ED,2F,0A,81,52,54,89,CD), + wwH16(4E,BD,0C,E7,BF,02,35,29,5F,95,5C,7D,B0,08,E1,EB), + wwH16(54,52,A6,98,94,41,5E,F0,87,5B,E4,40,86,3F,0B,19), + wwH16(C7,FA,A0,45,BE,BF,18,FF,AC,7E,D7,A0,EB,AF,7D,A4), + wwH16(06,46,77,C5,A9,8B,9B,25,F5,8F,82,87,9A,E1,CC,C3), + wwH16(29,2D,D6,DB,D0,BC,78,5D,23,EE,99,49,25,67,B4,4C), + wwH16(1F,77,E1,C3,57,02,55,C5,C7,CC,95,4D,EE,06,35,C7), + wwH16(87,58,BE,89,36,28,06,FD,15,E5,C4,C6,A0,4E,FE,75), + wwH16(82,05,B8,09,7D,FF,A4,7C,A6,49,16,6E,9A,B6,7D,E3), + wwH16(D9,39,5D,0D,8D,C1,2A,66,13,35,FD,E8,57,31,57,97), + wwH16(C1,72,7E,4A,85,05,79,10,22,A1,3A,F4,6C,39,9A,0E), + wwH16(1A,90,C5,8B,E5,3C,E6,8A,53,6C,D2,3E,1B,B6,FE,63), + wwH16(23,3C,09,BF,FE,20,8A,4D,6C,A3,7B,92,2B,94,C2,19), + wwH16(06,20,81,37,82,63,3B,32,C0,AE,75,FA,F2,11,F7,62), + wwH16(E0,82,97,6B,61,3E,77,98,B6,A8,6F,CA,F6,33,F6,F6), + wwH16(71,D8,26,4A,30,24,63,39,30,27,67,AE,6F,ED,CD,CD), + wwH16(01,38,ED,50,BE,AE,65,5D,8A,CE,F3,7C,54,E8,8E,AD), + wwH16(2B,68,D3,09,1C,F1,11,15,7F,D9,D7,79,4C,52,ED,16), + wwH16(9C,DC,F5,D7,D4,24,B6,80,C4,06,6B,BD,74,25,D0,6A), + wwH16(AA,0D,E2,63,AC,1F,E1,DB,46,EA,DB,32,8F,38,40,22), + wwH16(34,D3,B8,74,B4,81,F2,A3,5D,22,AD,10,EB,27,B4,88), + wwH16(61,16,80,DE,5E,86,EE,0E,EB,CA,90,00,89,6B,A7,2A), + wwH16(1D,90,9D,6E,1A,70,F0,D8,63,79,97,6C,58,8C,A2,F9), + wwH16(97,03,6B,44,06,33,64,4B,4F,2B,C6,5A,EC,C3,A0,E0), + wwH16(06,28,16,C7,F0,B6,77,1A,5D,2A,67,E4,16,89,0C,9F), + wwH16(C0,14,3D,8F,00,8C,1B,26,35,72,99,B5,27,07,F8,10), + wwH16(40,C2,53,67,19,57,B6,B3,A7,A6,5D,83,81,4C,AB,62), + wwH16(A4,B1,92,D9,23,FE,58,EF,6E,E7,D9,9C,C4,A6,EC,08), + wwH16(D7,55,8F,C2,D7,0A,4A,61,7A,48,BD,C8,49,99,29,28), + wwH16(06,64,91,DF,44,4D,DE,1A,64,B1,8F,ED,02,75,FC,04), + wwH16(EC,62,11,C4,82,AA,BF,83,30,DF,10,97,B0,AE,CD,4B), + wwH16(9F,6A,64,D7,37,CB,A5,79,8A,AF,28,EE,6C,26,44,82), + wwH16(35,49,B8,BE,99,08,07,61,8D,57,E4,29,44,58,33,37), + wwH16(C9,33,1D,B9,51,55,9D,3B,41,C5,94,41,AF,F5,20,79), + wwH16(D4,1C,2A,B3,C0,33,0E,F3,BB,D0,6D,36,38,17,43,9F), + wwH16(D0,BA,D4,DA,2D,CA,F4,C6,60,C7,85,88,9D,62,3B,2A), + wwH16(80,05,09,88,BC,8B,E5,7F,70,91,44,1A,7D,F6,FF,F7), + wwH16(ED,8F,86,07,FC,35,72,74,4B,7A,52,EF,4C,00,18,D6), + wwH16(F3,99,E0,67,03,1F,BE,CD,4E,C1,FB,5C,3F,63,B2,94), + wwH16(48,8F,CF,AF,0B,63,85,7B,AD,99,7B,93,51,40,33,CF), + wwH16(43,10,74,0B,1D,C0,A1,ED,3D,10,14,DA,7E,4F,1E,A9), + wwH16(BE,49,25,EF,4E,DA,A3,12,98,C3,84,C8,E3,C0,BD,73), + wwH16(EA,8E,B9,06,A4,4F,34,F7,84,E5,AD,12,82,43,79,64), + wwH16(F2,FB,98,54,77,95,60,9C,90,7E,4A,A7,26,33,50,50), + wwH16(66,A1,23,E2,82,57,B8,42,44,12,E4,0A,89,F9,CF,FE), + wwH16(08,C0,45,6A,9B,A3,70,F9,58,82,68,40,6A,F5,BD,9C), + wwH16(0F,8F,04,A0,AD,78,8D,09,83,BC,A0,E5,05,4F,7B,41), + wwH16(9B,E8,72,77,F2,AB,C2,2C,20,F6,61,62,D4,88,17,B6), + wwH16(70,95,73,3A,25,A0,E4,C7,7B,18,10,F3,E3,42,86,C1), + wwH16(00,6D,65,45,82,A2,C2,F8,11,CD,76,44,6D,19,E9,22), + wwH16(A1,7C,BE,C6,03,78,53,69,E0,CE,92,4A,7B,72,24,8B), + wwH16(BF,3E,06,6F,9E,19,5F,BE,79,3A,7E,FF,A2,BF,98,D2), + wwH16(FF,49,79,E5,20,19,38,E9,CE,ED,34,60,04,EA,E6,B7), + wwH16(42,0F,F0,5E,C4,7D,17,EF,4B,28,FE,91,49,7D,23,DF), + wwH16(23,E5,96,A4,2C,23,52,24,09,97,5B,D2,00,B8,A2,36), + wwH16(81,8F,14,A5,28,3A,60,2E,25,6F,D7,9D,A1,0B,A6,37), + wwH16(72,DE,A5,9A,55,18,A7,F8,2A,A0,EE,52,80,FA,37,ED), + wwH16(4F,9B,22,B9,F5,32,00,E7,EE,CC,C3,FD,3F,FF,B2,F7), + wwH16(D4,2F,21,C1,3D,E6,03,AC,0E,07,40,5D,BF,A2,98,32), + wwH16(70,5F,FE,8F,CC,C9,A9,9A,59,91,7A,E4,0B,43,6F,5E), + wwH16(62,4F,E8,A1,C4,7B,1E,69,F3,7A,29,CE,A5,42,19,4D), + wwH16(AA,D3,E0,5C,D6,84,3E,C8,1B,6F,47,1B,0D,94,FE,A2), + wwH16(15,35,C7,C7,81,D1,5F,6A,F6,26,8C,6B,97,76,AC,FA), + wwH16(77,CA,19,56,F5,DE,E0,CB,5F,63,91,F7,E0,B3,4F,58), + wwH16(2E,29,56,79,FC,FE,CE,2F,A8,03,80,9F,B9,08,3D,4D), + wwH16(C8,B7,66,D3,5C,C8,E3,A2,71,C2,12,4F,84,F6,C9,99), + wwH16(B9,65,E8,60,C2,BE,76,AE,9B,D8,48,CE,09,30,30,5A), + wwH16(D2,14,71,D2,4E,01,B3,05,C3,5C,2E,B8,B0,67,67,C8), + wwH16(39,10,8B,82,1A,30,0B,DB,5E,12,5A,91,25,3B,30,82), + wwH16(89,A8,A9,F8,CE,C1,AC,C2,80,8C,8C,45,EF,C7,36,01), + wwH16(74,57,09,DC,29,B2,E3,1A,1B,EB,1D,F8,EA,71,3D,5A), + wwH16(89,3F,88,03,EE,99,1E,F6,77,3F,D2,5A,62,7E,46,D2), + wwH16(F6,0E,1C,28,2E,67,D2,E7,E2,28,BC,34,64,A9,EF,E0), + wwH16(44,48,97,48,C3,A2,3F,C9,E3,68,36,88,3D,5D,14,7A), + wwH16(15,8B,B1,F3,09,01,32,74,A8,FB,FF,28,93,DB,8C,60), + wwH16(70,6B,83,FD,14,24,7E,F2,2F,05,22,03,6F,5F,0A,0F), + wwH16(C2,D9,E7,B3,99,4B,82,9B,33,CE,FD,BF,F8,5E,40,CD), + wwH16(97,E6,24,79,19,B4,F9,84,64,E1,0A,6C,9C,7C,7C,7A), + wwH16(9E,78,66,8C,D9,B1,5D,28,2F,F3,8F,A6,62,40,23,72), + wwH16(42,78,6E,3A,EF,FD,5F,22,86,C6,6F,DD,3B,10,DE,E7), + wwH16(B4,BA,3F,78,46,92,71,BC,5C,0E,F6,9D,CA,5B,65,10), + wwH16(CF,C7,CC,D7,6F,E6,75,D6,F6,4B,38,F5,0D,5E,74,FB), + wwH16(F5,DE,D0,AE,C3,B0,7A,4E,35,1B,0D,04,63,41,8A,45), + wwH16(3D,C5,6F,E2,B7,05,DE,62,28,40,7C,BC,D5,EC,31,56), + wwH16(C6,97,75,29,16,38,BB,54,AA,CE,4B,D9,8E,6B,8E,13), + wwH16(FA,C1,FE,F1,2B,E6,3D,1C,78,B9,F8,F2,53,0D,C5,78), + wwH16(8C,C9,36,47,66,62,72,AC,C6,2F,98,F8,52,78,7E,1B), + wwH16(AC,93,36,0B,02,9F,BB,1D,95,E6,00,97,0B,2B,23,79), + wwH16(90,99,DC,59,31,33,DB,F8,55,70,45,78,58,C5,24,AA), + wwH16(A2,D5,E8,42,8D,51,D0,BD,08,F6,29,21,B1,A1,A1,6E), + wwH16(F5,60,A0,94,91,F0,79,C4,EE,49,99,04,15,64,DD,EC), + wwH16(5B,59,86,4E,AA,2D,1C,46,CF,91,E1,8C,52,48,05,E9), + wwH16(D6,67,3C,98,8D,AC,4F,7D,72,5C,E5,A9,36,ED,AB,45), + wwH16(1E,17,44,7F,C5,C7,96,02,C2,FB,0A,88,4D,C2,10,42), + wwH16(EF,A2,E2,70,57,F6,56,9D,93,48,6D,C5,71,FF,CC,E5), + wwH16(43,D1,A1,90,20,E2,20,27,E7,F3,63,43,AB,42,7A,3A), + wwH16(DD,E3,82,73,B5,1D,F6,26,A9,1E,90,D9,B5,CC,70,27), + wwH16(02,E0,D6,8D,CD,F6,A0,EC,F1,2B,EA,07,41,C0,9F,04), + wwH16(57,E6,6E,2B,FE,7E,31,FE,23,38,90,4F,D3,AD,24,F8), + wwH16(23,4E,8C,67,81,F5,F6,DD,EF,C1,A9,03,39,B1,AB,09), + wwH16(2C,DF,43,A3,41,CE,C0,F3,6F,C3,DE,B3,A2,FE,23,85), + wwH16(D2,99,33,8D,BA,1E,EB,5E,2B,AE,9D,CB,23,0D,AC,5B), + wwH16(19,94,25,CD,BB,FF,95,3A,A6,75,45,15,43,11,E6,02), + wwH16(8E,72,EA,DA,AF,56,E0,56,6B,4D,57,DC,C5,03,25,CC), + wwH16(3F,94,09,00,AE,B3,91,60,EC,E1,20,EA,26,B1,F8,17), + wwH16(20,33,54,9D,6C,A5,E2,F8,39,C1,17,2D,BB,1F,D8,FE), + wwH16(77,06,C2,FF,DC,5F,10,41,97,EB,1F,1E,57,8E,BB,47), + wwH16(03,78,AF,A2,20,46,41,81,8C,C3,CC,E9,20,B7,69,BF), + wwH16(A6,C3,1F,0F,4A,0B,BE,ED,EA,3E,1D,B4,BB,87,19,FE), + wwH16(8F,9E,39,E4,B4,79,C7,55,2D,4B,88,C8,D6,80,9B,99), + wwH16(CC,CD,7F,0D,57,73,CA,D2,84,98,1A,FC,C6,71,A2,46), + wwH16(E0,13,C0,17,6E,BB,D2,6D,46,99,89,C2,2B,35,3C,B1), + wwH16(7E,FD,C4,1C,40,CD,31,F4,75,97,82,B8,95,84,EE,16), + wwH16(2B,90,BE,A9,47,27,2B,EC,09,C2,48,A3,ED,A5,1F,FF), + wwH16(03,9E,A4,89,92,F3,8A,CD,5E,FD,64,46,C0,C4,B0,BD), + wwH16(50,E5,D6,86,D6,86,42,95,DB,3F,A8,DF,25,11,C0,26), + wwH16(0E,F7,CB,9F,DD,32,3E,87,EC,39,F8,8F,69,74,CB,44), + wwH16(DA,B8,61,7D,D2,47,07,2D,7F,DD,84,E3,EF,5C,91,44), + wwH16(63,31,E4,1D,A1,AA,96,B2,7E,C5,63,26,90,6C,8C,94), + wwH16(1C,C5,E3,C8,6F,A4,70,6F,F9,8F,1D,02,41,B7,CB,D4), + wwH16(22,F9,2C,A5,19,82,22,DD,EC,63,E1,05,8E,F8,C5,FB), + wwH16(25,D6,C4,C1,6B,A1,02,D3,9F,A4,CE,7A,22,68,A5,A7), + wwH16(D1,83,40,43,BC,4D,E5,B5,BA,46,90,02,94,D1,18,8B), + wwH16(44,35,2E,0B,4B,64,F6,34,1A,B3,A5,78,86,CB,2B,6A), + wwH16(36,D1,51,51,B9,05,B8,AE,4D,4E,9B,D1,FF,13,37,7B), + wwH16(25,13,D5,8F,C8,F6,E0,50,28,C1,DD,A7,30,42,76,1C), + wwH16(FA,1C,1E,0D,59,75,AE,AC,04,D8,26,FF,70,57,E9,2A), + wwH16(7E,4B,4F,3F,A2,71,F3,74,A8,E3,F4,63,1A,23,B0,00), + wwH16(08,08,C0,5F,B9,B9,51,D4,85,09,F8,B3,D3,E7,27,80), + wwH16(BB,12,7C,C1,53,8F,C0,42,17,89,74,BE,54,6B,80,14), + wwH16(D0,BD,54,B4,CD,D2,11,E6,06,79,61,37,0C,DE,10,6C), + wwH16(A5,FC,C3,44,A2,47,84,7C,FD,1E,2C,D0,E3,E8,9D,56), + wwH16(3D,12,C3,E1,64,A6,E5,23,51,1D,78,7F,3D,3B,52,3B), + wwH16(9F,4E,75,45,86,B1,46,71,25,87,D5,F0,27,9F,FE,0B), + wwH16(E2,83,2D,C7,8F,94,A8,19,80,75,22,F3,F6,A9,77,95), + wwH16(D9,61,2F,7D,92,E0,BE,B5,C3,76,D4,1E,EF,50,CA,BC), + wwH16(9A,59,F4,26,BE,EF,C6,C1,49,D1,6B,EC,63,0A,8E,DD), + wwH16(50,60,E4,AC,1F,FE,1E,05,0A,65,C0,B0,AC,21,83,D1), + wwH16(16,90,36,44,6E,B9,60,74,34,63,6A,18,E5,25,83,4A), + wwH16(D5,11,45,A0,5E,33,D4,2E,65,59,F2,0B,78,6B,B2,7D), + wwH16(AB,D9,6E,DE,E0,66,56,F4,6F,F9,8B,6E,2B,32,C3,B8), + wwH16(DB,37,55,58,4A,DD,61,4C,E9,86,B0,37,C9,04,B5,1B), + wwH16(1E,B8,A5,AC,74,26,60,E9,A7,0A,A3,A3,E3,1B,3F,74), + wwH16(BF,0C,3F,F4,C8,36,D9,44,05,0F,94,12,76,5A,39,FB), + wwH16(82,8F,40,CE,53,04,B4,7F,94,87,9C,DE,26,76,54,8E), + wwH16(36,EA,36,1F,66,82,98,63,A4,AD,FD,3C,06,60,B9,76), + wwH16(50,EA,DB,4D,86,15,17,33,81,45,EB,15,A3,B2,E3,CC), + wwH16(04,59,DB,38,CC,2A,32,7D,67,15,47,04,B0,64,78,5D), + wwH16(AF,9E,AC,AB,7A,80,59,DD,5F,22,75,DF,E0,59,52,00), + wwH16(E3,E6,27,53,CB,B9,62,82,E8,D0,50,67,06,57,D4,21), + wwH16(1D,8F,99,B0,7F,18,49,B0,92,18,95,80,06,DB,8F,57), + wwH16(94,E8,BA,D7,C7,E1,9E,B4,89,DD,E1,F0,45,67,4C,97), + wwH16(4B,C2,E6,22,C1,70,02,76,C5,04,14,81,E5,1B,F7,EA), + wwH16(13,83,4A,F6,04,1F,D1,07,A3,6F,10,09,70,CB,3B,1C), + wwH16(43,17,F8,6F,2F,9D,9F,39,98,6F,39,72,5E,5E,C5,FC), + wwH16(E3,14,3D,12,63,E9,A5,05,E1,B4,EB,E0,78,25,04,C2), + wwH16(7E,13,C7,26,3B,D2,CC,8C,35,6F,08,D4,25,E2,39,C1), + wwH16(0C,0A,59,42,4C,9C,E4,45,C4,1B,09,80,3D,99,6B,A9), + wwH16(70,9A,DC,E5,5F,91,D8,0D,9D,9A,40,24,D2,31,03,04), + wwH16(EE,5B,7E,3E,9A,FB,63,F3,52,2F,D2,ED,CB,FD,48,A7), + wwH16(EF,2E,67,E8,0A,9D,FB,3A,93,EA,F8,0B,44,43,FE,43), + wwH16(EB,50,9A,C6,88,C4,56,59,32,42,5C,E3,B1,29,1D,FB), + wwH16(4B,BE,65,00,8C,82,A9,9D,8F,9C,69,8D,E8,FF,B8,A6), + wwH16(FF,34,CD,0D,83,AA,4E,5B,DB,43,32,0E,2A,63,D4,70), + wwH16(04,91,B2,44,DA,18,C0,5A,09,59,F1,B4,BA,D3,73,FA), + wwH16(FD,7B,5D,7A,56,00,B5,0C,48,7E,9E,1B,F4,D2,DA,4F), + wwH16(F9,3A,0F,46,90,F7,05,5F,D5,3E,AD,B5,3E,42,05,7C), + wwH16(75,E0,29,85,2D,95,E4,2E,09,9A,D3,97,CE,71,91,DF), + wwH16(4F,E6,82,04,99,43,EF,33,70,73,C4,51,6C,3D,4F,49), + wwH16(09,94,AA,22,03,37,C7,71,7F,95,03,CE,F6,36,43,D4), + wwH16(E7,96,89,29,4B,8D,E2,90,A3,64,E1,7D,33,44,83,09), + wwH16(F1,08,CD,99,50,E8,F3,E9,CB,8E,B2,AD,3F,38,F6,76), + wwH16(2A,41,4C,0D,61,C7,B5,6D,40,E2,C1,33,58,8E,44,58), + wwH16(A8,91,66,57,46,EE,BA,1F,ED,8A,AB,A3,2C,6A,C3,F6), + wwH16(11,25,03,AC,BA,75,13,37,46,85,61,81,F0,24,EA,2C), + wwH16(0D,3B,DD,16,B2,2D,9C,04,CB,A1,B6,95,86,BA,5B,8F), + wwH16(73,5E,41,30,DB,01,4B,BA,A4,78,82,4C,CF,F7,10,71), + wwH16(82,17,74,7A,78,B6,58,54,58,52,4F,A4,83,69,63,A9), + wwH16(39,39,BC,F5,CB,BB,DA,15,D7,D0,65,BB,D4,A6,07,9A), + wwH16(47,34,A4,3F,E6,E2,11,17,2C,A6,31,56,66,CF,E7,27), + wwH16(86,64,3C,25,4E,AF,D2,3D,AA,87,65,67,DA,DF,FB,A8), + wwH16(53,8B,BA,0C,8A,2A,28,02,B1,29,D0,05,8E,9A,20,D5), + wwH16(64,91,25,17,BD,69,DE,3E,DD,1B,76,D0,1A,28,02,62), + wwH16(94,54,C5,04,D8,A5,A7,E5,A5,3D,97,A9,DD,C3,E6,D7), + wwH16(F6,19,BD,EE,13,D5,A3,1D,DD,58,44,B5,CD,0B,40,A8), + wwH16(5E,5B,68,60,3B,87,9A,D1,5F,7F,29,D8,95,AA,00,F9), + wwH16(34,C4,FA,A9,69,01,D0,95,05,F5,12,2E,E4,B2,16,23), + wwH16(FE,25,D4,6D,AA,A8,64,44,50,6D,FB,C2,65,5C,EF,B4), + wwH16(71,3F,82,F3,79,0E,C2,17,9F,FF,4F,DB,4E,73,FA,0E), + wwH16(DD,60,5D,66,C3,A0,5A,AC,40,A9,7F,77,86,57,34,26), + wwH16(B3,18,D6,CE,D6,00,78,3D,D2,93,F8,CA,B3,EC,4B,B6), + wwH16(0D,28,60,A6,54,FE,47,7C,74,CC,A2,46,A7,AF,FA,32), + wwH16(C9,3D,9C,11,68,86,F8,D1,62,44,81,9A,A8,66,FA,D3), + wwH16(41,D3,ED,41,79,27,74,3D,90,59,D8,22,B6,3A,1E,BB), + wwH16(8D,E6,90,67,EC,F3,0A,30,64,9C,08,B8,C0,F6,C9,73), + wwH16(58,B0,18,65,B5,E5,EF,FA,E7,B7,30,5D,F4,B2,44,24), + wwH16(62,29,65,7F,11,8D,67,FB,05,5D,31,45,0D,37,C3,18), + wwH16(CC,F5,30,E8,40,CF,44,2A,81,04,8E,43,5F,4F,70,0A), + wwH16(90,E6,50,63,93,AF,7C,1F,06,91,41,91,EB,AC,BD,04), + wwH16(7F,47,3F,26,E2,2F,D8,04,C1,31,29,96,36,2F,0A,12), + wwH16(2E,75,99,B8,9A,3B,D0,75,84,6A,12,16,59,B9,3C,BA), + wwH16(88,A4,61,54,B0,31,7F,9A,5E,D1,D9,D1,28,04,C6,9C), + wwH16(9E,C4,4C,FF,6F,E0,ED,C9,B8,25,96,D7,EF,A8,97,BC), + wwH16(80,99,76,B1,DA,0F,5A,D0,0B,75,C3,8D,F5,DF,87,0E), + wwH16(A4,4A,0F,3D,21,CF,90,4E,49,D1,A7,C3,7F,A8,E5,75), + wwH16(62,80,DD,7F,2F,38,41,14,4D,06,05,B1,6B,96,0A,5D), + wwH16(77,65,86,A3,EE,79,0E,08,D9,91,A3,B7,AF,DA,C2,10), + wwH16(A7,BE,07,C7,4B,90,C7,DE,0C,E9,C2,9F,A6,BA,02,80), + wwH16(6B,2D,7C,CC,84,87,8F,4C,C0,70,AD,7D,D7,DD,AE,DA), + wwH16(B4,8B,32,C9,77,F2,EE,C7,77,2A,79,5B,4A,78,DF,90), + wwH16(7D,B0,61,86,81,9A,49,C3,A3,1B,DB,B2,A4,D5,CC,29), + wwH16(64,5B,22,69,FE,B9,46,5D,E8,E8,18,FD,05,16,A5,E4), + wwH16(1F,4D,0A,DB,13,C3,10,F7,FA,91,F0,ED,47,C9,12,D9), + wwH16(A0,BB,7A,5A,2F,E4,12,C3,B5,7B,00,21,51,12,BF,27), + wwH16(2B,1C,24,E4,48,1A,04,5C,F4,9B,E6,34,09,40,7A,22), + wwH16(2F,28,6B,71,1E,23,BA,E0,C4,36,B4,B6,45,71,9E,B1), + wwH16(A2,5C,CB,8B,82,E7,47,37,97,35,0B,C4,50,03,81,86), + wwH16(3E,44,92,54,ED,BC,16,1B,4E,22,D4,21,7A,74,6A,D1), + wwH16(AB,A8,F6,92,FB,D5,F0,36,4B,B8,1B,27,59,32,E6,6F), + wwH16(F4,6C,43,F3,53,E8,7A,7F,E8,C4,BC,87,00,69,D1,FE), + wwH16(11,91,2C,0F,49,70,98,E8,26,11,AD,9D,A2,C5,5D,B9), + wwH16(96,A7,66,A1,27,F7,13,8F,CC,BB,BC,C0,7A,83,66,D4), + wwH16(1D,82,B6,2D,92,08,0C,4A,4E,18,40,2B,89,95,71,A3), + wwH16(4D,58,5C,29,1F,C0,34,6E,6E,A0,B7,CF,27,CF,19,8F), + wwH16(EB,54,10,85,C0,93,50,7C,CB,8C,C1,85,A2,B9,DC,18), + wwH16(FF,94,C0,7B,EE,02,3F,9E,3A,4A,AE,5D,A5,7D,AF,C8), + wwH16(F5,18,97,48,AB,A4,1C,8F,9A,1F,BF,4D,8F,A3,C1,86), + wwH16(E5,EB,50,C8,F3,D5,E9,AD,FD,DB,D1,F4,58,0C,C8,1B), + wwH16(8F,3F,91,13,0E,86,BD,3A,D5,04,59,15,27,A2,80,14), + wwH16(18,CB,CF,93,1D,69,D8,15,DD,CF,59,FE,2A,D0,76,75), + wwH16(D6,C2,FE,78,D3,48,51,0C,09,29,5B,AF,90,DD,58,8B), + wwH16(02,E0,45,96,E3,29,84,1E,E5,07,3B,B8,A4,BA,39,07), + wwH16(E5,DD,F9,52,D9,D5,20,A0,39,CF,73,B5,8A,94,AF,2B), + wwH16(21,15,85,C5,8B,84,28,A3,CB,F2,D4,52,04,8D,BD,9B), + wwH16(F1,91,2F,54,5F,64,EC,05,7A,75,66,8C,EF,5B,36,F8), + wwH16(E7,C6,53,12,E5,22,DA,4D,9E,C1,56,70,85,FC,0C,23), + wwH16(0B,28,63,18,EC,60,37,CE,88,44,6B,C4,EB,44,5A,26), + wwH16(15,36,B8,27,01,BB,7F,F0,CC,65,92,51,AC,A2,D3,B7), + wwH16(E6,A7,6B,8B,43,9B,6C,51,4F,0D,10,89,AC,E1,03,9D), + wwH16(42,DA,98,4F,A5,A7,F5,37,F6,60,6A,89,8E,1D,43,A4), + wwH16(92,6A,66,4A,8C,36,26,8B,6D,1C,2C,DF,F6,52,48,67), + wwH16(9C,F7,B6,3C,32,6B,E1,27,BD,98,33,18,D4,9F,B5,0D), + wwH16(D9,E4,27,6A,D8,17,89,8F,F3,31,AF,89,8A,83,D4,EE), + wwH16(ED,DD,C9,D7,8D,4C,C6,97,F2,79,2E,5A,20,A1,02,C4), + wwH16(9D,87,8C,EE,AB,98,18,5B,0D,0E,F7,26,4E,DB,C8,45), + wwH16(B9,FA,F3,28,AD,F5,2C,3B,AC,1F,C7,0E,69,58,5D,D0), + wwH16(CB,54,93,31,2E,72,7D,EC,62,E0,41,37,E2,C9,91,02), + wwH16(EF,10,44,74,51,81,F0,1F,BA,62,47,7B,67,57,C4,D8), + wwH16(81,BF,09,A9,31,B0,8B,11,B6,43,CD,14,62,AC,E1,BD), + wwH16(DA,AB,F4,DB,2E,B7,0C,4D,61,5B,2C,8B,3F,87,9A,F3), + wwH16(23,65,AF,8E,1E,13,64,DD,E0,97,69,0A,40,37,4B,3C), + wwH16(A1,17,7E,A7,92,63,54,6B,34,F8,AB,DC,39,BA,E0,F9), + wwH16(D3,FD,AF,7C,A2,1A,B1,CB,1A,25,34,4D,3E,F5,47,73), + wwH16(45,6B,0C,01,51,63,D9,76,A7,07,7D,89,1B,15,B7,8C), + wwH16(3D,13,66,F1,71,D4,85,79,B4,45,4F,17,EE,8E,36,72), + wwH16(AC,EE,D2,B4,CB,59,AF,8B,8C,A2,F8,04,ED,34,47,21), + wwH16(AF,EA,04,EB,AC,A8,A6,60,0E,7C,43,5E,0E,D6,DF,B0), + wwH16(19,A5,C6,78,F4,F0,49,AA,AD,FC,53,39,BA,EF,54,24), + wwH16(19,1D,75,9E,43,94,AB,D6,21,19,B8,EE,01,63,02,F6), + wwH16(8E,37,9A,E3,7E,D0,B4,0C,A3,A5,BD,8E,13,2D,85,A4), + wwH16(7D,F0,BA,43,E8,66,06,4D,C5,ED,5F,A3,4A,B5,DB,CE), + wwH16(FC,6A,7F,F6,8E,0F,FF,D2,E1,AB,8D,E4,06,87,57,63), + wwH16(F9,96,65,51,54,C2,94,A4,4B,B9,59,14,A8,F3,FC,F0), + wwH16(DF,99,74,81,58,49,63,C1,4B,9C,D1,6A,32,2A,C8,7B), + wwH16(19,0C,AC,C8,2D,A5,80,6B,59,BC,51,D9,89,E2,59,59), + wwH16(61,9F,4C,1A,9A,3E,DF,2F,CD,E5,0B,E6,93,CE,AD,0C), + wwH16(0D,77,5D,AB,95,51,04,56,DF,1E,EA,46,57,D4,91,8E), + wwH16(2F,FF,49,E8,47,49,D3,84,A0,30,96,44,F6,A0,8A,DD), + wwH16(F4,BA,3F,7F,E2,AC,23,0C,76,D1,CE,BB,D4,72,FE,C6), + wwH16(87,3D,22,B8,7D,74,B0,9E,75,2C,72,24,F5,2A,19,7E), + wwH16(41,35,07,DC,F8,7F,1F,B0,0A,05,CC,72,C4,25,A9,31), + wwH16(1F,49,19,C5,F1,D4,3E,CE,39,BD,75,30,B6,81,60,36), + wwH16(6F,7C,6E,4B,A6,A3,CC,59,2E,C0,B0,00,7F,66,AA,AB), + wwH16(25,64,AB,E8,3F,85,A7,07,CF,BF,FD,08,94,8B,A1,C9), + wwH16(58,78,00,7C,77,2B,E9,45,D1,F7,81,14,53,3B,B9,12), + wwH16(37,54,55,54,FF,AE,17,57,A3,FA,9A,5B,5B,79,A7,BD), + wwH16(9C,0D,B0,E3,6F,4C,C4,34,A1,A4,18,03,E1,E8,A1,B1), + wwH16(72,93,02,A0,AD,27,5F,D9,1D,6F,C4,0A,8F,8B,C6,7B), + wwH16(05,80,19,E9,28,48,BC,09,6A,34,A7,6D,1A,4F,2C,9B), + wwH16(FD,EA,90,56,F2,1F,59,6A,F8,01,55,A2,E3,7B,05,00), + wwH16(F1,9E,C4,63,32,2B,91,32,8A,D6,78,D5,86,70,A4,9D), + wwH16(6E,BB,88,AF,6D,B6,73,E3,14,5B,43,4F,A5,81,D0,FB), + wwH16(E4,D6,C4,50,96,6E,2D,7B,50,29,E3,C0,2E,E0,63,57), + wwH16(A9,F3,30,F3,60,78,BF,F0,49,A6,8F,2B,57,4A,39,E9), + wwH16(07,27,85,33,1B,80,B4,5E,73,AA,E2,BB,A5,4E,39,53), + wwH16(67,F6,92,C8,66,D7,7A,8C,03,6F,C9,10,4A,90,42,42), + wwH16(10,8A,96,D7,9D,87,BC,FC,38,8B,B5,C5,7E,6F,C3,8B), + wwH16(E2,A1,08,74,05,44,D6,DB,F2,79,29,31,90,A3,9B,FE), + wwH16(70,81,C2,25,0B,0B,54,DD,18,C6,FF,DC,58,63,A8,D1), + wwH16(8E,2D,07,D6,9F,9A,8B,96,12,B0,66,CA,42,86,C2,26), + wwH16(26,B9,0F,F1,9B,99,31,1B,9C,09,30,FE,E2,BD,96,27), + wwH16(DC,41,E4,A0,4E,E2,47,B2,51,B6,89,35,49,B2,BE,1F), + wwH16(D0,2F,2C,0D,E4,A8,DF,08,F1,02,C8,B1,96,31,C0,C7), + wwH16(88,47,3C,4B,5F,48,D3,97,2E,48,3A,DB,4F,05,64,1F), + wwH16(20,02,57,AD,16,54,A0,6A,5A,1B,2D,59,CE,6E,CB,B7), + wwH16(A1,90,6A,21,93,3E,93,67,C2,7A,38,C9,D5,12,C1,1B), + wwH16(80,E2,2C,4D,22,7C,7A,61,C8,D3,5A,81,3C,79,8E,65), + wwH16(F1,31,B9,EA,89,97,CA,AF,EE,B0,6C,48,5D,6D,59,39), + wwH16(0D,0B,2D,33,57,0E,E0,C9,52,EB,99,C8,F1,60,66,2A), + wwH16(C6,F3,A8,24,E5,1D,76,41,09,AD,8A,23,A0,FA,F2,97), + wwH16(8B,D4,D3,9E,71,A3,C0,E9,90,3C,DF,DF,F2,0F,D1,3C), + wwH16(24,77,1E,28,3D,9C,52,33,38,CA,F7,DA,2C,06,0E,7E), + wwH16(D6,77,00,74,89,77,8F,C6,6F,1E,66,77,44,D8,05,FA), + wwH16(7B,26,7D,EB,8C,FC,DF,DD,23,2A,AD,D2,26,72,1B,94), + wwH16(C7,02,48,23,85,29,B2,AB,65,AB,84,6F,BE,A1,3A,7F), + wwH16(44,EF,1C,2E,8F,91,B4,4A,13,1F,87,D6,CD,AD,33,FF), + wwH16(69,31,AF,47,CD,09,CC,35,7A,BB,59,D4,3F,C5,CE,59), + wwH16(60,88,90,8E,76,56,AF,5B,EC,33,9B,CC,37,79,93,CF), + wwH16(CC,43,31,72,76,A8,50,3B,6E,34,07,A9,6C,77,58,23), + wwH16(0C,A5,6E,91,BA,05,63,2B,94,5D,47,D4,0D,EF,40,F1), + wwH16(FD,91,D1,D9,9C,0E,D0,81,4C,9B,18,14,67,9B,30,0B), + wwH16(2A,33,E2,7E,F3,15,14,EA,1F,4E,D7,BB,AC,1C,C3,FE), + wwH16(06,E0,F9,76,2C,90,95,C2,C0,62,88,C2,28,5E,F5,30), + wwH16(E2,60,16,D9,0D,A0,48,24,8C,DA,74,3D,47,77,13,56), + wwH16(7F,28,CF,30,F5,B7,7C,4C,5C,89,68,4E,C8,6C,BA,46), + wwH16(32,47,55,2B,5B,06,AA,CF,FE,B0,2A,92,42,F9,6C,A0), + wwH16(14,32,E1,A6,07,A6,D0,97,A9,C4,76,71,05,B0,17,76), + wwH16(C0,C4,2A,DB,19,76,84,8F,7D,F8,79,D3,F3,78,23,48), + wwH16(B4,27,48,65,B2,7F,E3,03,7E,42,D0,8F,AC,8B,C3,36), + wwH16(C2,8F,BE,78,F4,64,59,25,82,25,72,A3,4F,D0,0F,DD), + wwH16(CA,EC,CE,D7,55,73,42,C2,F3,32,A2,45,B3,E0,8D,70), + wwH16(DD,B4,13,81,1F,0A,2B,4D,0F,7A,4B,BB,E4,C1,76,DA), + wwH16(58,BD,DD,9E,2A,9E,89,07,3F,56,0E,5E,17,14,BF,0C), + wwH16(D3,35,1C,3E,E4,DC,9A,E9,8F,6C,C1,BD,07,C5,91,4E), + wwH16(E7,86,4C,13,0E,2F,AC,F1,3F,93,0C,E0,19,E9,5C,C6), + wwH16(D2,32,3E,3A,7C,C0,74,E5,E8,BE,63,45,57,99,C0,42), + wwH16(19,E7,71,27,8A,1E,24,B6,A4,B9,7C,4E,85,AE,4C,37), + wwH16(F2,B5,2B,BF,31,3F,8B,79,E5,ED,C3,48,D6,46,08,05), + wwH16(72,29,FF,CD,9A,33,EE,7A,2B,90,18,02,6D,12,8A,38), + wwH16(EA,2C,D1,FE,C1,B3,6C,66,74,A8,08,C4,9D,3F,A8,B1), + wwH16(C8,76,1D,3F,93,CF,0B,4D,93,6D,EA,09,5F,E5,06,B9), + wwH16(31,B9,0E,68,72,9E,3D,EE,0E,4B,98,CE,56,F5,8D,4C), + wwH16(31,42,E0,9C,95,14,9D,85,FD,7E,00,C1,10,65,CF,BB), + wwH16(F4,4C,84,C4,86,6B,9D,08,1E,D5,2B,69,F3,DA,62,14), + wwH16(66,F2,3E,53,B2,D9,DF,4C,6D,3E,0C,F4,C1,4D,80,E1), + wwH16(97,CF,09,2B,20,43,C6,C5,C1,09,73,3F,55,E9,C6,18), + wwH16(60,98,BE,3E,55,D2,91,15,6B,24,37,5A,10,A3,EA,B9), + wwH16(96,C7,52,4A,75,85,B3,90,B9,82,20,17,B7,AF,17,19), + wwH16(19,11,6A,26,E8,CB,27,E3,98,11,8F,61,7A,F9,46,2C), + wwH16(36,C6,42,D8,EE,30,EC,DC,38,C2,22,79,D5,61,51,58), + wwH16(31,EB,BC,E4,B4,84,2E,E5,51,E8,C9,EC,73,60,0B,B2), + wwH16(35,FB,DF,19,99,07,95,7A,35,96,AA,84,CA,EC,1A,58), + wwH16(B4,AB,84,B9,80,43,AD,F1,A4,F7,72,DA,35,8A,B8,25), + wwH16(41,30,2F,B4,19,86,22,5B,49,29,97,D2,88,80,D7,68), + wwH16(68,DD,A4,9B,27,AA,A1,B2,09,06,05,4F,28,5F,97,2D), + wwH16(39,C2,C4,09,AD,2E,55,F0,9D,ED,33,F9,10,97,39,C7), + wwH16(F4,0B,C2,95,DE,E4,A3,39,03,46,7C,2D,31,0A,42,99), + wwH16(89,FC,A5,28,B0,49,96,3B,CB,5B,D2,53,A2,70,2E,9D), + wwH16(ED,BB,FF,D8,C1,49,73,F9,AF,6C,A7,26,D9,CA,48,AE), + wwH16(29,89,D0,0C,F9,9D,48,F3,DB,F7,46,6E,E8,E5,FE,40), + wwH16(54,FD,9A,CD,BC,10,00,7D,FB,26,38,65,B6,8A,87,87), + wwH16(AD,DF,60,8C,2D,AD,21,CD,7F,C8,58,C0,8A,3D,E9,9E), + wwH16(B5,81,57,DC,13,73,D2,D8,30,0D,EA,EC,49,0B,80,0C), + wwH16(FD,CB,8E,A9,64,6A,75,96,4A,12,2D,BE,3F,AE,43,5D), + wwH16(B8,0F,FC,AD,06,6C,2A,60,27,8B,59,17,F5,FB,8F,3D), + wwH16(15,A2,B0,E7,F0,13,5C,5F,87,88,94,FE,5E,39,E9,D3), + wwH16(25,E2,80,80,AB,B0,79,21,7F,3A,DD,44,31,43,9B,F2), + wwH16(61,8E,0A,AF,69,D9,96,3C,77,6E,95,AB,D6,B1,D0,05), + wwH16(70,71,BC,0E,5D,C1,33,00,7B,4E,CA,02,4E,D3,E8,B2), + wwH16(B2,BC,47,7B,A3,2C,00,A0,23,CF,D3,8C,13,F8,84,32), + wwH16(1C,26,18,DD,6B,6A,C2,1D,88,1D,50,EB,E9,32,89,DE), + wwH16(E2,F2,48,1B,14,06,ED,3D,02,D9,04,FF,5D,B3,32,19), + wwH16(7E,2D,A1,FA,65,E3,BF,82,91,7C,64,2B,CC,F7,A0,14), + wwH16(28,83,20,35,DA,0F,DB,AB,D4,81,49,48,5C,A9,C3,B4), + wwH16(1A,69,B2,FE,9F,54,E7,0B,E8,D0,BA,12,3C,EE,F7,B7), + wwH16(E9,EA,D1,5D,2E,96,C2,6E,C9,8D,43,2E,51,EE,B1,28), + wwH16(42,1A,CC,CC,1F,6F,8E,56,84,B7,65,A5,E3,A5,53,92), + wwH16(58,E7,5E,10,FD,93,8B,7C,7E,76,02,01,69,3A,5D,7C), + wwH16(0D,C1,00,7E,0A,0A,FB,E7,07,51,99,1C,15,F1,6B,1E), + wwH16(38,B0,93,A9,CB,B7,B7,4C,63,9B,AD,0A,31,C7,96,91), + wwH16(5C,F2,87,02,CA,66,3F,7A,E3,C7,01,B2,43,18,27,9B), + wwH16(ED,D5,94,17,0D,35,88,DF,A9,48,C1,BE,51,3F,D9,14), + wwH16(B0,75,D4,E1,83,D3,B3,2F,F7,AF,2F,E1,99,53,29,0E), + wwH16(6F,A8,0D,B7,EF,AD,5E,2F,EB,EF,15,CB,74,C5,BE,ED), + wwH16(9C,4E,66,5B,98,05,8C,06,C0,8C,AD,01,96,5E,A8,4D), + wwH16(6C,F0,1B,7B,F6,D1,26,1A,9E,3B,54,EE,D9,32,3C,F4), + wwH16(4C,B3,AA,1F,3D,66,6F,73,62,68,15,9C,3A,99,FC,BE), + wwH16(B4,C6,FB,2C,8E,A5,15,64,2F,C5,50,6B,F0,E2,13,70), + wwH16(A5,36,35,70,E2,CB,CD,C3,DA,51,43,9B,B0,21,BD,46), + wwH16(28,13,65,A2,58,24,79,C2,76,D4,F2,F3,AC,13,B6,BD), + wwH16(38,5E,CB,6F,04,52,F3,4E,68,C6,75,B2,4A,6F,53,28), + wwH16(57,0F,C4,A7,A8,6E,03,AA,D1,EA,75,6F,A1,0C,34,97), + wwH16(1B,B3,24,23,95,AC,B0,4C,5B,93,49,6D,84,A8,3B,FF), + wwH16(B2,FC,FB,84,8B,1D,04,67,9D,FB,E8,B4,7E,BE,23,A2), + wwH16(91,75,5D,36,EF,AB,26,BF,7B,DC,CB,D6,20,CF,F7,9A), + wwH16(88,4F,A1,B0,B8,9D,94,01,F7,F2,E3,FD,BE,43,DB,DC), + wwH16(20,46,13,CA,19,84,21,C3,ED,E7,BC,DE,55,85,38,92), + wwH16(CC,FD,D6,E9,B8,A5,C7,5B,B8,19,FD,23,44,34,C1,B1), + wwH16(D3,B7,76,83,5D,77,F3,88,3A,F3,05,A9,AF,EF,73,E0), + wwH16(29,54,90,C6,40,59,AC,FD,84,24,1B,85,DF,14,E9,DD), + wwH16(E3,67,82,1F,31,F6,F0,6D,15,6F,D2,E8,A9,CA,BE,3F), + wwH16(3C,97,6C,E8,C6,A2,52,99,4D,4A,34,0E,5A,90,C4,BC), + wwH16(72,68,40,C9,03,60,41,66,26,27,A2,A7,5B,94,26,72), + wwH16(90,07,0E,9F,08,A0,89,CF,E6,B5,38,A9,02,1C,DD,04), + wwH16(4A,E1,C4,CC,48,D3,65,69,49,8F,3B,8C,B6,07,59,6F), + wwH16(13,59,0B,5B,D2,F8,45,93,C2,33,1F,BB,00,50,C4,EB), + wwH16(31,5C,43,CC,96,D5,44,39,94,8A,B4,A2,8D,2D,8C,D6), + wwH16(49,12,C5,39,09,63,91,46,C7,E4,62,32,41,D2,98,B0), + wwH16(DF,D5,03,84,8E,68,20,E4,E3,A0,BA,CD,A8,12,E9,99), + wwH16(42,96,A6,10,BA,4F,CA,77,45,7B,A3,4C,7D,1C,45,28), + wwH16(9E,65,A0,3D,83,87,33,54,EF,0C,AF,B1,B6,75,ED,A3), + wwH16(49,2C,DC,06,00,44,DD,CE,FC,42,14,01,13,A4,F0,03), + wwH16(BF,3E,D0,10,3D,F8,45,84,85,51,10,66,A1,3A,80,0F), + wwH16(9F,80,2B,12,53,3F,A9,C5,81,25,AC,22,E0,F2,C7,35), + wwH16(E0,0C,87,D3,A5,82,B5,8D,12,B7,A6,93,BF,CF,73,82), + wwH16(E8,83,1C,B8,E9,1B,FE,88,8B,59,61,FE,E2,34,C9,BA), + wwH16(5D,A1,2E,1A,13,06,8D,47,A8,DA,D2,E5,81,D5,D1,25), + wwH16(CE,E8,03,03,9A,51,2F,E3,C4,AA,9C,7B,B1,B1,4A,B4), + wwH16(A6,60,FD,DA,36,94,C9,AE,2E,2B,A0,2D,5B,5F,F4,58), + wwH16(36,EE,49,8E,DB,BD,28,12,F0,63,AA,7A,C5,34,C3,DF), + wwH16(30,91,E6,77,F4,61,E4,17,09,02,DC,33,A6,A2,E2,E7), + wwH16(8F,03,29,03,38,52,57,D6,A0,91,F6,36,26,F2,9F,70), + wwH16(B1,F3,33,26,A9,55,16,87,87,9B,52,D9,09,57,B1,B2), + wwH16(08,DB,16,5D,D8,AA,79,84,57,B9,3E,2C,12,22,13,31), + wwH16(14,9B,2A,9D,7C,2D,03,67,8A,AA,27,36,D5,42,B9,18), + wwH16(A8,70,91,F6,89,15,80,A9,24,EA,50,9F,35,31,D1,1B), + wwH16(01,72,08,92,A8,53,13,70,36,80,E6,79,B8,8C,80,E0), + wwH16(57,F0,D0,5E,9A,E1,DB,AA,64,D2,D4,7B,4E,83,0C,00), + wwH16(2A,09,1B,05,17,C6,C1,F1,BC,B3,30,1F,4D,33,7E,2B), + wwH16(58,5D,61,34,01,0D,3D,EE,2F,36,7A,B5,BF,15,CC,06), + wwH16(F6,70,2A,EE,8B,AB,1F,B4,B9,B4,7E,99,73,FB,ED,DD), + wwH16(DF,2F,DC,F8,A9,08,A5,7C,75,CA,1A,51,10,ED,F1,98), + wwH16(EB,D9,B3,16,84,CB,D6,DB,B4,98,72,99,15,AE,03,2A), + wwH16(B3,8C,81,E8,19,E9,4E,49,6B,09,9B,5D,D8,0F,B6,85), + wwH16(96,42,4E,79,BD,82,07,9F,BB,46,A7,96,80,DB,C0,A2), + wwH16(82,94,85,D3,DD,76,37,E7,FB,F0,70,76,10,ED,6F,07), + wwH16(BF,C2,ED,D3,46,65,F1,D6,5F,80,84,8E,FC,05,16,E1), + wwH16(BD,2E,8F,70,2B,0E,93,0B,6C,96,E9,8C,EA,AF,40,71), + wwH16(9B,74,CA,7D,D3,67,55,58,92,4C,75,A9,21,BB,B7,9D), + wwH16(A9,A1,70,69,7D,02,C3,E3,E4,84,59,F8,B1,F1,07,66), + wwH16(1A,C0,D8,E3,19,24,66,74,BA,C8,2B,F2,02,72,C7,D4), + wwH16(D6,62,26,EC,7B,A1,12,39,6E,D6,84,32,BB,80,70,E0), + wwH16(72,DC,9E,D4,3A,1D,7B,06,B0,66,89,87,25,17,E7,90), + wwH16(32,A7,A1,05,9B,D7,A3,F5,E2,30,42,05,E8,80,6C,8D), + wwH16(BF,F8,60,E7,41,08,15,ED,68,CF,8D,23,72,F6,F9,59), + wwH16(76,9E,40,98,69,EA,E4,33,3E,DB,DC,B9,77,8D,95,B7), + wwH16(42,2C,AC,C1,8A,6A,5C,36,96,00,88,15,0F,67,8A,8B), + wwH16(60,34,14,CD,22,35,42,42,32,58,FD,55,7A,B0,42,5A), + wwH16(08,CB,B4,1F,EF,20,ED,33,A6,CD,CF,0E,89,7C,2B,5F), + wwH16(4C,E4,3A,F9,9E,47,5A,62,12,BC,64,48,FD,92,6A,90), + wwH16(00,68,6C,FA,C2,BB,67,D0,F9,3C,29,FB,44,17,97,25), + wwH16(59,58,34,B7,69,02,BF,A0,6E,A7,D7,AE,A1,C6,8C,11), + wwH16(EF,28,31,94,24,10,F7,76,C7,55,ED,5C,A4,E4,38,11), + wwH16(48,AC,5A,32,D3,56,DD,2C,AF,43,B9,3B,B8,EE,F2,57), + wwH16(6D,C0,84,32,B4,C1,33,03,C0,FB,CF,06,9D,7A,72,6B), + wwH16(CD,55,89,2C,F7,56,14,79,37,5E,1B,F9,FD,E9,F3,D5), + wwH16(53,28,7E,7F,04,67,2A,DC,CB,0F,55,F3,5D,FD,F3,11), + wwH16(B1,DE,DF,2F,0E,F8,33,C9,78,1C,13,25,9E,39,DA,D8), + wwH16(3E,E6,EA,4D,D1,F5,51,E7,A7,57,2E,C5,54,46,56,F1), + wwH16(21,51,17,A3,D1,BB,5F,22,7B,2C,F2,89,DA,C9,45,67), + wwH16(54,01,F4,AE,92,60,DF,30,9F,32,76,78,F4,AD,63,7E), + wwH16(F1,DD,4A,10,26,A8,F1,EC,05,7E,98,01,96,13,C6,1F), + wwH16(12,F2,BB,88,16,3A,AD,5A,87,91,9D,E0,61,49,BB,1C), + wwH16(63,45,7C,5E,3D,5B,20,1D,54,4B,DB,07,3A,FB,A3,CF), + wwH16(0B,7C,EA,78,72,A2,99,38,A7,87,E2,C6,C9,40,92,C8), + wwH16(B9,D5,7E,DA,34,04,41,3C,94,66,32,41,3C,49,AD,31), + wwH16(D8,6D,40,F4,24,6C,4D,1C,A2,42,D9,29,4D,34,EA,E5), + wwH16(1B,C7,C9,FB,94,B0,1F,A9,0D,DE,12,BC,73,6F,39,9B), + wwH16(BE,2F,6D,9B,21,D3,40,4F,99,8D,3E,81,84,E3,B7,9B), + wwH16(C7,42,BE,BE,76,EB,E8,8D,D9,B5,58,A6,A7,56,1D,09), + wwH16(10,64,DC,89,33,13,70,5A,E3,7F,AD,C8,A9,F7,16,16), + wwH16(86,9F,44,8D,4C,8F,CB,B3,F5,14,5A,41,7E,C9,69,8E), + wwH16(01,BA,B3,35,68,66,8D,3B,B9,0B,92,C4,C6,DA,6D,ED), + wwH16(C1,27,C2,A9,74,1E,C7,78,2C,A7,B3,C7,BE,77,AD,CE), + wwH16(AD,8B,41,55,F1,14,2E,80,E3,43,B8,70,4A,5C,85,86), + wwH16(2F,29,42,95,E1,4B,E2,78,E8,59,D6,E2,B3,B2,E7,68), + wwH16(1C,A0,7E,5E,30,76,39,54,45,C3,F2,25,F5,6C,9C,A2), + wwH16(08,28,9F,A1,5E,2F,2D,BA,5F,42,EA,DF,1A,F7,B3,B0), + wwH16(98,93,ED,01,07,8E,71,F5,52,A3,5D,AA,A5,B2,25,1A), + wwH16(D9,23,7C,92,63,D1,0F,DE,CF,4E,77,CA,BB,1F,13,8B), + wwH16(E9,13,B5,52,4F,5F,2E,22,A9,DC,37,F3,40,A8,CC,3A), + wwH16(A2,12,69,D7,A9,91,A6,5C,21,E3,36,81,7C,3F,8E,E7), + wwH16(A2,72,11,79,A8,FD,C3,EA,86,E2,0F,FA,3A,93,56,93), + wwH16(AF,45,7E,CD,E0,0E,18,54,D7,35,6E,D1,19,77,01,AB), + wwH16(63,4B,56,E0,45,8E,26,E3,BC,99,A5,38,7E,43,6A,99), + wwH16(52,A0,3C,35,FE,D5,F0,2C,3A,28,7B,7B,1E,13,06,16), + wwH16(E5,2F,AB,CC,30,99,79,F7,62,4D,2B,5E,D1,33,56,D5), + wwH16(0E,5E,16,45,77,16,28,05,F8,40,7F,D9,DA,B6,93,8D), + wwH16(66,F0,42,CE,E0,CB,ED,AD,11,69,E8,45,8D,57,DC,C8), + wwH16(CF,F6,12,D0,E0,C9,40,B5,E4,B2,02,F5,9B,65,3D,F1), + wwH16(54,8D,CD,08,E9,6E,00,92,88,CF,27,3C,3E,E8,11,C3), + wwH16(E6,21,86,41,8E,70,05,D5,5A,32,4C,24,1B,3A,42,F1), + wwH16(75,E0,F9,78,90,4D,81,63,80,1A,59,EB,92,AA,7F,6A), +}}; diff --git a/src/crypto/bign/pre/bign256_pre_si8.c b/src/crypto/bign/pre/bign256_pre_si8.c new file mode 100644 index 00000000..85f70d0d --- /dev/null +++ b/src/crypto/bign/pre/bign256_pre_si8.c @@ -0,0 +1,1040 @@ +/* +******************************************************************************* +\file bign256_pre_si8.c +\brief STB 34.101.45 (bign): precomputed tables (l=256, type=si, w=8) +\project bee2 [cryptographic library] +\created 2026.03.05 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +\remark Built with bignPrePrint("1.2.112.0.2.0.34.101.45.3.3", ec_pre_si, 8). +******************************************************************************* +*/ + +static const ec_pre_t _pre = { + ec_pre_si, 8, 64, { + wwH16(82,11,24,4A,07,D8,95,0E,E9,60,15,75,3B,A0,00,B1), + wwH16(93,0A,78,7B,1F,88,20,94,5F,7E,36,A2,11,CF,58,B2), + wwH16(FA,43,41,31,B0,41,EF,F5,47,E5,59,DB,D6,6C,25,73), + wwH16(66,F9,30,94,AB,4C,FC,6D,4E,C3,73,4B,8B,53,C0,66), + wwH16(71,9A,EE,F9,A8,1E,B6,FA,0B,E7,13,86,46,88,51,FF), + wwH16(13,D6,26,D3,B5,01,3D,2F,D5,58,8E,6C,0A,00,C5,22), + wwH16(A0,C8,26,2F,C3,18,32,71,B3,FB,95,C8,92,ED,F9,5C), + wwH16(E4,F7,83,FD,AC,C7,32,99,7D,44,E8,70,ED,8A,83,07), + wwH16(72,C0,78,2D,75,69,80,F0,31,19,47,30,65,27,3D,E3), + wwH16(3A,82,BD,C9,7E,DC,97,AC,C2,E4,8A,95,C6,41,62,A6), + wwH16(33,C7,BC,ED,4B,9C,A7,73,1D,C1,91,88,52,B0,9A,C6), + wwH16(AA,1D,1B,6E,9E,A1,A4,93,A0,AA,4E,FA,45,40,C4,DF), + wwH16(6D,93,FA,9F,E0,20,24,F7,D2,EF,21,11,4A,14,3A,34), + wwH16(66,51,E7,5E,14,3B,FC,89,52,FB,30,5C,1F,6E,59,40), + wwH16(6A,5B,81,A6,D9,C6,45,57,33,01,47,A3,D9,8A,5B,C2), + wwH16(99,B6,F5,27,66,5B,53,5E,65,B2,98,5E,88,AA,F0,05), + wwH16(80,81,32,EC,19,93,FD,28,66,79,A6,E1,79,D3,F7,44), + wwH16(8E,44,C8,A4,D0,E7,81,99,3E,15,50,8E,0B,58,68,69), + wwH16(74,9C,BC,13,0B,FB,5C,67,04,6B,A7,94,D6,1D,69,3C), + wwH16(A2,CD,4E,6C,0E,1B,E5,3C,91,9E,A8,26,31,4E,9E,D2), + wwH16(8D,B5,B9,81,B0,4B,9F,9C,F3,C7,F6,5B,F9,A3,AA,10), + wwH16(4A,A9,3D,4C,2B,23,93,C0,A4,BA,C5,D3,4D,60,E6,EC), + wwH16(80,3F,F3,79,85,33,FD,F9,68,17,96,8A,3E,11,58,65), + wwH16(16,0B,15,12,5D,EA,CD,98,CC,52,C6,C4,2B,79,D4,39), + wwH16(B1,FD,CF,8D,8B,BC,40,06,5F,85,BA,38,80,A9,37,9E), + wwH16(7C,06,59,FA,26,B2,A8,B7,AB,BE,59,18,B5,E1,72,F1), + wwH16(74,AC,66,75,E5,F4,F9,08,9D,0A,76,C7,7C,24,1E,1D), + wwH16(01,26,BA,1D,A3,48,67,3C,AE,47,D7,76,8D,8C,E5,53), + wwH16(FD,6C,11,AC,B6,E4,80,38,4E,C8,88,14,F9,5B,B1,E1), + wwH16(70,FF,EA,FF,3B,37,2E,27,C1,A3,08,95,DE,FA,BD,C9), + wwH16(8D,E2,DC,13,2A,DF,7E,9A,80,A0,65,0F,56,16,71,B4), + wwH16(26,3F,12,FE,D4,C1,AB,7D,0C,05,75,FF,4D,F8,C0,47), + wwH16(BB,2D,11,E7,80,74,36,3C,42,DE,75,2D,D0,BE,2C,53), + wwH16(04,33,E2,87,58,6B,A7,69,FD,0C,2C,77,79,67,08,0B), + wwH16(6F,5A,22,3B,43,00,97,A3,E5,54,45,5A,5E,BE,FF,CF), + wwH16(9E,AD,1A,0B,D1,FB,2D,18,65,0A,EC,9D,50,09,CD,B4), + wwH16(A7,F3,7A,8A,49,39,BE,06,4E,31,B9,C3,FA,40,84,E1), + wwH16(97,45,52,B4,88,C6,D1,62,1C,1C,BE,31,AE,38,AD,AB), + wwH16(52,4A,BB,B0,CC,7D,93,AF,83,2A,88,7C,DB,96,7F,69), + wwH16(4B,70,AC,0C,87,B4,AD,F0,23,DE,C8,74,3E,43,3C,D1), + wwH16(CC,18,53,FF,3E,FA,97,33,36,44,91,10,DF,3B,32,FD), + wwH16(BA,D0,BD,02,F0,35,6B,2A,28,FB,D7,06,B4,7C,F6,69), + wwH16(27,60,D2,E0,C6,30,B9,D6,62,0A,40,9D,D7,B2,06,BF), + wwH16(5F,F4,09,03,A1,E5,E0,A1,C2,6D,E1,85,B0,77,0C,F5), + wwH16(DA,22,08,8F,C7,D7,72,62,54,A3,94,6D,CF,43,64,D6), + wwH16(DC,85,D9,30,81,6D,C4,23,BE,6A,BF,6E,46,D0,1C,1A), + wwH16(10,22,1E,D9,52,2C,26,2E,CA,63,B0,67,AB,91,63,3A), + wwH16(A2,66,25,22,5D,1F,C3,03,F6,A8,D3,E3,6A,7B,1C,E3), + wwH16(F7,D7,8E,3B,C5,4D,00,7C,AD,FE,79,6B,19,0C,D9,B0), + wwH16(C4,90,26,C1,17,7F,90,62,E1,2D,EF,EF,74,C2,48,92), + wwH16(DD,0E,98,1C,E9,0E,92,14,C6,4E,65,B4,09,A6,10,D6), + wwH16(D7,C1,AB,A3,BC,25,67,9A,66,FE,4C,B3,6D,43,37,D4), + wwH16(DE,49,44,47,AB,88,A3,21,99,05,E1,88,54,FE,65,45), + wwH16(D0,17,60,C5,BD,31,86,86,9D,B7,7B,ED,2D,19,FA,7D), + wwH16(FE,97,40,EE,E3,42,75,38,38,66,C9,DA,DF,98,5A,15), + wwH16(94,F2,24,5F,36,FF,CD,F4,4A,2B,E2,18,E8,45,1B,E9), + wwH16(C5,F9,76,30,61,E0,20,63,2F,3E,5A,13,45,CB,2E,2C), + wwH16(3D,13,6B,A5,0E,77,32,A1,F7,2A,17,00,72,01,C7,44), + wwH16(72,65,66,34,F2,C8,E7,63,F1,0F,5F,E4,AF,36,B9,A9), + wwH16(71,10,27,2A,C6,04,F8,57,F9,55,86,AF,EC,47,82,F9), + wwH16(23,DE,46,9F,00,9B,A6,DD,51,6E,92,9F,9D,B0,36,45), + wwH16(35,8F,7C,44,7C,27,A5,24,19,81,E3,82,F5,2B,52,16), + wwH16(92,0F,0E,22,F1,C3,71,FC,18,BE,01,57,C6,4F,CB,31), + wwH16(DA,00,3C,A2,44,18,F5,5B,14,95,B3,D5,AC,B2,1D,A8), + wwH16(ED,F8,93,E3,32,9A,E4,55,F5,67,5B,79,94,09,7C,74), + wwH16(82,62,FD,82,92,2B,6A,5E,B0,5C,4F,4E,DD,6B,F8,61), + wwH16(8A,98,DB,18,54,2C,58,A3,32,2C,CC,50,68,F7,79,38), + wwH16(83,1A,B2,32,39,7F,C0,22,C1,F8,E9,C5,D6,20,1F,9F), + wwH16(CB,E0,2F,09,7C,91,09,C8,37,62,12,FE,E2,7D,F6,24), + wwH16(28,09,88,CD,0E,A1,25,A9,3C,71,6B,5B,C2,F6,6C,AC), + wwH16(69,D0,C5,36,0B,5D,50,89,7F,95,2B,3F,55,A7,31,56), + wwH16(81,F9,84,42,FD,22,C6,0E,F1,45,F0,1A,07,5D,F9,9D), + wwH16(2D,BA,08,A8,4A,0E,26,64,9C,28,60,88,0C,8C,A1,F9), + wwH16(28,9D,BD,24,BB,41,E7,5C,1F,0B,FC,05,3A,E4,F0,D5), + wwH16(C2,7E,38,4E,BB,CF,5C,57,17,26,32,87,F6,79,82,88), + wwH16(6A,11,7F,BD,87,48,7E,EE,D1,DF,C0,DB,D5,45,D7,8D), + wwH16(53,95,3A,68,B8,26,05,03,75,5A,5A,DB,46,FB,E7,5C), + wwH16(E4,38,FE,1C,C5,F2,30,BE,B3,17,8E,C6,37,6F,E6,40), + wwH16(B5,86,BD,F9,AE,42,C8,02,40,0C,9E,F8,A0,A8,65,56), + wwH16(D6,8D,65,56,31,16,48,C3,F0,F0,7B,95,6A,60,5D,B5), + wwH16(D1,DB,0D,7B,DF,8C,88,65,D3,DC,F1,71,C9,16,05,28), + wwH16(AD,56,F9,EF,3B,DF,38,0E,E5,53,F0,B5,1E,9A,93,5C), + wwH16(7A,6B,5E,67,6C,45,ED,DA,A0,85,4F,2C,29,B1,09,80), + wwH16(D4,55,17,A2,6D,92,1F,66,74,6F,6B,B3,C2,1D,93,E8), + wwH16(7F,F7,7B,25,8A,EE,55,9E,0B,EA,96,5B,42,B2,2A,88), + wwH16(7E,45,35,75,5C,0F,6A,BC,01,63,9C,E8,41,90,C1,E9), + wwH16(6D,19,02,06,DB,DA,F7,AD,50,43,58,42,47,51,53,01), + wwH16(9B,41,CE,17,8B,BA,3A,5D,C9,DE,0B,31,BF,F4,5D,B2), + wwH16(03,DF,08,97,5B,4A,7A,AF,E8,3B,BB,2A,D2,B2,B6,D8), + wwH16(F9,80,C2,6C,2D,E0,53,C5,26,BD,DD,DB,8B,E9,7F,3C), + wwH16(B8,F5,4E,D0,B9,5F,B6,AC,8D,0E,DD,60,4E,C8,34,E2), + wwH16(DD,59,30,8B,33,88,11,51,AC,EF,01,6F,62,71,75,64), + wwH16(F3,2A,6A,5D,BF,2D,C6,04,C0,E3,02,00,10,BA,C9,A1), + wwH16(F4,1B,2D,22,8D,76,D9,8F,FA,BF,78,89,E9,C3,A4,94), + wwH16(10,9D,D0,30,B9,68,4C,56,9E,B2,31,60,94,37,B8,7C), + wwH16(79,1B,FD,68,B7,62,B9,5A,41,64,4A,2E,FB,DB,69,A9), + wwH16(2B,8C,03,0E,16,EB,AB,EB,6C,64,D0,FD,80,EC,1B,88), + wwH16(87,1D,45,B3,D9,AA,F3,42,4D,06,CB,D4,FA,CD,ED,46), + wwH16(16,16,CF,7B,8D,0D,EB,07,FF,58,23,BD,38,17,35,A3), + wwH16(25,B2,D2,E8,94,88,9D,3A,00,83,99,98,7D,23,99,A5), + wwH16(E6,91,EF,D8,FA,1B,EA,4A,85,E4,13,AD,43,2E,A2,DC), + wwH16(26,38,AD,FA,03,5A,AD,46,2B,83,33,D4,FC,3C,C1,FF), + wwH16(51,AE,6B,E6,27,18,8E,58,79,6A,91,70,DE,4A,C6,B2), + wwH16(01,61,13,05,5B,38,16,77,FA,ED,D9,10,EC,15,24,0B), + wwH16(7E,D0,4E,34,4B,67,AA,13,1A,A8,CB,25,AE,EE,4C,F3), + wwH16(EA,DC,A4,6E,62,38,4F,9A,39,EA,A8,A6,26,8C,EF,52), + wwH16(A9,B2,52,7D,7B,8A,09,58,64,2C,13,35,8F,9B,91,02), + wwH16(E8,CC,D0,EC,4D,EA,76,91,DC,6C,88,AC,4E,EB,8C,9B), + wwH16(F9,54,31,BB,7C,AE,EC,6E,85,E0,6E,49,05,04,B5,46), + wwH16(93,E8,D7,90,C5,36,1D,38,BF,E5,DB,CF,92,8B,AF,62), + wwH16(46,E7,22,C9,07,BB,68,CA,D1,A2,33,39,FE,9C,E9,D2), + wwH16(4B,DC,5F,89,0D,32,0D,2D,B3,B3,69,DB,A8,FF,86,82), + wwH16(80,69,25,3B,E3,4B,48,1F,75,66,32,F4,52,46,7A,E9), + wwH16(24,C0,3D,54,BA,1A,17,E2,63,81,A7,08,0D,4B,C5,DE), + wwH16(90,3F,A1,AE,69,F7,A0,74,EA,4D,66,8F,1F,88,3D,55), + wwH16(E3,BA,1B,83,C0,F4,42,2B,87,A1,2A,01,0E,D6,D0,B4), + wwH16(41,2C,E9,84,8E,BC,9C,09,A9,67,C3,7E,1D,B8,8C,04), + wwH16(D6,F2,8F,8A,A4,00,19,55,6B,16,E0,AF,E3,60,D8,23), + wwH16(5E,71,B5,D4,B8,54,FE,F3,97,F7,CC,D4,93,FD,3B,13), + wwH16(32,60,38,38,86,F7,A5,5E,94,54,BC,9F,3F,30,8E,EC), + wwH16(84,6F,06,4E,D2,56,D7,0D,8F,23,28,F9,A6,4F,EB,35), + wwH16(F9,48,F8,D3,B8,A2,48,B9,A5,40,D3,3C,20,92,36,1A), + wwH16(16,00,84,5E,46,28,17,AB,0F,F1,7A,FC,FA,A7,E2,A6), + wwH16(EC,A0,5C,E6,29,26,91,29,B5,C3,1A,7A,1C,0E,83,99), + wwH16(B6,FE,79,49,FD,F4,A9,AA,3F,7E,AA,EB,36,5F,98,5D), + wwH16(45,6A,24,0D,8B,4D,22,F6,A4,9A,73,32,1D,BD,79,94), + wwH16(11,9E,85,68,34,C6,B1,00,3F,C2,10,03,2D,4A,E1,FA), + wwH16(37,A4,AD,D4,CE,8B,F8,EB,10,B8,01,98,85,78,54,E0), + wwH16(88,2F,84,31,3C,B7,52,2B,41,B0,9B,DE,E8,A5,CE,04), + wwH16(91,61,F5,58,55,25,53,97,94,6A,A5,51,FD,7A,6A,3F), + wwH16(7D,12,C2,04,D1,39,E0,8F,E4,FA,D2,0A,6E,7C,1B,CD), + wwH16(2C,D0,DE,33,90,12,77,24,76,37,4D,92,0E,4D,9C,B5), + wwH16(99,89,A5,88,45,03,2C,E1,53,92,E9,99,5A,4B,26,91), + wwH16(D7,D2,C9,71,30,F1,18,6E,C0,3D,A7,53,6A,31,85,4D), + wwH16(32,F4,4F,09,CF,08,FE,89,09,E5,59,DE,27,F2,75,03), + wwH16(01,D8,4C,BD,22,AC,EC,52,69,51,E3,15,B4,E9,D9,F6), + wwH16(5F,39,6D,59,9A,2F,E4,44,C5,68,6E,E8,53,CD,18,7B), + wwH16(5E,4A,65,C9,BF,CB,DC,8D,B6,41,AA,8D,2B,11,C1,B2), + wwH16(7A,F9,59,16,79,0C,C8,9C,DE,C4,27,F8,B5,21,8B,E4), + wwH16(8F,C6,39,F1,A5,D0,AE,D3,51,AF,53,9A,BC,CC,FC,4A), + wwH16(4D,B5,CF,48,FA,D0,E0,2D,7D,5D,76,9B,7D,F8,CC,07), + wwH16(8A,B8,6E,AD,DC,5D,88,50,73,0F,52,B4,54,57,6A,9E), + wwH16(4B,5E,52,51,0E,BC,57,2D,DA,F9,EB,C3,59,E3,39,2B), + wwH16(D3,CF,4A,D9,CF,AE,2E,89,69,29,E0,1A,66,B7,66,82), + wwH16(C1,F7,24,8C,57,52,1A,4E,F2,2F,5B,DB,EE,63,F9,91), + wwH16(3B,8E,D2,09,A3,E5,52,B4,A1,77,F3,3D,FB,36,12,53), + wwH16(A2,1A,72,ED,48,86,09,6B,72,6D,40,56,EA,6A,DA,C4), + wwH16(D5,E0,DF,A1,93,42,4A,55,14,E0,F2,EE,D4,9C,61,3F), + wwH16(A9,DD,E5,4A,A4,5E,F9,E9,0D,4F,3B,06,39,8B,DB,DA), + wwH16(21,C3,C4,CB,18,F5,CA,F9,DC,38,B6,40,75,97,21,62), + wwH16(87,73,33,D1,8D,B6,8C,E1,ED,52,4D,55,1E,84,05,23), + wwH16(63,AD,C9,34,3F,EC,56,BC,D5,61,1E,66,7C,3C,CC,FA), + wwH16(4C,16,D5,DB,C5,D5,4D,AE,D0,FC,D9,13,BA,6B,F4,BE), + wwH16(37,A3,42,E7,62,1C,07,36,CD,CE,EF,1D,3E,1D,44,5E), + wwH16(02,61,F7,01,5F,68,C9,AD,0B,DF,08,CA,1C,F0,D6,61), + wwH16(58,C5,B2,7F,6F,D4,87,B5,5A,3F,C2,E5,48,E1,D1,9F), + wwH16(AE,C4,11,89,BC,95,66,EE,1E,9B,D0,26,72,13,B0,55), + wwH16(85,5D,1D,A8,D0,28,E5,BB,F0,59,61,EF,80,6E,E6,DC), + wwH16(38,7F,8C,05,6C,5B,22,A9,5E,85,30,03,90,CB,EB,28), + wwH16(AB,06,AD,AB,CC,69,19,EB,7B,11,8D,01,63,65,DA,76), + wwH16(36,02,53,64,64,EE,BB,24,1F,5A,FF,8F,09,0C,2C,6D), + wwH16(0B,73,E1,81,8B,DF,7E,55,3B,36,12,0A,42,4D,8B,0B), + wwH16(EF,14,4B,BB,A1,7A,D3,97,22,8D,70,37,5D,14,9E,D7), + wwH16(DE,F1,D3,83,F9,35,72,51,0C,3A,A7,86,47,7F,B8,D8), + wwH16(8D,6E,B1,C1,AC,DA,15,B3,5F,56,DA,D6,F2,63,BE,21), + wwH16(07,E9,42,8C,E0,51,14,C4,A6,25,27,D8,40,61,35,BB), + wwH16(CA,26,8C,87,D1,11,67,64,67,26,BA,87,9A,31,0B,87), + wwH16(CD,2A,7B,EC,0D,8C,89,93,E9,11,25,CA,B0,F5,3E,72), + wwH16(B4,63,9B,67,03,3A,A6,81,05,2F,4C,9E,90,AB,9F,02), + wwH16(78,1C,C5,2F,44,43,D0,B4,26,AF,07,54,4C,82,11,F9), + wwH16(73,A6,CB,9E,FE,B0,FA,E8,3F,DC,47,6D,B8,D8,13,39), + wwH16(ED,2A,56,B0,87,2C,EA,F8,FD,33,1C,CE,5E,0D,9C,82), + wwH16(E9,B4,D1,7D,28,FD,17,92,37,37,AB,1E,E8,26,32,EB), + wwH16(94,7A,E6,78,A3,D3,12,9A,7E,5A,E0,33,9C,4B,C1,F5), + wwH16(B7,B7,EB,EB,D1,39,B5,4C,64,93,1C,10,13,67,53,C9), + wwH16(83,41,5B,23,31,65,3A,B2,4F,1F,C0,97,5C,89,3E,4B), + wwH16(A2,5E,55,45,11,6E,80,DE,EA,59,70,BE,F8,B5,C9,98), + wwH16(D7,A3,56,05,B6,20,61,53,45,08,1F,89,8E,37,E6,59), + wwH16(53,1E,07,AC,4E,21,48,A0,1F,F5,56,CB,37,1E,4A,D0), + wwH16(D8,36,69,55,67,DC,4F,EA,1C,42,2B,C7,A0,34,56,E0), + wwH16(72,C7,37,B9,E6,10,A3,A8,FB,8D,B2,4E,2E,2B,6B,A4), + wwH16(86,42,BE,AC,BD,18,4C,42,56,B1,09,AF,40,0E,45,D5), + wwH16(F6,9E,A0,6C,57,F5,63,97,CE,49,AF,72,62,42,E8,79), + wwH16(34,01,95,7A,5F,81,D1,91,5F,F7,09,E9,39,1F,F6,D0), + wwH16(A6,E6,86,07,D2,F6,80,D1,26,B9,C7,1C,2B,56,64,CA), + wwH16(E7,53,36,8B,FE,CE,34,BC,A3,8C,DB,14,BD,35,7D,AC), + wwH16(1F,7B,C7,E4,E2,E1,B7,48,44,98,CF,BD,6C,55,CB,27), + wwH16(6B,C1,8D,95,52,4F,63,E7,BC,6D,0D,17,D0,A5,6D,6E), + wwH16(4F,A7,2E,1E,9C,FE,B8,20,79,90,1F,19,6F,99,C6,48), + wwH16(9F,40,50,82,74,97,9E,16,8E,AC,65,F8,21,A7,1F,08), + wwH16(11,34,19,EA,8B,7B,B2,BA,FD,9B,59,FA,1D,65,26,9B), + wwH16(A0,B6,52,51,2E,38,3F,F3,BE,27,6F,C3,45,1D,D6,99), + wwH16(6D,20,7A,03,00,B7,1B,8F,88,B3,76,F6,50,0A,CA,B4), + wwH16(86,9F,43,11,14,48,F8,17,8B,DC,CE,6D,08,2E,97,C3), + wwH16(F1,67,AF,62,60,F5,06,1D,EF,40,4B,2D,CA,65,A9,FD), + wwH16(38,7D,30,56,00,18,1A,14,17,4F,47,8E,A0,76,5F,6C), + wwH16(4E,47,0D,35,DE,77,F8,FA,31,46,8C,8C,0A,38,9B,B7), + wwH16(F6,EB,4E,C1,B1,B1,FB,17,62,43,BC,BC,68,8A,FD,DF), + wwH16(B8,A2,F7,BB,63,A7,E8,5C,C2,C7,FB,40,85,A2,21,47), + wwH16(24,7F,00,0A,10,E5,D1,94,49,CF,5E,2F,DB,A2,AB,1A), + wwH16(5E,43,52,0E,21,7A,82,EA,0E,D9,EA,EC,A8,85,26,77), + wwH16(C6,6E,E7,5B,A6,3C,F3,0C,40,DD,77,E6,69,FA,C2,7E), + wwH16(28,47,FF,9C,8C,81,98,BF,73,FF,06,41,E7,0C,B6,1F), + wwH16(9E,4C,29,EF,47,F7,37,16,74,0C,B8,27,C0,14,AF,C4), + wwH16(E0,7F,23,B4,68,51,44,EB,B1,71,81,12,D1,F3,6C,71), + wwH16(9A,6A,E3,C8,A6,00,88,A2,3C,41,6D,86,7C,F7,34,3B), + wwH16(ED,C2,4E,B2,4C,71,6F,ED,83,B4,D0,5F,C8,56,D1,D5), + wwH16(48,21,64,25,18,94,84,30,D4,2F,3B,97,82,CB,6E,47), + wwH16(2E,B9,23,C1,49,8E,5A,D6,DC,8B,1D,C2,1F,4E,7F,B0), + wwH16(79,00,A8,A4,55,88,36,E4,6C,B5,9D,19,DA,16,31,36), + wwH16(5C,A3,D6,00,93,6D,EF,35,02,72,75,28,C2,0B,FA,D9), + wwH16(31,63,60,9B,4F,F9,1D,0F,54,61,FA,FA,15,BA,C0,82), + wwH16(09,0C,07,B5,06,AB,87,A6,D1,8A,4A,13,CD,69,85,5A), + wwH16(F5,10,73,E5,9A,0C,15,2C,31,82,89,8E,DD,08,2C,E5), + wwH16(24,A3,2B,20,A3,D9,63,54,DB,16,BC,97,C5,54,F5,6E), + wwH16(4C,1C,AE,20,23,D3,01,85,81,D4,1F,52,8B,E9,B5,CC), + wwH16(6A,49,C3,98,B4,33,5F,41,F2,A7,E2,BE,EB,09,84,1A), + wwH16(BC,F1,69,4A,AA,DF,DD,23,8B,04,F5,0F,25,EE,A9,90), + wwH16(AF,B3,06,E2,B9,34,9C,B9,6B,25,58,CC,32,85,87,16), + wwH16(7D,E7,C2,89,FE,47,70,D1,14,8B,0B,5C,AA,B2,AA,A6), + wwH16(6A,3C,20,11,D3,B3,FD,4B,58,67,87,9D,6C,0D,95,11), + wwH16(D5,B9,DF,18,0A,6A,F1,1D,A1,46,BE,3E,4E,F2,A1,2F), + wwH16(1E,93,E1,34,31,52,5B,87,0B,40,A1,BB,04,2A,BC,A4), + wwH16(E6,E4,27,C9,38,EC,72,CC,8D,3E,AB,25,81,06,14,86), + wwH16(9F,DF,9E,A0,69,2D,E3,04,D7,60,7A,BB,B2,5B,30,EE), + wwH16(61,F9,17,2A,32,B7,98,2D,2A,F6,E8,76,D5,B5,1C,E3), + wwH16(2E,FD,90,BC,67,AB,0D,DA,8A,21,FC,D6,BE,14,0F,01), + wwH16(97,FD,83,04,AF,7C,B1,CF,A0,D8,27,68,60,59,11,9D), + wwH16(55,F2,D6,FB,93,51,99,CB,11,8D,63,6D,A9,1E,26,9F), + wwH16(98,23,B1,D9,9D,B0,3C,88,8F,F3,37,C8,FB,4E,53,E7), + wwH16(74,22,4B,A1,AE,63,F4,A6,F0,40,3F,BC,CC,FD,7A,48), + wwH16(E1,0A,90,A2,59,38,CA,BD,4D,92,BE,FB,94,BD,3F,E3), + wwH16(6A,2F,81,51,C6,C1,03,76,65,D0,FF,8D,72,D7,BC,CA), + wwH16(EA,2F,3B,D3,4E,3F,74,4E,7B,3A,BD,71,C7,E8,6E,A3), + wwH16(E1,54,6E,0F,BC,51,30,DD,5B,A4,52,8F,B1,53,CA,12), + wwH16(F4,72,BE,6F,A6,5E,E2,24,00,DD,88,E8,FF,48,D0,20), + wwH16(D9,DF,9B,12,2F,AE,39,34,01,FE,3E,C9,13,DD,3D,DC), + wwH16(F1,83,8D,C8,7E,A5,86,8A,40,62,94,B7,D2,29,48,9F), + wwH16(EE,04,8F,42,CF,63,D0,50,E6,31,E4,89,85,80,AB,FC), + wwH16(B8,BE,04,3D,63,38,D1,81,4F,C3,1C,6E,6B,24,6F,22), + wwH16(E5,1E,A2,3C,62,23,FC,76,A7,26,7E,A6,2A,4E,ED,66), + wwH16(03,49,CC,38,D8,15,F9,86,15,C3,EF,2E,AC,9F,A0,20), + wwH16(EE,BD,BE,77,45,29,2F,26,F8,2F,9D,19,B8,38,EF,FB), + wwH16(14,CD,44,13,31,F5,E1,A3,48,6B,D5,89,21,10,E0,A5), + wwH16(0A,F6,6A,64,15,B7,53,19,A9,A4,EC,2A,89,4B,95,08), + wwH16(9D,84,C2,C0,37,C9,36,B6,F6,DE,00,5B,45,10,47,32), + wwH16(1D,8D,8C,FE,FD,B8,DB,F1,04,83,BF,D1,AA,B0,23,D0), + wwH16(F8,33,9C,DC,47,62,75,E8,F3,A6,0F,BD,66,0E,20,C7), + wwH16(09,88,6D,0B,9A,21,42,F6,79,06,A2,13,9D,12,61,21), + wwH16(19,F0,76,31,AA,E8,58,BC,FB,1E,2D,5B,72,49,1A,D4), + wwH16(08,DD,DF,14,B5,EC,34,62,BE,C9,E6,09,2E,65,EA,B3), + wwH16(3A,47,3B,57,AD,E0,0B,B4,F0,84,3F,09,E7,7E,F1,B2), + wwH16(74,28,B7,74,72,A5,73,A0,5E,4B,A0,2C,7D,97,9D,E6), + wwH16(83,93,65,4D,03,E0,F0,49,AC,F8,DB,4F,08,28,DD,9F), + wwH16(1A,13,36,9B,94,17,DD,8E,E5,58,1F,F0,D2,F2,4D,FC), + wwH16(F2,6A,87,8E,9A,A8,69,FF,A3,F0,8E,CF,E2,94,4A,AB), + wwH16(30,23,5B,B7,03,37,90,C2,6B,97,82,A0,CE,49,AA,33), + wwH16(7B,9A,F7,1D,E3,4D,9D,DE,F3,4E,97,30,A5,B2,5E,21), + wwH16(4B,C5,28,E7,92,20,21,2B,59,C1,B7,05,C6,50,1F,E6), + wwH16(3D,04,E1,D3,54,0B,CE,62,66,5A,88,04,4E,EA,A4,13), + wwH16(6D,39,A0,CB,2E,93,26,C6,A7,A6,AB,89,CC,54,7B,AE), + wwH16(35,E2,63,09,DB,97,E7,87,F6,63,26,5F,BD,17,D3,CA), + wwH16(B0,35,5A,67,E5,81,9A,32,55,CB,29,93,57,44,5F,28), + wwH16(FE,F0,33,73,4B,B2,87,E3,41,95,C7,44,E7,20,4F,CE), + wwH16(30,46,CB,59,A4,35,61,31,13,30,B4,13,18,28,5B,D0), + wwH16(2A,02,63,5F,CE,3A,E5,08,CA,74,E4,07,41,78,DA,C2), + wwH16(9D,42,41,26,1E,40,8D,0F,42,4F,25,35,7A,39,06,CA), + wwH16(FB,37,E0,20,32,18,F3,81,78,91,0E,12,67,AE,E3,74), + wwH16(03,EC,94,94,FD,91,51,E4,97,0A,C1,3D,E9,BC,F7,CF), + wwH16(AA,FE,BB,8C,A6,11,54,CE,A1,54,E4,67,89,7E,4E,82), + wwH16(4C,19,84,51,B2,F2,33,64,7E,77,B0,C5,9B,95,B2,FB), + wwH16(CA,82,54,5F,24,6C,2A,64,E8,02,60,66,B2,52,CF,07), + wwH16(CB,5D,CB,8C,C0,36,00,BD,29,6B,01,06,52,32,19,32), + wwH16(BD,F0,2E,97,36,3F,B1,4D,D3,18,58,A1,84,BF,B6,6A), + wwH16(57,AB,99,E4,B7,B0,A7,C1,14,43,DE,41,F1,DA,BC,4B), + wwH16(9E,34,6F,5F,C6,B5,28,00,39,91,AC,D8,20,9B,10,BE), + wwH16(31,2A,B6,81,B8,BB,1D,C6,CF,7B,80,5C,FE,47,E0,EA), + wwH16(E3,56,7C,6D,8E,B8,F8,16,94,B4,F8,AB,BE,E2,0D,1E), + wwH16(AF,6C,1A,CF,A9,5A,B6,A2,2A,3C,84,86,99,2D,E4,97), + wwH16(C4,9B,15,6C,95,FE,B9,6E,57,A3,CD,C0,3D,39,1A,EC), + wwH16(62,BC,D2,F0,15,BC,DD,BC,19,BC,AC,E3,D2,B8,CE,A8), + wwH16(47,A5,BC,FF,47,10,5A,90,E7,04,A3,81,7D,D5,30,CD), + wwH16(45,6D,BA,E9,A4,88,7B,69,2E,C4,87,23,98,CE,4D,CA), + wwH16(B1,F6,0E,13,74,BF,9E,BF,31,9D,F3,F1,FC,9E,E2,EF), + wwH16(97,F5,BE,DA,81,F2,3C,6E,8C,0F,6C,5C,8A,71,7A,4E), + wwH16(35,5B,A8,90,7D,E1,44,31,9B,0D,5F,D6,5E,B8,4A,53), + wwH16(BD,D1,32,60,23,8B,CA,C0,F1,08,9B,F3,80,D9,8F,FD), + wwH16(0C,3B,C3,0B,9A,FD,0A,2D,46,7A,F9,6A,FD,E0,92,3E), + wwH16(B6,AB,75,82,F7,08,3E,6D,DA,C7,42,A4,3A,96,B4,96), + wwH16(AA,0D,22,A1,1B,6C,51,D1,FB,BD,38,00,BA,65,57,9D), + wwH16(83,A8,63,94,1F,10,64,9E,A8,74,52,24,79,04,6E,38), + wwH16(A2,32,FF,BE,AD,9C,3B,7D,75,F1,EB,C6,D4,7B,0C,4A), + wwH16(60,6E,C1,E1,3B,9C,F1,E7,5D,51,C8,65,32,2C,4A,7F), + wwH16(76,C4,AF,BA,3B,71,A6,D4,B3,F1,58,3D,46,D6,99,91), + wwH16(A1,4F,72,12,49,79,C1,54,82,07,46,1F,57,8F,16,0F), + wwH16(1F,91,74,91,08,ED,72,96,E9,98,48,C1,44,23,22,EC), + wwH16(3D,ED,65,27,29,F6,E7,41,2E,FD,C7,18,ED,8F,B1,7C), + wwH16(72,CD,67,69,F8,DF,19,3C,60,3E,0D,8F,D4,64,EC,20), + wwH16(E6,A6,93,94,30,AD,DB,35,76,67,75,FE,F9,EB,52,40), + wwH16(EC,6F,81,71,88,80,CA,B5,B2,E1,D6,22,E3,1A,BD,83), + wwH16(52,D4,4F,01,33,AD,99,2F,4F,6E,AC,A6,EA,A2,78,AB), + wwH16(D1,29,67,0E,3D,DA,71,9B,E5,44,0E,97,7B,4E,DE,8E), + wwH16(13,17,B7,A4,D9,FD,BC,A0,A2,38,FD,39,2F,45,EA,E4), + wwH16(37,46,39,1E,A5,29,B9,AD,9D,E6,D9,7A,24,AD,78,30), + wwH16(91,34,4F,88,92,EA,62,02,CB,E0,0B,42,00,60,CC,32), + wwH16(5A,0C,27,D7,9D,27,41,F4,D7,F5,C3,1F,DA,C7,A6,5E), + wwH16(CD,61,1F,12,C9,71,62,4B,D9,9B,5D,66,35,2F,22,B2), + wwH16(7D,02,0C,CB,03,08,3B,B9,42,6F,79,8B,91,B5,BE,70), + wwH16(A3,D7,3C,3A,D8,23,A4,58,DB,10,C4,E1,7E,22,76,8E), + wwH16(1E,42,30,76,E3,1B,B3,87,85,EC,1B,64,B5,D3,CD,81), + wwH16(FB,9B,AA,5F,21,79,68,A7,56,0F,0F,EE,42,EC,86,52), + wwH16(F5,84,21,41,1E,A3,72,61,A7,68,05,0C,40,FE,13,58), + wwH16(3D,E6,81,BC,68,1B,79,74,6E,24,E6,5C,D2,23,8C,0D), + wwH16(99,03,E3,DB,95,A3,72,50,15,8C,D5,14,7D,88,16,5D), + wwH16(DD,A6,27,0D,BF,AA,FC,3D,8D,33,D3,0A,78,82,03,ED), + wwH16(77,CA,5A,A4,B6,B5,17,5A,70,D3,0D,12,7D,A9,77,32), + wwH16(0C,0E,F4,F0,ED,B6,B8,24,B3,C9,8A,F2,78,B4,3D,84), + wwH16(E9,E2,27,4B,D4,5D,1C,8E,B5,1B,34,0D,28,7D,5F,18), + wwH16(BF,4A,E1,DD,E5,33,F0,9D,D9,32,39,AE,5A,AF,E4,DD), + wwH16(54,D2,41,D5,0F,98,59,45,11,E4,A7,FD,F3,D7,BF,9C), + wwH16(6E,19,81,2D,D6,6A,8D,AD,EA,70,40,69,C8,C3,AB,66), + wwH16(00,CC,6A,8F,3F,54,63,9A,2F,02,A6,BA,27,7D,45,C9), + wwH16(B7,D3,B2,56,C2,2E,01,2B,87,BB,11,38,AE,41,D8,48), + wwH16(3E,9A,1F,85,79,8D,B8,0D,CB,4E,8C,6D,8D,5F,07,C1), + wwH16(65,24,13,BA,1B,6D,97,82,60,45,69,E4,25,A2,77,B3), + wwH16(9C,1E,8B,3B,54,AC,34,D8,E8,B1,CD,BF,E3,19,25,A3), + wwH16(2D,9D,0F,4A,AA,CE,53,A7,51,0B,CD,A5,A0,6C,C1,A9), + wwH16(5E,4F,C6,18,2E,E2,97,B5,6C,41,8E,86,28,EE,D8,CE), + wwH16(91,C2,04,2C,BF,90,CE,DA,2B,9F,C7,F9,65,7A,41,B5), + wwH16(7F,9B,44,CC,B1,D1,49,D8,12,B2,9C,77,AA,D9,50,08), + wwH16(E4,06,EB,AF,2B,67,C9,EC,56,51,A7,BA,DF,C9,D7,2C), + wwH16(4B,B0,8B,0F,7E,03,8D,5E,F0,46,BB,28,CD,EA,4B,16), + wwH16(DC,3E,85,C2,6C,42,EE,0D,0D,03,E0,31,83,0D,1B,9E), + wwH16(13,53,7F,F6,E9,32,AE,D0,16,2C,89,7E,00,4B,05,06), + wwH16(6B,4F,19,C1,99,F2,ED,0F,06,85,53,5E,3B,39,2D,C7), + wwH16(B2,5E,80,00,70,1C,82,49,76,37,07,70,EF,34,58,ED), + wwH16(99,DC,38,AA,EA,FE,F2,AA,41,6F,FD,93,44,1A,82,24), + wwH16(66,93,14,26,B2,9E,55,76,4C,F5,3A,59,B3,99,B0,2F), + wwH16(D8,4C,CB,FE,A2,03,4B,08,E3,A2,55,17,1A,F3,3C,89), + wwH16(DD,63,1C,3C,40,AE,3A,F5,7A,CD,8A,2C,C5,0D,B7,13), + wwH16(84,FA,4E,9F,A9,23,E4,79,C9,5D,CA,0B,83,EC,2A,A0), + wwH16(BA,E4,B0,8E,5A,84,83,72,5B,1A,DC,F7,8C,CC,A6,BB), + wwH16(6E,E5,B0,00,97,D3,F1,C6,F0,BD,63,E8,F8,05,91,D8), + wwH16(E9,22,B9,4C,E7,E8,07,66,87,5B,A5,9A,AD,52,60,D2), + wwH16(2F,D0,B8,53,C9,0B,D1,9A,85,17,43,B2,B5,5D,A2,C8), + wwH16(42,5A,AD,E6,61,F3,14,44,F2,8D,21,60,BF,D1,09,42), + wwH16(72,9B,25,C6,1A,B9,44,2F,0C,BA,02,3B,32,BC,48,72), + wwH16(71,D5,20,FA,78,99,60,5A,7A,12,8B,61,60,A3,1F,A8), + wwH16(BA,64,B1,44,51,74,EA,3D,CE,EC,70,7A,A6,27,9F,EB), + wwH16(EB,3E,1E,20,29,09,96,F3,32,BE,20,6F,F7,99,BD,FF), + wwH16(6D,3C,85,5D,3E,10,77,DC,61,44,A6,74,D0,1F,F0,14), + wwH16(E7,AE,19,E3,2B,22,07,CD,28,48,12,5A,3B,9A,F9,BC), + wwH16(3D,4D,42,31,A6,6E,BB,CD,9E,B1,92,63,D9,BF,C0,CC), + wwH16(2F,B1,75,4D,8F,DF,0E,EC,6C,6A,4A,21,AC,BF,E1,37), + wwH16(6F,64,A2,C3,A6,55,DE,F4,8C,E2,2F,EB,56,59,D2,94), + wwH16(66,69,DE,BC,F4,6F,BA,11,71,7B,7A,C2,85,E9,39,98), + wwH16(81,F0,E1,61,3F,23,DC,A6,C5,4E,0E,5C,88,39,DC,C0), + wwH16(8B,B4,BF,9C,21,8A,D0,59,32,AD,B6,56,EB,0A,43,6E), + wwH16(03,3F,20,BA,39,96,9D,44,9C,31,A9,7D,41,F4,BF,83), + wwH16(95,05,2D,92,5F,BD,C0,C7,B9,A4,D6,F3,54,7E,72,75), + wwH16(DD,5C,E5,E0,6D,77,CF,1B,89,5B,9C,65,8D,85,F5,BC), + wwH16(1E,1E,2B,26,20,40,32,E3,08,95,DC,AF,C6,57,88,15), + wwH16(42,A3,7F,D5,12,44,F6,1C,21,5F,25,9D,DB,42,B6,0D), + wwH16(83,92,21,2B,F4,9D,BD,23,E6,6A,EA,CB,75,54,12,6F), + wwH16(6E,A8,58,32,FE,A2,E2,F9,63,60,12,6A,75,95,13,AD), + wwH16(8D,12,B3,ED,5D,31,02,3B,20,66,70,EC,3B,5C,9D,6D), + wwH16(85,AD,8F,A6,65,7E,CB,A3,EE,1F,31,5E,01,28,9E,81), + wwH16(62,C3,60,14,28,6D,8C,70,FE,BA,53,BA,D1,BE,D4,F9), + wwH16(C4,30,AC,41,C3,DD,76,18,69,2D,CC,70,45,88,EA,89), + wwH16(37,03,2F,EE,20,A7,89,16,38,66,4F,78,10,BB,7F,8C), + wwH16(DB,DA,FC,A4,E0,0A,2F,92,CE,EF,16,8B,1A,2F,1B,5B), + wwH16(35,B6,B6,81,91,31,68,87,B7,85,18,D3,FF,54,D1,86), + wwH16(70,63,22,2A,84,A0,79,5F,E6,69,A7,EF,05,2E,42,D9), + wwH16(F4,F6,57,A9,47,2D,27,FE,BD,40,18,74,56,A0,61,37), + wwH16(C0,7C,86,67,23,32,E4,9F,3A,B1,1E,0B,12,A5,B5,C1), + wwH16(AD,F7,67,5E,EC,C2,74,D8,5E,89,77,C1,E8,F1,09,2E), + wwH16(6C,F6,95,4F,C4,13,38,66,64,8E,E0,FD,98,40,0E,27), + wwH16(F1,DC,7F,3A,59,CC,E7,02,97,97,B9,54,3B,FE,3A,FC), + wwH16(3E,C3,62,61,F3,92,EB,A4,11,FA,BE,9A,26,D6,DB,27), + wwH16(F8,22,5B,39,E0,EF,D4,ED,2B,11,27,2C,C6,D7,E1,91), + wwH16(71,66,79,FB,6A,2D,13,32,43,8E,83,D8,99,2C,75,F4), + wwH16(15,2F,B0,25,8E,9F,71,00,4E,86,AC,91,F0,53,95,CD), + wwH16(4D,7C,B3,C2,CB,D2,B2,84,87,14,EA,1B,1E,F1,17,13), + wwH16(AD,06,C0,6C,2D,AB,FA,AE,06,9A,EA,D6,7C,3B,18,04), + wwH16(1C,78,66,56,24,5D,32,53,01,FF,6F,50,0D,D9,4D,9C), + wwH16(5E,C9,3C,81,1F,20,6A,93,10,60,80,E2,35,B5,6E,9B), + wwH16(54,69,D8,F3,6D,30,DD,12,0A,AE,1C,16,CC,B3,36,0E), + wwH16(5F,39,00,CA,D0,33,54,42,51,0F,D9,B1,2C,80,04,5D), + wwH16(5D,66,A3,4E,8C,0D,D1,F0,41,4A,7D,2F,B2,1C,29,CF), + wwH16(9B,F7,BA,3F,0C,82,82,6F,FA,0D,1D,D1,E9,95,25,C6), + wwH16(7D,D3,C2,D2,9D,64,86,61,B7,07,C1,F3,A6,39,D3,79), + wwH16(38,50,CA,A8,3E,81,81,9E,A2,20,1B,56,FA,80,55,0D), + wwH16(10,80,C7,78,0E,83,E4,2E,8B,AA,48,9C,39,ED,7F,EB), + wwH16(97,43,97,D2,81,61,A3,EF,34,AB,A8,58,98,7B,D7,9B), + wwH16(45,C9,68,6A,58,C6,22,AB,39,AA,91,C8,8D,EB,35,36), + wwH16(09,7D,EB,49,3C,F5,DC,66,B6,0E,BF,AF,C8,D5,39,15), + wwH16(46,E4,2C,D2,DC,17,4A,C9,30,14,95,9D,0A,1D,60,C4), + wwH16(A8,CA,4F,18,08,CB,D5,16,75,D5,E8,35,F6,21,C6,B7), + wwH16(30,56,51,22,EF,21,A1,59,0F,DC,99,33,B2,A4,EC,5C), + wwH16(3A,F9,E6,C1,26,64,39,F9,B3,C2,74,00,50,60,5E,9E), + wwH16(40,E4,A3,FD,33,5A,7B,42,97,69,04,C7,95,7E,46,22), + wwH16(FF,86,F3,0F,D2,82,DA,ED,15,27,31,9F,38,BF,99,FB), + wwH16(6F,63,C4,4E,AF,A1,EC,D2,48,F2,4D,35,9B,15,CB,98), + wwH16(58,17,F7,AC,7D,2C,F4,7F,60,12,C6,CB,AD,E8,3E,2D), + wwH16(0C,2D,08,AA,FC,E6,11,19,A9,93,9D,27,75,6C,BE,6A), + wwH16(E3,DA,22,64,8F,23,B7,91,DD,C5,CF,9D,30,40,C3,6F), + wwH16(9B,17,85,7C,12,1F,1D,56,26,0E,FE,27,B3,44,B3,88), + wwH16(9D,78,25,81,7D,2D,F3,CA,AD,43,86,D4,BE,D2,2F,4B), + wwH16(66,D3,96,9D,96,E2,8A,B7,85,13,5D,16,30,6B,49,EA), + wwH16(2A,E7,11,99,72,11,83,90,7F,60,E4,63,3B,8D,94,09), + wwH16(33,97,1E,FF,14,3D,65,E4,67,F3,4E,96,C0,8B,EB,13), + wwH16(90,85,78,17,C3,1A,C4,3D,3F,38,71,7C,47,64,D3,07), + wwH16(FF,8B,BD,8F,67,7A,0E,17,FD,EB,DE,5B,18,9C,4A,2F), + wwH16(C7,97,16,DF,45,92,20,ED,1C,D4,7D,40,0B,A2,E3,3E), + wwH16(BD,42,4E,58,61,F9,9A,9F,D0,23,49,EB,1B,3F,26,43), + wwH16(52,29,F9,63,58,4E,94,1E,E0,EE,C0,E4,63,62,28,CC), + wwH16(7E,0C,98,95,4F,76,C9,C0,15,EE,F1,4C,D7,76,3D,36), + wwH16(CC,A5,D8,77,29,F1,DB,5B,96,8E,01,FF,AE,87,44,9E), + wwH16(E1,0C,78,36,18,0C,F0,7A,74,8C,0A,41,57,93,DF,41), + wwH16(F8,21,61,58,99,44,18,8D,F9,47,75,74,99,B4,A1,E1), + wwH16(A2,B4,56,DA,C3,FE,9F,F4,65,BE,E3,AD,0F,40,36,00), + wwH16(D8,3C,28,56,55,F2,68,FF,E8,E6,50,9E,83,E7,A9,E2), + wwH16(F2,9F,EE,0F,42,69,30,27,C1,A4,FE,3B,09,EE,15,17), + wwH16(47,22,BA,32,E2,FE,EB,89,E5,45,1F,38,B0,B6,06,A8), + wwH16(B9,29,E4,20,9D,2C,C1,2B,04,62,65,94,F7,12,58,5D), + wwH16(64,57,D5,4E,1C,D1,50,AB,B2,60,33,06,6C,D9,C7,EF), + wwH16(1A,DA,86,84,16,F9,F5,4C,0C,89,60,E1,68,BD,C2,BF), + wwH16(46,C7,20,74,1C,BA,16,33,E8,1F,43,B3,6D,55,FB,6E), + wwH16(D4,F0,DD,D5,BD,E8,2C,CD,34,91,D7,1B,3B,58,D5,AC), + wwH16(DF,73,9B,11,05,11,D0,22,29,D3,BD,76,0C,FE,20,D7), + wwH16(2D,26,2D,DC,43,46,CB,E7,CC,D5,42,07,45,BF,2B,85), + wwH16(3C,A0,A5,E9,65,6A,4C,75,97,AA,88,16,1D,51,43,9B), + wwH16(59,2E,70,0B,90,23,44,9A,C4,5B,6D,F0,F2,B9,83,BD), + wwH16(B9,6E,B2,FC,9A,D4,7D,A9,0C,23,E5,BD,4C,92,A5,4A), + wwH16(EE,3C,45,3F,AE,8F,F9,52,65,2B,FB,D3,E2,D1,29,6E), + wwH16(5F,22,8F,5E,1D,2D,87,6E,F5,BD,DD,33,89,F6,AC,5D), + wwH16(C7,19,8A,6B,D5,52,A7,F3,CB,E7,88,FA,4C,AC,EB,90), + wwH16(40,FE,47,09,05,FE,B6,4A,F1,5C,18,82,7D,41,E1,7C), + wwH16(97,71,3D,D9,61,3B,06,3A,A6,BB,3B,47,57,DB,95,67), + wwH16(61,B9,F7,02,85,2B,10,47,77,B9,4E,54,CE,C7,6F,70), + wwH16(77,8D,E9,2E,68,0B,8F,0B,79,FB,BF,FD,70,89,EE,AD), + wwH16(DF,E8,D2,6A,FF,95,16,63,58,B3,14,A6,1A,AA,D5,F9), + wwH16(1E,EF,C5,55,9E,A2,34,D1,C9,5D,EE,D0,36,04,29,F4), + wwH16(3B,11,08,76,BB,68,7D,4A,5D,DD,2A,E7,41,D6,0E,E4), + wwH16(B4,30,F9,F8,08,4A,E4,4A,E7,FB,A4,CF,F2,4F,67,F8), + wwH16(3D,59,6A,3F,67,23,B9,1C,6A,7F,74,ED,9E,DB,80,31), + wwH16(18,F7,F4,C8,0A,2E,8F,87,1A,33,F4,2B,E3,28,8C,55), + wwH16(8B,A9,D1,D9,EF,E9,20,CD,F7,10,4F,71,BE,6F,AA,26), + wwH16(00,EC,97,CE,C8,2E,27,AF,C4,68,04,9A,D0,B4,B4,8F), + wwH16(3E,22,B3,19,F2,62,08,53,82,2A,1C,3F,B5,54,15,27), + wwH16(61,EE,30,EE,A4,76,9F,0B,A8,70,8D,9C,C2,B7,91,F8), + wwH16(FD,79,3B,8D,37,51,FA,8F,9E,FD,EC,54,0A,93,E0,67), + wwH16(80,5D,E8,4A,DC,07,0C,E2,DD,6D,88,35,62,82,79,39), + wwH16(4F,5E,60,2E,B4,6D,99,16,3F,68,7F,09,6E,96,68,39), + wwH16(0D,F5,69,EF,90,9B,8C,3C,D1,CD,58,E9,8C,C5,A4,AB), + wwH16(CF,DC,50,6A,F4,A1,45,23,EF,AD,C8,6F,AA,EE,A4,7F), + wwH16(81,13,E0,9B,DB,27,19,F6,B0,E5,D2,A2,E5,93,BD,B8), + wwH16(8C,6F,D3,DA,A1,DD,50,E2,5A,40,2A,A6,1C,70,F4,23), + wwH16(54,D9,F7,C7,03,BC,48,89,9D,2C,88,D1,CC,ED,AD,6F), + wwH16(60,95,F1,78,FC,2E,34,FE,91,B9,A4,2A,38,11,04,A6), + wwH16(B9,89,B2,C6,12,10,D0,03,C3,4A,3B,36,75,CE,D0,D6), + wwH16(7D,23,22,F8,87,E9,56,06,DA,52,A0,D6,40,B2,C8,12), + wwH16(05,C0,67,4A,6A,6C,9C,0F,C3,41,5E,0F,52,1E,76,F6), + wwH16(82,67,9E,DD,D8,77,23,73,CE,B5,74,BB,5F,7D,14,05), + wwH16(F0,AD,F4,67,56,E0,5F,68,C0,30,16,D4,D3,AB,DF,67), + wwH16(61,11,10,D0,28,7B,1B,C9,64,95,E5,BA,96,80,A1,49), + wwH16(6D,AC,96,31,C6,0B,91,B2,FC,11,FD,78,5D,B5,A1,C1), + wwH16(0F,37,CC,C8,49,4F,18,57,8F,42,CC,8A,EE,E9,DF,B4), + wwH16(B9,B5,6B,37,66,40,61,D8,EB,0C,B7,1F,D8,A6,96,86), + wwH16(2D,AD,AE,2D,E4,F8,5D,7E,7F,76,C0,91,BE,F3,A1,FE), + wwH16(0D,96,04,E9,4E,90,15,B8,92,A7,EC,B7,C2,CD,58,BE), + wwH16(BE,A2,AE,48,A8,62,65,FE,0B,5A,C6,3D,A9,E9,0E,FC), + wwH16(B4,7F,B3,62,4C,3E,42,0C,84,D1,DF,37,F4,B2,71,65), + wwH16(9A,99,35,FF,18,D0,74,AC,5F,18,E8,9C,37,ED,F8,54), + wwH16(78,CE,AF,63,DA,97,CE,A1,7C,B7,6A,ED,15,D6,A5,F7), + wwH16(90,96,FF,94,73,0E,B2,F2,15,B7,75,FB,FE,82,FD,61), + wwH16(BD,99,B5,77,FD,D1,BF,DE,8B,86,35,97,B2,AB,21,38), + wwH16(90,94,AA,73,F3,FF,EC,F1,25,0F,A3,E8,FD,97,67,BD), + wwH16(59,E9,6B,5D,FA,B4,12,BE,15,8A,C2,47,7A,AB,F9,B8), + wwH16(E3,4B,83,9F,60,18,23,79,C3,18,9D,4B,84,C7,7A,7E), + wwH16(AD,3F,39,E8,5F,9E,6D,F6,C4,55,D4,9F,C7,D8,23,57), + wwH16(DB,43,EC,5B,28,8B,FF,C3,80,D2,57,EE,E8,9E,B8,6E), + wwH16(F6,EA,62,2D,4D,B2,0F,EA,2C,F1,3A,5D,83,B4,51,9C), + wwH16(CB,8D,BA,BA,A5,0C,B0,6B,28,96,62,AC,87,D7,C9,B8), + wwH16(3D,3A,B9,A3,9D,51,38,55,E3,30,74,A5,15,70,8F,80), + wwH16(76,7F,DF,90,33,F3,31,DD,6A,E6,48,3D,8D,38,DA,5F), + wwH16(94,5B,BE,FB,0B,70,ED,E8,2A,E7,B1,40,38,82,60,0C), + wwH16(8F,99,A6,D8,DD,1A,EC,71,50,AA,DA,67,1D,19,B3,18), + wwH16(FE,A6,09,9E,DE,B8,AF,76,32,DD,37,35,BB,F8,AC,8C), + wwH16(FE,AC,96,26,3A,E7,B0,01,15,44,FC,B8,1A,E1,C1,06), + wwH16(FA,D1,BB,E1,94,E0,C3,7F,70,63,55,76,64,3B,5C,ED), + wwH16(2B,22,83,F3,FF,F6,7D,79,BC,A0,7C,4C,B3,E7,EC,39), + wwH16(07,E3,B2,E7,45,13,B0,05,83,29,5A,1A,F2,F6,81,39), + wwH16(DD,FE,C1,ED,9A,FF,40,DD,CD,DC,F4,69,57,44,61,E8), + wwH16(05,30,CD,EA,E4,8B,56,2A,81,F6,6E,81,71,37,01,E0), + wwH16(A7,92,1E,8B,72,D0,BF,4E,6D,CD,1C,03,30,E1,F9,66), + wwH16(D4,3A,E1,B1,B3,0C,BD,49,41,31,03,FD,87,E1,77,41), + wwH16(CF,2D,7D,39,AC,67,1A,6C,55,10,4B,6F,CB,4F,A2,1A), + wwH16(5F,B4,63,4D,7D,56,4A,AA,86,2F,AD,0C,73,3C,99,3B), + wwH16(4C,FF,CC,63,79,44,B8,CC,10,FB,4D,DD,23,7E,BE,CE), + wwH16(30,B6,05,C8,ED,20,C8,88,CE,7F,61,FB,C0,E8,E3,85), + wwH16(36,BF,C5,4A,25,11,F5,F6,DA,4B,C8,F7,D1,FE,50,AF), + wwH16(D5,73,E5,5F,CA,69,57,7C,79,4D,03,7E,40,5F,5E,F3), + wwH16(3D,81,9B,59,69,89,6E,1E,DA,D8,67,81,62,1D,E1,A2), + wwH16(15,A3,E5,28,31,4A,54,D8,2B,A0,1D,31,E0,4F,8A,AE), + wwH16(C1,BF,97,7D,24,86,AE,B5,97,FD,AD,76,CC,92,3F,D6), + wwH16(51,F7,FD,78,35,8D,9F,78,7E,62,49,50,51,83,BC,6B), + wwH16(37,EF,8A,69,B9,24,3C,0F,F1,67,58,7C,3D,94,0E,F3), + wwH16(9E,03,81,04,46,1F,50,78,46,0E,1B,2F,16,C2,F2,82), + wwH16(D1,49,87,6C,F2,C3,D2,06,6E,A0,E4,ED,D1,47,C6,CE), + wwH16(86,94,D8,D4,0E,2A,25,68,24,9E,41,F0,DA,FC,2D,53), + wwH16(BB,DF,70,A6,9A,85,F3,3D,B8,9D,4D,4E,5A,F6,D3,80), + wwH16(62,BB,3B,A7,B9,3B,B8,45,B4,4D,D5,7E,23,24,73,B4), + wwH16(96,7D,C1,88,46,B3,03,4E,AE,84,7B,24,63,53,7D,A4), + wwH16(30,89,5D,6C,EA,28,6A,0C,D0,1E,45,ED,BC,CB,2E,3C), + wwH16(BB,86,07,6D,80,B0,A5,44,2F,81,4D,95,5E,B9,2C,A6), + wwH16(93,63,C2,D2,B8,A3,C1,AF,78,B9,2E,8F,F5,0F,C1,45), + wwH16(7B,2A,83,75,52,19,93,AC,A7,98,2E,E0,F0,4B,3C,80), + wwH16(95,70,91,5A,59,3D,46,D2,97,2D,78,5F,6B,77,46,5B), + wwH16(F7,46,7F,82,5B,61,06,78,88,56,32,44,85,1D,13,D4), + wwH16(C3,23,B2,D7,80,A0,16,DE,0B,55,36,88,1E,52,C0,48), + wwH16(46,A0,0A,14,2D,3C,01,0C,4F,3C,79,3F,72,58,3A,3B), + wwH16(C8,67,81,FC,57,1D,7A,AF,D1,2D,DA,E8,36,7A,96,C4), + wwH16(7F,74,CE,EC,D4,4B,4D,A3,40,CD,2D,AD,49,31,78,53), + wwH16(E1,DA,27,0D,4D,47,55,39,AC,72,57,DA,4E,79,57,1D), + wwH16(1D,74,EA,E0,E4,81,27,52,C5,44,3C,4E,04,F0,27,C7), + wwH16(68,D9,CB,05,94,5D,5E,62,D2,C3,38,74,E4,BB,F5,99), + wwH16(6C,C7,0B,67,A0,78,D4,51,98,70,D2,BE,52,F7,1D,24), + wwH16(EC,D3,0A,F1,1D,53,F2,EB,28,8F,88,13,FC,5D,50,C7), + wwH16(F2,9C,6D,36,70,11,CB,39,80,B4,1F,64,FA,17,E4,DC), + wwH16(D8,E9,CF,81,56,6F,B6,38,91,B8,D4,C2,83,A3,E2,06), + wwH16(BE,25,4E,D5,72,10,80,9C,C6,BB,E9,7D,9A,43,5F,6F), + wwH16(9F,52,F2,9A,E6,0D,4B,EB,B0,DD,81,61,E1,CA,E7,65), + wwH16(34,43,4B,AD,CB,D3,C4,EA,D2,27,BD,C6,05,3B,96,1D), + wwH16(9B,B0,3C,42,57,9C,44,85,2B,D0,CB,5E,D4,58,DD,33), + wwH16(31,20,9A,86,20,2B,9B,68,57,03,16,D3,0B,89,AA,B3), + wwH16(12,23,96,A1,13,1D,BD,32,96,B1,63,B6,9F,AF,56,E8), + wwH16(BC,67,7E,E3,BA,C1,55,FD,58,39,B9,AA,D2,99,8B,D7), + wwH16(8A,11,DF,32,69,37,A4,3B,21,9E,95,A4,B9,B8,0E,0C), + wwH16(4B,FD,1C,65,CF,16,77,53,F8,3E,C1,66,F1,5D,0D,96), + wwH16(09,9A,ED,E0,07,6C,9C,08,2A,B2,6C,DE,BD,CE,B7,29), + wwH16(3E,1E,6B,85,F1,71,2A,8F,75,04,47,E7,80,62,34,42), + wwH16(6B,83,70,76,BA,8A,5F,1B,8A,B0,78,A7,92,CB,C0,2E), + wwH16(71,E6,44,E2,B7,05,6E,DF,07,EA,13,A7,EA,E9,F2,DC), + wwH16(54,F3,92,C8,AE,16,4C,5D,78,EE,1C,A6,DA,67,CB,C5), + wwH16(6C,C9,3F,84,0C,66,37,F2,AE,28,7C,32,29,1E,1C,86), + wwH16(B9,67,0E,2C,C9,F8,FB,3F,64,53,7B,18,31,AE,05,96), + wwH16(06,97,14,C1,A9,F4,D4,45,4E,B8,8B,A5,02,3D,D9,79), + wwH16(B4,D8,4B,2D,12,2D,2F,39,32,14,93,09,7E,A2,4B,25), + wwH16(2C,52,6D,EE,F6,0C,DA,FD,72,E8,4B,6E,05,3E,6D,4A), + wwH16(A1,9A,D0,31,D9,55,44,9C,BC,3F,10,88,E7,51,AB,64), + wwH16(F4,51,18,C9,A4,AF,80,35,36,24,34,43,5B,1B,34,FE), + wwH16(87,77,99,2D,8A,CE,E7,58,22,90,E4,BE,76,CE,20,4F), + wwH16(CD,A0,58,51,2E,8C,C6,F5,E3,F1,10,10,22,CF,73,72), + wwH16(1E,61,A0,3A,92,76,5B,74,05,86,4E,6F,AF,A8,10,64), + wwH16(63,0F,A0,24,8F,F5,74,A0,B9,CE,87,BE,01,F2,77,3C), + wwH16(A5,F9,2B,07,6E,45,C3,19,A2,E7,31,E1,84,8F,C9,E8), + wwH16(76,A4,5E,51,C1,4C,EC,90,19,97,FC,06,7A,AA,2C,03), + wwH16(59,7A,F1,B7,91,52,90,33,91,63,90,AA,0A,57,C6,79), + wwH16(65,DF,A1,5A,27,25,36,B8,92,D1,EB,22,56,57,9A,21), + wwH16(31,46,81,82,4C,AA,3D,71,B1,F0,7C,8F,DB,59,19,0F), + wwH16(DA,17,82,28,27,0E,8D,78,86,87,A1,D3,DF,62,2E,65), + wwH16(4E,92,82,76,DC,BB,39,3C,52,DC,64,8F,B4,9D,8D,89), + wwH16(7B,2A,D7,F8,73,98,96,A2,41,A0,7B,8E,AE,46,2F,64), + wwH16(6F,77,4E,11,A9,51,10,70,F3,8F,59,B9,8A,8D,CB,99), + wwH16(CD,9E,7D,B5,F3,6C,4C,0A,1F,72,8F,E6,99,D3,60,98), + wwH16(56,24,55,85,10,7E,92,84,B5,B8,00,AB,98,98,FA,8A), + wwH16(00,B1,61,F3,23,F9,1C,50,1A,CA,B9,03,AE,58,0A,05), + wwH16(45,9B,8F,CF,94,83,B9,8E,7B,5C,1A,7B,36,66,11,2D), + wwH16(D8,50,AB,23,F3,A8,B0,6D,96,2D,99,F7,95,C8,FE,81), + wwH16(AE,18,D2,0C,67,DA,6E,5F,27,2A,48,C2,FF,41,E7,72), + wwH16(97,40,4D,19,35,A9,A5,77,98,0F,CE,5F,BC,0A,B5,B9), + wwH16(EA,CA,D5,BA,3A,BA,B5,63,27,60,46,A9,95,3D,7F,2C), + wwH16(FB,56,C9,20,63,75,EC,57,72,7F,46,09,F2,77,C7,8A), + wwH16(A2,51,82,BC,A0,5D,1D,F9,0E,B5,37,7B,68,B0,B7,D8), + wwH16(0D,12,0B,B8,ED,4F,1E,98,10,A1,64,78,00,11,90,7C), + wwH16(9F,C9,DF,08,2F,5A,4C,1F,9F,AF,88,3E,26,08,EC,6D), + wwH16(68,F6,E0,A0,EA,9A,3A,67,E6,49,69,18,14,76,65,E7), + wwH16(91,B6,2F,A8,B8,CD,CB,99,C8,B6,5C,40,1F,1E,F1,8F), + wwH16(0C,6D,15,3E,1D,8F,8E,52,01,A2,20,68,97,F9,E8,06), + wwH16(FF,B3,5B,37,F6,1D,00,AA,79,98,EC,C5,D9,72,B7,BA), + wwH16(39,8B,57,EB,B2,B5,2E,2D,79,47,A1,AE,32,E9,8A,5D), + wwH16(34,D2,2C,33,C1,DD,1C,26,97,C7,C9,61,42,4D,07,4F), + wwH16(DB,46,46,D7,A9,75,1A,F1,76,E3,ED,7B,B1,8B,0E,BA), + wwH16(89,65,46,AF,C9,82,63,FD,6A,26,10,D5,7D,E2,95,09), + wwH16(D1,FD,D5,2E,6A,66,5D,C4,B3,53,9B,33,42,0F,BD,4B), + wwH16(2F,89,A7,D6,2E,71,8A,2A,85,01,63,9B,DA,73,36,AE), + wwH16(9E,25,91,20,15,7D,DC,A9,72,2B,29,C1,8C,B7,EA,DD), + wwH16(6B,7F,6E,7B,D0,55,0F,6D,FD,27,60,06,23,CB,A8,9E), + wwH16(72,43,88,8A,67,4F,08,C7,89,B8,EA,60,4D,D3,30,3E), + wwH16(F4,E9,66,00,90,6C,0E,CF,9C,9B,29,56,AB,D9,CE,1E), + wwH16(E5,89,33,18,3A,1D,26,12,EC,5A,D8,86,FB,DC,E3,C4), + wwH16(DB,C4,77,97,59,D5,8D,3B,BB,AC,11,7D,C5,A9,EE,1D), + wwH16(30,F4,EE,E9,DF,DD,A9,7A,95,82,0F,C9,B7,39,2E,9A), + wwH16(22,34,FB,04,8F,7A,F2,05,55,E6,0F,15,45,AD,4F,C1), + wwH16(86,EB,EB,EF,75,CB,97,F7,6E,7F,FA,54,61,39,C3,03), + wwH16(AA,09,CF,E4,78,71,D2,1B,21,B6,93,92,A5,18,19,27), + wwH16(47,81,D3,C7,21,AC,BC,9E,68,16,C8,33,3E,04,79,5A), + wwH16(91,54,A4,52,DF,EF,64,26,23,F1,CE,79,6F,DC,FD,D7), + wwH16(02,0A,B6,C5,89,D8,30,D2,97,CD,92,DA,88,24,6F,7F), + wwH16(F3,5F,AB,19,9E,2C,2B,9E,F3,83,93,85,A0,BD,DF,B0), + wwH16(63,5F,75,BB,BA,07,73,64,DD,F5,14,32,30,FD,57,5F), + wwH16(B1,F9,B1,B9,35,D0,2E,B1,78,C9,D2,DA,85,04,C7,AD), + wwH16(ED,4A,F8,49,4A,08,53,D8,95,E5,6F,37,73,3F,AE,8A), + wwH16(D5,28,C9,58,62,C2,A3,92,D5,00,C8,CD,6E,5A,17,DA), + wwH16(CC,79,00,E3,08,04,9B,26,E4,19,C3,EB,F1,0C,97,15), + wwH16(59,0E,B9,26,19,A0,A6,F2,C7,B3,8F,97,4B,F8,81,68), + wwH16(D2,2D,BF,1E,34,5E,C1,CD,81,2A,4D,5D,16,8C,A1,1C), + wwH16(71,18,DC,01,40,21,55,F2,87,97,95,82,0F,29,1E,01), + wwH16(83,F4,A0,6E,D8,52,A8,F8,08,ED,67,37,1B,97,85,24), + wwH16(59,8C,C0,D6,95,51,BD,37,AB,11,B3,D7,29,A2,3F,0A), + wwH16(15,03,97,A4,E9,A0,26,2B,1B,52,1E,10,37,C9,41,D6), + wwH16(DA,37,87,FD,35,95,D7,2C,B8,2E,DF,0A,2E,90,39,0B), + wwH16(EC,6D,0B,DD,21,50,97,70,20,AF,9D,AB,CD,F8,C3,BA), + wwH16(04,EA,0C,6E,D1,F2,E6,78,45,AF,49,36,DC,06,24,88), + wwH16(C9,B4,F1,5D,8B,88,24,06,9B,AE,F5,D5,19,80,DE,CE), + wwH16(4A,3D,F9,98,2E,04,CD,06,51,68,14,54,3D,83,63,CB), + wwH16(61,95,A5,AF,F9,5B,CD,35,B4,F3,9A,52,13,46,CA,07), + wwH16(0E,51,E7,2B,63,13,F3,3A,B8,50,18,0C,90,CD,D1,B8), + wwH16(F8,B5,71,2A,6A,54,52,1E,17,0F,A8,3F,78,CA,2E,22), + wwH16(27,EC,CF,A0,D7,29,F4,10,F8,69,71,7D,8F,A2,9F,C2), + wwH16(47,AC,3A,9C,AC,07,1F,51,74,BB,A9,4B,62,57,5E,EF), + wwH16(FE,66,BD,27,CA,D6,EB,8A,4F,E8,5B,1B,8A,D6,AB,3E), + wwH16(91,09,51,95,16,3E,27,52,A1,28,2C,B4,75,97,46,C9), + wwH16(73,C6,9C,D3,32,79,93,17,A4,14,11,14,8D,BA,5E,27), + wwH16(4B,ED,91,35,51,B3,15,8F,E4,70,CA,E7,46,B2,5A,3A), + wwH16(BA,48,E8,4D,C2,07,88,92,6F,2A,FF,33,70,65,5B,6C), + wwH16(70,FC,B8,27,AA,6E,01,EC,6A,75,BD,E7,FF,42,5D,BB), + wwH16(1A,B6,5A,F7,1C,E7,7F,69,3E,25,0D,33,0F,97,F6,BA), + wwH16(DA,11,5F,44,B8,2A,3F,23,FF,12,B9,A0,EF,D2,6F,83), + wwH16(9F,31,8D,77,31,6B,DB,9F,F2,22,F3,5B,D5,09,BC,E5), + wwH16(0C,5C,0C,19,70,5A,20,05,D4,69,9D,3D,0E,80,84,18), + wwH16(77,70,C1,A9,82,5F,A4,3D,06,65,55,FE,02,D6,A3,C1), + wwH16(44,3D,DA,B5,B9,4F,C6,C8,4F,34,7A,F3,30,DC,D1,64), + wwH16(AE,73,B3,08,B2,15,1A,7B,3E,EC,7B,85,E7,E2,7E,C3), + wwH16(EB,7F,E0,47,30,66,4F,C9,03,16,63,17,27,64,82,7B), + wwH16(4F,2B,19,C9,DB,2A,32,95,4F,EC,A1,A5,F7,1D,A7,39), + wwH16(77,65,39,58,CD,FC,44,CD,4C,38,16,DE,32,CB,3E,09), + wwH16(97,4F,18,9F,76,D5,9C,D2,AB,4F,46,0D,4E,DD,58,D1), + wwH16(2A,EC,C8,AB,22,A5,66,D1,F6,36,38,5C,45,DF,87,8E), + wwH16(4C,00,5E,D7,DA,14,93,E1,16,87,CF,3D,63,70,24,F6), + wwH16(82,49,D2,6F,A3,84,CF,A4,28,C6,0C,FC,46,24,84,96), + wwH16(FF,41,6D,71,B1,F4,75,55,F5,12,1C,71,80,EB,CD,F3), + wwH16(4E,BE,76,85,E6,32,AA,17,B2,E6,65,43,38,5C,93,AE), + wwH16(8A,EE,5F,4B,AD,02,C3,B9,B2,37,98,41,F4,9B,AD,57), + wwH16(D4,05,ED,0D,25,FB,5C,C3,2E,30,07,BD,06,DA,56,AD), + wwH16(EF,3D,CD,5E,61,1B,30,96,A3,0D,2B,8F,B2,7E,A1,A1), + wwH16(68,BB,E5,52,B5,CE,E0,07,C0,1C,B8,74,79,A8,72,27), + wwH16(9E,4D,14,21,EB,BF,8A,59,00,B4,14,87,05,8B,40,6E), + wwH16(3F,1A,74,57,75,11,C8,4B,93,1A,69,44,3E,75,15,52), + wwH16(1D,5B,65,C9,49,4A,12,40,94,A6,FC,C0,07,28,27,D9), + wwH16(E2,0C,5C,F3,E2,8A,5A,85,49,C9,8C,EC,BD,3C,48,C5), + wwH16(B5,29,A5,74,7D,21,CD,AD,76,09,3D,B4,9A,92,F3,1E), + wwH16(48,B0,45,20,1B,4D,48,15,17,6C,FC,C5,B8,C2,83,65), + wwH16(97,2C,F6,EA,DD,B0,47,20,64,38,C0,C8,4E,F6,8D,A8), + wwH16(0C,1E,C3,C8,9B,4B,4D,57,20,29,4D,D9,C6,E8,1D,A6), + wwH16(53,8C,00,08,E5,17,6A,25,50,27,97,A8,12,82,08,F6), + wwH16(3B,95,95,E4,19,2A,C1,10,CE,F6,C9,46,A1,46,8B,DB), + wwH16(C2,78,05,80,BB,80,BB,FF,52,7F,0F,B0,50,31,A3,D3), + wwH16(58,4C,A3,9F,13,F4,23,7D,34,0C,DA,4B,CF,E4,98,20), + wwH16(16,09,5B,1E,56,FB,B7,8F,9B,95,73,D0,65,EE,73,31), + wwH16(C8,D2,00,FA,01,71,7E,6D,5F,BB,E2,FF,49,99,20,B1), + wwH16(52,08,4D,99,3D,C9,E6,89,4B,23,26,22,FE,A0,F0,C3), + wwH16(C2,16,12,BB,CA,61,A1,69,76,14,6C,C6,97,11,7D,B9), + wwH16(D1,69,05,81,2E,DD,0C,96,21,37,B0,FD,1C,2E,EF,62), + wwH16(37,EF,26,22,B9,47,B2,5C,85,78,F0,4A,74,14,89,14), + wwH16(A1,86,07,8C,DC,2A,29,07,77,F1,27,AE,72,D4,54,C0), + wwH16(8D,FF,9C,26,E3,2B,4E,9B,89,6E,54,9D,CF,1C,F0,37), + wwH16(90,85,39,66,CA,02,6C,41,87,26,12,4B,4C,33,B3,39), + wwH16(3A,56,4E,16,8D,70,2D,3D,7B,C4,F6,6E,B2,3F,D9,86), + wwH16(B5,74,E3,F6,D1,85,E0,21,1F,E7,B7,21,74,D6,0C,53), + wwH16(88,CA,81,99,15,27,E0,1D,09,32,2A,F2,5A,D3,15,5F), + wwH16(BB,AC,71,A8,10,5B,23,C6,8D,4E,C4,3B,CF,31,62,4A), + wwH16(47,C5,C6,1E,6D,43,BA,BD,11,D7,46,41,D8,B7,2D,D7), + wwH16(92,0D,BE,AE,BC,06,5E,1A,0C,32,2E,3E,BB,E4,8C,AB), + wwH16(C3,01,13,F6,A9,0A,7C,AB,9B,64,DF,D5,9E,9F,8A,87), + wwH16(2E,E6,21,D1,87,F0,D1,E3,9B,F8,BE,D5,48,78,4F,6E), + wwH16(78,23,FB,5A,AF,36,5F,99,1C,BA,DD,83,3A,28,3F,03), + wwH16(AD,28,54,94,76,3C,B4,34,A6,29,0F,EE,A4,D7,9B,AB), + wwH16(90,C9,72,EA,D9,84,6A,0F,F9,89,61,07,A0,6F,F9,19), + wwH16(14,D9,E6,2B,6F,B4,09,0D,08,FA,59,3E,AA,37,70,B7), + wwH16(05,BD,6E,64,8B,C1,19,AA,E4,57,84,DC,71,E0,F5,17), + wwH16(78,FF,EE,B4,D3,8B,C5,11,48,DD,4C,0A,47,39,04,17), + wwH16(56,A0,8A,0F,88,F3,B6,10,EB,B9,DB,36,8A,83,9F,60), + wwH16(05,4A,96,D4,E3,B2,01,D9,A2,D5,53,60,DF,69,DF,CE), + wwH16(51,33,56,FF,51,10,84,8F,C8,56,40,35,DD,D8,D1,EC), + wwH16(AA,02,63,38,40,D1,9C,F9,4C,A0,18,F5,3B,E7,EB,59), + wwH16(E3,A4,E6,76,BF,64,69,AD,CB,B5,05,95,03,A5,51,2B), + wwH16(92,2B,B6,82,52,A2,EC,D9,31,18,10,DF,0D,8E,DB,D6), + wwH16(01,9C,B1,3E,9F,8A,BE,C8,A5,9C,8A,62,34,2A,16,B1), + wwH16(5A,7D,47,E7,DE,2E,1A,55,BC,94,20,27,4C,7F,95,A3), + wwH16(97,19,90,A3,AA,37,B7,65,CA,27,10,B6,3C,32,18,0C), + wwH16(A1,04,9E,1B,E9,9E,36,87,11,0A,55,1D,41,B0,0B,D6), + wwH16(87,DC,63,BE,9D,9E,28,02,EE,8B,AA,89,A4,7E,67,4F), + wwH16(ED,7E,82,40,ED,F8,BE,D0,82,EB,18,F1,72,6C,84,AC), + wwH16(E2,61,88,85,80,A5,CA,70,CE,77,B7,61,1F,42,04,32), + wwH16(91,87,C5,FF,CD,1D,48,8A,FA,8C,D9,E9,A6,C9,D6,D3), + wwH16(81,25,C1,EF,E3,78,D2,70,32,E0,12,75,27,ED,68,A9), + wwH16(FC,2A,01,25,03,0C,65,FB,17,8A,DC,C8,9F,63,3D,6E), + wwH16(0E,33,DE,E8,A2,1B,B9,C7,30,DC,0D,65,14,0C,25,7D), + wwH16(43,B8,42,30,71,45,35,D5,39,1F,F9,93,24,30,D2,E6), + wwH16(8E,A2,49,10,52,B8,C1,A5,C3,17,74,68,0D,33,C3,E8), + wwH16(78,F3,98,56,9B,25,15,3F,42,06,3C,8E,12,76,9F,52), + wwH16(8A,6D,23,BC,80,C6,5A,A9,E6,1E,0C,13,C7,73,8D,D0), + wwH16(3A,E0,96,CC,59,85,0A,CB,AF,1B,C4,63,43,11,C0,D8), + wwH16(E5,20,9F,57,18,80,DF,73,DA,ED,8B,DE,B9,1C,3B,8C), + wwH16(E4,47,1B,2A,09,77,63,39,2E,B5,B5,8A,07,C2,D6,54), + wwH16(5E,66,02,E6,AA,52,AB,1C,C2,8A,83,CC,0A,C2,80,A1), + wwH16(34,69,18,EA,F3,32,52,C7,68,A5,A7,AB,3B,B5,CF,71), + wwH16(EE,E1,36,9A,97,B3,92,14,9D,BA,DF,7B,50,D4,01,8A), + wwH16(44,47,94,56,9F,67,BA,21,63,2B,25,9D,D5,DF,0D,18), + wwH16(4D,3F,D4,D1,C7,FF,DA,A0,8B,51,F3,CB,51,C7,F1,1E), + wwH16(1A,84,30,B1,C2,5D,5C,3F,5C,28,C3,D4,52,8D,35,09), + wwH16(48,A7,FA,D4,6C,6B,FD,2D,E0,D8,7C,00,3F,4D,B3,FD), + wwH16(81,D9,F6,E4,25,77,46,4D,13,03,F9,B4,7C,06,6B,55), + wwH16(C7,F0,A7,B7,EC,16,A4,26,7D,80,54,EC,5B,84,6A,FE), + wwH16(CE,38,41,81,0F,88,74,F4,E2,DA,6B,82,D2,28,F9,C9), + wwH16(D9,D5,BC,5C,B3,36,A0,DA,21,7E,E4,A3,7E,78,A3,87), + wwH16(E0,FA,E2,FF,9E,E8,71,A7,0A,81,FA,D9,7F,67,60,85), + wwH16(6C,B5,5C,16,35,C0,A8,C4,65,59,E2,AD,0E,04,23,8F), + wwH16(19,35,5D,FB,D8,CC,97,8A,58,E0,36,88,7A,D5,FC,3B), + wwH16(54,50,FE,57,28,1C,AD,2A,0E,57,1B,69,DE,5F,5A,99), + wwH16(9D,26,65,C9,51,8F,93,9F,24,41,53,84,39,93,76,5C), + wwH16(CD,AF,74,63,51,7C,63,27,07,AF,FA,3E,7B,FB,9F,60), + wwH16(A9,59,FB,5A,F3,48,51,A1,A4,73,24,A1,CE,0F,1C,39), + wwH16(70,36,78,CF,A7,82,AB,3D,04,67,47,3A,86,06,1F,D8), + wwH16(24,65,30,42,30,97,76,19,8D,03,EB,C4,7B,75,B1,FE), + wwH16(A1,DD,75,90,2D,A2,F2,B6,07,47,17,87,9D,62,9C,05), + wwH16(2C,C8,B5,9B,E9,DD,CE,37,8D,A2,AF,1F,4E,52,8D,46), + wwH16(17,1B,59,96,A2,E7,AE,04,C1,41,BF,08,FF,49,00,56), + wwH16(2B,23,05,C2,81,5D,5B,DA,E4,1B,63,7B,B4,80,5D,4E), + wwH16(99,43,38,FD,1F,2C,DC,D7,33,89,E4,1E,CD,FF,C4,95), + wwH16(7F,D4,88,4D,7D,7D,D6,F2,A6,AA,E2,54,EC,8A,5A,F5), + wwH16(F7,EE,1E,5A,10,98,F7,03,1E,F8,30,24,D4,32,AE,AE), + wwH16(74,F1,91,0E,65,3A,26,CE,44,19,C9,CB,D9,7B,17,79), + wwH16(CF,82,5B,47,3F,56,1C,29,10,0E,6A,30,65,BC,C1,BE), + wwH16(0F,BB,7F,5F,76,7A,40,CF,3D,0F,26,1B,66,F0,38,C9), + wwH16(2B,51,8C,C7,32,7D,04,82,43,B5,99,F0,08,BB,57,2A), + wwH16(17,90,22,EE,3A,40,9C,EA,02,DE,85,C6,6D,18,E9,B9), + wwH16(34,B1,07,B3,C1,3B,30,B2,40,D3,EB,BD,39,22,EF,7A), + wwH16(F8,3C,C0,38,7E,06,60,7C,57,EB,F4,1B,9E,C9,AE,0A), + wwH16(DA,FB,20,66,30,91,84,9E,37,51,2C,42,8F,8E,18,64), + wwH16(A2,7C,51,EF,03,C9,86,3D,88,58,A1,A0,12,E0,9C,D3), + wwH16(97,87,5A,05,E9,49,08,05,4C,4C,D7,6E,11,12,7F,A8), + wwH16(17,A4,4F,B1,B0,27,B1,2D,4A,DA,AE,42,7B,70,D0,EB), + wwH16(A0,22,AB,D5,FA,11,CB,F0,57,97,C2,25,A0,7F,F0,06), + wwH16(0A,B8,C5,4E,FF,1B,7B,1A,6F,20,0D,AD,57,45,B3,A6), + wwH16(A1,9A,B5,07,A9,D3,3C,4A,91,C8,9E,91,E7,D1,8B,5D), + wwH16(59,A4,FA,F2,EE,49,48,4D,19,9A,10,75,34,1D,6A,25), + wwH16(C7,AB,90,75,DD,EC,3F,95,83,4D,F2,76,51,5A,63,EE), + wwH16(99,E3,12,F1,29,ED,2D,B5,A9,5E,B3,44,F9,76,2B,91), + wwH16(51,1D,48,9A,57,15,A8,D4,54,3F,64,CB,C9,21,DA,68), + wwH16(EF,5B,51,02,64,79,40,C2,BB,AD,09,74,42,4E,EE,09), + wwH16(A3,9F,E8,F7,45,08,33,ED,77,2F,E7,68,F6,A0,D6,15), + wwH16(C6,5E,91,C5,27,C9,C2,57,01,4E,55,1E,AD,15,DA,DD), + wwH16(F7,A1,E2,5D,65,D6,FD,6F,61,6C,0C,C9,2F,E6,F6,E2), + wwH16(B3,53,E3,DB,8B,66,CE,2D,A6,14,92,80,8F,F8,C8,1F), + wwH16(FF,FF,41,99,AC,13,A4,E0,9C,2B,FB,87,D1,6E,76,A3), + wwH16(D2,9D,3D,2C,AA,29,5C,87,AD,BA,E0,EC,FC,61,19,29), + wwH16(7D,F4,25,81,0A,91,91,B1,F2,25,4A,54,89,E5,5C,7A), + wwH16(D9,F1,F6,27,D9,32,C2,03,F3,F7,73,A6,E2,37,83,A5), + wwH16(EE,83,38,C4,CC,75,97,D1,63,24,22,4F,28,BA,EE,54), + wwH16(A1,C0,0F,A9,83,CA,E1,C5,E0,1E,96,78,5D,5B,EC,28), + wwH16(47,5F,49,BA,4A,A4,F6,98,C9,31,6D,21,CA,A5,0E,F4), + wwH16(35,BB,84,BB,08,E7,9C,97,1F,AF,96,DB,2C,02,01,3D), + wwH16(BD,EE,30,28,78,B7,B3,DE,28,FA,01,CD,DF,FC,2C,7D), + wwH16(BC,B7,8E,BD,68,C9,C9,F0,C7,EB,68,EB,1A,E3,6B,EC), + wwH16(8A,D7,AB,81,B4,D0,34,58,59,D5,8B,5B,AC,6B,5C,48), + wwH16(82,67,4B,54,0A,19,D9,14,50,59,26,66,46,50,83,AB), + wwH16(CC,9A,E4,F7,0C,DF,A3,88,DE,B4,1E,75,16,88,5A,59), + wwH16(FF,D5,74,A0,54,DB,3F,39,05,2A,32,F6,90,A9,30,03), + wwH16(61,A8,19,8A,F1,B9,84,2F,FA,1C,17,CC,C3,F5,BC,06), + wwH16(20,FC,59,0C,EF,0A,5C,E8,91,5E,E7,1F,E5,FA,8C,FD), + wwH16(6D,60,44,23,EC,AE,D5,EA,96,0A,DF,26,21,8D,1D,5A), + wwH16(FC,29,33,A6,4F,3B,5E,E7,3B,34,A8,DC,B7,2F,2F,8E), + wwH16(B2,1F,A9,8E,AD,BB,86,AB,C8,9F,0A,2B,67,4E,6E,8C), + wwH16(DA,61,A4,9C,40,13,49,00,C8,37,D2,59,14,26,9E,20), + wwH16(0C,89,5F,4A,D4,8D,02,B3,74,F8,CC,36,54,2C,F0,83), + wwH16(86,8D,EC,91,9D,C0,E2,52,5D,39,26,2F,77,7A,20,29), + wwH16(FC,16,7C,D2,FE,1F,8B,8D,9B,6F,20,60,12,D6,BE,B8), + wwH16(71,A3,4E,44,5E,5F,5B,FF,E3,E7,E2,80,91,3E,10,B3), + wwH16(42,2E,4B,E3,1A,48,34,C1,EA,82,F0,E5,3B,B8,79,4E), + wwH16(61,AB,18,19,69,5E,BA,C5,6A,A5,4A,12,9A,34,A1,3B), + wwH16(02,B8,C1,53,9A,1C,7F,52,C5,E5,F9,39,F2,FF,C4,62), + wwH16(82,AF,B3,1A,74,DA,AE,2C,2F,F7,A7,68,DF,0A,62,AE), + wwH16(B2,8B,43,4E,A3,F1,75,0B,B6,BD,4F,57,18,C8,30,38), + wwH16(42,26,86,39,41,56,BB,0D,A3,CE,E6,5D,EE,5D,A5,E3), + wwH16(01,8C,57,FB,33,9C,20,59,8F,7C,C0,53,BA,1C,4B,EB), + wwH16(C3,A1,C0,03,E2,AB,DA,4D,AB,FE,DC,24,1A,EB,85,92), + wwH16(92,39,A7,D5,F1,68,CD,DE,7A,12,9D,F8,70,BA,32,FA), + wwH16(D7,0A,44,3A,4A,35,75,31,19,74,28,D1,13,DA,8B,70), + wwH16(0E,48,02,C5,A6,01,5C,48,2E,08,52,A6,F8,8A,72,CF), + wwH16(2F,53,6B,95,43,67,1E,54,21,EA,9B,4E,AF,28,99,9E), + wwH16(28,96,01,89,FE,E3,D8,21,F6,3D,AC,B1,93,BF,7D,56), + wwH16(6B,05,40,FA,DE,7C,2E,26,20,24,DF,41,F7,E0,5B,58), + wwH16(A0,E1,92,DB,FD,8D,55,74,52,AD,69,79,4F,A3,51,88), + wwH16(E3,40,98,B0,4B,D0,52,03,8C,81,C5,F3,BB,5A,16,2A), + wwH16(6A,AC,2C,4F,A6,7A,19,1B,08,D8,7B,18,D0,DD,36,AC), + wwH16(30,51,1D,0A,6D,28,0B,0B,4F,72,D2,77,09,DB,9F,A5), + wwH16(46,4A,12,8D,E0,9C,E0,AF,9A,8A,3F,E6,20,15,82,01), + wwH16(BE,36,22,3C,D4,9A,CC,7C,DF,E3,CC,F0,D5,DC,BC,A6), + wwH16(92,63,AA,EF,23,08,CC,5B,15,FD,2F,2A,94,8A,16,52), + wwH16(79,BA,A1,1E,70,4E,8D,16,71,34,9A,DF,10,6B,E3,4E), + wwH16(5F,02,EF,5D,63,B6,BC,01,9C,BE,7B,39,4E,40,61,7F), + wwH16(5C,DA,14,8C,CA,61,D0,43,B6,86,C3,F9,EE,7C,5F,41), + wwH16(65,58,8E,77,CA,3D,3B,B8,F6,D7,F9,38,3A,38,69,21), + wwH16(ED,7C,52,6D,9B,C3,41,D3,4A,FC,65,D7,F4,1E,CB,C6), + wwH16(C7,85,B7,F9,E3,61,73,C4,0D,5F,35,19,B8,A9,51,31), + wwH16(38,3A,67,73,39,46,FE,52,27,AB,D0,E2,18,3B,78,EC), + wwH16(16,0B,04,A4,5E,93,3C,7A,2F,D9,A8,0C,24,DD,D5,2A), + wwH16(B4,89,F3,B7,81,C5,19,57,04,A4,7E,76,EE,31,8F,0A), + wwH16(E1,21,A2,04,93,9A,D9,B5,90,05,6A,87,32,27,22,9F), + wwH16(7E,7C,77,0F,F5,58,9D,13,69,26,54,17,16,D0,EE,31), + wwH16(97,BA,DF,08,72,46,45,FA,4A,9A,C8,31,26,D6,40,F9), + wwH16(47,D0,6D,76,57,CE,17,67,5E,1A,C9,52,CD,F4,CA,5B), + wwH16(EF,CB,C9,75,90,CF,6D,20,17,16,60,63,58,C8,3B,1D), + wwH16(4C,77,1A,20,77,90,13,58,CA,DE,A8,0E,96,D0,B2,55), + wwH16(8B,3C,5E,B8,AD,9A,8D,7D,31,04,B5,B2,0A,1C,8E,5F), + wwH16(A0,40,49,D3,61,CB,72,F8,EB,2B,D1,37,F4,9B,33,FF), + wwH16(27,A9,77,F7,15,08,00,ED,BF,85,90,30,46,6A,23,90), + wwH16(12,A7,8D,0F,74,30,72,91,85,86,40,12,F5,DB,8D,73), + wwH16(27,6B,6C,5B,8E,73,9F,EA,F8,33,21,EB,8D,87,61,42), + wwH16(E6,EC,65,77,92,11,68,C9,C5,47,BC,97,A4,E4,24,6A), + wwH16(99,11,73,65,CD,84,C6,2D,C1,D2,DF,5B,95,21,49,A1), + wwH16(FE,DE,2A,B5,B6,79,9B,FF,9F,52,E5,47,84,25,75,9C), + wwH16(F6,D4,F9,80,DD,37,1D,69,3F,BF,10,9C,0E,12,2E,8F), + wwH16(BA,C0,B1,86,0F,35,65,32,8E,85,03,E9,FB,B3,30,FD), + wwH16(1E,E8,F4,9D,CC,E9,6C,8A,1E,E5,E0,DE,80,3D,74,8A), + wwH16(6A,A7,A7,8F,AD,26,DE,A0,81,EA,3F,BD,82,1F,13,7A), + wwH16(DF,2F,A4,B3,8D,CA,EC,45,F6,50,F4,EA,FE,61,CA,CB), + wwH16(29,4B,5A,73,2D,B8,C4,6E,CF,F2,38,87,F1,44,56,CA), + wwH16(63,9B,40,A0,A1,6B,93,7F,69,35,E4,0A,75,76,E3,3A), + wwH16(F8,B7,15,91,08,13,4A,69,CB,CF,4C,EB,4E,82,04,2E), + wwH16(1B,48,ED,36,AC,46,AB,60,BD,16,7F,0D,05,1E,F8,93), + wwH16(5C,F2,AC,43,E9,41,E0,4F,FA,AB,B4,2F,EB,85,F7,AB), + wwH16(70,A6,CA,6B,38,1D,AD,69,7D,16,19,54,56,7F,7B,52), + wwH16(37,5E,01,98,C8,B9,B9,89,F4,DC,61,BC,CD,02,D4,C0), + wwH16(30,5A,B1,D9,6D,11,5D,8A,D8,53,FB,79,CF,30,6C,BD), + wwH16(F8,AB,17,74,5A,3A,13,9A,89,C5,C7,B7,F2,C1,E8,CA), + wwH16(14,60,D2,6F,F4,78,96,AC,06,4A,81,4E,54,7C,6D,75), + wwH16(CF,41,F6,E8,D8,97,BC,A5,FB,C4,E7,6F,B8,C1,9D,BB), + wwH16(E8,1A,66,D4,2B,DC,19,0C,95,E3,88,57,FC,E5,15,4A), + wwH16(C1,96,8C,76,39,97,4D,D1,66,EB,EF,F8,7D,FF,C9,C6), + wwH16(03,4C,C6,E6,4E,BA,E9,3F,0A,B3,53,C5,67,83,90,20), + wwH16(EA,00,CD,EF,2E,CC,E6,8F,98,02,B7,9C,C8,70,9D,4B), + wwH16(CF,AD,95,2A,DA,5C,48,22,AF,7E,FA,1E,FD,52,07,C4), + wwH16(41,11,06,FC,7E,D9,5E,B7,37,24,F5,97,04,21,4F,EA), + wwH16(0F,4B,2B,35,2F,25,1B,67,13,9E,E9,B8,8B,07,C1,36), + wwH16(4B,BD,99,89,EB,A5,8D,A2,41,EF,B7,A5,86,81,40,71), + wwH16(06,CF,A6,34,DD,C6,C4,AD,AD,76,D8,D2,FF,31,5B,5E), + wwH16(8D,09,8A,0D,5D,C7,6F,EC,7B,38,DC,EF,6D,30,22,C5), + wwH16(FA,5F,86,E7,B3,51,97,42,4E,5E,C7,07,EB,01,36,AC), + wwH16(A0,94,EA,70,AF,2A,B8,9C,AD,C5,32,A5,DC,63,41,D9), + wwH16(C8,ED,FE,DE,6A,B8,67,D2,0D,84,29,AF,02,A4,B6,63), + wwH16(29,F3,17,7D,04,91,65,81,81,E8,CB,86,D5,DE,43,70), + wwH16(DD,79,C5,EB,E3,B2,C3,A1,30,1B,9D,92,0E,7D,00,CF), + wwH16(7A,A2,FA,00,35,5B,CE,A2,37,3E,53,7A,6E,34,6D,75), + wwH16(32,0F,35,5A,CB,5D,16,24,9E,B5,27,40,20,6A,A9,77), + wwH16(6E,13,BA,30,56,5C,14,80,AF,FF,DB,1B,95,95,61,F2), + wwH16(67,16,8D,CC,69,C9,85,DD,3B,B6,E6,06,B9,86,D8,43), + wwH16(69,6F,41,54,22,79,65,90,78,51,52,4C,AE,E2,D3,86), + wwH16(EF,51,61,C7,B0,88,4A,54,1C,01,12,D2,0F,9C,02,CA), + wwH16(88,47,30,D9,58,96,6E,E9,2C,42,D4,BE,D9,52,7C,43), + wwH16(D2,0C,48,F6,38,89,F1,71,17,9A,C3,E9,7A,8B,16,2B), + wwH16(72,DB,FB,4D,23,EA,06,3A,66,E6,6F,D4,7A,FF,6A,94), + wwH16(DB,36,CA,08,A9,7C,74,40,51,1F,77,7A,36,7D,97,58), + wwH16(4B,6C,68,C7,09,34,46,CF,55,9B,69,D8,4F,6D,33,C7), + wwH16(AE,1A,BF,CD,E8,5C,C9,53,1C,9C,3F,A2,A6,6D,54,08), + wwH16(CC,EE,CE,71,BD,F7,14,4B,C0,3D,3F,EB,E0,3D,46,4E), + wwH16(D2,EB,16,CD,10,B5,7B,02,DF,51,87,CE,A9,02,35,EA), + wwH16(63,2C,47,CF,14,AE,9E,5F,9E,8A,12,83,BE,2A,8F,2A), + wwH16(C1,F1,9C,6B,55,30,97,91,93,DF,F0,2A,30,9A,7F,86), + wwH16(27,BC,8D,50,55,2C,02,7F,45,02,0C,77,B9,3F,3F,DB), + wwH16(3F,DB,A4,D7,E7,D4,7F,ED,BA,46,BE,C8,47,33,0D,63), + wwH16(39,D5,F1,2E,F9,35,AA,37,9E,D3,16,46,82,D3,DD,3A), + wwH16(C4,62,E6,5F,64,AD,6D,14,0E,34,70,A0,30,D8,F6,29), + wwH16(3A,7C,E9,23,77,17,BF,83,4F,DE,2B,33,9F,F1,28,4D), + wwH16(89,9F,1D,9E,07,A9,C0,15,C4,02,41,CC,F1,0E,25,69), + wwH16(D9,C1,E7,75,56,D6,FC,0A,D7,D1,79,B7,0E,47,BB,10), + wwH16(BD,E2,93,CA,17,6F,FC,08,B1,66,07,AA,6C,97,F8,B3), + wwH16(4A,68,FD,94,7D,1D,76,D7,A1,D0,BA,E4,06,7F,7C,6B), + wwH16(4A,77,09,B8,2F,6A,2D,28,4E,07,65,50,DE,82,84,AA), + wwH16(9E,E8,D1,6F,F6,CA,DF,47,AB,C0,38,2B,E1,7E,E0,5A), + wwH16(FB,D1,84,79,A1,07,CC,B2,CE,D3,0D,5E,B2,40,01,2D), + wwH16(94,87,5E,AD,61,AE,3E,24,78,54,58,E6,9D,96,CF,B8), + wwH16(70,08,86,42,62,26,81,3B,6E,BF,1B,38,95,49,C5,1A), + wwH16(82,46,98,64,AB,0F,F3,5C,1E,F8,6E,B4,2D,7E,70,E7), + wwH16(F1,FF,49,2F,4D,4E,B7,E8,B3,B9,C2,53,5F,9A,CE,63), + wwH16(6B,25,45,42,7B,1A,34,42,48,C9,1F,DB,54,2E,B3,F7), + wwH16(C6,C4,A6,8E,6C,8D,0C,9F,D7,C6,42,6A,EC,C0,56,BB), + wwH16(BC,9A,DA,FA,59,24,B3,0A,D8,B1,65,5F,40,25,A5,2B), + wwH16(20,DB,EE,6D,78,F8,D5,DC,94,DF,0B,E3,1A,FD,DA,26), + wwH16(E5,1B,B9,35,62,5D,FA,65,0C,45,D5,8E,DC,C6,7B,76), + wwH16(3B,E1,5D,7B,D8,C6,13,E3,5C,24,30,B8,89,89,6C,CE), + wwH16(46,0D,A3,4C,CF,A2,84,4B,BB,B4,6C,08,13,9C,8B,5E), + wwH16(CE,37,A3,FA,82,30,40,DB,7E,61,64,47,B7,23,E7,E9), + wwH16(62,18,93,CA,79,90,18,78,47,9B,B1,2B,79,79,B6,BB), + wwH16(26,E9,15,95,2F,DF,CE,D3,54,EC,EC,A5,58,75,84,67), + wwH16(38,88,92,84,5D,58,D8,44,7C,0B,81,F1,39,84,FE,03), + wwH16(9B,88,37,C9,C0,13,98,D5,D1,59,1C,E2,0F,13,FA,9B), + wwH16(EB,CF,B6,F5,EA,5E,5E,CC,79,D2,FA,EF,63,67,91,07), + wwH16(00,BB,09,1E,50,A6,91,F7,14,C0,6D,95,04,A9,87,9C), + wwH16(C1,11,2F,7B,FC,8A,32,8B,C2,9A,BC,AF,25,C0,2B,A6), + wwH16(F1,58,6B,22,13,5B,14,E2,39,E3,6A,B1,FA,9A,D7,36), + wwH16(87,DB,07,E7,A7,1D,16,1D,66,6D,60,0C,08,40,60,A0), + wwH16(03,A6,A2,81,94,46,39,4E,2D,BF,C7,7C,82,CB,17,56), + wwH16(BA,7B,E4,ED,D3,E9,A3,5C,ED,90,A0,05,87,40,23,96), + wwH16(09,49,14,34,70,2F,EE,5F,EA,3C,A3,02,29,B6,1C,28), + wwH16(C3,22,32,E6,48,83,0E,13,F0,A9,31,6D,7D,CF,D8,18), + wwH16(CD,47,4F,29,F6,89,A9,0F,B1,D8,46,42,0D,F9,6A,2C), + wwH16(56,2F,96,9D,7A,1F,2A,88,56,68,1E,8B,EA,F9,27,A6), + wwH16(5D,62,EC,B5,70,AC,B3,0B,73,75,C0,20,8F,3C,AB,20), + wwH16(E0,F2,B7,0F,67,FD,1D,13,29,B6,BC,BE,2E,5F,B1,FB), + wwH16(26,89,47,BD,C3,C4,2C,70,A5,F4,F2,CC,CA,95,11,37), + wwH16(24,36,33,E1,B0,52,56,6E,59,80,89,24,C8,3D,9E,F0), + wwH16(78,F4,44,EC,A5,EC,20,39,FD,EA,F1,FA,9A,17,C9,45), + wwH16(E5,D2,55,84,12,52,AD,7D,E8,32,AE,68,1D,CD,33,A2), + wwH16(55,E5,33,1B,B3,38,69,68,91,0D,CA,37,B2,9C,68,AA), + wwH16(62,D4,B3,F3,1B,87,61,B0,75,22,89,0F,BD,9C,93,A5), + wwH16(28,45,23,EA,FE,93,6D,56,4B,58,E4,19,75,17,2B,7B), + wwH16(C4,E9,20,15,A9,98,AD,5D,1A,B8,67,BE,59,76,26,38), + wwH16(BB,7E,90,DD,8F,B7,88,DA,31,E8,AF,E5,E8,9C,F0,26), + wwH16(16,3B,A5,A2,7E,20,4D,7F,E7,7A,D4,24,4F,01,8A,45), + wwH16(9B,6C,8F,44,7A,A7,51,D9,D8,93,FB,8A,34,44,72,AC), + wwH16(C0,ED,F3,B8,5E,8C,BB,1D,C2,71,93,61,80,DF,26,A2), + wwH16(E1,06,9C,DB,51,85,D0,C9,9F,EA,00,4A,07,82,88,86), + wwH16(9A,BA,95,A0,B5,A8,7F,1C,61,C4,44,8E,8A,52,A6,B5), + wwH16(EA,3F,28,1F,56,85,30,B7,F0,B1,A1,08,15,3A,EC,84), + wwH16(4A,1A,33,E9,D7,F8,6C,71,22,A0,D9,24,48,64,0E,9B), + wwH16(0A,D4,3D,E4,C2,41,B2,22,9E,AE,CE,BE,BA,E1,DE,82), + wwH16(91,F2,F7,3D,A8,4A,9E,05,26,02,51,F3,55,56,20,37), + wwH16(17,09,FF,93,27,B7,1F,22,27,B9,16,E3,71,FB,8F,24), + wwH16(36,0B,A5,14,DF,39,4D,8B,D8,8C,13,4F,3F,0F,7D,98), + wwH16(1C,21,B2,0B,F0,0A,10,D7,F5,4F,11,AD,61,82,AA,74), + wwH16(7D,5E,45,78,F5,BA,97,A5,DA,AA,6A,CF,33,8E,CF,91), + wwH16(F3,7D,24,71,ED,07,F2,5C,39,7B,0C,81,8E,69,1C,A9), + wwH16(AF,01,60,AD,77,68,EF,3F,27,4F,C6,91,0C,A3,1B,15), + wwH16(A6,B1,28,86,6F,AD,D8,22,0A,51,79,6A,9A,41,76,E1), + wwH16(76,BA,EA,90,1A,16,B0,55,FD,BD,F7,12,E1,2C,FF,7D), + wwH16(83,E8,14,69,78,94,E2,54,B3,CD,38,10,15,79,5E,9B), + wwH16(E6,26,A5,4A,24,55,3F,8A,AC,07,CF,D4,DF,54,A5,BA), + wwH16(F6,E7,3E,E0,30,0C,7E,63,EC,08,D7,9F,BF,4E,71,8B), + wwH16(47,03,FE,4C,19,FA,42,C5,9B,D5,F9,E3,AC,AD,C6,5A), + wwH16(16,EB,56,6E,9B,64,14,8E,8C,75,23,01,FD,28,96,66), + wwH16(B4,0C,5A,17,94,04,22,38,E8,0B,80,9B,C3,F9,9C,94), + wwH16(8B,FA,4F,A3,67,2C,DC,D7,46,29,E9,16,17,FB,6F,5C), + wwH16(1B,8B,11,78,7F,26,69,37,2E,98,F7,63,92,91,D0,C5), + wwH16(2A,FA,87,28,84,3A,A3,8A,7C,31,4A,AD,FC,3F,2F,17), + wwH16(85,E7,70,07,60,49,B4,3A,57,07,77,2B,5D,BA,FD,C8), + wwH16(61,1F,29,52,1A,88,3F,CC,73,CA,02,72,5B,24,31,55), + wwH16(4A,A1,21,13,53,1C,B5,75,BD,4D,A9,05,75,3A,A6,1C), + wwH16(2B,08,A5,BE,56,FF,72,03,18,7D,B5,65,53,F8,61,8E), + wwH16(6D,1F,6D,36,45,9E,F5,76,94,29,0A,C1,0B,1F,C9,9B), + wwH16(A8,2C,6D,30,BD,16,36,F1,1D,89,63,E2,33,DB,0B,05), + wwH16(93,69,4E,26,DA,48,10,F2,4B,B7,52,AA,84,E3,B9,E2), + wwH16(FA,0A,11,D2,A6,34,B8,3E,66,61,8B,A7,33,CB,6E,6A), + wwH16(5E,56,98,57,2C,76,2D,FC,0E,19,52,BC,D0,8E,21,91), + wwH16(F8,45,37,56,29,C5,DA,DA,5C,EA,31,8E,DE,98,7C,84), + wwH16(EB,C1,96,45,6C,97,BC,5B,7C,54,F3,59,10,41,90,86), + wwH16(C3,DA,A8,96,3C,5D,87,58,D8,04,45,C5,A2,2E,BB,99), + wwH16(5C,46,C0,98,90,2B,06,B1,97,38,C9,44,0A,76,00,DE), + wwH16(3C,49,D2,CA,13,A2,AB,50,F1,AD,3E,9B,EE,3C,9D,5B), + wwH16(98,BA,68,FD,19,99,6B,E0,0A,AE,C9,0C,DF,D5,6B,79), + wwH16(3A,C7,0C,3F,58,24,6B,2F,B3,4F,EB,67,A0,5D,11,1D), + wwH16(3F,D1,13,A4,E9,B9,55,D8,D8,20,33,F2,EC,BF,92,FA), + wwH16(16,FF,8A,CB,E9,77,5D,56,3C,DA,0B,8A,16,5E,B1,11), + wwH16(21,BE,E6,6C,2B,9B,EB,9F,AE,24,60,AD,32,E3,66,2F), + wwH16(0A,29,1A,BE,6E,B6,D0,9B,C2,60,1C,4A,5D,58,CA,40), + wwH16(C2,4E,96,46,F8,82,27,2B,AB,CD,04,3D,F0,28,72,FB), + wwH16(8F,D3,19,46,EB,FA,02,8C,23,58,36,14,F4,EB,33,B5), + wwH16(0E,F2,CF,4A,E5,97,22,17,9F,40,0F,B8,34,2C,B9,01), + wwH16(CF,FA,83,62,4C,0B,6E,9E,5B,48,C5,9C,1F,7E,9A,BF), + wwH16(3C,EB,D0,63,54,91,8E,F2,B9,6B,CF,E2,B5,F9,67,79), + wwH16(37,9B,4E,A9,5A,27,75,AF,84,F3,85,DA,70,63,7E,92), + wwH16(43,7C,76,10,D2,28,68,FC,F5,44,DA,33,87,CA,57,69), + wwH16(DF,B8,9D,31,79,AE,D6,15,72,D4,CD,C3,3A,9B,7A,3F), + wwH16(04,D8,F4,C7,A2,09,DC,F2,40,97,79,A1,83,56,12,4B), + wwH16(74,64,A5,AD,02,20,94,0A,1F,A6,5E,73,D1,36,E8,58), + wwH16(3C,0F,6E,32,28,33,5B,5D,66,D7,F1,7F,40,18,14,AF), + wwH16(5B,16,58,C1,D4,F6,5A,58,A5,DE,EC,3B,8C,A2,E2,55), + wwH16(3F,4A,48,C5,1B,AB,13,98,2C,86,63,DB,20,74,8F,32), + wwH16(8A,21,98,76,70,E5,3F,72,2B,6A,3D,00,86,1A,A7,69), + wwH16(5F,4D,D8,9C,36,80,3D,DD,1B,0B,9C,C4,B6,94,59,E9), + wwH16(0B,3B,3E,AF,74,1F,28,6B,31,69,BD,81,82,A8,34,CD), + wwH16(52,8C,18,9A,9B,56,C6,3F,40,05,91,33,14,86,83,39), + wwH16(73,59,7B,32,59,8D,1B,F7,20,14,BF,5A,D9,15,DF,9E), + wwH16(96,FF,7A,39,B3,9C,8E,A8,D9,3B,34,B9,E3,17,1F,F3), + wwH16(A5,BC,AC,12,3C,35,25,AD,A2,F4,A7,14,29,A9,8E,A1), + wwH16(CD,50,2D,40,D1,98,B2,50,E9,66,CA,C4,76,15,86,8E), + wwH16(FC,EA,DE,80,22,72,40,E2,C8,C8,D8,79,44,35,66,BD), + wwH16(05,F1,80,D5,38,69,22,00,85,68,4A,D2,67,34,4E,A2), + wwH16(C1,A3,F6,96,11,63,BC,01,6B,0E,03,E2,BF,ED,18,F7), + wwH16(D3,E4,AB,A9,57,34,1F,04,05,AF,BE,6B,B2,04,43,70), + wwH16(57,59,7D,4A,74,6C,6C,50,0C,DD,1E,65,AD,4C,DD,68), + wwH16(76,C2,30,81,5F,BC,E8,20,18,F3,8F,F9,D2,76,CF,03), + wwH16(7F,82,4F,70,A4,47,65,E2,40,B1,6D,4F,DC,26,93,20), + wwH16(2B,80,4E,FA,70,0B,38,CE,82,64,E4,20,55,51,E1,A2), + wwH16(61,1C,A1,9F,CE,49,F5,9F,3E,8D,B9,A5,48,72,A6,E8), + wwH16(34,39,72,3D,28,DD,17,2A,2C,34,2F,A2,85,66,84,C2), + wwH16(20,44,1B,18,AC,7D,EF,4A,96,82,91,EE,4E,40,DD,60), + wwH16(D4,A8,F9,79,78,22,C0,47,AD,97,27,6A,0B,27,8B,89), + wwH16(78,C3,B1,99,E5,01,FA,2B,F4,61,1C,E9,83,C7,1B,15), + wwH16(E5,D1,94,81,0D,CD,2F,50,5E,33,84,89,CB,CB,70,41), + wwH16(D5,17,62,D8,BC,B4,F7,10,F4,77,6A,71,17,06,CE,9A), + wwH16(1F,EC,EA,03,C2,89,1A,27,07,AF,38,FB,BC,90,BD,19), + wwH16(4D,E5,31,8F,C9,DC,8B,8F,7D,01,1D,6A,23,3F,36,D7), + wwH16(DF,98,0E,DA,97,34,75,D6,8D,95,3B,67,DE,C9,6B,FA), + wwH16(7C,5C,3C,02,60,69,CB,6F,DA,2E,BD,37,30,95,22,11), + wwH16(EC,F4,92,2B,1D,1B,62,72,0F,BC,DE,1A,57,BA,27,CC), + wwH16(E2,42,F8,58,BB,94,E5,2E,2D,A7,4E,D0,B8,77,12,96), + wwH16(4C,E2,BE,8E,B8,09,E6,33,37,20,CC,53,6E,D4,8B,40), + wwH16(2C,C6,29,D1,3D,B7,90,FA,C6,51,A3,47,3F,EC,8B,3A), + wwH16(E9,F7,FF,CF,84,7D,8D,53,CF,2E,24,5D,60,B6,B8,77), + wwH16(77,C7,AF,05,09,5D,EF,C4,D8,4B,D5,E1,69,5F,DE,4A), + wwH16(CE,2F,EF,B1,F1,7B,0F,D8,0F,0E,2A,AF,F1,17,CA,8C), + wwH16(C9,09,4F,1F,1E,23,11,F4,FD,F2,48,67,FC,50,0B,72), + wwH16(A5,D9,B9,40,F0,0A,F8,C5,B4,5E,64,91,DD,68,01,32), + wwH16(15,A2,5C,41,B6,0F,86,D2,75,A9,78,4E,CC,A0,B2,86), + wwH16(63,6A,03,45,11,16,6F,EB,A0,3B,52,9D,19,0C,7C,E7), + wwH16(9B,93,6A,B5,D3,57,6B,1A,3F,FE,3D,B9,D1,1C,BF,29), + wwH16(F0,9A,65,EF,29,7A,91,96,1D,73,FD,3B,10,96,BB,22), + wwH16(C9,9D,11,55,8D,60,84,1B,6B,9C,11,09,81,65,B1,36), + wwH16(31,A1,46,4A,BE,A3,6E,E9,B5,A9,BA,0C,EB,5C,C7,A5), + wwH16(20,01,9E,FA,2A,58,BF,BC,55,55,0B,A4,84,11,F4,94), +}}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 982990c2..8efcf231 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,9 @@ add_executable(testbee2 crypto/belt_bench.c crypto/belt_test.c crypto/bign_test.c + crypto/bign128_test.c + crypto/bign192_test.c + crypto/bign256_test.c crypto/bign96_test.c crypto/botp_test.c crypto/bpki_test.c diff --git a/test/crypto/bign128_test.c b/test/crypto/bign128_test.c new file mode 100644 index 00000000..2a969398 --- /dev/null +++ b/test/crypto/bign128_test.c @@ -0,0 +1,179 @@ +/* +******************************************************************************* +\file bign128_test.c +\brief Tests for Bign128 +\project bee2/test +\created 2026.03.06 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include +#include +#include +#include +#include +#include + +/* +******************************************************************************* +brngCTRX: Расширение brngCTR + +При инициализации можно передать дополнительное слово X. +******************************************************************************* +*/ +typedef struct +{ + const octet* X; /*< дополнительное слово */ + size_t count; /*< размер X в октетах */ + size_t offset; /*< текущее смещение в X */ + mem_align_t state_ex[]; /*< состояние brngCTR */ +} brng_ctrx_st; + +static size_t brngCTRX_keep() +{ + return sizeof(brng_ctrx_st) + brngCTR_keep(); +} + +static void brngCTRXStart(const octet theta[32], const octet iv[32], + const void* X, size_t count, void* state) +{ + brng_ctrx_st* s = (brng_ctrx_st*)state; + ASSERT(memIsValid(s, sizeof(brng_ctrx_st))); + ASSERT(count > 0); + ASSERT(memIsValid(s->state_ex, brngCTR_keep())); + brngCTRStart(s->state_ex, theta, iv); + s->X = (const octet*)X; + s->count = count; + s->offset = 0; +} + +static void brngCTRXStepR(void* buf, size_t count, void* stack) +{ + brng_ctrx_st* s = (brng_ctrx_st*)stack; + octet* buf1 = (octet*)buf; + size_t count1 = count; + ASSERT(memIsValid(s, sizeof(brng_ctrx_st))); + // заполнить buf + while (count1) + if (count1 < s->count - s->offset) + { + memCopy(buf1, s->X + s->offset, count1); + s->offset += count1; + count1 = 0; + } + else + { + memCopy(buf1, s->X + s->offset, s->count - s->offset); + buf1 += s->count - s->offset; + count1 -= s->count - s->offset; + s->offset = 0; + } + // сгенерировать + brngCTRStepR(buf, count, s->state_ex); +} + +/* +******************************************************************************* +Самотестирование +******************************************************************************* +*/ + +bool_t bign128Test() +{ + bign_params params[1]; + octet privkey[32]; + octet pubkey[64]; + octet hash[32]; + octet sig[48]; + octet token[80]; + mem_align_t state[1024 / sizeof(mem_align_t)]; + // подготовить память + if (sizeof(state) < brngCTRX_keep()) + return FALSE; + // инициализировать ГПСЧ + brngCTRXStart(beltH() + 128, beltH() + 128 + 64, beltH(), 8 * 32, state); + // загрузить параметры + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK) + return FALSE; + // тест Г.1 + if (bign128KeypairGen(privkey, pubkey, brngCTRXStepR, state) != ERR_OK || + !hexEq(privkey, + "1F66B5B84B7339674533F0329C74F218" + "34281FED0732429E0C79235FC273E269") || + !hexEq(pubkey, + "BD1A5650179D79E03FCEE49D4C2BD5DD" + "F54CE46D0CF11E4FF87BF7A890857FD0" + "7AC6A60361E8C8173491686D461B2826" + "190C2EDA5909054A9AB84D2AB9D99A90") || + bign128KeypairVal(privkey, pubkey) != ERR_OK || + bign128PubkeyVal(pubkey) != ERR_OK || + bign128PubkeyCalc(pubkey, privkey) != ERR_OK || + !hexEq(pubkey, + "BD1A5650179D79E03FCEE49D4C2BD5DD" + "F54CE46D0CF11E4FF87BF7A890857FD0" + "7AC6A60361E8C8173491686D461B2826" + "190C2EDA5909054A9AB84D2AB9D99A90")) + return FALSE; + memSetZero(pubkey, 32); + memCopy(pubkey + 32, params->yG, 32); + if (bign128DH(pubkey, privkey, pubkey, 64) != ERR_OK || + !hexEq(pubkey, + "BD1A5650179D79E03FCEE49D4C2BD5DD" + "F54CE46D0CF11E4FF87BF7A890857FD0" + "7AC6A60361E8C8173491686D461B2826" + "190C2EDA5909054A9AB84D2AB9D99A90")) + return FALSE; + // тест Г.2 + if (beltHash(hash, beltH(), 13) != ERR_OK || + bign128Sign(sig, hash, privkey, brngCTRXStepR, state) != ERR_OK || + !hexEq(sig, + "E36B7F0377AE4C524027C387FADF1B20" + "CE72F1530B71F2B5FD3A8C584FE2E1AE" + "D20082E30C8AF65011F4FB54649DFD3D") || + bign128Verify(hash, sig, pubkey) != ERR_OK) + return FALSE; + // детерминированная подпись + if (bign128Sign2(sig, hash, privkey, 0, 0) != ERR_OK || + bign128Verify(hash, sig, pubkey) != ERR_OK) + return FALSE; + // тест Г.4 + if (bign128KeyWrap(token, beltH(), 18, beltH() + 32, pubkey, brngCTRXStepR, + state) != ERR_OK || + !hexEq(token, + "9B4EA669DABDF100A7D4B6E6EB76EE52" + "51912531F426750AAC8A9DBB51C54D8D" + "EB9289B50A46952D0531861E45A8814B" + "008FDC65DE9FF1FA2A1F16B6A280E957" + "A814") || + bign128KeyUnwrap(token, token, 18 + 16 + 32, beltH() + 32, + privkey) != ERR_OK || + !memEq(token, beltH(), 18)) + return FALSE; + // тест Г.3 + if (beltHash(hash, beltH(), 48) != ERR_OK || + bign128Sign(sig, hash, privkey, brngCTRXStepR, state) != ERR_OK || + !hexEq(sig, + "47A63C8B9C936E94B5FAB3D9CBD78366" + "290F3210E163EEC8DB4E921E8479D413" + "8F112CC23E6DCE65EC5FF21DF4231C28") || + bign128Verify(hash, sig, pubkey) != ERR_OK) + return FALSE; + // тест Г.5 + if (bignKeyWrap(token, params, beltH(), 32, beltH() + 64, + pubkey, brngCTRXStepR, state) != ERR_OK || + !hexEq(token, + "4856093A0F6C13015FC8E15F1B23A762" + "02D2F4BA6E5EC52B78658477F6486DE6" + "87AFAEEA0EF7BC1326A7DCE7A10BA10E" + "3F91C0126044B22267BF30BD6F1DA29E" + "0647CF39C1D59A56BB0194E0F4F8A2BB") || + bign128KeyUnwrap(token, token, 32 + 16 + 32, beltH() + 64, + privkey) != ERR_OK || + !memEq(token, beltH(), 32)) + return FALSE; + // все нормально + return TRUE; +} diff --git a/test/crypto/bign192_test.c b/test/crypto/bign192_test.c new file mode 100644 index 00000000..fc81ebfe --- /dev/null +++ b/test/crypto/bign192_test.c @@ -0,0 +1,70 @@ +/* +******************************************************************************* +\file bign192_test.c +\brief Tests for Bign192 +\project bee2/test +\created 2026.03.06 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include +#include +#include +#include +#include +#include + +/* +******************************************************************************* +Самотестирование +******************************************************************************* +*/ + +bool_t bign192Test() +{ + bign_params params[1]; + octet privkey[48]; + octet pubkey[96]; + octet pubkey1[96]; + octet sig[72]; + octet token[19 + 16 + 48]; + mem_align_t state[64 / sizeof(mem_align_t)]; + // подготовить память + if (sizeof(state) < prngCOMBO_keep()) + return FALSE; + // инициализировать ГПСЧ + prngCOMBOStart(state, 19); + // загрузить параметры + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2") != ERR_OK) + return FALSE; + // упрвление ключами + if (bign192KeypairGen(privkey, pubkey, prngCOMBOStepR, state) != ERR_OK || + bign192KeypairVal(privkey, pubkey) != ERR_OK || + bign192PubkeyVal(pubkey) != ERR_OK || + bign192PubkeyCalc(pubkey1, privkey) != ERR_OK || + !memEq(pubkey, pubkey1, 96)) + return FALSE; + memSetZero(pubkey1, 48); + memCopy(pubkey1 + 48, params->yG, 48); + if (bign192DH(pubkey1, privkey, pubkey1, 96) != ERR_OK || + !memEq(pubkey, pubkey1, 96)) + return FALSE; + // ЭЦП + if (bign192Sign(sig, beltH(), privkey, prngCOMBOStepR, state) != ERR_OK || + bign192Verify(beltH(), sig, pubkey) != ERR_OK || + bign192Sign2(sig, beltH(), privkey, 0, 0) != ERR_OK || + bign192Verify(beltH(), sig, pubkey) != ERR_OK) + return FALSE; + // транспорт ключа + if (bign192KeyWrap(token, beltH(), 19, beltH() + 32, pubkey, prngCOMBOStepR, + state) != ERR_OK || + bign192KeyUnwrap(token, token, 19 + 16 + 48, beltH() + 32, + privkey) != ERR_OK || + !memEq(token, beltH(), 19)) + return FALSE; + // все нормально + return TRUE; +} diff --git a/test/crypto/bign256_test.c b/test/crypto/bign256_test.c new file mode 100644 index 00000000..164e3654 --- /dev/null +++ b/test/crypto/bign256_test.c @@ -0,0 +1,70 @@ +/* +******************************************************************************* +\file bign256_test.c +\brief Tests for Bign256 +\project bee2/test +\created 2026.03.06 +\version 2026.03.06 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include +#include +#include +#include +#include +#include + +/* +******************************************************************************* +Самотестирование +******************************************************************************* +*/ + +bool_t bign256Test() +{ + bign_params params[1]; + octet privkey[64]; + octet pubkey[128]; + octet pubkey1[128]; + octet sig[96]; + octet token[20 + 16 + 64]; + mem_align_t state[64 / sizeof(mem_align_t)]; + // подготовить память + if (sizeof(state) < prngCOMBO_keep()) + return FALSE; + // инициализировать ГПСЧ + prngCOMBOStart(state, 20); + // загрузить параметры + if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.3") != ERR_OK) + return FALSE; + // упрвление ключами + if (bign256KeypairGen(privkey, pubkey, prngCOMBOStepR, state) != ERR_OK || + bign256KeypairVal(privkey, pubkey) != ERR_OK || + bign256PubkeyVal(pubkey) != ERR_OK || + bign256PubkeyCalc(pubkey1, privkey) != ERR_OK || + !memEq(pubkey, pubkey1, 128)) + return FALSE; + memSetZero(pubkey1, 64); + memCopy(pubkey1 + 64, params->yG, 64); + if (bign256DH(pubkey1, privkey, pubkey1, 128) != ERR_OK || + !memEq(pubkey, pubkey1, 128)) + return FALSE; + // ЭЦП + if (bign256Sign(sig, beltH(), privkey, prngCOMBOStepR, state) != ERR_OK || + bign256Verify(beltH(), sig, pubkey) != ERR_OK || + bign256Sign2(sig, beltH(), privkey, 0, 0) != ERR_OK || + bign256Verify(beltH(), sig, pubkey) != ERR_OK) + return FALSE; + // транспорт ключа + if (bign256KeyWrap(token, beltH(), 20, beltH() + 32, pubkey, prngCOMBOStepR, + state) != ERR_OK || + bign256KeyUnwrap(token, token, 20 + 16 + 64, beltH() + 32, + privkey) != ERR_OK || + !memEq(token, beltH(), 20)) + return FALSE; + // все нормально + return TRUE; +} diff --git a/test/crypto/bign_test.c b/test/crypto/bign_test.c index 566d0a29..9259def1 100644 --- a/test/crypto/bign_test.c +++ b/test/crypto/bign_test.c @@ -4,7 +4,7 @@ \brief Tests for STB 34.101.45 (bign) \project bee2/test \created 2012.08.27 -\version 2026.03.05 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -143,7 +143,7 @@ static err_t bignPrePrintEc(const ec_o* ec, ec_pre_type type, size_t w) coord_size = O_OF_W(ec->f->n); if (w == 0 || (type == ec_pre_si && mb % w) || coord_size % 16) return ERR_BAD_INPUT; - h = (type != ec_pre_soa) ? h = (mb + w - 1) / w : 0; + h = (type != ec_pre_soa) ? (mb + w - 1) / w : 0; pre_count = SIZE_BIT_POS(w - 1); if (type == ec_pre_od) pre_count *= h; @@ -179,8 +179,8 @@ static err_t bignPrePrintEc(const ec_o* ec, ec_pre_type type, size_t w) ERR_CALL_HANDLE(code, blobClose(state)); // начало печати printf( - "ec_pre_t _pre = {\n" - "\t%s, %u, %u,\n", + "static const ec_pre_t _pre = {\n" + "\t%s, %u, %u, {\n", ecPreTypename(type), (unsigned)w, (unsigned)h); // цикл по точкам for (pos = 0; pos < pre_count; ++pos) @@ -193,7 +193,7 @@ static err_t bignPrePrintEc(const ec_o* ec, ec_pre_type type, size_t w) for (i = 0; i < 2 * coord_size; ++i) { if (i % 16 == 0) - printf("\twwFromH16(%02X,", ptr[i]); + printf("\twwH16(%02X,", ptr[i]); else if ((i + 1) % 16 == 0) printf("%02X),\n", ptr[i]); else @@ -201,7 +201,7 @@ static err_t bignPrePrintEc(const ec_o* ec, ec_pre_type type, size_t w) } } // завершение печати - printf("};\n"); + printf("}};\n"); // завершение blobClose(state); return code; @@ -399,8 +399,9 @@ bool_t bignTest() if (bignVerify(params, der, count, hash, sig, pubkey) != ERR_OK) return FALSE; // тест Г.5 - bignKeyWrap(token, params, beltH(), 32, beltH() + 64, - pubkey, brngCTRXStepR, state); + if (bignKeyWrap(token, params, beltH(), 32, beltH() + 64, + pubkey, brngCTRXStepR, state) != ERR_OK) + return FALSE; if (!hexEq(token, "4856093A0F6C13015FC8E15F1B23A762" "02D2F4BA6E5EC52B78658477F6486DE6" @@ -408,15 +409,15 @@ bool_t bignTest() "3F91C0126044B22267BF30BD6F1DA29E" "0647CF39C1D59A56BB0194E0F4F8A2BB")) return FALSE; - bignKeyUnwrap(token, params, token, 32 + 16 + 32, beltH() + 64, - privkey); + if (bignKeyUnwrap(token, params, token, 32 + 16 + 32, beltH() + 64, + privkey) != ERR_OK) + return FALSE; if (!memEq(token, beltH(), 32)) return FALSE; // тест Г.6 if (beltHash(hash, beltH(), 13) != ERR_OK) return FALSE; - if (bignSign2(sig, params, der, count, hash, privkey, 0, 0) - != ERR_OK) + if (bignSign2(sig, params, der, count, hash, privkey, 0, 0) != ERR_OK) return FALSE; wwFrom(q, params->q, 32); wwFrom(d, privkey, 32); @@ -502,8 +503,9 @@ bool_t bignTest() id_pubkey[0] ^= 1; // дополнительный тест для проверки bignIdSign2 if (bignIdSign2(id_sig, params, der, count, id_hash, hash, - id_privkey, 0, 0) != ERR_OK || - bignIdVerify(params, der, count, id_hash, hash, id_sig, + id_privkey, 0, 0) != ERR_OK) + return FALSE; + if (bignIdVerify(params, der, count, id_hash, hash, id_sig, id_pubkey, pubkey) != ERR_OK) return FALSE; id_sig[0] ^= 1; @@ -530,10 +532,12 @@ bool_t bignTest() return FALSE; // дополнительный тест: транспорт ключа из 16 октетов if (bignKeyWrap(token, params, beltH(), 16, beltH() + 64, - pubkey, brngCTRXStepR, state) != ERR_OK || - bignKeyUnwrap(token, params, token, 32 + 16 + 16, beltH() + 64, - privkey) != ERR_OK || - !memEq(token, beltH(), 16)) + pubkey, brngCTRXStepR, state) != ERR_OK) + return FALSE; + if (bignKeyUnwrap(token, params, token, 32 + 16 + 16, beltH() + 64, + privkey) != ERR_OK) + return FALSE; + if (!memEq(token, beltH(), 16)) return FALSE; // дополнительные тесты (vs OpenSSL) hexTo(key, "49FEFF8076CD9480"); diff --git a/test/crypto/dstu_test.c b/test/crypto/dstu_test.c index ab38d2a9..b49bfc75 100644 --- a/test/crypto/dstu_test.c +++ b/test/crypto/dstu_test.c @@ -4,7 +4,7 @@ \brief Tests for DSTU 4145-2002 (Ukraine) \project bee2/test \created 2012.03.01 -\version 2025.09.28 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -16,13 +16,6 @@ #include #include -/* -******************************************************************************* -COMBO-генератор -******************************************************************************* -*/ -#define combo_rng prngCOMBOStepR - /* ******************************************************************************* Самотестирование @@ -98,7 +91,7 @@ bool_t dstuTest() // максимальная длина ЭЦП ld = B_OF_O(2 * DSTU_SIZE); // сгенерировать hash - combo_rng(hash, 32, state); + prngCOMBOStepR(hash, 32, state); // проверить кривую dstu_163pb if (dstuPointGen(params->P, params, prngCOMBOStepR, state) != ERR_OK || dstuPointVal(params, params->P) != ERR_OK || diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index 22513c97..faf3f7bf 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -4,7 +4,7 @@ \brief Benchmarks for elliptic curves over prime fields \project bee2/test \created 2013.10.17 -\version 2026.03.05 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -37,8 +37,8 @@ static bool_t ecpBenchEc(const ec_o* ec) const size_t n = ec->f->n; const size_t m = wwWordSize(ec->order, n + 1); const size_t mo = wwOctetSize(ec->order, m); - const size_t min_w = 3; - const size_t max_w = 6; + const size_t min_w = 5; + const size_t max_w = 7; const size_t min_h = (B_OF_O(mo) + max_w - 1) / max_w; const size_t max_h = (B_OF_O(mo) + min_w - 1) / min_w; const size_t max_pre_count = min_h * SIZE_BIT_POS(max_w - 1); diff --git a/test/test.c b/test/test.c index 76bef5b5..1f1daccb 100644 --- a/test/test.c +++ b/test/test.c @@ -4,7 +4,7 @@ \brief Bee2 testing \project bee2/test \created 2014.04.02 -\version 2026.02.12 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -106,6 +106,9 @@ static int testMath() extern bool_t beltTest(); extern bool_t beltBench(); extern bool_t bignTest(); +extern bool_t bign128Test(); +extern bool_t bign192Test(); +extern bool_t bign256Test(); extern bool_t bign96Test(); extern bool_t brngTest(); extern bool_t belsTest(); @@ -130,6 +133,12 @@ static int testCrypto() code = beltBench(), ret |= !code; code = bashBench(), ret |= !code; printf("bignTest: %s\n", (code = bignTest()) ? "OK" : "Err"), ret |= !code; + printf("bign128Test: %s\n", (code = bign128Test()) ? "OK" : "Err"), + ret |= !code; + printf("bign192Test: %s\n", (code = bign192Test()) ? "OK" : "Err"), + ret |= !code; + printf("bign256Test: %s\n", (code = bign256Test()) ? "OK" : "Err"), + ret |= !code; printf("bign96Test: %s\n", (code = bign96Test()) ? "OK" : "Err"), ret |= !code; printf("botpTest: %s\n", (code = botpTest()) ? "OK" : "Err"), ret |= !code; diff --git a/win/bee2.def b/win/bee2.def index f0fdc510..e5e6bd0c 100644 --- a/win/bee2.def +++ b/win/bee2.def @@ -134,6 +134,39 @@ EXPORTS bignIdSign2 @319 bignIdVerify @320 + bign128KeypairGen @341 + bign128KeypairVal @342 + bign128PubkeyVal @343 + bign128PubkeyCalc @344 + bign128DH @345 + bign128Sign @346 + bign128Sign2 @347 + bign128Verify @348 + bign128KeyWrap @349 + bign128KeyUnwrap @350 + + bign192KeypairGen @361 + bign192KeypairVal @362 + bign192PubkeyVal @363 + bign192PubkeyCalc @364 + bign192DH @365 + bign192Sign @366 + bign192Sign2 @367 + bign192Verify @368 + bign192KeyWrap @369 + bign192KeyUnwrap @370 + + bign256KeypairGen @381 + bign256KeypairVal @382 + bign256PubkeyVal @383 + bign256PubkeyCalc @384 + bign256DH @385 + bign256Sign @386 + bign256Sign2 @387 + bign256Verify @388 + bign256KeyWrap @389 + bign256KeyUnwrap @390 + brngCTR_keep @401 brngCTRStart @402 brngCTRStepR @403 diff --git a/win/vs09/libbee2.vcproj b/win/vs09/libbee2.vcproj index f93ddfaa..67e58dcc 100644 --- a/win/vs09/libbee2.vcproj +++ b/win/vs09/libbee2.vcproj @@ -763,6 +763,18 @@ + + + + + + diff --git a/win/vs09/test.vcproj b/win/vs09/test.vcproj index a23386f5..0a867213 100644 --- a/win/vs09/test.vcproj +++ b/win/vs09/test.vcproj @@ -212,6 +212,18 @@ RelativePath="..\..\test\crypto\belt_test.c" > + + + + + + diff --git a/win/vs15/libbee2.vcxproj b/win/vs15/libbee2.vcxproj index 98694711..77067c96 100644 --- a/win/vs15/libbee2.vcxproj +++ b/win/vs15/libbee2.vcxproj @@ -404,6 +404,9 @@ + + + diff --git a/win/vs15/libbee2.vcxproj.filters b/win/vs15/libbee2.vcxproj.filters index 80de3a13..8c8247c0 100644 --- a/win/vs15/libbee2.vcxproj.filters +++ b/win/vs15/libbee2.vcxproj.filters @@ -345,6 +345,15 @@ Source Files\math\ecp + + Source Files\crypto\bign + + + Source Files\crypto\bign + + + Source Files\crypto\bign + diff --git a/win/vs15/test.vcxproj b/win/vs15/test.vcxproj index 6ef8b5a0..568bf3b8 100644 --- a/win/vs15/test.vcxproj +++ b/win/vs15/test.vcxproj @@ -380,6 +380,9 @@ + + + diff --git a/win/vs15/test.vcxproj.filters b/win/vs15/test.vcxproj.filters index 35ddcc39..ad0335d1 100644 --- a/win/vs15/test.vcxproj.filters +++ b/win/vs15/test.vcxproj.filters @@ -151,5 +151,14 @@ Source Files\math + + Source Files\crypto + + + Source Files\crypto + + + Source Files\crypto + \ No newline at end of file From f0c6fa7db181d7d31cc0e63b1a5b3c76dbfb5b9a Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 6 Mar 2026 19:16:13 +0300 Subject: [PATCH 56/77] Fix bignXXXEcUnlock() calls --- src/crypto/bign/bign128.c | 20 ++++++++++---------- src/crypto/bign/bign192.c | 20 ++++++++++---------- src/crypto/bign/bign256.c | 20 ++++++++++---------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/crypto/bign/bign128.c b/src/crypto/bign/bign128.c index 7b58434e..fe6aa813 100644 --- a/src/crypto/bign/bign128.c +++ b/src/crypto/bign/bign128.c @@ -105,7 +105,7 @@ err_t bign128KeypairGen(octet privkey[32], octet pubkey[64], gen_i rng, code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -116,7 +116,7 @@ err_t bign128KeypairVal(const octet privkey[32], const octet pubkey[64]) code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeypairValEc(ec, privkey, pubkey); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -127,7 +127,7 @@ err_t bign128PubkeyVal(const octet pubkey[64]) code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignPubkeyValEc(ec, pubkey); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -138,7 +138,7 @@ err_t bign128PubkeyCalc(octet pubkey[64], const octet privkey[32]) code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignPubkeyCalcEc(pubkey, ec, privkey); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -150,7 +150,7 @@ err_t bign128DH(octet key[], const octet privkey[32], const octet pubkey[64], code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignDHEc(key, ec, privkey, pubkey, key_len); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -173,7 +173,7 @@ err_t bign128Sign(octet sig[48], const octet hash[32], ERR_CALL_CHECK(code); code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, rng_state); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -186,7 +186,7 @@ err_t bign128Sign2(octet sig[48], const octet hash[32], ERR_CALL_CHECK(code); code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, t_len); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -198,7 +198,7 @@ err_t bign128Verify(const octet hash[32], const octet sig[48], code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -216,7 +216,7 @@ err_t bign128KeyWrap(octet token[], const octet key[], size_t len, code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } @@ -228,6 +228,6 @@ err_t bign128KeyUnwrap(octet key[], const octet token[], size_t len, code = bign128EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); - bign128EcUnlock(ec); + bign128EcUnlock(); return code; } diff --git a/src/crypto/bign/bign192.c b/src/crypto/bign/bign192.c index c8a64c8e..e2bbf1c7 100644 --- a/src/crypto/bign/bign192.c +++ b/src/crypto/bign/bign192.c @@ -105,7 +105,7 @@ err_t bign192KeypairGen(octet privkey[48], octet pubkey[96], gen_i rng, code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -116,7 +116,7 @@ err_t bign192KeypairVal(const octet privkey[48], const octet pubkey[96]) code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeypairValEc(ec, privkey, pubkey); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -127,7 +127,7 @@ err_t bign192PubkeyVal(const octet pubkey[96]) code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignPubkeyValEc(ec, pubkey); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -138,7 +138,7 @@ err_t bign192PubkeyCalc(octet pubkey[96], const octet privkey[48]) code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignPubkeyCalcEc(pubkey, ec, privkey); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -150,7 +150,7 @@ err_t bign192DH(octet key[], const octet privkey[48], const octet pubkey[96], code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignDHEc(key, ec, privkey, pubkey, key_len); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -173,7 +173,7 @@ err_t bign192Sign(octet sig[72], const octet hash[48], ERR_CALL_CHECK(code); code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, rng_state); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -186,7 +186,7 @@ err_t bign192Sign2(octet sig[72], const octet hash[48], ERR_CALL_CHECK(code); code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, t_len); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -198,7 +198,7 @@ err_t bign192Verify(const octet hash[48], const octet sig[72], code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -216,7 +216,7 @@ err_t bign192KeyWrap(octet token[], const octet key[], size_t len, code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } @@ -228,6 +228,6 @@ err_t bign192KeyUnwrap(octet key[], const octet token[], size_t len, code = bign192EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); - bign192EcUnlock(ec); + bign192EcUnlock(); return code; } diff --git a/src/crypto/bign/bign256.c b/src/crypto/bign/bign256.c index 795dcfd1..6dc5378e 100644 --- a/src/crypto/bign/bign256.c +++ b/src/crypto/bign/bign256.c @@ -105,7 +105,7 @@ err_t bign256KeypairGen(octet privkey[64], octet pubkey[128], gen_i rng, code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -116,7 +116,7 @@ err_t bign256KeypairVal(const octet privkey[64], const octet pubkey[128]) code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeypairValEc(ec, privkey, pubkey); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -127,7 +127,7 @@ err_t bign256PubkeyVal(const octet pubkey[128]) code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignPubkeyValEc(ec, pubkey); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -138,7 +138,7 @@ err_t bign256PubkeyCalc(octet pubkey[128], const octet privkey[64]) code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignPubkeyCalcEc(pubkey, ec, privkey); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -150,7 +150,7 @@ err_t bign256DH(octet key[], const octet privkey[64], const octet pubkey[128], code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignDHEc(key, ec, privkey, pubkey, key_len); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -173,7 +173,7 @@ err_t bign256Sign(octet sig[96], const octet hash[64], ERR_CALL_CHECK(code); code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, rng_state); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -186,7 +186,7 @@ err_t bign256Sign2(octet sig[96], const octet hash[64], ERR_CALL_CHECK(code); code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, t_len); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -198,7 +198,7 @@ err_t bign256Verify(const octet hash[64], const octet sig[96], code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -217,7 +217,7 @@ err_t bign256KeyWrap(octet token[], const octet key[], size_t len, code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } @@ -229,6 +229,6 @@ err_t bign256KeyUnwrap(octet key[], const octet token[], size_t len, code = bign256EcLock(&ec); ERR_CALL_CHECK(code); code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); - bign256EcUnlock(ec); + bign256EcUnlock(); return code; } From c10dc9b53ab33cd2ab98866c93a51b0bf634efd6 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 6 Mar 2026 19:21:52 +0300 Subject: [PATCH 57/77] Fix ecPreIsOperable() after Coverity warning --- src/math/ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/math/ec.c b/src/math/ec.c index 25469b29..ebd045dd 100644 --- a/src/math/ec.c +++ b/src/math/ec.c @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2014.03.04 -\version 2026.03.05 +\version 2026.03.06 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -151,7 +151,7 @@ bool_t ecGroupIsOperable(const ec_o* ec) bool_t ecPreIsOperable(const ec_pre_t* pre) { - return memIsValid(pre, sizeof(pre)) && + return memIsValid(pre, sizeof(ec_pre_t)) && (pre->type == ec_pre_so || pre->type == ec_pre_soa || pre->type == ec_pre_od || pre->type == ec_pre_si) && 0 < pre->w && pre->w < MIN2(B_PER_W, B_PER_S) && From ce5d51c3795ed6e3943698761b2ecc221ae3480f Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 6 Mar 2026 20:00:24 +0300 Subject: [PATCH 58/77] Fix unchecked return values in ec_test and ecp_bench --- test/math/ec_test.c | 6 ++++-- test/math/ecp_bench.c | 18 +++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/test/math/ec_test.c b/test/math/ec_test.c index 2abb882d..9ce2480f 100644 --- a/test/math/ec_test.c +++ b/test/math/ec_test.c @@ -208,8 +208,10 @@ bool_t ecTestEc(const ec_o* ec) ecDblA(pt1, ec->base, ec, stack); ec->dbladda(pt0, pt1, ec->base, ec, stack); // pt0 == 5 base? - d[0] = 5, ecMulA(pt3, ec->base, ec, d, 1, stack); - if (!ecToA(pt0, pt0, ec, stack) || !wwEq(pt0, pt3, 2 * n)) + d[0] = 5; + if (!ecMulA(pt3, ec->base, ec, d, 1, stack) || + !ecToA(pt0, pt0, ec, stack) || + !wwEq(pt0, pt3, 2 * n)) { blobClose(state); return FALSE; diff --git a/test/math/ecp_bench.c b/test/math/ecp_bench.c index faf3f7bf..90c3122b 100644 --- a/test/math/ecp_bench.c +++ b/test/math/ecp_bench.c @@ -85,7 +85,7 @@ static bool_t ecpBenchEc(const ec_o* ec) for (i = 0, ticks = tmTicks(); i < reps; ++i) { prngCOMBOStepR(d, mo, combo_state); - ecMulA(pt, ec->base, ec, d, m, stack); + (void)ecMulA(pt, ec->base, ec, d, m, stack); } ticks = tmTicks() - ticks; printf(" ecMulA: %u cycles/pt [%u pts/sec]\n", @@ -100,7 +100,7 @@ static bool_t ecpBenchEc(const ec_o* ec) { tm_ticks_t t = tmTicks(); ecpPreSOJ(pre, ec->base, w, ec, stack); - ecMulPreSO(pt, pre, ec, d, m, stack); + (void)ecMulPreSO(pt, pre, ec, d, m, stack); if ((t = tmTicks() - t) < ticks) ticks = t; } @@ -121,7 +121,7 @@ static bool_t ecpBenchEc(const ec_o* ec) { tm_ticks_t t = tmTicks(); ecpPreSOJ(pre, ec->base, w, ec, stack); - ecMulPreSO2(pt, pre, ec, d, m, stack); + (void)ecMulPreSO2(pt, pre, ec, d, m, stack); if ((t = tmTicks() - t) < ticks) ticks = t; } @@ -137,8 +137,8 @@ static bool_t ecpBenchEc(const ec_o* ec) for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { tm_ticks_t t = tmTicks(); - ecpPreSOA(pre, ec->base, w, ec, stack); - ecMulPreSOA(pt, pre, ec, d, m, stack); + (void)ecpPreSOA(pre, ec->base, w, ec, stack); + (void)ecMulPreSOA(pt, pre, ec, d, m, stack); if ((t = tmTicks() - t) < ticks) ticks = t; } @@ -151,12 +151,12 @@ static bool_t ecpBenchEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { size_t h = (B_OF_O(mo) + w - 1) / w; - ecPreOD(pre, ec->base, w, h, ec, stack); + (void)ecPreOD(pre, ec->base, w, h, ec, stack); wwSetW(d, m, 1); for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { tm_ticks_t t = tmTicks(); - ecMulPreOD(pt, pre, ec, d, m, stack); + (void)ecMulPreOD(pt, pre, ec, d, m, stack); if ((t = tmTicks() - t) < ticks) ticks = t; } @@ -169,12 +169,12 @@ static bool_t ecpBenchEc(const ec_o* ec) for (w = min_w; w <= max_w; ++w) { size_t h = (B_OF_O(mo) + w - 1) / w; - ecPreSI(pre, ec->base, w, h, ec, stack); + (void)ecPreSI(pre, ec->base, w, h, ec, stack); wwSetW(d, m, 1); for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) { tm_ticks_t t = tmTicks(); - ecMulPreSI(pt, pre, ec, d, m, stack); + (void)ecMulPreSI(pt, pre, ec, d, m, stack); if ((t = tmTicks() - t) < ticks) ticks = t; } From ddab213d5de917d3e4de0ef595003171ebe541a3 Mon Sep 17 00:00:00 2001 From: agievich Date: Sun, 8 Mar 2026 12:11:34 +0300 Subject: [PATCH 59/77] Simplify management of elliptic curves in crypto/bignXXX --- src/crypto/bign/bign128.c | 80 ++++++++++++++------------------------- src/crypto/bign/bign192.c | 80 ++++++++++++++------------------------- src/crypto/bign/bign256.c | 80 ++++++++++++++------------------------- 3 files changed, 87 insertions(+), 153 deletions(-) diff --git a/src/crypto/bign/bign128.c b/src/crypto/bign/bign128.c index fe6aa813..a2be996a 100644 --- a/src/crypto/bign/bign128.c +++ b/src/crypto/bign/bign128.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): Bign algorithms with bign-curve256v1 and belt-hash \project bee2 [cryptographic library] \created 2026.03.05 -\version 2026.03.06 +\version 2026.03.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -36,7 +36,7 @@ static mt_mtx_t _mtx[1]; /*< мьютекс */ static bool_t _inited; /*< мьютекс создан? */ static ec_o* _ec; /*< кривая */ -static void bign128EcDestroy() +static void bign128Destroy() { mtMtxLock(_mtx); bignEcClose(_ec), _ec = 0; @@ -44,14 +44,14 @@ static void bign128EcDestroy() mtMtxClose(_mtx); } -static void bign128EcInit() +static void bign128Init() { ASSERT(!_inited); // создать мьютекс if (!mtMtxCreate(_mtx)) return; // зарегистрировать деструктор - if (!utilOnExit(bign128EcDestroy)) + if (!utilOnExit(bign128Destroy)) { mtMtxClose(_mtx); return; @@ -59,11 +59,11 @@ static void bign128EcInit() _inited = TRUE; } -static err_t bign128EcLock(const ec_o** pec) +static err_t bign128Ec(const ec_o** pec) { ASSERT(memIsValid(pec, sizeof(const ec_o*))); // инициализировать однократно - if (!mtCallOnce(&_once, bign128EcInit) || !_inited) + if (!mtCallOnce(&_once, bign128Init) || !_inited) return ERR_FILE_CREATE; // заблокировать мьютекс mtMtxLock(_mtx); @@ -83,12 +83,8 @@ static err_t bign128EcLock(const ec_o** pec) } // возвратить кривую *pec = _ec; - return ERR_OK; -} - -static void bign128EcUnlock() -{ mtMtxUnlock(_mtx); + return ERR_OK; } /* @@ -102,44 +98,36 @@ err_t bign128KeypairGen(octet privkey[32], octet pubkey[64], gen_i rng, { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bign128EcUnlock(); - return code; + return bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); } err_t bign128KeypairVal(const octet privkey[32], const octet pubkey[64]) { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairValEc(ec, privkey, pubkey); - bign128EcUnlock(); - return code; + return bignKeypairValEc(ec, privkey, pubkey); } err_t bign128PubkeyVal(const octet pubkey[64]) { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyValEc(ec, pubkey); - bign128EcUnlock(); - return code; + return bignPubkeyValEc(ec, pubkey); } err_t bign128PubkeyCalc(octet pubkey[64], const octet privkey[32]) { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyCalcEc(pubkey, ec, privkey); - bign128EcUnlock(); - return code; + return bignPubkeyCalcEc(pubkey, ec, privkey); } err_t bign128DH(octet key[], const octet privkey[32], const octet pubkey[64], @@ -147,16 +135,16 @@ err_t bign128DH(octet key[], const octet privkey[32], const octet pubkey[64], { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignDHEc(key, ec, privkey, pubkey, key_len); - bign128EcUnlock(); - return code; + return bignDHEc(key, ec, privkey, pubkey, key_len); } /* ******************************************************************************* ЭЦП + +\remark DER(belt-hash) = 1.2.112.0.2.0.34.101.31.81 ******************************************************************************* */ @@ -169,12 +157,10 @@ err_t bign128Sign(octet sig[48], const octet hash[32], { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, + return bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, rng_state); - bign128EcUnlock(); - return code; } err_t bign128Sign2(octet sig[48], const octet hash[32], @@ -182,12 +168,10 @@ err_t bign128Sign2(octet sig[48], const octet hash[32], { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, + return bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, t_len); - bign128EcUnlock(); - return code; } err_t bign128Verify(const octet hash[32], const octet sig[48], @@ -195,11 +179,9 @@ err_t bign128Verify(const octet hash[32], const octet sig[48], { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); - bign128EcUnlock(); - return code; + return bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); } /* @@ -213,11 +195,9 @@ err_t bign128KeyWrap(octet token[], const octet key[], size_t len, { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); - bign128EcUnlock(); - return code; + return bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); } err_t bign128KeyUnwrap(octet key[], const octet token[], size_t len, @@ -225,9 +205,7 @@ err_t bign128KeyUnwrap(octet key[], const octet token[], size_t len, { err_t code; const ec_o* ec; - code = bign128EcLock(&ec); + code = bign128Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); - bign128EcUnlock(); - return code; + return bignKeyUnwrapEc(key, ec, token, len, header, privkey); } diff --git a/src/crypto/bign/bign192.c b/src/crypto/bign/bign192.c index e2bbf1c7..f2afe4f6 100644 --- a/src/crypto/bign/bign192.c +++ b/src/crypto/bign/bign192.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): Bign algorithms with bign-curve256v1 and bash384 \project bee2 [cryptographic library] \created 2026.03.06 -\version 2026.03.06 +\version 2026.03.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -36,7 +36,7 @@ static mt_mtx_t _mtx[1]; /*< мьютекс */ static bool_t _inited; /*< мьютекс создан? */ static ec_o* _ec; /*< кривая */ -static void bign192EcDestroy() +static void bign192Destroy() { mtMtxLock(_mtx); bignEcClose(_ec), _ec = 0; @@ -44,14 +44,14 @@ static void bign192EcDestroy() mtMtxClose(_mtx); } -static void bign192EcInit() +static void bign192Init() { ASSERT(!_inited); // создать мьютекс if (!mtMtxCreate(_mtx)) return; // зарегистрировать деструктор - if (!utilOnExit(bign192EcDestroy)) + if (!utilOnExit(bign192Destroy)) { mtMtxClose(_mtx); return; @@ -59,11 +59,11 @@ static void bign192EcInit() _inited = TRUE; } -static err_t bign192EcLock(const ec_o** pec) +static err_t bign192Ec(const ec_o** pec) { ASSERT(memIsValid(pec, sizeof(const ec_o*))); // инициализировать однократно - if (!mtCallOnce(&_once, bign192EcInit) || !_inited) + if (!mtCallOnce(&_once, bign192Init) || !_inited) return ERR_FILE_CREATE; // заблокировать мьютекс mtMtxLock(_mtx); @@ -83,12 +83,8 @@ static err_t bign192EcLock(const ec_o** pec) } // возвратить кривую *pec = _ec; - return ERR_OK; -} - -static void bign192EcUnlock() -{ mtMtxUnlock(_mtx); + return ERR_OK; } /* @@ -102,44 +98,36 @@ err_t bign192KeypairGen(octet privkey[48], octet pubkey[96], gen_i rng, { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bign192EcUnlock(); - return code; + return bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); } err_t bign192KeypairVal(const octet privkey[48], const octet pubkey[96]) { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairValEc(ec, privkey, pubkey); - bign192EcUnlock(); - return code; + return bignKeypairValEc(ec, privkey, pubkey); } err_t bign192PubkeyVal(const octet pubkey[96]) { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyValEc(ec, pubkey); - bign192EcUnlock(); - return code; + return bignPubkeyValEc(ec, pubkey); } err_t bign192PubkeyCalc(octet pubkey[96], const octet privkey[48]) { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyCalcEc(pubkey, ec, privkey); - bign192EcUnlock(); - return code; + return bignPubkeyCalcEc(pubkey, ec, privkey); } err_t bign192DH(octet key[], const octet privkey[48], const octet pubkey[96], @@ -147,16 +135,16 @@ err_t bign192DH(octet key[], const octet privkey[48], const octet pubkey[96], { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignDHEc(key, ec, privkey, pubkey, key_len); - bign192EcUnlock(); - return code; + return bignDHEc(key, ec, privkey, pubkey, key_len); } /* ******************************************************************************* ЭЦП + +\remark DER(bash384) = 1.2.112.0.2.0.34.101.77.12 ******************************************************************************* */ @@ -169,12 +157,10 @@ err_t bign192Sign(octet sig[72], const octet hash[48], { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, + return bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, rng_state); - bign192EcUnlock(); - return code; } err_t bign192Sign2(octet sig[72], const octet hash[48], @@ -182,12 +168,10 @@ err_t bign192Sign2(octet sig[72], const octet hash[48], { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, + return bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, t_len); - bign192EcUnlock(); - return code; } err_t bign192Verify(const octet hash[48], const octet sig[72], @@ -195,11 +179,9 @@ err_t bign192Verify(const octet hash[48], const octet sig[72], { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); - bign192EcUnlock(); - return code; + return bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); } /* @@ -213,11 +195,9 @@ err_t bign192KeyWrap(octet token[], const octet key[], size_t len, { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); - bign192EcUnlock(); - return code; + return bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); } err_t bign192KeyUnwrap(octet key[], const octet token[], size_t len, @@ -225,9 +205,7 @@ err_t bign192KeyUnwrap(octet key[], const octet token[], size_t len, { err_t code; const ec_o* ec; - code = bign192EcLock(&ec); + code = bign192Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); - bign192EcUnlock(); - return code; + return bignKeyUnwrapEc(key, ec, token, len, header, privkey); } diff --git a/src/crypto/bign/bign256.c b/src/crypto/bign/bign256.c index 6dc5378e..7c870c41 100644 --- a/src/crypto/bign/bign256.c +++ b/src/crypto/bign/bign256.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): Bign algorithms with bign-curve512v1 and bash512 \project bee2 [cryptographic library] \created 2026.03.06 -\version 2026.03.06 +\version 2026.03.08 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -36,7 +36,7 @@ static mt_mtx_t _mtx[1]; /*< мьютекс */ static bool_t _inited; /*< мьютекс создан? */ static ec_o* _ec; /*< кривая */ -static void bign256EcDestroy() +static void bign256Destroy() { mtMtxLock(_mtx); bignEcClose(_ec), _ec = 0; @@ -44,14 +44,14 @@ static void bign256EcDestroy() mtMtxClose(_mtx); } -static void bign256EcInit() +static void bign256Init() { ASSERT(!_inited); // создать мьютекс if (!mtMtxCreate(_mtx)) return; // зарегистрировать деструктор - if (!utilOnExit(bign256EcDestroy)) + if (!utilOnExit(bign256Destroy)) { mtMtxClose(_mtx); return; @@ -59,11 +59,11 @@ static void bign256EcInit() _inited = TRUE; } -static err_t bign256EcLock(const ec_o** pec) +static err_t bign256Ec(const ec_o** pec) { ASSERT(memIsValid(pec, sizeof(const ec_o*))); // инициализировать однократно - if (!mtCallOnce(&_once, bign256EcInit) || !_inited) + if (!mtCallOnce(&_once, bign256Init) || !_inited) return ERR_FILE_CREATE; // заблокировать мьютекс mtMtxLock(_mtx); @@ -83,12 +83,8 @@ static err_t bign256EcLock(const ec_o** pec) } // возвратить кривую *pec = _ec; - return ERR_OK; -} - -static void bign256EcUnlock() -{ mtMtxUnlock(_mtx); + return ERR_OK; } /* @@ -102,44 +98,36 @@ err_t bign256KeypairGen(octet privkey[64], octet pubkey[128], gen_i rng, { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bign256EcUnlock(); - return code; + return bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); } err_t bign256KeypairVal(const octet privkey[64], const octet pubkey[128]) { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairValEc(ec, privkey, pubkey); - bign256EcUnlock(); - return code; + return bignKeypairValEc(ec, privkey, pubkey); } err_t bign256PubkeyVal(const octet pubkey[128]) { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyValEc(ec, pubkey); - bign256EcUnlock(); - return code; + return bignPubkeyValEc(ec, pubkey); } err_t bign256PubkeyCalc(octet pubkey[128], const octet privkey[64]) { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyCalcEc(pubkey, ec, privkey); - bign256EcUnlock(); - return code; + return bignPubkeyCalcEc(pubkey, ec, privkey); } err_t bign256DH(octet key[], const octet privkey[64], const octet pubkey[128], @@ -147,16 +135,16 @@ err_t bign256DH(octet key[], const octet privkey[64], const octet pubkey[128], { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignDHEc(key, ec, privkey, pubkey, key_len); - bign256EcUnlock(); - return code; + return bignDHEc(key, ec, privkey, pubkey, key_len); } /* ******************************************************************************* ЭЦП + +\remark DER(bash512) = 1.2.112.0.2.0.34.101.77.13 ******************************************************************************* */ @@ -169,12 +157,10 @@ err_t bign256Sign(octet sig[96], const octet hash[64], { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, + return bignSignEc(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, rng, rng_state); - bign256EcUnlock(); - return code; } err_t bign256Sign2(octet sig[96], const octet hash[64], @@ -182,12 +168,10 @@ err_t bign256Sign2(octet sig[96], const octet hash[64], { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, + return bignSign2Ec(sig, ec, _oid_der, sizeof(_oid_der), hash, privkey, t, t_len); - bign256EcUnlock(); - return code; } err_t bign256Verify(const octet hash[64], const octet sig[96], @@ -195,11 +179,9 @@ err_t bign256Verify(const octet hash[64], const octet sig[96], { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); - bign256EcUnlock(); - return code; + return bignVerifyEc(ec, _oid_der, sizeof(_oid_der), hash, sig, pubkey); } /* @@ -214,11 +196,9 @@ err_t bign256KeyWrap(octet token[], const octet key[], size_t len, { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); - bign256EcUnlock(); - return code; + return bignKeyWrapEc(token, ec, key, len, header, pubkey, rng, rng_state); } err_t bign256KeyUnwrap(octet key[], const octet token[], size_t len, @@ -226,9 +206,7 @@ err_t bign256KeyUnwrap(octet key[], const octet token[], size_t len, { err_t code; const ec_o* ec; - code = bign256EcLock(&ec); + code = bign256Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeyUnwrapEc(key, ec, token, len, header, privkey); - bign256EcUnlock(); - return code; + return bignKeyUnwrapEc(key, ec, token, len, header, privkey); } From 8cab5c060dba6ff426a9f181c4b987d43fe0ebfc Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 9 Mar 2026 12:07:57 +0300 Subject: [PATCH 60/77] Add bign_bench --- test/CMakeLists.txt | 1 + test/crypto/bign_bench.c | 199 ++++++++++++++++++++++++++++++++++ test/test.c | 4 +- win/vs09/test.vcproj | 4 + win/vs15/test.vcxproj | 1 + win/vs15/test.vcxproj.filters | 3 + 6 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 test/crypto/bign_bench.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8efcf231..a68b5668 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(testbee2 crypto/bign192_test.c crypto/bign256_test.c crypto/bign96_test.c + crypto/bign_bench.c crypto/botp_test.c crypto/bpki_test.c crypto/brng_test.c diff --git a/test/crypto/bign_bench.c b/test/crypto/bign_bench.c new file mode 100644 index 00000000..62966128 --- /dev/null +++ b/test/crypto/bign_bench.c @@ -0,0 +1,199 @@ +/* +******************************************************************************* +\file bign_bench.c +\brief Benchmarks for STB 34.101.45 (bign) +\project bee2/test +\created 2026.03.09 +\version 2026.03.09 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* +******************************************************************************* +Оценка производительности алгоритмов Bign на определеннном уровне + +Оценивается производительность функций bignXXXKeypairGen(), bignXXXSign(), +bignXXXSign2(), bignXXXVerify(), bignXXXKeyWrap(), bignXXXKeyUnwrap(). + +\warning При оценке производительности не проверяются коды возврата функций. +Предполагается, что функции завершаются успешно. + +\warning Замеряется среднее время выполнения bignXXXVerify() и минимальное +время остальных функции. Предполагается, что последние функции регулярны. + +\remark Оценивается время транспорта ключа из 32 октетов. +******************************************************************************* +*/ + +typedef err_t (*bign_keypairgen_i)(octet privkey[], octet pubkey[], + gen_i rng, void* rng_state); +typedef err_t (*bign_sign_i)(octet sig[], const octet hash[], + const octet privkey[], gen_i rng, void* rng_state); +typedef err_t (*bign_sign2_i)(octet sig[], const octet hash[], + const octet privkey[], const void* t, size_t t_len); +typedef err_t (*bign_verify_i)(const octet hash[], const octet sig[], + const octet pubkey[]); +typedef err_t (*bign_keywrap_i)(octet token[], const octet key[], + size_t len, const octet header[16], const octet pubkey[], + gen_i rng, void* rng_state); +typedef err_t(*bign_keyunwrap_i)(octet key[], const octet token[], + size_t len, const octet header[16], const octet privkey[]); + +static bool_t bignBench_internal(size_t l) +{ + const size_t reps = 50; + bign_keypairgen_i keypairgen; + bign_sign_i sign; + bign_sign2_i sign2; + bign_verify_i verify; + bign_keywrap_i keywrap; + bign_keyunwrap_i keyunwrap; + mem_align_t combo_state[64 / sizeof(mem_align_t)]; + octet privkey[64]; + octet pubkey[128]; + octet hash[64]; + octet sig[96]; + octet key[32]; + octet token[32 + 64 + 16]; + tm_ticks_t ticks; + size_t i; + // pre + ASSERT(l == 128 || l == 192 || l == 256); + ASSERT(sizeof(combo_state) >= prngCOMBO_keep()); + // настройка + if (l == 128) + { + keypairgen = bign128KeypairGen; + sign = bign128Sign; + sign2 = bign128Sign2; + verify = bign128Verify; + keywrap = bign128KeyWrap; + keyunwrap = bign128KeyUnwrap; + } + else if (l == 192) + { + keypairgen = bign192KeypairGen; + sign = bign192Sign; + sign2 = bign192Sign2; + verify = bign192Verify; + keywrap = bign192KeyWrap; + keyunwrap = bign192KeyUnwrap; + } + else + { + keypairgen = bign256KeypairGen; + sign = bign256Sign; + sign2 = bign256Sign2; + verify = bign256Verify; + keywrap = bign256KeyWrap; + keyunwrap = bign256KeyUnwrap; + } + // создать генератор COMBO + prngCOMBOStart(combo_state, utilNonce32()); + // разогрев + if (keypairgen(privkey, pubkey, prngCOMBOStepR, combo_state) != ERR_OK) + return FALSE; + // подготовка + prngCOMBOStepR(hash, sizeof(hash), combo_state); + prngCOMBOStepR(key, sizeof(key), combo_state); + // keypairgen + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + (void)keypairgen(privkey, pubkey, prngCOMBOStepR, combo_state); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf("bign%uBench::KeypairGen: %u cycles/kp [%u kps/sec]\n", + (unsigned)l, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + // sign + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + (void)sign(sig, hash, privkey, prngCOMBOStepR, combo_state); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf("bign%uBench::Sign: %u cycles/sig [%u sigs/sec]\n", + (unsigned)l, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + // sign2 + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + (void)sign2(sig, hash, privkey, 0, 0); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf("bign%uBench::Sign2: %u cycles/sig [%u sigs/sec]\n", + (unsigned)l, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + // verify + for (i = 0, ticks = 0; i < reps; ++i) + { + tm_ticks_t t; + (void)sign(sig, hash, privkey, 0, 0); + t = tmTicks(); + (void)verify(hash, sig, pubkey); + ticks += tmTicks() - t; + } + printf("bign%uBench::Verify: %u cycles/sig [%u sigs/sec]\n", + (unsigned)l, + (unsigned)(ticks / reps), + (unsigned)tmSpeed(reps, ticks)); + // keywrap + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + (void)keywrap(token, key, sizeof(key), 0, pubkey, + prngCOMBOStepR, combo_state); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf("bign%uBench::KeyWrap: %u cycles/key [%u keys/sec]\n", + (unsigned)l, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + // keyunwrap + for (i = 0, ticks = (tm_ticks_t)-1; i < reps; ++i) + { + tm_ticks_t t = tmTicks(); + (void)keyunwrap(key, token, sizeof(key) + l / 4 + 16, 0, privkey); + if ((t = tmTicks() - t) < ticks) + ticks = t; + } + printf("bign%uBench::KeyUnwrap: %u cycles/key [%u keys/sec]\n", + (unsigned)l, + (unsigned)ticks, + (unsigned)tmSpeed(1, ticks)); + // завершение + return TRUE; +} + +/* +******************************************************************************* +Оценка производительности алгоритмов Bign +******************************************************************************* +*/ + +bool_t bignBench() +{ + return bignBench_internal(128) && + bignBench_internal(192) && + bignBench_internal(256); +} diff --git a/test/test.c b/test/test.c index 1f1daccb..407a9a30 100644 --- a/test/test.c +++ b/test/test.c @@ -4,7 +4,7 @@ \brief Bee2 testing \project bee2/test \created 2014.04.02 -\version 2026.03.06 +\version 2026.03.09 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -110,6 +110,7 @@ extern bool_t bign128Test(); extern bool_t bign192Test(); extern bool_t bign256Test(); extern bool_t bign96Test(); +extern bool_t bignBench(); extern bool_t brngTest(); extern bool_t belsTest(); extern bool_t bakeTest(); @@ -141,6 +142,7 @@ static int testCrypto() ret |= !code; printf("bign96Test: %s\n", (code = bign96Test()) ? "OK" : "Err"), ret |= !code; + code = bignBench(), ret |= !code; printf("botpTest: %s\n", (code = botpTest()) ? "OK" : "Err"), ret |= !code; printf("brngTest: %s\n", (code = brngTest()) ? "OK" : "Err"), ret |= !code; printf("belsTest: %s\n", (code = belsTest()) ? "OK" : "Err"), ret |= !code; diff --git a/win/vs09/test.vcproj b/win/vs09/test.vcproj index 0a867213..0317660f 100644 --- a/win/vs09/test.vcproj +++ b/win/vs09/test.vcproj @@ -228,6 +228,10 @@ RelativePath="..\..\test\crypto\bign96_test.c" > + + diff --git a/win/vs15/test.vcxproj b/win/vs15/test.vcxproj index 568bf3b8..9993c232 100644 --- a/win/vs15/test.vcxproj +++ b/win/vs15/test.vcxproj @@ -384,6 +384,7 @@ + diff --git a/win/vs15/test.vcxproj.filters b/win/vs15/test.vcxproj.filters index ad0335d1..fc0f7505 100644 --- a/win/vs15/test.vcxproj.filters +++ b/win/vs15/test.vcxproj.filters @@ -160,5 +160,8 @@ Source Files\crypto + + Source Files\crypto + \ No newline at end of file From b5ad20fa18e342e8b4286f121f85bc099770c6c6 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Mon, 9 Mar 2026 12:44:02 +0300 Subject: [PATCH 61/77] Clarify comments in ec.h --- include/bee2/math/ec.h | 57 +++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index fab78cfe..c1a274dd 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.03.06 +\version 2026.03.09 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -928,10 +928,17 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); выполняется без условных переходов; - ec->sgn != 0 и функция ec->sgn регулярна: знак точки устанавливается без условных переходов; - - справедливо, по крайней мере, одно из условий: + - справедливо одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadd != 0 и функция ec->finadd регулярна: сложение и удвоение - выполняются по одним и тем же формулам без условных переходов. + b) ec->finadd != 0 и в ec->finadd сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + с) ec->finadd == 0 и в ec->add сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + - справедливо одно из условий: + d) ec->finadd != 0 и в ec->finadd преобразование в аффинную точку + выполняется без условных переходов; + e) ec->finadd == 0 и в ec->toa преобразование в аффинную точку + выполняется без условных переходов. */ bool_t ecMulPreSO( word b[], /*!< [out] кратная точка */ @@ -1016,11 +1023,17 @@ size_t ecMulPreSO2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); переходов; - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - - справедливо, по крайней мере, одно из условий: + - справедливо одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и - удвоение выполняются по одним и тем же формулам без условных - переходов. + b) ec->finadda != 0 и в ec->finadda сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + с) ec->finadda == 0 и в ec->adda сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + - справедливо одно из условий: + d) ec->finadda != 0 и в ec->finadda преобразование в аффинную точку + выполняется без условных переходов; + e) ec->finadda == 0 и в ec->toa преобразование в аффинную точку + выполняется без условных переходов. */ bool_t ecMulPreSOA( word b[], /*!< [out] кратная точка */ @@ -1063,11 +1076,17 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); переходов; - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - - справедливо, по крайней мере, одно из условий: + - справедливо одно из условий: a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadda != 0 и функция ec->finadda регулярна: сложение и - удвоение выполняются по одним и тем же формулам без условных - переходов. + b) ec->finadda != 0 и в ec->finadda сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + с) ec->finadda == 0 и в ec->adda сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + - справедливо одно из условий: + d) ec->finadda != 0 и в ec->finadda преобразование в аффинную точку + выполняется без условных переходов; + e) ec->finadda == 0 и в ec->toa преобразование в аффинную точку + выполняется без условных переходов. */ bool_t ecMulPreOD( word b[], /*!< [out] кратная точка */ @@ -1112,9 +1131,17 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); переходов; - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - - ec->finadda != 0 и функция ec->finadda регулярна: сложение и удвоение - выполняются по одним и тем же формулам без условных переходов; - - pre->w * pre->h == wwBitSize(ec->order, m). + - pre->w * pre->h == wwBitSize(ec->order, m); + - справедливо одно из условий: + a) ec->finadda != 0 и в ec->finadda сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + b) ec->finadda == 0 и в ec->adda сложение и удвоение выполняются по + одним и тем же формулам без условных переходов; + - справедливо одно из условий: + c) ec->finadda != 0 и в ec->finadda преобразование в аффинную точку + выполняется без условных переходов; + d) ec->finadda == 0 и в ec->toa преобразование в аффинную точку + выполняется без условных переходов. */ bool_t ecMulPreSI( word b[], /*!< [out] кратная точка */ From c7cb624839ffbe6665217ac4b8ffb7d34ebc0238 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 10 Mar 2026 18:17:28 +0300 Subject: [PATCH 62/77] Clarify preconditions in ec.h --- include/bee2/math/ec.h | 65 ++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index c1a274dd..ef3f9dd1 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.03.09 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -928,17 +928,11 @@ size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); выполняется без условных переходов; - ec->sgn != 0 и функция ec->sgn регулярна: знак точки устанавливается без условных переходов; - - справедливо одно из условий: - a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadd != 0 и в ec->finadd сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - с) ec->finadd == 0 и в ec->add сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - - справедливо одно из условий: - d) ec->finadd != 0 и в ec->finadd преобразование в аффинную точку - выполняется без условных переходов; - e) ec->finadd == 0 и в ec->toa преобразование в аффинную точку - выполняется без условных переходов. + - функция (ec->finadd != 0) ? ec->finadd : ec->add регулярна: сложения + a + b, a + a, a + (-a) выполняются по одним и тем же формулам без + условных переходов; + - преобразование в аффинную точку в функции (ec->finadd != 0) ? + ec->finadd : ec->toa выполняется без условных переходов. */ bool_t ecMulPreSO( word b[], /*!< [out] кратная точка */ @@ -1023,17 +1017,11 @@ size_t ecMulPreSO2_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); переходов; - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - - справедливо одно из условий: - a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadda != 0 и в ec->finadda сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - с) ec->finadda == 0 и в ec->adda сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - - справедливо одно из условий: - d) ec->finadda != 0 и в ec->finadda преобразование в аффинную точку - выполняется без условных переходов; - e) ec->finadda == 0 и в ec->toa преобразование в аффинную точку - выполняется без условных переходов. + - функция (ec->finadda != 0) ? ec->finadda : ec->adda регулярна: сложения + a + b, a + a, a + (-a) выполняются по одним и тем же формулам без + условных переходов; + - преобразование в аффинную точку в функции (ec->finadda != 0) ? + ec->finadda : ec->toa выполняется без условных переходов. */ bool_t ecMulPreSOA( word b[], /*!< [out] кратная точка */ @@ -1076,17 +1064,11 @@ size_t ecMulPreSOA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); переходов; - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - - справедливо одно из условий: - a) ec->order mod 2^{pre->w} == ec->order mod 2^{pre->w + 1}; - b) ec->finadda != 0 и в ec->finadda сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - с) ec->finadda == 0 и в ec->adda сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - - справедливо одно из условий: - d) ec->finadda != 0 и в ec->finadda преобразование в аффинную точку - выполняется без условных переходов; - e) ec->finadda == 0 и в ec->toa преобразование в аффинную точку - выполняется без условных переходов. + - функция (ec->finadda != 0) ? ec->finadda : ec->adda регулярна: сложения + a + b, a + a, a + (-a) выполняются по одним и тем же формулам без + условных переходов; + - преобразование в аффинную точку в функции (ec->finadda != 0) ? + ec->finadda : ec->toa выполняется без условных переходов. */ bool_t ecMulPreOD( word b[], /*!< [out] кратная точка */ @@ -1132,16 +1114,11 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - pre->w * pre->h == wwBitSize(ec->order, m); - - справедливо одно из условий: - a) ec->finadda != 0 и в ec->finadda сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - b) ec->finadda == 0 и в ec->adda сложение и удвоение выполняются по - одним и тем же формулам без условных переходов; - - справедливо одно из условий: - c) ec->finadda != 0 и в ec->finadda преобразование в аффинную точку - выполняется без условных переходов; - d) ec->finadda == 0 и в ec->toa преобразование в аффинную точку - выполняется без условных переходов. + - функция (ec->finadda != 0) ? ec->finadda : ec->adda регулярна: сложения + a + b, a + a, a + (-a) выполняются по одним и тем же формулам без + условных переходов; + - преобразование в аффинную точку в функции (ec->finadda != 0) ? + ec->finadda : ec->toa выполняется без условных переходов. */ bool_t ecMulPreSI( word b[], /*!< [out] кратная точка */ From 113ec7eb23861de4a9e28bfccac68accb5170a9a Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 10 Mar 2026 21:29:32 +0300 Subject: [PATCH 63/77] Use bignXXX everywhere --- cmd/core/cmd_sig.c | 191 +- cmd/core/cmd_st.c | 19 +- cmd/kg/kg.c | 93 +- include/bee2/crypto/bign96.h | 92 +- src/CMakeLists.txt | 2 +- src/crypto/{ => bign}/bign96.c | 287 +- src/crypto/bign/pre/bign96_pre_od7.c | 5392 ++++++++++++++++++++++++++ src/crypto/bpki.c | 32 +- src/crypto/btok/btok_bauth.c | 18 +- src/crypto/btok/btok_cvc.c | 153 +- test/crypto/bign96_test.c | 39 +- test/crypto/bign_test.c | 4 +- test/crypto/btok_test.c | 19 +- win/bee2.def | 16 +- win/vs09/libbee2.vcproj | 8 +- win/vs15/libbee2.vcxproj | 2 +- win/vs15/libbee2.vcxproj.filters | 6 +- 17 files changed, 5805 insertions(+), 568 deletions(-) rename src/crypto/{ => bign}/bign96.c (74%) create mode 100644 src/crypto/bign/pre/bign96_pre_od7.c diff --git a/cmd/core/cmd_sig.c b/cmd/core/cmd_sig.c index e587265d..e4c7b324 100644 --- a/cmd/core/cmd_sig.c +++ b/cmd/core/cmd_sig.c @@ -4,7 +4,7 @@ \brief Command-line interface to Bee2: signing files \project bee2/cmd \created 2022.08.20 -\version 2025.10.10 +\version 2026.10.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -21,7 +21,9 @@ #include #include #include -#include +#include +#include +#include #include #include #include "bee2/cmd.h" @@ -298,28 +300,6 @@ static err_t cmdSigHash(octet hash[], size_t hash_len, const char* name, return code; } -/* -******************************************************************************* -Долговременные параметры -******************************************************************************* -*/ - -static err_t cmdSigParamsStd(bign_params* params, size_t privkey_len) -{ - switch (privkey_len) - { - case 24: - return bign96ParamsStd(params, "1.2.112.0.2.0.34.101.45.3.0"); - case 32: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1"); - case 48: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2"); - case 64: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.3"); - } - return ERR_BAD_INPUT; -} - /* ******************************************************************************* Выработка подписи @@ -330,11 +310,8 @@ err_t cmdSigSign(const char* sig_name, const char* name, const char* certs, const octet date[6], const octet privkey[], size_t privkey_len) { err_t code; - size_t oid_len = 16; void* state; cmd_sig_t* sig; /* [1] */ - bign_params* params; /* [1] */ - octet* oid_der; /* [oid_len] */ octet* hash; /* [privkey_len] */ octet* t; /* [privkey_len] */ size_t t_len; @@ -350,12 +327,10 @@ err_t cmdSigSign(const char* sig_name, const char* name, const char* certs, // выделить и разметить память code = cmdBlobCreate2(state, sizeof(cmd_sig_t), - sizeof(bign_params), - oid_len, privkey_len, privkey_len, SIZE_MAX, - &sig, ¶ms, &oid_der, &hash, &t); + &sig, &hash, &t); ERR_CALL_CHECK(code); // зафиксировать дату memCopy(sig->date, date, 6); @@ -385,44 +360,34 @@ err_t cmdSigSign(const char* sig_name, const char* name, const char* certs, privkey_len); ERR_CALL_HANDLE(code, cmdBlobClose(state)); } - // загрузить долговременные параметры - code = cmdSigParamsStd(params, privkey_len); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); // хэшировать code = cmdSigHash(hash, privkey_len, name, 0, sig->certs, sig->certs_len, sig->date); ERR_CALL_HANDLE(code, cmdBlobClose(state)); - if (privkey_len <= 32) - { - code = bignOidToDER(oid_der, &oid_len, "1.2.112.0.2.0.34.101.31.81"); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - ASSERT(oid_len == 11); - } - else - { - code = bignOidToDER(oid_der, &oid_len, privkey_len == 48 ? - "1.2.112.0.2.0.34.101.77.12" : "1.2.112.0.2.0.34.101.77.13"); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - ASSERT(oid_len == 11); - } // получить случайные числа if (rngIsValid()) rngStepR(t, t_len = privkey_len, 0); else t_len = 0; // подписать - if (privkey_len == 24) + switch (privkey_len) { - code = bign96Sign2(sig->sig, params, oid_der, oid_len, hash, privkey, - t, t_len); + case 32: + code = bign128Sign2(sig->sig, hash, privkey, t, t_len); + sig->sig_len = 48; + break; + case 48: + code = bign192Sign2(sig->sig, hash, privkey, t, t_len); + sig->sig_len = 72; + break; + case 64: + code = bign256Sign2(sig->sig, hash, privkey, t, t_len); + sig->sig_len = 96; + break; + case 24: + code = bign96Sign2(sig->sig, hash, privkey, t, t_len); sig->sig_len = 34; } - else - { - code = bignSign2(sig->sig, params, oid_der, oid_len, hash, privkey, - t, t_len); - sig->sig_len = privkey_len / 2 * 3; - } ERR_CALL_HANDLE(code, cmdBlobClose(state)); // сохранить подпись if (cmdFileAreSame(name, sig_name)) @@ -444,12 +409,9 @@ err_t cmdSigVerify(const char* name, const char* sig_name, const octet pubkey[], size_t pubkey_len) { err_t code; - size_t oid_len = 16; void* state; cmd_sig_t* sig; /* [1] */ btok_cvc_t* cvc; /* [1] */ - bign_params* params; /* [1] */ - octet* oid_der; /* [oid_len] */ octet* hash; /* [pubkey_len / 2] */ size_t drop; // входной контроль @@ -462,11 +424,9 @@ err_t cmdSigVerify(const char* name, const char* sig_name, code = cmdBlobCreate2(state, sizeof(cmd_sig_t), sizeof(btok_cvc_t), - sizeof(bign_params), - oid_len, pubkey_len / 2, SIZE_MAX, - &sig, &cvc, ¶ms, &oid_der, &hash); + &sig, &cvc, &hash); ERR_CALL_CHECK(code); // читать подпись code = cmdSigRead(sig, &drop, sig_name); @@ -497,37 +457,41 @@ err_t cmdSigVerify(const char* name, const char* sig_name, code = ERR_BAD_PUBKEY; ERR_CALL_HANDLE(code, cmdBlobClose(state)); } - // загрузить долговременные параметры - code = cmdSigParamsStd(params, pubkey_len / 2); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); // хэшировать code = cmdSigHash(hash, pubkey_len / 2, name, drop, sig->certs, sig->certs_len, sig->date); ERR_CALL_HANDLE(code, cmdBlobClose(state)); - if (pubkey_len <= 64) - { - code = bignOidToDER(oid_der, &oid_len, "1.2.112.0.2.0.34.101.31.81"); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - ASSERT(oid_len == 11); - } - else + // проверить открытый ключ + switch (pubkey_len) { - code = bignOidToDER(oid_der, &oid_len, pubkey_len == 96 ? - "1.2.112.0.2.0.34.101.77.12" : "1.2.112.0.2.0.34.101.77.13"); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - ASSERT(oid_len == 11); + case 64: + code = bign128PubkeyVal(pubkey); + break; + case 96: + code = bign192PubkeyVal(pubkey); + break; + case 128: + code = bign256PubkeyVal(pubkey); + break; + case 48: + code = bign96PubkeyVal(pubkey); } - // проверить открытый ключ - if (pubkey_len == 48) - code = bign96PubkeyVal(params, pubkey); - else - code = bignPubkeyVal(params, pubkey); ERR_CALL_HANDLE(code, cmdBlobClose(state)); // проверить подпись - if (pubkey_len == 48) - code = bign96Verify(params, oid_der, oid_len, hash, sig->sig, pubkey); - else - code = bignVerify(params, oid_der, oid_len, hash, sig->sig, pubkey); + switch (pubkey_len) + { + case 64: + code = bign128Verify(hash, sig->sig, pubkey); + break; + case 96: + code = bign192Verify(hash, sig->sig, pubkey); + break; + case 128: + code = bign256Verify(hash, sig->sig, pubkey); + break; + case 48: + code = bign96Verify(hash, sig->sig, pubkey); + } // завершить cmdBlobClose(state); return code; @@ -537,12 +501,9 @@ err_t cmdSigVerify2(const char* name, const char* sig_name, const octet anchor[], size_t anchor_len) { err_t code; - size_t oid_len = 16; void* state; cmd_sig_t* sig; /* [1] */ btok_cvc_t* cvc; /* [1] */ - bign_params* params; /* [1] */ - octet* oid_der; /* [oid_len] */ octet* hash; /* [64] */ size_t drop; size_t offset; @@ -555,11 +516,9 @@ err_t cmdSigVerify2(const char* name, const char* sig_name, code = cmdBlobCreate2(state, sizeof(cmd_sig_t), sizeof(btok_cvc_t), - sizeof(bign_params), - oid_len, (size_t)64, SIZE_MAX, - &sig, &cvc, ¶ms, &oid_der, &hash); + &sig, &cvc, &hash); ERR_CALL_CHECK(code); // читать подпись code = cmdSigRead(sig, &drop, sig_name); @@ -583,39 +542,41 @@ err_t cmdSigVerify2(const char* name, const char* sig_name, // разобрать последний сертификат code = btokCVCUnwrap(cvc, sig->certs + offset, cert_len, 0, 0); ERR_CALL_HANDLE(code, cmdBlobClose(state)); - // загрузить долговременные параметры - code = cmdSigParamsStd(params, cvc->pubkey_len / 2); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); // хэшировать code = cmdSigHash(hash, cvc->pubkey_len / 2, name, drop, sig->certs, sig->certs_len, sig->date); ERR_CALL_HANDLE(code, cmdBlobClose(state)); - if (cvc->pubkey_len <= 64) - { - code = bignOidToDER(oid_der, &oid_len, "1.2.112.0.2.0.34.101.31.81"); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - ASSERT(oid_len == 11); - } - else + // проверить открытый ключ + switch (cvc->pubkey_len) { - code = bignOidToDER(oid_der, &oid_len, cvc->pubkey_len == 96 ? - "1.2.112.0.2.0.34.101.77.12" : "1.2.112.0.2.0.34.101.77.13"); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - ASSERT(oid_len == 11); + case 64: + code = bign128PubkeyVal(cvc->pubkey); + break; + case 96: + code = bign192PubkeyVal(cvc->pubkey); + break; + case 128: + code = bign256PubkeyVal(cvc->pubkey); + break; + case 48: + code = bign96PubkeyVal(cvc->pubkey); } - // проверить открытый ключ - if (cvc->pubkey_len == 48) - code = bign96PubkeyVal(params, cvc->pubkey); - else - code = bignPubkeyVal(params, cvc->pubkey); ERR_CALL_HANDLE(code, cmdBlobClose(state)); // проверить подпись - if (cvc->pubkey_len == 48) - code = bign96Verify(params, oid_der, oid_len, hash, sig->sig, - cvc->pubkey); - else - code = bignVerify(params, oid_der, oid_len, hash, sig->sig, - cvc->pubkey); + switch (cvc->pubkey_len) + { + case 64: + code = bign128Verify(hash, sig->sig, cvc->pubkey); + break; + case 96: + code = bign192Verify(hash, sig->sig, cvc->pubkey); + break; + case 128: + code = bign256Verify(hash, sig->sig, cvc->pubkey); + break; + case 48: + code = bign96Verify(hash, sig->sig, cvc->pubkey); + } // завершить cmdBlobClose(state); return code; diff --git a/cmd/core/cmd_st.c b/cmd/core/cmd_st.c index 6978ce00..408ba344 100644 --- a/cmd/core/cmd_st.c +++ b/cmd/core/cmd_st.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include "bee2/cmd.h" @@ -185,13 +185,9 @@ static err_t cmdStBels() static err_t cmdStBign() { octet state[1024]; - bign_params params[1]; octet privkey[32]; octet pubkey[64]; octet hash[32]; - const octet oid[] = { - 0x06, 0x09, 0x2A, 0x70, 0x00, 0x02, 0x00, 0x22, 0x65, 0x1F, 0x51, - }; octet sig[48]; // bign-genkeypair hexTo(privkey, @@ -199,9 +195,7 @@ static err_t cmdStBign() "34281FED0732429E0C79235FC273E269"); ASSERT(sizeof(state) >= prngEcho_keep()); prngEchoStart(state, privkey, 32); - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || - bignKeypairGen(privkey, pubkey, params, prngEchoStepR, - state) != ERR_OK || + if (bign128KeypairGen(privkey, pubkey, prngEchoStepR, state) != ERR_OK || !hexEq(pubkey, "BD1A5650179D79E03FCEE49D4C2BD5DD" "F54CE46D0CF11E4FF87BF7A890857FD0" @@ -209,23 +203,22 @@ static err_t cmdStBign() "190C2EDA5909054A9AB84D2AB9D99A90")) return ERR_SELFTEST; // bign-valpubkey - if (bignPubkeyVal(params, pubkey) != ERR_OK) + if (bign128PubkeyVal(pubkey) != ERR_OK) return ERR_SELFTEST; // bign-sign if (beltHash(hash, beltH(), 13) != ERR_OK) return ERR_SELFTEST; - if (bignSign2(sig, params, oid, sizeof(oid), hash, privkey, - 0, 0) != ERR_OK) + if (bign128Sign2(sig, hash, privkey, 0, 0) != ERR_OK) return ERR_SELFTEST; if (!hexEq(sig, "19D32B7E01E25BAE4A70EB6BCA42602C" "CA6A13944451BCC5D4C54CFD8737619C" "328B8A58FB9C68FD17D569F7D06495FB")) return ERR_SELFTEST; - if (bignVerify(params, oid, sizeof(oid), hash, sig, pubkey) != ERR_OK) + if (bign128Verify(hash, sig, pubkey) != ERR_OK) return ERR_SELFTEST; sig[0] ^= 1; - if (bignVerify(params, oid, sizeof(oid), hash, sig, pubkey) == ERR_OK) + if (bign128Verify(hash, sig, pubkey) == ERR_OK) return ERR_SELFTEST; // все нормально return ERR_OK; diff --git a/cmd/kg/kg.c b/cmd/kg/kg.c index 4fe30976..22619e9a 100644 --- a/cmd/kg/kg.c +++ b/cmd/kg/kg.c @@ -4,7 +4,7 @@ \brief Generate and manage private keys \project bee2/cmd \created 2022.06.08 -\version 2025.10.22 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -17,7 +17,9 @@ #include #include #include -#include +#include +#include +#include #include #include "bee2/cmd.h" @@ -70,28 +72,6 @@ static int kgUsage() return -1; } -/* -******************************************************************************* -Вспомогательные функции -******************************************************************************* -*/ - -static err_t kgParamsStd(bign_params* params, size_t privkey_len) -{ - switch (privkey_len) - { - case 24: - return bign96ParamsStd(params, "1.2.112.0.2.0.34.101.45.3.0"); - case 32: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1"); - case 48: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2"); - case 64: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.3"); - } - return ERR_BAD_INPUT; -} - /* ******************************************************************************* Генерация ключа @@ -105,7 +85,6 @@ static err_t kgGen(int argc, char* argv[]) err_t code = ERR_OK; size_t len = 0; cmd_pwd_t pwd = 0; - bign_params params[1]; void* state = 0; octet* privkey; /* [len] */ octet* pubkey; /* [2 * len] */ @@ -163,11 +142,9 @@ static err_t kgGen(int argc, char* argv[]) // проверить файл-контейнер code = cmdFileValNotExist(1, argv); ERR_CALL_HANDLE(code, cmdPwdClose(pwd)); - // загрузить параметры + // длина ключа по умолчанию if (len == 0) len = 32; - code = kgParamsStd(params, len); - ERR_CALL_HANDLE(code, cmdPwdClose(pwd)); // запустить ГСЧ code = cmdRngStart(TRUE); ERR_CALL_HANDLE(code, cmdPwdClose(pwd)); @@ -179,8 +156,20 @@ static err_t kgGen(int argc, char* argv[]) &privkey, &pubkey); ERR_CALL_HANDLE(code, cmdPwdClose(pwd)); // генерировать ключ - code = len == 24 ? bign96KeypairGen(privkey, pubkey, params, rngStepR, 0) : - bignKeypairGen(privkey, pubkey, params, rngStepR, 0); + switch (len) + { + case 32: + code = bign128KeypairGen(privkey, pubkey, rngStepR, 0); + break; + case 48: + code = bign192KeypairGen(privkey, pubkey, rngStepR, 0); + break; + case 64: + code = bign256KeypairGen(privkey, pubkey, rngStepR, 0); + break; + case 24: + code = bign96KeypairGen(privkey, pubkey, rngStepR, 0); + } ERR_CALL_HANDLE(code, (cmdBlobClose(state), cmdPwdClose(pwd))); // обновить ключ ГСЧ rngRekey(); @@ -355,7 +344,6 @@ static err_t kgExtr(int argc, char* argv[]) { err_t code = ERR_OK; cmd_pwd_t pwd = 0; - bign_params params[1]; size_t len = 0; void* state; octet* privkey; /* [len] */ @@ -415,10 +403,23 @@ static err_t kgExtr(int argc, char* argv[]) cmdPwdClose(pwd); ERR_CALL_HANDLE(code, cmdBlobClose(state)); // определить открытый ключ - code = kgParamsStd(params, len); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - code = len == 24 ? bign96PubkeyCalc(pubkey, params, privkey) : - bignPubkeyCalc(pubkey, params, privkey); + switch (len) + { + case 32: + code = bign128PubkeyCalc(pubkey, privkey); + break; + case 48: + code = bign192PubkeyCalc(pubkey, privkey); + break; + case 64: + code = bign256PubkeyCalc(pubkey, privkey); + break; + case 24: + code = bign96PubkeyCalc(pubkey, privkey); + break; + default: + code = ERR_BAD_PRIVKEY; + } ERR_CALL_HANDLE(code, cmdBlobClose(state)); // записать открытый ключ code = cmdFileWrite(argv[1], pubkey, len * 2); @@ -439,7 +440,6 @@ static err_t kgPrint(int argc, char* argv[]) { err_t code = ERR_OK; cmd_pwd_t pwd = 0; - bign_params params[1]; size_t len = 0; void* state; octet* privkey; /* [len] */ @@ -498,10 +498,23 @@ static err_t kgPrint(int argc, char* argv[]) cmdPwdClose(pwd); ERR_CALL_HANDLE(code, cmdBlobClose(state)); // определить открытый ключ - code = kgParamsStd(params, len); - ERR_CALL_HANDLE(code, cmdBlobClose(state)); - code = len == 24 ? bign96PubkeyCalc(pubkey, params, privkey) : - bignPubkeyCalc(pubkey, params, privkey); + switch (len) + { + case 32: + code = bign128PubkeyCalc(pubkey, privkey); + break; + case 48: + code = bign192PubkeyCalc(pubkey, privkey); + break; + case 64: + code = bign256PubkeyCalc(pubkey, privkey); + break; + case 24: + code = bign96PubkeyCalc(pubkey, privkey); + break; + default: + code = ERR_BAD_PRIVKEY; + } ERR_CALL_HANDLE(code, cmdBlobClose(state)); // печатать открытый ключ hexFrom(hex, pubkey, len * 2); diff --git a/include/bee2/crypto/bign96.h b/include/bee2/crypto/bign96.h index faccf398..642a55a0 100644 --- a/include/bee2/crypto/bign96.h +++ b/include/bee2/crypto/bign96.h @@ -4,7 +4,7 @@ \brief Experimental Bign signatures of security level 96 \project bee2 [cryptographic library] \created 2021.01.20 -\version 2025.09.05 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -31,9 +31,12 @@ extern "C" { \file bign96.h Подпись Bign96 -- это подпись Bign при снижении уровня стойкости до l = 96 -и следующих корректировках: -1) уменьшается длина s0, первой часть подписи: с 96 битов до 80; -2) в детерминированном режиме выработки подписи (функция bign96Sign2) для +и следующих уточнениях и корректировках: +1) используется эллиптическая кривая bign-curve192v1; +2) при выработке и проверке ЭЦП предварительное хэширование выполняется с + помощью алгоритма belt-hash; +3) уменьшается длина s0, первой часть подписи: с 96 битов до 80; +4) в детерминированном режиме выработки подписи (функция bign96Sign2) для построения одноразового личного ключа вместо алгоритма belt-wblock используется алгоритм belt-32block (шифрование полутора блоков). Последний алгоритм определен в СТБ 34.101.31-2020 как вспомогательный для @@ -45,36 +48,6 @@ extern "C" { ******************************************************************************* */ -/* -******************************************************************************* -Управление параметрами -******************************************************************************* -*/ - - -/*! \brief Загрузка стандартных долговременных параметров - - В params загружаются стандартные долговременные параметры с именем name. - Поддерживается только одно имя: - "1.2.112.0.2.0.34.101.45.3.0". - \return ERR_OK, если параметры успешно загружены, и код ошибки в - противном случае. -*/ -err_t bign96ParamsStd( - bign_params* params, /*!< [out] стандартные параметры */ - const char* name /*!< [in] имя параметров */ -); - -/*! \brief Проверка долговременных параметров - - Проверяется корректность долговременных параметров params. - \return ERR_OK, если параметры корректны, и код ошибки - в противном случае. -*/ -err_t bign96ParamsVal( - const bign_params* params /*!< [in] долговременные параметры */ -); - /* ******************************************************************************* Управление ключами @@ -86,7 +59,6 @@ err_t bign96ParamsVal( При долговременных параметрах params генерируются личный privkey и открытый pubkey ключи. При генерации используется генератор rng и его состояние rng_state. - \expect{ERR_BAD_PARAMS} Параметры params корректны. \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. \expect Используется криптографически стойкий генератор rng. \return ERR_OK, если ключи успешно сгенерированы, и код ошибки @@ -95,48 +67,39 @@ err_t bign96ParamsVal( err_t bign96KeypairGen( octet privkey[24], /*!< [out] личный ключ */ octet pubkey[48], /*!< [out] открытый ключ */ - const bign_params* params, /*!< [in] долговременные параметры */ gen_i rng, /*!< [in] генератор случайных чисел */ void* rng_state /*!< [in/out] состояние генератора */ ); /*! \brief Проверка пары ключей - При долговременных параметрах params проверяется корректность - личного ключа privkey и соответствие ему открытого ключа pubkey. - \expect{ERR_BAD_PARAMS} Параметры params корректны. + Проверяется корректность личного ключа privkey и соответствие ему + открытого ключа pubkey. \return ERR_OK, если пара корректна, и код ошибки в противном случае. */ err_t bign96KeypairVal( - const bign_params* params, /*!< [in] долговременные параметры */ const octet privkey[24], /*!< [in] личный ключ */ const octet pubkey[48] /*!< [in] открытый ключ */ ); /*! \brief Проверка открытого ключа - При долговременных параметрах params проверяется корректность - открытого ключа pubkey. - \expect{ERR_BAD_PARAMS} Параметры params корректны. + Проверяется корректность открытого ключа pubkey. \return ERR_OK, если ключ корректен, и код ошибки в противном случае. */ err_t bign96PubkeyVal( - const bign_params* params, /*!< [in] долговременные параметры */ const octet pubkey[48] /*!< [in] проверяемый ключ */ ); /*! \brief Построение открытого ключа по личному - При долговременных параметрах params по личному ключу privkey - строится открытый ключ pubkey. - \expect{ERR_BAD_PARAMS} Параметры params корректны. + По личному ключу privkey строится открытый ключ pubkey. \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. \return ERR_OK, если открытый ключ успешно построен, и код ошибки в противном случае. */ err_t bign96PubkeyCalc( octet pubkey[48], /*!< [out] открытый ключ */ - const bign_params* params, /*!< [in] долговременные параметры */ const octet privkey[24] /*!< [in] личный ключ */ ); @@ -148,13 +111,9 @@ err_t bign96PubkeyCalc( /*! \brief Выработка ЭЦП - Вырабатывается подпись sig сообщения с хэш-значением hash, полученном - с помощью алгоритма с идентификатором [oid_len]oid_der, заданным DER-кодом. - Подпись вырабатывается на личном ключе privkey. При выработке ЭЦП - используются долговременные параметры params и генератор rng - с состоянием rng_state. - \expect{ERR_BAD_PARAMS} Параметры params корректны. - \expect{ERR_BAD_OID} Идентификатор oid_der корректен. + Вырабатывается подпись sig сообщения с хэш-значением hash. Подпись + вырабатывается на личном ключе privkey. Используется генератор rng с + состоянием rng_state. \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. \expect{ERR_BAD_RNG} Генератор rng (с состоянием rng_state) корректен. @@ -164,9 +123,6 @@ err_t bign96PubkeyCalc( */ err_t bign96Sign( octet sig[34], /*!< [out] подпись */ - const bign_params* params, /*!< [in] долговременные параметры */ - const octet oid_der[], /*!< [in] идентификатор хэш-алгоритма */ - size_t oid_len, /*!< [in] длина oid_der в октетах */ const octet hash[24], /*!< [in] хэш-значение */ const octet privkey[24], /*!< [in] личный ключ */ gen_i rng, /*!< [in] генератор случайных чисел */ @@ -175,14 +131,10 @@ err_t bign96Sign( /*! \brief Детерминированная выработка ЭЦП - Вырабатывается подпись sig сообщения с хэш-значением hash, полученном - с помощью алгоритма с идентификатором [oid_len]oid_der, заданным DER-кодом. - Подпись вырабатывается на личном ключе privkey. При выработке ЭЦП - используются долговременные параметры params. Одноразовый личный ключ + Вырабатывается подпись sig сообщения с хэш-значением hash. Подпись + вырабатывается на личном ключе privkey. Одноразовый личный ключ генерируется с использованием дополнительных данных [t_len]t. Если t == 0, то дополнительные данные не используются. - \expect{ERR_BAD_PARAMS} Параметры params корректны. - \expect{ERR_BAD_OID} Идентификатор oid_der корректен. \expect{ERR_BAD_INPUT} Буферы sig и hash не пересекаются. \expect{ERR_BAD_PRIVKEY} Личный ключ privkey корректен. \return ERR_OK, если подпись выработана, и код ошибки в противном @@ -190,9 +142,6 @@ err_t bign96Sign( */ err_t bign96Sign2( octet sig[34], /*!< [out] подпись */ - const bign_params* params, /*!< [in] долговременные параметры */ - const octet oid_der[], /*!< [in] идентификатор хэш-алгоритма */ - size_t oid_len, /*!< [in] длина oid_der в октетах */ const octet hash[24], /*!< [in] хэш-значение */ const octet privkey[24], /*!< [in] личный ключ */ const void* t, /*!< [in] дополнительные данные */ @@ -202,20 +151,13 @@ err_t bign96Sign2( /*! \brief Проверка ЭЦП Проверяется ЭЦП sig сообщения с хэш-значением hash. При проверке - используются долговременные параметры params и открытый ключ pubkey. - Считается, что хэш-значение hash получено с помощью алгоритма - с идентификатором [oid_len]oid_der, заданным DER-кодом. - \expect{ERR_BAD_PARAMS} Параметры params корректны. - \expect{ERR_BAD_OID} Идентификатор oid_der корректен. + используется открытый ключ pubkey. \expect{ERR_BAD_PUBKEY} Открытый ключ pubkey корректен. \return ERR_OK, если подпись корректна, и код ошибки в противном случае. \remark При нарушении ограничений на ЭЦП возвращается код ERR_BAD_SIG. */ err_t bign96Verify( - const bign_params* params, /*!< [in] долговременные параметры */ - const octet oid_der[], /*!< [in] идентификатор хэш-алгоритма */ - size_t oid_len, /*!< [in] длина oid_der в октетах */ const octet hash[24], /*!< [in] хэш-значение */ const octet sig[34], /*!< [in] подпись */ const octet pubkey[48] /*!< [in] открытый ключ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bf8fb702..b75e6219 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,7 +61,7 @@ set(src crypto/bign/bign128.c crypto/bign/bign192.c crypto/bign/bign256.c - crypto/bign96.c + crypto/bign/bign96.c crypto/bpki.c crypto/botp.c crypto/brng.c diff --git a/src/crypto/bign96.c b/src/crypto/bign/bign96.c similarity index 74% rename from src/crypto/bign96.c rename to src/crypto/bign/bign96.c index d3378f92..f344e97c 100644 --- a/src/crypto/bign96.c +++ b/src/crypto/bign/bign96.c @@ -4,7 +4,7 @@ \brief Experimental Bign signatures of security level 96 \project bee2 [cryptographic library] \created 2021.01.20 -\version 2025.10.02 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -12,19 +12,15 @@ #include "bee2/core/blob.h" #include "bee2/core/err.h" -#include "bee2/core/mem.h" -#include "bee2/core/oid.h" +#include "bee2/core/mt.h" #include "bee2/core/str.h" #include "bee2/core/u32.h" #include "bee2/core/util.h" #include "bee2/crypto/belt.h" -#include "bee2/crypto/bign.h" #include "bee2/crypto/bign96.h" -#include "bee2/math/gfp.h" -#include "bee2/math/ecp.h" #include "bee2/math/ww.h" #include "bee2/math/zz.h" -#include "bign/bign_lcl.h" +#include "bign_lcl.h" /* ******************************************************************************* @@ -69,38 +65,6 @@ static const octet _curve96v1_yG[24] = { 0x17, 0xC3, 0x68, 0xC1, 0x4B, 0x96, 0x38, 0x81, }; -/* -******************************************************************************* -belt-32block - -Алгоритм belt-32block определен в СТБ 34.101.31-2020. В Bign96 счетчик -числа тактов round не сбрасывается при последовательных обращениях -к belt-32block, а продолжает инкрементироваться. - -\remark На основе bee2/crypto/belt/belt_fmt.с. -******************************************************************************* -*/ - -static void belt32BlockEncr(octet block[24], const u32 key[8], u32* round) -{ - u32* t; - // подготовить память - ASSERT(memIsAligned(block, 4)); - t = (u32*)block; - u32From(t, block, 24); - // round #1 - beltBlockEncr3(t + 2, t + 3, t + 4, t + 5, key); - t[2] ^= (*round)++, t[0] ^= t[2], t[1] ^= t[3]; - // round #2 - beltBlockEncr3(t + 4, t + 5, t + 0, t + 1, key); - t[4] ^= (*round)++, t[2] ^= t[4], t[3] ^= t[5]; - // round #3 - beltBlockEncr3(t + 0, t + 1, t + 2, t + 3, key); - t[0] ^= (*round)++, t[4] ^= t[0], t[5] ^= t[1]; - // возврат - u32To(block, 24, t); -} - /* ******************************************************************************* Загрузка стандартных параметров @@ -128,35 +92,104 @@ err_t bign96ParamsStd(bign_params* params, const char* name) /* ******************************************************************************* -Предварительная проверка параметров +Предвычисленные точки +******************************************************************************* +*/ + +#include "pre/bign96_pre_od7.c" + +/* +******************************************************************************* +Кривая ******************************************************************************* */ -static err_t bign96ParamsCheck(const bign_params* params) +static size_t _once; /*< триггер однократности */ +static mt_mtx_t _mtx[1]; /*< мьютекс */ +static bool_t _inited; /*< мьютекс создан? */ +static ec_o* _ec; /*< кривая */ + +static void bign96Destroy() { - err_t code; - code = bignParamsCheck2(params); - ERR_CALL_CHECK(code); - return (params->l == 96) ? ERR_OK : ERR_BAD_PARAMS; + mtMtxLock(_mtx); + bignEcClose(_ec), _ec = 0; + mtMtxUnlock(_mtx); + mtMtxClose(_mtx); +} + +static void bign96Init() +{ + ASSERT(!_inited); + // создать мьютекс + if (!mtMtxCreate(_mtx)) + return; + // зарегистрировать деструктор + if (!utilOnExit(bign96Destroy)) + { + mtMtxClose(_mtx); + return; + } + _inited = TRUE; +} + +static err_t bign96Ec(const ec_o** pec) +{ + ASSERT(memIsValid(pec, sizeof(const ec_o*))); + // инициализировать однократно + if (!mtCallOnce(&_once, bign96Init) || !_inited) + return ERR_FILE_CREATE; + // заблокировать мьютекс + mtMtxLock(_mtx); + // кривая не создана? + if (_ec == 0) + { + err_t code; + bign_params params[1]; + // загрузить параметры + code = bign96ParamsStd(params, "1.2.112.0.2.0.34.101.45.3.0"); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // создать кривую + code = bignEcCreate(&_ec, params); + ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); + // настроить ec->pre +// _ec->pre = &_pre; + } + // возвратить кривую + *pec = _ec; + mtMtxUnlock(_mtx); + return ERR_OK; } /* ******************************************************************************* -Проверка параметров +belt-32block + +Алгоритм belt-32block определен в СТБ 34.101.31-2020. В Bign96 счетчик +числа тактов round не сбрасывается при последовательных обращениях +к belt-32block, а продолжает инкрементироваться. + +\remark На основе bee2/crypto/belt/belt_fmt.с. ******************************************************************************* */ -err_t bign96ParamsVal(const bign_params* params) +static void belt32BlockEncr(octet block[24], const u32 key[8], u32* round) { - err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); - ERR_CALL_CHECK(code); - code = bignParamsValEc(ec, params); - bignEcClose(ec); - return code; + u32* t; + // подготовить память + ASSERT(memIsAligned(block, 4)); + t = (u32*)block; + u32From(t, block, 24); + // round #1 + beltBlockEncr3(t + 2, t + 3, t + 4, t + 5, key); + t[2] ^= (*round)++, t[0] ^= t[2], t[1] ^= t[3]; + // round #2 + beltBlockEncr3(t + 4, t + 5, t + 0, t + 1, key); + t[4] ^= (*round)++, t[2] ^= t[4], t[3] ^= t[5]; + // round #3 + beltBlockEncr3(t + 0, t + 1, t + 2, t + 3, key); + t[0] ^= (*round)++, t[4] ^= t[0], t[5] ^= t[1]; + // возврат + u32To(block, 24, t); } /* @@ -165,70 +198,57 @@ err_t bign96ParamsVal(const bign_params* params) ******************************************************************************* */ -err_t bign96KeypairGen(octet privkey[24], octet pubkey[48], - const bign_params* params, gen_i rng, void* rng_state) +err_t bign96KeypairGen(octet privkey[24], octet pubkey[48], gen_i rng, + void* rng_state) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); - bignEcClose(ec); - return code; + return bignKeypairGenEc(privkey, pubkey, ec, rng, rng_state); } -err_t bign96KeypairVal(const bign_params* params, const octet privkey[24], - const octet pubkey[48]) +err_t bign96KeypairVal(const octet privkey[24], const octet pubkey[48]) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bignKeypairValEc(ec, privkey, pubkey); - bignEcClose(ec); - return code; + return bignKeypairValEc(ec, privkey, pubkey); } -err_t bign96PubkeyVal(const bign_params* params, const octet pubkey[48]) +err_t bign96PubkeyVal(const octet pubkey[48]) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyValEc(ec, pubkey); - bignEcClose(ec); - return code; + return bignPubkeyValEc(ec, pubkey); } -err_t bign96PubkeyCalc(octet pubkey[48], const bign_params* params, - const octet privkey[24]) +err_t bign96PubkeyCalc(octet pubkey[48], const octet privkey[24]) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bignPubkeyCalcEc(pubkey, ec, privkey); - bignEcClose(ec); - return code; + return bignPubkeyCalcEc(pubkey, ec, privkey); } /* ******************************************************************************* Выработка ЭЦП + +\remark DER(belt-hash) = 1.2.112.0.2.0.34.101.31.81 ******************************************************************************* */ -err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet oid_der[], - size_t oid_len, const octet hash[24], const octet privkey[24], gen_i rng, - void* rng_state) +static const octet _oid_der[] = { + 0x06, 0x09, 0x2A, 0x70, 0x00, 0x02, 0x00, 0x22, 0x65, 0x1F, 0x51, +}; + +err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet hash[24], + const octet privkey[24], gen_i rng, void* rng_state) { size_t n; void* state; @@ -246,8 +266,6 @@ err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet oid_der[], if (!memIsValid(hash, 24) || !memIsValid(privkey, 24) || !memIsValid(sig, 34) || !memIsDisjoint2(hash, 24, sig, 34)) return ERR_BAD_INPUT; - if (oid_len == SIZE_MAX || oidFromDER(0, oid_der, oid_len) == SIZE_MAX) - return ERR_BAD_OID; if (rng == 0) return ERR_BAD_RNG; // создать состояние @@ -259,7 +277,7 @@ err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet oid_der[], O_OF_W(W_OF_O(13)), utilMax(4, beltHash_keep(), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulBase_deep(n, ec->d, ec->deep), zzMul_deep(W_OF_O(13), n), zzMod_deep(n + W_OF_O(13), n)), SIZE_MAX, @@ -280,7 +298,7 @@ err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet oid_der[], return ERR_BAD_RNG; } // R <- k G - if (!ecMulA(R, ec->base, ec, k, n, stack)) + if (!bignMulBase(R, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -288,7 +306,7 @@ err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet oid_der[], qrTo((octet*)R, ecX(R), ec->f, stack); // s0 <- belt-hash(oid || R || H) mod 2^80 + 2^l beltHashStart(stack); - beltHashStepH(oid_der, oid_len, stack); + beltHashStepH(_oid_der, sizeof(_oid_der), stack); beltHashStepH(R, 24, stack); beltHashStepH(hash, 24, stack); beltHashStepG2(sig, 10, stack); @@ -309,31 +327,18 @@ err_t bign96SignEc(octet sig[34], const ec_o* ec, const octet oid_der[], return ERR_OK; } -err_t bign96Sign(octet sig[34], const bign_params* params, - const octet oid_der[], size_t oid_len, const octet hash[24], - const octet privkey[24], gen_i rng, void* rng_state) +err_t bign96Sign(octet sig[34], const octet hash[24], const octet privkey[24], + gen_i rng, void* rng_state) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); - ERR_CALL_CHECK(code); - code = bign96SignEc(sig, ec, oid_der, oid_len, hash, privkey, rng, - rng_state); - bignEcClose(ec); - return code; + return bign96SignEc(sig, ec, hash, privkey, rng, rng_state); } -/* -******************************************************************************* -Детерминированная выработка ЭЦП -******************************************************************************* -*/ - -err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet oid_der[], - size_t oid_len, const octet hash[24], const octet privkey[24], - const void* t, size_t t_len) +err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet hash[24], + const octet privkey[24], const void* t, size_t t_len) { size_t n; void* state; @@ -353,8 +358,6 @@ err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet oid_der[], if (!memIsValid(hash, 24) || !memIsValid(privkey, 24) || !memIsValid(sig, 34) || !memIsDisjoint2(hash, 24, sig, 34)) return ERR_BAD_INPUT; - if (oid_len == SIZE_MAX || oidFromDER(0, oid_der, oid_len) == SIZE_MAX) - return ERR_BAD_OID; if (!memIsNullOrValid(t, t_len)) return ERR_BAD_INPUT; // создать состояние @@ -369,7 +372,7 @@ err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet oid_der[], beltHash_keep(), (size_t)32, beltKWP_keep(), - ecMulA_deep(n, ec->d, ec->deep, n), + bignMulA_deep(n, ec->d, ec->deep), zzMul_deep(W_OF_O(13), n), zzMod_deep(n + W_OF_O(13), n)), SIZE_MAX, @@ -385,7 +388,7 @@ err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet oid_der[], } // хэшировать oid beltHashStart(hash_state); - beltHashStepH(oid_der, oid_len, hash_state); + beltHashStepH(_oid_der, sizeof(_oid_der), hash_state); // сгенерировать k по алгоритму 6.3.3 { // theta <- belt-hash(oid || d || t) @@ -408,7 +411,7 @@ err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet oid_der[], } } // R <- k G - if (!ecMulA(R, ec->base, ec, k, n, stack)) + if (!bignMulA(R, ec->base, ec, k, stack)) { blobClose(state); return ERR_BAD_PARAMS; @@ -435,19 +438,14 @@ err_t bign96Sign2Ec(octet sig[34], const ec_o* ec, const octet oid_der[], return ERR_OK; } -err_t bign96Sign2(octet sig[34], const bign_params* params, - const octet oid_der[], size_t oid_len, const octet hash[24], - const octet privkey[24], const void* t, size_t t_len) +err_t bign96Sign2(octet sig[34], const octet hash[24], const octet privkey[24], + const void* t, size_t t_len) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bign96Sign2Ec(sig, ec, oid_der, oid_len, hash, privkey, t, t_len); - bignEcClose(ec); - return code; + return bign96Sign2Ec(sig, ec, hash, privkey, t, t_len); } /* @@ -456,8 +454,8 @@ err_t bign96Sign2(octet sig[34], const bign_params* params, ******************************************************************************* */ -err_t bign96VerifyEc(const ec_o* ec, const octet oid_der[], size_t oid_len, - const octet hash[24], const octet sig[34], const octet pubkey[48]) +err_t bign96VerifyEc(const ec_o* ec, const octet hash[24], const octet sig[34], + const octet pubkey[48]) { err_t code; size_t n; @@ -476,8 +474,6 @@ err_t bign96VerifyEc(const ec_o* ec, const octet oid_der[], size_t oid_len, if (!memIsValid(hash, 24) || !memIsValid(sig, 34) || !memIsValid(pubkey, 48)) return ERR_BAD_INPUT; - if (oid_len == SIZE_MAX || oidFromDER(0, oid_der, oid_len) == SIZE_MAX) - return ERR_BAD_OID; // создать состояние state = blobCreate2( O_OF_W(2 * n), @@ -528,7 +524,7 @@ err_t bign96VerifyEc(const ec_o* ec, const octet oid_der[], size_t oid_len, qrTo((octet*)R, ecX(R), ec->f, stack); // s0 == belt-hash(oid || R || H) mod 2^80? beltHashStart(stack); - beltHashStepH(oid_der, oid_len, stack); + beltHashStepH(_oid_der, sizeof(_oid_der), stack); beltHashStepH(R, 24, stack); beltHashStepH(hash, 24, stack); code = beltHashStepV2(sig, 10, stack) ? ERR_OK : ERR_BAD_SIG; @@ -537,17 +533,12 @@ err_t bign96VerifyEc(const ec_o* ec, const octet oid_der[], size_t oid_len, return code; } -err_t bign96Verify(const bign_params* params, const octet oid_der[], - size_t oid_len, const octet hash[24], const octet sig[34], +err_t bign96Verify(const octet hash[24], const octet sig[34], const octet pubkey[48]) { err_t code; - ec_o* ec; - code = bign96ParamsCheck(params); - ERR_CALL_CHECK(code); - code = bignEcCreate(&ec, params); + const ec_o* ec; + code = bign96Ec(&ec); ERR_CALL_CHECK(code); - code = bign96VerifyEc(ec, oid_der, oid_len, hash, sig, pubkey); - bignEcClose(ec); - return code; + return bign96VerifyEc(ec, hash, sig, pubkey); } diff --git a/src/crypto/bign/pre/bign96_pre_od7.c b/src/crypto/bign/pre/bign96_pre_od7.c new file mode 100644 index 00000000..ee982c5b --- /dev/null +++ b/src/crypto/bign/pre/bign96_pre_od7.c @@ -0,0 +1,5392 @@ +/* +******************************************************************************* +\file bign96_pre_od7.c +\brief STB 34.101.45 (bign): precomputed tables (l=96, type=od, w=7) +\project bee2 [cryptographic library] +\created 2026.03.10 +\version 2026.03.10 +\copyright The Bee2 authors +\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). +******************************************************************************* +*/ + +static const ec_pre_t _pre = { + ec_pre_od, 7, 28, { + wwH16(00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00), + wwH16(00,00,00,00,00,00,00,00,EC,CC,48,F6,EB,7F,21,E0), + wwH16(0C,93,DA,03,B2,1B,F9,E6,17,C3,68,C1,4B,96,38,81), + wwH16(A7,E4,A1,9D,7C,02,DF,37,39,89,F2,E7,C9,86,B2,16), + wwH16(F8,38,60,D9,62,FC,E9,DB,7A,9D,57,CD,E4,01,27,45), + wwH16(42,0D,6D,91,F0,80,51,C8,9C,B3,2C,04,EF,A8,63,4C), + wwH16(A8,E4,D5,0E,4F,57,62,67,FD,5D,4D,D2,34,A6,49,50), + wwH16(D7,3B,75,57,8E,0C,3C,AB,EB,D9,0A,62,45,2C,9E,52), + wwH16(F2,F1,BC,2D,32,60,59,5F,25,79,F0,F3,24,13,F8,7F), + wwH16(7D,A9,0A,F6,78,D6,D2,81,47,65,B1,FE,D2,C1,DF,68), + wwH16(EE,23,97,B8,D3,D2,50,59,0C,A6,4A,49,35,44,62,1F), + wwH16(77,11,D9,5E,AB,D7,A0,BC,FB,17,05,85,EA,8C,C9,5F), + wwH16(7A,F0,B7,20,AB,AB,D9,71,4B,EE,C6,F2,A8,3A,1B,3B), + wwH16(DE,77,4F,84,25,2D,C0,4C,D4,F7,0A,59,A7,2C,7D,E7), + wwH16(C4,12,27,14,37,79,7A,AA,AF,19,03,A7,7B,E4,32,4E), + wwH16(53,3B,6E,C5,5A,9C,68,E6,23,8F,0F,B7,20,FE,B9,DD), + wwH16(93,6B,0E,DD,62,F4,02,80,22,00,95,F8,E8,B2,83,50), + wwH16(F3,DB,C4,9C,58,22,18,E7,5E,BD,36,A4,26,63,AB,44), + wwH16(1D,9A,32,40,BD,2C,F3,46,03,3F,6D,D5,D5,DA,05,53), + wwH16(B7,52,17,B5,68,26,C4,6B,3E,81,B0,B3,67,2A,3B,EF), + wwH16(6F,34,FC,25,1A,34,BE,38,CD,3C,C0,3B,AC,15,8D,01), + wwH16(0B,FA,0F,35,7A,35,3F,69,EF,D5,C6,43,02,7B,6E,47), + wwH16(0C,6B,BD,B1,08,2F,C9,68,59,38,40,2F,22,25,23,E5), + wwH16(B9,52,E8,A0,1F,60,4D,24,5A,C3,98,CE,63,A6,19,9C), + wwH16(99,CB,11,6E,FF,B3,BE,26,76,19,2C,F8,78,CB,0B,D1), + wwH16(83,90,EB,B4,0E,B3,FF,B4,FE,80,02,59,B3,7A,5A,DC), + wwH16(BB,99,CD,EA,2E,67,4F,40,BB,8A,77,D3,40,64,09,F4), + wwH16(CB,B1,4B,18,70,40,25,AF,B8,0D,68,E7,4C,89,2A,3B), + wwH16(0F,3F,4F,E0,B9,33,B6,4A,7C,35,D5,BD,78,02,0B,DC), + wwH16(35,81,7F,C9,92,64,FE,CE,1E,AA,A7,C2,EF,11,04,F2), + wwH16(C5,A2,30,43,8C,86,76,6D,2B,E3,10,5D,CA,9A,C9,01), + wwH16(38,D5,21,E3,4F,6A,39,79,55,EA,87,1A,48,B2,DF,94), + wwH16(46,FC,B2,B4,57,7F,48,E7,44,4D,AD,38,F5,58,02,4F), + wwH16(0A,F8,72,3A,FD,B7,E1,20,60,93,D1,F7,F8,13,00,29), + wwH16(14,D0,7E,45,9A,BA,4E,35,E7,E8,4D,78,55,66,BA,CD), + wwH16(66,D9,4B,59,0F,DA,34,43,95,1C,43,69,63,31,9F,FD), + wwH16(24,0A,A0,1E,C5,A8,42,6B,52,B5,6F,C3,BF,AD,9A,0E), + wwH16(C1,2C,63,52,44,73,B8,20,DC,F2,FA,2B,64,E2,64,44), + wwH16(DD,AD,73,70,B0,50,D6,3F,99,6C,B3,82,43,88,36,61), + wwH16(03,29,5A,D3,2B,C4,EE,C9,F8,03,09,CC,E2,F3,11,AE), + wwH16(97,F3,BD,C6,04,34,AD,E8,DF,C5,B1,9A,93,0C,12,F0), + wwH16(32,05,09,17,47,7D,3E,B7,67,14,0E,B8,CB,BE,16,D3), + wwH16(54,DF,DB,84,78,A3,50,F6,EC,CF,04,1E,2F,D9,ED,37), + wwH16(B0,39,17,6A,20,14,97,B6,4B,2D,8E,81,52,64,55,57), + wwH16(16,E4,30,CA,EF,84,1B,AA,00,F0,7B,25,88,17,59,0C), + wwH16(EE,59,70,6C,43,22,01,5B,DD,EB,3B,C8,D4,56,4C,17), + wwH16(52,50,6E,B8,DA,F3,4D,A4,EA,F3,57,B3,21,3C,90,77), + wwH16(54,33,63,98,09,1B,86,E5,B5,56,F1,F0,A1,F4,37,F1), + wwH16(7E,97,D2,67,7C,D3,74,2E,6B,FF,7A,4C,7D,3D,2E,9D), + wwH16(EB,8F,F6,B7,A6,AC,6E,5D,71,8C,50,40,CE,95,D5,F4), + wwH16(26,EB,55,25,6D,C2,02,73,82,3E,18,D1,16,E1,4E,67), + wwH16(7B,11,13,02,3F,21,EC,FA,E7,08,F3,CE,F4,8C,FB,C0), + wwH16(E0,B9,9F,7A,8B,77,8D,3C,E0,37,BB,9F,38,D0,22,FE), + wwH16(8D,57,5A,07,24,53,1C,10,3D,CE,3C,C5,23,7D,E6,EA), + wwH16(3A,20,84,7F,85,CF,82,83,0A,62,7D,71,15,57,E2,82), + wwH16(6F,6C,E0,37,BA,F6,04,B3,4A,E4,C8,68,76,CA,8D,76), + wwH16(21,EB,DC,5A,E9,2A,C4,D6,BB,2A,6B,E5,0E,30,47,E3), + wwH16(F1,C9,FA,BA,CB,8C,06,BD,CA,9D,DB,1E,51,FE,30,5B), + wwH16(49,0C,A0,85,F2,22,0C,D0,3B,7E,B4,FD,4B,FF,16,61), + wwH16(87,88,4E,19,87,85,14,38,CF,CE,46,89,66,60,FB,91), + wwH16(EA,5C,F6,3A,56,30,C4,56,7E,A5,F2,65,A2,44,53,61), + wwH16(05,C7,E4,4B,29,5F,B1,33,E1,32,2D,E8,3D,49,52,6B), + wwH16(F7,FE,7F,2D,BF,CB,99,8A,8A,49,60,22,D2,60,B0,63), + wwH16(F8,0D,A1,11,D2,CA,A8,2B,2F,45,C5,A4,57,38,C2,9B), + wwH16(ED,2B,DE,22,56,82,7D,8D,89,96,BD,5B,5D,3C,C7,A6), + wwH16(1E,8A,A4,89,5A,BD,E1,BB,7C,E0,12,4B,B4,48,6C,0A), + wwH16(3F,16,BC,62,AD,DA,AA,B3,54,27,7B,44,3E,44,39,55), + wwH16(22,B3,0D,EA,73,4C,78,FC,50,DF,50,43,07,21,CB,A2), + wwH16(36,85,98,09,23,12,68,7E,D2,D9,E5,45,65,32,17,30), + wwH16(02,CF,17,34,47,4E,9D,30,A6,C2,9A,9D,22,0B,7F,BA), + wwH16(A2,F6,C4,99,A8,83,C2,8C,E1,6A,21,ED,78,A6,F8,32), + wwH16(BF,B6,79,96,FD,3A,94,C0,C7,D1,47,F4,C1,C5,1B,24), + wwH16(CB,7D,08,1A,5F,5A,E2,86,06,B4,1E,5F,77,8D,B6,7D), + wwH16(37,44,90,9D,9F,E5,5C,7C,F4,E9,41,0F,0B,35,6C,95), + wwH16(38,A8,93,74,46,CB,31,DA,09,9F,DC,0F,92,CA,AF,F4), + wwH16(E1,4B,FA,30,9D,98,F3,32,29,7B,E1,67,4D,27,76,F0), + wwH16(FF,E2,31,FC,47,81,3D,FB,84,71,0F,F5,32,BE,03,47), + wwH16(55,B4,FC,BC,9C,FD,3C,18,86,1A,10,85,E9,22,86,C9), + wwH16(0B,C1,69,67,6D,02,DB,A2,68,FF,FA,8F,84,FA,8E,62), + wwH16(88,C9,59,7F,8C,26,D7,25,1B,E5,D5,A7,34,88,26,4A), + wwH16(1A,91,D1,EF,0F,F7,50,EE,0C,F1,67,7E,A0,00,45,C9), + wwH16(2D,6B,57,E1,9F,CD,5C,46,81,6E,F3,5E,37,64,B8,AE), + wwH16(DF,E9,BD,9E,28,92,54,B3,1B,E1,FD,32,CF,7C,8D,54), + wwH16(A6,1C,78,81,FF,79,A4,86,F0,60,B5,62,19,CC,9C,BD), + wwH16(FE,96,3F,47,FE,9D,08,74,7E,82,8F,E0,8C,55,24,DA), + wwH16(AD,7E,BC,FA,9D,9F,E5,0A,B9,8E,1C,CC,69,55,7F,17), + wwH16(FC,6D,44,01,7C,3A,93,C7,28,EF,D5,6B,67,65,2E,C4), + wwH16(95,25,A8,2B,66,1A,D3,8A,52,8D,AF,E5,A5,A2,D5,6C), + wwH16(82,61,AC,A8,2A,7D,77,E2,3A,10,FD,39,18,65,22,21), + wwH16(D4,62,9B,F5,20,A7,44,16,DA,F6,F9,18,C9,A7,7E,5F), + wwH16(09,87,9D,A3,B7,4C,80,6D,57,5A,5D,2E,F9,30,A8,AE), + wwH16(2F,82,88,E0,F0,FA,35,54,51,CD,71,6D,B6,4C,F6,29), + wwH16(C1,6D,9E,EE,20,DF,B8,75,01,0B,2E,4A,77,69,47,A6), + wwH16(8E,54,48,9D,1A,F3,C3,00,F0,62,6D,D8,FA,E3,ED,08), + wwH16(7F,29,7D,F9,8D,14,CA,FA,DD,CC,32,28,B9,98,E4,CC), + wwH16(41,69,CB,E7,5C,38,32,69,13,BE,1E,EF,41,74,D6,4C), + wwH16(3B,27,94,45,77,F1,A9,6F,FA,09,6A,1A,8F,28,D5,2E), + wwH16(E6,BC,7A,69,2C,34,BD,78,BA,D1,44,93,71,2A,EE,EB), + wwH16(59,C7,49,D8,A8,A9,27,88,AD,32,1E,4D,5B,09,E7,0A), + wwH16(8F,58,2F,06,96,D8,8B,35,41,51,65,B1,CF,62,62,15), + wwH16(84,27,C8,5A,F5,4C,E4,FD,40,59,84,93,83,0C,8D,D4), + wwH16(0F,00,CC,00,0B,6A,74,DE,6F,09,06,5F,EB,D4,C7,AC), + wwH16(E9,66,CD,22,A8,CA,57,13,D9,BB,95,44,0B,44,A9,68), + wwH16(22,07,99,18,9B,E0,6F,3A,67,6E,FD,CF,E9,75,E9,0E), + wwH16(30,25,CE,03,41,D0,CE,A0,76,0F,CE,1B,EB,F2,95,3B), + wwH16(7F,A2,2F,7F,76,D0,CD,DB,67,73,39,B9,56,39,44,AD), + wwH16(4C,2D,EE,48,D5,8E,E6,66,AA,D7,E7,E7,58,90,DD,67), + wwH16(98,DE,36,DF,7C,CE,E2,DF,2C,35,58,8A,C9,19,11,81), + wwH16(07,14,9E,2E,BF,22,33,D8,DA,75,EA,AB,8E,73,50,86), + wwH16(62,73,53,AC,C4,80,E3,9B,72,13,30,C7,DF,9A,9E,5A), + wwH16(09,AB,9B,D2,5F,B7,D1,16,3E,38,27,67,1A,F6,36,58), + wwH16(F6,18,C6,F3,64,74,DA,38,13,B1,F1,69,19,6E,3B,EE), + wwH16(56,02,66,9A,C1,5A,93,21,DB,8A,F6,7D,3D,2F,98,7A), + wwH16(23,9F,5D,07,FE,EB,7A,DB,21,91,50,3B,3D,8D,FB,A7), + wwH16(C3,06,08,5C,F2,FC,65,A1,B1,9D,76,3E,C8,34,76,28), + wwH16(F0,53,B5,C5,4D,B6,3F,F4,D9,28,84,7F,83,EF,A3,C9), + wwH16(94,EF,8F,13,BB,32,68,32,10,3D,A8,9D,11,41,9F,8A), + wwH16(EF,27,B1,3F,C4,57,73,D6,E3,FB,79,B5,75,03,DE,13), + wwH16(A9,2B,F4,24,82,8D,66,B6,18,B0,A3,3F,63,19,36,D7), + wwH16(C2,71,BE,F3,7C,05,9E,15,58,CF,D9,C6,DD,87,57,D0), + wwH16(A5,9F,2E,F5,35,CA,B4,3C,43,C9,A8,F3,74,33,E7,58), + wwH16(A3,22,77,4E,9D,0C,24,1B,C7,4A,F6,3F,BE,31,8D,94), + wwH16(B6,69,40,07,FD,C3,32,D1,DF,CE,89,DB,22,46,23,3F), + wwH16(1C,AF,25,D9,2E,FD,FB,EF,40,4C,EE,D9,00,8C,F7,E1), + wwH16(F5,0F,66,E4,C6,90,89,4E,97,0C,36,21,4C,BB,78,4E), + wwH16(BE,AC,78,8D,72,CA,C7,F3,0F,7E,7F,D7,B4,F3,EF,40), + wwH16(B4,A8,33,51,A0,07,B1,C2,4A,4D,25,60,28,F5,AF,A7), + wwH16(8A,16,CB,4B,BA,14,6F,6B,ED,38,DC,8F,A3,99,43,DA), + wwH16(72,57,8B,D5,04,1E,9F,C1,F6,52,29,77,AE,DF,1C,35), + wwH16(07,D3,43,E3,EA,D6,23,E3,BC,86,D5,78,05,AE,8A,89), + wwH16(3B,AD,D5,7D,7E,AC,8E,EF,44,D9,B8,EB,36,F9,A1,F0), + wwH16(2B,36,D8,35,62,72,D3,26,ED,62,25,AB,69,DB,13,D4), + wwH16(25,48,04,60,CC,30,F6,88,18,1F,9E,C5,56,90,2E,BC), + wwH16(0F,BF,1D,AA,02,88,AC,5D,BE,05,C5,87,B4,0B,F2,08), + wwH16(68,82,B1,D8,5D,7D,3E,E6,94,52,BF,3A,02,B4,9F,EB), + wwH16(6B,02,61,90,BC,A3,2A,83,98,1A,DE,B6,54,77,CB,D2), + wwH16(C7,CA,BD,45,FC,7F,B8,1D,9F,83,67,5E,0B,B5,88,D5), + wwH16(40,CA,26,2F,F9,F5,F3,05,FA,CE,C7,18,54,8A,C8,1B), + wwH16(AA,26,2C,3B,13,CE,35,57,52,E8,9F,A1,D1,8E,DF,D9), + wwH16(87,7A,B9,8A,62,30,73,7E,67,44,DA,FF,C0,BC,B8,C7), + wwH16(6C,32,51,34,FA,2A,C1,E7,4C,E7,69,49,4E,7C,1A,41), + wwH16(14,19,19,1A,6E,E9,04,35,AA,4C,95,A6,E1,34,13,90), + wwH16(B9,96,99,94,EA,0F,5C,18,C8,A9,04,DC,11,80,6D,2B), + wwH16(04,21,2D,2F,F4,CD,31,C9,1A,12,AE,CC,FA,31,B7,80), + wwH16(50,03,D2,A2,3F,CA,DC,08,F0,EA,A3,F7,90,CF,5C,E4), + wwH16(AE,55,B0,6A,05,38,18,54,03,BB,0E,AD,12,6D,A4,AE), + wwH16(1A,13,A5,8B,17,56,A7,D0,E3,08,70,5E,BA,0F,96,D6), + wwH16(DB,5C,17,9B,82,2E,07,DB,55,BC,AB,FB,63,E4,BF,85), + wwH16(DC,B1,E4,77,93,CF,E5,94,96,A3,AE,79,4D,57,D2,C5), + wwH16(4C,87,33,EE,80,6A,08,0C,D3,B3,03,E3,98,42,10,BD), + wwH16(A8,12,3D,1B,57,FF,1D,75,31,D7,7B,37,3F,A3,14,F1), + wwH16(7D,60,73,BB,A6,11,2E,E1,5A,9B,C8,B7,31,B8,2D,B0), + wwH16(DD,D8,A5,B3,DE,BD,FC,7A,03,68,4D,81,8C,B6,F0,1E), + wwH16(CC,73,85,03,FD,14,88,15,23,A6,1A,38,FB,13,FE,40), + wwH16(42,F0,5C,F3,76,B7,1E,32,7D,D6,D1,50,3E,81,8D,7A), + wwH16(1E,C3,6F,1D,65,7E,82,A4,67,FD,DC,7A,2A,E4,76,88), + wwH16(E2,6C,D3,10,39,79,0E,5A,25,D2,C8,5F,C5,03,E5,EC), + wwH16(41,C9,00,D4,7B,5A,9C,7F,57,23,FF,A7,83,5C,FA,AC), + wwH16(11,BC,E7,8D,57,9D,43,DE,90,48,9D,25,6D,2B,93,7D), + wwH16(90,9A,F3,41,05,26,32,B4,DC,2E,27,3D,37,5A,CB,F9), + wwH16(82,19,EC,7D,91,E3,80,EF,CF,45,FE,CF,F4,1C,30,F3), + wwH16(12,54,DE,33,21,D1,8B,79,22,FE,7D,34,20,27,A7,70), + wwH16(D4,8B,C2,E1,C8,DD,79,5A,78,C9,A8,02,3F,A1,71,BB), + wwH16(AF,63,57,3F,0D,08,A0,CF,B8,B8,CC,8D,B7,4C,25,A7), + wwH16(EF,6B,BB,DB,FB,F3,8A,4E,9F,BE,F2,12,3F,46,2A,87), + wwH16(21,2A,A5,F4,82,F0,68,D3,0B,B7,42,29,CF,06,EF,AC), + wwH16(BD,74,07,39,19,73,AE,69,94,95,66,BE,1B,AA,1E,54), + wwH16(62,3F,83,37,9D,15,9B,27,16,A2,9F,6D,C5,A3,DE,51), + wwH16(3F,9E,C6,99,B8,79,A9,D3,B0,7D,76,0D,B4,E2,23,8C), + wwH16(0B,53,EE,FE,1B,4D,F6,5A,53,8F,06,65,FF,52,2F,25), + wwH16(7E,3E,BE,68,CC,BD,E4,C9,AB,88,53,89,7A,5A,75,69), + wwH16(CE,12,1A,69,72,A4,66,6B,F7,D0,A1,3B,A0,B7,E4,88), + wwH16(CF,5E,0D,77,C6,C5,65,87,53,A6,00,95,E3,91,B2,4D), + wwH16(36,E8,95,26,2B,7B,5C,80,B0,D9,55,5E,29,BF,79,02), + wwH16(78,EC,FC,D0,61,6E,BC,C5,39,A5,25,D6,9C,18,1E,A2), + wwH16(29,1F,04,D1,8F,08,54,D2,E6,E8,10,A8,F7,E0,D3,A7), + wwH16(46,1E,67,46,C1,F0,1A,95,AA,95,AB,83,16,E8,2A,BE), + wwH16(D3,76,24,14,CF,A2,66,FF,B3,7B,D0,ED,99,02,27,9D), + wwH16(62,97,9B,1F,68,3A,0D,6C,A8,E8,81,BA,32,CE,F7,12), + wwH16(4C,1D,B9,9E,9E,61,6A,45,C7,EB,EC,C7,4A,63,7D,B6), + wwH16(66,1B,83,3A,5F,A7,12,6F,68,7B,3E,AB,A8,01,FF,B9), + wwH16(E6,8D,82,63,A9,81,69,41,A0,76,32,4D,C6,F9,55,DD), + wwH16(68,E4,C1,EA,18,0A,68,F9,2D,C4,7F,E0,2D,85,54,C8), + wwH16(97,A0,2C,32,45,18,BC,F5,AB,BE,1C,EF,AC,40,EB,D1), + wwH16(6D,DC,16,EF,9C,F5,DA,5E,73,CC,C6,00,94,87,F3,3D), + wwH16(C1,CC,9B,28,A4,D7,FD,26,46,99,D8,42,2F,EA,4D,AB), + wwH16(81,75,9D,BF,66,4D,73,F9,FA,F6,B0,5D,86,FF,BC,33), + wwH16(5E,B9,B0,5E,FA,4A,C1,D6,FB,C5,31,FE,8D,B4,FD,45), + wwH16(F7,FC,0D,3D,48,E7,49,CD,A6,4E,E9,35,D5,50,2F,D2), + wwH16(44,98,9B,B2,EA,32,5F,48,C4,A8,98,27,D8,81,50,F6), + wwH16(2E,BB,C1,D5,6B,B2,21,25,D7,8C,56,C4,B2,00,0D,04), + wwH16(24,70,77,A5,9C,EE,49,BE,9B,18,19,51,D3,0C,E9,D7), + wwH16(06,CF,0F,7D,F3,08,DB,FF,54,CA,65,B6,D7,4D,70,C8), + wwH16(3F,E9,F5,28,88,84,6F,3B,EB,1B,B6,F1,F6,3F,AE,BF), + wwH16(94,C8,36,04,4B,C8,89,BA,C6,53,E6,E3,D9,D0,E8,30), + wwH16(F8,F4,CF,80,21,05,20,16,BA,90,FB,65,6C,6C,29,9F), + wwH16(66,00,4E,90,21,CA,2D,3E,82,34,FE,61,0A,B3,14,A1), + wwH16(46,9D,39,4C,31,35,3C,69,0F,EA,41,4E,21,56,02,14), + wwH16(71,80,C6,B7,26,62,9B,41,27,67,F7,9B,98,87,F5,7D), + wwH16(3C,88,B2,37,B6,2D,83,CD,95,C5,A7,E8,AF,24,E2,57), + wwH16(5B,01,36,A2,F3,1F,2E,A4,FB,F5,A6,AD,7F,2D,C2,F3), + wwH16(3C,5E,19,1E,EF,BB,2C,1C,F6,40,FB,F6,8B,E8,87,DD), + wwH16(CE,6B,35,22,6D,9F,30,7F,47,7F,D2,1A,73,41,5D,5D), + wwH16(E6,84,48,3D,A5,46,AF,D5,1E,0F,42,84,DF,79,4B,44), + wwH16(22,46,7E,9A,7D,36,90,B4,4A,BF,69,98,7D,F7,34,41), + wwH16(4C,1E,97,DB,F9,9A,84,B8,45,82,B2,44,E5,C9,7D,96), + wwH16(8F,EE,B4,D4,70,3E,2A,A4,65,13,90,17,35,6C,82,E2), + wwH16(52,2B,12,AF,C1,04,CE,A5,CE,86,A5,2D,26,BB,22,18), + wwH16(F0,1B,FC,F7,E0,B2,FD,4C,4F,19,B3,F1,1F,88,1C,7B), + wwH16(35,67,67,DC,87,66,41,35,19,C9,C0,D3,91,61,EF,15), + wwH16(FC,D2,93,41,44,3D,52,5E,39,0A,5B,B3,8E,D7,12,1C), + wwH16(A5,07,4A,3B,F3,5E,53,7A,8D,6C,F2,1D,F5,12,FF,E7), + wwH16(51,3B,F6,E2,E9,DC,79,C7,6A,DA,5A,2B,BD,D9,A6,CA), + wwH16(C0,89,19,71,53,BC,CD,91,AA,3E,5A,D9,9D,3F,74,A8), + wwH16(52,2F,96,A0,7B,E5,7F,DF,07,64,46,55,C7,C1,E3,27), + wwH16(DD,FA,35,8B,30,C8,62,06,BC,2A,60,E9,82,66,C8,F4), + wwH16(DE,0D,80,C1,51,6D,90,AC,75,D9,65,3E,67,81,2F,83), + wwH16(36,C7,CF,EF,DF,7F,A8,4B,36,E7,F1,30,E8,C6,61,67), + wwH16(98,06,34,0F,35,C2,8F,98,1C,4C,76,DE,2A,8D,B0,A0), + wwH16(7F,88,3E,47,83,12,C5,09,AF,74,43,0D,D6,F2,B8,13), + wwH16(87,9F,D8,77,4E,78,29,25,45,6F,E6,FE,3E,FF,0E,C8), + wwH16(FD,1E,BA,2A,E1,7F,F5,AA,EF,F3,F5,0C,8D,25,08,C6), + wwH16(8A,7B,71,3C,65,D9,B4,7F,81,C3,67,B3,01,77,1A,76), + wwH16(46,A4,AF,66,75,17,9C,93,80,18,49,35,9D,77,2F,5F), + wwH16(40,A1,AD,0E,C6,3B,76,1D,44,4A,54,85,F2,69,23,96), + wwH16(1E,18,2E,C0,8A,55,AA,56,E3,E0,C2,8C,46,B5,30,08), + wwH16(69,99,C2,B7,22,D3,F2,72,2F,4E,FA,6F,A0,B8,6A,5A), + wwH16(BB,AC,66,6F,13,F7,8F,E2,91,3C,CF,6C,5E,14,0F,79), + wwH16(F0,FE,3E,51,F8,1E,33,13,98,99,31,77,FF,1F,B3,54), + wwH16(30,B7,BC,9D,2C,59,BC,E5,D6,CC,F9,73,2B,90,8E,02), + wwH16(19,3D,85,40,7D,0F,48,11,0E,62,D2,FF,43,FB,5B,72), + wwH16(C2,AF,ED,4C,63,75,51,87,54,F6,E8,28,E1,B0,A9,CC), + wwH16(0A,96,2E,46,73,20,77,38,FC,67,4C,57,77,C9,B3,6C), + wwH16(30,04,A4,BA,0E,5B,1B,8F,7D,22,31,E0,3D,85,54,5B), + wwH16(14,68,36,44,D4,FD,BE,87,7F,57,96,AA,97,2D,2C,98), + wwH16(92,DA,7A,07,69,F1,BE,A1,BD,E5,50,95,5F,E5,40,95), + wwH16(58,E7,B5,A9,59,29,DF,2C,E5,73,C2,B5,F8,52,DD,19), + wwH16(71,0B,61,10,99,4D,99,B6,A0,9C,04,32,00,2F,4A,A1), + wwH16(55,E3,B6,C1,45,32,A3,58,C8,71,CF,29,BA,6E,44,9E), + wwH16(49,4B,C7,EC,25,5F,D2,14,3E,17,20,ED,F0,CE,C3,D8), + wwH16(49,F9,97,EE,AC,4D,32,DA,6C,A9,CD,62,1E,8E,10,21), + wwH16(39,6C,2F,0F,3A,07,5C,8A,E4,1C,8E,B7,61,F5,16,F0), + wwH16(24,FE,40,9C,26,3C,11,52,79,EB,00,FB,21,8A,63,98), + wwH16(45,20,FA,AC,4C,E3,3A,96,D5,13,A6,A8,EF,87,8D,2E), + wwH16(E9,A8,27,C5,FF,DF,BC,5B,6A,55,1A,B5,60,82,E6,C8), + wwH16(3C,5A,B2,77,2D,EF,8F,A7,74,53,F1,AC,8A,58,4C,73), + wwH16(D2,D5,D9,D2,4A,55,A2,20,86,93,EE,B6,E1,9B,58,12), + wwH16(E3,B0,C6,2C,A6,52,2F,EB,DB,E6,49,4B,EF,71,7A,8C), + wwH16(88,83,BC,08,39,8B,C7,60,10,C0,1E,38,C5,1C,A8,D5), + wwH16(47,8D,36,94,52,B5,83,34,BD,6B,04,AE,78,0C,4A,3F), + wwH16(B4,F3,EC,2C,81,0C,14,D7,64,56,EB,9F,32,26,5E,9B), + wwH16(37,BA,A2,14,97,FA,CE,C9,DF,42,0B,07,26,CF,E5,14), + wwH16(37,83,F7,4C,39,B7,C6,61,C5,1C,61,9A,18,07,6A,F9), + wwH16(BB,D9,60,5E,C8,85,0E,A3,13,6B,40,29,A8,CF,31,4B), + wwH16(11,9C,CA,64,81,EE,D0,84,41,84,CB,13,7E,26,91,E7), + wwH16(6C,03,EB,F6,4B,01,6D,95,7F,D9,B3,EF,8B,6C,67,18), + wwH16(1D,7D,24,D8,D3,CD,F8,46,E3,84,C4,87,D7,06,6E,89), + wwH16(16,9E,2A,1C,BC,23,43,F3,A4,75,7D,DE,B0,CE,8D,54), + wwH16(25,FC,98,57,DF,79,EC,54,3D,F7,9C,99,59,11,62,88), + wwH16(CA,5B,C8,D0,32,46,E4,08,BB,27,B1,65,53,D1,C8,47), + wwH16(AC,11,5F,FB,B4,35,79,EB,2E,07,CF,A8,EF,4E,56,68), + wwH16(70,F2,ED,60,8D,A8,1B,14,B5,72,8B,06,B9,A3,FD,57), + wwH16(64,BB,A6,12,48,28,63,50,6E,89,FF,70,CC,DC,03,97), + wwH16(45,2D,12,D6,8F,92,55,1B,DA,66,66,27,9F,AC,FB,B6), + wwH16(FE,DB,82,51,2C,6B,51,D8,17,1F,5D,F7,0D,1B,C7,AE), + wwH16(5F,00,A5,6D,E7,E7,18,CC,21,38,58,14,E2,07,89,11), + wwH16(10,87,AA,70,87,9C,C2,AB,E8,2E,B8,8C,89,08,EF,EA), + wwH16(0C,7B,B0,67,7E,D0,BB,6C,DD,52,8A,40,7F,8B,F5,9E), + wwH16(2E,7D,B7,97,8A,14,C8,BA,05,7A,E1,5F,AA,99,60,E3), + wwH16(F2,36,B4,13,B6,68,D2,C0,B3,E6,8B,B4,A9,DF,12,BB), + wwH16(AD,86,09,C2,C4,D8,F3,36,AE,2C,EF,69,49,74,2F,13), + wwH16(78,F2,07,E2,31,EA,47,E2,02,F8,0C,EC,E6,AF,14,C2), + wwH16(AF,A2,C8,43,F1,B8,29,C5,DF,D4,9A,34,3B,9E,5C,CC), + wwH16(4C,8D,F8,3E,84,63,DA,7B,23,CF,CF,72,E6,4D,D1,40), + wwH16(DA,55,05,E1,BB,2D,54,F3,E7,AB,74,71,17,74,2C,81), + wwH16(FB,9A,A9,8B,C1,6E,02,B0,98,1A,58,D2,E4,38,66,B2), + wwH16(FA,6B,83,41,24,15,2E,48,79,23,52,81,1F,EC,61,CF), + wwH16(0F,C0,EE,A5,F5,54,6E,4F,7F,37,34,18,A8,04,19,56), + wwH16(42,5F,4B,BF,11,D3,D3,8D,99,1C,F3,EF,66,1F,5A,CE), + wwH16(DF,4C,A6,95,4D,78,1E,E1,6B,22,0F,F3,EA,28,86,39), + wwH16(60,26,CA,DE,31,6A,A4,04,F3,00,06,04,17,67,40,66), + wwH16(55,18,FE,C0,19,FE,EB,F1,B4,1A,CC,3D,1F,08,AE,B8), + wwH16(C8,50,13,0E,52,96,AD,3C,40,47,9D,ED,E7,B0,99,81), + wwH16(A5,62,F9,BB,9E,E5,65,0E,2B,E9,80,08,A8,70,82,FE), + wwH16(80,C8,9C,AA,DA,41,D4,2A,94,9B,58,44,04,C3,F3,ED), + wwH16(CF,EB,41,47,F1,50,A8,8A,F7,80,3E,9C,9D,15,5C,90), + wwH16(70,1D,7D,A8,DE,12,70,CE,6C,AD,F6,E6,D8,E0,D4,4D), + wwH16(49,7A,40,03,1D,B6,E7,95,42,1C,A0,CD,1D,8D,DF,5E), + wwH16(2D,B0,6F,ED,DB,2B,62,4D,79,0E,A5,B0,7E,33,54,77), + wwH16(BD,12,4C,55,B2,F3,FB,E2,64,D4,60,16,F7,5B,61,41), + wwH16(89,51,AB,95,7B,CB,40,43,42,D1,27,71,82,2E,59,37), + wwH16(5A,5D,66,16,DF,7E,B9,03,9E,32,9D,3C,D7,52,21,5B), + wwH16(58,AF,91,58,52,6B,22,9A,80,95,5B,90,59,5B,10,A9), + wwH16(C7,B3,1B,0F,B1,83,56,EE,CB,70,89,C9,8A,34,18,25), + wwH16(E2,0A,4F,2A,B3,CB,70,C6,6D,C4,3D,D5,C3,F4,98,A6), + wwH16(83,D1,44,46,2B,8E,9B,98,6D,A8,AE,D0,E0,C8,30,18), + wwH16(AD,8E,0E,0C,7C,8A,54,B8,25,DE,AC,35,64,64,FD,3D), + wwH16(45,D6,7B,C1,8A,45,C9,D4,CB,C0,17,FC,40,3F,7B,17), + wwH16(B2,86,4E,A3,9E,A7,19,9B,E0,9A,E6,25,04,A6,BC,1D), + wwH16(21,39,33,9E,44,17,DB,7D,61,48,FE,A2,1C,86,9A,53), + wwH16(B8,E5,39,2E,2E,F3,B9,B9,7C,C7,23,69,52,C7,5B,95), + wwH16(D0,76,C9,16,6A,C9,B2,B0,46,F6,89,8E,E0,B0,1A,BB), + wwH16(38,9A,14,64,08,2E,43,52,48,0A,F8,18,98,2B,71,80), + wwH16(19,C2,FE,06,69,1D,EC,95,B8,42,3F,10,F4,99,F3,21), + wwH16(50,52,C1,F1,DD,91,CB,FA,37,2F,A9,1B,90,F6,06,CF), + wwH16(49,BA,D3,B0,07,CA,53,98,3F,48,DB,5C,72,C9,39,EC), + wwH16(BD,5F,B0,48,67,E1,F0,DC,8A,CA,A3,B2,7B,52,94,EB), + wwH16(0F,65,93,A7,07,4B,0D,5D,04,E3,E6,95,6A,CF,55,D0), + wwH16(3B,55,1C,F4,4F,73,FE,98,C0,31,05,2A,85,96,7B,4B), + wwH16(89,4C,26,EC,3E,37,34,6B,28,2B,50,10,62,A3,5A,3C), + wwH16(2C,31,E1,5D,10,13,D9,9F,D7,46,F4,F2,71,06,62,2F), + wwH16(76,6A,C1,0B,98,76,1A,AF,65,E9,01,A1,13,2C,7E,C4), + wwH16(B3,1C,2C,6B,A5,8C,DE,70,86,43,2E,2B,49,56,54,E7), + wwH16(F8,62,03,FC,98,20,36,93,A0,05,1C,21,D9,A2,AC,AC), + wwH16(B6,C2,18,8F,31,8B,DB,97,B0,CB,B5,29,36,FE,4A,E0), + wwH16(D4,02,FA,2F,3E,6B,AE,47,EC,C3,6A,76,D5,C2,C9,B6), + wwH16(4D,0F,C1,96,FB,A8,5D,D2,29,AB,B8,6D,B6,2D,97,57), + wwH16(C8,79,5C,11,62,31,E2,E3,3B,F1,12,D8,00,ED,00,0C), + wwH16(EE,9D,60,09,78,21,67,00,25,A9,8B,06,FD,CC,69,F5), + wwH16(35,82,B1,86,DA,1B,AB,57,2C,CC,0E,C0,2C,0E,C1,F1), + wwH16(D0,67,1F,AE,05,72,5A,A2,1F,32,C4,97,71,A7,C7,DD), + wwH16(EB,46,15,5F,93,87,7F,28,E3,76,75,84,96,14,34,29), + wwH16(D3,C8,54,DA,9D,D7,8A,DE,F7,C3,64,D1,D2,2E,94,82), + wwH16(24,C6,E2,58,0E,CF,8C,53,43,7B,5E,12,04,8B,EA,DD), + wwH16(76,5A,11,F4,E5,99,1A,CC,57,A6,89,83,A6,A5,22,F8), + wwH16(85,B1,66,92,01,5B,48,76,B3,34,01,B1,D9,6D,E5,4A), + wwH16(AF,E4,63,20,86,66,DE,B5,E4,21,FB,8E,4C,38,42,09), + wwH16(23,E4,08,3C,98,43,BA,13,83,8B,94,EA,51,68,C1,3B), + wwH16(6B,6B,B4,06,2A,46,D3,51,55,19,1D,F8,03,48,DF,6A), + wwH16(76,40,1B,71,89,74,72,07,B1,59,F5,D2,D0,2E,89,D7), + wwH16(02,3F,97,25,AA,EE,60,6A,D2,5E,95,AD,EC,04,A3,2D), + wwH16(E4,00,72,C5,70,91,BA,46,EA,01,DD,6D,1C,8F,41,F1), + wwH16(88,82,14,63,6D,8F,8E,D3,56,B3,1B,61,E4,93,D4,BD), + wwH16(B9,6D,B1,E8,E2,E2,70,02,6B,10,82,50,6E,86,53,CE), + wwH16(05,31,07,B6,F7,56,46,EE,A0,B4,65,01,8A,9C,61,E8), + wwH16(19,21,4A,A1,08,30,0C,BB,E5,47,F7,D3,91,43,1B,F0), + wwH16(12,1B,A2,39,E2,FD,47,FF,A9,34,63,1E,CC,AE,7C,3B), + wwH16(AA,EF,4E,39,39,93,96,FC,37,9A,38,AA,4E,28,B5,06), + wwH16(B2,D9,14,0C,3F,0D,64,DE,E4,D8,EA,03,BD,B1,9F,F2), + wwH16(44,AE,DF,31,2E,45,7C,34,A3,2C,D7,E7,2C,8A,32,03), + wwH16(CF,D2,5B,A0,34,2E,90,61,1E,A0,FD,25,00,6E,AA,31), + wwH16(ED,02,50,57,19,07,3A,13,A5,74,B6,3C,5F,93,D3,F3), + wwH16(12,24,B1,0F,B6,93,93,E9,A1,B4,21,D2,6F,03,42,36), + wwH16(3A,CB,5D,66,C7,8A,A2,F2,A9,D0,A7,6B,7D,8D,6B,90), + wwH16(8A,2F,EE,55,B5,D2,4F,8E,E3,B3,8D,1A,9A,8D,74,4B), + wwH16(16,86,91,8E,78,54,19,C6,4A,51,8B,B8,EB,C5,8D,5D), + wwH16(34,1A,A8,A4,D1,95,88,38,73,7F,BE,51,EC,7E,70,98), + wwH16(9F,95,1F,24,F6,03,1F,A6,EE,BE,50,20,29,03,89,15), + wwH16(27,37,84,FE,07,9B,CB,F8,20,45,67,8C,47,64,13,63), + wwH16(C0,1B,F6,45,E7,97,51,91,39,3A,3C,1D,73,E4,CC,43), + wwH16(8F,4A,E1,27,DB,A7,CF,3A,BF,AD,A8,6C,6F,AD,C1,4E), + wwH16(74,5E,32,E8,74,A6,55,B2,6E,A6,03,34,52,84,C9,F9), + wwH16(4F,F1,3A,02,7E,CB,9F,C8,DA,F3,7B,ED,DD,83,B5,47), + wwH16(E2,C9,7F,75,32,DC,BA,49,BF,73,2D,65,C6,29,6B,73), + wwH16(A8,E8,33,9D,7C,C1,F9,08,F5,56,68,D2,99,76,93,D8), + wwH16(29,38,51,6D,BC,08,95,14,00,D6,A5,86,48,D0,0D,79), + wwH16(78,A3,F8,B2,28,1C,CD,59,42,72,04,1D,DA,AC,EB,CF), + wwH16(94,52,45,5B,44,86,10,C4,EF,0E,94,34,22,1E,9A,17), + wwH16(A2,49,F5,FE,99,AF,48,23,B0,B5,FB,7F,67,DF,B0,98), + wwH16(06,BA,EB,A9,FE,6C,90,4E,1F,B3,51,B1,91,C2,5C,FA), + wwH16(62,21,2E,D5,8A,BB,B3,87,F2,6D,E6,30,C0,06,5B,A3), + wwH16(68,F7,8F,29,0D,C0,94,79,0F,04,6B,B3,66,EC,DA,2E), + wwH16(F0,F3,04,7B,21,0F,CF,D5,48,EF,A7,DF,C5,CE,0E,FB), + wwH16(DF,79,27,E9,BE,04,EA,80,87,64,4E,28,B5,2A,45,0C), + wwH16(BE,0F,84,3B,16,01,B5,07,9A,F9,19,05,5F,7F,C1,6F), + wwH16(ED,F8,D9,DD,56,B4,AE,ED,FF,99,A5,66,9A,C5,40,46), + wwH16(B5,28,00,9F,34,25,E2,D6,E1,12,48,86,F6,19,4B,1A), + wwH16(0F,A6,5E,2E,08,CB,AB,54,82,36,3E,DD,21,31,6D,70), + wwH16(BD,EF,D6,44,B0,A4,12,6C,53,A6,0D,B7,9B,FF,AF,99), + wwH16(74,D0,90,25,68,07,4D,C8,05,D2,2E,E6,2C,6E,3B,43), + wwH16(07,3F,67,38,BB,9F,4B,14,46,FC,E9,D6,72,DD,13,12), + wwH16(2E,EB,A2,E3,77,97,85,CE,4C,DD,17,51,42,A4,01,53), + wwH16(B1,48,F9,63,EB,46,E2,56,D4,C4,03,B8,23,A6,94,F8), + wwH16(FC,5A,B3,BE,FB,79,97,4C,64,04,69,84,29,1B,70,7F), + wwH16(A3,23,00,F3,25,79,D6,C6,FA,C2,8C,39,06,64,6D,C5), + wwH16(79,47,03,8D,48,49,AE,DF,17,5C,8A,B7,92,18,7D,76), + wwH16(B6,A5,40,F9,6F,23,96,FD,B0,01,AE,0F,47,28,FA,5F), + wwH16(D4,33,23,16,B9,20,52,EF,54,8C,9F,39,5B,A7,EA,A1), + wwH16(99,92,D8,46,42,4A,FD,90,0C,67,63,DA,1D,83,1C,36), + wwH16(6E,C2,F5,BB,7B,C4,33,88,F8,75,CD,7A,DB,B2,88,B3), + wwH16(07,6D,A9,7B,7C,97,17,33,1D,E9,53,2B,03,6C,82,14), + wwH16(F1,A3,4E,6F,2C,08,D5,E5,A4,43,18,8A,13,56,41,0A), + wwH16(0B,9B,40,4D,AD,02,BC,FE,BD,BF,82,FA,67,D5,02,59), + wwH16(43,1C,C6,3E,66,FE,AD,3B,AC,7B,2A,C9,B5,3D,F2,17), + wwH16(2D,C7,41,FC,5E,61,38,BD,DC,4D,F7,41,87,49,47,48), + wwH16(30,84,C3,4B,D6,1F,BF,A9,AD,6D,9B,8E,C6,AB,42,B7), + wwH16(0D,DA,29,6D,C7,E7,21,52,5A,89,48,F0,C9,7C,1C,B4), + wwH16(40,23,F4,8E,B4,01,4C,4F,CD,7F,02,E7,74,05,F5,0F), + wwH16(A0,02,23,BA,E6,DB,AD,9C,E6,74,F1,FF,F2,AE,0D,CE), + wwH16(F5,6F,B4,09,AB,81,76,BA,0C,82,DB,AD,AC,63,64,CD), + wwH16(BB,7D,D3,40,EF,E8,37,02,DC,A2,FC,87,92,0D,73,A0), + wwH16(80,EE,7E,1C,CD,57,23,23,15,2F,54,0A,94,1E,12,F5), + wwH16(31,48,55,7E,7C,47,46,98,DA,EC,2E,C0,94,10,6C,E6), + wwH16(51,02,F5,73,81,AF,3C,E1,B5,BB,F9,EA,2D,78,F1,FD), + wwH16(99,F9,53,18,09,13,C5,2C,4E,FB,D6,E2,7E,E8,CD,F2), + wwH16(3E,04,3C,84,9B,72,0A,63,5E,3A,31,C6,B9,44,0E,73), + wwH16(56,A4,AA,49,BA,EA,4E,50,4C,09,5C,04,AF,C5,81,BD), + wwH16(CD,2F,1D,A5,90,63,78,1C,D2,11,82,BE,E4,93,FA,D8), + wwH16(20,B6,85,51,B1,41,57,AD,3D,B3,E9,85,9B,A4,8B,36), + wwH16(78,F4,BB,94,99,7E,23,CF,E3,A3,08,C9,2C,D4,B3,DA), + wwH16(31,F0,5A,E9,69,53,6A,10,E9,05,DF,BE,6B,58,55,22), + wwH16(9C,9D,CC,BE,51,50,65,EE,13,D7,B4,8F,4E,01,7C,B0), + wwH16(99,EA,10,D6,A1,17,C5,39,DA,96,EA,13,29,6A,6B,73), + wwH16(BF,08,79,BD,37,CA,8F,EC,50,45,19,C6,EC,E2,62,7F), + wwH16(AC,1D,10,BB,88,FE,43,FA,93,69,85,36,1B,7F,81,17), + wwH16(08,2D,92,9C,DC,5E,DD,DD,DE,EF,C5,7E,B2,53,67,C0), + wwH16(07,2D,45,44,B7,69,DC,60,BA,D6,F8,84,94,7E,8F,B6), + wwH16(E4,6D,FC,F7,6A,B1,D4,73,D0,9F,35,99,3C,83,B4,1E), + wwH16(DB,08,A3,B9,3B,AC,A9,5A,57,FE,1E,61,51,E5,EB,57), + wwH16(FA,7C,C7,A9,03,F2,16,A2,40,94,43,89,C6,F4,BB,89), + wwH16(77,C7,43,48,EB,5F,97,4D,21,68,C0,C1,33,A6,AE,78), + wwH16(5D,A6,10,9A,BF,18,4B,8D,A0,C4,2B,B6,35,62,AC,E0), + wwH16(1E,E3,B1,19,01,55,89,5D,C2,29,F3,42,CA,2D,99,BA), + wwH16(99,E9,89,1B,C2,42,AD,64,98,74,BB,77,CB,9A,17,66), + wwH16(92,DF,A6,41,9E,67,1C,2D,3A,40,29,A7,E1,5E,7C,F9), + wwH16(29,AE,86,46,90,59,DD,4F,D6,5C,50,71,B5,2C,F9,FB), + wwH16(3F,81,B2,BF,92,DD,10,EC,4A,4C,2B,DB,E0,D0,C3,0E), + wwH16(10,64,20,98,23,F0,5D,14,C2,CE,C3,10,85,03,81,94), + wwH16(03,3F,0A,27,6E,1F,CC,6A,9C,8D,DC,C5,D8,4F,28,97), + wwH16(04,7F,F3,88,18,A6,AD,2E,FF,F7,C7,8D,54,34,12,1E), + wwH16(2D,2D,44,6C,D1,5A,7F,17,A3,42,34,1B,F6,74,FF,16), + wwH16(30,E5,90,48,9E,4A,C8,56,3A,12,0A,DF,11,AB,DD,3C), + wwH16(91,4E,C3,59,01,D9,0E,CB,0E,C4,D9,27,07,11,FF,81), + wwH16(2B,D2,F6,20,9C,E0,61,34,7A,F8,53,5F,5E,5F,6D,9A), + wwH16(27,4A,94,83,8E,AB,5D,B8,D2,8C,F0,80,02,ED,8D,DA), + wwH16(F5,93,32,EC,BB,7D,71,23,2F,3E,98,44,82,0F,CA,D6), + wwH16(9E,C1,A5,33,28,F2,10,AB,59,A4,B1,DF,86,80,B2,4E), + wwH16(6C,EE,CE,0D,9E,58,B9,1D,D9,3E,D9,09,7F,E1,0A,C8), + wwH16(02,32,FF,E0,93,2B,46,F5,8C,2F,A3,16,6E,99,46,26), + wwH16(AF,4F,FF,4E,13,02,87,DD,03,D6,33,D7,1D,7D,12,7E), + wwH16(62,C7,33,78,D1,6E,87,44,20,1E,A4,EC,6F,11,58,EF), + wwH16(14,28,D5,8B,3E,77,AF,2A,62,A8,D8,29,B0,AB,A0,80), + wwH16(5B,9A,9B,E0,F7,70,B6,1C,BC,6C,30,12,CB,CC,A6,27), + wwH16(F9,99,3D,1F,26,44,67,3B,A5,20,C4,C0,4B,F9,30,E0), + wwH16(75,97,CF,71,7B,D6,26,DE,8F,9E,40,4C,AC,94,71,FC), + wwH16(34,3B,CA,CC,E5,C5,A7,2B,02,1F,5A,04,0B,8B,A9,6B), + wwH16(FC,A9,24,7B,D3,AA,E0,D1,4E,DF,38,E8,F5,B0,EF,73), + wwH16(CD,DE,12,2A,39,0C,AA,3C,A1,CF,F4,1A,EF,89,A1,09), + wwH16(21,2A,66,0B,92,8B,DF,69,28,36,2A,EA,E1,F1,E8,ED), + wwH16(95,87,A8,8E,0C,BA,7C,C4,F0,A2,22,02,7B,D8,18,E3), + wwH16(C7,94,28,50,D5,84,49,66,85,E8,6A,22,AB,E9,9E,1C), + wwH16(C2,BB,12,BD,25,E4,D7,74,40,BF,03,48,CF,79,83,BE), + wwH16(A3,3E,44,67,A1,39,BC,6A,7D,F7,A1,31,4A,C2,98,08), + wwH16(0C,61,48,89,5F,2A,62,E4,B4,DB,72,2E,82,8D,23,6F), + wwH16(6F,32,00,9C,73,EB,F0,D7,56,B0,3F,73,87,22,FC,CC), + wwH16(96,5C,8B,46,F2,4A,AD,AB,83,59,B4,D6,B3,36,20,76), + wwH16(D0,48,3E,BC,58,E2,BB,81,0D,CE,0C,9A,A3,FD,D1,FA), + wwH16(22,81,58,44,E0,E4,E6,D5,13,4A,04,68,55,A8,2B,10), + wwH16(F3,80,86,D0,2D,50,FD,C4,AB,BD,5D,A1,AE,F6,B7,A2), + wwH16(4B,D8,30,A4,01,66,D4,28,9D,FE,C6,98,DE,3D,69,0F), + wwH16(C9,2F,EE,3A,2D,E0,7F,F5,27,0F,E3,A6,1B,DC,09,27), + wwH16(31,CE,E2,B3,CE,F3,36,57,1F,32,C6,7B,3B,07,0E,B8), + wwH16(E1,1C,F6,19,E9,31,E8,A8,0B,40,84,E6,ED,2F,A3,0A), + wwH16(E7,66,AF,AC,87,01,32,3B,BA,82,5E,E8,50,1E,F5,DB), + wwH16(4A,E4,53,B1,B8,D7,E2,2D,52,11,DF,4D,D8,52,74,2C), + wwH16(84,A0,CC,4B,06,09,4B,5C,53,B7,B6,9B,22,16,91,A4), + wwH16(BE,E9,7F,B6,B9,B3,97,76,5E,AD,8B,1F,0D,F5,DD,B9), + wwH16(8E,2D,42,4C,72,87,A3,9E,D4,F4,1F,D9,D7,14,01,6D), + wwH16(FE,3B,3E,8C,21,2E,71,87,D8,34,FF,75,3D,FC,D6,B4), + wwH16(BA,63,A2,B3,BE,3D,2B,F5,A5,E7,72,9E,FF,72,60,21), + wwH16(90,C7,A9,14,6D,03,0F,35,80,10,F8,49,BC,41,01,E3), + wwH16(7E,5D,2D,B1,F7,03,87,A1,2E,C2,C7,53,B6,47,56,35), + wwH16(31,79,DC,F9,16,21,42,1E,C6,12,58,7B,81,72,29,11), + wwH16(9D,90,51,5F,A3,6F,F7,D6,56,57,D3,F4,FA,9C,24,E7), + wwH16(15,97,9E,38,02,F5,1E,D7,AC,28,1B,CF,56,CA,F0,3F), + wwH16(37,F4,1F,D1,A0,92,62,1A,CE,28,CA,2A,B2,34,3D,EC), + wwH16(3A,67,E0,82,EB,60,CC,98,BD,FB,9D,8C,03,F7,32,DA), + wwH16(44,A7,42,EF,E6,8C,6C,3F,CC,E0,BE,14,09,DE,22,0A), + wwH16(70,A8,68,3B,4F,FB,F8,A3,BF,7D,A0,18,B0,7E,59,DB), + wwH16(A4,6F,CC,57,03,ED,BF,AA,8D,1F,BA,E5,8A,E6,34,ED), + wwH16(52,AD,A4,2E,EE,42,DF,5E,01,C7,B5,24,B1,03,2E,87), + wwH16(83,9D,43,8A,81,A7,BD,21,F1,A0,4D,A9,D2,73,50,D8), + wwH16(26,97,96,FF,8E,70,E3,72,9D,25,7A,94,A4,A0,9A,0D), + wwH16(31,7E,60,76,4D,EB,89,C6,B8,2B,19,91,D5,B2,B3,7E), + wwH16(72,CF,23,CF,B6,6A,CF,39,58,08,FA,52,E9,6E,2E,15), + wwH16(FB,CA,1D,80,71,CC,5E,45,6E,60,A3,37,1E,C2,29,15), + wwH16(1D,DC,21,73,1E,71,F3,AC,7D,42,7C,85,AC,13,33,59), + wwH16(6F,29,36,9F,65,EA,5D,B7,16,63,6D,22,A9,0C,44,FC), + wwH16(4F,2D,DC,85,63,04,4C,97,3A,ED,56,B8,49,FB,A3,72), + wwH16(A7,CE,0F,77,A1,55,F6,6A,79,63,E3,25,7F,AB,62,BF), + wwH16(2B,5F,36,37,C7,CA,E4,9C,53,D7,05,15,53,48,68,22), + wwH16(DC,C6,09,73,64,DD,58,2F,4F,1D,A8,E6,9E,C3,C1,7E), + wwH16(9C,56,3B,6D,6D,01,9F,A0,8F,44,3E,21,74,6A,BC,06), + wwH16(AA,00,75,E7,1D,FA,E6,7B,45,51,3E,B8,C1,E1,E1,52), + wwH16(A7,FA,2E,AE,D4,5D,22,96,EB,F6,C8,13,5E,1A,C6,50), + wwH16(33,B1,81,4F,E3,F8,22,9E,AA,AF,42,3E,07,E7,7C,D8), + wwH16(BD,65,0C,31,E5,08,A4,49,4E,BD,6C,6D,BB,FD,A5,9C), + wwH16(F5,DC,22,94,B0,13,89,C0,DD,B9,E2,90,02,41,8E,DB), + wwH16(F6,D8,91,A2,EF,15,52,8D,44,F7,8C,49,3B,06,17,D7), + wwH16(81,9E,48,97,9C,9E,6B,A7,DA,66,DF,BF,37,6B,2F,F7), + wwH16(45,A1,8E,A4,BF,CD,A6,9E,D3,A7,87,BB,82,AB,69,63), + wwH16(47,8E,84,CD,4B,7F,1E,60,B6,D4,17,79,35,E0,23,C9), + wwH16(95,2C,6A,F7,7F,A0,77,26,69,4E,CD,CD,D4,06,31,EF), + wwH16(BB,B6,C7,D4,6C,59,1F,C5,D5,09,B8,6C,ED,72,F0,30), + wwH16(4B,AD,76,52,42,A6,BC,F3,37,7E,3C,E4,A0,22,9B,E5), + wwH16(B0,99,88,85,50,82,3F,91,77,ED,DB,E2,35,9E,39,3F), + wwH16(F4,EE,C2,F6,80,0A,AC,A4,93,40,2A,01,D0,A8,B6,60), + wwH16(C3,BB,1F,0C,64,DB,8D,77,DD,50,01,60,96,C6,6F,28), + wwH16(71,BA,7C,5A,DC,D0,36,3D,CF,95,ED,D3,B6,56,0D,6B), + wwH16(D1,63,A0,73,42,A7,4C,12,64,F2,43,83,3D,BC,E1,C4), + wwH16(3B,E2,60,34,BB,F1,A7,48,44,94,35,13,98,88,E9,7D), + wwH16(65,83,FA,33,42,FD,37,9E,BD,96,6B,53,E1,57,5D,45), + wwH16(77,C7,93,AF,74,BC,41,05,1E,01,6F,34,47,2D,41,E9), + wwH16(C9,EA,03,A3,54,4A,D2,C6,8E,78,68,15,9C,29,30,4B), + wwH16(86,C2,A0,94,2C,A9,8E,DA,3C,36,43,7A,71,14,E8,1E), + wwH16(06,42,FE,A8,42,D0,F2,9B,74,FB,B7,35,BA,77,02,F9), + wwH16(C9,B4,6D,DA,5D,1F,AF,9F,58,DC,36,6D,61,51,F2,B6), + wwH16(9B,E8,F3,49,6E,13,84,56,BA,5B,CA,F7,65,72,92,25), + wwH16(B8,96,57,B0,9C,11,E6,BF,50,AB,23,46,22,17,8C,AB), + wwH16(AB,1A,82,4D,1C,77,C6,01,6C,E9,F9,C4,4C,0D,00,7E), + wwH16(38,8A,37,C8,8B,D0,3F,E8,FA,61,AA,4C,16,0F,DE,EF), + wwH16(15,3D,F2,13,6E,43,E8,DF,02,B5,F9,3C,0A,A2,FB,DF), + wwH16(53,85,6F,FB,1A,96,0E,2C,18,93,34,17,31,FA,C2,98), + wwH16(20,43,75,92,F3,57,0B,C9,FD,59,2D,95,23,05,C9,4E), + wwH16(3F,C5,4F,24,62,F9,96,C1,A0,EA,B2,CE,0A,8B,5B,05), + wwH16(4A,48,9E,1D,CC,BB,FC,3D,8F,CC,4B,B2,8D,88,4D,4C), + wwH16(38,28,13,25,6F,48,A1,26,AA,CD,50,59,07,FF,C4,46), + wwH16(09,B5,A6,F5,D9,96,A7,8B,15,9D,5A,F7,F3,FA,A7,B3), + wwH16(09,D4,B5,CB,F2,6B,C8,2E,8D,18,90,27,A5,9F,7B,99), + wwH16(4A,9F,A4,92,72,6F,BA,F3,6C,F7,7E,B1,EA,78,76,CF), + wwH16(0B,0C,AF,96,37,8A,2F,82,32,A8,B3,0C,83,E0,70,E7), + wwH16(5C,DE,3F,1F,8A,4D,6F,18,80,F0,62,56,35,87,85,B5), + wwH16(EE,BD,57,58,F5,CB,BB,55,87,5A,00,B9,34,85,C8,A5), + wwH16(DF,CF,D0,BC,53,61,13,CC,C9,3D,BA,14,70,BB,3B,B8), + wwH16(A8,9F,4B,79,4D,0D,91,89,FA,18,FE,CC,E3,15,B0,85), + wwH16(7B,09,4C,F1,3D,8C,B0,09,07,48,90,63,D2,0F,42,7D), + wwH16(5D,B1,BE,39,BB,31,CC,60,7F,08,B6,9E,CD,5C,9F,70), + wwH16(E8,97,85,C4,A1,42,FC,6D,6B,CF,4E,5B,93,23,1E,D2), + wwH16(B5,FD,66,8B,92,69,43,FA,45,86,1F,92,4C,5E,0B,AE), + wwH16(D6,82,FB,06,79,92,B5,76,36,EE,B8,4C,DB,E0,CB,05), + wwH16(67,39,C0,CE,CA,BD,24,CA,B2,D3,94,70,1C,4A,34,0F), + wwH16(D5,0D,D0,DE,DA,5D,E5,3B,54,01,F5,99,FE,9C,1C,FD), + wwH16(E8,4F,18,D5,57,14,B0,EE,5C,A7,1E,FC,34,D0,B1,49), + wwH16(06,11,F1,14,33,22,FB,45,33,97,55,40,7A,85,70,D8), + wwH16(01,DD,89,9D,25,74,2C,23,CF,D8,1D,FA,E6,D4,8A,20), + wwH16(FD,57,08,50,3C,87,93,F1,11,0E,27,93,5B,9B,94,54), + wwH16(9F,29,58,E6,C4,CD,53,C0,F3,82,19,6B,02,65,6F,4A), + wwH16(CF,47,40,78,19,EB,B5,BD,73,CA,17,24,F0,E3,2E,A7), + wwH16(F4,0D,12,1F,3B,C0,22,24,C7,06,B4,B6,B3,0D,3A,20), + wwH16(45,79,91,A2,5F,89,95,8E,4F,EC,0D,61,12,C6,8F,D0), + wwH16(6A,F7,65,F0,5E,5D,E8,81,AA,4D,95,74,74,62,F9,7F), + wwH16(7E,25,5C,75,A9,6D,70,BE,93,9C,46,D5,1D,4A,4D,9D), + wwH16(36,C5,A5,F6,2B,F1,AA,71,90,A9,EC,9B,68,67,B1,73), + wwH16(ED,50,79,AD,BB,2F,45,B7,23,5F,F8,CD,D2,4D,87,FF), + wwH16(36,D6,E2,9D,5E,3E,33,3B,7A,97,C0,AE,F1,5E,8C,C2), + wwH16(D0,07,27,63,67,7F,24,1A,C8,2A,A4,EC,F0,83,C8,2E), + wwH16(56,B5,EA,29,89,F6,DD,CB,FE,C1,A6,1F,CE,E0,BA,82), + wwH16(95,81,EA,A3,F8,67,5D,34,A9,F2,BF,CE,A2,2E,81,D4), + wwH16(09,8B,25,6C,08,B8,43,D9,E2,2F,0A,FC,88,C0,7A,78), + wwH16(65,DC,67,D8,DE,B7,0C,3D,B9,E0,73,86,E5,76,81,7E), + wwH16(98,E1,C3,31,02,D7,46,C0,4F,D9,98,94,A7,CC,EC,64), + wwH16(9A,9E,FC,D2,0D,0A,FB,70,2A,4C,41,09,60,3A,BB,A4), + wwH16(31,B1,31,59,07,A0,63,E5,6A,45,25,92,87,4A,E5,7F), + wwH16(15,41,DB,46,A6,CF,75,84,75,F9,9B,2C,44,EF,2A,6E), + wwH16(8D,4F,C5,68,D3,94,26,04,B3,78,67,6F,7C,4D,00,53), + wwH16(AD,58,FE,0E,92,5F,F1,36,21,F5,D7,77,77,AA,E5,4A), + wwH16(D0,8E,9E,F2,D9,A8,13,D0,F8,7F,BF,00,20,C2,8A,32), + wwH16(1B,78,28,0F,81,00,5A,42,C4,34,D8,91,41,6B,CC,96), + wwH16(61,19,BF,7D,3C,14,6E,15,BF,90,DF,9F,AB,86,45,A0), + wwH16(6C,1C,82,73,2E,E9,B2,C5,81,77,72,27,01,A6,6F,14), + wwH16(F9,08,77,72,1F,D9,4C,77,56,B0,76,BA,4F,07,E4,28), + wwH16(BF,34,53,C6,D2,8F,CD,01,34,22,0D,2D,5B,F9,B1,D1), + wwH16(EE,2C,9A,99,4C,00,99,A8,02,D1,CE,E4,89,13,3E,A2), + wwH16(42,B7,B4,31,25,BB,11,7E,74,62,1B,AA,20,FE,2D,12), + wwH16(FF,9E,4F,17,B1,9F,FC,53,F4,C0,85,33,9D,7B,CF,9A), + wwH16(A6,6F,30,28,FF,E4,D5,9A,FE,2E,DA,A4,F0,D9,49,39), + wwH16(9F,6F,34,33,9B,2F,AE,9B,D2,6C,87,3C,DC,04,6F,6B), + wwH16(A2,06,D6,1C,C1,13,4C,3C,33,96,51,56,A9,C9,BE,F1), + wwH16(AA,02,37,B2,0D,AE,F7,5A,40,BD,62,FA,14,FD,E3,56), + wwH16(7D,4B,A7,08,7B,F9,64,AC,40,ED,D8,4A,DA,CD,24,14), + wwH16(69,79,9A,A4,DF,7C,96,DF,6C,E2,51,B5,C6,5C,6D,3E), + wwH16(67,17,17,CB,37,60,5A,1A,62,95,31,E1,D5,49,9C,E0), + wwH16(54,A5,F8,24,4A,33,97,59,AD,40,8B,8A,33,CD,7E,95), + wwH16(9A,14,1C,FE,1C,01,80,1B,37,33,34,C4,5E,8D,AC,B7), + wwH16(EC,61,DF,95,02,28,81,F6,D1,C9,B5,73,10,59,37,B8), + wwH16(9E,24,E1,F1,DB,5F,27,C1,2B,BE,57,0C,91,E1,75,C1), + wwH16(3B,FF,96,64,6E,D2,AB,A7,BB,36,25,09,A7,67,B0,8C), + wwH16(BE,38,43,41,5B,7D,9F,99,5A,05,3D,E9,44,29,52,EF), + wwH16(01,08,BA,CF,47,16,43,8F,A9,B1,C5,78,6B,2F,AE,CA), + wwH16(4D,FD,A5,36,9F,44,8B,8A,7B,A9,2D,CC,99,D0,FB,CF), + wwH16(67,BE,46,52,95,D5,AB,D3,0B,DC,6B,58,CC,7E,5E,C0), + wwH16(D0,A7,93,2B,9E,6F,D0,42,BA,5A,7F,B0,1A,E5,D2,D2), + wwH16(45,FD,AB,F2,2E,2A,21,B2,11,B9,D4,11,CB,8A,91,8A), + wwH16(E8,1F,DD,55,C1,54,05,B8,C0,C1,C7,2C,A1,3F,C6,61), + wwH16(9F,6D,14,39,57,F0,73,65,A2,D5,CE,1A,E6,A5,2E,C5), + wwH16(22,B4,71,A6,01,B1,DE,EB,5E,9A,D5,FD,C9,69,2A,05), + wwH16(59,D3,9A,46,9F,6A,FE,A0,7C,14,FA,C8,0E,86,0C,C7), + wwH16(1C,8F,03,03,EA,54,F2,44,75,7A,58,8D,21,FE,06,FB), + wwH16(A1,8F,30,68,4B,3F,99,BE,D8,41,2A,2B,F6,B4,08,CF), + wwH16(4F,5A,E9,DC,7A,AA,D6,20,83,D4,7F,F2,04,E4,0D,50), + wwH16(90,0E,76,1C,00,27,33,54,C8,FA,2B,92,28,9D,36,D6), + wwH16(1B,EB,AB,D6,6D,B4,54,A4,C3,E2,B7,DE,B5,12,C1,3C), + wwH16(91,50,EB,2C,F7,EE,CE,3A,60,09,1B,9C,EC,41,1D,0D), + wwH16(15,6B,53,C9,C7,37,D8,0A,52,53,2D,88,8F,74,89,7E), + wwH16(8D,F8,6F,87,23,7E,8E,9E,9C,B4,65,99,C4,B2,95,08), + wwH16(D1,47,F3,69,3C,66,0B,94,3E,93,18,3D,E2,FC,73,EF), + wwH16(AE,75,E2,77,71,26,6D,7C,AE,15,11,D7,ED,46,2C,3F), + wwH16(F6,71,35,B4,17,97,81,BA,37,6C,8B,4D,1C,12,9E,47), + wwH16(BD,E7,AB,BB,4E,1B,5C,28,CA,A9,96,78,91,D0,F4,65), + wwH16(37,65,3D,76,8E,E3,94,24,29,A6,32,D5,24,55,60,D2), + wwH16(85,33,53,95,79,1E,DD,56,E9,B8,A6,D8,C4,69,F2,FF), + wwH16(46,6F,39,21,89,23,1E,F4,B4,8D,F0,07,AF,11,49,BC), + wwH16(86,B9,C2,07,F9,A8,D3,48,5E,09,48,A1,C2,4D,7C,EE), + wwH16(F9,3A,98,C4,2C,7F,83,70,B6,13,07,47,E1,96,A5,B6), + wwH16(DE,AF,6D,94,F9,8F,48,1B,F9,51,38,CA,2B,2F,CA,7D), + wwH16(EB,8E,BB,01,5C,1A,CE,25,8D,68,EC,34,44,33,AB,4D), + wwH16(5B,C7,67,A5,B0,D0,C8,70,94,B1,6C,24,B5,2A,16,0E), + wwH16(76,00,3C,56,75,E0,18,F2,40,55,3D,5E,86,DB,4E,1A), + wwH16(56,E1,E6,07,71,CB,CC,B5,C4,7C,D0,9A,DB,B5,43,DE), + wwH16(8D,95,9C,F1,A8,5B,CE,FB,F3,6E,44,12,B4,2A,D9,A8), + wwH16(F3,E7,30,00,A0,3D,23,49,AA,95,A7,4F,AB,98,7E,A8), + wwH16(3C,FB,A3,A0,D7,6C,1C,38,2D,F2,87,A8,C6,8E,53,83), + wwH16(2A,67,24,01,BC,41,42,6D,91,10,50,3B,8C,3A,86,93), + wwH16(C8,C2,E1,8F,2B,60,52,C1,27,30,50,08,2C,E2,A6,47), + wwH16(07,1D,88,38,B2,77,15,30,17,80,14,D1,12,C0,86,B6), + wwH16(45,B0,2B,66,58,D1,2A,DA,00,94,6E,F9,D2,C9,19,2A), + wwH16(8F,D9,DE,10,92,25,23,AA,21,73,4D,D9,23,E9,B0,40), + wwH16(44,B7,C0,E9,72,8B,98,17,81,2B,68,46,B5,CF,60,12), + wwH16(68,FE,AD,13,C8,7A,1B,BC,F6,58,8F,F4,67,4E,8B,77), + wwH16(33,83,6E,10,42,48,08,1E,10,31,F6,B1,E3,3D,0F,95), + wwH16(97,AF,D0,32,9F,DD,2A,7A,D6,5C,4C,AA,DE,EE,45,78), + wwH16(8A,C9,EB,70,A9,92,49,9C,EA,7A,4D,C7,09,E0,95,3B), + wwH16(29,CE,49,29,5C,FC,91,34,8E,D7,A9,E3,E9,95,19,0F), + wwH16(18,5F,D6,78,6C,3A,16,DC,0F,57,07,47,DC,6F,47,C4), + wwH16(FA,2B,EA,3D,CF,89,9E,BA,F8,82,0C,D6,5B,82,FF,90), + wwH16(3B,06,32,10,6A,D7,DB,22,D1,03,97,28,09,E9,AE,EC), + wwH16(06,FD,D8,78,35,C3,CC,65,E7,D2,99,54,49,D4,6E,42), + wwH16(5A,F0,C2,8F,14,30,26,77,AA,97,87,2D,69,D6,88,AD), + wwH16(3A,76,43,96,24,E5,7B,40,40,81,68,EB,04,97,3D,BE), + wwH16(D8,CC,C2,49,BA,03,E2,20,BD,37,B9,31,1C,46,B4,8A), + wwH16(E8,90,79,8D,46,B0,F0,2E,D9,A9,7A,00,AF,47,84,D2), + wwH16(A3,86,BC,BD,36,17,05,D3,CD,4F,97,FC,9F,82,D6,07), + wwH16(1B,60,A9,B5,61,96,52,8C,40,FB,B4,9C,DD,3F,69,79), + wwH16(C4,15,71,6D,69,31,BE,7F,6A,60,30,DF,49,18,6A,12), + wwH16(24,0E,4B,F3,52,16,4A,6A,B5,D5,C5,C9,E4,12,5A,C0), + wwH16(85,A5,3C,59,F6,BA,15,4A,17,99,E6,90,18,A6,02,7B), + wwH16(70,68,EC,7F,74,74,41,F6,BC,89,D0,B2,4D,DC,6F,C7), + wwH16(D7,2E,4F,96,B1,75,DC,B4,3D,9C,41,C5,63,20,3D,52), + wwH16(F2,30,DD,8C,03,BE,A6,FC,E0,CB,F3,67,90,BD,F8,4E), + wwH16(D8,E4,F3,96,2C,D0,EF,16,2C,30,FE,ED,86,65,90,92), + wwH16(EC,ED,07,50,7A,81,32,BC,19,B2,AA,3A,9D,A0,E9,90), + wwH16(64,34,E8,C1,21,B3,E9,2D,CF,89,70,0B,04,28,29,0B), + wwH16(72,5D,2F,32,A5,EC,AE,2A,88,C2,70,76,34,62,50,CA), + wwH16(6E,49,1A,70,E3,35,3A,5E,68,23,BB,A4,AA,6A,10,8A), + wwH16(37,05,FD,FD,5F,65,6B,CD,94,01,9F,09,3B,90,8B,27), + wwH16(01,94,F7,B9,48,BE,6C,5F,E2,F2,DF,AC,A4,00,92,B5), + wwH16(35,DF,4D,CC,63,85,3F,81,B7,F0,56,13,0B,6E,BA,CC), + wwH16(43,26,E4,CA,E9,93,98,1F,83,4C,2F,C1,22,F7,88,72), + wwH16(9C,87,FA,CB,FA,87,C3,4D,92,B7,16,D1,00,A4,E1,90), + wwH16(49,17,E4,17,E9,02,30,3C,10,72,79,BE,68,9A,AA,2D), + wwH16(F0,B1,3B,7B,79,7A,A4,81,B1,8B,53,62,E1,CF,94,AF), + wwH16(DC,CE,E5,17,BF,7E,4B,9D,27,43,0B,FA,F1,D0,76,A8), + wwH16(66,B3,B3,19,D9,21,D2,93,E6,6C,45,AB,49,C7,BC,CE), + wwH16(FC,E2,C0,61,F4,16,03,43,AA,02,BC,81,75,5D,0C,2F), + wwH16(38,3D,3F,67,4D,A4,07,6D,E0,38,3C,74,35,93,9F,2A), + wwH16(6D,BF,E9,DD,04,3F,29,39,8B,01,D8,6A,2F,42,DD,F6), + wwH16(2F,2D,77,AD,E5,13,DA,54,FB,76,4C,7A,4B,3F,B5,36), + wwH16(70,5D,2E,F3,58,4D,0E,11,5A,2A,63,AE,A5,A7,AF,73), + wwH16(23,91,14,5C,F8,F0,D7,12,A5,60,BF,CA,36,B8,5A,7A), + wwH16(AB,CE,6C,7B,C5,85,F1,E7,5F,81,10,77,9C,60,64,FE), + wwH16(6E,31,71,CC,EA,D7,09,35,00,2D,C8,5D,93,30,57,66), + wwH16(C4,CA,8C,22,D1,04,20,43,79,E8,24,E7,9B,1F,3C,1D), + wwH16(0A,B4,65,B6,60,F1,3E,D9,F4,D4,5F,DB,DE,B9,D1,6D), + wwH16(0E,9F,E4,11,81,EA,9A,5A,CF,86,AF,24,8A,06,AE,05), + wwH16(E7,10,3D,4E,22,CF,DD,89,02,E8,28,16,48,B7,11,72), + wwH16(0C,6C,55,F4,7D,52,51,5D,33,F2,1B,3E,DA,8D,D5,2B), + wwH16(B3,D2,93,59,06,85,B9,76,69,8C,75,92,7D,98,C8,E5), + wwH16(40,13,9B,E4,AD,86,47,44,34,4A,37,84,CA,9A,87,76), + wwH16(FA,1A,C8,C1,E0,9E,A8,CA,99,52,DB,9A,F2,21,EF,F3), + wwH16(BC,DF,3D,59,58,03,41,1F,F5,0F,30,47,37,0D,CB,E0), + wwH16(03,76,AC,49,A9,76,3C,0B,AA,7D,FA,E7,86,D3,F2,13), + wwH16(F0,9D,83,B3,61,9B,9C,84,06,B0,15,8A,EB,86,DE,86), + wwH16(E3,EA,79,C1,F7,32,86,21,66,E1,09,F5,0A,21,5C,99), + wwH16(22,4F,3E,4E,9D,65,2F,AD,5A,AC,E8,40,A1,C2,01,76), + wwH16(11,47,9F,49,CF,E3,AB,A1,40,69,31,B6,1C,14,CD,EC), + wwH16(E7,CA,B0,27,6B,E5,46,F7,5A,6A,7B,08,AA,BA,27,C1), + wwH16(E7,7A,D6,1C,D6,F3,10,1F,F9,B6,35,64,07,2D,8A,8C), + wwH16(F8,80,3F,4D,73,96,1C,0A,EA,35,51,94,23,F6,C4,EA), + wwH16(94,5D,67,A3,9E,07,A4,7E,E3,80,7C,EA,DF,07,98,18), + wwH16(14,1B,CD,39,6D,5E,BB,61,85,9F,60,37,42,37,07,F0), + wwH16(BC,C1,83,5F,19,84,A1,28,45,63,1F,65,39,F3,7F,0B), + wwH16(74,70,3E,8D,AA,1B,DD,C4,CA,32,31,13,59,07,60,91), + wwH16(0D,D7,72,15,7D,47,2D,A8,A2,E9,96,1B,51,25,C6,AD), + wwH16(D3,9B,1A,9A,0D,17,E7,A1,10,85,C1,DB,B3,06,C2,D1), + wwH16(D9,6C,64,0D,3D,00,ED,EB,95,8F,EB,7D,19,69,A0,CB), + wwH16(E8,27,31,E2,E0,89,AD,32,65,DC,BA,EC,3E,CB,E7,9E), + wwH16(EC,2F,EB,31,9B,BF,4E,EA,1E,9F,6A,6D,16,FF,2B,40), + wwH16(5E,7D,0E,47,AE,9F,87,52,A3,1E,2C,C3,A8,A5,46,EB), + wwH16(C2,AB,3C,18,2E,AE,9F,1F,70,00,41,9A,10,16,71,F2), + wwH16(F5,B8,39,24,DE,C9,46,7F,F8,EB,4B,C7,2F,1F,7A,4A), + wwH16(03,3E,1D,86,5A,54,A9,D3,E9,38,68,BA,52,17,30,2D), + wwH16(2E,87,75,F7,07,57,79,83,D1,37,42,29,8D,CA,FF,62), + wwH16(00,FB,2A,39,39,F2,EC,CB,72,AD,BC,84,2C,AB,6C,DD), + wwH16(58,98,B1,14,51,CD,B4,26,2E,2D,4F,5D,81,15,E2,C8), + wwH16(4E,E3,14,7C,FF,99,EB,81,B4,0C,0B,FC,FD,2D,FD,EC), + wwH16(5F,B9,1C,7C,5C,09,22,F8,51,D4,32,C1,8F,2D,23,EE), + wwH16(1A,85,47,76,50,FD,F2,3D,A5,93,ED,9C,FD,33,11,AC), + wwH16(FC,2C,0E,65,2C,53,85,FF,D6,9B,B6,68,26,EC,76,C8), + wwH16(7B,2E,BF,BE,7F,EA,56,A6,8C,7B,BB,53,72,4E,F3,5E), + wwH16(27,F5,F8,93,A5,1A,8C,EA,89,EA,93,D5,1A,BE,79,46), + wwH16(73,6C,5F,AE,C5,22,BD,6D,11,A6,09,39,A7,9B,94,EE), + wwH16(AF,85,B8,E7,0F,7D,65,93,83,25,DA,EA,0C,0B,DF,9D), + wwH16(42,88,CA,CE,41,5F,AF,3D,C8,12,3F,E5,D7,85,37,27), + wwH16(40,E0,26,2B,C2,F0,E6,3D,57,2D,B8,DC,C3,A2,53,DA), + wwH16(54,A1,42,B8,32,21,7B,02,BE,8A,86,D3,E7,0F,E6,05), + wwH16(48,7A,C0,49,95,8E,8C,F2,B3,53,C7,24,39,68,0F,19), + wwH16(39,BD,A1,45,F6,73,BD,2F,7B,92,24,BE,82,E9,60,71), + wwH16(7C,C3,86,28,50,65,8F,5E,64,E1,B0,70,3A,8E,A7,D9), + wwH16(0F,D4,1A,1F,07,03,A9,28,7C,B9,6A,FE,F1,61,0D,98), + wwH16(9A,1E,31,D7,66,8D,FD,57,38,E8,DD,D4,27,9F,63,27), + wwH16(38,D6,2B,D9,ED,93,B2,AF,B0,5D,D6,E1,B3,1E,42,5C), + wwH16(B5,5F,63,02,94,7C,05,2B,DB,61,82,40,EE,6F,4A,55), + wwH16(35,27,A0,7F,EC,30,A3,99,24,7E,48,C8,35,CC,48,87), + wwH16(FA,E0,F4,85,CE,AF,A5,6D,4C,A2,67,9F,28,FE,EF,58), + wwH16(52,3F,C1,84,65,4B,6E,EB,2B,AE,28,CA,E8,B5,6E,51), + wwH16(BC,27,43,AF,5E,88,C7,15,BD,C8,4C,28,78,80,50,98), + wwH16(E0,41,59,70,93,7D,B2,2C,E9,A0,E5,19,06,6D,2D,CF), + wwH16(24,E5,04,F8,AF,93,08,99,70,8E,71,CE,88,BC,74,DF), + wwH16(69,DC,5A,F1,A9,C3,07,4A,38,32,42,38,FA,5D,9A,D8), + wwH16(39,33,13,46,D0,9F,3D,F0,43,AF,F1,0A,97,67,84,C3), + wwH16(E3,6E,F2,17,9C,AB,B6,6D,D8,19,C0,E1,A9,56,56,A0), + wwH16(A9,EB,57,64,72,74,38,F0,18,61,F9,D1,48,D9,31,B7), + wwH16(F5,FD,67,BE,63,7A,6A,04,89,65,3B,37,85,29,45,EA), + wwH16(7D,C0,D7,06,4F,D3,29,40,7F,7A,44,94,09,3D,3F,1D), + wwH16(EE,64,61,12,B0,29,8D,D2,CE,98,AB,83,72,A8,F7,20), + wwH16(DC,28,50,AE,60,B4,6E,72,C7,2F,D5,E8,5A,2C,16,90), + wwH16(DC,9D,CD,25,CA,46,3A,00,93,41,EF,F4,D9,ED,88,14), + wwH16(59,B4,D9,37,C1,58,99,4E,8A,86,EB,E8,EA,22,2A,72), + wwH16(FC,F1,2E,AE,1B,DF,4E,8E,52,80,12,40,A0,6C,68,51), + wwH16(9B,73,4C,23,4B,A5,40,E7,9B,15,66,FE,B9,1E,7D,DA), + wwH16(EF,8D,43,CA,E1,36,97,75,25,60,C3,F0,B6,78,7A,44), + wwH16(DE,BE,95,03,59,32,D4,55,87,DE,73,05,5F,BD,A2,5D), + wwH16(E6,60,BE,28,1D,55,03,0B,D5,05,78,BD,B8,BA,4B,08), + wwH16(39,3A,83,59,A4,2C,04,AB,12,54,F4,FE,93,94,46,5E), + wwH16(77,76,E0,82,AC,33,B0,80,3D,6D,A8,A3,A1,DD,01,83), + wwH16(02,DB,3E,A7,71,5C,D7,29,5B,92,92,C8,F7,45,90,FD), + wwH16(07,78,97,B4,67,AF,40,9D,79,92,B8,AA,AE,5A,6E,45), + wwH16(02,DD,C9,1B,97,55,F4,CC,D4,CE,FD,4A,94,47,BB,8A), + wwH16(79,12,35,47,3F,14,8D,51,89,FA,C4,8C,54,C2,1A,FC), + wwH16(FF,BB,C9,52,90,A4,D3,62,42,FB,17,5F,97,AB,6B,7E), + wwH16(9F,35,76,FA,15,0C,09,B0,08,71,FC,54,05,09,4B,7D), + wwH16(8C,D1,B8,E3,CC,45,F0,90,83,08,39,36,75,E4,09,47), + wwH16(C0,1F,26,17,2E,39,33,22,F9,12,51,CF,E4,E9,4E,F6), + wwH16(7F,56,87,29,22,38,88,96,CA,EF,E2,3D,8D,32,66,FF), + wwH16(3E,07,02,09,B2,D9,97,7E,C1,FF,77,A6,C3,2F,F4,43), + wwH16(21,A1,18,F1,7D,1C,DB,3E,72,FF,0E,7F,F6,FB,2E,9B), + wwH16(FF,9C,F1,31,AC,E8,43,1F,72,70,F7,11,FC,47,5E,01), + wwH16(83,88,EA,9E,16,2A,8F,E6,8D,1A,0E,D9,BC,89,E6,D9), + wwH16(04,17,8F,7A,BF,A5,B1,82,67,4E,CB,AC,F5,A5,F1,C5), + wwH16(6D,A6,14,B0,71,79,CD,ED,B3,55,66,DE,9E,8E,29,C5), + wwH16(CF,43,58,83,A8,0C,39,64,87,17,D6,0F,B5,BE,CA,89), + wwH16(E8,28,A6,EB,8F,30,E8,14,97,86,5B,7D,03,50,3D,BF), + wwH16(66,D8,57,DD,4A,0A,0D,B8,CC,29,B0,04,E0,58,84,05), + wwH16(37,5B,FE,6A,1D,A8,3A,FF,EC,4C,EA,22,0E,0D,FB,05), + wwH16(96,98,61,ED,5E,E3,2D,99,D4,7B,7F,B4,DE,CA,8E,BA), + wwH16(A9,2C,46,98,AE,BE,71,11,C1,28,7F,51,F9,DB,4E,7D), + wwH16(34,27,16,CF,65,F6,C7,28,D1,27,3E,F3,4B,A2,60,CE), + wwH16(60,97,09,4C,11,A8,18,2D,5C,E1,84,0E,59,F2,26,15), + wwH16(BC,83,56,88,6D,3B,B8,EE,5A,DA,EC,3F,D9,08,D2,86), + wwH16(08,5C,8D,86,F6,34,5F,C1,EA,3A,FD,34,03,5B,2A,E9), + wwH16(D5,76,98,89,88,42,47,0A,CB,9D,34,D6,0A,BD,6E,5C), + wwH16(55,1C,2B,45,67,F1,4C,5A,C3,6C,E7,29,18,7C,E3,57), + wwH16(2E,FB,61,48,C5,15,97,A3,C6,FC,73,8C,FD,06,92,DC), + wwH16(25,A8,FE,C8,CF,95,0F,35,AE,44,97,EF,E8,04,C3,E0), + wwH16(3E,C0,E7,5C,75,CC,A8,9F,1B,B0,DC,0D,1B,81,C2,0F), + wwH16(9A,3D,36,EC,85,93,75,C3,C5,70,63,01,20,B3,49,67), + wwH16(FE,FC,9B,06,82,A7,FA,06,15,C6,71,0E,F4,EF,60,F7), + wwH16(F0,2C,2F,F6,FD,83,EB,D9,CA,9F,0E,57,BC,6B,70,19), + wwH16(3B,19,B3,8E,D3,BF,D5,45,B9,FD,52,E2,57,E9,7F,85), + wwH16(98,74,FE,D0,19,6E,0E,D4,AF,99,55,A4,93,8E,26,85), + wwH16(B2,3C,4C,62,3D,AD,1A,6B,19,F8,BC,86,8E,80,29,47), + wwH16(1B,D2,64,19,6A,14,3F,C1,E1,D2,0D,93,73,83,32,03), + wwH16(1D,B9,33,43,3C,21,A9,19,D6,02,EE,B4,68,41,2E,BE), + wwH16(EE,EE,DF,01,9E,35,72,0D,A3,BB,8E,59,B4,3D,5C,21), + wwH16(25,4C,A5,50,38,43,8A,29,4F,CD,9A,7E,CB,29,2C,9D), + wwH16(15,82,F0,86,CA,2E,65,2D,E7,61,AB,0F,4C,A8,BC,8C), + wwH16(00,A4,8F,09,4D,87,27,32,75,23,3D,70,1D,00,4C,41), + wwH16(30,3A,05,75,90,98,EF,DD,5D,31,F5,A4,A0,81,2F,04), + wwH16(AE,20,65,A7,B2,13,A3,56,75,DF,5F,39,9A,9C,63,F2), + wwH16(69,21,73,67,80,37,E3,58,0F,66,03,DE,45,9E,14,FE), + wwH16(01,72,BF,A7,ED,22,96,78,9F,FB,67,53,F4,67,A9,91), + wwH16(4B,07,7E,C7,1F,49,1E,96,02,5B,FF,B5,A2,D5,89,AC), + wwH16(BE,B3,47,2F,55,70,84,9E,F3,C0,EA,62,C0,B6,E0,1D), + wwH16(00,1F,34,39,58,C6,45,24,9B,48,69,30,B0,95,D5,DE), + wwH16(35,7B,5A,CD,B9,D4,B8,7E,1B,A5,AF,73,DE,04,64,A5), + wwH16(4A,8D,9C,95,1A,AD,76,5A,21,67,B3,67,01,79,77,2C), + wwH16(93,D0,7C,F1,30,58,84,A1,42,DB,A3,CF,89,0B,99,72), + wwH16(B6,B1,6D,46,CA,A0,AF,AA,C2,23,24,26,3E,17,7A,8E), + wwH16(CB,FC,CE,4F,BD,E3,03,47,FB,9D,73,E5,E7,6C,F7,CA), + wwH16(E2,C9,FD,C2,7B,97,BB,BF,D8,1C,3A,6C,1A,8A,20,40), + wwH16(B9,CF,B9,86,D5,7E,DB,55,8E,5E,04,45,FD,CE,2E,D2), + wwH16(B8,D7,77,77,47,0A,F8,66,FD,39,81,79,72,0B,E8,CE), + wwH16(80,A1,DC,37,CB,90,1C,3F,E6,A6,99,40,15,3A,E4,CC), + wwH16(D5,04,E8,CD,DA,10,B1,B2,E3,D2,B4,D5,EA,EF,B0,6A), + wwH16(8D,9C,07,EE,AD,06,A1,C5,55,F1,0C,E7,14,98,5E,9B), + wwH16(8E,7F,E9,36,DC,DA,DD,44,6A,AB,EE,09,95,0D,5F,FB), + wwH16(76,D0,12,FC,42,0A,14,1F,66,9C,29,50,4D,81,75,73), + wwH16(79,12,3E,3E,8D,B6,C6,4C,54,A5,9C,63,F3,27,8D,EF), + wwH16(A7,15,08,A4,11,1A,7F,63,34,7E,6A,B1,64,E2,E9,F4), + wwH16(52,8E,CC,49,CE,81,74,14,FB,7F,16,97,07,68,4A,95), + wwH16(A7,E4,36,AD,A7,69,9B,C0,B0,CC,A7,9A,C6,4B,3C,C6), + wwH16(EB,7A,E2,7A,FE,30,81,A7,17,DA,8E,E7,F1,63,FC,A5), + wwH16(04,EC,00,F3,0B,F7,14,1B,02,C3,0B,F0,2C,F4,C9,BD), + wwH16(8B,82,78,63,A2,34,4F,1E,15,9D,44,06,0F,F6,8A,92), + wwH16(40,98,6E,F5,AA,BF,C9,14,70,F1,D5,AF,A9,1A,6A,A8), + wwH16(FF,DA,0B,0D,C9,CB,F6,3D,0A,5A,05,2E,36,35,E9,E3), + wwH16(35,30,9B,85,3F,E9,69,C2,5A,55,B0,56,51,22,3C,2D), + wwH16(99,40,E4,E1,DD,5B,49,10,6D,7F,4D,5A,ED,0B,9F,EC), + wwH16(98,65,BF,0E,49,23,4E,BE,91,81,C4,91,62,EC,9B,BC), + wwH16(CE,8E,58,A2,5F,EC,83,5B,E9,8D,D4,BB,BD,38,02,71), + wwH16(5E,B4,AD,BC,09,F4,93,7C,00,BC,2A,D3,D9,70,4C,32), + wwH16(0E,8A,C4,6F,ED,7B,B1,E8,4F,69,E5,50,B3,5E,4A,22), + wwH16(66,4B,89,08,F4,2E,AC,9D,38,FD,B9,30,BB,F3,FA,40), + wwH16(8E,4D,D6,00,DC,EC,17,86,F6,28,FA,66,83,DB,33,FA), + wwH16(01,9C,FA,AD,3B,F5,C9,8B,58,66,14,BF,E9,33,22,CF), + wwH16(65,1A,9E,0E,62,EF,76,6B,11,D4,BF,13,81,4F,24,88), + wwH16(E2,34,4C,57,0A,A5,E2,76,A5,1B,A4,05,6D,E2,A9,87), + wwH16(B2,3B,DE,83,55,2D,AE,CD,75,F5,B8,4E,FC,F5,FA,D3), + wwH16(78,A6,F2,1E,12,BD,01,C2,7E,FC,63,5F,B5,81,1E,80), + wwH16(FA,E5,52,46,31,45,24,FC,47,EC,D2,F4,45,CC,F2,97), + wwH16(63,82,50,9F,FF,A5,4F,A8,D5,C1,D3,BC,49,CE,BC,51), + wwH16(07,21,9B,AB,1D,C6,9C,CB,FF,BE,66,5C,56,A0,CB,87), + wwH16(FF,A2,64,37,1B,C4,44,B4,B8,FF,3C,6B,58,C1,37,86), + wwH16(EC,9E,7F,A9,10,2C,7D,4F,9F,AD,C8,4B,3E,4A,90,94), + wwH16(9F,C7,4C,97,1A,1F,8C,AE,1E,81,B3,29,64,6F,B5,6E), + wwH16(C7,32,9C,31,13,78,66,47,7E,40,51,44,59,BD,C3,EB), + wwH16(44,1E,15,7F,06,E7,54,96,E0,59,C1,B2,61,FB,BF,8F), + wwH16(37,06,97,BE,E4,DB,EC,CE,0D,BD,D3,3B,E0,02,4D,74), + wwH16(19,82,71,1F,CA,41,4E,16,38,07,FF,14,70,5D,B9,1D), + wwH16(46,14,8A,63,5F,1F,A2,BC,49,CF,D4,B2,C7,D8,0E,B9), + wwH16(FC,96,77,D0,DE,1C,72,1F,C7,B0,D0,59,AD,34,C1,E9), + wwH16(BB,D9,EC,9D,A1,84,52,F0,D5,6D,C9,6B,1F,A0,54,59), + wwH16(CC,65,5E,D2,6B,25,D1,96,E7,E8,02,AE,D6,EE,F8,23), + wwH16(A9,3E,24,AC,AA,6C,AE,70,41,B5,7E,76,9C,30,D6,29), + wwH16(80,71,30,9D,54,DC,05,9E,15,22,F1,DC,3F,A6,20,13), + wwH16(A1,F9,64,CC,B1,44,89,3F,DB,4D,A0,94,77,66,66,AC), + wwH16(F2,E4,B9,C4,88,53,24,86,66,BE,19,CF,41,A7,63,3D), + wwH16(C0,55,A4,B6,B1,1A,4F,52,3C,4D,7F,78,FB,49,CF,CB), + wwH16(2D,F9,4F,18,1E,6D,86,4A,50,68,E5,08,19,EE,86,02), + wwH16(A6,1E,F4,62,9E,C3,85,F5,10,CA,78,B1,E7,DA,10,E3), + wwH16(38,83,75,90,F6,D1,3D,28,24,00,50,5B,E7,00,E5,09), + wwH16(9F,A6,DC,A2,3B,0A,17,48,91,33,4A,5D,C2,A6,74,A4), + wwH16(A9,B6,F3,72,5F,37,0B,48,51,CF,27,43,0D,90,14,07), + wwH16(E3,91,65,F5,4A,C0,B5,C7,DA,F3,E0,98,05,02,62,78), + wwH16(30,BD,62,C3,49,99,B3,41,29,0C,2D,3B,55,9F,4B,98), + wwH16(33,09,F5,F1,B4,0B,71,E2,7C,07,BD,23,2A,96,0A,9C), + wwH16(B2,26,5A,43,9D,7E,79,45,1C,EE,9A,C1,41,41,DB,6A), + wwH16(CA,10,64,3C,5C,46,A3,E1,69,5E,28,48,8B,16,BC,49), + wwH16(B8,B1,C2,85,6A,5B,84,21,1F,94,53,73,13,FD,FF,2E), + wwH16(86,B7,92,88,9B,ED,21,92,2D,C9,C8,1F,C7,3E,86,19), + wwH16(EF,4F,88,63,2B,24,AE,72,BE,E5,61,E0,78,8C,80,BF), + wwH16(81,0A,68,39,8A,14,BE,AF,A4,1D,26,A9,91,88,4B,E7), + wwH16(2A,8C,01,21,FD,79,00,40,AE,67,B7,12,DB,3F,7A,CA), + wwH16(18,54,F0,ED,79,4E,35,11,19,8C,52,25,BB,13,E4,4E), + wwH16(44,DF,26,50,39,67,F4,4A,D3,E6,16,DB,05,BC,61,8A), + wwH16(AA,6B,52,38,85,23,2B,01,D0,DD,3A,1D,5B,25,99,67), + wwH16(7A,1A,89,64,56,67,1C,9D,9B,E9,C3,2F,EE,7F,06,69), + wwH16(77,79,C3,AB,BD,95,A0,B7,95,A1,5C,DE,97,9F,22,67), + wwH16(48,6F,D5,00,1C,C9,DB,0F,41,30,00,FD,E0,03,6A,7A), + wwH16(BC,DE,A0,E0,54,CD,47,48,73,C0,F9,17,29,A8,21,A9), + wwH16(DC,9B,F0,8E,0F,28,F8,53,C6,C9,ED,FA,8B,10,81,3F), + wwH16(9F,AC,D6,FE,5F,97,08,F6,46,C6,25,96,51,2C,17,94), + wwH16(69,04,25,FA,C2,3D,13,99,89,82,EC,1F,48,12,C3,37), + wwH16(33,D8,D2,AA,30,6D,61,3E,07,46,B4,21,EA,6F,7B,C9), + wwH16(CE,E5,5B,42,08,82,AC,F6,E7,F1,42,54,D2,C2,E2,95), + wwH16(E1,9B,20,C2,C7,28,02,D3,F9,64,C9,DA,A6,FA,7A,94), + wwH16(52,4B,9C,5A,28,5F,C3,FB,40,D0,2B,1A,A3,78,DE,C3), + wwH16(A8,04,4C,46,E2,7E,D0,55,A0,6B,6D,D3,B9,C3,8D,04), + wwH16(06,7A,0E,39,31,C7,55,15,92,F9,61,B2,CE,42,82,B1), + wwH16(A0,67,D5,E9,97,53,E9,89,D7,C3,AB,26,0A,7E,FE,F6), + wwH16(B5,8E,B8,07,82,13,8A,0F,18,75,47,18,20,90,55,C2), + wwH16(BF,29,C6,51,C8,06,C1,D2,DE,2F,E5,EB,F5,7E,D2,9B), + wwH16(C0,C9,5E,35,9E,81,96,29,1B,E3,35,CA,C7,C0,03,E3), + wwH16(10,66,90,C1,83,29,32,F4,9B,12,5F,86,DD,54,72,51), + wwH16(1A,E8,1C,96,0C,89,4E,2E,F3,34,DB,CE,43,DC,61,F3), + wwH16(75,CC,C1,31,99,20,77,29,9F,D2,0E,13,C1,BF,0D,2A), + wwH16(91,92,98,31,0D,11,F2,64,10,E6,BD,13,B8,B9,0D,F7), + wwH16(B0,24,FC,1A,BF,7A,E4,14,EC,39,CB,45,47,EF,5C,93), + wwH16(23,2C,06,C2,B6,8F,75,B6,19,C9,B5,FC,3A,08,73,E6), + wwH16(56,01,B1,32,DA,FE,3C,20,2C,7C,55,94,12,EB,30,59), + wwH16(DD,AF,77,95,3C,B8,3F,40,6A,34,40,BF,D7,79,2B,28), + wwH16(6C,FB,2B,57,BC,AE,80,51,F3,E5,4E,9E,F9,E8,D0,45), + wwH16(E5,FB,2E,67,14,08,FB,3F,1A,AD,0D,62,E2,9F,61,1A), + wwH16(BF,32,8F,E3,BA,CA,A7,2A,EA,73,71,E5,31,9D,99,13), + wwH16(49,43,4E,A3,AB,FA,0F,1A,7E,AB,B4,40,71,A6,89,3D), + wwH16(92,D4,0A,DC,4B,1F,57,46,91,BD,8A,41,74,BB,C0,C5), + wwH16(0B,EB,20,B7,FF,0B,09,20,EE,E4,4F,53,A2,4C,D5,5D), + wwH16(AF,8E,2E,AE,10,2D,1C,85,FB,57,73,D8,63,58,EA,47), + wwH16(87,07,E3,5F,5D,D6,61,9B,D1,BB,C5,67,75,D8,A4,96), + wwH16(27,AB,0B,A5,03,38,C4,6D,5F,94,67,95,8C,23,B1,2E), + wwH16(89,41,AB,0D,49,16,6D,4E,79,74,2F,6A,F6,8E,1A,6B), + wwH16(A1,AD,FA,AA,83,F6,17,68,EE,58,D5,B4,9C,20,15,A0), + wwH16(B8,C4,04,31,D0,47,3A,89,35,D3,5A,86,D7,15,27,F0), + wwH16(23,22,D9,16,FB,3A,31,14,CF,11,63,B3,98,0E,80,15), + wwH16(CB,51,B8,ED,12,80,98,30,79,92,AE,75,B7,CB,FD,06), + wwH16(7A,A8,27,1B,B9,42,C2,46,DD,19,BE,D6,0D,B2,C6,38), + wwH16(92,3D,DB,16,45,9F,94,7C,B8,07,64,DE,EC,73,79,D5), + wwH16(9A,16,ED,8E,E9,9A,89,E2,CF,F8,E5,EE,4C,0B,D3,69), + wwH16(D3,64,59,50,0C,92,16,67,53,53,1E,12,1E,0F,37,C7), + wwH16(35,28,4C,36,56,7C,55,14,4A,6A,59,1D,48,43,0C,65), + wwH16(B3,4E,E4,A0,03,3C,DF,87,18,F5,08,10,62,EB,15,46), + wwH16(2F,92,22,90,2C,C5,1F,63,50,26,5D,73,DC,E2,67,03), + wwH16(66,B8,E2,4A,88,AE,0C,AE,39,78,61,7F,DD,DA,51,67), + wwH16(0E,C3,38,8E,A8,99,80,94,9E,64,D4,CC,AC,52,5E,52), + wwH16(4D,B1,ED,CC,65,4C,7A,A4,7A,BF,31,99,D0,3B,9C,96), + wwH16(EC,F9,1D,BE,75,BD,83,4E,10,EF,38,7A,7F,1A,8A,66), + wwH16(4B,F3,5B,28,9B,38,D3,0C,65,24,AE,09,FB,85,C1,29), + wwH16(2D,41,5B,F8,A8,8C,59,EC,62,1E,A9,40,35,F9,B7,C7), + wwH16(EF,81,44,D5,64,0B,8B,B4,BC,9C,D5,D3,E9,24,66,3D), + wwH16(01,D6,8D,4A,B3,8F,DD,0D,DC,E7,CA,D1,3D,73,91,70), + wwH16(43,12,E1,4C,60,61,3A,EE,59,B6,1F,F3,69,BC,BD,78), + wwH16(2C,F4,5F,BA,E1,B8,C4,11,38,6D,BF,33,63,AB,D1,6E), + wwH16(F5,51,E6,1E,B0,9D,CE,ED,E9,10,1A,0D,6B,DC,C4,DD), + wwH16(60,32,98,BC,7E,5C,BB,D8,CC,6C,69,E1,67,87,92,36), + wwH16(90,4C,C9,AC,53,52,61,BE,98,39,44,47,74,7B,EF,68), + wwH16(3A,A3,B7,81,DB,9A,58,14,29,34,30,6D,29,E3,36,DB), + wwH16(69,C7,D4,86,D7,8B,BF,01,A4,0D,C1,B8,57,EB,8B,73), + wwH16(91,1A,95,1B,0E,EB,6F,27,F2,D8,C4,4D,64,F3,1D,EB), + wwH16(F6,F2,4E,CB,EE,B8,C4,65,82,86,45,14,22,5A,A5,79), + wwH16(DB,23,A8,10,16,C7,7F,1F,B1,F9,A0,C5,74,46,61,83), + wwH16(BF,6F,AA,65,F0,C3,64,82,53,5F,39,16,EE,2C,4B,E3), + wwH16(D5,31,45,CC,73,36,26,A1,AA,08,DE,A6,B9,A7,45,35), + wwH16(9E,62,53,24,87,F0,75,3A,E9,11,86,A7,F7,8B,55,05), + wwH16(0A,94,A8,21,6B,B1,7F,29,6E,56,85,59,09,A8,30,FD), + wwH16(CB,F6,79,46,82,B0,D5,C5,2E,97,8E,8C,D2,04,08,DA), + wwH16(16,56,C3,2A,F2,14,DA,F3,89,CF,7C,11,C5,5F,A4,D6), + wwH16(EA,C4,33,BD,E4,F4,34,EB,F1,30,9E,0A,DE,1A,D8,03), + wwH16(C7,66,2A,81,00,7D,E9,74,0C,1E,F0,7F,F5,38,26,55), + wwH16(B7,8B,F8,40,08,5C,0F,17,DE,F3,39,D7,00,68,FA,49), + wwH16(D3,A2,89,38,33,6C,65,B1,B9,7F,66,8C,7C,20,4A,B6), + wwH16(9A,3F,69,BB,7B,DF,C8,83,66,5D,D3,1A,3C,9D,39,5C), + wwH16(08,79,D4,B7,9E,32,15,BF,57,04,BE,C9,90,BA,A7,CD), + wwH16(8D,5F,A5,75,1D,DB,5B,44,99,31,33,2C,EB,BD,04,9B), + wwH16(2F,23,E2,9F,CE,E7,F8,5E,C0,D3,B6,8E,F2,B7,42,3C), + wwH16(F7,8E,1C,8D,E8,20,ED,47,71,4C,77,9B,A4,37,2C,5A), + wwH16(E4,01,52,E9,22,C5,B6,96,8B,0A,A5,5B,79,1A,54,9C), + wwH16(50,62,4A,D1,F7,4E,FA,C1,12,BF,54,F7,33,66,6A,06), + wwH16(EE,A5,AA,B5,38,8B,FE,C1,10,1F,3E,11,0E,47,C2,A2), + wwH16(B2,CB,BB,F5,1C,8C,BD,05,79,57,78,15,87,55,9C,7B), + wwH16(94,6C,EE,12,44,BE,A2,7A,FE,2F,16,62,9E,77,43,EC), + wwH16(48,D1,29,DF,EC,F7,62,A1,1B,3F,D8,9A,C0,0D,B8,FB), + wwH16(24,92,B1,8F,EC,04,38,B6,2E,2C,D4,0A,70,B2,95,E4), + wwH16(53,F2,7B,2E,18,DC,81,D3,32,4D,12,4E,73,CE,B6,BB), + wwH16(73,3B,97,3E,2C,41,28,5D,0B,85,6A,0A,A1,C0,C7,15), + wwH16(91,B2,32,96,50,D5,D3,D5,E7,CF,AC,9D,94,17,B5,47), + wwH16(46,94,35,D5,CE,9E,76,B9,59,F2,2D,38,7F,C8,A5,C3), + wwH16(76,F3,29,2E,8C,AC,D2,AA,01,33,6F,B9,9F,F9,90,B8), + wwH16(D1,4E,DF,2F,7D,1B,E6,C5,AA,27,41,28,19,80,11,8D), + wwH16(51,80,AC,30,E0,92,67,BE,5C,75,C6,10,62,2C,8E,9C), + wwH16(F1,55,A9,C8,06,75,FC,93,6B,B0,2D,8F,93,E6,85,6D), + wwH16(77,4F,84,96,7F,79,B3,8B,AD,EA,8D,E5,FC,A0,1A,2F), + wwH16(23,2F,C3,36,5B,56,EE,54,EE,1A,0D,35,2A,48,1E,15), + wwH16(0E,BD,65,D5,38,E1,53,C8,A3,78,C9,E9,A4,84,A7,B9), + wwH16(03,3A,CC,D6,41,1D,AA,C0,96,66,4B,C3,1E,66,2D,31), + wwH16(61,57,27,5A,E0,7E,EF,F7,30,BA,B0,F6,75,DC,63,CA), + wwH16(55,00,3B,6B,CF,DA,3F,EA,CF,03,D7,41,C5,04,FE,70), + wwH16(31,71,EC,66,85,EB,3A,59,E1,54,13,C1,37,86,C7,51), + wwH16(0C,41,8E,63,DD,1C,F1,6E,82,8D,06,C0,8D,4C,83,7A), + wwH16(10,F4,96,7C,8F,24,52,D9,5B,9F,55,93,E4,D4,23,27), + wwH16(47,16,FF,67,D1,B3,F7,09,B8,B2,50,49,B0,56,8F,42), + wwH16(E1,45,54,FB,D0,F8,29,73,CA,E3,68,AC,38,80,63,F0), + wwH16(F9,D3,93,19,7B,2A,C5,DD,0C,86,D7,50,3C,DA,F5,A2), + wwH16(EF,D5,83,EA,FF,D0,A4,FA,B2,A3,EB,C7,73,EC,6B,0D), + wwH16(8B,2E,5F,EA,0A,9C,C7,B5,45,C0,54,EA,3A,82,AD,0D), + wwH16(21,24,9D,F3,37,01,4F,75,F2,94,E4,4A,07,F7,BC,F9), + wwH16(76,0A,CF,7D,32,4B,74,D0,EF,0A,B8,80,7C,B6,DF,3C), + wwH16(C0,AE,FA,31,5E,B2,6B,38,62,C4,92,1F,82,61,48,21), + wwH16(96,A4,F6,D6,FD,A3,52,51,75,FC,89,7E,00,A2,D4,90), + wwH16(E0,D6,85,42,35,1B,C5,D1,A3,CB,3F,12,5B,49,F9,CB), + wwH16(5B,1E,EB,47,1C,3E,88,A3,02,C1,9D,C6,93,67,1A,D3), + wwH16(B1,8A,38,B4,4B,C4,4A,B6,CA,AE,DB,D4,42,61,BF,D8), + wwH16(5E,B6,9B,DB,06,DB,24,FB,DD,D9,DD,72,BA,26,98,A6), + wwH16(E8,81,DB,17,B0,CF,49,DE,55,66,D1,9C,4D,FD,41,CC), + wwH16(15,21,FD,30,E9,A8,6C,C0,BD,C3,7C,D0,D1,CD,BC,0C), + wwH16(93,AB,45,90,72,BA,5C,76,E4,84,57,15,30,F5,0A,14), + wwH16(B3,54,BE,F5,6B,6D,44,36,F6,7B,C2,ED,88,6D,DA,C1), + wwH16(37,06,46,D9,AA,B5,FA,A4,BD,C3,8C,F6,6E,E2,E9,9C), + wwH16(3D,40,F0,97,A4,F2,8B,85,22,AD,8E,54,0B,60,51,81), + wwH16(58,47,D7,F9,9C,DC,AB,17,32,65,A2,67,F1,72,32,F8), + wwH16(A9,7F,98,98,D7,89,E1,2B,EE,13,B2,2F,FC,98,9B,2F), + wwH16(EC,3F,3E,71,53,54,F9,C2,C7,44,C8,69,B9,D9,F7,83), + wwH16(EC,77,ED,5A,56,9E,99,83,DC,49,52,C4,45,5F,30,E1), + wwH16(39,0B,7A,51,B0,56,EE,5B,9F,B8,04,DF,F9,08,7D,6B), + wwH16(48,5E,1B,F4,2A,5D,11,4E,67,D3,B0,D4,EF,BA,5C,C8), + wwH16(C5,E3,0E,FD,8E,AE,27,AB,AD,AB,FF,43,B2,B6,54,46), + wwH16(FC,7B,1E,C1,1A,9A,AF,CA,AF,53,C8,04,51,A7,28,3D), + wwH16(E4,70,7F,C6,20,0D,49,CF,AE,89,DD,EC,43,10,30,C0), + wwH16(93,C5,33,11,63,C7,E7,D6,12,19,BC,9F,E3,43,37,E3), + wwH16(A6,55,2E,44,19,23,7C,BD,63,FC,EA,1F,08,96,66,3A), + wwH16(71,82,1F,B2,11,D7,B8,A5,F6,D2,F1,70,AA,F1,4E,60), + wwH16(B0,72,46,0A,63,62,C8,EE,D4,0C,37,D5,18,7E,53,28), + wwH16(5D,92,27,74,20,39,38,68,27,0A,2C,E6,F4,DD,15,85), + wwH16(B9,8A,C5,AB,63,27,53,6F,50,31,6E,21,8B,13,B2,8F), + wwH16(C8,7A,EC,E2,B0,0E,F1,A8,99,15,A3,60,40,50,32,21), + wwH16(DB,5B,51,D1,E7,7F,9E,D5,9E,31,CE,69,58,97,32,9F), + wwH16(50,DB,C1,52,D4,DD,70,D6,AE,3F,FE,DE,FC,23,98,BA), + wwH16(D6,55,3A,1F,76,5F,C8,76,5D,DC,07,65,55,C0,AF,6F), + wwH16(2E,88,80,9F,EA,2A,55,98,4E,D1,B4,39,51,6D,E8,83), + wwH16(D3,78,44,65,D7,7E,2E,AE,A7,1B,B9,D5,A2,5A,D7,0B), + wwH16(A8,16,00,55,F6,E5,7C,D1,9D,9E,45,95,1D,67,BC,CF), + wwH16(F5,1C,F7,BA,3E,09,1C,4C,02,2D,55,91,17,D6,42,76), + wwH16(86,28,90,D1,0B,07,5B,F0,5F,8B,19,51,B0,8D,0B,13), + wwH16(0A,DA,C8,61,ED,88,6B,A7,0F,EF,71,58,65,A4,6E,AE), + wwH16(83,55,EE,53,F0,EF,B5,F5,DB,19,B7,CE,FC,62,26,F6), + wwH16(E1,94,F9,0D,C4,01,5C,ED,07,C5,5C,5F,22,9A,BB,3C), + wwH16(5A,0D,81,B3,11,FE,17,F4,BD,CF,AB,A2,F3,37,D2,AA), + wwH16(38,6B,B3,C7,D1,34,04,2A,59,AB,89,27,0C,C5,3B,30), + wwH16(47,D5,AC,30,EF,00,C1,E5,43,74,B1,F5,9F,4E,ED,4E), + wwH16(EF,FF,DB,A0,44,6E,79,1F,F7,5F,2E,EA,A8,DD,64,66), + wwH16(D8,1D,0F,48,90,EB,78,9D,B8,4A,B4,3C,A0,6F,E0,AE), + wwH16(AB,75,4E,3D,5C,44,4F,EB,3A,64,76,EA,D4,FE,5B,FE), + wwH16(63,8A,A5,72,C9,41,EC,A8,4F,AB,AF,24,0A,2F,19,27), + wwH16(76,BA,66,F3,1A,ED,A6,69,50,2C,86,8E,41,CD,DF,04), + wwH16(F1,CC,64,AC,6D,6A,EF,DE,6F,DB,BE,03,BD,9C,4A,F8), + wwH16(1A,D1,94,83,3E,B4,08,DA,F2,D6,6C,69,C8,7E,76,AD), + wwH16(CA,09,B3,03,10,DB,E6,44,3E,31,65,B9,18,B0,3C,5F), + wwH16(93,25,41,B3,62,4D,4E,A6,DD,FB,9A,16,33,6E,36,2D), + wwH16(0C,C9,F2,E8,E5,5C,3E,E4,13,7D,01,59,18,CD,34,14), + wwH16(90,1D,DD,9E,54,1D,B9,54,6B,34,84,64,E0,4D,D4,45), + wwH16(B9,A5,21,73,65,D0,E8,25,D2,04,A3,1C,E7,A1,68,DF), + wwH16(C8,F5,28,52,0F,B4,70,0A,01,C5,89,BB,09,F1,4D,34), + wwH16(B7,F5,A5,96,5A,F7,32,C4,CD,CA,7F,4A,05,DA,C1,99), + wwH16(C3,01,6A,97,33,C3,E9,4F,65,A5,51,91,A0,C0,AC,AB), + wwH16(91,FB,E4,8D,06,42,65,61,DB,03,68,7F,D7,C5,8C,18), + wwH16(44,4C,23,46,EF,2B,CC,D1,9F,77,9F,11,C7,06,D0,57), + wwH16(3A,2F,AC,97,16,73,2B,D4,A9,5E,69,6A,B9,28,B3,2E), + wwH16(60,7A,F6,A5,78,82,C5,2F,FF,9A,27,A1,BC,93,02,2C), + wwH16(D1,6C,22,A9,64,01,36,5A,E3,B1,39,EF,04,9E,D4,58), + wwH16(B7,F4,6F,03,5E,E8,2C,E1,EE,75,25,F9,F5,D1,5C,2A), + wwH16(72,0C,1A,31,54,BA,FD,84,D7,DC,07,51,C8,A9,1D,48), + wwH16(1F,15,A6,A6,E6,06,1B,A5,16,9D,60,43,3F,DF,6A,48), + wwH16(3A,7F,3B,3F,B4,04,79,EA,F8,D8,19,84,C4,B1,50,F2), + wwH16(6B,67,07,CB,73,BF,78,A0,F2,44,4C,73,C3,D4,42,63), + wwH16(A0,C0,58,CC,A3,B8,C8,40,67,A4,96,20,52,37,C2,F7), + wwH16(AF,83,73,AF,24,66,90,5A,15,39,BF,B2,64,53,A9,47), + wwH16(1C,CC,D2,20,4F,7D,F8,7A,47,2D,49,E7,8F,3D,01,8B), + wwH16(81,CF,53,42,F2,78,86,13,AD,80,E9,AE,C8,FA,1C,49), + wwH16(B2,27,86,2B,4F,47,0F,63,29,6B,71,1E,C7,96,29,7F), + wwH16(4C,34,74,40,A8,32,60,68,E3,01,2B,E7,E5,0A,86,66), + wwH16(BE,D0,A9,BB,AC,4B,22,4D,B0,8D,A3,40,76,F9,F6,64), + wwH16(BE,6F,E1,EF,DA,FC,9D,45,CC,47,A3,3E,8E,83,FA,E4), + wwH16(6B,3D,36,14,0D,D1,9A,F8,EB,EB,F9,E1,5B,0C,26,7D), + wwH16(E2,2D,71,95,3B,0A,F4,BF,0D,EF,E6,F9,4B,D2,49,5D), + wwH16(51,34,9B,04,18,43,15,91,58,72,24,6B,67,20,AA,A1), + wwH16(C5,3B,27,66,70,EB,0B,4E,E3,F9,B7,9F,D8,7F,B4,4B), + wwH16(16,D8,F3,10,3B,46,FB,2E,70,B1,1C,BB,78,B6,76,CD), + wwH16(43,08,84,63,C8,70,0A,2A,5C,14,D9,3B,10,4E,17,C2), + wwH16(3A,E6,EA,3D,88,62,11,8E,A1,45,E8,24,4D,9E,72,C6), + wwH16(9D,51,00,4E,07,16,F5,56,7A,DA,D6,0C,AF,3A,C5,DA), + wwH16(1A,CC,7D,BF,7E,78,20,3E,A7,43,D9,E5,3F,F8,66,4D), + wwH16(6F,4A,99,51,31,FA,8C,54,A9,43,36,35,29,9D,E4,31), + wwH16(8E,7F,0D,93,BA,9E,59,44,25,35,5E,9F,AA,76,5E,A8), + wwH16(27,27,42,E0,B6,7F,C8,C2,CF,11,87,BF,F0,8F,88,83), + wwH16(45,1B,8D,55,80,49,D8,6D,C7,38,49,F3,A6,79,6D,E4), + wwH16(91,52,B1,64,07,93,60,48,AB,A9,6B,8D,D6,C3,CB,1A), + wwH16(C3,8B,3A,17,E3,91,EF,08,85,28,92,D0,2E,96,BD,26), + wwH16(73,72,11,83,07,99,74,F8,82,4D,C7,6B,3F,80,DA,F2), + wwH16(30,78,98,67,E5,AF,D4,B8,08,FF,9F,CE,A1,4F,3B,62), + wwH16(AA,CB,58,FF,2A,0E,D9,E5,AD,7A,98,79,D2,D0,ED,FA), + wwH16(FD,31,A4,08,7D,18,D0,50,B0,B6,82,A8,46,86,FE,FD), + wwH16(EE,F4,10,8E,79,F5,58,1A,0A,69,0C,68,AF,D5,29,02), + wwH16(D5,61,DC,66,CF,A5,4F,03,02,E0,E8,81,67,7C,FF,FE), + wwH16(53,39,73,89,F8,3A,6E,46,F1,70,E2,F4,CB,DB,F4,09), + wwH16(EB,7B,CD,53,D8,07,44,9B,A3,04,90,4C,C6,F6,4A,4E), + wwH16(00,54,E8,4F,FB,E1,2B,63,2A,43,97,D1,EC,6C,9F,93), + wwH16(9F,22,B1,F5,B5,C4,FF,B0,E9,23,73,EA,72,7A,33,07), + wwH16(22,CB,89,18,32,7B,00,90,12,32,0F,07,FB,88,F8,B9), + wwH16(56,25,C8,4B,38,BA,D2,D2,14,E7,38,D8,AE,A7,FE,D7), + wwH16(00,05,4D,55,97,D7,4B,66,6F,FA,4C,42,2D,35,9E,55), + wwH16(1D,C2,87,6C,5E,8A,61,71,46,A9,CB,A6,94,7C,3F,36), + wwH16(CF,CF,85,A0,98,39,E6,B7,BF,42,E9,F8,EF,85,9A,CC), + wwH16(22,B8,2A,B4,28,AC,85,C0,61,1E,20,14,98,95,98,1F), + wwH16(A0,FA,D5,FD,4E,75,60,F8,91,77,DD,AD,7C,92,B4,22), + wwH16(36,58,60,0C,AA,E9,C1,8D,15,B1,39,AB,0E,21,30,9C), + wwH16(A1,53,7E,B9,D4,45,23,E3,28,19,37,21,4C,3F,F9,BF), + wwH16(C1,5E,C2,D2,63,37,9B,A8,C5,70,9F,BE,BA,AC,6F,C8), + wwH16(19,6D,1B,AD,C0,8C,81,AA,DA,68,5F,96,09,B0,E0,8A), + wwH16(DE,05,CE,8A,91,D3,C5,A7,40,8F,32,77,EE,25,41,74), + wwH16(C9,DB,FD,68,35,DA,93,E2,B8,8A,FF,A4,1B,9C,74,8C), + wwH16(CB,48,33,0B,A3,A2,4C,79,BC,70,D7,66,FB,84,9B,B9), + wwH16(03,10,DA,79,25,88,40,0F,96,95,0C,38,1D,BF,26,C5), + wwH16(74,D0,7D,F0,0E,EE,53,64,0C,81,A6,E2,EF,71,A9,82), + wwH16(4D,71,D0,84,03,30,80,8B,11,6B,ED,6E,92,D2,19,A9), + wwH16(3E,21,7D,8C,64,0C,5C,44,1D,4D,5E,AB,44,93,70,B8), + wwH16(66,AD,BC,70,E0,D8,EC,29,41,29,0A,5D,98,FC,0C,1A), + wwH16(49,A1,9D,A4,53,1D,19,E8,3E,ED,3A,1F,2A,01,BC,F4), + wwH16(CE,C7,46,3C,D6,6B,8A,E1,64,ED,79,C6,AA,1D,D9,49), + wwH16(4D,6A,17,D0,23,F5,3B,0A,CC,58,B2,55,5A,0E,E9,B5), + wwH16(30,01,B9,6F,93,50,73,5F,52,B5,98,9F,6A,33,75,DC), + wwH16(71,97,D6,F3,AA,A8,1C,26,B5,50,B1,84,A3,BB,9F,F8), + wwH16(55,EF,76,05,93,4C,E7,49,CE,66,E3,0A,84,62,60,13), + wwH16(F8,18,1D,CF,54,71,20,F9,2B,75,0A,F4,32,E7,3C,8C), + wwH16(54,BD,E4,3C,EE,DF,AA,40,7B,72,95,9A,17,FF,FC,18), + wwH16(BF,37,C4,99,15,6B,89,94,D5,E5,15,C5,4A,C9,02,38), + wwH16(64,A4,E6,29,4B,19,EF,44,1D,70,33,31,A1,8D,DA,FD), + wwH16(43,2A,91,28,9C,72,83,FD,42,3C,BD,96,55,AE,98,34), + wwH16(C2,E4,EE,AB,5A,25,15,F8,5C,B7,06,7E,E2,D1,D9,A1), + wwH16(30,92,1F,22,A6,B7,C9,A6,1D,1B,89,14,96,12,D3,F3), + wwH16(DC,67,91,A5,D9,6E,8C,62,44,A6,68,03,EF,88,29,3F), + wwH16(50,DA,63,44,1F,E2,A2,38,AD,36,51,C4,46,61,BD,13), + wwH16(E5,2D,81,D2,2A,BB,98,8C,1E,10,AE,4A,39,92,3C,23), + wwH16(E2,7F,B0,41,8A,BC,1C,EA,AF,B6,F9,21,9C,60,72,8A), + wwH16(ED,BC,F7,93,DD,F7,4B,81,B6,9A,49,FF,08,31,E7,69), + wwH16(79,4D,F2,AC,CA,C9,90,8F,6B,E9,13,03,B1,EC,67,D3), + wwH16(E7,7C,A2,8B,F4,DE,C9,E5,C8,2C,23,52,2A,7A,29,89), + wwH16(22,5C,67,02,4C,98,9F,2E,54,B9,2B,59,29,31,F9,5C), + wwH16(FD,7F,BA,1B,DA,C3,83,A6,2B,1B,78,CC,44,E2,2D,D5), + wwH16(37,50,2A,6B,3A,1C,59,74,27,8A,74,7A,49,9D,E2,33), + wwH16(34,A2,EF,C3,F7,24,2D,26,BC,76,13,1E,17,B2,48,0D), + wwH16(72,1E,37,57,AB,DA,27,4C,39,EB,A9,2B,F1,4F,E2,A5), + wwH16(E4,9B,A4,C6,06,A7,12,83,C4,54,19,B7,4C,3D,0F,69), + wwH16(9A,8B,51,60,18,37,62,86,D8,BD,43,1C,A7,10,F2,84), + wwH16(5D,09,2A,5E,00,DA,42,5B,95,E8,7C,A8,17,FF,14,B7), + wwH16(AF,CC,C0,E7,6D,DD,AA,A3,6D,01,61,16,D1,D4,E3,9A), + wwH16(E7,41,B2,75,F9,5E,12,83,08,87,F7,AE,30,92,0D,36), + wwH16(8C,AB,48,42,9F,1B,DD,D1,BB,CB,04,80,24,D2,B5,DE), + wwH16(01,BA,76,7A,45,76,DB,41,6C,BC,D7,3F,77,F6,2E,62), + wwH16(78,D0,BC,C5,26,39,31,4D,D8,21,F1,59,A0,97,84,B8), + wwH16(F1,24,AE,F2,96,E2,E6,CE,A6,F7,A2,AC,32,ED,2B,3B), + wwH16(E1,BA,B4,C6,A0,F5,6F,2C,EE,80,79,0B,84,BF,98,B2), + wwH16(02,6E,B5,07,0B,13,EF,23,2A,F0,EE,1F,6E,57,0E,D1), + wwH16(F8,49,84,55,05,0C,E6,8B,37,A6,73,FB,D9,34,0C,BA), + wwH16(F8,77,3C,8E,20,C6,9C,7A,86,D9,27,F1,0B,B9,E4,BC), + wwH16(B5,35,D1,23,12,88,6D,E8,4D,72,DA,77,D1,5E,B2,8F), + wwH16(FA,66,A8,A2,C1,45,D7,22,77,9B,A5,AC,6C,AA,6A,33), + wwH16(1F,70,EA,36,67,E3,29,8C,AB,78,AD,A9,AC,EF,02,9E), + wwH16(9D,C2,3D,86,4E,C5,D0,1B,E3,1B,B9,9E,06,B7,A0,F0), + wwH16(17,0D,A8,6E,2E,0A,A6,E8,E2,07,F1,8F,5B,19,B9,A0), + wwH16(17,40,99,8B,9C,EA,4E,9D,7C,03,04,FE,19,88,5A,10), + wwH16(C2,D5,88,31,3A,56,70,C8,FA,61,07,0B,82,23,04,32), + wwH16(3C,E0,D4,71,4A,59,47,66,E2,05,C1,88,5F,D5,16,E3), + wwH16(3A,BF,7D,57,13,05,50,43,54,34,45,A4,7C,A2,53,8A), + wwH16(24,FB,1C,1A,2A,F5,D0,C3,65,0F,D5,DD,57,52,BD,B8), + wwH16(18,3E,10,6E,7B,09,E7,20,25,2B,63,7B,03,D3,19,17), + wwH16(96,FE,F8,48,DD,A4,7F,0B,21,35,5A,A6,B1,5D,FD,36), + wwH16(35,83,FA,E2,8B,9F,41,14,71,87,62,56,2F,46,28,CF), + wwH16(97,E4,ED,72,28,3C,25,F5,9C,36,41,6A,C8,32,33,EA), + wwH16(3B,A8,5A,13,03,AE,C2,7C,8A,CA,0D,82,B1,44,AC,0C), + wwH16(B9,D9,E8,2F,3E,0D,07,D0,A2,55,E3,75,54,FB,9E,B6), + wwH16(B9,C6,29,DD,24,A6,F4,9F,14,30,A0,1A,C0,6C,BF,8E), + wwH16(A1,39,27,97,AE,CD,37,C2,84,BB,CB,EB,76,D9,57,FC), + wwH16(2F,23,A9,1D,83,E5,E1,E7,A1,C1,87,E0,FE,86,B0,F7), + wwH16(59,D7,D2,41,51,FC,89,38,1F,B8,A6,0E,2D,7C,86,A6), + wwH16(4F,91,C0,F0,78,D9,BC,3A,FA,52,AB,BD,A7,3B,4A,20), + wwH16(97,A1,70,44,00,28,A4,1E,15,46,DC,1D,67,EB,8C,7F), + wwH16(76,5B,5F,E9,F1,19,E8,57,BF,0E,A6,5E,A1,C4,C6,53), + wwH16(CF,96,B8,BF,38,90,AC,FF,2E,42,95,89,AD,77,BB,39), + wwH16(53,2E,9A,70,55,B4,C0,16,11,D6,16,C5,69,E7,86,53), + wwH16(87,E7,FC,C7,A2,5E,F8,F3,4E,F1,9F,5E,AE,F9,56,4E), + wwH16(F1,14,49,7D,C9,33,21,B2,6D,2D,A7,0E,74,A8,6B,67), + wwH16(71,E6,64,7F,69,BC,51,C3,D6,6C,13,60,1B,E1,F9,31), + wwH16(D9,D7,6A,DA,17,1A,22,20,AB,19,4B,DC,48,9D,A7,74), + wwH16(5C,FB,78,72,13,3F,75,C5,C7,05,E6,37,EF,EE,11,F5), + wwH16(10,C5,53,4A,6B,5A,E8,98,BB,E7,15,76,7E,E2,E7,EA), + wwH16(5A,C2,07,2E,25,B3,EA,A6,32,69,DB,26,89,56,6F,A0), + wwH16(B1,DF,69,DB,BA,ED,76,A6,D0,3B,2A,B4,4D,CA,38,30), + wwH16(CA,4B,F1,DB,9D,63,E0,FD,47,DE,5E,6F,A3,BE,7B,C6), + wwH16(61,37,E6,A7,79,F7,73,6E,AB,18,58,EA,F7,B7,4C,65), + wwH16(4B,AC,61,7D,1D,E7,1A,2F,9C,C4,6B,42,9E,E0,2C,FC), + wwH16(29,BD,20,9C,BF,D1,E2,95,3C,D2,2F,16,29,31,BB,31), + wwH16(1C,EC,7A,B1,D5,34,99,12,25,CB,BD,DD,2B,4B,FC,F8), + wwH16(2D,73,D6,34,81,DC,54,9D,AA,0D,13,AE,C1,48,D4,B2), + wwH16(DD,BA,85,FE,12,9F,65,48,1A,BE,0E,93,45,DD,48,BA), + wwH16(48,48,B3,82,51,28,1F,FF,A1,33,C9,40,EA,9C,64,26), + wwH16(89,00,20,66,B1,78,ED,CB,28,0E,43,18,69,0A,CC,68), + wwH16(5B,92,DE,78,60,94,BA,B2,3C,D9,BB,1D,C6,65,EB,02), + wwH16(BB,6C,C1,BD,84,CF,6A,43,A2,B4,90,5F,0D,FF,9F,EF), + wwH16(21,1B,E2,AA,1D,74,C7,40,28,BF,56,A1,CB,73,8E,5F), + wwH16(B6,0E,5A,67,3F,CA,1A,94,7F,62,A5,37,CD,35,88,10), + wwH16(82,AA,79,CB,EE,2A,DF,54,A0,3A,83,E4,31,3D,1A,52), + wwH16(C2,83,AF,0C,E1,3C,68,EF,30,59,18,54,61,C3,FC,CD), + wwH16(89,78,15,B9,72,2C,13,1A,F6,99,14,0E,B7,CF,DD,EB), + wwH16(79,DC,C6,D6,2A,F4,A4,C7,23,EF,D3,5F,7C,D8,2D,A1), + wwH16(A6,82,80,F6,97,37,02,61,67,4D,79,94,BD,B5,66,4A), + wwH16(F2,EF,FD,B0,DC,9F,C8,BA,95,D0,9E,42,29,07,9C,5C), + wwH16(30,03,B2,85,3C,D1,9B,44,17,5C,70,20,A9,C6,D7,53), + wwH16(4F,2B,6B,46,38,C3,4B,1F,4B,2D,C2,25,B2,8E,68,AA), + wwH16(CC,C2,57,D2,D8,C1,1D,84,F0,71,F0,9B,17,DD,6E,CB), + wwH16(0D,A3,1B,29,D0,C2,BE,E3,93,CE,6B,51,0E,E9,7A,5B), + wwH16(CC,CB,9E,36,07,0E,52,B7,AA,56,48,DC,38,A4,0A,13), + wwH16(75,3F,70,E5,EF,68,B8,FC,E8,C1,76,E0,24,25,04,D3), + wwH16(C0,0A,77,02,05,29,B9,FB,F9,82,D1,E9,9D,CE,BA,40), + wwH16(CD,B5,C7,19,5D,8C,EF,79,D8,9C,59,84,1D,32,7D,17), + wwH16(3E,50,B6,2C,D6,C9,66,93,C0,67,F9,61,1E,8B,4D,69), + wwH16(C4,7A,C5,67,FA,EB,A4,7B,5A,1D,E1,74,A1,09,56,BD), + wwH16(74,B6,20,02,DB,D5,77,D8,FF,C3,F6,F0,50,7F,44,D6), + wwH16(C8,B8,B5,65,A5,00,56,6C,F5,38,BA,46,F5,97,5B,1D), + wwH16(01,BB,2F,72,7C,EC,02,94,3B,8B,D3,0A,40,1A,1F,01), + wwH16(AF,13,A6,FA,76,D2,9A,A1,BD,FA,71,8A,A1,03,02,52), + wwH16(40,20,2E,E0,16,A8,CC,F5,B8,2E,5D,48,D8,CD,17,58), + wwH16(93,63,83,36,4A,6B,9F,AD,82,95,52,E1,BA,79,F3,6A), + wwH16(1B,21,FA,A7,45,B0,86,B5,2D,3B,10,94,E8,96,B2,2A), + wwH16(D6,D9,68,40,7D,C2,21,9E,26,30,BE,8E,FD,78,D8,1F), + wwH16(01,B7,15,3D,E9,77,7A,2C,41,FD,87,05,DC,7A,D7,8F), + wwH16(8C,1B,F5,12,05,CC,5A,26,42,9D,CB,06,1B,3A,43,2B), + wwH16(82,AF,1E,8E,3D,EC,CE,0E,01,07,2E,0D,A6,A9,30,94), + wwH16(66,20,9D,FF,AB,95,83,2C,F6,40,A1,FC,32,E1,61,40), + wwH16(0B,15,BE,C2,4B,72,8D,78,11,46,17,2D,FF,10,2E,BB), + wwH16(B2,48,FB,67,9F,6E,E7,41,AF,EF,DA,05,73,C9,12,5B), + wwH16(1B,FF,46,FC,7E,69,C8,63,18,05,23,E6,7D,8C,F8,54), + wwH16(0A,1A,91,04,A5,BD,2E,FA,43,28,51,A9,09,0F,58,0B), + wwH16(20,E3,A9,46,4E,3F,D2,50,9F,1E,BC,40,B6,4E,D7,82), + wwH16(A7,83,1F,E1,87,D2,8B,32,FF,71,ED,10,71,09,60,4F), + wwH16(3D,96,11,90,76,4A,08,EC,8C,63,F6,22,24,26,86,D1), + wwH16(B1,BF,A2,E3,1F,B4,D2,F4,13,A9,D5,DE,AF,A8,38,E9), + wwH16(22,F4,87,B1,8C,EC,02,DE,53,4D,46,D1,CC,40,3A,83), + wwH16(03,93,F6,C3,FB,00,66,EC,77,F7,91,3B,60,32,F7,4D), + wwH16(6E,94,B2,4E,51,96,A1,30,C6,CD,63,A1,32,07,A4,ED), + wwH16(7D,F1,6E,92,D5,40,DF,C6,7A,DD,F9,D4,21,9F,78,C6), + wwH16(42,9A,21,AF,24,91,AD,78,29,6F,E4,B0,A7,C1,18,E2), + wwH16(E9,02,5A,C5,AE,8B,7B,49,3E,49,A6,B8,AA,4A,00,51), + wwH16(2D,8F,A6,0E,F3,EE,64,58,87,BA,90,D8,53,4A,EC,24), + wwH16(F3,1F,DD,E6,2B,E5,3D,C9,EA,90,98,F6,6F,DE,88,34), + wwH16(2E,09,0C,0A,93,38,22,0E,96,A0,FD,8E,35,CE,57,D3), + wwH16(D7,5F,04,82,77,7D,5C,0D,54,AE,54,97,BF,3E,16,9E), + wwH16(BD,0A,F5,21,93,58,4E,82,46,F6,8B,6E,EE,27,F7,07), + wwH16(BE,D1,C4,77,F6,83,30,CF,A0,A9,1C,D1,00,32,9B,FF), + wwH16(15,38,5B,FC,1D,12,09,F9,3E,A9,13,78,6E,DE,18,CC), + wwH16(87,4D,48,9D,67,95,C2,FE,EA,38,49,16,83,01,89,36), + wwH16(80,AF,44,90,0B,6C,11,90,14,03,22,C4,CB,94,74,6A), + wwH16(86,06,F3,71,22,A5,3F,05,51,9A,7C,76,02,17,2E,57), + wwH16(88,63,54,A6,45,13,A7,52,7D,B9,5A,05,7E,CC,61,74), + wwH16(B0,84,2C,34,9F,C0,D3,D1,82,09,2E,C1,65,75,A2,35), + wwH16(C5,9F,08,32,6F,98,73,A3,B4,0C,AD,96,F5,69,25,48), + wwH16(FD,D2,65,AD,2A,40,DC,36,C8,BD,E8,4E,99,42,56,2C), + wwH16(E4,27,DA,48,42,3D,A4,A9,F1,A7,5C,86,FB,0F,AF,38), + wwH16(B8,3A,CC,07,2A,57,2D,6C,5B,7B,79,E7,CC,3B,E4,11), + wwH16(C9,71,C4,4C,DF,DB,D5,49,3E,F0,6D,12,DB,E8,53,F2), + wwH16(1A,08,DF,2A,4D,6F,AE,C0,83,83,EA,E4,56,70,33,F4), + wwH16(D1,9A,2A,1D,6A,B6,27,08,D0,DF,8C,0A,F9,1E,3C,AF), + wwH16(D1,F3,09,A7,A2,BE,43,ED,9D,A5,7A,99,94,56,5A,4E), + wwH16(6C,BE,00,D3,9A,FB,2E,6E,84,16,29,BE,06,46,B8,D1), + wwH16(47,5F,78,23,7C,3A,F1,ED,D2,06,22,74,41,B3,2C,8C), + wwH16(46,EC,BF,16,75,B7,FD,75,5F,06,AF,3A,F1,94,7F,D7), + wwH16(F9,6A,5E,36,3F,CC,7D,80,A3,8F,B7,5D,BA,31,5A,CE), + wwH16(53,52,43,9F,49,80,10,0A,FC,0F,CA,11,56,C4,08,EF), + wwH16(83,F1,BA,F1,E8,64,C4,5E,39,59,BC,86,B8,6F,9A,E1), + wwH16(FF,17,15,E2,E0,F2,E1,C8,A7,43,D0,B0,B3,8A,6F,82), + wwH16(BD,00,A3,0A,52,86,75,E4,6B,EA,39,3E,D1,09,00,CF), + wwH16(77,63,FD,F5,56,1F,AA,65,C1,D6,C5,E5,3D,EE,C6,A3), + wwH16(54,12,07,5B,3B,F4,FE,47,61,F9,A7,EA,53,7B,0A,79), + wwH16(B8,D7,3B,BD,8F,4C,7D,07,74,3D,E5,7C,66,E3,A4,16), + wwH16(B1,81,26,DB,07,98,F3,E3,7A,34,07,25,67,98,2D,CC), + wwH16(06,CF,07,E6,FE,00,BE,CA,24,A3,A3,B9,81,5A,06,D7), + wwH16(69,EF,35,12,06,D3,93,99,01,22,E2,FA,7B,AF,93,78), + wwH16(E2,5A,CD,D2,40,32,0A,8C,EB,0A,DA,82,20,EB,2C,B9), + wwH16(EA,CB,5F,0E,89,B1,FA,B0,27,D7,04,C8,EA,2B,03,59), + wwH16(F8,59,B0,69,8B,98,9F,F5,4E,CD,98,AC,5C,73,B3,16), + wwH16(84,15,1B,C4,BC,C7,EF,18,74,18,C7,BC,43,BF,85,C4), + wwH16(BD,71,E2,E8,ED,F9,74,B2,D1,C4,DD,B9,B1,A4,70,52), + wwH16(F3,CC,44,9E,F0,C8,4E,C6,79,38,F2,42,25,F0,ED,46), + wwH16(11,11,64,46,8B,70,52,E6,13,9D,B9,B7,DC,55,E8,EC), + wwH16(5B,EC,C5,8A,6F,4D,A9,21,54,C1,EF,9D,E0,91,EE,86), + wwH16(BC,CD,B2,B2,D6,FF,BF,FA,2D,63,E3,33,9F,77,3A,F0), + wwH16(27,20,9F,78,57,47,C0,AE,C9,C1,E3,FF,74,07,E3,B0), + wwH16(06,8A,FF,A8,E5,47,BB,36,F7,11,CF,CD,93,9E,C6,3B), + wwH16(9F,8D,A3,50,78,83,19,93,91,CF,B3,F2,A6,0C,8A,E2), + wwH16(56,A7,75,4A,BC,52,2C,C8,E5,3A,6F,66,32,93,E8,FB), + wwH16(7F,71,19,77,AF,9B,35,6E,AD,F7,08,73,AA,43,A8,3D), + wwH16(E5,36,E2,BB,0A,F1,F4,27,81,77,C2,A9,58,03,76,9C), + wwH16(3D,0D,47,1B,FB,16,26,A4,9C,E9,50,85,2D,7A,68,EC), + wwH16(DC,34,5C,C8,8F,E7,A4,59,79,B1,CF,35,11,2C,0B,18), + wwH16(8E,51,AC,F2,6A,E5,7D,5A,1A,D7,CD,84,29,23,10,BD), + wwH16(7A,AD,77,3E,66,CF,5B,EA,FC,1E,42,CF,AE,4D,4F,50), + wwH16(C5,D6,BB,E6,D3,5A,A3,56,71,80,B1,4F,02,58,BD,A3), + wwH16(ED,13,0D,E7,E6,0A,C4,5C,B2,92,08,5D,70,37,6B,3E), + wwH16(29,F1,C1,63,F3,15,B3,AB,43,10,07,57,0F,3A,8C,01), + wwH16(21,78,AC,62,CE,AC,2D,EB,2F,0E,F4,3D,8B,CA,46,C1), + wwH16(B8,81,11,1B,AE,06,AA,01,23,4C,0D,F0,92,C9,15,BE), + wwH16(66,9A,DF,D3,D7,CA,FC,36,D4,DC,BF,4D,2D,48,9F,3B), + wwH16(E2,67,21,0A,A4,63,EF,A5,83,0A,EC,30,80,FC,5E,55), + wwH16(71,9A,3F,DC,CB,E8,8C,CE,37,73,4A,89,FA,AB,65,5A), + wwH16(F4,B8,8B,4A,59,5B,13,8D,50,51,66,AF,F8,DB,93,E6), + wwH16(C2,CA,D2,D8,5F,E7,20,1B,CA,65,0F,6F,AA,EE,DA,8F), + wwH16(37,13,97,5F,A8,87,60,BD,A1,91,C1,0C,08,57,D8,7F), + wwH16(66,68,7A,DD,BA,10,5D,5A,20,7C,31,83,61,B8,38,E2), + wwH16(F1,5C,F5,B4,35,28,19,AA,B6,13,AF,FC,B1,D1,1C,B1), + wwH16(C4,33,4F,01,18,29,5B,C9,C7,C4,57,57,A5,84,99,16), + wwH16(A6,DC,76,D5,7F,FF,4A,8F,25,5C,26,ED,1F,9B,23,8D), + wwH16(21,86,1D,DC,81,A6,46,05,95,9D,AE,43,2F,86,37,9C), + wwH16(D3,B6,1A,47,0C,9C,B5,2A,DF,1B,81,8C,B2,CA,E1,DE), + wwH16(A6,42,69,5E,A8,0C,39,BF,BA,B1,67,B2,EC,F0,DE,7F), + wwH16(53,BE,93,AB,EF,38,A8,11,21,7E,36,6C,F8,9B,06,6F), + wwH16(39,CE,1E,4C,DF,2F,D0,E3,63,41,EE,C3,EA,A6,FA,0F), + wwH16(0F,46,4F,12,83,25,30,BB,54,31,15,9B,E2,21,9D,14), + wwH16(95,09,AB,21,E6,76,B8,66,FD,21,88,AD,FC,0D,BC,CB), + wwH16(33,80,53,0D,DD,90,FA,E2,42,83,59,2A,78,61,B7,1E), + wwH16(6A,01,61,28,05,52,01,77,A2,C9,D1,28,54,EB,66,42), + wwH16(19,15,97,0A,ED,C5,E3,F5,60,89,34,98,ED,61,78,EA), + wwH16(2D,EA,A2,22,F7,00,89,16,15,C7,85,D9,0E,47,9C,AA), + wwH16(96,29,87,E2,61,E2,59,3A,1C,E2,93,59,48,10,CA,D5), + wwH16(EA,9D,3E,04,60,D9,B3,86,F3,98,DE,2D,1D,8E,EF,9E), + wwH16(40,B6,67,EB,78,1E,FC,79,61,F5,62,2C,93,7E,C7,A6), + wwH16(2B,4B,AA,1B,8B,47,34,06,CE,78,FA,8F,F9,7C,6D,68), + wwH16(44,2F,D9,3E,95,35,76,31,B2,5D,92,0D,D1,D7,2F,3C), + wwH16(2F,B7,24,DD,4D,CD,94,B3,81,19,21,66,42,9C,B8,34), + wwH16(87,D5,AA,D6,02,00,41,BD,D2,FC,AA,5F,AD,33,CB,F8), + wwH16(F0,1B,F9,3A,37,24,85,47,95,65,85,F8,43,25,BB,2A), + wwH16(8B,DA,D8,48,12,A0,11,7C,10,C9,B8,5A,4E,32,8C,7D), + wwH16(95,8D,30,48,CF,2F,44,56,77,08,2F,1D,2A,25,0C,C8), + wwH16(7B,A4,22,51,B3,98,55,0B,ED,2D,BD,71,56,B9,9D,9E), + wwH16(E5,77,60,FD,BC,B3,C3,96,9C,AD,AE,15,00,B4,10,EE), + wwH16(40,16,94,74,2C,90,96,84,B4,9C,3B,C9,55,D3,C8,9B), + wwH16(D2,EA,CD,63,3E,57,6F,58,9E,0B,42,DA,00,48,40,E7), + wwH16(EB,2D,13,8F,62,82,DA,52,AD,C4,90,E9,F8,C7,BC,F7), + wwH16(0A,AA,A8,81,FD,C4,F1,D4,7B,C2,59,75,C0,3E,BC,00), + wwH16(CB,F6,D1,7E,80,58,A9,B6,BF,3F,FE,0B,82,10,AD,57), + wwH16(55,0C,18,34,D7,F5,6E,EF,01,C7,DC,2E,F1,0E,67,FC), + wwH16(3A,70,2F,07,3C,49,19,4C,A0,00,FF,32,C1,FF,02,42), + wwH16(B3,7B,A5,4B,AA,E8,7F,F6,2D,0C,A1,58,A3,96,4E,5A), + wwH16(35,45,70,64,16,18,91,F0,39,53,12,BF,3C,CB,09,0B), + wwH16(D8,08,E0,33,FB,82,1D,7D,21,4F,10,FE,AB,BC,1F,BA), + wwH16(78,21,C6,92,5D,94,14,B8,C9,21,20,F7,0D,AF,D5,3D), + wwH16(AB,1F,46,D8,7F,34,95,8A,5B,DD,79,FB,97,10,54,71), + wwH16(9D,90,4C,91,5D,97,44,50,E7,F2,D2,43,A3,30,58,60), + wwH16(72,63,0C,35,73,E8,25,4B,AA,7F,4E,DF,D0,12,53,ED), + wwH16(C2,BF,C6,FB,4B,43,85,7B,16,F0,D6,04,BF,57,C8,BD), + wwH16(27,79,69,E4,62,B9,7F,D5,17,B7,B4,D9,71,DD,9B,3E), + wwH16(D6,9C,8A,7A,09,8B,2D,2F,90,8F,5F,09,91,7F,5A,FD), + wwH16(17,21,16,11,90,B1,AD,53,59,F0,BA,4C,8A,E0,50,82), + wwH16(AE,EA,37,75,90,7F,19,45,11,6D,47,6B,95,89,66,CF), + wwH16(2D,00,C7,BE,AC,9A,B7,A7,B1,62,2E,36,13,D7,F7,FA), + wwH16(2E,59,95,95,A1,23,52,E6,91,2B,44,3C,DD,C6,FF,F9), + wwH16(35,E8,D9,CA,78,3E,52,82,61,58,9A,AF,64,CC,18,C8), + wwH16(5F,1B,F9,79,79,4F,DE,A9,4A,CC,01,6D,76,75,63,D5), + wwH16(01,9A,EA,0D,1B,8C,A8,42,F7,2A,9C,98,F0,4B,B3,FA), + wwH16(BA,4C,C6,C5,64,EB,2D,27,0B,39,51,94,4D,A7,8F,8D), + wwH16(AF,30,9F,6B,12,EF,DE,C0,E9,8C,FF,1B,6E,33,2A,99), + wwH16(F0,E5,6E,7D,59,13,38,3C,69,43,38,12,C6,AD,43,D7), + wwH16(E8,ED,92,74,DF,E4,7D,C6,63,32,4E,37,EE,EF,65,8F), + wwH16(D1,43,B1,BE,62,1A,28,6E,10,1C,BA,29,79,05,36,13), + wwH16(A7,05,7B,23,1B,A7,8D,5B,9A,0B,59,FB,51,93,CA,E2), + wwH16(FD,DA,84,BB,13,F6,63,0C,9B,88,57,4B,23,EA,83,20), + wwH16(88,3F,59,73,13,CB,7A,4C,85,15,59,AB,34,42,69,42), + wwH16(90,F6,B8,C0,53,C7,F5,30,DD,7A,20,BA,FF,26,3B,43), + wwH16(89,0B,89,62,2D,6E,93,64,55,2C,D6,71,4D,11,5E,B0), + wwH16(B2,EC,AE,34,BF,56,FC,FA,D3,38,9D,17,46,7E,DC,EE), + wwH16(A7,6D,EB,3B,01,85,FE,4E,B7,C3,4F,0D,82,BC,6C,2D), + wwH16(6C,49,59,93,DB,73,B9,3C,8E,24,0D,6F,9D,D1,F9,65), + wwH16(FC,72,98,94,66,E6,54,9C,50,8E,3E,38,6B,01,92,98), + wwH16(7D,5F,20,EF,8C,25,EB,62,79,2F,77,E4,69,A9,33,54), + wwH16(5D,81,7F,A5,16,49,35,B3,8C,83,DF,A1,67,F5,5A,92), + wwH16(6F,E4,DF,FE,FD,48,75,F1,34,CA,2B,9F,DA,99,9B,A5), + wwH16(1C,E0,F0,57,42,AB,62,B0,88,B9,AC,09,15,B0,66,51), + wwH16(7E,04,1B,EC,57,B8,92,49,4D,55,E0,68,4E,1A,86,69), + wwH16(4B,7B,9F,A4,D2,AA,2E,8B,EB,8F,30,33,A5,D4,DB,3A), + wwH16(0B,47,7E,B0,63,4B,34,EC,61,B3,BE,0B,C5,F7,BB,8C), + wwH16(F2,82,35,F5,2C,A5,6C,3F,6D,E2,85,B1,7B,38,D6,C9), + wwH16(C2,F3,C5,93,6D,B9,B7,5D,CA,19,8A,EF,77,4F,DD,3C), + wwH16(EA,17,D2,40,B7,58,53,96,27,9B,F9,5D,63,97,70,BF), + wwH16(35,6F,4D,66,D5,59,68,6C,F5,FB,54,75,B1,81,27,7E), + wwH16(EE,0E,F5,07,DA,CF,9B,A9,EA,46,46,49,DA,19,52,35), + wwH16(78,9C,0C,A9,2E,4D,C8,7D,8A,1B,E8,41,8C,7B,1C,2C), + wwH16(99,25,17,D2,BA,12,A1,C7,03,6C,A8,79,2E,79,F2,06), + wwH16(E0,EC,B1,3E,84,D7,BF,2B,0E,97,69,47,99,DD,F9,92), + wwH16(E3,DD,2F,F0,86,79,AF,7B,42,8F,E6,A6,84,11,55,F4), + wwH16(29,64,4C,D2,FB,9D,0B,71,50,D9,A8,CF,68,2B,C4,4A), + wwH16(CD,11,D7,63,D1,B8,77,0B,CF,3F,EF,0B,75,B2,8A,40), + wwH16(14,70,7F,2A,95,71,D4,E1,39,4A,FD,EB,F1,E6,84,37), + wwH16(A3,A2,C0,6E,A2,DE,DF,17,F6,A6,27,78,B0,06,1C,3D), + wwH16(E1,8D,50,CB,BB,2C,1E,20,AC,C4,FF,0C,9C,DF,AC,BD), + wwH16(37,86,F4,4F,4C,C0,0F,B0,A0,86,43,FC,C4,69,3D,4F), + wwH16(90,B6,75,28,3A,46,01,D1,F1,26,4B,68,4E,8C,F2,A9), + wwH16(6E,FE,E4,CA,08,1E,2F,AA,DD,E6,BD,9F,69,08,42,1C), + wwH16(80,9C,5C,FA,7E,F8,E1,2B,CA,08,52,EB,58,BB,A1,52), + wwH16(F7,8C,DA,43,B5,08,7A,7F,C3,06,A2,F7,12,AE,11,52), + wwH16(AA,6B,E4,6B,3B,F5,CC,14,C6,9E,26,3D,5F,CB,99,3D), + wwH16(BD,55,F6,35,6F,7D,A8,F8,CB,B4,6F,97,3E,2C,7A,CC), + wwH16(6F,63,A1,F2,EA,76,1D,A0,C7,CE,8C,96,8D,0F,84,11), + wwH16(B4,A2,5D,3A,D3,93,AE,0B,42,27,29,1C,6F,97,2A,F4), + wwH16(52,9C,7C,CF,E4,F4,C0,73,6A,C3,86,57,82,54,47,E3), + wwH16(A6,6B,B9,D5,4F,F0,E5,91,43,6E,84,BE,1C,AC,66,C6), + wwH16(0C,22,F0,D3,D1,32,27,98,8E,2C,8D,7A,C6,1D,A1,5A), + wwH16(6B,C7,19,3B,D5,8A,31,56,57,5B,8A,EF,9C,62,CB,86), + wwH16(ED,BE,8F,94,91,20,6B,47,6A,0A,C3,91,27,66,FF,7A), + wwH16(5B,D2,A7,A5,A3,C1,B6,90,8B,F9,B6,10,80,40,57,8C), + wwH16(7B,39,42,B9,5E,17,F4,CF,0D,34,B3,D9,C7,1D,F4,F4), + wwH16(9B,EC,D2,D4,C1,23,AA,CE,58,A7,6C,37,9A,A1,76,E9), + wwH16(33,20,3F,0C,70,59,AD,1D,ED,F2,D0,A4,C7,D5,B7,F8), + wwH16(5F,C4,1E,50,9E,8B,36,BE,C3,7E,18,DD,DF,D2,24,27), + wwH16(9B,92,A4,60,A4,6F,5F,2C,21,86,6A,3B,78,56,DD,38), + wwH16(C6,1B,CD,9F,0E,06,A6,65,DD,1A,9C,27,51,54,A9,02), + wwH16(A0,FA,CF,CD,C8,F2,A4,61,01,97,B8,EE,1B,68,93,7E), + wwH16(BE,BD,CB,CD,F0,A4,2E,C5,D2,FD,B3,09,47,FC,26,41), + wwH16(FB,A6,8D,34,D5,15,42,D9,EF,57,36,3D,90,C4,6E,B6), + wwH16(99,F8,A6,F9,B7,55,FC,F7,F7,17,5E,C1,E2,E7,CF,F8), + wwH16(88,26,8B,CB,72,82,05,1F,20,ED,B8,05,DA,F4,F2,57), + wwH16(B2,8A,E1,09,AE,BC,C7,EE,AE,B0,CD,D5,28,5B,1E,5F), + wwH16(FD,5F,94,7C,27,60,75,CD,CA,28,D9,13,63,5D,04,AA), + wwH16(C4,ED,F8,E3,E0,F7,F7,7E,5E,D0,0B,D7,80,0C,D0,9A), + wwH16(23,8C,7F,63,0A,8D,8C,6B,29,BF,09,8B,C8,74,65,F6), + wwH16(B3,C0,4D,F0,F6,08,46,CB,A3,DB,21,DC,A6,EC,89,AB), + wwH16(4B,84,1B,75,60,CF,E0,47,66,5B,1F,1F,D7,20,6E,CD), + wwH16(D5,D3,56,98,17,74,9A,75,B4,74,94,6F,4D,B4,7D,BA), + wwH16(80,25,C3,2B,97,31,F5,B2,B4,01,38,A8,B6,93,D2,63), + wwH16(EF,AD,53,79,75,A5,A9,2D,92,59,0F,32,FC,D2,FE,B8), + wwH16(0D,D4,83,50,EA,4D,70,F0,63,1E,37,B0,23,71,D8,1D), + wwH16(5E,34,9B,78,04,13,BB,90,EE,03,1C,BC,D6,63,81,3B), + wwH16(36,F9,F4,FC,F6,FD,29,CD,64,B6,4B,DA,8E,48,54,00), + wwH16(C4,5C,33,64,93,21,BD,F3,D9,3D,36,8D,61,B6,D1,80), + wwH16(94,2C,2C,29,A7,0D,03,18,CA,7C,52,C3,8D,74,AD,89), + wwH16(14,4B,9D,E9,B1,85,98,AF,8B,D0,E5,BD,8A,CB,C7,85), + wwH16(2B,EC,44,A7,50,4E,72,9B,61,B5,2D,A9,14,77,31,75), + wwH16(38,D1,53,25,D0,81,6F,12,57,C8,6A,09,0A,66,AC,5D), + wwH16(BA,6F,C3,A0,0F,04,E8,FD,24,D4,70,78,F1,52,01,C7), + wwH16(E5,02,8A,5A,14,D7,35,85,F2,D3,5A,5A,9F,69,5C,14), + wwH16(80,85,8D,D3,E6,C7,27,E4,E7,05,8B,F1,B1,D0,CF,48), + wwH16(53,EA,72,3B,77,7A,3B,35,CF,5D,A7,D2,CF,60,6D,37), + wwH16(F5,05,00,13,38,B2,1F,ED,C7,71,70,F6,A9,26,6B,95), + wwH16(8C,5A,BB,06,92,3F,AC,E4,B9,3E,2C,E2,93,32,A6,BA), + wwH16(B3,F1,4B,BE,4E,42,23,3F,C7,78,13,65,62,C1,BB,6C), + wwH16(05,92,4A,E3,C1,EE,40,D5,2E,DD,C9,D8,3C,D6,27,82), + wwH16(A2,B6,A5,32,E4,49,53,B3,AA,26,03,84,CB,D8,8E,85), + wwH16(54,F0,A4,18,13,8A,B2,BD,9F,E9,D7,8F,92,3F,89,F1), + wwH16(05,A1,19,8C,74,7F,A7,E7,CB,BA,6B,FE,88,E2,16,44), + wwH16(23,31,4C,87,C1,51,5B,A1,4F,62,B0,56,92,C1,E7,2C), + wwH16(FB,81,3A,17,5E,1F,73,02,9D,25,36,69,69,DE,95,03), + wwH16(9B,74,96,22,6D,E2,2D,83,CE,AE,8A,F3,08,50,28,91), + wwH16(32,36,29,63,C9,68,A4,2C,51,64,53,11,C4,53,7D,B8), + wwH16(B1,89,A6,0F,20,B3,8F,5B,18,85,50,4F,51,95,FE,9A), + wwH16(F8,44,2D,53,2C,79,D7,D7,FF,BA,48,5B,70,BC,AB,0B), + wwH16(CE,A3,04,EC,4B,3A,0E,8C,C0,5B,6C,90,38,E6,D4,DD), + wwH16(DA,1F,B8,64,A4,41,1C,C9,62,FD,0E,E8,89,66,45,47), + wwH16(61,CB,98,AA,56,FA,31,DE,8C,97,9F,EA,59,F8,58,57), + wwH16(5D,6A,24,20,A0,D5,4E,F3,2E,AB,AE,C9,44,C0,BC,52), + wwH16(E1,17,0F,A9,DC,D5,BD,03,8A,04,2D,C8,E2,27,B6,00), + wwH16(ED,C5,DC,10,46,EF,91,24,21,DD,56,1A,68,0C,B8,5E), + wwH16(30,FF,D6,F8,D8,F6,C1,C1,17,4F,FF,FE,52,8D,E9,3C), + wwH16(6B,B1,08,72,B3,2B,8D,0F,D3,E3,32,18,C9,AF,73,7C), + wwH16(45,44,20,94,F2,6C,CF,F0,23,23,37,43,D5,26,C1,77), + wwH16(D3,ED,81,57,12,54,62,D3,4B,E7,85,BE,FA,CA,D5,E6), + wwH16(A3,19,BE,B4,0B,B0,2D,98,9F,23,01,F1,DF,A1,61,33), + wwH16(90,C5,38,62,B5,78,64,74,3B,02,51,F4,B0,F0,85,D7), + wwH16(E9,EE,1B,E3,C7,38,11,93,68,06,0C,D5,0A,71,9D,05), + wwH16(FB,0F,D3,D2,4F,8E,77,00,82,70,08,70,BD,61,BF,99), + wwH16(47,5D,DA,93,94,04,24,10,79,BE,DC,E3,02,69,B1,99), + wwH16(CB,E6,30,2A,81,0F,C1,BC,43,29,8A,44,BD,50,99,19), + wwH16(EB,AB,6F,35,7A,9F,0C,DF,94,30,A6,63,BD,B2,EB,CB), + wwH16(60,CA,26,8C,FC,CC,4F,BB,9C,2B,08,58,A1,A1,DD,D2), + wwH16(85,31,35,1B,70,DD,53,E3,17,89,D4,24,2B,7E,E0,88), + wwH16(21,55,7F,99,73,09,3C,FA,B1,F2,92,05,07,AF,4D,59), + wwH16(EE,EF,0D,48,CB,73,40,04,25,DC,8F,DF,87,B2,F6,2D), + wwH16(9C,27,67,02,B3,5B,C5,59,48,FF,FE,B5,0B,FE,00,15), + wwH16(BE,EF,71,1B,65,C5,BA,60,92,18,10,40,D5,23,19,9D), + wwH16(25,95,D3,06,B5,F4,B0,1A,B3,F1,39,25,68,B4,6D,D5), + wwH16(06,7E,88,40,CF,5C,15,A3,A5,95,F5,EF,B2,61,69,A7), + wwH16(12,4E,9F,B2,D4,78,5D,DD,8A,0A,4D,AF,7E,ED,1A,AD), + wwH16(E0,C9,B4,6B,94,B1,CE,CF,AD,C0,C3,67,43,C4,65,86), + wwH16(21,85,BF,73,38,2A,09,24,2D,56,42,92,CD,40,F4,CE), + wwH16(EC,27,EE,65,C9,18,84,5C,63,41,81,56,D9,AB,E8,21), + wwH16(89,64,63,CE,71,62,A9,AA,31,36,CE,73,F9,53,9A,DA), + wwH16(00,96,A2,5A,C4,D8,31,57,24,F1,4B,D2,DA,FB,45,5F), + wwH16(1D,EF,F5,E6,D1,AD,AB,CD,68,35,AD,89,7B,C9,53,AE), + wwH16(6D,8E,3D,15,86,1A,9A,EF,4D,C0,E6,A0,28,06,67,09), + wwH16(30,0E,80,72,2D,70,E8,64,94,B4,74,D6,AE,D9,DF,10), + wwH16(16,2B,7B,89,78,7F,95,42,F7,9B,F5,37,74,43,35,F8), + wwH16(EA,D5,69,BC,9E,F8,72,2E,9D,91,68,DA,94,EB,E2,5A), + wwH16(76,D9,C7,0E,4F,95,C4,82,F0,D1,4E,DC,25,3D,95,9C), + wwH16(C5,4D,11,ED,35,75,92,95,27,58,F6,1B,59,B7,46,FB), + wwH16(59,A6,AC,16,2E,6E,9A,24,15,A5,DF,66,99,FD,53,B2), + wwH16(05,4C,15,D7,1C,0A,41,F9,50,2C,4E,3E,6C,DA,19,AA), + wwH16(FB,F7,53,DB,B1,03,55,CE,C2,8B,D3,48,BD,63,FC,98), + wwH16(80,AA,0B,ED,E5,C5,4D,6F,87,FF,B1,28,9D,7C,EF,59), + wwH16(02,19,89,3F,E0,AF,79,0B,B5,E7,3E,88,C0,57,BB,F8), + wwH16(77,1D,C7,2A,15,32,92,DD,12,C7,73,08,D7,26,96,D5), + wwH16(4A,F1,7C,F9,44,C0,63,E1,75,A1,58,18,37,AB,DD,58), + wwH16(D4,94,6E,58,E1,65,A3,2E,1C,D2,26,47,08,0E,A9,3E), + wwH16(75,EB,89,73,BB,D1,2A,A7,DF,F4,9C,3F,02,9D,92,98), + wwH16(27,0A,4E,53,33,0D,F5,8E,BC,E3,AD,61,37,76,AE,BF), + wwH16(19,FE,E1,42,49,C3,6B,1A,23,7C,8E,FC,13,C4,52,68), + wwH16(62,3E,7B,77,60,65,14,B2,77,A2,FC,08,3E,8A,6B,97), + wwH16(4F,3C,FE,37,85,42,A2,A4,43,27,71,36,0B,0C,6D,D3), + wwH16(75,E1,01,F2,FF,C8,3D,76,B6,0F,C6,31,99,DE,17,35), + wwH16(9A,64,F7,B8,1E,B7,92,9D,D8,73,90,15,C8,76,DB,C2), + wwH16(FE,D3,36,A5,3D,95,1D,D2,45,D1,2F,65,FD,6D,DB,B7), + wwH16(12,A8,16,EC,96,11,0C,0C,ED,CB,02,B8,89,C7,8D,E9), + wwH16(81,26,71,DC,AC,AC,1A,A8,3C,CA,16,64,AC,2C,D8,8D), + wwH16(F8,F4,94,21,87,24,B4,BA,7E,87,0F,6D,13,60,F0,6D), + wwH16(B1,86,EB,62,B2,75,A8,17,12,92,16,D5,20,27,DD,32), + wwH16(34,A6,FC,58,E8,35,A1,82,DD,53,11,9A,6F,C6,9E,0C), + wwH16(00,E4,61,52,1D,2B,99,DC,2E,05,8D,DE,85,EA,A6,B9), + wwH16(BA,7E,DA,DB,75,2F,5D,32,3E,73,2F,3E,FE,84,92,26), + wwH16(CF,53,FF,EC,95,6A,97,BA,5E,CA,2F,8D,59,9C,E4,09), + wwH16(50,F4,29,7D,AC,71,D0,72,11,01,82,DA,5C,C3,5F,86), + wwH16(AD,D6,88,88,6F,41,68,BB,64,45,2A,22,3A,5A,93,2D), + wwH16(0E,D9,54,03,26,98,6B,14,55,B2,43,78,DD,29,8B,69), + wwH16(58,33,36,34,3C,BE,2E,E3,1A,5B,6D,5E,F4,20,D9,72), + wwH16(66,11,4A,59,5F,C6,CD,35,B0,77,58,33,6E,D1,5C,4C), + wwH16(12,A4,C8,2E,46,28,DC,DE,85,F7,12,65,22,A3,2B,52), + wwH16(AA,8A,04,92,86,9F,20,CA,D9,5F,9B,22,24,8F,6E,5F), + wwH16(7C,1C,42,9C,E6,C3,7F,4E,A6,F2,F5,4F,8A,86,28,A7), + wwH16(E3,E3,B3,B4,C3,21,BA,B5,86,47,1C,F3,78,E8,60,19), + wwH16(F9,7A,60,02,B8,5C,BE,B7,2D,9B,89,98,85,03,4E,60), + wwH16(8A,E0,14,30,8E,85,55,0B,A4,AB,FD,E7,2E,20,51,E8), + wwH16(FE,83,66,1F,5D,FE,88,5E,BB,F7,E9,A4,F7,5F,FC,ED), + wwH16(2C,63,43,CA,47,FE,D4,F4,DB,48,0F,BA,07,07,67,44), + wwH16(10,CF,EB,F6,FF,D0,69,5C,61,70,F6,BC,D6,4F,D4,48), + wwH16(24,E4,9E,E3,26,D4,AA,5F,24,A6,63,E1,AE,89,05,B1), + wwH16(77,10,83,86,5A,65,8A,77,54,5B,E9,53,63,F4,2D,36), + wwH16(B6,7C,65,30,7A,9F,D7,88,5B,2A,8B,7C,62,E7,69,C7), + wwH16(23,84,9F,D8,C8,42,9F,9B,38,B6,7C,FF,CF,F9,0D,D1), + wwH16(0E,2C,B8,C1,D6,61,47,F4,1B,65,40,CC,AA,F7,E7,59), + wwH16(53,3E,54,BC,80,39,0C,52,8E,05,73,DE,F1,81,D7,94), + wwH16(32,96,C4,40,3A,3C,5C,07,60,FE,8E,02,16,B5,36,95), + wwH16(21,8B,2E,49,9C,80,E8,32,2F,BF,38,61,A4,A2,D1,40), + wwH16(7E,41,C2,9F,D7,2D,3B,C3,9A,F6,87,7B,F1,41,12,CC), + wwH16(B1,73,D0,86,04,87,82,F1,D2,1D,7B,D5,B8,5B,5B,31), + wwH16(AA,DE,D0,48,D6,65,87,CA,45,33,54,39,B9,7E,A0,9F), + wwH16(92,26,B6,71,76,EF,90,23,33,86,AC,82,97,C0,E9,86), + wwH16(3C,0C,66,9C,47,13,5C,FF,CE,34,08,E1,AA,78,92,D3), + wwH16(89,01,1F,51,47,67,5B,F7,10,2A,8E,1F,2F,1F,18,DC), + wwH16(40,5E,11,37,66,D5,38,7C,CB,64,84,B3,C3,5E,7A,8B), + wwH16(CF,83,09,76,93,1F,29,53,3F,E3,52,31,E9,0C,7E,81), + wwH16(EE,09,87,C7,3B,36,23,6F,C0,6E,7E,65,D6,CA,3D,BB), + wwH16(30,5D,A0,4F,79,DE,1D,F5,64,5C,1C,67,C8,A5,BE,A1), + wwH16(91,67,41,2A,C2,27,5E,34,2B,50,FD,35,52,40,9E,D7), + wwH16(8E,25,2D,8B,70,41,64,33,2C,86,69,9B,8B,46,35,BA), + wwH16(18,33,B5,10,7F,62,F7,CB,27,EB,5B,0D,22,7C,C2,56), + wwH16(EA,2B,80,C6,A4,3D,D4,E3,59,D0,53,AE,71,A8,81,4F), + wwH16(15,B2,FF,FD,80,59,67,6C,22,0A,21,A7,F2,9B,50,A0), + wwH16(3D,5D,D1,B1,C0,90,FE,95,AD,1B,85,2B,59,D1,A7,8F), + wwH16(38,06,50,2D,C4,1D,57,02,8B,3A,A9,93,4F,CB,EE,B5), + wwH16(FF,B6,67,94,41,F2,E2,F5,99,E0,FE,A3,76,9B,3E,1F), + wwH16(E2,FF,F6,45,9B,0B,ED,35,8D,5D,8F,06,F7,47,64,B3), + wwH16(A7,D3,A7,26,4C,4F,2E,B3,E8,A4,4A,CD,E4,F2,14,3F), + wwH16(BA,1A,BE,D9,22,20,7C,35,05,A6,94,3F,BA,4A,AE,2A), + wwH16(B6,1B,05,72,1F,02,5C,DF,47,31,17,64,E6,43,74,2D), + wwH16(64,F1,8F,B0,90,D7,F0,13,90,09,8C,7C,FF,DA,7B,69), + wwH16(EA,4E,2F,46,32,F4,5B,C3,FF,2E,4D,36,63,2A,6A,EC), + wwH16(1C,88,61,59,4A,17,F9,DE,1A,D9,47,6C,AF,9B,B0,57), + wwH16(62,5E,F1,31,62,97,66,E2,99,F0,7A,06,74,EB,9C,03), + wwH16(65,90,99,DE,FD,91,A1,CA,21,03,0C,1C,96,A3,8A,FA), + wwH16(70,E1,1C,76,BD,20,D7,7D,11,25,CA,A1,E1,50,4F,EF), + wwH16(E4,AC,F6,1F,5E,C3,95,B1,D3,0E,23,53,23,E3,4C,97), + wwH16(44,1A,08,E1,B7,5D,E0,73,D3,3E,D6,29,99,44,C5,CA), + wwH16(32,F1,03,65,AD,70,CE,E2,C6,4C,5F,54,FD,8F,82,77), + wwH16(DA,FC,38,4E,E5,AD,71,5A,7A,9A,06,20,B1,D6,2D,DA), + wwH16(A1,F8,FE,65,B4,E0,46,0E,AE,21,1D,58,F8,86,14,E4), + wwH16(D8,79,FC,8A,E6,C4,0C,B5,43,A9,0E,4A,8F,66,C4,83), + wwH16(A3,D1,90,FE,69,BA,3F,17,4E,98,D1,7C,BA,28,E9,91), + wwH16(17,E5,F0,5C,E0,DD,94,3C,DC,89,AB,E9,7D,2A,C3,7F), + wwH16(9C,EC,5D,26,B4,E7,67,E5,2A,D2,D8,B7,C9,3C,52,88), + wwH16(7B,9A,DA,C9,D3,1B,79,E9,21,86,DC,70,03,E0,DD,F9), + wwH16(62,F4,A5,6A,3E,31,A1,A2,A5,07,A4,73,28,1D,57,60), + wwH16(CA,27,7C,79,6D,9F,5B,F1,DD,3A,4C,E6,7B,A3,4C,D0), + wwH16(3E,A5,6B,A2,5F,C8,D6,9D,E6,0F,E3,8A,E2,D2,5A,9E), + wwH16(F1,BC,DC,D9,6A,70,7B,C9,09,8D,99,68,A6,4D,38,4B), + wwH16(A0,4D,CE,16,88,7F,9E,EB,B5,1D,AC,5D,6D,5D,25,33), + wwH16(B1,9A,8D,4A,F6,73,0A,01,D9,D7,72,EF,41,CD,8C,7C), + wwH16(31,4C,07,3A,87,1B,2D,40,CF,F9,91,D1,2D,48,17,2F), + wwH16(25,1A,B8,E3,70,43,0D,A7,B2,D7,43,38,04,C2,28,B6), + wwH16(FE,9B,9D,50,39,CA,3D,0F,B1,C3,86,7B,80,7F,3F,55), + wwH16(CC,A8,4A,54,68,2B,84,72,63,5D,73,BB,5D,CB,64,5E), + wwH16(09,BC,9B,D0,16,4C,D0,CC,9C,13,78,33,F4,6A,FE,6A), + wwH16(50,97,33,F1,64,78,B3,5B,92,D7,1C,BB,13,9A,19,2B), + wwH16(C0,14,EE,08,91,1A,3D,B1,F6,FB,DB,C0,3A,2C,54,2E), + wwH16(00,F9,EA,14,E6,17,97,64,5D,44,9C,DC,B9,A8,D9,16), + wwH16(9C,A6,BD,3F,D4,E2,06,58,F5,09,CA,26,EE,5C,6C,D7), + wwH16(0E,FF,67,CB,47,1A,47,7C,F4,49,26,33,95,6D,34,5B), + wwH16(22,71,43,40,02,F8,5A,36,A6,BE,9B,89,9A,CD,2B,23), + wwH16(ED,B6,91,94,63,59,8A,0B,C6,A6,74,2E,CC,F6,2B,6C), + wwH16(BC,E3,60,88,26,D2,83,52,53,F4,47,98,53,8E,7F,99), + wwH16(5E,C1,13,8C,89,E9,0D,46,FF,84,05,24,DB,21,62,03), + wwH16(DA,F2,65,10,79,2A,DC,17,94,93,29,9E,F2,82,27,26), + wwH16(BD,C7,41,74,63,1F,D7,A1,B3,2B,41,AB,4D,71,68,20), + wwH16(B7,CC,9F,D7,64,62,2F,76,FB,CA,5C,23,35,88,A2,8A), + wwH16(29,D1,BB,62,96,24,FE,D2,83,76,74,69,18,E5,D3,19), + wwH16(8F,78,93,12,EB,E6,BA,53,3D,C9,2D,8D,4F,2C,78,77), + wwH16(4F,EC,C7,89,4D,96,6E,96,18,FF,09,45,08,49,C3,BF), + wwH16(8A,CE,49,3C,FB,8C,6D,EE,00,54,B6,52,C4,5D,4B,49), + wwH16(FA,4D,43,F4,9E,EF,99,75,7D,F8,E6,8C,78,D2,4C,EF), + wwH16(EE,17,CE,71,7E,FD,68,84,83,E8,E4,65,04,69,A5,18), + wwH16(AA,A9,A1,43,DB,AB,D2,15,EF,1F,0F,17,AC,05,B9,49), + wwH16(E6,6B,E4,66,89,94,78,5D,CE,12,D3,04,02,E2,84,9F), + wwH16(2D,73,E9,CA,98,FF,63,5F,15,1C,E8,23,17,4D,EF,4F), + wwH16(C8,EE,56,87,24,94,BC,5A,98,0F,35,FD,D8,0A,2A,53), + wwH16(16,06,FE,60,C3,1D,64,AB,06,10,73,32,D3,B0,B7,0E), + wwH16(F7,1A,EE,F7,BC,59,EF,11,A8,3A,05,29,67,B5,DC,83), + wwH16(22,B4,69,A7,37,47,34,1A,DB,B5,94,7A,73,22,3F,A5), + wwH16(56,78,E0,1A,88,26,5C,74,4E,92,73,1F,05,F5,48,95), + wwH16(94,1D,C0,17,BC,93,DF,A5,15,7F,8E,69,B6,F6,8F,B3), + wwH16(83,6A,A5,21,88,3D,A1,E0,43,29,E0,B7,BE,AF,5F,68), + wwH16(B2,4F,9F,49,3C,62,D7,B2,E7,3E,26,8F,B2,0E,2F,5D), + wwH16(21,F8,EB,0F,21,B0,37,72,24,A6,72,8A,0C,0B,84,41), + wwH16(2D,FB,13,72,51,77,D8,CA,FC,C6,86,A2,00,0E,29,42), + wwH16(02,A6,8B,D2,A5,90,D8,8C,3B,B2,EC,EA,B7,9A,5A,A0), + wwH16(F7,78,C2,2E,6E,31,1F,56,21,9E,9B,4D,FC,E2,DF,B7), + wwH16(50,20,F0,BC,DB,E8,66,E9,6E,1E,97,90,B1,71,72,3B), + wwH16(D9,27,F4,32,52,98,5F,C3,57,A7,A6,8C,F5,1D,3E,70), + wwH16(CF,1E,92,97,8C,1F,CC,18,71,9B,5E,9C,B8,30,22,37), + wwH16(B8,F6,6A,60,76,FD,44,6A,3F,47,86,5D,20,A1,69,A9), + wwH16(E3,2B,77,55,A4,F8,C1,51,FA,90,38,00,20,D0,35,29), + wwH16(03,67,AF,4F,EE,D4,5B,AA,82,93,5F,5E,54,C2,8A,0D), + wwH16(53,21,E2,5E,55,C7,F5,FB,07,13,1A,7C,03,6E,8B,ED), + wwH16(07,14,5C,CB,43,EA,78,80,98,96,3F,B6,8B,9D,80,80), + wwH16(72,A7,E2,1E,5B,57,08,EA,50,40,0F,F3,4F,AB,BB,B9), + wwH16(EC,D3,2D,94,54,7B,23,19,55,D8,3E,A3,98,01,C2,36), + wwH16(D3,FE,AC,83,85,1B,F6,C2,A7,12,CE,90,25,85,FA,5F), + wwH16(89,82,25,FF,AC,38,63,28,69,F3,5C,F4,92,83,3D,EB), + wwH16(A8,3C,9B,D7,1E,98,11,5F,A1,3E,24,B3,5A,AE,4A,CD), + wwH16(C4,6B,E1,91,8E,9C,F6,D8,F5,BF,A9,3C,79,FA,02,17), + wwH16(61,1C,0E,69,CC,0B,40,C8,69,8F,67,7F,A3,03,E0,71), + wwH16(43,24,2F,3A,8D,EF,F9,C3,DA,2D,12,23,3F,E2,7D,0C), + wwH16(38,16,33,6F,D3,90,E5,95,63,0D,65,F3,8E,B3,CC,2C), + wwH16(CF,A3,94,CD,82,F3,84,78,5D,92,EA,2F,9F,D7,F9,38), + wwH16(C0,D4,87,3F,B0,DB,86,61,A3,65,76,12,32,2E,7F,E9), + wwH16(BE,37,28,57,5D,84,88,36,20,CB,97,CC,8B,81,1F,E6), + wwH16(5F,21,54,9D,1D,BC,5F,FE,12,F3,BE,72,F4,36,0E,8A), + wwH16(47,CE,CD,ED,82,5C,4A,9B,2F,23,7F,6F,40,6F,32,FC), + wwH16(D0,CB,50,C4,FB,E0,A0,4B,B4,DB,FE,4C,74,CF,45,41), + wwH16(22,A5,6D,28,46,F8,B9,03,B5,A9,A8,2D,26,93,96,A8), + wwH16(31,7F,DA,07,39,F7,0B,AF,B6,D8,80,CF,0D,F0,95,1F), + wwH16(8F,56,3E,BF,31,7B,0E,19,28,2F,54,FB,B3,B3,50,F4), + wwH16(84,87,ED,14,5C,C8,00,C5,28,EC,73,DB,90,1F,D6,DC), + wwH16(A9,24,F1,E1,6B,F0,1E,A4,3B,A9,38,F5,7D,AF,27,E2), + wwH16(E2,CA,3F,B1,05,31,C8,F5,BE,B5,99,97,A2,8F,B9,A8), + wwH16(E5,BB,FC,F9,0D,95,69,C5,2F,A9,76,C0,FA,20,D5,D5), + wwH16(50,03,49,4D,7B,0E,C2,0D,A3,91,3C,05,AF,45,B1,D0), + wwH16(13,1C,7C,5A,D9,F8,3B,C1,E2,1C,28,E5,0D,62,14,2F), + wwH16(D0,50,46,9A,C3,5A,D5,34,C5,3C,98,2B,77,B0,71,9F), + wwH16(7B,9F,CC,74,8F,2A,73,04,8B,EA,88,52,3F,D0,39,06), + wwH16(12,13,93,7E,E5,7E,41,52,D8,74,80,E3,7D,DE,95,ED), + wwH16(65,1F,1C,86,37,17,87,29,21,E0,3B,19,19,20,36,D8), + wwH16(9E,18,EE,99,97,3D,28,AF,32,56,E1,25,BA,CB,FE,CB), + wwH16(90,40,E4,D7,C6,0A,CD,A5,20,C4,A0,84,3B,5E,7A,20), + wwH16(15,9E,7C,F2,51,36,B7,9E,D1,70,1A,A5,62,5B,B8,02), + wwH16(EE,F4,38,D5,5A,E7,BF,D4,A6,D7,38,B1,D0,28,7B,3C), + wwH16(0D,B5,FA,0F,8E,0B,2F,47,63,0F,24,25,8D,51,21,0D), + wwH16(E7,40,3B,73,72,3C,AD,95,D1,46,94,B0,6C,74,31,A3), + wwH16(41,B0,D2,55,9B,DC,F6,CF,7D,63,D5,E2,42,7F,8B,CD), + wwH16(7F,B5,65,3E,E5,A0,E7,28,41,AB,B8,96,63,7E,B6,50), + wwH16(0B,51,DF,2A,86,96,AF,BF,16,B1,74,22,06,9D,B5,7C), + wwH16(2C,02,F1,AE,E1,F3,46,F4,E4,CE,42,CA,05,78,25,F1), + wwH16(BC,27,63,38,44,AA,E3,51,52,79,06,BE,34,15,DF,99), + wwH16(96,98,9C,76,3E,C7,81,AF,79,6C,92,25,DA,E4,DF,4E), + wwH16(48,DE,0A,EF,E8,D1,4E,A9,8A,FA,40,E1,00,F1,48,36), + wwH16(DE,1F,29,C7,B6,F0,1F,3A,21,40,73,BD,C4,7D,A7,54), + wwH16(3E,5C,0C,FB,53,02,5F,81,B5,7A,59,AD,E4,31,26,8B), + wwH16(EB,7E,23,2B,02,88,83,44,81,9D,60,49,F0,6A,59,E8), + wwH16(42,3B,49,78,D3,E3,50,00,D7,A9,7D,8F,C5,EF,A1,B6), + wwH16(8F,F1,B9,68,45,8A,CA,65,51,B7,2C,18,C9,16,AC,20), + wwH16(55,C2,6A,F3,D0,31,5A,AC,90,5C,58,AA,82,E5,9F,FC), + wwH16(E8,DE,FD,E3,4B,E1,5D,EB,D5,B9,41,DA,CB,5B,0E,3F), + wwH16(12,98,C0,B9,B4,3B,2F,D9,54,D9,EA,57,49,EE,EE,32), + wwH16(9B,94,BB,16,00,14,C8,FE,94,76,7A,E0,A5,D2,BF,DF), + wwH16(76,E9,02,6C,36,04,0A,56,51,8E,AC,CF,7E,38,6F,C8), + wwH16(DA,DF,0D,0A,19,D0,6A,4B,79,76,F1,DB,41,B5,68,AA), + wwH16(64,81,BF,DC,CD,FD,5C,1B,EC,E6,80,5E,C4,29,6F,BB), + wwH16(DD,94,EF,99,EA,E2,0A,F2,AF,AA,CF,41,B0,98,16,2E), + wwH16(C3,EF,AE,24,70,B3,F1,BC,8C,A3,5C,50,B1,AD,D8,91), + wwH16(81,28,01,7E,CC,56,06,EA,95,AD,0A,43,38,FE,A2,63), + wwH16(9B,D8,FF,DC,C2,D0,11,43,45,6D,B6,EE,A0,B6,11,32), + wwH16(01,04,47,C2,66,62,63,B6,77,2F,DF,8A,B5,B8,1B,B1), + wwH16(EF,20,AE,2F,B7,3B,BA,C6,A9,0B,64,8B,B8,7E,F1,A7), + wwH16(84,03,29,1F,CD,96,44,9B,20,61,DC,11,0C,53,48,5C), + wwH16(9E,4E,F9,25,F9,E9,10,E8,08,11,42,39,75,13,80,1B), + wwH16(E3,70,BA,F4,F7,2C,FF,91,77,AF,34,19,AD,C1,A0,A6), + wwH16(19,A5,21,E8,2A,7C,4B,09,44,8D,59,58,5B,4F,2D,AB), + wwH16(4D,5C,49,9F,D7,62,27,0E,7C,BD,F8,98,78,F7,79,92), + wwH16(C3,76,DF,D3,B0,69,3B,EB,04,38,E0,C8,1C,D7,CF,0E), + wwH16(DB,16,16,F4,E0,53,1D,93,26,2B,D7,25,7A,5A,67,25), + wwH16(5F,AB,80,13,93,B4,31,6B,DB,0B,47,5D,01,19,17,D6), + wwH16(17,AA,D7,54,96,0F,9D,DF,7D,94,D6,75,F9,38,2A,33), + wwH16(00,3F,E6,EC,54,47,60,81,BD,18,3D,7C,65,E6,34,97), + wwH16(15,DB,36,1F,41,10,87,A9,35,08,B1,0F,FE,06,3E,F3), + wwH16(EE,F2,C3,6D,4B,7D,0B,91,17,54,3B,F1,6C,65,DC,D7), + wwH16(0C,DE,F6,B2,D3,21,FE,6D,3E,17,2C,82,28,71,D1,AA), + wwH16(63,73,44,66,00,B4,8B,F3,2B,5B,30,B5,B8,FB,51,1A), + wwH16(5C,EF,36,8C,AD,8D,3D,79,0F,9C,D1,21,F4,B9,A1,7B), + wwH16(F8,96,F4,5C,51,46,94,35,E8,78,E0,F7,83,04,EB,63), + wwH16(0F,39,A0,B6,81,BC,6C,CA,E1,8F,1A,7F,3A,6A,2B,FE), + wwH16(28,D6,BA,C1,FF,7F,9F,90,BA,30,AA,4F,A2,BA,92,35), + wwH16(BD,E0,E7,15,60,DA,72,95,3F,57,F3,B9,11,C9,74,ED), + wwH16(DE,D0,EB,1A,42,93,37,CC,02,33,5F,37,CD,B6,74,E3), + wwH16(F5,AB,CC,40,B7,DE,0F,8C,14,60,7A,9D,50,79,43,67), + wwH16(3B,9F,2F,9F,60,32,02,D6,07,F6,A5,3A,62,61,EB,31), + wwH16(FD,77,5A,53,C9,EB,92,35,46,43,01,59,F9,38,8E,AC), + wwH16(AC,96,05,11,6D,4C,E0,60,2F,D5,86,45,1A,7B,80,3A), + wwH16(1A,0A,82,2D,75,7D,A3,05,BF,70,29,F8,80,81,76,02), + wwH16(69,4F,CB,49,2E,48,97,1A,13,1B,21,B2,E7,B5,E7,DD), + wwH16(A0,FA,78,A2,4C,37,A9,E0,6D,F0,89,2B,67,A5,9A,95), + wwH16(7D,04,43,E6,CA,96,A2,B4,66,85,A5,36,58,D4,22,AB), + wwH16(82,2F,C6,32,61,E6,56,65,7E,4B,4F,4C,42,FC,B0,88), + wwH16(33,79,4A,A3,6B,16,F5,B2,5F,90,84,1D,43,E2,08,AA), + wwH16(94,4B,06,39,10,7E,52,C8,FE,6E,BB,65,BD,7D,59,AC), + wwH16(9C,B9,0F,78,62,DB,14,A6,C0,45,AC,0D,2A,77,F3,B7), + wwH16(22,E5,3F,92,93,65,45,59,19,B3,F2,BE,BB,75,02,85), + wwH16(76,D3,ED,0B,FE,72,AE,4B,14,0F,2F,67,69,A9,80,E9), + wwH16(63,A3,C4,3F,B1,48,FB,FE,5D,8D,35,38,75,1A,AD,9D), + wwH16(7F,00,03,E9,9C,B2,A0,77,06,AB,4B,3C,B5,24,03,7C), + wwH16(02,0B,EA,62,68,08,D3,E3,6B,28,95,98,F1,22,83,FC), + wwH16(45,B0,92,EC,E3,6C,A2,B2,64,A5,32,96,14,09,CA,82), + wwH16(22,53,48,AE,C7,DC,56,69,3E,02,5C,EF,18,93,A9,3E), + wwH16(34,B3,3C,31,C7,E5,16,2E,9A,87,74,80,69,B1,A2,08), + wwH16(98,25,11,B8,1A,B1,82,F5,85,D7,A2,FA,BE,CC,26,CB), + wwH16(82,26,0E,5F,F1,A2,A4,48,46,32,D8,98,3C,C9,EB,61), + wwH16(E7,84,16,DA,26,1B,9F,53,58,57,CB,42,ED,4C,10,FA), + wwH16(B4,A8,4A,B7,95,44,47,B7,7C,4B,56,0E,11,00,6D,B5), + wwH16(DE,C8,B5,96,F8,A0,76,05,B3,F3,B3,94,83,8F,DC,D4), + wwH16(55,EE,67,4F,2B,7A,00,37,4A,EB,B2,67,04,F8,18,9F), + wwH16(08,84,61,48,15,25,08,12,C6,9F,12,60,60,17,C6,11), + wwH16(D4,B5,CA,8D,AE,F5,62,8F,62,D0,C7,15,48,5C,7B,DB), + wwH16(49,55,82,5A,00,B7,BD,5B,3D,06,CB,8E,21,9A,7E,3F), + wwH16(EB,D4,86,17,3D,00,5B,FE,4E,0D,E4,E3,82,8E,96,A3), + wwH16(42,EA,20,C1,42,B6,78,FE,AF,89,01,DE,A2,B1,EA,20), + wwH16(FD,72,A3,7D,2A,8B,13,B6,30,DF,04,D4,D1,8B,A0,53), + wwH16(2E,FF,88,B3,36,4A,AD,62,59,FC,FF,39,44,43,4F,61), + wwH16(4D,A9,B8,7A,44,C7,F4,F2,87,51,50,00,94,2C,C2,A3), + wwH16(47,D5,F0,C1,CB,3B,DA,51,2E,68,E8,E7,A7,D9,65,36), + wwH16(58,E6,1A,D0,AF,2F,A0,25,31,0B,29,B2,44,E7,9D,C3), + wwH16(92,26,59,06,76,6B,4C,FE,37,05,32,CC,77,E1,1C,09), + wwH16(D3,1A,74,59,92,37,CE,4A,35,42,61,D6,87,D8,1F,B0), + wwH16(85,86,BD,0D,56,90,D4,E3,2E,C7,53,1A,6F,21,B3,53), + wwH16(A4,B2,98,44,91,D1,B5,7C,28,42,1F,BC,F6,91,A1,5C), + wwH16(1A,D6,B1,57,51,7E,9A,F0,E1,C1,F6,34,B7,45,79,E4), + wwH16(69,32,08,D4,25,C2,AD,35,88,F8,D1,2C,13,40,43,FF), + wwH16(09,3B,D8,51,E3,F1,B9,B0,20,10,6D,9A,E5,D0,EE,FE), + wwH16(7A,80,7E,D7,F5,42,8B,49,58,37,E7,A9,5A,AD,60,C7), + wwH16(0E,D7,45,D9,11,C9,56,18,6A,EB,CA,92,56,16,3C,75), + wwH16(30,B8,3F,AA,5E,E6,31,ED,6D,55,D9,7C,89,31,61,57), + wwH16(45,86,E5,08,36,BD,8E,1A,D4,95,2C,EE,80,75,0A,81), + wwH16(0C,D0,00,1D,1E,B3,56,8C,28,71,A8,FD,A2,67,36,B6), + wwH16(FB,52,50,D8,17,CC,A5,32,B4,30,73,C9,8A,CB,BB,B9), + wwH16(8B,92,14,E2,AE,C9,3E,2A,8D,D9,E0,78,CF,71,EE,F1), + wwH16(35,AF,FC,B2,4B,FD,84,F4,F1,B0,EF,22,FA,48,4F,1D), + wwH16(26,DD,05,9C,DE,8B,22,C7,E2,7C,6A,80,EB,77,11,DD), + wwH16(38,3A,D5,3C,F8,A3,8A,9A,7F,10,B0,DB,9C,09,B2,99), + wwH16(E5,09,32,4B,74,5A,E4,9A,C8,60,3C,7B,85,EC,9A,42), + wwH16(2B,A4,4D,50,B7,2B,62,BD,1F,E7,D0,C7,8C,99,92,96), + wwH16(0F,E2,6E,1F,FC,99,9F,34,2E,DC,AF,AE,8A,F3,A5,09), + wwH16(59,D1,14,D1,95,21,4C,C9,3D,FD,04,66,E4,AB,B4,96), + wwH16(5D,06,0E,62,73,C4,EF,02,15,23,9B,28,2D,64,97,FF), + wwH16(46,E8,8B,96,EB,C0,8C,08,5D,EA,08,32,95,1D,10,9F), + wwH16(B5,C8,23,7B,C6,FB,10,8F,B4,4C,61,F9,30,74,21,BF), + wwH16(C1,CF,85,9E,AF,B2,FE,36,69,22,CE,7E,AC,48,7B,AB), + wwH16(C3,B3,74,F9,23,5C,DB,18,FF,53,6D,DB,CC,75,69,1E), + wwH16(A2,E8,56,77,F7,EB,7F,01,C7,FC,11,2B,7E,44,DE,D0), + wwH16(42,BF,7F,19,D3,B1,95,37,DB,32,20,4E,05,F8,5E,78), + wwH16(CF,5C,71,0E,90,DA,7E,B9,76,D2,3A,56,AD,78,46,4D), + wwH16(04,39,6E,E1,50,F4,D4,DF,FE,2D,F1,F0,C9,FE,E5,93), + wwH16(FD,E2,8A,72,DA,1A,40,C7,91,1A,3D,D9,CB,E9,64,1D), + wwH16(10,2F,60,BD,F2,91,5B,49,92,2B,FD,DC,38,9D,E8,9D), + wwH16(BD,6B,C9,81,DC,DF,AF,A4,FE,75,E7,DF,04,0B,59,03), + wwH16(25,D1,FC,B5,79,F6,D6,E0,38,1E,19,A9,69,38,41,68), + wwH16(25,87,CA,9E,9B,49,FF,57,8E,98,32,37,77,B0,CD,1E), + wwH16(27,44,E0,99,58,7E,CA,A9,0A,17,C0,E0,03,C1,E5,5C), + wwH16(2E,EB,A0,43,3E,54,C8,E7,FF,1D,DF,19,0A,29,0F,FA), + wwH16(3C,7E,4F,63,84,47,4D,F4,78,76,7C,BD,4D,CC,4A,93), + wwH16(69,33,CB,D0,9D,69,EA,8B,55,DE,98,5B,F2,BF,FD,79), + wwH16(F9,EC,EF,E9,58,B9,66,2A,7E,00,E4,D9,DA,C2,C6,71), + wwH16(AF,CC,8E,B0,91,64,71,FD,5B,0F,68,90,8B,95,37,C3), + wwH16(D5,B6,B9,F6,BC,A9,57,12,18,06,48,41,2D,28,BA,53), + wwH16(85,A0,85,32,E6,C0,06,BE,10,22,98,7C,8E,02,24,5C), + wwH16(54,C2,08,FF,DD,FB,A6,FC,59,EA,CE,B8,68,EB,57,43), + wwH16(6D,7F,22,30,FB,C3,9A,9E,C6,88,B7,C5,A8,E3,F7,DB), + wwH16(43,96,99,8C,31,7D,63,C4,86,9A,8B,E5,B2,5C,34,59), + wwH16(55,E8,BB,C3,35,A1,C7,AA,60,F9,15,E0,42,75,0E,A8), + wwH16(15,54,E5,A5,6C,2E,D4,B0,2E,47,86,C6,00,72,59,CC), + wwH16(E9,2A,12,A8,36,DC,B1,C3,AF,01,36,1D,44,54,D6,D1), + wwH16(81,53,8A,60,34,51,7F,A8,CA,DC,0D,40,69,DA,73,3D), + wwH16(FD,A5,74,D7,F8,69,A3,69,B6,5F,4F,47,25,6F,86,0A), + wwH16(64,D3,90,6A,AC,CD,7D,EC,53,28,37,68,C7,A2,DA,3F), + wwH16(AC,60,68,8D,75,C5,31,46,17,75,90,8F,2B,95,ED,E3), + wwH16(82,F0,A2,90,C9,BB,D6,D4,45,4B,60,82,41,1A,2B,5B), + wwH16(55,5D,EB,2C,41,51,36,FC,24,DF,C2,EC,2B,C6,D3,E0), + wwH16(30,6E,E7,28,F9,D7,B7,8B,2F,C7,41,B9,E0,07,A9,BB), + wwH16(52,D8,1A,4E,CF,77,47,04,11,BA,58,D0,F7,A8,13,46), + wwH16(E9,8F,20,80,ED,1E,67,1E,D6,76,0B,3C,79,EF,D0,98), + wwH16(EB,41,B4,58,CF,76,20,AB,ED,35,88,EA,6A,15,C8,F7), + wwH16(C9,E3,25,EB,57,22,43,85,B4,49,DE,3D,B7,BF,4A,A2), + wwH16(DB,D9,99,AB,D5,E2,37,37,BE,90,14,6B,C4,1C,42,74), + wwH16(0A,86,4A,87,6D,6C,1A,CF,F9,F3,82,D2,AD,EF,F9,4C), + wwH16(B6,D2,2D,C3,CA,F9,DE,21,BB,D0,56,F9,80,76,1A,49), + wwH16(F7,D2,2A,D0,29,B6,20,99,A2,B1,F0,54,62,6E,47,54), + wwH16(DA,32,D4,C2,5A,7F,08,D6,FF,99,F0,B7,38,63,E8,1C), + wwH16(1C,BD,21,E1,F4,51,C9,4C,3B,27,22,3B,D2,5A,BD,CD), + wwH16(6E,3A,AE,31,2B,F6,B7,60,ED,4A,E9,75,AB,5A,45,3E), + wwH16(27,3C,77,78,43,39,7B,A4,AB,8F,5E,7F,07,20,D8,F5), + wwH16(27,2A,43,2C,12,CB,25,7E,50,06,06,2C,63,04,93,FF), + wwH16(C8,38,61,8C,08,9A,31,76,10,E8,AB,0F,C3,8F,89,C0), + wwH16(70,9C,37,F5,41,4B,E6,4F,DC,C5,B5,34,D0,DE,B7,AF), + wwH16(F6,2F,D6,F2,81,9D,B8,D7,97,CE,13,E4,21,26,FD,8E), + wwH16(F6,82,A4,D2,C8,1C,1C,EE,5D,64,93,F7,80,99,7E,FB), + wwH16(4C,1F,A4,12,21,CC,A3,9A,01,4B,B7,0A,78,43,E4,E1), + wwH16(A1,C5,80,7F,DD,E3,EA,AA,9E,01,90,51,0F,A2,B1,06), + wwH16(B9,A9,3B,E5,E5,A2,A5,67,15,F6,56,5D,9D,E0,78,5E), + wwH16(98,C5,8E,23,6E,E0,75,CC,C5,91,93,B2,F4,B8,BC,0F), + wwH16(31,B1,C1,AB,F8,CE,42,D8,A8,55,52,7F,38,2C,F7,2A), + wwH16(56,F0,0F,77,86,55,94,7F,A6,71,1B,51,D0,1B,13,2C), + wwH16(6E,79,AA,11,32,CB,0A,7C,A8,4C,B3,6D,20,63,FC,35), + wwH16(24,59,03,23,2E,6B,D4,79,BE,1D,3E,C7,92,44,98,90), + wwH16(4D,BA,28,F3,F1,6D,C0,7B,F3,68,61,A0,B1,3F,53,DF), + wwH16(B3,A5,92,9E,9B,B6,AC,ED,88,B8,5B,E3,52,A2,70,DC), + wwH16(39,AE,43,D6,E8,50,77,95,BB,BC,81,D9,F7,FC,10,B8), + wwH16(4D,B4,42,BF,43,EE,36,B7,EC,18,D4,E0,3F,69,53,6A), + wwH16(EA,41,05,35,72,1A,5F,71,62,B1,52,49,79,E1,4C,D3), + wwH16(33,44,E9,B7,96,65,EA,63,42,E2,78,F4,0E,5A,F9,4D), + wwH16(43,87,C4,C8,5C,CB,CF,95,8F,7C,98,0E,07,98,86,47), + wwH16(79,40,C9,15,87,5F,97,4C,E2,6C,37,AA,6E,91,1F,ED), + wwH16(1B,6E,65,83,C5,1F,04,57,24,07,E4,C0,FE,AC,C8,06), + wwH16(5F,2E,DD,B7,83,D7,09,C1,33,0A,09,00,8F,E8,DB,E0), + wwH16(BC,F9,3C,AC,09,15,C4,F1,63,B0,5C,9A,05,A3,5F,E4), + wwH16(86,5F,29,EA,D8,1E,A0,F4,BC,B3,6D,A2,A0,96,52,7F), + wwH16(6C,2C,04,1B,50,03,2B,84,89,5B,81,E0,C5,4D,93,77), + wwH16(A1,0F,53,DA,CD,06,56,02,49,63,BF,F2,B0,55,62,D5), + wwH16(A6,50,FF,95,83,16,D0,7C,3E,76,BB,BA,E8,79,1F,54), + wwH16(7E,57,1F,32,D5,DB,9D,33,73,CC,C2,E5,73,52,3B,65), + wwH16(BE,9C,27,4D,12,62,ED,AC,A0,15,5E,04,C3,6F,11,09), + wwH16(2A,C3,EE,A6,55,43,F0,BC,02,4E,1F,83,37,DC,93,BB), + wwH16(2A,E4,FB,8F,6D,79,C8,7B,EF,56,E3,24,DB,BA,3C,64), + wwH16(59,01,18,5C,27,E3,A7,6C,2E,72,F2,AC,B9,1E,7E,4E), + wwH16(72,15,F8,A8,BE,8B,81,94,AE,16,5C,40,C3,D6,B3,EA), + wwH16(78,4E,BB,3F,5E,9E,A4,17,22,6E,EF,E2,AF,97,A1,33), + wwH16(4F,0B,3C,9A,10,25,08,34,E9,54,4E,39,4F,24,07,26), + wwH16(B1,73,89,D6,81,11,04,8F,49,48,8C,DE,C2,06,88,14), + wwH16(D8,8B,3B,0E,55,24,21,49,F3,E4,79,68,32,5E,59,00), + wwH16(96,D1,6C,C5,7B,4F,02,94,00,B2,16,24,D0,06,B2,10), + wwH16(1A,C6,35,27,16,97,4D,72,C6,56,DF,6C,6B,AD,24,A4), + wwH16(0A,1F,7E,DE,2A,2B,D6,F6,D2,02,9C,89,45,42,D8,25), + wwH16(42,36,B5,71,53,56,6E,13,3E,F0,86,B9,2F,FE,A1,E5), + wwH16(3E,56,69,64,9F,63,F7,63,A9,15,89,80,E5,D3,0E,84), + wwH16(D0,26,FA,DD,06,B2,4D,9F,33,56,10,28,29,39,A1,56), + wwH16(AA,58,FA,69,B7,F7,AE,A5,73,BD,EA,BD,F1,1E,8E,EE), + wwH16(6C,D1,9D,83,4E,A7,D7,BA,B1,E3,00,5A,6A,A4,A8,BA), + wwH16(A4,63,4B,B8,B9,39,80,CE,8C,FA,E4,FC,24,BB,2E,2C), + wwH16(E9,80,2D,E9,D7,75,DD,72,81,27,1E,21,1F,85,45,47), + wwH16(52,40,E4,9F,DC,B9,E1,3C,DC,A3,AF,0A,A3,40,B0,64), + wwH16(53,FB,F4,D6,E0,53,E8,E3,65,86,A4,32,A3,F7,F5,91), + wwH16(59,3C,77,2D,9E,A6,DD,20,9E,73,B4,98,FE,91,02,62), + wwH16(87,75,43,D7,04,9F,73,CC,49,B6,02,6D,49,9F,81,B1), + wwH16(C5,19,78,67,D8,01,F7,7E,BA,9A,C4,89,0F,E8,D1,BA), + wwH16(C8,2D,F4,A0,8D,02,51,7D,F8,9F,55,43,93,98,C5,37), + wwH16(6C,F8,57,E1,2D,91,D0,02,DC,33,6F,1F,98,3C,DE,68), + wwH16(78,88,74,D8,0B,FA,38,39,BA,EB,63,3A,A4,FA,6B,F7), + wwH16(BC,DA,55,D0,EA,35,DF,CE,F6,57,DC,2D,2F,73,8C,E2), + wwH16(96,2A,9E,27,FA,87,4B,04,64,6A,AF,FE,D6,E2,B7,50), + wwH16(F0,12,48,78,51,FB,86,0F,F1,62,27,DA,27,6F,5A,CE), + wwH16(0D,0F,8D,3C,6D,4B,66,AD,39,5C,39,4C,C6,C7,14,CE), + wwH16(A7,08,A1,77,56,35,11,A7,7F,C8,2D,5A,8F,DB,68,DF), + wwH16(C9,24,A9,A6,E0,7C,61,77,91,0B,A0,26,D0,74,40,80), + wwH16(C7,CA,0D,35,7E,3F,83,BE,19,DC,1A,B7,61,7E,D3,B0), + wwH16(83,1F,F9,0F,F2,DA,32,31,4A,F8,BF,92,73,12,B0,E9), + wwH16(62,69,C2,17,86,FA,F7,31,74,B2,30,A8,72,DB,0B,BF), + wwH16(67,F3,33,20,E0,FA,2E,9B,27,BF,58,2E,16,74,B8,3F), + wwH16(A9,E6,DB,ED,8E,D9,6F,A1,2C,AB,DD,A2,D5,9D,6D,99), + wwH16(00,95,85,18,9C,B7,8B,C4,9F,F9,D2,CF,8E,38,69,4E), + wwH16(FA,CC,C9,07,4B,8C,29,70,BD,1B,AE,62,31,17,4A,BA), + wwH16(83,00,3C,03,C0,75,A2,1B,09,00,71,93,16,90,CA,7F), + wwH16(67,CE,FE,67,F2,6E,CB,84,18,CE,80,4F,6E,D4,C4,89), + wwH16(E5,02,14,2E,6F,AA,1D,2F,B3,B2,36,84,2E,56,94,BC), + wwH16(43,BC,33,70,9B,2F,E5,9A,50,80,7F,38,FD,6F,57,5A), + wwH16(C3,F9,DA,F2,F8,E6,1C,BE,A0,05,76,C5,88,55,23,02), + wwH16(2A,18,8B,53,76,17,2F,69,DF,4D,58,2A,08,6F,83,97), + wwH16(21,56,72,51,28,A0,CF,60,57,FA,1D,B1,39,41,0F,38), + wwH16(BC,19,64,39,2A,67,A1,DE,3B,E6,DA,02,4A,22,6C,B8), + wwH16(09,DB,9B,31,1C,FD,D1,09,A8,3B,B1,24,A5,CE,8B,B6), + wwH16(2C,96,47,EC,F9,99,BE,02,60,FD,6E,49,C3,FB,33,EC), + wwH16(A8,9B,0F,B6,B7,E6,F4,60,B5,31,CD,F9,06,EA,BE,38), + wwH16(28,D0,45,22,D1,BA,EA,EC,E6,B2,68,CC,22,1E,89,20), + wwH16(36,0F,14,96,82,FC,2E,35,B7,80,5A,A7,16,6D,45,83), + wwH16(42,5F,03,13,41,FA,F6,E2,FA,EA,DB,8E,A8,18,C6,D9), + wwH16(72,07,5A,3B,5E,4A,F5,36,F2,29,6F,5D,50,D6,E3,15), + wwH16(D0,F5,10,7D,80,9C,1D,16,54,FC,F7,0E,03,68,0B,55), + wwH16(25,BC,84,65,22,A0,DE,6F,CF,9E,67,A5,8D,CE,4C,28), + wwH16(B7,98,1E,E3,EA,68,C2,A5,D9,76,C3,39,1B,E8,4E,9D), + wwH16(C6,99,A5,81,46,BC,F4,E7,FB,B7,6E,79,46,87,C9,CD), + wwH16(75,4F,9D,78,F4,D9,38,9D,19,FD,D1,7B,06,18,7F,97), + wwH16(EA,12,03,0F,A9,32,0D,EC,10,66,35,C9,81,42,C6,A8), + wwH16(31,42,0F,80,01,65,A6,25,F1,85,AF,55,F5,9E,7B,26), + wwH16(C6,DB,D8,2A,98,37,A2,9A,20,A6,56,3B,61,A0,91,10), + wwH16(3E,96,4F,91,CF,F6,C8,A5,F3,A7,41,D9,EC,41,7F,E0), + wwH16(13,94,85,B7,62,A3,65,82,24,96,B1,8C,2E,3D,48,43), + wwH16(C1,27,D6,20,BC,45,8C,89,D6,D7,20,79,EE,EF,D0,FF), + wwH16(01,37,0D,53,7D,91,5E,54,A3,94,33,2D,0B,74,F3,5D), + wwH16(A5,26,4D,A1,1B,AC,EA,6B,7F,62,6F,94,12,EB,24,02), + wwH16(43,45,54,82,F2,A7,F7,88,3B,FD,F1,DD,64,9A,55,CD), + wwH16(02,1B,1C,58,79,5D,CC,F8,3F,FE,75,E5,6D,C7,4F,03), + wwH16(1D,B2,EA,DA,32,10,A9,93,71,83,BA,16,1E,3C,F2,BB), + wwH16(82,15,CD,D7,9A,E8,24,98,EF,20,F8,C6,61,93,9B,35), + wwH16(41,CE,C9,37,62,90,D9,67,FE,E8,10,8A,BE,5B,99,33), + wwH16(B5,46,98,0F,AD,48,15,84,DD,60,74,4A,0B,AA,55,7B), + wwH16(63,23,E4,F9,5F,DD,09,01,1D,09,3E,5F,B8,B9,09,80), + wwH16(5F,93,69,8E,99,B2,F1,A4,CA,19,F4,6A,05,74,88,C4), + wwH16(13,DC,35,43,94,2E,8C,4B,BE,80,8B,81,9A,C2,9C,3B), + wwH16(AE,70,DD,3B,2D,5B,58,7D,03,06,3A,CD,97,8F,B1,AD), + wwH16(DA,EC,EF,87,B9,93,AF,D5,97,2E,D2,EC,96,A0,9E,10), + wwH16(46,11,9B,94,BD,C7,8B,E1,AC,C7,97,DE,2D,F2,38,F6), + wwH16(8C,BE,22,00,0F,EC,E4,8C,35,56,45,84,8F,A9,BA,8F), + wwH16(44,40,39,4A,04,54,12,8D,57,AA,6F,FE,30,73,7B,96), + wwH16(C1,39,2E,06,1B,C3,B0,AB,01,F4,06,0A,56,9C,E9,BF), + wwH16(D0,35,CA,D9,4A,F3,25,3E,88,68,B4,C5,D2,0F,B0,C6), + wwH16(C8,96,D9,23,B3,0C,41,74,85,2B,48,1F,ED,92,DA,ED), + wwH16(AB,4D,F3,99,70,8C,22,F0,54,94,76,A3,AF,23,C8,33), + wwH16(D9,BB,73,F6,0E,68,B4,39,E8,15,B5,96,2D,F8,32,11), + wwH16(7D,38,08,A0,76,E0,C7,66,0B,7E,07,38,E5,05,19,64), + wwH16(0D,5A,3B,9E,4E,D9,06,87,74,69,E9,60,1F,36,51,88), + wwH16(98,FC,15,93,56,0F,BF,B7,D4,B4,EA,CF,0F,68,E4,2A), + wwH16(2B,B0,2B,DC,67,4E,CE,A6,51,9C,BA,B6,91,CC,20,5E), + wwH16(10,83,92,8D,3A,A7,F2,DE,DD,E5,8D,4B,F8,97,EA,2E), + wwH16(80,1E,D6,D9,47,F2,B1,FE,9F,EB,4E,55,B2,F9,FC,7E), + wwH16(A4,38,4E,A7,BB,7C,B3,28,27,7E,C0,4D,4C,FF,5E,16), + wwH16(75,B0,08,B9,B4,AD,79,DA,10,FF,FE,E6,AB,84,2A,A3), + wwH16(72,EC,84,5F,BA,94,32,3C,05,3A,8D,63,88,94,D7,83), + wwH16(54,17,E2,D1,4A,8E,FB,CD,F8,AF,FA,F1,75,8D,96,44), + wwH16(00,E5,49,B2,A4,12,FE,5D,64,AF,46,F8,23,D2,52,CA), + wwH16(F5,2C,88,AA,2D,35,94,FE,61,3E,03,5D,4A,BA,BB,3D), + wwH16(9C,64,08,AD,9D,22,82,0D,AA,D6,93,DF,A2,EE,FC,47), + wwH16(B9,C8,D2,43,46,DC,DC,4A,B5,51,37,75,9F,95,61,F6), + wwH16(43,A4,A3,CD,C0,D9,51,AA,88,EA,27,D5,20,0C,95,B0), + wwH16(F2,04,8C,89,7D,C5,2A,42,D8,92,0E,17,C8,57,75,72), + wwH16(C3,F0,12,09,6C,E5,C5,67,27,93,9E,DF,CD,7E,2A,00), + wwH16(12,E3,B8,69,E5,C2,E8,30,40,76,D9,6C,CE,E3,27,CA), + wwH16(06,DE,F5,D8,1A,19,63,C9,E1,EF,A6,C7,DB,AD,70,1A), + wwH16(79,93,CC,78,D4,8C,6E,CF,0F,09,BB,20,73,46,5F,D7), + wwH16(FD,1D,CD,C4,20,0E,DA,E7,CD,E2,41,B4,6E,86,C6,46), + wwH16(27,DE,6B,83,14,10,F9,50,04,7A,0B,3F,1D,76,C6,F1), + wwH16(D1,95,7A,11,F1,5A,29,08,3E,3D,AE,2B,54,DE,30,3E), + wwH16(E1,29,29,75,71,C7,91,CB,51,D9,48,DE,CA,F3,00,5E), + wwH16(FD,B6,78,2E,29,69,D2,40,2C,DC,23,3F,20,51,21,E6), + wwH16(49,58,C8,FE,62,10,14,1A,8B,3F,AC,1C,57,09,30,93), + wwH16(1A,24,7A,53,34,9A,D6,7A,D0,F1,D8,F7,D1,D8,C8,B1), + wwH16(55,89,8E,FB,09,97,60,61,4C,B0,9C,F2,5E,7B,90,F2), + wwH16(34,14,58,D4,C7,8D,58,F4,A1,9C,07,5E,5B,A3,6B,94), + wwH16(CD,B1,B2,9D,A3,5E,F5,72,78,9E,7A,A1,26,36,89,FE), + wwH16(ED,9B,91,EA,C3,3D,22,B3,10,0B,E7,AC,A7,4E,38,20), + wwH16(71,95,67,62,3C,8E,83,36,0C,5C,B7,6F,ED,A1,A0,BD), + wwH16(F0,02,EA,6F,41,F0,2D,96,79,77,90,45,1D,E2,DC,47), + wwH16(12,AC,E3,8C,C1,08,62,99,B3,64,FB,36,F9,6B,BC,52), + wwH16(E4,13,3D,7F,F9,CC,53,CB,55,65,36,79,22,48,8F,06), + wwH16(2B,76,C3,01,97,4F,0D,D7,98,5C,7B,40,99,0F,6D,C3), + wwH16(19,EA,20,79,4F,CF,8B,6D,83,40,27,63,29,09,50,63), + wwH16(4A,2B,5C,36,87,74,BA,B8,F3,B2,97,56,EA,A1,C4,6C), + wwH16(04,CF,10,87,88,A2,7B,1E,F3,D5,05,5B,F7,DF,11,0F), + wwH16(6E,5E,E9,8A,EF,40,BD,80,A0,89,C7,94,D8,0D,A3,F2), + wwH16(C2,3F,BF,E9,36,8C,C2,B2,A1,ED,B5,81,84,10,BB,6B), + wwH16(62,20,A4,58,70,22,A5,72,70,5C,64,21,54,2F,28,36), + wwH16(00,53,52,8B,0B,A4,3A,95,D8,CF,8B,2E,07,1F,44,27), + wwH16(C8,87,89,92,56,EF,EF,17,6E,03,7B,F6,A6,58,9F,7A), + wwH16(BA,BF,2D,17,92,1D,B5,97,2F,4C,F5,4C,25,6C,C4,CA), + wwH16(8B,D9,A3,15,E9,51,0C,2A,00,F7,1B,5D,10,4A,AD,1B), + wwH16(D9,4B,30,B2,D8,C7,4F,84,D5,2B,D8,9A,C2,B1,B2,44), + wwH16(CE,0C,C4,89,6C,99,17,C5,6C,B7,6B,CB,4F,5B,00,82), + wwH16(CA,8F,2A,95,EB,5D,60,42,F4,70,F1,3F,C7,51,B8,18), + wwH16(70,E2,42,93,CF,4C,0D,3C,2F,2D,9D,BF,68,71,86,F6), + wwH16(B4,2B,E6,69,8B,8D,D9,C7,85,A6,0E,FA,04,9E,4B,C9), + wwH16(26,52,D4,A2,F5,09,32,4A,33,06,C6,B5,1C,71,BE,C0), + wwH16(80,31,96,40,DD,3F,41,B8,8F,42,66,EE,84,6D,C5,CA), + wwH16(8E,69,0E,53,B2,F3,A9,60,2A,DC,62,51,B4,FE,4C,22), + wwH16(DC,B4,86,4B,03,BC,73,5B,D7,56,EB,07,E5,3B,3B,39), + wwH16(97,05,CC,8D,44,D0,ED,FB,98,80,21,76,DF,AA,4D,63), + wwH16(65,2C,D5,46,51,DA,9A,FF,D9,99,AD,8B,20,CF,68,EB), + wwH16(5C,B7,85,E4,61,82,CF,8A,47,1B,82,78,2C,8D,59,48), + wwH16(16,6C,56,49,A9,64,E3,C6,F1,88,80,14,B4,EC,DF,A5), + wwH16(13,77,DF,C7,D5,EB,3D,3A,54,70,0B,66,48,34,F7,4E), + wwH16(70,E4,23,7E,2A,A2,08,BA,64,90,D6,F3,81,76,80,4A), + wwH16(40,C7,08,54,D9,1C,BB,8A,75,FA,98,4A,74,1A,82,A7), + wwH16(E7,6A,B7,66,7B,90,01,D3,35,C6,EC,A4,1B,25,D4,00), + wwH16(96,15,BE,F6,6A,55,6E,D2,3A,9E,B7,39,DB,2D,A7,08), + wwH16(60,84,81,7F,31,50,0F,FC,7B,23,B6,2D,69,BD,EF,FA), + wwH16(E2,77,92,A1,AF,0A,61,19,91,E8,57,E8,4D,A7,08,F7), + wwH16(6D,0D,C7,47,53,E1,57,1E,3D,C5,F1,4F,F0,6B,94,3C), + wwH16(1E,D2,CF,BC,61,64,F9,22,6F,86,9F,5E,B4,B2,49,CF), + wwH16(D0,E0,A4,03,33,E0,85,56,1C,AB,12,C1,7E,F6,9E,2E), + wwH16(8D,C1,D5,84,7D,79,58,52,02,A8,57,21,4F,53,29,F3), + wwH16(A3,79,55,08,79,FD,AC,AF,F9,86,D6,7C,6D,5C,F1,A9), + wwH16(A1,5D,71,DD,01,EC,6A,8E,7C,B9,E6,00,58,A4,B0,F6), + wwH16(38,EC,2E,38,56,78,5A,17,C5,32,EF,34,A7,62,1A,E7), + wwH16(BE,2A,C3,EA,EF,AB,77,FD,E6,73,E0,80,6C,4C,A1,01), + wwH16(91,D8,7B,C9,D0,E3,0A,32,8C,59,0D,4A,CF,11,FC,FA), + wwH16(80,0D,16,68,86,1D,19,A7,FF,77,3E,13,A9,8A,75,48), + wwH16(F5,D9,51,04,2D,C1,C2,E9,C8,84,72,91,4E,59,3E,27), + wwH16(9C,36,6A,FC,E0,35,01,B8,89,46,1A,51,35,01,0D,BC), + wwH16(54,60,B7,16,9F,8C,EF,D7,28,58,33,31,06,11,E3,E5), + wwH16(5B,D1,5C,0B,4A,10,EF,3B,00,CE,DF,C2,82,A2,19,41), + wwH16(B2,7C,C5,0A,A2,B9,B3,E8,F1,48,62,C1,D9,16,95,5C), + wwH16(2F,24,1C,B3,79,06,FF,90,AA,ED,7D,EE,71,02,85,E7), + wwH16(9C,66,C2,69,C3,9D,6F,AA,B8,D6,8B,5A,57,D0,DC,F4), + wwH16(7C,90,43,AA,78,8B,AB,2C,B9,2A,70,FC,19,AD,64,7A), + wwH16(34,0B,CC,30,DA,73,EC,89,C3,FF,96,BE,E8,45,AC,07), + wwH16(5E,16,B3,B3,9B,43,E1,93,99,06,F9,C6,43,45,24,D1), + wwH16(4F,98,82,96,38,72,C7,61,7E,21,4A,54,60,BC,86,74), + wwH16(56,15,4A,72,3D,4E,7F,80,33,6A,05,A8,98,73,EA,5F), + wwH16(21,35,CF,15,00,22,F7,2F,6E,F5,8A,0A,D7,7A,8E,DF), + wwH16(F6,F4,C7,A4,B7,EA,F2,B6,62,B4,84,9A,B3,18,E1,EE), + wwH16(25,4D,67,42,A2,C8,17,64,D1,3A,62,13,C8,CC,85,54), + wwH16(67,75,A8,DC,A6,5D,2C,49,F8,31,AC,ED,8E,B5,95,BB), + wwH16(23,38,1F,39,D2,B0,44,02,33,00,8E,A4,2D,D8,48,8E), + wwH16(E7,2A,08,38,57,7E,69,57,B2,78,32,FE,52,EB,98,5F), + wwH16(83,89,EF,DD,43,2F,B1,87,0C,BB,23,1C,AF,85,7F,92), + wwH16(77,23,73,AF,2C,90,7B,E7,C4,B4,B2,3F,25,DF,CD,33), + wwH16(BF,2A,BB,7B,4A,F0,B6,4D,A4,17,A8,5E,91,FC,8F,FE), + wwH16(3C,FD,28,01,BA,29,18,83,23,18,A8,D8,59,F2,E9,41), + wwH16(FD,2F,B1,C0,9F,F8,C1,8A,AB,56,FE,85,9E,BA,86,BE), + wwH16(77,7C,68,C2,54,97,12,57,EC,21,14,9A,5D,DF,C3,4A), + wwH16(EF,C0,3A,70,2F,AB,BE,60,63,33,E4,9C,BF,B2,A4,74), + wwH16(B8,0D,01,8B,EF,53,35,60,2E,EA,5B,35,24,48,BD,40), + wwH16(74,87,33,8D,27,12,70,D3,7E,3F,71,1E,7B,FA,34,2D), + wwH16(C5,3B,FC,36,9A,45,A8,D9,6B,A8,FF,09,AC,C6,E2,E2), + wwH16(87,8D,14,AF,17,56,B5,CC,03,2F,BE,B8,B2,9F,D6,3B), + wwH16(84,03,2F,79,EC,F7,F3,DA,6B,86,87,50,46,DC,46,5A), + wwH16(49,31,40,6F,58,CF,7D,57,CC,7D,08,A8,88,03,33,FB), + wwH16(E8,5B,84,4C,45,1C,9F,BE,F7,A4,1B,10,33,E3,6D,86), + wwH16(92,8D,5C,6E,13,03,11,8D,B9,8F,F3,08,5E,53,AE,BC), + wwH16(37,86,C1,F6,B0,A4,C1,59,5C,2C,2A,C0,15,BD,21,21), + wwH16(BE,5E,8C,74,69,B0,07,8D,B0,03,C7,8A,EA,9E,3C,80), + wwH16(3A,33,37,63,6E,88,23,76,96,79,2A,C4,59,32,8A,6D), + wwH16(F0,16,B9,D4,A8,7E,BF,42,92,4E,67,05,72,E4,C6,07), + wwH16(BA,EA,D6,16,FA,90,B4,E6,A2,30,AF,13,3F,88,37,A4), + wwH16(73,AD,F7,3C,9F,E4,2E,37,E6,D8,DE,0B,78,6F,37,D1), + wwH16(51,0E,4F,51,6F,C9,A6,60,3B,2F,0B,B0,E8,02,3A,1A), + wwH16(CF,88,F2,5D,F4,C5,88,B5,A0,65,E1,DC,35,77,86,93), + wwH16(7D,1F,65,31,CA,70,3E,8C,50,CD,CC,34,8F,40,E9,84), + wwH16(B3,00,F0,CC,B4,1E,33,D4,BF,80,A3,40,FA,BF,A2,28), + wwH16(4D,AC,44,CB,97,CD,B3,CB,9F,91,DB,E2,30,99,44,71), + wwH16(F2,81,3C,69,1F,6C,48,39,7E,E4,4E,66,4C,C1,EB,77), + wwH16(C9,0F,A9,13,2A,D1,FB,98,18,D2,D0,EF,43,97,BE,B2), + wwH16(DF,66,2E,FD,D8,13,F5,49,BE,EC,FD,57,38,75,5C,14), + wwH16(9C,E3,5F,72,01,11,C1,32,94,3D,4C,4F,1B,DD,B9,79), + wwH16(CC,D2,0E,82,13,9D,BA,E2,5C,BE,B3,47,09,57,BC,32), + wwH16(C4,30,7D,7F,A3,09,05,F8,E8,48,3D,02,FB,38,00,1F), + wwH16(2B,9B,55,3E,C7,34,3B,AF,A5,88,C9,0C,15,01,DE,D9), + wwH16(B7,77,57,41,C9,B0,6F,F9,6E,82,21,9F,22,39,4C,3F), + wwH16(1A,D3,8D,55,BC,C8,08,90,20,79,F9,AD,7C,A1,BF,84), + wwH16(56,87,3E,26,E1,BA,8C,0D,21,2A,2C,F3,F5,FF,7C,B3), + wwH16(D9,15,C1,E0,BE,5F,7C,E4,C8,5E,80,56,BA,A7,51,31), + wwH16(6F,DD,14,20,33,E5,8F,28,A8,47,B5,A6,B5,18,73,80), + wwH16(D3,64,5A,A3,3A,B9,90,85,12,CB,DD,A4,71,38,1F,BD), + wwH16(C9,C9,0C,53,BA,98,C3,3E,28,AB,7B,22,38,7D,57,98), + wwH16(20,37,7B,2F,5F,A9,B5,13,04,5F,92,66,EB,79,C6,D1), + wwH16(C8,7E,07,36,D8,F5,47,02,48,45,FC,8C,94,A9,2F,79), + wwH16(6D,3B,86,CB,08,FB,D4,A5,0E,28,B6,C9,66,A4,2A,44), + wwH16(FC,17,FD,79,5F,D4,1B,8F,F6,BA,B6,65,0E,CA,D9,1C), + wwH16(B1,46,DE,FD,1A,93,97,DD,5A,E7,F9,B6,04,DE,89,6F), + wwH16(03,85,A2,21,69,00,4C,B7,F9,10,C1,9B,11,F2,A6,50), + wwH16(53,EC,92,14,B6,AD,B5,0E,22,0B,83,9D,5F,AC,60,A3), + wwH16(FB,05,4B,90,7C,E2,12,41,84,CC,93,C4,39,59,63,05), + wwH16(5F,7A,A2,DA,4F,09,98,AA,FD,3A,26,E7,76,4C,2F,C7), + wwH16(60,7B,E6,81,E0,9D,4C,0B,39,0B,BA,BC,8C,18,20,E5), + wwH16(24,EC,1C,98,98,B4,8B,AD,26,80,9D,1E,46,92,CF,CA), + wwH16(B0,88,51,BB,B8,19,95,E6,FE,55,C2,52,65,5F,73,9E), + wwH16(74,54,09,B2,00,33,FC,EE,4F,01,0A,02,C2,69,E8,DF), + wwH16(05,98,8E,57,F4,E4,2D,EE,A6,52,1A,1E,C7,59,6E,8A), + wwH16(11,A9,2B,08,B6,CD,04,7B,74,4A,95,D8,46,18,91,33), + wwH16(A7,0D,A4,C1,67,DF,06,D5,0A,DA,1F,1D,D2,52,A5,CE), + wwH16(4D,56,D9,05,68,9D,84,78,0D,F4,43,E9,61,5D,E5,2E), + wwH16(02,96,59,7D,D7,52,36,36,DA,66,EF,A1,F5,6E,4E,E7), + wwH16(DF,38,F3,F2,CD,63,DA,8C,0E,D7,5B,C4,84,68,FD,65), + wwH16(07,3B,A5,49,CE,65,09,AF,11,57,74,67,F1,A0,A2,68), + wwH16(8E,2D,AC,BB,76,93,41,4A,54,3D,9C,3C,CE,B7,15,EF), + wwH16(30,2A,9D,7F,03,63,62,DA,5C,C2,58,15,2A,E3,BC,94), + wwH16(36,63,0F,A5,A9,B9,3A,E4,CC,E2,BC,72,D7,EE,17,62), + wwH16(F7,42,9A,85,DC,0C,76,81,1A,BD,92,09,F8,2B,DE,24), + wwH16(0E,3F,FD,79,FF,14,BB,75,9B,35,FD,4D,86,4C,76,8C), + wwH16(3F,10,0C,DE,61,E0,DA,D1,17,6B,A0,FC,E0,72,2D,59), + wwH16(76,6D,E6,D9,5E,8C,BE,CC,81,2C,52,CC,02,2A,3B,7D), + wwH16(0D,CE,C3,80,71,AC,27,BA,83,8E,AA,B2,8A,63,C7,C0), + wwH16(A7,C6,56,C8,E3,71,97,28,80,87,B1,2E,FB,DD,D9,8D), + wwH16(2C,93,A4,87,F8,BE,66,4E,BF,DA,E8,54,B0,4C,A4,33), + wwH16(7D,DF,BA,61,BF,0C,CC,9F,1E,D8,C1,4C,23,87,32,FB), + wwH16(6E,A3,05,5B,DB,4B,49,40,A6,A9,C5,02,FF,E4,BF,2A), + wwH16(7C,E8,2B,8C,4C,43,47,07,33,A1,95,5B,75,B6,D9,6D), + wwH16(4B,8D,D7,78,74,1E,E0,55,F7,09,BA,B1,97,E1,63,F2), + wwH16(AF,84,81,3F,A1,85,86,54,EF,89,46,D5,52,B1,3D,51), + wwH16(14,A4,BC,87,3D,92,88,C7,C7,7A,91,EB,36,F5,11,8A), + wwH16(28,BD,82,29,2B,CD,3C,3C,FB,CA,EC,B2,EA,23,08,71), + wwH16(BE,E0,78,8A,05,24,AF,F8,BA,E0,49,54,AD,12,4A,5A), + wwH16(FF,5F,8A,B4,B1,49,02,F2,F8,D4,ED,4F,8C,05,92,D8), + wwH16(C2,B7,BB,1B,BB,89,A9,41,3F,88,46,DF,B2,FA,49,A9), + wwH16(8B,25,92,ED,CF,AB,90,FF,9C,66,28,94,94,27,FC,4D), + wwH16(60,95,F7,B6,ED,7A,62,33,9A,FF,3E,4B,56,C1,1C,25), + wwH16(35,FC,F5,00,B2,83,2A,84,B2,55,C9,61,18,90,45,DE), + wwH16(84,35,56,45,AA,4A,D3,81,FC,37,4E,47,51,02,32,B6), + wwH16(C1,27,B5,46,CC,43,70,53,8C,73,F0,1D,59,FA,8B,CD), + wwH16(39,02,C9,99,04,4E,84,E5,76,B3,50,6E,FB,9E,4E,E7), + wwH16(E3,FA,EC,6E,25,AB,3F,19,41,02,C1,CC,B7,2C,9B,CB), + wwH16(5E,8D,EC,5D,78,CA,E5,CC,0C,84,DE,AC,CC,CF,EC,40), + wwH16(65,0E,EB,D1,8E,89,4D,C8,40,BD,59,36,E8,09,D9,67), + wwH16(D9,F9,34,42,15,86,C1,D2,CF,8C,AD,DC,1D,70,2E,26), + wwH16(FC,48,AE,04,9B,8B,94,E9,1D,C9,05,43,B1,89,62,04), + wwH16(DF,22,7D,D8,4D,30,1B,C1,7B,AD,DA,A4,BE,CD,C1,18), + wwH16(F5,32,60,B1,55,38,A1,BE,9F,6E,64,D6,E4,A5,F3,9A), + wwH16(F6,34,D4,DB,D6,69,45,46,F0,CB,9C,7F,D1,B9,88,A6), + wwH16(47,AD,65,0A,F7,43,D8,EE,3F,8F,53,2C,8E,09,12,3A), + wwH16(C1,15,D8,AC,CE,9C,4E,54,8F,57,B3,C9,50,49,55,FB), + wwH16(D6,65,17,21,92,18,11,BE,7D,86,98,98,5B,49,72,19), + wwH16(C0,DF,B0,24,49,EA,FF,26,E1,6D,A2,CE,03,7F,3A,2B), + wwH16(99,9A,F4,A2,43,A6,BE,0D,FD,AF,F3,5C,A6,0D,24,15), + wwH16(4E,A5,0D,30,51,C2,66,28,0C,51,92,0A,FC,50,8D,AD), + wwH16(D8,F2,DB,8D,B1,47,06,80,16,F1,A1,9A,93,03,4A,20), + wwH16(1B,9D,21,64,7D,87,FD,4E,DE,21,AF,AE,EC,C1,B1,17), + wwH16(E3,53,F0,49,A4,D7,94,9F,83,67,36,3F,BE,2E,6C,54), + wwH16(D4,C0,53,92,2D,93,54,50,A3,7A,1E,F5,1F,FB,DB,44), + wwH16(64,AA,60,C1,C8,21,C4,9F,F1,1E,C5,5B,DD,7B,92,00), + wwH16(89,7C,44,B4,F0,CB,AC,04,8C,0D,BC,29,E5,64,BF,03), + wwH16(08,74,63,30,CB,6F,3A,E3,95,FB,E8,AF,00,E0,3A,8C), + wwH16(19,E6,A5,59,91,0F,F5,75,9A,70,00,41,7D,F4,CB,DA), + wwH16(EE,B3,91,9D,56,54,A7,73,4C,94,F6,92,4B,2A,55,63), + wwH16(C5,AC,AD,06,9F,66,D2,6B,41,6C,C5,9B,C1,34,CD,61), + wwH16(15,80,0F,CD,EF,B8,F7,C3,82,65,24,5B,0E,F5,3D,E7), + wwH16(82,8C,E5,EE,B2,E3,4D,A9,59,DD,91,09,51,E3,B0,A6), + wwH16(EC,E7,8F,F9,42,47,8E,FC,C3,CE,EC,2A,C7,DE,83,CC), + wwH16(49,7E,D2,60,EA,7F,13,DF,20,8A,9E,E7,0F,78,49,8E), + wwH16(91,8A,BB,DA,0B,CD,1F,57,F8,37,D0,42,41,15,F8,42), + wwH16(17,35,7B,CF,95,06,81,CA,4E,F8,D1,6A,DE,72,5B,BB), + wwH16(57,AD,DB,4E,21,41,77,0E,60,87,4F,C6,E7,13,F7,06), + wwH16(E0,CE,38,06,EE,D8,9F,C5,E7,BD,49,4B,B9,F9,8C,D6), + wwH16(74,A7,93,9B,E3,34,4C,78,08,A2,D3,25,32,F0,AA,FA), + wwH16(50,A0,73,A5,30,61,98,52,A5,4E,03,91,32,93,11,45), + wwH16(12,94,C8,C8,EE,12,04,72,79,CD,AC,82,80,EF,B4,05), + wwH16(AD,5E,24,16,39,13,74,51,7E,78,E6,FE,B9,8E,2A,64), + wwH16(E2,FB,24,CD,5F,FE,D7,01,CF,CC,F3,85,5F,8D,F9,EE), + wwH16(CF,44,43,7D,87,91,04,BA,B8,9D,91,7B,FF,C4,88,6B), + wwH16(62,BB,40,1E,A0,85,19,B9,9C,A1,5B,93,7E,02,88,10), + wwH16(D7,C4,76,CD,7B,15,7D,77,C8,69,2F,38,62,DA,7C,5D), + wwH16(20,14,61,1A,DC,46,80,E8,72,EA,CD,E3,72,9C,D5,5C), + wwH16(30,0C,6E,C7,F0,35,3F,99,48,A4,C8,36,A2,0D,A1,39), + wwH16(0D,62,AC,70,98,2C,12,DE,B9,A2,75,55,CB,7E,E0,C8), + wwH16(CB,87,BC,EE,6A,8E,63,A0,19,C8,3F,2B,52,23,0B,20), + wwH16(E5,E8,BB,20,33,CF,38,72,16,A5,6F,A8,BB,9B,70,E7), + wwH16(FA,83,47,5D,A0,37,89,8A,36,8B,63,7F,74,3D,FB,3F), + wwH16(28,AB,C7,F7,6F,43,9F,48,EE,72,E6,42,43,1E,41,84), + wwH16(06,B1,35,5E,09,D0,9B,63,E3,F0,60,64,C7,6F,90,1A), + wwH16(94,D7,50,56,1A,90,AE,E0,A0,AD,EB,04,78,2B,DA,58), + wwH16(65,A1,AD,0D,B1,1E,28,CB,AF,74,C2,01,2F,AF,E1,20), + wwH16(32,76,E4,CF,EB,9D,73,42,26,A7,42,F2,38,91,8A,9C), + wwH16(56,BE,9E,E3,50,18,D5,3F,D8,91,6F,68,01,3C,B3,B7), + wwH16(28,25,25,14,E7,F2,FF,8A,9F,99,5D,97,14,F9,39,D9), + wwH16(D9,5E,69,E0,5D,D8,EA,72,5D,2C,2C,D0,56,4F,3F,2E), + wwH16(D7,87,9B,28,AE,C0,76,E7,FD,84,89,3B,CE,54,A0,81), + wwH16(B8,64,A3,78,AB,AB,AB,CB,E5,25,22,A9,CF,58,38,90), + wwH16(19,FB,58,85,A8,3F,0F,35,F8,45,C3,26,FE,FD,00,35), + wwH16(E2,7E,F2,F3,50,41,3E,E7,67,66,19,B3,3E,36,A5,34), + wwH16(BB,AA,A0,58,C3,4B,A4,74,CF,41,6E,FE,9E,57,7D,3F), + wwH16(30,5E,E1,C1,B1,D8,35,C2,19,9D,91,78,56,1C,2C,43), + wwH16(D4,E0,8D,D6,D2,52,60,28,3E,9D,7D,1E,91,AB,59,F9), + wwH16(2A,1E,C5,13,99,3F,13,E1,7C,C9,8F,81,B3,03,79,A5), + wwH16(B6,FB,E6,BF,D6,83,94,13,76,C7,9A,E2,73,34,5C,F6), + wwH16(7C,FE,E1,9A,06,DA,6E,13,4B,C5,17,7D,E7,D0,8D,E2), + wwH16(49,EC,7D,DA,B3,18,8C,D5,CC,05,FE,A6,4A,DA,56,D1), + wwH16(2E,D7,B8,AD,D2,86,4A,BD,6E,82,C9,3B,60,7A,FB,72), + wwH16(AC,67,E8,3D,03,A3,D0,5E,51,89,B5,8B,D6,F6,91,B9), + wwH16(BA,83,94,56,36,5E,D8,AC,23,B2,7B,95,2E,FF,78,9C), + wwH16(89,B8,27,FD,38,25,BD,F6,ED,3C,14,43,CB,7A,39,0C), + wwH16(03,F9,4F,68,C9,6B,A2,3C,7B,7E,A7,63,F6,9B,E8,1F), + wwH16(85,EA,B0,7A,01,E0,C9,7C,96,24,95,D0,95,0B,28,F2), + wwH16(8A,EC,45,6A,63,08,29,A3,CC,10,AA,87,E9,DE,AF,73), + wwH16(21,13,0D,82,A2,51,DD,9D,8E,64,AA,99,FA,52,E2,77), + wwH16(FA,01,32,EA,42,FE,B0,D1,40,FE,98,3E,70,6A,FA,9C), + wwH16(19,CD,78,30,AC,59,66,BC,7D,7F,15,1C,87,AD,8B,36), + wwH16(F4,75,C6,C2,D2,AB,0F,68,09,ED,12,42,7B,81,42,9A), + wwH16(9F,46,74,39,E8,6B,83,C6,1D,19,0C,48,87,AA,2C,7C), + wwH16(88,A0,B2,9D,F0,D0,8D,D8,0F,D6,ED,41,16,49,FB,7A), + wwH16(4A,D4,1D,A2,F6,3A,56,5C,5D,09,C2,46,15,D0,CA,7E), + wwH16(E0,5A,EA,B0,50,54,A0,5F,56,BA,42,E9,3B,7E,68,46), + wwH16(B6,C4,30,A4,A6,D1,BF,9C,03,E9,D3,46,EA,94,CF,88), + wwH16(B2,E4,21,A0,7D,24,EA,69,A7,8A,B7,4C,22,1A,E6,DD), + wwH16(CD,68,BC,FC,0E,1B,14,A2,18,66,1D,84,C4,E1,C9,32), + wwH16(79,02,FC,DF,AA,1F,F0,5C,02,EB,5E,06,44,E1,AD,FC), + wwH16(2A,1A,DC,01,67,65,9D,74,48,9A,FF,9A,4F,BB,DE,06), + wwH16(65,FE,82,3D,D9,F1,0E,8F,DE,39,1C,73,F0,AD,0B,F6), + wwH16(1C,3A,78,5B,D8,75,37,D7,F2,CE,C7,4E,E4,FD,31,5E), + wwH16(B1,E3,A1,62,22,FF,4D,A3,F8,43,18,9A,E0,C6,D8,92), + wwH16(76,CD,57,84,3C,79,BC,1C,E1,95,6D,5B,B4,F0,20,1B), + wwH16(C4,12,F6,37,B2,D0,38,1B,D2,75,C6,AC,E0,70,EE,2A), + wwH16(2A,FD,D4,D6,86,31,DB,AB,83,3A,AF,A1,84,A4,23,84), + wwH16(61,BA,CD,B2,75,68,78,24,F4,A2,18,70,48,40,D0,07), + wwH16(9E,9B,1B,0A,E8,E2,76,24,70,B0,A3,7E,44,CB,9C,7C), + wwH16(B1,09,9B,16,0C,42,E7,8D,6C,90,E7,C5,82,0B,A5,88), + wwH16(47,44,7B,FA,4B,89,75,1E,0D,D0,B8,B2,E8,0A,B2,6A), + wwH16(05,A7,69,FD,A0,75,4A,2B,41,7C,CF,1C,0B,DE,00,EF), + wwH16(0D,F1,5D,B9,A3,17,15,34,98,3A,F1,48,4E,09,F0,12), + wwH16(8D,06,AF,C5,1D,6C,6E,C6,D0,61,49,6F,53,79,2E,F1), + wwH16(AF,B8,7B,4B,53,FF,1A,39,BF,17,1E,49,73,CC,0A,62), + wwH16(73,DB,24,B8,6B,94,A6,61,36,CE,B3,85,C9,9E,7C,09), + wwH16(E2,56,F9,EF,0B,2A,8A,6B,0A,6B,A1,E3,6E,25,24,AE), + wwH16(E5,8C,02,00,7E,FF,8C,EA,52,F5,8A,C5,EE,C4,76,D8), + wwH16(46,05,8B,EE,1D,5E,E9,C3,E6,00,22,B4,A7,F8,D9,80), + wwH16(CC,19,B9,93,D5,EB,B1,0C,09,D8,E7,4E,7E,E1,BB,20), + wwH16(6E,F7,DA,63,4A,40,77,C7,C8,29,5E,FE,DD,EB,40,CC), + wwH16(59,93,F2,AD,C5,74,61,53,CA,BA,AB,CE,EE,51,A9,59), + wwH16(44,C5,9E,A8,7B,B7,46,9C,24,B0,0D,C4,1D,D7,81,54), + wwH16(00,A3,CB,3B,02,DE,38,40,A9,A9,99,4D,08,12,7D,06), + wwH16(89,B1,05,01,16,2F,2B,A7,98,88,2E,44,E8,2A,71,92), + wwH16(E9,EE,F2,7A,A0,26,BD,32,A4,7B,08,BB,7C,D4,AF,88), + wwH16(F0,92,F6,E2,C6,01,10,47,E3,40,B5,7D,CD,C3,4F,76), + wwH16(1A,B6,6C,F1,72,CB,88,5B,D5,54,37,D0,E4,C4,16,4C), + wwH16(6A,B0,DE,65,54,17,74,63,33,85,DB,18,69,D3,3D,E2), + wwH16(00,24,2D,52,66,75,C2,83,71,B8,3A,F5,E8,D8,ED,1B), + wwH16(EB,EA,47,D0,A9,5F,C1,CE,C7,BC,68,A7,02,F3,70,A4), + wwH16(5E,D0,4A,7E,77,71,AD,F6,CD,5F,83,FF,F1,2B,6D,3C), + wwH16(5E,F1,AB,43,DF,8F,1C,D3,B7,7A,D1,F9,59,C7,2E,91), + wwH16(31,F7,17,CC,A5,CC,65,17,3E,FF,FB,D9,14,44,4E,CB), + wwH16(FB,8E,82,37,8C,C4,EA,5C,DC,F5,2A,89,D3,B5,0E,5A), + wwH16(7B,56,EE,99,46,4E,FA,90,A3,A4,3A,EF,77,65,D1,7C), + wwH16(F4,21,20,9E,17,18,46,95,1C,D6,53,15,67,F8,18,52), + wwH16(07,7E,B7,07,4A,07,F5,1A,61,1A,E5,56,4E,63,D5,6B), + wwH16(66,7D,84,48,BC,49,4B,A0,35,DB,E5,A0,7E,68,94,8D), + wwH16(C1,31,25,43,CF,5F,5C,00,F4,15,74,90,CD,A3,FF,1F), + wwH16(48,5C,35,D6,9D,E3,29,FD,6D,FB,E1,F1,BF,56,29,4C), + wwH16(0B,CF,34,3D,AC,0D,EC,04,D5,E1,0C,62,73,AE,13,26), + wwH16(83,49,E7,A5,F2,F8,67,FF,D6,1A,AF,29,08,CB,09,07), + wwH16(DB,BB,08,DE,BE,44,AC,30,96,EB,6E,5F,16,20,94,F7), + wwH16(29,DC,00,53,56,02,6D,8C,02,8F,F7,42,B9,DE,61,D8), + wwH16(36,14,07,10,51,F4,4A,47,5E,9E,25,46,75,FA,14,15), + wwH16(42,D7,E5,D4,1A,4C,B8,F3,D7,D4,E7,C2,3B,AD,E1,B8), + wwH16(56,4D,F7,41,16,C4,5D,0B,1D,CD,F7,02,E1,E3,B6,AB), + wwH16(36,84,28,13,47,BF,4F,AD,A4,35,1A,D8,87,86,83,19), + wwH16(39,4B,1E,05,3F,9B,73,9B,9F,DB,1F,AC,DF,7B,D2,0A), + wwH16(EC,61,CB,2A,E6,48,E0,B8,64,1B,2D,84,28,95,B3,3C), + wwH16(4B,1F,C6,B4,C3,96,BD,28,40,26,A5,27,30,6C,8D,38), + wwH16(CC,06,77,09,09,83,9B,43,7F,92,35,DD,84,68,25,33), + wwH16(ED,40,3A,B8,7F,93,4D,2F,01,82,83,73,E5,73,AE,7B), + wwH16(EB,D7,FB,3D,B9,6E,E0,A7,7C,86,08,37,5B,34,52,AF), + wwH16(F1,9F,90,78,44,68,3C,8C,12,CC,F8,4E,13,60,76,A2), + wwH16(F0,C7,BB,1A,CF,23,0C,A3,E4,E8,C4,55,2D,CF,90,3E), + wwH16(59,34,31,2F,B5,D7,E8,55,22,F2,4E,B5,70,B6,D6,C4), + wwH16(3C,63,2F,DC,83,36,40,52,36,BF,20,44,7E,67,F0,56), + wwH16(48,C9,C9,D8,67,24,19,BE,A4,8D,77,AD,FA,48,70,F3), + wwH16(20,BF,A9,21,88,83,A8,27,21,62,74,15,BD,38,09,36), + wwH16(A3,E4,D1,B9,07,7A,ED,CC,29,25,C3,0F,9C,ED,AF,90), + wwH16(6E,4A,4D,8A,B8,53,D9,08,94,C9,8D,6D,A9,BF,26,EC), + wwH16(B3,47,1B,90,16,4A,BF,3C,CE,05,B2,EA,B0,B4,41,09), + wwH16(A6,56,03,42,3E,12,9D,29,5A,22,49,5A,07,E6,B0,C2), + wwH16(8C,32,54,DF,1B,29,BD,B8,63,09,01,19,28,F6,F7,3C), + wwH16(79,6D,E9,23,D6,35,FD,2F,22,6A,30,95,B4,CB,EF,51), + wwH16(9C,74,40,FC,10,BA,1A,ED,81,F3,8F,25,76,DC,4C,62), + wwH16(2E,7B,01,5A,10,84,7F,04,20,7A,A8,3D,AD,C9,0A,CE), + wwH16(6A,66,86,D4,79,3A,23,3D,C2,88,E6,81,D5,3A,14,4F), + wwH16(1C,3B,31,59,E9,DA,AD,3F,D2,88,D4,C8,EC,31,C0,9E), + wwH16(B5,AD,A1,59,16,AE,8F,2E,24,47,0B,2D,79,1C,E3,D3), + wwH16(C2,52,51,87,DE,BE,51,6A,D5,9C,C0,AF,1F,B3,C1,DA), + wwH16(89,9B,B4,3E,77,D0,BD,AC,F8,DE,52,51,B5,3A,30,F7), + wwH16(E9,E9,99,65,71,32,6E,B5,96,80,8B,DF,29,40,92,34), + wwH16(06,A7,83,BF,0B,7A,00,D6,22,FC,0C,E4,2A,5A,B7,C9), + wwH16(EA,7F,25,AE,70,30,A3,DF,E9,58,47,BD,A1,32,A5,DB), + wwH16(DF,B6,33,CB,A5,AE,C0,15,80,8C,A0,E4,C9,E8,30,A8), + wwH16(48,3A,E8,CA,68,2D,1D,7C,0B,A1,AC,ED,A9,03,CC,60), + wwH16(32,5A,24,A0,70,FA,0F,42,3D,04,F5,6A,33,D1,08,10), + wwH16(D4,43,BF,59,DE,9C,B7,A2,C5,BC,8B,D4,AD,92,C7,1A), + wwH16(8D,53,EF,73,EA,C7,9E,FE,9D,0C,A1,DA,64,CB,88,43), + wwH16(9A,4B,47,C1,CE,AA,38,34,02,15,93,3A,56,B2,D7,F6), + wwH16(3B,4C,DB,5B,3E,6E,F0,66,F8,1D,EB,B6,35,3D,85,AE), + wwH16(E3,81,80,8A,E5,AD,FF,B6,15,5A,DA,5F,D2,2B,2C,39), + wwH16(33,41,D1,A5,D7,57,A1,27,88,7C,42,55,0E,FF,3F,7E), + wwH16(F2,24,43,67,A6,94,6F,6F,40,23,C7,B1,6C,CF,4B,EB), + wwH16(DC,B5,34,70,2C,B3,79,74,A0,65,77,41,6B,90,91,18), + wwH16(5D,AD,16,17,FD,9E,72,0F,92,83,78,CA,EF,9E,32,F0), + wwH16(B3,42,26,19,04,75,A0,EF,F4,8F,9C,C7,1C,F1,8C,D3), + wwH16(29,F5,BF,A2,AD,3E,F4,AC,BF,64,33,A7,AE,DB,6C,7B), + wwH16(D3,26,5B,28,EC,08,52,2B,31,1F,86,DB,FC,85,8A,70), + wwH16(29,99,73,A0,79,4E,03,AE,A2,61,E6,33,AB,2B,3B,DC), + wwH16(D3,2D,AE,E3,71,47,EB,95,9A,0D,C8,BD,C4,15,08,8C), + wwH16(6B,68,15,76,39,1B,91,1D,F6,40,0C,32,0F,4E,A0,49), + wwH16(09,8C,48,A9,B9,95,D8,CF,BA,75,6F,50,C2,96,A1,51), + wwH16(51,D0,73,E3,3E,DE,AD,B9,0C,74,04,31,5C,81,AA,BF), + wwH16(73,F5,1B,57,CB,E0,21,DD,A7,D2,92,C8,05,40,55,E3), + wwH16(5D,A2,42,2C,C5,CC,08,13,0F,2A,DC,57,A9,ED,D4,5E), + wwH16(FD,BB,73,11,5C,0E,AD,E4,C3,07,B7,EC,D8,AB,2C,96), + wwH16(CC,F7,8D,F2,DA,9B,5C,24,95,7E,63,98,1D,6A,6B,BD), + wwH16(DC,99,C2,BA,F1,56,5E,69,20,5E,88,B5,25,F2,C1,37), + wwH16(75,9D,28,6D,F4,A0,15,3E,EE,58,7F,0F,D1,05,4D,5C), + wwH16(B9,E8,42,0E,75,22,71,55,46,9D,BF,27,F1,FC,3F,3A), + wwH16(8F,23,1A,BE,15,F4,53,69,F6,52,35,CB,5C,74,CF,7E), + wwH16(DA,40,26,7E,F8,5D,EF,3E,3A,E2,1A,09,A5,3C,15,AD), + wwH16(82,CE,07,D0,89,BE,CA,94,09,F9,68,7B,10,BE,97,68), + wwH16(80,56,9F,C2,68,A1,29,72,B6,98,CC,87,57,1E,85,44), + wwH16(BE,C0,F3,34,01,F9,AC,0D,FA,53,F3,DC,00,33,6E,AC), + wwH16(AA,12,B8,7D,6B,17,9A,32,B5,6E,03,A8,AA,0E,73,90), + wwH16(79,02,E1,63,AF,5C,0D,B7,C0,80,9F,DF,C3,60,D3,C5), + wwH16(02,F4,A7,CE,80,08,28,55,3D,5E,B2,5F,FC,D4,7B,0F), + wwH16(17,12,2E,13,AF,1D,17,79,73,42,12,4A,0D,40,A9,26), + wwH16(99,06,93,29,54,6C,D2,3A,1A,DE,B6,C3,F0,8F,48,CF), + wwH16(BA,3D,6B,97,E6,9C,02,E3,96,40,AA,A3,67,C9,02,75), + wwH16(F6,BF,EC,21,9A,CA,BA,67,75,F8,60,B5,97,2D,05,30), + wwH16(3F,20,E5,CE,6D,AC,C2,4E,22,EF,DB,F0,F8,AA,15,FD), + wwH16(2B,70,67,F9,7C,37,31,15,97,C3,D8,53,42,25,5F,8B), + wwH16(5D,24,E2,9C,52,D4,9D,F9,EC,B9,BF,1B,AB,15,06,AB), + wwH16(A7,9C,CC,1E,11,CA,91,F5,7D,A2,79,30,1B,B0,31,E3), + wwH16(DF,54,D0,2E,E6,54,3A,A9,92,87,7F,1A,A1,AB,F6,80), + wwH16(19,AE,7B,97,20,B0,BD,DE,C8,76,9B,72,77,66,66,19), + wwH16(ED,FD,BE,D4,4F,2B,B8,57,7E,CD,DD,F3,5B,63,E4,59), + wwH16(1A,1A,60,0E,03,54,DD,55,07,FD,51,AF,C0,D5,07,4E), + wwH16(3A,4B,6C,BD,7D,C0,74,B2,86,EA,78,32,3C,13,4B,69), + wwH16(2C,8E,25,2F,41,15,9D,45,50,9E,86,9F,15,6C,44,E5), + wwH16(74,E9,0A,EA,7D,69,43,B2,CC,36,68,56,FC,02,2D,DF), + wwH16(C3,C9,1C,F5,91,9C,B3,BF,A9,9F,E0,52,1D,16,17,A4), + wwH16(19,0E,73,5E,47,9C,80,70,40,64,79,40,B0,06,0E,C3), + wwH16(93,74,D8,3B,74,E6,97,3A,7C,A0,BC,0D,B3,12,5C,2E), + wwH16(A9,2D,12,4E,2F,B8,1B,1C,47,B8,75,7D,FB,E1,29,E4), + wwH16(04,0F,8A,83,D4,D5,41,06,25,28,31,C4,F9,CD,F0,AE), + wwH16(B6,61,FE,EE,43,8F,C7,E2,64,AF,E3,2E,F7,F8,C5,49), + wwH16(44,65,3F,A5,B6,35,7F,3A,CF,CB,CE,DA,DA,36,9B,FA), + wwH16(26,4C,F2,0E,33,9F,16,B9,88,35,B6,18,49,FE,CA,BE), + wwH16(C0,BC,68,E6,31,B0,F7,E1,55,A3,87,9B,35,0F,89,60), + wwH16(EB,B4,52,FF,26,2B,AF,6C,01,2D,8C,79,C6,46,73,5F), + wwH16(68,37,96,19,36,65,E5,41,B3,D5,C3,03,C9,61,AC,FB), + wwH16(C9,2E,3D,C9,EC,B2,B0,74,F6,3F,9F,67,25,75,9F,C0), + wwH16(98,7A,F5,BC,F9,FC,9C,7E,1C,46,4D,11,F4,B7,EF,6B), + wwH16(F3,C3,9D,D7,B5,DC,20,00,23,9B,FF,28,04,EF,A5,97), + wwH16(3A,C4,46,66,33,69,DD,54,C0,35,14,60,7F,FC,15,7C), + wwH16(7E,FA,44,70,06,78,3F,01,48,8C,62,EB,4F,8B,04,56), + wwH16(06,C2,E1,12,08,3F,0A,0B,1B,31,36,8B,1C,21,DB,3B), + wwH16(53,AC,CE,F4,01,43,6C,4E,89,AE,0D,8B,69,07,0A,3A), + wwH16(76,2F,DF,87,42,4D,57,9B,55,32,ED,2E,8F,21,3D,D8), + wwH16(C2,55,58,04,F9,B2,C2,D5,44,78,AF,6D,D8,57,B1,D7), + wwH16(13,CE,59,E0,F5,45,20,AD,E0,29,2B,0B,AC,0E,FF,76), + wwH16(43,AE,46,5E,54,D8,6A,00,23,F8,F7,EE,53,CA,36,CD), + wwH16(68,60,1F,D4,A8,AE,CD,09,77,74,8E,9E,77,72,08,60), + wwH16(BE,F2,8F,9A,AA,38,81,40,3A,B0,38,3F,88,C7,9E,EB), + wwH16(B4,A0,0C,E1,B0,FC,CF,43,5E,EC,4E,7C,89,78,B7,D2), + wwH16(D0,91,70,9A,72,3C,9A,C5,50,87,A6,58,C9,C7,83,51), + wwH16(AA,F0,27,D4,0A,38,C7,07,41,54,6F,91,E2,FC,11,66), + wwH16(EF,3E,7F,7D,F9,CD,FF,A0,FB,EE,6C,0D,F6,7D,52,2A), + wwH16(87,6F,D6,E8,5C,01,A7,61,21,D2,A0,22,DD,46,4C,C2), + wwH16(E9,1D,43,B7,B6,D8,94,F0,1D,42,5E,C6,6A,CA,DC,E2), + wwH16(EC,F7,AF,55,00,C5,6C,DE,02,AB,DF,15,24,C1,29,DC), + wwH16(25,80,0D,61,6D,82,A9,99,1F,BD,AA,3A,A7,9E,00,9A), + wwH16(AE,43,08,D5,10,53,2A,0B,66,58,84,B0,4B,05,DF,0A), + wwH16(2E,91,68,CC,F9,5C,F4,97,DA,7A,98,FC,B3,D3,88,B9), + wwH16(83,A9,77,2C,36,C1,C7,B5,5A,AA,9B,3C,01,40,9C,4B), + wwH16(6A,B3,E7,45,F6,12,AB,BC,D2,26,A2,C6,4D,65,00,C6), + wwH16(11,41,E9,D2,9A,74,60,D0,99,A4,DF,F5,21,86,4C,AB), + wwH16(D8,60,4A,93,DD,E2,A6,13,E5,CA,EF,F4,01,0C,94,C0), + wwH16(A1,52,7F,2C,96,1F,66,13,22,EA,FF,6D,51,D6,7F,6D), + wwH16(77,9E,8B,94,EF,C5,2D,18,14,77,29,E7,43,91,E0,D6), + wwH16(F0,1F,CB,09,42,7F,30,1A,6D,E8,BB,AF,A7,4D,00,DC), + wwH16(64,EA,0D,B9,16,D7,E4,2F,C2,2E,70,BA,DF,35,CD,A9), + wwH16(A3,5C,EA,31,23,F3,BF,61,C2,22,BA,F3,AB,4E,8F,1D), + wwH16(3A,51,66,3B,80,D3,56,4F,AA,92,24,3C,25,78,DF,D9), + wwH16(F3,6C,46,F0,0B,C0,A8,D2,87,94,7D,0D,14,7B,03,C4), + wwH16(80,F2,67,F7,31,59,B0,64,1A,A2,56,16,45,A0,A9,B9), + wwH16(3F,14,4F,51,C3,6C,6B,6E,F3,20,F0,F6,26,9F,E6,68), + wwH16(D2,F8,C8,70,BB,8A,57,98,61,BB,5F,4A,B0,C6,55,AA), + wwH16(0D,32,D1,13,EA,DA,27,B5,00,2F,7D,17,80,B1,0B,E0), + wwH16(B9,66,3D,FD,50,99,1A,69,3F,B4,A3,C1,A6,6C,AE,70), + wwH16(FF,58,A8,22,87,00,D8,1A,8A,69,64,5C,5F,29,07,39), + wwH16(24,02,4F,55,BD,26,F7,AC,9D,67,79,1B,94,8E,94,AA), + wwH16(81,13,74,61,31,A1,8A,C3,74,ED,69,FF,9D,48,5E,D1), + wwH16(7A,B8,B0,2C,A0,CE,89,15,D2,75,E3,64,6B,E9,B9,DF), + wwH16(DC,D8,DE,6D,DD,76,5E,33,48,24,5D,C9,F4,3E,F5,C7), + wwH16(C4,E9,76,E3,A3,1C,36,7C,C8,03,92,EA,D9,E0,34,4C), + wwH16(DC,7E,76,8F,0A,5F,81,97,55,26,5D,C7,59,CF,B1,EE), + wwH16(82,EC,F9,63,CF,C0,2A,28,63,F0,2B,98,87,E4,74,C1), + wwH16(7C,98,0A,32,E5,C0,3F,82,F6,39,76,3E,C2,60,E9,CF), + wwH16(43,A4,EC,73,DB,10,CC,E0,13,66,84,4A,DC,BF,8E,15), + wwH16(25,34,77,58,CF,4A,A1,D5,99,DA,48,7C,CF,94,BA,8B), + wwH16(58,7C,EC,50,51,BB,C7,79,2C,4C,E5,F8,1F,03,BA,AE), + wwH16(4B,BE,DB,D5,D0,DE,16,7C,1F,75,80,EF,C3,AF,EC,95), + wwH16(71,11,DA,16,78,17,8D,3B,9F,E0,4A,FD,8C,4E,CD,CB), + wwH16(5A,B0,07,82,C9,FE,11,D5,38,7E,29,00,53,F5,8D,AF), + wwH16(8D,69,5E,15,1E,86,19,EB,4B,52,10,38,1C,8D,F8,6E), + wwH16(8A,3D,77,61,26,65,4A,89,BE,F7,FA,15,11,4B,2C,D5), + wwH16(BC,8E,25,01,15,77,4C,20,A1,92,85,43,8B,F7,79,1F), + wwH16(3D,A8,E2,4D,D2,48,21,62,3D,DC,FC,44,80,6B,26,F3), + wwH16(93,48,5A,F8,A9,B3,CA,9C,7C,13,E3,73,F5,BB,65,40), + wwH16(3D,98,86,67,18,4F,67,33,33,71,C6,17,F0,71,DE,3B), + wwH16(CC,F6,DA,90,33,83,36,03,F4,70,2F,DA,3E,16,C5,BB), + wwH16(8B,F7,3A,7C,3E,08,63,18,09,1C,0B,5E,D0,AD,1E,E8), + wwH16(C3,18,5C,7E,33,FE,0E,B9,75,07,0B,D3,E9,7E,C0,98), + wwH16(7E,C3,29,CB,A5,BA,72,3D,EE,96,02,8F,D8,D1,31,18), + wwH16(83,52,36,8B,AB,29,86,F4,39,71,0C,BC,41,55,B9,62), + wwH16(72,F7,34,5D,E5,1B,8E,01,52,35,99,EA,88,3D,C0,8B), + wwH16(4B,5B,A0,FD,8B,5E,8A,B1,82,9B,16,3A,69,85,1F,8F), + wwH16(E5,5A,76,B7,13,61,58,C2,88,71,D2,6A,55,02,F0,EE), + wwH16(EC,E6,45,3E,71,A8,41,FF,CB,AD,E8,96,2E,E6,E1,87), + wwH16(03,94,87,B5,8D,A4,FD,7D,AE,19,71,98,59,AF,06,56), + wwH16(40,6B,59,1D,47,69,6C,64,88,90,6B,B4,9F,A8,B6,49), + wwH16(3F,62,72,8C,0A,FF,49,AA,0E,7B,CD,EA,02,79,E4,8A), + wwH16(C1,EF,EA,DA,4B,E8,23,FC,7C,AA,63,69,60,A7,E2,81), + wwH16(24,E5,7F,92,99,D2,9C,D3,C6,B4,30,12,E1,11,C3,B1), + wwH16(96,9C,83,EA,DA,F8,67,75,7C,EF,A8,D7,34,8F,76,38), + wwH16(AD,7F,C2,20,C5,42,2D,7A,B8,E1,43,32,93,A0,35,01), + wwH16(82,CB,88,7B,EC,6D,52,21,2D,13,E9,4F,23,73,B1,FE), + wwH16(1B,32,3F,63,20,A3,BE,57,AD,F9,3C,60,20,32,D3,DD), + wwH16(79,9D,36,36,BD,1E,79,53,32,92,C2,F8,F2,34,5C,4A), + wwH16(7D,A0,89,3E,FF,E8,0D,13,3D,6B,C2,68,F3,FB,5B,04), + wwH16(27,FA,C8,5E,4E,95,52,0E,F7,05,57,3F,08,0F,8C,E9), + wwH16(5E,61,76,98,29,72,A0,FF,99,8A,50,3A,31,11,7D,AE), + wwH16(9B,B7,72,E0,2A,AE,34,8F,AC,5E,F0,2D,10,D4,FD,5A), + wwH16(42,A4,A7,E7,C2,80,38,FB,C0,13,F8,7C,9B,F0,B4,DA), + wwH16(BE,E9,20,11,ED,11,09,DE,D5,E2,E5,AE,7B,EB,29,F7), + wwH16(35,4D,BC,1C,B1,5E,F5,7E,3E,31,13,4A,19,38,A5,A4), + wwH16(39,AC,7F,72,4F,64,14,FA,82,E2,0A,AD,41,22,7E,E2), + wwH16(FA,7F,2A,F1,D8,9A,AC,86,1A,1F,06,12,21,91,BC,2F), + wwH16(20,B6,1C,A8,C9,F4,EB,59,4C,8D,99,87,AE,AB,D8,11), + wwH16(27,1A,EA,BB,D0,05,74,F9,BF,A7,99,88,0D,7C,30,12), + wwH16(B1,75,2C,07,1B,EF,A1,AF,06,33,34,92,DC,4D,16,ED), + wwH16(B6,57,48,2F,B4,15,51,5F,DD,EA,E2,5C,45,EB,BA,DB), + wwH16(95,BA,D9,D7,F0,A0,75,A7,8A,59,45,0D,EF,79,15,80), + wwH16(EE,B7,DB,F5,E8,2A,0E,20,5E,A3,02,64,D0,89,C2,65), + wwH16(3C,8D,BD,87,AD,4D,F0,28,77,7F,56,D6,5C,80,55,A9), + wwH16(3E,4F,B3,2A,35,73,71,1C,95,35,83,12,31,31,EC,B2), + wwH16(9D,73,8A,68,5E,1E,D1,96,04,2C,74,E9,1B,FA,E4,AF), + wwH16(1F,1A,6B,3C,6A,AB,F4,3E,B6,E9,54,53,19,9F,6F,3F), + wwH16(53,C3,58,0D,23,7A,67,59,13,62,ED,DD,36,D8,58,CF), + wwH16(BE,D1,A9,9C,D8,F2,C1,F0,2B,EA,62,49,12,09,96,28), + wwH16(C4,DC,65,8F,30,88,99,0A,28,EC,83,B4,69,18,DC,99), + wwH16(75,FA,93,08,92,4D,D5,F3,0D,3B,D2,79,71,29,7D,3D), + wwH16(71,DF,2D,F8,37,28,D6,56,60,A0,DD,53,8E,BB,B8,89), + wwH16(B5,6D,8F,89,E3,46,5A,AA,1E,0A,31,72,61,80,5D,D8), + wwH16(F5,02,3E,51,14,07,26,53,46,C5,DB,A4,97,99,BB,47), + wwH16(CE,6B,46,BF,D0,13,72,53,6F,EA,D7,4C,D8,57,AB,54), + wwH16(49,2E,78,1E,AE,5A,00,A0,6D,4E,27,EE,70,BF,CF,E8), + wwH16(A2,E4,CA,00,D9,70,12,BF,9C,CB,AE,12,1B,48,E0,B9), + wwH16(0F,4E,49,D5,F8,70,A6,36,A4,5F,2F,BD,79,7F,4A,C7), + wwH16(60,20,B5,5B,73,CC,3D,5F,5E,BD,91,06,76,AA,3B,88), + wwH16(4C,E3,0E,D2,D0,63,49,46,66,27,FC,A0,C7,B6,77,43), + wwH16(FF,F4,54,D7,CC,DE,9B,E9,54,F1,BB,D0,5C,E6,35,A9), + wwH16(1C,0C,FD,6F,10,2B,39,49,4C,CA,9C,E1,41,D9,5B,CD), + wwH16(02,2C,65,8E,D7,22,57,50,AF,DD,F8,1D,60,C1,5B,60), + wwH16(8D,4A,DF,FF,D4,C2,45,7B,CB,58,2A,9F,69,61,08,70), + wwH16(61,61,68,40,21,D9,C5,01,46,A4,43,2D,36,DE,EF,84), + wwH16(6C,5C,F5,9F,D1,C1,BB,B8,55,59,A0,1E,D1,CE,F4,9C), + wwH16(8A,62,53,BC,40,4B,BC,8B,2E,69,A6,74,20,C7,8A,2C), + wwH16(13,72,73,DF,97,4C,BD,C3,D1,F6,4D,4C,70,DB,AC,E1), + wwH16(67,A5,3B,A6,38,75,B0,11,CE,97,52,2E,30,7B,B9,4C), + wwH16(D4,79,45,45,9F,FA,36,1B,EB,9D,F3,66,43,30,C8,B5), + wwH16(E2,D8,C6,D7,25,CF,DD,8B,F9,61,01,74,02,47,58,01), + wwH16(A2,B4,15,D5,7D,74,6B,FB,AA,B5,2D,A4,F8,27,BA,48), + wwH16(DD,11,05,BD,48,52,65,6E,AF,A5,B7,35,EB,ED,8B,0B), + wwH16(61,EB,30,0E,41,22,99,04,FA,EB,A8,BD,CC,11,66,40), + wwH16(E3,27,E9,A1,FF,30,98,E8,C0,1A,AE,AE,68,49,BB,1F), + wwH16(EF,36,2F,41,2B,1F,36,09,F7,E2,A0,C6,8E,7F,DB,2A), + wwH16(06,E3,BB,AA,4A,ED,9C,3E,6E,8D,00,D7,99,96,CD,96), + wwH16(0D,52,D6,F9,C7,EB,B6,82,0F,47,05,D5,CF,91,2A,3C), + wwH16(DA,2C,86,DD,F6,9A,A8,FF,9A,E4,C0,4A,0E,7E,8D,8C), + wwH16(6A,E3,42,39,6D,FD,81,33,FD,12,CA,61,35,47,73,DE), + wwH16(A0,70,5F,0C,2A,92,6D,3A,06,EB,A9,EF,84,41,63,C6), + wwH16(53,A4,2F,09,0A,00,51,74,2C,25,89,7B,31,BF,E6,36), + wwH16(4F,71,44,93,ED,66,C2,53,CE,D6,57,42,71,17,00,D1), + wwH16(F7,58,D1,39,4F,FE,5C,F0,FD,09,AE,FA,6E,21,62,6F), + wwH16(69,67,79,51,7C,68,06,3D,FF,F5,E4,90,AA,EE,26,BE), + wwH16(97,29,15,80,84,60,3B,70,48,BB,EB,BC,5E,8F,EA,72), + wwH16(CB,AB,13,18,11,B1,90,BD,22,BA,98,8B,95,B0,22,6A), + wwH16(9A,8A,C0,56,05,73,09,D5,18,36,22,79,A6,23,D1,97), + wwH16(58,35,3A,16,2A,6F,36,CD,EB,0A,93,9B,57,B9,58,19), + wwH16(8D,42,01,12,75,3C,F1,BE,CE,B1,77,DC,93,EB,55,E1), + wwH16(4F,3C,88,E4,7E,29,0C,80,68,48,09,7C,02,C4,57,62), + wwH16(F4,7E,31,1A,8B,F7,50,91,C8,5B,E1,6C,4D,5C,CE,F1), + wwH16(08,F7,F8,37,9F,58,D8,7F,D6,DA,F7,31,5F,DC,D9,95), + wwH16(8E,D6,72,F5,70,9B,DD,4F,43,BF,7E,01,18,AA,09,A4), + wwH16(87,B7,89,56,0F,DF,08,86,4F,BD,D1,54,03,66,B3,A5), + wwH16(D9,30,5A,F0,C8,0E,30,68,A4,5B,96,CC,A0,96,FA,47), + wwH16(28,EB,B4,EC,DE,3F,78,AB,59,A3,B7,A8,20,39,DA,C2), + wwH16(AE,2A,C9,E8,9A,9C,64,CF,A2,04,EC,16,1D,AC,29,DF), + wwH16(EE,83,BD,7C,0E,C3,90,26,97,6E,F2,F3,D1,66,99,CC), + wwH16(05,8B,1E,E3,41,5B,90,03,85,03,47,5C,A1,71,AB,3A), + wwH16(84,F9,5E,FE,86,6F,2F,BE,46,71,AA,12,1F,24,5D,A4), + wwH16(06,A7,F7,91,D5,F0,8F,DC,17,2B,C2,FA,73,7A,23,7A), + wwH16(1E,40,D8,43,74,D1,49,C7,F8,9B,28,E3,8B,FA,0A,ED), + wwH16(3A,CB,73,85,61,0D,BB,69,A2,EE,67,73,96,D3,27,0C), + wwH16(03,81,8C,9B,29,C8,CE,4D,E1,AE,19,D5,E4,0B,D3,BA), + wwH16(76,6A,F0,AF,93,21,4A,9F,8A,AE,E5,27,9E,DF,65,87), + wwH16(D2,0B,50,58,84,23,61,AC,07,E9,D9,37,E6,C9,59,45), + wwH16(BA,57,C3,0F,89,9F,52,3E,9E,E9,B9,B3,41,63,B8,56), + wwH16(50,0D,86,D4,69,D4,4D,AB,54,22,F1,55,6B,C0,19,7F), + wwH16(A9,F0,D8,FC,B6,FF,3E,B3,A9,6F,BD,92,44,77,7E,F2), + wwH16(50,09,4A,F2,0A,93,1C,14,68,A1,E1,99,2E,82,C8,36), + wwH16(9A,8A,AE,8B,EC,7D,56,26,4E,B9,15,32,EC,B5,CF,12), + wwH16(AB,4B,E9,B0,C9,FF,AB,EE,07,C7,3E,B1,5E,97,83,83), + wwH16(E4,AF,E6,AF,4B,AC,4C,15,A3,84,94,D2,91,EB,7B,12), + wwH16(F1,73,21,99,82,43,D9,48,29,77,6A,45,A0,88,3A,43), + wwH16(F0,83,33,EE,28,C0,9A,A8,FB,0C,08,42,A0,5F,90,6F), + wwH16(CE,67,3A,F9,C8,CF,71,C8,DD,2A,D7,8F,2C,7D,D9,2D), + wwH16(95,07,2E,56,C3,8E,1A,C7,12,C6,AA,97,96,0A,B2,72), + wwH16(88,D0,D1,30,16,BF,EE,9C,CC,41,5B,23,93,86,3D,47), + wwH16(B8,61,6F,BE,1F,26,68,D1,A0,36,9D,90,D1,A1,43,84), + wwH16(5F,EA,F9,8A,AB,32,EC,05,79,85,70,49,FC,F6,A4,E4), + wwH16(ED,7F,BD,20,06,E5,60,F8,80,CF,4E,C8,49,6C,A6,6F), + wwH16(0C,AA,C9,02,12,CF,B7,33,36,9B,D3,C0,F2,77,3C,FF), + wwH16(F5,06,2C,D8,A4,B3,6C,A5,26,60,F1,98,6E,A8,B0,CE), + wwH16(35,F1,8E,E2,C3,D0,FA,EC,23,3F,47,39,15,FA,47,A0), + wwH16(AD,0A,5A,89,35,73,CD,D8,1E,0B,B5,C2,A7,C6,B4,DB), + wwH16(D6,EB,9C,C0,F7,DF,8E,DF,70,EB,E3,75,58,15,81,56), + wwH16(3B,AD,C7,DA,A6,31,D5,8B,87,91,A4,C0,4A,D8,D9,92), + wwH16(31,8A,C7,F3,EF,42,05,C5,9A,DE,C7,63,60,58,E6,76), + wwH16(0E,0B,7B,60,CA,0D,BB,DE,22,84,49,F9,CE,DD,59,55), + wwH16(F5,A7,94,D3,7D,46,23,53,4C,A8,33,99,55,F3,E2,7C), + wwH16(D9,E4,02,8E,8C,11,B1,AB,D2,55,5F,93,8C,AD,E6,01), + wwH16(37,28,9F,02,BF,3F,05,AD,2A,64,21,A9,F5,B7,57,2D), + wwH16(BE,B8,37,6F,48,FB,11,BD,81,96,18,04,52,9E,C6,99), + wwH16(32,B3,40,10,E2,3D,EA,19,E1,E9,69,2F,BE,9A,99,9A), + wwH16(78,20,06,B3,A7,37,38,32,66,75,B8,46,A1,51,3D,4C), + wwH16(D7,ED,9B,BA,CE,FA,9B,85,38,F5,40,D2,EB,B7,02,3E), + wwH16(AC,40,E6,42,C0,DE,7E,69,6A,B4,EC,AF,0E,14,B9,05), + wwH16(51,C9,43,03,3B,2D,EE,C0,1E,AE,2A,A6,A2,B6,AA,DD), + wwH16(FD,E2,5C,F4,91,6C,67,FD,72,20,B3,89,A3,A9,95,13), + wwH16(9F,49,39,28,B5,50,7B,84,A6,05,7D,FD,5F,3B,67,F2), + wwH16(38,32,A5,7B,65,61,24,39,5E,F6,75,2A,38,CF,43,DC), + wwH16(3F,E5,89,05,25,D2,76,3E,53,4D,C0,4A,A0,C6,9E,39), + wwH16(E7,7D,D5,9B,E6,EE,03,67,89,BA,8D,FD,BB,76,05,2F), + wwH16(4C,32,A5,22,52,CB,AF,12,34,BB,99,C6,AE,04,6F,BE), + wwH16(D3,B1,27,7E,35,F7,2D,47,44,0A,46,0E,EC,B0,F5,88), + wwH16(D5,A5,3D,62,66,70,F4,E5,FC,D6,AB,BE,41,02,7D,96), + wwH16(73,02,96,2D,CF,7D,5C,5F,E5,30,36,E7,FD,EA,B5,AD), + wwH16(62,1E,D1,99,BF,91,6C,6D,EB,E2,8F,6C,6D,02,17,AB), + wwH16(98,84,25,A0,AC,F9,3F,21,1B,98,A3,12,BC,7E,D7,31), + wwH16(A4,3D,73,F0,10,B0,E4,BD,67,2D,00,FC,2A,4D,3A,EB), + wwH16(14,AF,2E,96,DC,9A,AB,4E,C4,B5,A4,A3,4D,8B,87,C5), + wwH16(E6,2E,0E,D0,11,C6,6C,1E,D4,7F,62,16,55,B9,F2,83), + wwH16(86,5F,8A,95,73,64,2B,7A,50,61,4E,A1,5B,52,9A,49), + wwH16(06,98,86,B5,EB,64,1C,42,98,DB,0F,02,C6,AA,FB,11), + wwH16(FB,83,7C,55,8E,B9,04,B5,FF,04,20,68,09,EE,BE,4F), + wwH16(4D,FF,77,71,CF,F2,E4,C2,11,68,4B,7D,47,3B,3C,05), + wwH16(71,5B,D8,FE,84,BC,02,37,A6,DB,31,F0,5A,FD,21,9A), + wwH16(D0,20,3D,0F,0A,F8,76,00,1C,BB,FB,BC,85,68,CF,27), + wwH16(4B,1E,9E,20,93,DB,D0,2A,28,6A,BA,7F,5E,4B,E0,7B), + wwH16(22,28,07,86,47,E7,0C,1A,83,6F,6F,C2,FC,85,E3,A6), + wwH16(15,F6,4F,AC,2D,83,1C,35,AB,90,89,9D,2A,5B,85,E7), + wwH16(D7,8A,BF,32,F7,76,B9,CB,11,BA,89,F8,30,61,82,B1), + wwH16(CC,46,68,E9,6D,27,F4,34,DD,87,A0,D6,18,61,98,E3), + wwH16(E5,23,76,B7,3D,26,2D,1D,11,7A,EE,F1,65,2E,4F,2F), + wwH16(2B,ED,2B,DC,E8,F1,69,D2,55,41,09,D0,28,15,39,AA), + wwH16(DE,10,37,F4,1D,B4,DE,5C,F6,B7,AA,7D,57,86,48,9B), + wwH16(E2,E4,B8,04,97,15,A0,F2,DF,A7,32,D4,83,22,16,B8), + wwH16(62,9A,00,16,28,AB,2E,3A,A1,2A,68,F2,22,A0,B8,AB), + wwH16(79,37,CB,31,E8,D0,BE,68,30,BB,92,1E,E1,AB,5E,EF), + wwH16(48,E2,27,99,1E,7A,F4,CA,EA,C4,02,19,1B,1C,67,73), + wwH16(79,21,73,BC,31,27,02,9D,37,0B,DE,A0,6F,D0,A3,8E), + wwH16(D0,1F,B0,EB,89,CC,BF,10,9C,20,B9,6D,0B,3E,34,FE), + wwH16(F4,02,9B,4F,7F,12,51,26,79,A6,49,70,10,65,65,3C), + wwH16(D5,E0,77,39,24,2F,FE,6C,0F,54,31,B7,C9,C0,DD,5A), + wwH16(37,22,73,10,46,50,D2,CA,5A,CD,09,C9,C6,B9,97,3A), + wwH16(56,6A,82,C7,BA,C6,AB,58,20,77,B0,93,3E,EE,FC,F5), + wwH16(9E,C7,DA,36,82,08,BB,A5,64,AB,68,D6,FE,ED,89,BE), + wwH16(BA,4A,6D,F4,35,66,06,85,B2,0E,DD,68,72,E8,25,68), + wwH16(4A,ED,9C,33,C1,43,73,C0,D7,93,6D,C3,AC,44,28,61), + wwH16(66,8B,63,83,E1,10,4F,5E,F7,DA,B3,52,91,95,23,E2), + wwH16(65,D4,F7,C2,BF,0D,9A,50,A0,6C,CA,0E,D6,69,21,2C), + wwH16(7E,E8,27,71,26,FE,5D,9C,EB,07,65,B9,77,2C,B5,B8), + wwH16(E4,B6,38,77,4B,85,9F,95,AE,F3,98,17,55,D5,67,2A), + wwH16(9C,2D,F8,DC,C0,10,86,B9,C7,8B,88,8F,CC,CF,05,10), + wwH16(AC,F2,9C,EC,6E,54,31,4A,4D,0C,73,9B,68,86,57,BA), + wwH16(8D,DB,AD,BB,57,4F,9D,C3,0F,46,CE,40,89,72,07,91), + wwH16(16,F0,5B,9E,C1,30,65,4E,C1,1F,3C,1B,5E,45,12,6C), + wwH16(03,92,4E,76,82,4C,C3,9C,EA,23,3F,86,56,00,5D,AB), + wwH16(7E,4C,E4,F8,2A,B7,9A,75,EE,2C,54,BE,E2,57,2B,49), + wwH16(BB,B8,9C,12,BA,AE,53,67,6C,2E,CD,55,C0,33,DB,ED), + wwH16(7E,DC,09,65,77,03,A8,E9,A8,5B,EC,AB,6E,08,F7,2F), + wwH16(AA,1E,CE,8C,E5,4A,B7,84,87,3C,F5,B0,31,56,AE,DE), + wwH16(58,9F,37,2F,6D,42,7D,5C,4A,D3,24,85,18,C3,EE,5D), + wwH16(92,0D,BD,AC,23,F5,99,B8,A1,89,2A,D2,97,E8,A8,1A), + wwH16(C2,40,25,8D,1F,E6,05,95,71,13,52,38,79,C6,FD,D1), + wwH16(E1,25,24,DD,0F,2D,1A,5E,E1,86,17,8C,05,49,D0,D6), + wwH16(7A,EF,8B,15,FC,77,BA,B6,17,4F,B5,78,40,9B,FF,8D), + wwH16(34,B8,7A,06,6E,2A,1A,B1,99,C4,D7,0D,BB,22,8D,8F), + wwH16(B3,24,4C,D9,1D,8E,19,EE,17,05,AE,F5,79,87,6E,B3), + wwH16(1A,6D,2F,64,02,F4,9B,36,DB,3D,02,35,B0,33,53,66), + wwH16(1C,C5,54,A9,DE,6E,53,BA,EA,8A,AF,01,F4,9E,39,7B), + wwH16(1D,C8,90,23,9C,2E,B0,AB,CE,D9,AE,EE,0F,D2,16,71), + wwH16(28,E3,B3,1A,C1,91,FA,18,FD,25,4D,03,24,1C,F7,2A), + wwH16(D7,87,70,04,10,2F,BC,3F,25,1D,90,A2,FA,53,F8,25), + wwH16(AD,1A,B6,71,E1,E2,7D,24,F5,21,C2,12,9B,5A,C4,DF), + wwH16(71,72,D5,71,78,D8,39,F3,AA,8D,E2,82,3F,3C,8A,EB), + wwH16(52,25,D8,CC,B1,06,38,33,00,6B,78,71,3A,DC,D1,29), + wwH16(BB,4D,87,66,DC,5B,49,13,68,5B,96,E7,8C,E4,E4,40), + wwH16(15,F7,57,45,9E,62,DA,C0,0E,CF,3E,61,AD,C9,8E,C7), + wwH16(7C,8D,BA,36,31,41,67,60,B8,49,8F,AA,EA,03,31,00), + wwH16(3F,28,38,42,3E,CB,B1,09,E2,90,84,AA,4B,CA,B7,F8), + wwH16(5B,23,8E,96,79,51,26,ED,ED,0B,D6,65,E1,54,23,05), + wwH16(4C,39,AB,38,B9,3A,2E,B7,5D,6F,15,8E,DD,B6,AD,C8), + wwH16(43,59,AD,6B,3D,E9,7E,7E,6D,A2,03,28,46,42,B4,E2), + wwH16(4B,8F,D2,01,07,AB,35,37,F3,00,AD,C4,2E,32,76,90), + wwH16(C7,B4,B6,FA,68,5B,E2,64,30,2F,14,00,A8,D9,1D,62), + wwH16(DA,7F,D2,C2,2C,EA,25,9B,ED,C7,EB,19,E6,A4,EF,12), + wwH16(B4,7A,E0,F5,B3,B3,A9,A2,24,40,BB,11,E5,D1,2B,7D), + wwH16(AC,74,A2,9D,6E,1A,94,78,9D,A3,31,F9,4F,0E,5A,50), + wwH16(FF,46,41,73,52,E0,5E,FF,C1,55,4B,DB,0E,AB,94,C9), + wwH16(D4,83,66,A2,1A,04,05,0F,E8,09,40,B3,E4,9C,24,37), + wwH16(F2,F0,F6,14,40,E3,FC,CC,00,89,1D,B1,7D,D7,67,27), + wwH16(C7,0B,DF,BE,78,89,BF,BD,8D,10,8D,67,3B,2C,A6,1B), + wwH16(95,95,B0,2A,D3,27,0A,A5,E3,D3,B6,57,91,41,C0,BA), + wwH16(59,5D,63,77,B2,C4,56,8E,FE,0C,F4,E2,1D,F6,ED,F6), + wwH16(EC,CF,EC,03,23,53,63,DD,D2,29,C4,6F,67,59,4B,29), + wwH16(A5,B3,50,34,D9,72,EE,FC,F3,19,64,A0,DA,A1,C6,15), + wwH16(B8,CF,38,56,EB,1F,CC,AB,C2,E1,C5,AC,7B,07,AF,29), + wwH16(38,54,5D,9F,3F,41,20,AE,02,45,50,DE,94,82,63,BD), + wwH16(3B,BE,88,8D,1A,CB,40,01,B9,DA,32,D3,BF,4A,DB,BF), + wwH16(57,1A,2F,1F,EE,B6,E0,50,3D,69,36,66,84,F7,B3,9B), + wwH16(AE,F0,F3,0B,F0,72,02,83,19,D7,0D,EC,C3,59,20,49), + wwH16(5C,BC,E0,F0,1C,D8,CE,29,A7,32,5A,23,3C,83,F4,02), + wwH16(90,0A,A9,6E,DF,FD,0D,B9,B8,0C,54,90,23,E2,5C,CC), + wwH16(9D,DE,5D,DE,F8,E9,43,CF,07,C4,DB,79,CF,34,5A,27), + wwH16(57,8A,10,7B,AD,C0,DD,54,72,52,4E,9B,18,BB,BA,91), + wwH16(63,5E,18,F4,77,66,88,DE,1D,28,46,B0,7F,55,B2,2E), + wwH16(2F,F8,F0,CD,13,82,83,FC,13,68,29,BE,AA,22,BA,49), + wwH16(C8,06,E1,FB,4E,6D,32,FD,04,3B,5E,73,C5,58,39,AB), + wwH16(98,3D,C1,8F,E4,3E,95,5B,F8,D2,D9,4C,C6,DF,14,14), + wwH16(88,DC,88,CE,18,91,19,BA,D9,EA,3D,02,B1,19,03,7E), + wwH16(AD,8D,E0,C8,F6,4D,94,26,0A,42,3F,AC,38,A2,E8,85), + wwH16(38,F0,3E,2E,EC,FB,5E,15,3F,91,E1,E9,6C,55,91,64), + wwH16(A9,8F,7C,B1,B6,18,FF,58,1E,72,68,45,33,2F,CB,8E), + wwH16(E8,A3,27,9C,9A,5B,73,BF,78,B7,C5,7A,8F,21,6D,BA), + wwH16(62,FF,41,D3,71,11,49,1B,E3,B9,B3,07,32,7B,DC,E7), + wwH16(14,78,9E,83,8D,1C,5B,00,3C,18,39,DC,A1,41,13,0B), + wwH16(E1,D5,E9,EC,EA,39,00,58,43,97,C0,13,71,40,B0,35), + wwH16(13,45,3D,3F,3E,68,1F,63,39,73,77,2E,2A,7A,4A,6D), + wwH16(36,A8,EC,3B,CD,6C,F7,C8,FC,03,8C,48,8A,79,C3,5C), + wwH16(D0,2D,1F,9A,6E,04,5B,AD,03,D4,49,9B,AC,5E,08,38), + wwH16(08,EC,79,97,0E,50,F4,26,A2,76,0A,7E,61,6F,40,97), + wwH16(94,B1,48,69,82,BB,F7,07,2C,A8,DC,A7,F3,A5,C0,D0), + wwH16(E8,6C,21,AF,01,40,EB,2E,E5,8D,DD,A9,D9,0B,23,29), + wwH16(38,49,92,D9,A1,0D,77,39,2F,7D,0E,F5,FF,B9,4E,0D), + wwH16(74,3A,F2,E9,A6,3A,B0,D9,B1,7A,91,E2,82,26,0E,2C), + wwH16(EB,0D,64,CC,7C,D9,E5,54,BB,E9,70,1A,86,98,1D,A6), + wwH16(52,DA,3B,D2,EC,4A,8A,F8,0D,9B,C8,4B,77,59,B6,03), + wwH16(36,1E,A4,F3,D0,0A,6A,5D,35,59,D7,A2,E3,F8,B2,00), + wwH16(2F,EB,DA,E0,39,3A,5D,DF,6B,2F,9C,09,00,7F,19,92), + wwH16(02,39,97,9E,F9,E3,5E,C3,CE,CE,DC,0A,66,D1,55,56), + wwH16(5B,F6,19,6A,14,D0,3B,4C,4C,B6,4B,6D,4C,A1,EC,33), + wwH16(B4,88,56,9C,4D,B6,57,21,1C,14,03,51,AC,41,67,64), + wwH16(D7,10,C5,36,65,E7,EE,D9,1B,42,1C,CC,7F,DF,F0,09), + wwH16(F7,9F,95,18,EF,8D,8A,FE,C7,3C,28,E6,FE,FF,3C,83), + wwH16(AA,0A,94,89,23,0F,2C,9D,72,47,4A,3D,36,10,59,34), + wwH16(A0,85,5E,32,76,EA,8A,E4,8B,4A,26,AE,67,7E,D3,9D), + wwH16(25,A6,79,71,6A,4B,04,C4,0C,55,F5,D9,61,A9,10,CF), + wwH16(1C,D3,AD,E7,39,AC,BA,6E,83,F1,29,EF,81,E2,77,BD), + wwH16(78,77,B4,30,4F,90,47,8C,E9,66,A0,AB,E1,57,EF,EE), + wwH16(A2,05,DA,D2,17,C8,67,37,81,01,A1,6D,59,0B,B9,7F), + wwH16(16,3B,16,DD,B7,CD,24,EE,2C,0F,B1,B3,6F,A7,E1,E8), + wwH16(42,39,A7,E4,5F,77,5E,31,2E,4B,95,49,0B,AA,8E,1C), + wwH16(B6,C7,D8,F8,78,FF,1F,44,81,0D,20,01,B8,91,00,D7), + wwH16(A7,E1,C4,0E,A2,12,10,22,91,EC,29,7B,C2,4D,5A,94), + wwH16(A1,59,D5,23,64,D3,F2,36,F2,F1,D7,BE,11,E4,9D,6C), + wwH16(8F,37,6A,48,12,EF,BC,31,D8,6B,41,50,FA,CC,60,81), + wwH16(42,42,0B,12,01,20,55,5A,57,1B,5A,3C,61,16,22,BA), + wwH16(13,A4,CB,F3,CA,F8,C6,5F,B8,A6,93,53,70,E3,04,C8), + wwH16(67,E4,B6,F0,11,C5,95,88,7E,42,A2,0F,1A,8F,13,1F), + wwH16(A6,42,22,12,87,CD,52,BE,19,3E,8D,B7,50,D1,BC,1F), + wwH16(3D,B5,FE,31,B2,A8,63,2B,06,8B,49,B8,8A,FF,4B,68), + wwH16(EF,60,F5,D4,94,C8,DB,38,2B,52,CF,05,1A,85,A2,23), + wwH16(91,5F,55,EC,EE,B1,0C,D2,70,40,D6,33,46,84,5F,C4), + wwH16(C3,BF,A4,09,02,CC,C8,84,2B,E5,23,1C,DE,7A,34,D4), + wwH16(A6,B3,C9,2A,4C,D5,86,76,10,DF,4C,A0,2A,2F,5B,2C), + wwH16(DC,E3,AC,CF,72,6F,75,45,1B,A7,53,1D,07,B5,5F,EA), + wwH16(A0,6D,DE,16,7B,74,89,F8,F9,C3,D0,B0,13,9F,2B,55), + wwH16(82,99,AD,2D,8F,FE,AA,ED,7D,11,8B,45,B0,46,AE,28), + wwH16(58,D8,A0,EA,63,8C,C8,27,76,39,5C,08,42,A3,1E,90), + wwH16(11,4E,D0,EC,2B,2D,D6,AC,BE,74,EA,8A,96,0B,71,E7), + wwH16(CD,65,E0,E7,A1,FF,F0,36,43,E9,FA,AB,E2,9F,0D,9F), + wwH16(DD,32,7A,CE,43,9E,06,0B,76,2A,80,7A,4B,6D,7A,25), + wwH16(E4,45,AD,B9,8F,E0,21,A4,67,B9,8D,62,98,FD,28,0B), + wwH16(E0,75,C5,D0,41,BE,22,FB,50,C5,57,DA,7A,0C,FA,34), + wwH16(DE,51,B4,78,88,0D,84,97,8D,53,6D,9A,DE,5A,57,CF), + wwH16(1C,1E,A4,CE,96,86,00,1D,D6,72,B1,08,4C,61,F9,68), + wwH16(8F,86,7E,7F,04,20,36,EC,1B,C9,1C,5C,71,DD,5F,25), + wwH16(B9,AE,E4,93,8C,C6,26,B7,3C,2B,20,04,D9,F5,05,23), + wwH16(C7,79,CF,C5,C4,71,4C,9C,56,27,B5,2E,20,EB,4A,FF), + wwH16(EF,3A,47,35,33,BA,97,9B,B0,F9,18,20,CF,50,0B,B9), + wwH16(53,F0,0E,E6,BE,2B,72,A6,43,8C,3E,98,E4,71,2D,91), + wwH16(1C,F1,0C,9D,B5,56,0A,11,39,7F,FA,69,2F,2F,00,8B), + wwH16(13,89,0B,82,F8,94,C2,AA,6D,33,2B,92,9C,DA,17,3A), + wwH16(AF,0F,C2,42,45,BC,90,8C,D5,7A,64,12,C5,D4,32,0D), + wwH16(99,82,5F,8A,CC,E3,17,7D,63,D4,7D,B2,D8,01,A3,C1), + wwH16(F2,D7,7F,8E,5D,4E,F3,7F,43,C5,E8,EA,BB,8A,92,DB), + wwH16(2B,46,F4,F8,AE,02,4E,F7,D2,1D,6C,4F,95,BF,3B,0C), + wwH16(6A,53,86,8C,50,C6,FF,02,73,B4,78,03,A2,65,00,DD), + wwH16(C2,34,5E,E5,F3,01,A0,57,57,EC,2D,84,AD,3A,31,8D), + wwH16(F8,BE,6A,87,FD,EE,9D,C8,00,A0,E3,92,BF,B2,CC,CE), + wwH16(FB,2C,F3,18,92,3F,2E,1B,10,5E,70,99,02,6D,89,10), + wwH16(28,1C,08,40,A4,A6,70,0D,3E,FA,51,2F,E6,A6,24,EE), + wwH16(05,A8,B1,DF,54,1E,13,EE,C2,92,BD,23,D4,A6,B6,8A), + wwH16(DD,CF,AF,A7,90,B1,9F,E8,6C,CD,39,0C,2F,97,47,7C), + wwH16(6B,49,5C,A3,50,7A,38,3D,B8,D9,F3,3B,47,41,42,01), + wwH16(D5,DB,09,70,29,D3,4C,59,37,80,49,2E,67,52,B9,C0), + wwH16(FF,3D,14,87,52,F4,56,2B,D9,EA,FF,64,D6,F1,4C,35), + wwH16(72,6C,15,C4,54,41,D2,C8,CA,1F,1A,6F,82,8E,6E,7F), + wwH16(DF,DA,40,6A,32,7A,39,F1,5F,26,AA,EE,18,D4,53,3E), + wwH16(14,5A,94,95,10,41,0A,A2,46,84,EF,EB,1C,E6,2F,5E), + wwH16(E4,03,85,CA,94,FC,52,D9,F6,AD,03,45,42,63,F0,F5), + wwH16(8F,36,05,4F,D3,14,34,D0,22,07,08,50,E0,7F,87,2F), + wwH16(4F,3F,02,CA,EF,F3,09,CE,1B,6C,F4,52,49,A7,9D,9D), + wwH16(94,1F,06,F9,97,A1,8B,85,30,46,97,46,A0,56,B4,2D), + wwH16(5A,9D,55,05,1D,74,FF,7C,6C,AA,FD,D4,98,99,EC,A6), + wwH16(79,11,C9,0E,A2,89,B7,7B,69,71,8C,5A,42,67,00,99), + wwH16(D3,94,B9,75,D0,15,64,4D,79,E6,9B,60,D4,47,72,86), + wwH16(F7,E1,4B,22,4E,59,BF,1D,80,85,77,D8,DB,21,BA,1D), + wwH16(29,54,41,0D,F6,AA,C7,BA,45,D7,FC,30,90,B1,F1,E0), + wwH16(D3,23,44,C9,E7,1F,B3,A0,51,A3,40,03,EF,71,04,16), + wwH16(9D,98,29,DD,E2,E9,22,B5,41,1D,37,51,90,FD,F9,9C), + wwH16(3B,28,60,0F,F6,3D,93,5D,6F,0F,CE,1F,AD,18,1B,FC), + wwH16(92,20,9D,71,C9,38,03,9A,C2,77,A4,DC,D5,23,24,4C), + wwH16(6A,28,D7,0E,72,F6,0A,C7,70,E6,EB,F2,3E,C1,3A,44), + wwH16(D3,76,CC,4E,91,4B,34,EC,C2,A3,8E,13,D0,0F,1D,59), + wwH16(64,D7,60,40,4B,7C,AF,1B,FD,83,57,8F,B5,23,BE,BC), + wwH16(89,D7,E8,CA,C5,DB,5F,90,3E,35,ED,56,04,FC,75,86), + wwH16(26,BC,B7,ED,43,29,BE,FA,0B,CB,A6,44,B3,45,9F,D5), + wwH16(3C,D0,2B,D9,26,84,14,27,EE,19,A7,70,62,1A,5B,DA), + wwH16(18,A1,4D,DD,89,B5,E5,52,9C,BE,B7,BD,DF,A5,17,C7), + wwH16(16,23,8A,F7,78,E7,95,47,06,EC,A1,A7,6A,02,58,2F), + wwH16(B1,F0,98,99,D7,D8,AC,E7,99,55,13,AD,47,BA,F2,C1), + wwH16(EE,38,DA,2B,C1,59,76,57,4E,44,8D,9D,FD,BE,1F,0B), + wwH16(AE,DA,63,88,DE,7E,95,DE,80,C6,20,B9,F7,D2,99,3E), + wwH16(1E,EA,D7,C1,2E,F0,0F,6F,CB,0C,68,E2,4F,A6,3C,6F), + wwH16(AB,8E,AD,64,87,86,6C,39,90,EB,57,35,20,6C,33,9C), + wwH16(A5,AD,44,F7,02,94,BD,FF,96,58,DF,73,61,D2,BC,3D), + wwH16(E1,8E,50,EE,0A,08,BF,BF,C0,C9,CA,17,12,2E,C1,CB), + wwH16(55,37,73,5F,07,DD,B3,A2,37,77,BA,88,E1,C8,82,AD), + wwH16(37,E3,54,7E,48,6B,17,12,58,FC,71,A6,62,83,A2,B0), + wwH16(A2,D3,6E,E1,6D,5C,D9,CF,A1,AD,45,12,93,FB,1F,9E), + wwH16(80,DB,E6,70,13,07,AB,FB,ED,F0,67,E7,16,FA,47,B8), + wwH16(15,2F,39,91,57,2D,79,9F,FD,7B,37,46,89,8F,31,2F), + wwH16(F2,59,55,CE,A8,CC,76,93,78,77,2B,4B,34,36,F0,8C), + wwH16(63,39,55,B8,8D,B5,D7,11,86,69,88,C9,23,21,98,98), + wwH16(1B,06,B5,25,9B,BB,C0,EE,F1,84,B4,FE,79,11,CA,19), + wwH16(72,7F,F0,AE,4F,88,D6,A0,69,22,A3,E1,EF,2A,CD,A1), + wwH16(46,DA,A5,C1,80,8E,03,23,7A,5E,B8,F5,D1,38,8A,02), + wwH16(8B,A2,D3,E7,86,BD,E0,32,83,E7,9A,F1,A4,7B,E4,2C), + wwH16(5F,AC,5E,90,17,AE,25,BA,72,8A,43,74,03,A9,AB,FA), + wwH16(26,FD,72,86,80,5B,A0,B2,86,75,9C,E3,AA,E3,EF,5F), + wwH16(61,E6,32,A7,81,A3,C5,7A,6F,5E,ED,13,E6,18,EE,15), + wwH16(83,3E,26,D4,36,DB,C2,ED,E2,F6,DA,79,18,91,26,9D), + wwH16(F5,0E,ED,FD,47,4F,1B,3D,68,06,91,26,75,DC,21,90), + wwH16(60,01,F0,8B,08,A8,08,EF,7F,DF,30,FF,63,1F,96,DD), + wwH16(8B,A8,F8,E6,9B,ED,43,C5,09,49,19,DA,0B,D3,BF,39), + wwH16(9B,C5,25,0F,AA,09,CC,FD,39,2F,00,7A,B5,81,22,63), + wwH16(C1,B5,34,51,3A,36,45,50,C1,BE,B9,AB,F6,7D,36,25), + wwH16(9C,CD,50,53,BD,A3,EE,8B,C9,1A,6D,3E,2B,52,E8,CC), + wwH16(AD,FB,A5,B4,F5,C8,3D,12,37,F7,80,55,02,5B,E5,EB), + wwH16(FB,68,6C,A2,CB,BB,01,87,A1,3A,5E,38,64,A2,F2,A3), + wwH16(7E,F2,02,6D,4F,0D,49,51,28,34,E8,3E,9F,FC,D6,80), + wwH16(93,2C,D0,BA,9A,6A,F9,0F,2D,29,CC,97,54,6F,C8,4B), + wwH16(04,F1,92,AD,FE,D9,40,D2,D0,77,F9,AF,E5,68,9E,75), + wwH16(F8,F8,5F,4C,AA,EE,74,65,2F,25,F3,63,B5,21,61,DB), + wwH16(BB,02,BD,CC,BB,1C,97,2D,E9,C8,15,A0,AB,A3,16,07), + wwH16(F0,05,FB,6E,C3,6A,1C,16,E1,73,E9,91,2F,88,B1,A2), + wwH16(58,04,FC,40,8D,F3,DC,3E,2D,B8,5B,12,0D,86,9F,43), + wwH16(D3,C1,CD,4D,13,AA,88,4C,95,E4,A9,93,06,3B,83,DB), + wwH16(99,2C,EB,B7,5A,E9,20,A4,FD,01,02,AD,77,AD,0A,4E), + wwH16(D6,DC,C0,02,B4,62,C3,EA,43,2B,68,F6,92,B2,11,F3), + wwH16(1C,4C,44,70,A4,DD,F1,96,D6,34,90,FE,6C,27,0D,3C), + wwH16(69,B3,16,CC,17,AB,DF,C5,CA,E6,DC,5A,00,6F,BB,FB), + wwH16(96,47,C9,36,34,B7,06,EF,82,F6,FC,E0,0C,17,1B,EE), + wwH16(9D,35,1F,97,3B,ED,FA,07,63,F0,5A,28,85,C4,6B,EB), + wwH16(9C,A5,D8,D8,94,C6,E8,0F,E1,E8,4F,A4,C5,6F,FC,27), + wwH16(26,AD,4B,1A,D2,1E,E1,4D,7F,02,06,17,49,0F,D0,82), + wwH16(3E,28,FE,E0,19,18,76,9D,B2,86,E4,23,A3,66,B7,3A), + wwH16(31,DD,BC,01,9A,C0,6C,6F,3A,52,F5,F5,CD,66,6C,88), + wwH16(6B,5F,88,24,66,1D,63,58,8B,08,96,7D,94,47,18,E9), + wwH16(23,71,85,C6,25,8D,A8,C3,ED,91,91,C3,C4,B2,08,6F), + wwH16(FF,39,EA,64,29,FA,ED,DD,14,9E,87,B8,81,FC,B1,AE), + wwH16(F8,D6,E6,E9,8F,BC,EC,15,06,6E,EE,83,AB,A3,93,81), + wwH16(34,6E,5F,EC,89,14,7D,BE,5C,36,51,BD,06,08,03,EE), + wwH16(A6,1F,CE,1B,49,B4,A9,42,17,4D,60,80,A7,E1,7F,04), + wwH16(86,32,33,70,0B,14,7C,52,DF,3C,BA,65,72,BD,15,7D), + wwH16(A5,E9,99,06,EA,BC,05,EB,07,BE,CB,94,2C,16,B8,89), + wwH16(DF,67,A2,55,14,8D,B5,F0,62,59,85,B7,98,F5,BD,61), + wwH16(C0,B2,3C,67,38,B8,4C,0B,FB,AC,60,B3,EB,74,7D,D8), + wwH16(0A,54,1E,FC,60,C9,0F,F8,37,2C,43,03,07,6A,98,EE), + wwH16(EB,75,29,D5,81,4D,F9,98,9A,F9,60,CF,8C,21,88,BE), + wwH16(B7,23,84,2E,CF,C1,02,28,19,42,53,63,38,F6,62,46), + wwH16(F9,FD,E8,2D,EB,C4,83,D0,25,E8,30,2F,D3,7C,86,7D), + wwH16(C0,1D,5E,FF,56,B9,24,93,F0,4E,AC,DB,81,68,5D,52), + wwH16(93,ED,74,25,DD,E3,6D,D7,0F,1B,AA,48,53,84,88,F4), + wwH16(62,93,44,1A,99,27,F4,A4,0C,A4,E1,12,28,30,C6,E9), + wwH16(BB,38,C2,12,9A,93,C1,DF,4F,B3,7C,23,20,9A,A8,8D), + wwH16(BB,BA,C9,23,FC,37,78,F7,E8,E5,60,C6,97,FD,F8,1C), + wwH16(4A,A9,54,8B,B2,44,B9,1C,61,32,9E,3F,03,6C,4A,C6), + wwH16(86,2C,7C,19,3F,39,DF,46,AB,37,6D,FF,8E,06,2D,F3), + wwH16(59,AB,08,9B,93,BF,81,BD,DB,8D,0B,9C,95,A5,36,E4), + wwH16(DC,99,88,B9,E4,B8,48,59,E3,C4,13,87,9C,C1,7F,EF), + wwH16(45,C8,41,49,6F,EA,4E,EF,86,F1,87,60,25,4B,FC,BA), + wwH16(4A,0D,50,AF,D8,DD,5E,B7,4E,D1,84,DB,1E,B6,8A,3C), + wwH16(23,E0,DB,C8,93,3C,E4,F1,B2,44,2C,8C,80,BF,9C,E4), + wwH16(DE,AB,89,DF,D9,DA,99,D3,24,F0,39,75,5B,45,2D,69), + wwH16(32,CA,26,C5,4B,2E,84,28,7C,63,9C,68,9C,B9,74,6E), + wwH16(AD,D0,21,65,EC,3A,2F,8A,B4,63,93,ED,56,40,D3,05), + wwH16(57,E0,16,14,6C,C5,DC,9D,9F,28,4A,63,88,92,4C,A6), + wwH16(6F,71,09,6B,FE,63,59,27,51,13,B8,6B,AF,F0,AD,B6), + wwH16(92,68,0E,D0,70,5E,8D,02,37,99,1B,F8,0C,95,10,F9), + wwH16(04,CA,B7,C7,BF,B6,C6,D3,C9,E8,9A,32,FD,78,50,4C), + wwH16(8D,90,40,57,79,9C,A3,66,0A,B4,EF,F6,68,4E,49,0E), + wwH16(F3,B0,A4,5A,C5,3B,C9,60,A7,8F,45,C7,BC,55,F3,88), + wwH16(70,95,A6,4B,FB,53,FC,AD,FA,91,EC,5E,23,9D,98,6F), + wwH16(32,BC,5F,C6,AE,2D,DB,C1,BE,AC,6E,DB,B6,4D,35,11), + wwH16(68,F3,65,25,57,DD,04,56,22,61,F8,F7,57,2D,71,E8), + wwH16(3D,FA,08,6E,58,91,0F,E8,AA,15,F8,E8,DD,35,7F,7F), + wwH16(E8,9B,D2,C5,02,87,B0,04,93,B2,95,F7,B8,CA,09,54), + wwH16(31,FC,52,31,96,7B,1D,24,8A,68,82,B6,97,1C,3C,4B), + wwH16(80,8A,90,A9,83,BA,83,EA,AF,D1,29,51,5B,73,8A,38), + wwH16(CD,20,1C,29,94,6F,19,E9,B3,72,3C,6A,1F,9E,A0,29), + wwH16(09,01,1F,26,F6,A1,C5,B6,0D,A1,77,67,90,5A,73,10), + wwH16(DE,76,20,D7,47,16,6D,98,9D,D3,00,6E,EA,C4,D8,11), + wwH16(95,3A,B4,28,01,1B,FA,1D,25,2F,63,F2,01,C8,D4,FF), + wwH16(87,CE,63,ED,88,8A,E4,40,99,53,68,5F,5B,C7,24,1B), + wwH16(11,F4,FE,F9,3D,C4,10,6A,AD,3B,3B,CD,63,CA,B0,9C), + wwH16(14,BC,12,8E,BB,AE,3F,7E,2D,C9,AF,2E,28,FB,BA,88), + wwH16(0D,E7,22,11,2C,2B,FA,DB,54,38,59,CB,E0,78,51,6C), + wwH16(FD,11,46,06,14,D0,49,E0,C9,9B,CC,5C,73,29,A6,CA), + wwH16(71,DA,4E,74,89,9F,6C,66,CA,19,FF,F3,19,DD,E2,8A), + wwH16(E4,6D,3B,5E,A5,82,67,13,D0,EF,A6,0B,29,69,AA,EA), + wwH16(B1,2E,F7,86,2D,B8,F2,5C,30,81,7C,C1,8D,9F,9B,B4), + wwH16(8F,44,CA,29,F9,AA,F2,FF,3C,9A,B6,3A,F0,6D,57,81), + wwH16(11,7D,F4,0F,3E,1D,8F,73,5F,CC,7C,42,90,44,25,3A), + wwH16(84,62,8B,5A,0E,0B,E9,BA,00,E6,63,94,70,AB,3E,0A), + wwH16(E5,B0,EA,D6,B9,C5,80,18,09,3A,4E,62,3A,45,7B,27), + wwH16(A2,7F,25,DA,EE,C1,05,54,A6,B2,24,6F,BC,38,04,C4), + wwH16(DB,68,84,E2,4C,52,C5,67,5B,6C,97,B8,40,22,F3,CF), + wwH16(C0,B1,7A,56,87,9B,88,32,1F,72,01,6B,E7,20,C7,C7), + wwH16(CF,44,5F,1B,7E,6A,DC,EC,D7,93,2D,63,F0,72,42,A3), + wwH16(DE,64,9B,B7,88,A0,4A,B1,8A,3D,28,5D,B0,3F,8E,2B), + wwH16(96,3D,D7,FD,23,63,48,1C,09,9E,C9,D9,79,85,AF,19), + wwH16(83,08,D8,9B,EE,3B,10,EC,2C,8E,B8,0B,BE,F8,FF,4C), + wwH16(FB,61,BF,26,69,D1,01,3D,FA,25,72,C9,25,85,80,40), + wwH16(F8,88,F1,3E,62,4A,2F,9C,4E,D9,D1,64,BB,FB,C7,4B), + wwH16(88,DD,0F,7D,4F,C9,F2,FB,CA,3C,8B,79,C6,3B,FD,D8), + wwH16(A0,4C,E6,8C,DC,00,47,D8,F7,EC,EE,DE,84,29,D1,50), + wwH16(33,A4,B6,6D,D1,9C,42,11,87,AD,1B,70,23,E3,07,7C), + wwH16(E0,9D,19,AC,7F,2B,63,22,F5,ED,9D,69,BB,12,24,D4), + wwH16(67,C7,B4,E2,7F,E2,AC,B4,D2,A5,49,8A,F8,CB,A5,F0), + wwH16(B9,E2,7D,ED,52,84,33,09,7B,30,46,D8,B1,E8,A6,9A), + wwH16(05,F4,24,B6,E7,3E,61,64,EC,C0,5C,C8,08,9F,B2,6F), + wwH16(41,FE,11,E3,9A,25,92,09,2D,41,52,D5,8A,95,31,DD), + wwH16(92,C2,39,78,5D,AA,69,89,A9,68,97,7F,27,A6,5E,92), + wwH16(26,79,58,E2,9E,63,F9,C7,7A,BD,A5,85,C3,10,0B,FF), + wwH16(65,13,2D,5A,8D,88,AC,70,46,E3,4F,6B,DD,10,5E,E2), + wwH16(E5,D0,96,E2,CA,28,81,F6,00,BF,32,6A,A4,70,29,C3), + wwH16(3F,E6,E4,93,AD,22,6F,95,9B,C5,5E,D1,B8,8C,4F,98), + wwH16(D6,80,22,8B,AB,E7,61,73,25,80,F9,92,2A,B8,58,84), + wwH16(A5,FE,48,0B,C7,8F,D6,05,85,0D,36,E8,06,93,29,E3), + wwH16(62,58,68,9B,F8,B8,EF,C1,15,FA,AB,EC,FB,A4,00,43), + wwH16(51,55,BE,E6,93,B9,F2,F8,2C,32,25,8A,83,1A,83,DF), + wwH16(B4,47,5E,F2,4D,03,BC,BE,77,91,82,F0,AC,6A,32,65), + wwH16(3F,DB,69,D1,AF,FB,99,7E,4E,B1,03,1B,29,71,EB,00), + wwH16(DB,07,64,0F,E0,7A,2A,9B,2A,0D,56,A6,96,1A,AB,C5), + wwH16(ED,80,61,8F,D0,1F,E6,A7,A7,C3,F4,66,E8,97,41,EC), + wwH16(B4,F0,B7,05,4E,0F,CE,01,74,CA,1B,FB,A6,65,BC,93), + wwH16(FF,6E,DC,4A,5B,5C,6E,82,C6,B5,1A,82,11,A6,4E,0B), + wwH16(11,46,28,ED,D5,9A,22,A4,2E,AE,BE,6B,08,AD,66,6D), + wwH16(FF,01,08,72,BC,7A,CB,0A,72,4E,28,39,31,A9,C3,C9), + wwH16(3A,08,F9,27,55,4D,BA,32,98,24,EE,8A,D9,19,E4,06), + wwH16(48,87,09,B4,4B,B0,9F,AD,46,C1,B0,01,8F,64,10,26), + wwH16(C5,1C,54,C9,41,51,60,CA,BA,AD,BB,95,5C,70,20,8D), + wwH16(BD,B5,6C,D8,84,DB,7E,D9,95,39,7D,09,E7,7D,62,11), + wwH16(1C,1F,82,85,BC,09,4A,98,9D,5A,6A,B6,89,13,51,D2), + wwH16(54,D1,13,19,D2,75,C7,B2,FD,75,51,A9,39,29,D3,87), + wwH16(C1,5C,32,D5,00,E5,65,AF,E0,7D,08,AE,9C,2A,38,E5), + wwH16(46,0E,20,8B,D6,FA,E9,A4,BF,3D,0C,99,1A,10,D8,68), + wwH16(54,E5,74,93,6C,E9,62,FF,3F,51,C7,B8,BB,C3,DB,B4), + wwH16(62,3F,CC,99,18,6D,0F,26,5D,73,7F,FB,4A,6C,4F,8E), + wwH16(05,D1,68,DF,46,14,BD,52,E5,5C,94,D5,12,CA,A6,FA), + wwH16(98,54,5A,5F,73,D1,6B,9B,14,62,08,26,FB,76,23,26), + wwH16(87,2C,36,18,8B,9D,47,4F,4B,A6,45,8F,C2,58,96,00), + wwH16(2D,F5,C3,7C,4E,C3,A2,75,08,B2,FA,C4,3E,75,2C,01), + wwH16(4B,E9,D3,5B,A7,58,69,D3,7E,E2,EA,CF,ED,E0,D3,C6), + wwH16(51,EC,01,56,F5,6E,44,A2,8C,83,3B,29,13,2C,B1,4A), + wwH16(6C,66,7C,16,12,0C,61,A0,93,B7,66,13,6E,C5,8C,FE), + wwH16(64,09,8D,16,08,D2,68,86,4C,4E,DA,36,DC,37,1C,29), + wwH16(2F,26,40,C4,ED,49,07,28,C4,E9,2D,47,1B,DB,A9,69), + wwH16(B7,09,C5,9D,B2,21,72,DD,06,23,62,68,06,BC,15,0D), + wwH16(40,BE,F4,4F,72,0D,8D,10,6D,04,7A,47,5B,C6,1F,EB), + wwH16(4F,AB,65,D3,D8,F2,89,DD,79,C2,3A,56,4F,A5,BF,6E), + wwH16(46,B1,C0,55,FC,8B,4E,5C,C4,C5,A2,95,76,2A,29,AF), + wwH16(76,CA,21,3F,5A,7D,92,49,77,F2,FD,A8,52,06,45,DB), + wwH16(54,8E,56,14,5B,93,B0,42,28,CF,10,CF,3F,58,83,2E), + wwH16(CB,E4,83,8B,6F,3C,48,35,A6,3E,E2,C9,7D,5B,B4,2C), + wwH16(B8,CE,52,47,E3,5C,DC,23,05,87,C6,0D,DE,FE,96,E1), + wwH16(FB,4A,28,B0,06,5A,26,D5,11,77,09,D7,EC,C8,30,98), + wwH16(F9,61,3D,69,C4,68,89,8C,7D,2A,EA,AB,53,54,9D,4C), + wwH16(3A,7E,EA,0D,D9,5E,7B,3F,38,E5,5C,EF,3F,DE,DD,5B), + wwH16(24,8F,63,70,93,6F,F6,72,09,5F,E0,63,4E,D5,54,F2), + wwH16(05,AF,0C,4A,3D,45,CE,73,95,4C,72,BB,80,12,F1,BD), + wwH16(50,C2,88,2F,97,1B,9A,8D,BE,FB,4F,29,08,26,D0,33), + wwH16(14,72,09,6B,7A,03,02,7D,AB,17,37,7F,51,12,DE,F4), + wwH16(2B,AC,AB,1A,47,23,D7,00,9E,9A,F9,0D,6C,D0,13,FF), + wwH16(7B,D8,E5,AA,2D,41,CA,4C,9B,73,DE,CD,A8,96,1F,1B), + wwH16(42,00,7E,6A,80,8A,B4,3F,E4,D9,FE,82,B1,37,24,4D), + wwH16(F2,A6,16,1D,16,A5,23,F0,97,B2,29,3E,04,C7,E7,0A), + wwH16(CE,EE,FC,3B,CC,A8,7E,ED,A8,9E,4B,C9,29,0F,99,69), + wwH16(BB,60,C9,35,C9,4A,3D,F9,B1,1B,F0,3F,FF,04,ED,FA), + wwH16(CF,A9,51,8A,71,6C,54,F1,0A,58,8F,15,7A,9F,42,BD), + wwH16(35,99,40,1B,B9,60,2B,BA,29,E8,45,79,84,C8,EA,5E), + wwH16(22,2E,5F,F7,08,2C,83,0D,B5,D2,B2,F1,E2,BF,C1,E3), + wwH16(06,FF,A4,B1,99,B5,CE,70,91,62,1C,32,4B,2A,16,AA), + wwH16(0E,01,02,02,87,C5,1A,6F,5D,1F,C9,03,F7,E6,EC,19), + wwH16(CB,34,23,FB,EA,D8,64,BC,8C,C4,17,9A,68,BD,D7,27), + wwH16(3A,BF,05,E4,72,4B,DC,47,A4,83,7C,71,73,FC,9A,CD), + wwH16(1E,F8,65,02,AF,FA,F9,BF,AC,34,07,D6,87,94,65,47), + wwH16(1B,60,2A,BB,8F,A0,6D,58,22,A5,74,C3,25,FC,62,31), + wwH16(8D,8A,A5,3B,C5,A0,E1,04,E9,BB,D2,31,38,BE,76,5E), + wwH16(52,CF,EF,DE,72,C4,D6,2D,7B,7E,FB,65,37,33,28,37), + wwH16(29,72,8E,0A,C3,F7,BF,58,0A,66,89,35,BF,CC,90,03), + wwH16(57,9A,27,C4,57,3F,54,28,F3,8F,F0,23,4F,A7,97,B9), + wwH16(D7,4F,CD,92,0C,A0,4B,45,B0,AA,80,6C,43,59,0A,B1), + wwH16(ED,9D,8D,76,A9,8A,0E,86,9E,24,B0,E0,4D,DA,7B,8E), + wwH16(E0,5C,51,0C,B6,E2,74,A8,2A,9A,5D,D4,24,53,9B,9A), + wwH16(DB,8D,4F,48,96,10,29,18,D0,48,1B,36,C7,59,43,D4), + wwH16(FC,83,4A,EF,B7,96,A3,DD,03,3D,21,B3,FD,56,20,07), + wwH16(11,02,F1,7C,8E,EB,39,1F,B3,87,E1,F9,7D,4B,F6,1B), + wwH16(24,0C,F5,C0,FE,1F,CA,B5,16,B6,61,EB,86,DA,04,E1), + wwH16(03,CC,DE,49,8C,AF,85,82,1B,88,B6,30,9B,4E,0E,E8), + wwH16(87,11,7D,51,B4,E5,25,D3,2D,FC,9A,76,8E,9E,27,2F), + wwH16(6F,28,A0,DA,49,7E,21,F5,9C,78,77,36,B9,7F,E6,97), + wwH16(46,4E,15,AB,09,85,0F,6F,91,75,97,BB,84,EC,FF,23), + wwH16(C3,41,04,BE,E3,38,CA,3B,CE,C5,C5,68,93,64,5B,1A), + wwH16(DA,D5,72,E1,73,B1,2A,7A,B4,0C,5A,49,C2,7B,CA,2A), + wwH16(04,67,18,95,F1,CA,AD,BB,EB,75,03,29,68,77,8D,78), + wwH16(1B,B1,BC,40,6D,C1,6C,94,CA,5D,0B,07,C2,34,3F,1E), + wwH16(2B,32,D8,46,DC,78,1C,6D,7C,31,B0,7B,F3,50,32,55), + wwH16(2E,91,39,F7,CA,59,3F,50,1C,5E,66,E9,17,F7,9D,A4), + wwH16(FA,A5,09,15,C9,17,45,0B,02,DD,F3,B3,28,12,40,A0), + wwH16(E9,08,E5,32,41,2E,B5,DB,C3,F2,94,D7,9D,04,66,43), + wwH16(30,EA,DD,22,72,4C,7F,C5,BC,CF,46,C8,6A,3D,32,F3), + wwH16(E9,B5,31,D8,50,E7,D9,D5,B1,F9,FF,E4,9A,74,58,D8), + wwH16(B3,69,E5,44,5C,D6,B1,C8,A2,3A,4C,5E,77,B0,98,5C), + wwH16(DE,1C,1F,10,46,D9,4A,FD,79,EC,49,36,73,50,C5,EC), + wwH16(D4,23,B3,05,E6,FE,D2,5D,37,B6,80,3E,56,0B,77,4C), + wwH16(19,13,78,07,6F,B9,E5,72,AF,6A,94,69,36,55,B1,2E), + wwH16(D3,F2,F0,E4,35,4D,4F,70,68,AF,FA,98,98,B9,69,AC), + wwH16(41,93,8A,3B,46,04,9D,C8,27,F3,A9,44,B0,1B,07,D6), + wwH16(FB,9B,69,8A,16,03,2D,78,10,49,57,FC,96,1B,C0,6B), + wwH16(A0,80,26,0B,31,40,27,5B,4E,64,B2,64,42,91,C4,99), + wwH16(F6,A6,DA,C6,2F,4B,9E,11,76,EE,CF,CE,2E,93,3D,EE), + wwH16(C3,83,8C,CC,EC,AB,1A,CA,4C,4B,53,18,24,32,A0,09), + wwH16(69,DB,13,98,B7,F9,70,C0,73,FD,6B,EA,D1,DF,22,3C), + wwH16(78,0D,1D,1E,D0,5C,9B,FB,CA,15,74,0B,09,98,04,EA), + wwH16(B8,88,1C,EC,5E,A0,9F,8B,2C,7E,04,9C,88,8D,2C,10), + wwH16(EC,CF,31,5F,3E,C9,69,C9,67,2B,9A,5D,50,6C,DA,C4), + wwH16(40,C6,F1,51,D5,35,9F,0A,1B,03,78,B3,74,34,A4,96), + wwH16(25,5E,F2,FE,C2,4F,B1,C3,E6,C7,B5,41,46,84,60,AC), + wwH16(F5,D7,5A,88,45,6D,F8,20,E7,09,ED,35,21,1C,85,EB), + wwH16(9B,85,14,ED,C3,39,BA,C8,9F,0B,5F,5E,9E,50,1C,38), + wwH16(46,D6,57,BC,6C,56,36,94,87,31,0A,80,55,F7,BC,76), + wwH16(CC,5F,38,51,CF,B2,B8,1F,0B,95,B6,D1,FC,9E,98,BE), + wwH16(BB,A3,5D,75,98,A7,9D,B1,E3,58,71,CA,FE,D5,5F,5F), + wwH16(A5,BF,E7,0F,32,A4,D7,49,01,DB,84,B3,45,99,D0,EB), + wwH16(E3,51,28,3F,22,A2,C3,46,69,53,6A,66,6E,F3,C1,7C), + wwH16(9B,E3,B1,55,43,87,AB,CA,CC,4A,32,99,4B,CD,25,0E), + wwH16(7D,1F,DE,D3,9B,95,A3,E2,47,5B,EC,A1,A0,69,79,43), + wwH16(E6,FF,00,E1,B7,FA,F7,A1,53,CF,2B,BD,28,E8,DB,5F), + wwH16(3F,0E,00,57,7B,D5,BE,07,A2,EC,A5,D3,F9,17,C2,5F), + wwH16(DE,D3,2B,5E,DE,30,F5,76,FD,D7,F3,B2,FF,29,37,CA), + wwH16(3E,C9,5E,80,B1,12,72,F5,E2,9A,6D,9A,16,E1,8B,AE), + wwH16(15,D8,66,F5,B8,28,85,4B,A6,82,24,25,B6,E6,3A,C8), + wwH16(92,81,94,CA,AD,37,A6,75,65,F6,4C,71,F8,1C,B2,86), + wwH16(C9,0A,3D,F7,00,44,70,6E,8F,27,0C,BC,74,C3,C3,85), + wwH16(71,30,69,83,69,D5,88,1E,87,A1,0B,B2,CF,76,22,89), + wwH16(74,7B,F9,2C,90,5C,62,7A,9F,FB,4F,AE,A2,08,7C,49), + wwH16(39,B4,13,FE,7D,0D,6B,10,ED,F5,95,66,07,37,02,C0), + wwH16(02,57,80,E1,A5,EB,20,93,EF,E7,51,27,C4,59,EC,4D), + wwH16(D9,38,89,2D,C4,08,C9,40,F1,7E,54,52,FB,0B,4F,DD), + wwH16(90,5E,EE,19,EE,CD,DA,7F,F4,8C,58,02,E6,53,CA,B6), + wwH16(13,61,EC,BB,57,D0,3C,39,5F,2D,9D,77,42,9C,40,7B), + wwH16(B5,42,9B,27,4B,0B,DA,B2,99,BB,45,6E,4D,28,9F,9C), + wwH16(3F,FE,7D,FD,D9,4F,FB,0F,F7,75,21,D7,4F,84,DA,86), + wwH16(9D,E4,38,F6,8B,59,F8,3F,42,99,87,E6,27,B4,23,99), + wwH16(4D,47,DC,3D,39,A1,1D,8D,8A,A1,12,30,FA,C5,90,49), + wwH16(8C,A0,C6,25,13,A5,FB,31,4F,DF,3B,A9,74,CB,46,FA), + wwH16(C1,FA,AB,F7,15,B5,CB,18,33,28,75,A0,F3,94,58,39), + wwH16(9A,02,C5,87,57,A8,3D,AC,92,39,09,66,1E,30,38,B1), + wwH16(A3,C5,FD,75,DB,85,E4,23,E0,A5,DD,B0,4A,9B,0E,E6), + wwH16(D3,90,82,E4,6D,D3,0B,EB,C8,01,05,31,DF,E1,75,C5), + wwH16(80,F6,4E,8A,98,8C,F9,83,83,EA,C1,EE,4E,30,E2,E4), + wwH16(9C,11,E8,35,F2,C6,7D,3F,76,87,C6,4B,2E,F7,D3,F0), + wwH16(66,AE,9F,89,C0,59,B3,9A,F8,2B,E5,89,D8,B2,86,A8), + wwH16(29,D6,89,C3,AD,51,84,C4,59,58,FE,F2,AB,3A,7F,75), + wwH16(23,56,23,39,74,44,07,93,A7,22,39,05,D7,F8,D3,2A), + wwH16(7C,45,43,1E,1A,28,40,36,44,E5,AA,9E,4C,68,A1,47), + wwH16(E8,FD,36,0D,D5,4B,AD,54,5A,16,DD,EB,1F,EC,EA,71), + wwH16(F1,CF,B8,DF,0E,0B,25,FF,BF,A1,10,B2,5C,E1,0E,F2), + wwH16(13,17,A2,B0,F7,7D,0D,02,87,BE,F9,02,4D,D0,A1,90), + wwH16(31,0A,F2,8D,5C,BE,2F,7C,BA,28,E6,4B,77,F4,32,23), + wwH16(6C,C8,2B,C7,FF,80,7E,37,D9,CB,A2,F1,77,43,C4,86), + wwH16(5E,9C,AC,08,BE,DD,75,2D,40,17,B0,BB,C1,BA,60,6D), + wwH16(F3,E7,EA,7B,E5,A0,FC,04,22,9D,48,0B,37,B0,09,A3), + wwH16(65,C8,9D,46,74,5E,79,07,1F,87,FA,67,3D,B8,03,C5), + wwH16(52,87,57,EF,E8,E7,CB,04,60,13,5A,31,68,17,B7,5E), + wwH16(A9,F8,22,13,00,D7,F8,18,56,39,26,08,18,E7,44,D8), + wwH16(F2,B2,8A,E7,1B,28,0F,01,E4,94,90,8E,C8,1B,17,1A), + wwH16(24,8E,57,0A,CB,DF,EE,3B,30,D2,F1,70,4C,69,4C,66), + wwH16(77,49,D0,76,28,1A,0C,B9,75,C3,1E,43,2A,ED,5C,42), + wwH16(57,A8,60,AC,66,9B,A3,4C,40,0F,04,26,85,9C,6D,E5), + wwH16(C9,F7,F5,36,A4,77,80,C7,5C,40,02,B4,12,A5,0F,FF), + wwH16(DE,6A,9B,86,00,D2,19,27,3D,E2,98,65,5B,B9,51,C1), + wwH16(8A,F0,58,02,63,47,DB,8A,51,F0,3A,CC,42,19,C5,F8), + wwH16(8E,8A,0A,13,EE,00,63,E2,29,8E,0E,E6,CD,A2,1B,DF), + wwH16(32,96,E2,6C,2C,00,5E,BD,C7,36,BB,B3,87,53,9E,2A), + wwH16(89,9F,1D,1A,65,C9,00,33,52,76,06,63,A6,AF,2A,64), + wwH16(6E,B2,AF,9B,A8,8E,38,C5,21,4F,36,76,94,05,98,6C), + wwH16(11,20,BB,94,20,A0,C1,CC,98,C0,D0,B1,A6,5E,B8,2E), + wwH16(42,C7,2A,24,F4,CB,5F,DD,36,BC,2B,4A,5A,88,01,08), + wwH16(B7,7F,74,1E,72,FC,DE,5D,25,41,0B,12,92,84,32,7A), + wwH16(BF,DB,12,C4,86,CD,42,FE,D0,4F,CC,3F,42,41,F9,82), + wwH16(AF,E9,AC,72,21,AF,2D,A4,79,4C,09,B4,FD,18,50,03), + wwH16(A3,E1,7A,FF,49,D9,50,03,08,77,25,8D,13,FB,29,41), + wwH16(B9,23,FC,B9,D9,47,93,19,62,7B,D2,90,38,79,87,2E), + wwH16(0B,46,02,AA,C8,C7,B2,89,D6,F7,4C,40,86,52,8C,AF), + wwH16(9F,16,9E,BD,66,F8,13,5C,C4,E0,F5,D5,54,B1,B3,B2), + wwH16(C3,E8,E9,A4,4E,5E,6F,DE,C5,51,B9,EA,A9,D6,F9,08), + wwH16(D3,A1,E5,5A,75,E8,8D,61,D0,BD,64,75,1B,E0,F2,F1), + wwH16(50,7F,76,15,38,1E,60,F6,94,26,F0,47,00,0F,B1,08), + wwH16(82,02,D1,EC,38,A7,C8,2A,C6,95,41,3F,98,8E,4A,AF), + wwH16(53,D0,E6,5C,D1,E2,7E,02,D1,E4,6D,B6,CD,94,6E,B9), + wwH16(2D,A7,FD,6B,71,4A,61,56,86,15,03,CB,16,1F,2C,8C), + wwH16(34,D3,42,53,E4,70,41,3D,60,0F,B7,75,6C,D0,47,34), + wwH16(11,62,09,69,27,44,4D,5D,16,9B,9D,8F,13,82,53,39), + wwH16(8E,DE,9D,E6,FB,1D,79,8F,6D,F3,79,77,10,7B,C4,18), + wwH16(B2,87,03,DD,DB,66,3A,BF,0B,3B,C1,F0,0D,EB,09,18), + wwH16(0E,E6,D3,2C,F4,0A,AF,8F,44,15,91,E2,24,B5,EA,60), + wwH16(28,88,8B,F2,A2,63,FD,24,B8,09,1D,37,B6,21,78,2D), + wwH16(D9,F8,72,0D,8F,43,F3,B1,BB,A6,61,4E,6B,1D,72,05), + wwH16(A9,BF,30,5B,8A,89,BF,4D,8B,3A,58,02,78,60,11,98), + wwH16(8B,7C,59,5F,1B,D8,35,0C,46,90,09,CF,35,D5,2F,35), + wwH16(09,0F,8A,1A,B1,BD,C2,AE,04,DD,D6,42,63,4E,D8,68), + wwH16(61,95,C6,FB,87,FE,E0,26,75,FB,BE,E3,19,5D,DE,4A), + wwH16(14,9A,DE,7F,D0,4A,FB,C4,6B,D4,2F,CF,F0,45,97,8A), + wwH16(EB,F6,2A,4C,90,85,34,F1,C2,BA,FF,C4,EE,6D,C4,2E), + wwH16(CE,A8,19,A1,36,5E,60,AC,09,57,A0,EA,8D,2E,90,6C), + wwH16(A6,36,89,14,89,BD,9A,70,49,1A,EE,5C,A3,7D,6B,2D), + wwH16(D8,50,69,DA,40,A8,7C,34,DA,9D,79,5E,F5,39,09,F9), + wwH16(61,82,12,6B,EE,95,68,19,97,3B,9A,43,C8,E0,F9,43), + wwH16(2B,C9,AD,2C,64,A5,B4,ED,29,34,58,42,45,17,48,A2), + wwH16(A3,BB,EE,25,B3,13,DB,E1,40,CA,8B,95,1C,92,54,64), + wwH16(E0,9A,2A,6E,BD,2A,1E,AB,C2,01,CA,EF,6B,BF,93,B6), + wwH16(49,AF,66,A5,4B,3A,26,33,77,F4,EB,8E,B6,D8,E6,CA), + wwH16(34,55,85,10,9A,50,FD,05,2D,B8,15,98,8E,65,F6,27), + wwH16(BA,E1,BC,32,FC,21,D5,C7,4D,5E,A4,F3,E3,A6,8C,8F), + wwH16(BD,AA,C5,D5,BB,30,0A,7B,CB,05,14,AC,EA,21,F2,BD), + wwH16(0C,79,85,9C,80,E8,9B,E6,AD,AE,CB,03,A2,2A,7D,51), + wwH16(A6,CE,DF,C7,5F,0C,3C,FB,20,7F,CE,C2,8A,EF,69,10), + wwH16(44,0D,02,3A,FB,B0,31,08,C0,FF,3F,85,02,73,4D,55), + wwH16(01,1E,3F,50,E5,52,C5,81,42,6D,CC,59,66,7E,D0,9C), + wwH16(A6,AB,C8,04,0C,19,95,6A,A9,C2,71,6C,21,78,D7,00), + wwH16(33,37,31,F1,2E,FC,4E,DA,0A,D7,40,58,66,73,73,88), + wwH16(07,0F,21,F9,CE,AF,E5,BF,4E,CD,9B,8E,5A,4E,F7,1A), + wwH16(80,82,A2,69,36,93,51,55,51,AC,3C,48,F2,B0,EB,64), + wwH16(96,9E,BE,81,D4,BF,19,2B,58,B4,07,D5,08,57,FC,67), + wwH16(45,93,1E,D5,D3,B4,3C,75,CC,90,6A,9C,89,C9,A3,FA), + wwH16(F3,D8,BF,98,8A,6B,62,BE,ED,89,E7,80,00,9C,61,A1), + wwH16(EB,03,72,44,7B,07,91,53,24,DC,E0,6C,9B,D6,F4,C4), + wwH16(CE,C9,7E,D6,EC,0C,4D,FE,8E,EE,DC,59,13,5A,F6,1C), + wwH16(2E,79,9B,B5,C8,61,10,41,D8,C9,D8,8B,43,81,43,79), + wwH16(DE,96,53,98,E7,94,B6,AE,4D,A6,51,B3,27,9B,2C,51), + wwH16(BF,A7,D5,A8,F1,13,CA,78,D4,2C,82,AA,6E,A1,F7,88), + wwH16(15,64,5F,1F,B7,98,6F,AB,A3,C0,A9,C3,A3,2D,DE,59), + wwH16(5D,BB,D8,5D,D0,04,AF,29,DB,56,02,61,6E,EE,C2,B4), + wwH16(5C,E9,5B,9D,DF,D9,BD,C7,E6,F8,0E,5A,03,2D,0A,75), + wwH16(18,66,B4,4C,21,9E,F6,1D,24,46,34,BA,D4,32,B2,21), + wwH16(9D,F5,90,81,A1,56,07,C5,C3,4D,1E,04,E7,D8,68,EF), + wwH16(31,ED,9E,05,A1,5B,3B,16,C5,6D,E8,0A,0F,92,34,C2), + wwH16(44,AE,44,6F,4C,83,8B,13,C1,F5,90,58,DF,C8,42,EC), + wwH16(52,5B,24,12,C1,28,F9,93,96,27,D2,ED,25,0F,90,A7), + wwH16(5E,3D,54,3A,A6,B5,1A,B8,33,64,9C,1E,DA,24,24,41), + wwH16(23,06,CE,CB,8B,85,BA,81,5E,E8,E8,A8,CF,1C,E4,80), + wwH16(B6,5B,D2,32,23,66,52,AF,80,C7,3D,49,C5,5C,DB,4B), + wwH16(C6,F1,1C,0A,67,5A,50,1B,B1,D5,ED,DE,60,AB,8B,49), + wwH16(AB,F0,89,4B,90,A9,37,7F,7A,8D,F1,19,4C,1C,61,EF), + wwH16(B8,F9,CD,02,FA,B7,C7,B0,4C,E0,C7,8A,13,D1,E5,B4), + wwH16(3F,A6,EA,9E,F8,20,E9,1D,A4,38,0E,CF,B9,A7,E1,9A), + wwH16(8B,44,22,BC,A5,A9,49,CF,A9,5D,69,FC,B9,AD,94,37), + wwH16(9F,2B,67,CF,6E,2A,38,99,04,8F,1B,92,2F,BE,8B,BB), + wwH16(0A,B2,0C,8C,51,4D,64,D3,17,A0,FE,A0,05,B9,B2,B7), + wwH16(EF,8C,72,C0,F7,90,68,73,EA,8A,98,6E,BD,9E,2D,24), + wwH16(26,5A,0C,A7,AE,C0,B6,34,50,24,F0,CE,FD,A2,19,EB), + wwH16(31,5F,6A,E5,63,F0,92,BF,29,23,38,A3,BE,49,3B,43), + wwH16(3D,2A,45,93,FF,87,98,D3,43,E8,17,2D,8C,61,91,B1), + wwH16(B3,3F,F5,CA,DD,0A,AA,52,35,56,22,AE,89,C0,55,13), + wwH16(7B,6F,E3,E6,27,1A,BE,6C,83,72,79,FD,DC,7A,5A,FA), + wwH16(B9,A4,D8,90,0E,A1,DF,D5,C4,28,07,A7,9D,E0,36,51), + wwH16(CF,F6,6C,D7,50,16,EF,70,CD,E9,9C,64,F1,8E,22,12), + wwH16(68,22,41,26,BF,6D,1B,EA,7B,26,39,20,4A,C8,BC,79), + wwH16(0A,4E,13,D7,46,CD,DC,2A,F2,8A,BC,B6,8D,61,BC,57), + wwH16(20,63,46,E1,06,48,30,3A,ED,CE,35,B7,56,6A,15,3F), + wwH16(AA,0A,7D,5F,99,B5,47,C3,F5,C3,AE,B6,66,14,0F,FF), + wwH16(D4,88,1D,32,65,82,7B,80,69,78,9E,10,49,17,F1,B3), + wwH16(AC,B7,53,39,AD,DF,BA,F1,89,24,55,57,D5,F1,7E,29), + wwH16(EE,CD,C7,A1,D8,B1,25,74,BB,0F,08,16,9B,5C,35,22), + wwH16(1D,5C,0D,6F,8D,95,0E,B0,7D,80,1E,C8,8C,81,B8,6C), + wwH16(2C,E0,9C,2E,30,7C,57,EE,55,7A,0A,38,41,E8,6A,1C), + wwH16(75,AD,F7,3D,B2,6D,15,9D,83,FB,25,BB,B4,C9,A8,3C), + wwH16(A5,C0,CC,65,31,DD,A2,51,38,40,88,B5,EC,75,BF,D3), + wwH16(1A,D7,A0,93,00,6C,DC,16,57,BB,8D,21,3D,6E,AB,39), + wwH16(F6,B9,2E,75,9E,C9,DE,45,4E,F2,F0,A6,20,30,38,C0), + wwH16(25,39,35,18,3C,BA,B8,92,39,BC,44,C6,88,FE,86,0F), + wwH16(4C,D5,46,73,E6,F0,A6,10,91,82,01,0E,2C,6E,17,18), + wwH16(0F,5F,7A,3D,E5,5E,81,4C,C4,F0,83,86,71,D1,BB,78), + wwH16(48,83,80,19,E8,5C,39,9E,EC,31,96,B7,01,39,D8,70), + wwH16(1A,77,31,D3,06,65,07,32,EA,9D,7F,56,F0,B4,E0,73), + wwH16(82,11,E8,9F,32,60,DC,B6,FA,88,E4,C1,C1,59,1A,C7), + wwH16(25,3F,60,B8,1C,E9,ED,ED,CE,83,BD,41,98,6A,16,F3), + wwH16(2C,8C,F4,E7,33,3D,E4,45,51,A1,49,FC,B2,5A,13,8B), + wwH16(5F,D2,57,6B,A3,6B,B5,B5,03,57,C5,E9,F0,34,67,DD), + wwH16(7F,FC,C9,F8,A3,07,C6,FF,86,F1,0A,F4,A5,B1,12,3E), + wwH16(A8,C8,FA,0B,76,5D,0D,A2,FD,00,2E,C6,13,FC,D5,49), + wwH16(01,3B,7D,F0,CF,97,0F,28,BB,E4,A1,5B,CF,F2,60,0C), + wwH16(9D,23,CE,2D,99,8D,0C,25,F4,B7,DB,A9,F4,7C,59,8D), + wwH16(A9,3D,83,7B,A5,F5,A2,AA,76,DA,1F,33,9D,76,4B,00), + wwH16(A3,A4,02,A4,DE,47,D5,55,D7,51,74,C5,E8,58,B8,41), + wwH16(02,45,C9,8B,F8,60,F7,2C,9D,6A,B1,09,29,33,59,43), + wwH16(57,B8,CC,DE,0B,76,50,FF,2C,B3,03,28,B1,E8,5B,0B), + wwH16(72,AE,37,4C,6D,0C,E3,62,6B,A5,B6,81,9C,95,B6,FF), + wwH16(C1,F5,B3,AD,F7,51,A0,DD,7E,68,9B,69,C8,E9,80,C0), + wwH16(CC,78,B6,DF,81,00,27,99,02,77,4C,1C,31,BF,1A,3C), + wwH16(9C,70,77,6F,D2,30,C5,9B,43,60,B4,16,F9,9D,B7,F4), + wwH16(BB,9F,71,88,26,02,2F,A0,DC,18,B6,1B,94,1A,8D,F2), + wwH16(5A,5E,9A,70,52,92,21,0E,9E,30,D2,30,CA,6F,35,14), + wwH16(C7,6F,26,FD,58,94,41,D2,97,33,B1,4D,FF,D0,4A,5C), + wwH16(9F,06,75,5B,FF,07,A6,F1,9A,18,3F,87,19,B5,CB,47), + wwH16(C0,90,92,85,39,9D,B2,A7,D1,51,E9,98,99,E4,20,56), + wwH16(7D,47,64,EB,3C,23,D8,9E,98,0A,2E,34,26,B4,B5,58), + wwH16(B0,7E,1B,23,69,90,9F,77,77,93,3A,3C,32,E9,D0,6E), + wwH16(51,29,B3,18,E3,F9,BF,24,65,DC,04,AF,A8,68,4A,59), + wwH16(DB,D1,BF,0E,52,CB,47,1C,CC,BA,AE,12,71,92,7D,D0), + wwH16(F4,B6,29,FE,F0,90,AA,80,3E,9A,19,59,DE,9A,6D,65), + wwH16(17,D5,0F,0A,FA,A1,2D,E1,C5,A4,DB,08,EB,AF,CE,86), + wwH16(F9,CA,AE,AE,D3,59,8F,5B,2E,6C,26,E8,A5,FE,9B,49), + wwH16(70,C5,6A,C0,D6,3A,6C,2E,E5,EB,BB,13,4A,06,F3,1F), + wwH16(8F,C4,53,F9,D7,14,0E,9B,14,75,CA,F7,56,1D,FE,E0), + wwH16(2C,6F,7E,2B,0B,AE,07,35,B4,8D,52,46,85,6D,1D,92), + wwH16(81,53,46,8B,AD,4E,19,F2,2A,23,20,53,8D,2A,2D,21), + wwH16(03,29,39,47,0B,CD,E8,82,3E,71,F1,81,F0,EC,D8,4E), + wwH16(08,C5,A7,C7,09,A3,EA,B2,17,D0,FB,C7,E7,61,0E,F8), + wwH16(C9,52,B5,AF,57,F0,D2,B0,6C,5F,95,CB,87,2D,98,47), + wwH16(73,C6,03,94,D7,F6,4D,CA,15,18,45,3B,2E,70,15,87), + wwH16(94,0D,EC,D7,FB,D2,50,22,05,5A,7E,82,D2,AD,53,28), + wwH16(EB,4B,F6,B5,AA,CD,0B,98,4E,D7,92,63,C7,DD,9C,42), + wwH16(AC,88,C5,E1,48,F7,4C,2E,A1,48,45,F1,B7,40,F3,E4), + wwH16(37,C8,56,C8,70,2F,7C,32,33,11,07,4D,69,19,1B,23), + wwH16(7F,8F,D8,52,55,83,A5,EA,B1,42,D1,B0,ED,02,80,79), + wwH16(D4,43,1A,3A,2F,13,87,26,6C,1C,75,4F,7D,D1,65,4E), + wwH16(3A,C7,04,79,6B,6D,4A,42,02,EE,8F,26,7D,D5,CF,FF), + wwH16(72,BE,18,E4,7B,4C,33,70,60,A6,72,4C,7A,5D,B6,C3), + wwH16(7A,D0,58,9F,7A,2D,0C,4D,44,52,97,60,8D,EE,7E,B3), + wwH16(25,1A,19,6A,00,47,E4,63,88,36,1E,22,18,FD,6D,41), + wwH16(B6,D4,F0,DC,56,05,6A,51,27,1E,BE,50,AC,B9,93,AC), + wwH16(9F,7A,08,22,40,25,DB,FB,52,72,83,08,47,6C,04,12), + wwH16(74,75,79,AF,E0,DE,38,5E,07,64,36,DF,33,D2,2E,41), + wwH16(DD,E8,CA,D4,BB,AA,C8,07,66,DD,23,7C,DF,60,B3,A0), + wwH16(88,B4,76,60,93,33,F5,C3,66,4D,09,98,D3,E3,CB,20), + wwH16(5E,2B,D6,B6,53,32,10,80,1C,7B,7F,F4,9D,8D,7D,72), + wwH16(72,35,3A,58,2F,F9,1A,DD,97,9D,2E,DA,17,6D,07,4E), + wwH16(F2,56,E4,B4,9F,3F,2C,BC,87,46,33,33,CD,3A,39,DB), + wwH16(CA,9C,17,AD,19,F0,08,51,5B,8F,3B,36,B0,2B,ED,86), + wwH16(88,DC,5A,75,76,9A,34,92,87,4E,BB,26,0D,4B,BB,87), + wwH16(80,D2,9F,3D,31,04,21,98,91,90,D0,38,C2,8D,39,B2), + wwH16(5A,2E,45,3E,99,BE,E3,4D,64,A6,21,8B,54,C9,4E,E6), + wwH16(80,24,81,E0,94,F3,F8,9E,C8,31,B9,4F,76,27,F9,F0), + wwH16(8F,81,2B,6C,8C,EB,80,59,BF,09,CD,87,32,59,90,1F), + wwH16(18,96,EC,A0,50,E2,E8,13,85,AE,D5,8A,CB,12,5A,BA), + wwH16(86,BD,F3,A5,F0,CA,D4,1A,63,71,E8,83,25,76,33,F8), + wwH16(ED,EE,D7,6B,F6,13,5F,EE,5F,46,DE,6E,6D,9C,94,BC), + wwH16(79,31,4E,3F,5F,5C,01,FB,B4,DB,E7,BB,AD,7D,FD,9D), + wwH16(47,5B,73,9C,E8,4F,BC,67,E9,3D,72,9E,10,EC,D9,C3), + wwH16(8E,DE,DC,AB,C4,63,23,FD,0A,8A,59,67,28,77,AC,38), + wwH16(13,0E,95,7B,02,77,C6,5A,97,45,8A,B7,DD,F7,EB,23), + wwH16(48,F9,25,98,B9,C3,C9,B0,D4,09,77,DE,35,95,3E,89), + wwH16(45,44,3E,B6,E5,84,D4,F3,3A,B3,E3,8C,D6,FA,FF,6A), + wwH16(BB,F8,96,46,4F,F7,14,8C,B0,36,75,ED,07,2A,AC,38), + wwH16(DE,06,8A,A6,37,58,52,3A,35,AF,35,95,EB,7C,34,22), + wwH16(38,11,33,85,29,0E,7B,E7,5B,B3,67,BB,83,6A,93,5C), + wwH16(7B,75,67,A7,A5,3E,4A,FA,21,62,D3,A9,52,20,4C,F7), + wwH16(32,27,EB,5D,4C,54,5E,1D,93,D9,A7,0B,21,9D,7E,6D), + wwH16(92,E5,BE,DE,E1,50,77,E8,C7,58,5A,6C,F4,C2,00,27), + wwH16(12,EB,2C,DB,C4,71,76,5D,E7,ED,67,70,9B,1E,CF,4D), + wwH16(27,C4,8D,5D,7E,D2,25,4F,5A,34,94,31,B3,BC,E4,52), + wwH16(37,35,3A,5A,ED,D7,46,54,0E,6E,32,7D,2D,47,C4,E6), + wwH16(4F,5E,67,66,94,6E,E8,08,FF,16,60,98,5D,DD,10,12), + wwH16(0B,F1,DE,29,97,B2,79,72,0B,4B,D9,02,6F,61,17,B9), + wwH16(F6,42,52,4F,E1,1D,11,98,1B,1F,C0,30,82,19,DC,48), + wwH16(E2,CC,19,8F,69,63,33,F0,CC,81,38,14,74,4F,40,C7), + wwH16(4D,D7,71,45,9F,02,9A,BA,01,98,FD,33,2A,69,A7,DE), + wwH16(FD,3E,42,09,05,EB,A5,54,E5,00,BE,C5,C7,D0,FF,91), + wwH16(8B,FD,77,8A,B0,18,3C,CE,F1,12,1E,9C,C3,29,06,C5), + wwH16(71,83,A3,19,22,22,DA,62,F4,3E,B2,B1,82,2E,25,72), + wwH16(A0,62,83,E3,0C,8C,4B,72,0D,2A,BF,2A,A4,19,43,4D), + wwH16(0A,73,DC,7E,2D,30,0B,B7,99,8A,F3,58,7E,3D,68,81), + wwH16(09,E2,18,6A,99,C4,F6,DD,B3,DA,96,03,EB,B1,28,23), + wwH16(CB,62,75,8F,E1,5E,2C,4E,EF,B5,EE,81,81,BD,90,E0), + wwH16(8F,AB,8B,17,BC,AC,32,6D,4E,E5,96,9C,07,0E,E8,CE), + wwH16(74,56,B9,28,FD,E4,0F,DA,40,6A,2D,FE,F0,CB,1B,A1), + wwH16(6D,0B,EB,60,C9,6C,67,F0,73,BE,E2,AC,06,52,45,92), + wwH16(CD,C2,59,DB,2F,40,05,A3,B0,CF,94,46,9B,E1,DE,23), + wwH16(68,AD,36,7F,18,9D,0A,E5,39,35,22,8C,F1,80,41,B7), + wwH16(5A,A3,69,29,D3,C4,CF,3B,3A,8F,D7,B2,28,B5,CC,F9), + wwH16(14,6B,1C,53,FC,07,BC,3B,D1,91,81,85,D3,87,68,38), + wwH16(B6,C7,D0,49,77,E1,62,4E,AB,E5,A5,D6,39,05,65,DB), + wwH16(9C,34,DD,1F,F7,B1,52,1C,F3,B7,A4,53,A6,09,BB,08), + wwH16(2B,65,64,81,67,A5,06,6D,27,F8,17,68,5E,E3,68,CC), + wwH16(8E,D2,4E,EA,E6,CE,44,67,2E,78,F5,26,42,CC,71,D6), + wwH16(CE,CC,98,DC,70,7D,B8,E9,68,31,B6,AD,10,1D,9C,43), + wwH16(A5,A9,F1,B7,B2,B1,60,F9,19,C5,ED,B0,4F,D7,6A,4B), + wwH16(EA,F5,66,85,43,31,B6,FE,FB,9A,6B,E9,0A,0B,B1,0B), + wwH16(0C,3B,C5,00,4C,65,7E,5D,82,FF,EF,20,54,4B,80,FC), + wwH16(07,F8,D6,EB,64,5A,D9,CF,2B,D4,AE,7D,5A,C2,40,1C), + wwH16(39,57,23,91,12,E2,4D,14,7B,F6,7D,53,55,26,C8,4C), + wwH16(12,63,FE,0F,40,1C,C5,9C,D5,EF,30,77,30,59,FA,88), + wwH16(AF,08,A5,80,72,63,D3,BC,61,D3,A3,55,11,45,8B,15), + wwH16(9A,51,12,D0,0D,85,09,96,31,AE,70,6E,C1,03,EB,4D), + wwH16(AE,77,A5,29,9B,CC,F0,9F,52,73,60,DC,FB,C1,8F,82), + wwH16(D8,69,0C,AA,31,DB,A8,CD,F8,45,C0,21,0C,1F,59,FA), + wwH16(14,D5,48,1A,2A,57,3B,83,60,B2,96,5F,06,96,F1,42), + wwH16(5D,11,B6,E2,2B,92,48,61,42,4B,72,F9,6B,F1,72,E5), + wwH16(B1,79,7A,4E,27,6B,C1,6D,15,67,3A,BF,B8,46,13,0C), + wwH16(04,17,CA,70,CA,92,B1,9B,EF,E8,AC,23,C6,E4,08,95), + wwH16(78,4E,19,B9,09,C2,70,53,C6,F2,49,E1,11,4B,48,89), + wwH16(72,EB,74,3E,E8,64,B5,FB,51,93,1B,18,6D,A4,3D,F5), + wwH16(1E,1C,58,4D,F9,88,66,08,C6,49,DE,E8,04,7A,6D,14), + wwH16(77,6C,0B,E3,C1,BD,E2,C2,14,9C,DF,FC,49,7A,12,FF), + wwH16(BE,A1,E7,19,22,A3,22,D0,49,F8,AA,D1,D7,22,0C,2A), + wwH16(5F,64,1C,4C,94,16,81,1E,7A,62,D4,F3,7A,31,BE,5A), + wwH16(1F,36,10,70,52,1D,BB,AA,CB,64,08,AB,46,2F,F4,24), + wwH16(3F,44,AD,BA,20,CC,6E,6E,CA,63,EB,18,10,F8,70,A9), + wwH16(93,AB,0F,19,47,11,35,57,F9,74,CC,76,22,4C,51,3F), + wwH16(99,4A,D1,7D,2D,CD,71,86,6D,C8,06,03,68,28,F6,00), + wwH16(5F,F1,3B,63,A1,31,D0,F1,A8,CF,34,1A,CF,21,91,CA), + wwH16(32,F1,5A,31,3A,2C,FF,F5,2D,BE,D3,64,FA,68,D6,CB), + wwH16(73,AE,0D,75,DA,AC,DB,26,6B,13,26,6F,6F,45,44,F9), + wwH16(A6,42,AE,1A,9C,0A,FC,06,1F,0D,72,F8,75,DE,B8,8D), + wwH16(10,B2,C3,88,31,64,29,BD,F5,48,FB,C9,46,73,DD,EC), + wwH16(E9,70,81,E9,90,87,F2,B6,72,C5,7A,30,06,5B,23,1F), + wwH16(7F,ED,CC,F5,32,21,F8,D1,19,BB,69,B6,BF,45,77,A9), + wwH16(9E,DD,B8,62,8B,CD,28,52,82,6E,43,7A,89,BB,34,E5), + wwH16(DF,00,10,30,99,87,3A,8F,4E,A1,25,04,38,73,10,9D), + wwH16(72,FE,2A,46,9A,AE,1B,6A,DA,AF,EC,5B,74,EC,26,0D), + wwH16(7B,46,38,14,55,B0,C0,7F,58,BD,02,50,05,A4,BF,FC), + wwH16(AB,EF,81,E3,F0,AE,FB,97,2B,E9,E3,B8,C6,74,A6,DD), + wwH16(5F,4B,F1,BF,04,E5,A6,60,D0,09,B5,79,94,CA,F4,53), + wwH16(FE,41,EF,90,EC,5F,37,AE,A2,A3,D4,DC,ED,20,A1,78), + wwH16(58,CF,7F,E2,85,84,E7,AD,66,31,C7,A8,77,DF,89,A6), + wwH16(11,C5,5B,04,E7,7F,94,75,7F,CD,A2,93,59,2E,BA,F7), + wwH16(01,68,D0,CF,BD,7A,95,F2,2A,39,F6,0D,7F,B1,D0,FD), + wwH16(52,0D,70,5E,0A,94,06,E2,70,03,32,D3,02,FF,C2,D4), + wwH16(56,1C,E3,A3,F2,8D,C6,57,D6,0D,44,28,D6,25,EE,4A), + wwH16(FF,D1,A4,B5,75,4B,D9,FD,F3,61,59,B6,87,30,FF,DB), + wwH16(5D,AE,5B,26,28,0A,00,68,32,F1,69,84,0C,CD,CC,A0), + wwH16(3F,D6,F3,3B,85,DF,22,8E,7C,7B,E1,3E,03,4E,BD,7A), + wwH16(8C,2F,8D,36,2B,41,66,37,04,FC,3F,17,DA,7B,39,37), + wwH16(83,6B,68,6C,9F,0D,FE,F7,00,37,B5,69,0F,FD,71,96), + wwH16(BF,E0,5C,F5,E8,51,5F,97,87,96,B5,B2,00,EF,6B,6F), + wwH16(F6,31,64,3B,81,FC,01,11,48,5B,8D,D3,C1,CE,E7,B0), + wwH16(81,0D,A6,7D,38,DB,EC,F1,50,DA,44,2B,3D,3A,C0,66), + wwH16(08,A0,7C,FE,E6,F4,AC,6C,59,E8,93,23,4C,3C,20,E9), + wwH16(7B,49,0B,E9,27,7E,B9,69,94,20,CD,59,66,F6,E3,14), + wwH16(2B,63,07,F6,96,DB,23,46,3E,58,14,C4,70,7B,8A,8F), + wwH16(4A,93,45,C1,B2,C9,7D,22,D8,0C,86,B8,FE,6A,06,F3), + wwH16(2B,5D,25,F7,8B,64,55,1E,8A,61,EC,99,1F,69,F6,3D), + wwH16(07,52,CA,7A,6C,A6,7C,29,26,5F,20,DE,95,28,0D,5E), + wwH16(6A,36,62,66,07,0E,EE,B7,BA,69,31,03,26,55,F0,65), + wwH16(CE,3B,75,D7,37,23,F4,8E,8B,41,A5,11,51,FE,7E,48), + wwH16(06,1E,0C,8D,42,56,7A,CA,7D,52,96,29,6F,AA,E3,E7), + wwH16(6A,2F,C4,7A,83,15,9D,1C,05,F1,34,8F,FC,C1,88,66), + wwH16(03,49,42,2C,B9,A2,6C,DD,91,E3,EF,B6,EA,F8,B6,A1), + wwH16(0A,CB,01,8A,BA,E7,4D,65,59,C9,FB,9D,F0,F7,E9,DF), + wwH16(D4,C7,9A,4A,B8,8C,E1,0D,3A,57,78,F4,FD,F1,F1,27), + wwH16(0E,87,2B,35,75,4E,47,A0,C5,99,65,78,64,FC,D9,98), + wwH16(17,5B,4A,3A,24,A3,AB,F6,C7,D6,B7,37,D5,75,F3,1F), + wwH16(31,74,84,B2,BC,6C,E0,73,1D,0C,AC,1E,71,6C,72,D0), + wwH16(BB,FB,EA,2C,84,83,DD,08,C9,B4,30,41,1E,00,A7,41), + wwH16(9E,8C,4F,63,18,8F,75,B4,0F,F8,16,40,C0,CC,C4,78), + wwH16(25,EB,1D,B0,03,72,08,79,AD,10,9D,BD,41,AD,E6,7E), + wwH16(FF,59,CB,21,67,E3,2D,BA,D2,B7,D5,6A,CD,0F,18,4E), + wwH16(C7,48,93,98,35,6D,CB,07,37,3C,87,2B,58,09,1A,E2), + wwH16(3D,D4,04,1D,2F,A6,AF,7D,40,72,C0,D7,53,DF,87,80), + wwH16(DA,92,A5,36,14,E4,11,83,C1,26,7D,1F,9C,CE,B5,EF), + wwH16(88,FE,42,AF,FD,A8,8A,8C,45,03,A4,FE,CA,13,93,0A), + wwH16(BB,B0,C6,A8,0C,E4,EA,0C,42,9C,49,41,CD,40,6E,55), + wwH16(54,47,9A,B9,87,0D,4E,15,91,7E,5B,F9,E4,00,32,13), + wwH16(A4,CB,C8,18,70,18,DA,9F,97,52,BA,A8,5E,41,C3,FB), + wwH16(AA,F2,12,78,E4,01,0A,2F,C8,D4,27,ED,C8,AF,E7,A6), + wwH16(8A,71,07,AE,5D,8C,EB,88,EA,39,84,01,B3,00,2C,3B), + wwH16(27,11,0F,2E,4C,7D,00,2C,34,CC,6C,5E,AD,FE,A5,F3), + wwH16(55,80,8F,92,8B,87,91,49,44,06,20,B3,74,F1,82,B4), + wwH16(62,D8,90,06,7B,E4,91,3B,E7,CC,53,DE,63,0F,25,D4), + wwH16(99,84,B5,C2,4A,B0,78,1E,66,67,52,91,F1,67,DD,9A), + wwH16(5F,D2,45,AB,D0,77,10,F4,6C,F4,5B,1B,E8,39,BA,A7), + wwH16(5B,FE,72,94,96,03,5A,7C,54,FD,F0,8D,F0,33,E5,0B), + wwH16(C3,65,8D,6D,B0,57,3B,83,98,07,C6,41,C8,48,B2,01), + wwH16(C5,AB,82,B7,55,E5,53,A8,81,C5,94,1C,AB,65,85,C8), + wwH16(02,EA,7B,D5,00,A3,99,80,AE,29,92,AC,54,0A,87,0D), + wwH16(06,DF,95,C6,9D,91,29,2D,A3,3B,37,BB,58,75,87,47), + wwH16(33,E2,3B,25,9B,56,61,23,2E,9C,39,C3,41,27,F4,85), + wwH16(32,5C,68,64,75,E7,9A,DF,C8,FB,21,49,14,FC,8C,3F), + wwH16(52,9F,27,F7,0F,F0,87,3B,47,1B,5B,55,46,95,E7,86), + wwH16(C3,BD,5E,A8,8B,1D,67,F3,96,FF,0E,D8,88,B8,2C,76), + wwH16(39,1A,15,72,02,8C,7A,BC,D3,E2,FF,09,84,13,5F,F5), + wwH16(7F,8B,5E,56,7B,E3,D0,6E,57,94,C7,2B,31,06,1B,CE), + wwH16(BE,D2,EA,08,2B,1A,76,E2,C9,7A,45,91,86,7D,C0,69), + wwH16(70,C1,D2,69,65,B4,72,F4,90,48,05,FE,92,30,6F,28), + wwH16(82,70,7E,75,50,C5,CC,79,2A,46,27,BB,28,C7,22,96), + wwH16(BD,84,94,DF,CC,B8,ED,9A,26,39,77,B8,AE,12,1D,A3), + wwH16(E5,2A,FE,82,55,99,1E,ED,2F,CE,24,3C,0F,DE,82,F1), + wwH16(AC,30,A3,6A,B5,96,1C,A8,E4,6E,32,6F,CA,92,C3,B8), + wwH16(5F,17,31,FD,26,65,DC,1B,F0,5B,CB,DF,CD,57,2B,FE), + wwH16(3C,7F,3A,60,84,AE,0E,C4,F1,F7,9E,FC,39,9F,FE,74), + wwH16(2D,70,5C,52,CA,FD,8B,97,C5,3E,7A,CB,A6,C3,37,DA), + wwH16(2D,18,BC,39,AF,32,A3,54,CA,5B,00,2F,78,29,50,55), + wwH16(B2,EA,E0,BF,56,6F,92,20,E8,51,7A,B4,D3,89,0C,C4), + wwH16(19,29,61,3D,4D,05,C5,89,CC,8D,1E,B1,74,51,65,39), + wwH16(E9,7B,B6,F1,F3,B3,11,DA,A5,B7,F8,A3,52,30,5D,72), + wwH16(1E,59,00,47,76,19,26,79,CD,31,F2,B7,D5,81,64,DA), + wwH16(BE,7A,7C,2F,55,C3,03,3A,6F,89,22,F6,19,DB,73,14), + wwH16(7C,94,21,C4,46,33,99,D3,E6,53,94,A5,14,D5,1C,F7), + wwH16(AC,79,D7,A7,98,17,DE,6E,F0,C1,D2,2A,5E,4D,9B,4A), + wwH16(64,9A,BF,13,08,91,6D,DA,17,53,F8,E8,1E,14,E3,E4), + wwH16(96,90,71,5E,C0,79,6E,76,E4,D7,8B,47,34,3C,89,DF), + wwH16(38,D6,3B,04,9D,18,19,B8,71,7E,49,E0,A4,E6,37,43), + wwH16(98,8C,A0,96,53,BC,4F,CD,D8,8F,0C,57,66,C2,2F,14), + wwH16(01,B7,A3,2A,19,CF,F6,B6,5D,61,52,72,A4,AC,52,8F), + wwH16(68,52,15,00,BE,5B,E1,71,03,F9,7F,42,92,84,07,2A), + wwH16(61,CB,EC,BD,E3,97,59,5B,44,07,65,FD,7A,C1,15,46), + wwH16(FE,7E,99,56,E9,43,A2,73,A4,B2,60,57,AD,5D,44,FA), + wwH16(8D,0E,8D,3E,A0,97,F9,95,DA,0E,1C,1A,58,7B,A5,23), + wwH16(7F,0D,CB,EB,FE,E5,A2,94,DC,4E,03,43,95,8B,83,CE), + wwH16(73,F0,5E,34,24,97,FC,46,6E,01,79,81,F8,57,0C,9C), + wwH16(FE,45,2A,4C,85,F9,A3,18,CB,33,E6,9B,59,FA,27,3B), + wwH16(AF,2F,4F,92,75,16,94,85,C4,94,8D,20,5D,D2,F1,54), + wwH16(D7,61,22,9A,D1,EF,77,D2,68,23,FE,29,79,00,E8,BA), + wwH16(D8,E3,E3,DA,74,AA,6A,CE,5D,0E,09,38,18,FE,50,0D), + wwH16(57,B1,A2,AF,81,5D,48,74,34,BC,42,23,BB,A9,BD,1B), + wwH16(6C,F9,1F,2B,00,B3,6B,20,11,DB,6E,80,A7,5A,99,92), + wwH16(35,C7,55,A0,AD,31,BD,40,A1,66,DE,B5,02,F1,C1,C6), + wwH16(10,51,6A,CB,2E,F2,8F,BD,FB,6A,73,2A,9D,07,0E,2E), + wwH16(1C,A2,F7,2C,04,E4,B1,8F,0F,F8,0F,11,25,9F,50,82), + wwH16(52,BC,FA,F4,ED,D0,38,0B,07,C7,75,A2,48,2A,7B,D8), + wwH16(FC,80,8F,E6,57,EA,35,06,A3,77,02,9B,F4,03,F9,39), + wwH16(5C,40,B6,CC,81,36,8F,F3,C6,21,D3,7E,B9,05,C5,75), + wwH16(29,8D,F8,C4,4C,9D,63,C8,6A,4D,A4,84,15,C7,2A,69), + wwH16(59,E5,40,E9,23,0D,5D,B5,7A,71,90,DD,CD,3E,F4,ED), + wwH16(AF,06,55,91,72,96,97,C5,06,78,0D,21,79,82,A8,7E), + wwH16(71,48,FB,08,89,F5,16,22,B8,63,86,93,D3,E8,73,B2), + wwH16(7F,67,45,AD,F9,CC,6E,65,82,A2,8D,54,AD,62,1D,55), + wwH16(FE,D5,B1,21,22,79,7F,8D,E4,2D,4F,FF,CE,DE,B5,D0), + wwH16(5C,3D,C7,DE,7E,04,33,80,73,53,67,0C,6F,68,0F,83), + wwH16(35,C3,4E,89,73,87,A2,ED,F3,2F,BE,D9,43,E5,03,E4), + wwH16(77,C0,23,72,22,F5,1E,F4,F2,3C,B5,F0,A6,7F,03,DE), + wwH16(11,00,D8,64,47,3E,68,4E,72,D2,E7,CB,7E,7F,8F,56), + wwH16(89,E3,45,5D,5C,8A,B3,E9,39,BB,32,6C,32,57,37,AD), + wwH16(56,BB,F9,CE,5B,F3,01,7B,EE,F5,3B,98,A2,E7,E1,6A), + wwH16(05,18,5B,41,9D,37,FD,F3,6D,CB,CE,77,5A,69,1E,A6), + wwH16(81,D9,19,33,A9,3F,13,75,7A,8B,FD,6F,1A,38,84,13), + wwH16(C6,37,D6,8A,E6,5E,84,4D,11,F2,B9,52,53,71,F0,D6), + wwH16(88,CC,43,98,AB,AF,A3,33,2A,36,F1,D5,0D,63,14,A5), + wwH16(C8,BF,FD,80,21,73,40,BB,CF,0E,26,C3,D5,F2,A1,2C), + wwH16(F1,77,39,63,46,71,32,59,73,A1,70,D4,46,69,18,72), + wwH16(DE,0A,71,47,F6,03,67,D7,CE,E4,1E,3A,F4,C4,76,61), + wwH16(92,AD,83,FF,0A,BF,AB,0E,CB,DE,6F,BF,E4,F4,B7,F6), + wwH16(9E,22,00,C4,2C,F8,0B,35,81,2A,63,88,C5,D9,F6,10), + wwH16(9C,0D,AC,FC,B1,4E,72,58,1A,36,AD,92,E7,BA,26,00), + wwH16(6C,5F,9E,F5,14,BE,5E,84,58,3B,2D,6C,75,A8,E8,CA), + wwH16(9E,E5,33,03,32,99,2C,B4,99,6B,87,3B,B4,53,07,81), + wwH16(F8,44,BE,E0,DD,BC,81,5A,93,E3,64,59,30,6E,D6,F2), + wwH16(5F,8D,34,46,20,D0,49,7F,DA,83,69,1D,5F,3C,F3,B0), + wwH16(68,26,BE,33,3A,77,84,9F,4D,D8,40,9B,67,61,A8,4B), + wwH16(9E,C4,D6,FB,33,6E,E7,7A,D6,DF,9F,26,84,84,53,22), + wwH16(01,90,ED,18,1A,D9,16,A1,02,AF,AB,CC,AB,48,CD,94), + wwH16(7B,D0,8F,E5,B1,A2,B4,15,A9,07,97,FE,27,47,92,84), + wwH16(CD,EC,2D,64,A9,1B,D5,4A,56,0C,00,8E,76,E8,C2,6E), + wwH16(51,5F,FD,C8,AB,99,C8,EC,48,E6,89,BE,9F,77,BC,01), + wwH16(9E,E4,77,EE,38,83,AF,A6,26,0C,1C,AF,EF,DF,C9,35), + wwH16(0D,CF,7D,7A,01,0F,7A,CA,F2,36,55,76,F9,FE,BB,93), + wwH16(73,88,9A,DF,C3,92,9B,CD,FF,A0,86,F4,AC,46,8E,F4), + wwH16(6A,56,BB,0B,0B,EA,C5,73,5E,98,7A,FF,5E,D7,0A,DB), + wwH16(D3,61,35,6F,13,F0,68,F2,63,77,BF,3A,21,13,04,D9), + wwH16(49,45,8C,93,F6,7B,31,3A,40,16,58,1C,ED,99,C8,1D), + wwH16(4B,45,5A,23,5A,9B,F1,AE,97,E1,EB,A0,A4,6D,E5,5F), + wwH16(88,E5,AF,C0,72,E5,59,D0,34,A7,00,D6,A8,AF,20,CF), + wwH16(88,D4,DC,1C,67,04,B7,32,39,9D,C1,FF,7F,1A,91,B7), + wwH16(9E,3E,84,26,13,5B,ED,2B,46,D8,C0,39,36,9F,37,CE), + wwH16(82,A3,93,38,46,64,33,D8,64,AB,5E,94,F0,8D,82,72), + wwH16(DB,9D,93,A6,2D,A1,21,5A,8C,3A,FF,61,24,51,69,8B), + wwH16(43,3C,A4,23,67,90,73,AE,68,B4,1F,31,F8,9B,56,A6), + wwH16(2B,37,C6,DC,70,D8,22,1E,6C,C6,C2,A3,95,DD,8D,43), + wwH16(69,77,42,0B,3E,46,23,FC,E2,1E,0B,AF,24,58,DE,1B), + wwH16(B8,EE,AA,D6,C7,4F,90,3E,AE,2E,5B,C0,4C,C3,AC,74), + wwH16(46,F0,D1,EB,32,1E,73,75,56,D1,3A,1F,B6,CC,D8,68), + wwH16(2A,23,4C,55,72,CB,E3,CC,03,19,56,08,99,C0,3C,63), + wwH16(D1,2C,F9,0A,DE,ED,5E,C7,91,7B,95,AB,BC,AF,43,2B), + wwH16(91,75,DE,65,31,D1,3A,7B,24,19,D5,FF,00,F2,FC,B2), + wwH16(AF,64,2F,1E,6A,CF,BC,F5,4B,69,42,A4,41,C9,C5,3C), + wwH16(98,95,CF,E7,26,72,FB,97,80,3A,9E,07,69,31,AB,76), + wwH16(FD,84,8C,6D,E7,F2,C5,97,D2,F0,C5,FA,B7,9B,EC,77), + wwH16(BF,F8,49,7B,77,14,96,4C,F5,12,A1,EE,61,A3,7A,CF), + wwH16(F2,C1,71,BF,15,AE,91,31,02,26,F9,57,AC,66,06,16), + wwH16(B7,A7,02,A1,69,8B,D4,00,71,3D,B0,5D,4B,5F,9E,0A), + wwH16(5D,BB,B2,0B,30,0C,3B,AA,69,9B,67,AA,6F,0A,CE,A7), + wwH16(CA,1F,75,65,B7,1F,BF,36,0B,AC,D5,DE,78,4C,B2,21), + wwH16(9F,64,E5,8D,D4,2E,23,5A,26,0F,56,9E,E7,B2,E6,4D), + wwH16(BA,1F,2B,91,2E,32,A9,53,69,B5,93,80,2F,24,7E,70), + wwH16(5C,52,4E,DD,79,A1,7D,D3,0B,74,CA,E1,DC,2F,CB,42), + wwH16(47,81,02,0E,59,B3,D7,49,7D,B1,97,D5,C2,D3,CA,22), + wwH16(58,F3,AC,8C,CF,6A,0C,F3,81,EC,4C,85,BE,E4,AE,A0), + wwH16(D9,6A,8C,69,7D,4A,2F,48,CE,96,95,38,DD,5D,C3,DB), + wwH16(35,CA,8B,25,6D,91,2F,08,10,E2,AD,B3,71,D7,62,45), + wwH16(F8,89,E6,EE,48,CA,3E,03,C7,84,2D,39,D3,C4,F1,C4), + wwH16(45,E2,8F,2B,1D,3E,38,82,E4,4C,50,A4,E1,55,05,C3), + wwH16(AB,15,82,58,0C,AC,06,1A,0C,F0,61,90,29,27,8C,48), + wwH16(6B,E3,12,E1,C3,E8,FD,D2,F4,D6,7B,65,90,8C,7D,4A), + wwH16(23,41,C0,58,27,AE,AF,5F,F4,F0,75,72,07,32,2D,04), + wwH16(F3,C9,45,8D,DA,4F,47,FD,6B,BB,17,97,E7,58,82,52), + wwH16(A9,32,C4,86,BB,1F,C7,DB,1A,85,48,FB,9C,B9,A6,B9), + wwH16(85,5A,19,7A,2E,7C,7F,9E,83,19,22,38,FE,94,D2,FB), + wwH16(BF,99,14,51,2E,EB,3D,00,62,61,08,41,93,3F,0E,E5), + wwH16(5A,D5,81,73,81,00,B3,1D,B6,66,21,BD,08,65,EA,52), + wwH16(84,D2,40,0F,ED,07,48,94,8F,E2,2D,9D,19,77,7F,B1), + wwH16(D1,BB,CC,4A,4B,17,EF,CD,2E,93,C8,54,4A,CE,C2,B3), + wwH16(B4,6D,72,E1,B5,6D,4C,2B,4C,D4,F2,76,36,D5,5E,23), + wwH16(15,29,F3,12,CD,21,07,D7,E1,D0,7B,B1,0D,0B,7E,A3), + wwH16(0B,CF,0C,48,89,CF,88,F3,68,C7,FD,51,DC,04,53,C3), + wwH16(16,E1,4C,EC,B3,A5,6A,F1,90,23,EB,97,DB,76,2B,17), + wwH16(0B,BB,6E,28,DA,10,BF,93,B7,D5,A0,DD,AD,BC,17,6D), + wwH16(72,87,05,B4,D9,A1,68,49,00,4E,09,02,09,E3,50,E9), + wwH16(83,3E,D5,F7,BC,79,48,9D,F7,57,34,39,F0,CE,55,32), + wwH16(8D,C9,C5,4F,7E,65,31,92,91,EE,C1,BF,BF,11,A4,46), + wwH16(6E,F6,80,F4,CE,42,D7,45,07,45,DB,AE,9B,C5,71,39), + wwH16(B9,58,F7,7F,E3,A6,AF,05,77,F7,D9,88,4B,46,93,47), + wwH16(0D,4A,DA,16,D6,2C,40,3E,28,31,39,71,69,4C,8E,50), + wwH16(7E,F2,7C,75,BE,8F,DD,F7,8E,BE,79,16,AA,FF,AC,64), + wwH16(15,78,DC,64,4C,46,05,CE,7E,60,A6,DB,CB,AB,A6,4C), + wwH16(3A,CE,6C,E6,D1,E1,0B,3A,7A,57,5F,6F,4D,85,49,DA), + wwH16(0A,04,BA,66,9F,C7,4F,26,EB,E8,B0,EC,FB,E3,27,C9), + wwH16(9D,B0,4F,38,00,97,A5,91,7F,4C,BC,8E,77,B9,C7,C5), + wwH16(1B,A3,8D,FB,BA,EB,4B,54,49,45,4B,CE,3D,D7,AF,F7), + wwH16(BB,34,38,17,E6,F6,DA,6D,C2,23,3F,8D,0C,A1,15,6C), + wwH16(04,43,10,45,A8,0B,84,C2,BC,58,70,19,12,70,4C,AC), + wwH16(0E,B8,57,38,89,93,A1,C2,77,A3,E5,59,D6,C0,E2,07), + wwH16(7F,60,AF,84,B2,17,4B,96,49,B1,2E,08,9F,45,F9,04), + wwH16(E8,29,2B,8E,8D,93,54,48,96,32,49,FB,BA,01,FA,9B), + wwH16(E4,59,88,2B,1E,45,C4,BC,D2,0D,F4,21,2D,03,E6,4E), + wwH16(05,6B,9E,47,35,06,A4,79,BC,88,8E,2A,FB,E3,64,FB), + wwH16(FD,43,AC,E0,DB,FA,64,68,C2,0D,3B,30,5E,9A,05,EB), + wwH16(2E,84,12,F1,AD,52,2C,23,D3,80,F6,E2,EB,1B,42,D4), + wwH16(33,AE,E1,FC,8D,57,8A,73,04,88,60,8B,2C,F8,BD,82), + wwH16(57,ED,9B,FD,12,C2,51,40,5B,7B,99,80,32,C9,39,65), + wwH16(48,41,E9,B4,87,11,8F,D4,D1,7B,B9,20,5B,D6,46,3A), + wwH16(C4,63,CF,FC,87,99,CE,2E,3C,E8,7B,78,D1,EF,7F,42), + wwH16(93,87,63,23,1F,4B,08,D8,28,76,11,2F,28,92,F0,3B), + wwH16(C5,0B,E2,B3,9F,2F,64,34,12,EA,8F,48,8A,98,EF,AF), + wwH16(06,07,3F,70,CC,72,73,B2,E1,3F,0D,85,1A,8A,3A,11), + wwH16(69,68,69,DB,29,10,FD,5A,37,5A,48,E3,B2,73,43,8D), + wwH16(E6,03,94,53,2B,85,76,C8,68,12,4C,3A,5F,79,90,79), + wwH16(D1,D8,FB,97,1E,02,09,CD,9A,2A,34,61,29,A6,4A,64), + wwH16(E3,FB,9C,93,67,3E,68,80,12,46,7E,0F,FE,35,0A,A5), + wwH16(CB,8E,9E,75,4B,42,45,59,A0,FB,19,55,8F,21,95,F5), + wwH16(B9,0C,C5,ED,06,68,50,DA,41,BB,E7,D1,21,E1,75,F9), + wwH16(5C,91,83,EE,AE,8C,04,BD,CB,CB,D0,41,27,F5,CD,E1), + wwH16(72,BD,B7,05,68,72,4F,90,E3,69,86,65,72,6F,4F,63), + wwH16(41,F7,01,A7,08,BA,6C,C1,61,C8,2F,46,CF,AC,08,C0), + wwH16(15,EF,83,1F,E6,FB,67,3B,DB,7A,3C,25,B0,45,44,2B), + wwH16(6F,7A,37,F4,22,82,AA,3A,C7,90,77,FD,DA,0C,2C,86), + wwH16(C7,99,9C,AD,EA,76,E5,8D,16,6D,7C,25,EA,94,F9,14), + wwH16(23,F1,5B,02,A8,4F,88,D0,D7,60,54,0D,50,3B,52,84), + wwH16(BD,DF,99,83,6A,19,36,FB,76,FE,23,A9,A5,36,AD,78), + wwH16(A7,5A,BA,9A,68,A6,35,2D,AD,AF,E1,00,4C,8A,87,4F), + wwH16(7A,3A,15,46,83,A9,F3,8A,32,4F,74,6D,FC,C9,B3,5C), + wwH16(5B,BB,93,3C,AD,F1,0F,07,B2,32,73,A9,33,6D,EE,9D), + wwH16(56,7E,3E,74,04,6E,07,8C,DC,51,ED,BB,D4,C2,16,64), + wwH16(13,5D,61,CD,33,EE,6C,A0,CA,9F,5C,4F,00,39,49,84), + wwH16(5D,4C,0D,2E,95,F2,29,FD,03,3B,91,AF,0D,D5,5F,AC), + wwH16(6F,BB,B4,61,EB,BC,87,89,E8,AA,6E,3D,4E,14,45,C9), + wwH16(B2,3B,5D,31,05,15,7D,D2,0A,D2,A5,F7,2E,6D,B3,74), + wwH16(BB,BC,19,D0,09,87,19,21,FE,28,40,9C,1C,03,BA,12), + wwH16(39,60,B2,32,91,5D,21,B5,F5,89,F8,CF,66,BF,C0,D8), + wwH16(3E,BC,3F,55,06,BA,2F,9E,5D,B7,2C,DA,76,B4,E9,58), + wwH16(17,02,85,04,EC,CF,02,52,DB,85,1D,8E,28,75,CA,6D), + wwH16(B8,6C,44,55,15,CE,A4,5F,44,76,31,B0,22,58,A9,4E), + wwH16(79,96,8D,C0,8B,D2,98,DB,CC,7C,C4,18,2B,D1,A5,FB), + wwH16(FC,51,3F,83,EC,FF,DA,AA,0F,60,72,B1,BB,EA,56,01), + wwH16(65,BA,34,16,F1,C9,5B,71,22,F5,B2,2B,67,78,BB,19), + wwH16(8D,16,20,50,F9,48,82,08,71,B5,07,2E,47,A6,D6,9D), + wwH16(F2,43,76,6F,5A,B9,BF,F7,4E,CB,EB,84,C7,2A,93,C8), + wwH16(85,6A,4E,DA,90,3D,69,EB,5C,F1,13,85,FC,17,12,D6), + wwH16(F5,94,69,3D,95,F9,BE,9A,FD,E6,72,E5,7B,0C,27,74), + wwH16(92,86,03,DD,F8,A3,AF,13,FF,34,D0,F2,EF,0C,79,D1), + wwH16(08,0E,60,94,B8,73,45,5F,B5,2F,BB,A7,2F,AF,DD,F3), + wwH16(C3,3D,AC,11,CB,3F,AA,5B,90,3D,79,FF,6F,B6,06,D5), + wwH16(F1,DF,44,BC,7C,D6,E7,D7,28,31,8A,83,42,EC,7A,64), + wwH16(3B,B4,87,06,53,19,EF,FC,83,11,88,5E,E5,6B,EA,E3), + wwH16(7F,A9,6B,5E,54,D4,A3,FB,1A,18,C4,28,C5,4E,4C,DD), + wwH16(21,96,1B,4E,30,B8,7A,B8,BF,F1,A5,71,03,EB,E8,8E), + wwH16(FC,9D,4C,5F,26,DD,25,AB,62,BB,12,74,69,88,D4,E4), + wwH16(19,23,8B,71,25,C1,D1,0B,3D,0A,36,5A,B1,C3,2E,CA), + wwH16(D2,9A,51,40,08,E9,CB,C3,50,3D,10,5C,87,42,F0,1F), + wwH16(60,3F,D4,8F,32,61,F8,1E,F9,21,05,2E,40,24,DE,B0), + wwH16(FF,03,DB,BE,09,47,C3,E1,0B,A2,83,09,46,28,98,1B), + wwH16(94,16,BC,D6,76,BC,D6,70,32,44,C1,2E,5F,C7,B3,8F), + wwH16(C9,3C,C1,F9,F2,6A,F8,3A,C0,D9,C9,C2,B5,D0,AB,7C), + wwH16(1C,94,1C,E2,FF,27,E2,E4,F7,2C,52,30,C0,49,95,6C), + wwH16(BF,9E,9B,66,91,47,47,24,2A,E1,69,65,ED,01,1A,C8), + wwH16(A2,42,E2,F6,3B,38,C6,D9,80,AF,1B,BC,EB,E9,BE,9D), + wwH16(F9,D6,14,E6,93,BE,50,AB,45,24,89,94,4C,6A,42,DE), + wwH16(E4,32,33,E9,E8,36,E1,B1,77,B4,E1,F6,53,AA,55,04), + wwH16(00,DE,9D,86,DB,89,48,D8,85,2E,3A,06,5E,F3,AC,D4), + wwH16(6C,AD,A7,6F,80,BC,8C,65,73,0D,7D,D6,7F,1D,48,D0), + wwH16(C5,D9,1D,90,A8,96,8E,27,0B,5A,9D,4B,E8,6F,44,BB), + wwH16(3A,EA,AD,9E,16,45,8C,27,6C,19,45,6D,05,FA,C0,6D), + wwH16(4E,DF,B0,E2,49,28,08,B3,6E,B2,4C,72,46,A1,C6,7B), + wwH16(D6,62,59,AE,6A,6B,F4,90,AD,66,1F,91,F3,A4,01,07), + wwH16(54,7B,EE,28,7A,28,8B,F7,08,A4,19,4A,07,66,F9,F2), + wwH16(0E,BF,4E,83,06,BA,1F,1F,F5,F3,89,30,A3,D9,A2,CA), + wwH16(FE,3D,4E,06,2B,DD,6C,3E,18,8C,FE,4E,03,07,27,6B), + wwH16(16,E6,0A,E5,02,35,E6,E6,76,71,13,08,31,8D,01,B8), + wwH16(CE,F2,4E,44,03,BB,DF,53,50,61,F5,38,6C,18,F6,B7), + wwH16(BA,B1,FA,82,B5,62,76,01,A6,0E,7D,96,9B,80,53,01), + wwH16(24,4A,96,29,D2,A9,08,7F,0A,13,D5,2B,A4,55,D4,F7), + wwH16(E9,06,77,04,1E,5B,A1,5D,4D,7B,49,CA,57,55,79,4D), + wwH16(07,4D,2C,F3,B5,F4,49,F2,E7,01,CA,4A,BB,A7,87,13), + wwH16(19,56,93,B6,42,A3,B1,A7,DD,F3,16,4E,DF,DF,28,AF), + wwH16(65,C9,2B,18,4E,08,37,7D,F8,2A,B1,E7,7A,31,B2,EC), + wwH16(07,47,F7,9B,23,C7,F9,93,C8,59,ED,50,B7,9D,1E,85), + wwH16(F3,0D,36,16,9F,C9,EE,E1,AA,63,E2,8D,65,03,70,10), + wwH16(3F,C5,89,1D,62,A6,81,49,28,F5,C2,74,DC,37,6C,FA), + wwH16(75,6A,CF,1E,C0,9B,C3,53,70,22,5D,1D,2B,8D,A6,7C), + wwH16(4A,76,F9,CB,1D,54,93,12,21,FA,0A,DA,2A,D6,61,F0), + wwH16(2B,35,60,51,CE,D9,C4,81,18,02,0C,96,CD,32,D3,44), + wwH16(A5,55,05,B8,D5,77,A2,DE,F3,21,5F,16,F2,78,EB,D7), + wwH16(01,FB,34,48,14,06,7A,CE,E8,57,13,60,8C,1A,47,D0), + wwH16(80,E8,AB,2C,79,9C,FD,4E,E8,9C,4E,38,6D,03,CF,F6), + wwH16(29,1C,83,DE,BB,61,73,7D,40,91,F9,93,D4,86,DD,0D), + wwH16(14,A9,52,2A,D7,1E,3D,13,66,5F,5D,BE,27,4B,59,51), + wwH16(8B,DB,A3,94,37,3D,7E,88,87,C8,48,AE,DF,E6,BE,4E), + wwH16(46,48,85,08,EA,3C,4C,15,E0,FF,92,B7,CE,A4,57,AC), + wwH16(5C,41,3C,9A,53,BE,70,06,42,D9,6E,45,ED,EE,BE,8F), + wwH16(7B,2E,D0,01,9C,47,F8,72,7F,DC,84,B0,41,D2,EC,1A), + wwH16(59,0F,AE,58,91,34,53,8C,56,16,54,DA,4A,70,21,1C), + wwH16(F0,23,BD,B0,75,4A,B6,FA,97,80,59,32,F1,38,06,D4), + wwH16(62,43,33,C0,0E,A4,ED,C7,EE,A3,BC,CB,12,AB,80,27), + wwH16(B6,44,11,DE,66,B5,5A,26,5E,7B,A8,C5,93,32,61,3D), + wwH16(A9,03,43,74,66,CF,00,55,66,6C,24,84,C2,B5,F5,31), + wwH16(D2,2B,EB,07,35,75,89,A6,13,BC,91,02,D6,9B,CD,84), + wwH16(7F,C3,30,A3,8F,35,B2,BC,77,CB,01,18,A0,38,63,2D), + wwH16(14,23,B7,09,A2,7C,8B,A7,93,B1,78,91,65,5B,1B,E3), + wwH16(E2,C4,6F,99,D3,BB,38,90,AE,EB,AD,CC,D5,71,4B,DF), + wwH16(41,27,AA,74,AB,2F,CB,AB,7E,E9,26,5D,1D,93,54,4B), + wwH16(A5,CC,DE,6D,D6,0C,99,AA,06,9D,92,FF,BC,71,46,0A), + wwH16(12,9E,DD,6E,37,16,A3,DB,7C,90,F5,83,BB,97,51,21), + wwH16(64,AE,A6,71,89,31,AD,E9,19,91,69,03,28,72,FF,47), + wwH16(E7,0E,AC,02,FE,13,8A,68,63,15,4B,A0,B0,7E,32,03), + wwH16(60,F1,D7,C0,20,77,3A,17,05,37,80,68,64,7C,49,0C), + wwH16(0A,E2,84,D6,A5,56,FD,DE,A8,92,02,C5,CC,08,B7,94), + wwH16(BE,8C,EF,9D,FF,D1,69,31,9B,8A,A2,BB,56,67,52,E5), + wwH16(FF,F2,D3,AF,C2,8A,D5,1B,54,2A,67,8D,AD,4B,A0,2C), + wwH16(41,F7,29,97,56,91,BD,FE,C2,4F,C5,87,67,3D,6C,B2), + wwH16(BE,DA,91,E5,EC,35,1A,38,11,5D,83,A4,4E,E1,AC,24), + wwH16(61,3E,4D,30,B2,25,2E,93,D1,95,28,1F,C6,C8,BF,64), + wwH16(DB,7F,D6,43,1E,07,EF,3F,7E,B9,64,95,47,4B,71,E6), + wwH16(E0,1C,3E,E3,0F,E4,77,E5,0C,1F,43,BA,D0,B8,C2,74), + wwH16(88,29,DD,3C,89,D6,E0,0E,5D,D1,D1,C3,EC,22,3B,6A), + wwH16(28,11,01,07,17,69,3F,3D,8F,05,E1,C6,6D,17,5B,A9), + wwH16(8D,ED,1E,75,AD,86,1E,CF,20,0B,37,7D,86,0F,4E,76), + wwH16(77,FF,42,16,EE,47,8A,D2,C7,3F,B3,D9,24,A5,99,A8), + wwH16(59,B4,BB,09,A2,CF,E3,E6,CE,05,D8,AF,F8,F5,E1,E0), + wwH16(E0,06,61,4F,68,FA,D5,DF,D7,48,FB,F9,5D,DC,29,AF), + wwH16(5A,6F,EC,62,5E,10,FF,75,56,D3,52,DC,01,F4,A8,7D), + wwH16(C2,51,AA,9B,93,C5,84,9B,F5,13,17,53,1C,0B,D4,4A), + wwH16(C7,A0,C4,87,B6,FC,37,48,38,21,13,32,D3,A9,80,F2), + wwH16(29,37,25,C1,4B,F0,BD,89,07,D9,77,E2,15,D0,1A,35), + wwH16(1F,F0,8F,7D,94,28,58,9C,F5,9D,F9,A6,79,D2,FA,1E), + wwH16(8B,C7,57,71,0F,00,4A,A6,49,36,46,2E,70,40,D7,30), + wwH16(5F,0A,B3,12,D6,B2,83,DB,BB,74,C5,0F,D2,19,9F,66), + wwH16(A3,FE,7D,DB,D9,CD,66,11,E9,B7,5F,6A,36,0A,E4,06), + wwH16(35,61,83,34,C2,37,FA,1B,31,FE,9A,0D,08,06,EF,D6), + wwH16(22,FF,E7,35,F2,C1,AF,93,B2,81,B2,13,25,5F,6D,03), + wwH16(38,83,EA,F0,13,50,12,43,EA,43,F5,FE,37,5A,66,F4), + wwH16(C8,63,4D,B3,63,A9,CA,85,A8,7E,86,88,EC,6A,2C,67), + wwH16(8B,3D,2E,A0,E3,F5,20,A8,CE,7E,E9,71,A8,23,11,C6), + wwH16(56,D3,98,C9,84,BF,91,4E,7D,C1,74,DE,5F,BD,58,8D), + wwH16(2E,21,F7,34,8A,93,DA,D6,26,7D,BA,EE,20,AF,A6,E2), + wwH16(4B,58,79,60,83,D4,F1,1E,38,B5,BB,34,72,6C,61,1A), + wwH16(B3,41,44,C0,F9,6A,24,EA,25,D5,F4,FC,20,5F,B5,AB), + wwH16(E3,F2,41,A0,1E,BE,F8,CA,BA,39,05,92,4F,55,C3,CE), + wwH16(A0,5B,C9,1F,73,0B,12,6E,F2,C5,4C,83,20,35,1D,62), + wwH16(3F,14,F6,26,76,DA,37,99,4E,C3,EA,91,F3,77,19,A5), + wwH16(12,41,96,13,23,A4,2F,CD,EF,E2,E9,4B,05,B9,66,AE), + wwH16(45,DC,D8,C4,B9,79,03,23,A2,2F,50,F1,C7,7C,7C,8C), + wwH16(6E,0B,1F,43,3B,29,2C,C8,A8,1A,E3,63,AF,B6,E6,6E), + wwH16(DB,4D,55,D9,4B,CD,17,AC,4A,11,81,28,DF,30,9D,23), + wwH16(1B,B4,73,F9,25,42,86,16,80,B7,22,6C,49,42,03,14), + wwH16(AE,CF,17,B5,63,9C,DB,47,D1,10,94,28,1A,AC,96,3B), + wwH16(B0,67,B1,E0,22,77,8F,BF,A8,3C,C2,91,50,40,8E,2F), + wwH16(36,64,0C,D2,AD,20,7A,42,E6,68,A7,BC,BD,DF,5B,AD), + wwH16(57,1F,36,5A,04,BA,D1,F8,DE,D4,9A,61,A7,BA,D1,26), + wwH16(AB,08,ED,58,F4,5E,BC,90,29,A6,49,8D,1F,3A,F1,47), + wwH16(F0,2F,44,F9,21,88,FA,E5,34,D9,37,23,19,82,CA,95), + wwH16(73,5A,22,FE,58,5C,CC,C1,0F,F1,19,F2,76,C6,DB,92), + wwH16(33,24,A3,17,D0,8B,7D,FD,CA,59,52,5E,7C,77,71,EF), + wwH16(26,A5,0E,E2,6C,FC,05,6F,A8,E6,E6,E4,6B,2B,04,35), + wwH16(13,90,4E,9C,8C,FF,64,E7,3D,46,33,C3,E2,F2,62,EE), + wwH16(76,59,E4,7A,23,E3,66,4F,5B,4F,C4,2F,60,FC,32,4D), + wwH16(F3,2E,CF,88,25,77,77,1F,49,2D,B2,5C,E8,80,8C,4D), + wwH16(2F,F0,31,8D,A8,F6,D6,50,19,65,8C,32,9E,14,12,AC), + wwH16(FB,CB,B2,B9,13,28,29,4B,CB,74,8C,52,B4,9B,C2,48), + wwH16(8D,2A,C3,65,B5,BD,48,31,49,01,90,3A,E5,2F,D9,84), + wwH16(59,DA,75,11,F8,60,D4,2F,DD,D9,C8,19,EA,A3,5E,69), + wwH16(DD,10,17,94,E7,CB,CC,3A,B9,C2,66,BB,6A,AF,10,82), + wwH16(A9,7F,23,B0,B1,C9,A1,52,51,B6,FE,5E,18,37,1E,C6), + wwH16(FF,78,0D,32,7E,00,C1,BA,55,C9,53,F7,F3,64,0D,AB), + wwH16(8F,D7,BF,4F,5C,CE,E0,71,4B,4B,B5,B8,E1,A4,52,B9), + wwH16(6E,9F,BB,51,EC,C0,E9,FB,F3,EF,A5,2D,50,B0,F7,4F), + wwH16(C5,1B,10,1B,18,DB,F7,67,D0,B2,C5,70,B5,24,6C,5F), + wwH16(09,52,37,48,93,88,04,7C,00,D0,BC,D7,1C,B2,1F,F2), + wwH16(E3,3C,EB,53,A2,A1,43,A3,D8,C1,C7,C6,66,65,45,83), + wwH16(B9,33,92,48,F4,AA,26,C3,DE,9F,7C,9E,20,28,D5,B7), + wwH16(27,49,8E,7D,BF,53,16,84,5C,14,A2,9E,12,A6,9F,04), + wwH16(D7,BE,B4,96,E7,A3,1C,E9,8A,67,EB,0B,4B,58,DF,BB), + wwH16(A0,81,63,E3,35,1A,37,58,15,F6,35,CE,F6,C5,09,35), + wwH16(31,17,80,6F,D0,4B,65,A5,62,57,E4,F6,68,72,09,18), + wwH16(D3,D2,62,89,DE,9A,D5,D8,9B,4F,8E,BB,49,E0,AC,0B), + wwH16(6E,6F,B8,15,51,37,3B,6A,42,A4,43,52,05,F2,AE,E5), + wwH16(B4,F4,61,87,CA,AF,6B,DF,5D,A5,FF,A6,72,49,13,0B), + wwH16(D9,9D,0B,EB,70,35,73,AA,FD,75,F6,38,CA,27,D7,BC), + wwH16(FF,60,5F,3E,2D,D4,E9,1D,46,A2,7F,05,9F,DD,75,7A), + wwH16(58,96,0C,08,6F,20,74,46,9A,ED,94,57,2B,CE,83,6E), + wwH16(81,CF,FB,D3,3F,CA,D8,62,23,31,0F,CE,42,51,BA,80), + wwH16(42,62,47,5C,0C,E3,70,AD,BD,CF,D2,8A,A5,F8,B2,4A), + wwH16(31,36,2C,F2,F3,2B,B3,B3,D2,A5,BB,D2,14,CD,9D,96), + wwH16(77,17,F0,B6,47,41,A4,A1,2E,12,63,91,11,FB,49,BC), + wwH16(31,7E,6C,DC,B2,B3,CC,C5,16,5F,FA,6D,F1,08,C6,AF), + wwH16(2E,9F,5B,24,C6,02,05,BE,8E,B2,0E,D9,97,BD,70,DC), + wwH16(3D,EF,62,DF,94,66,D9,CE,7F,A4,03,E3,E5,F2,ED,A0), + wwH16(19,4A,7D,6A,F0,AA,B9,25,9B,F0,0C,B7,D1,CD,EE,F4), + wwH16(65,86,E5,72,7B,41,5E,44,02,7A,A3,4B,9F,5A,8C,9C), + wwH16(49,1A,C1,6F,6E,EB,D0,8B,EC,B5,FE,F7,A2,B0,A6,A0), + wwH16(C0,01,34,E4,30,75,E6,6C,2F,1B,C5,D3,92,87,99,81), + wwH16(70,C6,5A,BB,96,29,45,41,C0,EB,0D,90,B9,E8,09,1D), + wwH16(CD,D3,98,78,EC,99,45,24,5B,BF,18,A0,0B,CD,0E,24), + wwH16(3F,63,6D,CB,D0,C8,0E,2A,95,D1,74,73,FD,98,AB,4A), + wwH16(EB,DA,79,76,6B,7A,36,EA,C5,52,A7,02,1C,55,2D,0F), + wwH16(61,8E,17,6F,3C,0F,5B,8B,CC,EC,D2,FD,70,5E,23,5E), + wwH16(87,62,0E,CB,71,D3,C5,1F,1E,31,65,4C,0A,C8,1F,1F), + wwH16(C8,2C,30,F3,28,30,F0,8B,4C,43,C4,4F,63,C6,C7,F4), + wwH16(1D,92,C6,06,91,B5,30,10,EE,41,88,A7,1A,DF,84,A7), + wwH16(63,8E,1A,C0,C6,26,5A,A9,D7,1C,96,2B,3B,E2,52,54), + wwH16(E2,A9,5F,AF,78,D2,8B,20,6F,F0,59,AE,72,C0,0C,35), + wwH16(DE,5C,43,DE,D5,DB,56,4B,4B,10,23,38,72,E5,04,26), + wwH16(68,B0,C5,AC,53,F2,F5,8F,FB,06,4A,32,13,7C,71,51), + wwH16(7E,73,0D,A4,F4,72,67,B8,68,44,D1,BE,EC,9F,A9,73), + wwH16(DB,5A,8D,29,12,39,F3,F0,01,EA,FE,4D,6E,9D,EA,B6), + wwH16(54,94,AE,F9,D3,8C,04,96,C6,64,D5,4D,B8,C7,E9,EA), + wwH16(90,65,70,57,A7,04,7A,A7,1A,3E,40,16,B8,0B,FE,E1), + wwH16(A6,83,16,0F,5B,61,A9,75,D3,00,78,DC,52,9A,DD,1C), + wwH16(81,85,D8,A9,01,34,4D,9C,81,00,F8,DD,4C,4A,58,28), + wwH16(82,47,24,5F,EB,87,5F,D8,81,95,AF,34,A9,99,AE,68), + wwH16(F0,32,E4,E4,9F,AF,0A,B8,0C,37,63,48,BC,20,72,81), + wwH16(3C,1B,DD,F8,04,AE,C2,0D,0C,FA,D8,1B,7D,75,00,AB), + wwH16(14,44,2D,75,95,34,CC,66,53,9B,17,B8,ED,82,38,D5), + wwH16(08,54,06,B3,95,D5,26,6B,92,2C,74,F8,82,7E,67,2B), + wwH16(10,38,80,32,0C,C6,FB,A6,88,26,1A,96,B8,9C,FE,B2), + wwH16(1D,FC,4F,04,03,72,8D,0A,EE,18,19,2B,46,9A,81,D9), + wwH16(62,EA,EF,DA,C0,79,06,F2,3B,D8,67,B9,D2,F9,2C,59), + wwH16(A5,61,B8,ED,FE,E7,0C,28,85,02,B2,37,33,F4,65,85), + wwH16(5B,0A,68,91,09,2B,5F,80,99,E5,57,04,F9,A5,49,43), + wwH16(96,23,7E,27,4A,D6,38,BD,71,32,5A,5E,8B,01,82,DC), + wwH16(36,52,FB,70,62,38,60,F9,A0,AE,0E,C5,03,54,44,11), + wwH16(9B,E6,C3,2A,4C,61,52,C1,1B,63,A1,A2,80,CE,02,8F), + wwH16(AD,DD,9B,01,64,12,06,A2,7A,FD,38,C3,9E,66,2D,A9), + wwH16(8A,92,22,62,35,4D,E1,E3,22,81,A7,75,B2,A8,78,8F), + wwH16(15,BB,5B,3A,54,0D,E4,25,DA,C5,02,28,69,65,0B,CF), + wwH16(FC,E0,AE,F3,C0,CA,9A,02,FD,5F,46,4E,03,7B,87,DC), + wwH16(01,D9,DC,5C,2F,BE,20,60,39,A1,D0,24,DE,43,6E,19), + wwH16(BC,6E,A0,81,86,D5,47,75,7B,CD,13,28,2E,EE,92,90), + wwH16(F8,B5,F6,40,F3,DD,83,35,C4,5C,C9,96,05,BC,AE,56), + wwH16(08,05,A5,CC,F3,23,9D,97,F4,89,FD,AD,EC,3F,C9,3D), + wwH16(2A,BF,F9,DF,6F,6E,43,F8,5A,E5,5D,7C,A9,6E,7B,6C), + wwH16(0F,3E,B1,01,2D,B1,95,2C,5D,95,94,CE,B1,10,8B,D0), + wwH16(38,51,99,FF,EC,72,C1,BE,5D,76,DE,E4,59,DB,9C,BD), + wwH16(52,A1,69,4A,71,07,56,D9,23,42,0D,22,DD,63,3E,1D), + wwH16(20,78,01,86,71,AB,1A,96,FF,74,FB,A3,45,D5,4E,50), + wwH16(1A,80,76,1A,5B,5A,49,0E,5E,3D,EF,7B,44,EB,FA,2B), + wwH16(BA,D0,94,D4,A5,85,88,85,1B,20,BE,11,BA,6F,A9,AF), + wwH16(0B,7D,B7,A6,EC,40,55,F2,72,35,53,57,BF,D0,9C,84), + wwH16(32,5F,4E,7F,B4,37,6F,7C,4B,60,AF,B9,21,84,0A,B2), + wwH16(CE,E7,56,F7,B5,A6,97,55,23,99,04,60,E2,3A,82,89), + wwH16(13,5B,51,0E,8F,66,D4,26,EC,A4,DD,D9,B3,F5,4C,C6), + wwH16(69,62,A0,E9,9C,C5,81,AE,6E,12,EA,6B,60,05,AB,94), + wwH16(48,C1,7E,92,8A,E7,B3,0C,F6,70,09,E1,EE,DC,F5,38), + wwH16(A3,28,61,59,34,EB,B7,ED,EE,DC,91,7C,97,87,27,6A), + wwH16(EB,3A,7C,5C,72,C7,C2,08,21,9D,5B,2A,1E,E9,4E,EC), + wwH16(EE,17,55,C8,03,29,58,BF,4A,50,0C,23,AE,00,3E,96), + wwH16(AC,88,0B,AF,EE,C8,3A,94,98,EF,0E,30,CA,DA,6D,D7), + wwH16(12,4B,71,0C,DD,26,50,35,F1,E0,F8,F7,56,59,79,C6), + wwH16(F0,60,D9,D1,7D,CA,5E,A6,A5,DF,8F,2A,2B,98,02,5F), + wwH16(00,8E,E5,21,56,DA,26,2E,65,D8,AD,0D,B1,0A,B2,A0), + wwH16(EF,62,EB,02,AB,EB,13,28,F8,6C,E8,B0,B3,DF,0C,07), + wwH16(77,50,07,9E,9F,20,2B,E3,C7,F1,54,00,71,1F,70,AC), + wwH16(E5,49,6D,B2,D6,41,3A,E2,E9,52,D3,BA,91,8B,18,34), + wwH16(8C,82,59,06,4D,4C,96,94,29,41,80,FB,84,02,20,D7), + wwH16(4D,A6,FD,BA,AF,B8,87,1D,5A,8A,F4,AE,4A,61,2E,66), + wwH16(38,2F,FE,D8,2B,C7,D4,6E,DF,66,53,A2,05,77,7F,87), + wwH16(6D,37,7D,4F,5E,C9,A1,CF,C9,90,32,B4,CA,30,7B,1A), + wwH16(B7,A1,D5,28,9F,67,18,84,B7,DC,60,0D,57,71,6D,68), + wwH16(01,6B,94,67,6B,9B,0C,2E,45,53,C3,4F,68,1D,37,9D), + wwH16(93,C4,E3,DC,B5,47,4D,35,2B,1E,FA,57,4A,29,35,DA), + wwH16(7B,6E,E6,13,2A,5C,94,6C,54,C9,34,5B,59,39,59,6C), + wwH16(03,57,64,EA,5E,B3,12,44,18,16,E9,6B,15,57,40,C1), + wwH16(5C,8D,31,21,99,24,FB,7D,3A,6F,A4,9D,21,F5,1E,E5), + wwH16(F6,53,C5,9D,E7,67,43,0B,FE,BC,06,98,95,BF,3C,A4), + wwH16(F1,61,59,79,89,4D,96,D9,F3,62,5D,27,5E,5F,C5,D4), + wwH16(D1,9B,F8,A8,79,80,0C,0A,C2,86,32,3E,67,A4,D1,A2), + wwH16(43,B5,26,90,9F,BC,2B,3B,E5,D3,08,5E,3F,9B,FC,E6), + wwH16(A4,FE,9B,52,6C,5D,26,D5,B4,5C,93,56,62,10,AA,56), + wwH16(B1,F8,1A,64,21,C5,1A,04,1D,51,D5,7A,73,9E,E5,4B), + wwH16(6C,22,6B,43,5A,12,42,EE,B2,43,20,CC,ED,F6,1A,3A), + wwH16(ED,AD,93,33,D6,4F,B1,13,A9,2F,59,29,90,7A,B5,30), + wwH16(B1,F6,9D,F3,32,58,CF,BD,1F,A9,B1,1F,54,E3,3C,0B), + wwH16(0A,DF,61,3B,97,D3,FB,38,53,14,65,6E,A0,2A,41,5F), + wwH16(4B,8C,A7,F5,7A,AE,7E,DD,72,12,6B,FC,A6,D5,8C,9A), + wwH16(38,78,09,04,E5,5B,97,32,F1,8B,6C,0A,3C,DB,FB,B6), + wwH16(0F,AB,FE,0E,E0,95,A4,75,64,64,AA,A9,67,E0,1F,E6), + wwH16(3D,54,BE,30,76,66,E8,1E,96,D7,9A,C2,07,6F,BE,D6), + wwH16(1C,82,AE,76,4A,D5,10,6C,8A,9E,6A,0F,31,1B,CE,FA), + wwH16(20,17,A4,1B,C7,5E,77,25,04,69,B3,38,B1,5A,AC,CE), + wwH16(9D,1F,F6,B8,4A,BB,62,10,34,4A,62,91,B6,75,63,C4), + wwH16(56,10,AC,0D,7D,8E,99,88,3E,0A,15,6C,64,14,00,AF), + wwH16(D7,40,F8,C6,D5,95,26,4D,C5,B7,7E,D5,4E,57,BF,2B), + wwH16(9B,03,69,FB,09,29,F0,07,66,59,C5,31,04,E9,53,CB), + wwH16(26,0B,95,FE,28,D4,30,ED,B9,F9,3A,99,5D,2B,40,8B), + wwH16(92,FD,B5,F5,5B,64,E9,74,E4,C4,33,E1,06,82,51,D2), + wwH16(45,94,35,6E,6F,74,4D,35,31,1E,9E,95,91,D4,1D,DE), + wwH16(07,76,F2,49,12,F0,D5,5D,30,C0,ED,A6,3D,98,FA,CE), + wwH16(5D,F6,7A,6D,E3,28,F4,06,6C,0E,FF,28,00,A9,6C,82), + wwH16(8C,DC,98,AB,D7,97,AA,5F,28,21,B2,67,0C,E2,21,59), + wwH16(3B,02,55,2E,F3,78,E0,45,5A,0F,76,B6,32,FC,20,6E), + wwH16(B4,D0,CE,49,3C,FF,CD,5A,34,8D,7A,5D,A7,C9,3D,E1), + wwH16(9E,5B,B8,FE,CC,E2,78,AE,5B,CC,F9,88,31,FF,58,C4), + wwH16(73,26,83,8F,C3,E7,89,FD,EE,EF,06,67,4A,0A,D3,D7), + wwH16(2D,06,AA,F2,32,1F,5F,03,80,72,D8,2E,5D,C5,2F,BC), + wwH16(0F,01,C6,5E,F5,71,9F,AA,EA,60,86,53,6E,91,FF,F7), + wwH16(A7,C4,6F,A4,76,4C,09,82,29,63,40,DB,1E,E9,48,EC), + wwH16(83,08,06,AE,66,78,AC,DE,7C,C6,61,1B,98,E1,EE,A5), + wwH16(43,F6,F1,67,04,DE,DE,77,DC,10,EB,0F,64,99,5B,ED), + wwH16(FE,FE,B2,93,EF,28,0D,06,1B,83,BF,69,83,DF,67,51), + wwH16(7C,63,F8,98,CD,D3,CC,FF,35,91,62,E6,06,67,0C,81), + wwH16(66,7B,56,6B,0A,FD,F4,C2,75,9F,4F,14,4E,FA,57,02), + wwH16(5C,C6,99,D3,3D,A3,BB,93,67,24,1D,04,82,3A,BA,D1), + wwH16(24,B5,DB,7B,0B,1D,49,1A,DE,55,94,45,FF,ED,7D,F2), + wwH16(2D,83,F6,0C,1A,3E,49,C3,50,12,6C,9E,65,2E,CB,BD), + wwH16(71,DC,E8,A8,4E,0F,A7,6A,A0,75,91,35,78,E4,C3,B2), + wwH16(58,69,30,17,16,18,F9,6E,18,29,59,68,93,1C,FE,4B), + wwH16(61,88,7A,83,7C,87,EA,87,D6,EE,BF,50,04,BC,55,E3), + wwH16(6E,B2,CC,7D,92,83,B0,80,27,6A,72,72,74,6A,9E,9C), + wwH16(5D,F2,68,F0,D0,49,B0,04,44,CC,E1,56,93,BA,05,D3), + wwH16(D9,D7,CA,36,6B,5C,C7,8B,DE,80,31,45,38,FB,46,16), + wwH16(11,C2,88,BA,75,71,03,B1,38,1E,2F,6F,6A,40,30,BD), + wwH16(8D,99,F4,5B,98,35,D3,72,3F,95,EA,34,50,D9,A8,88), + wwH16(4C,7C,77,78,36,23,68,3D,06,AE,65,F4,62,23,47,FF), + wwH16(E2,03,D3,4A,54,18,3C,0A,52,61,33,ED,19,9F,57,80), + wwH16(E8,30,58,C5,EA,06,0A,58,4F,8C,8A,B8,50,42,53,C5), + wwH16(C0,5B,2A,51,0E,37,F3,D5,2D,C4,73,E9,D2,0E,FE,1C), + wwH16(4F,35,17,B6,66,B0,6D,E4,70,28,13,7E,1A,BD,06,31), + wwH16(42,4E,28,C1,29,74,59,8A,94,A2,68,9F,FA,95,E9,84), + wwH16(C4,B3,EA,25,8F,BB,C6,E1,37,51,14,81,B5,5C,DB,A4), + wwH16(AD,AE,91,65,77,EC,50,97,ED,0F,E6,3A,B6,D8,00,66), + wwH16(F0,7B,CC,9D,E6,0F,80,A7,02,98,57,68,D0,07,2C,FF), + wwH16(B6,5D,05,06,23,EA,BF,2D,76,D0,36,3D,06,A4,BD,D7), + wwH16(46,46,68,C6,A4,6A,6F,F1,8C,51,A2,EE,EE,C8,9B,29), + wwH16(5D,31,CE,00,4A,B0,B5,4A,3F,2A,21,E7,CC,85,B9,C9), + wwH16(8D,32,97,BC,78,25,F0,21,61,86,08,44,EB,10,9C,DC), + wwH16(9A,38,46,DC,62,B8,52,81,C3,BF,95,2B,3A,41,DA,06), + wwH16(59,3D,93,F9,DC,3C,57,9B,3C,CE,15,B5,68,5A,4F,9F), + wwH16(2D,45,7E,3D,58,83,18,04,39,4B,0E,CD,3B,14,25,2D), + wwH16(2A,FD,E1,6D,EE,36,8F,9F,0F,7D,E7,87,C6,05,2D,A6), + wwH16(36,69,DF,58,8A,5B,79,EC,65,01,DA,34,77,D6,78,11), + wwH16(17,1A,07,C6,F8,A9,F7,8E,5E,33,F0,84,A4,21,37,AD), + wwH16(C2,CA,00,08,D6,89,5F,4B,B1,3D,89,A1,A7,75,7B,48), + wwH16(9A,B2,B6,2B,B5,AF,18,78,AD,CF,98,C1,96,B4,8D,40), + wwH16(75,A1,B1,C9,23,B4,C7,33,BA,F4,56,3C,B0,14,11,13), + wwH16(D2,D5,05,78,95,55,25,9B,28,78,ED,EC,C4,10,57,B8), + wwH16(AA,26,B4,0C,3A,82,1D,AE,0C,B0,07,99,42,A7,C7,0E), + wwH16(6D,38,5A,64,C0,28,73,14,A0,C1,3D,2E,BA,C2,FF,07), + wwH16(F3,20,DB,C5,18,B8,DC,A4,1B,F8,B7,CC,8A,33,E7,3B), + wwH16(D7,05,2E,43,64,3B,A7,12,6C,EF,E3,D9,94,62,36,CD), + wwH16(A6,37,9A,8D,07,93,58,93,58,DE,FA,4F,B6,80,DB,A8), + wwH16(53,8D,0B,7A,9C,92,14,FE,D3,A4,A6,DE,A0,52,8C,A7), + wwH16(39,C7,9B,C0,DB,8C,B4,52,33,38,85,E4,3B,AC,7A,F0), + wwH16(86,C1,30,37,C3,D9,84,3C,F8,A8,D7,8D,67,7D,82,41), + wwH16(E9,5C,9F,75,07,AA,1B,29,A6,59,25,D5,9F,26,E8,86), + wwH16(F7,77,46,69,D6,DA,14,B9,85,74,0E,6B,DB,7A,04,42), + wwH16(BA,7A,97,1F,C3,89,01,6B,B1,31,D0,91,72,63,25,CF), + wwH16(AA,70,3E,94,F5,57,C1,78,77,07,4D,0C,04,AE,99,3A), + wwH16(B2,C5,A1,BE,73,C3,52,AA,D1,FD,24,12,7C,32,FD,9E), + wwH16(C7,55,34,CE,CF,63,80,52,05,27,73,52,08,FD,DF,EA), + wwH16(8F,0A,D0,C1,C3,FA,F6,42,D5,79,6A,FA,83,3E,AA,50), + wwH16(55,05,A8,A9,DE,C8,CA,7A,84,40,A9,5C,77,B0,87,F7), + wwH16(E2,62,20,7B,2F,8F,48,E0,13,51,C3,72,F0,76,61,0A), + wwH16(03,19,77,51,B4,EF,83,14,D0,27,0E,19,5E,3F,24,09), + wwH16(97,84,90,D2,48,F9,59,7B,BC,A2,94,C6,82,BC,6D,9D), + wwH16(FF,CA,5A,78,61,F9,A7,F8,06,F5,8B,F1,73,AA,DF,C6), + wwH16(A7,08,57,4D,33,8B,E6,10,42,BC,C1,F6,0F,83,EF,88), + wwH16(F0,44,C4,DA,05,D9,50,41,19,18,86,E0,58,A4,A6,F2), + wwH16(F1,68,6F,B1,1F,4F,BD,75,2E,47,2D,95,03,09,25,A9), + wwH16(14,69,EA,7D,EE,E1,C2,52,46,9A,4E,EB,08,0E,09,1A), + wwH16(5F,10,E8,3E,EC,D7,BA,AE,FA,EC,78,13,04,4D,69,D4), + wwH16(82,6A,C3,9E,BF,35,5D,5C,F9,DD,EF,C8,99,C9,66,FD), + wwH16(50,2B,C5,E4,D6,8E,6B,89,1A,A6,79,5D,B3,3D,00,36), + wwH16(12,14,99,92,36,9D,68,D1,B0,33,C3,2E,A2,A1,73,80), + wwH16(44,7F,A5,82,66,13,33,68,FD,A6,B0,F8,03,BC,26,CF), + wwH16(71,6E,F6,5E,95,22,07,BE,EA,1B,52,4D,3D,E5,FF,D7), + wwH16(81,A9,65,2F,CB,2F,8B,B4,BD,A4,03,CA,9E,A9,C0,9F), + wwH16(18,6A,0C,65,A5,D8,04,CB,59,46,3A,EE,6C,48,EB,1B), + wwH16(20,81,32,5D,EA,30,6F,9C,21,E3,93,0F,48,D5,AF,BC), + wwH16(71,16,40,5B,1B,00,B4,7C,3F,95,4F,D9,A9,98,30,4D), + wwH16(7B,F8,D5,F4,EB,3C,A9,A7,19,86,8E,49,90,8B,01,A2), + wwH16(9D,A9,A6,82,AF,05,52,49,84,FE,4D,99,20,E4,1E,66), + wwH16(05,F6,AC,71,A6,7A,AC,46,3A,EB,D0,0B,5E,1B,34,DC), + wwH16(07,E4,F3,68,88,54,3D,8B,A2,26,D8,49,E1,4A,A1,DC), + wwH16(50,C6,EC,1A,91,61,94,6E,81,70,F9,AC,E5,96,7B,D0), + wwH16(0D,60,E7,95,32,7D,89,2C,9C,5F,6D,A4,41,77,D5,F7), + wwH16(61,EE,CC,DA,9B,CB,0A,6D,59,DB,30,09,B5,FA,04,43), + wwH16(85,A0,E2,D2,2C,9E,BA,DC,60,2F,F5,80,0F,05,33,BC), + wwH16(93,13,83,B9,54,8C,17,C4,6B,37,0E,B5,57,A1,DF,84), + wwH16(40,09,39,A5,9C,E2,0D,63,5C,16,2A,BF,2A,B4,BF,C0), + wwH16(65,30,31,8C,82,1D,75,7B,41,13,72,70,53,D2,EE,1E), + wwH16(44,FB,64,46,95,E1,9D,26,2B,C4,50,8C,33,39,D6,29), + wwH16(7C,E2,06,4B,B9,D2,45,8B,5E,CF,08,83,56,69,40,25), + wwH16(A2,1D,AB,22,0A,7E,70,CA,4E,6F,FB,84,A6,D3,2D,DE), + wwH16(70,67,90,50,EB,58,D8,8E,2C,86,4C,AB,E2,EC,E0,4E), + wwH16(13,87,8C,CF,AA,09,04,A9,24,E1,B4,D3,8F,68,58,ED), + wwH16(7B,9E,64,32,E6,73,68,2E,34,67,C5,6D,2D,A0,97,3B), + wwH16(DA,B1,14,87,22,57,68,CD,41,10,E3,36,2C,3B,13,21), + wwH16(D6,F2,6F,C9,2C,5B,0E,36,D0,5B,EC,99,12,FB,45,0C), + wwH16(DF,B8,73,6D,4B,DE,AF,14,80,C8,C6,23,2D,39,93,60), + wwH16(CE,3B,77,1F,19,71,7A,67,4F,07,D1,3F,6C,B9,22,8B), + wwH16(3F,A4,76,08,E2,29,A7,91,4B,3F,2B,57,5E,0C,DB,31), + wwH16(D8,33,EA,59,97,1F,37,45,9A,2A,5E,BD,76,72,93,6A), + wwH16(CF,09,CC,80,72,9E,EE,CD,3C,AF,71,3A,15,74,DB,54), + wwH16(B6,B1,F2,B5,70,55,CD,02,8A,B3,B4,77,14,8F,E0,3E), + wwH16(25,63,B2,8D,E1,10,8A,04,52,59,FC,D1,56,78,71,46), + wwH16(2A,36,7F,0B,7F,DD,FA,8E,EC,AF,A4,A8,D5,2C,9A,65), + wwH16(E5,DE,32,BA,35,AB,9A,FF,3F,48,C2,FA,A5,F1,0E,06), + wwH16(F7,9D,EA,D8,DC,1A,4D,76,85,A2,11,34,37,10,31,D9), + wwH16(A7,92,1B,A4,87,0B,18,16,22,8D,F6,33,E3,21,0C,24), + wwH16(30,BA,24,8E,10,9D,9C,DF,9C,F5,2A,13,16,23,3E,31), + wwH16(B7,AD,A7,27,D5,C5,A4,E7,F2,54,3B,33,F4,C3,53,90), + wwH16(79,3B,60,A7,12,A1,54,EF,88,F1,22,4F,E3,A4,EC,25), + wwH16(60,F7,9B,07,F8,5C,D4,A2,31,4C,C6,B8,DA,26,A9,13), + wwH16(D9,2D,3C,63,82,0C,80,7C,2C,76,DE,BA,BE,76,D7,CF), + wwH16(1A,C9,76,91,C0,3B,7B,E5,8E,9C,FF,4F,85,7B,9B,DA), + wwH16(47,41,02,03,E1,ED,1E,2C,2A,40,0A,6E,6C,54,08,90), + wwH16(B5,34,A6,3C,2E,AC,D9,3B,26,DE,62,E7,5A,4F,65,1F), + wwH16(66,8B,C2,A5,2B,5B,64,9C,98,7A,6E,1B,2F,0A,4B,AF), + wwH16(CF,D9,C3,B2,6D,96,83,B2,DA,58,DF,BF,73,AC,8C,53), + wwH16(85,E0,D4,0D,BF,2B,32,90,0B,7C,C6,CC,39,0B,D7,5B), + wwH16(BC,0F,FB,6C,68,0D,19,DA,BA,49,0A,42,92,AF,DF,A9), + wwH16(F8,07,80,BB,D4,FC,74,D1,C4,8B,7C,EB,5E,39,42,F0), + wwH16(C1,62,63,BC,26,08,EB,5A,86,F9,39,EE,34,F7,CF,78), + wwH16(D8,B9,80,80,7C,1A,29,00,64,29,66,02,E6,F4,D3,E5), + wwH16(8A,CC,E0,22,83,AB,9D,2D,12,B2,04,A5,80,E1,95,42), + wwH16(AC,02,C2,95,DE,A5,E8,60,05,39,55,62,B0,35,C4,90), + wwH16(EC,F5,A7,A6,66,D5,9B,3D,3D,43,8A,8D,7F,0E,68,A8), + wwH16(20,32,A5,8C,B4,49,EF,F2,4A,3D,A3,15,AA,4B,E4,01), + wwH16(D4,80,30,F3,91,FC,CE,DC,76,8E,FE,5D,97,F3,9C,98), + wwH16(D2,A9,24,19,FC,2D,6B,0D,40,7B,99,E9,21,1D,82,A8), + wwH16(40,2C,68,60,4F,1F,23,59,BB,16,18,74,C2,48,5B,2A), + wwH16(84,92,49,03,84,94,A6,70,CE,F8,5B,DA,8D,55,87,95), + wwH16(F9,5A,E7,3E,AB,85,1E,30,11,18,D5,6B,3B,30,FA,30), + wwH16(88,7C,EC,71,79,02,E5,D6,2D,36,DC,A7,D2,CF,F0,67), + wwH16(51,B1,D9,82,35,B1,B0,BC,69,DB,EF,3E,04,6F,FC,7E), + wwH16(A9,13,AC,1B,13,4D,95,9B,1A,3B,E4,8B,80,FE,88,6B), + wwH16(56,A8,7E,9F,AB,9B,B5,D2,8D,B7,93,22,2C,D6,97,0F), + wwH16(31,58,A4,9C,95,B0,BC,1B,45,6F,EE,0C,3F,23,80,5F), + wwH16(A0,88,BC,D5,10,F3,79,9A,38,3E,24,C1,81,17,0C,7E), + wwH16(9F,4C,35,5A,A2,E5,CE,E9,05,70,2A,A8,08,1E,06,BE), + wwH16(F6,AB,72,B8,57,69,38,23,A8,5A,3F,28,6F,FC,BE,80), + wwH16(82,81,AF,FB,A7,ED,7A,4B,DB,BB,38,B4,8E,69,1C,F2), + wwH16(A9,D7,B0,6D,56,47,27,88,B0,3F,41,28,98,82,89,EC), + wwH16(9A,C6,93,E1,C5,7C,1D,96,85,2A,85,A3,D8,35,5C,D1), + wwH16(ED,7C,33,6C,EF,81,5F,ED,B3,AE,BA,8E,5C,C8,BE,21), + wwH16(F8,E1,69,EE,67,45,DD,AE,B2,0A,8C,46,D8,50,65,E6), + wwH16(A6,0A,87,F9,61,84,7E,B6,3E,B4,B3,E1,2B,1D,6F,95), + wwH16(70,0E,EC,82,5B,EC,C1,A0,3C,A3,39,D6,16,DD,7D,48), + wwH16(CE,E0,3A,BC,97,71,A8,57,0C,F2,1E,E4,60,3C,8B,87), + wwH16(98,FC,36,FC,2D,1C,CA,69,5C,AB,06,8A,6A,20,B9,69), + wwH16(CE,59,D4,E9,7D,24,DB,2F,33,42,1D,01,8E,19,F6,BB), + wwH16(9B,24,69,DD,50,44,AA,51,AA,0A,2B,C4,26,16,59,E0), + wwH16(BC,73,46,E9,4C,67,DC,5E,E1,54,F9,49,CA,29,5C,45), + wwH16(0B,14,2C,7B,64,CC,77,23,07,05,3C,09,21,22,54,8C), + wwH16(6A,EE,53,66,5F,AE,3D,68,62,CD,F1,D9,B0,77,EE,F9), + wwH16(E8,70,D1,00,14,10,31,02,7A,4E,6C,51,E5,BE,93,B7), + wwH16(80,61,F4,6E,79,CF,04,D8,1D,D9,AE,C3,01,C7,71,50), + wwH16(EC,8F,7B,CB,18,3A,84,9A,56,53,C0,6D,94,F5,27,30), + wwH16(57,40,97,1D,93,49,39,C1,89,F8,78,9A,A0,68,26,33), + wwH16(BA,B7,D0,CF,0B,DC,3B,63,16,CA,47,4A,33,A0,A6,6B), + wwH16(AD,FB,70,7A,98,2C,E4,E7,3C,76,23,AF,FE,FC,3E,BE), + wwH16(DE,1A,D2,01,D6,8F,8C,2A,CA,38,3C,2E,1F,27,9E,9F), + wwH16(4B,0C,E6,EA,46,E7,EE,DD,1D,A4,1B,DB,A4,59,3F,9C), + wwH16(C9,74,6D,3E,41,4C,EE,8D,E7,9A,DF,9B,36,D7,D3,DC), + wwH16(00,9A,A4,BC,FC,00,62,09,79,37,D7,C1,6A,6A,E7,C2), + wwH16(B6,FC,9B,B9,E3,DD,C9,F6,A3,21,AF,0D,FE,D9,5A,85), + wwH16(5A,83,3B,BF,DE,A1,91,1D,07,F9,83,E6,FC,46,7F,4E), + wwH16(7C,C9,EB,3A,CC,AF,5A,CB,A7,2E,D1,56,AC,93,8D,EE), + wwH16(A4,3A,A8,3E,D5,58,DF,08,2D,AA,83,5F,0B,11,65,E3), + wwH16(13,05,46,E6,7C,0C,86,F8,B7,F2,54,AF,03,E4,48,A8), + wwH16(BB,1D,EB,CF,0E,A0,D1,56,43,1A,B5,E2,3C,B6,DD,91), + wwH16(8A,E8,C2,F1,63,F2,29,91,DD,14,FC,93,91,BE,FF,DD), + wwH16(F2,D7,EA,98,8B,6F,86,60,0E,FD,D1,FF,F0,AB,DD,5A), + wwH16(BE,C4,A2,6F,CB,89,96,0D,F6,DB,6B,3C,3F,2A,31,33), + wwH16(BC,F5,6E,4F,4C,E8,1B,35,46,4D,6F,2A,4A,25,E0,D8), + wwH16(54,41,CE,28,F2,C5,89,85,56,54,3C,AB,7C,44,3A,15), + wwH16(D1,C8,C3,21,96,D4,94,A0,BE,A2,FC,D1,5F,87,48,6E), + wwH16(CC,55,BE,68,5F,43,EA,99,8D,C6,AB,13,0D,61,AD,0D), + wwH16(AF,A5,B6,2F,A4,09,12,9C,9A,C8,73,45,D3,EE,21,A7), + wwH16(2A,06,F4,D6,DC,AB,6F,0F,8C,88,22,09,96,A8,6E,1A), + wwH16(BB,02,8C,AA,FE,23,CB,B7,AD,35,7A,E1,90,78,6D,ED), + wwH16(BD,93,07,56,1C,FC,9F,81,9F,AB,BA,A6,F5,2B,31,C2), + wwH16(DF,C8,F8,20,8F,BE,4C,C7,43,51,EF,35,57,E5,86,99), + wwH16(0C,9E,59,82,F5,6B,7D,C8,C0,8C,44,8A,BB,94,F4,F7), + wwH16(C3,4D,FF,F4,AC,A1,15,79,CD,D1,B9,23,AA,AA,7B,F1), + wwH16(C0,82,1D,A5,28,F9,6E,CA,C8,09,25,B1,47,6E,F6,51), + wwH16(15,8C,1A,AC,15,06,12,D9,55,92,D8,33,A8,9E,5F,14), + wwH16(D7,FA,CF,A4,29,F5,67,81,25,5C,F0,D1,55,76,77,51), + wwH16(93,E6,A6,4A,4C,2F,3F,67,0A,2E,FA,C7,9D,94,F3,19), + wwH16(03,A8,28,5D,30,81,F2,98,7E,8E,F1,05,08,40,50,DB), + wwH16(BA,33,4B,F0,7D,9B,53,BC,0B,C5,21,D6,8A,4A,91,F9), + wwH16(2B,62,18,BA,20,BD,22,23,F4,90,5F,1C,8C,30,20,56), + wwH16(39,BC,06,24,F7,7C,E3,2A,6B,CE,04,E0,AA,59,21,22), + wwH16(F0,E8,55,09,D4,AB,F3,26,62,2D,13,EF,DE,4E,0B,DD), + wwH16(BD,0D,DD,2C,79,C1,35,25,D3,3C,BD,66,37,70,0F,E5), + wwH16(D2,30,D1,DD,1F,FA,89,25,76,03,0A,ED,02,42,83,D2), + wwH16(39,C8,84,15,4F,90,51,84,F7,1E,1F,12,4B,86,EF,85), + wwH16(2F,1F,38,3E,F9,B4,17,FE,46,8C,65,64,D6,48,C6,9C), + wwH16(B9,8E,96,F7,65,B7,23,39,72,43,99,F5,9F,7F,2E,5E), + wwH16(C2,00,AA,1D,37,18,BD,48,84,EC,3C,AE,91,48,E8,F1), + wwH16(94,93,63,7E,22,C0,6F,0F,32,B5,72,BB,D0,1D,EF,37), + wwH16(30,43,C2,29,B1,F1,E9,29,12,3B,8F,33,7D,63,7F,CD), + wwH16(1B,A1,9D,89,5C,D7,FA,2C,74,BD,C9,CD,C5,2B,36,D4), + wwH16(E2,27,0E,98,C7,C0,DC,E2,E4,3C,2D,37,84,2D,C0,3C), + wwH16(40,06,66,56,1D,2C,B5,4A,30,1F,D0,72,F6,39,A9,46), + wwH16(CF,19,E4,36,62,E2,2A,2F,14,B4,F5,C8,E1,CD,C3,13), + wwH16(78,1E,6F,FE,BA,CA,EE,5A,4F,A0,D7,43,B3,91,F2,91), + wwH16(BB,E1,D9,61,62,3D,2E,B7,24,F3,08,6C,37,50,DF,46), + wwH16(43,6E,A5,9F,0F,DE,F0,C0,FB,52,D3,5D,DE,CD,98,66), + wwH16(40,FD,D3,4B,0F,B4,97,AD,63,2F,A6,0A,55,2C,F2,AB), + wwH16(75,12,3E,00,D8,8B,C0,1D,20,26,1D,2C,2F,4E,6A,DF), + wwH16(6B,76,6F,30,99,CA,D5,92,55,74,CF,3F,B6,F9,2B,05), + wwH16(8D,DF,4B,59,E9,49,E7,E3,C6,39,AD,7F,B3,56,68,E0), + wwH16(D3,13,02,4D,CA,CB,D8,C5,69,0F,72,BA,E9,5E,D8,E7), + wwH16(45,53,60,19,25,8F,1A,6B,9B,3E,00,CB,EB,85,D7,0A), + wwH16(91,58,3F,78,67,AD,1C,7F,43,2C,43,FC,63,D3,D6,B6), + wwH16(C1,00,95,E2,69,EC,54,D5,C5,7F,70,10,D9,97,65,B9), + wwH16(CF,EB,FF,DD,B6,02,D3,1D,5B,FB,A4,1F,EC,1A,13,DC), + wwH16(94,CE,E5,06,47,01,C4,06,B9,A4,F2,8A,6C,F5,5E,D7), + wwH16(7C,FF,72,19,99,9A,77,F7,2A,9D,DF,66,7F,A5,FE,32), + wwH16(46,ED,EC,A7,74,94,87,73,25,E0,5B,73,80,C8,F8,83), + wwH16(32,A2,BC,C8,57,D2,9A,2B,35,F2,9B,5A,06,A7,6C,5B), + wwH16(70,F4,29,D5,49,A1,53,94,6D,09,EB,75,2A,80,59,1F), + wwH16(82,4D,BA,EB,FE,44,DC,20,41,31,6E,25,3C,35,00,06), + wwH16(23,12,30,A0,10,70,EB,0E,7E,E4,66,85,85,9F,3D,0A), + wwH16(54,45,F4,19,76,A5,A4,B9,7E,22,1C,FB,74,37,79,6B), + wwH16(D8,45,33,0C,12,07,2B,23,4D,C7,B9,68,32,BD,F1,43), + wwH16(06,FE,5A,B6,AC,0E,63,80,C9,9E,3C,0C,57,19,26,77), + wwH16(4C,3E,3B,A8,32,47,21,1E,75,76,5C,5B,3B,27,39,03), + wwH16(22,AB,35,FB,21,B8,F4,5E,5C,DA,67,A6,F9,F4,D3,52), + wwH16(FF,F1,E0,75,EA,ED,8A,50,9E,18,FC,7C,9D,B9,CD,0B), + wwH16(76,38,68,55,A8,D7,3C,B8,BE,FE,CB,07,FD,A4,DF,C5), + wwH16(63,1B,1C,4A,8E,A8,94,A8,3B,28,CB,12,08,12,EE,BE), + wwH16(8B,BA,23,23,28,CF,93,53,9B,08,DF,5B,E2,2F,D6,19), + wwH16(01,F7,D0,CD,97,D6,B5,B3,B8,4C,9A,AE,7F,E3,4B,64), + wwH16(B4,4B,C8,6A,CD,0A,BC,AC,A6,C2,AA,36,F7,3A,C0,0F), + wwH16(E7,49,A4,0A,0B,F2,27,05,B9,19,8B,05,F7,38,01,BD), + wwH16(B1,1B,DC,AB,C7,26,97,DD,E9,D2,D1,B5,6C,0B,64,B4), + wwH16(76,01,74,9A,DC,4D,FA,11,89,1F,A9,54,72,57,2A,28), + wwH16(06,46,D2,9F,51,53,68,28,2C,17,89,0A,9B,DC,84,A0), + wwH16(12,88,13,AE,A6,61,06,FE,B0,44,0F,9F,98,91,32,C4), + wwH16(81,A4,D7,BC,4B,D9,28,90,B1,B0,2E,40,9E,77,0F,F4), + wwH16(83,9E,EE,E6,1F,61,B3,8B,8C,A2,CC,B4,92,16,21,75), + wwH16(AD,A6,83,7A,0E,AA,4E,7A,27,A5,5A,68,E8,6B,15,9F), + wwH16(9C,7A,51,8F,AA,FF,15,B2,A3,97,28,AF,25,1C,A5,DD), + wwH16(BC,71,01,DD,E7,FB,B8,8E,52,43,8B,ED,1E,82,F6,1A), + wwH16(B5,FC,AE,59,66,97,85,F6,88,5E,B0,40,F1,1B,EB,30), + wwH16(0D,31,E2,33,80,F9,F1,04,36,57,C2,BB,FA,F0,6D,27), + wwH16(EE,F5,CE,BE,8C,53,68,05,15,78,A5,1C,1C,68,DB,DF), + wwH16(B0,79,90,59,01,7C,45,3F,DD,79,BB,40,34,7E,46,F3), + wwH16(F9,DD,36,4B,47,34,59,0B,7E,B0,AF,0E,36,14,68,BF), + wwH16(B7,35,C3,62,2F,04,38,04,A1,8C,75,09,90,E0,F2,73), + wwH16(33,88,5F,64,77,9D,DE,90,D6,55,87,FE,21,59,6E,66), + wwH16(74,FB,0B,F8,7A,3A,51,A2,44,14,76,49,DB,5E,F2,6E), + wwH16(90,3D,F0,F0,50,CA,4A,8D,19,87,B1,2F,A4,87,9F,5C), + wwH16(86,79,C0,40,3A,52,A3,29,65,34,1B,FE,E7,3C,97,33), + wwH16(F8,7E,56,01,4F,42,7C,09,9C,C2,30,72,EB,CC,9E,2F), + wwH16(7C,3E,B5,20,38,77,A9,C7,7C,84,1F,38,16,AA,BD,1D), + wwH16(E0,A4,E1,28,C9,35,E1,EF,A0,5F,75,0F,3A,57,C1,93), + wwH16(8D,D2,6E,B3,37,DF,91,25,B6,C5,3D,19,93,14,0A,8D), + wwH16(6A,8A,8E,13,A8,C3,25,04,5A,08,F3,92,91,80,7B,BA), + wwH16(C1,AF,BF,5C,39,67,5C,F4,F3,32,65,97,B1,30,A8,05), + wwH16(AA,A1,DF,04,0B,ED,2D,B9,FD,63,D5,02,36,71,FB,74), + wwH16(6D,2C,85,88,E9,79,C0,F5,85,5B,C9,E6,E7,DD,B2,8C), + wwH16(D2,30,1D,51,0F,42,F8,73,36,1B,76,7F,DD,5A,03,E6), + wwH16(19,D0,15,DF,C8,32,58,64,9C,FA,94,8D,22,E5,E8,30), + wwH16(22,F1,C3,7F,6F,E2,66,9E,8D,C0,25,2E,FC,32,68,DC), + wwH16(91,77,EC,4A,35,88,4B,B5,1D,24,92,D5,FB,06,CC,AA), + wwH16(FB,16,41,94,81,76,E3,B9,8B,20,67,14,43,C4,59,84), + wwH16(BC,ED,61,7E,D5,D6,C9,25,2F,0D,88,CE,F3,E7,8E,26), + wwH16(FD,89,E4,D0,7A,D0,A5,5E,07,11,A7,30,C4,F8,90,CB), + wwH16(BF,3E,4B,39,01,6F,8D,E8,7B,D8,07,1A,C2,88,67,F5), + wwH16(46,17,0B,5B,4E,CF,43,7C,80,50,95,1A,C9,AE,AD,04), + wwH16(5F,C9,57,8D,22,F8,E5,71,16,DF,8D,B0,2F,08,4C,B2), + wwH16(FB,55,1B,5E,09,3D,6C,1B,0D,DA,0E,AD,D0,F1,8D,23), + wwH16(13,DE,8C,07,12,F6,24,C5,67,F4,E0,3E,AF,E3,46,E4), + wwH16(E5,36,8A,D6,0D,60,83,20,9E,FF,63,44,92,CB,B4,30), + wwH16(77,EE,3E,E8,DE,D5,AC,CA,DC,1D,F8,75,D2,5B,FE,00), + wwH16(4B,A8,AD,3C,92,CA,91,E7,17,D8,7F,8E,54,74,C4,89), + wwH16(4B,F8,2D,32,98,E0,53,B1,5D,D2,8C,1B,FE,EC,49,66), + wwH16(9C,33,14,DE,5A,C6,A0,EF,D5,DB,F0,BD,A2,00,32,9A), + wwH16(E1,FB,1F,49,AB,19,24,B3,E0,91,79,23,6E,60,74,B5), + wwH16(2C,EB,C0,F7,D2,12,AC,4C,55,16,E5,CF,39,36,FC,C5), + wwH16(92,03,26,7C,BE,A5,54,0F,C1,80,5E,BF,F9,1D,3C,DF), + wwH16(08,95,4C,15,EE,07,64,C6,C8,FC,AE,21,43,B6,C6,59), + wwH16(04,28,6D,4D,2C,96,57,8B,14,83,2C,B0,43,72,5E,E7), + wwH16(DE,11,70,A9,AE,DC,D1,3F,FB,3E,81,ED,96,5B,1E,AB), + wwH16(F3,B0,93,A7,F1,2E,88,1D,7C,AF,57,8F,5C,BB,DF,53), + wwH16(1B,7D,52,56,90,C1,E3,7D,91,03,22,CF,CF,1B,13,9C), + wwH16(BB,19,75,0F,50,D6,27,43,70,08,72,45,67,14,3D,37), + wwH16(71,F3,AD,1D,5B,E5,8A,72,7B,F4,8F,BA,A1,F7,02,8A), + wwH16(C7,08,35,DA,BA,5A,32,C9,E7,11,21,D4,87,DC,21,DD), + wwH16(43,4F,38,39,FD,EC,12,32,08,01,F9,03,95,58,B4,51), + wwH16(8A,47,FD,42,BE,C5,45,EE,12,D5,76,13,BD,FC,F4,E4), + wwH16(CD,30,79,F5,AA,FF,5D,B8,3A,EE,62,FE,5D,0E,62,85), + wwH16(BD,F3,84,1C,F5,6A,BE,90,E8,9D,65,22,63,E7,7E,8D), + wwH16(5D,6C,36,CF,9A,A0,8F,C6,08,4F,71,6B,E2,46,35,CA), + wwH16(3B,05,2E,F2,9D,40,C8,49,2C,B7,24,28,61,C4,E5,7C), + wwH16(06,06,02,59,AC,0B,EA,25,7D,02,C9,09,8F,C7,CD,3B), + wwH16(98,2E,4B,08,9D,15,9E,D4,D2,A3,2D,AB,00,84,5F,E4), + wwH16(33,85,D6,69,EE,B4,54,DB,22,5C,C0,8B,10,F7,E0,33), + wwH16(BA,D3,2C,43,B8,BC,2F,88,C6,64,88,79,A5,29,58,8D), + wwH16(61,86,82,70,F3,63,8A,5B,C8,FC,D2,37,84,E4,5D,CC), + wwH16(5B,7D,26,2F,EA,E8,C6,3C,5B,E5,07,02,31,F4,7E,FC), + wwH16(F5,A2,19,D2,42,2F,FA,B9,BB,8A,34,69,E5,40,E0,04), + wwH16(FE,7D,40,28,98,F1,25,95,D8,7E,9E,BA,7D,7B,93,08), + wwH16(C8,63,F9,F3,43,A9,CB,E9,A1,E8,E5,A1,8B,7C,7D,2B), + wwH16(CA,CF,1E,E0,9C,21,7D,46,F5,D4,58,3F,AF,0F,62,CE), + wwH16(27,58,17,6B,ED,57,5E,3C,DF,1D,CB,DD,BD,E9,AF,90), + wwH16(AB,EC,62,88,B2,C6,2A,2C,0E,6E,89,6A,9D,F0,D4,EB), + wwH16(23,31,5C,EB,E3,E0,D5,98,4E,9D,4D,77,75,03,8C,E5), + wwH16(B3,EA,D7,25,2A,C5,58,2C,68,89,C4,4E,A8,0B,F0,72), + wwH16(33,C2,1F,B6,D4,B1,BB,F1,C5,73,04,CB,18,ED,EA,65), + wwH16(E1,2A,85,58,0C,9C,86,2B,B4,AD,86,20,2B,F2,1B,20), + wwH16(37,C4,39,BC,5F,B9,71,DF,52,DA,FB,84,3E,78,B3,7C), + wwH16(7A,8F,AE,7A,50,F9,22,FA,80,AC,8B,8E,4B,F6,CD,BC), + wwH16(78,2D,81,E3,E0,D1,90,2D,3D,DE,8A,00,3F,D7,80,C6), + wwH16(F6,C5,21,04,19,A5,BA,39,B4,8E,2F,61,1C,C1,DE,97), + wwH16(2F,2F,F2,20,F2,7B,03,EE,36,CD,B8,20,C4,4F,CF,2E), + wwH16(B5,B6,07,57,E3,E1,19,CB,76,51,D2,1C,78,73,0D,AC), + wwH16(17,A5,A2,7C,4B,1A,36,DC,7F,AA,F0,30,C4,51,90,C9), + wwH16(A6,0C,AB,A7,4B,B9,25,E7,B8,67,69,50,E7,40,57,CE), + wwH16(73,7A,12,E5,85,A4,22,AD,63,05,AA,FE,B2,05,7E,E8), + wwH16(51,74,17,15,0F,1B,34,7F,24,D4,E4,C7,85,D5,3E,91), + wwH16(B5,AE,19,FA,49,1A,C3,91,AA,95,36,12,30,BC,D7,26), + wwH16(45,1F,1A,ED,95,AF,6A,B1,65,D4,AF,72,66,4F,35,A1), + wwH16(0F,88,4D,2B,16,13,6D,72,76,DF,EE,88,F6,A9,34,0C), + wwH16(43,3D,4F,26,21,65,3E,60,FF,3D,5D,52,A5,4E,83,01), + wwH16(77,2C,5B,5F,9F,B5,01,61,AD,03,CD,E3,EB,88,8F,E8), + wwH16(63,E3,E3,5C,3A,3A,63,C0,80,DC,C8,66,9F,90,B1,ED), + wwH16(06,9B,DC,6E,B6,74,28,58,C9,A6,6A,95,60,F6,23,BA), + wwH16(4A,96,37,27,10,34,F0,D0,8F,E7,19,96,16,A9,3D,74), + wwH16(EF,2F,EE,39,BA,E9,6F,28,65,BB,0F,BB,7E,04,A2,5A), + wwH16(7B,09,BE,08,18,3D,24,73,DB,80,8B,28,A9,A6,D4,A3), + wwH16(88,07,8A,A3,A2,A7,5D,90,5B,CB,E6,71,88,3A,E1,B9), + wwH16(EC,E5,FB,62,02,17,41,36,F2,39,31,4D,24,50,E0,E5), + wwH16(59,2A,3F,44,46,70,9B,50,8E,4E,AB,DD,A9,7E,F8,D0), + wwH16(13,A5,6A,5C,F2,EC,31,7B,BC,26,F6,49,9E,7C,6C,75), + wwH16(C5,9D,09,20,3F,25,A6,72,4C,78,D6,7F,2B,8C,FA,3D), + wwH16(7B,D9,B9,01,F3,A2,03,71,5B,7E,86,56,70,1D,19,72), + wwH16(9A,AA,89,B9,85,1A,55,5B,DD,43,B2,6C,12,31,59,EC), + wwH16(04,01,FD,5C,B1,8A,0D,F1,60,8B,72,05,79,01,1C,B3), + wwH16(C3,96,4A,09,F2,A6,BF,E7,2C,58,DD,70,0C,08,63,B7), + wwH16(4B,D1,46,C3,48,A8,B8,84,C7,DD,F8,FB,CD,C9,42,D7), + wwH16(7A,E1,73,FD,7B,C8,64,F4,49,F8,6B,AD,83,94,32,69), + wwH16(43,0B,D3,E5,DC,2C,A6,AC,5B,94,01,EB,82,AE,9D,73), + wwH16(95,D2,F3,41,2F,FC,57,C7,49,F5,5D,04,31,70,AE,84), + wwH16(E1,CE,4B,40,31,F5,E2,41,93,2B,C3,65,49,78,B7,91), + wwH16(EA,3E,AA,6B,05,6F,36,30,21,AB,12,9B,A1,06,46,DD), + wwH16(01,9E,8A,66,42,68,73,87,9E,23,CE,DC,40,75,77,A7), + wwH16(63,38,E0,29,C7,4C,7A,92,1A,C9,B2,06,C8,01,99,73), + wwH16(A3,FD,41,DD,B7,8B,05,D6,C0,EE,D9,DC,A7,9D,25,79), + wwH16(05,18,20,9C,4A,6C,88,D6,CA,68,32,6B,5E,A0,E1,3C), + wwH16(49,40,F5,8A,4E,E4,EA,1C,46,91,4A,2D,9A,3C,E1,AD), + wwH16(69,44,58,DA,63,A8,43,A8,5F,62,26,8C,C8,01,FE,65), + wwH16(9C,C7,F9,85,C0,EA,AA,31,39,CA,2F,6E,4C,B5,F8,B8), + wwH16(2B,7B,95,BA,99,29,20,89,6F,9E,0A,17,36,73,0F,73), + wwH16(E0,C1,63,89,BC,D9,13,B5,0C,1D,E0,0E,07,CC,A7,8B), + wwH16(0D,4A,4D,D6,B9,25,5F,84,9D,D8,E2,1B,46,12,81,7A), + wwH16(0A,41,BC,01,2A,40,93,86,41,79,01,AD,E5,1B,E0,26), + wwH16(E3,80,CC,57,57,C7,71,3D,21,67,0A,86,46,B3,12,91), + wwH16(E1,A3,B8,97,71,9D,6A,5E,4F,A2,92,0B,64,A3,C0,76), + wwH16(69,39,E5,B3,E6,42,EF,5C,25,66,88,8F,2D,1A,B4,85), + wwH16(5D,3E,86,E0,C1,04,14,4C,6E,CF,ED,82,5B,33,09,E4), + wwH16(4E,ED,36,E5,58,EC,C5,56,5C,F4,E3,24,9F,C5,A5,5F), + wwH16(58,F8,4C,0C,8F,77,17,B7,E1,37,C4,56,D4,4E,A2,66), + wwH16(8D,BC,AD,E2,6F,30,2A,69,CC,9D,5F,BB,C8,24,71,B0), + wwH16(D5,A7,7C,3A,5D,AC,34,7D,C9,C4,52,0D,16,5D,D7,CF), + wwH16(28,07,C4,D5,26,53,9F,16,6A,FC,57,2A,C8,D7,57,B1), + wwH16(77,3B,BB,F1,D5,BE,63,2B,AF,F5,AF,87,15,85,A8,1D), + wwH16(8C,6A,56,88,0B,CA,D9,42,EB,18,95,B6,42,07,29,ED), + wwH16(3B,B5,EE,08,0A,30,DD,50,0F,B7,29,01,F8,A7,DC,B5), + wwH16(A8,C9,B0,10,AE,72,E9,0F,AE,DD,0A,E8,36,38,E1,8A), + wwH16(44,F3,FA,63,04,7A,5F,15,BB,8F,33,F1,0F,D6,17,77), + wwH16(39,F8,15,F2,88,3F,52,0F,D3,99,B5,8B,F0,6C,49,23), + wwH16(F7,82,A6,64,B6,DC,23,C7,38,11,1A,2C,3E,5F,23,A4), + wwH16(00,DF,DC,0A,FE,07,71,20,36,D2,2F,C0,CE,A5,F6,3B), + wwH16(8F,B7,C5,2E,F1,EC,EE,BD,5E,7F,78,8A,42,BA,D5,C7), + wwH16(8E,B5,77,FE,9F,4A,B6,52,61,77,C3,05,0F,50,01,3D), + wwH16(EB,6B,0B,FB,83,D4,F3,59,64,63,4E,4B,AD,6C,18,A9), + wwH16(A9,04,E2,27,8B,74,E6,D3,FE,A6,87,C2,98,C0,45,D3), + wwH16(E4,F1,93,16,46,B6,BD,B1,67,4D,6C,35,23,E0,EF,EF), + wwH16(17,27,E3,9D,8A,44,31,07,EA,B6,1A,1A,A3,7B,E6,79), + wwH16(87,85,4D,CF,6D,B1,24,0C,2B,49,38,EB,23,8D,14,A7), + wwH16(4E,19,86,B2,ED,85,3C,32,8C,4C,75,D7,13,4C,B2,7C), + wwH16(C7,F2,CB,6D,BC,89,09,11,05,35,A4,99,60,E6,62,A4), + wwH16(29,83,A3,D7,B7,57,AB,BC,3E,9B,2E,A0,4A,B2,8A,14), + wwH16(87,B7,08,25,EE,EC,D8,9F,74,F9,7F,EF,C1,0F,C6,1A), + wwH16(DF,0B,11,0E,1A,5E,32,26,F1,04,59,F7,7F,72,1F,AE), + wwH16(1C,AA,52,AA,D7,8E,F4,1A,48,1B,D6,3E,CC,8A,37,45), + wwH16(8C,CB,E0,A1,22,00,0C,78,FC,6D,18,06,4F,D5,BB,1C), + wwH16(E4,74,F7,B6,0B,7F,7D,78,35,1C,C4,61,B9,18,00,C2), + wwH16(C8,7C,DC,1E,5B,5C,65,9C,95,D4,62,0D,A3,1B,51,90), + wwH16(1A,AE,D4,74,5A,AF,49,BE,F0,9F,E6,3D,C8,EA,B1,54), + wwH16(F7,A5,38,4C,14,D4,8D,2A,88,DD,9C,74,25,07,CA,A1), + wwH16(63,AF,16,B2,DB,9C,33,9E,95,07,93,CE,C4,FD,1E,20), + wwH16(A0,A8,00,D4,E9,93,F7,7C,3C,7D,8E,87,FE,97,63,BE), + wwH16(81,47,84,39,F3,BF,3A,D3,4A,C6,F1,C4,E0,BB,42,1F), + wwH16(B3,DE,C9,3D,CD,FA,B1,B7,D2,09,39,A3,EF,21,D5,4C), + wwH16(33,A9,68,6E,9A,C0,C3,8D,9C,7C,A6,96,21,86,7A,A7), + wwH16(30,9C,48,07,97,32,88,49,97,C6,30,4B,64,60,47,FC), + wwH16(C9,65,0C,0E,FE,AD,65,AE,D7,E3,28,81,52,99,3C,11), + wwH16(7F,92,F7,35,ED,FC,F7,91,6C,78,71,2E,60,5B,92,EF), + wwH16(F3,14,34,7F,4E,A3,CC,2A,89,7E,D0,1E,F3,3E,9A,3E), + wwH16(51,F8,EC,1D,C9,A3,3C,C4,6B,D4,AF,67,B7,77,C5,F5), + wwH16(21,C7,84,08,24,07,A9,93,95,7F,9B,60,02,B2,CA,4F), + wwH16(CC,94,79,21,A4,F6,E4,43,FF,80,0A,49,0B,6B,A1,83), + wwH16(2A,F2,8F,76,01,5F,58,59,F8,64,7B,4F,43,3F,AE,A5), + wwH16(FB,7F,F4,28,2D,BD,66,47,6C,2D,00,2D,3B,67,5E,11), + wwH16(8B,D0,B2,8A,63,F4,D1,EB,26,CA,AB,87,DA,3D,28,F5), + wwH16(96,AD,EB,41,F1,B0,0C,8F,0C,5E,8C,8C,89,FF,BF,F1), + wwH16(44,E9,90,26,BB,21,F3,9E,A9,D9,FF,3F,A2,3F,CD,2E), + wwH16(69,DD,A8,3B,A5,FD,D3,AD,2D,2D,03,56,02,F2,73,29), + wwH16(A6,D2,D7,41,F3,34,EE,F5,7B,3E,3F,CA,6E,AF,78,CA), + wwH16(FE,7D,AD,1C,CA,BF,03,C9,E8,2A,B6,0C,12,C2,9B,C2), + wwH16(5C,7B,99,E1,B1,E2,72,08,E3,38,87,24,A5,EB,5D,13), + wwH16(77,13,A7,08,DE,8A,DA,54,06,AC,51,7C,1D,64,62,08), + wwH16(A9,04,6D,D9,5E,47,1A,37,57,E1,14,EC,88,93,D0,D5), + wwH16(F0,CB,B9,43,24,D5,82,48,86,60,11,59,4B,A6,3B,9F), + wwH16(7C,87,26,98,B1,E2,7B,AC,92,2D,18,C7,17,96,0C,A1), + wwH16(D4,46,C1,07,9B,94,E0,17,33,73,58,53,AC,F1,FC,60), + wwH16(1D,DC,D6,29,B7,13,5D,77,69,35,3F,57,01,91,5B,E8), + wwH16(ED,6E,30,B5,5E,94,AE,BE,49,B7,B0,D8,63,19,17,79), + wwH16(33,04,6E,DC,1D,8B,91,22,24,1B,90,86,66,41,00,22), + wwH16(2D,A8,F5,4D,3A,A8,1B,0C,0D,D4,67,36,0A,EB,E7,3F), + wwH16(E4,4F,F7,25,1F,3E,C2,0B,67,7F,56,0C,7F,9A,F1,91), + wwH16(7E,83,E7,FF,74,A0,94,75,02,3A,73,E4,BE,3B,23,D7), + wwH16(A8,5F,22,BD,2C,5D,73,5B,54,11,57,60,E5,14,40,38), + wwH16(DD,B3,90,7C,52,15,C6,CE,BD,BE,77,44,77,F3,72,E7), + wwH16(60,6D,0B,FB,19,77,A1,E4,5F,AD,DC,EC,60,FC,29,A4), + wwH16(F1,FD,D8,61,65,FA,7B,3A,13,9B,1D,BD,FF,9D,86,05), + wwH16(57,CF,8E,DF,80,E3,FC,63,8E,ED,C6,6F,09,67,17,F8), + wwH16(1F,A1,69,A9,1F,B2,51,D8,D7,65,8F,F4,86,00,B4,EA), + wwH16(72,78,61,3C,1D,0D,06,2F,EF,4C,B9,B7,56,06,39,6E), + wwH16(7F,07,20,89,D4,96,A3,56,0F,FD,C2,28,B9,3B,5E,94), + wwH16(56,E1,BF,84,72,61,85,E1,51,2A,3C,76,18,30,20,E6), + wwH16(13,72,69,41,6D,F7,F0,45,4F,F3,1F,87,1D,74,A7,94), + wwH16(D6,45,CE,E9,BB,D5,C1,13,DF,EF,74,43,B7,82,23,B4), + wwH16(BE,32,51,3D,E4,CC,9F,79,AF,BD,48,2E,A2,95,EF,4F), + wwH16(D9,1C,00,70,F4,EA,31,B2,92,D6,36,44,48,B1,F2,56), + wwH16(74,28,1D,1B,B6,FA,C3,7B,80,EF,97,1C,45,61,B0,3A), + wwH16(68,B5,1E,6F,BC,97,A0,FF,CF,27,6E,60,60,FF,63,6C), + wwH16(D4,B4,4A,36,87,81,7C,09,2D,C0,F8,E7,A0,C9,D0,77), + wwH16(19,1E,C2,76,36,0F,5A,CB,C7,96,C0,97,E1,3D,FE,D8), + wwH16(A0,F9,2E,82,B5,E4,3E,D6,53,C5,44,D2,E1,2B,97,D7), + wwH16(B0,62,58,C4,4B,7F,A9,45,4D,A7,6C,9F,C1,BC,A4,67), + wwH16(07,6A,66,8A,16,CC,85,43,03,FA,3A,17,ED,02,1F,B4), + wwH16(56,C4,E1,1C,E2,0E,D5,98,7B,49,6C,07,74,92,E0,52), + wwH16(93,81,BF,0C,D0,06,C1,BA,67,2E,4D,1F,1E,8A,A3,D6), + wwH16(22,0E,B4,B0,A3,6C,3E,4A,EE,7B,BC,E8,EE,12,72,4F), + wwH16(EC,90,FF,0C,63,63,5F,EF,4C,4E,09,7D,E8,1B,BE,83), + wwH16(AC,A5,B5,3C,64,7B,2F,49,79,C4,C1,A7,F5,D5,71,98), + wwH16(4C,44,2A,F3,DE,D1,74,53,E3,BB,28,74,A2,C1,90,D4), + wwH16(48,2A,A2,79,D8,6D,91,F6,47,18,24,2B,7F,B2,0B,26), + wwH16(25,4D,83,B1,CB,6D,10,A7,A9,DA,40,50,5F,7C,D3,17), + wwH16(21,71,4D,27,19,0C,94,94,4F,80,F0,51,42,93,B7,5C), + wwH16(C5,79,D8,05,F4,45,D4,51,4C,59,89,97,47,60,6F,62), + wwH16(8F,92,A6,BC,03,6F,7C,DA,AF,2C,CA,08,77,3C,62,DE), + wwH16(54,E2,E7,16,2F,D7,CF,AA,B7,22,76,48,DD,38,BF,69), + wwH16(F8,78,29,2E,42,06,EC,CC,8E,D0,40,EB,A1,CD,0B,A0), + wwH16(0F,80,83,6D,07,AE,07,27,4D,C4,1E,8B,97,E2,A9,E3), + wwH16(83,85,28,53,80,DA,55,05,1E,77,8B,00,5A,D0,1A,24), + wwH16(30,AE,6A,B0,C1,37,E2,18,A7,D6,0C,28,A6,9F,69,3A), + wwH16(9A,7E,AC,24,DF,A2,3A,D5,AF,3B,8F,F3,00,88,61,25), + wwH16(42,FC,A3,71,41,00,72,A0,36,F3,06,7C,F3,8A,A5,6F), + wwH16(02,9E,DA,91,A5,21,19,A1,4A,31,54,CD,80,FB,B4,A2), + wwH16(9B,38,ED,0C,35,CA,E9,3A,9F,CE,B6,25,0A,FE,76,DA), + wwH16(D9,28,C4,7A,08,9D,02,C4,50,7C,4F,2F,90,54,3D,90), + wwH16(BB,C3,1A,81,02,B4,A8,52,DC,EB,4A,05,41,05,43,8E), + wwH16(7A,E2,65,A0,E7,B4,36,C5,C0,D4,50,A6,E4,C4,35,1C), + wwH16(79,37,47,3A,39,2C,3A,75,7B,6F,BE,30,08,0D,77,94), + wwH16(64,BE,0C,E3,BF,74,FF,92,00,98,A8,01,E0,45,38,4D), + wwH16(A3,A1,B2,81,6E,A3,85,6D,72,A5,C3,19,E3,98,E6,C0), + wwH16(8D,A3,D8,C9,EC,33,4D,01,3F,26,6E,A5,93,90,E5,24), + wwH16(0F,90,DC,AC,D8,C9,56,21,30,4D,48,10,72,D4,36,11), + wwH16(C4,3C,94,89,E4,47,DA,AD,64,CD,D6,D9,F0,25,17,01), + wwH16(3B,05,50,32,F7,96,A9,5B,CB,C3,91,CE,ED,23,ED,93), + wwH16(70,84,FC,0E,F8,D6,1D,56,DE,11,3E,F3,27,C8,9D,99), + wwH16(77,7E,B5,BE,70,A1,CF,F6,A7,17,BF,5C,66,4F,1E,F4), + wwH16(45,F5,A4,C9,F1,C9,BF,C9,66,A9,44,C6,19,63,D2,9D), + wwH16(BE,E0,F9,51,4D,82,8E,B5,3F,27,09,5E,A0,81,A3,CB), + wwH16(33,23,10,6B,40,2F,2B,29,9E,F9,94,A4,C8,8A,0B,D8), + wwH16(E6,20,B8,01,76,C5,21,A1,64,81,05,BA,B4,CD,DE,BF), + wwH16(65,8B,FD,6C,AB,6C,4E,A8,4C,8F,68,01,A0,5B,3C,23), + wwH16(8B,29,B4,FA,D2,B2,5B,7B,AA,9F,F0,B3,EC,CC,5D,E1), + wwH16(14,C3,27,F5,2C,C9,56,46,7A,75,56,7D,E4,50,E1,4A), + wwH16(A5,5D,72,4D,9D,B8,AD,86,06,30,52,D0,A7,4A,32,EF), + wwH16(3E,70,44,97,25,18,6B,24,DD,F4,1C,68,18,DA,1B,B0), + wwH16(8A,6D,07,26,B0,86,3B,FB,CE,8E,88,C8,D2,10,46,0F), + wwH16(92,0D,CB,B0,50,08,46,70,BC,56,B3,C7,B5,5C,F9,1E), + wwH16(87,69,AD,57,44,B0,C9,78,C8,6D,FE,9E,45,71,C2,CE), + wwH16(EF,0B,9F,22,7C,05,BC,F6,33,4E,8D,C3,01,4E,81,A0), + wwH16(E6,6C,EE,CE,EE,19,F3,F3,96,68,3F,A5,8E,C5,B8,E5), + wwH16(F1,82,B9,78,EE,D8,DC,49,C6,9A,DB,03,5C,3E,8A,8F), + wwH16(5F,AA,A5,BC,86,DA,C8,3E,67,61,25,0C,BB,09,4A,0C), + wwH16(D4,28,CA,4F,6B,DE,83,D5,9D,11,20,05,45,02,B9,58), + wwH16(3A,08,DF,FF,9F,CA,41,CC,B1,9F,2C,9D,5D,C1,C7,A9), + wwH16(AF,A8,3C,A0,F8,CC,77,BD,3E,03,AE,82,77,1C,BA,69), + wwH16(E6,3D,02,9E,BC,9D,D5,84,9D,98,93,91,CE,00,80,26), + wwH16(FB,70,28,BC,C1,2F,36,28,6A,29,2B,96,99,A1,9E,96), + wwH16(D4,89,A3,21,65,3F,BC,03,95,3B,F0,6A,F5,AC,11,88), + wwH16(8A,6B,4B,9D,F9,EC,38,6F,82,53,B4,57,04,F9,1D,DE), + wwH16(9E,3B,03,B1,31,C1,22,48,02,9F,D5,42,9E,02,1A,1D), + wwH16(DC,3F,2B,02,C3,8E,8A,2E,B3,44,F9,88,9B,44,B4,15), + wwH16(38,4C,90,9D,C7,01,E7,F8,9A,BC,39,8B,6F,86,06,6C), + wwH16(3A,D0,9D,DC,7D,FC,30,F5,3C,9F,26,92,69,6E,35,4E), + wwH16(A3,72,0F,1A,63,86,27,E8,D0,22,0E,DC,3F,F5,0B,46), + wwH16(BA,BC,4D,E9,47,7A,B7,B4,36,95,2C,D1,1A,A6,6D,92), + wwH16(35,71,9B,54,36,76,32,36,FE,28,2D,56,E5,2F,A3,17), + wwH16(C2,79,CD,C2,EE,39,73,79,6B,47,2F,04,9A,A3,6B,7D), + wwH16(8F,DB,66,A0,5C,38,DA,73,DF,23,FF,9F,53,6D,3B,08), + wwH16(AC,0C,23,4D,35,94,B5,99,2F,50,14,1D,70,DD,A1,B4), + wwH16(FA,5A,C2,DA,BF,D3,EA,14,A0,61,40,B0,8B,DC,5C,D4), + wwH16(5A,A8,DA,A3,4F,30,B5,EF,A7,2A,3D,48,78,3D,0A,8A), + wwH16(27,92,F6,6D,C3,C8,D7,F1,6B,F7,45,14,A4,CC,7B,4E), + wwH16(B3,90,6B,67,C5,5F,DD,62,78,B4,06,73,62,04,C5,06), + wwH16(FF,31,E7,32,48,1F,97,51,5F,56,2F,C3,6E,17,51,2A), + wwH16(74,42,71,39,73,CE,72,85,0A,59,04,4B,3C,85,BF,0F), + wwH16(EF,CF,52,91,BA,18,D9,E2,CE,20,61,64,58,CA,09,33), + wwH16(EE,01,5A,9B,76,39,80,03,5D,75,9D,ED,04,D3,EA,86), + wwH16(76,1C,A6,A3,37,3C,DF,85,97,0F,F2,B7,C4,48,2C,71), + wwH16(16,93,37,0F,85,51,46,75,DE,BC,29,2C,2A,EE,F0,0F), + wwH16(F1,E7,3D,F4,FD,68,A1,95,8E,4F,F3,81,A9,95,1D,87), + wwH16(D0,94,DA,F2,AC,72,2D,C1,38,D9,89,30,DA,F3,D7,84), + wwH16(3A,05,4A,00,B6,21,EF,70,C2,FB,59,D2,FA,8E,7A,CA), + wwH16(EE,AE,EB,4B,27,E8,14,1A,A7,08,82,DB,00,F3,F8,9D), + wwH16(D3,2F,EB,EC,41,81,27,54,E3,03,90,96,9D,78,20,18), + wwH16(A0,FC,F6,5D,73,8A,C2,DF,F6,B4,E2,7E,C7,EF,49,56), + wwH16(68,94,C5,7C,B3,97,3A,4F,4A,A8,A3,14,3B,40,69,C6), + wwH16(13,84,0C,B5,7F,68,D2,41,C2,7E,0F,BE,16,3D,16,E5), + wwH16(82,16,C8,C0,FD,7D,52,4C,7D,FD,E4,F3,80,5B,D9,59), + wwH16(EE,7F,B6,C3,C3,F5,5E,37,6B,F8,27,90,43,4B,24,B4), + wwH16(CC,BD,27,E2,0D,26,23,1A,ED,2A,8E,14,DA,0E,A2,26), + wwH16(AF,AF,F0,ED,64,50,B3,17,A7,1F,31,C0,8D,3A,B2,6C), + wwH16(C7,48,49,86,F5,87,FA,BF,96,DA,40,4F,E2,5E,A0,06), + wwH16(9F,BB,96,3E,B0,23,B3,F6,3B,80,BB,6D,44,07,30,8C), + wwH16(74,12,72,4B,80,D3,F1,5E,25,E4,BE,53,3F,90,E1,97), + wwH16(AC,50,B2,65,25,71,F4,ED,B6,A6,64,10,F5,D6,89,2A), + wwH16(73,A7,82,C6,BE,24,A9,6B,D4,9B,F9,97,56,6F,B0,6B), + wwH16(27,78,BA,53,B7,E5,30,CB,78,13,E7,4F,00,B2,D1,3C), + wwH16(62,1B,5E,DD,38,62,06,E8,F9,DE,D1,C6,65,E5,5F,9F), + wwH16(CB,25,45,4D,96,8F,7F,43,02,7C,DF,65,B2,32,85,87), + wwH16(71,9E,D6,9A,9A,14,12,27,C3,5C,5A,00,70,1C,A7,C8), + wwH16(D2,9C,3A,C1,E2,13,2C,5D,81,7F,A2,7C,2F,F4,7D,46), + wwH16(ED,E7,0C,D5,A7,60,11,B6,C1,8D,33,26,10,C9,F9,93), + wwH16(30,43,8B,DA,16,38,2B,C4,16,AF,02,20,30,1D,EC,E5), + wwH16(8B,73,C9,AA,0F,FC,95,5E,B1,D6,8A,25,7E,93,D2,19), + wwH16(46,72,E9,B6,B7,DE,89,09,70,26,29,5F,AF,A0,D1,4F), + wwH16(DC,91,D2,45,8C,A1,75,A3,6F,ED,1C,59,BE,B4,DC,EA), + wwH16(26,2F,9D,DF,83,AD,A1,39,66,44,2B,3F,93,09,21,86), + wwH16(D1,6B,19,E9,94,55,33,78,1E,8D,11,6E,FD,F3,7A,09), + wwH16(D0,16,35,13,50,E8,B3,91,75,77,21,48,74,78,B3,B7), + wwH16(1F,64,57,6A,C3,64,63,E0,94,86,6F,28,1D,51,8F,1A), + wwH16(85,18,CB,BF,85,80,80,50,CE,3E,65,5A,85,3B,81,3B), + wwH16(51,65,58,AF,63,12,E8,12,E5,DF,74,17,B4,32,77,69), + wwH16(90,DB,CB,65,4F,7E,B0,E5,C9,72,D1,C3,50,FF,C2,AD), + wwH16(67,EC,94,01,11,19,EE,1C,98,9A,CA,4F,0B,88,7F,7B), + wwH16(E5,AE,97,B8,89,AD,5D,53,D6,10,CC,68,C8,02,A0,99), + wwH16(5A,3F,FE,7C,63,4E,04,74,56,11,79,65,7B,77,90,6E), + wwH16(3B,E6,D9,B8,A6,53,53,C8,CB,F9,52,DE,28,A4,A6,61), + wwH16(20,6B,58,6E,B4,41,56,AD,F2,95,D6,EC,61,BA,A4,50), + wwH16(53,62,85,91,AC,09,02,A0,0E,9C,FD,1F,84,69,14,AD), + wwH16(8F,A0,91,85,7C,DB,74,59,BC,5B,2E,21,25,06,63,56), + wwH16(18,97,FD,43,22,4D,1B,7B,DA,0B,C0,BE,24,19,7F,58), + wwH16(2D,76,CA,78,B8,93,C7,FA,FC,C3,BC,E5,4A,70,D0,E1), + wwH16(4B,FE,71,91,60,CE,7F,D5,73,14,A9,A0,44,49,F3,3B), + wwH16(50,BE,D9,FE,7A,E9,62,9E,DD,C6,24,76,52,11,22,B8), + wwH16(14,DB,A3,65,A6,45,7B,4E,08,7F,93,55,09,82,1E,45), + wwH16(5C,97,BC,F4,FA,ED,BE,86,DA,78,8B,BF,E6,38,36,82), + wwH16(96,01,D8,D7,6E,6E,FC,DB,8D,98,EF,F1,E5,91,0D,3C), + wwH16(11,D4,89,B1,05,CE,CE,11,87,77,9B,EC,1E,F9,08,44), + wwH16(2F,F6,FB,16,E4,64,2D,4E,8F,8C,FD,F3,44,5A,98,62), + wwH16(92,C0,42,F4,B9,A2,82,01,D9,7F,96,4B,75,6C,9F,61), + wwH16(A0,31,86,5A,1C,52,9F,E6,AE,16,D5,B3,F8,6F,6E,CE), + wwH16(4E,2A,32,09,29,12,16,CF,97,2B,CA,0B,20,06,D2,4C), + wwH16(93,62,73,5F,15,32,A8,65,E0,95,C6,C6,C4,B9,E9,37), + wwH16(7F,40,86,CA,C6,8E,9C,93,99,BD,13,F7,AD,C2,7E,BC), + wwH16(10,6B,5B,B4,90,A9,C4,31,6D,7A,42,C4,C0,3F,BB,BE), + wwH16(42,9F,6E,99,C2,56,C1,4B,0D,8F,40,FB,A8,28,F7,E6), + wwH16(98,A1,8C,4D,DA,6B,0E,BF,D1,AA,DC,58,E4,E5,9C,9F), + wwH16(F7,F0,AA,98,6E,2B,3C,A6,6D,CD,E0,31,8F,C7,8C,F8), + wwH16(B5,26,1B,C8,96,82,3E,EF,F3,05,52,9A,6B,39,C9,A7), + wwH16(0F,80,31,56,60,C2,3B,49,55,3E,D1,A9,C3,05,20,DF), + wwH16(BC,91,06,C4,3D,F6,83,14,C0,6F,8B,16,7D,B7,B7,3D), + wwH16(B2,71,8B,32,C6,E6,B1,46,28,F5,2A,88,8A,C7,70,55), + wwH16(5A,F8,4C,8F,C4,58,B6,AF,65,8E,B4,CB,BF,37,AD,78), + wwH16(AD,BE,3C,2F,BF,D3,A5,6E,83,67,4C,A9,8A,EC,E3,27), + wwH16(62,05,43,2D,A7,BE,74,66,E5,B5,B1,16,AF,16,3B,C9), + wwH16(BE,5E,CC,84,D0,B2,F3,43,ED,38,96,95,55,75,B4,50), + wwH16(F9,94,EE,34,96,25,D0,AA,44,74,4E,77,DF,D0,A3,D5), + wwH16(4F,D7,88,CB,3D,6E,EB,99,51,6D,A9,5E,3C,BA,57,D6), + wwH16(20,96,28,F7,91,48,33,D0,79,20,7B,B9,D1,D2,8F,E2), + wwH16(B5,A0,1A,DC,22,C5,91,CB,BB,57,21,CE,B7,42,CB,02), + wwH16(EF,41,AA,64,ED,0B,C8,B1,3E,EC,94,93,DD,B0,6F,A7), + wwH16(D6,99,0D,B1,33,FC,29,94,4A,EF,7C,EE,62,A0,BB,A3), + wwH16(F9,4A,6D,18,0A,30,3D,A5,6B,3A,80,AD,25,AC,C9,7D), + wwH16(22,CE,91,FB,82,92,81,D7,7F,85,17,9A,DE,54,00,20), + wwH16(6D,DC,7B,82,F3,22,C0,A4,74,7B,74,D4,4E,A6,76,DC), + wwH16(07,B0,29,76,89,46,73,C8,A6,E6,4A,7D,15,40,0F,71), + wwH16(CD,48,BC,0C,8E,1D,08,EC,28,46,C6,DE,24,70,8C,AE), + wwH16(15,51,4B,17,40,5A,66,46,8C,B6,89,F1,9F,8D,64,BE), + wwH16(E8,79,CC,DB,8C,E4,56,C1,70,0F,FC,19,F9,3D,B2,1D), + wwH16(54,EE,83,0C,53,FC,75,DD,D9,56,CD,B7,FA,CC,6B,83), + wwH16(3A,90,75,EC,E9,A1,D2,F3,F8,FD,95,CD,E6,65,1A,F0), + wwH16(0C,2D,8B,DB,3E,13,41,57,DD,F8,A0,1E,9E,32,5C,B4), + wwH16(CE,64,FD,D3,66,B3,63,AE,68,F1,B8,95,05,28,5C,BD), + wwH16(A9,E7,F2,01,BC,4D,E6,67,B2,F9,83,67,A2,F7,83,D8), + wwH16(F0,84,BF,2E,D5,FF,55,E3,6A,02,A5,BD,A1,0A,1D,56), + wwH16(CE,F3,E7,77,DF,25,63,B4,48,D5,00,D9,43,5F,F9,DD), + wwH16(63,41,8C,5D,71,A9,43,5B,87,CD,0A,6E,50,3C,A6,60), + wwH16(54,74,7C,FC,1B,99,32,52,17,6C,84,B1,58,45,2B,64), + wwH16(C0,27,CE,B7,B8,E8,53,FD,25,4D,9E,7B,82,53,AE,ED), + wwH16(8E,ED,14,82,FF,D3,1A,D1,FF,FD,B3,51,3E,83,21,EB), + wwH16(71,57,69,29,56,F5,FB,9A,E1,43,CF,35,0A,8A,44,36), + wwH16(40,84,CE,3A,9D,25,09,16,0C,D5,C4,5B,90,3E,09,28), + wwH16(28,F6,E5,4B,CE,26,54,59,8B,7F,1F,1B,63,BE,14,EA), + wwH16(D0,1E,B7,C3,70,2F,F4,3F,B1,8B,BA,D9,B9,E2,CE,D9), + wwH16(B2,82,7A,E0,D4,A5,50,FD,26,95,58,54,D3,9F,63,20), + wwH16(08,55,90,EA,66,37,F4,AA,9F,3C,07,2C,63,4B,7A,14), + wwH16(76,7B,9A,9D,2D,92,53,7D,18,69,C6,DA,98,E5,34,90), + wwH16(82,C1,A2,6E,0C,4B,77,16,B6,4D,6A,5F,DA,41,CF,03), + wwH16(59,79,D5,0E,09,E1,97,69,63,CB,6C,D3,97,0F,88,D9), + wwH16(70,DD,10,3D,17,AA,4A,B2,B5,4E,EE,D1,C4,2B,6B,DC), + wwH16(53,87,7D,68,64,60,20,D7,F8,2E,1F,94,90,BA,1A,05), + wwH16(3E,6C,0F,91,56,E1,C1,A6,EA,27,4B,C5,2C,DF,C1,02), + wwH16(7B,77,FE,A1,8F,23,60,C8,48,29,A9,4A,BC,4A,4A,FF), + wwH16(24,84,D1,ED,B8,43,FC,97,AF,A4,99,C3,18,24,B1,59), + wwH16(7A,AF,C8,78,26,26,F9,C1,EA,18,07,31,07,23,E5,EA), + wwH16(71,F5,B4,79,AE,B9,A0,EF,E6,A0,C9,28,9A,E9,BA,D1), + wwH16(15,F8,D9,8A,4F,5E,A5,00,3B,31,2F,EB,3B,20,9D,39), + wwH16(53,4C,F5,84,AE,0F,E0,49,B8,7A,18,22,48,B3,AA,91), + wwH16(60,92,7B,86,F5,55,81,C4,6F,47,63,50,83,C6,B4,BE), + wwH16(C1,CF,72,93,8D,F0,9E,6B,5B,8E,1F,2C,A5,42,EF,3D), + wwH16(95,9F,10,E5,72,A2,35,C9,84,9E,12,10,7E,C0,0D,B2), + wwH16(59,FA,A2,01,40,20,7E,C3,22,4D,8F,42,BA,17,D9,7E), + wwH16(3C,23,B2,48,16,E3,00,01,A4,81,D9,57,6B,45,48,04), + wwH16(64,31,67,C5,34,B1,A0,34,59,27,BA,94,AB,63,F9,16), + wwH16(22,CD,06,40,16,85,5E,46,DA,95,DE,63,DD,92,8D,7F), + wwH16(2E,B9,59,F4,35,BA,8A,1F,D1,F2,F8,F6,93,3C,FC,A7), + wwH16(90,F1,D9,AC,E9,74,B7,AE,91,ED,00,AE,29,17,F9,6B), + wwH16(A7,73,77,EE,B2,82,12,D7,C9,AC,47,5C,87,83,C5,C8), + wwH16(E7,5D,37,DA,11,92,90,14,2D,F3,AD,1C,94,03,0C,4E), + wwH16(37,7C,43,D1,08,6E,64,37,7C,48,91,AB,D4,FB,5F,E4), + wwH16(F4,02,62,3A,05,A8,43,CC,26,29,F6,28,FD,65,7E,8C), + wwH16(48,63,BF,7D,2A,C7,EB,38,F2,8D,71,BE,1E,F6,C9,C6), + wwH16(D5,CD,77,D7,9C,8F,7C,6A,A7,5E,E8,2E,3D,C1,66,2B), + wwH16(67,7F,47,D7,96,B3,17,AC,C0,85,E0,10,89,2B,85,52), + wwH16(56,A8,C4,F9,D8,C1,DA,39,56,4B,6E,91,0B,AD,8E,6A), + wwH16(CC,63,AF,94,B2,9F,78,3D,64,1C,62,DF,B9,93,3A,3E), + wwH16(F8,A7,1D,CE,4C,77,D7,F0,5E,BE,41,0D,6C,69,F8,7D), + wwH16(41,E2,EA,FE,11,D4,DF,0F,5A,D2,DF,66,48,EE,E9,74), + wwH16(BB,25,E8,5E,67,23,41,C7,AB,0E,FE,D5,A7,93,BF,2C), + wwH16(67,FA,AE,F4,3A,25,A1,7D,9F,F6,44,D7,CC,6C,B7,31), + wwH16(74,95,E2,B4,41,EC,A6,8B,0B,CE,3C,11,41,9C,5C,E4), + wwH16(7E,77,13,CA,7D,68,6F,09,B3,79,6E,F3,EF,26,EF,6B), + wwH16(BE,DA,B5,E6,6D,28,C6,6F,1A,21,D3,37,3A,3B,FB,51), + wwH16(94,43,B8,85,0B,07,D6,77,04,3C,05,70,CB,8B,E8,F7), + wwH16(6A,33,C5,7E,CA,41,2B,7F,08,09,E7,7D,33,DA,D0,DD), + wwH16(F4,E1,64,CF,79,7E,B0,6F,17,5F,39,04,A1,3A,2C,8C), + wwH16(DE,42,70,73,B7,BC,7E,08,5F,9B,15,FB,4F,6A,BC,A1), + wwH16(ED,B9,41,15,E4,E9,08,D7,64,A9,54,78,80,04,89,8F), + wwH16(06,6D,08,8A,EC,F5,31,A7,82,05,10,61,01,AC,5B,78), + wwH16(6E,2F,CD,61,17,EB,50,5B,F9,D1,D6,9E,E5,EB,7C,FF), + wwH16(41,C5,95,88,AD,EE,7F,4F,CA,1F,EA,54,1A,50,98,1D), + wwH16(44,6F,DF,8F,76,C5,95,68,78,1C,CD,08,65,5A,2E,6E), + wwH16(71,66,20,41,5B,C7,E3,C6,60,96,96,58,5B,90,07,41), + wwH16(4B,3C,7E,F5,FC,4D,02,0B,36,F8,46,35,30,A1,99,1F), + wwH16(55,F3,A8,0E,9D,62,93,FA,0D,7B,B8,4B,26,F7,7A,C4), + wwH16(EF,46,2F,3F,B0,1A,8A,93,AC,BF,34,7A,B2,F2,71,52), + wwH16(00,64,06,EF,3C,9A,48,97,1F,EF,D1,68,6D,BB,64,27), + wwH16(CE,21,73,BB,49,7E,02,53,F3,64,1F,4C,1D,50,13,EA), + wwH16(21,30,40,5C,F9,A3,96,B5,84,9D,3F,70,33,EB,BC,D9), + wwH16(5A,69,D3,80,EE,A4,FE,BE,7D,B6,72,B8,18,AE,F0,9C), + wwH16(DE,2C,4A,41,F8,9A,A1,91,18,0F,3F,C6,5B,74,08,0C), + wwH16(B2,0C,75,4A,69,61,22,09,39,B9,24,17,69,AB,58,15), + wwH16(C2,66,71,AE,88,DF,75,70,45,AC,68,68,C8,BF,F8,AA), + wwH16(B7,5A,7A,A2,6B,D3,38,71,C7,01,00,3A,8B,8B,50,F7), + wwH16(61,62,59,A1,A5,A8,73,57,8D,21,23,04,65,61,F6,6E), + wwH16(00,FC,F4,D4,1A,D5,15,AB,AD,3C,3B,D5,A6,92,37,C8), + wwH16(9C,B8,64,A9,AD,AC,F7,2A,DC,45,52,A3,80,69,73,C5), + wwH16(CE,B6,9D,76,A7,DE,23,7F,76,75,4E,1E,70,DA,36,0A), + wwH16(6D,6A,99,D9,92,30,BA,C6,22,30,90,0A,3E,E4,26,3F), + wwH16(D0,7D,DF,01,58,47,4D,F1,DF,79,7C,DA,60,87,7D,8E), + wwH16(CD,E0,49,CA,6E,98,2E,18,75,05,85,AF,9F,95,E6,8B), + wwH16(C6,EA,BC,FB,32,A7,88,81,0E,E1,AF,18,4F,9D,15,1E), + wwH16(DC,7F,C3,28,32,69,C5,FC,49,7C,7E,7D,E8,51,A7,02), + wwH16(9D,94,B4,EA,62,9E,04,90,E2,78,31,CE,2F,18,25,9B), + wwH16(88,D6,BC,EA,19,09,14,CE,D4,D1,34,07,C7,93,7C,F6), + wwH16(E5,A6,69,61,49,12,DC,4A,DE,75,93,EF,19,25,5E,BC), + wwH16(E6,BA,20,18,E6,F6,58,BC,E1,CE,9C,6F,A8,1A,23,B8), + wwH16(9A,2A,E3,C0,55,13,BF,13,95,AD,DE,95,CB,17,1D,DE), + wwH16(F0,E0,9C,C2,C7,D3,7B,67,E3,A3,D6,12,A0,30,78,49), + wwH16(5E,8C,60,AE,85,17,1C,8D,1F,A5,49,41,C5,1C,D4,3C), + wwH16(9B,CE,FA,48,6F,34,A5,C8,E7,B3,39,DB,90,58,17,A5), + wwH16(64,CD,1F,9D,C1,08,36,32,72,4B,2F,46,9F,49,58,FD), + wwH16(25,16,CA,DA,D6,93,BB,2D,7C,C9,FC,C7,43,40,7C,2D), + wwH16(6E,EC,BB,D0,0F,2D,5D,E8,BB,4C,2D,69,CB,68,54,7E), + wwH16(23,24,4C,15,43,9A,A6,ED,67,AA,68,81,2F,68,EC,9D), + wwH16(72,19,5A,BC,69,38,31,9C,BC,7A,92,8A,EC,ED,8F,84), + wwH16(3B,36,12,8C,E1,3B,D6,73,5A,F9,41,01,B3,EB,DC,C5), + wwH16(5E,40,95,4F,04,5E,4B,96,25,AA,3E,8E,9B,3A,5C,C6), + wwH16(10,8D,D4,AD,82,A7,0F,FD,3F,43,1B,70,19,51,71,AE), + wwH16(E7,5F,2B,41,D6,79,1C,59,DC,63,7A,41,D5,32,F7,B7), + wwH16(87,35,9F,8E,39,3D,95,EE,26,5B,5B,6D,52,E3,77,00), + wwH16(1C,09,08,E7,D9,93,D3,0E,07,01,2B,0E,54,B8,D5,CA), + wwH16(FF,79,31,D5,63,0B,8A,BA,AD,F0,F6,1B,D6,C9,66,A0), + wwH16(27,A7,10,AE,F4,1B,D3,6E,74,6B,FC,A6,3F,F7,19,87), + wwH16(B9,AE,1C,02,84,2C,AF,FD,0E,0B,E2,87,BD,69,69,3F), + wwH16(D8,6C,31,84,1E,E7,91,B4,73,94,05,92,2F,95,34,4F), + wwH16(F5,E0,9F,C8,C0,AE,3D,79,BD,69,8F,4B,36,F5,70,D2), + wwH16(8C,0C,04,F3,F6,7F,C0,BB,E7,86,0D,C6,1C,11,BE,34), + wwH16(21,AF,81,97,6F,DB,BC,26,D6,19,F1,1C,0F,5C,33,9F), + wwH16(E0,64,7C,A4,09,CB,0D,9C,BD,BA,AC,64,2C,7C,26,12), + wwH16(92,AA,11,26,24,B0,8B,BC,9F,DF,8F,1B,66,50,B1,DC), + wwH16(ED,AA,7A,3C,97,7C,02,D2,1E,50,E8,6B,E0,92,C9,59), + wwH16(4B,3B,B8,41,02,34,B1,96,9F,58,51,37,BA,5C,43,77), + wwH16(49,66,25,F2,1D,BD,BE,58,F5,E7,61,E1,29,F7,4B,FC), + wwH16(9E,86,6D,C9,FD,B9,01,AA,09,1E,86,B9,FA,01,1A,FB), + wwH16(EF,94,46,F4,C0,B9,97,5C,E4,53,08,24,71,88,66,03), + wwH16(E1,6D,F6,BB,C7,4C,96,A8,F6,99,C4,4B,FD,13,33,D7), + wwH16(11,DF,CE,7E,27,DB,8A,D7,42,8A,B3,4A,9B,53,47,E1), + wwH16(0F,75,A4,58,D5,88,A5,6E,35,20,BE,49,2D,32,DF,F1), + wwH16(EB,68,CC,82,1F,28,61,01,03,F8,7E,85,FA,34,A4,2F), + wwH16(46,48,58,F2,27,6A,39,8F,60,CA,25,5E,15,D1,65,0F), + wwH16(1D,CA,C0,7D,C3,53,CB,B0,2D,4C,62,3D,7D,70,B7,10), + wwH16(55,14,B2,F0,D9,27,21,01,EA,8D,DB,76,57,95,29,7B), + wwH16(06,BF,1C,FB,E5,1C,68,74,B4,C2,87,EF,35,05,0D,CB), + wwH16(83,0B,62,25,F7,A4,2F,38,E3,52,CC,6A,77,F0,55,00), + wwH16(B3,20,55,8D,85,48,6B,8D,2C,58,6F,E0,57,E3,F7,00), + wwH16(32,29,4E,7E,5E,34,3C,5F,7D,C2,30,C8,73,35,9F,71), + wwH16(07,C0,EA,D3,7F,27,6D,D5,8D,7D,EB,0F,08,53,8D,A5), + wwH16(4C,B2,7B,4F,EA,FD,20,8D,CD,E6,29,3B,B9,3F,A4,50), + wwH16(13,62,0A,20,18,E6,0A,3A,5C,DA,43,CC,CD,9C,D8,EC), + wwH16(43,F4,F4,D1,5E,DA,B4,A8,7C,BB,79,C5,F9,2A,44,D9), + wwH16(EF,D0,22,5D,4F,E4,E6,62,88,63,3E,22,3D,C2,01,BD), + wwH16(E6,04,31,7F,32,4B,28,38,0B,74,4A,85,0D,B7,38,A9), + wwH16(F1,44,2F,4D,C5,C5,12,C4,DE,22,9F,1B,F6,8F,00,3C), + wwH16(5C,08,8A,91,23,6A,E2,9A,68,60,F6,41,71,B5,DA,E2), + wwH16(7A,30,E5,53,CD,63,2B,1F,64,FE,95,5F,E3,99,1F,C3), + wwH16(77,A8,96,BB,A6,D8,6F,5D,D6,2B,40,C6,9D,A7,81,32), + wwH16(8A,7C,71,17,CA,EC,68,0D,9C,0D,90,7D,01,FA,F6,FF), + wwH16(0A,DC,77,25,CD,7C,8D,0B,7C,C6,06,48,18,DF,49,80), + wwH16(44,46,48,60,96,91,91,EA,5A,59,1C,DC,FE,01,0E,C2), + wwH16(79,AE,67,C3,FE,C1,DF,6C,E7,AF,E2,4A,E9,43,8A,BA), + wwH16(D9,23,1F,0C,4F,51,94,9A,11,D6,9F,A7,90,AD,2C,C1), + wwH16(34,CA,60,2E,B2,94,24,BB,2A,6D,58,44,0F,82,4B,6D), + wwH16(62,FA,4C,DE,BF,D0,05,8C,2A,C1,5A,1C,8B,E1,05,9F), + wwH16(B2,E0,29,C0,4E,A5,BF,AE,88,18,95,D7,CB,E0,C2,32), + wwH16(C4,AB,6D,87,9A,81,BD,6C,A2,48,6E,50,41,B4,AC,49), + wwH16(7F,F1,03,31,0C,B0,5D,7C,6E,13,28,64,91,25,A2,7A), + wwH16(11,3A,5B,2A,CE,37,E2,E7,46,10,AE,DD,BF,B1,3D,71), + wwH16(73,2F,CD,D3,7C,F3,09,9A,D1,2C,4D,3F,9A,CE,E5,39), + wwH16(F3,9C,1E,66,4C,3F,DB,32,C9,A1,FB,C1,A2,5A,47,B3), + wwH16(55,75,7E,AC,57,C8,CA,67,00,EF,29,CA,2C,84,E6,85), + wwH16(B4,0B,30,46,4C,A6,AF,38,E7,1F,32,F6,63,15,3B,83), + wwH16(D1,B4,1E,1A,A8,16,C6,B5,EB,7E,05,DC,5C,55,4D,3C), + wwH16(34,91,B7,32,D7,9C,6C,7D,E4,75,EE,D2,12,E5,9A,A3), + wwH16(66,6D,77,61,28,36,94,A2,21,94,05,5E,9E,F8,86,39), + wwH16(68,B9,AF,ED,B2,E9,BC,81,C7,24,97,CF,CE,CC,73,26), + wwH16(6A,41,B7,5A,68,38,B8,DD,B9,9B,7D,A4,25,F1,A4,8E), + wwH16(41,23,54,AA,42,68,2D,29,0F,83,7F,0E,60,3D,F1,DB), + wwH16(B3,36,3C,99,74,A6,C8,E3,CA,0B,F6,58,EE,DA,4B,0B), + wwH16(BA,00,B3,97,04,20,9D,BD,54,29,E5,A1,FF,38,AF,B0), + wwH16(D2,B4,BE,6E,53,B3,6B,4C,09,69,AB,81,EB,2F,2C,8A), + wwH16(10,F5,7F,E9,15,79,80,C1,C5,AA,AF,5E,C8,21,14,D0), + wwH16(0D,18,BA,6F,E0,BC,53,14,A8,14,39,6D,F1,A4,C7,D2), + wwH16(BF,D4,F6,FE,89,B2,D7,9B,6C,76,4D,10,1C,6E,A0,74), + wwH16(14,B6,3D,12,BA,6B,C0,31,14,C1,80,51,73,3B,2C,E5), + wwH16(ED,E1,30,86,EC,E5,AE,C9,5B,BB,72,45,BA,BE,37,6E), + wwH16(C2,FD,A4,8E,45,1A,66,B5,53,C2,B9,4F,51,AD,F3,E2), + wwH16(F0,AE,99,BF,10,56,E3,54,8D,62,C7,E4,44,CF,57,54), + wwH16(01,BA,DB,2A,0E,2F,1D,C5,8A,33,DB,84,51,36,40,0D), + wwH16(16,88,72,20,1B,C6,49,27,90,75,D8,33,A8,45,6F,30), + wwH16(C1,A4,D8,A1,01,E3,1D,3C,16,E0,46,88,0C,1D,60,77), + wwH16(CF,43,57,75,35,0C,D1,0B,6A,85,38,15,36,43,0A,CA), + wwH16(71,64,80,5D,98,18,4F,2F,F4,0C,03,72,44,7E,47,FA), + wwH16(13,BE,A9,A3,02,02,C1,8D,4A,8B,42,32,CA,E3,CE,63), + wwH16(74,CB,5B,9A,E7,82,22,23,E4,0D,03,72,4B,14,01,04), + wwH16(C3,5D,3E,FF,73,36,97,9C,FE,3E,06,D0,3A,CA,79,F4), + wwH16(74,DA,17,1A,AE,72,FB,8D,96,94,F5,0C,47,2F,DB,B1), + wwH16(BA,17,0C,F0,89,92,7C,0C,1D,F3,B9,F6,03,EA,F3,0E), + wwH16(87,A3,F5,CD,28,83,66,76,8F,FD,2F,8E,31,76,93,CE), + wwH16(9C,63,80,E5,3E,44,B1,6B,4F,38,DF,DD,7B,12,8D,5C), + wwH16(98,DC,6F,45,2B,5D,80,E2,3B,2D,1D,8A,3F,79,A0,90), + wwH16(27,40,CD,20,CC,46,65,69,E4,BA,13,DD,20,96,DB,FE), + wwH16(BF,B9,01,5D,9A,6D,ED,96,6D,1F,42,7A,10,40,49,0A), + wwH16(C4,3F,69,9E,8A,BF,05,41,1C,4D,5A,A5,30,05,70,02), + wwH16(9B,95,D6,A3,2D,BC,1D,6B,BD,5C,18,B0,B1,37,5E,E5), + wwH16(0F,F1,C9,9D,80,E3,2A,00,8F,64,A7,19,B7,F8,B5,80), + wwH16(A8,9C,6F,9E,7B,EE,C1,01,F0,67,49,9D,F5,81,FE,4F), + wwH16(80,DD,C4,FC,98,BD,45,51,AF,D0,28,C2,75,84,EC,B8), + wwH16(BD,07,45,05,1A,C6,4D,3A,39,57,FC,B2,8B,C5,1C,D7), + wwH16(28,3E,15,23,C6,E2,20,38,6E,76,51,81,D8,CF,27,CD), + wwH16(77,C0,43,B6,85,78,AF,13,28,84,F6,6F,03,A7,5E,F6), + wwH16(75,E9,FC,40,50,A4,33,7A,AA,59,03,9A,22,A9,FF,35), + wwH16(9D,17,41,3E,C2,67,5E,89,6F,B9,07,65,C6,34,EF,2E), + wwH16(D6,CE,12,3B,34,85,A9,27,0E,AB,A0,AD,2C,C7,FD,38), + wwH16(0B,5F,1E,72,7F,35,6E,E7,88,BE,0A,37,A0,9E,FE,49), + wwH16(15,2C,C7,1E,D7,CB,1B,8C,06,D3,CA,80,86,06,04,46), + wwH16(4B,0A,4F,B3,20,E0,CB,DA,03,A5,28,6D,C9,48,0E,5E), + wwH16(1E,67,D7,EE,ED,17,A6,38,87,08,32,73,0D,44,1F,9F), + wwH16(05,D4,75,16,C6,A8,A5,5A,5F,8C,1F,10,67,63,E8,61), + wwH16(E1,69,56,ED,81,73,A9,00,BC,FB,8C,3C,97,32,23,1B), + wwH16(10,5D,D3,AD,C0,FC,46,65,E2,FF,1D,E0,1A,61,6A,56), + wwH16(B0,D7,01,43,93,23,DB,C1,11,2C,F6,1E,E5,E9,82,AB), + wwH16(FD,66,20,72,7B,5A,11,4A,23,81,A2,57,B2,62,AE,EB), + wwH16(7B,86,4B,B0,2D,9B,CF,9F,4D,FA,E8,02,76,EF,5B,14), + wwH16(F1,DB,A1,2D,10,0B,F2,F4,46,A2,9E,C1,93,A9,45,CA), + wwH16(88,F5,16,B6,CF,55,A9,1B,48,EE,A0,41,FA,AD,5F,47), + wwH16(4A,B8,C2,05,BA,43,24,FD,15,48,08,91,66,BA,B8,CC), + wwH16(0F,08,96,35,A3,7D,9E,A1,A4,22,EA,1A,91,26,4F,36), + wwH16(C0,27,FF,35,3E,5C,52,BF,72,97,7B,42,8F,2E,C2,18), + wwH16(86,46,45,C2,32,BA,18,65,11,CE,52,84,BC,FB,78,E4), + wwH16(D2,07,F6,51,17,CD,C6,25,3A,AE,18,97,74,94,95,02), + wwH16(73,26,B1,38,6E,B5,D5,07,4D,18,47,32,0C,A5,0A,98), + wwH16(BF,3E,F5,67,A8,47,84,7D,81,9F,9B,FF,C6,BC,34,90), + wwH16(11,07,68,43,7F,B5,0F,3D,13,FD,75,C2,8E,20,6B,D4), + wwH16(FB,BB,A4,E8,E1,EE,8C,EB,51,19,BF,2B,65,E8,3D,3B), + wwH16(C0,6D,DC,E3,75,75,53,07,57,FB,7E,97,9B,91,86,AF), + wwH16(27,1C,65,8C,19,EE,7C,1F,03,D0,14,76,7A,29,57,61), + wwH16(A9,0C,24,FB,FE,0D,78,BF,39,CF,EE,68,84,49,71,7E), + wwH16(18,70,51,B2,19,8F,A8,F2,0D,EB,9C,5B,0D,CC,B9,A3), + wwH16(11,34,B2,A7,74,21,9A,57,AC,81,9A,23,4F,38,CD,89), + wwH16(BC,5F,50,0B,92,E7,1F,9E,7D,74,AD,6A,F6,1B,D4,50), + wwH16(38,66,01,40,D6,30,A8,2E,2F,EC,DB,77,C8,BC,7B,79), + wwH16(6D,62,2E,B2,AF,54,30,FE,6F,7E,2A,71,CA,7D,9D,57), + wwH16(51,CB,4B,85,0E,87,FB,1F,AA,32,D9,61,AB,31,93,BC), + wwH16(60,2F,F0,14,5C,2E,09,DE,DC,91,71,1B,01,D3,1F,62), + wwH16(05,83,3D,D5,44,0F,BD,87,C1,89,2A,1B,D9,FE,59,C4), + wwH16(F0,44,28,BC,D5,65,90,B7,4E,0C,9E,46,EB,55,69,F8), + wwH16(BE,32,EB,1E,65,8B,31,2C,81,85,24,20,59,D6,57,61), + wwH16(FF,17,49,F5,BA,3D,69,B7,D5,07,92,84,C5,D2,72,11), + wwH16(75,3B,61,77,3D,38,BC,CA,91,C0,BF,F9,6A,F2,D4,62), + wwH16(A2,82,04,99,F9,12,13,40,96,53,9F,A2,BA,60,14,9C), + wwH16(A6,6B,9F,80,7F,60,04,B7,A1,20,3A,F8,0A,A8,F0,81), + wwH16(B0,32,2D,DE,95,7F,E6,4F,15,02,DA,DF,86,42,70,56), + wwH16(BB,52,C5,7B,AF,BF,4B,AD,A9,F9,B2,F0,FD,BD,AF,77), + wwH16(5F,12,57,9E,C7,30,EF,3E,ED,38,D7,4F,0E,10,97,D4), + wwH16(0E,B1,6F,00,2D,1F,13,0E,2D,C1,F3,E7,A2,94,26,08), + wwH16(D6,DA,C9,5C,34,D9,F6,79,7D,73,EE,AE,1D,8F,8C,08), + wwH16(78,34,A5,B3,59,9B,01,EF,7B,B5,E7,15,2B,EC,B6,FF), + wwH16(A6,1E,A3,E1,83,5F,5D,8F,93,60,25,02,20,06,5E,8A), + wwH16(58,FF,A1,85,78,8D,F6,86,51,2F,97,98,9B,F9,7C,C3), + wwH16(35,9B,B3,C7,C1,A9,12,95,BB,AB,4C,76,69,7F,EA,C1), + wwH16(98,BE,01,00,AE,64,0F,5F,4B,10,5A,F1,9E,D1,6B,37), + wwH16(F2,E9,E7,74,06,C7,E1,86,09,B3,D2,0C,18,6F,70,D0), + wwH16(44,74,F0,A5,AB,81,94,FF,A4,C1,DF,4B,96,E9,EA,3F), + wwH16(40,C6,A7,F2,AD,84,33,3F,1B,2A,CB,B9,54,A5,36,63), + wwH16(F5,4E,CC,40,1A,30,88,C7,91,79,D6,ED,26,F9,FF,D3), + wwH16(24,17,FC,DD,4D,AC,B8,10,A3,7B,33,41,E4,90,B6,E9), + wwH16(4E,20,AE,65,27,2A,7E,CA,A8,F8,F5,F6,EC,58,DE,25), + wwH16(68,D5,A8,F7,E4,60,18,DD,80,FC,DF,01,98,E6,44,2F), + wwH16(3A,0E,6D,F3,14,76,4A,6B,64,95,3E,A6,38,5F,22,02), + wwH16(B7,48,95,E0,20,98,7C,E5,73,7C,5F,76,4E,E1,87,64), + wwH16(93,CC,D0,58,A3,AB,3F,4B,55,B5,E0,84,5A,03,7A,E7), + wwH16(5D,97,0C,17,C2,F0,BB,FB,98,74,96,6D,3E,36,11,32), + wwH16(CC,FE,6D,1C,8C,6E,44,BD,1A,3B,5F,68,9C,CD,69,48), + wwH16(D2,4D,E5,D3,2C,E2,94,D2,00,12,32,9E,80,FD,E2,79), + wwH16(9E,5D,D3,0E,25,53,A7,51,8B,84,AE,0E,35,9B,0F,8E), + wwH16(DB,64,EC,09,C1,DD,0E,A9,7A,9D,E7,08,FB,96,DF,71), + wwH16(24,F6,E2,C2,1E,5C,B5,D5,22,DA,9B,26,D8,C1,AE,EB), + wwH16(5B,61,33,91,58,83,82,A9,D6,81,27,68,73,31,65,BA), + wwH16(B1,8F,4C,43,F9,85,7D,ED,AC,81,AB,47,4C,38,4A,1E), + wwH16(43,09,A3,96,CC,D7,84,91,49,17,C7,98,C5,D4,04,F2), + wwH16(CF,87,3F,F7,E6,D8,84,7C,E1,BA,24,73,F3,79,4A,F2), + wwH16(08,20,18,7F,DB,59,36,14,83,E1,D6,B8,EE,C8,F2,68), + wwH16(B6,00,04,11,D2,61,9C,C3,1D,FC,EA,63,87,CC,BF,4E), + wwH16(60,DD,1B,5F,7B,47,07,77,02,04,9E,90,07,84,6D,3D), + wwH16(E4,FE,8D,5F,67,6C,AE,70,86,05,12,1A,FD,89,62,C7), + wwH16(1F,77,88,1F,35,60,F8,30,88,13,C2,95,80,FC,A2,41), + wwH16(68,41,81,69,F5,1C,E7,ED,6E,11,C1,29,9C,88,C2,61), + wwH16(25,28,D6,10,23,74,1C,8B,EA,72,5F,BE,D6,79,9F,AC), + wwH16(F7,9C,BF,27,AF,31,2E,B3,A5,D5,FB,77,12,82,DB,49), + wwH16(AA,EC,0F,D6,E8,EC,1E,A6,CE,DC,3B,95,2A,7A,B0,40), + wwH16(68,B1,8F,A4,82,97,8A,C9,94,6A,2A,A4,41,58,A9,7E), + wwH16(95,7B,14,2D,05,E7,7E,7B,9E,F3,1A,AB,C0,46,B2,73), + wwH16(E7,FC,60,51,16,58,91,96,9E,D1,04,08,05,EE,09,2A), + wwH16(9C,9B,B3,8A,8A,22,1D,4F,63,A2,9A,D4,50,DB,65,DF), + wwH16(B7,CC,42,4A,91,B6,FB,96,A0,37,BA,83,10,4C,46,04), + wwH16(1D,E7,31,2B,0C,3C,0A,4C,0A,5C,1F,63,09,01,9E,4F), + wwH16(EB,93,34,4E,23,E5,79,42,92,64,7B,94,40,45,83,CD), + wwH16(4B,95,0D,7B,00,0E,33,4D,DF,8A,EC,C3,B7,DD,37,7A), + wwH16(D0,1C,DA,1A,D4,14,87,38,A2,45,19,01,29,BD,90,D4), + wwH16(6F,57,7A,B0,D2,A0,57,51,F5,A9,52,D7,BF,3F,2E,93), + wwH16(6F,30,68,D4,E5,B9,B6,DE,09,14,04,88,27,23,DF,DE), + wwH16(BF,31,2F,80,29,61,2E,AC,EC,56,02,16,10,EC,00,BD), + wwH16(5F,BE,BC,81,70,52,04,2D,DE,E5,AB,43,DB,77,0A,68), + wwH16(F3,D6,1F,81,1D,06,47,A5,AF,47,C1,4E,82,59,A5,82), + wwH16(03,0D,09,3B,54,8D,B8,C3,BD,B7,70,DB,54,13,85,E5), + wwH16(83,93,06,26,72,99,91,BE,9C,41,DD,F5,35,13,42,27), + wwH16(26,0C,2B,49,D8,FE,89,23,37,51,40,CE,85,1E,4E,66), + wwH16(17,9C,D7,E0,D3,A2,63,3F,4E,D6,84,7D,86,00,F5,24), + wwH16(66,AB,00,5D,B7,F1,37,79,F9,8F,7A,44,0A,E9,14,E7), + wwH16(17,69,0D,FC,FA,48,CA,C1,F0,93,22,F3,B5,76,F7,D9), + wwH16(78,E0,88,7D,1A,0D,F7,AF,D5,22,78,78,5C,90,DA,2A), + wwH16(09,A9,18,A5,31,3E,AE,77,05,86,5D,21,B3,3E,35,5A), + wwH16(CB,CC,02,54,AD,C6,AF,6F,B2,3F,58,42,0E,43,3B,0F), + wwH16(6A,1F,0F,24,05,50,82,44,B9,FF,31,45,99,D6,E9,EE), + wwH16(3A,50,D1,CE,43,27,18,7D,17,DA,E4,A1,12,1B,FB,64), + wwH16(04,04,40,68,C7,19,5B,78,F3,C4,C1,7A,81,2D,55,B6), + wwH16(E6,8A,97,A8,0C,EB,82,57,F9,E0,04,96,A2,35,76,A9), + wwH16(3F,6F,10,9E,48,2C,2A,F4,F4,F8,C3,B1,71,7E,23,B5), + wwH16(2A,25,51,FE,44,A3,95,13,40,A0,B5,63,94,66,5C,AB), + wwH16(C7,8B,35,7C,B7,B7,0F,63,1C,A3,B7,5E,BB,1A,BB,B2), + wwH16(4F,0B,79,4E,36,FC,42,AB,F7,C7,86,8B,B2,4E,79,F0), + wwH16(FA,60,E6,1B,F8,35,0A,AA,F3,C4,EB,6E,8D,09,65,B6), + wwH16(5D,FC,77,6C,B6,80,FA,62,AE,C9,A6,28,51,58,12,6B), + wwH16(94,72,96,E0,77,FB,67,7B,E4,DB,09,45,5C,4D,AF,02), + wwH16(25,70,35,2D,E8,FC,80,0B,3C,42,27,15,25,09,79,ED), + wwH16(36,4B,D8,95,E9,75,83,FE,30,C6,88,18,0E,BE,25,D9), + wwH16(DD,35,78,94,3D,1E,E6,9B,6F,B8,3C,BE,58,9F,B9,6E), + wwH16(FC,6E,CD,E1,18,76,F4,D2,AF,E3,DC,2F,57,43,A4,7F), + wwH16(70,E0,B7,34,30,81,65,6D,43,EE,D6,2E,AB,DE,A0,4D), + wwH16(CF,EC,9B,C6,AE,1D,68,E1,B1,8C,51,65,78,A5,C7,B4), + wwH16(5D,85,9B,1C,C1,80,1C,D8,D4,C5,95,5C,C4,AE,9C,D1), + wwH16(16,79,0D,31,81,90,19,B1,A6,8E,1D,3B,14,1F,00,6C), + wwH16(FB,04,47,04,00,FF,4B,59,EF,9A,3B,EB,93,65,77,90), + wwH16(ED,97,C9,B6,13,80,16,0B,CC,E8,32,FB,11,C0,8C,3E), + wwH16(91,73,95,1B,7A,97,26,ED,65,C1,C2,0F,D2,CC,8C,8C), + wwH16(2B,92,D4,2F,F8,FF,6C,F3,62,04,EA,4F,34,47,95,AD), + wwH16(70,27,98,3F,74,32,DA,0D,B5,9E,F9,9B,A5,7A,25,31), + wwH16(79,24,34,5E,CF,2A,86,21,14,61,A9,0C,59,D1,D1,4F), + wwH16(5A,C3,AA,51,E2,7A,2E,B9,49,31,A0,B3,2A,8F,85,5E), + wwH16(4F,5A,A7,1C,CB,D7,9F,EA,53,E8,20,A4,61,39,FE,59), + wwH16(0D,10,4E,2E,72,84,B9,CB,F4,31,8C,1F,51,83,B0,20), + wwH16(5A,23,F6,C8,CD,B3,84,B9,20,1C,BD,50,74,B7,BF,DF), + wwH16(11,77,C2,EC,A9,71,42,F1,D1,06,41,23,7A,21,B7,EF), + wwH16(8A,0A,F9,06,17,11,40,0D,63,8B,32,63,C3,C1,3F,5F), + wwH16(35,87,90,4E,84,67,EF,8A,62,F1,2C,25,8C,F2,7A,6A), + wwH16(20,6F,05,7C,59,1F,8E,39,02,81,77,A1,2D,EE,E1,44), + wwH16(A8,F9,CA,B2,D2,6B,EE,4C,44,15,5E,DC,C7,79,39,56), + wwH16(BA,A7,76,C5,EE,AF,06,2A,07,44,B1,49,ED,D1,C1,47), + wwH16(F0,9E,BE,4C,4D,F8,71,09,30,C1,26,71,7E,AC,C7,43), + wwH16(EF,E5,5E,55,C3,6B,86,05,05,C8,B1,15,27,21,47,74), + wwH16(0C,29,FE,6E,7F,A3,A7,89,AD,2D,3A,D0,22,F8,54,ED), + wwH16(38,86,50,A4,61,92,CB,15,84,FD,57,1C,1D,BB,EF,11), + wwH16(49,3B,08,70,23,9B,F1,1D,05,3E,5E,14,63,A3,80,C5), + wwH16(62,3B,DA,59,74,16,CB,7B,BF,81,DE,74,95,BD,50,FD), + wwH16(14,06,39,7E,CA,BB,AA,CA,76,7F,78,BD,84,4C,B9,CF), + wwH16(33,05,7E,C1,B7,AD,BA,3A,45,05,20,47,EB,65,73,D2), + wwH16(FD,7C,DD,51,57,40,90,DF,8E,53,86,5D,91,AF,C1,CC), + wwH16(39,A5,52,35,E7,18,57,A2,6D,46,4A,CB,F8,CD,C6,77), + wwH16(15,32,A9,8A,42,78,13,FC,DF,C0,A9,85,C8,88,55,99), + wwH16(5C,0E,6C,90,E7,4C,B8,AC,33,D2,29,4C,A2,B0,32,29), + wwH16(CE,29,3D,26,C0,1A,E6,0B,50,4D,D9,77,FC,7E,35,36), + wwH16(26,10,C9,E1,5F,3B,49,29,DA,D6,C5,0F,2C,B0,50,19), + wwH16(C2,12,0A,AA,32,BF,6A,DB,CE,77,D2,E5,21,EF,00,5E), + wwH16(73,7F,BE,4B,CA,C6,B1,CE,09,37,C5,A2,19,7A,08,F2), + wwH16(46,21,81,CB,BE,13,67,B4,AE,64,E2,AA,1B,23,0F,C1), + wwH16(AB,21,E0,52,94,25,59,39,6D,E9,AE,82,6F,85,9D,F3), + wwH16(62,A7,B6,95,AE,3B,DC,14,B9,CD,EF,0C,56,08,37,CE), + wwH16(4C,78,62,91,34,9E,3D,94,39,89,91,3A,79,FE,E6,29), + wwH16(DB,99,9B,8C,42,0D,7F,94,B7,FA,BA,B7,E7,53,9C,C0), + wwH16(B5,F3,3C,B4,73,C1,DC,A7,3B,84,32,D7,5E,25,D9,58), + wwH16(F5,BC,31,B8,F9,31,68,97,16,F6,81,69,6A,B8,F2,80), + wwH16(51,40,CA,8E,E8,54,65,07,EC,9F,EF,2B,92,1E,13,7A), + wwH16(DD,C3,0C,0C,49,DE,40,82,22,BC,66,F8,B8,39,75,1B), + wwH16(3B,77,3B,C7,35,AD,7A,60,D9,FD,C6,C9,56,5C,42,61), + wwH16(AF,59,A9,94,66,F4,14,0E,B1,D6,97,6E,4F,B5,82,4F), + wwH16(9C,A5,4F,23,E8,F5,0F,4B,5B,40,49,78,51,EA,EC,88), + wwH16(DB,DB,06,5F,ED,A4,6A,1A,95,26,47,A0,68,F1,10,BD), + wwH16(3E,10,05,2E,6F,8B,0A,C0,C7,6C,19,70,90,31,55,DE), + wwH16(A3,1E,17,A7,95,8E,53,3F,26,A1,E2,90,17,56,1E,60), + wwH16(DA,0D,37,5D,AD,59,5C,7E,EA,56,0F,07,56,C0,98,64), + wwH16(51,41,DD,3F,39,9E,52,99,43,E5,C7,D2,83,7D,EF,E5), + wwH16(15,86,5F,98,F2,4F,76,1F,FB,04,0B,F8,54,A8,CB,64), + wwH16(57,CC,EF,91,02,36,CC,75,ED,64,D5,0D,45,0D,47,7E), + wwH16(AC,55,62,31,27,42,54,17,09,D0,C7,F2,CF,06,38,65), + wwH16(FF,75,58,4B,79,5D,C9,92,F8,44,C8,EF,27,66,B2,46), + wwH16(3E,EE,43,96,3A,01,62,0B,19,7D,F7,5E,69,6A,95,3A), + wwH16(98,0D,83,5E,BE,40,77,3E,34,6A,9B,F2,E6,11,78,32), + wwH16(69,E6,38,47,68,87,7A,C5,9B,40,4F,B8,B7,EA,6E,E0), + wwH16(A8,0A,0C,56,9A,F2,85,3F,57,3D,9F,B3,19,9E,BB,5E), + wwH16(00,1B,C7,CB,E3,0C,7F,CC,62,C5,8E,F5,B6,CB,01,25), + wwH16(39,06,AF,79,55,70,1F,F9,07,22,C7,5E,AC,85,4B,20), + wwH16(7E,35,84,CF,02,DB,F2,5E,88,68,79,41,B5,75,3D,C5), + wwH16(DF,E1,B3,6E,93,56,1E,84,69,8D,E7,14,18,FC,06,E8), + wwH16(4E,66,BB,5D,41,C3,87,B1,AC,1A,FD,D7,B9,67,DB,7F), + wwH16(A2,6D,E9,1C,4D,B3,CB,DA,DF,AD,F8,4F,B2,45,C3,82), + wwH16(C9,2C,B6,22,B4,45,05,8E,25,BA,3B,74,F5,CA,61,DC), + wwH16(B7,A4,D9,C8,B5,74,BE,76,B4,F7,B2,FD,5B,2F,A6,57), + wwH16(22,F4,8B,77,05,BD,04,29,8C,E0,79,DB,EE,1B,D7,23), + wwH16(22,2C,42,E2,7E,29,73,5B,6A,D0,23,BE,65,16,98,B0), + wwH16(E3,EA,61,7C,19,1D,1D,3F,F0,39,F4,38,82,4C,BD,94), + wwH16(2F,1F,D1,F7,8E,5C,FB,8C,4D,C2,23,65,FA,89,39,87), + wwH16(EC,EF,DB,77,A5,6C,38,EE,63,CE,BD,EF,B5,AA,7C,78), + wwH16(17,33,8A,B6,B3,53,6B,CF,0D,B0,30,C4,A9,A3,CF,3C), + wwH16(44,D6,4C,3E,0C,73,D8,2C,4A,55,93,88,38,E5,47,F4), + wwH16(32,1D,56,A2,27,59,EC,9C,D4,0D,43,A3,50,85,46,0C), + wwH16(D7,10,D3,EF,CA,A2,A2,9B,E7,67,5A,F8,BF,4F,0F,85), + wwH16(C7,4A,9D,67,7F,1B,89,CE,19,18,F4,80,30,01,BE,06), + wwH16(02,15,06,B9,3E,1D,77,E4,52,A2,DE,63,FE,EC,88,5C), + wwH16(D8,CD,79,69,84,B5,A4,B5,EB,90,BE,EB,C9,F1,8E,CD), + wwH16(08,8F,D6,D7,80,6D,7E,B3,05,3E,15,59,F7,2D,7C,D1), + wwH16(82,02,6B,33,02,62,07,9B,68,32,50,4D,C7,06,0E,CA), + wwH16(7E,4A,13,2E,BF,ED,8B,1E,5C,2D,F4,92,F0,9D,D3,F3), + wwH16(48,86,04,5E,90,47,37,B5,35,7C,59,F2,4E,32,FD,E5), + wwH16(B3,BE,95,17,71,5C,BB,BF,DF,7B,65,18,39,77,AA,9E), + wwH16(B8,3F,73,A3,13,4F,03,F9,0F,97,C8,1D,1D,CF,A2,C5), + wwH16(8A,34,51,95,8D,A4,24,41,CD,96,63,48,C8,62,EA,BC), + wwH16(66,51,5B,C3,A6,84,D4,C6,9C,83,9B,27,FB,8E,4B,EA), + wwH16(3D,1D,87,8F,C1,D9,32,C0,50,25,E3,56,E1,75,D1,5F), + wwH16(9E,17,97,8B,45,D8,65,AE,29,CC,9C,1A,A2,F9,5D,D8), + wwH16(FD,53,B5,2C,60,A6,37,F2,4C,8C,F5,65,BF,75,72,D1), + wwH16(21,AB,10,9C,D4,53,23,18,8E,65,E3,A6,B0,10,3E,73), + wwH16(C9,C3,91,54,4A,E2,B1,0A,BC,05,41,3E,CE,F6,51,FF), + wwH16(7A,55,02,D1,1B,64,93,29,01,01,45,91,C8,84,0B,89), + wwH16(F3,D7,B7,A6,C2,7C,AE,5E,C9,08,16,C1,7B,DD,95,BF), + wwH16(E1,C2,D1,AD,C3,BE,B5,71,4F,59,5C,D1,15,14,26,95), + wwH16(53,9D,46,02,0E,74,6B,15,E0,52,00,8D,1B,B6,79,49), + wwH16(E9,85,45,EF,FC,A9,4B,17,D6,3D,A7,1C,98,9C,16,2C), + wwH16(E1,2A,49,E8,B5,90,29,27,34,D3,55,DE,44,5B,7B,77), + wwH16(8E,5E,2F,BC,1E,24,AD,CB,15,7E,C8,56,F4,83,13,47), + wwH16(CC,75,B2,44,3F,59,D9,90,FF,86,11,8F,37,70,DF,6A), + wwH16(8F,ED,68,1E,2E,31,2A,16,66,2E,5A,BF,DF,C3,94,2E), + wwH16(0C,B0,43,76,F4,F7,47,9D,B5,BF,6E,19,2E,E6,C4,53), + wwH16(B5,4E,3A,EA,EF,1D,E1,7C,0A,1A,D9,32,73,DA,02,5D), + wwH16(0E,03,C9,74,01,64,B1,83,E7,EA,62,17,3C,37,40,BC), + wwH16(2E,5D,5D,3E,0D,B9,78,22,C6,E8,93,9A,29,33,43,B2), + wwH16(AB,3F,0B,61,F4,16,5F,EE,C0,85,E0,AC,30,80,34,93), + wwH16(06,05,B5,07,EA,26,B2,2C,BD,C3,2D,0F,CA,5D,EF,7D), + wwH16(DC,2F,C7,58,2A,81,B0,A8,51,47,7B,01,8A,3B,48,F6), + wwH16(54,C0,86,38,0A,34,8C,C5,47,FF,69,3B,96,59,CA,77), + wwH16(41,B5,1A,94,B6,13,4E,2B,11,F4,A3,6D,E9,EE,7C,00), + wwH16(3D,BD,14,86,BC,B9,E3,29,DC,17,4F,BF,E1,89,92,19), + wwH16(B5,E7,D1,BA,E9,F9,D2,73,E9,76,3E,8F,1A,CD,0C,3D), + wwH16(32,8B,86,59,A4,BC,D5,6C,BA,FE,01,B3,F8,78,15,64), + wwH16(F6,AE,6F,A5,A3,CE,2B,F7,90,35,A9,B1,BC,87,C6,B3), + wwH16(E5,CD,C3,CF,68,51,74,2D,89,4F,5B,50,ED,88,0A,70), + wwH16(2F,B8,23,DF,6A,95,0B,32,06,55,66,00,0B,2A,D1,5B), + wwH16(17,13,09,FD,35,C2,2A,E1,4B,96,26,1E,A3,1F,C3,82), + wwH16(FC,06,55,F6,E3,FB,C1,23,40,D0,A2,B6,39,6A,EA,78), + wwH16(A8,4F,5B,A2,AB,C0,91,58,84,D7,0E,C3,C1,41,6A,06), + wwH16(40,8D,02,8A,94,51,63,93,BE,C2,C9,2C,9D,B6,03,D7), + wwH16(56,9F,4F,CF,E9,9E,B4,54,F5,D4,CE,8B,D7,F0,D7,D0), + wwH16(7E,B6,FA,21,11,D0,6C,82,8F,FF,5A,12,A4,AD,DA,A9), + wwH16(95,FE,90,D6,39,73,AE,C5,5A,77,15,1C,98,E5,04,7E), + wwH16(8C,D6,49,75,F5,CF,8F,33,13,27,8D,21,38,7D,70,F8), + wwH16(A8,04,10,37,E0,E7,69,F9,FF,2C,86,FD,C3,A2,37,08), + wwH16(0B,D7,D0,1C,5F,0B,FF,80,CA,E3,09,3F,4E,8C,8F,BB), + wwH16(23,20,6F,58,4E,B1,29,8B,0F,7B,48,D9,59,02,A7,78), + wwH16(3A,9D,4E,35,81,22,D0,05,16,4E,45,A1,6A,CD,D1,F4), + wwH16(A3,F0,D1,7B,79,19,F8,52,0E,8D,28,CB,7B,B3,36,D1), + wwH16(09,EC,7B,63,8C,77,0B,CE,CE,26,AB,12,6B,BE,AC,2D), + wwH16(96,58,90,59,D0,F4,E9,42,EF,D8,73,80,75,49,47,4B), + wwH16(33,CD,53,7C,18,DC,CD,F7,38,F2,C8,80,AD,03,B8,6B), + wwH16(E6,32,8B,17,E4,11,B6,D0,73,88,E8,21,C2,6B,5C,E1), + wwH16(02,8C,DF,01,8E,FA,97,E6,FA,9A,30,49,73,98,64,21), + wwH16(A6,E4,AA,4E,31,D5,1F,90,32,56,37,B6,4D,20,5E,D0), + wwH16(1F,33,18,85,43,FE,A5,0A,1A,6D,64,6D,CA,A3,A5,75), + wwH16(18,84,1C,3F,BF,7D,EC,CF,00,9B,0D,70,14,7E,F2,86), + wwH16(82,9F,76,2D,B7,6E,78,4E,18,C9,10,FA,F5,18,D1,2A), + wwH16(F4,4B,D2,FB,F4,81,07,C5,D0,A8,5E,0F,2B,8C,71,E5), + wwH16(2E,F5,0C,55,C2,17,E9,86,1D,21,EB,27,9E,6F,9C,74), + wwH16(88,81,DF,D1,57,95,E4,A7,09,B1,08,BC,8C,63,77,58), + wwH16(5F,FD,6D,C6,40,44,6F,16,CE,49,B9,78,FC,DD,49,B7), + wwH16(EB,D5,08,18,65,AD,15,10,84,F5,30,89,FF,65,5B,76), + wwH16(C1,0B,3C,F0,66,7E,A9,3E,06,87,92,56,EB,0C,E7,0E), + wwH16(E3,FD,4F,3F,14,2F,DC,89,74,E6,A1,EC,6A,66,E6,6A), + wwH16(03,46,F4,B5,69,9B,17,20,32,54,2A,50,EA,17,87,4E), + wwH16(C0,6A,1A,06,F8,15,46,0C,76,9F,4A,83,31,A4,45,A5), + wwH16(0F,B4,6C,3B,2A,30,FC,68,2F,BD,91,23,E1,45,91,77), + wwH16(E8,CF,EC,3D,71,1E,9A,28,D6,8B,E5,94,AA,65,59,5A), + wwH16(F7,E4,90,10,87,AD,DE,A5,CE,1E,53,55,F9,AC,3E,F7), + wwH16(B8,59,B0,B7,0D,DD,84,0B,83,F9,FE,FB,27,4F,E8,2F), + wwH16(14,3C,79,A2,E3,F1,29,01,91,FE,A0,F1,59,AB,C5,DD), + wwH16(22,85,1C,84,90,A9,32,C3,44,8A,77,55,B0,29,1F,EA), + wwH16(5E,46,0E,85,6B,B1,17,8E,A5,CA,49,7A,5C,5A,19,DA), + wwH16(CC,F1,1E,42,F8,8D,E7,8A,9A,CC,8F,EA,41,B1,BA,0B), + wwH16(DE,4A,0A,62,FD,07,6C,DE,A6,96,32,1B,BE,4B,99,C2), + wwH16(62,0D,B1,7B,66,28,9F,F9,B6,3F,E8,8C,9C,32,F5,FA), + wwH16(22,55,5B,78,65,02,8B,77,19,BA,0F,3D,CB,2A,6F,7F), + wwH16(DF,C4,48,0E,00,29,2F,7B,28,F1,A6,B1,E9,88,F4,27), + wwH16(CB,77,1C,8F,6A,69,58,97,41,9B,2E,6A,61,F0,E7,E9), + wwH16(52,8E,E0,AD,34,3C,E3,EC,7C,1C,88,9B,BD,60,37,F1), + wwH16(94,FE,94,16,B5,7A,28,8E,FE,FA,AE,F7,C9,EC,DB,49), + wwH16(54,8A,70,54,7E,C8,54,76,98,CE,9E,E0,F5,FD,90,D0), + wwH16(42,CD,A9,D7,4A,8B,BD,27,59,2B,DF,C9,17,A3,0B,A5), + wwH16(C7,26,DA,06,82,EE,E8,72,EA,8C,FB,45,3F,BB,C9,DF), + wwH16(CB,BC,2D,5B,EC,AE,1F,7B,51,20,00,D1,6C,B6,04,4E), + wwH16(7F,DB,CE,73,E0,3D,C1,E6,8F,15,96,06,B8,B4,31,59), + wwH16(F9,4A,82,EA,CF,26,0A,42,3E,07,9D,77,DC,95,DD,5F), + wwH16(8F,27,D3,71,2B,BA,CE,0A,ED,1A,37,16,CB,CB,67,CC), + wwH16(FD,CC,65,25,C8,72,F9,AF,D5,D9,22,0D,12,C5,86,67), + wwH16(DD,5B,D2,F3,D3,DA,96,C5,30,0A,09,9A,72,7A,D2,3B), + wwH16(6D,EB,B8,5C,1D,6B,FF,F2,71,0A,7D,64,FF,64,5C,BA), + wwH16(D6,91,20,CF,77,A0,48,2D,DB,05,EF,E2,73,03,8D,EF), + wwH16(6E,5E,B9,AA,64,F2,36,0C,EC,2E,98,F5,F0,EB,4C,E9), + wwH16(F5,A0,0C,AC,B3,77,C5,99,8F,F3,F7,BD,7D,D5,D9,E1), + wwH16(B0,89,3C,DF,D8,10,86,50,A0,3A,FA,9B,6B,C2,CD,42), + wwH16(0F,2C,D4,50,DC,11,4D,C9,FB,74,BD,EB,C8,3B,1D,8A), + wwH16(ED,DB,F2,75,F9,91,46,D2,BC,7A,F4,7E,07,F8,DD,15), + wwH16(84,46,F1,45,27,AC,32,CC,2A,2C,9A,7E,55,0B,25,DD), + wwH16(AC,A2,87,50,B2,7F,20,49,A5,E6,2C,77,9D,DF,C0,78), + wwH16(1D,C7,44,58,27,BA,63,74,AE,66,53,31,8E,D0,57,12), + wwH16(D5,CB,9D,03,6F,F6,ED,AD,25,D9,C5,F5,1D,9A,2E,35), + wwH16(FF,11,80,49,35,BE,2D,88,FC,BC,1A,82,2D,77,F2,8B), + wwH16(AB,43,37,4E,56,77,6A,F7,7F,28,40,85,09,FA,48,97), + wwH16(49,D9,E6,F1,58,3C,8B,9F,1B,DC,6E,F5,9F,1D,30,F1), + wwH16(78,1E,87,FF,CC,11,80,69,6A,87,B0,64,C5,D4,67,EB), + wwH16(A3,20,DE,05,BD,97,AB,56,AB,3F,DF,73,B5,46,D4,5D), + wwH16(CB,D5,8B,4A,15,4A,55,0D,8E,A3,C1,3E,78,95,AB,FB), + wwH16(70,9D,DD,1A,83,0B,76,FA,C0,F1,69,C1,31,7C,D9,F1), + wwH16(C2,E6,57,4F,77,30,97,C9,C5,46,7F,27,E4,C1,F8,E1), + wwH16(5D,B2,89,8B,D1,DF,28,36,90,D9,9C,A8,0E,0C,D7,EF), + wwH16(01,58,C4,CC,4C,7E,64,80,DB,16,E6,BD,A0,A0,00,51), + wwH16(B0,A2,AA,29,84,1E,CC,99,F8,52,14,1B,58,40,79,05), + wwH16(7F,12,8A,62,F1,F2,39,9C,0E,27,79,FB,BD,DE,8F,F3), + wwH16(95,F6,FF,A2,E5,1F,67,87,6F,48,B2,21,CA,3E,9F,02), + wwH16(5D,15,B1,97,16,CA,88,ED,50,81,9D,AC,4D,6C,C8,A5), + wwH16(1A,85,61,C3,B3,B5,B7,A0,3B,94,FD,D0,19,66,50,A1), + wwH16(D1,42,C0,C2,99,22,C1,AF,EB,93,36,5D,2A,B9,E5,98), + wwH16(AA,C1,A5,A6,09,0B,90,16,4D,3F,1F,4B,19,5E,C6,2C), + wwH16(69,0B,C9,30,6B,99,50,A6,3D,A5,F4,25,E5,4A,F4,0A), + wwH16(F4,4B,8D,C8,3B,3C,4A,5C,F3,D4,7A,8C,E6,8A,70,5C), + wwH16(FE,AD,CA,62,0A,11,D5,4F,EF,F6,04,52,54,31,73,5A), + wwH16(73,BA,DF,6D,FB,9C,43,A2,67,63,38,96,BA,8F,A1,F5), + wwH16(9E,3F,B0,D3,F4,58,8E,6C,33,58,17,C8,9B,48,26,70), + wwH16(82,4E,2B,D6,47,15,9F,9F,3B,87,9D,56,DE,4E,18,91), + wwH16(F2,14,C3,80,9D,E7,E4,2D,2B,70,DA,92,A4,BE,CE,7B), + wwH16(EA,D2,BC,EB,DE,6E,9B,56,49,E0,C2,76,0A,A5,C5,77), + wwH16(A7,AC,E0,3E,EB,EF,3D,A8,82,DD,EA,83,82,5E,A1,36), + wwH16(1F,C8,AA,08,03,18,FC,DE,D5,B7,4F,04,29,DF,92,8C), + wwH16(7B,F0,BD,45,04,5A,91,A5,62,DF,5C,B1,20,C2,0C,AA), + wwH16(8E,87,B7,56,C3,CA,63,67,50,89,AE,62,8E,00,3A,63), + wwH16(07,31,53,14,58,23,7C,A8,F9,B7,FB,27,B0,B1,D1,7B), + wwH16(7C,EA,93,A8,FD,C9,71,04,86,EA,A5,2E,4B,C0,ED,A6), + wwH16(E2,37,F7,38,91,B7,09,5C,9D,D2,73,99,76,4F,B7,BC), + wwH16(D0,EF,1F,D4,C1,1F,F4,4C,FA,CC,6E,16,B5,C3,1C,88), + wwH16(88,5A,1F,29,7D,04,CB,BF,3D,0B,41,79,5A,40,CE,CD), + wwH16(DC,41,6E,03,62,02,08,27,E6,5D,D5,4A,04,2E,1C,FA), + wwH16(3A,C3,3A,FB,AF,D9,F5,CF,CC,17,F1,6D,50,27,E7,27), + wwH16(D5,72,47,B3,DE,2F,6D,99,B3,B6,B5,99,AD,4C,91,90), + wwH16(34,21,E4,15,53,05,96,00,98,E5,50,C5,68,EA,21,C5), + wwH16(6B,25,3E,06,44,E5,61,25,68,35,0D,92,D9,CE,FB,9F), + wwH16(58,AC,DA,51,4B,92,28,A5,83,83,45,EC,E2,85,7E,A5), + wwH16(51,4A,25,6C,F4,8E,AB,1C,11,8A,EE,C2,A1,E8,19,9B), + wwH16(47,4E,B3,D6,0B,11,BC,BE,7E,E4,50,6B,68,8A,49,A3), + wwH16(07,6A,0D,55,57,6B,C7,06,E5,BE,58,6F,43,DA,D3,BC), + wwH16(92,65,69,21,ED,7B,C4,73,74,53,65,88,E6,94,97,7C), + wwH16(85,8F,F2,9F,63,90,72,EC,0B,CF,B7,21,C6,F6,B6,7A), + wwH16(DD,CF,A8,37,2D,BA,71,89,B3,A1,D4,D2,A8,AA,4D,9F), + wwH16(A4,D2,9D,82,6E,A5,47,97,4F,E8,51,F6,AF,D6,DC,4C), + wwH16(90,C3,13,FE,BA,3D,5F,71,46,F7,48,63,3C,D7,7D,AD), + wwH16(7A,1F,57,A1,49,EC,73,ED,45,25,0B,C6,2D,0B,C0,AD), + wwH16(98,2A,6D,B6,46,37,AD,09,35,F7,E2,0A,5E,7A,4F,37), + wwH16(56,5A,BC,F1,5B,BB,7A,7E,F8,22,73,60,F6,3B,6D,0A), + wwH16(77,E4,56,85,0E,EC,3D,B0,07,8F,5C,64,5F,63,04,86), + wwH16(4F,6D,04,9F,2E,97,A9,9C,C6,88,CF,6D,08,79,5C,18), + wwH16(AE,82,3B,19,15,F6,6C,2E,D4,E8,CB,1E,27,27,4A,53), + wwH16(1B,0C,F3,ED,3C,11,D2,6F,D9,F6,A2,9E,9A,1E,69,A3), + wwH16(9A,7E,D2,5B,33,C9,65,A7,2F,92,D1,50,E1,8B,DA,8D), + wwH16(D3,63,2A,FA,F7,40,08,BA,96,76,9F,74,7D,19,8F,BB), + wwH16(8A,EC,83,BE,47,DB,96,DE,63,47,12,3C,EB,59,3D,02), + wwH16(39,A2,36,A0,E5,A8,BD,3C,84,49,3C,30,63,36,DA,D0), + wwH16(91,23,72,46,C3,D4,34,D8,3D,DF,7F,9B,36,7C,EA,B9), + wwH16(4E,C0,72,1E,D6,71,EF,06,39,C9,FA,10,23,D0,D1,C8), + wwH16(E8,61,BF,BB,C9,90,68,96,6C,C6,5A,97,27,51,84,0D), + wwH16(2D,EF,60,D7,3E,69,9B,2B,34,45,61,87,9A,21,00,9C), + wwH16(BF,8F,A5,3B,7F,7F,7C,C7,CB,09,FA,2F,44,F8,E5,5E), + wwH16(9B,AD,F3,DF,92,54,E6,45,C0,B8,93,89,7E,EA,D9,49), + wwH16(6B,9F,A1,C1,4E,FB,2A,81,62,C0,14,52,F7,1D,F4,3A), + wwH16(A7,92,F5,C0,E5,5A,D5,94,A8,C8,79,B9,1D,EE,9B,9B), + wwH16(4E,ED,A6,C1,36,A3,C6,12,80,EA,17,63,54,73,A3,CD), + wwH16(B9,EA,B6,14,91,FB,25,4B,21,05,BF,34,EA,D8,5E,D2), + wwH16(9A,A6,F0,5D,E9,3A,EC,5A,66,0D,D0,91,BD,15,CE,F6), + wwH16(C0,89,D5,85,DD,34,DE,66,D3,46,17,C5,8C,68,1E,2D), + wwH16(8F,3D,25,6D,4B,0F,1F,13,51,C1,79,2C,1A,E3,BD,0B), + wwH16(0F,03,14,2D,DF,90,67,CD,47,EC,86,88,32,7B,55,3D), + wwH16(E1,1D,9D,AF,F6,59,57,D3,2E,94,2C,76,18,5F,E6,DE), + wwH16(35,02,72,14,31,0F,14,44,E5,3F,16,37,25,C7,6E,AA), + wwH16(E7,EB,59,17,29,FA,29,49,54,4A,77,89,35,D3,1D,E0), + wwH16(36,6D,BB,C8,11,25,C1,9B,C5,46,39,0C,D7,72,5D,9D), + wwH16(C1,3A,D2,DC,75,5F,F3,90,5F,60,48,A6,1A,D1,22,91), + wwH16(78,49,DE,28,35,D1,E2,48,64,15,25,25,F1,DF,27,40), + wwH16(C1,11,B5,83,85,34,FC,F5,BD,D2,7C,05,D3,C1,CB,37), + wwH16(28,2C,3E,19,69,4E,D3,EC,2F,0C,9D,57,80,4F,DC,62), + wwH16(20,73,32,3C,84,7F,12,1D,53,79,CF,B7,2C,8A,BF,F9), + wwH16(F7,4A,1E,BC,F6,B9,45,11,14,C5,4D,03,9D,25,31,3B), + wwH16(BF,80,00,50,32,05,CF,80,E6,73,3F,6C,7D,F6,20,59), + wwH16(9D,F7,37,FD,6D,3E,B0,62,EC,A2,BC,46,55,9B,8E,32), + wwH16(A0,B9,62,F0,9D,97,17,0E,A6,5E,D0,64,3B,21,E7,39), + wwH16(A0,FA,4C,CA,94,02,68,12,4D,6D,03,4E,B9,36,7D,1E), + wwH16(38,23,40,EC,E4,06,05,83,65,82,C1,63,40,CB,67,BB), + wwH16(DB,83,88,93,D8,3F,80,75,47,0E,AD,92,9D,E9,A2,4E), + wwH16(15,6E,B9,CE,13,83,2F,6F,23,D5,94,47,2F,D6,75,07), + wwH16(4F,C0,B5,03,A7,20,D9,6C,88,15,1F,6F,6D,B6,7E,C5), + wwH16(55,B3,6B,5F,6E,3E,38,BF,D5,4D,93,1E,57,E3,C5,78), + wwH16(0A,7E,52,30,87,6A,02,F1,C1,B5,6C,3C,4A,27,A0,C0), + wwH16(3D,33,64,C0,5F,88,5C,7C,F2,01,24,37,12,3A,2D,10), + wwH16(99,81,47,5D,F8,46,30,7E,35,0B,E9,94,E6,35,86,8F), + wwH16(8C,20,94,B2,61,B5,88,FA,64,26,9E,B0,C7,81,2C,5C), + wwH16(61,DD,41,F1,EF,00,2D,91,3F,98,61,8B,36,60,24,FE), + wwH16(30,6D,66,DE,5F,D2,24,42,16,D7,1E,B5,D8,1D,F1,42), + wwH16(8E,37,5B,FC,60,C0,DA,C7,90,2D,32,D2,BE,8B,1B,B0), + wwH16(10,FC,73,99,6E,A5,AB,C6,0B,B9,D0,5A,06,93,40,9E), + wwH16(6D,89,29,07,ED,23,94,8B,5B,28,AD,66,CF,8B,AE,7A), + wwH16(32,DD,17,79,27,3B,0F,D2,C2,EA,AC,12,0B,5A,70,BC), + wwH16(43,82,6A,65,31,F2,33,65,CB,45,F3,8C,6A,84,68,67), + wwH16(0B,98,05,7F,EE,0C,8C,95,B7,EB,F0,D9,4C,56,FD,A6), + wwH16(65,DD,80,5D,EF,C4,1D,60,E2,30,73,EE,FD,70,55,4D), + wwH16(9F,0A,48,9A,5F,6F,A8,2F,37,7F,0C,82,9D,FF,78,FD), + wwH16(EF,75,60,CF,F1,13,C6,20,DE,3E,61,F1,8B,49,C7,E2), + wwH16(37,82,D2,EB,26,36,40,11,9C,33,32,8B,24,2A,CB,E9), + wwH16(73,E9,08,98,EF,D8,B2,81,61,2C,C9,6D,AC,10,67,3B), + wwH16(7F,3A,4D,AE,08,81,E5,65,ED,48,E9,8E,AB,3E,9A,98), + wwH16(22,89,38,F1,40,71,DE,7B,8E,13,11,7E,CD,DE,DC,D9), + wwH16(DC,1C,DE,4C,27,B6,EF,EF,33,17,33,7D,19,96,1D,FF), + wwH16(46,C4,77,78,F4,46,D5,BF,E2,A9,D7,18,41,EC,8D,DE), + wwH16(C6,0E,62,59,2C,13,79,4B,D3,C9,06,FE,97,B3,49,F0), + wwH16(9B,34,3D,80,5A,50,BC,E8,41,2E,0F,3B,04,94,C1,FA), + wwH16(3E,2A,77,11,F0,CB,20,B8,7C,6F,32,26,BB,24,04,82), + wwH16(CD,71,24,B1,C3,E7,C0,3C,58,E1,18,4A,A2,CA,8F,78), + wwH16(87,87,24,C0,66,B3,00,62,62,DE,8F,92,FF,C6,01,A1), + wwH16(FD,FB,0D,7C,A2,77,7A,09,13,FD,A2,38,79,29,17,D7), + wwH16(58,5B,25,10,3E,00,07,85,AE,0D,F0,92,55,CD,D1,C9), + wwH16(A1,A0,33,0B,15,D0,0E,B9,9B,EC,8C,77,3C,55,37,85), + wwH16(92,44,D2,29,FB,46,5A,6F,AF,66,5F,4E,EB,8D,F5,72), + wwH16(B5,74,A0,68,83,4B,0D,7D,AF,BF,72,67,C7,58,CF,6D), + wwH16(04,D5,FE,BD,D5,F7,EA,8C,E2,1F,2B,88,63,D0,24,A2), + wwH16(5B,1F,C0,D4,F5,D9,02,87,FF,FD,93,F8,C3,BE,FC,93), + wwH16(7E,A2,6A,42,22,A1,F8,5E,36,B8,D1,4B,64,63,A8,4F), + wwH16(32,89,A0,27,AA,47,6A,CF,DA,7C,BD,04,0A,C2,7F,8A), + wwH16(71,1B,D4,D5,9D,01,F0,76,90,DD,22,AA,3E,82,A8,60), + wwH16(E2,3B,CB,B7,5F,E8,6F,F3,44,0C,31,A9,40,2A,BC,15), + wwH16(E1,8B,BF,40,08,43,2B,3E,E2,15,6A,60,77,08,53,78), + wwH16(82,50,C7,3E,25,63,45,66,BC,77,22,40,3E,48,80,84), + wwH16(23,1E,D9,29,82,AC,2E,36,33,00,72,B0,8A,52,4D,57), + wwH16(06,A7,95,0F,E8,EB,94,09,DF,20,4A,3E,B2,A3,60,EF), + wwH16(ED,20,7A,2E,F6,22,4E,80,44,E4,73,43,CB,00,03,98), + wwH16(33,E3,73,A0,01,BD,A7,8E,18,FD,D5,28,DE,44,B7,E0), + wwH16(6B,38,79,94,7E,0F,2E,33,B5,D9,F8,0F,32,E2,CC,8E), + wwH16(41,99,3D,E9,6E,91,76,71,F5,3B,70,D4,A1,B1,8C,49), + wwH16(4B,92,CE,C5,7C,F5,7A,27,B9,2F,6A,FE,FF,25,D2,28), + wwH16(71,10,4E,8F,9D,24,25,73,AB,89,DE,E9,46,B5,B5,97), + wwH16(66,F8,F7,C9,30,AC,42,47,38,FF,51,2D,E0,2C,BA,68), + wwH16(47,54,52,96,4F,4F,54,24,8A,B9,D0,21,BB,E5,78,17), + wwH16(6A,29,39,15,49,44,6A,FA,5C,07,AA,73,DC,14,41,E0), + wwH16(9B,CB,61,E6,0C,7E,C1,A4,5E,33,50,2D,D1,BE,EF,AC), + wwH16(F7,D1,9F,D6,65,A0,39,E2,62,11,C5,1D,A2,0E,E8,38), + wwH16(5D,95,BE,2B,39,03,BA,FD,4C,07,F8,B0,73,87,F5,E6), + wwH16(53,04,92,B0,98,58,9A,3E,1F,9E,12,EC,47,8A,5D,2B), + wwH16(35,9F,07,B8,0A,62,55,BE,34,03,19,E0,24,AB,0C,65), + wwH16(C5,6F,78,CF,B9,89,40,CA,90,60,A4,FF,EC,9B,06,21), + wwH16(B9,58,15,2B,13,B1,B8,A2,23,EE,CC,9C,B4,A7,29,09), + wwH16(D0,63,AC,7F,AA,F4,78,FC,CA,27,FD,68,1A,C0,1B,26), + wwH16(66,B9,35,2E,DA,82,EA,4B,70,F1,31,9D,BE,3E,DC,27), + wwH16(CE,5D,9F,EC,DC,4E,41,6E,93,9B,65,46,04,35,A7,5F), + wwH16(A2,68,E0,BD,7E,36,FA,73,5C,FE,05,BD,A9,63,A6,29), + wwH16(B8,BE,62,02,3E,8F,A5,D2,66,84,98,67,EE,BB,63,54), + wwH16(65,9D,ED,C1,8F,83,11,D3,C4,D3,75,BF,B3,C4,CD,8D), + wwH16(0F,2C,DD,66,62,86,F1,F2,1F,AE,5E,EB,04,8C,79,B6), + wwH16(C5,CD,B9,CF,B1,77,F8,1A,EF,2E,E8,AE,84,F0,EC,AD), + wwH16(51,0C,6D,CB,03,CD,BE,52,63,EB,76,C6,B8,80,22,90), + wwH16(80,99,EC,C5,DF,CF,FF,89,05,96,98,7E,91,C5,02,96), + wwH16(F3,97,5C,C0,75,6F,DC,2E,00,E9,9B,AD,9C,2A,11,F2), + wwH16(37,F6,65,14,9F,EA,24,EB,B4,C6,C2,EC,59,84,97,67), + wwH16(67,20,62,50,5B,44,C5,71,E7,61,D9,71,C3,1E,83,E8), + wwH16(53,60,4F,F5,37,EF,48,C7,0E,48,3B,8F,A2,5C,27,A9), + wwH16(D3,DD,CA,DD,6E,19,5F,BA,FF,1A,8A,5E,F1,A8,E7,FE), + wwH16(F4,C3,B5,E0,07,59,C0,14,97,B0,E1,E5,09,12,A2,2A), + wwH16(6A,92,BE,D8,C6,A7,C6,16,31,2D,10,86,47,16,3E,9A), + wwH16(A0,95,C7,49,7F,58,6F,E5,68,4C,DE,F6,A1,1E,A5,9E), + wwH16(4A,BE,EF,35,31,B4,E2,04,60,00,82,E8,AD,F0,0C,CE), + wwH16(EC,21,2A,4D,F9,32,2E,F6,21,C0,60,ED,52,BB,6B,6A), + wwH16(54,A3,83,F0,73,E8,E7,23,46,CA,DC,96,08,94,F4,81), + wwH16(56,FF,92,C8,C7,FB,EC,60,76,6C,41,7A,58,8C,4A,02), + wwH16(45,BE,A0,1E,B8,90,4C,3C,7A,0A,AD,25,F0,AB,FD,E7), + wwH16(9D,55,35,A7,BE,7F,17,E7,BC,C2,9B,55,3C,25,CE,AF), + wwH16(4A,09,90,52,38,18,29,96,DF,BF,03,65,CA,E1,2E,25), + wwH16(48,8A,49,45,A2,BC,A7,29,C2,7D,A2,4C,B4,2C,3A,23), + wwH16(18,F9,D7,3E,C1,B8,BA,C5,F3,0C,BB,BB,02,7F,55,7C), + wwH16(E9,5D,71,CE,E8,DB,BF,E9,87,97,3C,F1,A7,54,48,44), + wwH16(1F,D6,68,C5,B7,4B,74,E1,D7,CF,FA,93,A9,BB,79,C5), + wwH16(05,13,F0,A0,32,5D,58,4E,AC,33,8F,83,B4,AF,96,C5), + wwH16(B8,2D,29,25,78,AB,03,C8,96,EC,B8,41,BD,F4,92,A0), + wwH16(E7,9F,41,DF,5C,E2,2F,20,27,04,FD,B1,5C,48,5C,89), + wwH16(E8,E7,6F,78,18,16,BE,7C,B0,37,E3,9A,66,AD,48,1F), + wwH16(E7,9E,C6,9C,4E,70,68,91,E6,BC,21,F7,6D,7F,87,75), + wwH16(46,52,7D,E2,C0,79,61,2E,CE,66,20,D4,B4,E5,4C,0B), + wwH16(D2,91,8D,E9,7F,B6,11,D2,AE,BC,FF,51,95,14,18,DA), + wwH16(A1,0C,8E,B1,42,33,10,6C,22,30,C8,6E,95,31,D6,E4), + wwH16(28,1F,49,AB,BA,F7,4B,57,D9,B9,AD,94,0B,57,EC,3F), + wwH16(95,89,9A,F6,A8,37,96,DD,BF,81,B1,FF,83,DE,38,07), + wwH16(38,B7,A5,F6,D5,75,EC,0F,7C,32,50,3A,F8,3C,97,78), + wwH16(A8,B9,20,4F,BC,3E,84,BD,61,DE,75,94,34,01,CB,D9), + wwH16(A1,ED,EE,04,D8,40,83,65,2D,17,61,3F,AE,1D,EC,F7), + wwH16(98,F0,6E,AF,43,0F,31,94,6A,28,D8,11,3F,83,5A,8D), + wwH16(A5,1D,B4,1F,F4,3D,85,B9,87,98,1A,2C,C1,95,59,6F), + wwH16(2E,9B,5A,B5,25,B1,0B,C2,25,3A,31,53,C2,0B,F1,CF), + wwH16(22,28,12,3D,22,1F,18,83,FD,44,AA,92,93,E4,2C,57), + wwH16(F6,CC,F1,BC,15,40,08,4B,E5,F5,7F,88,F1,BB,17,72), + wwH16(9B,CE,8E,CA,27,00,5F,A3,B2,D7,30,74,26,8D,1E,15), + wwH16(E0,42,9E,AD,C8,1D,11,8A,30,BE,AB,14,2E,24,3D,1B), + wwH16(53,C1,D4,3B,34,61,79,EB,EC,C4,A3,79,EE,E1,6C,E8), + wwH16(F4,23,34,D4,B3,DF,7E,3C,1B,86,87,33,9C,9C,AA,DF), + wwH16(20,DA,F5,AC,F9,22,FB,5B,37,BC,42,02,A3,8B,95,00), + wwH16(B3,D5,CE,42,DD,55,67,34,F1,5D,70,74,6F,26,8F,1B), + wwH16(2D,15,87,8C,6B,07,DE,2E,08,90,3E,21,B7,4F,2A,FA), + wwH16(FA,1D,BE,4E,12,D4,0C,21,22,F7,5D,AE,0A,50,5C,10), + wwH16(5F,59,B0,11,D4,7C,1A,92,7E,AA,8C,10,7D,46,01,6B), + wwH16(28,38,F7,A4,BF,DF,46,F5,61,DA,5E,F2,FF,36,CE,0C), + wwH16(70,E5,8D,D4,AD,B9,D1,80,E9,75,81,8C,E9,E2,A1,B2), + wwH16(96,5C,4B,1D,A1,D6,CE,8C,26,6D,03,AF,8D,C7,FF,35), + wwH16(B9,17,A0,D4,A1,E0,55,A5,5B,4B,E1,A3,54,DA,DD,E8), + wwH16(85,15,DB,45,1A,79,09,7B,2B,E7,BB,99,7C,92,49,C6), + wwH16(04,94,6B,87,79,89,79,69,71,8E,99,31,86,E4,B1,85), + wwH16(59,3F,38,62,01,80,BF,01,94,AA,70,C6,DE,81,25,7C), + wwH16(D3,8A,CB,76,4A,E0,31,74,66,D8,9E,3D,E1,F9,CF,3E), + wwH16(F0,E6,1B,44,DE,94,D8,87,AB,E0,8B,F7,94,0A,F8,15), + wwH16(B2,10,8D,3F,9E,6C,7F,A0,01,B0,26,18,D8,B8,77,1A), + wwH16(E8,F0,24,B3,9E,81,3F,EB,34,E0,8E,91,67,EE,28,D8), + wwH16(59,A6,A3,4C,78,E9,8F,3A,60,CD,F1,B0,57,1C,DA,62), + wwH16(6A,50,01,59,61,1A,0A,89,91,11,4A,BC,F0,87,F5,16), + wwH16(EC,84,DC,6F,FB,1D,D6,7C,49,35,76,60,73,31,28,6E), + wwH16(A5,04,87,79,DC,20,25,FA,29,EF,27,4D,82,B4,1F,14), + wwH16(99,0B,22,8B,BE,34,5D,CF,88,18,52,9E,5D,98,78,1B), + wwH16(E4,37,97,B7,EC,18,42,F9,17,93,7E,77,F9,DD,CF,F1), + wwH16(89,67,24,49,52,19,A0,0A,02,48,11,C9,5C,D5,B9,83), + wwH16(0E,F2,1D,31,9B,4B,7B,28,7C,5B,64,CF,80,54,E6,78), + wwH16(CA,B0,D9,77,9B,87,38,B7,2A,A8,FD,C6,11,31,ED,25), + wwH16(CE,51,5F,1F,7C,D7,ED,FB,72,A2,A1,49,B1,BB,64,42), + wwH16(F9,F9,E3,A5,D7,C6,A1,C4,28,2A,C4,32,51,4D,42,D6), + wwH16(28,37,C1,81,85,1A,C7,21,62,57,41,2B,98,DB,55,7D), + wwH16(A8,38,D9,BE,D4,9F,A3,37,18,C8,B7,8C,A1,A3,E0,89), + wwH16(EF,AB,DE,86,CA,EF,16,C8,8D,01,B2,18,35,D7,25,E1), + wwH16(7C,5B,27,14,B8,FE,F9,A1,FA,D6,85,16,DF,E9,B2,59), + wwH16(4B,22,2A,E2,E3,8B,2A,FB,6C,1B,14,69,04,A2,E4,91), + wwH16(B7,ED,C9,43,D1,51,80,B8,B3,7E,29,EB,73,39,E6,F1), + wwH16(FC,A6,0B,0D,E9,43,AD,01,DC,FA,D6,9A,29,DF,69,4A), + wwH16(C2,55,D2,A4,F6,37,33,FA,8B,4F,72,62,0C,A8,09,18), + wwH16(42,9C,AF,9F,D6,2F,A1,65,5C,1E,97,56,09,E1,0E,62), + wwH16(D7,F3,B9,5C,B9,70,B6,93,04,F3,18,AD,AC,E8,D8,8D), + wwH16(A8,33,52,00,E0,07,5B,0D,D6,44,9F,67,4F,F4,A9,A0), + wwH16(E7,62,6A,56,12,C8,74,28,C5,75,DE,41,C4,D8,08,21), + wwH16(EB,8B,11,5E,52,4E,F3,C7,D7,3D,E0,04,3F,E9,FC,86), + wwH16(67,B2,93,1D,13,3A,6C,34,CF,5E,BB,A4,F6,C0,12,10), + wwH16(BE,95,C8,6C,C5,F1,84,F4,F7,F2,01,97,7E,05,04,85), + wwH16(F5,E4,08,1E,08,D0,8A,FE,23,EE,08,C0,28,FD,A3,74), + wwH16(97,E4,EA,C7,EE,96,31,57,8E,83,CA,FF,B3,D3,40,64), + wwH16(49,48,55,E9,F6,09,48,34,95,73,61,3B,15,95,79,1F), + wwH16(AE,5D,15,CD,9F,83,95,7D,38,78,55,F5,01,46,D6,60), + wwH16(5F,44,B8,0C,56,CB,29,63,6A,71,00,59,CA,04,FB,FF), + wwH16(C0,D6,33,21,4A,F3,C0,BF,B7,BE,59,EC,D0,39,8E,10), + wwH16(93,82,8D,20,41,90,A6,2F,1B,1B,F1,82,7D,C4,49,22), + wwH16(86,9F,EA,90,2C,40,0D,67,31,F4,89,73,3F,D7,97,1E), + wwH16(B0,EA,DC,E6,01,4E,B4,82,72,64,94,4D,90,B7,86,DD), + wwH16(48,E0,17,80,18,2D,A4,E4,91,0B,D1,FC,2E,4F,C6,3E), + wwH16(22,F2,8C,54,DF,50,5E,7A,0E,C3,2F,4B,97,B1,FD,4F), + wwH16(C2,E2,C8,4F,13,0F,25,1A,C1,93,02,F4,9D,59,44,55), + wwH16(CD,F0,34,DA,20,9B,38,C0,5E,22,9B,F0,EE,5D,17,5F), + wwH16(2C,EE,D7,DF,9A,36,41,3A,89,0C,4B,99,38,05,83,85), + wwH16(D5,86,3B,D9,06,29,F6,4F,7F,96,41,92,1D,BC,70,19), + wwH16(DE,5B,AF,47,80,03,4A,7D,6F,C6,D2,E3,BC,23,0F,3C), + wwH16(53,54,15,4C,00,DE,31,69,9D,A3,FC,5E,AD,99,6F,21), + wwH16(01,0C,85,03,96,4A,A8,03,2B,69,ED,CD,41,F9,41,43), + wwH16(5F,CB,E8,16,31,F7,6F,11,E6,8F,3A,39,6D,7B,B5,FF), + wwH16(79,EF,F9,69,12,6E,56,6A,5A,CF,C4,29,F2,79,83,E5), + wwH16(51,34,D0,3E,25,94,A5,85,05,56,1F,04,2F,B8,A6,52), + wwH16(BB,B6,7A,79,DE,AD,76,49,5A,17,88,3A,46,AE,92,99), + wwH16(67,2E,57,78,CF,8A,71,76,F0,AF,15,75,15,5A,A9,9A), + wwH16(9F,46,DF,4F,7E,83,96,43,D0,B6,9B,B8,64,A5,4C,16), + wwH16(39,B9,79,93,77,77,9E,B8,F3,70,3A,9F,F0,86,B0,AF), + wwH16(69,99,15,38,01,EE,78,39,58,F3,E2,08,C6,0A,D1,97), + wwH16(E4,D8,50,A1,C3,35,B7,E9,50,B7,44,A8,11,AD,6F,7A), + wwH16(19,34,0F,52,70,E6,F3,55,34,59,67,04,4D,FE,D5,FE), + wwH16(62,35,D3,68,8B,0C,5C,DA,A6,40,52,DE,0B,D0,7E,47), + wwH16(74,82,EF,48,D1,1F,23,BF,B0,BE,40,A7,F0,01,AE,3F), + wwH16(97,C5,09,53,69,75,6E,73,CB,57,B3,60,1F,5B,95,3B), + wwH16(8A,56,A8,93,17,BA,95,60,1A,4F,5A,57,45,23,FE,85), + wwH16(35,AB,B9,6E,7B,E4,4F,4A,60,2E,24,79,4C,5E,25,A2), + wwH16(D0,8F,14,7C,CA,F2,0C,B1,84,D5,15,10,77,26,B5,79), + wwH16(B7,55,67,36,BE,D2,D2,E2,88,7C,6B,CD,C6,B6,FB,2C), + wwH16(04,E1,3C,21,81,F0,96,C0,51,ED,7D,7D,7D,7B,F5,C5), + wwH16(B6,76,63,25,44,CA,B2,5D,36,E6,61,34,64,ED,40,6B), + wwH16(D1,62,B7,9E,6A,12,A6,F0,0B,D2,1D,46,62,88,2F,68), + wwH16(15,1E,8C,B3,CE,E8,2B,27,5B,57,4F,33,1D,64,59,F7), + wwH16(09,BF,20,AC,63,F9,C4,24,E3,FB,AD,13,D5,A1,9D,52), + wwH16(59,8D,77,8B,0B,53,A2,93,D2,5B,04,86,55,A0,5F,E0), + wwH16(24,46,E6,A6,92,5A,48,60,4C,41,80,7D,B2,6D,45,4E), + wwH16(8F,CE,78,B1,05,AC,D1,BE,1E,E0,6B,0D,D0,D3,67,D7), + wwH16(9B,87,81,2B,AE,58,13,11,4D,DC,D6,DB,54,1F,94,C8), + wwH16(F0,84,D8,E1,F1,C6,27,F3,77,2B,52,51,03,A6,5C,1C), + wwH16(E1,29,40,39,3D,AC,87,9B,12,D7,E9,B8,6E,7A,98,26), + wwH16(0F,7D,51,1E,5B,66,A6,82,6D,28,FF,18,2A,6A,68,42), + wwH16(F5,93,AA,D3,3A,82,A5,79,3E,21,69,F2,E5,D9,3A,00), + wwH16(7F,D1,83,91,4F,54,90,60,34,86,A5,C0,89,37,C4,B8), + wwH16(47,0E,06,56,26,0C,68,56,27,06,CD,AD,39,1B,17,63), + wwH16(D0,FF,0B,E9,FB,1D,2C,EA,48,96,0E,DD,DB,2F,4E,D8), + wwH16(64,C9,52,57,59,BA,D5,0D,82,AF,33,08,BB,57,58,0C), + wwH16(33,2E,C8,6E,BF,12,62,14,B2,10,19,C3,A5,80,41,BE), + wwH16(33,49,DD,79,A4,3A,9C,6F,61,8B,3A,97,23,2C,0B,06), + wwH16(4A,41,4B,D2,07,9D,6D,54,DA,59,AC,E8,BA,59,91,A2), + wwH16(12,53,F7,AE,92,25,73,1C,CF,52,1C,9B,A3,43,09,5E), + wwH16(1A,F7,03,3C,AA,23,75,FC,1E,48,81,3D,15,CA,63,D9), + wwH16(5F,8B,65,DB,21,DD,15,D1,5D,EC,CE,48,A5,2A,A3,D4), + wwH16(DB,BC,AE,AF,A3,7F,E6,C3,A3,3F,75,BC,1E,A1,E1,5C), + wwH16(72,C1,7D,7B,1F,7F,25,D7,A1,2A,C3,BE,8C,97,A4,2B), + wwH16(5A,47,43,87,59,42,45,DD,19,C8,AC,0E,44,1F,FC,2A), + wwH16(78,97,F6,D7,59,D9,5E,BD,D8,B6,99,FF,EA,74,33,FA), + wwH16(9B,BF,1A,06,52,08,55,FF,65,D1,36,E5,73,D1,E7,B8), + wwH16(ED,B2,A3,A3,69,AF,09,31,63,71,0A,6E,73,8D,0F,5A), + wwH16(DF,C4,E9,C6,24,F2,96,37,D5,B8,BF,E3,CF,12,28,33), + wwH16(8D,CC,F3,42,8F,AF,14,E6,6B,8F,C3,D9,1E,AD,C6,10), + wwH16(DF,0B,7D,4F,36,6A,50,37,D7,2F,2C,AF,27,0F,12,29), + wwH16(5B,11,A0,EC,A9,13,6F,67,55,92,66,B3,AE,15,AC,50), + wwH16(45,73,29,6A,8F,CB,33,A1,31,38,9E,A2,60,7C,C7,70), + wwH16(1B,B8,58,81,1D,19,A1,9B,1C,64,91,88,65,1A,40,94), + wwH16(D3,A8,79,E3,C2,58,91,D3,0F,28,3E,85,A9,88,D5,3D), + wwH16(EC,82,6E,69,ED,8A,7E,FA,00,DE,D5,DB,5A,39,6B,3A), + wwH16(83,7C,9C,92,B1,EB,5E,EC,36,84,85,AD,5F,72,2C,E8), + wwH16(99,9D,C0,29,E1,82,70,EC,60,3D,D5,7C,7F,05,7B,A8), + wwH16(42,86,70,5E,F9,79,FA,E3,6D,07,79,00,84,91,D8,A6), + wwH16(82,B9,C8,7B,62,7C,30,46,DA,0D,29,C1,14,FB,4C,F8), + wwH16(DB,E5,74,BD,9E,7F,11,B2,24,45,B3,74,FB,32,CE,90), + wwH16(34,7D,E9,59,3B,50,60,0A,53,75,3F,54,1A,FE,0A,2A), + wwH16(5F,80,C4,50,D7,6E,91,49,50,72,E7,4D,71,93,C6,AC), + wwH16(FE,1D,F2,FB,D6,DA,93,1A,38,02,DE,BA,E3,18,BD,0C), + wwH16(FB,96,34,6B,49,06,4F,CC,AC,CF,51,51,7C,03,C9,CA), + wwH16(EF,15,C3,74,5E,FF,F8,B5,0E,95,66,E7,45,77,B2,C5), + wwH16(B6,75,E2,CC,35,38,AF,5C,30,3B,A6,4A,D6,B0,C6,CC), + wwH16(A5,C8,F3,11,55,8C,74,00,AF,4E,42,9F,F9,44,56,FB), + wwH16(1E,32,EF,68,21,60,C4,07,62,C3,BA,41,E8,DD,15,B9), + wwH16(A4,76,53,18,FD,6E,B7,CB,42,55,14,44,8C,DE,0A,C4), + wwH16(ED,46,0A,FF,64,88,BD,4F,13,8A,1F,D6,87,D1,9E,7E), + wwH16(DA,C5,7F,4C,19,5E,1B,8B,98,40,4E,F3,60,B5,88,37), + wwH16(F3,FB,7F,C7,03,EA,68,82,A0,93,92,B8,3A,A8,37,32), + wwH16(D5,32,F2,93,FA,E8,FF,AA,8E,99,B2,B1,64,D1,43,0B), + wwH16(DA,C6,66,85,3E,42,A9,2C,68,77,EE,61,CD,FB,CA,E9), + wwH16(80,97,C3,82,E1,FB,DD,E8,50,B6,C2,FB,5A,50,95,AD), + wwH16(31,86,72,4E,C1,DB,31,5C,A8,0E,C5,E1,BA,88,AE,52), + wwH16(0B,8F,39,06,F4,60,0A,3B,65,D9,F4,F2,DD,B7,E4,03), + wwH16(FD,42,9D,72,DE,89,D8,56,F5,B2,AE,7C,EF,9F,54,3C), + wwH16(FA,03,9B,1D,02,F5,BB,BE,1D,47,0D,49,E8,F4,CC,BE), + wwH16(9D,2E,A9,9C,5A,BE,03,DC,94,A0,1C,44,52,95,12,7A), + wwH16(1D,D2,95,A7,AA,66,8D,A7,3A,CF,9A,86,1D,62,52,5D), + wwH16(B1,72,4C,C7,23,08,4A,9D,09,D7,E4,50,03,EF,20,65), + wwH16(66,25,A9,00,31,8B,83,F5,A0,7E,05,F6,BB,AA,81,92), + wwH16(61,C5,01,73,60,3F,7F,69,E8,C1,A1,94,9E,71,47,53), + wwH16(EB,94,25,45,E8,4F,9B,26,15,DC,71,9D,91,FB,BD,F5), + wwH16(F1,1D,C0,1A,45,F2,A6,FA,62,AC,26,07,FF,A9,49,E3), + wwH16(84,86,F2,68,C5,C2,28,BC,9D,5B,95,D4,EC,A4,77,97), + wwH16(AA,A5,23,A3,AE,5C,AE,E4,88,07,FB,65,97,06,AB,5D), + wwH16(C7,B9,98,A6,C8,58,37,48,57,3C,5D,02,5C,AA,DA,E8), + wwH16(B6,B9,E1,F6,F2,71,B5,C6,16,32,B9,BB,C5,A8,F4,4F), + wwH16(EA,CF,76,FC,AF,22,94,B3,61,41,BC,96,ED,ED,0D,6F), + wwH16(75,16,A1,2D,AA,05,80,34,FC,FE,70,5C,7A,06,40,6A), + wwH16(D7,90,BE,08,86,B7,47,75,34,22,80,E3,0B,EF,47,1F), + wwH16(5A,A9,75,29,55,0C,E9,F9,B9,A8,7A,92,86,C2,A8,54), + wwH16(68,B6,41,17,B9,6F,0D,77,B2,FE,2D,01,9D,13,F5,12), + wwH16(8B,19,91,55,2C,7F,EC,8C,74,CA,6B,2C,73,F1,58,32), + wwH16(95,E0,F7,07,39,FC,0A,F7,9B,52,88,1E,DF,D0,A4,B4), + wwH16(9F,2D,10,AC,07,56,6B,58,40,34,1E,32,83,7F,7C,FF), + wwH16(7A,54,AE,02,C8,60,A0,75,C8,FA,67,F7,B8,4D,0C,F4), + wwH16(0A,D5,D7,56,A9,DC,16,EE,36,08,80,65,D9,4A,64,FC), + wwH16(4A,87,CD,43,6D,B6,BB,D6,73,95,FA,5F,10,68,CC,B3), + wwH16(61,FD,DE,33,5F,28,4E,F8,78,0E,70,8E,F8,2B,64,6B), + wwH16(EB,B3,A2,9A,C9,42,7E,7B,6F,36,8A,23,6E,92,33,58), + wwH16(86,FC,1A,09,75,F4,A4,C2,25,01,F2,08,0F,36,7E,27), + wwH16(B8,9F,7D,C8,DC,72,2A,62,8B,06,90,12,EE,91,0A,4D), + wwH16(1E,C3,60,EE,A4,56,10,15,F6,0B,52,0A,A4,09,CD,A1), + wwH16(0D,33,FD,91,8E,BB,B4,F4,5C,E9,63,B7,51,81,AD,9B), + wwH16(4B,97,7B,82,80,D3,B7,D7,57,CF,70,31,53,36,FA,8B), + wwH16(61,75,FF,F6,48,8B,69,5E,3E,4D,4C,E2,DB,83,22,A2), + wwH16(19,3F,79,E7,CD,8B,1C,A3,DB,3E,A3,ED,6B,F6,8F,67), + wwH16(FF,EC,46,F4,B5,33,A6,DD,06,E6,B2,F6,8C,8C,D1,5E), + wwH16(F5,1B,2F,6D,F2,35,04,3A,8B,D8,F3,5F,73,60,70,3E), + wwH16(20,0E,35,3D,8C,D8,95,95,01,D4,6E,27,10,DD,AD,3C), + wwH16(D9,90,DC,01,DB,5F,07,6B,12,75,3C,E2,95,57,1E,DD), + wwH16(99,E1,29,DF,00,35,04,A0,B0,CF,1E,A5,2E,21,A7,28), + wwH16(82,84,13,18,4C,1D,9A,AD,B9,35,F3,C4,65,8B,6C,3A), + wwH16(0D,B2,6B,B1,34,90,02,69,60,F0,06,38,83,C5,40,44), + wwH16(F4,65,1A,11,42,6C,38,86,2C,E7,FD,F4,B1,4D,5D,6B), + wwH16(85,B9,76,67,6A,CC,C1,39,BA,A4,27,A6,C0,19,14,27), + wwH16(D1,99,D0,7E,21,5E,9E,2B,FE,DA,2C,42,47,65,97,B6), + wwH16(5D,9D,8F,12,4C,B8,40,FF,76,4E,59,7E,23,5F,FE,28), + wwH16(CF,7B,7F,C1,08,C2,6E,42,CF,6A,46,43,30,37,90,81), + wwH16(B2,9A,2D,77,B4,03,E0,54,1A,F6,1C,B9,2C,A1,CC,69), + wwH16(A6,6E,34,64,5A,C2,63,8A,52,88,39,49,A0,46,64,76), + wwH16(B8,FE,9C,32,DF,8C,2F,8E,F9,F4,C1,42,B7,90,7C,84), + wwH16(51,D1,4C,5B,17,4E,3D,D4,57,A2,A6,54,DE,D5,B7,A1), + wwH16(DB,66,3B,AD,12,09,1A,02,C8,20,9E,8D,24,70,42,D5), + wwH16(C9,E5,B8,A4,C2,52,29,35,22,08,80,B7,63,5F,00,62), + wwH16(00,79,5D,DA,6E,4A,DC,91,BC,8B,7F,C7,18,49,19,FD), + wwH16(64,4B,4C,3E,F7,6E,C3,22,9C,13,40,46,50,F5,BE,33), + wwH16(E0,FA,E4,B0,BB,A2,08,85,64,04,C0,5F,7F,83,8D,98), + wwH16(2B,15,41,4A,89,13,FC,70,4E,9F,EF,F9,1B,85,0C,1E), + wwH16(2E,84,4B,41,F7,88,C4,2F,86,DE,60,AA,73,85,56,55), + wwH16(BF,9B,C4,CA,92,3D,07,EB,61,89,FA,B9,42,29,37,30), + wwH16(66,27,81,62,E4,F4,C6,5A,63,56,8B,37,DF,FC,CE,61), + wwH16(C9,34,71,01,A0,C6,1B,34,F6,3F,8C,64,18,4E,06,45), + wwH16(3C,D3,B4,9C,6E,6F,DB,9E,4D,7B,F0,70,A0,10,5C,83), + wwH16(49,06,F3,BA,C5,22,B8,B8,A6,80,39,F2,E0,EA,00,67), + wwH16(45,0E,73,D0,AC,C3,B6,73,ED,B7,28,DE,A8,23,CD,19), + wwH16(FD,6E,62,58,D2,B0,D0,27,14,FD,B6,BA,56,7E,AE,AC), + wwH16(EC,55,B4,86,7E,15,DE,BE,F2,4C,81,45,CA,D7,BF,5B), + wwH16(45,02,50,45,00,97,0F,FB,3D,5B,88,C7,60,22,61,F4), + wwH16(BF,F0,33,90,06,C4,5E,EE,44,76,5D,B6,D0,F3,CB,B1), + wwH16(6C,38,DB,FE,B8,A1,91,31,E2,CA,81,2C,A7,91,6C,20), + wwH16(ED,3F,01,55,7B,D7,A6,0D,86,53,2D,B5,58,FD,61,33), + wwH16(42,8F,21,0F,4A,79,DD,31,92,5C,D2,E7,D4,3C,BE,D9), + wwH16(23,8F,4B,46,66,9D,19,01,54,6F,4D,2B,D4,A6,0A,E3), + wwH16(4A,AE,53,F9,A4,AA,71,59,0D,B6,0D,96,D4,B0,D5,EC), + wwH16(79,75,5A,1F,FC,18,95,19,BF,5C,73,73,C3,E8,64,86), + wwH16(62,F8,ED,E1,86,B3,E9,36,13,08,C2,19,12,5E,B5,17), + wwH16(12,D6,E8,48,9E,43,29,8A,7D,04,96,D7,5D,E0,1A,7B), + wwH16(CC,FA,BA,35,3E,5F,75,A4,AF,B1,5F,5E,C6,D8,82,3A), + wwH16(07,98,11,F1,C1,78,2B,D2,C6,BF,17,C9,B7,79,74,E9), + wwH16(2D,48,06,96,0A,31,30,54,7F,EF,27,00,3B,9E,87,5A), + wwH16(B8,DF,2D,2F,EF,67,95,F7,AB,80,70,49,20,56,4F,6A), + wwH16(E8,BB,A0,82,98,2A,A0,07,8A,40,D8,7D,2F,CB,A4,EC), + wwH16(43,13,2C,B1,41,1A,83,40,8A,91,E0,DA,2A,4D,A5,08), + wwH16(3F,F8,8E,76,5A,8F,6E,32,41,09,12,47,2C,0D,D2,69), + wwH16(00,19,F7,29,D9,A7,81,60,E3,79,AF,6D,02,40,CA,A5), + wwH16(E3,30,FF,CD,96,AC,0B,F5,1E,D2,FD,A8,B5,EF,6F,46), + wwH16(1D,BB,23,C5,8A,A9,E3,12,3C,D6,13,15,2F,BF,FF,46), + wwH16(DD,8D,1B,7E,2F,C8,1E,1F,F0,54,E3,7B,2D,EC,9D,B3), + wwH16(7C,48,7A,32,F2,C6,3C,27,7A,F7,32,08,EE,D2,D7,C4), + wwH16(2C,57,DA,90,94,F6,2D,5A,F3,61,73,47,4B,C5,58,12), + wwH16(95,22,4A,6C,F8,18,58,20,C6,29,0F,A3,F3,6E,7D,3C), + wwH16(11,B0,60,34,92,AD,2B,E4,B6,9D,DA,CA,46,99,8F,22), + wwH16(EE,04,8F,6D,D5,66,DE,ED,2D,30,3E,F6,58,FD,81,01), + wwH16(B4,18,A1,F9,5D,2D,AA,00,37,F3,91,02,DB,93,7E,C7), + wwH16(EE,02,10,CD,6E,1D,DA,40,4B,67,45,53,1E,88,94,A1), + wwH16(9F,0E,FE,F9,BE,6C,F9,64,81,A6,B1,8D,14,03,0A,E0), + wwH16(FB,39,58,E7,8E,BC,B9,6F,00,C8,D9,8A,C2,75,E8,14), + wwH16(9D,73,47,BF,84,70,07,97,88,A4,51,E1,2A,A8,86,67), + wwH16(7B,04,D5,CB,E2,F1,2E,13,AF,FE,AE,BE,1F,5C,C4,7A), + wwH16(59,8E,37,3E,35,C0,99,F9,62,F2,71,86,6D,C8,DB,9F), + wwH16(E2,69,D0,B1,D3,82,96,FF,E0,C4,AF,B4,1C,1E,32,98), + wwH16(90,60,D7,53,BC,D3,B3,82,98,85,E2,94,36,85,CE,5E), + wwH16(3B,08,36,01,FC,AC,2B,DB,C5,DA,61,67,24,F4,86,6B), + wwH16(6E,45,B9,7C,52,F1,96,F7,87,C9,D0,C9,CC,E4,1D,4A), + wwH16(E1,6B,68,4F,09,ED,83,67,AA,DE,BB,EE,27,7B,96,E6), + wwH16(18,82,D9,FC,74,56,CE,3F,9B,FB,06,26,B5,6C,A2,A2), + wwH16(D4,24,E6,FF,E2,42,12,B2,E9,3A,FF,6F,64,61,48,13), + wwH16(2B,5A,FA,D7,E7,03,9E,99,50,74,13,15,2B,25,E5,BF), + wwH16(5B,7E,C5,46,66,9F,E6,47,70,E2,12,5E,9C,10,87,03), + wwH16(10,89,AE,F6,89,00,F7,C2,F3,37,DE,9A,F7,AE,B0,0B), + wwH16(F4,E7,AC,CE,A9,3B,A1,4F,2C,25,60,0A,18,02,04,6C), + wwH16(7A,EC,5A,72,1A,BC,78,8B,B1,79,05,61,40,AD,EF,71), + wwH16(2B,1B,90,CA,9D,C5,EB,FA,E0,1D,E8,79,5C,B1,2D,37), + wwH16(E5,04,C8,07,B4,78,D1,BE,0B,42,65,A3,D2,C5,F5,D8), + wwH16(A5,26,95,E2,AE,DE,7C,EA,B1,43,35,C5,C9,3D,58,41), + wwH16(6A,69,D4,9C,C7,FE,0A,23,6D,66,F5,FD,A5,9A,E8,92), + wwH16(ED,DC,A0,75,7B,8B,AF,F4,28,4C,A2,69,50,9E,78,C0), + wwH16(DA,3E,B5,A4,68,73,A7,68,D7,18,62,A8,CD,55,F4,ED), + wwH16(62,18,72,C6,49,24,0C,FA,83,B6,8A,B2,C2,E9,0A,B5), + wwH16(7A,BB,74,FB,50,7A,94,94,93,7D,BE,B2,5F,1A,B2,CD), + wwH16(44,11,C2,BB,F5,AD,A6,D5,B0,64,AD,7F,64,E7,0E,5E), + wwH16(DC,DE,B3,64,3B,34,0C,D1,20,47,BA,F0,3E,FD,32,73), + wwH16(CC,F2,83,7A,28,4A,4C,51,0A,56,9D,21,B8,50,AF,9F), + wwH16(4E,16,EC,F3,D3,D4,BB,87,CA,07,78,34,FC,63,9E,DA), + wwH16(3A,C1,F1,F4,7A,FC,8F,E7,C4,7A,A2,4B,76,CD,13,59), + wwH16(F7,85,83,13,BE,88,39,11,63,FB,E6,E9,73,95,CD,1F), + wwH16(65,53,47,70,60,47,D7,59,34,B5,96,D0,0B,35,62,29), + wwH16(DA,D3,6E,F3,DF,31,1A,7F,9E,53,24,EA,F7,82,F5,A9), + wwH16(CD,B7,19,E9,EC,FB,9B,99,31,65,B7,3E,27,18,E4,C6), + wwH16(A8,0E,3A,C0,33,2A,6C,25,06,48,EC,09,91,DB,67,B4), + wwH16(D6,9B,97,DC,7F,75,90,9A,BB,AF,44,C1,0C,C2,CC,FB), + wwH16(85,C2,61,62,71,8E,0E,2E,99,11,32,C2,4B,0F,02,CA), + wwH16(24,EB,26,32,5E,78,8B,53,C4,1C,B5,DE,68,28,8C,CE), + wwH16(08,AA,5C,5A,4E,1D,6B,10,2B,3C,6F,4A,0F,B4,F4,DF), + wwH16(3E,3E,BD,23,7C,AC,97,E7,F6,B5,49,24,EA,89,07,8F), + wwH16(48,C2,2A,9A,37,EA,B6,E4,1B,81,ED,0D,3C,2E,23,4C), + wwH16(84,A1,53,DF,7B,7C,66,AA,B6,6C,3B,08,2E,D3,54,5A), + wwH16(5D,32,5A,82,EC,64,3C,90,FC,53,6A,39,D5,A6,1A,BD), + wwH16(17,05,04,62,F7,8F,6D,9F,B6,94,47,95,26,3D,22,D4), + wwH16(10,45,EB,69,F4,86,02,AB,71,5D,F8,5A,0E,B4,B1,AA), + wwH16(31,42,0A,CF,59,AC,E5,D5,BD,4D,98,96,C2,56,94,62), + wwH16(14,33,E1,19,C5,5E,31,17,67,B5,54,BB,B5,B3,9D,EC), + wwH16(FB,D1,6F,48,03,5E,44,5C,10,CF,3C,4A,61,4A,8B,13), + wwH16(C0,23,8F,0F,8B,44,FF,3D,89,5C,DD,DF,60,D9,0B,84), + wwH16(4A,88,F4,AE,98,E0,2E,26,A1,31,6A,D4,2E,56,4C,85), + wwH16(4A,8C,2B,FF,F9,14,EB,B8,F7,F3,A7,22,75,F7,D0,6F), + wwH16(3B,85,E7,4B,A2,A9,6A,D4,A2,8B,93,71,7B,18,67,71), + wwH16(07,74,A7,32,41,4A,43,D4,8E,C7,51,8E,3B,AD,CF,E0), + wwH16(16,DD,96,2B,3E,10,B8,EF,09,D8,A3,4A,FA,0A,4A,68), + wwH16(6E,9E,60,D9,70,BB,B5,53,64,BA,C4,C4,15,CE,CB,F9), + wwH16(3A,7B,B7,62,80,8C,35,FB,74,80,C5,47,3B,98,E8,C4), + wwH16(7F,96,DA,D3,FA,04,FC,33,64,EF,FB,32,60,A0,CC,CC), + wwH16(89,45,8B,1C,C7,BF,F7,60,FF,FE,BE,B4,02,A4,AC,2F), + wwH16(80,0C,1D,8E,D1,F8,21,BA,27,FF,5F,45,7A,45,48,19), + wwH16(D8,37,4B,95,F1,35,68,83,6F,24,FC,B9,87,45,42,5D), + wwH16(01,CE,A9,39,9D,E6,5F,FD,6B,9C,B8,0B,16,D3,23,19), + wwH16(B4,A0,C1,60,B9,DB,41,CC,B7,23,56,3A,B9,0C,6E,2F), + wwH16(FD,6B,B2,B1,86,1B,83,27,39,7E,7F,6C,58,BC,68,7E), + wwH16(87,D4,1C,F3,0A,BC,98,1F,2F,74,B4,3D,EF,D1,58,40), + wwH16(03,1B,F1,A8,D7,70,DE,C8,61,92,8D,2C,76,15,72,E2), + wwH16(FC,4D,3A,14,78,DC,1E,DD,3E,D7,A0,00,1F,CD,7C,50), + wwH16(CF,E1,41,71,C0,C7,43,8B,EC,39,0F,10,39,11,E5,CE), + wwH16(9B,24,50,B2,B9,67,DB,56,B2,E9,07,69,43,A8,79,5A), + wwH16(E7,F8,CB,86,48,24,C7,87,70,5B,9A,06,5E,2A,EC,92), + wwH16(13,2E,30,C3,A3,73,AC,FF,57,58,71,2A,ED,5B,E6,9A), + wwH16(73,89,D6,52,00,5E,5C,97,EA,B1,94,D3,8F,E3,02,A8), + wwH16(46,F5,BB,49,2B,09,0F,BE,2E,5D,73,7C,44,3C,39,83), + wwH16(4C,3F,A0,69,07,CC,9B,99,BD,59,66,8C,59,B5,06,FF), + wwH16(1D,B0,68,E5,F8,2D,3B,B2,75,1D,DA,83,35,56,09,AC), + wwH16(34,1F,B6,95,25,02,06,5A,20,F4,F0,BB,76,C6,0F,89), + wwH16(A2,1F,3F,07,A1,CE,1B,EB,2A,CA,FF,6A,E4,B2,5D,45), + wwH16(50,B6,3E,39,61,B5,E0,07,68,B6,72,F1,67,27,80,B4), + wwH16(E7,3E,59,F4,E7,5D,12,D2,B1,5B,40,54,60,D4,85,88), + wwH16(F7,ED,93,4B,D2,D7,29,98,DA,FB,84,64,A6,84,FD,22), + wwH16(55,76,3E,48,37,1F,9F,AB,4B,FD,EE,78,41,6F,7B,1C), + wwH16(F9,0F,49,2B,D4,BC,9B,4D,08,37,A0,B1,7F,71,F9,76), + wwH16(96,46,93,63,0A,7F,E0,40,F8,22,C7,A7,ED,02,55,BD), + wwH16(57,86,D8,86,C2,FB,79,12,54,BF,C2,89,E9,F0,3C,EA), + wwH16(EC,CA,A4,AC,7C,4F,11,4B,7F,C5,18,69,BD,A4,75,0F), + wwH16(32,5D,16,6A,BD,9E,60,1E,3A,B0,51,01,4B,4C,91,C9), + wwH16(80,60,1E,04,18,EB,C6,40,6B,DD,E2,A6,12,C1,2E,D9), + wwH16(1E,C1,A3,18,31,90,05,8B,71,94,87,48,A5,5F,67,56), + wwH16(1A,D7,4D,41,0F,31,EC,AC,F4,8A,2B,1E,09,57,A6,49), + wwH16(09,5C,B7,38,B2,B7,43,A1,A8,43,9D,F7,11,57,53,61), + wwH16(8B,45,EB,31,7C,D4,4C,05,13,D5,2D,3F,1C,8E,D1,68), + wwH16(23,92,01,B7,3E,B0,01,30,4A,1C,EC,47,5C,E6,91,A4), + wwH16(38,E2,3A,CA,90,1D,B0,71,E7,15,1F,7D,97,32,60,AF), + wwH16(F7,80,4D,41,3B,7B,B2,93,7D,38,D3,29,EC,51,E3,D0), + wwH16(AE,C5,39,F8,E9,7A,1E,B1,1C,B3,7B,35,42,95,DC,2A), + wwH16(DC,5E,E3,19,50,22,A5,1F,B6,20,0B,55,3C,93,07,F0), + wwH16(B4,40,09,23,0B,C0,16,B7,34,5E,4F,A8,0A,42,90,FD), + wwH16(02,A5,B9,F4,C6,90,A1,88,C6,B7,A4,E0,A5,BD,A8,16), + wwH16(6F,AD,F0,BA,B7,97,41,9B,7C,50,53,ED,1D,76,0A,08), + wwH16(3C,FF,3A,28,06,A5,3F,59,1D,35,82,C8,06,8D,86,90), + wwH16(2E,A8,7A,0B,21,8B,1A,79,99,96,4B,A4,0D,D8,F8,87), + wwH16(B3,A4,8C,F1,2F,7B,A3,30,0E,B4,5C,DB,0A,27,A0,C4), + wwH16(81,ED,3B,09,C9,EE,73,E6,F5,5E,4C,6C,90,08,5C,71), + wwH16(FF,C0,A2,BC,F0,AD,7E,2C,8B,6C,D4,06,B3,E0,A5,C2), + wwH16(D7,AD,5B,69,22,D9,C0,1F,5B,2A,25,16,F9,21,95,3B), + wwH16(BC,A1,61,04,69,9B,32,C6,91,F2,3C,7A,19,94,97,82), + wwH16(05,16,2A,0A,CB,E0,21,DD,EF,B8,4E,02,64,A6,EC,24), + wwH16(AC,34,25,96,10,1F,46,61,E1,E5,9F,66,63,20,DC,4E), + wwH16(14,DB,B2,A4,C2,F1,F2,47,07,50,60,71,28,8B,4E,1D), + wwH16(2F,E3,22,97,0A,50,D3,EF,15,DC,78,51,60,42,EC,7E), + wwH16(DB,94,58,4B,5B,A7,DD,66,9B,CD,DD,11,04,DD,90,C2), + wwH16(B6,21,43,0F,6F,12,D6,7E,E9,D9,81,44,E8,35,3A,E2), + wwH16(9E,94,D7,5E,F7,58,22,34,C8,C4,C7,84,3D,75,B8,F6), + wwH16(09,A3,8D,B8,9E,17,58,C9,EA,5C,D3,8C,FC,73,32,66), + wwH16(C1,2B,26,08,07,69,6A,75,C8,3A,E4,9C,CD,25,79,90), + wwH16(29,5E,C8,47,86,E7,A8,5F,CA,C5,16,78,B7,0C,3A,89), + wwH16(21,67,E0,17,5B,30,25,A9,DD,04,98,39,7F,62,03,09), + wwH16(CE,85,53,DD,49,A3,3A,FB,CD,F1,C9,77,36,60,E1,10), + wwH16(89,9B,C2,BF,86,78,B9,24,49,C1,E9,FA,C9,F0,18,EB), + wwH16(DC,CF,0B,17,3C,38,C3,C6,55,CA,E7,61,EB,34,10,48), + wwH16(BE,FD,06,F5,7D,46,EF,36,59,5E,27,AB,FC,27,93,AC), + wwH16(42,38,AC,FC,F6,CC,EF,A6,EC,8F,54,D2,78,3D,EB,44), + wwH16(39,AA,34,03,8B,78,6F,F4,F2,08,1C,E7,7D,BA,52,71), + wwH16(1C,73,A2,5B,62,83,FD,33,E2,1F,E0,E4,E9,50,C4,A0), + wwH16(36,53,73,F2,58,2B,24,3B,1B,36,68,FF,B5,A2,45,AD), + wwH16(0F,34,A5,3A,E5,79,BB,C5,E6,C1,14,05,AC,A7,E6,03), + wwH16(CF,E3,70,5E,48,68,9C,33,B5,86,25,69,07,23,BB,53), + wwH16(54,42,F7,2F,68,51,56,B4,FA,44,28,D9,6E,D4,DB,87), + wwH16(DB,82,8F,CC,BF,0B,EA,DA,46,69,20,32,4B,52,DD,09), + wwH16(C7,4F,98,72,F8,A6,0C,F7,9C,E1,F7,F6,FB,9E,7C,9D), + wwH16(9B,5E,CA,0E,BC,F8,03,78,E4,46,E6,78,FA,3B,B6,67), + wwH16(1E,6C,24,F6,D5,2A,42,75,AC,0D,62,19,51,9E,46,0A), + wwH16(AD,26,71,81,BC,F2,C0,D1,70,A0,C6,2F,DF,C5,A6,AA), + wwH16(26,2F,89,03,2C,51,0B,34,0E,A7,56,B4,08,00,E5,D4), + wwH16(73,EC,50,84,E0,16,15,1B,1B,05,57,EE,34,C0,B6,55), + wwH16(C9,94,65,87,6D,D9,A6,C5,8D,F0,19,50,E0,C3,DF,52), + wwH16(F5,4A,C4,BA,E1,30,0A,FF,AB,92,EA,F3,33,03,8F,E9), + wwH16(A2,32,09,1D,D9,6D,A8,A2,3A,00,37,BD,99,A5,F8,62), + wwH16(46,A2,83,9D,55,74,26,29,70,4D,A2,98,49,44,E7,36), + wwH16(D6,86,DF,AD,56,B3,96,DF,55,35,DE,FA,45,55,73,78), + wwH16(E4,25,B2,42,DC,A2,33,7F,E6,59,5F,6B,E4,AD,6D,F9), +}}; + diff --git a/src/crypto/bpki.c b/src/crypto/bpki.c index 3faf23e1..33e22ab6 100644 --- a/src/crypto/bpki.c +++ b/src/crypto/bpki.c @@ -4,7 +4,7 @@ \brief STB 34.101.78 (bpki): PKI helpers \project bee2 [cryptographic library] \created 2021.04.03 -\version 2025.09.15 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -17,7 +17,7 @@ #include "bee2/core/rng.h" #include "bee2/core/util.h" #include "bee2/crypto/belt.h" -#include "bee2/crypto/bign.h" +#include "bee2/crypto/bign128.h" #include "bee2/crypto/bpki.h" /* @@ -48,7 +48,6 @@ do {\ ******************************************************************************* */ -static const char oid_belt_hash[] = "1.2.112.0.2.0.34.101.31.81"; static const char oid_bign_pubkey[] = "1.2.112.0.2.0.34.101.45.2.1"; static const char oid_bign_curve192v1[] = "1.2.112.0.2.0.34.101.45.3.0"; static const char oid_bign_curve256v1[] = "1.2.112.0.2.0.34.101.45.3.1"; @@ -622,9 +621,6 @@ err_t bpkiCSRRewrap(octet csr[], size_t csr_len, const octet privkey[], err_t code; size_t count; bpki_csr_info_t ci[1]; - bign_params params[1]; - octet oid_der[16]; - size_t oid_len = sizeof(oid_der); octet* hash; // входной контроль if (privkey_len != 32) @@ -635,26 +631,20 @@ err_t bpkiCSRRewrap(octet csr[], size_t csr_len, const octet privkey[], count = bpkiCSRDec(ci, csr, csr_len); if (count == SIZE_MAX || count != csr_len) return ERR_BAD_FORMAT; - // загрузить стандартные параметры - code = bignParamsStd(params, oid_bign_curve256v1); - ERR_CALL_CHECK(code); // сгенерировать открытый ключ - code = bignPubkeyCalc(csr + ci->pubkey_offset, params, privkey); + code = bign128PubkeyCalc(csr + ci->pubkey_offset, privkey); ERR_CALL_CHECK(code); // получить случайные числа if (rngIsValid()) rngStepR(csr + ci->sig_offset, 48, 0); - // кодировать идентификатор алгоритма хэширования - code = bignOidToDER(oid_der, &oid_len, oid_belt_hash); - ERR_CALL_CHECK(code); // хэшировать if (!(hash = blobCreate(32))) return ERR_OUTOFMEMORY; code = beltHash(hash, csr + ci->body_offset, ci->body_len); ERR_CALL_HANDLE(code, blobClose(hash)); // подписать - code = bignSign2(csr + ci->sig_offset, params, oid_der, oid_len, - hash, privkey, csr + ci->sig_offset, 48); + code = bign128Sign2(csr + ci->sig_offset, hash, privkey, + csr + ci->sig_offset, 48); // завершить blobClose(hash); return code; @@ -666,9 +656,6 @@ err_t bpkiCSRUnwrap(octet pubkey[], size_t* pubkey_len, const octet csr[], err_t code; size_t count; bpki_csr_info_t ci[1]; - bign_params params[1]; - octet oid_der[16]; - size_t oid_len = sizeof(oid_der); octet* hash; // входной контроль if (!memIsValid(csr, csr_len)) @@ -677,20 +664,13 @@ err_t bpkiCSRUnwrap(octet pubkey[], size_t* pubkey_len, const octet csr[], count = bpkiCSRDec(ci, csr, csr_len); if (count == SIZE_MAX || count != csr_len) return ERR_BAD_FORMAT; - // загрузить стандартные параметры - code = bignParamsStd(params, oid_bign_curve256v1); - ERR_CALL_CHECK(code); - // кодировать идентификатор алгоритма хэширования - code = bignOidToDER(oid_der, &oid_len, oid_belt_hash); - ERR_CALL_CHECK(code); // хэшировать if (!(hash = blobCreate(32))) return ERR_OUTOFMEMORY; code = beltHash(hash, csr + ci->body_offset, ci->body_len); ERR_CALL_HANDLE(code, blobClose(hash)); // проверить подпись - code = bignVerify(params, oid_der, oid_len, hash, csr + ci->sig_offset, - csr + ci->pubkey_offset); + code = bign128Verify(hash, csr + ci->sig_offset, csr + ci->pubkey_offset); blobClose(hash); ERR_CALL_CHECK(code); // завершить diff --git a/src/crypto/btok/btok_bauth.c b/src/crypto/btok/btok_bauth.c index 85f658e3..2d717f68 100644 --- a/src/crypto/btok/btok_bauth.c +++ b/src/crypto/btok/btok_bauth.c @@ -4,7 +4,7 @@ \brief STB 34.101.79 (btok): BAUTH protocol \project bee2 [cryptographic library] \created 2022.02.22 -\version 2025.10.22 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -319,10 +319,10 @@ err_t btokBAuthCTStep2(octet out[], const bake_cert* certt, void* state) s->settings->rng_state)) return ERR_BAD_RNG; // Vct <- uct G - if (!ecMulA(Vct, s->ec->base, s->ec, s->u, n, stack)) + if (!bignMulBase(Vct, s->ec, s->u, stack)) return ERR_BAD_PARAMS; // K <- uct Qt - if (!ecMulA(K, Qt, s->ec, s->u, n, stack)) + if (!bignMulA(K, Qt, s->ec, s->u, stack)) return ERR_BAD_PARAMS; // сохранить ecX(Vct) qrTo(s->V, ecX(Vct), s->ec->f, stack); @@ -340,9 +340,10 @@ static size_t btokBAuthCTStep2_deep(size_t n, size_t f_deep, size_t ec_d, { return memSliceSize( btokBAuthCTStep2_local(n), - utilMax(2, + utilMax(3, f_deep, - ecMulA_deep(n, ec_d, ec_deep, n)), + bignMulBase_deep(n, ec_d, ec_deep), + bignMulA_deep(n, ec_d, ec_deep)), SIZE_MAX); } @@ -382,7 +383,7 @@ err_t btokBAuthTStep3(octet out[], const octet in[], void* state) !ecpIsOnA(s->Vct, s->ec, stack)) return ERR_BAD_POINT; // K <- dt Vct - if (!ecMulA(K, s->Vct, s->ec, s->d, n, stack)) + if (!bignMulA(K, s->Vct, s->ec, s->d, stack)) return ERR_BAD_PARAMS; qrTo((octet*)K, ecX(K), s->ec->f, stack); // Rct <- belt-keyunwrap(Zct, 0^16, K) @@ -440,7 +441,7 @@ static size_t btokBAuthTStep3_deep(size_t n, size_t f_deep, size_t ec_d, btokBAuthTStep3_local(n, O_OF_W(n)), utilMax(5, f_deep, - ecMulA_deep(n, ec_d, ec_deep, n), + bignMulA_deep(n, ec_d, ec_deep), beltHash_keep(), beltKRP_keep(), beltMAC_keep()), @@ -545,9 +546,8 @@ static size_t btokBAuthCTStep4_deep(size_t n, size_t f_deep, size_t ec_d, { return memSliceSize( btokBAuthCTStep4_local(n), - utilMax(5, + utilMax(4, f_deep, - ecMulA_deep(n, ec_d, ec_deep, n), beltHash_keep(), beltKRP_keep(), beltMAC_keep()), diff --git a/src/crypto/btok/btok_cvc.c b/src/crypto/btok/btok_cvc.c index decf7ee8..3e642453 100644 --- a/src/crypto/btok/btok_cvc.c +++ b/src/crypto/btok/btok_cvc.c @@ -4,7 +4,7 @@ \brief STB 34.101.79 (btok): CV certificates \project bee2 [cryptographic library] \created 2022.07.04 -\version 2025.09.15 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -20,7 +20,9 @@ #include "bee2/core/util.h" #include "bee2/crypto/bash.h" #include "bee2/crypto/belt.h" -#include "bee2/crypto/bign.h" +#include "bee2/crypto/bign128.h" +#include "bee2/crypto/bign192.h" +#include "bee2/crypto/bign256.h" #include "bee2/crypto/bign96.h" #include "bee2/crypto/btok.h" @@ -41,81 +43,67 @@ static const char oid_esign_auth_ext[] = "1.2.112.0.2.0.34.101.79.8.1"; ******************************************************************************* */ -static err_t btokParamsStd(bign_params* params, size_t privkey_len) +static err_t btokPubkeyCalc(octet pubkey[], const octet privkey[], + size_t privkey_len) { switch (privkey_len) { - case 24: - return bign96ParamsStd(params, "1.2.112.0.2.0.34.101.45.3.0"); case 32: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1"); + return bign128PubkeyCalc(pubkey, privkey); case 48: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2"); + return bign192PubkeyCalc(pubkey, privkey); case 64: - return bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.3"); + return bign256PubkeyCalc(pubkey, privkey); + case 24: + return bign96PubkeyCalc(pubkey, privkey); } return ERR_BAD_INPUT; } -static err_t btokPubkeyCalc(octet pubkey[], const octet privkey[], - size_t privkey_len) -{ - err_t code; - bign_params params[1]; - // загрузить параметры - code = btokParamsStd(params, privkey_len); - ERR_CALL_CHECK(code); - // вычислить ключ - return privkey_len == 24 ? bign96PubkeyCalc(pubkey, params, privkey) : - bignPubkeyCalc(pubkey, params, privkey); -} - static err_t btokPubkeyVal(const octet pubkey[], size_t pubkey_len) { - err_t code; - bign_params params[1]; - // входной контроль - if (pubkey_len % 2) - return ERR_BAD_INPUT; - // загрузить параметры - code = btokParamsStd(params, pubkey_len / 2); - ERR_CALL_CHECK(code); - // проверить ключ - return pubkey_len == 48 ? bign96PubkeyVal(params, pubkey) : - bignPubkeyVal(params, pubkey); + switch (pubkey_len) + { + case 64: + return bign128PubkeyVal(pubkey); + case 96: + return bign192PubkeyVal(pubkey); + case 128: + return bign256PubkeyVal(pubkey); + case 48: + return bign96PubkeyVal(pubkey); + } + return ERR_BAD_INPUT; } static err_t btokKeypairVal(const octet privkey[], size_t privkey_len, const octet pubkey[], size_t pubkey_len) { - err_t code; - bign_params params[1]; - // входной еонтроль if (pubkey_len != 2 * privkey_len) return ERR_BAD_KEYPAIR; - // загрузить параметры - code = btokParamsStd(params, privkey_len); - ERR_CALL_CHECK(code); - // проверить пару ключей - return privkey_len == 24 ? bign96KeypairVal(params, privkey, pubkey) : - bignKeypairVal(params, privkey, pubkey); + switch (pubkey_len) + { + case 64: + return bign128KeypairVal(privkey, pubkey); + case 96: + return bign192KeypairVal(privkey, pubkey); + case 128: + return bign256KeypairVal(privkey, pubkey); + case 48: + return bign96KeypairVal(privkey, pubkey); + } + return ERR_BAD_INPUT; } static err_t btokSign(octet sig[], const void* buf, size_t count, const octet privkey[], size_t privkey_len) { err_t code; - bign_params params[1]; - octet oid_der[16]; - size_t oid_len = sizeof(oid_der); void* state; octet* hash; /* [privkey_len] */ octet* t; /* [privkey_len] */ void* hash_state; /* [beltHash_keep() || bashHash_keep()] */ size_t t_len; - // загрузить параметры - code = btokParamsStd(params, privkey_len); - ERR_CALL_CHECK(code); // создать состояние state = blobCreate2( privkey_len, @@ -131,19 +119,12 @@ static err_t btokSign(octet sig[], const void* buf, size_t count, beltHashStart(hash_state); beltHashStepH(buf, count, hash_state); beltHashStepG2(hash, privkey_len, hash_state); - code = bignOidToDER(oid_der, &oid_len, "1.2.112.0.2.0.34.101.31.81"); - ERR_CALL_HANDLE(code, blobClose(state)); - ASSERT(oid_len == 11); } else { bashHashStart(hash_state, privkey_len * 4); bashHashStepH(buf, count, hash_state); bashHashStepG(hash, privkey_len, hash_state); - code = bignOidToDER(oid_der, &oid_len, privkey_len == 48 ? - "1.2.112.0.2.0.34.101.77.12" : "1.2.112.0.2.0.34.101.77.13"); - ERR_CALL_HANDLE(code, blobClose(state)); - ASSERT(oid_len == 11); } // получить случайные числа if (rngIsValid()) @@ -151,12 +132,23 @@ static err_t btokSign(octet sig[], const void* buf, size_t count, else t_len = 0; // подписать - if (privkey_len == 24) - code = bign96Sign2(sig, params, oid_der, oid_len, hash, privkey, - t, t_len); - else - code = bignSign2(sig, params, oid_der, oid_len, hash, privkey, - t, t_len); + switch (privkey_len) + { + case 32: + code = bign128Sign2(sig, hash, privkey, t, t_len); + break; + case 48: + code = bign192Sign2(sig, hash, privkey, t, t_len); + break; + case 64: + code = bign256Sign2(sig, hash, privkey, t, t_len); + break; + case 24: + code = bign96Sign2(sig, hash, privkey, t, t_len); + break; + default: + code = ERR_BAD_INPUT; + } // завершить blobClose(state); return code; @@ -166,17 +158,11 @@ static err_t btokVerify(const void* buf, size_t count, const octet sig[], const octet pubkey[], size_t pubkey_len) { err_t code; - bign_params params[1]; - octet oid_der[16]; - size_t oid_len = sizeof(oid_der); void* state; octet* hash; /* [pubkey_len / 2] */ void* hash_state; /* [beltHash_keep() || bashHash_keep()] */ - // входной контроль - if (pubkey_len % 2) - return ERR_BAD_INPUT; - // загрузить параметры - code = btokParamsStd(params, pubkey_len / 2); + // проверить открытый ключ + code = btokPubkeyVal(pubkey, pubkey_len); ERR_CALL_CHECK(code); // создать состояние state = blobCreate2( @@ -192,31 +178,28 @@ static err_t btokVerify(const void* buf, size_t count, const octet sig[], beltHashStart(hash_state); beltHashStepH(buf, count, hash_state); beltHashStepG2(hash, pubkey_len / 2, hash_state); - code = bignOidToDER(oid_der, &oid_len, "1.2.112.0.2.0.34.101.31.81"); - ERR_CALL_HANDLE(code, blobClose(state)); - ASSERT(oid_len == 11); } else { bashHashStart(hash_state, pubkey_len * 2); bashHashStepH(buf, count, hash_state); bashHashStepG(hash, pubkey_len / 2, hash_state); - code = bignOidToDER(oid_der, &oid_len, pubkey_len == 96 ? - "1.2.112.0.2.0.34.101.77.12" : "1.2.112.0.2.0.34.101.77.13"); - ERR_CALL_HANDLE(code, blobClose(state)); - ASSERT(oid_len == 11); } // проверить открытый ключ - if (pubkey_len == 48) - code = bign96PubkeyVal(params, pubkey); - else - code = bignPubkeyVal(params, pubkey); - ERR_CALL_HANDLE(code, blobClose(state)); - // проверить подпись - if (pubkey_len == 48) - code = bign96Verify(params, oid_der, oid_len, hash, sig, pubkey); - else - code = bignVerify(params, oid_der, oid_len, hash, sig, pubkey); + switch (pubkey_len) + { + case 64: + code = bign128Verify(hash, sig, pubkey); + break; + case 96: + code = bign192Verify(hash, sig, pubkey); + break; + case 128: + code = bign256Verify(hash, sig, pubkey); + break; + case 48: + code = bign96Verify(hash, sig, pubkey); + } // завершить blobClose(state); return code; diff --git a/test/crypto/bign96_test.c b/test/crypto/bign96_test.c index 0ccd7e38..66ff066e 100644 --- a/test/crypto/bign96_test.c +++ b/test/crypto/bign96_test.c @@ -4,7 +4,7 @@ \brief Tests for bign96 signatures \project bee2/test \created 2021.01.20 -\version 2025.10.01 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -83,9 +83,6 @@ static void brngCTRXStepR(void* buf, size_t count, void* stack) bool_t bign96Test() { - bign_params params[1]; - octet oid_der[128]; - size_t oid_len; octet privkey[24]; octet pubkey[48]; octet hash[32]; @@ -94,19 +91,10 @@ bool_t bign96Test() // подготовить память if (sizeof(state) < brngCTRX_keep()) return FALSE; - // проверить параметры - if (bign96ParamsStd(params, "1.2.112.0.2.0.34.101.45.3.0") != ERR_OK || - bign96ParamsVal(params) != ERR_OK) - return FALSE; - // идентификатор объекта - oid_len = sizeof(oid_der); - if (bignOidToDER(oid_der, &oid_len, "1.2.112.0.2.0.34.101.31.81") - != ERR_OK || oid_len != 11) - return FALSE; // инициализировать ГПСЧ brngCTRXStart(beltH() + 128, beltH() + 128 + 64, beltH(), 8 * 32, state); // управление ключами - if (bign96KeypairGen(privkey, pubkey, params, brngCTRXStepR, state) != ERR_OK) + if (bign96KeypairGen(privkey, pubkey, brngCTRXStepR, state) != ERR_OK) return FALSE; if (!hexEq(privkey, "B1E1CDDFCF5DD7BA278390F292EEB72B" @@ -116,11 +104,11 @@ bool_t bign96Test() "C6F2CE13B710F1172D2C962F53D13115" "DE14E56D9EB2628C9A884F668059EEA5")) return FALSE; - if (bign96KeypairVal(params, privkey, pubkey) != ERR_OK) + if (bign96KeypairVal(privkey, pubkey) != ERR_OK) return FALSE; - if (bign96PubkeyVal(params, pubkey) != ERR_OK) + if (bign96PubkeyVal(pubkey) != ERR_OK) return FALSE; - if (bign96PubkeyCalc(pubkey, params, privkey) != ERR_OK) + if (bign96PubkeyCalc(pubkey, privkey) != ERR_OK) return FALSE; if (!hexEq(pubkey, "4CED8FBBA1842BE58B4C0444F359CB14" @@ -130,27 +118,26 @@ bool_t bign96Test() // выработка и проверка подписи if (beltHash(hash, beltH(), 13) != ERR_OK) return FALSE; - if (bign96Sign(sig, params, oid_der, oid_len, hash, privkey, - brngCTRXStepR, state) != ERR_OK) + if (bign96Sign(sig, hash, privkey, brngCTRXStepR, state) != ERR_OK) return FALSE; if (!hexEq(sig, "4981BBDD8721C08FA347B89BD16FDDE6" "47D310F55474C4182C1CC5BBD5642CC7" "E1B2")) return FALSE; - if (bign96Verify(params, oid_der, oid_len, hash, sig, pubkey) != ERR_OK) + if (bign96Verify(hash, sig, pubkey) != ERR_OK) return FALSE; sig[0] ^= 1; - if (bign96Verify(params, oid_der, oid_len, hash, sig, pubkey) == ERR_OK) + if (bign96Verify(hash, sig, pubkey) == ERR_OK) return FALSE; sig[0] ^= 1, pubkey[0] ^= 1; - if (bign96Verify(params, oid_der, oid_len, hash, sig, pubkey) == ERR_OK) + if (bign96Verify(hash, sig, pubkey) == ERR_OK) return FALSE; pubkey[0] ^= 1; // детерминированная подпись if (beltHash(hash, beltH(), 13) != ERR_OK) return FALSE; - if (bign96Sign2(sig, params, oid_der, oid_len, hash, privkey, 0, 0) != + if (bign96Sign2(sig, hash, privkey, 0, 0) != ERR_OK) return FALSE; if (!hexEq(sig, @@ -158,13 +145,13 @@ bool_t bign96Test() "CF44D615CCE5F45D474E7593D30E70B9" "B0C3")) return FALSE; - if (bign96Verify(params, oid_der, oid_len, hash, sig, pubkey) != ERR_OK) + if (bign96Verify(hash, sig, pubkey) != ERR_OK) return FALSE; sig[0] ^= 1; - if (bign96Verify(params, oid_der, oid_len, hash, sig, pubkey) == ERR_OK) + if (bign96Verify(hash, sig, pubkey) == ERR_OK) return FALSE; sig[0] ^= 1, pubkey[0] ^= 1; - if (bign96Verify(params, oid_der, oid_len, hash, sig, pubkey) == ERR_OK) + if (bign96Verify(hash, sig, pubkey) == ERR_OK) return FALSE; pubkey[0] ^= 1; // все нормально diff --git a/test/crypto/bign_test.c b/test/crypto/bign_test.c index 9259def1..87ffa86f 100644 --- a/test/crypto/bign_test.c +++ b/test/crypto/bign_test.c @@ -4,7 +4,7 @@ \brief Tests for STB 34.101.45 (bign) \project bee2/test \created 2012.08.27 -\version 2026.03.06 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -141,7 +141,7 @@ static err_t bignPrePrintEc(const ec_o* ec, ec_pre_type type, size_t w) // размерности mb = wwBitSize(ec->order, ec->f->n + 1); coord_size = O_OF_W(ec->f->n); - if (w == 0 || (type == ec_pre_si && mb % w) || coord_size % 16) + if (w == 0 || (type == ec_pre_si && mb % w) || coord_size % 8) return ERR_BAD_INPUT; h = (type != ec_pre_soa) ? (mb + w - 1) / w : 0; pre_count = SIZE_BIT_POS(w - 1); diff --git a/test/crypto/btok_test.c b/test/crypto/btok_test.c index 6806d142..87e7a341 100644 --- a/test/crypto/btok_test.c +++ b/test/crypto/btok_test.c @@ -4,7 +4,7 @@ \brief Tests for STB 34.101.79 (btok) \project bee2/test \created 2022.07.07 -\version 2025.09.29 +\version 2026.03.10 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -16,7 +16,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -109,7 +111,6 @@ static bool_t btokCVCTest() btok_cvc_t cvc1[1]; btok_cvc_t cvc2[1]; btok_cvc_t cvc3[1]; - bign_params params[1]; octet privkey0[64]; octet privkey1[48]; octet privkey2[32]; @@ -135,8 +136,7 @@ static bool_t btokCVCTest() cvc0->pubkey_len = 128; if (btokCVCCheck(cvc0) == ERR_OK) return FALSE; - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.3") != ERR_OK || - bignKeypairGen(privkey0, cvc0->pubkey, params, prngEchoStepR, + if (bign256KeypairGen(privkey0, cvc0->pubkey, prngEchoStepR, state) != ERR_OK || btokCVCCheck(cvc0) != ERR_OK) return FALSE; @@ -175,8 +175,7 @@ static bool_t btokCVCTest() memSet(cvc1->hat_eid, 0xDD, sizeof(cvc1->hat_eid)); memSet(cvc1->hat_esign, 0x33, sizeof(cvc1->hat_esign)); cvc1->pubkey_len = 96; - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.2") != ERR_OK || - bignKeypairGen(privkey1, cvc1->pubkey, params, prngEchoStepR, + if (bign192KeypairGen(privkey1, cvc1->pubkey, prngEchoStepR, state) != ERR_OK || btokCVCCheck(cvc1) != ERR_OK) return FALSE; @@ -212,8 +211,7 @@ static bool_t btokCVCTest() memSet(cvc2->hat_eid, 0x88, sizeof(cvc2->hat_eid)); memSet(cvc2->hat_esign, 0x11, sizeof(cvc2->hat_esign)); cvc2->pubkey_len = 64; - if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK || - bignKeypairGen(privkey2, cvc2->pubkey, params, prngEchoStepR, + if (bign128KeypairGen(privkey2, cvc2->pubkey, prngEchoStepR, state) != ERR_OK || btokCVCCheck(cvc2) != ERR_OK) return FALSE; @@ -236,8 +234,7 @@ static bool_t btokCVCTest() hexTo(cvc3->from, "020200070102"); hexTo(cvc3->until, "020901020301"); cvc3->pubkey_len = 48; - if (bign96ParamsStd(params, "1.2.112.0.2.0.34.101.45.3.0") != ERR_OK || - bign96KeypairGen(privkey3, cvc3->pubkey, params, prngEchoStepR, + if (bign96KeypairGen(privkey3, cvc3->pubkey, prngEchoStepR, state) != ERR_OK || btokCVCCheck(cvc3) != ERR_OK) return FALSE; diff --git a/win/bee2.def b/win/bee2.def index e5e6bd0c..bf4ae392 100644 --- a/win/bee2.def +++ b/win/bee2.def @@ -311,15 +311,13 @@ EXPORTS btokCVCVal @1507 btokCVCVal2 @1508 - bign96ParamsStd @1601 - bign96ParamsVal @1602 - bign96KeypairGen @1603 - bign96KeypairVal @1604 - bign96PubkeyVal @1605 - bign96PubkeyCalc @1606 - bign96Sign @1607 - bign96Sign2 @1608 - bign96Verify @1609 + bign96KeypairGen @1601 + bign96KeypairVal @1602 + bign96PubkeyVal @1603 + bign96PubkeyCalc @1604 + bign96Sign @1605 + bign96Sign2 @1606 + bign96Verify @1607 stb99SeedVal @1701 stb99SeedAdj @1702 diff --git a/win/vs09/libbee2.vcproj b/win/vs09/libbee2.vcproj index 67e58dcc..eb4e4176 100644 --- a/win/vs09/libbee2.vcproj +++ b/win/vs09/libbee2.vcproj @@ -612,10 +612,6 @@ RelativePath="..\..\src\crypto\bels.c" > - - @@ -775,6 +771,10 @@ RelativePath="..\..\src\crypto\bign\bign256.c" > + + diff --git a/win/vs15/libbee2.vcxproj b/win/vs15/libbee2.vcxproj index 77067c96..3a8a2c9a 100644 --- a/win/vs15/libbee2.vcxproj +++ b/win/vs15/libbee2.vcxproj @@ -403,10 +403,10 @@ - + diff --git a/win/vs15/libbee2.vcxproj.filters b/win/vs15/libbee2.vcxproj.filters index 8c8247c0..60c855ac 100644 --- a/win/vs15/libbee2.vcxproj.filters +++ b/win/vs15/libbee2.vcxproj.filters @@ -264,9 +264,6 @@ Source Files\crypto\btok - - Source Files\crypto - Source Files\crypto @@ -354,6 +351,9 @@ Source Files\crypto\bign + + Source Files\crypto\bign + From 6e7aae355f96d8f798d7e5a3c96c29a9fc5bb8af Mon Sep 17 00:00:00 2001 From: agievich Date: Tue, 10 Mar 2026 23:00:27 +0300 Subject: [PATCH 64/77] Use precomputed tables in bign96 --- src/crypto/bign/bign96.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/bign/bign96.c b/src/crypto/bign/bign96.c index f344e97c..4e9d8d1d 100644 --- a/src/crypto/bign/bign96.c +++ b/src/crypto/bign/bign96.c @@ -152,7 +152,7 @@ static err_t bign96Ec(const ec_o** pec) code = bignEcCreate(&_ec, params); ERR_CALL_HANDLE(code, mtMtxUnlock(_mtx)); // настроить ec->pre -// _ec->pre = &_pre; + _ec->pre = &_pre; } // возвратить кривую *pec = _ec; From 9293d4d08ee96b38779b956926108dff4a035cb5 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 13 Mar 2026 16:48:10 +0300 Subject: [PATCH 65/77] Go to v2.2.2 --- CMakeLists.txt | 2 +- include/bee2/info.h | 8 ++++---- include/bee2/math/ec.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 259d882e..77c7095e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5...3.31) project(BEE2 - VERSION 2.2.1 + VERSION 2.2.2 LANGUAGES C ) diff --git a/include/bee2/info.h b/include/bee2/info.h index 332516cb..b02bd5d5 100644 --- a/include/bee2/info.h +++ b/include/bee2/info.h @@ -22,18 +22,18 @@ #define __BEE2_INFO #define BEE2_NAME "Bee2" -#define BEE2_VERSION "2.2.1" +#define BEE2_VERSION "2.2.2" #define BEE2_VERSION_MAJOR "2" #define BEE2_VERSION_MINOR "2" -#define BEE2_VERSION_PATCH "1" +#define BEE2_VERSION_PATCH "2" #define BEE2_VERSION_NUM\ - 2, 2, 1 + 2, 2, 2 /*! ******************************************************************************* \mainpage Библиотека Bee2 -\version 2.2.1 +\version 2.2.2 \section toc Содержание diff --git a/include/bee2/math/ec.h b/include/bee2/math/ec.h index ef3f9dd1..9089b728 100644 --- a/include/bee2/math/ec.h +++ b/include/bee2/math/ec.h @@ -4,7 +4,7 @@ \brief Elliptic curves \project bee2 [cryptographic library] \created 2012.04.19 -\version 2026.03.10 +\version 2026.03.13 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -1113,7 +1113,7 @@ size_t ecMulPreOD_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m); переходов; - ec->sgna != 0 и функция ec->sgna регулярна: знак точки устанавливается без условных переходов; - - pre->w * pre->h == wwBitSize(ec->order, m); + - pre->h >= pre->w + 2 && (pre->h - 1) * pre->w < wwBitSize(ec->order, m); - функция (ec->finadda != 0) ? ec->finadda : ec->adda регулярна: сложения a + b, a + a, a + (-a) выполняются по одним и тем же формулам без условных переходов; From 05bbf6eea2ce9b3a81cea2148dc8a408008ebfb4 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 13 Mar 2026 17:12:38 +0300 Subject: [PATCH 66/77] Rearrange jobs in actions/build --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a78c3bc2..0d68f9ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: - BUILD_TYPE: Check BUILD_FAST: OFF BASH_PLATFORM: BASH_SSE2 - CC: gcc + CC: clang - BUILD_TYPE: ASan BUILD_FAST: OFF BASH_PLATFORM: BASH_32 From 5f942183dcc3a6d77a61ef8140d51bcbd46fffec Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 13 Mar 2026 18:08:38 +0300 Subject: [PATCH 67/77] Link bee2_static with phtread --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b75e6219..c3a4e5b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -103,7 +103,7 @@ add_library(bee2_static STATIC ${src}) set_target_properties(bee2_static PROPERTIES OUTPUT_NAME bee2_static) if(UNIX AND NOT APPLE) - target_link_libraries(bee2_static ${CMAKE_DL_LIBS}) + target_link_libraries(bee2_static ${CMAKE_DL_LIBS} pthread) else() target_link_libraries(bee2_static) endif() From a1aae80f82916aac938ffe85440c1420bc41ac9a Mon Sep 17 00:00:00 2001 From: agievich Date: Sun, 15 Mar 2026 14:08:53 +0300 Subject: [PATCH 68/77] Prefetch tables in belt_block --- include/bee2/core/mem.h | 15 ++++++++++++++- src/core/mem.c | 14 +++++++++++++- src/crypto/belt/belt_block.c | 16 +++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/include/bee2/core/mem.h b/include/bee2/core/mem.h index 2c602199..1918ca9d 100644 --- a/include/bee2/core/mem.h +++ b/include/bee2/core/mem.h @@ -4,7 +4,7 @@ \brief Memory management \project bee2 [cryptographic library] \created 2012.07.16 -\version 2025.10.20 +\version 2026.03.15 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -443,6 +443,19 @@ void memRev( size_t count /*!< [in] размер буфера */ ); +/*! \brief Предварительная загрузка + + Октеты буфера [count]buf прочитываются с шагом, который определяется + размером линии кэш-памяти (cache line). + \remark Функция предназначена для защиты от кэш-атак. + Функцию следует вызывать перед обращениями к ячейкам памяти по + ключезависимым адресам. +*/ +void memPrefetch( + const void* buf, /*!< [in] буфер */ + size_t count /*!< [in] размер буфера */ +); + /* ******************************************************************************* Разметка памяти diff --git a/src/core/mem.c b/src/core/mem.c index 491a02cc..f02e590f 100644 --- a/src/core/mem.c +++ b/src/core/mem.c @@ -4,7 +4,7 @@ \brief Memory management \project bee2 [cryptographic library] \created 2012.12.18 -\version 2026.01.20 +\version 2026.03.15 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -517,6 +517,18 @@ void memRev(void* buf, size_t count) } } +#define CACHE_LINE_SIZE 64 + +void memPrefetch(const void* buf, size_t count) +{ + register octet sum; + size_t i; + ASSERT(memIsValid(buf, count)); + for (i = 0, sum = 0; i < count; i += CACHE_LINE_SIZE) + sum += ((const octet*)buf)[i]; + CLEAN(sum); +} + /* ******************************************************************************* Разметка памяти diff --git a/src/crypto/belt/belt_block.c b/src/crypto/belt/belt_block.c index cd2b4459..4e03891d 100644 --- a/src/crypto/belt/belt_block.c +++ b/src/crypto/belt/belt_block.c @@ -4,7 +4,7 @@ \brief STB 34.101.31 (belt): block encryption \project bee2 [cryptographic library] \created 2012.12.18 -\version 2025.10.02 +\version 2026.03.15 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -194,6 +194,14 @@ static const u32 H29[256] = { HEx16(D4,EF,D9,B4,3A,62,28,75,91,14,10,EA,77,6C,DA,1D, 29), }; +#ifndef SAFE_FAST + #define beltHPrefetch()\ + memPrefetch(H5, 256 * 4), memPrefetch(H13, 256 * 4),\ + memPrefetch(H21, 256 * 4), memPrefetch(H29, 256 * 4) +#else + #define beltHPrefetch() +#endif + /* ******************************************************************************* G-блоки @@ -303,6 +311,7 @@ void beltBlockEncr(octet block[16], const u32 key[8]) t[2] = u32Rev(t[2]); t[3] = u32Rev(t[3]); #endif + beltHPrefetch(); E((t + 0), (t + 1), (t + 2), (t + 3), key); #if (OCTET_ORDER == BIG_ENDIAN) t[3] = u32Rev(t[3]); @@ -314,11 +323,13 @@ void beltBlockEncr(octet block[16], const u32 key[8]) void beltBlockEncr2(u32 block[4], const u32 key[8]) { + beltHPrefetch(); E((block + 0), (block + 1), (block + 2), (block + 3), key); } void beltBlockEncr3(u32* a, u32* b, u32* c, u32* d, const u32 key[8]) { + beltHPrefetch(); E(a, b, c, d, key); } @@ -339,6 +350,7 @@ void beltBlockDecr(octet block[16], const u32 key[8]) t[2] = u32Rev(t[2]); t[3] = u32Rev(t[3]); #endif + beltHPrefetch(); D((t + 0), (t + 1), (t + 2), (t + 3), key); #if (OCTET_ORDER == BIG_ENDIAN) t[3] = u32Rev(t[3]); @@ -350,10 +362,12 @@ void beltBlockDecr(octet block[16], const u32 key[8]) void beltBlockDecr2(u32 block[4], const u32 key[8]) { + beltHPrefetch(); D((block + 0), (block + 1), (block + 2), (block + 3), key); } void beltBlockDecr3(u32* a, u32* b, u32* c, u32* d, const u32 key[8]) { + beltHPrefetch(); D(a, b, c, d, key); } From 9d2b833c4cd3c81afc2fe8113c2f8b7adeef0e74 Mon Sep 17 00:00:00 2001 From: Arseni Novikau Date: Wed, 18 Mar 2026 09:14:43 +0300 Subject: [PATCH 69/77] Fix rngSysRead error handling for fileRead2 --- src/core/rng/rng_sys.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/rng/rng_sys.c b/src/core/rng/rng_sys.c index 147a2520..948d20ae 100644 --- a/src/core/rng/rng_sys.c +++ b/src/core/rng/rng_sys.c @@ -138,13 +138,17 @@ err_t rngSys2Read(void* buf, size_t* read, size_t count) err_t rngSysRead(void* buf, size_t* read, size_t count) { file_t file; + size_t n; ASSERT(memIsValid(read, O_PER_S)); ASSERT(memIsValid(buf, count)); file = fileOpen("/dev/urandom", "rb"); if (!file) return ERR_FILE_OPEN; - *read = fileRead2(buf, count, file); + n = fileRead2(buf, count, file); fclose(file); + if (n == SIZE_MAX) + return ERR_FILE_READ; + *read = n; return ERR_OK; } From ad3e7f17b653db9fe3d7f864e24a432a1f5cf445 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 18 Mar 2026 10:21:55 +0300 Subject: [PATCH 70/77] Update unix/rngSysRead() --- src/core/rng/rng_sys.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/core/rng/rng_sys.c b/src/core/rng/rng_sys.c index 948d20ae..53ae228e 100644 --- a/src/core/rng/rng_sys.c +++ b/src/core/rng/rng_sys.c @@ -4,7 +4,7 @@ \brief Random number generation: system entropy sources \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.12.24 +\version 2026.03.18 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -138,18 +138,14 @@ err_t rngSys2Read(void* buf, size_t* read, size_t count) err_t rngSysRead(void* buf, size_t* read, size_t count) { file_t file; - size_t n; - ASSERT(memIsValid(read, O_PER_S)); + err_t code; ASSERT(memIsValid(buf, count)); file = fileOpen("/dev/urandom", "rb"); if (!file) return ERR_FILE_OPEN; - n = fileRead2(buf, count, file); - fclose(file); - if (n == SIZE_MAX) - return ERR_FILE_READ; - *read = n; - return ERR_OK; + code = fileRead(read, buf, count, file); + fileClose(file); + return code; } #if defined(OS_APPLE) From 7202e284c00b615c7cbc7fdbac13cc2463240024 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 18 Mar 2026 10:23:11 +0300 Subject: [PATCH 71/77] Start v2.2.3 --- CMakeLists.txt | 2 +- include/bee2/info.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77c7095e..08715c76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5...3.31) project(BEE2 - VERSION 2.2.2 + VERSION 2.2.3 LANGUAGES C ) diff --git a/include/bee2/info.h b/include/bee2/info.h index b02bd5d5..9eba077f 100644 --- a/include/bee2/info.h +++ b/include/bee2/info.h @@ -22,18 +22,18 @@ #define __BEE2_INFO #define BEE2_NAME "Bee2" -#define BEE2_VERSION "2.2.2" +#define BEE2_VERSION "2.2.3" #define BEE2_VERSION_MAJOR "2" #define BEE2_VERSION_MINOR "2" -#define BEE2_VERSION_PATCH "2" +#define BEE2_VERSION_PATCH "3" #define BEE2_VERSION_NUM\ - 2, 2, 2 + 2, 2, 3 /*! ******************************************************************************* \mainpage Библиотека Bee2 -\version 2.2.2 +\version 2.2.3 \section toc Содержание From 145b0d8d959026735e4f037211e4419d5c147fb7 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 20 Mar 2026 13:01:03 +0300 Subject: [PATCH 72/77] Fix comments in math/qr and core/rng --- include/bee2/core/rng.h | 10 +++++----- src/math/qr.c | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/include/bee2/core/rng.h b/include/bee2/core/rng.h index 280f3226..15652cba 100644 --- a/include/bee2/core/rng.h +++ b/include/bee2/core/rng.h @@ -4,7 +4,7 @@ \brief Entropy sources and random number generators \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.10.03 +\version 2026.03.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -36,10 +36,10 @@ extern "C" { Реализованы статистические тесты стандарта FIPS 140-2. В этих тестах обрабатывает последовательность двоичных символов длины 20000 (2500 байтов). -Уровень значимости тестов p = 0.01. С такой вероятностью истинно случайная -последовательность не пройдет тест. Известно, что тесты FIPS слабо зависимы. -Поэтому истинно случайная последовательность не пройдет батарею из n тестов -с вероятностью 1 - (1 - p)^n \approx n p. +Уровень значимости тестов p \approx 0.0001. С такой вероятностью истинно +случайная последовательность не пройдет тест. Известно, что тесты FIPS слабо +зависимы. Поэтому истинно случайная последовательность не пройдет батарею из n +тестов с вероятностью 1 - (1 - p)^n \approx n p. \safe Обрабатываемые в тестах данные не считаются секретными. Эти данные не должны использовать для построения ключей. diff --git a/src/math/qr.c b/src/math/qr.c index 508b4a1d..149a66c8 100644 --- a/src/math/qr.c +++ b/src/math/qr.c @@ -4,7 +4,7 @@ \brief Quotient rings \project bee2 [cryptographic library] \created 2013.09.14 -\version 2026.02.05 +\version 2026.03.19 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -53,23 +53,25 @@ bool_t qrIsOperable(const qr_o* r) Возведение в степень В функции qrPower() реализован скользящий оконный метод возведения -в степень (c = a^b). Предварительно рассчитываются малые степени +в степень: + c <- a^b. +Предварительно рассчитываются малые степени a^1, a^3,..., a^{2^w} - 1, где w --- величина окна. Затем в b выделяются серии из нулей и слайды. Слайд -- это битовый фрагмент, окаймленный единицами, длина которого не превосходит w. Слайд интерпретируется как число (нечетное). -Обработка нуля серии состоит в возведении с в квадрат. -Обработка слайда длины slide_size состоит в slide_size возведениях c -в квадрат и последующем умножении c на малую степень a. +Обработка нуля серии состоит в возведении с в квадрат. Обработка слайда длины +slide_size состоит в slide_size возведениях c в квадрат и последующем +умножении c на малую степень a. -В начале вычислений выполняется присвоение c <- a^{старший_слайд_b}. +В начале вычислений выполняется присвоение + c <- a^{старший_слайд_b}. Скользящий оконный метод основан на представлении b = \sum {i=0}^{l - 1} b_i 2^i, b_i \in {0, 1, 3,..., 2^w - 1} -Известно [Moller B. Improved Techniques for Fast Exponentiation, -ICISC 2002], что l = wwBitSize(b) и доля ненулевых b_i для случайного -показателя b равняется 1 / (w + 1). +Известно [Mol02], что доля ненулевых b_i для случайного показателя b +равняется 1 / (w + 1). Для расчета малых степеней a требуется 2^{w - 1} - 1 умножения. Для расчета c в среднем требуется еще около (l - w) / (w + 1) умножений @@ -78,6 +80,9 @@ ICISC 2002], что l = wwBitSize(b) и доля ненулевых b_i для В функции qrCalcSlideWidth() определяется w, которое доставляет минимум целевой функции 2^{w - 1} + (l - w) / (w + 1). + +[Mol02] Moller B. Information Security and Cryptology – ICISC 2002, + Springer-Verlag LNCS 2587, pp. 298–312, ISBN 3-540-00716-4, 2003. ******************************************************************************* */ From c4777d1ad26bce307f98d34d50e87cbe1f5eedb7 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 20 Mar 2026 17:12:20 +0300 Subject: [PATCH 73/77] Improve tests for rngTestFIPSX() --- include/bee2/core/rng.h | 4 ++-- src/core/rng/rng_test.c | 27 +++++++++++++-------------- test/core/rng_test.c | 12 +++++++++++- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/include/bee2/core/rng.h b/include/bee2/core/rng.h index 15652cba..e945f36b 100644 --- a/include/bee2/core/rng.h +++ b/include/bee2/core/rng.h @@ -83,7 +83,7 @@ bool_t rngTestFIPS2( выполняется: S_1 \in [2315, 2685], S_2 \in [1114, 1386], S_3 \in [527, 723], S_4 \in [240, 384], S_5, S_6+ \in [103, 209]. Здесь S_i -- количество серий длины i = 1, 2, ..., S_6+ = S_6 + S_7 + .... - \remark Бит с номером i: wwTestBit((const word*)buf, i). + \remark Бит с номером i: (buf[i / 8] >> (i % 8)) & 1. */ bool_t rngTestFIPS3( const octet buf[2500] /*!< [in] тестируемая последовательность */ @@ -96,7 +96,7 @@ bool_t rngTestFIPS3( \return Признак успешного прохождения теста. \remark Тест пройден, если в последовательности отсутствуют серии длины 26 и больше. - \remark Бит с номером i: wwTestBit((const word*)buf, i). + \remark Бит с номером i: (buf[i / 8] >> (i % 8)) & 1. */ bool_t rngTestFIPS4( const octet buf[2500] /*!< [in] тестируемая последовательность */ diff --git a/src/core/rng/rng_test.c b/src/core/rng/rng_test.c index 76137860..3095ab01 100644 --- a/src/core/rng/rng_test.c +++ b/src/core/rng/rng_test.c @@ -4,7 +4,7 @@ \brief Random number generation: statistical tests \project bee2 [cryptographic library] \created 2014.10.13 -\version 2025.10.08 +\version 2026.03.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -24,10 +24,13 @@ ******************************************************************************* */ +#define memTestBit(buf, i)\ + ((((const octet*)(buf))[(i) / 8] >> ((i) % 8)) & 1) + bool_t rngTestFIPS1(const octet buf[2500]) { - register size_t s; - register word w; + size_t s; + word w; size_t count; ASSERT(memIsValid(buf, 2500)); for (w = 0, count = 2500; !memIsAligned(buf, O_PER_W); ++buf, --count) @@ -41,17 +44,16 @@ bool_t rngTestFIPS1(const octet buf[2500]) for (w = 0; count; ++buf, --count) w = (w << 8) | *buf; s += wordWeight(w); - CLEAN(w); return 9725 < s && s < 10275; } bool_t rngTestFIPS2(const octet buf[2500]) { u32 s[16]; - size_t i = 2500; + size_t i; ASSERT(memIsValid(buf, 2500)); memSetZero(s, sizeof(s)); - while (i--) + for (i = 2500; i--;) ++s[buf[i] & 15], ++s[buf[i] >> 4]; s[0] *= s[0]; for (i = 1; i < 16; ++i) @@ -62,16 +64,15 @@ bool_t rngTestFIPS2(const octet buf[2500]) bool_t rngTestFIPS3(const octet buf[2500]) { - word s[2][7]; + size_t s[2][7]; octet b; size_t l; size_t i; ASSERT(memIsValid(buf, 2500)); memSetZero(s, sizeof(s)); b = buf[0] & 1; - l = 1; - for (i = 1; i < 20000; ++i) - if ((buf[i / 8] >> i % 8 & 1) == b) + for (i = l = 1; i < 20000; ++i) + if (memTestBit(buf, i) == b) ++l; else ++s[b][MIN2(l, 6)], b = !b, l = 1; @@ -97,9 +98,8 @@ bool_t rngTestFIPS4(const octet buf[2500]) size_t i; ASSERT(memIsValid(buf, 2500)); b = buf[0] & 1; - l = 1; - for (i = 1; i < 20000; ++i) - if ((buf[i / 8] >> i % 8 & 1) == b) + for (i = l = 1; i < 20000; ++i) + if (memTestBit(buf, i) == b) ++l; else { @@ -109,4 +109,3 @@ bool_t rngTestFIPS4(const octet buf[2500]) } return l < 26; } - diff --git a/test/core/rng_test.c b/test/core/rng_test.c index 4191c0d0..2ff94c3d 100644 --- a/test/core/rng_test.c +++ b/test/core/rng_test.c @@ -4,7 +4,7 @@ \brief Tests for random number generators \project bee2/test \created 2014.10.10 -\version 2025.10.07 +\version 2026.03.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -54,6 +55,15 @@ bool_t rngTest() printf("rngSource[%5s]: %s\n", sources[pos], hex); } } + // тестирование специально подобранных данных + memSet(buf, 0x5C, 2500); + if (!rngTestFIPS1(buf) || rngTestFIPS2(buf) || + rngTestFIPS3(buf) || !rngTestFIPS4(buf)) + return FALSE; + memSet(buf + 10, 0x00, 69); + if (rngTestFIPS1(buf) || rngTestFIPS2(buf) || + rngTestFIPS3(buf) || rngTestFIPS4(buf)) + return FALSE; // работа с ГСЧ if (rngCreate(0, 0) != ERR_OK) return FALSE; From 75465b1da7c637d5286d39db011c1be8346344ed Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Fri, 20 Mar 2026 17:24:08 +0300 Subject: [PATCH 74/77] Fix a typo in zzSubIf() --- include/bee2/math/zz.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bee2/math/zz.h b/include/bee2/math/zz.h index e3ecff46..baac3031 100644 --- a/include/bee2/math/zz.h +++ b/include/bee2/math/zz.h @@ -4,7 +4,7 @@ \brief Multiple-precision unsigned integers \project bee2 [cryptographic library] \created 2012.04.22 -\version 2026.01.20 +\version 2026.03.20 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -292,7 +292,7 @@ word zzSubW2( \remark Функция используется при регуляризации. */ void zzSubIf( - word с[], /*!< [out] результат */ + word c[], /*!< [out] результат */ const word a[], /*!< [in] потенциальное уменьшаемое */ const word b[], /*!< [in] потенциальное вычитаемое */ size_t n, /*!< [in] длина чисел в машинных словах */ From 236b2b8b904dab964bdb596ae65944d69b21894f Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 24 Mar 2026 18:48:29 +0300 Subject: [PATCH 75/77] Fix bignKeyUnwrapEc() --- src/crypto/bign/bign_keyt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crypto/bign/bign_keyt.c b/src/crypto/bign/bign_keyt.c index 791a42a8..41d91061 100644 --- a/src/crypto/bign/bign_keyt.c +++ b/src/crypto/bign/bign_keyt.c @@ -4,7 +4,7 @@ \brief STB 34.101.45 (bign): key transport \project bee2 [cryptographic library] \created 2012.04.27 -\version 2026.02.13 +\version 2026.03.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -218,6 +218,7 @@ err_t bignKeyUnwrapEc(octet key[], const ec_o* ec, const octet token[], header == 0 && !memIsZero(header2, 16)) { memSetZero(key, len - no - 16); + blobClose(state); return ERR_BAD_KEYTOKEN; } // завершение From fa53ee1ce71023b86b01734b393e80352088e5ec Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Tue, 24 Mar 2026 19:22:17 +0300 Subject: [PATCH 76/77] Suppress non-const-time strEq() in crypto/botp --- src/crypto/botp.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/crypto/botp.c b/src/crypto/botp.c index 6cc616fe..7b35d1d4 100644 --- a/src/crypto/botp.c +++ b/src/crypto/botp.c @@ -4,7 +4,7 @@ \brief STB 34.101.47/botp: OTP algorithms \project bee2 [cryptographic library] \created 2015.11.02 -\version 2025.09.23 +\version 2026.03.24 \copyright The Bee2 authors \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt). ******************************************************************************* @@ -27,9 +27,8 @@ В botpDT() реализован механизм "динамической обрезки" (dynamic truncation), объявленный в RFC 4226 для HMAC(SHA-1), а затем продолженный в RFC 6238, 6287 для HMAC(SHA-256) и HMAC(SHA-512). Пояснений по продолжению нет, но как выяснили -экспериментально вот здесь - http://crypto.stackexchange.com/questions/27474/ - [how-does-the-hotp-dynamic-truncation-function-generalize-to-longer-hashes] +экспериментально вот здесь + http://crypto.stackexchange.com/questions/27474/ номер октета, с которого начинается пароль, определяется по последнему октету mac. @@ -164,7 +163,7 @@ bool_t botpHOTPStepV(const char* otp, void* state) memCopy(st->ctr1, st->ctr, 8); // проверить пароль botpHOTPStepR(st->otp, state); - if (strEq(st->otp, otp)) + if (strLen(otp) == st->digit && memEq(st->otp, otp, st->digit)) return TRUE; // вернуться к первоначальному счетчику memCopy(st->ctr, st->ctr1, 8); @@ -282,7 +281,7 @@ bool_t botpTOTPStepV(const char* otp, tm_time_t t, void* state) ASSERT(memIsDisjoint2(otp, strLen(otp) + 1, state, botpTOTP_keep())); // вычислить и проверить пароль botpTOTPStepR(st->otp, t, state); - return strEq(st->otp, otp); + return strLen(otp) == st->digit && memEq(st->otp, otp, st->digit); } err_t botpTOTPRand(char* otp, size_t digit, const octet key[], size_t key_len, @@ -573,7 +572,7 @@ bool_t botpOCRAStepV(const char* otp, const octet q[], size_t q_len, memCopy(st->ctr1, st->ctr, 8); // проверить пароль botpOCRAStepR(st->otp, q, q_len, t, state); - if (strEq(st->otp, otp)) + if (strLen(otp) == st->digit && memEq(st->otp, otp, st->digit)) return TRUE; // вернуться к первоначальному счетчику memCopy(st->ctr, st->ctr1, 8); From 758a5c2754b4a859f6f16bfb7fb603b8bf38b979 Mon Sep 17 00:00:00 2001 From: Sergey Agievich Date: Wed, 25 Mar 2026 09:46:18 +0300 Subject: [PATCH 77/77] Go to v2.2.4 --- CMakeLists.txt | 2 +- include/bee2/info.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08715c76..3a24164f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5...3.31) project(BEE2 - VERSION 2.2.3 + VERSION 2.2.4 LANGUAGES C ) diff --git a/include/bee2/info.h b/include/bee2/info.h index 9eba077f..a73ecdf6 100644 --- a/include/bee2/info.h +++ b/include/bee2/info.h @@ -22,18 +22,18 @@ #define __BEE2_INFO #define BEE2_NAME "Bee2" -#define BEE2_VERSION "2.2.3" +#define BEE2_VERSION "2.2.4" #define BEE2_VERSION_MAJOR "2" #define BEE2_VERSION_MINOR "2" -#define BEE2_VERSION_PATCH "3" +#define BEE2_VERSION_PATCH "4" #define BEE2_VERSION_NUM\ - 2, 2, 3 + 2, 2, 4 /*! ******************************************************************************* \mainpage Библиотека Bee2 -\version 2.2.3 +\version 2.2.4 \section toc Содержание