File indexing completed on 2026-10-26 09:34:38
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #pragma once
0018
0019 #include "corecel/cont/Range.hh"
0020 #include "corecel/cont/Span.hh"
0021 #include "corecel/math/NumericLimits.hh"
0022 #include "corecel/random/data/RanluxppTypes.hh"
0023
0024 namespace celeritas
0025 {
0026 namespace detail
0027 {
0028
0029
0030 inline CELER_FUNCTION RanluxppUInt add_overflow(
0031 RanluxppUInt a, RanluxppUInt b, unsigned int& overflow);
0032
0033
0034 inline CELER_FUNCTION RanluxppUInt add_carry(
0035 RanluxppUInt a, RanluxppUInt b, unsigned int& carry);
0036
0037
0038 inline CELER_FUNCTION RanluxppUInt sub_overflow(
0039 RanluxppUInt a, RanluxppUInt b, unsigned int& overflow);
0040
0041
0042 inline CELER_FUNCTION RanluxppUInt sub_carry(
0043 RanluxppUInt a, RanluxppUInt b, unsigned int& carry);
0044
0045
0046 inline CELER_FUNCTION int64_t compute_remainder(
0047 Span<RanluxppUInt const, 9> upper, Span<RanluxppUInt, 9> r);
0048
0049
0050 [[nodiscard]] inline CELER_FUNCTION RanluxppArray18 multiply_9x9(
0051 RanluxppArray9 const& in1, RanluxppArray9 const& in2);
0052
0053
0054 [[nodiscard]] inline CELER_FUNCTION RanluxppArray9 compute_modulus(
0055 RanluxppArray18 const& mul);
0056
0057
0058 [[nodiscard]] inline CELER_FUNCTION RanluxppArray9 compute_mod_multiply(
0059 RanluxppArray9 const& factor1, RanluxppArray9 const& factor2);
0060
0061
0062 [[nodiscard]] inline CELER_FUNCTION RanluxppArray9 compute_power_exp_modulus(
0063 RanluxppArray9 base, RanluxppUInt log_n);
0064
0065
0066 [[nodiscard]] inline CELER_FUNCTION RanluxppArray9 compute_power_modulus(
0067 RanluxppArray9 base, RanluxppUInt n);
0068
0069
0070 [[nodiscard]] inline CELER_FUNCTION RanluxppArray9 to_lcg(
0071 RanluxppNumber const& ranlux);
0072
0073
0074 [[nodiscard]] inline CELER_FUNCTION RanluxppNumber to_ranlux(
0075 RanluxppArray9 const& lcg);
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089 CELER_FUNCTION RanluxppUInt add_overflow(
0090 RanluxppUInt a, RanluxppUInt b, unsigned int& overflow)
0091 {
0092 RanluxppUInt add = a + b;
0093 overflow = (add < a);
0094 return add;
0095 }
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108 CELER_FUNCTION RanluxppUInt add_carry(
0109 RanluxppUInt a, RanluxppUInt b, unsigned int& carry)
0110 {
0111 unsigned int overflow;
0112 RanluxppUInt add = add_overflow(a, b, overflow);
0113
0114
0115
0116 carry += overflow;
0117 return add;
0118 }
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130 CELER_FUNCTION RanluxppUInt sub_overflow(
0131 RanluxppUInt a, RanluxppUInt b, unsigned int& overflow)
0132 {
0133 RanluxppUInt sub = a - b;
0134 overflow = (sub > a);
0135 return sub;
0136 }
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149 CELER_FUNCTION RanluxppUInt sub_carry(
0150 RanluxppUInt a, RanluxppUInt b, unsigned int& carry)
0151 {
0152 unsigned int overflow;
0153 RanluxppUInt sub = sub_overflow(a, b, overflow);
0154
0155
0156
0157 carry += overflow;
0158 return sub;
0159 }
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171 CELER_FUNCTION int64_t compute_remainder(Span<RanluxppUInt const, 9> upper,
0172 Span<RanluxppUInt, 9> r)
0173 {
0174
0175 unsigned int carry = 0;
0176 for (int i : celeritas::range(9))
0177 {
0178 RanluxppUInt r_i = r[i];
0179 r_i = sub_overflow(r_i, carry, carry);
0180
0181 RanluxppUInt t1_i = upper[i];
0182 r_i = sub_carry(r_i, t1_i, carry);
0183 r[i] = r_i;
0184 }
0185 int64_t c = -(static_cast<int64_t>(carry));
0186
0187
0188 carry = 0;
0189 for (int i : celeritas::range(9))
0190 {
0191 RanluxppUInt r_i = r[i];
0192 r_i = sub_overflow(r_i, carry, carry);
0193
0194 RanluxppUInt t2_bits = 0;
0195 if (i < 4)
0196 {
0197 t2_bits += upper[i + 5] >> 16;
0198 if (i < 3)
0199 {
0200 t2_bits += upper[i + 6] << 48;
0201 }
0202 }
0203 r_i = sub_carry(r_i, t2_bits, carry);
0204 r[i] = r_i;
0205 }
0206 c -= carry;
0207
0208
0209 carry = 0;
0210 {
0211 RanluxppUInt r_3 = r[3];
0212
0213 RanluxppUInt t2_bits = (upper[5] >> 16) << 48;
0214 RanluxppUInt t3_bits = (upper[0] << 48);
0215
0216 r_3 = add_carry(r_3, t2_bits, carry);
0217 r_3 = add_carry(r_3, t3_bits, carry);
0218
0219 r[3] = r_3;
0220 }
0221 for (int i : celeritas::range(3))
0222 {
0223 RanluxppUInt r_i = r[i + 4];
0224 r_i = add_overflow(r_i, carry, carry);
0225
0226 RanluxppUInt t2_bits = (upper[5 + i] >> 32) + (upper[6 + i] << 32);
0227 RanluxppUInt t3_bits = (upper[i] >> 16) + (upper[1 + i] << 48);
0228
0229 r_i = add_carry(r_i, t2_bits, carry);
0230 r_i = add_carry(r_i, t3_bits, carry);
0231
0232 r[i + 4] = r_i;
0233 }
0234 {
0235 RanluxppUInt r_7 = r[7];
0236 r_7 = add_overflow(r_7, carry, carry);
0237
0238 RanluxppUInt t2_bits = (upper[8] >> 32);
0239 RanluxppUInt t3_bits = (upper[3] >> 16) + (upper[4] << 48);
0240
0241 r_7 = add_carry(r_7, t2_bits, carry);
0242 r_7 = add_carry(r_7, t3_bits, carry);
0243
0244 r[7] = r_7;
0245 }
0246 {
0247 RanluxppUInt r_8 = r[8];
0248 r_8 = add_overflow(r_8, carry, carry);
0249
0250 RanluxppUInt t3_bits = (upper[4] >> 16) + (upper[5] << 48);
0251
0252 r_8 = add_carry(r_8, t3_bits, carry);
0253
0254 r[8] = r_8;
0255 }
0256 c += carry;
0257
0258
0259
0260
0261
0262
0263 bool greater_m = r[0] | r[1] | r[2] | (r[3] & 0x0000ffffffffffffull);
0264 greater_m &= (r[3] >> 48) == 0xffff;
0265 for (int i : celeritas::range(4, 9))
0266 {
0267 greater_m &= (r[i] == celeritas::numeric_limits<RanluxppUInt>::max());
0268 }
0269 return c + (c == 0 && greater_m);
0270 }
0271
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281 CELER_FUNCTION RanluxppArray18 multiply_9x9(RanluxppArray9 const& in1,
0282 RanluxppArray9 const& in2)
0283 {
0284 RanluxppArray18 result;
0285
0286 RanluxppUInt next = 0;
0287 unsigned int nextCarry = 0;
0288
0289 #if defined(__clang__) || defined(__INTEL_COMPILER) || defined(__CUDA_ARCH__)
0290 # pragma unroll
0291 #elif defined(__GNUC__) && __GNUC__ >= 8 && !defined(__CUDACC__)
0292
0293 # pragma GCC unroll 18
0294 #endif
0295 for (int i = 0; i < 18; ++i)
0296 {
0297 RanluxppUInt current = next;
0298 unsigned int carry = nextCarry;
0299
0300 next = 0;
0301 nextCarry = 0;
0302
0303 #if defined(__clang__) || defined(__INTEL_COMPILER) || defined(__CUDA_ARCH__)
0304 # pragma unroll
0305 #elif defined(__GNUC__) && __GNUC__ >= 8 && !defined(__CUDACC__)
0306
0307 # pragma GCC unroll 9
0308 #endif
0309 for (int j = 0; j < 9; ++j)
0310 {
0311 int k = i - j;
0312 if (k < 0 || k >= 9)
0313 {
0314 continue;
0315 }
0316 RanluxppUInt fac1 = in1[j];
0317 RanluxppUInt fac2 = in2[k];
0318 #if defined(__CUDA_ARCH__)
0319
0320
0321
0322 RanluxppUInt lower = fac1 * fac2;
0323 RanluxppUInt upper = __umul64hi(fac1, fac2);
0324 #elif defined(__SIZEOF_INT128__)
0325 # ifdef __GNUC__
0326
0327
0328
0329 # pragma GCC diagnostic push
0330 # pragma GCC diagnostic ignored "-Wpedantic"
0331 using uint128 = unsigned __int128;
0332 # pragma GCC diagnostic pop
0333 # else
0334 using uint128 = unsigned __int128;
0335 # endif
0336 uint128 prod = fac1;
0337 prod = prod * fac2;
0338
0339 RanluxppUInt upper = prod >> 64;
0340 RanluxppUInt lower = static_cast<RanluxppUInt>(prod);
0341 #else
0342 RanluxppUInt upper1 = fac1 >> 32;
0343 RanluxppUInt lower1 = static_cast<uint32_t>(fac1);
0344
0345 RanluxppUInt upper2 = fac2 >> 32;
0346 RanluxppUInt lower2 = static_cast<uint32_t>(fac2);
0347
0348
0349
0350 RanluxppUInt upper = upper1 * upper2;
0351 RanluxppUInt middle1 = upper1 * lower2;
0352 RanluxppUInt middle2 = lower1 * upper2;
0353 RanluxppUInt lower = lower1 * lower2;
0354
0355
0356
0357 unsigned int overflow;
0358 RanluxppUInt middle = add_overflow(middle1, middle2, overflow);
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370 RanluxppUInt overflow_add = overflow
0371 * (static_cast<RanluxppUInt>(1) << 32);
0372
0373
0374
0375
0376
0377 upper += overflow_add;
0378
0379 RanluxppUInt middle_upper = middle >> 32;
0380 RanluxppUInt middle_lower = middle << 32;
0381
0382 lower = add_overflow(lower, middle_lower, overflow);
0383 upper += overflow;
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400 upper += middle_upper;
0401 #endif
0402
0403
0404 current = add_carry(current, lower, carry);
0405
0406
0407 next = add_carry(next, upper, nextCarry);
0408 }
0409
0410 next = add_carry(next, carry, nextCarry);
0411 result[i] = current;
0412 }
0413
0414 return result;
0415 }
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427 CELER_FUNCTION RanluxppArray9 compute_modulus(RanluxppArray18 const& mul)
0428 {
0429 RanluxppArray9 result;
0430
0431 RanluxppArray9 r;
0432 for (auto i : celeritas::range(0, 9))
0433 {
0434 r[i] = mul[i];
0435 }
0436
0437
0438 auto mul_end = Span{mul}.subspan<9, 9>();
0439 static_assert(mul_end.size() == 9);
0440
0441 int64_t c = compute_remainder(mul_end, r);
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462 int64_t t0 = c >> 1;
0463
0464
0465 int64_t t2 = t0 - (c << 48);
0466
0467
0468
0469 int64_t t1 = t2 >> 48;
0470
0471 unsigned int carry = 0;
0472 {
0473 RanluxppUInt r_0 = r[0];
0474
0475 RanluxppUInt out_0 = sub_carry(r_0, c, carry);
0476 result[0] = out_0;
0477 }
0478 for (int i : celeritas::range(1, 3))
0479 {
0480 RanluxppUInt r_i = r[i];
0481 r_i = sub_overflow(r_i, carry, carry);
0482
0483 RanluxppUInt out_i = sub_carry(r_i, t0, carry);
0484 result[i] = out_i;
0485 }
0486 {
0487 RanluxppUInt r_3 = r[3];
0488 r_3 = sub_overflow(r_3, carry, carry);
0489
0490 RanluxppUInt out_3 = sub_carry(r_3, t2, carry);
0491 result[3] = out_3;
0492 }
0493 for (int i : celeritas::range(4, 9))
0494 {
0495 RanluxppUInt r_i = r[i];
0496 r_i = sub_overflow(r_i, carry, carry);
0497
0498 RanluxppUInt out_i = sub_carry(r_i, t1, carry);
0499 result[i] = out_i;
0500 }
0501
0502 return result;
0503 }
0504
0505
0506
0507
0508
0509
0510
0511
0512
0513
0514 CELER_FORCEINLINE_FUNCTION RanluxppArray9 compute_mod_multiply(
0515 RanluxppArray9 const& factor1, RanluxppArray9 const& factor2)
0516 {
0517 RanluxppArray18 mul = multiply_9x9(factor1, factor2);
0518 return compute_modulus(mul);
0519 }
0520
0521
0522
0523
0524
0525
0526
0527
0528 CELER_FUNCTION RanluxppArray9 compute_power_exp_modulus(RanluxppArray9 result,
0529 RanluxppUInt log_n)
0530 {
0531 for (; log_n > 0; --log_n)
0532 {
0533 result = compute_mod_multiply(result, result);
0534 }
0535 return result;
0536 }
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547 CELER_FUNCTION RanluxppArray9 compute_power_modulus(RanluxppArray9 base,
0548 RanluxppUInt n)
0549 {
0550
0551 RanluxppArray9 res = {1, 0, 0, 0, 0, 0, 0, 0, 0};
0552
0553 while (n)
0554 {
0555 if (n & 1)
0556 {
0557 res = compute_mod_multiply(res, base);
0558 }
0559 n >>= 1;
0560 if (!n)
0561 {
0562 break;
0563 }
0564 base = compute_mod_multiply(base, base);
0565 }
0566
0567 return res;
0568 }
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579 CELER_FUNCTION RanluxppArray9 to_lcg(RanluxppNumber const& ranlux)
0580 {
0581 RanluxppArray9 result;
0582
0583 RanluxppArray9 const& number = ranlux.number;
0584 unsigned int c = ranlux.carry;
0585
0586
0587 unsigned int carry = 0;
0588 for (int i : celeritas::range(9))
0589 {
0590 RanluxppUInt ranlux_i = number[i];
0591 RanluxppUInt lcg_i = sub_overflow(ranlux_i, carry, carry);
0592
0593 RanluxppUInt bits = 0;
0594 if (i < 4)
0595 {
0596 bits += number[i + 5] >> 16;
0597 if (i < 3)
0598 {
0599 bits += number[i + 6] << 48;
0600 }
0601 }
0602 lcg_i = sub_carry(lcg_i, bits, carry);
0603 result[i] = lcg_i;
0604 }
0605
0606
0607 for (RanluxppUInt& lcg_val : result)
0608 {
0609 lcg_val = add_overflow(lcg_val, c, c);
0610 }
0611
0612 return result;
0613 }
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625 CELER_FUNCTION RanluxppNumber to_ranlux(RanluxppArray9 const& lcg)
0626 {
0627 RanluxppNumber result;
0628 int64_t c = compute_remainder(lcg, result.number);
0629
0630
0631 unsigned int carry = 0;
0632 for (int i : celeritas::range(9))
0633 {
0634 RanluxppUInt in_i = lcg[i];
0635 RanluxppUInt tmp_i = add_overflow(in_i, carry, carry);
0636
0637 RanluxppUInt bits = 0;
0638 if (i < 4)
0639 {
0640 bits += lcg[i + 5] >> 16;
0641 if (i < 3)
0642 {
0643 bits += lcg[i + 6] << 48;
0644 }
0645 }
0646 tmp_i = add_carry(tmp_i, bits, carry);
0647 result.number[i] = tmp_i;
0648 }
0649
0650
0651 int64_t c1 = c >> 1;
0652 result.number[0] = add_overflow(result.number[0], c, carry);
0653 for (int i : celeritas::range(1, 9))
0654 {
0655 RanluxppUInt ranlux_i = result.number[i];
0656 add_overflow(ranlux_i, carry, carry);
0657 add_carry(ranlux_i, c1, carry);
0658 }
0659
0660 result.carry = carry;
0661
0662 return result;
0663 }
0664
0665
0666 }
0667 }