//public readonly double LogicalScore;
        public ProductLink(Engine engine, Product[] product1s, Product[] product2s)
        {
            this.Engine = engine;
            Product1s = product1s;
            Product2s = product2s;

            MutualCompanyIds = (from p1 in product1s join p2 in product2s on p1.DbProduct.CompanyId equals p2.DbProduct.CompanyId where p1 != null && p2 != null select p1.DbProduct.CompanyId).ToList();
            if (MutualCompanyIds.Count > 0)
            {
                MutualProductIds = (from p1 in product1s join p2 in product2s on p1.DbProduct.Id equals p2.DbProduct.Id where p1 != null && p2 != null select p1.DbProduct.Id).ToList();
                if (MutualProductIds.Count < MutualCompanyIds.Count)//chains contain different products of the same company so cannot be linked
                    Score = -1.1;
                else  //chains contain the same products so should be considered linked
                    Score = 1.1;
                return;
            }

            foreach (Product product1 in Product1s)
                foreach (Product product2 in Product2s)
                    Set(product1.DbProduct.Id, product2.DbProduct.Id, new ProductPair(engine, product1, product2));

            //word order
            //word density

            CategoryScore = (double)product1_id_product2_id_s2ProductPair.Values.Select(x => x.CategoryScore).Sum() / product1_id_product2_id_s2ProductPair.Count;
            NameScore = (double)product1_id_product2_id_s2ProductPair.Values.Select(x => x.NameScore).Sum() / product1_id_product2_id_s2ProductPair.Count;
            Score = 0.6 * CategoryScore + 0.4 * NameScore;
        }
 internal Product(Engine engine, Fhr.ProductOffice.Models.Product product)
 {
     this.engine = engine;
     if (product == null)
         throw new Exception("product is null");
     DbProduct = product;
 }
        internal Company(Engine engine, Fhr.ProductOffice.Models.Company company)
        {
            this.engine = engine;
            DbCompany = company;

            List<string> categories = engine.Db.Products.Where(p => p.CompanyId == company.Id).GroupBy(p => p.Category).Select(c => c.Key).ToList();
            this.categories = new HashSet<string>(categories);

            initialize_settings();
        }
            public ProductPair(Engine engine, Product product1, Product product2)
            {
                this.engine = engine;
                {
                    CategoryScore = engine.CompanyPairs.GetCategoryComparisonScore(product1, product2);
                }
                {
                    Dictionary<string, double> word2name_score = new Dictionary<string, double>();
                    foreach (string word in product1.Words(Field.Name))
                    {
                        if (product2.Words2Count(Field.Name).ContainsKey(word))
                            word2name_score[word] = .5 * engine.Companies.Get(product1.DbProduct.CompanyId).WordWeight(Field.Name, word)
                                + .5 * engine.Companies.Get(product2.DbProduct.CompanyId).WordWeight(Field.Name, word);
                    }
                    //MatchedWords[Field.Name] = word2name_score.Keys.ToList();
                    if (word2name_score.Count > 0)
                    {
                        NameScore = .5 * ((double)word2name_score.Values.Sum() / word2name_score.Count)
                            + .25 * ((double)word2name_score.Count / product1.Words(Field.Name).Count)
                            + .25 * ((double)word2name_score.Count / product2.Words(Field.Name).Count);
                    }
                }
                //    decimal p1 = product1.DbProduct.Price;

                //HashSet<string> matched_words = new HashSet<string>();
                //foreach (string word in product1.Words(Field.Description))
                //{
                //    if (product2.Word2Count(Field.Description).ContainsKey(word))
                //        matched_words.Add(word);
                //}
                //foreach (string word in matched_words)
                //{
                //    double score = get_word_score(word, product1, product2);
                //    SecondaryScore += score;
                //}
            }
 internal CompanyPairs(Engine engine)
 {
     this.engine = engine;
 }
 internal Companies(Engine engine)
 {
     this.engine = engine;
 }
 internal Products(Engine engine)
 {
     this.engine = engine;
 }