public void WeakMemoizationCacheFactory_Lru_CacheError_Error()
        {
            var mcf = WeakMemoizationCacheFactory.CreateLru(4);

            var n     = 0;
            var cache = mcf.Create <string, string>(x => { n++; return((1000 / x.Length).ToString()); }, MemoizationOptions.CacheException);

            Assert.AreEqual(0, cache.Count);
            Assert.AreEqual(0, n);

            Assert.AreEqual("1000", cache.GetOrAdd("*"));
            Assert.AreEqual(1, cache.Count);
            Assert.AreEqual(1, n);

            Assert.AreEqual("500", cache.GetOrAdd("**"));
            Assert.AreEqual(2, cache.Count);
            Assert.AreEqual(2, n);

            Assert.ThrowsException <DivideByZeroException>(() => cache.GetOrAdd(""));
            Assert.AreEqual(3, cache.Count);
            Assert.AreEqual(3, n);

            Assert.ThrowsException <DivideByZeroException>(() => cache.GetOrAdd(""));
            Assert.AreEqual(3, cache.Count);
            Assert.AreEqual(3, n);

            Assert.AreEqual("250", cache.GetOrAdd("****"));
            Assert.AreEqual(4, cache.Count);
            Assert.AreEqual(4, n);
        }
        public void WeakMemoizationCacheFactory_Evict_Lowest_LastAccessTime()
        {
            var c = new VirtualTimeClock();

            var cache = WeakMemoizationCacheFactory.CreateEvictedByLowest(m => m.LastAccessTime, 4, 1.0, StopwatchFactory.FromClock(c));

            var n = 0;
            var f = cache.Create <string, string>(x => { n++; c.Now += int.Parse(x) * 1000; return(x + "!"); }, MemoizationOptions.None);

            Assert.AreEqual("2!", f.GetOrAdd("2")); // [2]
            Assert.AreEqual(2000, c.Now);

            Assert.AreEqual("1!", f.GetOrAdd("1")); // [1, 2]
            Assert.AreEqual(3000, c.Now);

            Assert.AreEqual("3!", f.GetOrAdd("3")); // [3, 1, 2]
            Assert.AreEqual(6000, c.Now);

            Assert.AreEqual("1!", f.GetOrAdd("1")); // [1, 3, 2]
            Assert.AreEqual(6000, c.Now);

            Assert.AreEqual("4!", f.GetOrAdd("4")); // [4, 1, 3, 2]
            Assert.AreEqual(10000, c.Now);

            Assert.AreEqual("5!", f.GetOrAdd("5")); // [5, 4, 1, 3]
            Assert.AreEqual(15000, c.Now);

            Assert.AreEqual("1!", f.GetOrAdd("1")); // [1, 5, 4, 3]
            Assert.AreEqual(15000, c.Now);

            Assert.AreEqual("2!", f.GetOrAdd("2")); // [2, 1, 5, 4]
            Assert.AreEqual(17000, c.Now);
        }
        public void WeakMemoizationCacheFactory_Lru_Trim4()
        {
            var mcf = WeakMemoizationCacheFactory.CreateLru(10);
            var res = mcf.Create <string, string>(s => s + "!", MemoizationOptions.None);

            var trim = res.AsTrimmableByArgumentAndResult();

            Assert.IsNotNull(trim);

            {
                Assert.AreEqual("1!", res.GetOrAdd("1"));
                Assert.AreEqual(1, res.Count);

                Assert.AreEqual("2!", res.GetOrAdd("2"));
                Assert.AreEqual(2, res.Count);

                trim.Trim(kv => kv.Key == "1");
                Assert.AreEqual(1, res.Count);

                res.Clear();
            }

            {
                Assert.AreEqual("2!", res.GetOrAdd("2"));
                Assert.AreEqual(1, res.Count);

                Assert.AreEqual("1!", res.GetOrAdd("1"));
                Assert.AreEqual(2, res.Count);

                trim.Trim(kv => kv.Key == "2");
                Assert.AreEqual(1, res.Count);

                res.Clear();
            }
        }
        public void WeakMemoizationCacheFactory_Lru_NoError_Pattern2()
        {
            var M = 8;
            var N = 10;

            for (var i = 1; i <= M; i++)
            {
                var mcf = WeakMemoizationCacheFactory.CreateLru(i);

                var n     = 0;
                var cache = mcf.Create <string, string>(x => { n++; return(x + "!"); }, MemoizationOptions.None);

                var xs = Enumerable.Range(0, i + 1).Select(x => x.ToString()).ToArray(); // too large for cache, always evicts after one iteration

                for (var j = 1; j <= N; j++)
                {
                    foreach (var x in xs)
                    {
                        Assert.AreEqual(x + "!", cache.GetOrAdd(x));
                    }

                    Assert.AreEqual(i, cache.Count);
                    Assert.AreEqual(n, (i + 1) * j);
                }

                Assert.AreEqual(n, (i + 1) * N);
            }
        }
        public void WeakMemoizationCacheFactory_Evict_Error_Caching()
        {
            var cache = WeakMemoizationCacheFactory.CreateEvictedByLowest(m => m.HitCount, 4, 1.0);

            var n = 0;
            var f = cache.Create <string, string>(x => { n++; return((1000 / int.Parse(x)).ToString()); }, MemoizationOptions.CacheException);

            Assert.AreEqual("100", f.GetOrAdd("10"));
            Assert.AreEqual(1, n);

            Assert.AreEqual("100", f.GetOrAdd("10"));
            Assert.AreEqual(1, n);

            Assert.AreEqual("50", f.GetOrAdd("20"));
            Assert.AreEqual(2, n);

            Assert.AreEqual("20", f.GetOrAdd("50"));
            Assert.AreEqual(3, n);

            Assert.AreEqual("10", f.GetOrAdd("100"));
            Assert.AreEqual(4, n);

            Assert.ThrowsException <DivideByZeroException>(() => f.GetOrAdd("0"));
            Assert.AreEqual(5, n);

            Assert.ThrowsException <DivideByZeroException>(() => f.GetOrAdd("0"));
            Assert.AreEqual(5, n);

            Assert.AreEqual("100", f.GetOrAdd("10"));
            Assert.AreEqual(5, n);
        }
        public void WeakMemoizationCacheFactory_Lru_NoError_Pattern1()
        {
            var M = 8;
            var N = 10;

            for (var i = 1; i <= M; i++)
            {
                var mcf = WeakMemoizationCacheFactory.CreateLru(i);

                var n     = 0;
                var cache = mcf.Create <string, string>(x => { n++; return(x + "!"); }, MemoizationOptions.None);

                var xs = Enumerable.Range(0, i).Select(x => x.ToString()).ToArray();

                for (var j = 0; j < N; j++)
                {
                    foreach (var x in xs)
                    {
                        Assert.AreEqual(x + "!", cache.GetOrAdd(x));
                        Assert.AreEqual(n, cache.Count);
                    }
                }

                Assert.AreEqual(n, i);
            }
        }
        public void WeakMemoizationCacheFactory_Evict_Trim1()
        {
            var mcf = WeakMemoizationCacheFactory.CreateEvictedByLowest(e => e.HitCount, 10, 0.9, stopwatchFactory: null);
            var res = mcf.Create <string, string>(s => s + "!", MemoizationOptions.None);

            Assert.AreEqual("1!", res.GetOrAdd("1"));
            Assert.AreEqual(1, res.Count);

            var trim = res.AsTrimmableByArgumentAndResult();

            Assert.IsNotNull(trim);

            trim.Trim(kv => kv.Key.StartsWith("2"));
            Assert.AreEqual(1, res.Count);

            trim.Trim(kv => kv.Key.StartsWith("1"));
            Assert.AreEqual(0, res.Count);

            Assert.AreEqual("1!", res.GetOrAdd("1"));
            Assert.AreEqual(1, res.Count);

            trim.Trim(kv => kv.Key.StartsWith("2"));
            Assert.AreEqual(1, res.Count);

            trim.Trim(kv => kv.Key.StartsWith("1"));
            Assert.AreEqual(0, res.Count);
        }
        public void WeakMemoizationCacheFactory_Evict_Trim2()
        {
            var mcf = WeakMemoizationCacheFactory.CreateEvictedByLowest(e => e.HitCount, 10, 0.9, stopwatchFactory: null);
            var res = mcf.Create <string, string>(s => (100 / int.Parse(s)).ToString(), MemoizationOptions.CacheException);

            Assert.ThrowsException <DivideByZeroException>(() => res.GetOrAdd("0"));
            Assert.AreEqual(1, res.Count);

            Assert.AreEqual("50", res.GetOrAdd("2"));
            Assert.AreEqual(2, res.Count);

            var trim = res.AsTrimmableByArgumentAndResult();

            Assert.IsNotNull(trim);

            trim.Trim(kv => int.Parse(kv.Key) % 2 == 0);
            Assert.AreEqual(1, res.Count);

            var trimErr = res.AsTrimmableByArgumentAndResultOrError();

            Assert.IsNotNull(trimErr);

            trimErr.Trim(kv => kv.Value.Kind == ValueOrErrorKind.Error && kv.Key == "0");
            Assert.AreEqual(0, res.Count);

            trimErr.Trim(kv => int.Parse(kv.Key) % 3 == 0);
            Assert.AreEqual(0, res.Count);

            Assert.ThrowsException <DivideByZeroException>(() => res.GetOrAdd("0"));
            Assert.AreEqual(1, res.Count);

            Assert.AreEqual("50", res.GetOrAdd("2"));
            Assert.AreEqual(2, res.Count);
        }
        public void WeakMemoizationCacheFactory_Lru_Dispose()
        {
            var mcf = WeakMemoizationCacheFactory.CreateLru(10);

            var res = mcf.Create <string, string>(s => s + "!", MemoizationOptions.None);

            res.Dispose();

            Assert.ThrowsException <ObjectDisposedException>(() => res.GetOrAdd("1"));
        }
        public void WeakMemoizationCacheFactory_Evict_Dispose()
        {
            var mcf = WeakMemoizationCacheFactory.CreateEvictedByLowest(m => m.HitCount, 4, 1.0);

            var res = mcf.Create <string, string>(s => s + "!", MemoizationOptions.None);

            res.Dispose();

            Assert.ThrowsException <ObjectDisposedException>(() => res.GetOrAdd("1"));
        }
        public void WeakMemoizationCacheFactory_Lru_NoError_Simple()
        {
            var mcf = WeakMemoizationCacheFactory.CreateLru(4);

            var n     = 0;
            var cache = mcf.Create <string, string>(x => { n++; return(x + "!"); }, MemoizationOptions.None);

            Assert.AreEqual(0, cache.Count);
            Assert.AreEqual(0, n);

            Assert.AreEqual("1!", cache.GetOrAdd("1")); // [1->1!]
            Assert.AreEqual(1, cache.Count);
            Assert.AreEqual(1, n);

            Assert.AreEqual("1!", cache.GetOrAdd("1")); // [1->1!]
            Assert.AreEqual(1, cache.Count);
            Assert.AreEqual(1, n);

            Assert.AreEqual("2!", cache.GetOrAdd("2")); // [2->2!, 1->1!]
            Assert.AreEqual(2, cache.Count);
            Assert.AreEqual(2, n);

            Assert.AreEqual("1!", cache.GetOrAdd("1")); // [1->1!, 2->2!]
            Assert.AreEqual(2, cache.Count);
            Assert.AreEqual(2, n);

            Assert.AreEqual("3!", cache.GetOrAdd("3")); // [3->3!, 1->1!, 2->2!]
            Assert.AreEqual(3, cache.Count);
            Assert.AreEqual(3, n);

            Assert.AreEqual("4!", cache.GetOrAdd("4")); // [4->4!, 3->3!, 1->1!, 2->2!]
            Assert.AreEqual(4, cache.Count);
            Assert.AreEqual(4, n);

            Assert.AreEqual("2!", cache.GetOrAdd("2")); // [2->2!, 4->4!, 3->3!, 1->1!]
            Assert.AreEqual(4, cache.Count);
            Assert.AreEqual(4, n);

            Assert.AreEqual("5!", cache.GetOrAdd("5")); // [5->5!, 2->2!, 4->4!, 3->3!]
            Assert.AreEqual(4, cache.Count);
            Assert.AreEqual(5, n);

            cache.Clear();

            Assert.AreEqual(0, cache.Count);
            Assert.AreEqual(5, n);

            Assert.AreEqual("1!", cache.GetOrAdd("1")); // [1->1!]
            Assert.AreEqual(1, cache.Count);
            Assert.AreEqual(6, n);
        }
        public void WeakMemoizationCacheFactory_Evict_Highest_HitCount()
        {
            var cache = WeakMemoizationCacheFactory.CreateEvictedByHighest(m => m.HitCount, 4, 1.0);

            var n = 0;
            var f = cache.Create <string, string>(x => { n++; return(x + "!"); }, MemoizationOptions.None);

            Assert.AreEqual("1!", f.GetOrAdd("1"));
            Assert.AreEqual("1!", f.GetOrAdd("1"));
            Assert.AreEqual(1, n);

            // [1]

            Assert.AreEqual("2!", f.GetOrAdd("2"));
            Assert.AreEqual(2, n);

            // [2, 1]

            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual(3, n);

            // [2, 1, 3]

            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual(4, n);

            // [2, 1, 4, 3]

            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual(5, n);

            // [5, 2, 1, 4]

            Assert.AreEqual("2!", f.GetOrAdd("2"));
            Assert.AreEqual(5, n);

            // [5, 2, 1, 4]

            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual("2!", f.GetOrAdd("2"));
            Assert.AreEqual("1!", f.GetOrAdd("1"));
            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual(5, n);
        }
        public void WeakMemoizationCacheFactory_Lru_Trim5()
        {
            var mcf = WeakMemoizationCacheFactory.CreateLru(10);
            var res = mcf.Create <string, string>(s => s + "!", MemoizationOptions.None);

            var trim = res.AsTrimmableByArgumentAndResult();

            Assert.IsNotNull(trim);

            for (var i = 1; i <= 5; i++)
            {
                Assert.AreEqual(i.ToString() + "!", res.GetOrAdd(i.ToString()));
                Assert.AreEqual(i, res.Count);
            }

            trim.Trim(kv => true);
            Assert.AreEqual(0, res.Count);
        }
        public void WeakMemoizationCacheFactory_Lru_Lifetime_Trim()
        {
            // NB: Test is flaky on Mono.
            if (Type.GetType("Mono.Runtime") != null)
            {
                return;
            }

            lock (typeof(Obj)) // ensuring no concurrent tests are run
            {
                Obj.Reset();
                try
                {
                    var mcf = WeakMemoizationCacheFactory.CreateLru(4);

                    var cache = mcf.Create <Obj, string>(x => x.ToString(), MemoizationOptions.None);

                    var trim = cache.AsTrimmableByArgumentAndResult();
                    Assert.IsNotNull(trim);

                    Assert.IsTrue(GetOrAdd(cache).Contains("Obj"));
                    Assert.IsTrue(GetOrAdd(cache).Contains("Obj"));
                    Assert.IsTrue(GetOrAdd(cache).Contains("Obj"));
                    Assert.AreEqual(3, cache.Count);

                    GC.Collect();
                    GC.WaitForPendingFinalizers();

                    Assert.AreEqual(3, Obj.FinalizeCount);

                    Assert.IsTrue(cache.DebugView.Contains("(empty slot)"));
                    Assert.AreEqual(3, cache.Count);

                    trim.Trim(_ => false); // causes pruning

                    Assert.IsFalse(cache.DebugView.Contains("(empty slot)"));
                    Assert.AreEqual(0, cache.Count);
                }
                finally
                {
                    Obj.Reset();
                }
            }
        }
        public void WeakMemoizationCacheFactory_Evict_ArgumentChecking()
        {
            Assert.ThrowsException <ArgumentNullException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(default(Func <IMemoizationCacheEntryMetrics, int>), 1, 1.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(e => 0, -1, 1.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(e => 0, 0, 1.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(e => 0, 1, 0.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(e => 0, 1, -0.1));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(e => 0, 1, 1.1));

            Assert.ThrowsException <ArgumentNullException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(default(Func <IMemoizationCacheEntryMetrics, int>), 1, 1.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(e => 0, -1, 1.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(e => 0, 0, 1.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(e => 0, 1, 0.0));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(e => 0, 1, -0.1));
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(e => 0, 1, 1.1));

            Assert.ThrowsException <ArgumentNullException>(() => WeakMemoizationCacheFactory.CreateEvictedByHighest(e => 0, 1).Create(default(Func <string, string>), MemoizationOptions.None));
            Assert.ThrowsException <ArgumentNullException>(() => WeakMemoizationCacheFactory.CreateEvictedByLowest(e => 0, 1).Create(default(Func <string, string>), MemoizationOptions.None));
        }
        public void WeakMemoizationCacheFactory_Lru_NoError_Random()
        {
            var r = new Random(1983);

            var nums = Enumerable.Range(0, 100).Select(i => i.ToString()).ToArray();

            for (var c = 1; c <= 10000; c *= 10)
            {
                var mcf = WeakMemoizationCacheFactory.CreateLru(c);

                for (var i = 1; i <= 10000; i *= 10)
                {
                    var n     = 0;
                    var cache = mcf.Create <string, string>(x => { n++; return(x + "!"); }, MemoizationOptions.None);

                    var xs        = Enumerable.Range(0, c).Select(_ => nums[r.Next(0, 100)]).ToList();
                    var unique    = xs.Distinct().Count();
                    var bigEnough = unique <= c;
                    var seen      = new HashSet <string>();

                    var e = 0;
                    foreach (var x in xs)
                    {
                        Assert.AreEqual(x + "!", cache.GetOrAdd(x));

                        if (bigEnough)
                        {
                            if (seen.Add(x))
                            {
                                e++;
                            }

                            Assert.AreEqual(e, n);
                        }
                    }

                    Assert.AreEqual(cache.Count, Math.Min(unique, c));
                }
            }
        }
        public void WeakMemoizationCacheFactory_Evict_Trim3()
        {
            var mcf = WeakMemoizationCacheFactory.CreateEvictedByLowest(e => e.HitCount, 10, 0.9, stopwatchFactory: null);
            var res = mcf.Create <string, string>(s => s + "!", MemoizationOptions.CacheException);

            Assert.AreEqual("1!", res.GetOrAdd("1"));
            Assert.AreEqual(1, res.Count);

            Assert.AreEqual("2!", res.GetOrAdd("2"));
            Assert.AreEqual(2, res.Count);

            Assert.AreEqual("1!", res.GetOrAdd("1"));
            Assert.AreEqual(2, res.Count);

            var trim = res.AsTrimmableByMetrics();

            Assert.IsNotNull(trim);

            trim.Trim(e => e.HitCount == 1);
            Assert.AreEqual(1, res.Count);

            Assert.AreEqual("2!", res.GetOrAdd("2"));
            Assert.AreEqual(2, res.Count);
        }
 public void WeakMemoizationCacheFactory_Lru_ArgumentChecking()
 {
     Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateLru(-1));
     Assert.ThrowsException <ArgumentOutOfRangeException>(() => WeakMemoizationCacheFactory.CreateLru(0));
     Assert.ThrowsException <ArgumentNullException>(() => WeakMemoizationCacheFactory.CreateLru(1).Create(default(Func <string, string>), MemoizationOptions.None));
 }
        public void WeakMemoizationCacheFactory_Evict_Lowest_HitCount()
        {
            var cache = WeakMemoizationCacheFactory.CreateEvictedByLowest(m => m.HitCount, 4, 1.0);

            var n = 0;
            var f = cache.Create <string, string>(x => { n++; return(x + "!"); }, MemoizationOptions.None);

            Assert.AreEqual("1!", f.GetOrAdd("1"));
            Assert.AreEqual("1!", f.GetOrAdd("1"));
            Assert.AreEqual(1, n);
            Assert.AreEqual(1, f.Count);

            // [1]

            Assert.AreEqual("2!", f.GetOrAdd("2"));
            Assert.AreEqual(2, n);
            Assert.AreEqual(2, f.Count);

            // [2, 1]

            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual(3, n);
            Assert.AreEqual(3, f.Count);

            // [2, 1, 3]

            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual(4, n);
            Assert.AreEqual(4, f.Count);

            // [2, 1, 4, 3]

            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual(5, n);
            Assert.AreEqual(4, f.Count);

            // [1, 4, 3, 5]

            Assert.AreEqual("2!", f.GetOrAdd("2"));
            Assert.AreEqual(6, n);
            Assert.AreEqual(4, f.Count);

            // [2, 4, 3, 5]

            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual(7, n);
            Assert.AreEqual(4, f.Count);

            // [4, 3, 5, 6]

            Assert.AreEqual("4!", f.GetOrAdd("4"));
            Assert.AreEqual("3!", f.GetOrAdd("3"));
            Assert.AreEqual("5!", f.GetOrAdd("5"));
            Assert.AreEqual("6!", f.GetOrAdd("6"));
            Assert.AreEqual(7, n);
            Assert.AreEqual(4, f.Count);

            // []

            f.Clear();
            Assert.AreEqual(0, f.Count);

            // [1]

            Assert.AreEqual("1!", f.GetOrAdd("1"));
            Assert.AreEqual(8, n);
            Assert.AreEqual(1, f.Count);
        }