#pragma once
#include "model.hpp"


/**
 * @brief Helper class constructing identity, particle number and both ladder operators in one loop.
 */
template<typename T>
class LadderOperators {
    public:
        LadderOperators(unsigned dim);
        inline const e::MatrixX<T>& getIdentity() const { return identity; }
        inline const e::MatrixX<T>& getADagger() const { return aDagger; }
        inline const e::MatrixX<T>& getA() const { return a; }
        inline const e::MatrixX<T>& getN() const { return n; }
    private:
        e::MatrixX<T> identity;
        e::MatrixX<T> aDagger;
        e::MatrixX<T> a;
        e::MatrixX<T> n;
};


/**
 * @brief Bose-Hubbard Model in 1 dimension
 * Latex test \f$ 1 +2 \otimes 3\f$
 * @details
 *  The Hamiltonian:
 *  \f[ 
 *      H = 
 *      -t \sum_{i}\left(
 *          \hat{a}^\dagger_i \hat{a}_{i+1} 
 *          + \hat{a}_i \hat{a}^\dagger_{i+1}
 *      \right) 
 *      + \frac{U}{2} \sum_i \hat{n}_i \left(\hat{n}_i-\mathbbm{1}\right) 
 *      - \mu \sum_i \hat{n}_i
 *  \f]
 *
 *  The middle bonds are:
 *  \f[
 *      -t \left(
 *          \hat{a}^\dagger_i \otimes \hat{a}_{i+1}
 *          + \hat{a}_i \otimes \hat{a}^\dagger_{i+1}
 *      \right) 
 *      + \frac{U}{4} \Big(
 *          \hat{n}_{i} \left(\hat{n}_{i}-1_{i}\right) \otimes \mathbbm{1}_{i+1}
 *          + \mathbbm{1}_{i} \otimes \hat{n}_{i+1} \left(\hat{n}_{i+1}-1_{i+1} \right)
 *      \Big)
 *      - \frac{\mu}{2} \left( 
 *          \hat{n}_i \otimes \mathbbm{1}_{i+1} 
 *          \mathbbm{1}_{i} \otimes \hat{n}_{i+1}
 *      \right)
 *  \f]
 *
 *  The MPO is:
 *  \f[ W = \begin{pmatrix} 
 *      \mathbbm{1} & \hat{a}^\dagger & \hat{a} & \frac{U}{2}\hat{n}(\hat{n} - \mathbbm{1}) - \mu\hat{n} \\ 
 *      0 & 0 & 0 & -t \hat{a} \\ 0 & 0 & 0 & -t \hat{a}^\dagger\\ 
 *      0 & 0 & 0 & \mathbbm{1} \end{pmatrix} 
 *  \f]
 */ 
template<typename T>
class BoseHubbardModel1D : public Model1D<T> {
    public:
        BoseHubbardModel1D(size_t L, T U, T t, T mu, size_t nMax) : Model1D<T>(L, nMax+1), U(U), t(t), mu(mu), lo(nMax+1) {
            this->initH_Bonds();
            this->initH_MPO();
        }
        inline const T& getU() const { return U; }
        inline const T& gett() const { return t; }
        inline const T& getmu() const { return mu; }
        inline const LadderOperators<T>& getLadderOperators() const { return lo; }

        virtual std::string format() const override {
            return std::format("BoseHubbardModel1D(L={},U={},t={},mu={})", this->L, this->U, this->t, this->mu);
        } 
    protected:
        void initH_Bonds() override;
        void initH_MPO() override;
    private:
        T U;
        T t;
        T mu;
        LadderOperators<T> lo;
};
