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.container.hybrid_hashmap : 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 pure nothrow @safe @nogc unittest { 92 import std.algorithm.iteration : filter; 93 import nxt.container.hybrid_hashmap : HybridHashSet, byElement; 94 import nxt.digest.fnv : FNV; 95 96 alias X = HybridHashSet!(uint, FNV!(64, true)); 97 enum pred = "a != 11"; 98 // alias pred = (_) => _ != 1; 99 100 /+ TODO: activate +/ 101 // auto x = makeWithElements!(X)([11, 12].s).filteredInplace!pred.byElement; 102 // assert(x.front == 12); 103 104 // x.popFront(); 105 // assert(x.empty); 106 } 107 108 /** Fyilter `r` eagerly in-place using `predicate`. */ 109 void filterInplace(alias predicate, C)(ref C r) @trusted /+ TODO: remove @trusted +/ 110 if (hasIndexing!C && /+ TODO: extend to `isArrayContainer`!C eller `isRandomAccessContainer!C` +/ 111 is(typeof(unaryFun!predicate))) 112 { 113 import std.algorithm.mutation : move; 114 r = move(r).filteredInplace!predicate(); /+ TODO: remove move when compiler does it for us +/ 115 } 116 117 @system pure nothrow @nogc unittest /+ TODO: make @safe when equal() is @safe +/ 118 { 119 import std.algorithm.mutation : move; 120 import std.meta : AliasSeq; 121 import nxt.unique_range : intoUniqueRange; 122 import std.experimental.allocator.mallocator : Mallocator; 123 import nxt.container.dynamic_array : DynamicArray; 124 import nxt.algorithm.comparison : equal; 125 126 alias E = int; 127 foreach (C; AliasSeq!(DynamicArray// , 128 /+ TODO: SortedSetUniqueArray +/ 129 )) 130 { 131 alias A = C!(E, Mallocator); 132 133 static assert(is(A == typeof(A().filteredInplace!(_ => _ & 1)))); 134 135 // empty case 136 immutable E[] c0 = []; 137 assert(A() 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 }