**********************************************************************************
** ipfweight 1.0
** 27oct2011 MB
**********************************************************************************

program ipfweight
   version 10
   syntax varlist(numeric) [if], GENerate(string) ///
      VALues(numlist) MAXITer(numlist>0) ///
	  [STartwgt(string)] [TOLerance(numlist)] [UPthreshold(numlist)] ///
	  [LOthreshold(numlist)] [MISrep]

   * specify sample for optional if-condition (missings remain)
   marksample touse, novarlist

   * specify optional starting weight (1 otherwise)
   if "`startwgt'"=="" {
      quietly gen `generate' = 1 if `touse'
   }
   else {
      quietly gen `generate' = `startwgt' if `touse'
   }

   * loop for iterations
   foreach i of numlist 1/`maxiter' {

      * specify counter for known margins
      local cnt 1

      * loop for varlist
      foreach var of local varlist {

	     * specify variable containing final weighting factors
		 tempvar `generate'`i'_`var'
		 quietly gen ``generate'`i'_`var'' = `generate' if `touse'

	     * save N for var in varlist
	     quietly sum `var' [aweight=``generate'`i'_`var''] if `touse'
	     tempvar t_`var'
         quietly gen `t_`var'' = r(sum_w) if `touse'

	     * save levels of var in varlist as local macro
         quietly levelsof `var' if `touse', local(K)

	     * loop for levels of var in varlist
	     foreach k of local K {

			* save n of varlevels
		    quietly sum `var' if `var'==`k' & `touse' ///
			   [aweight=``generate'`i'_`var'']
		    tempvar t_`var'_`k'
            quietly gen `t_`var'_`k'' = r(sum_w) if `touse'

		    * compute sample margins
		    tempvar t_ist_`var'_`k'
	        quietly gen `t_ist_`var'_`k'' = `t_`var'_`k''/`t_`var'' ///
			   if `touse'

		    * compute weighting factors (known margins/sample margins)
		    local soll: word `cnt' of `values'
			quietly replace ``generate'`i'_`var'' = ///
			   (`soll'/`t_ist_`var'_`k'')*0.01 if `var'==`k' ///
			   & `touse'

			* optional replacement of missings with a value of 1
			if "`misrep'"=="misrep" {
			   quietly replace ``generate'`i'_`var'' = 1 if `var'==.
			}
			else {
			   quietly replace ``generate'`i'_`var'' = . if `var'==.
			}

			* specify tolerance criteria
			* (=difference between known margins and sample margins)
		    quietly gen t_crit`i'_`var'_`k' = (`soll')-(`t_ist_`var'_`k''/0.01)

			* save maximum difference of var
			quietly egen t_crit`i'_`var'_`k'_max = max(abs(t_crit`i'_`var'_`k'))

		    * add 1 to counter
		    local cnt = `cnt'+1
         }

		 * save weighting factors for next round
	     quietly replace `generate' = `generate'*``generate'`i'_`var'' ///
		    if `touse'

		 * optional trimming threshold for large weights with mean correction
		 * (mean==1) before trimming
		 if "`upthreshold'"!="" {
		    quietly sum `generate' if `touse'
		    quietly replace `generate' = `generate'/r(mean) if `touse'
		    quietly replace `generate' = `upthreshold' ///
			   if `generate'>`upthreshold' & `generate'!=. & `touse'
	     }


		 * optional trimming threshold for small weights with mean correction
		 * (mean==1) before trimming
		 if "`lothreshold'"!="" {
		    quietly sum `generate' if `touse'
		    quietly replace `generate' = `generate'/r(mean) if `touse'
		    quietly replace `generate' = `lothreshold' ///
			   if `generate'<`lothreshold' & `generate'!=. & `touse'
	     }

		 * overall mean correction (mean==1)
		 quietly sum `generate' if `touse'
		 quietly replace `generate' = `generate'/r(mean) if `touse'
      }

	  * save maximum difference as local macro
	  tempvar t_exititer`i'
	  quietly egen `t_exititer`i'' = rowmax(t_crit`i'_*_max)
	  local exititer = round(`t_exititer`i'', 0.001)
	  drop t_crit`i'_*

	  * tolerance criteria reached?
      if `i'>=2 & "`tolerance'"!=`""' {
	     if `exititer'<=`tolerance' {
		    dis ""
            dis "Tolerance criteria (`tolerance') reached after `i' iterations"
			dis "Maximum deviation: `exititer' percentage points"
			sum `generate' if `touse', d
	        continue, break
		 }
		 else if `exititer'>`tolerance' {
		    if `i'==`maxiter' {
			   dis ""
               dis "Tolerance criteria (`tolerance') not reached after `i' iterations"
			   dis "Maximum deviation: `exititer' percentage points"
			   sum `generate' if `touse', d
			}
		 }
	  }
	  else if `i'==`maxiter' & "`tolerance'"==`""' {
	     dis ""
		 dis "no tolerance criteria specified"
		 dis "Maximum deviation: `exititer' percentage points after `i' iterations"
		 sum `generate' if `touse', d
	  }
   }
end
exit