from random import randint
try:
    import sympy.ntheory
except ImportError:
    sympy = None


class Factorization:
    @staticmethod
    def find_small_multiplier_lopatin(what):
        """Finds the small multiplier by using Lopatin's method"""
        g = 0
        for i in range(3):
            q = (randint(0, 127) & 15) + 17
            x = randint(0, 1000000000) + 1
            y = x
            lim = 1 << (i + 18)
            for j in range(1, lim):
                a, b, c = x, x, q
                while b != 0:
                    if (b & 1) != 0:
                        c += a
                        if c >= what:
                            c -= what
                    a += a
                    if a >= what:
                        a -= what
                    b >>= 1

                x = c
                z = y - x if x < y else x - y
                g = Factorization.gcd(z, what)
                if g != 1:
                    break

                if (j & (j - 1)) == 0:
                    y = x

            if g > 1:
                break

        p = what // g
        return min(p, g)

    @staticmethod
    def gcd(a, b):
        """Calculates the greatest common divisor"""
        while a != 0 and b != 0:
            while b & 1 == 0:
                b >>= 1

            while a & 1 == 0:
                a >>= 1

            if a > b:
                a -= b
            else:
                b -= a

        return a if b == 0 else b

    @staticmethod
    def factorize(pq):
        """Factorizes the given number and returns both
           the divisor and the number divided by the divisor
        """
        if sympy:
            return tuple(sympy.ntheory.factorint(pq).keys())
        else:
            divisor = Factorization.find_small_multiplier_lopatin(pq)
            return divisor, pq // divisor
