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 }