package algoritmos;

import java.util.List;
import java.util.ArrayList;
import java.util.Random;

import sun.security.smartcardio.SunPCSC.Factory;

import ialgoritmos.IFatoracao;

public class CurvasElipticas /* implements IFatoracao */{
	private int a;
	private int x1;
	private int y1;
	private long b;
	private int numero;
	private long expoente;
	private List<Par> pares;

	public CurvasElipticas(int numero) {
		this.numero = numero;
		this.pares = new ArrayList<Par>();
	}

	public void setParametros() {
		int semente = 0;
		Random random = new Random(semente);
		a = 1 /*+ random.nextInt(numero - 2)*/;
		x1 = -3 /*+ random.nextInt(numero - 2)*/;
		y1 = 1 /*+ random.nextInt(numero - 2)*/;
		//System.out.println("SET PARAMETROS");
		//System.out.println(a + " " + x1 + " " + y1);
	}

	public long escolhaExpoente() {
		int semente = 0;
		Random random = new Random(semente);
		expoente = random.nextInt(100);
		return expoente;
	}

	public double inverso(double n, double mod) {
		double d = 0;
		double valor = 0;
		for (int j = 0; j < mod; j++) {
			d = n * j % mod;
			if (d == 1) {
				valor = j;
				break;
			}
			if (j == mod - 1) {
				System.out.println("Não foi possível calcular inverso");
			}
		}
		// deve lançar alguma execção quando não encontrar inverso
		return valor;
	}

	public boolean verificaInverso(double n, double mod) {
		double d = 0;

		for (int j = 0; j < mod; j++) {
			d = n * j % mod;
			if (d == 1) {
				return true;
			}

		}
		return false;
	}

	public boolean duplicaPontos(double x1, double y1, double a, long numero,
			int tamanho) {
		int i = 0;
		System.out.println(x1 + "," + y1);
		for (i = 0; i < tamanho; i++) {
			if (verificaInverso(2 * y1, numero)) {
				// System.out.println("entrada no for");
				double lambda = (3 * (Math.pow(x1, 2)) + a)
						* ((inverso(2 * y1, numero))) % numero;
				double x = (Math.pow(lambda, 2) - 2 * (x1)) % numero;
				if (x < 0) {
					x = numero + x;
				}
				double y = (lambda * (x1 - x) - y1) % numero;
				if (y < 0) {
					y = numero + y;
				}
				x1 = x;
				y1 = y;
				System.out.println(x1 + "," + y1);

			} else {
				System.out.println("Não é possivel calcular o inverso");

				break;
			}

		}
		if (i == tamanho - 1) {
			System.out.println("kkk");
			return false;
		} else
			System.out.println("fff");
			return false;
	}

	public List<Integer> toBinary(long decimal) {
		List<Integer> binarios = new ArrayList<Integer>();
		while (decimal != 0) {
			binarios.add((int) decimal % 2);
			decimal = (int) Math.floor(decimal / 2);
		}
		return binarios;

	}

	public void fatora() {
		long c = 0;
		int i = 0;
		while (true) {
			setParametros();
			b = (long) (Math.pow(y1, 2) - Math.pow(x1, 3) - a * x1);
			long delta = (long) (4 * Math.pow(a, 3) + 27 * Math.pow(b, 2));
			int d = (int) Metodos.mdc(delta, numero);
			if (d == numero) {
				continue;
			} else if (d > 1 && d < numero) {
				System.out.println(d + " é um divisor de " + numero);
				break;
			} else {
				long k = Metodos.fatorial(escolhaExpoente());
				List<Integer> binarios = toBinary(k);

				if (duplicaPontos(x1, y1, a, numero, binarios.size())) {
					c = (long) (Metodos.mdc(2 * y1, numero));
					if (c < numero) {
						System.out.println(c + " é fator de " + numero);
						if (c < numero) {
							System.out.println(c + " é fator de " + numero);
							break;
						} else if (c == numero) {
							continue;
						}
					}
				} else {

					continue;

				}
			}

		}
	}

	public void setX1(int x1) {
		this.x1 = x1;
	}

	public void setY1(int y1) {
		this.y1 = y1;
	}

}
