package jcgp.backend.function; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; /** * FunctionSet encapsulates a group of functions. This is done to * simplify the implementation of problem types. *

* FunctionSet contains a variety of useful methods for acquiring general * information, such as the maximum arity across all functions and the total * number of functions. *

* In addition, FunctionSet offers the ability to enable and disable functions. * Accessing the functions through {@code getAllowedFunction()} will return * allowed functions only, providing an easy way to control which functions * can be used in mutations. *

* An implementation of FunctionSet must simply use its constructor to set * the name field and use {@code registerFunctions()} to add the required * functions. * * @author Eduardo Pedroni * */ public abstract class FunctionSet { private ArrayList functionList = new ArrayList(); private ArrayList allowedFunctions = new ArrayList(); /** * @return the number of currently allowed functions. */ public int getAllowedFunctionCount() { return allowedFunctions.size(); } /** * @return the total number of functions, including disabled ones. */ public int getTotalFunctionCount() { return functionList.size(); } /** * Returns an allowed function. This throws an * ArrayIndexOutOfBoundsException if the supplied * index is beyond the count of allowed functions. * * @param index the allowed function index. * @return the allowed function object. */ public Function getAllowedFunction(int index) { return functionList.get(allowedFunctions.get(index)); } /** * Returns a function from the complete collection, * enabled or disabled alike. This throws an * ArrayIndexOutOfBoundsException if the supplied * index is beyond the count of allowed functions. * * @param index the function index. * @return the function object. */ public Function getFunction(int index) { return functionList.get(index); } /** * Computes and returns the maximum arity out of * all the function, enabled or disabled. * * @return */ public int getMaxArity(){ int arity = 0; for (Function function : functionList) { // if a higher arity is found, store it if (function.getArity() > arity) { arity = function.getArity(); } } return arity; } /** * Disables the indexed function. If the function * is already disabled, this does nothing. * * @param index the function to disable. */ public void disableFunction(int index) { /* * allowedFunctions is a list of the indices of all allowed functions, * as addressed in functionList. This method iterates through the whole * list of allowed functions and removes any elements which are equal * to the specified index. */ if (index < functionList.size()) { for (Iterator iterator = allowedFunctions.iterator(); iterator.hasNext();) { int function = iterator.next(); if (function == index) { iterator.remove(); } } } else { throw new ArrayIndexOutOfBoundsException("Function " + index + " does not exist, the set only has " + functionList.size() + " functions."); } } /** * Disables the indexed function. If the function is * already enabled, this does nothing. * * @param index the function to disable. */ public void enableFunction(int index) { // add the specified index to the list of allowed indices if (!allowedFunctions.contains(index)) { allowedFunctions.add(index); Collections.sort(allowedFunctions); } } /** * Checks if a specified function is enabled. If the function * does not belong in the FunctionSet, this returns false. * * @param function the function to check. * @return true if the function is enabled. */ public boolean isEnabled(Function function) { for (int i = 0; i < allowedFunctions.size(); i++) { if (functionList.get(allowedFunctions.get(i)) == function) { return true; } } return false; } /** * For internal use in subclass constructors. This method * adds the specified functions and enables them. The same * function cannot be added more than once. * * @param functions the functions to register in the function set. */ protected void registerFunctions(Function... functions) { for (int i = 0; i < functions.length; i++) { if (!alreadyHave(functions[i])) { functionList.add(functions[i]); enableFunction(functionList.size() - 1); } } } /** * For internal use only, this checks whether a function * is already present in the function set. * * @param function the function to look for. * @return true if the function is already in the function set. */ private boolean alreadyHave(Function function) { for (int i = 0; i < functionList.size(); i++) { if (functionList.get(i).getClass() == function.getClass()) { return true; } } return false; } }