/*******************************************************************************
* 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.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import com.analog.lyric.dimple.exceptions.DimpleException;
import org.eclipse.jdt.annotation.Nullable;
public final class ChainedJointDomainReindexer extends JointDomainReindexer
{
/*-------
* State
*/
private int _hashCode;
private final JointDomainReindexer[] _converters;
private volatile @Nullable ChainedJointDomainReindexer _inverse;
private final boolean _hasFastJointIndexConversion;
private final boolean _maintainsJointIndexOrder;
/*--------------
* Construction
*/
private ChainedJointDomainReindexer(
JointDomainIndexer fromDomains,
@Nullable JointDomainIndexer addedDomains,
JointDomainIndexer toDomains,
@Nullable JointDomainIndexer removedDomains,
JointDomainReindexer ... converters
)
{
super(fromDomains, addedDomains, toDomains, removedDomains);
_converters = converters.clone();
_hashCode = computeHashCode();
boolean fastJointIndex = true;
boolean maintainsOrder = true;
for (JointDomainReindexer converter : converters)
{
fastJointIndex &= converter.hasFastJointIndexConversion();
maintainsOrder &= converter.maintainsJointIndexOrder();
}
_hasFastJointIndexConversion = fastJointIndex;
_maintainsJointIndexOrder = maintainsOrder;
}
static JointDomainReindexer create(JointDomainReindexer firstConverter, JointDomainReindexer ... moreConverters)
{
if (moreConverters.length == 0)
{
return firstConverter;
}
JointDomainReindexer[] converters = new JointDomainReindexer[moreConverters.length + 1];
converters[0] = firstConverter;
System.arraycopy(moreConverters, 0, converters, 1, moreConverters.length);
// TODO: eliminate consecutive converters that cancel each other out, i.e. a converter
// followed by its inverse.
// Make sure that domains form a valid from->to/from->to chain.
for (int i = converters.length; --i>=1;)
{
if (!converters[i]._fromDomains.equals(converters[i-1]._toDomains))
{
throw new DimpleException("Cannot combine converters with mismatched domains.");
}
}
// Flatten out nested chains
int size = 0;
for (JointDomainReindexer converter : converters)
{
if (converter instanceof ChainedJointDomainReindexer)
{
ChainedJointDomainReindexer subchain = (ChainedJointDomainReindexer)converter;
size += subchain._converters.length;
}
else
{
++size;
}
}
if (converters.length != size)
{
JointDomainReindexer[] flattenedConverters = new JointDomainReindexer[size];
for (int to = 0, from = 0; from < converters.length; ++from)
{
JointDomainReindexer converter = converters[from];
if (converter instanceof ChainedJointDomainReindexer)
{
ChainedJointDomainReindexer subchain = (ChainedJointDomainReindexer)converter;
for (JointDomainReindexer subconverter : subchain._converters)
{
flattenedConverters[to++] = subconverter;
}
}
else
{
flattenedConverters[to++] = converter;
}
}
converters = flattenedConverters;
}
JointDomainIndexer addedDomains = null;
for (JointDomainReindexer converter : converters)
{
addedDomains = JointDomainIndexer.concat(addedDomains, converter._addedDomains);
}
JointDomainIndexer removedDomains = null;
for (int i = converters.length; --i>=0;)
{
removedDomains = JointDomainIndexer.concat(removedDomains, converters[i]._removedDomains);
}
return new ChainedJointDomainReindexer(
converters[0]._fromDomains,
addedDomains,
converters[size-1]._toDomains,
removedDomains,
converters);
}
@Override
protected int computeHashCode()
{
return super.computeHashCode() * 23 + Arrays.hashCode(_converters);
}
/*----------------
* Object methods
*/
@Override
public boolean equals(@Nullable Object other)
{
if (this == other)
{
return true;
}
if (other instanceof ChainedJointDomainReindexer)
{
ChainedJointDomainReindexer that = (ChainedJointDomainReindexer)other;
return Arrays.equals(_converters, that._converters);
}
return false;
}
@Override
public int hashCode()
{
return _hashCode;
}
/*--------------------------------------
* JointDomainReindexer methods
*/
@Override
public ChainedJointDomainReindexer getInverse()
{
ChainedJointDomainReindexer inverse = _inverse;
if (inverse == null)
{
final int size = _converters.length;
JointDomainReindexer[] converters = new JointDomainReindexer[size];
for (int from = 0, to = size - 1; from < size; ++from, --to)
{
converters[to] = _converters[from].getInverse();
}
inverse = _inverse = new ChainedJointDomainReindexer(
_toDomains, _removedDomains, _fromDomains, _addedDomains, converters);
}
return inverse;
}
@Override
public void convertIndices(Indices indices)
{
int addOffset = 0, removeOffset = indices.removedIndices.length;
Indices prev = null;
for (JointDomainReindexer converter : _converters)
{
final Indices scratch = converter.getScratch();
if (prev == null)
{
System.arraycopy(indices.fromIndices, 0, scratch.fromIndices, 0, scratch.fromIndices.length);
}
else
{
System.arraycopy(prev.toIndices, 0, scratch.fromIndices, 0, scratch.fromIndices.length);
}
final int addSize = scratch.addedIndices.length;
if (addSize > 0)
{
System.arraycopy(indices.addedIndices, addOffset, scratch.addedIndices, 0, addSize);
addOffset += addSize;
}
converter.convertIndices(scratch);
final int removeSize = scratch.removedIndices.length;
if (removeSize > 0)
{
removeOffset -= removeSize;
System.arraycopy(scratch.removedIndices, 0, indices.removedIndices, removeOffset, removeSize);
}
if (prev != null)
{
prev.release();
}
prev = scratch;
if (scratch.toIndices[0] < 0)
{
Arrays.fill(indices.toIndices, -1);
return;
}
}
if (prev != null)
{
System.arraycopy(prev.toIndices, 0, indices.toIndices, 0, indices.toIndices.length);
prev.release();
}
}
@Override
public int convertJointIndex(int jointIndex, int addedJointIndex)
{
if (_addedDomains == null)
{
for (JointDomainReindexer converter : _converters)
{
jointIndex = converter.convertJointIndex(jointIndex, addedJointIndex);
if (jointIndex < 0)
{
break;
}
}
}
else
{
for (JointDomainReindexer converter : _converters)
{
int localAddedJointIndex = 0;
JointDomainIndexer localAddedDomains = converter._addedDomains;
if (localAddedDomains != null)
{
int card = localAddedDomains.getCardinality();
localAddedJointIndex = addedJointIndex;
addedJointIndex /= card;
localAddedJointIndex -= card * addedJointIndex;
}
jointIndex = converter.convertJointIndex(jointIndex, localAddedJointIndex);
if (jointIndex < 0)
{
break;
}
}
}
return jointIndex;
}
@Override
public int convertJointIndex(int jointIndex, int addedJointIndex, @Nullable AtomicInteger removedJointIndexRef)
{
if (_removedDomains == null || removedJointIndexRef == null)
{
return convertJointIndex(jointIndex, addedJointIndex);
}
int removedJointIndex = 0;
for (JointDomainReindexer converter : _converters)
{
int localAddedJointIndex = 0;
final JointDomainIndexer addedDomains = converter._addedDomains;
if (addedDomains != null)
{
int card = addedDomains.getCardinality();
localAddedJointIndex = addedJointIndex;
addedJointIndex /= card;
localAddedJointIndex -= card * addedJointIndex;
}
jointIndex = converter.convertJointIndex(jointIndex, localAddedJointIndex, removedJointIndexRef);
final JointDomainIndexer removedDomains = converter._removedDomains;
if (removedDomains != null)
{
removedJointIndex *= removedDomains.getCardinality();
removedJointIndex += removedJointIndexRef.get();
}
if (jointIndex < 0)
{
break;
}
}
removedJointIndexRef.set(removedJointIndex);
return jointIndex;
}
@Override
public int[] convertSparseToJointIndex(int[] oldSparseToJointIndex)
{
int[] sparseToJoint = oldSparseToJointIndex;
for (JointDomainReindexer converter : _converters)
{
sparseToJoint = converter.convertSparseToJointIndex(sparseToJoint);
}
return sparseToJoint;
}
@Override
public boolean hasFastJointIndexConversion()
{
return _hasFastJointIndexConversion;
}
@Override
protected boolean maintainsJointIndexOrder()
{
return _maintainsJointIndexOrder;
}
}