<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ComputerAlgebra
{
    public static class FactorExtension
    {
        /// <summary>
        /// Distribute products across sums.
        /// </summary>
        /// <param name="f"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static Expression Factor(this Expression f, Expression x) 
        {
            if (f is Product)
                return Product.New(((Product)f).Terms.Select(i => i.Factor(x)));

            if (f is Power)
            {
                Expression l = ((Power)f).Left.Factor(x);
                Expression r = ((Power)f).Right;
                return Product.New(Product.TermsOf(l).Select(i => Power.New(i, r)));
            }

            if (!ReferenceEquals(x, null))
            {
                // If f is a polynomial of x, factor it.
                try
                {
                    return Polynomial.New(f, x).Factor();
                }
                catch (Exception) { }
            }

            if (f is Sum)
            {
                Sum s = (Sum)f;

                IEnumerable<Expression> terms = s.Terms.Select(i => i.Factor()).Buffer();
                
                // All of the distinct factors.
                IEnumerable<Expression> factors = terms.SelectMany(i => Product.TermsOf(i)).Distinct();
                // Choose the most common factor to use.
                Expression factor = factors.ArgMax(i => terms.Count(j => Product.TermsOf(j).Contains(i)));
                // Find the terms that contain the factor.
                IEnumerable<Expression> contains = terms.Where(i => Product.TermsOf(i).Contains(factor)).Buffer();
                // If more than one term contains the factor, pull it out and factor the resulting expression (again).
                if (contains.Count() > 1)
                    return Sum.New(
                        Product.New(factor, Sum.New(contains.Select(i => Product.New(Product.TermsOf(i).Except(factor)))).Evaluate()),
                        Sum.New(terms.Except(contains, Expression.RefComparer))).Factor(null);
            }

            return f;
        }

        public static Expression Factor(this Expression f) { return Factor(f, null); }
    }
}<commit_msg>More powerful factoring of common subexpressions<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ComputerAlgebra
{
    public static class FactorExtension
    {
        // Enumerates x, splitting negative constants into a positive constant and -1.
        private static IEnumerable<Expression> FactorsOf(Expression x)
        {
            foreach (Expression i in Product.TermsOf(x))
            {
                if (i is Constant && (Real)i < 0)
                {
                    yield return -1;
                    yield return Real.Abs((Real)i);
                }
                else if (i is Power)
                {
                    yield return i;
                    yield return ((Power)i).Left;
                }
                else
                {
                    yield return i;
                }
            }
        }

        /// <summary>
        /// Distribute products across sums.
        /// </summary>
        /// <param name="f"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static Expression Factor(this Expression f, Expression x) 
        {
            if (f is Product)
                return Product.New(((Product)f).Terms.Select(i => i.Factor(x)));

            if (f is Power)
            {
                Expression l = ((Power)f).Left.Factor(x);
                Expression r = ((Power)f).Right;
                return Product.New(Product.TermsOf(l).Select(i => Power.New(i, r)));
            }

            if (!ReferenceEquals(x, null))
            {
                // If f is a polynomial of x, factor it.
                try
                {
                    return Polynomial.New(f, x).Factor();
                }
                catch (Exception) { }
            }

            if (f is Sum)
            {
                Sum s = (Sum)f;

                IEnumerable<Expression> terms = s.Terms.Select(i => i.Factor()).Buffer();
                
                // All of the distinct factors.
                IEnumerable<Expression> factors = terms.SelectMany(i => FactorsOf(i)).Distinct();
                // Choose the most common factor to use.
                Expression factor = factors.ArgMax(i => terms.Count(j => FactorsOf(j).Contains(i)));
                // Find the terms that contain the factor.
                IEnumerable<Expression> contains = terms.Where(i => FactorsOf(i).Contains(factor)).Buffer();
                // If more than one term contains the factor, pull it out and factor the resulting expression (again).
                if (contains.Count() > 1)
                    return Sum.New(
                        Product.New(factor, Sum.New(contains.Select(i => Binary.Divide(i, factor))).Evaluate()),
                        Sum.New(terms.Except(contains, Expression.RefComparer))).Factor(null);
            }

            return f;
        }

        public static Expression Factor(this Expression f) { return Factor(f, null); }
    }
}
{'fraction_non_alphanumeric': 0.11641292239138508, 'fraction_numerical': 0.000928332714444857, 'mean_word_length': 1.4200359389038635, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 44, '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': '6628636', 'n_tokens_mistral': 1411, 'n_tokens_neox': 1303, 'n_words': 360}