diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dfe6f94b..c1c39ed7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -60,9 +60,12 @@ if (GTest_FOUND) add_executable(RandBLAS_stats test_basic_rng/rng_common.hh test_basic_rng/test_sample_indices.cc + test_basic_rng/test_r123_kat.cc ) target_link_libraries(RandBLAS_stats RandBLAS GTest::GTest GTest::Main) gtest_discover_tests(RandBLAS_stats) + file(COPY test_basic_rng/kat_vectors.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../test/) + file(COPY test_basic_rng/kat_vectors.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) endif() message(STATUS "Checking for regression tests ... ${tmp}") diff --git a/test/test_basic_rng/kat_vectors.txt b/test/test_basic_rng/kat_vectors.txt new file mode 100644 index 00000000..21c453dc --- /dev/null +++ b/test/test_basic_rng/kat_vectors.txt @@ -0,0 +1,75 @@ +# This file is copied from https://github.com/DEShawResearch/random123/blob/main/tests/kat_vectors +# +# For each generator, we test: gen(0, 0), gen(fff, fff) and gen(ctr=digits_of_pi, key=more_digits_of_pi). +# Ignoring endianness, these are the first few hexdigits of pi: +# 243F6A88 85A308D3 13198A2E 03707344 A4093822 299F31D0 082EFA98 EC4E6C89 452821E6 38D01377 BE5466CF 34E90C6C C0AC29B7 C97C50DD 3F84D5B5 B5470917 9216D5D9 8979FB1BD +# +#nameNxW R CTR KEY EXPECTED +# +philox2x32 7 00000000 00000000 00000000 257a3673 cd26be2a +philox2x32 7 ffffffff ffffffff ffffffff ab302c4d 3dc9d239 +philox2x32 7 243f6a88 85a308d3 13198a2e bedbbe6b e4c770b3 +philox2x32 10 00000000 00000000 00000000 ff1dae59 6cd10df2 +philox2x32 10 ffffffff ffffffff ffffffff 2c3f628b ab4fd7ad +philox2x32 10 243f6a88 85a308d3 13198a2e dd7ce038 f62a4c12 +# +philox4x32 7 00000000 00000000 00000000 00000000 00000000 00000000 5f6fb709 0d893f64 4f121f81 4f730a48 +philox4x32 7 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 5207ddc2 45165e59 4d8ee751 8c52f662 +philox4x32 7 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 4dfccaba 190a87f0 c47362ba b6b5242a +philox4x32 10 00000000 00000000 00000000 00000000 00000000 00000000 6627e8d5 e169c58d bc57ac4c 9b00dbd8 +philox4x32 10 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 408f276d 41c83b0e a20bc7c6 6d5451fd +philox4x32 10 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 d16cfe09 94fdcceb 5001e420 24126ea1 +# +philox2x64 7 0000000000000000 0000000000000000 0000000000000000 b41da69fbfefc666 511e9ce1a5534056 +philox2x64 7 ffffffffffffffff ffffffffffffffff ffffffffffffffff a4696cc04462015d 724782dae17169e9 +philox2x64 7 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 98ed1534392bf372 67528b1568882fd5 +philox2x64 10 0000000000000000 0000000000000000 0000000000000000 ca00a0459843d731 66c24222c9a845b5 +philox2x64 10 ffffffffffffffff ffffffffffffffff ffffffffffffffff 65b021d60cd8310f 4d02f3222f86df20 +philox2x64 10 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 0a5e742c2997341c b0f883d38000de5d +# +philox4x64 7 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 5dc8ee6268ec62cd 139bc570b6c125a0 84d6deb4fb65f49e aff7583376d378c2 +philox4x64 7 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 071dd84367903154 48e2bbdc722b37d1 6afa9890bb89f76c 9194c8d8ada56ac7 +philox4x64 7 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c 513a366704edf755 f05d9924c07044d3 bef2cb9cbea74c6c 8db948de4caa1f8a +philox4x64 10 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 16554d9eca36314c db20fe9d672d0fdc d7e772cee186176b 7e68b68aec7ba23b +philox4x64 10 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 87b092c3013fe90b 438c3c67be8d0224 9cc7d7c69cd777b6 a09caebf594f0ba0 +philox4x64 10 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c a528f45403e61d95 38c72dbd566e9788 a5a1610e72fd18b5 57bd43b5e52b7fe6 +# +threefry2x32 13 00000000 00000000 00000000 00000000 9d1c5ec6 8bd50731 +threefry2x32 13 ffffffff ffffffff ffffffff ffffffff fd36d048 2d17272c +threefry2x32 13 243f6a88 85a308d3 13198a2e 03707344 ba3e4725 f27d669e +threefry2x32 20 00000000 00000000 00000000 00000000 6b200159 99ba4efe +threefry2x32 20 ffffffff ffffffff ffffffff ffffffff 1cb996fc bb002be7 +threefry2x32 20 243f6a88 85a308d3 13198a2e 03707344 c4923a9c 483df7a0 +threefry2x32 32 00000000 00000000 00000000 00000000 cee3d47e a23dfd5c +threefry2x32 32 ffffffff ffffffff ffffffff ffffffff 6e2fe0d0 b1b76f82 +threefry2x32 32 243f6a88 85a308d3 13198a2e 03707344 e2827716 c3c05cdf +# +threefry4x32 13 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 531c7e4f 39491ee5 2c855a92 3d6abf9a +threefry4x32 13 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c4189358 1c9cc83a d5881c67 6a0a89e0 +threefry4x32 13 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 4aa71d8f 734738c2 431fc6a8 ae6debf1 +threefry4x32 20 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9c6ca96a e17eae66 fc10ecd4 5256a7d8 +threefry4x32 20 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 2a881696 57012287 f6c7446e a16a6732 +threefry4x32 20 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 59cd1dbb b8879579 86b5d00c ac8b6d84 +threefry4x32 72 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 93171da6 9220326d b392b7b1 ff58a002 +threefry4x32 72 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 60743f3d 9961e684 aab21c34 8c65fb7d +threefry4x32 72 243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 09930adf 7f27bd55 9ed68ce1 97f803f6 +# +threefry2x64 13 0000000000000000 0000000000000000 0000000000000000 0000000000000000 f167b032c3b480bd e91f9fee4b7a6fb5 +threefry2x64 13 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ccdec5c917a874b1 4df53abca26ceb01 +threefry2x64 13 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 c3aac71561042993 3fe7ae8801aff316 +threefry2x64 20 0000000000000000 0000000000000000 0000000000000000 0000000000000000 c2b6e3a8c2c69865 6f81ed42f350084d +threefry2x64 20 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff e02cb7c4d95d277a d06633d0893b8b68 +threefry2x64 20 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 263c7d30bb0f0af1 56be8361d3311526 +threefry2x64 32 0000000000000000 0000000000000000 0000000000000000 0000000000000000 38ba854d7f13cfb3 d02fca729d54fadc +threefry2x64 32 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 6b532f4f6e288646 0388f1ec135ee18e +threefry2x64 32 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 dad492f32efbd0c4 b6d7d0cd1f193e84 +# +threefry4x64 13 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 4071fabee1dc8e05 02ed3113695c9c62 397311b5b89f9d49 e21292c3258024bc +threefry4x64 13 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 7eaed935479722b5 90994358c429f31c 496381083e07a75b 627ed0d746821121 +threefry4x64 13 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c c0ac29b7c97c50dd 3f84d5b5b5470917 4361288ef9c1900c 8717291521782833 0d19db18c20cf47e a0b41d63ac8581e5 +threefry4x64 20 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 09218ebde6c85537 55941f5266d86105 4bd25e16282434dc ee29ec846bd2e40b + threefry4x64 20 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 29c24097942bba1b 0371bbfb0f6f4e11 3c231ffa33f83a1c cd29113fde32d168 +threefry4x64 20 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c be5466cf34e90c6c c0ac29b7c97c50dd a7e8fde591651bd9 baafd0c30138319b 84a5c1a729e685b9 901d406ccebc1ba4 +threefry4x64 72 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 94eeea8b1f2ada84 adf103313eae6670 952419a1f4b16d53 d83f13e63c9f6b11 +threefry4x64 72 ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffff 11518c034bc1ff4c 193f10b8bcdcc9f7 d024229cb58f20d8 563ed6e48e05183f +threefry4x64 72 243f6a8885a308d3 13198a2e03707344 a4093822299f31d0 082efa98ec4e6c89 452821e638d01377 be5466cf34e90c6c be5466cf34e90c6c c0ac29b7c97c50dd acf412ccaa3b2270 c9e99bd53f2e9173 43dad469dc825948 fbb19d06c8a2b4dc \ No newline at end of file diff --git a/test/test_basic_rng/r123_rngNxW.mm b/test/test_basic_rng/r123_rngNxW.mm new file mode 100644 index 00000000..5062522d --- /dev/null +++ b/test/test_basic_rng/r123_rngNxW.mm @@ -0,0 +1,57 @@ +/* +Copyright 2010-2011, D. E. Shaw Research. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of D. E. Shaw Research nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + We need this file of some crazy logic in test_random123.cc. + It very deliberately DOES NOT use the "#pragma once" directive. + + Please just ignore any warning messages from code linters or + integrated development environments like VSCode. They won't be + able to correctly infer how we intend to use this file. +*/ + +RNGNxW_TPL(philox, 2, 32) +RNGNxW_TPL(philox, 4, 32) +RNGNxW_TPL(threefry, 2, 32) +RNGNxW_TPL(threefry, 4, 32) +#if R123_USE_64BIT +#if R123_USE_PHILOX_64BIT +RNGNxW_TPL(philox, 2, 64) +RNGNxW_TPL(philox, 4, 64) +#endif +RNGNxW_TPL(threefry, 2, 64) +RNGNxW_TPL(threefry, 4, 64) +#endif +#if R123_USE_AES_NI +RNGNxW_TPL(ars, 4, 32) +RNGNxW_TPL(aesni, 4, 32) +#endif diff --git a/test/test_basic_rng/test_r123_kat.cc b/test/test_basic_rng/test_r123_kat.cc new file mode 100644 index 00000000..14e6517e --- /dev/null +++ b/test/test_basic_rng/test_r123_kat.cc @@ -0,0 +1,683 @@ +/* +Copyright 2010-2011, D. E. Shaw Research. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of D. E. Shaw Research nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LINESIZE 1024 + +#if R123_USE_AES_NI + int have_aesni = haveAESNI(); +#else + int have_aesni = 0; +#endif +int verbose = 0; +int debug = 0; + +/* strdup may or may not be in string.h, depending on the value + of the pp-symbol _XOPEN_SOURCE and other arcana. Just + do it ourselves. + Mnemonic: "ntcs" = "nul-terminated character string" */ +char *ntcsdup(const char *s){ + char *p = (char *)malloc(strlen(s)+1); + strcpy(p, s); + return p; +} + +// Functions to read a (portion of) a string in a given base and convert it to +// an unsigned integer. +// +// These functions differ from std::from_chars in how they handle white space. +// Specifically, they strip leading whitespace, and then they stop reading as +// soon as they reach a non-numeric character. (Note that the "a" in 257a3673 +// counts as a numeric character if we're reading in hexadecimal format.) +#ifdef _MSC_FULL_VER +#define strtoull _strtoui64 +#endif +uint32_t strtou32(const char *p, char **endp, int base){ + uint32_t ret; + errno = 0; + ret = strtoul(p, endp, base); + assert(errno==0); + return ret; +} +uint64_t strtou64(const char *p, char **endp, int base){ + uint64_t ret; + errno = 0; + ret = strtoull(p, endp, base); + assert(errno==0); + return ret; +} + +// A helper function to print unsigned integers in hexadecimal format, with leading zeros if necessary. +template +void prtu(std::ostream& os, T val) { + os << std::hex << std::setw(std::numeric_limits::digits / 4) << std::setfill('0') << val; + assert(!os.bad()); +} + +// Specializations for uint32_t and uint64_t +void prtu32(std::ostream& os, uint32_t v) { prtu(os, v); } +void prtu64(std::ostream& os, uint64_t v) { prtu(os, v); } + +#define PRINTARRAY(ARR, fp) \ +do { \ + char ofmt[64]; \ + size_t xj; \ + /* use %lu and the cast (instead of z) for portability to Microsoft, sizeof(v[0]) should fit easily in an unsigned long. Avoid inttypes for the same reason. */ \ + snprintf(ofmt, sizeof(ofmt), " %%0%lullx", (unsigned long)sizeof(ARR.v[0])*2UL); \ + for (xj = 0; xj < sizeof(ARR.v)/sizeof(ARR.v[0]); xj++) { \ + fprintf(fp, ofmt, (unsigned long long) ARR.v[xj]); \ + } \ +} while(0) + +#define PRINTLINE(NAME, N, W, R, ictr, ukey, octr, fp) \ +do { \ + fprintf(fp, "%s %d ", #NAME #N "x" #W, R); \ + PRINTARRAY(ictr, fp); \ + putc(' ', fp); \ + PRINTARRAY(ukey, fp); \ + putc(' ', fp); \ + PRINTARRAY(octr, fp); \ + putc('\n', fp); \ + fflush(fp); \ +} while(0) + +enum method_e{ +#define RNGNxW_TPL(base, N, W) base##N##x##W##_e, +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + last +}; + +#define RNGNxW_TPL(base, N, W) \ + typedef struct { \ + base##N##x##W##_ctr_t ctr; \ + base##N##x##W##_ukey_t ukey; \ + base##N##x##W##_ctr_t expected; \ + base##N##x##W##_ctr_t computed; \ + } base##N##x##W##_kat; +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + +typedef struct{ + enum method_e method; + unsigned nrounds; + union{ +#define RNGNxW_TPL(base, N, W) base##N##x##W##_kat base##N##x##W##_data; +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + /* Sigh... For those platforms that lack uint64_t, carve + out 128 bytes for the counter, key, expected, and computed. */ + char justbytes[128]; + }u; +} kat_instance; + +void host_execute_tests(kat_instance *tests, unsigned ntests); + +/* Keep track of the test vectors that we don't know how to deal with: */ +#define MAXUNKNOWNS 20 + +struct UnknownKatTracker { + int num_unknowns = 0; + const char *unknown_names[MAXUNKNOWNS]; + int unknown_counts[MAXUNKNOWNS]; +}; + +void register_unknown(UnknownKatTracker &ukt, const char *name){ + int i; + for(i=0; i< ukt.num_unknowns; ++i){ + if( strcmp(name, ukt.unknown_names[i]) == 0 ){ + ukt.unknown_counts[i]++; + return; + } + } + if( i >= MAXUNKNOWNS ){ + FAIL() << "Too many unknown rng types. Bye.\n"; + } + ukt.num_unknowns++; + ukt.unknown_names[i] = ntcsdup(name); + ukt.unknown_counts[i] = 1; +} + +/* read_NxW */ +#define RNGNxW_TPL(base, N, W) \ +int read_##base##N##x##W(const char *line, kat_instance* tinst){ \ + size_t i; \ + int nchar; \ + const char *p = line; \ + char *newp; \ + size_t nkey = sizeof(tinst->u.base##N##x##W##_data.ukey.v)/sizeof(tinst->u.base##N##x##W##_data.ukey.v[0]); \ + tinst->method = base##N##x##W##_e; \ + sscanf(p, "%u%n", &tinst->nrounds, &nchar); \ + p += nchar; \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i] = strtou##W(p, &newp, 16); \ + p = newp; \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i] = strtou##W(p, &newp, 16); \ + p = newp; \ + } \ + for(i=0; iu.base##N##x##W##_data.expected.v[i] = strtou##W(p, &newp, 16); \ + p = newp; \ + } \ + /* set the computed to 0xca. If the test fails to set computed, we'll see cacacaca in the FAILURE notices */ \ + memset(tinst->u.base##N##x##W##_data.computed.v, 0xca, sizeof(tinst->u.base##N##x##W##_data.computed.v)); \ + return 1; \ +} +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + + +/* readtest: dispatch to one of the read_NxW functions */ +static int readtest(UnknownKatTracker &ukt, const char *line, kat_instance* tinst){ + int nchar; + char name[LINESIZE]; + if( line[0] == '#') return 0; + sscanf(line, "%s%n", name, &nchar); + if(!have_aesni){ + /* skip any tests that require AESNI */ + if(strncmp(name, "aes", 3)==0 || + strncmp(name, "ars", 3)==0){ + register_unknown(ukt, name); + return 0; + } + } +#define RNGNxW_TPL(base, N, W) if(strcmp(name, #base #N "x" #W) == 0) return read_##base##N##x##W(line+nchar, tinst); +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + + register_unknown(ukt, name); + return 0; +} + +#define RNGNxW_TPL(base, N, W) \ +void report_##base##N##x##W##error(int &nfailed, const kat_instance *ti){ \ + size_t i; \ + size_t nkey = sizeof(ti->u.base##N##x##W##_data.ukey.v)/sizeof(ti->u.base##N##x##W##_data.ukey.v[0]); \ + std::stringstream ss; \ + ss << "FAIL: expected: "; \ + ss << #base #N "x" #W " " << ti->nrounds; \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.expected.v[i]); \ + } \ + ss << "\n"; \ + \ + ss << "FAIL: computed: "; \ + ss << #base #N "x" #W " " << ti->nrounds; \ + for(i=0; iu.base##N##x##W##_data.ctr.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.ukey.v[i]); \ + } \ + for(i=0; iu.base##N##x##W##_data.computed.v[i]); \ + } \ + ss << "\n"; \ + FAIL() << ss.str(); \ + nfailed++; \ +} +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + +// dispatch to one of the report_NxW() functions +void analyze_tests(int &nfailed, const kat_instance *tests, unsigned ntests){ + unsigned i; + char zeros[512] = {0}; + for(i=0; iu.base##N##x##W##_data.expected.v, N*W/8)==0){ \ + FAIL() << "kat expected all zeros? Something is wrong with the test harness!\n"; \ + nfailed++; \ + } \ + if (memcmp(ti->u.base##N##x##W##_data.computed.v, ti->u.base##N##x##W##_data.expected.v, N*W/8)) \ + report_##base##N##x##W##error(nfailed, ti); \ + break; +#include "r123_rngNxW.mm" +#undef RNGNxW_TPL + case last: ; + } + } +} + +#define NTESTS 1000 + +void run_base_rng_kat() { + kat_instance *tests; + unsigned t, ntests = NTESTS; + char linebuf[LINESIZE]; + FILE *inpfile; + const char *p; + const char *inname; + int nfailed = 0; + + UnknownKatTracker ukt{}; + + inname = "./kat_vectors.txt"; + inpfile = fopen(inname, "r"); + if (inpfile == NULL) + FAIL() << "Error opening input file " << inname << " for reading. Received error code " << errno << "\n"; + + if ((p = getenv("KATC_VERBOSE")) != NULL) + verbose = atoi(p); + + if ((p = getenv("KATC_DEBUG")) != NULL) + debug = atoi(p); + + tests = (kat_instance *) malloc(sizeof(tests[0])*ntests); + if (tests == NULL) { + FAIL() << "Could not allocate " << (unsigned long) ntests << " bytes for tests\n"; + } + t = 0; + while (fgets(linebuf, sizeof linebuf, inpfile) != NULL) { + if( t == ntests ) { + ntests *= 2; + tests = (kat_instance *)realloc(tests, sizeof(tests[0])*ntests); + if (tests == NULL) { + FAIL() << "Could not grow tests to " << (unsigned long) ntests << " bytes.\n"; + } + } + if( readtest(ukt, linebuf, &tests[t]) ) + ++t; + } + if(t==ntests){ + FAIL() << "No more space for tests? Recompile with a larger NTESTS\n"; + } + tests[t].method = last; // N.B *not* t++ - the 'ntests' value passed to host_execute_tests does not count the 'last' one. + + for(int i=0; i< ukt.num_unknowns; ++i){ + printf("%d test vectors of type %s skipped\n", ukt.unknown_counts[i], ukt.unknown_names[i]); + } + printf("Perform %lu tests.\n", (unsigned long)t); + host_execute_tests(tests, t); + + analyze_tests(nfailed, tests, t); + free(tests); + if(nfailed != 0) + FAIL() << "Failed " << nfailed << " out of " << t << std::endl; + return; +} + + + +// With C++, it's a little trickier to create the mapping from +// method-name/round-count to functions +// because the round-counts are template arguments that have to be +// specified at compile-time. Thus, we can't just do #define RNGNxW_TPL +// and #include "r123_rngNxW.mm". We have to build a static map from: +// pair to functions that apply the right generator +// with the right number of rounds. + +#ifdef _MSC_FULL_VER +// Engines have multiple copy constructors, quite legal C++, disable MSVC complaint +#pragma warning (disable : 4521) +#endif + +#include +#include +#include +#include +#include +#include + +using namespace std; + +typedef map, void (*)(kat_instance *)> genmap_t; +genmap_t genmap; + +void dev_execute_tests(kat_instance *tests, unsigned ntests){ + unsigned i; + for(i=0; imethod, ti->nrounds)); + if(p == genmap.end()) + throw std::runtime_error("pair not in map. You probably need to add more genmap entries in kat_cpp.cpp"); + + p->second(ti); + // TODO: check that the corresponding Engine and MicroURNG + // return the same values. Note that we have ut_Engine and + // ut_MicroURNG, which check basic functionality, but they + // don't have the breadth of the kat_vectors. + } +} + +static int murng_reported; +static int engine_reported; + +template +void do_test(kat_instance* ti){ + GEN g; + struct gdata{ + typename GEN::ctr_type ctr; + typename GEN::ukey_type ukey; + typename GEN::ctr_type expected; + typename GEN::ctr_type computed; + }; + gdata data; + // use memcpy. A reinterpret_cast would violate strict aliasing. + memcpy(&data, &ti->u, sizeof(data)); + data.computed = g(data.ctr, data.ukey); + + // Before we return, let's make sure that MicroURNG and + // Engine work as expeccted. This doesn't really "fit" the + // execution model of kat.c, which just expects us to fill in + // ti->u.computed, so we report the error by failing to write back + // the computed data item in the (hopefully unlikely) event that + // things don't match up as expected. + int errs = 0; + + // MicroURNG: throws if the top 32 bits of the high word of ctr + // are non-zero. + typedef typename GEN::ctr_type::value_type value_type; + + value_type hibits = data.ctr[data.ctr.size()-1]>>( std::numeric_limits::digits - 32 ); + try{ + r123::MicroURNG urng(data.ctr, data.ukey); + if(hibits) + errs++; // Should have thrown. + for (size_t i = 0; i < data.expected.size(); i++) { + size_t j = data.expected.size() - i - 1; + if (data.expected[j] != urng()) { + errs++; + } + } + }catch(std::runtime_error& /*ignored*/){ + // A runtime_error is expected from the constructor + // when hibit is set. + if(!hibits) + errs++; + } + if(errs && (murng_reported++ == 0)) + cerr << "Error in MicroURNG, will appear as \"computed\" value of zero in error summary\n"; + + // Engine + // N.B. exercising discard() arguably belongs in ut_Engine.cpp + typedef r123::Engine Etype; + typedef typename GEN::ctr_type::value_type value_type; + Etype e(data.ukey); + typename GEN::ctr_type c = data.ctr; + value_type c0; + if( c[0] > 0 ){ + c0 = c[0]-1; + }else{ + // N.B. Assume that if c[0] is 0, then so are all the + // others. Arrange to "roll over" to {0,..,0} on the first + // counter-increment. Alternatively, we could just + // skip the test for this case... + c.fill(std::numeric_limits::max()); + c0 = c[0]; + } + c[0] /= 3; + e.setcounter(c, 0); + if( c0 > c[0] ){ + // skip one value by calling e() + (void)e(); + if (c0 > c[0]+1) { + // skip many values by calling discard() + R123_ULONG_LONG ndiscard = (c0 - c[0] - 1); + // Take care not to overflow the long long + if( ndiscard >= std::numeric_limits::max() / c.size() ){ + for(size_t j=0; j, will appear as \"computed\" value of zero in error summary\n"; + } + } + + // Signal an error to the caller by *not* copying back + // the computed data object into the ti + if(errs == 0) + memcpy(&ti->u, &data, sizeof(data)); +} + +void host_execute_tests(kat_instance *tests, unsigned ntests){ + // In C++1x, this could be staticly declared with an initializer list. + genmap[make_pair(threefry2x32_e, 13u)] = do_test >; + genmap[make_pair(threefry2x32_e, 20u)] = do_test >; + genmap[make_pair(threefry2x32_e, 32u)] = do_test >; +#if R123_USE_64BIT + genmap[make_pair(threefry2x64_e, 13u)] = do_test >; + genmap[make_pair(threefry2x64_e, 20u)] = do_test >; + genmap[make_pair(threefry2x64_e, 32u)] = do_test >; +#endif + + genmap[make_pair(threefry4x32_e, 13u)] = do_test >; + genmap[make_pair(threefry4x32_e, 20u)] = do_test >; + genmap[make_pair(threefry4x32_e, 72u)] = do_test >; +#if R123_USE_64BIT + genmap[make_pair(threefry4x64_e, 13u)] = do_test >; + genmap[make_pair(threefry4x64_e, 20u)] = do_test >; + genmap[make_pair(threefry4x64_e, 72u)] = do_test >; +#endif + + genmap[make_pair(philox2x32_e, 7u)] = do_test >; + genmap[make_pair(philox2x32_e, 10u)] = do_test >; + genmap[make_pair(philox4x32_e, 7u)] = do_test >; + genmap[make_pair(philox4x32_e, 10u)] = do_test >; + +#if R123_USE_PHILOX_64BIT + genmap[make_pair(philox2x64_e, 7u)] = do_test >; + genmap[make_pair(philox2x64_e, 10u)] = do_test >; + genmap[make_pair(philox4x64_e, 7u)] = do_test >; + genmap[make_pair(philox4x64_e, 10u)] = do_test >; +#endif + +#if R123_USE_AES_NI + genmap[make_pair(aesni4x32_e, 10u)] = do_test; + genmap[make_pair(ars4x32_e, 7u)] = do_test >; + genmap[make_pair(ars4x32_e, 10u)] = do_test >; +#endif + + dev_execute_tests(tests, ntests); +} + + +using namespace r123; + +template +typename r123::make_unsigned::type U(T x){ return x; } + +template +typename r123::make_signed::type S(T x){ return x; } + +#define Chk(u, Rng, Ftype, _nfail_, _refhist_) do{ \ + chk(#u, #Rng, #Ftype, &u, _nfail_, _refhist_); \ + }while(0) + +std::map get_refmap(){ + using std::string; + std::map refmap{}; + refmap[string("u01 Threefry4x32 float")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01 Threefry4x32 double")]= string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01 Threefry4x32 long double")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01 Threefry4x64 float")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01 Threefry4x64 double")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01 Threefry4x64 long double")] = string(" 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("uneg11 Threefry4x32 float")] = string(" 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); + refmap[string("uneg11 Threefry4x32 double")] = string(" 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); + refmap[string("uneg11 Threefry4x32 long double")] = string( " 156 139 148 146 159 148 159 168 142 160 156 161 153 143 158 150 180 174 152 163 157 129 166 151 140 142"); + refmap[string("uneg11 Threefry4x64 float")] = string( " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); + refmap[string("uneg11 Threefry4x64 double")] = string( " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); + refmap[string("uneg11 Threefry4x64 long double")] = string( " 159 141 148 184 162 142 155 137 173 187 153 140 135 164 144 146 149 151 171 152 148 137 179 146 145 152"); + refmap[string("u01fixedpt Threefry4x32 float")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01fixedpt Threefry4x32 double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01fixedpt Threefry4x32 long double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 301 330 326 320 295 291 298 287 305 307 310 316 314"); + refmap[string("u01fixedpt Threefry4x64 float")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01fixedpt Threefry4x64 double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + refmap[string("u01fixedpt Threefry4x64 long double")] = string( " 0 0 0 0 0 0 0 0 0 0 0 0 0 308 295 322 300 316 291 311 289 346 297 310 340 275"); + return refmap; +} + +template +void chk(const std::string& fname, const std::string& rngname, const std::string& ftypename, Utype f, int &nfail, std::map &refmap){ + std::string key = fname + " " + rngname + " " + ftypename; + RNG rng; + typedef typename RNG::ukey_type ukey_type; + typedef typename RNG::ctr_type ctr_type; + typedef typename RNG::key_type key_type; + + ctr_type c = {{}}; + ukey_type uk = {{}}; + key_type k = uk; + // 26 bins - 13 greater than 0 and 13 less. Why 13? Because a + // prime number seems less likely to tickle the rounding-related + // corner cases, which is aruably both good and bad. + const int NBINS=26; + + int hist[NBINS] = {}; + for(int i=0; i<1000; ++i){ + c = c.incr(); + ctr_type r = rng(c, k); + for(int j=0; j= -1.); + R123_ASSERT( u <= 1.); + int idx = (int) ((u + Ftype(1.))*Ftype(NBINS/2)); + hist[idx]++; + } + } + std::ostringstream oss; + for(int i=0; i