#pragma once

namespace std
{

template <class T> class weak_ptr;
     
template <class T>
class shared_ptr 
{
public:
  shared_ptr() : ptr(0), count(init_counter()) { incref(); }
  ~shared_ptr() { decref(); }
         
  shared_ptr(const shared_ptr<T>& o) : ptr(o.ptr), count(o.count) { incref(); }
  shared_ptr(T* p) : ptr(p), count(new unsigned(1)) {}
  explicit shared_ptr(const weak_ptr<T>& w) : ptr(w.ptr), count(w.count) { incref(); }
         
  shared_ptr<T>& operator=(const shared_ptr<T>& o) 
  {
    if (ptr == o.ptr) 
      return *this;

    decref();
    ptr = o.ptr;
    count = o.count;
    incref();
    return *this;
  }
        
  inline T* get() { return ptr; }
  inline T* operator->() { return ptr; }
  inline T& operator*()  { return *ptr; }
 
  inline const T* get() const { return ptr; }
  inline const T* operator->() const { return ptr; }
  inline const T& operator*() const { return *ptr; }
 
  inline bool operator==(const shared_ptr<T>& o) const { return ptr == o.ptr; }
  inline bool operator!=(const shared_ptr<T>& o) const { return ptr != o.ptr; }
  inline bool operator<(const shared_ptr<T>& o) const { return ptr < o.ptr; }    
 
  unsigned int refcount() const { return *count; }

private:
  T* ptr;
  unsigned int* count; //
     
  static unsigned int* init_counter() 
  { 
    static unsigned int static_counter = 1; 
    return &static_counter;
  }
         
  inline void decref() 
  { 
    --(*count);
    if ((*count) == 0) 
    { 
      delete ptr; 
      delete count; 
    }
  }

  inline void incref() { ++(*count); }
         
  friend class weak_ptr<T>;
};

 
template <class T>
class weak_ptr 
{
public:
         
  weak_ptr() : ptr(0), count(shared_ptr<T>::init_counter()) {}
  explicit weak_ptr( const shared_ptr<T>& s) : ptr(s.ptr), count(s.count) {}
         
  shared_ptr<T> lock() const { return shared_ptr<T>(*this); } 
         
  inline T* get() { return ptr; }
  inline T* operator->() { return ptr; }
  inline T& operator*()  { return *ptr; } 
  
  inline const T* get() const { return ptr; }
  inline const T* operator->() const { return ptr; }
  inline const T& operator*() const { return *ptr; }
 
  inline bool operator==(const shared_ptr<T>& o) const { return ptr == o.ptr; }
  inline bool operator!=(const shared_ptr<T>& o) const { return ptr != o.ptr; }
  inline bool operator<(const shared_ptr<T>& o) const { return ptr < o.ptr; }    
         
  inline unsigned int refcount() const { return *count; }
private:
  T* ptr;
  unsigned int* count;
         
  friend class shared_ptr<T>;       
};


template <class T>
class shared_array 
{
public:
  shared_array() : count(init_counter()) 
  {
    ptr = new T[0];
    incref();
  }
  ~shared_array() { decref(); }
         
  shared_array(const shared_array<T>& o) : ptr(o.ptr), count(o.count) { incref(); }
  shared_array(T* p) : ptr(p), count(new unsigned(1)) {}
  //explicit shared_ptr(const weak_ptr<T>& w) : ptr(w.ptr), count(w.count) { incref(); }
         
  shared_array<T>& operator=(const shared_ptr<T>& o) 
  {
    if (ptr == o.ptr) 
      return *this;

    decref();
    ptr = o.ptr;
    count = o.count;
    incref();
    return *this;
  }
 
  
  inline T& operator[](unsigned int i) {return ptr[i];}
  inline T* get() { return ptr; }
  inline T* operator->() { return ptr; }
  inline T& operator*()  { return *ptr; }
 
  inline const T& operator[](unsigned int i) const {return ptr[i];}
  inline const T* get() const { return ptr; }
  inline const T* operator->() const { return ptr; }
  inline const T& operator*() const { return *ptr; }
 
  inline bool operator==(const shared_array<T>& o) const { return ptr == o.ptr; }
  inline bool operator!=(const shared_array<T>& o) const { return ptr != o.ptr; }
  inline bool operator<(const shared_array<T>& o) const { return ptr < o.ptr; }    
 
  unsigned int refcount() const { return *count; }

private:
  T* ptr;
  unsigned int* count; //
     
  static unsigned int* init_counter() 
  { 
    static unsigned int static_counter = 1; 
    return &static_counter;
  }
         
  inline void decref() 
  { 
    --(*count);
    if ((*count) == 0) 
    { 
      delete [] ptr; 
      delete count; 
    }
  }

  inline void incref() { ++(*count); }
         
  friend class weak_ptr<T>;
};


}