public void TestSmallDict()
        {
            var ht = new SmallDictionary<int, int>();
            const int elements = 150; 

            for (int i = 0; i < elements; i += 10)
            {
                ht.Add(i, i);
                ht.AssertBalanced();
                ht.Add(i - 2, i - 2);
                ht.AssertBalanced();
                ht.Add(i - 3, i - 3);
                ht.AssertBalanced();
                ht.Add(i - 4, i - 4);
                ht.AssertBalanced();
                ht.Add(i - 6, i - 6);
                ht.AssertBalanced();
                ht.Add(i - 5, i - 5);
                ht.AssertBalanced();
                ht.Add(i - 1, i - 1);
                ht.AssertBalanced();
                ht.Add(i - 7, i - 7);
                ht.AssertBalanced();
                ht.Add(i - 8, i - 8);
                ht.AssertBalanced();
                ht.Add(i - 9, i - 9);
                ht.AssertBalanced();
            }

            Assert.Equal(150, ht.Count());

            for (int i = 0; i < elements; i += 10)
            {
                Assert.Equal(ht[i], i);
                Assert.Equal(ht[i - 2], i - 2);
                Assert.Equal(ht[i - 3], i - 3);
                Assert.Equal(ht[i - 4], i - 4);
                Assert.Equal(ht[i - 6], i - 6);
                Assert.Equal(ht[i - 5], i - 5);
                Assert.Equal(ht[i - 1], i - 1);
                Assert.Equal(ht[i - 7], i - 7);
                Assert.Equal(ht[i - 8], i - 8);
                Assert.Equal(ht[i - 9], i - 9);
            }

            foreach (var p in ht)
            {
                Assert.Equal(p.Key, p.Value);
            }

            var keys = ht.Keys.ToArray();
            var values = ht.Values.ToArray();

            for (int i = 0, l = ht.Count(); i < l; i++)
            {
                Assert.Equal(keys[i], values[i]);
            }
        }
        public void SmallDictionaryAlwaysBalanced()
        {
            var sd = new SmallDictionary<int, string>();

            sd.Add(1, "1");
            sd.AssertBalanced();

            sd.Add(10, "1");
            sd.AssertBalanced();

            sd.Add(2, "1");
            sd.AssertBalanced();

            sd.Add(1000, "1");
            sd.AssertBalanced();

            sd.Add(-123, "1");
            sd.AssertBalanced();

            sd.Add(0, "1");
            sd.AssertBalanced();

            sd.Add(4, "1");
            sd.AssertBalanced();

            sd.Add(5, "1");
            sd.AssertBalanced();

            sd.Add(6, "1");
            sd.AssertBalanced();
        }
        public void SmallDictionaryAlwaysBalanced()
        {
            var sd = new SmallDictionary <int, string>();

            sd.Add(1, "1");
            sd.AssertBalanced();

            sd.Add(10, "1");
            sd.AssertBalanced();

            sd.Add(2, "1");
            sd.AssertBalanced();

            sd.Add(1000, "1");
            sd.AssertBalanced();

            sd.Add(-123, "1");
            sd.AssertBalanced();

            sd.Add(0, "1");
            sd.AssertBalanced();

            sd.Add(4, "1");
            sd.AssertBalanced();

            sd.Add(5, "1");
            sd.AssertBalanced();

            sd.Add(6, "1");
            sd.AssertBalanced();
        }
Beispiel #4
0
        /// <summary>
        /// Adds a mapping to this unification, creating a new unification.
        /// </summary>
        /// <param name="key">
        /// The key of the mapping to add.
        /// </param>
        /// <param name="value">
        /// The value of the mapping to add.
        /// </param>
        /// <returns>
        /// The result of adding, which will ignore this mapping if there is
        /// already a present mapping for <paramref name="key"/>.
        /// </returns>

        internal ImmutableTypeMap Add(TypeParameterSymbol key, TypeWithModifiers value)
        {
            var sd = new SmallDictionary <TypeParameterSymbol, TypeWithModifiers>();

            sd.Add(key, value);
            return(ComposeDict(sd));
        }
Beispiel #5
0
        public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing)
            : base(enclosing)
        {
            this.lambdaSymbol = lambdaSymbol;
            this.parameterMap = new MultiDictionary <string, ParameterSymbol>();

            var parameters = lambdaSymbol.Parameters;

            if (!parameters.IsDefaultOrEmpty)
            {
                _definitionMap = new SmallDictionary <string, ParameterSymbol>();
                foreach (var parameter in parameters)
                {
                    if (!parameter.IsDiscard)
                    {
                        var name = parameter.Name;
                        this.parameterMap.Add(name, parameter);
                        if (!_definitionMap.ContainsKey(name))
                        {
                            _definitionMap.Add(name, parameter);
                        }
                    }
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Sequentially composes this map with another one, then filters out
        /// the set of additions to the map using the given allowed set.
        /// </summary>
        /// <param name="other">The RHS of the sequential composition.</param>
        /// <param name="allowed">The array of allowed type parameters.</param>
        /// <returns>
        /// The composed map, with the present keys restricted to those either
        /// in this map or in <paramref name="allowed"/>.
        /// </returns>
        public ImmutableTypeMap RestrictedCompose(ImmutableTypeMap other, ImmutableArray <TypeParameterSymbol> allowed)
        {
            // Don't return 'other' if we're empty.
            // 'other' might need restricting.
            if (other.IsEmpty)
            {
                return(this);
            }

            // TODO(MattWindsor91): efficiency: we're creating and disposing
            //     of a lot of intermediate data structures here.
            var unfiltered = Compose(other);

            var rdict = new SmallDictionary <TypeParameterSymbol, TypeWithModifiers>();

            // De-duplicating this is important: SmallDictionary errors
            // on duplicate key insertion.
            var allAllowed = allowed.AddRange(Mapping.Keys).Distinct();

            foreach (var a in allAllowed)
            {
                if (unfiltered.Mapping.ContainsKey(a))
                {
                    rdict.Add(a, unfiltered.Mapping[a]);
                }
            }
            return(new ImmutableTypeMap(rdict));
        }
Beispiel #7
0
        /// <summary>
        /// Synthesizes the correct receiver of a witness invocation.
        /// </summary>
        /// <param name="syntax">The syntax to be attached to the node.</param>
        /// <param name="witness">The witness we are calling into.</param>
        /// <returns>
        /// The appropriate receiver for this witness.
        /// If we're in a block, this will be a local variable;
        /// otherwise, this will be a <c>default()</c>.
        /// </returns>
        private BoundExpression SynthesizeWitnessReceiver(SyntaxNode syntax, TypeSymbol witness)
        {
            Debug.Assert(syntax != null, "Syntax for witness receiver should not be null");
            Debug.Assert(witness != null, "Witness receiver should not be null");
            Debug.Assert(witness.IsInstanceType() || witness.IsConceptWitness, "Witness receiver should be a valid witness");

            // If we're not in a block, we can't synthesise a local
            if (_rootStatement.Kind != BoundKind.Block)
            {
                return(new BoundDefaultExpression(syntax, witness)
                {
                    WasCompilerGenerated = true
                });
            }

            // TODO(@MattWindsor91): this is probably inefficient
            if (_conceptWitnessesToHoist == null)
            {
                _conceptWitnessesToHoist = new SmallDictionary <TypeSymbol, LocalSymbol>();
            }
            if (!_conceptWitnessesToHoist.ContainsKey(witness))
            {
                _conceptWitnessesToHoist.Add(witness, WitnessDictionaryLocal(witness, syntax));
            }

            var local = _conceptWitnessesToHoist[witness];

            return(new BoundLocal(syntax, local, null, witness)
            {
                WasCompilerGenerated = true
            });
        }
        public void TestSmallDict()
        {
            var ht = new SmallDictionary<int, int>();
            const int elements = 150; 

            for (int i = 0; i < elements; i += 4)
            {
                ht.Add(i, i);
                ht.Add(i - 1, i - 1);
                ht.Add(i - 2, i - 2);
                ht.Add(i - 3, i - 3);
            }

            Assert.Equal(152, ht.Count());

            for (int j = 0; j < 100; j++)
            {
                for (int i = 0; i < elements; i += 4)
                {
                    int v;
                    ht.TryGetValue(i, out v);
                    Assert.Equal(i, v);

                    ht.TryGetValue(i - 1, out v);
                    Assert.Equal(i - 1, v);

                    ht.TryGetValue(i - 2, out v);
                    Assert.Equal(i - 2, v);

                    ht.TryGetValue(i - 3, out v);
                    Assert.Equal(i - 3, v);
                }
            }

            foreach(var p in ht)
            {
                Assert.Equal(p.Key, p.Value);
            }

            var keys = ht.Keys.ToArray();
            var values = ht.Values.ToArray();

            for (int i = 0, l = ht.Count(); i < l; i++)
            {
                Assert.Equal(keys[i], values[i]);
            }
        }
        public void TestSmallDict()
        {
            var       ht       = new SmallDictionary <int, int>();
            const int elements = 150;

            for (int i = 0; i < elements; i += 4)
            {
                ht.Add(i, i);
                ht.Add(i - 1, i - 1);
                ht.Add(i - 2, i - 2);
                ht.Add(i - 3, i - 3);
            }

            Assert.Equal(152, ht.Count());

            for (int j = 0; j < 100; j++)
            {
                for (int i = 0; i < elements; i += 4)
                {
                    int v;
                    ht.TryGetValue(i, out v);
                    Assert.Equal(i, v);

                    ht.TryGetValue(i - 1, out v);
                    Assert.Equal(i - 1, v);

                    ht.TryGetValue(i - 2, out v);
                    Assert.Equal(i - 2, v);

                    ht.TryGetValue(i - 3, out v);
                    Assert.Equal(i - 3, v);
                }
            }

            foreach (var p in ht)
            {
                Assert.Equal(p.Key, p.Value);
            }

            var keys   = ht.Keys.ToArray();
            var values = ht.Values.ToArray();

            for (int i = 0, l = ht.Count(); i < l; i++)
            {
                Assert.Equal(keys[i], values[i]);
            }
        }
Beispiel #10
0
 private static void RecordDefinition <T>(SmallDictionary <string, Symbol> declarationMap, ImmutableArray <T> definitions) where T : Symbol
 {
     foreach (Symbol s in definitions)
     {
         if (!declarationMap.ContainsKey(s.Name))
         {
             declarationMap.Add(s.Name, s);
         }
     }
 }
Beispiel #11
0
        public override void InitializeTupleFieldDefinitionsToIndexMap()
        {
            Debug.Assert(this.IsTupleType);
            Debug.Assert(this.IsDefinition); // we only store a map for definitions

            var retargetedMap = new SmallDictionary <FieldSymbol, int>(ReferenceEqualityComparer.Instance);

            foreach ((FieldSymbol field, int index) in _underlyingType.TupleFieldDefinitionsToIndexMap)
            {
                retargetedMap.Add(this.RetargetingTranslator.Retarget(field), index);
            }

            this.TupleData !.SetFieldDefinitionsToIndexMap(retargetedMap);
        }
                private void DeclareLocals <TSymbol>(Scope scope, ImmutableArray <TSymbol> locals, bool declareAsFree = false)
                    where TSymbol : Symbol
                {
                    foreach (var local in locals)
                    {
                        Debug.Assert(!_localToScope.ContainsKey(local));
                        if (declareAsFree)
                        {
#if DEBUG
                            Debug.Assert(_freeVariables.Add(local));
#endif
                        }
                        else
                        {
                            _localToScope.Add(local, scope);
                        }
                    }
                }
Beispiel #13
0
        private static SmallDictionary <TypeParameterSymbol, TypeSymbolWithAnnotations> ConstructMapping(ImmutableArray <TypeParameterSymbol> from, ImmutableArray <TypeSymbolWithAnnotations> to)
        {
            var mapping = new SmallDictionary <TypeParameterSymbol, TypeSymbolWithAnnotations>(ReferenceEqualityComparer.Instance);

            Debug.Assert(from.Length == to.Length);

            for (int i = 0; i < from.Length; i++)
            {
                TypeParameterSymbol       tp = from[i];
                TypeSymbolWithAnnotations ta = to[i];
                if (!ta.Is(tp))
                {
                    mapping.Add(tp, ta);
                }
            }

            return(mapping);
        }
        /// <summary>
        /// Given a meaning, ensure that it is the only meaning within this scope (or report a diagnostic that it isn't).
        /// Returns true if a diagnostic was reported.  Sets done=true if there is no need to check further enclosing
        /// scopes (for example, when this meaning is the same as a previous meaning, or it can be determined that a
        /// diagnostic had previously been reported).
        /// </summary>
        private bool EnsureSingleDefinition(SingleMeaning newMeaning, DiagnosticBag diagnostics, ref bool done)
        {
            if (singleMeaningTable == null)
            {
                Interlocked.CompareExchange(ref singleMeaningTable, new SmallDictionary <string, SingleMeaning>(), null);
            }

            lock (singleMeaningTable)
            {
                SingleMeaning oldMeaning;
                if (singleMeaningTable.TryGetValue(newMeaning.Name, out oldMeaning))
                {
                    if (oldMeaning.Direct)
                    {
                        return(ResolveConflict(oldMeaning, newMeaning, diagnostics, ref done));
                    }
                    else
                    {
                        if (newMeaning.Direct)
                        {
                            singleMeaningTable[newMeaning.Name] = newMeaning;
                            return(ResolveConflict(oldMeaning, newMeaning, diagnostics, ref done));
                        }
                        else
                        {
                            // both indirect
                            if ((object)oldMeaning.Symbol != null &&
                                newMeaning.Symbol == oldMeaning.Symbol &&
                                !newMeaning.IsDefinition)
                            {
                                done = true; // optimization
                            }
                            return(false);
                        }
                    }
                }
                else
                {
                    singleMeaningTable.Add(newMeaning.Name, newMeaning);
                    return(false);
                }
            }
        }
        /// <summary>
        /// Report an error if adding the edge (method1, method2) to the ctor-initializer
        /// graph would add a new cycle to that graph.
        /// </summary>
        /// <param name="method1">a calling ctor</param>
        /// <param name="method2">the chained-to ctor</param>
        /// <param name="syntax">where to report a cyclic error if needed</param>
        /// <param name="diagnostics">a diagnostic bag for receiving the diagnostic</param>
        internal void ReportCtorInitializerCycles(MethodSymbol method1, MethodSymbol method2, CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
        {
            // precondition and postcondition: the graph _constructorInitializers is acyclic.
            // If adding the edge (method1, method2) would induce a cycle, we report an error
            // and do not add it to the set of edges. If it would not induce a cycle we add
            // it to the set of edges and return.

            if (method1 == method2)
            {
                // direct recursion is diagnosed elsewhere
                throw ExceptionUtilities.Unreachable;
            }

            if (_constructorInitializers == null)
            {
                _constructorInitializers = new SmallDictionary <MethodSymbol, MethodSymbol>();
                _constructorInitializers.Add(method1, method2);
                return;
            }

            MethodSymbol next = method2;

            while (true)
            {
                if (_constructorInitializers.TryGetValue(next, out next))
                {
                    Debug.Assert((object)next != null);
                    if (method1 == next)
                    {
                        // We found a (new) cycle containing the edge (method1, method2). Report an
                        // error and do not add the edge.
                        diagnostics.Add(ErrorCode.ERR_IndirectRecursiveConstructorCall, syntax.Location, method1);
                        return;
                    }
                }
                else
                {
                    // we've reached the end of the path without finding a cycle. Add the new edge.
                    _constructorInitializers.Add(method1, method2);
                    return;
                }
            }
        }
Beispiel #16
0
            /// <summary>
            /// Create the optimized plan for the location of lambda methods and whether scopes need access to parent scopes
            ///  </summary>
            internal void ComputeLambdaScopesAndFrameCaptures(ParameterSymbol thisParam)
            {
                RemoveUnneededReferences(thisParam);

                VisitClosures(ScopeTree, (scope, closure) =>
                {
                    if (closure.CapturedVariables.Count > 0)
                    {
                        (Scope innermost, Scope outermost) = FindLambdaScopeRange(closure, scope);
                        RecordClosureScope(innermost, outermost, closure);
                    }
                });

                (Scope innermost, Scope outermost) FindLambdaScopeRange(Closure closure, Scope closureScope)
                {
                    Scope innermost = null;
                    Scope outermost = null;

                    var capturedVars = PooledHashSet <Symbol> .GetInstance();

                    capturedVars.AddAll(closure.CapturedVariables);

                    // If any of the captured variables are local functions we'll need
                    // to add the captured variables of that local function to the current
                    // set. This has the effect of ensuring that if the local function
                    // captures anything "above" the current scope then parent frame
                    // is itself captured (so that the current lambda can call that
                    // local function).
                    foreach (var captured in closure.CapturedVariables)
                    {
                        if (captured is LocalFunctionSymbol localFunc)
                        {
                            var(found, _) = GetVisibleClosure(closureScope, localFunc);
                            capturedVars.AddAll(found.CapturedVariables);
                        }
                    }

                    for (var curScope = closureScope;
                         curScope != null && capturedVars.Count > 0;
                         curScope = curScope.Parent)
                    {
                        if (!(capturedVars.Overlaps(curScope.DeclaredVariables) ||
                              capturedVars.Overlaps(curScope.Closures.Select(c => c.OriginalMethodSymbol))))
                        {
                            continue;
                        }

                        outermost = curScope;
                        if (innermost == null)
                        {
                            innermost = curScope;
                        }

                        capturedVars.RemoveAll(curScope.DeclaredVariables);
                        capturedVars.RemoveAll(curScope.Closures.Select(c => c.OriginalMethodSymbol));
                    }

                    // If any captured variables are left, they're captured above method scope
                    if (capturedVars.Count > 0)
                    {
                        outermost = null;
                    }

                    capturedVars.Free();

                    return(innermost, outermost);
                }

                void RecordClosureScope(Scope innermost, Scope outermost, Closure closure)
                {
                    // 1) if there is innermost scope, lambda goes there as we cannot go any higher.
                    // 2) scopes in [innermostScope, outermostScope) chain need to have access to the parent scope.
                    //
                    // Example:
                    //   if a lambda captures a method's parameter and `this`,
                    //   its innermost scope depth is 0 (method locals and parameters)
                    //   and outermost scope is -1
                    //   Such lambda will be placed in a closure frame that corresponds to the method's outer block
                    //   and this frame will also lift original `this` as a field when created by its parent.
                    //   Note that it is completely irrelevant how deeply the lexical scope of the lambda was originally nested.
                    if (innermost != null)
                    {
                        LambdaScopes.Add(closure.OriginalMethodSymbol, innermost.BoundNode);

                        // Disable struct closures on methods converted to delegates, as well as on async and iterator methods.
                        var markAsNoStruct = !CanTakeRefParameters(closure.OriginalMethodSymbol);
                        if (markAsNoStruct)
                        {
                            ScopesThatCantBeStructs.Add(innermost.BoundNode);
                        }

                        while (innermost != outermost)
                        {
                            NeedsParentFrame.Add(innermost.BoundNode);
                            innermost = innermost.Parent;
                            if (markAsNoStruct && innermost != null)
                            {
                                ScopesThatCantBeStructs.Add(innermost.BoundNode);
                            }
                        }
                    }
                }
            }
		public void AddZeroKey()
		{
			var dict = new SmallDictionary<int, string>();
			dict.Add(0,"abc");
		}
		public void GetMissingKeyOne()
		{
			var dict = new SmallDictionary<int, string>();
			dict.Add(2, "abc");
			var temp = dict[3];
		}
Beispiel #19
0
        private static SmallDictionary<TypeParameterSymbol, TypeWithModifiers> ConstructMapping(ImmutableArray<TypeParameterSymbol> from, ImmutableArray<TypeWithModifiers> to)
        {
            var mapping = new SmallDictionary<TypeParameterSymbol, TypeWithModifiers>(ReferenceEqualityComparer.Instance);

            Debug.Assert(from.Length == to.Length);

            for (int i = 0; i < from.Length; i++)
            {
                TypeParameterSymbol tp = from[i];
                TypeWithModifiers ta = to[i];
                if (!ta.Is(tp))
                {
                    mapping.Add(tp, ta);
                }
            }

            return mapping;
        }
		public void GetMissingKeyThree()
		{
			var dict = new SmallDictionary<int, string>();
			dict.Add(2, "abc");
			dict.Add(5, "def");
			dict.Add(11, "third");
			var temp = dict[3];
		}
        /// <summary>
        /// Report an error if adding the edge (method1, method2) to the ctor-initializer
        /// graph would add a new cycle to that graph.
        /// </summary>
        /// <param name="method1">a calling ctor</param>
        /// <param name="method2">the chained-to ctor</param>
        /// <param name="syntax">where to report a cyclic error if needed</param>
        /// <param name="diagnostics">a diagnostic bag for receiving the diagnostic</param>
        internal void ReportCtorInitializerCycles(MethodSymbol method1, MethodSymbol method2, SyntaxNode syntax, DiagnosticBag diagnostics)
        {
            // precondition and postcondition: the graph _constructorInitializers is acyclic.
            // If adding the edge (method1, method2) would induce a cycle, we report an error
            // and do not add it to the set of edges. If it would not induce a cycle we add
            // it to the set of edges and return.

            if (method1 == method2)
            {
                // direct recursion is diagnosed elsewhere
                throw ExceptionUtilities.Unreachable;
            }

            if (_constructorInitializers == null)
            {
                _constructorInitializers = new SmallDictionary<MethodSymbol, MethodSymbol>();
                _constructorInitializers.Add(method1, method2);
                return;
            }

            MethodSymbol next = method2;
            while (true)
            {
                if (_constructorInitializers.TryGetValue(next, out next))
                {
                    Debug.Assert((object)next != null);
                    if (method1 == next)
                    {
                        // We found a (new) cycle containing the edge (method1, method2). Report an
                        // error and do not add the edge.
                        diagnostics.Add(ErrorCode.ERR_IndirectRecursiveConstructorCall, syntax.Location, method1);
                        return;
                    }
                }
                else
                {
                    // we've reached the end of the path without finding a cycle. Add the new edge.
                    _constructorInitializers.Add(method1, method2);
                    return;
                }
            }
        }
		public void Add()
		{
			var dict = new SmallDictionary<int, string>();
			dict.Add(2, "abc");
			Assert.AreEqual(1, dict.Count);
			dict.Add(5,"def");
			Assert.AreEqual(2, dict.Count);
			dict.Add(11,"third");
			Assert.AreEqual(3, dict.Count);
			Assert.AreEqual("abc", dict[2]);
			Assert.AreEqual("def", dict[5]);
			Assert.AreEqual("third", dict[11]);
		}
		public void AddExistingKeyTwoKeys()
		{
			var dict = new SmallDictionary<int, string>();
			dict.Add(2, "abc");
			dict.Add(3, "def");
			dict.Add(3, "def");
		}
		public void Remove()
		{
			var dict = new SmallDictionary<int, string>();
			Assert.IsFalse(dict.Remove(2), "Remove a missing item from an empty dictionary");
			dict.Add(2, "abc");
			Assert.IsFalse(dict.Remove(3), "Remove a missing item from a dictionary with one item");
			Assert.IsTrue(dict.Remove(2), "Remove the only item from a dictionary");
			Assert.AreEqual(0, dict.Count, "Nothing remains after removing the only item");
			dict.Add(2, "abc");
			dict.Add(5, "def");
			dict.Add(11, "third");
			Assert.IsFalse(dict.Remove(7), "Remove a missing item from a dictionary with three items");
			Assert.IsTrue(dict.Remove(2), "Remove the first item from a dictionary with three items");
			Assert.AreEqual(2, dict.Count, "Two items remain after removing the first of three");
			string output;
			Assert.IsFalse(dict.TryGetValue(2, out output), "Removed item (first) should be gone from original three");
			Assert.AreEqual("def", dict[5]);
			Assert.AreEqual("third", dict[11]);
			dict.Add(2, "abc");
			Assert.IsTrue(dict.Remove(5), "Remove first of two items in others");
			Assert.AreEqual(2, dict.Count);
			Assert.IsFalse(dict.TryGetValue(5, out output));
			Assert.AreEqual("abc", dict[2]);
			Assert.AreEqual("third", dict[11]);

			Assert.IsTrue(dict.Remove(2), "Remove only item in others");
			Assert.AreEqual(1, dict.Count);
			Assert.IsFalse(dict.TryGetValue(2, out output));
			Assert.AreEqual("third", dict[11]);

			Assert.IsTrue(dict.Remove(11), "Remove only item in dictionary which previously had three");
			Assert.AreEqual(0, dict.Count);
			Assert.IsFalse(dict.TryGetValue(11, out output));

			dict.Add(2, "abc");
			dict.Add(5, "def");
			dict.Add(11, "third");
			dict.Add(13, "fourth");
			Assert.IsTrue(dict.Remove(11), "Remove middle of three items in others");
			Assert.AreEqual(3, dict.Count);
			Assert.IsFalse(dict.TryGetValue(11, out output));
			Assert.AreEqual("abc", dict[2]);
			Assert.AreEqual("def", dict[5]);
			Assert.AreEqual("fourth", dict[13]);

			Assert.IsTrue(dict.Remove(13), "Remove last of two items in others");
			Assert.AreEqual(2, dict.Count);
			Assert.IsFalse(dict.TryGetValue(13, out output));
			Assert.AreEqual("abc", dict[2]);
			Assert.AreEqual("def", dict[5]);

			Assert.IsTrue(dict.Remove(2), "Remove first item when others contains exactly one");
			Assert.AreEqual(1, dict.Count);
			Assert.IsFalse(dict.TryGetValue(2, out output));
			Assert.AreEqual("def", dict[5]);
		}
		public void Enumerator()
		{
			var dict = new SmallDictionary<int, string>();
			foreach (var kvp in dict)
			{
				Assert.Fail("Should get no iterations looping over empty dictionary");
			}
			dict.Add(2, "abc");
			int count = 0;
			foreach (var kvp in dict)
			{
				count++;
				Assert.AreEqual(2, kvp.Key);
				Assert.AreEqual("abc", kvp.Value);
			}
			Assert.AreEqual(1,count);

			dict.Add(5, "def");
			dict.Add(11, "third");
			count = 0;
			Dictionary<int, string> normalDict = new Dictionary<int, string>(dict);
			Assert.AreEqual("abc", normalDict[2]);
			Assert.AreEqual("def", normalDict[5]);
			Assert.AreEqual("third", normalDict[11]);
			Assert.AreEqual(3, normalDict.Count);
			foreach (var kvp in dict)
			{
				count++;
				Assert.IsTrue(normalDict.ContainsKey(kvp.Key));
				Assert.AreEqual(normalDict[kvp.Key], kvp.Value);
				normalDict.Remove(kvp.Key);
			}
			Assert.AreEqual(3, count);
		}
		public void KeysAndValues()
		{
			var dict = new SmallDictionary<int, string>();
			VerifyIntArrays(new int[0], dict.Keys.ToArray());
			VerifyStringArrays(new string[0], dict.Values.ToArray());

			dict.Add(2, "abc");
			VerifyIntArrays(new int[] {2}, dict.Keys.ToArray());
			VerifyStringArrays(new string[] {"abc"}, dict.Values.ToArray());

			// This test is too strict, it enforces a particular order of the results.
			dict.Add(5, "def");
			dict.Add(11, "third");
			VerifyIntArrays(new int[] { 2, 5, 11 }, dict.Keys.ToArray());
			VerifyStringArrays(new string[] { "abc", "def", "third" }, dict.Values.ToArray());
		}