#include "testlib.h"

const int64_t TOKEN_LIMIT = 600 * 50;

std::string form_case_insensitive_pattern_str(const std::string& s) {
    std::string pattern_str;
    for (char c : s)
        if (tolower(c) != toupper(c))
            pattern_str += std::format("[{}{}]", char(tolower(c)), char(toupper(c)));
        else
            pattern_str += std::format("\\{}", c);
    return pattern_str;
}

const std::string COLLATZ_QUERY = "collatz";
const std::string COLLATZ_QUERY_STR = form_case_insensitive_pattern_str(COLLATZ_QUERY);
const std::string RANDOM_QUERY = "random";
const std::string RANDOM_QUERY_STR = form_case_insensitive_pattern_str(RANDOM_QUERY);
const pattern COLLATZ_QUERY_PATTERN(COLLATZ_QUERY_STR);
const pattern RANDOM_QUERY_PATTERN(RANDOM_QUERY_STR);
const pattern QUERY_PATTERN(std::format("{}|{}", COLLATZ_QUERY_STR, RANDOM_QUERY_STR));

std::string english_plural(const size_t n) {
    return n == 1 ? "" : "s";
}

struct Number {
    std::string number;

    Number operator+(const int n) const {
        std::string ans;
        int carry = n;
        auto process = [&](char c) {
            int d = c - '0';
            d += carry;
            ans.push_back(d % 10 + '0');
            carry = d / 10;
        };
        for (char c : number)
            process(c);
        while (carry)
            process('0');
        return Number{.number = ans};
    }

    Number operator*(const int n) const {
        std::string ans;
        int carry = 0;
        auto process = [&](char c) {
            int d = c - '0';
            d *= n;
            d += carry;
            ans.push_back(d % 10 + '0');
            carry = d / 10;
        };
        for (char c : number)
            process(c);
        while (carry)
            process('0');
        return Number{.number = ans};
    }

    int operator%(const int n) const {
        int r = 0;
        for (auto it = number.rbegin(); it != number.rend(); ++it)
            r = (r * 10 + (*it - '0')) % n;
        return r;
    }

    Number operator/(const int n) const {
        std::string ans;
        int r = 0;
        for (auto it = number.rbegin(); it != number.rend(); ++it) {
            r = r * 10 + (*it - '0');
            if (r / n > 0 || !ans.empty())
                ans.push_back('0' + r / n);
            r %= n;
        }
        std::reverse(ans.begin(), ans.end());
        return Number{.number = ans};
    }

    bool operator<(const Number &other) const {
        if (number.size() != other.number.size())
            return number.size() < other.number.size();
        for (ssize_t i = ssize(number) - 1; i >= 0; --i)
            if (number[i] != other.number[i])
                return number[i] < other.number[i];
        return false;
    }

    template<class Int>
    static Number fromInt(const Int& n) {
        Number ans{
            .number = std::to_string(n),
        };
        std::reverse(ans.number.begin(), ans.number.end());
        return ans;
    }
};

std::ostream& operator<<(std::ostream& out, const Number& n) {
    if (n.number.empty())
        return out << '0';
    for (ssize_t i = ssize(n.number) - 1; i >= 0; --i)
        out << n.number[i];
    return out;
}

Number random_in_big_segment(const Number& left, const Number& right) {
    while (true) {
        Number ans;
        for (ssize_t i = 0; i < ssize(right.number); ++i)
            ans.number.push_back(rnd.next('0', '9'));
        while (!ans.number.empty() && ans.number.back() == '0')
            ans.number.pop_back();
        if (!(right < ans) && !(ans < left))
            return ans;
    }
}

std::optional<std::string> interact_test(int64_t& spent_tokens, int64_t x_0) {
    Number x = Number::fromInt(x_0);
    static const Number ONE = Number::fromInt(1);
    for (size_t query_index = 1;; ++query_index) {
        std::cout << x << std::endl;
        if (!(ONE < x))
            return {};
        std::string command_name = std::format("query[{}]", query_index);
        std::string command = ouf.readToken(QUERY_PATTERN, command_name);
        if (COLLATZ_QUERY_PATTERN.matches(command))
            if (x % 2 == 0)
                x = x / 2;
            else
                x = x * 3 + 1;
        else
            x = random_in_big_segment(x * 3 + 1, x * 6);
        spent_tokens += x.number.size();
        if (spent_tokens > TOKEN_LIMIT) {
            std::cout << 0 << std::endl;
            return std::format("exceeded {} > {} limit of tokens", spent_tokens, TOKEN_LIMIT);
        }
    }
}

struct MultitestInteractionResult {
    size_t successful_tests;
    std::string comment;
};

MultitestInteractionResult interact_multitest() {
    int64_t tokens = 0;
    size_t t = inf.readUnsignedLong(std::numeric_limits<size_t>::min(), std::numeric_limits<size_t>::max(), "t");
    inf.readEoln();
    std::cout << t << '\n';
    std::vector<int64_t> x_0 = inf.readLongs(t, std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), "x_0");
    for (size_t test_index = 1; test_index <= t; ++test_index) {
        std::optional<std::string> comment = interact_test(tokens, x_0[test_index - 1]);
        if (comment)
            return {
                .successful_tests = test_index - 1,
                .comment = *comment,
            };
    }
    return {
        .successful_tests = t,
        .comment = std::format("spent {} token{}, on average {:.3f} per test", tokens, english_plural(tokens), tokens / std::max<double>(1, t)),
    };
}

int main(int argc, char* argv[]) {
    registerInteraction(argc, argv);
    MultitestInteractionResult result = interact_multitest();
    tout << result.successful_tests << '\n';
    tout << result.comment << '\n';
}
