/******************************************************************************* * 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 java.lang.reflect.Array; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.collect.ArrayUtil; import com.analog.lyric.dimple.exceptions.DomainException; import net.jcip.annotations.Immutable; /** * A discrete domain representing the Cartesian product of other discrete domains. * Its elements are Java Object[] arrays with the nth element holding an element * of the nth component domain. * <p> * It is represented efficiently using a {@link JointDomainIndexer}, so the full * combinatoric list of elements does not need to be produced unless someone * calls {@link #getElements()}. * <p> * Construct instances using {@link DiscreteDomain#joint(DiscreteDomain...)} or * {@link DiscreteDomain#joint(JointDomainIndexer)}. */ @Immutable public class JointDiscreteDomain<Element> extends TypedDiscreteDomain<Element[]> { /*------- * State */ private static final long serialVersionUID = 1L; private final JointDomainIndexer _domains; private final Class<Element[]> _elementClass; /*-------------- * Construction */ JointDiscreteDomain(JointDomainIndexer domains) { super(domains.hashCode()); _domains = domains; @SuppressWarnings("unchecked") Class<Element[]> elementClass = (Class<Element[]>) Array.newInstance(domains.getElementClass(), 0).getClass(); _elementClass = elementClass; } /*---------------- * Object methods */ @Override public boolean equals(@Nullable Object that) { if (this == that) { return true; } if (that instanceof JointDiscreteDomain) { return ((JointDiscreteDomain<?>)that)._domains.equals(_domains); } return false; } @Override public String toString() { StringBuilder sb = new StringBuilder(); boolean comma = false; for (DiscreteDomain domain : _domains) { if (comma) { sb.append('x'); } else { comma = true; } sb.append(domain.toString()); } return sb.toString(); } /*------------------------ * DiscreteDomain methods */ /** * True if {@code value} is an array of length {@link #getDimensions()} for which * */ @Override public boolean inDomain(@Nullable Object value) { Object[] values = ArrayUtil.toArray(value); if (values != null) { int dimensions = getDimensions(); if (values.length == dimensions) { for (int i = 0; i < dimensions; ++i) { DiscreteDomain discrete = _domains.get(i); if (!discrete.inDomain(values[i])) { return false; } } return true; } } return false; } @Override public boolean containsValueWithRepresentation(Object value) { if (value instanceof Number) { return super.containsValueWithRepresentation(value); } int[] indices = ArrayUtil.toIntArray(value); if (indices != null) { final int dimensions = getDimensions(); if (indices.length == dimensions) { for (int i = 0; i < dimensions; ++i) { int index = indices[i]; if (index < 0 || index >= _domains.getDomainSize(i)) { return false; } } return true; } } return false; } @Override public final Class<Element[]> getElementClass() { return _elementClass; } /** * Returns ith element in domain. * <p> * This allocates a new array. You can copy into an existing array using {@link #getElement(int, Object[])}. * @param i must be in the range [0, {@link #size()}-1]. */ @Override public Element[] getElement(int i) { return getElement(i, null); } @Override public final int size() { return _domains.getCardinality(); } @Override public int getIndex(@Nullable Object value) { try { return getIndexOrThrow(value); } catch (DomainException ex) { return -1; } } @Override public int getIndexOrThrow(@Nullable Object value) { if (value instanceof Object[]) { return getIndexFromSubelements((Object[])value); } else if (value != null && value.getClass().isArray()) { int size = getDimensions(); Object[] values = new Object[size]; for (int i = 0; i < size; ++i) { values[i] = Array.get(value, i); } return getIndexFromSubelements(values); } throw domainError(value); } @Override public final boolean isNumber() { return false; } @Override public final boolean isNumeric() { return Number.class.isAssignableFrom(_elementClass); } @Override public final boolean isScalar() { return false; } /*----------------------------- * JointDiscreteDomain methods */ /** * Returns the number of dimensions, or subdomains that make up this joint domain. * Same as size of {@link #getDomainIndexer()}. */ @Override public final int getDimensions() { return _domains.size(); } /** * Returns the underlying {@link JointDomainIndexer} that represents this domain. */ public final JointDomainIndexer getDomainIndexer() { return _domains; } /** * Writes the subelements of the ith element in the domain into {@code array} and returns it. * <p> * @param i must be in the range [0, {@link #size()}-1]. * @param array will only be used if non-null and no shorter than {@link #size()}, otherwise * a new array will be allocated. * @return array containing ith element, which will be the same as {@code array} provided it was big enough. * @see #getElement(int) * @see #getElementIndices(int, int[]) */ public <T> T[] getElement(int i, @Nullable T[] array) { return _domains.jointIndexToElements(i, array); } /** * Returns any array containing the individual subdomain indexes for the * ith element in this domain. * <p> * This is equivalent to calling {@link #getElementIndices(int, int[])} with a null second argument. * <p> * @param i must be in the range [0, {@link #size()}-1]. * @return newly allocated array of indices */ public int[] getElementIndices(int i) { return _domains.jointIndexToIndices(i, null); } /** * Writes the individual subdomain indexes for the ith element in this domain into * provided {@code array} and returns it. * <p> * This is equivalent to calling {@link #getDomainIndexer()}.jointIndexToIndices(i, array) * <p> * @param i must be in the range [0, {@link #size()}-1]. * @param array must be at least {@link #size()} long or else a new array will be allocated. * @return array containing indices, which will be same as provided {@code array} if it is large enough * @see #getElementIndices(int) * @see #getElement(int, Object[]) */ public int[] getElementIndices(int i, int[] array) { return _domains.jointIndexToIndices(i, array); } public final int getIndexFromSubelements(Object ... values) { return _domains.jointIndexFromElements(values); } public final int getIndexFromIndices(int ... indices) { return _domains.jointIndexFromIndices(indices); } }