/*
         * public SudokuSolver(string[] lines)
         * {
         *  CreateMatrix();
         *  Apply(lines);
         * }
         */

        void CreateMatrix(SudokuGrid grid)
        {
            // Create requirements - columns of 0/1 matrix
            int num_cages = grid.NumCages;
            int diags     = 0;

            if (grid.MajorDiagonal)
            {
                ++diags;
            }
            if (grid.MinorDiagonal)
            {
                ++diags;
            }
            int d = grid.MajorDiagonal ? 1 : 0; // Where minor diagonal is found

            CreateRequirements(grid, Cells * Cells * 4 + diags * Cells);
            CreateOptionals(grid, num_cages);
            Houses[] houses = new Houses[] {
                Houses.Cell,
                Houses.Column,
                Houses.Row,
                Houses.Box
            };
            for (int i0 = 0; i0 < Cells; ++i0)
            {
                for (int i1 = 0; i1 < Cells; ++i1)
                {
                    for (int type = 0; type < 4; ++type)
                    {
                        Houses            house = houses[type];
                        SudokuRequirement c     = GetRequirement(i0, i1, house);
                        c.i0    = i0;
                        c.i1    = i1;
                        c.house = house;
                        requirements.AddRequirement(c);
                    }
                }
            }
            if (grid.MajorDiagonal)
            {
                for (int i0 = 0; i0 < Cells; ++i0)
                {
                    SudokuRequirement c = GetDiagonalRequirement(0, i0);
                    c.i0    = i0;
                    c.i1    = -1;
                    c.house = Houses.MajorDiagonal;
                    requirements.AddRequirement(c);
                }
            }
            if (grid.MinorDiagonal)
            {
                for (int i0 = 0; i0 < Cells; ++i0)
                {
                    SudokuRequirement c = GetDiagonalRequirement(d, i0);
                    c.i0    = i0;
                    c.i1    = -1;
                    c.house = Houses.MinorDiagonal;
                    requirements.AddRequirement(c);
                }
            }
            for (int i0 = 0; i0 < num_cages; ++i0)
            {
                for (int i1 = 0; i1 < Cells; ++i1)
                {
                    CageOptional ca = GetCageOptional(i0, i1);
                    ca.i0    = i0;
                    ca.i1    = i1;
                    ca.house = Houses.Cage;
                    //optionals.AddRequirement(ca);
                    ca.SetIncluded();
                }
            }

            // Create candidates - rows of 0/1 matrix
            CreateCandidates(new SudokuCandidate[Cells * Cells * Cells]);
            CreateSolution(Cells * Cells);
            for (int x = 0; x < Cells; ++x)
            {
                for (int y = 0; y < Cells; ++y)
                {
                    int cage = grid.CageAt(x, y);
                    int box  = grid.BoxAt(x, y);
                    for (int n = 0; n < Cells; ++n)
                    {
                        SudokuCandidate k = new SudokuCandidate(x, y, n, box, cage);
                        k.AddCandidate(GetRequirement(x, y, Houses.Cell));
                        k.AddCandidate(GetRequirement(x, n, Houses.Column));
                        k.AddCandidate(GetRequirement(y, n, Houses.Row));
                        k.AddCandidate(GetRequirement(box, n, Houses.Box));
                        if (grid.MajorDiagonal && x == y)
                        {
                            k.AddCandidate(GetDiagonalRequirement(0, n));
                        }
                        if (grid.MinorDiagonal && x == Cells - y - 1)
                        {
                            k.AddCandidate(GetDiagonalRequirement(d, n));
                        }
                        if (cage != -1)
                        {
                            k.AddCageOptional(GetCageOptional(cage, n));
                        }
                        tr[trc++] = k;

                        // Discard candidates where cage size already reached 1
                        //if (cage != -1 && grid.cageInfo.sizes[cage] == 1 && n + 1 != grid.cageInfo.totals[cage])
                        //    DiscardCandidate(k);
                    }
                }
            }

            // Discard candidates that do not meet cage total
            for (int i0 = 0; i0 < num_cages; ++i0)
            {
                CageOptional.CheckRemains(this, grid.cageInfo, i0, -1);
            }

            /*
             * if (grid.IsKiller)
             * {
             *  SudokuGrid.CageInfo info = grid.cageInfo;
             *  for (int ca = 0; ca < info.cages.Length; ++ca)
             *  {
             *      List<Point> pts = new List<Point>();
             *      for (int x = 0; x < Cells; ++x)
             *          for (int y = 0; y < Cells-1; ++y)
             *              if (info.cages[x, y] == ca)
             *                  pts.Add(new Point(x, y));
             *      int[] ns = new int[Cells];
             *      for (int i = 0; i < Cells; ++i) ns[i] = i;
             *      Candidate k = new Candidate();
             *      KillerRequire(k, info.totals[ca], pts.ToArray(), ns);
             *  }
             * }
             */
        }