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

class Program
{
	struct testedge : IEquatable<testedge>
	{
		readonly public int a;
		readonly public int b;
		public testedge(Random rnd)
		{
			a = rnd.Next();
			b = rnd.Next();
		}
		public override int GetHashCode()
		{
			return a ^ b;
		}

		public bool Equals(testedge other)
		{
			return this.a == other.a && this.b == other.b;
		}
	}

	static public bool debug_output;
	static LockFreeDictionary<int, testedge> d_gs;
	static public volatile bool f_stop = false;

	static void Main(string[] args)
	{
		debug_output = Debugger.IsAttached;
		debug_output = false;

		int c_tasks = Environment.ProcessorCount;
		//c_tasks = 1;
		//c_tasks = 2;

		long c_op_total = 0;
		TimeSpan ts_total = new TimeSpan();

		int i = 0;
		while (true)
		{
			counts c = new counts();
			do
			{
				d_gs = null;
				GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
				Thread.Sleep(10);
				d_gs = new LockFreeDictionary<int, testedge>();

				if (debug_output)
					d_gs.m_options |= LockFreeDictionary<int, testedge>.Options.DebugOutput;

#if DEBUG
				if (debug_output)
				{
					Console.WriteLine("=============== start {0} =================", i);
					Console.Out.Flush();
				}
#endif

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

				for (int z = 0; z < tasks.Length; z++)
				{
					try
					{
						c += tasks[z].Result;
					}
					catch (AggregateException aex)
					{
						throw aex.Flatten().InnerException;
					}
				}

#if DEBUG
				d_gs.m_config.CheckBuckets();
				if (debug_output)
					Console.WriteLine("check... ok");
#endif

				//		Console.ReadKey();
				//		f_stop = true;
				//Task.WaitAll(tasks);
				d_gs = null;
				tasks = null;
			}
			//while (++i % 500 != 0);
			while (++i == 0);

			long c_tot = c.add + c.get + c.update + c.remove;
			double mops = (c_tot / 1000.0) / c.ts.TotalMilliseconds;

			c_op_total += c_tot;
			ts_total += c.ts;
			double mops_tot = (c_op_total / 1000.0) / ts_total.TotalMilliseconds;

			Console.WriteLine("{0,6} {1,8:d\\:hh\\:mm\\:ss} add: {2,11:#,#} get: {3,11:#,#} upd: {4,11:#,#} rem: {5,11:#,#} tot: {6,11:#,#} {7:N4} {8:N4}",
						i,
						c.ts,
						c.add, c.get, c.update, c.remove, c_tot,
						mops,
						mops_tot);
		}
	}

	struct counts
	{
		public long add;
		public long get;
		public long update;
		public long remove;
		public TimeSpan ts;
		public static counts operator +(counts a, counts b)
		{
			counts c = new counts();
			c.add = a.add + b.add;
			c.get = a.get + b.get;
			c.update = a.update + b.update;
			c.remove = a.remove + b.remove;
			c.ts = a.ts + b.ts;
			return c;
		}
	}

	static counts Test(int num)
	{
		counts counts = new Program.counts();
		int iter = 0;
		Random rnd = new Random(Guid.NewGuid().ToByteArray()[0]);
		Dictionary<int, testedge> netd = new Dictionary<int, testedge>();
		Stopwatch stopw = new Stopwatch();

		while (!f_stop)
		{
			stopw.Start();
			int c = d_gs.Count;
			stopw.Stop();

			if (c >= 10000)
				break;

			int r = rnd.Next(3);
			if ((netd.Count > 0 && r == 0))// || netd.Count > 200)
			{
				// remove and check

				KeyValuePair<int,testedge> kvp = netd.ElementAt(rnd.Next(netd.Count));

				testedge got_val;
				stopw.Start();
				bool b = d_gs.TryGetValue(kvp.Key, out got_val);
				stopw.Stop();
				counts.get++;

				if (!b)
				{
					Console.ForegroundColor = ConsoleColor.Red;
					int retry = 0;
					do
					{
						Console.WriteLine("remove: didn't get {0} {1}\r\npause and retry #{2}...", kvp.Key, kvp.Value, retry);
						Thread.Sleep(10);
						if (d_gs.TryGetValue(kvp.Key, out got_val))
						{
							Console.WriteLine("ok!");
							Console.Out.Flush();
							Console.ResetColor();
							goto ok;
						}
					}
					while (retry++ < 5);

					Console.Out.Flush();
					Console.ResetColor();

					netd.Remove(kvp.Key);
					continue;
				}
			ok:

				if (!kvp.Value.Equals(got_val))
				{
					Console.ForegroundColor = ConsoleColor.Red;
					Console.WriteLine("remove: wrong value for {0} {1} : got {2}", kvp.Key, kvp.Value, got_val);
					Console.Out.Flush();
					Console.ResetColor();
				}

				stopw.Start();
				b = d_gs.TryRemove(kvp.Key, out got_val);
				stopw.Stop();
				counts.remove++;

				if (!b)
				{
					Console.Beep();
					Console.Beep();
					Console.ForegroundColor = ConsoleColor.Red;
					Console.WriteLine("couldn't remove confirmed value {0} {1}", kvp.Key, got_val);
					Console.Out.Flush();
					Console.ResetColor();
				}
				else
				{
					if (!got_val.Equals(kvp.Value))
					{
						Console.ForegroundColor = ConsoleColor.Red;
						Console.WriteLine("wrong value during removal {0} {1} : got {2}", kvp.Key, kvp.Value, got_val);
						Console.Out.Flush();
						Console.ResetColor();
					}

					if (!netd.Remove(kvp.Key))
					{
						Console.ForegroundColor = ConsoleColor.Red;
						Console.WriteLine("remove error in the .NET reference dict {0} {1} : got {2}", kvp.Key, kvp.Value);
						Console.Out.Flush();
						Console.ResetColor();
					}
				}
			}
			else if (netd.Count > 0 && r == 1)
			{
				var kvp = netd.ElementAt(rnd.Next(netd.Count));

				testedge got_val;
				stopw.Start();
				bool b = d_gs.TryGetValue(kvp.Key, out got_val);
				stopw.Stop();
				counts.get++;

				if (!b)
				{
					Console.ForegroundColor = ConsoleColor.Red;
					int retry = 0;
					do
					{
						Console.WriteLine("update1: didn't get {0} {1}\r\npause and retry #{2}...", kvp.Key, kvp.Value, retry);
						Thread.Sleep(10);
						if (d_gs.TryGetValue(kvp.Key, out got_val))
						{
							Console.WriteLine("ok!");
							Console.Out.Flush();
							Console.ResetColor();
							goto ok1;
						}
					}
					while (retry++ < 5);

					Console.Out.Flush();
					Console.ResetColor();

					netd.Remove(kvp.Key);
					continue;
				}
			ok1:
				if (!kvp.Value.Equals(got_val))
				{
					Console.ForegroundColor = ConsoleColor.Red;
					Console.WriteLine("update1: wrong value for {0} {1} : got {2}", kvp.Key, kvp.Value, got_val);
					Console.Out.Flush();
					Console.ResetColor();
				}


				testedge s_new = new testedge(rnd);

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

				stopw.Start();
				b = d_gs.TryGetValue(kvp.Key, out got_val);
				stopw.Stop();
				counts.get++;

				if (!b)
				{
					Console.ForegroundColor = ConsoleColor.Red;
					int retry = 0;
					do
					{
						Console.WriteLine("update2: didn't get {0} {1}\r\npause and retry #{2}...", kvp.Key, s_new, retry);
						Thread.Sleep(10);
						if (d_gs.TryGetValue(kvp.Key, out got_val))
						{
							Console.WriteLine("ok!");
							Console.Out.Flush();
							Console.ResetColor();
							goto ok2;
						}
					}
					while (retry++ < 5);

					Console.Out.Flush();
					Console.ResetColor();

					netd.Remove(kvp.Key);
					continue;
				}
			ok2:
				if (!s_new.Equals(got_val))
				{
					Console.ForegroundColor = ConsoleColor.Red;
					Console.WriteLine("update2: wrong value for {0} {1} : got {2}", kvp.Key, s_new, got_val);
					Console.Out.Flush();
					Console.ResetColor();
				}
			}
			else if (r == 2)
			{
				// add
				int k = (rnd.Next(int.MaxValue) & unchecked((int)0xFFFFFFF0)) | num;

				testedge v = new testedge(rnd);

				if (netd.ContainsKey(k))
				{
					stopw.Start();
					bool b = d_gs.ContainsKey(k);
					stopw.Stop();
					counts.get++;

					if (!b)
					{
						Console.Beep();
						Console.Beep();
						Console.ForegroundColor = ConsoleColor.Red;
						Console.WriteLine("selected Add key from control dict not found {0}", k);
						Console.Out.Flush();
						Console.ResetColor();
					}
				}
				else
				{
					stopw.Start();
					bool b = d_gs.TryAdd(k, v);
					stopw.Stop();
					counts.add++;

					if (!b)
					{
						Console.Beep();
						Console.Beep();
						Console.ForegroundColor = ConsoleColor.Red;
						Console.WriteLine("add error {0} {1}", k, v);
						Console.Out.Flush();
						Console.ResetColor();
					}
					netd.Add(k, v);
				}
			}
			iter++;
		}
		counts.ts = stopw.Elapsed;
		f_stop = true;
		return counts;
	}
}