# Учебная редакция: назначение, единицы и этапы алгоритма пояснены по-русски.
# Исходная программа остаётся самостоятельным свидетельством прежней проверки.
# Основа — II00-4.py: аналитическая функция расчёта с явно отмеченным отказом.
def series(planned_ids, solve):
    """Выполнить каждый уникальный режим, сохранив результат либо явную причину отказа.
    
    solve принимает идентификатор режима и возвращает потокосцепление, Вб.
    Значение None обозначает отказ; оно не заменяется физическим нулём."""
    # Повторение идентификатора сделало бы сопоставление режимов неоднозначным.
    if len(set(planned_ids)) != len(planned_ids):
        raise ValueError("Duplicate planned identifier")
    rows = []
    for mode in planned_ids:
        try:
            rows.append({"id": mode, "status": "ok", "value": solve(mode), "reason": ""})
        except Exception as error:
            rows.append({"id": mode, "status": "error", "value": None, "reason": str(error)})
    return rows


def write_csv(path, rows):
    # Для сохранения предыдущей серии задайте новый каталог: режим w заменяет файл.
    """Записать таблицу CSV с экранированием кавычек в описании отказа.
    
    Файл по указанному пути перезаписывается. В этом эталоне идентификаторы
    режимов — целые числа, значения — действительные потокосцепления в Вб."""
    with open(path, "w") as stream:
        stream.write("planned_id,status,linkage_Wb,reason\n")
        for row in rows:
            # По правилам CSV двойная кавычка внутри поля удваивается; перевод строки убирается.
            reason = row["reason"].replace('"', '""').replace("\n", " ").replace("\r", " ")
            # Пустое числовое поле сохраняет различие между отказом и корректным нулём.
            value = "" if row["value"] is None else "%.12g" % row["value"]
            stream.write('%s,%s,%s,"%s"\n' % (row["id"], row["status"], value, reason))


def run_checks():
    """Проверить воспроизводимость, порядок пяти режимов и один запланированный отказ."""
    def control(mode):
        """Вернуть аналитическое потокосцепление 0,12·I либо отказ для режима 2."""
        if mode == 2:
            raise ValueError("Planned analytic failure")
        return .12 * mode
    one, two = series([0, 1, 2, 3, 4], control), series([0, 1, 2, 3, 4], control)
    assert one == two and len(one) == 5
    assert one[2]["status"] == "error" and one[2]["value"] is None and one[2]["reason"]
    assert [r["id"] for r in one] == [0, 1, 2, 3, 4]
    return {"planned": 5, "recorded": 5, "ok": 4, "error": 1, "repeat_equal": True}
