/// <summary>
        /// Eş durumu gibi nedenlerle birbirine bağlı grupların birlikte çözümü için alternatif nöbetçi eczane optimizasyon modelli
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public EczaneNobetSonucModel Solve(EczaneNobetCokGrupDataModel data)
        {
            var model = Model(data);
            // Get a solver instance, change your solver
            var solver = new CplexSolver();

            try
            {
                // solve the model
                var solution = solver.Solve(model);

                var modelStatus    = solution.ModelStatus;
                var solutionStatus = solution.Status;
                var modelName      = solution.ModelName;
                var bestBound      = solution.BestBound;

                //var confilicts = new ConflictingSet();
                //confilicts = solution.ConflictingSet;
                //ConstraintsUB = new IEnumerable<Constraint>();
                //ConstraintsUB = confilicts.ConstraintsUB;

                if (modelStatus != ModelStatus.Feasible)
                {
                    throw new Exception("Uygun çözüm bulunamadı!");
                }
                else
                {
                    // import the results back into the model
                    model.VariableCollections.ForEach(vc => vc.SetVariableValues(solution.VariableValues));
                    var objective = solution.ObjectiveValues.Single();
                    var sure      = solution.OverallWallTime;

                    Results = new EczaneNobetSonucModel
                    {
                        CozumSuresi    = sure,
                        ObjectiveValue = objective.Value,
                        ResultModel    = new List <EczaneNobetCozum>()
                    };

                    foreach (var r in data.EczaneNobetTarihAralik.Where(s => _x[s].Value == 1))
                    {
                        Results.ResultModel.Add(new EczaneNobetCozum()
                        {
                            TakvimId          = r.TakvimId,
                            EczaneNobetGrupId = r.EczaneNobetGrupId,
                            NobetGorevTipId   = r.NobetGorevTipId
                        });
                    }
                }
            }
            catch (Exception ex)
            {
                var mesaj = ex.Message;
                throw new Exception(mesaj);
            }
            return(Results);
        }
        private Model Model(EczaneNobetCokGrupDataModel data)
        {
            var model = new Model()
            {
                Name = "Eczane Nöbet Çoklu Model Alternatif"
            };

            #region Veriler
            //int BigM = 1000;
            var bayramlar = data.TarihAraligi.Where(w => w.NobetGunKuralId > 7).Select(s => s.NobetGunKuralId).Distinct().ToList();
            #endregion

            #region Karar Değişkenleri
            _x = new VariableCollection <EczaneNobetTarihAralik>(
                model,
                data.EczaneNobetTarihAralik,
                "_x", null,
                h => data.LowerBound,
                h => data.UpperBound,
                a => VariableType.Binary);
            #endregion

            #region Amaç Fonksiyonu
            var amac = new Objective(Expression.Sum((data.EczaneNobetTarihAralik
                                                     .Select(i => _x[i]))),
                                     "Sum of all item-values: ",
                                     ObjectiveSense.Minimize);
            model.AddObjective(amac);
            #endregion

            #region Kısıtlar

            #region Talep Kısıtları
            //nöbet grubu bazında olacak
            foreach (var nobetGrupGorevTip in data.NobetGrupGorevTipler)
            {
                foreach (var d in data.TarihAraligi)
                {
                    int gunlukNobetciSayisi = (int)data.NobetGrupKurallar
                                              //.Where(s => s.NobetGrupId == nobetGrupGorevTip.NobetGrupId
                                              //         && s.NobetKuralId == 3)
                                              .Select(s => s.Deger).SingleOrDefault();

                    var talepFarkli = data.NobetGrupTalepler
                                      .Where(s => s.NobetGrupGorevTipId == nobetGrupGorevTip.Id &&
                                             s.TakvimId == d.TakvimId)
                                      .Select(s => s.NobetciSayisi).SingleOrDefault();

                    if (talepFarkli > 0)
                    {
                        gunlukNobetciSayisi = talepFarkli;
                    }

                    model.AddConstraint(
                        Expression.Sum(data.EczaneNobetTarihAralik
                                       .Where(k => k.TakvimId == d.TakvimId &&
                                              k.NobetGrupId == nobetGrupGorevTip.NobetGrupId &&
                                              k.NobetGorevTipId == nobetGrupGorevTip.NobetGorevTipId
                                              )
                                       .Select(m => _x[m])) == gunlukNobetciSayisi,
                        $"her gune talep kadar eczane atanmali: {gunlukNobetciSayisi}");
                }
            }

            #endregion

            #region Arz Kısıtları

            var pespeseGorev = true;
            #region Peşpeşe Görev Yazılmasın
            if (pespeseGorev)
            {
                foreach (var nobetGrup in data.NobetGruplar)
                {
                    int pespeseNobetSayisi = (int)data.NobetGrupKurallar
                                             //.Where(s => s.NobetGrupId == nobetGrup.Id
                                             //         && s.NobetKuralId == 1)
                                             .Select(s => s.Deger).SingleOrDefault();

                    foreach (var f in data.EczaneKumulatifHedefler.Where(w => w.NobetGrupId == nobetGrup.Id))
                    {
                        foreach (var g in data.TarihAraligi.Take(data.TarihAraligi.Count() - pespeseNobetSayisi))
                        {
                            //model.AddConstraint(
                            //  Expression.Sum(data.EczaneNobetTarihAralik
                            //                   .Where(e => e.EczaneNobetGrupId == f.EczaneNobetGrupId
                            //                               && (e.Gun >= g.Gun && e.Gun <= g.Gun + pespeseNobetSayisi)
                            //                               && e.NobetGrupId == nobetGrup.Id
                            //                               )
                            //                   .Select(m => _x[m])) <= 1,
                            //                   $"eczanelere pespese nobet yazilmasin, {f}");
                        }
                    }
                }
            }
            #endregion

            #region Her eczaneye yazılması gereken hedefler
            foreach (var nobetGrup in data.NobetGruplar)
            {
                foreach (var hedef in data.EczaneKumulatifHedefler.Where(w => w.NobetGrupId == nobetGrup.Id))
                {
                    #region Toplam Hedefler
                    var toplamMaxHedef = true;
                    if (toplamMaxHedef)
                    {
                        var maxToplam = hedef.Toplam <= 2 ? hedef.Toplam + 1 : hedef.Toplam + 2;
                        model.AddConstraint(
                            Expression.Sum(data.EczaneNobetTarihAralik
                                           .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId &&
                                                  e.NobetGorevTipId == hedef.NobetGorevTipId)
                                           .Select(m => _x[m])) <= hedef.Toplam,
                            $"her eczaneye bir ayda en çok nobet grubunun hedefi kadar nobet yazilmali, {hedef}");
                    }

                    var toplamMinHedef = true;
                    if (toplamMinHedef)
                    {
                        var minToplam = hedef.Toplam - 1 > 1 ? 1 : 0;
                        model.AddConstraint(
                            Expression.Sum(data.EczaneNobetTarihAralik
                                           .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId &&
                                                  e.NobetGorevTipId == hedef.NobetGorevTipId)
                                           .Select(m => _x[m])) >= hedef.Toplam - 1,
                            $"her eczaneye bir ayda en az nobet grubunun hedefinden bir eksik nobet yazilmali, {hedef}");
                    }

                    var herAyEnaz1Gorev = true;
                    if (herAyEnaz1Gorev)
                    {//her ay en az 1 tane yazılsın
                        model.AddConstraint(
                            Expression.Sum(data.EczaneNobetTarihAralik
                                           .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId &&
                                                  e.NobetGorevTipId == hedef.NobetGorevTipId)
                                           .Select(m => _x[m])) >= 1,
                            $"her eczaneye bir ayda en az nobet grubunun hedefinden bir eksik nobet yazilmali, {hedef}");
                    }

                    #endregion

                    //a2: sıralı gün varsa bu kısıt devre dışı kalır--o gün bayram olarak sayılmaz
                    int siraliGun;
                    //siraliGun = 0;
                    siraliGun = (int)data.NobetGrupKurallar
                                //.Where(s => s.NobetGrupId == nobetGrup.Id
                                //         && s.NobetKuralId == 4)
                                .Select(s => s.Deger).FirstOrDefault();

                    #region Bayram Toplam Hedefleri
                    if (bayramlar.Count() > 0)
                    {
                        var temp = hedef.ToplamBayram - 1;
                        if (temp < 0)
                        {
                            temp = 0;
                        }

                        var bayramToplamMaxHedef = false;
                        if (bayramToplamMaxHedef)
                        {
                            model.AddConstraint(
                                Expression.Sum(data.EczaneNobetTarihAralik
                                               .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId
                                                      //&& e.HaftaninGunu != siraliGun //a2
                                                      && e.NobetGunKuralId > 7)
                                               .Select(m => _x[m])) <= hedef.ToplamBayram,
                                $"her eczaneye bir ayda nobet grubunun hedefi kadar toplam bayram nobeti yazilmali, {hedef}");
                        }

                        var bayramToplamMinHedef = false;
                        if (bayramToplamMinHedef)
                        {
                            model.AddConstraint(
                                Expression.Sum(data.EczaneNobetTarihAralik
                                               .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId
                                                      //&& e.HaftaninGunu != siraliGun //a2
                                                      && e.NobetGunKuralId > 7)
                                               .Select(m => _x[m])) >= temp,
                                $"her eczaneye bir ayda nobet grubunun hedefi kadar toplam bayram nobeti yazilmali, {hedef}");
                        }
                    }
                    #endregion

                    #region Diğer Günlerin Hedefleri
                    foreach (var gunDeger in data.NobetGrupGunKurallar
                             .Where(s =>                    //s.NobetGunKuralId != siraliGun
                                    s.NobetGrupId == nobetGrup.Id).Select(s => s.NobetGunKuralId))
                    {
                        GetEczaneGunHedef(hedef, out double maxArz, out double minArz, gunDeger);

                        if (maxArz < 2)
                        {
                            maxArz += 1;
                            //minToplam = hedef.Toplam - 2;
                        }

                        var digerGunlerMaxHedef = false;
                        if (digerGunlerMaxHedef)
                        {
                            model.AddConstraint(
                                Expression.Sum(data.EczaneNobetTarihAralik
                                               .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId &&
                                                      e.NobetGunKuralId == gunDeger &&
                                                      e.NobetGrupId == nobetGrup.Id)
                                               .Select(m => _x[m])) <= maxArz,
                                $"her eczaneye bir ayda nobet grubunun {gunDeger} hedefi kadar nobet yazilmali, {hedef}");
                        }

                        var digerGunlerMinHedef = false;
                        if (digerGunlerMinHedef)
                        {
                            model.AddConstraint(
                                Expression.Sum(data.EczaneNobetTarihAralik
                                               .Where(e => e.EczaneNobetGrupId == hedef.EczaneNobetGrupId &&
                                                      e.NobetGunKuralId == gunDeger &&
                                                      e.NobetGrupId == nobetGrup.Id
                                                      //&& e.EczaneNobetGrupId != pazarTutanEczane
                                                      )
                                               .Select(m => _x[m])) >= minArz,
                                $"her eczaneye bir ayda nobet grubunun {gunDeger} hedefi kadar nobet yazilmali, {hedef}");
                        }
                    }
                    #endregion

                    #region son uc ayda pazar günü nöbet tutanlar çözüm ayında pazar tutmasın
                    //if (data.Ay > 1)
                    //{
                    //    //foreach (var eczaneNobetGrupId in data.SonUcAydaPazarGunuNobetTutanEczaneler.Where(s => hedef.EczaneNobetGrupId == s))
                    //    //{
                    //    var eczaneNobetGrupId = data.SonUcAydaPazarGunuNobetTutanEczaneler.Where(s => hedef.EczaneNobetGrupId == s).FirstOrDefault();
                    //    if (eczaneNobetGrupId > 0)
                    //    {
                    //        var pazarlar = data.TarihAraligi.Where(s => s.HaftaninGunu == 1);
                    //        foreach (var tarih in pazarlar)
                    //        {
                    //            model.AddConstraint(
                    //              Expression.Sum(data.EczaneNobetTarihAralik
                    //                               .Where(e => e.EczaneNobetGrupId == eczaneNobetGrupId
                    //                                        && e.TakvimId == tarih.TakvimId)
                    //                               .Select(m => _x[m])) == 0,
                    //                               $"son uc ayda pazar gunu nobet tutanlar cozum ayinda pazar tutmasin, {tarih}");
                    //        }
                    //    }
                    //    //}
                    //}

                    #endregion
                }
            }
            #endregion

            #region Yılda en fazla aynı gün 3'ten fazla nöbet tutulmasın

            var yildaEncokUcKezGrup = false;
            #region yil içinde en fazla 3 kez aynı gün nöbet tutulsun
            if (yildaEncokUcKezGrup)
            {
                foreach (var ciftGrup in data.YilIcindeAyniGunNobetTutanEczaneler.Select(s => s.Id).Distinct().ToList())
                {
                    var eczaneler = data.YilIcindeAyniGunNobetTutanEczaneler
                                    .Where(s => s.Id == ciftGrup).Select(w => w.EczaneId).ToList();

                    int siraliGun;
                    siraliGun = 1;
                    //siraliGun = (int)data.NobetGrupKurallar
                    //    .Where(s => s.NobetGrupId == nobetGrupId
                    //             && s.NobetKuralId == 4)
                    //    .Select(s => s.Deger).FirstOrDefault();

                    foreach (var tarih in data.TarihAraligi.Where(s => s.HaftaninGunu != siraliGun))
                    {
                        model.AddConstraint(
                            Expression.Sum(data.EczaneNobetTarihAralik
                                           .Where(e => eczaneler.Contains(e.EczaneId) &&
                                                  e.TakvimId == tarih.TakvimId)
                                           .Select(m => _x[m])) <= 1,
                            $"eczaneler ay icinde iki kez ayni grup olmasin, {tarih}");
                    }
                }
            }
            #endregion

            var sonIkiAydakiGrup = false;
            #region son iki ayda aynı gün nöbet tutanlar çözüm ayında aynı gün nöbet tutmasın
            if (sonIkiAydakiGrup)
            {
                foreach (var ciftGrup in data.SonIkiAyAyniGunNobetTutanEczaneler.Select(s => s.Id).Distinct().ToList())
                {
                    var eczaneler = data.SonIkiAyAyniGunNobetTutanEczaneler
                                    .Where(s => s.Id == ciftGrup).Select(w => w.EczaneId).ToList();

                    //var eczaneler2 = data.EczaneNobetGruplar.Where(s => eczaneler.Contains(s.EczaneId)).Select(e=> new { e.NobetGrupId, e.EczaneId }).ToList();

                    int siraliGun;
                    siraliGun = 1;
                    //siraliGun = (int)data.NobetGrupKurallar
                    //    .Where(s => s.NobetGrupId == nobetGrupId
                    //             && s.NobetKuralId == 4)
                    //    .Select(s => s.Deger).FirstOrDefault();

                    foreach (var tarih in data.TarihAraligi.Where(s => s.HaftaninGunu != siraliGun))
                    {
                        model.AddConstraint(
                            Expression.Sum(data.EczaneNobetTarihAralik
                                           .Where(e => eczaneler.Contains(e.EczaneId) &&
                                                  e.TakvimId == tarih.TakvimId
                                                  )
                                           .Select(m => _x[m])) <= 1,
                            $"eczaneler son uc ay ayni grup olmasin, {tarih}");
                    }
                }
            }
            #endregion

            var ayIcindeAyniGunNobet = true;
            #region ay içinde en fazla bir kez aynı gün nöbet tutulsun
            //ay içinde iki kez nöbet tutan eczane çiftleri oluştuğunda bu çiftler kısıt olacak şekilde yeniden çözüm yapılıyor.  (recursive)
            //Yeni modelde bu çiftlerin herhangi iki gündeki toplamları 3'ten küçük olma kısıtı eklenince sadece 1 kez çift olmaları sağlanıyor.
            if (ayIcindeAyniGunNobet)
            {
                foreach (var g in data.AyIcindeAyniGunNobetTutanEczaneler.Select(s => s.Id).Distinct().ToList())
                {
                    var ikiliEczaneler = data.AyIcindeAyniGunNobetTutanEczaneler
                                         .Where(w => w.Id == g)
                                         .Select(s => s.EczaneId).ToList();

                    foreach (var tarih in data.TarihAraligi.Take(data.TarihAraligi.Count() - 1))
                    {
                        foreach (var tarih2 in data.TarihAraligi.Where(s => s.TakvimId > tarih.TakvimId))
                        {
                            model.AddConstraint(
                                Expression.Sum(data.EczaneNobetTarihAralik
                                               .Where(e => ikiliEczaneler.Contains(e.EczaneId) &&
                                                      (e.TakvimId == tarih.TakvimId || e.TakvimId == tarih2.TakvimId))
                                               .Select(m => _x[m])) <= 3,
                                $"ay icinde diger gruptaki eczaneler ile en fazla bir kez nobet tutulsun, {g}");
                        }
                    }
                }
            }
            #endregion

            #endregion

            //var eczaneGrup = false;
            #region Eczane grup kısıtı
            //Birbiri ile coğrafi yakınlık ya da eş durumu gibi nedenlerle farklı gruplardaki eczanelere sürekli aynı gün nöbet yazılmasın
            //if (eczaneGrup)
            //{
            //    foreach (var g in data.EczaneGrupTanimlar)
            //    {
            //        var eczaneler = data.EczaneGruplar
            //                                .Where(x => x.EczaneGrupTanimId == g.Id)
            //                                .Select(s => s.EczaneId).Distinct().ToList();

            //        var nobetGruplar = data.EczaneNobetGruplar.Where(s => eczaneler.Contains(s.EczaneId)).Select(w => w.NobetGrupId).Distinct();

            //        //a1: sıralı gün varsa bu kısıt devre dışı kalır--o gün bayram olarak sayılmaz
            //        int siraliGun;
            //        //siraliGun = 0;
            //        siraliGun = (int)data.NobetGrupKurallar
            //                            .Where(s => nobetGruplar.Contains(s.NobetGrupId)
            //                                     && s.NobetKuralId == 4) //s.NobetKuralId == 4:sıralı gün kuralı
            //                            .Select(s => s.Deger).FirstOrDefault();

            //        foreach (var tarih in data.TarihAraligi.Take(data.TarihAraligi.Count() - g.ArdisikNobetSayisi))
            //        {
            //            var grupTanimlar = Expression.Sum(data.EczaneNobetTarihAralik
            //                                                    .Where(e => eczaneler.Contains(e.EczaneId)
            //                                                             && (e.Gun >= tarih.Gun && e.Gun <= tarih.Gun + g.ArdisikNobetSayisi)
            //                                                             && e.HaftaninGunu != siraliGun //a1
            //                                                          )
            //                                                    .Select(m => _x[m])) <= 1;
            //            model.AddConstraint(grupTanimlar,
            //                                        $"herbir_eczaneGrupTanimdaki_eczaneler_beraber_nobet_tutmasin, {tarih.Gun}");
            //        }
            //    }
            //}
            #endregion

            var istek = false;
            #region İsteğe Görev Yazılsın
            if (istek && data.Ay < 6)
            {
                foreach (var f in data.EczaneNobetIstekListe)
                {
                    model.AddConstraint(
                        Expression.Sum(data.EczaneNobetTarihAralik
                                       .Where(e => e.EczaneId == f.EczaneId &&
                                              e.NobetGrupId == f.NobetGrupId &&
                                              e.TakvimId == f.TakvimId
                                              )
                                       .Select(m => _x[m])) == 1,
                        $"istege nobet yaz, {f}");
                }
            }
            #endregion

            var mazeret = true;
            #region Mazerete Görev Yazılmasın
            if (mazeret)
            {
                foreach (var f in data.EczaneNobetMazeretListe)
                {
                    model.AddConstraint(
                        Expression.Sum(data.EczaneNobetTarihAralik
                                       .Where(e => e.EczaneId == f.EczaneId &&
                                              e.NobetGrupId == f.NobetGrupId &&
                                              e.TakvimId == f.TakvimId
                                              )
                                       .Select(m => _x[m])) == 0,
                        $"mazerete nobet yazma, {f}");
                }
            }
            #endregion

            var bayram = false;
            #region Bayram günlerinde en fazla 1 görev yazılsın.
            //eğer bayram günleri ardışık günlerden fazlaysa bayram süresince 2 görev yazılmasın
            if (bayram)
            {
                foreach (var nobetGrup in data.NobetGruplar)
                {
                    int pespeseNobetSayisi = (int)data.NobetGrupKurallar
                                             //.Where(s => s.NobetGrupId == nobetGrup.Id
                                             //         && s.NobetKuralId == 1)
                                             .Select(s => s.Deger).SingleOrDefault();

                    if (data.TarihAraligi.Where(w => w.NobetGunKuralId > 7).Count() > pespeseNobetSayisi)
                    {
                        foreach (var f in data.EczaneKumulatifHedefler.Where(x => x.NobetGrupId == nobetGrup.Id))
                        {
                            foreach (var g in data.TarihAraligi.Where(w => w.NobetGunKuralId > 7))
                            {
                                //model.AddConstraint(
                                //  Expression.Sum(data.EczaneNobetTarihAralik
                                //                   .Where(e => e.EczaneNobetGrupId == f.EczaneNobetGrupId
                                //                               && e.Gun == g.Gun
                                //                         )
                                //                   .Select(m => _x[m])) <= 1,
                                //                   $"bayram nobeti sinirla, {f}");
                            }
                        }
                    }
                }
            }
            #endregion

            #endregion
            #endregion

            return(model);
        }