RANDU is a random number generator that was in widespread use in the 1960s and 1970s. It's famous for being really bad. This graphics demo implements the spectral test, as seen in the picture on the wikipedia page about RANDU. It's a cube containing a number of points whose positions were generated by the RANDU function. Adjust the sliders to rotate the cube around. From some angles, the points visually appear to be randomly distributed... but from other angles, it becomes obvious that the points all lie on 15 distinct planes. ====== package require Tk tk appname "RANDU Spectral Test" pack [canvas .c -background black] -fill both -expand 1 foreach i {X Y Z} { pack [scale .s$i -variable $i -from -3.141593 -to 3.141593 -resolution 0 -orient h -command {rotateCube $X $Y $Z; return}] -fill x } set X -0.44 set Y 0.16 # The RANDU pseudo-random number generator. # RANDU is widely considered to be one of the most ill-conceived # random number generators ever designed, and was described # as "truly horrible" by Donald Knuth. set randu_seed 1 proc randu {} { global randu_seed set randu_seed [expr {($randu_seed * 65539) % 2147483648}] return [expr {$randu_seed / 2147483648.0}] } # Generate 1000 points in 3D space with RANDU for {set i 0} {$i<1000} {incr i} { lappend points [list [expr {[randu]*2.0-1.0}] [expr {[randu]*2.0-1.0}] [expr {[randu]*2.0-1.0}]] } # Procedure for calculating the dot product of two matrices proc dotProduct {a b} { set a_w [llength [lindex $a 0] ] set a_h [llength $a] set b_w [llength [lindex $b 0] ] set b_h [llength $b] set c_w $b_w set c_h $a_h set c {} set row {} for {set i 0} {$i<$c_w} {incr i} { lappend row 0 } for {set i 0} {$i<$c_h} {incr i} { lappend c $row } for {set i 0} {$i<$a_h} {incr i} { for {set j 0} {$j<$b_w} {incr j} { set product 0 for {set k 0} {$k<$a_w} {incr k} { set product [expr {[lindex $a $i $k] * [lindex $b $k $j] + $product}] } lset c $i $j $product } } set c } # Define the cube set cube { {-1.0 -1.0 -1.0} {-1.0 -1.0 1.0} { 1.0 -1.0 1.0} { 1.0 -1.0 -1.0} {-1.0 1.0 -1.0} {-1.0 1.0 1.0} { 1.0 1.0 1.0} { 1.0 1.0 -1.0} } proc rotateCube {xa ya za} { # Prepare rotation matrix set rx [list \ [list 1.0 0.0 0.0] \ [list 0.0 [expr {cos($xa)}] [expr {-sin($xa)}]] \ [list 0.0 [expr {sin($xa)}] [expr {cos($xa)}]] \ ] set ry [list \ [list [expr {cos($ya)}] 0.0 [expr {sin($ya)}]] \ {0.0 1.0 0.0} \ [list [expr {-sin($ya)}] 0.0 [expr {cos($ya)}]] \ ] set rz [list \ [list [expr {cos($za)}] [expr {-sin($za)}] 0.0] \ [list [expr {sin($za)}] [expr {cos($za)}] 0.0] \ {0.0 0.0 1.0} \ ] set rr [dotProduct $rz [dotProduct $rx $ry]] # Perform rotation global cube points set rotatedPoints [dotProduct $points $rr] set rotatedCube [dotProduct $cube $rr] # Draw result set wh [expr {min([winfo width .c],[winfo height .c])*0.26}] set cx [expr {[winfo width .c]*0.5}] set cy [expr {[winfo height .c]*0.5}] set l [llength $points] for {set i 0} {$i<$l} {incr i} { lset rotatedPoints $i 0 [expr {[lindex $rotatedPoints $i 0]*$wh+$cx}] lset rotatedPoints $i 1 [expr {[lindex $rotatedPoints $i 1]*$wh+$cy}] } for {set i 0} {$i<8} {incr i} { lappend xx [expr {[lindex $rotatedCube $i 0]*$wh+$cx}] lappend yy [expr {[lindex $rotatedCube $i 1]*$wh+$cy}] } .c delete all # Draw points set pointSize [expr {max(int($wh/70),1)}] foreach i $rotatedPoints { lassign $i x y .c create rectangle $x $y $x $y -width $pointSize -outline blue } # Draw cube foreach j {0 4} { for {set i 0} {$i<4} {incr i} { set a [expr {$i+$j}] set b [expr {(($i+1)&3)+$j}] .c create line \ [lindex $xx $a] \ [lindex $yy $a] \ [lindex $xx $b] \ [lindex $yy $b] -fill white -width 2 } } foreach i {0 1 2 3} { .c create line [lindex $xx $i] [lindex $yy $i] [lindex $xx $i+4] [lindex $yy $i+4] -fill white -width 2 } for {set i 0} {$i<8} {incr i} { set x [lindex $xx $i] set y [lindex $yy $i] .c create oval [expr {$x-4}] [expr {$y-4}] [expr {$x+4}] [expr {$y+4}] -fill white -outline grey } } bind .c [.sX cget -command] ======