static void Main(string[] args)
        {
            GRBEnv   env   = new GRBEnv("TJLinearCharacter.log");
            GRBModel model = new GRBModel(env);

            //declare the iv variables and key variables
            GRBVar[] keyvar = model.AddVars(80, GRB.BINARY);    //declare the assignment variables of key variables
            GRBVar[] ivvar  = model.AddVars(80, GRB.BINARY);    //declare the assignment variables of iv variables
            GRBVar[] kvf    = model.AddVars(80, GRB.BINARY);    //declare the flag variables of key variables
            GRBVar[] vvf    = model.AddVars(80, GRB.BINARY);    //declare the flag variables of iv variables

            GRBVar[] Uvar = model.AddVars(8000, GRB.BINARY);    //interval assignment variables used to long XOR expression into short ones y=k1+k2+k3+k4--> u1=k1+k2, u2=k3+k4, y=u1+u2
            GRBVar[] Vvar = model.AddVars(8000, GRB.INTEGER);   //interval variables
            GRBVar[] Wvar = model.AddVars(8000, GRB.BINARY);    //interval variables

            GRBVar[] UFvar = model.AddVars(8000, GRB.BINARY);   //UFvar[i] is the flag variables corresponding to Uvar[i]
            GRBVar[] NUvar = model.AddVars(8000, GRB.BINARY);   //NUvar[i]= 1-Uvar[i]
            GRBVar[] NUFvar = model.AddVars(8000, GRB.BINARY);  //NUFvar[i]= 1-UFvar[i]
            GRBVar[] Consvar = model.AddVars(8000, GRB.BINARY); //constant variables
            GRBVar[] bvar = model.AddVars(8000, GRB.BINARY);    //b variables
            int      i, j, k;
            int      loc0 = 0, loc1 = 0, loc2 = 0, loc3 = 0, loc4 = 0, locb = 0;
            int      locf = 0;
            int      con  = 0;

            // name all the variables used in this MILP model
            for (i = 0; i < keyvar.Length; i++)
            {
                keyvar[i].VarName = "k" + i.ToString();
                ivvar[i].VarName  = "v" + i.ToString();
                kvf[i].VarName    = "kf" + i.ToString();
                vvf[i].VarName    = "vf" + i.ToString();
            }
            for (i = 0; i < Uvar.Length; i++)
            {
                Uvar[i].VarName    = "U" + i.ToString();
                Wvar[i].VarName    = "W" + i.ToString();
                UFvar[i].VarName   = "UF" + i.ToString();
                Vvar[i].VarName    = "V" + i.ToString();
                NUvar[i].VarName   = "NU" + i.ToString();
                NUFvar[i].VarName  = "NUF" + i.ToString();
                bvar[i].VarName    = "b" + i.ToString();
                Consvar[i].VarName = "cons" + i.ToString();
            }

            UInt32[, ,] ConS = new UInt32[1000, 200, 6];//used to store the conditions derived to control the propagation of difference
            int[] conlen   = new int[1000];
            int[] consflag = new int[1000];
            int[] rec_loc1 = new int[1000];
            int[] rec_locf = new int[1000];

            //the number of the conditions
            int consnum = 652;

            int[] loc        = new int[2];
            int[] locw       = new int[1];
            int[] condiassig = new int[consnum];
            int[] condiflag  = new int[consnum];

            GRBLinExpr Tar     = new GRBLinExpr();//declare the target linear expression, which is used as the objective function
            List <int> indlist = new List <int>()
            {
            };
            List <GRBVar> Varlist = new List <GRBVar>()
            {
            };
            List <GRBVar> VarlistCopy = new List <GRBVar>()
            {
            };
            List <GRBVar> VarFlaglist = new List <GRBVar>()
            {
            };
            //List<GRBVar> VarFlaglistCopy = new List<GRBVar>() { };
            StreamWriter sw = new StreamWriter("FinalCons.txt");
            //the index of cube variables
            List <int> cube = new List <int>()
            {
                0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78
            };

            //set the state of cube variables to \delta
            for (i = 0; i < cube.Count; i++)
            {
                model.AddConstr(vvf[cube[i]] == 1, "iniiv" + (cube[i]).ToString());
            }

            //set the contraints between an assigment variable and the corresponding flag variable
            for (i = 0; i < 80; i++)
            {
                model.AddConstr(keyvar[i] <= 1 - kvf[i], "keyflagrel");
                model.AddConstr(ivvar[i] <= 1 - vvf[i], "ivflagrel");
            }
            model.Update();

            //read the derived constraints from the file
            ReadCondition(ConS, conlen, consflag);
            Console.Write("ReadConditionDone\n");

            //model each condition
            for (i = 0; i < consnum; i++)
            {
                loc[0] = loc1;
                loc[1] = locf;
                VarFlaglist.Clear();
                Varlist.Clear();
                //linearize a condition and return a list of involved variables.

                //In this condition, for condition f, it would linearize it and would return the assignment/flag variable of each monomial in f;
                //In paticular, Uvar[0],Uvar[1],...,Uvar[m-1] are the assignment variables of the linearized monomials
                //UFvar[0],UFvar[1],...,UFvar[m-1] are the flag variables of the linearized monomials
                //For eaxmple, for a condition f=k1+k2*k3+k4, it can be linearized as f=u1+u2+u3 where u2=k2k3.
                //Then, Varlist[0],Varlist[1],Varlist[2] are assignment variables of u1,u2,u3
                //and VarFlaglist[0],VarFlaglist[1],...,VarFlaglist[2] are the flag variables of u1,u2,u3
                Varlist = LinearConstrain(model, Uvar, UFvar, ConS, conlen, i, keyvar, ivvar, loc, kvf, vvf, Wvar, locw, VarFlaglist);
                loc1    = loc[0];
                locf    = loc[1];
                //here a condition f is linearized and so it is the XOR of several variables.
                //in the following, we would determine the assigment varible and flag variable of f
                // and we denote the assigment variable and flag variable of f by f_av and f_fv respectively for short.

                //The target expression 'Tar' is determined according to the assigment variable and flag variable of each condition f
                //In particular, Tar= sum(f in Conset) f_av +(-10000)*f_fv, where Conset is the set of conditions.

                //If the size of f is 1(excluding the constant), namely f consists of only one variable, then we do not need extra operations.
                if (Varlist.Count == 1)
                {
                    //if consflag[i]=1, then it means that there is a constant 1 in f.
                    //In this case, we add (-1.0*f_av) to Tar and increase con by 1 which is added to Tar in the end.
                    if (consflag[i] == 1)
                    {
                        Tar.AddTerm(-1.0, Varlist[0]);
                        con++;
                    }
                    else
                    {
                        Tar.AddTerm(1.0, Varlist[0]);
                    }
                    //add -10000*f_fv to Tar
                    Tar.AddTerm(-10000, VarFlaglist[0]);
                    rec_loc1[i] = loc1;
                    rec_locf[i] = locf;
                }

                //If f is the XOR of two variables(assumin that f=u1+u2+c), where c is a constant in {0,1}.
                //then we shoule calclate f_av and f_fv according to (u1_av, u2_av) and (u1_fv, u2_fv).
                if (Varlist.Count == 2)
                {
                    VarlistCopy.Clear();
                    GRBVar[] VarFlaglistCopy = new GRBVar[3];
                    GRBVar[] Vartemplist     = new GRBVar[4];
                    //copy Varlist/VarFlaglist to VarlistCopy/VarFlaglistCopy
                    for (j = 0; j < Varlist.Count(); j++)
                    {
                        VarlistCopy.Add(Varlist[j]);
                        VarFlaglistCopy[j] = VarFlaglist[j];
                    }
                    VarlistCopy.Add(Uvar[loc1]);

                    //model u1_av+u2_av. Here, VarlistCopy[0]and VarlistCoyp[1] are u1_av and u2_av respectively.
                    AddCons2Vars(model, VarlistCopy);
                    //model u1_av+u2_av+c. if consflag[i]=1, then c is 1. In this case, we add an constant variable Consvar[loc0] to the model
                    //and model u1_av+u2_av+c.
                    if (consflag[i] == 1)
                    {
                        model.AddConstr(Consvar[loc0++] == 1, "Flip" + j.ToString());
                        VarlistCopy[0] = Uvar[loc1];
                        VarlistCopy[1] = Consvar[loc0 - 1];
                        VarlistCopy[2] = Uvar[loc1 + 1];
                        //Console.WriteLine("*****\n" + loc1 + "\n******\n" + loc0 + "\n*****\n");
                        loc1++;
                        AddCons2Vars(model, VarlistCopy);
                    }

                    //until now, Uvar[loc1] is corresponding to beta_d in Proposition 2 of our paper.
                    //Then, we shall show how to obtain the final assignment/flag variable of f.

                    //UF flip the variables
                    model.AddConstr(NUvar[loc3++] == 1 - VarFlaglist[0], "Flip1" + (loc3).ToString());
                    model.AddConstr(NUvar[loc3++] == 1 - VarFlaglist[1], "Flip2" + (loc3).ToString());
                    VarFlaglistCopy[0] = NUvar[loc3 - 1];
                    VarFlaglistCopy[1] = VarFlaglist[0];
                    //Vvar[loc2++]=min(1 - VarFlaglist[1], VarFlaglist[0]). Vvar[loc2] corresponds to T_j in Proposition 2.
                    model.AddGenConstrMin(Vvar[loc2++], VarFlaglistCopy, 1000, "FlagTransOxr1" + loc2.ToString());
                    VarFlaglistCopy[0] = NUvar[loc3 - 2];
                    VarFlaglistCopy[1] = VarFlaglist[1];
                    //Vvar[loc2++]=min(1 - VarFlaglist[0], VarFlaglist[1]). Vvar[loc2] corresponds to T_j in Proposition 2.
                    model.AddGenConstrMin(Vvar[loc2++], VarFlaglistCopy, 1000, "FlagTransOxr2" + loc2.ToString());
                    //UFvar[loc1]=Vvar[loc2-1]+Vvar[loc2-2], UFvar[loc1] is the flag variable of f
                    // model.AddConstr(UFvar[loc1] == Vvar[loc2 - 1] + Vvar[loc2 - 2], "FlagTransOxr3" + loc2.ToString());
                    for (j = 0; j < Varlist.Count; j++)
                    {
                        Vartemplist[j] = Vvar[loc2 - 1 - j];
                    }
                    //UFvar[locf]=max(Vvar[loc2],Vvar[loc2-1],...,). is equivalent to UFvar[locf]= Vvar[loc2]+Vvar[loc2-1]+...+
                    //since only one of Vvar[loc2],Vvar[loc2-1],..., is equal to 1.
                    //UFvar[locf] is the flag variable of f, corresponding to F_d in Proposition 2.
                    model.AddGenConstrMax(UFvar[locf], Vartemplist, 0, "FlagTransXor-2" + loc2.ToString());

                    //NUFvar[loc4] =1-UFvar[loc1] and increase loc4 by 1. NUFvar[loc4] corresponds to 1-F_d in Proposition 2.
                    model.AddConstr(NUFvar[loc4++] == 1 - UFvar[locf], "Flip3" + loc4.ToString());
                    //max(u1_fv,u2_fv). bvar[locb] corresponds to b_d in Proposition 2.
                    VarFlaglistCopy[0] = VarFlaglist[0];
                    VarFlaglistCopy[1] = VarFlaglist[1];
                    model.AddGenConstrMax(bvar[locb++], VarFlaglistCopy, 0, "maxfv" + locb.ToString());

                    //NUFvar[loc4] =1-bvar[locb++] and increase loc4 by 1. NUFvar[loc4] corresponds to 1-b_d in Proposition 2.
                    model.AddConstr(NUFvar[loc4++] == 1 - bvar[locb - 1], "Flip3" + loc4.ToString());

                    VarFlaglistCopy[0] = NUFvar[loc4 - 1]; //corresponding to 1-F_d
                    VarFlaglistCopy[1] = NUFvar[loc4 - 2]; //corresponding to 1-b_d
                    VarFlaglistCopy[2] = Uvar[loc1];       //corresponding to beta_d
                    loc1++;
                    //Uvar[loc1]=min(NUFvar[loc4-1],NUFvar[loc4-2],loc1), is the  assigment variable of f.
                    model.AddGenConstrMin(Uvar[loc1], VarFlaglistCopy, 1000, "minav" + loc1.ToString());
                    //model.Update();
                    Tar.AddTerm(1.0, Uvar[loc1]);

                    Tar.AddTerm(-10000, UFvar[locf]);
                    rec_loc1[i] = loc1;
                    rec_locf[i] = locf;
                    loc1++;
                    locf++;
                }
                //in this case, assume that f=u1+u2+u3+c
                if (Varlist.Count == 3)
                {
                    VarlistCopy.Clear();
                    GRBVar[] VarFlaglistCopy  = new GRBVar[4];
                    GRBVar[] VarFlaglistCopy2 = new GRBVar[4];
                    GRBVar[] Vartemplist      = new GRBVar[4];
                    for (j = 0; j < Varlist.Count(); j++)
                    {
                        VarlistCopy.Add(Varlist[j]);
                        VarFlaglistCopy[j] = VarFlaglist[j];
                    }
                    VarlistCopy.Add(Uvar[loc1]);
                    //Uvar[loc1]=u1+u2+u3
                    AddCons3Vars(model, VarlistCopy);
                    //model.AddGenConstrMax(UFvar[loc1], VarFlaglistCopy, 0, "TransFlagXor" + loc1.ToString());

                    //if c==1, namely f has the constant term 1, U[loc1+1]=Uvar[loc]+1
                    if (consflag[i] == 1)
                    {
                        model.AddConstr(Consvar[loc0++] == 1, "Flip" + j.ToString());
                        VarlistCopy[0] = Uvar[loc1];
                        VarlistCopy[1] = Consvar[loc0 - 1];
                        VarlistCopy[2] = Uvar[loc1 + 1];
                        loc1++;
                        AddCons2Vars(model, VarlistCopy);
                    }

                    //filp every variable,
                    for (j = 0; j < Varlist.Count; j++)
                    {
                        model.AddConstr(NUvar[loc3++] == 1 - VarFlaglistCopy[j], "Flip" + j.ToString());
                    }
                    //
                    for (j = 0; j < Varlist.Count; j++)
                    {
                        for (k = 0; k < Varlist.Count; k++)
                        {
                            if (k != j)
                            {
                                Vartemplist[k] = NUvar[loc3 - 1 - (Varlist.Count - 1 - k)];
                            }
                            else
                            {
                                Vartemplist[k] = VarFlaglistCopy[k];
                            }
                        }
                        //Vvar[loc2]=min(1-VarFlaglistCopy[0],1-VarFlaglistCopy[2],...,VarFlaglistCopy[j],1-VarFlaglistCopy[j],...)
                        model.AddGenConstrMin(Vvar[loc2++], Vartemplist, 1000, "FlagTransOxr1" + loc2.ToString());
                    }

                    for (j = 0; j < Varlist.Count; j++)
                    {
                        Vartemplist[j] = Vvar[loc2 - 1 - j];
                    }
                    //Since only one of Vvar[loc2 - 1],Vvar[loc2 - 2],..., is equal to 0 and the remainings are equal to 0s,
                    //UFvar[loc1]=max(Vvar[loc2 - 1],Vvar[loc2 - 2],...,) is equivalently to UFvar[loc1]=Vvar[loc2 - 1]+Vvar[loc2 - 2]+...+
                    //UFvar[loc1]is the fianl flag variable of f
                    model.AddGenConstrMax(UFvar[locf], Vartemplist, 0, "FlagTransXor-2" + loc2.ToString());


                    //NUFvar[loc4] =1-UFvar[loc1] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4++] == 1 - UFvar[locf], "Flip3" + loc4.ToString());
                    //max(u1_fv,u2_fv,u3_fv)
                    VarFlaglistCopy[0] = VarFlaglist[0];
                    VarFlaglistCopy[1] = VarFlaglist[1];
                    VarFlaglistCopy[2] = VarFlaglist[2];
                    model.AddGenConstrMax(bvar[locb++], VarFlaglistCopy, 0, "maxfv" + locb.ToString());

                    //NUFvar[loc4] =1-bvar[locb++] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4++] == 1 - bvar[locb - 1], "Flip3" + loc4.ToString());

                    VarFlaglistCopy2[0] = NUFvar[loc4 - 1];
                    VarFlaglistCopy2[1] = NUFvar[loc4 - 2];
                    VarFlaglistCopy2[2] = Uvar[loc1];
                    loc1++;
                    //Uvar[loc1]=min(NUFvar[loc4-1],NUFvar[loc4-2],loc1), is the  assigment variable of f.
                    model.AddGenConstrMin(Uvar[loc1], VarFlaglistCopy2, 1000, "min_av" + loc1.ToString());


                    Tar.AddTerm(1.0, Uvar[loc1]);
                    Tar.AddTerm(-10000, UFvar[locf]);
                    rec_loc1[i] = loc1;
                    rec_locf[i] = locf;
                    loc1++;
                    locf++;
                }
                //assuming f=u1+u2+u3+u4+c
                if (Varlist.Count == 4)
                {
                    List <GRBVar> templist = new List <GRBVar>()
                    {
                    };
                    GRBVar[] VarFlaglistCopy  = new GRBVar[5];
                    GRBVar[] VarFlaglistCopy2 = new GRBVar[5];
                    GRBVar[] Vartemplist      = new GRBVar[5];

                    for (j = 0; j < Varlist.Count(); j++)
                    {
                        VarFlaglistCopy[j] = VarFlaglist[j];
                    }

                    templist.Add(Varlist[0]);
                    templist.Add(Varlist[1]);
                    templist.Add(Uvar[loc1]);
                    AddCons2Vars(model, templist);

                    templist.Clear();
                    loc1++;
                    templist.Add(Varlist[2]);
                    templist.Add(Varlist[3]);
                    templist.Add(Uvar[loc1]);
                    AddCons2Vars(model, templist);


                    templist.Clear();
                    loc1++;
                    templist.Add(Uvar[loc1 - 2]);
                    templist.Add(Uvar[loc1 - 1]);
                    templist.Add(Uvar[loc1]);
                    AddCons2Vars(model, templist);

                    //if c==1
                    if (consflag[i] == 1)
                    {
                        model.AddConstr(Consvar[loc0++] == 1, "Flip" + j.ToString());
                        VarlistCopy[0] = Uvar[loc1];
                        VarlistCopy[1] = Consvar[loc0 - 1];
                        VarlistCopy[2] = Uvar[loc1 + 1];
                        loc1++;
                        AddCons2Vars(model, VarlistCopy);
                    }


                    for (j = 0; j < Varlist.Count; j++)
                    {
                        model.AddConstr(NUvar[loc3++] == 1 - VarFlaglistCopy[j], "Flip" + j.ToString());
                    }

                    for (j = 0; j < Varlist.Count; j++)
                    {
                        for (k = 0; k < Varlist.Count; k++)
                        {
                            if (k != j)
                            {
                                Vartemplist[k] = NUvar[loc3 - 1 - (Varlist.Count - 1 - k)];
                            }
                            else
                            {
                                Vartemplist[k] = VarFlaglistCopy[k];
                            }
                        }
                        model.AddGenConstrMin(Vvar[loc2++], Vartemplist, 1000, "FlagTransOxr1" + loc2.ToString());
                    }

                    for (j = 0; j < Varlist.Count; j++)
                    {
                        Vartemplist[j] = Vvar[loc2 - 1 - j];
                    }
                    model.AddGenConstrMax(UFvar[locf], Vartemplist, 0, "FlagTransXor-2" + loc2.ToString());

                    //NUFvar[loc4] =1-UFvar[loc1] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4] == 1 - UFvar[locf], "Flip3" + loc4.ToString());
                    loc4++;
                    //max(u1_fv,u2_fv,u3_fv,u4_fv)
                    VarFlaglistCopy[0] = VarFlaglist[0];
                    VarFlaglistCopy[1] = VarFlaglist[1];
                    VarFlaglistCopy[2] = VarFlaglist[2];
                    VarFlaglistCopy[3] = VarFlaglist[3];
                    model.AddGenConstrMax(bvar[locb++], VarFlaglistCopy, 0, "maxfv" + locb.ToString());

                    //NUFvar[loc4] =1-bvar[locb++] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4] == 1 - bvar[locb - 1], "Flip4" + loc4.ToString());
                    loc4++;
                    //VarFlaglistCopy.Initialize();

                    VarFlaglistCopy2[0] = NUFvar[loc4 - 1];
                    VarFlaglistCopy2[1] = NUFvar[loc4 - 2];
                    VarFlaglistCopy2[2] = Uvar[loc1];
                    loc1++;

                    //Uvar[loc1]=min(NUFvar[loc4-1],NUFvar[loc4-2],loc1), is the  assigment variable of f.
                    model.AddGenConstrMin(Uvar[loc1], VarFlaglistCopy2, 1000, "min_av" + loc1.ToString());

                    //Tar=Tar+assigment variable +(-10000)*flag variable
                    Tar.AddTerm(1.0, Uvar[loc1]);
                    Tar.AddTerm(-10000, UFvar[locf]);
                    rec_loc1[i] = loc1;
                    rec_locf[i] = locf;
                    loc1++;
                    locf++;
                }
                //assuming f=u1+u2+u3+u4+u5+c
                if (Varlist.Count == 5)
                {
                    List <GRBVar> templist = new List <GRBVar>()
                    {
                    };
                    GRBVar[] VarFlaglistCopy  = new GRBVar[6];
                    GRBVar[] VarFlaglistCopy2 = new GRBVar[6];
                    GRBVar[] Vartemplist      = new GRBVar[6];

                    for (j = 0; j < Varlist.Count(); j++)
                    {
                        VarFlaglistCopy[j] = VarFlaglist[j];
                    }

                    templist.Add(Varlist[0]);
                    templist.Add(Varlist[1]);
                    templist.Add(Uvar[loc1]);
                    AddCons2Vars(model, templist);

                    templist.Clear();
                    loc1++;
                    templist.Add(Varlist[2]);
                    templist.Add(Varlist[3]);
                    templist.Add(Varlist[4]);
                    templist.Add(Uvar[loc1]);
                    AddCons3Vars(model, templist);


                    templist.Clear();
                    loc1++;
                    templist.Add(Uvar[loc1 - 2]);
                    templist.Add(Uvar[loc1 - 1]);
                    templist.Add(Uvar[loc1]);
                    AddCons2Vars(model, templist);


                    if (consflag[i] == 1)
                    {
                        model.AddConstr(Consvar[loc0++] == 1, "Flip" + j.ToString());
                        VarlistCopy[0] = Uvar[loc1];
                        VarlistCopy[1] = Consvar[loc0 - 1];
                        VarlistCopy[2] = Uvar[loc1 + 1];
                        loc1++;
                        AddCons2Vars(model, VarlistCopy);
                    }

                    for (j = 0; j < Varlist.Count; j++)
                    {
                        model.AddConstr(NUvar[loc3++] == 1 - VarFlaglistCopy[j], "Flip" + j.ToString());
                    }
                    //flip and calculate Tj
                    for (j = 0; j < Varlist.Count; j++)
                    {
                        for (k = 0; k < Varlist.Count; k++)
                        {
                            if (k != j)
                            {
                                Vartemplist[k] = NUvar[loc3 - 1 - (Varlist.Count - 1 - k)];
                            }
                            else
                            {
                                Vartemplist[k] = VarFlaglistCopy[k];
                            }
                        }
                        model.AddGenConstrMin(Vvar[loc2++], Vartemplist, 1000, "FlagTransOxr1" + loc2.ToString());
                    }
                    //
                    for (j = 0; j < Varlist.Count; j++)
                    {
                        Vartemplist[j] = Vvar[loc2 - 1 - j];
                    }
                    model.AddGenConstrMax(UFvar[locf], Vartemplist, 0, "FlagTransXor-2" + loc2.ToString());


                    //NUFvar[loc4] =1-UFvar[loc1] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4++] == 1 - UFvar[locf], "Flip3" + loc4.ToString());
                    //max(u1_fv,u2_fv,u3_fv,u4_fv,u5_fv)
                    VarFlaglistCopy[0] = VarFlaglist[0];
                    VarFlaglistCopy[1] = VarFlaglist[1];
                    VarFlaglistCopy[2] = VarFlaglist[2];
                    VarFlaglistCopy[3] = VarFlaglist[3];
                    VarFlaglistCopy[4] = VarFlaglist[4];
                    model.AddGenConstrMax(bvar[locb++], VarFlaglistCopy, 0, "maxfv" + locb.ToString());

                    //NUFvar[loc4] =1-bvar[locb++] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4++] == 1 - bvar[locb - 1], "Flip4" + loc4.ToString());

                    VarFlaglistCopy2[0] = NUFvar[loc4 - 1];
                    VarFlaglistCopy2[1] = NUFvar[loc4 - 2];
                    VarFlaglistCopy2[2] = Uvar[loc1];
                    loc1++;

                    //Uvar[loc1]=min(NUFvar[loc4-1],NUFvar[loc4-2],loc1), is the  assigment variable of f.
                    model.AddGenConstrMin(Uvar[loc1], VarFlaglistCopy2, 1000, "min_av" + loc1.ToString());
                    Tar.AddTerm(1.0, Uvar[loc1]);

                    Tar.AddTerm(-10000, UFvar[locf]);
                    rec_loc1[i] = loc1;
                    rec_locf[i] = locf;
                    loc1++;
                    locf++;
                }
                //Console.WriteLine("5: " +loc1);
                //assuming that f=u1+u2+u3+u4+u5...+um+c, where m>5.
                if (Varlist.Count > 5)
                {
                    int length;
                    length = Varlist.Count;
                    List <GRBVar> curvarlist = new List <GRBVar>()
                    {
                    };
                    GRBVar[] VarFlaglistCopy  = new GRBVar[Varlist.Count + 1];
                    GRBVar[] VarFlaglistCopy2 = new GRBVar[Varlist.Count + 1];
                    GRBVar[] Vartemplist      = new GRBVar[Varlist.Count + 1];

                    for (j = 0; j < Varlist.Count; j++)
                    {
                        curvarlist.Add(Varlist[j]);
                        VarFlaglistCopy[j] = VarFlaglist[j];
                    }

                    //model f=u1+u2+...+un. The method used here is a slight differnt from that presented in the paper.
                    //The method used here is more convenient for coding.
                    while (length > 3)
                    {
                        List <GRBVar> nextvarlist = new List <GRBVar>()
                        {
                        };
                        List <GRBVar> templist = new List <GRBVar>()
                        {
                        };
                        //
                        templist.Add(curvarlist[0]);
                        templist.Add(curvarlist[1]);
                        templist.Add(curvarlist[2]);
                        templist.Add(Uvar[loc1]);
                        AddCons3Vars(model, templist);

                        for (j = 3; j < curvarlist.Count; j++)
                        {
                            nextvarlist.Add(curvarlist[j]);
                        }
                        nextvarlist.Add(Uvar[loc1]);
                        curvarlist.Clear();
                        for (j = 0; j < nextvarlist.Count; j++)
                        {
                            curvarlist.Add(nextvarlist[j]);
                        }
                        length = curvarlist.Count;
                        loc1++;
                    }

                    if (length == 2)
                    {
                        curvarlist.Add(Uvar[loc1]);
                        AddCons2Vars(model, curvarlist);
                        //if c==1, then we need xor one more constant
                        if (consflag[i] == 1)
                        {
                            model.AddConstr(Consvar[loc0++] == 1, "Flip" + j.ToString());
                            VarlistCopy[0] = Uvar[loc1];
                            VarlistCopy[1] = Consvar[loc0 - 1];
                            VarlistCopy[2] = Uvar[loc1 + 1];
                            loc1++;
                            AddCons2Vars(model, VarlistCopy);
                        }
                    }

                    if (length == 3)
                    {
                        curvarlist.Add(Uvar[loc1]);
                        AddCons3Vars(model, curvarlist);
                        //if c==1, then we need xor one more constant
                        if (consflag[i] == 1)
                        {
                            model.AddConstr(Consvar[loc0++] == 1, "Flip" + j.ToString());
                            VarlistCopy[0] = Uvar[loc1];
                            VarlistCopy[1] = Consvar[loc0 - 1];
                            VarlistCopy[2] = Uvar[loc1 + 1];
                            loc1++;
                            AddCons2Vars(model, VarlistCopy);
                        }
                    }



                    for (j = 0; j < Varlist.Count; j++)
                    {
                        model.AddConstr(NUvar[loc3++] == 1 - VarFlaglistCopy[j], "Flip" + j.ToString());
                    }

                    for (j = 0; j < Varlist.Count; j++)
                    {
                        for (k = 0; k < Varlist.Count; k++)
                        {
                            if (k != j)
                            {
                                Vartemplist[k] = NUvar[loc3 - 1 - (Varlist.Count - 1 - k)];
                            }
                            else
                            {
                                Vartemplist[k] = VarFlaglistCopy[k];
                            }
                        }
                        model.AddGenConstrMin(Vvar[loc2++], Vartemplist, 1000, "FlagTransOxr1" + loc2.ToString());
                    }
                    for (j = 0; j < Varlist.Count; j++)
                    {
                        Vartemplist[j] = Vvar[loc2 - 1 - j];
                    }
                    model.AddGenConstrMax(UFvar[locf], Vartemplist, 0, "FlagTransXor-2" + loc2.ToString());

                    //NUFvar[loc4] =1-UFvar[loc1] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4++] == 1 - UFvar[locf], "Flip3" + loc4.ToString());
                    //bvar[locb]=max(u1_fv,u2_fv,u3_fv,u4_fv,...,un_fv) and increase locb by 1
                    for (j = 0; j < VarFlaglist.Count; j++)
                    {
                        VarFlaglistCopy[j] = VarFlaglist[j];
                    }
                    model.AddGenConstrMax(bvar[locb++], VarFlaglistCopy, 0, "maxfv" + locb.ToString());

                    //NUFvar[loc4] =1-bvar[locb++] and increase loc4 by 1
                    model.AddConstr(NUFvar[loc4++] == 1 - bvar[locb - 1], "Flip4" + loc4.ToString());

                    VarFlaglistCopy2[0] = NUFvar[loc4 - 1];
                    VarFlaglistCopy2[1] = NUFvar[loc4 - 2];
                    VarFlaglistCopy2[2] = Uvar[loc1];
                    loc1++;
                    //Uvar[loc1]=min(NUFvar[loc4-1],NUFvar[loc4-2],loc1), is the  assigment variable of f.
                    model.AddGenConstrMin(Uvar[loc1], VarFlaglistCopy2, 1000, "min_av" + loc1.ToString());

                    Tar.AddTerm(1.0, Uvar[loc1]);
                    Tar.AddTerm(-10000, UFvar[locf]);
                    rec_loc1[i] = loc1;
                    rec_locf[i] = locf;
                    loc1++;
                    locf++;
                }
            }

            //
            GRBLinExpr keyspace = new GRBLinExpr();

            for (i = 0; i < 80; i++)
            {
                keyspace.AddTerm(1.0, kvf[i]);
            }
            Tar.AddConstant((double)con);

            //Add the condition that Tar>=0. It gurantees that there is not any condition whose flag variable is equal to 1.
            model.AddConstr(Tar == 3, "finalcons");
            model.SetObjective(keyspace, GRB.MAXIMIZE);
            //Set the objective function of the model.
            //model.SetObjective(Tar, GRB.MINIMIZE);
            model.Optimize();

            //output the states of key variables and iv variables
            if (model.SolCount > 0)
            {
                sw.WriteLine("****************************Conditions on single key/iv variables***************************\n");
                Console.Write("Free Key bits:\n");
                sw.Write("Free Key bits:\n");
                for (i = 0; i < 80; i++)
                {
                    if ((keyvar[i].X == 0) && (kvf[i].X == 1))
                    {
                        Console.Write(i + ",");
                        sw.Write(i + ",");
                    }
                }
                Console.WriteLine();
                sw.WriteLine();

                Console.Write("Key bits set to 1:\n");
                sw.Write("Key bits set to 1:\n");
                for (i = 0; i < 80; i++)
                {
                    if ((keyvar[i].X == 1) && (kvf[i].X == 0))
                    {
                        Console.Write(i + ",");
                        sw.Write(i + ",");
                    }
                }
                Console.WriteLine();
                sw.WriteLine();

                Console.Write("Key bits set 0:\n");
                sw.Write("Key bits set 0:\n");
                for (i = 0; i < 80; i++)
                {
                    if ((keyvar[i].X == 0) && (kvf[i].X == 0))
                    {
                        Console.Write(i + ",");
                        sw.Write(i + ",");
                    }
                }
                Console.WriteLine();
                sw.WriteLine();

                //for (i = 0; i < loc2; i++)
                //{
                //    if (Vvar[i].X > 1)
                //    {
                //        Console.Write(Vvar[i].VarName + " " + Vvar[i].X + "\n");
                //    }

                //}

                Console.WriteLine("*******************************************************\n");
                sw.WriteLine("*******************************************************\n");

                Console.Write("Free Iv bits:\n");
                sw.Write("Free Iv bits:\n");
                for (i = 0; i < 80; i++)
                {
                    if ((ivvar[i].X == 0) && (vvf[i].X == 1))
                    {
                        Console.Write(i + ",");
                        sw.Write(i + ",");
                    }
                }
                Console.WriteLine();
                sw.WriteLine();

                Console.Write("Iv bits set to 1:\n");
                sw.Write("Iv bits set to 1:\n");
                for (i = 0; i < 80; i++)
                {
                    if ((ivvar[i].X == 1) && (vvf[i].X == 0))
                    {
                        Console.Write(i + ",");
                        sw.Write(i + ",");
                    }
                }
                Console.WriteLine();
                sw.WriteLine();

                Console.Write("Iv bits set to 0:\n");
                sw.Write("Iv bits set to 0:\n");
                for (i = 0; i < 80; i++)
                {
                    if ((ivvar[i].X == 0) && (vvf[i].X == 0))
                    {
                        Console.Write(i + ",");
                        sw.Write(i + ",");
                    }
                }
                Console.WriteLine();
                sw.WriteLine();

                double aa = Tar.Value;
                Console.WriteLine("*****************The number of 1's*****************");
                Console.WriteLine(aa);
                Console.WriteLine("***************************************************");
                sw.WriteLine("***********************The assigment/flag variable of key and iv variables***********************");
                for (i = 0; i < 80; i++)
                {
                    Console.Write(keyvar[i].VarName + "= " + keyvar[i].X + " " + kvf[i].VarName + "= " + kvf[i].X + "\n");
                    sw.Write(keyvar[i].VarName + "= " + keyvar[i].X + " " + kvf[i].VarName + "= " + kvf[i].X + "\n");
                }
                Console.WriteLine("*******************************");
                for (i = 0; i < 80; i++)
                {
                    Console.Write(ivvar[i].VarName + "= " + ivvar[i].X + " " + vvf[i].VarName + "= " + vvf[i].X + "\n");
                    sw.Write(ivvar[i].VarName + "= " + ivvar[i].X + " " + vvf[i].VarName + "= " + vvf[i].X + "\n");
                }
                Console.WriteLine();
                //
                StreamReader rw = new StreamReader("ConSet.txt");
                sw.WriteLine("***************The concrete conditions****************");
                for (j = 0; j < consnum; j++)
                {
                    string onecon = rw.ReadLine();
                    sw.WriteLine(onecon + "=" + Uvar[rec_loc1[j]].X);
                }

                sw.Close();
            }
        }
    static void Main()
    {
        try {
            // Example data:
            //   e.g. {0, n+1, 2} means clause (x0 or ~x1 or x2)
            int[,] Clauses = new int[, ]
            {
                { 0, n + 1, 2 }, { 1, n + 2, 3 },
                { 2, n + 3, 0 }, { 3, n + 0, 1 },
                { n + 0, n + 1, 2 }, { n + 1, n + 2, 3 },
                { n + 2, n + 3, 0 }, { n + 3, n + 0, 1 }
            };

            int i, status;

            // Create environment
            GRBEnv env = new GRBEnv("genconstr_cs.log");

            // Create initial model
            GRBModel model = new GRBModel(env);
            model.ModelName = "genconstr_cs";

            // Initialize decision variables and objective

            GRBVar[] Lit    = new GRBVar[NLITERALS];
            GRBVar[] NotLit = new GRBVar[NLITERALS];
            for (i = 0; i < NLITERALS; i++)
            {
                Lit[i]    = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, string.Format("X{0}", i));
                NotLit[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, string.Format("notX{0}", i));
            }

            GRBVar[] Cla = new GRBVar[NCLAUSES];
            for (i = 0; i < NCLAUSES; i++)
            {
                Cla[i] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, string.Format("Clause{0}", i));
            }

            GRBVar[] Obj = new GRBVar[NOBJ];
            for (i = 0; i < NOBJ; i++)
            {
                Obj[i] = model.AddVar(0.0, 1.0, 1.0, GRB.BINARY, string.Format("Obj{0}", i));
            }

            // Link Xi and notXi
            GRBLinExpr lhs;
            for (i = 0; i < NLITERALS; i++)
            {
                lhs = new GRBLinExpr();
                lhs.AddTerm(1.0, Lit[i]);
                lhs.AddTerm(1.0, NotLit[i]);
                model.AddConstr(lhs, GRB.EQUAL, 1.0, string.Format("CNSTR_X{0}", i));
            }

            // Link clauses and literals
            for (i = 0; i < NCLAUSES; i++)
            {
                GRBVar[] clause = new GRBVar[3];
                for (int j = 0; j < 3; j++)
                {
                    if (Clauses[i, j] >= n)
                    {
                        clause[j] = NotLit[Clauses[i, j] - n];
                    }
                    else
                    {
                        clause[j] = Lit[Clauses[i, j]];
                    }
                }
                model.AddGenConstrOr(Cla[i], clause, string.Format("CNSTR_Clause{0}", i));
            }

            // Link objs with clauses
            model.AddGenConstrMin(Obj[0], Cla, GRB.INFINITY, "CNSTR_Obj0");
            lhs = new GRBLinExpr();
            for (i = 0; i < NCLAUSES; i++)
            {
                lhs.AddTerm(1.0, Cla[i]);
            }
            model.AddGenConstrIndicator(Obj[1], 1, lhs, GRB.GREATER_EQUAL, 4.0, "CNSTR_Obj1");

            // Set global objective sense
            model.ModelSense = GRB.MAXIMIZE;

            // Save problem
            model.Write("genconstr_cs.mps");
            model.Write("genconstr_cs.lp");

            // Optimize
            model.Optimize();

            // Status checking
            status = model.Status;

            if (status == GRB.Status.INF_OR_UNBD ||
                status == GRB.Status.INFEASIBLE ||
                status == GRB.Status.UNBOUNDED)
            {
                Console.WriteLine("The model cannot be solved " +
                                  "because it is infeasible or unbounded");
                return;
            }
            if (status != GRB.Status.OPTIMAL)
            {
                Console.WriteLine("Optimization was stopped with status {0}", status);
                return;
            }

            // Print result
            double objval = model.ObjVal;

            if (objval > 1.9)
            {
                Console.WriteLine("Logical expression is satisfiable");
            }
            else if (objval > 0.9)
            {
                Console.WriteLine("At least four clauses can be satisfied");
            }
            else
            {
                Console.WriteLine("Not even three clauses can be satisfied");
            }

            // Dispose of model and environment
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: {0}. {1}", e.ErrorCode, e.Message);
        }
    }
        //linearize a condition f. For example, f=k1+k2*k3+k4, it would return f=u1+u2+u3, where u1=k1, u2=k2*k3, u3=k4
        //Furthermore, this function could identify if f has a constant term. If so, it would mark out that f has a constant term in the array consflag.
        static public List <GRBVar> LinearConstrain(GRBModel m, GRBVar[] Uvar, GRBVar[] UFvar, UInt32[, ,] ConS, int[] conlen, int ind, GRBVar[] keyvar, GRBVar[] ivvar, int[] loc1, GRBVar[] kvf, GRBVar[] vvf, GRBVar[] Wvar, int[] locw, List <GRBVar> LinFlagRes)
        {
            List <GRBVar> LinVar = new List <GRBVar>()
            {
            };
            List <GRBVar> LinVarFlag = new List <GRBVar>()
            {
            };
            List <GRBVar> TempVarFlag = new List <GRBVar>()
            {
            };                                                //store the flag variables of the variables appearing in a monimial
            List <GRBVar> TempVar = new List <GRBVar>()
            {
            };                                            //store the assignment variables of the variables appearing in a monimial
            int weight = 0;
            int i, j, k;
            int t;
            int loc   = 0;
            int locf  = 0;
            int loc_w = locw[0];

            loc  = loc1[0];
            locf = loc1[1];
            for (j = 0; j < conlen[ind]; j++)
            {
                //for the j-th monomial
                //Cons[ind,j,0-2]stores the iv variable appearing in the monimial
                //Cons[ind,j,3-5]stores the key variable appearing in the monimial
                weight = 0;
                TempVar.Clear();
                TempVarFlag.Clear();
                for (i = 0; i < 96; i++)
                {
                    t      = (int)((ConS[ind, j, (i >> 5)] >> (i & 0x1f)) & 0x01);
                    weight = weight + t;
                    if (t == 1)
                    {
                        TempVar.Add(ivvar[i]);
                        TempVarFlag.Add(vvf[i]);
                    }
                }
                for (i = 0; i < 96; i++)
                {
                    t      = (int)((ConS[ind, j, (i >> 5) + 3] >> (i & 0x1f)) & 0x01);
                    weight = weight + t;
                    if (t == 1)
                    {
                        TempVar.Add(keyvar[i]);
                        TempVarFlag.Add(kvf[i]);
                    }
                }
                //if weight ==1, it is a monomial of degree one, then we only replace it with a new variable
                if (weight == 1)
                {
                    m.AddConstr(Uvar[loc] == TempVar[0], "SingleBit" + loc.ToString());
                    m.AddConstr(UFvar[locf] == TempVarFlag[0], "SingleBit" + loc.ToString());
                    LinVar.Add(Uvar[loc]);
                    LinFlagRes.Add(UFvar[locf]);
                    loc++;
                    locf++;
                }
                //if weight>1, then it is a nonlinear monomial and we should linearize it accroding to the rule of AND in our paper.
                if (weight > 1)
                {
                    GRBVar[] PickVars = new GRBVar[TempVar.Count];
                    for (i = 0; i < TempVar.Count; i++)
                    {
                        PickVars[i] = TempVar[i];

                        //Wvar[loc_w+i]= TempVar[i]^TempVarFlag[i]
                        GRBVar[] tempor = new GRBVar[2] {
                            TempVar[i], TempVarFlag[i]
                        };
                        m.AddGenConstrOr(Wvar[loc_w + i], tempor, "tempor" + i.ToString());//变元与变元的标志相或
                    }
                    GRBVar[] tempm = new GRBVar[TempVar.Count];


                    //Wvar[loc_w+l]=min(Wvar[loc_w],Wvar[loc_w+1],...,Wvar[loc_w+l-1]), where l is the number variables appearing in the monomial
                    for (i = 0; i < TempVar.Count; i++)
                    {
                        tempm[i] = Wvar[loc_w + i];
                    }
                    loc_w = loc_w + TempVar.Count;
                    m.AddGenConstrMin(Wvar[loc_w], tempm, 1, "linmin" + loc_w.ToString());
                    loc_w++;


                    for (i = 0; i < TempVar.Count; i++)
                    {
                        tempm[i] = TempVarFlag[i];
                    }
                    //Wvar[loc_w]=max(TempVarFlag[0],TempVarFlag[1],...,TempVarFlag[l-1]), where l is the number variables appearing in the monomial
                    m.AddGenConstrMax(Wvar[loc_w], tempm, 0, "linmaxFlag" + loc_w.ToString());
                    loc_w++;


                    GRBVar[] tempminFlag = new GRBVar[2] {
                        Wvar[loc_w - 1], Wvar[loc_w - 2]
                    };
                    //UFvar[loc] is the final flag variable of the monomial
                    m.AddGenConstrMin(UFvar[locf], tempminFlag, 1, "minFlag" + loc.ToString());
                    //Uvar[loc] is the final assignment variable of the monomial
                    m.AddGenConstrMin(Uvar[loc], PickVars, 0, "Linearity" + loc.ToString());
                    LinVar.Add(Uvar[loc]);
                    LinFlagRes.Add(UFvar[locf]);
                    loc++;
                    locf++;
                }
            }
            loc1[0] = loc;
            loc1[1] = locf;
            locw[0] = loc_w;
            return(LinVar);
        }
예제 #4
0
    static void Main()
    {
        try {
            // Sample data
            // Sets of days and workers
            string[] Shifts =
                new string[]  { "Mon1", "Tue2", "Wed3", "Thu4", "Fri5", "Sat6",
                                "Sun7", "Mon8", "Tue9", "Wed10", "Thu11", "Fri12", "Sat13",
                                "Sun14" };
            string[] Workers =
                new string[] { "Amy", "Bob", "Cathy", "Dan", "Ed", "Fred", "Gu", "Tobi" };

            int nShifts  = Shifts.Length;
            int nWorkers = Workers.Length;

            // Number of workers required for each shift
            double[] shiftRequirements =
                new double[] { 3, 2, 4, 4, 5, 6, 5, 2, 2, 3, 4, 6, 7, 5 };

            // Worker availability: 0 if the worker is unavailable for a shift
            double[,] availability =
                new double[, ] {
                { 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1 },
                { 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 },
                { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
                { 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
                { 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1 },
                { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1 },
                { 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1 },
                { 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
            };

            // Create environment
            GRBEnv env = new GRBEnv();

            // Create initial model
            GRBModel model = new GRBModel(env);
            model.ModelName = "workforce5_cs";

            // Initialize assignment decision variables:
            // x[w][s] == 1 if worker w is assigned to shift s.
            // This is no longer a pure assignment model, so we must
            // use binary variables.
            GRBVar[,] x = new GRBVar[nWorkers, nShifts];
            for (int w = 0; w < nWorkers; ++w)
            {
                for (int s = 0; s < nShifts; ++s)
                {
                    x[w, s] =
                        model.AddVar(0, availability[w, s], 0, GRB.BINARY,
                                     string.Format("{0}.{1}", Workers[w], Shifts[s]));
                }
            }

            // Slack variables for each shift constraint so that the shifts can
            // be satisfied
            GRBVar[] slacks = new GRBVar[nShifts];
            for (int s = 0; s < nShifts; ++s)
            {
                slacks[s] =
                    model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                 string.Format("{0}Slack", Shifts[s]));
            }

            // Variable to represent the total slack
            GRBVar totSlack = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                           "totSlack");

            // Variables to count the total shifts worked by each worker
            GRBVar[] totShifts = new GRBVar[nWorkers];
            for (int w = 0; w < nWorkers; ++w)
            {
                totShifts[w] = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                            string.Format("{0}TotShifts", Workers[w]));
            }

            GRBLinExpr lhs;

            // Constraint: assign exactly shiftRequirements[s] workers
            // to each shift s, plus the slack
            for (int s = 0; s < nShifts; ++s)
            {
                lhs = new GRBLinExpr();
                lhs.AddTerm(1.0, slacks[s]);
                for (int w = 0; w < nWorkers; ++w)
                {
                    lhs.AddTerm(1.0, x[w, s]);
                }
                model.AddConstr(lhs, GRB.EQUAL, shiftRequirements[s], Shifts[s]);
            }

            // Constraint: set totSlack equal to the total slack
            lhs = new GRBLinExpr();
            lhs.AddTerm(-1.0, totSlack);
            for (int s = 0; s < nShifts; ++s)
            {
                lhs.AddTerm(1.0, slacks[s]);
            }
            model.AddConstr(lhs, GRB.EQUAL, 0, "totSlack");

            // Constraint: compute the total number of shifts for each worker
            for (int w = 0; w < nWorkers; ++w)
            {
                lhs = new GRBLinExpr();
                lhs.AddTerm(-1.0, totShifts[w]);
                for (int s = 0; s < nShifts; ++s)
                {
                    lhs.AddTerm(1.0, x[w, s]);
                }
                model.AddConstr(lhs, GRB.EQUAL, 0, string.Format("totShifts{0}", Workers[w]));
            }

            // Constraint: set minShift/maxShift variable to less <=/>= to the
            // number of shifts among all workers
            GRBVar minShift = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                           "minShift");
            GRBVar maxShift = model.AddVar(0, GRB.INFINITY, 0, GRB.CONTINUOUS,
                                           "maxShift");
            model.AddGenConstrMin(minShift, totShifts, GRB.INFINITY, "minShift");
            model.AddGenConstrMax(maxShift, totShifts, -GRB.INFINITY, "maxShift");

            // Set global sense for ALL objectives
            model.ModelSense = GRB.MINIMIZE;

            // Set primary objective
            model.SetObjectiveN(totSlack, 0, 2, 1.0, 2.0, 0.1, "TotalSlack");

            // Set secondary objective
            model.SetObjectiveN(maxShift - minShift, 1, 1, 1.0, 0, 0, "Fairness");

            // Save problem
            model.Write("workforce5_cs.lp");

            // Optimize
            int status = solveAndPrint(model, totSlack, nWorkers, Workers, totShifts);

            if (status != GRB.Status.OPTIMAL)
            {
                return;
            }

            // Dispose of model and environment
            model.Dispose();
            env.Dispose();
        } catch (GRBException e) {
            Console.WriteLine("Error code: {0}. {1}", e.ErrorCode, e.Message);
        }
    }