#!/usr/bin/env tclsh9.0 namespace import ::tcl::mathop::* proc defrag {line} { set disk [list] ;# as before, list of file IDs (-1 if free), by block set free [list] ;# now contains {startblock count} set file [list] ;# contains {startblock count}, by file ID set state 0 set id 0 set block 0 foreach c [split $line ""] { if {! [string is digit $c]} continue switch -- $state { 0 { if {$c != 0} {lappend file [list $block $c]} for {set i 0} {$i < $c} {incr i} { lappend disk $id incr block } if {$c != 0} {incr id} set state 1 } 1 { if {$c != 0} {lappend free [list $block $c]} for {set i 0} {$i < $c} {incr i} { lappend disk -1 incr block } set state 0 } } } for {set id [- [llength $file] 1]} {$id > 0} {incr id -1} { # puts "id=$id file=[lindex $file $id]" # puts "disk=$disk" # puts "free=$free" # Skip if there are no free chunks before the file. if {[lindex $free 0 0] >= [lindex $file $id 0]} continue # Iterate through free list, looking for a spot. for {set f 0} {$f < [llength $free]} {incr f} { # Skip if it's not to the left. if {[lindex $free $f 0] > [lindex $file $id 0]} break # puts "freelist=$f is to the left of file=$id" # Skip if it's too small. if {[lindex $file $id 1] > [lindex $free $f 1]} continue # Move each block. # puts "Moving id=$id to freelist=$f" set j [lindex $free $f 0] set k [lindex $file $id 0] for {set i 0} {$i < [lindex $file $id 1]} {incr i} { lset disk $j+$i $id lset disk $k+$i -1 } # Modify the free list entry. lset free $f 0 [+ [lindex $free $f 0] [lindex $file $id 1]] lset free $f 1 [- [lindex $free $f 1] [lindex $file $id 1]] # Don't bother updating the file's starting block. break } } return $disk } proc checksum {disk} { set i 0 set sum 0 while {$i < [llength $disk]} { if {[lindex $disk $i] > 0} { incr sum [* [lindex $disk $i] $i] } incr i } return $sum } set line [read stdin] set disk [defrag $line] # puts $disk puts [checksum $disk]