*! NJC 1.0.1 6 April 2011
*! NJC 1.0.0 19 Feb 2009
* cipolate NJC 1.0.0 4 July 2002
* ipolate 1.3.1  14jun2000
program csipolate, byable(onecall) sort
	version 9
	syntax varlist(min=2 max=2) [if] [in], Generate(string) [ BY(varlist) ]

	if _by() {
		if "`by'" != "" {
			di as err ///
			"option by() may not be combined with by prefix"
			exit 190
		}
		local by "`_byvars'"
	}

	confirm new var `generate'
	tokenize `varlist'
	args usery x

	if "`by'" == "" {
		tempvar by
		gen byte `by' = 1
	}

	tempvar group y z guse zmiss

	quietly {
		// don't mark out missing y: that's the whole point!
		marksample touse, novarlist
		markout `touse' `x'

		// average multiple y for repeated x
		bysort `touse' `by' `x': ///
		gen `y' = sum(`usery') / sum(`usery' < .) if `touse'
		by `touse' `by' `x': replace `y' = `y'[_N]

		// only use one of any repeated x
		by `touse' `by' `x': gen byte `guse' = `touse' & (_n == 1)

		sort `guse' `by' `x'
		egen `group' = group(`by') if `guse'
		su `group', meanonly
		local ng = r(max)
		gen `z' = `y' if `touse'
		gen byte `zmiss' = 0

		forval g = 1/`ng' {
			replace `guse' = `group' == `g'
			replace `zmiss' = `guse' & missing(`z')
			count if `zmiss'
			if r(N) > 1 {
	mata : csipolate("`y'", "`x'", "`guse'", "`z'", "`zmiss'")
			}
		}

		// copy interpolated y to repeated x
		bysort `touse' `by' `x' (`z') : ///
			replace `z' = `z'[1] if `touse' & `z' == .

		rename `z' `generate'
		count if `generate' == .
	}

	if r(N) > 0 {
		if r(N) != 1 local pl "s"
		di as txt "(" r(N) `" missing value`pl' generated)"'
	}
end

mata:

void csipolate(string scalar yvarname,
               string scalar xvarname,
	       string scalar tousename,
	       string scalar zvarname,
	       string scalar zmissname
	) {

real matrix xyvar
real colvector x, y, where

st_view(xyvar, ., (xvarname, yvarname), tousename)
x = select(xyvar[,1], (xyvar[,2] :< .))
where = select(xyvar[,1], (xyvar[,2] :== .))
y = select(xyvar[,2], (xyvar[,2] :< .))
st_store(., zvarname, zmissname, spline3eval(spline3(x, y), where))

}

end