using J2N;
using J2N.Text;
using J2N.Globalization;
using Lucene.Net.Util;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Text;

namespace Lucene.Net.Analysis.Util
{
    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    /// <summary>
    /// A simple class that stores key <see cref="string"/>s as <see cref="T:char[]"/>'s in a
    /// hash table. Note that this is not a general purpose
    /// class.  For example, it cannot remove items from the
    /// map, nor does it resize its hash table to be smaller,
    /// etc.  It is designed to be quick to retrieve items
    /// by <see cref="T:char[]"/> keys without the necessity of converting
    /// to a <see cref="string"/> first.
    /// 
    /// <a name="version"></a>
    /// <para>You must specify the required <see cref="LuceneVersion"/>
    /// compatibility when creating <see cref="CharArrayMap"/>:
    /// <list type="bullet">
    ///   <item><description> As of 3.1, supplementary characters are
    ///       properly lowercased.</description></item>
    /// </list>
    /// Before 3.1 supplementary characters could not be
    /// lowercased correctly due to the lack of Unicode 4
    /// support in JDK 1.4. To use instances of
    /// <see cref="CharArrayMap"/> with the behavior before Lucene
    /// 3.1 pass a <see cref="LuceneVersion"/> &lt; 3.1 to the constructors.
    /// </para>
    /// </summary>
    public class CharArrayMap<TValue> : ICharArrayMap, IDictionary<string, TValue>
    {
        // private only because missing generics
        private static readonly CharArrayMap<TValue> EMPTY_MAP = new CharArrayMap.EmptyCharArrayMap<TValue>();

        private const int INIT_SIZE = 8;
        private readonly CharacterUtils charUtils;
        private readonly bool ignoreCase;
        private int count;
        private readonly LuceneVersion matchVersion; // package private because used in CharArraySet
        internal char[][] keys; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
        internal MapValue[] values; // package private because used in CharArraySet's non Set-conform CharArraySetIterator

        /// <summary>
        /// LUCENENET: Moved this from CharArraySet so it doesn't need to know the generic type of CharArrayMap
        /// </summary>
        internal static readonly MapValue PLACEHOLDER = new MapValue();

        /// <summary>
        /// LUCENENET SPECIFIC type used to act as a placeholder. Since <c>null</c>
        /// means that our value is not populated, we need an instance of something
        /// to indicate it is. Using an instance of <typeparamref name="TValue"/> would only work if
        /// we could constrain it with the new() constraint, which isn't possible because
        /// some types such as <see cref="string"/> don't have a default constructor.
        /// So, this is a workaround that allows any type regardless of the type of constructor.
        /// 
        /// <para>
        /// Note also that we gain the ability to use value types for <typeparamref name="TValue"/>, but
        /// also create a difference in behavior from Java Lucene where the actual values 
        /// returned could be <c>null</c>.
        /// </para>
        /// </summary>
        internal class MapValue
        {
            private TValue value = default(TValue);
            public TValue Value
            {
                get => value;
                set => this.value = value;
            }

            public MapValue()
            { }

            public MapValue(TValue value)
            {
                this.value = value;
            }
        }

        /// <summary>
        /// Create map with enough capacity to hold <paramref name="startSize"/> terms
        /// </summary>
        /// <param name="matchVersion">
        ///          lucene compatibility version - see <see cref="CharArrayMap{TValue}"/> for details. </param>
        /// <param name="startSize">
        ///          the initial capacity </param>
        /// <param name="ignoreCase">
        ///          <c>false</c> if and only if the set should be case sensitive;
        ///          otherwise <c>true</c>. </param>
        public CharArrayMap(LuceneVersion matchVersion, int startSize, bool ignoreCase)
        {
            this.ignoreCase = ignoreCase;
            var size = INIT_SIZE;
            while (startSize + (startSize >> 2) > size)
            {
                size <<= 1;
            }
            keys = new char[size][];
            values = new MapValue[size];
            this.charUtils = CharacterUtils.GetInstance(matchVersion);
            this.matchVersion = matchVersion;
        }

        /// <summary>
        /// Creates a map from the mappings in another map. 
        /// </summary>
        /// <param name="matchVersion">
        ///          compatibility match version see <a href="#version">Version
        ///          note</a> above for details. </param>
        /// <param name="c">
        ///          a map (<see cref="T:IDictionary{string, V}"/>) whose mappings to be copied </param>
        /// <param name="ignoreCase">
        ///          <c>false</c> if and only if the set should be case sensitive;
        ///          otherwise <c>true</c>. </param>
        public CharArrayMap(LuceneVersion matchVersion, IDictionary<string, TValue> c, bool ignoreCase)
            : this(matchVersion, c.Count, ignoreCase)
        {
            foreach (var v in c)
            {
                Add(v);
            }
        }

        /// <summary>
        /// Create set from the supplied map (used internally for readonly maps...)
        /// </summary>
        internal CharArrayMap(CharArrayMap<TValue> toCopy)
        {
            this.keys = toCopy.keys;
            this.values = toCopy.values;
            this.ignoreCase = toCopy.ignoreCase;
            this.count = toCopy.count;
            this.charUtils = toCopy.charUtils;
            this.matchVersion = toCopy.matchVersion;
        }

        /// <summary>
        /// Adds the <see cref="T:KeyValuePair{string, V}.Value"/> for the passed in <see cref="T:KeyValuePair{string, V}.Key"/>.
        /// Note that the <see cref="T:KeyValuePair{string, V}"/> instance is not added to the dictionary.
        /// </summary>
        /// <param name="item">A <see cref="T:KeyValuePair{string, V}"/> whose <see cref="T:KeyValuePair{string, V}.Value"/> 
        /// will be added for the corresponding <see cref="T:KeyValuePair{string, V}.Key"/>. </param>
        public virtual void Add(KeyValuePair<string, TValue> item)
        {
            Add(item.Key, item.Value);
        }

        /// <summary>
        /// Adds the <paramref name="value"/> for the passed in <paramref name="key"/>.
        /// </summary>
        /// <param name="key">The string-able type to be added/updated in the dictionary.</param>
        /// <param name="value">The corresponding value for the given <paramref name="key"/>.</param>
        public virtual void Add(string key, TValue value)
        {
            if (ContainsKey(key))
            {
                throw new ArgumentException("The key " + key + " already exists in the dictionary");
            }
            Put(key, value);
        }

        /// <summary>
        /// Clears all entries in this map. This method is supported for reusing, but not 
        /// <see cref="IDictionary{TKey, TValue}.Remove(TKey)"/>. 
        /// </summary>
        public virtual void Clear()
        {
            count = 0;
            keys.Fill(null);
            values.Fill(null);
        }

        /// <summary>
        /// Not supported. 
        /// </summary>
        [Obsolete("Not applicable in this class.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public virtual bool Contains(KeyValuePair<string, TValue> item)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Copies all items in the current dictionary the <paramref name="array"/> starting at the <paramref name="arrayIndex"/>.
        /// The array is assumed to already be dimensioned to fit the elements in this dictionary; otherwise a <see cref="ArgumentOutOfRangeException"/>
        /// will be thrown.
        /// </summary>
        /// <param name="array">The array to copy the items into.</param>
        /// <param name="arrayIndex">A 32-bit integer that represents the index in <paramref name="array"/> at which copying begins.</param>
        public virtual void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
        {
            using (var iter = (EntryIterator)EntrySet().GetEnumerator())
            {
                for (int i = arrayIndex; iter.MoveNext(); i++)
                {
                    array[i] = new KeyValuePair<string, TValue>(iter.Current.Key, iter.CurrentValue);
                }
            }
        }

        /// <summary>
        /// Copies all items in the current <see cref="CharArrayMap{TValue}"/> to the passed in
        /// <see cref="CharArrayMap{TValue}"/>.
        /// </summary>
        /// <param name="map"></param>
        public virtual void CopyTo(CharArrayMap<TValue> map)
        {
            using (var iter = (EntryIterator)EntrySet().GetEnumerator())
            {
                while (iter.MoveNext())
                {
                    map.Put(iter.Current.Key, iter.CurrentValue);
                }
            }
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="length"/> chars of <paramref name="text"/> starting at <paramref name="offset"/>
        /// are in the <see cref="Keys"/> 
        /// </summary>
        public virtual bool ContainsKey(char[] text, int offset, int length)
        {
            return keys[GetSlot(text, offset, length)] != null;
        }

        /// <summary>
        /// <c>true</c> if the entire <see cref="Keys"/> is the same as the 
        /// <paramref name="text"/> <see cref="T:char[]"/> being passed in; 
        /// otherwise <c>false</c>.
        /// </summary>
        public virtual bool ContainsKey(char[] text)
        {
            return keys[GetSlot(text, 0, text.Length)] != null;
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="text"/> <see cref="string"/> is in the <see cref="Keys"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public virtual bool ContainsKey(string text)
        {
            return keys[GetSlot(text)] != null;
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="text"/> <see cref="ICharSequence"/> is in the <see cref="Keys"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public virtual bool ContainsKey(ICharSequence text)
        {
            return keys[GetSlot(text)] != null;
        }


        /// <summary>
        /// <c>true</c> if the <paramref name="o"/> <see cref="object.ToString()"/> is in the <see cref="Keys"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public virtual bool ContainsKey(object o)
        {
            if (o == null)
            {
                throw new ArgumentException("o can't be null", "o");
            }

            var c = o as char[];
            if (c != null)
            {
                var text = c;
                return ContainsKey(text, 0, text.Length);
            }
            return ContainsKey(o.ToString());
        }

        /// <summary>
        /// returns the value of the mapping of <paramref name="length"/> chars of <paramref name="text"/>
        /// starting at <paramref name="offset"/>
        /// </summary>
        public virtual TValue Get(char[] text, int offset, int length)
        {
            var value = values[GetSlot(text, offset, length)];
            return (value != null) ? value.Value : default(TValue);
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public virtual TValue Get(char[] text)
        {
            var value = values[GetSlot(text, 0, text.Length)];
            return (value != null) ? value.Value : default(TValue);
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <see cref="ICharSequence"/>
        /// </summary>
        public virtual TValue Get(ICharSequence text)
        {
            var value = values[GetSlot(text)];
            return (value != null) ? value.Value : default(TValue);
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <see cref="string"/>
        /// </summary>
        public virtual TValue Get(string text)
        {
            var value = values[GetSlot(text)];
            return (value != null) ? value.Value : default(TValue);
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <see cref="object.ToString()"/>
        /// </summary>
        public virtual TValue Get(object o)
        {
            // LUCENENET NOTE: Testing for *is* is at least 10x faster
            // than casting using *as* and then checking for null.
            // http://stackoverflow.com/q/1583050/181087
            if (o is char[])
            {
                var text = o as char[];
                return Get(text, 0, text.Length);
            }
            return Get(o.ToString());
        }

        private int GetSlot(char[] text, int offset, int length)
        {
            int code = GetHashCode(text, offset, length);
            int pos = code & (keys.Length - 1);
            char[] text2 = keys[pos];
            if (text2 != null && !Equals(text, offset, length, text2))
            {
                int inc = ((code >> 8) + code) | 1;
                do
                {
                    code += inc;
                    pos = code & (keys.Length - 1);
                    text2 = keys[pos];
                } while (text2 != null && !Equals(text, offset, length, text2));
            }
            return pos;
        }

        /// <summary>
        /// Returns true if the <see cref="ICharSequence"/> is in the set
        /// </summary>
        private int GetSlot(ICharSequence text)
        {
            int code = GetHashCode(text);
            int pos = code & (keys.Length - 1);
            char[] text2 = keys[pos];
            if (text2 != null && !Equals(text, text2))
            {
                int inc = ((code >> 8) + code) | 1;
                do
                {
                    code += inc;
                    pos = code & (keys.Length - 1);
                    text2 = keys[pos];
                } while (text2 != null && !Equals(text, text2));
            }
            return pos;
        }

        /// <summary>
        /// Returns true if the <see cref="string"/> is in the set
        /// </summary>
        private int GetSlot(string text)
        {
            int code = GetHashCode(text);
            int pos = code & (keys.Length - 1);
            char[] text2 = keys[pos];
            if (text2 != null && !Equals(text, text2))
            {
                int inc = ((code >> 8) + code) | 1;
                do
                {
                    code += inc;
                    pos = code & (keys.Length - 1);
                    text2 = keys[pos];
                } while (text2 != null && !Equals(text, text2));
            }
            return pos;
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public virtual TValue Put(ICharSequence text, TValue value)
        {
            MapValue oldValue = PutImpl(text, new MapValue(value)); // could be more efficient
            return (oldValue != null) ? oldValue.Value : default(TValue);
        }

        /// <summary>
        /// Add the given mapping using the <see cref="object.ToString()"/> representation
        /// of <paramref name="o"/> in the <see cref="CultureInfo.InvariantCulture"/>.
        /// </summary>
        public virtual TValue Put(object o, TValue value)
        {
            MapValue oldValue = PutImpl(o, new MapValue(value));
            return (oldValue != null) ? oldValue.Value : default(TValue);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public virtual TValue Put(string text, TValue value)
        {
            MapValue oldValue = PutImpl(text, new MapValue(value));
            return (oldValue != null) ? oldValue.Value : default(TValue);
        }

        /// <summary>
        /// Add the given mapping.
        /// If ignoreCase is true for this Set, the text array will be directly modified.
        /// The user should never modify this text array after calling this method.
        /// </summary>
        public virtual TValue Put(char[] text, TValue value)
        {
            MapValue oldValue = PutImpl(text, new MapValue(value));
            return (oldValue != null) ? oldValue.Value : default(TValue);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        private MapValue PutImpl(ICharSequence text, MapValue value)
        {
            return PutImpl(text.ToString(), value); // could be more efficient
        }

        private MapValue PutImpl(object o, MapValue value)
        {
            // LUCENENET NOTE: Testing for *is* is at least 10x faster
            // than casting using *as* and then checking for null.
            // http://stackoverflow.com/q/1583050/181087 
            if (o is char[])
            {
                var c = o as char[];
                return PutImpl(c, value);
            }
            // LUCENENET: We need value types to be represented using the invariant
            // culture, so it is consistent regardless of the current culture. 
            // It's easy to work out if this is a value type, but difficult
            // to get to the ToString(IFormatProvider) overload of the type without
            // a lot of special cases. It's easier just to change the culture of the 
            // thread before calling ToString(), but we don't want that behavior to
            // bleed into PutImpl.
            string s;
            using (var context = new CultureContext(CultureInfo.InvariantCulture))
            {
                s = o.ToString();
            }
            return PutImpl(s, value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        private MapValue PutImpl(string text, MapValue value)
        {
            return PutImpl(text.ToCharArray(), value);
        }

        /// <summary>
        /// LUCENENET specific. Centralizes the logic between Put()
        /// implementations that accept a value and those that don't. This value is
        /// so we know whether or not the value was set, since we can't reliably do
        /// a check for <c>null</c> on the <typeparamref name="TValue"/> type.
        /// </summary>
        private MapValue PutImpl(char[] text, MapValue value)
        {
            if (ignoreCase)
            {
                charUtils.ToLower(text, 0, text.Length);
            }
            int slot = GetSlot(text, 0, text.Length);
            if (keys[slot] != null)
            {
                MapValue oldValue = values[slot];
                values[slot] = value;
                return oldValue;
            }
            keys[slot] = text;
            values[slot] = value;
            count++;

            if (count + (count >> 2) > keys.Length)
            {
                Rehash();
            }

            return null;
        }

        #region PutAll

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IDictionary{char[],TValue}"/>'s
        /// entries, and calls this map's <see cref="Put(char[], TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
        public virtual void PutAll(IDictionary<char[], TValue> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IDictionary{string,TValue}"/>'s
        /// entries, and calls this map's <see cref="Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
        public virtual void PutAll(IDictionary<string, TValue> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IDictionary{ICharSequence,TValue}"/>'s
        /// entries, and calls this map's <see cref="Put(ICharSequence, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
        public virtual void PutAll(IDictionary<ICharSequence, TValue> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IDictionary{object,TValue}"/>'s
        /// entries, and calls this map's <see cref="Put(object, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
        public virtual void PutAll(IDictionary<object, TValue> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{char[],TValue}}"/>'s
        /// entries, and calls this map's <see cref="Put(char[], TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">The values to add/update in the current map.</param>
        public virtual void PutAll(IEnumerable<KeyValuePair<char[], TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{string,TValue}}"/>'s
        /// entries, and calls this map's <see cref="Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">The values to add/update in the current map.</param>
        public virtual void PutAll(IEnumerable<KeyValuePair<string, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{ICharSequence,TValue}}"/>'s
        /// entries, and calls this map's <see cref="Put(ICharSequence, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">The values to add/update in the current map.</param>
        public virtual void PutAll(IEnumerable<KeyValuePair<ICharSequence, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{object,TValue}}"/>'s
        /// entries, and calls this map's <see cref="Put(object, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="collection">The values to add/update in the current map.</param>
        public virtual void PutAll(IEnumerable<KeyValuePair<object, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                Put(kvp.Key, kvp.Value);
            }
        }

        #endregion

        private void Rehash()
        {
            Debug.Assert(keys.Length == values.Length);
            int newSize = 2 * keys.Length;
            char[][] oldkeys = keys;
            MapValue[] oldvalues = values;
            keys = new char[newSize][];
            values = new MapValue[newSize];

            for (int i = 0; i < oldkeys.Length; i++)
            {
                char[] text = oldkeys[i];
                if (text != null)
                {
                    // todo: could be faster... no need to compare strings on collision
                    int slot = GetSlot(text, 0, text.Length);
                    keys[slot] = text;
                    values[slot] = oldvalues[i];
                }
            }
        }

        private bool Equals(char[] text1, int offset, int length, char[] text2)
        {
            if (length != text2.Length)
            {
                return false;
            }
            int limit = offset + length;
            if (ignoreCase)
            {
                for (int i = 0; i < length;)
                {
                    var codePointAt = charUtils.CodePointAt(text1, offset + i, limit);
                    if (Character.ToLower(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
                    {
                        return false;
                    }
                    i += Character.CharCount(codePointAt);
                }
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    if (text1[offset + i] != text2[i])
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        private bool Equals(ICharSequence text1, char[] text2)
        {
            int length = text1.Length;
            if (length != text2.Length)
            {
                return false;
            }
            if (ignoreCase)
            {
                for (int i = 0; i < length;)
                {
                    int codePointAt = charUtils.CodePointAt(text1, i);
                    if (Character.ToLower(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
                    {
                        return false;
                    }
                    i += Character.CharCount(codePointAt);
                }
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    if (text1[i] != text2[i])
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        private bool Equals(string text1, char[] text2)
        {
            int length = text1.Length;
            if (length != text2.Length)
            {
                return false;
            }
            if (ignoreCase)
            {
                for (int i = 0; i < length;)
                {
                    int codePointAt = charUtils.CodePointAt(text1, i);
                    if (Character.ToLower(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
                    {
                        return false;
                    }
                    i += Character.CharCount(codePointAt);
                }
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    if (text1[i] != text2[i])
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// LUCENENET Specific - test for value equality similar to how it is done in Java
        /// </summary>
        /// <param name="obj">Another dictionary to test the values of</param>
        /// <returns><c>true</c> if the given object is an <see cref="T:IDictionary{object, V}"/> that contains
        /// the same key value pairs as the current map</returns>
        public override bool Equals(object obj)
        {
            var other = obj as IDictionary<string, TValue>;
            if (other == null)
                return false;

            if (this.Count != other.Count)
                return false;

            using (var iter = other.GetEnumerator())
            {
                while (iter.MoveNext())
                {
                    if (!this.TryGetValue(iter.Current.Key, out TValue value))
                        return false;

                    if (!EqualityComparer<TValue>.Default.Equals(value, iter.Current.Value))
                        return false;
                }
            }

            return true;
        }

        /// <summary>
        /// LUCENENET Specific - override required by .NET because we override Equals
        /// to simulate Java's value equality checking.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            const int PRIME = 31; // arbitrary prime
            int hash = PRIME;
            using (var iter = (EntryIterator)EntrySet().GetEnumerator())
            {
                while (iter.MoveNext())
                {
                    hash = (hash * PRIME) ^ iter.Current.Key.GetHashCode();
                    hash = (hash * PRIME) ^ iter.Current.Value.GetHashCode();
                }
            }
            return hash;
        }

        private int GetHashCode(char[] text, int offset, int length)
        {
            if (text == null)
            {
                throw new ArgumentException("text can't be null", "text");
            }
            int code = 0;
            int stop = offset + length;
            if (ignoreCase)
            {
                for (int i = offset; i < stop;)
                {
                    int codePointAt = charUtils.CodePointAt(text, i, stop);
                    code = code * 31 + Character.ToLower(codePointAt);
                    i += Character.CharCount(codePointAt);
                }
            }
            else
            {
                for (int i = offset; i < stop; i++)
                {
                    code = code * 31 + text[i];
                }
            }
            return code;
        }

        private int GetHashCode(ICharSequence text)
        {
            if (text == null)
            {
                throw new ArgumentException("text can't be null", "text");
            }

            int code = 0;
            int length = text.Length;
            if (ignoreCase)
            {
                for (int i = 0; i < length;)
                {
                    int codePointAt = charUtils.CodePointAt(text, i);
                    code = code * 31 + Character.ToLower(codePointAt);
                    i += Character.CharCount(codePointAt);
                }
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    code = code * 31 + text[i];
                }
            }
            return code;
        }

        private int GetHashCode(string text)
        {
            if (text == null)
            {
                throw new ArgumentException("text can't be null", "text");
            }

            int code = 0;
            int length = text.Length;
            if (ignoreCase)
            {
                for (int i = 0; i < length;)
                {
                    int codePointAt = charUtils.CodePointAt(text, i);
                    code = code * 31 + Character.ToLower(codePointAt);
                    i += Character.CharCount(codePointAt);
                }
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    code = code * 31 + text[i];
                }
            }
            return code;
        }

        #region For .NET Support LUCENENET

        /// <summary>
        /// The Lucene version corresponding to the compatibility behavior 
        /// that this instance emulates
        /// </summary>
        public virtual LuceneVersion MatchVersion => matchVersion;

        /// <summary>
        /// Adds a placeholder with the given <paramref name="text"/> as the key.
        /// Primarily for internal use by <see cref="CharArraySet"/>.
        /// </summary>
        public virtual bool Put(char[] text)
        {
            return PutImpl(text, PLACEHOLDER) == null;
        }

        /// <summary>
        /// Adds a placeholder with the given <paramref name="text"/> as the key.
        /// Primarily for internal use by <see cref="CharArraySet"/>.
        /// </summary>
        public virtual bool Put(ICharSequence text)
        {
            return PutImpl(text, PLACEHOLDER) == null;
        }

        /// <summary>
        /// Adds a placeholder with the given <paramref name="text"/> as the key.
        /// Primarily for internal use by <see cref="CharArraySet"/>.
        /// </summary>
        public virtual bool Put(string text)
        {
            return PutImpl(text, PLACEHOLDER) == null;
        }

        /// <summary>
        /// Adds a placeholder with the given <paramref name="o"/> as the key.
        /// Primarily for internal use by <see cref="CharArraySet"/>.
        /// </summary>
        public virtual bool Put(object o)
        {
            return PutImpl(o, PLACEHOLDER) == null;
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="offset">The position of the <paramref name="key"/> where the target key begins.</param>
        /// <param name="length">The total length of the <paramref name="key"/>.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public virtual bool TryGetValue(char[] key, int offset, int length, out TValue value)
        {
            var val = values[GetSlot(key, offset, length)];
            if (val != null)
            {
                value = val.Value;
                return true;
            }
            value = default(TValue);
            return false;
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public virtual bool TryGetValue(char[] key, out TValue value)
        {
            var val = values[GetSlot(key, 0, key.Length)];
            if (val != null)
            {
                value = val.Value;
                return true;
            }
            value = default(TValue);
            return false;
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public virtual bool TryGetValue(ICharSequence key, out TValue value)
        {
            var val = values[GetSlot(key)];
            if (val != null)
            {
                value = val.Value;
                return true;
            }
            value = default(TValue);
            return false;
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public virtual bool TryGetValue(string key, out TValue value)
        {
            var val = values[GetSlot(key)];
            if (val != null)
            {
                value = val.Value;
                return true;
            }
            value = default(TValue);
            return false;
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public virtual bool TryGetValue(object key, out TValue value)
        {
            // LUCENENET NOTE: Testing for *is* is at least 10x faster
            // than casting using *as* and then checking for null.
            // http://stackoverflow.com/q/1583050/181087
            if (key is char[])
            {
                var text = key as char[];
                return TryGetValue(text, 0, text.Length, out value);
            }
            return TryGetValue(key.ToString(), out value);
        }

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <param name="offset">The position of the <paramref name="key"/> where the target key begins.</param>
        /// <param name="length">The total length of the <paramref name="key"/>.</param>
        public virtual TValue this[char[] key, int offset, int length]
        {
            get => Get(key, offset, length);
            set => Put(key, value);
        }

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        public virtual TValue this[char[] key]
        {
            get => Get(key);
            set => Put(key, value);
        }

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        public virtual TValue this[ICharSequence key]
        {
            get => Get(key);
            set => Put(key, value);
        }

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        public virtual TValue this[string key]
        {
            get => Get(key);
            set => Put(key, value);
        }

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        public virtual TValue this[object key]
        {
            get => Get(key);
            set => Put(key, value);
        }

        /// <summary>
        /// Gets a collection containing the keys in the <see cref="CharArrayMap{TValue}"/>.
        /// </summary>
        public virtual ICollection<string> Keys => KeySet;


        private volatile ICollection<TValue> valueSet;

        /// <summary>
        /// Gets a collection containing the values in the <see cref="CharArrayMap{TValue}"/>.
        /// This specialized collection can be enumerated in order to read its values and 
        /// overrides <see cref="object.ToString()"/> in order to display a string 
        /// representation of the values.
        /// </summary>
        public ICollection<TValue> Values
        {
            get
            {
                if (valueSet == null)
                {
                    valueSet = new ValueCollection(this);
                }
                return valueSet;
            }
        }

        /// <summary>
        /// LUCENENET specific class used to break the infinite recursion when the
        /// CharArraySet iterates the keys of this dictionary via <see cref="OriginalKeySet"/>. 
        /// In Java, the keyset of the abstract base class was used to break the infinite recursion, 
        /// however this class doesn't have an abstract base class so that is not an option. 
        /// This class is just a facade around the keys (not another collection of keys), so it 
        /// doesn't consume any additional RAM while providing another "virtual" collection to iterate over.
        /// </summary>
        internal class KeyCollection : ICollection<string>
        {
            private readonly CharArrayMap<TValue> outerInstance;

            public KeyCollection(CharArrayMap<TValue> outerInstance)
            {
                this.outerInstance = outerInstance;
            }

            public int Count => outerInstance.Count;

            public bool IsReadOnly => outerInstance.IsReadOnly;

            public void Add(string item)
            {
                throw new NotSupportedException();
            }

            public void Clear()
            {
                outerInstance.Clear();
            }

            public bool Contains(string item)
            {
                return outerInstance.ContainsKey(item);
            }

            public void CopyTo(string[] array, int arrayIndex)
            {
                using (var iter = GetEnumerator())
                {
                    for (int i = arrayIndex; iter.MoveNext(); i++)
                    {
                        array[i] = iter.Current;
                    }
                }
            }

            public IEnumerator<string> GetEnumerator()
            {
                return new KeyEnumerator(outerInstance);
            }

            public bool Remove(string item)
            {
                throw new NotSupportedException();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            /// <summary>
            /// LUCENENET specific class to iterate the values in the <see cref="KeyCollection"/>.
            /// </summary>
            private class KeyEnumerator : IEnumerator<string>
            {
                private readonly CharArrayMap<TValue> outerInstance;
                private readonly EntryIterator entryIterator;

                public KeyEnumerator(CharArrayMap<TValue> outerInstance)
                {
                    this.outerInstance = outerInstance;
                    this.entryIterator = new EntryIterator(outerInstance, !outerInstance.IsReadOnly);
                }

                public string Current => entryIterator.Current.Key;

                object IEnumerator.Current => Current;

                public void Dispose()
                {
                    // nothing to do
                }

                public bool MoveNext()
                {
                    return entryIterator.MoveNext();
                }

                public void Reset()
                {
                    entryIterator.Reset();
                }
            }
        }

        /// <summary>
        /// LUCENENET specific class that represents the values in the <see cref="CharArrayMap{TValue}"/>.
        /// </summary>
        internal class ValueCollection : ICollection<TValue>
        {
            private readonly CharArrayMap<TValue> outerInstance;

            public ValueCollection(CharArrayMap<TValue> outerInstance)
            {
                this.outerInstance = outerInstance;
            }

            public int Count => outerInstance.Count;

            public bool IsReadOnly => outerInstance.IsReadOnly;

            public void Add(TValue item)
            {
                throw new NotSupportedException();
            }

            public void Clear()
            {
                outerInstance.Clear();
            }

            public bool Contains(TValue item)
            {
                for (int i = 0; i < outerInstance.values.Length; i++)
                {
                    var value = outerInstance.values[i];
                    if (J2N.Collections.Generic.EqualityComparer<TValue>.Equals(value, item))
                        return true;
                }
                return false;
            }

            public void CopyTo(TValue[] array, int arrayIndex)
            {
                throw new NotSupportedException();
            }

            public IEnumerator<TValue> GetEnumerator()
            {
                return new ValueEnumerator(outerInstance);
            }

            public bool Remove(TValue item)
            {
                throw new NotSupportedException();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public override string ToString()
            {
                using (var i = (ValueEnumerator)GetEnumerator())
                {
                    if (!i.HasNext)
                        return "[]";

                    StringBuilder sb = new StringBuilder();
                    sb.Append('[');
                    while (i.MoveNext())
                    {
                        TValue value = i.Current;
                        if (sb.Length > 1)
                        {
                            sb.Append(',').Append(' ');
                        }
                        sb.Append(value.ToString());
                    }

                    return sb.Append(']').ToString();
                }
            }

            /// <summary>
            /// LUCENENET specific class to enumerate the values in the <see cref="ValueCollection"/>.
            /// </summary>
            private class ValueEnumerator : IEnumerator<TValue>
            {
                private readonly CharArrayMap<TValue> outerInstance;
                private readonly EntryIterator entryIterator;

                public ValueEnumerator(CharArrayMap<TValue> outerInstance)
                {
                    this.outerInstance = outerInstance;
                    this.entryIterator = new EntryIterator(outerInstance, !outerInstance.IsReadOnly);
                }

                public TValue Current => entryIterator.CurrentValue;

                object IEnumerator.Current => Current;

                public void Dispose()
                {
                    entryIterator.Dispose();
                }

                public bool MoveNext()
                {
                    return entryIterator.MoveNext();
                }

                public void Reset()
                {
                    entryIterator.Reset();
                }

                public bool HasNext => entryIterator.HasNext;
            }
        }

        #endregion

        /// <summary>
        /// <c>true</c> if the <see cref="CharArrayMap{TValue}"/> is read-only; otherwise <c>false</c>.
        /// </summary>
        public virtual bool IsReadOnly { get; private set; }

        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="CharArrayMap{TValue}"/>.
        /// </summary>
        public virtual IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
        {
            return new EntryIterator(this, false);
        }

        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="CharArrayMap{TValue}"/>.
        /// </summary>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        [Obsolete("Not applicable in this class.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public virtual bool Remove(string key)
        {
            throw new NotSupportedException();
        }

        [Obsolete("Not applicable in this class.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public virtual bool Remove(KeyValuePair<string, TValue> item)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Gets the number of key/value pairs contained in the <see cref="CharArrayMap{TValue}"/>.
        /// </summary>
        public virtual int Count => count;

        /// <summary>
        /// Returns a string that represents the current object. (Inherited from <see cref="object"/>.)
        /// </summary>
        public override string ToString()
        {
            var sb = new StringBuilder("{");

            using (var iter1 = this.GetEnumerator())
            {
                while (iter1.MoveNext())
                {
                    KeyValuePair<string, TValue> entry = iter1.Current;
                    if (sb.Length > 1)
                    {
                        sb.Append(", ");
                    }
                    sb.Append(entry.Key);
                    sb.Append("=");
                    sb.Append(entry.Value);
                }
            }

            return sb.Append('}').ToString();
        }

        private EntrySet_ entrySet = null;
        private CharArraySet keySet = null;
        private KeyCollection originalKeySet = null;

        internal virtual EntrySet_ CreateEntrySet()
        {
            return new EntrySet_(this, true);
        }

        // LUCENENET NOTE: This MUST be a method, since there is an
        // extension method that this class needs to override the behavior of.
        public EntrySet_ EntrySet()
        {
            if (entrySet == null)
            {
                entrySet = CreateEntrySet();
            }
            return entrySet;
        }

        /// <summary>
        /// helper for <see cref="CharArraySet"/> to not produce endless recursion
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public ICollection<string> OriginalKeySet
        {
            get
            {
                if (originalKeySet == null)
                {
                    // prevent adding of entries
                    originalKeySet = new KeyCollection(this);
                }
                return originalKeySet;
            }
        }

        /// <summary>
        /// Returns an <see cref="CharArraySet"/> view on the map's keys.
        /// The set will use the same <see cref="matchVersion"/> as this map. 
        /// </summary>
        private CharArraySet KeySet
        {
            get
            {
                if (keySet == null)
                {
                    // prevent adding of entries
                    keySet = new UnmodifiableCharArraySet(this);
                }
                return keySet;
            }
        }

        private sealed class UnmodifiableCharArraySet : CharArraySet
        {
            internal UnmodifiableCharArraySet(ICharArrayMap map) 
                : base(map)
            {
            }

            public override bool Add(object o)
            {
                throw new NotSupportedException();
            }
            public override bool Add(ICharSequence text)
            {
                throw new NotSupportedException();
            }
            public override bool Add(string text)
            {
                throw new NotSupportedException();
            }
            public override bool Add(char[] text)
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// public iterator class so efficient methods are exposed to users
        /// </summary>
        public class EntryIterator : IEnumerator<KeyValuePair<string, TValue>>
        {
            private readonly CharArrayMap<TValue> outerInstance;

            internal int pos = -1;
            internal int lastPos;
            internal readonly bool allowModify;

            internal EntryIterator(CharArrayMap<TValue> outerInstance, bool allowModify)
            {
                this.outerInstance = outerInstance;
                this.allowModify = allowModify;
                GoNext();
            }

            internal void GoNext()
            {
                lastPos = pos;
                pos++;
                while (pos < outerInstance.keys.Length && outerInstance.keys[pos] == null)
                {
                    pos++;
                }
            }

            public virtual bool HasNext => pos < outerInstance.keys.Length;

            /// <summary>
            /// gets the next key... do not modify the returned char[]
            /// </summary>
            public virtual char[] NextKey()
            {
                GoNext();
                return outerInstance.keys[lastPos];
            }

            /// <summary>
            /// gets the next key as a newly created <see cref="string"/> object
            /// </summary>
            public virtual string NextKeyString()
            {
                return new string(NextKey());
            }

            /// <summary>
            /// returns the value associated with the current key
            /// </summary>
            public virtual TValue CurrentValue
            {
                get
                {
                    var val = outerInstance.values[lastPos];
                    return val != null ? val.Value : default(TValue);
                }
            }

            /// <summary>
            /// sets the value associated with the last key returned
            /// </summary>
            public virtual TValue SetValue(TValue value)
            {
                if (!allowModify)
                {
                    throw new NotSupportedException();
                }
                TValue old = outerInstance.values[lastPos].Value;
                outerInstance.values[lastPos].Value = value;
                return old;
            }

            // LUCENENET: Next() and Remove() methods eliminated here

            #region Added for better .NET support LUCENENET
            public virtual void Dispose()
            {
                // nothing to do
            }

            public virtual bool MoveNext()
            {
                if (!HasNext) return false;
                GoNext();
                return true;
            }

            public virtual void Reset()
            {
                pos = -1;
                GoNext();
            }

            public virtual KeyValuePair<string, TValue> Current
            {
                get
                {
                    var val = outerInstance.values[lastPos];
                    return new KeyValuePair<string, TValue>(
                        new string(outerInstance.keys[lastPos]), 
                        val != null ? val.Value : default(TValue));
                }
            }

            object IEnumerator.Current => Current;

            #endregion
        }

        // LUCENENET NOTE: The Java Lucene type MapEntry was removed here because it is not possible 
        // to inherit the value type KeyValuePair{TKey, TValue} in .NET.

        /// <summary>
        /// public EntrySet_ class so efficient methods are exposed to users
        /// 
        /// NOTE: In .NET this was renamed to EntrySet_ because it conflicted with the
        /// method EntrySet(). Since there is also an extension method named <see cref="T:IDictionary{K,V}.EntrySet()"/> 
        /// that this class needs to override, changing the name of the method was not
        /// possible because the extension method would produce incorrect results if it were
        /// inadvertently called, leading to hard-to-diagnose bugs.
        /// 
        /// Another difference between this set and the Java counterpart is that it implements
        /// <see cref="ICollection{T}"/> rather than <see cref="ISet{T}"/> so we don't have to implement
        /// a bunch of methods that we aren't really interested in. The <see cref="Keys"/> and <see cref="Values"/>
        /// properties both return <see cref="ICollection{T}"/>, and while there is no <see cref="EntrySet()"/> method
        /// or property in .NET, if there were it would certainly return <see cref="ICollection{T}"/>.
        /// </summary>
        public sealed class EntrySet_ : ICollection<KeyValuePair<string, TValue>>
        {
            private readonly CharArrayMap<TValue> outerInstance;

            internal readonly bool allowModify;

            internal EntrySet_(CharArrayMap<TValue> outerInstance, bool allowModify)
            {
                this.outerInstance = outerInstance;
                this.allowModify = allowModify;
            }

            public IEnumerator GetEnumerator()
            {
                return new EntryIterator(outerInstance, allowModify);
            }

            IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator()
            {
                return (IEnumerator<KeyValuePair<string, TValue>>)GetEnumerator();
            }

            public bool Contains(object o)
            {
                if (!(o is KeyValuePair<string, TValue>))
                {
                    return false;
                }
                var e = (KeyValuePair<string, TValue>)o;
                string key = e.Key;
                TValue val = e.Value;
                TValue v = outerInstance.Get(key);
                return v == null ? val == null : v.Equals(val);
            }

            [Obsolete("Not applicable in this class.")]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
            public bool Remove(KeyValuePair<string, TValue> item)
            {
                throw new NotSupportedException();
            }

            public int Count => outerInstance.count;

            public void Clear()
            {
                if (!allowModify)
                {
                    throw new NotSupportedException();
                }
                outerInstance.Clear();
            }

            #region LUCENENET Added for better .NET support

            public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
            {
                outerInstance.CopyTo(array, arrayIndex);
            }

            [Obsolete("Not applicable in this class.")]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
            public bool Contains(KeyValuePair<string, TValue> item)
            {
                return outerInstance.Contains(item);
            }

            public void Add(KeyValuePair<string, TValue> item)
            {
                outerInstance.Add(item);
            }

            public bool IsReadOnly => !allowModify;

            public override string ToString()
            {
                var sb = new StringBuilder("[");

                IEnumerator<KeyValuePair<string, TValue>> iter1 = new EntryIterator(this.outerInstance, false);
                while (iter1.MoveNext())
                {
                    KeyValuePair<string, TValue> entry = iter1.Current;
                    if (sb.Length > 1)
                    {
                        sb.Append(", ");
                    }
                    sb.Append(entry.Key);
                    sb.Append("=");
                    sb.Append(entry.Value);
                }

                return sb.Append(']').ToString();
            }
            #endregion
        }

        // LUCENENET: Moved UnmodifiableMap static methods to CharArrayMap class

        // LUCENENET: Moved Copy static methods to CharArrayMap class

        /// <summary>
        /// Returns an empty, unmodifiable map. </summary>
        public static CharArrayMap<TValue> EmptyMap()
        {
            return EMPTY_MAP;
        }

        // LUCENENET: Moved UnmodifiableCharArraymap to CharArrayMap class

        // LUCENENET: Moved EmptyCharArrayMap to CharArrayMap class
    }

    /// <summary>
    /// LUCENENET specific interface used so <see cref="CharArraySet"/>
    /// can hold a reference to <see cref="CharArrayMap{TValue}"/> without
    /// knowing its generic type.
    /// </summary>
    internal interface ICharArrayMap
    {
        void Clear();
        bool ContainsKey(char[] text, int offset, int length);
        bool ContainsKey(char[] text);
        bool ContainsKey(object o);
        bool ContainsKey(string text);
        bool ContainsKey(ICharSequence text);
        int Count { get; }
        LuceneVersion MatchVersion { get; }
        ICollection<string> OriginalKeySet { get; }
        bool Put(char[] text);
        bool Put(ICharSequence text);
        bool Put(object o);
        bool Put(string text);
    }

    public class CharArrayMap
    {
        // Prevent direct creation
        private CharArrayMap()
        { }

        /// <summary>
        /// Returns a copy of the given map as a <see cref="CharArrayMap{TValue}"/>. If the given map
        /// is a <see cref="CharArrayMap{TValue}"/> the ignoreCase property will be preserved.
        /// <para>
        /// <b>Note:</b> If you intend to create a copy of another <see cref="CharArrayMap{TValue}"/> where
        /// the <see cref="LuceneVersion"/> of the source map differs from its copy
        /// <see cref="CharArrayMap{TValue}.CharArrayMap(LuceneVersion, IDictionary{string, TValue}, bool)"/> should be used instead.
        /// The <see cref="Copy{TValue}(LuceneVersion, IDictionary{string, TValue})"/> will preserve the <see cref="LuceneVersion"/> of the
        /// source map if it is an instance of <see cref="CharArrayMap{TValue}"/>.
        /// </para>
        /// </summary>
        /// <param name="matchVersion">
        ///          compatibility match version see <a href="#version">Version
        ///          note</a> above for details. This argument will be ignored if the
        ///          given map is a <see cref="CharArrayMap{TValue}"/>. </param>
        /// <param name="map">
        ///          a map to copy </param>
        /// <returns> a copy of the given map as a <see cref="CharArrayMap{TValue}"/>. If the given map
        ///         is a <see cref="CharArrayMap{TValue}"/> the ignoreCase property as well as the
        ///         <paramref name="matchVersion"/> will be of the given map will be preserved. </returns>
        public static CharArrayMap<TValue> Copy<TValue>(LuceneVersion matchVersion, IDictionary<string, TValue> map)
        {
            if (map == CharArrayMap<TValue>.EmptyMap())
            {
                return CharArrayMap<TValue>.EmptyMap();
            }

            if (map is CharArrayMap<TValue>)
            {
                var m = map as CharArrayMap<TValue>;
                // use fast path instead of iterating all values
                // this is even on very small sets ~10 times faster than iterating
                var keys = new char[m.keys.Length][];
                Array.Copy(m.keys, 0, keys, 0, keys.Length);
                var values = new CharArrayMap<TValue>.MapValue[m.values.Length];
                Array.Copy(m.values, 0, values, 0, values.Length);
                m = new CharArrayMap<TValue>(m) { keys = keys, values = values };
                return m;
            }
            return new CharArrayMap<TValue>(matchVersion, map, false);
        }

        /// <summary>
        /// Used by <see cref="CharArraySet"/> to copy <see cref="CharArrayMap{TValue}"/> without knowing 
        /// its generic type.
        /// </summary>
        internal static CharArrayMap<TValue> Copy<TValue>(LuceneVersion matchVersion, ICharArrayMap map)
        {
            return Copy(matchVersion, map as IDictionary<string, TValue>);
        }

        /// <summary>
        /// Returns an unmodifiable <see cref="CharArrayMap{TValue}"/>. This allows to provide
        /// unmodifiable views of internal map for "read-only" use.
        /// </summary>
        /// <param name="map">
        ///          a map for which the unmodifiable map is returned. </param>
        /// <returns> an new unmodifiable <see cref="CharArrayMap{TValue}"/>. </returns>
        /// <exception cref="ArgumentException">
        ///           if the given map is <c>null</c>. </exception>
        public static CharArrayMap<TValue> UnmodifiableMap<TValue>(CharArrayMap<TValue> map) // LUCENENET TODO: API - Rename AsReadOnly() to match .NET convention
        {
            if (map == null)
            {
                throw new ArgumentException("Given map is null", "map");
            }
            if (map == CharArrayMap<TValue>.EmptyMap() || map.Count == 0)
            {
                return CharArrayMap<TValue>.EmptyMap();
            }
            if (map is CharArrayMap.UnmodifiableCharArrayMap<TValue>)
            {
                return map;
            }
            return new CharArrayMap.UnmodifiableCharArrayMap<TValue>(map);
        }

        /// <summary>
        /// Used by <see cref="CharArraySet"/> to create an <see cref="UnmodifiableCharArrayMap{TValue}"/> instance
        /// without knowing the type of <typeparamref name="TValue"/>.
        /// </summary>
        internal static ICharArrayMap UnmodifiableMap<TValue>(ICharArrayMap map)
        {
            if (map == null)
            {
                throw new ArgumentException("Given map is null", "map");
            }
            if (map == CharArrayMap<TValue>.EmptyMap() || map.Count == 0)
            {
                return CharArrayMap<TValue>.EmptyMap();
            }
            if (map is CharArrayMap.UnmodifiableCharArrayMap<TValue>)
            {
                return map;
            }
            return new CharArrayMap.UnmodifiableCharArrayMap<TValue>(map);
        }

        // package private CharArraySet instanceof check in CharArraySet
        internal class UnmodifiableCharArrayMap<TValue> : CharArrayMap<TValue>
        {
            public UnmodifiableCharArrayMap(CharArrayMap<TValue> map)
                : base(map)
            { }

            public UnmodifiableCharArrayMap(ICharArrayMap map)
                : base(map as CharArrayMap<TValue>)
            { }

            public override void Clear()
            {
                throw new NotSupportedException();
            }

            public override TValue Put(char[] text, TValue val)
            {
                throw new NotSupportedException();
            }

            public override TValue Put(ICharSequence text, TValue val)
            {
                throw new NotSupportedException();
            }

            public override TValue Put(string text, TValue val)
            {
                throw new NotSupportedException();
            }

            public override TValue Put(object o, TValue val)
            {
                throw new NotSupportedException();
            }

            public override bool Put(char[] text)
            {
                throw new NotSupportedException();
            }

            public override bool Put(ICharSequence text)
            {
                throw new NotSupportedException();
            }

            public override bool Put(string text)
            {
                throw new NotSupportedException();
            }

            public override bool Put(object o)
            {
                throw new NotSupportedException();
            }

            [Obsolete("Not applicable in this class.")]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
            public override bool Remove(string key)
            {
                throw new NotSupportedException();
            }

            internal override EntrySet_ CreateEntrySet()
            {
                return new EntrySet_(this, false);
            }

            #region Added for better .NET support LUCENENET
            public override void Add(string key, TValue value)
            {
                throw new NotSupportedException();
            }
            public override void Add(KeyValuePair<string, TValue> item)
            {
                throw new NotSupportedException();
            }
            public override TValue this[char[] key, int offset, int length]
            {
                get => base[key, offset, length];
                set => throw new NotSupportedException();
            }
            public override TValue this[char[] key]
            {
                get => base[key];
                set => throw new NotSupportedException();
            }
            public override TValue this[ICharSequence key]
            {
                get => base[key];
                set => throw new NotSupportedException();
            }
            public override TValue this[string key]
            {
                get => base[key];
                set => throw new NotSupportedException();
            }
            public override TValue this[object key]
            {
                get => base[key];
                set => throw new NotSupportedException();
            }

            [Obsolete("Not applicable in this class.")]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
            public override bool Remove(KeyValuePair<string, TValue> item)
            {
                throw new NotSupportedException();
            }
            #endregion
        }

        /// <summary>
        /// Empty <see cref="UnmodifiableCharArrayMap{V}"/> optimized for speed.
        /// Contains checks will always return <c>false</c> or throw
        /// NPE if necessary.
        /// </summary>
        internal class EmptyCharArrayMap<V> : UnmodifiableCharArrayMap<V>
        {
            public EmptyCharArrayMap()
#pragma warning disable 612, 618
                : base(new CharArrayMap<V>(LuceneVersion.LUCENE_CURRENT, 0, false))
#pragma warning restore 612, 618
            {
            }

            public override bool ContainsKey(char[] text, int offset, int length)
            {
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                return false;
            }

            public override bool ContainsKey(char[] text)
            {
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                return false;
            }

            public override bool ContainsKey(ICharSequence text)
            {
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                return false;
            }

            public override bool ContainsKey(object o)
            {
                if (o == null)
                {
                    throw new ArgumentNullException("o");
                }
                return false;
            }

            public override V Get(char[] text, int offset, int length)
            {
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                return default(V);
            }

            public override V Get(char[] text)
            {
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                return default(V);
            }

            public override V Get(ICharSequence text)
            {
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                return default(V);
            }

            public override V Get(object o)
            {
                if (o == null)
                {
                    throw new ArgumentNullException("o");
                }
                return default(V);
            }
        }
    }

    /// <summary>
    /// LUCENENET specific extension methods for CharArrayMap
    /// </summary>
    public static class CharArrayMapExtensions
    {
        #region ContainsKey

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="bool"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, bool key)
        {
            return map.ContainsKey(key.ToString());
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="byte"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, byte key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="char"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, char key)
        {
            return map.ContainsKey("" + key);
        }

        ///// <summary>
        ///// <c>true</c> if the <paramref name="key"/> <see cref="decimal"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        ///// otherwise <c>false</c>
        ///// </summary>
        //public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, decimal key)
        //{
        //    return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        //}

        ///// <summary>
        ///// <c>true</c> if the <paramref name="key"/> <see cref="double"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        ///// otherwise <c>false</c>
        ///// </summary>
        //public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, double key)
        //{
        //    return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        //}

        ///// <summary>
        ///// <c>true</c> if the <paramref name="key"/> <see cref="float"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        ///// otherwise <c>false</c>
        ///// </summary>
        //public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, float key)
        //{
        //    return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        //}

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="int"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, int key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="long"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, long key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="sbyte"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        [CLSCompliant(false)]
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, sbyte key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="short"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, short key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="uint"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        [CLSCompliant(false)]
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, uint key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="ulong"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        [CLSCompliant(false)]
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, ulong key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// <c>true</c> if the <paramref name="key"/> <see cref="ushort"/> is in the <see cref="CharArrayMap{TValue}.KeySet"/>; 
        /// otherwise <c>false</c>
        /// </summary>
        [CLSCompliant(false)]
        public static bool ContainsKey<TValue>(this CharArrayMap<TValue> map, ushort key)
        {
            return map.ContainsKey(key.ToString(CultureInfo.InvariantCulture));
        }

        #endregion

        #region Get

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, bool text)
        {
            return map.Get(text.ToString());
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, byte text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, char text)
        {
            return map.Get("" + text);
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, decimal text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, double text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, float text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, int text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, long text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, sbyte text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, short text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, uint text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, ulong text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        /// <summary>
        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Get<TValue>(this CharArrayMap<TValue> map, ushort text)
        {
            return map.Get(text.ToString(CultureInfo.InvariantCulture));
        }

        #endregion

        #region Put

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, bool text, TValue value)
        {
            return map.Put(text.ToString(), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, byte text, TValue value)
        {
            return map.Put(text.ToString(), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, char text, TValue value)
        {
            return map.Put(text.ToString(), value);
        }

        ///// <summary>
        ///// Add the given mapping.
        ///// </summary>
        //public static TValue Put<TValue>(this CharArrayMap<TValue> map, decimal text, TValue value)
        //{
        //    return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        //}

        ///// <summary>
        ///// Add the given mapping.
        ///// </summary>
        //public static TValue Put<TValue>(this CharArrayMap<TValue> map, double text, TValue value)
        //{
        //    return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        //}

        ///// <summary>
        ///// Add the given mapping.
        ///// </summary>
        //public static TValue Put<TValue>(this CharArrayMap<TValue> map, float text, TValue value)
        //{
        //    return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        //}

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, int text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, long text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, sbyte text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, short text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, uint text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, ulong text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        /// <summary>
        /// Add the given mapping.
        /// </summary>
        [CLSCompliant(false)]
        public static TValue Put<TValue>(this CharArrayMap<TValue> map, ushort text, TValue value)
        {
            return map.Put(text.ToString(CultureInfo.InvariantCulture), value);
        }

        #endregion

        #region PutAll

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<bool, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<byte, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<char, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put("" + kvp.Key, kvp.Value);
            }
        }

        ///// <summary>
        ///// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        ///// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        //public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<decimal, TValue> dictionary)
        //{
        //    foreach (var kvp in dictionary)
        //    {
        //        map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
        //    }
        //}

        ///// <summary>
        ///// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        ///// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        //public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<double, TValue> dictionary)
        //{
        //    foreach (var kvp in dictionary)
        //    {
        //        map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
        //    }
        //}

        ///// <summary>
        ///// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        ///// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        //public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<float, TValue> dictionary)
        //{
        //    foreach (var kvp in dictionary)
        //    {
        //        map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
        //    }
        //}

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<int, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<long, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<sbyte, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<short, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<uint, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<ulong, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="dictionary"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="dictionary">A dictionary of values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IDictionary<ushort, TValue> dictionary)
        {
            foreach (var kvp in dictionary)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }


        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<bool, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<byte, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<char, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put("" + kvp.Key, kvp.Value);
            }
        }

        ///// <summary>
        ///// This implementation enumerates over the specified <paramref name="collection"/>'s
        ///// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="collection">The values to add/update in the current map.</param>
        //public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<decimal, TValue>> collection)
        //{
        //    foreach (var kvp in collection)
        //    {
        //        map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
        //    }
        //}

        ///// <summary>
        ///// This implementation enumerates over the specified <paramref name="collection"/>'s
        ///// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="collection">The values to add/update in the current map.</param>
        //public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<double, TValue>> collection)
        //{
        //    foreach (var kvp in collection)
        //    {
        //        map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
        //    }
        //}

        ///// <summary>
        ///// This implementation enumerates over the specified <paramref name="collection"/>'s
        ///// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="collection">The values to add/update in the current map.</param>
        //public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<float, TValue>> collection)
        //{
        //    foreach (var kvp in collection)
        //    {
        //        map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
        //    }
        //}

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<int, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<long, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<sbyte, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<short, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<uint, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<ulong, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        /// <summary>
        /// This implementation enumerates over the specified <paramref name="collection"/>'s
        /// entries, and calls this map's <see cref="CharArrayMap{TValue}.Put(string, TValue)"/> operation once for each entry.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="collection">The values to add/update in the current map.</param>
        [CLSCompliant(false)]
        public static void PutAll<TValue>(this CharArrayMap<TValue> map, IEnumerable<KeyValuePair<ushort, TValue>> collection)
        {
            foreach (var kvp in collection)
            {
                map.Put(kvp.Key.ToString(CultureInfo.InvariantCulture), kvp.Value);
            }
        }

        #endregion

        #region TryGetValue

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, bool key, out TValue value)
        {
            return map.TryGetValue(key.ToString(), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, byte key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, char key, out TValue value)
        {
            return map.TryGetValue("" + key, out value);
        }

        ///// <summary>
        ///// Gets the value associated with the specified key.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="key">The key of the value to get.</param>
        ///// <param name="value">When this method returns, contains the value associated with the specified key, 
        ///// if the key is found; otherwise, the default value for the type of the value parameter. 
        ///// This parameter is passed uninitialized.</param>
        ///// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        //public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, decimal key, out TValue value)
        //{
        //    return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        //}

        ///// <summary>
        ///// Gets the value associated with the specified key.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="key">The key of the value to get.</param>
        ///// <param name="value">When this method returns, contains the value associated with the specified key, 
        ///// if the key is found; otherwise, the default value for the type of the value parameter. 
        ///// This parameter is passed uninitialized.</param>
        ///// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        //public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, double key, out TValue value)
        //{
        //    return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        //}

        ///// <summary>
        ///// Gets the value associated with the specified key.
        ///// </summary>
        ///// <param name="map">this map</param>
        ///// <param name="key">The key of the value to get.</param>
        ///// <param name="value">When this method returns, contains the value associated with the specified key, 
        ///// if the key is found; otherwise, the default value for the type of the value parameter. 
        ///// This parameter is passed uninitialized.</param>
        ///// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        //public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, float key, out TValue value)
        //{
        //    return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        //}

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, int key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, long key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        [CLSCompliant(false)]
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, sbyte key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, short key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        [CLSCompliant(false)]
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, uint key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        [CLSCompliant(false)]
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, ulong key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="map">this map</param>
        /// <param name="key">The key of the value to get.</param>
        /// <param name="value">When this method returns, contains the value associated with the specified key, 
        /// if the key is found; otherwise, the default value for the type of the value parameter. 
        /// This parameter is passed uninitialized.</param>
        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
        [CLSCompliant(false)]
        public static bool TryGetValue<TValue>(this CharArrayMap<TValue> map, ushort key, out TValue value)
        {
            return map.TryGetValue(key.ToString(CultureInfo.InvariantCulture), out value);
        }

#endregion
    }
}