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

Module: Value Set

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

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

#ifndef CPROVER_CEGAR_INVARIANT_SET_H
#define CPROVER_CEGAR_INVARIANT_SET_H

/*
#include <ref_expr_set.h>

*/

#include <pointer-analysis/value_set.h>

#include <std_code.h>
#include <numbering.h>
#include <union_find.h>
#include <threeval.h>

class inv_object_storet
{
public:
  inv_object_storet(const namespacet &_ns):ns(_ns)
  {
  }

  bool get(const exprt &expr, unsigned &n);
  
  unsigned add(const exprt &expr);
  
  bool is_constant(unsigned n) const;
  bool is_constant(const exprt &expr) const;

  static bool is_constant_address(const exprt &expr);
  
  const irep_idt &operator[](unsigned n) const
  {
    return map[n];
  }

  const exprt &get_expr(unsigned n) const
  {
    assert(n<entries.size());
    return entries[n].expr;
  }
  
  void output(std::ostream &out) const;
  
  std::string to_string(
    unsigned n,
    const irep_idt &identifier) const;

protected:
  const namespacet &ns;

  typedef hash_numbering<irep_idt, irep_id_hash> mapt;
  mapt map;
  
  struct entryt
  {
    bool is_constant;
    exprt expr;
  };
  
  std::vector<entryt> entries;
  
  std::string build_string(const exprt &expr) const;

  static bool is_constant_address_rec(const exprt &expr);
};

class invariant_sett
{
public:
  // stuff that we track here
  #if 0
  std::vector<bool> active;
  
  bool is_active(unsigned n) const
  {
    if(object_store->is_constant(n)) return true;
    if(n<active.size()) return active[n];
    return false;
  }
  
  void add_object(unsigned n)
  {
    if(n>=active.size())
      active.resize(n+1, false);

    active[n]=true;
  }
  
  void add_objects(const std::list<unsigned> &objects)
  {
    for(std::list<unsigned>::const_iterator it=objects.begin();
        it!=objects.end();
        it++)
      add_object(*it);
  }
  #endif

  // equalities ==
  unsigned_union_find eq_set;
  
  // <=
  typedef std::set<std::pair<unsigned, unsigned> > ineq_sett;
  ineq_sett le_set;
  
  // !=
  ineq_sett ne_set;

  bool threaded;
  bool is_false;
  
  invariant_sett():
    threaded(false),
    is_false(false),
    value_set(NULL),
    object_store(NULL),
    ns(NULL)
  {
  }
  
  void output(
    const irep_idt &identifier,
    std::ostream &out) const;

  // true = removed s.th.
  bool make_intersection(const invariant_sett &other_invariants);
    
  void strengthen(
    const exprt &expr,
    bool negate=false);
  
  void make_true()
  {
    eq_set.clear();
    le_set.clear();
    ne_set.clear();
    is_false=false;
  }

  void make_false()
  {
    eq_set.clear();
    le_set.clear();
    ne_set.clear();
    is_false=true;
  }

  void make_threaded()
  {
    make_true();
    threaded=true;
  }

  void apply_code(
    const codet &code);

  void modifies(
    const exprt &lhs);

  void assignment(
    const exprt &lhs,
    const exprt &rhs);
    
  void set_value_set(const value_sett &_value_set)
  {
    value_set=&_value_set;
  }

  void set_object_store(inv_object_storet &_object_store)
  {
    object_store=&_object_store;
  }
  
  void set_namespace(const namespacet &_ns)
  {
    ns=&_ns;
  }
  
  static void intersection(ineq_sett &dest, const ineq_sett &other)
  {
    ineq_sett::iterator it_d=dest.begin();
  
    while(it_d!=dest.end())
    {
      ineq_sett::iterator next_d(it_d);
      next_d++;

      if(other.find(*it_d)==other.end())
        dest.erase(it_d);
      
      it_d=next_d;
    }  
  }
  
  static void remove(ineq_sett &dest, unsigned a)
  {
    for(ineq_sett::iterator it=dest.begin();
        it!=dest.end();
        ) // no it++
    {
      ineq_sett::iterator next(it);
      next++;

      if(it->first==a || it->second==a)
        dest.erase(it);

      it=next;
    }
  }
  
  tvt implies(
    const exprt &expr,
    bool negate=false) const;
  
  void simplify(
    exprt &expr) const;
  
protected:
  const value_sett *value_set;
  inv_object_storet *object_store;
  const namespacet *ns;
  
  std::string to_string(
    unsigned a,
    const irep_idt &identifier) const;

  bool get_object(
    const exprt &expr,
    unsigned &n) const;
  
  exprt get_constant(const exprt &expr) const;

  // queries
  bool has_eq(const std::pair<unsigned, unsigned> &p) const
  {
    return eq_set.same_set(p.first, p.second);
  }
  
  bool has_le(const std::pair<unsigned, unsigned> &p) const
  {
    return le_set.find(p)!=le_set.end();
  }
  
  bool has_ne(const std::pair<unsigned, unsigned> &p) const
  {
    return ne_set.find(p)!=ne_set.end();
  }
  
  tvt is_eq(std::pair<unsigned, unsigned> p) const;
  tvt is_le(std::pair<unsigned, unsigned> p) const;
  
  tvt is_lt(std::pair<unsigned, unsigned> p) const
  {
    return is_le(p) && !is_eq(p);
  }
  
  tvt is_ge(std::pair<unsigned, unsigned> p) const
  {
    std::swap(p.first, p.second);
    return is_eq(p) || is_lt(p);
  }
  
  tvt is_gt(std::pair<unsigned, unsigned> p) const
  {
    return !is_le(p);
  }

  tvt is_ne(std::pair<unsigned, unsigned> p) const
  {
    return !is_eq(p);
  }

  void add(const std::pair<unsigned, unsigned> &p, ineq_sett &dest);

  void add_le(const std::pair<unsigned, unsigned> &p)
  {
    add(p, le_set);
  }
  
  void add_ne(const std::pair<unsigned, unsigned> &p)
  {
    add(p, ne_set);
  }

  void add_eq(const std::pair<unsigned, unsigned> &eq);

  void add_eq(
    ineq_sett &dest,
    const std::pair<unsigned, unsigned> &eq,
    const std::pair<unsigned, unsigned> &ineq);

  #if 0
  void filter_active();
  void filter_active(ineq_sett &dest);
  #endif
};

#endif
