// perhaps something like this
        // e.g. FormulaValueSource would have all the formulas
        //public ExecutionEngine AddValueSource(IValueSource valueSource)
        //{
        //}

        public ExecutionContext Execute(Guid formulaId, IEnumerable <ArgumentValue> argumentValues, IContextLogger logger)
        {
            Guard.AgainstNull(argumentValues, nameof(argumentValues));
            Guard.AgainstNull(logger, nameof(logger));

            Initialize();

            var context = new ExecutionContext(argumentValues, logger);

            foreach (var argumentValue in argumentValues)
            {
                var argument = GetArgument(argumentValue.Id);

                if (logger.LogLevel == ContextLogLevel.Verbose)
                {
                    logger.LogVerbose($"[inputParameter] {argumentValue.Id} / {argument.Name} = '{argumentValue.Value}'");
                }
            }

            try
            {
                Execute(context, formulaId, logger);
            }
            catch (Exception ex)
            {
                context.WithException(ex);
            }

            return(context);
        }
        private FormulaContext Execute(ExecutionContext executionContext, Guid formulaId, IContextLogger logger)
        {
            var formula = GetFormula(formulaId);

            executionContext.CyclicInvariant(formula.Name);

            using (var formulaContext = executionContext.FormulaContext(formula.Name))
            {
                foreach (var constraint in formula.Constraints)
                {
                    var argument      = GetArgument(constraint.ArgumentId);
                    var argumentValue = executionContext.GetArgumentValue(constraint.ArgumentId);

                    if (!_valueComparer.IsSatisfiedBy(argument.DataType, argumentValue, constraint.Comparison,
                                                      constraint.Value))
                    {
                        if (logger.LogLevel == ContextLogLevel.Verbose)
                        {
                            logger.LogVerbose($"[disqualified] {argument.Name} is '{argumentValue}' and should {constraint.Comparison} '{constraint.Value}'");
                        }

                        return(formulaContext.Disqualified(
                                   argument,
                                   argumentValue,
                                   constraint.Comparison,
                                   constraint.Value));
                    }
                }

                foreach (var operation in formula.Operations)
                {
                    decimal value = 0;

                    switch (operation.ValueProviderName.ToLower())
                    {
                    case "decimal":
                    {
                        value = Convert.ToDecimal(operation.InputParameter);

                        break;
                    }

                    case "argument":
                    {
                        value = Convert.ToDecimal(executionContext.GetArgumentValue(new Guid(operation.InputParameter)));

                        break;
                    }

                    case "matrix":
                    {
                        var matrix = GetMatrix(new Guid(operation.InputParameter));

                        value =
                            Convert.ToDecimal(matrix.GetValue(_valueComparer, executionContext,
                                                              GetArgument(matrix.RowArgumentId),
                                                              matrix.ColumnArgumentId.HasValue ? GetArgument(matrix.ColumnArgumentId.Value) : null));

                        break;
                    }

                    case "formula":
                    {
                        value = Execute(executionContext, new Guid(operation.InputParameter), logger).Result;

                        break;
                    }

                    case "result":
                    {
                        value = formulaContext.Result;

                        break;
                    }
                    }

                    if (logger.LogLevel == ContextLogLevel.Verbose)
                    {
                        logger.LogVerbose($"[operation] {formulaContext.Result} {operation.GetOperator()} {value}");
                    }

                    operation.Perform(formulaContext, value);
                }

                return(formulaContext);
            }
        }