/* * Copyright (c) 2014 Mellanox Technologies, Inc. * All rights reserved. * Copyright (c) 2016 Research Organization for Information Science * and Technology (RIST). All rights reserved. * Copyright (c) 2016-2020 Intel, Inc. All rights reserved. * Copyright (c) 2021 Nanook Consulting. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ #include "src/include/pmix_config.h" #include #include "pmix_alfg.h" /* Mask corresponding to the primitive polynomial *--------------------------------------------------- * * p(x) = 1 + x^25 + x^27 + x^29 + x^30 + x^31 + x^32 * *--------------------------------------------------- */ #define MASK 0x80000057U /* Additive lagged Fibonacci parameters: *--------------------------------------------------- * * x_n = (x_(n - TAP1) + x_(n - TAP2) ) mod M * *--------------------------------------------------- */ #define TAP1 127 #define TAP2 97 #define CBIT 21 /* Canonical bit */ /** * @brief Galois shift register: Used to seed the ALFG's * canonical rectangle * * @param[in] unsigned int *seed: used to seed the Galois register * @param[out] uint32_t lsb: least significant bit of the Galois * register after shift */ static uint32_t galois(unsigned int *seed) { uint32_t lsb; lsb = (*seed & 1) ? 1 : 0; *seed >>= 1; /* tap it with the mask */ *seed = *seed ^ (lsb * MASK); return lsb; } /* PMIX global rng buffer */ static pmix_rng_buff_t alfg_buffer; /** * @brief Routine to seed the ALFG register * * @param[in] uint32_t seed * @param[out] pmix_rng_buff_t *buff: handle to ALFG buffer state */ int pmix_srand(pmix_rng_buff_t *buff, uint32_t seed) { int i, j; uint32_t seed_cpy = seed; buff->tap1 = TAP1 - 1; buff->tap2 = TAP2 - 1; /* zero out the register */ for (i = 0; i < TAP1; i++) { buff->alfg[i] = 0; } /* set the canonical bit */ buff->alfg[CBIT] = 1; /* seed the ALFG register by blasting * the canonical rectangle with bits */ for (j = 1; j < TAP1; j++) { for (i = 1; i < 32; i++) { buff->alfg[j] = buff->alfg[j] ^ ((galois(&seed_cpy)) << i); } } /* copy the ALFG to the global buffer */ memcpy(&alfg_buffer, buff, sizeof(alfg_buffer)); return 1; } /** * @brief The additive lagged Fibonnaci PRNG * * @param[in] pmix_rng_buff_t *buff: handle to ALFG buffer state * @param[out] 32-bit unsigned random integer */ uint32_t pmix_rand(pmix_rng_buff_t *buff) { int *tap1 = &(buff->tap1); int *tap2 = &(buff->tap2); uint64_t overflow; uint32_t temp; /* prevent overflow */ overflow = (uint64_t) buff->alfg[*tap1] + (uint64_t) buff->alfg[*tap2]; /* circular buffer arithmetic */ temp = (*tap1 + 1) == TAP1 ? 0 : (*tap1 + 1); /* Division modulo 2^32 */ buff->alfg[temp] = (uint32_t)(overflow & ((1ULL << 32) - 1)); /* increment tap points */ *tap1 = (*tap1 + 1) % TAP1; *tap2 = (*tap2 + 1) % TAP1; return buff->alfg[temp]; } /** * @brief A wrapper for pmix_rand() with our global ALFG buffer; * * @param[in] none * @param[out] int, the same as normal rand(3) */ int pmix_random(void) { /* always return a positive int */ return (int) (pmix_rand(&alfg_buffer) & 0x7FFFFFFF); }