*! survbound v1.1.0 Long Hong 16Oct2016
capture program drop survbound
program define survbound, rclass
version 12
syntax varlist(max=1 numeric), THREShold(real) CENSORPCT(real) [GRID(integer 0)]


capture assert `varlist' > 0
if c(rc) {
	display in red "Observations must be > 0"
	exit 9
}

qui sum `varlist'
local min = r(min)
capture assert `threshold' <= `min'
if c(rc) {
	display in red "Threshold must be no larger than the smallest obversation"
	exit 9
}

capture assert `censorpct' > 0 & `censorpct' < 1
if c(rc) {
	display in red "Percentage of censoring must be in (0, 1)"
	exit 9
}

capture assert `grid' >= 0
if c(rc) {
	display in red "Grid size must in a non-negative integer"
	display in yellow "A value 0 implies switching off grid-search function."
	exit 9
}

capture qui ssc install fastgini	// Install fastgini


*** Known parameters
local pi1 = `censorpct'
local pi2 = 1 - `censorpct'

qui sum `varlist'
local mu2 = r(mean)

qui fastgini `varlist'
local G2 = r(gini)


****************** Analytic Gini Boundaries *******************************

tempname bound

local lower = (`mu2'*`pi2'*`pi2'*`G2' + `pi1'*`pi2'*(`mu2' - `threshold'))/(`threshold'*`pi1' + `mu2'*`pi2')
local upper = min(((`pi1'*`pi1'*`threshold')/(`threshold'*`pi1'+`mu2'*`pi2')) + `pi2'*`G2' + `pi1' , 1)

mat `bound' = (round(`lower', 0.00001), round(`upper', 0.00001))
matrix rownames `bound' = "Non-Parametric Gini"
matrix colnames `bound' = Lower(A) Upper(A)

return scalar upper_a = `upper'
return scalar lower_a = `lower'

if `grid' == 0 {

display " "
display in yellow "Non-Parametric Gini Numeric Boundaries: "
matlist `bound', twidth(20) border(rows) rowtitle("")
display in blue "Lower(A): Analytic lower bound"
display in blue "Upper(A): Analytic upper bound"

}

****************** Grid-Search Gini Boundaries *****************************
else {

tempname gini_grid bound_grid max_grid upper_g


local G1_increment  = (1-0)/(`grid')
local G1_min = `G1_increment'
local G1_max = 1 - `G1_increment'
local mu1_increment = (`threshold' - 0)/(`grid')
local mu1_min = `mu1_increment'
local mu1_max = `threshold'


forvalues G1 = `G1_min'(`G1_increment')`G1_max' {
forvalues mu1 = `mu1_min'(`mu1_increment')`mu1_max' {

	local s1   = `mu1'*`pi1'/(`mu1'*`pi1'+`mu2'*`pi2')
	local s2   = `mu2'*`pi2'/(`mu1'*`pi1'+`mu2'*`pi2')
	local mu   = `mu1'*`pi1' + `mu2'*`pi2'
	local G_ks = `s1'*`pi1'*`G1'
	local G_kb = `s2'*`pi2'*`G2'
	local GB   = (`pi1'*`pi2'*(`mu2' - `mu1'))/`mu'
	local gini_total  = `G_ks' + `G_kb' + `GB'
	matrix `gini_grid' = (nullmat(`gini_grid') \ `gini_total')

  }
}

mata: st_matrix("`max_grid'", colmax(st_matrix("`gini_grid'")))
scalar `upper_g' = `max_grid'[1,1]
mat `bound_grid' = (round(`lower', 0.00001), round(`upper', 0.00001), round(`upper_g', 0.00001))
matrix rownames `bound_grid' = "Non-Parametric Gini"
matrix colnames `bound_grid' = Lower(A) Upper(A) Upper(G)

display " "
display in blue "Non-Parametric Gini Numeric Boundaries: "
matlist `bound_grid', twidth(20) border(rows) rowtitle("")

display in blue "Lower(A): Analytic lower bound"
display in blue "Upper(A): Analytic upper bound"
display in blue "Upper(G): Upper bound approximation by Grid-search"

return scalar upper_g  = `max_grid'[1,1]
return scalar upper_a = `upper'
return scalar lower_a = `lower'

}


end