capture program drop fuzzy
*!fuzzy v2.0.1 KLongest 20feb2007
program define fuzzy, rclass
version 9.0
syntax varlist(min=2 numeric) [if] [fweight pweight iweight] [, LABel(namelist) KEEPSets DROP SETTest(namelist) group(varname) CONVal(real .800)  SIGonly SLEVel(real .05) GREATer(namelist) COMMon cluster(varname) NECessity matx(namelist) STANDardized ALTDisplay REDUCe REMAINders(numlist max=1) dnc(namelist) TRUTHtab(string) KEEPConfigs]
unab ultvlist: _all
marksample touse, novarlist
unab v: `varlist'
local origvars `v'

if "`settest'" ~= "" {
	foreach t of local settest {
	 if "`t'" ~= "yvn" & "`t'" ~= "yvo" & "`t'" ~= "yvv" & "`t'" ~= "yavyb" & "`t'"~="cmvom" & "`t'"~="mavmb"{
	 di as err `"Error: "`t'" Not Valid Set Test"'
	 keep `ultvlist'
	 exit
	 }
	}
}

if "`greater'" ~= "" {
	if "`greater'" ~= "col1" & "`greater'"~= "col2" {
		di as err `"Error: "`greater'" Not a Valid Option of Greater"'
		keep `ultvlist'
		exit
	}
}

if "`matx'" ~= "" {
	if "`matx'" ~= "coincid" & "`matx'"~= "suffnec" & "`matx'"~= "suffnec coincid" & "`matx'"~= "coincid suffnec"{
		di as err `"Error: "`matx'" Not a Valid Option of Matx"'
		keep `ultvlist'
		exit
	}
}

if "`group'" ~= "" {
qui tab `group'
	if `r(r)' ~=2 {
	di as err "Error: Group Variable Must Be 2 Categories"
	keep `ultvlist'
	exit
	}
}

local j: word count `v'
	if "`label'"~=""{
		local numlabs: word count `label'
			if `j'~=`numlabs' {
				di as err "Error: Number of Labels Specified Does NOT Match Number of Variables"
				capture keep `ultvlist'
				exit
				}
		foreach var of local label {
			local lenchck `var'
			local sing: length local lenchck
				if `sing' > 1 {
				di as err "Error: `var' Must be Single Capital Letter"
				capture keep `ultvlist'
				exit 119
				}
		    local exist:list posof "`var'" in varlist
			qui capture su `var'
			   if _rc == 0  & `exist' == 0 {
				  di as err `"Error: Can Not Use "`var'" as Label: Variable `var' Already Defined"'
					capture keep `ultvlist'
					exit 119
				 }
        	}
		tokenize `label'
		local i 1
	      foreach var of local v {
			qui capture gen ``i'' = `var'
			if _rc == 0 {
			label var ``i'' "Copy of `var'"
			}
	      	local i=`i' + 1
			}
		local y: word 1 of `label'
		local start: list label - y
		}
	else {
	   forvalues i = 1/`j' {
		local lencheck: word `i' of `v'
		local singlet: length local lencheck
			if `singlet' > 1 {
				local needlab yes
				continue, break
				}
		}
	if "`needlab'"==""{
	  	local label `origvars'
		local y: word 1 of `origvars'
		local start: list origvars - y
		}
	  else {
		local genlabs Y A B D E F G H I J L M N P Q R T
		local genlabs: list genlabs - ultvlist
		  local numgenlabs: word count `genlabs'
			if `numgenlabs' < `j' {
				di as err "Error: Number of Variables Entered Exceeds Number of Unique Possible Labels"
				di as err "Either Rename Existing Single Letter Variables or Reduce Configuration Size"
				keep `ultvlist'
				exit
				}
			tokenize `genlabs'
		   forvalues i = 1/`j' {
			local label `label' ``i''
			}
			tokenize `label'
				local i 1
	     	 	foreach var of local v {
				qui gen ``i'' = `var'
				label var ``i'' "Copy of `var'"
	      		local i=`i' + 1
			}
		local y: word 1 of `label'
		local start: list label - y
	   }
	}

foreach var of varlist `y' `start' {
	qui su `var'
	if `r(max)' > 1 | `r(min)'<0 {
		di as err "Error: Variable `var' Not Fuzzified"
		capture keep `ultvlist'
		exit 119
	}
}
capture myboolean `start'
local list : subinstr local start " " "", all
local list = lower("`list'")
local list2 = upper("`list'")
local singvar : word count `start'
if `singvar' == 1 {
	local set "`list2' `list'"
	}
if `singvar' > 1 {
	local set "`list' - `list2'"
	}
unab sets: `set'

mark nomis
markout nomis `start'
qui foreach var of local sets {
	capture replace `var'=. if nomis==0
	}
qui drop nomis

if "`weight'" == "" {
	local weight "fweight"
	local exp "=1"
	}

*Creating Single Best Fitting Variable
capture label drop bestfit
capture drop bestfit
qui foreach var of local sets {
	tempvar bstf`var'
	gen `bstf`var'' = 0
	replace `bstf`var'' = 1 if `var'>.5 & `var'~=. & `touse'
	}


gen str6 boolean = "zzzz"
qui foreach var of local sets {
	replace boolean = "`var'" if `bstf`var''==1 & boolean=="zzzz"
	}
qui encode boolean, g(bestfit)
qui replace bestfit = . if boolean=="zzzz"
qui drop boolean

if "`remainders'" ~= "" {
	qui foreach var of local sets {
		tempvar bstf`var'
		capture gen `bstf`var'' = .
		capture replace `bstf`var'' = 1 if `var'>.5
		tab `bstf`var''
			if `r(N)'<= `remainders' {
			 local remains "`remains' `var'"
			 }
	}
}

*Necessity*
if "`necessity'" == "necessity" {
 di
 di in green "Necessity"
 di as text in red %1s "Set", %12s "Necessity"
	foreach var of local sets{
	 qui gen num`var' = min(`y', `var')
	 qui ratio (C: num`var' / `y')  [`weight'`exp'] if `touse'
	 di in white "`var'", %9.3f _b[C]
	drop num`var'
	}
}

**MATRICES**
*Coincidence
local check: list posof "coincid" in matx
if `check'>0 {
	coincid `y' `start'  [`weight'`exp'] , `standardized' `configcluster'
	matrix coincid= r(coincid)
	return matrix coincid coincid
}

*Sufficiency and Necessity
local check: list posof "suffnec" in matx
if `check'>0 {
	suffnec `y' `start'  [`weight'`exp'] , `altdisplay'
matrix suffnec = r(suffnec)
return matrix suffnec suffnec
}

**YCONSIST vs. NCONSIST
local check: list posof "yvn" in settest
if `check' > 0 {
    yvn `y' `sets' [`weight'`exp'] if `touse', cluster(`cluster') `sigonly' slevel(`slevel') greater(`greater')
	local yvncolsig `r(colsig)'
}

**YCONSIST vs. OTHERS YCONSIST
local check: list posof "yvo" in settest
if `check' > 0 {
    yvo `y' `sets' [`weight'`exp'] if `touse', cluster(`cluster') `sigonly' slevel(`slevel') greater(`greater')
      local yvocolsig `r(colsig)'
}

**YCONSIST vs. Set Value (.8 Default)
local check: list posof "yvv" in settest
if `check' > 0 {
	yvv `y' `sets' [`weight'`exp'] if `touse', cluster(`cluster') `sigonly' slevel(`slevel') conval(`conval') greater(`greater')
	   local yvvcolsig `r(colsig)'
}

*Testing Y-Consistency By Groups
local check: list posof "yavyb" in settest
if `check' > 0 {
	yavyb `y' `sets' [`weight'`exp'] if `touse', group(`group') cluster(`cluster') `sigonly' slevel(`slevel')  greater(`greater')
	   local yavybcolsig `r(colsig)'
}

*Testing Means vs. All Other Means
local check: list posof "cmvom" in settest
if `check' > 0 {
	cmvom `y' `sets' if `touse', cluster(`cluster') `sigonly' slevel(`slevel')  greater(`greater')
	   local cmvomcolsig `r(colsig)'
}

*Testing Means By Groups
local check: list posof "mavmb" in settest
if `check' > 0 {
	mavmb `y' `sets' if `touse', cluster(`cluster') group(`group') `sigonly' slevel(`slevel')  greater(`greater')
	   local mavmbcolsig `r(colsig)'
}

*Common Sets*
if "`common'" == "common" {
	local tests : word count `settest'
		forvalues i = 1/`tests' {
		 local test`i' : word `i' of `settest'
		}
	if `tests' == 2 {
		local commsets: list `test1'colsig & `test2'colsig
	}
	if `tests' == 3 {
		local commsets: list `test1'colsig & `test2'colsig
		local commsets: list commsets & `test3'colsig
	}
	if `tests' == 4 {
		local commsets: list `test1'colsig & `test2'colsig
		local commsets: list commsets & `test3'colsig
		local commsets: list commsets & `test4'colsig
	}
	if `tests' == 5 {
		local commsets: list `test1'colsig & `test2'colsig
		local commsets: list commsets & `test3'colsig
		local commsets: list commsets & `test4'colsig
		local commsets: list commsets & `test5'colsig
	}
		di
		di in green "Common Sets"
		di in white "`commsets'"
}

*Truthtable
if "`truthtable'" ~= "" {
    if "`settest'" ~= "" {
		if "`common'" == "" {
			local sets "`r(colsig)'"
		}
	     if "`common'" == "common" {
	      	local sets "`commsets'"
	    	}
     	}
	else {
	  local sets `sets'
	}
truthtab `y' `sets', outsheet(`truthtable') indvars(`start')
}

***REDUCTION****
if "`reduce'" ~= "" {
    if "`settest'" ~= "" {
		if "`common'" == "" {
			local rsets "`r(colsig)'"
		}
	     if "`common'" == "common" {
	      	local rsets "`commsets'"
	    	}
	     if "`sigonly'" == "" & "`greater'"=="" {
		di as err "Error: No Criterion Given to Enter Sets Into Reduction"
		capture keep `ultvlist'
		exit
		}
	}
	else {
	  di as err "Error: No Criterion Given to Enter Sets Into Reduction"
	  capture keep `ultvlist'
	  exit
	}

local size: word count `rsets'
	if `size' == 0 {
		di
		di as err "Error: Can Not Reduce - No Sets Identified as True"
		capture keep `ultvlist'
		exit
	}
local possible: word count `start'
local possible=2^`possible'
if `size' == `possible'{
	di as err "Error: All Possible Sets Entered as True - Reduces to Nothing"
	keep `ultvlist'
	exit
	}

if "`remainders'" ~= "" {
	if "`remains'"~="" {
		local remains: subinstr local remains " " ""
		local rrsets: list rsets - remains
		di
		di
		di in white  "{ul:Complexity Solution: Remainders (`remains') Excluded}"
		reduce `y' `rrsets' if `touse', orig(`origvars') label(`label')
		di
		di
		di in white "{ul:Parsimony Solution: Remainders (Bestfit<=`remainders') Included As Do Not Cares}"
		di
		reduce `y' `rrsets' if `touse', dnc(`remains') orig(`origvars') label(`label')
		}


	if "`remains'"=="" {
		di in red "All Configurations Bestfit>=`remainders'"
		reduce `y' `rsets' if `touse',  orig(`origvars') label(`label')
	}

}

if "`remainders'" == "" {
	reduce `y' `rsets' if `touse',  orig(`origvars') label(`label') dnc(`dnc')
	}

}


local check: list posof "cmvom" in settest
if "`reduce'" ~= "" & `check'>0 {
	di in green "Weighted Means of Final Solution Configurations"
	local formeansdisp "`r(reducsols)'"
	foreach rv of local formeansdisp {
		cnfgen `rv'
		qui su `y' [aw=`rv']
		di in white %-6s "`rv'", %9.3f `r(mean)'
		}
}


*END OF OPTIONS*

return local comm `commsets'
foreach test of local settest {
	return local colsig ``test'colsig'
	}
return local start `start'
return local sets `sets'
return local y `y'
return local oldgroup `group'
return local reducsols `r(reducsols)'
if "`keepconfigs'" == "keepconfigs" {
	local ultvlist: list ultvlist | sets
	}
if "`drop'" == "" & "`genlabs'" == "" {
	local ultvlist: list ultvlist | label
	}

if "`keepsets'" == "keepsets" {
	local ultvlist: list ultvlist | genlabs
	}
capture keep `ultvlist' bestfit

end