accumulators.java

/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
 */
package org.jenetics.util;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.jenetics.internal.util.object.eq;

import java.util.Iterator;

import org.jscience.mathematics.structure.GroupAdditive;

import org.jenetics.internal.util.HashBuilder;


/**
 * Collection of some general purpose Accumulators and some static helper classes
 * for accumulating.
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
 * @since 1.0
 * @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
 */
public final class accumulators extends StaticObject {
	private accumulators() {}

	public static final Accumulator<Object> NULL = new Accumulator<Object>() {
		@Override
		public void accumulate(final Object value) {
		}
	};

	/**
	 * Calculates min value.
	 *
	 * <p/>
	 * <strong>Note that this implementation is not synchronized.</strong> If
	 * multiple threads access this object concurrently, and at least one of the
	 * threads modifies it, it must be synchronized externally.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
	 * @since 1.0
	 * @version 1.0 &ndash; <em>$Date: 2014-03-01 $</em>
	 */
	public static final class Min<C extends Comparable<? super C>>
		extends MappedAccumulator<C>
	{
		private C _min;

		/**
		 * Create a new Min accumulator.
		 */
		public Min() {
		}

		/**
		 * Copy constructor.
		 *
		 * @param min the accumulator to copy.
		 * @throws NullPointerException if {@code min} is {@code null}.
		 */
		public Min(final Min<C> min) {
			requireNonNull(min, "Min");
			_samples = min._samples;
			_min = min._min;
		}

		/**
		 * Return the min value, accumulated so far.
		 *
		 * @return the min value, accumulated so far.
		 */
		public C getMin() {
			return _min;
		}

		/**
		 * @throws NullPointerException if the given {@code value} is {@code null}.
		 */
		@Override
		public void accumulate(final C value) {
			if (_min == null) {
				_min = value;
			} else {
				if (value.compareTo(_min) < 0) {
					_min = value;
				}
			}

			++_samples;
		}

		@Override
		public int hashCode() {
			return HashBuilder.of(getClass()).and(super.hashCode()).and(_min).value();
		}

		@Override
		public boolean equals(final Object obj) {
			if (obj == this) {
				return true;
			}
			if (obj == null || obj.getClass() != getClass()) {
				return false;
			}

			final Min<?> min = (Min<?>)obj;
			return super.equals(obj) && eq(_min, min._min);
		}

		@Override
		public String toString() {
			return format(
					"%s[samples=%d, min=%s]",
					getClass().getSimpleName(), getSamples(), getMin()
				);
		}

		@Override
		public Min<C> clone() {
			return (Min<C>)super.clone();
		}
	}


	/**
	 * Calculates max value.
	 *
	 * <p/>
	 * <strong>Note that this implementation is not synchronized.</strong> If
	 * multiple threads access this object concurrently, and at least one of the
	 * threads modifies it, it must be synchronized externally.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
	 * @since 1.0
	 * @version 1.0 &ndash; <em>$Date: 2014-03-01 $</em>
	 */
	public static final class Max<C extends Comparable<? super C>>
		extends MappedAccumulator<C>
	{
		private C _max;

		/**
		 * Create a new Max accumulator.
		 */
		public Max() {
		}

		/**
		 * Copy constructor.
		 *
		 * @param max the accumulator to copy.
		 * @throws NullPointerException if {@code max} is {@code null}.
		 */
		public Max(final Max<C> max) {
			requireNonNull(max, "Max");
			_samples = max._samples;
			_max = max._max;
		}

		/**
		 * Return the max value, accumulated so far.
		 *
		 * @return the max value, accumulated so far.
		 */
		public C getMax() {
			return _max;
		}

		/**
		 * @throws NullPointerException if the given {@code value} is {@code null}.
		 */
		@Override
		public void accumulate(final C value) {
			if (_max == null) {
				_max = value;
			} else {
				if (value.compareTo(_max) > 0) {
					_max = value;
				}
			}

			++_samples;
		}

		@Override
		public int hashCode() {
			return HashBuilder.of(getClass()).and(super.hashCode()).and(_max).value();
		}

		@Override
		public boolean equals(final Object obj) {
			if (obj == this) {
				return true;
			}
			if (obj == null || obj.getClass() != getClass()) {
				return false;
			}

			final Max<?> max = (Max<?>)obj;
			return super.equals(obj) && eq(_max, max._max);
		}

		@Override
		public String toString() {
			return format(
					"%s[samples=%d, max=%s]",
					getClass().getSimpleName(), getSamples(), getMax()
				);
		}

		@Override
		public Max<C> clone() {
			return (Max<C>)super.clone();
		}
	}


	/**
	 * Calculates min and max values.
	 *
	 * <p/>
	 * <strong>Note that this implementation is not synchronized.</strong> If
	 * multiple threads access this object concurrently, and at least one of the
	 * threads modifies it, it must be synchronized externally.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
	 * @since 1.0
	 * @version 1.0 &ndash; <em>$Date: 2014-03-01 $</em>
	 */
	public static final class MinMax<C extends Comparable<? super C>>
		extends MappedAccumulator<C>
	{
		private C _min;
		private C _max;

		/**
		 * Create a new min-max accumulator.
		 */
		public MinMax() {
		}

		/**
		 * Copy constructor.
		 *
		 * @param mm the accumulator to copy.
		 * @throws NullPointerException if {@code mm} is {@code null}.
		 */
		public MinMax(final MinMax<C> mm) {
			requireNonNull(mm, "MinMax");
			_samples = mm._samples;
			_min = mm._min;
			_max = mm._max;
		}

		/**
		 * Return the min value, accumulated so far.
		 *
		 * @return the min value, accumulated so far.
		 */
		public C getMin() {
			return _min;
		}

		/**
		 * Return the max value, accumulated so far.
		 *
		 * @return the max value, accumulated so far.
		 */
		public C getMax() {
			return _max;
		}

		/**
		 * @throws NullPointerException if the given {@code value} is {@code null}.
		 */
		@Override
		public void accumulate(final C value) {
			if (_min == null) {
				_min = value;
				_max = value;
			} else {
				if (value.compareTo(_min) < 0) {
					_min = value;
				} else if (value.compareTo(_max) > 0) {
					_max = value;
				}
			}

			++_samples;
		}

		@Override
		public int hashCode() {
			return HashBuilder.of(getClass()).
					and(super.hashCode()).
					and(_min).
					and(_max).value();
		}

		@Override
		public boolean equals(final Object obj) {
			if (obj == this) {
				return true;
			}
			if (obj == null || obj.getClass() != getClass()) {
				return false;
			}

			final MinMax<?> mm = (MinMax<?>)obj;
			return super.equals(obj) && eq(_min, mm._min) && eq(_max, mm._max);
		}

		@Override
		public String toString() {
			return format(
					"%s[samples=%d, min=%s, max=%s]",
					getClass().getSimpleName(), getSamples(), getMin(), getMax()
				);
		}

		@Override
		public MinMax<C> clone() {
			return (MinMax<C>)super.clone();
		}
	}

	/**
	 * Calculates the sum of the accumulated values.
	 *
	 * <p/>
	 * <strong>Note that this implementation is not synchronized.</strong> If
	 * multiple threads access this object concurrently, and at least one of the
	 * threads modifies it, it must be synchronized externally.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
	 * @since 1.0
	 * @version 1.0 &ndash; <em>$Date: 2014-03-01 $</em>
	 *
	 * @deprecated Will be removed.
	 */
	@Deprecated
	public static class Sum<G extends GroupAdditive<G>>
		extends MappedAccumulator<G>
	{

		private G _sum = null;

		public Sum() {
		}

		public Sum(final G start) {
			_sum = start;
		}

		@Override
		public void accumulate(final G value) {
			if (_sum == null) {
				_sum = value;
			} else {
				_sum = _sum.plus(value);
			}

			++_samples;
		}

		public G getSum() {
			return _sum;
		}

	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of all given
	 * {@code accumulators} with each value of the given {@code values}. The
	 * accumulation is done in parallel.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param accus the accumulators to apply.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Seq<? extends Accumulator<? super T>> accus
	) {
		switch (accus.length()) {
		case 1:
			accumulators.<T>accumulate(
				values,
				accus.get(0)
			);
			break;
		case 2:
			accumulators.<T>accumulate(
				values,
				accus.get(0),
				accus.get(1)
			);
			break;
		case 3:
			accumulators.<T>accumulate(
				values,
				accus.get(0),
				accus.get(1),
				accus.get(2)
			);
			break;
		case 4:
			accumulators.<T>accumulate(
				values,
				accus.get(0),
				accus.get(1),
				accus.get(2),
				accus.get(3)
			);
			break;
		case 5:
			accumulators.<T>accumulate(
				values,
				accus.get(0),
				accus.get(1),
				accus.get(2),
				accus.get(3),
				accus.get(4)
			);
			break;
		default:
			try (Concurrency c = Concurrency.start()) {
				for (final Accumulator<? super T> accumulator : accus) {
					c.execute(new Acc<>(values, accumulator));
				}
			}
		}
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of all given
	 * {@code accumulators} with each value of the given {@code values}. The
	 * accumulation is done in parallel.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param accus the accumulators to apply.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	@SafeVarargs
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Accumulator<? super T>... accus
	) {
		accumulate(values, Array.of(accus));
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of the given
	 * {@code accumulator} with each value of the given {@code values}.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param a the accumulator.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterator<? extends T> values,
		final Accumulator<? super T> a
	) {
		while (values.hasNext()) {
			a.accumulate(values.next());
		}
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of the given
	 * {@code accumulator} with each value of the given {@code values}.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param a the accumulator.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Accumulator<? super T> a
	) {
		for (final T value : values) {
			a.accumulate(value);
		}
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of all given
	 * {@code accumulators} with each value of the given {@code values}. The
	 * accumulation is done in parallel.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param a1 the first accumulator.
	 * @param a2 the second accumulator.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Accumulator<? super T> a1,
		final Accumulator<? super T> a2
	) {
		try (Concurrency c = Concurrency.start()) {
			c.execute(new Acc<>(values, a1));
			c.execute(new Acc<>(values, a2));;
		}
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of all given
	 * {@code accumulators} with each value of the given {@code values}. The
	 * accumulation is done in parallel.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param a1 the first accumulator.
	 * @param a2 the second accumulator.
	 * @param a3 the third accumulator
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Accumulator<? super T> a1,
		final Accumulator<? super T> a2,
		final Accumulator<? super T> a3
	) {
		try (Concurrency c = Concurrency.start()) {
			c.execute(new Acc<>(values, a1));
			c.execute(new Acc<>(values, a2));
			c.execute(new Acc<>(values, a3));
		}
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of all given
	 * {@code accumulators} with each value of the given {@code values}. The
	 * accumulation is done in parallel.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param a1 the first accumulator.
	 * @param a2 the second accumulator.
	 * @param a3 the third accumulator.
	 * @param a4 the fourth accumulator.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Accumulator<? super T> a1,
		final Accumulator<? super T> a2,
		final Accumulator<? super T> a3,
		final Accumulator<? super T> a4
	) {
		try (Concurrency c = Concurrency.start()) {
			c.execute(new Acc<>(values, a1));
			c.execute(new Acc<>(values, a2));
			c.execute(new Acc<>(values, a3));
			c.execute(new Acc<>(values, a4));
		}
	}

	/**
	 * Calls the {@link Accumulator#accumulate(Object)} method of all given
	 * {@code accumulators} with each value of the given {@code values}. The
	 * accumulation is done in parallel.
	 *
	 * @param <T> the value type.
	 * @param values the values to accumulate.
	 * @param a1 the first accumulator.
	 * @param a2 the second accumulator.
	 * @param a3 the third accumulator.
	 * @param a4 the fourth accumulator.
	 * @param a5 the fifth accumulator.
	 * @throws NullPointerException if one of the given arguments is {@code null}.
	 */
	public static <T> void accumulate(
		final Iterable<? extends T> values,
		final Accumulator<? super T> a1,
		final Accumulator<? super T> a2,
		final Accumulator<? super T> a3,
		final Accumulator<? super T> a4,
		final Accumulator<? super T> a5
	) {
		try (Concurrency c = Concurrency.start()) {
			c.execute(new Acc<>(values, a1));
			c.execute(new Acc<>(values, a2));
			c.execute(new Acc<>(values, a3));
			c.execute(new Acc<>(values, a4));
			c.execute(new Acc<>(values, a5));
		}
	}

	private static final class Acc<T> implements Runnable {
		private final Iterable<? extends T> _values;
		private final Accumulator<? super T> _accumulator;

		public Acc(
			final Iterable<? extends T> values,
			final Accumulator<? super T> accumulator
		) {
			_values = values;
			_accumulator = accumulator;
		}

		@Override
		public void run() {
			for (final T value : _values) {
				_accumulator.accumulate(value);
			}
		}
	}

}