diff --git a/Cargo.toml b/Cargo.toml index b606aed..6b7b724 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,10 @@ readme = "README.md" keywords = ["unification", "union-find"] [features] +std = [ ] bench = [ ] -persistent = [ "dogged" ] +persistent = [ "std", "dogged" ] [dependencies] dogged = { version = "0.2.0", optional = true } -log = "0.4" +log = { version = "0.4", optional = true } diff --git a/src/bitvec.rs b/src/bitvec.rs deleted file mode 100644 index 3677c8c..0000000 --- a/src/bitvec.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// A very simple BitVector type. -pub struct BitVector { - data: Vec, -} - -impl BitVector { - pub fn new(num_bits: usize) -> BitVector { - let num_words = u64s(num_bits); - BitVector { data: vec![0; num_words] } - } - - pub fn contains(&self, bit: usize) -> bool { - let (word, mask) = word_mask(bit); - (self.data[word] & mask) != 0 - } - - /// Returns true if the bit has changed. - pub fn insert(&mut self, bit: usize) -> bool { - let (word, mask) = word_mask(bit); - let data = &mut self.data[word]; - let value = *data; - let new_value = value | mask; - *data = new_value; - new_value != value - } - - pub fn insert_all(&mut self, all: &BitVector) -> bool { - assert!(self.data.len() == all.data.len()); - let mut changed = false; - for (i, j) in self.data.iter_mut().zip(&all.data) { - let value = *i; - *i = value | *j; - if value != *i { - changed = true; - } - } - changed - } - - pub fn grow(&mut self, num_bits: usize) { - let num_words = u64s(num_bits); - let extra_words = self.data.len() - num_words; - self.data.extend((0..extra_words).map(|_| 0)); - } - - /// Iterates over indexes of set bits in a sorted order - pub fn iter<'a>(&'a self) -> BitVectorIter<'a> { - BitVectorIter { - iter: self.data.iter(), - current: 0, - idx: 0, - } - } -} - -pub struct BitVectorIter<'a> { - iter: ::std::slice::Iter<'a, u64>, - current: u64, - idx: usize, -} - -impl<'a> Iterator for BitVectorIter<'a> { - type Item = usize; - fn next(&mut self) -> Option { - while self.current == 0 { - self.current = if let Some(&i) = self.iter.next() { - if i == 0 { - self.idx += 64; - continue; - } else { - self.idx = u64s(self.idx) * 64; - i - } - } else { - return None; - } - } - let offset = self.current.trailing_zeros() as usize; - self.current >>= offset; - self.current >>= 1; // shift otherwise overflows for 0b1000_0000_…_0000 - self.idx += offset + 1; - return Some(self.idx - 1); - } -} - -/// A "bit matrix" is basically a square matrix of booleans -/// represented as one gigantic bitvector. In other words, it is as if -/// you have N bitvectors, each of length N. Note that `elements` here is `N`/ -#[derive(Clone)] -pub struct BitMatrix { - elements: usize, - vector: Vec, -} - -impl BitMatrix { - // Create a new `elements x elements` matrix, initially empty. - pub fn new(elements: usize) -> BitMatrix { - // For every element, we need one bit for every other - // element. Round up to an even number of u64s. - let u64s_per_elem = u64s(elements); - BitMatrix { - elements: elements, - vector: vec![0; elements * u64s_per_elem], - } - } - - /// The range of bits for a given element. - fn range(&self, element: usize) -> (usize, usize) { - let u64s_per_elem = u64s(self.elements); - let start = element * u64s_per_elem; - (start, start + u64s_per_elem) - } - - pub fn add(&mut self, source: usize, target: usize) -> bool { - let (start, _) = self.range(source); - let (word, mask) = word_mask(target); - let mut vector = &mut self.vector[..]; - let v1 = vector[start + word]; - let v2 = v1 | mask; - vector[start + word] = v2; - v1 != v2 - } - - /// Do the bits from `source` contain `target`? - /// - /// Put another way, if the matrix represents (transitive) - /// reachability, can `source` reach `target`? - pub fn contains(&self, source: usize, target: usize) -> bool { - let (start, _) = self.range(source); - let (word, mask) = word_mask(target); - (self.vector[start + word] & mask) != 0 - } - - /// Returns those indices that are reachable from both `a` and - /// `b`. This is an O(n) operation where `n` is the number of - /// elements (somewhat independent from the actual size of the - /// intersection, in particular). - pub fn intersection(&self, a: usize, b: usize) -> Vec { - let (a_start, a_end) = self.range(a); - let (b_start, b_end) = self.range(b); - let mut result = Vec::with_capacity(self.elements); - for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() { - let mut v = self.vector[i] & self.vector[j]; - for bit in 0..64 { - if v == 0 { - break; - } - if v & 0x1 != 0 { - result.push(base * 64 + bit); - } - v >>= 1; - } - } - result - } - - /// Add the bits from `read` to the bits from `write`, - /// return true if anything changed. - /// - /// This is used when computing transitive reachability because if - /// you have an edge `write -> read`, because in that case - /// `write` can reach everything that `read` can (and - /// potentially more). - pub fn merge(&mut self, read: usize, write: usize) -> bool { - let (read_start, read_end) = self.range(read); - let (write_start, write_end) = self.range(write); - let vector = &mut self.vector[..]; - let mut changed = false; - for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) { - let v1 = vector[write_index]; - let v2 = v1 | vector[read_index]; - vector[write_index] = v2; - changed = changed | (v1 != v2); - } - changed - } -} - -fn u64s(elements: usize) -> usize { - (elements + 63) / 64 -} - -fn word_mask(index: usize) -> (usize, u64) { - let word = index / 64; - let mask = 1 << (index % 64); - (word, mask) -} - -#[test] -fn bitvec_iter_works() { - let mut bitvec = BitVector::new(100); - bitvec.insert(1); - bitvec.insert(10); - bitvec.insert(19); - bitvec.insert(62); - bitvec.insert(63); - bitvec.insert(64); - bitvec.insert(65); - bitvec.insert(66); - bitvec.insert(99); - assert_eq!(bitvec.iter().collect::>(), - [1, 10, 19, 62, 63, 64, 65, 66, 99]); -} - -#[test] -fn bitvec_iter_works_2() { - let mut bitvec = BitVector::new(300); - bitvec.insert(1); - bitvec.insert(10); - bitvec.insert(19); - bitvec.insert(62); - bitvec.insert(66); - bitvec.insert(99); - bitvec.insert(299); - assert_eq!(bitvec.iter().collect::>(), - [1, 10, 19, 62, 66, 99, 299]); - -} - -#[test] -fn bitvec_iter_works_3() { - let mut bitvec = BitVector::new(319); - bitvec.insert(0); - bitvec.insert(127); - bitvec.insert(191); - bitvec.insert(255); - bitvec.insert(319); - assert_eq!(bitvec.iter().collect::>(), [0, 127, 191, 255, 319]); -} - -#[test] -fn union_two_vecs() { - let mut vec1 = BitVector::new(65); - let mut vec2 = BitVector::new(65); - assert!(vec1.insert(3)); - assert!(!vec1.insert(3)); - assert!(vec2.insert(5)); - assert!(vec2.insert(64)); - assert!(vec1.insert_all(&vec2)); - assert!(!vec1.insert_all(&vec2)); - assert!(vec1.contains(3)); - assert!(!vec1.contains(4)); - assert!(vec1.contains(5)); - assert!(!vec1.contains(63)); - assert!(vec1.contains(64)); -} - -#[test] -fn grow() { - let mut vec1 = BitVector::new(65); - assert!(vec1.insert(3)); - assert!(!vec1.insert(3)); - assert!(vec1.insert(5)); - assert!(vec1.insert(64)); - vec1.grow(128); - assert!(vec1.contains(3)); - assert!(vec1.contains(5)); - assert!(vec1.contains(64)); - assert!(!vec1.contains(126)); -} - -#[test] -fn matrix_intersection() { - let mut vec1 = BitMatrix::new(200); - - // (*) Elements reachable from both 2 and 65. - - vec1.add(2, 3); - vec1.add(2, 6); - vec1.add(2, 10); // (*) - vec1.add(2, 64); // (*) - vec1.add(2, 65); - vec1.add(2, 130); - vec1.add(2, 160); // (*) - - vec1.add(64, 133); - - vec1.add(65, 2); - vec1.add(65, 8); - vec1.add(65, 10); // (*) - vec1.add(65, 64); // (*) - vec1.add(65, 68); - vec1.add(65, 133); - vec1.add(65, 160); // (*) - - let intersection = vec1.intersection(2, 64); - assert!(intersection.is_empty()); - - let intersection = vec1.intersection(2, 65); - assert_eq!(intersection, &[10, 64, 160]); -} diff --git a/src/lib.rs b/src/lib.rs index aed3524..1e67518 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,15 +10,26 @@ //! An implementation of union-find. See the `unify` module for more //! details. - #![cfg_attr(feature = "bench", feature(test))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate core; +extern crate alloc; -#[macro_use] +#[cfg(feature = "log")] extern crate log; #[cfg(feature = "persistent")] extern crate dogged; +macro_rules! debug { + ($($tt:tt)*) => { + #[cfg(feature = "log")] + log::debug!($($tt)*); + }; +} + pub mod snapshot_vec; pub mod undo_log; pub mod unify; diff --git a/src/snapshot_vec.rs b/src/snapshot_vec.rs index efde49a..f765b30 100644 --- a/src/snapshot_vec.rs +++ b/src/snapshot_vec.rs @@ -21,10 +21,11 @@ use self::UndoLog::*; -use std::fmt; -use std::marker::PhantomData; -use std::mem; -use std::ops; +use core::fmt; +use core::marker::PhantomData; +use core::mem; +use core::ops; +use alloc::vec::Vec; use undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; diff --git a/src/undo_log.rs b/src/undo_log.rs index 93c3803..d40e13c 100644 --- a/src/undo_log.rs +++ b/src/undo_log.rs @@ -8,6 +8,9 @@ //! Since the `*Storage` variants do not have an undo log `with_log` must be called with the //! unified log before any mutating actions. +use core::ops; +use alloc::vec::Vec; + /// A trait which allows undo actions (`T`) to be pushed which can be used to rollback actio at a /// later time if needed. /// @@ -220,7 +223,7 @@ impl VecLog { } } -impl std::ops::Index for VecLog { +impl ops::Index for VecLog { type Output = T; fn index(&self, key: usize) -> &T { &self.log[key] diff --git a/src/unify/backing_vec.rs b/src/unify/backing_vec.rs index 7c1eb2c..58622ae 100644 --- a/src/unify/backing_vec.rs +++ b/src/unify/backing_vec.rs @@ -1,8 +1,9 @@ #[cfg(feature = "persistent")] use dogged::DVec; use snapshot_vec as sv; -use std::marker::PhantomData; -use std::ops::{self, Range}; +use core::marker::PhantomData; +use core::ops::{self, Range}; +use alloc::vec::Vec; use undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; diff --git a/src/unify/mod.rs b/src/unify/mod.rs index 604f3bd..1b80e6f 100644 --- a/src/unify/mod.rs +++ b/src/unify/mod.rs @@ -31,9 +31,10 @@ //! The best way to see how it is used is to read the `tests.rs` file; //! search for e.g. `UnitKey`. -use std::fmt::Debug; -use std::marker; -use std::ops::Range; +use core::fmt::Debug; +use core::marker; +use core::ops::Range; +use alloc::vec::Vec; use snapshot_vec::{self as sv, UndoLog}; use undo_log::{UndoLogs, VecLog}; diff --git a/src/unify/tests.rs b/src/unify/tests.rs index 5665aba..0818f5f 100644 --- a/src/unify/tests.rs +++ b/src/unify/tests.rs @@ -14,9 +14,12 @@ #[cfg(feature = "bench")] extern crate test; + #[cfg(feature = "bench")] use self::test::Bencher; -use std::cmp; +use core::cmp; +use alloc::vec::Vec; +use alloc::vec; #[cfg(feature = "persistent")] use unify::Persistent; use unify::{EqUnifyValue, InPlace, InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; @@ -385,6 +388,7 @@ impl UnifyKey for OrderedKey { b: OrderedKey, b_rank: &OrderedRank, ) -> Option<(OrderedKey, OrderedKey)> { + #[cfg(feature = "std")] println!("{:?} vs {:?}", a_rank, b_rank); if a_rank > b_rank { Some((a, b)) diff --git a/tests/external_undo_log.rs b/tests/external_undo_log.rs index 2537826..c1d8bc3 100644 --- a/tests/external_undo_log.rs +++ b/tests/external_undo_log.rs @@ -1,7 +1,14 @@ -#[macro_use] +#[cfg(feature = "log")] extern crate log; extern crate ena; +macro_rules! debug { + ($($tt:tt)*) => { + #[cfg(feature = "log")] + log::debug!($($tt)*); + }; +} + use ena::{ snapshot_vec as sv, undo_log::{Rollback, Snapshots, UndoLogs},