diff --git a/src/main/java/leekscript/runner/AI.java b/src/main/java/leekscript/runner/AI.java index 3b538b7f..e69c9246 100644 --- a/src/main/java/leekscript/runner/AI.java +++ b/src/main/java/leekscript/runner/AI.java @@ -2738,6 +2738,9 @@ public ArrayLeekValue range(Object value, Object start, Object end, Object strid var stride = longint(strideObject); return ((ArrayLeekValue) value).arraySlice(this, start, end, stride); } + if (value instanceof IntervalLeekValue) { + return ((IntervalLeekValue) value).range(this, start, end, real(strideObject)); + } addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new Object[] { value }); return null; } @@ -2752,6 +2755,9 @@ public ArrayLeekValue range_start(Object value, Object start, Object strideObjec var stride = longint(strideObject); return array.arraySlice(this, start, null, stride); } + if (value instanceof IntervalLeekValue) { + return ((IntervalLeekValue) value).range(this, start, null, real(strideObject)); + } addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new Object[] { value }); return null; } @@ -2766,6 +2772,9 @@ public ArrayLeekValue range_end(Object value, Object end, Object strideObject) t var stride = longint(strideObject); return array.arraySlice(this, null, end, stride); } + if (value instanceof IntervalLeekValue) { + return ((IntervalLeekValue) value).range(this, null, end, real(strideObject)); + } addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new Object[] { value }); return null; } @@ -2779,6 +2788,9 @@ public ArrayLeekValue range_all(Object value, Object strideObject) throws LeekRu var stride = longint(strideObject); return ((ArrayLeekValue) value).arraySlice(this, null, null, stride); } + if (value instanceof IntervalLeekValue) { + return ((IntervalLeekValue) value).range(this, null, null, real(strideObject)); + } addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new Object[] { value }); return null; } diff --git a/src/main/java/leekscript/runner/LeekFunctions.java b/src/main/java/leekscript/runner/LeekFunctions.java index 8b8eb97b..5d0a1f46 100644 --- a/src/main/java/leekscript/runner/LeekFunctions.java +++ b/src/main/java/leekscript/runner/LeekFunctions.java @@ -239,6 +239,11 @@ public class LeekFunctions { method("intervalUpperBound", "Interval", 1, Type.REAL, new Type[] { Type.INTERVAL }).setMinVersion(4); method("intervalIsEmpty", "Interval", 1, Type.BOOL, new Type[] { Type.INTERVAL }).setMinVersion(4); method("intervalIsBounded", "Interval", 1, Type.BOOL, new Type[] { Type.INTERVAL }).setMinVersion(4); + method("intervalIsRightBounded", "Interval", 1, Type.BOOL, new Type[] { Type.INTERVAL }).setMinVersion(4); + method("intervalIsLeftBounded", "Interval", 1, Type.BOOL, new Type[] { Type.INTERVAL }).setMinVersion(4); + method("intervalMidpoint", "Interval", 3, Type.REAL, new Type[] { Type.INTERVAL }).setMinVersion(4); + method("intervalIntersection", "Interval", 3, Type.INTERVAL, new Type[] { Type.INTERVAL, Type.INTERVAL }).setMinVersion(4); + method("intervalCombine", "Interval", 3, Type.INTERVAL, new Type[] { Type.INTERVAL, Type.INTERVAL }).setMinVersion(4); method("intervalToArray", "Interval", new CallableVersion[] { new CallableVersion(Type.ARRAY_REAL, new Type[] { Type.INTERVAL, Type.REAL }), new CallableVersion(Type.ARRAY_REAL, new Type[] { Type.INTERVAL}), diff --git a/src/main/java/leekscript/runner/values/IntervalLeekValue.java b/src/main/java/leekscript/runner/values/IntervalLeekValue.java index 22fb2c14..6bd050bf 100644 --- a/src/main/java/leekscript/runner/values/IntervalLeekValue.java +++ b/src/main/java/leekscript/runner/values/IntervalLeekValue.java @@ -42,11 +42,11 @@ public String toString(AI ai, Set visited) throws LeekRunException { StringBuilder sb = new StringBuilder("["); - if (isLeftBounded()) { + if (intervalIsLeftBounded(ai)) { sb.append(ai.export(from, visited)); } sb.append(".."); - if (isRightBounded()) { + if (intervalIsRightBounded(ai)) { sb.append(ai.export(to, visited)); } @@ -66,14 +66,14 @@ public boolean intervalIsEmpty(AI ai) { } public boolean intervalIsBounded(AI ai) { - return isLeftBounded() && isRightBounded(); + return intervalIsLeftBounded(ai) && intervalIsRightBounded(ai); } - public boolean isLeftBounded() { + public boolean intervalIsLeftBounded(AI ai) { return from != Double.NEGATIVE_INFINITY; } - public boolean isRightBounded() { + public boolean intervalIsRightBounded(AI ai) { return to != Double.POSITIVE_INFINITY; } @@ -83,6 +83,51 @@ public boolean operatorIn(Object value) throws LeekRunException { return from <= valueAsReal && valueAsReal <= to; } + public double intervalMidpoint(AI ai) throws LeekRunException { + if (intervalIsEmpty(ai)) { + return Double.NaN; + } + + // [a..b] + if (intervalIsBounded(ai)) { + return (from + to) / 2; + } + // [a..] + if (intervalIsLeftBounded(ai)) { + return Double.POSITIVE_INFINITY; + } + // [..b] + if (intervalIsRightBounded(ai)) { + return Double.NEGATIVE_INFINITY; + } + // [..] + return Double.NaN; + } + + public IntervalLeekValue intervalIntersection(AI ai, IntervalLeekValue interval) throws LeekRunException { + if (intervalIsEmpty(ai)) { + return new IntervalLeekValue(ai, from, to); + } + + if (interval.intervalIsEmpty(ai)) { + return new IntervalLeekValue(ai, interval.from, interval.to); + } + + return new IntervalLeekValue(ai, Math.max(from, interval.from), Math.min(to, interval.to)); + } + + public IntervalLeekValue intervalCombine(AI ai, IntervalLeekValue interval) throws LeekRunException { + if (intervalIsEmpty(ai)) { + return new IntervalLeekValue(ai, interval.from, interval.to); + } + + if (interval.intervalIsEmpty(ai)) { + return new IntervalLeekValue(ai, from, to); + } + + return new IntervalLeekValue(ai, Math.min(from, interval.from), Math.max(to, interval.to)); + } + public ArrayLeekValue intervalToArray(AI ai) throws LeekRunException { return intervalToArray(ai, 1); } @@ -93,7 +138,6 @@ public ArrayLeekValue intervalToArray(AI ai, double step) throws LeekRunExceptio return null; } - // Operations are added by the array var array = new ArrayLeekValue(ai); if (step >= 0.0) { @@ -110,4 +154,41 @@ public ArrayLeekValue intervalToArray(AI ai, double step) throws LeekRunExceptio return array; } + + public ArrayLeekValue range(AI ai, Object start, Object end, double step) throws LeekRunException { + if (!intervalIsBounded(ai)) { + ai.addSystemLog(AILog.ERROR, Error.CANNOT_ITERATE_UNBOUNDED_INTERVAL, new Object[] { this }); + return null; + } + + if (intervalIsEmpty(ai)) { + return new ArrayLeekValue(ai); + } + + if (step == 0.0) { + step = 1.0; + } + + int maxSize = (int) ((to - from) / Math.abs(step)) + 1; + + var startAsInteger = start == null ? 0 : ai.integer(start); + var endAsInteger = end == null ? maxSize : ai.integer(end); + + int minIdx = Math.max(0, startAsInteger < 0 ? maxSize + startAsInteger : startAsInteger); + int maxIdx = Math.min(maxSize, endAsInteger < 0 ? maxSize + endAsInteger : endAsInteger); + + var array = new ArrayLeekValue(ai); + + for (var i = minIdx; i < maxIdx; ++i) { + if (step >= 0) { + array.push(ai, from + i * step); + } else { + array.push(ai, to + i * step); + } + } + + ai.ops(array.size() * 2); + + return array; + } } diff --git a/src/test/java/test/TestInterval.java b/src/test/java/test/TestInterval.java index da02420f..cd0ec9c2 100644 --- a/src/test/java/test/TestInterval.java +++ b/src/test/java/test/TestInterval.java @@ -59,6 +59,16 @@ public void run() throws Exception { code_v4_("return intervalIsBounded([..]);").equals("false"); code_v4_("intervalIsBounded([1..2])").ops(3); + section("Interval.intervalIsLeftBounded"); + code_v4_("return intervalIsLeftBounded([1..]);").equals("true"); + code_v4_("return intervalIsLeftBounded([..2]);").equals("false"); + code_v4_("intervalIsLeftBounded([1..2])").ops(3); + + section("Interval.intervalIsRightBounded"); + code_v4_("return intervalIsRightBounded([1..]);").equals("false"); + code_v4_("return intervalIsRightBounded([..2]);").equals("true"); + code_v4_("intervalIsRightBounded([1..2])").ops(3); + section("Interval.in"); code_v4_("return 1 in [1..2];").equals("true"); code_v4_("return 1 in [-1..];").equals("true"); @@ -77,6 +87,38 @@ public void run() throws Exception { section("Interval typing"); code_strict_v4_("Interval i = [0..]; return i instanceof Interval").equals("true"); + section("Interval.intervalMidpoint"); + code_v4_("return intervalMidpoint([1..2]);").equals("1.5"); + code_v4_("return intervalMidpoint([-10..10]);").equals("0.0"); + code_v4_("return intervalMidpoint([1..1]);").equals("1.0"); + code_v4_("return intervalMidpoint([1..0]);").equals("NaN"); + code_v4_("return intervalMidpoint([..1]);").equals("-Infinity"); + code_v4_("return intervalMidpoint([1..]);").equals("Infinity"); + code_v4_("return intervalMidpoint([..]);").equals("NaN"); + code_v4_("intervalMidpoint([1..2])").ops(5); + + section("Interval.intervalIntersection"); + code_v4_("return intervalIntersection([1..2], [1..2]);").equals("[1.0..2.0]"); + code_v4_("return intervalIntersection([1..2], [1..3]);").equals("[1.0..2.0]"); + code_v4_("return intervalIntersection([1..2], [0..1]);").equals("[1.0..1.0]"); + code_v4_("return intervalIntersection([1..2], [-1..0]);").equals("[1.0..0.0]"); + code_v4_("return intervalIntersection([-1..2], [1..]);").equals("[1.0..2.0]"); + code_v4_("return intervalIntersection([1..2], [..1]);").equals("[1.0..1.0]"); + code_v4_("return intervalIntersection([1..2], [..]);").equals("[1.0..2.0]"); + code_v4_("return intervalIntersection([..2], [1..]);").equals("[1.0..2.0]"); + code_v4_("intervalIntersection([1..2], [1..2])").ops(7); + + section("Interval.intervalCombine"); + code_v4_("return intervalCombine([1..2], [1..2]);").equals("[1.0..2.0]"); + code_v4_("return intervalCombine([1..2], [1..3]);").equals("[1.0..3.0]"); + code_v4_("return intervalCombine([1..2], [0..1]);").equals("[0.0..2.0]"); + code_v4_("return intervalCombine([1..2], [-1..0]);").equals("[-1.0..2.0]"); + code_v4_("return intervalCombine([-1..2], [1..]);").equals("[-1.0..]"); + code_v4_("return intervalCombine([1..2], [..1]);").equals("[..2.0]"); + code_v4_("return intervalCombine([1..2], [..]);").equals("[..]"); + code_v4_("return intervalCombine([..2], [1..]);").equals("[..]"); + code_v4_("intervalCombine([1..2], [1..2])").ops(7); + section("Interval.intervalToArray()"); code_v4_("return intervalToArray([1..2]);").equals("[1.0, 2.0]"); code_v4_("return intervalToArray([-2..2]);").equals("[-2.0, -1.0, 0.0, 1.0, 2.0]"); @@ -99,5 +141,17 @@ public void run() throws Exception { code_v4_("return intervalToArray([-10..10], -5);").equals("[10.0, 5.0, 0.0, -5.0, -10.0]"); code_v4_("return intervalToArray([1..1], -7);").equals("[1.0]"); code_v4_("return intervalToArray([1..0], -2);").equals("[]"); + + section("Interval.[start:end:step]"); + code_v4_("return [1..10][2:4:2];").equals("[5.0, 7.0]"); + code_v4_("return [1..3][2:4:2];").equals("[]"); + code_v4_("return [1..3][::2];").equals("[1.0, 3.0]"); + code_v4_("return [1..3][::-1.5];").equals("[3.0, 1.5]"); + code_v4_("return [1..10][2:4:-2];").equals("[6.0, 4.0]"); + code_v4_("return [1..4][::];").equals("[1.0, 2.0, 3.0, 4.0]"); + code_v4_("return [1..4][-1::];").equals("[4.0]"); + code_v4_("return [1..4][:-1:];").equals("[1.0, 2.0, 3.0]"); + code_v4_("return [1..4][2::];").equals("[3.0, 4.0]"); + code_v4_("return [1..4][:2:];").equals("[1.0, 2.0]"); } }