/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   Packing problem.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

:- use_module(library(clpb)).
:- use_module(library(clpfd)).

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   R-tetromino
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

tile([[1,1,1,1]], r).

tile([[1],
      [1],
      [1],
      [1]], r).

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   S-tetromino
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

tile([[1,1],
      [1,1]], s).

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   T-tetromino
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

tile([[0,1,0],
      [1,1,1]], t).

tile([[1,0],
      [1,1],
      [1,0]], t).

tile([[1,1,1],
      [0,1,0]], t).

tile([[0,1],
      [1,1],
      [0,1]], t).

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   L-tetromino
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

tile([[1,0,0],
      [1,1,1]], l).


tile([[0,0,1],
      [1,1,1]], l).

tile([[1,1],
      [1,0],
      [1,0]], l).

tile([[1,1,1],
      [0,0,1]], l).

tile([[0,1],
      [0,1],
      [1,1]], l).

tile([[1,1],
      [0,1],
      [0,1]], l).


tile([[1,0],
      [1,0],
      [1,1]], l).


tile([[1,1,1],
      [1,0,0]], l).

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   Basic consistency check: No tile accidentally defined twice.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

%?- findall(Tile-Type, tile(Tile,Type), TTs), \+ ( select(T, TTs, TTs1), memberchk(T, TTs1)).

%?- main.

main :-
        matrix(4, 4, RowsTypes),
        pairs_keys_values(RowsTypes, Rows, Types),
        same_length(Rows, Vs),
        pairs_keys_values(TypesVs0, Types, Vs),
        keysort(TypesVs0, TypesVs),
        group_pairs_by_key(TypesVs, Groups),
        transpose(Rows, Cols),
        phrase((groups_numbers(Groups),
                all_cardinalities(Cols, Vs)), Cs),
        (   maplist(sat, Cs) -> writeln('yes.')
        ;   writeln('no.')
        ).

groups_numbers([]) --> [].
groups_numbers([Group-Vs|GVs]) -->
        { call(Group, N) },
        [card([N], Vs)],
        groups_numbers(GVs).

all_cardinalities([], _) --> [].
all_cardinalities([Col|Cols], Vs) -->
        { pairs_keys_values(Pairs0, Col, Vs),
          include(key_one, Pairs0, Pairs),
          pairs_values(Pairs, Cs) },
        [card([1], Cs)],
        all_cardinalities(Cols, Vs).

key_one(1-_).


matrix(M, N, Ms) :-
        Squares #= M*N,
        length(Ls, Squares),
        findall(Ls-Type, line(N,Ls,Type), Ms0),
        sort(Ms0, Ms).

line(N, Ls, Type) :-
        tile(Ts, Type),
        length(Ls, Max),
        phrase((zeros(0,P0),tile_(Ts,N,Max,P0,P1),zeros(P1,_)), Ls).

%?- line(4, Ls).

%?- matrix(4,4,Ms), maplist(writeln, Ms).

tile_([], _, _, P, P) --> [].
tile_([T|Ts], N, Max, P0, P) -->
        tile_part(T, N, P0, P1),
        { (P1 - 1) mod N #>= P0 mod N,
          P2 #= min(P0 + N, Max) },
        zeros(P1, P2),
        tile_(Ts, N, Max, P2, P).

tile_part([], _, P, P) --> [].
tile_part([L|Ls], N, P0, P) -->
        [L],
        { P1 #= P0 + 1 },
        tile_part(Ls, N, P1, P).

zeros(P, P) --> [].
zeros(P0, P) --> [0],
        { P1 #= P0 + 1 },
        zeros(P1, P).

%?- matrix(4, 4, Ms), maplist(writeln, Ms).