Artifact 689ec1242fc524bbda73c1abd555da344ad98cb3:
- File
packages/math/lib/rand.tcl
— part of check-in
[f7efe0061c]
at
2017-09-10 10:47:59
on branch trunk
— ycl {
{accomodate switch from [shelf .subcmd] to [shelf .routine]}
{further development across many modules}
} (user: pooryorick size: 3275)
#! /bin/env tclsh variable doc::flip { description { Assume that there is jitter inherent in the system, and use it as an entropy source by capturaing variations in time at the edge of a click. } } proc flip {} { variable repeat if {$repeat > 10} { set repeat [expr {entier($repeat - $repeat * .20)}] } set direction -1 while 1 { set diffs {} set count 25 set half [expr {entier($count / 2)}] for {set i 0} {$i < $count} {incr i} { set c1 [clock clicks] time {expr {cos($repeat)}} $repeat set c2 [clock clicks] set diff [expr {$c2 - $c1}] dict incr diffs $diff } set mindiffs [expr {entier(sqrt($count))}] # Select and whiten if {[llength [dict keys $diffs]] >= $mindiffs && [::tcl::mathfunc::max {*}[dict values $diffs]] <= $half} { # The calculations took place at the edge of a click , reflecting # jitter , or at some other point when the system is unstable . break } if {$repeat == 0} { set direction 1 } incr repeat $direction } expr {$diff % 2} } variable doc::randbytes { description { Produce random bytes . } } proc randbytes count { set res {} while {[string length $res] < $count} { set num 0 set diff 0 for {set i 0} {$i < 9} {incr i} { set num [expr {($num << 1) | [flip]}] } append res [binary format c [expr {$num & 255}]] } return $res } proc randbytes_udev {{bytes 256}} { set res {} if {[file exists /dev/urandom]} { set chan [open /dev/urandom rb] chan configure $chan -blocking 1 append res [read $chan $bytes] close $chan } else { #todo: accomodate more methods return -code error [list {no entropy source}] package require platform } return $res } proc rand ceiling { package require {ycl math rng} namespace import [yclprefix]::math::rng proc rand ceiling { set needed [expr {entier(ceil(log($ceiling)/log(2)/8))}] set data [rng take $needed] binary scan $data H* hex scan $hex %llx res return [expr {$res % $ceiling}] } tailcall rand $ceiling } variable doc::randprint_script { description { Creates a script that produces random strings of printable characters. By default, it produces a command that produces strings that, when interpreted as an integer in base-68 occupies approximately, but no more than 256 bits. } args { randscript { description { A script that evaluates to a random mumber between 0 and 67 , inclusive . } count { The size of the string to produce, in characters } } } } proc randprint_script args { dict update args randscript randscript count count {} if {![info exists randscript]} { set randscript {expr {entier(rand() * 68)}} } if {![info exists count]} { set count 42 } return "lindex [string repeat "\[lindex {# @ + - ^ = 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z} \[$randscript]]" $count]" } proc randprint_256 {} { package require {ycl math rng} namespace import [yclprefix]::math::rng proc randprint_256 {} [randprint_script randscript [namespace code { apply [list args { binary scan [rng take 8] H* data expr int(.[string reverse [expr 0x$data]] * 68) } [namespace current]] }]] tailcall randprint_256 } variable repeat 1