// Exercise 2: a generic bounded cache with constraints (medium) // // Implement BoundedCache: // - constructor takes `capacity` (> 0, else ArgumentOutOfRangeException) // - Put(key, value): inserts or overwrites. If inserting a NEW key would // exceed capacity, first evict the OLDEST inserted key (FIFO order). // Overwriting an existing key does NOT change its insertion position. // - TryGet(key, out value): standard Try-pattern // - Count // - Keys: IReadOnlyList in insertion order (oldest first) // // Constraint: TKey must be `notnull` (Dictionary keys can't be null). // Hint: a Dictionary for lookups + a Queue (or // LinkedList) for insertion order. // // Then implement the generic method: // static T MaxBy(IEnumerable items, Func selector) // where TKey : IComparable // returning the item with the largest selector(item); throw // InvalidOperationException on an empty sequence. using System; using System.Collections.Generic; class BoundedCache where TKey : notnull { // TODO: fields public BoundedCache(int capacity) { // TODO } public int Count => 0; // TODO public IReadOnlyList Keys => new List(); // TODO #pragma warning disable IDE0060 public void Put(TKey key, TValue value) { // TODO } public bool TryGet(TKey key, out TValue value) { // TODO value = default!; return false; } #pragma warning restore IDE0060 } class Program { #pragma warning disable IDE0060 static T MaxBy(IEnumerable items, Func selector) where TKey : IComparable { // TODO throw new NotImplementedException(); } #pragma warning restore IDE0060 static void Main() { int pass = 0, total = 0; var cache = new BoundedCache(2); cache.Put("a", 1); cache.Put("b", 2); cache.Put("a", 10); // overwrite, position unchanged cache.Put("c", 3); // evicts "a" (oldest) total++; if (cache.Count == 2 && !cache.TryGet("a", out _) && cache.TryGet("b", out var b) && b == 2) { Console.WriteLine("PASS: FIFO eviction"); pass++; } else Console.WriteLine("FAIL: FIFO eviction"); total++; if (string.Join(",", cache.Keys) == "b,c") { Console.WriteLine("PASS: Keys in insertion order"); pass++; } else Console.WriteLine("FAIL: Keys, got " + string.Join(",", cache.Keys)); total++; try { new BoundedCache(0); Console.WriteLine("FAIL: capacity 0 accepted"); } catch (ArgumentOutOfRangeException) { Console.WriteLine("PASS: capacity validated"); pass++; } total++; try { var longest = MaxBy(new[] { "kiwi", "banana", "fig" }, s => s.Length); if (longest == "banana") { Console.WriteLine("PASS: MaxBy"); pass++; } else Console.WriteLine("FAIL: MaxBy, got " + longest); } catch (NotImplementedException) { Console.WriteLine("FAIL: MaxBy not implemented"); } total++; try { MaxBy(Array.Empty(), x => x); Console.WriteLine("FAIL: empty sequence accepted"); } catch (InvalidOperationException) { Console.WriteLine("PASS: MaxBy empty throws"); pass++; } catch (NotImplementedException) { Console.WriteLine("FAIL: MaxBy not implemented"); } Console.WriteLine($"--- {pass}/{total} checks passed ---"); } }