Implement sorting for Vector (#1349)

This commit is contained in:
Ara Adkins 2020-12-15 14:20:59 +00:00 committed by GitHub
parent de817af655
commit 2c12a18b25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 801 additions and 65 deletions

View File

@ -102,8 +102,3 @@ Decimal.parse : Text -> Decimal | Nothing
Decimal.parse text = Decimal.parse text =
Panic.recover (Double.parseDouble [text]) . catch (_ -> Nothing) Panic.recover (Double.parseDouble [text]) . catch (_ -> Nothing)
## Compare two numbers.
Number.compare_to : Number -> Ordering
Number.compare_to that = if this == that then Ordering.Equal else
if this > that then Ordering.Greater else Ordering.Less

View File

@ -1,5 +1,9 @@
from Base import all from Base import all
from Builtins import Less, Equal, Greater
from Builtins export Less, Equal, Greater
## Types representing the ordering of values. ## Types representing the ordering of values.
These are intended to be returned from the `compare_to` function, that has a These are intended to be returned from the `compare_to` function, that has a
@ -9,8 +13,15 @@ from Base import all
`that`. So, if `this` is greater than `that`, you should return `Greater.` `that`. So, if `this` is greater than `that`, you should return `Greater.`
type Ordering type Ordering
## An ordering where a compared value is less than another. ## An ordering where a compared value is less than another.
type Less Less
## An ordering where a compared value is equal to another. ## An ordering where a compared value is equal to another.
type Equal Equal
## An ordering where a compared value is greater than another. ## An ordering where a compared value is greater than another.
type Greater Greater
## Converts the ordering to the signed notion of ordering based on integers.
to_sign : Integer
to_sign = case this of
Less -> -1
Equal -> 0
Greater -> 1

View File

@ -1,8 +1,6 @@
from Builtins import Array from Builtins import Array
from Base import all from Base import all
import Base.Data.Vector.Sort
## Creates a new vector of the given length, initializing elements using ## Creates a new vector of the given length, initializing elements using
the provided constructor function. the provided constructor function.
@ -30,7 +28,7 @@ new length constructor =
created by: created by:
Vector.fill length=50 item=42 Vector.fill length=50 item=42
fill : Number -> Any -> Vector fill : Number -> Any -> Vector
fill length item = fill length ~item =
arr = Array.new length arr = Array.new length
0.up_to length . each ix-> arr.set_at ix item 0.up_to length . each ix-> arr.set_at ix item
Vector arr Vector arr
@ -407,10 +405,7 @@ type Vector
rest : Vector | Nothing rest : Vector | Nothing
rest = this.tail rest = this.tail
## PRIVATE ## Sort the Vector.
Doesn't work yet.
Sort the Vector.
Arguments: Arguments:
- `on`: A projection from the element type to the value of that element - `on`: A projection from the element type to the value of that element
@ -420,10 +415,26 @@ type Vector
- `order`: The order in which the vector elements are sorted. - `order`: The order in which the vector elements are sorted.
By default, elements are sorted in ascending order, using the comparator By default, elements are sorted in ascending order, using the comparator
`>`. A custom comparator may be passed to the sort function. `compare_to`. A custom comparator may be passed to the sort function.
This implements a stable sort, meaning that items that compare the same This is a stable sort, meaning that items that compare the same will not
will not have their order changed. have their order changed by the sorting process.
The complexities for this sort are:
- *Worst-Case Time:* `O(n * log n)`
- *Best-Case Time:* `O(n)`
- *Average Time:* `O(n * log n)`
- *Worst-Case Space:* `O(n)` additional
? Implementation Note
The sort implementation is based upon an adaptive, iterative mergesort
that requires far fewer than `n * log(n)` comparisons when the vector
is partially sorted. When the vector is randomly ordered, the
performance is equivalent to a standard mergesort.
It takes equal advantage of ascending and descending runs in the array,
making it much simpler to merge two or more sorted arrays: simply
concatenate them and sort.
> Example > Example
Sorting a vector of numbers. Sorting a vector of numbers.
@ -434,7 +445,22 @@ type Vector
[Pair 1 2, Pair -1 8].sort (_.first) (order = Sort_Order.Descending) [Pair 1 2, Pair -1 8].sort (_.first) (order = Sort_Order.Descending)
sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Order -> Vector sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Order -> Vector
sort (on = x -> x) (by = (_.compare_to _)) (order = Sort_Order.Ascending) = sort (on = x -> x) (by = (_.compare_to _)) (order = Sort_Order.Ascending) =
Sort.run this on by order ## Prepare the destination array that will underlie the vector. We do
not want to sort in place on the original vector, as `sort` is not
intended to be mutable.
new_vec_arr = Array.new this.length
this.to_array.copy 0 new_vec_arr 0 this.length
## As we want to account for both custom projections and custom
comparisons we need to construct a comparator for internal use that
does both.
comp_ascending l r = by (on l) (on r)
comp_descending l r = by (on r) (on l)
compare = if order == Sort_Order.Ascending then comp_ascending else comp_descending
new_vec_arr.sort compare
Vector new_vec_arr
## A builder type for Enso vectors. ## A builder type for Enso vectors.

View File

@ -1,15 +0,0 @@
from Base import all
# TODO [AA] Implement vector sorting.
## PRIVATE
Execute the sort.
In the use-cases we anticipate for Enso, we can see `sort` being run over
both partially-sorted data and random data.
TODO [AA] Explain why we chose this sort.
run : Vector -> (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Order -> Vector
run vec _ _ _ = vec

View File

@ -40,5 +40,5 @@ from Base.Data.Text.Extensions export Text
from Base.Error.Extensions export all from Base.Error.Extensions export all
from Base.Meta.Enso_Project export all from Base.Meta.Enso_Project export all
from Base.Polyglot.Java export all from Base.Polyglot.Java export all
from Builtins export all hiding Meta from Builtins export all hiding Meta, Less, Equal, Greater, Ordering

View File

@ -423,7 +423,7 @@ public abstract class MethodResolverNode extends Node {
@CompilerDirectives.TruffleBoundary @CompilerDirectives.TruffleBoundary
Function resolveMethodOnArray(Context context, UnresolvedSymbol symbol) { Function resolveMethodOnArray(Context context, UnresolvedSymbol symbol) {
return symbol.resolveFor( return symbol.resolveFor(
context.getBuiltins().mutable().constructor(), context.getBuiltins().any()); context.getBuiltins().mutable().array(), context.getBuiltins().any());
} }
@CompilerDirectives.TruffleBoundary @CompilerDirectives.TruffleBoundary

View File

@ -0,0 +1,66 @@
package org.enso.interpreter.node.expression.builtin.mutable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.callable.InvokeCallableNode.ArgumentsExecutionMode;
import org.enso.interpreter.node.callable.InvokeCallableNode.DefaultsExecutionMode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.state.data.EmptyMap;
@NodeInfo(
shortName = "sortComparator",
description = "The implementation of the comparator for Array sorting.")
public abstract class ComparatorNode extends Node {
private @Child InvokeCallableNode invokeNode;
public static ComparatorNode build() {
return ComparatorNodeGen.create();
}
ComparatorNode() {
CallArgumentInfo[] callArguments = {new CallArgumentInfo(), new CallArgumentInfo()};
invokeNode =
InvokeCallableNode.build(
callArguments, DefaultsExecutionMode.EXECUTE, ArgumentsExecutionMode.PRE_EXECUTED);
}
abstract int execute(VirtualFrame frame, Object comparator, Object l, Object r);
@Specialization
int execute(
VirtualFrame frame,
Object comparator,
Object l,
Object r,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("ctxRef.get().getBuiltins().ordering().newLess()") Atom less,
@Cached("ctxRef.get().getBuiltins().ordering().newEqual()") Atom equal,
@Cached("ctxRef.get().getBuiltins().ordering().newGreater()") Atom greater) {
Stateful result = invokeNode.execute(comparator, frame, EmptyMap.create(), new Object[] {l, r});
Object atom = result.getValue();
if (atom == less) {
return -1;
} else if (atom == equal) {
return 0;
} else if (atom == greater) {
return 1;
} else {
CompilerDirectives.transferToInterpreter();
var ordering = ctxRef.get().getBuiltins().ordering().ordering();
throw new PanicException(
ctxRef.get().getBuiltins().error().makeTypeError(ordering, result.getValue()), this);
}
}
}

View File

@ -0,0 +1,33 @@
package org.enso.interpreter.node.expression.builtin.mutable;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.Array;
@BuiltinMethod(type = "Array", name = "copy", description = "Copies one array to another.")
public abstract class CopyNode extends Node {
static CopyNode build() {
return CopyNodeGen.create();
}
abstract Object execute(Array _this, long source_index, Array that, long dest_index, long count);
@Specialization
Object doArray(
Array _this,
long source_index,
Array that,
long dest_index,
long count,
@CachedContext(Language.class) ContextReference<Context> ctxRef) {
System.arraycopy(
_this.getItems(), (int) source_index, that.getItems(), (int) dest_index, (int) count);
return ctxRef.get().getBuiltins().nothing().newInstance();
}
}

View File

@ -0,0 +1,123 @@
package org.enso.interpreter.node.expression.builtin.mutable;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import java.util.Arrays;
import java.util.Comparator;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.state.data.EmptyMap;
@BuiltinMethod(type = "Array", name = "sort", description = "Sorts a mutable array in place.")
public abstract class SortNode extends Node {
private @Child ComparatorNode comparatorNode = ComparatorNode.build();
private final BranchProfile resultProfile = BranchProfile.create();
abstract Object execute(VirtualFrame frame, Object _this, Object comparator);
static SortNode build() {
return SortNodeGen.create();
}
@Specialization
Object doSortFunction(
VirtualFrame frame,
Array _this,
Function comparator,
@CachedContext(Language.class) ContextReference<Context> ctxRef) {
Comparator<Object> compare = getComparator(comparator, ctxRef);
return runSort(compare, _this, ctxRef);
}
@Specialization
Object doSortCallable(
VirtualFrame frame,
Array _this,
Object comparator,
@CachedContext(Language.class) ContextReference<Context> ctxRef) {
Comparator<Object> compare = (l, r) -> comparatorNode.execute(frame, comparator, l, r);
return runSort(compare, _this, ctxRef);
}
@Specialization
Object doAtomThis(
VirtualFrame frame,
Atom _this,
Object that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("ctxRef.get().getBuiltins().mutable().array()") AtomConstructor array) {
return ctxRef.get().getBuiltins().nothing().newInstance();
}
Object runSort(Comparator<Object> compare, Array _this, ContextReference<Context> ctxRef) {
doSort(_this.getItems(), compare);
LoopNode.reportLoopCount(this, _this.length());
return ctxRef.get().getBuiltins().nothing().newInstance();
}
@TruffleBoundary
void doSort(Object[] items, Comparator<Object> compare) {
Arrays.sort(items, compare);
}
private SortComparator getComparator(Function comp, ContextReference<Context> ctxRef) {
return new SortComparator(comp, ctxRef, this);
}
private class SortComparator implements Comparator<Object> {
private final Function compFn;
private final ContextReference<Context> ctxRef;
private final Atom less;
private final Atom equal;
private final Atom greater;
private final SortNode outerThis;
SortComparator(Function compFn, ContextReference<Context> ctxRef, SortNode outerThis) {
this.compFn = compFn;
this.ctxRef = ctxRef;
this.less = ctxRef.get().getBuiltins().ordering().newLess();
this.equal = ctxRef.get().getBuiltins().ordering().newEqual();
this.greater = ctxRef.get().getBuiltins().ordering().newGreater();
this.outerThis = outerThis;
}
@Override
public int compare(Object o1, Object o2) {
Object[] args =
Function.ArgumentsHelper.buildArguments(
compFn, null, EmptyMap.create(), new Object[] {o1, o2});
Stateful result = (Stateful) (compFn).getCallTarget().call(args);
Object value = result.getValue();
return convertResult(value);
}
private int convertResult(Object res) {
if (res == less) {
return -1;
} else if (res == equal) {
return 0;
} else if (res == greater) {
return 1;
} else {
resultProfile.enter();
var ordering = ctxRef.get().getBuiltins().ordering().ordering();
throw new PanicException(
ctxRef.get().getBuiltins().error().makeTypeError(ordering, res), outerThis);
}
}
}
}

View File

@ -0,0 +1,71 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Ordering;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(
type = "Big_Integer",
name = "compare_to",
description = "Comparison for big integers.")
public abstract class CompareToNode extends Node {
static CompareToNode build() {
return CompareToNodeGen.create();
}
abstract Atom execute(EnsoBigInteger _this, Object that);
@Specialization
Atom doLong(
EnsoBigInteger _this,
long that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
return ordering.fromJava(BigIntegerOps.compareTo(_this.getValue(), that));
}
@Specialization
Atom doBigInt(
EnsoBigInteger _this,
EnsoBigInteger that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
return ordering.fromJava(BigIntegerOps.compareTo(_this.getValue(), that.getValue()));
}
@Specialization
Atom doDecimal(
EnsoBigInteger _this,
double that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
return ordering.fromJava(BigIntegerOps.compareTo(_this.getValue(), that));
}
@Specialization
Atom doOther(
EnsoBigInteger _this,
Object that,
@CachedContext(Language.class) ContextReference<Context> ctxRef) {
CompilerDirectives.transferToInterpreter();
var number = ctxRef.get().getBuiltins().number().getNumber().newInstance();
var typeError = ctxRef.get().getBuiltins().error().makeTypeError(that, number);
throw new PanicException(typeError, this);
}
Ordering getOrdering(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().ordering();
}
}

View File

@ -0,0 +1,78 @@
package org.enso.interpreter.node.expression.builtin.number.decimal;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Ordering;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Decimal", name = "compare_to", description = "Comparison for decimals.")
public abstract class CompareToNode extends Node {
static CompareToNode build() {
return CompareToNodeGen.create();
}
abstract Atom execute(double _this, Object that);
@Specialization
Atom doLong(
double _this,
long that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
if (_this == that) {
return ordering.newEqual();
} else if (_this > that) {
return ordering.newGreater();
} else {
return ordering.newLess();
}
}
@Specialization
Atom doBigInt(
double _this,
EnsoBigInteger that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
return ordering.fromJava(BigIntegerOps.compareTo(_this, that.getValue()));
}
@Specialization
Atom doDecimal(
double _this,
double that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
if (_this == that) {
return ordering.newEqual();
} else if (_this > that) {
return ordering.newGreater();
} else {
return ordering.newLess();
}
}
@Specialization
Atom doOther(
double _this, Object that, @CachedContext(Language.class) ContextReference<Context> ctxRef) {
CompilerDirectives.transferToInterpreter();
var number = ctxRef.get().getBuiltins().number().getNumber().newInstance();
var typeError = ctxRef.get().getBuiltins().error().makeTypeError(that, number);
throw new PanicException(typeError, this);
}
Ordering getOrdering(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().ordering();
}
}

View File

@ -0,0 +1,81 @@
package org.enso.interpreter.node.expression.builtin.number.smallInteger;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Ordering;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(
type = "Small_Integer",
name = "compare_to",
description = "Comparison for small integers.")
public abstract class CompareToNode extends Node {
static CompareToNode build() {
return CompareToNodeGen.create();
}
abstract Atom execute(long _this, Object that);
@Specialization
Atom doLong(
long _this,
long that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
if (_this == that) {
return ordering.newEqual();
} else if (_this > that) {
return ordering.newGreater();
} else {
return ordering.newLess();
}
}
@Specialization
Atom doBigInt(
long _this,
EnsoBigInteger that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
return ordering.fromJava(BigIntegerOps.compareTo(_this, that.getValue()));
}
@Specialization
Atom doDecimal(
long _this,
double that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getOrdering(ctxRef)") Ordering ordering) {
if (_this == that) {
return ordering.newEqual();
} else if (_this > that) {
return ordering.newGreater();
} else {
return ordering.newLess();
}
}
@Specialization
Atom doOther(
long _this, Object that, @CachedContext(Language.class) ContextReference<Context> ctxRef) {
CompilerDirectives.transferToInterpreter();
var number = ctxRef.get().getBuiltins().number().getNumber().newInstance();
var typeError = ctxRef.get().getBuiltins().error().makeTypeError(that, number);
throw new PanicException(typeError, this);
}
Ordering getOrdering(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().ordering();
}
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.expression.builtin.number.utils;
import com.fasterxml.jackson.databind.node.BigIntegerNode; import com.fasterxml.jackson.databind.node.BigIntegerNode;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
/** Re-exposes big-integer operations behind a truffle boundary. */ /** Re-exposes big-integer operations behind a truffle boundary. */
@ -204,6 +205,31 @@ public class BigIntegerOps {
return BigIntegerOps.compare(a,BigInteger.ZERO) == 0; return BigIntegerOps.compare(a,BigInteger.ZERO) == 0;
} }
@CompilerDirectives.TruffleBoundary
public static int compareTo(long a, BigInteger b) {
return -b.signum();
}
@CompilerDirectives.TruffleBoundary
public static int compareTo(BigInteger a, long b) {
return a.signum();
}
@CompilerDirectives.TruffleBoundary
public static int compareTo(BigInteger a, BigInteger b) {
return a.compareTo(b);
}
@CompilerDirectives.TruffleBoundary
public static int compareTo(BigInteger a, double b) {
return (new BigDecimal(a)).compareTo(BigDecimal.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static int compareTo(double a, BigInteger b) {
return BigDecimal.valueOf(a).compareTo(new BigDecimal(b));
}
@CompilerDirectives.TruffleBoundary @CompilerDirectives.TruffleBoundary
public static boolean fitsInLong(BigInteger bigInteger) { public static boolean fitsInLong(BigInteger bigInteger) {
return bigInteger.compareTo(MIN_LONG_BIGINT) >= 0 && bigInteger.compareTo(MAX_LONG_BIGINT) <= 0; return bigInteger.compareTo(MIN_LONG_BIGINT) >= 0 && bigInteger.compareTo(MAX_LONG_BIGINT) <= 0;

View File

@ -38,22 +38,24 @@ public class Builtins {
} }
} }
private final Module module;
private final ModuleScope scope;
private final AtomConstructor nothing;
private final AtomConstructor any; private final AtomConstructor any;
private final Number number;
private final AtomConstructor function;
private final AtomConstructor debug; private final AtomConstructor debug;
private final AtomConstructor ensoProject; private final AtomConstructor ensoProject;
private final Text text; private final AtomConstructor function;
private final Error error; private final AtomConstructor nothing;
private final Bool bool; private final Bool bool;
private final System system; private final Error error;
private final Meta meta;
private final Module module;
private final ModuleScope scope;
private final Mutable mutable; private final Mutable mutable;
private final Number number;
private final Ordering ordering;
private final Polyglot polyglot; private final Polyglot polyglot;
private final Resource resource; private final Resource resource;
private final Meta meta; private final System system;
private final Text text;
/** /**
* Creates an instance with builtin methods installed. * Creates an instance with builtin methods installed.
@ -62,27 +64,28 @@ public class Builtins {
*/ */
public Builtins(Context context) { public Builtins(Context context) {
Language language = context.getLanguage(); Language language = context.getLanguage();
module = Module.empty(QualifiedName.fromString(MODULE_NAME).get()); module = Module.empty(QualifiedName.fromString(MODULE_NAME).get());
scope = module.compileScope(context); scope = module.compileScope(context);
nothing = new AtomConstructor("Nothing", scope).initializeFields();
any = new AtomConstructor("Any", scope).initializeFields(); any = new AtomConstructor("Any", scope).initializeFields();
bool = new Bool(language, scope); bool = new Bool(language, scope);
error = new Error(language, scope);
mutable = new Mutable(language, scope);
function = new AtomConstructor("Function", scope).initializeFields();
text = new Text(language, scope);
debug = new AtomConstructor("Debug", scope).initializeFields(); debug = new AtomConstructor("Debug", scope).initializeFields();
ensoProject = ensoProject =
new AtomConstructor("Enso_Project", scope) new AtomConstructor("Enso_Project", scope)
.initializeFields( .initializeFields(
new ArgumentDefinition( new ArgumentDefinition(
0, "prim_root_file", ArgumentDefinition.ExecutionMode.EXECUTE)); 0, "prim_root_file", ArgumentDefinition.ExecutionMode.EXECUTE));
system = new System(language, scope); error = new Error(language, scope);
function = new AtomConstructor("Function", scope).initializeFields();
meta = new Meta(language, scope);
mutable = new Mutable(language, scope);
nothing = new AtomConstructor("Nothing", scope).initializeFields();
number = new Number(language, scope); number = new Number(language, scope);
ordering = new Ordering(language, scope);
polyglot = new Polyglot(language, scope); polyglot = new Polyglot(language, scope);
resource = new Resource(language, scope); resource = new Resource(language, scope);
meta = new Meta(language, scope); system = new System(language, scope);
text = new Text(language, scope);
AtomConstructor nil = new AtomConstructor("Nil", scope).initializeFields(); AtomConstructor nil = new AtomConstructor("Nil", scope).initializeFields();
AtomConstructor cons = AtomConstructor cons =
@ -243,6 +246,11 @@ public class Builtins {
return polyglot; return polyglot;
} }
/** @return the container for ordering-related builtins */
public Ordering ordering() {
return ordering;
}
/** /**
* Returns the builtin module scope. * Returns the builtin module scope.
* *

View File

@ -11,6 +11,7 @@ import org.enso.interpreter.runtime.scope.ModuleScope;
/** Container for builtin Error types */ /** Container for builtin Error types */
public class Error { public class Error {
private final AtomConstructor syntaxError; private final AtomConstructor syntaxError;
private final AtomConstructor typeError;
private final AtomConstructor compileError; private final AtomConstructor compileError;
private final AtomConstructor inexhaustivePatternMatchError; private final AtomConstructor inexhaustivePatternMatchError;
private final AtomConstructor uninitializedState; private final AtomConstructor uninitializedState;
@ -34,6 +35,11 @@ public class Error {
new AtomConstructor("Syntax_Error", scope) new AtomConstructor("Syntax_Error", scope)
.initializeFields( .initializeFields(
new ArgumentDefinition(0, "message", ArgumentDefinition.ExecutionMode.EXECUTE)); new ArgumentDefinition(0, "message", ArgumentDefinition.ExecutionMode.EXECUTE));
typeError =
new AtomConstructor("Type_Error", scope)
.initializeFields(
new ArgumentDefinition(0, "expected", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(0, "actual", ArgumentDefinition.ExecutionMode.EXECUTE));
compileError = compileError =
new AtomConstructor("Compile_Error", scope) new AtomConstructor("Compile_Error", scope)
.initializeFields( .initializeFields(
@ -78,6 +84,9 @@ public class Error {
return syntaxError; return syntaxError;
} }
/** @return the builtin {@code Type_Error} atom constructor. */
public AtomConstructor typeError() { return typeError; }
/** @return the builtin {@code Compile_Error} atom constructor. */ /** @return the builtin {@code Compile_Error} atom constructor. */
public AtomConstructor compileError() { public AtomConstructor compileError() {
return compileError; return compileError;
@ -109,6 +118,17 @@ public class Error {
return noSuchMethodError.newInstance(target, symbol); return noSuchMethodError.newInstance(target, symbol);
} }
/**
* Creates an instance of the runtime representation of a {@code Type_Error}.
*
* @param expected the expected type
* @param actual the actual type
* @return a runtime representation of the error.
*/
public Atom makeTypeError(Object expected, Object actual) {
return typeError.newInstance(expected, actual);
}
/** /**
* Creates an instance of the runtime representation of a {@code Polyglot_Error}. * Creates an instance of the runtime representation of a {@code Polyglot_Error}.
* *

View File

@ -8,6 +8,7 @@ import org.enso.interpreter.runtime.scope.ModuleScope;
/** Container for builtin array-related types and functions. */ /** Container for builtin array-related types and functions. */
public class Mutable { public class Mutable {
private final AtomConstructor array; private final AtomConstructor array;
private final AtomConstructor ref;
/** /**
* Creates a new instance of this class, registering all relevant bindings in the provided scope. * Creates a new instance of this class, registering all relevant bindings in the provided scope.
@ -28,8 +29,10 @@ public class Mutable {
scope.registerMethod(array, "to_array", ToArrayMethodGen.makeFunction(language)); scope.registerMethod(array, "to_array", ToArrayMethodGen.makeFunction(language));
scope.registerMethod(array, "at", GetAtMethodGen.makeFunction(language)); scope.registerMethod(array, "at", GetAtMethodGen.makeFunction(language));
scope.registerMethod(array, "set_at", SetAtMethodGen.makeFunction(language)); scope.registerMethod(array, "set_at", SetAtMethodGen.makeFunction(language));
scope.registerMethod(array, "copy", CopyMethodGen.makeFunction(language));
scope.registerMethod(array, "sort", SortMethodGen.makeFunction(language));
AtomConstructor ref = new AtomConstructor("Ref", scope).initializeFields(); ref = new AtomConstructor("Ref", scope).initializeFields();
scope.registerConstructor(ref); scope.registerConstructor(ref);
scope.registerMethod(ref, "new", NewRefMethodGen.makeFunction(language)); scope.registerMethod(ref, "new", NewRefMethodGen.makeFunction(language));
scope.registerMethod(ref, "get", GetRefMethodGen.makeFunction(language)); scope.registerMethod(ref, "get", GetRefMethodGen.makeFunction(language));
@ -37,7 +40,12 @@ public class Mutable {
} }
/** @return the Array constructor. */ /** @return the Array constructor. */
public AtomConstructor constructor() { public AtomConstructor array() {
return array; return array;
} }
/** @return the Ref constructor. */
public AtomConstructor ref() {
return ref;
}
} }

View File

@ -157,6 +157,11 @@ public class Number {
"bit_shift_r", "bit_shift_r",
org.enso.interpreter.node.expression.builtin.number.smallInteger.BitShiftRightMethodGen org.enso.interpreter.node.expression.builtin.number.smallInteger.BitShiftRightMethodGen
.makeFunction(language)); .makeFunction(language));
scope.registerMethod(
smallInteger,
"compare_to",
org.enso.interpreter.node.expression.builtin.number.smallInteger.CompareToMethodGen
.makeFunction(language));
} }
private void registerBigIntegerMethods(Language language, ModuleScope scope) { private void registerBigIntegerMethods(Language language, ModuleScope scope) {
@ -281,6 +286,11 @@ public class Number {
"bit_shift_r", "bit_shift_r",
org.enso.interpreter.node.expression.builtin.number.bigInteger.BitShiftRightMethodGen org.enso.interpreter.node.expression.builtin.number.bigInteger.BitShiftRightMethodGen
.makeFunction(language)); .makeFunction(language));
scope.registerMethod(
bigInteger,
"compare_to",
org.enso.interpreter.node.expression.builtin.number.bigInteger.CompareToMethodGen
.makeFunction(language));
} }
private void registerDecimalMethods(Language language, ModuleScope scope) { private void registerDecimalMethods(Language language, ModuleScope scope) {
@ -360,6 +370,11 @@ public class Number {
"ceil", "ceil",
org.enso.interpreter.node.expression.builtin.number.decimal.CeilMethodGen.makeFunction( org.enso.interpreter.node.expression.builtin.number.decimal.CeilMethodGen.makeFunction(
language)); language));
scope.registerMethod(
decimal,
"compare_to",
org.enso.interpreter.node.expression.builtin.number.decimal.CompareToMethodGen.makeFunction(
language));
} }
/** @return the Int64 atom constructor. */ /** @return the Int64 atom constructor. */

View File

@ -0,0 +1,76 @@
package org.enso.interpreter.runtime.builtin;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A container for builtin ordering types. */
public class Ordering {
private final AtomConstructor ordering;
private final AtomConstructor less;
private final AtomConstructor equal;
private final AtomConstructor greater;
public Ordering(Language language, ModuleScope scope) {
ordering = new AtomConstructor("Ordering", scope).initializeFields();
less = new AtomConstructor("Less", scope).initializeFields();
equal = new AtomConstructor("Equal", scope).initializeFields();
greater = new AtomConstructor("Greater", scope).initializeFields();
scope.registerConstructor(ordering);
scope.registerConstructor(less);
scope.registerConstructor(equal);
scope.registerConstructor(greater);
}
/**
* Convert the java notion of ordering to the Enso notion of ordering.
*
* @param ord the java ordering
* @return the Enso ordering corresponding to {@code ord}
*/
public Atom fromJava(int ord) {
if (ord == 0) {
return newEqual();
} else if (ord > 0) {
return newGreater();
} else {
return newLess();
}
}
/** @return a new instance of Less */
public Atom newLess() {
return less.newInstance();
}
/** @return a new instance of Equal */
public Atom newEqual() {
return equal.newInstance();
}
/** @return a new instance of Greater */
public Atom newGreater() {
return greater.newInstance();
}
/** @return the Ordering constructor. */
public AtomConstructor ordering() {
return ordering;
}
/** @return the Less constructor */
public AtomConstructor less() {
return less;
}
/** @return the Equal constructor */
public AtomConstructor equal() {
return equal;
}
/** @return the Greater constructor */
public AtomConstructor greater() {
return greater;
}
}

View File

@ -59,10 +59,15 @@ public class Array implements TruffleObject {
return items[(int) index]; return items[(int) index];
} }
/** @return the size of this array */
public int length() {
return this.items.length;
}
/** /**
* Exposes the size of this collection through the polyglot API. * Exposes the size of this collection through the polyglot API.
* *
* @return * @return the size of this array
*/ */
@ExportMessage @ExportMessage
long getArraySize() { long getArraySize() {

View File

@ -702,7 +702,7 @@ class IrToTruffle(
runtimeConsOpt.map { atomCons => runtimeConsOpt.map { atomCons =>
val any = context.getBuiltins.any val any = context.getBuiltins.any
val array = context.getBuiltins.mutable.constructor val array = context.getBuiltins.mutable.array
val bool = context.getBuiltins.bool val bool = context.getBuiltins.bool
val number = context.getBuiltins.number val number = context.getBuiltins.number
val polyglot = context.getBuiltins.polyglot.getPolyglot val polyglot = context.getBuiltins.polyglot.getPolyglot

View File

@ -228,7 +228,7 @@ case object SuspendedArguments extends IRPass {
case (arg, typ) => case (arg, typ) =>
arg match { arg match {
case spec: IR.DefinitionArgument.Specified => case spec: IR.DefinitionArgument.Specified =>
if (representsSuspended(typ)) { if (representsSuspended(typ) || spec.suspended) {
spec.copy(suspended = true) spec.copy(suspended = true)
} else spec.copy(suspended = false) } else spec.copy(suspended = false)
} }

View File

@ -8,6 +8,7 @@ import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Locale; import java.util.Locale;
import java.util.Random;
/** Utils for standard library operations on Time. */ /** Utils for standard library operations on Time. */
public class Time_Utils { public class Time_Utils {

View File

@ -0,0 +1,73 @@
from Base import all
import Test.Bench
polyglot java import java.util.Random
polyglot java import org.enso.base.Time_Utils
## Bench Utilities ============================================================
vector_size = 1000000
iter_size = 100
num_iterations = 10
make_sorted_ascending_vec : Integer -> Base.Vector.Vector
make_sorted_ascending_vec n = 0.up_to n+1 . to_vector
make_partially_sorted_vec : Integer -> Base.Vector.Vector
make_partially_sorted_vec n =
random_gen = Random.new [n].to_array
direction = Ref.new Sort_Order.Ascending
last_num = Ref.new 0
run_length = Ref.new 0
Base.Vector.fill n <|
case (Ref.get run_length) == 0 of
True ->
new_direction = if (random_gen.nextDouble []) > 0 then Sort_Order.Ascending else
Sort_Order.Descending
Ref.put direction new_direction
Ref.put run_length (((random_gen.nextLong []) % (n / 10).floor) - 1)
num = random_gen.nextInt []
Ref.put last_num num
num
False ->
change = ((random_gen.nextInt []).abs) % n
num = case Ref.get direction of
Sort_Order.Ascending ->
num = (Ref.get last_num) + change
Ref.put last_num num
num
Sort_Order.Descending ->
num = (Ref.get last_num) - change
Ref.put last_num num
num
Ref.put run_length ((Ref.get run_length) - 1)
num
make_random_vec : Integer -> Base.Vector.Vector
make_random_vec n =
random_gen = Random.new [n].to_array
Base.Vector.fill n (random_gen.nextLong [])
# The Benchmarks ==============================================================
main =
sorted_vec = here.make_sorted_ascending_vec here.vector_size
partially_sorted_vec = here.make_partially_sorted_vec here.vector_size
random_vec = here.make_random_vec here.vector_size
projection = x -> x % 10
comparator = l -> r -> r.compare_to l
Bench.measure (sorted_vec.sort) "Already Sorted" here.iter_size here.num_iterations
Bench.measure (sorted_vec.sort order=Sort_Order.Descending) "Sorted in Opposite Order" here.iter_size here.num_iterations
Bench.measure (partially_sorted_vec.sort) "Sorted Runs Ascending" here.iter_size here.num_iterations
Bench.measure (partially_sorted_vec.sort order=Sort_Order.Descending) "Sorted Runs Descending" here.iter_size here.num_iterations
Bench.measure (random_vec.sort) "Random Elements Ascending" here.iter_size here.num_iterations
Bench.measure (random_vec.sort order=Sort_Order.Descending) "Random Elements Descending" here.iter_size here.num_iterations
Bench.measure (random_vec.sort on=projection) "Sorting with a Custom Projection" here.iter_size here.num_iterations
Bench.measure (random_vec.sort by=comparator) "Sorting with a Custom Comparison" here.iter_size here.num_iterations

View File

@ -14,6 +14,6 @@ spec = describe "Noise" <|
result-result . should_equal 0 result-result . should_equal 0
it "should allow the user to specify the interval" <| it "should allow the user to specify the interval" <|
interval = Interval.inclusive -250 250 interval = Interval.inclusive -250 250
values = 1.up_to 10000 . to_vector . map (_.noise interval) values = 1.up_to 10001 . to_vector . map (_.noise interval)
values.all (v -> (v >= -250) && (v <= 250)) . should_be_true values.all (v -> (v >= -250) && (v <= 250)) . should_be_true

View File

@ -2,6 +2,12 @@ from Base import all
import Test import Test
type Test a b
Test.== that = this.a == that.a
Test.compare_to that = if this == that then Ordering.Equal else
if this.a > that.a then Ordering.Greater else Ordering.Less
spec = describe "Vectors" <| spec = describe "Vectors" <|
it "should allow vector creation with a programmatic constructor" <| it "should allow vector creation with a programmatic constructor" <|
Vector.new 100 (ix -> ix + 1) . fold 0 (+) . should_equal 5050 Vector.new 100 (ix -> ix + 1) . fold 0 (+) . should_equal 5050
@ -115,9 +121,38 @@ spec = describe "Vectors" <|
non_empty_vec.rest . should_equal [2, 3, 4, 5] non_empty_vec.rest . should_equal [2, 3, 4, 5]
singleton_vec.rest . should_equal [] singleton_vec.rest . should_equal []
empty_vec.rest . should_equal Nothing empty_vec.rest . should_equal Nothing
# it "should be able to be sorted" <| it "should be able to be sorted" <|
# it "should have a stable sort" <| empty_vec = []
# it "should be able to use a custom element projection" <| short_vec = [2, 4, 38, -1, -1000, 3671, -32]
# it "should be able to use a custom comparator" <| short_expected = [-1000, -32, -1, 2, 4, 38, 3671]
# it "should be able to sort in ascending and descending order" <| empty_vec.sort . should_equal []
short_vec.sort . should_equal short_expected
it "should leave the original vector unchanged" <|
non_empty_vec = [2, 4, 2, 3, 2, 3]
sorted = non_empty_vec.sort
non_empty_vec . should_equal [2, 4, 2, 3, 2, 3]
sorted . should_equal [2, 2, 2, 3, 3, 4]
it "should have a stable sort" <|
small_vec = [Test 1 8, Test 1 3, Test -20 0, Test -1 1, Test -1 10, Test 4 0]
small_expected = [Test -20 0, Test -1 1, Test -1 10, Test 1 8, Test 1 3, Test 4 0]
small_vec.sort . should_equal small_expected
it "should be able to use a custom element projection" <|
small_vec = [Test 1 8, Test 1 3, Test -20 0, Test -1 1, Test -1 10, Test 4 0]
small_expected = [Test -20 0, Test 4 0, Test -1 1, Test 1 3, Test 1 8, Test -1 10]
small_vec.sort (on = _.b) . should_equal small_expected
it "should be able to use a custom comparator" <|
small_vec = [2, 7, -3, 383, -392, 28, -90]
small_expected = [383, 28, 7, 2, -3, -90, -392]
small_vec.sort (by = l -> r -> r.compare_to l) . should_equal small_expected
it "should be able to use a custom comparator and projection" <|
small_vec = [Test 1 8, Test 1 3, Test -20 0, Test -1 1, Test -1 10, Test 4 0]
small_expected = [Test -1 10, Test 1 8, Test 1 3, Test -1 1, Test -20 0, Test 4 0]
small_vec.sort (on = _.b) (by = l -> r -> r.compare_to l) . should_equal small_expected
it "should be able to sort in descending order" <|
small_vec = [2, 7, -3, 383, -392, 28, -90]
small_expected = [383, 28, 7, 2, -3, -90, -392]
small_vec.sort order=Sort_Order.Descending . should_equal small_expected
it "should be stable in descending order" <|
small_vec = [Test 1 8, Test 1 3, Test -20 0, Test -1 1, Test -1 10, Test 4 0]
small_expected = [Test 4 0, Test 1 3, Test 1 8, Test -1 10, Test -1 1, Test -20 0]
small_vec.sort order=Sort_Order.Descending . should_equal small_expected