package autodiff.nodes;

import static multij.tools.Tools.cast;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import multij.tools.Tools;

/**
 * @author codistmonk (creation 2016-07-11)
 */
public final class Mapping extends UnaryNode<Mapping> {
	
	private String operation;
	
	@Override
	public final <V> V accept(final NodeVisitor<V> visitor) {
		return visitor.visit(this);
	}
	
	public final String getOperation() {
		return this.operation;
	}
	
	public final Mapping setOperation(final String operation) {
		this.operation = operation;
		
		return this;
	}
	
	@Override
	public final Mapping autoShape() {
		return this.setShape(this.getArgument().getShape());
	}
	
	private static final long serialVersionUID = -2566458738220643925L;
	
	private static final Map<String, Object[]> forwards = new HashMap<>();
	
	private static final Map<String, Object[]> diffs = new HashMap<>();
	
	public static final String FORALL = "\\forall";
	
	public static final String IN = "\\in";
	
	public static final String R = "\\mathbb{R}";
	
	public static final String SQUARED = "^2";
	
	public static final String SQRT = "sqrt";
	
	public static final String TIMES = "\\cdot";
	
	public static final String D = "\\partial";
	
	public static final String EXP = "\\exp";
	
	public static final String LN = "\\ln";
	
	/**
	 * Smoothed half identity.
	 */
	public static final String SHI = "\\shi";
	
	public static final String SIGMOID = "\\sigmoid";
	
	public static final String BUMP = "\\bump";
	
	public static final String RELU = "\\relu";
	
	public static final String MAX = "\\max";
	
	public static final String STEP = "\\step";
	
	static {
		final String x = "x";
		
		define(SQUARED, x, $(x, SQUARED),
				$(x, TIMES, x));
		defineDiff(SQUARED, x,
				$(2, TIMES, x));
		
		define(SHI, x,
				$(LN, $(1, "+", $(EXP, x))));
		defineDiff(SHI, x,
				$(SIGMOID, x));
		
		define(SIGMOID, x,
				$(1, "/", $(1, "+", $(EXP, $("-", x)))));
		defineDiff(SIGMOID, x,
				$($(EXP, x), "/", $($(1, "+", $(EXP, x)), SQUARED)));
		
		define(RELU, x,
				$(MAX, 0, x));
		defineDiff(RELU, x,
				$(SIGMOID, x));
		
		defineDiff(SQRT, x,
				$(1, "/", $(2, TIMES, $(SQRT, x))));
		
		defineDiff(EXP, x,
				$(EXP, x));
		
		defineDiff(LN, x,
				$(1, "/", x));
	}
	
	public static final void define(final String functionName, final String variableName, final Object... definition) {
		forwards.put(functionName, $(FORALL, $(variableName), IN, R, $($(functionName, variableName), "=", definition)));
	}
	
	public static final void define(final String functionName, final String variableName, final Object[] notation, final Object... definition) {
		forwards.put(functionName, $(FORALL, $(variableName), IN, R, $(notation, "=", definition)));
	}
	
	public static final void defineDiff(final String functionName, final String variableName, final Object... definition) {
		diffs.put(functionName, $(FORALL, $(variableName), IN, R, $($($(D, functionName, 0), variableName), "=", definition)));
	}
	
	public static final Object[] $(final Object... objects) {
		final int n = objects.length;
		
		for (int i = 0; i < n; ++i) {
			final Number number = cast(Number.class, objects[i]);
			
			if (number != null && !(number instanceof BigDecimal)) {
				objects[i] = new BigDecimal("" + number);
			}
		}
		
		return objects;
	}
	
}

@@ -18,16 +18,50 @@
 nodes;%0A%0A
+import static java.lang.Math.pow;%0A
 import s
@@ -174,36 +174,8 @@
 p;%0A%0A
-import multij.tools.Tools;%0A%0A
 /**%0A
@@ -1692,16 +1692,57 @@
  = %22x%22;%0A
+%09%09final double epsilon = pow(2.0, -4.0);%0A
 %09%09%0A%09%09def
@@ -1834,32 +1834,59 @@
  TIMES, x));%0A%09%09%0A
+%09%09// TODO asymptotic cases%0A
 %09%09define(SHI, x,
@@ -1926,49 +1926,32 @@
 ;%0A%09%09
-defineDiff(SHI, x,%0A%09%09%09%09$(SIGMOID, x));%0A%09%09
+// TODO asymptotic cases
 %0A%09%09d
@@ -2020,26 +2020,46 @@
 ;%0A%09%09
-defineDiff(SIGMOID
+// TODO asymptotic cases%0A%09%09define(BUMP
 , x,
@@ -2071,137 +2071,340 @@
 $($(
-EXP, x), %22/
+1, %22-
 %22, $(
-$(1, %22+%22, $(EXP, x)), SQUARED)));%0A%09%09%0A%09%09define(RELU, x,%0A%09%09%09%09$(MAX, 0, x));%0A%09%09defineDiff(RELU, x,%0A%09%09%09%09$(SIGMOID, x)
+SIGMOID, x)), TIMES, $(SIGMOID, x)));%0A%09%09%0A%09%09defineDiff(SHI, x,%0A%09%09%09%09$(SIGMOID, x));%0A%09%09defineDiff(SIGMOID, x,%0A%09%09%09%09$(BUMP, x));%0A%09%09%0A%09%09define(RELU, x,%0A%09%09%09%09$(MAX, 0, x));%0A%09%09defineDiff(RELU, x,%0A%09%09%09%09$(SIGMOID, x));%0A%09%09%0A%09%09define(STEP, x,%0A%09%09%09%09$(%22cases%22, $(0, %22if%22, $(x, %22%3C%22, 0)), $(1, %22otherwise%22)));%0A%09%09defineDiff(STEP, x,%0A%09%09%09%09epsilon
 );%0A%09

Update function definitions.