#include "ipm/ipx/info.h"
#include <cstring>              // for memset
#include <map>
#include <string>
#include "ipm/ipx/control.h"
namespace ipx {

template <typename T>
static void dump(std::ostream& os, const char* name, T value) {
    os << Textline(std::string("info.")+name) << value << '\n';
}

std::ostream& operator<<(std::ostream& os, const Info& info) {
    dump(os, "status", info.status);
    dump(os, "status_ipm", info.status_ipm);
    dump(os, "status_crossover", info.status_crossover);
    dump(os, "errflag", info.errflag);

    dump(os, "num_var", info.num_var);
    dump(os, "num_constr", info.num_constr);
    dump(os, "num_entries", info.num_entries);

    dump(os, "num_rows_solver", info.num_rows_solver);
    dump(os, "num_cols_solver", info.num_cols_solver);
    dump(os, "num_entries_solver", info.num_entries_solver);

    dump(os, "dualized", info.dualized);
    dump(os, "dense_cols", info.dense_cols);

    dump(os, "dependent_rows", info.dependent_rows);
    dump(os, "dependent_cols", info.dependent_cols);
    dump(os, "rows_inconsistent", info.rows_inconsistent);
    dump(os, "cols_inconsistent", info.cols_inconsistent);
    dump(os, "primal_dropped", info.primal_dropped);
    dump(os, "dual_dropped", info.dual_dropped);

    dump(os, "abs_presidual", sci2(info.abs_presidual));
    dump(os, "abs_dresidual", sci2(info.abs_dresidual));
    dump(os, "rel_presidual", sci2(info.rel_presidual));
    dump(os, "rel_dresidual", sci2(info.rel_dresidual));
    dump(os, "pobjval", sci8(info.pobjval));
    dump(os, "dobjval", sci8(info.dobjval));
    dump(os, "rel_objgap", sci2(info.rel_objgap));
    dump(os, "complementarity", sci2(info.complementarity));
    dump(os, "normx", sci2(info.normx));
    dump(os, "normy", sci2(info.normy));
    dump(os, "normz", sci2(info.normz));

    dump(os, "objval", sci8(info.objval));
    dump(os, "primal_infeas", sci2(info.primal_infeas));
    dump(os, "dual_infeas", sci2(info.dual_infeas));

    dump(os, "iter", info.iter);
    dump(os, "kktiter1", info.kktiter1);
    dump(os, "kktiter2", info.kktiter2);
    dump(os, "basis_repairs", info.basis_repairs);
    dump(os, "updates_start", info.updates_start);
    dump(os, "updates_ipm", info.updates_ipm);
    dump(os, "updates_crossover", info.updates_crossover);

    dump(os, "time_total", fix2(info.time_total));
    dump(os, "time_ipm1", fix2(info.time_ipm1));
    dump(os, "time_ipm2", fix2(info.time_ipm2));
    dump(os, "time_starting_basis", fix2(info.time_starting_basis));
    dump(os, "time_crossover", fix2(info.time_crossover));

    dump(os, "time_kkt_factorize", fix2(info.time_kkt_factorize));
    dump(os, "time_kkt_solve", fix2(info.time_kkt_solve));
    dump(os, "time_maxvol", fix2(info.time_maxvol));
    dump(os, "time_cr1", fix2(info.time_cr1));
    dump(os, "time_cr1_AAt", fix2(info.time_cr1_AAt));
    dump(os, "time_cr1_pre", fix2(info.time_cr1_pre));
    dump(os, "time_cr2", fix2(info.time_cr2));
    dump(os, "time_cr2_NNt", fix2(info.time_cr2_NNt));
    dump(os, "time_cr2_B", fix2(info.time_cr2_B));
    dump(os, "time_cr2_Bt", fix2(info.time_cr2_Bt));

    dump(os, "ftran_sparse", fix2(info.ftran_sparse));
    dump(os, "btran_sparse", fix2(info.btran_sparse));
    dump(os, "time_ftran", fix2(info.time_ftran));
    dump(os, "time_btran", fix2(info.time_btran));
    dump(os, "time_lu_invert", fix2(info.time_lu_invert));
    dump(os, "time_lu_update", fix2(info.time_lu_update));
    dump(os, "mean_fill", fix2(info.mean_fill));
    dump(os, "max_fill", fix2(info.max_fill));
    dump(os, "time_symb_invert", fix2(info.time_symb_invert));

    dump(os, "maxvol_updates", info.maxvol_updates);
    dump(os, "maxvol_skipped", info.maxvol_skipped);
    dump(os, "maxvol_passes", info.maxvol_passes);
    dump(os, "tbl_nnz", info.tbl_nnz);
    dump(os, "tbl_max", sci2(info.tbl_max));
    dump(os, "frobnorm_squared", sci2(info.frobnorm_squared));
    dump(os, "lambdamax", sci2(info.lambdamax));
    dump(os, "volume_increase", sci2(info.volume_increase));

    return os;
}

std::string StatusString(Int status) {
    const std::map<int,std::string> status_name = {
        {IPX_STATUS_not_run, "not run"},
        {IPX_STATUS_solved, "solved"},
        {IPX_STATUS_stopped, "stopped"},
        {IPX_STATUS_no_model, "no model"},
        {IPX_STATUS_out_of_memory, "out of memory"},
        {IPX_STATUS_internal_error, "internal error"},
        {IPX_STATUS_optimal, "optimal"},
        {IPX_STATUS_imprecise, "imprecise"},
        {IPX_STATUS_primal_infeas, "primal infeas"},
        {IPX_STATUS_dual_infeas, "dual infeas"},
        {IPX_STATUS_time_limit, "time limit"},
        {IPX_STATUS_iter_limit, "iter limit"},
        {IPX_STATUS_no_progress, "no progress"},
        {IPX_STATUS_failed, "failed"},
        {IPX_STATUS_debug, "debug"}
    };
    auto entry = status_name.find(status);
    if (entry != status_name.end())
        return entry->second;
    return "unknown";
}

}  // namespace ipx
