#include <stdint.h>
#include <tuple>
#include <iostream>
typedef std::tuple< uint32_t, uint32_t > split_t;
split_t split( uint64_t a )
{
static const uint32_t mask = -1;
auto retval = std::make_tuple( mask&a, ( a >> 32 ) );
// std::cout << "(" << std::get<0>(retval) << "," << std::get<1>(retval) << ")\n";
return retval;
}
typedef std::tuple< uint64_t, uint64_t, uint64_t, uint64_t > cross_t;
template<typename Lambda>
cross_t cross( split_t lhs, split_t rhs, Lambda&& op )
{
return std::make_tuple(
op(std::get<0>(lhs), std::get<0>(rhs)),
op(std::get<1>(lhs), std::get<0>(rhs)),
op(std::get<0>(lhs), std::get<1>(rhs)),
op(std::get<1>(lhs), std::get<1>(rhs))
);
}
// a and c must have high bit unset:
uint64_t a_times_2_k_mod_c( uint64_t a, unsigned k, uint64_t c )
{
a %= c;
for (unsigned i = 0; i < k; ++i)
{
a <<= 1;
a %= c;
}
return a;
}
uint64_t a_times_b_mod_c( uint64_t a, uint64_t b, uint64_t c )
{
// ensure a and b are < c:
a %= c;
b %= c;
auto Z = cross( split(a), split(b), [](uint32_t lhs, uint32_t rhs)->uint64_t {
return (uint64_t)lhs * (uint64_t)rhs;
} );
uint64_t to_the_0;
uint64_t to_the_32_a;
uint64_t to_the_32_b;
uint64_t to_the_64;
std::tie( to_the_0, to_the_32_a, to_the_32_b, to_the_64 ) = Z;
// std::cout << to_the_0 << "+ 2^32 *(" << to_the_32_a << "+" << to_the_32_b << ") + 2^64 * " << to_the_64 << "\n";
return
(to_the_0
+ a_times_2_k_mod_c(to_the_32_a%c+to_the_32_b%c, 32, c)
+ a_times_2_k_mod_c(to_the_64, 64, c) )
%c;
}
int main()
{
uint64_t retval = a_times_b_mod_c( 19010000000000000000, 1011000000000000, 1231231231231211 );
std::cout << retval << "\n";
}
I2luY2x1ZGUgPHN0ZGludC5oPgojaW5jbHVkZSA8dHVwbGU+CiNpbmNsdWRlIDxpb3N0cmVhbT4KCnR5cGVkZWYgc3RkOjp0dXBsZTwgdWludDMyX3QsIHVpbnQzMl90ID4gc3BsaXRfdDsKc3BsaXRfdCBzcGxpdCggdWludDY0X3QgYSApCnsKICAgIHN0YXRpYyBjb25zdCB1aW50MzJfdCBtYXNrID0gLTE7CiAgICBhdXRvIHJldHZhbCA9IHN0ZDo6bWFrZV90dXBsZSggbWFzayZhLCAoIGEgPj4gMzIgKSApOwogICAgLy8gc3RkOjpjb3V0IDw8ICIoIiA8PCBzdGQ6OmdldDwwPihyZXR2YWwpIDw8ICIsIiA8PCBzdGQ6OmdldDwxPihyZXR2YWwpIDw8ICIpXG4iOwogICAgcmV0dXJuIHJldHZhbDsKfQoKdHlwZWRlZiBzdGQ6OnR1cGxlPCB1aW50NjRfdCwgdWludDY0X3QsIHVpbnQ2NF90LCB1aW50NjRfdCA+IGNyb3NzX3Q7CnRlbXBsYXRlPHR5cGVuYW1lIExhbWJkYT4KY3Jvc3NfdCBjcm9zcyggc3BsaXRfdCBsaHMsIHNwbGl0X3QgcmhzLCBMYW1iZGEmJiBvcCApCnsKICAgIHJldHVybiBzdGQ6Om1ha2VfdHVwbGUoIAogICAgICAgIG9wKHN0ZDo6Z2V0PDA+KGxocyksIHN0ZDo6Z2V0PDA+KHJocykpLAogICAgICAgIG9wKHN0ZDo6Z2V0PDE+KGxocyksIHN0ZDo6Z2V0PDA+KHJocykpLAogICAgICAgIG9wKHN0ZDo6Z2V0PDA+KGxocyksIHN0ZDo6Z2V0PDE+KHJocykpLAogICAgICAgIG9wKHN0ZDo6Z2V0PDE+KGxocyksIHN0ZDo6Z2V0PDE+KHJocykpCiAgICApOwp9CgovLyBhIGFuZCBjIG11c3QgaGF2ZSBoaWdoIGJpdCB1bnNldDoKdWludDY0X3QgYV90aW1lc18yX2tfbW9kX2MoIHVpbnQ2NF90IGEsIHVuc2lnbmVkIGssIHVpbnQ2NF90IGMgKQp7CiAgICBhICU9IGM7CiAgICBmb3IgKHVuc2lnbmVkIGkgPSAwOyBpIDwgazsgKytpKQogICAgewogICAgICAgIGEgPDw9IDE7CiAgICAgICAgYSAlPSBjOwogICAgfQogICAgcmV0dXJuIGE7Cn0KCnVpbnQ2NF90IGFfdGltZXNfYl9tb2RfYyggdWludDY0X3QgYSwgdWludDY0X3QgYiwgdWludDY0X3QgYyApCnsKICAgIC8vIGVuc3VyZSBhIGFuZCBiIGFyZSA8IGM6CiAgICBhICU9IGM7CiAgICBiICU9IGM7CiAgICAKICAgIGF1dG8gWiA9IGNyb3NzKCBzcGxpdChhKSwgc3BsaXQoYiksIFtdKHVpbnQzMl90IGxocywgdWludDMyX3QgcmhzKS0+dWludDY0X3QgewogICAgICAgIHJldHVybiAodWludDY0X3QpbGhzICogKHVpbnQ2NF90KXJoczsKICAgIH0gKTsKICAgIAogICAgdWludDY0X3QgdG9fdGhlXzA7CiAgICB1aW50NjRfdCB0b190aGVfMzJfYTsKICAgIHVpbnQ2NF90IHRvX3RoZV8zMl9iOwogICAgdWludDY0X3QgdG9fdGhlXzY0OwogICAgc3RkOjp0aWUoIHRvX3RoZV8wLCB0b190aGVfMzJfYSwgdG9fdGhlXzMyX2IsIHRvX3RoZV82NCApID0gWjsKICAgIAogICAgLy8gc3RkOjpjb3V0IDw8IHRvX3RoZV8wIDw8ICIrIDJeMzIgKigiIDw8IHRvX3RoZV8zMl9hIDw8ICIrIiA8PCB0b190aGVfMzJfYiA8PCAiKSArIDJeNjQgKiAiIDw8IHRvX3RoZV82NCA8PCAiXG4iOwogICAgCiAgICByZXR1cm4KICAgICAgICAodG9fdGhlXzAKICAgICAgICArIGFfdGltZXNfMl9rX21vZF9jKHRvX3RoZV8zMl9hJWMrdG9fdGhlXzMyX2IlYywgMzIsIGMpCiAgICAgICAgKyBhX3RpbWVzXzJfa19tb2RfYyh0b190aGVfNjQsIDY0LCBjKSApCiAgICAlYzsKfQoKaW50IG1haW4oKQp7CiAgICB1aW50NjRfdCByZXR2YWwgPSBhX3RpbWVzX2JfbW9kX2MoIDE5MDEwMDAwMDAwMDAwMDAwMDAwLCAxMDExMDAwMDAwMDAwMDAwLCAxMjMxMjMxMjMxMjMxMjExICk7CiAgICBzdGQ6OmNvdXQgPDwgcmV0dmFsIDw8ICJcbiI7Cn0=