#!/usr/bin/tclsh

proc test {name format value min max} {
	global fail
	puts -nonewline "[format %20s $name]: [format $format $value]\
	(<[format $format $min] and >[format $format $max])  "
	if {$value > $min && $value < $max} {
		puts "PASSED"
	} else {
		incr fail
		puts "FAILED"
	}
}	

if {$argc != 1} {
	puts stderr "Usage $argv0 filename\n\
	\twhere filename file with at least 20000 random bits (i.e.  /dev/random)"
	exit 2
}	
fconfigure stderr -buffering none
set f [open [lindex $argv 0]]
fconfigure $f -translation binary -blocking y
puts -nonewline stderr  "Reading 20000 bits"
set bytes [read $f 2500]
puts " done"
close $f
if {[string length $bytes]<2500} {
	puts stderr "Need at least 2500 bytes, got [string length $bytes]"
	exit 2
}	
# monobit test 

array set hex2ones {0 0 1 1 2 1 3 2 4 1 5 2 6 2 7 3 8 1 9 2 a 2 b 3 c 2
d 3 e 3 f 4}

binary scan $bytes H* hex 

set ones 0
array set hist {0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 a 0 b 0 c 0 d 0 e 0 f 0}

foreach nibble [split $hex ""] {
	incr hist($nibble) 
}
set sumsquare 0
foreach {nibble count} [array get hist] {
	set sumsquare [expr {$sumsquare + $count * $count}]
	incr ones [expr $hist($nibble)*$hex2ones($nibble)] 
}
#parray hist
set fail 0
test "Monobit test" "%5d" $ones 9654 10346

set chi [expr {$sumsquare*16.0/5000-5000}]

test "Poker test" %5.2f $chi 1.03 57.4
binary scan $bytes b* bits
append bits 2
set run 0
array set runs {0,1 0 0,2 0 0,3 0 0,4 0 0,5 0 0,6 0
1,1 0 1,2 0 1,3 0 1,4 0 1,5 0 1,6 0}
array set runlimits {
	1 {2267 2733} 
	2 {1079 1421} 
	3 {502 748} 
	4 {223 402}
	5 {90 223}
	6 {90 223}
}	

set prevbit 2
set longest 0
foreach bit [split $bits ""] {
 	if {$bit == $prevbit} {
		incr run
	} else {
		if {$run>0} {
			if {$run<=6} {
				incr runs($prevbit,$run)
			} else {
				incr runs($prevbit,6)
			} 	
			if {$run>$longest} {
				set longest $run
			}
		}
		set prevbit $bit
		set run 1
	}
}
puts "[format %20s "Runs test"]:"
for {set i 1} {$i<=6} {incr i} {
	eval test [list "$i zeroes"] %5d $runs(0,$i) $runlimits($i)
	eval test [list "$i ones  "] %5d $runs(0,$i) $runlimits($i)
	
}
test "Long Run test" %5d  $longest 6 34

if {$fail!=0} {
	puts "Total result: FAILED!!!"
	exit 1
}	


