diff --git a/.gitignore b/.gitignore index 32475dd..2850197 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules test_files/out.* +misc/coverage.html diff --git a/Makefile b/Makefile index 603afa1..fffdf55 100644 --- a/Makefile +++ b/Makefile @@ -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 $$! diff --git a/README.md b/README.md index 7724494..5b560bf 100644 --- a/README.md +++ b/README.md @@ -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 + +``` + +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 + + +```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 diff --git a/index.html b/index.html new file mode 100644 index 0000000..04f35a4 --- /dev/null +++ b/index.html @@ -0,0 +1,231 @@ + + + + + + + VDC Live Demo + + + VDC Live Demo
+ Source Code
+ Issues? Something look weird? Click here and report an issue
+
+
+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 +low-discrepancy +(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). +

+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 π 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. +

+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). +

+VDC sequences can be "seeded" by setting the starting index for the calculation. +In this demo a random integer is chosen using Math.random. +

+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! +
+
+
+
+
VDC (2,3) Sampling
+
+ +
+
Estimate of PI:
+
+
+
Math.random Sampling
+
+ +
+
Estimate of PI:
+
+
+ + + + + + + diff --git a/misc/help.sh b/misc/help.sh new file mode 100755 index 0000000..7a1c4c4 --- /dev/null +++ b/misc/help.sh @@ -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 + diff --git a/misc/spin.sh b/misc/spin.sh new file mode 100755 index 0000000..471dfee --- /dev/null +++ b/misc/spin.sh @@ -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 diff --git a/package.json b/package.json index b252f3c..340fe8b 100644 --- a/package.json +++ b/package.json @@ -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" }