summaryrefslogtreecommitdiffstats
path: root/2024/tcl/09.tcl
blob: 38a156c49b14772eeb835fc6ff1018c7eed7a2cb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/bin/env tclsh

source lib.tcl
setup 9 data

puts {Part 1: Compacted Filesystem Checksum}

## PARSE
set disk {}
set steps 0
for {set i 0} {$i < [string length $input]} {incr i} {
  set chunk [
    if {$i % 2 == 0} {
      expr {$i / 2}
    } else {
      incr steps [string index $input $i]
      expr {"."}
    }]
  append disk [string repeat "$chunk " [string index $input $i]]
}

## COMPACT
set free_idx -1
for {set i 0} {$i < $steps} {incr i} {
  set fid [lindex $disk end-$i]
  if {$fid == "."} continue

  set free_idx [lsearch -start $free_idx+1 -exact $disk .]

  lset disk end-$i .
  lset disk $free_idx $fid
}

set disk [lrange $disk 0 end-$steps]

## CHECKSUM
set checksum 0
for {set i 0} {$i < [llength $disk]} {incr i} {
  incr checksum [expr {[lindex $disk $i] * $i}]
}

puts "Checksum: $checksum"

puts ""
puts {Part 2: Unfragmented Compacted Filesystem Checksum}

## PARSE
set disk {}
set steps 0
for {set i 0} {$i < [string length $input]} {incr i} {
  if {[string index $input $i] == 0} continue
  lappend disk [list [expr {$i % 2 == 0 ? $i / 2 : "."}] [string index $input $i]]
}

## COMPACT
set free_idx -1
set max_fid [llength $disk]
for {set i [expr {[llength $disk] - 1}]} {$i >= 0} {incr i -1} {
  lassign [lindex $disk $i] fid flen
  if {$fid == "."} continue
  if {$fid >= $max_fid} continue
  set max_fid $fid

#  puts "fid: $fid"


  set free_idx [lsearch -start $free_idx -index 0 -exact $disk .]

  # find large enough free space or bail
  set usable_free_idx $free_idx
#  puts "first free_idx: [lindex $disk $usable_free_idx 1] // $flen"
  while {[lindex $disk $usable_free_idx 1] < $flen} {
#    puts "in this loop"
    set usable_free_idx [lsearch -start $usable_free_idx+1 -index 0 -exact $disk .]
    if {$usable_free_idx == -1} break
  }
#  puts "free_idx: $usable_free_idx // $i"
  if {$usable_free_idx == -1 || $usable_free_idx > $i} continue

  set free_len [lindex $disk $usable_free_idx 1]
#  puts "free_len: $free_len // $flen"
  if {$free_len == $flen} {
    lset disk $usable_free_idx [list $fid $flen]
  } else {
    set disk [lreplace $disk $usable_free_idx $usable_free_idx \
                      [list $fid $flen] \
                      [list . [expr {$free_len - $flen}]]]
    incr i
  }

  # this can create sequences like {20 1} {. 4} {. 5} {. 3} {22 6} but it won't matter
  lset disk $i [list . $flen]
}

## CHECKSUM
set checksum 0
set idx 0
foreach chunk $disk {
  lassign $chunk fid flen
  if {$fid == "."} {
    incr idx $flen
    continue
  }

  set extent [expr {$idx + $flen}]
  for {} {$idx < $extent} {incr idx} {
    incr checksum [expr {$idx * $fid}]
  }
}

puts "Checksum: $checksum"