## this file will be migrated to inst/tests/tests.Rraw when branch will be ready to merge
cc.used = all(sapply(c("test","froll"), exists))
if (!interactive() && !cc.used) {
  library(data.table)
  test = data.table:::test
  froll = data.table:::froll
}
oldDTthreads = setDTthreads(1) # mimics CRAN check, tested also on 4 cores

## rolling features

#### atomic vectors input and single window returns atomic vectors
x = 1:6/2
ans1 = frollmean(x, 3)
ans2 = frollmean(x, 3, algo="exact")
expected = c(rep(NA_real_,2), seq(1,2.5,0.5))
test(9999.001, ans1, expected)
test(9999.002, ans2, expected)

#### multiple columns at once
d = as.data.table(list(1:6/2, 3:8/4))
ans1 = frollmean(d, 3)
ans2 = frollmean(d, 3, algo="exact")
expected = list(
  c(rep(NA_real_,2), seq(1,2.5,0.5)),
  c(rep(NA_real_,2), seq(1,1.75,0.25))
)
test(9999.003, ans1, expected)
test(9999.004, ans2, expected)

#### multiple windows at once
ans1 = frollmean(d[, .(V1)], c(3, 4))
ans2 = frollmean(d[, .(V1)], c(3, 4), algo="exact")
expected = list(
  c(rep(NA_real_,2), seq(1,2.5,0.5)),
  c(rep(NA_real_,3), seq(1.25,2.25,0.5))
)
test(9999.005, ans1, expected)
test(9999.006, ans2, expected)

#### multiple columns and multiple windows at once
ans1 = frollmean(d, c(3, 4))
ans2 = frollmean(d, c(3, 4), algo="exact")
expected = list(
  c(rep(NA_real_,2), seq(1,2.5,0.5)), c(rep(NA_real_,3), seq(1.25,2.25,0.5)),
  c(rep(NA_real_,2), seq(1,1.75,0.25)), c(rep(NA_real_,3), seq(1.125,1.625,0.25))
)
test(9999.007, ans1, expected)
test(9999.008, ans2, expected)

#### in x integer converted to double
di = data.table(real=1:10/2, int=1:10)
ans = frollmean(di, 3)
expected = list(
  c(rep(NA_real_,2), seq(1,4.5,0.5)),
  c(rep(NA_real_,2), seq(2,9,1))
)
test(9999.009, ans, expected)

#### in n double converted to integer
x = 1:3/2
n = 2
test(9999.010, frollmean(x, n), c(NA, 0.75, 1.25))
n = list(c(2L,1L,2L), c(2,1,2))
test(9999.011, frollmean(x, n, adaptive=TRUE), list(c(NA, 1, 1.25), c(NA, 1, 1.25)))

#### error on unsupported type
dx = data.table(real=1:10/2, char=letters[1:10])
test(9999.012, frollmean(dx, 3), error="x must be list, data.frame or data.table of numeric types")
dx = data.table(real=1:10/2, fact=factor(letters[1:10]))
test(9999.013, frollmean(dx, 3), error="x must be list, data.frame or data.table of numeric types")
dx = data.table(real=1:10/2, logi=logical(10))
test(9999.014, frollmean(dx, 3), error="x must be list, data.frame or data.table of numeric types")
dx = data.table(real=1:10/2, list=rep(list(NA), 10))
test(9999.015, frollmean(dx, 3), error="x must be list, data.frame or data.table of numeric types")
x = letters[1:10]
test(9999.016, frollmean(x, 3), error="x must be of type numeric")
x = 1:10/2
test(9999.017, frollmean(x, "a"), error="n must be integer")
test(9999.018, frollmean(x, factor("a")), error="n must be integer")
test(9999.019, frollmean(x, TRUE), error="n must be integer")
test(9999.020, frollmean(x, list(1:10)), error="n must be integer, list is accepted for adaptive TRUE")
test(9999.021, frollmean(x, list(NA), adaptive=TRUE), error="n must be integer vector or list of integer vectors")
test(9999.022, frollmean(x, list(c(1:5,1:5), NA), adaptive=TRUE), error="n must be integer vector or list of integer vectors")

#### various length list vectors
l = list(1:6/2, 3:10/4)
ans = frollmean(l, c(3, 4))
expected = list(
  c(rep(NA_real_,2), seq(1,2.5,0.5)), c(rep(NA_real_,3), seq(1.25,2.25,0.5)),
  c(rep(NA_real_,2), seq(1,2.25,0.25)), c(rep(NA_real_,3), seq(1.125,2.125,0.25))
)
test(9999.023, ans, expected)

#### exact
set.seed(108)
x = sample(c(rnorm(1e3, 1e6, 5e5), 5e9, 5e-9))
n = 15
ma = function(x, n, na.rm=FALSE) {
  ans = rep(NA_real_, nx<-length(x))
  for (i in n:nx) ans[i] = mean(x[(i-n+1):i], na.rm=na.rm)
  ans
}
fastma = function(x, n, na.rm) {
  if (!missing(na.rm)) stop("NAs are unsupported, wrongly propagated by cumsum")
  cs = cumsum(x)
  scs = shift(cs, n)
  scs[n] = 0
  as.double((cs-scs)/n)
}
ans1 = ma(x, n)
ans2 = fastma(x, n)
ans3 = frollmean(x, n, algo="exact")
ans4 = frollmean(x, n)
anserr = list(
  froll_exact_f = ans4-ans1,
  froll_exact_t = ans3-ans1,
  fastma = ans2-ans1
)
errs = sapply(lapply(anserr, abs), sum, na.rm=TRUE)
test(9999.024, errs[["froll_exact_t"]]==0)
test(9999.025, errs[["froll_exact_f"]]>errs[["froll_exact_t"]])
test(9999.026, errs[["fastma"]]>errs[["froll_exact_t"]])
test(9999.027, errs[["fastma"]]>errs[["froll_exact_f"]])

#### align: right/center/left
d = as.data.table(list(1:8/2, 3:10/4))
ans1 = frollmean(d, 5, align="right") # default
ans2 = frollmean(d, 5, align="right", algo="exact")
expected = list(
  c(rep(NA_real_,4), seq(1.5,3,0.5)),
  c(rep(NA_real_,4), seq(1.25,2,0.25))
)
test(9999.028, ans1, expected)
test(9999.029, ans2, expected)
ans1 = frollmean(d, 5, align="center") # x even, n odd
ans2 = frollmean(d, 5, align="center", algo="exact")
expected = list(
  c(rep(NA_real_, 2), seq(1.5,3,0.5), rep(NA_real_, 2)),
  c(rep(NA_real_, 2), seq(1.25,2,0.25), rep(NA_real_, 2))
)
test(9999.030, ans1, expected)
test(9999.031, ans2, expected)
ans1 = frollmean(d, 6, align="center") # x even, n even
ans2 = frollmean(d, 6, align="center", algo="exact")
expected = list(
  c(rep(NA_real_, 2), seq(1.75,2.75,0.5), rep(NA_real_,3)),
  c(rep(NA_real_, 2), seq(1.375,1.875,0.25), rep(NA_real_,3))
)
test(9999.032, ans1, expected)
test(9999.033, ans2, expected)
de = rbind(d, data.table(4.5, 2.75))
ans1 = frollmean(de, 5, align="center") # x odd, n odd
ans2 = frollmean(de, 5, align="center", algo="exact")
expected = list(
  c(rep(NA_real_, 2), seq(1.5,3.5,0.5), rep(NA_real_, 2)),
  c(rep(NA_real_, 2), seq(1.25,2.25,0.25), rep(NA_real_, 2))
)
test(9999.034, ans1, expected)
test(9999.035, ans2, expected)
ans1 = frollmean(de, 6, align="center") # x odd, n even
ans2 = frollmean(de, 6, align="center", algo="exact")
expected = list(
  c(rep(NA_real_, 2), seq(1.75,3.25,0.5), rep(NA_real_,3)),
  c(rep(NA_real_, 2), seq(1.375,2.125,0.25), rep(NA_real_,3))
)
test(9999.036, ans1, expected)
test(9999.037, ans2, expected)
ans1 = frollmean(d, 5, align="left")
ans2 = frollmean(d, 5, align="left", algo="exact")
expected = list(
  c(seq(1.5,3,0.5), rep(NA_real_,4)),
  c(seq(1.25,2,0.25), rep(NA_real_,4))
)
test(9999.038, ans1, expected)
test(9999.039, ans2, expected)

#### handling NAs align na.rm
d = as.data.table(list(1:8/2, 3:10/4))
d[c(2L, 7L), "V1" := NA][c(1:2,8L), "V2" := NA]
ans1 = frollmean(d, 3, align="right") # default
ans2 = frollmean(d, 3, align="right", algo="exact")
expected = list(
  c(rep(NA_real_,4), seq(2,2.5,0.5), rep(NA_real_, 2)),
  c(rep(NA_real_,4), seq(1.5,2,0.25), rep(NA_real_, 1))
)
test(9999.040, ans1, expected)
test(9999.041, ans2, expected)
ans1 = frollmean(d, 3, align="right", na.rm=TRUE)
ans2 = frollmean(d, 3, align="right", algo="exact", na.rm=TRUE)
expected = list(
  c(rep(NA_real_,2), 1, 1.75, 2, 2.5, 2.75, 3.5),
  c(rep(NA_real_,2), 1.25, 1.375, 1.5, 1.75, 2, 2.125)
)
test(9999.042, ans1, expected)
test(9999.043, ans2, expected)
ans1 = frollmean(d, 3, align="center") # x even, n odd
ans2 = frollmean(d, 3, align="center", algo="exact")
expected = list(
  c(rep(NA_real_,3), seq(2,2.5,0.5), rep(NA_real_, 3)),
  c(rep(NA_real_,3), seq(1.5,2,0.25), rep(NA_real_, 2))
)
test(9999.044, ans1, expected)
test(9999.045, ans2, expected)
ans1 = frollmean(d, 3, align="center", na.rm=TRUE) # x even, n odd
ans2 = frollmean(d, 3, align="center", algo="exact", na.rm=TRUE)
expected = list(
  c(rep(NA_real_,1), 1, 1.75, 2, 2.5, 2.75, 3.5, rep(NA_real_,1)),
  c(rep(NA_real_,1), 1.25, 1.375, 1.5, 1.75, 2, 2.125, rep(NA_real_,1))
)
test(9999.046, ans1, expected)
test(9999.047, ans2, expected)
ans1 = frollmean(d, 4, align="center") # x even, n even
ans2 = frollmean(d, 4, align="center", algo="exact")
expected = list(
  c(rep(NA_real_,3), 2.25, rep(NA_real_, 4)),
  c(rep(NA_real_,3), 1.625, 1.875, rep(NA_real_, 3))
)
test(9999.048, ans1, expected)
test(9999.049, ans2, expected)
ans1 = frollmean(d, 4, align="center", na.rm=TRUE) # x even, n even
ans2 = frollmean(d, 4, align="center", algo="exact", na.rm=TRUE)
expected = list(
  c(rep(NA_real_,1), 4/3, 2, 2.25, 2.5, 9.5/3, rep(NA_real_,2)),
  c(rep(NA_real_,1), 1.375, 1.5, 1.625, 1.875, 2, rep(NA_real_,2))
)
test(9999.050, ans1, expected)
test(9999.051, ans2, expected)
de = rbind(d, data.table(4.5, 2.75))
ans1 = frollmean(de, 3, align="center") # x odd, n odd
ans2 = frollmean(de, 3, align="center", algo="exact")
expected = list(
  c(rep(NA_real_,3), 2, 2.5, rep(NA_real_, 4)),
  c(rep(NA_real_,3), 1.5, 1.75, 2, rep(NA_real_, 3))
)
test(9999.052, ans1, expected)
test(9999.053, ans2, expected)
ans1 = frollmean(de, 3, align="center", na.rm=TRUE) # x odd, n odd
ans2 = frollmean(de, 3, align="center", algo="exact", na.rm=TRUE)
expected = list(
  c(rep(NA_real_,1), 1, 1.75, 2, 2.5, 2.75, 3.5, 4.25, rep(NA_real_,1)),
  c(rep(NA_real_,1), 1.25, 1.375, 1.5, 1.75, 2, 2.125, 2.5, rep(NA_real_,1))
)
test(9999.054, ans1, expected)
test(9999.055, ans2, expected)
ans1 = frollmean(de, 4, align="center") # x odd, n even
ans2 = frollmean(de, 4, align="center", algo="exact")
expected = list(
  c(rep(NA_real_, 3), 2.25, rep(NA_real_,5)),
  c(rep(NA_real_, 3), 1.625, 1.875, rep(NA_real_,4))
)
test(9999.056, ans1, expected)
test(9999.057, ans2, expected)
ans1 = frollmean(de, 4, align="center", na.rm=TRUE) # x odd, n even
ans2 = frollmean(de, 4, align="center", algo="exact", na.rm=TRUE)
expected = list(
  c(rep(NA_real_, 1), 4/3, 2, 2.25, 2.5, 9.5/3, 11.5/3, rep(NA_real_,2)),
  c(rep(NA_real_, 1), 1.375, 1.5, 1.625, 1.875, 2, 7/3, rep(NA_real_,2))
)
test(9999.058, ans1, expected)
test(9999.059, ans2, expected)
ans1 = frollmean(d, 3, align="left")
ans2 = frollmean(d, 3, align="left", algo="exact")
expected = list(
  c(rep(NA_real_, 2), 2, 2.5, rep(NA_real_,4)),
  c(rep(NA_real_, 2), 1.5, 1.75, 2, rep(NA_real_,3))
)
test(9999.060, ans1, expected)
test(9999.061, ans2, expected)
ans1 = frollmean(d, 3, align="left", na.rm=TRUE)
ans2 = frollmean(d, 3, align="left", algo="exact", na.rm=TRUE)
expected = list(
  c(1, 1.75, 2, 2.5, 2.75, 3.5, rep(NA_real_,2)),
  c(1.25, 1.375, 1.5, 1.75, 2, 2.125, rep(NA_real_,2))
)
test(9999.062, ans1, expected)
test(9999.063, ans2, expected)
#### handling NAs for NaN output also
d = as.data.table(list(1:6/2, 3:8/4))
d[c(2L, 5L), V1:=NA][4:6, V2:=NA]
ans1 = frollmean(d, 2:3)
ans2 = frollmean(d, 2:3, algo="exact")
expected = list(c(NA, NA, NA, 1.75, NA, NA), rep(NA_real_, 6), c(NA, 0.875, 1.125, NA, NA, NA), c(NA, NA, 1, NA, NA, NA))
test(9999.064, ans1, expected)
test(9999.065, ans2, expected)
ans1 = frollmean(d, 2:3, na.rm=TRUE)
ans2 = frollmean(d, 2:3, algo="exact", na.rm=TRUE)
expected = list(c(NA, 0.5, 1.5, 1.75, 2, 3), c(NA, NA, 1, 1.75, 1.75, 2.5), c(NA, 0.875, 1.125, 1.25, NaN, NaN), c(NA, NA, 1, 1.125, 1.25, NaN))
test(9999.066, ans1, expected)
test(9999.067, ans2, expected)
#### early stopping NAs in leading k obs
test(9999.0671, frollmean(c(1:2,NA,4:10), 4, verbose=TRUE), c(rep(NA_real_, 6), 5.5, 6.5, 7.5, 8.5), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 4, hasna 0, narm 0",
  "frollmeanFast: NA (or other non-finite) value(s) are present in input, skip non-NA attempt and run with extra care for NAs"
))
test(9999.0672, frollmean(c(1:2,NA,4:10), 4, hasNA=FALSE, verbose=TRUE), c(rep(NA_real_, 6), 5.5, 6.5, 7.5, 8.5), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 4, hasna -1, narm 0",
  "frollmeanFast: NA (or other non-finite) value(s) are present in input, skip non-NA attempt and run with extra care for NAs"
), warning="hasNA=FALSE used but NA (or other non-finite) value(s) are present in input, use default hasNA=NA to avoid this warning")
test(9999.0673, ans<-frollmean(c(1:2,NA,4:10), 2, hasNA=FALSE, verbose=TRUE), c(NA, 1.5, NA, NA, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 2, hasna -1, narm 0",
  "frollmeanFast: NA (or other non-finite) value(s) are present in input, re-running with extra care for NAs"
), warning="hasNA=FALSE used but NA (or other non-finite) value(s) are present in input, use default hasNA=NA to avoid this warning")
test(9999.0674, frollmean(c(1:2,NA,4:10), 4, verbose=TRUE, align="center"), c(rep(NA_real_, 4), 5.5, 6.5, 7.5, 8.5, NA, NA), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 4, hasna 0, narm 0",
  "frollmeanFast: NA (or other non-finite) value(s) are present in input, skip non-NA attempt and run with extra care for NAs",
  "frollmean: align 0, shift answer by -2"
))

#### fill constant
test(9999.068, frollmean(1:5, 4, fill=0), c(0, 0, 0, 2.5, 3.5))
test(9999.069, frollmean(1:5, 4, fill=-5), c(-5, -5, -5, 2.5, 3.5))
test(9999.070, frollmean(1:5, 4, fill=100), c(100, 100, 100, 2.5, 3.5))
test(9999.071, frollmean(1:5, 4, fill=Inf), c(Inf, Inf, Inf, 2.5, 3.5))
test(9999.072, frollmean(1:5, 4, fill=NaN), c(NaN, NaN, NaN, 2.5, 3.5))

#### fill coercion
test(9999.073, frollmean(1:3, 2, fill=0), c(0, 1.5, 2.5))
test(9999.074, frollmean(1:3, 2, fill=0L), c(0, 1.5, 2.5))
test(9999.075, frollmean(1:3, 2, fill=NA_integer_), c(NA_real_, 1.5, 2.5))
test(9999.076, frollmean(1:3, 2, fill=1:2), error="fill must be a vector of length 1")
test(9999.077, frollmean(1:3, 2, fill=NA), c(NA_real_, 1.5, 2.5))
test(9999.078, frollmean(1:3, 2, fill=TRUE), error="fill must be numeric")
test(9999.079, frollmean(1:3, 2, fill=FALSE), error="fill must be numeric")
test(9999.080, frollmean(1:3, 2, fill="a"), error="fill must be numeric")
test(9999.081, frollmean(1:3, 2, fill=factor("a")), error="fill must be numeric")
test(9999.082, frollmean(1:3, 2, fill=list(NA)), error="fill must be numeric")

## edge cases
#### length(x)==0
test(9999.083, frollmean(numeric(0), 2), numeric(0))
test(9999.084, frollmean(list(1:3, numeric()), 2), list(c(NA_real_, 1.5, 2.5), numeric(0)))
#### length(n)==0
test(9999.085, frollmean(1:3, integer()), error="n must be non 0 length")
test(9999.086, frollmean(list(1:3, 2:4), integer()), error="n must be non 0 length")
#### n==0
test(9999.087, frollmean(1:3, c(2,0)), error="n must be positive integer values")
test(9999.088, frollmean(list(1:3, 2:4), 0), error="n must be positive integer values")
#### n<0
test(9999.089, frollmean(1:3, -2), error="n must be positive integer values")
#### n[[1L]]>0 && n[[2L]]<0
test(9999.090, frollmean(1:3, c(2, -2)), error="n must be positive integer values")
#### n[[1L]]==n[[2L]]
test(9999.091, frollmean(1:3, c(2, 2)), list(c(NA_real_, 1.5, 2.5), c(NA_real_, 1.5, 2.5)))
test(9999.092, frollmean(list(1:3, 4:6), c(2, 2)), list(c(NA_real_, 1.5, 2.5), c(NA_real_, 1.5, 2.5), c(NA_real_, 4.5, 5.5), c(NA_real_, 4.5, 5.5)))
#### n>length(x)
test(9999.093, frollmean(list(1:3, 4:6), 4), list(c(NA_real_, NA_real_, NA_real_), c(NA_real_, NA_real_, NA_real_)))
test(9999.0931, frollmean(list(1:3, 4:6), 4, align="center"), list(c(NA_real_, NA_real_, NA_real_), c(NA_real_, NA_real_, NA_real_)))
test(9999.0932, frollmean(list(1:3, 4:6), 4, align="left"), list(c(NA_real_, NA_real_, NA_real_), c(NA_real_, NA_real_, NA_real_)))
test(9999.0933, frollmean(list(1:3, 4:6), 4, verbose=TRUE), list(c(NA_real_, NA_real_, NA_real_), c(NA_real_, NA_real_, NA_real_)), output="frollmean: window width longer than input vector, returning all NA vector")
#### n==length(x)
test(9999.094, frollmean(list(1:3, 4:6), 3), list(c(NA_real_, NA_real_, 2), c(NA_real_, NA_real_, 5)))
#### n<length(x[[1L]]) && n>length(x[[2L]])
test(9999.095, frollmean(list(1:5, 1:2), 3), list(c(NA_real_, NA_real_, 2, 3, 4), c(NA_real_, NA_real_)))
#### n==1
test(9999.096, frollmean(1:4, 1), as.double(1:4))
test(9999.097, frollmean(1:4, 1, algo="exact"), as.double(1:4))
test(9999.098, frollmean(1:4, 1, align="center"), as.double(1:4))
test(9999.099, frollmean(1:4, 1, align="center", algo="exact"), as.double(1:4))
test(9999.100, frollmean(1:4, 1, align="left"), as.double(1:4))
test(9999.101, frollmean(1:4, 1, align="left", algo="exact"), as.double(1:4))
#### length(x)==1 && n==1
test(9999.102, frollmean(5, 1), 5)
test(9999.103, frollmean(list(1, 10, 5), 1), list(1, 10, 5))
test(9999.104, frollmean(5, 1, align="left"), 5)
test(9999.105, frollmean(list(1, 10, 5), 1, align="left"), list(1, 10, 5))
test(9999.106, frollmean(5, 1, align="center"), 5)
test(9999.107, frollmean(list(1, 10, 5), 1, align="center"), list(1, 10, 5))
#### length(x)==1 && n==2
test(9999.108, frollmean(5, 2), NA_real_)
test(9999.109, frollmean(list(1, 10, 5), 2), list(NA_real_, NA_real_, NA_real_))
test(9999.110, frollmean(5, 2, align="left"), NA_real_)
test(9999.111, frollmean(list(1, 10, 5), 2, align="left"), list(NA_real_, NA_real_, NA_real_))
test(9999.112, frollmean(5, 2, align="center"), NA_real_)
test(9999.113, frollmean(list(1, 10, 5), 2, align="center"), list(NA_real_, NA_real_, NA_real_))
#### n==Inf
test(9999.114, frollmean(1:5, Inf), error="n must be positive integer values", warning="NAs introduced by coercion to integer range")
#### n==c(5, Inf)
test(9999.115, frollmean(1:5, c(5, Inf)), error="n must be positive integer values", warning="NAs introduced by coercion to integer range")
#### is.complex(n)
test(9999.116, frollmean(1:5, 3i), error="n must be integer")
#### is.character(n)
test(9999.117, frollmean(1:5, "a"), error="n must be integer")
#### is.factor(n)
test(9999.118, frollmean(1:5, as.factor("a")), error="n must be integer")
#### is.list(n)
test(9999.119, frollmean(1:5, list(1:5)), error="n must be integer, list is accepted for adaptive TRUE")
#### verbose=NA
test(9999.1191, frollmean(1:5, 2, verbose=NA), error="verbose must be TRUE or FALSE")
#### adaptive=NA
test(9999.1192, frollmean(1:5, 2, adaptive=NA), error="adaptive must be TRUE or FALSE")
#### na.rm=NA
test(9999.1193, frollmean(1:5, 2, na.rm=NA), error="na.rm must be TRUE or FALSE")
#### hasNA=1
test(9999.1194, frollmean(1:5, 2, hasNA=1), error="hasNA must be TRUE, FALSE or NA")
#### hasNA=FALSE na.rm=TRUE
test(9999.1195, frollmean(1:5, 2, na.rm=TRUE, hasNA=FALSE), error="using hasNA FALSE and na.rm TRUE does not make sense, if you know there are NA values use hasNA TRUE, otherwise leave it as default NA")
#### exact na.rm=TRUE adaptive=TRUE verbose=TRUE
test(9999.1196, frollmean(c(1:5,NA), 1:6, algo="exact", na.rm=TRUE, adaptive=TRUE, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel", 
  "fadaptiverollmeanExact: running for input length 6, hasna 0, narm 1", 
  "fadaptiverollmeanExact: NA (or other non-finite) value(s) are present in input, re-running with extra care for NAs"
))
#### exact na.rm=TRUE verbose=TRUE
test(9999.1197, frollmean(c(1:5,NA), 2, algo="exact", na.rm=TRUE, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel", 
  "frollmeanExact: running for input length 6, window 2, hasna 0, narm 1", 
  "frollmeanExact: NA (or other non-finite) value(s) are present in input, re-running with extra care for NAs"
))
#### adaptive=TRUE n=character
test(9999.1198, frollmean(1:5, n=letters[1:5], adaptive=TRUE), error="n must be integer vector or list of integer vectors")

#### non-finite values (NA, NaN, Inf, -Inf)
ma = function(x, n, na.rm=FALSE, nf.rm=FALSE) {
  if (!is.double(x)) x = as.double(x)
  if (!is.integer(n)) n = as.integer(n)
  ans = rep(NA_real_, nx<-length(x))
  if (nf.rm) x[!is.finite(x)] = NA_real_ # exact=F consistency due to https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17441
  for (i in n:nx) ans[i]=mean(x[(i-n+1):i], na.rm=na.rm)
  ans
}
n = 4
x = 1:16
x[5] = NaN
test(9999.120, identical(frollmean(x, n), ma(x, n, nf.rm=TRUE)))
test(9999.121, identical(frollmean(x, n, algo="exact"), ma(x, n)))
x[6] = NA
test(9999.122, identical(frollmean(x, n), ma(x, n, nf.rm=TRUE)))
test(9999.123, identical(frollmean(x, n, algo="exact"), ma(x, n)))
#### test inconsistency of NaN-NA order is consistent to https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17441
x[5] = NA
x[6] = NaN
test(9999.124, identical(frollmean(x, n), ma(x, n, nf.rm=TRUE)))
test(9999.125, identical(frollmean(x, n, algo="exact"), ma(x, n)))
x[5] = Inf
test(9999.126, identical(frollmean(x, n), ma(x, n, nf.rm=TRUE)))
test(9999.127, identical(frollmean(x, n, algo="exact"), ma(x, n)))
x[6] = -Inf
test(9999.128, identical(frollmean(x, n), ma(x, n, nf.rm=TRUE)))
test(9999.129, identical(frollmean(x, n, algo="exact"), ma(x, n)))
x[5:7] = c(NA, Inf, -Inf)
test(9999.130, identical(frollmean(x, n), ma(x, n, nf.rm=TRUE)))
test(9999.131, identical(frollmean(x, n, algo="exact"), ma(x, n)))

#### adaptive window
ama = function(x, n, na.rm=FALSE, fill=NA, nf.rm=FALSE) {
  # adaptive moving average in R
  stopifnot((nx<-length(x))==length(n))
  if (nf.rm) x[!is.finite(x)] = NA_real_
  ans = rep(NA_real_, nx)
  for (i in seq_along(x)) {
    ans[i] = if (i >= n[i])
      mean(x[(i-n[i]+1):i], na.rm=na.rm)
    else as.double(fill)
  }
  ans
}

x = rnorm(1e3)
n = rep(20L, 1e3) # pseudo adaptive
test(9999.132, frollmean(x, n[1L]), frollmean(x, n, adaptive=TRUE)) # n auto wrapped in list
test(9999.133, frollmean(x, n[1L]), frollmean(x, list(n), adaptive=TRUE))
test(9999.134, frollmean(x, n[1L]), frollmean(x, n, algo="exact", adaptive=TRUE))

x = c(1:4,2:5,4:6,5L)
n = c(2L, 2L, 2L, 5L, 4L, 5L, 1L, 1L, 2L, 3L, 6L, 3L)
ans1 = ama(x, n)
ans2 = frollmean(x, list(n), adaptive=TRUE)
ans3 = frollmean(x, list(n), algo="exact", adaptive=TRUE)
test(9999.135, ans1, ans2)
test(9999.136, ans1, ans3)

x = data.table(x=x, y=x/2) # multiple columns and multiple windows
ln = list(n, n+1L)
ans1 = list(ama(x[[1L]], ln[[1L]]), ama(x[[1L]], ln[[2L]]), ama(x[[2L]], ln[[1L]]), ama(x[[2L]], ln[[2L]]))
ans2 = frollmean(x, ln, adaptive=TRUE)
ans3 = frollmean(x, ln, algo="exact", adaptive=TRUE)
test(9999.137, ans1, ans2)
test(9999.138, ans1, ans3)

#### adaptive fill
x = c(1:4,2:5,4:6,5L)
n = c(2L, 2L, 2L, 5L, 4L, 5L, 1L, 1L, 2L, 3L, 6L, 3L)
ans1 = ama(x, n, fill=150)
ans2 = frollmean(x, n, adaptive=TRUE, fill=150)
ans3 = frollmean(x, n, adaptive=TRUE, algo="exact", fill=150)
test(9999.139, ans1, ans2)
test(9999.140, ans1, ans3)

#### adaptive na.rm
x = c(1:4,NA,2:5,NA,4:6,NA,5L)
n = c(2L, 2L, 2L, 5L, 3L, 4L, 5L, 1L, 2L, 1L, 2L, 4L, 3L, 6L, 3L)
ans1 = ama(x, n)
ans2 = frollmean(x, n, adaptive=TRUE)
ans3 = frollmean(x, n, algo="exact", adaptive=TRUE)
test(9999.141, ans1, ans2)
test(9999.142, ans1, ans3)
ans1 = ama(x, n, na.rm=TRUE)
ans2 = frollmean(x, n, na.rm=TRUE, adaptive=TRUE)
ans3 = frollmean(x, n, na.rm=TRUE, algo="exact", adaptive=TRUE)
test(9999.143, ans1, ans2)
test(9999.144, ans1, ans3)
#### interactive test 3e9 vector where 2.5e9 are NAs to confirm uint_fast64_t running NA counter
if (FALSE) {
  x = c(rep(1, 3e8), rep(NA_real_, 2.5e9), rep(1, 2e8))
  n1 = 1e3; n2 = 1e4
  n = c(rep(n1, 1.5e9), rep(n2, 1.5e9))
  stopifnot(length(x)==3e9, length(n)==3e9)
  ans = frollmean(x, list(n), adaptive=TRUE)
  stopifnot(
    all.equal(ans[1:(n1+1)], c(rep(NA_real_, n1-1), 1, 1)),
    all.equal(ans[3e8+(-1:1)], c(1, 1, NA)),
    all.equal(ans[2.8e9+n2+(-1:1)], c(NA_real_, 1, 1)),
    all.equal(ans[(3e9-n2):3e9], rep(1, n2+1))
  )
}

#### adaptive limitations
test(9999.145, frollmean(1:2, 1:2, adaptive=TRUE, align="right"), c(1, 1.5))
test(9999.146, frollmean(1:2, 1:2, adaptive=TRUE, align="center"), error="using adaptive TRUE and align argument different than 'right' is not implemented")
test(9999.147, frollmean(1:2, 1:2, adaptive=TRUE, align="left"), error="using adaptive TRUE and align argument different than 'right' is not implemented")
test(9999.148, frollmean(list(1:2, 1:3), list(1:2), adaptive=TRUE), error="adaptive rolling function can only process 'x' having equal length of elements, like data.table or data.frame. If you want to call rolling function on list having variable length of elements call it for each field separately")

#### adaptive exact
fastama = function(x, n, na.rm, fill=NA) {
  if (!missing(na.rm)) stop("fast adaptive moving average implemented in R does not handle NAs, input having NAs will result in incorrect answer so not even try to compare to it")
  # fast implementation of adaptive moving average in R, in case of NAs incorrect answer
  stopifnot((nx<-length(x))==length(n))
  cs = cumsum(x)
  ans = rep(NA_real_, nx)
  for (i in seq_along(cs)) {
    ans[i] = if (i == n[i])
      cs[i]/n[i]
    else if (i > n[i])
      (cs[i]-cs[i-n[i]])/n[i]
    else as.double(fill)
  }
  ans
}
x = c(1:3, 1e9L, 2:5, 5e9, 4:6)
n = c(2L, 2L, 2L, 5L, 4L, 5L, 1L, 1L, 2L, 3L, 6L, 3L)
ans1 = ama(x, n)
ans2 = frollmean(x, n, adaptive=TRUE)
ans3 = frollmean(x, n, adaptive=TRUE, algo="exact")
ans4 = fastama(x, n)
test(9999.149, ans1, ans2)
test(9999.150, ans1, ans3)
test(9999.151, ans1, ans4)

x = sample(c(rnorm(1e3, 1e2), rnorm(1e1, 1e9, 1e2), abs(rnorm(1e1, 1e-9, 1e-2))))
n = sample(1:20, length(x), TRUE)
ans1 = ama(x, n)
ans2 = frollmean(x, n, adaptive=TRUE)
ans3 = frollmean(x, n, adaptive=TRUE, algo="exact")
ans4 = fastama(x, n)
anserr = list(
  froll_exact_f = ans1-ans2,
  froll_exact_t = ans1-ans3,
  fastama = ans1-ans4
)
errs = lapply(lapply(anserr, abs), sum, na.rm=TRUE)
test(9999.152, errs[["froll_exact_t"]] < errs[["froll_exact_f"]])
test(9999.153, errs[["froll_exact_t"]] < errs[["fastama"]])

x = sample(c(1:100, 5e9, 5e-9))
n = sample(1:10, length(x), TRUE)
ans1 = ama(x, n)
ans2 = frollmean(x, n, adaptive=TRUE)
ans3 = frollmean(x, n, adaptive=TRUE, algo="exact")
ans4 = fastama(x, n)
anserr = list(
  froll_exact_f = ans1-ans2,
  froll_exact_t = ans1-ans3,
  fastama = ans1-ans4
)
errs = lapply(lapply(anserr, abs), sum, na.rm=TRUE)
test(9999.154, errs[["froll_exact_t"]] < errs[["froll_exact_f"]])
test(9999.155, errs[["froll_exact_t"]] < errs[["fastama"]])

## edge cases adaptive
#### is.integer(n)
test(9999.156, frollmean(1:5, 1:5, adaptive=TRUE), seq(1,3,0.5))
#### is.integer(n) && length(n)!=length(x)
test(9999.157, frollmean(1:10, 1:5, adaptive=TRUE), error="length of integer vector(s) provided as list to 'n' argument must be equal to number of observations provided in 'x'")
#### is.list(n) && length(n[[1L]])!=length(x)
test(9999.158, frollmean(1:10, list(1:5), adaptive=TRUE), error="length of integer vector(s) provided as list to 'n' argument must be equal to number of observations provided in 'x'")

#### non-finite values (NA, NaN, Inf, -Inf)
n = c(4,1,4,5,5,4,6,5,4,4,2,3,4,3,2,4)
x = 1:16
x[5] = NaN
test(9999.159, identical(frollmean(x, n, adaptive=TRUE), ama(x, n, nf.rm=TRUE)))
test(9999.160, identical(frollmean(x, n, algo="exact", adaptive=TRUE), ama(x, n)))
x[6] = NA
test(9999.161, identical(frollmean(x, n, adaptive=TRUE), ama(x, n, nf.rm=TRUE)))
test(9999.162, identical(frollmean(x, n, algo="exact", adaptive=TRUE), ama(x, n)))
#### test inconsistency of NaN-NA order is consistent to https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17441
x[5] = NA
x[6] = NaN
test(9999.163, identical(frollmean(x, n, adaptive=TRUE), ama(x, n, nf.rm=TRUE)))
test(9999.164, identical(frollmean(x, n, algo="exact", adaptive=TRUE), ama(x, n)))
x[5] = Inf
test(9999.165, identical(frollmean(x, n, adaptive=TRUE), ama(x, n, nf.rm=TRUE)))
test(9999.166, identical(frollmean(x, n, algo="exact", adaptive=TRUE), ama(x, n)))
x[6] = -Inf
test(9999.167, identical(frollmean(x, n, adaptive=TRUE), ama(x, n, nf.rm=TRUE)))
test(9999.168, identical(frollmean(x, n, algo="exact", adaptive=TRUE), ama(x, n)))
x[5:7] = c(NA, Inf, -Inf)
test(9999.169, identical(frollmean(x, n, adaptive=TRUE), ama(x, n, nf.rm=TRUE)))
test(9999.170, identical(frollmean(x, n, algo="exact", adaptive=TRUE), ama(x, n)))

## test verbose messages
x = 1:10
n = 3
test(9999.171, frollmean(x, n, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0"))
test(9999.172, frollmean(list(x, x+1), n, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 2x1",
  "frollfunR: 2 column(s) and 1 window(s), entering parallel execution, but actually single threaded due to enabled verbose which is not thread safe",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0"))
test(9999.173, frollmean(x, c(n, n+1), verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x2",
  "frollfunR: 1 column(s) and 2 window(s), entering parallel execution, but actually single threaded due to enabled verbose which is not thread safe",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanFast: running for input length 10, window 4, hasna 0, narm 0"))
test(9999.174, frollmean(list(x, x+1), c(n, n+1), verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 2x2",
  "frollfunR: 2 column(s) and 2 window(s), entering parallel execution, but actually single threaded due to enabled verbose which is not thread safe",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanFast: running for input length 10, window 4, hasna 0, narm 0"))
test(9999.175, frollmean(x, n, algo="exact", verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel",
  "frollmeanExact: running for input length 10, window 3, hasna 0, narm 0"))
test(9999.176, frollmean(x, n, align="center", verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0",
  "frollmean: align 0, shift answer by -1"))
test(9999.177, frollmean(x, n, align="left", verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0",
  "frollmean: align -1, shift answer by -2"))
nn = c(1:4,2:3,1:4)
test(9999.178, frollmean(x, nn, adaptive=TRUE, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "fadaptiverollmeanFast: running for input length 10, hasna 0, narm 0"))
test(9999.179, frollmean(x, nn, algo="exact", adaptive=TRUE, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel",
  "fadaptiverollmeanExact: running for input length 10, hasna 0, narm 0"))

x[8] = NA
test(9999.180, frollmean(x, n, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "frollmeanFast: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanFast: NA (or other non-finite) value(s) are present in input, re-running with extra care for NAs"))
test(9999.181, frollmean(x, n, algo="exact", verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel",
  "frollmeanExact: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanExact: NA (or other non-finite) value(s) are present in input, na.rm was FALSE so in 'exact' implementation NAs were handled already, no need to re-run"))
test(9999.182, frollmean(x, nn, adaptive=TRUE, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped",
  "fadaptiverollmeanFast: running for input length 10, hasna 0, narm 0",
  "fadaptiverollmeanFast: NA (or other non-finite) value(s) are present in input, re-running with extra care for NAs"))
test(9999.183, frollmean(x, nn, algo="exact", adaptive=TRUE, verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel",
  "fadaptiverollmeanExact: running for input length 10, hasna 0, narm 0",
  "fadaptiverollmeanExact: NA (or other non-finite) value(s) are present in input, na.rm was FALSE so in 'exact' implementation NAs were handled already, no need to re-run"))

d = as.data.table(list(1:10/2, 10:1/4))
test(9999.184, frollmean(d[,1], 3, algo="exact", verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 1x1",
  "frollfunR: single column and single window, parallel processing by multiple answer vectors skipped but 'exact' version of rolling function will compute results in parallel",
  "frollmeanExact: running for input length 10, window 3, hasna 0, narm 0"
))
test(9999.185, frollmean(d, 3:4, algo="exact", verbose=TRUE), output=c(
  "frollfunR: allocating memory for results 2x2",
  "frollfunR: 2 column(s) and 2 window(s), entering parallel execution, but actually single threaded due to enabled verbose which is not thread safe, 'exact' version of rolling function will compute results in parallel anyway as it does not print with verbose",
  "frollmeanExact: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanExact: running for input length 10, window 4, hasna 0, narm 0",
  "frollmeanExact: running for input length 10, window 3, hasna 0, narm 0",
  "frollmeanExact: running for input length 10, window 4, hasna 0, narm 0"
))

## test warnings
test(9999.186, frollmean(c(1:2,NA,4:10), 4, hasNA=FALSE), c(rep(NA_real_, 6), 5.5, 6.5, 7.5, 8.5), warning="hasNA=FALSE used but NA (or other non-finite) value(s) are present in input, use default hasNA=NA to avoid this warning")
test(9999.187, frollmean(c(1:2,NA,4:10), 2, hasNA=FALSE), c(NA, 1.5, NA, NA, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5), warning="hasNA=FALSE used but NA (or other non-finite) value(s) are present in input, use default hasNA=NA to avoid this warning")

## validation

set.seed(108)
makeNA = function(x, ratio=0.1) {id=sample(length(x), as.integer(length(x) * ratio)); x[id]=NA; x}
num = 9999.5000 # book 4000 tests for zoo
#### against zoo
if (requireNamespace("zoo", quietly=TRUE)) {
  drollapply = function(...) as.double(zoo::rollapply(...)) # rollapply is not consistent in data type of answer, force to double
  zoo_compare = function(x, n) {
    num.step = 0.0001
    #### fun, align, na.rm, fill, exact
    for (fun in c("mean")) { # ,"sum"
      for (align in c("right","center","left")) {
        for (na.rm in c(FALSE, TRUE)) {
          for (fill in c(NA_real_, 0)) {
            for (algo in c("fast", "exact")) {
              num <<- num + num.step
              eval(substitute( # so we can have values displayed in output/log rather than variables
                test(.num,
                     froll(.fun, x, n, align=.align, fill=.fill, na.rm=.na.rm, algo=.algo),
                     drollapply(x, n, FUN=.fun, fill=.fill, align=.align, na.rm=.na.rm)),
                list(.num=num, .fun=fun, .align=align, .fill=fill, .na.rm=na.rm, .algo=algo)
              ))
            }
          }
        }
      }
    }
  }
  ## no NA
  x = rnorm(1e3); n = 50 # x even, n even
  zoo_compare(x, n)
  x = rnorm(1e3+1); n = 50 # x odd, n even
  zoo_compare(x, n)
  x = rnorm(1e3); n = 51 # x even, n odd
  zoo_compare(x, n)
  x = rnorm(1e3+1); n = 51 # x odd, n odd
  zoo_compare(x, n)
  ## leading and trailing NAs
  x = c(rep(NA, 60), rnorm(1e3), rep(NA, 60)); n = 50
  zoo_compare(x, n)
  x = c(rep(NA, 60), rnorm(1e3+1), rep(NA, 60)); n = 50
  zoo_compare(x, n)
  x = c(rep(NA, 60), rnorm(1e3), rep(NA, 60)); n = 51
  zoo_compare(x, n)
  x = c(rep(NA, 60), rnorm(1e3+1), rep(NA, 60)); n = 51
  zoo_compare(x, n)
  ## random NA
  x = makeNA(rnorm(1e3)); n = 50
  zoo_compare(x, n)
  x = makeNA(rnorm(1e3+1)); n = 50
  zoo_compare(x, n)
  x = makeNA(rnorm(1e3)); n = 51
  zoo_compare(x, n)
  x = makeNA(rnorm(1e3+1)); n = 51
  zoo_compare(x, n)
}
#### adaptive moving average compare
num = 9999.9000 # 1000 tests should be more than enough
afun = function(fun, x, n, na.rm=FALSE, fill=NA, nf.rm=FALSE) {
  # adaptive moving average in R
  stopifnot((nx<-length(x))==length(n))
  ans = rep(NA_real_, nx)
  if (nf.rm) x[!is.finite(x)] = NA_real_
  FUN = match.fun(fun)
  for (i in seq_along(x)) {
    ans[i] = if (i >= n[i])
      FUN(x[(i-n[i]+1):i], na.rm=na.rm)
    else as.double(fill)
  }
  ans
}
afun_compare = function(x, n) {
  num.step = 0.0001
  #### fun, na.rm, fill, exact
  for (fun in c("mean")) { # ,"sum"
    for (na.rm in c(FALSE, TRUE)) {
      for (fill in c(NA_real_, 0)) {
        for (algo in c("fast", "exact")) {
          num <<- num + num.step
          eval(substitute(
            test(.num,
                 froll(.fun, x, n, fill=.fill, na.rm=.na.rm, algo=.algo, adaptive=TRUE),
                 afun(.fun, x, n, fill=.fill, na.rm=.na.rm, nf.rm=.nf.rm)),
            list(.num=num, .fun=fun, .fill=fill, .na.rm=na.rm, .algo=algo, .nf.rm=algo!="exact")
          ))
        }
      }
    }
  }
}
#### no NA
x = rnorm(1e3); n = sample(50, length(x), TRUE) # x even, n even
afun_compare(x, n)
x = rnorm(1e3+1); n = sample(50, length(x), TRUE) # x odd, n even
afun_compare(x, n)
x = rnorm(1e3); n = sample(51, length(x), TRUE) # x even, n odd
afun_compare(x, n)
x = rnorm(1e3+1); n = sample(51, length(x), TRUE) # x odd, n odd
afun_compare(x, n)
#### leading and trailing NAs
x = c(rep(NA, 60), rnorm(1e3), rep(NA, 60)); n = sample(50, length(x), TRUE)
afun_compare(x, n)
x = c(rep(NA, 60), rnorm(1e3+1), rep(NA, 60)); n = sample(50, length(x), TRUE)
afun_compare(x, n)
x = c(rep(NA, 60), rnorm(1e3), rep(NA, 60)); n = sample(51, length(x), TRUE)
afun_compare(x, n)
x = c(rep(NA, 60), rnorm(1e3+1), rep(NA, 60)); n = sample(51, length(x), TRUE)
afun_compare(x, n)
#### random NA
x = makeNA(rnorm(1e3)); n = sample(50, length(x), TRUE)
afun_compare(x, n)
x = makeNA(rnorm(1e3+1)); n = sample(50, length(x), TRUE)
afun_compare(x, n)
x = makeNA(rnorm(1e3)); n = sample(51, length(x), TRUE)
afun_compare(x, n)
x = makeNA(rnorm(1e3+1)); n = sample(51, length(x), TRUE)
afun_compare(x, n)
rm(num)

setDTthreads(oldDTthreads)
cat("froll unit tests successfully passed\n")
