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
def decompress(input)
  output = ''
  ss = StringScanner.new(input)
  until ss.eos?
    if ss.scan(/(.*?)\((\d+)x(\d+)\)/)
      output << ss[1]

      times = ss[3].to_i
      seq = ss.scan(/.{#{ss[2].to_i}}/)

      output << (seq * times)
    else
      output << ss.scan(/.*/)
    end
  end
  output
end

def decompressed_length(input)
  length = 0
  ss = StringScanner.new(input)
  until ss.eos?
    case
    when ss.scan(/\((\d+)x(\d+)\)/)
      times = ss[2].to_i
      seq = ss.scan(/.{#{ss[1].to_i}}/)
      length += times * decompressed_length(seq)
    when ss.scan(/[^(]+/)
      length += ss.matched.length
    else
      raise 'omg!'
    end
  end
  length
end

if __FILE__ == $0
  puts decompressed_length(DATA.read.chomp)
end

require 'minitest'
# require 'minitest/autorun'

class TestDecompress < Minitest::Test
  def test_decompress
    assert_equal 'ADVENT', decompress('ADVENT')
    assert_equal 'ABBBBBC', decompress('A(1x5)BC')
    assert_equal 'XYZXYZXYZ', decompress('(3x3)XYZ')
    assert_equal 'ABCBCDEFEFG', decompress('A(2x2)BCD(2x2)EFG')
    assert_equal '(1x3)A', decompress('(6x1)(1x3)A')
    assert_equal 'X(3x3)ABC(3x3)ABCY', decompress('X(8x2)(3x3)ABCY')
  end

  def test_decompressed_length
    assert_equal 9, decompressed_length('(3x3)XYZ')
    assert_equal 20, decompressed_length('X(8x2)(3x3)ABCY')
    assert_equal 241920, decompressed_length('(27x12)(20x12)(13x14)(7x10)(1x12)A')
    input = '(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN'
    assert_equal 445, decompressed_length(input)
  end
end

__END__