123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- # Copyright Materialize, Inc. and contributors. All rights reserved.
- #
- # Use of this software is governed by the Business Source License
- # included in the LICENSE file at the root of this repository.
- #
- # As of the Change Date specified in that file, in accordance with
- # the Business Source License, use of this software will be governed
- # by the Apache License, Version 2.0.
- # https://github.com/MaterializeInc/advent-of-code-2023/blob/main/week1/aoc_1219.md
- mode cockroach
- statement ok
- CREATE TABLE input (input TEXT);
- statement ok
- INSERT INTO input VALUES (
- 'in{x<1164:zoz,s>1473:A,a<8576:ask,A}
- ask{m<2275:rsx,zoz}
- rsx{a>8922:A,s>4213:A,R}
- zoz{m>5813:A,s>4522:A,x<245:R,krw}
- krw{a>3747:dqu,a>299:R,a<927:A,A}
- ton{a<8226:ktx,m>1965:ktx,s>3591:uhu,dqu}
- dqu{m<6866:uhu,s>4649:A,R}
- uhu{a<6293:A,lel}
- lel{a<6145:ktx,A}
- ktx{s>8889:R,a>3215:R,R}
- <EMPTY_LINE>
- {x=61,m=818,a=525,s=29}
- {x=225,m=7722,a=964,s=466}
- {x=528,m=3628,a=914,s=8823}
- {x=13,m=675,a=5933,s=9}
- {x=9693,m=8583,a=125,s=787}');
- statement ok
- UPDATE input SET input = replace(input, '<EMPTY_LINE>', '');
- query II
- WITH MUTUALLY RECURSIVE
- blocks(block1 TEXT, block2 TEXT) AS (
- SELECT
- trim(regexp_split_to_array(input, '\n\n')[1]) block1,
- trim(regexp_split_to_array(input, '\n\n')[2]) block2
- FROM input
- ),
- states(state TEXT, trans TEXT) AS (
- SELECT
- regexp_split_to_array(line, '\{')[1] state,
- trim('}' FROM regexp_split_to_array(line, '\{')[2]) trans
- FROM (SELECT regexp_split_to_table(block1, '\n') line FROM blocks)
- ),
- steps(state TEXT, priority INT, rule TEXT) AS (
- SELECT
- state,
- priority,
- regexp_split_to_array(trans, ',')[priority]
- FROM states, generate_series(1, array_length(regexp_split_to_array(trans, ','), 1)) priority
- ),
- starts(x INT, m INT, a INT, s INT) AS (
- SELECT
- substring(regexp_split_to_array(trimmed, ',')[1], 3)::INT,
- substring(regexp_split_to_array(trimmed, ',')[2], 3)::INT,
- substring(regexp_split_to_array(trimmed, ',')[3], 3)::INT,
- substring(regexp_split_to_array(trimmed, ',')[4], 3)::INT
- FROM (SELECT trim('\{' FROM trim('\}' FROM regexp_split_to_table(block2, '\n'))) trimmed FROM blocks)
- ),
- --
- rules(state TEXT, priority INT, field TEXT, cmp TEXT, val INT, next TEXT) AS (
- SELECT
- state,
- priority,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN substring(rule, 1, 1)
- ELSE 'x'
- END,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN substring(rule, 2, 1)
- ELSE '>'
- END,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN regexp_split_to_array(substring(rule, 3), ':')[1]::INT
- ELSE '0'
- END,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN regexp_split_to_array(substring(rule, 3), ':')[2]
- ELSE rule
- END
- FROM steps
- ),
- -- PART 1: iterate folks forward from `in`
- movement(state TEXT, x INT, m INT, a INT, s INT) AS (
- SELECT 'in' state, * FROM starts
- UNION ALL
- SELECT next, x, m, a, s
- FROM (
- SELECT DISTINCT ON (state, x, m, a, s) state, x, m, a, s, priority, next
- FROM (
- SELECT movement.*, rules.next, rules.priority
- FROM movement, rules
- WHERE movement.state = rules.state
- AND CASE WHEN rules.cmp = '<'
- THEN CASE WHEN rules.field = 'x' THEN x < val
- WHEN rules.field = 'm' THEN m < val
- WHEN rules.field = 'a' THEN a < val
- WHEN rules.field = 's' THEN s < val
- ELSE false
- END
- WHEN rules.cmp = '>'
- THEN CASE WHEN rules.field = 'x' THEN x > val
- WHEN rules.field = 'm' THEN m > val
- WHEN rules.field = 'a' THEN a > val
- WHEN rules.field = 's' THEN s > val
- ELSE false
- END
- ELSE false
- END
- )
- ORDER BY state, x, m, a, s, priority
- )
- ),
- part1(part1 BIGINT) AS (
- SELECT SUM(x + m + a + s)
- FROM movement
- WHERE state = 'A'
- ),
- -- PART 2: just find all the bounding regions and label them 'A' or 'R'.
- region(state TEXT, priority INT, xl INT, xu INT, ml INT, mu INT, al INT, au INT, sl INT, su INT) AS (
- SELECT 'in', 1, 1, 4000, 1, 4000, 1, 4000, 1, 4000
- -- Could satisfy the rule, and transition to the next state ..
- UNION ALL
- SELECT
- next,
- 1,
- CASE WHEN rules.field = 'x' AND rules.cmp = '>' THEN GREATEST(val+1, xl) ELSE xl END,
- CASE WHEN rules.field = 'x' AND rules.cmp = '<' THEN LEAST(val-1, xu) ELSE xu END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '>' THEN GREATEST(val+1, ml) ELSE ml END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '<' THEN LEAST(val-1, mu) ELSE mu END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '>' THEN GREATEST(val+1, al) ELSE al END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '<' THEN LEAST(val-1, au) ELSE au END,
- CASE WHEN rules.field = 's' AND rules.cmp = '>' THEN GREATEST(val+1, sl) ELSE sl END,
- CASE WHEN rules.field = 's' AND rules.cmp = '<' THEN LEAST(val-1, su) ELSE su END
- FROM region, rules
- WHERE region.state = rules.state
- AND region.priority = rules.priority
- -- .. or could fail the rule, and advance to the next priority.
- UNION ALL
- SELECT
- region.state,
- region.priority + 1,
- CASE WHEN rules.field = 'x' AND rules.cmp = '<' THEN GREATEST(val, xl) ELSE xl END,
- CASE WHEN rules.field = 'x' AND rules.cmp = '>' THEN LEAST(val, xu) ELSE xu END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '<' THEN GREATEST(val, ml) ELSE ml END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '>' THEN LEAST(val, mu) ELSE mu END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '<' THEN GREATEST(val, al) ELSE al END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '>' THEN LEAST(val, au) ELSE au END,
- CASE WHEN rules.field = 's' AND rules.cmp = '<' THEN GREATEST(val, sl) ELSE sl END,
- CASE WHEN rules.field = 's' AND rules.cmp = '>' THEN LEAST(val, su) ELSE su END
- FROM region, rules
- WHERE region.state = rules.state
- AND region.priority = rules.priority
- ),
- part2(part2 NUMERIC) AS (
- SELECT SUM((1 + xu - xl)::BIGINT * (1 + mu - ml)::BIGINT * (1 + au - al)::BIGINT * (1 + su - sl)::BIGINT)
- FROM region
- WHERE state = 'A'
- ),
- potato(x INT) AS (SELECT 1)
- SELECT * FROM part1, part2;
- ----
- 42458 -257636238955235
- query T multiline
- EXPLAIN OPTIMIZED PLAN WITH(humanized expressions, arity, join implementations) AS VERBOSE TEXT FOR
- WITH MUTUALLY RECURSIVE
- blocks(block1 TEXT, block2 TEXT) AS (
- SELECT
- regexp_split_to_array(input, '\n\n')[1] block1,
- regexp_split_to_array(input, '\n\n')[2] block2
- FROM input
- ),
- states(state TEXT, trans TEXT) AS (
- SELECT
- regexp_split_to_array(line, '\{')[1] state,
- trim('}' FROM regexp_split_to_array(line, '\{')[2]) trans
- FROM (SELECT regexp_split_to_table(block1, '\n') line FROM blocks)
- ),
- steps(state TEXT, priority INT, rule TEXT) AS (
- SELECT
- state,
- priority,
- regexp_split_to_array(trans, ',')[priority]
- FROM states, generate_series(1, array_length(regexp_split_to_array(trans, ','), 1)) priority
- ),
- starts(x INT, m INT, a INT, s INT) AS (
- SELECT
- substring(regexp_split_to_array(trimmed, ',')[1], 3)::INT,
- substring(regexp_split_to_array(trimmed, ',')[2], 3)::INT,
- substring(regexp_split_to_array(trimmed, ',')[3], 3)::INT,
- substring(regexp_split_to_array(trimmed, ',')[4], 3)::INT
- FROM (SELECT trim('\{' FROM trim('\}' FROM regexp_split_to_table(block2, '\n'))) trimmed FROM blocks)
- ),
- --
- rules(state TEXT, priority INT, field TEXT, cmp TEXT, val INT, next TEXT) AS (
- SELECT
- state,
- priority,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN substring(rule, 1, 1)
- ELSE 'x'
- END,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN substring(rule, 2, 1)
- ELSE '>'
- END,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN regexp_split_to_array(substring(rule, 3), ':')[1]::INT
- ELSE '0'
- END,
- CASE WHEN substring(rule, 2, 1) = '<' OR substring(rule, 2, 1) = '>'
- THEN regexp_split_to_array(substring(rule, 3), ':')[2]
- ELSE rule
- END
- FROM steps
- ),
- -- PART 1: iterate folks forward from `in`
- movement(state TEXT, x INT, m INT, a INT, s INT) AS (
- SELECT 'in' state, * FROM starts
- UNION ALL
- SELECT next, x, m, a, s
- FROM (
- SELECT DISTINCT ON (state, x, m, a, s) state, x, m, a, s, priority, next
- FROM (
- SELECT movement.*, rules.next, rules.priority
- FROM movement, rules
- WHERE movement.state = rules.state
- AND CASE WHEN rules.cmp = '<'
- THEN CASE WHEN rules.field = 'x' THEN x < val
- WHEN rules.field = 'm' THEN m < val
- WHEN rules.field = 'a' THEN a < val
- WHEN rules.field = 's' THEN s < val
- ELSE false
- END
- WHEN rules.cmp = '>'
- THEN CASE WHEN rules.field = 'x' THEN x > val
- WHEN rules.field = 'm' THEN m > val
- WHEN rules.field = 'a' THEN a > val
- WHEN rules.field = 's' THEN s > val
- ELSE false
- END
- ELSE false
- END
- )
- ORDER BY state, x, m, a, s, priority
- )
- ),
- part1(part1 BIGINT) AS (
- SELECT SUM(x + m + a + s)
- FROM movement
- WHERE state = 'A'
- ),
- -- PART 2: just find all the bounding regions and label them 'A' or 'R'.
- region(state TEXT, priority INT, xl INT, xu INT, ml INT, mu INT, al INT, au INT, sl INT, su INT) AS (
- SELECT 'in', 1, 1, 4000, 1, 4000, 1, 4000, 1, 4000
- -- Could satisfy the rule, and transition to the next state ..
- UNION ALL
- SELECT
- next,
- 1,
- CASE WHEN rules.field = 'x' AND rules.cmp = '>' THEN GREATEST(val+1, xl) ELSE xl END,
- CASE WHEN rules.field = 'x' AND rules.cmp = '<' THEN LEAST(val-1, xu) ELSE xu END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '>' THEN GREATEST(val+1, ml) ELSE ml END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '<' THEN LEAST(val-1, mu) ELSE mu END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '>' THEN GREATEST(val+1, al) ELSE al END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '<' THEN LEAST(val-1, au) ELSE au END,
- CASE WHEN rules.field = 's' AND rules.cmp = '>' THEN GREATEST(val+1, sl) ELSE sl END,
- CASE WHEN rules.field = 's' AND rules.cmp = '<' THEN LEAST(val-1, su) ELSE su END
- FROM region, rules
- WHERE region.state = rules.state
- AND region.priority = rules.priority
- -- .. or could fail the rule, and advance to the next priority.
- UNION ALL
- SELECT
- region.state,
- region.priority + 1,
- CASE WHEN rules.field = 'x' AND rules.cmp = '<' THEN GREATEST(val, xl) ELSE xl END,
- CASE WHEN rules.field = 'x' AND rules.cmp = '>' THEN LEAST(val, xu) ELSE xu END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '<' THEN GREATEST(val, ml) ELSE ml END,
- CASE WHEN rules.field = 'm' AND rules.cmp = '>' THEN LEAST(val, mu) ELSE mu END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '<' THEN GREATEST(val, al) ELSE al END,
- CASE WHEN rules.field = 'a' AND rules.cmp = '>' THEN LEAST(val, au) ELSE au END,
- CASE WHEN rules.field = 's' AND rules.cmp = '<' THEN GREATEST(val, sl) ELSE sl END,
- CASE WHEN rules.field = 's' AND rules.cmp = '>' THEN LEAST(val, su) ELSE su END
- FROM region, rules
- WHERE region.state = rules.state
- AND region.priority = rules.priority
- ),
- part2(part2 NUMERIC) AS (
- SELECT SUM((1 + xu - xl)::BIGINT * (1 + mu - ml)::BIGINT * (1 + au - al)::BIGINT * (1 + su - sl)::BIGINT)
- FROM region
- WHERE state = 'A'
- ),
- potato(x INT) AS (SELECT 1)
- SELECT * FROM part1, part2;
- ----
- Explained Query:
- With
- cte l0 =
- Project (#0, #2, #6..=#9) // { arity: 6 }
- Map (array_index(regexp_split_to_array[",", case_insensitive=false](#1{trans}), integer_to_bigint(#2{priority})), substr(#3{rule}, 2, 1), ((#4 = "<") OR (#4 = ">")), case when #5 then substr(#3, 1, 1) else "x" end, case when #5 then substr(#3, 2, 1) else ">" end, case when #5 then text_to_integer(array_index(regexp_split_to_array[":", case_insensitive=false](substr(#3, 3)), 1)) else 0 end, case when #5 then array_index(regexp_split_to_array[":", case_insensitive=false](substr(#3, 3)), 2) else #3 end) // { arity: 10 }
- FlatMap generate_series(1, (regexp_split_to_array[",", case_insensitive=false](#1{trans}) array_length 1), 1) // { arity: 3 }
- Project (#3, #4) // { arity: 2 }
- Filter (#3) IS NOT NULL // { arity: 5 }
- Map (regexp_split_to_array["\{", case_insensitive=false](#1{line}), array_index(#2, 1), btrim(array_index(#2, 2), "}")) // { arity: 5 }
- FlatMap unnest_array(regexp_split_to_array["\n", case_insensitive=false](#0{block1})) // { arity: 2 }
- Project (#1) // { arity: 1 }
- Map (array_index(regexp_split_to_array["\n\n", case_insensitive=false](#0{input}), 1)) // { arity: 2 }
- ReadStorage materialize.public.input // { arity: 1 }
- Return // { arity: 2 }
- With Mutually Recursive
- cte l1 =
- Union // { arity: 5 }
- Project (#7, #3..=#6) // { arity: 5 }
- Map (regexp_split_to_array[",", case_insensitive=false](btrim(btrim(#1{unnest}, "\}"), "\{")), text_to_integer(substr(array_index(#2, 1), 3)), text_to_integer(substr(array_index(#2, 2), 3)), text_to_integer(substr(array_index(#2, 3), 3)), text_to_integer(substr(array_index(#2, 4), 3)), "in") // { arity: 8 }
- FlatMap unnest_array(regexp_split_to_array["\n", case_insensitive=false](#0{block2})) // { arity: 2 }
- Project (#1) // { arity: 1 }
- Map (array_index(regexp_split_to_array["\n\n", case_insensitive=false](#0{input}), 2)) // { arity: 2 }
- ReadStorage materialize.public.input // { arity: 1 }
- Project (#6, #1..=#4) // { arity: 5 }
- TopK group_by=[#0, #1, #2, #3, #4] order_by=[#5 asc nulls_last] limit=1 // { arity: 7 }
- Project (#0..=#4, #6, #10) // { arity: 7 }
- Filter case when (#8{cmp} = "<") then case when (#7{field} = "x") then (#1{x} < #9{val}) else case when (#7{field} = "m") then (#2{m} < #9{val}) else case when (#7{field} = "a") then (#3{a} < #9{val}) else case when (#7{field} = "s") then (#4{s} < #9{val}) else false end end end end else case when (#8{cmp} = ">") then case when (#7{field} = "x") then (#1{x} > #9{val}) else case when (#7{field} = "m") then (#2{m} > #9{val}) else case when (#7{field} = "a") then (#3{a} > #9{val}) else case when (#7{field} = "s") then (#4{s} > #9{val}) else false end end end end else false end end // { arity: 11 }
- Join on=(#0{state} = #5{state}) type=differential // { arity: 11 }
- implementation
- %0:l1[#0{state}]K » %1:l0[#0{state}]K
- ArrangeBy keys=[[#0{state}]] // { arity: 5 }
- Filter (#0{state}) IS NOT NULL // { arity: 5 }
- Get l1 // { arity: 5 }
- ArrangeBy keys=[[#0{state}]] // { arity: 6 }
- Get l0 // { arity: 6 }
- cte l2 =
- Reduce aggregates=[sum((((#0{x} + #1{m}) + #2{a}) + #3{s}))] // { arity: 1 }
- Project (#1..=#4) // { arity: 4 }
- Filter (#0{state} = "A") // { arity: 5 }
- Get l1 // { arity: 5 }
- cte l3 =
- Project (#0..=#9, #12..=#15) // { arity: 14 }
- Join on=(#0{state} = #10{state} AND #1{priority} = #11{priority}) type=differential // { arity: 16 }
- implementation
- %0:l4[#0{state}, #1{priority}]KK » %1:l0[#0{state}, #1{priority}]KK
- ArrangeBy keys=[[#0{state}, #1{priority}]] // { arity: 10 }
- Filter (#0{state}) IS NOT NULL // { arity: 10 }
- Get l4 // { arity: 10 }
- ArrangeBy keys=[[#0{state}, #1{priority}]] // { arity: 6 }
- Get l0 // { arity: 6 }
- cte l4 =
- Union // { arity: 10 }
- Project (#13, #28, #16, #18, #20, #21, #23, #24, #26, #27) // { arity: 10 }
- Map ((#10{field} = "x"), (#11{cmp} = ">"), case when (#14 AND #15) then greatest((#12{val} + 1), #2{xl}) else #2{xl} end, (#11{cmp} = "<"), case when (#14 AND #17) then least((#12{val} - 1), #3{xu}) else #3{xu} end, (#10{field} = "m"), case when (#15 AND #19) then greatest((#12{val} + 1), #4{ml}) else #4{ml} end, case when (#17 AND #19) then least((#12{val} - 1), #5{mu}) else #5{mu} end, (#10{field} = "a"), case when (#15 AND #22) then greatest((#12{val} + 1), #6{al}) else #6{al} end, case when (#17 AND #22) then least((#12{val} - 1), #7{au}) else #7{au} end, (#10{field} = "s"), case when (#15 AND #25) then greatest((#12{val} + 1), #8{sl}) else #8{sl} end, case when (#17 AND #25) then least((#12{val} - 1), #9{su}) else #9{su} end, 1) // { arity: 29 }
- Get l3 // { arity: 14 }
- Project (#0, #14, #17, #19, #21, #22, #24, #25, #27, #28) // { arity: 10 }
- Map ((#1{priority} + 1), (#10{field} = "x"), (#11{cmp} = "<"), case when (#15 AND #16) then greatest(#12{val}, #2{xl}) else #2{xl} end, (#11{cmp} = ">"), case when (#15 AND #18) then least(#12{val}, #3{xu}) else #3{xu} end, (#10{field} = "m"), case when (#16 AND #20) then greatest(#12{val}, #4{ml}) else #4{ml} end, case when (#18 AND #20) then least(#12{val}, #5{mu}) else #5{mu} end, (#10{field} = "a"), case when (#16 AND #23) then greatest(#12{val}, #6{al}) else #6{al} end, case when (#18 AND #23) then least(#12{val}, #7{au}) else #7{au} end, (#10{field} = "s"), case when (#16 AND #26) then greatest(#12{val}, #8{sl}) else #8{sl} end, case when (#18 AND #26) then least(#12{val}, #9{su}) else #9{su} end) // { arity: 29 }
- Get l3 // { arity: 14 }
- Constant // { arity: 10 }
- - ("in", 1, 1, 4000, 1, 4000, 1, 4000, 1, 4000)
- Return // { arity: 2 }
- With
- cte l5 =
- Reduce aggregates=[sum((((integer_to_bigint(((1 + #1{xu}) - #0{xl})) * integer_to_bigint(((1 + #3{mu}) - #2{ml}))) * integer_to_bigint(((1 + #5{au}) - #4{al}))) * integer_to_bigint(((1 + #7{su}) - #6{sl}))))] // { arity: 1 }
- Project (#2..=#9) // { arity: 8 }
- Filter (#0{state} = "A") // { arity: 10 }
- Get l4 // { arity: 10 }
- Return // { arity: 2 }
- CrossJoin type=differential // { arity: 2 }
- implementation
- %0[×]U » %1[×]U
- ArrangeBy keys=[[]] // { arity: 1 }
- Union // { arity: 1 }
- Get l2 // { arity: 1 }
- Map (null) // { arity: 1 }
- Union // { arity: 0 }
- Negate // { arity: 0 }
- Project () // { arity: 0 }
- Get l2 // { arity: 1 }
- Constant // { arity: 0 }
- - ()
- ArrangeBy keys=[[]] // { arity: 1 }
- Union // { arity: 1 }
- Get l5 // { arity: 1 }
- Map (null) // { arity: 1 }
- Union // { arity: 0 }
- Negate // { arity: 0 }
- Project () // { arity: 0 }
- Get l5 // { arity: 1 }
- Constant // { arity: 0 }
- - ()
- Source materialize.public.input
- Target cluster: quickstart
- EOF
|