// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#ifndef NCNN_SIMPLESTL_H
#define NCNN_SIMPLESTL_H

#include <stddef.h>
#include <stdint.h>
#include <string.h>

#if !NCNN_SIMPLESTL

#include <new>

#else

// allocation functions
NCNN_EXPORT void* operator new(size_t size);
NCNN_EXPORT void* operator new[](size_t size);
// placement allocation functions
NCNN_EXPORT void* operator new(size_t size, void* ptr);
NCNN_EXPORT void* operator new[](size_t size, void* ptr);
// deallocation functions
NCNN_EXPORT void operator delete(void* ptr);
NCNN_EXPORT void operator delete[](void* ptr);
// deallocation functions since c++14
#if __cplusplus >= 201402L
NCNN_EXPORT void operator delete(void* ptr, size_t sz);
NCNN_EXPORT void operator delete[](void* ptr, size_t sz);
#endif
// placement deallocation functions
NCNN_EXPORT void operator delete(void* ptr, void* voidptr2);
NCNN_EXPORT void operator delete[](void* ptr, void* voidptr2);

#endif

// minimal stl data structure implementation
namespace std {

template<typename T>
const T& max(const T& a, const T& b)
{
    return (a < b) ? b : a;
}

template<typename T>
const T& min(const T& a, const T& b)
{
    return (a > b) ? b : a;
}

template<typename T>
void swap(T& a, T& b)
{
    T temp(a);
    a = b;
    b = temp;
}

template<typename T1, typename T2>
struct pair
{
    pair()
        : first(), second()
    {
    }
    pair(const T1& t1, const T2& t2)
        : first(t1), second(t2)
    {
    }

    T1 first;
    T2 second;
};

template<typename T1, typename T2>
bool operator==(const pair<T1, T2>& x, const pair<T1, T2>& y)
{
    return (x.first == y.first && x.second == y.second);
}
template<typename T1, typename T2>
bool operator<(const pair<T1, T2>& x, const pair<T1, T2>& y)
{
    return x.first < y.first || (!(y.first < x.first) && x.second < y.second);
}
template<typename T1, typename T2>
bool operator!=(const pair<T1, T2>& x, const pair<T1, T2>& y)
{
    return !(x == y);
}
template<typename T1, typename T2>
bool operator>(const pair<T1, T2>& x, const pair<T1, T2>& y)
{
    return y < x;
}
template<typename T1, typename T2>
bool operator<=(const pair<T1, T2>& x, const pair<T1, T2>& y)
{
    return !(y < x);
}
template<typename T1, typename T2>
bool operator>=(const pair<T1, T2>& x, const pair<T1, T2>& y)
{
    return !(x < y);
}

template<typename T1, typename T2>
pair<T1, T2> make_pair(const T1& t1, const T2& t2)
{
    return pair<T1, T2>(t1, t2);
}

template<typename T>
struct node
{
    node* prev_;
    node* next_;
    T data_;

    node()
        : prev_(0), next_(0), data_()
    {
    }
    node(const T& t)
        : prev_(0), next_(0), data_(t)
    {
    }
};

template<typename T>
struct iter_list
{
    iter_list()
        : curr_(0)
    {
    }
    iter_list(node<T>* n)
        : curr_(n)
    {
    }
    iter_list(const iter_list& i)
        : curr_(i.curr_)
    {
    }
    ~iter_list()
    {
    }

    iter_list& operator=(const iter_list& i)
    {
        curr_ = i.curr_;
        return *this;
    }

    T& operator*()
    {
        return curr_->data_;
    }
    T* operator->()
    {
        return &(curr_->data_);
    }

    bool operator==(const iter_list& i)
    {
        return curr_ == i.curr_;
    }
    bool operator!=(const iter_list& i)
    {
        return curr_ != i.curr_;
    }

    iter_list& operator++()
    {
        curr_ = curr_->next_;
        return *this;
    }
    iter_list& operator--()
    {
        curr_ = curr_->prev_;
        return *this;
    }

    node<T>* curr_;
};

template<typename T>
struct list
{
    typedef iter_list<T> iterator;

    list()
    {
        head_ = new node<T>();
        tail_ = head_;
        count_ = 0;
    }
    ~list()
    {
        clear();
        delete head_;
    }
    list(const list& l)
    {
        head_ = new node<T>();
        tail_ = head_;
        count_ = 0;

        for (iter_list<T> i = l.begin(); i != l.end(); ++i)
        {
            push_back(*i);
        }
    }

    list& operator=(const list& l)
    {
        if (this == &l)
        {
            return *this;
        }
        clear();

        for (iter_list<T> i = l.begin(); i != l.end(); ++i)
        {
            push_back(*i);
        }
        return *this;
    }

    void clear()
    {
        while (count_ > 0)
        {
            pop_front();
        }
    }

    void pop_front()
    {
        if (count_ > 0)
        {
            head_ = head_->next_;
            delete head_->prev_;
            head_->prev_ = 0;
            --count_;
        }
    }

    size_t size() const
    {
        return count_;
    }
    iter_list<T> begin() const
    {
        return iter_list<T>(head_);
    }
    iter_list<T> end() const
    {
        return iter_list<T>(tail_);
    }
    bool empty() const
    {
        return count_ == 0;
    }

    void push_back(const T& t)
    {
        if (count_ == 0)
        {
            head_ = new node<T>(t);
            head_->prev_ = 0;
            head_->next_ = tail_;
            tail_->prev_ = head_;
            count_ = 1;
        }
        else
        {
            node<T>* temp = new node<T>(t);
            temp->prev_ = tail_->prev_;
            temp->next_ = tail_;
            tail_->prev_->next_ = temp;
            tail_->prev_ = temp;
            ++count_;
        }
    }

    iter_list<T> erase(iter_list<T> pos)
    {
        if (pos != end())
        {
            node<T>* temp = pos.curr_;
            if (temp == head_)
            {
                ++pos;
                temp->next_->prev_ = 0;
                head_ = temp->next_;
            }
            else
            {
                --pos;
                temp->next_->prev_ = temp->prev_;
                temp->prev_->next_ = temp->next_;
                ++pos;
            }
            delete temp;
            --count_;
        }
        return pos;
    }

protected:
    node<T>* head_;
    node<T>* tail_;
    size_t count_;
};

template<typename T>
struct greater
{
    bool operator()(const T& x, const T& y) const
    {
        return (x > y);
    }
};

template<typename T>
struct less
{
    bool operator()(const T& x, const T& y) const
    {
        return (x < y);
    }
};

template<typename RandomAccessIter, typename Compare>
void partial_sort(RandomAccessIter first, RandomAccessIter middle, RandomAccessIter last, Compare comp)
{
    // [TODO] heap sort should be used here, but we simply use bubble sort now
    for (RandomAccessIter i = first; i < middle; ++i)
    {
        // bubble sort
        for (RandomAccessIter j = last - 1; j > first; --j)
        {
            if (comp(*j, *(j - 1)))
            {
                swap(*j, *(j - 1));
            }
        }
    }
}

template<typename T>
struct vector
{
    vector()
        : data_(0), size_(0), capacity_(0)
    {
    }
    vector(const size_t new_size, const T& value = T())
        : data_(0), size_(0), capacity_(0)
    {
        resize(new_size, value);
    }
    ~vector()
    {
        clear();
    }
    vector(const vector& v)
        : data_(0), size_(0), capacity_(0)
    {
        resize(v.size());
        for (size_t i = 0; i < size_; i++)
        {
            data_[i] = v.data_[i];
        }
    }

    vector& operator=(const vector& v)
    {
        if (this == &v)
        {
            return *this;
        }
        resize(0);
        resize(v.size());
        for (size_t i = 0; i < size_; i++)
        {
            data_[i] = v.data_[i];
        }
        return *this;
    }

    void resize(const size_t new_size, const T& value = T())
    {
        try_alloc(new_size);
        if (new_size > size_)
        {
            for (size_t i = size_; i < new_size; i++)
            {
                new (&data_[i]) T(value);
            }
        }
        else if (new_size < size_)
        {
            for (size_t i = new_size; i < size_; i++)
            {
                data_[i].~T();
            }
        }
        size_ = new_size;
    }

    void clear()
    {
        for (size_t i = 0; i < size_; i++)
        {
            data_[i].~T();
        }
        delete[](char*) data_;
        data_ = 0;
        size_ = 0;
        capacity_ = 0;
    }

    T* data() const
    {
        return data_;
    }
    size_t size() const
    {
        return size_;
    }
    T& operator[](size_t i) const
    {
        return data_[i];
    }
    T* begin() const
    {
        return &data_[0];
    }
    T* end() const
    {
        return &data_[size_];
    }
    bool empty() const
    {
        return size_ == 0;
    }

    void push_back(const T& t)
    {
        try_alloc(size_ + 1);
        new (&data_[size_]) T(t);
        size_++;
    }

    void insert(T* pos, T* b, T* e)
    {
        vector* v = 0;
        if (b >= begin() && b < end())
        {
            //the same vector
            v = new vector(*this);
            b = v->begin() + (b - begin());
            e = v->begin() + (e - begin());
        }
        size_t diff = pos - begin();
        try_alloc(size_ + (e - b));
        pos = begin() + diff;
        memmove(pos + (e - b), pos, (end() - pos) * sizeof(T));
        size_t len = e - b;
        size_ += len;
        for (size_t i = 0; i < len; i++)
        {
            *pos = *b;
            pos++;
            b++;
        }
        delete v;
    }

    T* erase(T* pos)
    {
        pos->~T();
        memmove(pos, pos + 1, (end() - pos - 1) * sizeof(T));
        size_--;
        return pos;
    }

protected:
    T* data_;
    size_t size_;
    size_t capacity_;
    void try_alloc(size_t new_size)
    {
        if (new_size * 3 / 2 > capacity_ / 2)
        {
            capacity_ = new_size * 2;
            T* new_data = (T*)new char[capacity_ * sizeof(T)];
            memset(new_data, 0, capacity_ * sizeof(T));
            if (data_)
            {
                memmove(new_data, data_, sizeof(T) * size_);
                delete[](char*) data_;
            }
            data_ = new_data;
        }
    }
};

struct NCNN_EXPORT string : public vector<char>
{
    string()
    {
    }
    string(const char* str)
    {
        size_t len = strlen(str);
        resize(len);
        memcpy(data_, str, len);
    }
    const char* c_str() const
    {
        return (const char*)data_;
    }
    bool operator==(const string& str2) const
    {
        return strcmp(data_, str2.data_) == 0;
    }
    bool operator==(const char* str2) const
    {
        return strcmp(data_, str2) == 0;
    }
    bool operator!=(const char* str2) const
    {
        return strcmp(data_, str2) != 0;
    }
    string& operator+=(const string& str1)
    {
        insert(end(), str1.begin(), str1.end());
        return *this;
    }
};

inline string operator+(const string& str1, const string& str2)
{
    string str(str1);
    str.insert(str.end(), str2.begin(), str2.end());
    return str;
}

} // namespace std

#endif // NCNN_SIMPLESTL_H
