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

Module: Value Set

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

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

#ifndef CPROVER_UTIL_REF_EXPR_SET_H
#define CPROVER_UTIL_REF_EXPR_SET_H

#include <set>
#include <map>

#include <hash_cont.h>
#include <expr.h>
#include <namespace.h>

extern const hash_set_cont<exprt, irep_hash> empty_expr_set;

class ref_expr_sett
{
public:
  typedef hash_set_cont<exprt, irep_hash> expr_sett;

  ref_expr_sett():d(NULL)
  {
  }
  
  ref_expr_sett(const ref_expr_sett &s):d(NULL)
  {
    copy_from(s);
  }
  
  ref_expr_sett &operator=(const ref_expr_sett &s)
  {
    copy_from(s);
    return *this;
  }
  
  ~ref_expr_sett()
  {
    deallocate();
  }
  
  bool empty() const
  {
    if(d==NULL) return true;
    return d->expr_set.empty();
  }

  const expr_sett &expr_set() const
  {
    if(d==NULL) return empty_expr_set;
    return d->expr_set;
  }
  
  bool make_union(const ref_expr_sett &s2)
  {
    if(s2.d==NULL) return false;
    
    if(s2.d==d) return false;
  
    if(d==NULL)
    {
      copy_from(s2);
      return true;
    }

    return make_union(s2.d->expr_set);
  }

  bool make_union(const expr_sett &s2)
  {
    allocate();

    expr_sett tmp(d->expr_set);
    unsigned old_size=tmp.size();
    tmp.insert(s2.begin(), s2.end());
    
    // anything new?
    if(tmp.size()==old_size) return false;
    move(tmp);
    return true;
  }

  void move(expr_sett &s2)
  {
    deallocate();
    allocate();
    d->expr_set.swap(s2);
  }
  
  void clear()
  {
    deallocate();
  }

protected:
  struct dt
  {
    expr_sett expr_set;
    unsigned count;
    
    dt():count(1)
    {
    }
  };
  
  void copy_from(const ref_expr_sett &s)
  {
    deallocate();
    if(s.d!=NULL)
    {
      s.d->count++;
      d=s.d;
    }
  }
  
  void allocate()
  {
    if(d==NULL) d=new dt;
  }

  void deallocate()
  {
    if(d!=NULL)
    {
      d->count--;
      if(d->count==0) delete d;
      d=NULL;
    }
  }

  dt *d;
};

#endif
