#!/usr/bin/ruby
# coding: utf-8

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

load '../module.rb'
load '../module2.rb'

puts `clear`

def wielokrotnoscPunktu2(a,b,p,n,x,y)
  counter = n - 1
  punktQ = []
  punktP = [x,y]
  temp = [x,y]
  while counter > 0
    punktQ = sumaPunktow(a,b,p,temp[0],temp[1],punktP[0],punktP[1])
    temp = punktQ
    counter = counter - 1
  end
  return punktQ
end

def wielokrotnoscPunktu(a,b,p,n,x,y)
  punktQ = [x,y]
  punktR = ["e","e"]

  while n > 0
    if n % 2 == 1
      punktR = sumaPunktow(a,b,p,punktR[0],punktR[1],punktQ[0],punktQ[1])
      n = n - 1
    end
    punktQ = sumaPunktow(a,b,p,punktQ[0],punktQ[1],punktQ[0],punktQ[1])
    n = n / 2
  end
  return punktR
end

def liczenieOrd(a,b,p)
  threads = []
  punktyNaKrzywej = Array.new
  unikalnePunktyNaKrzywej = Array.new

  threads << Thread.new {
    counter = 10
    while counter > 0
      punktyNaKrzywej <<  punktNaKrzywej(a,b,p)[0]
      counter = counter -1
    end
  }

  threads << Thread.new {
    counter = 10
    while counter > 0
      punktyNaKrzywej <<  punktNaKrzywej(a,b,p)[0]
      counter = counter -1
    end
  }

  threads << Thread.new {
    counter = 10
    while counter > 0
      punktyNaKrzywej <<  punktNaKrzywej(a,b,p)[0]
      counter = counter -1
    end
  }

  ThreadsWait.all_waits(*threads)

  punktyNaKrzywej.sort.each { |e|
    unikalnePunktyNaKrzywej.push(e) if e != unikalnePunktyNaKrzywej[-1]
  }

  return unikalnePunktyNaKrzywej.count
end

def generowanieKluczyElGamalKrzywaEliptyczna(k)
  while true
  p = generate(k)
    if (primalityTest(p)) && (p % 4 == 3)
      krzywa = generatorKrzywej(p)
      a = krzywa[0].to_i
      b = krzywa[1].to_i

      punktyNaKrzywej = Array.new

      punktP = punktNaKrzywej(a,b,p)
      ord = liczenieOrd(a,b,p)

      while true
        x = SecureRandom.random_number(1..ord)
        if x < ord
          punktQ = wielokrotnoscPunktu(a,b,p,x,punktP[0],punktP[1])
          pubKey = [a,b,p,punktP[0],punktP[1],punktQ[0],punktQ[1]]
          privKey = [a,b,p,punktP[0],punktP[1],punktQ[0],punktQ[1],x]
          return a,b,p,punktP[0],punktP[1],punktQ[0],punktQ[1],x
        end
      end
    end
  end
end

def algorytmKodowania(a,b,p,m,n,u)
  if (m < n) && (p > n*u)
    for i in 1..u
      x = (m * u % p) + (i % p)
      fx = rownanieKrzywej(a,b,p,x)
      if remSqEuler(fx,p)
        y = betterExponentiation(fx,((p+1)/4),p)
      end
    end
  else
    puts "Nieprawidołowe dane"
  end
  return [x,y]
end

def algorytmDeKodowania(a,b,p,x,y,n,u)
  m = (x - 1) / u
  return m
end

def szyfrowanieElGamala(m,n,u,a,b,p,px,py,qx,qy)
  c = algorytmKodowania(a,b,p,m,n,u)
  puts "Wiadomość na prostej jako punkt #{c.inspect}"
  return c
end

def main
  k = 30
  m = 73
  n = m + SecureRandom.random_number(0..1000000)
  u = SecureRandom.random_number(30..50)
  a,b,p,px,py,qx,qy,x = generowanieKluczyElGamalKrzywaEliptyczna(k)

  pubKey = [a,b,p,px,py,qx,qy]
  privKey = [a,b,p,px,py,qx,qy,x]

  pm = szyfrowanieElGamala(m,n,u,a,b,p,px,py,qx,qy)

  # BOB
  y = SecureRandom.random_number(0..liczenieOrd(a,b,p))
  c1 = wielokrotnoscPunktu(a,b,p,y,px,py)
  yq = wielokrotnoscPunktu(a,b,p,y,qx,qy)
  c2 = sumaPunktow(a,b,p,pm[0],pm[1],yq[0],yq[1])
  puts "Ciphers: #{c1} + #{c2}"

  # ALICE
  xc1 = wielokrotnoscPunktu(a,b,p,x,c1[0],c1[1])
  pmd = sumaPunktow(a,b,p,c2[0],c2[1],xc1[0],-xc1[1])
  d = algorytmDeKodowania(a,b,p,pmd[0],pmd[1],n,u)
  puts "Dekodowana wiadomość #{d.inspect}"
end

#puts generowanieKluczyElGamalKrzywaEliptyczna(2048).inspect
#puts wielokrotnoscPunktu(8,10,19,3,15,3).inspect
#puts algorytmSzyfrowania(8,10,19,29102901920190,29102901920199,50).inspect
#puts algorytmDeSzyfrowania(8,10,19,12,17,50)
puts main.inspect
