package jscl.math.operator; import jscl.math.Generic; import jscl.math.JsclInteger; import jscl.math.Variable; import jscl.math.function.Constant; import jscl.mathml.MathML; import jscl.util.ArrayComparator; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.HashSet; import java.util.Set; /** * User: serso * Date: 11/29/11 * Time: 9:50 PM */ public abstract class AbstractFunction extends Variable { protected static final Generic UNDEFINED_PARAMETER = JsclInteger.valueOf(Long.MIN_VALUE + 1); private static final String DEFAULT_PARAMETER_NAMES = "xyzabcdefghijklmnopqrstuvw"; protected Generic parameters[]; protected AbstractFunction(@Nonnull String name, Generic[] parameters) { super(name); checkParameters(parameters); this.parameters = parameters; } @Nullable protected static Generic getParameter(@Nullable Generic[] parameters, final int i) { return parameters == null ? null : (parameters.length > i ? parameters[i] : null); } private void checkParameters(@Nullable Generic[] parameters) { assert parameters == null || (getMinParameters() <= parameters.length && parameters.length <= getMaxParameters()); } public Generic[] getParameters() { return parameters; } public void setParameters(@Nullable Generic[] parameters) { checkParameters(parameters); this.parameters = parameters; } public abstract int getMinParameters(); public int getMaxParameters() { return getMinParameters(); } public abstract Generic selfExpand(); public Generic expand() { final AbstractFunction function = newExpandedFunction(); return function.selfExpand(); } @Nonnull protected AbstractFunction newExpandedFunction() { final AbstractFunction function = (AbstractFunction) newInstance(); for (int i = 0; i < parameters.length; i++) { function.parameters[i] = parameters[i].expand(); } return function; } public Generic elementary() { final AbstractFunction function = newElementarizedFunction(); return function.selfElementary(); } @Nonnull protected AbstractFunction newElementarizedFunction() { final AbstractFunction function = (AbstractFunction) newInstance(); for (int i = 0; i < parameters.length; i++) { function.parameters[i] = parameters[i].elementary(); } return function; } public abstract Generic selfElementary(); public Generic factorize() { final AbstractFunction function = newFactorizedFunction(); return function.expressionValue(); } @Nonnull protected AbstractFunction newFactorizedFunction() { final AbstractFunction function = (AbstractFunction) newInstance(); for (int i = 0; i < parameters.length; i++) { function.parameters[i] = parameters[i].factorize(); } return function; } public Generic simplify() { final AbstractFunction function = newSimplifiedFunction(); return function.selfSimplify(); } @Nonnull protected final AbstractFunction newSimplifiedFunction() { final AbstractFunction function = (AbstractFunction) newInstance(); for (int i = 0; i < parameters.length; i++) { function.parameters[i] = parameters[i].simplify(); } return function; } public abstract Generic selfSimplify(); public Generic numeric() { final AbstractFunction result = newNumericFunction(); return result.selfNumeric(); } @Nonnull protected final AbstractFunction newNumericFunction() { final AbstractFunction result = (AbstractFunction) newInstance(); for (int i = 0; i < parameters.length; i++) { result.parameters[i] = parameters[i].numeric(); } return result; } public abstract Generic selfNumeric(); public String toString() { final StringBuilder result = new StringBuilder(); // f(x, y, z) result.append(name); result.append("("); for (int i = 0; i < parameters.length; i++) { result.append(formatParameter(i)); if (i < parameters.length - 1) { result.append(", "); } } result.append(")"); return result.toString(); } @Nonnull protected final String formatParameter(int i) { Generic parameter = parameters[i]; String result; if (parameter != null) { result = parameter.toString(); } else { result = formatUndefinedParameter(i); } return result; } @Nonnull protected String formatUndefinedParameter(int i) { return String.valueOf(DEFAULT_PARAMETER_NAMES.charAt(i - (i / DEFAULT_PARAMETER_NAMES.length()) * DEFAULT_PARAMETER_NAMES.length())); } public String toJava() { StringBuilder result = new StringBuilder(); result.append(parameters[0].toJava()); result.append(".").append(name).append("()"); return result.toString(); } public int compareTo(Variable that) { if (this == that) return 0; int c = comparator.compare(this, that); if (c < 0) { return -1; } else if (c > 0) { return 1; } else { final AbstractFunction thatFunction = (AbstractFunction) that; c = name.compareTo(thatFunction.name); if (c < 0) { return -1; } else if (c > 0) { return 1; } else { return ArrayComparator.comparator.compare(parameters, thatFunction.parameters); } } } public Generic substitute(@Nonnull Variable variable, @Nonnull Generic generic) { final AbstractFunction function = (AbstractFunction) newInstance(); for (int i = 0; i < parameters.length; i++) { function.parameters[i] = parameters[i].substitute(variable, generic); } if (function.isIdentity(variable)) { return generic; } else { return function.selfExpand(); } } public void toMathML(MathML element, Object data) { int exponent = data instanceof Integer ? (Integer) data : 1; MathML result; if (exponent == 1) { nameToMathML(element); } else { result = element.element("msup"); nameToMathML(result); MathML e2 = element.element("mn"); e2.appendChild(element.text(String.valueOf(exponent))); result.appendChild(e2); element.appendChild(result); } result = element.element("mfenced"); for (Generic parameter : parameters) { parameter.toMathML(result, null); } element.appendChild(result); } @Nonnull @Override public Set<? extends Constant> getConstants() { final Set<Constant> result = new HashSet<Constant>(); for (Generic parameter : parameters) { result.addAll(parameter.getConstants()); } return result; } }