0.3.0: remove vm tests
This commit is contained in:
parent
8aaed43324
commit
9f1121ab2f
@ -1,18 +1,5 @@
|
||||
This contains the automated validation suite for the VM and built-in libraries.
|
||||
This contains the automated validation suite for the CLI.
|
||||
|
||||
* `benchmark/` - Performance tests. These aren't strictly pass/fail, but let us
|
||||
compare performance both against other languages and against previous builds
|
||||
of Wren itself.
|
||||
* `unit/` - tests various aspects of the CLI core APIs.
|
||||
|
||||
* `core/` - Tests for the built in core library, mainly methods on the core
|
||||
classes. If a bug is in `wren_core.c` or `wren_value.c`, it will most likely
|
||||
break one of these tests.
|
||||
|
||||
* `language/` - Tests of the language itself, its grammar and runtime
|
||||
semantics. If a bug is in `wren_compiler.c` or `wren_vm.c`, it will most
|
||||
likely break one of these tests. This includes tests for the syntax for the
|
||||
literal forms of the core classes.
|
||||
|
||||
* `limit/` - Tests for various hardcoded limits. The language doesn't
|
||||
officially *specify* these limits, but the Wren implementation has them.
|
||||
These tests ensure that limit behavior is well-defined and tested.
|
||||
* `*/` - tests various modules and mechanics of the CLI, like module resolution.
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
|
||||
static void arguments(WrenVM* vm)
|
||||
{
|
||||
double result = 0;
|
||||
|
||||
result += wrenGetSlotDouble(vm, 1);
|
||||
result += wrenGetSlotDouble(vm, 2);
|
||||
result += wrenGetSlotDouble(vm, 3);
|
||||
result += wrenGetSlotDouble(vm, 4);
|
||||
|
||||
wrenSetSlotDouble(vm, 0, result);
|
||||
}
|
||||
|
||||
const char* testScript =
|
||||
"class Test {\n"
|
||||
" static method(a, b, c, d) { a + b + c + d }\n"
|
||||
"}\n";
|
||||
|
||||
static void call(WrenVM* vm)
|
||||
{
|
||||
int iterations = (int)wrenGetSlotDouble(vm, 1);
|
||||
|
||||
// Since the VM is not re-entrant, we can't call from within this foreign
|
||||
// method. Instead, make a new VM to run the call test in.
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
WrenVM* otherVM = wrenNewVM(&config);
|
||||
|
||||
wrenInterpret(otherVM, "main", testScript);
|
||||
|
||||
WrenHandle* method = wrenMakeCallHandle(otherVM, "method(_,_,_,_)");
|
||||
|
||||
wrenEnsureSlots(otherVM, 1);
|
||||
wrenGetVariable(otherVM, "main", "Test", 0);
|
||||
WrenHandle* testClass = wrenGetSlotHandle(otherVM, 0);
|
||||
|
||||
double startTime = (double)clock() / CLOCKS_PER_SEC;
|
||||
|
||||
double result = 0;
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
wrenEnsureSlots(otherVM, 5);
|
||||
wrenSetSlotHandle(otherVM, 0, testClass);
|
||||
wrenSetSlotDouble(otherVM, 1, 1.0);
|
||||
wrenSetSlotDouble(otherVM, 2, 2.0);
|
||||
wrenSetSlotDouble(otherVM, 3, 3.0);
|
||||
wrenSetSlotDouble(otherVM, 4, 4.0);
|
||||
|
||||
wrenCall(otherVM, method);
|
||||
|
||||
result += wrenGetSlotDouble(otherVM, 0);
|
||||
}
|
||||
|
||||
double elapsed = (double)clock() / CLOCKS_PER_SEC - startTime;
|
||||
|
||||
wrenReleaseHandle(otherVM, testClass);
|
||||
wrenReleaseHandle(otherVM, method);
|
||||
wrenFreeVM(otherVM);
|
||||
|
||||
if (result == (1.0 + 2.0 + 3.0 + 4.0) * iterations)
|
||||
{
|
||||
wrenSetSlotDouble(vm, 0, elapsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Got the wrong result.
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
WrenForeignMethodFn benchmarkBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Benchmark.arguments(_,_,_,_)") == 0) return arguments;
|
||||
if (strcmp(signature, "static Benchmark.call(_)") == 0) return call;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn benchmarkBindMethod(const char* signature);
|
||||
124
test/api/call.c
124
test/api/call.c
@ -1,124 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "call.h"
|
||||
#include "vm.h"
|
||||
|
||||
void callRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "./test/api/call", "Call", 0);
|
||||
WrenHandle* callClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
WrenHandle* noParams = wrenMakeCallHandle(vm, "noParams");
|
||||
WrenHandle* zero = wrenMakeCallHandle(vm, "zero()");
|
||||
WrenHandle* one = wrenMakeCallHandle(vm, "one(_)");
|
||||
WrenHandle* two = wrenMakeCallHandle(vm, "two(_,_)");
|
||||
WrenHandle* unary = wrenMakeCallHandle(vm, "-");
|
||||
WrenHandle* binary = wrenMakeCallHandle(vm, "-(_)");
|
||||
WrenHandle* subscript = wrenMakeCallHandle(vm, "[_,_]");
|
||||
WrenHandle* subscriptSet = wrenMakeCallHandle(vm, "[_,_]=(_)");
|
||||
|
||||
// Different arity.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, noParams);
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, zero);
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenCall(vm, one);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, two);
|
||||
|
||||
// Operators.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, unary);
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenCall(vm, binary);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, subscript);
|
||||
|
||||
wrenEnsureSlots(vm, 4);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenSetSlotDouble(vm, 3, 3.0);
|
||||
wrenCall(vm, subscriptSet);
|
||||
|
||||
// Returning a value.
|
||||
WrenHandle* getValue = wrenMakeCallHandle(vm, "getValue()");
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, getValue);
|
||||
printf("slots after call: %d\n", wrenGetSlotCount(vm));
|
||||
WrenHandle* value = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
// Different argument types.
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotBool(vm, 1, true);
|
||||
wrenSetSlotBool(vm, 2, false);
|
||||
wrenCall(vm, two);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.2);
|
||||
wrenSetSlotDouble(vm, 2, 3.4);
|
||||
wrenCall(vm, two);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotString(vm, 1, "string");
|
||||
wrenSetSlotString(vm, 2, "another");
|
||||
wrenCall(vm, two);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotNull(vm, 1);
|
||||
wrenSetSlotHandle(vm, 2, value);
|
||||
wrenCall(vm, two);
|
||||
|
||||
// Truncate a string, or allow null bytes.
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotBytes(vm, 1, "string", 3);
|
||||
wrenSetSlotBytes(vm, 2, "b\0y\0t\0e", 7);
|
||||
wrenCall(vm, two);
|
||||
|
||||
// Call ignores with extra temporary slots on stack.
|
||||
wrenEnsureSlots(vm, 10);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
wrenSetSlotDouble(vm, i, i * 0.1);
|
||||
}
|
||||
wrenCall(vm, one);
|
||||
|
||||
wrenReleaseHandle(vm, callClass);
|
||||
wrenReleaseHandle(vm, noParams);
|
||||
wrenReleaseHandle(vm, zero);
|
||||
wrenReleaseHandle(vm, one);
|
||||
wrenReleaseHandle(vm, two);
|
||||
wrenReleaseHandle(vm, getValue);
|
||||
wrenReleaseHandle(vm, value);
|
||||
wrenReleaseHandle(vm, unary);
|
||||
wrenReleaseHandle(vm, binary);
|
||||
wrenReleaseHandle(vm, subscript);
|
||||
wrenReleaseHandle(vm, subscriptSet);
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
void callRunTests(WrenVM* vm);
|
||||
@ -1,57 +0,0 @@
|
||||
class Call {
|
||||
static noParams {
|
||||
System.print("noParams")
|
||||
}
|
||||
|
||||
static zero() {
|
||||
System.print("zero")
|
||||
}
|
||||
|
||||
static one(one) {
|
||||
System.print("one %(one)")
|
||||
}
|
||||
|
||||
static two(one, two) {
|
||||
// Don't print null bytes.
|
||||
if (two is String && two.bytes.contains(0)) {
|
||||
two = two.bytes.toList
|
||||
}
|
||||
|
||||
System.print("two %(one) %(two)")
|
||||
}
|
||||
|
||||
static getValue() { ["a", "b"] }
|
||||
|
||||
static - {
|
||||
System.print("unary")
|
||||
}
|
||||
|
||||
static -(arg) {
|
||||
System.print("binary %(arg)")
|
||||
}
|
||||
|
||||
static [one, two] {
|
||||
System.print("subscript %(one) %(two)")
|
||||
}
|
||||
|
||||
static [one, two]=(three) {
|
||||
System.print("subscript set %(one) %(two) %(three)")
|
||||
}
|
||||
}
|
||||
|
||||
// expect: noParams
|
||||
// expect: zero
|
||||
// expect: one 1
|
||||
// expect: two 1 2
|
||||
// expect: unary
|
||||
// expect: binary 1
|
||||
// expect: subscript 1 2
|
||||
// expect: subscript set 1 2 3
|
||||
|
||||
// expect: slots after call: 1
|
||||
// expect: two true false
|
||||
// expect: two 1.2 3.4
|
||||
// expect: two string another
|
||||
// expect: two null [a, b]
|
||||
// expect: two str [98, 0, 121, 0, 116, 0, 101]
|
||||
// expect: one 0.1
|
||||
@ -1,44 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
static void api(WrenVM *vm) {
|
||||
// Grow the slot array. This should trigger the stack to be moved.
|
||||
wrenEnsureSlots(vm, 10);
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
wrenSetSlotDouble(vm, i, i);
|
||||
wrenInsertInList(vm, 0, -1, i);
|
||||
}
|
||||
}
|
||||
|
||||
WrenForeignMethodFn callCallsForeignBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static CallCallsForeign.api()") == 0) return api;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void callCallsForeignRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "./test/api/call_calls_foreign", "CallCallsForeign", 0);
|
||||
WrenHandle* apiClass = wrenGetSlotHandle(vm, 0);
|
||||
WrenHandle *call = wrenMakeCallHandle(vm, "call(_)");
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotHandle(vm, 0, apiClass);
|
||||
wrenSetSlotString(vm, 1, "parameter");
|
||||
|
||||
printf("slots before %d\n", wrenGetSlotCount(vm));
|
||||
wrenCall(vm, call);
|
||||
|
||||
// We should have a single slot count for the return.
|
||||
printf("slots after %d\n", wrenGetSlotCount(vm));
|
||||
|
||||
wrenReleaseHandle(vm, call);
|
||||
wrenReleaseHandle(vm, apiClass);
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn callCallsForeignBindMethod(const char* signature);
|
||||
void callCallsForeignRunTests(WrenVM* vm);
|
||||
@ -1,17 +0,0 @@
|
||||
// Regression test for https://github.com/munificent/wren/issues/510.
|
||||
//
|
||||
// Tests that re-entrant API calls are handled correctly. The host uses
|
||||
// `wrenCall()` to invoke `CallCallsForeign.call()`. That in turn calls
|
||||
// `CallCallsForeign.api()`, which goes back through the API.
|
||||
class CallCallsForeign {
|
||||
foreign static api()
|
||||
|
||||
static call(param) {
|
||||
System.print(api())
|
||||
// expect: slots before 2
|
||||
// expect: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
System.print(param) // expect: parameter
|
||||
// expect: slots after 1
|
||||
return "result"
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
#include "vm.h"
|
||||
|
||||
void callWrenCallRootRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "./test/api/call_wren_call_root", "Test", 0);
|
||||
WrenHandle* testClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
WrenHandle* run = wrenMakeCallHandle(vm, "run()");
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
WrenInterpretResult result = wrenCall(vm, run);
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
{
|
||||
setExitCode(70);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Missing runtime error.\n");
|
||||
}
|
||||
|
||||
wrenReleaseHandle(vm, testClass);
|
||||
wrenReleaseHandle(vm, run);
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
void callWrenCallRootRunTests(WrenVM* vm);
|
||||
@ -1,12 +0,0 @@
|
||||
class Test {
|
||||
static run() {
|
||||
var root = Fiber.current
|
||||
System.print("begin root") // expect: begin root
|
||||
|
||||
Fiber.new {
|
||||
System.print("in new fiber") // expect: in new fiber
|
||||
root.call() // expect runtime error: Cannot call root fiber.
|
||||
System.print("called root")
|
||||
}.transfer()
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
static void runtimeError(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotString(vm, 0, "Error!");
|
||||
wrenAbortFiber(vm, 0);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn errorBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Error.runtimeError") == 0) return runtimeError;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn errorBindMethod(const char* signature);
|
||||
@ -1,12 +0,0 @@
|
||||
class Error {
|
||||
foreign static runtimeError
|
||||
}
|
||||
|
||||
var fiber = Fiber.new {
|
||||
Error.runtimeError
|
||||
}
|
||||
|
||||
var error = fiber.try()
|
||||
System.print(error) // expect: Error!
|
||||
System.print(fiber.isDone) // expect: true
|
||||
System.print(fiber.error) // expect: Error!
|
||||
@ -1,130 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "foreign_class.h"
|
||||
|
||||
static int finalized = 0;
|
||||
|
||||
static void apiFinalized(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotDouble(vm, 0, finalized);
|
||||
}
|
||||
|
||||
static void counterAllocate(WrenVM* vm)
|
||||
{
|
||||
double* value = (double*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(double));
|
||||
*value = 0;
|
||||
}
|
||||
|
||||
static void counterIncrement(WrenVM* vm)
|
||||
{
|
||||
double* value = (double*)wrenGetSlotForeign(vm, 0);
|
||||
double increment = wrenGetSlotDouble(vm, 1);
|
||||
|
||||
*value += increment;
|
||||
}
|
||||
|
||||
static void counterValue(WrenVM* vm)
|
||||
{
|
||||
double value = *(double*)wrenGetSlotForeign(vm, 0);
|
||||
wrenSetSlotDouble(vm, 0, value);
|
||||
}
|
||||
|
||||
static void pointAllocate(WrenVM* vm)
|
||||
{
|
||||
double* coordinates = (double*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(double[3]));
|
||||
|
||||
// This gets called by both constructors, so sniff the slot count to see
|
||||
// which one was invoked.
|
||||
if (wrenGetSlotCount(vm) == 1)
|
||||
{
|
||||
coordinates[0] = 0.0;
|
||||
coordinates[1] = 0.0;
|
||||
coordinates[2] = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
coordinates[0] = wrenGetSlotDouble(vm, 1);
|
||||
coordinates[1] = wrenGetSlotDouble(vm, 2);
|
||||
coordinates[2] = wrenGetSlotDouble(vm, 3);
|
||||
}
|
||||
}
|
||||
|
||||
static void pointTranslate(WrenVM* vm)
|
||||
{
|
||||
double* coordinates = (double*)wrenGetSlotForeign(vm, 0);
|
||||
coordinates[0] += wrenGetSlotDouble(vm, 1);
|
||||
coordinates[1] += wrenGetSlotDouble(vm, 2);
|
||||
coordinates[2] += wrenGetSlotDouble(vm, 3);
|
||||
}
|
||||
|
||||
static void pointToString(WrenVM* vm)
|
||||
{
|
||||
double* coordinates = (double*)wrenGetSlotForeign(vm, 0);
|
||||
char result[100];
|
||||
sprintf(result, "(%g, %g, %g)",
|
||||
coordinates[0], coordinates[1], coordinates[2]);
|
||||
wrenSetSlotString(vm, 0, result);
|
||||
}
|
||||
|
||||
static void resourceAllocate(WrenVM* vm)
|
||||
{
|
||||
int* value = (int*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(int));
|
||||
*value = 123;
|
||||
}
|
||||
|
||||
static void resourceFinalize(void* data)
|
||||
{
|
||||
// Make sure we get the right data back.
|
||||
int* value = (int*)data;
|
||||
if (*value != 123) exit(1);
|
||||
|
||||
finalized++;
|
||||
}
|
||||
|
||||
static void badClassAllocate(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotString(vm, 0, "Something went wrong");
|
||||
wrenAbortFiber(vm, 0);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn foreignClassBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static ForeignClass.finalized") == 0) return apiFinalized;
|
||||
if (strcmp(signature, "Counter.increment(_)") == 0) return counterIncrement;
|
||||
if (strcmp(signature, "Counter.value") == 0) return counterValue;
|
||||
if (strcmp(signature, "Point.translate(_,_,_)") == 0) return pointTranslate;
|
||||
if (strcmp(signature, "Point.toString") == 0) return pointToString;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void foreignClassBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods)
|
||||
{
|
||||
if (strcmp(className, "Counter") == 0)
|
||||
{
|
||||
methods->allocate = counterAllocate;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(className, "Point") == 0)
|
||||
{
|
||||
methods->allocate = pointAllocate;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(className, "Resource") == 0)
|
||||
{
|
||||
methods->allocate = resourceAllocate;
|
||||
methods->finalize = resourceFinalize;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(className, "BadClass") == 0)
|
||||
{
|
||||
methods->allocate = badClassAllocate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn foreignClassBindMethod(const char* signature);
|
||||
void foreignClassBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods);
|
||||
@ -1,87 +0,0 @@
|
||||
class ForeignClass {
|
||||
foreign static finalized
|
||||
}
|
||||
|
||||
// Class with a default constructor.
|
||||
foreign class Counter {
|
||||
construct new() {}
|
||||
foreign increment(amount)
|
||||
foreign value
|
||||
}
|
||||
|
||||
var counter = Counter.new()
|
||||
System.print(counter.value) // expect: 0
|
||||
counter.increment(3.1)
|
||||
System.print(counter.value) // expect: 3.1
|
||||
counter.increment(1.2)
|
||||
System.print(counter.value) // expect: 4.3
|
||||
|
||||
// Foreign classes can inherit a class as long as it has no fields.
|
||||
class PointBase {
|
||||
inherited() {
|
||||
System.print("inherited method")
|
||||
}
|
||||
}
|
||||
|
||||
// Class with non-default constructor.
|
||||
foreign class Point is PointBase {
|
||||
construct new() {
|
||||
System.print("default")
|
||||
}
|
||||
|
||||
construct new(x, y, z) {
|
||||
System.print("%(x), %(y), %(z)")
|
||||
}
|
||||
|
||||
foreign translate(x, y, z)
|
||||
foreign toString
|
||||
}
|
||||
|
||||
var p = Point.new(1, 2, 3) // expect: 1, 2, 3
|
||||
System.print(p) // expect: (1, 2, 3)
|
||||
p.translate(3, 4, 5)
|
||||
System.print(p) // expect: (4, 6, 8)
|
||||
|
||||
p = Point.new() // expect: default
|
||||
System.print(p) // expect: (0, 0, 0)
|
||||
|
||||
p.inherited() // expect: inherited method
|
||||
|
||||
var error = Fiber.new {
|
||||
class Subclass is Point {}
|
||||
}.try()
|
||||
System.print(error) // expect: Class 'Subclass' cannot inherit from foreign class 'Point'.
|
||||
|
||||
// Class with a finalizer.
|
||||
foreign class Resource {
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
var resources = [
|
||||
Resource.new(),
|
||||
Resource.new(),
|
||||
Resource.new()
|
||||
]
|
||||
|
||||
System.gc()
|
||||
System.print(ForeignClass.finalized) // expect: 0
|
||||
|
||||
resources.removeAt(-1)
|
||||
|
||||
System.gc()
|
||||
System.print(ForeignClass.finalized) // expect: 1
|
||||
|
||||
resources.clear()
|
||||
|
||||
System.gc()
|
||||
System.print(ForeignClass.finalized) // expect: 3
|
||||
|
||||
// Class that aborts fiber
|
||||
foreign class BadClass {
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
error = Fiber.new {
|
||||
BadClass.new()
|
||||
}.try()
|
||||
System.print(error) // expect: Something went wrong
|
||||
@ -1,44 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "get_variable.h"
|
||||
|
||||
static void beforeDefined(WrenVM* vm)
|
||||
{
|
||||
wrenGetVariable(vm, "./test/api/get_variable", "A", 0);
|
||||
}
|
||||
|
||||
static void afterDefined(WrenVM* vm)
|
||||
{
|
||||
wrenGetVariable(vm, "./test/api/get_variable", "A", 0);
|
||||
}
|
||||
|
||||
static void afterAssigned(WrenVM* vm)
|
||||
{
|
||||
wrenGetVariable(vm, "./test/api/get_variable", "A", 0);
|
||||
}
|
||||
|
||||
static void otherSlot(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenGetVariable(vm, "./test/api/get_variable", "B", 2);
|
||||
|
||||
// Move it into return position.
|
||||
const char* string = wrenGetSlotString(vm, 2);
|
||||
wrenSetSlotString(vm, 0, string);
|
||||
}
|
||||
|
||||
static void otherModule(WrenVM* vm)
|
||||
{
|
||||
wrenGetVariable(vm, "./test/api/get_variable_module", "Variable", 0);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn getVariableBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static GetVariable.beforeDefined()") == 0) return beforeDefined;
|
||||
if (strcmp(signature, "static GetVariable.afterDefined()") == 0) return afterDefined;
|
||||
if (strcmp(signature, "static GetVariable.afterAssigned()") == 0) return afterAssigned;
|
||||
if (strcmp(signature, "static GetVariable.otherSlot()") == 0) return otherSlot;
|
||||
if (strcmp(signature, "static GetVariable.otherModule()") == 0) return otherModule;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn getVariableBindMethod(const char* signature);
|
||||
@ -1,24 +0,0 @@
|
||||
import "./get_variable_module"
|
||||
|
||||
class GetVariable {
|
||||
foreign static beforeDefined()
|
||||
foreign static afterDefined()
|
||||
foreign static afterAssigned()
|
||||
foreign static otherSlot()
|
||||
foreign static otherModule()
|
||||
}
|
||||
|
||||
System.print(GetVariable.beforeDefined()) // expect: null
|
||||
|
||||
var A = "a"
|
||||
|
||||
System.print(GetVariable.afterDefined()) // expect: a
|
||||
|
||||
A = "changed"
|
||||
|
||||
System.print(GetVariable.afterAssigned()) // expect: changed
|
||||
|
||||
var B = "b"
|
||||
System.print(GetVariable.otherSlot()) // expect: b
|
||||
|
||||
System.print(GetVariable.otherModule()) // expect: value
|
||||
@ -1,3 +0,0 @@
|
||||
// nontest
|
||||
|
||||
var Variable = "value"
|
||||
@ -1,24 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "handle.h"
|
||||
|
||||
static WrenHandle* handle;
|
||||
|
||||
static void setValue(WrenVM* vm)
|
||||
{
|
||||
handle = wrenGetSlotHandle(vm, 1);
|
||||
}
|
||||
|
||||
static void getValue(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotHandle(vm, 0, handle);
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn handleBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Handle.value=(_)") == 0) return setValue;
|
||||
if (strcmp(signature, "static Handle.value") == 0) return getValue;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn handleBindMethod(const char* signature);
|
||||
@ -1,11 +0,0 @@
|
||||
class Handle {
|
||||
foreign static value=(value)
|
||||
foreign static value
|
||||
}
|
||||
|
||||
Handle.value = ["list", "of", "strings"]
|
||||
|
||||
// Make sure the handle lives through a GC.
|
||||
System.gc()
|
||||
|
||||
System.print(Handle.value) // expect: [list, of, strings]
|
||||
@ -1,46 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "lists.h"
|
||||
|
||||
static void newList(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
}
|
||||
|
||||
// Helper function to store a double in a slot then insert it into the list at
|
||||
// slot zero.
|
||||
static void insertNumber(WrenVM* vm, int index, double value)
|
||||
{
|
||||
wrenSetSlotDouble(vm, 1, value);
|
||||
wrenInsertInList(vm, 0, index, 1);
|
||||
}
|
||||
|
||||
static void insert(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
|
||||
// Appending.
|
||||
insertNumber(vm, 0, 1.0);
|
||||
insertNumber(vm, 1, 2.0);
|
||||
insertNumber(vm, 2, 3.0);
|
||||
|
||||
// Inserting.
|
||||
insertNumber(vm, 0, 4.0);
|
||||
insertNumber(vm, 1, 5.0);
|
||||
insertNumber(vm, 2, 6.0);
|
||||
|
||||
// Negative indexes.
|
||||
insertNumber(vm, -1, 7.0);
|
||||
insertNumber(vm, -2, 8.0);
|
||||
insertNumber(vm, -3, 9.0);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn listsBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Lists.newList()") == 0) return newList;
|
||||
if (strcmp(signature, "static Lists.insert()") == 0) return insert;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn listsBindMethod(const char* signature);
|
||||
@ -1,10 +0,0 @@
|
||||
class Lists {
|
||||
foreign static newList()
|
||||
foreign static insert()
|
||||
}
|
||||
|
||||
var list = Lists.newList()
|
||||
System.print(list is List) // expect: true
|
||||
System.print(list.count) // expect: 0
|
||||
|
||||
System.print(Lists.insert()) // expect: [4, 5, 6, 1, 2, 3, 9, 8, 7]
|
||||
139
test/api/main.c
139
test/api/main.c
@ -1,139 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vm.h"
|
||||
#include "wren.h"
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "call.h"
|
||||
#include "call_calls_foreign.h"
|
||||
#include "call_wren_call_root.h"
|
||||
#include "error.h"
|
||||
#include "get_variable.h"
|
||||
#include "foreign_class.h"
|
||||
#include "handle.h"
|
||||
#include "lists.h"
|
||||
#include "new_vm.h"
|
||||
#include "reset_stack_after_call_abort.h"
|
||||
#include "reset_stack_after_foreign_construct.h"
|
||||
#include "resolution.h"
|
||||
#include "slots.h"
|
||||
#include "user_data.h"
|
||||
|
||||
// The name of the currently executing API test.
|
||||
const char* testName;
|
||||
|
||||
static WrenForeignMethodFn bindForeignMethod(
|
||||
WrenVM* vm, const char* module, const char* className,
|
||||
bool isStatic, const char* signature)
|
||||
{
|
||||
if (strncmp(module, "./test/", 7) != 0) return NULL;
|
||||
|
||||
// For convenience, concatenate all of the method qualifiers into a single
|
||||
// signature string.
|
||||
char fullName[256];
|
||||
fullName[0] = '\0';
|
||||
if (isStatic) strcat(fullName, "static ");
|
||||
strcat(fullName, className);
|
||||
strcat(fullName, ".");
|
||||
strcat(fullName, signature);
|
||||
|
||||
WrenForeignMethodFn method = NULL;
|
||||
|
||||
method = benchmarkBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = callCallsForeignBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = errorBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = getVariableBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = foreignClassBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = handleBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = listsBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = newVMBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = resolutionBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = slotsBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = userDataBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
fprintf(stderr,
|
||||
"Unknown foreign method '%s' for test '%s'\n", fullName, testName);
|
||||
exit(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static WrenForeignClassMethods bindForeignClass(
|
||||
WrenVM* vm, const char* module, const char* className)
|
||||
{
|
||||
WrenForeignClassMethods methods = { NULL, NULL };
|
||||
if (strncmp(module, "./test/", 7) != 0) return methods;
|
||||
|
||||
foreignClassBindClass(className, &methods);
|
||||
if (methods.allocate != NULL) return methods;
|
||||
|
||||
resetStackAfterForeignConstructBindClass(className, &methods);
|
||||
if (methods.allocate != NULL) return methods;
|
||||
|
||||
slotsBindClass(className, &methods);
|
||||
if (methods.allocate != NULL) return methods;
|
||||
|
||||
fprintf(stderr,
|
||||
"Unknown foreign class '%s' for test '%s'\n", className, testName);
|
||||
exit(1);
|
||||
return methods;
|
||||
}
|
||||
|
||||
static void afterLoad(WrenVM* vm)
|
||||
{
|
||||
if (strstr(testName, "/call.wren") != NULL)
|
||||
{
|
||||
callRunTests(vm);
|
||||
}
|
||||
else if (strstr(testName, "/call_calls_foreign.wren") != NULL)
|
||||
{
|
||||
callCallsForeignRunTests(vm);
|
||||
}
|
||||
else if (strstr(testName, "/call_wren_call_root.wren") != NULL)
|
||||
{
|
||||
callWrenCallRootRunTests(vm);
|
||||
}
|
||||
else if (strstr(testName, "/reset_stack_after_call_abort.wren") != NULL)
|
||||
{
|
||||
resetStackAfterCallAbortRunTests(vm);
|
||||
}
|
||||
else if (strstr(testName, "/reset_stack_after_foreign_construct.wren") != NULL)
|
||||
{
|
||||
resetStackAfterForeignConstructRunTests(vm);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: wren <test>\n");
|
||||
return 64; // EX_USAGE.
|
||||
}
|
||||
|
||||
testName = argv[1];
|
||||
setTestCallbacks(bindForeignMethod, bindForeignClass, afterLoad);
|
||||
runFile(testName);
|
||||
return getExitCode();
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "new_vm.h"
|
||||
|
||||
static void nullConfig(WrenVM* vm)
|
||||
{
|
||||
WrenVM* otherVM = wrenNewVM(NULL);
|
||||
|
||||
// We should be able to execute code.
|
||||
WrenInterpretResult result = wrenInterpret(otherVM, "main", "1 + 2");
|
||||
wrenSetSlotBool(vm, 0, result == WREN_RESULT_SUCCESS);
|
||||
|
||||
wrenFreeVM(otherVM);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn newVMBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static VM.nullConfig()") == 0) return nullConfig;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn newVMBindMethod(const char* signature);
|
||||
@ -1,6 +0,0 @@
|
||||
class VM {
|
||||
foreign static nullConfig()
|
||||
}
|
||||
// TODO: Other configuration settings.
|
||||
|
||||
System.print(VM.nullConfig()) // expect: true
|
||||
@ -1,28 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
void resetStackAfterCallAbortRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "./test/api/reset_stack_after_call_abort", "Test", 0);
|
||||
WrenHandle* testClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
WrenHandle* abortFiber = wrenMakeCallHandle(vm, "abortFiber()");
|
||||
WrenHandle* afterAbort = wrenMakeCallHandle(vm, "afterAbort(_,_)");
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
wrenCall(vm, abortFiber);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, afterAbort);
|
||||
|
||||
wrenReleaseHandle(vm, testClass);
|
||||
wrenReleaseHandle(vm, abortFiber);
|
||||
wrenReleaseHandle(vm, afterAbort);
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
void resetStackAfterCallAbortRunTests(WrenVM* vm);
|
||||
@ -1,14 +0,0 @@
|
||||
// Regression test.
|
||||
//
|
||||
// If you invoked some code with wrenCall() and that code aborted the current
|
||||
// fiber, it did not reset the API stack. If you tried to immediately reuse the
|
||||
// API stack by calling wrenCall(), it would be in a broken state.
|
||||
class Test {
|
||||
static abortFiber() {
|
||||
Fiber.abort("Abort!") // expect handled runtime error: Abort!
|
||||
}
|
||||
|
||||
static afterAbort(a, b) {
|
||||
System.print(a + b) // expect: 3
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
static void counterAllocate(WrenVM* vm)
|
||||
{
|
||||
double* counter = (double*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(double));
|
||||
*counter = wrenGetSlotDouble(vm, 1);
|
||||
}
|
||||
|
||||
void resetStackAfterForeignConstructBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods)
|
||||
{
|
||||
if (strcmp(className, "ResetStackForeign") == 0)
|
||||
{
|
||||
methods->allocate = counterAllocate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void resetStackAfterForeignConstructRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm,
|
||||
"./test/api/reset_stack_after_foreign_construct", "Test", 0);
|
||||
WrenHandle* testClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
WrenHandle* callConstruct = wrenMakeCallHandle(vm, "callConstruct()");
|
||||
WrenHandle* afterConstruct = wrenMakeCallHandle(vm, "afterConstruct(_,_)");
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
wrenCall(vm, callConstruct);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotHandle(vm, 0, testClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, afterConstruct);
|
||||
|
||||
wrenReleaseHandle(vm, testClass);
|
||||
wrenReleaseHandle(vm, callConstruct);
|
||||
wrenReleaseHandle(vm, afterConstruct);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
void resetStackAfterForeignConstructBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods);
|
||||
void resetStackAfterForeignConstructRunTests(WrenVM* vm);
|
||||
@ -1,18 +0,0 @@
|
||||
// Regression test.
|
||||
//
|
||||
// After a foreign constructor was called, it did not reset the API stack. If
|
||||
// you tried to immediately reuse the API stack by calling wrenCall(), it
|
||||
// would be in a broken state.
|
||||
foreign class ResetStackForeign {
|
||||
construct new(a) {}
|
||||
}
|
||||
|
||||
class Test {
|
||||
static callConstruct() {
|
||||
ResetStackForeign.new(1)
|
||||
}
|
||||
|
||||
static afterConstruct(a, b) {
|
||||
System.print(a + b) // expect: 3
|
||||
}
|
||||
}
|
||||
@ -1,149 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "resolution.h"
|
||||
|
||||
static void write(WrenVM* vm, const char* text)
|
||||
{
|
||||
printf("%s", text);
|
||||
}
|
||||
|
||||
static void reportError(WrenVM* vm, WrenErrorType type,
|
||||
const char* module, int line, const char* message)
|
||||
{
|
||||
if (type == WREN_ERROR_RUNTIME) printf("%s\n", message);
|
||||
}
|
||||
|
||||
static char* loadModule(WrenVM* vm, const char* module)
|
||||
{
|
||||
printf("loading %s\n", module);
|
||||
|
||||
const char* source;
|
||||
if (strcmp(module, "main/baz/bang") == 0)
|
||||
{
|
||||
source = "import \"foo|bar\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
source = "System.print(\"ok\")";
|
||||
}
|
||||
|
||||
char* string = (char*)malloc(strlen(source) + 1);
|
||||
strcpy(string, source);
|
||||
return string;
|
||||
}
|
||||
|
||||
static void runTestVM(WrenVM* vm, WrenConfiguration* configuration,
|
||||
const char* source)
|
||||
{
|
||||
configuration->writeFn = write;
|
||||
configuration->errorFn = reportError;
|
||||
configuration->loadModuleFn = loadModule;
|
||||
|
||||
WrenVM* otherVM = wrenNewVM(configuration);
|
||||
|
||||
// We should be able to execute code.
|
||||
WrenInterpretResult result = wrenInterpret(otherVM, "main", source);
|
||||
if (result != WREN_RESULT_SUCCESS)
|
||||
{
|
||||
wrenSetSlotString(vm, 0, "error");
|
||||
}
|
||||
else
|
||||
{
|
||||
wrenSetSlotString(vm, 0, "success");
|
||||
}
|
||||
|
||||
wrenFreeVM(otherVM);
|
||||
}
|
||||
|
||||
static void noResolver(WrenVM* vm)
|
||||
{
|
||||
WrenConfiguration configuration;
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
// Should default to no resolution function.
|
||||
if (configuration.resolveModuleFn != NULL)
|
||||
{
|
||||
wrenSetSlotString(vm, 0, "Did not have null resolve function.");
|
||||
return;
|
||||
}
|
||||
|
||||
runTestVM(vm, &configuration, "import \"foo/bar\"");
|
||||
}
|
||||
|
||||
static const char* resolveToNull(WrenVM* vm, const char* importer,
|
||||
const char* name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void returnsNull(WrenVM* vm)
|
||||
{
|
||||
WrenConfiguration configuration;
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
configuration.resolveModuleFn = resolveToNull;
|
||||
runTestVM(vm, &configuration, "import \"foo/bar\"");
|
||||
}
|
||||
|
||||
static const char* resolveChange(WrenVM* vm, const char* importer,
|
||||
const char* name)
|
||||
{
|
||||
// Concatenate importer and name.
|
||||
size_t length = strlen(importer) + 1 + strlen(name) + 1;
|
||||
char* result = (char*)malloc(length);
|
||||
strcpy(result, importer);
|
||||
strcat(result, "/");
|
||||
strcat(result, name);
|
||||
|
||||
// Replace "|" with "/".
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
if (result[i] == '|') result[i] = '/';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void changesString(WrenVM* vm)
|
||||
{
|
||||
WrenConfiguration configuration;
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
configuration.resolveModuleFn = resolveChange;
|
||||
runTestVM(vm, &configuration, "import \"foo|bar\"");
|
||||
}
|
||||
|
||||
static void shared(WrenVM* vm)
|
||||
{
|
||||
WrenConfiguration configuration;
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
configuration.resolveModuleFn = resolveChange;
|
||||
runTestVM(vm, &configuration, "import \"foo|bar\"\nimport \"foo/bar\"");
|
||||
}
|
||||
|
||||
static void importer(WrenVM* vm)
|
||||
{
|
||||
WrenConfiguration configuration;
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
configuration.resolveModuleFn = resolveChange;
|
||||
runTestVM(vm, &configuration, "import \"baz|bang\"");
|
||||
}
|
||||
|
||||
WrenForeignMethodFn resolutionBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Resolution.noResolver()") == 0) return noResolver;
|
||||
if (strcmp(signature, "static Resolution.returnsNull()") == 0) return returnsNull;
|
||||
if (strcmp(signature, "static Resolution.changesString()") == 0) return changesString;
|
||||
if (strcmp(signature, "static Resolution.shared()") == 0) return shared;
|
||||
if (strcmp(signature, "static Resolution.importer()") == 0) return importer;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void resolutionBindClass(const char* className, WrenForeignClassMethods* methods)
|
||||
{
|
||||
// methods->allocate = foreignClassAllocate;
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn resolutionBindMethod(const char* signature);
|
||||
void resolutionBindClass(const char* className, WrenForeignClassMethods* methods);
|
||||
@ -1,39 +0,0 @@
|
||||
class Resolution {
|
||||
foreign static noResolver()
|
||||
foreign static returnsNull()
|
||||
foreign static changesString()
|
||||
foreign static shared()
|
||||
foreign static importer()
|
||||
}
|
||||
|
||||
// If no resolver function is configured, the default resolver just passes
|
||||
// along the import string unchanged.
|
||||
System.print(Resolution.noResolver())
|
||||
// expect: loading foo/bar
|
||||
// expect: ok
|
||||
// expect: success
|
||||
|
||||
// If the resolver returns NULL, it's reported as an error.
|
||||
System.print(Resolution.returnsNull())
|
||||
// expect: Could not resolve module 'foo/bar' imported from 'main'.
|
||||
// expect: error
|
||||
|
||||
// The resolver function can change the string.
|
||||
System.print(Resolution.changesString())
|
||||
// expect: loading main/foo/bar
|
||||
// expect: ok
|
||||
// expect: success
|
||||
|
||||
// Imports both "foo/bar" and "foo|bar", but only loads the module once because
|
||||
// they resolve to the same module.
|
||||
System.print(Resolution.shared())
|
||||
// expect: loading main/foo/bar
|
||||
// expect: ok
|
||||
// expect: success
|
||||
|
||||
// The string passed as importer is the resolver string of the importing module.
|
||||
System.print(Resolution.importer())
|
||||
// expect: loading main/baz/bang
|
||||
// expect: loading main/baz/bang/foo/bar
|
||||
// expect: ok
|
||||
// expect: success
|
||||
186
test/api/slots.c
186
test/api/slots.c
@ -1,186 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "slots.h"
|
||||
|
||||
static void noSet(WrenVM* vm)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static void getSlots(WrenVM* vm)
|
||||
{
|
||||
bool result = true;
|
||||
if (wrenGetSlotBool(vm, 1) != true) result = false;
|
||||
|
||||
int length;
|
||||
const char* bytes = wrenGetSlotBytes(vm, 2, &length);
|
||||
if (length != 5) result = false;
|
||||
if (memcmp(bytes, "by\0te", length) != 0) result = false;
|
||||
|
||||
if (wrenGetSlotDouble(vm, 3) != 1.5) result = false;
|
||||
if (strcmp(wrenGetSlotString(vm, 4), "str") != 0) result = false;
|
||||
|
||||
WrenHandle* handle = wrenGetSlotHandle(vm, 5);
|
||||
|
||||
if (result)
|
||||
{
|
||||
// Otherwise, return the value so we can tell if we captured it correctly.
|
||||
wrenSetSlotHandle(vm, 0, handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If anything failed, return false.
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
}
|
||||
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
|
||||
static void setSlots(WrenVM* vm)
|
||||
{
|
||||
WrenHandle* handle = wrenGetSlotHandle(vm, 1);
|
||||
|
||||
wrenSetSlotBool(vm, 1, true);
|
||||
wrenSetSlotBytes(vm, 2, "by\0te", 5);
|
||||
wrenSetSlotDouble(vm, 3, 1.5);
|
||||
wrenSetSlotString(vm, 4, "str");
|
||||
wrenSetSlotNull(vm, 5);
|
||||
|
||||
// Read the slots back to make sure they were set correctly.
|
||||
|
||||
bool result = true;
|
||||
if (wrenGetSlotBool(vm, 1) != true) result = false;
|
||||
|
||||
int length;
|
||||
const char* bytes = wrenGetSlotBytes(vm, 2, &length);
|
||||
if (length != 5) result = false;
|
||||
if (memcmp(bytes, "by\0te", length) != 0) result = false;
|
||||
|
||||
if (wrenGetSlotDouble(vm, 3) != 1.5) result = false;
|
||||
if (strcmp(wrenGetSlotString(vm, 4), "str") != 0) result = false;
|
||||
|
||||
if (wrenGetSlotType(vm, 5) != WREN_TYPE_NULL) result = false;
|
||||
|
||||
if (result)
|
||||
{
|
||||
// Move the value into the return position.
|
||||
wrenSetSlotHandle(vm, 0, handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If anything failed, return false.
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
}
|
||||
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
|
||||
static void slotTypes(WrenVM* vm)
|
||||
{
|
||||
bool result =
|
||||
wrenGetSlotType(vm, 1) == WREN_TYPE_BOOL &&
|
||||
wrenGetSlotType(vm, 2) == WREN_TYPE_FOREIGN &&
|
||||
wrenGetSlotType(vm, 3) == WREN_TYPE_LIST &&
|
||||
wrenGetSlotType(vm, 4) == WREN_TYPE_NULL &&
|
||||
wrenGetSlotType(vm, 5) == WREN_TYPE_NUM &&
|
||||
wrenGetSlotType(vm, 6) == WREN_TYPE_STRING &&
|
||||
wrenGetSlotType(vm, 7) == WREN_TYPE_UNKNOWN;
|
||||
|
||||
wrenSetSlotBool(vm, 0, result);
|
||||
}
|
||||
|
||||
static void ensure(WrenVM* vm)
|
||||
{
|
||||
int before = wrenGetSlotCount(vm);
|
||||
|
||||
wrenEnsureSlots(vm, 20);
|
||||
|
||||
int after = wrenGetSlotCount(vm);
|
||||
|
||||
// Use the slots to make sure they're available.
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
wrenSetSlotDouble(vm, i, i);
|
||||
}
|
||||
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
sum += (int)wrenGetSlotDouble(vm, i);
|
||||
}
|
||||
|
||||
char result[100];
|
||||
sprintf(result, "%d -> %d (%d)", before, after, sum);
|
||||
wrenSetSlotString(vm, 0, result);
|
||||
}
|
||||
|
||||
static void ensureOutsideForeign(WrenVM* vm)
|
||||
{
|
||||
// To test the behavior outside of a foreign method (which we're currently
|
||||
// in), create a new separate VM.
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
WrenVM* otherVM = wrenNewVM(&config);
|
||||
|
||||
int before = wrenGetSlotCount(otherVM);
|
||||
|
||||
wrenEnsureSlots(otherVM, 20);
|
||||
|
||||
int after = wrenGetSlotCount(otherVM);
|
||||
|
||||
// Use the slots to make sure they're available.
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
wrenSetSlotDouble(otherVM, i, i);
|
||||
}
|
||||
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
sum += (int)wrenGetSlotDouble(otherVM, i);
|
||||
}
|
||||
|
||||
wrenFreeVM(otherVM);
|
||||
|
||||
char result[100];
|
||||
sprintf(result, "%d -> %d (%d)", before, after, sum);
|
||||
wrenSetSlotString(vm, 0, result);
|
||||
}
|
||||
|
||||
static void foreignClassAllocate(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewForeign(vm, 0, 0, 4);
|
||||
}
|
||||
|
||||
static void getListCount(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotDouble(vm, 0, wrenGetListCount(vm, 1));
|
||||
}
|
||||
|
||||
static void getListElement(WrenVM* vm)
|
||||
{
|
||||
int index = (int)wrenGetSlotDouble(vm, 2);
|
||||
wrenGetListElement(vm, 1, index, 0);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn slotsBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Slots.noSet") == 0) return noSet;
|
||||
if (strcmp(signature, "static Slots.getSlots(_,_,_,_,_)") == 0) return getSlots;
|
||||
if (strcmp(signature, "static Slots.setSlots(_,_,_,_,_)") == 0) return setSlots;
|
||||
if (strcmp(signature, "static Slots.slotTypes(_,_,_,_,_,_,_)") == 0) return slotTypes;
|
||||
if (strcmp(signature, "static Slots.ensure()") == 0) return ensure;
|
||||
if (strcmp(signature, "static Slots.ensureOutsideForeign()") == 0) return ensureOutsideForeign;
|
||||
if (strcmp(signature, "static Slots.getListCount(_)") == 0) return getListCount;
|
||||
if (strcmp(signature, "static Slots.getListElement(_,_)") == 0) return getListElement;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void slotsBindClass(const char* className, WrenForeignClassMethods* methods)
|
||||
{
|
||||
methods->allocate = foreignClassAllocate;
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn slotsBindMethod(const char* signature);
|
||||
void slotsBindClass(const char* className, WrenForeignClassMethods* methods);
|
||||
@ -1,39 +0,0 @@
|
||||
class Slots {
|
||||
foreign static noSet
|
||||
foreign static getSlots(bool, num, string, bytes, value)
|
||||
foreign static setSlots(a, b, c, d, e)
|
||||
foreign static slotTypes(bool, foreignObj, list, nullObj, num, string, unknown)
|
||||
foreign static ensure()
|
||||
foreign static ensureOutsideForeign()
|
||||
foreign static getListCount(list)
|
||||
foreign static getListElement(list, index)
|
||||
}
|
||||
|
||||
foreign class ForeignType {
|
||||
construct new() {}
|
||||
}
|
||||
|
||||
// If nothing is set in the return slot, it retains its previous value, the
|
||||
// receiver.
|
||||
System.print(Slots.noSet == Slots) // expect: true
|
||||
|
||||
var value = ["value"]
|
||||
System.print(Slots.getSlots(true, "by\0te", 1.5, "str", value) == value)
|
||||
// expect: true
|
||||
|
||||
System.print(Slots.setSlots(value, 0, 0, 0, 0) == value)
|
||||
// expect: true
|
||||
|
||||
System.print(Slots.slotTypes(false, ForeignType.new(), [], null, 1.2, "str", 1..2))
|
||||
// expect: true
|
||||
|
||||
System.print(Slots.ensure())
|
||||
// expect: 1 -> 20 (190)
|
||||
|
||||
System.print(Slots.ensureOutsideForeign())
|
||||
// expect: 0 -> 20 (190)
|
||||
|
||||
var ducks = ["Huey", "Dewey", "Louie"]
|
||||
System.print(Slots.getListCount(ducks)) // expect: 3
|
||||
System.print(Slots.getListElement(ducks, 0)) // expect: Huey
|
||||
System.print(Slots.getListElement(ducks, 1)) // expect: Dewey
|
||||
@ -1,51 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "user_data.h"
|
||||
|
||||
static const char* data = "my user data";
|
||||
static const char* otherData = "other user data";
|
||||
|
||||
static void test(WrenVM* vm)
|
||||
{
|
||||
WrenConfiguration configuration;
|
||||
wrenInitConfiguration(&configuration);
|
||||
|
||||
// Should default to NULL.
|
||||
if (configuration.userData != NULL)
|
||||
{
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
return;
|
||||
}
|
||||
|
||||
configuration.userData = (void*)data;
|
||||
|
||||
WrenVM* otherVM = wrenNewVM(&configuration);
|
||||
|
||||
// Should be able to get it.
|
||||
if (wrenGetUserData(otherVM) != data)
|
||||
{
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
wrenFreeVM(otherVM);
|
||||
return;
|
||||
}
|
||||
|
||||
// Should be able to set it.
|
||||
wrenSetUserData(otherVM, (void*)otherData);
|
||||
|
||||
if (wrenGetUserData(otherVM) != otherData)
|
||||
{
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
wrenFreeVM(otherVM);
|
||||
return;
|
||||
}
|
||||
|
||||
wrenSetSlotBool(vm, 0, true);
|
||||
wrenFreeVM(otherVM);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn userDataBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static UserData.test") == 0) return test;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn userDataBindMethod(const char* signature);
|
||||
@ -1,5 +0,0 @@
|
||||
class UserData {
|
||||
foreign static test
|
||||
}
|
||||
|
||||
System.print(UserData.test) // expect: true
|
||||
@ -1,19 +0,0 @@
|
||||
The benchmarks in here attempt to faithfully implement the exact same algorithm in a few different languages. We're using Lua, Python, and Ruby for comparison here because those are all in Wren's ballpark: dynamically-typed, object-oriented, bytecode-compiled.
|
||||
|
||||
A bit about each benchmark:
|
||||
|
||||
### binary_trees
|
||||
|
||||
This benchmark stresses object creation and garbage collection. It builds a few big, deeply nested binaries and then traverses them.
|
||||
|
||||
### fib
|
||||
|
||||
This is just a simple naïve Fibonacci number calculator. It was the first benchmark I wrote when Wren supported little more than function calls and arithmetic. It isn't particularly representative of real-world code, but it does stress function call and arithmetic.
|
||||
|
||||
### for
|
||||
|
||||
This microbenchmark just tests the performance of for loops. Not too useful, but i used it when implementing `for` in Wren to make sure it wasn't too far off the mark.
|
||||
|
||||
### method_call
|
||||
|
||||
This is the most useful benchmark: it tests dynamic dispatch and polymorphism. You'll note that the main iteration loop is unrolled in all of the implementations. This is to ensure that the loop overhead itself doesn't dwarf the method call time.
|
||||
@ -1,9 +0,0 @@
|
||||
class Benchmark {
|
||||
foreign static call(iterations)
|
||||
}
|
||||
|
||||
var result = Benchmark.call(1000000)
|
||||
// Returns false if it didn't calculate the right value. Otherwise returns the
|
||||
// elapsed time.
|
||||
System.print(result is Num)
|
||||
System.print("elapsed: %(result)")
|
||||
@ -1,20 +0,0 @@
|
||||
class Benchmark {
|
||||
foreign static arguments(a, b, c, d)
|
||||
}
|
||||
|
||||
var start = System.clock
|
||||
var result = 0
|
||||
for (i in 1..1000000) {
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
result = result + Benchmark.arguments(1, 2, 3, 4)
|
||||
}
|
||||
System.print(result)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,60 +0,0 @@
|
||||
// Ported from the Wren version.
|
||||
|
||||
class Tree {
|
||||
var _item;
|
||||
var _left;
|
||||
var _right;
|
||||
|
||||
Tree(item, depth) {
|
||||
_item = item;
|
||||
if (depth > 0) {
|
||||
var item2 = item + item;
|
||||
depth--;
|
||||
_left = new Tree(item2 - 1, depth);
|
||||
_right = new Tree(item2, depth);
|
||||
}
|
||||
}
|
||||
|
||||
get check {
|
||||
if (_left == null) {
|
||||
return _item;
|
||||
}
|
||||
|
||||
return _item + _left.check - _right.check;
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
var minDepth = 4;
|
||||
var maxDepth = 12;
|
||||
var stretchDepth = maxDepth + 1;
|
||||
|
||||
Stopwatch watch = new Stopwatch();
|
||||
watch.start();
|
||||
|
||||
print("stretch tree of depth ${stretchDepth} check: "
|
||||
"${new Tree(0, stretchDepth).check}");
|
||||
|
||||
var longLivedTree = new Tree(0, maxDepth);
|
||||
|
||||
// iterations = 2 ** maxDepth
|
||||
var iterations = 1;
|
||||
for (var d = 0; d < maxDepth; d++) {
|
||||
iterations = iterations * 2;
|
||||
}
|
||||
|
||||
var depth = minDepth;
|
||||
while (depth < stretchDepth) {
|
||||
var check = 0;
|
||||
for (var i = 1; i <= iterations; i++) {
|
||||
check += new Tree(i, depth).check + new Tree(-i, depth).check;
|
||||
}
|
||||
|
||||
print("${iterations * 2} trees of depth ${depth} check: ${check}");
|
||||
iterations ~/= 4;
|
||||
depth += 2;
|
||||
}
|
||||
|
||||
print("long lived tree of depth ${maxDepth} check: ${longLivedTree.check}");
|
||||
print("elapsed: ${watch.elapsedMilliseconds / 1000}");
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
-- The Computer Language Benchmarks Game
|
||||
-- http://shootout.alioth.debian.org/
|
||||
-- contributed by Mike Pall
|
||||
|
||||
local function BottomUpTree(item, depth)
|
||||
if depth > 0 then
|
||||
local i = item + item
|
||||
depth = depth - 1
|
||||
local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth)
|
||||
return { item, left, right }
|
||||
else
|
||||
return { item }
|
||||
end
|
||||
end
|
||||
|
||||
local function ItemCheck(tree)
|
||||
if tree[2] then
|
||||
return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3])
|
||||
else
|
||||
return tree[1]
|
||||
end
|
||||
end
|
||||
|
||||
local N = 12
|
||||
local mindepth = 4
|
||||
local maxdepth = mindepth + 2
|
||||
if maxdepth < N then maxdepth = N end
|
||||
|
||||
local start = os.clock()
|
||||
|
||||
do
|
||||
local stretchdepth = maxdepth + 1
|
||||
local stretchtree = BottomUpTree(0, stretchdepth)
|
||||
io.write(string.format("stretch tree of depth %d check: %d\n",
|
||||
stretchdepth, ItemCheck(stretchtree)))
|
||||
end
|
||||
|
||||
local longlivedtree = BottomUpTree(0, maxdepth)
|
||||
|
||||
for depth=mindepth,maxdepth,2 do
|
||||
local iterations = 2 ^ (maxdepth - depth + mindepth)
|
||||
local check = 0
|
||||
for i=1,iterations do
|
||||
check = check + ItemCheck(BottomUpTree(1, depth)) +
|
||||
ItemCheck(BottomUpTree(-1, depth))
|
||||
end
|
||||
io.write(string.format("%d trees of depth %d check: %d\n",
|
||||
iterations*2, depth, check))
|
||||
end
|
||||
|
||||
io.write(string.format("long lived tree of depth %d check: %d\n",
|
||||
maxdepth, ItemCheck(longlivedtree)))
|
||||
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,48 +0,0 @@
|
||||
# The Computer Language Benchmarks Game
|
||||
# http://shootout.alioth.debian.org/
|
||||
#
|
||||
# contributed by Antoine Pitrou
|
||||
# modified by Dominique Wahli
|
||||
# modified by Heinrich Acker
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
# Map "range" to an efficient range in both Python 2 and 3.
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
def make_tree(item, depth):
|
||||
if not depth: return item, None, None
|
||||
item2 = item + item
|
||||
depth -= 1
|
||||
return item, make_tree(item2 - 1, depth), make_tree(item2, depth)
|
||||
|
||||
def check_tree(node):
|
||||
item, left, right = node
|
||||
if not left: return item
|
||||
return item + check_tree(left) - check_tree(right)
|
||||
|
||||
min_depth = 4
|
||||
max_depth = 12
|
||||
stretch_depth = max_depth + 1
|
||||
|
||||
start = time.clock()
|
||||
print("stretch tree of depth %d check:" % stretch_depth, check_tree(make_tree(0, stretch_depth)))
|
||||
|
||||
long_lived_tree = make_tree(0, max_depth)
|
||||
|
||||
iterations = 2 ** max_depth
|
||||
for depth in range(min_depth, stretch_depth, 2):
|
||||
|
||||
check = 0
|
||||
for i in range(1, iterations + 1):
|
||||
check += check_tree(make_tree(i, depth)) + check_tree(make_tree(-i, depth))
|
||||
|
||||
print("%d trees of depth %d check:" % (iterations * 2, depth), check)
|
||||
iterations //= 4
|
||||
|
||||
print("long lived tree of depth %d check:" % max_depth, check_tree(long_lived_tree))
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,51 +0,0 @@
|
||||
# The Computer Language Shootout Benchmarks
|
||||
# http://shootout.alioth.debian.org
|
||||
#
|
||||
# contributed by Jesse Millikan
|
||||
# Modified by Wesley Moxam
|
||||
|
||||
|
||||
def item_check(left, item, right)
|
||||
return item if left.nil?
|
||||
item + item_check(*left) - item_check(*right)
|
||||
end
|
||||
|
||||
def bottom_up_tree(item, depth)
|
||||
return [nil, item, nil] unless depth > 0
|
||||
item_item = 2 * item
|
||||
depth -= 1
|
||||
[bottom_up_tree(item_item - 1, depth), item, bottom_up_tree(item_item, depth)]
|
||||
end
|
||||
|
||||
max_depth = 12
|
||||
min_depth = 4
|
||||
|
||||
max_depth = min_depth + 2 if min_depth + 2 > max_depth
|
||||
|
||||
stretch_depth = max_depth + 1
|
||||
stretch_tree = bottom_up_tree(0, stretch_depth)
|
||||
|
||||
start = Time.now
|
||||
puts "stretch tree of depth #{stretch_depth} check: #{item_check(*stretch_tree)}"
|
||||
stretch_tree = nil
|
||||
|
||||
long_lived_tree = bottom_up_tree(0, max_depth)
|
||||
|
||||
min_depth.step(max_depth + 1, 2) do |depth|
|
||||
iterations = 2**(max_depth - depth + min_depth)
|
||||
|
||||
check = 0
|
||||
|
||||
for i in 1..iterations
|
||||
temp_tree = bottom_up_tree(i, depth)
|
||||
check += item_check(*temp_tree)
|
||||
|
||||
temp_tree = bottom_up_tree(-i, depth)
|
||||
check += item_check(*temp_tree)
|
||||
end
|
||||
|
||||
puts "#{iterations * 2} trees of depth #{depth} check: #{check}"
|
||||
end
|
||||
|
||||
puts "long lived tree of depth #{max_depth} check: #{item_check(*long_lived_tree)}"
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,54 +0,0 @@
|
||||
// Ported from the Python version.
|
||||
|
||||
class Tree {
|
||||
construct new(item, depth) {
|
||||
_item = item
|
||||
if (depth > 0) {
|
||||
var item2 = item + item
|
||||
depth = depth - 1
|
||||
_left = Tree.new(item2 - 1, depth)
|
||||
_right = Tree.new(item2, depth)
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
if (_left == null) {
|
||||
return _item
|
||||
}
|
||||
|
||||
return _item + _left.check - _right.check
|
||||
}
|
||||
}
|
||||
|
||||
var minDepth = 4
|
||||
var maxDepth = 12
|
||||
var stretchDepth = maxDepth + 1
|
||||
|
||||
var start = System.clock
|
||||
|
||||
System.print("stretch tree of depth %(stretchDepth) check: " +
|
||||
"%(Tree.new(0, stretchDepth).check)")
|
||||
|
||||
var longLivedTree = Tree.new(0, maxDepth)
|
||||
|
||||
// iterations = 2 ** maxDepth
|
||||
var iterations = 1
|
||||
for (d in 0...maxDepth) {
|
||||
iterations = iterations * 2
|
||||
}
|
||||
|
||||
var depth = minDepth
|
||||
while (depth < stretchDepth) {
|
||||
var check = 0
|
||||
for (i in 1..iterations) {
|
||||
check = check + Tree.new(i, depth).check + Tree.new(-i, depth).check
|
||||
}
|
||||
|
||||
System.print("%(iterations * 2) trees of depth %(depth) check: %(check)")
|
||||
iterations = iterations / 4
|
||||
depth = depth + 2
|
||||
}
|
||||
|
||||
System.print(
|
||||
"long lived tree of depth %(maxDepth) check: %(longLivedTree.check)")
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,59 +0,0 @@
|
||||
// Ported from the Python version.
|
||||
|
||||
class Tree {
|
||||
construct new(item, depth) {
|
||||
_item = item
|
||||
if (depth > 0) {
|
||||
var item2 = item + item
|
||||
depth = depth - 1
|
||||
_left = Tree.new(item2 - 1, depth)
|
||||
_right = Tree.new(item2, depth)
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
if (_left == null) {
|
||||
return _item
|
||||
}
|
||||
|
||||
return _item + _left.check - _right.check
|
||||
}
|
||||
}
|
||||
|
||||
var minDepth = 4
|
||||
var maxDepth = 12
|
||||
var stretchDepth = maxDepth + 1
|
||||
|
||||
var start = System.clock
|
||||
|
||||
System.print("stretch tree of depth %(stretchDepth) check: " +
|
||||
"%(Tree.new(0, stretchDepth).check)")
|
||||
for (i in 1...1000) System.gc()
|
||||
|
||||
var longLivedTree = Tree.new(0, maxDepth)
|
||||
|
||||
// iterations = 2 ** maxDepth
|
||||
var iterations = 1
|
||||
for (d in 0...maxDepth) {
|
||||
iterations = iterations * 2
|
||||
}
|
||||
|
||||
var depth = minDepth
|
||||
while (depth < stretchDepth) {
|
||||
var check = 0
|
||||
for (i in 1..iterations) {
|
||||
check = check + Tree.new(i, depth).check + Tree.new(-i, depth).check
|
||||
}
|
||||
|
||||
System.print("%(iterations * 2) trees of depth %(depth) check: %(check)")
|
||||
for (i in 1...1000) System.gc()
|
||||
|
||||
iterations = iterations / 4
|
||||
depth = depth + 2
|
||||
}
|
||||
|
||||
System.print(
|
||||
"long lived tree of depth %(maxDepth) check: %(longLivedTree.check)")
|
||||
for (i in 1...1000) System.gc()
|
||||
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,728 +0,0 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Copyright 1996 John Maloney and Mario Wolczko
|
||||
//
|
||||
// This file is part of GNU Smalltalk.
|
||||
//
|
||||
// GNU Smalltalk is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 2, or (at your option) any later version.
|
||||
//
|
||||
// GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
// details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// GNU Smalltalk; see the file COPYING. If not, write to the Free Software
|
||||
// Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// Translated first from Smalltalk to JavaScript, and finally to
|
||||
// Dart by Google 2008-2010.
|
||||
|
||||
/**
|
||||
* A Dart implementation of the DeltaBlue constraint-solving
|
||||
* algorithm, as described in:
|
||||
*
|
||||
* "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
|
||||
* Bjorn N. Freeman-Benson and John Maloney
|
||||
* January 1990 Communications of the ACM,
|
||||
* also available as University of Washington TR 89-08-06.
|
||||
*
|
||||
* Beware: this benchmark is written in a grotesque style where
|
||||
* the constraint model is built by side-effects from constructors.
|
||||
* I've kept it this way to avoid deviating too much from the original
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
var total = 0;
|
||||
|
||||
//Greg: Not using the dart benchmark harness, so I can observe warm up behaviour.
|
||||
main() {
|
||||
int iterations = 40;
|
||||
|
||||
Stopwatch watch = new Stopwatch();
|
||||
watch.start();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
chainTest(100);
|
||||
projectionTest(100);
|
||||
}
|
||||
|
||||
print(total);
|
||||
print("elapsed: ${watch.elapsedMilliseconds / 1000}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Strengths are used to measure the relative importance of constraints.
|
||||
* New strengths may be inserted in the strength hierarchy without
|
||||
* disrupting current constraints. Strengths cannot be created outside
|
||||
* this class, so == can be used for value comparison.
|
||||
*/
|
||||
class Strength {
|
||||
|
||||
final int value;
|
||||
final String name;
|
||||
|
||||
const Strength(this.value, this.name);
|
||||
|
||||
Strength nextWeaker() =>
|
||||
const <Strength>[WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT,
|
||||
PREFERRED, STRONG_REFERRED][value];
|
||||
|
||||
static bool stronger(Strength s1, Strength s2) {
|
||||
return s1.value < s2.value;
|
||||
}
|
||||
|
||||
static bool weaker(Strength s1, Strength s2) {
|
||||
return s1.value > s2.value;
|
||||
}
|
||||
|
||||
static Strength weakest(Strength s1, Strength s2) {
|
||||
return weaker(s1, s2) ? s1 : s2;
|
||||
}
|
||||
|
||||
static Strength strongest(Strength s1, Strength s2) {
|
||||
return stronger(s1, s2) ? s1 : s2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compile time computed constants.
|
||||
const REQUIRED = const Strength(0, "required");
|
||||
const STRONG_REFERRED = const Strength(1, "strongPreferred");
|
||||
const PREFERRED = const Strength(2, "preferred");
|
||||
const STRONG_DEFAULT = const Strength(3, "strongDefault");
|
||||
const NORMAL = const Strength(4, "normal");
|
||||
const WEAK_DEFAULT = const Strength(5, "weakDefault");
|
||||
const WEAKEST = const Strength(6, "weakest");
|
||||
|
||||
|
||||
abstract class Constraint {
|
||||
|
||||
final Strength strength;
|
||||
|
||||
const Constraint(this.strength);
|
||||
|
||||
bool isSatisfied();
|
||||
void markUnsatisfied();
|
||||
void addToGraph();
|
||||
void removeFromGraph();
|
||||
void chooseMethod(int mark);
|
||||
void markInputs(int mark);
|
||||
bool inputsKnown(int mark);
|
||||
Variable output();
|
||||
void execute();
|
||||
void recalculate();
|
||||
|
||||
/// Activate this constraint and attempt to satisfy it.
|
||||
void addConstraint() {
|
||||
addToGraph();
|
||||
planner.incrementalAdd(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find a way to enforce this constraint. If successful,
|
||||
* record the solution, perhaps modifying the current dataflow
|
||||
* graph. Answer the constraint that this constraint overrides, if
|
||||
* there is one, or nil, if there isn't.
|
||||
* Assume: I am not already satisfied.
|
||||
*/
|
||||
Constraint satisfy(mark) {
|
||||
chooseMethod(mark);
|
||||
if (!isSatisfied()) {
|
||||
if (strength == REQUIRED) {
|
||||
print("Could not satisfy a required constraint!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
markInputs(mark);
|
||||
Variable out = output();
|
||||
Constraint overridden = out.determinedBy;
|
||||
if (overridden != null) overridden.markUnsatisfied();
|
||||
out.determinedBy = this;
|
||||
if (!planner.addPropagate(this, mark)) print("Cycle encountered");
|
||||
out.mark = mark;
|
||||
return overridden;
|
||||
}
|
||||
|
||||
void destroyConstraint() {
|
||||
if (isSatisfied()) planner.incrementalRemove(this);
|
||||
removeFromGraph();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal constraints are not input constraints. An input constraint
|
||||
* is one that depends on external state, such as the mouse, the
|
||||
* keybord, a clock, or some arbitraty piece of imperative code.
|
||||
*/
|
||||
bool isInput() => false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract superclass for constraints having a single possible output variable.
|
||||
*/
|
||||
abstract class UnaryConstraint extends Constraint {
|
||||
|
||||
final Variable myOutput;
|
||||
bool satisfied = false;
|
||||
|
||||
UnaryConstraint(this.myOutput, Strength strength) : super(strength) {
|
||||
addConstraint();
|
||||
}
|
||||
|
||||
/// Adds this constraint to the constraint graph
|
||||
void addToGraph() {
|
||||
myOutput.addConstraint(this);
|
||||
satisfied = false;
|
||||
}
|
||||
|
||||
/// Decides if this constraint can be satisfied and records that decision.
|
||||
void chooseMethod(int mark) {
|
||||
satisfied = (myOutput.mark != mark)
|
||||
&& Strength.stronger(strength, myOutput.walkStrength);
|
||||
}
|
||||
|
||||
/// Returns true if this constraint is satisfied in the current solution.
|
||||
bool isSatisfied() => satisfied;
|
||||
|
||||
void markInputs(int mark) {
|
||||
// has no inputs.
|
||||
}
|
||||
|
||||
/// Returns the current output variable.
|
||||
Variable output() => myOutput;
|
||||
|
||||
/**
|
||||
* Calculate the walkabout strength, the stay flag, and, if it is
|
||||
* 'stay', the value for the current output of this constraint. Assume
|
||||
* this constraint is satisfied.
|
||||
*/
|
||||
void recalculate() {
|
||||
myOutput.walkStrength = strength;
|
||||
myOutput.stay = !isInput();
|
||||
if (myOutput.stay) execute(); // Stay optimization.
|
||||
}
|
||||
|
||||
/// Records that this constraint is unsatisfied.
|
||||
void markUnsatisfied() {
|
||||
satisfied = false;
|
||||
}
|
||||
|
||||
bool inputsKnown(int mark) => true;
|
||||
|
||||
void removeFromGraph() {
|
||||
if (myOutput != null) myOutput.removeConstraint(this);
|
||||
satisfied = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Variables that should, with some level of preference, stay the same.
|
||||
* Planners may exploit the fact that instances, if satisfied, will not
|
||||
* change their output during plan execution. This is called "stay
|
||||
* optimization".
|
||||
*/
|
||||
class StayConstraint extends UnaryConstraint {
|
||||
|
||||
StayConstraint(Variable v, Strength str) : super(v, str);
|
||||
|
||||
void execute() {
|
||||
// Stay constraints do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A unary input constraint used to mark a variable that the client
|
||||
* wishes to change.
|
||||
*/
|
||||
class EditConstraint extends UnaryConstraint {
|
||||
|
||||
EditConstraint(Variable v, Strength str) : super(v, str);
|
||||
|
||||
/// Edits indicate that a variable is to be changed by imperative code.
|
||||
bool isInput() => true;
|
||||
|
||||
void execute() {
|
||||
// Edit constraints do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Directions.
|
||||
const int NONE = 1;
|
||||
const int FORWARD = 2;
|
||||
const int BACKWARD = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract superclass for constraints having two possible output
|
||||
* variables.
|
||||
*/
|
||||
abstract class BinaryConstraint extends Constraint {
|
||||
|
||||
Variable v1;
|
||||
Variable v2;
|
||||
int direction = NONE;
|
||||
|
||||
BinaryConstraint(this.v1, this.v2, Strength strength) : super(strength) {
|
||||
addConstraint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if this constraint can be satisfied and which way it
|
||||
* should flow based on the relative strength of the variables related,
|
||||
* and record that decision.
|
||||
*/
|
||||
void chooseMethod(int mark) {
|
||||
if (v1.mark == mark) {
|
||||
direction = (v2.mark != mark &&
|
||||
Strength.stronger(strength, v2.walkStrength))
|
||||
? FORWARD : NONE;
|
||||
}
|
||||
if (v2.mark == mark) {
|
||||
direction = (v1.mark != mark &&
|
||||
Strength.stronger(strength, v1.walkStrength))
|
||||
? BACKWARD : NONE;
|
||||
}
|
||||
if (Strength.weaker(v1.walkStrength, v2.walkStrength)) {
|
||||
direction = Strength.stronger(strength, v1.walkStrength)
|
||||
? BACKWARD : NONE;
|
||||
} else {
|
||||
direction = Strength.stronger(strength, v2.walkStrength)
|
||||
? FORWARD : BACKWARD;
|
||||
}
|
||||
}
|
||||
|
||||
/// Add this constraint to the constraint graph.
|
||||
void addToGraph() {
|
||||
v1.addConstraint(this);
|
||||
v2.addConstraint(this);
|
||||
direction = NONE;
|
||||
}
|
||||
|
||||
/// Answer true if this constraint is satisfied in the current solution.
|
||||
bool isSatisfied() => direction != NONE;
|
||||
|
||||
/// Mark the input variable with the given mark.
|
||||
void markInputs(int mark) {
|
||||
input().mark = mark;
|
||||
}
|
||||
|
||||
/// Returns the current input variable
|
||||
Variable input() => direction == FORWARD ? v1 : v2;
|
||||
|
||||
/// Returns the current output variable.
|
||||
Variable output() => direction == FORWARD ? v2 : v1;
|
||||
|
||||
/**
|
||||
* Calculate the walkabout strength, the stay flag, and, if it is
|
||||
* 'stay', the value for the current output of this
|
||||
* constraint. Assume this constraint is satisfied.
|
||||
*/
|
||||
void recalculate() {
|
||||
Variable ihn = input(), out = output();
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength);
|
||||
out.stay = ihn.stay;
|
||||
if (out.stay) execute();
|
||||
}
|
||||
|
||||
/// Record the fact that this constraint is unsatisfied.
|
||||
void markUnsatisfied() {
|
||||
direction = NONE;
|
||||
}
|
||||
|
||||
bool inputsKnown(int mark) {
|
||||
Variable i = input();
|
||||
return i.mark == mark || i.stay || i.determinedBy == null;
|
||||
}
|
||||
|
||||
void removeFromGraph() {
|
||||
if (v1 != null) v1.removeConstraint(this);
|
||||
if (v2 != null) v2.removeConstraint(this);
|
||||
direction = NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Relates two variables by the linear scaling relationship: "v2 =
|
||||
* (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
|
||||
* this relationship but the scale factor and offset are considered
|
||||
* read-only.
|
||||
*/
|
||||
|
||||
class ScaleConstraint extends BinaryConstraint {
|
||||
|
||||
final Variable scale;
|
||||
final Variable offset;
|
||||
|
||||
ScaleConstraint(Variable src, this.scale, this.offset,
|
||||
Variable dest, Strength strength)
|
||||
: super(src, dest, strength);
|
||||
|
||||
/// Adds this constraint to the constraint graph.
|
||||
void addToGraph() {
|
||||
super.addToGraph();
|
||||
scale.addConstraint(this);
|
||||
offset.addConstraint(this);
|
||||
}
|
||||
|
||||
void removeFromGraph() {
|
||||
super.removeFromGraph();
|
||||
if (scale != null) scale.removeConstraint(this);
|
||||
if (offset != null) offset.removeConstraint(this);
|
||||
}
|
||||
|
||||
void markInputs(int mark) {
|
||||
super.markInputs(mark);
|
||||
scale.mark = offset.mark = mark;
|
||||
}
|
||||
|
||||
/// Enforce this constraint. Assume that it is satisfied.
|
||||
void execute() {
|
||||
if (direction == FORWARD) {
|
||||
v2.value = v1.value * scale.value + offset.value;
|
||||
} else {
|
||||
v1.value = (v2.value - offset.value) ~/ scale.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the walkabout strength, the stay flag, and, if it is
|
||||
* 'stay', the value for the current output of this constraint. Assume
|
||||
* this constraint is satisfied.
|
||||
*/
|
||||
void recalculate() {
|
||||
Variable ihn = input(), out = output();
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength);
|
||||
out.stay = ihn.stay && scale.stay && offset.stay;
|
||||
if (out.stay) execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constrains two variables to have the same value.
|
||||
*/
|
||||
class EqualityConstraint extends BinaryConstraint {
|
||||
|
||||
EqualityConstraint(Variable v1, Variable v2, Strength strength)
|
||||
: super(v1, v2, strength);
|
||||
|
||||
/// Enforce this constraint. Assume that it is satisfied.
|
||||
void execute() {
|
||||
output().value = input().value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A constrained variable. In addition to its value, it maintain the
|
||||
* structure of the constraint graph, the current dataflow graph, and
|
||||
* various parameters of interest to the DeltaBlue incremental
|
||||
* constraint solver.
|
||||
**/
|
||||
class Variable {
|
||||
|
||||
List<Constraint> constraints = <Constraint>[];
|
||||
Constraint determinedBy;
|
||||
int mark = 0;
|
||||
Strength walkStrength = WEAKEST;
|
||||
bool stay = true;
|
||||
int value;
|
||||
final String name;
|
||||
|
||||
Variable(this.name, this.value);
|
||||
|
||||
/**
|
||||
* Add the given constraint to the set of all constraints that refer
|
||||
* this variable.
|
||||
*/
|
||||
void addConstraint(Constraint c) {
|
||||
constraints.add(c);
|
||||
}
|
||||
|
||||
/// Removes all traces of c from this variable.
|
||||
void removeConstraint(Constraint c) {
|
||||
constraints = constraints.where((e) => c != e).toList();
|
||||
if (determinedBy == c) determinedBy = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Planner {
|
||||
|
||||
int currentMark = 0;
|
||||
|
||||
/**
|
||||
* Attempt to satisfy the given constraint and, if successful,
|
||||
* incrementally update the dataflow graph. Details: If satifying
|
||||
* the constraint is successful, it may override a weaker constraint
|
||||
* on its output. The algorithm attempts to resatisfy that
|
||||
* constraint using some other method. This process is repeated
|
||||
* until either a) it reaches a variable that was not previously
|
||||
* determined by any constraint or b) it reaches a constraint that
|
||||
* is too weak to be satisfied using any of its methods. The
|
||||
* variables of constraints that have been processed are marked with
|
||||
* a unique mark value so that we know where we've been. This allows
|
||||
* the algorithm to avoid getting into an infinite loop even if the
|
||||
* constraint graph has an inadvertent cycle.
|
||||
*/
|
||||
void incrementalAdd(Constraint c) {
|
||||
int mark = newMark();
|
||||
for(Constraint overridden = c.satisfy(mark);
|
||||
overridden != null;
|
||||
overridden = overridden.satisfy(mark));
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for retracting a constraint. Remove the given
|
||||
* constraint and incrementally update the dataflow graph.
|
||||
* Details: Retracting the given constraint may allow some currently
|
||||
* unsatisfiable downstream constraint to be satisfied. We therefore collect
|
||||
* a list of unsatisfied downstream constraints and attempt to
|
||||
* satisfy each one in turn. This list is traversed by constraint
|
||||
* strength, strongest first, as a heuristic for avoiding
|
||||
* unnecessarily adding and then overriding weak constraints.
|
||||
* Assume: [c] is satisfied.
|
||||
*/
|
||||
void incrementalRemove(Constraint c) {
|
||||
Variable out = c.output();
|
||||
c.markUnsatisfied();
|
||||
c.removeFromGraph();
|
||||
List<Constraint> unsatisfied = removePropagateFrom(out);
|
||||
Strength strength = REQUIRED;
|
||||
do {
|
||||
for (int i = 0; i < unsatisfied.length; i++) {
|
||||
Constraint u = unsatisfied[i];
|
||||
if (u.strength == strength) incrementalAdd(u);
|
||||
}
|
||||
strength = strength.nextWeaker();
|
||||
} while (strength != WEAKEST);
|
||||
}
|
||||
|
||||
/// Select a previously unused mark value.
|
||||
int newMark() => ++currentMark;
|
||||
|
||||
/**
|
||||
* Extract a plan for resatisfaction starting from the given source
|
||||
* constraints, usually a set of input constraints. This method
|
||||
* assumes that stay optimization is desired; the plan will contain
|
||||
* only constraints whose output variables are not stay. Constraints
|
||||
* that do no computation, such as stay and edit constraints, are
|
||||
* not included in the plan.
|
||||
* Details: The outputs of a constraint are marked when it is added
|
||||
* to the plan under construction. A constraint may be appended to
|
||||
* the plan when all its input variables are known. A variable is
|
||||
* known if either a) the variable is marked (indicating that has
|
||||
* been computed by a constraint appearing earlier in the plan), b)
|
||||
* the variable is 'stay' (i.e. it is a constant at plan execution
|
||||
* time), or c) the variable is not determined by any
|
||||
* constraint. The last provision is for past states of history
|
||||
* variables, which are not stay but which are also not computed by
|
||||
* any constraint.
|
||||
* Assume: [sources] are all satisfied.
|
||||
*/
|
||||
Plan makePlan(List<Constraint> sources) {
|
||||
int mark = newMark();
|
||||
Plan plan = new Plan();
|
||||
List<Constraint> todo = sources;
|
||||
while (todo.length > 0) {
|
||||
Constraint c = todo.removeLast();
|
||||
if (c.output().mark != mark && c.inputsKnown(mark)) {
|
||||
plan.addConstraint(c);
|
||||
c.output().mark = mark;
|
||||
addConstraintsConsumingTo(c.output(), todo);
|
||||
}
|
||||
}
|
||||
return plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a plan for resatisfying starting from the output of the
|
||||
* given [constraints], usually a set of input constraints.
|
||||
*/
|
||||
Plan extractPlanFromConstraints(List<Constraint> constraints) {
|
||||
List<Constraint> sources = <Constraint>[];
|
||||
for (int i = 0; i < constraints.length; i++) {
|
||||
Constraint c = constraints[i];
|
||||
// if not in plan already and eligible for inclusion.
|
||||
if (c.isInput() && c.isSatisfied()) sources.add(c);
|
||||
}
|
||||
return makePlan(sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompute the walkabout strengths and stay flags of all variables
|
||||
* downstream of the given constraint and recompute the actual
|
||||
* values of all variables whose stay flag is true. If a cycle is
|
||||
* detected, remove the given constraint and answer
|
||||
* false. Otherwise, answer true.
|
||||
* Details: Cycles are detected when a marked variable is
|
||||
* encountered downstream of the given constraint. The sender is
|
||||
* assumed to have marked the inputs of the given constraint with
|
||||
* the given mark. Thus, encountering a marked node downstream of
|
||||
* the output constraint means that there is a path from the
|
||||
* constraint's output to one of its inputs.
|
||||
*/
|
||||
bool addPropagate(Constraint c, int mark) {
|
||||
List<Constraint> todo = <Constraint>[c];
|
||||
while (todo.length > 0) {
|
||||
Constraint d = todo.removeLast();
|
||||
if (d.output().mark == mark) {
|
||||
incrementalRemove(c);
|
||||
return false;
|
||||
}
|
||||
d.recalculate();
|
||||
addConstraintsConsumingTo(d.output(), todo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the walkabout strengths and stay flags of all variables
|
||||
* downstream of the given constraint. Answer a collection of
|
||||
* unsatisfied constraints sorted in order of decreasing strength.
|
||||
*/
|
||||
List<Constraint> removePropagateFrom(Variable out) {
|
||||
out.determinedBy = null;
|
||||
out.walkStrength = WEAKEST;
|
||||
out.stay = true;
|
||||
List<Constraint> unsatisfied = <Constraint>[];
|
||||
List<Variable> todo = <Variable>[out];
|
||||
while (todo.length > 0) {
|
||||
Variable v = todo.removeLast();
|
||||
for (int i = 0; i < v.constraints.length; i++) {
|
||||
Constraint c = v.constraints[i];
|
||||
if (!c.isSatisfied()) unsatisfied.add(c);
|
||||
}
|
||||
Constraint determining = v.determinedBy;
|
||||
for (int i = 0; i < v.constraints.length; i++) {
|
||||
Constraint next = v.constraints[i];
|
||||
if (next != determining && next.isSatisfied()) {
|
||||
next.recalculate();
|
||||
todo.add(next.output());
|
||||
}
|
||||
}
|
||||
}
|
||||
return unsatisfied;
|
||||
}
|
||||
|
||||
void addConstraintsConsumingTo(Variable v, List<Constraint> coll) {
|
||||
Constraint determining = v.determinedBy;
|
||||
for (int i = 0; i < v.constraints.length; i++) {
|
||||
Constraint c = v.constraints[i];
|
||||
if (c != determining && c.isSatisfied()) coll.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A Plan is an ordered list of constraints to be executed in sequence
|
||||
* to resatisfy all currently satisfiable constraints in the face of
|
||||
* one or more changing inputs.
|
||||
*/
|
||||
class Plan {
|
||||
List<Constraint> list = <Constraint>[];
|
||||
|
||||
void addConstraint(Constraint c) {
|
||||
list.add(c);
|
||||
}
|
||||
|
||||
int size() => list.length;
|
||||
|
||||
void execute() {
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
list[i].execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
* constraints is constructed with a stay constraint on one end. An
|
||||
* edit constraint is then added to the opposite end and the time is
|
||||
* measured for adding and removing this constraint, and extracting
|
||||
* and executing a constraint satisfaction plan. There are two cases.
|
||||
* In case 1, the added constraint is stronger than the stay
|
||||
* constraint and values must propagate down the entire length of the
|
||||
* chain. In case 2, the added constraint is weaker than the stay
|
||||
* constraint so it cannot be accomodated. The cost in this case is,
|
||||
* of course, very low. Typical situations lie somewhere between these
|
||||
* two extremes.
|
||||
*/
|
||||
void chainTest(int n) {
|
||||
planner = new Planner();
|
||||
Variable prev = null, first = null, last = null;
|
||||
// Build chain of n equality constraints.
|
||||
for (int i = 0; i <= n; i++) {
|
||||
Variable v = new Variable("v", 0);
|
||||
if (prev != null) new EqualityConstraint(prev, v, REQUIRED);
|
||||
if (i == 0) first = v;
|
||||
if (i == n) last = v;
|
||||
prev = v;
|
||||
}
|
||||
new StayConstraint(last, STRONG_DEFAULT);
|
||||
EditConstraint edit = new EditConstraint(first, PREFERRED);
|
||||
Plan plan = planner.extractPlanFromConstraints(<Constraint>[edit]);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
first.value = i;
|
||||
plan.execute();
|
||||
total += last.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test constructs a two sets of variables related to each
|
||||
* other by a simple linear transformation (scale and offset). The
|
||||
* time is measured to change a variable on either side of the
|
||||
* mapping and to change the scale and offset factors.
|
||||
*/
|
||||
void projectionTest(int n) {
|
||||
planner = new Planner();
|
||||
Variable scale = new Variable("scale", 10);
|
||||
Variable offset = new Variable("offset", 1000);
|
||||
Variable src = null, dst = null;
|
||||
|
||||
List<Variable> dests = <Variable>[];
|
||||
for (int i = 0; i < n; i++) {
|
||||
src = new Variable("src", i);
|
||||
dst = new Variable("dst", i);
|
||||
dests.add(dst);
|
||||
new StayConstraint(src, NORMAL);
|
||||
new ScaleConstraint(src, scale, offset, dst, REQUIRED);
|
||||
}
|
||||
change(src, 17);
|
||||
total += dst.value;
|
||||
if (dst.value != 1170) print("Projection 1 failed");
|
||||
change(dst, 1050);
|
||||
total += src.value;
|
||||
if (src.value != 5) print("Projection 2 failed");
|
||||
change(scale, 5);
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
total += dests[i].value;
|
||||
if (dests[i].value != i * 5 + 1000) print("Projection 3 failed");
|
||||
}
|
||||
change(offset, 2000);
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
total += dests[i].value;
|
||||
if (dests[i].value != i * 5 + 2000) print("Projection 4 failed");
|
||||
}
|
||||
}
|
||||
|
||||
void change(Variable v, int newValue) {
|
||||
EditConstraint edit = new EditConstraint(v, PREFERRED);
|
||||
Plan plan = planner.extractPlanFromConstraints(<EditConstraint>[edit]);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
v.value = newValue;
|
||||
plan.execute();
|
||||
}
|
||||
edit.destroyConstraint();
|
||||
}
|
||||
|
||||
Planner planner;
|
||||
@ -1,914 +0,0 @@
|
||||
-- Copyright 2008 the V8 project authors. All rights reserved.
|
||||
-- Copyright 1996 John Maloney and Mario Wolczko.
|
||||
|
||||
-- This program is free software; you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation; either version 2 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program; if not, write to the Free Software
|
||||
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
-- This implementation of the DeltaBlue benchmark is derived
|
||||
-- from the Smalltalk implementation by John Maloney and Mario
|
||||
-- Wolczko. Some parts have been translated directly, whereas
|
||||
-- others have been modified more aggresively to make it feel
|
||||
-- more like a JavaScript program.
|
||||
|
||||
|
||||
--
|
||||
-- A JavaScript implementation of the DeltaBlue constraint-solving
|
||||
-- algorithm, as described in:
|
||||
--
|
||||
-- "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
|
||||
-- Bjorn N. Freeman-Benson and John Maloney
|
||||
-- January 1990 Communications of the ACM,
|
||||
-- also available as University of Washington TR 89-08-06.
|
||||
--
|
||||
-- Beware: this benchmark is written in a grotesque style where
|
||||
-- the constraint model is built by side-effects from constructors.
|
||||
-- I've kept it this way to avoid deviating too much from the original
|
||||
-- implementation.
|
||||
--
|
||||
|
||||
-- From: https://github.com/mraleph/deltablue.lua
|
||||
|
||||
local planner
|
||||
|
||||
--- O b j e c t M o d e l ---
|
||||
|
||||
local function alert (...) print(...) end
|
||||
|
||||
local OrderedCollection = class()
|
||||
|
||||
function OrderedCollection:constructor()
|
||||
self.elms = {}
|
||||
end
|
||||
|
||||
function OrderedCollection:add(elm)
|
||||
self.elms[#self.elms + 1] = elm
|
||||
end
|
||||
|
||||
function OrderedCollection:at (index)
|
||||
return self.elms[index]
|
||||
end
|
||||
|
||||
function OrderedCollection:size ()
|
||||
return #self.elms
|
||||
end
|
||||
|
||||
function OrderedCollection:removeFirst ()
|
||||
local e = self.elms[#self.elms]
|
||||
self.elms[#self.elms] = nil
|
||||
return e
|
||||
end
|
||||
|
||||
function OrderedCollection:remove (elm)
|
||||
local index = 0
|
||||
local skipped = 0
|
||||
|
||||
for i = 1, #self.elms do
|
||||
local value = self.elms[i]
|
||||
if value ~= elm then
|
||||
self.elms[index] = value
|
||||
index = index + 1
|
||||
else
|
||||
skipped = skipped + 1
|
||||
end
|
||||
end
|
||||
|
||||
local l = #self.elms
|
||||
for i = 1, skipped do self.elms[l - i + 1] = nil end
|
||||
end
|
||||
|
||||
--
|
||||
-- S t r e n g t h
|
||||
--
|
||||
|
||||
--
|
||||
-- Strengths are used to measure the relative importance of constraints.
|
||||
-- New strengths may be inserted in the strength hierarchy without
|
||||
-- disrupting current constraints. Strengths cannot be created outside
|
||||
-- this class, so pointer comparison can be used for value comparison.
|
||||
--
|
||||
|
||||
local Strength = class()
|
||||
|
||||
function Strength:constructor(strengthValue, name)
|
||||
self.strengthValue = strengthValue
|
||||
self.name = name
|
||||
end
|
||||
|
||||
function Strength.stronger (s1, s2)
|
||||
return s1.strengthValue < s2.strengthValue
|
||||
end
|
||||
|
||||
function Strength.weaker (s1, s2)
|
||||
return s1.strengthValue > s2.strengthValue
|
||||
end
|
||||
|
||||
function Strength.weakestOf (s1, s2)
|
||||
return Strength.weaker(s1, s2) and s1 or s2
|
||||
end
|
||||
|
||||
function Strength.strongest (s1, s2)
|
||||
return Strength.stronger(s1, s2) and s1 or s2
|
||||
end
|
||||
|
||||
function Strength:nextWeaker ()
|
||||
local v = self.strengthValue
|
||||
if v == 0 then return Strength.WEAKEST
|
||||
elseif v == 1 then return Strength.WEAK_DEFAULT
|
||||
elseif v == 2 then return Strength.NORMAL
|
||||
elseif v == 3 then return Strength.STRONG_DEFAULT
|
||||
elseif v == 4 then return Strength.PREFERRED
|
||||
elseif v == 5 then return Strength.REQUIRED
|
||||
end
|
||||
end
|
||||
|
||||
-- Strength constants.
|
||||
Strength.REQUIRED = Strength.new(0, "required");
|
||||
Strength.STONG_PREFERRED = Strength.new(1, "strongPreferred");
|
||||
Strength.PREFERRED = Strength.new(2, "preferred");
|
||||
Strength.STRONG_DEFAULT = Strength.new(3, "strongDefault");
|
||||
Strength.NORMAL = Strength.new(4, "normal");
|
||||
Strength.WEAK_DEFAULT = Strength.new(5, "weakDefault");
|
||||
Strength.WEAKEST = Strength.new(6, "weakest");
|
||||
|
||||
--
|
||||
-- C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- An abstract class representing a system-maintainable relationship
|
||||
-- (or "constraint") between a set of variables. A constraint supplies
|
||||
-- a strength instance variable; concrete subclasses provide a means
|
||||
-- of storing the constrained variables and other information required
|
||||
-- to represent a constraint.
|
||||
--
|
||||
|
||||
local Constraint = class ()
|
||||
|
||||
function Constraint:constructor(strength)
|
||||
self.strength = strength
|
||||
end
|
||||
|
||||
--
|
||||
-- Activate this constraint and attempt to satisfy it.
|
||||
--
|
||||
function Constraint:addConstraint ()
|
||||
self:addToGraph()
|
||||
planner:incrementalAdd(self)
|
||||
end
|
||||
|
||||
--
|
||||
-- Attempt to find a way to enforce this constraint. If successful,
|
||||
-- record the solution, perhaps modifying the current dataflow
|
||||
-- graph. Answer the constraint that this constraint overrides, if
|
||||
-- there is one, or nil, if there isn't.
|
||||
-- Assume: I am not already satisfied.
|
||||
--
|
||||
function Constraint:satisfy (mark)
|
||||
self:chooseMethod(mark)
|
||||
if not self:isSatisfied() then
|
||||
if self.strength == Strength.REQUIRED then
|
||||
alert("Could not satisfy a required constraint!")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
self:markInputs(mark)
|
||||
local out = self:output()
|
||||
local overridden = out.determinedBy
|
||||
if overridden ~= nil then overridden:markUnsatisfied() end
|
||||
out.determinedBy = self
|
||||
if not planner:addPropagate(self, mark) then alert("Cycle encountered") end
|
||||
out.mark = mark
|
||||
return overridden
|
||||
end
|
||||
|
||||
function Constraint:destroyConstraint ()
|
||||
if self:isSatisfied()
|
||||
then planner:incrementalRemove(self)
|
||||
else self:removeFromGraph()
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Normal constraints are not input constraints. An input constraint
|
||||
-- is one that depends on external state, such as the mouse, the
|
||||
-- keybord, a clock, or some arbitraty piece of imperative code.
|
||||
--
|
||||
function Constraint:isInput ()
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- U n a r y C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Abstract superclass for constraints having a single possible output
|
||||
-- variable.
|
||||
--
|
||||
|
||||
local UnaryConstraint = class(Constraint)
|
||||
|
||||
function UnaryConstraint:constructor (v, strength)
|
||||
UnaryConstraint.super.constructor(self, strength)
|
||||
self.myOutput = v
|
||||
self.satisfied = false
|
||||
self:addConstraint()
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds this constraint to the constraint graph
|
||||
--
|
||||
function UnaryConstraint:addToGraph ()
|
||||
self.myOutput:addConstraint(self)
|
||||
self.satisfied = false
|
||||
end
|
||||
|
||||
--
|
||||
-- Decides if this constraint can be satisfied and records that
|
||||
-- decision.
|
||||
--
|
||||
function UnaryConstraint:chooseMethod (mark)
|
||||
self.satisfied = (self.myOutput.mark ~= mark)
|
||||
and Strength.stronger(self.strength, self.myOutput.walkStrength);
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns true if this constraint is satisfied in the current solution.
|
||||
--
|
||||
function UnaryConstraint:isSatisfied ()
|
||||
return self.satisfied;
|
||||
end
|
||||
|
||||
function UnaryConstraint:markInputs (mark)
|
||||
-- has no inputs
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the current output variable.
|
||||
--
|
||||
function UnaryConstraint:output ()
|
||||
return self.myOutput
|
||||
end
|
||||
|
||||
--
|
||||
-- Calculate the walkabout strength, the stay flag, and, if it is
|
||||
-- 'stay', the value for the current output of this constraint. Assume
|
||||
-- this constraint is satisfied.
|
||||
--
|
||||
function UnaryConstraint:recalculate ()
|
||||
self.myOutput.walkStrength = self.strength
|
||||
self.myOutput.stay = not self:isInput()
|
||||
if self.myOutput.stay then
|
||||
self:execute() -- Stay optimization
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Records that this constraint is unsatisfied
|
||||
--
|
||||
function UnaryConstraint:markUnsatisfied ()
|
||||
self.satisfied = false
|
||||
end
|
||||
|
||||
function UnaryConstraint:inputsKnown ()
|
||||
return true
|
||||
end
|
||||
|
||||
function UnaryConstraint:removeFromGraph ()
|
||||
if self.myOutput ~= nil then
|
||||
self.myOutput:removeConstraint(self)
|
||||
end
|
||||
self.satisfied = false
|
||||
end
|
||||
|
||||
--
|
||||
-- S t a y C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Variables that should, with some level of preference, stay the same.
|
||||
-- Planners may exploit the fact that instances, if satisfied, will not
|
||||
-- change their output during plan execution. This is called "stay
|
||||
-- optimization".
|
||||
--
|
||||
|
||||
local StayConstraint = class(UnaryConstraint)
|
||||
|
||||
function StayConstraint:constructor(v, str)
|
||||
StayConstraint.super.constructor(self, v, str)
|
||||
end
|
||||
|
||||
function StayConstraint:execute ()
|
||||
-- Stay constraints do nothing
|
||||
end
|
||||
|
||||
--
|
||||
-- E d i t C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- A unary input constraint used to mark a variable that the client
|
||||
-- wishes to change.
|
||||
--
|
||||
|
||||
local EditConstraint = class (UnaryConstraint)
|
||||
|
||||
function EditConstraint:constructor(v, str)
|
||||
EditConstraint.super.constructor(self, v, str)
|
||||
end
|
||||
|
||||
--
|
||||
-- Edits indicate that a variable is to be changed by imperative code.
|
||||
--
|
||||
function EditConstraint:isInput ()
|
||||
return true
|
||||
end
|
||||
|
||||
function EditConstraint:execute ()
|
||||
-- Edit constraints do nothing
|
||||
end
|
||||
|
||||
--
|
||||
-- B i n a r y C o n s t r a i n t
|
||||
--
|
||||
|
||||
local Direction = {}
|
||||
Direction.NONE = 0
|
||||
Direction.FORWARD = 1
|
||||
Direction.BACKWARD = -1
|
||||
|
||||
--
|
||||
-- Abstract superclass for constraints having two possible output
|
||||
-- variables.
|
||||
--
|
||||
|
||||
local BinaryConstraint = class(Constraint)
|
||||
|
||||
function BinaryConstraint:constructor(var1, var2, strength)
|
||||
BinaryConstraint.super.constructor(self, strength);
|
||||
self.v1 = var1
|
||||
self.v2 = var2
|
||||
self.direction = Direction.NONE
|
||||
self:addConstraint()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Decides if this constraint can be satisfied and which way it
|
||||
-- should flow based on the relative strength of the variables related,
|
||||
-- and record that decision.
|
||||
--
|
||||
function BinaryConstraint:chooseMethod (mark)
|
||||
if self.v1.mark == mark then
|
||||
self.direction = (self.v2.mark ~= mark and Strength.stronger(self.strength, self.v2.walkStrength)) and Direction.FORWARD or Direction.NONE
|
||||
end
|
||||
if self.v2.mark == mark then
|
||||
self.direction = (self.v1.mark ~= mark and Strength.stronger(self.strength, self.v1.walkStrength)) and Direction.BACKWARD or Direction.NONE
|
||||
end
|
||||
if Strength.weaker(self.v1.walkStrength, self.v2.walkStrength) then
|
||||
self.direction = Strength.stronger(self.strength, self.v1.walkStrength) and Direction.BACKWARD or Direction.NONE
|
||||
else
|
||||
self.direction = Strength.stronger(self.strength, self.v2.walkStrength) and Direction.FORWARD or Direction.BACKWARD
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Add this constraint to the constraint graph
|
||||
--
|
||||
function BinaryConstraint:addToGraph ()
|
||||
self.v1:addConstraint(self)
|
||||
self.v2:addConstraint(self)
|
||||
self.direction = Direction.NONE
|
||||
end
|
||||
|
||||
--
|
||||
-- Answer true if this constraint is satisfied in the current solution.
|
||||
--
|
||||
function BinaryConstraint:isSatisfied ()
|
||||
return self.direction ~= Direction.NONE
|
||||
end
|
||||
|
||||
--
|
||||
-- Mark the input variable with the given mark.
|
||||
--
|
||||
function BinaryConstraint:markInputs (mark)
|
||||
self:input().mark = mark
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the current input variable
|
||||
--
|
||||
function BinaryConstraint:input ()
|
||||
return (self.direction == Direction.FORWARD) and self.v1 or self.v2
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the current output variable
|
||||
--
|
||||
function BinaryConstraint:output ()
|
||||
return (self.direction == Direction.FORWARD) and self.v2 or self.v1
|
||||
end
|
||||
|
||||
--
|
||||
-- Calculate the walkabout strength, the stay flag, and, if it is
|
||||
-- 'stay', the value for the current output of this
|
||||
-- constraint. Assume this constraint is satisfied.
|
||||
--
|
||||
function BinaryConstraint:recalculate ()
|
||||
local ihn = self:input()
|
||||
local out = self:output()
|
||||
out.walkStrength = Strength.weakestOf(self.strength, ihn.walkStrength);
|
||||
out.stay = ihn.stay
|
||||
if out.stay then self:execute() end
|
||||
end
|
||||
|
||||
--
|
||||
-- Record the fact that self constraint is unsatisfied.
|
||||
--
|
||||
function BinaryConstraint:markUnsatisfied ()
|
||||
self.direction = Direction.NONE
|
||||
end
|
||||
|
||||
function BinaryConstraint:inputsKnown (mark)
|
||||
local i = self:input()
|
||||
return i.mark == mark or i.stay or i.determinedBy == nil
|
||||
end
|
||||
|
||||
function BinaryConstraint:removeFromGraph ()
|
||||
if (self.v1 ~= nil) then self.v1:removeConstraint(self) end
|
||||
if (self.v2 ~= nil) then self.v2:removeConstraint(self) end
|
||||
self.direction = Direction.NONE
|
||||
end
|
||||
|
||||
--
|
||||
-- S c a l e C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Relates two variables by the linear scaling relationship: "v2 =
|
||||
-- (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
|
||||
-- this relationship but the scale factor and offset are considered
|
||||
-- read-only.
|
||||
--
|
||||
|
||||
local ScaleConstraint = class (BinaryConstraint)
|
||||
|
||||
function ScaleConstraint:constructor(src, scale, offset, dest, strength)
|
||||
self.direction = Direction.NONE
|
||||
self.scale = scale
|
||||
self.offset = offset
|
||||
ScaleConstraint.super.constructor(self, src, dest, strength)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Adds this constraint to the constraint graph.
|
||||
--
|
||||
function ScaleConstraint:addToGraph ()
|
||||
ScaleConstraint.super.addToGraph(self)
|
||||
self.scale:addConstraint(self)
|
||||
self.offset:addConstraint(self)
|
||||
end
|
||||
|
||||
function ScaleConstraint:removeFromGraph ()
|
||||
ScaleConstraint.super.removeFromGraph(self)
|
||||
if (self.scale ~= nil) then self.scale:removeConstraint(self) end
|
||||
if (self.offset ~= nil) then self.offset:removeConstraint(self) end
|
||||
end
|
||||
|
||||
function ScaleConstraint:markInputs (mark)
|
||||
ScaleConstraint.super.markInputs(self, mark);
|
||||
self.offset.mark = mark
|
||||
self.scale.mark = mark
|
||||
end
|
||||
|
||||
--
|
||||
-- Enforce this constraint. Assume that it is satisfied.
|
||||
--
|
||||
function ScaleConstraint:execute ()
|
||||
if self.direction == Direction.FORWARD then
|
||||
self.v2.value = self.v1.value * self.scale.value + self.offset.value
|
||||
else
|
||||
self.v1.value = (self.v2.value - self.offset.value) / self.scale.value
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Calculate the walkabout strength, the stay flag, and, if it is
|
||||
-- 'stay', the value for the current output of this constraint. Assume
|
||||
-- this constraint is satisfied.
|
||||
--
|
||||
function ScaleConstraint:recalculate ()
|
||||
local ihn = self:input()
|
||||
local out = self:output()
|
||||
out.walkStrength = Strength.weakestOf(self.strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay and self.scale.stay and self.offset.stay
|
||||
if out.stay then self:execute() end
|
||||
end
|
||||
|
||||
--
|
||||
-- E q u a l i t y C o n s t r a i n t
|
||||
--
|
||||
|
||||
--
|
||||
-- Constrains two variables to have the same value.
|
||||
--
|
||||
|
||||
local EqualityConstraint = class (BinaryConstraint)
|
||||
|
||||
function EqualityConstraint:constructor(var1, var2, strength)
|
||||
EqualityConstraint.super.constructor(self, var1, var2, strength)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Enforce this constraint. Assume that it is satisfied.
|
||||
--
|
||||
function EqualityConstraint:execute ()
|
||||
self:output().value = self:input().value
|
||||
end
|
||||
|
||||
--
|
||||
-- V a r i a b l e
|
||||
--
|
||||
|
||||
--
|
||||
-- A constrained variable. In addition to its value, it maintain the
|
||||
-- structure of the constraint graph, the current dataflow graph, and
|
||||
-- various parameters of interest to the DeltaBlue incremental
|
||||
-- constraint solver.
|
||||
--
|
||||
local Variable = class ()
|
||||
|
||||
function Variable:constructor(name, initialValue)
|
||||
self.value = initialValue or 0
|
||||
self.constraints = OrderedCollection.new()
|
||||
self.determinedBy = nil
|
||||
self.mark = 0
|
||||
self.walkStrength = Strength.WEAKEST
|
||||
self.stay = true
|
||||
self.name = name
|
||||
end
|
||||
|
||||
--
|
||||
-- Add the given constraint to the set of all constraints that refer
|
||||
-- this variable.
|
||||
--
|
||||
function Variable:addConstraint (c)
|
||||
self.constraints:add(c)
|
||||
end
|
||||
|
||||
--
|
||||
-- Removes all traces of c from this variable.
|
||||
--
|
||||
function Variable:removeConstraint (c)
|
||||
self.constraints:remove(c)
|
||||
if self.determinedBy == c then
|
||||
self.determinedBy = nil
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- P l a n n e r
|
||||
--
|
||||
|
||||
--
|
||||
-- The DeltaBlue planner
|
||||
--
|
||||
local Planner = class()
|
||||
function Planner:constructor()
|
||||
self.currentMark = 0
|
||||
end
|
||||
|
||||
--
|
||||
-- Attempt to satisfy the given constraint and, if successful,
|
||||
-- incrementally update the dataflow graph. Details: If satifying
|
||||
-- the constraint is successful, it may override a weaker constraint
|
||||
-- on its output. The algorithm attempts to resatisfy that
|
||||
-- constraint using some other method. This process is repeated
|
||||
-- until either a) it reaches a variable that was not previously
|
||||
-- determined by any constraint or b) it reaches a constraint that
|
||||
-- is too weak to be satisfied using any of its methods. The
|
||||
-- variables of constraints that have been processed are marked with
|
||||
-- a unique mark value so that we know where we've been. This allows
|
||||
-- the algorithm to avoid getting into an infinite loop even if the
|
||||
-- constraint graph has an inadvertent cycle.
|
||||
--
|
||||
function Planner:incrementalAdd (c)
|
||||
local mark = self:newMark()
|
||||
local overridden = c:satisfy(mark)
|
||||
while overridden ~= nil do
|
||||
overridden = overridden:satisfy(mark)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Entry point for retracting a constraint. Remove the given
|
||||
-- constraint and incrementally update the dataflow graph.
|
||||
-- Details: Retracting the given constraint may allow some currently
|
||||
-- unsatisfiable downstream constraint to be satisfied. We therefore collect
|
||||
-- a list of unsatisfied downstream constraints and attempt to
|
||||
-- satisfy each one in turn. This list is traversed by constraint
|
||||
-- strength, strongest first, as a heuristic for avoiding
|
||||
-- unnecessarily adding and then overriding weak constraints.
|
||||
-- Assume: c is satisfied.
|
||||
--
|
||||
function Planner:incrementalRemove (c)
|
||||
local out = c:output()
|
||||
c:markUnsatisfied()
|
||||
c:removeFromGraph()
|
||||
local unsatisfied = self:removePropagateFrom(out)
|
||||
local strength = Strength.REQUIRED
|
||||
repeat
|
||||
for i = 1, unsatisfied:size() do
|
||||
local u = unsatisfied:at(i)
|
||||
if u.strength == strength then
|
||||
self:incrementalAdd(u)
|
||||
end
|
||||
end
|
||||
strength = strength:nextWeaker()
|
||||
until strength == Strength.WEAKEST
|
||||
end
|
||||
|
||||
--
|
||||
-- Select a previously unused mark value.
|
||||
--
|
||||
function Planner:newMark ()
|
||||
self.currentMark = self.currentMark + 1
|
||||
return self.currentMark
|
||||
end
|
||||
|
||||
--
|
||||
-- Extract a plan for resatisfaction starting from the given source
|
||||
-- constraints, usually a set of input constraints. This method
|
||||
-- assumes that stay optimization is desired; the plan will contain
|
||||
-- only constraints whose output variables are not stay. Constraints
|
||||
-- that do no computation, such as stay and edit constraints, are
|
||||
-- not included in the plan.
|
||||
-- Details: The outputs of a constraint are marked when it is added
|
||||
-- to the plan under construction. A constraint may be appended to
|
||||
-- the plan when all its input variables are known. A variable is
|
||||
-- known if either a) the variable is marked (indicating that has
|
||||
-- been computed by a constraint appearing earlier in the plan), b)
|
||||
-- the variable is 'stay' (i.e. it is a constant at plan execution
|
||||
-- time), or c) the variable is not determined by any
|
||||
-- constraint. The last provision is for past states of history
|
||||
-- variables, which are not stay but which are also not computed by
|
||||
-- any constraint.
|
||||
-- Assume: sources are all satisfied.
|
||||
--
|
||||
local Plan -- FORWARD DECLARATION
|
||||
function Planner:makePlan (sources)
|
||||
local mark = self:newMark()
|
||||
local plan = Plan.new()
|
||||
local todo = sources
|
||||
while todo:size() > 0 do
|
||||
local c = todo:removeFirst()
|
||||
if c:output().mark ~= mark and c:inputsKnown(mark) then
|
||||
plan:addConstraint(c)
|
||||
c:output().mark = mark
|
||||
self:addConstraintsConsumingTo(c:output(), todo)
|
||||
end
|
||||
end
|
||||
return plan
|
||||
end
|
||||
|
||||
--
|
||||
-- Extract a plan for resatisfying starting from the output of the
|
||||
-- given constraints, usually a set of input constraints.
|
||||
--
|
||||
function Planner:extractPlanFromConstraints (constraints)
|
||||
local sources = OrderedCollection.new()
|
||||
for i = 1, constraints:size() do
|
||||
local c = constraints:at(i)
|
||||
if c:isInput() and c:isSatisfied() then
|
||||
-- not in plan already and eligible for inclusion
|
||||
sources:add(c)
|
||||
end
|
||||
end
|
||||
return self:makePlan(sources)
|
||||
end
|
||||
|
||||
--
|
||||
-- Recompute the walkabout strengths and stay flags of all variables
|
||||
-- downstream of the given constraint and recompute the actual
|
||||
-- values of all variables whose stay flag is true. If a cycle is
|
||||
-- detected, remove the given constraint and answer
|
||||
-- false. Otherwise, answer true.
|
||||
-- Details: Cycles are detected when a marked variable is
|
||||
-- encountered downstream of the given constraint. The sender is
|
||||
-- assumed to have marked the inputs of the given constraint with
|
||||
-- the given mark. Thus, encountering a marked node downstream of
|
||||
-- the output constraint means that there is a path from the
|
||||
-- constraint's output to one of its inputs.
|
||||
--
|
||||
function Planner:addPropagate (c, mark)
|
||||
local todo = OrderedCollection.new()
|
||||
todo:add(c)
|
||||
while todo:size() > 0 do
|
||||
local d = todo:removeFirst()
|
||||
if d:output().mark == mark then
|
||||
self:incrementalRemove(c)
|
||||
return false
|
||||
end
|
||||
d:recalculate()
|
||||
self:addConstraintsConsumingTo(d:output(), todo)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Update the walkabout strengths and stay flags of all variables
|
||||
-- downstream of the given constraint. Answer a collection of
|
||||
-- unsatisfied constraints sorted in order of decreasing strength.
|
||||
--
|
||||
function Planner:removePropagateFrom (out)
|
||||
out.determinedBy = nil
|
||||
out.walkStrength = Strength.WEAKEST
|
||||
out.stay = true
|
||||
local unsatisfied = OrderedCollection.new()
|
||||
local todo = OrderedCollection.new()
|
||||
todo:add(out)
|
||||
while todo:size() > 0 do
|
||||
local v = todo:removeFirst()
|
||||
for i = 1, v.constraints:size() do
|
||||
local c = v.constraints:at(i)
|
||||
if not c:isSatisfied() then unsatisfied:add(c) end
|
||||
end
|
||||
local determining = v.determinedBy
|
||||
for i = 1, v.constraints:size() do
|
||||
local next = v.constraints:at(i);
|
||||
if next ~= determining and next:isSatisfied() then
|
||||
next:recalculate()
|
||||
todo:add(next:output())
|
||||
end
|
||||
end
|
||||
end
|
||||
return unsatisfied
|
||||
end
|
||||
|
||||
function Planner:addConstraintsConsumingTo (v, coll)
|
||||
local determining = v.determinedBy
|
||||
local cc = v.constraints
|
||||
for i = 1, cc:size() do
|
||||
local c = cc:at(i)
|
||||
if c ~= determining and c:isSatisfied() then
|
||||
coll:add(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- P l a n
|
||||
--
|
||||
|
||||
--
|
||||
-- A Plan is an ordered list of constraints to be executed in sequence
|
||||
-- to resatisfy all currently satisfiable constraints in the face of
|
||||
-- one or more changing inputs.
|
||||
--
|
||||
Plan = class()
|
||||
function Plan:constructor()
|
||||
self.v = OrderedCollection.new()
|
||||
end
|
||||
|
||||
function Plan:addConstraint (c)
|
||||
self.v:add(c)
|
||||
end
|
||||
|
||||
function Plan:size ()
|
||||
return self.v:size()
|
||||
end
|
||||
|
||||
function Plan:constraintAt (index)
|
||||
return self.v:at(index)
|
||||
end
|
||||
|
||||
function Plan:execute ()
|
||||
for i = 1, self:size() do
|
||||
local c = self:constraintAt(i)
|
||||
c:execute()
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- M a i n
|
||||
--
|
||||
|
||||
--
|
||||
-- This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
-- constraints is constructed with a stay constraint on one end. An
|
||||
-- edit constraint is then added to the opposite end and the time is
|
||||
-- measured for adding and removing this constraint, and extracting
|
||||
-- and executing a constraint satisfaction plan. There are two cases.
|
||||
-- In case 1, the added constraint is stronger than the stay
|
||||
-- constraint and values must propagate down the entire length of the
|
||||
-- chain. In case 2, the added constraint is weaker than the stay
|
||||
-- constraint so it cannot be accomodated. The cost in this case is,
|
||||
-- of course, very low. Typical situations lie somewhere between these
|
||||
-- two extremes.
|
||||
--
|
||||
local function chainTest(n)
|
||||
planner = Planner.new()
|
||||
local prev = nil
|
||||
local first = nil
|
||||
local last = nil
|
||||
|
||||
-- Build chain of n equality constraints
|
||||
for i = 0, n do
|
||||
local name = "v" .. i;
|
||||
local v = Variable.new(name)
|
||||
if prev ~= nil then EqualityConstraint.new(prev, v, Strength.REQUIRED) end
|
||||
if i == 0 then first = v end
|
||||
if i == n then last = v end
|
||||
prev = v
|
||||
end
|
||||
|
||||
StayConstraint.new(last, Strength.STRONG_DEFAULT)
|
||||
local edit = EditConstraint.new(first, Strength.PREFERRED)
|
||||
local edits = OrderedCollection.new()
|
||||
edits:add(edit)
|
||||
local plan = planner:extractPlanFromConstraints(edits)
|
||||
for i = 0, 99 do
|
||||
first.value = i
|
||||
plan:execute()
|
||||
if last.value ~= i then
|
||||
alert("Chain test failed.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function change(v, newValue)
|
||||
local edit = EditConstraint.new(v, Strength.PREFERRED)
|
||||
local edits = OrderedCollection.new()
|
||||
edits:add(edit)
|
||||
local plan = planner:extractPlanFromConstraints(edits)
|
||||
for i = 1, 10 do
|
||||
v.value = newValue
|
||||
plan:execute()
|
||||
end
|
||||
edit:destroyConstraint()
|
||||
end
|
||||
|
||||
--
|
||||
-- This test constructs a two sets of variables related to each
|
||||
-- other by a simple linear transformation (scale and offset). The
|
||||
-- time is measured to change a variable on either side of the
|
||||
-- mapping and to change the scale and offset factors.
|
||||
--
|
||||
local function projectionTest(n)
|
||||
planner = Planner.new();
|
||||
local scale = Variable.new("scale", 10);
|
||||
local offset = Variable.new("offset", 1000);
|
||||
local src = nil
|
||||
local dst = nil;
|
||||
|
||||
local dests = OrderedCollection.new();
|
||||
for i = 0, n - 1 do
|
||||
src = Variable.new("src" .. i, i);
|
||||
dst = Variable.new("dst" .. i, i);
|
||||
dests:add(dst);
|
||||
StayConstraint.new(src, Strength.NORMAL);
|
||||
ScaleConstraint.new(src, scale, offset, dst, Strength.REQUIRED);
|
||||
end
|
||||
|
||||
change(src, 17)
|
||||
if dst.value ~= 1170 then alert("Projection 1 failed") end
|
||||
change(dst, 1050)
|
||||
if src.value ~= 5 then alert("Projection 2 failed") end
|
||||
change(scale, 5)
|
||||
for i = 0, n - 2 do
|
||||
if dests:at(i + 1).value ~= i * 5 + 1000 then
|
||||
alert("Projection 3 failed")
|
||||
end
|
||||
end
|
||||
change(offset, 2000)
|
||||
for i = 0, n - 2 do
|
||||
if dests:at(i + 1).value ~= i * 5 + 2000 then
|
||||
alert("Projection 4 failed")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function deltaBlue()
|
||||
chainTest(100);
|
||||
projectionTest(100);
|
||||
end
|
||||
|
||||
DeltaBlue = BenchmarkSuite.new('DeltaBlue', 66118, {
|
||||
Benchmark.new('DeltaBlue', deltaBlue)
|
||||
})
|
||||
@ -1,637 +0,0 @@
|
||||
"""
|
||||
deltablue.py
|
||||
============
|
||||
|
||||
Ported for the PyPy project.
|
||||
|
||||
This implementation of the DeltaBlue benchmark was directly ported
|
||||
from the `V8's source code`_, which was in turn derived
|
||||
from the Smalltalk implementation by John Maloney and Mario
|
||||
Wolczko. The original Javascript implementation was licensed under the GPL.
|
||||
|
||||
It's been updated in places to be more idiomatic to Python (for loops over
|
||||
collections, a couple magic methods, ``OrderedCollection`` being a list & things
|
||||
altering those collections changed to the builtin methods) but largely retains
|
||||
the layout & logic from the original. (Ugh.)
|
||||
|
||||
.. _`V8's source code`: (http://code.google.com/p/v8/source/browse/branches/bleeding_edge/benchmarks/deltablue.js)
|
||||
|
||||
From: https://gist.github.com/toastdriven/6408132
|
||||
|
||||
I (Bob Nystrom) tweaked it a bit more. It now prints some output just to be
|
||||
sure it's doing the same work, and I use normal lists instead of wrapping it in
|
||||
OrderedCollection.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import time
|
||||
|
||||
__author__ = 'Daniel Lindsley'
|
||||
__license__ = 'BSD'
|
||||
|
||||
|
||||
class Strength(object):
|
||||
REQUIRED = None
|
||||
STRONG_PREFERRED = None
|
||||
PREFERRED = None
|
||||
STRONG_DEFAULT = None
|
||||
NORMAL = None
|
||||
WEAK_DEFAULT = None
|
||||
WEAKEST = None
|
||||
|
||||
def __init__(self, strength, name):
|
||||
super(Strength, self).__init__()
|
||||
self.strength = strength
|
||||
self.name = name
|
||||
|
||||
@classmethod
|
||||
def stronger(cls, s1, s2):
|
||||
return s1.strength < s2.strength
|
||||
|
||||
@classmethod
|
||||
def weaker(cls, s1, s2):
|
||||
return s1.strength > s2.strength
|
||||
|
||||
@classmethod
|
||||
def weakest_of(cls, s1, s2):
|
||||
if cls.weaker(s1, s2):
|
||||
return s1
|
||||
|
||||
return s2
|
||||
|
||||
@classmethod
|
||||
def strongest(cls, s1, s2):
|
||||
if cls.stronger(s1, s2):
|
||||
return s1
|
||||
|
||||
return s2
|
||||
|
||||
def next_weaker(self):
|
||||
strengths = {
|
||||
0: self.__class__.WEAKEST,
|
||||
1: self.__class__.WEAK_DEFAULT,
|
||||
2: self.__class__.NORMAL,
|
||||
3: self.__class__.STRONG_DEFAULT,
|
||||
4: self.__class__.PREFERRED,
|
||||
# TODO: This looks like a bug in the original code. Shouldn't this be
|
||||
# ``STRONG_PREFERRED? Keeping for porting sake...
|
||||
5: self.__class__.REQUIRED,
|
||||
}
|
||||
return strengths[self.strength]
|
||||
|
||||
|
||||
# This is a terrible pattern IMO, but true to the original JS implementation.
|
||||
Strength.REQUIRED = Strength(0, "required")
|
||||
Strength.STONG_PREFERRED = Strength(1, "strongPreferred")
|
||||
Strength.PREFERRED = Strength(2, "preferred")
|
||||
Strength.STRONG_DEFAULT = Strength(3, "strongDefault")
|
||||
Strength.NORMAL = Strength(4, "normal")
|
||||
Strength.WEAK_DEFAULT = Strength(5, "weakDefault")
|
||||
Strength.WEAKEST = Strength(6, "weakest")
|
||||
|
||||
|
||||
class Constraint(object):
|
||||
def __init__(self, strength):
|
||||
super(Constraint, self).__init__()
|
||||
self.strength = strength
|
||||
|
||||
def add_constraint(self):
|
||||
global planner
|
||||
self.add_to_graph()
|
||||
planner.incremental_add(self)
|
||||
|
||||
def satisfy(self, mark):
|
||||
global planner
|
||||
self.choose_method(mark)
|
||||
|
||||
if not self.is_satisfied():
|
||||
if self.strength == Strength.REQUIRED:
|
||||
print('Could not satisfy a required constraint!')
|
||||
|
||||
return None
|
||||
|
||||
self.mark_inputs(mark)
|
||||
out = self.output()
|
||||
overridden = out.determined_by
|
||||
|
||||
if overridden is not None:
|
||||
overridden.mark_unsatisfied()
|
||||
|
||||
out.determined_by = self
|
||||
|
||||
if not planner.add_propagate(self, mark):
|
||||
print('Cycle encountered')
|
||||
|
||||
out.mark = mark
|
||||
return overridden
|
||||
|
||||
def destroy_constraint(self):
|
||||
global planner
|
||||
if self.is_satisfied():
|
||||
planner.incremental_remove(self)
|
||||
else:
|
||||
self.remove_from_graph()
|
||||
|
||||
def is_input(self):
|
||||
return False
|
||||
|
||||
|
||||
class UrnaryConstraint(Constraint):
|
||||
def __init__(self, v, strength):
|
||||
super(UrnaryConstraint, self).__init__(strength)
|
||||
self.my_output = v
|
||||
self.satisfied = False
|
||||
self.add_constraint()
|
||||
|
||||
def add_to_graph(self):
|
||||
self.my_output.add_constraint(self)
|
||||
self.satisfied = False
|
||||
|
||||
def choose_method(self, mark):
|
||||
if self.my_output.mark != mark and \
|
||||
Strength.stronger(self.strength, self.my_output.walk_strength):
|
||||
self.satisfied = True
|
||||
else:
|
||||
self.satisfied = False
|
||||
|
||||
def is_satisfied(self):
|
||||
return self.satisfied
|
||||
|
||||
def mark_inputs(self, mark):
|
||||
# No-ops.
|
||||
pass
|
||||
|
||||
def output(self):
|
||||
# Ugh. Keeping it for consistency with the original. So much for
|
||||
# "we're all adults here"...
|
||||
return self.my_output
|
||||
|
||||
def recalculate(self):
|
||||
self.my_output.walk_strength = self.strength
|
||||
self.my_output.stay = not self.is_input()
|
||||
|
||||
if self.my_output.stay:
|
||||
self.execute()
|
||||
|
||||
def mark_unsatisfied(self):
|
||||
self.satisfied = False
|
||||
|
||||
def inputs_known(self, mark):
|
||||
return True
|
||||
|
||||
def remove_from_graph(self):
|
||||
if self.my_output is not None:
|
||||
self.my_output.remove_constraint(self)
|
||||
self.satisfied = False
|
||||
|
||||
|
||||
class StayConstraint(UrnaryConstraint):
|
||||
def __init__(self, v, string):
|
||||
super(StayConstraint, self).__init__(v, string)
|
||||
|
||||
def execute(self):
|
||||
# The methods, THEY DO NOTHING.
|
||||
pass
|
||||
|
||||
|
||||
class EditConstraint(UrnaryConstraint):
|
||||
def __init__(self, v, string):
|
||||
super(EditConstraint, self).__init__(v, string)
|
||||
|
||||
def is_input(self):
|
||||
return True
|
||||
|
||||
def execute(self):
|
||||
# This constraint also does nothing.
|
||||
pass
|
||||
|
||||
|
||||
class Direction(object):
|
||||
# Hooray for things that ought to be structs!
|
||||
NONE = 0
|
||||
FORWARD = 1
|
||||
BACKWARD = -1
|
||||
|
||||
|
||||
class BinaryConstraint(Constraint):
|
||||
def __init__(self, v1, v2, strength):
|
||||
super(BinaryConstraint, self).__init__(strength)
|
||||
self.v1 = v1
|
||||
self.v2 = v2
|
||||
self.direction = Direction.NONE
|
||||
self.add_constraint()
|
||||
|
||||
def choose_method(self, mark):
|
||||
if self.v1.mark == mark:
|
||||
if self.v2.mark != mark and Strength.stronger(self.strength, self.v2.walk_strength):
|
||||
self.direction = Direction.FORWARD
|
||||
else:
|
||||
self.direction = Direction.BACKWARD
|
||||
|
||||
if self.v2.mark == mark:
|
||||
if self.v1.mark != mark and Strength.stronger(self.strength, self.v1.walk_strength):
|
||||
self.direction = Direction.BACKWARD
|
||||
else:
|
||||
self.direction = Direction.NONE
|
||||
|
||||
if Strength.weaker(self.v1.walk_strength, self.v2.walk_strength):
|
||||
if Strength.stronger(self.strength, self.v1.walk_strength):
|
||||
self.direction = Direction.BACKWARD
|
||||
else:
|
||||
self.direction = Direction.NONE
|
||||
else:
|
||||
if Strength.stronger(self.strength, self.v2.walk_strength):
|
||||
self.direction = Direction.FORWARD
|
||||
else:
|
||||
self.direction = Direction.BACKWARD
|
||||
|
||||
def add_to_graph(self):
|
||||
self.v1.add_constraint(self)
|
||||
self.v2.add_constraint(self)
|
||||
self.direction = Direction.NONE
|
||||
|
||||
def is_satisfied(self):
|
||||
return self.direction != Direction.NONE
|
||||
|
||||
def mark_inputs(self, mark):
|
||||
self.input().mark = mark
|
||||
|
||||
def input(self):
|
||||
if self.direction == Direction.FORWARD:
|
||||
return self.v1
|
||||
|
||||
return self.v2
|
||||
|
||||
def output(self):
|
||||
if self.direction == Direction.FORWARD:
|
||||
return self.v2
|
||||
|
||||
return self.v1
|
||||
|
||||
def recalculate(self):
|
||||
ihn = self.input()
|
||||
out = self.output()
|
||||
out.walk_strength = Strength.weakest_of(self.strength, ihn.walk_strength)
|
||||
out.stay = ihn.stay
|
||||
|
||||
if out.stay:
|
||||
self.execute()
|
||||
|
||||
def mark_unsatisfied(self):
|
||||
self.direction = Direction.NONE
|
||||
|
||||
def inputs_known(self, mark):
|
||||
i = self.input()
|
||||
return i.mark == mark or i.stay or i.determined_by == None
|
||||
|
||||
def remove_from_graph(self):
|
||||
if self.v1 is not None:
|
||||
self.v1.remove_constraint(self)
|
||||
|
||||
if self.v2 is not None:
|
||||
self.v2.remove_constraint(self)
|
||||
|
||||
self.direction = Direction.NONE
|
||||
|
||||
|
||||
class ScaleConstraint(BinaryConstraint):
|
||||
def __init__(self, src, scale, offset, dest, strength):
|
||||
self.direction = Direction.NONE
|
||||
self.scale = scale
|
||||
self.offset = offset
|
||||
super(ScaleConstraint, self).__init__(src, dest, strength)
|
||||
|
||||
def add_to_graph(self):
|
||||
super(ScaleConstraint, self).add_to_graph()
|
||||
self.scale.add_constraint(self)
|
||||
self.offset.add_constraint(self)
|
||||
|
||||
def remove_from_graph(self):
|
||||
super(ScaleConstraint, self).remove_from_graph()
|
||||
|
||||
if self.scale is not None:
|
||||
self.scale.remove_constraint(self)
|
||||
|
||||
if self.offset is not None:
|
||||
self.offset.remove_constraint(self)
|
||||
|
||||
def mark_inputs(self, mark):
|
||||
super(ScaleConstraint, self).mark_inputs(mark)
|
||||
self.scale.mark = mark
|
||||
self.offset.mark = mark
|
||||
|
||||
def execute(self):
|
||||
if self.direction == Direction.FORWARD:
|
||||
self.v2.value = self.v1.value * self.scale.value + self.offset.value
|
||||
else:
|
||||
self.v1.value = (self.v2.value - self.offset.value) / self.scale.value
|
||||
|
||||
def recalculate(self):
|
||||
ihn = self.input()
|
||||
out = self.output()
|
||||
out.walk_strength = Strength.weakest_of(self.strength, ihn.walk_strength)
|
||||
out.stay = ihn.stay and self.scale.stay and self.offset.stay
|
||||
|
||||
if out.stay:
|
||||
self.execute()
|
||||
|
||||
|
||||
class EqualityConstraint(BinaryConstraint):
|
||||
def execute(self):
|
||||
self.output().value = self.input().value
|
||||
|
||||
|
||||
class Variable(object):
|
||||
def __init__(self, name, initial_value=0):
|
||||
super(Variable, self).__init__()
|
||||
self.name = name
|
||||
self.value = initial_value
|
||||
self.constraints = []
|
||||
self.determined_by = None
|
||||
self.mark = 0
|
||||
self.walk_strength = Strength.WEAKEST
|
||||
self.stay = True
|
||||
|
||||
def __repr__(self):
|
||||
# To make debugging this beast from pdb easier...
|
||||
return '<Variable: %s - %s>' % (
|
||||
self.name,
|
||||
self.value
|
||||
)
|
||||
|
||||
def add_constraint(self, constraint):
|
||||
self.constraints.append(constraint)
|
||||
|
||||
def remove_constraint(self, constraint):
|
||||
self.constraints.remove(constraint)
|
||||
|
||||
if self.determined_by == constraint:
|
||||
self.determined_by = None
|
||||
|
||||
|
||||
class Planner(object):
|
||||
def __init__(self):
|
||||
super(Planner, self).__init__()
|
||||
self.current_mark = 0
|
||||
|
||||
def incremental_add(self, constraint):
|
||||
mark = self.new_mark()
|
||||
overridden = constraint.satisfy(mark)
|
||||
|
||||
while overridden is not None:
|
||||
overridden = overridden.satisfy(mark)
|
||||
|
||||
def incremental_remove(self, constraint):
|
||||
out = constraint.output()
|
||||
constraint.mark_unsatisfied()
|
||||
constraint.remove_from_graph()
|
||||
unsatisfied = self.remove_propagate_from(out)
|
||||
strength = Strength.REQUIRED
|
||||
# Do-while, the Python way.
|
||||
repeat = True
|
||||
|
||||
while repeat:
|
||||
for u in unsatisfied:
|
||||
if u.strength == strength:
|
||||
self.incremental_add(u)
|
||||
|
||||
strength = strength.next_weaker()
|
||||
|
||||
repeat = strength != Strength.WEAKEST
|
||||
|
||||
def new_mark(self):
|
||||
self.current_mark += 1
|
||||
return self.current_mark
|
||||
|
||||
def make_plan(self, sources):
|
||||
mark = self.new_mark()
|
||||
plan = Plan()
|
||||
todo = sources
|
||||
|
||||
while len(todo):
|
||||
c = todo.pop(0)
|
||||
|
||||
if c.output().mark != mark and c.inputs_known(mark):
|
||||
plan.add_constraint(c)
|
||||
c.output().mark = mark
|
||||
self.add_constraints_consuming_to(c.output(), todo)
|
||||
|
||||
return plan
|
||||
|
||||
def extract_plan_from_constraints(self, constraints):
|
||||
sources = []
|
||||
|
||||
for c in constraints:
|
||||
if c.is_input() and c.is_satisfied():
|
||||
sources.append(c)
|
||||
|
||||
return self.make_plan(sources)
|
||||
|
||||
def add_propagate(self, c, mark):
|
||||
todo = []
|
||||
todo.append(c)
|
||||
|
||||
while len(todo):
|
||||
d = todo.pop(0)
|
||||
|
||||
if d.output().mark == mark:
|
||||
self.incremental_remove(c)
|
||||
return False
|
||||
|
||||
d.recalculate()
|
||||
self.add_constraints_consuming_to(d.output(), todo)
|
||||
|
||||
return True
|
||||
|
||||
def remove_propagate_from(self, out):
|
||||
out.determined_by = None
|
||||
out.walk_strength = Strength.WEAKEST
|
||||
out.stay = True
|
||||
unsatisfied = []
|
||||
todo = []
|
||||
todo.append(out)
|
||||
|
||||
while len(todo):
|
||||
v = todo.pop(0)
|
||||
|
||||
for c in v.constraints:
|
||||
if not c.is_satisfied():
|
||||
unsatisfied.append(c)
|
||||
|
||||
determining = v.determined_by
|
||||
|
||||
for c in v.constraints:
|
||||
if c != determining and c.is_satisfied():
|
||||
c.recalculate()
|
||||
todo.append(c.output())
|
||||
|
||||
return unsatisfied
|
||||
|
||||
def add_constraints_consuming_to(self, v, coll):
|
||||
determining = v.determined_by
|
||||
cc = v.constraints
|
||||
|
||||
for c in cc:
|
||||
if c != determining and c.is_satisfied():
|
||||
# I guess we're just updating a reference (``coll``)? Seems
|
||||
# inconsistent with the rest of the implementation, where they
|
||||
# return the lists...
|
||||
coll.append(c)
|
||||
|
||||
|
||||
class Plan(object):
|
||||
def __init__(self):
|
||||
super(Plan, self).__init__()
|
||||
self.v = []
|
||||
|
||||
def add_constraint(self, c):
|
||||
self.v.append(c)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.v)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.v[index]
|
||||
|
||||
def execute(self):
|
||||
for c in self.v:
|
||||
c.execute()
|
||||
|
||||
|
||||
# Main
|
||||
total = 0
|
||||
|
||||
def chain_test(n):
|
||||
"""
|
||||
This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
constraints is constructed with a stay constraint on one end. An
|
||||
edit constraint is then added to the opposite end and the time is
|
||||
measured for adding and removing this constraint, and extracting
|
||||
and executing a constraint satisfaction plan. There are two cases.
|
||||
In case 1, the added constraint is stronger than the stay
|
||||
constraint and values must propagate down the entire length of the
|
||||
chain. In case 2, the added constraint is weaker than the stay
|
||||
constraint so it cannot be accomodated. The cost in this case is,
|
||||
of course, very low. Typical situations lie somewhere between these
|
||||
two extremes.
|
||||
"""
|
||||
global planner
|
||||
global total
|
||||
|
||||
planner = Planner()
|
||||
prev, first, last = None, None, None
|
||||
|
||||
# We need to go up to n inclusively.
|
||||
for i in range(n + 1):
|
||||
name = "v%s" % i
|
||||
v = Variable(name)
|
||||
|
||||
if prev is not None:
|
||||
EqualityConstraint(prev, v, Strength.REQUIRED)
|
||||
|
||||
if i == 0:
|
||||
first = v
|
||||
|
||||
if i == n:
|
||||
last = v
|
||||
|
||||
prev = v
|
||||
|
||||
StayConstraint(last, Strength.STRONG_DEFAULT)
|
||||
edit = EditConstraint(first, Strength.PREFERRED)
|
||||
edits = []
|
||||
edits.append(edit)
|
||||
plan = planner.extract_plan_from_constraints(edits)
|
||||
|
||||
for i in range(100):
|
||||
first.value = i
|
||||
plan.execute()
|
||||
|
||||
total += int(last.value)
|
||||
if last.value != i:
|
||||
print("Chain test failed.")
|
||||
|
||||
|
||||
def projection_test(n):
|
||||
"""
|
||||
This test constructs a two sets of variables related to each
|
||||
other by a simple linear transformation (scale and offset). The
|
||||
time is measured to change a variable on either side of the
|
||||
mapping and to change the scale and offset factors.
|
||||
"""
|
||||
global planner
|
||||
global total
|
||||
|
||||
planner = Planner()
|
||||
scale = Variable("scale", 10)
|
||||
offset = Variable("offset", 1000)
|
||||
src, dest = None, None
|
||||
|
||||
dests = []
|
||||
|
||||
for i in range(n):
|
||||
src = Variable("src%s" % i, i)
|
||||
dst = Variable("dst%s" % i, i)
|
||||
dests.append(dst)
|
||||
StayConstraint(src, Strength.NORMAL)
|
||||
ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED)
|
||||
|
||||
change(src, 17)
|
||||
|
||||
total += int(dst.value)
|
||||
if dst.value != 1170:
|
||||
print("Projection 1 failed")
|
||||
|
||||
change(dst, 1050)
|
||||
|
||||
total += int(src.value)
|
||||
if src.value != 5:
|
||||
print("Projection 2 failed")
|
||||
|
||||
change(scale, 5)
|
||||
|
||||
for i in range(n - 1):
|
||||
total += int(dests[i].value)
|
||||
if dests[i].value != (i * 5 + 1000):
|
||||
print("Projection 3 failed")
|
||||
|
||||
change(offset, 2000)
|
||||
|
||||
for i in range(n - 1):
|
||||
total += int(dests[i].value)
|
||||
if dests[i].value != (i * 5 + 2000):
|
||||
print("Projection 4 failed")
|
||||
|
||||
|
||||
def change(v, new_value):
|
||||
global planner
|
||||
edit = EditConstraint(v, Strength.PREFERRED)
|
||||
edits = []
|
||||
edits.append(edit)
|
||||
|
||||
plan = planner.extract_plan_from_constraints(edits)
|
||||
|
||||
for i in range(10):
|
||||
v.value = new_value
|
||||
plan.execute()
|
||||
|
||||
edit.destroy_constraint()
|
||||
|
||||
|
||||
# HOORAY FOR GLOBALS... Oh wait.
|
||||
# In spirit of the original, we'll keep it, but ugh.
|
||||
planner = None
|
||||
|
||||
|
||||
def delta_blue():
|
||||
global total
|
||||
start = time.clock()
|
||||
for i in range(40):
|
||||
chain_test(100)
|
||||
projection_test(100)
|
||||
print(total)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
delta_blue()
|
||||
@ -1,690 +0,0 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Copyright 1996 John Maloney and Mario Wolczko
|
||||
//
|
||||
// This file is part of GNU Smalltalk.
|
||||
//
|
||||
// GNU Smalltalk is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 2, or (at your option) any later version.
|
||||
//
|
||||
// GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
// details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// GNU Smalltalk; see the file COPYING. If not, write to the Free Software
|
||||
// Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// Translated first from Smalltalk to JavaScript, and finally to
|
||||
// Dart by Google 2008-2010.
|
||||
//
|
||||
// Translated to Wren by Bob Nystrom 2014.
|
||||
|
||||
// A Wren implementation of the DeltaBlue constraint-solving
|
||||
// algorithm, as described in:
|
||||
//
|
||||
// "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
|
||||
// Bjorn N. Freeman-Benson and John Maloney
|
||||
// January 1990 Communications of the ACM,
|
||||
// also available as University of Washington TR 89-08-06.
|
||||
//
|
||||
// Beware: this benchmark is written in a grotesque style where
|
||||
// the constraint model is built by side-effects from constructors.
|
||||
// I've kept it this way to avoid deviating too much from the original
|
||||
// implementation.
|
||||
|
||||
// Strengths are used to measure the relative importance of constraints.
|
||||
// New strengths may be inserted in the strength hierarchy without
|
||||
// disrupting current constraints. Strengths cannot be created outside
|
||||
// this class, so == can be used for value comparison.
|
||||
class Strength {
|
||||
construct new(value, name) {
|
||||
_value = value
|
||||
_name = name
|
||||
}
|
||||
|
||||
value { _value }
|
||||
name { _name }
|
||||
|
||||
nextWeaker { ORDERED[_value] }
|
||||
|
||||
static stronger(s1, s2) { s1.value < s2.value }
|
||||
static weaker(s1, s2) { s1.value > s2.value }
|
||||
static weakest(s1, s2) { Strength.weaker(s1, s2) ? s1 : s2 }
|
||||
static strongest(s1, s2) { Strength.stronger(s1, s2) ? s1 : s2 }
|
||||
}
|
||||
|
||||
// Compile time computed constants.
|
||||
var REQUIRED = Strength.new(0, "required")
|
||||
var STRONG_REFERRED = Strength.new(1, "strongPreferred")
|
||||
var PREFERRED = Strength.new(2, "preferred")
|
||||
var STRONG_DEFAULT = Strength.new(3, "strongDefault")
|
||||
var NORMAL = Strength.new(4, "normal")
|
||||
var WEAK_DEFAULT = Strength.new(5, "weakDefault")
|
||||
var WEAKEST = Strength.new(6, "weakest")
|
||||
|
||||
var ORDERED = [
|
||||
WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT, PREFERRED, STRONG_REFERRED
|
||||
]
|
||||
|
||||
var ThePlanner
|
||||
|
||||
class Constraint {
|
||||
construct new(strength) {
|
||||
_strength = strength
|
||||
}
|
||||
|
||||
strength { _strength }
|
||||
|
||||
// Activate this constraint and attempt to satisfy it.
|
||||
addConstraint() {
|
||||
addToGraph()
|
||||
ThePlanner.incrementalAdd(this)
|
||||
}
|
||||
|
||||
// Attempt to find a way to enforce this constraint. If successful,
|
||||
// record the solution, perhaps modifying the current dataflow
|
||||
// graph. Answer the constraint that this constraint overrides, if
|
||||
// there is one, or nil, if there isn't.
|
||||
// Assume: I am not already satisfied.
|
||||
satisfy(mark) {
|
||||
chooseMethod(mark)
|
||||
if (!isSatisfied) {
|
||||
if (_strength == REQUIRED) {
|
||||
System.print("Could not satisfy a required constraint!")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
markInputs(mark)
|
||||
var out = output
|
||||
var overridden = out.determinedBy
|
||||
if (overridden != null) overridden.markUnsatisfied()
|
||||
out.determinedBy = this
|
||||
if (!ThePlanner.addPropagate(this, mark)) System.print("Cycle encountered")
|
||||
out.mark = mark
|
||||
return overridden
|
||||
}
|
||||
|
||||
destroyConstraint() {
|
||||
if (isSatisfied) ThePlanner.incrementalRemove(this)
|
||||
removeFromGraph()
|
||||
}
|
||||
|
||||
// Normal constraints are not input constraints. An input constraint
|
||||
// is one that depends on external state, such as the mouse, the
|
||||
// keybord, a clock, or some arbitraty piece of imperative code.
|
||||
isInput { false }
|
||||
}
|
||||
|
||||
// Abstract superclass for constraints having a single possible output variable.
|
||||
class UnaryConstraint is Constraint {
|
||||
construct new(myOutput, strength) {
|
||||
super(strength)
|
||||
_satisfied = false
|
||||
_myOutput = myOutput
|
||||
addConstraint()
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph() {
|
||||
_myOutput.addConstraint(this)
|
||||
_satisfied = false
|
||||
}
|
||||
|
||||
// Decides if this constraint can be satisfied and records that decision.
|
||||
chooseMethod(mark) {
|
||||
_satisfied = (_myOutput.mark != mark) &&
|
||||
Strength.stronger(strength, _myOutput.walkStrength)
|
||||
}
|
||||
|
||||
// Returns true if this constraint is satisfied in the current solution.
|
||||
isSatisfied { _satisfied }
|
||||
|
||||
markInputs(mark) {
|
||||
// has no inputs.
|
||||
}
|
||||
|
||||
// Returns the current output variable.
|
||||
output { _myOutput }
|
||||
|
||||
// Calculate the walkabout strength, the stay flag, and, if it is
|
||||
// 'stay', the value for the current output of this constraint. Assume
|
||||
// this constraint is satisfied.
|
||||
recalculate() {
|
||||
_myOutput.walkStrength = strength
|
||||
_myOutput.stay = !isInput
|
||||
if (_myOutput.stay) execute() // Stay optimization.
|
||||
}
|
||||
|
||||
// Records that this constraint is unsatisfied.
|
||||
markUnsatisfied() {
|
||||
_satisfied = false
|
||||
}
|
||||
|
||||
inputsKnown(mark) { true }
|
||||
|
||||
removeFromGraph() {
|
||||
if (_myOutput != null) _myOutput.removeConstraint(this)
|
||||
_satisfied = false
|
||||
}
|
||||
}
|
||||
|
||||
// Variables that should, with some level of preference, stay the same.
|
||||
// Planners may exploit the fact that instances, if satisfied, will not
|
||||
// change their output during plan execution. This is called "stay
|
||||
// optimization".
|
||||
class StayConstraint is UnaryConstraint {
|
||||
construct new(variable, strength) {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
execute() {
|
||||
// Stay constraints do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// A unary input constraint used to mark a variable that the client
|
||||
// wishes to change.
|
||||
class EditConstraint is UnaryConstraint {
|
||||
construct new(variable, strength) {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
// Edits indicate that a variable is to be changed by imperative code.
|
||||
isInput { true }
|
||||
|
||||
execute() {
|
||||
// Edit constraints do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// Directions.
|
||||
var NONE = 1
|
||||
var FORWARD = 2
|
||||
var BACKWARD = 0
|
||||
|
||||
// Abstract superclass for constraints having two possible output
|
||||
// variables.
|
||||
class BinaryConstraint is Constraint {
|
||||
construct new(v1, v2, strength) {
|
||||
super(strength)
|
||||
_v1 = v1
|
||||
_v2 = v2
|
||||
_direction = NONE
|
||||
addConstraint()
|
||||
}
|
||||
|
||||
direction { _direction }
|
||||
v1 { _v1 }
|
||||
v2 { _v2 }
|
||||
|
||||
// Decides if this constraint can be satisfied and which way it
|
||||
// should flow based on the relative strength of the variables related,
|
||||
// and record that decision.
|
||||
chooseMethod(mark) {
|
||||
if (_v1.mark == mark) {
|
||||
if (_v2.mark != mark &&
|
||||
Strength.stronger(strength, _v2.walkStrength)) {
|
||||
_direction = FORWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
}
|
||||
|
||||
if (_v2.mark == mark) {
|
||||
if (_v1.mark != mark &&
|
||||
Strength.stronger(strength, _v1.walkStrength)) {
|
||||
_direction = BACKWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
}
|
||||
|
||||
if (Strength.weaker(_v1.walkStrength, _v2.walkStrength)) {
|
||||
if (Strength.stronger(strength, _v1.walkStrength)) {
|
||||
_direction = BACKWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
} else {
|
||||
if (Strength.stronger(strength, _v2.walkStrength)) {
|
||||
_direction = FORWARD
|
||||
} else {
|
||||
_direction = BACKWARD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add this constraint to the constraint graph.
|
||||
addToGraph() {
|
||||
_v1.addConstraint(this)
|
||||
_v2.addConstraint(this)
|
||||
_direction = NONE
|
||||
}
|
||||
|
||||
// Answer true if this constraint is satisfied in the current solution.
|
||||
isSatisfied { _direction != NONE }
|
||||
|
||||
// Mark the input variable with the given mark.
|
||||
markInputs(mark) {
|
||||
input.mark = mark
|
||||
}
|
||||
|
||||
// Returns the current input variable
|
||||
input { _direction == FORWARD ? _v1 : _v2 }
|
||||
|
||||
// Returns the current output variable.
|
||||
output { _direction == FORWARD ? _v2 : _v1 }
|
||||
|
||||
// Calculate the walkabout strength, the stay flag, and, if it is
|
||||
// 'stay', the value for the current output of this
|
||||
// constraint. Assume this constraint is satisfied.
|
||||
recalculate() {
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay
|
||||
if (out.stay) execute()
|
||||
}
|
||||
|
||||
// Record the fact that this constraint is unsatisfied.
|
||||
markUnsatisfied() {
|
||||
_direction = NONE
|
||||
}
|
||||
|
||||
inputsKnown(mark) {
|
||||
var i = input
|
||||
return i.mark == mark || i.stay || i.determinedBy == null
|
||||
}
|
||||
|
||||
removeFromGraph() {
|
||||
if (_v1 != null) _v1.removeConstraint(this)
|
||||
if (_v2 != null) _v2.removeConstraint(this)
|
||||
_direction = NONE
|
||||
}
|
||||
}
|
||||
|
||||
// Relates two variables by the linear scaling relationship: "v2 =
|
||||
// (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
|
||||
// this relationship but the scale factor and offset are considered
|
||||
// read-only.
|
||||
class ScaleConstraint is BinaryConstraint {
|
||||
construct new(src, scale, offset, dest, strength) {
|
||||
_scale = scale
|
||||
_offset = offset
|
||||
super(src, dest, strength)
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph() {
|
||||
super()
|
||||
_scale.addConstraint(this)
|
||||
_offset.addConstraint(this)
|
||||
}
|
||||
|
||||
removeFromGraph() {
|
||||
super()
|
||||
if (_scale != null) _scale.removeConstraint(this)
|
||||
if (_offset != null) _offset.removeConstraint(this)
|
||||
}
|
||||
|
||||
markInputs(mark) {
|
||||
super.markInputs(mark)
|
||||
_scale.mark = _offset.mark = mark
|
||||
}
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute() {
|
||||
if (direction == FORWARD) {
|
||||
v2.value = v1.value * _scale.value + _offset.value
|
||||
} else {
|
||||
// TODO: Is this the same semantics as ~/?
|
||||
v1.value = ((v2.value - _offset.value) / _scale.value).floor
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the walkabout strength, the stay flag, and, if it is
|
||||
// 'stay', the value for the current output of this constraint. Assume
|
||||
// this constraint is satisfied.
|
||||
recalculate() {
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay && _scale.stay && _offset.stay
|
||||
if (out.stay) execute()
|
||||
}
|
||||
}
|
||||
|
||||
// Constrains two variables to have the same value.
|
||||
class EqualityConstraint is BinaryConstraint {
|
||||
construct new(v1, v2, strength) {
|
||||
super(v1, v2, strength)
|
||||
}
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute() {
|
||||
output.value = input.value
|
||||
}
|
||||
}
|
||||
|
||||
// A constrained variable. In addition to its value, it maintain the
|
||||
// structure of the constraint graph, the current dataflow graph, and
|
||||
// various parameters of interest to the DeltaBlue incremental
|
||||
// constraint solver.
|
||||
class Variable {
|
||||
construct new(name, value) {
|
||||
_constraints = []
|
||||
_determinedBy = null
|
||||
_mark = 0
|
||||
_walkStrength = WEAKEST
|
||||
_stay = true
|
||||
_name = name
|
||||
_value = value
|
||||
}
|
||||
|
||||
constraints { _constraints }
|
||||
determinedBy { _determinedBy }
|
||||
determinedBy=(value) { _determinedBy = value }
|
||||
mark { _mark }
|
||||
mark=(value) { _mark = value }
|
||||
walkStrength { _walkStrength }
|
||||
walkStrength=(value) { _walkStrength = value }
|
||||
stay { _stay }
|
||||
stay=(value) { _stay = value }
|
||||
value { _value }
|
||||
value=(newValue) { _value = newValue }
|
||||
|
||||
// Add the given constraint to the set of all constraints that refer
|
||||
// this variable.
|
||||
addConstraint(constraint) {
|
||||
_constraints.add(constraint)
|
||||
}
|
||||
|
||||
// Removes all traces of c from this variable.
|
||||
removeConstraint(constraint) {
|
||||
_constraints = _constraints.where { |c| c != constraint }
|
||||
if (_determinedBy == constraint) _determinedBy = null
|
||||
}
|
||||
}
|
||||
|
||||
// A Plan is an ordered list of constraints to be executed in sequence
|
||||
// to resatisfy all currently satisfiable constraints in the face of
|
||||
// one or more changing inputs.
|
||||
class Plan {
|
||||
construct new() {
|
||||
_list = []
|
||||
}
|
||||
|
||||
addConstraint(constraint) {
|
||||
_list.add(constraint)
|
||||
}
|
||||
|
||||
size { _list.count }
|
||||
|
||||
execute() {
|
||||
for (constraint in _list) {
|
||||
constraint.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Planner {
|
||||
construct new() {
|
||||
_currentMark = 0
|
||||
}
|
||||
|
||||
// Attempt to satisfy the given constraint and, if successful,
|
||||
// incrementally update the dataflow graph. Details: If satifying
|
||||
// the constraint is successful, it may override a weaker constraint
|
||||
// on its output. The algorithm attempts to resatisfy that
|
||||
// constraint using some other method. This process is repeated
|
||||
// until either a) it reaches a variable that was not previously
|
||||
// determined by any constraint or b) it reaches a constraint that
|
||||
// is too weak to be satisfied using any of its methods. The
|
||||
// variables of constraints that have been processed are marked with
|
||||
// a unique mark value so that we know where we've been. This allows
|
||||
// the algorithm to avoid getting into an infinite loop even if the
|
||||
// constraint graph has an inadvertent cycle.
|
||||
incrementalAdd(constraint) {
|
||||
var mark = newMark()
|
||||
var overridden = constraint.satisfy(mark)
|
||||
while (overridden != null) {
|
||||
overridden = overridden.satisfy(mark)
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for retracting a constraint. Remove the given
|
||||
// constraint and incrementally update the dataflow graph.
|
||||
// Details: Retracting the given constraint may allow some currently
|
||||
// unsatisfiable downstream constraint to be satisfied. We therefore collect
|
||||
// a list of unsatisfied downstream constraints and attempt to
|
||||
// satisfy each one in turn. This list is traversed by constraint
|
||||
// strength, strongest first, as a heuristic for avoiding
|
||||
// unnecessarily adding and then overriding weak constraints.
|
||||
// Assume: [c] is satisfied.
|
||||
incrementalRemove(constraint) {
|
||||
var out = constraint.output
|
||||
constraint.markUnsatisfied()
|
||||
constraint.removeFromGraph()
|
||||
var unsatisfied = removePropagateFrom(out)
|
||||
var strength = REQUIRED
|
||||
while (true) {
|
||||
for (u in unsatisfied) {
|
||||
if (u.strength == strength) incrementalAdd(u)
|
||||
}
|
||||
strength = strength.nextWeaker
|
||||
if (strength == WEAKEST) break
|
||||
}
|
||||
}
|
||||
|
||||
// Select a previously unused mark value.
|
||||
newMark() { _currentMark = _currentMark + 1 }
|
||||
|
||||
// Extract a plan for resatisfaction starting from the given source
|
||||
// constraints, usually a set of input constraints. This method
|
||||
// assumes that stay optimization is desired; the plan will contain
|
||||
// only constraints whose output variables are not stay. Constraints
|
||||
// that do no computation, such as stay and edit constraints, are
|
||||
// not included in the plan.
|
||||
// Details: The outputs of a constraint are marked when it is added
|
||||
// to the plan under construction. A constraint may be appended to
|
||||
// the plan when all its input variables are known. A variable is
|
||||
// known if either a) the variable is marked (indicating that has
|
||||
// been computed by a constraint appearing earlier in the plan), b)
|
||||
// the variable is 'stay' (i.e. it is a constant at plan execution
|
||||
// time), or c) the variable is not determined by any
|
||||
// constraint. The last provision is for past states of history
|
||||
// variables, which are not stay but which are also not computed by
|
||||
// any constraint.
|
||||
// Assume: [sources] are all satisfied.
|
||||
makePlan(sources) {
|
||||
var mark = newMark()
|
||||
var plan = Plan.new()
|
||||
var todo = sources
|
||||
while (todo.count > 0) {
|
||||
var constraint = todo.removeAt(-1)
|
||||
if (constraint.output.mark != mark && constraint.inputsKnown(mark)) {
|
||||
plan.addConstraint(constraint)
|
||||
constraint.output.mark = mark
|
||||
addConstraintsConsumingTo(constraint.output, todo)
|
||||
}
|
||||
}
|
||||
return plan
|
||||
}
|
||||
|
||||
// Extract a plan for resatisfying starting from the output of the
|
||||
// given [constraints], usually a set of input constraints.
|
||||
extractPlanFromConstraints(constraints) {
|
||||
var sources = []
|
||||
for (constraint in constraints) {
|
||||
// if not in plan already and eligible for inclusion.
|
||||
if (constraint.isInput && constraint.isSatisfied) sources.add(constraint)
|
||||
}
|
||||
return makePlan(sources)
|
||||
}
|
||||
|
||||
// Recompute the walkabout strengths and stay flags of all variables
|
||||
// downstream of the given constraint and recompute the actual
|
||||
// values of all variables whose stay flag is true. If a cycle is
|
||||
// detected, remove the given constraint and answer
|
||||
// false. Otherwise, answer true.
|
||||
// Details: Cycles are detected when a marked variable is
|
||||
// encountered downstream of the given constraint. The sender is
|
||||
// assumed to have marked the inputs of the given constraint with
|
||||
// the given mark. Thus, encountering a marked node downstream of
|
||||
// the output constraint means that there is a path from the
|
||||
// constraint's output to one of its inputs.
|
||||
addPropagate(constraint, mark) {
|
||||
var todo = [constraint]
|
||||
while (todo.count > 0) {
|
||||
var d = todo.removeAt(-1)
|
||||
if (d.output.mark == mark) {
|
||||
incrementalRemove(constraint)
|
||||
return false
|
||||
}
|
||||
|
||||
d.recalculate()
|
||||
addConstraintsConsumingTo(d.output, todo)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Update the walkabout strengths and stay flags of all variables
|
||||
// downstream of the given constraint. Answer a collection of
|
||||
// unsatisfied constraints sorted in order of decreasing strength.
|
||||
removePropagateFrom(out) {
|
||||
out.determinedBy = null
|
||||
out.walkStrength = WEAKEST
|
||||
out.stay = true
|
||||
var unsatisfied = []
|
||||
var todo = [out]
|
||||
while (todo.count > 0) {
|
||||
var v = todo.removeAt(-1)
|
||||
for (constraint in v.constraints) {
|
||||
if (!constraint.isSatisfied) unsatisfied.add(constraint)
|
||||
}
|
||||
|
||||
var determining = v.determinedBy
|
||||
for (next in v.constraints) {
|
||||
if (next != determining && next.isSatisfied) {
|
||||
next.recalculate()
|
||||
todo.add(next.output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unsatisfied
|
||||
}
|
||||
|
||||
addConstraintsConsumingTo(v, coll) {
|
||||
var determining = v.determinedBy
|
||||
for (constraint in v.constraints) {
|
||||
if (constraint != determining && constraint.isSatisfied) {
|
||||
coll.add(constraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var total = 0
|
||||
|
||||
// This is the standard DeltaBlue benchmark. A long chain of equality
|
||||
// constraints is constructed with a stay constraint on one end. An
|
||||
// edit constraint is then added to the opposite end and the time is
|
||||
// measured for adding and removing this constraint, and extracting
|
||||
// and executing a constraint satisfaction plan. There are two cases.
|
||||
// In case 1, the added constraint is stronger than the stay
|
||||
// constraint and values must propagate down the entire length of the
|
||||
// chain. In case 2, the added constraint is weaker than the stay
|
||||
// constraint so it cannot be accomodated. The cost in this case is,
|
||||
// of course, very low. Typical situations lie somewhere between these
|
||||
// two extremes.
|
||||
var chainTest = Fn.new {|n|
|
||||
ThePlanner = Planner.new()
|
||||
var prev = null
|
||||
var first = null
|
||||
var last = null
|
||||
|
||||
// Build chain of n equality constraints.
|
||||
for (i in 0..n) {
|
||||
var v = Variable.new("v", 0)
|
||||
if (prev != null) EqualityConstraint.new(prev, v, REQUIRED)
|
||||
if (i == 0) first = v
|
||||
if (i == n) last = v
|
||||
prev = v
|
||||
}
|
||||
|
||||
StayConstraint.new(last, STRONG_DEFAULT)
|
||||
var edit = EditConstraint.new(first, PREFERRED)
|
||||
var plan = ThePlanner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...100) {
|
||||
first.value = i
|
||||
plan.execute()
|
||||
total = total + last.value
|
||||
}
|
||||
}
|
||||
|
||||
var change = Fn.new {|v, newValue|
|
||||
var edit = EditConstraint.new(v, PREFERRED)
|
||||
var plan = ThePlanner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...10) {
|
||||
v.value = newValue
|
||||
plan.execute()
|
||||
}
|
||||
|
||||
edit.destroyConstraint()
|
||||
}
|
||||
|
||||
// This test constructs a two sets of variables related to each
|
||||
// other by a simple linear transformation (scale and offset). The
|
||||
// time is measured to change a variable on either side of the
|
||||
// mapping and to change the scale and offset factors.
|
||||
var projectionTest = Fn.new {|n|
|
||||
ThePlanner = Planner.new()
|
||||
var scale = Variable.new("scale", 10)
|
||||
var offset = Variable.new("offset", 1000)
|
||||
var src = null
|
||||
var dst = null
|
||||
|
||||
var dests = []
|
||||
for (i in 0...n) {
|
||||
src = Variable.new("src", i)
|
||||
dst = Variable.new("dst", i)
|
||||
dests.add(dst)
|
||||
StayConstraint.new(src, NORMAL)
|
||||
ScaleConstraint.new(src, scale, offset, dst, REQUIRED)
|
||||
}
|
||||
|
||||
change.call(src, 17)
|
||||
total = total + dst.value
|
||||
if (dst.value != 1170) System.print("Projection 1 failed")
|
||||
|
||||
change.call(dst, 1050)
|
||||
|
||||
total = total + src.value
|
||||
if (src.value != 5) System.print("Projection 2 failed")
|
||||
|
||||
change.call(scale, 5)
|
||||
for (i in 0...n - 1) {
|
||||
total = total + dests[i].value
|
||||
if (dests[i].value != i * 5 + 1000) System.print("Projection 3 failed")
|
||||
}
|
||||
|
||||
change.call(offset, 2000)
|
||||
for (i in 0...n - 1) {
|
||||
total = total + dests[i].value
|
||||
if (dests[i].value != i * 5 + 2000) System.print("Projection 4 failed")
|
||||
}
|
||||
}
|
||||
|
||||
var start = System.clock
|
||||
for (i in 0...40) {
|
||||
chainTest.call(100)
|
||||
projectionTest.call(100)
|
||||
}
|
||||
|
||||
System.print(total)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,48 +0,0 @@
|
||||
-- The Computer Language Benchmarks Game
|
||||
-- http://benchmarksgame.alioth.debian.org/
|
||||
-- contributed by Mike Pall
|
||||
|
||||
local function fannkuch(n)
|
||||
local p, q, s, sign, maxflips, sum = {}, {}, {}, 1, 0, 0
|
||||
for i=1,n do p[i] = i; q[i] = i; s[i] = i end
|
||||
repeat
|
||||
-- Copy and flip.
|
||||
local q1 = p[1] -- Cache 1st element.
|
||||
if q1 ~= 1 then
|
||||
for i=2,n do q[i] = p[i] end -- Work on a copy.
|
||||
local flips = 1
|
||||
repeat
|
||||
local qq = q[q1]
|
||||
if qq == 1 then -- ... until 1st element is 1.
|
||||
sum = sum + sign*flips
|
||||
if flips > maxflips then maxflips = flips end -- New maximum?
|
||||
break
|
||||
end
|
||||
q[q1] = q1
|
||||
if q1 >= 4 then
|
||||
local i, j = 2, q1 - 1
|
||||
repeat q[i], q[j] = q[j], q[i]; i = i + 1; j = j - 1; until i >= j
|
||||
end
|
||||
q1 = qq; flips = flips + 1
|
||||
until false
|
||||
end
|
||||
-- Permute.
|
||||
if sign == 1 then
|
||||
p[2], p[1] = p[1], p[2]; sign = -1 -- Rotate 1<-2.
|
||||
else
|
||||
p[2], p[3] = p[3], p[2]; sign = 1 -- Rotate 1<-2 and 1<-2<-3.
|
||||
for i=3,n do
|
||||
local sx = s[i]
|
||||
if sx ~= 1 then s[i] = sx-1; break end
|
||||
if i == n then return sum, maxflips end -- Out of permutations.
|
||||
s[i] = i
|
||||
-- Rotate 1<-...<-i+1.
|
||||
local t = p[1]; for j=1,i do p[j] = p[j+1] end; p[i+1] = t
|
||||
end
|
||||
end
|
||||
until false
|
||||
end
|
||||
|
||||
local n = 9
|
||||
local sum, flips = fannkuch(n)
|
||||
io.write(sum, "\nPfannkuchen(", n, ") = ", flips, "\n")
|
||||
@ -1,56 +0,0 @@
|
||||
# The Computer Language Benchmarks Game
|
||||
# http://benchmarksgame.alioth.debian.org/
|
||||
|
||||
# contributed by Isaac Gouy
|
||||
# converted to Java by Oleg Mazurov
|
||||
# converted to Python by Buck Golemon
|
||||
# modified by Justin Peel
|
||||
|
||||
def fannkuch(n):
|
||||
maxFlipsCount = 0
|
||||
permSign = True
|
||||
checksum = 0
|
||||
|
||||
perm1 = list(range(n))
|
||||
count = perm1[:]
|
||||
rxrange = range(2, n - 1)
|
||||
nm = n - 1
|
||||
while 1:
|
||||
k = perm1[0]
|
||||
if k:
|
||||
perm = perm1[:]
|
||||
flipsCount = 1
|
||||
kk = perm[k]
|
||||
while kk:
|
||||
perm[:k+1] = perm[k::-1]
|
||||
flipsCount += 1
|
||||
k = kk
|
||||
kk = perm[kk]
|
||||
if maxFlipsCount < flipsCount:
|
||||
maxFlipsCount = flipsCount
|
||||
checksum += flipsCount if permSign else -flipsCount
|
||||
|
||||
# Use incremental change to generate another permutation
|
||||
if permSign:
|
||||
perm1[0],perm1[1] = perm1[1],perm1[0]
|
||||
permSign = False
|
||||
else:
|
||||
perm1[1],perm1[2] = perm1[2],perm1[1]
|
||||
permSign = True
|
||||
for r in rxrange:
|
||||
if count[r]:
|
||||
break
|
||||
count[r] = r
|
||||
perm0 = perm1[0]
|
||||
perm1[:r+1] = perm1[1:r+2]
|
||||
perm1[r+1] = perm0
|
||||
else:
|
||||
r = nm
|
||||
if not count[r]:
|
||||
print( checksum )
|
||||
return maxFlipsCount
|
||||
count[r] -= 1
|
||||
|
||||
n = 9
|
||||
|
||||
print(( "Pfannkuchen(%i) = %i" % (n, fannkuch(n)) ))
|
||||
@ -1,60 +0,0 @@
|
||||
def fannkuch(n)
|
||||
p = (0..n).to_a
|
||||
s = p.dup
|
||||
q = p.dup
|
||||
sign = 1
|
||||
sum = maxflips = 0
|
||||
while(true)
|
||||
# flip.
|
||||
|
||||
if (q1 = p[1]) != 1
|
||||
q[0..-1] = p
|
||||
flips = 1
|
||||
until (qq = q[q1]) == 1
|
||||
q[q1] = q1
|
||||
if q1 >= 4
|
||||
i, j = 2, q1 - 1
|
||||
while i < j
|
||||
q[i], q[j] = q[j], q[i]
|
||||
i += 1
|
||||
j -= 1
|
||||
end
|
||||
end
|
||||
q1 = qq
|
||||
flips += 1
|
||||
end
|
||||
sum += sign * flips
|
||||
maxflips = flips if flips > maxflips # New maximum?
|
||||
|
||||
end
|
||||
# Permute.
|
||||
|
||||
if sign == 1
|
||||
# Rotate 1<-2.
|
||||
|
||||
p[1], p[2] = p[2], p[1]
|
||||
sign = -1
|
||||
else
|
||||
# Rotate 1<-2 and 1<-2<-3.
|
||||
|
||||
p[2], p[3] = p[3], p[2]
|
||||
sign = 1
|
||||
i = 3
|
||||
while i <= n && s[i] == 1
|
||||
return [sum, maxflips] if i == n # Out of permutations.
|
||||
|
||||
s[i] = i
|
||||
# Rotate 1<-...<-i+1.
|
||||
|
||||
t = p.delete_at(1)
|
||||
i += 1
|
||||
p.insert(i, t)
|
||||
end
|
||||
s[i] -= 1 if i <= n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
n = 9
|
||||
sum, flips = fannkuch(n)
|
||||
printf "%d\nPfannkuchen(%d) = %d\n", sum, n, flips
|
||||
@ -1,13 +0,0 @@
|
||||
fib(n) {
|
||||
if (n < 2) return n;
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
main() {
|
||||
Stopwatch watch = new Stopwatch();
|
||||
watch.start();
|
||||
for (var i = 0; i < 5; i++) {
|
||||
print(fib(28));
|
||||
}
|
||||
print("elapsed: ${watch.elapsedMilliseconds / 1000}");
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
function fib(n)
|
||||
if n < 2 then return n end
|
||||
return fib(n - 2) + fib(n - 1)
|
||||
end
|
||||
|
||||
local start = os.clock()
|
||||
for i = 1, 5 do
|
||||
io.write(fib(28) .. "\n")
|
||||
end
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,12 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
def fib(n):
|
||||
if n < 2: return n
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
|
||||
start = time.clock()
|
||||
for i in range(0, 5):
|
||||
print(fib(28))
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,13 +0,0 @@
|
||||
def fib(n)
|
||||
if n < 2 then
|
||||
n
|
||||
else
|
||||
fib(n - 1) + fib(n - 2)
|
||||
end
|
||||
end
|
||||
|
||||
start = Time.now
|
||||
for i in 0...5
|
||||
puts fib(28)
|
||||
end
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,12 +0,0 @@
|
||||
class Fib {
|
||||
static get(n) {
|
||||
if (n < 2) return n
|
||||
return get(n - 1) + get(n - 2)
|
||||
}
|
||||
}
|
||||
|
||||
var start = System.clock
|
||||
for (i in 1..5) {
|
||||
System.print(Fib.get(28))
|
||||
}
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,16 +0,0 @@
|
||||
// Creates 100000 fibers. Each one calls the next in a chain until the last.
|
||||
var fibers = []
|
||||
var sum = 0
|
||||
|
||||
var start = System.clock
|
||||
|
||||
for (i in 0...100000) {
|
||||
fibers.add(Fiber.new {
|
||||
sum = sum + i
|
||||
if (i < 99999) fibers[i + 1].call()
|
||||
})
|
||||
}
|
||||
|
||||
fibers[0].call()
|
||||
System.print(sum)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,13 +0,0 @@
|
||||
main() {
|
||||
var list = [];
|
||||
|
||||
Stopwatch watch = new Stopwatch();
|
||||
watch.start();
|
||||
for (var i = 0; i < 1000000; i++) list.add(i);
|
||||
|
||||
var sum = 0;
|
||||
for (i in list) sum += i;
|
||||
|
||||
print(sum);
|
||||
print("elapsed: ${watch.elapsedMilliseconds / 1000}");
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
local start = os.clock()
|
||||
local list = {}
|
||||
for i = 0, 999999 do
|
||||
list[i] = i
|
||||
end
|
||||
|
||||
local sum = 0
|
||||
for k, i in pairs(list) do
|
||||
sum = sum + i
|
||||
end
|
||||
io.write(sum .. "\n")
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,20 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
# Map "range" to an efficient range in both Python 2 and 3.
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
start = time.clock()
|
||||
list = []
|
||||
for i in range(0, 1000000):
|
||||
list.append(i)
|
||||
|
||||
sum = 0
|
||||
for i in list:
|
||||
sum += i
|
||||
print(sum)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,8 +0,0 @@
|
||||
start = Time.now
|
||||
list = []
|
||||
1000000.times {|i| list << i}
|
||||
|
||||
sum = 0
|
||||
list.each {|i| sum += i}
|
||||
puts sum
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,10 +0,0 @@
|
||||
var list = []
|
||||
|
||||
var start = System.clock
|
||||
for (i in 0...1000000) list.add(i)
|
||||
|
||||
var sum = 0
|
||||
for (i in list) sum = sum + i
|
||||
|
||||
System.print(sum)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,24 +0,0 @@
|
||||
// Skipping this for now because it is >10x slower than the other languages (!)
|
||||
// and makes the benchmark running annoyingly slow.
|
||||
main() {
|
||||
Stopwatch watch = new Stopwatch();
|
||||
watch.start();
|
||||
|
||||
var map = {};
|
||||
|
||||
for (var i = 1; i <= 2000000; i++) {
|
||||
map[i] = i;
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var i = 1; i <= 2000000; i++) {
|
||||
sum += map[i];
|
||||
}
|
||||
print(sum);
|
||||
|
||||
for (var i = 1; i <= 2000000; i++) {
|
||||
map.remove(i);
|
||||
}
|
||||
|
||||
print("elapsed: ${watch.elapsedMilliseconds / 1000}");
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
local start = os.clock()
|
||||
|
||||
local map = {}
|
||||
|
||||
for i = 1, 2000000 do
|
||||
map[i] = i
|
||||
end
|
||||
|
||||
local sum = 0
|
||||
for i = 1, 2000000 do
|
||||
sum = sum + map[i]
|
||||
end
|
||||
io.write(string.format("%d\n", sum))
|
||||
|
||||
for i = 1, 2000000 do
|
||||
map[i] = nil
|
||||
end
|
||||
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,20 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
start = time.clock()
|
||||
|
||||
map = {}
|
||||
|
||||
for i in range(1, 2000001):
|
||||
map[i] = i
|
||||
|
||||
sum = 0
|
||||
for i in range(1, 2000001):
|
||||
sum = sum + map[i]
|
||||
print(sum)
|
||||
|
||||
for i in range(1, 2000001):
|
||||
del map[i]
|
||||
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,19 +0,0 @@
|
||||
start = Time.now
|
||||
|
||||
map = Hash.new
|
||||
|
||||
for i in (1..2000000)
|
||||
map[i] = i
|
||||
end
|
||||
|
||||
sum = 0
|
||||
for i in (1..2000000)
|
||||
sum = sum + map[i]
|
||||
end
|
||||
puts sum
|
||||
|
||||
for i in (1..2000000)
|
||||
map.delete(i)
|
||||
end
|
||||
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,19 +0,0 @@
|
||||
var start = System.clock
|
||||
|
||||
var map = {}
|
||||
|
||||
for (i in 1..2000000) {
|
||||
map[i] = i
|
||||
}
|
||||
|
||||
var sum = 0
|
||||
for (i in 1..2000000) {
|
||||
sum = sum + map[i]
|
||||
}
|
||||
System.print(sum)
|
||||
|
||||
for (i in 1..2000000) {
|
||||
map.remove(i)
|
||||
}
|
||||
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,99 +0,0 @@
|
||||
local adverbs = {
|
||||
"moderately", "really", "slightly", "very"
|
||||
}
|
||||
|
||||
local adjectives = {
|
||||
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
|
||||
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
|
||||
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
|
||||
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
|
||||
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
|
||||
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
|
||||
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
|
||||
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
|
||||
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
|
||||
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
|
||||
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
|
||||
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
|
||||
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
|
||||
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
|
||||
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
|
||||
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
|
||||
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
|
||||
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
|
||||
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
|
||||
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
|
||||
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
|
||||
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
|
||||
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
|
||||
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
|
||||
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
|
||||
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
|
||||
"common", "compassionate", "competent", "complete", "complex", "complicated",
|
||||
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
|
||||
"constant", "content", "conventional", "cooked", "cool", "cooperative",
|
||||
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
|
||||
"crafty"
|
||||
}
|
||||
|
||||
local animals = {
|
||||
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
|
||||
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
|
||||
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
|
||||
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
|
||||
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
|
||||
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
|
||||
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
|
||||
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
|
||||
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
|
||||
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
|
||||
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
|
||||
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
|
||||
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
|
||||
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
|
||||
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
|
||||
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
|
||||
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
|
||||
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
|
||||
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
|
||||
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
|
||||
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
|
||||
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
|
||||
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
|
||||
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
|
||||
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
|
||||
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
|
||||
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
|
||||
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
|
||||
"wallaby", "wildebeest"
|
||||
}
|
||||
|
||||
local keys = {}
|
||||
for _, animal in ipairs(animals) do
|
||||
for _, adjective in ipairs(adjectives) do
|
||||
for _, adverb in ipairs(adverbs) do
|
||||
table.insert(keys, adverb .. " " .. adjective .. " " .. animal)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local start = os.clock()
|
||||
|
||||
local map = {}
|
||||
local i = 0
|
||||
for _, key in ipairs(keys) do
|
||||
map[key] = i
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
local sum = 0
|
||||
for _, key in ipairs(keys) do
|
||||
sum = sum + map[key]
|
||||
end
|
||||
|
||||
for _, key in ipairs(keys) do
|
||||
map[key] = nil
|
||||
end
|
||||
|
||||
io.write(string.format("%d\n", sum))
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@ -1,97 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
adverbs = [
|
||||
"moderately", "really", "slightly", "very"
|
||||
]
|
||||
|
||||
adjectives = [
|
||||
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
|
||||
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
|
||||
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
|
||||
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
|
||||
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
|
||||
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
|
||||
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
|
||||
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
|
||||
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
|
||||
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
|
||||
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
|
||||
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
|
||||
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
|
||||
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
|
||||
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
|
||||
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
|
||||
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
|
||||
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
|
||||
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
|
||||
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
|
||||
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
|
||||
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
|
||||
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
|
||||
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
|
||||
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
|
||||
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
|
||||
"common", "compassionate", "competent", "complete", "complex", "complicated",
|
||||
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
|
||||
"constant", "content", "conventional", "cooked", "cool", "cooperative",
|
||||
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
|
||||
"crafty"
|
||||
]
|
||||
|
||||
animals = [
|
||||
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
|
||||
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
|
||||
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
|
||||
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
|
||||
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
|
||||
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
|
||||
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
|
||||
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
|
||||
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
|
||||
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
|
||||
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
|
||||
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
|
||||
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
|
||||
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
|
||||
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
|
||||
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
|
||||
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
|
||||
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
|
||||
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
|
||||
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
|
||||
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
|
||||
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
|
||||
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
|
||||
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
|
||||
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
|
||||
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
|
||||
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
|
||||
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
|
||||
"wallaby", "wildebeest"
|
||||
]
|
||||
|
||||
keys = []
|
||||
for animal in animals:
|
||||
for adjective in adjectives:
|
||||
for adverb in adverbs:
|
||||
keys.append(adverb + " " + adjective + " " + animal)
|
||||
|
||||
start = time.clock()
|
||||
|
||||
map = {}
|
||||
i = 0
|
||||
for key in keys:
|
||||
map[key] = i
|
||||
i += 1
|
||||
|
||||
sum = 0
|
||||
for key in keys:
|
||||
sum = sum + map[key]
|
||||
|
||||
for key in keys:
|
||||
del map[key]
|
||||
|
||||
print(sum)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,99 +0,0 @@
|
||||
adverbs = [
|
||||
"moderately", "really", "slightly", "very"
|
||||
]
|
||||
|
||||
adjectives = [
|
||||
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
|
||||
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
|
||||
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
|
||||
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
|
||||
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
|
||||
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
|
||||
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
|
||||
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
|
||||
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
|
||||
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
|
||||
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
|
||||
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
|
||||
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
|
||||
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
|
||||
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
|
||||
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
|
||||
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
|
||||
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
|
||||
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
|
||||
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
|
||||
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
|
||||
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
|
||||
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
|
||||
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
|
||||
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
|
||||
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
|
||||
"common", "compassionate", "competent", "complete", "complex", "complicated",
|
||||
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
|
||||
"constant", "content", "conventional", "cooked", "cool", "cooperative",
|
||||
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
|
||||
"crafty"
|
||||
]
|
||||
|
||||
animals = [
|
||||
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
|
||||
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
|
||||
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
|
||||
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
|
||||
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
|
||||
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
|
||||
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
|
||||
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
|
||||
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
|
||||
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
|
||||
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
|
||||
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
|
||||
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
|
||||
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
|
||||
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
|
||||
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
|
||||
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
|
||||
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
|
||||
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
|
||||
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
|
||||
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
|
||||
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
|
||||
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
|
||||
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
|
||||
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
|
||||
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
|
||||
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
|
||||
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
|
||||
"wallaby", "wildebeest"
|
||||
]
|
||||
|
||||
keys = []
|
||||
for animal in animals
|
||||
for adjective in adjectives
|
||||
for adverb in adverbs
|
||||
keys << adverb + " " + adjective + " " + animal
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
start = Time.now
|
||||
|
||||
map = Hash.new
|
||||
i = 0
|
||||
for key in keys
|
||||
map[key] = i
|
||||
i += 1
|
||||
end
|
||||
|
||||
sum = 0
|
||||
for key in keys
|
||||
sum = sum + map[key]
|
||||
end
|
||||
|
||||
for key in keys
|
||||
map.delete(key)
|
||||
end
|
||||
|
||||
puts sum
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@ -1,100 +0,0 @@
|
||||
var adverbs = [
|
||||
"moderately", "really", "slightly", "very"
|
||||
]
|
||||
|
||||
var adjectives = [
|
||||
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
|
||||
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
|
||||
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
|
||||
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
|
||||
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
|
||||
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
|
||||
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
|
||||
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
|
||||
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
|
||||
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
|
||||
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
|
||||
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
|
||||
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
|
||||
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
|
||||
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
|
||||
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
|
||||
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
|
||||
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
|
||||
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
|
||||
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
|
||||
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
|
||||
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
|
||||
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
|
||||
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
|
||||
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
|
||||
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
|
||||
"common", "compassionate", "competent", "complete", "complex", "complicated",
|
||||
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
|
||||
"constant", "content", "conventional", "cooked", "cool", "cooperative",
|
||||
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
|
||||
"crafty"
|
||||
]
|
||||
|
||||
var animals = [
|
||||
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
|
||||
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
|
||||
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
|
||||
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
|
||||
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
|
||||
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
|
||||
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
|
||||
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
|
||||
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
|
||||
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
|
||||
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
|
||||
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
|
||||
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
|
||||
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
|
||||
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
|
||||
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
|
||||
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
|
||||
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
|
||||
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
|
||||
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
|
||||
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
|
||||
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
|
||||
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
|
||||
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
|
||||
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
|
||||
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
|
||||
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
|
||||
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
|
||||
"wallaby", "wildebeest"
|
||||
]
|
||||
|
||||
var keys = []
|
||||
for (animal in animals) {
|
||||
for (adjective in adjectives) {
|
||||
for (adverb in adverbs) {
|
||||
keys.add(adverb + " " + adjective + " " + animal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var start = System.clock
|
||||
|
||||
var map = {}
|
||||
|
||||
var i = 0
|
||||
for (key in keys) {
|
||||
map[key] = i
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
var sum = 0
|
||||
for (key in keys) {
|
||||
sum = sum + map[key]
|
||||
}
|
||||
|
||||
for (key in keys) {
|
||||
map.remove(key)
|
||||
}
|
||||
|
||||
System.print(sum)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,78 +0,0 @@
|
||||
class Toggle {
|
||||
var _state;
|
||||
|
||||
Toggle(startState) {
|
||||
_state = startState;
|
||||
}
|
||||
|
||||
get value => _state;
|
||||
|
||||
activate() {
|
||||
_state = !_state;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class NthToggle extends Toggle {
|
||||
var _count;
|
||||
var _countMax;
|
||||
|
||||
NthToggle(startState, maxCounter)
|
||||
: super(startState) {
|
||||
_countMax = maxCounter;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
activate() {
|
||||
_count = _count + 1;
|
||||
if (_count >= _countMax) {
|
||||
super.activate();
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
Stopwatch watch = new Stopwatch();
|
||||
watch.start();
|
||||
|
||||
var n = 100000;
|
||||
var val = true;
|
||||
var toggle = new Toggle(val);
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
val = toggle.activate().value;
|
||||
}
|
||||
|
||||
print(toggle.value);
|
||||
|
||||
val = true;
|
||||
var ntoggle = new NthToggle(val, 3);
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
val = ntoggle.activate().value;
|
||||
}
|
||||
|
||||
print(ntoggle.value);
|
||||
print("elapsed: ${watch.elapsedMilliseconds / 1000}");
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
-- $Id: methcall.lua,v 1.2 2004-06-12 16:19:43 bfulgham Exp $
|
||||
-- http://shootout.alioth.debian.org
|
||||
-- contributed by Roberto Ierusalimschy
|
||||
|
||||
--------------------------------------------------------------
|
||||
-- Toggle class
|
||||
--------------------------------------------------------------
|
||||
|
||||
Toggle = {}
|
||||
|
||||
function Toggle:value ()
|
||||
return self.state
|
||||
end
|
||||
|
||||
function Toggle:activate ()
|
||||
self.state = not self.state
|
||||
return self
|
||||
end
|
||||
|
||||
function Toggle:new (start_state)
|
||||
local o = {state = start_state}
|
||||
self.__index =self
|
||||
setmetatable(o, self)
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------
|
||||
-- NthToggle class
|
||||
--------------------------------------------------------------
|
||||
|
||||
NthToggle = Toggle:new()
|
||||
|
||||
function NthToggle:activate ()
|
||||
self.counter = self.counter + 1
|
||||
if self.counter >= self.count_max then
|
||||
Toggle.activate(self)
|
||||
self.counter = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function NthToggle:new (start_state, max_counter)
|
||||
local o = Toggle.new(self, start_state)
|
||||
o.count_max = max_counter
|
||||
o.counter = 0
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- main
|
||||
-----------------------------------------------------------
|
||||
|
||||
function main ()
|
||||
local start = os.clock()
|
||||
local N = 100000
|
||||
|
||||
local val = 1
|
||||
local toggle = Toggle:new(val)
|
||||
for i=1,N do
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
val = toggle:activate():value()
|
||||
end
|
||||
print(val and "true" or "false")
|
||||
|
||||
val = 1
|
||||
local ntoggle = NthToggle:new(val, 3)
|
||||
for i=1,N do
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
val = ntoggle:activate():value()
|
||||
end
|
||||
print(val and "true" or "false")
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
end
|
||||
|
||||
main()
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# http://www.bagley.org/~doug/shootout/
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Map "range" to an efficient range in both Python 2 and 3.
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
class Toggle(object):
|
||||
def __init__(self, start_state):
|
||||
self.bool = start_state
|
||||
def value(self):
|
||||
return(self.bool)
|
||||
def activate(self):
|
||||
self.bool = not self.bool
|
||||
return(self)
|
||||
|
||||
class NthToggle(Toggle):
|
||||
def __init__(self, start_state, max_counter):
|
||||
Toggle.__init__(self, start_state)
|
||||
self.count_max = max_counter
|
||||
self.counter = 0
|
||||
def activate(self):
|
||||
self.counter += 1
|
||||
if (self.counter >= self.count_max):
|
||||
super(NthToggle, self).activate()
|
||||
self.counter = 0
|
||||
return(self)
|
||||
|
||||
|
||||
def main():
|
||||
start = time.clock()
|
||||
|
||||
NUM = 100000
|
||||
|
||||
val = 1
|
||||
toggle = Toggle(val)
|
||||
for i in range(0,NUM):
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
if val:
|
||||
print("true")
|
||||
else:
|
||||
print("false")
|
||||
|
||||
val = 1
|
||||
ntoggle = NthToggle(val, 3)
|
||||
for i in range(0,NUM):
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
if val:
|
||||
print("true")
|
||||
else:
|
||||
print("false")
|
||||
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
|
||||
|
||||
main()
|
||||
@ -1,79 +0,0 @@
|
||||
#!/usr/bin/ruby
|
||||
# -*- mode: ruby -*-
|
||||
# $Id: methcall.ruby,v 1.1 2004-05-19 18:10:41 bfulgham Exp $
|
||||
# http://www.bagley.org/~doug/shootout/
|
||||
# with help from Aristarkh Zagorodnikov
|
||||
|
||||
class Toggle
|
||||
def initialize(start_state)
|
||||
@bool = start_state
|
||||
end
|
||||
|
||||
def value
|
||||
@bool
|
||||
end
|
||||
|
||||
def activate
|
||||
@bool = !@bool
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class NthToggle < Toggle
|
||||
def initialize(start_state, max_counter)
|
||||
super start_state
|
||||
@count_max = max_counter
|
||||
@counter = 0
|
||||
end
|
||||
|
||||
def activate
|
||||
@counter += 1
|
||||
if @counter >= @count_max
|
||||
super
|
||||
@counter = 0
|
||||
end
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def main()
|
||||
start = Time.now
|
||||
|
||||
n = 100000
|
||||
|
||||
val = 1
|
||||
toggle = Toggle.new(val)
|
||||
n.times do
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
val = toggle.activate().value()
|
||||
end
|
||||
if val then puts "true" else puts "false" end
|
||||
|
||||
val = 1
|
||||
ntoggle = NthToggle.new(val, 3)
|
||||
n.times do
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
val = ntoggle.activate().value()
|
||||
end
|
||||
if val then puts "true" else puts "false" end
|
||||
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
end
|
||||
|
||||
main()
|
||||
@ -1,68 +0,0 @@
|
||||
class Toggle {
|
||||
construct new(startState) {
|
||||
_state = startState
|
||||
}
|
||||
|
||||
value { _state }
|
||||
activate {
|
||||
_state = !_state
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
class NthToggle is Toggle {
|
||||
construct new(startState, maxCounter) {
|
||||
super(startState)
|
||||
_countMax = maxCounter
|
||||
_count = 0
|
||||
}
|
||||
|
||||
activate {
|
||||
_count = _count + 1
|
||||
if (_count >= _countMax) {
|
||||
super.activate
|
||||
_count = 0
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
var start = System.clock
|
||||
var n = 100000
|
||||
var val = true
|
||||
var toggle = Toggle.new(val)
|
||||
|
||||
for (i in 0...n) {
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
val = toggle.activate.value
|
||||
}
|
||||
|
||||
System.print(toggle.value)
|
||||
|
||||
val = true
|
||||
var ntoggle = NthToggle.new(val, 3)
|
||||
|
||||
for (i in 0...n) {
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
val = ntoggle.activate.value
|
||||
}
|
||||
|
||||
System.print(ntoggle.value)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,35 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
start = time.clock()
|
||||
|
||||
count = 0
|
||||
for i in range(0, 1000000):
|
||||
if "abc" == "abc":
|
||||
count = count + 1
|
||||
if "a slightly longer string" == \
|
||||
"a slightly longer string":
|
||||
count = count + 1
|
||||
if "a significantly longer string but still not overwhelmingly long string" == \
|
||||
"a significantly longer string but still not overwhelmingly long string":
|
||||
count = count + 1
|
||||
|
||||
if "" == "abc":
|
||||
count = count + 1
|
||||
if "abc" == "abcd":
|
||||
count = count + 1
|
||||
if "changed one character" == "changed !ne character":
|
||||
count = count + 1
|
||||
if "123" == 123: count = count + 1
|
||||
if "a slightly longer string" == \
|
||||
"a slightly longer string!":
|
||||
count = count + 1
|
||||
if "a slightly longer string" == \
|
||||
"a slightly longer strinh":
|
||||
count = count + 1
|
||||
if "a significantly longer string but still not overwhelmingly long string" == \
|
||||
"another":
|
||||
count = count + 1
|
||||
|
||||
print(count)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@ -1,24 +0,0 @@
|
||||
var start = System.clock
|
||||
|
||||
var count = 0
|
||||
for (i in 1..1000000) {
|
||||
if ("abc" == "abc") count = count + 1
|
||||
if ("a slightly longer string" ==
|
||||
"a slightly longer string") count = count + 1
|
||||
if ("a significantly longer string but still not overwhelmingly long string" ==
|
||||
"a significantly longer string but still not overwhelmingly long string") count = count + 1
|
||||
|
||||
if ("" == "abc") count = count + 1
|
||||
if ("abc" == "abcd") count = count + 1
|
||||
if ("changed one character" == "changed !ne character") count = count + 1
|
||||
if ("123" == 123) count = count + 1
|
||||
if ("a slightly longer string" ==
|
||||
"a slightly longer string!") count = count + 1
|
||||
if ("a slightly longer string" ==
|
||||
"a slightly longer strinh") count = count + 1
|
||||
if ("a significantly longer string but still not overwhelmingly long string" ==
|
||||
"another") count = count + 1
|
||||
}
|
||||
|
||||
System.print(count)
|
||||
System.print("elapsed: %(System.clock - start)")
|
||||
@ -1,23 +0,0 @@
|
||||
System.print(true == true) // expect: true
|
||||
System.print(true == false) // expect: false
|
||||
System.print(false == true) // expect: false
|
||||
System.print(false == false) // expect: true
|
||||
|
||||
// Not equal to other types.
|
||||
System.print(true == 1) // expect: false
|
||||
System.print(false == 0) // expect: false
|
||||
System.print(true == "true") // expect: false
|
||||
System.print(false == "false") // expect: false
|
||||
System.print(false == "") // expect: false
|
||||
|
||||
System.print(true != true) // expect: false
|
||||
System.print(true != false) // expect: true
|
||||
System.print(false != true) // expect: true
|
||||
System.print(false != false) // expect: false
|
||||
|
||||
// Not equal to other types.
|
||||
System.print(true != 1) // expect: true
|
||||
System.print(false != 0) // expect: true
|
||||
System.print(true != "true") // expect: true
|
||||
System.print(false != "false") // expect: true
|
||||
System.print(false != "") // expect: true
|
||||
@ -1 +0,0 @@
|
||||
Bool.new() // expect runtime error: Bool metaclass does not implement 'new()'.
|
||||
@ -1,3 +0,0 @@
|
||||
System.print(!true) // expect: false
|
||||
System.print(!false) // expect: true
|
||||
System.print(!!true) // expect: true
|
||||
@ -1,2 +0,0 @@
|
||||
System.print(true.toString) // expect: true
|
||||
System.print(false.toString) // expect: false
|
||||
@ -1,4 +0,0 @@
|
||||
System.print(true is Bool) // expect: true
|
||||
System.print(true is Object) // expect: true
|
||||
System.print(true is Num) // expect: false
|
||||
System.print(true.type == Bool) // expect: true
|
||||
@ -1,13 +0,0 @@
|
||||
System.print(Num == Num) // expect: true
|
||||
System.print(Num == Bool) // expect: false
|
||||
|
||||
// Not equal to other types.
|
||||
System.print(Num == 123) // expect: false
|
||||
System.print(Num == true) // expect: false
|
||||
|
||||
System.print(Num != Num) // expect: false
|
||||
System.print(Num != Bool) // expect: true
|
||||
|
||||
// Not equal to other types.
|
||||
System.print(Num != 123) // expect: true
|
||||
System.print(Num != true) // expect: true
|
||||
@ -1,13 +0,0 @@
|
||||
class Foo {}
|
||||
|
||||
System.print(Foo.name) // expect: Foo
|
||||
System.print(Foo.type.name) // expect: Foo metaclass
|
||||
|
||||
// Make sure the built-in classes have proper names too.
|
||||
System.print(Object.name) // expect: Object
|
||||
System.print(Bool.name) // expect: Bool
|
||||
System.print(Class.name) // expect: Class
|
||||
|
||||
// And metaclass names.
|
||||
System.print(Object.type.name) // expect: Object metaclass
|
||||
System.print(Bool.type.name) // expect: Bool metaclass
|
||||
@ -1 +0,0 @@
|
||||
Class.new() // expect runtime error: Class does not implement 'new()'.
|
||||
@ -1,15 +0,0 @@
|
||||
class Foo {}
|
||||
|
||||
class Bar is Foo {}
|
||||
|
||||
class Baz is Bar {}
|
||||
|
||||
// A class with no explicit superclass inherits Object.
|
||||
System.print(Foo.supertype == Object) // expect: true
|
||||
|
||||
// Otherwise, it's the superclass.
|
||||
System.print(Bar.supertype == Foo) // expect: true
|
||||
System.print(Baz.supertype == Bar) // expect: true
|
||||
|
||||
// Object has no supertype.
|
||||
System.print(Object.supertype) // expect: null
|
||||
@ -1,15 +0,0 @@
|
||||
class Foo {}
|
||||
|
||||
// A class is a class.
|
||||
System.print(Foo is Class) // expect: true
|
||||
|
||||
// Its metatype is also a class.
|
||||
System.print(Foo.type is Class) // expect: true
|
||||
|
||||
// The metatype's metatype is Class.
|
||||
System.print(Foo.type.type == Class) // expect: true
|
||||
|
||||
// And Class's metatype circles back onto itself.
|
||||
System.print(Foo.type.type.type == Class) // expect: true
|
||||
System.print(Foo.type.type.type.type == Class) // expect: true
|
||||
System.print(Foo.type.type.type.type.type == Class) // expect: true
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user