Skip to main content

qfall_math/integer/mat_poly_over_z/sample/
binomial.rs

1// Copyright 2023 Jan Niklas Siemer
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! This module contains algorithms for sampling
10//! according to the binomial distribution.
11
12use crate::{
13    error::MathError,
14    integer::{MatPolyOverZ, PolyOverZ, Z},
15    macros::seeded::seedable_function,
16    rational::Q,
17    traits::{MatrixDimensions, MatrixSetEntry, SetCoefficient},
18    utils::{index::evaluate_index, sample::binomial::BinomialSampler},
19};
20use std::fmt::Display;
21
22impl MatPolyOverZ {
23    seedable_function!(
24        /// Outputs a [`MatPolyOverZ`] instance with entries chosen according to the binomial
25        /// distribution parameterized by `n` and `p`.
26        ///
27        /// Parameters:
28        /// - `num_rows`: specifies the number of rows the new matrix should have
29        /// - `num_cols`: specifies the number of columns the new matrix should have
30        /// - `max_degree`: specifies the maximum length of all polynomials in the matrix,
31        ///   i.e. the maximum number of coefficients any polynomial in the matrix can have
32        /// - `n`: specifies the number of trials
33        /// - `p`: specifies the probability of success
34        #[seeded]
35        /// - `seed`: specifies the 256-bit seed of the PRNG used for sampling
36        #[optionally_seeded]
37        /// - `seed`: specifies an optional 256-bit seed of the PRNG used for sampling.
38        #[optionally_seeded]
39        ///   If `None` is provided, a fresh [`ThreadRng`](rand::rngs::ThreadRng) is used instead.
40        ///
41        /// Returns a new [`MatPolyOverZ`] instance with entries chosen
42        /// according to the binomial distribution or a [`MathError`]
43        /// if `n < 0`, `p ∉ (0,1)`, `n` does not fit into an [`i64`],
44        /// or the dimensions of the matrix were chosen too small.
45        ///
46        /// # Examples
47        /// ```
48        /// use qfall_math::integer::MatPolyOverZ;
49        ///
50        #[unseeded]
51        /// let sample = MatPolyOverZ::sample_binomial(2, 2, 5, 2, 0.5).unwrap();
52        #[seeded]
53        /// let sample = MatPolyOverZ::sample_binomial_seeded(2, 2, 5, 2, 0.5, [42; 32]).unwrap();
54        /// ```
55        ///
56        /// # Errors and Failures
57        /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput)
58        ///   if `n < 0` or `p ∉ (0,1)`.
59        /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError)
60        ///   if `n` does not fit into an [`i64`].
61        ///
62        /// # Panics ...
63        /// - if the provided number of rows and columns are not suited to create a matrix.
64        ///   For further information see [`MatPolyOverZ::new`].
65        pub(crate) fn sample_binomial(
66            num_rows: impl TryInto<i64> + Display,
67            num_cols: impl TryInto<i64> + Display,
68            max_degree: impl TryInto<i64> + Display,
69            n: impl Into<Z>,
70            p: impl Into<Q>,
71            seed: Option<[u8; 32]>,
72        ) -> Result<Self, MathError> {
73            Self::sample_binomial_with_offset_optionally_seeded(
74                num_rows, num_cols, max_degree, 0, n, p, seed,
75            )
76        }
77    );
78
79    seedable_function!(
80        /// Outputs a [`MatPolyOverZ`] instance with entries chosen according to the binomial
81        /// distribution parameterized by `n` and `p` with given `offset`.
82        ///
83        /// Parameters:
84        /// - `num_rows`: specifies the number of rows the new matrix should have
85        /// - `num_cols`: specifies the number of columns the new matrix should have
86        /// - `max_degree`: specifies the maximum length of all polynomials in the matrix,
87        ///   i.e. the maximum number of coefficients any polynomial in the matrix can have
88        /// - `offset`: specifies an offset applied to each sample
89        ///   collected from the binomial distribution
90        /// - `n`: specifies the number of trials
91        /// - `p`: specifies the probability of success
92        #[seeded]
93        /// - `seed`: specifies the 256-bit seed of the PRNG used for sampling
94        #[optionally_seeded]
95        /// - `seed`: specifies an optional 256-bit seed of the PRNG used for sampling.
96        #[optionally_seeded]
97        ///   If `None` is provided, a fresh [`ThreadRng`](rand::rngs::ThreadRng) is used instead.
98        ///
99        /// Returns a new [`MatPolyOverZ`] instance with entries chosen
100        /// according to the binomial distribution or a [`MathError`]
101        /// if `n < 0`, `p ∉ (0,1)`, `n` does not fit into an [`i64`],
102        /// or the dimensions of the matrix were chosen too small.
103        ///
104        /// # Examples
105        /// ```
106        /// use qfall_math::integer::MatPolyOverZ;
107        ///
108        #[unseeded]
109        /// let sample = MatPolyOverZ::sample_binomial_with_offset(2, 2, 5, -1, 2, 0.5).unwrap();
110        #[seeded]
111        /// let sample = MatPolyOverZ::sample_binomial_with_offset_seeded(2, 2, 5, -1, 2, 0.5, [42; 32]).unwrap();
112        /// ```
113        ///
114        /// # Errors and Failures
115        /// - Returns a [`MathError`] of type [`InvalidIntegerInput`](MathError::InvalidIntegerInput)
116        ///   if `n < 0` or `p ∉ (0,1)`.
117        /// - Returns a [`MathError`] of type [`ConversionError`](MathError::ConversionError)
118        ///   if `n` does not fit into an [`i64`].
119        /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) if
120        ///   the `max_degree` is negative or it does not fit into an [`i64`].
121        ///
122        /// # Panics ...
123        /// - if the provided number of rows and columns are not suited to create a matrix.
124        ///   For further information see [`MatPolyOverZ::new`].
125        pub(crate) fn sample_binomial_with_offset(
126            num_rows: impl TryInto<i64> + Display,
127            num_cols: impl TryInto<i64> + Display,
128            max_degree: impl TryInto<i64> + Display,
129            offset: impl Into<Z>,
130            n: impl Into<Z>,
131            p: impl Into<Q>,
132            seed: Option<[u8; 32]>,
133        ) -> Result<Self, MathError> {
134            let max_degree = evaluate_index(max_degree)?;
135            let offset: Z = offset.into();
136            let mut bin_sampler = BinomialSampler::init(n, p, seed)?;
137            let mut matrix = MatPolyOverZ::new(num_rows, num_cols);
138
139            for row in 0..matrix.get_num_rows() {
140                for col in 0..matrix.get_num_columns() {
141                    let mut poly_z = PolyOverZ::default();
142
143                    for index in 0..=max_degree {
144                        let mut sample = bin_sampler.sample();
145                        sample += &offset;
146                        unsafe { poly_z.set_coeff_unchecked(index, sample) };
147                    }
148
149                    unsafe { matrix.set_entry_unchecked(row, col, poly_z) };
150                }
151            }
152
153            Ok(matrix)
154        }
155    );
156}
157
158#[cfg(test)]
159mod test_sample_binomial {
160    use super::{MatPolyOverZ, Q, Z};
161    use crate::traits::{GetCoefficient, MatrixDimensions, MatrixGetEntry};
162    use crate::utils::sample::test_seed;
163
164    // As all major tests regarding an appropriate binomial distribution,
165    // whether the correct interval is kept, and if the errors are thrown correctly,
166    // are performed in the `utils` module, we omit these tests here.
167
168    /// Checks whether the boundaries of the interval are kept.
169    #[test]
170    fn boundaries_kept() {
171        for seeded in [false, true] {
172            for _ in 0..8 {
173                let matrix = MatPolyOverZ::sample_binomial_optionally_seeded(
174                    1,
175                    1,
176                    0,
177                    2,
178                    0.5,
179                    seeded.then(test_seed),
180                )
181                .unwrap();
182                let entry = matrix.get_entry(0, 0).unwrap();
183                let poly = entry.get_coeff(0).unwrap();
184
185                assert!(Z::ZERO <= poly);
186                assert!(poly <= 2);
187            }
188        }
189    }
190
191    /// Checks whether the number of coefficients is correct.
192    #[test]
193    fn nr_coeffs() {
194        for seeded in [false, true] {
195            let degrees = [1, 3, 7, 15, 32, 120];
196            for degree in degrees {
197                let matrix = MatPolyOverZ::sample_binomial_optionally_seeded(
198                    1,
199                    1,
200                    degree,
201                    256,
202                    0.99999,
203                    seeded.then(test_seed),
204                )
205                .unwrap();
206                let poly = matrix.get_entry(0, 0).unwrap();
207
208                assert_eq!(
209                    degree,
210                    poly.get_degree(),
211                    "This test can fail with probability close to 0."
212                );
213            }
214        }
215    }
216
217    /// Checks whether matrices with at least one dimension chosen smaller than `1`
218    /// or too big for an [`i64`] results in an error.
219    #[test]
220    fn false_size() {
221        for seeded in [false, true] {
222            let result = std::panic::catch_unwind(|| {
223                let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
224                    0,
225                    3,
226                    0,
227                    1,
228                    0.5,
229                    seeded.then(test_seed),
230                );
231            });
232            assert!(result.is_err());
233        }
234    }
235
236    /// Checks whether providing a length smaller than `0` results in an error.
237    #[test]
238    fn invalid_max_degree() {
239        for seeded in [false, true] {
240            let res_0 = MatPolyOverZ::sample_binomial_optionally_seeded(
241                1,
242                1,
243                -1,
244                2,
245                0.5,
246                seeded.then(test_seed),
247            );
248            let res_1 = MatPolyOverZ::sample_binomial_optionally_seeded(
249                1,
250                1,
251                i64::MIN,
252                2,
253                0.5,
254                seeded.then(test_seed),
255            );
256
257            assert!(res_0.is_err());
258            assert!(res_1.is_err());
259        }
260    }
261
262    /// Checks whether `sample_binomial` is available for all types
263    /// implementing [`Into<Z>`], i.e. u8, u16, u32, u64, i8, ...
264    /// and [`Into<Q>`], i.e. u8, u16, i8, i16, f32, f64, ...
265    #[test]
266    fn availability() {
267        for seeded in [false, true] {
268            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
269                1,
270                1,
271                0u8,
272                1u16,
273                7u8,
274                seeded.then(test_seed),
275            );
276            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
277                1,
278                1,
279                0u16,
280                1u32,
281                7u16,
282                seeded.then(test_seed),
283            );
284            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
285                1,
286                1,
287                0u32,
288                1u64,
289                7u32,
290                seeded.then(test_seed),
291            );
292            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
293                1,
294                1,
295                0u64,
296                1i8,
297                7u64,
298                seeded.then(test_seed),
299            );
300            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
301                1,
302                1,
303                0i8,
304                1i16,
305                7i8,
306                seeded.then(test_seed),
307            );
308            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
309                1,
310                1,
311                0i16,
312                1i32,
313                7i16,
314                seeded.then(test_seed),
315            );
316            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
317                1,
318                1,
319                0i32,
320                1i64,
321                7i32,
322                seeded.then(test_seed),
323            );
324            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
325                1,
326                1,
327                0i64,
328                Z::ONE,
329                7i64,
330                seeded.then(test_seed),
331            );
332            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
333                1,
334                1,
335                0,
336                1u8,
337                0.5f32,
338                seeded.then(test_seed),
339            );
340            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
341                1,
342                1,
343                Z::ZERO,
344                1u8,
345                0.5f64,
346                seeded.then(test_seed),
347            );
348            let _ = MatPolyOverZ::sample_binomial_optionally_seeded(
349                1,
350                1,
351                0,
352                1,
353                Q::from((1, 2)),
354                seeded.then(test_seed),
355            );
356
357            // explicitly checks the existence of the unseeded and the seeded function
358            if seeded {
359                let _ = MatPolyOverZ::sample_binomial_seeded(1, 1, 0u8, 1u16, 7u8, test_seed());
360            } else {
361                let _ = MatPolyOverZ::sample_binomial(1, 1, 0u8, 1u16, 7u8);
362            }
363        }
364    }
365
366    /// Checks whether the size of uniformly random sampled matrices
367    /// fits the specified dimensions.
368    #[test]
369    fn matrix_size() {
370        for seeded in [false, true] {
371            let mat_0 = MatPolyOverZ::sample_binomial_optionally_seeded(
372                3,
373                3,
374                0,
375                1,
376                0.5,
377                seeded.then(test_seed),
378            )
379            .unwrap();
380            let mat_1 = MatPolyOverZ::sample_binomial_optionally_seeded(
381                4,
382                1,
383                0,
384                1,
385                0.5,
386                seeded.then(test_seed),
387            )
388            .unwrap();
389            let mat_2 = MatPolyOverZ::sample_binomial_optionally_seeded(
390                1,
391                5,
392                0,
393                1,
394                0.5,
395                seeded.then(test_seed),
396            )
397            .unwrap();
398            let mat_3 = MatPolyOverZ::sample_binomial_optionally_seeded(
399                15,
400                20,
401                0,
402                1,
403                0.5,
404                seeded.then(test_seed),
405            )
406            .unwrap();
407
408            assert_eq!(3, mat_0.get_num_rows());
409            assert_eq!(3, mat_0.get_num_columns());
410            assert_eq!(4, mat_1.get_num_rows());
411            assert_eq!(1, mat_1.get_num_columns());
412            assert_eq!(1, mat_2.get_num_rows());
413            assert_eq!(5, mat_2.get_num_columns());
414            assert_eq!(15, mat_3.get_num_rows());
415            assert_eq!(20, mat_3.get_num_columns());
416        }
417    }
418
419    /// Checks whether the same seed results in the same sample.
420    #[test]
421    fn same_seed_same_sample() {
422        use crate::integer::MatPolyOverZ;
423        let sample_0 = MatPolyOverZ::sample_binomial_seeded(2, 2, 5, 2, 0.5, [42; 32]).unwrap();
424        let sample_1 = MatPolyOverZ::sample_binomial_seeded(2, 2, 5, 2, 0.5, [42; 32]).unwrap();
425
426        assert_eq!(sample_0, sample_1);
427    }
428
429    /// Checks whether the seeded variant outputs the known sample for a fixed seed.
430    #[test]
431    fn known_answer() {
432        use crate::integer::MatPolyOverZ;
433        let sample = MatPolyOverZ::sample_binomial_seeded(2, 2, 5, 2, 0.5, [42; 32]).unwrap();
434
435        assert_eq!(
436            "[[6  0 2 1 2 0 1, 6  0 1 0 1 0 1],[6  0 2 2 1 2 1, 5  1 1 1 2 1]]",
437            sample.to_string()
438        );
439    }
440}
441
442#[cfg(test)]
443mod test_sample_binomial_with_offset {
444    use super::{MatPolyOverZ, Q, Z};
445    use crate::traits::{GetCoefficient, MatrixDimensions, MatrixGetEntry};
446    use crate::utils::sample::test_seed;
447
448    // As all major tests regarding an appropriate binomial distribution,
449    // whether the correct interval is kept, and if the errors are thrown correctly,
450    // are performed in the `utils` module, we omit these tests here.
451
452    /// Checks whether the boundaries of the interval are kept.
453    #[test]
454    fn boundaries_kept() {
455        for seeded in [false, true] {
456            for _ in 0..8 {
457                let matrix = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
458                    1,
459                    1,
460                    0,
461                    -1,
462                    2,
463                    0.5,
464                    seeded.then(test_seed),
465                )
466                .unwrap();
467                let entry = matrix.get_entry(0, 0).unwrap();
468                let poly = entry.get_coeff(0).unwrap();
469
470                assert!(Z::MINUS_ONE <= poly);
471                assert!(poly <= Z::ONE);
472            }
473        }
474    }
475
476    /// Checks whether the number of coefficients is correct.
477    #[test]
478    fn nr_coeffs() {
479        for seeded in [false, true] {
480            let degrees = [1, 3, 7, 15, 32, 120];
481            for degree in degrees {
482                let matrix = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
483                    1,
484                    1,
485                    degree,
486                    -1,
487                    256,
488                    0.99999,
489                    seeded.then(test_seed),
490                )
491                .unwrap();
492                let poly = matrix.get_entry(0, 0).unwrap();
493
494                assert_eq!(
495                    degree,
496                    poly.get_degree(),
497                    "This test can fail with probability close to 0."
498                );
499            }
500        }
501    }
502
503    /// Checks whether matrices with at least one dimension chosen smaller than `1`
504    /// or too big for an [`i64`] results in an error.
505    #[test]
506    fn false_size() {
507        for seeded in [false, true] {
508            let result = std::panic::catch_unwind(|| {
509                let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
510                    0,
511                    3,
512                    0,
513                    0,
514                    1,
515                    0.5,
516                    seeded.then(test_seed),
517                );
518            });
519            assert!(result.is_err());
520        }
521    }
522
523    /// Checks whether providing a length smaller than `0` results in an error.
524    #[test]
525    fn invalid_max_degree() {
526        for seeded in [false, true] {
527            let res_0 = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
528                1,
529                1,
530                -1,
531                -1,
532                2,
533                0.5,
534                seeded.then(test_seed),
535            );
536            let res_1 = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
537                1,
538                1,
539                i64::MIN,
540                -1,
541                2,
542                0.5,
543                seeded.then(test_seed),
544            );
545
546            assert!(res_0.is_err());
547            assert!(res_1.is_err());
548        }
549    }
550
551    /// Checks whether `sample_binomial_with_offset` is available for all types
552    /// implementing [`Into<Z>`], i.e. u8, u16, u32, u64, i8, ...
553    /// and [`Into<Q>`], i.e. u8, u16, i8, i16, f32, f64, ...
554    #[test]
555    fn availability() {
556        for seeded in [false, true] {
557            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
558                1,
559                1,
560                0u8,
561                -1,
562                1u16,
563                7u8,
564                seeded.then(test_seed),
565            );
566            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
567                1,
568                1,
569                0u16,
570                0,
571                1u32,
572                7u16,
573                seeded.then(test_seed),
574            );
575            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
576                1,
577                1,
578                0u32,
579                Z::ONE,
580                1u64,
581                7u32,
582                seeded.then(test_seed),
583            );
584            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
585                1,
586                1,
587                0u64,
588                Z::MINUS_ONE,
589                1i8,
590                7u64,
591                seeded.then(test_seed),
592            );
593            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
594                1,
595                1,
596                0i8,
597                -1,
598                1i16,
599                7i8,
600                seeded.then(test_seed),
601            );
602            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
603                1,
604                1,
605                0i16,
606                -1,
607                1i32,
608                7i16,
609                seeded.then(test_seed),
610            );
611            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
612                1,
613                1,
614                0i32,
615                -1,
616                1i64,
617                7i32,
618                seeded.then(test_seed),
619            );
620            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
621                1,
622                1,
623                0i64,
624                -1,
625                Z::ONE,
626                7i64,
627                seeded.then(test_seed),
628            );
629            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
630                1,
631                1,
632                0,
633                -1,
634                1u8,
635                0.5f32,
636                seeded.then(test_seed),
637            );
638            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
639                1,
640                1,
641                Z::ZERO,
642                -1,
643                1u8,
644                0.5f64,
645                seeded.then(test_seed),
646            );
647            let _ = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
648                1,
649                1,
650                0,
651                -1,
652                1,
653                Q::from((1, 2)),
654                seeded.then(test_seed),
655            );
656
657            // explicitly checks the existence of the unseeded and the seeded function
658            if seeded {
659                let _ = MatPolyOverZ::sample_binomial_with_offset_seeded(
660                    1,
661                    1,
662                    0u8,
663                    -1,
664                    1u16,
665                    7u8,
666                    test_seed(),
667                );
668            } else {
669                let _ = MatPolyOverZ::sample_binomial_with_offset(1, 1, 0u8, -1, 1u16, 7u8);
670            }
671        }
672    }
673
674    /// Checks whether the size of uniformly random sampled matrices
675    /// fits the specified dimensions.
676    #[test]
677    fn matrix_size() {
678        for seeded in [false, true] {
679            let mat_0 = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
680                3,
681                3,
682                0,
683                -1,
684                1,
685                0.5,
686                seeded.then(test_seed),
687            )
688            .unwrap();
689            let mat_1 = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
690                4,
691                1,
692                0,
693                -1,
694                1,
695                0.5,
696                seeded.then(test_seed),
697            )
698            .unwrap();
699            let mat_2 = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
700                1,
701                5,
702                0,
703                -1,
704                1,
705                0.5,
706                seeded.then(test_seed),
707            )
708            .unwrap();
709            let mat_3 = MatPolyOverZ::sample_binomial_with_offset_optionally_seeded(
710                15,
711                20,
712                0,
713                -1,
714                1,
715                0.5,
716                seeded.then(test_seed),
717            )
718            .unwrap();
719
720            assert_eq!(3, mat_0.get_num_rows());
721            assert_eq!(3, mat_0.get_num_columns());
722            assert_eq!(4, mat_1.get_num_rows());
723            assert_eq!(1, mat_1.get_num_columns());
724            assert_eq!(1, mat_2.get_num_rows());
725            assert_eq!(5, mat_2.get_num_columns());
726            assert_eq!(15, mat_3.get_num_rows());
727            assert_eq!(20, mat_3.get_num_columns());
728        }
729    }
730
731    /// Checks whether the same seed results in the same sample.
732    #[test]
733    fn same_seed_same_sample() {
734        use crate::integer::MatPolyOverZ;
735        let sample_0 =
736            MatPolyOverZ::sample_binomial_with_offset_seeded(2, 2, 5, -1, 2, 0.5, [42; 32])
737                .unwrap();
738        let sample_1 =
739            MatPolyOverZ::sample_binomial_with_offset_seeded(2, 2, 5, -1, 2, 0.5, [42; 32])
740                .unwrap();
741
742        assert_eq!(sample_0, sample_1);
743    }
744
745    /// Checks whether the seeded variant outputs the known sample for a fixed seed.
746    #[test]
747    fn known_answer() {
748        use crate::integer::MatPolyOverZ;
749        let sample =
750            MatPolyOverZ::sample_binomial_with_offset_seeded(2, 2, 5, -1, 2, 0.5, [42; 32])
751                .unwrap();
752
753        assert_eq!(
754            "[[5  -1 1 0 1 -1, 5  -1 0 -1 0 -1],[5  -1 1 1 0 1, 6  0 0 0 1 0 -1]]",
755            sample.to_string()
756        );
757    }
758}