m4ri_sys/mzd.rs
1//! Links to mzd.h
2//!
3//! FIXME implement missing functions
4//!
5use libc;
6use std::mem::size_of;
7
8use crate::misc::m4ri_one;
9use crate::misc::m4ri_radix;
10use crate::misc::Rci;
11use crate::misc::Wi;
12use crate::misc::Word;
13use crate::misc::BIT;
14
15/// Represents the blocks used by M4RI internally
16#[repr(C)]
17struct MzdBlock {
18 size: libc::size_t,
19 begin: *mut Word,
20 end: *mut Word,
21}
22
23/// Represents the Mzd data type used by M4RI
24#[repr(C)]
25pub struct Mzd {
26 /// Number of rows
27 pub nrows: Rci,
28 /// Number of columns
29 pub ncols: Rci,
30 /// Number of words with valid bits:
31 /// width = ceil(ncols / m4ri_radix)
32 pub width: Wi,
33
34 /// Offset in words between rows
35 /// ``
36 /// rowstride = (width < mzd_paddingwidth || (width & 1) == 0) ? width : width + 1;
37 /// ``
38 /// where width is the width of the underlying non-windowed matrix
39 rowstride: Wi,
40
41 /// Offset in words from start of block to first word
42 ///
43 /// ``rows[0] = blocks[0].begin + offset_vector``
44 offset_vector: Wi,
45
46 /// Number of rows to the first row counting from the start of the
47 /// first block
48 row_offset: Wi,
49
50 /// Booleans to speed up things
51 ///
52 /// The bits have the following meaning
53 ///
54 /// 1: Has non-zero excess
55 /// 2. Is windowed, but has zero offset
56 /// 3. Is windowed, but has zero excess
57 /// 4. Is windowed, but owns the Blocks allocations
58 /// 5. Spans more than 1 Block
59 flags: u8,
60
61 /// blockrows_log = log2(blockrows)
62 /// where blockrows is the number of rows in one block,
63 /// which is a power of 2.
64 blockrows_log: u8,
65
66 // Ensures sizeof(mzd_t) == 64
67 padding: [u8; 62
68 - 2 * size_of::<Rci>()
69 - 4 * size_of::<Wi>()
70 - size_of::<Word>()
71 - 2 * size_of::<*const libc::c_void>()],
72
73 /// Mask for valid bits in the word with the highest index (width - 1)
74 high_bitmask: Word,
75 /// Pointers to the actual blocks of memory containing the values packed into words
76 blocks: *const MzdBlock,
77 /// Address of first word in each row, so the first word of `row [i]` is in `m->rows[i]`
78 pub rows: *const *mut Word,
79}
80
81/// Flag when `ncols%64 == 0`
82pub static MZD_FLAG_NONZERO_EXCESS: u8 = 0x2;
83
84/// Flag for windowed matrix
85pub static MZD_FLAG_WINDOWED_ZEROOFFSET: u8 = 0x4;
86
87/// Flag for windowed matrix where `ncols % 64 == 0`
88pub static MZD_FLAG_WINDOWED_ZEROEXCESS: u8 = 0x8;
89
90/// Flag for windowed matrix which owns its memory
91pub static MZD_FLAG_WINDOWED_OWNSBLOCKS: u8 = 0x10;
92
93/// Flag for multiple blocks
94pub static MZD_FLAG_MULTIPLE_BLOCKS: u8 = 0x20;
95
96extern "C" {
97 /// Create a new rows x columns matrix
98 pub fn mzd_init(rows: Rci, columns: Rci) -> *mut Mzd;
99
100 /// Free a matrix created with mzd_init.
101 /// Automatically done by the Deref trait on Mzd
102 pub fn mzd_free(matrix: *mut Mzd);
103
104 /// \brief Create a window/view into the matrix M.
105 ///
106 /// A matrix window for M is a meta structure on the matrix M. It i
107 /// setup to point into the matrix so M \em must \em not be freed while the
108 /// matrix window is used.
109 ///
110 /// This function puts the restriction on the provided parameters that
111 /// all parameters must be within range for M which is not enforced
112 /// currently.
113 ///
114 /// Use mzd_free_window to free the window.
115 ///
116 /// \param M Matrix
117 /// \param lowr Starting row (inclusive)
118 /// \param lowc Starting column (inclusive, **must be multiple of m4ri_radix**)
119 /// \param highr End row (exclusive)
120 /// \param highc End column (exclusive)
121 pub fn mzd_init_window(
122 matrix: *mut Mzd,
123 lowr: Rci,
124 lowc: Rci,
125 highr: Rci,
126 highc: Rci,
127 ) -> *mut Mzd;
128
129 /// Swap the two rows rowa and rowb
130 pub fn mzd_row_swap(matrix: *mut Mzd, rowa: Rci, rowb: Rci);
131
132 /// \brief copy row j from A to row i from B.
133 ///
134 /// The offsets of A and B must match and the number of columns of A
135 /// must be less than or equal to the number of columns of B.
136 ///
137 /// \param B Target matrix.
138 /// \param i Target row index.
139 /// \param A Source matrix.
140 /// \param j Source row index.
141 pub fn mzd_copy_row(b: *mut Mzd, i: Rci, a: *const Mzd, j: Rci);
142
143 /// Swap the two columns cola and colb
144 pub fn mzd_col_swap(matrix: *mut Mzd, cola: Rci, colb: Rci);
145
146 /// Transpose a matrix
147 /// Dest may be null for automatic creation
148 pub fn mzd_transpose(dest: *mut Mzd, source: *const Mzd) -> *mut Mzd;
149
150 /// naive cubic matrix multiplication
151 /// the first argument may be null for automatic creation
152 pub fn mzd_mul_naive(dest: *mut Mzd, a: *const Mzd, b: *const Mzd) -> *mut Mzd;
153
154 ///
155 /// brief Naive cubic matrix multiplication with the pre-transposed B.
156 ///
157 /// That is, compute C such that C == AB^t.
158 ///
159 /// param C Preallocated product matrix.
160 /// param A Input matrix A.
161 /// param B Pre-transposed input matrix B.
162 /// param clear Whether to clear C before accumulating AB
163 pub fn _mzd_mul_naive(
164 dest: *mut Mzd,
165 a: *const Mzd,
166 b: *const Mzd,
167 clear: libc::c_int,
168 ) -> *mut Mzd;
169
170 /// naive cubic matrix multiplication and addition
171 ///
172 /// C == C + AB
173 pub fn mzd_addmul_naive(c: *mut Mzd, a: *const Mzd, b: *const Mzd) -> *mut Mzd;
174
175 /// Matrix multiplication optimized for v*A where v is a vector
176 ///
177 /// param C: preallocated product matrix
178 /// param v: input matrix v
179 /// param A: input matrix A
180 /// param clear: if set clear C first, otherwise add result to C
181 pub fn _mzd_mul_va(c: *mut Mzd, v: *const Mzd, a: *const Mzd, clear: libc::c_int) -> *mut Mzd;
182
183 /// Fill the matrix m with uniformly distributed bits.
184 pub fn mzd_randomize(m: *mut Mzd);
185
186 /// Return true if A == B
187 pub fn mzd_equal(a: *const Mzd, b: *const Mzd) -> libc::c_int;
188
189 /// Copy a matrix to dest
190 ///
191 /// Dest may be null for automatic creation
192 pub fn mzd_copy(dest: *mut Mzd, src: *const Mzd) -> *mut Mzd;
193
194 /// Concatenate B to A and write the result to C
195 pub fn mzd_concat(c: *mut Mzd, a: *const Mzd, b: *const Mzd) -> *mut Mzd;
196
197 /// Set to identity matrix if the second argument is 1
198 pub fn mzd_set_ui(a: *mut Mzd, n: libc::c_uint);
199
200 /// Stack A on top of B into C
201 pub fn mzd_stack(c: *mut Mzd, a: *mut Mzd, b: *const Mzd) -> *mut Mzd;
202
203 /// Copy a submatrix
204 /// first argument may be preallocated space or null
205 pub fn mzd_submatrix(
206 s: *mut Mzd,
207 m: *const Mzd,
208 lowr: Rci,
209 lowc: Rci,
210 highr: Rci,
211 highc: Rci,
212 ) -> *mut Mzd;
213
214 /// Invert the target matrix using gaussian elimination
215 /// To avoid recomputing the identity matrix over and over again,
216 /// I may be passed in as identity parameter
217 /// The first parameter may be null to have the space automatically allocated
218 pub fn mzd_invert_naive(inv: *mut Mzd, a: *const Mzd, identity: *const Mzd) -> *mut Mzd;
219
220 /// Set C = A + B
221 /// If C is passed in, the result is written there
222 /// otherwise a new matrix is created
223 pub fn mzd_add(c: *mut Mzd, a: *const Mzd, b: *const Mzd) -> *mut Mzd;
224
225 /// Set C = A - B
226 /// If C is passed in, the result is written there
227 /// otherwise a new matrix is created
228 ///
229 /// Secretly an alias for mzd_add
230 pub fn mzd_sub(c: *mut Mzd, a: *const Mzd, b: *const Mzd) -> *mut Mzd;
231
232 /// Zero test for matrix
233 pub fn mzd_is_zero(a: *const Mzd);
234
235 /// Clear the given row, but only begins at the column coloffset.
236 ///
237 /// param M Matrix
238 /// param row Index of row
239 /// param coloffset Column offset
240 pub fn mzd_row_clear_offset(m: *mut Mzd, row: Rci, coloffset: Rci);
241
242}
243
244/// Write the bit to position M[row, col]
245#[inline]
246pub unsafe fn mzd_write_bit(matrix: *mut Mzd, row: Rci, col: Rci, value: BIT) {
247 let therow: *const *mut Word = (*matrix).rows.offset(row as isize);
248 let column: *mut Word = (*therow).offset((col / m4ri_radix) as isize);
249 let pos = col % m4ri_radix;
250 let column_bitmasked: Word = *column & !(m4ri_one << pos);
251 let column_newbit: Word = (value as Word & m4ri_one) << pos;
252 debug_assert_eq!(column_newbit.count_ones(), value as u32);
253 *column = column_bitmasked | column_newbit;
254}
255
256/// Read the bit at position M[row, col]
257///
258/// # Unsafe behaviour
259/// No bounds checking
260///
261/// Reimplemented in Rust as the C library declares it as inline
262#[inline]
263pub unsafe fn mzd_read_bit(matrix: *const Mzd, row: Rci, col: Rci) -> BIT {
264 let therow: *const *mut Word = (*matrix).rows.offset(row as isize);
265 let column: Word = *(*therow).offset((col / m4ri_radix) as isize);
266
267 let thebit = (column >> (col % m4ri_radix)) & m4ri_one;
268 thebit as BIT
269}
270
271/// Get a pointer to the first word
272///
273/// param: M: matrix
274///
275/// Return a pointer to the first word of the first row
276#[inline]
277pub unsafe fn mzd_first_row(matrix: *const Mzd) -> *mut Word {
278 let result: *mut Word = (*(*matrix).blocks)
279 .begin
280 .offset((*matrix).offset_vector as isize);
281 debug_assert!(
282 (*matrix).nrows == 0 || result == *(*matrix).rows,
283 "Result is not the expected ptr"
284 );
285 result
286}
287
288/// Get pointer to first word of row
289///
290/// Param M Matrix
291/// Param row the row index
292#[inline]
293pub unsafe fn mzd_row(matrix: *const Mzd, row: Rci) -> *mut Word {
294 debug_assert!(row >= 0);
295 let big_vector: Wi = (*matrix).offset_vector + row * (*matrix).rowstride;
296 // FIXME __M4RI_UNLIKELY -> _builtin_expect
297 let result: *mut Word = if (*matrix).flags & MZD_FLAG_MULTIPLE_BLOCKS != 0 {
298 let n = ((*matrix).row_offset + row) >> (*matrix).blockrows_log;
299 (*(*matrix).blocks.add(n as usize)).begin.offset(
300 (big_vector - n * ((*(*matrix).blocks).size / ::std::mem::size_of::<Word>()) as i32)
301 as isize,
302 )
303 } else {
304 (*(*matrix).blocks).begin.add(big_vector as usize)
305 };
306
307 debug_assert_eq!(
308 result,
309 *(*matrix).rows.add(row as usize),
310 "Result is not the expected ptr"
311 );
312 result
313}
314
315/// Create a const window/view into a const matrix
316///
317/// Note that this function still allocates a new Mzd struct that needs to be dropped.
318/// You **must** call ``mzd_free_window_const``.
319///
320/// Also, this function copies a bunch of references
321/// As a result, you could get multiple mut references into the same memory.
322/// This is very unsafe if not used properly.
323pub unsafe fn mzd_init_window_const(
324 matrix: *const Mzd,
325 lowr: Rci,
326 lowc: Rci,
327 highr: Rci,
328 highc: Rci,
329) -> *const Mzd {
330 mzd_init_window(std::mem::transmute(matrix), lowr, lowc, highr, highc)
331}
332
333/// Test if a matrix is windowed
334///
335/// return a non-zero value if the matrix is windowed, otherwise return zero
336#[inline]
337pub unsafe fn mzd_is_windowed(m: *const Mzd) -> u8 {
338 (*m).flags & MZD_FLAG_WINDOWED_ZEROOFFSET
339}
340
341/// Test if this mzd_t should free blocks
342#[inline]
343pub unsafe fn mzd_owns_blocks(m: *const Mzd) -> bool {
344 !(*m).blocks.is_null()
345 && (mzd_is_windowed(m) == 0 || ((*m).flags & MZD_FLAG_WINDOWED_OWNSBLOCKS != 0))
346}
347
348impl Drop for Mzd {
349 #[inline]
350 fn drop(&mut self) {
351 unsafe {
352 mzd_free(self);
353 }
354 }
355}
356
357/// Free a matrix window created with mzd_init_window
358///
359/// This is actually just `mzd_free` so call `ptr::drop_in_place` instead
360#[inline]
361pub unsafe fn mzd_free_window(matrix: *mut Mzd) {
362 std::ptr::drop_in_place(matrix)
363}
364
365/// Free a "const" window created with `mzd_init_window_const`.
366///
367/// This function *MUST* be called for const windows.
368#[inline]
369pub unsafe fn mzd_free_window_const(matrix: *const Mzd) {
370 let matrix: *mut Mzd = std::mem::transmute(matrix);
371 std::ptr::drop_in_place(matrix)
372}
373
374#[cfg(test)]
375mod test {
376 use super::*;
377 use std::mem;
378 use std::ptr;
379
380 #[test]
381 fn init() {
382 for _ in 0..100 {
383 let result: libc::c_int;
384 unsafe {
385 assert_eq!(mem::size_of::<Mzd>(), 64);
386 let matrix = mzd_init(10, 10);
387 assert!(!(*matrix).blocks.is_null());
388 assert!(!(*matrix).rows.is_null());
389 mzd_randomize(matrix);
390 result = mzd_equal(matrix, matrix);
391 assert_eq!(result, 1);
392 let m2 = mzd_copy(ptr::null_mut(), matrix);
393 mzd_randomize(m2);
394 assert_eq!(mzd_equal(m2, matrix), 0);
395 ptr::drop_in_place(matrix);
396 ptr::drop_in_place(m2);
397 }
398 }
399 }
400 #[test]
401 fn test_mzd_first_row() {
402 for _ in 0..100 {
403 unsafe {
404 let matrix = mzd_init(10, 10);
405 mzd_set_ui(matrix, 0);
406 mzd_first_row(matrix);
407 ptr::drop_in_place(matrix);
408 }
409 }
410 }
411
412 #[test]
413 fn test_mzd_row() {
414 for _ in 0..100 {
415 unsafe {
416 let matrix = mzd_init(10, 10);
417 mzd_set_ui(matrix, 0);
418 mzd_row(matrix, 5);
419 ptr::drop_in_place(matrix);
420 }
421 }
422 }
423
424 #[test]
425 fn test_mzd_read_bit() {
426 for _ in 0..10 {
427 unsafe {
428 let matrix = mzd_init(1000, 1000);
429 mzd_set_ui(matrix, 1);
430 for i in 0..1000 {
431 for j in 0..1000 {
432 let bit = mzd_read_bit(matrix, i as Rci, j as Rci);
433 assert_eq!(bit == 1, i == j, "Should be unit matrix");
434 }
435 }
436 ptr::drop_in_place(matrix);
437 }
438 }
439 }
440
441 #[test]
442 fn test_mzd_write_bit() {
443 for _ in 0..10 {
444 unsafe {
445 let matrix = mzd_init(1000, 1000);
446 for i in 0..1000 {
447 for j in 0..1000 {
448 mzd_write_bit(matrix, i as Rci, j as Rci, if i == j { 1 } else { 0 });
449 }
450 }
451 for i in 0..1000 {
452 for j in 0..1000 {
453 let bit = mzd_read_bit(matrix, i as Rci, j as Rci);
454 assert_eq!(bit == 1, i == j, "Should be unit matrix");
455 }
456 }
457 ptr::drop_in_place(matrix);
458 }
459 }
460 }
461}