context("effect scan by scan1coef with polygenic effect")

# calc estimates via lm(), just one chromosome
eff_via_lm <-
    function(probs, pheno, kinship, addcovar=NULL, intcovar=NULL,
             se=TRUE)
{
    kinship <- qtl2:::double_kinship(kinship) # need 2*kinship for LMM
    kinship <- decomp_kinship(kinship)
    eigenvec <- kinship$vectors
    hsq <- qtl2:::calc_hsq_clean(kinship, as.matrix(pheno), addcovar, NULL, FALSE, NULL,
                                 reml=TRUE, cores=1, check_boundary=TRUE, tol=1e-12)$hsq[1,1]

    wts <- 1/(hsq*kinship$values + (1-hsq))

    npos <- dim(probs)[3]
    nind <- length(pheno)
    ngen <- dim(probs)[2]

    nadd <- ifelse(is.null(addcovar), 0, ncol(addcovar))
    nint <- ifelse(is.null(intcovar), 0, ncol(intcovar))
    ncoef <- ngen + nadd + (ngen-1)*nint

    result <- matrix(nrow=npos, ncol=ncoef)
    SEs <- matrix(nrow=npos, ncol=ncoef)

    if(!is.null(intcovar)) intcovar <- as.matrix(intcovar)

    pheno <- eigenvec %*% pheno

    for(i in 1:npos) {
        X <- probs[,,i]
        if(!is.null(addcovar)) X <- cbind(X, addcovar)
        if(!is.null(intcovar)) {
            for(j in 1:ncol(intcovar))
                X <- cbind(X, probs[,-1,i]*intcovar[,j])
        }
        X <- eigenvec %*% X

        lm.out <- lm(pheno ~ -1 + X, weights=wts)
        result[i,] <- lm.out$coef
        if(se) # need to deal with NAs
            SEs[i,!is.na(lm.out$coef)] <- summary(lm.out)$coef[,2]
        else SEs <- NULL
    }

    attr(result, "SE") <- SEs

    class(result) <- c("scan1coef", "scan1", "matrix")
    result
}

# function to maybe swap NAs between 2nd and 3rd columns in 22nd row
swap_nas <-
    function(mat, row=22, col=2:3, also_se=TRUE)
{
    if(is.na(mat[row,col[1]]) && !is.na(mat[row, col[2]])) {
        mat[row,col] <- mat[row,rev(col)]
    }

    if(also_se) {
        attr(mat, "SE") <- swap_nas(attr(mat, "SE"), row, col, also_se=FALSE)
    }

    mat
}

test_that("scan1coef_pg for grav", {

    set.seed(9308594)

    grav <- read_cross2(system.file("extdata", "grav2.zip", package="qtl2"))
    grav <- grav[1:30,c(3,4)]
    map <- insert_pseudomarkers(grav$gmap, step=1)
    pr <- calc_genoprob(grav, map)
    K <- calc_kinship(pr)
    phe <- grav$pheno[,"T330",drop=FALSE]

    est <- scan1coef(pr[,"3"], phe, K, se=FALSE, zerosum=FALSE)
    est_lm <- eff_via_lm(pr[["3"]], phe, K)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, K, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # pre-decomp kinship
    Ke <- decomp_kinship(K)
    est <- scan1coef(pr[,"3"], phe, Ke, zerosum=FALSE)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, Ke, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # kinship is a list of length 1
    Klist <- list("1"=K)
    est <- scan1coef(pr[,"3"], phe, Klist, zerosum=FALSE)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, Klist, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # include covariate
    covar <- cbind(chr3=pr[["3"]][,2,"CC.266L"])
    est <- scan1coef(pr[,"3"], phe, K, covar, se=FALSE, zerosum=FALSE)
    est <- swap_nas(est, also_se=FALSE)
    est_lm <- eff_via_lm(pr[["3"]], phe, K, covar)
    est_lm <- swap_nas(est_lm)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, K, covar, se=TRUE, zerosum=FALSE)
    est <- swap_nas(est)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # pre-computed eigen decomp
    est <- scan1coef(pr[,"3"], phe, Ke, covar, zerosum=FALSE)
    est <- swap_nas(est, also_se=FALSE)
    expect_equivalent(est, est_lm)  ## <- fix problem here
    est <- scan1coef(pr[,"3"], phe, Ke, covar, se=TRUE, zerosum=FALSE)
    est <- swap_nas(est)
    expect_equivalent(est, est_lm)  ## <- fix problem here
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))  ## <- fix problem here

    # interactive covariate
    est <- scan1coef(pr[,"3"], phe, K, covar, intcovar=covar, se=FALSE, zerosum=FALSE)
    est_lm <- eff_via_lm(pr[["3"]], phe, K, covar, covar)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, K, covar, intcovar=covar, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # pre-computed eigen decomp
    est <- scan1coef(pr[,"3"], phe, Ke, covar, intcovar=covar, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    est <- scan1coef(pr[,"3"], phe, Ke, covar, intcovar=covar, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    skip_on_cran()

    # two covariates
    covar <- cbind(covar, chr4=pr[["4"]][,2,"CD.329C-Col"])
    est <- scan1coef(pr[,"3"], phe, K, covar, se=FALSE, zerosum=FALSE)
    est <- swap_nas(est, also_se=FALSE)
    est_lm <- eff_via_lm(pr[["3"]], phe, K, covar)
    est_lm <- swap_nas(est_lm)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, K, covar, se=TRUE, zerosum=FALSE)
    est <- swap_nas(est)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # pre-computed eigen decomp
    est <- scan1coef(pr[,"3"], phe, Ke, covar, zerosum=FALSE)
    est <- swap_nas(est, also_se=FALSE)
    expect_equivalent(est, est_lm)
    est <- scan1coef(pr[,"3"], phe, Ke, covar, se=TRUE, zerosum=FALSE)
    est <- swap_nas(est)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # two interactive covariates
    est <- scan1coef(pr[,"3"], phe, K, covar, intcovar=covar, se=FALSE, zerosum=FALSE)
    est_lm <- eff_via_lm(pr[["3"]], phe, K, covar, covar)
    expect_equivalent(est, est_lm)

    est <- scan1coef(pr[,"3"], phe, K, covar, intcovar=covar, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

    # pre-computed eigen decomp
    est <- scan1coef(pr[,"3"], phe, Ke, covar, intcovar=covar, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    est <- scan1coef(pr[,"3"], phe, Ke, covar, intcovar=covar, se=TRUE, zerosum=FALSE)
    expect_equivalent(est, est_lm)
    expect_equivalent(attr(est, "SE"), attr(est_lm, "SE"))

})


test_that("scan1coef deals with mismatching individuals", {

    skip_on_cran()

    iron <- read_cross2(system.file("extdata", "iron.zip", package="qtl2"))
    map <- insert_pseudomarkers(iron$gmap, step=2.5)
    probs <- calc_genoprob(iron, map, error_prob=0.002)
    kinship <- calc_kinship(probs, "loco")[["3"]]
    probs <- probs[,"3"]
    Xc <- get_x_covar(iron)
    X <- match(iron$covar$sex, c("f", "m"))-1
    names(X) <- rownames(iron$covar)

    phe <- iron$pheno[,2,drop=FALSE]

    ind <- c(1:50, 101:150)
    expected <- scan1coef(probs[ind,], phe[ind,,drop=FALSE], kinship[ind,ind], addcovar=X[ind])
    expect_equal(scan1coef(probs[ind,], phe, kinship, addcovar=X), expected)
    expect_equal(scan1coef(probs, phe[ind,,drop=FALSE], kinship, addcovar=X), expected)
    expect_equal(scan1coef(probs, phe, kinship[ind,ind], addcovar=X), expected)
    expect_equal(scan1coef(probs, phe, kinship, addcovar=X[ind]), expected)

})

test_that("scan1coef with weights works", {

    skip_on_cran()

    iron <- read_cross2(system.file("extdata", "iron.zip", package="qtl2"))
    map <- insert_pseudomarkers(iron$gmap, step=2.5)
    probs <- calc_genoprob(iron, map, error_prob=0.002)
    kinship <- calc_kinship(probs, "loco")[["3"]]
    probs <- probs[,"3"]
    X <- match(iron$covar$sex, c("f", "m"))-1
    names(X) <- rownames(iron$covar)
    phe <- iron$pheno[,2,drop=FALSE]

    weights <- setNames(runif(n_ind(iron), 1, 10), ind_ids(iron))

    # plain least squares match LMM when hsq==0?
    coef_hk <- scan1coef(probs, phe, addcovar=X, se=TRUE, zerosum=FALSE)
    coef_lmm0 <- scan1coef(probs, phe, kinship, X, se=TRUE, hsq=0, zerosum=FALSE)
    expect_equal(coef_hk, coef_lmm0)

    # plain least squares match LMM when hsq==0, with weights?
    coef_hk <- scan1coef(probs, phe, addcovar=X, weights=weights, se=TRUE, zerosum=FALSE)
    coef_lmm0 <- scan1coef(probs, phe, kinship, X, weights=weights, se=TRUE, hsq=0, zerosum=FALSE)
    expect_equal(coef_hk, coef_lmm0)

    # same with contrasts, no weights
    coef_hk <- scan1coef(probs, phe, addcovar=X, se=TRUE,
                    contrasts=cbind(mu=c(1,1,1), a=c(-1, 0, 1), d=c(0, 1, 0)))
    coef_lmm0 <- scan1coef(probs, phe, kinship, X, se=TRUE, hsq=0,
                    contrasts=cbind(mu=c(1,1,1), a=c(-1, 0, 1), d=c(0, 1, 0)))
    expect_equal(coef_hk, coef_lmm0)

    # same with contrasts, weights
    coef_hk <- scan1coef(probs, phe, addcovar=X, weights=weights, se=TRUE,
                    contrasts=cbind(mu=c(1,1,1), a=c(-1, 0, 1), d=c(0, 1, 0)))
    coef_lmm0 <- scan1coef(probs, phe, kinship, X, weights=weights, se=TRUE, hsq=0,
                    contrasts=cbind(mu=c(1,1,1), a=c(-1, 0, 1), d=c(0, 1, 0)))
    expect_equal(coef_hk, coef_lmm0)


    # check LMM with hsq not 0
    coef_lmm <- scan1coef(probs, phe, kinship, X, weights=weights, se=TRUE, zerosum=FALSE)

    ### compare to regress::regress()
    # k <- 2*kinship
    # v <- diag(1/weights)
    # out0_regress <- regress::regress(phe ~ X, ~ k + v, identity=FALSE)
    # p <- probs[[1]][,,6]
    # hsq <- out0_regress$sigma[1]/sum(out0_regress$sigma)
    # V <- hsq*k + (1-hsq)*v
    # out1_regress <- regress::regress(phe ~ -1 + p + X, ~ V, identity=FALSE)
    # co <- cbind(coef_lmm[6,], attr(coef_lmm, "SE")[6,])

})
