Suppose we conduct experiment with complete randomization, obtain the assignment \(W = (1,0,0,0,0,0,1,1)'\), and the observed outcome for all units are \(Y^{\text{obs}} = (14, 6, 4, 5, 6, 7, 10, 9)'\).

W <- c(1,0,0,0,0,0,1,1)

Yobs <- c(14, 6, 4, 5, 6, 7, 10, 9) 

# Under the sharp null of no causal effect
Y0 <- Y1 <- Yobs
## observed test statistics value, here you can also replace the difference-in-means estimator with other types of test statistics
tau_obs <- mean(Yobs[W==1]) - mean(Yobs[W==0])

### get all possible assignments
treated_all = combn(8, 3)
W_all = matrix(0, nrow = 56, ncol = 8)
for(m in 1:56){
  W_all[m, treated_all[,m]] = 1
}
dim(W_all)
[1] 56  8
tau_rand <- apply(W_all, 1, function(w) mean(Y1[w==1]) - mean(Y0[w==0]))
hist(tau_rand, breaks = 10, main="Distribution under sharp Null of no individual causal effect", xlab="Obs Diff in Means")

hist(tau_rand, breaks = 10, main="Distribution under sharp Null of no individual causal effect", xlab="Obs Diff in Means")
abline(v = tau_obs, col = "red")


## calculate p-value 
p <- mean(abs(tau_rand) >= abs(tau_obs))
print(p)
[1] 0.01785714
# Under the sharp null of causal effect is 1
Y0 <- Y1 <- rep(NA, length(Yobs))
Y0[W == 0] <- Yobs[W == 0]
Y0[W == 1] <- Yobs[W == 1] - 1

Y1[W == 1] <- Yobs[W == 1]
Y1[W == 0] <- Yobs[W == 0] + 1

## recompute the distribution of Y1[w = 1] - Y0[w = 0] - 1
tau_rand <- apply(W_all, 1, function(w) mean(Y1[w==1]) - mean(Y0[w==0]))
p = mean(abs(tau_rand - 1) >= abs(tau_obs - 1))
print(p)
[1] 0.01785714
# need to write a function that compute p-value for any tau
getPvalue <- function(tau, Yobs, W) {
  Y0 <- Y1 <- rep(NA, length(Yobs))
  Y0[W == 0] <- Yobs[W == 0]
  Y0[W == 1] <- Yobs[W == 1] - tau

  Y1[W == 1] <- Yobs[W == 1]
  Y1[W == 0] <- Yobs[W == 0] + tau

  ## recompute the distribution of Y1[w = 1] - Y0[w = 0] - 1
  tau_rand <- apply(W_all, 1, function(w) mean(Y1[w==1]) - mean(Y0[w==0]))
  p = mean(abs(tau_rand - tau) >= abs(tau_obs - tau))
  return(p)
}

## Compute the p-value for a seqeunce of tau
tau_seq <- seq(-20, 20, by = 1)
pvalue_seq <- sapply(tau_seq, getPvalue, Yobs, W)
names(pvalue_seq) <- tau_seq
print(pvalue_seq, digits = 3)
   -20    -19    -18    -17    -16    -15    -14    -13    -12    -11    -10     -9     -8     -7     -6 
0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 
    -5     -4     -3     -2     -1      0      1      2      3      4      5      6      7      8      9 
0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0357 0.1786 0.3929 0.8571 0.6964 0.3571 0.1429 0.0536 
    10     11     12     13     14     15     16     17     18     19     20 
0.0357 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 0.0179 

A rough 95% confidence interval is [2, 10]. You may want a more refined confidence interval by finding the cutoff that is close to 0.05.

## find a better lower bound
tau_seq1 <- seq(2, 3, by = 0.1)
pvalue_seq1 <- sapply(tau_seq1, getPvalue, Yobs, W)
names(pvalue_seq1) <- tau_seq1
print(pvalue_seq1, digits = 3)
     2    2.1    2.2    2.3    2.4    2.5    2.6    2.7    2.8    2.9      3 
0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.1786 
## find a better upper bound

tau_seq2 <- seq(9, 10, by = 0.1)
pvalue_seq2 <- sapply(tau_seq2, getPvalue, Yobs, W)
names(pvalue_seq2) <- tau_seq2
print(pvalue_seq2, digits = 3)
     9    9.1    9.2    9.3    9.4    9.5    9.6    9.7    9.8    9.9     10 
0.0536 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 0.0357 

A more refined 95% CI is [2.9, 9.1]

## Say we want M = 10000 random samples of the treatment assignments
### get M random samples of possible assignments
getPvalueMC <- function(tau, Yobs, W, M) {
  Y0 <- Y1 <- rep(NA, length(Yobs))
  Y0[W == 0] <- Yobs[W == 0]
  Y0[W == 1] <- Yobs[W == 1] - tau

  Y1[W == 1] <- Yobs[W == 1]
  Y1[W == 0] <- Yobs[W == 0] + tau

  ## randomly sample the treatment assignment vector following the complete randomization mechanism
  
  set.seed(0)
  tau_rand <- sapply(1:M, function(i) {
    w <- sample(W)
    return(mean(Y1[w==1]) - mean(Y0[w==0]))
  })
  p = mean(abs(tau_rand - tau) >= abs(tau_obs - tau))
  return(p)
}

getPvalueMC(0, Yobs, W, 10000)
[1] 0.0171
LS0tCnRpdGxlOiAiQW4gZXhhbXBsZSB0byBjb21wdXRlIEZpc2hlcidzIGV4YWN0IHAtdmFsdWUgYW5kIENJIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KClN1cHBvc2Ugd2UgY29uZHVjdCBleHBlcmltZW50IHdpdGggY29tcGxldGUgcmFuZG9taXphdGlvbiwgb2J0YWluIHRoZSBhc3NpZ25tZW50ICRXID0gKDEsMCwwLDAsMCwwLDEsMSknJCwgYW5kIHRoZSBvYnNlcnZlZCBvdXRjb21lIGZvciBhbGwgdW5pdHMgYXJlICRZXntcdGV4dHtvYnN9fSA9ICgxNCwgNiwgNCwgNSwgNiwgNywgMTAsIDkpJyQuICAKCi0gSW1wdXRlIGFsbCB0aGUgbWlzc2luZyBwb3RlbnRpYWwgb3V0Y29tZXMgdW5kZXIgdGhlIHNoYXJwIG51bGwgaHlwb3RoZXNpcyBvZiBubyBpbmRpdmlkdWFsIGNhdXNhbCBlZmZlY3RzLiAKYGBge3J9ClcgPC0gYygxLDAsMCwwLDAsMCwxLDEpCgpZb2JzIDwtIGMoMTQsIDYsIDQsIDUsIDYsIDcsIDEwLCA5KSAKCiMgVW5kZXIgdGhlIHNoYXJwIG51bGwgb2Ygbm8gY2F1c2FsIGVmZmVjdApZMCA8LSBZMSA8LSBZb2JzCmBgYAoKLSBBc3N1bWUgdGhlIHNoYXJwIG51bGwgaHlwb3RoZXNpcyBvZiBubyBpbmRpdmlkdWFsIGNhdXNhbCBlZmZlY3RzLiBXaGF0IGlzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRpZmZlcmVuY2UtaW4tbWVhbnMgZXN0aW1hdG9yICRcaGF0e1x0YXV9JC4gCmBgYHtyfQojIyBvYnNlcnZlZCB0ZXN0IHN0YXRpc3RpY3MgdmFsdWUsIGhlcmUgeW91IGNhbiBhbHNvIHJlcGxhY2UgdGhlIGRpZmZlcmVuY2UtaW4tbWVhbnMgZXN0aW1hdG9yIHdpdGggb3RoZXIgdHlwZXMgb2YgdGVzdCBzdGF0aXN0aWNzCnRhdV9vYnMgPC0gbWVhbihZb2JzW1c9PTFdKSAtIG1lYW4oWW9ic1tXPT0wXSkKCiMjIyBnZXQgYWxsIHBvc3NpYmxlIGFzc2lnbm1lbnRzCnRyZWF0ZWRfYWxsID0gY29tYm4oOCwgMykKV19hbGwgPSBtYXRyaXgoMCwgbnJvdyA9IDU2LCBuY29sID0gOCkKZm9yKG0gaW4gMTo1Nil7CiAgV19hbGxbbSwgdHJlYXRlZF9hbGxbLG1dXSA9IDEKfQpkaW0oV19hbGwpCgp0YXVfcmFuZCA8LSBhcHBseShXX2FsbCwgMSwgZnVuY3Rpb24odykgbWVhbihZMVt3PT0xXSkgLSBtZWFuKFkwW3c9PTBdKSkKaGlzdCh0YXVfcmFuZCwgYnJlYWtzID0gMTAsIG1haW49IkRpc3RyaWJ1dGlvbiB1bmRlciBzaGFycCBOdWxsIG9mIG5vIGluZGl2aWR1YWwgY2F1c2FsIGVmZmVjdCIsIHhsYWI9Ik9icyBEaWZmIGluIE1lYW5zIikKYGBgCgoKCi0gSG93IGV4dHJlbWUgaXMgdGhlIG9ic2VydmVkIGRpZmZlcmVuY2UtaW4tbWVhbnMgZXN0aW1hdG9yICRcaGF0e1x0YXV9XntcdGV4dHtvYnN9fSQ/IFdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIGhhdmluZyBhdCBsZWFzdCBhcyBleHRyZW1lIGFzIHRoZSBvYnNlcnZlZCBvbmU/IE1vcmUgcmlnb3JvdXNseSwgc3BlY2lmeSB5b3VyIHRlc3Qgc3RhdGlzdGljIGFuZCByZXBvcnQgeW91ciByYW5kb21pemF0aW9uIHRlc3QgJHAkLXZhbHVlLiAKCmBgYHtyfQpoaXN0KHRhdV9yYW5kLCBicmVha3MgPSAxMCwgbWFpbj0iRGlzdHJpYnV0aW9uIHVuZGVyIHNoYXJwIE51bGwgb2Ygbm8gaW5kaXZpZHVhbCBjYXVzYWwgZWZmZWN0IiwgeGxhYj0iT2JzIERpZmYgaW4gTWVhbnMiKQphYmxpbmUodiA9IHRhdV9vYnMsIGNvbCA9ICJyZWQiKQoKIyMgY2FsY3VsYXRlIHAtdmFsdWUgCnAgPC0gbWVhbihhYnModGF1X3JhbmQpID49IGFicyh0YXVfb2JzKSkKcHJpbnQocCkKYGBgCgotIE9idGFpbiBGaXNoZXIncyBleGFjdCBwLXZhbHVlIHVuZGVyIHRoZSBzaGFycCBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgaW5kaXZpZHVhbCB0cmVhdG1lbnQgZWZmZWN0cyBhcmUgYWxsIDEuIApgYGB7cn0KIyBVbmRlciB0aGUgc2hhcnAgbnVsbCBvZiBjYXVzYWwgZWZmZWN0IGlzIDEKWTAgPC0gWTEgPC0gcmVwKE5BLCBsZW5ndGgoWW9icykpClkwW1cgPT0gMF0gPC0gWW9ic1tXID09IDBdClkwW1cgPT0gMV0gPC0gWW9ic1tXID09IDFdIC0gMQoKWTFbVyA9PSAxXSA8LSBZb2JzW1cgPT0gMV0KWTFbVyA9PSAwXSA8LSBZb2JzW1cgPT0gMF0gKyAxCgojIyByZWNvbXB1dGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBZMVt3ID0gMV0gLSBZMFt3ID0gMF0gLSAxCnRhdV9yYW5kIDwtIGFwcGx5KFdfYWxsLCAxLCBmdW5jdGlvbih3KSBtZWFuKFkxW3c9PTFdKSAtIG1lYW4oWTBbdz09MF0pKQpwID0gbWVhbihhYnModGF1X3JhbmQgLSAxKSA+PSBhYnModGF1X29icyAtIDEpKQpwcmludChwKQpgYGAKCi0gQ2FsY3VsYXRlIHRoZSByYW5kb21pemF0aW9uICRwJC12YWx1ZSBmb3IgYSBzZXF1ZW5jZSBvZiBzaGFycCBudWxsIGh5cG90aGVzZXMgb2YgYWRkaXRpdmUgdHJlYXRtZW50IGVmZmVjdHMgb2YgJFx7LTIwLC0xOSwgXGxkb3RzLCAxOSwyMFx9JCwgYW5kIGdldCB0aGUgJDk1XCUkIGNvbmZpZGVuY2UgaW50ZXJ2YWwKYGBge3J9CiMgbmVlZCB0byB3cml0ZSBhIGZ1bmN0aW9uIHRoYXQgY29tcHV0ZSBwLXZhbHVlIGZvciBhbnkgdGF1CmdldFB2YWx1ZSA8LSBmdW5jdGlvbih0YXUsIFlvYnMsIFcpIHsKICBZMCA8LSBZMSA8LSByZXAoTkEsIGxlbmd0aChZb2JzKSkKICBZMFtXID09IDBdIDwtIFlvYnNbVyA9PSAwXQogIFkwW1cgPT0gMV0gPC0gWW9ic1tXID09IDFdIC0gdGF1CgogIFkxW1cgPT0gMV0gPC0gWW9ic1tXID09IDFdCiAgWTFbVyA9PSAwXSA8LSBZb2JzW1cgPT0gMF0gKyB0YXUKCiAgIyMgcmVjb21wdXRlIHRoZSBkaXN0cmlidXRpb24gb2YgWTFbdyA9IDFdIC0gWTBbdyA9IDBdIC0gMQogIHRhdV9yYW5kIDwtIGFwcGx5KFdfYWxsLCAxLCBmdW5jdGlvbih3KSBtZWFuKFkxW3c9PTFdKSAtIG1lYW4oWTBbdz09MF0pKQogIHAgPSBtZWFuKGFicyh0YXVfcmFuZCAtIHRhdSkgPj0gYWJzKHRhdV9vYnMgLSB0YXUpKQogIHJldHVybihwKQp9CgojIyBDb21wdXRlIHRoZSBwLXZhbHVlIGZvciBhIHNlcWV1bmNlIG9mIHRhdQp0YXVfc2VxIDwtIHNlcSgtMjAsIDIwLCBieSA9IDEpCnB2YWx1ZV9zZXEgPC0gc2FwcGx5KHRhdV9zZXEsIGdldFB2YWx1ZSwgWW9icywgVykKbmFtZXMocHZhbHVlX3NlcSkgPC0gdGF1X3NlcQpwcmludChwdmFsdWVfc2VxLCBkaWdpdHMgPSAzKQpgYGAKCkEgcm91Z2ggOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgWzIsIDEwXS4gWW91IG1heSB3YW50IGEgbW9yZSByZWZpbmVkIGNvbmZpZGVuY2UgaW50ZXJ2YWwgYnkgZmluZGluZyB0aGUgY3V0b2ZmIHRoYXQgaXMgY2xvc2UgdG8gMC4wNS4KYGBge3J9CiMjIGZpbmQgYSBiZXR0ZXIgbG93ZXIgYm91bmQKdGF1X3NlcTEgPC0gc2VxKDIsIDMsIGJ5ID0gMC4xKQpwdmFsdWVfc2VxMSA8LSBzYXBwbHkodGF1X3NlcTEsIGdldFB2YWx1ZSwgWW9icywgVykKbmFtZXMocHZhbHVlX3NlcTEpIDwtIHRhdV9zZXExCnByaW50KHB2YWx1ZV9zZXExLCBkaWdpdHMgPSAzKQoKIyMgZmluZCBhIGJldHRlciB1cHBlciBib3VuZAoKdGF1X3NlcTIgPC0gc2VxKDksIDEwLCBieSA9IDAuMSkKcHZhbHVlX3NlcTIgPC0gc2FwcGx5KHRhdV9zZXEyLCBnZXRQdmFsdWUsIFlvYnMsIFcpCm5hbWVzKHB2YWx1ZV9zZXEyKSA8LSB0YXVfc2VxMgpwcmludChwdmFsdWVfc2VxMiwgZGlnaXRzID0gMykKYGBgCgpBIG1vcmUgcmVmaW5lZCA5NSUgQ0kgaXMgWzIuOSwgOS4xXQoKLSBXaGVuIHRoZSBzYW1wbGUgc2l6ZSBpcyBsYXJnZSwgaXQgaXMgaW1wb3NzaWJsZSB0byBlbnVtZXJhdGUgYWxsIHBvc3NpYmxlIHJhbmRvbWl6YXRpb25zLiBIb3cgd291bGQgeW91IGdldCBhbiBhcHByb3hpbWF0aW9uIGZvciB0aGUgcmFuZG9taXphdGlvbiB0ZXN0ICRwJC12YWx1ZT8KCmBgYHtyfQojIyBTYXkgd2Ugd2FudCBNID0gMTAwMDAgcmFuZG9tIHNhbXBsZXMgb2YgdGhlIHRyZWF0bWVudCBhc3NpZ25tZW50cwojIyMgZ2V0IE0gcmFuZG9tIHNhbXBsZXMgb2YgcG9zc2libGUgYXNzaWdubWVudHMKZ2V0UHZhbHVlTUMgPC0gZnVuY3Rpb24odGF1LCBZb2JzLCBXLCBNKSB7CiAgWTAgPC0gWTEgPC0gcmVwKE5BLCBsZW5ndGgoWW9icykpCiAgWTBbVyA9PSAwXSA8LSBZb2JzW1cgPT0gMF0KICBZMFtXID09IDFdIDwtIFlvYnNbVyA9PSAxXSAtIHRhdQoKICBZMVtXID09IDFdIDwtIFlvYnNbVyA9PSAxXQogIFkxW1cgPT0gMF0gPC0gWW9ic1tXID09IDBdICsgdGF1CgogICMjIHJhbmRvbWx5IHNhbXBsZSB0aGUgdHJlYXRtZW50IGFzc2lnbm1lbnQgdmVjdG9yIGZvbGxvd2luZyB0aGUgY29tcGxldGUgcmFuZG9taXphdGlvbiBtZWNoYW5pc20KICAKICBzZXQuc2VlZCgwKQogIHRhdV9yYW5kIDwtIHNhcHBseSgxOk0sIGZ1bmN0aW9uKGkpIHsKICAgIHcgPC0gc2FtcGxlKFcpCiAgICByZXR1cm4obWVhbihZMVt3PT0xXSkgLSBtZWFuKFkwW3c9PTBdKSkKICB9KQogIHAgPSBtZWFuKGFicyh0YXVfcmFuZCAtIHRhdSkgPj0gYWJzKHRhdV9vYnMgLSB0YXUpKQogIHJldHVybihwKQp9CgpnZXRQdmFsdWVNQygwLCBZb2JzLCBXLCAxMDAwMCkKCmBgYAo=