/*******************************************************************************
* 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.Iterator;
import org.eclipse.jdt.annotation.Nullable;
import net.jcip.annotations.NotThreadSafe;
/**
* An iterator over any array of integer indices each with a specified bounds
* iterating over the index at position n before that at n+1. This is the
* same as dictionary order if the indices are read from back to front.
* <p>
* For example, the following code:
* <pre>
* DiscreteIndicesIterator iter = new DiscreteIndicesIterator(2, 3);
* while (iter.hasNext())
* {
* System.out.format("%s\n", Arrays.toString(iter.next()));
* }
* </pre>
* will produce the output:
* <pre>
* [0,0]
* [1,0]
* [0,1]
* [1,1]
* [0,2]
* [1,2]
* </pre>
* Unlike most iterators, this always returns the same int[] instance, so if you want
* a fresh copy, you must make sure to clone it.
* <p>
* As a special case, when passed an empty domain list, the iterator will still iterate once!
*
* @since 0.05
*/
@NotThreadSafe
public final class DiscreteIndicesIterator implements Iterator<int[]>
{
/**
* Maximum value for each index.
*/
private final int[] _limits;
/**
* Index values
*/
private final int[] _indices;
/**
* The highest numbered index of {@code _indices} that has not already reached its maximum value.
*/
private int _lastNotAtLimit;
/**
* The highest numbered index of {@code _limits} that is non-zero.
*/
private final int _lastNonZeroLimit;
private boolean _doOnce;
/*--------------
* Construction
*/
/**
* Constructs an iterator over the indices over the specified {@code domains}.
* If {@code domains} isa {@link JointDomainIndexer}, then this will produce
* the same order as the undirected joint index order.
* <p>
* @see #DiscreteIndicesIterator(DomainList, int[])
*/
public DiscreteIndicesIterator(DomainList<DiscreteDomain> domains)
{
this(domains, null);
}
/**
* Constructs an iterator over the indices over the specified {@code domains}
* using the specified {@code indices} array.
* @param indices must be null or have length equal to {@code domains} size.
*
* @see #DiscreteIndicesIterator(DomainList)
*/
public DiscreteIndicesIterator(DomainList<DiscreteDomain> domains, @Nullable int[] indices)
{
this(limitsFromDomains(domains), indices, false);
}
/**
* Constructs an iterator over the indices each in the range [0, sizes[i] - 1].
* @see #DiscreteIndicesIterator(int[], int[])
*/
public DiscreteIndicesIterator(int ... sizes)
{
this(sizes, null);
}
/**
* Constructs an iterator over the indices each in the range [0, sizes[i] - 1].
*
* @param indices must be null or have length equal to {@code domains} size.
*
* @see #DiscreteIndicesIterator(int[], int[])
*/
public DiscreteIndicesIterator(int[] sizes, @Nullable int[] indices)
{
this(sizes, indices, true);
}
private DiscreteIndicesIterator(int[] sizesOrLimits, @Nullable int[] indices, boolean sizes)
{
final int dimensions = sizesOrLimits.length;
if (sizes)
{
_limits = new int[dimensions];
for (int i = 0; i < dimensions; ++i)
{
_limits[i] = sizesOrLimits[i] - 1;
}
}
else
{
_limits = sizesOrLimits;
}
_indices = indices != null ? indices : new int[dimensions];
int lastNonZeroLimit = -1;
for (int i = dimensions; --i>=0;)
{
if (_limits[i] > 0)
{
lastNonZeroLimit = i;
break;
}
}
_lastNonZeroLimit = lastNonZeroLimit;
reset();
}
private static int[] limitsFromDomains(DomainList<DiscreteDomain> domains)
{
final int dimensions = domains.size();
final int[] limits = new int[dimensions];
for (int i = 0; i < dimensions; ++i)
{
limits[i] = domains.get(i).size() - 1;
}
return limits;
}
/*------------------
* Iterator methods
*/
@Override
public boolean hasNext()
{
return _lastNotAtLimit >= 0 || _doOnce;
}
@Override
public int[] next()
{
_doOnce = false;
for (int i = 0; i <= _lastNotAtLimit; ++i)
{
final int limit = _limits[i];
int val = _indices[i] + 1;
if (val <= limit)
{
_indices[i] = val;
if (val == limit && i == _lastNotAtLimit)
{
while (--i>0 && _limits[i] == 0) {}
_lastNotAtLimit = i;
}
break;
}
else // val > limit
{
_indices[i] = 0;
}
}
return _indices;
}
@Override
public void remove()
{
throw new UnsupportedOperationException(getClass().getSimpleName() + ".remove");
}
/*---------------------------------
* DiscreteIndicesIterator methods
*/
/**
* Reset position back to head of iterator.
*/
public void reset()
{
Arrays.fill(_indices, 0);
if (_indices.length > 0)
{
_indices[0] = -1;
}
else
{
_doOnce = true;
}
_lastNotAtLimit = _lastNonZeroLimit;
}
}