
:- ensure_loaded( library(real) ).
:- ensure_loaded( library(lists) ).
:- use_module( library(apply_macros) ).
:- use_module( library(readutil) ).

:- debug( for_real ).

% for_real.
%
%  Some examples illustrating usage of the r..eal library.
%==
% ?- for_real.
%==
%
for_real :-
     ( Head = ex(_Ex); Head = tut(_Tut) ),
     clause( Head, Body ),
     write(running:Head), nl, nl,
     portray_clause( (Head:-Body) ), nl, nl,
     write( 'Output: ' ), nl,
     ( catch(Head,Exc,Fex=true) -> 
          ( Fex==true-> 
               write( '! ' ), write( caught(Exc) ), nl, abort
               ;
               write(status:true)
          )
          ;
          write( '! ' ), write( failure ), nl, abort
     ),
     nl, nl, write('-----'), nl, nl,
     fail.
for_real :-
     write( 'All done.' ), nl.


% ex(int).
%
%  Pass the salt please. 
%  The most basic example: pass a Prolog list of integers to an R variable 
%  and then back again to a Prolog variable.
%
ex(int) :-
     i <- [1,2,3,4],
     <- i,
     I <- i,
     write( i(I) ), nl.

% float.
%
%  Pass a Prolog list of floats to an R variable and then back again to a Prolog variable.
%
ex(float) :-
     f <- [1.0,2,3,4],
     <- f,
     F <- f,
     write( f(F) ), nl.

% ex( float ).
%
%  Pass a mixed Prolog list of integers and floats to an R variable and
%  then back again to a Prolog variable.
%  The returning list is occupied by floats as is the R variable.
%
ex(to_float) :-
     m <- [1,2,3,4.0],
     <- m,
     M1 <- m,
     write( m(M1) ), nl,
     m <- [1,2,3,4.1],
     <- m,
     M2 <- m,
     write( m(M2) ), nl.

% ex(bool).
%
%
ex(bool) :- 
     b <- [true,false,true,true],
     <- b,
     B <- b,
     write( b(B) ), nl.

% at_bool.
%
%  In cases where disambiguation is needed, boolean values can be represented by @Val terms.
%
ex(at_bool) :- 
     b <- [@true,@false,@true,@true],
     <- b,
     B <- b,
     write( at_b(B) ), nl.

%  ex(bool_f).
%
%   This fails since there is a non boolean value in a list.
%
%   On SWI this fails...
%   On YAP this throuws exception....
%
ex(bool_f) :-
     ( catch(b <- [true,false,true,true,other],_,fail) ->
          fail
          ;
          true
     ).

%  ex(bool_back).
%
%   Get some boolean values back from applying a vector element equality to an integer
%   vector we just passed to R. Prints the R bools first for comparison.
%
ex(bool_back) :- 
     t <- [1,2,3,4,5,1],
     <- t,
     s <- t==1,
     <- s,
     S <- s,
     write( s(S) ), nl.

% ex(atom_char).
%
%  Pass some atoms to an R vector of characters.
%
ex(atom_char) :- 
     f <- [a,b,c],
     <- f,
     F <- f,
     write( f(F) ), nl.

% ex(matrix_int). 
%
%  Pass a 2-level list of lists of integers to an R matrix (and back again).
%
ex(matrix_int) :-
     a <- [[1,2,3],[4,5,6]],
     <- a,
     A <- a,
     nl, write( a(A) ), nl.

% ex(matrix_char). 
%
%  Pass a 2-level list of lists of characters to an R matrix (and back again).
%
ex(matrix_char) :-
     a <- [[a,b,c],[d,e,f]],
     <- a,
     A <- a,
     write( a(A) ), nl.


/*
% ex(matrix_idx).
%
ex(matrix_idx) :-
     a <- [[1,2,3],[4,5,6]],
     <- a,
     J <- aË[[1,3],*],
     write( j(J) ), nl.
     */

% ex(list).
%
%  A Prolog = pairlist to an R list. Shows 2 alternative ways to access the list items.
% 
ex(list) :-
     a <- [x=1,y=0,z=3],
     <- a,
     write( 'First element of list :' ), nl,
     <- a^[[1]],
     write( 'Second element of list :' ), nl,
     <- a$y,
     A <- a,
     write( a(A) ), nl.

% ex(list).
%
%  R allows for unamed lists.
% 
ex(unamed) :-
      li <- 'list()',
      li[[1]] <- c(1,2,3),
       <- li,
       L <- li,
       write( l(L) ), nl.

% ex(list_ea).
%
% Produces error due to name of constructor: -.
%
ex(list_ea) :-  % produces error
     catch_controlled( a <- [x=1,y=0,z-3] ),
     <- a,
     A <- a,
     write( a(A) ), nl.

% ex(list_eb).
%
% Produces an error due to mismatch of arity of =.
%
ex(list_eb) :- 
     catch_controlled( a <- [x=1,y=0,=(z,3,4)] ),
     <- a,
     A <- a,
     write( a(A) ), nl.

% ex(char_list).
%
%  Pass a list which has a char value.
%
ex(char_list) :-
     a <- [x=1,y=0,z=three],
     <- a,
     A <- a,
     memberchk( Z=three, A ),
     write( z(Z):a(A) ), nl.

% ex(mix_list). 
%
%  An R-list of mixed types.
%
ex(mix_list) :-
     a <- [x=1,y=[1,2,3],z=[[a,b,c],[d,e,f]],w=[true,false]],
     A <- a, 
     <- print(a),
     write( a(A) ), nl.

% ex(list2).
%
%  Illustrates ways of accessing list elements.
% 
ex(list2) :-
     l <- list(.), 
     l^[[+"what"]] <- c(1,2,3),
     l$who <- c(4,5,6),
     <- l,
     L <- l,
     write( l(L) ), nl.

% ex(slot).
%
%  Creating formal objects and accessing their content.
%
ex(slot) :-
     <- setClass(+"track", representation(x=+"numeric", y=+"numeric")),
     myTrack <- new(+"track", x = -4:4, y = exp(-4:4)),
      <- print( myTrack@x ),
     % [1] -4 -3 -2 -1  0  1  2  3  4
      Y <- myTrack@y, 
      write( y(Y) ), nl,
      <- setClass(+"nest", representation(z=+"numeric", t=+"track")),
      myNest <- new(+"nest", z=c(1,2,3) ),
      myNest@t <- myTrack,
      myNest@t@x <- Y+1,  % good ex. for hidden vars.
      <- myNest,
      % N <- myNest, % unsupported r-type
      % X <- myNest@t@x,
      <- print(myNest@t@x),
      X <- myNest@t@x,
      <- myNest@t@x,
      write( x(X) ), nl.

      % myTrack@x <- c(1,2,3).

% ex(add_element).
%
%   Adds a third element to a list after creation.
%
ex(add_element) :-
     x <- [a=1,b=2],
     x$c <- [3,4], 
     <- x,    % print   =   $a 3
     X <- x,
     write( x(X) ), nl.   % X = [a=3.0].

% ex(singletons).
%
%  Pass an integer and a singleton number list and get them back.
% Although in R both are passed as vectors of length on, when back in Prolog
% the singleton list constructors are stripped, returing a single integer value in both cases.
%  
ex(singletons) :- 
     s <- 3,
     <- s,
     S <- s,
     <- s,
     t <- [3],
     <- t,
     T <- t,
     write( s(S)-t(T) ), nl.

% ex(assign). 
%
% Simple assignment of an R function (+) application on 2 R values originated in Prolog.
% 
ex(assign) :- 
     a <- 3,
     <- a,
     b <- [2],
     <- b,
     C <- a + b,
     write( c(C) ), nl.


% ex(assign_1).
%
%  Assign the result of an R operation on matrix and value to a Prolog variable.
%
ex(assign_1) :- 
     a <- [[1,2,3],[4,5,6]], 
     <- a,
     B <- a*3, 
     write( b(B) ), nl.

% ex(assign_2).
%
%  Assign the result of an R operation on matrices to a Prolog variable.
%
ex(assign_2) :- 
     a <- [[1,2,3],[4,5,6]], 
     <- a,
     b <- 3,
     <- b,
     C <- a*b,
     write( c(C) ), nl.

% ex(assign_r).
%
% Assign values to R variables and operate on them. 
% Using c as an R variable is also a good test, as we test against c(...).
%
ex(assign_r) :- 
     a <- [3],
     <- a,
     b <- [2],
     <- b,
     c <- a + b,
     <- c.

/*  disable for now. once Yap supports . in atoms
    re-establish this, but make sure you restor
    relevant flag back to its original setting.

% ex(dot_in_function_names).
%
% Test dots in functions names via the .. mechanism.
%
ex(dot_in_function_names) :-
     a <- [1.1,2,3],
     <- a,
     x <- as..integer(a),
     <- x.
     */

/* as aove
% ex(dot_in_rvars).
%
%  Test dots in R variable names via the .. mechanism. Generates an error on the last goal.
%
ex(dot_in_rvar) :-
     a..b <- [1,2,3],
     <- a..b,
     <- 'a.b',
     catch_controlled( <- print('a..b') ).
     */

% ex(semi_column).
%
%  A:B in R generates a vector of all integers from A to B.
%
ex(semi_column) :-
     z <- 1:50,
     <- z,
     Z <- z, 
     length( Z, Len ),
     write( len(Len) ), nl.

% ex(c_vectors).
%
%  r.eal also supports c() R function concatenation.
%
ex(c_vectors) :-
     a <- c(1,2,3,5),  % this goes via the fast route
     <- a,
     b <- c(1,1,2,2) + c(1:4),
     <- b,
     C <- a+b,
     write( 'C'(C) ), nl.

% ex(empty_args).
%
%  Test calling R functions that take no arguments (via foo(.)).
%
ex(empty_args) :-
     <- plot( 1:10, 1:10 ),
     findall( I, (between(1,6,I),write('.'), flush_output, sleep(1)), _ ),
     nl,
     r_devoff.  % fixme use dev.off() when Yap starts supporting it.

% ex(string).
%
%  Test new (2013/11/22) string type in SWI Prolog v7.
%
ex(string) :-
     ( (current_predicate(string/1),string("abc")) ->
          <- plot( 1:10, 1:10, main="native string type has arrived to Prolog" ),
          findall( I, (between(1,6,I),write('.'), flush_output, sleep(1)), _ )
          ;
          true
     ).


% ex(dot_in_atoms).
%
%  Test new (2016/8/25) . without allow_dot_in_atom
%
ex(dot_in_atoms) :-
	current_prolog_flag( allow_dot_in_atom, Allows ),
	set_prolog_flag( allow_dot_in_atom, false ),
	v1 <- as.integer(c(1,2,3)),
	V1 <- v1,
	write( v1(V1) ), nl,
	set_prolog_flag( allow_dot_in_atom, Allows ).

% ex(binary_op).
%
% Early versions of r..eal were not handling this example properly.  
% Thanks to Michiel Hildebrand for spotting this.
% The correct answer is =|[0.0,4.0]|=. First subtract v1 from v2 and then take power 2.
%
ex(binary_op) :-
     v1 <- c(1,1),
     <- v1,
     v2 <- c(1,-1),
     <- v2,
     P <- (v1-v2)^2,
     write( P ), nl.
     % not !!! : P = [0.0, 0.0].

% ex(utf).
%
% Plots 3 points with the x-axis label showing some Greek letters (alpha/Omega).
%
ex(utf) :-
     <- plot( c(1,2,3), c(3,2,1), xlab= +[945,0'/,937] ),
     findall( I, (between(1,4,I),write('.'), flush_output, sleep(1)), _ ),
     nl,
     r_devoff.


% ex(utf_atom).
%
% Plots 3 points with the x-axis label showing some Greek letters (alpha/Omega) as atom preceded by +.
%
ex(utf_atom) :-
     <- plot( c(1,2,3), c(3,2,1), xlab= +'Î±/Î©' ),
     findall( I, (between(1,4,I),write('.'), flush_output, sleep(1)), _ ),
     nl,
     r_devoff.

% ex( utf_1 ).
%
%  Thanks to  Guillem R.
%
ex(utf_1) :-
     s <- ['Pour ce garÃ§on Ãªtre sur une Ã®le, y avoir des histoires de cÅur ambiguÃ«s et vider un fÃ»t de biÃ¨re sur un canoÃ« entourÃ© par des caÃ¯mans, ne fut pas un mince affaire.'],
     <- s,
     S <- s,
     write( s(S) ), nl.

% ex( utf1 ).
%
%  Mostly Vitor's then Sander and last one from Nicos.
%
ex(utf_2) :-
     x <- [hello, 'olÃ¡', 'à¤à¥à¤¸à¤¾ à¤à¤¹à¤° à¤¬à¤°à¤ªà¤¾ à¤¤à¥ à¤¬à¤°à¥à¤¬à¤¾à¤¦ à¤¹à¥ à¤à¤¾à¤à¤à¥ à¤®à¥à¤à¤¬à¤','BeÃ«indigen','Î¬Î¼ÏÎµÎ»Î¿Ï'],
     <- x,
     X <- x,
     write( x(X) ), nl.


% ex(plot_cpu).
%
%  Create a plot of 4 time points. Each having a push and a pull time component.
%  These are the time it takes to push a list through to R and the time to Pull the same
%  (very long) list back.
%
ex(plot_cpu) :-
     plot_cpu( 1000 ).

ex(debug) :-
	debug( real ),
     write( started_debugging ), nl,
     x <- c(1,2,3),  % c-vectors
     y <- [1,2,3],   % PL data lists
     X <- x,         % R var to PL var
     x <- [a=[1,2,4],b=[4,5,6]],
     A <- x,
     B <- x$b,       % R expression to PL var
     Y <- x$b + x$a,
     x$c <- [6,3,7],
	nodebug( real ),
     write( x(X) ), nl,
     write( a(A) ), nl,
     write( b(B) ), nl,
     write( y(Y) ), nl,
     write( stopped_debugging ), nl.

%% ex(nan).
%
%  NaN examples.
%==
% ?- ex(nan).
% Testing NA in num matrices
% Returned matrix: [[$NaN,2,3],[4,$NaN,6],[7,8,$NaN]]
% Returned floats vector: [2.0,NaN,3.0]
% Returned ints vector: [-2147483648,3,1]
%  
%==
%
ex(nan) :-
     debug( for_real, 'Testing NA in num matrices', [] ),
     mtx <- [r('',2,3),row(4,'',6),c(7,8,'$NaN')],
     Mtx <- mtx,
     debug( for_real, 'Returned matrix: ~w', [Mtx] ),
     xtm <- [2.0,'',3],
     Xtm <- xtm,
     debug( for_real, 'Returned floats vector: ~w', [Xtm] ),
     itm <- ['',3,1],
     Itm <- itm,
     debug( for_real, 'Returned ints vector: ~w', [Itm] ),
     true.

% ex(rtest).
% Some tests from r_session, 
%
ex(rtest) :-
     <- 'set.seed'(1),  % fixme: dot
     y <- rnorm(50),
     <- y,
     x <- rnorm(y),
     <- x,
     <- x11(width=5,height=3.5),
     <- plot(x,y),
     r_wait,
     r_devoff,
     Y <- y,
     write( y(Y) ), nl,
     findall( Zx, between(1,9,Zx), Z ),
     z <- Z,
     <- z,
     cars <- [1, 3, 6, 4, 9],
     % cars <- c(1, 3, 6, 4, 9),
     <- cars,
     <- pie(cars),
     r_wait,
     r_devoff.

% ex(noteq).
%
% Test non equallity operator
% fixme: Try to claim != as a valid operator. 
/* 
?- ex(noteq).
  a b
2 l 2
4 m 4
true.

*/
ex(noteq) :-
	x <- [a=[k,l,k,m], b=[1,2,3,4]],
	x <- 'as.data.frame'( x ),
	nks <- x[x$a \= "k", * ],
	<- print(nks),
	r_remove(nks),
	r_remove(x),
	true.


% ex(iexpr).
%
% Test expresssions in indices.
% ==
% ?- ex(iexpr).
%   a b
% 1 k 1
% 3 k 3
% ==
ex(iexpr) :-
	x <- [a=[k,l,k,m], b=[1,2,3,4]],
	x <- 'as.data.frame'( x ),
	ks <- x[ x$a == "k", * ],
	<- print(ks),
	r_remove(ks),
	r_remove(x).

% ex(el_and).
%
% Test expresssions in indices.
% ==
% ?- ex(el_and).
%   a b
% 1 k 1
% 3 k 3
% ==
ex(el_and) :-
	x <- [a=[k,l,k,m], b=[1,2,3,4]],
	x <- 'as.data.frame'( x ),
	ks <- x[ x$a == "k" & x$b == 1, * ],
	<- print(ks),
	r_remove(ks),
	r_remove(x).

% ex(el_or).
%
% Test expresssions in indices.
% ==
% ?- ex(el_or).
ex(el_or) :-
	x <- [a=[k,l,k,m], b=[1,2,3,1]],
	x <- 'as.data.frame'( x ),
	ks <- x[ x$a == "k" ; x$b == 1, * ],
	<- print(ks),
	r_remove(ks),
	r_remove(x).

% ex(not).
% 
% Test negation operator...
ex(not) :-
	% y <- c(true,false,true,false), % this no longer work ?
	y <- [true,false,true,false],
	<- y, 
	<- print( !y ).

% list_times.
%
% Print some timing statistics for operations on a long list of integers.
%
list_times :-
     findall( I, between(1,10000000,I), List ),
     statistics( cputime, Cpu1 ), write( cpu_1(Cpu1) ), nl,
     l <- List,
     a <- median( l ),
     statistics( cputime, Cpu2 ), write( cpu_2(Cpu2) ), nl,
     b <- median( List ),
     statistics( cputime, Cpu3 ), write( cpu_3(Cpu3) ), nl,
     <- a,
     <- b.

% adapted from YapR

% Intrinsic attributes: mode and length
tut(tut1) :-
     z <- 0:9,
     <- z,
     digits <- 'as.character'(z), % fixme: dot
     <- digits,
     d <- 'as.integer'(digits),  % fixme: dot
     <- d.


% changing the length of an object
tut(tut2) :-
     e <- numeric(.),
     (e^[3]) <- 17,
     <- e,
     alpha <- 1:10,
     alpha <- alpha^[2 * 1:5],
     <- alpha,   % = 2, 4, 6, 8 10
     length(alpha) <- 3,
     <- alpha,   % = 2, 4, 6
     nl, write( ' on beta now ' ), nl, nl,
     beta <- 1:10,
     beta <- 2 * beta,
     <- beta,    % 2  4  6  8 10 12 14 16 18 2
     length(beta) <- 3,
     <- beta.    % 2 4 6

% Getting and setting attributes
tut(tut3) :-
     z <- 1:100,
     attr(z, +"dim") <- c(10,10),
     <- z.

% factors and tapply.
tut(tut4) :-
      /* state <- c("tas", "sa",  "qld", "nsw", "nsw", "nt",  "wa",  "wa",
                  "qld", "vic", "nsw", "vic", "qld", "qld", "sa",  "tas",
                  "sa",  "nt",  "wa",  "vic", "qld", "nsw", "nsw", "wa",
                  "sa",  "act", "nsw", "vic", "vic", "act"), */
     state <- [tas,sa,qld,nsw,nsw,nt,wa,wa,qld,vic,nsw,vic,qld,qld,sa,tas,sa,nt,wa,vic,qld,nsw,nsw,wa,sa,act,nsw,vic,vic,act],
     <- state,
     % <- astate,
     statef <- factor(state),
     <- statef,
     <- levels(statef),
     incomes <- c(60, 49, 40, 61, 64, 60, 59, 54, 62, 69, 70, 42, 56,
                    61, 61, 61, 58, 51, 48, 65, 49, 49, 41, 48, 52, 46,
                    59, 46, 58, 43),
     incmeans <- tapply(incomes, statef, mean),
     % notice the function definition.
     stderr <- ( function(x) :-  sqrt(var(x)/length(x)) ),
     incster <- tapply(incomes, statef, stderr),
     <- incster.

tut(tut5) :-
     z <- 1:1500,
     dim(z) <- c(3,5,100),
     a <- 1:24,
     dim(a) <- c(3,4,2),
     <- print(a^[2,*,*]),
     <- print(dim(a)),
     x <- array(1:20, dim=c(4,5)),
     <- x,
     i <- array(c(1:3,3:1), dim=c(3,2)),
     <- i,
     x^[i] <- 0,
     <- x,
     h <- 1:10,
     z <- array(h, dim=c(3,4,2)),
     <- z,
     a <- array(0, dim=c(3,4,2)),
     <- a,
     % ab <- z '%o%' a,
     ab <- z @^@ a,
     <- ab,
     f <- ( function(x, y) :- cos(y)/(1 + x^2) ),
     w <- outer(z, a, f),
     <- w.

tut(tut6) :-
     d <- outer(0:9, 0:9),
     fr <- table(outer(d, d, +"-")),
     r(plot('as.numeric'(names(fr)), fr, type=+"h", xlab=+"Determinant", ylab=+"Frequency")), % fixme: dot
     nl, write( '   type query: ``r_devoff.\'\' to close the plot display ' ), nl, nl.

% auxiliary,
cpu_points( [], [], [] ).
cpu_points( [H|T], [S|Ss], [L|Ls] ) :-
     between_1_and(H,Long),
     statistics( cputime, _) , 
     length( Long, Lengtho ), write( leno(Lengtho) ), nl,
     statistics( cputime, S0 ), 
     ( number(S0) -> S0 = S ; S0 = [_,S] ),
     % statistics( cputime, [_,S] ), 
     long <- Long,
     Back <- long,
     Back = [Hb|_],
     Hb =:= 1,
     statistics( cputime, L0 ), 
     ( number(L0) -> L0 = L ; L0 = [_,L] ),
     % statistics( cputime, [_,L] ), 
     length( Back, BackLen ),
     write( back_len(BackLen) ), nl,
     % L = 0,
     cpu_points( T, Ss, Ls ) .

% auxiliaries,

catch_controlled( Expr ) :-
     catch( Expr, Caught, true ),
     ( \+ var(Caught) -> write( caught_controlled(Caught) ), nl; fail ).

between_1_and(N,X) :-
     ( var(N) -> N is 100; true ),
     IntN is integer(N),
     findall( I, between(1,IntN,I), Is ),
     i <- Is,
     X <- i.
cpu( R ) :-
     ( var(R) -> R is 10000; true ),
     findall( F, between_1_and(R,F), Fs ),
     f <- Fs, 
     statistics( cputime,  Cpu1 ),
     write( cputime_to_push(Cpu1) ), nl,
     X <- f,   % when   F <- f   the predicate fails midway for large Len !!!
     statistics( cputime,  Cpu2 ),
     write( cputime_to_pull(Cpu2) ), nl,
     length( X, Len ),
     write( len(Len) ), nl.
plot_cpu( Factor ) :-
     nl,
     ( Factor > 10 ->
         M='if your set-up fails on this test increase the size of stack.',
         write( M ), nl, nl 
          ;
          true
     ),
     points <- [10,100,500,1000],
     points <- 'as.integer'( points * Factor ), % fixme: dot
     <- points,
     Points <- points,
     write( points(Points) ), nl,
     cpu_points( Points, Push, Pull ), 
     push <- Push,
     pull <- Pull,
     write( plotting(Pull,Push) ), nl,
     <- plot( points, pull, ylab =+"pull & push (red) - in seconds" ),
     r_char( red, Red ),
     <- points( points, push, col=Red ).
