@@ -21,7 +21,7 @@ use super::{
2121 BitIter , DenseBitSet , WORD_BITS , Word , bitwise, clear_excess_bits_in_final_word, count_ones,
2222 num_words, word_index_and_mask,
2323} ;
24- use crate :: id:: Id ;
24+ use crate :: id:: { Id , IdVec } ;
2525
2626#[ cfg( test) ]
2727mod tests;
@@ -38,8 +38,8 @@ mod tests;
3838///
3939/// Obtained via [`BitMatrix::row`] or [`SparseBitMatrix::row`].
4040#[ derive( Clone , Copy ) ]
41- pub struct RowRef < ' a , C > {
42- words : & ' a [ Word ] ,
41+ pub struct RowRef < ' set , C > {
42+ words : & ' set [ Word ] ,
4343 col_domain_size : usize ,
4444 marker : PhantomData < C > ,
4545}
@@ -130,8 +130,8 @@ impl<C: Id> fmt::Debug for RowRef<'_, C> {
130130 }
131131}
132132
133- impl < ' a , C : Id > IntoIterator for & RowRef < ' a , C > {
134- type IntoIter = BitIter < ' a , C > ;
133+ impl < ' set , C : Id > IntoIterator for & RowRef < ' set , C > {
134+ type IntoIter = BitIter < ' set , C > ;
135135 type Item = C ;
136136
137137 #[ inline]
@@ -161,15 +161,15 @@ impl<'a, C: Id> IntoIterator for RowRef<'a, C> {
161161/// operations against other rows or [`DenseBitSet`]s.
162162///
163163/// Obtained via [`BitMatrix::row_mut`].
164- pub struct RowMut < ' a , C > {
165- words : & ' a mut [ Word ] ,
164+ pub struct RowMut < ' set , C > {
165+ words : & ' set mut [ Word ] ,
166166 col_domain_size : usize ,
167167 marker : PhantomData < C > ,
168168}
169169
170- impl < ' a , C : Id > RowMut < ' a , C > {
170+ impl < ' set , C : Id > RowMut < ' set , C > {
171171 #[ inline]
172- const fn new ( words : & ' a mut [ Word ] , col_domain_size : usize ) -> Self {
172+ const fn new ( words : & ' set mut [ Word ] , col_domain_size : usize ) -> Self {
173173 Self {
174174 words,
175175 col_domain_size,
@@ -312,8 +312,8 @@ impl<C: Id> fmt::Debug for RowMut<'_, C> {
312312 }
313313}
314314
315- impl < ' a , C : Id > IntoIterator for & ' a RowMut < ' a , C > {
316- type IntoIter = BitIter < ' a , C > ;
315+ impl < ' row , C : Id > IntoIterator for & ' row RowMut < ' _ , C > {
316+ type IntoIter = BitIter < ' row , C > ;
317317 type Item = C ;
318318
319319 #[ inline]
@@ -351,7 +351,7 @@ pub struct BitMatrix<R, C, A: Allocator = Global> {
351351 row_domain_size : usize ,
352352 col_domain_size : usize ,
353353 words : Vec < Word , A > ,
354- marker : PhantomData < fn ( R , C ) > ,
354+ marker : PhantomData < ( R , C ) > ,
355355}
356356
357357impl < R : Id , C : Id > BitMatrix < R , C > {
@@ -685,9 +685,13 @@ impl<T: Id, A: Allocator> BitMatrix<T, T, A> {
685685 ///
686686 /// Runs in O(n³/w) time where n is the domain size and w is the word size (64),
687687 /// achieving a 64× speedup over the scalar version through bitwise parallelism.
688+ ///
689+ /// # Panics
690+ ///
691+ /// If the matrix is not square.
688692 pub fn transitive_closure ( & mut self ) {
693+ assert_eq ! ( self . row_domain_size, self . col_domain_size) ;
689694 let size = self . row_domain_size ;
690- debug_assert_eq ! ( size, self . col_domain_size) ;
691695
692696 for pivot in 0 ..size {
693697 let pivot_id = T :: from_usize ( pivot) ;
@@ -773,18 +777,17 @@ struct RowSlot {
773777#[ derive( Clone ) ]
774778pub struct SparseBitMatrix < R , C , A : Allocator = Global > {
775779 col_domain_size : usize ,
776- words_per_row : usize ,
777780
778781 /// Shared backing buffer for all row data.
779782 backing : Vec < Word , A > ,
780783
781784 /// Maps row index → slot (or `None` if unallocated).
782- index : Vec < Option < RowSlot > , A > ,
785+ index : IdVec < R , Option < RowSlot > , A > ,
783786
784787 /// Free-list of word offsets for reuse by newly activated rows.
785788 free_slots : Vec < u32 , A > ,
786789
787- marker : PhantomData < fn ( R , C ) > ,
790+ marker : PhantomData < ( R , C ) > ,
788791}
789792
790793impl < R : Id , C : Id > SparseBitMatrix < R , C > {
@@ -804,64 +807,62 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
804807 where
805808 A : Clone ,
806809 {
807- let words_per_row = num_words ( col_domain_size) ;
808-
809810 Self {
810811 col_domain_size,
811- words_per_row,
812812 backing : Vec :: new_in ( alloc. clone ( ) ) ,
813- index : Vec :: new_in ( alloc. clone ( ) ) ,
813+ index : IdVec :: new_in ( alloc. clone ( ) ) ,
814814 free_slots : Vec :: new_in ( alloc) ,
815815 marker : PhantomData ,
816816 }
817817 }
818818
819+ #[ inline]
820+ const fn words_per_row ( & self ) -> usize {
821+ num_words ( self . col_domain_size )
822+ }
823+
819824 /// Ensures `row` has an allocated slot, returning the word offset.
820825 #[ inline]
821826 fn ensure_row ( & mut self , row : R ) -> u32 {
822- let row_idx = row. as_usize ( ) ;
823-
824- // Extend the index if needed.
825- if row_idx >= self . index . len ( ) {
826- self . index . resize ( row_idx + 1 , None ) ;
827- }
828-
829- if let Some ( slot) = self . index [ row_idx] {
827+ if let Some ( slot) = self . index . lookup ( row) {
830828 return slot. offset ;
831829 }
832830
833831 // Allocate from free-list or extend backing.
834832 let offset = if let Some ( offset) = self . free_slots . pop ( ) {
835833 // Reuse a freed slot — zero it.
836834 let start = offset as usize ;
837- self . backing [ start..start + self . words_per_row ] . fill ( 0 ) ;
835+ let words_per_row = self . words_per_row ( ) ;
836+ self . backing [ start..start + words_per_row] . fill ( 0 ) ;
838837 offset
839838 } else {
840839 debug_assert ! ( u32 :: try_from( self . backing. len( ) ) . is_ok( ) ) ;
840+
841841 let offset = self . backing . len ( ) as u32 ;
842842 self . backing
843- . resize ( self . backing . len ( ) + self . words_per_row , 0 ) ;
843+ . resize ( self . backing . len ( ) + self . words_per_row ( ) , 0 ) ;
844844 offset
845845 } ;
846846
847- self . index [ row_idx ] = Some ( RowSlot { offset } ) ;
847+ self . index . insert ( row , RowSlot { offset } ) ;
848848 offset
849849 }
850850
851851 /// Returns the word slice for an allocated row, or `None`.
852852 #[ inline]
853853 fn row_words ( & self , row : R ) -> Option < & [ Word ] > {
854- let slot = self . index . get ( row. as_usize ( ) ) ? . as_ref ( ) ?;
854+ let slot = self . index . lookup ( row) ?;
855855 let start = slot. offset as usize ;
856- Some ( & self . backing [ start..start + self . words_per_row ] )
856+ Some ( & self . backing [ start..start + self . words_per_row ( ) ] )
857857 }
858858
859859 /// Returns the mutable word slice for an allocated row, or `None`.
860860 #[ inline]
861861 fn row_words_mut ( & mut self , row : R ) -> Option < & mut [ Word ] > {
862- let slot = self . index . get ( row. as_usize ( ) ) ? . as_ref ( ) ?;
862+ let slot = self . index . lookup ( row) ?;
863863 let start = slot. offset as usize ;
864- Some ( & mut self . backing [ start..start + self . words_per_row ] )
864+ let words_per_row = self . words_per_row ( ) ;
865+ Some ( & mut self . backing [ start..start + words_per_row] )
865866 }
866867
867868 /// Returns an immutable view of `row`, or `None` if the row is unallocated.
@@ -937,10 +938,9 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
937938 /// Clears all bits in `row` and returns its slot to the free-list.
938939 #[ inline]
939940 pub fn clear_row ( & mut self , row : R ) {
940- let row_idx = row. as_usize ( ) ;
941- if let Some ( Some ( slot) ) = self . index . get ( row_idx) {
941+ if let Some ( slot) = self . index . lookup ( row) {
942942 self . free_slots . push ( slot. offset ) ;
943- self . index [ row_idx ] = None ;
943+ self . index . remove ( row ) ;
944944 }
945945 }
946946
@@ -958,7 +958,8 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
958958 #[ inline]
959959 pub fn insert_all_into_row ( & mut self , row : R ) {
960960 let offset = self . ensure_row ( row) as usize ;
961- let words = & mut self . backing [ offset..offset + self . words_per_row ] ;
961+ let words_per_row = self . words_per_row ( ) ;
962+ let words = & mut self . backing [ offset..offset + words_per_row] ;
962963 words. fill ( !0 ) ;
963964 clear_excess_bits_in_final_word ( self . col_domain_size , words) ;
964965 }
@@ -974,18 +975,14 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
974975 return false ;
975976 }
976977
977- let Some ( read_offset) = self
978- . index
979- . get ( read. as_usize ( ) )
980- . and_then ( |slot| slot. map ( |slot| slot. offset as usize ) )
981- else {
978+ let Some ( read_offset) = self . index . lookup ( read) . map ( |slot| slot. offset as usize ) else {
982979 return false ;
983980 } ;
984981
985982 let write_offset = self . ensure_row ( write) as usize ;
986983
987984 let mut changed: Word = 0 ;
988- for offset in 0 ..self . words_per_row {
985+ for offset in 0 ..self . words_per_row ( ) {
989986 let old = self . backing [ write_offset + offset] ;
990987 let new = old | self . backing [ read_offset + offset] ;
991988 self . backing [ write_offset + offset] = new;
@@ -1006,18 +1003,14 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10061003 }
10071004
10081005 let ( Some ( read_offset) , Some ( write_offset) ) = (
1009- self . index
1010- . get ( read. as_usize ( ) )
1011- . and_then ( |slot| slot. map ( |slot| slot. offset as usize ) ) ,
1012- self . index
1013- . get ( write. as_usize ( ) )
1014- . and_then ( |slot| slot. map ( |slot| slot. offset as usize ) ) ,
1006+ self . index . lookup ( read) . map ( |slot| slot. offset as usize ) ,
1007+ self . index . lookup ( write) . map ( |slot| slot. offset as usize ) ,
10151008 ) else {
10161009 return false ;
10171010 } ;
10181011
10191012 let mut changed: Word = 0 ;
1020- for offset in 0 ..self . words_per_row {
1013+ for offset in 0 ..self . words_per_row ( ) {
10211014 let old = self . backing [ write_offset + offset] ;
10221015 let new = old & !self . backing [ read_offset + offset] ;
10231016 self . backing [ write_offset + offset] = new;
@@ -1035,19 +1028,11 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10351028 return false ;
10361029 }
10371030
1038- let Some ( write_offset) = self
1039- . index
1040- . get ( write. as_usize ( ) )
1041- . and_then ( |slot| slot. map ( |slot| slot. offset as usize ) )
1042- else {
1031+ let Some ( write_offset) = self . index . lookup ( write) . map ( |slot| slot. offset as usize ) else {
10431032 return false ;
10441033 } ;
10451034
1046- let Some ( read_offset) = self
1047- . index
1048- . get ( read. as_usize ( ) )
1049- . and_then ( |slot| slot. map ( |slot| slot. offset as usize ) )
1050- else {
1035+ let Some ( read_offset) = self . index . lookup ( read) . map ( |slot| slot. offset as usize ) else {
10511036 // read is empty → write becomes empty
10521037 let was_nonempty = self . row ( write) . is_some_and ( |row_ref| !row_ref. is_empty ( ) ) ;
10531038 if was_nonempty {
@@ -1057,7 +1042,7 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10571042 } ;
10581043
10591044 let mut changed: Word = 0 ;
1060- for offset in 0 ..self . words_per_row {
1045+ for offset in 0 ..self . words_per_row ( ) {
10611046 let old = self . backing [ write_offset + offset] ;
10621047 let new = old & self . backing [ read_offset + offset] ;
10631048 self . backing [ write_offset + offset] = new;
@@ -1073,8 +1058,10 @@ impl<R: Id, C: Id, A: Allocator> SparseBitMatrix<R, C, A> {
10731058 pub fn union_row_with ( & mut self , row : R , other : & DenseBitSet < C > ) -> bool {
10741059 debug_assert_eq ! ( other. domain_size( ) , self . col_domain_size) ;
10751060 let offset = self . ensure_row ( row) as usize ;
1061+ let words_per_row = self . words_per_row ( ) ;
1062+
10761063 bitwise (
1077- & mut self . backing [ offset..offset + self . words_per_row ] ,
1064+ & mut self . backing [ offset..offset + words_per_row] ,
10781065 & other. words ,
10791066 |lhs, rhs| lhs | rhs,
10801067 )
0 commit comments