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