|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +use std::collections::BTreeMap; |
| 12 | +use std::collections::btree_map::Entry; |
| 13 | +use std::marker::PhantomData; |
11 | 14 | use std::iter::FromIterator;
|
| 15 | +use indexed_vec::{Idx, IndexVec}; |
12 | 16 |
|
13 | 17 | type Word = u128;
|
14 | 18 | const WORD_BITS: usize = 128;
|
@@ -257,6 +261,199 @@ impl BitMatrix {
|
257 | 261 | }
|
258 | 262 | }
|
259 | 263 |
|
| 264 | +#[derive(Clone, Debug)] |
| 265 | +pub struct SparseBitMatrix<R, C> where R: Idx, C: Idx { |
| 266 | + vector: IndexVec<R, SparseBitSet<C>>, |
| 267 | +} |
| 268 | + |
| 269 | +impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { |
| 270 | + /// Create a new `rows x columns` matrix, initially empty. |
| 271 | + pub fn new(rows: R, _columns: C) -> SparseBitMatrix<R, C> { |
| 272 | + SparseBitMatrix { |
| 273 | + vector: IndexVec::from_elem_n(SparseBitSet::new(), rows.index()), |
| 274 | + } |
| 275 | + } |
| 276 | + |
| 277 | + /// Sets the cell at `(row, column)` to true. Put another way, insert |
| 278 | + /// `column` to the bitset for `row`. |
| 279 | + /// |
| 280 | + /// Returns true if this changed the matrix, and false otherwise. |
| 281 | + pub fn add(&mut self, row: R, column: C) -> bool { |
| 282 | + self.vector[row].insert(column) |
| 283 | + } |
| 284 | + |
| 285 | + /// Do the bits from `row` contain `column`? Put another way, is |
| 286 | + /// the matrix cell at `(row, column)` true? Put yet another way, |
| 287 | + /// if the matrix represents (transitive) reachability, can |
| 288 | + /// `row` reach `column`? |
| 289 | + pub fn contains(&self, row: R, column: C) -> bool { |
| 290 | + self.vector[row].contains(column) |
| 291 | + } |
| 292 | + |
| 293 | + /// Add the bits from row `read` to the bits from row `write`, |
| 294 | + /// return true if anything changed. |
| 295 | + /// |
| 296 | + /// This is used when computing transitive reachability because if |
| 297 | + /// you have an edge `write -> read`, because in that case |
| 298 | + /// `write` can reach everything that `read` can (and |
| 299 | + /// potentially more). |
| 300 | + pub fn merge(&mut self, read: R, write: R) -> bool { |
| 301 | + let mut changed = false; |
| 302 | + |
| 303 | + if read != write { |
| 304 | + let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write); |
| 305 | + |
| 306 | + for read_val in bit_set_read.iter() { |
| 307 | + changed = changed | bit_set_write.insert(read_val); |
| 308 | + } |
| 309 | + } |
| 310 | + |
| 311 | + changed |
| 312 | + } |
| 313 | + |
| 314 | + /// Iterates through all the columns set to true in a given row of |
| 315 | + /// the matrix. |
| 316 | + pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a { |
| 317 | + self.vector[row].iter() |
| 318 | + } |
| 319 | +} |
| 320 | + |
| 321 | +#[derive(Clone, Debug)] |
| 322 | +pub struct SparseBitSet<I: Idx> { |
| 323 | + chunk_bits: BTreeMap<u32, Word>, |
| 324 | + _marker: PhantomData<I>, |
| 325 | +} |
| 326 | + |
| 327 | +#[derive(Copy, Clone)] |
| 328 | +pub struct SparseChunk<I> { |
| 329 | + key: u32, |
| 330 | + bits: Word, |
| 331 | + _marker: PhantomData<I>, |
| 332 | +} |
| 333 | + |
| 334 | +impl<I: Idx> SparseChunk<I> { |
| 335 | + pub fn one(index: I) -> Self { |
| 336 | + let index = index.index(); |
| 337 | + let key_usize = index / 128; |
| 338 | + let key = key_usize as u32; |
| 339 | + assert_eq!(key as usize, key_usize); |
| 340 | + SparseChunk { |
| 341 | + key, |
| 342 | + bits: 1 << (index % 128), |
| 343 | + _marker: PhantomData |
| 344 | + } |
| 345 | + } |
| 346 | + |
| 347 | + pub fn any(&self) -> bool { |
| 348 | + self.bits != 0 |
| 349 | + } |
| 350 | + |
| 351 | + pub fn iter(&self) -> impl Iterator<Item = I> { |
| 352 | + let base = self.key as usize * 128; |
| 353 | + let mut bits = self.bits; |
| 354 | + (0..128).map(move |i| { |
| 355 | + let current_bits = bits; |
| 356 | + bits >>= 1; |
| 357 | + (i, current_bits) |
| 358 | + }).take_while(|&(_, bits)| bits != 0) |
| 359 | + .filter_map(move |(i, bits)| { |
| 360 | + if (bits & 1) != 0 { |
| 361 | + Some(I::new(base + i)) |
| 362 | + } else { |
| 363 | + None |
| 364 | + } |
| 365 | + }) |
| 366 | + } |
| 367 | +} |
| 368 | + |
| 369 | +impl<I: Idx> SparseBitSet<I> { |
| 370 | + pub fn new() -> Self { |
| 371 | + SparseBitSet { |
| 372 | + chunk_bits: BTreeMap::new(), |
| 373 | + _marker: PhantomData |
| 374 | + } |
| 375 | + } |
| 376 | + |
| 377 | + pub fn capacity(&self) -> usize { |
| 378 | + self.chunk_bits.len() * 128 |
| 379 | + } |
| 380 | + |
| 381 | + pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> { |
| 382 | + SparseChunk { |
| 383 | + bits: self.chunk_bits.get(&chunk.key).map_or(0, |bits| bits & chunk.bits), |
| 384 | + ..chunk |
| 385 | + } |
| 386 | + } |
| 387 | + |
| 388 | + pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> { |
| 389 | + if chunk.bits == 0 { |
| 390 | + return chunk; |
| 391 | + } |
| 392 | + let bits = self.chunk_bits.entry(chunk.key).or_insert(0); |
| 393 | + let old_bits = *bits; |
| 394 | + let new_bits = old_bits | chunk.bits; |
| 395 | + *bits = new_bits; |
| 396 | + let changed = new_bits ^ old_bits; |
| 397 | + SparseChunk { |
| 398 | + bits: changed, |
| 399 | + ..chunk |
| 400 | + } |
| 401 | + } |
| 402 | + |
| 403 | + pub fn remove_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> { |
| 404 | + if chunk.bits == 0 { |
| 405 | + return chunk; |
| 406 | + } |
| 407 | + let changed = match self.chunk_bits.entry(chunk.key) { |
| 408 | + Entry::Occupied(mut bits) => { |
| 409 | + let old_bits = *bits.get(); |
| 410 | + let new_bits = old_bits & !chunk.bits; |
| 411 | + if new_bits == 0 { |
| 412 | + bits.remove(); |
| 413 | + } else { |
| 414 | + bits.insert(new_bits); |
| 415 | + } |
| 416 | + new_bits ^ old_bits |
| 417 | + } |
| 418 | + Entry::Vacant(_) => 0 |
| 419 | + }; |
| 420 | + SparseChunk { |
| 421 | + bits: changed, |
| 422 | + ..chunk |
| 423 | + } |
| 424 | + } |
| 425 | + |
| 426 | + pub fn clear(&mut self) { |
| 427 | + self.chunk_bits.clear(); |
| 428 | + } |
| 429 | + |
| 430 | + pub fn chunks<'a>(&'a self) -> impl Iterator<Item = SparseChunk<I>> + 'a { |
| 431 | + self.chunk_bits.iter().map(|(&key, &bits)| { |
| 432 | + SparseChunk { |
| 433 | + key, |
| 434 | + bits, |
| 435 | + _marker: PhantomData |
| 436 | + } |
| 437 | + }) |
| 438 | + } |
| 439 | + |
| 440 | + pub fn contains(&self, index: I) -> bool { |
| 441 | + self.contains_chunk(SparseChunk::one(index)).any() |
| 442 | + } |
| 443 | + |
| 444 | + pub fn insert(&mut self, index: I) -> bool { |
| 445 | + self.insert_chunk(SparseChunk::one(index)).any() |
| 446 | + } |
| 447 | + |
| 448 | + pub fn remove(&mut self, index: I) -> bool { |
| 449 | + self.remove_chunk(SparseChunk::one(index)).any() |
| 450 | + } |
| 451 | + |
| 452 | + pub fn iter<'a>(&'a self) -> impl Iterator<Item = I> + 'a { |
| 453 | + self.chunks().flat_map(|chunk| chunk.iter()) |
| 454 | + } |
| 455 | +} |
| 456 | + |
260 | 457 | #[inline]
|
261 | 458 | fn words(elements: usize) -> usize {
|
262 | 459 | (elements + WORD_BITS - 1) / WORD_BITS
|
|
0 commit comments