public void Test_Empty_ColaSet()
		{
			var cola = new ColaOrderedSet<string>(42, StringComparer.Ordinal);
			Assert.That(cola.Count, Is.EqualTo(0));
			Assert.That(cola.Comparer, Is.SameAs(StringComparer.Ordinal));
			Assert.That(cola.Capacity, Is.EqualTo(63), "Capacity should be the next power of 2, minus 1");
		}
		public void Test_ColaOrderedSet_Add()
		{
			var cola = new ColaOrderedSet<int?>();
			Assert.That(cola.Count, Is.EqualTo(0));

			cola.Add(42);
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(1));
			Assert.That(cola.Contains(42), Is.True);

			cola.Add(1);
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(2));
			Assert.That(cola.Contains(1), Is.True);

			cola.Add(66);
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(3));
			Assert.That(cola.Contains(66), Is.True);

			cola.Add(123);
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(4));
			Assert.That(cola.Contains(123), Is.True);

			cola.Add(-77);
			cola.Add(-76);
			cola.Add(-75);
			cola.Add(-74);
			cola.Add(-73);
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(9));
		}
        public void Test_ColaOrderedSet_Add()
        {
            var cola = new ColaOrderedSet <int?>();

            Assert.That(cola.Count, Is.EqualTo(0));

            cola.Add(42);
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(1));
            Assert.That(cola.Contains(42), Is.True);

            cola.Add(1);
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(2));
            Assert.That(cola.Contains(1), Is.True);

            cola.Add(66);
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(3));
            Assert.That(cola.Contains(66), Is.True);

            cola.Add(123);
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(4));
            Assert.That(cola.Contains(123), Is.True);

            cola.Add(-77);
            cola.Add(-76);
            cola.Add(-75);
            cola.Add(-74);
            cola.Add(-73);
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(9));
        }
        public void Test_Empty_ColaSet()
        {
            var cola = new ColaOrderedSet <string>(42, StringComparer.Ordinal);

            Assert.That(cola.Count, Is.EqualTo(0));
            Assert.That(cola.Comparer, Is.SameAs(StringComparer.Ordinal));
            Assert.That(cola.Capacity, Is.EqualTo(63), "Capacity should be the next power of 2, minus 1");
        }
        public void Test_Check_Costs()
        {
            const int N    = 100;
            var       cmp  = new CountingComparer <Slice>(Comparer <Slice> .Default);
            var       cola = new ColaOrderedSet <Slice>(cmp);

            Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Parameters: N = {0}, Log(N) = {1}, Log2(N) = {2}, N.Log2(N) = {3}", N, Math.Log(N), Math.Log(N, 2), N * Math.Log(N, 2)));

            Console.WriteLine("Inserting (" + N + " items)");
            for (int i = 0; i < N; i++)
            {
                cola.Add(FdbTuple.EncodeKey(i << 1));
            }

            Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / N) + " / insert)");
            cola.Debug_Dump();

            Console.WriteLine("Full scan (" + (N << 1) + " lookups)");
            cmp.Reset();
            int n = 0;

            for (int i = 0; i < (N << 1); i++)
            {
                if (cola.Contains(FdbTuple.EncodeKey(i)))
                {
                    ++n;
                }
            }
            Assert.That(n, Is.EqualTo(N));
            Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / (N << 1)) + " / lookup)");

            cmp.Reset();
            n = 0;
            int tail   = Math.Min(16, N >> 1);
            int offset = N - tail;

            Console.WriteLine("Tail scan (" + tail + " lookups)");
            for (int i = 0; i < tail; i++)
            {
                if (cola.Contains(FdbTuple.EncodeKey(offset + i)))
                {
                    ++n;
                }
            }
            Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / tail) + " / lookup)");

            Console.WriteLine("ForEach");
            cmp.Reset();
            int p = 0;

            foreach (var x in cola)
            {
                Assert.That(FdbTuple.DecodeKey <int>(x), Is.EqualTo(p << 1));
                ++p;
            }
            Assert.That(p, Is.EqualTo(N));
            Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / N) + " / item)");
        }
		public void Test_Capacity_Is_Rounded_Up()
		{
			// default capacity is 4 levels, for 31 items max
			var cola = new ColaOrderedSet<string>();
			Assert.That(cola.Capacity, Is.EqualTo(31));

			// 63 items completely fill 5 levels
			cola = new ColaOrderedSet<string>(63);
			Assert.That(cola.Capacity, Is.EqualTo(63));

			// 64 items need 6 levels, which can hold up to 127 items
			cola = new ColaOrderedSet<string>(64);
			Assert.That(cola.Capacity, Is.EqualTo(127));
		}
        public void Test_CopyTo_Return_Ordered_Values()
        {
            const int N   = 1000;
            var       rnd = new Random();

            var cola = new ColaOrderedSet <int>();

            // create a list of random values
            var numbers = new int[N];

            for (int i = 0, x = 0; i < N; i++)
            {
                numbers[i] = (x += 1 + rnd.Next(10));
            }

            // insert the list in a random order
            var list = new List <int>(numbers);

            while (list.Count > 0)
            {
                int p = rnd.Next(list.Count);
                cola.Add(list[p]);
                list.RemoveAt(p);
            }
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(N));

            // we can now sort the numbers to get a reference sequence
            Array.Sort(numbers);

            // foreach()ing should return the value in natural order
            list.Clear();
            foreach (var x in cola)
            {
                list.Add(x);
            }
            Assert.That(list.Count, Is.EqualTo(N));
            Assert.That(list, Is.EqualTo(numbers));

            // CopyTo() should produce the item in the expected order
            var tmp = new int[N];

            cola.CopyTo(tmp);
            Assert.That(tmp, Is.EqualTo(numbers));

            // ToArray() should do the same thing
            tmp = cola.ToArray();
            Assert.That(tmp, Is.EqualTo(numbers));
        }
        public void Test_Capacity_Is_Rounded_Up()
        {
            // default capacity is 4 levels, for 31 items max
            var cola = new ColaOrderedSet <string>();

            Assert.That(cola.Capacity, Is.EqualTo(31));

            // 63 items completely fill 5 levels
            cola = new ColaOrderedSet <string>(63);
            Assert.That(cola.Capacity, Is.EqualTo(63));

            // 64 items need 6 levels, which can hold up to 127 items
            cola = new ColaOrderedSet <string>(64);
            Assert.That(cola.Capacity, Is.EqualTo(127));
        }
        public void Test_ColaOrderedSet_Remove()
        {
            const int N = 1000;

            var cola = new ColaOrderedSet <int>();
            var list = new List <int>();

            for (int i = 0; i < N; i++)
            {
                cola.Add(i);
                list.Add(i);
            }
            cola.Debug_Dump();
            Assert.That(cola.Count, Is.EqualTo(N));

            for (int i = 0; i < N; i++)
            {
                Assert.That(cola.Contains(list[i]), Is.True, "{0} is missing (offset {1})", list[i], i);
            }

            var rnd  = new Random();
            int seed = 1073704892;             // rnd.Next();

            Console.WriteLine("Seed: " + seed);
            rnd = new Random(seed);
            int old = -1;

            while (list.Count > 0)
            {
                int p = rnd.Next(list.Count);
                int x = list[p];
                if (!cola.Contains(x))
                {
                    Assert.Fail("{0} disapeared after removing {1} ?", x, old);
                }

                bool res = cola.Remove(x);
                Assert.That(res, Is.True, "Removing {0} did nothing", x);
                //Assert.That(cola.Contains(191), "blah {0}", x);

                list.RemoveAt(p);
                Assert.That(cola.Count, Is.EqualTo(list.Count));
                old = x;
            }
            cola.Debug_Dump();
        }
		public void Test_ColaOrderedSet_Remove()
		{
			const int N = 1000;

			var cola = new ColaOrderedSet<int>();
			var list = new List<int>();

			for (int i = 0; i < N;i++)
			{
				cola.Add(i);
				list.Add(i);
			}
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(N));

			for (int i = 0; i < N; i++)
			{
				Assert.That(cola.Contains(list[i]), Is.True, "{0} is missing (offset {1})", list[i], i);
			}

			var rnd = new Random();
			int seed = 1073704892; // rnd.Next();
			Console.WriteLine("Seed: " + seed);
			rnd = new Random(seed);
			int old = -1;
			while (list.Count > 0)
			{
				int p = rnd.Next(list.Count);
				int x = list[p]; 
				if (!cola.Contains(x))
				{
					Assert.Fail("{0} disapeared after removing {1} ?", x, old);
				}

				bool res = cola.Remove(x);
				Assert.That(res, Is.True, "Removing {0} did nothing", x);
				//Assert.That(cola.Contains(191), "blah {0}", x);

				list.RemoveAt(p);
				Assert.That(cola.Count, Is.EqualTo(list.Count));
				old = x;
			}
			cola.Debug_Dump();

		}
		public void Test_MiniBench()
		{
			const int N = 10 * 1000 * 1000;

			var rnd = new Random();
			long x;


			//WARMUP
			var store = new ColaOrderedSet<long>();
			store.Add(1);
			store.Add(42);
			store.Add(1234);
			store.TryGetValue(42, out x);
			store.TryGetValue(404, out x);

			#region Sequentially inserted....

			Console.WriteLine("Inserting " + N.ToString("N0") + " sequential key/value pairs into a COLA ordered set");
			GC.Collect();
			store = new ColaOrderedSet<long>();
			long total = 0;
			var sw = Stopwatch.StartNew();
			for (int i = 0; i < N; i++)
			{
				store.Add(i);
				Interlocked.Increment(ref total);
				if (i % (N / 10) == 0) Console.Write(".");
			}
			sw.Stop();

			Console.WriteLine("done");
			Console.WriteLine("* Inserted: " + total.ToString("N0") + " keys");
			Console.WriteLine("* Elapsed : " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec");
			Console.WriteLine("* KPS: " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " key/sec");
			Console.WriteLine("* Latency : " + (sw.Elapsed.TotalMilliseconds * 1000000 / total).ToString("N1") + " nanos / insert");

			// sequential reads

			sw.Restart();
			for (int i = 0; i < total; i++)
			{
				var _ = store.TryGetValue(i, out x);
				if (!_ || x != i) Assert.Fail();
			}
			sw.Stop();
			Console.WriteLine("SeqReadOrdered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

			// random reads

			sw.Restart();
			for (int i = 0; i < total; i++)
			{
				var _ = store.TryGetValue(rnd.Next(N), out x);
				if (!_) Assert.Fail();
			}
			sw.Stop();
			Console.WriteLine("RndReadOrdered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

			#endregion

			#region Randomly inserted....

			Console.WriteLine("(preparing random insert list)");

			var tmp = new long[N];
			var values = new long[N];
			for (int i = 0; i < N; i++)
			{
				tmp[i] = rnd.Next(N);
				values[i] = i;
			}
			Array.Sort(tmp, values);

			Console.WriteLine("Inserting " + N.ToString("N0") + " sequential keys into a COLA store");
			GC.Collect();
			store = new ColaOrderedSet<long>();
			total = 0;
			sw.Restart();
			for (int i = 0; i < N; i++)
			{
				store.Add(values[i]);
				Interlocked.Increment(ref total);
				if (i % (N / 10) == 0) Console.Write(".");
			}
			sw.Stop();

			Console.WriteLine("done");
			Console.WriteLine("* Inserted: " + total.ToString("N0") + " keys");
			Console.WriteLine("* Elapsed : " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec");
			Console.WriteLine("* KPS     : " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " key/sec");
			Console.WriteLine("* Latency : " + (sw.Elapsed.TotalMilliseconds * 1000000 / total).ToString("N1") + " nanos / insert");


			// sequential reads

			sw.Restart();
			for (int i = 0; i < total; i++)
			{
				var _ = store.TryGetValue(i, out x);
				if (!_ || x != i) Assert.Fail();
			}
			sw.Stop();
			Console.WriteLine("SeqReadUnordered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

			// random reads

			sw.Restart();
			for (int i = 0; i < total; i++)
			{
				var _ = store.TryGetValue(rnd.Next(N), out x);
				if (!_) Assert.Fail();
			}
			sw.Stop();
			Console.WriteLine("RndReadUnordered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

			#endregion

		}
		public void Test_Check_Costs()
		{
			const int N = 100;
			var cmp = new CountingComparer<Slice>(Comparer<Slice>.Default);
			var cola = new ColaOrderedSet<Slice>(cmp);

			Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "Parameters: N = {0}, Log(N) = {1}, Log2(N) = {2}, N.Log2(N) = {3}", N, Math.Log(N), Math.Log(N, 2), N * Math.Log(N, 2)));

			Console.WriteLine("Inserting (" + N + " items)");
			for (int i = 0; i < N; i++)
			{
				cola.Add(FdbTuple.EncodeKey(i << 1));
			}

			Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / N) + " / insert)");
			cola.Debug_Dump();

			Console.WriteLine("Full scan (" + (N << 1) + " lookups)");
			cmp.Reset();
			int n = 0;
			for (int i = 0; i < (N << 1); i++)
			{
				if (cola.Contains(FdbTuple.EncodeKey(i))) ++n;
			}
			Assert.That(n, Is.EqualTo(N));
			Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / (N << 1)) + " / lookup)");

			cmp.Reset();
			n = 0;
			int tail = Math.Min(16, N >> 1);
			int offset = N - tail;
			Console.WriteLine("Tail scan (" + tail + " lookups)");
			for (int i = 0; i < tail; i++)
			{
				if (cola.Contains(FdbTuple.EncodeKey(offset + i))) ++n;
			}
			Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / tail) + " / lookup)");

			Console.WriteLine("ForEach");
			cmp.Reset();
			int p = 0;
			foreach(var x in cola)
			{
				Assert.That(FdbTuple.DecodeKey<int>(x), Is.EqualTo(p << 1));
				++p;
			}
			Assert.That(p, Is.EqualTo(N));
			Console.WriteLine("> " + cmp.Count + " cmps (" + ((double)cmp.Count / N) + " / item)");
		}
		public void Test_CopyTo_Return_Ordered_Values()
		{
			const int N = 1000;
			var rnd = new Random();

			var cola = new ColaOrderedSet<int>();

			// create a list of random values
			var numbers = new int[N];
			for (int i = 0, x = 0; i < N; i++) numbers[i] = (x += 1 + rnd.Next(10));

			// insert the list in a random order
			var list = new List<int>(numbers);
			while(list.Count > 0)
			{
				int p = rnd.Next(list.Count);
				cola.Add(list[p]);
				list.RemoveAt(p);
			}
			cola.Debug_Dump();
			Assert.That(cola.Count, Is.EqualTo(N));

			// we can now sort the numbers to get a reference sequence
			Array.Sort(numbers);

			// foreach()ing should return the value in natural order
			list.Clear();
			foreach (var x in cola) list.Add(x);
			Assert.That(list.Count, Is.EqualTo(N));
			Assert.That(list, Is.EqualTo(numbers));

			// CopyTo() should produce the item in the expected order
			var tmp = new int[N];
			cola.CopyTo(tmp);
			Assert.That(tmp, Is.EqualTo(numbers));

			// ToArray() should do the same thing
			tmp = cola.ToArray();
			Assert.That(tmp, Is.EqualTo(numbers));

		}
        public void Test_MiniBench()
        {
            const int N = 10 * 1000 * 1000;

            var  rnd = new Random();
            long x;


            //WARMUP
            var store = new ColaOrderedSet <long>();

            store.Add(1);
            store.Add(42);
            store.Add(1234);
            store.TryGetValue(42, out x);
            store.TryGetValue(404, out x);

            #region Sequentially inserted....

            Console.WriteLine("Inserting " + N.ToString("N0") + " sequential key/value pairs into a COLA ordered set");
            GC.Collect();
            store = new ColaOrderedSet <long>();
            long total = 0;
            var  sw    = Stopwatch.StartNew();
            for (int i = 0; i < N; i++)
            {
                store.Add(i);
                Interlocked.Increment(ref total);
                if (i % (N / 10) == 0)
                {
                    Console.Write(".");
                }
            }
            sw.Stop();

            Console.WriteLine("done");
            Console.WriteLine("* Inserted: " + total.ToString("N0") + " keys");
            Console.WriteLine("* Elapsed : " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec");
            Console.WriteLine("* KPS: " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " key/sec");
            Console.WriteLine("* Latency : " + (sw.Elapsed.TotalMilliseconds * 1000000 / total).ToString("N1") + " nanos / insert");

            // sequential reads

            sw.Restart();
            for (int i = 0; i < total; i++)
            {
                var _ = store.TryGetValue(i, out x);
                if (!_ || x != i)
                {
                    Assert.Fail();
                }
            }
            sw.Stop();
            Console.WriteLine("SeqReadOrdered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

            // random reads

            sw.Restart();
            for (int i = 0; i < total; i++)
            {
                var _ = store.TryGetValue(rnd.Next(N), out x);
                if (!_)
                {
                    Assert.Fail();
                }
            }
            sw.Stop();
            Console.WriteLine("RndReadOrdered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

            #endregion

            #region Randomly inserted....

            Console.WriteLine("(preparing random insert list)");

            var tmp    = new long[N];
            var values = new long[N];
            for (int i = 0; i < N; i++)
            {
                tmp[i]    = rnd.Next(N);
                values[i] = i;
            }
            Array.Sort(tmp, values);

            Console.WriteLine("Inserting " + N.ToString("N0") + " sequential keys into a COLA store");
            GC.Collect();
            store = new ColaOrderedSet <long>();
            total = 0;
            sw.Restart();
            for (int i = 0; i < N; i++)
            {
                store.Add(values[i]);
                Interlocked.Increment(ref total);
                if (i % (N / 10) == 0)
                {
                    Console.Write(".");
                }
            }
            sw.Stop();

            Console.WriteLine("done");
            Console.WriteLine("* Inserted: " + total.ToString("N0") + " keys");
            Console.WriteLine("* Elapsed : " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec");
            Console.WriteLine("* KPS     : " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " key/sec");
            Console.WriteLine("* Latency : " + (sw.Elapsed.TotalMilliseconds * 1000000 / total).ToString("N1") + " nanos / insert");


            // sequential reads

            sw.Restart();
            for (int i = 0; i < total; i++)
            {
                var _ = store.TryGetValue(i, out x);
                if (!_ || x != i)
                {
                    Assert.Fail();
                }
            }
            sw.Stop();
            Console.WriteLine("SeqReadUnordered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

            // random reads

            sw.Restart();
            for (int i = 0; i < total; i++)
            {
                var _ = store.TryGetValue(rnd.Next(N), out x);
                if (!_)
                {
                    Assert.Fail();
                }
            }
            sw.Stop();
            Console.WriteLine("RndReadUnordered: " + total.ToString("N0") + " keys in " + sw.Elapsed.TotalSeconds.ToString("N3") + " sec => " + (total / sw.Elapsed.TotalSeconds).ToString("N0") + " kps");

            #endregion
        }