// Testapplet mit etwas AWT und Zahlentheorie
// 12.11.2010 Einführung von Signaturen zur Performanzverbesserung
// 15.11.2010 Richtige Erkennung 0 mod p versus 0 mod p^2; Anhand Signatur erkennen, ob es überhaupt Quadrat gibt.
// 16.11.2010 mod p gibt es ein Muster, da gelegentlich für die Fälle mod p^2 == 0 es Ausreisser gibt
// --> läßt sich da was machen ?
// 21.12.2010 Modus indeterministisch verbessert zu Filterung mit Primzahlen; damit auch dies komplett
//            ausserdem bei Primzahlen noch Vereinfachungen; fehlt noch Verb. bei zusammenges. n
// 27.12.2010 Diverse Fehlerkorrekturen, so dass es für verschiedene Stufen funktioniert; Faktorisierung von n
//            zur Vereinfachung; hprof-Ausgabe verwendet
// 28.12.2010 Numerische Überläufe für long besser abfangen. Stufe pro Rechenschritt auf maximalen Wert begrenzen
// Oktober '12: Wurzel_Thread ausgelagert, Primbasis variabel erweiterbar


import java.awt.*;
import java.math.BigInteger;


public final class WurzelApplet3 extends Frame /* ehem. Applet */
{
  private Label l_modul, l_quadrat, l_status;
  private TextField tf_modul, tf_quadrat;
  private Button b_berechnen;

  // Variablen für Berechnung
  private static Wurzel_Thread t = null;
  private static BigInteger n = null, q = null;


  // a) für GUI
  public WurzelApplet3()
  {
    super("Modulare Quadratwurzel-Berechnung mod n");

    setLayout(new GridLayout(3,2));
 
    l_modul = new Label("Zahl n -->"); add(l_modul);
    tf_modul = new TextField(""); add(tf_modul);
    l_quadrat = new Label("Quadrat q -->"); add(l_quadrat);
    tf_quadrat = new TextField(""); add(tf_quadrat);

    b_berechnen = new Button("Berechnen"); add(b_berechnen);
    l_status = new Label(); add(l_status);
    this.pack();
  }

  // b) für Kommandozeile. Startet den Thread ohne Ereignisbehandlung oder Anzeige
  public static void main (String [] args)
  {
    // test_quadrate(); if (true) System.exit(0);  // Testmöglichkeit komplett pro n

    if (args.length < 1)  // a) GUI
    {
      WurzelApplet3 wa = new WurzelApplet3();
      wa.show();
    }
    else if (args.length < 2)  // b) Fehlermeldung
    {
      System.out.println ("Aufruf: java WurzelApplet3 <quadrat> <zahl>"); System.exit(1);
    }
    else
    {
      try  // c) Kommandozeile
      {
        q = new BigInteger(args[0]); n = new BigInteger(args[1]);
        if (q.compareTo(BigInteger.ZERO) < 0 || n.compareTo(BigInteger.ZERO) < 0 ||
            n.compareTo(q) <= 0) throw new Exception("Eingabewerte: negativ oder quadrat >= zahl");

        t = new Wurzel_Thread(q, n, null, null);
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();
        t = null; n = null; q = null;
      } 
      catch (Exception ex) { System.out.println ("Ungültige Eingabezahlen"); System.exit(1); }
    }

  }


  // Testet, ob alle Quadrate einer Zahl errechnet werden können. Fehler sind, nicht gefunden, wenn existent,
  // oder Rückgabewert falsch
  public static boolean test_quadrate()
  {
    BigInteger ZWEI = BigInteger.valueOf(2);
    BigInteger z1 = BigInteger.valueOf(179);  // 467
    BigInteger z2 = BigInteger.valueOf(227);  // 587
    BigInteger zus = z1.multiply(z2); System.out.println ("Test für " + zus);
    for (int i = 0; i < zus.longValue(); ++i)
    {
      BigInteger wert = BigInteger.valueOf(i);
      if (Wurzel_Thread.jacobiSymbol(wert, z1) == +1 &&
          Wurzel_Thread.jacobiSymbol(wert, z2) == +1)
      {
        Wurzel_Thread t = new Wurzel_Thread(BigInteger.valueOf(i), zus, null, null);
        BigInteger wurzel = t.istWurzelGesamt(BigInteger.valueOf(i));
        if (wurzel == null) { System.out.println ("Nicht gefunden: " + i); System.exit(1); }
        else { 
          BigInteger vergleich = wurzel.modPow(ZWEI, zus);
          if (! vergleich.equals(BigInteger.valueOf(i)))
          {
            System.out.println ("Falscher Rückgabewert " + wert + " bei " + i); System.exit(1);
          }
        }
        t = null;
      }
    }
    return false;
  }

  public boolean handleEvent(Event evt)
  {
    // System.out.println (evt.id);
    if (evt.id == Event.WINDOW_DESTROY) {
      if (t != null && t.isAlive()) t.laufStatus = 2;
      this.dispose();
    }

    // 1001 ist Drücken der Maus, 401 drücken Eingabetaste
    if ((evt.target != b_berechnen) || (evt.id != 1001 && evt.id != 401)) return super.handleEvent(evt);

    // Ein neuer Thread soll gestartet werden, wenn
    // a) berechnen gedrückt wurde und die Zahlen zu den letzten verschieden waren
    // b) weiter mit verschiedenen Werten gedrückt wurde
    boolean berechnen = ((Button)evt.target).getLabel().equals("Berechnen");
    boolean weiter = ((Button)evt.target).getLabel().equals("Weiter");
    if (berechnen || weiter)
    {
      if ((t == null) || 
          !tf_modul.getText().equals(n.toString()) || 
          !tf_quadrat.getText().equals(q.toString()) )
      {
        // QuadratWurzel neu berechnen (u.U. auch wegen Änderung der Eingabedaten)
        try
        {
          n = new BigInteger(tf_modul.getText());
        }
        catch (NumberFormatException ex) { l_status.setText("Fehler bei n"); return true; }
        try
        {
          q = new BigInteger(tf_quadrat.getText());
        }
        catch (NumberFormatException ex) { l_status.setText("Fehler bei Quadrat"); return true; }
        // Einschränkung auf positive Werte
        if (n.compareTo(BigInteger.ZERO) < 0 ||
            q.compareTo(BigInteger.ZERO) < 0)
        {
          l_status.setText("Negativer Eingabewert nicht möglich"); return true;
        }

        if (t != null && t.isAlive()) { t.weitermachen((short)2); t = null; }

        if (q.compareTo(n) >= 0) { l_status.setText("Quadrat >= n"); return true; }
        if (Wurzel_Thread.jacobiSymbol(q, n) != +1) { l_status.setText("Kein Quadrat"); return true; }
 
        BigInteger erg [] = new BigInteger [1];
        t = new Wurzel_Thread(q, n, l_status, b_berechnen);
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();
        b_berechnen.setLabel("Anhalten"); l_status.setText("");
      }
      else if ((t != null) && weiter)
      {
        System.out.println("Thread weitermachen");
        if (t.isAlive()) t.weitermachen((short)0);
        b_berechnen.setLabel("Anhalten"); l_status.setText("");
      }
      else
      {
        System.out.println ("Keine Aktion");
      }
    }
    else if (((Button)evt.target).getLabel().equals("Anhalten"))
    {
      System.out.println("Thread unterbrechen");
      if (t.isAlive()) t.laufStatus = 1; // weder suspend() noch interrupt() brachten Erfolg
      b_berechnen.setLabel("Weiter"); l_status.setText("");
    }

    return true;  // behandelt
  }

}


