* This is a test file for validly (see below for the program version)
* Parts are formal validation, and parts are a 'stress test' across varied input.
*
 quietly {
* ________________________________________________
* test file for validly
* currently:
local vers "3.2"
* ________________________________________________
*
capture program drop validly  // ensure newest
vy set_defaults
local shreek 0
local tvers "`r(vers)'"
*
forvalues jjj = 1/2 {
   if `jjj'==2 local nonind nonind

* validate v2.0
* validation do-file for validly v2.5
* 31/3/13
*
   set more off
* The verification strategy is:
*   verify (by inspection) that simple expressions (one operator) 
*         are correctly coded
*   verify a sample of complex expressions against the answer 
*      derivable from repea ted application of the verified 'simples'.
*   do this in both extended and non-extended modes
*
*  The 'sample of complex' is of course arbitrary, but has been chosen
*    to flex as many different coding characteristics as possible
*
*  Make test data
   if `jjj'==1 {
      noisily di as txt _n(8) " {hline}" _n(2) ///
      " UTILITY TO TEST/VALIDATE validly v`vers'" _n(2) 
      if "`tvers'"!="`vers'" {
         noisily di "{err}  NOTE: validly's version number (v`tvers') does not match" _n ///
         "{space 8}this test file (which is designed for v`vers')" _n ///
         "  Proceeding nevertheless" _n(2)
         local shreek 1
      }
      noisily di as txt " Strategy:" _n ///
     "   generate test variables" _n ///
     "   construct and verify dyadic expressions" _n ///
     "   use these to verify more complex operations " _n(2) ///
     " (all estimations, other than the very last, are more-or-less" _n ///
     "  instantaneous" _n(2) ///
     " The tests are first run on a data array of indicator variables (values 1,0,sysmiss,.a,.b)" _n ///
     " then rerun on non-indicator vars, to double-check" _n(2) ///
     " This utility first {err}{bf}clears{sf}{txt} the data array:  do you wish to proceed (y/n)?{txt}  " _r(yy)
     if ("$yy"!="Y") & ("$yy"!="y")  error 4
   }
   else {
      noisily di as txt _n(8) " {hline}" _n(2) ///
      " Now RERUNNING ALL, on the array without constraining the variables to be indicator variables" _n(2)
     local k 99
     while `k'==99 {
        noisily di " Skip (y/n)?"  _r(yy)
        if "$yy"=="Y"|"$yy"=="y" {
           local skip1 1
           continue, break    
        }
        if "$yy"=="N"|"$yy"=="n" {
           local skip1 0
           continue, break    
        }
      }
      if `skip1' continue,break
   }

   clear
   vy set_defaults


* data with extended-missing to ensurethese correctly handled
validly gen_test_vars p q r s t u, extended `nonind'
*  we additionally make two string variables, ps and qs
gen ps = "dog" if p!=0 & !mi(p)
replace ps = "feline" if p==0
gen qs = "dog" if q!=0 & !mi(q)
replace qs = "feline" if q==0
quietly compress
label define tf 1 True 0 False
*
local i " "
if `jjj'==2 local i " nonind"
noisily di as txt " {hline}"_n ///
  " We have used:" _n ///
   `"   {help validly##utility:.validly gen_test_vars p q r s, extended`i'}"' _n ///
   " to make test variables, adding following string variables:" _n ///
   `"   .gen ps = "dog" if p!=0 & !mi(p) "' _n ///
   `"   .replace ps = "feline" if p==0"' _n ///
   `"   .gen qs = "dog" if q!=0 & !mi(q)"' _n ///
   `"   .replace qs = "feline" if q==0"' _n

local first 1
if `jjj'==2 local first True
noisily  di as txt " {hline}" _n ///
  " Having generated the basic test dataset" _n ///
  " with all  possible patterns of values:{bf} `first' 0 . .a .b{sf}" _n 
if `jjj'==2 noisily di " ('True' values are random integers, range 1-100)"
noisily  di " across variables {bf}p q r s t u{sf}" _n(2) ///
  " we first, by visual inspection, check that all" _n ///
  " single logical/relational operators " _n ///
  " give the desired result." _n(2) ///
  " then we do formal checks" _n(2) ///
  " These validated simples (so note var names) are then used" _n ///
  " to validate more complex expressions " _n ///
  " {hline}" _n ///
   " press Enter to continue:" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
* For each non-extended initial command we display one extended-missing 
* to ensure that there are no misspecified p=. rather than p>. errors
foreach x in default extended {
local y ""
local t ""
local lif "if p<.b & q <.b"
if "`x'"=="extended" {
   local t ", e"
   local y "_e"
   local lif ""
   vy set_extended on
}
validly gen np`y':tf = !p
noisily di " {hline}"
noisily list p np`y' in 1/4
noisily di "     .validly generate np`y' = !p`t'"
noisily di " in `x' mode;  (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
validly gen paq`y':tf = p&q
noisily di " {hline}"
noisily list p q paq`y' `lif' in 1/25
noisily di "     .validly generate paq`y' = p&q`t'"
noisily di " in `x' mode;  (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
validly gen pvq`y':tf = p|q
noisily di " {hline}"
noisily list p q pvq`y' `lif' in 1/25
noisily di "     .validly generate pvq`y' = p|q`t'"
noisily di " in `x' mode;  (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
validly gen pGTq`y':tf = p>q
noisily di " {hline}"
noisily list p q pGTq`y' `lif' in 1/25
noisily di "     .validly generate pGTq`y' = p>q`t'"
noisily di " in `x' mode;  (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
validly gen psysm`y':tf = p==.
validly gen pexmb`y':tf = p==.b
validly gen exmbp`y':tf = .b==p
noisily di " {hline}"
noisily list p  psysm`y' pexmb`y' exmbp`y'  in 1/5
noisily di "     .validly generate psysm`y' = p==. `t'"
noisily di "     .validly generate pexmb`y' = p==.b`t'"
noisily di "     .validly generate exmbp`y' = .b==p`t'"
noisily di " in `x' mode;  (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
capture drop ys1
capture drop ys2
capture drop ys3  // same in either e/not, but check
validly gen ys1:tf = ps==qs
validly gen ys2:tf = ps==""
validly gen ys3:tf = ""==qs
noisily di " {hline}"
noisily list ps qs ys1 ys2 ys3 in 1/25 if p<=. & q<=.
noisily di `"     .validly generate ys1 = ps==qs `t'"'
noisily di `"     .validly generate ys2 = ps=="" `t'"'
noisily di `"     .validly generate ys3 = ""==qs `t'"'
noisily di " string operations;  (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
* If we rerun these in **replace* mode there should be zero changes throughout
noisily di as txt " {hline}" _n ///
 " We now rerun these using {bf}validly replace{sf}" _n ///
 " Checking that there are (as there should be) {bf}ZERO{sf} changes" _n ///
 " (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
capture drop z
gen z = np`y'
validly np`y' = !p
assert z== np`y'
capture drop z
gen z = paq`y'
validly paq`y' = p&q
assert z== paq`y'
capture drop z
gen z = pvq`y'
validly pvq`y' = p|q
assert z== pvq`y'
capture drop z
gen z = pGTq`y'
validly pGTq`y' = p>q
assert z== pGTq`y'
capture drop z
gen z = psysm`y'
validly psysm`y' = p==.
assert z== psysm`y'
capture drop z
}
capture drop Spaq_e
capture drop Spaq
generate Spaq_e = p&q if !mi(p,q)
replace  Spaq_e = p if mi(p)& (!mi(q) | (p==q))
replace  Spaq_e = q if mi(q)& (!mi(p) | (p==q))
replace  Spaq_e = 0 if (p==0 & mi(q))|(q==0 & mi(p))
generate Spaq = Spaq_e
replace  Spaq =. if mi(Spaq)
assert paq_e == Spaq_e
assert paq == Spaq
noisily di as txt " {hline}" _n ///
 " We next do formal tests on these same variables" _n ///
 " comparing them to correct versions, named Sxxx," _n ///
 " constructed using raw Stata" _n(2) ///
 " Handcrafted Stata p {bf}and{sf} q:" _n ///
 "   .generate Spaq_e = p&q if !mi(p,q)" _n ///
 "   .replace  Spaq_e = p if mi(p)& (!mi(q) | (p==q))" _n ///
 "   .replace  Spaq_e = q if mi(q)& (!mi(p) | (p==q))" _n ///
 "   .replace  Spaq_e = 0 if (p==0 & mi(q))|(q==0 & mi(p))" _n ///
 "   .generate Spaq = Spaq_e" _n ///
 "   .replace  Spaq =. if mi(Spaq)" _n ///
 "   {bf}.assert paq_e == Spaq_e" _n ///
 "   .assert paq == Spaq{sf} " _n(2) ///
 " These {bf}asserts{sf} for p&q accepted. (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
capture drop v1
validly v1 = !(!(!p))
capture drop v2
validly v2 = !p  
assert v1==v2 
if `jjj'==1 { // !!p works for indicator only
   capture drop v1
   validly v1 = (!(!p))
   assert v1==p 
}
capture drop v1
validly v1 = p|q
capture drop v2
validly v2 = !(!p&!q)
assert v1==v2 
capture drop Spvq_e
capture drop Spvq
generate Spvq_e = p|q if !mi(p,q)
replace  Spvq_e = p if mi(p)& (!mi(q) | (p==q))
replace  Spvq_e = q if mi(q)& (!mi(p) | (p==q))
replace  Spvq_e = 1 if ((p&!mi(p)) & mi(q))|((q&!mi(q)) & mi(p))
generate Spvq = Spvq_e
replace  Spvq =. if mi(Spvq)
assert pvq_e == Spvq_e
assert pvq == Spvq
noisily di as txt " {hline}" _n ///
 " Handcrafted Stata p {bf}or{sf} q:" _n ///
 "   .generate Spvq_e = p|q if !mi(p,q)" _n ///
 "   .replace  Spvq_e = p if mi(p)& (!mi(q) | (p==q))" _n ///
 "   .replace  Spvq_e = q if mi(q)& (!mi(p) | (p==q))" _n ///
 "   .replace  Spvq_e = 1 if ((p&!mi(p)) & mi(q))|((q&!mi(q)) & mi(p))" _n ///
 "   .generate Spvq = Spvq_e" _n ///
 "   .replace  Spvq =. if mi(Spvq)" _n ///
 "   .assert pvq_e == Spvq_e" _n ///
 "   .assert pvq == Spvq" _n(2) ///
 " These {bf}asserts{sf} for p|q accepted. (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
capture drop Snp_e
capture drop Snp
generate Snp_e = !p if !mi(p)
replace  Snp_e = p if mi(p)
generate Snp = Snp_e
replace  Snp =. if mi(Snp)
assert np_e == Snp_e
assert np == Snp
noisily di as txt " {hline}" _n ///
 " Handcrafted Stata !p " _n ///
 "   .generate Snp_e = !p if !mi(p)" _n ///
 "   .replace  Snp_e = p if mi(p)" _n ///
 "   .generate Snp = Snp_e" _n ///
 "   .replace  Snp =. if mi(Snp)" _n ///
 "   .assert np_e == Snp_e" _n ///
 "   .assert np == Snp" _n(2) ///
 " These {bf}asserts{sf} for !p accepted. (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
capture drop SpGTq_e
capture drop SpGTq
generate SpGTq_e = p>q if !mi(p,q)
replace  SpGTq_e = p if mi(p)& (!mi(q) | (p==q))
replace  SpGTq_e = q if mi(q)& (!mi(p) | (p==q))
generate SpGTq = SpGTq_e
replace  SpGTq =. if mi(SpGTq)
assert pGTq_e == SpGTq_e
assert pGTq == SpGTq
noisily di as txt " {hline}" _n ///
 " Handcrafted Stata p > q" _n ///
 "   .generate SpGTq_e = p>q if !mi(p,q)" _n ///
 "   .replace  SpGTq_e = p if mi(p)& (!mi(q) | (p==q))" _n ///
 "   .replace  SpGTq_e = q if mi(q)& (!mi(p) | (p==q))" _n ///
 "   .generate SpGTq = SpGTq_e" _n ///
 "   .replace  SpGTq =. if mi(SpGTq)" _n ///
 "   .assert pGTq_e == SpGTq_e" _n ///
 "   .assert pGTq == SpGTq" _n(2) ///
 " These {bf}asserts{sf} for p > q accepted. (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
validly set_extended off
*
* OK, assume simples work
* compare
*   direct assessment of complex expression with
*   assessment built up through repeated deployment of validated simples
* using these validated definitions to validate conditionals
* p equivalents
*quietly { 
noisily di as txt _n(3) " {hline}" _n " Using these validated expressions as comparator variables," _n ///
 _col(4) "now assessing the operators within conditionals;"  _n ///
 _col(4) "Note: [to ease programming] {bf}pt{sf} below is simply {bf}p{sf} " _n ///
 _col(4) "truncated to 0,1,sysmiss, {bf}pt_e{sf} is simply full {bf}p{sf} " _n(2) ///
  _col(4) "for each operator" _n ///
_col(4) "looking at the conditional across the twelve possible states of the " _n ///
 _col(4) "options:  wide  ifnot(.z) else(.y)  extended" _n(2) ///
 _col(4) "For example, for one state of the operator '&' we have, :" _n ///
 _col(4) "using the validated comparator paq_e (see above), the test:" _n ///
 _col(11) "validly generate v = 7 if p&q,  ifn(.z) e" _n ///
 _col(11) "assert (paq_e==1)==(v==7)" _n ///
 _col(11) "assert (paq_e==0)==(v==.z)" _n ///
 _col(11) "assert (paq_e==.)==(v==.)" _n ///
 _col(11) "assert (paq_e==.a)==(v==.a)" _n ///
 _col(11) "assert (paq_e==.b)==(v==.b)" _n ///
 _col(11) "assert (v==7)|(v==.z)|(v==.)|(v==.a)|(v==.b)" _n ///
 _col(14) "(see the do-file for full details of the other tests)" _n " {hline}"
noisily di as txt " (press Enter to continue)" _r(yy)
*
validly set_extended off
capture drop pt
capture drop pt_e
capture drop pexmb
gen pexmb = p==.b if p!=. //nuanced definition of ==.x
capture drop pexmb_e
gen pexmb_e = pexmb
gen pt_e = p
gen pt = p if !mi(p)
local act "pt np paq pvq pGTq psysm pexmb pexmb"
local i 0
*
foreach p in p !p p&q p|q p>q p==. p==.b .b==p {
*
local ++i
local x = word("`act'",`i')
*
noisily di  " {txt}{hline}" _n " Assessing:  {bf}validly v = 7 if `p' {sf}" _n ///
     "   across the twelve valid  combinations of options {bf} w  ifn(.z) else(.y) e {sf}" _n ///
     "   with validated comparator: {bf}`x'{sf} (default mode),  {bf}`x'_e{sf} (extended mode)"
capture drop v
validly v=7 if `p', w ifn(.z)
assert (`x'!=0)==(v==7)
assert (`x'==0)==(v==.z)
assert (v==7)|(v==.z)
drop v
validly v=7 if `p', w 
assert (`x'!=0)==(v==7)
assert (`x'==0)==(v==.)
assert (v==7)|(v==.)
drop v
validly v=7 if `p',  ifn(.z)
assert (`x'>=1&`x'<.)==(v==7)
assert (`x'==0)==(v==.z)
assert (`x'==.)==(v==.)
assert (v==7)|(v==.z)|(v==.)
capture drop v
validly v = 7 if `p'
assert (`x'>=1&`x'<.)==(v==7)
assert (`x'==0|`x'>=.)==(v==.)
assert (v==7)|(v==.)
* and now do same in extended mode
drop v
validly v=7 if `p', w ifn(.z) e
assert (`x'_e!=0)==(v==7)
assert (`x'_e==0)==(v==.z)
assert (v==7)|(v==.z)
drop v
validly v=7 if `p', w e
assert (`x'_e!=0)==(v==7)
assert (`x'_e==0)==(v==.)
assert (v==7)|(v==.)
drop v
validly v=7 if `p',  ifn(.z) e
assert (`x'_e>=1&`x'_e<.)==(v==7)
assert (`x'_e==0)==(v==.z)
assert (`x'_e==.)==(v==.)
assert (`x'_e==.a)==(v==.a)
assert (`x'_e==.b)==(v==.b)
assert (v==7)|(v==.z)|(v==.)|(v==.a)|(v==.b)
drop v
validly v = 7 if `p', e
assert (`x'_e>=1&`x'_e<.)==(v==7)
assert ((`x'_e==0)|(`x'_e==.))==(v==.)
assert (`x'_e==.a)==(v==.a)
assert (`x'_e==.b)==(v==.b)
assert (v==7)|(v==.)|(v==.a)|(v==.b)
* and now repeat for the acceptable with else
drop v
validly v=7 if `p',  ifn(.z)  else(.y)
assert (`x'>=1&`x'<.)==(v==7)
assert (`x'==0)==(v==.z)
assert (`x'==.)==(v==.y)
assert (v==7)|(v==.z)|(v==.y)
capture drop v
validly v = 7 if `p',  else(.y)
assert (`x'>=1&`x'<.)==(v==7)
assert (`x'==0|`x'>=.)==(v==.y)
assert (v==7)|(v==.y)
* and now do same in extended mode
drop v
validly v=7 if `p',  ifn(.z) e  else(.y)
assert (`x'>=1&`x'<.)==(v==7)
assert (`x'==0)==(v==.z)
assert (`x'==.)==(v==.y)
assert (v==7)|(v==.z)|(v==.y)
drop v
validly v = 7 if `p', e  else(.y)
assert (`x'>=1&`x'<.)==(v==7)
assert (`x'==0|`x'>=.)==(v==.y)
assert (v==7)|(v==.y)
}
* simple minded test of sousrce
* first generate actual vars corresponding to the 'as if' vars
capture drop pa
generate pa = p
replace pa = .a if mi(p)
capture drop qb
generate qb = q
replace qb = .b if mi(q)
capture drop rc
generate rc = r
replace rc = .c if mi(r)
capture drop sd
generate sd = s
replace sd = .d if mi(s)
*
capture drop vr
validly gen vr = qb&pa, e //we know from above this works
capture drop v
validly gen v = p&q, source //to test
assert v==vr
*
*
noisily di as txt _n(2) " {hline}" _n ///
 " Now checking that option {bf}source{sf} works as intended" _n ///
 " we first generate actual vars pa, qb etc" _n /// 
 " with their extended-missing values" _n /// 
 " corresponding to the notional vars enacted by source" _n(2) ///
 " Then compare." _n ///
 "   .validly gen vr = qb&pa, e //we know from above this works" _n ///
 "   .validly gen v = p&q, source //to test" _n ///
 "   .validly gen vr2 = vr if rc|sd, e //we now know that to work" _n ///
 "   .validly gen v2 = p&q if r|s, source //to test" _n ///
 "   .assert v==vr" _n ///
 "   .assert v==vr2" _n(2) ///
 " These {bf}asserts{sf} for source accepted. (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
* no easy way to "verify" source's random splitting option
* but we can do soome visual checking in bivariate mode
noisily di as txt _n(2) " {hline}" _n ///
 " No easy way to exactly verify the random splitting in " _n ///
 " source(r).  But we can do some checking " _n ///
 " in bivariate mode running" _n ///
 "   .validly vr = pa R qb, e" _n ///
 "   .validly v = p R q, source(r)" _n ///
 " for the relations & |  >=" _n(2) ///
 " Splitting should only happen when vr is sysmis" _n ///
 "   .assert vr==v if vr!=." _n(2) ///
 " Also  by visually inspecting the tab of v by vr when v!=vr" _n ///
 " source(r) should split these {ul:roughly} equally" _n(2) ///
 " (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
foreach r in & | >= {
capture drop vr
validly vr = pa`r'qb, e
capture drop v
validly v = p`r'q, source(r)
* disagreements between v and vr should only be when vr gives sysmis
* and v should have spli these roughly equally
assert vr==v if vr!=.
noisily di as txt "{hline 11}{c RT} for `r' when v!=vr {c LT}{hline}"
noisily tab vr v if v!=vr, m
}
capture drop v
capture drop vr
validly vr = !p, source
validly v = !p, source(r)
assert v==vr
noisily di as txt _n(2) " {hline}" _n ///
" (We have also checked that source and source(r) give identical" _n ///
"  results when making !p)" _n(2) ///
 " (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
*
noisily di as txt _n(2) " {hline}" _n ///
" There is no way to exhaustively check the RPN-parser. " _n(2) ///
" But we can try throwing at it disparate but logically equivalent renditions, and " _n ///
" asserting equivalence.  Thus for example: " _n(2) ///
" .validly gen y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s))) " _n ///
" .validly gen y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)) " _n ///
" .assert y1==y2" _n(2) ///
" or " _n(2) ///
" .validly gen y1 = (p&q)|r if s|(t&u) , ifnot((q>r)|t) else(p|!u) e" _n ///
" .validly gen y2 = !((!p|!q)&!r) if !((!u|!t)&!s) , ifnot(!(!t&(q<=r))) else(!(!p&u)) e"  _n ///
" .assert y1==y2" _n(2) ///
" or, checking source"  _n(2) ///
" .validly gen y1 = (p&q)|(r>u) if s|(t&u) , ifnot(!q|t) else(p|!u) source"  _n ///
" .validly gen y2 = !((!p|!q)&(r<=u)) if !((!u|!t)&!s) , ifnot(!(!t&q)) else(!(!p&u)) s"  _n ///
" .replace y2 = .x if y2==.e //since assigned in different sequence"  _n ///
" .replace y2 = .e if y2==.f"  _n ///
" .replace y2 = .f if y2==.x"  _n ///
" .assert y1==y2"_n(2) ///
 " (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert y1==y2
capture drop y1
validly y1 = (p&q)|r if s|(t&u) , ifnot((q>r)|t) else(p|!u) e
capture drop y2
validly y2 = !((!p|!q)&!r) if !((!u|!t)&!s) , ifnot(!(!t&(q<=r))) else(!(!p&u)) e
assert y1==y2
capture drop y1
validly y1 = (p&q)|(r>u) if s|(t&u) , ifnot(!q|t) else(p|!u) source
capture drop y2
validly y2 = !((!p|!q)&(r<=u)) if !((!u|!t)&!s) , ifnot(!(!t&q)) else(!(!p&u)) s
assert y1==y2
*
noisily di as txt _n(2) " {hline}" _n ///
" Now taking the first pair of expressions above andusing them" _n ///
" to test the 'optimisation' to plain relations for missing data" _n ///
" by running the equivalence test on the data-set reduced sequentially to non-missing" _n(2) ///
" sequentially from each end of the array, and also a couple of random sequences" _n ///
" and checking that with no missing, Stata and validly agree." _n(2) ///
" This section also exercises various optimisations found in 'source' mode," _n ///
" and checks that they work as expected when paired with non-missing variables." _n(2) ///
 " (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
noisily di " Wait {c 133}{c 133}"
foreach vv in p q r s t u { // from front
   replace `vv' = 7 if `vv'>=.
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert y1==y2
}
* and with no missingthey should match plain
capture drop sy1
gen sy1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop sy2
gen sy2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert sy1==sy2
assert sy1==y1
clear
validly gen_test_vars u t s r q p, extended `nonind'
foreach vv in p q r s t u {
   replace `vv' = 7 if `vv'>=.
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert y1==y2
}
clear
validly gen_test_vars p q r s t u, extended `nonind'
foreach vv in p q r s t u { // from back
   replace `vv' = 7 if `vv'>=.
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert y1==y2
}
clear
validly gen_test_vars p q r s t u, extended `nonind'
foreach vv in p u t s r q { // arbitrary 1
   replace `vv' = 7 if `vv'>=.
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert y1==y2
}
clear
validly gen_test_vars p q r s t u, extended `nonind'
foreach vv in q r s t u p { // arbitrary 2
   replace `vv' = 7 if `vv'>=.
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
assert y1==y2
}

* section to test v3.2 introduced optimisation on sourced variables
clear
vy gen_test_vars p q  r s  t u, e
local sc 97
foreach i in p q r s t u {
   local val = char(`sc')
   local val .`val'
   replace `i' =  `val' if mi(`i')
   local ++sc
}
capture drop a
capture drop aa
validly gen aa = !((p&q)|(r&s))>(!t&!u), e d 
validly gen a = !((p&q)|(r&s))>(!t&!u), s d 
assert a==aa
capture drop as
validly gen as = !((p&q)|(r&s))>(!t&!u),  d 
replace a = . if mi(a)
assert as==a
local sc 97
foreach i in p  r  t  {
   local val = char(`sc')
   local val .`val'
   replace `i' =  `val' if mi(`i')
   local ++sc
}
foreach i in q s u  {
   replace `i' =  7 if mi(`i') //nonindicator
}

capture drop a
capture drop aa
validly gen aa = !((p&q)|(r&s))>(!t&!u), e d 
validly gen a = !((p&q)|(r&s))>(!t&!u), s d 
assert a==aa
capture drop as
validly gen as = !((p&q)|(r&s))>(!t&!u),  d 
replace a = . if mi(a)
assert as==a
foreach i in q s u  {
   replace `i' =  1 if `i'==7 // indicator
}
capture drop a
capture drop aa
validly gen aa = !((p&q)|(r&s))>(!t&!u), e d 
validly gen a = !((p&q)|(r&s))>(!t&!u), s d 
assert a==aa
*
clear
vy gen_test_vars p q  r s  t u, e
local sc 97
foreach i in s t u  {
   local val = char(`sc')
   local val .`val'
   replace `i' =  `val' if mi(`i')
   local ++sc
}
foreach i in p q r  {
   replace `i' =  7 if mi(`i') //nonindicator
}
capture drop a
capture drop aa
validly gen aa = !((p&q)|(r&s))>(!t&!u), e d 
validly gen a = !((p&q)|(r&s))>(!t&!u), s d 
assert a==aa
capture drop as
validly gen as = !((p&q)|(r&s))>(!t&!u),  d 
replace a = . if mi(a)
assert as==a
*
* further, since the coding for one-no-miss is idiosyncratic, we should give it a workout
clear
vy gen_test_vars p q r s t, e n
foreach i in p q {
   vy `i'n = `i'
   vy `i'n = 1 if mi(`i')
}
foreach j in | & > {
foreach j2 in e s " " {
local jn  & 
if "`j'"=="&" local jn "|"   
capture drop a
capture drop b
di "vy a = pn`j'q, `j2'"
vy a = pn`j'q, `j2'
if "`j'"!=">" vy b  = !(!pn `jn' !q), `j2'
else vy b = q < pn, `j2'
assert a==b
}
}
* abd checking that ifnot and else behave under these circumstances
foreach p in p pn {
foreach q in q qn {
foreach ii in " " "ifnot(`p'&`q') else(`p'&`q')" {
local jj " "
if "`ii'"!=" " local jj "if t"
capture dro`p' a
capture dro`p' a2
vy a = (`p'&`q') `jj', `ii' when(r&s) 
vy a2 = (`p'&`q') if r&s 
assert a==a2
vy a = !(!`p'|!`q') `jj', when( !r|!s) `ii' 
vy a2 = !(!`p'|!`q') if !r|!s
assert a==a2
capture dro`p' ras
vy ras = r&s
vy a = !(!(`p'&`q')) `jj', `ii' when(mi(ras)) 
vy a2 = !(!(`p'&`q')) if mi(ras)
assert a==a2
capture dro`p' b
vy b = `p'&`q'
assert a==b
}
}
}

clear
validly gen_test_vars p q r s t u, extended `nonind'
*
*
noisily di as txt _n(2) " {hline}" _n ///
" Now taking the first pair of expressions above and testing them" _n ///
" when positioned within the condition, and {bf}ifnot{sf}, and {bf}else{sf}" _n(2) ///
" and verifying them against the expression cumulatively constructed" _n ///
" from (verifiied) simple calls to validly with dyadic relations." _n(2) ///
" Also checking that {sf}validly global{sf} parses equivalently." _n(2) ///
" (See  do file for details)" _n ///
 " (press Enter to continue)" _r(yy)
if "$yy"!="Y"&"$yy"!="y"&"$yy"!="" exit
noisily di " Wait {c 133}{c 133}"
* build from simples
capture drop paq
validly paq = p&q
capture drop ras
validly ras = r&s
capture drop paqvras
validly paqvras = paq | ras
capture drop npaqvras
validly npaqvras = ! paqvras
capture drop nt
validly nt = ! t
capture drop nu
validly nu = ! u
capture drop ntanu
validly ntanu = nt & nu
capture drop pvr
validly pvr = p|r
capture drop pvrvs
validly pvrvs = pvr|s
capture drop part2
validly part2 = ntanu < pvrvs
capture drop part1
validly part1 = npaqvras > part2
capture drop ref
validly ref  = ! part1
capture drop y1
validly y1 = !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
assert ref== y1
*
capture drop y2
validly y2 = ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)) //now known correct
validly global m1 !(!((p&q)|(r&s))>((!t&!u)<(p|r|s)))
validly global m2 ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
capture drop y1
gen y1 = $m1
assert y1==y2
capture drop y2
gen y2 = $m2
assert y1==y2
capture drop y1 y2
gen y1 = 7 if $m1 == 1
validly y2 = 7 if ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
capture drop y3
validly y3 = 7 if ref
assert y1 == y2
assert y1 == y3
capture drop y1 y2 y3
validly y1 = 7 if p, ifnot(!(!((p&q)|(r&s))>((!t&!u)<(p|r|s))))
validly y2 = 7 if p, ifnot(((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)))
validly y3 = 7 if p, ifnot(ref)
assert y1==y2
assert y1==y3
capture drop y1 y2 y3
validly y1 = 7 if p, else(!(!((p&q)|(r&s))>((!t&!u)<(p|r|s))))
validly y2 = 7 if p, else(((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)))
validly y3 = 7 if p, else(ref)
assert y1==y2
assert y1==y3
capture drop y1 y2 y3
validly y1 = 7 if p, ifnot(8) else(!(!((p&q)|(r&s))>((!t&!u)<(p|r|s))))
validly y2 = 7 if p, ifnot(8) else(((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)))
validly y3 = 7 if p, ifnot(8) else(ref)
assert y1==y2
assert y1==y3
capture drop y1 y2 y3
validly y1 = 7 if p, ifnot(8) when(!(!((p&q)|(r&s))>((!t&!u)<(p|r|s))))
validly y2 = 7 if p, ifnot(8) when(((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)))
validly y3 = 7 if p, ifnot(8) when(ref)
assert y1==y2
assert y1==y3
capture drop y2 y3
gen y2 = 7 if (p>=1 & p<.) & $m1==1
replace y2 = 8 if p==0 & $m1==1
assert y1==y2
capture drop y1 y2 y3
validly y1 = 7 if p, ifnot(8) else(9) when(!(!((p&q)|(r&s))>((!t&!u)<(p|r|s))))
validly y2 = 7 if p, ifnot(8) else(9) when(((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p)))
validly y3 = 7 if p, ifnot(8) else(9) when(ref)
assert y1==y2
assert y1==y3
capture drop y2 y3
gen y2 = 7 if (p>=1 & p<.) & $m1==1
replace y2 = 8 if p==0 & $m1==1
replace y2 = 9 if mi(p) & $m1==1
assert y1==y2
macro drop m1
macro drop m2
*
* use this to check on wrapper behaviour
capture drop rn
gen rn = r
replace rn = 5 if mi(r)  //so no mi in reg itself
replace rn=20 if ref!=1 //so that range matters to reg
capture drop sn
gen sn = s
replace sn = 5 if mi(s)
replace sn=20 if ref!=1
validly reg rn sn if ((!s|!r)&(!q|!p))<=!(!(u|t)>=!(!s&!r&!p))
capture drop preg
validly predict preg if !(!((p&q)|(r&s))>((!t&!u)<(p|r|s))) //the other form
reg rn sn if ref==1
capture drop preg2
predict preg2 if ref==1
assert preg==preg2
*
*
validly global paq p&q
validly global pvq p|q
capture drop y1
gen y1 = cond(r,$paq,$pvq,.)
capture drop y2
validly gen y2 = p&q if r, ifnot(p|q)
assert y1==y2
*
* a simplified version of further
* since the full version (across all combinations)
* takes forever (i.e. >10hrs on FAST machine)
noisily di as txt _n(2) " {hline}" _n ///
" The next two parts take a 'try everything' approach" _n(2) ///
" PART 1 Looking at:"  _n(2) ///
 " validly v = pRq if rRs, ifnot(tRu) else(rRt) when(qRu) e"  _n(2) ///
" varying by the possible combinations of ifnot/else/when"  _n ///
" and within that allowing each R to"  _n ///
" range over & | >=, so 3^5  permutations" _n ///
" each explicitly checked against known validated expressions"  _n ///
" (see the do file for details)"  _n(2) ///
" PART 2 A slightly different test is to take some arbitrary complex expression" _n ///
" ((p R1 q) R2 !(!r R3 s)) R4 !t" _n ///
" and try all possible permutations acrosss R1,R2,R3,R4 of & | > >= < <= == !=" _n ///
" in each case validating against a sequential pairwise construct version;" _n ///
" using the expression in each of five possible spaces of " _n ///
" (andd in extended and non-extended mode)" _n ///
" v = exp if exp, ifnot(exp) else(exp) when(exp)" _n(2) ///
" Scanning across these permutations takes some time " _n ///
`" ("some time" == leisurely lunchtime-ish ? -- depends on your machine)"' _n ///
" so you can, if you wish, skip for now. "
local k 99
while `k'==99 {
  noisily di " Skip (y/n)?"  _r(yy)
  if "$yy"=="Y"|"$yy"=="y" {
     local skip 1
     continue, break    
  }
  if "$yy"=="N"|"$yy"=="n" {
     local skip 0
     continue, break    
  }
}
if !`skip' {
*
local v1 p
local v2 q
local v3 r
local v4 s
local v5 t
local v6 u
local v7 r
local v8 t
*
noisily di " {txt}PART 1" _n ///
" Patience, until the dots reach{hline 47}here." _n " ."_c 
foreach r1 in & | >= {
foreach r2 in & | >= {
foreach r3 in & | >= {
foreach r4 in & | >= {
noisily di "." _c
foreach r5 in & | >= {
*
local s `v1'`r1'`v2'
local p `v3'`r2'`v4'
local q `v5'`r3'`v6'
local r `v7'`r4'`v8'
local t `v2'`r5'`v6'
*
foreach x in p q r s t { // ie across expressions, not vars
local v`x' "``x''"
if strpos("``x''","&")!=0 local v`x' = subinstr("``x''","&","a",1)
if strpos("``x''","|")!=0 local v`x' = subinstr("``x''","|","v",1)
if strpos("``x''",">=")!=0 local v`x' = subinstr("``x''",">=","GE",1)
capture drop `v`x''_e
quietly validly `v`x''_e = ``x'' , e
}
*
capture drop v
validly v = `s' if `p', ifnot(`q') e
assert (v==`vs'_e) if (`vp'_e==1)
assert (v==`vq'_e) if (`vp'_e==0)
assert (v==.) if (`vp'_e==.)
assert  (v==.a) if (`vp'_e==.a) & ((`vs'_e==.a)|(`vs'_e<.))
assert  (v==.b) if (`vp'_e==.b) & ((`vs'_e==.b)|(`vs'_e<.))
capture drop v
validly v = `s' if `p', ifnot(`q') else(`r') e
assert (v==`vs'_e) if (`vp'_e==1)
assert (v==`vq'_e) if (`vp'_e==0)
assert (v==`vr'_e) if ((`vp'_e!=1)&(`vp'_e!=0))
capture drop v
validly v = `s' if `p', else(`r') e
assert (v==`vs'_e) if (`vp'_e==1)
assert (v==`vr'_e) if (`vp'_e!=1)
*
capture drop v
validly v = `s' if `p', ifnot(`q') when(`t') e
assert (v==`vs'_e) if (`vp'_e==1)&(`vt'_e==1)
assert (v==`vq'_e) if (`vp'_e==0)&(`vt'_e==1)
assert (v==.) if ((`vp'_e==.)&(`vt'_e==1))|(`vt'_e!=1)
assert  (v==.a) if (`vp'_e==.a) & ((`vs'_e==.a)|(`vs'_e<.))&(`vt'_e==1)
assert  (v==.b) if (`vp'_e==.b) & ((`vs'_e==.b)|(`vs'_e<.))&(`vt'_e==1)
capture drop v
gen v = 3
validly v = `s' if `p', ifnot(`q') when(`t') e // no e-m passthru from p
assert (v==`vs'_e) if (`vp'_e==1)&(`vt'_e==1)
assert (v==`vq'_e) if (`vp'_e==0)&(`vt'_e==1)
assert (v==3) if (`vp'_e>=.)|(`vt'_e!=1)
}
}
}
}
}

clear
validly gen_test_vars p q r s t, e `nonind' // can slightly shrink data
local list "& | > >= < <= == !="
noisily di _n " PART 2 (without extended)" _n ///
 " Patience, until the dots reach{hline 30}here." _n " ."_c 
validly set_extended off
foreach r1 of local list {
foreach r2 of local list {
noisily di "." _c
foreach r3 of local list {
foreach r4 of local list {
*
capture drop v
capture drop v1
capture drop v2
capture drop v3
capture drop v4
*
capture drop v
validly v = ((p`r1'!q)`r2'!(!r`r3's))`r4'!t
capture drop vt
validly vt = !q
capture drop v1
validly v1 = (p`r1'vt)
capture drop vt
validly vt = !r
capture drop v2
capture drop v3
validly v2 = (vt`r3's)
validly v2 = !v2
validly v3 = v1`r2'v2
capture drop vt
validly vt = !t
capture drop v4
validly v4 = v3`r4'vt
assert v4==v
capture drop v
capture drop v1
validly v = 7 if v4
validly v1 = 7 if ((p`r1'!q)`r2'!(!r`r3's))`r4'!t
assert v==v1
capture drop v
capture drop v1
validly v = 7 if p, ifn(v4)
validly v1 = 7 if p, ifnot(((p`r1'!q)`r2'!(!r`r3's))`r4'!t)
assert v==v1
capture drop v
capture drop v1
validly v = 7 if p, ifn(3) when(v4)
validly v1 = 7 if p, ifn(3) when(((p`r1'!q)`r2'!(!r`r3's))`r4'!t)
assert v==v1
*
}
}
}
}
*
clear
validly gen_test_vars p q r s , e `nonind' // can further shrink data
noisily di _n " PART 2 (with extended)" _n ///
 " Patience, until the dots reach{hline 30}here." _n " ."_c 

validly set_extended on
local list "& | > >= < <= == !="
foreach r1 of local list {
foreach r2 of local list {
noisily di "." _c
foreach r3 of local list {
capture drop v
capture drop v1
capture drop v2
capture drop v3
capture drop v4
validly v = (p`r1'!q)`r2'!(!r`r3's)
capture drop vt
validly vt = !q
validly v1 = (p`r1'vt)
capture drop vt
validly vt = !r
validly v2 = (vt`r3's)
validly v2 = !v2
validly v4 = v1`r2'v2
assert v4==v
capture drop v
capture drop v1
validly v = 7 if v4
validly v1 = 7 if ((p`r1'!q)`r2'!(!r`r3's))
assert v==v1
capture drop v
capture drop v1
validly v = 7 if p, ifn(v4)
validly v1 = 7 if p, ifnot((p`r1'!q)`r2'!(!r`r3's))
assert v==v1
capture drop v
capture drop v1
validly v = 7 if p, ifn(3) when(v4)
validly v1 = 7 if p, ifn(3) when((p`r1'!q)`r2'!(!r`r3's))
assert v==v1
}
}
}
}
}
noisily di _n(3) "{hline}" _n(2) _col(14) "NO ERRORS DETECTED" 
if `shreek' noisily di " {err}BUT note that since validly's version was v`tvers'" _n ///
     "     whilst this test file was designed to exercise v`vers'" _n ///
     "     there may be slippage;" _n ///
     "     get and use the relevant test file before believing implications.{txt}" _n(2) 
noisily di _n _col(8) "Implication?" _n(2) ///
 _col(8) "Translation from RPN to the cond() functions is robust;" _n ///
_col(8) "Syntax of: if p, ifnot(q) else(r) when(t) is robust;" _n ///
_col(8) "Translations from simple algebraic expressions into RPN is robust;" 
if `skip' {
   noisily di _col(8) "Examples of complex algebraic expressions are handled correctly" 
}
else {
   noisily di _col(8) "An extensive range of examples of complex algebraic expressions is handled correctly" _n ///
   _col(8) "(these examples provide a comprehensive test of program features)."  
}
if `skip'|`skip1' noisily di  _col(8) "(but, when at more leisure, do run the full test [not skipping])" 
noisily di _n(2) _col(8) "These constitute reasonable grounds for continuing to believe that" _n ///
_col(8) "{bf}validly{sf} does what it says: validly handle missing data" _n ///
_col(8) "in logical and relational expressions." _n(2) "{hline}"
vy set_defaults
}