@@ -14,11 +14,12 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1414use std:: sync:: mpsc:: { Receiver , Sender , channel} ;
1515use std:: thread;
1616
17+ const MOD : usize = 0x7fffffff ;
1718const PART_ONE : usize = 40_000_000 ;
1819const PART_TWO : usize = 5_000_000 ;
1920const BLOCK : usize = 50_000 ;
2021
21- type Input = ( u32 , u32 ) ;
22+ type Input = ( usize , usize ) ;
2223
2324/// State shared between all threads.
2425pub struct Shared {
@@ -31,7 +32,7 @@ pub struct Shared {
3132/// Generated numbers from `start` to `start + BLOCK`.
3233struct Block {
3334 start : usize ,
34- ones : u32 ,
35+ ones : usize ,
3536 fours : Vec < u16 > ,
3637 eights : Vec < u16 > ,
3738}
@@ -51,30 +52,30 @@ pub fn parse(input: &str) -> Input {
5152 } )
5253}
5354
54- pub fn part1 ( input : & Input ) -> u32 {
55+ pub fn part1 ( input : & Input ) -> usize {
5556 input. 0
5657}
5758
58- pub fn part2 ( input : & Input ) -> u32 {
59+ pub fn part2 ( input : & Input ) -> usize {
5960 input. 1
6061}
6162
6263fn sender ( shared : & Shared , tx : & Sender < Block > ) {
6364 while !shared. done . load ( Ordering :: Relaxed ) {
6465 // Start at any point in the sequence using modular exponentiation.
6566 let start = shared. start . fetch_add ( BLOCK , Ordering :: Relaxed ) ;
66- let mut first = shared. first * 16807 . mod_pow ( start, 0x7fffffff ) ;
67- let mut second = shared. second * 48271 . mod_pow ( start, 0x7fffffff ) ;
67+ let mut first = shared. first * 16807 . mod_pow ( start, MOD ) ;
68+ let mut second = shared. second * 48271 . mod_pow ( start, MOD ) ;
6869
69- // Estimate capacity at one quarter or one eight, plus a little extra for variance .
70+ // Estimate capacity at one quarter or one eight.
7071 let mut ones = 0 ;
71- let mut fours = Vec :: with_capacity ( ( BLOCK * 30 ) / 100 ) ;
72- let mut eights = Vec :: with_capacity ( ( BLOCK * 15 ) / 100 ) ;
72+ let mut fours = Vec :: with_capacity ( BLOCK / 4 ) ;
73+ let mut eights = Vec :: with_capacity ( BLOCK / 8 ) ;
7374
7475 // Check part one pairs immediately while queueing part two pairs.
7576 for _ in 0 ..BLOCK {
76- first = ( first * 16807 ) % 0x7fffffff ;
77- second = ( second * 48271 ) % 0x7fffffff ;
77+ first = fast_mod ( first * 16807 ) ;
78+ second = fast_mod ( second * 48271 ) ;
7879
7980 let left = first as u16 ;
8081 let right = second as u16 ;
@@ -94,76 +95,57 @@ fn sender(shared: &Shared, tx: &Sender<Block>) {
9495 }
9596}
9697
97- fn receiver ( shared : & Shared , rx : & Receiver < Block > ) -> ( u32 , u32 ) {
98- let mut remaining = PART_TWO ;
99- let mut part_two = 0 ;
100-
98+ fn receiver ( shared : & Shared , rx : & Receiver < Block > ) -> Input {
10199 let mut required = 0 ;
102100 let mut out_of_order = FastMap :: new ( ) ;
103- let mut blocks = Vec :: new ( ) ;
104101
105- let mut fours_block = 0 ;
106- let mut fours_index = 0 ;
102+ let mut fours = Vec :: with_capacity ( PART_TWO + BLOCK ) ;
103+ let mut eights = Vec :: with_capacity ( PART_TWO + BLOCK ) ;
104+ let mut start = 0 ;
107105
108- let mut eights_block = 0 ;
109- let mut eights_index = 0 ;
106+ let mut part_one = 0 ;
107+ let mut part_two = 0 ;
110108
111- while remaining > 0 {
109+ while required < PART_ONE || fours . len ( ) < PART_TWO || eights . len ( ) < PART_TWO {
112110 // Blocks could be received in any order, as there's no guarantee threads will finish
113111 // processing at the same time. The `start` field of the block defines the order they
114112 // must be added to the vec.
115- while fours_block >= blocks. len ( ) || eights_block >= blocks. len ( ) {
116- let block = rx. recv ( ) . unwrap ( ) ;
113+ while let Ok ( block) = rx. try_recv ( ) {
117114 out_of_order. insert ( block. start , block) ;
118-
119- while let Some ( next) = out_of_order. remove ( & required) {
120- blocks. push ( next) ;
121- required += BLOCK ;
122- }
123115 }
124116
125- // Iterate over the minimum block size or numbers left to check.
126- let fours = & blocks[ fours_block] . fours ;
127- let eights = & blocks[ eights_block] . eights ;
128- let iterations = remaining. min ( fours. len ( ) - fours_index) . min ( eights. len ( ) - eights_index) ;
129-
130- remaining -= iterations;
117+ while let Some ( block) = out_of_order. remove ( & required) {
118+ required += BLOCK ;
131119
132- for _ in 0 ..iterations {
133- if fours[ fours_index] == eights[ eights_index] {
134- part_two += 1 ;
120+ if required <= PART_ONE {
121+ part_one += block. ones ;
135122 }
136- fours_index += 1 ;
137- eights_index += 1 ;
138- }
139123
140- // If we've checked all the numbers in a block, advance to the next one.
141- // This may require waiting for a worker thread to create it first.
142- if fours_index == fours. len ( ) {
143- fours_block += 1 ;
144- fours_index = 0 ;
145- }
146- if eights_index == eights. len ( ) {
147- eights_block += 1 ;
148- eights_index = 0 ;
149- }
150- }
124+ if fours. len ( ) < PART_TWO {
125+ fours. extend_from_slice ( & block. fours ) ;
126+ }
151127
152- // Just in case, make sure we have enough blocks for part one.
153- while required < PART_ONE {
154- let block = rx. recv ( ) . unwrap ( ) ;
155- out_of_order. insert ( block. start , block) ;
128+ if eights. len ( ) < PART_TWO {
129+ eights. extend_from_slice ( & block. eights ) ;
130+ }
156131
157- while let Some ( next) = out_of_order. remove ( & required) {
158- blocks. push ( next) ;
159- required += BLOCK ;
132+ let end = PART_TWO . min ( fours. len ( ) ) . min ( eights. len ( ) ) ;
133+ part_two +=
134+ fours[ start..end] . iter ( ) . zip ( & eights[ start..end] ) . filter ( |( a, b) | a == b) . count ( ) ;
135+ start = end;
160136 }
161137 }
162138
163- // Signal worker thread to finish.
139+ // Signal worker threads to finish.
164140 shared. done . store ( true , Ordering :: Relaxed ) ;
165141
166- // Return results.
167- let part_one = blocks. iter ( ) . take ( PART_ONE / BLOCK ) . map ( |p| p. ones ) . sum ( ) ;
168142 ( part_one, part_two)
169143}
144+
145+ #[ inline]
146+ fn fast_mod ( n : usize ) -> usize {
147+ let low = n & MOD ;
148+ let high = n >> 31 ;
149+ let sum = low + high;
150+ if sum < MOD { sum } else { sum - MOD }
151+ }
0 commit comments