1 module tupleops;
2 
3 import std.typecons;
4 
5 auto depthFirstMap(alias func, Ts ...)(return auto ref Ts ts)
6 {
7     static if (isTuple!(typeof(ts[0])))
8     {
9         static if (ts.length == 1)
10         {
11             return depthFirstMap!func(ts[0].expand);
12         }
13         else
14         {
15             return tuple(depthFirstMap!func(ts[0].expand).expand,
16                          depthFirstMap!func(ts[1..$]).expand);
17         }
18     }
19     else
20     {
21         static if (ts.length == 1)
22         {
23             return tuple(func(ts[0]));
24         }
25         else
26         {
27             return tuple(func(ts[0]),
28                          depthFirstMap!func(ts[1..$]).expand);
29         }
30     }
31 }
32 
33 auto flatten(Ts ...)(return auto ref Ts ts)
34 {
35     return depthFirstMap!((return auto ref x) => x)(ts);
36 }
37 
38 auto ptrs(Ts ...)(return ref Ts ts)
39 {
40     return depthFirstMap!((return ref x) => &x)(ts);
41 }
42 
43 unittest
44 {
45     enum t = tuple(1, 2, tuple(3, tuple(tuple(4, 5), 6), 7));
46     static assert(t.flatten == tuple(1, 2, 3, 4, 5, 6, 7));
47 }
48 
49 unittest
50 {
51     auto t = tuple(1, 2, tuple(3, tuple(tuple(4, 5), 6), 7));
52     auto p = t.ptrs;
53     t[1] = 222;
54     auto d = t.flatten;
55     assert(d == tuple(1, 222, 3, 4, 5, 6, 7));
56 
57     static foreach (i; 0 .. t.length) {
58         assert(*p[i] == d[i]);
59     }
60 }
61 
62 struct UnzipRange(Z)
63 {
64     Z zipped;
65     alias Tp = typeof(Z.init.front());
66     const size_t length = Tp.length;
67 
68     static auto opIndex(D...)(auto ref D i)
69     {
70         import std.array : array;
71         import std.algorithm : map;
72         return this.zipped.map!(x => x[i]);
73     }
74 }
75 
76 
77 auto unzip(Z)(Z zipped)
78 {
79     import std.algorithm : map;
80     mixin(
81         {
82             import std.conv : to;
83             alias Tp = typeof(Z.init.front());
84             auto m = "return tuple(";
85             foreach (i; 0 .. Tp.length)
86             {
87                 m ~= "zipped.map!(x => x[" ~ i.to!string ~ "]),";
88             }
89             return m[0 .. $-1] ~ ");";
90         }());
91 }
92 
93 unittest
94 {
95     import std.algorithm : equal;
96     import std.range : zip;
97     immutable a = [1, 2, 3];
98     immutable b = ["a", "b", "c"];
99     immutable c = [0.1, 0.2, 0.3];
100 
101     auto x = tuple(a, b, c);
102     auto z = zip(x.expand);
103     auto u = unzip(zip(a, b, c));
104     static foreach (i; 0 .. x.length)
105     {
106         assert(u[i].equal(x[i]));
107     }
108 }