diff --git a/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs b/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs index 193c172209..23d53ba1c6 100644 --- a/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs +++ b/src/EntityFramework/Core/Query/InternalTrees/VarMap.cs @@ -2,7 +2,6 @@ namespace System.Data.Entity.Core.Query.InternalTrees { - using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; @@ -10,21 +9,25 @@ namespace System.Data.Entity.Core.Query.InternalTrees // // Helps map one variable to the next. // - internal class VarMap : IDictionary + internal class VarMap : Dictionary { #region public surfaces - private Dictionary map; - private Dictionary reverseMap; - internal VarMap GetReverseMap() { - return new VarMap(reverseMap, map); - } - - public bool ContainsValue(Var value) - { - return reverseMap.ContainsKey(value); + var reverseMap = new VarMap(); + foreach (var kv in this) + { + Var x; + // On the odd chance that a var is in the varMap more than once, the first one + // is going to be the one we want to use, because it might be the discriminator + // var; + if (!reverseMap.TryGetValue(kv.Value, out x)) + { + reverseMap[kv.Value] = kv.Key; + } + } + return reverseMap; } public override string ToString() @@ -32,7 +35,7 @@ public override string ToString() var sb = new StringBuilder(); var separator = string.Empty; - foreach (var v in map.Keys) + foreach (var v in Keys) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", separator, v.Id, this[v].Id); separator = ","; @@ -42,134 +45,8 @@ public override string ToString() #endregion - #region IDictionary - - public Var this[Var key] - { - get - { - return map[key]; - } - set - { - map[key] = value; - } - } - - public ICollection Keys - { - get - { - return map.Keys; - } - } - - public ICollection Values - { - get - { - return map.Values; - } - } - - public int Count - { - get - { - return map.Count; - } - } - - public bool IsReadOnly - { - get - { - return false; - } - } - - public void Add(Var key, Var value) - { - if (!reverseMap.ContainsKey(value)) - { - reverseMap.Add(value, key); - } - map.Add(key, value); - } - - public void Add(KeyValuePair item) - { - if (!reverseMap.ContainsKey(item.Value)) - { - ((IDictionary)reverseMap).Add(new KeyValuePair(item.Value, item.Key)); - } - ((IDictionary)map).Add(item); - } - - public void Clear() - { - map.Clear(); - reverseMap.Clear(); - } - - public bool Contains(KeyValuePair item) - { - return ((IDictionary)map).Contains(item); - } - - public bool ContainsKey(Var key) - { - return map.ContainsKey(key); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ((IDictionary)map).CopyTo(array, arrayIndex); - } - - public IEnumerator> GetEnumerator() - { - return map.GetEnumerator(); - } - - public bool Remove(Var key) - { - reverseMap.Remove(map[key]); - return map.Remove(key); - } - - public bool Remove(KeyValuePair item) - { - reverseMap.Remove(map[item.Value]); - return ((IDictionary)map).Remove(item); - } - - public bool TryGetValue(Var key, out Var value) - { - return ((IDictionary)map).TryGetValue(key, out value); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return map.GetEnumerator(); - } - - #endregion - #region constructors - public VarMap() - { - map = new Dictionary(); - reverseMap = new Dictionary(); - } - - private VarMap(Dictionary map, Dictionary reverseMap) - { - this.map = map; - this.reverseMap = reverseMap; - } - #endregion } } diff --git a/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs b/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs index 07a9d2c97a..427341c5d6 100644 --- a/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs +++ b/src/EntityFramework/Core/Query/InternalTrees/VarVec.cs @@ -3,7 +3,6 @@ namespace System.Data.Entity.Core.Query.InternalTrees { using System.Collections; - using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Text; @@ -29,7 +28,7 @@ internal class VarVecEnumerator : IEnumerator, IDisposable private int m_position; private Command m_command; - private BitVec m_bitArray; + private BitArray m_bitArray; #endregion @@ -78,54 +77,19 @@ object IEnumerator.Current get { return Current; } } - static readonly int[] MultiplyDeBruijnBitPosition = - { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - // // Move to the next position // public bool MoveNext() { - int[] values = m_bitArray.m_array; m_position++; - int length = m_bitArray.Length; - int valuesLen = BitVec.GetArrayLength(length, 32); - int i = m_position / 32; - int v = 0, mask = 0; - - if (i < valuesLen) + for (; m_position < m_bitArray.Length; m_position++) { - - v = values[i]; - // zero lowest bits that are skipped - mask = (~0 << (m_position % 32)); - - v &= mask; - - if (v != 0) + if (m_bitArray[m_position]) { - m_position = (i * 32) + MultiplyDeBruijnBitPosition[((uint)((v & -v) * 0x077CB531U)) >> 27]; - return true; - } - - i++; - for (; i < valuesLen; i++) - { - v = values[i]; - - if (v == 0) - { - continue; - } - - m_position = (i * 32) + MultiplyDeBruijnBitPosition[((uint)((v & -v) * 0x077CB531U)) >> 27]; return true; } } - m_position = length; return false; } @@ -210,26 +174,10 @@ internal bool Overlaps(VarVec other) // internal bool Subsumes(VarVec other) { - int[] values = m_bitVector.m_array; - int[] otherValues = other.m_bitVector.m_array; - - // if the other is longer, and it has a bit set past the current vector's length return false - if (otherValues.Length > values.Length) + for (var i = 0; i < other.m_bitVector.Length; i++) { - for (var i = values.Length; i < otherValues.Length; i++) - { - if (otherValues[i] != 0) - { - return false; - } - } - } - - int length = Math.Min(otherValues.Length, values.Length); - - for (var i = 0; i < length; i++) - { - if (!((values[i] & otherValues[i]) == otherValues[i])) + if (other.m_bitVector[i] + && ((i >= m_bitVector.Length) || !m_bitVector[i])) { return false; } @@ -339,7 +287,7 @@ internal Var First // // dictionary of renamed vars // a new VarVec - internal VarVec Remap(IDictionary varMap) + internal VarVec Remap(Dictionary varMap) { var newVec = m_command.CreateVarVec(); foreach (var v in this) @@ -360,7 +308,7 @@ internal VarVec Remap(IDictionary varMap) internal VarVec(Command command) { - m_bitVector = new BitVec(64); + m_bitVector = new BitArray(64); m_command = command; } @@ -413,7 +361,7 @@ public override string ToString() #region private state - private readonly BitVec m_bitVector; + private readonly BitArray m_bitVector; private readonly Command m_command; #endregion @@ -432,465 +380,4 @@ public VarVec Clone() #endregion } - - internal class BitVec - { - private BitVec() - { - } - - /*========================================================================= - ** Allocates space to hold length bit values. All of the values in the bit - ** array are set to false. - ** - ** Exceptions: ArgumentException if length < 0. - =========================================================================*/ - public BitVec(int length) - : this(length, false) - { - } - - /*========================================================================= - ** Allocates space to hold length bit values. All of the values in the bit - ** array are set to defaultValue. - ** - ** Exceptions: ArgumentOutOfRangeException if length < 0. - =========================================================================*/ - public BitVec(int length, bool defaultValue) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NeedNonNegNum"); - } - - m_array = ArrayPool.Instance.GetArray(GetArrayLength(length, BitsPerInt32)); - m_length = length; - - int fillValue = defaultValue ? unchecked(((int)0xffffffff)) : 0; - for (int i = 0; i < m_array.Length; i++) - { - m_array[i] = fillValue; - } - - _version = 0; - } - - /*========================================================================= - ** Allocates space to hold the bit values in bytes. bytes[0] represents - ** bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte - ** represents the lowest index value; bytes[0] & 1 represents bit 0, - ** bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc. - ** - ** Exceptions: ArgumentException if bytes == null. - =========================================================================*/ - public BitVec(byte[] bytes) - { - if (bytes == null) - { - throw new ArgumentNullException("bytes"); - } - - // this value is chosen to prevent overflow when computing m_length. - // m_length is of type int32 and is exposed as a property, so - // type of m_length can't be changed to accommodate. - if (bytes.Length > Int32.MaxValue / BitsPerByte) - { - throw new ArgumentException("Argument_ArrayTooLarge", "bytes"); - } - - m_array = ArrayPool.Instance.GetArray(GetArrayLength(bytes.Length, BytesPerInt32)); - m_length = bytes.Length * BitsPerByte; - - int i = 0; - int j = 0; - while (bytes.Length - j >= 4) - { - m_array[i++] = (bytes[j] & 0xff) | - ((bytes[j + 1] & 0xff) << 8) | - ((bytes[j + 2] & 0xff) << 16) | - ((bytes[j + 3] & 0xff) << 24); - j += 4; - } - - switch (bytes.Length - j) - { - case 3: - m_array[i] = ((bytes[j + 2] & 0xff) << 16); - goto case 2; - // fall through - case 2: - m_array[i] |= ((bytes[j + 1] & 0xff) << 8); - goto case 1; - // fall through - case 1: - m_array[i] |= (bytes[j] & 0xff); - break; - } - - _version = 0; - } - - public BitVec(bool[] values) - { - if (values == null) - { - throw new ArgumentNullException("values"); - } - - m_array = ArrayPool.Instance.GetArray(GetArrayLength(values.Length, BitsPerInt32)); - m_length = values.Length; - - for (int i = 0; i < values.Length; i++) - { - if (values[i]) - m_array[i / 32] |= (1 << (i % 32)); - } - - _version = 0; - - } - - /*========================================================================= - ** Allocates space to hold the bit values in values. values[0] represents - ** bits 0 - 31, values[1] represents bits 32 - 63, etc. The LSB of each - ** integer represents the lowest index value; values[0] & 1 represents bit - ** 0, values[0] & 2 represents bit 1, values[0] & 4 represents bit 2, etc. - ** - ** Exceptions: ArgumentException if values == null. - =========================================================================*/ - public BitVec(int[] values) - { - if (values == null) - { - throw new ArgumentNullException("values"); - } - - // this value is chosen to prevent overflow when computing m_length - if (values.Length > Int32.MaxValue / BitsPerInt32) - { - //throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", BitsPerInt32), "values"); - } - - m_array = ArrayPool.Instance.GetArray(values.Length); - m_length = values.Length * BitsPerInt32; - - Array.Copy(values, m_array, values.Length); - - _version = 0; - } - - /*========================================================================= - ** Allocates a new BitVec with the same length and bit values as bits. - ** - ** Exceptions: ArgumentException if bits == null. - =========================================================================*/ - public BitVec(BitVec bits) - { - if (bits == null) - { - throw new ArgumentNullException("bits"); - } - - int arrayLength = GetArrayLength(bits.m_length, BitsPerInt32); - m_array = ArrayPool.Instance.GetArray(arrayLength); - m_length = bits.m_length; - - Array.Copy(bits.m_array, m_array, arrayLength); - - _version = bits._version; - } - - public bool this[int index] - { - get - { - return Get(index); - } - set - { - Set(index, value); - } - } - - /*========================================================================= - ** Returns the bit value at position index. - ** - ** Exceptions: ArgumentOutOfRangeException if index < 0 or - ** index >= GetLength(). - =========================================================================*/ - public bool Get(int index) - { - if (index < 0 || index >= Length) - { - throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); - } - - return (m_array[index / 32] & (1 << (index % 32))) != 0; - } - - /*========================================================================= - ** Sets the bit value at position index to value. - ** - ** Exceptions: ArgumentOutOfRangeException if index < 0 or - ** index >= GetLength(). - =========================================================================*/ - public void Set(int index, bool value) - { - if (index < 0 || index >= Length) - { - throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); - } - - if (value) - { - m_array[index / 32] |= (1 << (index % 32)); - } - else - { - m_array[index / 32] &= ~(1 << (index % 32)); - } - - _version++; - } - - /*========================================================================= - ** Sets all the bit values to value. - =========================================================================*/ - public void SetAll(bool value) - { - int fillValue = value ? unchecked(((int)0xffffffff)) : 0; - int ints = GetArrayLength(m_length, BitsPerInt32); - for (int i = 0; i < ints; i++) - { - m_array[i] = fillValue; - } - - _version++; - } - - /*========================================================================= - ** Returns a reference to the current instance ANDed with value. - ** - ** Exceptions: ArgumentException if value == null or - ** value.Length != this.Length. - =========================================================================*/ - public BitVec And(BitVec value) - { - if (value == null) - throw new ArgumentNullException("value"); - if (Length != value.Length) - throw new ArgumentException("Arg_ArrayLengthsDiffer"); - - int ints = GetArrayLength(m_length, BitsPerInt32); - for (int i = 0; i < ints; i++) - { - m_array[i] &= value.m_array[i]; - } - - _version++; - return this; - } - - /*========================================================================= - ** Returns a reference to the current instance ORed with value. - ** - ** Exceptions: ArgumentException if value == null or - ** value.Length != this.Length. - =========================================================================*/ - public BitVec Or(BitVec value) - { - if (value == null) - throw new ArgumentNullException("value"); - if (Length != value.Length) - throw new ArgumentException("Arg_ArrayLengthsDiffer"); - - int ints = GetArrayLength(m_length, BitsPerInt32); - for (int i = 0; i < ints; i++) - { - m_array[i] |= value.m_array[i]; - } - - _version++; - return this; - } - - /*========================================================================= - ** Returns a reference to the current instance XORed with value. - ** - ** Exceptions: ArgumentException if value == null or - ** value.Length != this.Length. - =========================================================================*/ - public BitVec Xor(BitVec value) - { - if (value == null) - throw new ArgumentNullException("value"); - if (Length != value.Length) - throw new ArgumentException("Arg_ArrayLengthsDiffer"); - - int ints = GetArrayLength(m_length, BitsPerInt32); - for (int i = 0; i < ints; i++) - { - m_array[i] ^= value.m_array[i]; - } - - _version++; - return this; - } - - /*========================================================================= - ** Inverts all the bit values. On/true bit values are converted to - ** off/false. Off/false bit values are turned on/true. The current instance - ** is updated and returned. - =========================================================================*/ - public BitVec Not() - { - int ints = GetArrayLength(m_length, BitsPerInt32); - for (int i = 0; i < ints; i++) - { - m_array[i] = ~m_array[i]; - } - - _version++; - return this; - } - - public int Length - { - get - { - return m_length; - } - set - { - if (value < 0) - { - throw new ArgumentOutOfRangeException("value", "ArgumentOutOfRange_NeedNonNegNum"); - } - - int newints = GetArraySize(value, BitsPerInt32); - if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length) - { - // grow or shrink (if wasting more than _ShrinkThreshold ints) - int[] newarray = ArrayPool.Instance.GetArray(newints); //new int[newints]; - Array.Copy(m_array, newarray, newints > m_array.Length ? m_array.Length : newints); - ArrayPool.Instance.PutArray(m_array); - m_array = newarray; - } - - if (value > m_length) - { - // clear high bit values in the last int - int last = GetArrayLength(m_length, BitsPerInt32) - 1; - int bits = m_length % 32; - if (bits > 0) - { - m_array[last] &= (1 << bits) - 1; - } - - // clear remaining int values - Array.Clear(m_array, last + 1, newints - last - 1); - } - - m_length = value; - _version++; - } - } - - // XPerY=n means that n Xs can be stored in 1 Y. - private const int BitsPerInt32 = 32; - private const int BytesPerInt32 = 4; - private const int BitsPerByte = 8; - - /// - /// Used for conversion between different representations of bit array. - /// Returns (n+(div-1))/div, rearranged to avoid arithmetic overflow. - /// For example, in the bit to int case, the straightforward calc would - /// be (n+31)/32, but that would cause overflow. So instead it's - /// rearranged to ((n-1)/32) + 1, with special casing for 0. - /// - /// Usage: - /// GetArrayLength(77, BitsPerInt32): returns how many ints must be - /// allocated to store 77 bits. - /// - /// length of array - /// use a conversion constant, e.g. BytesPerInt32 to get - /// how many ints are required to store n bytes - /// length of the array - public static int GetArrayLength(int n, int div) - { - return n > 0 ? (((n - 1) / div) + 1) : 0; - } - - private static int GetArraySize(int n, int div) - { - // compute the next highest power of 2 of 32-bit v - uint v = Convert.ToUInt32(GetArrayLength(n, div)); - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - - return Convert.ToInt32(v); - } - - public int[] m_array; - private int m_length; - private int _version; - private const int _ShrinkThreshold = 1024; //256; - - private class ArrayPool - { - private Dictionary> dictionary; - - private ArrayPool() - { - dictionary = new Dictionary>(); - } - - private static readonly ArrayPool instance = new ArrayPool(); - - public static ArrayPool Instance - { - get - { - return instance; - } - } - - public int[] GetArray(int length) - { - ConcurrentBag arrays = GetBag(length); - - int[] arr; - if (arrays.TryTake(out arr)) return arr; - - return new int[length]; - } - - private ConcurrentBag GetBag(int length) - { - ConcurrentBag arrays; - if (!dictionary.ContainsKey(length)) - { - arrays = new ConcurrentBag(); - dictionary[length] = arrays; - } - else - { - arrays = dictionary[length]; - } - return arrays; - } - - public void PutArray(int[] arr) - { - ConcurrentBag arrays = GetBag(arr.Length); - Array.Clear(arr, 0, arr.Length); - arrays.Add(arr); - } - } - } } diff --git a/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs b/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs index 17d80f1214..e368f43b4c 100644 --- a/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs +++ b/src/EntityFramework/Core/Query/PlanCompiler/ColumnMapTranslator.cs @@ -49,7 +49,7 @@ private ColumnMapTranslator() // replacement. Note that we will follow the chain of replacements, in // case the replacement was also replaced. // - private static Var GetReplacementVar(Var originalVar, IDictionary replacementVarMap) + private static Var GetReplacementVar(Var originalVar, Dictionary replacementVarMap) { // SQLBUDT #478509: Follow the chain of mapped vars, don't // just stop at the first one @@ -124,7 +124,7 @@ internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary // Replace VarRefColumnMaps with new VarRefColumnMaps with the specified Var // - internal static ColumnMap Translate(ColumnMap columnMapToTranslate, IDictionary varToVarMap) + internal static ColumnMap Translate(ColumnMap columnMapToTranslate, Dictionary varToVarMap) { var result = Translate( columnMapToTranslate, diff --git a/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs b/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs index 541114bb5a..fc7b9b92b4 100644 --- a/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs +++ b/src/EntityFramework/Core/Query/PlanCompiler/ITreeGenerator.cs @@ -163,7 +163,7 @@ internal override bool IsPredicate(string name) [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpCopier")] [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Assert(System.Boolean,System.String)")] - private void MapCopiedNodeVars(IList sources, IList copies, IDictionary varMappings) + private void MapCopiedNodeVars(IList sources, IList copies, Dictionary varMappings) { PlanCompiler.Assert(sources.Count == copies.Count, "Source/Copy Node count mismatch"); diff --git a/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs b/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs index cc8a06b0e2..3b70a34509 100644 --- a/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs +++ b/src/EntityFramework/Core/Query/PlanCompiler/NestPullup.cs @@ -2533,7 +2533,7 @@ private Node BuildUnionAllSubqueryForNestOp( private static VarList GetUnionOutputs(UnionAllOp unionOp, VarList leftVars) { var varMap = unionOp.VarMap[0]; - IDictionary reverseVarMap = varMap.GetReverseMap(); + Dictionary reverseVarMap = varMap.GetReverseMap(); var unionAllVars = Command.CreateVarList(); foreach (var v in leftVars) diff --git a/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs b/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs index d30bbc32c8..d8a423e55d 100644 --- a/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs +++ b/src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs @@ -57,7 +57,7 @@ internal class PlanCompiler // Determines the maximum size of the query in terms of Iqt nodes for which we attempt to do transformation rules. // This number is ignored if applyTransformationsRegardlessOfSize is enabled. // - private const int MaxNodeCountForTransformations = 10000; + private const int MaxNodeCountForTransformations = 100000; // // The CTree we're compiling a plan for. diff --git a/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs b/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs index c590a50ef6..019256dc7d 100644 --- a/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs +++ b/src/EntityFramework/Core/Query/PlanCompiler/VarRemapper.cs @@ -13,7 +13,7 @@ internal class VarRemapper : BasicOpVisitor { #region Private state - private readonly IDictionary m_varMap; + private readonly Dictionary m_varMap; protected readonly Command m_command; #endregion @@ -34,7 +34,7 @@ internal VarRemapper(Command command) // // Current iqt command // Var map to be used - internal VarRemapper(Command command, IDictionary varMap) + internal VarRemapper(Command command, Dictionary varMap) { m_command = command; m_varMap = varMap; @@ -102,7 +102,7 @@ internal VarList RemapVarList(VarList varList) // // Remap the given varList using the given varMap // - internal static VarList RemapVarList(Command command, IDictionary varMap, VarList varList) + internal static VarList RemapVarList(Command command, Dictionary varMap, VarList varList) { var varRemapper = new VarRemapper(command, varMap); return varRemapper.RemapVarList(varList); diff --git a/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs b/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs index a80a5a1690..b27a7fd757 100644 --- a/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs +++ b/test/EntityFramework/UnitTests/Core/Query/InternalTrees/VarVecTests.cs @@ -11,8 +11,7 @@ public class VarVecTests [Fact] public void MoveNext_returns_true_for_true_bits_and_false_when_end_is_reached() { - var enumerator = new VarVec.VarVecEnumerator(CreateVarVec(1, 0, 1)); - Assert.True(enumerator.MoveNext()); + var enumerator = new VarVec.VarVecEnumerator(CreateVarVec(1)); Assert.True(enumerator.MoveNext()); Assert.False(enumerator.MoveNext()); }