/*
Some general utilities in Picat.
Others have been moved to the modules basic, math, sys, etc.
by Hakan Kjellerstrand and Neng-Fa Zhou.
*/
module util.
% array_matrix_to_list(A) = array_matrix_to_list(A).
% array_matrix_to_list_matrix(A) = array_matrix_to_list_matrix(A).
% chunks_of(L,N) = chunks_of(L,N).
% columns(Matrix) = columns(Matrix).
% diagonal1(Matrix) = diagonal1(Matrix).
% diagonal2(Matrix) = diagonal2(Matrix).
% drop(L,N) = drop(L,N).
% find(String, SubString, From, To) => find(String, SubString, From, To).
% find_first_of(Compound,Pattern) = find_first_of(Compound,Pattern).
% find_ignore_case(String, SubString, From, To) => find_ignore_case(String, SubString, From, To).
% find_last_of(Compound,Pattern) = find_last_of(Compound,Pattern).
% join(S) = join(S).
% join(S, Seperator) = join(S, Seperator).
% list_matrix_to_array_matrix(L) = list_matrix_to_array_matrix(L).
% lstrip(L) = lstrip(L," \t\n\r").
% lstrip(L,Chars) = lstrip(L,Chars).
% matrix_multi(A,B) = matrix_multi(A,B).
% nextto(X,Y,List) => nextto(X,Y,List).
% permutation(Xs,Ys) => permutation(Xs,Ys).
% permutations(Xs) = permutations(Xs).
% power_set(Set) = power_set(Set).
% replace(Term,Old,New) = replace(Term,Old,New).
% replace_at(Compound,I,NewArg) = replace_at(Compound,I,NewArg).
% rows(Matrix) = rows(Matrix).
% rstrip(L) = rstrip(L," \t\n\r").
% rstrip(L,Chars) = rstrip(L,Chars).
% split(Str) = split(Str).
% split(Str,Seperators) = split(Str,Seperators).
% strip(L) = strip(L," \t\n\r").
% strip(L,Chars) = strip(L,Chars).
% take(L,N) = take(L,N).
% transpose(Matrix) = transpose(Matrix).
%
% Convert a 2D array to a list
%
array_matrix_to_list(A) = L =>
NRows = A.length,
NCols = A[1].length,
L = [A[I,J] : I in 1..NRows, J in 1..NCols].
% Convert a 2D array to a 2D matrix of lists
array_matrix_to_list_matrix(A) = L =>
L = [A[I].to_list() : I in 1..A.length].
% Convert a 2D list matrix to a 2D array matrix
list_matrix_to_array_matrix(L) = A =>
bp.list_matrix_to_array_matrix(L,A).
%
% Join a list of strings with a join character.
% Res = join(String,JoinChar)
%
join(S) = join(S," ").
join(S, JoinAtm) = Res, atom(JoinAtm) =>
join_aux(S, JoinAtm.to_string(), Res).
join(S,JoinStr) = Res, string(JoinStr) =>
join_aux(S,JoinStr,Res).
join(S,JoinStr) = _Res =>
handle_exception($string_or_atom_expected(JoinStr), $join(S,JoinStr)).
private
join_aux([],_JoinStr,Res) => Res = [].
join_aux([W|Str],JoinStr,Res) =>
once(append(W,Res1,Res)),
(Str == [] ->
Res2 = Res1
;
once(append(JoinStr,Res2,Res1))
),
join_aux(Str,JoinStr,Res2).
%%%%
% replace occurrences of Old in T (a variable or an atomic value) by New
replace(T,Old,New) = Res =>
replace_aux(T,Old,New,Res).
replace_aux(Old,Old,New,Res) => Res = New.
replace_aux(T,Old,New,Res), atomic(T) => Res = T.
replace_aux(T,Old,New,Res), var(T) => Res = T.
replace_aux([H|T],Old,New,Res) =>
Res = [NH|NT],
replace_aux(H,Old,New,NH),
replace_aux(T,Old,New,NT).
replace_aux(T,Old,New,Res) =>
Res = new_struct(T.name,T.length),
foreach(I in 1 .. T.length)
replace_aux(T[I],Old,New,Res[I])
end.
%%%%
% return a copy of the compound value, replacing the Ith argument by NewVal
replace_at(List,I,NewVal) = NewList, integer(I), list(List) =>
replace_list_at(List,I,NewVal,NewList,ErrorFlag),
(var(ErrorFlag) -> true; handle_exception($domain_error(I), $replace_at)).
replace_at(Struct,I,NewVal) = NewStruct, integer(I), struct(Struct) =>
Arity = len(Struct),
(I >= 1, I =< Arity ->
NewStruct = new_struct(Struct.name,Arity),
foreach(J in 1 .. Arity)
(J == I ->
NewStruct[J] = NewVal
;
NewStruct[J] = Struct[J]
)
end
;
handle_exception($domain_error(I), $replace_at)
).
private
replace_list_at([_|List],1,NewVal,NewList,_ErrorFlag) => NewList = [NewVal|List].
replace_list_at([E|List],I,NewVal,NewList,ErrorFlag) =>
NewList = [E|NewList1],
replace_list_at(List,I-1,NewVal,NewList1,ErrorFlag).
replace_list_at(_,_,_,_,ErrorFlag) => ErrorFlag = 1.
% match a string
% find(String, SubString,From,To)
%
% (If we want to have multiple results it must be a predicate,
% not a function.)
find(String, SubString, From, To) =>
SubLen = SubString.length,
bp.append(Pre,SubString,_,String),
From = Pre.length+1,
To = From+SubLen-1.
% Case insensitive match
find_ignore_case(String, SubString, From, To) =>
String2 = String.to_lowercase(),
SubString2 = SubString.to_lowercase(),
find(String2,SubString2,From,To).
%%%%
% searches for the first argument that unifies with Pattern and returns the argument's index
find_first_of(List,Pattern) = Index, list(List) =>
find_list_first_of(List,Pattern,1,Index).
find_first_of(Struct,Pattern) = Index, struct(Struct) =>
find_struct_first_of(Struct,Pattern,1, len(Struct), Index).
find_first_of(Struct,_Pattern) = _ =>
handle_exception($compound_expected(Struct), find_first_of).
find_list_first_of([],_Pattern,_CurIndex,Index) => Index = -1.
find_list_first_of([E|L],Pattern,CurIndex,Index), E != Pattern => % not unifiable
find_list_first_of(L,Pattern,CurIndex+1,Index).
find_list_first_of(_L,_Pattern,CurIndex,Index) => Index = CurIndex.
find_struct_first_of(_Struct,_Pattern,CurIndex,Arity,Index), CurIndex > Arity => Index = -1.
find_struct_first_of(Struct,Pattern,CurIndex,Arity,Index), Struct[CurIndex] != Pattern => % not unifiable
find_struct_first_of(Struct,Pattern,CurIndex+1,Arity,Index).
find_struct_first_of(_,_,CurIndex,_Arity,Index) => Index = CurIndex.
%%%%
% searches for the last argument that unifies with Pattern and returns the argument's index
find_last_of(List,Pattern) = Index, list(List) =>
find_list_last_of(List,Pattern,1,-1,Index).
find_last_of(Struct,Pattern) = Index, struct(Struct) =>
find_struct_last_of(Struct,Pattern, len(Struct), Index).
find_last_of(Struct,_) = _ =>
handle_exception($compound_expected(Struct), find_last_of).
find_list_last_of([],_Pattern,_CurIndex,Index0,Index) => Index = Index0.
find_list_last_of([E|L],Pattern,CurIndex,Index0,Index), E != Pattern => % not unifiable
find_list_last_of(L,Pattern,CurIndex+1,Index0,Index).
find_list_last_of([_|L],Pattern,CurIndex,_Index0,Index) =>
find_list_last_of(L,Pattern,CurIndex+1,CurIndex,Index).
find_struct_last_of(_Struct,_Pattern,0,Index) => Index = -1.
find_struct_last_of(Struct,Pattern,CurIndex,Index), Struct[CurIndex] != Pattern => % not unifiable
find_struct_last_of(Struct,Pattern,CurIndex-1,Index).
find_struct_last_of(_,_,CurIndex,Index) => Index = CurIndex.
% A*B=C
matrix_multi(A,B) = C, array(A), array(B) => % A and B must be array matricies
C = new_array(A.length,B[1].length),
foreach(I in 1..A.length, J in 1..B[1].length)
C[I, J] = sum([A[I,K]*B[K,J] : K in 1..A[1].length])
end.
% nondet
permutation(Xs, Ys) => bp.permutation(Xs,Ys).
% generate permutations
permutations([]) = [[]].
permutations([H|T]) = [insert(P,I,H) : P in Ps, I in 1..P.length+1] => Ps = permutations(T).
%nonet
nextto(X,Y, List) => bp.nextto(X,Y,List).
% generate the power set
power_set([]) = [[]].
power_set([H|T]) = P1++P2 =>
P1 = power_set(T),
P2 = [[H|S] : S in P1].
%
% Split a string into tokens given some split chars
% List = split(String, Seperators)
%
split(Str) = split(Str," \t\n\r"). % use white spaces as the default set of separators
split(Str,Seperators) = Tokens =>
bp.picat_split_string(Str,Seperators,Tokens).
lstrip(L) = lstrip(L," \t\n\r").
lstrip([],_Elms) = [].
lstrip([E|L],Elms) = NewL, membchk(E, Elms) => NewL = lstrip(L,Elms).
lstrip(L,_Elms) = NewL => NewL = L.
rstrip(L) = rstrip(L," \t\n\r").
rstrip(L, Elms) = L.reverse().lstrip(Elms).reverse().
strip(L) = strip(L," \t\n\r").
strip(L, Elms) = L.lstrip(Elms).rstrip(Elms).
%
% Transpose a 2D matrix
%
transpose(Matrix) = Transposed, array(Matrix) => % array matrix
N = Matrix.length,
M = Matrix[1].length,
Transposed = new_array(M,N),
foreach(I in 1..N, J in 1..M)
Transposed[J,I] = Matrix[I,J]
end.
transpose(Matrix) = Transposed => % assumed to be list matrix
N = Matrix.length,
M = Matrix[1].length,
Transposed = [Mj : J in 1..M, Mj = [Matrix[I,J] : I in 1..N]].
%=============
% for matrices (inspired by B-Prolog's ^rows, ^columns, ^diag1, ^diag2)
%
% These should be put in util.pi since transpose/1 is used.
rows(M) = M, list(M) => true.
rows(A) = Rows, array(A) => NRows = A.length, Rows = [A[I] : I in 1..NRows].
columns(M) = [Column : Column in M.transpose()], list(M) => true.
columns(A) = [Column : Column in A.transpose()], array(A) => true.
diagonal1(M) = [M[I,I] : I in 1..M.length], list(M) => true.
diagonal1(A) = [A[I,I] : I in 1..A.length], array(A) => true.
diagonal2(M) = [M[I,M.length-I+1] : I in 1..M.length], list(M) => true.
diagonal2(A) = [A[I,A.length-I+1] : I in 1..A.length], array(A) => true.
%=============
% from Haskell prelude
%
take(L,N) = Taken, list(L), integer(N), take_aux(L,N,Taken) => true.
private
take_aux([H|T],N,Taken), N > 0 => Taken = [H|TakenR], take_aux(T,N-1,TakenR).
take_aux(_List,_N,Taken) => Taken = [].
drop(L,N) = Taken, list(L), integer(N), drop_aux(L,N,Taken) => true.
private
drop_aux([_|T],N,Taken), N > 0 => drop_aux(T,N-1,Taken).
drop_aux(L,_,Taken), list(L) => Taken = L.
chunks_of([],_N) = [].
chunks_of(L,N) = Chunks, list(L) =>
Chunks = [Chunk|ChunksR],
chunks_of(L,Chunk,0,N,ChunksR).
chunks_of([],Chunk,_,_,Chunks) => Chunk = [], Chunks = [].
chunks_of(L,Chunk,N,N,Chunks) =>
Chunk = [],
(L == [] ->
Chunks = []
;
Chunks = [NextChunk|ChunksR],
chunks_of(L,NextChunk,0,N,ChunksR)
).
chunks_of([X|Xs],Chunk,Count,N,Chunks) =>
Chunk = [X|ChunkR],
chunks_of(Xs,ChunkR,Count+1,N,Chunks).