/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
	public static void main (String[] args) throws java.lang.Exception
	{
        // (4 == 2 + 2) && true
        No<Double> mais = new Galho<Double,Double>(new Soma(), new FolhaNumerica(2), new FolhaNumerica(2));
        No<Boolean> igual = new Galho<Double,Boolean>(new Igualdade(), new FolhaNumerica(4), mais);
        No<Boolean> raiz = new Galho<Boolean,Boolean>(new Conjuncao(), igual, new FolhaBooleana(true));

        boolean resultado = raiz.avaliar(); // true
        System.out.println(resultado);
	}
}

interface No<T> {
    T avaliar();
}

class FolhaNumerica implements No<Double> {
    double valor;
    public Double avaliar() { return valor; }
    FolhaNumerica(double valor) { this.valor = valor; }
}

class FolhaBooleana implements No<Boolean> {
    boolean valor;
    public Boolean avaliar() { return valor; }
    FolhaBooleana(boolean valor) { this.valor = valor; }
}

interface Operador<E,S> {
    S aplicar(Object[] operandos);
}

class Soma implements Operador<Double,Double> {
    public String toString() { return "+"; }
    public Double aplicar(Object[] operandos) {
        double ret = 0;
        for ( Object d : operandos )
            ret += (Double)d;
        return ret;
    }
}

class Conjuncao implements Operador<Boolean,Boolean> {
    public String toString() { return "&&"; }
    public Boolean aplicar(Object[] operandos) {
        for ( Object b : operandos )
            if ( !(Boolean)b )
                return false;
        return true;
    }
}

class Igualdade implements Operador<Double,Boolean> {
    public String toString() { return "=="; }
    public Boolean aplicar(Object[] operandos) {
        double primeiro = (Double)operandos[0];
        for ( Object d : operandos )
            if ( (Double)d != primeiro )
                return false;
        return true;
    }
}

class Galho<E,S> implements No<S> {
    Operador<E,S> op;
    No<E>[] operandos;

    public S avaliar() {
        List<E> args = new ArrayList<E>();
        for ( No<E> no : operandos )
            args.add(no.avaliar()); // Avalia cada sub-nó recursivamente
        return op.aplicar((E[])args.toArray()); // Passa os resultados para o operador
    }

    Galho(Operador<E,S> op, No<E>... operandos) {
        this.op = op;
        this.operandos = operandos;
    }
}
