Skip to main content

vstd/
endian.rs

1/*!
2Reasoning about representations of non-negative numbers with endianness, including conversion between bases.
3*/
4use crate::vstd::arithmetic::div_mod::*;
5use crate::vstd::arithmetic::mul::*;
6use crate::vstd::arithmetic::power::*;
7use crate::vstd::arithmetic::power2::*;
8use crate::vstd::calc_macro::*;
9use crate::vstd::group_vstd_default;
10use crate::vstd::layout;
11use crate::vstd::prelude::*;
12use core::marker::PhantomData;
13
14verus! {
15
16broadcast use group_vstd_default;
17
18/// Represents a base with value [`Self::base()`].
19pub trait Base {
20    spec fn base() -> nat;
21
22    proof fn base_min()
23        ensures
24            Self::base() > 1,
25    ;
26}
27
28/// The exclusive upper bound on values that can be stored with `len` digits in base [`B::base()`].
29///
30/// [`B::base()`]: Base::base
31pub open spec fn base_upper_bound_excl<B: Base>(len: nat) -> int {
32    pow(B::base() as int, len)
33}
34
35/// Represents a base which is a power of 2 specified by [`Self::bits()`].
36pub trait BasePow2: Base {
37    spec fn bits() -> nat;
38
39    proof fn bits_to_base()
40        ensures
41            Self::bits() > 1,
42            Self::base() == pow2(Self::bits()),
43    ;
44}
45
46/// Represents a base for which each digit of `BIG` converts to multiple digits in this base.
47pub trait CompatibleSmallerBaseFor<BIG: BasePow2>: BasePow2 {
48    proof fn compatible()
49        ensures
50            BIG::bits() > Self::bits() && BIG::bits() % Self::bits() == 0,
51    ;
52}
53
54/// Represents little-endian or big-endian interpretation.
55pub enum Endian {
56    Little,
57    Big,
58}
59
60/// Abstracts the endianness of the currently executing hardware.
61/// Use this when you want to use the same endianness everywhere, but don't wish to specify which endianness is used.
62pub uninterp spec fn endianness() -> Endian;
63
64/// Provides either little-endian or big-endian interpretation of a sequence of numbers with a given base. This interpretation may have any number of leading zeros.
65/// With little-endian, the first digit is the least significant position; the last digit is the most significant position.
66/// With big-endian, the last digit of a sequence is the least significant position; the first digit is the most significant position.
67#[verifier::ext_equal]
68pub struct EndianNat<B: Base> {
69    pub endian: Endian,
70    pub digits: Seq<int>,
71    pub phantom: core::marker::PhantomData<B>,
72}
73
74impl<B: Base> EndianNat<B> {
75    /// True when all numbers in `s` are valid digits in the given base.
76    pub open spec fn in_bounds(s: Seq<int>) -> bool {
77        forall|i| 0 <= i < s.len() ==> 0 <= #[trigger] s[i] < B::base()
78    }
79
80    /// True when all of `self.digits` are valid digits in the given base.
81    pub open spec fn wf(self) -> bool {
82        Self::in_bounds(self.digits)
83    }
84
85    /// Creates a new `EndianNat` with the given digits and endianness.
86    pub open spec fn new(endian: Endian, digits: Seq<int>) -> Self
87        recommends
88            Self::in_bounds(digits),
89    {
90        EndianNat { endian, digits, phantom: PhantomData }
91    }
92
93    /// Creates a new `EndianNat` with the given digits and the default endianness ([`endianness()`]).
94    pub open spec fn new_default(digits: Seq<int>) -> Self
95        recommends
96            Self::in_bounds(digits),
97    {
98        EndianNat { endian: endianness(), digits, phantom: PhantomData }
99    }
100
101    /// Number of digits in this `EndianNat`.
102    pub open spec fn len(self) -> nat {
103        self.digits.len()
104    }
105
106    /// The `i`th digit in this `EndianNat`.
107    pub open spec fn index(self, i: int) -> int
108        recommends
109            0 <= i < self.digits.len(),
110    {
111        self.digits[i]
112    }
113
114    #[verifier::inline]
115    pub open spec fn spec_index(self, i: int) -> int
116        recommends
117            0 <= i < self.digits.len(),
118    {
119        self.index(i)
120    }
121
122    /// The first digit in this `EndianNat`. Ignores endianness.
123    pub open spec fn first(self) -> nat
124        recommends
125            self.digits.len() > 0,
126    {
127        self.digits.first() as nat
128    }
129
130    /// The last digit in this `EndianNat`. Ignores endianness.
131    pub open spec fn last(self) -> nat
132        recommends
133            self.digits.len() > 0,
134    {
135        self.digits.last() as nat
136    }
137
138    /// The least significant digit in this `EndianNat`.
139    pub open spec fn least(self) -> nat
140        recommends
141            self.digits.len() > 0,
142    {
143        match self.endian {
144            Endian::Little => self.first(),
145            Endian::Big => self.last(),
146        }
147    }
148
149    /// The most significant digit in this `EndianNat`.
150    pub open spec fn most(self) -> nat
151        recommends
152            self.digits.len() > 0,
153    {
154        match self.endian {
155            Endian::Little => self.last(),
156            Endian::Big => self.first(),
157        }
158    }
159
160    /// Constructs an `EndianNat` by skipping the first `n` digits of the original `EndianNat`. Ignores endianness.
161    pub open spec fn skip(self, n: nat) -> Self
162        recommends
163            0 <= n <= self.digits.len(),
164    {
165        EndianNat { endian: self.endian, digits: self.digits.skip(n as int), phantom: self.phantom }
166    }
167
168    /// Constructs an `EndianNat` by taking only the first `n` digits of the original `EndianNat`. Ignores endianness.
169    pub open spec fn take(self, n: nat) -> Self
170        recommends
171            0 <= n <= self.digits.len(),
172    {
173        EndianNat { endian: self.endian, digits: self.digits[..n], phantom: self.phantom }
174    }
175
176    #[verifier::inline]
177    pub open spec fn spec_index_range_from<N: Integer>(self, n: N) -> Self
178        recommends
179            0 <= n as int <= self.digits.len(),
180    {
181        self.skip(n as nat)
182    }
183
184    #[verifier::inline]
185    pub open spec fn spec_index_range_to<N: Integer>(self, n: N) -> Self
186        recommends
187            0 <= n as int <= self.digits.len(),
188    {
189        self.take(n as nat)
190    }
191
192    /// Constructs an `EndianNat` by skipping the least significant `n` digits of the original `EndianNat`.
193    pub open spec fn skip_least(self, n: nat) -> Self
194        recommends
195            0 <= n <= self.digits.len(),
196    {
197        match self.endian {
198            Endian::Little => self[n..],
199            Endian::Big => self[..self.len() - n],
200        }
201    }
202
203    /// Constructs an `EndianNat` by skipping the most significant `n` digits of the original `EndianNat`.
204    pub open spec fn skip_most(self, n: nat) -> Self
205        recommends
206            0 <= n <= self.digits.len(),
207    {
208        match self.endian {
209            Endian::Little => self[..self.len() - n],
210            Endian::Big => self[n..],
211        }
212    }
213
214    /// Constructs an `EndianNat` by taking only the least significant `n` digits of the original `EndianNat`.
215    pub open spec fn take_least(self, n: nat) -> Self
216        recommends
217            0 <= n <= self.digits.len(),
218    {
219        match self.endian {
220            Endian::Little => self[..n],
221            Endian::Big => self[self.len() - n..],
222        }
223    }
224
225    /// Constructs an `EndianNat` by taking only the most significant `n` digits of the original `EndianNat`.
226    pub open spec fn take_most(self, n: nat) -> Self
227        recommends
228            0 <= n <= self.digits.len(),
229    {
230        match self.endian {
231            Endian::Little => self[self.len() - n..],
232            Endian::Big => self[..n],
233        }
234    }
235
236    /// Constructs an `EndianNat` by dropping the first digit of the original `EndianNat`. Ignores endianness.
237    pub open spec fn drop_first(self) -> Self
238        recommends
239            self.len() > 0,
240    {
241        EndianNat { endian: self.endian, digits: self.digits.drop_first(), phantom: self.phantom }
242    }
243
244    /// Constructs an `EndianNat` by dropping the last digit of the original `EndianNat`. Ignores endianness.
245    pub open spec fn drop_last(self) -> Self
246        recommends
247            self.len() > 0,
248    {
249        EndianNat { endian: self.endian, digits: self.digits.drop_last(), phantom: self.phantom }
250    }
251
252    /// Constructs an `EndianNat` by dropping the least significant digit of the original `EndianNat`.
253    pub open spec fn drop_least(self) -> Self
254        recommends
255            self.len() > 0,
256    {
257        match self.endian {
258            Endian::Little => self.drop_first(),
259            Endian::Big => self.drop_last(),
260        }
261    }
262
263    /// Constructs an `EndianNat` by dropping the most significant digits of the original `EndianNat`.
264    pub open spec fn drop_most(self) -> Self
265        recommends
266            self.len() > 0,
267    {
268        match self.endian {
269            Endian::Little => self.drop_last(),
270            Endian::Big => self.drop_first(),
271        }
272    }
273
274    /// Constructs an `EndianNat` by appending the digits of `other` to the end of the digits of `self`. Ignores endianness.
275    pub open spec fn append(self, other: Self) -> Self
276        recommends
277            self.endian == other.endian,
278    {
279        EndianNat::new(self.endian, self.digits + other.digits)
280    }
281
282    /// Constructs an `EndianNat` by appending the digits of `other` to the digits of `self` in the least significant position
283    /// (i.e., `other` becomes the least significant bits).
284    pub open spec fn append_least(self, other: Self) -> Self
285        recommends
286            self.endian == other.endian,
287    {
288        match self.endian {
289            Endian::Little => other.append(self),
290            Endian::Big => self.append(other),
291        }
292    }
293
294    /// Constructs an `EndianNat` by appending the digits of `other` to the digits of `self` in the most significant position
295    /// (i.e., `other` becomes the most significant bits).
296    pub open spec fn append_most(self, other: Self) -> Self
297        recommends
298            self.endian == other.endian,
299    {
300        match self.endian {
301            Endian::Little => self.append(other),
302            Endian::Big => other.append(self),
303        }
304    }
305
306    /// Constructs an `EndianNat` by appending `n` to the front of the digits of `self`. Ignores endianness.
307    pub open spec fn push_first(self, n: int) -> Self
308        recommends
309            n < B::base(),
310    {
311        EndianNat { endian: self.endian, digits: seq![n].add(self.digits), phantom: self.phantom }
312    }
313
314    /// Constructs an `EndianNat` by appending `n` to the end of the digits of `self`. Ignores endianness.
315    pub open spec fn push_last(self, n: int) -> Self
316        recommends
317            n < B::base(),
318    {
319        EndianNat { endian: self.endian, digits: self.digits.push(n), phantom: self.phantom }
320    }
321
322    /// Constructs an `EndianNat` by appending `n` to the least significant position in the digits of `self`.
323    pub open spec fn push_least(self, n: int) -> Self
324        recommends
325            n < B::base(),
326    {
327        match self.endian {
328            Endian::Little => self.push_first(n),
329            Endian::Big => self.push_last(n),
330        }
331    }
332
333    /// Constructs an `EndianNat` by appending `n` to the most significant position in the digits of `self`.
334    pub open spec fn push_most(self, n: int) -> Self
335        recommends
336            n < B::base(),
337    {
338        match self.endian {
339            Endian::Little => self.push_last(n),
340            Endian::Big => self.push_first(n),
341        }
342    }
343
344    /// Converts an `EndianNat` to the natural number that it represents, processing the digits from least significant to most significant, hiding the details of endianness.
345    #[verifier::opaque]
346    pub open spec fn to_nat(self) -> nat
347        decreases self.len(),
348    {
349        if self.len() == 0 {
350            0
351        } else {
352            self.drop_least().to_nat() * B::base() + self.least()
353        }
354    }
355
356    pub broadcast proof fn to_nat_properties(self)
357        requires
358            self.wf(),
359        ensures
360            #[trigger] self.to_nat() < base_upper_bound_excl::<B>(self.len()),
361        decreases self.len(),
362    {
363        reveal(EndianNat::to_nat);
364        reveal(pow);
365        if self.len() == 0 {
366        } else {
367            self.drop_least().to_nat_properties();
368
369            calc! {
370                (<)
371                self.to_nat(); (==) {}
372                self.drop_least().to_nat() * B::base() + self.least(); (<) {}
373                self.drop_least().to_nat() * B::base() + B::base(); (<=) {
374                    broadcast use lemma_mul_inequality, lemma_mul_is_distributive_sub_other_way;
375
376                    assert((base_upper_bound_excl::<B>((self.len() - 1) as nat) - 1)
377                        * B::base() as nat == (base_upper_bound_excl::<B>((self.len() - 1) as nat)
378                        * B::base() - B::base()) as nat);
379                }
380                (base_upper_bound_excl::<B>((self.len() - 1) as nat) * B::base() - B::base()
381                    + B::base()) as nat; (==) {
382                    broadcast use lemma_pow1;
383
384                }
385                (base_upper_bound_excl::<B>((self.len() - 1) as nat) * pow(
386                    B::base() as int,
387                    1,
388                )) as nat; (==) {
389                    broadcast use lemma_pow_sub_add_cancel;
390
391                }
392                base_upper_bound_excl::<B>(self.len()) as nat;
393            }
394        }
395    }
396
397    pub proof fn to_nat_append_least(endian: Self, other: Self)
398        requires
399            endian.wf(),
400            other.wf(),
401            endian.endian == other.endian,
402        ensures
403            endian.append_least(other).to_nat() == (endian.to_nat() * base_upper_bound_excl::<B>(
404                other.len(),
405            )) + other.to_nat(),
406        decreases other.len(),
407    {
408        reveal(EndianNat::to_nat);
409        reveal(pow);
410        if other.len() == 0 {
411        } else {
412            let least = other.least();
413            let rest = other.drop_least();
414            Self::to_nat_append_least(endian, rest);
415            assert(endian.append_least(other).drop_least() =~= endian.append_least(rest));
416            assert(endian.to_nat() * base_upper_bound_excl::<B>(rest.len()) * B::base()
417                == endian.to_nat() * base_upper_bound_excl::<B>(rest.len() + 1)) by {
418                broadcast use lemma_pow1;
419
420                assert(B::base() == pow(B::base() as int, 1));
421                broadcast use lemma_pow_adds;
422
423                assert(pow(B::base() as int, rest.len() + 1) == pow(B::base() as int, rest.len())
424                    * pow(B::base() as int, 1));
425                assert(endian.to_nat() * base_upper_bound_excl::<B>(rest.len()) * B::base()
426                    == endian.to_nat() * base_upper_bound_excl::<B>(rest.len() + 1))
427                    by (nonlinear_arith)
428                    requires
429                        base_upper_bound_excl::<B>(rest.len()) * B::base()
430                            == base_upper_bound_excl::<B>(rest.len() + 1),
431                ;
432            }
433            assert(endian.append_least(other).to_nat() == (endian.to_nat()
434                * base_upper_bound_excl::<B>(other.len())) + other.to_nat()) by (nonlinear_arith)
435                requires
436                    endian.append_least(other).to_nat() == endian.append_least(rest).to_nat()
437                        * B::base() + least,
438                    other.to_nat() == rest.to_nat() * B::base() + least,
439                    endian.append_least(rest).to_nat() == (endian.to_nat()
440                        * base_upper_bound_excl::<B>(rest.len())) + rest.to_nat(),
441                    endian.to_nat() * base_upper_bound_excl::<B>(rest.len()) * B::base()
442                        == endian.to_nat() * base_upper_bound_excl::<B>(rest.len() + 1),
443                    other.len() == rest.len() + 1,
444            ;
445        }
446    }
447
448    /// Converts a natural number to an `EndianNat` representation with the specified number of digits (`len`) and default endianness ([`endianness()`]).
449    /// `n` should be less than the maximum number that can be represented in `len` digits in this base.
450    pub open spec fn from_nat(n: nat, len: nat) -> Self
451        recommends
452            n < base_upper_bound_excl::<B>(len),
453        decreases len,
454    {
455        if len == 0 {
456            EndianNat { digits: Seq::empty(), endian: endianness(), phantom: PhantomData }
457        } else {
458            let least = (n % B::base()) as int;
459            let rest = n / B::base();
460            let rest_endian = Self::from_nat(rest, (len - 1) as nat);
461            rest_endian.push_least(least)
462        }
463    }
464
465    proof fn base_upper_bound_excl_len(n: nat, len: nat)
466        requires
467            n < base_upper_bound_excl::<B>(len),
468            0 < len,
469        ensures
470            n / B::base() < base_upper_bound_excl::<B>((len - 1) as nat),
471    {
472        reveal(pow);
473        assert(n / B::base() < base_upper_bound_excl::<B>((len - 1) as nat)) by (nonlinear_arith)
474            requires
475                n < B::base() * base_upper_bound_excl::<B>((len - 1) as nat),
476                B::base() > 0,
477        ;
478    }
479
480    pub broadcast proof fn from_nat_properties(n: nat, len: nat)
481        requires
482            n < base_upper_bound_excl::<B>(len),
483        ensures
484            #![trigger Self::from_nat(n, len)]
485            Self::from_nat(n, len).len() == len,
486            Self::from_nat(n, len).endian == endianness(),
487            Self::from_nat(n, len).wf(),
488        decreases len,
489    {
490        if len == 0 {
491        } else {
492            Self::base_upper_bound_excl_len(n, len);
493            let endian_nat = Self::from_nat(n, len);
494            let least = endian_nat.least();
495            Self::from_nat_properties(n / B::base(), (len - 1) as nat);
496            B::base_min();
497            assert(least < B::base()) by (nonlinear_arith)
498                requires
499                    B::base() > 0,
500                    least == (n % B::base()),
501            ;
502        }
503    }
504
505    /// Ensures that performing [`from_nat`] and then [`to_nat`] results in the original value,
506    /// provided that the value can be encoded in the given base in the given number of digits.
507    ///
508    /// [`from_nat`]: EndianNat::from_nat
509    /// [`to_nat`]: EndianNat::to_nat
510    pub broadcast proof fn from_nat_to_nat(n: nat, len: nat)
511        requires
512            n < base_upper_bound_excl::<B>(len),
513        ensures
514            #[trigger] Self::from_nat(n, len).to_nat() == n,
515        decreases Self::from_nat(n, len).len(),
516    {
517        reveal(pow);
518        reveal(EndianNat::to_nat);
519        if Self::from_nat(n, len).len() == 0 {
520        } else {
521            B::base_min();
522            let endian_nat = Self::from_nat(n, len);
523            let least = endian_nat.least();
524            let rest = endian_nat.drop_least();
525            assert(rest =~= Self::from_nat(n / B::base(), (len - 1) as nat));
526            Self::base_upper_bound_excl_len(n, len);
527            Self::from_nat_to_nat(n / B::base(), (len - 1) as nat);
528            assert((n % B::base()) + (n / B::base()) * B::base() == n) by (nonlinear_arith)
529                requires
530                    B::base() > 0,
531            ;
532        }
533    }
534
535    /// Ensures that performing [`to_nat`] and then [`from_nat`] results in the original `EndianNat`.
536    ///
537    /// [`from_nat`]: EndianNat::from_nat
538    /// [`to_nat`]: EndianNat::to_nat
539    pub broadcast proof fn to_nat_from_nat(endian_nat: Self)
540        requires
541            endian_nat.wf(),
542            endian_nat.endian == endianness(),
543        ensures
544            #[trigger] Self::from_nat(endian_nat.to_nat(), endian_nat.len()) == endian_nat,
545        decreases endian_nat.len(),
546    {
547        reveal(pow);
548        reveal(EndianNat::to_nat);
549        if endian_nat.len() == 0 {
550        } else {
551            let n = endian_nat.to_nat();
552            let least = endian_nat.least();
553            let rest = endian_nat.drop_least();
554            Self::to_nat_from_nat(rest);
555            assert(least == n % B::base() && rest.to_nat() == n / B::base()) by {
556                assert(n == rest.to_nat() * B::base() + least);
557                assert(least < B::base());
558                lemma_fundamental_div_mod_converse(
559                    n as int,
560                    B::base() as int,
561                    rest.to_nat() as int,
562                    least as int,
563                );
564            }
565        }
566    }
567
568    pub broadcast proof fn to_nat_injective(n1: Self, n2: Self)
569        requires
570            n1.wf(),
571            n2.wf(),
572            n1.endian == endianness(),
573            n2.endian == endianness(),
574            n1.len() == n2.len(),
575            #[trigger] n1.to_nat() == #[trigger] n2.to_nat(),
576        ensures
577            n1 == n2,
578    {
579        broadcast use EndianNat::to_nat_from_nat;
580
581        assert(Self::from_nat(n1.to_nat(), n1.len()) == n1);
582        assert(Self::from_nat(n2.to_nat(), n2.len()) == n2);
583    }
584
585    pub broadcast proof fn from_nat_injective(n1: nat, len1: nat, n2: nat, len2: nat)
586        requires
587            n1 < base_upper_bound_excl::<B>(len1),
588            n2 < base_upper_bound_excl::<B>(len2),
589            #[trigger] Self::from_nat(n1, len1) == #[trigger] Self::from_nat(n2, len2),
590        ensures
591            n1 == n2,
592    {
593        broadcast use EndianNat::from_nat_to_nat;
594
595        assert(Self::from_nat(n1, len1).to_nat() == n1);
596        assert(Self::from_nat(n2, len2).to_nat() == n2);
597    }
598
599    /// Converts an `EndianNat` to the natural number that it represents, processing the digits from most significant to least significant, hiding the details of endianness.
600    #[verifier::opaque]
601    pub open spec fn to_nat_most(self) -> nat
602        decreases self.len(),
603    {
604        if self.len() == 0 {
605            0
606        } else {
607            (self.drop_most().to_nat_most() + self.most() * pow(
608                B::base() as int,
609                (self.len() - 1) as nat,
610            )) as nat
611        }
612    }
613
614    /// Ensures that [`to_nat`] and [`to_nat_most`] agree.
615    ///
616    /// [`to_nat`]: EndianNat::to_nat
617    /// [`to_nat_most`]: EndianNat::to_nat_most
618    pub broadcast proof fn to_nat_eq_to_nat_most(self)
619        requires
620            self.wf(),
621        ensures
622            #[trigger] self.to_nat() == #[trigger] self.to_nat_most(),
623        decreases self.len(),
624    {
625        reveal(EndianNat::to_nat);
626        reveal(EndianNat::to_nat_most);
627        if self.len() == 0 {
628        } else {
629            if self.drop_most().len() == 0 {
630                calc! {
631                    (==)
632                    self.to_nat_most(); {
633                        assert(self.drop_most().to_nat_most() == 0);
634                        assert(self.to_nat_most() == (self.most() * pow(
635                            B::base() as int,
636                            (self.len() - 1) as nat,
637                        )) as nat);
638                    }
639                    (self.most() * pow(B::base() as int, (self.len() - 1) as nat)) as nat; {
640                        reveal(pow);
641                        assert((self.most() * pow(B::base() as int, (self.len() - 1) as nat))
642                            == self.most()) by (nonlinear_arith)
643                            requires
644                                pow(B::base() as int, (self.len() - 1) as nat) == 1,
645                                self.most() >= 0,
646                        ;
647                    }
648                    self.most(); {}
649                    self.least(); {
650                        assert(self.drop_least().to_nat() == 0);
651                        assert(0 * B::base() == 0);
652                    }
653                    self.to_nat();
654                };
655            } else {
656                calc! {
657                    (==)
658                    self.to_nat_most() as int; {}
659                    ((self.drop_most().to_nat_most() + self.most() * pow(
660                        B::base() as int,
661                        (self.len() - 1) as nat,
662                    )) as nat) as int; {
663                        assert(self.most() >= 0);
664                        assert(self.len() > 1);
665                        lemma_pow_positive(B::base() as int, (self.len() - 1) as nat);
666                        assert(pow(B::base() as int, (self.len() - 1) as nat) >= 0);
667                    }
668                    self.drop_most().to_nat_most() + self.most() * pow(
669                        B::base() as int,
670                        (self.len() - 1) as nat,
671                    ); {
672                        self.drop_most().to_nat_eq_to_nat_most();
673                    }
674                    self.drop_most().to_nat() + self.most() * pow(
675                        B::base() as int,
676                        (self.len() - 1) as nat,
677                    ); {}
678                    self.drop_most().drop_least().to_nat() * B::base() + self.drop_most().least()
679                        + self.most() * pow(B::base() as int, (self.len() - 1) as nat); {
680                        self.drop_most().drop_least().to_nat_eq_to_nat_most();
681                    }
682                    self.drop_most().drop_least().to_nat_most() * B::base()
683                        + self.drop_most().least() + self.most() * pow(
684                        B::base() as int,
685                        (self.len() - 1) as nat,
686                    ); {
687                        assert(self.drop_most().drop_least() == self.drop_least().drop_most());
688                    }
689                    self.drop_least().drop_most().to_nat_most() * B::base() + self.least()
690                        + self.most() * pow(B::base() as int, (self.len() - 1) as nat); {
691                        assert(self.most() * pow(B::base() as int, (self.len() - 2) as nat)
692                            * B::base() == self.most() * (pow(
693                            B::base() as int,
694                            (self.len() - 2) as nat,
695                        ) * B::base())) by {
696                            broadcast use crate::vstd::arithmetic::mul::lemma_mul_is_associative;
697
698                        }
699                        assert(pow(B::base() as int, (self.len() - 1) as nat) == pow(
700                            B::base() as int,
701                            (self.len() - 2) as nat,
702                        ) * B::base()) by {
703                            assert(B::base() == pow(B::base() as int, 1)) by {
704                                lemma_pow1(B::base() as int);
705                            }
706                            lemma_pow_adds(B::base() as int, (self.len() - 2) as nat, 1);
707                        }
708                    }
709                    self.drop_least().drop_most().to_nat_most() * B::base() + (self.most() * pow(
710                        B::base() as int,
711                        (self.len() - 2) as nat,
712                    )) * B::base() + self.least(); {
713                        lemma_mul_is_distributive_add_other_way(
714                            B::base() as int,
715                            self.drop_least().drop_most().to_nat_most() as int,
716                            self.most() * pow(B::base() as int, (self.len() - 2) as nat),
717                        );
718                    }
719                    (self.drop_least().drop_most().to_nat_most() + self.most() * pow(
720                        B::base() as int,
721                        (self.len() - 2) as nat,
722                    )) * B::base() + self.least(); {
723                        assert((self.drop_least().drop_most().to_nat_most() + self.most() * pow(
724                            B::base() as int,
725                            (self.len() - 2) as nat,
726                        )) == self.drop_least().to_nat_most()) by {
727                            lemma_pow_positive(B::base() as int, (self.len() - 2) as nat);
728                        };
729                    }
730                    (self.drop_least().to_nat_most() * B::base() + self.least()) as int; {
731                        self.drop_least().to_nat_eq_to_nat_most();
732                    }
733                    self.to_nat() as int;
734                }
735            }
736        }
737    }
738}
739
740// /////////////////////////////////////////////////// //
741//         Conversion Routines                         //
742// /////////////////////////////////////////////////// //
743impl<B: Base> EndianNat<B> {
744    /// [`B::base()`] to the power of `exp()` is [`BIG::base()`].
745    /// In other words, `exp()` is the number of digits in base `B` that correspond to a single digit in base `BIG`.
746    ///
747    /// [`B::base()`]: Base::base
748    /// [`BIG::base()`]: Base::base
749    pub open spec fn exp<BIG>() -> nat where BIG: BasePow2, B: CompatibleSmallerBaseFor<BIG> {
750        BIG::bits() / B::bits()
751    }
752
753    pub broadcast proof fn exp_properties<BIG>() where
754        BIG: BasePow2,
755        B: CompatibleSmallerBaseFor<BIG>,
756
757        ensures
758            #![trigger Self::exp()]
759            base_upper_bound_excl::<B>(Self::exp()) == BIG::base(),
760            Self::exp() > 0,
761    {
762        broadcast use crate::vstd::arithmetic::div_mod::group_div_basics;
763
764        assert(forall|x| x != 0 ==> #[trigger] (0int / x) == 0);
765        broadcast use crate::vstd::arithmetic::power::lemma_pow_multiplies;
766
767        B::bits_to_base();
768
769        calc! {
770            (==)
771            BIG::bits(); {
772                crate::vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
773                    BIG::bits() as int,
774                    B::bits() as int,
775                );
776            }
777            B::bits() * (BIG::bits() / B::bits()) + (BIG::bits() % B::bits()); {
778                B::compatible();
779            }
780            B::bits() * (BIG::bits() / B::bits()); {}
781            B::bits() * Self::exp();
782        }
783        calc! {
784            (==)
785            base_upper_bound_excl::<B>(Self::exp()); {
786                crate::vstd::arithmetic::power::lemma_pow_multiplies(2, B::bits(), Self::exp());
787                crate::vstd::arithmetic::power2::lemma_pow2(B::bits());
788            }
789            pow(2, B::bits() * Self::exp()) as int; {}
790            pow(2, BIG::bits()) as int; {
791                crate::vstd::arithmetic::power2::lemma_pow2(BIG::bits());
792            }
793            pow2(BIG::bits()) as int; {
794                BIG::bits_to_base();
795            }
796            BIG::base() as int;
797        }
798
799        B::compatible();
800        assert((BIG::bits() / B::bits()) != 0);
801        BIG::bits_to_base();
802
803    }
804
805    /// Converts an `EndianNat` representation in the "big" base `BIG` to an `EndianNat` representation in the "small" base `B`.
806    /// The result represents the same non-negative number as the original.
807    pub open spec fn from_big<BIG>(n: EndianNat<BIG>) -> Self where
808        BIG: BasePow2,
809        B: CompatibleSmallerBaseFor<BIG>,
810
811        decreases n.len(),
812    {
813        if n.len() == 0 {
814            EndianNat::new(n.endian, Seq::empty())
815        } else {
816            Self::from_big(n.drop_least()).append_least(EndianNat::from_nat(n.least(), Self::exp()))
817        }
818    }
819
820    pub broadcast proof fn from_big_properties<BIG>(n: EndianNat<BIG>) where
821        BIG: BasePow2,
822        B: CompatibleSmallerBaseFor<BIG>,
823
824        requires
825            n.wf(),
826            n.endian == endianness(),
827        ensures
828            #![trigger Self::from_big(n)]
829            Self::from_big(n).wf(),
830            Self::from_big(n).endian == endianness(),
831            Self::from_big(n).len() == n.len() * Self::exp(),
832            Self::from_big(n).to_nat() == n.to_nat(),
833        decreases n.len(),
834    {
835        reveal(EndianNat::to_nat);
836        broadcast use EndianNat::exp_properties;
837
838        if n.len() == 0 {
839        } else {
840            let least = n.least();
841            let rest = n.drop_least();
842            Self::from_big_properties(rest);
843            Self::from_nat_properties(least, Self::exp());
844            assert(Self::from_big(n).len() == n.len() * Self::exp()) by (nonlinear_arith)
845                requires
846                    Self::from_big(n).len() == (n.len() - 1) * Self::exp() + Self::exp(),
847            ;
848            let small_least = EndianNat::from_nat(least, Self::exp());
849            let small_rest = Self::from_big(rest);
850            Self::to_nat_append_least(small_rest, small_least);
851            Self::from_nat_to_nat(least, Self::exp());
852        }
853    }
854
855    /// Converts an `EndianNat` representation in the "small" base `B` to an `EndianNat` representation in the "big" base `BIG`.
856    /// The result represents the same non-negative number as the original.
857    #[verifier::opaque]
858    pub open spec fn to_big<BIG>(n: EndianNat<B>) -> EndianNat<BIG> where
859        BIG: BasePow2,
860        B: CompatibleSmallerBaseFor<BIG>,
861
862        recommends
863            n.len() % Self::exp() == 0,
864        decreases n.len(),
865        when n.len() % Self::exp() == 0
866        via Self::to_big_decreases
867    {
868        if n.len() == 0 {
869            EndianNat::new(n.endian, Seq::empty())
870        } else {
871            Self::to_big(n.skip_least(Self::exp())).append_least(
872                EndianNat::new(n.endian, seq![n.take_least(Self::exp()).to_nat() as int]),
873            )
874        }
875    }
876
877    #[via_fn]
878    proof fn to_big_decreases<BIG>(n: EndianNat<B>) where
879        BIG: BasePow2,
880        B: CompatibleSmallerBaseFor<BIG>,
881     {
882        broadcast use EndianNat::exp_properties;
883
884        if n.len() != 0 {
885            assert(Self::exp() <= n.len()) by {
886                broadcast use crate::vstd::arithmetic::div_mod::lemma_mod_is_zero;
887
888            }
889            assert(n.skip_least(Self::exp()).len() < n.len());
890        }
891    }
892
893    pub broadcast proof fn to_big_properties<BIG>(n: EndianNat<B>) where
894        BIG: BasePow2,
895        B: CompatibleSmallerBaseFor<BIG>,
896
897        requires
898            n.wf(),
899            n.endian == endianness(),
900            n.len() % Self::exp() == 0,
901        ensures
902            #![trigger Self::to_big(n)]
903            Self::to_big(n).wf(),
904            Self::to_big(n).endian == endianness(),
905            Self::to_big(n).len() == n.len() / Self::exp(),
906            Self::to_big(n).to_nat() == n.to_nat(),
907        decreases n.len(),
908    {
909        reveal(EndianNat::to_big);
910        reveal(EndianNat::to_nat);
911        broadcast use EndianNat::exp_properties;
912
913        if n.len() == 0 {
914        } else {
915            let least = n.take_least(Self::exp());
916            let rest = n.skip_least(Self::exp());
917            assert(n.len() >= Self::exp()) by (nonlinear_arith)
918                requires
919                    n.len() % Self::exp() == 0,
920                    Self::exp() > 0,
921                    n.len() > 0,
922            ;
923            assert(rest.len() % Self::exp() == 0) by {
924                lemma_mod_multiples_vanish(-1 as int, n.len() as int, Self::exp() as int);
925                assert(n.len() % Self::exp() == 0);
926                assert(rest.len() == n.len() - Self::exp() == -1 * Self::exp() + n.len());
927            }
928            Self::to_big_properties(rest);
929            Self::to_nat_properties(least);
930            let big_least = EndianNat::<BIG>::new(n.endian, seq![least.to_nat() as int]);
931            let big_rest = Self::to_big(rest);
932            assert(Self::to_big(n).to_nat() == n.to_nat()) by {
933                assert(big_rest.append_least(big_least).to_nat() == (big_rest.to_nat()
934                    * base_upper_bound_excl::<BIG>(big_least.len())) + big_least.to_nat()) by {
935                    EndianNat::<BIG>::to_nat_append_least(big_rest, big_least);
936                }
937                assert(big_least.to_nat() == least.to_nat()) by {
938                    assert(big_least.drop_least().to_nat() * BIG::base() + big_least.least()
939                        == least.to_nat()) by {
940                        assert(big_least.least() == least.to_nat());
941                        assert(big_least.drop_least().to_nat() == 0);
942                        lemma_mul_basics(BIG::base() as int);
943                    }
944                }
945                assert(n.to_nat() == (rest.to_nat() * base_upper_bound_excl::<B>(Self::exp()))
946                    + least.to_nat()) by {
947                    assert(n =~= rest.append_least(least));
948                    EndianNat::<B>::to_nat_append_least(rest, least);
949                }
950                assert(base_upper_bound_excl::<B>(Self::exp()) == base_upper_bound_excl::<BIG>(
951                    big_least.len(),
952                )) by {
953                    broadcast use lemma_pow1;
954
955                }
956            }
957            assert(Self::to_big(n).len() == n.len() / Self::exp()) by {
958                assert(big_rest.len() == rest.len() / Self::exp());
959                assert(Self::to_big(n).len() == big_rest.len() + 1);
960                assert(n.len() == rest.len() + Self::exp());
961                assert(Self::exp() > 0);
962                lemma_div_plus_one(rest.len() as int, Self::exp() as int);
963            }
964        }
965
966    }
967
968    /// Ensures that performing [`to_big`] and then [`from_big`] results in the original `EndianNat<B>`.
969    ///
970    /// [`to_big`]: EndianNat::to_big
971    /// [`from_big`]: EndianNat::from_big
972    pub broadcast proof fn to_big_from_big<BIG>(n: EndianNat<B>) where
973        BIG: BasePow2,
974        B: CompatibleSmallerBaseFor<BIG>,
975
976        requires
977            n.wf(),
978            n.endian == endianness(),
979            n.len() % Self::exp() == 0,
980        ensures
981            #[trigger] Self::from_big(Self::to_big(n)) == n,
982        decreases n.len(),
983    {
984        reveal(EndianNat::to_big);
985        broadcast use EndianNat::exp_properties;
986
987        if n.len() == 0 {
988        } else {
989            let least = n.take_least(Self::exp());
990            let rest = n.skip_least(Self::exp());
991            assert(n.len() >= Self::exp()) by (nonlinear_arith)
992                requires
993                    n.len() % Self::exp() == 0,
994                    Self::exp() > 0,
995                    n.len() > 0,
996            ;
997            // `rest.len() == n.len() - exp` and `n.len() % exp == 0`, so
998            // `rest.len() % exp == (-exp + n.len()) % exp == n.len() % exp == 0`.
999            lemma_mod_sub_multiples_vanish(n.len() as int, Self::exp() as int);
1000            assert(rest.len() % Self::exp() == 0);
1001            Self::to_big_from_big(rest);
1002            let big = Self::to_big(n);
1003            assert(big =~= Self::to_big(rest).append_least(
1004                EndianNat::new(n.endian, seq![least.to_nat() as int]),
1005            ));
1006            let big_least = big.least();
1007            let big_rest = big.drop_least();
1008            assert(big_rest =~= Self::to_big(rest));
1009            assert(Self::from_big(big_rest) == rest);
1010            Self::to_nat_from_nat(least);
1011            assert(EndianNat::<B>::from_nat(big_least, Self::exp()) == least);
1012        }
1013    }
1014
1015    /// Ensures that performing [`from_big`] and then [`to_big`] results in the original `EndianNat<BIG>`.
1016    ///
1017    /// [`to_big`]: EndianNat::to_big
1018    /// [`from_big`]: EndianNat::from_big
1019    #[verifier::spinoff_prover]
1020    #[verifier::rlimit(20)]
1021    pub broadcast proof fn from_big_to_big<BIG>(n: EndianNat<BIG>) where
1022        BIG: BasePow2,
1023        B: CompatibleSmallerBaseFor<BIG>,
1024
1025        requires
1026            n.wf(),
1027            n.endian == endianness(),
1028        ensures
1029            #[trigger] Self::to_big(Self::from_big(n)) == n,
1030        decreases n.len(),
1031    {
1032        reveal(EndianNat::to_big);
1033        reveal(EndianNat::to_nat);
1034        broadcast use EndianNat::exp_properties;
1035
1036        if n.len() == 0 {
1037            Self::from_big_properties(n);
1038            let small = Self::from_big(n);
1039            assert(small.len() % Self::exp() == 0) by (nonlinear_arith)
1040                requires
1041                    small.len() == 0,
1042                    Self::exp() > 0,
1043            ;
1044            Self::to_big_properties(small);
1045            assert(Self::to_big(Self::from_big(n)).digits == n.digits);
1046            assert(Self::to_big(Self::from_big(n)).endian == n.endian);
1047        } else {
1048            let least = n.least();
1049            let rest = n.drop_least();
1050            Self::from_big_to_big(rest);
1051            Self::from_big_properties(rest);
1052
1053            let small = Self::from_big(n);
1054            let small_least = small.take_least(Self::exp());
1055            let small_rest = small.skip_least(Self::exp());
1056            Self::from_nat_properties(least, Self::exp());
1057            let from_nat_least = EndianNat::<B>::from_nat(least, Self::exp());
1058            assert(small_least =~= from_nat_least);
1059            EndianNat::<B>::from_nat_to_nat(least, Self::exp());
1060
1061            assert(small_rest =~= Self::from_big(rest));
1062            // `(rest.len() * exp) % exp == 0`
1063            lemma_mod_multiples_basic(rest.len() as int, Self::exp() as int);
1064            assert(small_rest.len() % Self::exp() == 0);
1065
1066            Self::from_big_properties(n);
1067            // `(n.len() * exp) % exp == 0`.  Use the dedicated lemma rather than
1068            // a bare `nonlinear_arith`, which times out under Z3 >= 4.13.
1069            lemma_mod_multiples_basic(n.len() as int, Self::exp() as int);
1070            assert(small.len() % Self::exp() == 0);
1071        }
1072    }
1073
1074    pub broadcast proof fn to_big_injective<BIG>(n1: EndianNat<B>, n2: EndianNat<B>) where
1075        BIG: BasePow2,
1076        B: CompatibleSmallerBaseFor<BIG>,
1077
1078        requires
1079            n1.wf(),
1080            n2.wf(),
1081            n1.endian == endianness(),
1082            n2.endian == endianness(),
1083            n1.len() % Self::exp() == 0,
1084            n2.len() % Self::exp() == 0,
1085            #[trigger] Self::to_big(n1) == #[trigger] Self::to_big(n2),
1086        ensures
1087            n1 == n2,
1088    {
1089        broadcast use EndianNat::to_big_from_big;
1090
1091        assert(Self::from_big(Self::to_big(n1)) == n1);
1092        assert(Self::from_big(Self::to_big(n2)) == n2);
1093    }
1094
1095    pub broadcast proof fn from_big_injective<BIG>(n1: EndianNat<BIG>, n2: EndianNat<BIG>) where
1096        BIG: BasePow2,
1097        B: CompatibleSmallerBaseFor<BIG>,
1098
1099        requires
1100            n1.wf(),
1101            n2.wf(),
1102            n1.endian == endianness(),
1103            n2.endian == endianness(),
1104            #[trigger] Self::from_big(n1) == #[trigger] Self::from_big(n2),
1105        ensures
1106            n1 == n2,
1107    {
1108        broadcast use EndianNat::from_big_to_big;
1109
1110        assert(Self::to_big(Self::from_big(n1)) == n1);
1111        assert(Self::to_big(Self::from_big(n2)) == n2);
1112    }
1113
1114    #[verifier::spinoff_prover]
1115    pub proof fn to_big_single<BIG>(x: EndianNat<B>) where
1116        BIG: BasePow2,
1117        B: CompatibleSmallerBaseFor<BIG>,
1118
1119        requires
1120            x.len() == Self::exp(),
1121            x.len() > 0,
1122        ensures
1123            Self::to_big(x).len() == 1,
1124            Self::to_big(x)[0] == x.to_nat(),
1125    {
1126        broadcast use EndianNat::exp_properties;
1127
1128        reveal(EndianNat::to_big);
1129        crate::vstd::arithmetic::div_mod::lemma_mod_self_0(Self::exp() as int);
1130        crate::vstd::arithmetic::div_mod::lemma_small_mod(0, Self::exp());
1131        assert(x.len() % Self::exp() == 0);
1132        assert(x.skip_least(Self::exp()).len() == 0);
1133        assert(x.skip_least(Self::exp()).len() % Self::exp() == 0);
1134        assert(x =~= x.take_least(Self::exp()));
1135        assert(Self::to_big(x) == Self::to_big(x.skip_least(Self::exp())).append_least(
1136            EndianNat::new(x.endian, seq![x.take_least(Self::exp()).to_nat() as int]),
1137        ));
1138    }
1139}
1140
1141/***** Functions involving both little and big endian *****/
1142
1143/// Converts a sequence of digits in base `B` in default endianness ([`endianness()`]) to an `EndianNat` in the larger base `BIG`.
1144pub open spec fn to_big_from_digits<BIG, B>(n: Seq<B>) -> EndianNat<BIG> where
1145    BIG: BasePow2,
1146    B: CompatibleSmallerBaseFor<BIG> + Integer,
1147 {
1148    EndianNat::<B>::to_big::<BIG>(EndianNat::<B>::new(endianness(), n.map(|i, d| d as int)))
1149}
1150
1151/***** Implementations and proofs for specific types *****/
1152
1153impl Base for u8 {
1154    open spec fn base() -> nat {
1155        u8::MAX as nat + 1
1156    }
1157
1158    proof fn base_min() {
1159    }
1160}
1161
1162impl Base for u64 {
1163    open spec fn base() -> nat {
1164        u64::MAX as nat + 1
1165    }
1166
1167    proof fn base_min() {
1168    }
1169}
1170
1171impl Base for usize {
1172    open spec fn base() -> nat {
1173        usize::MAX as nat + 1
1174    }
1175
1176    proof fn base_min() {
1177    }
1178}
1179
1180impl BasePow2 for u8 {
1181    open spec fn bits() -> nat {
1182        8
1183    }
1184
1185    proof fn bits_to_base() {
1186        crate::vstd::arithmetic::power2::lemma2_to64();
1187    }
1188}
1189
1190impl BasePow2 for u64 {
1191    open spec fn bits() -> nat {
1192        64
1193    }
1194
1195    proof fn bits_to_base() {
1196        crate::vstd::arithmetic::power2::lemma2_to64();
1197    }
1198}
1199
1200impl BasePow2 for usize {
1201    open spec fn bits() -> nat {
1202        usize::BITS as nat
1203    }
1204
1205    proof fn bits_to_base() {
1206        crate::vstd::arithmetic::power2::lemma2_to64();
1207    }
1208}
1209
1210impl CompatibleSmallerBaseFor<u64> for u8 {
1211    proof fn compatible() {
1212    }
1213}
1214
1215impl CompatibleSmallerBaseFor<usize> for u8 {
1216    proof fn compatible() {
1217    }
1218}
1219
1220pub broadcast group group_endian_nat_axioms {
1221    EndianNat::from_nat_properties,
1222    EndianNat::to_nat_properties,
1223    EndianNat::from_nat_to_nat,
1224    EndianNat::to_nat_from_nat,
1225    EndianNat::to_nat_injective,
1226    EndianNat::from_nat_injective,
1227    EndianNat::to_nat_eq_to_nat_most,
1228    EndianNat::exp_properties,
1229    EndianNat::to_big_properties,
1230    EndianNat::from_big_properties,
1231    EndianNat::from_big_to_big,
1232    EndianNat::to_big_from_big,
1233    EndianNat::to_big_injective,
1234    EndianNat::from_big_injective,
1235}
1236
1237} // verus!