#pragma once

#include <iostream>
#include <vector>
#include <memory>
#include <sstream>
#include <cstring>
#include "robin_hood.hpp"

typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef float f32;
typedef double f64;

template <typename T> const std::shared_ptr<T>&
shared_ptr_not_none(const std::shared_ptr<T>& obj);

size_t encode_utf8(char *dst_p, i32 ch);

// To make str(bool) and print(bool) show True and False.
struct Bool {
    bool m_value;

    Bool() : m_value(false)
    {
    }

    Bool(bool value) : m_value(value)
    {
    }

    operator bool() const
    {
        return m_value;
    }
};

struct Char {
    i32 m_value;

    Char() : m_value(-1)
    {
    }

    Char(i32 value) : m_value(value)
    {
    }

    bool operator==(const Char& other) const
    {
        return m_value == other.m_value;
    }

    bool operator!=(const Char& other) const
    {
        return m_value != other.m_value;
    }
};

std::ostream& operator<<(std::ostream& os, const Char& obj);

// A bytes. Should be unicode, but is ascii atm.
class Bytes final {

public:
    std::shared_ptr<std::vector<u8>> m_bytes;

    Bytes() : m_bytes(nullptr)
    {
    }

    Bytes(std::initializer_list<u8> il) :
        m_bytes(std::make_shared<std::vector<u8>>(il))
    {
    }

    u8& operator[](u64 index) const;

    bool operator==(const Bytes& other) const
    {
        return *m_bytes == *other.m_bytes;
    }

    bool operator!=(const Bytes& other) const
    {
        return *m_bytes != *other.m_bytes;
    }

    void operator+=(const Bytes& other) const
    {
        m_bytes->insert(m_bytes->end(),
                        other.m_bytes->begin(),
                        other.m_bytes->end());
    }

    void operator+=(u8 other) const
    {
        m_bytes->push_back(other);
    }

    int __len__() const
    {
        return shared_ptr_not_none(m_bytes)->size();
    }
};

// A string.
class String final {

public:
    std::shared_ptr<std::vector<Char>> m_string;

    String() : m_string(nullptr)
    {
    }

    String(const char *str)
    {
        if (str) {
            m_string = std::make_shared<std::vector<Char>>();

            for (int i = 0; i < strlen(str); i++) {
                m_string->push_back(str[i]);
            }
        } else {
            m_string = nullptr;
        }
    }

    String(const std::string& str) : String(str.c_str())
    {
    }

    String(std::initializer_list<Char> il) :
        m_string(std::make_shared<std::vector<Char>>(il))
    {
    }

    String(const Bytes& bytes) : m_string(nullptr)
    {
    }

    String(i8 value) : String(std::to_string(value))
    {
    }

    String(i16 value) : String(std::to_string(value))
    {
    }

    String(i32 value) : String(std::to_string(value))
    {
    }

    String(i64 value) : String(std::to_string(value))
    {
    }

    String(u8 value) : String(std::to_string(value))
    {
    }

    String(u16 value) : String(std::to_string(value))
    {
    }

    String(u32 value) : String(std::to_string(value))
    {
    }

    String(u64 value) : String(std::to_string(value))
    {
    }

    String(f32 value) : String(std::to_string(value))
    {
    }

    String(f64 value) : String(std::to_string(value))
    {
    }

    String(Bool value) : String(value ? "True" : "False")
    {
    }

    String(const Char& value) : String(std::initializer_list<Char>{value})
    {
    }

    void operator+=(const String& other) const
    {
        m_string->insert(m_string->end(),
                         other.m_string->begin(),
                         other.m_string->end());
    }

    void operator+=(const Char& other) const
    {
        m_string->push_back(other);
    }

    String operator+(const String& other)
    {
        String res("");

        res += *this;
        res += other;

        return res;
    }

    String operator*(int value) const
    {
        String res;
        int i;

        for (i = 0; i < value; i++) {
            res += *this;
        }

        return res;
    }

    bool operator==(const String& other) const
    {
        return *m_string == *other.m_string;
    }

    bool operator!=(const String& other) const
    {
        return !(*this == other);
    }

    Bytes to_utf8() const
    {
        Bytes res({});
        size_t size;
        char buf[4];

        for (const auto & ch : *m_string) {
            size = encode_utf8(&buf[0], ch.m_value);

            for (size_t i = 0; i < size; i++) {
                res += buf[i];
            }
        }

        return res;
    }

    void upper() const
    {
        for (auto& ch : *m_string) {
            ch.m_value = toupper(ch.m_value);
        }
    }

    void lower() const
    {
        for (auto& ch : *m_string) {
            ch.m_value = tolower(ch.m_value);
        }
    }

    Bool starts_with(const String& value) const
    {
        size_t value_length = value.__len__();

        if (value_length > m_string->size()) {
            return Bool(false);
        }

        for (u64 i = 0; i < value_length; i++) {
            if ((*m_string)[i] != (*value.m_string)[i]) {
                return false;
            }
        }

        return true;
    }

    Char& get(u64 index) const;

    int __len__() const
    {
        return shared_ptr_not_none(m_string)->size();
    }

    String __str__() const
    {
        String res("");

        res.m_string->insert(res.m_string->end(),
                             shared_ptr_not_none(m_string)->begin(),
                             shared_ptr_not_none(m_string)->end());

        return res;
    }

    i64 __int__() const;
};

namespace std
{
    template<> struct hash<String>
    {
        std::size_t operator()(String const& s) const noexcept
        {
            // ToDo
            return 0;
        }
    };

    template<> struct hash<Bool>
    {
        std::size_t operator()(Bool const& s) const noexcept
        {
            return s.m_value;
        }
    };
}

class Exception : public std::exception {

public:
    String m_what;

    Exception() : Exception("Exception")
    {
    }

    Exception(const char *name_p) : Exception(name_p, "")
    {
    }

    Exception(const char *name_p, String message)
    {
        m_what = String(name_p);
        m_what += ": ";
        m_what += message;
    }

    virtual ~Exception()
    {
    }

    virtual const char *what() const noexcept
    {
        return "todo";
    }
};

std::ostream& operator<<(std::ostream& os, const Exception& e);

class TypeError : public Exception {

public:
    TypeError() : TypeError("")
    {
    }

    TypeError(String message) :
        Exception("TypeError", message)
    {
    }
};

class ValueError : public Exception {

public:

    ValueError() : ValueError("")
    {
    }

    ValueError(String message) :
        Exception("ValueError", message)
    {
    }
};

class GeneralError : public Exception {

public:

    GeneralError() : GeneralError("")
    {
    }

    GeneralError(String message) :
        Exception("GeneralError", message)
    {
    }
};

class NoneError : public Exception {

public:

    NoneError() : NoneError("")
    {
    }

    NoneError(String message) :
        Exception("NoneError", message)
    {
    }
};

class KeyError : public Exception {

public:

    KeyError() : KeyError("")
    {
    }

    KeyError(String message) :
        Exception("KeyError", message)
    {
    }
};

class IndexError : public Exception {

public:

    IndexError() : IndexError("")
    {
    }

    IndexError(String message) :
        Exception("IndexError", message)
    {
    }
};

class NotImplementedError : public Exception {

public:
    NotImplementedError() : NotImplementedError("")
    {
    }

    NotImplementedError(String message) :
        Exception("NotImplementedError", message)
    {
    }
};

class ZeroDivisionError : public Exception {

public:
    ZeroDivisionError() : ZeroDivisionError("")
    {
    }

    ZeroDivisionError(String message) :
        Exception("ZeroDivisionError", message)
    {
    }
};

class AssertionError : public Exception {

public:
    AssertionError() : AssertionError("")
    {
    }

    AssertionError(String message) :
        Exception("AssertionError", message)
    {
    }
};

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::shared_ptr<T>& obj)
{
    if (obj) {
        os << *obj;
    } else {
        os << "None";
    }

    return os;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& obj)
{
    const char *delim_p;

    os << "[";
    delim_p = "";

    for (auto item = obj.begin(); item != obj.end(); item++, delim_p = ", ") {
        os << delim_p << *item;
    }

    os << "]";

    return os;
}

std::ostream& operator<<(std::ostream& os, const Bool& obj);

// Tuples.
template<class T, size_t... I> std::ostream&
format_tuple(std::ostream& os,
             const T& tup,
             std::index_sequence<I...>)
{
    os << "(";
    (..., (os << (I == 0 ? "" : ", ") << std::get<I>(tup)));
    os << ")";

    return os;
}

template<class... T> std::ostream&
operator<<(std::ostream& os, const std::tuple<T...>& tup)
{
    return format_tuple(os, tup, std::make_index_sequence<sizeof...(T)>());
}

template<class... T>
class Tuple {
public:
    std::tuple<T...> m_tuple;

    Tuple(const T&... args) : m_tuple(std::tuple<T...>(args...))
    {
    }

    String __str__() const
    {
        std::stringstream ss;
        ss << m_tuple;
        return String(ss.str().c_str());
    }
};

template<class... T>
std::ostream&
operator<<(std::ostream& os, const Tuple<T...>& obj)
{
    os << obj.m_tuple;

    return os;
}

template<class... T> bool
operator==(const std::shared_ptr<Tuple<T...>>& a,
           const std::shared_ptr<Tuple<T...>>& b)
{
    return shared_ptr_not_none(a)->m_tuple == shared_ptr_not_none(b)->m_tuple;
}

template<class... T> bool
operator!=(const std::shared_ptr<Tuple<T...>>& a,
           const std::shared_ptr<Tuple<T...>>& b)
{
    return !(a == b);
}

// Lists.
template<typename T>
class List final
{
public:
    std::vector<T> m_list;

    List() : m_list(std::vector<T>())
    {
    }

    List(const std::vector<T>& list) : m_list(list)
    {
    }

    List(std::initializer_list<T> il) : m_list(std::vector<T>(il))
    {
    }

    void sort() const
    {
        throw NotImplementedError("sort list");
    }

    void reverse() const
    {
        throw NotImplementedError("reverse list");
    }

    T get(size_t pos) const
    {
        try {
            return m_list.at(pos);
        } catch (const std::out_of_range& e) {
            throw IndexError("out of range");
        }
    }

    T& get(size_t pos)
    {
        try {
            return m_list.at(pos);
        } catch (const std::out_of_range& e) {
            throw IndexError("out of range");
        }
    }

    std::shared_ptr<List<T>> operator*(int value) const
    {
        auto res = std::make_shared<List<T>>();
        int i;

        for (i = 0; i < value; i++) {
            for (auto item: m_list) {
                res->append(item);
            }
        }

        return res;
    }

    bool operator==(const List<T>& other) const
    {
        size_t i;

        if (m_list.size() != other.m_list.size()) {
            return false;
        }

        for (i = 0; i < m_list.size(); i++) {
            if (m_list[i] != other.m_list[i]) {
                return false;
            }
        }

        return true;
    }

    typename std::vector<T>::iterator begin() const
    {
        return m_list.begin();
    }

    typename std::vector<T>::iterator end() const
    {
        return m_list.end();
    }

    typename std::vector<T>::iterator begin()
    {
        return m_list.begin();
    }

    typename std::vector<T>::iterator end()
    {
        return m_list.end();
    }

    typename std::vector<T>::reverse_iterator rbegin() const
    {
        return m_list.rbegin();
    }

    typename std::vector<T>::reverse_iterator rend() const
    {
        return m_list.rend();
    }

    typename std::vector<T>::reverse_iterator rbegin()
    {
        return m_list.rbegin();
    }

    typename std::vector<T>::reverse_iterator rend()
    {
        return m_list.rend();
    }

    bool operator!=(const List<T>& other) const
    {
        return !(*this == other);
    }

    void append(const T& item)
    {
        m_list.push_back(item);
    }

    int __len__() const
    {
        return m_list.size();
    }

    T __min__() const
    {
        T minimum;

        if (m_list.size() == 0) {
            throw ValueError("min() arg is an empty sequence");
        }

        minimum = m_list[0];

        for (auto item: m_list) {
            if (item < minimum) {
                minimum = item;
            }
        }

        return minimum;
    }

    T __max__() const
    {
        T maximum;

        if (m_list.size() == 0) {
            throw ValueError("max() arg is an empty sequence");
        }

        maximum = m_list[0];

        for (auto item: m_list) {
            if (item > maximum) {
                maximum = item;
            }
        }

        return maximum;
    }

    T __sum__() const
    {
        T sum = 0;

        for (auto item: m_list) {
            sum += item;
        }

        return sum;
    }

    bool __contains__(const T& value) const
    {
        for (auto item: m_list) {
            if (item == value) {
                return true;
            }
        }

        return false;
    }

    String __str__() const
    {
        std::stringstream ss;
        ss << m_list;
        return String(ss.str().c_str());
    }
};

template<typename T> std::ostream&
operator<<(std::ostream& os, const List<T>& obj)
{
    os << obj.m_list;

    return os;
}

template<typename T> bool
operator==(const std::shared_ptr<List<T>>& a,
           const std::shared_ptr<List<T>>& b)
{
    return shared_ptr_not_none(a)->m_list == shared_ptr_not_none(b)->m_list;
}

template<typename T> bool
operator!=(const std::shared_ptr<List<T>>& a,
           const std::shared_ptr<List<T>>& b)
{
    return !(a == b);
}

template<typename T> std::shared_ptr<List<T>>
operator+(const std::shared_ptr<List<T>>& a,
          const std::shared_ptr<List<T>>& b)
{
    auto list = std::make_shared<List<T>>();
    auto aa = shared_ptr_not_none(a);
    auto bb = shared_ptr_not_none(b);
    list->m_list.reserve(distance(aa->m_list.begin(), aa->m_list.end())
                         + distance(bb->m_list.begin(), bb->m_list.end()));
    list->m_list.insert(list->m_list.end(), aa->m_list.begin(), aa->m_list.end());
    list->m_list.insert(list->m_list.end(), bb->m_list.begin(), bb->m_list.end());
    return list;
}

// Dicts.
template<typename TK, typename TV>
class Dict final
{
public:
    robin_hood::unordered_map<TK, TV> m_map;

    Dict() : m_map(robin_hood::unordered_map<TK, TV>())
    {
    }

    Dict(std::initializer_list<robin_hood::pair<TK, TV>> il) :
        m_map(robin_hood::unordered_map<TK, TV>(il))
    {
    }

    // Only used to get an item, not set.
    // ToDo: Throw KeyError if missing.
    TV& operator[](const TK& key)
    {
        return m_map[key];
    }

    // Only used to get an item, not set.
    // ToDo: Throw KeyError if missing.
    TV& operator[](TK& key)
    {
        return m_map[key];
    }

    void set(const TK& key, const TV& value)
    {
        m_map[key] = value;
    }

    const TV& get(const TK& key, const TV& value)
    {
        auto it = m_map.find(key);

        if (it != m_map.end()) {
            return it->second;
        } else {
            return value;
        }
    }

    std::shared_ptr<List<TK>> keys() const
    {
        std::vector<TK> keys;
        for (const auto& kv : m_map) {
            keys.push_back(kv.first);
        }
        return std::make_shared<List<TK>>(keys);
    }

    std::shared_ptr<List<TV>> values() const
    {
        std::vector<TV> values;
        for (const auto& kv : m_map) {
            values.push_back(kv.second);
        }
        return std::make_shared<List<TV>>(values);
    }

    int __len__() const
    {
        return m_map.size();
    }

    bool __contains__(const TK& key) const
    {
        return m_map.count(key) > 0;
    }

    String __str__() const
    {
        std::stringstream ss;
        ss << *this;
        return String(ss.str().c_str());
    }
};

template<class TK, class TV> std::ostream&
operator<<(std::ostream& os, const Dict<TK, TV>& dict)
{
    const char *delim_p;

    os << "{";
    delim_p = "";

    for (auto item = dict.m_map.begin();
         item != dict.m_map.end();
         item++, delim_p = ", ") {
        os << delim_p << item->first << ": " << item->second;
    }

    os << "}";

    return os;
}

template<typename TK, typename TV> bool
operator==(const std::shared_ptr<Dict<TK, TV>>& a,
           const std::shared_ptr<Dict<TK, TV>>& b)
{
    return shared_ptr_not_none(a)->m_map == shared_ptr_not_none(b)->m_map;
}

template<typename TK, typename TV> bool
operator!=(const std::shared_ptr<Dict<TK, TV>>& a,
           const std::shared_ptr<Dict<TK, TV>>& b)
{
    return !(a == b);
}

// Integer power (a ** b).
template <typename TB, typename TE>
TB ipow(TB base, TE exp)
{
    TB result = 1;

    while (exp != 0) {
        if ((exp & 1) == 1) {
            result *= base;
        }

        exp >>= 1;
        base *= base;
    }

    return result;
}

// Exception output.
std::ostream& operator<<(std::ostream& os, const std::exception& e);

#define ASSERT(cond)                            \
    if (!(cond)) {                              \
        throw AssertionError(#cond);            \
    }

std::ostream&
operator<<(std::ostream& os, const String& obj);

std::ostream&
operator<<(std::ostream& os, const Bytes& obj);

static inline String operator*(int value, const String& string)
{
    return string * value;
}

static inline String operator+(const char *value_p, const String& string)
{
    String res(value_p);

    res += string;

    return res;
}

template<typename T>
static inline List<T> operator*(int value, const List<T>& list)
{
    return list * value;
}

std::shared_ptr<List<String>> create_args(int argc, const char *argv[]);

template <typename T1, typename T2, typename... Tail>
auto vmin(T1&& v1, T2&& v2, Tail&&... tail)
{
    if constexpr (sizeof...(tail) == 0) {
        return v1 < v2 ? v1 : v2;
    } else {
        return vmin(vmin(v1, v2), tail...);
    }
}

template <typename T, typename... Tail>
auto min(T&& obj, Tail&&... tail)
{
    if constexpr (sizeof...(tail) == 0) {
        return obj->__min__();
    } else {
        return vmin(obj, tail...);
    }
}

template <typename T1, typename T2, typename... Tail>
auto vmax(T1&& v1, T2&& v2, Tail&&... tail)
{
    if constexpr (sizeof...(tail) == 0) {
        return v1 > v2 ? v1 : v2;
    } else {
        return vmax(vmax(v1, v2), tail...);
    }
}

template <typename T, typename... Tail>
auto max(T&& obj, Tail&&... tail)
{
    if constexpr (sizeof...(tail) == 0) {
        return obj->__max__();
    } else {
        return vmax(obj, tail...);
    }
}

template <typename T>
auto sum(T obj)
{
    return obj.__sum__();
}

template <typename TI, typename TC>
bool contains(const TI& item, const TC& container)
{
    return container->__contains__(item);
}

using std::abs;

static inline String chr(int value)
{
    char buf[2] = {(char)value, '\0'};

    return String(buf);
}

#define assert_eq(v1, v2)                                               \
    if (!((v1) == (v2))) {                                              \
        std::cout << "Assert: " << (v1) << " != " << (v2) << std::endl; \
                                                                        \
        throw AssertionError("assert_eq failed");                       \
    }

class Test;

extern Test *tests_head_p;
extern Test *tests_tail_p;

typedef void (*test_func_t)(void);

class Test {

public:
    const char *m_name_p;
    test_func_t m_func;
    Test *m_next_p;

    Test(const char *name_p, test_func_t func) {
        m_name_p = name_p;
        m_func = func;
        m_next_p = NULL;

        if (tests_head_p == NULL) {
            tests_head_p = this;
        } else {
            tests_tail_p->m_next_p = this;
        }

        tests_tail_p = this;
    }
};

class Object {

public:

    virtual void __format__(std::ostream& os) const
    {
    }

    virtual String __str__() const
    {
        return String("Object()");
    }
};

/* slice(), enumerate() and range() used in for loops. */

struct Slice {
    i64 m_begin;
    i64 m_end;
    i64 m_step;

    Slice(i64 begin, i64 end, i64 step, i64 length) {
        if (step == 0) {
            throw ValueError("slice step can't be zero");
        }

        if (begin < 0) {
            begin += length;

            if (begin < 0) {
                begin = 0;
            }
        }

        if (end < 0) {
            end += length;

            if (end < 0) {
                end = 0;
            }
        }

        if (((begin < end) && (step < 0))
            || ((begin > end) && (step > 0))
            || (begin == end)) {
            begin = 0;
            end = 0;
            step = 1;
        }

        m_begin = begin;
        m_end = end;
        m_step = step;
    }

    i64 length() {
        if (m_begin <= m_end) {
            return (m_end - m_begin + m_step - 1) / m_step;
        } else {
            return (m_end - m_begin + m_step + 1) / m_step;
        }
    }
};

class OpenSlice {

public:
    i64 m_begin;

    OpenSlice(i64 begin) {
        if (begin < 0) {
            begin = 0;
        }

        m_begin = begin;
    }
};

template <typename T>
class Range {

public:
    T m_begin;
    T m_end;
    T m_step;
    T m_next;

    Range(T end) : m_begin(0), m_end(end), m_step(1) {
    }

    Range(T begin, T end) : m_begin(begin), m_end(end), m_step(1) {
    }

    Range(T begin, T end, T step) : m_begin(begin), m_end(end), m_step(step) {
    }

    void iter() {
        m_next = m_begin;
    }

    T next() {
        T next = m_next;
        m_next += m_step;
        return next;
    }

    void slice(Slice& slice) {
        T begin = (m_begin + slice.m_begin * m_step);

        if (begin > m_end) {
            begin = m_end;
        }

        m_begin = begin;
        T end = m_begin + slice.length() * slice.m_step;

        if (end > m_end) {
            end = m_end;
        }

        m_end = end;
        m_step *= slice.m_step;
    }

    void slice(OpenSlice& slice) {
        m_begin += (slice.m_begin * m_step);
    }

    void reversed() {
        T begin;
        T l;

        begin = m_begin;
        l = length();
        m_begin = begin + (l - 1) * m_step;
        m_end = m_begin - (l - 1) * m_step;

        if (m_step > 0) {
            m_end--;
        } else {
            m_end++;
        }

        m_step *= -1;
    }

    i64 length() {
        if (m_step > 0) {
            return (m_end - m_begin + m_step - 1) / m_step;
        } else {
            return (m_end - m_begin + m_step + 1) / m_step;
        }
    }
};

template <typename T>
class Enumerate {

public:
    T m_begin;
    T m_end;
    T m_step;
    T m_next;

    Enumerate(T begin, T end) {
        m_begin = begin;
        m_end = begin + end;
        m_step = 1;
    }

    void iter() {
        m_next = m_begin;
    }

    T next() {
        T next = m_next;
        m_next += m_step;
        return next;
    }

    void slice(Slice& slice) {
        T begin = (m_begin + slice.m_begin * m_step);

        if (begin > m_end) {
            begin = m_end;
        }

        m_begin = begin;
        T end = m_begin + slice.length() * slice.m_step;

        if (end > m_end) {
            end = m_end;
        }

        m_end = end;
        m_step *= slice.m_step;
    }

    void slice(OpenSlice& slice) {
        m_begin += slice.m_begin;
    }

    void reversed() {
        T begin;
        T l;

        begin = m_begin;
        l = length();
        m_begin = begin + (l - 1) * m_step;
        m_end = m_begin - (l - 1) * m_step;

        if (m_step > 0) {
            m_end--;
        } else {
            m_end++;
        }

        m_step *= -1;
    }

    i64 length() {
        if (m_step > 0) {
            return (m_end - m_begin + m_step - 1) / m_step;
        } else {
            return (m_end - m_begin + m_step + 1) / m_step;
        }
    }
};

template <typename T>
class Data {

public:
    T m_begin;
    T m_end;
    T m_step;
    T m_next;

    Data(T end) : m_begin(0), m_end(end), m_step(1) {
    }

    void iter() {
        m_next = m_begin;
    }

    T next() {
        T next = m_next;
        m_next += m_step;
        return next;
    }

    void slice(Slice& slice) {
        T begin = (m_begin + slice.m_begin * m_step);

        if (begin > m_end) {
            begin = m_end;
        }

        m_begin = begin;
        T end = m_begin + slice.length() * slice.m_step;

        if (end > m_end) {
            end = m_end;
        }

        m_end = end;
        m_step *= slice.m_step;
    }

    void slice(OpenSlice& slice) {
        m_begin += slice.m_begin;
    }

    void reversed() {
        T begin;
        T l;

        begin = m_begin;
        l = length();
        m_begin = begin + (l - 1) * m_step;
        m_end = m_begin - (l - 1) * m_step;

        if (m_step > 0) {
            m_end--;
        } else {
            m_end++;
        }

        m_step *= -1;
    }

    i64 length() {
        if (m_step > 0) {
            return (m_end - m_begin + m_step - 1) / m_step;
        } else {
            return (m_end - m_begin + m_step + 1) / m_step;
        }
    }
};

template <typename T> const std::shared_ptr<T>&
shared_ptr_not_none(const std::shared_ptr<T>& obj)
{
    if (!obj) {
        throw NoneError("object is None");
    }

    return obj;
}

const String& string_not_none(const String& obj);

const Bytes& bytes_not_none(const Bytes& obj);

template<typename TK, typename TV>
std::shared_ptr<List<std::shared_ptr<Tuple<TK, TV>>>>
create_list_from_dict(const std::shared_ptr<Dict<TK, TV>>& dict)
{
    auto list = std::make_shared<List<std::shared_ptr<Tuple<TK, TV>>>>();

    for (auto const& [key, value] : shared_ptr_not_none(dict)->m_map) {
        list->append(std::make_shared<Tuple<TK, TV>>(key, value));
    }

    return list;
}

template<typename T> bool
is(const std::shared_ptr<T>& a, const std::shared_ptr<T>& b)
{
    return a.get() == b.get();
}

template<typename T> bool
is(const std::shared_ptr<T>& a, void *b)
{
    return a.get() == nullptr;
}

template<typename T> bool
is(void *a, const std::shared_ptr<T>& b)
{
    return b.get() == nullptr;
}

// For nullptr == nullptr
static inline bool is(void *a, void *b)
{
    return a == b;
}

std::ostream& operator<<(std::ostream& os, const Object& obj);
