/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*               This file is part of the program and library                */
/*    PaPILO --- Parallel Presolve for Integer and Linear Optimization       */
/*                                                                           */
/* Copyright (C) 2020-2026 Zuse Institute Berlin (ZIB)                       */
/*                                                                           */
/* Licensed under the Apache License, Version 2.0 (the "License");           */
/* you may not use this file except in compliance with the License.          */
/* You may obtain a copy of the License at                                   */
/*                                                                           */
/*     http://www.apache.org/licenses/LICENSE-2.0                            */
/*                                                                           */
/* 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.                                            */
/*                                                                           */
/* You should have received a copy of the Apache-2.0 license                 */
/* along with PaPILO; see the file LICENSE. If not visit scipopt.org.        */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef _PAPILO_CORE_SAVED_ROW_HPP_
#define _PAPILO_CORE_SAVED_ROW_HPP_

#include "papilo/core/Solution.hpp"
#include "papilo/core/postsolve/BoundStorage.hpp"
#include "papilo/core/postsolve/ReductionType.hpp"
#include "papilo/misc/Num.hpp"
#include "papilo/misc/StableSum.hpp"
#include "papilo/misc/Vec.hpp"

namespace papilo
{

template <typename REAL>
class SavedRow
{
 private:
   Num<REAL> num;

   int row;

   Vec<int> col_coefficients;
   Vec<REAL> row_values;

   REAL slack;

   bool is_lhs_inf;
   REAL lhs;
   bool is_rhs_inf;
   REAL rhs;
   int length;

 public:
   SavedRow( const Num<REAL>& n, int i, const Vec<ReductionType>& types,
             const Vec<int>& start, const Vec<int>& indices,
             const Vec<REAL>& values, const Vec<REAL>& primal_solution )
   {
      int next_type = i - 1;
      int next_but_one_type = i - 2;

      int saved_row = start[next_type];
      if( types[next_type] == ReductionType::kSaveRow )
         saved_row = start[next_type];
      else if( types[next_but_one_type] == ReductionType::kSaveRow )
         saved_row = start[next_but_one_type];
      else
         assert( false );
      row = indices[saved_row];
      length = (int)values[saved_row];
      is_lhs_inf = indices[saved_row + 1] == 1;
      lhs = values[saved_row + 1];
      is_rhs_inf = indices[saved_row + 2] == 1;
      rhs = values[saved_row + 2];

      col_coefficients.resize( length );
      row_values.resize( length );

      StableSum<REAL> slack_of_row{};
      for( int j = 0; j < length; ++j )
      {
         int col_index = indices[saved_row + 3 + j];
         REAL val = values[saved_row + 3 + j];
         col_coefficients[j] = col_index;
         row_values[j] = val;
         slack_of_row.add( val * primal_solution[col_index] );
      }
      slack = slack_of_row.get();
   }

   bool
   is_on_lhs()
   {
      return !is_lhs_inf && num.isFeasEq( slack, lhs );
   }

   bool
   is_on_rhs()
   {
      return !is_rhs_inf && num.isFeasEq( slack, rhs );
   }

   int
   getRow()
   {
      return row;
   }

   int
   getLength()
   {
      return length;
   }

   REAL
   getCoeffOfCol( int col )
   {
      for( int j = 0; j < length; ++j )
      {
         if( col_coefficients[j] == col )
            return row_values[j];
      }
      assert( false );
      return 0;
   }

   int
   getCoeff( int index )
   {
      return col_coefficients[index];
   }

   REAL
   getValue( int index )
   {
      return row_values[index];
   }

   VarBasisStatus
   getVBS()
   {
      if( is_on_lhs() && is_on_rhs() )
         return VarBasisStatus::FIXED;
      else if( is_on_rhs() )
         return VarBasisStatus::ON_UPPER;
      else if( is_on_lhs() )
         return VarBasisStatus::ON_LOWER;
      else if( is_lhs_inf && is_rhs_inf && num.isZero( slack ) )
         return VarBasisStatus::ZERO;
      return VarBasisStatus::BASIC;
   }

   bool
   is_violated( const Vec<REAL>& primal,
                const BoundStorage<REAL>& stored_bounds )
   {
      for( int i = 0; i < length; i++ )
      {
         int index = col_coefficients[i];
         REAL sol = primal[index];
         bool is_lb_violated = !stored_bounds.col_infinity_lower[index] &&
                               num.isLT( sol, stored_bounds.col_lower[index] );
         bool is_ub_violated = !stored_bounds.col_infinity_upper[index] &&
                               num.isGT( sol, stored_bounds.col_upper[index] );
         if(is_lb_violated || is_ub_violated)
            return true;
      }
      bool rhs_violated = !is_rhs_inf && num.isFeasGT( slack, rhs );
      bool lhs_violated = !is_lhs_inf && num.isFeasLT( slack, lhs );
      return rhs_violated || lhs_violated;
   }
};

} // namespace papilo

#endif
