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

Java tutorial

Introduction

Here is the source code for it.unibo.alchemist.language.protelis.util.HoodOp.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 com.google.common.collect.ImmutableList.of;
import static java.lang.Double.NEGATIVE_INFINITY;
import static java.lang.Double.NaN;
import static java.lang.Double.POSITIVE_INFINITY;
import static org.apache.commons.math3.util.Pair.create;
import it.unibo.alchemist.language.protelis.datatype.Field;
import it.unibo.alchemist.language.protelis.datatype.Tuple;
import it.unibo.alchemist.language.protelis.datatype.Tuples;
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.functionals.Function;
import it.unibo.alchemist.language.protelis.java7.functionals.Supplier;

import java.util.Arrays;
import java.util.List;

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

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

    /**
     * Minimum.
     */
    MIN(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.min(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return POSITIVE_INFINITY;
        }
    }, (List) of(create(Number.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return POSITIVE_INFINITY;
        }
    })), (List) of(create(Tuple.class, new Function<Object, Object>() {
        @Override
        public Object apply(Object t) {
            return fTup(POSITIVE_INFINITY, (Tuple) t);
        }
    }))),
    /**
     * Maximum.
     */
    MAX(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.max(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return NEGATIVE_INFINITY;
        }
    }, (List) of(create(Number.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return NEGATIVE_INFINITY;
        }
    })), (List) of(create(Tuple.class, new Function<Object, Object>() {
        @Override
        public Object apply(Object t) {
            return fTup(NEGATIVE_INFINITY, (Tuple) t);
        }
    }))),
    /**
     * Any value.
     */
    ANY(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.any(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return HoodOp.no();
        }
    }, (List) of(create(Boolean.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return false;
        }
    })), (List) of()),
    /**
     * All values.
     */
    ALL(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.all(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return HoodOp.no();
        }
    }, (List) of(create(Boolean.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return true;
        }
    })), (List) of()),
    /**
     * Mean of values.
     */
    MEAN(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.mean(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return NaN;
        }
    }, (List) of(create(Number.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return NaN;
        }
    })), (List) of(create(Tuple.class, new Function<Object, Object>() {
        @Override
        public Object apply(Object t) {
            return fTup(NaN, (Tuple) t);
        }
    }))),
    /**
     * Sum of values.
     */
    SUM(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.sum(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return 0d;
        }
    }, (List) of(create(Number.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return 0d;
        }
    })), (List) of(create(Tuple.class, new Function<Object, Object>() {
        @Override
        public Object apply(Object t) {
            return fTup(0d, (Tuple) t);
        }
    }))),
    /**
     * Union of values.
     */
    UNION(new BiFunction<Field, DeviceUID, Object>() {
        @Override
        public Object apply(Field f, DeviceUID n) {
            return HoodOp.union(f, n);
        }
    }, new Supplier<Object>() {
        @Override
        public Object get() {
            return Tuples.create(new Object[0]);
        }
    }, (List) of(create(Object.class, new Supplier<Object>() {
        @Override
        public Object get() {
            return Tuples.create(new Object[0]);
        }
    })), (List) of());
    private final BiFunction<Field, DeviceUID, Object> f;
    private final Function<Field, Object> defs;

    /**
     * @param fun
     * @param empty
     *            function that generates a default in case of empty field
     * @param suppliers
     *            list of pairs mapping classes to 0-ary functions that provide
     *            a default
     * @param cloners
     *            list of pairs mapping classes to 1-ary functions that, given
     *            an element of the field as input, provide a comparison. Such
     *            functions are used in case there is no supplier that can
     *            provide a specific value-agnostic default
     */
    private HoodOp(final BiFunction<Field, DeviceUID, Object> fun, final Supplier<Object> empty,
            final List<Pair<Class<?>, Supplier<Object>>> suppliers,
            final List<Pair<Class<?>, Function<Object, Object>>> cloners) {
        f = fun;
        defs = new Function<Field, Object>() {
            @Override
            public Object apply(Field field) {
                /*
                 * Field empty: generate a default.
                 */
                if (field.isEmpty()) {
                    return empty.get();
                }
                final Class<?> type = field.getExpectedType();
                for (Pair<Class<?>, Supplier<Object>> sup : suppliers) {
                    if (sup.getFirst().isAssignableFrom(type)) {
                        /*
                         * Field has compatible type
                         */
                        return sup.getSecond().get();
                    }
                }
                for (Pair<Class<?>, Function<Object, Object>> cloner : cloners) {
                    if (cloner.getFirst().isAssignableFrom(type)) {
                        return cloner.getSecond().apply(field.valIterator().iterator().next());
                    }
                }
                return no(type);
            }
        };
    }

    private <T> T no(final Class<?> c) {
        throw new UnsupportedOperationException(this + " cannot compute on " + c);
    }

    private static Object no() {
        throw new UnsupportedOperationException("Unsupported operation on empty fields.");
    }

    private static Tuple fTup(final Object defVal, final Tuple in) {
        return cTup(defVal, in.size());
    }

    private static Tuple cTup(final Object v, final int size) {
        final Object[] r = new Object[size];
        Arrays.fill(r, v);
        return Tuples.create(r);
    }

    /**
     * @param reducer the desired operator
     * @return the corresponding {@link HoodOp}
     */
    public static HoodOp get(final String reducer) {
        for (HoodOp ho : values()) {
            if (ho.name().equalsIgnoreCase(reducer)) {
                return ho;
            }
        }
        return null;
    }

    /**
     * @param o
     *            the field
     * @param n
     *            the node on which the field is sampled
     * @return the Object resulting in the hood application
     */
    public Object run(final Field o, final DeviceUID n) {
        return f.apply(o, n);
    }

    private static Object min(final Field f, final DeviceUID n) {
        return f.reduceVals(Op2.MIN.getFunction(), n, MIN.defs.apply(f));
    }

    private static Object max(final Field f, final DeviceUID n) {
        return f.reduceVals(Op2.MAX.getFunction(), n, MAX.defs.apply(f));
    }

    private static Object any(final Field f, final DeviceUID n) {
        return f.reduceVals(Op2.OR.getFunction(), n, ANY.defs.apply(f));
    }

    private static Object all(final Field f, final DeviceUID n) {
        return f.reduceVals(Op2.AND.getFunction(), n, ALL.defs.apply(f));
    }

    private static Object sum(final Field f, final DeviceUID n) {
        return f.reduceVals(Op2.PLUS.getFunction(), n, SUM.defs.apply(f));
    }

    private static Object mean(final Field f, final DeviceUID n) {
        if (f.isEmpty()) {
            return Double.NaN;
        }
        return Op2.DIVIDE.getFunction().apply(sum(f, n), f.size());
    }

    private static Object union(final Field f, final DeviceUID n) {
        return f.reduceVals(new BinaryOperator<Object>() {
            @Override
            public Object apply(Object a, Object b) {
                final Tuple at = a instanceof Tuple ? (Tuple) a : Tuples.create(a);
                final Tuple bt = b instanceof Tuple ? (Tuple) b : Tuples.create(b);
                return Tuples.union(at, bt);
            }
        }, n, UNION.defs.apply(f));
    }

}