************************************************************************************************************
* Stata program : Raschfit
* The Raschfit and the Raschfit-fast procedures to construct sub-scales of items
* Release 2 : June 8, 2004
*
* Historic
* Version 1 (2004-05-06) [Jean-Benoit Hardouin]
*
* Jean-benoit Hardouin, Regional Health Observatory of Orléans - France
* jean-benoit.hardouin@neuf.fr
*
* Use the Stata programs mmsrm, raschtest and gammasym who can be download on http://anaqol.free.fr
* News about this program :http://anaqol.free.fr
*
* FreeIRT Project website : http://freeirt.free.fr
*
* Copyright 2004 Jean-Benoit Hardouin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
************************************************************************************************************


program define raschfit,rclass
version 7.0
syntax varlist(min=2 numeric) [,KERnel(integer 0) NBSCales(integer 1) ITEMSorder(string) nofast]
if "`itemsorder'"=="" {
        local itemsorder mspinv
}
local nbitemstot : word count `varlist'
tokenize `varlist'

tempfile raschfitfile
qui save `raschfitfile',replace
preserve

tempname affect
matrix define `affect'=J(1,`nbitemstot',0)
matrix colnames  `affect'=`varlist'

tempname rep item  matbetadim1 matbetadim2
if `kernel'!=0 {
   local listkernel
   forvalues i=1/`kernel' {
             local listkernel `listkernel' `rep'`i'
             matrix `affect'[1,`i']=1
   }
}

local dim=0
local nbitemsnosel=`nbitemstot'
local nbitemstotdim1=`nbitemstot'
local nbitemsnoselukernel=`nbitemstot'-`kernel'


tempvar id betadim1 betadim2
forvalues i=1/`nbitemstot' {
      qui drop if ``i''==.
      rename ``i'' `rep'`i'
}
qui gen `id'=_n
tempfile filescale
qui save `filescale',replace


while `nbitemsnosel'>2&`dim'<`nbscales' {
      use `filescale',replace
      local iteration=0
      local dim=`dim'+1
      if `dim'>1 {
         local kernel=0
         local listkernel
      }

      di in green "SCALE: " in yellow `dim'
      di in green "{hline 9}"
      di
      tempname result`dim'
      local listitemsnosel
      local varlist`dim'
      tokenize `varlist'
      forvalues i=1/`nbitemstot' {
            if `affect'[1,`i']==0 {
                   local varlist`dim' `varlist`dim'' ``i''
                   local listitemsnosel `listitemsnosel' `rep'`i'
            }
      }
      local nbitemsnosel:word count `listitemsnosel'
      if `dim'>1 {
            local nbitemstotdim`dim':word count `listitemsnosel'
      }

      if `kernel'>=2 {
            local fixed=`kernel'
      }
      else {
            local fixed=2
      }
      matrix define `result`dim''=J(`=`nbitemstotdim`dim''-`fixed'',`=`nbitemstotdim`dim''+7',0)

      tempname order`dim' affect`dim'
      matrix `order`dim''=J(1,`nbitemstotdim`dim'',0)
      matrix `affect`dim''=J(1,`nbitemstotdim`dim'',0)

      if "`itemsorder'"=="msp"|"`itemsorder'"=="mspinv" {
            di in green _col(0) "The program is ordering the items"
            qui msp `listkernel' `listitemsnosel',c(-99)  notest kernel(`kernel')
            local scale1 "`r(scale1)'"
            local scalenum1 "`r(scalenum1)'"
            tokenize `scalenum1'
            local listitemsselnum
            forvalues j=`=`nbitemstotdim`dim''+1-`fixed''/`nbitemstotdim`dim'' {
                matrix `order`dim''[1,``j'']=1
                local k:word `j' of `scalenum1'
                matrix `affect`dim''[1,`k']=1
                local listitemsselnum `listitemsselnum' `k'
            }
            forvalues j=1/`nbitemsnosel' {
                matrix `order`dim''[1,`j']=`=`nbitemsnosel'+1-`j''
            }
            tokenize `scale1'
            local listitemssel ``=`nbitemstotdim`dim''-1'' ``nbitemstotdim`dim'''

            local listitemsnosel
            local listitemsnoselnum
            
            if "`itemsorder'"=="mspinv" {
                forvalues j=1/`=`nbitemstotdim`dim''-`fixed'' {
                      local listitemsnosel `listitemsnosel' ``j''
                      local k:word `j' of `scalenum1'
                      local listitemsnoselnum `listitemsnoselnum' `k'
                }
            }
            else if "`itemsorder'"=="msp"{
                 forvalues j=`=`nbitemstotdim`dim''-`fixed''(-1)1 {
                      local listitemsnosel  `listitemsnosel' ``j''
                      local k:word `j' of `scalenum1'
                      local listitemsnoselnum  `listitemsnoselnum' `k'
                 }
            }
      }
      else if "`itemsorder'"=="order" {
           tokenize `listkernel' `varlist`dim''
           local listitemssel
           local listitemsselnum
           local listitemsnosel
           local listitemsnoselnum
           forvalues j=1/`fixed'{
                     local listitemssel `listitemssel' `rep'`j'
                     local listitemsselnum `listitemsselnum' `j'
                     matrix `affect`dim''[1,`j']=1
           }
           forvalues j=`=`fixed'+1'/`nbitemstotdim`dim'' {
                     local listitemsnosel `listitemsnosel' `rep'`j'
                     local listitemsnoselnum `listitemsnoselnum' `j'
           }
      }

      if `dim'>1 {
             tokenize `varlist`dim''
      }
      else {
             tokenize `varlist'
      }

      local nbitemsnosel:word count `listitemsnosel'
      local list
      *tokenize `varlist'
      forvalues i=1/`=`nbitemsnosel'+`fixed'' {
            local tmp:word `i' of "`listitemsnosel' `listitemssel'"
            local list `list' ``i''
      }

      matrix colnames `result`dim''=`list'  iteration nbitems ll1 AIC1 ll2 AIC2 choose

      di _col(0) in green "The kernel of the scale is " in yellow _continue
      forvalues i=1/`fixed' {
            local inum:word `i' of `listitemsselnum'
            di in yellow "``inum'' " _continue
      }
      di
      tokenize `listitemsnosel'
      forvalues i=1/`=`nbitemsnosel-2'' {
            local iteration=`iteration'+1
            di
            di in green _col(10) "iteration:" in yellow" `iteration'"
            di in green _col(10) "{hline 13}"
            di
            qui use `filescale' , clear
            local i2:word `i' of `listitemsnosel'
            local i2num:word `i' of `listitemsnoselnum'
            qui keep `id' `listitemssel' `i2'
            tempname score1 score2
            qui gen `score2'=0
            tokenize `listitemssel'
            local nbitemssel: word count `listitemssel'
            forvalues j=1/`i' {
                      local j2num:word `j' of `listitemsnoselnum'
                      if `affect`dim''[1,`j2num']==1 {
                            matrix `result`dim''[`iteration',`j']=1
                      }
            }
            forvalues j=1/`nbitemssel' {
                      local j2:word `j' of `listitemssel'
                      local j2num:word `j' of `listitemsselnum'
                      qui replace `score2'=`score2'+`j2'
            }
            tokenize `listitemsnosel'
            qui gen `score1'=`score2'+`i2'
            forvalues j=`=`nbitemsnosel'+1'/`=`nbitemsnosel'+`nbitemssel'' {
                      matrix `result`dim''[`iteration',`j']=1
            }
            matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+1']=`iteration'
            matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+2']=`nbitemssel'
            matrix `result`dim''[`iteration',`i']=2

            if "`fast'"=="" {
                        qui count if `score1'!=`=`nbitemssel'+1'&`score1'!=0&`score2'!=`nbitemssel'
                        local N=r(N)
                        di in green _col(10)"The program is estimating the parameters of the models (N=" in yellow "`N'" in green ")"
                        qui raschtest `listitemssel' `i2' if `score1'!=3&`score1'!=0&`score2'!=2, mml notest

                        matrix `matbetadim1'=e(beta),0
                        qui raschtest `listitemssel' if `score1'!=3&`score1'!=0&`score2'!=2, mml notest
                        matrix `matbetadim2'=e(beta),0
                        qui reshape long `rep' , i(`id') j(`item')
                        qui gen `betadim1'=0
                        qui gen `betadim2'=0

                        forvalues j=1/`nbitemssel' {
                                  local j2num:word `j' of `listitemsselnum'
                                  qui replace `betadim1'=-`matbetadim1'[1,`j'] if `item'==`j2num'
                                  qui replace `betadim2'=-`matbetadim2'[1,`j'] if `item'==`j2num'
                        }
                        qui replace `betadim1'= -`matbetadim1'[1,`=`nbitemssel'+1'] if `item'==`i2num'
                        qui count if `rep'==1&`item'==`i2num'&`score1'!=`=`nbitemssel'+1'&`score1'!=0&`score2'!=`nbitemssel'
                        local nb1=r(N)
                        qui count if `item'==`i2num'&`score1'!=`=`nbitemssel'+1'&`score1'!=0&`score2'!=`nbitemssel'
                        local nb2=r(N)

                        qui replace `betadim2'= log(`nb1'/(`nb2'-`nb1')) if `item'==`i2num'
                        qui xi:logit `rep' i.`score1' if `score1'!=`=`nbitemssel'+1'&`score1'!=0&`score2'!=`nbitemssel', nocons offset(`betadim1') iter(100)
                        local N1=e(N)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+3']=e(ll)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+4']=2*(-e(ll)+2*(`nbitemssel'+1)-1)

                        tempvar  newscore2
                        qui gen `newscore2'=`score2'
                        qui replace `newscore2'=99 if `item'==`i2num'
                        qui xi:logit `rep' i.`newscore2' if `score1'!=`=`nbitemssel'+1'&`score1'!=0&`score2'!=`nbitemssel',  offset(`betadim2') iter(100)
                        local N2=e(N)
                        local ll=e(ll)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+5']=e(ll)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+6']=2*(-e(ll)+2*(`nbitemssel'+1)-2)
            }
            else {
                        qui count
                        local N=r(N)
                        di in green _col(10)"The program is estimating the parameters of the models (N=" in yellow "`N'" in green ")"
                        qui raschtest `listitemssel' `i2' , mml notest
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+3']=e(ll)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+4']=2*(-e(ll)+`nbitemssel'+2)

                        local nb1:word count `listitemssel'
                        qui mmsrm `listitemssel' `i2' , part(`nb1' 1)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+5']=e(ll)
                        matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+6']=2*(-e(ll)+`nbitemssel'+4)
            }
            di in green _col(10)"Unidimensional model: " _col(32) "ll: "  in yellow _col(37) %12.4f `result`dim''[`iteration',`=`nbitemstotdim`dim''+3'] _col(52) in green "AIC: " in yellow _col(57) %12.4f `result`dim''[`iteration',`=`nbitemstotdim`dim''+4']
            di in green _col(10)"Bidimensional model: " _col(32) "ll: "   in yellow _col(37) %12.4f `result`dim''[`iteration',`=`nbitemstotdim`dim''+5'] _col(52) in green "AIC: " in yellow _col(57) %12.4f `result`dim''[`iteration',`=`nbitemstotdim`dim''+6']
            tokenize `listkernel' `varlist`dim''
            if `result`dim''[`iteration',`=`nbitemstot'+4']<=`result`dim''[`iteration',`=`nbitemstot'+6'] {
                 matrix `result`dim''[`iteration',`i']=1
                 matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+7']=1
                 local nbitemssel=`nbitemssel'+1
                 local nbitemsnosel=`nbitemsnosel'-1
                 local listitemssel `listitemssel' `rep'`i2num'
                 local listitemsselnum `listitemsselnum' `i2num'
                 matrix `affect`dim''[1,`i2num']=1
                 di _col(10) in green "The item " in yellow "``i2num''" in green " is selected in the scale " in yellow `dim'
            }
            else {
                 matrix `result`dim''[`iteration',`=`nbitemstotdim`dim''+7']=2
                 di _col(10) in green "The item "in yellow "``i2num''" in green " is not selected in the scale " in yellow `dim'

            }
     }
     return matrix result`dim' `result`dim''
     local j=`kernel'+1
     forvalues i=`=`kernel'+1'/`nbitemstot' {
            if `affect'[1,`i']==0 {
                 if `affect`dim''[1,`j']==1 {
                        matrix `affect'[1,`i']=`dim'
                 }
            local j=`j'+1
            }
     }
}
use `raschfitfile',clear

return matrix affect `affect'

end
