/*******************************************************************************
* Copyright 2012-2013 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.io.Serializable;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.dimple.exceptions.DomainException;
import com.analog.lyric.dimple.model.values.Value;
import com.analog.lyric.dimple.model.variables.Complex;
import com.analog.lyric.dimple.model.variables.Discrete;
import com.analog.lyric.dimple.model.variables.RealJoint;
import com.google.common.math.DoubleMath;
/**
* Base class for variable domains, which specify a set of valid values for
* a variable type.
*/
public abstract class Domain implements Serializable
{
/*-------
* State
*/
private static final long serialVersionUID = 1L;
private final int _hashCode;
/*--------------
* Construction
*/
Domain(int hashCode)
{
_hashCode = hashCode;
}
/*---------------
* Serialization
*/
/**
* If supported by subclass, returns interned version
* of domain.
*/
protected Domain intern()
{
return this;
}
protected Object readResolve()
{
// Replace with interned version of domain, if available.
return intern();
}
/*----------------
* Object methods
*/
@Override
public final int hashCode()
{
return _hashCode;
}
@Override
public abstract boolean equals(@Nullable Object other);
/*----------------
* Domain methods
*/
/**
* If domain {@link #isDiscrete()} returns object cast to {@link DiscreteDomain},
* otherwise returns null.
*/
public @Nullable DiscreteDomain asDiscrete() { return null; }
/**
* If domain {@link #isReal()} returns object cast to {@link RealDomain},
* otherwise returns null.
*/
public @Nullable RealDomain asReal() { return null; }
/**
* If domain {@link #isRealJoint()} returns object cast to {@link RealJointDomain},
* otherwise returns null.
*/
public @Nullable RealJointDomain asRealJoint() { return null; }
/**
* If domain {@link #isComplex()} returns object cast to {@link ComplexDomain},
* otherwise returns null.
*/
public @Nullable ComplexDomain asComplex() { return null; }
/**
* The number of dimensions for elements of the domain.
* <p>
* Returns one for {@linkplain #isScalar() scalar} domains. For {@link RealJointDomain}
* and {@link JointDiscreteDomain}, returns the size of the array needed to hold the
* components of a single element.
* <p>
* @since 0.07
*/
public int getDimensions()
{
return 1;
}
/**
* True if domain only contains values that can be represented using an {@code int}.
*/
public boolean hasIntCompatibleValues() { return false; }
/**
* True if the elements of the domain are bounded to some subset.
* <p>
* The default implementation returns true if not {@link #isDiscrete()}.
* <p>
* @since 0.06
*/
public boolean isBounded()
{
return !isDiscrete();
}
/**
* True if domain is an instance of {@link Discrete}.
* @see #asDiscrete()
*/
public boolean isDiscrete() { return false; }
/**
* True if values of domain are (32-bit) integers.
* <p>
* @since 0.05
*/
public boolean isIntegral() { return false; }
/**
* True if domain is an instance of {@link RealDomain}.
* @see #asReal()
*/
public boolean isReal() { return false; }
/**
* True if domain is an instance of {@link RealJoint}
* @see #asRealJoint()
*/
public boolean isRealJoint() { return false; }
/**
* True if domain is an instance of {@link Complex}
* @see #asComplex()
*/
public boolean isComplex() { return false; }
/**
* True if all values of domain are scalar numbers.
* @since 0.05
*/
public boolean isNumber() { return true; }
/**
* True if all values of domain are (not necessarily scalar) numbers.
* @since 0.05
*/
public boolean isNumeric() { return true; }
/**
* True if all elements of domain are scalar values.
* <p>
* This is false for {@link RealJointDomain} and {@link JointDiscreteDomain}.
* @since 0.05
*/
public boolean isScalar() { return true; }
/**
* @return true if {@code value} is a valid member of the domain. Implementors
* should not throw a cast exception.
* @see #valueInDomain(Value)
*/
public abstract boolean inDomain(@Nullable Object value);
/**
* @return true if {@code representation} corresponds to a valid member of the domain for
* domains that can represent values using an alternate representation, such as the index
* of a {@link Discrete} domain with enumerated elements.
* <p>
* The default implementation simply invokes {@link #inDomain(Object)}.
*/
public boolean containsValueWithRepresentation(Object representation)
{
return inDomain(representation);
}
/**
* @return an exception stating that {@code value} is not a member of this domain.
*/
public DomainException domainError(@Nullable Object value)
{
return new DomainException("'%s' is not a member of domain '%s'", value, this);
}
/**
* Indicate whether {@code value} is a valid member of the domain.
* <p>
* Semantically equivalent to
* <blockquote><tt>
* {@link #inDomain(Object) inDomain}(value.{@link Value#getObject getObject}());
* </tt></blockquote>
* @param value holds the value to be checked.
* @since 0.08
* @see #inDomain(Object)
*/
public abstract boolean valueInDomain(Value value);
/*----------------
* Static methods
*/
/**
* True if {@code type} is one of: {@link Integer}, {@link Short}, {@link Byte}.
*/
public static boolean isIntCompatibleClass(Class<?> type)
{
return Number.class.isAssignableFrom(type) &&
(type == Integer.class ||
type == Short.class ||
type == Byte.class);
}
public static boolean isIntCompatibleValue(Object value)
{
return value instanceof Number && isIntCompatibleValue((Number)value);
}
public static boolean isIntCompatibleValue(Number value)
{
if (isIntCompatibleClass(value.getClass()))
{
return true;
}
if (value instanceof Long)
{
return isIntCompatibleValue(((Long)value).longValue());
}
return isIntCompatibleValue(value.doubleValue());
}
public static boolean isIntCompatibleValue(double value)
{
return DoubleMath.isMathematicalInteger(value) && isIntCompatibleValue((long)value);
}
public static boolean isIntCompatibleValue(long value)
{
return Math.abs(value) <= Integer.MAX_VALUE;
}
}