#include <iostream>
#include <memory>
#include <vector>
#include <ctime>

static const uint64_t SEED = 0xDEADBEEFull;
static const size_t ROWS = 10000;
static const size_t COLS = 10000;
static const uint32_t COLORS_NUM = 3;

uint32_t LCGRand()
{
    static uint64_t seed = SEED;
    seed = seed * 6364136223846793005 + 1442695040888963407;
    return seed >> 32;
}

template <typename T>
class IntrusivePtr
{
public:
	IntrusivePtr() : ptr(nullptr) { }
	IntrusivePtr(T *raw) : ptr(raw) {
		if (ptr) ptr->AddRef();
	}
	IntrusivePtr(const IntrusivePtr& other) : ptr(other.ptr) {
		if (ptr) ptr->AddRef();
	}
	~IntrusivePtr() {
		if (ptr) ptr->Release();
	}
	IntrusivePtr & operator = (const IntrusivePtr& other) {
		T *old = ptr;
		ptr = other.ptr;
		if (ptr) ptr->AddRef();
		if (old) old->Release();
		return *this;
	}
	
	T *operator -> () {
		return ptr;
	}
	
    operator T*() {
		return ptr;
	}
	

private:
	T* ptr;
};

struct Block {
	typedef IntrusivePtr<Block> Ptr;
	
	uint32_t color;
	size_t size;
	Ptr redir;
	
	size_t ref_count;
	Block *next_free;
	
	Block() : color(0), size(0), redir(), ref_count(0), next_free(nullptr) { }
	
	static Ptr Allocate() {
		Block *item = pool;
		if (item)
			pool = item->next_free;
		else
			item = new Block();
		*item = Block();
		return item;
	}
	
	void AddRef() {
		++ref_count;
	}
	
	void Release() {
		if (!--ref_count) {
			next_free = pool;
			pool = this;
		}
	}
	
	static Block *pool;
};

Block *Block::pool = nullptr;

int main()
{
	std::vector<Block::Ptr> up_row(COLS);
	size_t max_size = 0;
	
	clock_t start = clock();
	
    for (size_t row = 0; row < ROWS; row++) {
    	Block::Ptr left;
    	
    	for (size_t col = 0; col < COLS; col++) {
    		uint32_t color = LCGRand() % COLORS_NUM;
    		
    		Block::Ptr &up = up_row[col];
    		while (up && up->redir)
    			up = up->redir;

    		Block::Ptr cur;
    		if (left && (left->color == color)) {
    			cur = left;
    			
    			if (up && (up->color == color) && (up != left)) {
    				left->size += up->size;
    				up->redir = left;
    			}
    		} else if (up && (up->color == color)) {
    			cur = up;
    		} else {
    			cur = Block::Allocate();
    			cur->color = color;
    		}
    		
    		max_size = std::max(max_size, ++cur->size);
    		left = up = cur;
    	}
    }
	
    std::cout << "Max block size: " << max_size << std::endl;
    std::cout << "Time: " << (clock() - start) / (CLOCKS_PER_SEC / 1000) << " ms" << std::endl;
    return 0;
}
