:- ensure_loaded(reif).
:- ensure_loaded(assoc).
:- use_module(library(lists)).
%:- ensure_loaded(x).

:-style_check(-singleton),style_check(-no_effect).

list_atom_string([], []).
list_atom_string(L1, L2) :-
	L1 = [H | T],
	atom_string(H, H2),
	L2 = [H2 | T2],
	list_atom_string(T, T2).

cthrow(S) :- write("RuntimeError: "),write(S),nl,nl,backtrace(10),throw(error).

% c-compatibility? %

def(X) :- def(X,"def").

def(X,S) :- (var(X)->writeln(X),cthrow(S);true).

% temporary? used by old compiler %
% possibly remove or move to core2 %

%loaded manually for compatibility with old swipl versions
:- ensure_loaded(clp).

has(L,X) :- member(X,L).
has_(L,X) :- var(L),!,freeze(L,has_(L,X)).
has_(L,X) :- is_list(L),!,member(X,L).
has_(L,X) :- is_assoc(L),!,get_(L,_,X).
has_(L,X) :- string(L),!,s_get(L,_,X).

member1(X,[E|Es]) :- (X=E;dif(X,E),member1(X,Es)).
member2(X,[E|Es]) :- if_(X=E,true,member2(X,Es)).
tostr(X,Y) :- dump([X],[_],[Y]).
%fd_mul(X,Y,Z) :- Z #= X*Y.fd_div(X,Y,Z) :- Z #= X/Y.fd_sub(X,Y,Z) :- Z #= X-Y.fd_add(X,Y,Z) :- Z #= X+Y.fd_inc(X,Y) :- X+1 #= Y.fd_dec(X,Y) :- X-1 #= Y.

add(X,Y,Z) :- r_add(X,Y,Z).
sub(X,Y,Z) :- r_sub(X,Y,Z).
mul(X,Y,Z) :- r_mul(X,Y,Z).
div(X,Y,Z) :- r_div(X,Y,Z).

le(X,Y) :- {X =< Y}.
ge(X,Y) :- {X >= Y}.
lt(X,Y) :- {X < Y}.
gt(X,Y) :- {X > Y}.

n_mul(X,Y,Z) :- Z = X*Y.
n_div(X,Y,Z) :- Z = X/Y.
n_sub(X,Y,Z) :- Z = X-Y.
n_add(X,Y,Z) :- Z = X+Y.
n_inc(X,Y) :- X+1 = Y.
n_dec(X,Y) :- X-1 = Y.

n_le(X,Y) :- X =< Y.
n_ge(X,Y) :- X >= Y.
n_lt(X,Y) :- X < Y.
n_gt(X,Y) :- X > Y.
n_neq(X,Y) :- X \== Y.


r_mul(X,Y,Z) :- {Z = X*Y}.
r_div(X,Y,Z) :- {Z = X/Y}.
r_sub(X,Y,Z) :- {Z = X-Y}.
r_add(X,Y,Z) :- {Z = X+Y}.

r_le(X,Y) :- {X =< Y}.
r_ge(X,Y) :- {X >= Y}.
r_lt(X,Y) :- {X < Y}.
r_gt(X,Y) :- {X > Y}.

eq(X,Y) :- X = Y.

neq(X,Y) :- dif(X,Y). %(number(X) -> n_neq(X,Y) ; X \== Y).
assert_(P,X) :- assert(predicate(P,X)).
assert_get(P,X) :- predicate(P,X).

range(X,Y,Z) :- (Y1 is floor(X),Z=Y1);(Y1 is floor(X)+1,Y1=<Y,range(Y1,Y,Z)).

replace_word(Old, New, Orig, Replaced) :-
    atomic_list_concat(Split, Old, Orig),
    atomic_list_concat(Split, New, Replaced).
	
strrep(S,S2) :-
	re_replace("\"*","\\\"",S,S2).

s_concat(S,S1,S2) :-
	atomic_concat(S,S1,_x),atom_string(_x,S2).
	%in 8.2.3, string_concat sometimes fails

str2(S,S2) :-
	(ground(S2) ->
		str(S2,S)
	 ; S2=S).

str(S,S2) :-
	def(S,"string is an unbound variable (In)"),
	(S='+'(X,Y) ->
	(%writeq(X),nl, writeq(Y),nl,writeln("-"),
	str(X,X1),	str(Y,Y1),
	(number(X1),number(Y1) -> S2 is X1+Y1 ; s_concat(X1,Y1,S2))
	) ; S2=S).

%calc(X+Y,Z) :- (number(X)->{Z = X+Y};((string(X),string(Y))->s_concat(X,Y,Z);Z = X+Y)).
calc(X+Y,Z) :- ((string(X),string(Y))->s_concat(X,Y,Z);((number(X),number(Y))->{Z = X+Y};Z = X+Y)).
str_calc(X+Y,Z) :- ((string(X),string(Y))->s_concat(X,Y,Z);Z = X+Y).

test :- nb_getval(call,Cl),	writeln(Cl).

real(X,Y) :- {Y=X}.
int(X,Y) :- Y is floor(X). %{Y1=X},freeze(Y1,Y is floor(Y1)).
int_(X,Y) :- {Y1=X},Y is floor(Y1).

math(X,Y) :- real(Y,X).
math_str2(X,Y) :- writeln(X),str(X,Y1),Y1=Y.
math_str(X,Y) :- when(ground(X),math_str2(X,Y)).

%s_get(L,N,Element) :- nth0(N,L,Element).
s_get(S,N,C) :- freeze(S,sub_string(S,N,1,_,C)).
s_at(S,N,C) :- sub_string(S,N,1,_,C).
s_size1(S,N) :- var(N)->string_length(S,N);(int(N,Y),string_length(S,Y)).
s_size(S,N) :- freeze(S,s_size1(S,N)).
s_code(S,N) :- var(N) -> (string_code(1, S, N)) ; nonvar(N) -> (_i is integer(N), string_codes(S, [_i])) ; true.
s_codes(S,N) :- string_codes(S, N).
s_slice(S1,I,J,S2) :- s_size(S1,L), ({J < L} -> End=J ; End=L), sub(End,I,JJ), _i is integer(I), _j is integer(JJ), sub_string(S1,_i,_j,_,S2).
s_slice1(S1,I,J,S2) :- s_size(S1,L), ({J < L} -> End=J ; End=L), sub(End,I,JJ), sub_string(S1,I,JJ,_,S2).
s_slice2(S1,I,J,S2) :- freeze(S1,s_slice(S1,I,J,S2)). %s_slice2(S1,I,J,S2) :- freeze(S1,freeze(I,freeze(J,s_slice1(S1,I,J,S2)))).
s_first(S1,S2) :- s_at(S1, 0, S2).
s_le(S1,S2) :- S1 @=< S2.

safeNot(C) :- when(ground(C),\+call(C)).

is_rel(X) :- X=clos(_,_).

%nt(X) :- \+call(X).%L=arg list,I=[name]

:- nb_setval(call,0).

enter_hook(L,I) :-
	nb_getval(call,Cl),%writeln(Cl),
	(Cl=clos(X,Y)->call_cl(Z,L);true).
	
exit_db(Z,L,I) :-
	I=[Name],
	write(Name),write(''),writeq(L),nl.
	
enter_db(Z,L,I) :-
	I=[Name],
	write("| "), write(Name),write(''),write_(L),nl.
	
fail_db(Z,L,I) :-
	I=[Name],
	write("| "), write(Name),write(''),write_(L),nl.
	
freeze2(X,Z,L) :- freeze(X,call_cl(Z,L)).

call_db(Z,L,I) :-
	enter_db(Z,L,I),enter_hook(L,I),
	(call_cl(Z,L),write("- ");write("x "),exit_db(Z,L,I),false),
	exit_db(Z,L,I).
	
%(X;list of upval-args,Y;predicate)
call_cl(Z,L) :- Z=clos(X,P), %write("clos: "), writeln(X),
	%def(X,"undefined closure"),
	append(L,[X],X2),
	%write("|"), write(P),write(';'),writeq(L),nl,
	apply(P,X2).

apply2(Z,L) :- call_cl(Z,L).
apply_cl(Z,L) :- call_cl(Z,L).

forall_cl(P,L1,P2,L2) :- forall(apply_cl(P,L1),apply_cl(P2,L2)).

reif(X,Y,P,L1,P2,L2) :-
	if_(X=Y,apply_cl(P,L1),apply_cl(P2,L2)).

folds([X|L2],Sep) :- X=A-B,write_(A),write(": "),write_(B),(L2=[]->true;write(Sep),folds(L2,Sep)).
folds([],Sep).
assoc_str(X) :- assoc_to_list(X,L),write("{"),folds(L,", "),write("}"),!.

write_(X) :-
	(var(X) -> (write("var#"), write(X))
		;
		(is_rel(X) ->
		write("rel#") ;
		(is_assoc(X) ->
		%write("dict#")
		%writeq(X)
		assoc_str(X)
		;
			
			(functor(X,A,B) ->
			(writeq(X)) ;
			%string?
			(write("\""),writeq(X),write("\""))
		)))
	).

writeln_(X) :-
	write_(X),nl.
	%write("\n").

call_(X) :- writeln_(X).

functor_info(F, Name, Terms) :- F =.. [Name|Terms].

pair(H,K,V) :- H=K-V.

% sys %

require(S,F) :- loaded(S,F);[S].

cload(F) :- exists_source(F), ensure_loaded(F).
creq_(S) :- %atom_concat('./',S,S1),atom_concat(S1,'.pl',F),
	(cload(S) -> true ; (cload(cosmos(S))->true;throw("cannot load module: "+S))).
creq(S,Y) :- atom_string(F,S), creq_(F), call(F,Y).
creq1(S,Y,F) :- atom_string(F,S), creq_(F).
cread(X).
creadFile(F,S).
cmodule(F,X) :- ensure_loaded(F), call(F,X).
	
% sys %

clock(X) :- get_time(X).
args(L) :- current_prolog_flag(os_argv,L).

% files %
ioread(S) :- read_string(user_input,"\n","\r",_,S).
fread(F,S) :- read_string(F,"\n","\r",_,S).
fread_all(F,S) :- read_string(F,"","",_,S).
fread_char(F,S) :- read_string(F,1,S).
fread_custom(F,A,B,C,S) :- read_string(F,A,B,C,S).

fopen(Filename,Mode,File) :-
	(Mode="read" -> open(Filename,read,File) ;
		(Mode="write" -> open(Filename,write,File) ;
			(Mode="update" -> open(Filename,update,File) ; throw("not a correct mode for 'open'.")))).
	
fopen_binary(Filename,Mode,File) :-
	(Mode="read" -> open(Filename,read,File,[type(binary)]) ;
		(Mode="write" -> open(Filename,write,File,[type(binary), encoding(octet)]) ;
			(Mode="update" -> open(Filename,update,File,[type(binary)]) ; throw("not a correct mode for 'open'.")))).
		
pure_comp(X,Y,S) :- when(ground(X),when(ground(Y),(compare(Z,X,Y),atom_string(Z,S)))).
	
% records %

new(T) :- empty_assoc(T).

get_(T, Key, Value) :- get_assoc(Key, T, Value).

set_(T, X, V, T2) :- put_assoc(X, T, V, T2).
%get__(T, Key, Value) :- (get_assoc(Key, T, Value)->true;(writeln(T),concat("CosmosError: cannot find \"",Key,S1),concat(S1,"\" in table",S),throw(S))).
del_(T, X, V, T2) :- del_assoc(X, T, V, T2).

next_pair([K-V|L2],L2,K,V).

%next(T, T2, K, V) :- assoc_to_list(T,L), next_pair(L,T2,K,V).
next_assoc(T, T2, K, V) :- assoc_to_list(T,L), next_pair(L,T2,K,V).

next(t(K,V,_,X1,X2), T2, K, V) :- T2=t1(X1,X2).
%next(t1(t,X2), T2, K, V) :- next(X2,T2,K,V).
%next(t1(X1,t), T2, K, V) :- next(X1,T2,K,V).
%next(t, T2, K, V) :- next(X1,T2,K,V).
%next(t(X1,X2), T2, K, V) :- next(X1,T2,K,V);next(X2,T2,K,V).
	
%:- ensure_loaded("logic.pl").

%custom_var(Name, X) :- (write(Name), write(" = "), (logic_env(T),logic_toString(T,X,S), write(S))).
%
custom_var(Name, X) :- (write(Name), write(" = "), write(S)).
custom_vars(N,A) :- A = [] -> write("true") ;
	(A = [H] -> (N=[NH|_], custom_var(NH,H), write("")) ; 
		(A = [H|T], N=[NH|NT], custom_var(NH,H), write(" and "), custom_vars(NT,T))).
custom_vars0(N,A) :- write("| "), custom_vars(N,A), writeln("").
apply_forall_custom_vars(G1, N, A1) :- forall(apply(G1, A1), apply(custom_vars0, [N,A1])).
%apply_forall_custom_vars(G1, N, A1) :- forall(apply(G1, A1), apply(custom_vars0, [N,A1])).