>>> *** Source:
    func ToDigit(ch) :=
      ch - "1"[0] if "1"[0] <= ch <= "9"[0] else
      -1;
    Sudoku := module {
        const M := 3;
        const N := M * M;
        const NumCells := N * N;
        const NumMoves := NumCells * N;
        const Moves := Range(NumMoves)
            ->ForEach(as Id,
                With(cell: Id div N, row: cell div N, col: cell mod N,
                    { Id, XRow: row, XCol: col, YBlk: row div M * M + col div M, ZVal: Id mod N }));
        const MovesByRowCol := Moves->GroupBy([key] _:XRow, [key] _:XCol);
        const MovesByValRow := Moves->GroupBy([key] _:ZVal, [key] _:XRow);
        const MovesByValCol := Moves->GroupBy([key] _:ZVal, [key] _:XCol);
        const MovesByValBlk := Moves->GroupBy([key] _:ZVal, [key] _:YBlk);
        param Fixed :=
            "    2  7 " &
            "    34   " &
            "358      " &
            "5 48     " &
            "   1   89" &
            "  2     6" &
            "24    7  " &
            " 9   52  " &
            "    671  ";
        const ImposedIds :=
            Range(NumCells)
            ->{ cell: it, value: Fixed[it]->ToDigit() }
            ->TakeIf(0 <= value < N)
            ->Map(N * cell + value);
        const NumImposed := ImposedIds->Count();
        const ImposedFlags := Tensor.From(Range(NumMoves)->(it in ImposedIds));
        // Define a variable (tensor of flags) for the moves that have been made.
        var Flags from Tensor.Fill(false, NumMoves) def ImposedFlags;
        // Need to maximize the number of moves made without violating constraints.
        // REVIEW: This should be linearizable!
        // msr NumMade := Sum(m:Range(NumMoves), Flags[m]);
        msr NumMade := Flags.Values->Sum();
        // REVIEW: All of these should work!
        // con NeedImposed  := Flags.Values >= ImposedFlags.Values;
        // con NeedImposed  := ForEach(a:Flags.Values, b:ImposedFlags.Values, a >= b);
        // con NeedImposed  := ForEach(m:Moves, Flags[Id] >= ImposedFlags[Id]);
        con NeedImposed := ForEach(id:ImposedIds, Flags[id]);
        // con NeedImposed := ForEach(id:ImposedIds, Flags[id] = 1);
        con OnePerRowCol := ForEach(c:MovesByRowCol, Sum(c, Flags[Id]) <= 1);
        con OnePerValRow := ForEach(c:MovesByValRow, Sum(c, Flags[Id]) <= 1);
        con OnePerValCol := ForEach(c:MovesByValCol, Sum(c, Flags[Id]) <= 1);
        con OnePerValBlk := ForEach(c:MovesByValBlk, Sum(c, Flags[Id]) <= 1);
        const Symbols := "_123456789";
        let Board :=
            Moves
            ->TakeIf(Flags[Id])
            ->SortUp(XRow, XCol)
            ->GroupBy(_:XRow)
            ->ForEach(With(row: it,
                ForEach(c: Range(N), With(i: First(row, XCol = c).ZVal + 1 ?? 0, Symbols[i:*1])))
                ->Text.Concat("|"));
    };
    // Display the (initial) board.
    Sudoku.NumMade;
    Sudoku.Board;
    // Solve.
    Sln := Sudoku->Maximize(NumMade);
    Sln.NumMade;
    Sln.Board;

>>> *** Instructions:
   0) [0] DefineFunc ToDigit(ch) <- ch - "1"[0] if "1"[0] $<= ch $<= "9"[0] else -1
   1) [0] Define Sudoku <- module { const M := 3; const N := M * M; const NumCells := N * N; const NumMoves := NumCells * N; const Moves := Range(NumMoves)->ForEach(as Id, With(cell : Id div N, row : cell div N, col : cell mod N, { Id, XRow : row, XCol : col, YBlk : row div M * M + col div M, ZVal : Id mod N })); const MovesByRowCol := Moves->GroupBy([key] _ : XRow, [key] _ : XCol); const MovesByValRow := Moves->GroupBy([key] _ : ZVal, [key] _ : XRow); const MovesByValCol := Moves->GroupBy([key] _ : ZVal, [key] _ : XCol); const MovesByValBlk := Moves->GroupBy([key] _ : ZVal, [key] _ : YBlk); param Fixed := "    2  7 " & "    34   " & "358      " & "5 48     " & "   1   89" & "  2     6" & "24    7  " & " 9   52  " & "    671  "; const ImposedIds := Range(NumCells)->{ cell : it, value : Fixed[it]->ToDigit() }->TakeIf(0 $<= value $< N)->Map(N * cell + value); const NumImposed := ImposedIds->Count(); const ImposedFlags := Tensor.From(Range(NumMoves)->(it in ImposedIds)); var Flags from Tensor.Fill(false, NumMoves) def ImposedFlags; msr NumMade := Flags.Values->Sum(); con NeedImposed := ForEach(id : ImposedIds, Flags[id]); con OnePerRowCol := ForEach(c : MovesByRowCol, Sum(c, Flags[Id]) $<= 1); con OnePerValRow := ForEach(c : MovesByValRow, Sum(c, Flags[Id]) $<= 1); con OnePerValCol := ForEach(c : MovesByValCol, Sum(c, Flags[Id]) $<= 1); con OnePerValBlk := ForEach(c : MovesByValBlk, Sum(c, Flags[Id]) $<= 1); const Symbols := "_123456789"; let Board := Moves->TakeIf(Flags[Id])->SortUp(XRow, XCol)->GroupBy(_ : XRow)->ForEach(With(row : it, ForEach(c : Range(N), With(i : First(row, XCol @= c).ZVal + 1 ?? 0, Symbols[i:*1])))->Text.Concat("|")) }
   2) [0] Expr Sudoku.NumMade
   3) [0] Expr Sudoku.Board
   4) [0] Define Sln <- Sudoku->Maximize(NumMade)
   5) [0] Expr Sln.NumMade
   6) [0] Expr Sln.Board
   7) [0] End

>    0) [0] DefineFunc ToDigit(ch) <- ch - "1"[0] if "1"[0] $<= ch $<= "9"[0] else -1
UDF 'ToDigit' has arity 1
>    1) [0] Define Sudoku <- module { const M := 3; const N := M * M; const NumCells := N * N; const NumMoves := NumCells * N; const Moves := Range(NumMoves)->ForEach(as Id, With(cell : Id div N, row : cell div N, col : cell mod N, { Id, XRow : row, XCol : col, YBlk : row div M * M + col div M, ZVal : Id mod N })); const MovesByRowCol := Moves->GroupBy([key] _ : XRow, [key] _ : XCol); const MovesByValRow := Moves->GroupBy([key] _ : ZVal, [key] _ : XRow); const MovesByValCol := Moves->GroupBy([key] _ : ZVal, [key] _ : XCol); const MovesByValBlk := Moves->GroupBy([key] _ : ZVal, [key] _ : YBlk); param Fixed := "    2  7 " & "    34   " & "358      " & "5 48     " & "   1   89" & "  2     6" & "24    7  " & " 9   52  " & "    671  "; const ImposedIds := Range(NumCells)->{ cell : it, value : Fixed[it]->ToDigit() }->TakeIf(0 $<= value $< N)->Map(N * cell + value); const NumImposed := ImposedIds->Count(); const ImposedFlags := Tensor.From(Range(NumMoves)->(it in ImposedIds)); var Flags from Tensor.Fill(false, NumMoves) def ImposedFlags; msr NumMade := Flags.Values->Sum(); con NeedImposed := ForEach(id : ImposedIds, Flags[id]); con OnePerRowCol := ForEach(c : MovesByRowCol, Sum(c, Flags[Id]) $<= 1); con OnePerValRow := ForEach(c : MovesByValRow, Sum(c, Flags[Id]) $<= 1); con OnePerValCol := ForEach(c : MovesByValCol, Sum(c, Flags[Id]) $<= 1); con OnePerValBlk := ForEach(c : MovesByValBlk, Sum(c, Flags[Id]) $<= 1); const Symbols := "_123456789"; let Board := Moves->TakeIf(Flags[Id])->SortUp(XRow, XCol)->GroupBy(_ : XRow)->ForEach(With(row : it, ForEach(c : Range(N), With(i : First(row, XCol @= c).ZVal + 1 ?? 0, Symbols[i:*1])))->Text.Concat("|")) }
Global 'Sudoku' has DType: M{let Board:s*, param Fixed:s, var Flags:b[*], const ImposedFlags:b[*], const ImposedIds:i8*, const M:i8, const Moves:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}*, const MovesByRowCol:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const MovesByValBlk:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const MovesByValCol:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const MovesByValRow:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const N:i8, con NeedImposed:b*, const NumCells:i8, const NumImposed:i8, msr NumMade:i8, const NumMoves:i8, con OnePerRowCol:b*, con OnePerValBlk:b*, con OnePerValCol:b*, con OnePerValRow:b*, const Symbols:s}, SysType: RuntimeModule<{Seq<str>,str,Ten<bool>,Ten<bool>,Seq<i8>,i8,Seq<{i8,i8,i8,i8,i8}>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,i8,Seq<bool>,i8,i8,i8,i8,Seq<bool>,Seq<bool>,Seq<bool>,...},(i8,i8,i8,i8,Seq<{i8,i8,i8,i8,i8}>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,str,Seq<i8>,i8,Ten<bool>,Ten<bool>,Ten<bool>,i8,Seq<bool>,Seq<bool>,Seq<bool>,Seq<bool>,...)>
>    2) [0] Expr Sudoku.NumMade
24
>    3) [0] Expr Sudoku.Board
Seq<str>
   0) _|_|_|_|2|_|_|7|_
   1) _|_|_|_|3|4|_|_|_
   2) 3|5|8|_|_|_|_|_|_
   3) 5|_|4|8|_|_|_|_|_
   4) _|_|_|1|_|_|_|8|9
   5) _|_|2|_|_|_|_|_|6
   6) 2|4|_|_|_|_|7|_|_
   7) _|9|_|_|_|5|2|_|_
   8) _|_|_|_|6|7|1|_|_
>    4) [0] Define Sln <- Sudoku->Maximize(NumMade)
Solver: HiGHS
Global 'Sln' has DType: M{let Board:s*, param Fixed:s, var Flags:b[*], const ImposedFlags:b[*], const ImposedIds:i8*, const M:i8, const Moves:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}*, const MovesByRowCol:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const MovesByValBlk:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const MovesByValCol:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const MovesByValRow:{Id:i8, XCol:i8, XRow:i8, YBlk:i8, ZVal:i8}**, const N:i8, con NeedImposed:b*, const NumCells:i8, const NumImposed:i8, msr NumMade:i8, const NumMoves:i8, con OnePerRowCol:b*, con OnePerValBlk:b*, con OnePerValCol:b*, con OnePerValRow:b*, const Symbols:s}?, SysType: RuntimeModule<{Seq<str>,str,Ten<bool>,Ten<bool>,Seq<i8>,i8,Seq<{i8,i8,i8,i8,i8}>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,i8,Seq<bool>,i8,i8,i8,i8,Seq<bool>,Seq<bool>,Seq<bool>,...},(i8,i8,i8,i8,Seq<{i8,i8,i8,i8,i8}>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,Seq<Seq<{i8,i8,i8,i8,i8}>>,str,Seq<i8>,i8,Ten<bool>,Ten<bool>,Ten<bool>,i8,Seq<bool>,Seq<bool>,Seq<bool>,Seq<bool>,...)>
>    5) [0] Expr Sln.NumMade
81
>    6) [0] Expr Sln.Board
Seq<str>
   0) 4|6|1|5|2|8|9|7|3
   1) 7|2|9|6|3|4|8|5|1
   2) 3|5|8|7|1|9|6|4|2
   3) 5|1|4|8|9|6|3|2|7
   4) 6|7|3|1|5|2|4|8|9
   5) 9|8|2|4|7|3|5|1|6
   6) 2|4|6|9|8|1|7|3|5
   7) 1|9|7|3|4|5|2|6|8
   8) 8|3|5|2|6|7|1|9|4
>    7) [0] End

