@@ -84,13 +84,129 @@ impl<const N: usize> Fingerprint<N> {
8484 out
8585 }
8686
87+ /// Get a specific bit (0-indexed).
88+ #[ inline]
89+ pub fn get_bit ( & self , index : usize ) -> bool {
90+ debug_assert ! ( index < Self :: BITS ) ;
91+ let word_idx = index / 64 ;
92+ let bit_idx = index % 64 ;
93+ ( self . words [ word_idx] >> bit_idx) & 1 == 1
94+ }
95+
96+ /// Set a specific bit.
97+ #[ inline]
98+ pub fn set_bit ( & mut self , index : usize , value : bool ) {
99+ debug_assert ! ( index < Self :: BITS ) ;
100+ let word_idx = index / 64 ;
101+ let bit_idx = index % 64 ;
102+ if value {
103+ self . words [ word_idx] |= 1u64 << bit_idx;
104+ } else {
105+ self . words [ word_idx] &= !( 1u64 << bit_idx) ;
106+ }
107+ }
108+
109+ /// Toggle a specific bit.
110+ #[ inline]
111+ pub fn toggle_bit ( & mut self , index : usize ) {
112+ debug_assert ! ( index < Self :: BITS ) ;
113+ let word_idx = index / 64 ;
114+ let bit_idx = index % 64 ;
115+ self . words [ word_idx] ^= 1u64 << bit_idx;
116+ }
117+
118+ /// Create a random fingerprint from a seed (xorshift128+).
119+ pub fn random ( seed : u64 ) -> Self {
120+ let mut s0 = seed;
121+ let mut s1 = seed. wrapping_mul ( 0x9E3779B97F4A7C15 ) ;
122+ let mut words = [ 0u64 ; N ] ;
123+ for word in & mut words {
124+ let mut s = s0;
125+ s0 = s1;
126+ s ^= s << 23 ;
127+ s ^= s >> 18 ;
128+ s ^= s1;
129+ s ^= s1 >> 5 ;
130+ s1 = s;
131+ * word = s0. wrapping_add ( s1) ;
132+ }
133+ Self { words }
134+ }
135+
87136 /// Hamming distance (number of differing bits).
88137 /// Delegates to ndarray's SIMD dispatch (AVX-512 → AVX2 → scalar).
89138 #[ inline]
90139 pub fn hamming_distance ( & self , other : & Self ) -> u32 {
91140 super :: bitwise:: hamming_distance_raw ( self . as_bytes ( ) , other. as_bytes ( ) ) as u32
92141 }
93142
143+ /// Alias for `hamming_distance` (ladybug-rs compat).
144+ #[ inline]
145+ pub fn hamming ( & self , other : & Self ) -> u32 {
146+ self . hamming_distance ( other)
147+ }
148+
149+ /// XOR bind (ladybug-rs compat). Returns a new fingerprint.
150+ #[ inline]
151+ pub fn bind ( & self , other : & Self ) -> Self {
152+ let mut words = [ 0u64 ; N ] ;
153+ for i in 0 ..N { words[ i] = self . words [ i] ^ other. words [ i] ; }
154+ Self { words }
155+ }
156+
157+ /// AND (bitwise intersection).
158+ #[ inline]
159+ pub fn and ( & self , other : & Self ) -> Self {
160+ let mut words = [ 0u64 ; N ] ;
161+ for i in 0 ..N { words[ i] = self . words [ i] & other. words [ i] ; }
162+ Self { words }
163+ }
164+
165+ /// Bitwise NOT.
166+ #[ inline]
167+ pub fn not ( & self ) -> Self {
168+ let mut words = [ 0u64 ; N ] ;
169+ for i in 0 ..N { words[ i] = !self . words [ i] ; }
170+ Self { words }
171+ }
172+
173+ /// Density: fraction of set bits (popcount / total bits).
174+ #[ inline]
175+ pub fn density ( & self ) -> f32 {
176+ self . popcount ( ) as f32 / Self :: BITS as f32
177+ }
178+
179+ /// Access raw words as slice.
180+ #[ inline]
181+ pub fn as_raw ( & self ) -> & [ u64 ; N ] {
182+ & self . words
183+ }
184+
185+ /// Create from content string (SHA-256-like hash expansion).
186+ pub fn from_content ( data : & str ) -> Self {
187+ let mut h = 0x736f6d6570736575u64 ;
188+ for ( i, b) in data. bytes ( ) . enumerate ( ) {
189+ h ^= ( b as u64 ) << ( ( i % 8 ) * 8 ) ;
190+ h = h. rotate_left ( 13 ) . wrapping_mul ( 5 ) . wrapping_add ( 0xe6546b64 ) ;
191+ }
192+ Self :: random ( h)
193+ }
194+
195+ /// Permute: circular bit shift by `positions` (positive = left).
196+ pub fn permute ( & self , positions : i32 ) -> Self {
197+ let total = Self :: BITS as i32 ;
198+ let shift = ( ( positions % total) + total) % total;
199+ if shift == 0 { return self . clone ( ) ; }
200+ let mut result = Self :: zero ( ) ;
201+ for i in 0 ..Self :: BITS {
202+ if self . get_bit ( i) {
203+ let new_pos = ( ( i as i32 + shift) % total) as usize ;
204+ result. set_bit ( new_pos, true ) ;
205+ }
206+ }
207+ result
208+ }
209+
94210 /// Hamming weight (number of set bits).
95211 #[ inline]
96212 pub fn popcount ( & self ) -> u32 {
@@ -265,6 +381,71 @@ pub type Fingerprint1K = Fingerprint<128>;
265381/// 64K-bit fingerprint (recognition projections).
266382pub type Fingerprint64K = Fingerprint < 1024 > ;
267383
384+ // ─── Vector width config (LazyLock, switchable) ─────────────────
385+
386+ use std:: sync:: LazyLock ;
387+
388+ /// Supported vector widths for the BindSpace substrate.
389+ ///
390+ /// NOTE: 4096 is NOT a vector width — it's the 0xFFF schema/command address
391+ /// space (4096 CAM operations, verb vocabulary). Vectors are 8K or 16K.
392+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
393+ #[ repr( u16 ) ]
394+ pub enum VectorWidth {
395+ /// 8,192 bits = 128 words = 1 KB. Deprecated, still referenced in some code.
396+ W8K = 128 ,
397+ /// 16,384 bits = 256 words = 2 KB. Production default.
398+ W16K = 256 ,
399+ }
400+
401+ /// Runtime vector configuration. Frozen on first access.
402+ ///
403+ /// Like `simd_caps()` — detect once, read everywhere.
404+ /// Controls serialization format, network protocol, and storage layout.
405+ /// Does NOT change the Rust type (use the matching Fingerprint\<N\> alias).
406+ #[ derive( Clone , Copy , Debug ) ]
407+ pub struct VectorConfig {
408+ pub width : VectorWidth ,
409+ pub words : usize ,
410+ pub bits : usize ,
411+ pub bytes : usize ,
412+ }
413+
414+ impl VectorConfig {
415+ const fn from_width ( w : VectorWidth ) -> Self {
416+ let words = w as usize ;
417+ VectorConfig { width : w, words, bits : words * 64 , bytes : words * 8 }
418+ }
419+ }
420+
421+ static VECTOR_WIDTH : LazyLock < VectorConfig > = LazyLock :: new ( || {
422+ let w = std:: env:: var ( "NDARRAY_VECTOR_WIDTH" )
423+ . ok ( )
424+ . and_then ( |s| match s. as_str ( ) {
425+ "8192" | "8k" | "8K" => Some ( VectorWidth :: W8K ) ,
426+ "16384" | "16k" | "16K" => Some ( VectorWidth :: W16K ) ,
427+ _ => None ,
428+ } )
429+ . unwrap_or ( VectorWidth :: W16K ) ;
430+ VectorConfig :: from_width ( w)
431+ } ) ;
432+
433+ /// Get the frozen vector width configuration.
434+ ///
435+ /// Defaults to 16K (production). Override with `NDARRAY_VECTOR_WIDTH=8192`
436+ /// env var before first access. After first call, width is frozen.
437+ ///
438+ /// ```
439+ /// use ndarray::hpc::fingerprint::vector_config;
440+ /// let cfg = vector_config();
441+ /// assert_eq!(cfg.bits, 16_384); // default
442+ /// assert_eq!(cfg.words, 256);
443+ /// assert_eq!(cfg.bytes, 2_048);
444+ /// ```
445+ pub fn vector_config ( ) -> & ' static VectorConfig {
446+ & VECTOR_WIDTH
447+ }
448+
268449#[ cfg( test) ]
269450mod tests {
270451 use super :: * ;
0 commit comments