*! v1.21 Waffle_plot. Corrects IF
* v1.2 Waffle_plot. With Stata 14 or earlier?
* v1.1 Waffle_plot. adds default size
* v1 Waffle_plot. Simple Waffle plot
/*capture program drop waffle_plot
capture program drop waffle_i
capture program drop mbyparser
capture program drop make_shares*/

program default, rclass
	syntax, [msize(str asis) msymbol(str asis) xnobs(int 0) ynobs(int 0) over(str) waffle_i * ]
	tempvar x xx
	if "`waffle_i'"=="" {
		gen byte `x'=1
		bysort `x' `over':gen  `xx'=_n
		sum `xx' if `xx'==1, meanonly
		local nover = ceil(sqrt(`r(N)'))
		local nnobx = max(`xnobs',`ynobs')*(`nover')
		if `"`msize'"'=="" {
			if !inlist("`msymbol'","square","smsquare","square_hollow","smsquare_hollow") & ///
			   !inlist("`msymbol'","S","s","sh","Sh") local msize `=100/`nnobx''
			else local msize `=75/`nnobx''
		}
	}
	else {
		local nnobx = max(`xnobs',`ynobs')
		if `"`msize'"'=="" {
			if !inlist("`msymbol'","square","smsquare","square_hollow","smsquare_hollow") & ///
			   !inlist("`msymbol'","S","s","sh","Sh") local msize `=100/`nnobx''
			else local msize `=75/`nnobx''
		}
	}
	return local msize  msize(`msize')
end

*** Waffle_i as i-command
program waffle_i
	syntax anything, [nobs(int 0) xnobs(int 0) ynobs(int 0) * ///
			SCTopt(string asis) flip ///
			color(str asis) pstyle(str) ///
			color0(str asis) amargin(real 0) ///
			msymbol(passthru)        	///
			msize(passthru)    		  	///
			msangle(passthru)			///
			mfcolor(passthru)         	///
			mlcolor(passthru)         	///
			mlwidth(passthru)     		///
			mlalign(passthru)     		///
			mlstyle(passthru)           ///
			mstyle(passthru)         	///
			TOTAL TOTAL1(str)           /// <- to give a manual CAP
			rseed(int 0) legend(str asis)]
	** define dimensions
	numlist "`anything'", range(>=0 <=100 )
	local value `r(numlist)'
	capture numlist "`anything'", range(>=0 <=1 )
	if _rc==0 {
		foreach i in `value' {
			local vvalue `vvalue' `=`i'*100'
		}
		local value `vvalue'
	}


 *************************************
	if `nobs'!=0 {
		local xnobs = `nobs'
		local ynobs = `nobs'
	}
	else {
		if `xnobs'==0 local xnobs=10
		if `ynobs'==0 local ynobs=10
	}

	** SCT opts
	local sctopt `sctopt' `msymbol' `msize' `msangle' `mfcolor' `mlcolor' `mlwidth' `mlalign' `mlstyle' `mstyle'

	default , `sctopt' xnobs(`xnobs') ynobs(`xnobs') waffle_i
	local sctopt `sctopt' `r(msize)'
	** create FR
	clear
*	tempname fr
*	frame create `fr'
*	qui:frame `fr':
	qui: {

		set obs `ynobs'
		gen y=_n
		expand `xnobs'
		bysort y:gen x=_n

		if `rseed'!=0 {
			set seed `rseed'
			gen rnd = runiform()
			sort rnd
		}
		*** Rescale x and y
		*replace x = x * 10/`xnobs'
		*replace y = y * 10/`ynobs'
		*** Identify Flags
		qui:replace x=x-.5-`xnobs'/2
		qui:replace y=y-.5-`ynobs'/2
		gen flag = 0
		foreach i in `value' {
			local cnt=`cnt'+1
			local j = `j'+`i'
			replace flag = `cnt' if _n <= round(_N*`j'/100) & flag==0
		}

		*** Prepare Scatter
		local cnt
		foreach i in `value' {
			local cnt=`cnt'+1

			local clr:word `cnt' of `color'
			local pst:word `cnt' of `pstyle'
			*display in w "`clr'"
			local mcmc mcolor(`"`clr'"')

			if "`clr'"=="" local mcmc

 			local sct `sct' (scatter y x if flag==`cnt', `sctopt' `mcmc'  pstyle(`pst') )
		}

			local sct `sct' (scatter y x if flag==0, `sctopt' mc(`color0') )
		   local xmrg = `=0.5*`xnobs'+`amargin''
		   local ymrg = `=0.5*`ynobs'+`amargin'*`ynobs'/`xnobs''
		   if "`flip'"!="" {
			ren (y x) (x y)
			local aux   = `ynobs'
			local ynobs = `xnobs'
			local xnobs = `aux'
			local aux   = `ymrg'
			local ymrg  = `xmrg'
			local xmrg  = `aux'
	   }

		 if `"`legend'"'=="off" | `"`legend'"'=="" {
			local mlg legend(off)
			local legend
		}


	   *display  in w "`ymrg':`xmrg'"
	   ** The plot
		two `sct' , ///
			aspect(`=`ynobs'/`xnobs'') ///
			ylabel("") xlabel("")  xtitle("") ytitle("") ///
			`options' ///
			xscale( range(-`xmrg'  `xmrg')) yscale( range(-`ymrg'  `ymrg')) `mlg' legend(`legend')
	}
end


program mbyparser, rclass
	syntax [varlist(default=none)], [*]
	return local rvars `varlist'
	return local ropt  =`"`options'"'
end

program make_shares
	syntax varlist, [total ototal(varname)]
	tempvar mtotal

	if "`total'"!="" {
		qui:egen `mtotal'=rowtotal(`varlist')
	}
	if "`ototal'"!="" {
		qui:gen `mtotal'=`ototal'

	}
	foreach i of varlist `varlist' {
		replace `i'=`i'/`mtotal'*100
	}
end

program waffle_plot
	syntax anything [if] [in] [aw/], ///
			[by(string asis) nobs(int 0) xnobs(int 0) ynobs(int 0)  ///
			SCTopt(string asis)  /// scatter options
			color(str asis) pstyle(str ) color0(str asis) /// other color options
			amargin(real 0) rseed(int 0) ///
			msymbol(passthru)        	/// <--- Other SCT options
			msize(passthru)    		  	///
			msangle(passthru)			///
			mfcolor(passthru)         	///
			mlcolor(passthru)         	///
			mlwidth(passthru)     		///
			mlalign(passthru)     		///
			mlstyle(passthru)           ///
			mstyle(passthru)         	/// <--- Fin other options
		    title(passthru)                ///         overall title
			subtitle(passthru)             ///         subtitle of title
			note(passthru)                 ///         note about graph
			caption(passthru)              ///         explanation of graph
			legend(string asis) flip * newframe(name) TOTAL TOTAL1(varname) INDividual]

	qui:mbyparser `by'
	local over  `r(rvars)'
	local byopt `r(ropt)'

	capture numlist "`anything'", range(>=0 <=100 )
	if _rc==0 	{
		waffle_i `0'
		exit
	}
	else {
		foreach i of varlist `anything' {
			local nvl `nvl' `i'
		}
		local anything `nvl'
		confirm var `nvl'
		marksample touse
		markout `touse' `anything' `exp' `over' `total1', strok
		** check all variables are less than 100
		if "`total'`total1'"=="" {
 			foreach i of varlist `anything' {
				qui:sum `i' if `touse', meanonly
				if `r(max)'>100 {
					display in red "There are values Larger than 100 in the varlist"
					error 999
				}
			}
		}
	}

	** Determine size
	if `nobs'!=0 {
		local xnobs = `nobs'
		local ynobs = `nobs'
	}
	else {
		if `xnobs'==0 local xnobs=10
		if `ynobs'==0 local ynobs=10
	}



	if `c(stata_version)'>=16 {
		tempname myframe

		qui:frame put `anything' `exp' `over' `total1' if `touse' , into(`myframe')

		syntax anything [aw iw pw fw] [if] [in], [*]
		*frame `new':mscatter_do `anything' [`weight'`exp'], `options'
		qui:frame `myframe': waffle_i2 `anything' [`weight'`exp'], `options'
	}
	if `c(stata_version)'<16 {
		preserve
			qui:waffle_i2 `0'
		restore
	}
end

program waffle_i2
	syntax anything [if] [in] [aw/], ///
			[by(string asis) nobs(int 0) xnobs(int 0) ynobs(int 0)  ///
			SCTopt(string asis)  /// scatter options
			color(str asis) pstyle(str ) color0(str asis) /// other color options
			amargin(real 0) rseed(int 0) ///
			msymbol(passthru)        	/// <--- Other SCT options
			msize(passthru)    		  	///
			msangle(passthru)			///
			mfcolor(passthru)         	///
			mlcolor(passthru)         	///
			mlwidth(passthru)     		///
			mlalign(passthru)     		///
			mlstyle(passthru)           ///
			mstyle(passthru)         	/// <--- Fin other options
		    title(passthru)                ///         overall title
			subtitle(passthru)             ///         subtitle of title
			note(passthru)                 ///         note about graph
			caption(passthru)              ///         explanation of graph
			legend(string asis) flip * newframe(name) TOTAL TOTAL1(varname) INDividual]

	** colorpalette(string asis) /// colorpalette
	** define dimensions
	** Number or variable
	* First Is it a number?
	qui:mbyparser `by'
	local over  `r(rvars)'
	local byopt `r(ropt)'

	capture numlist "`anything'", range(>=0 <=100 )
	if _rc==0 	{
		waffle_i `0'
		exit
	}
	else {
		foreach i of varlist `anything' {
			local nvl `nvl' `i'
		}
		local anything `nvl'
		confirm var `nvl'
		marksample touse
		markout `touse' `anything' `exp' `over' `total1', strok
		** check all variables are less than 100
		if "`total'`total1'"=="" {
 			foreach i of varlist `anything' {
				qui:sum `i' if `touse', meanonly
				if `r(max)'>100 {
					display in red "There are values Larger than 100 in the varlist"
					error 999
				}
			}
		}
	}

	** Determine size
	if `nobs'!=0 {
		local xnobs = `nobs'
		local ynobs = `nobs'
	}
	else {
		if `xnobs'==0 local xnobs=10
		if `ynobs'==0 local ynobs=10
	}

	keep if `touse'
	keep `anything' `exp' `over' `total1'


	*, into(`newframe')

	** sct opts
	local sctopt `sctopt' `msymbol' `msize' `msangle' `mfcolor' `mlcolor' `mlwidth' `mlalign' `mlstyle' `mstyle'

	default , `sctopt' xnobs(`xnobs') ynobs(`xnobs') over(`over')

	local sctopt `sctopt' `r(msize)'
	** by opt
	local byopt `byopt' `title' `subtitle' `note' `caption'

	*qui: frame `newframe':
	{
		** Step 1: Get "means"
		** check all variables are <1

		local less1 = 1
		foreach i of varlist `anything' {
			qui:sum `i' , meanonly

			if `r(max)'>1 local less1 = 0
		}

		foreach i of varlist `anything' {

			if `less1' == 1 replace `i'=`i'*100
		}

		** Shares

		if "`total'`total1'"!="" & "`individual'"!="" make_shares `anything', `total' ototal(`total1')

		tempvar mobs xx
		gen `xx'=1
		bysort `xx' `over':gen `mobs'=_N
		** Collapse
		sum `mons', meanonly
		if `r(max)'>1 {
			if "`exp'"!="" local wgt [iw=`exp']
			*if "`group'"!="" local stat (sum)
			collapse `stat' `anything' `total1' `wgt', by(`xx' `over') fast
		}

	 ** check if Totals activated
		if "`total'`total1'"!="" & "`individual'"=="" make_shares `anything', `total' ototal(`total1')
	*** Defaults



		** Step2 Exapnd
		expand `ynobs'
		tempvar y x
		bysort `xx' `over':gen `y'=_n
		expand `xnobs'
		bysort `xx' `over' `y':gen `x'=_n

		** ID for later
		tempvar id
		qui:gen `id'=_n
		if `rseed'!=0 {
			tempvar rnd
			set seed `rseed'
			gen `rnd' = runiform()
			*sort `rnd'
		}

		*** Rescale x and y
		** ID DOTS
		qui:replace `x'=`x'-.5-`xnobs'/2
		qui:replace `y'=`y'-.5-`ynobs'/2
		tempvar flag j
		gen `flag' = 0
		gen `j'=0

		foreach i in `anything' {
			local cnt=`cnt'+1
			replace `j' = `j'+`i'
			bysort `xx' `over' (`rnd' `id'):replace `flag' = `cnt' if _n <= round(_N*`j'/100) & `flag'==0
		}
********************************************************************
  		*** Step3: Prepare Scatter
		local cnt
		foreach i in `anything' {
			local cnt=`cnt'+1

			if `"`color'"'!="" {
				local clr:word `cnt' of `color'
				local mcmc mcolor(`"`clr'"')
			}

			else if `"`clr'"'=="" local mcmc
			local pst:word `cnt' of `pstyle'
			local sct `sct' (scatter `y' `x' if `flag'==`cnt', `sctopt' `mcmc'  pstyle(`pst') )
		}

	   local sct `sct' (scatter `y' `x' if `flag'==0, `sctopt' mc(`color0') )
	   local xmrg = `=0.5*`xnobs'+`amargin''
	   local ymrg = `=0.5*`ynobs'+`amargin'*`ynobs'/`xnobs''
	   *display  in w "`ymrg':`xmrg'"
	   ** Option flip

	   if "`flip'"!="" {
			ren (`y' `x') (`x' `y')
			local aux   = `ynobs'
			local ynobs = `xnobs'
			local xnobs = `aux'
			local aux   = `ymrg'
			local ymrg  = `xmrg'
			local xmrg  = `aux'
	   }
		** Legend default OFF
	   if `"`legend'"'=="off" | `"`legend'"'=="" {
			local mlg legend(off)
			local legend
		}


	   *** step 4: Do the scatter
	   if "`over'"!="" {
	   two `sct' , ///
			aspect(`=`ynobs'/`xnobs'') ///
			by(`over', `byopt' noiylabel noixlabel noixtick noiytick `mlg'   ) ///
			xscale(noline) yscale(noline) ytitle("") xtitle("")	`options' ///
			xscale( range(-`xmrg'  `xmrg') ) yscale( range(-`ymrg'  `ymrg') ) legend(`legend')  xlabel("",nogrid) ylabel("",nogrid)
	   }
	   else {
			two `sct' , ///
			aspect(`=`ynobs'/`xnobs'')  ///
			xscale(noline) yscale(noline) ytitle("") xtitle("")	`options' ///
			xscale( range(-`xmrg'  `xmrg') ) yscale( range(-`ymrg'  `ymrg') ) `mlg' legend(`legend')  ///
			xlabel("",nogrid) ylabel("",nogrid) `byopt'
	   }
	}
end

*xscale( range(`=-`amargin'-0.5*`xnobs''                  `=0.5*`xnobs'+`amargin'') ) ///
*yscale(lstyle(thin) range(`=-0.5*`ynobs'-`amargin'*`ynobs'/`xnobs''  `=0.5*`ynobs'+`amargin'*`ynobs'/`xnobs'') ) ///
/*			else if `"`colorpalette'"'!="" {
				if strpos( `"`colorpalette'"' , ",") == 0 local colorpalette `colorpalette', nograph n(`cnt')
				else local colorpalette `colorpalette' nograph n(`cnt')

				colorpalette `colorpalette'

				local mcmc mcolor("`r(p`cnt')'")
			}*/
/**
To Be considered, a faster version of waffle plot
Does not rely on by.

This will be faster because it creates a single plot rather than multiple put together.
So far biggest limitation, How to add Holes

distinct agegrp
local cols=ceil(sqrt(17))
display `cols'

gen x2 = x
gen y2 = y

drop yc xc
sum y
gen yc = r(max)
gen xc = 0
forvalues i = 1 / 17 {
	local xcnt = `xcnt'+1
	if `xcnt'>7 {
		local xcnt = 1
	}
	if `xcnt'==1 local ycnt = `ycnt'+1

	replace x2 = x + (`xcnt'-1)*(9+3)	if agegrp==`i'
	replace y2 = y - (`ycnt'-1)*(9+5)	if agegrp==`i'

	replace xc = xc+  (`xcnt'-1)*(9+3)     if agegrp==`i'
	replace yc = yc- (`ycnt'-1)*(9+5) + 2  if agegrp==`i'

}
sum y2
local l1 = `r(max)'-`r(min)'
sum x2
local l2 = `r(max)'-`r(min)'
**
two scatter  y2 x2 || scatter  y2 x2 if flag==1 || (scatter yc xc if ll==1, msymbol(none) mlab(s)   mlabpos(0) mlabsize(3)), aspectratio(`=`l1'/`l2'') xlabel("") ylabel("")
**/