#ifndef __RHEAP_H__
#define __RHEAP_H__

#include <inttypes.h>
#include <iostream>
#include <stdlib.h>

#if !(defined(DEBUG) ^ defined(NDEBUG))
	#error "Neither or both DEBUG and NDEBUG are defined."
#endif

#ifdef DEBUG
	#define ASSERT(cond, msg) \
		do { \
			if(!(cond)) { \
				std::cerr << "ASSERT FAILED (" << __FILE__ \
					<< ":" << __LINE__ << "): " << msg << std::endl; \
				exit(EXIT_FAILURE); \
			} \
		} while(false);
#else
	#define ASSERT(cond, msg) ;
#endif

#if defined(DEBUG) && defined(VERBOSE)
    #include "mex.h" // DEBUG
    #include <sstream>
	#define MSG(msg) \
        do { std::stringstream ss; ss << msg << std::endl; mexPrintf("%s", ss.str().c_str()); } while(false);
		// do { std::cout << msg << std::endl; } while(false);
#else
	#define MSG(msg) ;
#endif

namespace rheap {

	struct node_t {
		int64_t name;
		int64_t d;
		int32_t bucket;
		node_t *pprev;
		node_t *pnext;

		node_t();
        inline void assign(int64_t name, int64_t d);
	};

	struct bucket_t {
		int64_t u;
		node_t *pnodes;

		bucket_t();
		~bucket_t();
        inline bool is_empty() const { return pnodes->pprev == pnodes && pnodes->pnext == pnodes; }
		inline void drop_nodes();
		void insert(node_t *pnode);
		static void remove(node_t *pnode);
		node_t* min();

#ifdef DEBUG
		inline const node_t *get_nodes() const { return pnodes; }
#endif
	};

	class rheap_t {
		public:
			rheap_t(uint64_t nnodes, uint64_t max_cost);
			~rheap_t();

			void insert(int64_t name, int64_t d);
			void decrease_key(int64_t name, int64_t d_new);
			int64_t delete_min();
			inline bool is_empty() const { return nitems <= 0; }
			inline bool contains(int64_t name) const {
                ASSERT(pnodes, "Contains can only be called for a non-empty heap.");
                return pnodes[name].name >= 0;
            }

		private:
			node_t *pnodes;
			bucket_t *pbuckets;
			int32_t nbuckets;
			int64_t nitems;

			void insert_node(int64_t irmost_bucket, node_t *pnode);

#ifdef DEBUG
		public:
			void dump() const;
			int32_t get_nbuckets() const { return nbuckets; }
			int64_t get_nitems() const { return nitems; } 
			const bucket_t *get_buckets() const { return pbuckets; }
#endif
	};

}

#endif // __RHEAP_H__
