it.unibo.alchemist.language.protelis.util.Op2.java Source code

Java tutorial

Introduction

Here is the source code for it.unibo.alchemist.language.protelis.util.Op2.java

Source

/*
 * Copyright (C) 2010-2015, Danilo Pianini and contributors
 * listed in the project's pom.xml file.
 * 
 * This file is part of Alchemist, and is distributed under the terms of
 * the GNU General Public License, with a linking exception, as described
 * in the file LICENSE in the Alchemist distribution's top directory.
 */
package it.unibo.alchemist.language.protelis.util;

import static it.unibo.alchemist.language.protelis.util.OpUtil.unsupported;
import it.unibo.alchemist.language.protelis.datatype.Field;
import it.unibo.alchemist.language.protelis.datatype.Fields;
import it.unibo.alchemist.language.protelis.java7.functionals.BiFunction;
import it.unibo.alchemist.language.protelis.java7.functionals.BinaryOperator;
import it.unibo.alchemist.language.protelis.java7.util.L;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.math3.util.FastMath;

/**
 * @author Danilo Pianini
 *
 */
public enum Op2 {

    AND("&&", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.and(a, b);
        }
    }), DIVIDE("/", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.divide(a, b);
        }
    }), EQUALS("==", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.equals(a, b);
        }
    }), NOT_EQUALS("!=", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return !Op2.equals(a, b);
        }
    }), GREATER(">", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.greater(a, b);
        }
    }), GREATER_EQUALS(">=", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.greaterEquals(a, b);
        }
    }), MAX("min", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.max(a, b);
        }
    }), MIN("min", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.min(a, b);
        }
    }), MINUS("-", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.minus(a, b);
        }
    }), MODULUS("%", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.modulus(a, b);
        }
    }), OR("||", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.or(a, b);
        }
    }), PLUS("+", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.plus(a, b);
        }
    }), POWER("^", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.pow(a, b);
        }
    }), SMALLER("<", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.smaller(a, b);
        }
    }), SMALLER_EQUALS("<=", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.smallerEquals(a, b);
        }
    }), TIMES("*", new BinaryOperator<Object>() {
        @Override
        public Object apply(Object a, Object b) {
            return Op2.times(a, b);
        }
    });

    private static final int[] BOTH = new int[] { 0, 1 };
    private static final int[] LEFT = new int[] { 0 };
    private static final int[] RIGHT = new int[] { 1 };
    private static final int[] NONE = new int[] {};
    private static final Map<String, Op2> MAP = new ConcurrentHashMap<>();
    private final BinaryOperator<Object> fun;
    private final String n;

    private Op2(final String name, final BinaryOperator<Object> function) {
        fun = function;
        n = name;
    }

    public BinaryOperator<Object> getFunction() {
        return fun;
    }

    public Object run(final Object a, final Object b) {
        final boolean afield = a instanceof Field;
        final boolean bfield = b instanceof Field;
        final int[] fields = afield && bfield ? BOTH : afield ? LEFT : bfield ? RIGHT : NONE;
        if (fields.length > 0) {
            return Fields.apply(fun, fields, a, b);
        }
        return fun.apply(a, b);
    }

    @Override
    public String toString() {
        return n;
    }

    /**
     * Translates a name into an operator.
     * 
     * @param name operator name
     * @return an {@link Op2}
     */
    public static Op2 getOp(final String name) {
        Op2 op = MAP.get(name);
        if (op == null) {
            for (Op2 o : values()) {
                if (o.n.equals(name)) {
                    op = o;
                    break;
                }
            }
            MAP.put(name, op);
        }
        return op;
    }

    private static Object and(final Object a, final Object b) {
        return logical("&&", a, b, new BiFunction<Boolean, Boolean, Boolean>() {
            @Override
            public Boolean apply(Boolean v1, Boolean v2) {
                return v1 && v2;
            }
        });
    }

    private static Object divide(final Object a, final Object b) {
        return arithmetic("/", a, b, new BiFunction<Double, Double, Double>() {
            @Override
            public Double apply(Double v1, Double v2) {
                return v1 / v2;
            }
        });
    }

    private static boolean equals(final Object a, final Object b) {
        if (a == null && b == null) {
            return true;
        }
        if (a == null || b == null) {
            return false;
        }
        return a.equals(b);
    }

    private static boolean greater(final Object a, final Object b) {
        return comparison(">", a, b, new BiFunction<Double, Double, Boolean>() {
            @Override
            public Boolean apply(Double v1, Double v2) {
                return v1 > v2;
            }
        });
    }

    private static boolean greaterEquals(final Object a, final Object b) {
        return comparison(">=", a, b, new BiFunction<Double, Double, Boolean>() {
            @Override
            public Boolean apply(Double v1, Double v2) {
                return v1 >= v2;
            }
        });
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static <T> boolean comparison(final String op, final T a, final T b,
            final BiFunction<Double, Double, Boolean> f) {
        if (a instanceof Number && b instanceof Number) {
            return f.apply(((Number) a).doubleValue(), ((Number) b).doubleValue());
        }
        try {
            if (a instanceof Comparable && b instanceof Comparable) {
                return f.apply((double) ((Comparable) a).compareTo(b), 0d);
            }
        } catch (RuntimeException e) {
            /*
             * Comparison of different types
             */
        }
        /*
         * Fall back to lexicographic comparison
         */
        return f.apply((double) a.toString().compareTo(b.toString()), 0d);
    }

    private static <T> boolean logical(final String op, final T a, final T b,
            final BiFunction<Boolean, Boolean, Boolean> f) {
        if (a instanceof Boolean && b instanceof Boolean) {
            return f.apply((Boolean) a, (Boolean) b);
        }
        return unsupported(op, a, b);
    }

    private static <T> T selection(final String op, final T a, final T b, final BinaryOperator<T> selector) {
        final boolean aNum = a instanceof Number;
        final boolean bNum = b instanceof Number;
        if (aNum && bNum) {
            final double ad = ((Number) a).doubleValue();
            final double bd = ((Number) b).doubleValue();
            if (ad > bd) {
                return selector.apply(a, b);
            }
            return selector.apply(b, a);
        }
        if (a instanceof Comparable && b instanceof Comparable) {
            try {
                @SuppressWarnings({ "rawtypes", "unchecked" })
                final int v = ((Comparable) a).compareTo(b);
                if (v > 0) {
                    return selector.apply(a, b);
                }
                return selector.apply(b, a);
            } catch (RuntimeException e) {
                /*
                 * Comparison of different types, fallback to lexicographic comparison
                 */
                L.debug("Comparison of different types.");
            }
        }
        /*
         * Fall back to lexicographic comparison
         */
        final int v = a.toString().compareTo(b.toString());
        if (v > 0) {
            return selector.apply(a, b);
        }
        return selector.apply(b, a);
    }

    private static Object min(final Object a, final Object b) {
        return selection("min", a, b, new BinaryOperator<Object>() {
            @Override
            public Object apply(Object v1, Object v2) {
                return v2;
            }
        });
    }

    private static Object max(final Object a, final Object b) {
        return selection("max", a, b, new BinaryOperator<Object>() {
            @Override
            public Object apply(Object v1, Object v2) {
                return v1;
            }
        });
    }

    private static Object minus(final Object a, final Object b) {
        return arithmetic("-", a, b, new BiFunction<Double, Double, Object>() {
            @Override
            public Object apply(Double v1, Double v2) {
                return v1 - v2;
            }
        });
    }

    private static Object modulus(final Object a, final Object b) {
        return arithmetic("%", a, b, new BiFunction<Double, Double, Object>() {
            @Override
            public Object apply(Double v1, Double v2) {
                return v1 % v2;
            }
        });
    }

    private static Object or(final Object a, final Object b) {
        return logical("||", a, b, new BiFunction<Boolean, Boolean, Boolean>() {
            @Override
            public Boolean apply(Boolean v1, Boolean v2) {
                return v1 || v2;
            }
        });
    }

    private static Object plus(final Object a, final Object b) {
        try {
            return arithmetic("+", a, b, new BiFunction<Double, Double, Object>() {
                @Override
                public Object apply(Double v1, Double v2) {
                    return v1 + v2;
                }
            });
        } catch (UnsupportedOperationException e) {
            /*
             * Sum falls back to string sum.
             */
            return a.toString() + b.toString();
        }
    }

    private static Object pow(final Object a, final Object b) {
        return arithmetic("^", a, b, new BiFunction<Double, Double, Object>() {
            @Override
            public Object apply(Double v1, Double v2) {
                return FastMath.pow(v1, v2);
            }
        });
    }

    private static <I, O> O arithmetic(final String op, final I a, final I b,
            final BiFunction<Double, Double, O> f) {
        if (a instanceof Double && b instanceof Double) {
            return f.apply(((Double) a), ((Double) b));
        }
        if (a instanceof Number && b instanceof Number) {
            return f.apply(((Number) a).doubleValue(), ((Number) b).doubleValue());
        }
        return unsupported(op, a, b);
    }

    private static boolean smaller(final Object a, final Object b) {
        return comparison("<", a, b, new BiFunction<Double, Double, Boolean>() {
            @Override
            public Boolean apply(Double v1, Double v2) {
                return v1 < v2;
            }
        });
    }

    private static boolean smallerEquals(final Object a, final Object b) {
        return comparison("<=", a, b, new BiFunction<Double, Double, Boolean>() {
            @Override
            public Boolean apply(Double v1, Double v2) {
                return v1 <= v2;
            }
        });
    }

    private static Object times(final Object a, final Object b) {
        return arithmetic("*", a, b, new BiFunction<Double, Double, Object>() {
            @Override
            public Object apply(Double v1, Double v2) {
                return v1 * v2;
            }
        });
    }

}