// WCA Ao5 · 17 Languages · 1 Algorithm

One Ao5, Seventeen Languages

The same WCA Average-of-5 algorithm, written in seventeen languages. Watch each language handle "DNF" — that "value doesn't exist" case is a litmus test for any type system.

The WCA Ao5 rules (in 60 seconds)

  1. Cuber attempts the event 5 times; each result in centiseconds
  2. Drop the fastest and the slowest attempt
  3. Arithmetic mean of the remaining 3 = Ao5
  4. A DNF (Did Not Finish) sorts to the very end — it gets dropped as the "slowest"
  5. But with ≥ 2 DNFs, the whole Ao5 itself becomes DNF
Example[12.34, 11.22, DNF, 13.45, 10.99]排序: [10.99, 11.22, 12.34, 13.45, DNF]drop ends, mean(11.22, 12.34, 13.45) = 12.34s
TypeScriptdeep dive →
type Time = number | null; // null = DNF, value in centiseconds
 
function ao5(times: Time[]): Time {
const dnfs = times.filter(t => t === null).length;
if (dnfs >= 2) return null;
 
const sorted = [...times].sort((a, b) => {
if (a === null) return 1;
if (b === null) return -1;
return a - b;
});
 
const mid = sorted.slice(1, 4) as number[];
return Math.round(mid.reduce((s, t) => s + t, 0) / 3);
}
Union number | null + filter chain
Rustdeep dive →
type Time = Option<u32>;
 
fn ao5(times: &[Time; 5]) -> Time {
if times.iter().filter(|t| t.is_none()).count() >= 2 {
return None;
}
 
let mut s = *times;
s.sort_by(|a, b| match (a, b) {
(None, None) => std::cmp::Ordering::Equal,
(None, _) => std::cmp::Ordering::Greater,
(_, None) => std::cmp::Ordering::Less,
(Some(a), Some(b)) => a.cmp(b),
});
 
let sum: u32 = s[1..4].iter().filter_map(|&t| t).sum();
Some(sum / 3)
}
Option<T> + tuple match
Godeep dive →
package main
 
import "sort"
 
const DNF = -1
 
func ao5(times [5]int) int {
dnfs := 0
for _, t := range times {
if t == DNF {
dnfs++
}
}
if dnfs >= 2 {
return DNF
}
 
s := times
sort.Slice(s[:], func(i, j int) bool {
if s[i] == DNF { return false }
if s[j] == DNF { return true }
return s[i] < s[j]
})
 
return (s[1] + s[2] + s[3]) / 3
}
-1 sentinel + sort.Slice
Pythondeep dive →
from typing import Optional
 
# WCA Ao5: drop best & worst, mean of middle 3.
# DNFs sort to the bottom; 2+ DNFs → entire average is DNF.
 
def ao5(times: list[Optional[int]]) -> Optional[int]:
if sum(1 for t in times if t is None) >= 2:
return None
 
s = sorted(times, key=lambda t: float('inf') if t is None else t)
return sum(s[1:4]) // 3
Type hints + sorted key lambda
Cdeep dive →
#include <stdlib.h>
 
#define DNF -1
 
static int cmp(const void *a, const void *b) {
int x = *(const int*)a, y = *(const int*)b;
if (x == DNF) return 1;
if (y == DNF) return -1;
return x - y;
}
 
int ao5(int t[5]) {
int dnfs = 0;
for (int i = 0; i < 5; i++)
if (t[i] == DNF) dnfs++;
if (dnfs >= 2) return DNF;
 
int s[5];
for (int i = 0; i < 5; i++) s[i] = t[i];
qsort(s, 5, sizeof(int), cmp);
 
return (s[1] + s[2] + s[3]) / 3;
}
#define + qsort with custom comparator
C++deep dive →
#include <array>
#include <optional>
#include <algorithm>
 
using Time = std::optional<int>;
 
Time ao5(std::array<Time, 5> t) {
if (std::ranges::count(t, std::nullopt) >= 2)
return std::nullopt;
 
std::ranges::sort(t, [](auto a, auto b) {
if (!a) return false;
if (!b) return true;
return *a < *b;
});
 
return (*t[1] + *t[2] + *t[3]) / 3;
}
std::optional + ranges::sort
Zigdeep dive →
const std = @import("std");
 
const Time = ?u32;
 
fn ao5(times: [5]Time) Time {
var dnfs: u32 = 0;
for (times) |t| if (t == null) { dnfs += 1; };
if (dnfs >= 2) return null;
 
var s = times;
std.mem.sort(Time, &s, {}, struct {
fn lt(_: void, a: Time, b: Time) bool {
if (a == null) return false;
if (b == null) return true;
return a.? < b.?;
}
}.lt);
 
return (s[1].? + s[2].? + s[3].?) / 3;
}
?T optional + struct-wrapped fn
Swiftdeep dive →
func ao5(_ times: [Int?]) -> Int? {
let dnfs = times.lazy.filter { $0 == nil }.count
guard dnfs < 2 else { return nil }
 
let sorted = times.sorted {
switch ($0, $1) {
case (nil, _): return false
case (_, nil): return true
case let (a?, b?): return a < b
}
}
 
let mid = sorted[1...3].compactMap { $0 }
return mid.reduce(0, +) / 3
}
Optional + tuple switch
Kotlindeep dive →
fun ao5(times: List<Int?>): Int? {
if (times.count { it == null } >= 2) return null
 
val sorted = times.sortedWith(
compareBy(nullsLast()) { it }
)
 
return sorted.subList(1, 4)
.filterNotNull()
.sum() / 3
}
compareBy(nullsLast()) + filterNotNull
Javadeep dive →
import java.util.*;
 
// WCA Ao5: DNF = null. Drop best & worst, mean of middle 3.
 
public static Integer ao5(Integer[] times) {
long dnfs = Arrays.stream(times)
.filter(Objects::isNull).count();
if (dnfs >= 2) return null;
 
Integer[] s = times.clone();
Arrays.sort(s, Comparator.nullsLast(Integer::compare));
return (s[1] + s[2] + s[3]) / 3;
}
Comparator.nullsLast + boxed Integer
JavaScriptdeep dive →
// WCA Ao5: DNF = null.
// No type system to remind you DNF is unhandled — write defensively.
 
const ao5 = (times) => {
if (times.filter(t => t == null).length >= 2) return null;
 
const s = [...times].sort((a, b) =>
a == null ? 1 : b == null ? -1 : a - b
);
 
return Math.round((s[1] + s[2] + s[3]) / 3);
};
Same shape as TS, no types at the door
Mojodeep dive →
# Mojo: fn = strict typed (vs Python's loose def).
# Same shape as Python, but compiles to native via MLIR.
 
fn ao5(times: List[Optional[Int]]) -> Optional[Int]:
var dnfs = 0
for t in times:
if not t: dnfs += 1
if dnfs >= 2: return None
 
var s = sorted(
times,
key=fn(t: Optional[Int]) -> Int:
return t.value() if t else Int.MAX,
)
return (s[1].value() + s[2].value() + s[3].value()) // 3
Strict fn + Optional + MLIR backend
C#deep dive →
// C# 8+: int? is Nullable<int>. LINQ for sort & sum.
// DNFs become null and float to the end via int.MaxValue key.
 
public static int? Ao5(int?[] times)
{
if (times.Count(t => t is null) >= 2) return null;
 
var s = times
.OrderBy(t => t ?? int.MaxValue)
.ToArray();
 
return (s[1]!.Value + s[2]!.Value + s[3]!.Value) / 3;
}
Nullable<int> + LINQ OrderBy
Rubydeep dive →
# WCA Ao5: nil = DNF. INFINITY in sort_by pushes nils to the end.
 
def ao5(times)
return nil if times.count(&:nil?) >= 2
 
s = times.sort_by { |t| t.nil? ? Float::INFINITY : t }
(s[1] + s[2] + s[3]) / 3
end
nil + sort_by Float::INFINITY
PHPdeep dive →
<?php
// PHP 8: nullable return ?int, arrow fn, spaceship operator <=>.
 
function ao5(array $times): ?int {
$dnfs = count(array_filter($times, fn($t) => $t === null));
if ($dnfs >= 2) return null;
 
$s = $times;
usort($s, fn($a, $b) =>
$a === null ? 1 : ($b === null ? -1 : $a <=> $b)
);
 
return intdiv($s[1] + $s[2] + $s[3], 3);
}
?int nullable + usort + spaceship <=>
Luadeep dive →
-- Lua: tables drop trailing nils, so we use -1 as DNF.
-- Tables are 1-indexed.
 
local DNF = -1
 
local function ao5(times)
local dnfs = 0
for i = 1, 5 do
if times[i] == DNF then dnfs = dnfs + 1 end
end
if dnfs >= 2 then return DNF end
 
local s = { table.unpack(times) }
table.sort(s, function(a, b)
if a == DNF then return false end
if b == DNF then return true end
return a < b
end)
 
return math.floor((s[2] + s[3] + s[4]) / 3)
end
1-indexed table + -1 sentinel (nil holes vanish)
Haskelldeep dive →
-- WCA Ao5: Maybe Int. Total functions, pattern-matching the empties out.
 
import Data.List (sortBy)
import Data.Ord (comparing)
import Data.Maybe (catMaybes, isNothing)
 
ao5 :: [Maybe Int] -> Maybe Int
ao5 ts
| length (filter isNothing ts) >= 2 = Nothing
| otherwise = Just (sum middle `div` 3)
where
sorted = sortBy (comparing (maybe maxBound id)) ts
middle = catMaybes (take 3 (drop 1 sorted))
Maybe Int + sortBy comparing + catMaybes

What to notice

The DNF empty value

Seventeen languages, four distinct answers to "value doesn't exist": union types (TS number | null / PHP ?int), Option / Optional / Maybe (Rust / C++ / Swift / Kotlin / Zig / Python / Java's boxed null / Mojo's Optional[Int] / C# int? / Haskell Maybe Int), sentinel values (C / Go / Lua using -1), and no type guard at all (JS / Ruby). The first two surface unhandled DNFs at compile time; the last two need tests to catch — JS / Ruby are the foil to TS here.

Where do empties sort to

Kotlin in one line: compareBy(nullsLast()). Ruby sort_by in one line. Python in one lambda key. Haskell with comparing (maybe maxBound id) in one line. Rust takes a four-arm exhaustive match. Zig wraps it in an inline struct. Same semantics, 5× variance in line count — the real "language density" gap.

Higher-order vs imperative

Python / Swift / Kotlin / TS lean on filter / map / reduce / sorted chains in a pipeline style; C and Zig still go imperative with for loops and scratch arrays. Rust sits in between — iterator chains or imperative, both feel native.

Errors / boundary handling

WCA rules demand exactly 5 attempts. Rust / Zig / C++ guarantee this with fixed-size array types like [Time; 5] — too few args, won't compile. Python / TS use list, so they need a runtime assert.