Skip to main content

wasm4pm_compat/
nightly_foundry.rs

1//! Nightly foundry — zero-cost type-law surfaces derived from process-mining papers.
2//!
3//! Always compiled (the crate is nightly-only). No cfg gate, no `RUSTFLAGS`.
4//! This is an experimental staging area; the main type law lives in
5//! [`crate::law`], [`crate::petri`], [`crate::conformance`], [`crate::process_tree`],
6//! [`crate::powl`], [`crate::formats`], and [`crate::strict`].
7//!
8//! ## Four surfaces, four nightly features, four paper mappings
9//!
10//! | Surface | Feature | Paper |
11//! |---------|---------|-------|
12//! | [`petri_law`] | `generic_const_exprs` | Murata (1989) §2 incidence matrices W⁻, W⁺ |
13//! | [`powl_law`]  | `adt_const_params`    | Kourani (2505.07052) §3 POWL fragment kinds |
14//! | [`evidence_law`] | `min_specialization` | Blue River Dam — admitted vs raw label |
15//! | [`token_law`] | `portable_simd`       | Murata §2 enabling condition `∀p: M[p] ≥ W⁻[p][t]` |
16//!
17//! ## Zero-cost guarantee
18//!
19//! Every type in this module is `#[repr(transparent)]` over a fixed-size array
20//! or a `u32`, or is a zero-sized marker.  There is no heap allocation, no
21//! runtime dispatch, and no branch in the hot path.  The nightly features move
22//! paper-derived invariants into the *type system*, not into runtime machinery.
23//!
24//! [`petri_law`]: crate::nightly_foundry::petri_law
25//! [`powl_law`]: crate::nightly_foundry::powl_law
26//! [`evidence_law`]: crate::nightly_foundry::evidence_law
27//! [`token_law`]: crate::nightly_foundry::token_law
28
29// ─────────────────────────────────────────────────────────────────────────────
30// Surface 1: Bipartite Petri-net arc matrices  (generic_const_exprs)
31// Paper: Murata (1989) IEEE Proc. 77(4) "Petri Nets: Properties, Analysis …"
32//   §2: N = (P, T, F) is bipartite; arcs in F ⊆ (P×T) ∪ (T×P).
33//   W⁻: P×T→ℕ pre-incidence, W⁺: T×P→ℕ post-incidence.
34//   Enabling: ∀p: M[p] ≥ W⁻(p,t).  Firing: M'[p] = M[p]−W⁻(p,t)+W⁺(t,p).
35//
36// `generic_const_exprs` lets us write `[u8; P * T]` as a struct field —
37// the flat arc matrix is zero-cost and bipartite-direction-safe at the type level:
38//   PreMatrix<P, T>  ≠  PostMatrix<P, T>  (same count, opposite semantics).
39// ─────────────────────────────────────────────────────────────────────────────
40
41/// **Compile-pass law**: `Marking<P>::EMPTY` is a const-generic compile-time constant.
42///
43/// ```
44/// use wasm4pm_compat::nightly_foundry::petri_law::Marking;
45/// const M0: Marking<3> = Marking::EMPTY;
46/// assert_eq!(M0.total_tokens(), 0);
47/// let m1 = Marking([1u32, 2u32, 0u32]);
48/// assert_eq!(m1.total_tokens(), 3);
49/// ```
50///
51/// **Compile-pass law**: pre-matrix enabling check and firing are sound.
52///
53/// ```
54/// use wasm4pm_compat::nightly_foundry::petri_law::{Marking, PreMatrix, PostMatrix};
55/// // 2 places, 1 transition. p0 → t0 → p1.
56/// let mut pre = PreMatrix::<2, 1>::ZERO;
57/// pre.weights[0] = 1; // W⁻(p0,t0) = 1
58/// let mut post = PostMatrix::<2, 1>::ZERO;
59/// post.weights[1] = 1; // W⁺(t0,p1) = 1
60/// let m = Marking([1u32, 0u32]);
61/// assert!(pre.is_enabled(0, &m));
62/// let m2 = post.fire(0, m, &pre);
63/// assert_eq!(m2, Marking([0u32, 1u32]));
64/// ```
65pub mod petri_law {
66    /// Token marking of exactly `P` places — M: P → ℕ.
67    ///
68    /// Paper: Murata (1989) §2 Def. 2 — M₀ ∈ ℕᴾ.
69    /// Zero-cost: `#[repr(transparent)]` over `[u32; P]`.
70    #[repr(transparent)]
71    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
72    pub struct Marking<const P: usize>(pub [u32; P]);
73
74    impl<const P: usize> Marking<P> {
75        /// Zero marking: no tokens anywhere.
76        pub const EMPTY: Self = Self([0u32; P]);
77
78        /// Total token count across all places.
79        #[inline]
80        pub fn total_tokens(&self) -> u32 {
81            let mut s = 0u32;
82            let mut i = 0;
83            while i < P {
84                s += self.0[i];
85                i += 1;
86            }
87            s
88        }
89
90        /// Token count at place `p`. Returns `None` if `p >= P`.
91        #[must_use]
92        #[inline]
93        pub fn at(&self, p: usize) -> Option<u32> {
94            self.0.get(p).copied()
95        }
96    }
97
98    impl<const P: usize> Default for Marking<P> {
99        fn default() -> Self {
100            Self::EMPTY
101        }
102    }
103
104    /// Pre-incidence matrix W⁻: P×T→ℕ, stored flat row-major as `[u8; P * T]`.
105    ///
106    /// Paper: Murata (1989) §2 — W⁻(p,t) = arc weight from place p to transition t.
107    /// Enabling condition: `∀p: M[p] ≥ W⁻(p,t)`.
108    ///
109    /// **Requires `generic_const_exprs`**: `P * T` is a const expression in a
110    /// where-bound and in the array-length field.  Zero-cost flat array, no heap.
111    pub struct PreMatrix<const P: usize, const T: usize>
112    where
113        [(); P * T]: Sized,
114    {
115        /// Row-major weights; index `p * T + t`.
116        pub weights: [u8; P * T],
117    }
118
119    impl<const P: usize, const T: usize> PreMatrix<P, T>
120    where
121        [(); P * T]: Sized,
122    {
123        /// Zero arc-weight matrix.
124        pub const ZERO: Self = Self {
125            weights: [0u8; P * T],
126        };
127
128        /// Arc weight W⁻(p, t).
129        #[inline]
130        pub fn w(&self, p: usize, t: usize) -> u8 {
131            self.weights[p * T + t]
132        }
133
134        /// Is transition `t` enabled in marking `m`?
135        ///
136        /// Paper: Murata §2 Rule 1 — t is enabled iff `∀p: M[p] ≥ W⁻(p,t)`.
137        #[inline]
138        pub fn is_enabled(&self, t: usize, m: &Marking<P>) -> bool {
139            (0..P).all(|p| m.0[p] >= self.weights[p * T + t] as u32)
140        }
141    }
142
143    impl<const P: usize, const T: usize> Default for PreMatrix<P, T>
144    where
145        [(); P * T]: Sized,
146    {
147        fn default() -> Self {
148            Self::ZERO
149        }
150    }
151
152    /// Post-incidence matrix W⁺: T×P→ℕ, stored flat row-major as `[u8; T * P]`.
153    ///
154    /// Paper: Murata §2 — W⁺(t,p) = arc weight from transition t to place p.
155    ///
156    /// Note: `PostMatrix<P,T>` and `PreMatrix<P,T>` are **distinct types** even
157    /// though `P*T == T*P` arithmetically.  The bipartite direction is in the type.
158    pub struct PostMatrix<const P: usize, const T: usize>
159    where
160        [(); T * P]: Sized,
161    {
162        /// Row-major weights; index `t * P + p`.
163        pub weights: [u8; T * P],
164    }
165
166    impl<const P: usize, const T: usize> PostMatrix<P, T>
167    where
168        [(); T * P]: Sized,
169    {
170        /// Zero arc-weight matrix.
171        pub const ZERO: Self = Self {
172            weights: [0u8; T * P],
173        };
174
175        /// Arc weight W⁺(t, p).
176        #[inline]
177        pub fn w(&self, t: usize, p: usize) -> u8 {
178            self.weights[t * P + p]
179        }
180
181        #[inline]
182        pub fn fire(&self, t: usize, m: Marking<P>, pre: &PreMatrix<P, T>) -> Marking<P>
183        where
184            [(); P * T]: Sized,
185        {
186            let mut next = m;
187            let mut p = 0;
188            while p < P {
189                next.0[p] =
190                    next.0[p] - pre.weights[p * T + t] as u32 + self.weights[t * P + p] as u32;
191                p += 1;
192            }
193            next
194        }
195    }
196
197    impl<const P: usize, const T: usize> Default for PostMatrix<P, T>
198    where
199        [(); T * P]: Sized,
200    {
201        fn default() -> Self {
202            Self::ZERO
203        }
204    }
205}
206
207// ─────────────────────────────────────────────────────────────────────────────
208// Surface 2: Typed POWL nodes  (adt_const_params)
209// Paper: Kourani (arXiv:2505.07052) §3 — POWL recursive grammar:
210//   POWL ::= A | γ(M₁, ..., Mₙ) | P(M⁺, ≺) | τ
211//
212// `adt_const_params` + `ConstParamTy` let an enum variant become a const
213// generic: `TypedNode<{ PowlKind::Atom }>` vs `TypedNode<{ PowlKind::Partial }>`.
214// The compiler rejects calling atom-only APIs on a partial-order node.
215// Zero-cost: KIND is fully erased at runtime; the struct is just a `u32` id.
216// ─────────────────────────────────────────────────────────────────────────────
217
218/// **Compile-fail law**: an `Atom` node must NOT expose the partial-order API.
219///
220/// The `compile_fail` annotation verifies that the type system refuses the call.
221/// This module is always compiled (the crate is nightly-only; no cfg gate).
222///
223/// ```compile_fail
224/// use wasm4pm_compat::nightly_foundry::powl_law::TypedNode;
225/// let atom = TypedNode::atom(1u32);
226/// // E0599: no method `are_concurrent` found for `TypedNode<{PowlKind::Atom}>`
227/// let _ = atom.are_concurrent(&[], 1, 2);
228/// ```
229///
230/// **Compile-fail law**: `Atom` and `Silent` are distinct types; assignment must fail.
231///
232/// ```compile_fail
233/// use wasm4pm_compat::nightly_foundry::powl_law::{TypedNode, PowlKind};
234/// // E0308: mismatched types — `TypedNode<{Atom}>` ≠ `TypedNode<{Silent}>`
235/// let _: TypedNode<{ PowlKind::Silent }> = TypedNode::atom(0u32);
236/// ```
237///
238/// **Compile-pass law**: a well-formed atom node is admitted.
239///
240/// ```
241/// use wasm4pm_compat::nightly_foundry::powl_law::TypedNode;
242/// let a = TypedNode::atom(42u32);
243/// assert!(a.is_observable());
244/// assert_eq!(a.id(), 42);
245/// ```
246pub mod powl_law {
247    use core::marker::ConstParamTy;
248
249    /// POWL fragment kind — used as a const generic parameter.
250    ///
251    /// Paper: Kourani (2505.07052) §3.
252    #[derive(Debug, Clone, Copy, PartialEq, Eq, ConstParamTy)]
253    pub enum PowlKind {
254        Atom,
255        ChoiceGraph,
256        Partial,
257        Silent,
258        Xor,
259        Loop,
260    }
261
262    /// A POWL node with its fragment kind encoded at the type level.
263    ///
264    /// `KIND` is erased at runtime — the value is just a `u32` id.
265    /// Fragment-specific APIs are only available on the correct variant.
266    #[repr(transparent)]
267    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
268    pub struct TypedNode<const KIND: PowlKind>(pub u32);
269
270    // ── Atom ──────────────────────────────────────────────────────────────────
271    impl TypedNode<{ PowlKind::Atom }> {
272        #[inline]
273        pub const fn atom(id: u32) -> Self {
274            Self(id)
275        }
276        /// Atoms are always observable (carry an activity label).
277        #[inline]
278        pub const fn is_observable(&self) -> bool {
279            true
280        }
281    }
282
283    // ── Silent ────────────────────────────────────────────────────────────────
284    impl TypedNode<{ PowlKind::Silent }> {
285        #[inline]
286        pub const fn silent(id: u32) -> Self {
287            Self(id)
288        }
289        /// Silent steps are never observable.
290        #[inline]
291        pub const fn is_observable(&self) -> bool {
292            false
293        }
294    }
295
296    // ── Partial order ─────────────────────────────────────────────────────────
297
298    /// Precedence edge a ≺ b within a POWL partial-order node.
299    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
300    pub struct OrderEdge {
301        pub before: u32,
302        pub after: u32,
303    }
304
305    impl TypedNode<{ PowlKind::Partial }> {
306        #[inline]
307        pub const fn partial(id: u32) -> Self {
308            Self(id)
309        }
310
311        /// Are `a` and `b` concurrent (neither precedes the other)?
312        /// Paper: Kourani §3 — concurrency = absence of precedence in both directions.
313        #[inline]
314        pub fn are_concurrent(&self, edges: &[OrderEdge], a: u32, b: u32) -> bool {
315            let ab = edges.iter().any(|e| e.before == a && e.after == b);
316            let ba = edges.iter().any(|e| e.before == b && e.after == a);
317            !ab && !ba
318        }
319    }
320
321    // ── Choice Graph ─────────────────────────────────────────────────────────
322    impl TypedNode<{ PowlKind::ChoiceGraph }> {
323        #[inline]
324        pub const fn choice_graph(id: u32) -> Self {
325            Self(id)
326        }
327    }
328
329    impl TypedNode<{ PowlKind::Xor }> {
330        #[inline]
331        pub const fn xor(id: u32) -> Self {
332            Self(id)
333        }
334    }
335
336    impl TypedNode<{ PowlKind::Loop }> {
337        #[inline]
338        pub const fn loop_node(id: u32) -> Self {
339            Self(id)
340        }
341    }
342
343    // ── Universal id accessor (macro avoids repeated `where` complexity) ──────
344    macro_rules! impl_id {
345        ($kind:expr) => {
346            impl TypedNode<{ $kind }> {
347                #[inline]
348                pub const fn id(&self) -> u32 {
349                    self.0
350                }
351            }
352        };
353    }
354    impl_id!(PowlKind::Atom);
355    impl_id!(PowlKind::Silent);
356    impl_id!(PowlKind::Partial);
357    impl_id!(PowlKind::ChoiceGraph);
358    impl_id!(PowlKind::Xor);
359    impl_id!(PowlKind::Loop);
360}
361
362// ─────────────────────────────────────────────────────────────────────────────
363// Surface 3: Evidence-kind label via specialization  (min_specialization)
364// Doctrine: Blue River Dam — `Admitted` and `Raw` are first-class, distinct states.
365//
366// The blanket impl gives every T the label "raw".
367// The specialised impl overrides that for T: AdmittedMarker to "admitted".
368// Resolution is at compile time: no vtable, no branch, no heap.
369// ─────────────────────────────────────────────────────────────────────────────
370
371pub mod evidence_law {
372    /// Compile-time evidence-kind label — `"raw"` or `"admitted"`.
373    ///
374    /// Uses `min_specialization` to override the blanket `"raw"` impl
375    /// with `"admitted"` for any `Admitted<T>` wrapper — resolved at compile
376    /// time with no vtable and no branch.
377    pub trait EvidenceKind {
378        fn kind_label(&self) -> &'static str;
379    }
380
381    // Blanket: every T that is not Admitted<_> is "raw".
382    impl<T> EvidenceKind for T {
383        default fn kind_label(&self) -> &'static str {
384            "raw"
385        }
386    }
387
388    /// Newtype wrapper that marks a value as having crossed a named boundary.
389    /// Zero-cost: `#[repr(transparent)]` — same ABI as `T`.
390    #[repr(transparent)]
391    pub struct Admitted<T>(pub T);
392
393    // Specialization on the concrete type constructor `Admitted<T>`.
394    // `min_specialization` allows this because `Admitted<T>` is strictly
395    // more specific than the blanket `T` — it narrows on the type constructor,
396    // not on an arbitrary trait bound.
397    impl<T> EvidenceKind for Admitted<T> {
398        fn kind_label(&self) -> &'static str {
399            "admitted"
400        }
401    }
402}
403
404// ─────────────────────────────────────────────────────────────────────────────
405// Surface 4: SIMD token-enabling check  (portable_simd)
406// Paper: Murata (1989) §2 Rule 1 — t is enabled iff ∀p: M[p] ≥ W⁻(p,t).
407//
408// With portable_simd we check 4 or 8 places at once via u32x4 / u32x8.
409// For small Petri subnets this is the entire enabling condition in one
410// SIMD lane comparison + mask reduction — zero branches, no heap.
411// ─────────────────────────────────────────────────────────────────────────────
412
413pub mod token_law {
414    use core::simd::{cmp::SimdPartialOrd, u32x4, u32x8};
415
416    /// Check enabling for a 4-place subnet — single SIMD vector comparison.
417    ///
418    /// Returns `true` iff ∀p ∈ {0..4}: `marking[p] >= pre_weights[p]`.
419    #[inline]
420    pub fn transition_enabled_4(marking: [u32; 4], pre_weights: [u32; 4]) -> bool {
421        u32x4::from_array(marking)
422            .simd_ge(u32x4::from_array(pre_weights))
423            .all()
424    }
425
426    /// Check enabling for an 8-place subnet.
427    #[inline]
428    pub fn transition_enabled_8(marking: [u32; 8], pre_weights: [u32; 8]) -> bool {
429        u32x8::from_array(marking)
430            .simd_ge(u32x8::from_array(pre_weights))
431            .all()
432    }
433
434    /// Fire a transition on a 4-place marking via SIMD arithmetic.
435    ///
436    /// Paper: Murata §2 Rule 2 — `M'[p] = M[p] − W⁻[p] + W⁺[p]`.
437    /// **Requires `transition_enabled_4` was true.** No runtime check.
438    #[inline]
439    pub fn fire_4(marking: [u32; 4], pre: [u32; 4], post: [u32; 4]) -> [u32; 4] {
440        (u32x4::from_array(marking) - u32x4::from_array(pre) + u32x4::from_array(post)).to_array()
441    }
442}
443
444// ─────────────────────────────────────────────────────────────────────────────
445// Surface 5: Witness family batch check  (portable_simd + adt_const_params)
446//
447// `WitnessFamily` now derives `ConstParamTy`, so each variant is a `u8`-ordinal
448// value. We pack up to 8 family tags into a `u8x8` SIMD lane and compare all at
449// once — 8 comparisons in one instruction on architectures that support SIMD.
450//
451// Use case: checking that all witnesses in a `GraduationCandidate` belong to the
452// same family (e.g. all `Paper`) before graduation — zero runtime cost beyond the
453// SIMD load/compare/bitmask sequence.
454// ─────────────────────────────────────────────────────────────────────────────
455
456/// Batch family-membership check for up to 8 witness family tags via SIMD.
457///
458/// Each `WitnessFamily` value is cast to its `u8` ordinal. All 8 are loaded into
459/// a `u8x8` SIMD vector and compared against `target` in one operation.
460/// The result is a bitmask: bit `i` is set iff `families[i] == target`.
461///
462/// On x86-64 with SSE2 this is a single `pcmpeqb` + `pmovmskb` pair.
463/// On aarch64 with NEON it is `vceqq_u8` + `vmovmaskq_u8`.
464///
465/// # Examples
466///
467/// ```
468/// use wasm4pm_compat::nightly_foundry::families_match_simd;
469/// use wasm4pm_compat::witness::WitnessFamily;
470///
471/// let all_paper = [WitnessFamily::Paper; 8];
472/// let mask = families_match_simd(all_paper, WitnessFamily::Paper);
473/// assert_eq!(mask, 0b1111_1111u8); // all 8 match
474///
475/// let mixed = [
476///     WitnessFamily::Paper, WitnessFamily::Standard,
477///     WitnessFamily::Paper, WitnessFamily::Paper,
478///     WitnessFamily::Standard, WitnessFamily::Paper,
479///     WitnessFamily::Paper, WitnessFamily::Paper,
480/// ];
481/// let mask = families_match_simd(mixed, WitnessFamily::Paper);
482/// assert_eq!(mask, 0b1110_1101u8); // bits 1 and 4 unset (Standard slots)
483/// ```
484pub fn families_match_simd(
485    families: [crate::witness::WitnessFamily; 8],
486    target: crate::witness::WitnessFamily,
487) -> u8 {
488    use core::simd::{cmp::SimdPartialEq, u8x8};
489    let fam_vec = u8x8::from_array(families.map(|f| f as u8));
490    let target_vec = u8x8::splat(target as u8);
491    fam_vec.simd_eq(target_vec).to_bitmask() as u8
492}
493
494// ─────────────────────────────────────────────────────────────────────────────
495// Tests — always compiled (nightly-only crate, no cfg gate required)
496// ─────────────────────────────────────────────────────────────────────────────
497
498#[cfg(test)]
499mod tests {
500    use super::*;
501
502    // petri_law ────────────────────────────────────────────────────────────────
503
504    #[test]
505    fn marking_empty_is_zero() {
506        assert_eq!(petri_law::Marking::<4>::EMPTY.total_tokens(), 0);
507    }
508
509    #[test]
510    fn pre_matrix_enables_and_blocks() {
511        // 2 places, 2 transitions.
512        // W⁻(p0,t0)=1, W⁻(p1,t1)=1; all others zero.
513        let mut pre = petri_law::PreMatrix::<2, 2>::ZERO;
514        pre.weights[0] = 1; // W⁻(p0,t0): p=0,t=0 → index p*T+t = 0*2+0 = 0
515        pre.weights[3] = 1; // W⁻(p1,t1): p=1,t=1 → index p*T+t = 1*2+1 = 3
516
517        let m = petri_law::Marking([1u32, 0u32]);
518        assert!(pre.is_enabled(0, &m)); // t0 enabled: M[p0]=1 ≥ 1
519        assert!(!pre.is_enabled(1, &m)); // t1 blocked: M[p1]=0 < 1
520    }
521
522    #[test]
523    fn firing_token_moves_correctly() {
524        // 2 places, 1 transition. p0 → t0 → p1.
525        let mut pre = petri_law::PreMatrix::<2, 1>::ZERO;
526        pre.weights[0] = 1; // W⁻(p0,t0) = 1
527        let mut post = petri_law::PostMatrix::<2, 1>::ZERO;
528        post.weights[1] = 1; // W⁺(t0,p1) = 1
529
530        let m = petri_law::Marking([1u32, 0u32]);
531        assert!(pre.is_enabled(0, &m));
532        let m2 = post.fire(0, m, &pre);
533        assert_eq!(m2, petri_law::Marking([0u32, 1u32]));
534    }
535
536    // powl_law ────────────────────────────────────────────────────────────────
537
538    #[test]
539    fn atom_observable_silent_not() {
540        assert!(powl_law::TypedNode::atom(1).is_observable());
541        assert!(!powl_law::TypedNode::silent(2).is_observable());
542    }
543
544    #[test]
545    fn partial_concurrency_correct() {
546        let p = powl_law::TypedNode::partial(0);
547        let edges = [powl_law::OrderEdge {
548            before: 1,
549            after: 2,
550        }];
551        assert!(!p.are_concurrent(&edges, 1, 2)); // 1 ≺ 2: not concurrent
552        assert!(p.are_concurrent(&edges, 1, 3)); // no edge: concurrent
553    }
554
555    // evidence_law ────────────────────────────────────────────────────────────
556
557    #[test]
558    fn raw_u32_labels_raw() {
559        use evidence_law::EvidenceKind;
560        assert_eq!(42u32.kind_label(), "raw");
561    }
562
563    #[test]
564    fn admitted_wrapper_labels_admitted() {
565        use evidence_law::{Admitted, EvidenceKind};
566        assert_eq!(Admitted(42u32).kind_label(), "admitted");
567    }
568
569    // token_law ───────────────────────────────────────────────────────────────
570
571    #[test]
572    fn simd_enabled_all_met() {
573        assert!(token_law::transition_enabled_4([5, 3, 1, 0], [1, 1, 1, 0]));
574    }
575
576    #[test]
577    fn simd_enabled_one_unmet() {
578        assert!(!token_law::transition_enabled_4([5, 0, 1, 0], [1, 1, 1, 0]));
579    }
580
581    #[test]
582    fn simd_fire_moves_tokens() {
583        let m = token_law::fire_4([2, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0]);
584        assert_eq!(m, [1, 1, 0, 0]);
585    }
586
587    #[test]
588    fn simd_enabled_8_all_met() {
589        assert!(token_law::transition_enabled_8(
590            [9, 8, 7, 6, 5, 4, 3, 2],
591            [1, 1, 1, 1, 1, 1, 1, 1],
592        ));
593    }
594}