#include <cassert>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <optional>
#include <string>
#include <vector>

bool valid_score(double value) {
    return std::isfinite(value) && value >= 0.0 && value <= 20.0;
}

std::optional<double> average(const std::vector<double>& scores) {
    if (scores.empty()) return std::nullopt;
    const double total = std::accumulate(scores.begin(), scores.end(), 0.0);
    return total / static_cast<double>(scores.size());
}

void add_bonus(std::vector<double>& scores, double bonus) {
    for (double& score : scores) {
        score = std::min(20.0, std::max(0.0, score + bonus));
    }
}

int main(int argc, char* argv[]) {
    if (argc == 2 && std::string(argv[1]) == "--self-test") {
        assert(!average({}).has_value());
        assert(average({12.0, 16.0, 20.0}).value() == 16.0);
        assert(valid_score(0.0) && valid_score(20.0));
        assert(!valid_score(-1.0) && !valid_score(21.0));
        std::vector<double> sample{19.0, 10.0};
        add_bonus(sample, 2.0);
        assert(sample.at(0) == 20.0 && sample.at(1) == 12.0);
        std::cout << "C++ grades: all checks passed\n";
        return 0;
    }
    std::cout << "Enter scores from 0 to 20; finish with EOF:\n";
    std::vector<double> scores;
    double value;
    while (std::cin >> value) {
        if (!valid_score(value)) {
            std::cerr << "Score must be finite and between 0 and 20.\n";
            return 1;
        }
        scores.push_back(value);
    }
    if (!std::cin.eof()) {
        std::cerr << "Invalid numeric input.\n";
        return 1;
    }
    const auto result = average(scores);
    if (!result) {
        std::cout << "No scores were entered.\n";
        return 0;
    }
    std::cout << "Average: " << std::fixed << std::setprecision(2)
              << *result << '\n';
}
