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