/*******************************************************************************
* Copyright 2015 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.solvers.core;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jdt.annotation.Nullable;
import com.analog.lyric.dimple.data.IDatum;
import com.analog.lyric.dimple.model.values.Value;
/**
* Holds prior and condition values for a solver variable.
* <p>
* This is primarily intended for internal use in solver variable implementations.
* <p>
* Note that {@link #release()} can be used to return instance for reuse.
* <p>
* @since 0.08
* @author Christopher Barber
* @see SVariableBase#getPriorAndCondition
*/
public final class PriorAndCondition extends AbstractList<IDatum>
{
/*------
* State
*/
/**
* Array holding prior in position 0 and condition in position 1.
*/
private final IDatum[] _data = new IDatum[2];
/**
* Bit mask indicating positions of non-null elements.
*/
private int _mask;
private static final AtomicReference<PriorAndCondition> _reusableInstance = new AtomicReference<>();
/*---------------
* Construction
*/
private PriorAndCondition()
{
}
/**
* Returns an instance with given prior and condition values.
* <p>
* If the object is to be used within a single function call, consider
* {@linkplain #release releasing} it for reuse by the next caller.
* <p>
* Typically it is easier to use the {@linkplain SVariableBase#getPriorAndCondition() getPriorAndCondition()}
* method when working with solver variable instances.
* <p>
* @since 0.08
*/
public static PriorAndCondition create(@Nullable IDatum prior, @Nullable IDatum condition)
{
PriorAndCondition instance = _reusableInstance.getAndSet(null);
if (instance == null)
{
instance = new PriorAndCondition();
}
instance._data[0] = prior;
instance._data[1] = condition;
instance._mask = (prior == null ? 0 : 1) | (condition == null ? 0 : 1) << 1;
return instance;
}
/**
* Returns this instance for reuse.
* <p>
* Returns instance so that it may be returned by the next call to {@link #create}.
* This will avoid having to allocate a new instance.
* <p>
* @since 0.08
* @return null (can be used to assign back to variable to prevent further use)
*/
public @Nullable PriorAndCondition release()
{
Arrays.fill(_data, null);
_mask = -2; // size will be -1
_reusableInstance.set(this);
return null;
}
/*--------------
* List methods
*/
@Override
public IDatum get(int index)
{
// Flip the low bit of the mask to give an offset of 0 or 1.
// This means that this may return a null rather than throwing an out of bounds exception.
return _data[(1&_mask^1) + index];
}
@Override
public int size()
{
// This only works because _mask only can have its lower two bits set.
return _mask + 1 >> 1;
}
/*---------------
* Local methods
*/
/**
* Evaluates energy of prior and condition for given value.
* <p>
* This is the sum of the energies for the passed in {@code value} for the
* {@link #prior()} and {@link #condition()}, except that if the prior is a
* {@link Value} instance, the condition will not be used (this mimics the behavior
* of {@link com.analog.lyric.dimple.data.DataStack#computeTotalEnergy() DataStack.computeTotalEnergy})
* <p>
* @since 0.08
*/
public double evalEnergy(Value value)
{
double energy = 0.0;
for (IDatum datum : _data)
{
if (datum != null)
{
energy += datum.evalEnergy(value);
if (datum instanceof Value)
{
break;
}
}
}
return energy;
}
public @Nullable IDatum prior()
{
return _data[0];
}
public @Nullable IDatum condition()
{
return _data[1];
}
/**
* Returns first of {@link #prior} or {@link #condition} that is a {@link Value} instance.
* @since 0.08
*/
public @Nullable Value value()
{
for (IDatum datum : _data)
{
if (datum instanceof Value)
{
return (Value)datum;
}
}
return null;
}
}