ver1-bitmask.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #define _GNU_SOURCE
  2. #include <assert.h>
  3. #include <limits.h>
  4. #include <stdbool.h>
  5. #include <stdio.h>
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <linux/random.h>
  10. #include <sys/syscall.h>
  11. #include <sys/time.h>
  12. #include <gmp.h>
  13. #define INIT_TIMEIT() \
  14. struct timeval __start, __end; \
  15. double __sdiff = 0, __udiff = 0
  16. #define START_TIMEIT() \
  17. gettimeofday(&__start, NULL)
  18. #define END_TIMEIT() \
  19. gettimeofday(&__end, NULL); \
  20. __sdiff += (__end.tv_sec - __start.tv_sec); \
  21. __udiff += (__end.tv_usec - __start.tv_usec)
  22. #define GET_TIMEIT() \
  23. __sdiff + __udiff * 1e-6
  24. #define TIMEIT_FORMAT "%lf"
  25. typedef __uint128_t uint128_t;
  26. /**
  27. * p is our prime modulus, and is 2^n - g
  28. * where g is referred to as "gamma" (built-in function in C, so transliterated)
  29. */
  30. const static char* p_str =
  31. "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
  32. "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
  33. "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
  34. "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
  35. "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
  36. "505CAF";
  37. const static char* g_str =
  38. "11510609";
  39. mpz_t p, g;
  40. uint32_t gg = 11510609;
  41. static uint8_t lookup[256];
  42. static uint8_t offset[256];
  43. static inline ssize_t
  44. getrandom(void *buffer, size_t length, unsigned int flags)
  45. {
  46. return syscall(SYS_getrandom, buffer, length, flags);
  47. }
  48. INIT_TIMEIT();
  49. #define strip_size 16
  50. #define halfstrip_size ((strip_size)/2)
  51. static inline
  52. void add_1(uint64_t *b, const size_t start, const size_t len, uint64_t a)
  53. {
  54. for (size_t i = start; a != 0; i = (i+1)%len) {
  55. const uint64_t r = b[i] + a;
  56. a = (r < a);
  57. b[i] = r;
  58. }
  59. /*
  60. we don't check for overflows in "i".
  61. If it happens, buy a lottery ticket and retry.
  62. */
  63. }
  64. uint32_t convert(uint64_t * nn)
  65. {
  66. static const uint64_t topmask = ~(ULLONG_MAX >> halfstrip_size);
  67. static const uint64_t topbigmask = ~(ULLONG_MAX >> strip_size);
  68. static const uint64_t bottommask = (0x01 << halfstrip_size) -1;
  69. uint32_t w;
  70. uint32_t steps;
  71. size_t head = 23;
  72. #define next_head ((head + 23) % 24)
  73. #define tail ((head + 1) % 24)
  74. #define next_tail ((head + 2) % 24)
  75. #define distinguished(x) (((x)[head] & topbigmask)) == 0
  76. /** Check the most significant block */
  77. const uint64_t x = nn[head];
  78. for (uint32_t w2 = halfstrip_size; w2 < 64-halfstrip_size; w2 += halfstrip_size) {
  79. if (!(x & (topmask >> w2))) {
  80. for (w = w2-1; !(x & (topmask >> w)); w--);
  81. ++w;
  82. if (!(x & (topbigmask >> w))) return w;
  83. }
  84. }
  85. for (steps = 64; !distinguished(nn); steps += 64) {
  86. const uint64_t x = nn[head];
  87. const uint64_t y = nn[next_head];
  88. if (!(x & bottommask)) {
  89. const size_t previous = (x >> halfstrip_size) & bottommask;
  90. const uint8_t next = y >> (64 - halfstrip_size);
  91. if (next <= lookup[previous]) return steps - halfstrip_size - offset[previous];
  92. }
  93. if (!(y & topmask)) {
  94. const size_t previous = x & bottommask;
  95. const uint8_t next = (y >> (64 - 2*halfstrip_size)) & bottommask;
  96. if (next <= lookup[previous]) return steps - offset[previous];
  97. }
  98. for (uint32_t w2 = halfstrip_size; w2 < 64-halfstrip_size; w2 += halfstrip_size) {
  99. if (!(y & (topmask >> w2))) {
  100. for (w = w2-1; !(y & (topmask >> w)); w--);
  101. ++w;
  102. if (!(y & (topbigmask >> w))) return steps + w;
  103. }
  104. }
  105. /**
  106. * We found no distinguished point.
  107. */
  108. const uint128_t a = (uint128_t) x * gg;
  109. const uint64_t al = (uint64_t) a;
  110. const uint64_t ah = (a >> 64);
  111. head = next_head;
  112. nn[tail] = al;
  113. add_1(nn, next_tail, 24, ah);
  114. }
  115. return steps;
  116. }
  117. uint32_t naif_convert(mpz_t n)
  118. {
  119. uint32_t i;
  120. mpz_t t;
  121. mpz_init_set_ui(t, 1);
  122. mpz_mul_2exp(t, t, 1536-strip_size);
  123. for (i = 0; mpz_cmp(n, t) > -1; i++) {
  124. mpz_mul_2exp(n, n, 1);
  125. mpz_mod(n, n, p);
  126. }
  127. mpz_clear(t);
  128. return i;
  129. }
  130. int main()
  131. {
  132. mpz_init_set_str(p, p_str, 0);
  133. mpz_init_set_str(g, g_str, 10);
  134. gmp_randstate_t _rstate;
  135. unsigned long int _rseed;
  136. gmp_randinit_default(_rstate);
  137. getrandom(&_rseed, sizeof(unsigned long int), GRND_NONBLOCK);
  138. gmp_randseed_ui(_rstate, _rseed);
  139. for (size_t i = 0; i <= 0xFF; i++) {
  140. uint32_t j = ffs(i) ? ffs(i) - 1 : 8;
  141. lookup[i] = 0xFF >> (8-j);
  142. offset[i] = j;
  143. }
  144. uint32_t converted;
  145. for (int i=0; i < (int) 15258; i++) {
  146. mpz_t n, n0;
  147. mpz_inits(n, n0, NULL);
  148. mpz_urandomm(n0, _rstate, p);
  149. mpz_set(n, n0);
  150. START_TIMEIT();
  151. converted = convert(n->_mp_d);
  152. END_TIMEIT();
  153. mpz_set(n, n0);
  154. #ifndef NDEBUG
  155. uint32_t expected = naif_convert(n);
  156. //printf("%d %d\n", converted, expected);
  157. assert(converted == expected);
  158. #endif
  159. mpz_clears(n, n0, NULL);
  160. }
  161. printf(TIMEIT_FORMAT "\n", GET_TIMEIT());
  162. mpz_clears(p, g, NULL);
  163. return 0;
  164. }