/*******************************************************************\

Module: Counterexample Beautification using Incremental SAT

Author: Daniel Kroening, kroening@cs.cmu.edu

\*******************************************************************/

#include <algorithm>

#include <bitvector.h>
#include <expr_util.h>
#include <arith_tools.h>

#include "counterexample_beautification_greedy.h"

#ifdef SATCHECK_ZCHAFF
#include <zchaff_solver.h>
#endif

/*******************************************************************\

Function: counterexample_beautification_greedyt::beautify_try

  Inputs:

 Outputs:

 Purpose: try specific assignment v to literal l

\*******************************************************************/

#if 0
void counterexample_beautification_greedyt::beautify_try(
  cnf_clause_listt &cnf,
  literalt l,
  bool v)
{
  int clause;
  literalt clause_lit(l);

  if(!v) clause_lit=satcheck.lnot(clause_lit);
  clause=clause_lit.get();

  if(satcheck.l_get(l)==tvt(v))
  {
    // already ok, but fix it
    state.constraints.push_back(clause_lit);
    return;
  }

  #ifdef SATCHECK_ZCHAFF
  CSolver &solver=*satcheck.zchaff_solver();

  // remember satisfying assignment in case we get UNSAT
  // as last result
  if(state.last_was_sat)
  {
    state.assignment.resize(solver.variables().size());
    for(unsigned i=0; i<solver.variables().size(); i++)    
      state.assignment[i]=solver.variables()[i].value();
  }

  // undo state
  if(state.gid!=0)
  {
    solver.delete_clause_group(state.gid);

    // add permanent constraints
    for(std::list<literalt>::const_iterator
        it=state.constraints.begin();
        it!=state.constraints.end();
        it++)
    {
      int l=it->get();
      solver.add_orig_clause(&l, 1);
    }

    state.constraints.clear();
  }

  // set up state
  state.gid=solver.alloc_gid();

  solver.add_orig_clause(&clause, 1, state.gid);
  solver.reset();

  // try it
  if(((SAT_StatusT)solver.solve())==SATISFIABLE)
  {
    // if sat, make it permanent
    state.constraints.push_back(clause_lit);
    state.last_was_sat=true;
    std::cout << "DEB: SAT\n";
  }
  else
  {
    state.last_was_sat=false;
    std::cout << "DEB: UNSAT\n";
  }
  #else
  assert(false);
  #endif
}

/*******************************************************************\

Function: counterexample_beautification_greedyt::beautify

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void counterexample_beautification_greedyt::beautify(
  cnf_clause_listt &cnf,
  bv_cbmct &bv_cbmc,
  const namespacet &ns,
  const exprt &expr,
  const typet &type,
  unsigned offset,
  unsigned bit_nr) // starting from _most_ significant bit
{
  // array or struct?

  if(type.id()=="array")
  {
    // get size
    const exprt &size_expr=(exprt &)type.find("size");
    mp_integer size_int, i;

    if(to_integer(size_expr, size_int)) return;

    // get element width
    unsigned width;

    if(boolbv_get_width(type.subtype(), width))
      return;

    for(i=0; i<size_int; ++i)
    {
      beautify(satcheck, bv_cbmc, ns, expr, type.subtype(), offset, bit_nr);
      offset+=width;
    }    
  }
  else if(type.id()=="struct")
  {
    const irept::subt &components=type.find("components").get_sub();

    forall_irep(it, components)
    {
      const typet &subtype=(typet &)it->find("type");
      unsigned width;

      if(boolbv_get_width(subtype, width))
        return;

      beautify(satcheck, bv_cbmc, ns, expr, subtype, offset, bit_nr);

      offset+=width;
    }
  }
  else if(type.id()=="symbol")
  {
    const symbolt &s=ns.lookup(type.get("identifier"));
    beautify(satcheck, bv_cbmc, ns, expr, s.type, offset, bit_nr);
  }
  else if(type.id()=="pointer")
  {
    // no beautification for pointers right now
  }
  else if(type.id()=="signedbv" ||
          type.id()=="unsignedbv")
  {
    unsigned width;

    if(boolbv_get_width(type, width))
      return;

    if(bit_nr>=width) return;

    unsigned bit=offset+width-bit_nr-1;

    //std::cout << "XX: " << expr.get("identifier") << " sign=" << sign_bit <<std::endl;

    if(type.id()=="signedbv") // signed?
    {
      if(bit_nr==0) // is it the sign bit?
      {
        literalt l;
        if(!bv_cbmc.literal(expr, bit, l))
          beautify_try(satcheck, l, false);
      }
      else
      {
        // try to get the sign bit
        literalt sign_l;
        if(bv_cbmc.literal(expr, offset+width-1, sign_l))
          return;

        bool want=(satcheck.l_get(sign_l).is_true());

        // see if we think it is worth it
        for(unsigned b=offset+width-2; b!=bit; b--)
        {
          literalt other_l;
          if(bv_cbmc.literal(expr, offset+width-1, other_l))
            return;

          if(satcheck.l_get(other_l)==tvt(!want))
            return;
        }

        literalt l;
        if(!bv_cbmc.literal(expr, bit, l))
          beautify_try(satcheck, l, want);
      }

    }
    else // unsigned
    {
      // see if we think it is worth it
      for(unsigned b=offset+width-1; b!=bit; b--)
      {
        literalt other_l;

        if(bv_cbmc.literal(expr, offset+width-1, other_l))
          return;

        if(satcheck.l_get(other_l).is_true())
          return;
      }

      literalt l;
      if(!bv_cbmc.literal(expr, bit, l))
        beautify_try(satcheck, l, false);
    }
  }
}

/*******************************************************************\

Function: counterexample_beautification_greedyt::get_max_width

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

unsigned counterexample_beautification_greedyt::get_max_width(
  const namespacet &ns,
  const typet &type)
{
  // array or struct?

  if(type.id()=="array")
  {
    return get_max_width(ns, type.subtype());
  }
  else if(type.id()=="struct")
  {
    const irept::subt &components=type.find("components").get_sub();
    unsigned max_width=0;

    forall_irep(it, components)
    {
      const typet &subtype=(typet &)it->find("type");
      max_width=std::max(max_width, get_max_width(ns, subtype));
    }

    return max_width;
  }
  else if(type.id()=="symbol")
  {
    const symbolt &s=ns.lookup(type.get("identifier"));
    return get_max_width(ns, s.type);
  }
  else if(type.id()=="pointer")
  {
  }
  else
  {
    unsigned width;

    if(boolbv_get_width(type, width))
      return 0;

    return width;
  }

  return 0;
}

/*******************************************************************\

Function: counterexample_beautification_greedyt::counterexample_beautification_values

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

void counterexample_beautification_greedyt::counterexample_beautification_values(
  cnf_clause_listt &cnf,
  bv_cbmct &bv_cbmc,
  symex_target_equationt &equation,      
  const namespacet &ns)
{
  // get symbols we care about
  minimization_symbolst minimization_symbols;

  get_minimization_symbols(bv_cbmc, equation, failed, minimization_symbols);

  // get max width
  unsigned max_width=0;

  for(minimization_symbolst::const_iterator it=minimization_symbols.begin();
      it!=minimization_symbols.end(); it++)
    max_width=std::max(max_width, get_max_width(ns, (*it).type()));

  for(unsigned i=0; i<max_width; i++)
    for(minimization_symbolst::const_iterator it=minimization_symbols.begin();
        it!=minimization_symbols.end(); it++)
      beautify(satcheck, bv_cbmc, ns, *it, (*it).type(), 0, i);
}

/*******************************************************************\

Function: counterexample_beautification_greedyt::counterexample_beautification_guards

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/

struct guard_structt
{
  guard_structt(literalt _literal, unsigned _count):
    literal(_literal), count(_count) { }

  literalt literal;
  unsigned count;
};

struct guard_comp: public std::binary_function<guard_structt, guard_structt, bool>
{
  bool operator()(const guard_structt &x,
                  const guard_structt &y) const
  { return x.count<y.count; }
};

void counterexample_beautification_greedyt::counterexample_beautification_guards(
  cnf_clause_listt &cnf,
  bv_cbmct &bv_cbmc,
  symex_target_equationt &equation,
  const namespacet &ns)
{
  // lock the failed assertion
  {
    bvt clause;
    literalt l=satcheck.lnot(failed->cond_literal);
    clause.push_back(l);
    satcheck.lcnf(clause);
  }

  // compute weights
  typedef std::map<literalt, unsigned> guard_countt;
  guard_countt guard_count;

  for(symex_target_equationt::statest::const_iterator
      it=equation.states.begin();
      it!=equation.states.end(); it++)
  {
    if(it->is_assignment() &&
       it->assignment_type!=symex_targett::HIDDEN)
    {
      if(it->guard_literal!=satcheck.constant(false) &&
         it->guard_literal!=satcheck.constant(true))
        guard_count[it->guard_literal]++;
    }

    // reached failed assertion?
    if(it==failed)
      break;
  }

  // put it in a vector to sort it
  typedef std::vector<guard_structt> guard_vectort;
  guard_vectort guard_vector;

  guard_vector.reserve(guard_count.size());

  for(guard_countt::const_iterator
      it=guard_count.begin();
      it!=guard_count.end();
      it++)
    guard_vector.push_back(guard_structt(it->first, it->second));

  // sort it
  std::sort(guard_vector.begin(), guard_vector.end(), guard_comp());

  // minimize, starting with the largest one

  for(guard_vectort::const_reverse_iterator
      it=guard_vector.rbegin();
      it!=((const guard_vectort &)guard_vector).rend();
      it++)
    beautify_try(satcheck, it->literal, false);
}

/*******************************************************************\

Function: counterexample_beautification_greedyt::counterexample_beautification

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/
#endif

void counterexample_beautification_greedyt::counterexample_beautification(
  cnf_clause_listt &cnf,
  bv_cbmct &bv_cbmc,
  symex_target_equationt &equation,
  const namespacet &ns)
{
  // find failed claim

  #if 0
  failed=get_failed_claim(bv_cbmc, equation);

  bv_cbmc.status("Beautifying Counterexample (Guards)");
  counterexample_beautification_guards(cnf, bv_cbmc, equation, ns);

  bv_cbmc.status("Beautifying Counterexample (Values)");
  counterexample_beautification_values(cnf, bv_cbmc, equation, ns);

  // restore last satisfying assignment, if we got UNSAT before

  #ifdef SATCHECK_ZCHAFF
  if(!state.last_was_sat)
  {
    CSolver &solver=*satcheck.zchaff_solver();

    for(unsigned i=0; i<solver.variables().size(); i++)
      solver.variables()[i].set_value(state.assignment[i]);
  }
  #else
  assert(false);
  #endif
  #endif
  assert(false);
}
