abolish_table_info :- at(compress/2).

time :- statistics(runtime,_), benchmark, fail;
        statistics(runtime,[_,T]), write(T).

benchmark :- data(Data), compress(Data, _C).

test :-
    data(Data), reinit,
    cputime(T0),
    compress(Data, C), write(C), write(' '),
    cputime(T1), T is T1-T0, write(T), write(' msecs'), nl, fail.
test.

%% data([a,a,b,a,a,b]).
%% data([a,a,b,a,a,b,a,a,b]).
%% data([a,a,a,a,a,a,a,a,a,a,a,a]).
%% data([a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a]).
%% data([a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a]).
%% data(D) :- bigdata(D).
%% data(D2) :- bigdata(D), append(D,D,D2).
data(D4) :- bigdata(D), append(D,D,D2), append(D2,D2,D4).
%% data(D8) :- bigdata(D), append(D,D,D2), append(D2,D2,D4), append(D4,D4,D8).
%% data(D16) :- bigdata(D), append(D,D,D2), append(D2,D2,D4), append(D4,D4,D8), append(D8,D8,D16).

bigdata([x,c,a,a,b,a,a,b,a,a,b,c,c,a,d,a,d,c,a,a,b,a,a,b,a,a,b,c,c,a,d,a,d,y]).

%% ==================== VERSION USING ASSERT FOR TABLES ======================
%% reinit :-
%%	retractall(memo_compress(_, _)),
%%	assert(memo_compress([C1], [C1])),
%%	assert(memo_compress([C1,C2], [C1,C2])).
%%
%% compress(Initial, Compressed) :-
%%	( memo_compress(Initial, Compressed) -> true
%%	  mlength(Initial, LenInitial),
%%	  CurrentBest = Initial,
%%	  LenCurrentBest = LenInitial,
%%	  compress4(Initial, CurrentBest, LenCurrentBest, Compressed)
%%	  assert(memo_compress(Initial, Compressed))
%%	).
%% ===========================================================================

reinit :- abolish_all_tables.

:- table compress/2.

compress(Initial, Compressed) :-
	mlength(Initial, LenInitial),
	CurrentBest = Initial,
	LenCurrentBest = LenInitial,
	compress4(Initial, CurrentBest, LenCurrentBest, Compressed).

compress4(Initial, CurrentBest, LenCurrentBest, Compressed) :-
	( compress_with_bound(Initial, LenCurrentBest, NewBest) ->
	        mlength(NewBest, NewLenBest),
		compress4(Initial, NewBest, NewLenBest, Compressed)
	; Compressed = CurrentBest
	).

compress_with_bound(Initial, LenBound, Better) :-
	repetition_compress(Initial, LenBound, Better).
compress_with_bound(Initial, LenBound, Better) :-
	two_price_compress(Initial, LenBound, Better).

repetition_compress(Initial, LenBound, Better) :-
	chopup(Initial, Piece, Repeated),
	( Piece = [C] ->
		Better = [C,Repeated]
	; compress(Piece, CompressedPiece),
	  append(['('|CompressedPiece], [')',Repeated], Better)
	),
	mlength(Better, LenBetter),
	LenBetter < LenBound.

two_price_compress(Initial, LenBound, Better) :-
	append(Piece1, Piece2, Initial),
	Piece1 \== [],
	Piece2 \== [],
	compress(Piece1, Compressed1),
	mlength(Compressed1, LenCompressed1),
	LenCompressed1 < LenBound,
	compress(Piece2, Compressed2),
	mlength(Compressed2, LenCompressed2),
	LenCompressed1 + LenCompressed2 < LenBound,
	append(Compressed1, Compressed2, Better).

chopup(List, Part, Repeated) :-
	append(Part, Rest, List),
	Part \== [],
	Rest \== [],
	count_parts(Rest, Part, 1, Repeated).

count_parts(Rest, Part, I, O) :-
	( Rest == [] -> I = O
	; append(Part, Rest1, Rest),
	  I1 is I+1,
	  count_parts(Rest1, Part, I1, O)
	).

%%----------------------------------------------------------------------------
%% Utilities below
%%----------------------------------------------------------------------------

append([],L,L).
append([H|L1],L2,[H|L3]) :- append(L1,L2,L3).

mlength(L,N) :- mlength(L,0,N).

mlength([],N,N).
mlength([_|R],N0,N) :- N1 is N0+1, mlength(R,N1,N).