version bump 0.2.1: cleanup

This commit is contained in:
SheetJS 2016-09-24 14:53:39 -04:00
parent 8bc7f35eaf
commit 3b7543e631
7 changed files with 367 additions and 23 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules
test_files/out.*
misc/coverage.html

View File

@ -2,38 +2,66 @@ LIB=vdc
REQS=
ADDONS=
AUXTARGETS=
HTMLLINT=index.html
ULIB=$(shell echo $(LIB) | tr a-z A-Z)
DEPS=
TARGET=$(LIB).js
FLOWTARGET=$(LIB).flow.js
## Main Targets
.PHONY: all
all: $(TARGET) $(AUXTARGETS)
all: $(TARGET) $(AUXTARGETS) ## Build library and auxiliary scripts
$(TARGET) $(AUXTARGETS): %.js : %.flow.js
node -e 'process.stdout.write(require("fs").readFileSync("$<","utf8").replace(/^\s*\/\*:[^*]*\*\/\s*(\n)?/gm,"").replace(/\/\*:[^*]*\*\//gm,""))' > $@
node -e 'process.stdout.write(require("fs").readFileSync("$<","utf8").replace(/^[ \t]*\/\*[:#][^*]*\*\/\s*(\n)?/gm,"").replace(/\/\*[:#][^*]*\*\//gm,""))' > $@
.PHONY: clean
clean: clean-baseline
clean: clean-baseline ## Remove targets and build artifacts
rm -f $(TARGET)
## Testing
.PHONY: test mocha
test mocha: test.js $(TARGET)
test mocha: test.js $(TARGET) baseline ## Run test suite
mocha -R spec -t 20000
.PHONY: baseline
baseline: ## Build test baselines
@bash ./misc/make_baseline.sh
.PHONY: clean-baseline
clean-baseline: ## Remove test baselines
rm -f test_files/*.*
## Code Checking
.PHONY: lint
lint: $(TARGET) $(AUXTARGETS)
jshint --show-non-errors $(TARGET) $(AUXTARGETS)
jshint --show-non-errors package.json
jscs $(TARGET) $(AUXTARGETS)
lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks
@jshint --show-non-errors $(TARGET) $(AUXTARGETS)
@jshint --show-non-errors package.json
@jshint --show-non-errors --extract=always $(HTMLLINT)
@jscs $(TARGET) $(AUXTARGETS)
.PHONY: flow
flow: lint
flow check --all --show-all-errors
flow: lint ## Run flow checker
@flow check --all --show-all-errors
.PHONY: baseline clean-baseline
baseline:
./misc/make_baseline.sh
.PHONY: cov
cov: misc/coverage.html ## Run coverage test
clean-baseline:
rm -f test_files/*.*
misc/coverage.html: $(TARGET) test.js
mocha --require blanket -R html-cov -t 20000 > $@
.PHONY: coveralls
coveralls: ## Coverage Test + Send to coveralls.io
mocha --require blanket --reporter mocha-lcov-reporter -t 20000 | node ./node_modules/coveralls/bin/coveralls.js
.PHONY: help
help:
@grep -hE '(^[a-zA-Z_-][ a-zA-Z_-]*:.*?|^#[#*])' $(MAKEFILE_LIST) | bash misc/help.sh
#* To show a spinner, append "-spin" to any target e.g. cov-spin
%-spin:
@make $* & bash misc/spin.sh $$!

View File

@ -4,13 +4,21 @@ Pure JS implementation of van der Corput low-discrepancy sequences.
## Installation
Available on [npm vdc](http://npm.im/vdc):
With [npm](http://npm.im/vdc):
```
```bash
$ npm install vdc
```
## Usage
In the browser:
```html
<script src="vdc.js"></script>
```
The browser exposes a variable `VDC`
## Usage
The exported function `VDC` accepts a `opts` object with the following fields:
@ -19,15 +27,15 @@ The exported function `VDC` accepts a `opts` object with the following fields:
Calling without arguments will default to the aforementioned values.
The object returned by `VDC` exposes a `next()` method to get the next element.
The object returned by `VDC` exposes a `next()` method to get the next element.
The field `last` holds the most recently generated value (accessing the field
does not trigger a recalculation)
## Sample Session
For example:
```
var VDC = require('vdc')
```js
//var VDC = require('vdc') // uncomment this line if in node
var opts = {'n':0, 'b':2};
var generator = VDC(opts);
@ -49,6 +57,21 @@ The expected output is
0.5625 (9/16)
```
## Testing
`make test` will run the nodejs-based test.
`make baseline` will generate the test baselines using Mathematica by explicitly
extracting and reversing the digits. The implementation is based off a tutorial
<http://reference.wolfram.com/mathematica/CUDALink/tutorial/Applications.html>
```mathematica
VanDerCorput[base_][len_] := Table[
With[{digits = Reverse@IntegerDigits[n, base]},
Sum[2^(-ii)*digits[[ii]], {ii, Length[digits]}]
], {n, len}]
```
## Notes
`0` is the first value. Some sources (notably Wikipedia) start the sequence at

231
index.html Normal file
View File

@ -0,0 +1,231 @@
<!DOCTYPE html>
<!-- vdc.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>VDC Live Demo</title>
</head>
<body>
<b>VDC Live Demo</b><br />
<a href="https://github.com/SheetJS/js-vdc">Source Code</a><br />
<a href="https://github.com/SheetJS/js-vdc/issues">Issues? Something look weird? Click here and report an issue</a><br />
<br />
<div>
Using the van der Corput sequences for bases 2,3 we can generate a sequence of
quasirandom points in the unit square. The sequence is said to be
<a href="https://en.wikipedia.org/wiki/Low-discrepancy_sequence">low-discrepancy</a>
(basically the points are somewhat more uniformly spread across the square; you
would expect a truly random sequence of points to have some concentrated lumps
and small areas with no points).
<br /><br />
quasi-Monte Carlo methods generally have better error properties compared to the
standard random and pseudo-random MC methods. As a demonstration, we will
estimate &#960; by sampling points in the unit square. The left side shows the
VDC estimate using bases 2 (x) and 3 (y). The right side shows the random
estimate by repeatedly calling Math.random.
<br /><br />
The graphs show the results of sampling many points and estimating PI. Below
the graphs, the PI value shows the calculated estimate, "err %" shows the
relative error (smaller is better) and "ln err" shows the natural log of the
error (smaller is better).
<br /><br />
VDC sequences can be "seeded" by setting the starting index for the calculation.
In this demo a random integer is chosen using Math.random.
<br /><br />
The values and graphs are calculated in your browser window. If the graphs do
not appear or the values are not calculated, please report the issue!
</div>
<br />
<div width=620>
<div style="float:left">
<b><center id="ltext">VDC (2,3) Sampling </center></b>
<br />
<canvas id="canvas1" width=300 height=300></canvas>
<br />
<b><center id="lout">Estimate of PI: </center></b>
</div>
<div style="float:right">
<b><center id="rtext">Math.random Sampling </center></b>
<br />
<canvas id="canvas2" width=300 height=300></canvas>
<br />
<b><center id="rout">Estimate of PI: </center></b>
</div>
</div>
</body>
<script src="http://d3js.org/d3.v2.js"></script>
<script src="vdc.js"></script>
<script src="https://cdn.rawgit.com/SheetJS/printj/master/printj.js"></script>
<script>
/*jshint browser:true */
var PTS = 10000;
var seed = (Math.random()*100000)|0;
document.getElementById("ltext").innerHTML += PTS + " points; seed " + seed;
document.getElementById("rtext").innerHTML += PTS + " points";
var canvas1, canvas2;
// from http://bl.ocks.org/syntagmatic/3341641 (WTFPL)
var renderQueue = (function(func) {
var _queue = [], // data to be rendered
_rate = 1000, // number of calls per frame
_invalidate = function() {}, // invalidate last render queue
_clear = function() {}; // clearing function
var rq = function(data) {
if (data) rq.data(data);
_invalidate();
_clear();
rq.render();
};
rq.render = function() {
var valid = true;
_invalidate = rq.invalidate = function() {
valid = false;
};
function doFrame() {
if (!valid) return true;
var chunk = _queue.splice(0,_rate);
chunk.map(func);
timer_frame(doFrame);
}
doFrame();
};
rq.data = function(data) {
_invalidate();
_queue = data.slice(0); // creates a copy of the data
return rq;
};
rq.add = function(data) {
_queue = _queue.concat(data);
};
rq.rate = function(value) {
if (!arguments.length) return _rate;
_rate = value;
return rq;
};
rq.remaining = function() {
return _queue.length;
};
// clear the canvas
rq.clear = function(func) {
if (!arguments.length) {
_clear();
return rq;
}
_clear = func;
return rq;
};
rq.invalidate = _invalidate;
var timer_frame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { setTimeout(callback, 17); };
return rq;
});
// ---
var color = d3.scale.linear()
.domain([0, 0.5, 1])
.range(["#ef2212", "#e7c767", "#2799df"])
.interpolate(d3.interpolateHcl);
var VDC2 = VDC({b:2,n:seed});
var VDC3 = VDC({b:3,n:seed});
var VDC5 = VDC({b:5,n:seed});
var in1 = 0, in2 = 0, out1 = 0, out2 = 0;
function update1() {
var mypi = (in1 / (in1 + out1))*4, err = Math.abs(mypi - Math.PI)/Math.PI, lnerr = Math.log(err);
document.getElementById("lout").innerHTML = PRINTJ.sprintf("PI = %f <br />err%% = %f<br />ln err = %f", mypi, err, lnerr);
}
function update2() {
var mypi = (in2 / (in2 + out2))*4, err = Math.abs(mypi - Math.PI)/Math.PI, lnerr = Math.log(err);
document.getElementById("rout").innerHTML = PRINTJ.sprintf("PI = %f <br />err%% = %f<br />ln err = %f", mypi, err, lnerr);
}
function generate1(n) {
return d3.range(n).map(function(i) {
var x = VDC2.next(), y = VDC3.next();
if(x*x + y*y <= 1) in1++; else out1++;
if((in1 + out1) % 100 === 0) update1();
return [ canvas1.width*x, canvas1.height*y, color(VDC5.next()) ];
});
}
function generate2(n) {
return d3.range(n).map(function(i) {
var x = Math.random(), y = Math.random();
if(x*x + y*y <= 1) in2++; else out2++;
if((in2 + out2) % 100 === 0) update2();
return [ canvas2.width*x, canvas2.height*y, color(Math.random()) ];
});
}
function make_dot(ctx) {
return function(pos) {
ctx.fillStyle = pos[2];
ctx.beginPath();
ctx.fillRect(pos[0]-1,pos[1]-1,2,2);
ctx.stroke();
ctx.fill();
};
}
function make_clear_canvas(ctx, canvas) {
return function() { ctx.clearRect(0,0,canvas.width,canvas.height); make_boundary(canvas, ctx); };
}
function make_boundary(canvas, ctx) {
ctx.beginPath();
ctx.arc(0, canvas.height, canvas.width, 0, 2*Math.PI);
ctx.stroke();
}
window.onload = function() {
canvas1 = document.getElementById("canvas1");
var ctx1 = canvas1.getContext("2d");
ctx1.globalCompositeOperation = "destination-over";
canvas2 = document.getElementById("canvas2");
var ctx2 = canvas2.getContext("2d");
ctx2.globalCompositeOperation = "destination-over";
make_boundary(canvas1, ctx1);
make_boundary(canvas2, ctx2);
var dot1 = make_dot(ctx1);
var dot2 = make_dot(ctx2);
var clear_canvas1 = make_clear_canvas(ctx1, canvas1);
var clear_canvas2 = make_clear_canvas(ctx2, canvas2);
var render1 = renderQueue(dot1).clear(clear_canvas1);
var render2 = renderQueue(dot2).clear(clear_canvas2);
render1(generate1(PTS));
render2(generate2(PTS));
};
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</html>

42
misc/help.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash
# make_help.sh -- process listing of targets and special items in Makefile
# Copyright (C) 2016-present SheetJS
#
# usage in makefile: pipe the output of the following command:
# @grep -hE '(^[a-zA-Z_-][ a-zA-Z_-]*:.*?|^#[#*])' $(MAKEFILE_LIST)
#
# lines starting with "## " are treated as subtitles
# lines starting with "#* " are treated as plaintext comments
# multiple targets with "## " after the ":" are rendered as separate targets
# if the presumed default target is labeled, it will be assigned a unique color
awk '
BEGIN{recipes=0;}
!/#[#*] .*$/ {next;}
{multi=0; isrecipe=0;}
/^[^#]*:/ {isrecipe=1; ++recipes;}
/^[^ :]* .*:/ {multi=1}
multi==0 && isrecipe>0 { if(recipes > 1) print; else print $0, "[default]"; next}
isrecipe == 0 {print; next}
multi>0 {
k=split($0, msg, "##"); m=split($0, a, ":"); n=split(a[1], b, " ");
for(i=1; i<=n; ++i) print b[i] ":", "##" msg[2], (recipes==1 && i==1 ? "[default]" : "")
}
END {}
' | if [[ -t 1 ]]; then
awk '
BEGIN {FS = ":.*?## "}
{color=36}
/\[default\]/ {color=35}
NF==1 && /^##/ {color=34}
NF==1 && /^#\*/ {color=20; $1 = substr($1, 4)}
{printf "\033[" color "m%-20s\033[0m %s\n", $1, $2;}
END{}' -
else
awk '
BEGIN {FS = ":.*?## "}
/^#\* / {$1 = substr($1, 4)}
{printf "%-20s %s\n", $1, $2;}
END{}' -
fi

14
misc/spin.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# spin.sh -- show a spinner (for coverage test)
# Copyright (C) 2014-present SheetJS
wpid=$1
delay=1
str="|/-\\"
while [ $(ps -a|awk '$1=='$wpid' {print $1}') ]; do
t=${str#?}
printf " [%c]" "$str"
str=$t${str%"$t"}
sleep $delay
printf "\b\b\b\b"
done

View File

@ -1,6 +1,6 @@
{
"name": "vdc",
"version": "0.2.0",
"version": "0.2.1",
"author": "SheetJS",
"description": "van der Corput low-discrepancy sequences",
"keywords": [ "math", "random", "qrng", "lds" ],
@ -19,6 +19,11 @@
"scripts": {
"test": "make test"
},
"config": {
"blanket": {
"pattern": "vdc.js"
}
},
"bugs": { "url": "https://github.com/SheetJS/js-vdc/issues" },
"license": "Apache-2.0",
"engines": { "node": ">=0.8" }