using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Runtime.InteropServices;


abstract class Tester<K, V> : IDisposable
	where K : IEquatable<K>
	where V : IEquatable<V>
{
	protected IDictionary<K, V> test_dict;
	protected volatile bool f_stop = false;
	protected Func<int, Random, K> key_getter;
	protected Func<int, Random, V> value_getter;
	protected int ms_timeout = Timeout.Infinite;
	protected int c_iter = 1;
	int c_tasks;
	String dict_name;

	public Tester(
		int c_tasks,
		Func<int, Random, K> key_getter,
		Func<int, Random, V> value_getter,
		Type t_gen_dict)
	{
		this.c_tasks = c_tasks;
		this.key_getter = key_getter;
		this.value_getter = value_getter;
		this.dict_name = t_gen_dict.Name.Replace("`2", "");

		Type tg = t_gen_dict.MakeGenericType(new Type[] { typeof(K), typeof(V) });
		test_dict = (IDictionary<K, V>)Activator.CreateInstance(tg, new Object[] { });
	}

	public void StartTest()
	{
		Thread.Sleep(1000);
		Console.Write("{0,30}  {1,25}  {2,9} {3,9}  {4,2}",
			this.GetType().Name.Replace("`2", ""),
			dict_name,
			typeof(K).Name,
			typeof(V).Name,
			c_tasks);

		Counts c = new Counts();

		for (int iter = 0; iter < c_iter; iter++)
		{
			Task<Counts>[] tasks = new Task<Counts>[c_tasks];

			f_stop = false;
			for (int z = 0; z < tasks.Length; z++)
			{
				int _z = z;
				tasks[z] = Task.Factory.StartNew<Counts>(() => RunTest(_z));
			}

			if (ms_timeout != Timeout.Infinite)
			{
				Thread.Sleep(ms_timeout);
				f_stop = true;
			}

			for (int z = 0; z < tasks.Length; z++)
			{
				c += tasks[z].Result;
			}

			if (c_iter > 1)
			{
				Console.Write(".");
				test_dict.Clear();
			}
		}
		double c_tot = c.add + c.get + c.update + c.remove;
		double mops = (c_tot / 1000.0) / c.ts.TotalMilliseconds;

#if true
		Console.WriteLine(" {0,8:N4}", mops);
#else
		Console.WriteLine("{0,8:mm\\:ss\\.ffffff} add: {1,11:#,#} get: {2,11:#,#} upd: {3,11:#,#} rem: {4,11:#,#} tot: {5,11:#,#} {6:N4} Mop/s",
					c.ts,
					c.add, c.get, c.update, c.remove, c_tot,
					mops);
		Console.WriteLine();
#endif
	}

	public abstract Counts RunTest(int num);

	public void Dispose()
	{
		key_getter = null;
		value_getter = null;
		test_dict = null;
		GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
	}
};

class _45SecondRandomUsageTest<K, V> : Tester<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{
	protected int max_items = int.MaxValue;

	public _45SecondRandomUsageTest(int c_tasks, Func<int, Random, K> key_getter, Func<int, Random, V> value_getter, Type t_gen_dict)
		: base(c_tasks, key_getter, value_getter, t_gen_dict)
	{
		ms_timeout = 45000;
	}

	public override Counts RunTest(int num)
	{
		Counts counts = new Counts();
		Random rnd = new Random(Guid.NewGuid().ToByteArray()[0]);
		Dictionary<K, V> control_dict = new Dictionary<K, V>();
		Stopwatch stopw = new Stopwatch();

		while (!f_stop)
		{
			// count
			stopw.Start();
			int c = test_dict.Count;
			stopw.Stop();

			int r = rnd.Next(3);

			if ((control_dict.Count > 0 && r == 0))
			{
				// get - remove

				KeyValuePair<K, V> kvp = control_dict.ElementAt(rnd.Next(control_dict.Count));

				V got_val;
				stopw.Start();
				if (!test_dict.TryGetValue(kvp.Key, out got_val))
					throw new Exception();
				stopw.Stop();
				counts.get++;

				stopw.Start();
				test_dict.Remove(kvp.Key);
				stopw.Stop();
				counts.remove++;

				control_dict.Remove(kvp.Key);
			}
			else if (control_dict.Count > 0 && r == 1)
			{
				// get - update - get

				var kvp = control_dict.ElementAt(rnd.Next(control_dict.Count));

				V got_val;
				stopw.Start();
				if (!test_dict.TryGetValue(kvp.Key, out got_val))
					throw new Exception();
				stopw.Stop();
				counts.get++;

				V s_new = value_getter(num, rnd);

				stopw.Start();
				test_dict[kvp.Key] = s_new;
				stopw.Stop();
				counts.update++;
				control_dict[kvp.Key] = s_new;

				stopw.Start();
				if (!test_dict.TryGetValue(kvp.Key, out got_val))
					throw new Exception();
				stopw.Stop();
				counts.get++;
			}
			else if (r == 2 && c < max_items)
			{
				// get - add
				K k = key_getter(num, rnd);
				V v = value_getter(num, rnd);

				if (control_dict.ContainsKey(k))
				{
					stopw.Start();
					if (!test_dict.ContainsKey(k))
						throw new Exception();
					stopw.Stop();
					counts.get++;
				}
				else
				{
					stopw.Start();
					test_dict.Add(k, v);
					stopw.Stop();
					counts.add++;
					control_dict.Add(k, v);
				}
			}
		}
		counts.ts = stopw.Elapsed;
		f_stop = true;
		return counts;
	}
};


class _45Sec10000MaxUsageTest<K, V> : _45SecondRandomUsageTest<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{

	public _45Sec10000MaxUsageTest(int c_tasks, Func<int, Random, K> key_getter, Func<int, Random, V> value_getter, Type t_gen_dict)
		: base(c_tasks, key_getter, value_getter, t_gen_dict)
	{
		max_items = 10000;
	}
};



class Add10E6Test<K, V> : Tester<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{

	public Add10E6Test(int c_tasks, Func<int, Random, K> key_getter, Func<int, Random, V> value_getter, Type t_gen_dict)
		: base(c_tasks, key_getter, value_getter, t_gen_dict)
	{
	}

	public override Counts RunTest(int num)
	{
		Counts counts = new Counts();
		Random rnd = new Random(Guid.NewGuid().ToByteArray()[0]);
		Stopwatch stopw = new Stopwatch();

		while (test_dict.Count < 10E6)
		{
			K k = key_getter(num, rnd);
			V v = value_getter(num, rnd);

			if (!test_dict.ContainsKey(k))
			{
				stopw.Start();
				test_dict.Add(k, v);
				stopw.Stop();
				counts.add++;
			}
		}
		counts.ts = stopw.Elapsed;
		f_stop = true;
		return counts;
	}
};

class _60SecReadOnly1E5Test<K, V> : Tester<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{

	Random rnd = new Random(Guid.NewGuid().ToByteArray()[0]);
	List<K> keys = new List<K>();
	public _60SecReadOnly1E5Test(int c_tasks, Func<int, Random, K> key_getter, Func<int, Random, V> value_getter, Type t_gen_dict)
		: base(c_tasks, key_getter, value_getter, t_gen_dict)
	{
		ms_timeout = 45000;

		while (test_dict.Count < 1E5)//1E6)
		{
			K k = key_getter(0, rnd);

			if (!test_dict.ContainsKey(k))
			{
				test_dict.Add(k, value_getter(0, rnd));
				keys.Add(k);
			}
		}
	}

	public override Counts RunTest(int num)
	{
		Counts counts = new Counts();
		Stopwatch stopw = new Stopwatch();

		while (!f_stop)
		{
			K k = keys[rnd.Next(keys.Count)];
			stopw.Start();
			V v = test_dict[k];
			stopw.Stop();
			counts.get++;
		}
		counts.ts = stopw.Elapsed;
		f_stop = true;
		return counts;
	}
};


class _RandomUsageTestTo1000<K, V> : Tester<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{
	protected int max_items = int.MaxValue;

	public _RandomUsageTestTo1000(int c_tasks, Func<int, Random, K> key_getter, Func<int, Random, V> value_getter, Type t_gen_dict)
		: base(c_tasks, key_getter, value_getter, t_gen_dict)
	{
		c_iter = 3;
	}

	public override Counts RunTest(int num)
	{
		Counts counts = new Counts();
		Random rnd = new Random(Guid.NewGuid().ToByteArray()[0]);
		Dictionary<K, V> control_dict = new Dictionary<K, V>();
		Stopwatch stopw = new Stopwatch();

		while (!f_stop)
		{
			// count
			stopw.Start();
			int c = test_dict.Count;
			stopw.Stop();

			if (c > 1000)
				break;

			int r = rnd.Next(3);

			if ((control_dict.Count > 0 && r == 0))
			{
				// get - remove

				KeyValuePair<K, V> kvp = control_dict.ElementAt(rnd.Next(control_dict.Count));

				V got_val;
				stopw.Start();
				if (!test_dict.TryGetValue(kvp.Key, out got_val))
					throw new Exception();
				stopw.Stop();
				counts.get++;

				stopw.Start();
				test_dict.Remove(kvp.Key);
				stopw.Stop();
				counts.remove++;

				control_dict.Remove(kvp.Key);
			}
			else if (control_dict.Count > 0 && r == 1)
			{
				// get - update - get

				var kvp = control_dict.ElementAt(rnd.Next(control_dict.Count));

				V got_val;
				stopw.Start();
				if (!test_dict.TryGetValue(kvp.Key, out got_val))
					throw new Exception();
				stopw.Stop();
				counts.get++;

				V s_new = value_getter(num, rnd);

				stopw.Start();
				test_dict[kvp.Key] = s_new;
				stopw.Stop();
				counts.update++;
				control_dict[kvp.Key] = s_new;

				stopw.Start();
				if (!test_dict.TryGetValue(kvp.Key, out got_val))
					throw new Exception();
				stopw.Stop();
				counts.get++;
			}
			else if (r == 2 && c < max_items)
			{
				// get - add
				K k = key_getter(num, rnd);
				V v = value_getter(num, rnd);

				if (control_dict.ContainsKey(k))
				{
					stopw.Start();
					if (!test_dict.ContainsKey(k))
						throw new Exception();
					stopw.Stop();
					counts.get++;
				}
				else
				{
					stopw.Start();
					test_dict.Add(k, v);
					stopw.Stop();
					counts.add++;
					control_dict.Add(k, v);
				}
			}
		}
		counts.ts = stopw.Elapsed;
		f_stop = true;
		return counts;
	}
};