@@ -1631,8 +1631,6 @@ if (s != SwapStrategy.stable
16311631 && hasLength! Range
16321632 && Offset.length >= 1 )
16331633{
1634- import std.algorithm.comparison : min;
1635-
16361634 Tuple ! (size_t , " pos" , size_t , " len" )[offset.length] blackouts;
16371635 foreach (i, v; offset)
16381636 {
@@ -1659,7 +1657,7 @@ if (s != SwapStrategy.stable
16591657
16601658 size_t left = 0 , right = offset.length - 1 ;
16611659 auto tgt = range.save;
1662- size_t steps = 0 ;
1660+ size_t tgtPos = 0 ;
16631661
16641662 while (left <= right)
16651663 {
@@ -1680,25 +1678,31 @@ if (s != SwapStrategy.stable
16801678 break ;
16811679 }
16821680 // Advance to next blackout on the left
1683- assert (blackouts[left].pos >= steps);
1684- tgt.popFrontExactly(blackouts[left].pos - steps);
1685- steps = blackouts[left].pos;
1686- immutable toMove = min(
1687- blackouts[left].len,
1688- range.length - (blackouts[right].pos + blackouts[right].len));
1681+ assert (blackouts[left].pos >= tgtPos);
1682+ tgt.popFrontExactly(blackouts[left].pos - tgtPos);
1683+ tgtPos = blackouts[left].pos;
1684+
1685+ // Number of elements to the right of blackouts[right]
1686+ immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len);
1687+ size_t toMove = void ;
1688+ if (tailLen < blackouts[left].len)
1689+ {
1690+ toMove = tailLen;
1691+ blackouts[left].pos += toMove;
1692+ blackouts[left].len -= toMove;
1693+ }
1694+ else
1695+ {
1696+ toMove = blackouts[left].len;
1697+ ++ left;
1698+ }
1699+ tgtPos += toMove;
16891700 foreach (i; 0 .. toMove)
16901701 {
16911702 move(range.back, tgt.front);
16921703 range.popBack();
16931704 tgt.popFront();
16941705 }
1695- steps += toMove;
1696- if (toMove == blackouts[left].len)
1697- {
1698- // Filled the entire left hole
1699- ++ left;
1700- continue ;
1701- }
17021706 }
17031707
17041708 return range;
@@ -1770,7 +1774,7 @@ if (s == SwapStrategy.stable
17701774 a = [ 0 , 1 , 2 , 3 , 4 , 5 ];
17711775 assert (remove! (SwapStrategy.unstable)(a, 1 ) == [0 , 5 , 2 , 3 , 4 ]);
17721776 a = [ 0 , 1 , 2 , 3 , 4 , 5 ];
1773- assert (remove! (SwapStrategy.unstable)(a, tuple(1 , 4 )) == [0 ]);
1777+ assert (remove! (SwapStrategy.unstable)(a, tuple(1 , 4 )) == [0 , 5 , 4 ]);
17741778}
17751779
17761780@safe unittest
@@ -1940,6 +1944,81 @@ if (isBidirectionalRange!Range
19401944 [ 1 , 3 , 3 , 4 , 5 , 5 , 6 ]);
19411945}
19421946
1947+ @nogc unittest
1948+ {
1949+ // @nogc test
1950+ int [10 ] arr = [0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ];
1951+ alias pred = e => e < 5 ;
1952+
1953+ auto r = arr[].remove! (SwapStrategy.unstable)(0 );
1954+ r = r.remove! (SwapStrategy.stable)(0 );
1955+ r = r.remove! (pred, SwapStrategy.unstable);
1956+ r = r.remove! (pred, SwapStrategy.stable);
1957+ }
1958+
1959+ @safe unittest
1960+ {
1961+ import std.algorithm.comparison : min;
1962+ import std.algorithm.searching : all, any;
1963+ import std.algorithm.sorting : isStrictlyMonotonic;
1964+ import std.array : array;
1965+ import std.meta : AliasSeq;
1966+ import std.range : iota, only;
1967+ import std.typecons : Tuple ;
1968+ alias S = Tuple ! (int [2 ]);
1969+ S[] soffsets;
1970+ foreach (start; 0 .. 5 )
1971+ foreach (end; min(start+ 1 ,5 ) .. 5 )
1972+ soffsets ~= S([start,end]);
1973+ alias D = Tuple ! (int [2 ],int [2 ]);
1974+ D[] doffsets;
1975+ foreach (start1; 0 .. 10 )
1976+ foreach (end1; min(start1+ 1 ,10 ) .. 10 )
1977+ foreach (start2; end1 .. 10 )
1978+ foreach (end2; min(start2+ 1 ,10 ) .. 10 )
1979+ doffsets ~= D([start1,end1],[start2,end2]);
1980+ alias T = Tuple ! (int [2 ],int [2 ],int [2 ]);
1981+ T[] toffsets;
1982+ foreach (start1; 0 .. 15 )
1983+ foreach (end1; min(start1+ 1 ,15 ) .. 15 )
1984+ foreach (start2; end1.. 15 )
1985+ foreach (end2; min(start2+ 1 ,15 ) .. 15 )
1986+ foreach (start3; end2.. 15 )
1987+ foreach (end3; min(start3+ 1 ,15 ) .. 15 )
1988+ toffsets ~= T([start1,end1],[start2,end2],[start3,end3]);
1989+
1990+ static void verify (O... )(int [] r, int len, int removed, bool stable, O offsets)
1991+ {
1992+ assert (r.length == len - removed);
1993+ assert (! stable || r.isStrictlyMonotonic);
1994+ assert (r.all! (e => all! (o => e < o[0 ] || e >= o[1 ])(offsets.only)));
1995+ }
1996+
1997+ foreach (offsets; AliasSeq! (soffsets,doffsets,toffsets))
1998+ foreach (os; offsets)
1999+ {
2000+ int len = 5 * os.length;
2001+ auto w = iota(0 , len).array;
2002+ auto x = w.dup ;
2003+ auto y = w.dup ;
2004+ auto z = w.dup ;
2005+ alias pred = e => any! (o => o[0 ] <= e && e < o[1 ])(only(os.expand));
2006+ w = w.remove! (SwapStrategy.unstable)(os.expand);
2007+ x = x.remove! (SwapStrategy.stable)(os.expand);
2008+ y = y.remove! (pred, SwapStrategy.unstable);
2009+ z = z.remove! (pred, SwapStrategy.stable);
2010+ int removed;
2011+ foreach (o; os)
2012+ removed += o[1 ] - o[0 ];
2013+ verify(w, len, removed, false , os[]);
2014+ verify(x, len, removed, true , os[]);
2015+ verify(y, len, removed, false , os[]);
2016+ verify(z, len, removed, true , os[]);
2017+ assert (w == y);
2018+ assert (x == z);
2019+ }
2020+ }
2021+
19432022// reverse
19442023/**
19452024Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D
0 commit comments