/* --------------------------------------------------------- *
* __________ D E L T A S C R I P T *
* (_________() *
* / === / - A fast, dynamic scripting language *
* | == | - Version 4.13.11.0 *
* / === / - Developed by Adam R. Nelson *
* | = = | - 2011-2013 *
* / === / - Distributed under GNU LGPL v3 *
* (________() - http://github.com/ar-nelson/deltascript *
* *
* --------------------------------------------------------- */
package com.sector91.delta.script.objects;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.sector91.delta.script.DScriptErr;
import com.sector91.delta.script.DeltaScript;
import com.sector91.delta.script.NumberTypes;
import com.sector91.delta.script.Operator;
import com.sector91.delta.script.annotations.DSDynamicField;
import com.sector91.delta.script.annotations.DSInaccessible;
import com.sector91.delta.script.annotations.DSName;
import com.sector91.delta.script.annotations.DSType;
import com.sector91.delta.script.objects.reflect.DS_JavaClass;
/**
* <p>A range of numbers, which is treated as a sequence and can be iterated
* over.</p>
*
* <p>Ranges are defined with the {@code to} operator (ex: {@code 1 to 5}) or
* the {@code until} operator (ex: {@code 1 until 5}). Ranges are immutable, and
* support most basic sequence operations (indexing, slices, etc).</p>
*
* @author Adam R. Nelson
* @version 4.13.11.0
*/
@DSType("Range")
public class DS_Range extends DS_AbstractObject
implements DS_Iterable,
DS_Indexable,
DS_Numeric,
Serializable
{
private static final long serialVersionUID =
DeltaScript.VERSION.majorVersion();
private static final DS_Scalar DEFAULT_STEP = ScalarFactory.fromInt(1);
public static final String TYPE_NAME = "Range";
private static final DS_JavaClass DSCLASS = DS_JavaClass.fromClass(
DS_Range.class);
private final int nType;
private final DS_Scalar upperBound, lowerBound, step;
private final boolean inclusive;
public DS_Range(DS_Scalar lowerBound, DS_Scalar upperBound,
boolean inclusive)
{this(lowerBound, upperBound, DEFAULT_STEP, inclusive);}
@DSInaccessible
public DS_Range(DS_Scalar lowerBound, DS_Scalar upperBound,
boolean inclusive, int nType)
{this(lowerBound, upperBound, DEFAULT_STEP, inclusive, nType);}
public DS_Range(DS_Scalar lowerBound, DS_Scalar upperBound, DS_Scalar step,
boolean inclusive)
{
this(lowerBound, upperBound, step, inclusive,
upperBound.getNumberType() |
lowerBound.getNumberType() |
step.getNumberType());
}
@DSInaccessible
public DS_Range(DS_Scalar lowerBound, DS_Scalar upperBound, DS_Scalar step,
boolean inclusive, int nType)
{
this.nType = nType;
if (step.signum() != 1)
throw new IllegalArgumentException("A range's step must be" +
" positive and nonzero.");
if (upperBound.compareTo(lowerBound) >= 0)
{
this.upperBound = upperBound.castTo(nType);
this.lowerBound = lowerBound.castTo(nType);
this.step = step.castTo(nType);
}
else
{
this.upperBound = lowerBound.castTo(nType);
this.lowerBound = upperBound.castTo(nType);
this.step = step.negate().castTo(nType);
}
this.inclusive = inclusive;
}
/**
* <p>Returns the number at which this range starts.</p>
*
* @return The number at which this range starts.
*/
@DSName({"lowerBound", "start", "min"}) @DSDynamicField
public DS_Scalar lowerBound()
{return lowerBound;}
/**
* <p>Returns the number at which this range ends.</p>
*
* @return The number at which this range ends.
*/
@DSName({"upperBound", "end", "max"}) @DSDynamicField
public DS_Scalar upperBound()
{return upperBound;}
/**
* <p>Returns the amount by which this range increments every
* iteration.</p>
*
* @return The amount by which this range increments every iteration.
*/
@DSName("step") @DSDynamicField
public DS_Scalar step()
{return step;}
@DSName("span") @DSDynamicField
public DS_Scalar span()
{return ScalarFactory.sub(upperBound, lowerBound, nType);}
@DSInaccessible
public DS_Scalar random()
{return ScalarFactory.add(lowerBound, span().random(), nType);}
@DSName("clamp")
public DS_Scalar clamp(DS_Scalar num)
{
if (num.compareTo(upperBound) > 0)
return upperBound;
else if (num.compareTo(lowerBound) < 0)
return lowerBound;
return num;
}
// DS_Sequence Methods
// ----------------------------------------------------
@DSName("size") @DSDynamicField
public DS_Integer realSize()
{
final DS_Scalar div = ScalarFactory.div(span(), step.absolute(), nType);
final DS_Integer size = div.floor();
if (inclusive || !div.equals(size))
return size.increment(1);
return size;
}
@DSName("iterSize") @DSDynamicField
public int size()
{
// TODO: Handle int overflow for size().
return realSize().intValue();
}
@DSName("empty") @DSDynamicField
public boolean isEmpty()
{return !inclusive && upperBound.equals(lowerBound);}
@DSName("contains")
public boolean contains(DS_Object o)
{
if (o instanceof DS_Scalar)
{
DS_Scalar n = (DS_Scalar)o;
if (n.compareTo(lowerBound) >= 0)
{
final int ucmp = n.compareTo(upperBound);
if (ucmp < 0 || (inclusive && ucmp == 0))
return true;
}
return false;
}
else if (o instanceof DS_Range)
{
DS_Range r = (DS_Range)o;
if (r.lowerBound.compareTo(lowerBound) >= 0)
{
final int ucmp = r.upperBound.compareTo(upperBound);
if (ucmp < 0 || ((inclusive || !r.inclusive) && ucmp == 0))
return true;
}
return false;
}
else return false;
}
@DSInaccessible
public Iterator<DS_Object> iterator()
{
switch (nType)
{
case NumberTypes.SHORT_INT:
return new ShortIntRangeIterator();
case NumberTypes.LONG_INT:
return new LongIntRangeIterator();
case NumberTypes.SHORT_FLOAT:
return new ShortFloatRangeIterator();
case NumberTypes.LONG_FLOAT:
return new LongFloatRangeIterator();
case NumberTypes.REAL_DECIMAL:
case NumberTypes.REAL_INT:
throw new UnsupportedOperationException("This range uses a large" +
" number type (REAL or REAL_INT) for which iteration is not" +
" yet supported.");
default:
throw new IllegalStateException(String.format("This range has an" +
" invalid or unsupported number type (0x%x)", nType));
}
}
@DSInaccessible
public DS_Object[] dumpToArray()
{
final List<DS_Object> list = new ArrayList<DS_Object>();
final Iterator<DS_Object> iter = iterator();
while (iter.hasNext())
list.add(iter.next());
return list.toArray(new DS_Object[list.size()]);
}
@DSName("each")
public void each(DS_Callable func) throws DScriptErr
{
final Iterator<DS_Object> iter = iterator();
while (iter.hasNext())
func.call(iter.next());
}
@DSInaccessible
public DS_Object getIndex(DS_Object index)
throws DScriptErr
{
if (index instanceof DS_Scalar)
{
final DS_Scalar s = (DS_Scalar)index;
final int t = nType | s.getNumberType();
final DS_Scalar item;
if (s.signum() >= 0)
item = ScalarFactory.add(
lowerBound, ScalarFactory.mul(step, s, t), t);
else
{
if (s.isIntegral())
{
final DS_Scalar s2 = ScalarFactory.add(realSize(), s, t);
item = ScalarFactory.add(
lowerBound, ScalarFactory.mul(step, s2, t), t);
}
else
item = ScalarFactory.add(
upperBound, ScalarFactory.mul(step, s, t), t);
}
if (!contains(item))
throw new DScriptErr(TYPE_NAME + " index out of bounds: " +
index);
return item;
}
else
throw new DScriptErr(TYPE_NAME + " index must be a Scalar.");
}
@DSInaccessible
public DS_Object setIndex(DS_Object index, DS_Object value)
throws DScriptErr
{
throw new DScriptErr(TYPE_NAME + " objects are immutable.",
DScriptErr.T_IMMUTABLE);
}
@DSInaccessible
public int getNumberType()
{return nType;}
@DSInaccessible
public boolean isIntegral()
{
return nType != NumberTypes.SHORT_FLOAT
&& nType != NumberTypes.LONG_FLOAT
&& nType != NumberTypes.REAL_DECIMAL;
}
@DSInaccessible
public DS_Numeric castTo(int nType)
{
if (nType == this.nType)
return this;
return new DS_Range(upperBound, lowerBound, step, inclusive, nType);
}
// DS_Object Methods
// ----------------------------------------------------
public DS_Range unbox()
{return this;}
@Override public boolean booleanValue()
{return lowerBound != upperBound;}
public String getTypeName()
{return TYPE_NAME;}
@Override protected DS_JavaClass getDeltaScriptClass()
{return DSCLASS;}
@SuppressWarnings("incomplete-switch")
@Override public DS_Object operator(Operator op, DS_Object other)
throws DScriptErr
{
switch (op)
{
case RANGE_STEP:
if (other instanceof DS_Scalar)
{
if (step.signum() > 0)
return new DS_Range(lowerBound, upperBound,(DS_Scalar)other,
inclusive, nType | ((DS_Scalar)other).getNumberType());
else
return new DS_Range(upperBound, lowerBound,(DS_Scalar)other,
inclusive, nType | ((DS_Scalar)other).getNumberType());
}
else
throw new DScriptErr("The \"" + op.str + "\" operator" +
" must be followed by a Scalar.",
DScriptErr.T_INVALID_TYPE,
DScriptErr.T_NOT_NUMBER);
case IN:
return DS_Boolean.box(contains(other));
case UNARY_MINUS:
return new DS_Range(lowerBound, upperBound, step.negate(),
inclusive, nType);
case RANDOM:
return random();
}
return super.operator(op, other);
}
@Override
public String toString()
{
return lowerBound + (inclusive ? " to " : " until ") + upperBound +
(step.intValue()!=1 ? " by "+step : "");
}
public boolean equals(DS_Object other)
{
if (other instanceof DS_Range)
{
if (this == other) return true;
return ((DS_Range)other).lowerBound.equals(lowerBound) &&
((DS_Range)other).upperBound.equals(upperBound) &&
((DS_Range)other).step.equals(step) &&
((DS_Range)other).inclusive == inclusive;
}
else return false;
}
// Range Iterator Subclasses
// ----------------------------------------------------
protected abstract class RangeIterator implements Iterator<DS_Object>
{
protected boolean done;
public boolean hasNext()
{return !done;}
public final void remove()
{throw new UnsupportedOperationException("Unsupported.");}
}
protected class ShortIntRangeIterator extends RangeIterator
{
private final int start, end, step;
private int next;
ShortIntRangeIterator()
{
step = DS_Range.this.step.intValue();
if (step > 0)
{
start = lowerBound.intValue();
end = upperBound.intValue();
}
else
{
start = upperBound.intValue();
end = lowerBound.intValue();
}
next = start;
}
public DS_Scalar next()
{
if (done)
throw new RuntimeException("Range iteration finished.");
DS_Scalar ret = new DS_ShortIntScalar(next);
next += step;
done = checkIfDone();
return ret;
}
private boolean checkIfDone()
{
if (inclusive)
return step>0 ? next > end : next < end;
else
return step>0 ? next >= end : next <= end;
}
}
protected class LongIntRangeIterator extends RangeIterator
{
private final long start, end, step;
private long next;
LongIntRangeIterator()
{
step = DS_Range.this.step.longValue();
if (step > 0)
{
start = lowerBound.longValue();
end = upperBound.longValue();
}
else
{
start = upperBound.longValue();
end = lowerBound.longValue();
}
next = start;
}
public DS_Scalar next()
{
if (done)
throw new RuntimeException("Range iteration finished.");
DS_Scalar ret = new DS_LongIntScalar(next);
next += step;
done = checkIfDone();
return ret;
}
private boolean checkIfDone()
{
if (inclusive)
return step>0 ? next > end : next < end;
else
return step>0 ? next >= end : next <= end;
}
}
// TODO: Add BigInteger iterator support.
protected class ShortFloatRangeIterator extends RangeIterator
{
private final float start, end, step;
private float next;
ShortFloatRangeIterator()
{
step = DS_Range.this.step.floatValue();
if (step > 0)
{
start = lowerBound.floatValue();
end = upperBound.floatValue();
}
else
{
start = upperBound.floatValue();
end = lowerBound.floatValue();
}
next = start;
}
public DS_Scalar next()
{
if (done)
throw new RuntimeException("Range iteration finished.");
DS_Scalar ret = new DS_ShortFloatScalar(next);
next += step;
done = checkIfDone();
return ret;
}
private boolean checkIfDone()
{
if (inclusive)
return step>0 ? next > end : next < end;
else
return step>0 ? next >= end : next <= end;
}
}
protected class LongFloatRangeIterator extends RangeIterator
{
private final double start, end, step;
private double next;
LongFloatRangeIterator()
{
step = DS_Range.this.step.doubleValue();
if (step > 0)
{
start = lowerBound.doubleValue();
end = upperBound.doubleValue();
}
else
{
start = upperBound.doubleValue();
end = lowerBound.doubleValue();
}
next = start;
}
public DS_Scalar next()
{
if (done)
throw new RuntimeException("Range iteration finished.");
DS_Scalar ret = new DS_LongFloatScalar(next);
next += step;
done = checkIfDone();
return ret;
}
private boolean checkIfDone()
{
if (inclusive)
return step>0 ? next > end : next < end;
else
return step>0 ? next >= end : next <= end;
}
}
// TODO: Add BigDecimal iterator support.
}