/*******************************************************************************
* Copyright 2012 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 java.util.Arrays;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.collect.WeakInterner;
import com.analog.lyric.dimple.model.values.Value;
import com.google.common.collect.Interner;
import net.jcip.annotations.Immutable;
@Immutable
public class RealJointDomain extends Domain
{
/*-------
* State
*/
private static final long serialVersionUID = 1L;
private final RealDomain [] _domains;
private final int _size;
/**
* If true, then _domains will contain only one element representing the common domain
* for all dimensions.
*/
private final boolean _homogeneous;
private static enum InternedDomains
{
INSTANCE;
private final Interner<RealJointDomain> interner = WeakInterner.create();
}
/*--------------
* Construction
*/
protected RealJointDomain(RealDomain domain, int size)
{
super(43 + domain.hashCode() * size);
_domains = new RealDomain[] { domain };
_size = size;
_homogeneous = true;
}
protected RealJointDomain(RealDomain[] domains, boolean cloneDomains)
{
super(computeHashCode(domains));
if (allEqual(domains))
{
_domains = new RealDomain[] { domains[0] };
_size = domains.length;
_homogeneous = true;
}
else
{
_domains = cloneDomains ? domains.clone() : domains;
_size = domains.length;
_homogeneous = false;
}
}
@Override
protected RealJointDomain intern()
{
return InternedDomains.INSTANCE.interner.intern(this);
}
public static RealJointDomain create(RealDomain... domains)
{
if (domains.length < 1)
{
throw new IllegalArgumentException("RealJointDomain requires at least one domain");
}
return new RealJointDomain(domains, true).intern();
}
public static RealJointDomain create(int size)
{
return create(RealDomain.unbounded(), size);
}
public static RealJointDomain create(RealDomain domain, int size)
{
return new RealJointDomain(domain, size).intern();
}
private static int computeHashCode(RealDomain[] domains)
{
int hashCode = 43;
for (RealDomain domain : domains)
{
hashCode += domain.hashCode();
}
return hashCode;
}
private static boolean allEqual(RealDomain[] domains)
{
RealDomain domain = domains[0];
for (int i = domains.length; --i>=1;)
{
if (!domain.equals(domains[i]))
{
return false;
}
}
return true;
}
/*----------------
* Object methods
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if ((obj instanceof RealJointDomain))
{
RealJointDomain other = (RealJointDomain) obj;
return _size == other._size &&
_homogeneous == other._homogeneous &&
Arrays.equals(this._domains, other._domains);
}
return false;
}
/*----------------
* Domain methods
*/
@Override
public final RealJointDomain asRealJoint()
{
return this;
}
/**
* @return true if value is an array of length matching {@link #getNumVars()} and whose
* elements are contained in the correspond {@link RealDomain} in {@link #getRealDomains()}.
*/
@Override
public boolean inDomain(@Nullable Object value)
{
if (value != null && value.getClass().isArray() && Array.getLength(value) == _size)
{
if (_homogeneous)
{
RealDomain domain = _domains[0];
for (int i = 0, end = _size; i < end; ++i)
{
if (! domain.inDomain(Array.get(value, i)))
{
return false;
}
}
}
else
{
for (int i = 0, end = _size; i < end; ++i)
{
if (! _domains[i].inDomain(Array.get(value, i)))
{
return false;
}
}
}
return true;
}
return false;
}
public boolean inDomain(double ... values)
{
int size = values.length;
if (size != _size)
return false;
if (_homogeneous)
{
final RealDomain domain = _domains[0];
for (double value : values)
if (!domain.inDomain(value))
return false;
}
else
{
for (int i = 0; i < size; i++)
if (!_domains[i].inDomain(values[i]))
return false;
}
return true;
}
/**
* True if all {@link RealDomain} subdomains are equal.
*/
public final boolean isHomogeneous()
{
return _homogeneous;
}
@Override
public final boolean isNumber()
{
return false;
}
@Override
public final boolean isRealJoint()
{
return true;
}
@Override
public final boolean isScalar()
{
return false;
}
@Override
public boolean valueInDomain(Value value)
{
return inDomain(value.getDoubleArray());
}
/*-------------------------
* RealJointDomain methods
*/
/**
* Returns the number of dimensions, or subdomains that make up this joint domain.
*/
@Override
public final int getDimensions()
{
return _size;
}
/**
* Returns copy of all of the subdomains making up this joint domain.
* <p>
* If you just want the size use {@link #getDimensions()} and if you just want
* to access a domain at a given index use {@link #getRealDomain(int)} instead
* to avoid the overhead of copying the array.
*/
public RealDomain [] getRealDomains()
{
if (_homogeneous)
{
RealDomain[] domains = new RealDomain[_size];
Arrays.fill(domains, _domains[0]);
return domains;
}
else
{
return _domains.clone();
}
}
public RealDomain getRealDomain(int dimension)
{
return _domains[_homogeneous ? 0 : dimension];
}
public int getNumVars()
{
return _size;
}
/**
* True if values are confined to a subset of R<sup>n</sup>
* <p>
* True if at least one subdomain in {@link #getRealDomains()} is bounded.
*/
@Override
public final boolean isBounded()
{
// True if any dimension is bounded
for (int i = 0; i < _domains.length; i++)
{
if (_domains[i].isBounded())
return true;
}
return false;
}
}