Operation.java

package net.bmahe.genetics4j.gp;

import java.util.List;
import java.util.function.BiFunction;

import org.immutables.value.Value;
import org.immutables.value.Value.Parameter;

/**
 * Represents an operation (function or terminal) in genetic programming.
 * 
 * <p>An operation defines a computational unit that can be used as a node in genetic programming trees.
 * Operations can be either functions (with arguments) or terminals (without arguments). Each operation
 * has a defined signature including the types it accepts as input and the type it returns.
 * 
 * <p>Operations are the building blocks of genetic programming expressions and define:
 * <ul>
 * <li>The computation to perform</li>
 * <li>Type constraints for strongly-typed GP</li>
 * <li>Arity (number of arguments)</li>
 * <li>Whether it's a terminal or function</li>
 * </ul>
 * 
 * <p>Common operation types include:
 * <ul>
 * <li><strong>Mathematical functions</strong>: +, -, *, /, sin, cos, exp</li>
 * <li><strong>Logical functions</strong>: AND, OR, NOT, IF-THEN-ELSE</li>
 * <li><strong>Terminals</strong>: constants, variables, input values</li>
 * <li><strong>Domain-specific</strong>: problem-specific functions and operators</li>
 * </ul>
 * 
 * @param <T> the base type used for computation in this operation
 * @see OperationFactory
 * @see net.bmahe.genetics4j.gp.program.Program
 */
@Value.Immutable
public abstract class Operation<T> {

	/**
	 * Returns the name of this operation.
	 * 
	 * @return the operation name, used for identification and display
	 */
	@Parameter
	public abstract String getName();

	/**
	 * Returns the list of types that this operation accepts as arguments.
	 * 
	 * <p>For strongly-typed genetic programming, this defines the type constraints
	 * for each argument position. The list size determines the operation's arity.
	 * 
	 * @return the list of accepted argument types, empty for terminals
	 */
	@SuppressWarnings("rawtypes")
	@Parameter
	public abstract List<Class> acceptedTypes();

	/**
	 * Returns the type that this operation returns.
	 * 
	 * @return the return type of this operation
	 */
	@SuppressWarnings("rawtypes")
	@Parameter
	public abstract Class returnedType();

	/**
	 * Returns the computation function for this operation.
	 * 
	 * @return a function that takes input arguments and parameters and returns the computed result
	 */
	@Parameter
	@Value.Auxiliary
	public abstract BiFunction<T[], Object[], Object> compute();

	/**
	 * Returns a human-readable name for this operation.
	 * 
	 * <p>By default, this returns the same value as {@link #getName()}, but can be
	 * overridden to provide more descriptive names for display purposes.
	 * 
	 * @return the pretty name for display purposes
	 */
	@Value.Default
	public String getPrettyName() {
		return getName();
	}

	/**
	 * Applies this operation to the given input and parameters.
	 * 
	 * @param input the input arguments to the operation
	 * @param parameters additional parameters for the operation
	 * @return the result of applying this operation
	 */
	public Object apply(final T[] input, final Object[] parameters) {
		final BiFunction<T[], Object[], Object> function = compute();
		return function.apply(input, parameters);
	}

	/**
	 * Returns the arity (number of arguments) of this operation.
	 * 
	 * @return the number of arguments this operation accepts
	 */
	public int getArity() {
		return acceptedTypes().size();
	}

	/**
	 * Checks if this operation is a terminal (has no arguments).
	 * 
	 * @return {@code true} if this operation takes no arguments, {@code false} otherwise
	 */
	public boolean isTerminal() {
		return getArity() == 0;
	}
}