// $G $F.go && $L $F.$A && ./$A.out

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

// ----------------------------------------------------------------------------
// Helper functions

func ASSERT(p bool) {
	if !p {
		// panic 0;
	}
}


// ----------------------------------------------------------------------------
// Implementation of the HashMap

type KeyType interface {
	Hash() uint32;
	Match(other *KeyType) bool
}


type ValueType interface {
	// empty interface
}


type Entry struct {
	key *KeyType;
	value *ValueType;
}


// Using the Array type below doesn't seem to work
//type Array array [1024] Entry;

type HashMap struct {
	map_ *[1024] Entry;
	log2_capacity_ uint32;
	occupancy_ uint32;
}


func (m *HashMap) capacity() uint32 {
	return 1 << m.log2_capacity_;
}


func (m *HashMap) Clear() {
	// Mark all entries as empty.
	var i uint32 = m.capacity() - 1;
	for i > 0 {
		m.map_[i].key = nil;
		i = i - 1
	}
	m.occupancy_ = 0
}


func (m *HashMap) Initialize (initial_log2_capacity uint32) {
	m.log2_capacity_ = initial_log2_capacity;
	m.map_ = new([1024] Entry);
	m.Clear();
}


func (m *HashMap) Probe (key *KeyType) *Entry {
	ASSERT(key != nil);

	var i uint32 = key.Hash() % m.capacity();
	ASSERT(0 <= i && i < m.capacity());

	ASSERT(m.occupancy_ < m.capacity());	// guarantees loop termination
	for m.map_[i].key != nil && !m.map_[i].key.Match(key) {
		i++;
		if i >= m.capacity() {
			i = 0;
		}
	}

	return &m.map_[i];
}


func (m *HashMap) Lookup (key *KeyType, insert bool) *Entry {
	// Find a matching entry.
	var p *Entry = m.Probe(key);
		if p.key != nil {
		return p;
	}

	// No entry found; insert one if necessary.
	if insert {
		p.key = key;
		p.value = nil;
		m.occupancy_++;

		// Grow the map if we reached >= 80% occupancy.
		if m.occupancy_ + m.occupancy_/4 >= m.capacity() {
			m.Resize();
			p = m.Probe(key);
		}

		return p;
	}

	// No entry found and none inserted.
	return nil;
}


func (m *HashMap) Resize() {
	var hmap *[1024] Entry = m.map_;
	var n uint32 = m.occupancy_;

	// Allocate a new map of twice the current size.
	m.Initialize(m.log2_capacity_ << 1);

	// Rehash all current entries.
	var i uint32 = 0;
	for n > 0 {
		if hmap[i].key != nil {
			m.Lookup(hmap[i].key, true).value = hmap[i].value;
			n = n - 1;
		}
		i++;
	}
}


// ----------------------------------------------------------------------------
// Test code

type Number struct {
	x uint32;
}


func (n *Number) Hash() uint32 {
	return n.x * 23;
}


func (n *Number) Match(other *KeyType) bool {
	// var y *Number = other;
	// return n.x == y.x;
	return false;
}


func MakeNumber (x uint32) *Number {
	var n *Number = new(Number);
	n.x = x;
	return n;
}


func main() {
	//f unc (n int) int { return n + 1; }(1);

	//print "HashMap - gri 2/8/2008\n";

	var hmap *HashMap = new(HashMap);
	hmap.Initialize(0);

	var x1 *Number = MakeNumber(1001);
	var x2 *Number = MakeNumber(2002);
	var x3 *Number = MakeNumber(3003);
	_, _, _ = x1, x2, x3;

	// this doesn't work I think...
	//hmap.Lookup(x1, true);
	//hmap.Lookup(x2, true);
	//hmap.Lookup(x3, true);

	//print "done\n";
}
