pprv <- function(y, x, alpha=NULL)
{
  
  # --------------------------------------------------
  # log(sum(exp(v))), a utility function to 
  # handle overflow/underflow
  # --------------------------------------------------
  "logsumexp" <- 
  function(v, na.rm=FALSE)
  {
    if(any(is.infinite(v))){
      stop("infinite values\n") 
    }
    
    if(na.rm){
      v = na.omit(v)
    }else{
      if(any(is.na(v))){
        stop("NA values\n") 
      }
    }
    
    lv = length(v)
    if(lv==0){ return(-Inf) }
    if(lv==1){ return(v[1]) }
    
    w1  = which.max(v)
    res = sum(exp(v[-w1] - v[w1]))
    lse = v[w1] + log(1+res)
    lse
  }
  
  # --------------------------------------------------
  # count the number of permutations of case-control
  # status given a matrix of k_i, k_j, k_l ...
  # each row of Omega is one combinations of those ks
  # --------------------------------------------------
  
  "logCount" <- 
  function(Omega, ns, n, ny)
  {
    len1  = nrow(Omega)
    len2  = ncol(Omega)
    njsum = sum(ns)
    
    tmp   = rep(NA, len1)
    
    for(i in 1:len1){
      kjsum = 0
      
      for(j in 1:len2){
        kj = Omega[i,j]
        nj = ns[j]
        if(j == 1){
          tmp[i] = lchoose(nj, kj)
        }else{
          tmp[i] = tmp[i] + lchoose(nj, kj)
        }
        kjsum = kjsum + kj
      }
      
      tmp[i] = tmp[i] + lchoose(n - njsum, ny - kjsum)
    }
    
    logsumexp(tmp)
  }
  
  # --------------------------------------------------
  # hyper cubic
  # --------------------------------------------------
    
  "hcube" <-
  function(nUpper, nLower=NULL)
  {
    # This is a modified version of the function  
    # with the same name in R/combinat
    # 
    # it outputs all points in a hypercuboid lattice
    # with lower and upper bounds by nLower and nUpper
    # or (if nLower == NULL) from 0 to nUpper.
    
    if(!is.null(nLower)) {
      x = nUpper - nLower
    }else{
      x = nUpper
    }
    
    x1 = x + 1
    ncols = length(x)
    nrows = prod(x1)
    cp    = c(1, cumprod(x1)[ - ncols])
    out   = lapply(as.list(1:length(x)), function(i, a, b, d, nr)
                  rep(rep(0:a[i], rep(b[i], d[i])), length = nr), 
                  a = x, b = cp, d=x1, nr = nrows)
    out   = array(unlist(out), c(nrows, ncols))

    if(!is.null(nLower)) {
      out = sweep(out, 2, nLower, FUN = "+")
    }
    out
  }
  
  # --------------------------------------------------
  # check inputs
  # --------------------------------------------------
  
  if(!is.numeric(y)){
    stop("y must be a nuemric vector\n")  
  }
  
  if(!is.matrix(x)){
    stop("x must be a matrix\n")  
  }
  
  if(ncol(x) < 2){
    stop("x must be a matrix with at least two columns\n")  
  }
  
  if(nrow(x) != length(y)){
    stop("x and y have different lengths\n")
  }
  
  if(any(sort(unique(y)) != c(0,1))){
    stop("y must be a binary vector with values 0 or 1\n")  
  }
  
  if(any(sort(unique(as.vector(x))) != c(0,1))){
    stop("x can only be 0 or 1\n")  
  }
    
  # --------------------------------------------------
  # data dimension 
  # --------------------------------------------------
  n  = length(y)
  ny = sum(y)
  nx = ncol(x) 
  
  # --------------------------------------------------
  # make sure the number of cases are larger than the 
  # number of samples with at least one rare variant
  # --------------------------------------------------
  wcase   = which(y==1)
  r1      = rep(1, ncol(x))
  rowSums = drop(x %*% r1)
  wrare   = which(rowSums > 0)
  
  if(length(wcase) <= length(wrare)){
    str1 = "number of cases must be larger than the number of samples"
    str1 = paste(str1, "with at least one rare variant")
    stop(str1)
  }
  
  # --------------------------------------------------
  # if there are only two covariates, 
  # handle them sepearately 
  # --------------------------------------------------
  
  if(nx==2){
    x1 = x[,1]
    x2 = x[,2]
    x3 = x1 + x2    
    n1  = sum(x1)
    n2  = sum(x2)
    w12 = which(x1 > 0 & x2 > 0)
    h12 = length(w12)
    n12 = n1 + n2 - h12
    
    if(h12 > 0 ){ x3[w12] = 1 }
    
    if(is.null(alpha)){
      
      m1  = length(which(x1[wcase]==1))
      p1  = 1 - phyper(m1 - 1, n1, n-n1, ny)
      
      m2  = length(which(x2[wcase]==1))
      p2  = 1 - phyper(m2 - 1, n2, n-n2, ny)
      
      m3  = length(which(x3[wcase]==1))
      p12 = 1 - phyper(m3 - 1, n12, n-n12, ny)
      
      xp = c(p1, p2, p12)
      alpha = min(xp)
    }

    t1  = qhyper(1 - alpha, ny, n-ny, n1) + 1
    t2  = qhyper(1 - alpha, ny, n-ny, n2) + 1
    t12 = qhyper(1 - alpha, ny, n-ny, n12) + 1

    if(h12 == 0){
      # if there is no overlap between x1 and x2
      gall   = numeric(0)
      
      if(n1 >= t1){
        Omega1 = matrix(seq(t1, n1, by=1), ncol=1)
        g1     = logCount(Omega1, ns=n1, n, ny)
        gall   = c(gall, g1)
      }
      
      nUpper    = nLower = rep(NA, 2)
      nUpper[1] = min(t1-1, n1)
      nUpper[2] = n2
      nLower[1] = 0
      nLower[2] = t2
      
      if(all(nUpper >= nLower)){
        Omega2 = hcube(nUpper, nLower)
        g2     = logCount(Omega2, ns=c(n1, n2), n, ny)
        gall   = c(gall, g2)
      }
      
      nUpper    = nLower = rep(NA, 2)
      nUpper[1] = min(t1-1, n1)
      nUpper[2] = min(t2-1, n2)
      
      nLower[1] = max(0, t12 - nUpper[2])
      nLower[2] = max(0, t12 - nUpper[1])
      
      if(all(nUpper >= nLower)){
        Omega3 = hcube(nUpper, nLower)
        sum3   = Omega3 %*% c(1,1)
        w2kp   = which(sum3 >= t12)
        if(length(w2kp) >= 1){
          Omega3 = Omega3[w2kp,,drop=FALSE]
          g12    = logCount(Omega3, ns=c(n1, n2), n, ny)
          gall   = c(gall, g12)
        }
      }
      
    }else{
      h1 = n1 - h12
      h2 = n2 - h12
      
      # if there is overlap between x1 and x2
      gall   = numeric(0)
      
      if(n1 >= t1){
        Omega1 = matrix(seq(t1, n1, by=1), ncol=1)
        gall   = logCount(Omega1, ns=n1, n, ny)
      }
      
      nUpper = nLower = rep(NA, 3)
      nUpper[1] = min(h1, t1-1)
      nUpper[2] = h2
      nUpper[3] = h12

      nLower[1] = 0
      nLower[2] = max(0, t2-h12)
      nLower[3] = 0

      if(all(nUpper >= nLower)){
        Omega2 = hcube(nUpper, nLower)
        sum1   = drop(Omega2[,c(1,3)] %*% c(1,1))
        sum2   = drop(Omega2[,c(2,3)] %*% c(1,1))
        w2kp   = which(sum1 < t1 & sum2 >= t2)
        
        if(length(w2kp) > 0){
          Omega2 = Omega2[w2kp,,drop=FALSE]
          g2     = logCount(Omega2, ns=c(h1, h2, h12), n, ny)
          gall   = c(gall, g2)
        }
      }
      
      nUpper = nLower = rep(NA, 3)
      nUpper[1] = min(h1, t1-1)
      nUpper[2] = min(h2, t2-1)
      nUpper[3] = h12
      
      nLower[1] = max(0, t12 - nUpper[2])
      nLower[2] = max(0, t12 - nUpper[1])
      nLower[3] = 0
      
      if(all(nUpper >= nLower)){ 
        Omega3 = hcube(nUpper, nLower)
        sum1   = drop(Omega3[,c(1,3)] %*% c(1,1))
        sum2   = drop(Omega3[,c(2,3)] %*% c(1,1))
        sum3   = drop(Omega3 %*% c(1,1,1))
        
        w2kp   = which(sum1 < t1 & sum2 < t2 & sum3 >= t12)
        if(length(w2kp) > 0){
          Omega3 = Omega3[w2kp,,drop=FALSE]
          g12    = logCount(Omega3, ns=c(h1, h2, h12), n, ny)
          gall   = c(gall, g12)
        }
      }
    }
    
  }else{
    # --------------------------------------------------
    # if there are more than 2 covariates 
    # --------------------------------------------------
    r1 = matrix(1, nrow=1, ncol=nrow(x))
    hs = ns = drop(r1 %*% x)
    nclass = nx

    wInterSec = which(rowSums > 1)
    interSect = FALSE
    
    # cntIntSum[i] = # smaples with rare allele in SNP i and 
    # at least one other SNP
    cntIntSum = rep(0, nx)
    idxL = cntL = namL = list()
    
    idxL[[1]] = diag(nx)
    cntL[[1]] = ns
    namL[[1]] = as.character(1:nx)
    
    if(length(wInterSec) > 0){
      interSect = TRUE
      idxInsTb  = table(apply(x[wInterSec, ,drop=FALSE], 1, paste, collapse="-"))
      xnew      = x[-wInterSec,]
      rnew      = r1[-wInterSec]
      hs        = as.numeric(c(drop(rnew %*% xnew), idxInsTb))
      nclass    = nclass + length(idxInsTb)
      
      idx1 = strsplit(names(idxInsTb), split="-")
      nam1 = lapply(idx1, function(v){which(v=="1")})
      
      nam1Str = sapply(nam1, paste, collapse=";")
      nam1Str = paste(nam1Str, ";", sep="")
      namL[[1]] = c(namL[[1]], nam1Str)
      
      idx1   = matrix(as.numeric(unlist(idx1)), ncol=length(idxInsTb))
      cntIntSum = drop(idx1 %*% idxInsTb)
    }

    idxs <- function(c1){
      v=rep(0,nx)
      v[c1]=1
      v
    }
    
    counts <- function(c1){
      xx = x[,c1,drop=FALSE]
      xnew = drop(xx %*% rep(1, ncol(xx)) )
      length(which(xnew > 0))
    }
    
    for(i in 2:nx){
      comb1 = combn(nx, i)
      if(is.vector(comb1)){
        comb1 = matrix(comb1, ncol=1)
      }
      
      idxL[[i]] = apply(comb1, 2, idxs)
      cntL[[i]] = apply(comb1, 2, counts)
      namL[[i]] = apply(comb1, 2, paste, collapse="-")
    }
    
    if(interSect){
      for(i in 1:length(idxL)){
        idxM = idxL[[i]]
        
        for(j in 1:length(nam1)){
          idxT = apply(idxM[nam1[[j]],,drop=FALSE], 2, sum)
          idxT[idxT > 1] = 1
          idxM = rbind(idxM, idxT)
        }
        idxL[[i]] = idxM
      }
    }
    
    # --------------------------------------------------
    # calculate nominal p-value 
    # --------------------------------------------------
    
    if(is.null(alpha)){
      alpha = 1.0
      
      for(i in 1:nx){
        idxLi = idxL[[i]][1:nx,,drop=FALSE]
        cntLi = cntL[[i]]
        
        for(j in 1:ncol(idxLi)){
          idxLij = which(idxLi[,j] == 1)
          xnew   = x[,idxLij,drop=FALSE]
          xnew   = drop(xnew %*% rep(1, ncol(xnew)))
          xnew[which(xnew > 1)] = 1
          
          m1   = sum(xnew*y)
          pval = phyper(m1-1, ny, n-ny, cntLi[j], lower.tail=FALSE)
          
          if(pval < alpha){ alpha = pval }
        }
      }
    }

    # --------------------------------------------------
    # save the quantiles 
    # --------------------------------------------------
    qntL = list()
    
    for(i in 1:nx){
      cntLi = cntL[[i]]
      lenCi = length(cntLi)
      qnt1  = rep(NA, lenCi)
      
      for(j in 1:lenCi){
        qnt1[j] = qhyper(1 - alpha, ny, n-ny, cntLi[j]) + 1
      }
      
      qntL[[i]] = qnt1
    }
        
    # --------------------------------------------------
    # find the feasible combinations of ks 
    # and count the number of permutations
    # --------------------------------------------------
    
    gall = numeric(0)
    
    # --------------------------------------------------
    # Only consider the orignal SNPs
    # --------------------------------------------------
    
    cnt1 = cntL[[1]]
    qnt1 = qntL[[1]]
    nam1 = namL[[1]]
    idx1 = idxL[[1]]
    
    if(cnt1[1] >= qnt1[1]){
      Omega1 = matrix(seq(qnt1[1], cnt1[1], by=1), ncol=1)
      g1     = logCount(Omega1, ns=cnt1[1], n, ny)
      gall   = c(gall, g1)
    }
    
    for(s in 2:nx){    
      if(cnt1[s] < qnt1[s]){ next }
      s2use = which(qnt1[1:(s-1)] < cnt1[1:(s-1)])
      
      if(length(s2use) <= 0){
        Omega2 = matrix(seq(qnt1[s], cnt1[s], by=1), ncol=1)
        g1     = logCount(Omega2, ns=cnt1[s], n, ny)
        gall   = c(gall, g1)
      }else{
        h2use = x2use = c(s2use, s)
        
        if(interSect){
          for(xx1 in x2use){
            sI = paste(xx1, ";", sep="")
            h2use = c(h2use, grep(sI, nam1))
          }
          
          h2use  = sort(unique(h2use))
        }

        nUpper = hs[h2use]
        nLower = rep(0,  length(h2use))
        wtmp1  = which(h2use < s)
        wtmp2  = h2use[wtmp1]
        nUpper[wtmp1] = pmin(qnt1[wtmp2] - 1, nUpper[wtmp1])
        
        wtmp   = which(h2use == s)
        nLower[wtmp] = qnt1[s] - cntIntSum[s]
        
        if(any(nLower > nUpper)){ next }

        Omega2 = hcube(nUpper, nLower)
        
        if(interSect){
          for(xx1 in s2use){
            if(cntIntSum[xx1] > 0){
              v1 = qnt1[xx1]
              r1 = idx1[,xx1][h2use]
              w1 = which(drop(Omega2 %*% r1) < v1)
              Omega2 = Omega2[w1, ,drop=FALSE]
            }
          }
          
          if(cntIntSum[s] > 0){
            v1 = qnt1[s]
            r1 = idx1[,s][h2use]
            w1 = which(Omega2 %*% r1 >= v1)
            Omega2 = Omega2[w1, ,drop=FALSE]
          }
        }
        
        if(nrow(Omega2) > 0){
          g1 = logCount(Omega2, ns=hs[h2use], n, ny)
          gall = c(gall, g1)
        }
        
      }
    }
    
    # --------------------------------------------------
    # Consider SNP pairs
    # --------------------------------------------------
    idx1 = idxL[[1]]
    qnt1 = qntL[[1]]
    
    qnt2 = qntL[[2]]
    cnt2 = cntL[[2]]
    nam2 = namL[[2]]
    idx2 = idxL[[2]]
    
    nUpper = hs
    nUpper[1:nx] = pmin(qnt1 - 1, hs[1:nx])
    nLower = rep(0, nclass)
    
    for(i in 1:(nx-1)){
      for(j in (i+1):nx){
        
        # cat(i,j,date(),"\n")
        
        ww1 = c(i,j)
        ww0 = (1:nx)[-(ww1)]
        
        # if there is intersection, we put intersection group 
        # into ww1
        if(interSect){
          ww1 = c(ww1, (nx+1):nrow(idx2))
        }

        widx   = which(paste(i, j, sep="-") == nam2)
        if(cnt2[widx] < qnt2[widx]){ next }
        
        nUpper1 = nUpper[ww1]
        nLower1 = nLower[ww1]
        
        # here subtract cntIntSum[i] is a conservtive bound, 
        # should the intersection between SNPi and any SNP other than SNPj
        nLower1[1] = max(0, qnt2[widx] - min(qnt1[j]-1, ns[j]) - cntIntSum[i])
        nLower1[2] = max(0, qnt2[widx] - min(qnt1[i]-1, ns[i]) - cntIntSum[j])
        
        if(any(nLower1 > nUpper1)){ next }

        Omega1 = hcube(nUpper1, nLower1)
      
        wkp    = which(drop(Omega1 %*% idx2[ww1,widx]) >= qnt2[widx])
        if(length(wkp)<=0){ next }
        Omega1 = Omega1[wkp,,drop=FALSE]
        
        wkpi = which(drop(Omega1 %*% idx1[ww1,i]) < qnt1[i])
        wkpj = which(drop(Omega1 %*% idx1[ww1,j]) < qnt1[j])
        
        wkp  = intersect(wkpi, wkpj)
        if(length(wkp)<=0){ next }
        
        Omega2 = Omega1[wkp,,drop=FALSE]
        
        if(length(ww0) == 0){
          g2   = logCount(Omega2, ns=hs, n, ny)
          gall = c(gall, g2)
        }else{
          ## Now search for eligible combinations for other SNPs
          Omega4 = Omega2
          cntIt  = hs[ww1]
          skipIt = 0
          
          for(ss in 1:length(ww0)){
            w0  = ww0[ss]
            ww1 = c(ww1, w0)
            
            if(ss < length(ww0)){
              wwremain = ww0[(ss+1):length(ww0)]
            }else{
              wwremain = numeric(0)
            }
            
            Omega3 = numeric(0)
            for(m1 in 0:min(qnt1[w0]-1, hs[w0])){
              Omega3 = rbind(Omega3, cbind(Omega4, rep(m1, nrow(Omega4))))
            }
            
            # cat(sprintf("ss=%d, nrow(Omega3)=%d", ss, nrow(Omega3)), date(), "\n")
            A  = tv = numeric(0)
            
            # w2remain indicates the indexes where
            # that has nothing to do with remaining variables
            if(length(wwremain) == 0){
              w2remain = 1:ncol(idx1)
            }else{
              w2remain = which(apply(idx1[wwremain,,drop=FALSE], 2, sum) == 0)
            }
            
            if(interSect){
              w2use = intersect(which(idx1[w0,] > 0), w2remain)
              if(length(w2use) > 0){
                A  = cbind(A, idx1[ww1,w2use,drop=FALSE])
                tv = c(tv, qnt1[w2use])
              }
            }
            
            if(length(wwremain) == 0){
              w2remain = 1:ncol(idx2)
            }else{
              w2remain = which(apply(idx2[wwremain,,drop=FALSE], 2, sum) == 0)
            }
            
            if(widx > 1){
              wtmpSet = intersect(1:(widx-1), w2remain)
              for(wtmp in wtmpSet){
                if(idx2[w0,wtmp] > 0){
                  A  = cbind(A, idx2[ww1, wtmp, drop=FALSE])
                  tv = c(tv, qnt2[wtmp])
                }
              }
            }
            
            if(length(tv)>0){
              sumA = Omega3 %*% A
              
              w2kp = rep(TRUE, nrow(sumA))
              for(wsi in 1:ncol(sumA)){
                w2kp = w2kp & (sumA[,wsi] < tv[wsi])
              }
              
              w2kp = which(w2kp)
            }else{
              w2kp = 1:nrow(Omega3)
            }
            
            if(length(w2kp) == 0){
              skipIt = 1
              break
            }else{ 
              Omega4 = Omega3[w2kp,,drop=FALSE]
              cntIt  = c(cntIt, hs[w0])
            }
            
          }
##
          if(skipIt==0){
            g2   = logCount(Omega4, ns=cntIt, n, ny)
            gall = c(gall, g2)
          }
        }
        
      }
    }
    
    # --------------------------------------------------
    # Consider collapsing more than two SNPs
    # --------------------------------------------------
    
    qnt1 = qntL[[1]]

    for(hd in 3:nx){
      cntLhd = cntL[[hd]]
      idxLhd = idxL[[hd]]
      namLhd = namL[[hd]]
      qntLhd = qntL[[hd]]
      hdLen  = length(cntLhd)
      
      namLhd1 = namL[[hd - 1]]
      qntLhd1 = qntL[[hd - 1]]
      cntLhd1 = cntL[[hd - 1]]
      
      for(j in 1:hdLen){
        if(cntLhd[j] < qntLhd[j]){ next }
        
        ww0 = which(idxLhd[1:nx,j]==0)
        ww1 = which(idxLhd[1:nx,j]==1)
        
        # if there is intersection, we put intersection group 
        # into ww1
        if(interSect){
          ww1 = c(ww1, (nx+1):nrow(idxLhd))
        }
        
        wwremain = ww0
        
        nUpper1 = nUpper[ww1]
        nLower1 = nLower[ww1]
        
        ww1x = ww1[ww1 <= nx]
        
        if(length(ww1x) != hd){
          stop("length of ww1x is wrong\n")
        }
        
        for(i in 1:hd){
          wi = which(namLhd1 == paste(ww1x[-i], collapse="-"))
          nLower1[i] = qntLhd[j] - min(qntLhd1[wi] - 1, cntLhd1[wi]) - cntIntSum[ww1[i]]
        }

        if(any(nLower1 > nUpper1)){ next }

        Omega1 = hcube(nUpper1, nLower1)
        A  = tv = numeric(0)
                        
        if(interSect){ sStart = 1} else { sStart = 2 }
        
        for(s in sStart:(hd-1)){
          idxLs = idxL[[s]]
          
          if(length(wwremain) == 0){
            w2remain = 1:ncol(idxLs)
          }else{
            w2remain = which(apply(idxLs[wwremain,,drop=FALSE], 2, sum) == 0)
          }
          
          if(length(w2remain) > 0){
            A  = cbind(A, idxLs[ww1,w2remain,drop=FALSE])
            tv = c(tv, qntL[[s]][w2remain])
          }
        }
        
        if(length(wwremain) == 0){
          w2remain = 1:ncol(idxLhd)
        }else{
          w2remain = which(apply(idxLhd[wwremain,,drop=FALSE], 2, sum) == 0)
        }
        
        if(j > 1){
          wtmpSet = intersect(1:(j-1), w2remain)
          for(wtmp in wtmpSet){
            A  = cbind(A, idxLhd[ww1, wtmp, drop=FALSE])
            tv = c(tv, qntLhd[wtmp])
          }
        }
        
        if(length(tv) > 0){
          sumA = Omega1 %*% A
          
          w2kp = rep(TRUE, nrow(sumA))
          for(i in 1:ncol(sumA)){
            w2kp = w2kp & (sumA[,i] < tv[i])
          }
          w2kp = which(w2kp)
          
          if(length(w2kp) == 0){ next } 
          Omega1 = Omega1[w2kp,,drop=FALSE]
        }
        
        r1 = idxLhd[ww1,j]
        wkp  = which(drop(Omega1 %*% r1) >= qntLhd[j])
        
        if(length(wkp)==0){ next }
        Omega2 = Omega1[wkp,,drop=FALSE]
        
        if(length(ww0) == 0){
          g2   = logCount(Omega2, ns=hs, n, ny)
          gall = c(gall, g2)
        }else{
          ## Now search for eligible combinations for other SNPs
          Omega4 = Omega2
          cntIt  = hs[ww1]
          skipIt = 0

          for(ss in 1:length(ww0)){
            w0  = ww0[ss]
            ww1 = c(ww1, w0)
            
            if(ss < length(ww0)){
              wwremain = ww0[(ss+1):length(ww0)]
            }else{
              wwremain = numeric(0)
            }
            
            Omega3 = numeric(0)
            for(m1 in 0:min(qnt1[w0]-1, hs[w0])){
              Omega3 = rbind(Omega3, cbind(Omega4, rep(m1, nrow(Omega4))))
            }
                        
            A  = tv = numeric(0)
            if(interSect){ sStart = 1} else { sStart = 2 }

            for(s in sStart:(hd - 1)){
              idxLs = idxL[[s]]

              if(length(wwremain) == 0){
                w2remain = 1:ncol(idxLs)
              }else{
                w2remain = which(apply(idxLs[wwremain,,drop=FALSE], 2, sum) == 0)
              }
              
              w2use = intersect(which(idxLs[w0,] > 0), w2remain)
              
              if(length(w2use) > 0){
                A  = cbind(A, idxLs[ww1,w2use,drop=FALSE])
                tv = c(tv, qntL[[s]][w2use])
              }

            }
            
            if(length(wwremain) == 0){
              w2remain = 1:ncol(idxLhd)
            }else{
              w2remain = which(apply(idxLhd[wwremain,,drop=FALSE], 2, sum) == 0)
            }
            
            if(j > 1){
              wtmpSet = intersect(1:(j-1), w2remain)
              for(wtmp in wtmpSet){
                if(idxLhd[w0,wtmp] > 0){
                  A  = cbind(A, idxLhd[ww1, wtmp, drop=FALSE])
                  tv = c(tv, qntLhd[wtmp])
                }
              }
            }
            
            if(length(tv) > 0){
              sumA = Omega3 %*% A
              
              w2kp = rep(TRUE, nrow(sumA))
              for(i in 1:ncol(sumA)){
                w2kp = w2kp & (sumA[,i] < tv[i])
              }
              
              w2kp = which(w2kp)
            }else{
              w2kp = 1:nrow(Omega3)
            }
            
            if(length(w2kp) == 0){
              skipIt = 1
              break
            }else{ 
              Omega4 = Omega3[w2kp,,drop=FALSE]
              cntIt  = c(cntIt, hs[w0])
            }
          }
          ##
          if(skipIt==0){
            g2   = logCount(Omega4, ns=cntIt, n, ny)
            gall = c(gall, g2)
          }
        }
        
      }
    }
  }
  
  # --------------------------------------------------
  # finally, calculate permutation p-values
  # --------------------------------------------------
    
  list(p=alpha, perP=exp(logsumexp(gall) - lchoose(n, ny)))
  
}
