#!/usr/bin/env python3
from Cryptotools.Groups.cyclic import Cyclic
from Cryptotools.Numbers.primeNumber import getPrimeNumber
from random import randint, choice
from math import log, log10, log2

"""
    Here, we will try to understand why we need to have a generator when we encrypt data for Diffie-Hellman
    https://crypto.stackexchange.com/questions/25489/why-does-diffie-hellman-need-be-a-cyclic-group
"""
def operation(a, b, n):
    return (a ** b) % n

def getGenerator(gr, p):
    index = 1
    for g in range(2, p):
        z = list()
        for entry in range(1, p):
            res = operation(g, index, p)
            #if res not in z:
            z.append(res)
            index = index + 1
        print(f"{g}: {sorted(z)}")

def generateGroupByG(gr, p, g):
    index = 1
    z = list()
    for entry in range(1, p):
        res = operation(g, index, p)
        #if res not in z:
        z.append(res)
        index = index + 1
    return z

def computePublicKey(key, p, g):
    return (g ** key) % p


gr = list()
# Public value
p = 5
g = 0
for i in range(1, p):
    gr.append(i)

print(f"p = {p}")
print(f"G = {gr}")
# We try with a generator which is not in list
cyclic = Cyclic(gr, p, operation)
generators = cyclic.getGenerators()
print(f"All generators: {generators}")

# Generate group with g = 2
grWithG = generateGroupByG(gr, p, 2)
print(f"Group generated with g = 2: {grWithG}")

for a in grWithG:
    # print(f"log2({a}) = {log(a, 2)}") # Same as below
    print(f"log2({a}) = {log2(a)}")

print()

for a in range(1, len(grWithG) + 1):
    print(f"log2({a}) = {log2(a)}")
