/**
 * calcfract.java - 28/09/02 - 13/10/02
 * multiplication implicite
 * 
 * auteur : Jean-Paul QUELEN
 * 
 * calculatrice "grandes fractions"
 * modifications et ajout de main() le 06/01/18 et modification le 31/08/22
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.math.*;
import java.util.*;

public class calcfract extends JFrame implements ActionListener {
	static final long serialVersionUID = 220831L;
	JTextField tf[], tfd[], lignecmd, tdec, tauto;
	BigInteger zero, un, deux, trois, dix;
	JButton ok, raz, auto;
	boolean erreur, der, dernbr, resval;
	BigInteger[] pile;
	int npile;
	BigInteger[] liste;
	int nliste;
	int dec;
	String errs, calci, abcd, chiffres, lettres;
	static calculfractions cf;

	private JTextField champnum (String s, JPanel p) {
		p.add (new JLabel (s));
		JTextField tf;
		p.add (tf = new JTextField (30));
		return tf;
	}

	private JPanel np() {
		JPanel p = new JPanel ();
		p.setBackground (Color.lightGray);
		add (p);
		return p;
	}

	public calcfract (String titre) {
		super (titre);
		setBackground (Color.lightGray);
		setLayout (new GridLayout (6, 1));
		tf = new JTextField [4];
		tfd = new JTextField [4];
		JPanel p = np();
		tf [0] = champnum ("a =", p);
		tfd [0] = champnum ("~", p);
		p = np();
		tf [1] = champnum ("b =", p);
		tfd [1] = champnum ("~", p);
		p = np();
		tf [2] = champnum ("c =", p);
		tfd [2] = champnum ("~", p);
		p = np();
		tf [3] = champnum ("d =", p);
		tfd [3] = champnum ("~", p);
		p = np();
		p.add (new JLabel ("ligne de commandes :"));
		p.add (lignecmd = new JTextField (40));
		p = np();
		p.add (raz = new JButton ("raz"));
		raz.addActionListener (this);
		p.add (new JLabel ("déc :"));
		p.add (tdec = new JTextField ("2", 2));
		p.add (ok = new JButton ("exe"));
		ok.addActionListener (this);
		p.add (new JLabel ("itérations :"));
		p.add (tauto = new JTextField ("1", 4));
		p.add (auto = new JButton ("auto"));
		auto.addActionListener (this);
		zero = BigInteger.ZERO;
		un = BigInteger.ONE;
		deux = BigInteger.valueOf (2);
		trois = BigInteger.valueOf (3);
		dix = BigInteger.TEN;
//		maxint = BigInteger.valueOf((long)Integer.MAX_VALUE);
		errs = "erreur de syntaxe";
		calci = "calcul impossible ";
		abcd = "abcd";
		lettres = "abcdefghijklmnopqrstuvwxyz";
		chiffres = "0123456789.";
		cf = new calculfractions();
	}

	private void exec() {
		StringTokenizer st = new StringTokenizer (lignecmd.getText(), ";", false);
		cf.erreur = erreur = false;
		resval = true;
		String s1 = tf[3].getText();
		if (s1.equals (errs) || s1.equals (calci))
			tf[3].setText ("");
		while ((! erreur) && (st.hasMoreTokens ())) {
			StringTokenizer st1 = new StringTokenizer (st.nextToken (), "=", false);
			s1 = "";
			String s2 = "";
			try {
				s1 = st1.nextToken().trim().toLowerCase();
			}
			catch (NoSuchElementException nse) {}
			try {
				s2 = st1.nextToken().trim();
			}
			catch (NoSuchElementException nse) {
				erreur = true;
			}
			res result = new res();
			result.erreur = erreur;
			if (! erreur)
				result = cf.calculfractions (s2);
			if (result.erreur)
				tf[3].setText (errs);
			else if (! result.resval)
				tf[3].setText (calci);
			else {
				BigInteger[] bi = new BigInteger [2];
				bi = cf.simplifie (result.n, result.d);
				BigInteger n = bi [0];
				BigInteger d = bi [1];
				s2 = n.toString();
				if (d.compareTo(un) != 0)
					s2 += " / " + d.toString();
				int ind = abcd.indexOf (s1);
				if (ind >= 0) {
					tf[ind].setText (s2);
					n = n.multiply(dix.pow (dec)).divide (d);
					BigDecimal bd = new BigDecimal (n, dec);
					tfd[ind].setText (bd.toString());
				}
			}
		}
	}

	public void actionPerformed (ActionEvent evt) {
		if ((evt.getSource() == ok ) ||(evt.getSource () == auto)) {
			try {
				dec = Integer.parseInt (tdec.getText());
			}
			catch (NumberFormatException nfe) {}
			if (dec < 0)
				dec = 0;
			tdec.setText (Integer.toString (dec));
			if (evt.getSource() == ok )
				exec();
			else {
				int n = 1;
				try {
					n = Integer.parseInt (tauto.getText());
				}
				catch (NumberFormatException nfe) {}
				if (n < 0)
					n = 0;
				tauto.setText (Integer.toString (n));
				erreur = false;
				resval = true;
				for (int i = 0; (i < n) && (! erreur) && resval; i ++)
					exec();
			}
		} else if (evt.getSource() == raz) {
			for (byte i = 0; i < 4; i ++) {
				tf [i].setText ("");
				tfd [i].setText ("");
			}
//			lignecmd.setText ("");
		}
	}

/**
 * classe res
 */

protected class res {
	boolean erreur, resval;
	BigInteger n;
	BigInteger d;

	public res() {
//	erreur = false;
	resval = true;
	n = BigInteger.ZERO;
	d = BigInteger.ONE;
	}
}

/**
 * classe calculfractions
 */

protected class calculfractions {
	StringTokenizer st;
	boolean erreur, der, dernbr, resval;
	BigInteger[] pile;
	int npile;
	BigInteger[] liste;
	int nliste;

//	public calculfractions() {
//}

	private void parse() {
		der = true;
		int i = 0;
		while (st.hasMoreTokens()) {
			if (p (i) == -1)
				break;
			i++;
		}
	}

	private int p (int i) {
		int resultat = -1;
		String s = "";
		while ((st.hasMoreTokens ()) && (s.length () == 0))
			s = st.nextToken ().trim ();
		if (s.length() == 0)
			resultat = 0;
		else {
			try {
				addListeN (new BigInteger (s));
				resultat = 0;
				der = false;
			}
			catch (NumberFormatException excp) {
				s = s.toLowerCase();
				try {
					BigDecimal bd = new BigDecimal (s);
					int sc = bd.scale();
// non implanté dans IE5 : addListeN (bd.unscaledValue ());
					addListeN (bd.movePointRight(sc).toBigInteger ());
					addListeN (dix.pow (sc));
					addListe (-21);
					resultat = 0;
					der = false;
				}
				catch (NumberFormatException excp1) {
					if (s.equals ("a")) {
						addListe (1);
						resultat = 0;
						der = false;
					} else if (s.equals ("b")) {
						addListe (2);
						resultat = 0;
						der = false;
					} else if (s.equals ("c")) {
						addListe (3);
						resultat = 0;
						der = false;
					} else if (s.equals ("d")) {
						addListe (4);
						resultat = 0;
						der = false;
					} else if (s.equals ("(")) {
						parse();
						resultat = 0;
					} else if (s.equals (",")) {
						parse();
						resultat = 0;
					} else if (s.equals (")")) {
						der = false;
					} else {
						int op = nop (s);
						if ((op == -22) && (i == 0))
							addListeN (zero);
						if ((op == 14) || (op == 15)) {
							p (0);							// i
							addListe (op);
						} else {
							int opt = Math.abs (op);
							int opa = (i > 0) ? liste [nliste - 1].intValue () : 0;
							int opat = Math.abs (opa);
							if ((opat < 20) || !der)
								opat = 0;
							int opb = 0;
							int opbt = 0;
							if ((opt >= 20) && (opt < opat)) {
								nliste--;
								if (nliste >= 1)
									opb = liste [nliste - 1].intValue();
								opbt = Math.abs (opb);
								if (opbt < 20)
									opbt = 0;
								if ((opt < opbt) && (opbt < opat))
									nliste--; 
							}
							boolean b = der;
							resultat = p (0);					//i
							addListe (op);
							der = b;
							if (opt >= 20) {
								if ((opt < opbt) && (opbt < opat))
									addListe (opb);
								if (opt < opat)
									addListe (opa);
							}
							der = true;
						}
					}
				}
			}
		}
		return resultat;
	}

	private int nop (String s) {
		int r = nop1 (s);
		if (r == 0)
			erreur = true;
		return r;
	}

	private int nop1 (String s) {
		int r = 0;
		if (s.equals ("abs"))
			r = 5;
		else if (s.equals ("ent"))
			r = 6;
		else if (s.equals ("frac"))
			r = 7;
		else if (s.equals ("max"))
			r = 14;
		else if (s.equals ("min"))
			r = 15;
		else if (s.equals ("^"))
			r = 20;
		else if (s.equals ("*"))
			r = 21;
		else if (s.equals ("/"))
			r = -21;
		else if (s.equals ("+"))
			r = 22;
		else if (s.equals ("-"))
			r = -22;
		return r;
	}

	private void addListe (int x) {
		int max = (liste == null) ? 0 : liste.length;
		if (nliste > max - 2)
			if (liste == null) {
				max = 20;
				liste = new BigInteger [max];
			} else {
				max *= 2;
				BigInteger[] nf = new BigInteger [max];
				System.arraycopy (liste, 0, nf, 0, liste.length);
				liste = nf;
			}
		liste [nliste++] = new BigInteger (Integer.toString(x));
	}

	private void addListeN (BigInteger x) {
		int max = (liste == null) ? 0 : liste.length;
		if (nliste > max - 2)
			if (liste == null) {
				max = 20;
				liste = new BigInteger [max];
			} else {
				max *= 2;
				BigInteger[] nf = new BigInteger [max];
				System.arraycopy (liste, 0, nf, 0, liste.length);
				liste = nf;
			}
		liste [nliste++] = zero;
		liste [nliste++] = x;
	}

	private BigInteger[] lregistre (int id) {
		calculfractions cf = new calculfractions();
		res result = new res();
		result = cf.calculfractions(tf[id-1].getText());
		erreur = result.erreur;
		resval = result.resval;
		BigInteger[] bi = new BigInteger [2];
		bi [0] = result.n;
		bi [1] = result.d;
		return bi;
	}

	private void calcul() {
		resval = true;
		int max = liste.length;
		if ((pile == null) || (pile.length < max + max))
			pile = new BigInteger [max + max];
		if ((liste == null) || (nliste == 0))
			resval = false;
		else {
			npile = 0;
			int i = 0;
			while ((i < nliste) && (!erreur) && (resval)) {
				int id = liste [i++].intValue ();
				if (id == 0) {
					pile [npile++] = liste [i++];
					pile [npile++] = un;
			} else if ((id > 0) && (id < 5)) {
				BigInteger[] bi = lregistre (id);
				pile [npile++] = bi [0];
				pile [npile++] = bi [1];
			} else if ((id >= 5) && (id < 14)) {
				if (npile == 0)
					erreur = true;
				else {
					BigInteger d = pile [-- npile];
					BigInteger n = pile [-- npile];
					if (d.compareTo (zero) < 0) {
						d = d.negate();
						n = n.negate();
					}
					switch (id) {
						case 5 :
							n = n.abs();
							break;
						case 6 :
							boolean b = (n.compareTo (zero) < 0);
							boolean b1 = false;
							try {
								b1 = (n.remainder (d).compareTo (zero) != 0);
								n = n.abs().divide (d); d = un;
							}
							catch (ArithmeticException ae) {
								resval = false;
							}
							if (b && b1)
								n = n.add(un).negate();
							else if (b)
								n = n.negate();
							break;
						case 7 :
							b = (n.compareTo (zero) < 0);
							try {
								n = n.abs().remainder(d);
							}
							catch (ArithmeticException ae) {
								resval = false;
							}
							if (b && (n.compareTo (zero) != 0))
								n = d.subtract(n);
					}
					pile [npile ++] = n;
					pile [npile ++] = d;
				}
			} else {
				if (npile < 4)
					erreur = true;
				else {
					BigInteger n1 = pile[npile - 4];
					BigInteger d1 = pile [npile - 3];
					BigInteger d2 = pile [--npile];
					BigInteger n2 = pile [--npile];
					if (d2.compareTo (zero) < 0) {
						d2 = d2.negate();
						n2 = n2.negate();
					}
					if (d1.compareTo (zero) < 0) {
						d1 = d1.negate();
						n1 = n1.negate();
					}
					switch (id) {
						case 14 :
							if (n1.multiply (d2).subtract (n2.multiply (d1)).compareTo (zero) < 0) {
								n1 = n2;
								d1 = d2;
							}
							break;
						case 15 :
							if (n1.multiply (d2).subtract (n2.multiply (d1)).compareTo (zero) > 0) {
								n1 = n2;
								d1 = d2;
							}
							break;
						case 20 :
							BigInteger[] bi = new BigInteger [2];
							bi = simplifie (n1, d1);
							n1 = bi [0];
							d1 = bi [1];
							bi = simplifie (n2, d2);
							n2 = bi [0];
							d2 = bi [1];
							if (d2.compareTo (un) != 0)
								resval = false;
							else if (n2.compareTo (zero) < 0) {
								n2 = n2.negate();
								bi [0] = n1;
								n1 = d1;
								d1 = bi [0];
							}
							try {
								n1 = n1.pow (n2.intValue());
							}
							catch (ArithmeticException ae) {
								resval = false;
							}
							try {
								d1 = d1.pow (n2.intValue());
							}
							catch (ArithmeticException ae) {
								resval = false;
							}
							break;
						case 21 :
							n1 = n1.multiply (n2);
							d1 = d1.multiply (d2);
							break;
						case -21 :
							resval = (n2.compareTo (zero) != 0);
							n1 =n1.multiply (d2);
							d1 = d1.multiply (n2);
							break;
						case 22 :
							n1 = n1.multiply (d2).add (n2.multiply (d1));
							d1 = d1.multiply (d2);
							break;
						case -22 :
							n1 = n1.multiply (d2).subtract (n2.multiply (d1));
							d1 = d1.multiply (d2);
					}
					pile [npile - 2] = n1;
					pile [npile - 1] = d1;
				}
			}
		}
		if (resval)
			if (npile != 2)
				erreur = true;
		}
	}

	public BigInteger[] simplifie (BigInteger n, BigInteger d) {
		if (d.compareTo (zero) < 0) {
			d = d.negate();
			n = n.negate();
		}
		BigInteger pgcd = n.gcd (d);
		n = n.divide (pgcd);
		d = d.divide (pgcd);
		BigInteger[] bi = {n, d};
		return bi;
	}

	public res calculfractions (String s) {
		s = s.toLowerCase().trim();
		res result = new res ();
		if (s.length() != 0) {
////////// multiplication implicite 13/10/02
			String s1 = "";
			String mot = "";
			char anc = ' ';
			boolean space = false;
			for (int iof = 0; iof < s.length(); iof++) {
				char c = s.charAt (iof);
				if ((c == ' '))
					space = true;
				else {
					boolean numanc = chiffres.indexOf (anc) >= 0;
					boolean alphanc = lettres.indexOf (anc) >= 0;
					boolean numc = chiffres.indexOf (c) >= 0;
					boolean alphc = lettres.indexOf (c) >= 0;
					boolean po = (c == '(');
					boolean pf = (anc == ')');
					if (alphanc)
						mot += anc;
					boolean nfct = (nop1 (mot) == 0);
					alphanc = alphanc && nfct;
					if ( space && numanc && numc || space && alphanc && alphc
							|| numanc && (alphc || po) || alphanc && (numc || po)
							|| (pf && (numc || alphc || po)))
						s1 += '*';
					else if (! nfct && numc)
						s1 += ' ';
						anc = c;
						space = false;
						if (! (alphc && alphanc))
							mot = "";
				}
				s1 += c;
			}

			st = new StringTokenizer (s1, "+-*/^(), ", true);
			nliste = 0;
			parse();
			calcul();
			if ( !(result.erreur = erreur))
				if (result.resval = resval) {
					BigInteger[] bi = new BigInteger [2];
					bi = simplifie (pile [0], pile [1]);
					result.n = bi [0];
					result.d = bi [1];
				}
			} else
				result.erreur = true;
			return result;
		}
	}


	public static void main (String [] args) {
		calcfract f = new calcfract ("calcfract");
		f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		f.setSize (800, 400);
		f.setVisible (true);
	}

}
