Skip to content

Commit ee1110d

Browse files
authored
Merge pull request #4810 from andralex/medianOf
Add medianOf function
2 parents 81c09ed + 25dac83 commit ee1110d

File tree

1 file changed

+179
-2
lines changed

1 file changed

+179
-2
lines changed

std/algorithm/sorting.d

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2603,7 +2603,6 @@ schwartzSort(alias transform, alias less = "a < b",
26032603
foreach (ref p; probs)
26042604
{
26052605
if (!p) continue;
2606-
//enforce(p > 0 && p <= 1, "Wrong probability passed to entropy");
26072606
result -= p * log2(p);
26082607
}
26092608
return result;
@@ -2637,7 +2636,6 @@ schwartzSort(alias transform, alias less = "a < b",
26372636
foreach (ref p; probs)
26382637
{
26392638
if (!p) continue;
2640-
//enforce(p > 0 && p <= 1, "Wrong probability passed to entropy");
26412639
result -= p * log2(p);
26422640
}
26432641
return result;
@@ -3120,6 +3118,185 @@ unittest
31203118
}
31213119
}
31223120

3121+
// medianOf
3122+
/*
3123+
Private for the time being.
3124+
3125+
Computes the median of 2 to 5 arbitrary indexes in random-access range `r`
3126+
using hand-written specialized algorithms. The indexes must be distinct (if not,
3127+
behavior is implementation-defined). The function also partitions the elements
3128+
involved around the median, e.g. $(D medianOf(r, a, b, c)) not only fills `r[b]`
3129+
with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in
3130+
`r[a]` and the maximum in `r[c]`.
3131+
3132+
Params:
3133+
less = The comparison predicate used, modeled as a $(LUCKY strict weak
3134+
ordering) (irreflexive, antisymmetric, transitive, and implying a transitive
3135+
equivalence).
3136+
flag = Used only for even values of `T.length`. If `No.leanRight`, the median
3137+
"leans left", meaning $(D medianOf(r, a, b, c, d)) puts the lower median of the
3138+
four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`.
3139+
Conversely, $(D median!("a < b", Yes.leanRight)(r, a, b, c, d)) puts the upper
3140+
median of the four in `r[c]`, the maximum in `r[d]`, and the two others in
3141+
`r[a]` and `r[b]`.
3142+
r = The range containing the indexes.
3143+
i = Two to five indexes inside `r`.
3144+
*/
3145+
private void medianOf(
3146+
alias less = "a < b",
3147+
Flag!"leanRight" flag = No.leanRight,
3148+
Range,
3149+
Indexes...)
3150+
(Range r, Indexes i)
3151+
if (isRandomAccessRange!Range && hasLength!Range &&
3152+
Indexes.length >= 2 && Indexes.length <= 5 &&
3153+
allSatisfy!(isUnsigned, Indexes))
3154+
{
3155+
assert(r.length >= Indexes.length);
3156+
import std.functional : binaryFun;
3157+
alias lt = binaryFun!less;
3158+
enum k = Indexes.length;
3159+
import std.algorithm.mutation : swapAt;
3160+
3161+
alias a = i[0];
3162+
static assert(is(typeof(a) == size_t));
3163+
static if (k >= 2)
3164+
{
3165+
alias b = i[1];
3166+
static assert(is(typeof(b) == size_t));
3167+
assert(a != b);
3168+
}
3169+
static if (k >= 3)
3170+
{
3171+
alias c = i[2];
3172+
static assert(is(typeof(c) == size_t));
3173+
assert(a != c && b != c);
3174+
}
3175+
static if (k >= 4)
3176+
{
3177+
alias d = i[3];
3178+
static assert(is(typeof(d) == size_t));
3179+
assert(a != d && b != d && c != d);
3180+
}
3181+
static if (k >= 5)
3182+
{
3183+
alias e = i[4];
3184+
static assert(is(typeof(e) == size_t));
3185+
assert(a != e && b != e && c != e && d != e);
3186+
}
3187+
3188+
static if (k == 2)
3189+
{
3190+
if (lt(r[b], r[a])) r.swapAt(a, b);
3191+
}
3192+
else static if (k == 3)
3193+
{
3194+
if (lt(r[c], r[a])) // c < a
3195+
{
3196+
if (lt(r[a], r[b])) // c < a < b
3197+
{
3198+
r.swapAt(a, b);
3199+
r.swapAt(a, c);
3200+
}
3201+
else // c < a, b <= a
3202+
{
3203+
r.swapAt(a, c);
3204+
if (lt(r[b], r[a])) r.swapAt(a, b);
3205+
}
3206+
}
3207+
else // a <= c
3208+
{
3209+
if (lt(r[b], r[a])) // b < a <= c
3210+
{
3211+
r.swapAt(a, b);
3212+
}
3213+
else // a <= c, a <= b
3214+
{
3215+
if (lt(r[c], r[b])) r.swapAt(b, c);
3216+
}
3217+
}
3218+
assert(!lt(r[b], r[a]));
3219+
assert(!lt(r[c], r[b]));
3220+
}
3221+
else static if (k == 4)
3222+
{
3223+
static if (flag == No.leanRight)
3224+
{
3225+
// Eliminate the rightmost from the competition
3226+
if (lt(r[d], r[c])) r.swapAt(c, d); // c<=d
3227+
if (lt(r[d], r[b])) r.swapAt(b, d); // b<=d
3228+
medianOf!lt(r, a, b, c);
3229+
}
3230+
else
3231+
{
3232+
// Eliminate the leftmost from the competition
3233+
if (lt(r[b], r[a])) r.swapAt(a, b); // a<=b
3234+
if (lt(r[c], r[a])) r.swapAt(a, c); // a<=c
3235+
medianOf!lt(r, b, c, d);
3236+
}
3237+
}
3238+
else static if (k == 5)
3239+
{
3240+
// Credit: Teppo Niinimäki
3241+
version(unittest) scope(success)
3242+
{
3243+
assert(!lt(r[c], r[a]));
3244+
assert(!lt(r[c], r[b]));
3245+
assert(!lt(r[d], r[c]));
3246+
assert(!lt(r[e], r[c]));
3247+
}
3248+
3249+
if (lt(r[c], r[a])) r.swapAt(a, c);
3250+
if (lt(r[d], r[b])) r.swapAt(b, d);
3251+
if (lt(r[d], r[c]))
3252+
{
3253+
r.swapAt(c, d);
3254+
r.swapAt(a, b);
3255+
}
3256+
if (lt(r[e], r[b])) r.swapAt(b, e);
3257+
if (lt(r[e], r[c]))
3258+
{
3259+
r.swapAt(c, e);
3260+
if (lt(r[c], r[a])) r.swapAt(a, c);
3261+
}
3262+
else
3263+
{
3264+
if (lt(r[c], r[b])) r.swapAt(b, c);
3265+
}
3266+
}
3267+
}
3268+
3269+
///
3270+
unittest
3271+
{
3272+
// Verify medianOf for all permutations of [1, 2, 2, 3, 4].
3273+
int[5] data = [1, 2, 2, 3, 4];
3274+
do
3275+
{
3276+
int[5] a = data;
3277+
medianOf(a[], size_t(0), size_t(1));
3278+
assert(a[0] <= a[1]);
3279+
3280+
a[] = data[];
3281+
medianOf(a[], size_t(0), size_t(1), size_t(2));
3282+
assert(ordered(a[0], a[1], a[2]));
3283+
3284+
a[] = data[];
3285+
medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3));
3286+
assert(a[0] <= a[1] && a[1] <= a[2] && a[1] <= a[3]);
3287+
3288+
a[] = data[];
3289+
medianOf!("a < b", Yes.leanRight)(a[], size_t(0), size_t(1),
3290+
size_t(2), size_t(3));
3291+
assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3]);
3292+
3293+
a[] = data[];
3294+
medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3), size_t(4));
3295+
assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3] && a[2] <= a[4]);
3296+
}
3297+
while (nextPermutation(data[]));
3298+
}
3299+
31233300
// nextPermutation
31243301
/**
31253302
* Permutes $(D range) in-place to the next lexicographically greater

0 commit comments

Comments
 (0)