#ifndef __SPQ_H__
#define __SPQ_H__

#include <inttypes.h>
#include <vector>
#ifdef __clang__
    #include <unordered_map>
	using std::unordered_map;
#else
    #include <tr1/unordered_map>
	using std::tr1::unordered_map;
#endif

namespace spq {

struct bucket_t;

struct node_t {
	int64_t _name;
	node_t *_pprev;
	node_t *_pnext;
	bucket_t *_pbucket;

	inline bool owned() const { return _pbucket; }
};

struct bucket_t {
private:
	uint64_t _d;
	int64_t _heapind;
	node_t *_pnodes;
	uint64_t _nnodes;

public:
	bucket_t();
	~bucket_t();
	inline bool is_empty() const { return !_nnodes; }
	inline uint64_t d() const { return _d; }
	inline void d(uint64_t d) { _d = d; }
	inline int64_t heapind() const { return _heapind; }
	inline void heapind(int64_t ind) { _heapind = ind; }
	inline uint64_t nnodes() const { return _nnodes; }
	void add_node(node_t *pnode);
	void remove_node(node_t *pnode);
	node_t *disown_nodes();
#ifdef DEBUG
	inline node_t *pnodes() { return _pnodes; }
#endif
};

class spq_t {
public:
	spq_t(uint64_t nnodes, uint64_t maxcost);
	~spq_t();
	void insert(int64_t name, uint64_t d);
	void decrease_key(int64_t name, uint64_t dnew);
	node_t *delete_mins(uint64_t &dmin);
	inline uint64_t nitems() const { return _nitems; }
	inline bool is_empty() const { return !_nitems; }
	inline bool contains(int64_t name) const { return _pnodes[name].owned(); }
#ifdef DEBUG
	void dump();
#endif
private:
	int64_t _nitems;
	unordered_map<uint64_t, bucket_t*> _d2pbucket;
	std::vector<uint64_t> _heap;
	node_t *_pnodes;

	bucket_t *find_bucket_ensure_exists(uint64_t d);
	void delete_bucket(bucket_t *pbucket);

	void heap_insert(uint64_t key);
	void heap_delete_at(uint64_t i);
	void heap_siftdown(uint64_t i);
	void heap_siftup(uint64_t i);
};

}

#endif // __SPQ_H__
