1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::mem::MaybeUninit;

#[macro_export]
macro_rules! count {
    () => { 0 };
    ($v:ident $(,$vs:ident)*) => {1 + $crate::count!($($vs),*) };
}

#[macro_export]
macro_rules! tuples {
    ($m:ident) => {
        $m!(tuples0, 0);
        $m!(tuples1, 1, p0, T0, 0);
        $m!(tuples2, 2, p0, T0, 0, p1, T1, 1);
        $m!(tuples3, 3, p0, T0, 0, p1, T1, 1, p2, T2, 2);
        $m!(tuples4, 4, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3);
        $m!(tuples5, 5, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4);
        $m!(tuples6, 6, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5);
        $m!(
            tuples7, 7, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6, 6
        );
        $m!(
            tuples8, 8, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7
        );
        $m!(
            tuples9, 9, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8
        );
        $m!(
            tuples10, 10, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9
        );
        $m!(
            tuples11, 11, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9, p10, T10, 10
        );
        $m!(
            tuples12, 12, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9, p10, T10, 10, p11, T11, 11
        );
        $m!(
            tuples13, 13, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9, p10, T10, 10, p11, T11, 11, p12, T12, 12
        );
        $m!(
            tuples14, 14, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9, p10, T10, 10, p11, T11, 11, p12, T12, 12, p13, T13,
            13
        );
        $m!(
            tuples15, 15, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9, p10, T10, 10, p11, T11, 11, p12, T12, 12, p13, T13,
            13, p14, T14, 14
        );
        $m!(
            tuples16, 16, p0, T0, 0, p1, T1, 1, p2, T2, 2, p3, T3, 3, p4, T4, 4, p5, T5, 5, p6, T6,
            6, p7, T7, 7, p8, T8, 8, p9, T9, 9, p10, T10, 10, p11, T11, 11, p12, T12, 12, p13, T13,
            13, p14, T14, 14, p15, T15, 15
        );
    };
}

pub trait Nudge {
    fn nudge(self, force: Self) -> Self;
}

macro_rules! floating {
    ($t:ty) => {
        impl Nudge for $t {
            #[inline]
            fn nudge(self, force: Self) -> Self {
                if self == 0.0 {
                    force / Self::MAX
                } else if self == -0.0 {
                    force / Self::MIN
                } else {
                    self * (1.0 + Self::EPSILON * force)
                }
            }
        }
    };
    ($($t:ty),*) => { $(floating!($t);)* }
}

floating!(f32, f64);

pub trait Unzip {
    type Target;
    fn unzip(self) -> Self::Target;
}

macro_rules! unzip {
    ($n:ident, $c:tt $(,$p:ident, $t:ident, $i:tt)*) => {
        impl<$($t,)* const N: usize> Unzip for [($($t,)*); N] {
            type Target = ($([$t; N],)*);

            fn unzip(self) -> Self::Target {
                let mut _uninits = ($(MaybeUninit::<[$t; N]>::uninit(),)*);
                let mut _pointers = ($(_uninits.$i.as_mut_ptr() as *mut $t,)*);
                for (_i, _items) in self.into_iter().enumerate() {
                    $(unsafe { _pointers.$i.add(_i).write(_items.$i); })*
                }
                ($(unsafe { _uninits.$i.assume_init() },)*)
            }
        }
    };
}

tuples!(unzip);

#[test]
fn unzip2() -> Result<(), Box<dyn std::error::Error>> {
    use crate::generate::*;
    usize::generator()
        .map(|value| (value, value))
        .array::<100>()
        .map(|tuples| tuples.unzip())
        .check(1000, |arrays| {
            arrays
                .0
                .into_iter()
                .zip(arrays.1)
                .all(|pair| pair.0 == pair.1)
        })?;
    Ok(())
}