#!/usr/bin/ruby

#####################################
#
# Marcin Woźniak
# s434812
#
#####################################

require 'openssl'
require 'securerandom'
require 'prime'
require 'thread'

def nwd(a, b)
  if a == 0 
    return false
  end
  b == 0 ? a : nwd(b, a.modulo(b))
end

def extended_euklides(a, b)
  return 1, 0 if b == 0

  q, r = a.divmod b
  s, t = extended_euklides(b, r)

  return t, s - q * t
end

# Zad. 1.1
def random_gen_Zn(k,n)

  if n == 0
    n = 2 ** k
  end

  if k == 1
    max = 1
  else
    kb = k.to_s(2)
    minimum = []
    minimum << 1
    k = kb.length - 1

    while (k != 0) do
      j = SecureRandom.random_number(2)
      minimum << j
      k = k - 1
    end
    min = minimum.join.to_i(2)
    max = n - 1
    if min < max
      return SecureRandom.random_number(min..max)
    end
  end
end

# Zad. 1.2
def reciprocal_Phi_p(n,p)
  u = extended_euklides(n,p)[0]
  v = extended_euklides(n,p)[1]

  if u * n % p == 1
    return u
  else
    return v
  end
end

# Zad. 1.3
def betterExponentiation(x,k,n)
  if n == 0
    return false
  end

  if x < n && x > 0
    b = k.to_s(2).reverse
    l = b.count "[0-1]"
    y = 1
    i = l - 1

    while i >= 0
      y = y**2 % n
      if b[i]=="1"
        y = y * x % n
      end
      i = i - 1
    end
    return y
  end
end

# Zad. 1.4
def remSqEuler(a,p)
  ans = betterExponentiation(a,(p-1)/2,p)

  if ans == 1 && Prime.prime?(p)
    return true
  else
    return false
  end
end

# Zad. 1.5
def squareRootFp(p,b)
  if p % 4 == 3 && remSqEuler(p,b) == true
      a = betterExponentiation(b, (p+1)/4, p)
      return a
  end
end

# Zad. 1.6
def primalityTest(n)
  if n == 1
    return false
  end

  if n == 2 || n == 3
    return true
  end

  counter = 20
  while (counter != 0) do
    b = SecureRandom.random_number(2..n-2) # Tez dziala n-1
      if betterExponentiation(b,n-1,n) != 1
        return false
      end
      counter = counter - 1
  end
  return true
end

def specyficPrimaryNumber
  p = 0
  q = 0

  qThread = Thread.new {
  while true
    q = SecureRandom.random_number(2 ** 256)
    if primalityTest(q)
      break
    end
  end
  }

  qThread.join

  while true do
    q = SecureRandom.random_number(2 ** 256)
    p = 2 * q + 1
    if primalityTest(p)
      return p,q
    end
  end
end

def generator(p,q)
  while true
    g = SecureRandom.random_number(2..p-2)
    if betterExponentiation(g,q,p) == 1
      next
    else
      return g
    end
  end
end

def generate(n)
  return `openssl prime -generate -bits '#{n}'`.gsub(/\n$/, '').to_i
end