All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.meliorbis.numerics.index.impl.Index Maven / Gradle / Ivy

Go to download

A library for working with large multi-dimensional arrays and the functions they represent

There is a newer version: 1.2
Show newest version
package com.meliorbis.numerics.index.impl;

import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.function.IntConsumer;

import org.apache.commons.lang.ArrayUtils;

import com.meliorbis.numerics.NumericsException;
import com.meliorbis.numerics.index.IndexIterator;
import com.meliorbis.numerics.index.SkippingLinearIterator;
import com.meliorbis.numerics.index.SubIndexIterator;
import com.meliorbis.utils.Pair;
import com.meliorbis.utils.Utils;

/**
 * A multi-dimensional index that can convert to/from linear indexing and provides
 * iterators, including separate simultaneous iterators on different sub-spaces
 * 
 * @author Tobias Grasl
 */
public class Index implements Iterable, com.meliorbis.numerics.index.Index
{
	private static final boolean FORCE_CORRECT_DIMS = Boolean.getBoolean("com.meliorbis.numerics.forceCorrectDims");
	
	private final int[] _sizes;
	public final int[] _stepSizes;
	private int _numberOfElements;
	
	public Index(int... sizes_)
	{
		_sizes = Arrays.copyOf(sizes_,sizes_.length);
		
		// Create an array to hold the size of the subspace beyond each dimension
		_stepSizes = new int[getSizes().length];
		
		// The last dimensions stepsize is just one
		_stepSizes[getSizes().length - 1] = 1;
		
		// each previous one is the product of the next dimensions size*times it's stepsize
		// (effectively the reverse cumulative product)
		for(int index = getSizes().length-1; index-- > 0;)
		{
			_stepSizes[index] = _stepSizes[index+1]*getSizes()[index+1];
		}
		
		// The total number of elements
		_numberOfElements = _stepSizes[0]*_sizes[0];
	}

	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#getSizes()
	 */
	@Override
	public int[] getSizes()
	{
		return _sizes;
	}
	
	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#numberOfElements()
	 */
	@Override
	public int numberOfElements()
	{
		return _numberOfElements;
	}
	
	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#numberOfDimensions()
	 */
	@Override
	public int numberOfDimensions()
	{
		return _sizes.length;
	}
	
	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#toLinearIndex(int)
	 */
	@Override
	public int toLinearIndex(int... logicalIndex_) throws IndexOutOfBoundsException
	{	
		int linearIndex = 0;
		
		if(logicalIndex_.length != numberOfDimensions())
		{
			throw new IndexOutOfBoundsException(
					String.format("The index has %s dimensions, but %s index elements were passed",numberOfDimensions(), logicalIndex_.length));
		}
		
		for (int dimIndex = 0; dimIndex < numberOfDimensions(); dimIndex++)
		{
			if(logicalIndex_[dimIndex] >= _sizes[dimIndex])
			{
				throw new IndexOutOfBoundsException(
						String.format("Dimension %s is of size %s, but element %s was requested", dimIndex, _sizes[dimIndex], logicalIndex_[dimIndex]));
			}
			linearIndex += logicalIndex_[dimIndex]*_stepSizes[dimIndex];
		}
		
		return linearIndex;
	}
	
	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#toLogicalIndex(int)
	 */
	@Override
	public int[] toLogicalIndex(int linearIndex_)
	{
		int[] logicalIndex = new int[numberOfDimensions()];
		
		for(int dimIndex = 0; dimIndex < logicalIndex.length; dimIndex++)
		{
			// How many times does the stepSize fit in the index?
			logicalIndex[dimIndex] = linearIndex_/_stepSizes[dimIndex];
			
			// Pass the remainder to the next dimension
			linearIndex_ -= logicalIndex[dimIndex]*_stepSizes[dimIndex];
		}
		
		return logicalIndex;
	}

	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#iterator()
	 */
	@Override
	public IndexIterator iterator()
	{
		return new Iterator();
	}

    @Override
    public Iterator rangeIterator(int from_, int to_)
    {
        return new Iterator(from_, to_);
    }

    /* (non-Javadoc)
     * @see com.meliorbis.numerics.IMultiDimensionalIndex#iterator(int)
     */
	@Override
	public SubSpaceSplitIterator iterator(int... dimensions_)
	{
		return new SubSpaceSplitIterator(dimensions_);
	}

    @Override
	public SkippingLinearIterator[] parallelIterators(int maxCount_, int... dimensions_)
    {
    	// Ensure minimum one
    	if(maxCount_ < 1)
    	{
    		maxCount_ = 1;
    	}
    	
        final int[] currentIndex = Utils.repeatArray(-1, _sizes.length);
        
        // This iterator is across the correct dimensions
        SubSpaceSplitIterator parallelTemplate = new SubSpaceSplitIterator(currentIndex, dimensions_);
        SubSpaceSplitIterator preStart = parallelTemplate.getOrthogonalIterator();
        
        // Start the iterator to initialise the index
        parallelTemplate.nextInt();

     // This one is orthogonal to it

        SubIndex orthSubIndex = subIndexAt(parallelTemplate.getCurrentFullIndex());
        
        // want to go back to pre-start
        parallelTemplate = preStart.getOrthogonalIterator();
        
        /* Figure out how many elements there are in orthogonal dimensions, and from that the number and of iterators to
         * return and the range of each of those iterators
         */
        int orthDimsElements = orthSubIndex.numberOfElements();
        
        int orthRange;
        int orthCount;
        
        if(orthDimsElements < maxCount_) 
        {
        	orthRange = 1;
        	orthCount = orthDimsElements;
        }
        else if(orthDimsElements % maxCount_ == 0)
        {
        	orthRange = orthDimsElements/maxCount_;
        	orthCount = maxCount_;
        }
        else 
        {
        	// maxCount == 1 is caught by the previous condition, so no need to worry
        	orthRange = orthDimsElements/maxCount_+1;
        	orthCount = orthDimsElements/orthRange + (orthDimsElements%orthRange == 0 ? 0 : 1); 
        }

        // Reference the step sizes of the template - they are the same for all the parallel iterators
        final int[] parallelStepSizes = parallelTemplate._subStepSizes;
        // The lenght will again be the same
        final int length = parallelTemplate.numberOfElements();

        // As will the sizes
        final int[] sizes = new int[parallelTemplate._dimensions.length];

        // Copy just the sizes of the dimensions being iterated
        for(int i = 0; i < sizes.length; i++)
        {
            sizes[i] = _sizes[parallelTemplate._dimensions[i]];
        }

        ParallelIterator[] parallelIterators = new ParallelIterator[orthCount];
        
        int from = 0;
        
        for(int i = 0; i < orthCount; i++) {
        	
        	int to = Math.min(from + orthRange,orthDimsElements);

        	SkippingLinearIterator orthRangeIter;
        	if(orthSubIndex._dimensions.length > 0)
        	{
    			orthRangeIter = new SubSpaceSplitIterator(preStart.getCurrentFullIndex(), orthSubIndex._dimensions, from, to);        		
        	}
        	else {
        		to = 1;
        		orthRangeIter = new SkippingLinearIterator()
				{
					boolean hasNext = true;
					@Override
					public boolean hasNext()
					{
						return hasNext;
					}
					
					@Override
					public int nextInt()
					{
						hasNext = false;
						return 0;
					}
					
					@Override
					public void reset()
					{
						hasNext = true;
					}
					
					@Override
					public int nextParallel()
					{
						throw new UnsupportedOperationException();
					}
					
					@Override
					public boolean hasNextParallel()
					{
						throw new UnsupportedOperationException();
					}
					
					@Override
					public int getCurrentFullLinearIndex()
					{
						return -1;
					}
				};
        	}
			

            parallelIterators[i] = new ParallelIterator(parallelStepSizes,
                    sizes,
                    length, orthRangeIter, to - from);
            
			from = to;
        }
       
        return parallelIterators;
    }
	
	public class Iterator implements IndexIterator
	{
		private final int[] _currentIndex;
		private int _linearIndex = 0;

        private final int _last;
		private int _from;

		public Iterator()
		{
			_currentIndex = new int[_sizes.length];
			
			// Initialise the highest index to -1, i.e. pre-first element
            _currentIndex[getCurrentIndex().length - 1] = -1;	
            
	        _last = numberOfElements();

           reset();
		}

        public Iterator(int from_, int to_)
        {
            _from = from_;
			_linearIndex = from_;

            if(from_ > 0)
            {
                // Initialise the highest index to -1, i.e. pre-first element
                _currentIndex = toLogicalIndex(from_);
            }
            else
            {
                _currentIndex = new int[_sizes.length];
            }

            // Initialise the highest index to -1, i.e. pre-first element
            _currentIndex[_currentIndex.length-1] -= 1;

            if(to_ > numberOfElements())
            {
                to_ = numberOfElements();
            }

            if(from_ > to_)
            {
                throw new NumericsException("The start of the iterator must be before the end");
            }

            _last = to_;
        }

		/* (non-Javadoc)
		 * @see com.meliorbis.numerics.IMultiDimensionalIterator#hasNext()
		 */
		@Override
		public boolean hasNext()
		{
			// Is the linear index smaller than the maximal one?
			return _linearIndex < _last;
		}

		@Override
        public Integer next()
        {
        	throw new UnsupportedOperationException("Use nextInt");
        }
		
		/* (non-Javadoc)
		 * @see com.meliorbis.numerics.IMultiDimensionalIterator#next()
		 */
		@Override
		public int nextInt()
		{
            if(_linearIndex == _last)
            {
                throw new NoSuchElementException();
            }

			// Start from the highest dimension
			int dimIndex = _sizes.length-1;
			
			do
			{
				// Increment the index in the current dimension
				_currentIndex[dimIndex]++;
				
				// If we didn't overflow, we're done!
				if(_currentIndex[dimIndex] != _sizes[dimIndex])
				{
					break;
				}
				
				// Otherwise, set the current dimension to 0 and continue
				_currentIndex[dimIndex] = 0;
			}while (dimIndex-- > 0 );
			
			// increment the linear index and return it
			return _linearIndex++;
		}

		/* (non-Javadoc)
		 * @see com.meliorbis.numerics.IMultiDimensionalIterator#remove()
		 */
		@Override
		public void remove()
		{
			throw new UnsupportedOperationException();
		}
		/* (non-Javadoc)
		 * @see com.meliorbis.numerics.IMultiDimensionalIterator#getCurrentIndex()
		 */
		@Override
		public int[] getCurrentIndex()
		{
			return _currentIndex;
		}

		@Override
		public void forEachRemaining(IntConsumer action_)
		{
			// TODO Auto-generated method stub
			
		}

		@Override
		public void reset()
		{
			Arrays.fill(_currentIndex, 0);
			
			// Initialise the highest index to -1, i.e. pre-first element
            _currentIndex[getCurrentIndex().length - 1] = -1;
            _linearIndex = _from;
		}
	}

    public class ParallelIterator implements IndexIterator, SkippingLinearIterator
    {
        private final int[] _currentIndex;
        private int _linearIndex;
        private int _fullLinearIndex;

        private final int[] _subStepSizes;
        private final int[] _sizes;
        private int _last;
		private SkippingLinearIterator _orthRangeIter;
		private final int _numParallel;

        public ParallelIterator(int[] subStepSizes_, int[] sizes_, int length_,
                                SkippingLinearIterator orthRangeIter_, int numParallel_)
        {
            _numParallel = numParallel_;
			_currentIndex = new int[subStepSizes_.length];
            _subStepSizes = subStepSizes_;
            _sizes = sizes_;
            _last = length_;
            _orthRangeIter = orthRangeIter_;
            _fullLinearIndex = _orthRangeIter.getCurrentFullLinearIndex();
        }

        @Override
        public boolean hasNext()
        {
            // Is the linear index smaller than the maximal one?
            return _linearIndex < _last;
        }

        @Override
        public Integer next()
        {
        	throw new UnsupportedOperationException("Use nextInt");
        }
        
        @Override
        public int nextInt()
        {
            // Start from the highest dimension
            int dimIndex = _currentIndex.length-1;

            // Does this iterator have any dimensions
            if(dimIndex >= 0)
            {
                do
                {
                    // Increment the index in the current dimension
                    _currentIndex[dimIndex]++;

                    // If we didn't overflow, we're done!
                    if(_currentIndex[dimIndex] != _sizes[dimIndex])
                    {
                        _fullLinearIndex += _subStepSizes[dimIndex];
                        break;
                    }

                    // If we overflowed the lowest dimension we're beyond the edge of space
                    if(dimIndex == 0)
                    {
                        throw new NoSuchElementException();
                    }

                    // Otherwise, set the current dimension to 0 and continue
                    _currentIndex[dimIndex] = 0;

                }while (dimIndex-- > 0 );
            }

            // increment the linear index and return it
            return _linearIndex++;
        }

        @Override
        public void remove()
        {
            throw new UnsupportedOperationException();
        }

        @Override
        public int[] getCurrentIndex()
        {
            return _currentIndex;
        }

        public int getCurrentFullLinearIndex()
        {
            return _fullLinearIndex;
        }

		@Override
		public void forEachRemaining(IntConsumer action_)
		{
			// TODO Auto-generated method stub
			
		}

		@Override
		public boolean hasNextParallel()
		{
			return _orthRangeIter.hasNext();
		}

		@Override
		public int nextParallel()
		{
			int result = _orthRangeIter.nextInt();
			_fullLinearIndex = _orthRangeIter.getCurrentFullLinearIndex();
			
			Arrays.fill(_currentIndex, 0);
			_currentIndex[_currentIndex.length - 1] = -1;
			_linearIndex = 0;
			
			return result;
		}

		@Override
		public void reset()
		{
			throw new UnsupportedOperationException("Parallel iterators are not resettable");
		}

		public int getNumParallel()
		{
			return _numParallel;
		}
    }
	
	public class SubSpaceSplitIterator implements SubIndexIterator, SkippingLinearIterator
    {
		private final int[] _currentIndex;
		private final int[] _dimensions;
		private int[] _dimensionsToIgnore;
		
		private int _linearIndex = 0;
		private int _fullLinearIndex = 0;
		
		private final int[] _subStepSizes;
		private int _last;
		private final boolean _orthStarted;
		private int _from = -1;
		private int _to = -1;
		
		public SubSpaceSplitIterator(int[] dimensions_)
		{
			this(Utils.repeatArray(-1, _sizes.length),dimensions_);
		}
		
		public SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_)
		{
			this(currentIndex_, dimensions_, new int[0]);
		}
		
		protected SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_, int [] dimensionsToIgnore_)
		{
			_dimensions = dimensions_;
			_currentIndex = Arrays.copyOf(currentIndex_,currentIndex_.length);
			_dimensionsToIgnore = dimensionsToIgnore_;
			
			// The orthogonal iterator is already at some place if the dimensions to ignore are well-defined
			boolean orthStarted = true;
			
			for (int i = 0; i < currentIndex_.length; i++) {
				if(!ArrayUtils.contains(dimensions_, i) && currentIndex_[i] == -1)
				{
					orthStarted = false;
					break;
				}
			}
			
			_orthStarted = orthStarted;
			
			int subNumberOfElements = 1;
            int[] subStepSizes = new int[_dimensions.length];
			int[] stepSizeHelper = new int[_dimensions.length];
			
			// Set the dimensions this iterator iterates over to 0 in the index
			for(int dimIndex = _dimensions.length - 1; dimIndex >= 0; dimIndex--)
			{
                // Initialise the index elements to 0, except the highest - see below
                _currentIndex[_dimensions[dimIndex]] = 0;

                subNumberOfElements *= _sizes[_dimensions[dimIndex]];
				
				if(dimIndex == _dimensions.length - 1)
				{
					// For the highest dimension iterated over it is just the stepsize
                    subStepSizes[dimIndex] = _stepSizes[_dimensions[dimIndex]];
				}
				else
				{
					// This may seem like a complicated way to do it but the possibility of
					// transposed dimensions means we can't do it more succinctly
					stepSizeHelper[dimIndex] = _sizes[_dimensions[dimIndex+1]]*(stepSizeHelper[dimIndex+1] + subStepSizes[dimIndex+1])- subStepSizes[dimIndex+1];

                    subStepSizes[dimIndex] = _stepSizes[_dimensions[dimIndex]] - stepSizeHelper[dimIndex];
				}
			}
            _subStepSizes = subStepSizes;

            _last = subNumberOfElements;
			
			_fullLinearIndex = toLinearIndex(_currentIndex);
			if(numberOfDimensions() > 0)
			{
				_fullLinearIndex -= _subStepSizes[_dimensions.length -1];
				
				// Except the highest, which we set to -1 to indicate 'pre-start'
				_currentIndex[_dimensions[_dimensions.length - 1]] = -1;
			}
			
		}
		
		public SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_, int[] dimensionsToIgnore_, int from_, int to_)
        {
            this(currentIndex_, dimensions_, dimensionsToIgnore_);
			_from = from_;
			_to = to_;

			if(_to < _last)
            {
                _last = _to;
            }

			initCurrentIndex();
        }
		 
        public SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_, int from_, int to_)
        {
            this(currentIndex_, dimensions_, new int[0]);
			_from = from_;
			_to = to_;

			if(_to < _last)
            {
                _last = _to;
            }

			initCurrentIndex();
        }

		/**
		 * @throws IndexOutOfBoundsException
		 */
		private void initCurrentIndex() throws IndexOutOfBoundsException
		{
			int from = _from;
            
            if (from == -1)
            {
                from = 0;
            }
            
            _linearIndex = from;

            if(_dimensions.length == 0) {
            	return;
            }
            int[] stepSizes = new int[_dimensions.length];

            stepSizes[_dimensions.length-1] = 1;

            for(int dim = _dimensions.length-2; dim >= 0; dim --)
            {
                stepSizes[dim] =  stepSizes[dim+1]*_sizes[_dimensions[dim+1]];
            }

            for(int dim = 0; dim < _subStepSizes.length - 1; dim++)
            {
                if(from < stepSizes[dim])
                {
                	_currentIndex[_dimensions[dim]] = 0;
                    continue;
                }
                else
                {
                    _currentIndex[_dimensions[dim]] = from/stepSizes[dim];
                    from = from % stepSizes[dim];
                }
            }

            // The remainder goes in the last dimension
            _currentIndex[_dimensions[_dimensions.length-1]] = from;
            _fullLinearIndex = toLinearIndex(_currentIndex);

            if(numberOfDimensions() > 0)
            {
                _fullLinearIndex -= _subStepSizes[_dimensions.length -1];

                // Except the highest, which we set to -1 to indicate 'pre-start'
                _currentIndex[_dimensions[_dimensions.length - 1]] -= 1;
            }
		}

        public int numberOfDimensions()
		{
			return _dimensions.length;
		}
		
		public int numberOfElements()
		{
			return _last;
		}
		
		@Override
		public boolean hasNext()
		{
			// Is the linear index smaller than the maximal one?
			return _linearIndex < _last;
		}

		
		@Override
		public int nextInt()
		{
			// Start from the highest dimension
			int dimIndex = _dimensions.length-1;

			// Does this iterator have any dimensions
			if(dimIndex >= 0)
			{
				do
				{
					// Increment the index in the current dimension
					int dimension = _dimensions[dimIndex];
					_currentIndex[dimension]++;
					
					// If we didn't overflow, we're done!
					if(_currentIndex[dimension] != _sizes[dimension])
					{
						_fullLinearIndex += _subStepSizes[dimIndex];
						break;
					}
					
					// If we overflowed the lowest dimension we're beyond the edge of space
					if(dimIndex == 0)
					{
						throw new NoSuchElementException();
					}
					
					// Otherwise, set the current dimension to 0 and continue
					_currentIndex[dimension] = 0;
				}while (dimIndex-- > 0 );
			}
			
			// increment the linear index and return it
			return _linearIndex++;
		}

		@Override
		public Integer next()
		{
			throw new UnsupportedOperationException("use nextInt");
		}

		@Override
		public void remove()
		{
			throw new UnsupportedOperationException();
		}
		
		/**
		 * @return the currentIndex
		 */
		@Override
		public int[] getCurrentIndex()
		{
			if(numberOfDimensions() == 0)
			{
				// The linear index is post-incremented in next, so we need to use the value one earlier
				return new int[]{_linearIndex-1};
			}
			
			int[] subIndex = new int[_dimensions.length];
			
			for (int i = 0; i < subIndex.length; i++)
			{
				subIndex[i] = _currentIndex[_dimensions[i]];
			}
			
			return subIndex;
		}
		
		public SubSpaceSplitIterator getOrthogonalIterator()
		{
			// Clone the index so that we can work in a multi-threaded environment
			int[] clonedIndex = Arrays.copyOf(_currentIndex,_currentIndex.length);
			
			if(numberOfDimensions() + _dimensionsToIgnore.length == Index.this.numberOfDimensions())
			{
				return new SubSpaceSplitIterator(clonedIndex,new int[]{},_dimensionsToIgnore);
			}
			
			int[] otherDimensions = new int[_sizes.length - _dimensions.length-_dimensionsToIgnore.length];

			int otherDimIndex = 0;
			
			for(int dimIndex = 0; dimIndex < _sizes.length; dimIndex++)
			{
				if(!ArrayUtils.contains(_dimensions, dimIndex) && !ArrayUtils.contains(_dimensionsToIgnore, dimIndex))
				{
					otherDimensions[otherDimIndex++] = dimIndex;
				}
			}
			
			return new SubSpaceSplitIterator(clonedIndex, otherDimensions,_dimensionsToIgnore);
		}
		
		@Override
		public int[] getOtherIndex()
		{
			int[] otherIndex = new int[_sizes.length - _dimensions.length-_dimensionsToIgnore.length];

			int otherDimIndex = 0;
			
			for(int dimIndex = 0; dimIndex < _sizes.length; dimIndex++)
			{
				if(!ArrayUtils.contains(_dimensions, dimIndex) && !ArrayUtils.contains(_dimensionsToIgnore, dimIndex))
				{
					otherIndex[otherDimIndex++] = _currentIndex[dimIndex];
				}
			}
			
			return otherIndex;
		}
		
		public boolean orthStarted()
		{
			return _orthStarted;
		}

		@Override
		public int[] getCurrentFullIndex()
		{
			int[] fullIndex = new int[_sizes.length - _dimensionsToIgnore.length];

			int otherDimIndex = 0;
			
			for(int dimIndex = 0; dimIndex < _sizes.length; dimIndex++)
			{
				if(!ArrayUtils.contains(_dimensionsToIgnore, dimIndex))
				{
					fullIndex[otherDimIndex++] = _currentIndex[dimIndex];
				}
			}
			
			return fullIndex;
		}

		@Override
        public int getCurrentFullLinearIndex()
		{
			return _fullLinearIndex;
		}
		
		public int[] getDimensions()
		{
			return _dimensions;
		}
		
		public int[] getSizes()
		{
			return getSubSizes(_dimensions);
		}

		public void ignoreDimensions(int[] dimensionsToIgnore_) {
			_dimensionsToIgnore = dimensionsToIgnore_;
		}

		@Override
		public void forEachRemaining(IntConsumer action_)
		{
			// TODO Auto-generated method stub
			
		}

		@Override
		public boolean hasNextParallel()
		{
			return false;
		}

		@Override
		public int nextParallel()
		{
			throw new UnsupportedOperationException();
		}

		@Override
		public void reset()
		{
			initCurrentIndex();
		}
	}

	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#iteratorAt(int[])
	 */
	@Override
	public SubIndexIterator iteratorAt(int[] index_)
	{
		Pair pair = getOtherDimsAndFullIndexAt(index_);
		return new SubSpaceSplitIterator(pair.getRight(), pair.getLeft());
	}

	public Pair getOtherDimsAndFullIndexAt(int[] index_)
	{
		int[] initialisedIndex = new int[numberOfDimensions()];
		
		// Create arrays that are big enough if needed
		int[] otherDimensions = new int[numberOfDimensions()];
			
		int otherCount = 0;
		
		// Determine which dimensions are not tied down in index_; these are the ones to fill along
		for(int dimIndex = 0; dimIndex < numberOfDimensions(); dimIndex++)
		{
			if(dimIndex >= index_.length || index_[dimIndex]== -1)
			{
				otherDimensions[otherCount++] = dimIndex;
				initialisedIndex[dimIndex] = -1;
			}
			else
			{
				// Copy the index to iterate at to the current index that will be used for the iterator
				// We have to do this because index_ may be too short
				initialisedIndex[dimIndex] = index_[dimIndex];
			}
		}
		
		otherDimensions = Arrays.copyOf(otherDimensions,otherCount);
		Pair pair = new Pair(otherDimensions, initialisedIndex);
		return pair;
	}
	
	public Pair getOtherDimsAndFullIndexAt(int[] index_, int[] dimensionsToIgnore_)
	{
		int[] initialisedIndex = new int[numberOfDimensions()-dimensionsToIgnore_.length];
		
		// Create arrays that are big enough if needed
		int[] otherDimensions = new int[numberOfDimensions()-dimensionsToIgnore_.length];
			
		int otherCount = 0;
		int subDimIndex = 0;
		int dimToIgnoreIndex = 0;
		
		// Determine which dimensions are not tied down in index_; these are the ones to fill along
		for(int dimIndex = 0; dimIndex < numberOfDimensions(); dimIndex++)
		{
			// Skip the dimension if it is to be ignored
			if(dimIndex == dimensionsToIgnore_[dimToIgnoreIndex])
			{
				dimToIgnoreIndex++;
				continue;
			}
			
			if(dimIndex >= index_.length || index_[dimIndex]== -1)
			{
				otherDimensions[otherCount++] = dimIndex;
				initialisedIndex[subDimIndex] = -1;
			}
			else
			{
				// Copy the index to iterate at to the current index that will be used for the iterator
				// We have to do this because index_ may be too short
				initialisedIndex[subDimIndex] = index_[dimIndex];
			}
			subDimIndex++;
		}
		
		otherDimensions = Arrays.copyOf(otherDimensions,otherCount);
		Pair pair = new Pair(otherDimensions, initialisedIndex);
		return pair;
	}

	/* (non-Javadoc)
	 * @see com.meliorbis.numerics.IMultiDimensionalIndex#subIndexAt(int[])
	 */
	@Override
	public SubIndex subIndexAt(int[] atPosition_)
	{
		return new SubIndex(atPosition_);
	}
	
	public int[] getSubSizes(int[] dimensions)
	{
		if(dimensions.length == 0)
		{
			return new int[]{1};
		}
		
		int[] sizes = new int[dimensions.length];
		
		for (int i = 0; i < sizes.length; i++)
		{
			// Get the size of each dimension that is part of this iterator
			sizes[i] = _sizes[dimensions[i]];
		}
		
		return sizes;
	}

	public class SubIndex implements com.meliorbis.numerics.index.SubIndex
	{
		private final int[] _currentIndex;
		private final int[] _dimensions;
		private final int[] _subSizes;
		private final int _subNumberOfElements;
		
		private final Index _subIndex;
		
		private SubIndex(int[] indexAt_)
		{
			if(FORCE_CORRECT_DIMS && Index.this._sizes.length != indexAt_.length)
			{
				throw new RuntimeException("Incorrect number of dims");
			}
			
			Pair otherDimsAndFullIndexAt = getOtherDimsAndFullIndexAt(indexAt_);

			_dimensions = otherDimsAndFullIndexAt.getLeft();
			_subSizes = getSubSizes(otherDimsAndFullIndexAt.getLeft());
			_currentIndex = otherDimsAndFullIndexAt.getRight();
			
			_subIndex = new Index(_subSizes);
			
			int subNumberOfElements = 1;
			
			for (int i = 0; i < _subSizes.length; i++)
			{
				subNumberOfElements *= _subSizes[i];
			}
			_subNumberOfElements = subNumberOfElements;
		}
		
		private SubIndex(int[] indexAt_, int[] dimensionOrder_)
		{
			Pair otherDimsAndFullIndexAt = getOtherDimsAndFullIndexAt(indexAt_);

			_dimensions = dimensionOrder_;
			_subSizes = getSubSizes(dimensionOrder_);
			_currentIndex = otherDimsAndFullIndexAt.getRight();
			
			_subIndex = new Index(_subSizes);
			
			int subNumberOfElements = 1;
			
			for (int i = 0; i < _subSizes.length; i++)
			{
				subNumberOfElements *= _subSizes[i];
			}
			_subNumberOfElements = subNumberOfElements;
		}


		@Override
		public int[] getSizes()
		{
			return _subSizes;
		}

		@Override
		public int numberOfElements()
		{
			return _subNumberOfElements;
		}

		@Override
		public int numberOfDimensions()
		{
			return getDimensions().length;
		}
		
		@Override
		public int[] fullIndex(int[] subIndex_)
		{
			int[] currentIndex = Arrays.copyOf(_currentIndex, _currentIndex.length);
			
			if(_dimensions.length == 0)
			{
				if(subIndex_.length != 1 || subIndex_[0] != 0)
				{
					throw new RuntimeException("Singleton indices only support index [0]");
				}
				
				// All is well, the currentIndex is the daddy
				return currentIndex;
			}
			
			for (int i = 0; i < subIndex_.length; i++)
			{
				currentIndex[getDimensions()[i]] = subIndex_[i];
			}
			
			return currentIndex;
		}
		
		@Override
		public int[] subIndex(int[] fullIndex_)
		{
			int[] subIndex = new int[getDimensions().length];
			
			for (int i = 0; i < subIndex.length; i++)
			{
				subIndex[i] = fullIndex_[getDimensions()[i]];
			}
			
			return subIndex;
		}

		@Override
		public int toLinearIndex(int... logicalIndex_)
				throws IndexOutOfBoundsException
		{
			return _subIndex.toLinearIndex(logicalIndex_);
		}

		@Override
		public int[] toLogicalIndex(int linearIndex_)
		{
			return _subIndex.toLogicalIndex(linearIndex_);			
		}

		@Override
		public IndexIterator iterator()
		{
			return Index.this.new SubSpaceSplitIterator(_currentIndex, getDimensions());
		}

        @Override
        public SubSpaceSplitIterator rangeIterator(int from_, int to_)
        {
            return Index.this.new SubSpaceSplitIterator(_currentIndex, getDimensions(), from_, to_);
        }

        @Override
		public SubSpaceSplitIterator iterator(int... dimensions_)
		{
			int[] dimensionsToIgnore = otherDimensions();
			
			return Index.this.new SubSpaceSplitIterator(_currentIndex.clone(),subDimensionsInFullIndex(dimensions_),dimensionsToIgnore);
		}

        @Override
        public SkippingLinearIterator[] parallelIterators(int maxCount_, int... dimensions_)
        {
        	// Ensure minimum one
        	if(maxCount_ < 1)
        	{
        		maxCount_ = 1;
        	}
        	
            int[] dimensionsToIgnore = otherDimensions();

            // This iterator is across the correct dimensions
            SubSpaceSplitIterator parallelTemplate = new SubSpaceSplitIterator(_currentIndex.clone(),
                    subDimensionsInFullIndex(dimensions_),
                    dimensionsToIgnore);
            SubSpaceSplitIterator preStart = parallelTemplate.getOrthogonalIterator();
            
            // Start the iterator to initialise the index
            parallelTemplate.nextInt();

            // This one is orthogonal to it
            SubIndex orthSubIndex = subIndexAt(parallelTemplate.getCurrentFullIndex());
         
            // want to go back to pre-start
            parallelTemplate = preStart.getOrthogonalIterator();
            
            /* Figure out how many elements there are in orthogonal dimensions, and from that the number and of iterators to
             * return and the range of each of those iterators
             */
            int orthDimsElements = orthSubIndex.numberOfElements();
            
            int orthRange;
            int orthCount;
            
            if(orthDimsElements < maxCount_) 
            {
            	orthRange = 1;
            	orthCount = orthDimsElements;
            }
            else
            {
            	orthRange = orthDimsElements/maxCount_ + (orthDimsElements % maxCount_ == 0 ? 0 : 1);
            	
            	orthCount = maxCount_;
            }

            // Reference the step sizes of the template - they are the same for all the parallel iterators
            final int[] parallelStepSizes = parallelTemplate._subStepSizes;
            
            // The length will again be the same
            final int length = parallelTemplate.numberOfElements();

            // As will the sizes
            final int[] sizes = new int[parallelTemplate._dimensions.length];

            // Copy just the sizes of the dimensions being iterated
            for(int i = 0; i < sizes.length; i++)
            {
                sizes[i] = _sizes[parallelTemplate._dimensions[i]];
            }

            ParallelIterator[] parallelIterators = new ParallelIterator[orthCount];
            
            int from = 0;
            
            for(int i = 0; i < orthCount; i++) {
            	int to = Math.min(from + orthRange,orthDimsElements);
    			SubSpaceSplitIterator orthRangeIter = new SubSpaceSplitIterator(
    					preStart._currentIndex.clone(), orthSubIndex._dimensions,
    					from, to);
    			


                parallelIterators[i] = new ParallelIterator(parallelStepSizes,
                        sizes,
                        length, orthRangeIter, to - from);
                
    			from = to;
            }
           
            return parallelIterators;
        }

		/**
		 * @return Returns the dimensions to ignore in the full space when a subspaceSplitIteator is created
		 * on this subindex
		 */
		@Override
		public int[] otherDimensions() 
        {
			int[] dimensionsToIgnore = new int[Index.this.numberOfDimensions() - getDimensions().length];
			
			int dimCount = 0;
			// The dimensions that are fixed in this subIndex can be ignored in the split iterator (since it only iterates over the
			// subIndex
			for(int i = 0; i < Index.this.numberOfDimensions(); i++)
			{
				if(!ArrayUtils.contains(getDimensions(),i))
				{
					dimensionsToIgnore[dimCount++] = i;
				}
			}
			return dimensionsToIgnore;
		}

		/**
		 * Retrieves the index of the specified sub-space dimensions in the full space
		 * 
		 * @param dimensions_ The dimensions of this index to get
		 * 
		 * @return The same dimensions but in the full index
		 */
		@Override
		public int[] subDimensionsInFullIndex(int... dimensions_) {
			int[] actualDimensions = new int[dimensions_.length];
			
			for (int i = 0; i < actualDimensions.length; i++)
			{
				actualDimensions[i] = getDimensions()[dimensions_[i]];
			}
			return actualDimensions;
		}

		@Override
		public SubIndexIterator iteratorAt(int[] index_)
		{
			int[] dimensionsToIgnore = otherDimensions();
			
			Pair pair = getOtherDimsAndFullIndexAt(fullIndex(index_));
			
			return Index.this.new SubSpaceSplitIterator(pair.getRight(),pair.getLeft(),dimensionsToIgnore);			
		}

		private int[] subSubIndex(int[] index_)
		{
			int[] newIndex = Arrays.copyOf(_currentIndex, _currentIndex.length);
			
			for(int i = 0; i < getDimensions().length; i++)
			{
				// 'unset' the dimensions this index is over, meaning it can't be part of the current index
				newIndex[getDimensions()[i]] = -1;
			}
			
			// Now set the additional dimensions being fixed
			for(int i = 0;i < index_.length; i++)
			{
				if(index_[i] != -1)
				{
					newIndex[getDimensions()[i]] = index_[i];
				}
			}
			return newIndex;
		}

		@Override
		public SubIndex subIndexAt(int[] atPosition_)
		{
			return Index.this.subIndexAt(subSubIndex(atPosition_));
		}

		@Override
		public Index nonSubbed()
		{
			// Get a multidimensional index with the same sizes as this one
			return new Index(getSizes());
		}

		@Override
		public int[] getDimensions() {
			return _dimensions;
		}

		@Override
		public com.meliorbis.numerics.index.Index transpose(int dimA_, int dimB_) {
			int[] indexAt = new int[_currentIndex.length];
			int[] dimensions = Arrays.copyOf(_dimensions, _dimensions.length);
			
			for (int i = 0; i < indexAt.length; i++) {
				indexAt[i] = ArrayUtils.contains(dimensions, i)?-1:_currentIndex[i];
			}
			
			int tmp = dimensions[dimA_];
			dimensions[dimA_] = dimensions[dimB_];
			dimensions[dimB_] = tmp;
			
			return new SubIndex(indexAt, dimensions);
		}

		@Override
		public Index reorder(int[] order_)
		{
			// TODO: Implement - not hard!
			throw new UnsupportedOperationException();
		}
	}

	@Override
	public Index nonSubbed()
	{
		// This is a non-subbed index and it is immutable, so return it
		return this;
	}

	@Override
	public com.meliorbis.numerics.index.Index transpose(int dimA_, int dimB_) {
		
		if(_sizes.length == 1)
		{
			// do nothing
			return new SubIndex(new int[0], new int[]{0});
		}
		
		int[] newDims = new int[_sizes.length];
		
		for (int i = 0; i < newDims.length; i++) 
		{
			newDims[i] = (i == dimA_ ? dimB_: i == dimB_ ? dimA_ : i);
		}
		
		return new SubIndex(new int[0], newDims);
	}
	
	@Override
	public com.meliorbis.numerics.index.Index reorder(int[] order_) {
		
		return new SubIndex(new int[0], order_);
	}

	@Override
	public String toString()
	{
		return "Index(size=" + Arrays.toString(_sizes) + ")";
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy