import java.io.*;
import java.lang.*;
import java.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Twenty4 extends Applet implements ActionListener {
    TextField a, b, c, d, t;
    TextArea results;
    Label targetLabel;
    Button go;
    Integer first, second, third, fourth, target;

    //add Enter action event - clear results and start

    public void init() {
       	setLayout(null);
	a = new TextField();
	b = new TextField();
	c = new TextField();
	d = new TextField();
	t = new TextField();
	results = new TextArea();
	targetLabel = new Label("Target: ");
	go = new Button("Go!");

	a.setBounds(75, 0, 50, 25);
	b.setBounds(75, 30, 50, 25);
	c.setBounds(75, 60, 50, 25);
	d.setBounds(75, 90, 50, 25);
	t.setBounds(75, 120, 50, 25);
	results.setBounds(150, 0, 300, 300);
	targetLabel.setBounds(30, 120, 70, 25);
	go.setBounds(75, 150, 50, 25);

	add(a);
	add(b);
	add(c);
	add(d);
	add(t);
	add(targetLabel);
	add(results);
	add(go);
    }

    public void start() {
	a.addActionListener(this);
	b.addActionListener(this);
	c.addActionListener(this);
	d.addActionListener(this);
	t.addActionListener(this);
	go.addActionListener(this);
    }

    public void stop() {
	a.addActionListener(null);
	b.addActionListener(null);
	c.addActionListener(null);
	d.addActionListener(null);
	t.addActionListener(null);
	go.addActionListener(null);
    }

    public void actionPerformed(ActionEvent e) {
	if (e.getSource() == go) {
	    results.setText("");
	    doIt();
	}
    }

    public void doIt() {
	
	try {
	    first = new Integer(a.getText());
	    second = new Integer(b.getText());
	    third = new Integer(c.getText());
	    fourth = new Integer(d.getText());
	    target = new Integer(t.getText());
	}
	catch (Exception e) {
	    results.append("Error: " + ((Object) e).getClass().getName() );
	}
	
	for(int a = 1; a < 5; a++) {
	    for(int b = 1; b < 5; b++) {
		if (b!=a) {			
		    for (int c = 1; c < 5; c++) {
			if (c!=a && c!= b) {
			    for (int d = 1; d < 5; d++) {
				if (d!=a && d!=b && d!=c) {
				    // StringBuffer equation = new StringBuffer();
				    Vector equation = new Vector();
				    switch(a) {
				    case 1: equation.addElement(first.toString() + " ");
					break;
				    case 2: equation.addElement(second.toString() + " ");
					break;
				    case 3: equation.addElement(third.toString() + " ");
					break;
				    case 4: equation.addElement(fourth.toString() + " ");
					break;
				    }
				    switch(b) {
				    case 1: equation.addElement(first.toString() + " ");
					break;
				    case 2: equation.addElement(second.toString() + " ");
					break;
				    case 3: equation.addElement(third.toString() + " ");
					break;
				    case 4: equation.addElement(fourth.toString() + " ");
					break;
				    }
				    switch(c) {
				    case 1: equation.addElement(first.toString() + " ");
					break;
				    case 2: equation.addElement(second.toString() + " ");
					break;
				    case 3: equation.addElement(third.toString() + " ");
					break;
				    case 4: equation.addElement(fourth.toString() + " ");
					break;
				    }
				    
				    switch(d) {
				    case 1: equation.addElement(first.toString() + " ");
					break;
				    case 2: equation.addElement(second.toString() + " ");
					break;
				    case 3: equation.addElement(third.toString() + " ");
					break;
				    case 4: equation.addElement(fourth.toString() + " ");
					break;
				    }
				    
				    InsertSymbols(equation);
				}
			    }
			}
		    }
		}
	    }
	}
    }

    public void InsertSymbols(Vector equation) {
	String o1="", o2="", o3="";
	for (int op1 = 1; op1 < 5; op1++) {
	    for (int op2 = 1; op2 < 5; op2++) {
		for (int op3 = 1; op3 < 5; op3++) {

		    //StringBuffer equation2 = new StringBuffer(equation.toString());
		    
		    switch(op1) {
		    case 1: o1 = "+ ";
			break;
		    case 2: o1 = "- ";
			break;
		    case 3: o1 = "* ";
			break;
		    case 4: o1 = "/ ";
			break;
		    }
		    switch(op2)	{
		    case 1: o2 = "+ ";
			break;
		    case 2: o2 = "- ";
			break;
		    case 3: o2 = "* ";
			break;
		    case 4: o2 = "/ ";
			break;
		    }
		    switch(op3) {
		    case 1: o3 = "+ ";
			break;
		    case 2: o3 = "- ";
			break;
		    case 3: o3 = "* ";
			break;
		    case 4: o3 = "/ ";
			break;
		    }
		    String term1 = (String) equation.elementAt(0);
		    String term2 = (String) equation.elementAt(1);
		    String term3 = (String) equation.elementAt(2);
		    String term4 = (String) equation.elementAt(3);
		    test(new StringTokenizer(term1 + term2 + o1 + term3 + term4 + o2 + o3));
		    test(new StringTokenizer(term1 + term2 + o1 + term3 + o2 + term4 + o3));
		    test(new StringTokenizer(term1 + term2 + term3 + o1 + term4 + o2 + o3));
		    test(new StringTokenizer(term1 + term2 + term3 + o1 + o2 + term4 + o3));
		    test(new StringTokenizer(term1 + term2 + term3 + term4 + o1 + o2 + o3));

		} // end 1st for
	    } // end 2nd for
	}
    }

    public void test(StringTokenizer tokens) {
	Tree nodeTree = new Tree();

	while (tokens.hasMoreTokens()) {
	    String token = tokens.nextToken();
	    nodeTree.Insert(token);
	}
	try {
	    if (nodeTree.Eval() == target.intValue()) {
		results.append( nodeTree.PrintInfix() + " = " + target + "\n");
	    }
	}
	catch (Exception e) { }
    }
	
}

// The following code is from 15-211, HW 2
interface Node {
    public double Eval();
    public String PrintInfix();
}

class EmptyNode implements Node {
    public double Eval() throws java.lang.NullPointerException { throw new java.lang.NullPointerException(); }
    public String PrintInfix() throws java.lang.NullPointerException { throw new java.lang.NullPointerException(); }
}

class DoubleNode implements Node {
    private double value;
    public DoubleNode(String val) { 
    	value = (Double.valueOf(val)).doubleValue(); } 
    public double Eval() { return value; } 
    public String PrintInfix() {
    	if (value == (int) value) {
	    return Integer.toString( (int) value); 
    	}
    	return Double.toString( value ); 
    }
}

class PlusNode implements Node {
    private Node left;
    private Node right;
    
    public PlusNode(Node left, Node right) {
    	if (left != null) {
	    this.left = left;
    	} else {
	    this.left = new EmptyNode();
    	}
    	if (right != null) {
	    this.right = right;
	} else {
	    this.right = new EmptyNode(); 
    	}
    }
    public double Eval() { return left.Eval() + right.Eval(); }
    public String PrintInfix() { return ( left.PrintInfix() + " + " + right.PrintInfix() ); }
}

class MinusNode implements Node {

    private Node left;
    private Node right;

    public MinusNode(Node left, Node right) {
    	if (left != null) {
	    this.left = left;
	} else {
	    this.left = new EmptyNode();
    	}
	if (right != null) {
	    this.right = right;
	} else {
	    this.right = new EmptyNode(); 
    	}
    }
 
    public double Eval() { return left.Eval() - right.Eval(); }
      
    public String PrintInfix() {
    	String result = new String("");
	result += left.PrintInfix();
     	result += " - ";
	if (( right instanceof PlusNode ) || ( right instanceof MinusNode )) {
	    result += "(" + right.PrintInfix() + ")";
     	} else {
	    result += right.PrintInfix();
     	}
     	return result;  
    }
}

class MultNode implements Node {
    
    private Node left;
    private Node right;
    
    public MultNode(Node left, Node right) {
    	if (left != null) {
	    this.left = left;
	} else {
	    this.left = new EmptyNode();
	}
	if (right != null) {
	    this.right = right;
	} else {
	    this.right = new EmptyNode(); 
	}
    }
    
    public double Eval() { return left.Eval() * right.Eval(); }
    
    public String PrintInfix() {
	String result = new String();
	if (( left instanceof PlusNode ) || ( left instanceof MinusNode )) {
	    result = "(" + left.PrintInfix() + ")";
	} else {
	    result = left.PrintInfix();
	}
	result += " * ";
	if (( right instanceof PlusNode ) || ( right instanceof MinusNode )) {
	    result += "(" + right.PrintInfix() + ")";
	} else {
	    result += right.PrintInfix();
	}
	return result;
    }
}

class DivideNode implements Node {
    
    private Node left;
    private Node right;
    
    public DivideNode(Node left, Node right) {
    	if (left != null) {
	    this.left=left; 
	} else {
	    this.left = new EmptyNode(); }
	if (right != null) {
	    this.right=right;
	} else {
	    this.right = new EmptyNode();
	}
    }
    
    public double Eval() throws java.lang.ArithmeticException {
	if (right.Eval() != 0) {
	    return left.Eval() / right.Eval();
	} else {
	    throw new java.lang.ArithmeticException("Divide by 0");
	}
    }
    
    public String PrintInfix() {
    	String result = new String("");
	if (( left instanceof PlusNode ) || ( left instanceof MinusNode )) {
	    result += "(" + left.PrintInfix() + ")";
	} else {
	    result += left.PrintInfix();
	}
	result += " / ";
	if (!( right instanceof DoubleNode )) {
	    result += "(";
	}
	result += right.PrintInfix();
	if (!( right instanceof DoubleNode )) {
	    result += ")";
	}
	return result;
    }
}

class Tree {
    
    private Stack nodeStack = new Stack();  // stack of input
    
    public Tree() { }
    
    public void Insert ( String type ) throws EmptyStackException {
	if ( type.equals("+") || type.equals("-") || type.equals("*") ||
	     type.equals("/") ) {
	    Node right, left, current;
	    if (!nodeStack.empty()) {
		right = (Node) nodeStack.pop();
	    } else {
		throw new EmptyStackException();
	    }
	    if (!nodeStack.empty()) {
		left = (Node) nodeStack.pop();
	    } else {
		throw new EmptyStackException();
	    }
	    if (type.equals("+")) {
		current = new PlusNode(left, right);
	    } else if (type.equals("-")) {
		current = new MinusNode(left, right);
	    } else if (type.equals("*")) {
		current = new MultNode(left, right);
	    } else {
		current = new DivideNode(left, right);
	    }
	    nodeStack.push(current);
	} else {
	    nodeStack.push(new DoubleNode(type));
	}
    }
    
    public double Eval() {
	Node currentNode = (Node) nodeStack.peek();
	return currentNode.Eval();
    }
    
    public String PrintInfix() { 
	return ( ( Node ) nodeStack.peek()).PrintInfix();
    }
}


