#lang racket
(define-values (struct:histogram
histogram-constructor
histogram?
histogram-field-ref
set-histogram-field!)
(make-struct-type 'histogram #f 4 0))
(define (make-histogram n)
(unless (> n 0)
(error 'make-histogram
"number of bins must be greater than 0"))
(histogram-constructor n
(make-vector (+ n 1))
(make-vector n)
#f))
(define (make-histogram-with-ranges-uniform n min max)
(unless (> n 0)
(error 'make-histogram-with-ranges-uniform
"number of bins must be greater than 0"))
(let ((h (make-histogram n)))
(set-histogram-ranges-uniform! h min max)
h))
(define histogram-n
(make-struct-field-accessor histogram-field-ref 0 'n))
(define histogram-ranges
(make-struct-field-accessor histogram-field-ref 1 'ranges))
(define (set-histogram-ranges! h ranges)
(unless (= (vector-length ranges)
(+ (histogram-n h) 1))
(error 'set-histogram-ranges
"size of ranges vector must match size of histogram"))
(do ((i 0 (+ i 1)))
((> i (histogram-n h)) (void))
(vector-set! (histogram-ranges h) i
(vector-ref ranges i)))
(do ((i 0 (+ i 1)))
((= i (histogram-n h)) (void))
(vector-set! (histogram-bins h) i 0))
(set-histogram-ranges-uniform?! h #f)
(void))
(define (set-histogram-ranges-uniform! h min max)
(let* ((n (histogram-n h))
(ranges (histogram-ranges h))
(bin-size (/ (- max min) n)))
(do ((i 0 (+ i 1)))
((= i n) (void))
(vector-set! ranges i (+ min (* i bin-size))))
(vector-set! ranges n max))
(do ((i 0 (+ i 1)))
((= i (histogram-n h)) (void))
(vector-set! (histogram-bins h) i 0))
(set-histogram-ranges-uniform?! h #t)
(void))
(define histogram-bins
(make-struct-field-accessor histogram-field-ref 2 'bins))
(define histogram-ranges-uniform?
(make-struct-field-accessor histogram-field-ref 3
'ranges-uniform?))
(define set-histogram-ranges-uniform?!
(make-struct-field-mutator set-histogram-field! 3
'ranges-uniform?))
(define (histogram-increment! h x)
(let ((n (histogram-n h))
(ranges (histogram-ranges h))
(bins (histogram-bins h))
(uniform-ranges? (histogram-ranges-uniform? h)))
(if uniform-ranges?
(let ((i (inexact->exact
(floor (/ (- x (vector-ref ranges 0))
(/ (- (vector-ref ranges n)
(vector-ref ranges 0))
n))))))
(when (<= 0 i (- n 1))
(vector-set! bins i
(+ (vector-ref bins i) 1))))
(let/ec exit
(when (< x (vector-ref ranges 0))
(exit (void)))
(do ((i 0 (+ i 1)))
((= i n) (void))
(when (< x (vector-ref ranges (+ i 1)))
(vector-set! bins i
(+ (vector-ref bins i) 1))
(exit (void))))))))
(define (histogram-accumulate! h x weight)
(let ((n (histogram-n h))
(ranges (histogram-ranges h))
(bins (histogram-bins h))
(uniform-ranges? (histogram-ranges-uniform? h)))
(if uniform-ranges?
(let ((i (inexact->exact
(floor (/ (- x (vector-ref ranges 0))
(/ (- (vector-ref ranges n)
(vector-ref ranges 0))
n))))))
(when (<= 0 i (- n 1))
(vector-set! bins i
(+ (vector-ref bins i) weight))))
(let/ec exit
(when (< x (vector-ref ranges 0))
(exit (void)))
(do ((i 0 (+ i 1)))
((= i n) (void))
(when (< x (vector-ref ranges (+ i 1)))
(vector-set! bins i
(+ (vector-ref bins i) weight))
(exit (void))))))))
(define (histogram-get h i)
(vector-ref (histogram-bins h) i))
(define (histogram-get-range h i)
(values (vector-ref (histogram-ranges h) i)
(vector-ref (histogram-ranges h) (+ i 1))))
(define (histogram-max h)
(let ((n (histogram-n h))
(bins (histogram-bins h))
(max (vector-ref (histogram-bins h) 0)))
(do ((i 0 (+ i 1)))
((= i n) max)
(when (> (vector-ref bins i) max)
(set! max (vector-ref bins i))))))
(define (histogram-min h)
(let ((n (histogram-n h))
(bins (histogram-bins h))
(min (vector-ref (histogram-bins h) 0)))
(do ((i 0 (+ i 1)))
((= i n) min)
(when (< (vector-ref bins i) min)
(set! min (vector-ref bins i))))))
(define (histogram-mean h)
(let ((n (histogram-n h))
(ranges (histogram-ranges h))
(bins (histogram-bins h))
(wmean 0.0)
(W 0.0))
(do ((i 0 (+ i 1)))
((= i n) wmean)
(let ((xi (/ (+ (vector-ref ranges (+ i 1))
(vector-ref ranges i))
2.0))
(wi (vector-ref bins i)))
(when (> wi 0.0)
(set! W (+ W wi))
(set! wmean (+ wmean (* (- xi wmean) (/ wi W)))))))))
(define (histogram-sigma h)
(let ((n (histogram-n h))
(ranges (histogram-ranges h))
(bins (histogram-bins h))
(wvariance 0.0)
(wmean 0.0)
(W 0))
(do ((i 0 (+ i 1)))
((= i n) wmean)
(let ((xi (/ (+ (vector-ref ranges (+ i 1))
(vector-ref ranges i))
2.0))
(wi (vector-ref bins i)))
(when (> wi 0.0)
(set! W (+ W wi))
(set! wmean (+ wmean (* (- xi wmean) (/ wi W)))))))
(set! W 0.0)
(do ((i 0 (+ i 1)))
((= i n) (sqrt wvariance))
(let ((xi (/ (+ (vector-ref ranges (+ i 1))
(vector-ref ranges i))
2.0))
(wi (vector-ref bins i)))
(when (> wi 0.0)
(let ((delta (- xi wmean)))
(set! W (+ W wi))
(set! wvariance (+ wvariance
(* (- (* delta delta) wvariance)
(/ wi W))))))))))
(define (histogram-sum h)
(let ((n (histogram-n h))
(bins (histogram-bins h))
(sum 0.0))
(do ((i 0 (+ i 1)))
((= i n) sum)
(set! sum (+ sum (vector-ref bins i))))))
(provide
(rename-out (histogram-increment! unchecked-histogram-increment!)
(histogram-accumulate! unchecked-histogram-accumulate!)))
(provide/contract
(histogram?
(-> any/c boolean?))
(make-histogram
(-> (and/c integer? (>=/c 1)) histogram?))
(make-histogram-with-ranges-uniform
(->i ((n (and/c integer? (>=/c 1)))
(min real?)
(max (min) (>/c min)))
(result () histogram?)))
(histogram-n
(-> histogram? (and/c integer? (>=/c 1))))
(histogram-ranges
(-> histogram? (vectorof real?)))
(set-histogram-ranges!
(-> histogram? (vectorof real?) void?))
(set-histogram-ranges-uniform!
(->i ((h histogram?)
(min real?)
(max (min) (>/c min)))
(result () void?)))
(histogram-bins
(-> histogram? (vectorof real?)))
(histogram-increment!
(-> histogram? real? void?))
(histogram-accumulate!
(-> histogram? real? (>=/c 0.0) void?))
(histogram-get
(-> histogram? natural-number/c (>=/c 0.0)))
(histogram-get-range
(-> histogram? natural-number/c (values real? real?)))
(histogram-max
(-> histogram? (>=/c 0.0)))
(histogram-min
(-> histogram? (>=/c 0.0)))
(histogram-mean
(-> histogram? real?))
(histogram-sigma
(-> histogram? (>=/c 0.0)))
(histogram-sum
(-> histogram? (>=/c 0.0))))