/*
* Copyright 2015 S. Webber
*
* 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.
*/
package org.oakgp.util;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.groupingBy;
import static org.oakgp.Type.booleanType;
import static org.oakgp.Type.integerType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.oakgp.Type;
import org.oakgp.node.ConstantNode;
import org.oakgp.node.Node;
/** Utility methods that support the functionality provided by the rest of the framework. */
public final class Utils {
/** Represents the boolean value {@code true}. */
public static final ConstantNode TRUE_NODE = new ConstantNode(TRUE, booleanType());
/** Represents the boolean value {@code false}. */
public static final ConstantNode FALSE_NODE = new ConstantNode(FALSE, booleanType());
/** Private constructor as all methods are static. */
private Utils() {
// do nothing
}
/**
* Returns an array consisting of a {@code ConstantNode} instance for each of the possible values of the specified enum.
*
* @param e
* the enum that the {@code ConstantNode} instances should wrap
* @param t
* the {@code Type} that should be associated with the {@code ConstantNode} instances
*/
public static ConstantNode[] createEnumConstants(Class<? extends Enum<?>> e, Type t) {
Enum<?>[] enumConstants = e.getEnumConstants();
ConstantNode[] constants = new ConstantNode[enumConstants.length];
for (int i = 0; i < enumConstants.length; i++) {
constants[i] = new ConstantNode(enumConstants[i], t);
}
return constants;
}
/**
* Returns an array consisting of a {@code ConstantNode} instance for each of the integer values in the specified range.
*
* @param minInclusive
* the minimum value (inclusive) to be represented by a {@code ConstantNode} in the returned array
* @param maxInclusive
* the minimum value (inclusive) to be represented by a {@code ConstantNode} in the returned array
*/
public static ConstantNode[] createIntegerConstants(int minInclusive, int maxInclusive) {
ConstantNode[] constants = new ConstantNode[maxInclusive - minInclusive + 1];
for (int n = minInclusive, i = 0; n <= maxInclusive; i++, n++) {
constants[i] = new ConstantNode(n, integerType());
}
return constants;
}
/** Creates an array of the specified size and assigns the result of {@link Type#integerType()} to each element. */
public static Type[] createIntegerTypeArray(int size) {
Type[] array = new Type[size];
Type type = integerType();
Arrays.fill(array, type);
return array;
}
/** Returns a map grouping the specified nodes by their {@code Type}. */
public static <T extends Node> Map<Type, List<T>> groupByType(T[] nodes) {
return groupBy(nodes, Node::getType);
}
/**
* Returns a map grouping the specified values according to the specified classification function.
*
* @param values
* the values to group
* @param valueToKey
* the classification function used to group values
*/
public static <K, V> Map<K, List<V>> groupBy(V[] values, Function<V, K> valueToKey) {
Map<K, List<V>> nodesByType = Arrays.stream(values).collect(groupingBy(valueToKey));
makeValuesImmutable(nodesByType);
return nodesByType;
}
/** Replaces each {@code List} stored as a value in the specified {@code Map} with an immutable version. */
private static <K, V> void makeValuesImmutable(Map<K, List<V>> map) {
for (Map.Entry<K, List<V>> e : map.entrySet()) {
map.put(e.getKey(), unmodifiableList(e.getValue()));
}
}
/** Returns randomly selected index of a node from the specified tree. */
public static int selectSubNodeIndex(Random random, Node tree) {
int nodeCount = tree.getNodeCount();
if (nodeCount == 1) {
// will get here if and only if 'tree' is a terminal (i.e. variable or constant) rather than a function node
return 0;
} else {
return selectSubNodeIndex(random, nodeCount);
}
}
/** Returns a int value between 0 (inclusive) and the specified {@code nodeCount} value minus 1 (exclusive). */
public static int selectSubNodeIndex(Random random, int nodeCount) {
// Note: -1 to avoid selecting root node
return random.nextInt(nodeCount - 1);
}
/** Returns a copy of the specified array. */
public static <T> T[] copyOf(T[] original) {
return Arrays.copyOf(original, original.length);
}
}