Skip to main content

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}