[oden] Grab my incomplete QuickJS wrapper
This commit is contained in:
parent
aa70df41a3
commit
898b1fe129
114 changed files with 244181 additions and 0 deletions
10
oden-js-sys/quickjs/tests/bench-v8/Makefile
Normal file
10
oden-js-sys/quickjs/tests/bench-v8/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
all: combined.js
|
||||
|
||||
FILES=base.js richards.js deltablue.js crypto.js raytrace.js earley-boyer.js regexp.js splay.js navier-stokes.js run_harness.js
|
||||
#FILES=base.js richards.js deltablue.js crypto.js raytrace.js earley-boyer.js splay.js navier-stokes.js run_harness.js
|
||||
|
||||
combined.js: $(FILES)
|
||||
cat $(FILES) >$@
|
||||
|
||||
clean:
|
||||
rm -f combined.js
|
||||
86
oden-js-sys/quickjs/tests/bench-v8/README.txt
Normal file
86
oden-js-sys/quickjs/tests/bench-v8/README.txt
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
V8 Benchmark Suite
|
||||
==================
|
||||
|
||||
This is the V8 benchmark suite: A collection of pure JavaScript
|
||||
benchmarks that we have used to tune V8. The licenses for the
|
||||
individual benchmarks are included in the JavaScript files.
|
||||
|
||||
In addition to the benchmarks, the suite consists of the benchmark
|
||||
framework (base.js), which must be loaded before any of the individual
|
||||
benchmark files, and two benchmark runners: An HTML version (run.html)
|
||||
and a standalone JavaScript version (run.js).
|
||||
|
||||
|
||||
Changes From Version 1 To Version 2
|
||||
===================================
|
||||
|
||||
For version 2 the crypto benchmark was fixed. Previously, the
|
||||
decryption stage was given plaintext as input, which resulted in an
|
||||
error. Now, the decryption stage is given the output of the
|
||||
encryption stage as input. The result is checked against the original
|
||||
plaintext. For this to give the correct results the crypto objects
|
||||
are reset for each iteration of the benchmark. In addition, the size
|
||||
of the plain text has been increased a little and the use of
|
||||
Math.random() and new Date() to build an RNG pool has been removed.
|
||||
|
||||
Other benchmarks were fixed to do elementary verification of the
|
||||
results of their calculations. This is to avoid accidentally
|
||||
obtaining scores that are the result of an incorrect JavaScript engine
|
||||
optimization.
|
||||
|
||||
|
||||
Changes From Version 2 To Version 3
|
||||
===================================
|
||||
|
||||
Version 3 adds a new benchmark, RegExp. The RegExp benchmark is
|
||||
generated by loading 50 of the most popular pages on the web and
|
||||
logging all regexp operations performed. Each operation is given a
|
||||
weight that is calculated from an estimate of the popularity of the
|
||||
pages where it occurs and the number of times it is executed while
|
||||
loading each page. Finally the literal letters in the data are
|
||||
encoded using ROT13 in a way that does not affect how the regexps
|
||||
match their input.
|
||||
|
||||
|
||||
Changes from Version 3 to Version 4
|
||||
===================================
|
||||
|
||||
The Splay benchmark is a newcomer in version 4. It manipulates a
|
||||
splay tree by adding and removing data nodes, thus exercising the
|
||||
memory management subsystem of the JavaScript engine.
|
||||
|
||||
Furthermore, all the unused parts of the Prototype library were
|
||||
removed from the RayTrace benchmark. This does not affect the running
|
||||
of the benchmark.
|
||||
|
||||
|
||||
Changes from Version 4 to Version 5
|
||||
===================================
|
||||
|
||||
Removed duplicate line in random seed code, and changed the name of
|
||||
the Object.prototype.inherits function in the DeltaBlue benchmark to
|
||||
inheritsFrom to avoid name clashes when running in Chromium with
|
||||
extensions enabled.
|
||||
|
||||
|
||||
Changes from Version 5 to Version 6
|
||||
===================================
|
||||
|
||||
Removed dead code from the RayTrace benchmark and fixed a couple of
|
||||
typos in the DeltaBlue implementation. Changed the Splay benchmark to
|
||||
avoid converting the same numeric key to a string over and over again
|
||||
and to avoid inserting and removing the same element repeatedly thus
|
||||
increasing pressure on the memory subsystem. Changed the RegExp
|
||||
benchmark to exercise the regular expression engine on different
|
||||
input strings.
|
||||
|
||||
Furthermore, the benchmark runner was changed to run the benchmarks
|
||||
for at least a few times to stabilize the reported numbers on slower
|
||||
machines.
|
||||
|
||||
|
||||
Changes from Version 6 to Version 7
|
||||
===================================
|
||||
|
||||
Added the Navier-Stokes benchmark, a 2D differential equation solver
|
||||
that stresses arithmetic computations on double arrays.
|
||||
285
oden-js-sys/quickjs/tests/bench-v8/base.js
Normal file
285
oden-js-sys/quickjs/tests/bench-v8/base.js
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
"use strict";
|
||||
"use strip";
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Simple framework for running the benchmark suites and
|
||||
// computing a score based on the timing measurements.
|
||||
|
||||
|
||||
// A benchmark has a name (string) and a function that will be run to
|
||||
// do the performance measurement. The optional setup and tearDown
|
||||
// arguments are functions that will be invoked before and after
|
||||
// running the benchmark, but the running time of these functions will
|
||||
// not be accounted for in the benchmark score.
|
||||
function Benchmark(name, run, setup, tearDown) {
|
||||
this.name = name;
|
||||
this.run = run;
|
||||
this.Setup = setup ? setup : function() { };
|
||||
this.TearDown = tearDown ? tearDown : function() { };
|
||||
}
|
||||
|
||||
|
||||
// Benchmark results hold the benchmark and the measured time used to
|
||||
// run the benchmark. The benchmark score is computed later once a
|
||||
// full benchmark suite has run to completion.
|
||||
function BenchmarkResult(benchmark, time) {
|
||||
this.benchmark = benchmark;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
|
||||
// Automatically convert results to numbers. Used by the geometric
|
||||
// mean computation.
|
||||
BenchmarkResult.prototype.valueOf = function() {
|
||||
return this.time;
|
||||
};
|
||||
|
||||
|
||||
// Suites of benchmarks consist of a name and the set of benchmarks in
|
||||
// addition to the reference timing that the final score will be based
|
||||
// on. This way, all scores are relative to a reference run and higher
|
||||
// scores implies better performance.
|
||||
function BenchmarkSuite(name, reference, benchmarks) {
|
||||
this.name = name;
|
||||
this.reference = reference;
|
||||
this.benchmarks = benchmarks;
|
||||
BenchmarkSuite.suites.push(this);
|
||||
}
|
||||
|
||||
|
||||
// Keep track of all declared benchmark suites.
|
||||
BenchmarkSuite.suites = [];
|
||||
|
||||
|
||||
// Scores are not comparable across versions. Bump the version if
|
||||
// you're making changes that will affect that scores, e.g. if you add
|
||||
// a new benchmark or change an existing one.
|
||||
BenchmarkSuite.version = '7';
|
||||
|
||||
|
||||
// To make the benchmark results predictable, we replace Math.random
|
||||
// with a 100% deterministic alternative.
|
||||
Math.random = (function() {
|
||||
var seed = 49734321;
|
||||
return function() {
|
||||
// Robert Jenkins' 32 bit integer hash function.
|
||||
seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
|
||||
seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
|
||||
seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
|
||||
seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
|
||||
seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
|
||||
seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
|
||||
return (seed & 0xfffffff) / 0x10000000;
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
// Runs all registered benchmark suites and optionally yields between
|
||||
// each individual benchmark to avoid running for too long in the
|
||||
// context of browsers. Once done, the final score is reported to the
|
||||
// runner.
|
||||
BenchmarkSuite.RunSuites = function(runner) {
|
||||
var continuation = null;
|
||||
var suites = BenchmarkSuite.suites;
|
||||
var length = suites.length;
|
||||
BenchmarkSuite.scores = [];
|
||||
var index = 0;
|
||||
function RunStep() {
|
||||
while (continuation || index < length) {
|
||||
if (continuation) {
|
||||
continuation = continuation();
|
||||
} else {
|
||||
var suite = suites[index++];
|
||||
if (runner.NotifyStart) runner.NotifyStart(suite.name);
|
||||
continuation = suite.RunStep(runner);
|
||||
}
|
||||
if (continuation && typeof window != 'undefined' && window.setTimeout) {
|
||||
window.setTimeout(RunStep, 25);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (runner.NotifyScore) {
|
||||
var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);
|
||||
var formatted = BenchmarkSuite.FormatScore(100 * score);
|
||||
runner.NotifyScore(formatted);
|
||||
}
|
||||
}
|
||||
RunStep();
|
||||
};
|
||||
|
||||
|
||||
// Counts the total number of registered benchmarks. Useful for
|
||||
// showing progress as a percentage.
|
||||
BenchmarkSuite.CountBenchmarks = function() {
|
||||
var result = 0;
|
||||
var suites = BenchmarkSuite.suites;
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
result += suites[i].benchmarks.length;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
// Computes the geometric mean of a set of numbers.
|
||||
BenchmarkSuite.GeometricMean = function(numbers) {
|
||||
var log = 0;
|
||||
for (var i = 0; i < numbers.length; i++) {
|
||||
log += Math.log(numbers[i]);
|
||||
}
|
||||
return Math.pow(Math.E, log / numbers.length);
|
||||
};
|
||||
|
||||
|
||||
// Converts a score value to a string with at least three significant
|
||||
// digits.
|
||||
BenchmarkSuite.FormatScore = function(value) {
|
||||
if (value > 100) {
|
||||
return value.toFixed(0);
|
||||
} else {
|
||||
return value.toPrecision(3);
|
||||
}
|
||||
};
|
||||
|
||||
// Notifies the runner that we're done running a single benchmark in
|
||||
// the benchmark suite. This can be useful to report progress.
|
||||
BenchmarkSuite.prototype.NotifyStep = function(result) {
|
||||
this.results.push(result);
|
||||
if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);
|
||||
};
|
||||
|
||||
|
||||
// Notifies the runner that we're done with running a suite and that
|
||||
// we have a result which can be reported to the user if needed.
|
||||
BenchmarkSuite.prototype.NotifyResult = function() {
|
||||
var mean = BenchmarkSuite.GeometricMean(this.results);
|
||||
var score = this.reference / mean;
|
||||
BenchmarkSuite.scores.push(score);
|
||||
if (this.runner.NotifyResult) {
|
||||
var formatted = BenchmarkSuite.FormatScore(100 * score);
|
||||
this.runner.NotifyResult(this.name, formatted);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Notifies the runner that running a benchmark resulted in an error.
|
||||
BenchmarkSuite.prototype.NotifyError = function(error) {
|
||||
if (this.runner.NotifyError) {
|
||||
this.runner.NotifyError(this.name, error);
|
||||
}
|
||||
if (this.runner.NotifyStep) {
|
||||
this.runner.NotifyStep(this.name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Runs a single benchmark for at least a second and computes the
|
||||
// average time it takes to run a single iteration.
|
||||
BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark, data) {
|
||||
function Measure(data) {
|
||||
var elapsed = 0;
|
||||
var start = new Date();
|
||||
for (var n = 0; elapsed < 1000; n++) {
|
||||
benchmark.run();
|
||||
elapsed = new Date() - start;
|
||||
}
|
||||
if (data != null) {
|
||||
data.runs += n;
|
||||
data.elapsed += elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
// Measure the benchmark once for warm up and throw the result
|
||||
// away. Return a fresh data object.
|
||||
Measure(null);
|
||||
return { runs: 0, elapsed: 0 };
|
||||
} else {
|
||||
Measure(data);
|
||||
// If we've run too few iterations, we continue for another second.
|
||||
if (data.runs < 32) return data;
|
||||
var usec = (data.elapsed * 1000) / data.runs;
|
||||
this.NotifyStep(new BenchmarkResult(benchmark, usec));
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// This function starts running a suite, but stops between each
|
||||
// individual benchmark in the suite and returns a continuation
|
||||
// function which can be invoked to run the next benchmark. Once the
|
||||
// last benchmark has been executed, null is returned.
|
||||
BenchmarkSuite.prototype.RunStep = function(runner) {
|
||||
this.results = [];
|
||||
this.runner = runner;
|
||||
var length = this.benchmarks.length;
|
||||
var index = 0;
|
||||
var suite = this;
|
||||
var data;
|
||||
|
||||
// Run the setup, the actual benchmark, and the tear down in three
|
||||
// separate steps to allow the framework to yield between any of the
|
||||
// steps.
|
||||
|
||||
function RunNextSetup() {
|
||||
if (index < length) {
|
||||
try {
|
||||
suite.benchmarks[index].Setup();
|
||||
} catch (e) {
|
||||
suite.NotifyError(e);
|
||||
return null;
|
||||
}
|
||||
return RunNextBenchmark;
|
||||
}
|
||||
suite.NotifyResult();
|
||||
return null;
|
||||
}
|
||||
|
||||
function RunNextBenchmark() {
|
||||
try {
|
||||
data = suite.RunSingleBenchmark(suite.benchmarks[index], data);
|
||||
} catch (e) {
|
||||
suite.NotifyError(e);
|
||||
return null;
|
||||
}
|
||||
// If data is null, we're done with this benchmark.
|
||||
return (data == null) ? RunNextTearDown : RunNextBenchmark();
|
||||
}
|
||||
|
||||
function RunNextTearDown() {
|
||||
try {
|
||||
suite.benchmarks[index++].TearDown();
|
||||
} catch (e) {
|
||||
suite.NotifyError(e);
|
||||
return null;
|
||||
}
|
||||
return RunNextSetup;
|
||||
}
|
||||
|
||||
// Start out running the setup.
|
||||
return RunNextSetup();
|
||||
};
|
||||
11573
oden-js-sys/quickjs/tests/bench-v8/combined.js
Normal file
11573
oden-js-sys/quickjs/tests/bench-v8/combined.js
Normal file
File diff suppressed because one or more lines are too long
1697
oden-js-sys/quickjs/tests/bench-v8/crypto.js
Normal file
1697
oden-js-sys/quickjs/tests/bench-v8/crypto.js
Normal file
File diff suppressed because it is too large
Load diff
879
oden-js-sys/quickjs/tests/bench-v8/deltablue.js
Normal file
879
oden-js-sys/quickjs/tests/bench-v8/deltablue.js
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
// 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.
|
||||
*/
|
||||
|
||||
|
||||
/* --- O b j e c t M o d e l --- */
|
||||
|
||||
Object.prototype.inheritsFrom = function (shuper) {
|
||||
function Inheriter() { }
|
||||
Inheriter.prototype = shuper.prototype;
|
||||
this.prototype = new Inheriter();
|
||||
this.superConstructor = shuper;
|
||||
};
|
||||
|
||||
function OrderedCollection() {
|
||||
this.elms = new Array();
|
||||
}
|
||||
|
||||
OrderedCollection.prototype.add = function (elm) {
|
||||
this.elms.push(elm);
|
||||
};
|
||||
|
||||
OrderedCollection.prototype.at = function (index) {
|
||||
return this.elms[index];
|
||||
};
|
||||
|
||||
OrderedCollection.prototype.size = function () {
|
||||
return this.elms.length;
|
||||
};
|
||||
|
||||
OrderedCollection.prototype.removeFirst = function () {
|
||||
return this.elms.pop();
|
||||
};
|
||||
|
||||
OrderedCollection.prototype.remove = function (elm) {
|
||||
var index = 0, skipped = 0;
|
||||
for (var i = 0; i < this.elms.length; i++) {
|
||||
var value = this.elms[i];
|
||||
if (value != elm) {
|
||||
this.elms[index] = value;
|
||||
index++;
|
||||
} else {
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < skipped; i++)
|
||||
this.elms.pop();
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function Strength(strengthValue, name) {
|
||||
this.strengthValue = strengthValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Strength.stronger = function (s1, s2) {
|
||||
return s1.strengthValue < s2.strengthValue;
|
||||
};
|
||||
|
||||
Strength.weaker = function (s1, s2) {
|
||||
return s1.strengthValue > s2.strengthValue;
|
||||
};
|
||||
|
||||
Strength.weakestOf = function (s1, s2) {
|
||||
return this.weaker(s1, s2) ? s1 : s2;
|
||||
};
|
||||
|
||||
Strength.strongest = function (s1, s2) {
|
||||
return this.stronger(s1, s2) ? s1 : s2;
|
||||
};
|
||||
|
||||
Strength.prototype.nextWeaker = function () {
|
||||
switch (this.strengthValue) {
|
||||
case 0: return Strength.STRONG_PREFERRED;
|
||||
case 1: return Strength.PREFERRED;
|
||||
case 2: return Strength.STRONG_DEFAULT;
|
||||
case 3: return Strength.NORMAL;
|
||||
case 4: return Strength.WEAK_DEFAULT;
|
||||
case 5: return Strength.WEAKEST;
|
||||
}
|
||||
};
|
||||
|
||||
// Strength constants.
|
||||
Strength.REQUIRED = new Strength(0, "required");
|
||||
Strength.STRONG_PREFERRED = new Strength(1, "strongPreferred");
|
||||
Strength.PREFERRED = new Strength(2, "preferred");
|
||||
Strength.STRONG_DEFAULT = new Strength(3, "strongDefault");
|
||||
Strength.NORMAL = new Strength(4, "normal");
|
||||
Strength.WEAK_DEFAULT = new Strength(5, "weakDefault");
|
||||
Strength.WEAKEST = new Strength(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.
|
||||
*/
|
||||
function Constraint(strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate this constraint and attempt to satisfy it.
|
||||
*/
|
||||
Constraint.prototype.addConstraint = function () {
|
||||
this.addToGraph();
|
||||
planner.incrementalAdd(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to find a way to enforce this constraint. If successful,
|
||||
* record the solution, perhaps modifying the current dataflow
|
||||
* graph. Answer the constraint that this constraint overrides, if
|
||||
* there is one, or nil, if there isn't.
|
||||
* Assume: I am not already satisfied.
|
||||
*/
|
||||
Constraint.prototype.satisfy = function (mark) {
|
||||
this.chooseMethod(mark);
|
||||
if (!this.isSatisfied()) {
|
||||
if (this.strength == Strength.REQUIRED)
|
||||
alert("Could not satisfy a required constraint!");
|
||||
return null;
|
||||
}
|
||||
this.markInputs(mark);
|
||||
var out = this.output();
|
||||
var overridden = out.determinedBy;
|
||||
if (overridden != null) overridden.markUnsatisfied();
|
||||
out.determinedBy = this;
|
||||
if (!planner.addPropagate(this, mark))
|
||||
alert("Cycle encountered");
|
||||
out.mark = mark;
|
||||
return overridden;
|
||||
};
|
||||
|
||||
Constraint.prototype.destroyConstraint = function () {
|
||||
if (this.isSatisfied()) planner.incrementalRemove(this);
|
||||
else 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.
|
||||
*/
|
||||
Constraint.prototype.isInput = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function UnaryConstraint(v, strength) {
|
||||
UnaryConstraint.superConstructor.call(this, strength);
|
||||
this.myOutput = v;
|
||||
this.satisfied = false;
|
||||
this.addConstraint();
|
||||
}
|
||||
|
||||
UnaryConstraint.inheritsFrom(Constraint);
|
||||
|
||||
/**
|
||||
* Adds this constraint to the constraint graph
|
||||
*/
|
||||
UnaryConstraint.prototype.addToGraph = function () {
|
||||
this.myOutput.addConstraint(this);
|
||||
this.satisfied = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decides if this constraint can be satisfied and records that
|
||||
* decision.
|
||||
*/
|
||||
UnaryConstraint.prototype.chooseMethod = function (mark) {
|
||||
this.satisfied = (this.myOutput.mark != mark)
|
||||
&& Strength.stronger(this.strength, this.myOutput.walkStrength);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if this constraint is satisfied in the current solution.
|
||||
*/
|
||||
UnaryConstraint.prototype.isSatisfied = function () {
|
||||
return this.satisfied;
|
||||
};
|
||||
|
||||
UnaryConstraint.prototype.markInputs = function (mark) {
|
||||
// has no inputs
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current output variable.
|
||||
*/
|
||||
UnaryConstraint.prototype.output = function () {
|
||||
return this.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.
|
||||
*/
|
||||
UnaryConstraint.prototype.recalculate = function () {
|
||||
this.myOutput.walkStrength = this.strength;
|
||||
this.myOutput.stay = !this.isInput();
|
||||
if (this.myOutput.stay) this.execute(); // Stay optimization
|
||||
};
|
||||
|
||||
/**
|
||||
* Records that this constraint is unsatisfied
|
||||
*/
|
||||
UnaryConstraint.prototype.markUnsatisfied = function () {
|
||||
this.satisfied = false;
|
||||
};
|
||||
|
||||
UnaryConstraint.prototype.inputsKnown = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
UnaryConstraint.prototype.removeFromGraph = function () {
|
||||
if (this.myOutput != null) this.myOutput.removeConstraint(this);
|
||||
this.satisfied = false;
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* 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".
|
||||
*/
|
||||
function StayConstraint(v, str) {
|
||||
StayConstraint.superConstructor.call(this, v, str);
|
||||
}
|
||||
|
||||
StayConstraint.inheritsFrom(UnaryConstraint);
|
||||
|
||||
StayConstraint.prototype.execute = function () {
|
||||
// Stay constraints do nothing
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function EditConstraint(v, str) {
|
||||
EditConstraint.superConstructor.call(this, v, str);
|
||||
}
|
||||
|
||||
EditConstraint.inheritsFrom(UnaryConstraint);
|
||||
|
||||
/**
|
||||
* Edits indicate that a variable is to be changed by imperative code.
|
||||
*/
|
||||
EditConstraint.prototype.isInput = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
EditConstraint.prototype.execute = function () {
|
||||
// Edit constraints do nothing
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* B i n a r y C o n s t r a i n t
|
||||
* --- */
|
||||
|
||||
var Direction = new Object();
|
||||
Direction.NONE = 0;
|
||||
Direction.FORWARD = 1;
|
||||
Direction.BACKWARD = -1;
|
||||
|
||||
/**
|
||||
* Abstract superclass for constraints having two possible output
|
||||
* variables.
|
||||
*/
|
||||
function BinaryConstraint(var1, var2, strength) {
|
||||
BinaryConstraint.superConstructor.call(this, strength);
|
||||
this.v1 = var1;
|
||||
this.v2 = var2;
|
||||
this.direction = Direction.NONE;
|
||||
this.addConstraint();
|
||||
}
|
||||
|
||||
BinaryConstraint.inheritsFrom(Constraint);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
BinaryConstraint.prototype.chooseMethod = function (mark) {
|
||||
if (this.v1.mark == mark) {
|
||||
this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))
|
||||
? Direction.FORWARD
|
||||
: Direction.NONE;
|
||||
}
|
||||
if (this.v2.mark == mark) {
|
||||
this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))
|
||||
? Direction.BACKWARD
|
||||
: Direction.NONE;
|
||||
}
|
||||
if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
|
||||
this.direction = Strength.stronger(this.strength, this.v1.walkStrength)
|
||||
? Direction.BACKWARD
|
||||
: Direction.NONE;
|
||||
} else {
|
||||
this.direction = Strength.stronger(this.strength, this.v2.walkStrength)
|
||||
? Direction.FORWARD
|
||||
: Direction.BACKWARD
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add this constraint to the constraint graph
|
||||
*/
|
||||
BinaryConstraint.prototype.addToGraph = function () {
|
||||
this.v1.addConstraint(this);
|
||||
this.v2.addConstraint(this);
|
||||
this.direction = Direction.NONE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Answer true if this constraint is satisfied in the current solution.
|
||||
*/
|
||||
BinaryConstraint.prototype.isSatisfied = function () {
|
||||
return this.direction != Direction.NONE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark the input variable with the given mark.
|
||||
*/
|
||||
BinaryConstraint.prototype.markInputs = function (mark) {
|
||||
this.input().mark = mark;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current input variable
|
||||
*/
|
||||
BinaryConstraint.prototype.input = function () {
|
||||
return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current output variable
|
||||
*/
|
||||
BinaryConstraint.prototype.output = function () {
|
||||
return (this.direction == Direction.FORWARD) ? this.v2 : this.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.
|
||||
*/
|
||||
BinaryConstraint.prototype.recalculate = function () {
|
||||
var ihn = this.input(), out = this.output();
|
||||
out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
|
||||
out.stay = ihn.stay;
|
||||
if (out.stay) this.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the fact that this constraint is unsatisfied.
|
||||
*/
|
||||
BinaryConstraint.prototype.markUnsatisfied = function () {
|
||||
this.direction = Direction.NONE;
|
||||
};
|
||||
|
||||
BinaryConstraint.prototype.inputsKnown = function (mark) {
|
||||
var i = this.input();
|
||||
return i.mark == mark || i.stay || i.determinedBy == null;
|
||||
};
|
||||
|
||||
BinaryConstraint.prototype.removeFromGraph = function () {
|
||||
if (this.v1 != null) this.v1.removeConstraint(this);
|
||||
if (this.v2 != null) this.v2.removeConstraint(this);
|
||||
this.direction = Direction.NONE;
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function ScaleConstraint(src, scale, offset, dest, strength) {
|
||||
this.direction = Direction.NONE;
|
||||
this.scale = scale;
|
||||
this.offset = offset;
|
||||
ScaleConstraint.superConstructor.call(this, src, dest, strength);
|
||||
};
|
||||
|
||||
ScaleConstraint.inheritsFrom(BinaryConstraint);
|
||||
|
||||
/**
|
||||
* Adds this constraint to the constraint graph.
|
||||
*/
|
||||
ScaleConstraint.prototype.addToGraph = function () {
|
||||
ScaleConstraint.superConstructor.prototype.addToGraph.call(this);
|
||||
this.scale.addConstraint(this);
|
||||
this.offset.addConstraint(this);
|
||||
};
|
||||
|
||||
ScaleConstraint.prototype.removeFromGraph = function () {
|
||||
ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);
|
||||
if (this.scale != null) this.scale.removeConstraint(this);
|
||||
if (this.offset != null) this.offset.removeConstraint(this);
|
||||
};
|
||||
|
||||
ScaleConstraint.prototype.markInputs = function (mark) {
|
||||
ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);
|
||||
this.scale.mark = this.offset.mark = mark;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enforce this constraint. Assume that it is satisfied.
|
||||
*/
|
||||
ScaleConstraint.prototype.execute = function () {
|
||||
if (this.direction == Direction.FORWARD) {
|
||||
this.v2.value = this.v1.value * this.scale.value + this.offset.value;
|
||||
} else {
|
||||
this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the walkabout strength, the stay flag, and, if it is
|
||||
* 'stay', the value for the current output of this constraint. Assume
|
||||
* this constraint is satisfied.
|
||||
*/
|
||||
ScaleConstraint.prototype.recalculate = function () {
|
||||
var ihn = this.input(), out = this.output();
|
||||
out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
|
||||
out.stay = ihn.stay && this.scale.stay && this.offset.stay;
|
||||
if (out.stay) this.execute();
|
||||
}
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function EqualityConstraint(var1, var2, strength) {
|
||||
EqualityConstraint.superConstructor.call(this, var1, var2, strength);
|
||||
}
|
||||
|
||||
EqualityConstraint.inheritsFrom(BinaryConstraint);
|
||||
|
||||
/**
|
||||
* Enforce this constraint. Assume that it is satisfied.
|
||||
*/
|
||||
EqualityConstraint.prototype.execute = function () {
|
||||
this.output().value = this.input().value;
|
||||
}
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
**/
|
||||
function Variable(name, initialValue) {
|
||||
this.value = initialValue || 0;
|
||||
this.constraints = new OrderedCollection();
|
||||
this.determinedBy = null;
|
||||
this.mark = 0;
|
||||
this.walkStrength = Strength.WEAKEST;
|
||||
this.stay = true;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given constraint to the set of all constraints that refer
|
||||
* this variable.
|
||||
*/
|
||||
Variable.prototype.addConstraint = function (c) {
|
||||
this.constraints.add(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all traces of c from this variable.
|
||||
*/
|
||||
Variable.prototype.removeConstraint = function (c) {
|
||||
this.constraints.remove(c);
|
||||
if (this.determinedBy == c) this.determinedBy = null;
|
||||
}
|
||||
|
||||
/* --- *
|
||||
* P l a n n e r
|
||||
* --- */
|
||||
|
||||
/**
|
||||
* The DeltaBlue planner
|
||||
*/
|
||||
function Planner() {
|
||||
this.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.
|
||||
*/
|
||||
Planner.prototype.incrementalAdd = function (c) {
|
||||
var mark = this.newMark();
|
||||
var overridden = c.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.
|
||||
*/
|
||||
Planner.prototype.incrementalRemove = function (c) {
|
||||
var out = c.output();
|
||||
c.markUnsatisfied();
|
||||
c.removeFromGraph();
|
||||
var unsatisfied = this.removePropagateFrom(out);
|
||||
var strength = Strength.REQUIRED;
|
||||
do {
|
||||
for (var i = 0; i < unsatisfied.size(); i++) {
|
||||
var u = unsatisfied.at(i);
|
||||
if (u.strength == strength)
|
||||
this.incrementalAdd(u);
|
||||
}
|
||||
strength = strength.nextWeaker();
|
||||
} while (strength != Strength.WEAKEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a previously unused mark value.
|
||||
*/
|
||||
Planner.prototype.newMark = function () {
|
||||
return ++this.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.
|
||||
*/
|
||||
Planner.prototype.makePlan = function (sources) {
|
||||
var mark = this.newMark();
|
||||
var plan = new Plan();
|
||||
var todo = sources;
|
||||
while (todo.size() > 0) {
|
||||
var c = todo.removeFirst();
|
||||
if (c.output().mark != mark && c.inputsKnown(mark)) {
|
||||
plan.addConstraint(c);
|
||||
c.output().mark = mark;
|
||||
this.addConstraintsConsumingTo(c.output(), todo);
|
||||
}
|
||||
}
|
||||
return plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a plan for resatisfying starting from the output of the
|
||||
* given constraints, usually a set of input constraints.
|
||||
*/
|
||||
Planner.prototype.extractPlanFromConstraints = function (constraints) {
|
||||
var sources = new OrderedCollection();
|
||||
for (var i = 0; i < constraints.size(); i++) {
|
||||
var c = constraints.at(i);
|
||||
if (c.isInput() && c.isSatisfied())
|
||||
// not in plan already and eligible for inclusion
|
||||
sources.add(c);
|
||||
}
|
||||
return this.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.
|
||||
*/
|
||||
Planner.prototype.addPropagate = function (c, mark) {
|
||||
var todo = new OrderedCollection();
|
||||
todo.add(c);
|
||||
while (todo.size() > 0) {
|
||||
var d = todo.removeFirst();
|
||||
if (d.output().mark == mark) {
|
||||
this.incrementalRemove(c);
|
||||
return false;
|
||||
}
|
||||
d.recalculate();
|
||||
this.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.
|
||||
*/
|
||||
Planner.prototype.removePropagateFrom = function (out) {
|
||||
out.determinedBy = null;
|
||||
out.walkStrength = Strength.WEAKEST;
|
||||
out.stay = true;
|
||||
var unsatisfied = new OrderedCollection();
|
||||
var todo = new OrderedCollection();
|
||||
todo.add(out);
|
||||
while (todo.size() > 0) {
|
||||
var v = todo.removeFirst();
|
||||
for (var i = 0; i < v.constraints.size(); i++) {
|
||||
var c = v.constraints.at(i);
|
||||
if (!c.isSatisfied())
|
||||
unsatisfied.add(c);
|
||||
}
|
||||
var determining = v.determinedBy;
|
||||
for (var i = 0; i < v.constraints.size(); i++) {
|
||||
var next = v.constraints.at(i);
|
||||
if (next != determining && next.isSatisfied()) {
|
||||
next.recalculate();
|
||||
todo.add(next.output());
|
||||
}
|
||||
}
|
||||
}
|
||||
return unsatisfied;
|
||||
}
|
||||
|
||||
Planner.prototype.addConstraintsConsumingTo = function (v, coll) {
|
||||
var determining = v.determinedBy;
|
||||
var cc = v.constraints;
|
||||
for (var i = 0; i < cc.size(); i++) {
|
||||
var c = cc.at(i);
|
||||
if (c != determining && c.isSatisfied())
|
||||
coll.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function Plan() {
|
||||
this.v = new OrderedCollection();
|
||||
}
|
||||
|
||||
Plan.prototype.addConstraint = function (c) {
|
||||
this.v.add(c);
|
||||
}
|
||||
|
||||
Plan.prototype.size = function () {
|
||||
return this.v.size();
|
||||
}
|
||||
|
||||
Plan.prototype.constraintAt = function (index) {
|
||||
return this.v.at(index);
|
||||
}
|
||||
|
||||
Plan.prototype.execute = function () {
|
||||
for (var i = 0; i < this.size(); i++) {
|
||||
var c = this.constraintAt(i);
|
||||
c.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/* --- *
|
||||
* 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.
|
||||
*/
|
||||
function chainTest(n) {
|
||||
planner = new Planner();
|
||||
var prev = null, first = null, last = null;
|
||||
|
||||
// Build chain of n equality constraints
|
||||
for (var i = 0; i <= n; i++) {
|
||||
var name = "v" + i;
|
||||
var v = new Variable(name);
|
||||
if (prev != null)
|
||||
new EqualityConstraint(prev, v, Strength.REQUIRED);
|
||||
if (i == 0) first = v;
|
||||
if (i == n) last = v;
|
||||
prev = v;
|
||||
}
|
||||
|
||||
new StayConstraint(last, Strength.STRONG_DEFAULT);
|
||||
var edit = new EditConstraint(first, Strength.PREFERRED);
|
||||
var edits = new OrderedCollection();
|
||||
edits.add(edit);
|
||||
var plan = planner.extractPlanFromConstraints(edits);
|
||||
for (var i = 0; i < 100; i++) {
|
||||
first.value = i;
|
||||
plan.execute();
|
||||
if (last.value != i)
|
||||
alert("Chain test failed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function projectionTest(n) {
|
||||
planner = new Planner();
|
||||
var scale = new Variable("scale", 10);
|
||||
var offset = new Variable("offset", 1000);
|
||||
var src = null, dst = null;
|
||||
|
||||
var dests = new OrderedCollection();
|
||||
for (var i = 0; i < n; i++) {
|
||||
src = new Variable("src" + i, i);
|
||||
dst = new Variable("dst" + i, i);
|
||||
dests.add(dst);
|
||||
new StayConstraint(src, Strength.NORMAL);
|
||||
new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);
|
||||
}
|
||||
|
||||
change(src, 17);
|
||||
if (dst.value != 1170) alert("Projection 1 failed");
|
||||
change(dst, 1050);
|
||||
if (src.value != 5) alert("Projection 2 failed");
|
||||
change(scale, 5);
|
||||
for (var i = 0; i < n - 1; i++) {
|
||||
if (dests.at(i).value != i * 5 + 1000)
|
||||
alert("Projection 3 failed");
|
||||
}
|
||||
change(offset, 2000);
|
||||
for (var i = 0; i < n - 1; i++) {
|
||||
if (dests.at(i).value != i * 5 + 2000)
|
||||
alert("Projection 4 failed");
|
||||
}
|
||||
}
|
||||
|
||||
function change(v, newValue) {
|
||||
var edit = new EditConstraint(v, Strength.PREFERRED);
|
||||
var edits = new OrderedCollection();
|
||||
edits.add(edit);
|
||||
var plan = planner.extractPlanFromConstraints(edits);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
v.value = newValue;
|
||||
plan.execute();
|
||||
}
|
||||
edit.destroyConstraint();
|
||||
}
|
||||
|
||||
// Global variable holding the current planner.
|
||||
var planner = null;
|
||||
|
||||
function deltaBlue() {
|
||||
chainTest(100);
|
||||
projectionTest(100);
|
||||
}
|
||||
|
||||
var DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [
|
||||
new Benchmark('DeltaBlue', deltaBlue)
|
||||
]);
|
||||
4682
oden-js-sys/quickjs/tests/bench-v8/earley-boyer.js
Normal file
4682
oden-js-sys/quickjs/tests/bench-v8/earley-boyer.js
Normal file
File diff suppressed because one or more lines are too long
388
oden-js-sys/quickjs/tests/bench-v8/navier-stokes.js
Normal file
388
oden-js-sys/quickjs/tests/bench-v8/navier-stokes.js
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
/**
|
||||
* Copyright 2012 the V8 project authors. All rights reserved.
|
||||
* Copyright 2009 Oliver Hunt <http://nerget.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
var solver = null;
|
||||
|
||||
function runNavierStokes()
|
||||
{
|
||||
solver.update();
|
||||
}
|
||||
|
||||
function setupNavierStokes()
|
||||
{
|
||||
solver = new FluidField(null);
|
||||
solver.setResolution(128, 128);
|
||||
solver.setIterations(20);
|
||||
solver.setDisplayFunction(function(){});
|
||||
solver.setUICallback(prepareFrame);
|
||||
solver.reset();
|
||||
}
|
||||
|
||||
function tearDownNavierStokes()
|
||||
{
|
||||
solver = null;
|
||||
}
|
||||
|
||||
function addPoints(field) {
|
||||
var n = 64;
|
||||
for (var i = 1; i <= n; i++) {
|
||||
field.setVelocity(i, i, n, n);
|
||||
field.setDensity(i, i, 5);
|
||||
field.setVelocity(i, n - i, -n, -n);
|
||||
field.setDensity(i, n - i, 20);
|
||||
field.setVelocity(128 - i, n + i, -n, -n);
|
||||
field.setDensity(128 - i, n + i, 30);
|
||||
}
|
||||
}
|
||||
|
||||
var framesTillAddingPoints = 0;
|
||||
var framesBetweenAddingPoints = 5;
|
||||
|
||||
function prepareFrame(field)
|
||||
{
|
||||
if (framesTillAddingPoints == 0) {
|
||||
addPoints(field);
|
||||
framesTillAddingPoints = framesBetweenAddingPoints;
|
||||
framesBetweenAddingPoints++;
|
||||
} else {
|
||||
framesTillAddingPoints--;
|
||||
}
|
||||
}
|
||||
|
||||
// Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here.
|
||||
function FluidField(canvas) {
|
||||
function addFields(x, s, dt)
|
||||
{
|
||||
for (var i=0; i<size ; i++ ) x[i] += dt*s[i];
|
||||
}
|
||||
|
||||
function set_bnd(b, x)
|
||||
{
|
||||
if (b===1) {
|
||||
for (var i = 1; i <= width; i++) {
|
||||
x[i] = x[i + rowSize];
|
||||
x[i + (height+1) *rowSize] = x[i + height * rowSize];
|
||||
}
|
||||
|
||||
for (var j = 1; i <= height; i++) {
|
||||
x[j * rowSize] = -x[1 + j * rowSize];
|
||||
x[(width + 1) + j * rowSize] = -x[width + j * rowSize];
|
||||
}
|
||||
} else if (b === 2) {
|
||||
for (var i = 1; i <= width; i++) {
|
||||
x[i] = -x[i + rowSize];
|
||||
x[i + (height + 1) * rowSize] = -x[i + height * rowSize];
|
||||
}
|
||||
|
||||
for (var j = 1; j <= height; j++) {
|
||||
x[j * rowSize] = x[1 + j * rowSize];
|
||||
x[(width + 1) + j * rowSize] = x[width + j * rowSize];
|
||||
}
|
||||
} else {
|
||||
for (var i = 1; i <= width; i++) {
|
||||
x[i] = x[i + rowSize];
|
||||
x[i + (height + 1) * rowSize] = x[i + height * rowSize];
|
||||
}
|
||||
|
||||
for (var j = 1; j <= height; j++) {
|
||||
x[j * rowSize] = x[1 + j * rowSize];
|
||||
x[(width + 1) + j * rowSize] = x[width + j * rowSize];
|
||||
}
|
||||
}
|
||||
var maxEdge = (height + 1) * rowSize;
|
||||
x[0] = 0.5 * (x[1] + x[rowSize]);
|
||||
x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[height * rowSize]);
|
||||
x[(width+1)] = 0.5 * (x[width] + x[(width + 1) + rowSize]);
|
||||
x[(width+1)+maxEdge] = 0.5 * (x[width + maxEdge] + x[(width + 1) + height * rowSize]);
|
||||
}
|
||||
|
||||
function lin_solve(b, x, x0, a, c)
|
||||
{
|
||||
if (a === 0 && c === 1) {
|
||||
for (var j=1 ; j<=height; j++) {
|
||||
var currentRow = j * rowSize;
|
||||
++currentRow;
|
||||
for (var i = 0; i < width; i++) {
|
||||
x[currentRow] = x0[currentRow];
|
||||
++currentRow;
|
||||
}
|
||||
}
|
||||
set_bnd(b, x);
|
||||
} else {
|
||||
var invC = 1 / c;
|
||||
for (var k=0 ; k<iterations; k++) {
|
||||
for (var j=1 ; j<=height; j++) {
|
||||
var lastRow = (j - 1) * rowSize;
|
||||
var currentRow = j * rowSize;
|
||||
var nextRow = (j + 1) * rowSize;
|
||||
var lastX = x[currentRow];
|
||||
++currentRow;
|
||||
for (var i=1; i<=width; i++)
|
||||
lastX = x[currentRow] = (x0[currentRow] + a*(lastX+x[++currentRow]+x[++lastRow]+x[++nextRow])) * invC;
|
||||
}
|
||||
set_bnd(b, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function diffuse(b, x, x0, dt)
|
||||
{
|
||||
var a = 0;
|
||||
lin_solve(b, x, x0, a, 1 + 4*a);
|
||||
}
|
||||
|
||||
function lin_solve2(x, x0, y, y0, a, c)
|
||||
{
|
||||
if (a === 0 && c === 1) {
|
||||
for (var j=1 ; j <= height; j++) {
|
||||
var currentRow = j * rowSize;
|
||||
++currentRow;
|
||||
for (var i = 0; i < width; i++) {
|
||||
x[currentRow] = x0[currentRow];
|
||||
y[currentRow] = y0[currentRow];
|
||||
++currentRow;
|
||||
}
|
||||
}
|
||||
set_bnd(1, x);
|
||||
set_bnd(2, y);
|
||||
} else {
|
||||
var invC = 1/c;
|
||||
for (var k=0 ; k<iterations; k++) {
|
||||
for (var j=1 ; j <= height; j++) {
|
||||
var lastRow = (j - 1) * rowSize;
|
||||
var currentRow = j * rowSize;
|
||||
var nextRow = (j + 1) * rowSize;
|
||||
var lastX = x[currentRow];
|
||||
var lastY = y[currentRow];
|
||||
++currentRow;
|
||||
for (var i = 1; i <= width; i++) {
|
||||
lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC;
|
||||
lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC;
|
||||
}
|
||||
}
|
||||
set_bnd(1, x);
|
||||
set_bnd(2, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function diffuse2(x, x0, y, y0, dt)
|
||||
{
|
||||
var a = 0;
|
||||
lin_solve2(x, x0, y, y0, a, 1 + 4 * a);
|
||||
}
|
||||
|
||||
function advect(b, d, d0, u, v, dt)
|
||||
{
|
||||
var Wdt0 = dt * width;
|
||||
var Hdt0 = dt * height;
|
||||
var Wp5 = width + 0.5;
|
||||
var Hp5 = height + 0.5;
|
||||
for (var j = 1; j<= height; j++) {
|
||||
var pos = j * rowSize;
|
||||
for (var i = 1; i <= width; i++) {
|
||||
var x = i - Wdt0 * u[++pos];
|
||||
var y = j - Hdt0 * v[pos];
|
||||
if (x < 0.5)
|
||||
x = 0.5;
|
||||
else if (x > Wp5)
|
||||
x = Wp5;
|
||||
var i0 = x | 0;
|
||||
var i1 = i0 + 1;
|
||||
if (y < 0.5)
|
||||
y = 0.5;
|
||||
else if (y > Hp5)
|
||||
y = Hp5;
|
||||
var j0 = y | 0;
|
||||
var j1 = j0 + 1;
|
||||
var s1 = x - i0;
|
||||
var s0 = 1 - s1;
|
||||
var t1 = y - j0;
|
||||
var t0 = 1 - t1;
|
||||
var row1 = j0 * rowSize;
|
||||
var row2 = j1 * rowSize;
|
||||
d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]);
|
||||
}
|
||||
}
|
||||
set_bnd(b, d);
|
||||
}
|
||||
|
||||
function project(u, v, p, div)
|
||||
{
|
||||
var h = -0.5 / Math.sqrt(width * height);
|
||||
for (var j = 1 ; j <= height; j++ ) {
|
||||
var row = j * rowSize;
|
||||
var previousRow = (j - 1) * rowSize;
|
||||
var prevValue = row - 1;
|
||||
var currentRow = row;
|
||||
var nextValue = row + 1;
|
||||
var nextRow = (j + 1) * rowSize;
|
||||
for (var i = 1; i <= width; i++ ) {
|
||||
div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]);
|
||||
p[currentRow] = 0;
|
||||
}
|
||||
}
|
||||
set_bnd(0, div);
|
||||
set_bnd(0, p);
|
||||
|
||||
lin_solve(0, p, div, 1, 4 );
|
||||
var wScale = 0.5 * width;
|
||||
var hScale = 0.5 * height;
|
||||
for (var j = 1; j<= height; j++ ) {
|
||||
var prevPos = j * rowSize - 1;
|
||||
var currentPos = j * rowSize;
|
||||
var nextPos = j * rowSize + 1;
|
||||
var prevRow = (j - 1) * rowSize;
|
||||
var currentRow = j * rowSize;
|
||||
var nextRow = (j + 1) * rowSize;
|
||||
|
||||
for (var i = 1; i<= width; i++) {
|
||||
u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]);
|
||||
v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]);
|
||||
}
|
||||
}
|
||||
set_bnd(1, u);
|
||||
set_bnd(2, v);
|
||||
}
|
||||
|
||||
function dens_step(x, x0, u, v, dt)
|
||||
{
|
||||
addFields(x, x0, dt);
|
||||
diffuse(0, x0, x, dt );
|
||||
advect(0, x, x0, u, v, dt );
|
||||
}
|
||||
|
||||
function vel_step(u, v, u0, v0, dt)
|
||||
{
|
||||
addFields(u, u0, dt );
|
||||
addFields(v, v0, dt );
|
||||
var temp = u0; u0 = u; u = temp;
|
||||
var temp = v0; v0 = v; v = temp;
|
||||
diffuse2(u,u0,v,v0, dt);
|
||||
project(u, v, u0, v0);
|
||||
var temp = u0; u0 = u; u = temp;
|
||||
var temp = v0; v0 = v; v = temp;
|
||||
advect(1, u, u0, u0, v0, dt);
|
||||
advect(2, v, v0, u0, v0, dt);
|
||||
project(u, v, u0, v0 );
|
||||
}
|
||||
var uiCallback = function(d,u,v) {};
|
||||
|
||||
function Field(dens, u, v) {
|
||||
// Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%)
|
||||
// but makes the code ugly.
|
||||
this.setDensity = function(x, y, d) {
|
||||
dens[(x + 1) + (y + 1) * rowSize] = d;
|
||||
}
|
||||
this.getDensity = function(x, y) {
|
||||
return dens[(x + 1) + (y + 1) * rowSize];
|
||||
}
|
||||
this.setVelocity = function(x, y, xv, yv) {
|
||||
u[(x + 1) + (y + 1) * rowSize] = xv;
|
||||
v[(x + 1) + (y + 1) * rowSize] = yv;
|
||||
}
|
||||
this.getXVelocity = function(x, y) {
|
||||
return u[(x + 1) + (y + 1) * rowSize];
|
||||
}
|
||||
this.getYVelocity = function(x, y) {
|
||||
return v[(x + 1) + (y + 1) * rowSize];
|
||||
}
|
||||
this.width = function() { return width; }
|
||||
this.height = function() { return height; }
|
||||
}
|
||||
function queryUI(d, u, v)
|
||||
{
|
||||
for (var i = 0; i < size; i++)
|
||||
u[i] = v[i] = d[i] = 0.0;
|
||||
uiCallback(new Field(d, u, v));
|
||||
}
|
||||
|
||||
this.update = function () {
|
||||
queryUI(dens_prev, u_prev, v_prev);
|
||||
vel_step(u, v, u_prev, v_prev, dt);
|
||||
dens_step(dens, dens_prev, u, v, dt);
|
||||
displayFunc(new Field(dens, u, v));
|
||||
}
|
||||
this.setDisplayFunction = function(func) {
|
||||
displayFunc = func;
|
||||
}
|
||||
|
||||
this.iterations = function() { return iterations; }
|
||||
this.setIterations = function(iters) {
|
||||
if (iters > 0 && iters <= 100)
|
||||
iterations = iters;
|
||||
}
|
||||
this.setUICallback = function(callback) {
|
||||
uiCallback = callback;
|
||||
}
|
||||
var iterations = 10;
|
||||
var visc = 0.5;
|
||||
var dt = 0.1;
|
||||
var dens;
|
||||
var dens_prev;
|
||||
var u;
|
||||
var u_prev;
|
||||
var v;
|
||||
var v_prev;
|
||||
var width;
|
||||
var height;
|
||||
var rowSize;
|
||||
var size;
|
||||
var displayFunc;
|
||||
function reset()
|
||||
{
|
||||
rowSize = width + 2;
|
||||
size = (width+2)*(height+2);
|
||||
dens = new Array(size);
|
||||
dens_prev = new Array(size);
|
||||
u = new Array(size);
|
||||
u_prev = new Array(size);
|
||||
v = new Array(size);
|
||||
v_prev = new Array(size);
|
||||
for (var i = 0; i < size; i++)
|
||||
dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0;
|
||||
}
|
||||
this.reset = reset;
|
||||
this.setResolution = function (hRes, wRes)
|
||||
{
|
||||
var res = wRes * hRes;
|
||||
if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) {
|
||||
width = wRes;
|
||||
height = hRes;
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
this.setResolution(64, 64);
|
||||
}
|
||||
|
||||
var NavierStokes = new BenchmarkSuite('NavierStokes', 1484000,
|
||||
[new Benchmark('NavierStokes',
|
||||
runNavierStokes,
|
||||
setupNavierStokes,
|
||||
tearDownNavierStokes)]);
|
||||
|
||||
904
oden-js-sys/quickjs/tests/bench-v8/raytrace.js
Normal file
904
oden-js-sys/quickjs/tests/bench-v8/raytrace.js
Normal file
|
|
@ -0,0 +1,904 @@
|
|||
// The ray tracer code in this file is written by Adam Burmister. It
|
||||
// is available in its original form from:
|
||||
//
|
||||
// http://labs.flog.nz.co/raytracer/
|
||||
//
|
||||
// It has been modified slightly by Google to work as a standalone
|
||||
// benchmark, but the all the computational code remains
|
||||
// untouched. This file also contains a copy of parts of the Prototype
|
||||
// JavaScript framework which is used by the ray tracer.
|
||||
|
||||
// Variable used to hold a number that can be used to verify that
|
||||
// the scene was ray traced correctly.
|
||||
var checkNumber;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// The following is a copy of parts of the Prototype JavaScript library:
|
||||
|
||||
// Prototype JavaScript framework, version 1.5.0
|
||||
// (c) 2005-2007 Sam Stephenson
|
||||
//
|
||||
// Prototype is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the Prototype web site: http://prototype.conio.net/
|
||||
|
||||
var Class = {
|
||||
create: function() {
|
||||
return function() {
|
||||
this.initialize.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Object.extend = function(destination, source) {
|
||||
for (var property in source) {
|
||||
destination[property] = source[property];
|
||||
}
|
||||
return destination;
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// The rest of this file is the actual ray tracer written by Adam
|
||||
// Burmister. It's a concatenation of the following files:
|
||||
//
|
||||
// flog/color.js
|
||||
// flog/light.js
|
||||
// flog/vector.js
|
||||
// flog/ray.js
|
||||
// flog/scene.js
|
||||
// flog/material/basematerial.js
|
||||
// flog/material/solid.js
|
||||
// flog/material/chessboard.js
|
||||
// flog/shape/baseshape.js
|
||||
// flog/shape/sphere.js
|
||||
// flog/shape/plane.js
|
||||
// flog/intersectioninfo.js
|
||||
// flog/camera.js
|
||||
// flog/background.js
|
||||
// flog/engine.js
|
||||
|
||||
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Color = Class.create();
|
||||
|
||||
Flog.RayTracer.Color.prototype = {
|
||||
red : 0.0,
|
||||
green : 0.0,
|
||||
blue : 0.0,
|
||||
|
||||
initialize : function(r, g, b) {
|
||||
if(!r) r = 0.0;
|
||||
if(!g) g = 0.0;
|
||||
if(!b) b = 0.0;
|
||||
|
||||
this.red = r;
|
||||
this.green = g;
|
||||
this.blue = b;
|
||||
},
|
||||
|
||||
add : function(c1, c2){
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
|
||||
result.red = c1.red + c2.red;
|
||||
result.green = c1.green + c2.green;
|
||||
result.blue = c1.blue + c2.blue;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
addScalar: function(c1, s){
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
|
||||
result.red = c1.red + s;
|
||||
result.green = c1.green + s;
|
||||
result.blue = c1.blue + s;
|
||||
|
||||
result.limit();
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
subtract: function(c1, c2){
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
|
||||
result.red = c1.red - c2.red;
|
||||
result.green = c1.green - c2.green;
|
||||
result.blue = c1.blue - c2.blue;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
multiply : function(c1, c2) {
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
|
||||
result.red = c1.red * c2.red;
|
||||
result.green = c1.green * c2.green;
|
||||
result.blue = c1.blue * c2.blue;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
multiplyScalar : function(c1, f) {
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
|
||||
result.red = c1.red * f;
|
||||
result.green = c1.green * f;
|
||||
result.blue = c1.blue * f;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
divideFactor : function(c1, f) {
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
|
||||
result.red = c1.red / f;
|
||||
result.green = c1.green / f;
|
||||
result.blue = c1.blue / f;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
limit: function(){
|
||||
this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
|
||||
this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
|
||||
this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
|
||||
},
|
||||
|
||||
distance : function(color) {
|
||||
var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
|
||||
return d;
|
||||
},
|
||||
|
||||
blend: function(c1, c2, w){
|
||||
var result = new Flog.RayTracer.Color(0,0,0);
|
||||
result = Flog.RayTracer.Color.prototype.add(
|
||||
Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
|
||||
Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
|
||||
);
|
||||
return result;
|
||||
},
|
||||
|
||||
brightness : function() {
|
||||
var r = Math.floor(this.red*255);
|
||||
var g = Math.floor(this.green*255);
|
||||
var b = Math.floor(this.blue*255);
|
||||
return (r * 77 + g * 150 + b * 29) >> 8;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
var r = Math.floor(this.red*255);
|
||||
var g = Math.floor(this.green*255);
|
||||
var b = Math.floor(this.blue*255);
|
||||
|
||||
return "rgb("+ r +","+ g +","+ b +")";
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Light = Class.create();
|
||||
|
||||
Flog.RayTracer.Light.prototype = {
|
||||
position: null,
|
||||
color: null,
|
||||
intensity: 10.0,
|
||||
|
||||
initialize : function(pos, color, intensity) {
|
||||
this.position = pos;
|
||||
this.color = color;
|
||||
this.intensity = (intensity ? intensity : 10.0);
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Vector = Class.create();
|
||||
|
||||
Flog.RayTracer.Vector.prototype = {
|
||||
x : 0.0,
|
||||
y : 0.0,
|
||||
z : 0.0,
|
||||
|
||||
initialize : function(x, y, z) {
|
||||
this.x = (x ? x : 0);
|
||||
this.y = (y ? y : 0);
|
||||
this.z = (z ? z : 0);
|
||||
},
|
||||
|
||||
copy: function(vector){
|
||||
this.x = vector.x;
|
||||
this.y = vector.y;
|
||||
this.z = vector.z;
|
||||
},
|
||||
|
||||
normalize : function() {
|
||||
var m = this.magnitude();
|
||||
return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
|
||||
},
|
||||
|
||||
magnitude : function() {
|
||||
return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
|
||||
},
|
||||
|
||||
cross : function(w) {
|
||||
return new Flog.RayTracer.Vector(
|
||||
-this.z * w.y + this.y * w.z,
|
||||
this.z * w.x - this.x * w.z,
|
||||
-this.y * w.x + this.x * w.y);
|
||||
},
|
||||
|
||||
dot : function(w) {
|
||||
return this.x * w.x + this.y * w.y + this.z * w.z;
|
||||
},
|
||||
|
||||
add : function(v, w) {
|
||||
return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
|
||||
},
|
||||
|
||||
subtract : function(v, w) {
|
||||
if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
|
||||
return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
|
||||
},
|
||||
|
||||
multiplyVector : function(v, w) {
|
||||
return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
|
||||
},
|
||||
|
||||
multiplyScalar : function(v, w) {
|
||||
return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Ray = Class.create();
|
||||
|
||||
Flog.RayTracer.Ray.prototype = {
|
||||
position : null,
|
||||
direction : null,
|
||||
initialize : function(pos, dir) {
|
||||
this.position = pos;
|
||||
this.direction = dir;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Ray [' + this.position + ',' + this.direction + ']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Scene = Class.create();
|
||||
|
||||
Flog.RayTracer.Scene.prototype = {
|
||||
camera : null,
|
||||
shapes : [],
|
||||
lights : [],
|
||||
background : null,
|
||||
|
||||
initialize : function() {
|
||||
this.camera = new Flog.RayTracer.Camera(
|
||||
new Flog.RayTracer.Vector(0,0,-5),
|
||||
new Flog.RayTracer.Vector(0,0,1),
|
||||
new Flog.RayTracer.Vector(0,1,0)
|
||||
);
|
||||
this.shapes = new Array();
|
||||
this.lights = new Array();
|
||||
this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
|
||||
|
||||
Flog.RayTracer.Material.BaseMaterial = Class.create();
|
||||
|
||||
Flog.RayTracer.Material.BaseMaterial.prototype = {
|
||||
|
||||
gloss: 2.0, // [0...infinity] 0 = matt
|
||||
transparency: 0.0, // 0=opaque
|
||||
reflection: 0.0, // [0...infinity] 0 = no reflection
|
||||
refraction: 0.50,
|
||||
hasTexture: false,
|
||||
|
||||
initialize : function() {
|
||||
|
||||
},
|
||||
|
||||
getColor: function(u, v){
|
||||
|
||||
},
|
||||
|
||||
wrapUp: function(t){
|
||||
t = t % 2.0;
|
||||
if(t < -1) t += 2.0;
|
||||
if(t >= 1) t -= 2.0;
|
||||
return t;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Material.Solid = Class.create();
|
||||
|
||||
Flog.RayTracer.Material.Solid.prototype = Object.extend(
|
||||
new Flog.RayTracer.Material.BaseMaterial(), {
|
||||
initialize : function(color, reflection, refraction, transparency, gloss) {
|
||||
this.color = color;
|
||||
this.reflection = reflection;
|
||||
this.transparency = transparency;
|
||||
this.gloss = gloss;
|
||||
this.hasTexture = false;
|
||||
},
|
||||
|
||||
getColor: function(u, v){
|
||||
return this.color;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
|
||||
}
|
||||
}
|
||||
);
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Material.Chessboard = Class.create();
|
||||
|
||||
Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
|
||||
new Flog.RayTracer.Material.BaseMaterial(), {
|
||||
colorEven: null,
|
||||
colorOdd: null,
|
||||
density: 0.5,
|
||||
|
||||
initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
|
||||
this.colorEven = colorEven;
|
||||
this.colorOdd = colorOdd;
|
||||
this.reflection = reflection;
|
||||
this.transparency = transparency;
|
||||
this.gloss = gloss;
|
||||
this.density = density;
|
||||
this.hasTexture = true;
|
||||
},
|
||||
|
||||
getColor: function(u, v){
|
||||
var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
|
||||
|
||||
if(t < 0.0)
|
||||
return this.colorEven;
|
||||
else
|
||||
return this.colorOdd;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
|
||||
}
|
||||
}
|
||||
);
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
|
||||
|
||||
Flog.RayTracer.Shape.Sphere = Class.create();
|
||||
|
||||
Flog.RayTracer.Shape.Sphere.prototype = {
|
||||
initialize : function(pos, radius, material) {
|
||||
this.radius = radius;
|
||||
this.position = pos;
|
||||
this.material = material;
|
||||
},
|
||||
|
||||
intersect: function(ray){
|
||||
var info = new Flog.RayTracer.IntersectionInfo();
|
||||
info.shape = this;
|
||||
|
||||
var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
|
||||
|
||||
var B = dst.dot(ray.direction);
|
||||
var C = dst.dot(dst) - (this.radius * this.radius);
|
||||
var D = (B * B) - C;
|
||||
|
||||
if(D > 0){ // intersection!
|
||||
info.isHit = true;
|
||||
info.distance = (-B) - Math.sqrt(D);
|
||||
info.position = Flog.RayTracer.Vector.prototype.add(
|
||||
ray.position,
|
||||
Flog.RayTracer.Vector.prototype.multiplyScalar(
|
||||
ray.direction,
|
||||
info.distance
|
||||
)
|
||||
);
|
||||
info.normal = Flog.RayTracer.Vector.prototype.subtract(
|
||||
info.position,
|
||||
this.position
|
||||
).normalize();
|
||||
|
||||
info.color = this.material.getColor(0,0);
|
||||
} else {
|
||||
info.isHit = false;
|
||||
}
|
||||
return info;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
|
||||
|
||||
Flog.RayTracer.Shape.Plane = Class.create();
|
||||
|
||||
Flog.RayTracer.Shape.Plane.prototype = {
|
||||
d: 0.0,
|
||||
|
||||
initialize : function(pos, d, material) {
|
||||
this.position = pos;
|
||||
this.d = d;
|
||||
this.material = material;
|
||||
},
|
||||
|
||||
intersect: function(ray){
|
||||
var info = new Flog.RayTracer.IntersectionInfo();
|
||||
|
||||
var Vd = this.position.dot(ray.direction);
|
||||
if(Vd == 0) return info; // no intersection
|
||||
|
||||
var t = -(this.position.dot(ray.position) + this.d) / Vd;
|
||||
if(t <= 0) return info;
|
||||
|
||||
info.shape = this;
|
||||
info.isHit = true;
|
||||
info.position = Flog.RayTracer.Vector.prototype.add(
|
||||
ray.position,
|
||||
Flog.RayTracer.Vector.prototype.multiplyScalar(
|
||||
ray.direction,
|
||||
t
|
||||
)
|
||||
);
|
||||
info.normal = this.position;
|
||||
info.distance = t;
|
||||
|
||||
if(this.material.hasTexture){
|
||||
var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
|
||||
var vV = vU.cross(this.position);
|
||||
var u = info.position.dot(vU);
|
||||
var v = info.position.dot(vV);
|
||||
info.color = this.material.getColor(u,v);
|
||||
} else {
|
||||
info.color = this.material.getColor(0,0);
|
||||
}
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Plane [' + this.position + ', d=' + this.d + ']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.IntersectionInfo = Class.create();
|
||||
|
||||
Flog.RayTracer.IntersectionInfo.prototype = {
|
||||
isHit: false,
|
||||
hitCount: 0,
|
||||
shape: null,
|
||||
position: null,
|
||||
normal: null,
|
||||
color: null,
|
||||
distance: null,
|
||||
|
||||
initialize : function() {
|
||||
this.color = new Flog.RayTracer.Color(0,0,0);
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Intersection [' + this.position + ']';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Camera = Class.create();
|
||||
|
||||
Flog.RayTracer.Camera.prototype = {
|
||||
position: null,
|
||||
lookAt: null,
|
||||
equator: null,
|
||||
up: null,
|
||||
screen: null,
|
||||
|
||||
initialize : function(pos, lookAt, up) {
|
||||
this.position = pos;
|
||||
this.lookAt = lookAt;
|
||||
this.up = up;
|
||||
this.equator = lookAt.normalize().cross(this.up);
|
||||
this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
|
||||
},
|
||||
|
||||
getRay: function(vx, vy){
|
||||
var pos = Flog.RayTracer.Vector.prototype.subtract(
|
||||
this.screen,
|
||||
Flog.RayTracer.Vector.prototype.subtract(
|
||||
Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
|
||||
Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
|
||||
)
|
||||
);
|
||||
pos.y = pos.y * -1;
|
||||
var dir = Flog.RayTracer.Vector.prototype.subtract(
|
||||
pos,
|
||||
this.position
|
||||
);
|
||||
|
||||
var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
|
||||
|
||||
return ray;
|
||||
},
|
||||
|
||||
toString : function () {
|
||||
return 'Ray []';
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Background = Class.create();
|
||||
|
||||
Flog.RayTracer.Background.prototype = {
|
||||
color : null,
|
||||
ambience : 0.0,
|
||||
|
||||
initialize : function(color, ambience) {
|
||||
this.color = color;
|
||||
this.ambience = ambience;
|
||||
}
|
||||
}
|
||||
/* Fake a Flog.* namespace */
|
||||
if(typeof(Flog) == 'undefined') var Flog = {};
|
||||
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
|
||||
|
||||
Flog.RayTracer.Engine = Class.create();
|
||||
|
||||
Flog.RayTracer.Engine.prototype = {
|
||||
canvas: null, /* 2d context we can render to */
|
||||
|
||||
initialize: function(options){
|
||||
this.options = Object.extend({
|
||||
canvasHeight: 100,
|
||||
canvasWidth: 100,
|
||||
pixelWidth: 2,
|
||||
pixelHeight: 2,
|
||||
renderDiffuse: false,
|
||||
renderShadows: false,
|
||||
renderHighlights: false,
|
||||
renderReflections: false,
|
||||
rayDepth: 2
|
||||
}, options || {});
|
||||
|
||||
this.options.canvasHeight /= this.options.pixelHeight;
|
||||
this.options.canvasWidth /= this.options.pixelWidth;
|
||||
|
||||
/* TODO: dynamically include other scripts */
|
||||
},
|
||||
|
||||
setPixel: function(x, y, color){
|
||||
var pxW, pxH;
|
||||
pxW = this.options.pixelWidth;
|
||||
pxH = this.options.pixelHeight;
|
||||
|
||||
if (this.canvas) {
|
||||
this.canvas.fillStyle = color.toString();
|
||||
this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
|
||||
} else {
|
||||
if (x === y) {
|
||||
checkNumber += color.brightness();
|
||||
}
|
||||
// print(x * pxW, y * pxH, pxW, pxH);
|
||||
}
|
||||
},
|
||||
|
||||
renderScene: function(scene, canvas){
|
||||
checkNumber = 0;
|
||||
/* Get canvas */
|
||||
if (canvas) {
|
||||
this.canvas = canvas.getContext("2d");
|
||||
} else {
|
||||
this.canvas = null;
|
||||
}
|
||||
|
||||
var canvasHeight = this.options.canvasHeight;
|
||||
var canvasWidth = this.options.canvasWidth;
|
||||
|
||||
for(var y=0; y < canvasHeight; y++){
|
||||
for(var x=0; x < canvasWidth; x++){
|
||||
var yp = y * 1.0 / canvasHeight * 2 - 1;
|
||||
var xp = x * 1.0 / canvasWidth * 2 - 1;
|
||||
|
||||
var ray = scene.camera.getRay(xp, yp);
|
||||
|
||||
var color = this.getPixelColor(ray, scene);
|
||||
|
||||
this.setPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
if (checkNumber !== 2321) {
|
||||
throw new Error("Scene rendered incorrectly");
|
||||
}
|
||||
},
|
||||
|
||||
getPixelColor: function(ray, scene){
|
||||
var info = this.testIntersection(ray, scene, null);
|
||||
if(info.isHit){
|
||||
var color = this.rayTrace(info, ray, scene, 0);
|
||||
return color;
|
||||
}
|
||||
return scene.background.color;
|
||||
},
|
||||
|
||||
testIntersection: function(ray, scene, exclude){
|
||||
var hits = 0;
|
||||
var best = new Flog.RayTracer.IntersectionInfo();
|
||||
best.distance = 2000;
|
||||
|
||||
for(var i=0; i<scene.shapes.length; i++){
|
||||
var shape = scene.shapes[i];
|
||||
|
||||
if(shape != exclude){
|
||||
var info = shape.intersect(ray);
|
||||
if(info.isHit && info.distance >= 0 && info.distance < best.distance){
|
||||
best = info;
|
||||
hits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
best.hitCount = hits;
|
||||
return best;
|
||||
},
|
||||
|
||||
getReflectionRay: function(P,N,V){
|
||||
var c1 = -N.dot(V);
|
||||
var R1 = Flog.RayTracer.Vector.prototype.add(
|
||||
Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
|
||||
V
|
||||
);
|
||||
return new Flog.RayTracer.Ray(P, R1);
|
||||
},
|
||||
|
||||
rayTrace: function(info, ray, scene, depth){
|
||||
// Calc ambient
|
||||
var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
|
||||
var oldColor = color;
|
||||
var shininess = Math.pow(10, info.shape.material.gloss + 1);
|
||||
|
||||
for(var i=0; i<scene.lights.length; i++){
|
||||
var light = scene.lights[i];
|
||||
|
||||
// Calc diffuse lighting
|
||||
var v = Flog.RayTracer.Vector.prototype.subtract(
|
||||
light.position,
|
||||
info.position
|
||||
).normalize();
|
||||
|
||||
if(this.options.renderDiffuse){
|
||||
var L = v.dot(info.normal);
|
||||
if(L > 0.0){
|
||||
color = Flog.RayTracer.Color.prototype.add(
|
||||
color,
|
||||
Flog.RayTracer.Color.prototype.multiply(
|
||||
info.color,
|
||||
Flog.RayTracer.Color.prototype.multiplyScalar(
|
||||
light.color,
|
||||
L
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The greater the depth the more accurate the colours, but
|
||||
// this is exponentially (!) expensive
|
||||
if(depth <= this.options.rayDepth){
|
||||
// calculate reflection ray
|
||||
if(this.options.renderReflections && info.shape.material.reflection > 0)
|
||||
{
|
||||
var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
|
||||
var refl = this.testIntersection(reflectionRay, scene, info.shape);
|
||||
|
||||
if (refl.isHit && refl.distance > 0){
|
||||
refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
|
||||
} else {
|
||||
refl.color = scene.background.color;
|
||||
}
|
||||
|
||||
color = Flog.RayTracer.Color.prototype.blend(
|
||||
color,
|
||||
refl.color,
|
||||
info.shape.material.reflection
|
||||
);
|
||||
}
|
||||
|
||||
// Refraction
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
/* Render shadows and highlights */
|
||||
|
||||
var shadowInfo = new Flog.RayTracer.IntersectionInfo();
|
||||
|
||||
if(this.options.renderShadows){
|
||||
var shadowRay = new Flog.RayTracer.Ray(info.position, v);
|
||||
|
||||
shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
|
||||
if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
|
||||
var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
|
||||
var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
|
||||
color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
|
||||
}
|
||||
}
|
||||
|
||||
// Phong specular highlights
|
||||
if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
|
||||
var Lv = Flog.RayTracer.Vector.prototype.subtract(
|
||||
info.shape.position,
|
||||
light.position
|
||||
).normalize();
|
||||
|
||||
var E = Flog.RayTracer.Vector.prototype.subtract(
|
||||
scene.camera.position,
|
||||
info.shape.position
|
||||
).normalize();
|
||||
|
||||
var H = Flog.RayTracer.Vector.prototype.subtract(
|
||||
E,
|
||||
Lv
|
||||
).normalize();
|
||||
|
||||
var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
|
||||
color = Flog.RayTracer.Color.prototype.add(
|
||||
Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
|
||||
color
|
||||
);
|
||||
}
|
||||
}
|
||||
color.limit();
|
||||
return color;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function renderScene(){
|
||||
var scene = new Flog.RayTracer.Scene();
|
||||
|
||||
scene.camera = new Flog.RayTracer.Camera(
|
||||
new Flog.RayTracer.Vector(0, 0, -15),
|
||||
new Flog.RayTracer.Vector(-0.2, 0, 5),
|
||||
new Flog.RayTracer.Vector(0, 1, 0)
|
||||
);
|
||||
|
||||
scene.background = new Flog.RayTracer.Background(
|
||||
new Flog.RayTracer.Color(0.5, 0.5, 0.5),
|
||||
0.4
|
||||
);
|
||||
|
||||
var sphere = new Flog.RayTracer.Shape.Sphere(
|
||||
new Flog.RayTracer.Vector(-1.5, 1.5, 2),
|
||||
1.5,
|
||||
new Flog.RayTracer.Material.Solid(
|
||||
new Flog.RayTracer.Color(0,0.5,0.5),
|
||||
0.3,
|
||||
0.0,
|
||||
0.0,
|
||||
2.0
|
||||
)
|
||||
);
|
||||
|
||||
var sphere1 = new Flog.RayTracer.Shape.Sphere(
|
||||
new Flog.RayTracer.Vector(1, 0.25, 1),
|
||||
0.5,
|
||||
new Flog.RayTracer.Material.Solid(
|
||||
new Flog.RayTracer.Color(0.9,0.9,0.9),
|
||||
0.1,
|
||||
0.0,
|
||||
0.0,
|
||||
1.5
|
||||
)
|
||||
);
|
||||
|
||||
var plane = new Flog.RayTracer.Shape.Plane(
|
||||
new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
|
||||
1.2,
|
||||
new Flog.RayTracer.Material.Chessboard(
|
||||
new Flog.RayTracer.Color(1,1,1),
|
||||
new Flog.RayTracer.Color(0,0,0),
|
||||
0.2,
|
||||
0.0,
|
||||
1.0,
|
||||
0.7
|
||||
)
|
||||
);
|
||||
|
||||
scene.shapes.push(plane);
|
||||
scene.shapes.push(sphere);
|
||||
scene.shapes.push(sphere1);
|
||||
|
||||
var light = new Flog.RayTracer.Light(
|
||||
new Flog.RayTracer.Vector(5, 10, -1),
|
||||
new Flog.RayTracer.Color(0.8, 0.8, 0.8)
|
||||
);
|
||||
|
||||
var light1 = new Flog.RayTracer.Light(
|
||||
new Flog.RayTracer.Vector(-3, 5, -15),
|
||||
new Flog.RayTracer.Color(0.8, 0.8, 0.8),
|
||||
100
|
||||
);
|
||||
|
||||
scene.lights.push(light);
|
||||
scene.lights.push(light1);
|
||||
|
||||
var imageWidth = 100; // $F('imageWidth');
|
||||
var imageHeight = 100; // $F('imageHeight');
|
||||
var pixelSize = "5,5".split(','); // $F('pixelSize').split(',');
|
||||
var renderDiffuse = true; // $F('renderDiffuse');
|
||||
var renderShadows = true; // $F('renderShadows');
|
||||
var renderHighlights = true; // $F('renderHighlights');
|
||||
var renderReflections = true; // $F('renderReflections');
|
||||
var rayDepth = 2;//$F('rayDepth');
|
||||
|
||||
var raytracer = new Flog.RayTracer.Engine(
|
||||
{
|
||||
canvasWidth: imageWidth,
|
||||
canvasHeight: imageHeight,
|
||||
pixelWidth: pixelSize[0],
|
||||
pixelHeight: pixelSize[1],
|
||||
"renderDiffuse": renderDiffuse,
|
||||
"renderHighlights": renderHighlights,
|
||||
"renderShadows": renderShadows,
|
||||
"renderReflections": renderReflections,
|
||||
"rayDepth": rayDepth
|
||||
}
|
||||
);
|
||||
|
||||
raytracer.renderScene(scene, null, 0);
|
||||
}
|
||||
|
||||
var RayTrace = new BenchmarkSuite('RayTrace', 739989, [
|
||||
new Benchmark('RayTrace', renderScene)
|
||||
]);
|
||||
|
||||
|
||||
1764
oden-js-sys/quickjs/tests/bench-v8/regexp.js
Normal file
1764
oden-js-sys/quickjs/tests/bench-v8/regexp.js
Normal file
File diff suppressed because it is too large
Load diff
104
oden-js-sys/quickjs/tests/bench-v8/revisions.html
Normal file
104
oden-js-sys/quickjs/tests/bench-v8/revisions.html
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>V8 Benchmark Suite Revisions</title>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div class="title"><h1>V8 Benchmark Suite Revisions</h1></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="contents">
|
||||
|
||||
<p>
|
||||
|
||||
The V8 benchmark suite is changed from time to time as we fix bugs or
|
||||
expand the scope of the benchmarks. Here is a list of revisions, with
|
||||
a description of the changes made. Note that benchmark results are
|
||||
not comparable unless both results are run with the same revision of
|
||||
the benchmark suite.
|
||||
|
||||
</p>
|
||||
<div class="subtitle"><h3>Version 7 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v7/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>This version includes the new Navier-Stokes benchmark, a 2D differential
|
||||
equation solver that stresses arithmetic computations on double arrays.</p>
|
||||
|
||||
<div class="subtitle"><h3>Version 6 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v6/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>Removed dead code from the RayTrace benchmark and fixed a couple of
|
||||
typos in the DeltaBlue implementation. Changed the Splay benchmark to
|
||||
avoid converting the same numeric key to a string over and over again
|
||||
and to avoid inserting and removing the same element repeatedly thus
|
||||
increasing pressure on the memory subsystem. Changed the RegExp
|
||||
benchmark to exercise the regular expression engine on different input
|
||||
strings.</p>
|
||||
|
||||
<p>Furthermore, the benchmark runner was changed to run the benchmarks
|
||||
for at least a few times to stabilize the reported numbers on slower
|
||||
machines.</p>
|
||||
|
||||
<div class="subtitle"><h3>Version 5 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v5/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>Removed duplicate line in random seed code, and changed the name of
|
||||
the Object.prototype.inherits function in the DeltaBlue benchmark to
|
||||
inheritsFrom to avoid name clashes when running in Chromium with
|
||||
extensions enabled.
|
||||
</p>
|
||||
|
||||
<div class="subtitle"><h3>Version 4 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v4/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>The <i>Splay</i> benchmark is a newcomer in version 4. It
|
||||
manipulates a splay tree by adding and removing data nodes, thus
|
||||
exercising the memory management subsystem of the JavaScript engine.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Furthermore, all the unused parts of the Prototype library were
|
||||
removed from the RayTrace benchmark. This does not affect the running
|
||||
of the benchmark.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="subtitle"><h3>Version 3 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v3/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>Version 3 adds a new benchmark, <i>RegExp</i>. The RegExp
|
||||
benchmark is generated by loading 50 of the most popular pages on the
|
||||
web and logging all regexp operations performed. Each operation is
|
||||
given a weight that is calculated from an estimate of the popularity
|
||||
of the pages where it occurs and the number of times it is executed
|
||||
while loading each page. Finally the literal letters in the data are
|
||||
encoded using ROT13 in a way that does not affect how the regexps
|
||||
match their input.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="subtitle"><h3>Version 2 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v2/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>For version 2 the Crypto benchmark was fixed. Previously, the
|
||||
decryption stage was given plaintext as input, which resulted in an
|
||||
error. Now, the decryption stage is given the output of the
|
||||
encryption stage as input. The result is checked against the original
|
||||
plaintext. For this to give the correct results the crypto objects
|
||||
are reset for each iteration of the benchmark. In addition, the size
|
||||
of the plain text has been increased a little and the use of
|
||||
Math.random() and new Date() to build an RNG pool has been
|
||||
removed. </p>
|
||||
|
||||
<p>Other benchmarks were fixed to do elementary verification of the
|
||||
results of their calculations. This is to avoid accidentally
|
||||
obtaining scores that are the result of an incorrect JavaScript engine
|
||||
optimization.</p>
|
||||
|
||||
|
||||
<div class="subtitle"><h3>Version 1 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v1/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>Initial release.</p>
|
||||
|
||||
</td><td style="text-align: center">
|
||||
</td></tr></table>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
539
oden-js-sys/quickjs/tests/bench-v8/richards.js
Normal file
539
oden-js-sys/quickjs/tests/bench-v8/richards.js
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// This is a JavaScript implementation of the Richards
|
||||
// benchmark from:
|
||||
//
|
||||
// http://www.cl.cam.ac.uk/~mr10/Bench.html
|
||||
//
|
||||
// The benchmark was originally implemented in BCPL by
|
||||
// Martin Richards.
|
||||
|
||||
|
||||
/**
|
||||
* The Richards benchmark simulates the task dispatcher of an
|
||||
* operating system.
|
||||
**/
|
||||
function runRichards() {
|
||||
var scheduler = new Scheduler();
|
||||
scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
|
||||
|
||||
var queue = new Packet(null, ID_WORKER, KIND_WORK);
|
||||
queue = new Packet(queue, ID_WORKER, KIND_WORK);
|
||||
scheduler.addWorkerTask(ID_WORKER, 1000, queue);
|
||||
|
||||
queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
|
||||
scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
|
||||
|
||||
queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
|
||||
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
|
||||
scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
|
||||
|
||||
scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
|
||||
|
||||
scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
|
||||
|
||||
scheduler.schedule();
|
||||
|
||||
if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
|
||||
scheduler.holdCount != EXPECTED_HOLD_COUNT) {
|
||||
var msg =
|
||||
"Error during execution: queueCount = " + scheduler.queueCount +
|
||||
", holdCount = " + scheduler.holdCount + ".";
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
var COUNT = 1000;
|
||||
|
||||
/**
|
||||
* These two constants specify how many times a packet is queued and
|
||||
* how many times a task is put on hold in a correct run of richards.
|
||||
* They don't have any meaning a such but are characteristic of a
|
||||
* correct run so if the actual queue or hold count is different from
|
||||
* the expected there must be a bug in the implementation.
|
||||
**/
|
||||
var EXPECTED_QUEUE_COUNT = 2322;
|
||||
var EXPECTED_HOLD_COUNT = 928;
|
||||
|
||||
|
||||
/**
|
||||
* A scheduler can be used to schedule a set of tasks based on their relative
|
||||
* priorities. Scheduling is done by maintaining a list of task control blocks
|
||||
* which holds tasks and the data queue they are processing.
|
||||
* @constructor
|
||||
*/
|
||||
function Scheduler() {
|
||||
this.queueCount = 0;
|
||||
this.holdCount = 0;
|
||||
this.blocks = new Array(NUMBER_OF_IDS);
|
||||
this.list = null;
|
||||
this.currentTcb = null;
|
||||
this.currentId = null;
|
||||
}
|
||||
|
||||
var ID_IDLE = 0;
|
||||
var ID_WORKER = 1;
|
||||
var ID_HANDLER_A = 2;
|
||||
var ID_HANDLER_B = 3;
|
||||
var ID_DEVICE_A = 4;
|
||||
var ID_DEVICE_B = 5;
|
||||
var NUMBER_OF_IDS = 6;
|
||||
|
||||
var KIND_DEVICE = 0;
|
||||
var KIND_WORK = 1;
|
||||
|
||||
/**
|
||||
* Add an idle task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {int} count the number of times to schedule the task
|
||||
*/
|
||||
Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
|
||||
this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a work task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new HandlerTask(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
*/
|
||||
Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
|
||||
this.addTask(id, priority, queue, new DeviceTask(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified task and mark it as running.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {Task} task the task to add
|
||||
*/
|
||||
Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
|
||||
this.addTask(id, priority, queue, task);
|
||||
this.currentTcb.setRunning();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified task to this scheduler.
|
||||
* @param {int} id the identity of the task
|
||||
* @param {int} priority the task's priority
|
||||
* @param {Packet} queue the queue of work to be processed by the task
|
||||
* @param {Task} task the task to add
|
||||
*/
|
||||
Scheduler.prototype.addTask = function (id, priority, queue, task) {
|
||||
this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
|
||||
this.list = this.currentTcb;
|
||||
this.blocks[id] = this.currentTcb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the tasks managed by this scheduler.
|
||||
*/
|
||||
Scheduler.prototype.schedule = function () {
|
||||
this.currentTcb = this.list;
|
||||
while (this.currentTcb != null) {
|
||||
if (this.currentTcb.isHeldOrSuspended()) {
|
||||
this.currentTcb = this.currentTcb.link;
|
||||
} else {
|
||||
this.currentId = this.currentTcb.id;
|
||||
this.currentTcb = this.currentTcb.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Release a task that is currently blocked and return the next block to run.
|
||||
* @param {int} id the id of the task to suspend
|
||||
*/
|
||||
Scheduler.prototype.release = function (id) {
|
||||
var tcb = this.blocks[id];
|
||||
if (tcb == null) return tcb;
|
||||
tcb.markAsNotHeld();
|
||||
if (tcb.priority > this.currentTcb.priority) {
|
||||
return tcb;
|
||||
} else {
|
||||
return this.currentTcb;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Block the currently executing task and return the next task control block
|
||||
* to run. The blocked task will not be made runnable until it is explicitly
|
||||
* released, even if new work is added to it.
|
||||
*/
|
||||
Scheduler.prototype.holdCurrent = function () {
|
||||
this.holdCount++;
|
||||
this.currentTcb.markAsHeld();
|
||||
return this.currentTcb.link;
|
||||
};
|
||||
|
||||
/**
|
||||
* Suspend the currently executing task and return the next task control block
|
||||
* to run. If new work is added to the suspended task it will be made runnable.
|
||||
*/
|
||||
Scheduler.prototype.suspendCurrent = function () {
|
||||
this.currentTcb.markAsSuspended();
|
||||
return this.currentTcb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the specified packet to the end of the worklist used by the task
|
||||
* associated with the packet and make the task runnable if it is currently
|
||||
* suspended.
|
||||
* @param {Packet} packet the packet to add
|
||||
*/
|
||||
Scheduler.prototype.queue = function (packet) {
|
||||
var t = this.blocks[packet.id];
|
||||
if (t == null) return t;
|
||||
this.queueCount++;
|
||||
packet.link = null;
|
||||
packet.id = this.currentId;
|
||||
return t.checkPriorityAdd(this.currentTcb, packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* A task control block manages a task and the queue of work packages associated
|
||||
* with it.
|
||||
* @param {TaskControlBlock} link the preceding block in the linked block list
|
||||
* @param {int} id the id of this block
|
||||
* @param {int} priority the priority of this block
|
||||
* @param {Packet} queue the queue of packages to be processed by the task
|
||||
* @param {Task} task the task
|
||||
* @constructor
|
||||
*/
|
||||
function TaskControlBlock(link, id, priority, queue, task) {
|
||||
this.link = link;
|
||||
this.id = id;
|
||||
this.priority = priority;
|
||||
this.queue = queue;
|
||||
this.task = task;
|
||||
if (queue == null) {
|
||||
this.state = STATE_SUSPENDED;
|
||||
} else {
|
||||
this.state = STATE_SUSPENDED_RUNNABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The task is running and is currently scheduled.
|
||||
*/
|
||||
var STATE_RUNNING = 0;
|
||||
|
||||
/**
|
||||
* The task has packets left to process.
|
||||
*/
|
||||
var STATE_RUNNABLE = 1;
|
||||
|
||||
/**
|
||||
* The task is not currently running. The task is not blocked as such and may
|
||||
* be started by the scheduler.
|
||||
*/
|
||||
var STATE_SUSPENDED = 2;
|
||||
|
||||
/**
|
||||
* The task is blocked and cannot be run until it is explicitly released.
|
||||
*/
|
||||
var STATE_HELD = 4;
|
||||
|
||||
var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
|
||||
var STATE_NOT_HELD = ~STATE_HELD;
|
||||
|
||||
TaskControlBlock.prototype.setRunning = function () {
|
||||
this.state = STATE_RUNNING;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsNotHeld = function () {
|
||||
this.state = this.state & STATE_NOT_HELD;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsHeld = function () {
|
||||
this.state = this.state | STATE_HELD;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.isHeldOrSuspended = function () {
|
||||
return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsSuspended = function () {
|
||||
this.state = this.state | STATE_SUSPENDED;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.markAsRunnable = function () {
|
||||
this.state = this.state | STATE_RUNNABLE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs this task, if it is ready to be run, and returns the next task to run.
|
||||
*/
|
||||
TaskControlBlock.prototype.run = function () {
|
||||
var packet;
|
||||
if (this.state == STATE_SUSPENDED_RUNNABLE) {
|
||||
packet = this.queue;
|
||||
this.queue = packet.link;
|
||||
if (this.queue == null) {
|
||||
this.state = STATE_RUNNING;
|
||||
} else {
|
||||
this.state = STATE_RUNNABLE;
|
||||
}
|
||||
} else {
|
||||
packet = null;
|
||||
}
|
||||
return this.task.run(packet);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a packet to the worklist of this block's task, marks this as runnable if
|
||||
* necessary, and returns the next runnable object to run (the one
|
||||
* with the highest priority).
|
||||
*/
|
||||
TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
|
||||
if (this.queue == null) {
|
||||
this.queue = packet;
|
||||
this.markAsRunnable();
|
||||
if (this.priority > task.priority) return this;
|
||||
} else {
|
||||
this.queue = packet.addTo(this.queue);
|
||||
}
|
||||
return task;
|
||||
};
|
||||
|
||||
TaskControlBlock.prototype.toString = function () {
|
||||
return "tcb { " + this.task + "@" + this.state + " }";
|
||||
};
|
||||
|
||||
/**
|
||||
* An idle task doesn't do any work itself but cycles control between the two
|
||||
* device tasks.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @param {int} v1 a seed value that controls how the device tasks are scheduled
|
||||
* @param {int} count the number of times this task should be scheduled
|
||||
* @constructor
|
||||
*/
|
||||
function IdleTask(scheduler, v1, count) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = v1;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
IdleTask.prototype.run = function (packet) {
|
||||
this.count--;
|
||||
if (this.count == 0) return this.scheduler.holdCurrent();
|
||||
if ((this.v1 & 1) == 0) {
|
||||
this.v1 = this.v1 >> 1;
|
||||
return this.scheduler.release(ID_DEVICE_A);
|
||||
} else {
|
||||
this.v1 = (this.v1 >> 1) ^ 0xD008;
|
||||
return this.scheduler.release(ID_DEVICE_B);
|
||||
}
|
||||
};
|
||||
|
||||
IdleTask.prototype.toString = function () {
|
||||
return "IdleTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that suspends itself after each time it has been run to simulate
|
||||
* waiting for data from an external device.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @constructor
|
||||
*/
|
||||
function DeviceTask(scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = null;
|
||||
}
|
||||
|
||||
DeviceTask.prototype.run = function (packet) {
|
||||
if (packet == null) {
|
||||
if (this.v1 == null) return this.scheduler.suspendCurrent();
|
||||
var v = this.v1;
|
||||
this.v1 = null;
|
||||
return this.scheduler.queue(v);
|
||||
} else {
|
||||
this.v1 = packet;
|
||||
return this.scheduler.holdCurrent();
|
||||
}
|
||||
};
|
||||
|
||||
DeviceTask.prototype.toString = function () {
|
||||
return "DeviceTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that manipulates work packets.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @param {int} v1 a seed used to specify how work packets are manipulated
|
||||
* @param {int} v2 another seed used to specify how work packets are manipulated
|
||||
* @constructor
|
||||
*/
|
||||
function WorkerTask(scheduler, v1, v2) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
}
|
||||
|
||||
WorkerTask.prototype.run = function (packet) {
|
||||
if (packet == null) {
|
||||
return this.scheduler.suspendCurrent();
|
||||
} else {
|
||||
if (this.v1 == ID_HANDLER_A) {
|
||||
this.v1 = ID_HANDLER_B;
|
||||
} else {
|
||||
this.v1 = ID_HANDLER_A;
|
||||
}
|
||||
packet.id = this.v1;
|
||||
packet.a1 = 0;
|
||||
for (var i = 0; i < DATA_SIZE; i++) {
|
||||
this.v2++;
|
||||
if (this.v2 > 26) this.v2 = 1;
|
||||
packet.a2[i] = this.v2;
|
||||
}
|
||||
return this.scheduler.queue(packet);
|
||||
}
|
||||
};
|
||||
|
||||
WorkerTask.prototype.toString = function () {
|
||||
return "WorkerTask";
|
||||
};
|
||||
|
||||
/**
|
||||
* A task that manipulates work packets and then suspends itself.
|
||||
* @param {Scheduler} scheduler the scheduler that manages this task
|
||||
* @constructor
|
||||
*/
|
||||
function HandlerTask(scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.v1 = null;
|
||||
this.v2 = null;
|
||||
}
|
||||
|
||||
HandlerTask.prototype.run = function (packet) {
|
||||
if (packet != null) {
|
||||
if (packet.kind == KIND_WORK) {
|
||||
this.v1 = packet.addTo(this.v1);
|
||||
} else {
|
||||
this.v2 = packet.addTo(this.v2);
|
||||
}
|
||||
}
|
||||
if (this.v1 != null) {
|
||||
var count = this.v1.a1;
|
||||
var v;
|
||||
if (count < DATA_SIZE) {
|
||||
if (this.v2 != null) {
|
||||
v = this.v2;
|
||||
this.v2 = this.v2.link;
|
||||
v.a1 = this.v1.a2[count];
|
||||
this.v1.a1 = count + 1;
|
||||
return this.scheduler.queue(v);
|
||||
}
|
||||
} else {
|
||||
v = this.v1;
|
||||
this.v1 = this.v1.link;
|
||||
return this.scheduler.queue(v);
|
||||
}
|
||||
}
|
||||
return this.scheduler.suspendCurrent();
|
||||
};
|
||||
|
||||
HandlerTask.prototype.toString = function () {
|
||||
return "HandlerTask";
|
||||
};
|
||||
|
||||
/* --- *
|
||||
* P a c k e t
|
||||
* --- */
|
||||
|
||||
var DATA_SIZE = 4;
|
||||
|
||||
/**
|
||||
* A simple package of data that is manipulated by the tasks. The exact layout
|
||||
* of the payload data carried by a packet is not importaint, and neither is the
|
||||
* nature of the work performed on packets by the tasks.
|
||||
*
|
||||
* Besides carrying data, packets form linked lists and are hence used both as
|
||||
* data and worklists.
|
||||
* @param {Packet} link the tail of the linked list of packets
|
||||
* @param {int} id an ID for this packet
|
||||
* @param {int} kind the type of this packet
|
||||
* @constructor
|
||||
*/
|
||||
function Packet(link, id, kind) {
|
||||
this.link = link;
|
||||
this.id = id;
|
||||
this.kind = kind;
|
||||
this.a1 = 0;
|
||||
this.a2 = new Array(DATA_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this packet to the end of a worklist, and return the worklist.
|
||||
* @param {Packet} queue the worklist to add this packet to
|
||||
*/
|
||||
Packet.prototype.addTo = function (queue) {
|
||||
this.link = null;
|
||||
if (queue == null) return this;
|
||||
var peek, next = queue;
|
||||
while ((peek = next.link) != null)
|
||||
next = peek;
|
||||
next.link = this;
|
||||
return queue;
|
||||
};
|
||||
|
||||
Packet.prototype.toString = function () {
|
||||
return "Packet";
|
||||
};
|
||||
|
||||
var Richards = new BenchmarkSuite('Richards', 35302, [
|
||||
new Benchmark("Richards", runRichards)
|
||||
]);
|
||||
|
||||
42
oden-js-sys/quickjs/tests/bench-v8/run_harness.js
Normal file
42
oden-js-sys/quickjs/tests/bench-v8/run_harness.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* run_harness.js */
|
||||
var print = console.log;
|
||||
|
||||
function Run() {
|
||||
BenchmarkSuite.RunSuites({ NotifyStep: ShowProgress,
|
||||
NotifyError: AddError,
|
||||
NotifyResult: AddResult,
|
||||
NotifyScore: AddScore });
|
||||
}
|
||||
|
||||
var harnessErrorCount = 0;
|
||||
|
||||
function ShowProgress(name) {
|
||||
print('PROGRESS', name);
|
||||
}
|
||||
|
||||
function AddError(name, error) {
|
||||
print('ERROR', name, error);
|
||||
print(error.stack);
|
||||
harnessErrorCount++;
|
||||
}
|
||||
|
||||
function AddResult(name, result) {
|
||||
print('RESULT', name, result);
|
||||
}
|
||||
|
||||
function AddScore(score) {
|
||||
print('SCORE', score);
|
||||
}
|
||||
|
||||
try {
|
||||
Run();
|
||||
} catch (e) {
|
||||
print('*** Run() failed');
|
||||
print(e.stack || e);
|
||||
}
|
||||
|
||||
if (harnessErrorCount > 0) {
|
||||
// Throw an error so that 'duk' has a non-zero exit code which helps
|
||||
// automatic testing.
|
||||
throw new Error('Benchmark had ' + harnessErrorCount + ' errors');
|
||||
}
|
||||
393
oden-js-sys/quickjs/tests/bench-v8/splay.js
Normal file
393
oden-js-sys/quickjs/tests/bench-v8/splay.js
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This benchmark is based on a JavaScript log processing module used
|
||||
// by the V8 profiler to generate execution time profiles for runs of
|
||||
// JavaScript applications, and it effectively measures how fast the
|
||||
// JavaScript engine is at allocating nodes and reclaiming the memory
|
||||
// used for old nodes. Because of the way splay trees work, the engine
|
||||
// also has to deal with a lot of changes to the large tree object
|
||||
// graph.
|
||||
|
||||
// Configuration.
|
||||
var kSplayTreeSize = 8000;
|
||||
var kSplayTreeModifications = 80;
|
||||
var kSplayTreePayloadDepth = 5;
|
||||
|
||||
var splayTree = null;
|
||||
|
||||
|
||||
function GeneratePayloadTree(depth, tag) {
|
||||
if (depth == 0) {
|
||||
return {
|
||||
array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
|
||||
string : 'String for key ' + tag + ' in leaf node'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
left: GeneratePayloadTree(depth - 1, tag),
|
||||
right: GeneratePayloadTree(depth - 1, tag)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function GenerateKey() {
|
||||
// The benchmark framework guarantees that Math.random is
|
||||
// deterministic; see base.js.
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
|
||||
function InsertNewNode() {
|
||||
// Insert new node with a unique key.
|
||||
var key;
|
||||
do {
|
||||
key = GenerateKey();
|
||||
} while (splayTree.find(key) != null);
|
||||
var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key));
|
||||
splayTree.insert(key, payload);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function SplaySetup() {
|
||||
splayTree = new SplayTree();
|
||||
for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
|
||||
}
|
||||
|
||||
|
||||
function SplayTearDown() {
|
||||
// Allow the garbage collector to reclaim the memory
|
||||
// used by the splay tree no matter how we exit the
|
||||
// tear down function.
|
||||
var keys = splayTree.exportKeys();
|
||||
splayTree = null;
|
||||
|
||||
// Verify that the splay tree has the right size.
|
||||
var length = keys.length;
|
||||
if (length != kSplayTreeSize) {
|
||||
throw new Error("Splay tree has wrong size");
|
||||
}
|
||||
|
||||
// Verify that the splay tree has sorted, unique keys.
|
||||
for (var i = 0; i < length - 1; i++) {
|
||||
if (keys[i] >= keys[i + 1]) {
|
||||
throw new Error("Splay tree not sorted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function SplayRun() {
|
||||
// Replace a few nodes in the splay tree.
|
||||
for (var i = 0; i < kSplayTreeModifications; i++) {
|
||||
var key = InsertNewNode();
|
||||
var greatest = splayTree.findGreatestLessThan(key);
|
||||
if (greatest == null) splayTree.remove(key);
|
||||
else splayTree.remove(greatest.key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a Splay tree. A splay tree is a self-balancing binary
|
||||
* search tree with the additional property that recently accessed
|
||||
* elements are quick to access again. It performs basic operations
|
||||
* such as insertion, look-up and removal in O(log(n)) amortized time.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SplayTree() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pointer to the root node of the tree.
|
||||
*
|
||||
* @type {SplayTree.Node}
|
||||
* @private
|
||||
*/
|
||||
SplayTree.prototype.root_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the tree is empty.
|
||||
*/
|
||||
SplayTree.prototype.isEmpty = function() {
|
||||
return !this.root_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a node into the tree with the specified key and value if
|
||||
* the tree does not already contain a node with the specified key. If
|
||||
* the value is inserted, it becomes the root of the tree.
|
||||
*
|
||||
* @param {number} key Key to insert into the tree.
|
||||
* @param {*} value Value to insert into the tree.
|
||||
*/
|
||||
SplayTree.prototype.insert = function(key, value) {
|
||||
if (this.isEmpty()) {
|
||||
this.root_ = new SplayTree.Node(key, value);
|
||||
return;
|
||||
}
|
||||
// Splay on the key to move the last node on the search path for
|
||||
// the key to the root of the tree.
|
||||
this.splay_(key);
|
||||
if (this.root_.key == key) {
|
||||
return;
|
||||
}
|
||||
var node = new SplayTree.Node(key, value);
|
||||
if (key > this.root_.key) {
|
||||
node.left = this.root_;
|
||||
node.right = this.root_.right;
|
||||
this.root_.right = null;
|
||||
} else {
|
||||
node.right = this.root_;
|
||||
node.left = this.root_.left;
|
||||
this.root_.left = null;
|
||||
}
|
||||
this.root_ = node;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a node with the specified key from the tree if the tree
|
||||
* contains a node with this key. The removed node is returned. If the
|
||||
* key is not found, an exception is thrown.
|
||||
*
|
||||
* @param {number} key Key to find and remove from the tree.
|
||||
* @return {SplayTree.Node} The removed node.
|
||||
*/
|
||||
SplayTree.prototype.remove = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
throw Error('Key not found: ' + key);
|
||||
}
|
||||
this.splay_(key);
|
||||
if (this.root_.key != key) {
|
||||
throw Error('Key not found: ' + key);
|
||||
}
|
||||
var removed = this.root_;
|
||||
if (!this.root_.left) {
|
||||
this.root_ = this.root_.right;
|
||||
} else {
|
||||
var right = this.root_.right;
|
||||
this.root_ = this.root_.left;
|
||||
// Splay to make sure that the new root has an empty right child.
|
||||
this.splay_(key);
|
||||
// Insert the original right child as the right child of the new
|
||||
// root.
|
||||
this.root_.right = right;
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the node having the specified key or null if the tree doesn't contain
|
||||
* a node with the specified key.
|
||||
*
|
||||
* @param {number} key Key to find in the tree.
|
||||
* @return {SplayTree.Node} Node having the specified key.
|
||||
*/
|
||||
SplayTree.prototype.find = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
this.splay_(key);
|
||||
return this.root_.key == key ? this.root_ : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {SplayTree.Node} Node having the maximum key value.
|
||||
*/
|
||||
SplayTree.prototype.findMax = function(opt_startNode) {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
var current = opt_startNode || this.root_;
|
||||
while (current.right) {
|
||||
current = current.right;
|
||||
}
|
||||
return current;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {SplayTree.Node} Node having the maximum key value that
|
||||
* is less than the specified key value.
|
||||
*/
|
||||
SplayTree.prototype.findGreatestLessThan = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Splay on the key to move the node with the given key or the last
|
||||
// node on the search path to the top of the tree.
|
||||
this.splay_(key);
|
||||
// Now the result is either the root node or the greatest node in
|
||||
// the left subtree.
|
||||
if (this.root_.key < key) {
|
||||
return this.root_;
|
||||
} else if (this.root_.left) {
|
||||
return this.findMax(this.root_.left);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array<*>} An array containing all the keys of tree's nodes.
|
||||
*/
|
||||
SplayTree.prototype.exportKeys = function() {
|
||||
var result = [];
|
||||
if (!this.isEmpty()) {
|
||||
this.root_.traverse_(function(node) { result.push(node.key); });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Perform the splay operation for the given key. Moves the node with
|
||||
* the given key to the top of the tree. If no node has the given
|
||||
* key, the last node on the search path is moved to the top of the
|
||||
* tree. This is the simplified top-down splaying algorithm from:
|
||||
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
|
||||
*
|
||||
* @param {number} key Key to splay the tree on.
|
||||
* @private
|
||||
*/
|
||||
SplayTree.prototype.splay_ = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Create a dummy node. The use of the dummy node is a bit
|
||||
// counter-intuitive: The right child of the dummy node will hold
|
||||
// the L tree of the algorithm. The left child of the dummy node
|
||||
// will hold the R tree of the algorithm. Using a dummy node, left
|
||||
// and right will always be nodes and we avoid special cases.
|
||||
var dummy, left, right;
|
||||
dummy = left = right = new SplayTree.Node(null, null);
|
||||
var current = this.root_;
|
||||
while (true) {
|
||||
if (key < current.key) {
|
||||
if (!current.left) {
|
||||
break;
|
||||
}
|
||||
if (key < current.left.key) {
|
||||
// Rotate right.
|
||||
var tmp = current.left;
|
||||
current.left = tmp.right;
|
||||
tmp.right = current;
|
||||
current = tmp;
|
||||
if (!current.left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Link right.
|
||||
right.left = current;
|
||||
right = current;
|
||||
current = current.left;
|
||||
} else if (key > current.key) {
|
||||
if (!current.right) {
|
||||
break;
|
||||
}
|
||||
if (key > current.right.key) {
|
||||
// Rotate left.
|
||||
var tmp = current.right;
|
||||
current.right = tmp.left;
|
||||
tmp.left = current;
|
||||
current = tmp;
|
||||
if (!current.right) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Link left.
|
||||
left.right = current;
|
||||
left = current;
|
||||
current = current.right;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Assemble.
|
||||
left.right = current.left;
|
||||
right.left = current.right;
|
||||
current.left = dummy.right;
|
||||
current.right = dummy.left;
|
||||
this.root_ = current;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a Splay tree node.
|
||||
*
|
||||
* @param {number} key Key.
|
||||
* @param {*} value Value.
|
||||
*/
|
||||
SplayTree.Node = function(key, value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @type {SplayTree.Node}
|
||||
*/
|
||||
SplayTree.Node.prototype.left = null;
|
||||
|
||||
|
||||
/**
|
||||
* @type {SplayTree.Node}
|
||||
*/
|
||||
SplayTree.Node.prototype.right = null;
|
||||
|
||||
|
||||
/**
|
||||
* Performs an ordered traversal of the subtree starting at
|
||||
* this SplayTree.Node.
|
||||
*
|
||||
* @param {function(SplayTree.Node)} f Visitor function.
|
||||
* @private
|
||||
*/
|
||||
SplayTree.Node.prototype.traverse_ = function(f) {
|
||||
var current = this;
|
||||
while (current) {
|
||||
var left = current.left;
|
||||
if (left) left.traverse_(f);
|
||||
f(current);
|
||||
current = current.right;
|
||||
}
|
||||
};
|
||||
|
||||
var Splay = new BenchmarkSuite('Splay', 81491, [
|
||||
new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
|
||||
]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue