--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitignore Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,10 @@
+/target
+/lib
+/classes
+/checkouts
+pom.xml
+*.jar
+*.class
+.lein-deps-sum
+.lein-failures
+.lein-plugins
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,12 @@
+syntax:glob
+target/
+lib/
+classes/
+checkouts/
+pom.xml
+docs/build/
+*.jar
+*.class
+.lein-deps-sum
+.lein-failures
+.lein-plugins
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README.markdown Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,44 @@
+roul
+====
+
+Roul (from "roulette") is a tiny Clojure library for working with random numbers
+more easily.
+
+For example:
+
+ (require '[roul.random :as rr])
+
+ ; Get a random integer between 0 and 10
+ (rand-int 10) ; Default Clojure
+ (rr/rand-int 10) ; Roul
+
+ ; Get a random integer between 10 and 20
+ (+ 10 (rand-int 10)) ; Default Clojure
+ (rr/rand-int 10 20) ; Roul
+
+ ; Get a random element from a weighted collection
+
+ ; Returns coffee roughly 80% of the time, tea 15%, and soda 5%.
+ (rr/rand-nth-weighted {:coffee 0.80 :tea 0.15 :soda 0.05})
+
+ ; Returns cats roughly twice as often as boots.
+ (rr/rand-nth-weighted [[:boots 14] [:cats 28]])
+
+Installation
+------------
+
+You need Leiningen 2 to use this. Sorry.
+
+ [roul "0.1.0"]
+
+Usage
+-----
+
+Read the [documentation](http://sjl.bitbucket.org/roul/).
+
+License
+-------
+
+Copyright © 2012 Steve Losh and Contributors
+
+MIT/X11 Licensed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/footer.markdown Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,18 @@
+Created by [Steve Losh](http://stevelosh.com).
+Docs made with [d](http://sjl.bitbucket.org/d/).
+
+<br/><a id='rochester-made' href='http://rochestermade.com' title='Rochester Made'><img src='http://rochestermade.com/media/images/rochester-made-dark-on-light.png' alt='Rochester Made' title='Rochester Made' /></a>
+
+<script type="text/javascript">
+ var _gauges = _gauges || [];
+ (function() {
+ var t = document.createElement('script');
+ t.type = 'text/javascript';
+ t.async = true;
+ t.id = 'gauges-tracker';
+ t.setAttribute('data-site-id', '4f80ace2f5a1f538860000c2');
+ t.src = '//secure.gaug.es/track.js';
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(t, s);
+ })();
+</script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/index.markdown Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,11 @@
+Roul (from "roulette") is a tiny Clojure library that makes working with random
+numbers more convenient.
+
+It's still in the early stages of development, but if you can think of something
+useful that should be in it feel free to open a feature request on the issue
+tracker!
+
+* License: MIT/X11
+* Mercurial: <http://bitbucket.org/sjl/roul/>
+* Git: <http://github.com/sjl/roul/>
+* Issues: <http://github.com/sjl/roul/issues/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/installation.markdown Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,10 @@
+Installation
+============
+
+Roul requires Leiningen 2. Sorry.
+
+Add the following to your `project.clj`:
+
+ [roul "0.1.0"]
+
+Run `lein deps` and you're all set.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/publish.sh Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+rm -rf ./build
+~/lib/virtualenvs/d/bin/d
+hg -R ~/src/sjl.bitbucket.org pull -u
+rsync --delete -a ./build/ ~/src/sjl.bitbucket.org/roul
+hg -R ~/src/sjl.bitbucket.org commit -Am 'roul: Update site.'
+hg -R ~/src/sjl.bitbucket.org push
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/title Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,1 @@
+Roul
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/usage.markdown Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,79 @@
+Usage
+=====
+
+Roul currently contains a few functions in one namespace. It'll expand in the
+future, but for now that's all it is.
+
+[TOC]
+
+roul.random
+-----------
+
+The `roul.random` namespace contains wrappers around some of Clojure's built-in
+random functions to make them more user friendly, as well as some uniqe
+functions of its own.
+
+The recommended way is to `require` this namespace into your own instead of
+overwriting Clojure's builtins:
+
+ :::clojure
+ (ns foo.core
+ (:require [roul.random :as rr]))
+
+ ; or
+
+ (require '[roul.random :as rr])
+
+### rand
+
+ :::clojure
+ (rand) ; return a float in [0, 1)
+ (rand end) ; return a float in [0, end)
+ (rand start end) ; return a float in [start, end)
+
+A wrapper around the built-in `rand`. Returns a random floating point number
+between `start` (inclusive) and `end` (exclusive).
+
+If not given, `start` defaults to `0` and `end` defaults to `1`.
+
+### rand-int
+
+ :::clojure
+ (rand-int end) ; return an int in [0, end)
+ (rand-int start end) ; return an int in [start, end)
+
+A wrapper around the built-in `rand-int`. Returns a random integer between
+`start` (inclusive) and `end` (exclusive).
+
+If not given, `start` defaults to `0`.
+
+### rand-nth
+
+ :::clojure
+ (rand-nth coll) ; return a random element of coll
+
+A wrapper around the built-in `rand-nth`. Returns a random element of the given
+collection.
+
+This is a transparent wrapper around the builtin, included only for
+completeness. Unlike the other wrappers no new functionality has been added
+(yet).
+
+### rand-nth-weighted
+
+ :::clojure
+ (rand-nth-weighted coll) ; return a random element of coll
+
+Returns a random element of a weighted collection.
+
+A weighted collection can be any seq of `[choice, weight]` elements. The
+weights can be arbitrary numbers -- they do not need to add up to anything
+specific.
+
+ :::clojure
+ ; Returns coffee roughly 80% of the time, tea 15%, and soda 5%.
+ (rr/rand-nth-weighted {:coffee 0.80, :tea 0.15, :soda 0.05})
+
+ ; Returns cats roughly twice as often as boots.
+ (rr/rand-nth-weighted [[:boots 14]
+ [:cats 28]])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project.clj Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,8 @@
+(defproject roul "0.1.0"
+ :license {:name "MIT/X11"}
+ :dependencies [[org.clojure/clojure "1.3.0"]]
+ :plugins [[sjl/lein2-generative "0.1.4.2"]]
+ :generative-path "test/"
+ :url "http://sjl.bitbicket.org/roul/"
+ :min-lein-version "2.0.0"
+ :description "A library that wraps and extends Clojure's random number generators.")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/roul/random.clj Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,45 @@
+(ns roul.random
+ (:refer-clojure :exclude [rand-int rand rand-nth]))
+
+
+(defn rand
+ "Return a random float between start (inclusive) and end (exclusive).
+
+ start defaults to 0
+ end defaults to 1"
+ ([] (clojure.core/rand))
+ ([end] (clojure.core/rand end))
+ ([start end] (+ start (clojure.core/rand (- end start)))))
+
+(defn rand-int
+ "Return a random int between start (inclusive) and end (exclusive).
+
+ start defaults to 0"
+ ([end] (clojure.core/rand-int end))
+ ([start end] (+ start (clojure.core/rand-int (- end start)))))
+
+(defn rand-nth
+ "Return a random element of the collection. Delegates to the built-in
+ rand-nth for now."
+ [coll]
+ (clojure.core/rand-nth coll))
+
+(defn rand-nth-weighted
+ "Return a random element from the weighted collection.
+
+ A weighted collection can be any seq of [choice, weight] elements. The
+ weights can be arbitrary numbers -- they do not need to add up to anything
+ specific.
+
+ Examples:
+
+ (rand-nth-weighted [[:a 0.50] [:b 0.20] [:c 0.30]])
+ (rand-nth-weighted {:a 10 :b 200})"
+ [coll]
+ (let [total (reduce + (map second coll))]
+ (loop [i (rand total)
+ [[choice weight] & remaining] (seq coll)]
+ (if (>= weight i)
+ choice
+ (recur (- i weight) remaining)))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/roul/random_test.clj Sat Apr 07 17:33:03 2012 -0400
@@ -0,0 +1,50 @@
+(ns roul.random_test
+ (:use clojure.test)
+ (:require [clojure.test.generative.generators :as gen])
+ (:use [clojure.test.generative :only [defspec]])
+ (:require [roul.random :as rr]))
+
+
+(defn abs [i]
+ (if (< i 0)
+ (- i)
+ i))
+
+
+(defn random-smallish-int []
+ (gen/uniform -2000 2000))
+
+(defspec abs-is-never-negative
+ abs
+ [^{:tag `random-smallish-int} n]
+ (assert (not (neg? %))))
+
+
+(defn all-within [actual expected tolerance]
+ (letfn [(is-bad [[i p]]
+ (<= (abs (- p (expected i)))
+ tolerance))]
+ (empty? (remove identity (map is-bad actual)))))
+
+(defn percentquencies [data]
+ (let [freqs (frequencies data)
+ total (reduce + (map second freqs))]
+ (into {} (map (juxt first #(-> % second (/ total) float))
+ freqs))))
+
+
+(defn random-data [] (gen/vec gen/int))
+(defspec percentquencies-are-sane
+ percentquencies
+ [^{:tag `random-data} data]
+ (assert (= (set (keys %)) (set data))))
+
+
+(deftest test-rand-nth-weighted
+ (testing "Values should be selected approximately according to their weights."
+ (dotimes [_ 10]
+ (let [data (repeatedly 10000 #(rr/rand-nth-weighted {:a 0.5 :b 0.2 :c 0.3}))]
+ (is (all-within (percentquencies data) {:a 0.5 :b 0.2 :c 0.3} 0.05))))
+ (dotimes [_ 10]
+ (let [data (repeatedly 10000 #(rr/rand-nth-weighted {:a 100 :b 900}))]
+ (is (all-within (percentquencies data) {:a 0.10 :b 0.9} 0.05))))))