1
1
//! # Crossed Wires
2
+ //!
3
+ //! Part one is a straightforward simulation of the gates. Part two asks us to fix a broken
4
+ //! [ripple carry adder](https://en.wikipedia.org/wiki/Adder_(electronics)).
5
+ //!
6
+ //! The structure of the adder is:
7
+ //!
8
+ //! * Half adder for bits `x00` and `y00`. Outputs sum to `z00` and carry to `z01`.
9
+ //! * Full adder for bits `x01..x44` and `y01..y44`. Outputs carry to next bit in the chain
10
+ //! "rippling" up to final bit.
11
+ //! * `z45` is the carry output from `x44` and `y44`.
12
+ //!
13
+ //! Implemented in logic gates this looks like:
14
+ //!
15
+ //! ```none
16
+ //! Half Adder Full Adder
17
+ //! ┌───┐ ┌───┐ ┌───┐ ┌───┐
18
+ //! |x00| |y00| |x01| |y01|
19
+ //! └───┘ └───┘ └───┘ └───┘
20
+ //! | | ┌─┘ | | | ┌─┘ |
21
+ //! | └───┐ | | └───┐ |
22
+ //! | ┌-┘ | | | ┌-┘ | |
23
+ //! ┌───┐ ┌───┐ ┌───┐ ┌───┐
24
+ //! |XOR| |AND| |XOR| |AND|
25
+ //! └───┘ └───┘ └───┘ └───┘
26
+ //! | | ┌───┴┐ |
27
+ //! | └──┬────┐ | |
28
+ //! | Carry| | ┌───┐ |
29
+ //! | out | | |AND| |
30
+ //! | | | └───┘ |
31
+ //! | | | └────┐ |
32
+ //! | | └────┐ | |
33
+ //! | └────┐ | | |
34
+ //! | ┌───┐ ┌───┐
35
+ //! | |XOR| |OR | Carry
36
+ //! | └───┘ └───┘ out
37
+ //! | | | |
38
+ //! ┌───┐ ┌───┐ | ┌───┐
39
+ //! |z00| |z01| Carry ...repeat for z01 to z44... |z45|
40
+ //! └───┘ └───┘ out └───┘
41
+ //! ```
42
+ //!
43
+ //! Then we can deduce some rules for the output of each gate type:
44
+ //!
45
+ //! 1. **XOR** If inputs are `x` and `y` then output must be another XOR gate
46
+ //! (except for inputs `x00` and `y00`) otherwise output must be `z`.
47
+ //! 2. **AND** Output must be an OR gate (except for inputs `x00` and `y00`).
48
+ //! 3. **OR** Output must be both AND and XOR gate, except for final carry
49
+ //! which must output to `z45`.
50
+ //!
51
+ //! We only need to find swapped outputs (not fix them) so the result is the labels of gates
52
+ //! that breaks the rules in alphabetical order.
2
53
use crate :: util:: hash:: * ;
3
54
use crate :: util:: iter:: * ;
4
55
use crate :: util:: parse:: * ;
@@ -15,21 +66,27 @@ pub fn parse(input: &str) -> Input<'_> {
15
66
pub fn part1 ( input : & Input < ' _ > ) -> u64 {
16
67
let ( prefix, gates) = input;
17
68
69
+ // Using an array to store already computed values is much faster than a `HashMap`.
18
70
let mut todo: VecDeque < _ > = gates. iter ( ) . copied ( ) . collect ( ) ;
19
71
let mut cache = vec ! [ u8 :: MAX ; 1 << 15 ] ;
20
72
let mut result = 0 ;
21
73
74
+ // Convert each character to a 5 bit number from 0..31
75
+ // then each group of 3 to a 15 bit index from 0..32768.
22
76
let to_index = |s : & str | {
23
77
let b = s. as_bytes ( ) ;
24
78
( ( b[ 0 ] as usize & 31 ) << 10 ) + ( ( b[ 1 ] as usize & 31 ) << 5 ) + ( b[ 2 ] as usize & 31 )
25
79
} ;
26
80
81
+ // Add input signals to cache.
27
82
for line in prefix. lines ( ) {
28
83
let prefix = & line[ ..3 ] ;
29
84
let suffix = & line[ 5 ..] ;
30
85
cache[ to_index ( prefix) ] = suffix. unsigned ( ) ;
31
86
}
32
87
88
+ // If both inputs are available then add gate output to cache
89
+ // otherwise push back to end of queue for reprocessing later.
33
90
while let Some ( gate @ [ left, kind, right, _, to] ) = todo. pop_front ( ) {
34
91
let left = cache[ to_index ( left) ] ;
35
92
let right = cache[ to_index ( right) ] ;
@@ -46,7 +103,8 @@ pub fn part1(input: &Input<'_>) -> u64 {
46
103
}
47
104
}
48
105
49
- for i in ( to_index ( "z00" ) ..to_index ( "z64" ) ) . rev ( ) {
106
+ // Output 46 bit result.
107
+ for i in ( to_index ( "z00" ) ..to_index ( "z46" ) ) . rev ( ) {
50
108
if cache[ i] != u8:: MAX {
51
109
result = ( result << 1 ) | ( cache[ i] as u64 ) ;
52
110
}
@@ -58,18 +116,19 @@ pub fn part1(input: &Input<'_>) -> u64 {
58
116
pub fn part2 ( input : & Input < ' _ > ) -> String {
59
117
let ( _, gates) = input;
60
118
61
- let mut lookup = FastSet :: new ( ) ;
119
+ let mut output = FastSet :: new ( ) ;
62
120
let mut swapped = FastSet :: new ( ) ;
63
121
122
+ // Track the kind of gate that each wire label outputs to.
64
123
for & [ left, kind, right, _, _] in gates {
65
- lookup . insert ( ( left, kind) ) ;
66
- lookup . insert ( ( right, kind) ) ;
124
+ output . insert ( ( left, kind) ) ;
125
+ output . insert ( ( right, kind) ) ;
67
126
}
68
127
69
128
for & [ left, kind, right, _, to] in gates {
70
129
if kind == "AND" {
71
130
// Check that all AND gates point to an OR, except for first AND.
72
- if left != "x00" && right != "x00" && !lookup . contains ( & ( to, "OR" ) ) {
131
+ if left != "x00" && right != "x00" && !output . contains ( & ( to, "OR" ) ) {
73
132
swapped. insert ( to) ;
74
133
}
75
134
}
@@ -80,15 +139,15 @@ pub fn part2(input: &Input<'_>) -> String {
80
139
swapped. insert ( to) ;
81
140
}
82
141
// OR can never point to OR.
83
- if lookup . contains ( & ( to, "OR" ) ) {
142
+ if output . contains ( & ( to, "OR" ) ) {
84
143
swapped. insert ( to) ;
85
144
}
86
145
}
87
146
88
147
if kind == "XOR" {
89
148
if left. starts_with ( 'x' ) || right. starts_with ( 'x' ) {
90
149
// Check that first level XOR points to second level XOR, except for first XOR.
91
- if left != "x00" && right != "x00" && !lookup . contains ( & ( to, "XOR" ) ) {
150
+ if left != "x00" && right != "x00" && !output . contains ( & ( to, "XOR" ) ) {
92
151
swapped. insert ( to) ;
93
152
}
94
153
} else {
0 commit comments