/**
 * carissan.java - jpq - 21/04/02
 * avec des BigInteger - 23/04/02
 *
 * modifications et ajout de main() le 06/01/18 et le 27/08/22
 */

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.math.BigInteger;

public class carissan extends JPanel {
	static final long serialVersionUID = 220827L;
	controles C = new controles();
	dessin D = new dessin();

	public carissan() {
		setLayout (new BorderLayout());
		add (C, BorderLayout.NORTH);
		add (D, BorderLayout.CENTER);
}

	public static void main (String [] args) {
		int w = 700;
		int h = 300;

		carissan c = new carissan();

		JFrame jf = new JFrame ("Carissan");
		jf.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		jf.add (c);
		jf.setSize (w, h);
		jf.setVisible (true);
	}

/**
 * classe dessin
 * date : 27/08/22
 */

protected class dessin extends Canvas {
	static final long serialVersionUID = 220827L;
	String[] str;
	int[][] tab;
	int bok, dim;
	Color c;
	boolean bauto;
	BigInteger un, deux;

	public dessin() {
		un = BigInteger.ONE;
		deux = BigInteger.valueOf (2);
	}

	private boolean affiche (Graphics g) {
		boolean b = false;
		for (int i = 0; i < dim; i++) {
			int n = C.nmodulos [i];
//int y = (int)((C.X - 2) % n);
			int y = C.X.subtract (deux).remainder (BigInteger.valueOf (n)).intValue();
			for (int j = 0; j < 6; j++) {
				c = Color.blue;
				int t = tab [i] [y ++];
				if (y == n)
					y = 0;
				if (t < 0) {
					c = Color.red;
					t = -t;
					if (j == 2)
						b = true;
				}
				if (t == n)
					t = 0;
				g.setColor (c);
				g.drawString (Integer.toString (t), 20 * (i + 1), 20 * (j + 1));
			}
			g.setColor (Color.black);
			g.drawRect (20 * i + 15, 45, 20, 20);
		}
		g.drawString ("X = " + C.X.toString(), 20, 160);
//double Y = Math.sqrt (C.X * C.X - C.N);
		double Y = Math.sqrt (C.X.multiply (C.X).subtract (C.N).doubleValue());
		g.drawString ("Y = " + Double.toString (Y), 20, 180);
		g.drawString (C.N.toString() + " = (" + C.X.toString() + " + "
			+ Double.toString (Y) + ") (" + C.X.toString() + " - "
			+ Double.toString (Y) + ")", 20, 220);
		g.drawString (C.N.toString() + " = " + Double.toString (C.X.doubleValue() + Y)
			+ " x " + Double.toString (C.X.doubleValue() - Y), 20, 240);
		return b;
	}

	public void paint (Graphics g) {
		int L = getWidth();
		int H = getHeight();
		g.setColor (Color.white);
		g.fillRect (0, 0, L, H);
		if ((bok == 1) && (str != null)) {
			g.setColor (Color.black);
			g.drawString ("résidus quadratiques", 10, 20);
			for (int i = 0; i < dim; i++)
				g.drawString (str [i], 10, 20 * (i + 3));
		}
		if ((bok == 2) && (tab != null)) {
			if (bauto) {
				while (bauto) {
					C.X = C.X.add (un);
					g.setColor (Color.white);
					g.fillRect (0, 0, L, H);
					bauto = affiche (g);
					try {
						Thread.sleep (100);
					}
					catch (InterruptedException ie) {}
				}
			} else {
				boolean b = affiche (g);
			}
		}
	}
}


/**
 * classe controles
 * date : 27/08/22
 */

protected class controles extends JPanel implements ActionListener {
	static final long serialVersionUID = 220827L;
	JTextField tfmodulos, tfN;
	JButton ok, init, plus, moins, auto;
	Font f;
	BigInteger N, X, un;
	int[] nmodulos;
	int dim;
	boolean[] btab;

	public controles () {
		setBackground (Color.lightGray);
		setBorder (BorderFactory.createMatteBorder(1, 1, 1, 1, Color.BLACK));
		f = new Font ("Arial", Font.PLAIN, 10);
		add (ajoutlbl ("modulos :"));
		add (tfmodulos = ajouttf (20));
		add (ok = ajoutb ("ok")); 
		add (ajoutlbl ("N :"));
		add (tfN = ajouttf (10));
		add (moins = ajoutb ("<-")); 
		add (init = ajoutb ("O"));
		add (plus = ajoutb ("->"));
		add (auto = ajoutb ("auto"));
		un = BigInteger.valueOf (1);
	}

	private JLabel ajoutlbl (String s) {
		JLabel l = new JLabel (s);
		l.setBackground (Color.lightGray);
		l.setFont (f);
		return l;
	}

	private JTextField ajouttf (int i) {
		JTextField T = new JTextField (i);
		T.setFont (f);
		return T;
	}

	private JButton ajoutb (String s) {
		JButton B = new JButton (s);
		B.setFont (f);
		B.addActionListener (this);
		return B;
	}

	public void actionPerformed (ActionEvent e) {
		Object obj = e.getSource();
		if (obj == ok) {
			StringTokenizer st = new StringTokenizer (tfmodulos.getText());
			dim = 0;
			D.bok = 0;
			nmodulos = new int [Math.max (1, st.countTokens())]; 
			int max = 0;
			while (st.hasMoreTokens()) {
				int n = 0;
				try {
					n = Integer.parseInt (st.nextToken());
					if (n > 1)
						nmodulos [dim ++] = n;
					if (n > max)
						max = n;
				}
				catch (NumberFormatException nfe) {}
			}
			btab = new boolean [max];
			D.str = new String [dim];
			D.dim = dim;
			for (int i = 0; i < dim; i++) {
				int n = nmodulos [i];
				for (int j = 0; j < n; j++)
					btab [j] = false;
				for (int j = 0; j < n; j++)
					btab [(j * j) % n] = true;
				D.str [i] = Integer.toString (n) + " : ";
				for (int j = 0; j < n; j++)
				if (btab [j])
					D.str [i] += Integer.toString (j) + " ";
			}
			D.tab = new int [dim] [max];
			D.bok = 1;
		}
		if ((obj == init) && (D.tab != null)
				|| ((obj == plus) || (obj == moins) || (obj == auto)) && (D.bok < 2)) {
			try {
				N = new BigInteger (tfN.getText());
				if (N.compareTo (un) >= 0)
				X = BigInteger.valueOf ((long)(Math.floor (Math.sqrt (N.doubleValue())) + 1.0));
				for (int i = 0; i < dim; i ++) {
					int n = nmodulos [i];
//int Nn = N % n;
					int Nn = N.remainder (BigInteger.valueOf (n)).intValue();
					for (int j = 0; j < n; j ++)
						btab [j] = false;
					for (int j = 0; j < n; j ++)
						btab [(j * j) % n] = true;
					for (int j = 0; j < n; j ++) {
						int k = (j * j + n - Nn) % n;
						int j1 = (j == 0) ? n : j;
						D.tab [i] [j] = (btab [k]) ? j1 : - j1; 
					}
				}
				D.bok = 2;
			}
			catch (NumberFormatException nfe) {}
		}
		if (obj == plus)
			X = X.add (un); // X ++;
		else if (obj == moins)
			X = X.subtract (un); // X --;
		else if (obj == auto)
			D.bauto = true;
		D.repaint();
	}
}

}