#pragma once
#include <util/system/types.h>
#include <util/generic/algorithm.h>
#include <util/generic/ylimits.h>
#include <util/generic/vector.h>

template <class T, bool Grow>
class TLongestSubSequenceBuilder {
private:

    struct TContainer {
        enum class EValueType { vtMin, vtMax, vtValue };

        EValueType ValueType;
        T Value;
        ui32 Index = ::Max<ui32>();
        TContainer(const T& value, const ui32 index)
            : ValueType(EValueType::vtValue)
            , Value(value)
            , Index(index)
        {

        }

        TContainer(const T& value)
            : ValueType(EValueType::vtValue)
            , Value(value)
        {

        }

        TContainer(const EValueType& valueType)
            : ValueType(valueType)
        {
            CHECK_WITH_LOG(valueType != EValueType::vtValue);
        }

        bool CompareLess(const TContainer& item) const {
            if (ValueType == EValueType::vtMin)
                return item.ValueType != EValueType::vtMin;
            if (ValueType == EValueType::vtMax)
                return false;
            if (item.ValueType == EValueType::vtMin)
                return false;
            if (item.ValueType == EValueType::vtMax)
                return ValueType != EValueType::vtMax;
            return Value < item.Value;
        }

        bool operator<(const TContainer& item) const {
            if (Grow) {
                return CompareLess(item);
            } else {
                return item.CompareLess(*this);
            }
        }

    };

public:
    static TVector<T> Execute(const TVector<T>& info) {
        TVector<T> result;
        TVector<TContainer> data(info.size() + 1, Grow ? TContainer::EValueType::vtMax : TContainer::EValueType::vtMin);
        TVector<ui32> parent(info.size() + 1, ::Max<ui32>());
        data[0] = TContainer(Grow ? TContainer::EValueType::vtMin : TContainer::EValueType::vtMax);

        i32 maxJ = 0;
        i32 maxIndex = 0;
        for (ui32 i = 0; i < info.size(); ++i) {
            TContainer contInfo(info[i], i);
            int j = UpperBound(data.begin(), data.end(), contInfo) - data.begin();
            if (data[j - 1] < contInfo && contInfo < data[j]) {
                data[j] = contInfo;
                parent[i] = data[j - 1].Index;
                if (maxJ <= j) {
                    maxJ = j;
                    maxIndex = i;
                }
            }
        }
        if (maxJ > 0) {
            ui32 i = maxIndex;
            while (i != Max<ui32>()) {
                result.push_back(info[i]);
                i = parent[i];
            }
            std::reverse(result.begin(), result.end());
        }
        return result;
    }
};

template <class T>
using TLongestGrowSubSequenceBuilder = TLongestSubSequenceBuilder<T, true>;
