broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) >= limit:
                continue
            if emoji not in first:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in users:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) >= limit:
                continue
            if len(users) <= 1:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in users:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
An emoji that disappeared and was added again keeps its old position instead of moving to the end.

events [seq, user, emoji, action] are applied in seq order. A user holds each emoji at most once and at most `limit` distinct emoji per message; an add beyond the limit is ignored. A remove only releases a reaction the user holds. Result rows [emoji, count, mine] for emoji with at least one user, ordered by the seq at which the emoji most recently went from zero to one user.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) >= limit:
                continue
            if not users:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in users:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items()]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

meaning: Fixture-count band, not measured model difficulty

deps: stdlib

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) >= limit:
                continue
            if not users:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in users:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u is not None]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
Emoji whose last reaction was removed remain as zero-count chips.

events [seq, user, emoji, action] are applied in seq order. A user holds each emoji at most once and at most `limit` distinct emoji per message; an add beyond the limit is ignored. A remove only releases a reaction the user holds. Result rows [emoji, count, mine] for emoji with at least one user, ordered by the seq at which the emoji most recently went from zero to one user.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) > limit:
                continue
            if not users:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in users:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) > limit - 1 if limit > 1 else False:
                continue
            if not users:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in users:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
A user can place one more distinct emoji than the configured limit.

events [seq, user, emoji, action] are applied in seq order. A user holds each emoji at most once and at most `limit` distinct emoji per message; an add beyond the limit is ignored. A remove only releases a reaction the user holds. Result rows [emoji, count, mine] for emoji with at least one user, ordered by the seq at which the emoji most recently went from zero to one user.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) >= limit:
                continue
            if not users:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        else:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, viewer, limit):
    current = {}
    first = {}
    per_user = {}
    for seq, user, emoji, action in sorted(events):
        users = current.get(emoji, set())
        if action == 'add':
            if user in users or per_user.get(user, 0) >= limit:
                continue
            if not users:
                first[emoji] = seq
            users.add(user)
            current[emoji] = users
            per_user[user] = per_user.get(user, 0) + 1
        elif user in per_user:
            users.discard(user)
            per_user[user] = per_user.get(user, 0) - 1
    rows = [[e, len(u), viewer in u] for e, u in current.items() if u]
    rows.sort(key=lambda r: first[r[0]])
    return rows
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('remove delivered before add', ([[3, 'bo', 'up', 'remove'], [2, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [5, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 2: [('remove delivered before add', ([[4, 'bo', 'up', 'remove'], [3, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [6, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 3: [('remove delivered before add', ([[5, 'bo', 'up', 'remove'], [4, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [7, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 4: [('remove delivered before add', ([[6, 'bo', 'up', 'remove'], [5, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [8, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])], 5: [('remove delivered before add', ([[7, 'bo', 'up', 'remove'], [6, 'bo', 'up', 'add']], 'me', 3), []), ('emoji vanishes and returns', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'bo', 'up', 'remove'], [9, 'di', 'up', 'add']], 'me', 3), [['ok', 1, False], ['up', 1, False]]), ('second user joins an emoji', ([[1, 'bo', 'up', 'add'], [2, 'cy', 'ok', 'add'], [3, 'di', 'up', 'add']], 'di', 3), [['up', 2, True], ['ok', 1, False]]), ('only reaction removed', ([[1, 'me', 'up', 'add'], [2, 'me', 'up', 'remove']], 'me', 2), []), ('limit reached', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'b', 'add'], [3, 'bo', 'c', 'add']], 'bo', 2), [['a', 1, True], ['b', 1, True]]), ('stray remove does not free a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'z', 'remove'], [3, 'bo', 'b', 'add']], 'me', 1), [['a', 1, False]]), ('removal frees a slot', ([[1, 'bo', 'a', 'add'], [2, 'bo', 'a', 'remove'], [3, 'bo', 'b', 'add']], 'bo', 1), [['b', 1, True]])]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
A remove for a reaction the user never held frees a slot, letting them exceed the limit.

events [seq, user, emoji, action] are applied in seq order. A user holds each emoji at most once and at most `limit` distinct emoji per message; an add beyond the limit is ignored. A remove only releases a reaction the user holds. Result rows [emoji, count, mine] for emoji with at least one user, ordered by the seq at which the emoji most recently went from zero to one user.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        else:
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts <= 6000)
    who = [names.get(u, u) for _, u in active]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) == 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        else:
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and abs(now - ts) < 6000)
    who = [names.get(u, u) for _, u in active]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) == 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
Typing indicators linger for the whole boundary tick after the timeout.

events [ts_ms, user, kind] with kind start, stop or message are applied in (ts, user, kind) order. A user is typing when their latest start has no later stop or message and now - start < 6000 ms (a start stamped in the future by a skewed clock stays active). The viewer is excluded. Typers are listed by start time: one -> "A is typing", two -> "A and B are typing", more -> "Several people are typing", none -> "". names maps ids to display names.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        elif kind == 'stop':
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts < 6000)
    who = [names.get(u, u) for _, u in active]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) == 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        elif kind in ('stop', 'send'):
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts < 6000)
    who = [names.get(u, u) for _, u in active]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) == 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
After a user sends their message the indicator keeps saying they are typing.

events [ts_ms, user, kind] with kind start, stop or message are applied in (ts, user, kind) order. A user is typing when their latest start has no later stop or message and now - start < 6000 ms (a start stamped in the future by a skewed clock stays active). The viewer is excluded. Typers are listed by start time: one -> "A is typing", two -> "A and B are typing", more -> "Several people are typing", none -> "". names maps ids to display names.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        else:
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts < 6000)
    who = sorted(names.get(u, u) for _, u in active)
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) == 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        else:
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts < 6000)
    who = [names.get(u, u) for _, u in reversed(active)]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) == 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
Typers are listed alphabetically instead of by who started first.

events [ts_ms, user, kind] with kind start, stop or message are applied in (ts, user, kind) order. A user is typing when their latest start has no later stop or message and now - start < 6000 ms (a start stamped in the future by a skewed clock stays active). The viewer is excluded. Typers are listed by start time: one -> "A is typing", two -> "A and B are typing", more -> "Several people are typing", none -> "". names maps ids to display names.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        else:
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts < 6000)
    who = [names.get(u, u) for _, u in active]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) >= 2:
        return who[0] + ' and ' + who[1] + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(events, me, now, names):
    last = {}
    for ts, user, kind in sorted(events):
        if kind == 'start':
            last[user] = ts
        else:
            last.pop(user, None)
    active = sorted((ts, user) for user, ts in last.items() if user != me and now - ts < 6000)
    who = [names.get(u, u) for _, u in active]
    if not who:
        return ''
    if len(who) == 1:
        return who[0] + ' is typing'
    if len(who) >= 2:
        return ' and '.join(who) + ' are typing'
    return 'Several people are typing'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('exact expiry', ([[1000, 'bo', 'start']], 'me', 7000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50001, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [301, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [701, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 11, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2501, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 21, {}), '')], 2: [('exact expiry', ([[2000, 'bo', 'start']], 'me', 8000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50002, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [302, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [702, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 12, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2502, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 22, {}), '')], 3: [('exact expiry', ([[3000, 'bo', 'start']], 'me', 9000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50003, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [303, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [703, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 13, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2503, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 23, {}), '')], 4: [('exact expiry', ([[4000, 'bo', 'start']], 'me', 10000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50004, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [304, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [704, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 14, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2504, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 24, {}), '')], 5: [('exact expiry', ([[5000, 'bo', 'start']], 'me', 11000, {'bo': 'Bo'}), ''), ('future stamped start', ([[50005, 'bo', 'start']], 'me', 40000, {}), 'bo is typing'), ('message clears typing', ([[100, 'bo', 'start'], [305, 'bo', 'message']], 'me', 1000, {'bo': 'Bo'}), ''), ('start order', ([[500, 'zed', 'start'], [705, 'amy', 'start']], 'me', 2000, {'zed': 'Zed', 'amy': 'Amy'}), 'Zed and Amy are typing'), ('three typers', ([[1, 'a', 'start'], [2, 'b', 'start'], [3, 'c', 'start']], 'me', 15, {'a': 'A', 'b': 'B', 'c': 'C'}), 'Several people are typing'), ('stop overtaken in transit', ([[2505, 'bo', 'stop'], [2100, 'bo', 'start'], [2600, 'cy', 'start']], 'me', 3000, {'cy': 'Cy'}), 'Cy is typing'), ('self typing hidden', ([[10, 'me', 'start']], 'me', 25, {}), '')]}
for _label, _args, _expected in _CASES[N]:
    check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)

prompt:
With three typers the indicator silently drops the third name.

events [ts_ms, user, kind] with kind start, stop or message are applied in (ts, user, kind) order. A user is typing when their latest start has no later stop or message and now - start < 6000 ms (a start stamped in the future by a skewed clock stays active). The viewer is excluded. Typers are listed by start time: one -> "A is typing", two -> "A and B are typing", more -> "Several people are typing", none -> "". names maps ids to display names.

split: open-access