// Copyright (c) 2018-2024 Charlie Vanaret
// Licensed under the MIT license. See LICENSE file in the project directory for details.

#ifndef UNO_L1RELAXEDPROBLEM_H
#define UNO_L1RELAXEDPROBLEM_H

#include <functional>
#include "ElasticVariables.hpp"
#include "optimization/OptimizationProblem.hpp"
#include "tools/Infinity.hpp"

namespace uno {
   class l1RelaxedProblem: public OptimizationProblem {
   public:
      l1RelaxedProblem(const Model& model, double objective_multiplier, double constraint_violation_coefficient,
         bool relax_linear_constraints);
      ~l1RelaxedProblem() override = default;

      [[nodiscard]] double get_objective_multiplier() const override;
      void set_proximal_coefficient(double proximal_coefficient);
      void set_proximal_center(double* proximal_center);

      // sparsity patterns of Jacobian and Hessian
      [[nodiscard]] size_t number_jacobian_nonzeros() const override;
      [[nodiscard]] bool has_curvature(const HessianModel& hessian_model) const override;
      [[nodiscard]] size_t number_hessian_nonzeros(const HessianModel& hessian_model) const override;
      void compute_jacobian_sparsity(uno_int* row_indices, uno_int* column_indices, uno_int solver_indexing,
         MatrixOrder matrix_order) const override;
      void compute_hessian_sparsity(const HessianModel& hessian_model, uno_int* row_indices, uno_int* column_indices,
         uno_int solver_indexing) const override;

      // numerical evaluations of constraints, objective gradient, Jacobian and Hessian
      void evaluate_constraints(const Iterate& iterate, double* constraints, Evaluations& evaluations) const override;
      void evaluate_objective_gradient(const Iterate& iterate, double* objective_gradient, Evaluations& evaluations) const override;
      void evaluate_jacobian(const Vector<double>& primals, double* jacobian_values, Evaluations& evaluations) const override;
      void evaluate_lagrangian_gradient(const Iterate& iterate, Evaluations& evaluations,
         Vector<double>& lagrangian_gradient) const override;
      void evaluate_lagrangian_hessian(Statistics& statistics, HessianModel& hessian_model, const Vector<double>& primal_variables,
         const Multipliers& multipliers, double* hessian_values) const override;
      void compute_hessian_vector_product(HessianModel& hessian_model, const double* x, const double* vector,
         const Multipliers& multipliers, double* result) const override;

      [[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
      [[nodiscard]] double variable_upper_bound(size_t variable_index) const override;
      [[nodiscard]] const Vector<size_t>& get_fixed_variables() const override;
      // [[nodiscard]] virtual const Collection<size_t>& get_primal_regularization_variables() const;

      [[nodiscard]] double constraint_lower_bound(size_t constraint_index) const override;
      [[nodiscard]] double constraint_upper_bound(size_t constraint_index) const override;
      [[nodiscard]] const Collection<size_t>& get_equality_constraints() const override;
      [[nodiscard]] const Collection<size_t>& get_inequality_constraints() const override;
      [[nodiscard]] const Collection<size_t>& get_dual_regularization_constraints() const override;

      [[nodiscard]] SolutionStatus check_first_order_convergence(const Iterate& current_iterate, double primal_tolerance,
         double dual_tolerance) const override;

      void set_elastic_variable_values(Iterate& iterate, const std::function<void(Iterate&, size_t, size_t, double)>& elastic_setting_function) const;

      // progress measures
      void set_auxiliary_measure(Iterate& iterate) const override;
      [[nodiscard]] double compute_predicted_auxiliary_reduction(const Iterate& current_iterate,
         const Vector<double>& primal_direction, double step_length) const override;

   protected:
      ElasticVariables elastic_variables;
      const size_t number_elastic_variables;
      const double objective_multiplier;
      const double constraint_violation_coefficient;
      double proximal_coefficient{INF<double>};
      double* proximal_center{};
      const ForwardRange dual_regularization_constraints{0};

      // delegating constructor
      l1RelaxedProblem(const Model& model, ElasticVariables&& elastic_variables, double objective_multiplier,
         double constraint_violation_coefficient);
   };
} // namespace

#endif // UNO_L1RELAXEDPROBLEM_H