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

class Program
{
	static public bool debug_output;
	public class TestException : Exception
	{
	}

	//static LockFreeDictionary<String, String> d_ss = new LockFreeDictionary<String, String>();
	//static LockFreeDictionary<int, String> d_is = new LockFreeDictionary<int, String>();
	static LockFreeDictionary<Guid, String> d_gs = new LockFreeDictionary<Guid, string>();
	static public bool f_stop = false;

	static string GetRandomString(Random r)
	{

		return "";
	}

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


		//int? i = null;
		//int? j = null;
		//bool zzz = i.Equals(null);
		//Nullable.Equals(i, j);

		//var z = new Dictionary<String, String>();
		//z.Add("foo", "bar");
		//z.Add("foo", "bar");

		//d_ss.Add("Hello", "Lock-free");

		//Debug.Assert(d_ss["Hello"] == "Lock-free");

		//Test();
#if false
		//unchecked
		{
			ulong h = 0;// 0xaaaaaaaaaaaaaaaa;
			while (true)
			{
				h ^= 0x55555555aaaaaaaa;
				h ^= 0xaaaaaaaa55555555;
				if (h != 0xffffffffffffffff)
					Debugger.Break();
			}
		}
#endif
#if false
		unchecked
		{
			long m_tail = 0;
			long new_tail;
			long tail = 0x0000170e000000e9;
			int gx = -1;
			while (true)
			{
				/// the tail needs to be advanced
				new_tail = tail + 0x0000000100000000;
				new_tail &= (long)0xFFFFFFFF00000000;
				new_tail |= (uint)gx;

				if ((new_tail & 0x0ff0) == 0x0ff0)
				{
					Debugger.Break();
				}

				//Int64 prev = 
				Interlocked.CompareExchange(ref m_tail, new_tail, tail);
			}
		}
#endif
		//LockFreeDictionary<int, String> foo = new LockFreeDictionary<int, string>();
		//foo.Add(99, "hello");


		//		d_gs.m_config.TestFreelist();
		//	Environment.Exit(0);
#if false
		d_is.Add(99999, "five nines");

		d_is.Add(13, "first");
		d_is.Add(50, "second");
		d_is.Add(87, "third");
		d_is.Add(124, "fourth");
		d_is.Add(161, "fifth");


		String s;
		Debug.Assert(d_is.TryRemove(87, out s));
#endif
#if false
		Int64 h = 0;
		Action a1 = () =>
		{
			while (true)
			{
				Interlocked.Exchange(ref h, 0x2aaaaaaaaaaaaaaa);
				Interlocked.Exchange(ref h, 0x5555555555555555);
			}
		};

		Action a2 = () =>
		{
			while (true)
			{
				Int64 i = Read64(ref h);
				if (i != 0x2aaaaaaaaaaaaaaa && i != 0x5555555555555555)
					throw new Exception("immediately fails with a torn read on 32-bit .NET");
			}
		};

		Task.Factory.StartNew(a1);
		Task.Factory.StartNew(a1);
		Task.Factory.StartNew(a1);
		Task.Factory.StartNew(a2);



		Console.ReadKey();
		//d_ss.m_config.Test();
#endif


		//Console.WriteLine(d_is.m_config.Dump());

		//Guid gg = Guid.NewGuid();
		//d_gs.Add(gg, "abcd");
		//d_gs.TryUpdate(gg, String.Intern("efgh"), "abcd");
		//	Test();

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

		Task<counts>[] tasks;

		int i = 0;
		while (true)
		{
			counts c = new counts();
			do
			{
				//GC.Collect(3, GCCollectionMode.Forced);
				tasks = new Task<counts>[c_tasks];
				d_gs = new LockFreeDictionary<Guid, string>();

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

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

				f_stop = false;
				for (int z = 0; z < tasks.Length; 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)
					{
						if (!(aex.Flatten().InnerException is TestException))
							throw;
					}
#if false
					catch
					{
						Console.ForegroundColor = ConsoleColor.Red;
						Console.WriteLine("failed");
						Console.Out.Flush();
						Console.ResetColor();
						break;
					}
#endif
				}

				var cfg = d_gs.m_config;
			//	Task.Factory.StartNew(() =>
				//	{
						cfg.CheckBuckets();
						if (debug_output)
							Console.WriteLine("check... ok");
					//});

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


			long c_tot = c.add + c.get + c.update + c.remove;
			double mops = c.ts.TotalMilliseconds == 0 ? double.PositiveInfinity : (c_tot / 1000.0) / c.ts.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} Mop/s",
						i,
						c.ts,
						c.add, c.get, c.update, c.remove, c_tot,
						mops);
		}
	}

	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 public ManualResetEvent mre_go = new ManualResetEvent(true);

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

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

			if (c >= 700)
				break;
			//mre_go.WaitOne();

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

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

				String 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 != 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 != 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));

				String 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 != 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();
				}


				String s_new = Guid.NewGuid().ToString();// new String(Enumerable.Range(0, rnd.Next(1, 100)).Select(w => (Char)rnd.Next(32, 123)).ToArray());

				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 != 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
				var rgb = Guid.NewGuid().ToByteArray();
				rgb[0] = (byte)num;
				Guid k = new Guid(rgb);

				String v = k.ToString();// new String(Enumerable.Range(0, rnd.Next(100)).Select(w => (Char)rnd.Next(32, 123)).ToArray());

				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++;
			if (num == 1)
			{
				//if (iter % 1000 == 0)
				//Console.WriteLine("{0} {1,-4} {2,-4}", iter, netd.Count, d_gs.m_config.m_c_free);

#if false
				if (iter == 100)
				{
					d_gs.m_config.Verify();
				}
#endif

#if false
				if (iter == 200000)
				{
					f_stop = true;
					Thread.Sleep(15);
					Console.WriteLine(d_gs.m_config.Dump());
					Console.Out.Flush();
					Debugger.Break();
				}
#endif
				//if (iter % 10000 == 0)
				//Console.WriteLine("{0}", d_gs.m_config.m_c_free);
#if false
				if (iter == 1000)
				{
				//	f_stop = true;
					Thread.Sleep(15);
					Console.WriteLine(d_gs.m_config.Dump());
					Debugger.Break();
				}
#endif
			}

			//	if (iter == 500000)
			//return;
		}
		counts.ts = stopw.Elapsed;
		f_stop = true;
		return counts;
	}
}