From 7159dd7b248ea573df6612efea2522103ea3e889 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Sat, 11 Apr 2020 17:13:39 +0900 Subject: [PATCH 01/33] Common code with rkdb, most known type conversion issue was rectified --- embedr.c | 8 + rinit.q | 21 +-- rtest.q | 208 ++++++++++++++++++------- src/common.c | 421 +++++++++++++++++++++++--------------------------- src/rserver.c | 373 +++++++++++++++++++++++++++++++++----------- 5 files changed, 638 insertions(+), 393 deletions(-) diff --git a/embedr.c b/embedr.c index 25f0553..29eaf2b 100644 --- a/embedr.c +++ b/embedr.c @@ -23,6 +23,14 @@ #define KXVER 3 #include "src/k.h" +#define INT64(x) ((J*) REAL(x)) +// Offsets used in conversion between R and q +static J epoch_offset=10957*24*60*60*1000000000LL; +// Seconds in a day +static int sec2day = 86400; +// Days+Seconds between 1970.01.01 & 2000.01.01 +static int kdbDateOffset = 10957; +static int kdbSecOffset = 946684800; #include "src/common.c" #include "src/rserver.c" diff --git a/rinit.q b/rinit.q index 6bc02d7..9a53410 100644 --- a/rinit.q +++ b/rinit.q @@ -5,25 +5,10 @@ Ropen:`embedr 2:(`ropen;1) Rcmd0:`embedr 2:(`rcmd;1) Rget0:`embedr 2:(`rget;1) Rset0:`embedr 2:(`rset;2) -Rcmd:{Rcmd0 x} -Rget:{r:Rget0 x;Rconv r} -Rset:{Rset0[x;y]} +Rcmd:Rcmd0; +Rget:Rget0; +Rset:Rset0; -Rconvmap:()!() -Rconvmap[enlist "Date"]:{-10957+`date$last x} -Rconvmap[enlist "POSIXt"]:{(-10957D)+`timestamp$1e9*last x} -Rconvmap[enlist "data.frame"]:{ - a:first x;if[0=count a`names;:last x]; - r:flip ((),`$a`names)!Rconv each last x; - r[`row.names]:$[null first rn:a`row.names;1+til last neg rn;rn]; - r} -Rconvmap[enlist "factor"]:{`$x[0;`levels] -1+last x} -Rconv:{ - if[(2<>count x) or 99<>type first x;:x]; // no attrs - c:first[x]`class; - if[10=type c;c:enlist c];c:c where 10h=type each c; - first[asc (),Rconvmap[c]] x - } Rinstall:{[pkg] pkg:$[-11=type pkg;string pkg;pkg];rcloud:"https://cloud.r-project.org"; if[0i=first Rget"is.element('",pkg,"',installed.packages()[,1])"; diff --git a/rtest.q b/rtest.q index 5481b21..72074ed 100644 --- a/rtest.q +++ b/rtest.q @@ -1,49 +1,103 @@ / test R server for Q + + +//%% Define Test Function/Variable %%//vvvvvvvvvvvvvvvvvvvvvvvvv/ + +HRULE:40#"+-"; +TESTCASE:0i; +SUCCESS:0i; +FAILURE:0i; + +PROGRESS:{[checkpoint] + -1 ""; + -1 HRULE; + -1 "\t",checkpoint; + -1 "\tScore:\t",string[SUCCESS],"/",string TESTCASE; + -1 "\tFail:\t",string[FAILURE],"/",string TESTCASE; + -1 HRULE; + -1 ""; + }; + +EQUAL:{[id;x;y] + TESTCASE+:1; + $[x~y; + SUCCESS+:1; + [FAILURE+:1; -1 "[",string[id],"] Fail:", -3!x] + ]; + }; + +//%% System Setting %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +//Load embedR file \l rinit.q -Ropen 1 // set verbose mode +//Set seed 42 +\S 42 + +//Set console width +\c 25 200 + +// set verbose mode +Ropen 1 + +//%% Test %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +//Numerical Array//-------------------------/ + +PROGRESS["Test Start!!"]; Rcmd "a=array(1:24,c(2,3,4))" -Rget "dim(a)" -Rget "a" + +EQUAL[1; Rget "dim(a)"; 2 3 4i]; +EQUAL[2; Rget "a"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; if[3<=.z.K;Rset["a";2?0Ng]] -Rget "a" +EQUAL[3; Rget "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed1-f4df-58fcdedd8376")]; Rcmd "b= 2 == array(1:24,c(2,3,4))" -Rget "dim(b)" -Rget "b" +EQUAL[4; Rget "dim(b)"; 2 3 4i]; +EQUAL[5; Rget "b"; ((0 0 0i;1 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i))]; -Rget "1.1*array(1:24,c(2,3,4))" +EQUAL[6; Rget "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11.0 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22.0 24.2 26.4))]; Rset["xyz";1 2 3i] -Rget "xyz" +EQUAL[7; Rget "xyz"; 1 2 3i]; -Rget "pi" -Rget "2+3"; -Rget "11:11" -Rget "11:15" +EQUAL[8; Rget "pi"; (), acos -1]; +EQUAL[9; Rget "2+3"; (), 5f]; +EQUAL[10; Rget "11:11"; (), 11i]; +EQUAL[11; Rget "11:15"; 11 12 13 14 15i]; a:Rget "matrix(1:6,2,3)" -a[1] +EQUAL[12; a[1]; 2 4 6i]; Rcmd "m=array(1:24,c(2,3,4))" -Rget "m" -Rget "length(m)" -Rget "dim(m)" -Rget "c(1,2,Inf,-Inf,NaN,NA)" +EQUAL[13; Rget "m"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; +EQUAL[14; Rget "length(m)"; (), 24i]; +EQUAL[15; Rget "dim(m)"; 2 3 4i]; +EQUAL[16; Rget "c(1,2,Inf,-Inf,NaN,NA)"; 1 2 0w -0w 0n 0n]; + +PROGRESS["Numeric Array Finished!!"]; + +//Plot Functionality//----------------------/ Rcmd "pdf(tempfile(\"t1\",fileext=\".pdf\"))" Rcmd "plot(c(2,3,5,7,11))" Rcmd "dev.off()" +//Function Test//---------------------------/ + Rcmd "x=factor(c('one','two','three','four'))" -Rget "x" -Rget "mode(x)" -Rget "typeof(x)" -Rget "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)" +EQUAL[17; Rget "x"; `one`two`three`four]; +EQUAL[18; Rget "mode(x)"; "numeric"]; +EQUAL[19; Rget "typeof(x)"; "integer"]; +EQUAL[20; Rget "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 1 0 0N 1 1 0i]; Rcmd "foo <- function(x,y) {x + 2 * y}" Rget "foo" -Rget "typeof(foo)" -Rget "foo (5,3)" +EQUAL[21; Rget "typeof(foo)"; "closure"]; +EQUAL[22; Rget "foo (5,3)"; (), 11f]; + +PROGRESS["Function Test Finished!!"]; + +//Object//-----------------------------------/ Rget "wilcox.test(c(1,2,3),c(4,5,6))" Rcmd "data(OrchardSprays)" @@ -64,72 +118,106 @@ Rget".Internal" @[Rcmd;"typeof()";like[;"eval error*"]] Rget each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"dim";"length";"names";".External") Rget "getGeneric('+')" -Rget"as.raw(10)" -Rget"as.logical(c(1,FALSE,NA))" -Rget"1:10" + +EQUAL[23; Rget"as.raw(10)"; (), 0x0a]; +EQUAL[24; Rget"as.logical(c(1,FALSE,NA))"; 1 0 0Ni]; + +PROGRESS["Onject Test Finished!!"]; + +//Table//-----------------------------------/ + // data.frame -Rget"data.frame(a=1:3, b=c('a','b','c'))" -Rget"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)" -Rget"data.frame(a=1:3)" -Rget"data.frame()" +EQUAL[25; Rget"data.frame(a=1:3, b=c('a','b','c'))"; flip `a`b!(1 2 3i;`a`b`c)]; +EQUAL[26; Rget"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b!(1 2 3i;1#/:("a";"b";"c"))]; +EQUAL[27; Rget"data.frame(a=1:3)"; flip enlist[`a]!enlist (1 2 3i)]; +EQUAL[28; Rget"data.frame()"; ()]; + +PROGRESS["Table Test Finished!!"]; + +//Time//------------------------------------/ + // dates -Rget"as.Date('2005-12-31')" -Rget"as.Date(NA)" -Rget"rep(as.Date('2005-12-31'),2)" +EQUAL[29; Rget"as.Date('2005-12-31')"; (), 2005.12.31]; +EQUAL[30; Rget"as.Date(NA)"; (), 0Nd]; +EQUAL[31; Rget"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; +// datetime +Rcmd["Sys.setenv(TZ='UTC')"]; +EQUAL[32; Rget"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[33; Rget"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[34; Rget"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[35; Rget"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +Rset["dttm"; 2018.02.18T04:00:01.000z]; +EQUAL[36; Rget"dttm"; (), 2018.02.18T04:00:01.000z]; +//timestamp +Rset["tmstp"; 2020.03.16D17:30:45.123456789]; +EQUAL[37; Rget"tmstp"; (), 2020.03.16D17:30:45.123456789]; + +PROGRESS["Time Test Finished!!"]; + +//List//---------------------------------------/ //lang -Rget "as.pairlist(1:10)" -Rget "as.pairlist(TRUE)" -Rget "as.pairlist(as.raw(1))" -Rget "pairlist('rnorm', 10L, 0.0, 2.0 )" +EQUAL[38; Rget "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; +EQUAL[39; Rget "as.pairlist(TRUE)"; (enlist 1i; ())]; +EQUAL[40; Rget "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; +EQUAL[41; Rget "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; Rget "list(x ~ y + z)" -Rget "list( c(1, 5), c(2, 6), c(3, 7) )" -Rget "matrix( 1:16+.5, nc = 4 )" +EQUAL[42; Rget "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; +EQUAL[43; Rget "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; Rget "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" Rget "Instrument$accessors(c('id', 'description'))" Rget "Instrument$new(id='AAPL', description='Apple')" -Rget "(1+1i)" -Rget "(0:9)^2" -Rget"expression(rnorm, rnorm(10), mean(1:10))" -Rget"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )" -Rget"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)" +EQUAL[44; Rget "(1+1i)"; "complex"]; +EQUAL[45; Rget "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; +EQUAL[46; Rget"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; +EQUAL[47; Rget"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; +EQUAL[48; Rget"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; + +PROGRESS["List Test Finished!!"]; + +//Q-Like R Interface//--------------------------/ // long vectors Rcmd"x<-c(as.raw(1))" //Rcmd"x[2147483648L]<-as.raw(1)" -count Rget`x +EQUAL[49; count Rget`x; 1]; -.[Rset;("x[0]";1);"nyi"~] -Rget["c()"]~Rget"NULL" -()~Rget"c()" -{@[Rget;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)) +EQUAL[50; .[Rset;("x[0]";1); "nyi"~]; 1b]; +EQUAL[51; Rget["c()"]; Rget"NULL"]; +EQUAL[52; (); Rget"c()"]; +EQUAL[53; {@[Rget;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; Rset[`x;1] -Rget each ("x";enlist "x";`x;`x`x) // ("x";"x")? +EQUAL[54; Rget each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? + +PROGRESS["Q-Like R Command Test Finished!!"]; + +//Genral Test//----------------------------------/ + Rcmd"rm(x)" // run gc Rget"gc()" -Rset["a";`sym?`a`b`c] -`:x set string 10?`4 -Rset["a";get `:x] -hdel `:x; +//Rset["a";`sym?`a`b`c] +//`:x set string 10?`4 +//Rset["a";get `:x] +//hdel `:x; Rinstall`data.table Rcmd"library(data.table)" Rcmd"a<-data.frame(a=c(1,2))" -Rget`a +EQUAL[55; Rget`a; flip enlist[`a]!enlist (1 2f)]; Rcmd "b<-data.table(a=c(1,2))" -Rget`b +EQUAL[56; Rget`b; flip enlist[`a]!enlist (1 2f)]; Rcmd"inspect <- function(x, ...) .Internal(inspect(x,...))" Rget`inspect Rget"substitute(log(1))" -flip[`a`b`row.names!(`1`2`1;`a`b`b;1 2 3)]~Rget"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))" -flip[`a`b`row.names!(`1`2`1;("a";"b";"b");1 2 3)]~Rget"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))" -flip[`a`b`row.names!(`1`2`1;`10`20`30;1 2 3)]~Rget"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))" - +EQUAL[57; flip[`a`b!(`1`2`1;`a`b`b)]; Rget"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[58; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; Rget"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[59; flip[`a`b!(`1`2`1;`10`20`30)]; Rget"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; +PROGRESS["Completed!!"]; // all {.[Rset;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2) diff --git a/src/common.c b/src/common.c index 43e4edb..7a3463c 100644 --- a/src/common.c +++ b/src/common.c @@ -114,14 +114,67 @@ void make_data_frame(SEXP data) UNPROTECT(3); } -/* for datetime, timestamp */ -static void setdatetimeclass(SEXP sxp) +/* for datetime */ +static SEXP setdatetimeclass(SEXP sxp) { SEXP datetimeclass = PROTECT(allocVector(STRSXP,2)); SET_STRING_ELT(datetimeclass, 0, mkChar("POSIXt")); SET_STRING_ELT(datetimeclass, 1, mkChar("POSIXct")); - setAttrib(sxp, R_ClassSymbol, datetimeclass); - UNPROTECT(2); + classgets(sxp, datetimeclass); + UNPROTECT(1); + return sxp; +} + +/* for timestamp */ +static SEXP settimestampclass(SEXP sxp) { + SEXP classValue; + SEXP tag = PROTECT(mkString(".S3Class")); + SEXP val = PROTECT(mkString("integer64")); + setAttrib(sxp, tag, val); + UNPROTECT(2); + + + classValue= PROTECT(mkString("nanotime")); + tag = PROTECT(mkString("package")); + val = PROTECT(mkString("nanotime")); + setAttrib(classValue, tag, val); + classgets(sxp, classValue); + UNPROTECT(3); + return asS4(sxp,TRUE,0); +} + +static SEXP R_UnitsSymbol = NULL; +static SEXP R_TzSymbol = NULL; + +/* for timespan, minute, second */ +static SEXP setdifftimeclass(SEXP sxp, char* units) { + SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); + SET_STRING_ELT(difftimeclass, 0, mkChar("difftime")); + classgets(sxp, difftimeclass); + if (R_UnitsSymbol == NULL) R_UnitsSymbol = install("units"); + SEXP difftimeunits= PROTECT(allocVector(STRSXP, 1)); + SET_STRING_ELT(difftimeunits, 0, mkChar(units)); + setAttrib(sxp, R_UnitsSymbol, difftimeunits); + UNPROTECT(2); + return sxp; +} + +/* for setting timezone */ +static SEXP settimezone(SEXP sxp, char* tzone) { + SEXP timezone= PROTECT(allocVector(STRSXP, 1)); + SET_STRING_ELT(timezone, 0, mkChar(tzone)); + if (R_TzSymbol == NULL) R_TzSymbol = install("tzone"); + setAttrib(sxp, R_TzSymbol, timezone); + UNPROTECT(1); + return sxp; +} +/* for date,month */ +static SEXP setdateclass(SEXP sxp) { + SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); + SET_STRING_ELT(difftimeclass, 0, mkChar("Date")); + classgets(sxp, difftimeclass); + UNPROTECT(1); + return sxp; } /* @@ -253,14 +306,21 @@ static SEXP error_broken_kobject(K broken) */ static SEXP from_list_of_kobjects(K x) { - SEXP result; - int i, length = x->n; - PROTECT(result = NEW_LIST(length)); - for (i = 0; i < length; i++) { - SET_VECTOR_ELT(result, i, from_any_kobject(xK[i])); - } - UNPROTECT(1); - return result; + SEXP result; + K y; + J i, length= x->n, utype; + PROTECT(result= allocVector(VECSXP,length)); + utype= length > 0 ? kK(x)[0]->t : 0; + for(i= 0; i < length; i++) { + y= kK(x)[i]; + utype= utype == y->t ? utype : 0; + SET_VECTOR_ELT(result, i, from_any_kobject(y)); + } + if(utype == KC) { + result= coerceVector(result, STRSXP); + } + UNPROTECT(1); + return result; } /* @@ -277,39 +337,25 @@ static SEXP from_list_of_kobjects(K x) #define scalar(x) (x->t < 0) -static SEXP from_bool_kobject(K x) -{ - SEXP result; - int length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_LOGICAL(1)); - LOGICAL_POINTER(result)[0] = x->g; - } - else { - int i; - PROTECT(result = NEW_LOGICAL(length)); - for(i = 0; i < length; i++) - LOGICAL_POINTER(result)[i] = kG(x)[i]; - } - UNPROTECT(1); - return result; +static SEXP from_bool_kobject(K x) { + SEXP result; + if(scalar(x)) return ScalarLogical(x->g); + PROTECT(result= allocVector(LGLSXP,x->n)); + for(J i= 0; i < x->n; i++) + LOGICAL(result)[i]= kG(x)[i]; + UNPROTECT(1); + return result; } -static SEXP from_byte_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = (int) x->g; - } - else { - PROTECT(result = NEW_INTEGER(length)); - for(i = 0; i < length; i++) - INTEGER_POINTER(result)[i] = kG(x)[i]; - } - UNPROTECT(1); - return result; +static SEXP from_byte_kobject(K x) { + SEXP result;G*r; + if(scalar(x)) return ScalarRaw(x->g); + PROTECT(result= allocVector(RAWSXP,x->n)); + r=RAW(result); + for(J i= 0; i < x->n; i++) + r[i]= kG(x)[i]; + UNPROTECT(1); + return result; } static SEXP from_guid_kobject(K x) @@ -319,134 +365,94 @@ static SEXP from_guid_kobject(K x) return r; } -static SEXP from_short_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = (int) x->h; - } - else { - PROTECT(result = NEW_INTEGER(xn)); - for(i = 0; i < length; i++) - INTEGER_POINTER(result)[i] = (int) xH[i]; - } - UNPROTECT(1); - return result; +static SEXP from_short_kobject(K x) { + SEXP result; + if(scalar(x)) return ScalarInteger(x->h==nh?NA_INTEGER:(int)x->h); + PROTECT(result= allocVector(INTSXP,x->n)); + for(J i= 0; i < x->n; i++) + INTEGER(result)[i]= kH(x)[i]==nh?NA_INTEGER:kH(x)[i]; + UNPROTECT(1); + return result; } -static SEXP from_int_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = x->i; - } - else { - PROTECT(result = NEW_INTEGER(length)); - for(i = 0; i < length; i++) - INTEGER_POINTER(result)[i] = (int) xI[i]; - } - UNPROTECT(1); - return result; +static SEXP from_int_kobject(K x) { + SEXP result; + if(scalar(x)) return ScalarInteger(x->i); + PROTECT(result= allocVector(INTSXP,x->n)); + for(J i= 0; i < x->n; i++) + INTEGER(result)[i]= kI(x)[i]; + UNPROTECT(1); + return result; } -static SEXP from_long_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_NUMERIC(1)); - NUMERIC_POINTER(result)[0] = (double) x->j; - } - else { - PROTECT(result = NEW_NUMERIC(length)); - for(i = 0; i < length; i++) - NUMERIC_POINTER(result)[i] = (double) xJ[i]; - } - UNPROTECT(1); - return result; +static SEXP from_long_kobject(K x) { + SEXP result; + J i, n=scalar(x)?1:x->n; + PROTECT(result= allocVector(REALSXP,n)); + if(scalar(x)) { + INT64(result)[0]= x->j; + } else { + for(i= 0; i < n; i++) + INT64(result)[i]= kJ(x)[i]; + } + classgets(result, mkString("integer64")); + UNPROTECT(1); + return result; } -static SEXP from_float_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_NUMERIC(1)); - NUMERIC_POINTER(result)[0] = (double) x->e; - } - else { - PROTECT(result = NEW_NUMERIC(length)); - for(i = 0; i < length; i++) - NUMERIC_POINTER(result)[i] = (double) xE[i]; - } - UNPROTECT(1); - return result; +static SEXP from_float_kobject(K x) { + SEXP result; + if(scalar(x)) return ScalarReal(ISNAN(x->e)?R_NaN:x->e); + PROTECT(result= allocVector(REALSXP,x->n)); + for(J i= 0; i < x->n; i++) + REAL(result)[i]= (double) ISNAN(kE(x)[i])?R_NaN:kE(x)[i]; + UNPROTECT(1); + return result; } -static SEXP from_double_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_NUMERIC(1)); - NUMERIC_POINTER(result)[0] = x->f; - } - else { - PROTECT(result = NEW_NUMERIC(length)); - for(i = 0; i < length; i++) - NUMERIC_POINTER(result)[i] = xF[i]; - } - UNPROTECT(1); - return result; +static SEXP from_double_kobject(K x) { + SEXP result; + if(scalar(x)) return ScalarReal(ISNAN(x->f)?R_NaN:x->f); + PROTECT(result= allocVector(REALSXP,x->n)); + for(J i= 0; i < x->n; i++) + REAL(result)[i]= ISNAN(kF(x)[i])?R_NaN:kF(x)[i]; + UNPROTECT(1); + return result; } -static SEXP from_string_kobject(K x) -{ - SEXP result; - int length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_CHARACTER(1)); - SET_STRING_ELT(result, 0, mkCharLen((S)&x->g,1)); - } - else { - PROTECT(result = allocVector(STRSXP, 1)); - SET_STRING_ELT(result, 0, mkCharLen((S)xG,length)); - }; - UNPROTECT(1); - return result; +static SEXP from_string_kobject(K x) { + SEXP result; + J n=scalar(x)?1:x->n; + PROTECT(result= allocVector(STRSXP,1)); + if(scalar(x)) { + SET_STRING_ELT(result, 0, mkCharLen((S) &x->g, 1)); + } else { + SET_STRING_ELT(result, 0, mkCharLen((S) kC(x), n)); + }; + UNPROTECT(1); + return result; } static SEXP from_string_column_kobject(K x) { SEXP result; - int i, length = x->n; - PROTECT(result = NEW_CHARACTER(length)); - for(i = 0; i < length; i++) { + J i, n=scalar(x)?1:x->n; + PROTECT(result= allocVector(STRSXP,n)); + for(i = 0; i < n; i++) { SET_STRING_ELT(result, i, mkCharLen((S)&kC(x)[i],1)); } UNPROTECT(1); return result; } -static SEXP from_symbol_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_CHARACTER(1)); - SET_STRING_ELT(result, 0, mkChar(xs)); - } - else { - PROTECT(result = NEW_CHARACTER(length)); - for(i = 0; i < length; i++) - SET_STRING_ELT(result, i, mkChar((S)xS[i])); - } - UNPROTECT(1); - return result; +static SEXP from_symbol_kobject(K x) { + SEXP result; + if(scalar(x)) return mkString(x->s); + PROTECT(result= allocVector(STRSXP,x->n)); + for(J i= 0; i < x->n; i++) + SET_STRING_ELT(result, i, mkChar(kS(x)[i])); + UNPROTECT(1); + return result; } static SEXP from_month_kobject(K object) @@ -454,102 +460,69 @@ static SEXP from_month_kobject(K object) return from_int_kobject(object); } -static SEXP from_date_kobject(K x) -{ - SEXP result; - SEXP dateclass; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = x->i + 10957; - } - else { - PROTECT(result = NEW_INTEGER(length)); - for(i = 0; i < length; i++) - INTEGER_POINTER(result)[i] = (int) xI[i] + 10957; - } - dateclass = PROTECT(allocVector(STRSXP,1)); - SET_STRING_ELT(dateclass, 0, mkChar("Date")); - setAttrib(result, R_ClassSymbol, dateclass); - UNPROTECT(2); - return result; +static SEXP from_date_kobject(K x) { + SEXP result=PROTECT(from_int_kobject(x)); + for(J i= 0; i < XLENGTH(result); i++) + if(INTEGER(result)[i]!=NA_INTEGER) INTEGER(result)[i]+=kdbDateOffset; + setdateclass(result); + UNPROTECT(1); + return result; } -static SEXP from_datetime_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_NUMERIC(1)); - NUMERIC_POINTER(result)[0] = (x->f + 10957) * 86400; - } - else { - PROTECT(result = NEW_NUMERIC(length)); - for(i = 0; i < length; i++) - NUMERIC_POINTER(result)[i] = (kF(x)[i] + 10957) * 86400; - } +static SEXP from_datetime_kobject(K x) { + SEXP result=PROTECT(from_double_kobject(x)); + for(J i= 0; i < XLENGTH(result); i++) + REAL(result)[i]= REAL(result)[i]* sec2day + kdbDateOffset * sec2day; setdatetimeclass(result); - return result; + settimezone(result,"GMT"); + UNPROTECT(1); + return result; } -static SEXP from_minute_kobject(K object) -{ - return from_int_kobject(object); -} +static SEXP from_minute_kobject(K object) { + SEXP result=PROTECT(from_int_kobject(object)); + setdifftimeclass(result,"mins"); + UNPROTECT(1); + return result; + } -static SEXP from_second_kobject(K object) -{ - return from_int_kobject(object); -} +static SEXP from_second_kobject(K object) { + SEXP result=PROTECT(from_int_kobject(object)); + setdifftimeclass(result,"secs"); + UNPROTECT(1); + return result; + } static SEXP from_time_kobject(K object) { return from_int_kobject(object); } -static SEXP from_timespan_kobject(K x) -{ - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_NUMERIC(1)); - NUMERIC_POINTER(result)[0] = x->j / 1e9; - } - else { - PROTECT(result = NEW_NUMERIC(length)); - for(i = 0; i < length; i++) - NUMERIC_POINTER(result)[i] = xJ[i] / 1e9; - } - UNPROTECT(1); - return result; +static SEXP from_timespan_kobject(K x) { + return from_long_kobject(x); } static SEXP from_timestamp_kobject(K x) { - SEXP result; - int i, length = x->n; - if (scalar(x)) { - PROTECT(result = NEW_NUMERIC(1)); - NUMERIC_POINTER(result)[0] = 946684800 + x->j / 1e9; - } - else { - PROTECT(result = NEW_NUMERIC(length)); - for(i = 0; i < length; i++) - NUMERIC_POINTER(result)[i] = 946684800 + kJ(x)[i] / 1e9; - } - setdatetimeclass(result); - return result; + SEXP result=from_long_kobject(x); + J i,n=XLENGTH(result); + PROTECT(result); + for(i= 0; i < n; i++) + if(INT64(result)[i]!=nj)INT64(result)[i]+=epoch_offset; + settimestampclass(result); + UNPROTECT(1); + return result; } static SEXP from_dictionary_kobject(K x) { SEXP names, result; - K table; + K table, k= kK(x)[0], v= kK(x)[1]; /* if keyed, try to create a simple table */ /* ktd will free its argument if successful */ /* if fails, x is still valid */ - if (XT==xx->t && XT==xy->t) { + if (XT==k->t && XT==v->t) { r1(x); if ((table = ktd(x))) { result = from_table_kobject(table); @@ -559,9 +532,9 @@ static SEXP from_dictionary_kobject(K x) r0(x); } - PROTECT(names = from_any_kobject(xx)); - PROTECT(result = from_any_kobject(xy)); - SET_NAMES(result, names); + PROTECT(names = from_any_kobject(k)); + PROTECT(result = from_any_kobject(v)); + setAttrib(result, R_NamesSymbol, names); UNPROTECT(2); return result; } diff --git a/src/rserver.c b/src/rserver.c index 4d11dad..580d62c 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -6,6 +6,7 @@ * https://cran.r-project.org/doc/manuals/r-release/R-ints.pdf * https://cran.r-project.org/doc/manuals/r-release/R-exts.html */ + K ropen(K x); K rclose(K x); K rcmd(K x); @@ -15,6 +16,7 @@ K rset(K x,K y); ZK rexec(int type,K x); ZK kintv(J len, int *val); ZK kinta(J len, int rank, int *shape, int *val); +ZK klonga(J len, int rank, int *shape, J*val); ZK kdoublev(J len, double *val); ZK kdoublea(J len, int rank, int *shape, double *val); ZK from_any_robject(SEXP sxp); @@ -32,70 +34,127 @@ ZK from_symbol_robject(SEXP); ZK from_pairlist_robject(SEXP); ZK from_closure_robject(SEXP); ZK from_language_robject(SEXP); +ZK from_date_robject(SEXP); +ZK from_datetime_robject(SEXP); +ZK from_datetime_ct_robject(SEXP); +ZK from_datetime_lt_robject(SEXP); ZK from_char_robject(SEXP); ZK from_logical_robject(SEXP); ZK from_integer_robject(SEXP); ZK from_double_robject(SEXP); ZK from_character_robject(SEXP); ZK from_vector_robject(SEXP); -ZK from_raw_robject(SEXP sxp); -ZK from_nyi_robject(S m,SEXP sxp); +ZK from_raw_robject(SEXP); +ZK from_nyi_robject(SEXP); +ZK from_frame_robject(SEXP); +ZK from_factor_robject(SEXP); + +Rboolean isClass(const char *class_, SEXP s) { + SEXP klass; + int i; + if(OBJECT(s)) { + klass= getAttrib(s, R_ClassSymbol); + for(i= 0; i < length(klass); i++) + if(!strcmp(CHAR(STRING_ELT(klass, i)), class_)) + return TRUE; + } + return FALSE; +} -ZK from_any_robject(SEXP sxp) -{ - K result = 0; - int type = TYPEOF(sxp); - switch (type) { - case NILSXP : return from_null_robject(sxp); break; /* nil = NULL */ - case SYMSXP : return from_symbol_robject(sxp); break; /* symbols */ - case LISTSXP : return from_pairlist_robject(sxp); break; /* lists of dotted pairs */ - case CLOSXP : return from_closure_robject(sxp); break; /* closures */ - case ENVSXP : return from_nyi_robject("environment",sxp); break; /* environments */ - case PROMSXP : return from_nyi_robject("promise",sxp); break; /* promises: [un]evaluated closure arguments */ - case LANGSXP : return from_language_robject(sxp); break; /* language constructs (special lists) */ - case SPECIALSXP : return from_nyi_robject("special",sxp); break; /* special forms */ - case BUILTINSXP : return from_nyi_robject("builtin",sxp); break; /* builtin non-special forms */ - case CHARSXP : return from_char_robject(sxp); break; /* "scalar" string type (internal only)*/ - case LGLSXP : return from_logical_robject(sxp); break; /* logical vectors */ - case INTSXP : return from_integer_robject(sxp); break; /* integer vectors */ - case REALSXP : return from_double_robject(sxp); break; /* real variables */ - case CPLXSXP : return from_nyi_robject("complex", sxp); break; /* complex variables */ - case STRSXP : return from_character_robject(sxp); break; /* string vectors */ - case DOTSXP : return from_nyi_robject("dot",sxp); break; /* dot-dot-dot object */ - case ANYSXP : return error_broken_robject(sxp); break; /* make "any" args work */ - case VECSXP : return from_vector_robject(sxp); break; /* generic vectors */ - case EXPRSXP : return from_nyi_robject("exprlist",sxp); break; /* sxps vectors */ - case BCODESXP : return from_nyi_robject("bcode",sxp); break; /* byte code */ - case EXTPTRSXP : return from_nyi_robject("external",sxp); break; /* external pointer */ - case WEAKREFSXP : return error_broken_robject(sxp); break; /* weak reference */ - case RAWSXP : return from_raw_robject(sxp); break; /* raw bytes */ - case S4SXP : return from_nyi_robject("s4",sxp); break; /* S4 non-vector */ - - case NEWSXP : return error_broken_robject(sxp); break; /* fresh node created in new page */ - case FREESXP : return error_broken_robject(sxp); break; /* node released by GC */ - case FUNSXP : return from_nyi_robject("fun",sxp); break; /* Closure or Builtin */ +ZK from_any_robject(SEXP sxp){ + if(isClass("data.frame", sxp)) { + return from_frame_robject(sxp); + } + if(isClass("factor", sxp)) { + return from_factor_robject(sxp); + } + if(isClass("Date", sxp)){ + return from_date_robject(sxp); + } + if(isClass("POSIXt", sxp)){ + return from_datetime_robject(sxp); + } + K result = 0; + int type = TYPEOF(sxp); + switch (type) { + case NILSXP : + return from_null_robject(sxp); + break; /* nil = NULL */ + case SYMSXP : + return from_symbol_robject(sxp); + break; /* symbols */ + case LISTSXP : + return from_pairlist_robject(sxp); + break; /* lists of dotted pairs */ + case CLOSXP : + return from_closure_robject(sxp); + break; /* closures */ + case LANGSXP : + return from_language_robject(sxp); + break; /* language constructs (special lists) */ + case CHARSXP : + return from_char_robject(sxp); + break; /* "scalar" string type (internal only)*/ + case LGLSXP : + return from_logical_robject(sxp); + break; /* logical vectors */ + case RAWSXP : + return from_raw_robject(sxp); + break; /* raw bytes */ + case INTSXP : + return from_integer_robject(sxp); + break; /* integer vectors */ + case REALSXP : + return from_double_robject(sxp); + break; /* real variables */ + case STRSXP : + return from_character_robject(sxp); + break; /* string vectors */ + case VECSXP : + return from_vector_robject(sxp); + break; /* generic vectors */ + case FREESXP : + return error_broken_robject(sxp); + break; /* node released by GC */ + case ANYSXP : + return error_broken_robject(sxp); + break; /* make "any" args work */ + case EXPRSXP : + case BCODESXP : + case EXTPTRSXP : + case WEAKREFSXP : + case S4SXP : + case NEWSXP : + case FUNSXP : + case PROMSXP : + case SPECIALSXP : + case BUILTINSXP : + case ENVSXP : + case CPLXSXP : + case DOTSXP : + return from_nyi_robject(sxp); + break; } return result; } -ZK dictpairlist(SEXP sxp) -{ - K k = ktn(0,length(sxp)); - K v = ktn(0,length(sxp)); - SEXP s = sxp;J i; - for(i=0;in; + K res=ktn(KZ, element_length); + for(i=0; i < element_length; i++){ + //Relying on the order of tm key + struct tm dttm; + dttm.tm_sec =kF(kK(x)[0])[i]; + dttm.tm_min =kI(kK(x)[1])[i]; + dttm.tm_hour =kI(kK(x)[2])[i]; + dttm.tm_mday =kI(kK(x)[3])[i]; + dttm.tm_mon =kI(kK(x)[4])[i]; + dttm.tm_year =kI(kK(x)[5])[i]; + dttm.tm_wday =kI(kK(x)[6])[i]; + dttm.tm_yday =kI(kK(x)[7])[i]; + dttm.tm_isdst=kI(kK(x)[8])[i]; + kF(res)[i]=(((F)mktime(&dttm)-kdbSecOffset)/sec2day); + } + return res; +} + +//Wraper function of POSIXt +ZK from_datetime_robject(SEXP sxp){ + if(isClass("POSIXct", sxp)) + return from_datetime_ct_robject(sxp); + else + return from_datetime_lt_robject(sxp); +} + // NULL in R(R_NilValue): often used as generic zero length vector // NULL objects cannot have attributes and attempting to assign one by attr gives an error ZK from_null_robject(SEXP sxp) @@ -177,72 +321,72 @@ ZK from_logical_robject(SEXP sxp) { K x; J len = XLENGTH(sxp); - int *s = malloc(len*sizeof(int)); - DO(len,s[i]=LOGICAL_POINTER(sxp)[i]); - SEXP dim = GET_DIM(sxp); + SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { - x = kintv(len,s); - free(s); + x = kintv(len,LOGICAL(sxp)); return attR(x,sxp); } - x = kinta(len,length(dim),INTEGER(dim),s); - free(s); - SEXP dimnames = GET_DIMNAMES(sxp); + x = kinta(len,length(dim),INTEGER(dim),LOGICAL(sxp)); + SEXP dimnames= getAttrib(sxp, R_DimNamesSymbol); if (!isNull(dimnames)) return attR(x,sxp); SEXP e; PROTECT(e = duplicate(sxp)); - SET_DIM(e, R_NilValue); + setAttrib(e, R_DimSymbol, R_NilValue); x = attR(x,e); UNPROTECT(1); return x; } -ZK from_integer_robject(SEXP sxp) -{ +ZK from_integer_robject(SEXP sxp){ K x; J len = XLENGTH(sxp); - int *s = malloc(len*sizeof(int)); - DO(len,s[i]=INTEGER_POINTER(sxp)[i]); - SEXP dim = GET_DIM(sxp); + SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { - x = kintv(len,s); - free(s); + x = kintv(len,INTEGER(sxp)); return attR(x,sxp); } - x = kinta(len,length(dim),INTEGER(dim),s); - free(s); - SEXP dimnames = GET_DIMNAMES(sxp); + x = kinta(len,length(dim),INTEGER(dim),INTEGER(sxp)); + SEXP dimnames = getAttrib(sxp, R_DimNamesSymbol); if (!isNull(dimnames)) return attR(x,sxp); SEXP e; PROTECT(e = duplicate(sxp)); - SET_DIM(e, R_NilValue); + setAttrib(e, R_DimSymbol, R_NilValue); x = attR(x,e); UNPROTECT(1); return x; } -ZK from_double_robject(SEXP sxp) -{ +ZK from_double_robject(SEXP sxp){ K x; + I nano, bit64=isClass("integer64",sxp); J len = XLENGTH(sxp); - double *s = malloc(len*sizeof(double)); - DO(len,s[i]=REAL(sxp)[i]); - SEXP dim = GET_DIM(sxp); + SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { - x = kdoublev(len,s); - free(s); - return attR(x,sxp); + nano = isClass("nanotime",sxp); + if(nano || bit64) { + x=ktn(nano?KP:KJ,len); + DO(len,kJ(x)[i]=INT64(sxp)[i]) + if(nano) + DO(len,if(kJ(x)[i]!=nj)kJ(x)[i]-=epoch_offset) + return x; + } + x= kdoublev(len, REAL(sxp)); + return attR(x, sxp); } - x = kdoublea(len,length(dim),INTEGER(dim),s); - free(s); + if(bit64){ + x= klonga(len, length(dim), INTEGER(dim), (J*)REAL(sxp)); + }else{ + x= kdoublea(len, length(dim), INTEGER(dim), REAL(sxp)); + } SEXP dimnames = GET_DIMNAMES(sxp); if (!isNull(dimnames)) return attR(x,sxp); SEXP e; PROTECT(e = duplicate(sxp)); - SET_DIM(e, R_NilValue); + setAttrib(e, R_DimSymbol, R_NilValue); + if(bit64) classgets(e,R_NilValue); x = attR(x,e); UNPROTECT(1); return x; @@ -270,7 +414,16 @@ ZK from_vector_robject(SEXP sxp) for (i = 0; i < length; i++) { xK[i] = from_any_robject(VECTOR_ELT(sxp, i)); } - return attR(x,sxp); + SEXP colNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(colNames)&&length==XLENGTH(colNames)){ + K k= ktn(KS, length); + for(i= 0; i < length; i++) { + const char *colName= CHAR(STRING_ELT(colNames, i)); + kS(k)[i]= ss((S) colName); + } + return xD(k,x); + } + return attR(x, sxp); } /* @@ -312,23 +465,61 @@ ZK kinta(J len, int rank, int *shape, int *val) K x,y; J i,j,r,c,k; switch (rank) { - case 1 : x = kintv(len,val); break; + case 1 : + x = kintv(len,val); + break; case 2 : - r = shape[0]; c = shape[1]; x = knk(0); - for (i=0;i Date: Sat, 11 Apr 2020 18:02:12 +0900 Subject: [PATCH 02/33] Name change of interface function --- rinit.q | 26 +++---- rtest.q | 210 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 118 insertions(+), 118 deletions(-) diff --git a/rinit.q b/rinit.q index 9a53410..64d280d 100644 --- a/rinit.q +++ b/rinit.q @@ -1,19 +1,19 @@ / R server for Q -Rclose:`embedr 2:(`rclose;1) -Ropen:`embedr 2:(`ropen;1) -Rcmd0:`embedr 2:(`rcmd;1) -Rget0:`embedr 2:(`rget;1) -Rset0:`embedr 2:(`rset;2) -Rcmd:Rcmd0; -Rget:Rget0; -Rset:Rset0; +.rk.close:`embedr 2:(`rclose;1) +.rk.open:`embedr 2:(`ropen;1) +.rk.exec:`embedr 2:(`rcmd;1) +.rk.get:`embedr 2:(`rget;1) +.rk.set:`embedr 2:(`rset;2) +//Rcmd:Rcmd0; +//Rget:Rget0; +//Rset:Rset0; -Rinstall:{[pkg] +.rk.install:{[pkg] pkg:$[-11=type pkg;string pkg;pkg];rcloud:"https://cloud.r-project.org"; - if[0i=first Rget"is.element('",pkg,"',installed.packages()[,1])"; - Rcmd"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; + if[0i=first .rk.get"is.element('",pkg,"',installed.packages()[,1])"; + .rk.exec"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; } -Roff:{Rcmd "dev.off()"} -Rnew:{Rcmd "dev.new(noRStudioGD=TRUE)"} +Roff:{.rk.exec "dev.off()"} +Rnew:{.rk.exec "dev.new(noRStudioGD=TRUE)"} setenv[`R_HOME;first @[system;@[.z.o like "w*";"call";"env"]," R RHOME";enlist""]] diff --git a/rtest.q b/rtest.q index 72074ed..2c36070 100644 --- a/rtest.q +++ b/rtest.q @@ -38,7 +38,7 @@ EQUAL:{[id;x;y] \c 25 200 // set verbose mode -Ropen 1 +.rk.open 1 //%% Test %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ @@ -46,178 +46,178 @@ Ropen 1 PROGRESS["Test Start!!"]; -Rcmd "a=array(1:24,c(2,3,4))" +.rk.exec "a=array(1:24,c(2,3,4))" -EQUAL[1; Rget "dim(a)"; 2 3 4i]; -EQUAL[2; Rget "a"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; +EQUAL[1; .rk.get "dim(a)"; 2 3 4i]; +EQUAL[2; .rk.get "a"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; -if[3<=.z.K;Rset["a";2?0Ng]] -EQUAL[3; Rget "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed1-f4df-58fcdedd8376")]; +if[3<=.z.K;.rk.set["a";2?0Ng]] +EQUAL[3; .rk.get "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed1-f4df-58fcdedd8376")]; -Rcmd "b= 2 == array(1:24,c(2,3,4))" -EQUAL[4; Rget "dim(b)"; 2 3 4i]; -EQUAL[5; Rget "b"; ((0 0 0i;1 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i))]; +.rk.exec "b= 2 == array(1:24,c(2,3,4))" +EQUAL[4; .rk.get "dim(b)"; 2 3 4i]; +EQUAL[5; .rk.get "b"; ((0 0 0i;1 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i))]; -EQUAL[6; Rget "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11.0 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22.0 24.2 26.4))]; +EQUAL[6; .rk.get "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11.0 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22.0 24.2 26.4))]; -Rset["xyz";1 2 3i] -EQUAL[7; Rget "xyz"; 1 2 3i]; +.rk.set["xyz";1 2 3i] +EQUAL[7; .rk.get "xyz"; 1 2 3i]; -EQUAL[8; Rget "pi"; (), acos -1]; -EQUAL[9; Rget "2+3"; (), 5f]; -EQUAL[10; Rget "11:11"; (), 11i]; -EQUAL[11; Rget "11:15"; 11 12 13 14 15i]; -a:Rget "matrix(1:6,2,3)" +EQUAL[8; .rk.get "pi"; (), acos -1]; +EQUAL[9; .rk.get "2+3"; (), 5f]; +EQUAL[10; .rk.get "11:11"; (), 11i]; +EQUAL[11; .rk.get "11:15"; 11 12 13 14 15i]; +a:.rk.get "matrix(1:6,2,3)" EQUAL[12; a[1]; 2 4 6i]; -Rcmd "m=array(1:24,c(2,3,4))" -EQUAL[13; Rget "m"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; -EQUAL[14; Rget "length(m)"; (), 24i]; -EQUAL[15; Rget "dim(m)"; 2 3 4i]; -EQUAL[16; Rget "c(1,2,Inf,-Inf,NaN,NA)"; 1 2 0w -0w 0n 0n]; +.rk.exec "m=array(1:24,c(2,3,4))" +EQUAL[13; .rk.get "m"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; +EQUAL[14; .rk.get "length(m)"; (), 24i]; +EQUAL[15; .rk.get "dim(m)"; 2 3 4i]; +EQUAL[16; .rk.get "c(1,2,Inf,-Inf,NaN,NA)"; 1 2 0w -0w 0n 0n]; PROGRESS["Numeric Array Finished!!"]; //Plot Functionality//----------------------/ -Rcmd "pdf(tempfile(\"t1\",fileext=\".pdf\"))" -Rcmd "plot(c(2,3,5,7,11))" -Rcmd "dev.off()" +.rk.exec "pdf(tempfile(\"t1\",fileext=\".pdf\"))" +.rk.exec "plot(c(2,3,5,7,11))" +.rk.exec "dev.off()" //Function Test//---------------------------/ -Rcmd "x=factor(c('one','two','three','four'))" -EQUAL[17; Rget "x"; `one`two`three`four]; -EQUAL[18; Rget "mode(x)"; "numeric"]; -EQUAL[19; Rget "typeof(x)"; "integer"]; -EQUAL[20; Rget "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 1 0 0N 1 1 0i]; -Rcmd "foo <- function(x,y) {x + 2 * y}" -Rget "foo" -EQUAL[21; Rget "typeof(foo)"; "closure"]; -EQUAL[22; Rget "foo (5,3)"; (), 11f]; +.rk.exec "x=factor(c('one','two','three','four'))" +EQUAL[17; .rk.get "x"; `one`two`three`four]; +EQUAL[18; .rk.get "mode(x)"; "numeric"]; +EQUAL[19; .rk.get "typeof(x)"; "integer"]; +EQUAL[20; .rk.get "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 1 0 0N 1 1 0i]; +.rk.exec "foo <- function(x,y) {x + 2 * y}" +.rk.get "foo" +EQUAL[21; .rk.get "typeof(foo)"; "closure"]; +EQUAL[22; .rk.get "foo (5,3)"; (), 11f]; PROGRESS["Function Test Finished!!"]; //Object//-----------------------------------/ -Rget "wilcox.test(c(1,2,3),c(4,5,6))" -Rcmd "data(OrchardSprays)" -a:Rget "OrchardSprays" -a +show .rk.get "wilcox.test(c(1,2,3),c(4,5,6))" +.rk.exec "data(OrchardSprays)" +show .rk.get "OrchardSprays" + // to install package in non-interactive way // install.packages("zoo", repos="http://cran.r-project.org") -Rget"install.packages" +.rk.get"install.packages" //'Broken R object. -Rget".GlobalEnv" +EQUAL[23; .rk.get".GlobalEnv"; "environment"]; //"environment" -Rget"emptyenv()" +EQUAL[24; .rk.get"emptyenv()"; "environment"]; //"environment" -Rget".Internal" +EQUAL[25; .rk.get".Internal"; "special"]; //"special" -@[Rcmd;"typeof(";like[;"incomplete: *"]] -@[Rcmd;"typeof()";like[;"eval error*"]] -Rget each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"dim";"length";"names";".External") -Rget "getGeneric('+')" +EQUAL[26; @[.rk.exec; "typeof("; like[;"incomplete: *"]]; 1b]; +EQUAL[27; @[.rk.exec; "typeof()"; like[;"eval error*"]]; 1b]; +EQUAL[28; .rk.get each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"dim";"length";"names";".External"); ("builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin")]; +.rk.get "getGeneric('+')" -EQUAL[23; Rget"as.raw(10)"; (), 0x0a]; -EQUAL[24; Rget"as.logical(c(1,FALSE,NA))"; 1 0 0Ni]; +EQUAL[29; .rk.get"as.raw(10)"; (), 0x0a]; +EQUAL[30; .rk.get"as.logical(c(1,FALSE,NA))"; 1 0 0Ni]; PROGRESS["Onject Test Finished!!"]; //Table//-----------------------------------/ // data.frame -EQUAL[25; Rget"data.frame(a=1:3, b=c('a','b','c'))"; flip `a`b!(1 2 3i;`a`b`c)]; -EQUAL[26; Rget"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b!(1 2 3i;1#/:("a";"b";"c"))]; -EQUAL[27; Rget"data.frame(a=1:3)"; flip enlist[`a]!enlist (1 2 3i)]; -EQUAL[28; Rget"data.frame()"; ()]; +EQUAL[31; .rk.get"data.frame(a=1:3, b=c('a','b','c'))"; flip `a`b!(1 2 3i;`a`b`c)]; +EQUAL[32; .rk.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b!(1 2 3i;1#/:("a";"b";"c"))]; +EQUAL[33; .rk.get"data.frame(a=1:3)"; flip enlist[`a]!enlist (1 2 3i)]; +EQUAL[34; .rk.get"data.frame()"; ()]; PROGRESS["Table Test Finished!!"]; //Time//------------------------------------/ // dates -EQUAL[29; Rget"as.Date('2005-12-31')"; (), 2005.12.31]; -EQUAL[30; Rget"as.Date(NA)"; (), 0Nd]; -EQUAL[31; Rget"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; +EQUAL[35; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; +EQUAL[36; .rk.get"as.Date(NA)"; (), 0Nd]; +EQUAL[37; .rk.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; // datetime -Rcmd["Sys.setenv(TZ='UTC')"]; -EQUAL[32; Rget"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[33; Rget"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[34; Rget"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; -EQUAL[35; Rget"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; -Rset["dttm"; 2018.02.18T04:00:01.000z]; -EQUAL[36; Rget"dttm"; (), 2018.02.18T04:00:01.000z]; +.rk.exec["Sys.setenv(TZ='UTC')"]; +EQUAL[38; .rk.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[39; .rk.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[40; .rk.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[41; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +.rk.set["dttm"; 2018.02.18T04:00:01.000z]; +EQUAL[42; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; //timestamp -Rset["tmstp"; 2020.03.16D17:30:45.123456789]; -EQUAL[37; Rget"tmstp"; (), 2020.03.16D17:30:45.123456789]; +.rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; +EQUAL[43; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; PROGRESS["Time Test Finished!!"]; //List//---------------------------------------/ //lang -EQUAL[38; Rget "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; -EQUAL[39; Rget "as.pairlist(TRUE)"; (enlist 1i; ())]; -EQUAL[40; Rget "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; -EQUAL[41; Rget "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; -Rget "list(x ~ y + z)" -EQUAL[42; Rget "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; -EQUAL[43; Rget "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; -Rget "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" -Rget "Instrument$accessors(c('id', 'description'))" -Rget "Instrument$new(id='AAPL', description='Apple')" -EQUAL[44; Rget "(1+1i)"; "complex"]; -EQUAL[45; Rget "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; -EQUAL[46; Rget"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; -EQUAL[47; Rget"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; -EQUAL[48; Rget"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; +EQUAL[44; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; +EQUAL[45; .rk.get "as.pairlist(TRUE)"; (enlist 1i; ())]; +EQUAL[46; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; +EQUAL[47; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; +.rk.get "list(x ~ y + z)" +EQUAL[48; .rk.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; +EQUAL[49; .rk.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; +.rk.get "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" +.rk.get "Instrument$accessors(c('id', 'description'))" +.rk.get "Instrument$new(id='AAPL', description='Apple')" +EQUAL[50; .rk.get "(1+1i)"; "complex"]; +EQUAL[51; .rk.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; +EQUAL[52; .rk.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; +EQUAL[53; .rk.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; +EQUAL[54; .rk.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; PROGRESS["List Test Finished!!"]; //Q-Like R Interface//--------------------------/ // long vectors -Rcmd"x<-c(as.raw(1))" -//Rcmd"x[2147483648L]<-as.raw(1)" -EQUAL[49; count Rget`x; 1]; +.rk.exec"x<-c(as.raw(1))" +//.rk.exec"x[2147483648L]<-as.raw(1)" +EQUAL[55; count .rk.get`x; 1]; -EQUAL[50; .[Rset;("x[0]";1); "nyi"~]; 1b]; -EQUAL[51; Rget["c()"]; Rget"NULL"]; -EQUAL[52; (); Rget"c()"]; -EQUAL[53; {@[Rget;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; -Rset[`x;1] -EQUAL[54; Rget each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? +EQUAL[56; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; +EQUAL[57; .rk.get["c()"]; .rk.get"NULL"]; +EQUAL[58; (); .rk.get"c()"]; +EQUAL[59; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; +.rk.set[`x;1] +EQUAL[60; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? PROGRESS["Q-Like R Command Test Finished!!"]; //Genral Test//----------------------------------/ -Rcmd"rm(x)" +.rk.exec"rm(x)" // run gc -Rget"gc()" +.rk.get"gc()" -//Rset["a";`sym?`a`b`c] +//.rk.set["a";`sym?`a`b`c] //`:x set string 10?`4 -//Rset["a";get `:x] +//.rk.set["a";get `:x] //hdel `:x; -Rinstall`data.table -Rcmd"library(data.table)" -Rcmd"a<-data.frame(a=c(1,2))" -EQUAL[55; Rget`a; flip enlist[`a]!enlist (1 2f)]; -Rcmd "b<-data.table(a=c(1,2))" -EQUAL[56; Rget`b; flip enlist[`a]!enlist (1 2f)]; -Rcmd"inspect <- function(x, ...) .Internal(inspect(x,...))" -Rget`inspect -Rget"substitute(log(1))" +.rk.install`data.table +.rk.exec"library(data.table)" +.rk.exec"a<-data.frame(a=c(1,2))" +EQUAL[61; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; +.rk.exec "b<-data.table(a=c(1,2))" +EQUAL[62; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; +.rk.exec"inspect <- function(x, ...) .Internal(inspect(x,...))" +.rk.get`inspect +.rk.get"substitute(log(1))" -EQUAL[57; flip[`a`b!(`1`2`1;`a`b`b)]; Rget"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[58; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; Rget"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[59; flip[`a`b!(`1`2`1;`10`20`30)]; Rget"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; +EQUAL[63; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[64; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[65; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; PROGRESS["Completed!!"]; -// all {.[Rset;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2) +// all {.[.rk.set;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2) From 8f7e75284f60438f7e69847ec16186b26b64e7b4 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Mon, 13 Apr 2020 11:19:38 +0900 Subject: [PATCH 03/33] Fixed boolean conversion bug --- rtest.q | 8 ++++---- src/rserver.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/rtest.q b/rtest.q index 2c36070..93b2c00 100644 --- a/rtest.q +++ b/rtest.q @@ -56,7 +56,7 @@ EQUAL[3; .rk.get "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed .rk.exec "b= 2 == array(1:24,c(2,3,4))" EQUAL[4; .rk.get "dim(b)"; 2 3 4i]; -EQUAL[5; .rk.get "b"; ((0 0 0i;1 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i))]; +EQUAL[5; .rk.get "b"; ((000b;100b);(000b;000b);(000b;000b);(000b;000b))]; EQUAL[6; .rk.get "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11.0 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22.0 24.2 26.4))]; @@ -89,7 +89,7 @@ PROGRESS["Numeric Array Finished!!"]; EQUAL[17; .rk.get "x"; `one`two`three`four]; EQUAL[18; .rk.get "mode(x)"; "numeric"]; EQUAL[19; .rk.get "typeof(x)"; "integer"]; -EQUAL[20; .rk.get "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 1 0 0N 1 1 0i]; +EQUAL[20; .rk.get "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 100110b]; .rk.exec "foo <- function(x,y) {x + 2 * y}" .rk.get "foo" EQUAL[21; .rk.get "typeof(foo)"; "closure"]; @@ -120,7 +120,7 @@ EQUAL[28; .rk.get each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"di .rk.get "getGeneric('+')" EQUAL[29; .rk.get"as.raw(10)"; (), 0x0a]; -EQUAL[30; .rk.get"as.logical(c(1,FALSE,NA))"; 1 0 0Ni]; +EQUAL[30; .rk.get"as.logical(c(1,FALSE,NA))"; 100b]; PROGRESS["Onject Test Finished!!"]; @@ -160,7 +160,7 @@ PROGRESS["Time Test Finished!!"]; //lang EQUAL[44; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; -EQUAL[45; .rk.get "as.pairlist(TRUE)"; (enlist 1i; ())]; +EQUAL[45; .rk.get "as.pairlist(TRUE)"; (enlist 1b; ())]; EQUAL[46; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; EQUAL[47; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; .rk.get "list(x ~ y + z)" diff --git a/src/rserver.c b/src/rserver.c index 580d62c..a3fb49e 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -14,6 +14,8 @@ K rget(K x); K rset(K x,K y); ZK rexec(int type,K x); +ZK klogicv(J len, int *val); +ZK klogica(J len, int rank, int *shape, int *val); ZK kintv(J len, int *val); ZK kinta(J len, int rank, int *shape, int *val); ZK klonga(J len, int rank, int *shape, J*val); @@ -323,10 +325,10 @@ ZK from_logical_robject(SEXP sxp) J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { - x = kintv(len,LOGICAL(sxp)); + x = klogicv(len,LOGICAL(sxp)); return attR(x,sxp); } - x = kinta(len,length(dim),INTEGER(dim),LOGICAL(sxp)); + x = klogica(len,length(dim),INTEGER(dim),LOGICAL(sxp)); SEXP dimnames= getAttrib(sxp, R_DimNamesSymbol); if (!isNull(dimnames)) return attR(x,sxp); @@ -450,9 +452,44 @@ static char * getkstring(K x) /* * convert R arrays to K lists - * done for int, double + * done for boolean, int, double */ +ZK klogicv(J len, int *val) { + K x= ktn(KB, len); + DO(len, kG(x)[i]= (val)[i]); + return x; +} + +ZK klogica(J len, int rank, int *shape, int *val) { + K x, y; + J i, j, r, c, k; + switch(rank) { + case 1: + x= kintv(len, val); + break; + case 2: + r= shape[0]; + c= shape[1]; + x= knk(0); + for(i= 0; i < r; i++) { + y= ktn(KB, c); + for(j= 0; j < c; j++) + kG(y)[j]= val[i + r * j]; + x= jk(&x, y); + }; + break; + default: + k= rank - 1; + r= shape[k]; + c= len / r; + x= knk(0); + for(i= 0; i < r; i++) + x= jk(&x, klogica(c, k, shape, val + c * i)); + } + return x; +} + ZK kintv(J len, int *val) { K x = ktn(KI, len); From 5e173d63990514057e203204342a04bfdcb6245a Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Mon, 13 Apr 2020 15:12:43 +0900 Subject: [PATCH 04/33] Handle atom value dictionary --- rtest.q | 73 +++++++++++++++++++++++++++++---------------------- src/rserver.c | 43 ++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/rtest.q b/rtest.q index 93b2c00..599e1cb 100644 --- a/rtest.q +++ b/rtest.q @@ -134,46 +134,57 @@ EQUAL[34; .rk.get"data.frame()"; ()]; PROGRESS["Table Test Finished!!"]; +//Dictionary//------------------------------/ + +.rk.set["dictI"; `a`b`c!1 2 3i]; +EQUAL[35; .rk.get"dictI"; `a`b`c!1 2 3i]; +.rk.set["dictJ"; `a`b`c!1 2 3]; +EQUAL[36; .rk.get"dictJ"; `a`b`c!1 2 3]; +.rk.set["dictB"; `a`b`c!101b]; +EQUAL[37; .rk.get"dictB"; `a`b`c!101b]; +.rk.set["dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; +EQUAL[38; .rk.get"dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; + //Time//------------------------------------/ // dates -EQUAL[35; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; -EQUAL[36; .rk.get"as.Date(NA)"; (), 0Nd]; -EQUAL[37; .rk.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; +EQUAL[39; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; +EQUAL[40; .rk.get"as.Date(NA)"; (), 0Nd]; +EQUAL[41; .rk.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; // datetime .rk.exec["Sys.setenv(TZ='UTC')"]; -EQUAL[38; .rk.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[39; .rk.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[40; .rk.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; -EQUAL[41; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[42; .rk.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[43; .rk.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[44; .rk.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[45; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; .rk.set["dttm"; 2018.02.18T04:00:01.000z]; -EQUAL[42; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; +EQUAL[46; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; //timestamp .rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; -EQUAL[43; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; +EQUAL[47; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; PROGRESS["Time Test Finished!!"]; //List//---------------------------------------/ //lang -EQUAL[44; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; -EQUAL[45; .rk.get "as.pairlist(TRUE)"; (enlist 1b; ())]; -EQUAL[46; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; -EQUAL[47; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; +EQUAL[48; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; +EQUAL[49; .rk.get "as.pairlist(TRUE)"; (enlist 1b; ())]; +EQUAL[50; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; +EQUAL[51; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; .rk.get "list(x ~ y + z)" -EQUAL[48; .rk.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; -EQUAL[49; .rk.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; +EQUAL[52; .rk.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; +EQUAL[53; .rk.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; .rk.get "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" .rk.get "Instrument$accessors(c('id', 'description'))" .rk.get "Instrument$new(id='AAPL', description='Apple')" -EQUAL[50; .rk.get "(1+1i)"; "complex"]; -EQUAL[51; .rk.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; -EQUAL[52; .rk.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; -EQUAL[53; .rk.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; -EQUAL[54; .rk.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; +EQUAL[54; .rk.get "(1+1i)"; "complex"]; +EQUAL[55; .rk.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; +EQUAL[56; .rk.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; +EQUAL[57; .rk.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; +EQUAL[58; .rk.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; PROGRESS["List Test Finished!!"]; @@ -182,14 +193,14 @@ PROGRESS["List Test Finished!!"]; // long vectors .rk.exec"x<-c(as.raw(1))" //.rk.exec"x[2147483648L]<-as.raw(1)" -EQUAL[55; count .rk.get`x; 1]; +EQUAL[59; count .rk.get`x; 1]; -EQUAL[56; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; -EQUAL[57; .rk.get["c()"]; .rk.get"NULL"]; -EQUAL[58; (); .rk.get"c()"]; -EQUAL[59; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; +EQUAL[60; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; +EQUAL[61; .rk.get["c()"]; .rk.get"NULL"]; +EQUAL[62; (); .rk.get"c()"]; +EQUAL[63; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; .rk.set[`x;1] -EQUAL[60; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? +EQUAL[64; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? PROGRESS["Q-Like R Command Test Finished!!"]; @@ -208,16 +219,16 @@ PROGRESS["Q-Like R Command Test Finished!!"]; .rk.install`data.table .rk.exec"library(data.table)" .rk.exec"a<-data.frame(a=c(1,2))" -EQUAL[61; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; +EQUAL[65; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; .rk.exec "b<-data.table(a=c(1,2))" -EQUAL[62; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; +EQUAL[66; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; .rk.exec"inspect <- function(x, ...) .Internal(inspect(x,...))" .rk.get`inspect .rk.get"substitute(log(1))" -EQUAL[63; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[64; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[65; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; +EQUAL[67; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[68; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[69; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; PROGRESS["Completed!!"]; // all {.[.rk.set;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2) diff --git a/src/rserver.c b/src/rserver.c index a3fb49e..9efc9ee 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -21,7 +21,7 @@ ZK kinta(J len, int rank, int *shape, int *val); ZK klonga(J len, int rank, int *shape, J*val); ZK kdoublev(J len, double *val); ZK kdoublea(J len, int rank, int *shape, double *val); -ZK from_any_robject(SEXP sxp); +ZK atom_value_dict(J len, K v, SEXP keys); __thread int ROPEN=-1; // initialise thread-local. Will fail in other threads. Ideally need to check if on q main thread. __thread int RLOAD=0; @@ -168,6 +168,15 @@ ZK attR(K x,SEXP sxp) return addattR(x,att); } +ZK atom_value_dict(J len, K v, SEXP keys){ + K k= ktn(KS, len); + for(J i= 0; i < len; i++) { + const char *keyName= CHAR(STRING_ELT(keys, i)); + kS(k)[i]= ss((S) keyName); + } + return xD(k,v); +} + ZK error_broken_robject(SEXP sxp) { return krr("Broken R object."); @@ -325,7 +334,14 @@ ZK from_logical_robject(SEXP sxp) J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { + //Process values x = klogicv(len,LOGICAL(sxp)); + //Dictionary with atom values + SEXP keyNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ + return atom_value_dict(len, x, keyNames); + } + //Normal kdb+ list return attR(x,sxp); } x = klogica(len,length(dim),INTEGER(dim),LOGICAL(sxp)); @@ -345,7 +361,14 @@ ZK from_integer_robject(SEXP sxp){ J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { + //Process values x = kintv(len,INTEGER(sxp)); + //Dictionary with atom values + SEXP keyNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ + return atom_value_dict(len, x, keyNames); + } + //Normal kdb+ list return attR(x,sxp); } x = kinta(len,length(dim),INTEGER(dim),INTEGER(sxp)); @@ -366,15 +389,25 @@ ZK from_double_robject(SEXP sxp){ J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { + //Process values nano = isClass("nanotime",sxp); if(nano || bit64) { x=ktn(nano?KP:KJ,len); DO(len,kJ(x)[i]=INT64(sxp)[i]) if(nano) DO(len,if(kJ(x)[i]!=nj)kJ(x)[i]-=epoch_offset) - return x; - } - x= kdoublev(len, REAL(sxp)); + }else{ + x= kdoublev(len, REAL(sxp)); + } + //Dictionary with atom values + SEXP keyNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ + return atom_value_dict(len, x, keyNames); + }else if(nano || bit64){ + //Class object + return x; + } + //Normal kdb+ list return attR(x, sxp); } if(bit64){ @@ -414,7 +447,7 @@ ZK from_vector_robject(SEXP sxp) J i, length = LENGTH(sxp); K x = ktn(0, length); for (i = 0; i < length; i++) { - xK[i] = from_any_robject(VECTOR_ELT(sxp, i)); + kK(x)[i] = from_any_robject(VECTOR_ELT(sxp, i)); } SEXP colNames= getAttrib(sxp, R_NamesSymbol); if(!isNull(colNames)&&length==XLENGTH(colNames)){ From 3f65981776aebbc0757667d217bacfa7b7eb7791 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Thu, 16 Apr 2020 16:18:58 +0900 Subject: [PATCH 05/33] Handle second, minute and days --- rtest.q | 17 ++++++++++++++++- src/common.c | 13 ++++++++++++- src/rserver.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/rtest.q b/rtest.q index 599e1cb..8445bc4 100644 --- a/rtest.q +++ b/rtest.q @@ -161,10 +161,25 @@ EQUAL[45; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M: .rk.set["dttm"; 2018.02.18T04:00:01.000z]; EQUAL[46; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; -//timestamp +// timestamp .rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; EQUAL[47; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; +// second +.rk.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +EQUAL[48; .rk.get"scnd"; 12:00:30 12:30:45]; +EQUAL[49; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; + +// minute +.rk.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +EQUAL[50; .rk.get "mnt"; 12:00 12:30]; +EQUAL[51; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; + +// days +.rk.set["days"; 1D 2D]; +EQUAL[52; .rk.get"days"; 1D 2D]; +EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; + PROGRESS["Time Test Finished!!"]; //List//---------------------------------------/ diff --git a/src/common.c b/src/common.c index 7a3463c..7a93ed7 100644 --- a/src/common.c +++ b/src/common.c @@ -147,6 +147,7 @@ static SEXP R_UnitsSymbol = NULL; static SEXP R_TzSymbol = NULL; /* for timespan, minute, second */ +//Available units: "secs", "mins", "hours", "days", "weeks" static SEXP setdifftimeclass(SEXP sxp, char* units) { SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(difftimeclass, 0, mkChar("difftime")); @@ -498,8 +499,18 @@ static SEXP from_time_kobject(K object) return from_int_kobject(object); } +//Not general timespan: days only static SEXP from_timespan_kobject(K x) { - return from_long_kobject(x); + SEXP result=from_long_kobject(x); + SEXP realresult; + J i,n=XLENGTH(result); + PROTECT(realresult=allocVector(REALSXP, n)); + PROTECT(result); + for(i= 0; i < n; i++) + REAL(realresult)[i]=(INT64(result)[i]!=nj)?((INT64(result)[i]/1000000000LL)/sec2day):NA_REAL; + setdifftimeclass(realresult,"days"); + UNPROTECT(2); + return realresult; } static SEXP from_timestamp_kobject(K x) diff --git a/src/rserver.c b/src/rserver.c index 9efc9ee..190dee4 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -40,6 +40,9 @@ ZK from_date_robject(SEXP); ZK from_datetime_robject(SEXP); ZK from_datetime_ct_robject(SEXP); ZK from_datetime_lt_robject(SEXP); +ZK from_difftime_robject(SEXP); +ZK from_second_or_minute_robject(SEXP); +ZK from_days_robject(SEXP); ZK from_char_robject(SEXP); ZK from_logical_robject(SEXP); ZK from_integer_robject(SEXP); @@ -63,6 +66,14 @@ Rboolean isClass(const char *class_, SEXP s) { return FALSE; } +Rboolean isUnit(const char *units_, SEXP s){ + SEXP unit; + unit=getAttrib(s, R_UnitsSymbol); + if(!strcmp(CHAR(asChar(unit)), units_)) + return TRUE; + return FALSE; +} + ZK from_any_robject(SEXP sxp){ if(isClass("data.frame", sxp)) { return from_frame_robject(sxp); @@ -76,6 +87,9 @@ ZK from_any_robject(SEXP sxp){ if(isClass("POSIXt", sxp)){ return from_datetime_robject(sxp); } + if(isClass("difftime", sxp)){ + return from_difftime_robject(sxp); + } K result = 0; int type = TYPEOF(sxp); switch (type) { @@ -278,6 +292,39 @@ ZK from_datetime_robject(SEXP sxp){ return from_datetime_lt_robject(sxp); } +ZK from_second_or_minute_robject(SEXP sxp){ + K x; + J length=XLENGTH(sxp); + x=ktn(isUnit("secs", sxp)?KV:KU, length); + int type=TYPEOF(sxp); + switch(type){ + case INTSXP: + DO(length, kI(x)[i]=INTEGER(sxp)[i]); + break; + default: + DO(length,kI(x)[i]=ISNA(REAL(sxp)[i])?NA_INTEGER:(I) REAL(sxp)[i]); + } + return x; +} + +ZK from_days_robject(SEXP sxp){ + K x; + J length= XLENGTH(sxp); + x= ktn(KN,length); + DO(length,kJ(x)[i]=(J) (REAL(sxp)[i]*sec2day)*1000000000LL) + return x; +} + +//Wrapper function of difftime +ZK from_difftime_robject(SEXP sxp){ + if(isUnit("secs", sxp) || isUnit("mins", sxp)) + return from_second_or_minute_robject(sxp); + else if(isUnit("days", sxp)) + return from_days_robject(sxp); + else /* hours */ + return from_nyi_robject(sxp); +} + // NULL in R(R_NilValue): often used as generic zero length vector // NULL objects cannot have attributes and attempting to assign one by attr gives an error ZK from_null_robject(SEXP sxp) From 482d210257444b16bf535ae41b7abfc6283d7e1a Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Thu, 16 Apr 2020 17:43:51 +0900 Subject: [PATCH 06/33] Handle timespan and month --- rtest.q | 52 +++++++++++++++++++++++++++++---------------------- src/common.c | 46 ++++++++++++++++++++++++++++++++++----------- src/rserver.c | 13 ++++++++----- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/rtest.q b/rtest.q index 8445bc4..afc3e59 100644 --- a/rtest.q +++ b/rtest.q @@ -180,26 +180,34 @@ EQUAL[51; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; EQUAL[52; .rk.get"days"; 1D 2D]; EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; +// month +.rk.set["mnth"; `month$/:2020.04.02 2010.01.29] +EQUAL[54; .rk.get"mnth"; 2020.04 2010.01m]; + +// timespan +.rk.set["tmspans"; 0D12 0D04:20:17.123456789] +EQUAL[55; .rk.get"tmspans"; 0D12 0D04:20:17.123456789]; + PROGRESS["Time Test Finished!!"]; //List//---------------------------------------/ //lang -EQUAL[48; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; -EQUAL[49; .rk.get "as.pairlist(TRUE)"; (enlist 1b; ())]; -EQUAL[50; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; -EQUAL[51; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; +EQUAL[56; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; +EQUAL[57; .rk.get "as.pairlist(TRUE)"; (enlist 1b; ())]; +EQUAL[58; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; +EQUAL[59; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; .rk.get "list(x ~ y + z)" -EQUAL[52; .rk.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; -EQUAL[53; .rk.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; +EQUAL[60; .rk.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; +EQUAL[61; .rk.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; .rk.get "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" .rk.get "Instrument$accessors(c('id', 'description'))" .rk.get "Instrument$new(id='AAPL', description='Apple')" -EQUAL[54; .rk.get "(1+1i)"; "complex"]; -EQUAL[55; .rk.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; -EQUAL[56; .rk.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; -EQUAL[57; .rk.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; -EQUAL[58; .rk.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; +EQUAL[62; .rk.get "(1+1i)"; "complex"]; +EQUAL[63; .rk.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; +EQUAL[64; .rk.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; +EQUAL[65; .rk.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; +EQUAL[66; .rk.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; PROGRESS["List Test Finished!!"]; @@ -208,14 +216,14 @@ PROGRESS["List Test Finished!!"]; // long vectors .rk.exec"x<-c(as.raw(1))" //.rk.exec"x[2147483648L]<-as.raw(1)" -EQUAL[59; count .rk.get`x; 1]; +EQUAL[67; count .rk.get`x; 1]; -EQUAL[60; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; -EQUAL[61; .rk.get["c()"]; .rk.get"NULL"]; -EQUAL[62; (); .rk.get"c()"]; -EQUAL[63; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; +EQUAL[68; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; +EQUAL[69; .rk.get["c()"]; .rk.get"NULL"]; +EQUAL[70; (); .rk.get"c()"]; +EQUAL[71; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; .rk.set[`x;1] -EQUAL[64; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? +EQUAL[72; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? PROGRESS["Q-Like R Command Test Finished!!"]; @@ -234,16 +242,16 @@ PROGRESS["Q-Like R Command Test Finished!!"]; .rk.install`data.table .rk.exec"library(data.table)" .rk.exec"a<-data.frame(a=c(1,2))" -EQUAL[65; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; +EQUAL[73; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; .rk.exec "b<-data.table(a=c(1,2))" -EQUAL[66; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; +EQUAL[74; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; .rk.exec"inspect <- function(x, ...) .Internal(inspect(x,...))" .rk.get`inspect .rk.get"substitute(log(1))" -EQUAL[67; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[68; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[69; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; +EQUAL[75; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[76; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[77; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; PROGRESS["Completed!!"]; // all {.[.rk.set;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2) diff --git a/src/common.c b/src/common.c index 7a93ed7..0586022 100644 --- a/src/common.c +++ b/src/common.c @@ -169,7 +169,7 @@ static SEXP settimezone(SEXP sxp, char* tzone) { UNPROTECT(1); return sxp; } -/* for date,month */ +/* for date */ static SEXP setdateclass(SEXP sxp) { SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(difftimeclass, 0, mkChar("Date")); @@ -177,6 +177,15 @@ static SEXP setdateclass(SEXP sxp) { UNPROTECT(1); return sxp; } +/* month */ +static SEXP setmonthclass(SEXP sxp){ + SEXP difftimeclass= PROTECT(allocVector(STRSXP, 2)); + SET_STRING_ELT(difftimeclass, 0, mkChar("Date")); + SET_STRING_ELT(difftimeclass, 1, mkChar("month")); + classgets(sxp, difftimeclass); + UNPROTECT(1); + return sxp; +} /* * We have functions that turn any K object into the appropriate R SEXP. @@ -456,9 +465,14 @@ static SEXP from_symbol_kobject(K x) { return result; } -static SEXP from_month_kobject(K object) +static SEXP from_month_kobject(K x) { - return from_int_kobject(object); + SEXP result=PROTECT(from_int_kobject(x)); + for(J i= 0; i < XLENGTH(result); i++) + if(INTEGER(result)[i]!=NA_INTEGER) INTEGER(result)[i]+=kdbDateOffset; + setmonthclass(result); + UNPROTECT(1); + return result; } static SEXP from_date_kobject(K x) { @@ -499,18 +513,28 @@ static SEXP from_time_kobject(K object) return from_int_kobject(object); } -//Not general timespan: days only static SEXP from_timespan_kobject(K x) { SEXP result=from_long_kobject(x); - SEXP realresult; J i,n=XLENGTH(result); - PROTECT(realresult=allocVector(REALSXP, n)); PROTECT(result); - for(i= 0; i < n; i++) - REAL(realresult)[i]=(INT64(result)[i]!=nj)?((INT64(result)[i]/1000000000LL)/sec2day):NA_REAL; - setdifftimeclass(realresult,"days"); - UNPROTECT(2); - return realresult; + + //judge timespan or days + if(((INT64(result)[0]/1000000000LL) % sec2day)==0){ + //difftime days + SEXP realresult; + PROTECT(realresult=allocVector(REALSXP, n)); + for(i= 0; i < n; i++) + REAL(realresult)[i]=(INT64(result)[i]!=nj)?((INT64(result)[i]/1000000000LL)/sec2day):NA_REAL; + setdifftimeclass(realresult,"days"); + UNPROTECT(2); + return realresult; + } + else{ + //timespan + setdifftimeclass(result,"timespan"); + UNPROTECT(1); + return result; + } } static SEXP from_timestamp_kobject(K x) diff --git a/src/rserver.c b/src/rserver.c index 190dee4..cb04c61 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -239,7 +239,7 @@ ZK from_raw_robject(SEXP sxp) ZK from_date_robject(SEXP sxp) { K x; J length= XLENGTH(sxp); - x= ktn(KD,length); + x= ktn(isClass("month", sxp)?KM:KD,length); int type= TYPEOF(sxp); switch(type) { case INTSXP: @@ -321,6 +321,8 @@ ZK from_difftime_robject(SEXP sxp){ return from_second_or_minute_robject(sxp); else if(isUnit("days", sxp)) return from_days_robject(sxp); + else if(isUnit("timespan", sxp)) + return from_double_robject(sxp); else /* hours */ return from_nyi_robject(sxp); } @@ -432,14 +434,15 @@ ZK from_integer_robject(SEXP sxp){ ZK from_double_robject(SEXP sxp){ K x; - I nano, bit64=isClass("integer64",sxp); + I nano, span, bit64=isClass("integer64",sxp); J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); if (isNull(dim)) { //Process values nano = isClass("nanotime",sxp); - if(nano || bit64) { - x=ktn(nano?KP:KJ,len); + span = isClass("difftime", sxp) && isUnit("timespan", sxp); + if(nano || span || bit64) { + x=ktn(nano?KP:(span?KN:KJ),len); DO(len,kJ(x)[i]=INT64(sxp)[i]) if(nano) DO(len,if(kJ(x)[i]!=nj)kJ(x)[i]-=epoch_offset) @@ -450,7 +453,7 @@ ZK from_double_robject(SEXP sxp){ SEXP keyNames= getAttrib(sxp, R_NamesSymbol); if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ return atom_value_dict(len, x, keyNames); - }else if(nano || bit64){ + }else if(nano || span || bit64){ //Class object return x; } From c587b34211f8f32ac190609d0e8a984ff3d617f5 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Thu, 16 Apr 2020 10:22:37 +0100 Subject: [PATCH 07/33] Code modifications to bring in line with other interfaces, update to examples and addition of aliases --- .travis.yml | 4 +- Makefile | 44 +- README.md | 61 +-- appveyor.yml | 2 +- examples/e4.q | 41 +- examples/pcd.q | 30 +- init.q | 35 ++ rinit.q | 19 - src/common.c | 411 ++++++++--------- embedr.c => src/embedr.c | 8 +- src/qserver.c | 113 +++-- src/rserver.c | 860 +++++++++++++++++------------------ rtest.q => tests/test.q | 2 +- w32.sh => win_install/w32.sh | 3 +- w64.sh => win_install/w64.sh | 3 +- 15 files changed, 788 insertions(+), 848 deletions(-) create mode 100644 init.q delete mode 100644 rinit.q rename embedr.c => src/embedr.c (88%) rename rtest.q => tests/test.q (99%) rename w32.sh => win_install/w32.sh (54%) mode change 100644 => 100755 rename w64.sh => win_install/w64.sh (54%) mode change 100644 => 100755 diff --git a/.travis.yml b/.travis.yml index 8815f7d..1c12d91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ before_install: script: - make - echo "Preparing version $TRAVIS_BRANCH-$TRAVIS_COMMIT" -- mkdir embedr; cp -r rinit.q rtest.q l32 l64 m32 m64 embedr/ || true; +- mkdir embedr; cp -r init.q test.q l32 l64 m32 m64 embedr/ || true; - echo "$TRAVIS_BRANCH-$TRAVIS_COMMIT" > embedr/VERSION.embedr; - cp LICENSE embedr/LICENSE.embedr; cp README.md embedr/README.embedr; - tar czvf embedr_$TRAVIS_OS_NAME-$TRAVIS_BRANCH.tar.gz embedr @@ -21,4 +21,4 @@ deploy: file: embedr_*.tar.gz skip_cleanup: true on: - tags: true \ No newline at end of file + tags: true diff --git a/Makefile b/Makefile index 0fad294..f103522 100644 --- a/Makefile +++ b/Makefile @@ -1,40 +1,32 @@ -# rserver makefile - +MS = $(shell getconf LONG_BIT) # 32/64 +CFLAGS = -g -O0 -fPIC -m$(MS) UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) - OSFLAG = l + OSFLAG = l + CFLAGS += -shared +else ifeq ($(UNAME_S),Darwin) + OSFLAG = m + CFLAGS += -dynamiclib -undefined dynamic_lookup endif -ifeq ($(UNAME_S),Darwin) - OSFLAG = m -endif -MS=$(shell getconf LONG_BIT) # 32/64 -QARCH=$(OSFLAG)$(MS) -Q=${QHOME}/$(QARCH) -CFLAGS=-g -O0 -fPIC -m$(MS) -ifeq ($(OSFLAG),m) - CFLAGS+=-dynamiclib -undefined dynamic_lookup -endif -ifeq ($(OSFLAG),l) - CFLAGS+=-shared -endif +QARCH = $(OSFLAG)$(MS) +Q = ${QHOME}/$(QARCH) -R_HOME=$(shell R RHOME) -R_INCLUDES=$(shell R CMD config --cppflags) -LIBS=-lpthread -L$(R_HOME)/lib -lR +R_HOME = $(shell R RHOME) +R_INCLUDES = $(shell R CMD config --cppflags) +LIBS = -lpthread -L$(R_HOME)/lib -lR -SRC=embedr.c -TGT=$(addsuffix /embedr.so,$(QARCH)) +SRC = src/embedr.c +TGT = embedr.so -all: - mkdir -p $(QARCH) +all: src/k.h R CMD gcc -o $(TGT) $(CFLAGS) $(R_INCLUDES) $(SRC) $(LIBS) -Wall - install: install $(TGT) $(Q) - clean: rm -rf $(TGT) - fmt: clang-format -style=file embedr.c src/rserver.c src/qserver.c src/common.c -i +src/k.h: + curl -s -O -L src/https://github.com/KxSystems/kdb/raw/master/c/c/k.h diff --git a/README.md b/README.md index a4897f9..f817e47 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,13 @@ # embedR: Embedding R inside q - - See - ## Installation ### Download Download the appropriate release archive from [releases](../../releases/latest) page. -### Unpack and install content of the archive +#### Unpack and install content of the archive environment | action ----------------|--------------------------------------------------------------------------------------- @@ -18,10 +15,26 @@ Linux | `tar xzvf embedr_linux-v*.tar.gz -C $QHOME --strip 1` macOS | `tar xzvf embedr_osx-v*.tar.gz -C $QHOME --strip 1` Windows | Open the archive and copy content of the `embedr` folder (`embedr\*`) to `%QHOME%` or `c:\q`
Copy R_HOME/x64/*.dll or R_HOME/i386/*.dll to QHOME/w64 or QHOME/w32 respectively. +### Building +For Linux/MacOS it is possible to build the library from source using the makefile provided. From the root of the directory: + +```bash +// Create the `.so` binary +$ make + +// Install into $QHOME +$ make install + +// Remove the binary from current directory +$ make clean + +// Or in a single command +$ make && make install && make clean +``` ## Calling R -When calling R, you need to set `R_HOME`. Required are: +When calling R, you need to set `R_HOME`. This can be set as follows: ```bash # Linux/macOS @@ -32,38 +45,26 @@ for /f "delims=" %a in ('R RHOME') do @set R_HOME=%a The library has four main methods: -- `Ropen`: Initialise embedded R. Optional to call. Allows to set verbose mode as `Ropen 1`. -- `Rcmd`: run an R command, do not return a result -- `Rget`: run an R command, return the result to q -- `Rset`: set a variable in the R memory space +- `.rk.open`: Initialise embedR. Optional to call. Allows to set verbose mode as `Ropen 1`. +- `.rk.exec`: execute an R command, do not return a result to q. +- `.rk.get`: execute an R command, return the result to q. +- `.rk.set`: set a variable in the R memory space -### Interactive plotting - -If using interactive plotting with `lattice` and/or `ggplot2` you will need to call `print` on a chart object. +## Documentation +Documentation for this interface is available [here](https://code.kx.com/q/interfaces/r/embedr) ## Examples -See [examples](examples) folder. - -Note: Examples are kdb+ 3.5 or higher. - - -### Example 1 - -`e4.q` is a simple example of plot 'moving window volatility' of returns. Converted from http://www.mayin.org/ajayshah/KB/R/html/p4.html - - -### Example 2 - -`pcd.q` is based on [Corporate credit card transactions 2014-15](https://data.gov.uk/dataset/corporate-credit-card-transaction-2014-15). - -Download CSV from the link above and save it in the same folder as `pcd.q` under name `pcd2014v1.csv`. +A number of example scripts are provided in the [examples](examples) folder. +**Note:** Examples are kdb+ 3.5 or higher. -### Example 3 +1. Plotting the 'moving window volatility' of returns +2. Analysis of corporate credit card transactions in the UK dataset available [here](https://ckan.publishing.service.gov.uk/dataset/corporate-credit-card-transactions-2014-152) - +## Notes -Left for the reader :) +1. If using interactive plotting with `lattice` and/or `ggplot2` you will need to call `print` on a chart object. +2. As of v2.0 the callable functions have migrated to the `.rk` namespace in line with the other fusion interfaces. The historic interface using `Rcmd`,`Ropen` etc will be deprecated in v2.1 diff --git a/appveyor.yml b/appveyor.yml index c9bce7a..01853e1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,7 +21,7 @@ environment: build_script: - cmd: mkdir w%BITS% -- cmd: R.exe CMD SHLIB -o w%BITS%/embedr.dll embedr.c src/w%BITS%/q.a -lws2_32 +- cmd: R.exe CMD SHLIB -o w%BITS%/embedr.dll src/embedr.c src/w%BITS%/q.a -lws2_32 after_build: - cmd: mkdir embedr diff --git a/examples/e4.q b/examples/e4.q index 9e24f51..c7b86fa 100644 --- a/examples/e4.q +++ b/examples/e4.q @@ -1,34 +1,37 @@ // Goal: To do `moving window volatility' of returns. // http://www.mayin.org/ajayshah/KB/R/html/p4.html -//\l rtest.q // check if lib installed -if[0i=first Rget"is.element(\"zoo\",installed.packages()[,1])";Rcmd"install.packages(\"zoo\",repos=\"https://cloud.r-project.org\")"] -Rcmd"library(zoo)"; + +\l ../init.q + +if[0i=first .rk.get"is.element(\"zoo\",installed.packages()[,1])"; + .rk.exec"install.packages(\"zoo\",repos=\"https://cloud.r-project.org\")"] +.rk.exec"library(zoo)"; // Some data to play with (Nifty on all fridays for calendar 2004) -- pr:1946.05 1971.9 1900.65 1847.55 1809.75 1833.65 1913.6 1852.65 1800.3 1867.7 1812.2 1725.1 1747.5 1841.1 1853.55 1868.95 1892.45 1796.1 1804.45 1582.4 1560.2 1508.75 1521.1 1508.45 1491.2 1488.5 1537.5 1553.2 1558.8 1601.6 1632.3 1633.4 1607.2 1590.35 1609 1634.1 1668.75 1733.65 1722.5 1775.15 1820.2 1795 1779.75 1786.9 1852.3 1872.95 1872.35 1901.05 1996.2 1969 2012.1 2062.7 2080.5 d: 2004.01.02 2004.01.09 2004.01.16 2004.01.23 2004.01.30 2004.02.06 2004.02.13 2004.02.20 2004.02.27 2004.03.05 2004.03.12 2004.03.19 2004.03.26 2004.04.02 2004.04.09 2004.04.16 2004.04.23 2004.04.30 2004.05.07 2004.05.14 2004.05.21 2004.05.28 2004.06.04 2004.06.11 2004.06.18 2004.06.25 2004.07.02 2004.07.09 2004.07.16 2004.07.23 2004.07.30 2004.08.06 2004.08.13 2004.08.20 2004.08.27 2004.09.03 2004.09.10 2004.09.17 2004.09.24 2004.10.01 2004.10.08 2004.10.15 2004.10.22 2004.10.29 2004.11.05 2004.11.12 2004.11.19 2004.11.26 2004.12.03 2004.12.10 2004.12.17 2004.12.24 2004.12.31 -Rset[`d;d] -Rset[`pr;pr] -Rcmd"p <- structure(pr, index = d, frequency = 0.142857142857143, class = c(\"zooreg", "zoo\"))"; +.rk.set[`d;d] +.rk.set[`pr;pr] +.rk.exec"p <- structure(pr, index = d, frequency = 0.142857142857143, class = c(\"zooreg", "zoo\"))"; // Shift to returns -- -Rcmd"r <- 100*diff(log(p))"; -Rget"head(r)" -Rget"summary(r)" -Rget"sd(r)" +.rk.exec"r <- 100*diff(log(p))"; +.rk.get"head(r)" +.rk.get"summary(r)" +.rk.get"sd(r)" // Compute the moving window vol -- -Rcmd"vol <- sqrt(250) * rollapply(r, 20, sd, align = \"right\")"; +.rk.exec"vol <- sqrt(250) * rollapply(r, 20, sd, align = \"right\")"; // A pretty plot -- -Rcmd"png('pretty_plot_test.png')"; -Rcmd"plot(vol, type=\"l\", ylim=c(0,max(vol,na.rm=TRUE)),lwd=2, col=\"purple\", xlab=\"2004\",ylab=paste(\"Annualised sigma, 20-week window\"))"; -Rcmd"grid()"; -Rget"legend(x=\"bottomleft\", col=c(\"purple\", \"darkgreen\"),lwd=c(2,2), bty=\"n\", cex=0.8,legend=c(\"Annualised 20-week vol (left scale)\", \"Nifty (right scale)\"))" -Rcmd"par(new=TRUE)"; -Rcmd"plot(p, type=\"l\", lwd=2, col=\"darkgreen\", xaxt=\"n\", yaxt=\"n\", xlab=\"\", ylab=\"\")"; -Rcmd"axis(4)"; -Roff[] +.rk.exec"png('pretty_plot_test.png')"; +.rk.exec"plot(vol, type=\"l\", ylim=c(0,max(vol,na.rm=TRUE)),lwd=2, col=\"purple\", xlab=\"2004\",ylab=paste(\"Annualised sigma, 20-week window\"))"; +.rk.exec"grid()"; +.rk.get"legend(x=\"bottomleft\", col=c(\"purple\", \"darkgreen\"),lwd=c(2,2), bty=\"n\", cex=0.8,legend=c(\"Annualised 20-week vol (left scale)\", \"Nifty (right scale)\"))" +.rk.exec"par(new=TRUE)"; +.rk.exec"plot(p, type=\"l\", lwd=2, col=\"darkgreen\", xaxt=\"n\", yaxt=\"n\", xlab=\"\", ylab=\"\")"; +.rk.exec"axis(4)"; +.rk.off[] hcount `:pretty_plot_test.png //hdel `:pretty_plot_test.png diff --git a/examples/pcd.q b/examples/pcd.q index be0fcda..6546e84 100644 --- a/examples/pcd.q +++ b/examples/pcd.q @@ -1,4 +1,4 @@ -//\l rinit.q +\l ../init.q basedir:`:.^hsym `$last -2 _ get{} datafile:` sv first[` vs basedir],`pcd2014v1.csv @@ -9,20 +9,20 @@ system"z 1" cct:("***D*DE";enlist csv) 0:datafile cct:(`$"_"^string cols cct) xcol cct -Rcmd"library(lattice)" -Rnew[] -Rset["tpd";select txn:count i by Transaction_Date from cct] -Rcmd"plot1<-xyplot(txn~Transaction_Date,data=tpd,main='Payments per day')" -Rcmd"print(plot1)" +.rk.exec"library(lattice)" +.rk.new[] +.rk.set["tpd";select txn:count i by Transaction_Date from cct] +.rk.exec"plot1<-xyplot(txn~Transaction_Date,data=tpd,main='Payments per day')" +.rk.exec"print(plot1)" -Rset[`tpt;select Transaction_Date,spent:sums JV_Value from select sum JV_Value by Transaction_Date from cct] -Rnew[] -Rcmd"plot2<-xyplot(spent~Transaction_Date,data=tpt,main='Total spent')" -Rcmd"print(plot2)" +.rk.set[`tpt;select Transaction_Date,spent:sums JV_Value from select sum JV_Value by Transaction_Date from cct] +.rk.new[] +.rk.exec"plot2<-xyplot(spent~Transaction_Date,data=tpt,main='Total spent')" +.rk.exec"print(plot2)" -Rinstall`plotly -Rcmd"library(plotly)" -Rset[`catd;select sum JV_Value by Service_Area from cct] -Rcmd"plot3<-plot_ly(catd, labels = ~Service_Area, values = ~JV_Value, type = 'pie')" -Rcmd"print(plot3)" \ No newline at end of file +.rk.install`plotly +.rk.exec"library(plotly)" +.rk.set[`catd;select sum JV_Value by Service_Area from cct] +.rk.exec"plot3<-plot_ly(catd, labels = ~Service_Area, values = ~JV_Value, type = 'pie')" +.rk.exec"print(plot3)" diff --git a/init.q b/init.q new file mode 100644 index 0000000..a4c063f --- /dev/null +++ b/init.q @@ -0,0 +1,35 @@ +\d .rk + +LIBPATH:`:embedr 2: + +funcs:( + (`rclose;1); + (`ropen;1); + (`rexec;1); + (`rget;1); + (`rset;2) + ) +.rk,:(`$1_'string funcs[;0])!LIBPATH@/:funcs + +install:{[pkg] + pkg:$[-11=type pkg;string pkg;pkg];rcloud:"https://cloud.r-project.org"; + if[0i=first .rk.get"is.element('",pkg,"',installed.packages()[,1])"; + .rk.exec"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; + } +off:{.rk.exec "dev.off()"} +new:{.rk.exec "dev.new(noRStudioGD=TRUE)"} + +\d . + +// The following block of functions are aliases for the above +// these aliases will be removed in a future version of the code + +Rclose :.rk.close +Rcmd :.rk.exec +Rget :.rk.get +Rinstall:.rk.install +Rnew :.rk.new +Roff :.rk.off +Rset :.rk.set + +setenv[`R_HOME;first @[system;@[.z.o like "w*";"call";"env"]," R RHOME";enlist""]] diff --git a/rinit.q b/rinit.q deleted file mode 100644 index 64d280d..0000000 --- a/rinit.q +++ /dev/null @@ -1,19 +0,0 @@ -/ R server for Q - -.rk.close:`embedr 2:(`rclose;1) -.rk.open:`embedr 2:(`ropen;1) -.rk.exec:`embedr 2:(`rcmd;1) -.rk.get:`embedr 2:(`rget;1) -.rk.set:`embedr 2:(`rset;2) -//Rcmd:Rcmd0; -//Rget:Rget0; -//Rset:Rset0; - -.rk.install:{[pkg] - pkg:$[-11=type pkg;string pkg;pkg];rcloud:"https://cloud.r-project.org"; - if[0i=first .rk.get"is.element('",pkg,"',installed.packages()[,1])"; - .rk.exec"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; - } -Roff:{.rk.exec "dev.off()"} -Rnew:{.rk.exec "dev.new(noRStudioGD=TRUE)"} -setenv[`R_HOME;first @[system;@[.z.o like "w*";"call";"env"]," R RHOME";enlist""]] diff --git a/src/common.c b/src/common.c index 7a3463c..e9eef27 100644 --- a/src/common.c +++ b/src/common.c @@ -8,121 +8,115 @@ int kx_connection=0; * A (readable type name, R data type number) pair. */ struct data_types { - char *name; - Sint id; + char *name; + Sint id; }; /* * A mapping from readable names to R data type numbers. */ const struct data_types r_data_types[] = { - {"unknown", -1}, - {"NULL", NILSXP}, - {"symbol", SYMSXP}, - {"pairlist", LISTSXP}, - {"closure", CLOSXP}, - {"environment", ENVSXP}, - {"promise", PROMSXP}, - {"language", LANGSXP}, - {"special", SPECIALSXP}, - {"builtin", BUILTINSXP}, - {"char", CHARSXP}, - {"logical", LGLSXP}, - {"integer", INTSXP}, - {"double", REALSXP}, - {"complex", CPLXSXP}, - {"character", STRSXP}, - {"...", DOTSXP}, - {"any", ANYSXP}, - {"expression", EXPRSXP}, - {"list", VECSXP}, - {"numeric", REALSXP}, - {"name", SYMSXP}, - {0, -1} + {"unknown", -1}, + {"NULL", NILSXP}, + {"symbol", SYMSXP}, + {"pairlist", LISTSXP}, + {"closure", CLOSXP}, + {"environment", ENVSXP}, + {"promise", PROMSXP}, + {"language", LANGSXP}, + {"special", SPECIALSXP}, + {"builtin", BUILTINSXP}, + {"char", CHARSXP}, + {"logical", LGLSXP}, + {"integer", INTSXP}, + {"double", REALSXP}, + {"complex", CPLXSXP}, + {"character", STRSXP}, + {"...", DOTSXP}, + {"any", ANYSXP}, + {"expression", EXPRSXP}, + {"list", VECSXP}, + {"numeric", REALSXP}, + {"name", SYMSXP}, + {0, -1} }; /* * Brute force search of R type table. * eg. get_type_name(LISTSXP) */ -char* get_type_name(Sint type) -{ - int i; - for (i = 1; r_data_types[i].name != 0; i++) { - if (type == r_data_types[i].id) - return r_data_types[i].name; - } - return r_data_types[0].name; +char* get_type_name(Sint type) { + for (int i = 1; r_data_types[i].name != 0; i++){ + if(type == r_data_types[i].id) + return r_data_types[i].name; + } + return r_data_types[0].name; } /* * Given the appropriate names, types, and lengths, create an R named list. */ -SEXP make_named_list(char **names, SEXPTYPE *types, Sint *lengths, Sint n) -{ - SEXP output, output_names, object = NULL_USER_OBJECT; - Sint elements; int i; - - PROTECT(output = NEW_LIST(n)); - PROTECT(output_names = NEW_CHARACTER(n)); - - for(i = 0; i < n; i++){ - elements = lengths[i]; - switch((int)types[i]) { - case LGLSXP: - PROTECT(object = NEW_LOGICAL(elements)); - break; - case INTSXP: - PROTECT(object = NEW_INTEGER(elements)); - break; - case REALSXP: - PROTECT(object = NEW_NUMERIC(elements)); - break; - case STRSXP: - PROTECT(object = NEW_CHARACTER(elements)); - break; - case VECSXP: - PROTECT(object = NEW_LIST(elements)); - break; - default: - error("Unsupported data type at %d %s\n", __LINE__, __FILE__); - } - SET_VECTOR_ELT(output, (Sint)i, object); - SET_STRING_ELT(output_names, i, COPY_TO_USER_STRING(names[i])); - } - SET_NAMES(output, output_names); - UNPROTECT(n+2); - return output; +SEXP make_named_list(char **names, SEXPTYPE *types, Sint *lengths, Sint n) { + SEXP output, output_names, object = NULL_USER_OBJECT; + Sint elements; + PROTECT(output = NEW_LIST(n)); + PROTECT(output_names = NEW_CHARACTER(n)); + for(int i = 0; i < n; i++){ + elements = lengths[i]; + switch((int)types[i]) { + case LGLSXP: + PROTECT(object = NEW_LOGICAL(elements)); + break; + case INTSXP: + PROTECT(object = NEW_INTEGER(elements)); + break; + case REALSXP: + PROTECT(object = NEW_NUMERIC(elements)); + break; + case STRSXP: + PROTECT(object = NEW_CHARACTER(elements)); + break; + case VECSXP: + PROTECT(object = NEW_LIST(elements)); + break; + default: + error("Unsupported data type at %d %s\n", __LINE__, __FILE__); + } + SET_VECTOR_ELT(output, (Sint)i, object); + SET_STRING_ELT(output_names, i, COPY_TO_USER_STRING(names[i])); + } + SET_NAMES(output, output_names); + UNPROTECT(n+2); + return output; } /* * Make a data.frame from a named list by adding row.names, and class * attribute. Uses "1", "2", .. as row.names. */ -void make_data_frame(SEXP data) -{ - SEXP class_name, row_names; Sint n; - PROTECT(data); - PROTECT(class_name = NEW_CHARACTER((Sint) 1)); - SET_STRING_ELT(class_name, 0, COPY_TO_USER_STRING("data.frame")); - - /* Set the row.names. */ - n = GET_LENGTH(VECTOR_ELT(data,0)); - PROTECT(row_names=NEW_INTEGER(2)); INTEGER(row_names)[0]=NA_INTEGER; INTEGER(row_names)[1]=-n; +void make_data_frame(SEXP data) { + SEXP class_name, row_names; Sint n; + PROTECT(data); + PROTECT(class_name = NEW_CHARACTER((Sint) 1)); + SET_STRING_ELT(class_name, 0, COPY_TO_USER_STRING("data.frame")); + /* Set the row.names. */ + n = GET_LENGTH(VECTOR_ELT(data,0)); + PROTECT(row_names=NEW_INTEGER(2)); + INTEGER(row_names)[0]=NA_INTEGER; + INTEGER(row_names)[1]=-n; setAttrib(data, R_RowNamesSymbol, row_names); SET_CLASS(data, class_name); UNPROTECT(3); } /* for datetime */ -static SEXP setdatetimeclass(SEXP sxp) -{ - SEXP datetimeclass = PROTECT(allocVector(STRSXP,2)); - SET_STRING_ELT(datetimeclass, 0, mkChar("POSIXt")); - SET_STRING_ELT(datetimeclass, 1, mkChar("POSIXct")); - classgets(sxp, datetimeclass); - UNPROTECT(1); - return sxp; +static SEXP setdatetimeclass(SEXP sxp) { + SEXP datetimeclass = PROTECT(allocVector(STRSXP,2)); + SET_STRING_ELT(datetimeclass, 0, mkChar("POSIXt")); + SET_STRING_ELT(datetimeclass, 1, mkChar("POSIXct")); + classgets(sxp, datetimeclass); + UNPROTECT(1); + return sxp; } /* for timestamp */ @@ -132,8 +126,6 @@ static SEXP settimestampclass(SEXP sxp) { SEXP val = PROTECT(mkString("integer64")); setAttrib(sxp, tag, val); UNPROTECT(2); - - classValue= PROTECT(mkString("nanotime")); tag = PROTECT(mkString("package")); val = PROTECT(mkString("nanotime")); @@ -151,7 +143,8 @@ static SEXP setdifftimeclass(SEXP sxp, char* units) { SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(difftimeclass, 0, mkChar("difftime")); classgets(sxp, difftimeclass); - if (R_UnitsSymbol == NULL) R_UnitsSymbol = install("units"); + if (R_UnitsSymbol == NULL) + R_UnitsSymbol = install("units"); SEXP difftimeunits= PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(difftimeunits, 0, mkChar(units)); setAttrib(sxp, R_UnitsSymbol, difftimeunits); @@ -163,11 +156,13 @@ static SEXP setdifftimeclass(SEXP sxp, char* units) { static SEXP settimezone(SEXP sxp, char* tzone) { SEXP timezone= PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(timezone, 0, mkChar(tzone)); - if (R_TzSymbol == NULL) R_TzSymbol = install("tzone"); + if (R_TzSymbol == NULL) + R_TzSymbol = install("tzone"); setAttrib(sxp, R_TzSymbol, timezone); UNPROTECT(1); return sxp; } + /* for date,month */ static SEXP setdateclass(SEXP sxp) { SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); @@ -213,73 +208,61 @@ static SEXP from_table_kobject(K); typedef SEXP(*conversion_function)(K); conversion_function kdbplus_types[] = { - from_list_of_kobjects, - from_bool_kobject, - from_guid_kobject, - error_broken_kobject, - from_byte_kobject, - from_short_kobject, - from_int_kobject, - from_long_kobject, - from_float_kobject, - from_double_kobject, - from_string_kobject, - from_symbol_kobject, - from_timestamp_kobject, - from_month_kobject, - from_date_kobject, - from_datetime_kobject, - from_timespan_kobject, - from_minute_kobject, - from_second_kobject, - from_time_kobject + from_list_of_kobjects, from_bool_kobject, from_guid_kobject, + error_broken_kobject, from_byte_kobject, from_short_kobject, + from_int_kobject, from_long_kobject, from_float_kobject, + from_double_kobject, from_string_kobject, from_symbol_kobject, + from_timestamp_kobject, from_month_kobject, from_date_kobject, + from_datetime_kobject, from_timespan_kobject, from_minute_kobject, + from_second_kobject, from_time_kobject }; /* * Convert K object to R object */ -static SEXP from_any_kobject(K x) -{ - SEXP result; - int type = abs(x->t); - if (XT == type) - result = from_table_kobject(x); - else if (XD == type) - result = from_dictionary_kobject(x); - else if (105 == type || 101 == type) - result = from_int_kobject(ki(0)); - else if (type <= KT) - result = kdbplus_types[type](x); - else if (KTt!=-128) { - result = from_any_kobject(t); - r0(t); - }else - result = error_broken_kobject(x); - } else if(77t!=-128) { - result = from_any_kobject(t); - r0(t); - }else - result = error_broken_kobject(x); - } - else - result = error_broken_kobject(x); - return result; +static SEXP from_any_kobject(K x) { + SEXP result; + int type = abs(x->t); + if (XT == type) + result = from_table_kobject(x); + else if (XD == type) + result = from_dictionary_kobject(x); + else if (105 == type || 101 == type) + result = from_int_kobject(ki(0)); + else if (type <= KT) + result = kdbplus_types[type](x); + else if (KTt!=-128) { + result = from_any_kobject(t); + r0(t); + } + else + result = error_broken_kobject(x); + } + else if(77t!=-128) { + result = from_any_kobject(t); + r0(t); + } + else + result = error_broken_kobject(x); + } + else + result = error_broken_kobject(x); + return result; } /* * Convert K columns to R object */ -static SEXP from_columns_kobject(K x) -{ +static SEXP from_columns_kobject(K x) { SEXP col, result; - int i, type, length = x->n; + int type, length = x->n; K c; PROTECT(result = NEW_LIST(length)); - for (i = 0; i < length; i++) { + for (int i = 0; i < length; i++) { c = xK[i]; type = abs(c->t); if (type == KC) @@ -295,30 +278,27 @@ static SEXP from_columns_kobject(K x) /* * Complain that the given K object is not valid and return "unknown". */ -static SEXP error_broken_kobject(K broken) -{ - error("Value is not a valid kdb+ object; unknown type %d\n", broken->t); - return mkChar(r_data_types[0].name); +static SEXP error_broken_kobject(K broken) { + error("Value is not a valid kdb+ object; unknown type %d\n", broken->t); + return mkChar(r_data_types[0].name); } /* * An R list from a K list object. */ -static SEXP from_list_of_kobjects(K x) -{ +static SEXP from_list_of_kobjects(K x) { SEXP result; K y; - J i, length= x->n, utype; + long length= x->n, utype; PROTECT(result= allocVector(VECSXP,length)); utype= length > 0 ? kK(x)[0]->t : 0; - for(i= 0; i < length; i++) { + for(int i= 0; i < length; i++) { y= kK(x)[i]; utype= utype == y->t ? utype : 0; SET_VECTOR_ELT(result, i, from_any_kobject(y)); } - if(utype == KC) { + if(utype == KC) result= coerceVector(result, STRSXP); - } UNPROTECT(1); return result; } @@ -341,7 +321,7 @@ static SEXP from_bool_kobject(K x) { SEXP result; if(scalar(x)) return ScalarLogical(x->g); PROTECT(result= allocVector(LGLSXP,x->n)); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) LOGICAL(result)[i]= kG(x)[i]; UNPROTECT(1); return result; @@ -352,24 +332,23 @@ static SEXP from_byte_kobject(K x) { if(scalar(x)) return ScalarRaw(x->g); PROTECT(result= allocVector(RAWSXP,x->n)); r=RAW(result); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) r[i]= kG(x)[i]; UNPROTECT(1); return result; } -static SEXP from_guid_kobject(K x) -{ - K y = k(kx_connection,"string",r1(x),(K)0); - SEXP r = from_any_kobject(y);r0(y); - return r; +static SEXP from_guid_kobject(K x) { + K y = k(kx_connection,"string",r1(x),(K)0); + SEXP r = from_any_kobject(y);r0(y); + return r; } static SEXP from_short_kobject(K x) { SEXP result; if(scalar(x)) return ScalarInteger(x->h==nh?NA_INTEGER:(int)x->h); PROTECT(result= allocVector(INTSXP,x->n)); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) INTEGER(result)[i]= kH(x)[i]==nh?NA_INTEGER:kH(x)[i]; UNPROTECT(1); return result; @@ -379,7 +358,7 @@ static SEXP from_int_kobject(K x) { SEXP result; if(scalar(x)) return ScalarInteger(x->i); PROTECT(result= allocVector(INTSXP,x->n)); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) INTEGER(result)[i]= kI(x)[i]; UNPROTECT(1); return result; @@ -387,12 +366,12 @@ static SEXP from_int_kobject(K x) { static SEXP from_long_kobject(K x) { SEXP result; - J i, n=scalar(x)?1:x->n; + long n=scalar(x)?1:x->n; PROTECT(result= allocVector(REALSXP,n)); - if(scalar(x)) { + if(scalar(x)) INT64(result)[0]= x->j; - } else { - for(i= 0; i < n; i++) + else { + for(int i= 0; i < n; i++) INT64(result)[i]= kJ(x)[i]; } classgets(result, mkString("integer64")); @@ -404,7 +383,7 @@ static SEXP from_float_kobject(K x) { SEXP result; if(scalar(x)) return ScalarReal(ISNAN(x->e)?R_NaN:x->e); PROTECT(result= allocVector(REALSXP,x->n)); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) REAL(result)[i]= (double) ISNAN(kE(x)[i])?R_NaN:kE(x)[i]; UNPROTECT(1); return result; @@ -414,7 +393,7 @@ static SEXP from_double_kobject(K x) { SEXP result; if(scalar(x)) return ScalarReal(ISNAN(x->f)?R_NaN:x->f); PROTECT(result= allocVector(REALSXP,x->n)); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) REAL(result)[i]= ISNAN(kF(x)[i])?R_NaN:kF(x)[i]; UNPROTECT(1); return result; @@ -422,25 +401,22 @@ static SEXP from_double_kobject(K x) { static SEXP from_string_kobject(K x) { SEXP result; - J n=scalar(x)?1:x->n; + long n=scalar(x)?1:x->n; PROTECT(result= allocVector(STRSXP,1)); - if(scalar(x)) { + if(scalar(x)) SET_STRING_ELT(result, 0, mkCharLen((S) &x->g, 1)); - } else { + else SET_STRING_ELT(result, 0, mkCharLen((S) kC(x), n)); - }; UNPROTECT(1); return result; } -static SEXP from_string_column_kobject(K x) -{ +static SEXP from_string_column_kobject(K x) { SEXP result; - J i, n=scalar(x)?1:x->n; + long n=scalar(x)?1:x->n; PROTECT(result= allocVector(STRSXP,n)); - for(i = 0; i < n; i++) { + for(int i = 0; i < n; i++) SET_STRING_ELT(result, i, mkCharLen((S)&kC(x)[i],1)); - } UNPROTECT(1); return result; } @@ -449,20 +425,19 @@ static SEXP from_symbol_kobject(K x) { SEXP result; if(scalar(x)) return mkString(x->s); PROTECT(result= allocVector(STRSXP,x->n)); - for(J i= 0; i < x->n; i++) + for(int i= 0; i < x->n; i++) SET_STRING_ELT(result, i, mkChar(kS(x)[i])); UNPROTECT(1); return result; } -static SEXP from_month_kobject(K object) -{ - return from_int_kobject(object); +static SEXP from_month_kobject(K object) { + return from_int_kobject(object); } static SEXP from_date_kobject(K x) { SEXP result=PROTECT(from_int_kobject(x)); - for(J i= 0; i < XLENGTH(result); i++) + for(int i= 0; i < XLENGTH(result); i++) if(INTEGER(result)[i]!=NA_INTEGER) INTEGER(result)[i]+=kdbDateOffset; setdateclass(result); UNPROTECT(1); @@ -471,7 +446,7 @@ static SEXP from_date_kobject(K x) { static SEXP from_datetime_kobject(K x) { SEXP result=PROTECT(from_double_kobject(x)); - for(J i= 0; i < XLENGTH(result); i++) + for(int i= 0; i < XLENGTH(result); i++) REAL(result)[i]= REAL(result)[i]* sec2day + kdbDateOffset * sec2day; setdatetimeclass(result); settimezone(result,"GMT"); @@ -484,63 +459,57 @@ static SEXP from_minute_kobject(K object) { setdifftimeclass(result,"mins"); UNPROTECT(1); return result; - } +} static SEXP from_second_kobject(K object) { SEXP result=PROTECT(from_int_kobject(object)); setdifftimeclass(result,"secs"); UNPROTECT(1); return result; - } +} -static SEXP from_time_kobject(K object) -{ - return from_int_kobject(object); +static SEXP from_time_kobject(K object) { + return from_int_kobject(object); } static SEXP from_timespan_kobject(K x) { return from_long_kobject(x); } -static SEXP from_timestamp_kobject(K x) -{ +static SEXP from_timestamp_kobject(K x) { SEXP result=from_long_kobject(x); - J i,n=XLENGTH(result); + long n=XLENGTH(result); PROTECT(result); - for(i= 0; i < n; i++) - if(INT64(result)[i]!=nj)INT64(result)[i]+=epoch_offset; + for(int i= 0; i < n; i++) + if(INT64(result)[i]!=nj)INT64(result)[i]+=epoch_offset; settimestampclass(result); UNPROTECT(1); return result; } -static SEXP from_dictionary_kobject(K x) -{ - SEXP names, result; - K table, k= kK(x)[0], v= kK(x)[1]; - - /* if keyed, try to create a simple table */ - /* ktd will free its argument if successful */ - /* if fails, x is still valid */ - if (XT==k->t && XT==v->t) { - r1(x); - if ((table = ktd(x))) { - result = from_table_kobject(table); - r0(table); - return result; - } - r0(x); - } - - PROTECT(names = from_any_kobject(k)); - PROTECT(result = from_any_kobject(v)); - setAttrib(result, R_NamesSymbol, names); - UNPROTECT(2); - return result; -} - -static SEXP from_table_kobject(K x) -{ +static SEXP from_dictionary_kobject(K x) { + SEXP names, result; + K table, k= kK(x)[0], v= kK(x)[1]; + /* if keyed, try to create a simple table */ + /* ktd will free its argument if successful */ + /* if fails, x is still valid */ + if (XT==k->t && XT==v->t) { + r1(x); + if ((table = ktd(x))) { + result = from_table_kobject(table); + r0(table); + return result; + } + r0(x); + } + PROTECT(names = from_any_kobject(k)); + PROTECT(result = from_any_kobject(v)); + setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + return result; +} + +static SEXP from_table_kobject(K x) { SEXP names, result; PROTECT(names = from_any_kobject(kK(x->k)[0])); PROTECT(result = from_columns_kobject(kK(x->k)[1])); diff --git a/embedr.c b/src/embedr.c similarity index 88% rename from embedr.c rename to src/embedr.c index 29eaf2b..edc47dc 100644 --- a/embedr.c +++ b/src/embedr.c @@ -19,10 +19,10 @@ #endif #include -#include "src/socketpair.c" +#include "socketpair.c" #define KXVER 3 -#include "src/k.h" +#include "k.h" #define INT64(x) ((J*) REAL(x)) // Offsets used in conversion between R and q static J epoch_offset=10957*24*60*60*1000000000LL; @@ -32,7 +32,7 @@ static int sec2day = 86400; static int kdbDateOffset = 10957; static int kdbSecOffset = 946684800; -#include "src/common.c" -#include "src/rserver.c" +#include "common.c" +#include "rserver.c" int R_SignalHandlers = 0; diff --git a/src/qserver.c b/src/qserver.c index 8e837fb..33bd241 100644 --- a/src/qserver.c +++ b/src/qserver.c @@ -22,78 +22,69 @@ EXPORT SEXP kx_r_execute(SEXP c, SEXP); * If we just have a host and port we call khp from the kdb+ interface. * If we have a host, port, "username:password" we call instead khpu. */ -SEXP kx_r_open_connection(SEXP whence) -{ - SEXP result; - int connection, port; - char *host; - int length = GET_LENGTH(whence); - if (length < 2) - error("Can't connect with so few parameters.."); - - port = INTEGER_POINTER (VECTOR_ELT(whence, 1))[0]; - host = (char*) CHARACTER_VALUE(VECTOR_ELT(whence, 0)); - - if (2 == length) - connection = khp(host, port); - else { - char *user = (char*) CHARACTER_VALUE(VECTOR_ELT (whence, 2)); - connection = khpu(host, port, user); - } - if (!connection) - error("Could not authenticate"); - else if (connection < 0) { +SEXP kx_r_open_connection(SEXP whence) { + SEXP result; + int connection, port; + char *host; + int length = GET_LENGTH(whence); + if (length < 2) + error("Can't connect with so few parameters.."); + port = INTEGER_POINTER (VECTOR_ELT(whence, 1))[0]; + host = (char*) CHARACTER_VALUE(VECTOR_ELT(whence, 0)); + if (2 == length) + connection = khp(host, port); + else { + char *user = (char*) CHARACTER_VALUE(VECTOR_ELT (whence, 2)); + connection = khpu(host, port, user); + } + if (!connection) + error("Could not authenticate"); + else if (connection < 0) { #ifdef WIN32 - char buf[256]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); - error(buf); + char buf[256]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); + error(buf); #else - error(strerror(errno)); + error(strerror(errno)); #endif - } - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = connection; - UNPROTECT(1); - return result; + } + PROTECT(result = NEW_INTEGER(1)); + INTEGER_POINTER(result)[0] = connection; + UNPROTECT(1); + return result; } /* * Close a connection to an existing kdb+ process. */ -SEXP kx_r_close_connection(SEXP connection) -{ - SEXP result; - - /* Close the connection. */ - kclose(INTEGER_VALUE(connection)); - - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = 0; - UNPROTECT(1); - return result; +SEXP kx_r_close_connection(SEXP connection) { + SEXP result; + /* Close the connection. */ + kclose(INTEGER_VALUE(connection)); + PROTECT(result = NEW_INTEGER(1)); + INTEGER_POINTER(result)[0] = 0; + UNPROTECT(1); + return result; } /* * Execute a kdb+ query over the given connection. */ -SEXP kx_r_execute(SEXP connection, SEXP query) -{ - K result; - SEXP s; - kx_connection = INTEGER_VALUE(connection); - - result = k(kx_connection, (char*) CHARACTER_VALUE(query), (K)0); - if (0 == result) { - error("Error: not connected to kdb+ server\n"); - } - else if (-128 == result->t) { - char *e = calloc(strlen(result->s) + 1, 1); - strcpy(e, result->s); - r0(result); - error("Error from kdb+: `%s\n", e); - } - s = from_any_kobject(result); - r0(result); - return s; +SEXP kx_r_execute(SEXP connection, SEXP query) { + K result; + SEXP s; + kx_connection = INTEGER_VALUE(connection); + result = k(kx_connection, (char*) CHARACTER_VALUE(query), (K)0); + if (0 == result) + error("Error: not connected to kdb+ server\n"); + else if (-128 == result->t) { + char *e = calloc(strlen(result->s) + 1, 1); + strcpy(e, result->s); + r0(result); + error("Error from kdb+: `%s\n", e); + } + s = from_any_kobject(result); + r0(result); + return s; } diff --git a/src/rserver.c b/src/rserver.c index 9efc9ee..ad26cee 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -9,11 +9,11 @@ K ropen(K x); K rclose(K x); -K rcmd(K x); +K rexec(K x); K rget(K x); K rset(K x,K y); -ZK rexec(int type,K x); +ZK rcmd(int type,K x); ZK klogicv(J len, int *val); ZK klogica(J len, int rank, int *shape, int *val); ZK kintv(J len, int *val); @@ -64,142 +64,120 @@ Rboolean isClass(const char *class_, SEXP s) { } ZK from_any_robject(SEXP sxp){ - if(isClass("data.frame", sxp)) { + if(isClass("data.frame", sxp)) return from_frame_robject(sxp); - } - if(isClass("factor", sxp)) { + if(isClass("factor", sxp)) return from_factor_robject(sxp); - } - if(isClass("Date", sxp)){ + if(isClass("Date", sxp)) return from_date_robject(sxp); - } - if(isClass("POSIXt", sxp)){ + if(isClass("POSIXt", sxp)) return from_datetime_robject(sxp); - } K result = 0; int type = TYPEOF(sxp); switch (type) { - case NILSXP : - return from_null_robject(sxp); - break; /* nil = NULL */ - case SYMSXP : - return from_symbol_robject(sxp); - break; /* symbols */ - case LISTSXP : - return from_pairlist_robject(sxp); - break; /* lists of dotted pairs */ - case CLOSXP : - return from_closure_robject(sxp); - break; /* closures */ - case LANGSXP : - return from_language_robject(sxp); - break; /* language constructs (special lists) */ - case CHARSXP : - return from_char_robject(sxp); - break; /* "scalar" string type (internal only)*/ - case LGLSXP : - return from_logical_robject(sxp); - break; /* logical vectors */ - case RAWSXP : - return from_raw_robject(sxp); - break; /* raw bytes */ - case INTSXP : - return from_integer_robject(sxp); - break; /* integer vectors */ - case REALSXP : - return from_double_robject(sxp); - break; /* real variables */ - case STRSXP : - return from_character_robject(sxp); - break; /* string vectors */ - case VECSXP : - return from_vector_robject(sxp); - break; /* generic vectors */ - case FREESXP : - return error_broken_robject(sxp); - break; /* node released by GC */ - case ANYSXP : - return error_broken_robject(sxp); - break; /* make "any" args work */ - case EXPRSXP : - case BCODESXP : - case EXTPTRSXP : - case WEAKREFSXP : - case S4SXP : - case NEWSXP : - case FUNSXP : - case PROMSXP : - case SPECIALSXP : - case BUILTINSXP : - case ENVSXP : - case CPLXSXP : - case DOTSXP : - return from_nyi_robject(sxp); + case NILSXP : + return from_null_robject(sxp); + break; /* nil = NULL */ + case SYMSXP : + return from_symbol_robject(sxp); + break; /* symbols */ + case LISTSXP : + return from_pairlist_robject(sxp); + break; /* lists of dotted pairs */ + case CLOSXP : + return from_closure_robject(sxp); + break; /* closures */ + case LANGSXP : + return from_language_robject(sxp); + break; /* language constructs (special lists) */ + case CHARSXP : + return from_char_robject(sxp); + break; /* "scalar" string type (internal only)*/ + case LGLSXP : + return from_logical_robject(sxp); + break; /* logical vectors */ + case RAWSXP : + return from_raw_robject(sxp); + break; /* raw bytes */ + case INTSXP : + return from_integer_robject(sxp); + break; /* integer vectors */ + case REALSXP : + return from_double_robject(sxp); + break; /* real variables */ + case STRSXP : + return from_character_robject(sxp); + break; /* string vectors */ + case VECSXP : + return from_vector_robject(sxp); + break; /* generic vectors */ + case FREESXP : + return error_broken_robject(sxp); + break; /* node released by GC */ + case ANYSXP : + return error_broken_robject(sxp); + break; /* make "any" args work */ + case EXPRSXP : + case BCODESXP : + case EXTPTRSXP : + case WEAKREFSXP : + case S4SXP : + case NEWSXP : + case FUNSXP : + case PROMSXP : + case SPECIALSXP : + case BUILTINSXP : + case ENVSXP : + case CPLXSXP : + case DOTSXP : + return from_nyi_robject(sxp); break; - } - return result; -} - -ZK dictpairlist(SEXP sxp){ - K k= knk(0); - K v= knk(0); - SEXP s= sxp; - while(s!=R_NilValue){ - jk(&k,from_any_robject(TAG(s))); - jk(&v,from_any_robject(CAR(s))); - s= CDR(s); - } - return xD(k, v); + } + return result; } /* add attribute */ -ZK addattR (K x,SEXP att) -{ - // attrs are pairlists: LISTSXP - K u = from_pairlist_robject(att); - return knk(2,u,x); +ZK addattR (K x,SEXP att) { + // attrs are pairlists: LISTSXP + K u = from_pairlist_robject(att); + return knk(2,u,x); } /* add attribute if any */ -ZK attR(K x,SEXP sxp) -{ - SEXP att = ATTRIB(sxp); - if (isNull(att)) return x; - return addattR(x,att); +ZK attR(K x,SEXP sxp) { + SEXP att = ATTRIB(sxp); + if (isNull(att)) return x; + return addattR(x,att); } ZK atom_value_dict(J len, K v, SEXP keys){ - K k= ktn(KS, len); - for(J i= 0; i < len; i++) { - const char *keyName= CHAR(STRING_ELT(keys, i)); - kS(k)[i]= ss((S) keyName); - } - return xD(k,v); + K k= ktn(KS, len); + for(J i= 0; i < len; i++) { + const char *keyName= CHAR(STRING_ELT(keys, i)); + kS(k)[i]= ss((S) keyName); + } + return xD(k,v); } -ZK error_broken_robject(SEXP sxp) -{ - return krr("Broken R object."); +ZK error_broken_robject(SEXP sxp) { + return krr("Broken R object."); } ZK from_nyi_robject(SEXP sxp){ - return attR(kp((S)Rf_type2char(TYPEOF(sxp))),sxp); + return attR(kp((S)Rf_type2char(TYPEOF(sxp))),sxp); } ZK from_frame_robject(SEXP sxp) { J length= XLENGTH(sxp); if(length == 0) return from_null_robject(sxp); - SEXP colNames= getAttrib(sxp, R_NamesSymbol); - K k= ktn(KS, length), v= ktn(0, length); for(J i= 0; i < length; i++) { kK(v)[i]= from_any_robject(VECTOR_ELT(sxp, i)); const char *colName= CHAR(STRING_ELT(colNames, i)); kS(k)[i]= ss((S) colName); } - K tbl= xT(xD(k, v)); return tbl; } @@ -215,11 +193,10 @@ ZK from_factor_robject(SEXP sxp) { return x; } -ZK from_raw_robject(SEXP sxp) -{ - K x = ktn(KG,XLENGTH(sxp)); - DO(xn,kG(x)[i]=RAW(sxp)[i]) - return x; +ZK from_raw_robject(SEXP sxp) { + K x = ktn(KG,XLENGTH(sxp)); + DO(xn,kG(x)[i]=RAW(sxp)[i]) + return x; } ZK from_date_robject(SEXP sxp) { @@ -237,7 +214,7 @@ ZK from_date_robject(SEXP sxp) { return x; } -ZK from_datetime_ct_robject(SEXP sxp){ +ZK from_datetime_ct_robject(SEXP sxp) { K x; J length = XLENGTH(sxp); x = ktn(KZ,length); @@ -245,7 +222,7 @@ ZK from_datetime_ct_robject(SEXP sxp){ return x; } -ZK from_datetime_lt_robject(SEXP sxp){ +ZK from_datetime_lt_robject(SEXP sxp) { K x; J i, key_length= XLENGTH(sxp); x= ktn(0, key_length); @@ -271,7 +248,7 @@ ZK from_datetime_lt_robject(SEXP sxp){ } //Wraper function of POSIXt -ZK from_datetime_robject(SEXP sxp){ +ZK from_datetime_robject(SEXP sxp) { if(isClass("POSIXct", sxp)) return from_datetime_ct_robject(sxp); else @@ -280,185 +257,170 @@ ZK from_datetime_robject(SEXP sxp){ // NULL in R(R_NilValue): often used as generic zero length vector // NULL objects cannot have attributes and attempting to assign one by attr gives an error -ZK from_null_robject(SEXP sxp) -{ - return knk(0); -} - -ZK from_symbol_robject(SEXP sxp) -{ - const char* t = CHAR(PRINTNAME(sxp)); - K x = ks((S)t); - return attR(x,sxp); -} - -ZK from_pairlist_robject(SEXP sxp) -{ - K x = ktn(0,2*length(sxp)); - SEXP s = sxp;J i; - for(i=0;in;i+=2) { - kK(x)[i] = from_any_robject(CAR(s)); - kK(x)[i+1] = from_any_robject(TAG(s)); - s=CDR(s); - } - return attR(x,sxp); -} - -ZK from_closure_robject(SEXP sxp) -{ - K x = from_any_robject(FORMALS(sxp)); - K y = from_any_robject(BODY(sxp)); - return attR(knk(2,x,y),sxp); -} - -ZK from_language_robject(SEXP sxp) -{ - K x = knk(0); - SEXP s = sxp; - while (0 < length(s)) { - x = jk(&x,from_any_robject(CAR(s))); - s = CDR(s); - } - return attR(x,sxp); -} - -ZK from_char_robject(SEXP sxp) -{ - K x = kpn((S)CHAR(STRING_ELT(sxp,0)),LENGTH(sxp)); - return attR(x,sxp); -} - -ZK from_logical_robject(SEXP sxp) -{ - K x; - J len = XLENGTH(sxp); - SEXP dim= getAttrib(sxp, R_DimSymbol); - if (isNull(dim)) { - //Process values - x = klogicv(len,LOGICAL(sxp)); - //Dictionary with atom values - SEXP keyNames= getAttrib(sxp, R_NamesSymbol); - if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ - return atom_value_dict(len, x, keyNames); - } - //Normal kdb+ list - return attR(x,sxp); - } - x = klogica(len,length(dim),INTEGER(dim),LOGICAL(sxp)); - SEXP dimnames= getAttrib(sxp, R_DimNamesSymbol); - if (!isNull(dimnames)) - return attR(x,sxp); - SEXP e; - PROTECT(e = duplicate(sxp)); - setAttrib(e, R_DimSymbol, R_NilValue); - x = attR(x,e); - UNPROTECT(1); - return x; -} - -ZK from_integer_robject(SEXP sxp){ - K x; - J len = XLENGTH(sxp); - SEXP dim= getAttrib(sxp, R_DimSymbol); - if (isNull(dim)) { - //Process values - x = kintv(len,INTEGER(sxp)); - //Dictionary with atom values - SEXP keyNames= getAttrib(sxp, R_NamesSymbol); - if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ - return atom_value_dict(len, x, keyNames); - } - //Normal kdb+ list - return attR(x,sxp); - } - x = kinta(len,length(dim),INTEGER(dim),INTEGER(sxp)); - SEXP dimnames = getAttrib(sxp, R_DimNamesSymbol); - if (!isNull(dimnames)) - return attR(x,sxp); - SEXP e; - PROTECT(e = duplicate(sxp)); - setAttrib(e, R_DimSymbol, R_NilValue); - x = attR(x,e); - UNPROTECT(1); - return x; +ZK from_null_robject(SEXP sxp) { + return knk(0); +} + +ZK from_symbol_robject(SEXP sxp) { + const char* t = CHAR(PRINTNAME(sxp)); + K x = ks((S)t); + return attR(x,sxp); +} + +ZK from_pairlist_robject(SEXP sxp) { + K x = ktn(0,2*length(sxp)); + SEXP s = sxp;J i; + for(i=0;in;i+=2) { + kK(x)[i] = from_any_robject(CAR(s)); + kK(x)[i+1] = from_any_robject(TAG(s)); + s=CDR(s); + } + return attR(x,sxp); +} + +ZK from_closure_robject(SEXP sxp) { + K x = from_any_robject(FORMALS(sxp)); + K y = from_any_robject(BODY(sxp)); + return attR(knk(2,x,y),sxp); +} + +ZK from_language_robject(SEXP sxp) { + K x = knk(0); + SEXP s = sxp; + while (0 < length(s)) { + x = jk(&x,from_any_robject(CAR(s))); + s = CDR(s); + } + return attR(x,sxp); +} + +ZK from_char_robject(SEXP sxp) { + K x = kpn((S)CHAR(STRING_ELT(sxp,0)),LENGTH(sxp)); + return attR(x,sxp); +} + +ZK from_logical_robject(SEXP sxp) { + K x; + J len = XLENGTH(sxp); + SEXP dim= getAttrib(sxp, R_DimSymbol); + if (isNull(dim)) { + //Process values + x = klogicv(len,LOGICAL(sxp)); + //Dictionary with atom values + SEXP keyNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(keyNames)&&len==XLENGTH(keyNames)) + return atom_value_dict(len, x, keyNames); + //Normal kdb+ list + return attR(x,sxp); + } + x = klogica(len,length(dim),INTEGER(dim),LOGICAL(sxp)); + SEXP dimnames= getAttrib(sxp, R_DimNamesSymbol); + if (!isNull(dimnames)) + return attR(x,sxp); + SEXP e; + PROTECT(e = duplicate(sxp)); + setAttrib(e, R_DimSymbol, R_NilValue); + x = attR(x,e); + UNPROTECT(1); + return x; +} + +ZK from_integer_robject(SEXP sxp) { + K x; + J len = XLENGTH(sxp); + SEXP dim= getAttrib(sxp, R_DimSymbol); + if (isNull(dim)) { + //Process values + x = kintv(len,INTEGER(sxp)); + //Dictionary with atom values + SEXP keyNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(keyNames)&&len==XLENGTH(keyNames)) + return atom_value_dict(len, x, keyNames); + //Normal kdb+ list + return attR(x,sxp); + } + x = kinta(len,length(dim),INTEGER(dim),INTEGER(sxp)); + SEXP dimnames = getAttrib(sxp, R_DimNamesSymbol); + if (!isNull(dimnames)) + return attR(x,sxp); + SEXP e; + PROTECT(e = duplicate(sxp)); + setAttrib(e, R_DimSymbol, R_NilValue); + x = attR(x,e); + UNPROTECT(1); + return x; } ZK from_double_robject(SEXP sxp){ - K x; - I nano, bit64=isClass("integer64",sxp); - J len = XLENGTH(sxp); - SEXP dim= getAttrib(sxp, R_DimSymbol); - if (isNull(dim)) { - //Process values - nano = isClass("nanotime",sxp); - if(nano || bit64) { - x=ktn(nano?KP:KJ,len); - DO(len,kJ(x)[i]=INT64(sxp)[i]) - if(nano) - DO(len,if(kJ(x)[i]!=nj)kJ(x)[i]-=epoch_offset) - }else{ - x= kdoublev(len, REAL(sxp)); - } - //Dictionary with atom values - SEXP keyNames= getAttrib(sxp, R_NamesSymbol); - if(!isNull(keyNames)&&len==XLENGTH(keyNames)){ - return atom_value_dict(len, x, keyNames); - }else if(nano || bit64){ - //Class object - return x; - } - //Normal kdb+ list - return attR(x, sxp); - } - if(bit64){ - x= klonga(len, length(dim), INTEGER(dim), (J*)REAL(sxp)); - }else{ + K x; + I nano, bit64=isClass("integer64",sxp); + J len = XLENGTH(sxp); + SEXP dim= getAttrib(sxp, R_DimSymbol); + if (isNull(dim)) { + //Process values + nano = isClass("nanotime",sxp); + if(nano || bit64) { + x=ktn(nano?KP:KJ,len); + DO(len,kJ(x)[i]=INT64(sxp)[i]) + if(nano) + DO(len,if(kJ(x)[i]!=nj)kJ(x)[i]-=epoch_offset) + } + else + x= kdoublev(len, REAL(sxp)); + //Dictionary with atom values + SEXP keyNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(keyNames)&&len==XLENGTH(keyNames)) + return atom_value_dict(len, x, keyNames); + else if(nano || bit64) + return x; + //Normal kdb+ list + return attR(x, sxp); + } + if(bit64) + x= klonga(len, length(dim), INTEGER(dim), (J*)REAL(sxp)); + else x= kdoublea(len, length(dim), INTEGER(dim), REAL(sxp)); - } - SEXP dimnames = GET_DIMNAMES(sxp); - if (!isNull(dimnames)) - return attR(x,sxp); - SEXP e; - PROTECT(e = duplicate(sxp)); - setAttrib(e, R_DimSymbol, R_NilValue); - if(bit64) classgets(e,R_NilValue); - x = attR(x,e); - UNPROTECT(1); - return x; -} - -ZK from_character_robject(SEXP sxp) -{ - K x; - J i, length = XLENGTH(sxp); - if (length == 1) - x = kp((char*) CHAR(STRING_ELT(sxp,0))); - else { - x = ktn(0, length); - for (i = 0; i < length; i++) { - xK[i] = kp((char*) CHAR(STRING_ELT(sxp,i))); - } - } + SEXP dimnames = GET_DIMNAMES(sxp); + if (!isNull(dimnames)) + return attR(x,sxp); + SEXP e; + PROTECT(e = duplicate(sxp)); + setAttrib(e, R_DimSymbol, R_NilValue); + if(bit64) + classgets(e,R_NilValue); + x = attR(x,e); + UNPROTECT(1); + return x; +} + +ZK from_character_robject(SEXP sxp) { + K x; + J i, length = XLENGTH(sxp); + if (length == 1) + x = kp((char*) CHAR(STRING_ELT(sxp,0))); + else { + x = ktn(0, length); + for (i = 0; i < length; i++) + xK[i] = kp((char*) CHAR(STRING_ELT(sxp,i))); + } return attR(x,sxp); } -ZK from_vector_robject(SEXP sxp) -{ - J i, length = LENGTH(sxp); - K x = ktn(0, length); - for (i = 0; i < length; i++) { - kK(x)[i] = from_any_robject(VECTOR_ELT(sxp, i)); - } - SEXP colNames= getAttrib(sxp, R_NamesSymbol); - if(!isNull(colNames)&&length==XLENGTH(colNames)){ - K k= ktn(KS, length); - for(i= 0; i < length; i++) { - const char *colName= CHAR(STRING_ELT(colNames, i)); - kS(k)[i]= ss((S) colName); - } - return xD(k,x); - } - return attR(x, sxp); +ZK from_vector_robject(SEXP sxp) { + J i, length = LENGTH(sxp); + K x = ktn(0, length); + for (i = 0; i < length; i++) + kK(x)[i] = from_any_robject(VECTOR_ELT(sxp, i)); + SEXP colNames= getAttrib(sxp, R_NamesSymbol); + if(!isNull(colNames)&&length==XLENGTH(colNames)) { + K k= ktn(KS, length); + for(i= 0; i < length; i++) { + const char *colName= CHAR(STRING_ELT(colNames, i)); + kS(k)[i]= ss((S) colName); + } + return xD(k,x); + } + return attR(x, sxp); } /* @@ -466,21 +428,23 @@ ZK from_vector_robject(SEXP sxp) */ /* get k string or symbol name */ -static char * getkstring(K x) -{ - char *s=NULL; - int len; - switch (xt) { - case -KC : - s = calloc(2,1); s[0] = xg; break; - case KC : - s = calloc(1+xn,1); memmove(s, xG, xn); break; - case -KS : // TODO: xs is already 0 terminated and fixed. can just return xs - len = 1+strlen(xs); - s = calloc(len,1); memmove(s, xs, len); break; - default : krr("invalid name"); - } - return s; +static char * getkstring(K x) { + char *s=NULL; + int len; + switch (xt) { + case -KC : + s = calloc(2,1); s[0] = xg; + break; + case KC : + s = calloc(1+xn,1); memmove(s, xG, xn); + break; + case -KS : // TODO: xs is already 0 terminated and fixed. can just return xs + len = 1+strlen(xs); + s = calloc(len,1); memmove(s, xs, len); break; + default : + krr("invalid name"); + } + return s; } /* @@ -523,41 +487,39 @@ ZK klogica(J len, int rank, int *shape, int *val) { return x; } -ZK kintv(J len, int *val) -{ - K x = ktn(KI, len); - DO(len,kI(x)[i]=(val)[i]); - return x; -} - -ZK kinta(J len, int rank, int *shape, int *val) -{ - K x,y; - J i,j,r,c,k; - switch (rank) { - case 1 : - x = kintv(len,val); - break; - case 2 : - r = shape[0]; - c = shape[1]; - x = knk(0); - for (i=0;i= 0) return ki(ROPEN); - int s,mode=0; char *argv[] = {"R","--slave"}; - if (x && (-KI ==x->t || -KJ ==x->t)) mode=(x->t==-KI?x->i:x->j)!=0; - if (mode) argv[1] = "--verbose"; - int argc = sizeof(argv)/sizeof(argv[0]); - s=Rf_initEmbeddedR(argc, argv); - if (s<0) return krr("open failed"); - if(dumb_socketpair(spair, 1) == -1){ +K ropen(K x) { + if(!RLOAD) return krr("main thread only"); + if (ROPEN >= 0) return ki(ROPEN); + int s,mode=0; char *argv[] = {"R","--slave"}; + if (x && (-KI ==x->t || -KJ ==x->t)) mode=(x->t==-KI?x->i:x->j)!=0; + if (mode) argv[1] = "--verbose"; + int argc = sizeof(argv)/sizeof(argv[0]); + s=Rf_initEmbeddedR(argc, argv); + if (s<0) return krr("open failed"); + if(dumb_socketpair(spair, 1) == -1) return krr("Init failed for socketpair"); - } - sd1(-spair[0], &processR); - #ifndef WIN32 - pthread_t t; - if(pthread_create(&t, NULL, pingmain, NULL)) - R krr("poller_thread"); - pingthread= &t; - #else - if(_beginthreadex(0,0,pingmain,NULL,0,0)==-1) + sd1(-spair[0], &processR); + #ifndef WIN32 + pthread_t t; + if(pthread_create(&t, NULL, pingmain, NULL)) + R krr("poller_thread"); + pingthread= &t; + #else + if(_beginthreadex(0,0,pingmain,NULL,0,0)==-1) R krr("poller_thread"); - #endif - ROPEN=mode; - return ki(ROPEN); + #endif + ROPEN=mode; + return ki(ROPEN); } // note that embedded R can be initialised once. No open/close/open supported // http://r.789695.n4.nabble.com/Terminating-and-restarting-an-embedded-R-instance-possible-td4641823.html K rclose(K x){R NULL;} -K rcmd(K x) { return rexec(0,x); } -K rget(K x) { return rexec(1,x); } +K rexec(K x) { return rcmd(0,x); } +K rget(K x) { return rcmd(1,x); } static char* ParseError[5]={"null","ok","incomplete","error","eof"}; -K rexec(int type,K x) -{ - if(!RLOAD) return krr("main thread only"); - if (ROPEN < 0) ropen(NULL); - SEXP e, p, r, xp; - char rerr[256];extern char R_ParseErrorMsg[256]; - int error; - ParseStatus status; - if(abs(x->t)==KS) e=from_symbol_kobject(x); - else if(abs(x->t)==KC) e=from_string_kobject(x); - else return krr("type"); - PROTECT(e); - PROTECT(p=R_ParseVector(e, 1, &status, R_NilValue)); - if (status != PARSE_OK) { - UNPROTECT(2); - snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); - return krr(rerr); - } - PROTECT(xp=VECTOR_ELT(p, 0)); - r=R_tryEvalSilent(xp, R_GlobalEnv, &error); - UNPROTECT(3); - R_ProcessEvents(); - if (error) { - snprintf(rerr,sizeof(rerr),"eval error: %s",R_curErrorBuf()); - return krr(rerr); - } - if (type==1) return from_any_robject(r); - return (K)0; //return knk(0) for cmd success? +K rcmd(int type,K x) { + if(!RLOAD) return krr("main thread only"); + if (ROPEN < 0) ropen(NULL); + SEXP e, p, r, xp; + char rerr[256];extern char R_ParseErrorMsg[256]; + int error; + ParseStatus status; + if(abs(x->t)==KS) + e=from_symbol_kobject(x); + else if(abs(x->t)==KC) + e=from_string_kobject(x); + else + return krr("type"); + PROTECT(e); + PROTECT(p=R_ParseVector(e, 1, &status, R_NilValue)); + if (status != PARSE_OK) { + UNPROTECT(2); + snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); + return krr(rerr); + } + PROTECT(xp=VECTOR_ELT(p, 0)); + r=R_tryEvalSilent(xp, R_GlobalEnv, &error); + UNPROTECT(3); + R_ProcessEvents(); + if (error) { + snprintf(rerr,sizeof(rerr),"eval error: %s",R_curErrorBuf()); + return krr(rerr); + } + if (type==1) + return from_any_robject(r); + return (K)0; } K rset(K x,K y) { - if(!RLOAD) return krr("main thread only"); - if (ROPEN < 0) ropen(NULL); - ParseStatus status; - SEXP txt, sym, val; - char rerr[256];extern char R_ParseErrorMsg[256]; - char *name = getkstring(x); - /* generate symbol to check name is valid */ - PROTECT(txt=allocVector(STRSXP, 1)); - SET_STRING_ELT(txt, 0, mkChar(name)); - free(name); - PROTECT(sym = R_ParseVector(txt, 1, &status,R_NilValue)); - if (status != PARSE_OK) { - UNPROTECT(2); - snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); - return krr(rerr); - } - if(SYMSXP != TYPEOF(VECTOR_ELT(sym,0))){ - UNPROTECT(2); - return krr("nyi"); - } - /* read back symbol string */ - const char *c = CHAR(PRINTNAME(VECTOR_ELT(sym,0))); - PROTECT(val = from_any_kobject(y)); - defineVar(install(c),val,R_GlobalEnv); - UNPROTECT(3); - R_ProcessEvents(); - return (K)0; + if(!RLOAD) + return krr("main thread only"); + if (ROPEN < 0) + ropen(NULL); + ParseStatus status; + SEXP txt, sym, val; + char rerr[256];extern char R_ParseErrorMsg[256]; + char *name = getkstring(x); + /* generate symbol to check name is valid */ + PROTECT(txt=allocVector(STRSXP, 1)); + SET_STRING_ELT(txt, 0, mkChar(name)); + free(name); + PROTECT(sym = R_ParseVector(txt, 1, &status,R_NilValue)); + if (status != PARSE_OK) { + UNPROTECT(2); + snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); + return krr(rerr); + } + if(SYMSXP != TYPEOF(VECTOR_ELT(sym,0))){ + UNPROTECT(2); + return krr("nyi"); + } + /* read back symbol string */ + const char *c = CHAR(PRINTNAME(VECTOR_ELT(sym,0))); + PROTECT(val = from_any_kobject(y)); + defineVar(install(c),val,R_GlobalEnv); + UNPROTECT(3); + R_ProcessEvents(); + return (K)0; } __attribute__((constructor)) V __attach(V) {RLOAD=1;} diff --git a/rtest.q b/tests/test.q similarity index 99% rename from rtest.q rename to tests/test.q index 599e1cb..982558e 100644 --- a/rtest.q +++ b/tests/test.q @@ -29,7 +29,7 @@ EQUAL:{[id;x;y] //%% System Setting %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ //Load embedR file -\l rinit.q +\l ../init.q //Set seed 42 \S 42 diff --git a/w32.sh b/win_install/w32.sh old mode 100644 new mode 100755 similarity index 54% rename from w32.sh rename to win_install/w32.sh index 8c21d41..9856624 --- a/w32.sh +++ b/win_install/w32.sh @@ -1,5 +1,6 @@ R32=`R.exe RHOME`/bin/i386 export PATH=$R32:"$PATH" rm -f embedr.o -$R32/R.exe CMD SHLIB -o w32/embedr.dll embedr.c src/w32/q.a +mkdir -p w32 +$R32/R.exe CMD SHLIB -o w32/embedr.dll ../embedr.c ../src/w32/q.a cp w32/embedr.dll $QHOME/w32 diff --git a/w64.sh b/win_install/w64.sh old mode 100644 new mode 100755 similarity index 54% rename from w64.sh rename to win_install/w64.sh index c42d6bf..77ae0a7 --- a/w64.sh +++ b/win_install/w64.sh @@ -1,5 +1,6 @@ R64=`R.exe RHOME`/bin/x64 export PATH=$R64:"$PATH" rm -f embedr.o -$R64/R.exe CMD SHLIB -o w64/embedr.dll embedr.c src/w64/q.a +mkdir -p w64 +$R64/R.exe CMD SHLIB -o w64/embedr.dll ../embedr.c ../src/w64/q.a cp w64/embedr.dll $QHOME/w64 From ebaed3c380d2e7ecaf551369dd6af4c561de6490 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Thu, 16 Apr 2020 21:43:44 +0900 Subject: [PATCH 08/33] Fix bug of small timesapn value --- src/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/common.c b/src/common.c index aebaae1..a746599 100644 --- a/src/common.c +++ b/src/common.c @@ -493,7 +493,12 @@ static SEXP from_timespan_kobject(K x) { PROTECT(result); //judge timespan or days - if(((INT64(result)[0]/1000000000LL) % sec2day)==0){ + int isDay=1; + const int examine=n<5?n:5; + for(int j= 0; j < examine; j++) + isDay= (((INT64(result)[j] % sec2day)==0) < isDay)?0:isDay; + + if(isDay){ //difftime days SEXP realresult; PROTECT(realresult=allocVector(REALSXP, n)); From 434734198cbff1a991b1969b6f08c03838716878 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Thu, 16 Apr 2020 22:38:34 +0900 Subject: [PATCH 09/33] Removed unportable code. GUID conversion got more sophisticated. --- src/common.c | 41 ++++++++++++++++++++++++----- src/rserver.c | 73 ++++++++++++++++++++++++++------------------------- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/common.c b/src/common.c index a746599..a24c0ad 100644 --- a/src/common.c +++ b/src/common.c @@ -211,6 +211,11 @@ static SEXP from_columns_kobject(K object); static SEXP from_dictionary_kobject(K); static SEXP from_table_kobject(K); +/* + * Function used in the conversion of kdb guid to R char array + */ +static K guid_2_char(K); + /* * An array of functions that deal with kdbplus data types. Note that the order * is very important as we index it based on the kdb+ type number in the K object. @@ -299,10 +304,10 @@ static SEXP error_broken_kobject(K broken) { static SEXP from_list_of_kobjects(K x) { SEXP result; K y; - long length= x->n, utype; + J i, length= x->n, utype; PROTECT(result= allocVector(VECSXP,length)); utype= length > 0 ? kK(x)[0]->t : 0; - for(int i= 0; i < length; i++) { + for(i= 0; i < length; i++) { y= kK(x)[i]; utype= utype == y->t ? utype : 0; SET_VECTOR_ELT(result, i, from_any_kobject(y)); @@ -325,7 +330,7 @@ static SEXP from_list_of_kobjects(K x) { * doubles respectively). */ -#define scalar(x) (x->t < 0) +static I scalar(K x) { return x->t < 0; } static SEXP from_bool_kobject(K x) { SEXP result; @@ -348,9 +353,21 @@ static SEXP from_byte_kobject(K x) { return result; } -static SEXP from_guid_kobject(K x) { - K y = k(kx_connection,"string",r1(x),(K)0); - SEXP r = from_any_kobject(y);r0(y); +static SEXP from_guid_kobject(K x){ + SEXP r;K y,z= ktn(0,x->n); + if(scalar(x)){ + y= guid_2_char(kG(x)); + r= from_any_kobject(y); + r0(y); + return r; + } + for(J i=0;in;i++){ + y= guid_2_char((G*)(&kU(x)[i])); + kK(z)[i]= kp(kC(y)); + r0(y); + } + r = from_any_kobject(z); + r0(z); return r; } @@ -558,3 +575,15 @@ static SEXP from_table_kobject(K x) { make_data_frame(result); return result; } + +/* + * Util function + */ + +static K guid_2_char(K x){ + K y= ktn(KC,37); + G*gv= x; + sprintf(kC(y),"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",gv[ 0],gv[ 1],gv[ 2],gv[ 3],gv[ 4],gv[ 5],gv[ 6],gv[ 7],gv[ 8],gv[ 9],gv[10],gv[11],gv[12],gv[13],gv[14],gv[15]); + y->n= 36; + return(y); +} diff --git a/src/rserver.c b/src/rserver.c index 70bbcd0..3cf7ca8 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -159,7 +159,8 @@ ZK addattR (K x,SEXP att) { /* add attribute if any */ ZK attR(K x,SEXP sxp) { SEXP att = ATTRIB(sxp); - if (isNull(att)) return x; + if (isNull(att)) + return x; return addattR(x,att); } @@ -429,7 +430,7 @@ ZK from_double_robject(SEXP sxp){ x= klonga(len, length(dim), INTEGER(dim), (J*)REAL(sxp)); else x= kdoublea(len, length(dim), INTEGER(dim), REAL(sxp)); - SEXP dimnames = GET_DIMNAMES(sxp); + SEXP dimnames= getAttrib(sxp, R_DimNamesSymbol); if (!isNull(dimnames)) return attR(x,sxp); SEXP e; @@ -450,7 +451,7 @@ ZK from_character_robject(SEXP sxp) { else { x = ktn(0, length); for (i = 0; i < length; i++) - xK[i] = kp((char*) CHAR(STRING_ELT(sxp,i))); + kK(x)[i] = kp((char*) CHAR(STRING_ELT(sxp,i))); } return attR(x,sxp); } @@ -481,17 +482,17 @@ static char * getkstring(K x) { char *s=NULL; int len; switch (xt) { - case -KC : - s = calloc(2,1); s[0] = xg; - break; - case KC : - s = calloc(1+xn,1); memmove(s, xG, xn); - break; - case -KS : // TODO: xs is already 0 terminated and fixed. can just return xs - len = 1+strlen(xs); - s = calloc(len,1); memmove(s, xs, len); break; - default : - krr("invalid name"); + case -KC : + s = calloc(2,1); s[0] = xg; + break; + case KC : + s = calloc(1+xn,1); memmove(s, xG, xn); + break; + case -KS : // TODO: xs is already 0 terminated and fixed. can just return xs + len = 1+strlen(xs); + s = calloc(len,1); memmove(s, xs, len); break; + default : + krr("invalid name"); } return s; } @@ -511,27 +512,27 @@ ZK klogica(J len, int rank, int *shape, int *val) { K x, y; J i, j, r, c, k; switch(rank) { - case 1: - x= kintv(len, val); - break; - case 2: - r= shape[0]; - c= shape[1]; - x= knk(0); - for(i= 0; i < r; i++) { - y= ktn(KB, c); - for(j= 0; j < c; j++) - kG(y)[j]= val[i + r * j]; - x= jk(&x, y); - }; - break; - default: - k= rank - 1; - r= shape[k]; - c= len / r; - x= knk(0); - for(i= 0; i < r; i++) - x= jk(&x, klogica(c, k, shape, val + c * i)); + case 1: + x= kintv(len, val); + break; + case 2: + r= shape[0]; + c= shape[1]; + x= knk(0); + for(i= 0; i < r; i++) { + y= ktn(KB, c); + for(j= 0; j < c; j++) + kG(y)[j]= val[i + r * j]; + x= jk(&x, y); + }; + break; + default: + k= rank - 1; + r= shape[k]; + c= len / r; + x= knk(0); + for(i= 0; i < r; i++) + x= jk(&x, klogica(c, k, shape, val + c * i)); } return x; } @@ -556,7 +557,7 @@ ZK kinta(J len, int rank, int *shape, int *val) { for (i=0;i Date: Fri, 17 Apr 2020 15:41:33 +0900 Subject: [PATCH 10/33] Fixed bug of judgement of timespan and days. Fixed bug of month conversion. --- src/common.c | 66 +++++++++++++++++++++++++++++++++++++++++---------- src/embedr.c | 1 + src/rserver.c | 57 ++++++++++++++++++++++++++++++++++++++------ tests/test.q | 4 ++-- 4 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/common.c b/src/common.c index a24c0ad..3d154f1 100644 --- a/src/common.c +++ b/src/common.c @@ -163,21 +163,32 @@ static SEXP settimezone(SEXP sxp, char* tzone) { UNPROTECT(1); return sxp; } + /* for date */ static SEXP setdateclass(SEXP sxp) { - SEXP difftimeclass= PROTECT(allocVector(STRSXP, 1)); - SET_STRING_ELT(difftimeclass, 0, mkChar("Date")); - classgets(sxp, difftimeclass); + SEXP timeclass= PROTECT(allocVector(STRSXP, 1)); + SET_STRING_ELT(timeclass, 0, mkChar("Date")); + classgets(sxp, timeclass); UNPROTECT(1); return sxp; } /* month */ static SEXP setmonthclass(SEXP sxp){ - SEXP difftimeclass= PROTECT(allocVector(STRSXP, 2)); - SET_STRING_ELT(difftimeclass, 0, mkChar("Date")); - SET_STRING_ELT(difftimeclass, 1, mkChar("month")); - classgets(sxp, difftimeclass); + SEXP timeclass= PROTECT(allocVector(STRSXP, 2)); + SET_STRING_ELT(timeclass, 0, mkChar("Date")); + SET_STRING_ELT(timeclass, 1, mkChar("month")); + classgets(sxp, timeclass); + UNPROTECT(1); + return sxp; +} + +/* for timespan */ +static SEXP settimespanclass(SEXP sxp) { + SEXP timeclass= PROTECT(allocVector(STRSXP, 2)); + SET_STRING_ELT(timeclass, 0, mkChar("integer64")); + SET_STRING_ELT(timeclass, 1, mkChar("timespan")); + classgets(sxp, timeclass); UNPROTECT(1); return sxp; } @@ -216,6 +227,31 @@ static SEXP from_table_kobject(K); */ static K guid_2_char(K); +/* + * Functions to derive day count since kdb+ epoch from month count + */ + +bool is_leap(const int year){ + return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0); +} + +int months2days(const int monthcount){ + int days=0; + const int mdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + const int years = monthcount / 12; + for(int i= 0; i < years; i++) + days+=is_leap(2000+i)?366:365; + const int this_year = 2000+years; + const int months= monthcount % 12; + for(int i=0; i < months; i++){ + if(i == 1) + days+=is_leap(this_year)?29:28; + else + days+=mdays[i]; + } + return days; +} + /* * An array of functions that deal with kdbplus data types. Note that the order * is very important as we index it based on the kdb+ type number in the K object. @@ -242,8 +278,10 @@ static SEXP from_any_kobject(K x) { result = from_table_kobject(x); else if (XD == type) result = from_dictionary_kobject(x); - else if (105 == type || 101 == type) - result = from_int_kobject(ki(0)); + else if(101 == type) + result= R_NilValue; + else if(105 == type) + result= from_int_kobject(ki(0)); else if (type <= KT) result = kdbplus_types[type](x); else if (KT #include #include +#include #ifndef WIN32 #include #include diff --git a/src/rserver.c b/src/rserver.c index 3cf7ca8..419186c 100644 --- a/src/rserver.c +++ b/src/rserver.c @@ -54,6 +54,41 @@ ZK from_nyi_robject(SEXP); ZK from_frame_robject(SEXP); ZK from_factor_robject(SEXP); + +/* + * Functions to derive month count since kdb epoch from day count + */ + +extern bool is_leap(const int year); + +int days2months(const int daycount){ + int year=2000, months=0, days=0; + const int mdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + while(true){ + if(daycount < days+(is_leap(year)?366:365)) + break; + days+=is_leap(year)?366:365; + months+=12; + year++; + } + for(int i= 0; i < 12; i++){ + if(days < daycount){ + if(i==1) + days+=is_leap(year)?29:28; + else + days+=mdays[i]; + months+=1; + } + else + break; + } + return months; +} + +/* + * Utility functions to identify class and unit + */ + Rboolean isClass(const char *class_, SEXP s) { SEXP klass; int i; @@ -220,10 +255,20 @@ ZK from_date_robject(SEXP sxp) { int type= TYPEOF(sxp); switch(type) { case INTSXP: - DO(length,kI(x)[i]=INTEGER(sxp)[i]-kdbDateOffset); + if(isClass("month", sxp)){ + DO(length,kI(x)[i]=days2months(INTEGER(sxp)[i]-kdbDateOffset)); + } + else{ + DO(length,kI(x)[i]=INTEGER(sxp)[i]-kdbDateOffset); + } break; default: - DO(length,kI(x)[i]=ISNA(REAL(sxp)[i])?NA_INTEGER:(I)REAL(sxp)[i]-kdbDateOffset); + if(isClass("month", sxp)){ + DO(length,kI(x)[i]=ISNA(REAL(sxp)[i])?NA_INTEGER:days2months((I)REAL(sxp)[i]-kdbDateOffset)); + } + else{ + DO(length,kI(x)[i]=ISNA(REAL(sxp)[i])?NA_INTEGER:(I)REAL(sxp)[i]-kdbDateOffset); + } } return x; } @@ -298,8 +343,6 @@ ZK from_difftime_robject(SEXP sxp){ return from_second_or_minute_robject(sxp); else if(isUnit("days", sxp)) return from_days_robject(sxp); - else if(isUnit("timespan", sxp)) - return from_double_robject(sxp); else /* hours */ return from_nyi_robject(sxp); } @@ -408,7 +451,7 @@ ZK from_double_robject(SEXP sxp){ if (isNull(dim)) { //Process values nano = isClass("nanotime",sxp); - span = isClass("difftime", sxp) && isUnit("timespan", sxp); + span = isClass("timespan",sxp); if(nano || span || bit64) { x=ktn(nano?KP:(span?KN:KJ),len); DO(len,kJ(x)[i]=INT64(sxp)[i]) @@ -423,8 +466,8 @@ ZK from_double_robject(SEXP sxp){ return atom_value_dict(len, x, keyNames); else if(nano || span || bit64) return x; - //Normal kdb+ list - return attR(x, sxp); + //Normal kdb+ list + return attR(x, sxp); } if(bit64) x= klonga(len, length(dim), INTEGER(dim), (J*)REAL(sxp)); diff --git a/tests/test.q b/tests/test.q index 194e5a0..eb5a3a7 100644 --- a/tests/test.q +++ b/tests/test.q @@ -185,8 +185,8 @@ EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; EQUAL[54; .rk.get"mnth"; 2020.04 2010.01m]; // timespan -.rk.set["tmspans"; 0D12 0D04:20:17.123456789] -EQUAL[55; .rk.get"tmspans"; 0D12 0D04:20:17.123456789]; +.rk.set["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] +EQUAL[55; .rk.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; PROGRESS["Time Test Finished!!"]; From 32c0a75ed9798661757451c685083eea6262d3fc Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Fri, 17 Apr 2020 18:27:44 +0900 Subject: [PATCH 11/33] Restructured so that common files are apparent. --- src/{common.c => common_R_interface/q2r.c} | 103 +++----- src/{rserver.c => common_R_interface/r2q.c} | 262 ++++---------------- src/embedr.c | 20 +- src/feature_for_embedR/q2r_ex.c | 41 +++ src/feature_for_embedR/r2q_ex.c | 188 ++++++++++++++ src/{ => feature_for_embedR}/socketpair.c | 0 src/qserver.c | 90 ------- 7 files changed, 329 insertions(+), 375 deletions(-) rename src/{common.c => common_R_interface/q2r.c} (86%) rename src/{rserver.c => common_R_interface/r2q.c} (76%) create mode 100644 src/feature_for_embedR/q2r_ex.c create mode 100644 src/feature_for_embedR/r2q_ex.c rename src/{ => feature_for_embedR}/socketpair.c (100%) delete mode 100644 src/qserver.c diff --git a/src/common.c b/src/common_R_interface/q2r.c similarity index 86% rename from src/common.c rename to src/common_R_interface/q2r.c index 3d154f1..bad4a99 100644 --- a/src/common.c +++ b/src/common_R_interface/q2r.c @@ -1,5 +1,12 @@ +/*-----------------------------------------------*/ +/* File: q2r.c */ +/* Overview: Common code for Q -> R interface */ +/*-----------------------------------------------*/ + /* - * common code for Q/R interface + * The public interface used from Q. + * https://cran.r-project.org/doc/manuals/r-release/R-ints.pdf + * https://cran.r-project.org/doc/manuals/r-release/R-exts.html */ int kx_connection=0; @@ -53,67 +60,27 @@ char* get_type_name(Sint type) { return r_data_types[0].name; } -/* - * Given the appropriate names, types, and lengths, create an R named list. - */ -SEXP make_named_list(char **names, SEXPTYPE *types, Sint *lengths, Sint n) { - SEXP output, output_names, object = NULL_USER_OBJECT; - Sint elements; - PROTECT(output = NEW_LIST(n)); - PROTECT(output_names = NEW_CHARACTER(n)); - for(int i = 0; i < n; i++){ - elements = lengths[i]; - switch((int)types[i]) { - case LGLSXP: - PROTECT(object = NEW_LOGICAL(elements)); - break; - case INTSXP: - PROTECT(object = NEW_INTEGER(elements)); - break; - case REALSXP: - PROTECT(object = NEW_NUMERIC(elements)); - break; - case STRSXP: - PROTECT(object = NEW_CHARACTER(elements)); - break; - case VECSXP: - PROTECT(object = NEW_LIST(elements)); - break; - default: - error("Unsupported data type at %d %s\n", __LINE__, __FILE__); - } - SET_VECTOR_ELT(output, (Sint)i, object); - SET_STRING_ELT(output_names, i, COPY_TO_USER_STRING(names[i])); - } - SET_NAMES(output, output_names); - UNPROTECT(n+2); - return output; -} - /* * Make a data.frame from a named list by adding row.names, and class * attribute. Uses "1", "2", .. as row.names. */ void make_data_frame(SEXP data) { - SEXP class_name, row_names; Sint n; - PROTECT(data); - PROTECT(class_name = NEW_CHARACTER((Sint) 1)); - SET_STRING_ELT(class_name, 0, COPY_TO_USER_STRING("data.frame")); + SEXP row_names; /* Set the row.names. */ - n = GET_LENGTH(VECTOR_ELT(data,0)); - PROTECT(row_names=NEW_INTEGER(2)); - INTEGER(row_names)[0]=NA_INTEGER; - INTEGER(row_names)[1]=-n; + J n= XLENGTH(VECTOR_ELT(data, 0)); + PROTECT(row_names= allocVector(INTSXP,2)); + INTEGER(row_names)[0]= NA_INTEGER; + INTEGER(row_names)[1]= -n; setAttrib(data, R_RowNamesSymbol, row_names); - SET_CLASS(data, class_name); - UNPROTECT(3); + classgets(data, PROTECT(mkString("data.frame"))); + UNPROTECT(2); } /* for datetime */ static SEXP setdatetimeclass(SEXP sxp) { SEXP datetimeclass = PROTECT(allocVector(STRSXP,2)); - SET_STRING_ELT(datetimeclass, 0, mkChar("POSIXt")); - SET_STRING_ELT(datetimeclass, 1, mkChar("POSIXct")); + SET_STRING_ELT(datetimeclass, 0, mkChar("POSIXct")); + SET_STRING_ELT(datetimeclass, 1, mkChar("POSIXt")); classgets(sxp, datetimeclass); UNPROTECT(1); return sxp; @@ -218,7 +185,7 @@ static SEXP from_second_kobject(K); static SEXP from_time_kobject(K); static SEXP from_timespan_kobject(K); static SEXP from_timestamp_kobject(K); -static SEXP from_columns_kobject(K object); +static SEXP from_columns_kobject(K); static SEXP from_dictionary_kobject(K); static SEXP from_table_kobject(K); @@ -232,7 +199,7 @@ static K guid_2_char(K); */ bool is_leap(const int year){ - return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0); + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int months2days(const int monthcount){ @@ -312,16 +279,16 @@ static SEXP from_any_kobject(K x) { */ static SEXP from_columns_kobject(K x) { SEXP col, result; - int type, length = x->n; + J i, type, length= x->n; K c; - PROTECT(result = NEW_LIST(length)); - for (int i = 0; i < length; i++) { - c = xK[i]; - type = abs(c->t); - if (type == KC) - col = from_string_column_kobject(c); + PROTECT(result= allocVector(VECSXP,length)); + for(i= 0; i < length; i++) { + c= kK(x)[i]; + type= abs(c->t); + if(type == KC) + col= from_string_column_kobject(c); else - col = from_any_kobject(c); + col= from_any_kobject(c); SET_VECTOR_ELT(result, i, col); } UNPROTECT(1); @@ -526,22 +493,22 @@ static SEXP from_datetime_kobject(K x) { return result; } -static SEXP from_minute_kobject(K object) { - SEXP result=PROTECT(from_int_kobject(object)); +static SEXP from_minute_kobject(K x) { + SEXP result=PROTECT(from_int_kobject(x)); setdifftimeclass(result,"mins"); UNPROTECT(1); return result; } -static SEXP from_second_kobject(K object) { - SEXP result=PROTECT(from_int_kobject(object)); +static SEXP from_second_kobject(K x) { + SEXP result=PROTECT(from_int_kobject(x)); setdifftimeclass(result,"secs"); UNPROTECT(1); return result; } -static SEXP from_time_kobject(K object) { - return from_int_kobject(object); +static SEXP from_time_kobject(K x) { + return from_int_kobject(x); } static SEXP from_timespan_kobject(K x) { @@ -610,7 +577,7 @@ static SEXP from_table_kobject(K x) { SEXP names, result; PROTECT(names = from_any_kobject(kK(x->k)[0])); PROTECT(result = from_columns_kobject(kK(x->k)[1])); - SET_NAMES(result, names); + setAttrib(result, R_NamesSymbol, names); UNPROTECT(2); make_data_frame(result); return result; @@ -626,4 +593,4 @@ static K guid_2_char(K x){ sprintf(kC(y),"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",gv[ 0],gv[ 1],gv[ 2],gv[ 3],gv[ 4],gv[ 5],gv[ 6],gv[ 7],gv[ 8],gv[ 9],gv[10],gv[11],gv[12],gv[13],gv[14],gv[15]); y->n= 36; return(y); -} +} \ No newline at end of file diff --git a/src/rserver.c b/src/common_R_interface/r2q.c similarity index 76% rename from src/rserver.c rename to src/common_R_interface/r2q.c index 419186c..1b33293 100644 --- a/src/rserver.c +++ b/src/common_R_interface/r2q.c @@ -1,19 +1,14 @@ -/* - * R server for Q - */ +/*-----------------------------------------------*/ +/* File: r2q.c */ +/* Overview: common code for R -> Q interface */ +/*-----------------------------------------------*/ + /* * The public interface used from Q. * https://cran.r-project.org/doc/manuals/r-release/R-ints.pdf * https://cran.r-project.org/doc/manuals/r-release/R-exts.html */ -K ropen(K x); -K rclose(K x); -K rexec(K x); -K rget(K x); -K rset(K x,K y); - -ZK rcmd(int type,K x); ZK klogicv(J len, int *val); ZK klogica(J len, int rank, int *shape, int *val); ZK kintv(J len, int *val); @@ -23,9 +18,6 @@ ZK kdoublev(J len, double *val); ZK kdoublea(J len, int rank, int *shape, double *val); ZK atom_value_dict(J len, K v, SEXP keys); -__thread int ROPEN=-1; // initialise thread-local. Will fail in other threads. Ideally need to check if on q main thread. -__thread int RLOAD=0; - /* * convert R SEXP into K object. */ @@ -54,7 +46,6 @@ ZK from_nyi_robject(SEXP); ZK from_frame_robject(SEXP); ZK from_factor_robject(SEXP); - /* * Functions to derive month count since kdb epoch from day count */ @@ -109,7 +100,41 @@ Rboolean isUnit(const char *units_, SEXP s){ return FALSE; } -ZK from_any_robject(SEXP sxp){ +/* + * Utility functions to handle attribute + */ + +//extern ZK from_pairlist_robject(SEXP sxp); + +/* add attribute */ +ZK addattR (K x,SEXP att) { + // attrs are pairlists: LISTSXP + K u = from_pairlist_robject(att); + return knk(2,u,x); +} + +/* add attribute if any */ +ZK attR(K x,SEXP sxp) { + SEXP att = ATTRIB(sxp); + if (isNull(att)) + return x; + return addattR(x,att); +} + +ZK atom_value_dict(J len, K v, SEXP keys){ + K k= ktn(KS, len); + for(J i= 0; i < len; i++) { + const char *keyName= CHAR(STRING_ELT(keys, i)); + kS(k)[i]= ss((S) keyName); + } + return xD(k,v); +} + +/* + * Conversopn from R to Q + */ + + ZK from_any_robject(SEXP sxp){ if(isClass("data.frame", sxp)) return from_frame_robject(sxp); if(isClass("factor", sxp)) @@ -184,30 +209,6 @@ ZK from_any_robject(SEXP sxp){ return result; } -/* add attribute */ -ZK addattR (K x,SEXP att) { - // attrs are pairlists: LISTSXP - K u = from_pairlist_robject(att); - return knk(2,u,x); -} - -/* add attribute if any */ -ZK attR(K x,SEXP sxp) { - SEXP att = ATTRIB(sxp); - if (isNull(att)) - return x; - return addattR(x,att); -} - -ZK atom_value_dict(J len, K v, SEXP keys){ - K k= ktn(KS, len); - for(J i= 0; i < len; i++) { - const char *keyName= CHAR(STRING_ELT(keys, i)); - kS(k)[i]= ss((S) keyName); - } - return xD(k,v); -} - ZK error_broken_robject(SEXP sxp) { return krr("Broken R object."); } @@ -248,6 +249,7 @@ ZK from_raw_robject(SEXP sxp) { return x; } + ZK from_date_robject(SEXP sxp) { K x; J length= XLENGTH(sxp); @@ -337,7 +339,7 @@ ZK from_days_robject(SEXP sxp){ return x; } -//Wrapper function of difftime +/* Wrapper function of difftime */ ZK from_difftime_robject(SEXP sxp){ if(isUnit("secs", sxp) || isUnit("mins", sxp)) return from_second_or_minute_robject(sxp); @@ -347,8 +349,10 @@ ZK from_difftime_robject(SEXP sxp){ return from_nyi_robject(sxp); } -// NULL in R(R_NilValue): often used as generic zero length vector -// NULL objects cannot have attributes and attempting to assign one by attr gives an error +/* + * NULL in R(R_NilValue): often used as generic zero length vector + * NULL objects cannot have attributes and attempting to assign one by attr gives an error + */ ZK from_null_robject(SEXP sxp) { return knk(0); } @@ -359,17 +363,6 @@ ZK from_symbol_robject(SEXP sxp) { return attR(x,sxp); } -ZK from_pairlist_robject(SEXP sxp) { - K x = ktn(0,2*length(sxp)); - SEXP s = sxp;J i; - for(i=0;in;i+=2) { - kK(x)[i] = from_any_robject(CAR(s)); - kK(x)[i+1] = from_any_robject(TAG(s)); - s=CDR(s); - } - return attR(x,sxp); -} - ZK from_closure_robject(SEXP sxp) { K x = from_any_robject(FORMALS(sxp)); K y = from_any_robject(BODY(sxp)); @@ -516,30 +509,6 @@ ZK from_vector_robject(SEXP sxp) { return attR(x, sxp); } -/* - * various utilities - */ - -/* get k string or symbol name */ -static char * getkstring(K x) { - char *s=NULL; - int len; - switch (xt) { - case -KC : - s = calloc(2,1); s[0] = xg; - break; - case KC : - s = calloc(1+xn,1); memmove(s, xG, xn); - break; - case -KS : // TODO: xs is already 0 terminated and fixed. can just return xs - len = 1+strlen(xs); - s = calloc(len,1); memmove(s, xs, len); break; - default : - krr("invalid name"); - } - return s; -} - /* * convert R arrays to K lists * done for boolean, int, double @@ -659,7 +628,9 @@ ZK kdoublea(J len, int rank, int *shape, double *val) { x = kdoublev(len,val); break; case 2 : - r = shape[0]; c = shape[1]; x = knk(0); + r = shape[0]; + c = shape[1]; + x = knk(0); for (i=0;i= 0) return ki(ROPEN); - int s,mode=0; char *argv[] = {"R","--slave"}; - if (x && (-KI ==x->t || -KJ ==x->t)) mode=(x->t==-KI?x->i:x->j)!=0; - if (mode) argv[1] = "--verbose"; - int argc = sizeof(argv)/sizeof(argv[0]); - s=Rf_initEmbeddedR(argc, argv); - if (s<0) return krr("open failed"); - if(dumb_socketpair(spair, 1) == -1) - return krr("Init failed for socketpair"); - sd1(-spair[0], &processR); - #ifndef WIN32 - pthread_t t; - if(pthread_create(&t, NULL, pingmain, NULL)) - R krr("poller_thread"); - pingthread= &t; - #else - if(_beginthreadex(0,0,pingmain,NULL,0,0)==-1) - R krr("poller_thread"); - #endif - ROPEN=mode; - return ki(ROPEN); -} - -// note that embedded R can be initialised once. No open/close/open supported -// http://r.789695.n4.nabble.com/Terminating-and-restarting-an-embedded-R-instance-possible-td4641823.html -K rclose(K x){R NULL;} -K rexec(K x) { return rcmd(0,x); } -K rget(K x) { return rcmd(1,x); } - -static char* ParseError[5]={"null","ok","incomplete","error","eof"}; - - -K rcmd(int type,K x) { - if(!RLOAD) return krr("main thread only"); - if (ROPEN < 0) ropen(NULL); - SEXP e, p, r, xp; - char rerr[256];extern char R_ParseErrorMsg[256]; - int error; - ParseStatus status; - if(abs(x->t)==KS) - e=from_symbol_kobject(x); - else if(abs(x->t)==KC) - e=from_string_kobject(x); - else - return krr("type"); - PROTECT(e); - PROTECT(p=R_ParseVector(e, 1, &status, R_NilValue)); - if (status != PARSE_OK) { - UNPROTECT(2); - snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); - return krr(rerr); - } - PROTECT(xp=VECTOR_ELT(p, 0)); - r=R_tryEvalSilent(xp, R_GlobalEnv, &error); - UNPROTECT(3); - R_ProcessEvents(); - if (error) { - snprintf(rerr,sizeof(rerr),"eval error: %s",R_curErrorBuf()); - return krr(rerr); - } - if (type==1) - return from_any_robject(r); - return (K)0; -} - -K rset(K x,K y) { - if(!RLOAD) - return krr("main thread only"); - if (ROPEN < 0) - ropen(NULL); - ParseStatus status; - SEXP txt, sym, val; - char rerr[256];extern char R_ParseErrorMsg[256]; - char *name = getkstring(x); - /* generate symbol to check name is valid */ - PROTECT(txt=allocVector(STRSXP, 1)); - SET_STRING_ELT(txt, 0, mkChar(name)); - free(name); - PROTECT(sym = R_ParseVector(txt, 1, &status,R_NilValue)); - if (status != PARSE_OK) { - UNPROTECT(2); - snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); - return krr(rerr); - } - if(SYMSXP != TYPEOF(VECTOR_ELT(sym,0))){ - UNPROTECT(2); - return krr("nyi"); - } - /* read back symbol string */ - const char *c = CHAR(PRINTNAME(VECTOR_ELT(sym,0))); - PROTECT(val = from_any_kobject(y)); - defineVar(install(c),val,R_GlobalEnv); - UNPROTECT(3); - R_ProcessEvents(); - return (K)0; -} - -__attribute__((constructor)) V __attach(V) {RLOAD=1;} +} \ No newline at end of file diff --git a/src/embedr.c b/src/embedr.c index 7d3658f..bcd5f9f 100644 --- a/src/embedr.c +++ b/src/embedr.c @@ -3,11 +3,11 @@ */ #include #include +#include +#include #include #include #include -#include -#include #ifndef WIN32 #include #include @@ -20,20 +20,22 @@ #endif #include -#include "socketpair.c" #define KXVER 3 #include "k.h" #define INT64(x) ((J*) REAL(x)) + // Offsets used in conversion between R and q -static J epoch_offset=10957*24*60*60*1000000000LL; +static const J epoch_offset=10957*24*60*60*1000000000LL; // Seconds in a day -static int sec2day = 86400; +static const int sec2day = 86400; // Days+Seconds between 1970.01.01 & 2000.01.01 -static int kdbDateOffset = 10957; -static int kdbSecOffset = 946684800; +static const int kdbDateOffset = 10957; +static const int kdbSecOffset = 946684800; -#include "common.c" -#include "rserver.c" +#include "common_R_interface/q2r.c" +#include "common_R_interface/r2q.c" +#include "feature_for_embedR/q2r_ex.c" +#include "feature_for_embedR/r2q_ex.c" int R_SignalHandlers = 0; diff --git a/src/feature_for_embedR/q2r_ex.c b/src/feature_for_embedR/q2r_ex.c new file mode 100644 index 0000000..33a9992 --- /dev/null +++ b/src/feature_for_embedR/q2r_ex.c @@ -0,0 +1,41 @@ +/*-----------------------------------------------------------*/ +/* File: q2r_ex.c */ +/* Overview: Distinct code in embedR for Q -> R interface */ +/*-----------------------------------------------------------*/ + +/* + * Given the appropriate names, types, and lengths, create an R named list. + */ +SEXP make_named_list(char **names, SEXPTYPE *types, Sint *lengths, Sint n) { + SEXP output, output_names, object = NULL_USER_OBJECT; + Sint elements; + PROTECT(output = NEW_LIST(n)); + PROTECT(output_names = NEW_CHARACTER(n)); + for(int i = 0; i < n; i++){ + elements = lengths[i]; + switch((int)types[i]) { + case LGLSXP: + PROTECT(object = NEW_LOGICAL(elements)); + break; + case INTSXP: + PROTECT(object = NEW_INTEGER(elements)); + break; + case REALSXP: + PROTECT(object = NEW_NUMERIC(elements)); + break; + case STRSXP: + PROTECT(object = NEW_CHARACTER(elements)); + break; + case VECSXP: + PROTECT(object = NEW_LIST(elements)); + break; + default: + error("Unsupported data type at %d %s\n", __LINE__, __FILE__); + } + SET_VECTOR_ELT(output, (Sint)i, object); + SET_STRING_ELT(output_names, i, COPY_TO_USER_STRING(names[i])); + } + SET_NAMES(output, output_names); + UNPROTECT(n+2); + return output; +} \ No newline at end of file diff --git a/src/feature_for_embedR/r2q_ex.c b/src/feature_for_embedR/r2q_ex.c new file mode 100644 index 0000000..7a7ae9e --- /dev/null +++ b/src/feature_for_embedR/r2q_ex.c @@ -0,0 +1,188 @@ +/*-----------------------------------------------------------*/ +/* File: r2q_ex.c */ +/* Overview: Distinct code in embedR for R -> Q interface */ +/*-----------------------------------------------------------*/ + +#include "socketpair.c" + +/* + * User interface + */ + +K ropen(K x); +K rclose(K x); +K rexec(K x); +K rget(K x); +K rset(K x,K y); +ZK rcmd(int type,K x); + +__thread int ROPEN=-1; // initialise thread-local. Will fail in other threads. Ideally need to check if on q main thread. +__thread int RLOAD=0; + +/* + * Conversion from R to Q + */ + +ZK from_pairlist_robject(SEXP sxp) { + K x = ktn(0,2*length(sxp)); + SEXP s = sxp;J i; + for(i=0;in;i+=2) { + kK(x)[i] = from_any_robject(CAR(s)); + kK(x)[i+1] = from_any_robject(TAG(s)); + s=CDR(s); + } + return attR(x,sxp); +} + +/* + * various utilities + */ + +/* get k string or symbol name */ +static char * getkstring(K x) { + char *s=NULL; + int len; + switch (xt) { + case -KC : + s = calloc(2,1); s[0] = xg; + break; + case KC : + s = calloc(1+xn,1); memmove(s, xG, xn); + break; + case -KS : // TODO: xs is already 0 terminated and fixed. can just return xs + len = 1+strlen(xs); + s = calloc(len,1); memmove(s, xs, len); break; + default : + krr("invalid name"); + } + return s; +} + +/* + * The public interface used from Q. + */ + +static I spair[2]; +void* pingthread; + +V* pingmain(V* v){ + while(1){ + nanosleep(&(struct timespec){.tv_sec=0,.tv_nsec=1000000}, NULL); + send(spair[1], "M", 1, 0); + } +} + +K processR(I d){ + char buf[1024]; + /*MSG_DONTWAIT - set in sd1(-h,...) */ + while(0 < recv(d, buf, sizeof(buf), 0)) + ; + R_ProcessEvents(); + return (K)0; +} + +/* + * ropen argument is empty, 0 or 1 + * empty,0 --slave (R is quietest) + * 1 --verbose + */ + +K ropen(K x) { + if(!RLOAD) return krr("main thread only"); + if (ROPEN >= 0) return ki(ROPEN); + int s,mode=0; char *argv[] = {"R","--slave"}; + if (x && (-KI ==x->t || -KJ ==x->t)) mode=(x->t==-KI?x->i:x->j)!=0; + if (mode) argv[1] = "--verbose"; + int argc = sizeof(argv)/sizeof(argv[0]); + s=Rf_initEmbeddedR(argc, argv); + if (s<0) return krr("open failed"); + if(dumb_socketpair(spair, 1) == -1) + return krr("Init failed for socketpair"); + sd1(-spair[0], &processR); + #ifndef WIN32 + pthread_t t; + if(pthread_create(&t, NULL, pingmain, NULL)) + R krr("poller_thread"); + pingthread= &t; + #else + if(_beginthreadex(0,0,pingmain,NULL,0,0)==-1) + R krr("poller_thread"); + #endif + ROPEN=mode; + return ki(ROPEN); +} + +// note that embedded R can be initialised once. No open/close/open supported +// http://r.789695.n4.nabble.com/Terminating-and-restarting-an-embedded-R-instance-possible-td4641823.html +K rclose(K x){R NULL;} +K rexec(K x) { return rcmd(0,x); } +K rget(K x) { return rcmd(1,x); } + +static char* ParseError[5]={"null","ok","incomplete","error","eof"}; + +K rcmd(int type,K x) { + if(!RLOAD) return krr("main thread only"); + if (ROPEN < 0) ropen(NULL); + SEXP e, p, r, xp; + char rerr[256];extern char R_ParseErrorMsg[256]; + int error; + ParseStatus status; + if(abs(x->t)==KS) + e=from_symbol_kobject(x); + else if(abs(x->t)==KC) + e=from_string_kobject(x); + else + return krr("type"); + PROTECT(e); + PROTECT(p=R_ParseVector(e, 1, &status, R_NilValue)); + if (status != PARSE_OK) { + UNPROTECT(2); + snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); + return krr(rerr); + } + PROTECT(xp=VECTOR_ELT(p, 0)); + r=R_tryEvalSilent(xp, R_GlobalEnv, &error); + UNPROTECT(3); + R_ProcessEvents(); + if (error) { + snprintf(rerr,sizeof(rerr),"eval error: %s",R_curErrorBuf()); + return krr(rerr); + } + if (type==1) + return from_any_robject(r); + return (K)0; +} + +K rset(K x,K y) { + if(!RLOAD) + return krr("main thread only"); + if (ROPEN < 0) + ropen(NULL); + ParseStatus status; + SEXP txt, sym, val; + char rerr[256];extern char R_ParseErrorMsg[256]; + char *name = getkstring(x); + /* generate symbol to check name is valid */ + PROTECT(txt=allocVector(STRSXP, 1)); + SET_STRING_ELT(txt, 0, mkChar(name)); + free(name); + PROTECT(sym = R_ParseVector(txt, 1, &status,R_NilValue)); + if (status != PARSE_OK) { + UNPROTECT(2); + snprintf(rerr,sizeof(rerr),"%s: %s",ParseError[status], R_ParseErrorMsg); + return krr(rerr); + } + if(SYMSXP != TYPEOF(VECTOR_ELT(sym,0))){ + UNPROTECT(2); + return krr("nyi"); + } + /* read back symbol string */ + const char *c = CHAR(PRINTNAME(VECTOR_ELT(sym,0))); + PROTECT(val = from_any_kobject(y)); + defineVar(install(c),val,R_GlobalEnv); + UNPROTECT(3); + R_ProcessEvents(); + return (K)0; +} + +__attribute__((constructor)) V __attach(V) {RLOAD=1;} diff --git a/src/socketpair.c b/src/feature_for_embedR/socketpair.c similarity index 100% rename from src/socketpair.c rename to src/feature_for_embedR/socketpair.c diff --git a/src/qserver.c b/src/qserver.c deleted file mode 100644 index 33bd241..0000000 --- a/src/qserver.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Q server for R - */ - -/* - * The public interface used from R. - */ - -#ifdef WIN32 -#define EXPORT __declspec(dllexport) -#else -#define EXPORT -#endif - -EXPORT SEXP kx_r_open_connection(SEXP); -EXPORT SEXP kx_r_close_connection(SEXP); -EXPORT SEXP kx_r_execute(SEXP c, SEXP); - -/* - * Open a connection to an existing kdb+ process. - * - * If we just have a host and port we call khp from the kdb+ interface. - * If we have a host, port, "username:password" we call instead khpu. - */ -SEXP kx_r_open_connection(SEXP whence) { - SEXP result; - int connection, port; - char *host; - int length = GET_LENGTH(whence); - if (length < 2) - error("Can't connect with so few parameters.."); - port = INTEGER_POINTER (VECTOR_ELT(whence, 1))[0]; - host = (char*) CHARACTER_VALUE(VECTOR_ELT(whence, 0)); - if (2 == length) - connection = khp(host, port); - else { - char *user = (char*) CHARACTER_VALUE(VECTOR_ELT (whence, 2)); - connection = khpu(host, port, user); - } - if (!connection) - error("Could not authenticate"); - else if (connection < 0) { -#ifdef WIN32 - char buf[256]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); - error(buf); -#else - error(strerror(errno)); -#endif - } - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = connection; - UNPROTECT(1); - return result; -} - -/* - * Close a connection to an existing kdb+ process. - */ -SEXP kx_r_close_connection(SEXP connection) { - SEXP result; - /* Close the connection. */ - kclose(INTEGER_VALUE(connection)); - PROTECT(result = NEW_INTEGER(1)); - INTEGER_POINTER(result)[0] = 0; - UNPROTECT(1); - return result; -} - -/* - * Execute a kdb+ query over the given connection. - */ -SEXP kx_r_execute(SEXP connection, SEXP query) { - K result; - SEXP s; - kx_connection = INTEGER_VALUE(connection); - result = k(kx_connection, (char*) CHARACTER_VALUE(query), (K)0); - if (0 == result) - error("Error: not connected to kdb+ server\n"); - else if (-128 == result->t) { - char *e = calloc(strlen(result->s) + 1, 1); - strcpy(e, result->s); - r0(result); - error("Error from kdb+: `%s\n", e); - } - s = from_any_kobject(result); - r0(result); - return s; -} From f982d55935f7158546bb50216c1c5f7ff7c9c05b Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Wed, 22 Apr 2020 15:13:12 +0900 Subject: [PATCH 12/33] Set the first convesions of time types at the initialization --- init.q | 3 +++ 1 file changed, 3 insertions(+) diff --git a/init.q b/init.q index a4c063f..22ae8a8 100644 --- a/init.q +++ b/init.q @@ -33,3 +33,6 @@ Roff :.rk.off Rset :.rk.set setenv[`R_HOME;first @[system;@[.z.o like "w*";"call";"env"]," R RHOME";enlist""]] + +// Some time conversion from R -> q do not work without doing .rk.set first. +("kdbtimestamp_"; "kdbtimespan_"; "kdbdate_"; "kdbmonth_"; "kdbminute_"; "kdbsecond_") .rk.set' `timestamp`timespan`date`month`minute`second$/: 2000.01.01D00:00:00.000000000; \ No newline at end of file From 36f4bbbd28414201c1d2b1464cd889eef5a10e2d Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Thu, 4 Mar 2021 23:49:54 +0900 Subject: [PATCH 13/33] Fixed branching for from_any_kobject && Fixed socketpair type for Windows --- src/common_R_interface/q2r.c | 4 ++-- src/feature_for_embedR/r2q_ex.c | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/common_R_interface/q2r.c b/src/common_R_interface/q2r.c index bad4a99..c9eeed8 100644 --- a/src/common_R_interface/q2r.c +++ b/src/common_R_interface/q2r.c @@ -251,7 +251,7 @@ static SEXP from_any_kobject(K x) { result= from_int_kobject(ki(0)); else if (type <= KT) result = kdbplus_types[type](x); - else if (KTt!=-128) { result = from_any_kobject(t); @@ -260,7 +260,7 @@ static SEXP from_any_kobject(K x) { else result = error_broken_kobject(x); } - else if(77t!=-128) { result = from_any_kobject(t); diff --git a/src/feature_for_embedR/r2q_ex.c b/src/feature_for_embedR/r2q_ex.c index 7a7ae9e..4fc0dcf 100644 --- a/src/feature_for_embedR/r2q_ex.c +++ b/src/feature_for_embedR/r2q_ex.c @@ -62,7 +62,12 @@ static char * getkstring(K x) { * The public interface used from Q. */ -static I spair[2]; +#ifdef _WIN32 +static SOCKET spair[2]; +#else +static int spair[2]; +#endif + void* pingthread; V* pingmain(V* v){ From 0d8e600249205d27d1bbbb93161a60b2f2076289 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Thu, 4 Mar 2021 23:57:44 +0900 Subject: [PATCH 14/33] Modified brew related command for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1c12d91..e90dabc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: - osx before_install: - if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get install r-base; fi -- if [ $TRAVIS_OS_NAME = osx ]; then brew update;brew install gcc; brew link --overwrite gcc; brew install r; fi +- if [ $TRAVIS_OS_NAME = osx ]; then brew update; brew install gcc; brew unlink gcc && brew link gcc; brew install --build-from-source r; fi script: - make - echo "Preparing version $TRAVIS_BRANCH-$TRAVIS_COMMIT" From 010d0cc3d2c6079ed42ddca34a64cb8bf9da069d Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Fri, 5 Mar 2021 00:50:22 +0900 Subject: [PATCH 15/33] Added CMake --- .gitignore | 3 + CMakeLists.txt | 32 +++++ Makefile | 32 ----- include/.git_keep | 0 install.sh | 87 ++++++++++++ init.q => q/embedr.q | 0 src/CMakeLists.txt | 100 +++++++++++++ src/k.h | 148 -------------------- src/{feature_for_embedR => }/q2r_ex.c | 0 src/{feature_for_embedR => }/r2q_ex.c | 0 src/{feature_for_embedR => }/socketpair.c | 0 {src => windows}/w32/q.a | Bin {src => windows}/w64/q.a | Bin {win_install => windows/win_install}/w32.sh | 0 {win_install => windows/win_install}/w64.sh | 0 15 files changed, 222 insertions(+), 180 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 include/.git_keep create mode 100644 install.sh rename init.q => q/embedr.q (100%) create mode 100644 src/CMakeLists.txt delete mode 100644 src/k.h rename src/{feature_for_embedR => }/q2r_ex.c (100%) rename src/{feature_for_embedR => }/r2q_ex.c (100%) rename src/{feature_for_embedR => }/socketpair.c (100%) rename {src => windows}/w32/q.a (100%) rename {src => windows}/w64/q.a (100%) rename {win_install => windows/win_install}/w32.sh (100%) rename {win_install => windows/win_install}/w64.sh (100%) diff --git a/.gitignore b/.gitignore index bf9cabf..6c28c08 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,9 @@ *.x86_64 *.hex +# k.h +include/k.h + # Debug files *.dSYM/ *.su diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7d09ef6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +##%% General Settings %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv# + +cmake_minimum_required(VERSION 3.1) +project(embedr C) + +# Set library name +set(MY_LIBRARY_NAME embedr) + +# Add src directry +add_subdirectory(src) + +# Default option is Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +##%% Installation %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv# + +# Install script +if(WIN32) + set(INSTALL_SCRIPT "install.bat") +else() + set(INSTALL_SCRIPT "install.sh") +endif() + +# Build package always +file(COPY README.md LICENSE ${INSTALL_SCRIPT} DESTINATION ${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}) +file(COPY examples/ DESTINATION ${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}/examples/) +file(COPY q/ DESTINATION ${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}/q/) + +# Copy q files to QHOME +install(DIRECTORY q/ DESTINATION $ENV{QHOME}/ CONFIGURATIONS Release) diff --git a/Makefile b/Makefile deleted file mode 100644 index f103522..0000000 --- a/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -MS = $(shell getconf LONG_BIT) # 32/64 -CFLAGS = -g -O0 -fPIC -m$(MS) -UNAME_S := $(shell uname -s) - -ifeq ($(UNAME_S),Linux) - OSFLAG = l - CFLAGS += -shared -else ifeq ($(UNAME_S),Darwin) - OSFLAG = m - CFLAGS += -dynamiclib -undefined dynamic_lookup -endif - -QARCH = $(OSFLAG)$(MS) -Q = ${QHOME}/$(QARCH) - -R_HOME = $(shell R RHOME) -R_INCLUDES = $(shell R CMD config --cppflags) -LIBS = -lpthread -L$(R_HOME)/lib -lR - -SRC = src/embedr.c -TGT = embedr.so - -all: src/k.h - R CMD gcc -o $(TGT) $(CFLAGS) $(R_INCLUDES) $(SRC) $(LIBS) -Wall -install: - install $(TGT) $(Q) -clean: - rm -rf $(TGT) -fmt: - clang-format -style=file embedr.c src/rserver.c src/qserver.c src/common.c -i -src/k.h: - curl -s -O -L src/https://github.com/KxSystems/kdb/raw/master/c/c/k.h diff --git a/include/.git_keep b/include/.git_keep new file mode 100644 index 0000000..e69de29 diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..3efd80c --- /dev/null +++ b/install.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +## @file install.sh +## @fileoverview Install shared object to `${QHOME}/[os-bitness]` and q scripts to `${QHOME}`. + +if [ -z "$QHOME" ] +then + echo "ERROR: QHOME environment not set. Installation failed." + exit 1 +fi + +echo "Detected System" +echo "* OS: $OSTYPE" +echo "* TYPE: $HOSTTYPE" +echo "* MACHINE TYPE: $MACHTYPE" + +# DETECT OS TYPE BEING USED +Q_PATH_SEP="/" +Q_HOST_TYPE="" +if [[ "$OSTYPE" == "linux-gnu" ]]; then + Q_HOST_TYPE="l" +elif [[ "$OSTYPE" == "darwin"* ]]; then + # Mac OSX + Q_HOST_TYPE="m" +elif [[ "$OSTYPE" == "cygwin" ]]; then + # POSIX compatibility layer and Linux environment emulation for Windows + Q_HOST_TYPE="w" + Q_PATH_SEP="\\" +elif [[ "$OSTYPE" == "msys" ]]; then + # Lightweight shell and GNU utilities compiled for Windows (part of MinGW) + Q_HOST_TYPE="w" + Q_PATH_SEP="\\" +elif [[ "$OSTYPE" == "win32" ]]; then + Q_HOST_TYPE="w" + Q_PATH_SEP="\\" +elif [[ "$OSTYPE" == "freebsd"* ]]; then + Q_HOST_TYPE="l" +else + echo "ERROR: OSTYPE $OSTYPE not currently supported by this script" + echo "Please view README.md for installation instructions" + exit 1 +fi + +# DETECT WHETHER 32 OR 64 BIT +Q_MACH_TYPE="" +if [[ "$HOSTTYPE" == "x86_64" ]]; then + Q_MACH_TYPE="64" +else + Q_MACH_TYPE="32" +fi + +Q_SCRIPT_DIR=${QHOME}${Q_PATH_SEP} +Q_SHARED_LIB_DIR="${QHOME}${Q_PATH_SEP}${Q_HOST_TYPE}${Q_MACH_TYPE}${Q_PATH_SEP}" + +# check destination directory exists +if [ ! -w "$Q_SCRIPT_DIR" ]; then + echo "ERROR: Directory '$Q_SCRIPT_DIR' does not exist" + exit 1 +fi +if [ ! -w "$Q_SHARED_LIB_DIR" ]; then + echo "ERROR: Directory '$Q_SHARED_LIB_DIR' does not exist" + exit 1 +fi +if [ ! -d q ]; then + echo "ERROR: Directory 'q' does not exist. Please run from release package" + exit 1 +fi +if [ ! -d lib ]; then + echo "ERROR: Directory 'lib' does not exist. Please run from release package" + exit 1 +fi + +echo "Copying q script to $Q_SCRIPT_DIR ..." +cp q/* $Q_SCRIPT_DIR +if [ $? -ne 0 ]; then + echo "ERROR: copy failed" + exit 1 +fi +echo "Copying shared lib to $Q_SHARED_LIB_DIR ..." +cp lib/* $Q_SHARED_LIB_DIR +if [ $? -ne 0 ]; then + echo "ERROR: copy failed" + exit 1 +fi + +echo "Install complete" +exit 0 \ No newline at end of file diff --git a/init.q b/q/embedr.q similarity index 100% rename from init.q rename to q/embedr.q diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..bc3adf9 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,100 @@ +##%% Compilation %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +# Default option is Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Download k.h +file(DOWNLOAD "https://github.com/KxSystems/kdb/raw/master/c/c/k.h" "${PROJECT_SOURCE_DIR}/include/k.h" ) + +# Specify target shared library name +add_library(${MY_LIBRARY_NAME} SHARED + common_R_interface/q2r.c + common_R_interface/r2q.c + q2r_ex.c + r2q_ex.c + socketpair.c +) + +# Specify include directory +target_include_directories(${MY_LIBRARY_NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/include + $ENV{R_INSTALL_DIR}/include/ +) + +# Find dependency +find_library(R_LIBRARY + REQUIRED + NAMES libR + HINTS $ENV{R_INSTALL_DIR}/lib +) + +if(MSVC) + # q library + file(DOWNLOAD "https://github.com/KxSystems/kdb/raw/master/w64/q.lib" "${CMAKE_BINARY_DIR}/q.lib" ) + set(Q_LIBRARY "${CMAKE_BINARY_DIR}/q.lib") +endif() + +# Compile option +target_compile_options(${MY_LIBRARY_NAME} PRIVATE + # kdb+ version + -DKXVER=3 + + # Compiler option + $<$:${CMAKE_C_FLAGS} /W3 /D WIN32_LEAN_AND_MEAN> + $<$>:${CMAKE_C_FLAGS} -std=c99 -fPIC -Wno-strict-aliasing> + + # Config option + $<$,$>>:-O3 -DNDEBUG> + $<$,$>>:-O0 -g> +) + +# Shared library prefix and suffix +# ex.) embedr.so for linux +# Suffix must be `.so` for Mac +if(APPLE) + set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") +endif() + +set_target_properties(${MY_LIBRARY_NAME} PROPERTIES SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) +set_target_properties(${MY_LIBRARY_NAME} PROPERTIES PREFIX "") + +# Link flag +if(APPLE) + set_target_properties(${MY_LIBRARY_NAME} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -mmacosx-version-min=10.12") +endif() + +# Link library +target_link_libraries(${MY_LIBRARY_NAME} PRIVATE + ${Q_LIBRARY} + ${KAFKA_LIBRARY} + ${OPENSSL_LIBRARY} +) + +##%% Installation %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +# OS Specific +if(APPLE) + set(OSFLAG m) +elseif(WIN32) + set(OSFLAG w) +else() + set(OSFLAG l) +endif() + +# Check 32bit or 64bit +set(BITNESS 32) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BITNESS 64) +endif() + +# Copy built shared object after build instead of during installation +add_custom_command(TARGET ${MY_LIBRARY_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "$" ${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}/lib/${MY_LIBRARY_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + DEPENDS ${MY_LIBRARY_NAME} +) + +# Install shared object at Release to QHOME and package directory +install(TARGETS ${MY_LIBRARY_NAME} DESTINATION $ENV{QHOME}/${OSFLAG}${BITNESS}/ CONFIGURATIONS Release) \ No newline at end of file diff --git a/src/k.h b/src/k.h deleted file mode 100644 index 86e3021..0000000 --- a/src/k.h +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef KX -#define KX -typedef char*S,C;typedef unsigned char G;typedef short H;typedef int I;typedef long long J;typedef float E;typedef double F;typedef void V; -#ifdef __cplusplus -extern"C"{ -#endif -#if !defined(KXVER) -#error "Set KXVER=3 for kdb+3.0 or standalone c-api after 2011-04-20. Otherwise set KXVER=2. e.g. #define KXVER 3 or gcc -DKXVER=3" -#endif -#if KXVER>=3 -typedef struct k0{signed char m,a,t;C u;I r;union{G g;H h;I i;J j;E e;F f;S s;struct k0*k;struct{J n;G G0[1];};};}*K; -typedef struct{G g[16];}U; -#define kU(x) ((U*)kG(x)) -#define xU ((U*)xG) -extern K ku(U),ktn(I,J),kpn(S,J); -extern I setm(I); -#define DO(n,x) {J i=0,_i=(n);for(;i<_i;++i){x;}} -#else -typedef struct k0{I r;H t,u;union{G g;H h;I i;J j;E e;F f;S s;struct k0*k;struct{I n;G G0[1];};};}*K; -extern K ktn(I,I),kpn(S,I); -#define DO(n,x) {I i=0,_i=(n);for(;i<_i;++i){x;}} -#endif -#ifdef __cplusplus -} -#endif -//#include -// vector accessors, e.g. kF(x)[i] for float&datetime -#define kG(x) ((x)->G0) -#define kC(x) kG(x) -#define kH(x) ((H*)kG(x)) -#define kI(x) ((I*)kG(x)) -#define kJ(x) ((J*)kG(x)) -#define kE(x) ((E*)kG(x)) -#define kF(x) ((F*)kG(x)) -#define kS(x) ((S*)kG(x)) -#define kK(x) ((K*)kG(x)) - -// type bytes qtype ctype accessor -#define KB 1 // 1 boolean char kG -#define UU 2 // 16 guid U kU -#define KG 4 // 1 byte char kG -#define KH 5 // 2 short short kH -#define KI 6 // 4 int int kI -#define KJ 7 // 8 long long kJ -#define KE 8 // 4 real float kE -#define KF 9 // 8 float double kF -#define KC 10 // 1 char char kC -#define KS 11 // * symbol char* kS - -#define KP 12 // 8 timestamp long kJ (nanoseconds from 2000.01.01) -#define KM 13 // 4 month int kI (months from 2000.01.01) -#define KD 14 // 4 date int kI (days from 2000.01.01) - -#define KN 16 // 8 timespan long kJ (nanoseconds) -#define KU 17 // 4 minute int kI -#define KV 18 // 4 second int kI -#define KT 19 // 4 time int kI (millisecond) - -#define KZ 15 // 8 datetime double kF (DO NOT USE) - -// table,dict -#define XT 98 // x->k is XD -#define XD 99 // kK(x)[0] is keys. kK(x)[1] is values. - -#ifdef __cplusplus -extern"C"{ -extern V m9(); -#else -extern V m9(V); -#endif -extern I khpun(const S,I,const S,I),khpu(const S,I,const S),khp(const S,I),okx(K),ymd(I,I,I),dj(I);extern V r0(K),sd0(I),kclose(I);extern S sn(S,I),ss(S); -extern K ktj(I,J),ka(I),kb(I),kg(I),kh(I),ki(I),kj(J),ke(F),kf(F),kc(I),ks(S),kd(I),kz(F),kt(I),sd1(I,K(*)(I)),dl(V*f,I), - knk(I,...),kp(S),ja(K*,V*),js(K*,S),jk(K*,K),jv(K*k,K),k(I,const S,...),xT(K),xD(K,K),ktd(K),r1(K),krr(const S),orr(const S),dot(K,K),b9(I,K),d9(K); -#ifdef __cplusplus -} -#endif - -// nulls(n?) and infinities(w?) -#define nh ((I)0xFFFF8000) -#define wh ((I)0x7FFF) -#define ni ((I)0x80000000) -#define wi ((I)0x7FFFFFFF) -#define nj ((J)0x8000000000000000LL) -#define wj 0x7FFFFFFFFFFFFFFFLL -#if defined(WIN32) || defined(_WIN32) -#define nf (log(-1.0)) -#define wf (-log(0.0)) -#if !defined(isnan) -#define isnan _isnan -#endif -#define finite _finite -extern double log(double); -#else -#define nf (0/0.0) -#define wf (1/0.0) -#define closesocket(x) close(x) -#endif - -// remove more clutter -#define O printf -#define R return -#define Z static -#define P(x,y) {if(x)R(y);} -#define U(x) P(!(x),0) -#define SW switch -#define CS(n,x) case n:x;break; -#define CD default - -#define ZV Z V -#define ZK Z K -#define ZH Z H -#define ZI Z I -#define ZJ Z J -#define ZE Z E -#define ZF Z F -#define ZC Z C -#define ZS Z S - -#define K1(f) K f(K x) -#define K2(f) K f(K x,K y) -#define TX(T,x) (*(T*)((G*)(x)+8)) -#define xr x->r -#define xt x->t -#define xu x->u -#define xn x->n -#define xx xK[0] -#define xy xK[1] -#define xg TX(G,x) -#define xh TX(H,x) -#define xi TX(I,x) -#define xj TX(J,x) -#define xe TX(E,x) -#define xf TX(F,x) -#define xs TX(S,x) -#define xk TX(K,x) -#define xG x->G0 -#define xH ((H*)xG) -#define xI ((I*)xG) -#define xJ ((J*)xG) -#define xE ((E*)xG) -#define xF ((F*)xG) -#define xS ((S*)xG) -#define xK ((K*)xG) -#define xC xG -#define xB ((G*)xG) - -#endif - diff --git a/src/feature_for_embedR/q2r_ex.c b/src/q2r_ex.c similarity index 100% rename from src/feature_for_embedR/q2r_ex.c rename to src/q2r_ex.c diff --git a/src/feature_for_embedR/r2q_ex.c b/src/r2q_ex.c similarity index 100% rename from src/feature_for_embedR/r2q_ex.c rename to src/r2q_ex.c diff --git a/src/feature_for_embedR/socketpair.c b/src/socketpair.c similarity index 100% rename from src/feature_for_embedR/socketpair.c rename to src/socketpair.c diff --git a/src/w32/q.a b/windows/w32/q.a similarity index 100% rename from src/w32/q.a rename to windows/w32/q.a diff --git a/src/w64/q.a b/windows/w64/q.a similarity index 100% rename from src/w64/q.a rename to windows/w64/q.a diff --git a/win_install/w32.sh b/windows/win_install/w32.sh similarity index 100% rename from win_install/w32.sh rename to windows/win_install/w32.sh diff --git a/win_install/w64.sh b/windows/win_install/w64.sh similarity index 100% rename from win_install/w64.sh rename to windows/win_install/w64.sh From 251ce1a0a1527e4d04b86396f6be3ddf03fa1ca9 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Fri, 5 Mar 2021 07:03:33 +0900 Subject: [PATCH 16/33] Restructured to reflect visibility of functions properly && Checked Linux build --- .gitignore | 3 + .travis.yml | 97 +++++++++-- README.md | 26 ++- include/.git_keep | 0 include/common_R_interface/common.h | 100 ++++++++++++ include/embedr.h | 21 +++ include/socketpair.h | 40 +++++ src/CMakeLists.txt | 13 +- src/common_R_interface/common.c | 83 ++++++++++ src/common_R_interface/q2r.c | 242 ++++++++++++++-------------- src/common_R_interface/r2q.c | 194 +++++++++++----------- src/embedr.c | 41 ----- src/{q2r_ex.c => embedr_q2r.c} | 11 ++ src/{r2q_ex.c => embedr_r2q.c} | 26 ++- src/socketpair.c | 14 +- tests/test.q | 51 +++--- 16 files changed, 628 insertions(+), 334 deletions(-) delete mode 100644 include/.git_keep create mode 100644 include/common_R_interface/common.h create mode 100644 include/embedr.h create mode 100644 include/socketpair.h create mode 100644 src/common_R_interface/common.c delete mode 100644 src/embedr.c rename src/{q2r_ex.c => embedr_q2r.c} (78%) rename src/{r2q_ex.c => embedr_r2q.c} (84%) diff --git a/.gitignore b/.gitignore index 6c28c08..feeb709 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,9 @@ # k.h include/k.h +# Package +build/ + # Debug files *.dSYM/ *.su diff --git a/.travis.yml b/.travis.yml index e90dabc..2d44676 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,91 @@ +jobs: + include: + - dist: xenial + os: linux + - dist: bionic + os: linux + - os: osx +os: linux +dist: xenial language: c -os: -- linux -- osx +compiler: gcc + before_install: -- if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get install r-base; fi -- if [ $TRAVIS_OS_NAME = osx ]; then brew update; brew install gcc; brew unlink gcc && brew link gcc; brew install --build-from-source r; fi + # Set global flag + - export TESTS="True" + + # Install R + - if [[ $TRAVIS_OS_NAME == linux ]]; then + sudo apt-get install r-base; + export R_INSTALL_DIR=/usr/lib64/R + export LD_LIBRARY_PATH=${R_INSTALL_DIR}:${LD_LIBRARY_PATH}; + elif [[ $TRAVIS_OS_NAME == osx ]]; then + export HOMEBREW_AUTO_UPDATING=0; + brew install --build-from-source r; + export R_INSTALL_DIR=/usr/local/Cellar/R + export DYLD_LIBRARY_PATH=${R_INSTALL_DIR}:${LD_LIBRARY_PATH}; + fi + + # Install q and set QHOME + - if [[ $TRAVIS_OS_NAME == "linux" ]]; then + QLIBDIR=l64; OD=$L64; + elif [[ $TRAVIS_OS_NAME == "osx" ]]; then + QLIBDIR=m64; OD=$M64; + elif [[ $TRAVIS_OS_NAME == "windows" ]]; then + QLIBDIR=w64; OD=$W64; + else + echo "unknown OS ('$TRAVIS_OS_NAME')" >&2; exit 1; + fi + - export QLIBDIR + - mkdir qhome; + - export QHOME=$(pwd)/qhome; + - export PATH=$QHOME/$QLIBDIR:$PATH; + + # Set up q for testing and execute tests on multiple + - if [[ $TESTS == "True" && "x$OD" != "x" && "x$QLIC_KC" != "x" ]]; then + export PATH=$R_INSTALL_DIR/lib:$PATH; + curl -o ${QHOME}/q.zip -L $OD; + unzip -d ${QHOME} ${QHOME}/q.zip; + rm ${QHOME}/q.zip; + echo -n $QLIC_KC |base64 --decode > ${QHOME}/kc.lic; + else + echo "No kdb+, no tests"; + fi + + # Set package name + - if [[ $TRAVIS_OS_NAME == "windows" ]]; then + export FILE_TAIL="zip"; + else + export FILE_TAIL="tgz"; + fi + - export FILE_NAME=$FILE_ROOT-$TRAVIS_OS_NAME-$ARCH-$TRAVIS_BRANCH.$FILE_TAIL + + # Build package + - mkdir build + - cd build + - if [[ $TRAVIS_OS_NAME == windows ]]; then + cmake --config Release ..; + cmake --config Release --build . --target install; + else + cmake ..; + cmake --build . --target install; + fi + - cd .. + script: -- make -- echo "Preparing version $TRAVIS_BRANCH-$TRAVIS_COMMIT" -- mkdir embedr; cp -r init.q test.q l32 l64 m32 m64 embedr/ || true; -- echo "$TRAVIS_BRANCH-$TRAVIS_COMMIT" > embedr/VERSION.embedr; -- cp LICENSE embedr/LICENSE.embedr; cp README.md embedr/README.embedr; -- tar czvf embedr_$TRAVIS_OS_NAME-$TRAVIS_BRANCH.tar.gz embedr -- echo "Packaged as embedr_$TRAVIS_OS_NAME-$TRAVIS_BRANCH.tar.gz" + - if [[ $TESTS == "True" && "x$OD" != "x" && "x$QLIC_KC" != "x" ]]; then + q tests/test.q; + fi + - if [[ $TRAVIS_OS_NAME == "windows" && $BUILD == "True" ]]; then + 7z a -tzip -r $FILE_NAME ./build/$FILE_ROOT/*; + elif [[ $BUILD == "True" && ( $TRAVIS_OS_NAME == "linux" || $TRAVIS_OS_NAME == "osx" ) ]]; then + tar -zcvf $FILE_NAME -C build/$FILE_ROOT .; + elif [[ $TRAVIS_OS_NAME == "windows" ]]; then + 7z a -tzip $FILE_NAME README.md install.bat install32.bat LICENSE q examples; + elif [[ $TRAVIS_OS_NAME == "linux" || $TRAVIS_OS_NAME == "osx" ]]; then + tar -zcvf $FILE_NAME README.md install.sh LICENSE q examples; + fi + deploy: provider: releases api_key: diff --git a/README.md b/README.md index f817e47..a558097 100644 --- a/README.md +++ b/README.md @@ -4,32 +4,28 @@ See ## Installation -### Download +### Download Pre-built Binary + Download the appropriate release archive from [releases](../../releases/latest) page. -#### Unpack and install content of the archive +#### Unpack and install content of the archive environment | action ----------------|--------------------------------------------------------------------------------------- Linux | `tar xzvf embedr_linux-v*.tar.gz -C $QHOME --strip 1` macOS | `tar xzvf embedr_osx-v*.tar.gz -C $QHOME --strip 1` -Windows | Open the archive and copy content of the `embedr` folder (`embedr\*`) to `%QHOME%` or `c:\q`
Copy R_HOME/x64/*.dll or R_HOME/i386/*.dll to QHOME/w64 or QHOME/w32 respectively. +Windows | Open the archive and copy content of the `embedr` folder (`embedr\*`) to `%QHOME%` or `c:\q`
Copy R_HOME/x64/*.dll or R_HOME/i386/*.dll to QHOME/w64 or QHOME/w32 respectively. -### Building -For Linux/MacOS it is possible to build the library from source using the makefile provided. From the root of the directory: +### Install from Source -```bash -// Create the `.so` binary -$ make +For Linux/MacOS it is possible to build the library from source using the CMake file provided. You need to set a directory which includes `lib` and `include` directories to an environmental variable `R_INSTALL_DIR`. Then from the root of the directory: -// Install into $QHOME -$ make install +```bash -// Remove the binary from current directory -$ make clean +embedR]$ mkdir build && cd build +build]$ cmake .. +build]$ cmake --build . --target install -// Or in a single command -$ make && make install && make clean ``` ## Calling R @@ -37,10 +33,12 @@ $ make && make install && make clean When calling R, you need to set `R_HOME`. This can be set as follows: ```bash + # Linux/macOS export R_HOME=`R RHOME` # Windows for /f "delims=" %a in ('R RHOME') do @set R_HOME=%a + ``` The library has four main methods: diff --git a/include/.git_keep b/include/.git_keep deleted file mode 100644 index e69de29..0000000 diff --git a/include/common_R_interface/common.h b/include/common_R_interface/common.h new file mode 100644 index 0000000..039e26f --- /dev/null +++ b/include/common_R_interface/common.h @@ -0,0 +1,100 @@ +/** + * Common headers for integration with R. + */ + +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include "k.h" + +/*-----------------------------------------------*/ +/* Macro */ +/*-----------------------------------------------*/ + +#define INT64(x) ((J*) REAL(x)) + +/*-----------------------------------------------*/ +/* Global Variable */ +/*-----------------------------------------------*/ + +// Offsets used in conversion between R and q +extern const J epoch_offset; +// Seconds in a day +extern const int sec2day; +// Days between 1970.01.01 & 2000.01.01 +extern const int kdbDateOffset; +// Seconds between 1970.01.01 & 2000.01.01 +extern const int kdbSecOffset; + +/** + * @brief Attribute set to q time related types when it is converted to R object. + */ +SEXP R_UnitsSymbol; +/** + * @brief Attribute set to q time related types when it is converted to R object. + */ +SEXP R_TzSymbol; + +/*-----------------------------------------------*/ +/* Functions */ +/*-----------------------------------------------*/ + +// Utility //-------------------------------------/ + +/** + * @brief Check if it is a leap year. + */ +bool is_leap(const int year); + +/** + * @brief Functions to derive day count since kdb+ epoch from month count + */ +int months2days(const int monthcount); + +/** + * @brief Functions to derive month count since kdb epoch from day count + */ +int days2months(const int daycount); + +// R -> q //--------------------------------------/ + +/** + * @brief Entry point of converting R object to q object. + */ +K from_any_robject(SEXP sxp); + +/** + * @brief Convert R pair-list object to q dictionary object. + * @note + * The definition varies between embedR and rkdb. + */ +K from_pairlist_robject(SEXP sxp); + +/** + * @brief add attribute if any. + **/ +K attR(K x,SEXP sxp); + +// q -> R //--------------------------------------/ + +/** + * @brief Entry point of converting q object to R object. + */ +SEXP from_any_kobject(K x); + +/** + * @brief Convert R string to q object. + */ +SEXP from_string_kobject(K); + +/** + * @brief Convert R symbol object to q object. + */ +SEXP from_symbol_kobject(K); diff --git a/include/embedr.h b/include/embedr.h new file mode 100644 index 0000000..75e764c --- /dev/null +++ b/include/embedr.h @@ -0,0 +1,21 @@ +/* + * This library provides an R server for Q + */ + +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include +#include +#include +#ifndef WIN32 +#include +#include +#include +#include +#else +#include +#include +#endif +#include diff --git a/include/socketpair.h b/include/socketpair.h new file mode 100644 index 0000000..536fc32 --- /dev/null +++ b/include/socketpair.h @@ -0,0 +1,40 @@ +/* + * socketpair.h + * Header file of socketpair.c + * Created to ensure defining functions only once. + */ + +#ifndef __SOCKETPAIR_H__ +#define __SOCKETPAIR_H__ + +#include + +#ifdef _WIN32 + +# include +# include +# include +# include +#else +# include +# include + +#endif + +#ifdef _WIN32 + +/** + * @brief If make_overlapped is nonzero, both sockets created will be usable for + * "overlapped" operations via WSASend etc. If make_overlapped is zero, + * socks[0] (only) will be usable with regular ReadFile etc., and thus + * suitable for use as stdin or stdout of a child process. Note that the + * sockets must be closed with closesocket() regardless. + */ +int dumb_socketpair(SOCKET socks[2], int make_overlapped); + +#else +int dumb_socketpair(int socks[2], int dummy); +#endif + +// __SOCKETPAIR_H__ +#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc3adf9..14328ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,23 +10,25 @@ file(DOWNLOAD "https://github.com/KxSystems/kdb/raw/master/c/c/k.h" "${PROJECT_S # Specify target shared library name add_library(${MY_LIBRARY_NAME} SHARED + common_R_interface/common.c common_R_interface/q2r.c common_R_interface/r2q.c - q2r_ex.c - r2q_ex.c + embedr_q2r.c + embedr_r2q.c socketpair.c ) # Specify include directory target_include_directories(${MY_LIBRARY_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/include/common_R_interface $ENV{R_INSTALL_DIR}/include/ ) # Find dependency find_library(R_LIBRARY REQUIRED - NAMES libR + NAMES libR R HINTS $ENV{R_INSTALL_DIR}/lib ) @@ -43,7 +45,7 @@ target_compile_options(${MY_LIBRARY_NAME} PRIVATE # Compiler option $<$:${CMAKE_C_FLAGS} /W3 /D WIN32_LEAN_AND_MEAN> - $<$>:${CMAKE_C_FLAGS} -std=c99 -fPIC -Wno-strict-aliasing> + $<$>:${CMAKE_C_FLAGS} -fPIC -Wno-strict-aliasing> # Config option $<$,$>>:-O3 -DNDEBUG> @@ -68,8 +70,7 @@ endif() # Link library target_link_libraries(${MY_LIBRARY_NAME} PRIVATE ${Q_LIBRARY} - ${KAFKA_LIBRARY} - ${OPENSSL_LIBRARY} + ${R_LIBRARY} ) ##%% Installation %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ diff --git a/src/common_R_interface/common.c b/src/common_R_interface/common.c new file mode 100644 index 0000000..2a73bcb --- /dev/null +++ b/src/common_R_interface/common.c @@ -0,0 +1,83 @@ +/** + * Common source for R integration with kdb+. Define global variables. + */ + +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include "common.h" + +/*-----------------------------------------------*/ +/* Global Variable */ +/*-----------------------------------------------*/ + +// Offsets used in conversion between R and q +const J epoch_offset=10957*24*60*60*1000000000LL; +// Seconds in a day +const int sec2day = 86400; +// Days between 1970.01.01 & 2000.01.01 +const int kdbDateOffset = 10957; +// Seconds between 1970.01.01 & 2000.01.01 +const int kdbSecOffset = 946684800; + +SEXP R_UnitsSymbol=NULL; +SEXP R_TzSymbol=NULL; + +/*-----------------------------------------------*/ +/* Utility Functions */ +/*-----------------------------------------------*/ + +/** + * @brief Check if it is a leap year. + */ +bool is_leap(const int year){ + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); +} + +/** + * @brief Functions to derive day count since kdb+ epoch from month count + */ +int months2days(const int monthcount){ + int days=0; + const int mdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + const int years = monthcount / 12; + for(int i= 0; i < years; i++) + days+=is_leap(2000+i)?366:365; + const int this_year = 2000+years; + const int months= monthcount % 12; + for(int i=0; i < months; i++){ + if(i == 1) + days+=is_leap(this_year)?29:28; + else + days+=mdays[i]; + } + return days; +} + +/** + * @brief Functions to derive month count since kdb epoch from day count + */ +int days2months(const int daycount){ + int year=2000, months=0, days=0; + const int mdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + while(true){ + if(daycount < days+(is_leap(year)?366:365)) + break; + days+=is_leap(year)?366:365; + months+=12; + year++; + } + for(int i= 0; i < 12; i++){ + if(days < daycount){ + if(i==1) + days+=is_leap(year)?29:28; + else + days+=mdays[i]; + months+=1; + } + else + break; + } + return months; +} diff --git a/src/common_R_interface/q2r.c b/src/common_R_interface/q2r.c index c9eeed8..d49c9cd 100644 --- a/src/common_R_interface/q2r.c +++ b/src/common_R_interface/q2r.c @@ -9,8 +9,23 @@ * https://cran.r-project.org/doc/manuals/r-release/R-exts.html */ +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include "common.h" +//#include "q2r.h" + +/*-----------------------------------------------*/ +/* Global Variable */ +/*-----------------------------------------------*/ + int kx_connection=0; +/*-----------------------------------------------*/ +/* Structs */ +/*-----------------------------------------------*/ + /* * A (readable type name, R data type number) pair. */ @@ -48,6 +63,65 @@ const struct data_types r_data_types[] = { {0, -1} }; +/*-----------------------------------------------*/ +/* Predefinition of Functions */ +/*-----------------------------------------------*/ + +/** + *@brief Function used in the conversion of kdb guid to R char array + */ +static K guid_2_char(K x); + +/* + * We have functions that turn any K object into the appropriate R SEXP. + */ +SEXP from_any_kobject(K x); +static SEXP from_bool_kobject(K); +static SEXP from_byte_kobject(K); +static SEXP from_guid_kobject(K); +static SEXP from_string_column_kobject(K); +static SEXP from_short_kobject(K); +static SEXP from_int_kobject(K); +static SEXP from_long_kobject(K); +static SEXP from_real_kobject(K); +static SEXP from_float_kobject(K); +SEXP from_string_kobject(K); +SEXP from_symbol_kobject(K); +static SEXP from_timestamp_kobject(K); +static SEXP from_month_kobject(K); +static SEXP from_date_kobject(K); +static SEXP from_datetime_kobject(K); +static SEXP from_timespan_kobject(K); +static SEXP from_minute_kobject(K); +static SEXP from_second_kobject(K); +static SEXP from_time_kobject(K); +static SEXP from_columns_kobject(K); +static SEXP from_table_kobject(K); +static SEXP from_dictionary_kobject(K); +static SEXP from_columns_kobject(K x); +static SEXP error_broken_kobject(K broken); +static SEXP from_list_of_kobjects(K x); + +/* + * An array of functions that deal with kdbplus data types. Note that the order + * is very important as we index it based on the kdb+ type number in the K object. + */ +typedef SEXP(*conversion_function)(K); + +conversion_function kdbplus_types[] = { + from_list_of_kobjects, from_bool_kobject, from_guid_kobject, + error_broken_kobject, from_byte_kobject, from_short_kobject, + from_int_kobject, from_long_kobject, from_real_kobject, + from_float_kobject, from_string_kobject, from_symbol_kobject, + from_timestamp_kobject, from_month_kobject, from_date_kobject, + from_datetime_kobject, from_timespan_kobject, from_minute_kobject, + from_second_kobject, from_time_kobject +}; + +/*-----------------------------------------------*/ +/* Functions */ +/*-----------------------------------------------*/ + /* * Brute force search of R type table. * eg. get_type_name(LISTSXP) @@ -60,8 +134,8 @@ char* get_type_name(Sint type) { return r_data_types[0].name; } -/* - * Make a data.frame from a named list by adding row.names, and class +/** + *@brief Make a data.frame from a named list by adding row.names, and class * attribute. Uses "1", "2", .. as row.names. */ void make_data_frame(SEXP data) { @@ -102,9 +176,6 @@ static SEXP settimestampclass(SEXP sxp) { return asS4(sxp,TRUE,0); } -static SEXP R_UnitsSymbol = NULL; -static SEXP R_TzSymbol = NULL; - /* for timespan, minute, second */ //Available units: "secs", "mins", "hours", "days", "weeks" static SEXP setdifftimeclass(SEXP sxp, char* units) { @@ -160,85 +231,10 @@ static SEXP settimespanclass(SEXP sxp) { return sxp; } -/* - * We have functions that turn any K object into the appropriate R SEXP. - */ -static SEXP from_any_kobject(K object); -static SEXP error_broken_kobject(K); -static SEXP from_list_of_kobjects(K); -static SEXP from_bool_kobject(K); -static SEXP from_byte_kobject(K); -static SEXP from_guid_kobject(K); -static SEXP from_string_kobject(K); -static SEXP from_string_column_kobject(K); -static SEXP from_short_kobject(K); -static SEXP from_int_kobject(K); -static SEXP from_long_kobject(K); -static SEXP from_float_kobject(K); -static SEXP from_double_kobject(K); -static SEXP from_symbol_kobject(K); -static SEXP from_month_kobject(K); -static SEXP from_date_kobject(K); -static SEXP from_datetime_kobject(K); -static SEXP from_minute_kobject(K); -static SEXP from_second_kobject(K); -static SEXP from_time_kobject(K); -static SEXP from_timespan_kobject(K); -static SEXP from_timestamp_kobject(K); -static SEXP from_columns_kobject(K); -static SEXP from_dictionary_kobject(K); -static SEXP from_table_kobject(K); - -/* - * Function used in the conversion of kdb guid to R char array - */ -static K guid_2_char(K); - -/* - * Functions to derive day count since kdb+ epoch from month count - */ - -bool is_leap(const int year){ - return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); -} - -int months2days(const int monthcount){ - int days=0; - const int mdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - const int years = monthcount / 12; - for(int i= 0; i < years; i++) - days+=is_leap(2000+i)?366:365; - const int this_year = 2000+years; - const int months= monthcount % 12; - for(int i=0; i < months; i++){ - if(i == 1) - days+=is_leap(this_year)?29:28; - else - days+=mdays[i]; - } - return days; -} - -/* - * An array of functions that deal with kdbplus data types. Note that the order - * is very important as we index it based on the kdb+ type number in the K object. - */ -typedef SEXP(*conversion_function)(K); - -conversion_function kdbplus_types[] = { - from_list_of_kobjects, from_bool_kobject, from_guid_kobject, - error_broken_kobject, from_byte_kobject, from_short_kobject, - from_int_kobject, from_long_kobject, from_float_kobject, - from_double_kobject, from_string_kobject, from_symbol_kobject, - from_timestamp_kobject, from_month_kobject, from_date_kobject, - from_datetime_kobject, from_timespan_kobject, from_minute_kobject, - from_second_kobject, from_time_kobject -}; - /* * Convert K object to R object */ -static SEXP from_any_kobject(K x) { +SEXP from_any_kobject(K x) { SEXP result; int type = abs(x->t); if (XT == type) @@ -411,7 +407,7 @@ static SEXP from_long_kobject(K x) { return result; } -static SEXP from_float_kobject(K x) { +static SEXP from_real_kobject(K x) { SEXP result; if(scalar(x)) return ScalarReal(ISNAN(x->e)?R_NaN:x->e); PROTECT(result= allocVector(REALSXP,x->n)); @@ -421,7 +417,7 @@ static SEXP from_float_kobject(K x) { return result; } -static SEXP from_double_kobject(K x) { +static SEXP from_float_kobject(K x) { SEXP result; if(scalar(x)) return ScalarReal(ISNAN(x->f)?R_NaN:x->f); PROTECT(result= allocVector(REALSXP,x->n)); @@ -431,7 +427,7 @@ static SEXP from_double_kobject(K x) { return result; } -static SEXP from_string_kobject(K x) { +SEXP from_string_kobject(K x) { SEXP result; long n=scalar(x)?1:x->n; PROTECT(result= allocVector(STRSXP,1)); @@ -453,7 +449,7 @@ static SEXP from_string_column_kobject(K x) { return result; } -static SEXP from_symbol_kobject(K x) { +SEXP from_symbol_kobject(K x) { SEXP result; if(scalar(x)) return mkString(x->s); PROTECT(result= allocVector(STRSXP,x->n)); @@ -463,6 +459,17 @@ static SEXP from_symbol_kobject(K x) { return result; } +static SEXP from_timestamp_kobject(K x) { + SEXP result=from_long_kobject(x); + long n=XLENGTH(result); + PROTECT(result); + for(int i= 0; i < n; i++) + if(INT64(result)[i]!=nj)INT64(result)[i]+=epoch_offset; + settimestampclass(result); + UNPROTECT(1); + return result; +} + static SEXP from_month_kobject(K x) { SEXP result=PROTECT(from_int_kobject(x)); for(J i= 0; i < XLENGTH(result); i++) @@ -484,7 +491,7 @@ static SEXP from_date_kobject(K x) { } static SEXP from_datetime_kobject(K x) { - SEXP result=PROTECT(from_double_kobject(x)); + SEXP result=PROTECT(from_float_kobject(x)); for(int i= 0; i < XLENGTH(result); i++) REAL(result)[i]= REAL(result)[i]* sec2day + kdbDateOffset * sec2day; setdatetimeclass(result); @@ -493,24 +500,6 @@ static SEXP from_datetime_kobject(K x) { return result; } -static SEXP from_minute_kobject(K x) { - SEXP result=PROTECT(from_int_kobject(x)); - setdifftimeclass(result,"mins"); - UNPROTECT(1); - return result; -} - -static SEXP from_second_kobject(K x) { - SEXP result=PROTECT(from_int_kobject(x)); - setdifftimeclass(result,"secs"); - UNPROTECT(1); - return result; -} - -static SEXP from_time_kobject(K x) { - return from_int_kobject(x); -} - static SEXP from_timespan_kobject(K x) { SEXP result=from_long_kobject(x); J i,n=XLENGTH(result); @@ -540,14 +529,31 @@ static SEXP from_timespan_kobject(K x) { } } -static SEXP from_timestamp_kobject(K x) { - SEXP result=from_long_kobject(x); - long n=XLENGTH(result); - PROTECT(result); - for(int i= 0; i < n; i++) - if(INT64(result)[i]!=nj)INT64(result)[i]+=epoch_offset; - settimestampclass(result); - UNPROTECT(1); +static SEXP from_minute_kobject(K x) { + SEXP result=PROTECT(from_int_kobject(x)); + setdifftimeclass(result,"mins"); + UNPROTECT(1); + return result; +} + +static SEXP from_second_kobject(K x) { + SEXP result=PROTECT(from_int_kobject(x)); + setdifftimeclass(result,"secs"); + UNPROTECT(1); + return result; +} + +static SEXP from_time_kobject(K x) { + return from_int_kobject(x); +} + +static SEXP from_table_kobject(K x) { + SEXP names, result; + PROTECT(names = from_any_kobject(kK(x->k)[0])); + PROTECT(result = from_columns_kobject(kK(x->k)[1])); + setAttrib(result, R_NamesSymbol, names); + UNPROTECT(2); + make_data_frame(result); return result; } @@ -573,16 +579,6 @@ static SEXP from_dictionary_kobject(K x) { return result; } -static SEXP from_table_kobject(K x) { - SEXP names, result; - PROTECT(names = from_any_kobject(kK(x->k)[0])); - PROTECT(result = from_columns_kobject(kK(x->k)[1])); - setAttrib(result, R_NamesSymbol, names); - UNPROTECT(2); - make_data_frame(result); - return result; -} - /* * Util function */ @@ -593,4 +589,4 @@ static K guid_2_char(K x){ sprintf(kC(y),"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",gv[ 0],gv[ 1],gv[ 2],gv[ 3],gv[ 4],gv[ 5],gv[ 6],gv[ 7],gv[ 8],gv[ 9],gv[10],gv[11],gv[12],gv[13],gv[14],gv[15]); y->n= 36; return(y); -} \ No newline at end of file +} diff --git a/src/common_R_interface/r2q.c b/src/common_R_interface/r2q.c index 1b33293..62916a2 100644 --- a/src/common_R_interface/r2q.c +++ b/src/common_R_interface/r2q.c @@ -9,72 +9,52 @@ * https://cran.r-project.org/doc/manuals/r-release/R-exts.html */ -ZK klogicv(J len, int *val); -ZK klogica(J len, int rank, int *shape, int *val); -ZK kintv(J len, int *val); -ZK kinta(J len, int rank, int *shape, int *val); -ZK klonga(J len, int rank, int *shape, J*val); -ZK kdoublev(J len, double *val); -ZK kdoublea(J len, int rank, int *shape, double *val); -ZK atom_value_dict(J len, K v, SEXP keys); +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include "common.h" + +/*-----------------------------------------------*/ +/* Predefinition of Functions */ +/*-----------------------------------------------*/ /* - * convert R SEXP into K object. + * Conversion utility. */ -ZK from_any_robject(SEXP); -ZK error_broken_robject(SEXP); -ZK from_null_robject(SEXP); -ZK from_symbol_robject(SEXP); -ZK from_pairlist_robject(SEXP); -ZK from_closure_robject(SEXP); -ZK from_language_robject(SEXP); -ZK from_date_robject(SEXP); -ZK from_datetime_robject(SEXP); -ZK from_datetime_ct_robject(SEXP); -ZK from_datetime_lt_robject(SEXP); -ZK from_difftime_robject(SEXP); -ZK from_second_or_minute_robject(SEXP); -ZK from_days_robject(SEXP); -ZK from_char_robject(SEXP); -ZK from_logical_robject(SEXP); -ZK from_integer_robject(SEXP); -ZK from_double_robject(SEXP); -ZK from_character_robject(SEXP); -ZK from_vector_robject(SEXP); -ZK from_raw_robject(SEXP); -ZK from_nyi_robject(SEXP); -ZK from_frame_robject(SEXP); -ZK from_factor_robject(SEXP); +static K klogicv(J len, int *val); +static K klogica(J len, int rank, int *shape, int *val); +static K kintv(J len, int *val); +static K kinta(J len, int rank, int *shape, int *val); +static K klonga(J len, int rank, int *shape, J*val); +static K kdoublev(J len, double *val); +static K kdoublea(J len, int rank, int *shape, double *val); /* - * Functions to derive month count since kdb epoch from day count + * convert R SEXP into K object. */ +static K error_broken_robject(SEXP); +static K from_logical_robject(SEXP); +static K from_integer_robject(SEXP); +static K from_double_robject(SEXP); +static K from_char_robject(SEXP); +static K from_symbol_robject(SEXP); +static K from_date_robject(SEXP); +static K from_datetime_robject(SEXP); +static K from_difftime_robject(SEXP); +static K from_null_robject(SEXP); +static K from_character_robject(SEXP); +static K from_vector_robject(SEXP); +static K from_raw_robject(SEXP); +static K from_nyi_robject(SEXP); +static K from_frame_robject(SEXP); +static K from_factor_robject(SEXP); +static K from_closure_robject(SEXP); +static K from_language_robject(SEXP); -extern bool is_leap(const int year); - -int days2months(const int daycount){ - int year=2000, months=0, days=0; - const int mdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - while(true){ - if(daycount < days+(is_leap(year)?366:365)) - break; - days+=is_leap(year)?366:365; - months+=12; - year++; - } - for(int i= 0; i < 12; i++){ - if(days < daycount){ - if(i==1) - days+=is_leap(year)?29:28; - else - days+=mdays[i]; - months+=1; - } - else - break; - } - return months; -} +/*-----------------------------------------------*/ +/* Functions */ +/*-----------------------------------------------*/ /* * Utility functions to identify class and unit @@ -104,24 +84,29 @@ Rboolean isUnit(const char *units_, SEXP s){ * Utility functions to handle attribute */ -//extern ZK from_pairlist_robject(SEXP sxp); - -/* add attribute */ -ZK addattR (K x,SEXP att) { +/** + * @brief add attribute + **/ +static K addattR (K x,SEXP att) { // attrs are pairlists: LISTSXP K u = from_pairlist_robject(att); return knk(2,u,x); } -/* add attribute if any */ -ZK attR(K x,SEXP sxp) { +/** + * @brief add attribute if any + **/ +K attR(K x,SEXP sxp) { SEXP att = ATTRIB(sxp); if (isNull(att)) return x; return addattR(x,att); } -ZK atom_value_dict(J len, K v, SEXP keys){ +/** + * @brief Build atom value dictionary. + */ +static K atom_value_dict(J len, K v, SEXP keys){ K k= ktn(KS, len); for(J i= 0; i < len; i++) { const char *keyName= CHAR(STRING_ELT(keys, i)); @@ -131,10 +116,10 @@ ZK atom_value_dict(J len, K v, SEXP keys){ } /* - * Conversopn from R to Q + * Conversion from R to Q */ - ZK from_any_robject(SEXP sxp){ +K from_any_robject(SEXP sxp){ if(isClass("data.frame", sxp)) return from_frame_robject(sxp); if(isClass("factor", sxp)) @@ -209,15 +194,15 @@ ZK atom_value_dict(J len, K v, SEXP keys){ return result; } -ZK error_broken_robject(SEXP sxp) { +static K error_broken_robject(SEXP sxp) { return krr("Broken R object."); } -ZK from_nyi_robject(SEXP sxp){ +static K from_nyi_robject(SEXP sxp){ return attR(kp((S)Rf_type2char(TYPEOF(sxp))),sxp); } -ZK from_frame_robject(SEXP sxp) { +static K from_frame_robject(SEXP sxp) { J length= XLENGTH(sxp); if(length == 0) return from_null_robject(sxp); @@ -232,7 +217,7 @@ ZK from_frame_robject(SEXP sxp) { return tbl; } -ZK from_factor_robject(SEXP sxp) { +static K from_factor_robject(SEXP sxp) { J length= XLENGTH(sxp); SEXP levels= asCharacterFactor(sxp); K x= ktn(KS, length); @@ -243,14 +228,14 @@ ZK from_factor_robject(SEXP sxp) { return x; } -ZK from_raw_robject(SEXP sxp) { +static K from_raw_robject(SEXP sxp) { K x = ktn(KG,XLENGTH(sxp)); DO(xn,kG(x)[i]=RAW(sxp)[i]) return x; } -ZK from_date_robject(SEXP sxp) { +static K from_date_robject(SEXP sxp) { K x; J length= XLENGTH(sxp); x= ktn(isClass("month", sxp)?KM:KD,length); @@ -275,7 +260,7 @@ ZK from_date_robject(SEXP sxp) { return x; } -ZK from_datetime_ct_robject(SEXP sxp) { +static K from_datetime_ct_robject(SEXP sxp) { K x; J length = XLENGTH(sxp); x = ktn(KZ,length); @@ -283,7 +268,7 @@ ZK from_datetime_ct_robject(SEXP sxp) { return x; } -ZK from_datetime_lt_robject(SEXP sxp) { +static K from_datetime_lt_robject(SEXP sxp) { K x; J i, key_length= XLENGTH(sxp); x= ktn(0, key_length); @@ -309,14 +294,14 @@ ZK from_datetime_lt_robject(SEXP sxp) { } //Wraper function of POSIXt -ZK from_datetime_robject(SEXP sxp) { +static K from_datetime_robject(SEXP sxp) { if(isClass("POSIXct", sxp)) return from_datetime_ct_robject(sxp); else return from_datetime_lt_robject(sxp); } -ZK from_second_or_minute_robject(SEXP sxp){ +static K from_second_or_minute_robject(SEXP sxp){ K x; J length=XLENGTH(sxp); x=ktn(isUnit("secs", sxp)?KV:KU, length); @@ -331,7 +316,8 @@ ZK from_second_or_minute_robject(SEXP sxp){ return x; } -ZK from_days_robject(SEXP sxp){ +static K from_days_robject(SEXP sxp){ + printf("HOOO\n"); K x; J length= XLENGTH(sxp); x= ktn(KN,length); @@ -340,36 +326,42 @@ ZK from_days_robject(SEXP sxp){ } /* Wrapper function of difftime */ -ZK from_difftime_robject(SEXP sxp){ - if(isUnit("secs", sxp) || isUnit("mins", sxp)) +static K from_difftime_robject(SEXP sxp){ + if(isUnit("secs", sxp) || isUnit("mins", sxp)){ + // secs or mins return from_second_or_minute_robject(sxp); - else if(isUnit("days", sxp)) + } + else if(isUnit("days", sxp)){ + // days return from_days_robject(sxp); - else /* hours */ + } + else{ + // hours return from_nyi_robject(sxp); + } } /* * NULL in R(R_NilValue): often used as generic zero length vector * NULL objects cannot have attributes and attempting to assign one by attr gives an error */ -ZK from_null_robject(SEXP sxp) { +static K from_null_robject(SEXP sxp) { return knk(0); } -ZK from_symbol_robject(SEXP sxp) { +static K from_symbol_robject(SEXP sxp) { const char* t = CHAR(PRINTNAME(sxp)); K x = ks((S)t); return attR(x,sxp); } -ZK from_closure_robject(SEXP sxp) { +static K from_closure_robject(SEXP sxp) { K x = from_any_robject(FORMALS(sxp)); K y = from_any_robject(BODY(sxp)); return attR(knk(2,x,y),sxp); } -ZK from_language_robject(SEXP sxp) { +static K from_language_robject(SEXP sxp) { K x = knk(0); SEXP s = sxp; while (0 < length(s)) { @@ -379,12 +371,12 @@ ZK from_language_robject(SEXP sxp) { return attR(x,sxp); } -ZK from_char_robject(SEXP sxp) { +static K from_char_robject(SEXP sxp) { K x = kpn((S)CHAR(STRING_ELT(sxp,0)),LENGTH(sxp)); return attR(x,sxp); } -ZK from_logical_robject(SEXP sxp) { +static K from_logical_robject(SEXP sxp) { K x; J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); @@ -410,7 +402,7 @@ ZK from_logical_robject(SEXP sxp) { return x; } -ZK from_integer_robject(SEXP sxp) { +static K from_integer_robject(SEXP sxp) { K x; J len = XLENGTH(sxp); SEXP dim= getAttrib(sxp, R_DimSymbol); @@ -436,7 +428,7 @@ ZK from_integer_robject(SEXP sxp) { return x; } -ZK from_double_robject(SEXP sxp){ +static K from_double_robject(SEXP sxp){ K x; I nano, span, bit64=isClass("integer64",sxp); J len = XLENGTH(sxp); @@ -479,7 +471,7 @@ ZK from_double_robject(SEXP sxp){ return x; } -ZK from_character_robject(SEXP sxp) { +static K from_character_robject(SEXP sxp) { K x; J i, length = XLENGTH(sxp); if (length == 1) @@ -492,7 +484,7 @@ ZK from_character_robject(SEXP sxp) { return attR(x,sxp); } -ZK from_vector_robject(SEXP sxp) { +static K from_vector_robject(SEXP sxp) { J i, length = LENGTH(sxp); K x = ktn(0, length); for (i = 0; i < length; i++) @@ -514,13 +506,13 @@ ZK from_vector_robject(SEXP sxp) { * done for boolean, int, double */ -ZK klogicv(J len, int *val) { +static K klogicv(J len, int *val) { K x= ktn(KB, len); DO(len, kG(x)[i]= (val)[i]); return x; } -ZK klogica(J len, int rank, int *shape, int *val) { +static K klogica(J len, int rank, int *shape, int *val) { K x, y; J i, j, r, c, k; switch(rank) { @@ -549,13 +541,13 @@ ZK klogica(J len, int rank, int *shape, int *val) { return x; } -ZK kintv(J len, int *val) { +static K kintv(J len, int *val) { K x = ktn(KI, len); DO(len,kI(x)[i]=(val)[i]); return x; } -ZK kinta(J len, int rank, int *shape, int *val) { +static K kinta(J len, int rank, int *shape, int *val) { K x,y; J i,j,r,c,k; switch (rank) { @@ -584,7 +576,7 @@ ZK kinta(J len, int rank, int *shape, int *val) { return x; } -ZK klonga(J len, int rank, int *shape, J*val) { +static K klonga(J len, int rank, int *shape, J*val) { K x, y; J i, j, r, c, k; switch(rank) { @@ -614,13 +606,13 @@ ZK klonga(J len, int rank, int *shape, J*val) { return x; } -ZK kdoublev(J len, double *val) { +static K kdoublev(J len, double *val) { K x = ktn(KF, len); DO(len,kF(x)[i]=(val)[i]); return x; } -ZK kdoublea(J len, int rank, int *shape, double *val) { +static K kdoublea(J len, int rank, int *shape, double *val) { K x,y; J i,j,r,c,k; switch (rank) { @@ -647,4 +639,4 @@ ZK kdoublea(J len, int rank, int *shape, double *val) { x = jk(&x,kdoublea(c,k,shape,val+c*i)); } return x; -} \ No newline at end of file +} diff --git a/src/embedr.c b/src/embedr.c deleted file mode 100644 index bcd5f9f..0000000 --- a/src/embedr.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This library provides an R server for Q - */ -#include -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#include -#include -#include -//#include -#else -#include -#include -#endif - -#include - -#define KXVER 3 -#include "k.h" -#define INT64(x) ((J*) REAL(x)) - -// Offsets used in conversion between R and q -static const J epoch_offset=10957*24*60*60*1000000000LL; -// Seconds in a day -static const int sec2day = 86400; -// Days+Seconds between 1970.01.01 & 2000.01.01 -static const int kdbDateOffset = 10957; -static const int kdbSecOffset = 946684800; - -#include "common_R_interface/q2r.c" -#include "common_R_interface/r2q.c" -#include "feature_for_embedR/q2r_ex.c" -#include "feature_for_embedR/r2q_ex.c" - -int R_SignalHandlers = 0; diff --git a/src/q2r_ex.c b/src/embedr_q2r.c similarity index 78% rename from src/q2r_ex.c rename to src/embedr_q2r.c index 33a9992..54794cc 100644 --- a/src/q2r_ex.c +++ b/src/embedr_q2r.c @@ -3,6 +3,17 @@ /* Overview: Distinct code in embedR for Q -> R interface */ /*-----------------------------------------------------------*/ +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include "common.h" +#include "embedr.h" + +/*-----------------------------------------------*/ +/* Functions */ +/*-----------------------------------------------*/ + /* * Given the appropriate names, types, and lengths, create an R named list. */ diff --git a/src/r2q_ex.c b/src/embedr_r2q.c similarity index 84% rename from src/r2q_ex.c rename to src/embedr_r2q.c index 4fc0dcf..12d63df 100644 --- a/src/r2q_ex.c +++ b/src/embedr_r2q.c @@ -3,7 +3,17 @@ /* Overview: Distinct code in embedR for R -> Q interface */ /*-----------------------------------------------------------*/ -#include "socketpair.c" +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +#include "socketpair.h" +#include "common.h" +#include "embedr.h" + +/*-----------------------------------------------*/ +/* List of Functions */ +/*-----------------------------------------------*/ /* * User interface @@ -14,16 +24,24 @@ K rclose(K x); K rexec(K x); K rget(K x); K rset(K x,K y); -ZK rcmd(int type,K x); +static K rcmd(int type,K x); + +/*-----------------------------------------------*/ +/* Global Variable */ +/*-----------------------------------------------*/ __thread int ROPEN=-1; // initialise thread-local. Will fail in other threads. Ideally need to check if on q main thread. __thread int RLOAD=0; +/*-----------------------------------------------*/ +/* Functions */ +/*-----------------------------------------------*/ + /* * Conversion from R to Q */ -ZK from_pairlist_robject(SEXP sxp) { +K from_pairlist_robject(SEXP sxp) { K x = ktn(0,2*length(sxp)); SEXP s = sxp;J i; for(i=0;in;i+=2) { @@ -72,7 +90,7 @@ void* pingthread; V* pingmain(V* v){ while(1){ - nanosleep(&(struct timespec){.tv_sec=0,.tv_nsec=1000000}, NULL); + nanosleep(&(struct timespec){.tv_sec=0, .tv_nsec=1000000}, NULL); send(spair[1], "M", 1, 0); } } diff --git a/src/socketpair.c b/src/socketpair.c index 715ee2c..0316c8d 100644 --- a/src/socketpair.c +++ b/src/socketpair.c @@ -23,19 +23,9 @@ * add argument make_overlapped */ -#include +#include "socketpair.h" -#ifdef WIN32 -# include -# include -# include -# include -#else -# include -# include -#endif - -#ifdef WIN32 +#ifdef _WIN32 /* dumb_socketpair: * If make_overlapped is nonzero, both sockets created will be usable for diff --git a/tests/test.q b/tests/test.q index eb5a3a7..6863f06 100644 --- a/tests/test.q +++ b/tests/test.q @@ -29,7 +29,7 @@ EQUAL:{[id;x;y] //%% System Setting %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ //Load embedR file -\l ../init.q +\l q/embedr.q //Set seed 42 \S 42 @@ -103,7 +103,6 @@ show .rk.get "wilcox.test(c(1,2,3),c(4,5,6))" .rk.exec "data(OrchardSprays)" show .rk.get "OrchardSprays" - // to install package in non-interactive way // install.packages("zoo", repos="http://cran.r-project.org") .rk.get"install.packages" @@ -122,12 +121,12 @@ EQUAL[28; .rk.get each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"di EQUAL[29; .rk.get"as.raw(10)"; (), 0x0a]; EQUAL[30; .rk.get"as.logical(c(1,FALSE,NA))"; 100b]; -PROGRESS["Onject Test Finished!!"]; +PROGRESS["Object Test Finished!!"]; //Table//-----------------------------------/ // data.frame -EQUAL[31; .rk.get"data.frame(a=1:3, b=c('a','b','c'))"; flip `a`b!(1 2 3i;`a`b`c)]; +EQUAL[31; .rk.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=TRUE)"; flip `a`b!(1 2 3i;`a`b`c)]; EQUAL[32; .rk.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b!(1 2 3i;1#/:("a";"b";"c"))]; EQUAL[33; .rk.get"data.frame(a=1:3)"; flip enlist[`a]!enlist (1 2 3i)]; EQUAL[34; .rk.get"data.frame()"; ()]; @@ -145,8 +144,18 @@ EQUAL[37; .rk.get"dictB"; `a`b`c!101b]; .rk.set["dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; EQUAL[38; .rk.get"dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; +PROGRESS["Dictionary Test Finished!!"]; + //Time//------------------------------------/ +// timestamp +.rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; +EQUAL[47; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; + +// month +.rk.set["mnth"; `month$/:2020.04.02 2010.01.29] +EQUAL[54; .rk.get"mnth"; 2020.04 2010.01m]; + // dates EQUAL[39; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; EQUAL[40; .rk.get"as.Date(NA)"; (), 0Nd]; @@ -161,33 +170,33 @@ EQUAL[45; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M: .rk.set["dttm"; 2018.02.18T04:00:01.000z]; EQUAL[46; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; -// timestamp -.rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; -EQUAL[47; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; - -// second -.rk.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; -EQUAL[48; .rk.get"scnd"; 12:00:30 12:30:45]; -EQUAL[49; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; - -// minute -.rk.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; -EQUAL[50; .rk.get "mnt"; 12:00 12:30]; -EQUAL[51; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; +-1 "AAAA"; // days .rk.set["days"; 1D 2D]; EQUAL[52; .rk.get"days"; 1D 2D]; EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; -// month -.rk.set["mnth"; `month$/:2020.04.02 2010.01.29] -EQUAL[54; .rk.get"mnth"; 2020.04 2010.01m]; +-1 "VVV"; // timespan .rk.set["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] EQUAL[55; .rk.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; +-1 "AAAA"; + +// minute +.rk.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +EQUAL[50; .rk.get "mnt"; 12:00 12:30]; +EQUAL[51; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; + +-1 "AAAA"; + +// second +.rk.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +EQUAL[48; .rk.get"scnd"; 12:00:30 12:30:45]; +EQUAL[49; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; + PROGRESS["Time Test Finished!!"]; //List//---------------------------------------/ @@ -249,7 +258,7 @@ EQUAL[74; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; .rk.get`inspect .rk.get"substitute(log(1))" -EQUAL[75; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[75; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"),stringsAsFactors=TRUE)"]; EQUAL[76; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; EQUAL[77; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; From 61a95fe8215a1768a9f9de884692a78002948a92 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Fri, 19 Feb 2021 22:34:43 +0000 Subject: [PATCH 17/33] Modifid README --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a558097..9e87bfd 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,11 @@ See Download the appropriate release archive from [releases](../../releases/latest) page. -#### Unpack and install content of the archive +- Linux/Mac -environment | action -----------------|--------------------------------------------------------------------------------------- -Linux | `tar xzvf embedr_linux-v*.tar.gz -C $QHOME --strip 1` -macOS | `tar xzvf embedr_osx-v*.tar.gz -C $QHOME --strip 1` -Windows | Open the archive and copy content of the `embedr` folder (`embedr\*`) to `%QHOME%` or `c:\q`
Copy R_HOME/x64/*.dll or R_HOME/i386/*.dll to QHOME/w64 or QHOME/w32 respectively. + $ install.sh + +- Windows: Open the archive and copy content of the `embedr` folder (`embedr\*`) to `%QHOME%` or `c:\q`
Copy R_HOME/x64/*.dll or R_HOME/i386/*.dll to QHOME/w64 or QHOME/w32 respectively. ### Install from Source From 799bfd5e72852328901b696b2f0429f3e7121826 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Fri, 5 Mar 2021 15:41:50 +0000 Subject: [PATCH 18/33] Use 2 environmental variables to point to R headers and library && Removed debug print --- .travis.yml | 24 ++++++++++++------ README.md | 15 ++++++++++- src/CMakeLists.txt | 4 +-- src/common_R_interface/r2q.c | 1 - tests/test.q | 48 +++++++++++++++++++++++++----------- 5 files changed, 65 insertions(+), 27 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2d44676..5873abe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,22 +17,30 @@ before_install: # Install R - if [[ $TRAVIS_OS_NAME == linux ]]; then sudo apt-get install r-base; - export R_INSTALL_DIR=/usr/lib64/R - export LD_LIBRARY_PATH=${R_INSTALL_DIR}:${LD_LIBRARY_PATH}; elif [[ $TRAVIS_OS_NAME == osx ]]; then export HOMEBREW_AUTO_UPDATING=0; brew install --build-from-source r; - export R_INSTALL_DIR=/usr/local/Cellar/R - export DYLD_LIBRARY_PATH=${R_INSTALL_DIR}:${LD_LIBRARY_PATH}; + else + echo "$TRAVIS_OS_NAME is not supported"; + exit 1; fi + # Setup R directory. + - export R_LIBRARY_DIR=$(R RHOME)/lib + - export R_INCLUDE_DIR=$(R CMD config --cppflags | cut -c 3-) + - export LD_LIBRARY_PATH=${R_LIBRARY_DIR}:${LD_LIBRARY_PATH} + - export DYLD_LIBRARY_PATH=${R_LIBRARY_DIR}:${DYLD_LIBRARY_PATH} + # Install q and set QHOME - if [[ $TRAVIS_OS_NAME == "linux" ]]; then - QLIBDIR=l64; OD=$L64; + QLIBDIR=l64; + OD=$L64; elif [[ $TRAVIS_OS_NAME == "osx" ]]; then - QLIBDIR=m64; OD=$M64; + QLIBDIR=m64; + OD=$M64; elif [[ $TRAVIS_OS_NAME == "windows" ]]; then - QLIBDIR=w64; OD=$W64; + QLIBDIR=w64; + OD=$W64; else echo "unknown OS ('$TRAVIS_OS_NAME')" >&2; exit 1; fi @@ -74,7 +82,7 @@ before_install: script: - if [[ $TESTS == "True" && "x$OD" != "x" && "x$QLIC_KC" != "x" ]]; then - q tests/test.q; + q tests/test.q -test_data_frame false fi - if [[ $TRAVIS_OS_NAME == "windows" && $BUILD == "True" ]]; then 7z a -tzip -r $FILE_NAME ./build/$FILE_ROOT/*; diff --git a/README.md b/README.md index 9e87bfd..7307a3f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,18 @@ Download the appropriate release archive from [releases](../../releases/latest) ### Install from Source -For Linux/MacOS it is possible to build the library from source using the CMake file provided. You need to set a directory which includes `lib` and `include` directories to an environmental variable `R_INSTALL_DIR`. Then from the root of the directory: +For Linux/MacOS it is possible to build the library from source using the CMake file provided. + +For successful installation you need to set a path to `lib` directory on `R_LIBRARY_DIR` and a path to `include` directory on `R_INCLUDE_DIR` with following commands: + +```bash + +]$ export R_LIBRARY_DIR=$(R RHOME)/lib +]$ export R_INCLUDE_DIR=$(R CMD config --cppflags | cut -c 3-) + +``` + +Then execte the commands below at the root directory of this repository: ```bash @@ -26,6 +37,8 @@ build]$ cmake --build . --target install ``` +**Note:** `cmake --build . --config Release --target install` installs the required share object and q files to the `QHOME\[os]64` and `QHOME` directories respectively. If you do not wish to install these files directly, you can execute `cmake --build . --config Release` instead of `cmake --build . --config Release --target install` and move the files from their build location at `build/embedr`. + ## Calling R When calling R, you need to set `R_HOME`. This can be set as follows: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14328ae..8270994 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,14 +22,14 @@ add_library(${MY_LIBRARY_NAME} SHARED target_include_directories(${MY_LIBRARY_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/include/common_R_interface - $ENV{R_INSTALL_DIR}/include/ + $ENV{R_INCLUDE_DIR} ) # Find dependency find_library(R_LIBRARY REQUIRED NAMES libR R - HINTS $ENV{R_INSTALL_DIR}/lib + HINTS $ENV{R_LIBRARY_DIR} ) if(MSVC) diff --git a/src/common_R_interface/r2q.c b/src/common_R_interface/r2q.c index 62916a2..d681148 100644 --- a/src/common_R_interface/r2q.c +++ b/src/common_R_interface/r2q.c @@ -317,7 +317,6 @@ static K from_second_or_minute_robject(SEXP sxp){ } static K from_days_robject(SEXP sxp){ - printf("HOOO\n"); K x; J length= XLENGTH(sxp); x= ktn(KN,length); diff --git a/tests/test.q b/tests/test.q index 6863f06..d2bbd70 100644 --- a/tests/test.q +++ b/tests/test.q @@ -1,5 +1,16 @@ -/ test R server for Q +/ +* test R server for Q. +* # Note +* - When testing on travis CI `.rk.install` should not be run; therefore commandline argument +* `test_data_frame` must be passed with its value `true`, e.g., +* $ q tests/test.q -test_data_frame true +* - `-s` flag must be passed to test limtation of main thread only. If `\s` is 0, Final test will be ignored. +\ +//%% Commandline arguments %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +COMMANDLINE_ARGS: .Q.opt .z.x; +if[`test_data_frame in key COMMANDLINE_ARGS; @[`COMMANDLINE_ARGS; `test_data_frame; {lower first x}]]; //%% Define Test Function/Variable %%//vvvvvvvvvvvvvvvvvvvvvvvvv/ @@ -170,28 +181,20 @@ EQUAL[45; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M: .rk.set["dttm"; 2018.02.18T04:00:01.000z]; EQUAL[46; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; --1 "AAAA"; - // days .rk.set["days"; 1D 2D]; EQUAL[52; .rk.get"days"; 1D 2D]; EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; --1 "VVV"; - // timespan .rk.set["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] EQUAL[55; .rk.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; --1 "AAAA"; - // minute .rk.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; EQUAL[50; .rk.get "mnt"; 12:00 12:30]; EQUAL[51; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; --1 "AAAA"; - // second .rk.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; EQUAL[48; .rk.get"scnd"; 12:00:30 12:30:45]; @@ -243,12 +246,19 @@ PROGRESS["Q-Like R Command Test Finished!!"]; // run gc .rk.get"gc()" -//.rk.set["a";`sym?`a`b`c] -//`:x set string 10?`4 -//.rk.set["a";get `:x] -//hdel `:x; +.rk.set["a";`sym?`a`b`c]; +`:x set string 10?`4; +.rk.set["a";get `:x]; +hdel `:x; +hdel `$":x#"; + +// Finish testing if `test_data_frame` is not "true". +if[not "true" ~ COMMANDLINE_ARGS `test_data_frame; + PROGRESS["Completed!!"]; + exit 0 + ]; -.rk.install`data.table +.rk.install `data.table .rk.exec"library(data.table)" .rk.exec"a<-data.frame(a=c(1,2))" EQUAL[73; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; @@ -262,5 +272,13 @@ EQUAL[75; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), EQUAL[76; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; EQUAL[77; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; +// Finish testing if slave thread is not used. +if[0i ~ system "s"; + PROGRESS["Completed!!"]; + exit 0 + ]; + +EQUAL[78; all {.[.rk.set;("x"; x);"main thread only"~]} peach 2#enlist ([]1 2); 1b]; PROGRESS["Completed!!"]; -// all {.[.rk.set;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2) + +exit 0 From a642b799905b33906aa4d3acd07bd56f4eacbfc2 Mon Sep 17 00:00:00 2001 From: Masato Shimizu Date: Sat, 6 Mar 2021 00:19:52 +0000 Subject: [PATCH 19/33] Modified to resolve MSVC compile error as much as possible (not compilable yet) --- .travis.yml | 2 +- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++- install.bat | 33 +++++++++++++++++++++++++++++ src/embedr_q2r.c | 3 ++- src/embedr_r2q.c | 44 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 install.bat diff --git a/.travis.yml b/.travis.yml index 5873abe..cb35186 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,7 +73,7 @@ before_install: - cd build - if [[ $TRAVIS_OS_NAME == windows ]]; then cmake --config Release ..; - cmake --config Release --build . --target install; + cmake --build . --config Release --target install; else cmake ..; cmake --build . --target install; diff --git a/README.md b/README.md index 7307a3f..5f823d1 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Download the appropriate release archive from [releases](../../releases/latest) ### Install from Source -For Linux/MacOS it is possible to build the library from source using the CMake file provided. +Building the library from source uses the CMake file provided. + +#### Linux/ Mac For successful installation you need to set a path to `lib` directory on `R_LIBRARY_DIR` and a path to `include` directory on `R_INCLUDE_DIR` with following commands: @@ -37,6 +39,56 @@ build]$ cmake --build . --target install ``` +**Note:** `cmake --build . --target install` installs the required share object and q files to the `QHOME\[os]64` and `QHOME` directories respectively. If you do not wish to install these files directly, you can execute `cmake --build .` instead of `cmake --build . --target install` and move the files from their build location at `build/embedr`. + +#### Windows + +Set a path to `lib` directory on `R_LIBRARY_DIR` and a path to `include` directory on `R_INCLUDE_DIR` with following commands: + +```bat + +:: Example output as we don't know how to evaluate an expression... +> R RHOME +C:\PROGRA~1\R\R-40~1.4 +> set R_HOME=C:\PROGRA~1\R\R-40~1.4 +> set R_LIBRARY_DIR=%R_HOME%\bin\x64 +:: looks like `include` is in the same layer as `lib`. Check your own environment. +> set R_INCLUDE_DIR=%R_HOME%\include + +``` + +Next you need to create `R.lib` from `R.dll` in `R_LIBRARY_DIR` 🔨🔨🔨. This `R.lib` will be used for linking the interface. + +```bat + +> cd %R_LIBRARY_DIR% +x64> echo EXPORTS > R.def +x64> for /f "usebackq tokens=4,* delims=_ " %i in (`dumpbin /exports "R.dll"`) do echo %i_%j >> R.def +:: Then delete 2-9 lines manually. +:: After the manual processing, create lib file. +x64> lib /def:R.def /out:R.lib /machine:x64 + +``` + +Then you need to create a symlink to `R.dll`. + +```bat + +x64> cd %QHOME%\w64 +w64> MKLINK R.dll %R_HOME%\bin\R.dll + +``` + +Then execte the commands below at the root directory of this repository on Visual Studio (assuming `-G "Visual Studio 15 2017 Win64"` is not necessary): + +```bat + +embedR>$ mkdir build && cd build +build> cmake --config Release .. +build> cmake --build . --config Release --target install + +``` + **Note:** `cmake --build . --config Release --target install` installs the required share object and q files to the `QHOME\[os]64` and `QHOME` directories respectively. If you do not wish to install these files directly, you can execute `cmake --build . --config Release` instead of `cmake --build . --config Release --target install` and move the files from their build location at `build/embedr`. ## Calling R diff --git a/install.bat b/install.bat new file mode 100644 index 0000000..3b8bf0e --- /dev/null +++ b/install.bat @@ -0,0 +1,33 @@ +@echo off + +IF "%QHOME%"=="" ( + ECHO ERROR: Enviroment variable QHOME is NOT defined + EXIT /B +) + +IF NOT EXIST %QHOME%\w64 ( + ECHO ERROR: Installation destination %QHOME%\w64 does not exist + EXIT /B +) + + +IF EXIST q ( + ECHO Copying q script to %QHOME% + COPY q\* %QHOME% + IF %ERRORLEVEL% NEQ 0 ( + ECHO ERROR: Copy failed + EXIT /B %ERRORLEVEL% + ) +) + +IF EXIST lib ( + ECHO Copying DLL to %QHOME%\w64 + COPY lib\* %QHOME%\w64\ + IF %ERRORLEVEL% NEQ 0 ( + ECHO ERROR: Copy failed + EXIT /B %ERRORLEVEL% + ) +) + +ECHO Installation complete + diff --git a/src/embedr_q2r.c b/src/embedr_q2r.c index 54794cc..d988b36 100644 --- a/src/embedr_q2r.c +++ b/src/embedr_q2r.c @@ -7,8 +7,9 @@ /* Load Libraries */ /*-----------------------------------------------*/ -#include "common.h" +// Order of include does matter. #include "embedr.h" +#include "common.h" /*-----------------------------------------------*/ /* Functions */ diff --git a/src/embedr_r2q.c b/src/embedr_r2q.c index 12d63df..2ffaa78 100644 --- a/src/embedr_r2q.c +++ b/src/embedr_r2q.c @@ -7,9 +7,10 @@ /* Load Libraries */ /*-----------------------------------------------*/ +// Order of include does matter. +#include "embedr.h" #include "socketpair.h" #include "common.h" -#include "embedr.h" /*-----------------------------------------------*/ /* List of Functions */ @@ -30,8 +31,17 @@ static K rcmd(int type,K x); /* Global Variable */ /*-----------------------------------------------*/ -__thread int ROPEN=-1; // initialise thread-local. Will fail in other threads. Ideally need to check if on q main thread. +#ifdef _WIN32 +// initialise thread-local. Will fail in other threads. Ideally need to check if +// on q main thread. +__declspec(thread) int ROPEN= -1; +__declspec(thread) int RLOAD= 0; +#else +// initialise thread-local. Will fail in other threads. Ideally need to check if +// on q main thread. +__thread int ROPEN=-1; __thread int RLOAD=0; +#endif /*-----------------------------------------------*/ /* Functions */ @@ -208,4 +218,32 @@ K rset(K x,K y) { return (K)0; } -__attribute__((constructor)) V __attach(V) {RLOAD=1;} +// Preamble to quench compile error for MSVC +// https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif +#else + #define INITIALIZER(f) \ + static void f(void) __attribute__((constructor)); \ + static void f(void) +#endif + +INITIALIZER(__attach) +{ + RLOAD=1; +} From 8209d8ff88eff5cbdc942e6a979a0bb044d06968 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Sat, 6 Mar 2021 23:00:52 +0000 Subject: [PATCH 20/33] Link WS2_32 in source --- include/embedr.h | 4 ++-- include/socketpair.h | 4 ++-- install.bat | 33 +++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 2 ++ src/embedr_q2r.c | 2 +- src/embedr_r2q.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 install.bat diff --git a/include/embedr.h b/include/embedr.h index 75e764c..b7467a9 100644 --- a/include/embedr.h +++ b/include/embedr.h @@ -9,13 +9,13 @@ #include #include #include -#ifndef WIN32 +#ifndef _WIN32 #include #include #include #include #else #include -#include +#include #endif #include diff --git a/include/socketpair.h b/include/socketpair.h index 536fc32..ef513fb 100644 --- a/include/socketpair.h +++ b/include/socketpair.h @@ -10,8 +10,8 @@ #include #ifdef _WIN32 - -# include +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#pragma comment(lib, "ws2_32.lib") # include # include # include diff --git a/install.bat b/install.bat new file mode 100644 index 0000000..3b8bf0e --- /dev/null +++ b/install.bat @@ -0,0 +1,33 @@ +@echo off + +IF "%QHOME%"=="" ( + ECHO ERROR: Enviroment variable QHOME is NOT defined + EXIT /B +) + +IF NOT EXIST %QHOME%\w64 ( + ECHO ERROR: Installation destination %QHOME%\w64 does not exist + EXIT /B +) + + +IF EXIST q ( + ECHO Copying q script to %QHOME% + COPY q\* %QHOME% + IF %ERRORLEVEL% NEQ 0 ( + ECHO ERROR: Copy failed + EXIT /B %ERRORLEVEL% + ) +) + +IF EXIST lib ( + ECHO Copying DLL to %QHOME%\w64 + COPY lib\* %QHOME%\w64\ + IF %ERRORLEVEL% NEQ 0 ( + ECHO ERROR: Copy failed + EXIT /B %ERRORLEVEL% + ) +) + +ECHO Installation complete + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8270994..769bb99 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,6 +71,8 @@ endif() target_link_libraries(${MY_LIBRARY_NAME} PRIVATE ${Q_LIBRARY} ${R_LIBRARY} + #$<$:${WS2_LIBRARY}> + #${WS2_LIBRARY} ) ##%% Installation %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ diff --git a/src/embedr_q2r.c b/src/embedr_q2r.c index 54794cc..36c5e27 100644 --- a/src/embedr_q2r.c +++ b/src/embedr_q2r.c @@ -7,8 +7,8 @@ /* Load Libraries */ /*-----------------------------------------------*/ -#include "common.h" #include "embedr.h" +#include "common.h" /*-----------------------------------------------*/ /* Functions */ diff --git a/src/embedr_r2q.c b/src/embedr_r2q.c index 12d63df..9cc99aa 100644 --- a/src/embedr_r2q.c +++ b/src/embedr_r2q.c @@ -8,8 +8,8 @@ /*-----------------------------------------------*/ #include "socketpair.h" -#include "common.h" #include "embedr.h" +#include "common.h" /*-----------------------------------------------*/ /* List of Functions */ @@ -30,8 +30,17 @@ static K rcmd(int type,K x); /* Global Variable */ /*-----------------------------------------------*/ -__thread int ROPEN=-1; // initialise thread-local. Will fail in other threads. Ideally need to check if on q main thread. +#ifdef _WIN32 +// initialise thread-local. Will fail in other threads. Ideally need to check if +// on q main thread. +__declspec(thread) int ROPEN= -1; +__declspec(thread) int RLOAD= 0; +#else +// initialise thread-local. Will fail in other threads. Ideally need to check if +// on q main thread. +__thread int ROPEN=-1; __thread int RLOAD=0; +#endif /*-----------------------------------------------*/ /* Functions */ @@ -208,4 +217,32 @@ K rset(K x,K y) { return (K)0; } -__attribute__((constructor)) V __attach(V) {RLOAD=1;} + +// Enable Visual Studio to compile. +// See: https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +#ifdef __cplusplus +#define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { \ + f##_t_(void) { f(); } \ + }; \ + static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) +#pragma section(".CRT$XCU", read) +#define INITIALIZER2_(f, p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void)= f; \ + __pragma(comment(linker, "/include:" p #f "_")) static void f(void) +#ifdef _WIN64 +#define INITIALIZER(f) INITIALIZER2_(f, "") +#else +#define INITIALIZER(f) INITIALIZER2_(f, "_") +#endif +#else +#define INITIALIZER(f) \ + static void f(void) __attribute__((constructor)); \ + static void f(void) +#endif + +INITIALIZER(attach) { RLOAD= 1; } From 3f39bd89e39b3c339be006f84557617baaf4a742 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Sat, 6 Mar 2021 23:32:46 +0000 Subject: [PATCH 21/33] Windows nanosleep. Compile done but cannot find module --- src/embedr_q2r.c | 2 +- src/embedr_r2q.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/embedr_q2r.c b/src/embedr_q2r.c index d988b36..05e9e84 100644 --- a/src/embedr_q2r.c +++ b/src/embedr_q2r.c @@ -50,4 +50,4 @@ SEXP make_named_list(char **names, SEXPTYPE *types, Sint *lengths, Sint n) { SET_NAMES(output, output_names); UNPROTECT(n+2); return output; -} \ No newline at end of file +} diff --git a/src/embedr_r2q.c b/src/embedr_r2q.c index 2ffaa78..a282ca7 100644 --- a/src/embedr_r2q.c +++ b/src/embedr_r2q.c @@ -12,6 +12,32 @@ #include "socketpair.h" #include "common.h" +#ifdef _WIN32 +#include /* WinAPI */ + +/* Windows sleep in 100ns units */ +BOOLEAN nanosleep(LONGLONG ns) { + /* Declarations */ + HANDLE timer; /* Timer handle */ + LARGE_INTEGER li; /* Time defintion */ + /* Create timer */ + if(!(timer= CreateWaitableTimer(NULL, TRUE, NULL))) + return FALSE; + /* Set timer properties */ + li.QuadPart= -ns; + if(!SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE)) { + CloseHandle(timer); + return FALSE; + } + /* Start & wait for timer */ + WaitForSingleObject(timer, INFINITE); + /* Clean resources */ + CloseHandle(timer); + /* Slept without problems */ + return TRUE; +} +#endif + /*-----------------------------------------------*/ /* List of Functions */ /*-----------------------------------------------*/ @@ -100,7 +126,11 @@ void* pingthread; V* pingmain(V* v){ while(1){ - nanosleep(&(struct timespec){.tv_sec=0, .tv_nsec=1000000}, NULL); + #ifndef _WIN32 + nanosleep(&(struct timespec){.tv_sec=0, .tv_nsec=1000000}, NULL); + #else + nanosleep(1000000); + #endif send(spair[1], "M", 1, 0); } } @@ -132,7 +162,7 @@ K ropen(K x) { if(dumb_socketpair(spair, 1) == -1) return krr("Init failed for socketpair"); sd1(-spair[0], &processR); - #ifndef WIN32 + #ifndef _WIN32 pthread_t t; if(pthread_create(&t, NULL, pingmain, NULL)) R krr("poller_thread"); From 63c86efb5503f840b3d157eb446e6e42ec4a41c7 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Sun, 7 Mar 2021 00:27:46 +0000 Subject: [PATCH 22/33] More exact branching for nanosleep --- src/embedr_r2q.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/embedr_r2q.c b/src/embedr_r2q.c index a282ca7..447232e 100644 --- a/src/embedr_r2q.c +++ b/src/embedr_r2q.c @@ -12,7 +12,7 @@ #include "socketpair.h" #include "common.h" -#ifdef _WIN32 +#if(defined(_WIN32) && (defined(_MSC_VER))) #include /* WinAPI */ /* Windows sleep in 100ns units */ @@ -126,11 +126,11 @@ void* pingthread; V* pingmain(V* v){ while(1){ - #ifndef _WIN32 - nanosleep(&(struct timespec){.tv_sec=0, .tv_nsec=1000000}, NULL); - #else - nanosleep(1000000); - #endif +#if(defined(_WIN32) && (defined(_MSC_VER))) + nanosleep(1000000); +#else + nanosleep(&(struct timespec){ .tv_sec= 0, .tv_nsec= 1000000 }, NULL); +#endif send(spair[1], "M", 1, 0); } } From e2903af92c1c5dc48fb44b6e4aca3caa12d60880 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Sun, 7 Mar 2021 15:50:49 +0000 Subject: [PATCH 23/33] Complete Windows build and test --- .clang-format | 98 ------------------------------------- README.md | 23 +++++---- appveyor.yml | 46 ----------------- include/embedr.h | 12 +++-- include/socketpair.h | 2 +- q/embedr.q | 68 ++++++++++++++++--------- src/CMakeLists.txt | 2 - src/embedr_r2q.c | 81 +++++++++++++++++------------- windows/w32/q.a | Bin 32526 -> 0 bytes windows/w64/q.a | Bin 33478 -> 0 bytes windows/win_install/w32.sh | 6 --- windows/win_install/w64.sh | 6 --- 12 files changed, 113 insertions(+), 231 deletions(-) delete mode 100644 .clang-format delete mode 100644 appveyor.yml delete mode 100644 windows/w32/q.a delete mode 100755 windows/w64/q.a delete mode 100755 windows/win_install/w32.sh delete mode 100755 windows/win_install/w64.sh diff --git a/.clang-format b/.clang-format deleted file mode 100644 index cf0bf57..0000000 --- a/.clang-format +++ /dev/null @@ -1,98 +0,0 @@ ---- -Language: Cpp -# BasedOnStyle: LLVM -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: false -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: false -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' -BreakBeforeInheritanceComma: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: false -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '$' -IndentCaseLabels: false -IndentWidth: 2 -IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right -ReflowComments: true -SortIncludes: false -SpaceAfterCStyleCast: true -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: false -SpaceBeforeParens: Never -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 8 -UseTab: Never -... - diff --git a/README.md b/README.md index 5f4229c..6a82331 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,13 @@ See Download the appropriate release archive from [releases](../../releases/latest) page. -- Linux/Mac +- Linux/Mac: $ install.sh -- Windows: Open the archive and copy content of the `embedr` folder (`embedr\*`) to `%QHOME%` or `c:\q`
Copy R_HOME/x64/*.dll or R_HOME/i386/*.dll to QHOME/w64 or QHOME/w32 respectively. +- Windows: + + > install.bat ### Install from Source @@ -49,10 +51,9 @@ Set a path to `lib` directory on `R_LIBRARY_DIR` and a path to `include` directo :: Example output as we don't know how to evaluate an expression... > R RHOME -C:\PROGRA~1\R\R-40~1.4 -> set R_HOME=C:\PROGRA~1\R\R-40~1.4 +C:\PROGRA~1\R\R-36~1.3 +> set R_HOME=C:\PROGRA~1\R\R-36~1.3 > set R_LIBRARY_DIR=%R_HOME%\bin\x64 -:: looks like `include` is in the same layer as `lib`. Check your own environment. > set R_INCLUDE_DIR=%R_HOME%\include ``` @@ -70,20 +71,24 @@ x64> lib /def:R.def /out:R.lib /machine:x64 ``` -Then you need to create a symlink to `R.dll`. +Then you need to create a symlink to `R.dll` and its sub R-related `.dll` files in the same directory (`Rblas.dll`, `Rgraphapp.dll`, `Riconv.dll` and `Rlapack.dll`). ```bat x64> cd %QHOME%\w64 -w64> MKLINK R.dll %R_HOME%\bin\R.dll +w64> MKLINK R.dll %R_LIBRARY_DIR%\R.dll +w64> MKLINK Rblas.dll %R_LIBRARY_DIR%\Rblas.dll +w64> MKLINK Rgraphapp.dll %R_LIBRARY_DIR%\Rgraphapp.dll +w64> MKLINK Riconv.dll %R_LIBRARY_DIR%\Riconv.dll +w64> MKLINK Rlapack.dll %R_LIBRARY_DIR%\Rlapack.dll ``` -Then execte the commands below at the root directory of this repository on Visual Studio (assuming `-G "Visual Studio 15 2017 Win64"` is not necessary): +Finally build embedR library by executing the commands below at the root directory of this repository on Visual Studio (assuming `-G "Visual Studio 15 2017 Win64"` is not necessary): ```bat -embedR>$ mkdir build && cd build +embedR> mkdir build && cd build build> cmake --config Release .. build> cmake --build . --config Release --target install diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 01853e1..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,46 +0,0 @@ -image: Visual Studio 2017 -# Download script file from GitHub -init: - ps: | - $ErrorActionPreference = "Stop" - Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" - Import-Module '..\appveyor-tool.ps1' - -install: - ps: Bootstrap - -environment: - global: - GCC_PATH: mingw_64 - matrix: - - BITS: 64 - R_ARCH: x64 - - - BITS: 32 - R_ARCH: i386 - -build_script: -- cmd: mkdir w%BITS% -- cmd: R.exe CMD SHLIB -o w%BITS%/embedr.dll src/embedr.c src/w%BITS%/q.a -lws2_32 - -after_build: -- cmd: mkdir embedr -- ps: Copy-Item w$env:BITS -Destination .\embedr\w$env:BITS -Recurse -- ps: Copy-Item rinit.q -Destination .\embedr -- ps: Copy-Item rtest.q -Destination .\embedr -- ps: Copy-Item LICENSE -Destination .\embedr\LICENSE.embedr -- ps: Copy-Item README.md -Destination .\embedr\README.embedr -- IF /I "%APPVEYOR_REPO_TAG%" == "false" set APPVEYOR_REPO_TAG_NAME=%APPVEYOR_REPO_BRANCH% -- cmd: 7z a embedr_win%BITS%-%APPVEYOR_REPO_TAG_NAME%.zip %APPVEYOR_BUILD_FOLDER%\embedr -artifacts: -- path: 'embedr_win*.zip' - -deploy: - provider: GitHub - draft: true - auth_token: - secure: P2U9jL8L7es2Iv/SSthmQ+a9qSZ41OSiGHjSdOMve7eKwWoo3zh/8DvJqkkMfhS7 - prerelease: true - on: - branch: master # release from master branch only - appveyor_repo_tag: true # deploy on tag push only diff --git a/include/embedr.h b/include/embedr.h index b7467a9..5fbb0dc 100644 --- a/include/embedr.h +++ b/include/embedr.h @@ -9,13 +9,17 @@ #include #include #include -#ifndef _WIN32 + +#ifdef _WIN32 +#include +#include +#define EXP __declspec(dllexport) +#else #include #include #include #include -#else -#include -#include +#include +#define EXP #endif #include diff --git a/include/socketpair.h b/include/socketpair.h index ef513fb..7360ff5 100644 --- a/include/socketpair.h +++ b/include/socketpair.h @@ -11,7 +11,7 @@ #ifdef _WIN32 #define _WINSOCK_DEPRECATED_NO_WARNINGS -#pragma comment(lib, "ws2_32.lib") +# include # include # include # include diff --git a/q/embedr.q b/q/embedr.q index 22ae8a8..efe73a0 100644 --- a/q/embedr.q +++ b/q/embedr.q @@ -1,38 +1,58 @@ -\d .rk +/ +* @file embedr.q +* @overview +* Define interface functions of embedR. +\ + +/*-----------------------------------------------*/ +/* Initial Setting */ +/*-----------------------------------------------*/ + +setenv[`R_HOME;first @[system;$[.z.o like "w*";"call";"env"]," R RHOME";enlist""]]; LIBPATH:`:embedr 2: -funcs:( - (`rclose;1); - (`ropen;1); - (`rexec;1); - (`rget;1); - (`rset;2) - ) -.rk,:(`$1_'string funcs[;0])!LIBPATH@/:funcs - -install:{[pkg] - pkg:$[-11=type pkg;string pkg;pkg];rcloud:"https://cloud.r-project.org"; +/*-----------------------------------------------*/ +/* Load Libraries */ +/*-----------------------------------------------*/ + +.rk.close: LIBPATH (`rclose;1); +.rk.open: LIBPATH (`ropen;1); +.rk.exec: LIBPATH (`rexec;1); +.rk.get: LIBPATH (`rget;1); +.rk.set: LIBPATH (`rset;2) + +/*-----------------------------------------------*/ +/* Additional Functions */ +/*-----------------------------------------------*/ + +.rk.install:{[pkg] + pkg:$[-11=type pkg;string pkg;pkg]; + rcloud:"https://cloud.r-project.org"; if[0i=first .rk.get"is.element('",pkg,"',installed.packages()[,1])"; .rk.exec"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; } -off:{.rk.exec "dev.off()"} -new:{.rk.exec "dev.new(noRStudioGD=TRUE)"} +.rk.off:{.rk.exec "dev.off()"} +.rk.new:{.rk.exec "dev.new(noRStudioGD=TRUE)"} -\d . +/*-----------------------------------------------*/ +/* Deprecated Function */ +/*-----------------------------------------------*/ // The following block of functions are aliases for the above // these aliases will be removed in a future version of the code -Rclose :.rk.close -Rcmd :.rk.exec -Rget :.rk.get -Rinstall:.rk.install -Rnew :.rk.new -Roff :.rk.off -Rset :.rk.set - -setenv[`R_HOME;first @[system;@[.z.o like "w*";"call";"env"]," R RHOME";enlist""]] +Rclose :.rk.close; +Rcmd :.rk.exec; +Rget :.rk.get; +Rinstall:.rk.install; +Rnew :.rk.new; +Roff :.rk.off; +Rset :.rk.set; + +/*-----------------------------------------------*/ +/* Start embedR */ +/*-----------------------------------------------*/ // Some time conversion from R -> q do not work without doing .rk.set first. ("kdbtimestamp_"; "kdbtimespan_"; "kdbdate_"; "kdbmonth_"; "kdbminute_"; "kdbsecond_") .rk.set' `timestamp`timespan`date`month`minute`second$/: 2000.01.01D00:00:00.000000000; \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 769bb99..8270994 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,8 +71,6 @@ endif() target_link_libraries(${MY_LIBRARY_NAME} PRIVATE ${Q_LIBRARY} ${R_LIBRARY} - #$<$:${WS2_LIBRARY}> - #${WS2_LIBRARY} ) ##%% Installation %%##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ diff --git a/src/embedr_r2q.c b/src/embedr_r2q.c index 447232e..77bbbcc 100644 --- a/src/embedr_r2q.c +++ b/src/embedr_r2q.c @@ -12,30 +12,15 @@ #include "socketpair.h" #include "common.h" -#if(defined(_WIN32) && (defined(_MSC_VER))) -#include /* WinAPI */ +//%% Socket Library %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ -/* Windows sleep in 100ns units */ -BOOLEAN nanosleep(LONGLONG ns) { - /* Declarations */ - HANDLE timer; /* Timer handle */ - LARGE_INTEGER li; /* Time defintion */ - /* Create timer */ - if(!(timer= CreateWaitableTimer(NULL, TRUE, NULL))) - return FALSE; - /* Set timer properties */ - li.QuadPart= -ns; - if(!SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE)) { - CloseHandle(timer); - return FALSE; - } - /* Start & wait for timer */ - WaitForSingleObject(timer, INFINITE); - /* Clean resources */ - CloseHandle(timer); - /* Slept without problems */ - return TRUE; -} +#ifdef _WIN32 +#pragma comment(lib, "ws2_32.lib") +SOCKET spair[2]; +#else +#include +#define SOCKET_ERROR -1 +I spair[2]; #endif /*-----------------------------------------------*/ @@ -46,12 +31,12 @@ BOOLEAN nanosleep(LONGLONG ns) { * User interface */ -K ropen(K x); -K rclose(K x); -K rexec(K x); -K rget(K x); -K rset(K x,K y); -static K rcmd(int type,K x); +EXP K ropen(K x); +EXP K rclose(K x); +EXP K rexec(K x); +EXP K rget(K x); +EXP K rset(K x,K y); +K rcmd(int type,K x); /*-----------------------------------------------*/ /* Global Variable */ @@ -73,6 +58,31 @@ __thread int RLOAD=0; /* Functions */ /*-----------------------------------------------*/ +#if(defined(_WIN32) && (defined(_MSC_VER))) + +/* Windows sleep in 100ns units */ +BOOLEAN nanosleep(LONGLONG ns) { + /* Declarations */ + HANDLE timer; /* Timer handle */ + LARGE_INTEGER li; /* Time defintion */ + /* Create timer */ + if(!(timer= CreateWaitableTimer(NULL, TRUE, NULL))) + return FALSE; + /* Set timer properties */ + li.QuadPart= -ns; + if(!SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE)) { + CloseHandle(timer); + return FALSE; + } + /* Start & wait for timer */ + WaitForSingleObject(timer, INFINITE); + /* Clean resources */ + CloseHandle(timer); + /* Slept without problems */ + return TRUE; +} +#endif + /* * Conversion from R to Q */ @@ -116,12 +126,13 @@ static char * getkstring(K x) { * The public interface used from Q. */ +/* #ifdef _WIN32 static SOCKET spair[2]; #else static int spair[2]; #endif - +*/ void* pingthread; V* pingmain(V* v){ @@ -150,7 +161,7 @@ K processR(I d){ * 1 --verbose */ -K ropen(K x) { +EXP K ropen(K x) { if(!RLOAD) return krr("main thread only"); if (ROPEN >= 0) return ki(ROPEN); int s,mode=0; char *argv[] = {"R","--slave"}; @@ -177,9 +188,9 @@ K ropen(K x) { // note that embedded R can be initialised once. No open/close/open supported // http://r.789695.n4.nabble.com/Terminating-and-restarting-an-embedded-R-instance-possible-td4641823.html -K rclose(K x){R NULL;} -K rexec(K x) { return rcmd(0,x); } -K rget(K x) { return rcmd(1,x); } +EXP K rclose(K x){R NULL;} +EXP K rexec(K x) { return rcmd(0,x); } +EXP K rget(K x) { return rcmd(1,x); } static char* ParseError[5]={"null","ok","incomplete","error","eof"}; @@ -216,7 +227,7 @@ K rcmd(int type,K x) { return (K)0; } -K rset(K x,K y) { +EXP K rset(K x,K y) { if(!RLOAD) return krr("main thread only"); if (ROPEN < 0) diff --git a/windows/w32/q.a b/windows/w32/q.a deleted file mode 100644 index eed84b07df92fbddb3ba4507d2ae31e83ff64702..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32526 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 diff --git a/windows/w64/q.a b/windows/w64/q.a deleted file mode 100755 index 9087cd4cf788137ef067aaaf5893ee76d813b7e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33478 zcmeI5&yO6%702K1tPS26aKI$SKp2RDK%Axb*LXLC$U=@1MONY@2qBWNJG(RM*=_Hx zy)!|ZA4oo-9652~%poWK1S`s!BL_|>rzpq#0g(57uV4R|?ispg#D7akf%3Qp7}Qs>g8ZJZ1M(q18g-28{iUY!VX>qC{p z3q*JJRJwdT$h&+z$T1%i@>f0{8_@8G`WR0BhkvXuDVo#s7S|ac^OZVgH(N7wL9!z)Sp} z)$kyH&2hfc;VK1|Zq?$E?(E=nSJLUu7o@ZO_I+oeeca}ag`M47_(u5hE4CstJ57ct z6VhdcL)ZF_kHqP6njMrTRYtWzX;S4gCuwly*$|bXl+7vLRht=@M#=?uzc!-`WRJvU zA4OpAES=-ewB-YJog(U1vNf7f*I42m5)$efTv|9T&Xq1ni7NQGkJK&M>CPiJoUaSf zoy6TJqR!H%zf^B{zt3_+jqr*8V@=BEA<@e4*pXU^lXH-JOFO;JpV)*- z5F#=qjfN zJ9S=4(q$~<8mDO$r;Ay|bzJ2%2gT_cmMz?+Uqopvj<5LMSI=YXgAbCpL<^*aq<0SI z(YtwuHKjrCJkm!Dq-r6Ys+PB-lrA^1niP>EC4@8ZuheQL7SUqlsx!RF_2F*UqP()o z0cTgwSg(roNlAjcRKg1X+2S==)-Z577*UkC=BCopam4~YYbp)0hiay z66h`9Fak#zTYgb|;O_i_|*HI=sBbMmyTGyL)M zycR~{4^IZ?`SV_m6XWk$JfF?MjkccVRS8VD^)$X>ty|j5;YV0d;wbM)`PS<4CV(^4 z@w*BZMzMPG)Qdm(xLO8Ypx{X^mRaXMD7 z<9qV9lRNy4_iT&2*_GpOsjP>?KieYz{YmooG~Tb{;7F@a@+yUp8EesNsMhLRD(lzq z)J>ear|>4HJJhA#GSpgENBHy_DpFV0o8d?JCQjXx^3Jd`wrrR}%ConGp2S?g^l2;`#Q|n56#ah?UfS23nN)Q%r=s@dS(3@zSb?`bgU7U@5-Dn?(i2)sleqh8h<>AOrF28 z9u8mYPMp8*V3r{cj?}uz0CRSPMdB;gx@G+up1O%s_c-RWa)&y}s}w?(I*G4HU0H92 zr*7iZoyW{+?ocOrEuc=~t5LV;eHcYFO{ItN80P46hdRk?0d*2zkvi|gI5FxT#cYu7 zP$zjUpibf|Qdic8;cM6;I>+kGejD?Sx#8HUJIy` z_=?n(^-_51CQjW$m>=F9>LjlP)Jc3r>dN{iFvWa4&$rGo)ngaQykukW+{xED4FbOE zQ=B{b&D42QZ%gFnPTnj|m#bm-iqplmZjP_7>Ks#Nc_jiz{1Ui@Q9wb=M)O9gx2z&@ z-Y+2~sX)c+9^{C-AG39T3oBm8{c~XkQO&CqR;Ep-@fE4_eu)#KZXRo^xIsJ$ zL>)HJmPJUY8#HU4E{GLB@D$IQ7nJo;ET>lw(q6?5H;k};Ncq)+rn4doR)Vn>KC2w! zwp*rG5%A96Y62#6>IU^%bs6TkiI?Xo6#k$*cZDC?Q Date: Sun, 7 Mar 2021 16:33:14 +0000 Subject: [PATCH 24/33] Added R logo --- README.md | 30 +++++++++++++----------------- images/R_logo.png | Bin 0 -> 84708 bytes q/embedr.q | 7 ------- 3 files changed, 13 insertions(+), 24 deletions(-) create mode 100644 images/R_logo.png diff --git a/README.md b/README.md index 6a82331..03d9f68 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # embedR: Embedding R inside q -See +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/kxsystems/embedr)](https://github.com/kxsystems/kafka/releases) [![Travis (.org) branch](https://img.shields.io/travis/kxsystems/embedr/master)](https://travis-ci.org/kxsystems/embedr/branches) + +## Introduction + +[](images/R_logo.png) While **kdb+** is good at pre-processing data it does not have native libraries to analyze scientifically. On the other hand, the programming language **R** is used for the very statistic analysis and therefore has numerous libraries. + +This interface **embedR** embeds R process inside q process and allows user to call R functions from q process after passing data to R side with a simple function. ## Installation @@ -31,7 +37,7 @@ For successful installation you need to set a path to `lib` directory on `R_LIBR ``` -Then execte the commands below at the root directory of this repository: +Then execute the commands below at the root directory of this repository: ```bash @@ -94,27 +100,17 @@ build> cmake --build . --config Release --target install ``` -**Note:** `cmake --build . --config Release --target install` installs the required share object and q files to the `QHOME\[os]64` and `QHOME` directories respectively. If you do not wish to install these files directly, you can execute `cmake --build . --config Release` instead of `cmake --build . --config Release --target install` and move the files from their build location at `build/embedr`. +**Note:** `cmake --build . --config Release --target install` installs the required share object and q files to the `QHOME\w64` and `QHOME` directories respectively. If you do not wish to install these files directly, you can execute `cmake --build . --config Release` instead of `cmake --build . --config Release --target install` and move the files from their build location at `build/embedr`. ## Calling R -When calling R, you need to set `R_HOME`. This can be set as follows: - -```bash - -# Linux/macOS -export R_HOME=`R RHOME` -# Windows -for /f "delims=" %a in ('R RHOME') do @set R_HOME=%a - -``` The library has four main methods: -- `.rk.open`: Initialise embedR. Optional to call. Allows to set verbose mode as `Ropen 1`. -- `.rk.exec`: execute an R command, do not return a result to q. -- `.rk.get`: execute an R command, return the result to q. -- `.rk.set`: set a variable in the R memory space +- `.rk.open`: Initialise embedR. Optional to call. Allows to set verbose mode as `.rk.open 1`. +- `.rk.exec`: Execute an R command, do not return a result to q. +- `.rk.get`: Execute an R command, return the result to q. +- `.rk.set`: Set a variable in the R memory space. ## Documentation diff --git a/images/R_logo.png b/images/R_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0fae87396526fd39cef9af9281d25998a5e9bd0d GIT binary patch literal 84708 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

u6$aAR)Bh7=j>N$&w=)oB;5P-J$yM^3U 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 literal 0 HcmV?d00001 diff --git a/q/embedr.q b/q/embedr.q index efe73a0..cd97ae4 100644 --- a/q/embedr.q +++ b/q/embedr.q @@ -49,10 +49,3 @@ Rinstall:.rk.install; Rnew :.rk.new; Roff :.rk.off; Rset :.rk.set; - -/*-----------------------------------------------*/ -/* Start embedR */ -/*-----------------------------------------------*/ - -// Some time conversion from R -> q do not work without doing .rk.set first. -("kdbtimestamp_"; "kdbtimespan_"; "kdbdate_"; "kdbmonth_"; "kdbminute_"; "kdbsecond_") .rk.set' `timestamp`timespan`date`month`minute`second$/: 2000.01.01D00:00:00.000000000; \ No newline at end of file From 24cc26460adefd4339857153ac642553ef8b8bf7 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Mon, 8 Mar 2021 02:30:29 +0900 Subject: [PATCH 25/33] Fixed visibility issue for Linux/Mac --- include/common_R_interface/common.h | 4 ++-- include/embedr.h | 1 - q/embedr.q | 3 +++ src/common_R_interface/q2r.c | 1 - src/embedr_r2q.c | 10 +--------- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/include/common_R_interface/common.h b/include/common_R_interface/common.h index 039e26f..2d3b72f 100644 --- a/include/common_R_interface/common.h +++ b/include/common_R_interface/common.h @@ -36,11 +36,11 @@ extern const int kdbSecOffset; /** * @brief Attribute set to q time related types when it is converted to R object. */ -SEXP R_UnitsSymbol; +extern SEXP R_UnitsSymbol; /** * @brief Attribute set to q time related types when it is converted to R object. */ -SEXP R_TzSymbol; +extern SEXP R_TzSymbol; /*-----------------------------------------------*/ /* Functions */ diff --git a/include/embedr.h b/include/embedr.h index 5fbb0dc..5ff3458 100644 --- a/include/embedr.h +++ b/include/embedr.h @@ -19,7 +19,6 @@ #include #include #include -#include #define EXP #endif #include diff --git a/q/embedr.q b/q/embedr.q index cd97ae4..312fbe4 100644 --- a/q/embedr.q +++ b/q/embedr.q @@ -49,3 +49,6 @@ Rinstall:.rk.install; Rnew :.rk.new; Roff :.rk.off; Rset :.rk.set; + +// Some time conversion from R -> q do not work without doing .rk.set first. +("kdbtimestamp_"; "kdbtimespan_"; "kdbdate_"; "kdbmonth_"; "kdbminute_"; "kdbsecond_") .rk.set' `timestamp`timespan`date`month`minute`second$/: 2000.01.01D00:00:00.000000000; diff --git a/src/common_R_interface/q2r.c b/src/common_R_interface/q2r.c index d49c9cd..2f1d43b 100644 --- a/src/common_R_interface/q2r.c +++ b/src/common_R_interface/q2r.c @@ -14,7 +14,6 @@ /*-----------------------------------------------*/ #include "common.h" -//#include "q2r.h" /*-----------------------------------------------*/ /* Global Variable */ diff --git a/src/embedr_r2q.c b/src/embedr_r2q.c index 77bbbcc..c0b6f2b 100644 --- a/src/embedr_r2q.c +++ b/src/embedr_r2q.c @@ -18,7 +18,6 @@ #pragma comment(lib, "ws2_32.lib") SOCKET spair[2]; #else -#include #define SOCKET_ERROR -1 I spair[2]; #endif @@ -36,7 +35,7 @@ EXP K rclose(K x); EXP K rexec(K x); EXP K rget(K x); EXP K rset(K x,K y); -K rcmd(int type,K x); +static K rcmd(int type,K x); /*-----------------------------------------------*/ /* Global Variable */ @@ -126,13 +125,6 @@ static char * getkstring(K x) { * The public interface used from Q. */ -/* -#ifdef _WIN32 -static SOCKET spair[2]; -#else -static int spair[2]; -#endif -*/ void* pingthread; V* pingmain(V* v){ From bf08e7b5e82cb2168e985d616a8c936e54e3579b Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Mon, 8 Mar 2021 19:09:29 +0900 Subject: [PATCH 26/33] Added document --- docs/examples.md | 79 ++++++++++++++++ docs/index.md | 101 ++++++++++++++++++++ docs/reference.md | 230 +++++++++++++++++++++++++++++++++++++++++++++ images/figure4.svg | 146 ++++++++++++++++++++++++++++ q/embedr.q | 3 - 5 files changed, 556 insertions(+), 3 deletions(-) create mode 100644 docs/examples.md create mode 100644 docs/index.md create mode 100644 docs/reference.md create mode 100644 images/figure4.svg diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..bd499c5 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,79 @@ +--- +title: embedR Examples +date: April 2020 +description: Examples of embedR +keywords: interface, kdb+, q, r, +--- + +# embedR Examples + +An example is outlined below, using q to subselect some data and then passing it to R for graphical display. + +```q +q)select count i by date from trade +date | x +----------| -------- +2014.01.07| 29205636 +2014.01.08| 30953246 +2014.01.09| 30395962 +2014.01.10| 29253110 +2014.01.13| 32763630 +2014.01.14| 29721162 +2014.01.15| 30035729 +.. + +q)//extract mid prices in 5 minute bars +q)mids:select mid:last .5*bid+ask by time:0D00:05 xbar date+time from quotes where date=2014.01.17,sym=`IBM,time within 09:30 16:00 +q)mids +time | mid +-----------------------------| -------- +2014.01.15D09:30:00.000000000| 185.92 +2014.01.15D09:35:00.000000000| 185.74 +2014.01.15D09:40:00.000000000| 186.11 +2014.01.15D09:45:00.000000000| 186.36 +2014.01.15D09:50:00.000000000| 186.5 +2014.01.15D09:55:00.000000000| 186.98 +2014.01.15D10:00:00.000000000| 187.45 +2014.01.15D10:05:00.000000000| 187.48 +2014.01.15D10:10:00.000000000| 187.66 +.. + +q)Load in R +q)\l init.q +q)//Pass the table into the R memory space +q).rk.set["mids";mids] +q)//Graph it +q).rk.exec["plot(mids$time, mids$mid, type='l', xlab='time', ylab='price')"] +``` + +This will produce a plot as shown in Figure 4: + +![Quote mid price plot drawn from q](../images/figure4.svg) +_Figure 4: Quote mid price plot drawn from q_ + +```q +q)//Save as a PDF file +q).rk.exec "pdf('test.pdf')" +q)//Close plot window +q).rk.off[] +``` + +If the q and R installations are running remotely from the user on a Linux machine, the graphics can be seen locally using X11 forwarding over SSH. + +Aside from using R’s powerful graphics, this mechanism also allows you to call R analytics from within q. + +Note that R’s timezone setting affects date transfers between R and q. For example, in the R server: + +```q +q).rk.exec "Sys.setenv(TZ='GMT')" +q).rk.get "date()" +"Fri Feb 3 06:33:43 2012" +q).rk.exec "Sys.setenv(TZ='EST')" +q).rk.get "date()" +"Fri Feb 3 01:33:57 2012" +``` + + +Knowledge Base: [Timezones and Daylight Saving Time]([../../kb/timezones.md](https://code.kx.com/q/kb/timezones/)) + +Further examples are provided in the `examples` folder of [KxSystems/embedR](https://github.com/KxSystems/embedR). \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..0c3feb3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,101 @@ +--- +title: embedR, an interface for calling R from q +description: embedR is an interface that allows the R programming language to be invoked by q programs +keywords: interface, kdb+, q, r +hero: Fusion for Kdb+ +--- + +# embedR + +## Introduction + +Kdb+ and R are complementary technologies. Kdb+ is the world’s leading timeseries database and incorporates a programming language called q. [R](https://www.r-project.org/) is a programming language and environment for statistical computing and graphics. Both are tools used by data scientists to interrogate and analyze data. Their features sets overlap in that they both: + +- are interactive development environments +- incorporate vector languages +- have a built-in set of statistical operations +- can be extended by the user +- are well suited for both structured and ad-hoc analysis + +They do however have several differences: + +- q can store and analyze petabytes of data directly from disk whereas R is limited to reading data into memory for analysis +- q has a larger set of datatypes including extensive temporal times (timestamp, timespan, time, second, minute, date, month) which make temporal arithmetic straightforward +- R contains advanced graphing capabilities whereas q does not +- built-in routines in q are generally faster than R +- R has a more comprehensive set of pre-built statistical routines + +When used together, q and R provide an excellent platform for easily performing advanced statistical analysis and visualization on large volumes of data. + +R can be called as a server from q, and vice-versa. + +### q and R working together + +Given the complementary characteristics of the two languages, it is important to utilize their respective strengths. +All the analysis could be done in q; the q language is sufficiently flexible and powerful to replicate any of the pre-built R functions. +Below are some best practice guidelines, although where the line is drawn between q and R will depend on both the system architecture and the strengths of the data scientists using the system. + +- Do as much of the analysis as possible in q. This is because q analyzes the data directly from the disk and it is always most efficient to do as much work as possible as close to the data as possible. Whenever possible, avoid extracting large raw datasets from q and if extractions are required, use q to create smaller aggregated datasets +- Do not re-implement tried and tested R routines in q unless they either + * can be written more efficiently in q and are going to be called often + * require more data than is feasible to ship to the R installation +- Use R for data visualization + +Considering the potential size of the data, it is probably more likely that the kdb+ installation containing the data will be hosted remotely from the user. + +### What Does embedR Provide? + +What **embedR** provides is to embed R inside q and invoke R routines. can only be used if the q and R installations are installed on the same server. + +## Install + +Work for both 32-bit and 64-bit kdb+. + +Pre-built packages are available from [here](https://github.com/KxSystems/embedR/releases/tag/v1.2.1) for: + +- Linux +- macOS + +If the appropriate build is not available on your target system, download from [KxSystems/embedR](https://github.com/KxSystems/embedR) and follow the installation instruction in `README.md`. + +The `R_HOME` environment variable must be set prior to starting q. +To find out what that should be, run R from the Bash shell and see the result of `R.home()` + +```r +> R.home() +[1] "/Library/Frameworks/R.framework/Resources" +``` + +and then set it accordingly in your environment; e.g. for macOS with a Bash shell + +```bash +$export R_HOME=/Library/Frameworks/R.framework/Resources +``` + +Optional additional environment variables are `R_SHARE_DIR`, `R_INCLUDE_DIR`, `LD_LIBRARY_PATH` (for libR.so). + +## Testing + +User can test embedR with the test script named `test.q` provided in `tests` folder of [KxSystems/embedR](https://github.com/KxSystems/embedR). + +Procedure is simple. Go to `tests` directory and run `test.q`. + +```bash +$ cd embedR/tests +$ q test.q +0i + ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + Test Start!! + Score: 0/0 + Fail: 0/0 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + +... + ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + Completed!! + Score: 77/77 + Fail: 0/77 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +``` diff --git a/docs/reference.md b/docs/reference.md new file mode 100644 index 0000000..f8c3569 --- /dev/null +++ b/docs/reference.md @@ -0,0 +1,230 @@ +--- +title: embedR User Guide +date: April 2020 +description: Lists functions available for use within the embedR +keywords: interface, kdb+, q, r, +--- + +# :fontawesome-brands-r-project: embedR User Guide + +

+:fontawesome-brands-superpowers: [Fusion for kdb+](../fusion.md) +
+:fontawesome-brands-github: +[kxsystems/embedr](https://github.com/kxsystems/embedr) + +A shared library can be loaded which brings R into the q memory space, +meaning all the R statistical routines and graphing capabilities can be invoked directly from q. +Using this method means data is not passed between remote processes. + +The library has five methods: + +```txt + //Connection + .rk.open: Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1 + .rk.close: Close internal R connection + + //Execution + .rk.exec: Run an R command, do not return a result + .rk.get: Run an R command, return the result to q + .rk.set: Set a variable in the R memory space + + //Graphic + .rk.dev Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically) + .rk.off Close plot window + + //Utility + .rk.install Install package in embeded R process over the connection + +``` + +### Connection + +#### .rk.open + +_Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1_ + +Syntax: `.rk.open[signal]` + +Where + +- `signal` is an integer which is one of empty, 0 or 1. + * 0 or empty: quiet mode + * 1: verbose mode + +```q +q).rk.open[] +q)//or +q).rk.open[1] +``` + +!!! note "Note" + + As of version 2.0 Ropen was migrated to .rk.open. Ropen will be depricated in version 2.1. + +#### .rk.close + +_Close internal R connection_ + +Syntax: `.rk.close[]` + +!!! note "Note" + + As of version 2.0 Rclose was migrated to .rk.close. Rclose will be depricated in version 2.1. + +### Execution + +#### .rk.exec + +_Run an R command, do not return a result_ + +Synatax: `.rk.exec[command]` + +Where + +- `command` is an R expression to execute in R process + +```q +q).rk.exec "Sys.setenv(TZ = 'UTC')" +q).rk.exec "library(xts)" +Loading required package: zoo + +Attaching package: ‘zoo’ + +The following objects are masked from ‘package:base’: + + as.Date, as.Date.numeric +``` + +#### .rk.get + +_Run an R command, return the result to q_ + +Synatax: `.rk.get[rexp]` + +Where + +- `rexp` is a string denoting an R expression to execute in R process + +```q +q).rk.exec "today <- as.Date('2020-04-01')" +q).rk.get "today" +,2019.04.01 +``` + +!!! note "Note" + + As of version 2.0 Rget was migrated to .rk.get. Rget will be depricated in version 2.1. + +#### .rk.set + +_Set a variable in the R memory space_ + +Synatax: `.rk.set[rvar; qvar]` + +Where + +- `rvar` is a string denoting a name of R variable used in R process +- `qvar` is any q object used to assign to the R variable in R process + +```q +q).rk.set["mnth"; `month$/:2010.01.29 2020.04.02] +q).rk.get "mnth" +2010.01 2020.04m +``` + +!!! note "Note" + + As of version 2.0 Rset was migrated to .rk.set. Rset will be depricated in version 2.1. + +### Graphic + +#### .rk.new + +_Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically)_ + +Syntax: `.rk.new[]` + +!!! note "Note" + + As of version 2.0 Rnew was migrated to .rk.new. Rnew will be depricated in version 2.1. + +#### .rk.off + +_Close plot window_ + +Syntax: `.rk.off[]` + +To close the graphics window, use `dev.off()` rather than the close button on the window. + +!!! note "Note" + + As of version 2.0 Roff was migrated to .rk.off. Roff will be depricated in version 2.1. + +### Utility + +#### .rk.install + +_Install package in embeded R process over the connection_ + +Syntax: `.rk.install[package]` + +Where + +- `package` is a symbol denoting the name of a package to install in the R process + +You must be a super user who has an access to the library directory. + +The result is any information regarding install if the package is installed for the first time; otherwise nothing is returned. + +```q +q).rk.install["psy"] +Installing package into ‘/usr/lib64/R/library’ +(as ‘lib’ is unspecified) +trying URL 'https://cloud.r-project.org/src/contrib/psy_1.1.tar.gz' +Content type 'application/x-gzip' length 36107 bytes (35 KB) +================================================== +downloaded 35 KB + +* installing *source* package ‘psy’ ... +** package ‘psy’ successfully unpacked and MD5 sums checked +** using staged installation +** R +** data +** byte-compile and prepare package for lazy loading +** help +*** installing help indices + converting help for package ‘psy’ + finding HTML links ... done + ckappa html + cronbach html + ehd html + expsy html + fpca html + icc html + lkappa html + mdspca html + mtmm html + psy-package html + scree.plot html + sleep html + sphpca html + wkappa html +** building package indices +** testing if installed package can be loaded from temporary location +** testing if installed package can be loaded from final location +** testing if installed package keeps a record of temporary installation path +* DONE (psy) +Making 'packages.html' ... done + +The downloaded source packages are in + ‘/tmp/Rtmp8Pnq5n/downloaded_packages’ +Updating HTML index of packages in '.Library' +``` + +!!! note "Note" + + As of version 2.0 Rinstall was migrated to .rk.install. Rinstall will be depricated in version 2.1. + + +Simple examples of embedR are available in [Examples](examples.md) page. \ No newline at end of file diff --git a/images/figure4.svg b/images/figure4.svg new file mode 100644 index 0000000..b3e75ae --- /dev/null +++ b/images/figure4.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/q/embedr.q b/q/embedr.q index 312fbe4..cd97ae4 100644 --- a/q/embedr.q +++ b/q/embedr.q @@ -49,6 +49,3 @@ Rinstall:.rk.install; Rnew :.rk.new; Roff :.rk.off; Rset :.rk.set; - -// Some time conversion from R -> q do not work without doing .rk.set first. -("kdbtimestamp_"; "kdbtimespan_"; "kdbdate_"; "kdbmonth_"; "kdbminute_"; "kdbsecond_") .rk.set' `timestamp`timespan`date`month`minute`second$/: 2000.01.01D00:00:00.000000000; From 152aacd843c6d9f47b382f3aa49c9c234bb4d3e7 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Mon, 8 Mar 2021 19:13:26 +0900 Subject: [PATCH 27/33] Changed size of logo --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 0c3feb3..7b73615 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,7 +5,7 @@ keywords: interface, kdb+, q, r hero: Fusion for Kdb+ --- -# embedR +# embedR ## Introduction From 08603bc31f8f68c702cf689365a1908963f1bef3 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Mon, 8 Mar 2021 19:23:52 +0900 Subject: [PATCH 28/33] Removed internal macros --- docs/index.md | 22 +++++----------------- docs/reference.md | 36 +++++++++++++++--------------------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/docs/index.md b/docs/index.md index 7b73615..fbb67cd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -58,20 +58,6 @@ Pre-built packages are available from [here](https://github.com/KxSystems/embedR If the appropriate build is not available on your target system, download from [KxSystems/embedR](https://github.com/KxSystems/embedR) and follow the installation instruction in `README.md`. -The `R_HOME` environment variable must be set prior to starting q. -To find out what that should be, run R from the Bash shell and see the result of `R.home()` - -```r -> R.home() -[1] "/Library/Frameworks/R.framework/Resources" -``` - -and then set it accordingly in your environment; e.g. for macOS with a Bash shell - -```bash -$export R_HOME=/Library/Frameworks/R.framework/Resources -``` - Optional additional environment variables are `R_SHARE_DIR`, `R_INCLUDE_DIR`, `LD_LIBRARY_PATH` (for libR.so). ## Testing @@ -80,9 +66,11 @@ User can test embedR with the test script named `test.q` provided in `tests` fol Procedure is simple. Go to `tests` directory and run `test.q`. +**Note:** If you don't have multi-threading environment eliminate `-s 2` flag. + ```bash $ cd embedR/tests -$ q test.q +$ q test.q -test_data_frame true -s 2 0i +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- @@ -95,7 +83,7 @@ $ q test.q +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- Completed!! - Score: 77/77 - Fail: 0/77 + Score: 78/78 + Fail: 0/78 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ``` diff --git a/docs/reference.md b/docs/reference.md index f8c3569..d033b01 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -5,13 +5,7 @@ description: Lists functions available for use within the embedR keywords: interface, kdb+, q, r, --- -# :fontawesome-brands-r-project: embedR User Guide - -
-:fontawesome-brands-superpowers: [Fusion for kdb+](../fusion.md) -
-:fontawesome-brands-github: -[kxsystems/embedr](https://github.com/kxsystems/embedr) +# embedR User Guide A shared library can be loaded which brings R into the q memory space, meaning all the R statistical routines and graphing capabilities can be invoked directly from q. @@ -38,9 +32,9 @@ The library has five methods: ``` -### Connection +## Connection -#### .rk.open +### .rk.open _Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1_ @@ -49,8 +43,8 @@ Syntax: `.rk.open[signal]` Where - `signal` is an integer which is one of empty, 0 or 1. - * 0 or empty: quiet mode - * 1: verbose mode + * 0 or empty: quiet mode + * 1: verbose mode ```q q).rk.open[] @@ -62,7 +56,7 @@ q).rk.open[1] As of version 2.0 Ropen was migrated to .rk.open. Ropen will be depricated in version 2.1. -#### .rk.close +### .rk.close _Close internal R connection_ @@ -72,9 +66,9 @@ Syntax: `.rk.close[]` As of version 2.0 Rclose was migrated to .rk.close. Rclose will be depricated in version 2.1. -### Execution +## Execution -#### .rk.exec +### .rk.exec _Run an R command, do not return a result_ @@ -96,7 +90,7 @@ The following objects are masked from ‘package:base’: as.Date, as.Date.numeric ``` -#### .rk.get +### .rk.get _Run an R command, return the result to q_ @@ -116,7 +110,7 @@ q).rk.get "today" As of version 2.0 Rget was migrated to .rk.get. Rget will be depricated in version 2.1. -#### .rk.set +### .rk.set _Set a variable in the R memory space_ @@ -137,9 +131,9 @@ q).rk.get "mnth" As of version 2.0 Rset was migrated to .rk.set. Rset will be depricated in version 2.1. -### Graphic +## Graphic -#### .rk.new +### .rk.new _Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically)_ @@ -149,7 +143,7 @@ Syntax: `.rk.new[]` As of version 2.0 Rnew was migrated to .rk.new. Rnew will be depricated in version 2.1. -#### .rk.off +### .rk.off _Close plot window_ @@ -161,9 +155,9 @@ To close the graphics window, use `dev.off()` rather than the close button on th As of version 2.0 Roff was migrated to .rk.off. Roff will be depricated in version 2.1. -### Utility +## Utility -#### .rk.install +### .rk.install _Install package in embeded R process over the connection_ From abba8864a2a22ff63b7bf30d8eb015d8a69a295d Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Mon, 8 Mar 2021 19:23:52 +0900 Subject: [PATCH 29/33] Removed internal macros --- docs/index.md | 24 +++++------------------- docs/reference.md | 36 +++++++++++++++--------------------- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/docs/index.md b/docs/index.md index 7b73615..36566a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -58,31 +58,17 @@ Pre-built packages are available from [here](https://github.com/KxSystems/embedR If the appropriate build is not available on your target system, download from [KxSystems/embedR](https://github.com/KxSystems/embedR) and follow the installation instruction in `README.md`. -The `R_HOME` environment variable must be set prior to starting q. -To find out what that should be, run R from the Bash shell and see the result of `R.home()` - -```r -> R.home() -[1] "/Library/Frameworks/R.framework/Resources" -``` - -and then set it accordingly in your environment; e.g. for macOS with a Bash shell - -```bash -$export R_HOME=/Library/Frameworks/R.framework/Resources -``` - -Optional additional environment variables are `R_SHARE_DIR`, `R_INCLUDE_DIR`, `LD_LIBRARY_PATH` (for libR.so). - ## Testing User can test embedR with the test script named `test.q` provided in `tests` folder of [KxSystems/embedR](https://github.com/KxSystems/embedR). Procedure is simple. Go to `tests` directory and run `test.q`. +**Note:** If you don't have multi-threading environment eliminate `-s 2` flag. + ```bash $ cd embedR/tests -$ q test.q +$ q test.q -test_data_frame true -s 2 0i +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- @@ -95,7 +81,7 @@ $ q test.q +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- Completed!! - Score: 77/77 - Fail: 0/77 + Score: 78/78 + Fail: 0/78 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ``` diff --git a/docs/reference.md b/docs/reference.md index f8c3569..d033b01 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -5,13 +5,7 @@ description: Lists functions available for use within the embedR keywords: interface, kdb+, q, r, --- -# :fontawesome-brands-r-project: embedR User Guide - -
-:fontawesome-brands-superpowers: [Fusion for kdb+](../fusion.md) -
-:fontawesome-brands-github: -[kxsystems/embedr](https://github.com/kxsystems/embedr) +# embedR User Guide A shared library can be loaded which brings R into the q memory space, meaning all the R statistical routines and graphing capabilities can be invoked directly from q. @@ -38,9 +32,9 @@ The library has five methods: ``` -### Connection +## Connection -#### .rk.open +### .rk.open _Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1_ @@ -49,8 +43,8 @@ Syntax: `.rk.open[signal]` Where - `signal` is an integer which is one of empty, 0 or 1. - * 0 or empty: quiet mode - * 1: verbose mode + * 0 or empty: quiet mode + * 1: verbose mode ```q q).rk.open[] @@ -62,7 +56,7 @@ q).rk.open[1] As of version 2.0 Ropen was migrated to .rk.open. Ropen will be depricated in version 2.1. -#### .rk.close +### .rk.close _Close internal R connection_ @@ -72,9 +66,9 @@ Syntax: `.rk.close[]` As of version 2.0 Rclose was migrated to .rk.close. Rclose will be depricated in version 2.1. -### Execution +## Execution -#### .rk.exec +### .rk.exec _Run an R command, do not return a result_ @@ -96,7 +90,7 @@ The following objects are masked from ‘package:base’: as.Date, as.Date.numeric ``` -#### .rk.get +### .rk.get _Run an R command, return the result to q_ @@ -116,7 +110,7 @@ q).rk.get "today" As of version 2.0 Rget was migrated to .rk.get. Rget will be depricated in version 2.1. -#### .rk.set +### .rk.set _Set a variable in the R memory space_ @@ -137,9 +131,9 @@ q).rk.get "mnth" As of version 2.0 Rset was migrated to .rk.set. Rset will be depricated in version 2.1. -### Graphic +## Graphic -#### .rk.new +### .rk.new _Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically)_ @@ -149,7 +143,7 @@ Syntax: `.rk.new[]` As of version 2.0 Rnew was migrated to .rk.new. Rnew will be depricated in version 2.1. -#### .rk.off +### .rk.off _Close plot window_ @@ -161,9 +155,9 @@ To close the graphics window, use `dev.off()` rather than the close button on th As of version 2.0 Roff was migrated to .rk.off. Roff will be depricated in version 2.1. -### Utility +## Utility -#### .rk.install +### .rk.install _Install package in embeded R process over the connection_ From 99fefbf5c8c9a4457986adc12f1084e5d2875b8f Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Thu, 11 Mar 2021 04:34:32 +0900 Subject: [PATCH 30/33] Modified Rget to be compatible as much as possible --- q/embedr.q | 50 +++++++- tests/test.q | 4 +- tests/test_old.q | 319 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 tests/test_old.q diff --git a/q/embedr.q b/q/embedr.q index cd97ae4..21136ef 100644 --- a/q/embedr.q +++ b/q/embedr.q @@ -42,9 +42,57 @@ LIBPATH:`:embedr 2: // The following block of functions are aliases for the above // these aliases will be removed in a future version of the code +Ropen :.rk.open; Rclose :.rk.close; Rcmd :.rk.exec; -Rget :.rk.get; +Rget :{[variable] + result:.rk.get[variable]; + $[ + // Foreign type + 112h ~ type result; + result; + // Table has extra column + 98h ~ type result; + update row.names:1+i from result; + // bool -> int, month -> int, datetime -> timestamp, timespan -> float, minute -> int, second -> int + any 1 13 15 16 17 18h in key typemap:group abs type each result; + [ + if[count typemap 1h; + $[99h ~ type result; + result:` _ @[(enlist[`]!enlist (::)), result; typemap 1h; `int$]; + result:1 _ @[(::), result; 1+typemap 1h; `int$] + ] + ]; + if[count typemap 13h; + $[99h ~ type result; + result:` _ @[(enlist[`]!enlist (::)), result; typemap 13h; `int$]; + result:1 _ @[(::), result; 1+typemap 13h; `int$] + ] + ]; + if[count typemap 15h; + $[99h ~ type result; + result:` _ @[(enlist[`]!enlist (::)), result; typemap 15h; `timestamp$]; + result:1 _ @[(::), result; 1+typemap 15h; `timestamp$] + ] + ]; + if[count typemap 16h; + $[99h ~ type result; + result:` _ @[(enlist[`]!enlist (::)), result; typemap 16h; {[timespan] 86400 * timespan % 1D}]; + result:1 _ @[(::), result; 1+typemap 16h; {[timespan] 86400 * timespan % 1D}] + ] + ]; + if[sum count each typemap 17 18h; + $[99h ~ type result; + result:` _ @[(enlist[`]!enlist (::)), result; raze typemap 17 18h; `int$]; + result:1 _ @[(::), result; 1+raze typemap 17 18h; `int$] + ] + ]; + result + ]; + // Nothing to do + result + ] + } Rinstall:.rk.install; Rnew :.rk.new; Roff :.rk.off; diff --git a/tests/test.q b/tests/test.q index d2bbd70..ffc822a 100644 --- a/tests/test.q +++ b/tests/test.q @@ -46,7 +46,7 @@ EQUAL:{[id;x;y] \S 42 //Set console width -\c 25 200 +\c 25 300 // set verbose mode .rk.open 1 @@ -232,7 +232,7 @@ EQUAL[67; count .rk.get`x; 1]; EQUAL[68; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; EQUAL[69; .rk.get["c()"]; .rk.get"NULL"]; -EQUAL[70; (); .rk.get"c()"]; +EQUAL[70; .rk.get"c()"; ()]; EQUAL[71; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; .rk.set[`x;1] EQUAL[72; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? diff --git a/tests/test_old.q b/tests/test_old.q new file mode 100644 index 0000000..f0ff706 --- /dev/null +++ b/tests/test_old.q @@ -0,0 +1,319 @@ +/ +* test R server for Q +* These tests are for checking compatability with v1.2. +\ + +//%% Commandline arguments %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +COMMANDLINE_ARGS: .Q.opt .z.x; +if[`test_data_frame in key COMMANDLINE_ARGS; @[`COMMANDLINE_ARGS; `test_data_frame; {lower first x}]]; + +//%% Define Test Function/Variable %%//vvvvvvvvvvvvvvvvvvvvvvvvv/ + +HRULE:40#"+-"; +TESTCASE:0i; +SUCCESS:0i; +FAILURE:0i; + +PROGRESS:{[checkpoint] + -1 ""; + -1 HRULE; + -1 "\t",checkpoint; + -1 "\tScore:\t",string[SUCCESS],"/",string TESTCASE; + -1 "\tFail:\t",string[FAILURE],"/",string TESTCASE; + -1 HRULE; + -1 ""; + }; + +EQUAL:{[id;x;y] + TESTCASE+:1; + $[x~y; + SUCCESS+:1; + [FAILURE+:1; -1 "[",string[id],"] Fail:", -3!x] + ]; + }; + +//%% System Setting %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +//Load embedR file +\l q/embedr.q + +//Set seed 42 +\S 42 + +//Set console width +\c 25 300 + +// set verbose mode +Ropen 1 // set verbose mode + +//%% Test %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ + +//Numerical Array//-------------------------/ + +PROGRESS["Test Start!!"]; + +Rcmd "a=array(1:24,c(2,3,4))" +EQUAL[1; Rget "dim(a)"; 2 3 4i]; +EQUAL[2; Rget "a"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; + +if[3<=.z.K; Rset["a";2?0Ng]] +EQUAL[3; Rget "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed1-f4df-58fcdedd8376")]; + +Rcmd "b= 2 == array(1:24,c(2,3,4))" +EQUAL[4; Rget "dim(b)"; 2 3 4i]; +// Type mapping issue of v1.2 +EQUAL[5; Rget "b"; ((0 0 0i;1 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i);(0 0 0i;0 0 0i))]; + +EQUAL[6; Rget "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22 24.2 26.4))]; + +Rset["xyz";1 2 3i] +EQUAL[7; Rget "xyz"; 1 2 3i]; + +EQUAL[8; Rget "pi"; (), acos -1]; +EQUAL[9; Rget "2+3"; (), 5f]; +EQUAL[10; Rget "11:11"; (), 11i]; +EQUAL[11; Rget "11:15"; 11 12 13 14 15i]; +a:Rget "matrix(1:6,2,3)" +EQUAL[12; a[1]; 2 4 6i]; +Rcmd "m=array(1:24,c(2,3,4))" +EQUAL[13; Rget "m"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; +EQUAL[14; Rget "length(m)"; (), 24i]; +EQUAL[15; Rget "dim(m)"; 2 3 4i]; +EQUAL[16; Rget "c(1,2,Inf,-Inf,NaN,NA)"; 1 2 0w -0w 0n 0n]; + +PROGRESS["Numeric Array Finished!!"]; + +//Plot Functionality//----------------------/ + +Rcmd "pdf(tempfile(\"t1\",fileext=\".pdf\"))" +Rcmd "plot(c(2,3,5,7,11))" +Rcmd "dev.off()" + +//Function Test//---------------------------/ + +Rcmd "x=factor(c('one','two','three','four'))" +EQUAL[17; Rget "x"; `one`two`three`four]; +EQUAL[18; Rget "mode(x)"; "numeric"]; +EQUAL[19; Rget "typeof(x)"; "integer"]; +// Type mapping issue of v1.2 +EQUAL[20; Rget "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 1 0 0N 1 1 0i]; +Rcmd "foo <- function(x,y) {x + 2 * y}" +Rget "foo" +EQUAL[21; Rget "typeof(foo)"; "closure"]; +EQUAL[22; Rget "foo (5,3)"; (), 11f]; + +PROGRESS["Function Test Finished!!"]; + +//Object//-----------------------------------/ + +// Old output is: +// ((`names;`class)!(("statistic";"parameter";"p.value";"null.value";"alternative";"method";"data.name");"htest");((,`names!,,"W";,0f);();,0.1;(,`names!,"location shift";,0f);"two.sided";"Wilcoxon rank sum exact test";"c(1, 2, 3) and c(4, 5, 6)")) +// New output is: +// `statistic`parameter`p.value`null.value`alternative`method`data.name!((,`W)!,0f;();,0.1;(,`location shift)!,0f;"two.sided";"Wilcoxon rank sum exact test";"c(1, 2, 3) and c(4, 5, 6)") +Rget "wilcox.test(c(1,2,3),c(4,5,6))" +Rcmd "data(OrchardSprays)" +a:Rget "OrchardSprays" +a + +// to install package in non-interactive way +// install.packages("zoo", repos="http://cran.r-project.org") +Rget"install.packages" +//'Broken R object. +EQUAL[23; Rget".GlobalEnv"; "environment"]; +//"environment" +EQUAL[24; Rget"emptyenv()"; "environment"]; +//"environment" +EQUAL[25; Rget".Internal"; "special"]; +//"special" +EQUAL[26; @[Rcmd; "typeof("; like[;"incomplete: *"]]; 1b]; +EQUAL[27; @[Rcmd; "typeof()"; like[;"eval error*"]]; 1b]; +EQUAL[28; Rget each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"dim";"length";"names";".External"); ("builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin")]; +Rget "getGeneric('+')" + +EQUAL[29; Rget"as.raw(10)"; (), 0x0a]; +// Type mapping issue of v1.2 +EQUAL[30; Rget"as.logical(c(1,FALSE,NA))"; 1 0 0Ni]; + +PROGRESS["Object Test Finished!!"]; + +//Table//-----------------------------------/ + +// data.frame +EQUAL[31; Rget"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=TRUE)"; flip `a`b`row.names!(1 2 3i;`a`b`c;1 2 3)]; +EQUAL[32; Rget"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b`row.names!(1 2 3i;(enlist "a";enlist "b";enlist "c");1 2 3)]; +EQUAL[33; Rget"data.frame(a=1:3)"; flip `a`row.names!(1 2 3i;1 2 3)]; +EQUAL[34; Rget"data.frame()"; ()]; + +PROGRESS["Table Test Finished!!"]; + +//Dictionary//------------------------------/ + +Rset["dictI"; `a`b`c!1 2 3i]; +// Type mapping issue in v1.2 +// Key has type 0 +EQUAL[35; Rget"dictI"; (enlist[`names]!enlist 1#/:("a"; "b"; "c"); 1 2 3i)]; +Rset["dictJ"; `a`b`c!1 2 3]; +// Type mapping issue in v1.2 +// Key has type 0 +EQUAL[36; Rget"dictJ"; (enlist[`names]!enlist 1#/:("a"; "b"; "c"); 1 2 3f)]; +Rset["dictB"; `a`b`c!101b]; +// Type mapping issue in v1.2 +// Key has type 0 +EQUAL[37; Rget"dictB"; (enlist[`names]!enlist 1#/:("a"; "b"; "c"); 1 0 1i)]; +Rset["dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; +EQUAL[38; Rget"dictP"; 2020.04.13D06:08:03.712336128 2020.04.13D06:08:03.712336128 2020.04.13D06:08:03.712336128]; + +PROGRESS["Dictionary Test Finished!!"]; + +//Time//------------------------------------/ + +// timestamp +Rset["tmstp"; 2020.03.16D17:30:45.123456789]; +// Calculation is not acculate in v1.2. +EQUAL[39; Rget"tmstp"; (), 2020.03.16D17:30:45.123456768]; + +// month +Rset["mnth"; `month$/:2020.04.02 2010.01.29] +EQUAL[40; Rget"mnth"; 243 120i]; + +// dates +EQUAL[41; Rget"as.Date('2005-12-31')"; (), 2005.12.31]; +EQUAL[42; Rget"as.Date(NA)"; (), 0Nd]; +EQUAL[43; Rget"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; + +// datetime +Rcmd["Sys.setenv(TZ='UTC')"]; +// Type mapping issue in v1.2 +EQUAL[44; Rget"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (), 2018.02.18D04:00:01.000000000]; +// Type mapping issue in v1.2 +EQUAL[45; Rget"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; 2015.03.16D17:30:00.000000000 1978.06.01D12:30:59.000000000]; +// POSIXlt is not supported +EQUAL[46; Rget"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +Rset["dttm"; 2018.02.18T04:00:01.000z]; +// Type mapping issue in v1.2 +EQUAL[47; Rget"dttm"; (), 2018.02.18D04:00:01.000000000]; + +// days +Rset["days"; 1D 2D]; +// Type mapping issue in v1.2 +EQUAL[48; Rget"days"; 86400 172800f]; +// Type mapping issue in v1.2 +// Key is type 0. +EQUAL[49; Rget"as.difftime(c(1, 2), units=\"days\")"; ((`class;`units)!("difftime";"days");1 2f)]; + +// timespan +Rset["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] +// Type mapping issue in v1.2 +EQUAL[50; Rget"tmspans"; (43200f; 86400 * 0D04:20:17.123456789 % 1D; 1.2e-08)]; + +// minute +Rset["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +// Type mapping issue in v1.2 +EQUAL[51; Rget "mnt"; 720 750i]; +// Type mapping issue in v1.2 +// Key is type 0. +EQUAL[52; Rget"as.difftime(c(1, 2), units=\"mins\")"; ((`class;`units)!("difftime";"mins");1 2f)]; + +// second +Rset["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +// Type mapping issue in v1.2 +EQUAL[53; Rget"scnd"; 43230 45045i]; +// Type mapping issue in v1.2 +// Key is type 0. +EQUAL[54; Rget"as.difftime(c(1, 2), units=\"secs\")"; ((`class;`units)!("difftime";"secs");1 2f)]; + +PROGRESS["Time Test Finished!!"]; + +//List//---------------------------------------/ + +//lang +EQUAL[55; Rget "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; +// Type mapping issue of v1.2 +EQUAL[56; Rget "as.pairlist(TRUE)"; (enlist 1i; ())]; +EQUAL[57; Rget "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; +EQUAL[58; Rget "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; +Rget "list(x ~ y + z)" +EQUAL[59; Rget "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; +EQUAL[60; Rget "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; + +// Old output: +// ((`className;`package;`generator;`class)!((,`package!,".GlobalEnv";"Instrument");".GlobalEnv";((`.xData;`class)!("environment";(,`package!,"methods";"refGeneratorSlot"));"S4");(,`package!,"methods";"refObjectGenerator"));((`;`...);(`new;(,`package!,".GlobalEnv";"Instrument");`...))) +// New output: +// ((((".GlobalEnv";`package);"Instrument");`className;".GlobalEnv";`package;(("environment";`.xData;(("methods";`package);"refGeneratorSlot");`class);"S4");`generator;(("methods";`package);"refObjectGenerator");`class);((`;`...);(`new;((".GlobalEnv";`package);"Instrument");`...))) +Rget "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" +// Old output: +// (,`names!,("getId";"setId";"getDescription";"setDescription");((();`id);((`;`value);(`{;(`<<-;`id;`value);(`invisible;`value)));(();`description);((`;`value);(`{;(`<<-;`description;`value);(`invisible;`value))))) +// New output: +// `getId`setId`getDescription`setDescription!((();`id);((`;`value);(`{;(`<<-;`id;`value);(`invisible;`value)));(();`description);((`;`value);(`{;(`<<-;`description;`value);(`invisible;`value)))) +Rget "Instrument$accessors(c('id', 'description'))" +// Old output: +// ((`.xData;`class)!("environment";(,`package!,".GlobalEnv";"Instrument"));"S4") +// New output: +// (("environment";`.xData;((".GlobalEnv";`package);"Instrument");`class);"S4") +Rget "Instrument$new(id='AAPL', description='Apple')" +EQUAL[61; Rget "(1+1i)"; "complex"]; +EQUAL[62; Rget "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; +EQUAL[63; Rget"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; +EQUAL[64; Rget"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; +EQUAL[65; Rget"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; + +//Q-Like R Interface//--------------------------/ + +// long vectors +Rcmd"x<-c(as.raw(1))" +//Rcmd"x[2147483648L]<-as.raw(1)" +EQUAL[66; count Rget`x; 1]; + +EQUAL[67; .[Rset;("x[0]";1); "nyi"~]; 1b]; +EQUAL[68; Rget["c()"]; Rget"NULL"]; +EQUAL[69; Rget"c()"; ()]; +EQUAL[70; {@[Rget;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; +Rset[`x;1] +EQUAL[71; Rget each ("x";enlist "x";`x;`x`x); 1#/:(1f;1f;1f;1f)]; // ("x";"x")? + +PROGRESS["Q-Like R Command Test Finished!!"]; + +//Genral Test//----------------------------------/ + +Rcmd"rm(x)" + +// run gc +Rget"gc()" + +Rset["a";`sym?`a`b`c] +`:x set string 10?`4; +Rset["a";get `:x] +hdel `:x; +hdel `$":x#"; + +// Finish testing if `test_data_frame` is not "true". +if[not "true" ~ COMMANDLINE_ARGS `test_data_frame; + PROGRESS["Completed!!"]; + exit 0 + ]; + +Rinstall`data.table +Rcmd"library(data.table)" +Rcmd"a<-data.frame(a=c(1,2))" +EQUAL[72; Rget`a; flip `a`row.names!(1 2f;1 2)]; +Rcmd "b<-data.table(a=c(1,2))" +EQUAL[73; Rget`b; flip `a`row.names!(1 2f;1 2)]; +Rcmd"inspect <- function(x, ...) .Internal(inspect(x,...))" +Rget`inspect +Rget"substitute(log(1))" + +EQUAL[74; Rget"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"),stringsAsFactors=TRUE)"; flip `a`b`row.names!(`1`2`1;`a`b`b;1 2 3)]; +EQUAL[75; Rget"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"; flip `a`b`row.names!(`1`2`1;1#/:("a";"b";"b");1 2 3)]; +EQUAL[76; Rget"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"; flip `a`b`row.names!(`1`2`1;`10`20`30;1 2 3)]; + +// Finish testing if slave thread is not used. +if[0i ~ system "s"; + PROGRESS["Completed!!"]; + exit 0 + ]; + +EQUAL[77; all {.[Rset;("x";0N!x);"main thread only"~]} peach 2#enlist ([]1 2); 1b]; +PROGRESS["Completed!!"]; + +exit 0 From 1e766f0ed65a6dbc1c6e5fb0518452a63dda004e Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Thu, 11 Mar 2021 20:08:30 +0900 Subject: [PATCH 31/33] Fixed indices of test --- tests/test.q | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/test.q b/tests/test.q index ffc822a..e808bd1 100644 --- a/tests/test.q +++ b/tests/test.q @@ -161,44 +161,44 @@ PROGRESS["Dictionary Test Finished!!"]; // timestamp .rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; -EQUAL[47; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; +EQUAL[39; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; // month .rk.set["mnth"; `month$/:2020.04.02 2010.01.29] -EQUAL[54; .rk.get"mnth"; 2020.04 2010.01m]; +EQUAL[40; .rk.get"mnth"; 2020.04 2010.01m]; // dates -EQUAL[39; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; -EQUAL[40; .rk.get"as.Date(NA)"; (), 0Nd]; -EQUAL[41; .rk.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; +EQUAL[41; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; +EQUAL[42; .rk.get"as.Date(NA)"; (), 0Nd]; +EQUAL[43; .rk.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; // datetime .rk.exec["Sys.setenv(TZ='UTC')"]; -EQUAL[42; .rk.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[43; .rk.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[44; .rk.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; -EQUAL[45; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[44; .rk.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[45; .rk.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[46; .rk.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[47; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; .rk.set["dttm"; 2018.02.18T04:00:01.000z]; -EQUAL[46; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; +EQUAL[48; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; // days .rk.set["days"; 1D 2D]; -EQUAL[52; .rk.get"days"; 1D 2D]; -EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; +EQUAL[49; .rk.get"days"; 1D 2D]; +EQUAL[50; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; // timespan .rk.set["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] -EQUAL[55; .rk.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; +EQUAL[51; .rk.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; // minute .rk.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; -EQUAL[50; .rk.get "mnt"; 12:00 12:30]; -EQUAL[51; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; +EQUAL[52; .rk.get "mnt"; 12:00 12:30]; +EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; // second .rk.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; -EQUAL[48; .rk.get"scnd"; 12:00:30 12:30:45]; -EQUAL[49; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; +EQUAL[54; .rk.get"scnd"; 12:00:30 12:30:45]; +EQUAL[55; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; PROGRESS["Time Test Finished!!"]; From d211eab0f6b9ab8b4b7be523521893b524660685 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Thu, 11 Mar 2021 23:02:50 +0900 Subject: [PATCH 32/33] Added missing ; to travis file --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6f7a0c2..5df9f2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,7 +82,7 @@ before_install: script: - if [[ $TESTS == "True" && "x$OD" != "x" && "x$QLIC_KC" != "x" ]]; then - q tests/test.q -test_data_frame false + q tests/test.q -test_data_frame false; fi - if [[ $TRAVIS_OS_NAME == "windows" && $BUILD == "True" ]]; then 7z a -tzip -r $FILE_NAME ./build/$FILE_ROOT/*; From 0e12cff25d0ecdad485dc9e47d47d7616b3c5255 Mon Sep 17 00:00:00 2001 From: mshimizu-kx Date: Tue, 16 Mar 2021 19:27:32 +0900 Subject: [PATCH 33/33] Changed namespace from rk to r in line with embedPy --- README.md | 8 +- docs/examples.md | 16 +-- docs/reference.md | 80 +++++++-------- examples/e4.q | 38 +++---- examples/pcd.q | 28 +++--- q/embedr.q | 36 +++---- tests/test.q | 252 +++++++++++++++++++++++----------------------- 7 files changed, 229 insertions(+), 229 deletions(-) diff --git a/README.md b/README.md index 03d9f68..109d0fe 100644 --- a/README.md +++ b/README.md @@ -107,10 +107,10 @@ build> cmake --build . --config Release --target install The library has four main methods: -- `.rk.open`: Initialise embedR. Optional to call. Allows to set verbose mode as `.rk.open 1`. -- `.rk.exec`: Execute an R command, do not return a result to q. -- `.rk.get`: Execute an R command, return the result to q. -- `.rk.set`: Set a variable in the R memory space. +- `.r.open`: Initialise embedR. Optional to call. Allows to set verbose mode as `.r.open 1`. +- `.r.exec`: Execute an R command, do not return a result to q. +- `.r.get`: Execute an R command, return the result to q. +- `.r.set`: Set a variable in the R memory space. ## Documentation diff --git a/docs/examples.md b/docs/examples.md index bd499c5..25405ad 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -41,9 +41,9 @@ time | mid q)Load in R q)\l init.q q)//Pass the table into the R memory space -q).rk.set["mids";mids] +q).r.set["mids";mids] q)//Graph it -q).rk.exec["plot(mids$time, mids$mid, type='l', xlab='time', ylab='price')"] +q).r.exec["plot(mids$time, mids$mid, type='l', xlab='time', ylab='price')"] ``` This will produce a plot as shown in Figure 4: @@ -53,9 +53,9 @@ _Figure 4: Quote mid price plot drawn from q_ ```q q)//Save as a PDF file -q).rk.exec "pdf('test.pdf')" +q).r.exec "pdf('test.pdf')" q)//Close plot window -q).rk.off[] +q).r.off[] ``` If the q and R installations are running remotely from the user on a Linux machine, the graphics can be seen locally using X11 forwarding over SSH. @@ -65,11 +65,11 @@ Aside from using R’s powerful graphics, this mechanism also allows you to call Note that R’s timezone setting affects date transfers between R and q. For example, in the R server: ```q -q).rk.exec "Sys.setenv(TZ='GMT')" -q).rk.get "date()" +q).r.exec "Sys.setenv(TZ='GMT')" +q).r.get "date()" "Fri Feb 3 06:33:43 2012" -q).rk.exec "Sys.setenv(TZ='EST')" -q).rk.get "date()" +q).r.exec "Sys.setenv(TZ='EST')" +q).r.get "date()" "Fri Feb 3 01:33:57 2012" ``` diff --git a/docs/reference.md b/docs/reference.md index d033b01..e24445b 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -15,30 +15,30 @@ The library has five methods: ```txt //Connection - .rk.open: Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1 - .rk.close: Close internal R connection + .r.open: Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1 + .r.close: Close internal R connection //Execution - .rk.exec: Run an R command, do not return a result - .rk.get: Run an R command, return the result to q - .rk.set: Set a variable in the R memory space + .r.exec: Run an R command, do not return a result + .r.get: Run an R command, return the result to q + .r.set: Set a variable in the R memory space //Graphic - .rk.dev Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically) - .rk.off Close plot window + .r.dev Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically) + .r.off Close plot window //Utility - .rk.install Install package in embeded R process over the connection + .r.install Install package in embeded R process over the connection ``` ## Connection -### .rk.open +### .r.open _Initialise embedR. Optional to call. Allows to set verbose mode as Ropen 1_ -Syntax: `.rk.open[signal]` +Syntax: `.r.open[signal]` Where @@ -47,40 +47,40 @@ Where * 1: verbose mode ```q -q).rk.open[] +q).r.open[] q)//or -q).rk.open[1] +q).r.open[1] ``` !!! note "Note" - As of version 2.0 Ropen was migrated to .rk.open. Ropen will be depricated in version 2.1. + As of version 2.0 Ropen was migrated to .r.open. Ropen will be depricated in version 2.1. -### .rk.close +### .r.close _Close internal R connection_ -Syntax: `.rk.close[]` +Syntax: `.r.close[]` !!! note "Note" - As of version 2.0 Rclose was migrated to .rk.close. Rclose will be depricated in version 2.1. + As of version 2.0 Rclose was migrated to .r.close. Rclose will be depricated in version 2.1. ## Execution -### .rk.exec +### .r.exec _Run an R command, do not return a result_ -Synatax: `.rk.exec[command]` +Synatax: `.r.exec[command]` Where - `command` is an R expression to execute in R process ```q -q).rk.exec "Sys.setenv(TZ = 'UTC')" -q).rk.exec "library(xts)" +q).r.exec "Sys.setenv(TZ = 'UTC')" +q).r.exec "library(xts)" Loading required package: zoo Attaching package: ‘zoo’ @@ -90,31 +90,31 @@ The following objects are masked from ‘package:base’: as.Date, as.Date.numeric ``` -### .rk.get +### .r.get _Run an R command, return the result to q_ -Synatax: `.rk.get[rexp]` +Synatax: `.r.get[rexp]` Where - `rexp` is a string denoting an R expression to execute in R process ```q -q).rk.exec "today <- as.Date('2020-04-01')" -q).rk.get "today" +q).r.exec "today <- as.Date('2020-04-01')" +q).r.get "today" ,2019.04.01 ``` !!! note "Note" - As of version 2.0 Rget was migrated to .rk.get. Rget will be depricated in version 2.1. + As of version 2.0 Rget was migrated to .r.get. Rget will be depricated in version 2.1. -### .rk.set +### .r.set _Set a variable in the R memory space_ -Synatax: `.rk.set[rvar; qvar]` +Synatax: `.r.set[rvar; qvar]` Where @@ -122,46 +122,46 @@ Where - `qvar` is any q object used to assign to the R variable in R process ```q -q).rk.set["mnth"; `month$/:2010.01.29 2020.04.02] -q).rk.get "mnth" +q).r.set["mnth"; `month$/:2010.01.29 2020.04.02] +q).r.get "mnth" 2010.01 2020.04m ``` !!! note "Note" - As of version 2.0 Rset was migrated to .rk.set. Rset will be depricated in version 2.1. + As of version 2.0 Rset was migrated to .r.set. Rset will be depricated in version 2.1. ## Graphic -### .rk.new +### .r.new _Open plot window with noRStudioGD=TRUE (Normally R will open a new device automatically)_ -Syntax: `.rk.new[]` +Syntax: `.r.new[]` !!! note "Note" - As of version 2.0 Rnew was migrated to .rk.new. Rnew will be depricated in version 2.1. + As of version 2.0 Rnew was migrated to .r.new. Rnew will be depricated in version 2.1. -### .rk.off +### .r.off _Close plot window_ -Syntax: `.rk.off[]` +Syntax: `.r.off[]` To close the graphics window, use `dev.off()` rather than the close button on the window. !!! note "Note" - As of version 2.0 Roff was migrated to .rk.off. Roff will be depricated in version 2.1. + As of version 2.0 Roff was migrated to .r.off. Roff will be depricated in version 2.1. ## Utility -### .rk.install +### .r.install _Install package in embeded R process over the connection_ -Syntax: `.rk.install[package]` +Syntax: `.r.install[package]` Where @@ -172,7 +172,7 @@ You must be a super user who has an access to the library directory. The result is any information regarding install if the package is installed for the first time; otherwise nothing is returned. ```q -q).rk.install["psy"] +q).r.install["psy"] Installing package into ‘/usr/lib64/R/library’ (as ‘lib’ is unspecified) trying URL 'https://cloud.r-project.org/src/contrib/psy_1.1.tar.gz' @@ -218,7 +218,7 @@ Updating HTML index of packages in '.Library' !!! note "Note" - As of version 2.0 Rinstall was migrated to .rk.install. Rinstall will be depricated in version 2.1. + As of version 2.0 Rinstall was migrated to .r.install. Rinstall will be depricated in version 2.1. Simple examples of embedR are available in [Examples](examples.md) page. \ No newline at end of file diff --git a/examples/e4.q b/examples/e4.q index c7b86fa..6b5a38d 100644 --- a/examples/e4.q +++ b/examples/e4.q @@ -4,34 +4,34 @@ \l ../init.q -if[0i=first .rk.get"is.element(\"zoo\",installed.packages()[,1])"; - .rk.exec"install.packages(\"zoo\",repos=\"https://cloud.r-project.org\")"] -.rk.exec"library(zoo)"; +if[0i=first .r.get"is.element(\"zoo\",installed.packages()[,1])"; + .r.exec"install.packages(\"zoo\",repos=\"https://cloud.r-project.org\")"] +.r.exec"library(zoo)"; // Some data to play with (Nifty on all fridays for calendar 2004) -- pr:1946.05 1971.9 1900.65 1847.55 1809.75 1833.65 1913.6 1852.65 1800.3 1867.7 1812.2 1725.1 1747.5 1841.1 1853.55 1868.95 1892.45 1796.1 1804.45 1582.4 1560.2 1508.75 1521.1 1508.45 1491.2 1488.5 1537.5 1553.2 1558.8 1601.6 1632.3 1633.4 1607.2 1590.35 1609 1634.1 1668.75 1733.65 1722.5 1775.15 1820.2 1795 1779.75 1786.9 1852.3 1872.95 1872.35 1901.05 1996.2 1969 2012.1 2062.7 2080.5 d: 2004.01.02 2004.01.09 2004.01.16 2004.01.23 2004.01.30 2004.02.06 2004.02.13 2004.02.20 2004.02.27 2004.03.05 2004.03.12 2004.03.19 2004.03.26 2004.04.02 2004.04.09 2004.04.16 2004.04.23 2004.04.30 2004.05.07 2004.05.14 2004.05.21 2004.05.28 2004.06.04 2004.06.11 2004.06.18 2004.06.25 2004.07.02 2004.07.09 2004.07.16 2004.07.23 2004.07.30 2004.08.06 2004.08.13 2004.08.20 2004.08.27 2004.09.03 2004.09.10 2004.09.17 2004.09.24 2004.10.01 2004.10.08 2004.10.15 2004.10.22 2004.10.29 2004.11.05 2004.11.12 2004.11.19 2004.11.26 2004.12.03 2004.12.10 2004.12.17 2004.12.24 2004.12.31 -.rk.set[`d;d] -.rk.set[`pr;pr] -.rk.exec"p <- structure(pr, index = d, frequency = 0.142857142857143, class = c(\"zooreg", "zoo\"))"; +.r.set[`d;d] +.r.set[`pr;pr] +.r.exec"p <- structure(pr, index = d, frequency = 0.142857142857143, class = c(\"zooreg", "zoo\"))"; // Shift to returns -- -.rk.exec"r <- 100*diff(log(p))"; -.rk.get"head(r)" -.rk.get"summary(r)" -.rk.get"sd(r)" +.r.exec"r <- 100*diff(log(p))"; +.r.get"head(r)" +.r.get"summary(r)" +.r.get"sd(r)" // Compute the moving window vol -- -.rk.exec"vol <- sqrt(250) * rollapply(r, 20, sd, align = \"right\")"; +.r.exec"vol <- sqrt(250) * rollapply(r, 20, sd, align = \"right\")"; // A pretty plot -- -.rk.exec"png('pretty_plot_test.png')"; -.rk.exec"plot(vol, type=\"l\", ylim=c(0,max(vol,na.rm=TRUE)),lwd=2, col=\"purple\", xlab=\"2004\",ylab=paste(\"Annualised sigma, 20-week window\"))"; -.rk.exec"grid()"; -.rk.get"legend(x=\"bottomleft\", col=c(\"purple\", \"darkgreen\"),lwd=c(2,2), bty=\"n\", cex=0.8,legend=c(\"Annualised 20-week vol (left scale)\", \"Nifty (right scale)\"))" -.rk.exec"par(new=TRUE)"; -.rk.exec"plot(p, type=\"l\", lwd=2, col=\"darkgreen\", xaxt=\"n\", yaxt=\"n\", xlab=\"\", ylab=\"\")"; -.rk.exec"axis(4)"; -.rk.off[] +.r.exec"png('pretty_plot_test.png')"; +.r.exec"plot(vol, type=\"l\", ylim=c(0,max(vol,na.rm=TRUE)),lwd=2, col=\"purple\", xlab=\"2004\",ylab=paste(\"Annualised sigma, 20-week window\"))"; +.r.exec"grid()"; +.r.get"legend(x=\"bottomleft\", col=c(\"purple\", \"darkgreen\"),lwd=c(2,2), bty=\"n\", cex=0.8,legend=c(\"Annualised 20-week vol (left scale)\", \"Nifty (right scale)\"))" +.r.exec"par(new=TRUE)"; +.r.exec"plot(p, type=\"l\", lwd=2, col=\"darkgreen\", xaxt=\"n\", yaxt=\"n\", xlab=\"\", ylab=\"\")"; +.r.exec"axis(4)"; +.r.off[] hcount `:pretty_plot_test.png //hdel `:pretty_plot_test.png diff --git a/examples/pcd.q b/examples/pcd.q index 6546e84..ea5a622 100644 --- a/examples/pcd.q +++ b/examples/pcd.q @@ -9,20 +9,20 @@ system"z 1" cct:("***D*DE";enlist csv) 0:datafile cct:(`$"_"^string cols cct) xcol cct -.rk.exec"library(lattice)" -.rk.new[] -.rk.set["tpd";select txn:count i by Transaction_Date from cct] -.rk.exec"plot1<-xyplot(txn~Transaction_Date,data=tpd,main='Payments per day')" -.rk.exec"print(plot1)" +.r.exec"library(lattice)" +.r.new[] +.r.set["tpd";select txn:count i by Transaction_Date from cct] +.r.exec"plot1<-xyplot(txn~Transaction_Date,data=tpd,main='Payments per day')" +.r.exec"print(plot1)" -.rk.set[`tpt;select Transaction_Date,spent:sums JV_Value from select sum JV_Value by Transaction_Date from cct] -.rk.new[] -.rk.exec"plot2<-xyplot(spent~Transaction_Date,data=tpt,main='Total spent')" -.rk.exec"print(plot2)" +.r.set[`tpt;select Transaction_Date,spent:sums JV_Value from select sum JV_Value by Transaction_Date from cct] +.r.new[] +.r.exec"plot2<-xyplot(spent~Transaction_Date,data=tpt,main='Total spent')" +.r.exec"print(plot2)" -.rk.install`plotly -.rk.exec"library(plotly)" -.rk.set[`catd;select sum JV_Value by Service_Area from cct] -.rk.exec"plot3<-plot_ly(catd, labels = ~Service_Area, values = ~JV_Value, type = 'pie')" -.rk.exec"print(plot3)" +.r.install`plotly +.r.exec"library(plotly)" +.r.set[`catd;select sum JV_Value by Service_Area from cct] +.r.exec"plot3<-plot_ly(catd, labels = ~Service_Area, values = ~JV_Value, type = 'pie')" +.r.exec"print(plot3)" diff --git a/q/embedr.q b/q/embedr.q index 21136ef..60f8bce 100644 --- a/q/embedr.q +++ b/q/embedr.q @@ -16,24 +16,24 @@ LIBPATH:`:embedr 2: /* Load Libraries */ /*-----------------------------------------------*/ -.rk.close: LIBPATH (`rclose;1); -.rk.open: LIBPATH (`ropen;1); -.rk.exec: LIBPATH (`rexec;1); -.rk.get: LIBPATH (`rget;1); -.rk.set: LIBPATH (`rset;2) +.r.close: LIBPATH (`rclose;1); +.r.open: LIBPATH (`ropen;1); +.r.exec: LIBPATH (`rexec;1); +.r.get: LIBPATH (`rget;1); +.r.set: LIBPATH (`rset;2) /*-----------------------------------------------*/ /* Additional Functions */ /*-----------------------------------------------*/ -.rk.install:{[pkg] +.r.install:{[pkg] pkg:$[-11=type pkg;string pkg;pkg]; rcloud:"https://cloud.r-project.org"; - if[0i=first .rk.get"is.element('",pkg,"',installed.packages()[,1])"; - .rk.exec"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; + if[0i=first .r.get"is.element('",pkg,"',installed.packages()[,1])"; + .r.exec"install.packages('",pkg,"',repos='",rcloud,"',dependencies = TRUE)"]; } -.rk.off:{.rk.exec "dev.off()"} -.rk.new:{.rk.exec "dev.new(noRStudioGD=TRUE)"} +.r.off:{.r.exec "dev.off()"} +.r.new:{.r.exec "dev.new(noRStudioGD=TRUE)"} /*-----------------------------------------------*/ /* Deprecated Function */ @@ -42,11 +42,11 @@ LIBPATH:`:embedr 2: // The following block of functions are aliases for the above // these aliases will be removed in a future version of the code -Ropen :.rk.open; -Rclose :.rk.close; -Rcmd :.rk.exec; +Ropen :.r.open; +Rclose :.r.close; +Rcmd :.r.exec; Rget :{[variable] - result:.rk.get[variable]; + result:.r.get[variable]; $[ // Foreign type 112h ~ type result; @@ -93,7 +93,7 @@ Rget :{[variable] result ] } -Rinstall:.rk.install; -Rnew :.rk.new; -Roff :.rk.off; -Rset :.rk.set; +Rinstall:.r.install; +Rnew :.r.new; +Roff :.r.off; +Rset :.r.set; diff --git a/tests/test.q b/tests/test.q index e808bd1..83cb88c 100644 --- a/tests/test.q +++ b/tests/test.q @@ -1,7 +1,7 @@ / * test R server for Q. * # Note -* - When testing on travis CI `.rk.install` should not be run; therefore commandline argument +* - When testing on travis CI `.r.install` should not be run; therefore commandline argument * `test_data_frame` must be passed with its value `true`, e.g., * $ q tests/test.q -test_data_frame true * - `-s` flag must be passed to test limtation of main thread only. If `\s` is 0, Final test will be ignored. @@ -49,7 +49,7 @@ EQUAL:{[id;x;y] \c 25 300 // set verbose mode -.rk.open 1 +.r.open 1 //%% Test %%//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/ @@ -57,198 +57,198 @@ EQUAL:{[id;x;y] PROGRESS["Test Start!!"]; -.rk.exec "a=array(1:24,c(2,3,4))" +.r.exec "a=array(1:24,c(2,3,4))" -EQUAL[1; .rk.get "dim(a)"; 2 3 4i]; -EQUAL[2; .rk.get "a"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; +EQUAL[1; .r.get "dim(a)"; 2 3 4i]; +EQUAL[2; .r.get "a"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; -if[3<=.z.K;.rk.set["a";2?0Ng]] -EQUAL[3; .rk.get "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed1-f4df-58fcdedd8376")]; +if[3<=.z.K;.r.set["a";2?0Ng]] +EQUAL[3; .r.get "a"; ("84cf32c6-c711-79b4-2f31-6e85923decff";"22371003-8997-eed1-f4df-58fcdedd8376")]; -.rk.exec "b= 2 == array(1:24,c(2,3,4))" -EQUAL[4; .rk.get "dim(b)"; 2 3 4i]; -EQUAL[5; .rk.get "b"; ((000b;100b);(000b;000b);(000b;000b);(000b;000b))]; +.r.exec "b= 2 == array(1:24,c(2,3,4))" +EQUAL[4; .r.get "dim(b)"; 2 3 4i]; +EQUAL[5; .r.get "b"; ((000b;100b);(000b;000b);(000b;000b);(000b;000b))]; -EQUAL[6; .rk.get "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11.0 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22.0 24.2 26.4))]; +EQUAL[6; .r.get "1.1*array(1:24,c(2,3,4))"; ((1.1 3.3 5.5;2.2 4.4 6.6);(7.7 9.9 12.1;8.8 11.0 13.2);(14.3 16.5 18.7;15.4 17.6 19.8);(20.9 23.1 25.3;22.0 24.2 26.4))]; -.rk.set["xyz";1 2 3i] -EQUAL[7; .rk.get "xyz"; 1 2 3i]; +.r.set["xyz";1 2 3i] +EQUAL[7; .r.get "xyz"; 1 2 3i]; -EQUAL[8; .rk.get "pi"; (), acos -1]; -EQUAL[9; .rk.get "2+3"; (), 5f]; -EQUAL[10; .rk.get "11:11"; (), 11i]; -EQUAL[11; .rk.get "11:15"; 11 12 13 14 15i]; -a:.rk.get "matrix(1:6,2,3)" +EQUAL[8; .r.get "pi"; (), acos -1]; +EQUAL[9; .r.get "2+3"; (), 5f]; +EQUAL[10; .r.get "11:11"; (), 11i]; +EQUAL[11; .r.get "11:15"; 11 12 13 14 15i]; +a:.r.get "matrix(1:6,2,3)" EQUAL[12; a[1]; 2 4 6i]; -.rk.exec "m=array(1:24,c(2,3,4))" -EQUAL[13; .rk.get "m"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; -EQUAL[14; .rk.get "length(m)"; (), 24i]; -EQUAL[15; .rk.get "dim(m)"; 2 3 4i]; -EQUAL[16; .rk.get "c(1,2,Inf,-Inf,NaN,NA)"; 1 2 0w -0w 0n 0n]; +.r.exec "m=array(1:24,c(2,3,4))" +EQUAL[13; .r.get "m"; ((1 3 5i;2 4 6i);(7 9 11i;8 10 12i);(13 15 17i;14 16 18i);(19 21 23i;20 22 24i))]; +EQUAL[14; .r.get "length(m)"; (), 24i]; +EQUAL[15; .r.get "dim(m)"; 2 3 4i]; +EQUAL[16; .r.get "c(1,2,Inf,-Inf,NaN,NA)"; 1 2 0w -0w 0n 0n]; PROGRESS["Numeric Array Finished!!"]; //Plot Functionality//----------------------/ -.rk.exec "pdf(tempfile(\"t1\",fileext=\".pdf\"))" -.rk.exec "plot(c(2,3,5,7,11))" -.rk.exec "dev.off()" +.r.exec "pdf(tempfile(\"t1\",fileext=\".pdf\"))" +.r.exec "plot(c(2,3,5,7,11))" +.r.exec "dev.off()" //Function Test//---------------------------/ -.rk.exec "x=factor(c('one','two','three','four'))" -EQUAL[17; .rk.get "x"; `one`two`three`four]; -EQUAL[18; .rk.get "mode(x)"; "numeric"]; -EQUAL[19; .rk.get "typeof(x)"; "integer"]; -EQUAL[20; .rk.get "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 100110b]; -.rk.exec "foo <- function(x,y) {x + 2 * y}" -.rk.get "foo" -EQUAL[21; .rk.get "typeof(foo)"; "closure"]; -EQUAL[22; .rk.get "foo (5,3)"; (), 11f]; +.r.exec "x=factor(c('one','two','three','four'))" +EQUAL[17; .r.get "x"; `one`two`three`four]; +EQUAL[18; .r.get "mode(x)"; "numeric"]; +EQUAL[19; .r.get "typeof(x)"; "integer"]; +EQUAL[20; .r.get "c(TRUE,FALSE,NA,TRUE,TRUE,FALSE)"; 100110b]; +.r.exec "foo <- function(x,y) {x + 2 * y}" +.r.get "foo" +EQUAL[21; .r.get "typeof(foo)"; "closure"]; +EQUAL[22; .r.get "foo (5,3)"; (), 11f]; PROGRESS["Function Test Finished!!"]; //Object//-----------------------------------/ -show .rk.get "wilcox.test(c(1,2,3),c(4,5,6))" -.rk.exec "data(OrchardSprays)" -show .rk.get "OrchardSprays" +show .r.get "wilcox.test(c(1,2,3),c(4,5,6))" +.r.exec "data(OrchardSprays)" +show .r.get "OrchardSprays" // to install package in non-interactive way // install.packages("zoo", repos="http://cran.r-project.org") -.rk.get"install.packages" +.r.get"install.packages" //'Broken R object. -EQUAL[23; .rk.get".GlobalEnv"; "environment"]; +EQUAL[23; .r.get".GlobalEnv"; "environment"]; //"environment" -EQUAL[24; .rk.get"emptyenv()"; "environment"]; +EQUAL[24; .r.get"emptyenv()"; "environment"]; //"environment" -EQUAL[25; .rk.get".Internal"; "special"]; +EQUAL[25; .r.get".Internal"; "special"]; //"special" -EQUAL[26; @[.rk.exec; "typeof("; like[;"incomplete: *"]]; 1b]; -EQUAL[27; @[.rk.exec; "typeof()"; like[;"eval error*"]]; 1b]; -EQUAL[28; .rk.get each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"dim";"length";"names";".External"); ("builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin")]; -.rk.get "getGeneric('+')" +EQUAL[26; @[.r.exec; "typeof("; like[;"incomplete: *"]]; 1b]; +EQUAL[27; @[.r.exec; "typeof()"; like[;"eval error*"]]; 1b]; +EQUAL[28; .r.get each ("cos";".C";"floor";"Im";"cumsum";"nargs";"proc.time";"dim";"length";"names";".External"); ("builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin";"builtin")]; +.r.get "getGeneric('+')" -EQUAL[29; .rk.get"as.raw(10)"; (), 0x0a]; -EQUAL[30; .rk.get"as.logical(c(1,FALSE,NA))"; 100b]; +EQUAL[29; .r.get"as.raw(10)"; (), 0x0a]; +EQUAL[30; .r.get"as.logical(c(1,FALSE,NA))"; 100b]; PROGRESS["Object Test Finished!!"]; //Table//-----------------------------------/ // data.frame -EQUAL[31; .rk.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=TRUE)"; flip `a`b!(1 2 3i;`a`b`c)]; -EQUAL[32; .rk.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b!(1 2 3i;1#/:("a";"b";"c"))]; -EQUAL[33; .rk.get"data.frame(a=1:3)"; flip enlist[`a]!enlist (1 2 3i)]; -EQUAL[34; .rk.get"data.frame()"; ()]; +EQUAL[31; .r.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=TRUE)"; flip `a`b!(1 2 3i;`a`b`c)]; +EQUAL[32; .r.get"data.frame(a=1:3, b=c('a','b','c'),stringsAsFactors=FALSE)"; flip `a`b!(1 2 3i;1#/:("a";"b";"c"))]; +EQUAL[33; .r.get"data.frame(a=1:3)"; flip enlist[`a]!enlist (1 2 3i)]; +EQUAL[34; .r.get"data.frame()"; ()]; PROGRESS["Table Test Finished!!"]; //Dictionary//------------------------------/ -.rk.set["dictI"; `a`b`c!1 2 3i]; -EQUAL[35; .rk.get"dictI"; `a`b`c!1 2 3i]; -.rk.set["dictJ"; `a`b`c!1 2 3]; -EQUAL[36; .rk.get"dictJ"; `a`b`c!1 2 3]; -.rk.set["dictB"; `a`b`c!101b]; -EQUAL[37; .rk.get"dictB"; `a`b`c!101b]; -.rk.set["dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; -EQUAL[38; .rk.get"dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; +.r.set["dictI"; `a`b`c!1 2 3i]; +EQUAL[35; .r.get"dictI"; `a`b`c!1 2 3i]; +.r.set["dictJ"; `a`b`c!1 2 3]; +EQUAL[36; .r.get"dictJ"; `a`b`c!1 2 3]; +.r.set["dictB"; `a`b`c!101b]; +EQUAL[37; .r.get"dictB"; `a`b`c!101b]; +.r.set["dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; +EQUAL[38; .r.get"dictP"; `a`b`c!(2020.04.13D06:08:03.712336000; 2020.04.13D06:08:03.712336001; 2020.04.13D06:08:03.712336002)]; PROGRESS["Dictionary Test Finished!!"]; //Time//------------------------------------/ // timestamp -.rk.set["tmstp"; 2020.03.16D17:30:45.123456789]; -EQUAL[39; .rk.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; +.r.set["tmstp"; 2020.03.16D17:30:45.123456789]; +EQUAL[39; .r.get"tmstp"; (), 2020.03.16D17:30:45.123456789]; // month -.rk.set["mnth"; `month$/:2020.04.02 2010.01.29] -EQUAL[40; .rk.get"mnth"; 2020.04 2010.01m]; +.r.set["mnth"; `month$/:2020.04.02 2010.01.29] +EQUAL[40; .r.get"mnth"; 2020.04 2010.01m]; // dates -EQUAL[41; .rk.get"as.Date('2005-12-31')"; (), 2005.12.31]; -EQUAL[42; .rk.get"as.Date(NA)"; (), 0Nd]; -EQUAL[43; .rk.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; +EQUAL[41; .r.get"as.Date('2005-12-31')"; (), 2005.12.31]; +EQUAL[42; .r.get"as.Date(NA)"; (), 0Nd]; +EQUAL[43; .r.get"rep(as.Date('2005-12-31'),2)"; 2005.12.31 2005.12.31]; // datetime -.rk.exec["Sys.setenv(TZ='UTC')"]; -EQUAL[44; .rk.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[45; .rk.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; -EQUAL[46; .rk.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; -EQUAL[47; .rk.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; -.rk.set["dttm"; 2018.02.18T04:00:01.000z]; -EQUAL[48; .rk.get"dttm"; (), 2018.02.18T04:00:01.000z]; +.r.exec["Sys.setenv(TZ='UTC')"]; +EQUAL[44; .r.get"as.POSIXct(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[45; .r.get"as.POSIXlt(\"2018-02-18 04:00:01\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC')"; (),2018.02.18T04:00:01.000z]; +EQUAL[46; .r.get"c(as.POSIXct(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXct(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +EQUAL[47; .r.get"c(as.POSIXlt(\"2015-03-16 17:30:00\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'), as.POSIXlt(\"1978-06-01 12:30:59\", format=\"%Y-%m-%d %H:%M:%S\", tz='UTC'))"; (2015.03.16T17:30:00.000z; 1978.06.01T12:30:59.000z)]; +.r.set["dttm"; 2018.02.18T04:00:01.000z]; +EQUAL[48; .r.get"dttm"; (), 2018.02.18T04:00:01.000z]; // days -.rk.set["days"; 1D 2D]; -EQUAL[49; .rk.get"days"; 1D 2D]; -EQUAL[50; .rk.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; +.r.set["days"; 1D 2D]; +EQUAL[49; .r.get"days"; 1D 2D]; +EQUAL[50; .r.get"as.difftime(c(1, 2), units=\"days\")"; 1D 2D]; // timespan -.rk.set["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] -EQUAL[51; .rk.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; +.r.set["tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012] +EQUAL[51; .r.get"tmspans"; 0D12 0D04:20:17.123456789 0D00:00:00.000000012]; // minute -.rk.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; -EQUAL[52; .rk.get "mnt"; 12:00 12:30]; -EQUAL[53; .rk.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; +.r.set["mnt"; `minute$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +EQUAL[52; .r.get "mnt"; 12:00 12:30]; +EQUAL[53; .r.get"as.difftime(c(1, 2), units=\"mins\")"; 00:01 00:02]; // second -.rk.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; -EQUAL[54; .rk.get"scnd"; 12:00:30 12:30:45]; -EQUAL[55; .rk.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; +.r.set["scnd"; `second$(2019.04.01D12:00:30; 2019.04.01D12:30:45)]; +EQUAL[54; .r.get"scnd"; 12:00:30 12:30:45]; +EQUAL[55; .r.get"as.difftime(c(1, 2), units=\"secs\")"; 00:00:01 00:00:02]; PROGRESS["Time Test Finished!!"]; //List//---------------------------------------/ //lang -EQUAL[56; .rk.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; -EQUAL[57; .rk.get "as.pairlist(TRUE)"; (enlist 1b; ())]; -EQUAL[58; .rk.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; -EQUAL[59; .rk.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; -.rk.get "list(x ~ y + z)" -EQUAL[60; .rk.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; -EQUAL[61; .rk.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; -.rk.get "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" -.rk.get "Instrument$accessors(c('id', 'description'))" -.rk.get "Instrument$new(id='AAPL', description='Apple')" -EQUAL[62; .rk.get "(1+1i)"; "complex"]; -EQUAL[63; .rk.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; -EQUAL[64; .rk.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; -EQUAL[65; .rk.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; -EQUAL[66; .rk.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; +EQUAL[56; .r.get "as.pairlist(1:10)"; (enlist 1i;();enlist 2i;();enlist 3i;();enlist 4i;();enlist 5i;();enlist 6i;();enlist 7i;();enlist 8i;();enlist 9i;();enlist 10i;())]; +EQUAL[57; .r.get "as.pairlist(TRUE)"; (enlist 1b; ())]; +EQUAL[58; .r.get "as.pairlist(as.raw(1))"; (enlist 0x01; ())]; +EQUAL[59; .r.get "pairlist('rnorm', 10L, 0.0, 2.0 )"; ("rnorm";();enlist 10i;();enlist 0f;();enlist 2f;())]; +.r.get "list(x ~ y + z)" +EQUAL[60; .r.get "list( c(1, 5), c(2, 6), c(3, 7) )"; (1 5f;2 6f;3 7f)]; +EQUAL[61; .r.get "matrix( 1:16+.5, nc = 4 )"; (1.5 5.5 9.5 13.5;2.5 6.5 10.5 14.5;3.5 7.5 11.5 15.5;4.5 8.5 12.5 16.5)]; +.r.get "Instrument <- setRefClass(Class='Instrument',fields=list('id'='character', 'description'='character'))" +.r.get "Instrument$accessors(c('id', 'description'))" +.r.get "Instrument$new(id='AAPL', description='Apple')" +EQUAL[62; .r.get "(1+1i)"; "complex"]; +EQUAL[63; .r.get "(0:9)^2"; 0 1 4 9 16 25 36 49 64 81f]; +EQUAL[64; .r.get"expression(rnorm, rnorm(10), mean(1:10))"; "expression"]; +EQUAL[65; .r.get"list( rep(NA_real_, 20L), rep(NA_real_, 6L) )"; (0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n 0n;0n 0n 0n 0n 0n 0n)]; +EQUAL[66; .r.get"c(1, 2, 1, 1, NA, NaN, -Inf, Inf)"; 1 2 1 1 0n 0n -0w 0w]; PROGRESS["List Test Finished!!"]; //Q-Like R Interface//--------------------------/ // long vectors -.rk.exec"x<-c(as.raw(1))" -//.rk.exec"x[2147483648L]<-as.raw(1)" -EQUAL[67; count .rk.get`x; 1]; +.r.exec"x<-c(as.raw(1))" +//.r.exec"x[2147483648L]<-as.raw(1)" +EQUAL[67; count .r.get`x; 1]; -EQUAL[68; .[.rk.set;("x[0]";1); "nyi"~]; 1b]; -EQUAL[69; .rk.get["c()"]; .rk.get"NULL"]; -EQUAL[70; .rk.get"c()"; ()]; -EQUAL[71; {@[.rk.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; -.rk.set[`x;1] -EQUAL[72; .rk.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? +EQUAL[68; .[.r.set;("x[0]";1); "nyi"~]; 1b]; +EQUAL[69; .r.get["c()"]; .r.get"NULL"]; +EQUAL[70; .r.get"c()"; ()]; +EQUAL[71; {@[.r.get;x;"type"~]}each (.z.p;0b;1;1f;{};([1 2 3]1 2 3)); 111111b]; +.r.set[`x;1] +EQUAL[72; .r.get each ("x";enlist "x";`x;`x`x); 1#/:(1;1;1;1)]; // ("x";"x")? PROGRESS["Q-Like R Command Test Finished!!"]; //Genral Test//----------------------------------/ -.rk.exec"rm(x)" +.r.exec"rm(x)" // run gc -.rk.get"gc()" +.r.get"gc()" -.rk.set["a";`sym?`a`b`c]; +.r.set["a";`sym?`a`b`c]; `:x set string 10?`4; -.rk.set["a";get `:x]; +.r.set["a";get `:x]; hdel `:x; hdel `$":x#"; @@ -258,19 +258,19 @@ if[not "true" ~ COMMANDLINE_ARGS `test_data_frame; exit 0 ]; -.rk.install `data.table -.rk.exec"library(data.table)" -.rk.exec"a<-data.frame(a=c(1,2))" -EQUAL[73; .rk.get`a; flip enlist[`a]!enlist (1 2f)]; -.rk.exec "b<-data.table(a=c(1,2))" -EQUAL[74; .rk.get`b; flip enlist[`a]!enlist (1 2f)]; -.rk.exec"inspect <- function(x, ...) .Internal(inspect(x,...))" -.rk.get`inspect -.rk.get"substitute(log(1))" +.r.install `data.table +.r.exec"library(data.table)" +.r.exec"a<-data.frame(a=c(1,2))" +EQUAL[73; .r.get`a; flip enlist[`a]!enlist (1 2f)]; +.r.exec "b<-data.table(a=c(1,2))" +EQUAL[74; .r.get`b; flip enlist[`a]!enlist (1 2f)]; +.r.exec"inspect <- function(x, ...) .Internal(inspect(x,...))" +.r.get`inspect +.r.get"substitute(log(1))" -EQUAL[75; flip[`a`b!(`1`2`1;`a`b`b)]; .rk.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"),stringsAsFactors=TRUE)"]; -EQUAL[76; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; -EQUAL[77; flip[`a`b!(`1`2`1;`10`20`30)]; .rk.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; +EQUAL[75; flip[`a`b!(`1`2`1;`a`b`b)]; .r.get"data.frame(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"),stringsAsFactors=TRUE)"]; +EQUAL[76; flip[`a`b!(`1`2`1;1#/:("a";"b";"b"))]; .r.get"data.table(a=as.factor(c(1,2,1)), b=c(\"a\",\"b\",\"b\"))"]; +EQUAL[77; flip[`a`b!(`1`2`1;`10`20`30)]; .r.get"data.table(a=as.factor(c(1,2,1)), b=as.factor(c(10,20,30)))"]; // Finish testing if slave thread is not used. if[0i ~ system "s"; @@ -278,7 +278,7 @@ if[0i ~ system "s"; exit 0 ]; -EQUAL[78; all {.[.rk.set;("x"; x);"main thread only"~]} peach 2#enlist ([]1 2); 1b]; +EQUAL[78; all {.[.r.set;("x"; x);"main thread only"~]} peach 2#enlist ([]1 2); 1b]; PROGRESS["Completed!!"]; exit 0