import tester.*;

/*
              +------+
              | IExp |<---------------+-+
              +------+                | |
                / \                   | |
                ---                   | |
                 |                    | |
       ---------------------          | |
       |                   |          | |
 +-----------+     +--------------+   | |
 | Value     |     | BinOp        |   | |
 +-----------+     +--------------+   | |
 | int value |  +--| IOperator op |   | |
 +-----------+  |  | IExp left    |---+ |
                |  | IExp right   |-----+
                |  +--------------+
                v
        +---------------------------+
        | IOperator                 |
        +---------------------------+
        | int eval(int r1, int r2); |
        +---------------------------+
  
 */

// an interface to represent the action of an operator
interface Operator{
	
	// produce a value of the type Integer from two operands of the type Integer
	public Integer eval(Integer rand1, Integer rand2);
}

// to represent the addition arithmetic operator
class Plus implements Operator{
	
	// produce a sum of the two given operands
	public Integer eval(Integer rand1, Integer rand2){
		return rand1 + rand2;
	}
}

// to represent an integer arithmetic expression 
interface IExp{

	// produce the value this expression represents
	public Integer eval();
}

// an interface to represent the actual value in an expression
class Value implements IExp{
	Integer data;
	
	Value(Integer data){
		this.data = data;
	}
	
	// produce the value this item represents
	public Integer eval(){
		return data;
	}
}

// a class to represent a binary expression
class BinOp implements IExp{
	Operator op;
	IExp rand1;
	IExp rand2;
	
	BinOp(Operator op, IExp rand1, IExp rand2){
		this.op = op;
		this.rand1 = rand1;
		this.rand2 = rand2;
	}
	
	public Integer eval(){
		return op.eval(rand1.eval(), rand2.eval());
	}
}

// tests for the classes that represent an expression
class ExamplesExpression{
	ExamplesExpression(){}
	
	Operator plus = new Plus();
	
	IExp n5 = new Value(5);
	IExp negn5 = new Value(-5);
	IExp n8 = new Value(8);
	
	IExp exp = new BinOp(this.plus, 
                       this.n5, 
                       new BinOp(this.plus, 
                                 this.n5, 
                                 this.n8));
                                 
  // tests for the method eval for the classes that represent an expression                                          
	void testPlus(Tester t){
		t.checkExpect((new BinOp(this.plus, this.n5, this.n8)).eval(), 13);
		t.checkExpect((new BinOp(this.plus, this.negn5, this.n8)).eval(), 3);	
		
		t.checkExpect(this.n5.eval(), 5);
		t.checkExpect(this.n8.eval(), 8);

		t.checkExpect(this.plus.eval(this.n5.eval(), this.n8.eval()), 13);
		
		t.checkExpect(this.exp.eval(), 18);
		
	}	
}