<commit_before>using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace ClosedXML.Excel.CalcEngine
{
    internal class Tally : IEnumerable<Object>
    {
        private readonly List<object> _list = new List<object>();
        private readonly bool NumbersOnly;

        private double[] _numericValues;

        public Tally()
            : this(false)
        { }

        public Tally(bool numbersOnly)
            : this(null, numbersOnly)
        { }

        public Tally(IEnumerable<Expression> p)
            : this(p, false)
        { }

        public Tally(IEnumerable<Expression> p, bool numbersOnly)
        {
            if (p != null)
            {
                foreach (var e in p)
                {
                    Add(e);
                }
            }

            NumbersOnly = numbersOnly;
        }

        public void Add(Expression e)
        {
            // handle enumerables
            var ienum = e as IEnumerable;
            if (ienum != null)
            {
                foreach (var value in ienum)
                {
                    _list.Add(value);
                }
                _numericValues = null;
                return;
            }

            // handle expressions
            var val = e.Evaluate();
            var valEnumerable = val as IEnumerable;
            if (valEnumerable == null || val is string)
                _list.Add(val);
            else
                foreach (var v in valEnumerable)
                    _list.Add(v);

            _numericValues = null;
        }

        public void AddValue(Object v)
        {
            _list.Add(v);
            _numericValues = null;
        }

        public double Count()
        {
            return Count(NumbersOnly);
        }

        public double Count(bool numbersOnly)
        {
            if (numbersOnly)
                return NumericValuesInternal().Length;
            else
                return _list.Count(o => !CalcEngineHelpers.ValueIsBlank(o));
        }

        private IEnumerable<double> NumericValuesEnumerable()
        {
            foreach (var value in _list)
            {
                var vEnumerable = value as IEnumerable;
                if (vEnumerable == null || vEnumerable is string)
                {
                    if (TryParseToDouble(value, out double tmp))
                        yield return tmp;
                }
                else
                {
                    foreach (var v in vEnumerable)
                    {
                        if (TryParseToDouble(v, out double tmp))
                            yield return tmp;
                    }
                }
            }
        }

        private bool TryParseToDouble(object value, out double d)
        {
            if (value.IsNumber())
            {
                d = Convert.ToDouble(value);
                return true;
            }
            else if (value is DateTime)
            {
                d = Convert.ToDouble(((DateTime)value).ToOADate());
                return true;
            }
            else
            {
                return double.TryParse(value.ToString(), out d);
            }
        }

        private double[] NumericValuesInternal()
            => LazyInitializer.EnsureInitialized(ref _numericValues, () => NumericValuesEnumerable().ToArray());

        public IEnumerable<double> NumericValues()
            => NumericValuesInternal().AsEnumerable();

        public double Product()
        {
            var nums = NumericValuesInternal();
            return nums.Length == 0
                ? 0
                : nums.Aggregate(1d, (a, b) => a * b);
        }

        public double Sum() => NumericValuesInternal().Sum();<commit_msg>Small Tally class improvements<commit_after>using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace ClosedXML.Excel.CalcEngine
{
    internal class Tally : IEnumerable<Object>
    {
        private readonly List<object> _list = new List<object>();
        private readonly bool NumbersOnly;

        private double[] _numericValues;

        public Tally()
            : this(false)
        { }

        public Tally(bool numbersOnly)
            : this(null, numbersOnly)
        { }

        public Tally(IEnumerable<Expression> p)
            : this(p, false)
        { }

        public Tally(IEnumerable<Expression> p, bool numbersOnly)
        {
            if (p != null)
            {
                foreach (var e in p)
                {
                    Add(e);
                }
            }

            NumbersOnly = numbersOnly;
        }

        public void Add(Expression e)
        {
            // handle enumerables
            if (e is IEnumerable ienum)
            {
                foreach (var value in ienum)
                {
                    _list.Add(value);
                }
                _numericValues = null;
                return;
            }

            // handle expressions
            var val = e.Evaluate();
            if (val is string || !(val is IEnumerable valEnumerable))
                _list.Add(val);
            else
                foreach (var v in valEnumerable)
                    _list.Add(v);

            _numericValues = null;
        }

        public void AddValue(Object v)
        {
            _list.Add(v);
            _numericValues = null;
        }

        public double Count()
        {
            return Count(NumbersOnly);
        }

        public double Count(bool numbersOnly)
        {
            if (numbersOnly)
                return NumericValuesInternal().Length;
            else
                return _list.Count(o => !CalcEngineHelpers.ValueIsBlank(o));
        }

        private IEnumerable<double> NumericValuesEnumerable()
        {
            foreach (var value in _list)
            {
                if (value is string || !(value is IEnumerable vEnumerable))
                {
                    if (TryParseToDouble(value, out double tmp))
                        yield return tmp;
                }
                else
                {
                    foreach (var v in vEnumerable)
                    {
                        if (TryParseToDouble(v, out double tmp))
                            yield return tmp;
                    }
                }
            }
        }

        private bool TryParseToDouble(object value, out double d)
        {
            if (value.IsNumber())
            {
                d = Convert.ToDouble(value);
                return true;
            }
            else if (value is DateTime dt)
            {
                d = dt.ToOADate();
                return true;
            }
            else
            {
                return double.TryParse(value.ToString(), out d);
            }
        }

        private double[] NumericValuesInternal()
            => LazyInitializer.EnsureInitialized(ref _numericValues, () => NumericValuesEnumerable().ToArray());

        public IEnumerable<double> NumericValues()
            => NumericValuesInternal().AsEnumerable();

        public double Product()
        {
            var nums = NumericValuesInternal();
            return nums.Length == 0
                ? 0
                : nums.Aggregate(1d, (a, b) => a * b);
        }

        public double Sum() => NumericValuesInternal().Sum();
{'fraction_non_alphanumeric': 0.08282208588957055, 'fraction_numerical': 0.0008002133902373967, 'mean_word_length': 1.169221868672259, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4545670', 'n_tokens_mistral': 1866, 'n_tokens_neox': 1758, 'n_words': 450}