/*  _________________________________________________________________________
 *
 *  Acro: A Common Repository for Optimizers
 *  Copyright (c) 2008 Sandia Corporation.
 *  This software is distributed under the BSD License.
 *  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
 *  the U.S. Government retains certain rights in this software.
 *  For more information, see the README.txt file in the top Acro directory.
 *  _________________________________________________________________________
 */

/**
 * \file StatusCodes.h
 *
 * Defines enumeration types for model/solver status codes.
 */

//
// TODO: Re-assess whether all of these status codes are meaningful for
//		Acro solvers.

#ifndef __colin_StatusCodes_h
#define __colin_StatusCodes_h

#include <iostream>
#include <acro_config.h>

namespace colin
{

/// ------------------------------------------------------------------------------------

/// Status of final solution.
enum model_status_enum 	{
   model_status_unknown = 0,
   model_optimal = 1,
   model_locally_optimal = 2,
   model_feasible = 3,
   model_infeasible = 4,
   model_locally_infeasible = 5,
   model_intermediate_infeasible = 6,
   model_intermediate_nonoptimal = 7,
   model_integer_solution = 8,
   model_intermediate_integer_solution = 9,
   model_integer_infeasible = 10,
   model_licensing_problems = 11,
   model_error_unknown = 12,
   model_error_no_solution = 13,
   model_no_solution_returned = 14,
   model_solved_unique = 15,
   model_solved = 16,
   model_solved_singular = 17,
   model_unbounded_no_solution = 18,
   model_infeasible_no_solution = 19
};


/// Solver status
enum solver_status_enum {
    solver_ok = 0,           // Normal termination
    solver_warning = 1,      // Termination with unusual condition
    solver_error = 2,        // Terminated internally with error
    solver_aborted = 3,      // Terminated due to external conditions (e.g. interrupts)
    solver_unknown = 4       // Unknown status
};


/// Termination Condition
enum termination_condition_enum	{
    termination_maxIterations = 1000,        // Exceeded maximum number of iterations allowed by user
    termination_minFunctionValue = 1001,     // Found solution smaller than specified function value
    termination_minStepLength = 1002,        // Step length is smaller than specified limit
    termination_globallyOptimal = 1003,      // Found a globally optimal solution
    termination_locallyOptimal = 1004,       // Found a locally optimal solution
    termination_optimal = 1005,              // Found an optimal solution
    termination_maxEvaluations = 1006,       // Exceeded maximum number of problem evaluations
    termination_maxTime = 1007,              // Exceeded the specified time limit
    termination_unbounded = 2000,            // Demonstrated that problem is unbounded
    termination_infeasible = 2001,           // Demonstrated that the problem is infeasible
    termination_invalidProblem = 2002,       // Demonstrated that the problem is infeasible
    termination_other = 2003,                // Other, uncategorized normal termination
    termination_solverFailure = 3000,        // Solver failed to terminate correctly
    termination_internalSolverError = 3001,  // Internal solver error
    termination_error = 3002,                // Other errors
    termination_userInterrupt = 4000,        // Interrupt signal generated by user
    termination_resourceInterrupt = 4001,    // Interrupt signal in resources used by optimizer
    termination_licensingProblems = 4002,    // Problem accessing solver license
    termination_unknown = 5000               // An unitialized value

};


inline void guess_solver_status(solver_status_enum& ss, termination_condition_enum tc)
{
if (ss != solver_unknown) return;
if (tc < 2000) {
   ss = solver_ok;
   return;
}
if (tc < 3000) {
   ss = solver_warning;
   return;
}
if (tc < 4000) {
   ss = solver_error;
   return;
}
if (tc < 5000) {
   ss = solver_aborted;
   return;
}
ss = solver_unknown;
}

}

/// Pack a colin::model_status_enum data.
inline utilib::PackBuffer& operator<< (utilib::PackBuffer& buff,
                                       const colin::model_status_enum& data)
{buff.pack(static_cast<int>(data)); return buff;}

/// Unpack a colin::model_status_enum data.
inline utilib::UnPackBuffer& operator>> (utilib::UnPackBuffer& buff,
      colin::model_status_enum& data)
{
   int tmp;
   buff.unpack(tmp);
   data = static_cast< colin::model_status_enum >(tmp);
   return buff;
}

/// Write colin::model_status_enum data.
inline std::ostream& operator<< (std::ostream& ostr,
                                 const colin::model_status_enum data)
{
   switch (data)
   {
   case colin::model_status_unknown:
      ostr << "Unknown"; break;
   case colin::model_optimal:
      ostr << "Optimal"; break;
   case colin::model_locally_optimal:
      ostr << "LocallyOptimal"; break;
   case colin::model_feasible:
      ostr << "Feasible"; break;
   case colin::model_infeasible:
      ostr << "Infeasible"; break;
   case colin::model_locally_infeasible:
      ostr << "LocallyInfeasible"; break;
   case colin::model_intermediate_infeasible:
      ostr << "Intermediate_Infeasible"; break;
   case colin::model_intermediate_nonoptimal:
      ostr << "Intermediate_Nonoptimal"; break;
   case colin::model_integer_solution:
      ostr << "IntegerSolution"; break;
   case colin::model_intermediate_integer_solution:
      ostr << "Intermediate_IntegerSolution"; break;
   case colin::model_integer_infeasible:
      ostr << "IntegerInfeasible"; break;
   case colin::model_licensing_problems:
      ostr << "LicensingProblems"; break;
   case colin::model_error_unknown:
      ostr << "Error_Unknown"; break;
   case colin::model_error_no_solution:
      ostr << "Error_NoSolution"; break;
   case colin::model_no_solution_returned:
      ostr << "Error_NoSolution"; break;
   case colin::model_solved_unique:
      ostr << "SolvedUnique"; break;
   case colin::model_solved:
      ostr << "Solved"; break;
   case colin::model_solved_singular:
      ostr << "SolvedSingular"; break;
   case colin::model_unbounded_no_solution:
      ostr << "Unbounded_NoSolution"; break;
   case colin::model_infeasible_no_solution:
      ostr << "Infeasible_NoSolution"; break;
   default:
      ostr << "BadModelStatus";
   }

   return ostr;
}

/// Read colin::model_status_enum data.
inline std::istream& operator>> (std::istream& istr,
                                 colin::model_status_enum& data)
{
   std::string tmp;
   istr >> tmp;
   if (tmp == "Unknown")
      data = colin::model_status_unknown;
   else if (tmp == "Optimal")
      data = colin::model_optimal;
   else if (tmp == "LocallyOptimal")
      data = colin::model_locally_optimal;
   else if (tmp == "Feasible")
      data = colin::model_feasible;
   else if (tmp == "Infeasible")
      data = colin::model_infeasible;
   else if (tmp == "LocallyInfeasible")
      data = colin::model_locally_infeasible;
   else if (tmp == "Intermediate_Infeasible")
      data = colin::model_intermediate_infeasible;
   else if (tmp == "Intermediate_Nonoptimal")
      data = colin::model_intermediate_nonoptimal;
   else if (tmp == "IntegerSolution")
      data = colin::model_integer_solution;
   else if (tmp == "Intermediate_IntegerSolution")
      data = colin::model_intermediate_integer_solution;
   else if (tmp == "IntegerInfeasible")
      data = colin::model_integer_infeasible;
   else if (tmp == "LicensingProblems")
      data = colin::model_licensing_problems;
   else if (tmp == "Error_Unknown")
      data = colin::model_error_unknown;
   else if (tmp == "Error_NoSolution")
      data = colin::model_error_no_solution;
   else if (tmp == "SolvedUnique")
      data = colin::model_solved_unique;
   else if (tmp == "Solved")
      data = colin::model_solved;
   else if (tmp == "SolvedSingular")
      data = colin::model_solved_singular;
   else if (tmp == "Unbounded_NoSolution")
      data = colin::model_unbounded_no_solution;
   else if (tmp == "Infeasible_NoSolution")
      data = colin::model_infeasible_no_solution;
   else
      data = colin::model_error_unknown;
   return istr;
}


/// Pack a colin::solver_status_enum data.
inline utilib::PackBuffer& operator<< (utilib::PackBuffer& buff,
                                       const colin::solver_status_enum& data)
{buff.pack(static_cast<int>(data)); return buff;}

/// Unpack a colin::solver_status_enum data.
inline utilib::UnPackBuffer& operator>> (utilib::UnPackBuffer& buff,
      colin::solver_status_enum& data)
{
   int tmp;
   buff.unpack(tmp);
   data = static_cast< colin::solver_status_enum >(tmp);
   return buff;
}

/// Write colin::solver_status_enum data.
inline std::ostream& operator<< (std::ostream& ostr,
                                 const colin::solver_status_enum data)
{
   switch (data)
   {
   case colin::solver_ok:
      ostr << "ok"; break;
   case colin::solver_warning:
      ostr << "warning"; break;
   case colin::solver_error:
      ostr << "error"; break;
   case colin::solver_aborted:
      ostr << "aborted"; break;
   default:
      ostr << "unknown"; break;
   }

   return ostr;
}

/// Read colin::solver_status_enum data.
inline std::istream& operator>> (std::istream& istr,
                                 colin::solver_status_enum& data)
{
   std::string tmp;
   istr >> tmp;
   if (tmp == "ok")
      data = colin::solver_ok;
   else if (tmp == "warning")
      data = colin::solver_warning;
   else if (tmp == "error")
      data = colin::solver_error;
   else if (tmp == "aborted")
      data = colin::solver_aborted;
   else
      data = colin::solver_unknown;

   return istr;
}


/// Pack a colin::termination_condition_enum data.
inline utilib::PackBuffer& operator<< (utilib::PackBuffer& buff,
                                       const colin::termination_condition_enum& data)
{buff.pack(static_cast<int>(data)); return buff;}

/// Unpack a colin::termination_condition_enum data.
inline utilib::UnPackBuffer& operator>> (utilib::UnPackBuffer& buff,
      colin::termination_condition_enum& data)
{
   int tmp;
   buff.unpack(tmp);
   data = static_cast< colin::termination_condition_enum >(tmp);
   return buff;
}

/// Write colin::termination_condition_enum data.
inline std::ostream& operator<< (std::ostream& ostr,
                                 const colin::termination_condition_enum data)
{
   switch (data)
   {
   case colin::termination_maxIterations:
      ostr << "maxIterations"; break;
   case colin::termination_minFunctionValue:
      ostr << "minFunctionValue"; break;
   case colin::termination_minStepLength:
      ostr << "minStepLength"; break;
   case colin::termination_globallyOptimal:
      ostr << "globallyOptimal"; break;
   case colin::termination_locallyOptimal:
      ostr << "locallyOptimal"; break;
   case colin::termination_optimal:
      ostr << "optimal"; break;
   case colin::termination_unbounded:
      ostr << "unbounded"; break;
   case colin::termination_infeasible:
      ostr << "infeasible"; break;
   case colin::termination_other:
      ostr << "other"; break;
   case colin::termination_solverFailure:
      ostr << "solverFailure"; break;
   case colin::termination_internalSolverError:
      ostr << "internalSolverError"; break;
   case colin::termination_error:
      ostr << "error"; break;
   case colin::termination_userInterrupt:
      ostr << "userInterrupt"; break;
   case colin::termination_resourceInterrupt:
      ostr << "resourceInterrupt"; break;
   case colin::termination_licensingProblems:
      ostr << "licensingProblems"; break;
   default:
      ostr << "unknown"; break;
   }

   return ostr;
}

/// Read colin::termination_condition_enum data.
inline std::istream& operator>> (std::istream& istr,
                                 colin::termination_condition_enum& data)
{
    std::string tmp;
    istr >> tmp;

    if (tmp == "maxIterations")
        data = colin::termination_maxIterations;
    else if (tmp == "minFunctionValue")
        data = colin::termination_minFunctionValue;
    else if (tmp == "minStepLength")
        data = colin::termination_minStepLength;
    else if (tmp == "globallyOptimal")
        data = colin::termination_globallyOptimal;
    else if (tmp == "locallyOptimal")
        data = colin::termination_locallyOptimal;
    else if (tmp == "optimal")
        data = colin::termination_optimal;
    else if (tmp == "unbounded")
        data = colin::termination_unbounded;
    else if (tmp == "infeasible")
        data = colin::termination_infeasible;
    else if (tmp == "other")
        data = colin::termination_other;
    else if (tmp == "solverFailure")
        data = colin::termination_solverFailure;
    else if (tmp == "internalSolverError")
        data = colin::termination_internalSolverError;
    else if (tmp == "error")
        data = colin::termination_error;
    else if (tmp == "userInterrupt")
        data = colin::termination_userInterrupt;
    else if (tmp == "resourceInterrupt")
        data = colin::termination_resourceInterrupt;
    else if (tmp == "licensingProblems")
        data = colin::termination_licensingProblems;
    else 
        data = colin::termination_unknown;

   return istr;
}

#endif
