Hello! So I decided to start trying to implement MTProto protocol (Telegram) in 
Nim, did the most basic things so I can get a reply from the server for the 
first time.

To continue I need to decompose a number `pq` into two prime cofactors `p` and 
`q`, such as `p` < `q`. So basically I need to find the largest prime cofactor 
of a number and then divide the number by that cofactor to get the second one. 
The Python version I found runs really fast in under 100ms:
    
    
    from random import randint
    from math import gcd
    
    
    def brent(N):
        if N % 2 == 0:
            return 2
        # y, c, m = randint(1, N - 1), randint(1, N - 1), randint(1, N - 1)
        y = 398563423414958448
        c = 259203014236224986
        m = 359097073186387306
        
        g, r, q = 1, 1, 1
        while g == 1:
            x = y
            for i in range(r):
                y = ((y * y) % N + c) % N
            k = 0
            while k < r and g == 1:
                ys = y
                for i in range(min(m, r - k)):
                    y = ((y * y) % N + c) % N
                    q = q * (abs(x - y)) % N
                g = gcd(q, N)
                k = k + m
            r *= 2
        if g == N:
            while True:
                ys = ((ys * ys) % N + c) % N
                g = gcd(abs(x - ys), N)
                if g > 1:
                    break
        
        return g
    
    def factors(n):
      a = brent(n)
      b = n // a
      return (a, b) if a < b else (b, a)
    
    print(factors(1243804461473944423))
    
    
    Run

I tried my hand at porting it to Nim but I guess I failed: 
    
    
    import random, math
    
    randomize()
    
    proc brent*(n: uint64): uint64 =
      if n mod 2 == 0:
        return 2
      
      var
        # y = rand(1'u64 ..< n - 1)
        y = 398563423414958448'u64
        c = 259203014236224986'u64
        m = 359097073186387306'u64
        g, r, q = 1'u64
        x, k, ys = 0'u64
      
      while g == 1:
        x = y
        for i in 0 ..< r:
          y = ((y * y) mod n + c) mod n
        k = 0'u64
        while k < r and g == 1:
          ys = y
          for i in 0 ..< min(m, r - k):
            y = ((y * y) mod n + c) mod n
            q = q * uint64(abs(int64(x - y))) mod n
          g = gcd(q, n)
          k = k + m
        r *= 2
      if g == n:
        while true:
          ys = ((ys * ys) mod n + c) mod n
          g = gcd(uint64(abs(int64(x - ys))), n)
          if g > 1:
            break
      
      result = g
    
    proc factors(n: uint64): (uint64, uint64) =
      let a = brent(n)
      let b = n div a
      if a < b: (a, b) else: (b, a)
    
    echo factors(1243804461473944423'u64)
    
    
    Run

As far as I can see one of the issues if that there's an overflow when two big 
uint64's are multiplied. So can someone recommend a good way to port this 
algorithm, or maybe I can use some other algorithm which won't use such big 
numbers for multiplication? (I also tried porting 
[https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)
 and succeeded, but it's quite slow)

Reply via email to