'decomposes the changes in a variable between two scenarios based on a particualr model specification and adds the table to the dep-spool
logmode all
include .\subroutines.prg

'strings - inputs
	%m		= @equaloption("model") '"_kvarts"
	%var		= @equaloption("variable") '"rnok"'"l82"'"a50" '"ctot"'"a50"' "cpeb"'"ctot"
	'%0		= @equaloption("scenario0") '"_ktg"   
	'%1		= @equaloption("scenario1") '"_kt", typically the active scenario in the model object
	%spool	= @equaloption("spool") 
	%count	= @equaloption("count") 

	!tablewidth=12

	'check if it is a new eviews version
	!builddate=@dateval(@builddate, "YYYY-MM-DD")
	!updatedate= @dateval("2019-09-01", "YYYY-MM-DD")
	!neweviews=@datediff({!updatedate}, {!builddate}, "dd")-1

'other auxiliary strings
	%tabledec=@getnextname("table_decompose")  'table to be added to spool in DEP-addin
	%page	=@pagename
	%smpl 	= @pagesmpl
	%0z		=  "_zz0" 	'auxiliary baseline scenario in auxiliary model 
	%1z		=  "_zz1" 	'auxiliary scenarios in auxiliary model 
	'%spec
		call spec(%m, %var)  	'returns a string %SPEC with the specification of a variable within a model object (including coefficients), see addins\subroutines.prg

	'create txt-error object
		%txterror=@getnextname("DEP_error")
		text {%txterror}
		%txtmsg="The following errors occured when decomposing the spec for " +%var+ ":"
		{%txterror}.append {%txtmsg}
		{%txterror}.append 

	'txt-info object
		%textinfo=@getnextname("dep_info")
		text {%textinfo} 
		{%textinfo}.append The following variables have been used to decompose the contributions:
		{%textinfo}.append 

'check for existence
	%var0=%var+%0
	%var1=%var+%1
	if @isobject(%var)=0 then
		%txtmsg="- The variable  " +%var+ " does not exist in the workfile"
		{%txterror}.append {%txtmsg}
	endif	
	if @isobject(%var0)=0 then
		%txtmsg="- The variable  " +%var0+ " does not exist in the workfile"
		{%txterror}.append {%txtmsg}
	endif	
	if @isobject(%var1)=0 then
		%txtmsg="- The variable  " +%var1+ " does not exist in the workfile"
		{%txterror}.append {%txtmsg}
	endif	


'check if scenarios 1 and 0 is present in the model
	'if yes: %active1="yes"
	'in no: %active1 is empty
	%scenariotable=@getnextname("auxtable")
	freeze({%scenariotable}) {%m}.scenlist
		'loop across table to see if the scenarioname %1 is present
		for !r=4 to {%scenariotable}.@rows
			%modelaliases={%scenariotable}({!r},2)
			if @upper(@trim(%modelaliases))=@upper(@trim(%1)) then
				%active1="yes"
				%scenarioname1={%scenariotable}({!r},1)
			endif
	
			if @upper(@trim(%modelaliases))=@upper(@trim(%0)) then
				%active0="yes"
				%scenarioname0={%scenariotable}({!r},1)
			endif
		next
	delete(noerr)  {%scenariotable}

	'scenario 0
		%endoglistinscenario0		= {%m}.@endoglist(%scenarioname0)
			'take away the scenario name from the strings
				%old="*"+@upper(%0)
				%new="*"
				%endoglistinscenario0	= @wreplace(@upper(%endoglistinscenario0), %old, %new)
			'must subtract excluded variables from the endogenous list
				%endoglistinscenario0=@wnotin(%endoglistinscenario0, {%m}.@excludelist(%scenarioname0))
				'add overridden variables	
					%overridelist0={%m}.@overridelist(%scenarioname0)
					'take away the alias  from the strings
					%old="*"+%0
					%new="*"
					%overridelist0		= @wreplace(@upper(%overridelist0), %old, %new)	  'string with all variables that hold add-factors in the mother model (i.e. without alias)
				%endoglistinscenario0=%endoglistinscenario0+ " " +%overridelist0


		%exoglistinscenario0		= {%m}.@exoglist(%scenarioname0)
		
		%addfactorsscenario0	=  {%m}.@addfactors(%scenarioname0) 'string with all variables that hold add-factors in the mother model 
			'take away the alias (A) from the strings
			%old="*_A"
			%new="*"
		%addfactorsscenario0		= @wreplace(@upper(%addfactorsscenario0), %old, %new)	  'string with all variables that hold add-factors in the mother model (i.e. without alias)

	'scenario 1
		%endoglistinscenario1		= {%m}.@endoglist(%scenarioname1)
			'take away the scenario name from the strings
				%old="*"+@upper(%1)
				%new="*"
				%endoglistinscenario1	= @wreplace(@upper(%endoglistinscenario1	), %old, %new)
			'must subtract excluded variables from the endogenous list
				%endoglistinscenario1=@wnotin(%endoglistinscenario1,  {%m}.@excludelist(%scenarioname1))
				'add overridden variables	
					%overridelist1={%m}.@overridelist(%scenarioname1)
					'take away the alias  from the strings
					%old="*"+%1
					%new="*"
					%overridelist1		= @wreplace(@upper(%overridelist1), %old, %new)	  'string with all variables that hold add-factors in the mother model (i.e. without alias)
				%endoglistinscenario1=%endoglistinscenario1+ " " +%overridelist1
			
		%exoglistinscenario1		= {%m}.@exoglist(%scenarioname1)
			%addfactorsscenario1	=  {%m}.@addfactors(%scenarioname1) 'string with all variables that hold add-factors in the mother model 
			'take away the alias (A) from the strings
			%old="*_A"
			%new="*"
		%addfactorsscenario1		= @wreplace(@upper(%addfactorsscenario1), %old, %new)	  'string with all variables that hold add-factors in the mother model (i.e. without alias)




'find active scenario alias
	%active={%m}.@endoglist("@active")  'list of all endogenous variables in active scenario
	%active=@word(%active,1) 'take the first variable (with active scenario alias)
	!start=@rinstr(%active,"_") 'find the starting position of underscore (starting in reverse)
	if !start=0 then
		%prompt="Cannot find the active scenario name. Please specify an active scenario with an underscore, e.g. _1"
		@uiprompt(%prompt)
		stop
	endif
	%active=@trim(@mid(%active,{!start})) 'alias including the _


	
'create auxiliary model of specification %spec
	%maux=@getnextname("aux")
	model {%maux}
	{%maux}.append {%spec}

	'strings (need to make separatel lists depending on variable-status and scenario)
		'exogenous variables in the auxiliary model that are exogenous in scenario0 in the "mother"-model
		%exogexoglist0		= {%maux}.@exoglist
		%exogexoglist0		= @wintersect(@upper(%exoglistinscenario1), @upper(%exogexoglist0))
	
		'exogenous variables in the auxiliary model that are exogenous in scenario1 in the "mother"-model
		%exogexoglist1		= {%maux}.@exoglist
		%exogexoglist1		= @wintersect(@upper(%exoglistinscenario1), @upper(%exogexoglist1))
			
		'exogenous variables in the auxiliary model that are endogenous in scenario0 in the "mother"-model	
		%exogendoglist0	= {%maux}.@exoglist
		%exogendoglist0	= @wintersect(@upper(%endoglistinscenario0), @upper(%exogendoglist0))
		
		'exogenous variables in the auxiliary model that are endogenous in scenario1 in the "mother"-model	
		%exogendoglist1	= {%maux}.@exoglist
		%exogendoglist1	= @wintersect(@upper(%endoglistinscenario1), @upper(%exogendoglist1))
		

	'find list of endogenous variables from %m and both copy and override them in the model %maux	
		%overridelist=@wintersect(@upper(%endoglistinscenario0), @upper(%mauxexoglist))

	'assigns add-factor if it exists in the mother model:
	if @wfindnc(%addfactorsscenario0, %var)>0 then
		{%maux}.addassign {%var}							'assings add-factor
		
		'add the add-factor to the exog-list og variables that will be  copied below
		%exogexoglist0=%exogexoglist0+" " +%var+"_a"
		%exogexoglist1=%exogexoglist1+" " +%var+"_a"
	endif



'create auxiliary page
	%auxpage="_$aux$"
	smpl @all
	%allobjects=@wflookup("*")
	%scalars=@wflookup("*", "scalar")  'need to include scalars since some model specifications hold scalars (and it is not possible to find the scalars being used from the model object)
	%copyobjects=%var+" "+%var+%0+" "+%var+%1+ " "+%maux+" "+ %scalars
	%copyobjects=@wintersect(@upper(%allobjects), @upper(%copyobjects)) ' copy only objects that exists in workfile

	pagecopy(page={%auxpage}) {%copyobjects} {%txterror}' {%var} {%var}{%0} {%var}{%1} {%maux}
	pageselect {%auxpage}
		wfdetails
		smpl {%smpl}



				
	pageselect {%page}
		smpl {%smpl}
	'copy series for the 4-different alternatives 
		'endogenous variables all have aliases, but .... 
		'...  exogenous variables must be checked specifically. if active scenario, no alias. if not active scenario, the variable must have an alias (else make error message). 
		
		'exog0
			if @isempty(%exogexoglist0)=0 then
				for %v {%exogexoglist0}
					'check first it the variable holds an alias, if not, pick actuals (this ensures that overridden variables and _KTG are picked first)
					%old=%v+%0
					%new=%v '+%0z
						if @isobject(%old)=1 then 'exogenous variable has a scenario alias
							copy  {%page}\{%old}  {%auxpage}\{%new} 
							{%textinfo}.append - {%old}
						else	
							%old=%v 'the exogenous variable holds no alias
								if @isobject(%old)=1 then 'exogenous variable has no scenario alias and exists
									copy  {%page}\{%old}  {%auxpage}\{%new} 
									{%textinfo}.append - {%old}									
									else
									%txtmsg="The variable " %v + " or  "+%v+%1+" is not found in the workfile"'
									{%txterror}.append - {%txtmsg}
									{%txterror}.append 
								endif					
						endif
				next
			endif

			'exog1
			if @isempty(%exogexoglist1)=0 then
				for %v {%exogexoglist1}
					'check first it the variable holds an alias, if not, pick actuals
					%old=%v+%1
					%new=%v+%1z
						if @isobject(%old)=1 then 'exogenous variable has a scenario alias
							copy  {%page}\{%old}  {%auxpage}\{%new} 
							{%textinfo}.append - {%old}
						else
							%old=%v 'the exogenous variable holds no alias
								if @isobject(%old)=1 then 'exogenous variable has no scenario alias and exists
									copy  {%page}\{%old}  {%auxpage}\{%new} 
									{%textinfo}.append - {%old}									
									else
									%txtmsg="The variable " %v + " or  "+%v+%1+" is not found in the workfile"'
									{%txterror}.append - {%txtmsg}
									{%txterror}.append 
								endif														
						endif
				next
			endif
		

		'endog0
			if @isempty(%exogendoglist0)=0 then
				for %v {%exogendoglist0}
					%old=%v+%0
					%new=%v'+%0z
					if @isobject(%old)=1 then
							copy  {%page}\{%old}  {%auxpage}\{%new} 
							{%textinfo}.append - {%old}
						else
							%txtmsg="The endogenous variable " +%v+%0+" is not found in the workfile. Check that the scenario "+%0 + " is the active scenario in the model: "+ %m
							{%txterror}.append - {%txtmsg}
							{%txterror}.append 
					endif
				next
			endif
		

		'endog1
			if @isempty(%exogendoglist1)=0 then
				for %v {%exogendoglist1}
					%old=%v+%1
					%new=%v+%1z
						if @isobject(%old)=1 then ' 
							copy  {%page}\{%old}  {%auxpage}\{%new} 
							{%textinfo}.append - {%old}
						else
							%txtmsg="The endogenous variable " +%v+%1+" is not found in the workfile"' and the scenario "+%1 + " is not found in the model: "+ %m
							{%txterror}.append - {%txtmsg}
							{%txterror}.append 
						endif
				next
			endif

	
!lines={%txterror}.@linecount
if !lines=2 then	

	pageselect {%auxpage}

	
		'list of exogenous variables in the auxiliary model
			%mauxvarlist={%maux}.@exoglist("actuals")+" "+{%maux}.@addfactors("actuals")
	

	'check if exogenous variables has non-NA values in the specified smp, use the CHECKNA-addin
	checkna(nostatus,nostop, txt="checknatxt") {%maux}
				if @isobject("checknatxt")=1 then
					call copytxttotxt(checknatxt, {%txterror}) 'copy checkna to txterror object
				endif

	'solve first for the baseline path  and set it as the active scenario
		{%maux}.scenario(n, a=%0z) baseline_{%0} 			'scenario 1
	'		{%maux}.scenario baseline
			smpl {%smpl}
					setmaxerrs 20000  'allow for errors when solving the model in case exogenous variables has NA-values
						{%maux}.solve
					clearerrs
					setmaxerrs 0
			'the difference between y{%0z} and y_{%0} is due to the model being different
	
	'make decomposition - total and model contribution
		genr dec_total		= {%var}{%1}-{%var}{%0}		'total change
		genr dec_model	= {%var}{%0z}-{%var}{%0}  		'contribution from changed model specification when going from old-scenario (0) to new scenario (1). this equals the difference between the old scenario and the model solution using the current/new model specification
	
	
		'make alternative scenario %1z and set it as the active scenario
		{%maux}.scenario(n, a=%1z) {%1} 			'scenario 1
	
	
	'loop across exogenous variables to find the 1. order effect of the different changes
		'let %0 be baseline, e.g. y_0=a+b*X_0 and y_1-y_0=b*(x_1-x_0)
		for %v {%mauxvarlist}
			{%maux}.override {%v}  'replaces existing override list with {%v}-variable
			smpl {%smpl}
					setmaxerrs 20000
						{%maux}.solve
'					clearerrs
'					setmaxerrs 0
						'copy  {%var}{%1z}  dec_{%var}_{%v}2	
						smpl @all
							genr   dec_{%v}={%var}{%1z}-{%var}{%0z}
							%sum=%sum+ "+"+ "dec_"+%v
							%varlist=%varlist+ " dec_"+%v
						smpl {%smpl}
		next




		
				%sum=%sum'+"+dec_model"
				%varlist=%varlist'+ " dec_Model"
				genr   dec_other=dec_total-({%sum})

		'can only proceed if there has not been an error, i.e. some variables hold NA values
		if @isobject("checknatxt")=0  and @errorcount=0 then

			clearerrs
			setmaxerrs 0			
			
			'fixed number of decimals
				!max=@max(@abs(dec_total))
				%100=""
				if !max<1 then
					!decimals=5
					else
					if !max>1000 then
						!decimals=0
						else
						!decimals=2
					endif
				endif
			
			'frequency conversion
			setmaxerrs 100 'needs to allow for errors to check if @lohi exist in the running EViews program (was included after july 2019)
				%freqtoannual=@lower({%var}.@lohi)  'frequency conversion operator, will create an error if old EViews version is being executed
					'default with average: if old EViews or if sum is not specified in the series being run on a new EViews version
					%freqtoannualtab="avgtran"			'code to be used in ddtable below if converion as average
					%frecconv="an average"			'string to be used in the table below
					if @errorcount = 0 then
						if %freqtoannual="sum" then
							%freqtoannualtab="sumtran"		'code to be used in ddtable below if converion as sum
							%frecconv="a sum"				'string to be used in the table below
						endif	
					endif	
'			clearerrs
'			setmaxerrs 0  'set back to zero errors
				
			'table with results
				%tablename="tab"
				group _dec {%var}{%1}  {%var}{%0}  dec_total {%varlist} dec_Other
				
				_dec.ddtabopts display(FIRST,4) firstfreq(A) format(1, fmt=F.{!decimals}, units=N, THOUSAND, -COMMA, -PARENS, ) colheader(B,-I) freqconv(1, {%freqtoannualtab})
				freeze({%tablename}) _dec.ddtable
				
				
				{%tablename}.deleterow(2)
			
 				!cols= {%tablename}.@cols			'antall kolonner i tabellen
 				!rows={%tablename}.@rows			'antall rader i tabellen
			
				!max=@max({%var}{%1} )
				'ta bort komma og skaler
 				for !r = 2 to !rows
 					for !c=2 to !cols  'inkluderer ikke den første tekstkolonnen i tabellen
 						'a) ta bort komma		
 						%cell=@replace({%tablename}(!r,!c), ",", " ")
 						{%tablename}(!r,!c)=@stripquotes(%cell)  'skifter ut komma med mellomrom
 						'b) erstatte punktum med komma
 			 			%cell=@replace({%tablename}(!r,!c), ".", ",")
 			 			{%tablename}(!r,!c)=@stripquotes(%cell)  'skifter ut punktum med komma
					next
				next
			
			
				'change labels in table
				!iter=2
				for %label {%varlist} dec_sum dec_total
				!iter=!iter+1
					%currentlabel={%tablename}({!iter},1)
					%newlabel=@mid(%currentlabel,5) '@lower(@mid(%currentlabel,5))
					{%tablename}({!iter},1)="   "+%newlabel
				next
				{%tablename}(2,1)= %var+%1 
				{%tablename}(3,1)= %var+%0 
				{%tablename}(4,1)="Difference"
				!nexttolast=!rows-1
				'{%tablename}(!nexttolast,1)="  Changed  spec.**"
				{%tablename}(!rows,1)="   Other**"
				
				!comment=!rows+1
				{%tablename}(!comment,1)=" *All variables are transformed to annual frequency as "+ %frecconv+ " and calculated as: x"+%1+"-x"+%0
				{%tablename}.setjust(!comment) left
				
				!star1=!rows+2
				%star1=" **Changes due to compositional effects, different model specifications, data revisions of the endogenous variable, etc." '+ %var+%1+" and "+ %var+%0
				{%tablename}(!star1,1)=%star1 	

				
				!star2=!rows+3
				%star2="Type 'DEP HELP' in the command window to open a PDF-documentation with further information about  the decomposition."
				{%tablename}({!star2},1)=%star2	
			
		
				'formatering av tabell
					{%tablename}.setlines(1) b
					'setter gråfarge på annenhver rad
					for !r = 2 to !rows step 2
		 					{%tablename}.setfillcolor(!r) @rgb(235, 235,235) '@rgb(240,248,255) '@rgb(238,233,233) '
					next
				
					'font
					{%tablename}.setfont(@all) 8.8pt
					'{%tablename}.setfont(1) 9pt
					{%tablename}.setfont(!comment) 7pt
					{%tablename}.setfont(!star1) 7pt
					{%tablename}.setfont({!star2}) 7pt
										
					'bold and heigh
					{%tablename}.setfont(4) +b
					{%tablename}.setheight(2:4) 1
					{%tablename}.setlines(!rows) b
					{%tablename}.setheight(@all) 1.3
					{%tablename}.setheight(!comment) 0.8
					{%tablename}.setheight(!star1) 0.8
					{%tablename}.setheight(!star2) 0.8
				
					'width
					if !neweviews <0 then 'hvis det en en ny versjon av EViews, dvs etter 1. sept  2019
						!colwidth={%tablename}.@colwidth(2)
						if !colwidth<10 then
					 		!colwidth= 10
						endif
						else
						!colwidth= 10
					endif
					{%tablename}.setwidth(@all) !colwidth

				
					'fotnote

					
					'title
					{%tablename}.insertrow(1)
					{%tablename}(1,1)="Decomposing the difference between "+%var+%1+ " and "+%var+%0 +"*"
					{%tablename}.setfont(1) +b 9.3pt
					{%tablename}.setheight(1) 1.9
					{%tablename}.insertrow(2)		
					{%tablename}.setheight(2) 0.3
					{%tablename}.setlines(2) b
					{%tablename}.setjust(1) left
					{%tablename}.setwidth(1) 18
					{%tablename}.setwidth(A) 30'
		
					


	'split table if it has more than 10 columns
				!columns_in_each_table=16
				!cols={%tablename}.@cols
				!rows={%tablename}.@rows
				!remaining=!cols-1 'counting the remaining columns in the loop after subtracting 10 col per loop
				!iter=0 'number of tables being copied

				%tableauxname=@getnextname("tabnraux")
				copy {%tablename} {%tableauxname}

				while !remaining>0
					!remaining=!remaining-!columns_in_each_table		
					!last=!iter
					!iter=!iter+1
					%tablename{!iter}=@getnextname("tabnr")


					'delete the first set of columns
						if !iter>1 then 'do not delete columns in the first iteration
					 		{%tableauxname}.deletecol(2) !columns_in_each_table
						endif
						copy {%tableauxname} {%tablename}{!iter}

					'delete the last set of columns
						!coliter={%tablename}{!iter}.@cols
						!startcol=!columns_in_each_table+2
					 	{%tablename}{!iter}.deletecol(!startcol) !coliter

		
					%tables=%tables+ " "+%tablename+@str(!iter)
			wend


		
			'append tables to spool object
			!iter=0
			for %tab {%tables}
				!iter=!iter+1
				pageselect {%page}
				%auxtab2=@getnextname("tabdec")
				copy {%auxpage}\{%tab} {%page}\{%auxtab2}
				'pageselect {%page}
				'%name=%1+_vs"+%0+"_"
				if @wcount(%tables)>1 then
					{%spool}.append(name={%1}_vs{%0}_{!iter}) {%auxtab2}
					'{%spool}.width(obj={%1}_vs{%0}_{!iter}) !tablewidth
				else
					{%spool}.append(name={%1}_vs{%0}) {%auxtab2}
					'{%spool}.width(obj={%1}_vs{%0}) !tablewidth
				endif


				delete(noerr) {%auxtab2}
			next
		
			'cleanup
				delete(noerr) {%maux} {%txterror} {%tabledec} 
		
			else
				'adds error-msg to spoolobject if there are errors
				copy {%txterror} {%page}\{%txterror}
				pageselect {%page}
				{%spool}.append(name={%1}_vs{%0}) {%txterror}
				'cleanup
				delete(noerr) {%maux} '{%txterror}
	
		endif  '	if @isobject("checknatxt")=0  then
	else
		clearerrs
		setmaxerrs 0	
	'adds error-msg to spoolobject if there are errors
	copy {%txterror} {%page}\{%txterror}
	pageselect {%page}
	{%spool}.append(name={%1}_vs{%0}) {%txterror}
	'cleanup
	delete(noerr) {%maux} '{%txterror}
endif


'cleanup
	delete(noerr) {%textinfo} {%txterror}  {%tabledec} 

'legger til description
	{%spool}.setattr("Description") Spool-object with information about the variable {%var} in the model {%m} for the scenarios {%0} and {%1}

'delete auxiliary page
	pagedelete {%auxpage}



subroutine copytxttotxt(text x, text y)
	'append all txt in text object X to the text object Y
	for !i=1 to x.@linecount
		%line=x.@line({!i})
		y.append {%line}
	next
		y.append  'append an empty line
endsub


