Skip to content

Commit 4087a32

Browse files
committed
Bit set iterator
1 parent 8a85454 commit 4087a32

File tree

7 files changed

+76
-44
lines changed

7 files changed

+76
-44
lines changed

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
/// # Utility modules to handle common recurring Advent of Code patterns.
1414
pub mod util {
1515
pub mod ansi;
16+
pub mod bitset;
1617
pub mod grid;
1718
pub mod hash;
1819
pub mod heap;

src/util/bitset.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//! Add `biterator` method that treats an integer as a set, iterating over each element where
2+
//! the respective bit is set. For example `1101` would return 0, 2 and 3.
3+
use crate::util::integer::*;
4+
5+
pub trait BitOps<T> {
6+
fn biterator(self) -> Bitset<T>;
7+
}
8+
9+
impl<T> BitOps<T> for T
10+
where
11+
T: Integer<T>,
12+
{
13+
fn biterator(self) -> Bitset<T> {
14+
Bitset { t: self }
15+
}
16+
}
17+
18+
pub struct Bitset<T> {
19+
t: T,
20+
}
21+
22+
impl<T> Iterator for Bitset<T>
23+
where
24+
T: Integer<T>,
25+
T: TryInto<usize>,
26+
{
27+
type Item = usize;
28+
29+
#[inline]
30+
fn next(&mut self) -> Option<Self::Item> {
31+
if self.t == T::ZERO {
32+
return None;
33+
}
34+
35+
let tz = self.t.trailing_zeros();
36+
self.t = self.t ^ (T::ONE << tz);
37+
38+
tz.try_into().ok()
39+
}
40+
}

src/util/integer.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Combines common [operators](https://doc.rust-lang.org/book/appendix-02-operators.html)
22
//! and constants `0`, `1` and `10` to enable generic methods on integer types.
3-
use std::ops::{Add, BitAnd, BitOr, Div, Mul, Neg, Rem, Shl, Shr, Sub};
3+
use std::ops::*;
44

55
pub trait Integer<T>:
66
Copy
@@ -10,6 +10,7 @@ pub trait Integer<T>:
1010
+ Add<Output = T>
1111
+ BitAnd<Output = T>
1212
+ BitOr<Output = T>
13+
+ BitXor<Output = T>
1314
+ Div<Output = T>
1415
+ Mul<Output = T>
1516
+ Rem<Output = T>
@@ -22,6 +23,7 @@ pub trait Integer<T>:
2223
const TEN: T;
2324

2425
fn ilog2(self) -> T;
26+
fn trailing_zeros(self) -> T;
2527
}
2628

2729
pub trait Unsigned<T>: Integer<T> {}
@@ -38,7 +40,13 @@ macro_rules! integer {
3840
#[inline]
3941
#[allow(trivial_numeric_casts)]
4042
fn ilog2(self) -> $t {
41-
self.ilog2() as $t
43+
<$t>::ilog2(self) as $t
44+
}
45+
46+
#[inline]
47+
#[allow(trivial_numeric_casts)]
48+
fn trailing_zeros(self) -> $t {
49+
<$t>::trailing_zeros(self) as $t
4250
}
4351
}
4452
)*)

src/year2017/day24.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//!
1414
//! If we can place such a component then there's no need to consider further components which
1515
//! reduces the total number of combination to consider.
16+
use crate::util::bitset::*;
1617
use crate::util::iter::*;
1718
use crate::util::parse::*;
1819

@@ -107,16 +108,12 @@ pub fn part2(input: &[usize]) -> usize {
107108

108109
fn build(state: &mut State, current: usize, used: usize, strength: usize, length: usize) {
109110
// Bitset of all unused components that have a matching port.
110-
let mut remaining = state.possible[current] & !used;
111-
112-
while remaining > 0 {
113-
// Extract the index of each component from the bitset.
114-
let index = remaining.trailing_zeros() as usize;
115-
let mask = 1 << index;
116-
remaining ^= mask;
111+
let remaining = state.possible[current] & !used;
117112

113+
// Extract the index of each component from the bitset.
114+
for index in remaining.biterator() {
118115
let next = current ^ state.both[index];
119-
let used = used | mask;
116+
let used = used | (1 << index);
120117
let strength = strength + state.weight[index];
121118
let length = length + state.length[index];
122119

src/year2019/day18.rs

+7-16
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#![allow(clippy::needless_range_loop)]
5151
#![allow(clippy::unnecessary_lazy_evaluations)]
5252

53+
use crate::util::bitset::*;
5354
use crate::util::grid::*;
5455
use crate::util::hash::*;
5556
use crate::util::heap::*;
@@ -196,27 +197,17 @@ fn explore(width: i32, bytes: &[u8]) -> u32 {
196197
return total;
197198
}
198199

199-
let mut robots = position;
200-
201-
while robots != 0 {
202-
// The set of robots is stored as bits in a `u32` shifted by the index of the location.
203-
let from = robots.trailing_zeros() as usize;
204-
let from_mask = 1 << from;
205-
robots ^= from_mask;
206-
207-
let mut keys = remaining;
208-
209-
while keys != 0 {
210-
// The set of keys still needed is also stored as bits in a `u32` similar as robots.
211-
let to = keys.trailing_zeros() as usize;
212-
let to_mask = 1 << to;
213-
keys ^= to_mask;
214-
200+
// The set of robots is stored as bits in a `u32` shifted by the index of the location.
201+
for from in position.biterator() {
202+
// The set of keys still needed is also stored as bits in a `u32` similar as robots.
203+
for to in remaining.biterator() {
215204
let Door { distance, needed } = maze[from][to];
216205

217206
// u32::MAX indicates that two nodes are not connected. Only possible in part two.
218207
if distance != u32::MAX && remaining & needed == 0 {
219208
let next_total = total + distance;
209+
let from_mask = 1 << from;
210+
let to_mask = 1 << to;
220211
let next_state = State {
221212
position: position ^ from_mask ^ to_mask,
222213
remaining: remaining ^ to_mask,

src/year2021/day12.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! cave connections as an [adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_matrix)
1313
//! and the list of visited caves compressed into a single `u32` is the low level strategy to
1414
//! quickly and efficiently store the small cardinality set of caves.
15+
use crate::util::bitset::*;
1516
use crate::util::hash::*;
1617
use crate::util::iter::*;
1718

@@ -139,19 +140,17 @@ fn paths(input: &Input, state: &State, cache: &mut [u32]) -> u32 {
139140

140141
let mut caves = input.edges[from];
141142
let mut total = 0;
142-
let mut mask = 1 << END;
143+
let end = 1 << END;
143144

144-
if caves & mask != 0 {
145-
caves ^= mask;
145+
if caves & end != 0 {
146+
caves ^= end;
146147
total += 1;
147148
}
148149

149-
while caves != 0 {
150-
let to = caves.trailing_zeros() as usize;
151-
mask = 1 << to;
152-
caves ^= mask;
153-
150+
for to in caves.biterator() {
151+
let mask = 1 << to;
154152
let once = input.small & mask == 0 || visited & mask == 0;
153+
155154
if once || twice {
156155
let next = State { from: to, visited: visited | mask, twice: once && twice };
157156
total += paths(input, &next, cache);

src/year2022/day16.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
//! Then we check every possible pair formed by those values, considering only the pairs
3939
//! where the sets of valves are [disjoint](https://en.wikipedia.org/wiki/Disjoint_sets),
4040
//! which is when you and the elephant have visited different sets of valves.
41+
use crate::util::bitset::*;
4142
use crate::util::hash::*;
4243
use crate::util::parse::*;
4344
use std::cmp::Ordering;
@@ -264,24 +265,19 @@ pub fn part2(input: &Input) -> u32 {
264265
fn explore(input: &Input, state: &State, high_score: &mut impl FnMut(usize, u32) -> u32) {
265266
let State { todo, from, time, pressure } = *state;
266267
let score = high_score(todo, pressure);
267-
let mut valves = todo;
268-
269-
while valves > 0 {
270-
// Stores the set of unopened valves in a single integer as a bit mask with a 1
271-
// for each unopened valve. This code iterates over each valve by finding the lowest
272-
// 1 bit then removing it from the set.
273-
let to = valves.trailing_zeros() as usize;
274-
let mask = 1 << to;
275-
valves ^= mask;
276268

269+
// Stores the set of unopened valves in a single integer as a bit mask with a 1
270+
// for each unopened valve. This code iterates over each valve by finding the lowest
271+
// 1 bit then removing it from the set.
272+
for to in todo.biterator() {
277273
// Check if there's enough time to reach the valve.
278274
let needed = input.distance[from * input.size + to];
279275
if needed >= time {
280276
continue;
281277
}
282278

283279
// Calculate the total pressure released by a valve up front.
284-
let todo = todo ^ mask;
280+
let todo = todo ^ (1 << to);
285281
let time = time - needed;
286282
let pressure = pressure + time * input.flow[to];
287283

0 commit comments

Comments
 (0)