1

Basically, I am writing a program that works with large integer values that overflow the cpp integer. I am trying to compute something like: gdc(pow(a, b), c) where a ^ b is the value overflowing the integer limit. Is there a way to do this where I don't have to rely on big integer libraries? If not, are there any recommended big integer libraries?

  • Do you need solution in `C++` or `Python`? You can do modular exponentiation `gcd(powmod(a, b, c), c)`, it doesn't need big integers. I'll write a simple small program for you if you tell me what language to use. – Arty Nov 09 '21 at 15:09

1 Answers1

2

We can use a property of greatest common divisor that gcd(a, b) = gcd(a % b, b). Hence gcd(pow(a, b), c) = gcd(pow(a, b) % c, c) = gcd(powmod(a, b, c), c), where powmod() is modular exponentiation.

In my C++ code below PowMod() is implemented using exponentiation by squaring approach.

Try it online!

#include <cstdint>
#include <iostream>

using Word = uint32_t;
using DWord = uint64_t;

Word GCD(Word a, Word b) {
    Word t = 0;
    while (b != 0) {
        t = b;
        b = a % b;
        a = t;
    }
    return a;
}

Word PowMod(Word a, Word b, Word c) {
    Word r = 1;
    while (b != 0) {
        if (b & 1)
            r = (DWord(r) * a) % c;
        a = (DWord(a) * a) % c;
        b >>= 1;
    }
    return r;
}

int main() {
    Word const
        a = 2645680092U, b = 3562429202U, c = 3045001828U,
        powmod = PowMod(a, b, c), gcd = GCD(powmod, c);
    std::cout << "a = " << a << ", b = " << b
        << ", c = " << c << std::endl;
    std::cout << "PowMod(a, b, c) = "
        << powmod << std::endl; // 592284924
    std::cout << "GCD(PowMod(a, b, c), c) = "
        << gcd << std::endl; // 1892
}

Output:

a = 2645680092, b = 3562429202, c = 3045001828
PowMod(a, b, c) = 592284924
GCD(PowMod(a, b, c), c) = 1892

which gives correct results, that can be verified through following simple Python program giving same result:

Try it online!

import random, math
random.seed(0)
bits = 32
while True:
    c = random.randrange(1 << (bits - 1), 1 << bits)
    a = random.randrange(1 << (bits - 1), 1 << bits) % c
    b = random.randrange(1 << (bits - 1), 1 << bits)
    pm = pow(a, b, c)
    gcd = math.gcd(pm, c)
    if gcd >= 1000:
        print('a =', a, ', b =', b, ', c =', c,
            ', powmod =', pm, ', gcd =', gcd)
        break

Output:

a = 2645680092 , b = 3562429202 , c = 3045001828 ,
    powmod = 592284924 , gcd = 1892

If you have GCC/CLang compiler, you can make Word to be 64-bit and DWord to be 128-bit, by changing following lines of code:

using Word = uint64_t;
using DWord = unsigned __int128;

my code supports 32-bit inputs, but after this change you can have 64-bit inputs.


Part 2. Using large integer arithmetics library GMP.

If for some reason you have large input integers then you can use great library GMP for large arithmetics (it supports integer, rational, floating point numbers).

This library has all mathematical operations, including modular exponentiation (PowMod) and some number theoretical functions (including GCD). Also this library is very popular and highly optimized.

In following code I do same things like in me code above, but using only GMP's functions. As an example I use 512-bit integers to show that it can accept large inputs (it can accept even millions of digits):

Try it online!

#include <iostream>
#include <cstdlib>
#include <gmpxx.h>

int main() {
    mpz_class const
        a("1953143455988359840868749111326065201169739169335107410565117106311318704164104986194255770982854472823807334163384557922525376038346976291413843761504166", 10),
        b("5126002245539530470958611905297854592859344951467500786493685495603638740444446597426402800257519403404965463713689509774040138494219032682986554069941558", 10),
        c("4396071968291195248321035664209400217968667450140674696924686844534284953565382985421958604880273584922294910355449271193696338132720472184903935323837626", 10);
    
    mpz_class powmod, gcd;
    // PowMod
    mpz_powm(powmod.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t(), c.get_mpz_t()); // 1632164707041502536171492944083090257113212090861915134477312917063125646194834706890409016008321666479437224930114914370387958138698748075752168351835856
    // GCD
    mpz_gcd(gcd.get_mpz_t(), powmod.get_mpz_t(), c.get_mpz_t()); // 51842
    // Output
    std::cout << "PowMod = " << powmod.get_str() << std::endl
              << "GCD = " << gcd.get_str() << std::endl;
}

Output:

PowMod = 1632164707041502536171492944083090257113212090861915134477312917063125646194834706890409016008321666479437224930114914370387958138698748075752168351835856
GCD = 51842

To use GMP library under Linux just install sudo apt install libgmp-dev and compile clang++ -std=c++11 -O2 -lgmp -o main main.cpp.

Using GMP under Windows is a bit more tricky. One way is to build yourself MPIR library which is a Windows friendly clone of GMP. Another way is to install MSYS and use prebuilt GMP from there following these instructions that I wrote in my other answer.

Arty
  • 14,883
  • 6
  • 36
  • 69
  • @patrickbies Please put a look at Part-2 of my answer above, I just posted it now. There I wrote how to use [GMP](https://gmplib.org/) large integer library for same purposes of `GCD(powmod(a, b, c), c)` as I did for small integers in Part-1 of my answer. This Part-2 shall be used only if you have large inputs, i.e. if `a`, `b`, `c` are all large, for example 1024-bits. Click `Try it online!` link in Part-2 to see this library in action. – Arty Nov 09 '21 at 16:56