package algoritmos;

import ialgoritmos.IFatoracao;

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

public class ECM implements IFatoracao{
	
	private long a;
	private long x1;
	private long y1;
	private long b;
	private long delta;
	private long numero;
	private long k;
	private Random random;
	private long semente;
	private List<Integer> binarios;
	public ECM(long numero){
		semente = System.currentTimeMillis();
		random = new Random(semente);
		a = 1 + random.nextInt((int)numero-2);
		x1 = 1 + random.nextInt((int)numero-2);
		y1 = 1 + random.nextInt((int)numero-1);
		delta = (long) (4 * Math.pow(a, 3) + 27 * Math.pow(b, 2));
		this.numero = numero;
		k = random.nextInt(100);
		binarios = new ArrayList<Integer>();
	}
	public void setAll(){
		semente = System.currentTimeMillis();
		random = new Random(semente);
		a = a + 1;
		x1 = x1 + 1;
		y1 = y1 + 1;
		delta = (long) (4 * Math.pow(a, 3) + 27 * Math.pow(b, 2));
		this.numero = numero;
		k = random.nextInt(100);
		
	}
	public void setK(){
		k = k++;
	}
	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 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");
				System.out.println(2*y1);

				break;
			}

		}
		if (i == tamanho - 1) {
			//System.out.println("kkk");
			return false;
		} else
			//System.out.println("ff");
			return true;
	}
	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 String fatora(){
		String retorno = "";
		retorno = retorno + "----------------ECM-----------------------"+"</br>";
		
		int itera = 0;
		while(true){
			itera++;
		if(Metodos.mdc(delta, numero)==1){
			//primeiro if testa se mdc (delat, numero)==1
			long fat = Metodos.fatorial(k);
			binarios = toBinary(fat);
			if (duplicaPontos(x1, y1, a, numero, binarios.size())){
				if(Metodos.mdc(2*y1, numero)>1 && Metodos.mdc(2*y1, numero)<numero){
					retorno = retorno + Metodos.mdc(2*y1, numero)+ " é fator de "+ numero+" "+ itera+ " ITERAÇÕES!"+"</br>";
					
					break;
				}else{
					
					setAll();
					
					setK();
					continue;
					
				}
					
			}else{
				System.out.println("Escolhendo outra curva");
				setAll();
				continue;
			}
		}else if (Metodos.mdc(delta, numero)==numero){
			System.out.println("Escolhendo outra curva");
			setAll();
		}else if(Metodos.mdc(delta, numero)>1 && Metodos.mdc(delta, numero)<numero){
			retorno = retorno + Metodos.mdc(delta, numero) + " é fator de "+ numero + " "+ itera + " ITERAÇÕES!"+"</br>";
			break;
		}
		
	}
		retorno = retorno + "---------------------------------------</br>";
		return retorno;
		//System.out.println("---------------------------------------");
	}
}
