*! version 2.2.2  20july2018

program define scores, byable(onecall) sortpreserve
  version 9.2
  gettoken newv 0 : 0, parse("=")
  local newv = trim("`newv'")
  gettoken opt 0 : 0, parse("(")
  local opt = trim(subinstr("`opt'","=","",1))
  gettoken vars 0 : 0, match(parns)
  local 0 = "`vars' `0'"
  syntax varlist(numeric) [if] [in] [fweight aweight iweight] [, minvalid(integer 1) nv(integer 1) SCore(name) MINval(integer 0) MAXval(integer 0) P(real 50) ENDshift(real 0) Center(real 0.5) Auto replace]
  marksample touse, nov
  if `nv' != 1 {
    local minvalid = `nv'
  }
  if ("`score'"=="c") {
    local score = "centered"
  }
  if ("`score'"=="p" | "`score'"=="po") {
    local score = "pomp"
  }
  if ("`score'"=="pp") {
    local score = "prop"
  }
  if ("`score'"=="sp") {
    local score = "sprop"
  }
  if ("`opt'"=="tot") {
    local opt = "total"
  }
  if ("`opt'"=="med") {
    local opt = "median"
  }
  if ("`opt'"=="median") {
    if c(stata_version) < 11.2 {
      di as error "Median scores require at least Stata version 11.2, current version is `c(stata_version)'"
      di "-scores- not executed"
      exit 498
    }
    else {
      version 11.2
    }
  }
  if ("`opt'"=="pct") {
    local opt = "pctile"
  }
  if ("`opt'"=="pctile") {
    if c(stata_version) < 11.2 {
      di as error "Percentile scores require at least Stata version 11.2, current version is `c(stata_version)'"
      di "-scores- not executed"
      exit 498
    }
    else {
      version 11.2
    }
  }
  if ("`varlist'")==("`newv'") {
    tempvar nv
	qui gen `nv' = `varlist'
	local varlist = "`nv'"
  }
  if subinword("`varlist'","`newv'"," ",.) != "`varlist'" {
    display as error "{hi:newvar (`newv')} may not be an element of {hi:varlist (`varlist')}"
    display "-scores- not executed"
    exit 498
  }
  tempvar nvalid tmpscore m_stat gr grcons
  if inlist("`opt'","min","max","total","mean","median","pctile","sd")==0 {
    display as error "{hi:={it:function}(varlist)} must specify one of {hi:min}, {hi:max}, {hi:total}, {hi:mean}," _n "{hi:median}, {hi:pctile}, or {hi:sd} as {it:function}"
    display "-scores- not executed"
    exit 498
  }
  if inlist("`score'","z_2","z","centered","sd2","pomp","prop","sprop") & "`opt'"!="mean" {
    display as error "{hi:score(`score')} is a valid option only if {hi:{it:function}(varlist)} = {hi:{it:mean}}"
    display "-scores- not executed"
    exit 498
  }
  local egenopt = ""
  if "`opt'"=="pctile" {
    if float(`p') <= 0 | float(`p') >= 100 {
      display as error "To specify the {hi:#th} percentile a value of the open interval {hi:]0,100[} must be used"
      display "-scores- not executed
      exit 498
    }
    local egenopt = ", p(`p')"
  }
  if "`score'"=="prop" {
    if float(`endshift') < 0 | float(`endshift') >= .5 {
      display as error "To shift the endpoints of the proportion [0,1]" _n "{hi:endshift} must be a value of the half open interval {hi:[0,0.5[}"
      display "-scores- not executed
      exit 498
    }
  }
  if "`score'"=="sprop" {
    if float(`center') < 0 | float(`center') > 1 {
      display as error "The {hi:center} of a shrunken proportion must be a value of the closed interval {hi:[0,1]}"
      display "-scores- not executed
      exit 498
    }
  }
  if inlist("`score'","","z_2","z","centered","sd2","pomp","prop","sprop")==0 {
    display as error "{hi:`score'} is an invalid argument of option {hi:score()}"
    display "-scores- not executed"
    exit 498
  }
  if (`minvalid' < 1) {
    display as error "value specified in {hi:minvalid({it:#})} may not be less than 1"
    display "-scores- not executed"
    exit 498
  }
  local k: word count `varlist'
  if (`minvalid' > `k') {
    display as error "value specified in {hi:minvalid({it:#})} may not be greater than"
    display as error "the number of variables specified in {hi:={it:function}(varlist)} (=`k')"
    display "-scores- not executed"
    exit 498
  }
  if inlist("`score'","pomp","prop","sprop") {
    if "`auto'"=="auto" {
      foreach vname of varlist `varlist' {
        local vlab: value label `vname'
        if "`vlab'"=="" {
          di as error "Option {hi:auto} requires value labels of all variables of {hi:varlist}."
          di as error "{hi:`vname'} has no value labels."
          di "-scores- not executed"
          exit 498
        }
        qui label list `vlab'
        local firstv: word 1 of `varlist'
        if "`firstv'"=="`vname'" {
          local minval = `r(min)'
          local maxval = `r(max)'
        }
        else {
          if `minval' != `r(min)' | `maxval' != `r(max)' {
            di as error "The range of labeled values of {hi:`vname'} differs from the range of labeled values of {hi:`firstv'}."
            di "-scores- not executed"
            exit 498
          }
        }
      }
    }
    if (`minval'>=`maxval') {
      display as error "{hi:minval({it:#})} and {hi:maxval({it:#})} options should allow {hi:minval} to be less than {hi:maxval}"
      display "-scores- not executed"
      exit 498
    }
  }
  if "`replace'"=="" {
    capture confirm new var `newv'
    if _rc > 0 {
      di as error "variable {bf:`newv'} already defined
	  exit 110
    }
	else qui gen `newv' = .
  }
  else {
    capture confirm new var `newv'
	if _rc==0 qui gen `newv' = .
  }
  if inlist("`score'","z_2","z","centered","sd2","sprop") {  // prefix by: or weights are relevant
    if "`_byvars'"=="" {
      qui gen int `grcons' = 1
      local _byvars = "`grcons'"
    }
    qui egen `gr' = group(`_byvars'), missing
    qui replace `gr' = . if !`touse'
    qui levelsof `gr', local(K)
    qui replace `newv' = .
    foreach k of local K {
      cap drop `nvalid'
      cap drop `tmpscore'
      qui egen `nvalid'=rownonmiss(`varlist') if `touse' & `gr'==`k'
      qui egen double `tmpscore' = row`opt'(`varlist') if `touse' & `gr'==`k' `egenopt'
      qui replace `tmpscore' = cond(`nvalid'>=`minvalid',`tmpscore', .)
      if "`score'" != "" {
        qui sum `tmpscore' if `touse' & `gr'==`k' [`weight' `exp']
        if r(sum_w) > 0 {
          if "`score'"=="z" | "`score'"=="z_2" {
            qui replace `tmpscore' = (`tmpscore'-r(mean))/r(sd)
			if "`score'"=="z_2" qui replace `tmpscore' = `tmpscore'/2
            if r(sd)==0 {
              if "`_byvars'" == "`grcons'" {
                di "Warning: Standard deviation of {hi:`tmpscore'} = 0,"
              }
              else {
                di "Warning: Standard deviation of {hi:`tmpscore'} in group {hi:`k'} = 0,"
              }
			  if "`score'"=="z_2" di "z_2-scores of {hi:`tmpscore'} have been set to missing for all cases."
              else di "z-scores of {hi:`tmpscore'} have been set to missing for all cases."
            }
          }
          else if "`score'"=="centered" {
            qui replace `tmpscore' = (`tmpscore'-r(mean))
          }
          else if "`score'"=="sd2" {
            qui replace `tmpscore' = (`tmpscore'/(2*r(sd)))
          }
          else if "`score'"=="sprop" {
            qui replace `tmpscore' = (`tmpscore'-`minval')/(`maxval'-`minval')
            qui replace `tmpscore' = (`tmpscore'*(r(sum_w)-1)+`center')/r(sum_w)
            cap drop `m_stat'
            qui egen `m_stat' = rowmin(`varlist') if `touse' & `gr'==`k'
            qui sum `m_stat' if `touse' & `gr'==`k' [`weight' `exp']
            drop `m_stat'
            if r(min)<`minval' {
              di "Warning: Value(s) of {hi:{it:varlist}} less than {hi:minval(`minval')}."
            }
            qui egen `m_stat' = rowmax(`varlist') if `touse' & `gr'==`k'
            qui sum `m_stat' if `touse' & `gr'==`k' [`weight' `exp']
            if r(max)>`maxval' {
              di "Warning: Value(s) of {hi:{it:varlist}} greater than {hi:maxval(`maxval')}."
            }
          }
        }
      }
      qui replace `newv' = `tmpscore' if `tmpscore' < .
    }
  }
  else {   //  prefix by: or weights are not relevant
    qui egen `nvalid'=rownonmiss(`varlist') if `touse'
    qui egen double `tmpscore' = row`opt'(`varlist') if `touse' `egenopt'
    qui replace `tmpscore' = cond(`nvalid'>=`minvalid',`tmpscore', .)
    if "`score'" != "" {
      qui sum `tmpscore' if `touse'
      if r(N) > 0 {
        if inlist("`score'","pomp","prop") {
          qui replace `tmpscore' = (`tmpscore'-`minval')/(`maxval'-`minval')
          qui if "`score'"=="pomp" replace `tmpscore' = 100*`tmpscore'
          if "`score'"=="prop" & float(`endshift') > 0 {
            qui replace `tmpscore' = `tmpscore' + `endshift' if float(`tmpscore')==0
            qui replace `tmpscore' = `tmpscore' - `endshift' if float(`tmpscore')==1
          }
          qui egen `m_stat' = rowmin(`varlist') if `touse'
          qui sum `m_stat' if `touse'
          drop `m_stat'
          if r(min)<`minval' {
            di "Warning: Value(s) of {hi:{it:varlist}} less than {hi:minval(`minval')}."
          }
          qui egen `m_stat' = rowmax(`varlist') if `touse'
          qui sum `m_stat' if `touse'
          if r(max)>`maxval' {
            di "Warning: Value(s) of {hi:{it:varlist}} greater than {hi:maxval(`maxval')}."
          }
        }
      }
    }
    qui replace `newv' = `tmpscore'
  }
  qui sum `newv' if `touse' [`weight' `exp']  //  to create returns of -summarize- of the scores created
end