PsyArray.java

package coneforest.psylla.core;

import coneforest.psylla.runtime.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

/**
*	The representation of {@code array}.
*/
@Type("array")
public class PsyArray
	implements PsyFormalArray<PsyObject>
{
	/**
	*	Context action of the {@code array} operator.
	*/
	@OperatorType("array")
	public static final ContextAction PSY_ARRAY
		=ContextAction.ofSupplier(PsyArray::new);

	/**
	*	Context action of the {@code arraytomark} operator.
	*/
	@OperatorType("arraytomark")
	public static final ContextAction PSY_ARRAYTOMARK=oContext->
		{
			final var ostack=oContext.operandStack();
			final var i=ostack.findMarkPosition();
			final var oArray=new PsyArray();
			for(int j=i+1; j<ostack.size(); j++)
				oArray.psyAppend(ostack.get(j));
			ostack.setSize(i);
			ostack.push(oArray);
		};

	/**
	*	Context action of the {@code binarysearch} operator.
	*/
	@OperatorType("binarysearch")
	public static final ContextAction PSY_BINARYSEARCH=oContext->
		{
			final var ostack=oContext.operandStackBacked(3);
			final var oIndex=ostack.<PsyArray>getBacked(0).psyBinarySearch(
					ostack.getBacked(1), ostack.getBacked(2), oContext);
			final var index=oIndex.intValue();
			if(index>=0)
			{
				ostack.push(oIndex);
				ostack.push(PsyBoolean.TRUE);
			}
			else
			{
				ostack.push(PsyInteger.of(-index-1));
				ostack.push(PsyBoolean.FALSE);
			}
		};

	/**
	*	Context action of the {@code sort} operator.
	*/
	@OperatorType("sort")
	public static final ContextAction PSY_SORT=oContext->
		{
			final var ostack=oContext.operandStackBacked(2);
			ostack.<PsyArray>getBacked(0).psySort(ostack.getBacked(1), oContext);
		};

	protected final ArrayList<PsyObject> array;

	/**
	*	Constructs a new empty {@code array}.
	*/
	public PsyArray()
	{
		this(new ArrayList<PsyObject>());
	}

	/**
	*	Constructs a new {@code array} wrapped around the given array list.
	*
	*	@param array a given array list.
	*/
	public PsyArray(final ArrayList<PsyObject> array)
	{
		this.array=array;
	}

	@Override
	public int length()
	{
		return array.size();
	}

	@Override
	public Iterator<PsyObject> iterator()
	{
		return array.iterator();
	}

	@SuppressWarnings("unchecked")
	@Override
	public PsyArray psyClone()
	{
		return new PsyArray((ArrayList<PsyObject>)array.clone());
	}

	@Override
	public PsyObject get(final int index)
		throws PsyRangeCheckException
	{
		try
		{
			return array.get(index<0? index+length(): index);
		}
		catch(final IndexOutOfBoundsException e)
		{
			throw new PsyRangeCheckException();
		}
	}

	@Override
	public PsyArray psyGetInterval(final PsyInteger oStart, final PsyInteger oCount)
		throws PsyRangeCheckException
	{
		try
		{
			return new PsyArray(new ArrayList<PsyObject>(array.subList(oStart.intValue(),
					oStart.intValue()+oCount.intValue())));
		}
		catch(final IndexOutOfBoundsException|IllegalArgumentException e)
		{
			throw new PsyRangeCheckException();
		}
	}

	@Override
	public void psyAppend(final PsyObject o)
		throws PsyLimitCheckException
	{
		if(length()==Integer.MAX_VALUE)
			throw new PsyLimitCheckException();
		array.add(o);
	}

	@Override
	public void insert(final int indexValue, final PsyObject o)
		throws PsyRangeCheckException
	{
		try
		{
			array.add(indexValue<0? indexValue+length(): indexValue, o);
		}
		catch(final IndexOutOfBoundsException e)
		{
			throw new PsyRangeCheckException();
		}
	}

	@Override
	public void put(final int indexValue, final PsyObject o)
		throws PsyRangeCheckException
	{
		try
		{
			array.set(indexValue<0? indexValue+length(): indexValue, o);
		}
		catch(final IndexOutOfBoundsException e)
		{
			throw new PsyRangeCheckException();
		}
	}

	@Override
	public void delete(final int indexValue)
		throws PsyRangeCheckException
	{
		try
		{
			array.remove(indexValue);
		}
		catch(final IndexOutOfBoundsException e)
		{
			throw new PsyRangeCheckException();
		}
	}

	@Override
	public PsyObject extract(final int indexValue)
		throws PsyRangeCheckException
	{
		try
		{
			return array.remove(indexValue<0? indexValue+length(): indexValue);
		}
		catch(final IndexOutOfBoundsException e)
		{
			throw new PsyRangeCheckException();
		}
	}

	@Override
	public PsyArray psyExtractInterval(final PsyInteger oStart, final PsyInteger oCount)
		throws PsyRangeCheckException
	{
		final var oResult=psyGetInterval(oStart, oCount);
		array.subList(oStart.intValue(), oStart.intValue()+oCount.intValue()).clear();
		return oResult;
	}

	@Override
	public PsyArray psySlice(final PsyIterable<PsyInteger> oIndices)
		throws PsyRangeCheckException, PsyLimitCheckException
	{
		final var oValues=new PsyArray();
		for(final var oIndex: oIndices)
			oValues.psyAppend(psyGet(oIndex));
		return oValues;
	}

	@Override
	public void psyClear()
	{
		array.clear();
	}

	@Override
	public void psySetLength(final PsyInteger oLength)
		throws PsyRangeCheckException, PsyLimitCheckException
	{
		final var length=oLength.longValue();
		if(length<0)
			throw new PsyRangeCheckException();
		if(length>Integer.MAX_VALUE)
			throw new PsyLimitCheckException();
		int i=length();
		if(length<i)
			array.subList((int)length, i).clear();
		else
		{
			array.ensureCapacity((int)length);
			while(i++<length)
				array.add(PsyNull.NULL);
		}
	}

	/**
	*	Sorts this list according to the order induced by the specified comparator. The sort is
	*	stable: this method must not reorder equal elements.
	*
	*	@param oComparator the {@code proc} comparator used to compare array elements.
	*	@param oContext the execution context.
	*/
	public void psySort(final PsyProc oComparator, final PsyContext oContext)
	{
		array.sort(oComparator.asComparator(oContext));
	}

	/**
	*	Searches this array for the specified value using the binary search algorithm. The array
	*	must be sorted into ascending order according to the specified comparator. TODO
	*
	*	@param o the value to be searched for.
	*	@param oComparator the comparator by which the array is ordered.
	*	@param oContext the execution context.
	*	@return TODO
	*/
	public PsyInteger psyBinarySearch(final PsyObject o, final PsyProc oComparator, final PsyContext oContext)
	{
		return PsyInteger.of(Collections.<PsyObject>binarySearch(array, o,
				oComparator.asComparator(oContext)));
	}

	@Override
	public PsyStream psyStream()
	{
		return new PsyStream(array.stream());
	}
}