/// <summary>
        /// Combines vectors into their product vector.
        /// for example: [ v1, v2 ] => v3: {Value:v1 * v2, Index:[v1.Index, v2.Index]
        /// </summary>
        /// <param name="vectors">vectors to combine</param>
        /// <param name="vectorSize">the size of the passed vectors</param>
        /// <returns>combined vector</returns>
        public static IndexedVector Combine(IEnumerable <IndexedVector> vectors, int vectorSize)
        {
            var result = new IndexedVector
            {
                Index = Enumerable.Empty <int>().ToHashSet(),
                Value = Vector.One(vectorSize)
            };

            foreach (var vector in vectors)
            {
                result.Value = result.Value.Multiply(vector.Value);
                result.Index.UnionWith(vector.Index);
            }
            return(result);
        }
        /// <summary>
        /// Gets the 'characteristic' vectors for a given matrix row(IndexedVector).
        /// </summary>
        /// <param name="vector">row to get the vectors for</param>
        /// <returns>characteristic vectors</returns>
        public IList <Vector> GetCharacteristicVectorsFor(IndexedVector vector)
        {
            var contains = _characteristicVectors.TryGetValue(vector.Index, out var result);

            if (contains)
            {
                return(result);
            }
            var indices            = vector.GetCharacteristicVectorIndices(M);
            var indicesWithInverse = indices
                                     //get index and the 'inverse' of the index
                                     .SelectMany(index => new[] { -index, index })
                                     .ToList();

            var combinations = indicesWithInverse
                               //get all combinations of the indices in indices.Count means.
                               .GetCombinations(indices.Count)
                               //convert to hashset
                               .Select(index => index.ToHashSet())
                               //skip indexes like {...,x, ..., -x, ...}
                               .Where(index => index.All(part => !index.Contains(-part)))
                               .ToList();

            var characteristics = new List <Vector>(combinations.Count);

            foreach (var combination in combinations)
            {
                var characteristicVector = Vector.One(VectorSize);
                foreach (var index in combination)
                {
                    //Get either the inverse of the vector (complement), or the vector itself
                    bool isInverse = index < 0;
                    var  row       = this[new HashSet <int> {
                                              Math.Abs(index)
                                          }].Value;
                    var toMultiply = isInverse ? row.Complement() : row;

                    characteristicVector = characteristicVector.Multiply(toMultiply);
                }
                characteristics.Add(characteristicVector);
            }

            _characteristicVectors.Add(vector.Index, characteristics);

            return(characteristics);
        }
        /// <summary>
        /// Generates the matrix rows using R and M parameters.
        /// Stores the result in _rows;
        /// </summary>
        private void Generate()
        {
            var baseVectors = GetBaseVectors().ToList();

            // if R <= 1, then there is no need to generate the products from vector combinations
            if (R <= 1)
            {
                Rows = baseVectors;
                return;
            }
            //get all possible combinations from the base vectors in collections of size 2 - R.
            //skip the first 'base' vector, as it's equivalent to 1, and when multiplying by it later, the resulting vector is simply itself.
            var combinations = baseVectors.Skip(1).GetCombinations(2, R);

            var indexedCombinedVectors = combinations
                                         .Select(combination => IndexedVector.Combine(combination, VectorSize));

            Rows = baseVectors
                   .Concat(indexedCombinedVectors)
                   .ToList();
        }