Skip to main content

feanor_math/rings/zn/
zn_pow2.rs

1use smallvec::SmallVec;
2
3use crate::delegate::DelegateRing;
4use crate::divisibility::DivisibilityRing;
5use crate::homomorphism::*;
6use crate::integer::*;
7use crate::pid::*;
8use crate::primitive_int::{StaticRing, StaticRingBase};
9use crate::ring::*;
10use crate::rings::rust_bigint::{RustBigintRing, RustBigintRingBase};
11use crate::rings::zn::*;
12use crate::specialization::*;
13use crate::{
14    impl_field_wrap_unwrap_homs, impl_field_wrap_unwrap_isos, impl_localpir_wrap_unwrap_homs,
15    impl_localpir_wrap_unwrap_isos,
16};
17
18const DEFAULT_SMALLVEC_SIZE: usize = 4;
19
20/// Ring representing `Z/nZ`, where `n = 2^k` for some `k`, computing the modular reductions
21/// via standard bit masking.
22///
23/// # Performance
24///
25/// This implementation uses schoolbook multiplication.  As such, it is only optimized for a
26/// small enough `k`.
27/// Elements are internally stored using `SmallVec<[u64; N]>`, where `N` is a compile-time
28/// constant, and is the threshold for when dynamic allocation is used.
29/// It is recommended to set N to be exactly div_ceil(k, 64).
30///
31///
32/// # Example
33/// ```rust
34/// # use feanor_math::ring::*;
35/// # use feanor_math::homomorphism::*;
36/// # use feanor_math::rings::zn::*;
37/// # use feanor_math::rings::zn::zn_pow2::*;
38/// # use feanor_math::primitive_int::*;
39/// # use feanor_math::assert_el_eq;
40/// let R = Z2k::<3>::new(130);
41/// let a = R
42///     .get_ring()
43///     .from_base_u64_repr([u64::MAX, u64::MAX, 3].into_iter());
44/// assert_el_eq!(R, R.int_hom().map(-1), a);
45/// ```
46#[derive(Clone)]
47pub struct Z2kBase<const N: usize = DEFAULT_SMALLVEC_SIZE> {
48    k: usize,
49    n_limbs: usize,
50    last_limb_mask: u64,
51    modulus: El<RustBigintRing>,
52}
53impl<const N: usize> Z2kBase<N>
54where
55    [u64; N]: smallvec::Array<Item = u64>,
56{
57    pub fn new(k: usize) -> Self {
58        assert!(k >= 1);
59        let n_limbs = k.div_ceil(64);
60        assert!(
61            n_limbs <= 16,
62            "This implementation is not optimized for such a large modulus."
63        );
64        let modulus = RustBigintRing::RING.power_of_two(k);
65
66        let last_limb_mask = if k % 64 == 0 {
67            0xFFFFFFFFFFFFFFFF
68        } else {
69            0xFFFFFFFFFFFFFFFF >> (64 - (k % 64))
70        };
71        Self {
72            k,
73            n_limbs,
74            last_limb_mask,
75            modulus,
76        }
77    }
78
79    fn mask_el(&self, el: &mut Z2kEl<N>) {
80        el.0[self.n_limbs - 1] &= self.last_limb_mask;
81        el.0.truncate(self.n_limbs);
82    }
83
84    fn bigint_to_el(&self, ZZ: &RustBigintRing, x: &El<RustBigintRing>) -> Z2kEl<N> {
85        let mut rem = ZZ.euclidean_rem(ZZ.clone_el(x), self.modulus());
86
87        // normalize to `[0, 2^k)`.
88        if ZZ.is_neg(&rem) {
89            ZZ.add_assign_ref(&mut rem, self.modulus());
90        }
91
92        let mut limbs = SmallVec::from_elem(0, self.n_limbs);
93        for (i, d) in ZZ.get_ring().abs_base_u64_repr(&rem).take(self.n_limbs).enumerate() {
94            limbs[i] = d;
95        }
96        Z2kEl(limbs)
97    }
98
99    fn el_to_bigint(&self, ZZ: &RustBigintRing, mut el: Z2kEl<N>) -> El<RustBigintRing> {
100        self.mask_el(&mut el);
101        let mut acc = ZZ.zero();
102        let i128_ring = StaticRing::<i128>::RING;
103        for i in 0..self.n_limbs {
104            let limb = int_cast(el.0[i] as i128, ZZ, &i128_ring);
105            if !ZZ.is_zero(&limb) {
106                let shifted = ZZ.mul(limb, ZZ.power_of_two(64 * i));
107                ZZ.add_assign(&mut acc, shifted);
108            }
109        }
110        acc
111    }
112
113    /// Returns an iterator over the digits of the `2^64`-adic digit
114    /// representation of the absolute value of the given element.
115    pub fn abs_base_u64_repr(&self, el: &Z2kEl<N>) -> impl Iterator<Item = u64> {
116        el.0.iter().take(self.n_limbs).enumerate().map(|(i, &x)| {
117            if i == self.n_limbs - 1 {
118                x & self.last_limb_mask
119            } else {
120                x
121            }
122        })
123    }
124
125    /// Interprets the elements of the iterator as digits in a `2^64`-adic
126    /// digit representation, and returns the big integer represented by it.
127    pub fn from_base_u64_repr<I>(&self, data: I) -> Z2kEl<N>
128    where
129        I: Iterator<Item = u64>,
130    {
131        let data = data.take(self.n_limbs).enumerate().map(|(i, x)| {
132            if i == self.n_limbs - 1 {
133                x & self.last_limb_mask
134            } else {
135                x
136            }
137        });
138        let mut data = SmallVec::from_iter(data);
139        data.resize(self.n_limbs, 0);
140        Z2kEl(data)
141    }
142}
143
144impl<const N: usize> PartialEq for Z2kBase<N>
145where
146    [u64; N]: smallvec::Array<Item = u64>,
147{
148    fn eq(&self, other: &Self) -> bool { self.k == other.k }
149}
150
151impl<const N: usize> Eq for Z2kBase<N> where [u64; N]: smallvec::Array<Item = u64> {}
152
153pub type Z2k<const N: usize = DEFAULT_SMALLVEC_SIZE> = RingValue<Z2kBase<N>>;
154impl<const N: usize> Z2k<N>
155where
156    [u64; N]: smallvec::Array<Item = u64>,
157{
158    pub fn new(log_modulus: usize) -> Self { RingValue::from(Z2kBase::<N>::new(log_modulus)) }
159}
160
161#[derive(Clone)]
162pub struct Z2kEl<const N: usize>(SmallVec<[u64; N]>)
163where
164    [u64; N]: smallvec::Array<Item = u64>;
165impl<const N: usize> RingBase for Z2kBase<N>
166where
167    [u64; N]: smallvec::Array<Item = u64>,
168{
169    type Element = Z2kEl<N>;
170
171    fn clone_el(&self, val: &Self::Element) -> Self::Element { val.clone() }
172
173    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
174        let mut carry = 0u64;
175        for i in 0..self.n_limbs {
176            let (sum, o0) = lhs.0[i].overflowing_add(rhs.0[i]);
177            let (sum, o1) = sum.overflowing_add(carry);
178            lhs.0[i] = sum;
179            carry = (o0 || o1) as u64;
180        }
181    }
182
183    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.add_assign_ref(lhs, &rhs); }
184
185    fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
186        let mut borrow = 0u64;
187        for i in 0..self.n_limbs {
188            let (diff, o0) = lhs.0[i].overflowing_sub(rhs.0[i]);
189            let (diff, o1) = diff.overflowing_sub(borrow);
190            lhs.0[i] = diff;
191            borrow = (o0 || o1) as u64;
192        }
193    }
194
195    fn negate_inplace(&self, lhs: &mut Self::Element) {
196        let mut z = self.zero();
197        self.sub_assign_ref(&mut z, lhs);
198        *lhs = z;
199    }
200
201    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.mul_assign_ref(lhs, &rhs); }
202
203    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = mul_z2k(lhs, rhs, self.n_limbs); }
204
205    fn from_int(&self, value: i32) -> Self::Element {
206        let ZZ = &RustBigintRing::RING;
207        let x = ZZ.int_hom().map(value);
208        self.bigint_to_el(ZZ, &x)
209    }
210
211    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
212        let mut a = lhs.clone();
213        let mut b = rhs.clone();
214        self.mask_el(&mut a);
215        self.mask_el(&mut b);
216        a.0 == b.0
217    }
218
219    fn is_zero(&self, value: &Self::Element) -> bool {
220        let mut v = value.clone();
221        self.mask_el(&mut v);
222        v.0.iter().all(|x| *x == 0)
223    }
224
225    fn is_one(&self, value: &Self::Element) -> bool { self.eq_el(value, &self.one()) }
226
227    fn is_commutative(&self) -> bool { true }
228    fn is_noetherian(&self) -> bool { true }
229
230    fn dbg_within<'a>(
231        &self,
232        value: &Self::Element,
233        out: &mut std::fmt::Formatter<'a>,
234        _: EnvBindingStrength,
235    ) -> std::fmt::Result {
236        let ZZ = RustBigintRing::RING;
237        write!(out, "{}", ZZ.format(&self.el_to_bigint(&ZZ, value.clone())))
238    }
239
240    fn characteristic<J: RingStore + Copy>(&self, ZZ: J) -> Option<El<J>>
241    where
242        J::Type: IntegerRing,
243    {
244        self.size(ZZ)
245    }
246
247    fn is_approximate(&self) -> bool { false }
248}
249
250// Schoolbook multiplication, only keeps the first `n_limbs` limbs.
251fn mul_z2k<const N: usize>(Z2kEl(a): &Z2kEl<N>, Z2kEl(b): &Z2kEl<N>, n_limbs: usize) -> Z2kEl<N>
252where
253    [u64; N]: smallvec::Array<Item = u64>,
254{
255    let mut res = SmallVec::from_elem(0, n_limbs);
256    for i in 0..n_limbs {
257        let mut carry = 0u128;
258        for j in 0..n_limbs - i {
259            let idx = i + j;
260            let sum = a[i] as u128 * b[j] as u128 + res[idx] as u128 + carry;
261            res[idx] = sum as u64;
262            carry = sum >> 64;
263        }
264    }
265    Z2kEl(res)
266}
267
268impl<const N: usize> DivisibilityRing for Z2kBase<N>
269where
270    [u64; N]: smallvec::Array<Item = u64>,
271{
272    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
273        super::generic_impls::checked_left_div(RingRef::new(self), lhs, rhs)
274    }
275}
276
277impl<const N: usize> CanHomFrom<Z2kBase<N>> for Z2kBase<N>
278where
279    [u64; N]: smallvec::Array<Item = u64>,
280{
281    type Homomorphism = ();
282
283    fn has_canonical_hom(&self, from: &Z2kBase<N>) -> Option<Self::Homomorphism> {
284        // Canonical homomorphism is expected to be unital. As such, characteristics must match.
285        (from.k == self.k).then_some(())
286    }
287
288    fn map_in(
289        &self,
290        _from: &Z2kBase<N>,
291        el: <Z2kBase<N> as RingBase>::Element,
292        _: &Self::Homomorphism,
293    ) -> Self::Element {
294        el
295    }
296}
297
298impl<const N: usize> CanIsoFromTo<Z2kBase<N>> for Z2kBase<N>
299where
300    [u64; N]: smallvec::Array<Item = u64>,
301{
302    type Isomorphism = ();
303
304    fn has_canonical_iso(&self, from: &Z2kBase<N>) -> Option<Self::Isomorphism> { (self.k == from.k).then_some(()) }
305
306    fn map_out(
307        &self,
308        _from: &Z2kBase<N>,
309        el: Self::Element,
310        _: &Self::Isomorphism,
311    ) -> <Z2kBase<N> as RingBase>::Element {
312        el
313    }
314}
315
316#[derive(Clone)]
317pub struct Z2KBaseElementsIter<'a, const N: usize>
318where
319    [u64; N]: smallvec::Array<Item = u64>,
320{
321    ring: &'a Z2kBase<N>,
322    next_index: Z2kEl<N>,
323    wrap_around: bool,
324}
325
326impl<'a, const N: usize> Iterator for Z2KBaseElementsIter<'a, N>
327where
328    [u64; N]: smallvec::Array<Item = u64>,
329{
330    type Item = Z2kEl<N>;
331
332    fn next(&mut self) -> Option<Self::Item> {
333        if self.wrap_around {
334            return None;
335        }
336
337        let out = self.next_index.clone();
338        self.ring.add_assign(&mut self.next_index, self.ring.one());
339        if self.ring.is_zero(&self.next_index) {
340            self.wrap_around = true;
341        }
342        Some(out)
343    }
344}
345
346impl<const N: usize> FiniteRingSpecializable for Z2kBase<N>
347where
348    [u64; N]: smallvec::Array<Item = u64>,
349{
350    fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.execute() }
351}
352
353impl<const N: usize> FiniteRing for Z2kBase<N>
354where
355    [u64; N]: smallvec::Array<Item = u64>,
356{
357    type ElementsIter<'a>
358        = Z2KBaseElementsIter<'a, N>
359    where
360        Self: 'a;
361
362    fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
363        Z2KBaseElementsIter {
364            ring: self,
365            next_index: self.zero(),
366            wrap_around: false,
367        }
368    }
369
370    fn random_element<G: FnMut() -> u64>(&self, rng: G) -> <Self as RingBase>::Element {
371        super::generic_impls::random_element(self, rng)
372    }
373
374    fn size<I: RingStore + Copy>(&self, other_ZZ: I) -> Option<El<I>>
375    where
376        I::Type: IntegerRing,
377    {
378        {
379            if other_ZZ.get_ring().representable_bits().is_none()
380                || self.k < other_ZZ.get_ring().representable_bits().unwrap()
381            {
382                Some(int_cast(
383                    RustBigintRing::RING.clone_el(&self.modulus),
384                    other_ZZ,
385                    &RustBigintRing::RING,
386                ))
387            } else {
388                None
389            }
390        }
391    }
392}
393
394impl<const N: usize> PrincipalIdealRing for Z2kBase<N>
395where
396    [u64; N]: smallvec::Array<Item = u64>,
397{
398    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
399        super::generic_impls::checked_div_min(RingRef::new(self), lhs, rhs)
400    }
401
402    fn extended_ideal_gen(
403        &self,
404        lhs: &Self::Element,
405        rhs: &Self::Element,
406    ) -> (Self::Element, Self::Element, Self::Element) {
407        let ZZ = RustBigintRing::RING;
408        let l = self.el_to_bigint(&ZZ, lhs.clone());
409        let r = self.el_to_bigint(&ZZ, rhs.clone());
410        let (s, t, d) = ZZ.extended_ideal_gen(&l, &r);
411        let [s, t, d] = [s, t, d].map(|x| self.bigint_to_el(&ZZ, &x));
412        (s, t, d)
413    }
414}
415
416impl<const N: usize, I: ?Sized + IntegerRing> CanHomFrom<I> for Z2kBase<N>
417where
418    [u64; N]: smallvec::Array<Item = u64>,
419{
420    type Homomorphism = super::generic_impls::BigIntToZnHom<I, RustBigintRingBase, Self>;
421
422    fn has_canonical_hom(&self, from: &I) -> Option<Self::Homomorphism> {
423        super::generic_impls::has_canonical_hom_from_bigint(from, self, RustBigintRing::RING.get_ring(), None)
424    }
425
426    default fn map_in(&self, from: &I, el: I::Element, hom: &Self::Homomorphism) -> Self::Element {
427        let ZZ = &RustBigintRing::RING;
428        super::generic_impls::map_in_from_bigint(
429            from,
430            self,
431            ZZ.get_ring(),
432            el,
433            hom,
434            |n| self.bigint_to_el(ZZ, &n),
435            |n| self.bigint_to_el(ZZ, &n),
436        )
437    }
438}
439
440impl<const N: usize> CanHomFrom<RustBigintRingBase> for Z2kBase<N>
441where
442    [u64; N]: smallvec::Array<Item = u64>,
443{
444    fn map_in(
445        &self,
446        _from: &RustBigintRingBase,
447        el: <RustBigintRingBase as RingBase>::Element,
448        _: &Self::Homomorphism,
449    ) -> Self::Element {
450        self.bigint_to_el(&RustBigintRing::RING, &el)
451    }
452}
453
454macro_rules! impl_static_int_to_z2k {
455    ($($int:ident),*) => {
456        $(
457            impl<const N: usize> CanHomFrom<StaticRingBase<$int>> for Z2kBase<N>
458            where
459                [u64; N]: smallvec::Array<Item = u64>,
460            {
461                fn map_in(
462                    &self,
463                    from: &StaticRingBase<$int>,
464                    el: $int,
465                    _: &Self::Homomorphism,
466                ) -> Self::Element {
467                    let ZZ = &RustBigintRing::RING;
468                    let x = int_cast(el, ZZ, RingRef::new(from));
469                    self.bigint_to_el(ZZ, &x)
470                }
471            }
472        )*
473    };
474}
475
476impl_static_int_to_z2k! { i8, i16, i32, i64, i128 }
477
478impl<const N: usize> ZnRing for Z2kBase<N>
479where
480    [u64; N]: smallvec::Array<Item = u64>,
481{
482    type IntegerRingBase = RustBigintRingBase;
483    type IntegerRing = RustBigintRing;
484
485    fn integer_ring(&self) -> &Self::IntegerRing { &RustBigintRing::RING }
486
487    fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
488        let ZZ = RustBigintRing::RING;
489        self.el_to_bigint(&ZZ, el)
490    }
491
492    fn smallest_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
493        let result = self.smallest_positive_lift(el);
494        let mut mod_half = self.integer_ring().clone_el(self.modulus());
495        self.integer_ring().euclidean_div_pow_2(&mut mod_half, 1);
496        if self.integer_ring().is_gt(&result, &mod_half) {
497            self.integer_ring().sub_ref_snd(result, self.modulus())
498        } else {
499            result
500        }
501    }
502
503    fn modulus(&self) -> &El<Self::IntegerRing> { &self.modulus }
504
505    fn any_lift(&self, el: Self::Element) -> El<Self::IntegerRing> { self.smallest_positive_lift(el) }
506
507    fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element {
508        debug_assert!({
509            let ZZ = RustBigintRing::RING;
510            !ZZ.is_neg(&x) && ZZ.is_lt(&x, &self.modulus)
511        });
512        self.bigint_to_el(&RustBigintRing::RING, &x)
513    }
514}
515
516impl_field_wrap_unwrap_homs! { Z2kBase, Z2kBase }
517impl_field_wrap_unwrap_isos! { Z2kBase, Z2kBase }
518impl_localpir_wrap_unwrap_homs! { Z2kBase, Z2kBase }
519impl_localpir_wrap_unwrap_isos! { Z2kBase, Z2kBase }
520
521#[cfg(test)]
522mod tests {
523    use super::*;
524    use crate::primitive_int::StaticRing;
525    use crate::ring::generic_tests as ring_generic_tests;
526    use crate::rings::zn::generic_tests as zn_generic_tests;
527
528    const SMALL_Ks: [usize; 2] = [3, 8];
529    const ZZ: BigIntRing = BigIntRing::RING;
530
531    #[test]
532    fn test_z2k_can_hom_map_in_large_power() {
533        let r: Z2k = Z2k::new(256);
534        zn_generic_tests::test_map_in_large_int(r);
535    }
536
537    #[test]
538    fn test_z2k_can_hom_axioms_static_i32() {
539        let to: Z2k = Z2k::new(14);
540        ring_generic_tests::test_hom_axioms(StaticRing::<i32>::RING, to, -12i32..=12);
541    }
542
543    #[test]
544    fn test_z2k_can_hom_axioms_bigint_ring() {
545        let to: Z2k = Z2k::new(20);
546        let edge = [
547            ZZ.zero(),
548            ZZ.one(),
549            ZZ.negate(ZZ.one()),
550            ZZ.int_hom().map(17),
551            ZZ.int_hom().map(-42),
552            ZZ.pow(ZZ.int_hom().map(2), 200),
553        ];
554        ring_generic_tests::test_hom_axioms(&ZZ, to, edge.into_iter());
555    }
556
557    #[test]
558    fn test_z2k_pow2_64_add_mul_edge_cases() {
559        let r: Z2k = Z2k::new(64);
560
561        let almost_mod = ZZ.sub(ZZ.power_of_two(64), ZZ.int_hom().map(5));
562        let a = r.coerce(&ZZ, almost_mod);
563        let b = r.int_hom().map(5);
564        assert_el_eq!(r, r.zero(), r.add_ref(&a, &b));
565
566        // `2^32 * 2^32` = 0 mod `2^64`.
567        let pow32 = ZZ.power_of_two(32);
568        let u = r.coerce(&ZZ, pow32);
569        assert_el_eq!(r, r.zero(), r.mul_ref(&u, &u));
570    }
571
572    #[test]
573    fn test_z2k_pow2_130_add_mul_edge_cases() {
574        let r: Z2k = Z2k::new(130);
575
576        // `k` is not a multiple of 64, so the last limb is partially masked.
577        let almost_mod = ZZ.sub(ZZ.power_of_two(130), ZZ.int_hom().map(11));
578        let a = r.coerce(&ZZ, almost_mod);
579        let b = r.int_hom().map(11);
580        assert_el_eq!(r, r.zero(), r.add_ref(&a, &b));
581
582        // `2^100 + 2^129` = 0 mod `2^130`.
583        let hi = ZZ.add(ZZ.power_of_two(100), ZZ.power_of_two(129));
584        let expected = ZZ.euclidean_rem(ZZ.mul_ref(&hi, &hi), &r.modulus());
585        let x = r.coerce(&ZZ, hi);
586        let y = r.mul_ref(&x, &x);
587        assert!(ZZ.eq_el(&expected, &r.smallest_positive_lift(y)));
588    }
589
590    #[test]
591    fn test_ring_axioms_z2kbase() {
592        for k in SMALL_Ks {
593            let ring: Z2k = Z2k::new(k);
594            crate::ring::generic_tests::test_ring_axioms(&ring, ring.elements())
595        }
596    }
597
598    #[test]
599    fn test_zn_ring_axioms_znbase() {
600        for k in SMALL_Ks {
601            let ring: Z2k = Z2k::new(k);
602            crate::rings::zn::generic_tests::test_zn_axioms(ring);
603        }
604    }
605
606    #[test]
607    fn test_divisibility_axioms() {
608        for k in SMALL_Ks {
609            let R: Z2k = Z2k::new(k);
610            crate::divisibility::generic_tests::test_divisibility_axioms(&R, R.elements());
611        }
612    }
613
614    #[test]
615    fn test_principal_ideal_ring_axioms() {
616        for k in SMALL_Ks {
617            let R: Z2k = Z2k::new(k);
618            crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
619        }
620    }
621
622    #[test]
623    fn test_finite_field_axioms() {
624        for k in SMALL_Ks {
625            let R: Z2k = Z2k::new(k);
626            crate::rings::finite::generic_tests::test_finite_ring_axioms(&R);
627        }
628    }
629
630    #[test]
631    fn test_abs_from_base_u64_repr_roundtrip() {
632        let r: Z2k = Z2k::new(130);
633        let rb = r.get_ring();
634        let x = rb.from_base_u64_repr([1u64, 2, 0xFFFF_FFFF_FFFF_FFFDu64].into_iter());
635        let y = rb.from_base_u64_repr(rb.abs_base_u64_repr(&x));
636        assert_el_eq!(r, x, y);
637    }
638
639    #[test]
640    fn test_from_base_u64_repr_iterator_shorter_than_limbs() {
641        let r: Z2k = Z2k::new(130);
642        let rb = r.get_ring();
643        assert_eq!(rb.n_limbs, 3);
644
645        let padded = rb.from_base_u64_repr([0xDEAD_BEEF_CAFEu64].into_iter());
646        let full = rb.from_base_u64_repr([0xDEAD_BEEF_CAFEu64, 0, 0].into_iter());
647        assert_el_eq!(r, padded, full);
648    }
649}