1 module nxt.inplace_algorithm;
2 
3 import std.functional : unaryFun;
4 
5 import nxt.container.traits : isSet;
6 import nxt.typecons_ex : hasIndexing;
7 
8 /** Returns: `r` eagerly in-place filtered on `predicate`.
9  *
10  * TODO: Move to free function in array_ex.d to get @trusted access to private Array._mptr
11  */
12 C filteredInplace(alias predicate, C)(C r) @trusted // TODO: remove @trusted
13 if (is(typeof(unaryFun!predicate)) &&
14     hasIndexing!C)          // TODO: extend to `isArrayContainer`!C eller `isRandomAccessContainer!C`
15 {
16     import core.internal.traits : hasElaborateDestructor, Unqual;
17     import std.traits : isMutable, hasIndirections;
18     import std.range.primitives : ElementType;
19     import std.algorithm.mutation : move;
20     import nxt.traits_ex : ownsItsElements;
21 
22     alias pred = unaryFun!predicate;
23     alias E = ElementType!C;
24     alias MutableC = Unqual!C;
25 
26     static if (__traits(hasMember, r, `ptr`))
27     {
28         size_t dstIx = 0;           // destination index
29 
30         // skip leading passing elements
31         // TODO: reuse .indexOf!(_ => !pred(_)) algorithm in `Array`
32         while (dstIx < r.length && pred(r.ptr[dstIx]))
33             dstIx += 1;
34 
35         // inline filtering
36         foreach (immutable srcIx; dstIx + 1 .. r.length)
37         {
38             // TODO: move this into @trusted member of Array
39             if (pred(r.ptr[srcIx]))
40             {
41                 static if (isMutable!E &&
42                            !hasIndirections!E)
43                     move(r.ptr[srcIx], r.ptr[dstIx]); // TODO: reuse function in array
44                 else static if (ownsItsElements!C)
45                     move(r.ptr[srcIx], r.ptr[dstIx]); // TODO: reuse function in array
46                 else
47                     static assert(0, `Cannot move elements in instance of ` ~ C.stringof);
48                 dstIx += 1;
49             }
50             else
51             {
52                 static if (hasElaborateDestructor!E)
53                     .destroy(e);
54             }
55         }
56 
57         static if (__traits(hasMember, C, `shrinkTo`))
58             r.shrinkTo(dstIx);  // length will all always shrink
59         else
60             r.length = dstIx;
61     }
62 
63     return move(r);             // TODO: remove move when compiler does it for us
64 }
65 
66 /** Returns: `r` eagerly in-place filtered on `predicate`.
67  */
68 C filteredInplace(alias predicate, C)(C r)
69 if (isSet!C &&
70     is(typeof(unaryFun!predicate(C.ElementType.init))))
71 {
72     import std.algorithm.mutation : move;
73     static if (__traits(hasMember, C, "remove"))
74     {
75         import std.functional : not;
76         import nxt.sso_hashmap_or_hashset : filtered;
77         return move(r).filtered!predicate(); // TODO: remove move when compiler does it for us
78     }
79     else
80     {
81         C s;
82         import std.algorithm.iteration : filter;
83         foreach (e; r[].filter!predicate)
84             s.insert(e);
85         return move(s);             // TODO: remove move when compiler does it for us
86     }
87 }
88 
89 /// inplace filtering on hashset
90 version(none)                   // TODO: activate or remove SSOHashSet
91 @safe pure nothrow @nogc unittest
92 {
93     import std.algorithm.iteration : filter;
94     import nxt.sso_hashmap_or_hashset : SSOHashSet, byElement;
95     import nxt.digestx.fnv : FNV;
96 
97     alias X = SSOHashSet!(uint, null, FNV!(64, true));
98     enum pred = "a != 11";
99     // alias pred = (_) => _ != 1;
100 
101     // TODO: activate
102     // auto x = X.withElements([11, 12].s).filteredInplace!pred.byElement;
103     // assert(x.front == 12);
104 
105     // x.popFront();
106     // assert(x.empty);
107 }
108 
109 /** Fyilter `r` eagerly in-place using `predicate`. */
110 void filterInplace(alias predicate, C)(ref C r) @trusted // TODO: remove @trusted
111 if (hasIndexing!C && // TODO: extend to `isArrayContainer`!C eller `isRandomAccessContainer!C`
112     is(typeof(unaryFun!predicate)))
113 {
114     import std.algorithm.mutation : move;
115     r = move(r).filteredInplace!predicate(); // TODO: remove move when compiler does it for us
116 }
117 
118 @safe pure nothrow @nogc unittest
119 {
120     import std.algorithm.mutation : move;
121     import std.meta : AliasSeq;
122     import nxt.unique_range : intoUniqueRange;
123     import nxt.container.dynamic_array : DynamicArray;
124     import nxt.array_algorithm : equal;
125 
126     alias E = int;
127     foreach (C; AliasSeq!(DynamicArray// ,
128                           // TODO: SortedSetUniqueArray
129                  ))
130     {
131         alias A = C!E;
132 
133         static assert(is(A == typeof(A().filteredInplace!(_ => _ & 1))));
134 
135         // empty case
136         immutable E[0] c0 = [];
137         assert(A.withCapacity(0)
138                 .filteredInplace!(_ => _ & 1)
139                 .intoUniqueRange()
140                 .equal(c0[]));
141 
142         // few elements triggers small-array optimization
143         immutable E[2] c2 = [3, 11];
144         auto a2 = A([2, 3, 11, 12].s);
145         assert(move(a2).filteredInplace!(_ => _ & 1)
146                        .intoUniqueRange()
147                        .equal(c2[]));
148 
149         // odd elements
150         immutable E[6] c6 = [3, 11, 13, 15, 17, 19];
151         auto a6 = A([3, 11, 12, 13, 14, 15, 16, 17, 18, 19].s);
152         assert(move(a6).filteredInplace!(_ => _ & 1)
153                        .intoUniqueRange()
154                        .equal(c6[]));
155 
156         // elements less than or equal to limit
157         immutable E[7] c7 = [3, 11, 12, 13, 14, 15, 16];
158         auto a7 = A([3, 11, 12, 13, 14, 15, 16, 17, 18, 19].s
159             );
160         assert(move(a7).filteredInplace!(_ => _ <= 16)
161                        .intoUniqueRange()
162                        .equal(c7[]));
163 
164         auto a3 = A([2, 4, 11].s);
165         a3.filterInplace!(_ => _ & 1);
166         assert(a3.length == 1);
167         assert(a3[0] == 11);
168     }
169 
170 }
171 
172 version(unittest)
173 {
174     import nxt.array_help : s;
175 }