refactor: move benchmark directory under test and update all references
The benchmark directory is relocated from the project root into the test directory, and all scripts, documentation links, and gitignore paths are updated accordingly. The test runner is also refactored to explicitly walk subdirectories (core, io, language, limit) instead of using a single test root, and a new test/README.md is added to document the test suite structure. Additionally, the constructor test for the Foo class is updated to add a zero-arity constructor and adjust expected output strings.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
This contains the automated validation suite for the VM and built-in libraries.
|
||||
|
||||
* `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.
|
||||
|
||||
* `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.
|
||||
|
||||
* `io/` - Tests for the built in IO library. In other words, methods on the
|
||||
`IO` class. If a bug is in `wren_io.c`, it should 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.
|
||||
@@ -0,0 +1,19 @@
|
||||
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.
|
||||
@@ -0,0 +1,54 @@
|
||||
-- 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))
|
||||
@@ -0,0 +1,48 @@
|
||||
# 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))
|
||||
@@ -0,0 +1,51 @@
|
||||
# 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
|
||||
@@ -0,0 +1,53 @@
|
||||
// Ported from the Python version.
|
||||
|
||||
class Tree {
|
||||
new(item, depth) {
|
||||
_item = item
|
||||
if (depth > 0) {
|
||||
var item2 = item + item
|
||||
depth = depth - 1
|
||||
_left = new Tree(item2 - 1, depth)
|
||||
_right = new Tree(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 = IO.clock
|
||||
|
||||
IO.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 (d in 0...maxDepth) {
|
||||
iterations = iterations * 2
|
||||
}
|
||||
|
||||
var depth = minDepth
|
||||
while (depth < stretchDepth) {
|
||||
var check = 0
|
||||
for (i in 1..iterations) {
|
||||
check = check + new Tree(i, depth).check + new Tree(-i, depth).check
|
||||
}
|
||||
|
||||
IO.print((iterations * 2), " trees of depth ", depth, " check: ", check)
|
||||
iterations = iterations / 4
|
||||
depth = depth + 2
|
||||
}
|
||||
|
||||
IO.print("long lived tree of depth ", maxDepth, " check: ", longLivedTree.check)
|
||||
IO.print("elapsed: ", (IO.clock - start))
|
||||
@@ -0,0 +1,914 @@
|
||||
-- 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)
|
||||
})
|
||||
@@ -0,0 +1,637 @@
|
||||
"""
|
||||
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(20):
|
||||
chain_test(100)
|
||||
projection_test(100)
|
||||
print(total)
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
delta_blue()
|
||||
@@ -0,0 +1,711 @@
|
||||
// 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.
|
||||
|
||||
// TODO: Support forward declarations of globals.
|
||||
var REQUIRED = null
|
||||
var STRONG_REFERRED = null
|
||||
var PREFERRED = null
|
||||
var STRONG_DEFAULT = null
|
||||
var NORMAL = null
|
||||
var WEAK_DEFAULT = null
|
||||
var WEAKEST = null
|
||||
|
||||
var ORDERED = null
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
REQUIRED = new Strength(0, "required")
|
||||
STRONG_REFERRED = new Strength(1, "strongPreferred")
|
||||
PREFERRED = new Strength(2, "preferred")
|
||||
STRONG_DEFAULT = new Strength(3, "strongDefault")
|
||||
NORMAL = new Strength(4, "normal")
|
||||
WEAK_DEFAULT = new Strength(5, "weakDefault")
|
||||
WEAKEST = new Strength(6, "weakest")
|
||||
|
||||
ORDERED = [
|
||||
WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT, PREFERRED, STRONG_REFERRED
|
||||
]
|
||||
|
||||
var ThePlanner
|
||||
|
||||
class Constraint {
|
||||
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) {
|
||||
IO.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)) IO.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 {
|
||||
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 {
|
||||
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 {
|
||||
EditConstraint(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 {
|
||||
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 {
|
||||
if (_direction == FORWARD) return _v1
|
||||
return _v2
|
||||
}
|
||||
|
||||
// Returns the current output variable.
|
||||
output {
|
||||
if (_direction == FORWARD) return _v2
|
||||
return _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 {
|
||||
new(src, scale, offset, dest, strength) {
|
||||
_scale = scale
|
||||
_offset = offset
|
||||
super(src, dest, strength)
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
addToGraph {
|
||||
super.addToGraph
|
||||
_scale.addConstraint(this)
|
||||
_offset.addConstraint(this)
|
||||
}
|
||||
|
||||
removeFromGraph {
|
||||
super.removeFromGraph
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
new {
|
||||
_list = []
|
||||
}
|
||||
|
||||
addConstraint(constraint) {
|
||||
_list.add(constraint)
|
||||
}
|
||||
|
||||
size { _list.count }
|
||||
|
||||
execute {
|
||||
for (constraint in _list) {
|
||||
constraint.execute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Planner {
|
||||
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
|
||||
return _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.
|
||||
makePlan(sources) {
|
||||
var mark = newMark
|
||||
var plan = new Plan
|
||||
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 = new Fn {|n|
|
||||
ThePlanner = new Planner
|
||||
var prev = null
|
||||
var first = null
|
||||
var last = null
|
||||
|
||||
// Build chain of n equality constraints.
|
||||
for (i in 0..n) {
|
||||
var 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)
|
||||
var edit = new EditConstraint(first, PREFERRED)
|
||||
var plan = ThePlanner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...100) {
|
||||
first.value = i
|
||||
plan.execute
|
||||
total = total + last.value
|
||||
}
|
||||
}
|
||||
|
||||
var change = new Fn {|v, newValue|
|
||||
var edit = new EditConstraint(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 = new Fn {|n|
|
||||
ThePlanner = new Planner
|
||||
var scale = new Variable("scale", 10)
|
||||
var offset = new Variable("offset", 1000)
|
||||
var src = null
|
||||
var dst = null
|
||||
|
||||
var dests = []
|
||||
for (i in 0...n) {
|
||||
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.call(src, 17)
|
||||
total = total + dst.value
|
||||
if (dst.value != 1170) IO.print("Projection 1 failed")
|
||||
|
||||
change.call(dst, 1050)
|
||||
|
||||
total = total + src.value
|
||||
if (src.value != 5) IO.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) IO.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) IO.print("Projection 4 failed")
|
||||
}
|
||||
}
|
||||
|
||||
var start = IO.clock
|
||||
for (i in 0...20) {
|
||||
chainTest.call(100)
|
||||
projectionTest.call(100)
|
||||
}
|
||||
|
||||
IO.print(total)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
-- 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")
|
||||
@@ -0,0 +1,56 @@
|
||||
# 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)) ))
|
||||
@@ -0,0 +1,60 @@
|
||||
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
|
||||
@@ -0,0 +1,10 @@
|
||||
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))
|
||||
@@ -0,0 +1,12 @@
|
||||
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))
|
||||
@@ -0,0 +1,13 @@
|
||||
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
|
||||
@@ -0,0 +1,12 @@
|
||||
class Fib {
|
||||
static get(n) {
|
||||
if (n < 2) return n
|
||||
return get(n - 1) + get(n - 2)
|
||||
}
|
||||
}
|
||||
|
||||
var start = IO.clock
|
||||
for (i in 1..5) {
|
||||
IO.print(Fib.get(28))
|
||||
}
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@@ -0,0 +1,12 @@
|
||||
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))
|
||||
@@ -0,0 +1,20 @@
|
||||
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))
|
||||
@@ -0,0 +1,8 @@
|
||||
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
|
||||
@@ -0,0 +1,10 @@
|
||||
var list = []
|
||||
|
||||
var start = IO.clock
|
||||
for (i in 0...1000000) list.add(i)
|
||||
|
||||
var sum = 0
|
||||
for (i in list) sum = sum + i
|
||||
|
||||
IO.print(sum)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@@ -0,0 +1,19 @@
|
||||
local start = os.clock()
|
||||
|
||||
local map = {}
|
||||
|
||||
for i = 1, 1000000 do
|
||||
map[i] = i
|
||||
end
|
||||
|
||||
local sum = 0
|
||||
for i = 1, 1000000 do
|
||||
sum = sum + map[i]
|
||||
end
|
||||
io.write(string.format("%d\n", sum))
|
||||
|
||||
for i = 1, 1000000 do
|
||||
map[i] = nil
|
||||
end
|
||||
|
||||
io.write(string.format("elapsed: %.8f\n", os.clock() - start))
|
||||
@@ -0,0 +1,20 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
|
||||
start = time.clock()
|
||||
|
||||
map = {}
|
||||
|
||||
for i in range(1, 1000001):
|
||||
map[i] = i
|
||||
|
||||
sum = 0
|
||||
for i in range(1, 1000001):
|
||||
sum = sum + map[i]
|
||||
print(sum)
|
||||
|
||||
for i in range(1, 1000001):
|
||||
del map[i]
|
||||
|
||||
print("elapsed: " + str(time.clock() - start))
|
||||
@@ -0,0 +1,19 @@
|
||||
start = Time.now
|
||||
|
||||
map = Hash.new
|
||||
|
||||
for i in (1..1000000)
|
||||
map[i] = i
|
||||
end
|
||||
|
||||
sum = 0
|
||||
for i in (1..1000000)
|
||||
sum = sum + map[i]
|
||||
end
|
||||
puts sum
|
||||
|
||||
for i in (1..1000000)
|
||||
map.delete(i)
|
||||
end
|
||||
|
||||
puts "elapsed: " + (Time.now - start).to_s
|
||||
@@ -0,0 +1,19 @@
|
||||
var start = IO.clock
|
||||
|
||||
var map = {}
|
||||
|
||||
for (i in 1..1000000) {
|
||||
map[i] = i
|
||||
}
|
||||
|
||||
var sum = 0
|
||||
for (i in 1..1000000) {
|
||||
sum = sum + map[i]
|
||||
}
|
||||
IO.print(sum)
|
||||
|
||||
for (i in 1..1000000) {
|
||||
map.remove(i)
|
||||
}
|
||||
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@@ -0,0 +1,98 @@
|
||||
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 = {}
|
||||
|
||||
for _, key in ipairs(keys) do
|
||||
map[key] = string.len(key)
|
||||
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))
|
||||
@@ -0,0 +1,96 @@
|
||||
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 = {}
|
||||
|
||||
for key in keys:
|
||||
map[key] = len(key)
|
||||
|
||||
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))
|
||||
@@ -0,0 +1,98 @@
|
||||
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
|
||||
|
||||
for key in keys
|
||||
map[key] = key.length
|
||||
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
|
||||
@@ -0,0 +1,98 @@
|
||||
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 = IO.clock
|
||||
|
||||
var map = {}
|
||||
|
||||
for (key in keys) {
|
||||
map[key] = key.count
|
||||
}
|
||||
|
||||
var sum = 0
|
||||
for (key in keys) {
|
||||
sum = sum + map[key]
|
||||
}
|
||||
|
||||
for (key in keys) {
|
||||
map.remove(key)
|
||||
}
|
||||
|
||||
IO.print(sum)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
@@ -0,0 +1,94 @@
|
||||
-- $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()
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/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()
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/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()
|
||||
@@ -0,0 +1,68 @@
|
||||
class Toggle {
|
||||
new(startState) {
|
||||
_state = startState
|
||||
}
|
||||
|
||||
value { _state }
|
||||
activate {
|
||||
_state = !_state
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
class NthToggle is Toggle {
|
||||
new(startState, maxCounter) {
|
||||
super(startState)
|
||||
_countMax = maxCounter
|
||||
_count = 0
|
||||
}
|
||||
|
||||
activate {
|
||||
_count = _count + 1
|
||||
if (_count >= _countMax) {
|
||||
super.activate
|
||||
_count = 0
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
var start = IO.clock
|
||||
var n = 100000
|
||||
var val = true
|
||||
var toggle = new Toggle(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
|
||||
}
|
||||
|
||||
IO.print(toggle.value)
|
||||
|
||||
val = true
|
||||
var ntoggle = new NthToggle(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
|
||||
}
|
||||
|
||||
IO.print(ntoggle.value)
|
||||
IO.print("elapsed: ", IO.clock - start)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user