1use super::MatQ;
12use crate::rational::Q;
13use crate::traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix};
14use flint_sys::fmpq_mat::{fmpq_mat_init_set, fmpq_mat_window_clear, fmpq_mat_window_init};
15use flint_sys::{
16 fmpq::{fmpq, fmpq_set},
17 fmpq_mat::fmpq_mat_entry,
18};
19use std::mem::MaybeUninit;
20
21impl MatrixDimensions for MatQ {
22 fn get_num_rows(&self) -> i64 {
33 self.matrix.r
34 }
35
36 fn get_num_columns(&self) -> i64 {
47 self.matrix.c
48 }
49}
50
51impl MatrixGetEntry<Q> for MatQ {
52 unsafe fn get_entry_unchecked(&self, row: i64, column: i64) -> Q {
80 let mut copy = Q::default();
81 let entry = unsafe { fmpq_mat_entry(&self.matrix, row, column) };
82 unsafe { fmpq_set(&mut copy.value, entry) };
83
84 copy
85 }
86}
87
88impl MatrixGetSubmatrix for MatQ {
89 unsafe fn get_submatrix_unchecked(
123 &self,
124 row_1: i64,
125 row_2: i64,
126 col_1: i64,
127 col_2: i64,
128 ) -> Self {
129 let mut window = MaybeUninit::uninit();
130 unsafe {
132 fmpq_mat_window_init(
133 window.as_mut_ptr(),
134 &self.matrix,
135 row_1,
136 col_1,
137 row_2,
138 col_2,
139 )
140 };
141 let mut window_copy = MaybeUninit::uninit();
142 unsafe {
143 fmpq_mat_init_set(window_copy.as_mut_ptr(), window.as_ptr());
145 fmpq_mat_window_clear(window.as_mut_ptr());
148 }
149 MatQ {
150 matrix: unsafe { window_copy.assume_init() },
151 }
152 }
153}
154
155impl MatQ {
156 pub(crate) fn collect_entries(&self) -> Vec<fmpq> {
176 let mut entries: Vec<fmpq> =
177 Vec::with_capacity((self.get_num_rows() * self.get_num_columns()) as usize);
178
179 for row in 0..self.get_num_rows() {
180 for col in 0..self.get_num_columns() {
181 let entry = unsafe { *fmpq_mat_entry(&self.matrix, row, col) };
183 entries.push(entry);
184 }
185 }
186
187 entries
188 }
189
190 pub fn collect_entries_f64(&self) -> Vec<Vec<f64>> {
211 let num_rows = self.get_num_rows() as usize;
212 let num_cols = self.get_num_columns() as usize;
213
214 let mut entries: Vec<Vec<f64>> = vec![Vec::with_capacity(num_cols); num_rows];
215
216 for (i, row) in entries.iter_mut().enumerate() {
217 for j in 0..num_cols {
218 let entry = unsafe {
220 flint_sys::fmpq::fmpq_get_d(fmpq_mat_entry(&self.matrix, i as i64, j as i64))
221 };
222 row.push(entry);
223 }
224 }
225
226 entries
227 }
228}
229
230#[cfg(test)]
231mod test_get_entry {
232 use super::Q;
233 use crate::{
234 rational::MatQ,
235 traits::{MatrixGetEntry, MatrixSetEntry},
236 };
237 use std::str::FromStr;
238
239 #[test]
241 fn max_int_positive() {
242 let mut matrix = MatQ::new(5, 10);
243 let value_1 = Q::from(i64::MAX);
244 let value_2 = Q::from((1, i64::MAX));
245 matrix.set_entry(0, 0, value_1).unwrap();
246 matrix.set_entry(1, 1, value_2).unwrap();
247
248 let entry_1 = matrix.get_entry(0, 0).unwrap();
249 let entry_2 = matrix.get_entry(1, 1).unwrap();
250
251 assert_eq!(Q::from(i64::MAX), entry_1);
252 assert_eq!(Q::from((1, i64::MAX)), entry_2);
253 }
254
255 #[test]
257 fn large_positive() {
258 let mut matrix = MatQ::new(5, 10);
259 let value_1 = Q::from(u64::MAX);
260 let value_2 = Q::from((1, u64::MAX));
261 matrix.set_entry(0, 0, value_1).unwrap();
262 matrix.set_entry(1, 1, value_2).unwrap();
263
264 let entry_1 = matrix.get_entry(0, 0).unwrap();
265 let entry_2 = matrix.get_entry(1, 1).unwrap();
266
267 assert_eq!(Q::from(u64::MAX), entry_1);
268 assert_eq!(Q::from((1, u64::MAX)), entry_2);
269 }
270
271 #[test]
273 fn max_int_negative() {
274 let mut matrix = MatQ::new(5, 10);
275 let value_1 = Q::from(i64::MIN);
276 let value_2 = Q::from((1, i64::MIN));
277 matrix.set_entry(0, 0, value_1).unwrap();
278 matrix.set_entry(1, 1, value_2).unwrap();
279
280 let entry_1 = matrix.get_entry(0, 0).unwrap();
281 let entry_2 = matrix.get_entry(1, 1).unwrap();
282
283 assert_eq!(Q::from(i64::MIN), entry_1);
284 assert_eq!(Q::from((1, i64::MIN)), entry_2);
285 }
286
287 #[test]
289 fn large_negative() {
290 let mut matrix = MatQ::new(5, 10);
291 let value_1 = format!("-{}", u64::MAX);
292 let value_2 = format!("1/-{}", u64::MAX);
293 matrix
294 .set_entry(0, 0, Q::from_str(&value_1).unwrap())
295 .unwrap();
296 matrix
297 .set_entry(1, 1, Q::from_str(&value_2).unwrap())
298 .unwrap();
299
300 let entry_1 = matrix.get_entry(0, 0).unwrap();
301 let entry_2 = matrix.get_entry(1, 1).unwrap();
302
303 assert_eq!(Q::from_str(&format!("-{}", u64::MAX)).unwrap(), entry_1);
304 assert_eq!(Q::from_str(&format!("1/-{}", u64::MAX)).unwrap(), entry_2);
305 }
306
307 #[test]
309 fn getting_at_zero() {
310 let mut matrix = MatQ::new(5, 10);
311 let value = Q::from(i64::MIN);
312 matrix.set_entry(0, 0, value).unwrap();
313
314 let entry = matrix.get_entry(0, 0).unwrap();
315
316 assert_eq!(entry, Q::from(i64::MIN));
317 }
318
319 #[test]
321 fn error_wrong_row() {
322 let matrix = MatQ::new(5, 10);
323
324 assert!(matrix.get_entry(5, 1).is_err());
325 assert!(matrix.get_entry(-6, 1).is_err());
326 }
327
328 #[test]
330 fn error_wrong_column() {
331 let matrix = MatQ::new(5, 10);
332
333 assert!(matrix.get_entry(1, 100).is_err());
334 assert!(matrix.get_entry(1, -11).is_err());
335 }
336
337 #[test]
339 fn negative_indexing() {
340 let matrix = MatQ::from_str("[[1, 2, 3],[4, 5, 6],[7, 8, 9]]").unwrap();
341
342 assert_eq!(matrix.get_entry(-1, -1).unwrap(), Q::from(9));
343 assert_eq!(matrix.get_entry(-1, -2).unwrap(), Q::from(8));
344 assert_eq!(matrix.get_entry(-3, -3).unwrap(), Q::from(1));
345 }
346
347 #[test]
349 fn memory_test() {
350 let mut matrix = MatQ::new(5, 10);
351 let value = Q::from(u64::MAX);
352 matrix.set_entry(1, 1, value).unwrap();
353 let entry = matrix.get_entry(1, 1).unwrap();
354 matrix.set_entry(1, 1, Q::ZERO).unwrap();
355
356 assert_eq!(Q::from(u64::MAX), entry);
357 }
358}
359
360#[cfg(test)]
361mod test_get_num {
362 use crate::{rational::MatQ, traits::MatrixDimensions};
363
364 #[test]
366 fn num_rows() {
367 let matrix = MatQ::new(5, 10);
368
369 assert_eq!(matrix.get_num_rows(), 5);
370 }
371
372 #[test]
374 fn num_columns() {
375 let matrix = MatQ::new(5, 10);
376
377 assert_eq!(matrix.get_num_columns(), 10);
378 }
379}
380
381#[cfg(test)]
382mod test_get_vec {
383 use crate::{rational::MatQ, traits::MatrixGetSubmatrix};
384 use std::str::FromStr;
385
386 #[test]
388 fn get_row_works() {
389 let matrix = MatQ::from_str(&format!(
390 "[[0, 0, 0, 0, 0],[4/3, {}, {}, 1/{}, 1/{}]]",
391 i64::MAX,
392 i64::MIN,
393 i64::MAX,
394 i64::MIN
395 ))
396 .unwrap();
397 let row_1 = matrix.get_row(0).unwrap();
398 let row_2 = matrix.get_row(1).unwrap();
399
400 let cmp_1 = MatQ::from_str("[[0, 0, 0, 0, 0]]").unwrap();
401 let cmp_2 = MatQ::from_str(&format!(
402 "[[4/3, {}, {}, 1/{}, 1/{}]]",
403 i64::MAX,
404 i64::MIN,
405 i64::MAX,
406 i64::MIN
407 ))
408 .unwrap();
409
410 assert_eq!(cmp_1, row_1);
411 assert_eq!(cmp_2, row_2);
412 }
413
414 #[test]
416 fn get_row_negative_indexing_works() {
417 let matrix =
418 MatQ::from_str(&format!("[[0, 0, 0],[42, {}, {}]]", i64::MAX, i64::MIN)).unwrap();
419 let row_1 = matrix.get_row(-2).unwrap();
420 let row_2 = matrix.get_row(-1).unwrap();
421
422 let cmp_1 = MatQ::from_str("[[0, 0, 0]]").unwrap();
423 let cmp_2 = MatQ::from_str(&format!("[[42, {}, {}]]", i64::MAX, i64::MIN)).unwrap();
424
425 assert_eq!(cmp_1, row_1);
426 assert_eq!(cmp_2, row_2);
427 }
428
429 #[test]
431 fn get_column_works() {
432 let matrix = MatQ::from_str(&format!(
433 "[[1, 0, 3],[{}, 0, 5],[{}, 0, 7],[1/{}, 0, 9],[1/{}, 0, 11]]",
434 i64::MAX,
435 i64::MIN,
436 i64::MAX,
437 i64::MIN
438 ))
439 .unwrap();
440 let column_1 = matrix.get_column(0).unwrap();
441 let column_2 = matrix.get_column(1).unwrap();
442 let column_3 = matrix.get_column(2).unwrap();
443
444 let cmp_1 = MatQ::from_str(&format!(
445 "[[1],[{}],[{}],[1/{}],[1/{}]]",
446 i64::MAX,
447 i64::MIN,
448 i64::MAX,
449 i64::MIN
450 ))
451 .unwrap();
452 let cmp_2 = MatQ::from_str("[[0],[0],[0],[0],[0]]").unwrap();
453 let cmp_3 = MatQ::from_str("[[3],[5],[7],[9],[11]]").unwrap();
454
455 assert_eq!(cmp_1, column_1);
456 assert_eq!(cmp_2, column_2);
457 assert_eq!(cmp_3, column_3);
458 }
459
460 #[test]
462 fn get_column_negative_indexing_works() {
463 let matrix = MatQ::from_str(&format!(
464 "[[42, 0, 42],[{}, 0, 17],[{}, 0, 42]]",
465 i64::MAX,
466 i64::MIN
467 ))
468 .unwrap();
469 let column_1 = matrix.get_column(-3).unwrap();
470 let column_2 = matrix.get_column(-2).unwrap();
471 let column_3 = matrix.get_column(-1).unwrap();
472
473 let cmp_1 = MatQ::from_str(&format!("[[42],[{}],[{}]]", i64::MAX, i64::MIN)).unwrap();
474 let cmp_2 = MatQ::from_str("[[0],[0],[0]]").unwrap();
475 let cmp_3 = MatQ::from_str("[[42],[17],[42]]").unwrap();
476
477 assert_eq!(cmp_1, column_1);
478 assert_eq!(cmp_2, column_2);
479 assert_eq!(cmp_3, column_3);
480 }
481
482 #[test]
484 fn wrong_dim_error() {
485 let matrix = MatQ::from_str(&format!(
486 "[[1, 2, 3],[{}, 4, 5],[{}, 6, 7]]",
487 i64::MAX,
488 i64::MIN
489 ))
490 .unwrap();
491 let row_1 = matrix.get_row(-4);
492 let row_2 = matrix.get_row(4);
493 let column_1 = matrix.get_column(-4);
494 let column_2 = matrix.get_column(4);
495
496 assert!(row_1.is_err());
497 assert!(row_2.is_err());
498 assert!(column_1.is_err());
499 assert!(column_2.is_err());
500 }
501}
502
503#[cfg(test)]
504mod test_get_submatrix {
505 use crate::{
506 integer::Z,
507 rational::MatQ,
508 traits::{MatrixDimensions, MatrixGetSubmatrix},
509 };
510 use std::str::FromStr;
511
512 #[test]
514 fn entire_matrix() {
515 let mat = MatQ::identity(5, 5);
516
517 let sub_mat = mat.get_submatrix(0, 4, 0, 4).unwrap();
518
519 assert_eq!(mat, sub_mat);
520 }
521
522 #[test]
524 fn matrix_single_entry() {
525 let mat = MatQ::identity(5, 5);
526
527 let sub_mat = mat.get_submatrix(0, 0, 0, 0).unwrap();
528
529 let cmp_mat = MatQ::identity(1, 1);
530 assert_eq!(cmp_mat, sub_mat);
531 }
532
533 #[test]
535 fn correct_dimensions() {
536 let mat = MatQ::identity(100, 100);
537
538 let sub_mat = mat.get_submatrix(1, 37, 0, 29).unwrap();
539
540 assert_eq!(37, sub_mat.get_num_rows());
541 assert_eq!(30, sub_mat.get_num_columns());
542 }
543
544 #[test]
547 fn large_entries() {
548 let mat =
549 MatQ::from_str(&format!("[[{}/3, 2, 3],[1, {}, 3]]", u64::MAX, i64::MIN)).unwrap();
550
551 let sub_mat = mat.get_submatrix(0, 1, 0, 1).unwrap();
552
553 let cmp_mat = MatQ::from_str(&format!("[[{}/3, 2],[1, {}]]", u64::MAX, i64::MIN)).unwrap();
554 assert_eq!(cmp_mat, sub_mat);
555 }
556
557 #[test]
560 fn invalid_coordinates() {
561 let mat = MatQ::identity(10, 10);
562
563 assert!(mat.get_submatrix(0, 0, 0, 10).is_err());
564 assert!(mat.get_submatrix(0, 10, 0, 0).is_err());
565 assert!(mat.get_submatrix(0, 0, -11, 0).is_err());
566 assert!(mat.get_submatrix(-11, 0, 0, 0).is_err());
567 }
568
569 #[test]
571 fn negative_indexing() {
572 let matrix = MatQ::identity(3, 3);
573
574 assert_eq!(matrix, matrix.get_submatrix(0, -1, 0, -1).unwrap());
575 assert_eq!(matrix, matrix.get_submatrix(-3, -1, -3, -1).unwrap());
576 assert_eq!(
577 matrix.get_row(0).unwrap(),
578 matrix.get_submatrix(0, -3, -3, -1).unwrap()
579 );
580 }
581
582 #[test]
584 #[should_panic]
585 fn no_columns() {
586 let mat = MatQ::identity(10, 10);
587
588 let _ = mat.get_submatrix(0, 0, 6, 5);
589 }
590
591 #[test]
593 #[should_panic]
594 fn no_rows() {
595 let mat = MatQ::identity(10, 10);
596
597 let _ = mat.get_submatrix(5, 4, 0, 0);
598 }
599
600 #[test]
602 fn availability() {
603 let mat = MatQ::identity(10, 10);
604
605 let _ = mat.get_submatrix(0_i8, 0_i8, 0_i8, 0_i8);
606 let _ = mat.get_submatrix(0_i16, 0_i16, 0_i16, 0_i16);
607 let _ = mat.get_submatrix(0_i32, 0_i32, 0_i32, 0_i32);
608 let _ = mat.get_submatrix(0_i64, 0_i64, 0_i64, 0_i64);
609 let _ = mat.get_submatrix(0_u8, 0_u8, 0_u8, 0_u8);
610 let _ = mat.get_submatrix(0_u16, 0_i16, 0_u16, 0_u16);
611 let _ = mat.get_submatrix(0_u32, 0_i32, 0_u32, 0_u32);
612 let _ = mat.get_submatrix(0_u64, 0_i64, 0_u64, 0_u64);
613 let _ = mat.get_submatrix(&Z::ZERO, &Z::ZERO, &Z::ZERO, &Z::ZERO);
614 }
615}
616
617#[cfg(test)]
618mod test_collect_entries {
619 use super::MatQ;
620 use std::str::FromStr;
621
622 #[test]
624 fn all_entries_collected() {
625 let mat_1 = MatQ::from_str(&format!(
626 "[[1/{}, 2],[{}, {}],[-3/-4, 4]]",
627 i64::MAX,
628 i64::MAX,
629 i64::MIN
630 ))
631 .unwrap();
632 let mat_2 = MatQ::from_str("[[-1/1, 2/-4]]").unwrap();
633
634 let entries_1 = mat_1.collect_entries();
635 let entries_2 = mat_2.collect_entries();
636
637 assert_eq!(entries_1.len(), 6);
638 assert_eq!(entries_1[0].num.0, 1);
639 assert!(entries_1[0].den.0 >= 2_i64.pow(62));
640 assert_eq!(entries_1[1].num.0, 2);
641 assert_eq!(entries_1[1].den.0, 1);
642 assert!(entries_1[2].num.0 >= 2_i64.pow(62));
643 assert_eq!(entries_1[2].den.0, 1);
644 assert!(entries_1[3].num.0 >= 2_i64.pow(62));
645 assert_eq!(entries_1[3].den.0, 1);
646 assert_eq!(entries_1[4].num.0, 3);
647 assert_eq!(entries_1[4].den.0, 4);
648 assert_eq!(entries_1[5].num.0, 4);
649 assert_eq!(entries_1[5].den.0, 1);
650
651 assert_eq!(entries_2.len(), 2);
652 assert_eq!(entries_2[0].num.0, -1);
653 assert_eq!(entries_2[0].den.0, 1);
654 assert_eq!(entries_2[1].num.0, -1);
655 assert_eq!(entries_2[1].den.0, 2);
656 }
657
658 #[test]
661 fn all_entries_collected_f64() {
662 let mat_1 = MatQ::from_str(&format!(
663 "[[1/{}, 2],[{}, 4/5],[-3/-4, {}]]",
664 i64::MAX,
665 i64::MAX,
666 i64::MIN
667 ))
668 .unwrap();
669 let mat_2 = MatQ::from_str("[[-1/1, 2/-4]]").unwrap();
670
671 let entries_1 = mat_1.collect_entries_f64();
672 let entries_2 = mat_2.collect_entries_f64();
673
674 assert_eq!(entries_1.len(), 3);
675 assert_eq!(entries_1[0].len(), 2);
676 assert_eq!(entries_1[0][0], 1.0 / i64::MAX as f64);
677 assert_eq!(entries_1[0][1], 2.0);
678 assert!((entries_1[1][0] - i64::MAX as f64).abs() < 1_025.0);
679 assert!((entries_1[1][1] - 0.8).abs() < 0.00001);
680 assert_eq!(entries_1[2][0], 0.75);
681 assert_eq!(entries_1[2][1], i64::MIN as f64);
682
683 assert_eq!(entries_2.len(), 1);
684 assert_eq!(entries_2[0].len(), 2);
685 assert_eq!(entries_2[0][0], -1.0);
686 assert_eq!(entries_2[0][1], -0.5);
687 }
688}