// uchar_string_traits.hpp
#pragma once
#include <cstddef>
#include <cstring>
#include <string>
#include <ostream>
#include <type_traits>

static_assert(sizeof(unsigned char) == 1, "Assumes 8-bit unsigned char");

namespace std {

// Пользовательская специализация char_traits для unsigned char.
// Разрешено стандартом, т.к. char_traits предназначен для специализации.
template<>
struct char_traits<unsigned char> {
    using char_type   = unsigned char;
    using int_type    = int;             // согласовано с базовой специализацией
    using off_type    = std::streamoff;
    using pos_type    = std::streampos;
    using state_type  = std::mbstate_t;

    static void assign(char_type& r, const char_type& a) noexcept { r = a; }
    static constexpr bool eq(char_type a, char_type b) noexcept    { return a == b; }
    static constexpr bool lt(char_type a, char_type b) noexcept    { return a <  b; }

    static int compare(const char_type* s1, const char_type* s2, size_t n) noexcept {
        for (size_t i = 0; i < n; ++i) {
            if (lt(s1[i], s2[i])) return -1;
            if (lt(s2[i], s1[i])) return  1;
        }
        return 0;
    }

    static size_t length(const char_type* s) noexcept {
        // Ищем нуль-терминатор (0), как в C-строках
        size_t i = 0;
        while (s[i] != 0) ++i;
        return i;
    }

    static const char_type* find(const char_type* s, size_t n, const char_type& a) noexcept {
        for (size_t i = 0; i < n; ++i)
            if (eq(s[i], a)) return s + i;
        return nullptr;
    }

    static char_type* move(char_type* s1, const char_type* s2, size_t n) noexcept {
        if (s1 == s2 || n == 0) return s1;
        // корректно обрабатываем перекрытие
        std::memmove(s1, s2, n);
        return s1;
    }

    static char_type* copy(char_type* s1, const char_type* s2, size_t n) noexcept {
        if (n) std::memcpy(s1, s2, n);
        return s1;
    }

    static char_type* assign(char_type* s, size_t n, char_type a) noexcept {
        if (n) std::memset(s, a, n);
        return s;
    }

    static constexpr int_type to_int_type(char_type c) noexcept {
        return static_cast<unsigned int>(c);
    }

    static constexpr char_type to_char_type(int_type c) noexcept {
        return static_cast<char_type>(static_cast<unsigned int>(c) & 0xFFu);
    }

    static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept {
        return c1 == c2;
    }

    static constexpr int_type eof() noexcept {
        return -1;
    }

    static constexpr int_type not_eof(int_type c) noexcept {
        return (c == eof()) ? 0 : c;
    }
};

} // namespace std

// Удобный вывод: печатаем как байты/символы ASCII (без гарантий по локали)
inline std::ostream& operator<<(std::ostream& os, const std::basic_string<unsigned char>& s) {
    for (unsigned char ch : s) os.put(static_cast<char>(ch));
    return os;
}
