Dunaj extends available math facilities with means to specify precision of arithmetic operations and to round numbers. A pluggable mechanism for using random number generators is introduced, and math related API was extended with functions of an angle, exponentiations and logarithms.

Instead of using prefixes and suffixes like in Clojure, facilities for unchecked and precise math are in Dunaj specified in separate namespaces. Common arithmetic functions are provided in dunaj.math namespace, with unchecked functions and arithmetic functions with arbitrary precisions defined in dunaj.math.unchecked and dunaj.math.precise respectively.

List of available math functions is extended with exponentiation, logarithms functions and several miscellaneous ones. A separate namespace called dunaj.math.angle contains math functions of an angle, with floating point numbers. Precision of these functions is as implemented by host. Both circular and hyperbolic functions are available.

For objects that have a canonical numeric representation, Dunaj provides a function called num.

Data types that have a canonical numeric representation provide constructors that accept a numeric value as an argument.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(num 10)
;;=> 10

(num \\Q)
;;=> 81

(num #inst \"2013-10-20\")
;;=> 1382227200000

(instant 1382227200000)
;;=> #<BasicInstant 2013-10-20T00:00:00.000000000-00:00>

(num #uuid \"462e019f-e7f2-4971-8ba6-14150556f147\")
;;=> 93284838311908100738254531818413289799

(uuid 93284838311908100738254531818413289799)
;;=> #uuid \"462e019f-e7f2-4971-8ba6-14150556f147\"

Precision and Rounding

In Dunaj, a precision is defined as a map with following keys:

  • :precision - Any integer. Nonnegative in case of :significant type. Defaults to 0.

  • :type - :significant or :decimal. Defaults to :significant.

  • :mode - :ceiling, :floor, :half-up, :half-down, :half-even, :up, :down and :unnecessary. Defaults to :half-up.

0 precision with :significant type means that any number of significant digits is OK

A precision config map can be used in rounding with round or used in with-precision to set precision for BigDecimal operations. A convenience functions floor and ceil are provided too.

Rounding
1
2
3
4
5
6
7
8
9
10
11
12
(round 123.456 {:precision 5})
;;=> 123.46

(round 123.456 :precision 5 :mode :down)
;;=> 123.45

;; one arg round uses {:type :decimal :precision 0 :mode :half-up} precision
(round 123.456)
;;=> 123.0

(round 123.456 {})
;;=> 123.456
Setting precision
1
2
3
4
5
6
7
(with-precision {:precision 10}
  (/ 1M 3))
;;=> 0.3333333333M

(with-precision {:precision 5 :mode :up}
  (/ 1M 3))
;;=> 0.33334M

Random Number Generators

While convenience functions rand, rand-integer and rand-nth are provided, Dunaj offers more powerful means to generate random numbers.

Random numbers are generated by a random number generator (rng), which produces collection of random bytes. The random number generator is created with the rng function. Dunaj provides four rng types, each having its own rng factory:

  • secure-rng - A thread safe secure rng with host-specific algorithm and providers

  • seedable-rng - A thread safe rng with the ability to specify initial seed and with a guarantee that the same seed yields same sequence of items in the first reduction of the rng. Slower compared to other rngs.

  • splittable-rng - A non thread safe rng which is however intended for fork-join tasks, as it is able to split into two separate rngs. Can be seeded with same guarantees as in seedable-rng.

  • thread-local-rng - A fast thread local rng.

The IRngFactory protocol serves as an extension point for custom rngs. To generate numbers of other types, the dunaj.math.random namespace provides several transducers to convert a collection of bytes to the collection of integers, floats, etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(ns foo.bar
  (:api dunaj)
  (:require [dunaj.math.random :refer
             [rng secure-rng splittable-rng seedable-rng floats integers]]))

(defn t5
  [x]
  (seq (take 5 x)))
#'foo.bar/t5

;; default rng is thread-local-rng
(t5 (rng))
;;=> (119 115 42 -52 -65)

(t5 (rng splittable-rng))
;;=> (-87 68 95 -22 -67)

;; different values for each call
(t5 (rng splittable-rng))
;;=> (61 2 -5 -70 -58)

;; same sequence of values if seed is given
(t5 (rng splittable-rng :seed 37))
;;=> (77 -63 -96 60 -45)

(t5 (rng splittable-rng :seed 37))
;;=> (77 -63 -96 60 -45)

;; a specific rng algorithm
(t5 (rng secure-rng :algorithm :SHA1PRNG))
;;=> (-5 20 -46 40 86)

(t5 (floats (rng seedable-rng :seed 37)))
;;=> (0.6213673786196462 0.11767774115427432 0.3057012221689267 0.2779979466319322 0.8238345844110927)

(t5 (integers (rng seedable-rng :seed 37)))
;;=> (2484939960054915017 -1407233582011305665 -2505961012080803273 6132965348399674666 2831527535557967913)

(t5 (integers 900 1000 (rng)))
;;=> (952 997 998 970 977)