// <XMP>
// Decomposition of a number in a sum of four cubes
//
// Written by Dario Alejandro Alpern (Buenos Aires - Argentina)
// Last updated March 27th, 2005.
// See http://www.alpertron.com.ar/FCUBES.HTM
//
// No part of this code can be used for commercial purposes without
// the written consent from the author. Otherwise it can be used freely
// except that you have to write somewhere in the code this header.
// 
import java.applet.*;
import java.awt.*;
import java.math.*;

public final class fcubes extends Applet implements Runnable {

  private TextField textNumber;
  private TextArea lowerTextArea;
  private boolean TerminateThread;
  private Thread calcThread;
  private String StringToLabel;
  private StringBuffer textAreaContents;
  private int digitsInGroup = 6;
  private String paneFooter;
  private BigInteger Nbr, mult1, mult2, mult3, mult4;
  private int[] sums =
  {
    6, 0, 1, -1, -1, 0, -1, 0, 1, 1,
    6, 3, 1, 0, -1, 4, 2, -5, -2, 4,
    18, 1, 2, 14, -2, -23, -3, -26, 3, 30,
    18, 7, 1, 2, 6, -1, 8, -2, -9, 2,
    18, 8, 1, -5, -1, 14, -3, 29, 3, -30,
    18, 10, 1, 6, -1, -15, -3, -32, 3, 33,
    18, 11, 1, -1, 6, 7, 8, 10, -9, -11,
    18, 17, 2, -12, -2, 21, -3, 23, 3, -27,
    54, 20, 3, -11, -3, 10, 1, 2, -1, 7,
    72, 56, -9, 4, 1, 4, 6, -2, 8, -4,
    108, 2, -1, -22, 1, 4, -3, -41, 3, 43,
    216, 92, 3, -164, -3, 160, 1, -35, -1, 71,
    270, 146, -60, 91, -3, 13, 22, -37, 59, -89,
    270, 200, 3, 259, -3, -254, 1, 62, -1, -107,
    270, 218, -3, -56, 3, 31, -5, -69, 5, 78,
    432, 380, -3, 64, 3, -80, 2, -29, -2, 65,
    540, 38, 5, -285, -5, 267, 3, -140, -3, 190,
    810, 56, 5, -755, -5, 836, 9, -1445, -9, 1420,
    1080, 380, -1, -1438, 1, 1258, -3, -4037, 3, 4057,
    1620, 1334, -5, -3269, 5, 3107, -9, -5714, 9, 5764,
    1620, 1352, -5, 434, 5, -353, 9, -722, -9, 697,
    2160, 362, -5, -180, 5, 108, -6, -149, 6, 199,
    6480, 794, -5, -83, 5, 11, -6, -35, 6, 85, 
  };

  public void init() {
    Label labelTop, labelBottom;
    setBackground(Color.lightGray);
    setLayout(null);
    labelTop = new Label("Type number or numerical expression here and press Return");
    labelTop.reshape(10, 10, 570, 14);
    labelTop.setFont(new Font("Courier", Font.PLAIN, 12));
    labelTop.setAlignment(Label.CENTER);
    add(labelTop);
    textNumber = new TextField(1000);
    textNumber.reshape(10, 30, 570, 30);
    textNumber.setEditable(true);
    add(textNumber);
    lowerTextArea = new TextArea("", 6,75, TextArea.SCROLLBARS_VERTICAL_ONLY);
    lowerTextArea.reshape(10, 70, 570, 200);
    lowerTextArea.setEditable(false); 
    lowerTextArea.setFont(new Font("Courier", Font.PLAIN, 12));
    add(lowerTextArea);
    labelBottom = new Label("Written by Dario Alejandro Alpern. Last updated March 27th, 2005");
    labelBottom.reshape(10, 280, 570, 14);
    labelBottom.setFont(new Font("Courier", Font.PLAIN, 12));
    labelBottom.setAlignment(Label.CENTER);
    add(labelBottom);
    textNumber.requestFocus();
    validate();
    }

  public void destroy() {           /* Applet end */
    TerminateThread = true;
  }

public boolean handleEvent(Event e) {
  if (e.id == Event.KEY_PRESS && e.key == Event.ENTER) {
    if (calcThread != null) {
      TerminateThread = true;
      try {
        calcThread.join();        /* Wait until the solving thread dies */
        } catch (InterruptedException ie) {};
      }
    calcThread = new Thread(this);  /* Start solving thread */
    calcThread.start();
    return true;
    }
  return false;
  }

  public void run() {
    BigInteger nbr, k, x, Mult1, Mult2, Mult3, Mult4;
    BigInteger mod = BigInteger.valueOf(0);
    BigInteger [] ExpressionResult = new BigInteger[1];
    int ExpressionRC;
    long StartTime;
    int i, j, mask;
    boolean converted = false;

    StartTime = System.currentTimeMillis();
    TerminateThread = false;
    mult1 = mult2 = mult3 = mult4 = BigInteger.valueOf(0);
    lowerTextArea.setText("Computing input expression...");
    try {
      ExpressionRC = expression.ComputeExpression(textNumber.getText().trim(),1,ExpressionResult);
    } catch (OutOfMemoryError e) {
      lowerTextArea.setText("Out of memory.");
      return;
    } catch (ArithmeticException e) {
      return;
    }
    Nbr = nbr = ExpressionResult[0];
    if (ExpressionRC != 0) {
      switch (ExpressionRC) {
        case -2:
          lowerTextArea.setText("Number too high (more than 1000 digits).");
          break;
        case -3:
          lowerTextArea.setText("Intermediate expression too high (more than 2000 digits).");
          break;
        case -4:
          lowerTextArea.setText("Non-integer division.");
          break;
        case -5:
          lowerTextArea.setText("Parentheses mismatch.");
          break;
        case -6:
          lowerTextArea.setText("Syntax error.");
          break;
        case -7:
          lowerTextArea.setText("Too many parentheses.");
          break;
        case -8:
          lowerTextArea.setText("Invalid parameter.");
          break;
        }
      return;
      }
    int mod18 = nbr.mod(BigInteger.valueOf(18)).intValue();
    if (mod18 == 4 || mod18 == 5 || mod18 == 13 || mod18 == 14) {
      lowerTextArea.setText("This applet does not work if the number is\ncongruent to 4 or 5 (mod 9).");
      return;
      }
    if (mod18 == 16) {
      converted = true;
      nbr = nbr.negate();
      }
    for (i=0; i<sums.length; i+=10) {
      mod = BigInteger.valueOf(sums[i]);
      if (nbr.mod(mod).intValue() == sums[i+1]) {
        break;
        }
      }
    if (i<sums.length) {
      x = nbr.subtract(BigInteger.valueOf(sums[i+1])).divide(mod);
      Mult1 = BigInteger.valueOf(sums[i+2]).multiply(x).
              add(BigInteger.valueOf(sums[i+3]));
      Mult2 = BigInteger.valueOf(sums[i+4]).multiply(x).
              add(BigInteger.valueOf(sums[i+5]));
      Mult3 = BigInteger.valueOf(sums[i+6]).multiply(x).
              add(BigInteger.valueOf(sums[i+7]));
      Mult4 = BigInteger.valueOf(sums[i+8]).multiply(x).
              add(BigInteger.valueOf(sums[i+9]));
      }
    else {
      mod = BigInteger.valueOf(54);
      if (nbr.mod(mod).intValue() == 2) {
        x = nbr.subtract(BigInteger.valueOf(2)).divide(mod);
        Mult1 = BigInteger.valueOf(29484).multiply(x).
                add(BigInteger.valueOf(2211)).multiply(x).
                add(BigInteger.valueOf(43));
        Mult2 = BigInteger.valueOf(-29484).multiply(x).
                add(BigInteger.valueOf(-2157)).multiply(x).
                add(BigInteger.valueOf(-41));
        Mult3 = BigInteger.valueOf(9828).multiply(x).
                add(BigInteger.valueOf(485)).multiply(x).
                add(BigInteger.valueOf(4));
        Mult4 = BigInteger.valueOf(-9828).multiply(x).
                add(BigInteger.valueOf(-971)).multiply(x).
                add(BigInteger.valueOf(-22));
        }
      else {
        mod = BigInteger.valueOf(83*108);
        if (nbr.mod(mod).intValue() == 83*46) {
          x = nbr.subtract(BigInteger.valueOf(83*46)).divide(mod);
          Mult1 = BigInteger.valueOf(29484).multiply(x).
                  add(BigInteger.valueOf(25143)).multiply(x).
                  add(BigInteger.valueOf(5371));
          Mult2 = BigInteger.valueOf(-29484).multiply(x).
                  add(BigInteger.valueOf(-25089)).multiply(x).
                  add(BigInteger.valueOf(-5348));
          Mult3 = BigInteger.valueOf(9828).multiply(x).
                  add(BigInteger.valueOf(8129)).multiply(x).
                  add(BigInteger.valueOf(1682));
          Mult4 = BigInteger.valueOf(-9828).multiply(x).
                  add(BigInteger.valueOf(-8615)).multiply(x).
                  add(BigInteger.valueOf(-1889));
          }
        else {
          lowerTextArea.setText("Computing solutions...");
          // Perform Pell solution of Demjanenko's theorem
            // Using these values of P, Q, R and S, a and b will be
            // always one and zero (mod 6) respectively.
          BigInteger P = new BigInteger("-112488782561");
          BigInteger Q = new BigInteger("-6578430178320");
          BigInteger R = new BigInteger("-1923517596");
          BigInteger S = P;
          BigInteger tmpA;
          int mod83 = nbr.mod(BigInteger.valueOf(83)).intValue();
          int pow = 71;
          int exp = 0;
          while (pow != mod83) {
            exp++;
            pow = (pow*50)%83;
            }
          if (exp > 82/2) {
            exp = 82 - exp;
            Q = Q.negate();
            R = R.negate();
            }
          BigInteger P1 = BigInteger.valueOf(1);
          BigInteger Q1 = BigInteger.valueOf(0);
          BigInteger R1 = Q1;
          BigInteger S1 = P1;
          mask = 32;
          while (mask > 0) {
            BigInteger tmpP1 = P1.pow(2).add(Q1.multiply(R1));
            BigInteger tmpQ1 = P1.add(S1).multiply(Q1);
            BigInteger tmpR1 = P1.add(S1).multiply(R1);
            BigInteger tmpS1 = S1.pow(2).add(Q1.multiply(R1));
            P1 = tmpP1;
            Q1 = tmpQ1;
            R1 = tmpR1;
            S1 = tmpS1;
            if ((exp & mask) != 0) {
              tmpP1 = P.multiply(P1).add(Q.multiply(R1));
              tmpQ1 = P.multiply(Q1).add(Q.multiply(S1));
              tmpR1 = R.multiply(P1).add(S.multiply(R1));
              tmpS1 = R.multiply(Q1).add(S.multiply(S1));
              P1 = tmpP1;
              Q1 = tmpQ1;
              R1 = tmpR1;
              S1 = tmpS1;
              }
            mask /= 2;
            }
          BigInteger a = P1.multiply(BigInteger.valueOf(-3041)).
                         add(Q1.multiply(BigInteger.valueOf(-52)));
          BigInteger b = R1.multiply(BigInteger.valueOf(-3041)).
                         add(S1.multiply(BigInteger.valueOf(-52)));

          Mult1 = a.multiply(BigInteger.valueOf(27)).
                  add(b.multiply(BigInteger.valueOf(-928)));
          Mult2 = a.multiply(BigInteger.valueOf(-9)).
                  add(b.multiply(BigInteger.valueOf(-602)));
          Mult3 = a.multiply(BigInteger.valueOf(25)).
                  add(b.multiply(BigInteger.valueOf(-2937)));
          Mult4 = a.multiply(BigInteger.valueOf(-19)).
                  add(b.multiply(BigInteger.valueOf(2746)));
          k = nbr.subtract(Mult1.pow(3)).subtract(Mult2.pow(3)).
                  subtract(Mult3.pow(3)).subtract(Mult4.pow(3)).
                  divide(BigInteger.valueOf(18*83));
          Mult1 = Mult1.add(k.multiply(BigInteger.valueOf(10)));
          Mult2 = Mult2.add(k.multiply(BigInteger.valueOf(-19)));
          Mult3 = Mult3.add(k.multiply(BigInteger.valueOf(-24)));
          Mult4 = Mult4.add(k.multiply(BigInteger.valueOf(27)));
          }
        }
      }

    if (converted) {
      Mult1 = Mult1.negate();
      Mult2 = Mult2.negate();
      Mult3 = Mult3.negate();
      Mult4 = Mult4.negate();
      nbr = nbr.negate();
      }
    // Validate

    lowerTextArea.setText("Validating...");
    if (Mult1.pow(3).add(Mult2.pow(3)).add(Mult3.pow(3)).add(Mult4.pow(3)).
        equals(nbr) == false)
    {
      lowerTextArea.setText("Internal error!\n\nPlease send the number to the author of the applet");
      return;
    }

    lowerTextArea.setText("Formatting output...");
    mult1 = Mult1;
    mult2 = Mult2;
    mult3 = Mult3;
    mult4 = Mult4;
    Nbr = nbr;
    long t=(System.currentTimeMillis()-StartTime)/1000;
    String timeElapsed = "Time elapsed: "+t/86400+"d "+(t%86400)/3600+"h "+((t%3600)/60)+"m "+(t%60)+"s";
    paneFooter = "\n\n"+timeElapsed;
    ShowPane();
  }

  private void ShowPane()
  {
    textAreaContents = new StringBuffer();
    StringToLabel = "n = ";
    insertBigNbr(Nbr);
    textAreaContents = textAreaContents.append(StringToLabel + "\n\n").
                       append("n = a^3 + b^3 + c^3 + d^3\n");
    StringToLabel = "a = ";            // Start new line.
    insertBigNbr(mult1);
    textAreaContents = textAreaContents.append(StringToLabel + "\n");
    StringToLabel = "b = ";            // Start new line.
    insertBigNbr(mult2);
    textAreaContents = textAreaContents.append(StringToLabel + "\n");
    StringToLabel = "c = ";            // Start new line.
    insertBigNbr(mult3);
    textAreaContents = textAreaContents.append(StringToLabel + "\n");
    StringToLabel = "d = ";            // Start new line.
    insertBigNbr(mult4);
    textAreaContents = textAreaContents.append(StringToLabel + paneFooter);
    lowerTextArea.setText(textAreaContents.toString());
  }

  private void addStringToLabel(String value) {
    if (value.length() + 1 + StringToLabel.length() >= 79) {
      textAreaContents = textAreaContents.append(StringToLabel + "\n");
      StringToLabel = value+" ";
    }
    else {
      StringToLabel += value+" ";
    }
  }

  private void insertBigNbr(BigInteger N)
  {
    int i,dig,len;
    dig = digitsInGroup & 0x3FF;
    String value = N.toString();
    len = value.length();
    i = (len + dig - 1)%dig + 1;
    addStringToLabel(value.substring(0,i));
    while (i<value.length())
    {
      addStringToLabel(value.substring(i,i+dig));
      i += dig;
    }
    if (value.charAt(0) == '-')
    {
      len--;
    }
    if (len > 100)
    {
      addStringToLabel("("+len+" digits)");
    }
  }

  public String getTextFromPane()
  {
    if ((calcThread == null || calcThread.isAlive() == false) &&
         lowerTextArea.getText() != "")
    {
      String text="<HTML><TITLE>Sum of four cubes</TITLE><BODY><H2>Sum of four cubes</H2><P><I>Written by Dario Alejandro Alpern (alpertron@hotmail.com)</I><P><PRE>"
               + lowerTextArea.getText() + "</PRE><BODY></HTML>";
      return text;
    }
    return "";
  }

  public void setDigits(int digits)
  {
    digitsInGroup = digits;
    if ((calcThread == null || calcThread.isAlive() == false) &&
         lowerTextArea.getText() != "")
    {
      ShowPane();
    }
  }
}       // End applet class

// </XMP>
