about summary refs log blame commit diff stats
path: root/picat/util.pi
blob: 6d6b70e3bc7fbd862850f9f77774d871ee0ba7d0 (plain) (tree)





































































































































































































































































































                                                                                                          
/*
  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).