/******************************************************************************* * Copyright 2014 Analog Devices, Inc. * * 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 com.analog.lyric.dimple.model.domains; import net.jcip.annotations.Immutable; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.dimple.model.values.Value; @Immutable public class StandardJointDomainIndexer extends JointDomainIndexer { /*------- * State */ private static final long serialVersionUID = 1L; /** * Contains cumulative products of domain sizes, such that _products[0] == 1 * and otherwise _products[i] == getDomainSize(i-1) * _products[i-1]. */ private final int[] _products; /** * The joint cardinality of all of the domains: the product of all of the domain * sizes. */ private final int _cardinality; private final int _sumOfSizes; /*-------------- * Construction */ StandardJointDomainIndexer(int hashCode, DiscreteDomain[] domains) { super(hashCode, domains); final int nDomains = domains.length; _products = new int[nDomains]; int product = 1; int sum = 0; for (int i = 0; i < nDomains; ++i) { final int size = domains[i].size(); _products[i] = product; product *= size; sum += size; } _cardinality = product; _sumOfSizes = sum; } StandardJointDomainIndexer(DiscreteDomain[] domains) { this(computeHashCode(domains), domains); } /*---------------------------- * JointDomainIndexer methods */ /** * The number of possible combinations of all domain elements. Equal to the product of * all of the domain sizes. * <p> * @see #getInputCardinality() * @see #getOutputCardinality() */ @Override public final int getCardinality() { return _cardinality; } /** * Returns the index of the ith domain designated as an input domain. * <p> * This is equivalent to returning the ith element of {@link #getInputDomainIndices()} but * without having to allocate and copy an array. * <p> * @throws ArrayIndexOutOfBoundsException if i is not in range [0, {@link #getInputSize()}-1] * (will always throw if not {@link #isDirected()}. */ @Override public int getInputDomainIndex(int i) { throw new ArrayIndexOutOfBoundsException(); } /** * The number of possible combinations of output domain elements. Equal to the product of * all of the output domain sizes. Will be the same as {@link #getCardinality()} if not {@link #isDirected()}. * @see #getInputCardinality() */ @Override public int getOutputCardinality() { return _cardinality; } /** * Returns amount by which joint index returned by {@link #jointIndexFromIndices(int...)} changes * when ith element index changes by 1. * <p> * This can be used to iterate over the joint indexes for one dimension for fixed values of all of * the other dimensions. * <p> * @see #getUndirectedStride(int) */ @Override public int getStride(int i) { return _products[i]; } @Override public int getSumOfDomainSizes() { return _sumOfSizes; } /** * Returns amount by which joint index returned by {@link #undirectedJointIndexFromIndices(int...)} changes * when ith element index changes by 1. * <p> * This can be used to iterate over the joint indexes for one dimension for fixed values of all of * the other dimensions. * <p> * @see #getStride(int) */ @Override public final int getUndirectedStride(int i) { return _products[i]; } @Override public final int undirectedJointIndexFromElements(Object ... elements) { final DiscreteDomain[] domains = _domains; final int[] products = _products; int joint = domains[0].getIndexOrThrow(elements[0]); for (int i = 1, end = products.length; i < end; ++i) { joint += products[i] * domains[i].getIndexOrThrow(elements[i]); } return joint; } @Override public final int undirectedJointIndexFromIndices(int ... indices) { final int length = size(); int joint = indices[0]; // _products[0] is 1, so we can skip the multiply for (int i = 1, end = length; i != end; ++i) // != is slightly faster than < comparison { joint += indices[i] * _products[i]; } return joint; } @Override public final int undirectedJointIndexFromValues(Value ... values) { final int length = size(); int joint = values[0].getIndex(); // _products[0] is 1, so we can skip the multiply for (int i = 1, end = length; i != end; ++i) // != is slightly faster than < comparison { joint += values[i].getIndex() * _products[i]; } return joint; } @Override public final <T> T[] undirectedJointIndexToElements(int jointIndex, @Nullable T[] elements) { final DiscreteDomain[] domains = _domains; final int[] products = _products; elements = allocateElements(elements); int product; for (int i = products.length; --i >= 0;) { final int index = jointIndex / (product = products[i]); @SuppressWarnings("unchecked") T element = (T) domains[i].getElement(index); elements[i] = element; jointIndex -= index * product; } return elements; } @Override public final Value[] undirectedJointIndexToValues(int jointIndex, Value[] values) { final DiscreteDomain[] domains = _domains; final int[] products = _products; int product; for (int i = products.length; --i >= 0;) { final Value value = values[i]; final DiscreteDomain domain = domains[i]; final int index = jointIndex / (product = products[i]); if (value.getDomain() == domain) { // If domain matches, then use the faster setIndex method. // // Because domains are interned, the == check should be sufficient the vast // majority of the time, and in the unlikely event it is not, setObject will // still do the right thing. value.setIndex(index); } else { value.setObject(domain.getElement(index)); } jointIndex -= index * product; } return values; } /** * Computes element index for a single domain from a joint index using undirected ordering * of domains. * <p> * This is like {@link #undirectedJointIndexToIndices} but only computes one element index. * <p> * @param jointIndex must be in range [0, {@link #getCardinality()}-1]. * @param domainIndex must be in range [0, {@link #size()}-1]. * <p> * @see #jointIndexToElementIndex(int, int) */ @Override public int undirectedJointIndexToElementIndex(int jointIndex, int domainIndex) { return (jointIndex / _products[domainIndex]) % _domains[domainIndex].size(); } @Override public final int[] undirectedJointIndexToIndices(int jointIndex, @Nullable int[] indices) { final int[] products = _products; indices = allocateIndices(indices); int product; for (int i = products.length; --i >= 0;) { final int index = jointIndex / (product = products[i]); indices[i] = index; jointIndex -= index * product; } return indices; } @Override public boolean supportsJointIndexing() { return true; } @Override public boolean supportsOutputIndexing() { return true; } }