GPArotation/0000755000176200001440000000000014776556662012471 5ustar liggesusersGPArotation/tests/0000755000176200001440000000000014767556440013625 5ustar liggesusersGPArotation/tests/Revelle.R0000644000176200001440000000072214323662112015325 0ustar liggesusers# This tests fix for an error caused by an exact initial setting. # (from William Revelle) require("GPArotation") f3 <- structure(c(0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,0), .Dim = c(6L, 3L), .Dimnames = list(NULL, c("PC1", "PC2", "PC3"))) f3 # PC1 PC2 PC3 #[1,] 0 0 1 #[2,] 0 1 0 #[3,] 1 0 0 #[4,] 0 0 1 #[5,] 0 1 0 #[6,] 1 0 0 # These previously gave object 'VgQt' not found GPForth(f3) Varimax(f3) GPArotation/tests/lp.R0000644000176200001440000001350314763342661014357 0ustar liggesusers Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() #sortFac <- function(x){ # Based on Fungible faSort # vx <- order(colSums(x$loadings^2), decreasing = TRUE) # Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vx] # x$Th <- x$Th %*% Dsgn # x$loadings <- x$loadings %*% Dsgn # if ("Phi" %in% names(x)) { # x$Phi <- diag(1/diag(Dsgn)) %*% x$Phi %*% Dsgn # } # x #} sortFac <- function(x){ # Based on Fungible faSort vx <- order(colSums(x$loadings^2), decreasing = TRUE) Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vx] x$Th <- x$Th %*% Dsgn x$loadings <- x$loadings %*% Dsgn if ("Phi" %in% names(x)) { x$Phi <- diag(1/diag(Dsgn)) %*% x$Phi %*% Dsgn } x } fuzz <- 1e-5 # using eps=1e-5 these tests do not do better than this all.ok <- TRUE ####################################### ####################################### #test 1 L <- rbind(diag(3),diag(3),diag(3),diag(3),diag(3),diag(3),diag(3),diag(3),diag(3),diag(3)) True_rot<-matrix(c(1,0.02079577,0.5024378, 0,0.99978374,0.2635086, 0,0,0.8234801),3,3,byrow=TRUE) L1 <- L%*%t(True_rot) r1 <- lpQ(L1,diag(3),maxit=1000) tst <- t(matrix(c( 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881, 9.990889e-01, 6.082765e-05, 0.0018180081, 5.911473e-05, 9.997556e-01, 0.0008914537, 1.819385e-03, 8.932991e-04, 0.9988458881 ), 3, 30)) if( fuzz < max(abs(r1$loadings - tst ))) { cat("irls: Calculated value is not the same as test value in test test 1. Value:\n") print(r1$loadings, digits=18) cat("difference:\n") print(r1$loadings - tst, digits=18) all.ok <- FALSE } # test 2 set.seed(1001) r2 <- lpQ(L1,p=1,randomStarts=10) tst <- t(matrix(c( 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399, 6.068202e-05, 9.990904e-01, 0.0018150388, 9.997560e-01, 5.852987e-05, 0.0008900377, 8.933085e-04, 1.819065e-03, 0.9988460399 ), 3, 30)) if( fuzz < max(abs(sortFac(r2)$loadings - tst ))) { cat("irls Calculated value is not the same as test value in test 2. Value:\n") print(r2$loadings, digits=18) cat("difference:\n") print(r2$loadings - tst, digits=18) all.ok <- FALSE } #test 3 data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, rotation="none") set.seed(100102) r3 <- lpQ(fa.unrotated$loadings, p=0.75, randomStarts = 50) tst <- t(matrix(c( -0.002173496, 0.792259797, 0.100607850, 0.781465993, 0.002154186, 0.772058937, 0.617379876, 0.138657906, 0.707274693, 0.090566478, 0.822845923, -0.009242103, 0.725228135, 0.002691264 ), 2, 7)) if( fuzz < max(abs(sortFac(r3)$loadings - tst ))) { cat("irls: Calculated value is not the same as test value in test 3. Value:\n") print(r3$loadings, digits=18) cat("difference:\n") print(r3$loadings - tst, digits=18) all.ok <- FALSE } #test 4 data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, rotation="none") set.seed(100102) r4 <- lpT(fa.unrotated$loadings, p=0.75, randomStarts = 50) tst <- t(matrix(c( 0.4231105, 0.669080641, 0.0078944327, 0.5205654, 0.659289508, -0.0008080892, 0.4202373, 0.648462232, -0.0127761636, 0.7171426, 0.100648821, -0.0888999851, 0.7436880, 0.086465344, 0.0308002902, 0.8111649, -0.001129993, -0.0001462372, 0.7373292, -0.000421333, 0.6718224963 ), 3, 7)) if( fuzz < max(abs(sortFac(r4)$loadings - tst ))) { cat("irls: Calculated value is not the same as test value in test 4. Value:\n") print(r4$loadings, digits=18) cat("difference:\n") print(r4$loadings - tst, digits=18) all.ok <- FALSE } if (! all.ok) stop("some tests FAILED") GPArotation/tests/Thurstone.R0000644000176200001440000003353114323662112015726 0ustar liggesusers#Example from: Gradient Projection Algorithms and Software for # Arbitrary Rotation Criteria in Factor Analysis. # by Coen A. Bernaards and Robert I. Jennrich # Website: http://www.stat.ucla.edu/research Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() data("Thurstone", package="GPArotation") if (!exists("box20")) stop("Test data not found. Testing stopped.") fuzz <- 1e-5 all.ok <- TRUE # Thurstone's box problem. (1947, p. 136) # The matrix box20 is the initial loading matrix from Thurstone's box problem. # This takes a lot of iterations to converge at a higher tolerance qbox20 <- quartimax(box20, eps=1e-5) qbox20G <- GPForth(box20, Tmat=diag(1,3), method="quartimax", eps=1e-5) if( fuzz < max(abs(qbox20$loadings - qbox20G$loadings))) { cat("Calculated value is not the same as test value in test Thurstone 1. Value:\n") print(qbox20$loadings - qbox20G$loadings, digits=18) cat("difference:\n") print(qbox20$loadings - tst, digits=18) all.ok <- FALSE } #qbox20$Th - qbox20G$Th # These values compare with those in: # http://www.stat.ucla.edu/research/web.pdf tst <- t(matrix(c( 0.0104916072210123716, -0.993396087928394733, -0.089861775335686706, 0.1584646383898045685, -0.167305085570175344, -0.967087879524061056, 0.9822741057703969769, -0.094961339079248266, -0.081938545344928893, 0.1249962020162782989, -0.597065497283680413, -0.789290657131387352, 0.8695614167874907707, -0.471622450093366785, -0.090438968384549553, 0.8757114893176747294, -0.141012080768234127, -0.452333925937943637, 0.0679423211019681700, -0.811411071716238719, -0.588554936857709099, 0.4066768108416509708, -0.907862149146695163, -0.115673202040957226, 0.5770808894249742638, -0.142370726163931066, -0.806527261406603468, 0.1012712863762783577, -0.723336747696182614, -0.694640249329106285, 0.5000928657774492692, -0.949746569049947253, -0.046846346456817907, 0.7412589798326677526, -0.140350561965914555, -0.663578062154924320, 0.0055655501003109590, -0.983847100401775698, -0.120037109608235590, 0.2142330103903098415, -0.119429100752156334, -0.947421187831809397, 0.9550804066106526324, -0.108275659756619305, -0.039227521113362487, 0.7823218737697450464, -0.405437596810190704, -0.439275358874331168, 0.3626971102221024923, -0.753122462957226402, -0.546281394544768872, 0.0162483298780003657, -0.966230359337758582, -0.052114148464710915, 0.1076692386876715729, -0.206734953950642314, -0.934620775424686911, 0.9744239420161749932, -0.092650552854598708, -0.090828719474599584 ), 3, 20)) if( fuzz < max(abs(qbox20$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 2. Value:\n") print(qbox20$loadings, digits=18) cat("difference:\n") print(qbox20$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 0.57232345894276127, -0.60751194947821441, -0.55079496147384377, 0.60249460283341838, 0.76716797198365361, -0.22012168525406509, 0.55627880770383020, -0.20587018726291534, 0.80509089803322043 ), 3, 3)) if( fuzz < max(abs(qbox20$Th - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 3. Value:\n") print(qbox20$Th, digits=18) cat("difference:\n") print(qbox20$Th - tst, digits=18) all.ok <- FALSE } # sorted absolute loading plots. sal <- abs(c(loadings(qbox20)))[order(abs(c(loadings(qbox20))))] plot(seq(length(sal)), sal) #compare quartimax rotation of the initial loading matrix box20. if( fuzz < max(abs(loadings(qbox20) - box20 %*% qbox20$Th ))) { cat("Calculated value is not the same as test value in test Thurstone 4. Value:\n") print(loadings(qbox20), digits=18) cat("difference:\n") print(loadings(qbox20) - box20 %*% qbox20$Th, digits=18) all.ok <- FALSE } qminbox20G <- GPFoblq(box20, Tmat=diag(1,3), method="quartimin", eps=1e-5) qminbox20 <- quartimin(box20, eps=1e-5) if( fuzz < max(abs(loadings(qminbox20) - qminbox20G$loadings))) { cat("Calculated value is not the same as test value in test Thurstone 5. Value:\n") print(qminbox20G$loadings , digits=18) cat("difference:\n") print(loadings(qminbox20) - qminbox20G$loadings, digits=18) all.ok <- FALSE } #qminbox20$Th - quartimin(box20)$Th # These values compare with those in: # http://www.stat.ucla.edu/research/web.pdf tst <- t(matrix(c( -0.099561899210599963, -1.0236437309424475384, 0.017110338313848200, -0.007103778102200991, 0.0427848301281630802, -1.009962780073245581, 1.012864497258948226, 0.0331727792925069487, 0.050367710973030555, -0.054843850612513692, -0.4493155290974688021, -0.772334543778026350, 0.856287122381722998, -0.3740197232441037078, 0.069350368268248391, 0.835580575619599641, 0.0487450425576793633, -0.360381644212301344, -0.102893671454670210, -0.7226715938020771279, -0.537456650126404090, 0.322103633211960838, -0.8816846447967544576, 0.031159743715387874, 0.462799683447739529, 0.0852338438217692257, -0.783762970578423479, -0.076585435689138226, -0.6043060025891554554, -0.658295846696152820, 0.427772530893690217, -0.9288687512327726825, 0.122866182561916254, 0.659408232467282085, 0.0772080094990600374, -0.607348040513722709, -0.108761719100651882, -1.0079608432113262850, -0.017378089000366713, 0.059518597564186392, 0.0955950614351480238, -0.986779686330629513, 0.989890866913205381, 0.0071520817823045348, 0.094691644950703049, 0.713733277219835149, -0.2427293600063723522, -0.328268187306521242, 0.220344503737931546, -0.6353746612195683152, -0.459661643730432223, -0.084703580704062989, -1.0022284232457450148, 0.055740317456252478, -0.059151779416785115, -0.0113377397453605679, -0.976867596293413132, 1.003360458549731771, 0.0365098037316876067, 0.039427150580815938 ), 3, 20)) if( fuzz < max(abs(qminbox20G$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 6. Value:\n") print(qminbox20G$loadings, digits=18) cat("difference:\n") print(qminbox20G$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 1.00000000000000000, -0.25676300454795098, -0.32155119431295237, -0.25676300454795098, 1.00000000000000000, 0.33656790396842257, -0.32155119431295237, 0.33656790396842257, 1.00000000000000000 ), 3, 3)) if( fuzz < max(abs(qminbox20G$Phi - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 7. Value:\n") print(qminbox20G$Phi, digits=18) cat("difference:\n") print(qminbox20G$Phi - tst, digits=18) all.ok <- FALSE } #To fuzz precision the rotated loading matrix and the factor cor- #relation matrix phi are identical to those produced using the oblique GP #algorithm with exact derivatives. if( fuzz < max(abs(qminbox20G$Phi - t(qminbox20G$Th )%*% qminbox20G$Th ))) { cat("Calculated value is not the same as test value in test Thurstone 8. Value:\n") print(qminbox20G$Phi, digits=18) cat("difference:\n") print(qminbox20G$Phi - t(qminbox20G$Th )%*% qminbox20G$Th, digits=18) all.ok <- FALSE } #compare quartimin rotation of the initial loading matrix box20. if( fuzz < max(abs(qminbox20G$loadings - box20 %*% solve(t(qminbox20G$Th))))) { cat("Calculated value is not the same as test value in test Thurstone 9. Value:\n") print(qminbox20G$loadings, digits=18) cat("difference:\n") print(qminbox20G$loadings - box20 %*% solve(t(qminbox20G$Th)), digits=18) all.ok <- FALSE } data("box26", package="GPArotation") if (!exists("box26")) stop("Test data box26 not found. Testing stopped.") qbox26 <- GPForth(box26, Tmat=diag(1,3), method="quartimax", eps=1e-5) tst <- t(matrix(c( 0.6245197355925140581, -0.2708954695931116152, 0.7151983951389878635, 0.7386116884036847408, 0.6266342260884526505, -0.0617439911892987553, 0.7803093788467402314, -0.3830982859243221017, -0.4578886072022986253, 0.8540550453155928423, 0.2886436985992582027, 0.4062915145925659610, 0.8810593765418006651, -0.4428658074662961130, 0.1233946983666596581, 0.9084731768740617053, 0.1540526132602804965, -0.3723026715563940159, 0.8150592858039771293, 0.0441965358534676597, 0.5600768044145943980, 0.8466584455973064083, 0.4551177395514792168, 0.1889929089788950356, 0.8156808837280125069, -0.4090629943132625956, 0.3690652552112651530, 0.9629492340906220527, -0.4781483041690369196, -0.0866081507974762743, 0.8731366884896356595, 0.3451069860590937899, -0.2914969834947889749, 0.8921854600753849063, -0.0276323108621970258, -0.4257376659710629951, -0.0938760381595044741, -0.7873218033841372643, 0.6012450975895150540, 0.0938760381595044741, 0.7873218033841372643, -0.6012450975895150540, -0.0986092863860908303, 0.1513605567468480073, 0.9692559984337008050, 0.0986092863860908303, -0.1513605567468480073, -0.9692559984337008050, -0.0189573629854957251, 0.9527983290277913797, 0.2944078167958268377, 0.0189573629854957251, -0.9527983290277913797, -0.2944078167958268377, 0.8394181189595459891, 0.3631767908642606346, 0.3398717995655929913, 0.8703065201362156778, -0.4691145408161159214, 0.0770980453920554615, 0.9141063746617547059, 0.1583184861345137973, -0.3535658252020681958, 0.8348118627305495254, 0.3535663452183119837, 0.3271666140872500073, 0.8541352373790773722, -0.4476738735312740247, 0.0569042988261160704, 0.9034738474019414767, 0.1663655738425987851, -0.3227406124130587362, 0.9861758757457432800, 0.0103496363116840455, 0.0635926656567585569, 0.9643516568468981642, 0.0660181478622221818, -0.0304218028637989850 ), 3, 26)) if( fuzz < max(abs(qbox26$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 10. Value:\n") print(qbox26$loadings, digits=18) cat("difference:\n") print(qbox26$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 0.9996572020207266096, 0.0216275672176080257, 0.0147555679097727491, -0.0158190757965277796, 0.9480178905874908635, -0.3178235925273457108, -0.0208622934749700742, 0.3174812237948764770, 0.9480350400953921897 ), 3, 3)) if( fuzz < max(abs(qbox26$Th - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 11. Value:\n") print(qbox26$Th, digits=18) cat("difference:\n") print(qbox26$Th - tst, digits=18) all.ok <- FALSE } qminbox26 <- GPFoblq(box26, Tmat=diag(1,3), method="quartimin", eps=1e-5) tst <- t(matrix(c( 0.6088436426802223966, -0.2567107018725688361, 0.7213648290819488773, 0.7318447535507376367, 0.6298398026581654152, -0.0549983771960348838, 0.7973321695017724364, -0.3855960314746548212, -0.4504478973568259437, 0.8392144987741166906, 0.2994932968625432235, 0.4143558581243267924, 0.8833452352200144020, -0.4361046712803113290, 0.1319331147095905710, 0.9161366872228343672, 0.1535557844336666034, -0.3638337328539109072, 0.7993355454002614158, 0.0571270784641514026, 0.5678963531379384033, 0.8354288250614068101, 0.4626764152757318893, 0.1968789749765105790, 0.8109923806202916641, -0.3989909333845649830, 0.3770226870580207779, 0.9712737747877250305, -0.4740722765307348041, -0.0773243882106463137, 0.8761501947960563808, 0.3456235893514668089, -0.2834183138879167174, 0.9036601763684347643, -0.0290211959776035672, -0.4173652812159966974, -0.0995525797764766768, -0.7788574612781464790, 0.6007791331268093060, 0.0995525797764766768, 0.7788574612781464790, -0.6007791331268093060, -0.1264036712449473909, 0.1653130238928011975, 0.9684661160120416890, 0.1264036712449473909, -0.1653130238928011975, -0.9684661160120416890, -0.0392946742598458687, 0.9571059478962877787, 0.2939285303852590125, 0.0392946742598458687, -0.9571059478962877787, -0.2939285303852590125, 0.8253744379910458173, 0.3729516010405902748, 0.3477554718030251846, 0.8741734789142978634, -0.4631063486451737488, 0.0855349365396926159, 0.9212130243051569467, 0.1581334796580046720, -0.3450412531516501846, 0.8212340853954427367, 0.3631252613622908965, 0.3350076577679809153, 0.8582635618771776720, -0.4420579024138228674, 0.0651757040961165046, 0.9096561314838297330, 0.1665824736284239604, -0.3143133889989875307, 0.9840845767693481294, 0.0168070160966761091, 0.0729425956763933708, 0.9640420478016114014, 0.0709475796833391181, -0.0213192081807395371 ), 3, 26)) if( fuzz < max(abs(qminbox26$loadings - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 12. Value:\n") print(qminbox26$loadings, digits=18) cat("difference:\n") print(qminbox26$loadings - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 1.000000000000000 , 0.00767934084449363279, 0.0170654511973979163, 0.00767934084449363279, 1.000000000000000 , -0.0144994900961642244, 0.01706545119739791630, -0.01449949009616422445, 1.000000000000000 ), 3, 3)) if( fuzz < max(abs(qminbox26$Phi - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 13. Value:\n") print(qminbox26$Phi, digits=18) cat("difference:\n") print(qminbox26$Phi - tst, digits=18) all.ok <- FALSE } tst <- t(matrix(c( 0.9993401424148040668, 0.0347479564402226465, 0.0408645923859655008, -0.0179660947915933414, 0.9476477730670300748, -0.3324117322929439067, -0.0315673755054017430, 0.3174212937474846785, 0.9422486536594960604 ), 3, 3)) if( fuzz < max(abs(qminbox26$Th - tst ))) { cat("Calculated value is not the same as test value in test Thurstone 14. Value:\n") print(qminbox26$Th, digits=18) cat("difference:\n") print(qminbox26$Th - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/Harman.R0000644000176200001440000001261514323662112015141 0ustar liggesusers#Example from: Gradient Projection Algorithms and Software for # Arbitrary Rotation Criteria in Factor Analysis. # by Coen A. Bernaards and Robert I. Jennrich # Website: http://www.stat.ucla.edu/research Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() fuzz <- 1e-5 # using eps=1e-5 these tests do not do better than this all.ok <- TRUE # quartimax (orthogonal) rotation of Harman's 8 physical variables. data("Harman", package="GPArotation") qHarman <- GPForth(Harman8, Tmat=diag(2), method="quartimax") qHarman2 <- quartimax(Harman8) if( fuzz < max(abs(qHarman$loadings - qHarman2$loadings))) { cat("Calculated value is not the same as test value in test Harman 1. Value:\n") print(qHarman$loadings, digits=18) cat("difference:\n") print(qHarman$loadings - qHarman2$loadings, digits=18) all.ok <- FALSE } #qHarman$Th - qHarman2$Th # with eps=1e-8 # tst <- t(matrix(c( # 0.898754567491920398, 0.194823580226859222, # 0.933943406208487592, 0.129748657024604030, # 0.902131483644799892, 0.103864268239045668, # 0.876508251941102934, 0.171284220753554678, # 0.315572019798302239, 0.876476069451083251, # 0.251123191235179066, 0.773488941629975613, # 0.198007116064591759, 0.714678376605717203, # 0.307857241091366252, 0.659334451631046314 # ), 2, 8)) # with eps=1e-5 tst <- t(matrix(c( 0.898755404698461491, 0.194819718009510034, 0.933943963768413821, 0.129744643590955028, 0.902131929972106672, 0.103860391510923730, 0.876508987992224209, 0.171280454135453869, 0.315575786273609882, 0.876474713336210853, 0.251126515144778573, 0.773487862471829213, 0.198010187248201075, 0.714677525703678707, 0.307860074444663512, 0.659333128670876345 ), 2, 8)) if( fuzz < max(abs(qHarman$loadings - tst ))) { cat("Calculated value is not the same as test value in test Harman 2. Value:\n") print(qHarman$loadings, digits=18) cat("difference:\n") print(qHarman$loadings - tst, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 0.790828307905322436, 0.612038060430562525, # -0.612038060430562525, 0.790828307905322214 # ), 2, 2)) # with eps=1e-5 tst <- t(matrix(c( 0.790830938007507367, 0.612034662000581764, -0.612034662000581764, 0.790830938007507145 ), 2, 2)) if( fuzz < max(abs(qHarman$Th - tst ))) { cat("Calculated value is not the same as test value in test Harman 3. Value:\n") print(qHarman$Th, digits=18) cat("difference:\n") print(qHarman$Th - tst, digits=18) all.ok <- FALSE } # quartimin (oblique) rotation of Harman's 8 physical variables. qminHarman <- GPFoblq(Harman8, Tmat=diag(2), method="quartimin") qminHarman2 <- quartimin(Harman8) if( fuzz < max(abs(qminHarman$loadings - qminHarman2$loadings))) { cat("Calculated value is not the same as test value in test Harman 4. Value:\n") print(qminHarman$loadings, digits=18) cat("difference:\n") print(qminHarman$loadings - qminHarman2$loadings, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 0.8918217697289939627, 0.0560146456758183961, # 0.9536799985772628219, -0.0232460005406671701, # 0.9291498623396581280, -0.0465027396531852502, # 0.8766828510822184395, 0.0336582451338717017, # 0.0136988312985193428, 0.9250013826349388069, # -0.0172668087945964319, 0.8212535444941218010, # -0.0524468998178311899, 0.7649536381341245361, # 0.0858880630098148856, 0.6831160953442911854 # ),2, 8)) # with eps=1e-5 tst <- t(matrix(c( 0.8918219293548808047, 0.0560145122875230911, 0.9536799846795966928, -0.0232460559140742311, 0.9291497958388006406, -0.0465027685653178480, 0.8766829604751505967, 0.0336581364763500201, 0.0137008854716444972, 0.9250004106413580729, -0.0172649861805529957, 0.8212526839806429946, -0.0524452035885302342, 0.7649528396536503516, 0.0858895830186393733, 0.6831153711863455769 ),2, 8)) if( fuzz < max(abs(qminHarman$loadings - tst ))) { cat("Calculated value is not the same as test value in test Harman 5. Value:\n") print(qminHarman$loadings, digits=18) cat("difference:\n") print(qminHarman$loadings - tst, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 1.000000000000000000, 0.472747617396915065, # 0.472747617396915065, 1.000000000000000000 # ),2, 2)) # with eps=1e-5 tst <- t(matrix(c( 1.000000000000000222, 0.472745958387102538, 0.472745958387102538, 1.000000000000000000 ),2, 2)) if( fuzz < max(abs(qminHarman$Phi - tst ))) { cat("Calculated value is not the same as test value in test Harman 6. Value:\n") print(qminHarman$Phi, digits=18) cat("difference:\n") print(qminHarman$Phi - tst, digits=18) all.ok <- FALSE } # with eps=1e-8 # tst <- t(matrix(c( # 0.878125245495924522, 0.836723841642554422, # -0.478430823863515542, 0.547625065922776710 # ),2, 2)) # with eps=1e-5 tst <- t(matrix(c( 0.878125280760480686, 0.836722770276292271, -0.478430759137962514, 0.547626702874473570 ),2, 2)) if( fuzz < max(abs(qminHarman$Th - tst ))) { cat("Calculated value is not the same as test value in test Harman 7. Value:\n") print(qminHarman$Th, digits=18) cat("difference:\n") print(qminHarman$Th - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/varimaxVarimax.R0000644000176200001440000000250614336234075016737 0ustar liggesusers# Also see the first test in rotations.R # compares varimax to Varimax to 0.001 discrepancy Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() ### note that this is a slightly lower bar than other tests ### to correct for the built-in varimax function working differently ### than GPA, and to ensure Varimax convergence ### these are differences in the 4th decimal or better fuzz <- 1e-4 ### all.ok <- TRUE sortFac <- function(x){ # Based on Fungible faSort vx <- order(colSums(x$loadings^2), decreasing = TRUE) Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vx] x$Th <- x$Th %*% Dsgn x$loadings <- x$loadings %*% Dsgn if ("Phi" %in% names(x)) { x$Phi <- diag(1/diag(Dsgn)) %*% x$Phi %*% Dsgn } x } data(Thurstone, package="GPArotation") yv1 <- varimax(box20, normalize = FALSE, eps = 1e-7) #built-in R names(yv1) <- c("loadings","Th") yv1 <- sortFac(yv1) yv2 <- sortFac(Varimax(box20, normalize = FALSE, maxit = 10000, eps = 1e-7)) #GPArotation version # yv.diff <- unclass(yv1$loadings) - unclass(yv2$loadings) # max(abs(yv.diff)) if( fuzz < max(abs(yv1$loadings - yv2$loadings))) { cat("Calculated varimax is not the same as Varimax:\n") # print(yv2$loadings, digits=18) cat("difference:\n") print(yv1$loadings - yv2$loadings, digits=18) all.ok <- FALSE } GPArotation/tests/Jennrich2002.R0000644000176200001440000001062514337257176016015 0ustar liggesusers# test by William Revelle # from Jennrich, Psychometrika, 2002, solution for the Thurstone 20 box problem. # Specifying 27 elements to be 0 as discussed in that article (Table 1 at # page 12) and using vgQ.target as revised or vgQ.pst with a W matrix # and Target as specified does not yield the reported solution. # The solution is almost identical for the high loadings but differs slightly # for the small loadings. The two models have a factor congruence of .99 for # all three factors, but do not agree completely. # Jennrich (2002) apparently was using the oblique rotation option. # When running TargetQ the results are fine, or when running # the vgQ.pst function with GPFoblq. # This a good test case for both TargetQ # (It could also be adapted for pst but there is already a test for it.) require("GPArotation") data(Thurstone) #the 20 box problem #solution reported in Jennrich 2002 browne <- t(matrix(c( 0.013, 0.994, 0.007, 0.991, 0.012, 0.001, 0.018, 0.003, 0.986, 0.772, 0.477, 0.002, 0.003, 0.393, 0.874, 0.409, 0.003, 0.816, 0.548, 0.730, -0.020, 0.023, 0.870, 0.405, 0.799, -0.024, 0.453, 0.664, 0.621, -0.005, -0.058, 0.915, 0.512, 0.639, -0.018, 0.644, 0.046, 0.980, -0.003, 0.971, -0.038, 0.060, -0.026, 0.025, 0.965, 0.380, 0.281, 0.726, 0.490, 0.652, 0.286, -0.025, 0.971, 0.019, 0.957, 0.061, -0.045, 0.028, 0.000, 0.976), 3,20,dimnames = list(c("B1", "B2", "B3"), NULL))) #a simplified target matrix, with NAs for ? and 0 for 0s. # (compare to pst appproach) Target <- t(matrix(c( 0, NA, 0, NA, 0, 0, 0, 0, NA, NA, NA, 0, 0, NA, NA, NA, 0, NA, NA, NA, 0, 0, NA, NA, NA, 0, NA, NA, NA, 0, 0, NA, NA, NA, 0, NA, 0, NA, 0, NA, 0, 0, 0, 0, NA, NA, NA, NA, NA, NA, NA, 0, NA, 0, NA, 0, 0, 0, 0, NA), 3, 20, dimnames = list(c("T1", "T2", "T3"), NULL))) v <- targetQ(box20,Target=Target)$loadings # THIS ONE WORKS #v <- GPFoblq(box20, method ="target", methodArgs = list(Target=Target))$loadings all.ok <- TRUE #slightly larger fuzz for comparison with published value. # note max(abs(v) - abs(browne))rather than max(abs(v - browne)) # as sign change is possible if( 10e-4 < max(abs(v) - abs(browne))) { cat("Calculated value is not the same as test value in Jennrich2002. Value:\n") print(v, digits=18) cat("difference:\n") print(v - browne, digits=18) all.ok <- FALSE } good <- t(matrix(c( 0.01324194563970146343, -0.99360765277094842407, 0.007265459960371034587, 0.99121314541487770544, -0.01178320700232154961, 0.000654586020267855506, 0.01798447315534307256, -0.00266076852016330911, 0.985581004768931734361, 0.77198435084052174915, -0.47723548341238952730, 0.001547735983967568618, 0.00334198654247502835, -0.39290416948063611180, 0.874043793719835537814, 0.40934347835281348349, -0.00274610551094590233, 0.815649888720176186041, 0.54757055519984310088, -0.72951044925148011977, -0.020211353947714422175, 0.02292379053779741716, -0.87011712730189194609, 0.404542252780873523577, 0.79911058029224457666, 0.02416810475294199623, 0.452727043944761764482, 0.66393502364020362538, -0.62149665012300570055, -0.005186928343372421146, -0.05839790682548451350, -0.91517931889838155524, 0.511521949806932663130, 0.63924406199386740735, 0.01841750353525576159, 0.643544196342115570886, 0.04597086497418309547, -0.97980801598321454193, -0.002918643110053173451, 0.97103389549392915558, 0.03847065084578840666, 0.060066450372699808913, -0.02622776344285615568, -0.02482060086975104718, 0.965272709232911085842, 0.37998105522582992233, -0.28073835673932595602, 0.726047993725112084107, 0.48985182554738604388, -0.65226812910595410866, 0.285738966726349907788, -0.02451057644240206557, -0.97122042802717223342, 0.019132901654980147277, 0.95708220223038309449, -0.06086293722346142188, -0.045050942196376064786, 0.02797903728304645954, 0.00036458752733534161, 0.976083771686937051726), 3,20,dimnames = list(c("B1", "B2", "B3"), NULL))) #tighter fuzz for numerical comparison with previous test value if( 10e-12 < max(abs(v - good))) { cat("Calculated value is not the same as previous test value. Value:\n") print(v, digits=18) cat("difference:\n") print(v - good, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/KaiserNormalization.R0000644000176200001440000000726314524252342017726 0ustar liggesusers# tests using normalization # All tests below use Kaiser normalization # A few other tests also use normalization when comparing varimax and Varimax # Following examples are from SPSS # See https://psych.unl.edu/psycrs/statpage/pc_rot.pdf Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") fuzz <- 1e-3 #less strict; differences in 4rd decimal compared to SPSS all.ok <- TRUE # unrotated matrix L <- matrix(c(.758, .413, 1.164E-03, .693, .489, -.199, .362, .656, -.204, .826, 6.589E-02, .235, .540, -.510, .441, .654, -.335, .507, -.349, .539, .669, -.580, .450, .551), byrow=T, ncol=3) # quartimax, Kaiser normalization # uses the print command to get the right order of factors v <- print(quartimax(L, normalize = TRUE, eps = 1e-6))$loadings tst <- matrix(c(.814, .285, -4.99E-02, .856, 8.321E-02, -.135, .746, -.203, 7.244E-02, .576, .634, -8.73E-02, -6.10E-02, .850, -.142, .129, .882, -3.86E-02, 2.063E-02, -4.15E-02, .927, -.181, -.220, .873), byrow=T, ncol=3) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # oblimin, Kaiser normalization # Pattern Matrix vw <- print(oblimin(L, normalize = TRUE, eps = 1e-7)) v <- vw$loadings tst <- matrix(c(.241, .787, -1.36E-02, 1.783E-02, .848, -.119, -.240, .779, 6.824E-02, .608, .507, -2.52E-02, .858, -.163, -7.26E-02, .896, 3.050E-02, 3.954E-02, 9.405E-02, 7.397E-02, .949, -8.61E-02, -.113, .875), byrow=T, ncol=3) tst <- tst %*% matrix(c(0,1,0,1,0,0,0,0,1), 3) # Needed to line up factors correctly fuzz <- 3e-3 #less strict; differences in 4th decimal compared to SPSS; 0.003 or smaller diff if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # oblimin, Kaiser normalization # Structure Matrix v <- vw$loadings %*% vw$Phi tst <- matrix(c(.379, .829, -.146, .191, .862, -.203, -.123, .731, .051, .701, .613, -.218, .847, -.010, -.261, .891, .180, -.176, -.118, .000, .919, -.313, -.211, .906), byrow=T, ncol=3) tst <- tst %*% matrix(c(0,1,0,1,0,0,0,0,1), 3) # Needed to line up factors correctly fuzz <- 4e-3 #less strict; differences in 4th decimal compared to SPSS; 0.004 or smaller diff if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ################################################################# # # Confirmation that a row of zeroes will not break the normalization function # Normalizing with a column of zeroes was not affected # based on example from Kim-Laura Speck (25 October 2023) # Only affects Normalize=TRUE settings fuzz <- 1e-6 D <- matrix(c(0,0,0, 1,2,3, 2,3,4, 5,2,5, 1,2,1, 3,4,5),ncol=3,byrow=T) set.seed(1000) #set seed becasuse some variance is observed in converged values v <- geominQ(D, normalize = TRUE, maxit = 10000)$loadings tst <- matrix(c( 0.00000000, 0.00000000, 0.00000000, -0.36979732, -0.13603325, 3.99622380, 0.03102554, 0.76678245, 4.68896063, 3.28926158, 0.01317821, 5.35447764, -0.02755956, 2.40311582, 0.06247234, 0.43184841, 1.66959816, 5.38169746), ncol = 3, byrow = TRUE) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } GPArotation/tests/rotationsRS.R0000644000176200001440000004350014763337751016237 0ustar liggesusers# Tests here only compare against values computed previously with this code, # to ensure there was no accidental change. It would be better to have # comparisons with known correct values. # Test for oblimax is commented out as it appears to be unstable. Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") fuzz <- 1e-6 all.ok <- TRUE sortFac <- function(x){ # Based on Fungible faSort vx <- order(colSums(x$loadings^2), decreasing = TRUE) Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vx] x$Th <- x$Th %*% Dsgn x$loadings <- x$loadings %*% Dsgn if ("Phi" %in% names(x)) { x$Phi <- diag(1/diag(Dsgn)) %*% x$Phi %*% Dsgn } x } data(ability.cov) L <- loadings(factanal(factors = 2, covmat=ability.cov)) set.seed(100) tst <- sortFac(Varimax(L, normalize=FALSE, randomStarts = 100))$loadings if( 0.001 < max(abs(varimax(L, normalize=FALSE)$loadings - tst))){ cat("Calculated difference exceeds tolerance\n") cat("difference:\n") print(varimax(L, normalize=FALSE)$loadings - tst, digits=18) all.ok <- FALSE } set.seed(100) tst <- sortFac(Varimax(L, normalize=TRUE, randomStarts = 100))$loadings if( 0.01 < max(abs(varimax(L, normalize=TRUE)$loadings - tst))) { cat("Calculated difference exceeds tolerance\n") cat("difference:\n") print(varimax(L, normalize=TRUE)$loadings - tst, digits=18) all.ok <- FALSE } set.seed(99) v <- sortFac(oblimin(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(98) v <- sortFac(quartimin(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 2. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # # This fails with the old Random.Start # set.seed(97) # v <- sortFac(targetT(L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), # eps=1e-5, randomStarts = 100))$loadings # tst <- t(matrix(c( # 0.551529228817982942, 0.4905002767031292898, # 0.217748645523411000, 0.6027046291262584399, # 0.291173432863349457, 0.8348885228488550636, # 0.154994397662456290, 0.4544843569140373241, # 0.969702339393929247, 0.0850652965070581996, # 0.803390575440818822, 0.1448091121037717866 # ), 2, 6)) # # if( fuzz < max(abs(v - tst))) { # cat("Calculated value is not the same as test value in test rotations 3. Value:\n") # print(v, digits=18) # cat("difference:\n") # print(v - tst, digits=18) # all.ok <- FALSE # } # # This fails with the old Random.Start # Random starts get to a lower f value (1.8) which is a mismatch to the tst matrix # generated without random starts. Its f value is 5.6 # removed the test # set.seed(96) # v <- sortFac(targetQ(L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), # eps=1e-5, randomStarts = 100))$loadings # tst <- t(matrix(c( # 0.735795682866631218, 0.565351705145453853, # 0.433590223819374398, 0.664644550038417159, # 0.589924557708411568, 0.920006940799857786, # 0.317543426981046928, 0.500590650032113116, # 1.021758247914384077, 0.155121528590726393, # 0.872521244896209747, 0.208735706420634437 # ), 2, 6)) # # if( fuzz < max(abs(v - tst))) { # cat("Calculated value is not the same as test value in test rotations 4. Value:\n") # print(v, digits=18) # cat("difference:\n") # print(v - tst, digits=18) # all.ok <- FALSE # } # oblimax # this is test value on one computer # tst <- t(matrix(c( # -8111059.94622692652, 8111060.62253121007, # 1495036.43465861562, -1495035.79614594672, # 2331634.63904705830, -2331633.75893370388, # 1356735.91680212389, -1356735.43916810025, # -23187491.19758165255, 23187491.68068471923, # -18357040.58573083207, 18357041.05348757654 # ), 2, 6)) # # this is test value on another computer # tst <- t(matrix(c( # 2694770.06630349346, -2694769.38999920478, # -496701.45733913727, 496702.09585180727, # -774647.63529061736, 774648.51540397422, # -450753.43529273639, 450753.91292676108, # 7703672.48495316971, -7703672.00185009185, # 6098832.71036116872, -6098832.24260441773 # ), 2, 6)) # # this does not converge on all platforms and has large differences possible a mistake ??? # v <- oblimax(L, eps=1e-5)$loadings # if( fuzz < max(abs(v - tst))) { # cat("Calculated value is not the same as test value in test rotations 7. Value:\n") # print(v, digits=18) # cat("difference:\n") # print(v - tst, digits=18) # all.ok <- FALSE # } v <- sortFac(entropy(L, maxit=3000, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.528292107548243184, 0.515443945340967824, 0.189686511729033253, 0.612116304198454975, 0.252311894464850861, 0.847442931117894815, 0.133843268148035738, 0.461156452364903380, 0.964740133927989407, 0.129750551769587635, 0.795847094000000532, 0.181751199795689433 ), 2, 6)) if( 0.01 < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 8. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-4 # 4th decimal differences set.seed(110) v <- sortFac(quartimax(L, eps=1e-7, randomStarts = 100))$loadings tst <- t(matrix(c( 0.534714740804540178, 0.508778102568043678, 0.197348140750149392, 0.609689309353509956, 0.262919828098457153, 0.844212045390758559, 0.139616102327241837, 0.459441658926639795, 0.966291466215733252, 0.117641548844535412, 0.798063848020893585, 0.171756193883937508 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 9. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(110) v <- sortFac(GPFRSorth(L, eps = 1e-7, method = "quartimax", randomStarts = 100))$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 9-GPFRSorth. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(90) v <- sortFac(Varimax(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.515866523962843160, 0.527879475961036904, 0.175054634278874244, 0.616460231981747930, 0.232057748479543163, 0.853211588623112749, 0.122822468397975171, 0.464213243286899446, 0.961376376417989453, 0.152689863976982837, 0.791292800869773050, 0.200653429940987366 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 10. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(90) v <- sortFac(GPFRSorth(L, eps = 1e-7, method = "varimax", randomStarts = 100))$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 10-GPFRSorth. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(89) v <- sortFac(simplimax(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.3384175759313114429, 0.508414890494446547464, -0.0654601124161610648, 0.670992229004664153535, -0.1016231721735353366, 0.930535379393095940515, -0.0589933707274080121, 0.506904360351960181497, 0.9733094402675376289, 0.000234046050254643859, 0.7702037184085044341, 0.085651123319384916965 ), 2, 6)) if( fuzz < max(abs(v - tst[,2:1]))) { cat("Calculated value is not the same as test value in test rotations 11. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(89) v <- sortFac(GPFRSoblq(L, eps = 1e-5, method = "simplimax", randomStarts = 100))$loadings if( fuzz < max(abs(v - tst[,2:1]))) { cat("Calculated value is not the same as test value in test rotations 11-GPFRSoblq. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(88) v <- sortFac(bentlerT(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.523583611303327312, 0.520226117818945788, 0.184113022124463677, 0.613815719643687197, 0.244596116053327067, 0.849702038129718673, 0.129644684715025493, 0.462354355134084738, 0.963520501269179652, 0.138517057902201340, 0.794161628656258278, 0.188979901644201559 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 12. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(88) v <- sortFac(GPFRSorth(L, eps = 1e-7, method = "bentler", randomStarts = 100))$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 12-GPFRSorth. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(97) v <- sortFac(bentlerQ(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.3801726240258240241, 0.4741208368044214638, -0.0223632969057368826, 0.6514196922540864687, -0.0421105927111659756, 0.9039359851665277334, -0.0266594447192576613, 0.4925968005718689424, 0.9961524457620027917, -0.0485973498906049697, 0.7939648477384558811, 0.0440983921679098251 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 13. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-5 set.seed(85) v <- sortFac(tandemI(L, eps=1e-6, maxit = 1000, randomStarts = 100))$loadings tst <- t(matrix(c( 0.615424480780047745, 0.4074649925368262759, 0.300894306348887419, 0.5658002819054848143, 0.406455233467338028, 0.7852483408305571677, 0.217785179074990981, 0.4279590047675180808, 0.971977129465111611, -0.0530960591067626969, 0.815800376450207976, 0.0295946184147908228 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 14. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-5 set.seed(84) v <- sortFac(tandemII(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.512160139332842212, 0.531476249107136312, 0.170736763115044710, 0.617670057812827134, 0.226081850628144149, 0.854814488884392154, 0.119571200821562001, 0.465061309851099225, 0.960284416460420398, 0.159413208985883820, 0.789869387186175276, 0.206185467095899383 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 15. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-6 set.seed(81) v <- sortFac(geominT(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.572197044101002361, 0.4662247895688098054, 0.243573415560656120, 0.5927388411683653935, 0.326956608263186954, 0.8215352639437966120, 0.174476792179181994, 0.4473668997335142894, 0.972471249855535680, 0.0431091626026945812, 0.808894688433769660, 0.1099794466209375043 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 16. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-6 set.seed(80) v <- sortFac(geominQ(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.39672053553904490508, 0.4713295988080449250, 0.00424452688463150020, 0.6389466007374070555, -0.00510976786312981532, 0.8864521406378518265, -0.00646959173137159373, 0.4830101828530461994, 0.98709860078485589518, -0.0318959930081098297, 0.79011178369962709045, 0.0558689642678330683 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 17. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(70) v <- sortFac(cfT(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.534721263659975854, 0.508771247100584523, 0.197355957387199576, 0.609686779159006154, 0.262930651479430233, 0.844208674501022327, 0.139621992686633722, 0.459439868910532512, 0.966292974385164483, 0.117629160286744874, 0.798066049992627313, 0.171745962120156664 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 18. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(60) v <- sortFac(cfQ(L, eps=1e-8, randomStarts = 100))$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 19. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-5 set.seed(55) v <- sortFac(infomaxT(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.495330443338021176, 0.547195361446864537, 0.151384273205308784, 0.622695868320644275, 0.199304253086364791, 0.861451466010626055, 0.105004533733904976, 0.468565194910632365, 0.954843809781045660, 0.189293503899924942, 0.783052579543945471, 0.230726576980168713 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 20. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } fuzz <- 1e-5 set.seed(50) v <- sortFac(infomaxQ(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.39327554287862442894, 0.4693137508305071925, -0.00319802321222481794, 0.6422985517185823001, -0.01549245038490981718, 0.8912279460026399924, -0.01214605901641467763, 0.4856544522916727002, 0.99260028929193111491, -0.0433225495465055510, 0.79356458059567791530, 0.0471559021503157039 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 21. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(40) v <- sortFac(mccammon(L, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.4293472299617892007, 0.600363196582340275, 0.0790140496845253004, 0.635943490060206229, 0.0992523811009183854, 0.878618107277518656, 0.0506062164774049028, 0.477512622702450096, 0.9268544198491108776, 0.297488850382792269, 0.7514463663627769519, 0.318958389348199534 ), 2, 6)) tst <- tst[, 2:1] if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } set.seed(35) v <- sortFac(oblimax(Harman8, eps=1e-5, randomStarts = 100))$loadings tst <- t(matrix(c( 0.93395421734409445058, -0.0302013026726007383, 0.99243032312927881300, -0.1121899246869615951, 0.96509469978483286567, -0.1322258547171115683, 0.91647702431117861188, -0.0502569243958834178, 0.08441855308346873921, 0.8875309317276611765, 0.04427084251510177149, 0.7907585046311147448, 0.00332736511424391868, 0.7399752420126202157, 0.14133359391312094733, 0.6483050831171799366 ), 2, 8)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/WansbeekMeijer.R0000644000176200001440000000606314323662112016626 0ustar liggesusers Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() fuzz <- 1e-6 all.ok <- TRUE data(WansbeekMeijer, package="GPArotation") fa.none <- factanal(factors=2, covmat=NetherlandsTV, rotation="none") tst <- t(matrix(c( 0.6972803, -0.3736554, 0.7774628, -0.3184149, 0.6832300, -0.3620428, 0.6612198, 0.2361132, 0.6972393, 0.3026050, 0.7100285, 0.4059509, 0.6353584, 0.3526947 ), 2, 7)) if( fuzz < max(abs(fa.none$loadings - tst))) { cat("Calculated value is not the same as test value in test WansbeekMeijer 1. Value:\n") print(fa.none$loadings, digits=18) cat("difference:\n") print(fa.none$loadings - tst, digits=18) all.ok <- FALSE } fa.varimax <- GPFoblq(fa.none$loadings, method="varimax", normalize=TRUE) # with eps=1e-8 # tst <- t(matrix(c( # 0.229695829694226694, -0.757005882905721683, # 0.325474298411086493, -0.774533969509160203, # 0.227951538606475851, -0.738861531224136225, # 0.634850649690308022, -0.299876110481063607, # 0.707312661165822032, -0.278246783076943283, # 0.789359884149245072, -0.214120439603779994, # 0.698885205896135120, -0.199081171877497243 # ), 2, 7)) # with eps=1e-5 tst <- t(matrix(c( 0.229698038368303409, -0.757005212686898243, 0.325476558225504142, -0.774533019824047542, 0.227953694341768043, -0.738860866094951829, 0.634851524619887475, -0.299874258087383661, 0.707313472988376213, -0.278244719250824557, 0.789360508873491518, -0.214118136377292989, 0.698885786741510029, -0.199079132641678647 ), 2, 7)) if( fuzz < max(abs(fa.varimax$loadings - tst))) { cat("Calculated value is not the same as test value in test WansbeekMeijer 2. Value:\n") print(fa.varimax$loadings, digits=18) cat("difference:\n") print(fa.varimax$loadings - tst, digits=18) all.ok <- FALSE } fa.oblimin <- GPFoblq(fa.none$loadings, method="oblimin", normalize=TRUE) # with eps=1e-8 # tst <- t(matrix(c( # -0.0244898894997362740, -0.8055076884898763057, # 0.0821776433220552660, -0.7883517482514345032, # -0.0194442483441249758, -0.7847120136813017233, # 0.6350106056917923514, -0.1038114236654337219, # 0.7293893902400611085, -0.0495156037400738894, # 0.8517915457391848078, 0.0588983480418694277, # 0.7504355940804637859, 0.0408946221245683056 # ), 2, 7)) # with eps=1e-5 tst <- t(matrix(c( -0.0244886312423446446, -0.8055069385602275922, 0.0821788889356081659, -0.7883509906546982693, -0.0194430219824419312, -0.7847112821295906260, 0.6350108529538124325, -0.1038111848933331444, 0.7293895650539216069, -0.0495153948664520185, 0.8517915670863017708, 0.0588984825074335624, 0.7504356301074717184, 0.0408947509009953206 ), 2, 7)) if( fuzz < max(abs(fa.oblimin$loadings - tst))) { cat("Calculated value is not the same as test value in test WansbeekMeijer 3. Value:\n") print(fa.oblimin$loadings, digits=18) cat("difference:\n") print(fa.oblimin$loadings - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/print-GPArotation.R0000644000176200001440000000720614570377620017267 0ustar liggesusers# testing that the print.GPArotation output is identical # for 2 runs of quartimin rotation, that have 2 # different looking loadings matrices wrt sign and order # the print.GPArotation should look identical Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") athl <- matrix(c( .73, -.07, .50, .82, -.01, .27, .77, -.46, -.22, .78, .17, .03, .77, .41, .13, .81, -.01, .27, .71, -.45, -.30, .82, .12, -.11, .66, -.15, -.45, .39, .76, -.40), byrow=T, ncol =3) ## z1 gives the results that have the right ordering and sign of the factors ## z2 is a random other order and sign set.seed(238) z1 <- quartimin(athl, Tmat = Random.Start(3)) head(z1$loadings) set.seed(46) z2 <- quartimin(athl, Tmat = Random.Start(3)) head(z2$loadings) # WITHOUT SORTING all.ok <- TRUE # are z1 and z2 loadings different? Test at the 5th decimal # if tst = T then there are differences (good). if tst = F then no differences (bad) tst <- max(z1$loadings - z2$loadings) > 1e-5 if (! tst) { all.ok <- F} # rotation matrix tst <- max(z1$Th - z2$Th) > 1e-5 if (! tst) { all.ok <- F} # correlation matrix tst <- max(z1$Phi - z2$Phi) > 1e-5 if (! tst) { all.ok <- F} if (! all.ok) stop("some tests FAILED before sorting") # NOW WE SORT: z1 to z1s and z2 to z2s z1s <- print(z1, sortLoadings = TRUE, rotateMat = TRUE, Table = TRUE) z2s <- print(z2, sortLoadings = TRUE, rotateMat = TRUE, Table = TRUE) all.ok <- TRUE # are z1 and z2 loadings different? Test at the 5th decimal # if tst = T then there are differences (bad). if tst = F then no differences (good) tst <- max(z1s$loadings - z2s$loadings) > 1e-5 if (tst) { all.ok <- F} # rotation matrix tst <- max(z1s$Th - z2s$Th) > 1e-5 if (tst) { all.ok <- F} # correlation matrix tst <- max(z1s$Phi - z2s$Phi) > 1e-5 if (tst) { all.ok <- F} if (! all.ok) stop("some tests FAILED after sorting") #> z1 #Oblique rotation method Quartimin converged. #Loadings: # [,1] [,2] [,3] # [1,] 0.9451 -0.0535 -0.18033 # [2,] 0.7725 0.1431 0.01187 # [3,] 0.1323 0.8617 -0.12847 # [4,] 0.5377 0.2050 0.28753 # [5,] 0.6888 -0.0555 0.44072 # [6,] 0.7665 0.1386 0.00987 # [7,] 0.0150 0.8967 -0.08931 # [8,] 0.4047 0.3792 0.32647 # [9,] -0.1056 0.7915 0.24071 #[10,] -0.0155 -0.0165 0.94994 # # [,1] [,2] [,3] #SS loadings 3.034 2.405 1.401 #Proportion Var 0.303 0.240 0.140 #Cumulative Var 0.303 0.544 0.684 # #Phi: # [,1] [,2] [,3] #[1,] 1.000 0.554 0.259 #[2,] 0.554 1.000 0.186 #[3,] 0.259 0.186 1.000 #> z2 #Oblique rotation method Quartimin converged. #Loadings: # [,1] [,2] [,3] # [1,] 0.9451 -0.0535 -0.18033 # [2,] 0.7725 0.1431 0.01187 # [3,] 0.1323 0.8617 -0.12847 # [4,] 0.5377 0.2050 0.28753 # [5,] 0.6888 -0.0555 0.44072 # [6,] 0.7665 0.1386 0.00987 # [7,] 0.0150 0.8967 -0.08930 # [8,] 0.4047 0.3792 0.32647 # [9,] -0.1056 0.7915 0.24071 #[10,] -0.0155 -0.0165 0.94994 # # [,1] [,2] [,3] #SS loadings 3.034 2.405 1.401 #Proportion Var 0.303 0.240 0.140 #Cumulative Var 0.303 0.544 0.684 # #Phi: # [,1] [,2] [,3] #[1,] 1.000 0.554 0.259 #[2,] 0.554 1.000 0.186 #[3,] 0.259 0.186 1.000 ########################################################## # RUNNING A PRINT WITHOUT ERRORS ########################################################## # data(ability.cov) # L <- loadings(factanal(factors = 2, covmat=ability.cov)) # #v <- GPFRSoblq(L, eps = 1e-7, method = "oblimin", methodArgs = list(gam = .5), randomStarts = 100) #GPArotation:::print.GPArotation(v, rotateMat = T, Table = T) #print(v, rotateMat = T, Table = T) # #GPArotation:::summary.GPArotation(v) #summary(v) GPArotation/tests/errormessages.R0000644000176200001440000000244114750537304016620 0ustar liggesusers# test for to understand if there is breaking in the code # when an error is produced. Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() all.ok <- TRUE # 1-factor model loadings vector xv <- runif(5) # Testing if single factor models will break when error is called #test 1 y <- try(GPArotation::quartimin(xv), TRUE) if (!inherits(y, "try-error")) { print("error messages: test 1 failed") all.ok <- FALSE } #test 2 y <- try(GPForth(xv, method = "quartimax"), TRUE) if (!inherits(y, "try-error")) { print("error messages: test 2 failed") all.ok <- FALSE } #test 3 y <- try(GPFoblq(xv, method = "quartimin"), TRUE) if (!inherits(y, "try-error")) { print("error messages: test 3 failed") all.ok <- FALSE } # same but with matrix instead of vector xw <- matrix(xv) #test 4 y <- try(GPForth(xw, method = "quartimax"), TRUE) if (! grep("rotation does not make sense for single factor models", attr(y, "condition")$message) ) { print("error messages: test 4 failed") all.ok <- FALSE } #test 5 y <- try(GPFoblq(xw, method = "quartimin"), TRUE) if (! grep("rotation does not make sense for single factor models", attr(y, "condition")$message) ) { print("error messages: test 5 failed") all.ok <- FALSE } if (! all.ok) stop("some tests FAILED") GPArotation/tests/MASSoblimin.R0000644000176200001440000000447614323662112016056 0ustar liggesusers Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() #require("stats") fuzz <- 1e-6 all.ok <- TRUE # test MASS 4th ed. p 322-324 data(ability.cov) ability.cov ability.FA <- factanal(factors = 1, covmat=ability.cov) (ability.FA <- update(ability.FA, factors = 2)) # ability.FA2 <- factanal(factors = 2, covmat = ability.cov) # max(abs(ability.FA2$loadings - ability.FA$loadings)) # summary(ability.FA) MASS ed.4 p 323 seems to be print not summary in R 2.0.1 ability.FA # this is default varimax rotation. There are 3rd+ digit differences with MASS tst <- t(matrix(c( 0.499437829039896530, 0.54344904693111962, 0.156070079431279873, 0.62153798991197484, 0.205786989958578748, 0.85992588538426895, 0.108530754440558652, 0.46776101732283504, 0.956242470279811574, 0.18209631992182243, 0.784768183877880943, 0.22482213687364205 ), 2, 6)) if( fuzz < max(abs(loadings(ability.FA) - tst))) { cat("Calculated value is not the same as test value in test 1. Value:\n") #print(loadings(ability.FA), digits=18) this truncates print(unclass(ability.FA$loadings), digits=18) cat("difference:\n") print(unclass(ability.FA$loadings) - tst, digits=18) all.ok <- FALSE } # differences with MASS here are a bit more than might be expected, # but there is already a difference before rotation. (oblirot <- oblimin(loadings(ability.FA))) obli2 <- factanal(factors = 2, covmat = ability.cov, rotation="oblimin") max(abs(loadings(oblirot) - loadings(obli2))) # factanal(factors = 2, covmat = ability.cov, scores = Bartlett, rotation="oblimin") tst <- t(matrix(c( 0.3863637969729337152, 0.4745113977203344047, -0.0110032278171669998, 0.6458708261423832253, -0.0262888675561207576, 0.8961123879025085781, -0.0180180060207963122, 0.4882918937716873575, 0.9900948712271664398, -0.0370729040114848238, 0.7905663749272058283, 0.0526099352008769991 ), 2, 6)) if( fuzz < max(abs(loadings(oblirot) - tst ))) { cat("Calculated value is not the same as test value in test 2. Value:\n") print(loadings(oblirot), digits=18) cat("difference:\n") print(loadings(oblirot) - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/tests/rotations.R0000644000176200001440000007202414570431011015751 0ustar liggesusers# Tests here only compare against values computed previously with this code, # to ensure there was no accidental change. It would be better to have # comparisons with known correct values. # Test for oblimax is commented out as it appears to be unstable. Sys.getenv("R_LIBS") library() require("GPArotation") search() Sys.info() require("stats") require("GPArotation") fuzz <- 1e-6 all.ok <- TRUE data(ability.cov) L <- loadings(factanal(factors = 2, covmat=ability.cov)) if( 0.001 < max(abs(varimax(L, normalize=FALSE)$loadings - Varimax(L, normalize=FALSE)$loadings))) { cat("Calculated difference exceeds tolerance\n") cat("difference:\n") print(varimax(L, normalize=FALSE)$loadings - Varimax(L, normalize=FALSE)$loadings, digits=18) all.ok <- FALSE } if( 0.01 < max(abs(varimax(L, normalize=TRUE)$loadings - Varimax(L, normalize=TRUE, eps=1e-5)$loadings))) { cat("Calculated difference exceeds tolerance\n") cat("difference:\n") print(varimax(L, normalize=TRUE)$loadings - Varimax(L, normalize=TRUE, eps=1e-5)$loadings, digits=18) all.ok <- FALSE } v <- oblimin(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- oblimin(L, gam = 1, eps=1e-8)$loadings tst <- t(matrix(c( 0.2160827, 0.5403732, -0.4787025, 1.0224006, -0.6800410, 1.4244177, -0.3758706, 0.7781390, 1.4517362, -0.5873946, 1.1002585, -0.3396290 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1-gam=1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- oblimin(L, gam = .1, eps=1e-8)$loadings tst <- t(matrix(c( 0.37893531, 0.47408606, -0.02465543, 0.65257986, -0.04530330, 0.90557045, -0.02840333, 0.49349573, 0.99740452, -0.05088932, 0.79467442, 0.04241284 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 1-gam=.1. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- quartimin(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 2. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- targetT(L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), eps=1e-5)$loadings tst <- t(matrix(c( 0.551529228817982942, 0.4905002767031292898, 0.217748645523411000, 0.6027046291262584399, 0.291173432863349457, 0.8348885228488550636, 0.154994397662456290, 0.4544843569140373241, 0.969702339393929247, 0.0850652965070581996, 0.803390575440818822, 0.1448091121037717866 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 3. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- targetT(L = L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 3L. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- targetQ(L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), eps=1e-5)$loadings tst <- t(matrix(c( 0.735795682866631218, 0.565351705145453853, 0.433590223819374398, 0.664644550038417159, 0.589924557708411568, 0.920006940799857786, 0.317543426981046928, 0.500590650032113116, 1.021758247914384077, 0.155121528590726393, 0.872521244896209747, 0.208735706420634437 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 4. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- targetQ(L = L, Target=matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 4L. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # Does not converge even with maxit=10000, but the loadings matrix is not # changing. Possibly the gradient is extremely large even very close to opt. v <- pstT(L, W = matrix(c(rep(.4,6),rep(.6,6)), 6,2), Target= matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), maxit=1000, eps=1e-5)$loadings tst <- t(matrix(c( 0.37067889993474656407, 0.638257130653133720, 0.01855112570739854416, 0.640564749523800270, 0.01576132191496706567, 0.884065831441111172, 0.00524531003824213384, 0.480158078874985073, 0.89458633399812259590, 0.383762977265515448, 0.71793428958051475064, 0.388556883222951677 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 5. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- pstT(L = L, W = matrix(c(rep(.4,6),rep(.6,6)), 6,2), Target= matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), maxit=1000, eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 5L. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # Does not converge even with maxit=10000, but the loadings matrix is not # changing. Possibly the gradient is extremely large even very close to opt. v <- pstQ(L, W = matrix(c(rep(.4,6),rep(.6,6)), 6,2), Target= matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), maxit=1000, eps=1e-5)$loadings tst <- t(matrix(c( 0.573125161748393785, 0.700868331877288475, 0.214899397066479453, 0.681727425525818886, 0.286558275327103040, 0.940272379393286339, 0.152257795885557295, 0.510481967637567036, 1.029289798076480578, 0.462598702071116141, 0.850691132520651205, 0.456859727346562328 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 6. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- pstQ(L = L, W = matrix(c(rep(.4,6),rep(.6,6)), 6,2), Target= matrix(c(rep(1,3),rep(0,6),rep(1,3)), 6,2), maxit=1000, eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 6L. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # oblimax # this is test value on one computer # tst <- t(matrix(c( # -8111059.94622692652, 8111060.62253121007, # 1495036.43465861562, -1495035.79614594672, # 2331634.63904705830, -2331633.75893370388, # 1356735.91680212389, -1356735.43916810025, # -23187491.19758165255, 23187491.68068471923, # -18357040.58573083207, 18357041.05348757654 # ), 2, 6)) # # this is test value on another computer # tst <- t(matrix(c( # 2694770.06630349346, -2694769.38999920478, # -496701.45733913727, 496702.09585180727, # -774647.63529061736, 774648.51540397422, # -450753.43529273639, 450753.91292676108, # 7703672.48495316971, -7703672.00185009185, # 6098832.71036116872, -6098832.24260441773 # ), 2, 6)) # # this does not converge on all platforms and has large differences possible a mistake ??? # v <- oblimax(L, eps=1e-5)$loadings # if( fuzz < max(abs(v - tst))) { # cat("Calculated value is not the same as test value in test rotations 7. Value:\n") # print(v, digits=18) # cat("difference:\n") # print(v - tst, digits=18) # all.ok <- FALSE # } v <- entropy(L, maxit=3000, eps=1e-5)$loadings tst <- t(matrix(c( 0.528292107548243184, 0.515443945340967824, 0.189686511729033253, 0.612116304198454975, 0.252311894464850861, 0.847442931117894815, 0.133843268148035738, 0.461156452364903380, 0.964740133927989407, 0.129750551769587635, 0.795847094000000532, 0.181751199795689433 ), 2, 6)) if( 0.01 < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 8. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- quartimax(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.534714740804540178, 0.508778102568043678, 0.197348140750149392, 0.609689309353509956, 0.262919828098457153, 0.844212045390758559, 0.139616102327241837, 0.459441658926639795, 0.966291466215733252, 0.117641548844535412, 0.798063848020893585, 0.171756193883937508 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 9. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- Varimax(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.515866523962843160, 0.527879475961036904, 0.175054634278874244, 0.616460231981747930, 0.232057748479543163, 0.853211588623112749, 0.122822468397975171, 0.464213243286899446, 0.961376376417989453, 0.152689863976982837, 0.791292800869773050, 0.200653429940987366 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 10. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- simplimax(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.3384175759313114429, 0.508414890494446547464, -0.0654601124161610648, 0.670992229004664153535, -0.1016231721735353366, 0.930535379393095940515, -0.0589933707274080121, 0.506904360351960181497, 0.9733094402675376289, 0.000234046050254643859, 0.7702037184085044341, 0.085651123319384916965 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 11. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bentlerT(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.523583611303327312, 0.520226117818945788, 0.184113022124463677, 0.613815719643687197, 0.244596116053327067, 0.849702038129718673, 0.129644684715025493, 0.462354355134084738, 0.963520501269179652, 0.138517057902201340, 0.794161628656258278, 0.188979901644201559 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 12. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bentlerQ(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3801726240258240241, 0.4741208368044214638, -0.0223632969057368826, 0.6514196922540864687, -0.0421105927111659756, 0.9039359851665277334, -0.0266594447192576613, 0.4925968005718689424, 0.9961524457620027917, -0.0485973498906049697, 0.7939648477384558811, 0.0440983921679098251 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 13. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- tandemI(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.615424480780047745, 0.4074649925368262759, 0.300894306348887419, 0.5658002819054848143, 0.406455233467338028, 0.7852483408305571677, 0.217785179074990981, 0.4279590047675180808, 0.971977129465111611, -0.0530960591067626969, 0.815800376450207976, 0.0295946184147908228 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 14. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- tandemII(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.512160139332842212, 0.531476249107136312, 0.170736763115044710, 0.617670057812827134, 0.226081850628144149, 0.854814488884392154, 0.119571200821562001, 0.465061309851099225, 0.960284416460420398, 0.159413208985883820, 0.789869387186175276, 0.206185467095899383 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 15. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- geominT(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.572197044101002361, 0.4662247895688098054, 0.243573415560656120, 0.5927388411683653935, 0.326956608263186954, 0.8215352639437966120, 0.174476792179181994, 0.4473668997335142894, 0.972471249855535680, 0.0431091626026945812, 0.808894688433769660, 0.1099794466209375043 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 16. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- geominQ(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.39672053553904490508, 0.4713295988080449250, 0.00424452688463150020, 0.6389466007374070555, -0.00510976786312981532, 0.8864521406378518265, -0.00646959173137159373, 0.4830101828530461994, 0.98709860078485589518, -0.0318959930081098297, 0.79011178369962709045, 0.0558689642678330683 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 17. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bigeominT(L, eps = 1e-5, delta = 0.01)$loadings tst <- t(matrix(c( 0.735864675930537948, 0.0572554836159610558, 0.537832587849186305, 0.3484299786828288781, 0.736693622718023078, 0.4889819217894930681, 0.398234350199199783, 0.2683070932862005598, 0.823835376232448180, -0.5185113350380095021, 0.727481839163037991, -0.3703731487890759011 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 16-bigeominT. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bigeominQ(L, eps = 1e-5, delta = 0.01)$loadings tst <- t(matrix(c( 0.735864459785110725, 0.0572566968425438083, 0.537831272508815683, 0.3484308654124375071, 0.736691776787216535, 0.4889831363831567135, 0.398233337326754422, 0.2683077498589007681, 0.823837333630433988, -0.5185099767738823306, 0.727483237333638733, -0.3703719493837349663 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 17-bigeominQ. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- cfT(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.534721263659975854, 0.508771247100584523, 0.197355957387199576, 0.609686779159006154, 0.262930651479430233, 0.844208674501022327, 0.139621992686633722, 0.459439868910532512, 0.966292974385164483, 0.117629160286744874, 0.798066049992627313, 0.171745962120156664 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 18. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- cfQ(L, eps=1e-8)$loadings tst <- t(matrix(c( 0.3863615904740822504, 0.4745127741495974161, -0.0110059418769087539, 0.6458720769633764514, -0.0262926272350604423, 0.8961141105684561348, -0.0180200526810754824, 0.4882928281695405048, 0.9900944939102318543, -0.0370718282544326011, 0.7905657274265397438, 0.0526109550054999417 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 19. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- infomaxT(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.495330443338021176, 0.547195361446864537, 0.151384273205308784, 0.622695868320644275, 0.199304253086364791, 0.861451466010626055, 0.105004533733904976, 0.468565194910632365, 0.954843809781045660, 0.189293503899924942, 0.783052579543945471, 0.230726576980168713 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 20. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- infomaxQ(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.39327554287862442894, 0.4693137508305071925, -0.00319802321222481794, 0.6422985517185823001, -0.01549245038490981718, 0.8912279460026399924, -0.01214605901641467763, 0.4856544522916727002, 0.99260028929193111491, -0.0433225495465055510, 0.79356458059567791530, 0.0471559021503157039 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 21. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- mccammon(L, eps=1e-5)$loadings tst <- t(matrix(c( 0.4293472299617892007, 0.600363196582340275, 0.0790140496845253004, 0.635943490060206229, 0.0992523811009183854, 0.878618107277518656, 0.0506062164774049028, 0.477512622702450096, 0.9268544198491108776, 0.297488850382792269, 0.7514463663627769519, 0.318958389348199534 ), 2, 6)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ###### ADDED IN NOVEMBER 2022 FOR EQUAMAX, PARSIMAX, VARIMIN, OBLIMAX data(Thurstone) v <- equamax(box26, eps=1e-5)$loadings tst <- t(matrix(c( 0.511813618717971597, 0.1252460667724786814, 0.835031881099661200, 0.211275278125612587, 0.9469860693462274215, 0.024701038786419674, 0.923671387190205140, 0.1861505968810791833, -0.278366886980007111, 0.414270797796799317, 0.7243752493532077397, 0.530526346393166759, 0.927099794400001564, 0.1710560637343615797, 0.314400690653154735, 0.685509679739711331, 0.6873945075387188908, -0.212674093365320949, 0.500975325417812756, 0.4985944480056956341, 0.693100497576226382, 0.350251174602310256, 0.8631423492204841619, 0.303299191676876356, 0.809196181501955492, 0.1468111894018074293, 0.540855816747015439, 1.051940508364259674, 0.2023337382785123650, 0.126016765617061266, 0.528246625368315792, 0.8145581663496035407, -0.154555803579673606, 0.791784749686200273, 0.5353191515116044741, -0.254010464723911089, 0.283760830721282831, -0.7132278971933163625, 0.633221728633476699, -0.283760830721282831, 0.7132278971933163625, -0.633221728633476699, -0.351981708826951678, 0.0145585781278812498, 0.920862598031950474, 0.351981708826951678, -0.0145585781278812498, -0.920862598031950474, -0.641238077659381234, 0.7340358583767647715, 0.211813801195267382, 0.641238077659381234, -0.7340358583767647715, -0.211813801195267382, 0.370916272566192251, 0.7781992933002486179, 0.457012011497068604, 0.943267697340363864, 0.1458935486092693412, 0.269085717994103968, 0.683769139477491628, 0.6932804480935084168, -0.193612975261152009, 0.375506314902942506, 0.7683789003013250518, 0.444462454027040654, 0.921697465732450816, 0.1542330203892136042, 0.244709944799956780, 0.664806997738585315, 0.6918110118942031317, -0.165931249557543792, 0.748952844572093657, 0.5985308972371030656, 0.239842451746804020, 0.716556890444816297, 0.6343221919993241587, 0.139425892477791219 ), 3, 26)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ### SAME FOR CRAWFORD FERGUSON WITH KAPPA = m / (2 * p) = 3 / (2 * 26) v <- cfT(box26, kappa = (3 / (2 * 26)), eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- parsimax(box26, eps=1e-5)$loadings tst <- t(matrix(c( 0.7201835790622810318, -0.2820790149262949464, 0.6137467244615277817, -0.0679423851913938670, 0.6010788795762025405, 0.7590243822315081434, 0.6707172136894012926, 0.7174085874409354968, -0.0277909684381195121, 0.3564975652920873705, 0.2169149780725644350, 0.8964682806594965747, 0.8905375652375422391, 0.2961288407436278303, 0.3269136806262873951, 0.3419238671745732927, 0.8395902605853544642, 0.4072361273101923196, 0.5551801796495600128, 0.0181442676741417341, 0.8193941381745735164, 0.1791815177720131602, 0.4232257028261393605, 0.8651329309165379788, 0.8735154515073240145, 0.0707778916688399651, 0.4481498031114935499, 0.9254209439635597834, 0.5018907196720771013, 0.2347334274887991901, 0.1871266535649374341, 0.7975561080494565358, 0.5434380093797205324, 0.4642991413806251688, 0.8329607560592182658, 0.2619421428072832847, 0.6793187635833454197, -0.7070938474525871875, -0.1694942722875707464, -0.6793187635833454197, 0.7070938474525871875, 0.1694942722875707464, 0.0126413340577520572, -0.7959999181796318934, 0.5816488003350992475, -0.0126413340577520572, 0.7959999181796318934, -0.5816488003350992475, -0.7005089039174136056, -0.0349340740508546910, 0.7091733821870598309, 0.7005089039174136056, 0.0349340740508546910, -0.7091733821870598309, 0.2764675266718980007, 0.2782887989292237019, 0.8933946782281918519, 0.8957285854821546156, 0.3212930506383097073, 0.2790825626255922787, 0.3455615085575033385, 0.8287155760723180498, 0.4236528505493507568, 0.2788032457489866833, 0.2837361452757249936, 0.8779069142135194070, 0.8654448252950356357, 0.3331277242202841382, 0.2706070467039994321, 0.3390107603224090660, 0.7999236919074760310, 0.4396564471388281214, 0.5855440811683275681, 0.5029973433114328651, 0.6171108503586539840, 0.5106729412991620753, 0.5782224406112924653, 0.5832066153589001711 ), 3, 26)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } ### SAME FOR CRAWFORD FERGUSON WITH KAPPA = (m - 1) / (p + m - 2) = (3 -1) / (26 + 3 - 2) v <- cfT(box26, kappa=( (3-1)/(26+3-2) ), eps=1e-5)$loadings if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } data(Harman, package= "GPArotation") v <- varimin(Harman8, eps=1e-5)$loadings tst <- t(matrix(c( 0.800626657046876855, -0.452452158825595752, 0.783606930490612252, -0.524447498313301397, 0.742635936060292656, -0.522609669324872517, 0.768357486963803682, -0.455227165519225097, 0.818696625686402668, 0.444445536696790211, 0.702064973637186673, 0.410429985249392060, 0.623283524595303340, 0.401857745935120247, 0.668480210595655877, 0.287458184858228272 ), 2, 8)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- oblimax(Harman8, eps=1e-5)$loadings tst <- t(matrix(c( 0.93395421734409445058, -0.0302013026726007383, 0.99243032312927881300, -0.1121899246869615951, 0.96509469978483286567, -0.1322258547171115683, 0.91647702431117861188, -0.0502569243958834178, 0.08441855308346873921, 0.8875309317276611765, 0.04427084251510177149, 0.7907585046311147448, 0.00332736511424391868, 0.7399752420126202157, 0.14133359391312094733, 0.6483050831171799366 ), 2, 8)) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 22. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # TAKEN FROM THE EXAMPLES IN THE DOCUMENTATION OF echelon data(WansbeekMeijer) fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, rotation="none") v <- echelon(fa.unrotated$loadings)$loadings tst <- matrix(c( 0.7910866, 0.000000000, 0.8356693, 0.086562877, 0.7732175, 0.003599155, 0.4712891, 0.520430204, 0.4716313, 0.596050663, 0.4340904, 0.693182608, 0.3934293, 0.610972389 ), ncol = 2, nrow = 7, byrow=7) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 23. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # TAKEN FROM THE EXAMPLES IN THE DOCUMENTATION OF eiv v <- eiv(fa.unrotated$loadings)$loadings tst <- matrix(c( 1.0000000, 0.0000000, 0.0000000, 1.0000000, 0.9334902, 0.0415785, -5.7552381, 6.0121639, -6.6776277, 6.8857539, -7.9104168, 8.0078509, -6.9585766, 7.0581340 ), ncol = 2, nrow = 7, byrow=7) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations 24. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } # BIFACTOR data(WansbeekMeijer) fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, rotation="none") v <- bifactorT(fa.unrotated$loadings)$loadings tst <- matrix(c( 0.605259846207760410, 0.50926282550276680272, -0.0326441696928007064, 0.674848533008883589, 0.49892997353743967492, 0.0360559565502543422, 0.585307624074672961, 0.50452427555859225006, -0.0116655363009391944, 0.569143997010692737, 0.07907134469847305891, 0.4496106087464453172, 0.639591951170676909, -0.00317621525167442742, 0.3903956610664964244, 0.644285080202307459, -0.06194060671151470354, 0.4889273316625122323, 0.894466584777320994, -0.44135797428894490979, -0.0115183403900823399 ), ncol = 3, nrow = 7, byrow=7) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations bifactorT. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } v <- bifactorQ(fa.unrotated$loadings)$loadings tst <- matrix(c( 0.639628269247016989, 0.4617887681341789063, -0.03408526722627130967, 0.708202536330854282, 0.4531948277009248405, 0.03509478386512845938, 0.619356995328652293, 0.4604997427039814184, -0.01303680319918767982, 0.572539420889668138, 0.0819716834028682007, 0.45225086629811012129, 0.637128483915923693, -0.0107264127434737802, 0.39334646255480443244, 0.637511978242973232, -0.0601392817604798208, 0.49259101012935263553, 0.861232867683719872, -0.5044079449050349329, -0.00751942773799910234 ), ncol = 3, nrow = 7, byrow=7) if( fuzz < max(abs(v - tst))) { cat("Calculated value is not the same as test value in test rotations bifactorQ. Value:\n") print(v, digits=18) cat("difference:\n") print(v - tst, digits=18) all.ok <- FALSE } cat("tests completed.\n") if (! all.ok) stop("some tests FAILED") GPArotation/MD50000644000176200001440000000511614776556662013004 0ustar liggesusers85ea49f951ebe37fa3b4584cd163a16e *DESCRIPTION 7131086477488936421b178769ad4952 *NAMESPACE c9827c8c8b31215714aec421b5e3274b *NEWS 8a4132c99961e9e87f8313ab3c5dbb3d *R/GPF.R 9387383de4cdef2b30c841a672d11c48 *R/GPFRS.R 9f4b96a2dfee75edb4a4901f065918f0 *R/NormalizingWeight.R 86debeb4d542123494aea1387e2fcb3e *R/RandomStart.R b1f7933b2f25386768593ef3dc82606c *R/echelon.R 2764be0f9d533d4488f9a91590025227 *R/eiv.R a6f52ca8790934bedbb206e7304eef99 *R/lp.R df85dc6a7f25b195782441290afcfb21 *R/printsummary.R c2edd6507be8fe5583834d69e2362751 *R/rotations.R e1c8d8c416732f220676828c09237dbd *build/vignette.rds 7f348dc5f8a2ab65e2dea15b43ed84df *data/Harman.rda 1f218e36ca05bd6e16c50d212aebd6df *data/Thurstone.rda 08afb21d4b7673c7deb2269a3df860e9 *data/WansbeekMeijer.rda e8a386c68402ae9d8f05c7a9bea60c51 *inst/CITATION e7704312a929f314ffb264a2a6f17d8a *inst/doc/GPAguide.R 93a42b0cea98bd6d60d2ed50c07147ba *inst/doc/GPAguide.Stex 2cb7a341fcb86d948baf5771c6d068f1 *inst/doc/GPAguide.pdf f77834c2bfa540de1868e7e103e3db92 *inst/doc/GPArotateDF.R e150ab1c5873face932f18d69bb87090 *inst/doc/GPArotateDF.Stex bc774bde51ad981229450f094b9f6d95 *inst/doc/GPArotateDF.pdf caec51153b9e6cc9488b5d1791f98a91 *man/00.GPArotation.Rd 0061f266c6eddb26abe266c75059ab58 *man/GPA.Rd b07d349e1b8f30647094651a5d2a0afd *man/Harman.Rd 5c36d60ed084d2698b5a0543ecbbd4df *man/NormalizingWeight.Rd b2a9a5bceb07824a7557396819a1e891 *man/Random.Start.Rd 1b2feb8eed2d7acc01d9bf6c4b926c6b *man/Thurstone.Rd c172806d91b6ead4f5586323cc3b0d68 *man/WansbeekMeijer.Rd 0592722b702e6921a3b24c2c3a242289 *man/echelon.Rd 483715e52b2dd4468f5294330eba56bf *man/eiv.Rd 8f81faaedd46cd41dabc166b84e61958 *man/lp.Rd 76236e632de875edc2edecefe65d0f57 *man/print.GPArotation.Rd 197afbffbeb75b98f0ce48898c7fdaf5 *man/rotations.Rd 9fc2701b78eebdeeb8169786dd7642fe *man/vgQ.Rd 676d839d54d47a340f6ba36a5934221b *tests/Harman.R 9f69c980178c705014f0b982c1200f0f *tests/Jennrich2002.R b23cf38f1863aec555c6f4c910c41d66 *tests/KaiserNormalization.R 781b18f963b96896b8db5224e710ebd1 *tests/MASSoblimin.R 46d697ec9edc75bcc1822dbdae7c3f0f *tests/Revelle.R 4a45741f72f718179f8070cf11681727 *tests/Thurstone.R 721c8721d216481c1af1e14680c61627 *tests/WansbeekMeijer.R b2ebe64dcc2f5333e5ad12bd4a32050c *tests/errormessages.R 00f65da9bb5c6ee3fe919a4ab8c87932 *tests/lp.R 991d48c2a00529e0916c21ea11dd704b *tests/print-GPArotation.R 9af15ef32c1722d36e97e4ae5b17ee71 *tests/rotations.R ec436c7f4294c7c4b79b9d3967443df6 *tests/rotationsRS.R 0ff9640302263cb72e34844d7a090dcd *tests/varimaxVarimax.R 93a42b0cea98bd6d60d2ed50c07147ba *vignettes/GPAguide.Stex e150ab1c5873face932f18d69bb87090 *vignettes/GPArotateDF.Stex GPArotation/R/0000755000176200001440000000000014767556440012664 5ustar liggesusersGPArotation/R/NormalizingWeight.R0000644000176200001440000000271214524247121016432 0ustar liggesusers# Kaiser normalization #NormalizingWeight <- function(A, normalize=FALSE){ # if ("function" == mode(normalize)) normalize <- normalize(A) # if (is.logical(normalize)){ # if (normalize) normalize <- sqrt(rowSums(A^2)) # else return(array(1, dim(A))) # } # if (is.vector(normalize)) # {if(nrow(A) != length(normalize)) # stop("normalize length wrong in NormalizingWeight") # return(array(normalize, dim(A))) # } # stop("normalize argument not recognized in NormalizingWeight") #} # # # Version below submitted by Kim-Laura Speck, Uni Kassel, 25 October 2023 # # avoid NaNs in matrix A by adding machine precision values to zeros NormalizingWeight <- function(A, normalize=FALSE){ # custom function to normalize; input from user if ("function" == mode(normalize)) normalize <- normalize(A) # Kaiser normalization if (is.logical(normalize)){ if (normalize) { normalize <- sqrt(rowSums(A^2)) # this is only a vector # avoid division by zero exceptions by checking that double != exactly zero idxZero <- which(normalize == 0) # add machine precision to values that are exactly zero normalize[idxZero] <- normalize[idxZero] + .Machine$double.eps } else return(array(1, dim(A))) } if (is.vector(normalize)) {if(nrow(A) != length(normalize)) stop("normalize length wrong in NormalizingWeight") return(array(normalize, dim(A))) } stop("normalize argument not recognized in NormalizingWeight") } GPArotation/R/GPF.R0000644000176200001440000000711514344535050013410 0ustar liggesusers# legacy functions that contain the actual GP algorithms # these function shall not be changed # functions have not changes since 2008 GPForth <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL){ if((!is.logical(normalize)) || normalize) { W <- NormalizingWeight(A, normalize=normalize) normalize <- TRUE A <- A/W } if(1 >= ncol(A)) stop("rotation does not make sense for single factor models.") al <- 1 L <- A %*% Tmat #Method <- get(paste("vgQ",method,sep=".")) #VgQ <- Method(L, ...) Method <- paste("vgQ",method,sep=".") VgQ <- do.call(Method, append(list(L), methodArgs)) G <- crossprod(A,VgQ$Gq) f <- VgQ$f Table <- NULL #set initial value for the unusual case of an exact initial solution VgQt <- do.call(Method, append(list(L), methodArgs)) for (iter in 0:maxit){ M <- crossprod(Tmat,G) S <- (M + t(M))/2 Gp <- G - Tmat %*% S s <- sqrt(sum(diag(crossprod(Gp)))) Table <- rbind(Table, c(iter, f, log10(s), al)) if (s < eps) break al <- 2*al for (i in 0:10){ X <- Tmat - al * Gp UDV <- svd(X) Tmatt <- UDV$u %*% t(UDV$v) L <- A %*% Tmatt #VgQt <- Method(L, ...) VgQt <- do.call(Method, append(list(L), methodArgs)) if (VgQt$f < (f - 0.5*s^2*al)) break al <- al/2 } Tmat <- Tmatt f <- VgQt$f G <- crossprod(A,VgQt$Gq) } convergence <- (s < eps) if ((iter == maxit) & !convergence) warning("convergence not obtained in GPForth. ", maxit, " iterations used.") if(normalize) L <- L * W dimnames(L) <- dimnames(A) r <- list(loadings=L, Th=Tmat, Table=Table, method=VgQ$Method, orthogonal=TRUE, convergence=convergence, Gq=VgQt$Gq) class(r) <- "GPArotation" r } GPFoblq <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL){ if(1 >= ncol(A)) stop("rotation does not make sense for single factor models.") if((!is.logical(normalize)) || normalize) { W <- NormalizingWeight(A, normalize=normalize) normalize <- TRUE A <- A/W } al <- 1 L <- A %*% t(solve(Tmat)) #Method <- get(paste("vgQ",method,sep=".")) #VgQ <- Method(L, ...) Method <- paste("vgQ",method,sep=".") VgQ <- do.call(Method, append(list(L), methodArgs)) G <- -t(t(L) %*% VgQ$Gq %*% solve(Tmat)) f <- VgQ$f Table <- NULL #Table <- c(-1,f,log10(sqrt(sum(diag(crossprod(G))))),al) #set initial value for the unusual case of an exact initial solution VgQt <- do.call(Method, append(list(L), methodArgs)) for (iter in 0:maxit){ Gp <- G - Tmat %*% diag(c(rep(1,nrow(G)) %*% (Tmat*G))) s <- sqrt(sum(diag(crossprod(Gp)))) Table <- rbind(Table,c(iter,f,log10(s),al)) if (s < eps) break al <- 2*al for (i in 0:10){ X <- Tmat - al*Gp v <- 1/sqrt(c(rep(1,nrow(X)) %*% X^2)) Tmatt <- X %*% diag(v) L <- A %*% t(solve(Tmatt)) #VgQt <- Method(L, ...) VgQt <- do.call(Method, append(list(L), methodArgs)) improvement <- f - VgQt$f if (improvement > 0.5*s^2*al) break al <- al/2 } Tmat <- Tmatt f <- VgQt$f G <- -t(t(L) %*% VgQt$Gq %*% solve(Tmatt)) } convergence <- (s < eps) if ((iter == maxit) & !convergence) warning("convergence not obtained in GPFoblq. ", maxit, " iterations used.") if(normalize) L <- L * W dimnames(L) <- dimnames(A) # N.B. renaming Lh to loadings in specificific rotations # uses fact that Lh is first. r <- list(loadings=L, Phi=t(Tmat) %*% Tmat, Th=Tmat, Table=Table, method=VgQ$Method, orthogonal=FALSE, convergence=convergence, Gq=VgQt$Gq) class(r) <- "GPArotation" r } GPArotation/R/lp.R0000644000176200001440000001722514767556033013427 0ustar liggesusers# The functions for iterative re-weighted least squares for Lp rotation were provided by # Xinyi Liu. The functions include an inner and an outer loop for each iteration. # Adding randomstarts then adds another loop for random starts inclusion. # lpQ and lpT are wrapper function for random starts. This function has to exist separately from the # other RS functions b/c of the call to the lpT.GPFoblq and lpT.GPForth functions. # Generalizing this function to work with the existing GPFRSoblq would involve a # change to the legacy code for GPFoblq to include an if-then calling for lp. # Since I did not want to change the legacy code, the RS function has to exist # separately. Coen Bernaards, February 2025. # 4 functions in the lp.R file: # lpQ: the wrapper function for oblique Lp rotation # GPFoblq.lp: the main Lp GPFoblq function # lpT: the wrapper for orthogonal rotation # GPForth.lp: the main Lp GPForth function # lpQ is oblique rotation lpQ<-function (A, Tmat = diag(ncol(A)),p=1, normalize = FALSE, eps = 1e-05, maxit = 1000, randomStarts = 0, gpaiter = 5) { # message("Warnings may appear, but as long as the convergence in the output value is TRUE, the warnings can be ignored.") if (0 < randomStarts) { Tmat <- Random.Start(ncol(A)) } if (normalize){ message("Normalization is not recommended for Lp rotation. Use may have unexpected results") } r <- GPFoblq.lp(A,Tmat = Tmat, p,normalize = normalize, eps = eps, maxit = maxit, gpaiter = gpaiter) if (randomStarts > 1) { #3 Qvalues <- sum(abs(r$loadings)^p) Qvalues <- sum(abs(r$loadings)^p)/nrow(A) Qmin <- Qvalues Qconverged <- r$convergence for (inum in 2:randomStarts) { gpout <-GPFoblq.lp(A,Tmat = Random.Start(ncol(A)), p,normalize = normalize, eps = eps, maxit = maxit, gpaiter = gpaiter) Qvalues <- c(Qvalues, sum(abs(gpout$loadings)^p)/nrow(A)) Qconverged <- c(Qconverged, gpout$convergence) if (sum(abs(gpout$loadings)^p)/nrow(A) < Qmin) { r <- gpout #3 Qmin <- sum(abs(gpout$loadings)^p) Qmin <- sum(abs(gpout$loadings)^p)/nrow(A) } } Qmin <- eps * round(Qmin * 1/eps, 0) Qvalues <- eps * round(Qvalues * 1/eps, 0) Qvaluessame <- Qvalues == Qmin randStartChar <- c(randomStarts, sum(Qconverged), sum(Qvaluessame), length(unique(Qvalues))) names(randStartChar) <- c("randomStarts", "Converged", "atMinimum", "localMins") r <- list(loadings = r$loadings, Phi = r$Phi, Th = r$Th, Table = r$Table, method = r$method, orthogonal = FALSE, convergence = r$convergence, Gq = r$Gq, randStartChar = randStartChar, Qvalues = Qvalues) class(r) <- "GPArotation" } dimnames(r$Phi) <- list(colnames(r$loadings), colnames(r$loadings)) return(r) } GPFoblq.lp <-function (A, Tmat = diag(rep(1, ncol(A))),p=1, normalize = FALSE, eps = 1e-05,maxit = 10000, gpaiter = 5) { j_sum <- 0 start_time <- proc.time() convergence <- FALSE #2 W <- (A^2 + eps)^(p/2 - 1) W <- ((A%*%t(solve(Tmat)))^2 + eps)^(p / 2 - 1)# corrected initial weights for (it in 1:maxit) { # r <- GPFoblq(A, Tmat = Tmat, normalize = normalize, eps = eps, # maxit = gpaiter, method = "lp.wls", methodArgs = list(W = W)) r <- suppressWarnings(GPFoblq(A, Tmat = Tmat, normalize = normalize, eps = eps, maxit = gpaiter, method = "lp.wls", methodArgs = list(W = W))) T_new <- r$Th L <- r$loadings k <- nrow(r$Table) ft <- r$Table[k, 2] j <- r$Table[k, 1] j_sum <- j_sum + j W <- (L^2 + eps)^(p/2 - 1) if (max(abs(L - A %*% solve(t(Tmat)))) < eps) { Tmat <- T_new convergence <- TRUE break } Tmat <- T_new } Phi <- t(Tmat) %*% Tmat dimnames(Phi) <- list(colnames(A), colnames(A)) Table <- data.frame(iter = it, f = sum(abs(L)^p)/nrow(L), time = proc.time()[3] - start_time[3]) r <- list(loadings = L, Phi = Phi, Th = Tmat, Table = Table, method = paste0("Lp rotation, p=", p), orthogonal = FALSE, convergence = convergence) class(r) <- "GPArotation" return(r) } # lpT for Orthogonal Rotation lpT<-function (A, Tmat = diag(ncol(A)),p=1, normalize = FALSE, eps = 1e-05, maxit = 1000, randomStarts = 0,gpaiter = 5) { # message("Warnings may appear, but as long as the convergence in the output value is TRUE, the warnings can be ignored.") if (0 < randomStarts) { Tmat <- Random.Start(ncol(A)) } if (normalize){ message("Normalization is not recommended for Lp rotation. Use may have unexpected results") } r <- GPForth.lp(A,Tmat = Tmat, p,normalize = normalize, eps = eps, maxit = maxit, gpaiter = gpaiter) if (randomStarts > 1) { Qvalues <- sum(abs(r$loadings)^p)/nrow(A) Qmin <- Qvalues Qconverged <- r$convergence for (inum in 2:randomStarts) { gpout <-GPForth.lp(A,Tmat = Random.Start(ncol(A)), p,normalize = normalize, eps = eps, maxit = maxit, gpaiter = gpaiter) Qvalues <- c(Qvalues, sum(abs(gpout$loadings)^p)/nrow(A)) Qconverged <- c(Qconverged, gpout$convergence) #3 if (sum(abs(gpout$loadings)^p) < Qmin) { if (sum(abs(gpout$loadings)^p)/nrow(A)< Qmin) { r <- gpout #3 Qmin <- sum(abs(gpout$loadings)^p) Qmin <- sum(abs(gpout$loadings)^p)/nrow(A) } } Qmin <- eps * round(Qmin * 1/eps, 0) Qvalues <- eps * round(Qvalues * 1/eps, 0) Qvaluessame <- Qvalues == Qmin randStartChar <- c(randomStarts, sum(Qconverged), sum(Qvaluessame), length(unique(Qvalues))) names(randStartChar) <- c("randomStarts", "Converged", "atMinimum", "localMins") r <- list(loadings = r$loadings, Phi = r$Phi, Th = r$Th, Table = r$Table, method = r$method, orthogonal = TRUE, convergence = r$convergence, Gq = r$Gq, randStartChar = randStartChar, Qvalues = Qvalues) class(r) <- "GPArotation" } dimnames(r$Phi) <- list(colnames(r$loadings), colnames(r$loadings)) return(r) } GPForth.lp <-function (A, Tmat = diag(rep(1, ncol(A))),p=1, normalize = FALSE, eps = 1e-05,maxit = 10000, gpaiter = 5) { j_sum <- 0 start_time <- proc.time() convergence <- FALSE #2 W <- (A^2 + eps)^(p/2 - 1) W <- ((A%*%t(solve(Tmat)))^2 + eps)^(p / 2 - 1)# corrected initial weights for (it in 1:maxit) { #r <- GPForth(A, Tmat = Tmat, normalize = normalize, eps = eps, # maxit = gpaiter, method = "lp.wls", methodArgs = list(W = W)) r <- suppressWarnings(GPForth(A, Tmat = Tmat, normalize = normalize, eps = eps, maxit = gpaiter, method = "lp.wls", methodArgs = list(W = W))) T_new <- r$Th L <- r$loadings k <- nrow(r$Table) ft <- r$Table[k, 2] j <- r$Table[k, 1] j_sum <- j_sum + j W <- (L^2 + eps)^(p/2 - 1) if (max(abs(L - A %*% solve(t(Tmat)))) < eps) { Tmat <- T_new convergence <- TRUE break } Tmat <- T_new } Phi <- t(Tmat) %*% Tmat dimnames(Phi) <- list(colnames(A), colnames(A)) Table <- data.frame(iter = it, f = sum(abs(L)^p)/nrow(L), time = proc.time()[3] - start_time[3]) r <- list(loadings = L, Phi = Phi, Th = Tmat, Table = Table, method = paste0("Lp rotation, p=", p), orthogonal = TRUE, convergence = convergence) class(r) <- "GPArotation" return(r) } GPArotation/R/printsummary.R0000644000176200001440000000563614557553777015602 0ustar liggesusers# summaries for GPArotation # S3 class functions # print.GPArotation # summary.GPArotation # print.summary.GPArotation print.GPArotation <- function (x, digits=3L, sortLoadings=TRUE, rotateMat=FALSE, Table=FALSE, ...){ cln <- colnames(x$loadings) # Based on Fungible faSort and orderFactors ifelse(x$orthogonal, vx <- colSums(x$loadings^2), vx <- diag(x$Phi %*% t(x$loadings) %*% x$loadings)) if (sortLoadings){ vxo <- order(vx, decreasing = TRUE) vx <- vx[vxo] Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vxo] x$Th <- x$Th %*% Dsgn x$loadings <- x$loadings %*% Dsgn if ("Phi" %in% names(x)) { x$Phi <- t(Dsgn) %*% x$Phi %*% Dsgn } colnames(x$loadings) <- cln } cat(if(x$orthogonal)"Orthogonal" else "Oblique") cat(" rotation method", x$method) cat((if(!x$convergence)" NOT" ), "converged") cat(if ("randStartChar" %in% names(x)) " at lowest minimum.\n" else ".\n") if ("randStartChar" %in% names(x)){ cat("Of ",x$randStartChar[1]," random starts ",round(100 * x$randStartChar[2] / x$randStartChar[1]),"% converged, ", sep="") cat(round(100 * x$randStartChar[3] / x$randStartChar[1]),"% at the same lowest minimum.\n", sep="") if (x$randStartChar[4] > 1){ cat("Random starts converged to ",x$randStartChar[4], " different local minima\n", sep="") } } cat(if ("randStartChar" %in% names(x)) "Loadings at lowest minimum:\n" else "Loadings:\n") print(x$loadings, digits=digits) varex <- rbind(`SS loadings` = vx) colnames(varex) <- cln if (is.null(attr(x, "covariance"))) { varex <- rbind(varex, `Proportion Var` = vx/nrow(x$loadings)) varex <- rbind(varex, `Cumulative Var` = cumsum(vx/nrow(x$loadings))) } cat("\n") print(round(varex, digits)) if(!x$orthogonal){ dimnames(x$Phi) <- list(cln, cln) cat("\nPhi:\n") print(x$Phi, digits=digits) } if(rotateMat){ cat("\nRotating matrix:\n") print(t(solve(x$Th)), digits=digits) } if(Table){ cat("\nIteration table:\n") print(x$Table, digits=digits) } invisible(x) } summary.GPArotation <- function (object, digits=3L, Structure=TRUE, ...){ r <- list(loadings=object$loadings, Phi=object$Phi, method=object$method, orthogonal=object$orthogonal, convergence=object$convergence, iters= rev(object$Table[, 1])[1], Structure=Structure, digits=digits) class(r) <- "summary.GPArotation" r } print.summary.GPArotation <- function (x, ...){ cat(ifelse(x$orthogonal, "Orthogonal", "Oblique")) cat(" rotation method", x$method) if(!x$convergence) cat(" NOT" ) cat(" converged in ", x$iters, " iterations.\n", sep="") prstr <- !x$orthogonal && x$Structure cat(ifelse(prstr, "Pattern (loadings):\n", "Loadings:\n")) print(x$loadings, digits=x$digits) if (prstr){ cat("\nStructure:\n") print(x$loadings %*% x$Phi, digits=x$digits) } } GPArotation/R/eiv.R0000644000176200001440000000114714570234527013564 0ustar liggesuserseiv <- function(L, identity=seq(NCOL(L)), ...){ A1 <- L[ identity, , drop=FALSE] g <- solve(A1) if(1e-14 < max(abs(diag(1, length(identity)) - A1 %*% g))) warning("Inverse is not well conditioned. Consider setting identity to select different rows.") B <- array(NA, dim(L)) B[identity, ] <- diag(1, length(identity)) B[-identity,] <- L[-identity,, drop=FALSE] %*% g dimnames(B) <- list(dimnames(L)[[1]], paste("factor", seq(NCOL(L)))) r <- list(loadings=B, Th=t(A1), method="eiv", orthogonal=FALSE, convergence=TRUE, Phi= tcrossprod(A1)) class(r) <- "GPArotation" r } GPArotation/R/echelon.R0000644000176200001440000000206114570234575014415 0ustar liggesusersechelon <- function(L, reference=seq(NCOL(L)), ...) { # Split L in reference part and the rest A1 <- L[reference,, drop=FALSE] #A2 is L[-reference,] # Compute the part of A Phi A' corresponding to the reference variables # Compute cholesky rot = rotated reference part # No check or error message for singularity. Exact singularity is rare in # practice but ill-conditioning is a real danger. # now assuming orthogonal (Phi=I) #newPhi <- if (is.null(Phi)) A1 %*% t(A1) else A1 %*% Phi %*% t(A1) #B1 <- t(chol(newPhi)) B1 <- t(chol(A1 %*% t(A1))) # Transformation matrix: B1 = A1 * Tmat # Rotated solution for non-reference part: B2 = A2 * Tmat Tmat <- solve(A1, B1) # Assemble rotated solution B <- matrix(0, NROW(L), NCOL(L)) B[reference,] <- B1 B[-reference,] <- L[-reference,, drop=FALSE] %*% Tmat dimnames(B) <- list(dimnames(L)[[1]], paste("factor", seq(NCOL(L)))) r <- list(loadings=B, Th=Tmat, method="echelon", orthogonal=TRUE, convergence=TRUE) class(r) <- "GPArotation" r } GPArotation/R/GPFRS.R0000644000176200001440000000601414344533753013662 0ustar liggesusers# Main functions that serve as a wrapper function to GPForth and GPFoblq # these functions have the added functionality of random starts # Functions added in version 2023-1.1 GPFRSorth <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL, randomStarts = 0){ if (0 < randomStarts){ Tmat <- Random.Start(ncol(A)) } r <- GPForth(A, Tmat = Tmat, normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) if (randomStarts > 1){ Qvalues <- rev(r$Table[,2])[1] Qmin <- Qvalues Qconverged <- r$convergence for (inum in 2:randomStarts){ gpout <- GPForth(A, Tmat = Random.Start(ncol(A)), normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) Qvalues <- c(Qvalues, rev(gpout$Table[,2])[1]) Qconverged <- c(Qconverged, gpout$convergence) if (rev(gpout$Table[,2])[1] < Qmin){ r <- gpout Qmin <- rev(gpout$Table[,2])[1] } } Qmin <- eps * round(Qmin * 1/eps,0) Qvalues <- eps * round(Qvalues * 1/eps,0) Qvaluessame <- Qvalues == Qmin randStartChar <- c(randomStarts, sum(Qconverged), sum(Qvaluessame), length(unique(Qvalues))) names(randStartChar) <- c("randomStarts","Converged","atMinimum", "localMins") r <- list(loadings = r$loadings, Th = r$Th, Table = r$Table, method = r$method, orthogonal = TRUE, convergence = r$convergence, Gq=r$Gq, randStartChar = randStartChar) class(r) <- "GPArotation" } colnames(r$Table) <- c("iter", "f", "log10(s)", "alpha") r } GPFRSoblq <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL, randomStarts=0){ if (0 < randomStarts){ Tmat <- Random.Start(ncol(A)) } r <- GPFoblq(A, Tmat = Tmat, normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) if (randomStarts > 1){ Qvalues <- rev(r$Table[,2])[1] Qmin <- Qvalues Qconverged <- r$convergence for (inum in 2:randomStarts){ gpout <- GPFoblq(A, Tmat = Random.Start(ncol(A)), normalize = normalize, eps = eps, maxit = maxit, method = method, methodArgs = methodArgs) Qvalues <- c(Qvalues, rev(gpout$Table[,2])[1]) Qconverged <- c(Qconverged, gpout$convergence) if (rev(gpout$Table[,2])[1] < Qmin){ r <- gpout Qmin <- rev(gpout$Table[,2])[1] } } Qmin <- eps * round(Qmin * 1/eps,0) Qvalues <- eps * round(Qvalues * 1/eps,0) Qvaluessame <- Qvalues == Qmin randStartChar <- c(randomStarts, sum(Qconverged), sum(Qvaluessame), length(unique(Qvalues))) names(randStartChar) <- c("randomStarts","Converged","atMinimum", "localMins") r <- list(loadings = r$loadings, Phi = r$Phi, Th = r$Th, Table = r$Table, method = r$method, orthogonal = FALSE, convergence = r$convergence, Gq=r$Gq, randStartChar = randStartChar) class(r) <- "GPArotation" } dimnames(r$Phi) <- list(colnames(r$loadings),colnames(r$loadings)) colnames(r$Table) <- c("iter", "f", "log10(s)", "alpha") r } GPArotation/R/RandomStart.R0000644000176200001440000000160414557753545015250 0ustar liggesusers# Random start routine that outputs 1 orthogonal random matrix #Random.Start <- function(k){ # qr.Q(qr(matrix(rnorm(k*k),k))) # } # Stewart, G. W. (1980). The Efficient Generation of Random Orthogonal Matrices # with an Application to Condition Estimators. SIAM Journal on Numerical Analysis, # 17(3), 403-409. http://www.jstor.org/stable/2156882 # # Mezzadri, F. (2007). How to generate random matrices from the classical # compact groups. Notices of the American Mathematical Society, 54(5), 592-604. # see https://arxiv.org/abs/math-ph/0609050 # # This updated version changed as of GPArotation 2024-2.1 # Thanks to Yves Rosseel for pointing this out # Random.Start <- function(k = 2L) { qr.out <- qr(matrix(rnorm(k * k), nrow = k, ncol = k)) Q <- qr.Q(qr.out) R <- qr.R(qr.out) R.diag <- diag(R) R.diag2 <- R.diag/abs(R.diag) out <- t( t(Q) * R.diag2 ) out } GPArotation/R/rotations.R0000644000176200001440000005076414757002647015037 0ustar liggesusers########################################### ########################################### ### ### OBLIMIN ### ########################################### ########################################### oblimin <- function(A, Tmat=diag(ncol(A)), gam=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="oblimin", methodArgs=list(gam=gam), randomStarts = randomStarts ) } vgQ.oblimin <- function(L, gam=0){ X <- L^2 %*% (!diag(TRUE,ncol(L))) if (0 != gam) { p <- nrow(L) X <- (diag(1,p) - matrix(gam/p,p,p)) %*% X } list(Gq=L*X, f=sum(L^2 * X)/4, Method= if (gam == 0) "Oblimin Quartimin" else if (gam == .5) "Oblimin Biquartimin" else paste("Oblimin g=", gam,sep="") ) } # original # vgQ.oblimin <- function(L, gam=0){ # Method <- paste("Oblimin g=",gam,sep="") # if (gam == 0) Method <- "Oblimin Quartimin" # if (gam == .5) Method <- "Oblimin Biquartimin" # if (gam == 1) Method <- "Oblimin Covarimin" # k <- ncol(L) # p <- nrow(L) # N <- matrix(1,k,k)-diag(k) # f <- sum(L^2 * (diag(p)-gam*matrix(1/p,p,p)) %*% L^2 %*% N)/4 # Gq <- L * ((diag(p)-gam*matrix(1/p,p,p)) %*% L^2 %*% N) # return(list(Gq=Gq,f=f,Method=Method)) # } ########################################### ########################################### ### ### QUARTIMIN ### ########################################### ########################################### quartimin <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, method="quartimin", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.quartimin <- function(L){ X <- L^2 %*% (!diag(TRUE,ncol(L))) list(Gq= L*X, f= sum(L^2 * X)/4, Method= "Quartimin" ) } #original #vgQ.quartimin <- function(L){ # Method="Quartimin" # L2 <- L^2 # k <- ncol(L) # M <- matrix(1,k,k)-diag(k) # f <- sum(L2 * (L2 %*% M))/4 # Gq <- L * (L2 %*% M) # return(list(Gq=Gq,f=f,Method=Method)) #} ########################################### ########################################### ### ### TARGET ROTATION ### required argument is a ### target matrix 'Target' ### ########################################### ########################################### targetT <- function(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(Target)) stop("argument Target must be specified.") GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="target", methodArgs=list(Target=Target), randomStarts = randomStarts) } targetQ <- function(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(Target)) stop("argument Target must be specified.") GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="target", methodArgs=list(Target=Target), randomStarts = randomStarts) } vgQ.target <- function(L, Target=NULL){ if(is.null(Target)) stop("argument Target must be specified.") # e.g. Target <- matrix(c(rep(NA,4),rep(0,8),rep(NA,4)),8) # approximates Michael Brown approach Gq <- 2 * (L - Target) Gq[is.na(Gq)] <- 0 #missing elements in target do not affect the first derivative list(Gq=Gq, f=sum((L-Target)^2, na.rm=TRUE), Method="Target rotation") #The target rotation ? option in Michael Browne's algorithm should be NA } ########################################### ########################################### ### ### PARTIALLY SPECIFIED TARGET ROTATION ### required arguments are a ### target matrix 'Target' and ### weight matrix 'W' ### ########################################### ########################################### pstT <- function(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(W)) stop("argument W must be specified.") if(is.null(Target)) stop("argument Target must be specified.") GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="pst", methodArgs=list(W=W, Target=Target), randomStarts = randomStarts) } pstQ <- function(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0, L=NULL) { if (missing(A) & !is.null(L)){ message("Use of 'L=' is deprecated. Please use 'A='. ") A <- L Tmat <- diag(ncol(L)) } if(is.null(W)) stop("argument W must be specified.") if(is.null(Target)) stop("argument Target must be specified.") GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="pst", methodArgs=list(W=W, Target=Target), randomStarts = randomStarts) } vgQ.pst <- function(L, W=NULL, Target=NULL){ if(is.null(W)) stop("argument W must be specified.") if(is.null(Target)) stop("argument Target must be specified.") # Needs weight matrix W with 1's at specified values, 0 otherwise # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). # When W has only 1's this is procrustes rotation # Needs a Target matrix Target with hypothesized factor loadings. # e.g. Target = matrix(0,8,2) Btilde <- W * Target list(Gq= 2*(W*L-Btilde), f = sum((W*L-Btilde)^2), Method="Partially specified target") } ########################################### ########################################### ### ### OBLIMAX ### ########################################### ########################################### oblimax <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="oblimax", methodArgs = NULL, randomStarts = randomStarts) } vgQ.oblimax <- function(L){ list(Gq= -(4*L^3/(sum(L^4))-4*L/(sum(L^2))), f= -(log(sum(L^4))-2*log(sum(L^2))), Method="Oblimax") } ########################################### ########################################### ### ### MINIMUM ENTROPY ### ########################################### ########################################### entropy <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="entropy", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.entropy <- function(L){ list(Gq= -(L*log(L^2 + (L^2==0)) + L), f= -sum(L^2*log(L^2 + (L^2==0)))/2, Method="Minimum entropy") } ########################################### ########################################### ### ### QUARTIMAX ### ########################################### ########################################### quartimax <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="quartimax", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.quartimax <- function(L){ list(Gq= -L^3, f= -sum(diag(crossprod(L^2)))/4, Method="Quartimax") } ########################################### ########################################### ### ### VARIMAX ### ########################################### ########################################### Varimax <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="varimax", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.varimax <- function(L){ QL <- sweep(L^2,2,colMeans(L^2),"-") list(Gq= -L * QL, f= -sqrt(sum(diag(crossprod(QL))))^2/4, Method="varimax") } ########################################### ########################################### ### ### SIMPLIMAX ### argument: # close to zero loadings 'k' ### ########################################### ########################################### simplimax <- function(A, Tmat=diag(ncol(A)), k=nrow(A), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="simplimax", methodArgs=list(k=k), randomStarts = randomStarts) } vgQ.simplimax <- function(L, k=nrow(L)){ # k: Number of close to zero loadings Imat <- sign(L^2 <= sort(L^2)[k]) list(Gq= 2*Imat*L, f= sum(Imat*L^2), Method="Simplimax") } ########################################### ########################################### ### ### BENTLER'S INVARIANT PATTERN SIMPLICITY ### ########################################### ########################################### bentlerT <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bentler", methodArgs = NULL, randomStarts = randomStarts) } bentlerQ <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bentler", methodArgs = NULL, randomStarts = randomStarts) } vgQ.bentler <- function(L){ L2 <- L^2 M <- crossprod(L2) D <- diag(diag(M)) list(Gq= -L * (L2 %*% (solve(M)-solve(D))), f= -(log(det(M))-log(det(D)))/4, Method="Bentler's criterion") } ########################################### ########################################### ### ### TANDEM CRITERIA ### ########################################### ########################################### tandemI <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="tandemI", methodArgs = NULL, randomStarts = randomStarts) } #vgQ.tandemI <- function(L){ # Tandem Criterion, Comrey, 1967. # Method <- "Tandem I" # LL <- (L %*% t(L)) # LL2 <- LL^2 # f <- -sum(diag(crossprod(L^2, LL2 %*% L^2))) # Gq1 <- 4 * L *(LL2 %*% L^2) # Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L # Gq <- -Gq1 - Gq2 # return(list(Gq=Gq,f=f,Method=Method)) #} vgQ.tandemI <- function(L){ # Tandem Criterion, Comrey, 1967. LL <- (L %*% t(L)) LL2 <- LL^2 Gq1 <- 4 * L *(LL2 %*% L^2) Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L Gq <- -Gq1 - Gq2 list(Gq=Gq, f= -sum(diag(crossprod(L^2, LL2 %*% L^2))), Method="Tandem I") } tandemII <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="tandemII", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.tandemII <- function(L){ # Tandem Criterion, Comrey, 1967. LL <- (L %*% t(L)) LL2 <- LL^2 f <- sum(diag(crossprod(L^2, (1-LL2) %*% L^2))) Gq1 <- 4 * L *((1-LL2) %*% L^2) Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L Gq <- Gq1 - Gq2 list(Gq=Gq, f=f, Method="Tandem II") } ########################################### ########################################### ### ### GEOMIN ### ########################################### ########################################### geominT <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="geomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } geominQ <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="geomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } vgQ.geomin <- function(L, delta=.01){ k <- ncol(L) p <- nrow(L) L2 <- L^2 + delta pro <- exp(rowSums(log(L2))/k) list(Gq=(2/k)*(L/L2)*matrix(rep(pro,k),p), f= sum(pro), Method="Geomin") } ########################################### ########################################### ### ### BI-GEOMIN ### ########################################### ########################################### bigeominT <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bigeomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } bigeominQ <- function(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bigeomin", methodArgs=list(delta=delta), randomStarts = randomStarts) } vgQ.bigeomin <- function(L, delta = 0.01){ Lg <- L[ , -1, drop = FALSE] out <- vgQ.geomin(Lg, delta = delta) list(Gq=cbind(0, out$Gq), f= out$f, Method="Bi-Geomin") } ########################################### ########################################### ### ### CRAWFORD FERGUSON FAMILY ### needs kappa parameter ### ### EQUAMAX PARSIMAX ### ########################################### ########################################### cfT <- function(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } cfQ <- function(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } equamax <- function(A, Tmat=diag(ncol(A)), kappa=ncol(A)/(2*nrow(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } parsimax <- function(A, Tmat=diag(ncol(A)), kappa=(ncol(A) - 1)/(ncol(A) + nrow(A) - 2), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="cf", methodArgs=list(kappa=kappa), randomStarts = randomStarts) } vgQ.cf <- function(L, kappa=0){ k <- ncol(L) p <- nrow(L) # kappa <- 0 # quartimax # kappa <- 1/p # varimax # kappa <- k/(2*p) # equamax # kappa <- (k-1)/(p+k-2) # parsimax # kappa <- 1 # factor parsimony N <- matrix(1,k,k)-diag(k) M <- matrix(1,p,p)-diag(p) L2 <- L^2 f1 <- (1-kappa)*sum(diag(crossprod(L2,L2 %*% N)))/4 f2 <- kappa*sum(diag(crossprod(L2,M %*% L2)))/4 list(Gq= (1-kappa) * L * (L2 %*% N) + kappa * L * (M %*% L2), f= f1 + f2, Method= if (kappa == 0) "Crawford-Ferguson Quartimax/Quartimin" else if (kappa == 1/p) "Crawford-Ferguson Varimax" else if (kappa == k/(2*p)) "Equamax" else if (kappa == (k-1)/(p+k-2)) "Parsimax" else if (kappa == 1) "Factor Parsimony" else paste("Crawford-Ferguson:k=",kappa,sep="")) } ########################################### ########################################### ### ### INFOMAX ### ########################################### ########################################### infomaxT <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, method="infomax", randomStarts = randomStarts) } infomaxQ <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="infomax", methodArgs = NULL, randomStarts = randomStarts) } vgQ.infomax <- function(L){ k <- ncol(L) p <- nrow(L) S <- L^2 s <- sum(S) s1 <- rowSums(S) s2 <- colSums(S) E <- S/s e1 <- s1/s e2 <- s2/s Q0 <- sum(-E * log(E)) Q1 <- sum(-e1 * log(e1)) Q2 <- sum(-e2 * log(e2)) f <- log(k) + Q0 - Q1 - Q2 H <- -(log(E) + 1) alpha <- sum(S * H)/s^2 G0 <- H/s - alpha * matrix(1, p, k) h1 <- -(log(e1) + 1) alpha1 <- s1 %*% h1/s^2 G1 <- matrix(rep(h1,k), p)/s - as.vector(alpha1) * matrix(1, p, k) h2 <- -(log(e2) + 1) alpha2 <- h2 %*% s2/s^2 G2 <- matrix(rep(h2,p), ncol=k, byrow=T)/s - as.vector(alpha2) * matrix(1, p, k) Gq <- 2 * L * (G0 - G1 - G2) list(f = f, Gq=Gq, Method="Infomax") } ########################################### ########################################### ### ### MCCAMMON ENTROPY ### ########################################### ########################################### mccammon <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, method="mccammon", normalize=normalize, eps=eps, maxit=maxit, methodArgs = NULL, randomStarts = randomStarts) } vgQ.mccammon <- function(L){ k <- ncol(L) p <- nrow(L) S <- L^2 M <- matrix(1,p,p) s2 <- colSums(S) P <- S / matrix(rep(s2,p),ncol=k,byrow=T) Q1 <- -sum(P * log(P)) H <- -(log(P) + 1) R <- M %*% S G1 <- H/R - M %*% (S*H/R^2) s <- sum(S) p2 <- s2/s Q2 <- -sum(p2 * log(p2)) h <- -(log(p2) + 1) alpha <- h %*% p2 G2 <- rep(1,p) %*% t(h)/s - as.vector(alpha)*matrix(1,p,k) Gq <- 2*L*(G1/Q1 - G2/Q2) f <- log(Q1) - log(Q2) list(f = f, Gq = Gq, Method = "McCammon entropy") } ########################################### ########################################### ### ### BIFACTOR BIQUARTIMIN ### ########################################### ########################################### bifactorT <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ #adapted from Jennrich and Bentler 2011. GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bifactor", randomStarts = randomStarts) } bifactorQ <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0){ #oblique. Adapted from Jennrich and Bentler 2011. GPFRSoblq(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="bifactor", randomStarts = randomStarts) } vgQ.bifactor <- function(L){ k <- ncol(L) Lt <- L[,2:k] Lt2 <- Lt^2 N <- matrix(1, nrow=k-1, ncol=k-1) - diag(k-1) f <- sum(Lt2 * (Lt2 %*% N)) Gt <- 4 * Lt * (Lt2 %*% N) G <- cbind(0, Gt) list(f = f, Gq = G, Method = "Bifactor Biquartimin") } ########################################### ########################################### ### ### VARIMIN ### ########################################### ########################################### varimin <- function(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) { GPFRSorth(A, Tmat=Tmat, normalize=normalize, eps=eps, maxit=maxit, method="varimin", methodArgs=NULL, randomStarts = randomStarts) } vgQ.varimin <- function (L){ QL <- sweep(L^2, 2, colMeans(L^2), "-") list(Gq = L * QL, f = sqrt(sum(diag(crossprod(QL))))^2/4, Method = "varimin") } ########################################### ########################################### ### ### Lp rotation ### ########################################### ########################################### # for lpT and lpQ functions see the file lp.R vgQ.lp.wls<-function(L, W) { list( Gq = 2 * W * L/nrow(L), f = sum(W * L * L)/nrow(L), Method = "Weighted least squares for Lp rotation" # Method description ) } ########################################### ########################################### ### ### PROMAX ### (not in use) ### ########################################### ########################################### # promax is already defined in the stats (previously mva) package # #GPromax <- function(A,pow=3){ # method <- "Promax" # # Initial rotation: Standardized Varimax # require(statsa) # xx <- promax(A,pow) # Lh <- xx$loadings # Th <- xx$rotmat # orthogonal <- F # Table <- NULL #return(list(loadings=Lh,Th=Th,Table=NULL,method,orthogonal=orthogonal)) #} GPArotation/NEWS0000644000176200001440000001066414763331763013164 0ustar liggesusersKnown problems o Very occassionally (about 1 in 1000 in monte carlo experiments) the algorithm gets stuck and does not improve (so does not converge). The workaround is to restart with a very slightly perturbed starting point. Changes in GPArotation version 2025.3-1 o Added Lp rotation (via Xinyi Liu), including examples, help file, tests o Added some automated tests for error messages o Edits to the manual pages Changes in GPArotation version 2024.3-1 o Added tests for use in automated validation systems o Added correct class to echelon and eiv rotation Changes in GPArotation version 2024.2-1 o Improved Random.Start for orthonormal matrix generation o Added Bi-Geomin rotation criterion o Edits to the manual pages Changes in GPArotation version 2023.11-1 o Updated NormalizingWeight to handle cases with a row of zeroes Changes in GPArotation version 2023.8-1 o Added L to target rotation and pst to better handle backward compatibility. Changes in GPArotation version 2023.3-1 o January 2023: GPArotation turns 18 years old. o Included GPFRSorth and GPFRSoblq functions, and set rotation routines to call GPFRSorth instead of GPForth, GPFRSoblq instead of GPFoblq. o GPFRSorth and GPFRSoblq include randomStarts = 0 as default. o Included equamax, parsimax and varimin (added vgQ.varimin). o Cleaned up print.GPArotation to account for random starts. o Cleaned up vgQ.bifactor. o Expanded vignette GPAguide. o Added vignette on derivative-free GPA: the GPArotateDF package. Changes in GPArotation version 2022.10-1 o Changed maintainer from Paul Gilbert to Coen Bernaards Changes in GPArotation version 2022.4-1 o Switched URL from defunct http://:www.stat.ucla.edu/research/gpa to https://optimizer.r-forge.r-project.org/GPArotation_www/ o Added importFrom("stats", "rnorm") to NAMESPACE file. o Fixed Rbuildignore so that files from building vignettes are not omitted in the package build. Changes in GPArotation version 2015.7-1 o Added default package imports as now required by CRAN. Changes in GPArotation version 2014.11-1 o Minor format and cleanup required by CRAN checks, no real changes. Changes in GPArotation version 2012.3-1 o no real changes, but bumping version for new CRAN suitability check. Changes in GPArotation version 2011.11-1 o updated maintainer email address. Changes in GPArotation version 2011.10-1 o Modification to vgQ.target to allow NA in target, which is replaced by 0.0 (from William Revelle). o Added bifactorT and bifactorQ (biquartimin) from William Revelle. Changes in GPArotation version 2010.07-1 o Fix an error caused by an exact initial setting (from William Revelle). Changes in GPArotation version 2009.02-2 o Standardized NEWS format for new function news(). Changes in GPArotation version 2009.02-1 o minor documentation corrections as found by a new R-devel. Changes in GPArotation version 2008.05-1 o added echelon rotation. o added gradient Gq to result list from GPForth and GPFoblq. o change license from "GPL-2" to "GPL-2 or later". Changes in GPArotation version 2007.06-1 o fixed a couple of lingering $Lh (in print and summary methods) that should have been changed to $loadings. Changes in GPArotation version 2007.04-1 o removed an extra comma in c() that caused a test failure with R-2.5.0 o added eiv rotation. o renamed $Lh in the result from GPForth and GPFoblq to $loadings. As a result, rotation methods calling these function no longer need to rename this element in order to work with factanal and other programs. (It is a good idea to use the extractor function loadings() rather than refer directly to object structure.) o changed rotation method functions to return all elements of GPFoblq and GFForth. o fixed the documentation file primary alias for all rotations (which was being called oblimin). Changes in GPArotation version 2006.2-2 o extra argument (...) to invisible in print.GPArotation was removed. Changes in GPArotation version 2006.2-1 o broken references in documentation were fixed and updated. Changes in GPArotation version 2005.10-1 o warning message about non-convergence expanded to indicate function. Changes in GPArotation version 2005.4-1 o First released version. GPArotation/vignettes/0000755000176200001440000000000014767556440014473 5ustar liggesusersGPArotation/vignettes/GPArotateDF.Stex0000644000176200001440000002402114404451600017353 0ustar liggesusers%\VignetteIndexEntry{Derivative-Free Gradient Projection: the GPArotateDF package} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{cubimax} %\VignetteKeyword{forced simple structure} \documentclass[english, 10pt]{article} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=FALSE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Derivative-Free Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotateDF} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \subsection*{Principle of derivative-Free Factor Rotation} The gradient projection algorithm package \emph{GPArotation} consists of wrapper functions and functions that compute the gradients, $G_q$, needed for minimization. For all functions included in the {\em GPArotation} package, the gradients are included in the \texttt{vgQ} routines. For example, the \texttt{vgQ.quartimax} function provides the gradients $G_q$ for quartimax rotation. Examples of gradient derivations, computations are provided in \cite{gpa.1} and \cite{gpa.2}, as well as \cite{gpa.rotate}. However, the derivation of the gradient can be quite involved for complex rotation criteria. In such cases, a derivative free version of gradient projection algorithm can be used for minimization of the criterion function, {\em without} the need for a gradient. Details of the methods are described in \cite{gpa.df}. The method is implemented in the package \emph{GPArotateDF} that may be downloaded and installed. To perform derivative-free rotation, the main algorithms \texttt{GPForth.df} and \texttt{GPFoblq.df} are available for orthogonal and oblique rotation, respectively. Both are minimization algorithms. The algorithms differ from the regular algorithms by the inclusion of the numerical derivates $G_f$ for the rotation criteria in \texttt{GPForth.df} and \texttt{GPFoblq.df}. The algorithms require: an initial loadings matrix \texttt{A} and a rotation method. Optional are initial rotation matrix \texttt{Tmat} (default is the identity matrix). Other arguments needed for individual rotations are applied in the same way as in the {\em GPArotation} package. The rotation method is provided between quotation marks, and refers to the name of the ff-function. For example, the \texttt{method = "varimax"} through \texttt{GPForth.df} calls the \texttt{ff.varimax} function. The ff-functions are the derivative-free analogues of the GPArotation vgQ functions. The output of \texttt{ff.varimax} is the rotation criteria value, \texttt{f}, and the \texttt{Method} name, e.g. \texttt{DF-Varimax}. New rotation functions need to be programmed as \texttt{ff.newmethod}. The only required input is an initial loadings matrix \texttt{A}, and any potential additional arguments. The output consist of the value \texttt{f} of the criterion, and the \texttt{Method} name (the \texttt{GPForth.df} and \texttt{GPFoblq.df} algorithms expect this included in the result). \subsection*{Derivative-free quartimax rotation} As an example, consider quartimax rotation. Gradient projection quartimax orthogonal rotation seeks to minimize the sum of all loadings raised to power 4. Thus, using the notation of \cite{gpa.rotate} (page 682), the criterion $f$ for minimization is calculated as \[ f = Q(\Lambda) = -\frac{1}{4}\sum_{i} \sum_{r} \lambda_{ir}^{4}. \] Derivative-free quartimax rotation using \texttt{ff.quartimax} is then very simple \begin{Scode} library(GPArotateDF) ff.quartimax<- function(L){ f = -sum(L^4) / 4 list(f = f, Method = "DF-Quartimax") } data(Harman, package="GPArotation") GPForth.df(Harman8, method="quartimax") \end{Scode} Of course, for quartimax, the gradient is easy to derive and regular rotation is a better choice. \subsection*{Rotation when the derivative is complicated: cubimax} Sometimes the gradient is hard to derive. For example, a criterion that seeks to minimize loadings to the power 3, the absolute value is needed for a meaningful result. \[ f = Q(\Lambda) = -\sum_{i} \sum_{r} | \lambda_{ir}^{3} |. \] While the gradient may be complicated, the derivative-free function for minimization is straightforward. \begin{Scode} ff.cubimax<- function(L){ f = -sum(abs(L^3)) list(f = f, Method = "DF-Cubimax") } GPForth.df(Harman8, method="cubimax") \end{Scode} Results differ from quartimax and varimax rotation \cite{mulaik} describes Absolmin, the sum of absolute factor loadings minimized. Minimizing the criterion is straightforward using the {\em GPArotateDF} package. \subsection*{Rotation when an algorithm is involved: Forced Simple Structure} In certain cases the derivate is so poorly defined that deriving the \texttt{vgQ} function is a non-starter. For example, an algorithm that updates a weight matrix that, when multiplied with the loadings matrix provides a rotation criterion to be minimized. The algorithm Forced Simple Structure chooses a weight matrix focused on the lowest loadings. The rotation criterion value $f$ is minimized representing a rotated factor pattern which many low loadings, restricted to each factor having at least some salient loadings. In each iteration, the weight matrix \texttt{Imat} gets weight 1 at the lowest factor loadings, and 0 elsewhere. Assume we have \texttt{p} items, and \texttt{m} factors (for a \texttt{p x m} loadings matrix). In each iteration, first the lowest loadings get weight 1. Next, for each pair \texttt{(i,j)} of factors, lowest loadings get weight 1 until there are at least \texttt{(m + kij)} items with weight 1 on a single factor \texttt{i} or \texttt{j} (but not the other factor), or not enough loadings are left to get weight 1. Possible values for \texttt{kij = (0, ..., [p - m] )} and defaults to 2. Forced Simple Structure is most effective when \texttt{kij} has a lower value. For each increase of 1, an additional \texttt{(m)} loadings get weight~1. The criterion \texttt{f} minimizes the squared loadings for low loadings (``non-salient''). Salient loadings are therefor increased as the sum of squared non-salient loadings is minimized. \begin{Scode} ff.fss <- function(L, kij=2){ m <- ncol(L) p <- nrow(L) zm <- m + kij Imat <- matrix(0, p, m) for (j in 1:m){ Imat[abs(L[,j]) <= sort(abs(L[,j]))[zm],j] <- 1 } for (i in 1:(m-1)){ for (j in (i+1):m){ nz <- sum( (Imat[,i] + Imat[,j]) ==1) while (nz < zm && sum(Imat[ ,c(i,j)]) < m * 2){ tbc <- c(abs(L[,i]), abs(L[,j])) tbcs <- sort(tbc [c(Imat[,i], Imat[,j])==0])[1] Imat[abs(L) == tbcs] <- 1 nz <- sum( (Imat[,i] + Imat[,j]) ==1) } } } Method <- paste("DF-Forced Simple Structure (kij = ",kij,")", sep="") f <- sum(Imat*L^2) list(f = f, Imat = Imat, Method = Method) } data(WansbeekMeijer, package = "GPArotation") z <- factanal(covmat = NetherlandsTV, factors = 3, rotation = "none") fssT.df(loadings(z), kij = 3) # which loadings get weight 1 in the first iteration? ff.fss(loadings(z), kij = 3)$Imat \end{Scode} The added \texttt{sum(Imat) < m * 2} requirement was added to avoid infinite looping. It is useful to consider random starts as the rotation tends to have many local minima. The method works both orthogonal and oblique. \subsection*{Examples of other ff-functions} Writing ff-functions is straightforward because only the criterion value is needed. Here are a few additional examples of ff-functions. For all vgQ-functions exist and are preferable to be used. The oblique rotation criterion for oblimax \begin{Scode} ff.oblimax <- function(L){ f <- -(log(sum(L^4))-2*log(sum(L^2))) list(f = f, Method = "DF-Oblimax") } \end{Scode} Entropy criterion for orthogonal rotation \begin{Scode} ff.entropy <- function(L){ f <- -sum(L^2 * log(L^2 + (L^2==0)))/2 list(f = f, Method = "DF-Entropy") } \end{Scode} Simplimax that works well in oblique rotation \begin{Scode} ff.simplimax <- function(L,k=nrow(L)){ # k: Number of close to zero loadings Imat <- sign(L^2 <= sort(L^2)[k]) f <- sum(Imat*L^2) list(f = f, Method = "DF-Simplimax") } \end{Scode} Target rotation. Requires both a weight matrix and a target matrix. Target rotation can be both orthogonal and oblique. \begin{Scode} ff.pst <- function(L,W,Target){ # Needs weight matrix W with 1's at specified values, 0 otherwise # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). # When W has only 1's this is procrustes rotation # Needs a Target matrix Target with hypothesized factor loadings. # e.g. Target = matrix(0,8,2) Btilde <- W * Target f <- sum((W*L-Btilde)^2) list(f = f, Method = "DF-PST") } \end{Scode} \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock doi: 10.1177/0013164404272507 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2001}]{gpa.1} Jennrich, R. I. (2002). \newblock A simple general procedure for orthogonal rotation. \newblock{\em Psychometrika}, 66(3), 289--306. \newblock doi: 10.1007/BF02294840 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2002}]{gpa.2} Jennrich, R. I. (2002). \newblock A simple general method for oblique rotation. \newblock{\em Psychometrika}, 67(3), 7--19. \newblock doi: 10.1007/BF02294706 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2004}]{gpa.df} Jennrich, R. I. (2004). \newblock Derivative free gradient projection algorithms for rotation. \newblock{\em Psychometrika}, 69(3), 475--480. \newblock doi: 10.1007/BF02295647 \bibitem[\protect\citeauthoryear{Jennrich}{Mulaik}{2010}]{mulaik} Mulaik, S.A. (2010). \newblock {\em Foundations of factor analysis} \newblock (2nd ed.). Chapman and Hall/CRC Press, Taylor \& Francis Group. \newblock doi: 10.1201/b15851 \end{thebibliography} \end{document}GPArotation/vignettes/GPAguide.Stex0000644000176200001440000002700314557733750016765 0ustar liggesusers%\VignetteIndexEntry{Gradient Projection Factor Rotation} %\VignettePackage{GPArotation} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{varimax} %\VignetteKeyword{oblimin} \documentclass[english, 10pt]{article} \usepackage{hyperref} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=TRUE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotation} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \section*{GPArotation Functions} In R, the functions in this package are made available with \begin{Scode} library("GPArotation") \end{Scode} The most complete reference for the software is: Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. Educational and Psychological Measurement. A mirror of the original repository that is referenced in the paper, with additional material is available here: \href{https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html} {https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html}. Rotations can be performed by providing an orthogonal matrix to the gradient projection function. Orthogonal matrix for rotation can be obtained by extracting an unrotated factor loadings matrix. A rotation is done by calling the rotation name directly, or by calling one of the wrapper functions \texttt{GPFRSorth} or \texttt{GPFRSoblq}, for orthogonal and oblique rotation, respectively. Under the hood, rotations are computed using the Gradient Projection Algorithm code, which can be called directly. The key functionality of the algorithm is included in the \texttt{GPForth} and \texttt{GPFoblq} functions for orthogonal and oblique rotation, respectively. Calling these functions directly works as it always has (the codes have not changed). The rotated loadings matrix is the pattern matrix. The structure matrix may be obtained using the \texttt{summary} command. \subsection*{GPArotation Functions with \texttt{factanal}} The {\em GPArotation} can be used in conjunction with the built-in R \texttt{factanal} function. It is recommended to rotate outside of \texttt{factanal}. \begin{Scode} data(ability.cov) z <- factanal(factors = 2, covmat = ability.cov, rotation = "none") # quartimax rotation GPFRSorth(loadings(z), method = "quartimax") quartimax(z$loadings) # oblimin rotation GPFRSoblq(z$loadings, method = "oblimin") oblimin(loadings(z)) \end{Scode} {\bf Important note}: \texttt{factanal} allows for calling a rotation directly from the \texttt{factanal} call. However, due to a \texttt{factanal} calculation error in the computation of the correlation matrix, the produced correlation matrix {\em may} be wrong for oblique rotation (orthogonal rotation are not affected). However, the correlation matrix \texttt{Phi} produced by {\em GPArotation} is the correct correlation matrix when performing rotation outside of \texttt{factanal}. \subsection*{Recovery of The Unrotated Loadings Matrix} Recovery of the unrotated loadings matrix is consistent with the definitions used in \cite{gpa.rotate} (page 678). For example, the unrotated matrix $A$ may be recovered as follows. \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none") y.quart <- quartimax(y$loadings) max( loadings(y.quart) %*% t(y.quart$Th) - loadings(y) ) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max( loadings(y.obli) %*% t(y.obli$Th) - loadings(y) ) # last equation on Page 678 max( loadings(y.obli) - loadings(y) %*% solve(t(y.obli$Th)) ) \end{Scode} By the same definitions logic, the factor correlation matrix is calculated as \cite{gpa.rotate} (page 695), \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none", randomStarts=15) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max(abs(y.obli$Phi - t(y.obli$Th) %*% y.obli$Th)) \end{Scode} \subsection*{Random Starts} If multiple random starts are desired then the \texttt{randomStarts} option may be utilized. For example, 100 random starts of the oblique infomax rotation, \begin{Scode} data(Thurstone, package = "GPArotation") infomaxQ(box26, randomStarts = 100) # 100 random starts infomaxQ(box26, Tmat=Random.Start(3)) # a single random start infomaxQ(box26, randomStarts = 1) # also a single random start \end{Scode} The loadings that are output have the lowest complexity value \texttt{f}. While the lowest local minimum may be the global minimum solution, technically it can not be guaranteed that the lowest local minimum is in fact the global minimum. To further investigate the local minima it is recommended to use the {\em fungible} package using the \texttt{faMain} function. When in doubt, trying random initial rotation matrix is advised. For a detailed discussion, consult \cite{nguwall}. Additional algorithmic considerations are in \cite{gpa.rotate} (page 680). \subsection*{An Example of Target Rotation} \cite{fisfon} describe measuring self-reported extra-role behavior in samples of British and East German employees. They publish rotation matrices for two samples, and investigate the structural equivalence of the loadings matrices. Additional context is available in the manuscript. Structural equivalence includes target rotation, as well as calculation of a number of agreement coefficients. The table lists the varimax rotated loadings matrices. Performing target rotation of one loadings matrix to the other can help in interpreting assessing equivalence. \begin{tabular}{l c c c c} \hline & \multicolumn{2}{c}{Britain} & \multicolumn{2}{c}{East Germany} \\ & Factor 1& Factor 2 & Factor 1& Factor 2\\ \hline\hline I am always punctual.&.783&-.163& .778 &-.066\\ I do not take extra breaks.&.811&.202&.875&.081\\ I follow work rules and instructions &.724&.209&.751&.079\\ ~~~ with extreme care.& & & & \\ I never take long lunches or breaks.&.850&.064&.739&.092\\ I search for causes for something .&-.031&.592&.195&.574\\ ~~~ that did not function properly.& & & & \\ I often motivate others to express &-.028&.723&-.030&.807\\\ ~~~ their ideas and opinions.& & & & \\ During the last year I changed &.388&.434&-.135&.717\\ ~~~ something. in my work.& & & & \\ I encourage others to speak up at meetings.&.141&.808&.125& .738\\ I continuously try to submit suggestions&.215&.709& .060&.691\\ ~~~ to improve my work.& & & & \\ \hline \end{tabular} \\ The varimax rotations for each of the samples may be expected to be similar because the two loadings matrices are from different samples measuring the same constructs. Below are target rotation of the East German loadings matrix towards the Britain one, followed by calculation of agreement coefficients. \cite{fisfon} note that coefficients generally should be ``beyond the commonly accepted value of 0.90.'' \begin{Scode} origdigits <- options("digits") options(digits = 2) trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) # orthogonal rotation of trGermany towards trBritain trx <- targetT(trGermany, Target = trBritain) # Factor loadings after target rotation trx # Differences between loadings matrices after rotation y <- trx$loadings - trBritain print(y, digits = 1) # Square Root of the mean squared difference per item sqrt(apply((y^2), 1, mean)) # Square Root of the mean squared difference per factor sqrt(apply((y^2), 2, mean)) # Identity coefficient per factor after rotation 2 * colSums(trx$loadings*trBritain)/( colSums(trx$loadings^2)+colSums(trBritain^2)) # Additivity coefficient per factor after rotation diag(2 * cov(trx$loadings, trBritain) ) / diag(var(trx$loadings)+var(trBritain)) # Proportionality coefficient per factor after rotation colSums(trBritain * trx$loadings)/sqrt(colSums(trBritain^2)*colSums(trx$loadings^2)) # Correlation for each factor per factor after rotation diag(cor(trBritain, trx$loadings)) options(digits = origdigits$digits) \end{Scode} \subsection*{An Example of Partially Specified Target Rotation } \cite{browne} reported an initial loadings matrix and a partially specified target to rotated towards. In {\em GPArotation} the partially specified target matrix is of the same dimension as the initial matrix \texttt{A}, and with \texttt{NA} in the matrix entries that are not pre-specified. Both procedures target rotation and partially specified target rotation can be used to reproduce \cite{browne} results. In this orthogonal rotation example, \texttt{targetT} includes a \texttt{Target} matrix with \texttt{NA} in entries not used in target rotation. With \texttt{pst} no missing values are present in the \texttt{Target} matrix, and the weight matrix \texttt{W} includes weight 0 for entries not used, and 1 for entries included in the rotation. \begin{Scode} A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) # using targetT SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) xt <- targetT(A, Target=SPA) # using pstT SPApst <- matrix(c(rep(0, 6), .7,.0,.7, rep(0,3), rep(0, 7), 0, 0, 0, 0, rep(0, 4)), ncol=3) SPAW <- matrix(c(rep(0, 6), rep(1, 6), rep(0, 7), 1, 1, 0, 1, rep(0, 4)), ncol=3) xpst <- pstT(A, Target = SPApst, W = SPAW) max(abs(loadings(xt)- loadings(xpst))) \end{Scode} Note that convergence tables are identical for both methods. Additional examples are available in the help pages of \texttt{GPFoblq} and \texttt{rotations}. \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock\href{https://doi.org/10.1177/0013164404272507}{https://doi.org/10.1177/0013164404272507} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Browne}{1972}]{browne} Browne, M.W. (1972). \newblock Orthogonal rotation to a partially specified target. \newblock\textit{British Journal of Mathematical and Statistical Psychology}, 25(1), 115--120. \newblock\href{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x}{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Fischer \& Fontaine}{2010}]{fisfon} Fischer, R., \& Fontaine, J. (2010). \newblock Methods for investigating structural equivalence. \newblock In D. Matsumoto, \& F. van de Vijver (Eds.), {\em Cross-Cultural Research Methods in Psychology} (179--215). \newblock Cambridge University press. \newblock \href{https://doi.org/10.1017/CBO9780511779381.010}{https://doi.org/10.1017/CBO9780511779381.010} \bibitem[\protect\citeauthoryear{Nguyen \& Waller}{Nguyen \& Waller}{2022}]{nguwall} Nguyen, H. V., \& Waller, N. G. (2022). \newblock Local minima and factor rotations in exploratory factor analysis. \newblock\textit{Psychological Methods}. Advance online publication. \newblock\href{https://doi.org/10.1037/met0000467}{https://doi.org/10.1037/met0000467} \end{thebibliography} \end{document} GPArotation/data/0000755000176200001440000000000014323662111013351 5ustar liggesusersGPArotation/data/Harman.rda0000644000176200001440000000031314323662111015244 0ustar liggesusers r0b```b`a@& =r,r|@_HLqW_jrǖ/OTK?fgjde@@ҧKd'R}+?vw[Dɪy[Uy)@(GPArotation/data/Thurstone.rda0000644000176200001440000000142314323662111016034 0ustar liggesusersmSiHTQ~$6HI{VDI|FiCD",lZ  3QX:3:F6 Eta}w+f5ML^[<mff8=pfq'6samZ\ [7v.7ъlKГB_lu%F@oC^W|y}yJpkje:tέqc7ݫ{Lb890mzlpzJ15#ߛ 2OMlÉ` 6Q^YڃN-*R[J̧ϨGME}CG2`5xPc@C|p` 3^)Z1䜔a7xPi9Q9ՏPy]!߁-!>E.YC& ֿKuf~Nzn7\ 4;@?4-@yd{$N%n 6v貅=p ttDJ}iJL[0_JHEJ%̫R:1Us=&Tzԧ0`ԡyf7Ua^V+h7Jv-[׊W|L=\҅ ~{`CEsJf.((J8JsSla::~>0fHBd 1Ab K-LN; C3GPArotation/NAMESPACE0000644000176200001440000000155414763332731013676 0ustar liggesusersimportFrom("stats", "rnorm") export( "GPFoblq", "GPForth") export( "GPFRSoblq", "GPFRSorth") export("Random.Start") export("oblimin") export("quartimin") export("targetT") export("targetQ") export("pstT") export("pstQ") export("oblimax") export("entropy") export("quartimax") export("Varimax") export("simplimax") export("bentlerT") export("bentlerQ") export("tandemI") export("tandemII") export("geominT") export("geominQ") export("bigeominT") export("bigeominQ") export("cfT") export("cfQ") export("infomaxT") export("infomaxQ") export("mccammon") export("bifactorT") export("bifactorQ") export("equamax") export("parsimax") export("varimin") export("lpT") export("lpQ") export("GPForth.lp") export("GPFoblq.lp") export( "eiv", "echelon") S3method("print", "GPArotation") S3method("summary", "GPArotation") S3method("print", "summary.GPArotation") GPArotation/inst/0000755000176200001440000000000014767556440013440 5ustar liggesusersGPArotation/inst/CITATION0000644000176200001440000000056414405360531014561 0ustar liggesusers bibentry(bibtype="article", title = "Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis", author = "Coen A. Bernaards and Robert I. Jennrich", journal = "Educational and Psychological Measurement", year = "2005", volume = "65", issue = "5", pages = "676--696", doi = "10.1177/0013164404272507" )GPArotation/inst/doc/0000755000176200001440000000000014767556440014205 5ustar liggesusersGPArotation/inst/doc/GPArotateDF.pdf0000644000176200001440000065173714767556440016763 0ustar liggesusers%PDF-1.5 % 3 0 obj << /Length 2961 /Filter /FlateDecode >> stream xڥZKoW |08~4ڬw#w!`@DrUSU$-\4]_wtFhcrJLٕͭ( V$oyX[jԛwB&]]7zjְ-h_<7p9(KvJ˟VRFg"ry[-C櫔6pAwyc q$ix7|U‚0M7dJA>^88TyR#sZ'Mڲ쪞)+)Ee̴cy5usC,9l"- k6I,yN WBnFeirJfPs55O,ŧMp\ Z;\UtL&zR~Z >v ƲNѾ6ʠ(P#lmvjlwPfC L Yr*X-rDvvGX7¬-xu]M{!w\'zC <lw4wPeP&V.2RBe~VZMfqbGI>ϗǩ");80=3;3-ҹ⇛d!lRdw8M[ _ z ԟ=k@pJDJq,ٗcGT>UI% a<,%UTV!zJ aǎ6jdqhQ {e?0wK6u.0Jbmƛ-Θ4KUJ|L[}e3hC~4ósvoado63obc)xTt]+ 4?6ÎhSCLx#@X)b<鏣qt]5r Hr#oF">9K,sXGPYf>|29Oc_W4%a^pi+Ž\+cr4@Ī20) ACkC ؿNT} w{QT>Tu`HJ RWVf"/[{:JFޓG l].XfҾbD/$>(4VTDV/&`!n-I-P i?rG pjy{ 2cԳ̯{BH[>Χ)8n0hi2ŵi $Uk5BcmÞoO߾b#]RbX{g;9B1( z%PDy 7꼊8s=|TaZјhԴ1;LPz][WyJVp12C%}9rsVCjS3CРvp F"-E-j_]9B7ަ"Bn.lgn es v 8x(jUOa3_a夰K+I TSe)q7=Iv @BD^r:M~@|XcAS_Ɣi0ˠ v Mi'xx}MK: '~h~ˬx{3`EX_1c8J7RN0 yOpf8zm*[Z?g)O3@w #M'>8k܃1x,Wlz_.R3dU)˼8*Bl7X'c¦3Jr6NL6t@鉡8 [@~dpgc} :z&uܫt`XCT:k~__0{)Lb?LwE9y(d hȾVjv.| ,v*( AGBڋ]/N9;X?ȉMsBv)m ~WFĎTL?I62?XZ?bUt˦ ܂';W'MeS+y *Lk.vdP,G\q.?B0©9%`0z!%KdQ/0Uvs4` bΠ-eU5#1B\iEMds d$̎QA3(PR+z}y{Xe mЇX|;[2333aGvN| /R aTshQƁ7m6x@`P5'2Vob)\X;X>Fٰ5M5P|:tChCi4V8]>~G0Ѐ7 ea4+Ѩ ="9w^2gz"Kwo&)|SX5W]=O?3`e^H78g&O 濠| endstream endobj 21 0 obj << /Length 2449 /Filter /FlateDecode >> stream xڝYێ}W~n6oL'no 8RkD'$eȿTW&Qc#/+zg/nxYv,4 S/vt]sjA7FѤ^YzEX)gQğES2+mwfc^>|ٕ |S'ۜ \^?e]y[TS0K՛nwf+ }&tL3?@/azX7V5Ԯx,w|SR^ ؼ"+^غD;Nme^.D̹[DZmԉa`i9N8c,ZƧI}Ya!ckцʓy'u(uwxxi悊S?LSq%J0>Dathwy,sfª}!0 ¥c3%/žVVpy#2xP^l<$-vj샵+!^h:DzsXb|y$|m<@Z.*mLnRmogH$~(4AȩI'iO4}̢ cƙ%t1 SI ] %A RStXgA-y$|xGԑN8J-T~%R 9H%@- fIP{( +Jr4GN܇}N/,JyqsB4\|"t2\l?tuLncFn8r-H[\\{W K{lZڮO=]kv K2ݦx!̭<ɘۺڟݓ w`a<1F->b]rҁ 89EB/)U(L\0!w-6_SAkѿPfB*5 x] 3szڨr`qZK C)xf$%L]/#呚73&CdW}`G|?:;6'7ᓼk T^^U2l23kWM]AAm}\~HdHdn Ro7U.+fΜ+w)MfxP\Ř."h:Ida`I5Rd33BƦ:1:hk1BDZ?, ,hhAw, i+7:7}@A#@X T6Vr)T@<Tlp= /`%Wu DYz}1ݗ ]ŞŎILtQE߭MɝmIV:}iG"9AЩ9oL5Ig`|>pCΥA7@\98"6owFC*J.`} A8M5?FN*oW& :vQnG hvth*un#:t҇]osE4|GF^O'd]qљ\88C]8K_01ݮ9 o3>P2946@y ?-yir~d(_e{v?p󱸤d&nμf9 -?`H%g8#Dԏ~<zJv| RQD?TH$}^-ʤ('V⣐֬Z&R&={ЏA#gC[ڭlBZ:Z0c彡iJV% ̏%)i$˫rA OJͯ=9 t\9n6&FՔR5EW.]aX}"=.vxŤsҙdYpn޷vp̹'zt$屄Ú@@Rs<ͦԜ*H'R `4,@0\$Icwy8 4fcEkAq PLu m83k\\iٞTIuZFÔ2ip}'+P)皯 ٶΠTC3譒B""~R5 4<}g6 g_ڮr~uۏ a=HBFiw״_o" endstream endobj 24 0 obj << /Length 1337 /Filter /FlateDecode >> stream xڭW[o6~ϯ0SKofCLLdɥ$Ͱsx(byM=$ynwxH8yu؊C7-ґ̙(Dѥg&,?ځ}ב=2NJ?n %q+nnXSc@g[=n9O I;P놦"'wJoؖćA\Rw9R^ٜlp .7WN?ʩ9臌>R8 F?vi(w`7vHSqb5 Z|9h_7^Ġd7TNYNip=f~7:f907P`ˤV%BzqTn(q d0sq/CKj],qoYs y͒[]$GYT@.zK* '] _.3/_T Yvy:F-2/r~XZZ kbYeccI+J1YSLd|_q=uUc}`:D!KET\DijAVMF+Hi},:w[pÈ,02K8j@ ۴ufz߱gѬ%睐|qsVcRӪ"\K"h{~xw[q} pYsfόںІw%Ow2+߷BVVRXr=J=pP`!iY1Y8U(Sf%p hq (#a /IXWE.6 SRl*jޮ j=TkpU RIq;nFu4f1x}Y`(t'F9\Ndy3M=J&%2RID<+xvQ)%%x€{y-%O%wَyF9%PĞ z'4~m(j9h: JAUt9RHR ܭV6!ƒŭ)U!F}+{7}zKLQetM{WGa֊EI׶bw hk'~9iLknk <cN鎟& uYYl?Q#=$n-/ϧkf6$I}hx2x){孇pc+ەO,Naw endstream endobj 27 0 obj << /Length 1531 /Filter /FlateDecode >> stream xڥWmo6_al@'6C%4mm$/bӶYr)i:GٖYfMm[Q?l¶'w'ts,Ʃe6$4;6b2`N)O0WH$A.hʻb%_h=CMQFw P;@]KJd{)Uj\;R?[c uo$v_.mEdbDNoYG2bWlk ip)>t`2lz?u 5+lB~ȇ0acZc(raϽuU c1(@lQ'vݴ->T(r5vbw ,nWrOnґeCm0 鷺mQNM(/IУ{+.$UZ}I|_=ﰬX*A|)l$z4+˔ff&9@Q14s;u0 cc'8^YwW֗ (*xa!W3/3!uym! Irԉ  p[C35 ^-? t[4*{(q| ~Mj<4+f9Oד0::o|5E]a,yAKZzc߬QXcjD,b&i}SD"֙()L18$9+8dJ$" y@B$1 $xҵK@e:83ԟXT`I"'f~uz* > stream xڍtT6]ҡ" 3twJw+C0 *]4J  -(H}{y}Yy}]{_02R"aHZ$ kX PL%6aɸ-`(o8!5 W@}$p$eAR@ @Dp(@_pyq!=PpgU>HFFJw8@C>L8 .h0[rV.7 ~ 0{&L 0s{0E:(W!>( 0zl$ W?ѿ#NpwPSO_@7* [4U A=p_3Js@{OA=@!~+'8k 9Q2 C$2RqU, e!8] ;>do/ F1 @4 G;Qp-~ ?+AS;FSUDe$8@JJDF`F#XFݳQ Wԅxt;r;G_YWwG>?qE] Wb@7vaP{u+9 poM? jGC\b_v_Zs#`FHo w%0 }E.ؕ~YRAB MTBFdW|`@W "@BW(_g*- }Aޞ_TPWr̈́^6 L!!rQϢ:~ԫŽ(|Z>̠:}~RgF,U2Pͯj)ϲc[cݻo2m'ex_J"dtdߊuGҨ_cܻ)zqd$ "ǼI&N"+ ?;4/ I+,L.TzblĊDn6Fu#GLEbG祖Y8'G:l+\F5sG xg$ג /W}ZMK̂s [MA!!զTD.!f_X*I?AAґ ^OqxRwpt(5 El𞸅?z/Uf"|&j=:K奉F]ֵI<n{Tr2.eJuqfr.6moasT&L{[ip"Լ}ѷCqA f^=?9ni%}.9qysCy* 0ǀ(9+!Oytv3~v<o[t)V#vFp!\gL|zFu q/0g#,'~~et~6h(]6l:>Q>_)so5"*@pKru!vWEJ;M÷:ߵ."$o9#,:,-CE|4xcw\_ٯ%|gff%:z-^,*?*~?f L);n?ɶh )ySVƃ\mݬq"ױ&rL62(9$y6TRVsv{9InB*0~ʾJ>Bq}?&]~7.9f@ _X:OlY^%UJg-XcΩPi#k y;b L=)7YX{ڳmkthC>~ȀKgNfioQ-g 6Nx[Bf@uI*Cm ]g~2Bޭ;o{} 0b̩vͷ k(*).oOjel5x'Ns߷=g3U登NmQtע^.8noL?)<>1hN w д۩bP-|kIܺvGzֹ#H14:̘#+ b5_(o. d#yTwSu…y5get/Gb׷8qhnn~>Jq(䀃0gl׃TGY I{ Z妛Qg9k;*2G)ɸ5\=$I}HY)F?JK66;5LW"S]8C= ^҆ٷq 7OX=3 LÒ26 CqAulCѾ~#(QQVx)5U|C ~nЧ26K܏cO`pp- |#osLF;:7nY)6 #WplRVavwBɳ"n;=l,(K@v=E!jr;Lx.Vߨl ]{LeG 9mk ih"̱ȌѠD#2e磴oBk+o 3co1j6M$ȤM24WShE93,G;K hL󆤙-ucCw#gYPD{K~tu>v + 8-بC[ڀ>%yPFN|{ʱxZGdakZIMg L:\h^q9}g~5>0fqÁ9{9dG;Idy}hm~JZ*^5(l+ě׊~u{7Е̃qUK4H}OژBl8/mw;AF O{]ŭ5~{7v^ yqCCwO Vc{k)ܔ)TxCw/3@K%M60*ce+hU ~(\T?*o4fgynӈ`^-tPA2DY~jqmNufۮ O*Z%"sY0z߯'oP,G|$JĩԿEfp Xڈ nsa._8K L!5hdURpLj;(N6(ۼ.g7FaϘ=v5EkY&X9n(ZGg6;jZKkQ\jRoB<zGGLEWkD /"GͯgvϾH(+5s 0LVhy~YGY:=Oi2$HIMvxZmʒV kbEW+I藸/m)(aXKf?~z7Qh}Q7)5\P2Oɨ -O{)(TRܣZU/A>`1\3Ij9V~HtZkuHz>2^9#KD=Na(!}x9ښ>|}N,xI@LŜͬdrwO(9ފ̓M>w1|Oj]O"5X?39T`G+-) Qyc ֟CP2.J[,Q2:kIцITs?!4G^_Jdgٵm `H=r6>^y5،olK8ڄRS6GjDưw˕nϺjp'o( x%9hb"D-J:8z?sW:>rB)GL*wc13[.\"W[-=kxEhj_f'ܘbmcE8l\_H91_559xJuvG۱u"K=i LF<%p]mxg׭xKsUіbJh ׊B};bUH% ,Y62f ;\=WXO39zPzOC^ 9<~O F,sZggLq=\r3~29yP|0a2/R ˢ畞X?1MyZ$bi`JYkߍpM#{w/3(`c3Ea[M"q[ԅ5^Tir"j'WzTwҶWEf m ,,AP7M])fÑȪxWdLY.WW \\t5ndgG QC5riӻOȔTҮܘ+lMT ʳIԐeo(P.U^w]OSODYت8..|4T~,Ԗ}N{T!<ێ)UW›󋖱=ɽS'i/[.qߋo$ljsKSQ y,ugSGظٕ%wCuɒET\!le'ŏFv`P좔xޅef"VD6^.Jy&wsk͛&Hu%iNK3sL(,{vp7Iqyf(e?0/s'8Q?\OV_[q?V |Ux:𡢮<è$/OFgA\ģe9*ҋ]E/vZ{Fgy~y\DUI3e}hU-+Y e<۝QZR [YU-̙䭡ryݖA H&HވڂR᫃ͩm 8nUC q L&DƱHmΕPykjʧV\j(>U^`d/q՛ 4?8p@}tL|Ƚ+Q?/8}qc'ɒь%<)]؊Jϙ(4G we%LRyB 3cqK՗nxtݤt+ĝ:z?)g+tvRѵKSBvO}yk&ڑ3gbh 8{7oCLu vx:Q.hr1 ͋RIrVMEYNk~l0` =\ endstream endobj 47 0 obj << /Length1 1460 /Length2 6741 /Length3 0 /Length 7736 /Filter /FlateDecode >> stream xڍwTk-ҤwQ$* = ]zE  IJE.U ER G|]ޕ;{3̞wQVp5N,(jAHD pH8 EC 'ꢜZO,. ݿ(@ꂰ PNp,2 AwJ(:1 ' H GCKBPG c++pEp,juiM C`rlpP  yd 4uh_d? I'W"`( rDCNW¹P'_D(C]$ O]:h;AqX!,G4kVuVF9:pX_ 0p݁ru۲A8Yj hp~TCl8HRBDR w`v_@N/߃'ۀ{#l/ O,a= Fp+-‰?0/? 0k?#***i )("  QIyA_N6(d_5ϟ ; /]8?J7`?oW"'Ho?_uD 0}ï. NM5pkjuPtKZ,* G`np{/Z8$ ~Ez@ `W{l8~] ekP N>xkj wnP Ç={lP_꿠ߖ$2@La uIhlQֿU{K-)|ۿ_pF11IW6+ .ʐl&}LRX5H.V=oSr>̜TQ./ȡja|bXۭqzJ7 0]#mhTv|sf*T jki+/Lafk!,{NgfaIzeZ(b]{z;#\8qҽl*L_Ҝfl2xZ3*lXwM<۶[0&ago qNfRJd笄[Oep`M:Rog_aw v?}h;G@7Vdw WsM0aD%U~tuoݜȌ}1SGvM黁ED]ɥ4={ziiQt.pbJ}q TD*{:.{-8_{49}DpdF]j zj3tF skBWǻ~QM".~g= tI+&#=/( &.~5&j:* Ejz=[_27.1ܥ_B4*]$UQ\'GvTp : 5n)q:]q`ϺfH턘 m3qi~+G1xP;ιvAėQ tAbdCmjeOG9\t)w;:@u^>J {~c1Sm AqB@#W3ƠyO(`vIY#jn4U=u\8CǮ*[ p]kNu(yy24BDOA~rrgwhkmڊŮSjX6]{m4#)g 4߷7C@Ե:^Þ &K98Fa.7`Xo#X.\nǤqq*{׮=QeA+cuݍp1o⼻W˳nCiR {k~X3<5,om;(ņv#ȹ_ ͅwIM3VMC+tU͚}n}g˾QRѣݹ {E4KۑJr~h ҇wkjx"(^=?ʗ!h[t~FtbaV#AL0LlHQ~s:L[}hdYNu̡ҷ6-/zb!c[ȹ{Z'ܸH< 2,z4r'\Wh V %ּ>ϙkMHd#r#sCB c@l?)䚕7PnṔ۵YחbY> 4`ֹ'SwUOtHw#b#\ }A2OOM;2T%W#8G3o֩ ן__ԉxJamYۘ5fGCUƃGb}l $r|3 |Qf$@"~&@F385_Թ0{AiǷU6W #|'!LwO6 N&GҖkW@5Ho"3={KjoB@*LtFM<|Gv&kqs핬ׂLdK=&D& Z>M+*[j?Xd0T&iB_VS_W {d|GB!c$ ;g,Xd5hn0[f=g`HЉn"“?]99>*.RwL4E|9̒Q}T$HTrVmf5ϣ9qv}~3ORT`|:k 8kGS,5WTy&H!;!oaRLjY\;+61 pTjBonQޟSoއ9/itmsc=K-R@J«^s}ީUZY[_ɉ&ֽځ G"Js2Ny][A/)4/oHR,,{"T)D%2/X[ևμ y&CPɧd?޳ճM6a,sN\Q&¥5~ J>9x_}nEL+b*!\\)l11,;ؽ '˙Zā?{bdN˞sklgﻉIsчmtM8v:{ 3`YJ2t3gI|4;Xb岶[/cfV7>y\0k[Ԉ+ N);vVŕ$mޱJ)ܻgg_xqvf9DzwĴK@0HN;^͋rC;S ԗM7jw|n,}+\>SsvAԟ.-ct-nv=y$͛4y!)]˓(>ž79=" c}ޯ58Ыl.5uGO=_TJJXJ iج`Zƺ(;WAA-Zf 뗈xJl*xOЃ*SIڛ>aV"7e'lHWٗwMh. c_Ν(Vǽ3 u 6~0,J|T~Z$;y>l͔t~#l>Ӛg^U/A~R>JަaBoyCV{B^hE5-',c)LS$m-B>Bl @udwO9KvDt(Rp26tʌ v .cfU{ $+ hU׾afUe\VGIZXVֻnkω:ņ& o5hgMz a5> ZyT~yRݽv3j2.>txm#g܅1geVM=9'K..(rOYOT۬vf{E^OC[ gvc(y/v5̈YIG\Udx}7sr$ي-2=.¹A*, e*k%k)~$G8(wrT(!=bֵPҲiSOzHIaa{Kj#o2{$Z}BI %z sL#O"̑uk"o%{GwƟT6w9X{ 3ǴnIUb.2[6$ȋ h2>ShjQt3( mAU6+0b֭wyOnuXOzE;:&OCNnE&RQ\ouRߞ>jY *1tu2&%=|X ;zz_UYѩ>;`L]{TAHT=#qqR_Ie.ʣDqu#8e0ȖчYKm%ՙ,~nv=MR=MIUMbׯEXHI8fr}['[AxgSJ `Ѷ8FZ.2,Pr‰GFzR/rϑ.Zq L"|Wũ;{|xc)^w{տRg^zբNY?wg= mqun/_4<&)+,j״)$^?Ϣ$5qb3ΕB,Rw]^GL:uiKrbkwn0<ҡpw&VjP'vtcbn͆MCMH;gH{DȏKhDʅ .{jZnjT[:5۽d7U " x /3A-ŝܲI:g{kw,26p(,'&D"ƞً21mjO_ D>[=Wf)e%N|6a1ҁJTK #;w;Z^rR}Us¬mzdL>iX;u*;dors0oo&(ұNje1Z-zѨ'*랐Ǔ+סӸ#j>T\|)̟ R`ut:9b=D f-h8cwLQ?gnT`CHImAב+љWkt%wmY'.kG˫H8M~{ n.ϻ6E|9۬?)n('0Dp;>YJ[Tvi?l̪/ ~.6!):('-*ֲ Rk0B)K9_}yf!%$e*Njc:0׽T tO n$: -k o~S\a!~:LgFuyVBoO_"Q7G3Ķ{ .y\ilW$\N*8$B) u '63D,0> stream xڍtT>)tC ] J 03 ! !)R)Ҋ !%)|Z߷fߜ9@F-]Yk%T GyAyu"H`HG_n< @ A| $ T`XX,"A  PAnx@y+_K,&&; uYAu EXH`C"<==y!NnW[)n' iЁA]=ր_4 N?zv0?~]  0+( Pt4?`?n_g]_`++3 l`P/ !nT>sX;dԀf sF ''(?+ u|ne6vwӇ\ܡ\xlHHTD@u@~vQ:#6!0(  ]ݡaVH%: c.x Bq {e57*h+jr199 `0@g-.@> `ПnQ="_Z(o@VL)?Rrwtf'_iݑ(#P27GPkG#!(!mQd anJ0/ ie2Cn_o * J_V ?!Jlˆ>V_@\]!xGYB_0JPHT 5? ~o_z\6ф+Au/KzAgV!!mgղ)̌R^}Ycߵ uނ< Jl,gnOѧ}15&=/rCӢm.ȏg.ѓXID%M]uqr@o]脊-{)d4&UɎsϪ_{黢T<XĊt 8UKWE;i$H9Nq$ǯ`W 9mpϓ)ʐRG/La=~ۚ%!*̠;mq2Yv@tUeUpIY}X١*f~]b}=#FWOKE&~:/JyrgM)]I)wx(8)?l:!>ג/zY3vN#=Ck`»}׽擔l>1dp^$8I&E K7oi J#:|J޹MgsD<c/HiSx)̏`aE7X>_Uhr|ʽCæ]#=?8oX wЇɱXՀHD~ K:Oaļl= Q{&+$Q~G X₫=,.ZnV[[E@2[:M>g>X]ئL=JjBkkS5$0ylg~c=ZixV䭹WjfnG lV"L?-T e/8q̏+rfﯛ*L}yѫm}sߐ_R-w1*"|EUB-,tTx.f,>by,c-';-ܾ8ЄEAFQ xk~O=4ϳ$[,_ye@P3mE)JSՏ[E98*k3TUz}#hf޳p?"t/LHސ#7&9 (btV8P_.BxjإxdEרŐ,g,DZΜƝLJo=\]@İS)WCNk,x 2c }( =fz bC8AME*|D uKnjI]U{4#yH>8vq1%^ /;DZL78䬔#oqMF=<&j&HM2œgoQ0CFPaB* __ $C P*iT44 -]n>axS̻Iɒ-kbX64E1Zb0̜9ۡH-A#lPWI`N9!iSf%䂵>}&x$U%kJwtӮၵuf/HΚ7_*Hצʜ6K7B0@it Q~f-pPt!ۗýn!ʐex^-Iqо]VU48yuuBZe0}wý^'y &Clɞi("qEhԌzʛKvadkN/Wf0(rW 8ps0|߬4oĦֈ0~ ɝ[X }.\y&6SA:c=2?f~ j2!rZ3ޤm.@p{4*D^(1Ƅv-I}ޟG叭]E11Wm >|?x$0 茯x;qȅc˃5sʼngoi2756+U&;eە%-siߗa4U!~3AjwSqmn_ zWY#k+u@`婅wR]Kpd>e>LV,1@yC6xN$=ϴ6A,5]Y[1=ۖ:Cw1[H8 F#x6Xkh}c \6{gZ^̽"ʌvǞ$, gs2/+:[i׼}{J}e#8I=9̔TUx&d}*`8Ps ɻCfDrʨΫ^9k}v9=c`-xNQyʤQKğE}@|OfTr;l:7oC:1Ɛ9i2UW+v&d6H~a >Kk6uB`?@H!hIA?nzK5`CHnJj H\7{lW~(9m%}C|}# jOOŏۯ0@8 %3u 2s*Rb$a9\췾cfT4vU65rM36KEhoR_|ZNPp%i5 N3 Ďm|ASd0m]$kyvBQ㫾,ʠY})$֙<`>ӧ=ⰾrgZ) 0ۤIPk")l[JZ@A]/[G7/: 9H%(Ri]7։AQs>[ӚƐ"Z)}E^Ak`ϢA0zx/`3mQql :Jw;ͪ60@qwI(Z7fmBfk%]ydƼS4Y .1@:Y;mIJh <ք$] jv*`#/FPKʸ co;MçI)ՓWě܊_Ͽ|4P $wsɳcu|CƱՎ݄ޥʡ8rt|ś?{+=c-hzirY_t§X1C2ٺ:H/eEg,%x}bG.+tQQ.^9b}MFޡ>+_e$oyvY [+<?)^En̓ɇei$f_Vmk|3|}a?v,[[Sj{اi3Ig=,YROͩ2;mU~յ0O@8|'7=pXkr %9Z{^3sŪi\04.6H]K7Py2Wx*F~ Zg.aG<щll GZ&mGw;yhwbf7*|p=b?1yF Lɱ2[cQN=w3=燧E}9?Df%ڠs/tDh^ԕh,h+ORvXoݱG7`tfԨ^$[*uǣuk(8jqt9a -bx P&ωaSAA'܇AZ|/vB%dbUc6",5bo Xa+4@NPY7f@tA4ԊW"k{S=:9_t3p`3]Nw&*sBH2R6':E?hm-֑N2aǡwm%R6A}/-o}58N (V)/,C8$Be8xD^oSءz%WleW .$S A9Oj8WX('y uܐGp3= ᅯ/=d!ܬ ԋD`+<9*iCa:톃)sFB|4OT+>n',i33]"LkQX-̹Z_~gSkNƛ[p M˘njfj@ة휞Tӊ$D_@ZP3{;2e9#f J1]'KZW66t  #-y;]Ns>>&T[y8sLo;1glQ$۷ X8݁+RX?o ]ȞíըZ _U6L^Dp_S;d B}bob\\wxjuP[|'tsD=egM)7*d3E=u'#]~44}J:t]zn42N}oݡO{#FU2o6;h+Oij_t@,E՛*TpqP¶t:u/dfRM>ْ,&D\KPIb=!(ħOaLO̝$uN't9j7g73O T(KsUvCzY]2} V2VE`Z#E_`n֨[Z<{-YYƝC; hvϮbp>XŒu 6&2Wŗr*x|n5} KL#IZ7zf^SSbW׸tqGֻzԠ؍GOk23̌#0цCQEhˤMI-knN]אp ޚFP|Q1 b3i6Zqqo& NIܓ /gOr|+W^ f_&OhnkO|3-a0Os CjH Z@췉?ȋ}  ,vmėRhŒ8Z?(zJq I  b H4–ƾ*,ٲ1g3'o'qLd=y-oIq nJU5e"ӯ$JbTL܄8<ju>zmss}ŤN]:9/Y ʹʟvz(2*ȑk_)KJf'?Ky:GVL/&v?0^ Np-e ݨxQ?p=X'=ˍ] I(>SD/e2~7aj )Z#o&#S?yGKFl?rf96xrƛ4npZ,}LCJCkSϻ'h E<>B𣵝|*OK=>< Iqg^Odڵ/f?K /`ͬ1̊lVк{LOC'Đ3ϡ;-ٛx7B\s$~jW[D~m v5}D_f`K柖d)tumTF?-_\h4ޮ\l.>tLp~< 1 {4&NɚreFademqA=73sWs}7_?NU𪓭C2!i n0J 1y'$b˔L#~m;fB_5 4$l"Dϱ/WV;4n!:OL?) q endstream endobj 51 0 obj << /Length1 1463 /Length2 6659 /Length3 0 /Length 7652 /Filter /FlateDecode >> stream xڍtTk6"! J 1tw730 4 %H 79=}kz׎{{_{0;*p/P~ ౰BPpaahL Dia ((HE%%' Ѐx,pW#ݞw# @toAp{RH/ŝpxA΀`w0Y0@]/ 7;"@0 @!`;FЗ Ե`oc܀?og 3 @`NG UEz# OCy PW >.Oy+ҝY"0WV9(]\0; =}~w #8B`?pp3Ax&%a=`_$H ]^QA|`7t'B|p߀8|5?o D"_ MO%CV6ų6 =ʲfoqNxGyEı޶s+f/nG'7Ix0ր75D5-Pn8V l71Bפ^:Q}k"X1q*Fl$-'Ɂ7Qkz.X|?%gg%-TXIY65(' 约_H+<մWxM7W_Bw&UՐ ulI͎ ƭ_̈9oնrsJkIl,S"jnV"-F X. [0)'_nc g}PZ}Ӽ4F< " ѹ YP6=='MiBd%z,wTJTC,<9_H]}m73\=Rx\}՝hcbk-AWBφ9Oe릨Gxs2ގkǴkRGdb(}‹–?ݩ%?m5N6H &Sv;w矱b14p5FH çf?hePG-@R8& 0z(uU`%_5CS6^黩tILf`G WĖEv=p7C>VݲrTXe&lܡ Y#ު9Qԫr xZVs08>$Xg#멌KTOP9!GxjHPtG'/Rl<>Բ=>6~?(#ʤ8p}ö2E8)R [.U5gS  ١[A8oߦR&oSe߲x|$'%=FaH 4=[?R1nAR)Ԑã>TtWt,nABB i6'+ ZǿH[|qe_'H|p֛(H-}|;phӣ -W|?,{Ƽ{6tQag+8}Pfs)c3GeORH3S#ref@BPJ68b-}[{Z(J-qQYjoU@ i?B~ RQ{K\xx)U^˜ [綩۷;rg@Xr$9 ֥P{¤(Lœ dߧ|v)[0DFhWb~ĭ6&7kõmH'F_/^$4jo^gWX78r 1ikOv_[`\ƨ<%Ί4d7Lh69oBs PR75vc2x7ny|Cec1`jIL}M?PgH#w̽c$1\P{8'`x9GJ{jz a_j~rYd]@:Njw,Iy6WkP<7T_bXr*?rLW7s (&V.dz.^"XwZx &Ew<(Qʸ*?k!k0%`,ߣrQꫜdLa \-u&1[VųV.L"'ɲKV>|N'ZC:=[fU@ȋꋿ<&`OZ5ֵ+nwƨ&cI; <[T~ndsKmU8QsC:c\mېpw:q8\R&v,i=f;IwS4wO"ο `)99 i'Ra4ח R^B^'Xrv6_d;2TPo͂e3E<[L^`ihP'UndJ+֡T^=~fHkUk3 3$\Z_- 1)EzY[!|3Ax_>"}=c;t0%;0'wW<ÖciZRnAIUmv$r) lS[1"+X#/G'#M1A by.xɳTN^ pNETp| eH)=&Jp3<6kT?3xW|x-ME*]3RFAoG맟,G-O):(WQˣjw>Nߊ 2ebIŭDX#]|8 Q[ێM :(X>PA?l{ɞ4ː.(7KuYC)QDž?-uN0,>Vb5 AN1c-l!JqN|#|ہ4^ unh_޲Y u"K̤芓{;[p x:G_P'|A5!^ǣ-3Ϊb%oPݱ.QP wev~Տn7u?dVwyW2S*>lj# R&vsKp]*p7 5g>fơkb\9g@QjɥKuBF:;6ڿb>%q03fV8Eɲi\^C#MZaO~6t4ZOLg2Y{h TƇ$oX,4àJ_fw Rӫ0wrdu6G/fLR3oë*i55W1 Xo5&{0H3TXW;@ >>+_G2rn%?Ծx1@p19mӛFբd 6Gmc&F5fձAc,Ԛuծqzۜ܌8|QCXl5DŽ<}E4˦,,"j@uI۰mWR TPIUYc-(J0QS4k;93{8k+I[f[Ϧ;|TVu}T\ASۺl3jV\Oy1#^x9ػs 2\[g?m!4.lFfsC$u:fl[*WnC2%K<vd[zN` XS-}Ra 1\}/(MUf$BxJ3*zB YTv |Tw;'[P / [JRC4炞J4J;]ߗzۨ>AÿZ[>[zZ,EP pZwD9;3ӞvbZkNYԩjK cyuo7\UG :?Y4#xͯ6 !n*}?q k$$̓&Xz띗- <7@=#ޝeEj${N0^nz[(ñZm87ߵR˭ZF )oU?T6D"k(+7OsuByd;IȱNE9nw k[ QڤyƆ`eG&VwGMQǷO ݘ;5qWf8:h%9ξьo^UtG鼰z9#ň\姟{q8F}"wXrxȔ_W>G|ۭ??NS'*X?qcu0?u{ۡ5^e@/wi,h܏80萦NxP3tn9~g*!p+M~qE^2\vEݧƭI{X6x!>>E~h܂Q'3\,K: s !0cQ6 WL:qg+91^%Q1I+ad: v3|pVJKIyu|x<"D}QB85y'M Ql!sFD[)vVMzy*UJ"E\:_(s4Sm|U&(Yp}bPCon*BR8R=wpLDخJ%*MC@)Ihv:6nzxoWX`% iԖzBP\gXl*gGy-$n25˗'z~g_u$޷ɾHj;WGߜ3[¶s Wr' %J/LI=p)>1jWփUSXXa|S^wW b*^lǦ{zso=D? JQb nzr3kd˱26br֖hT@+:h<8(6+"NN= A3󬋀29gm9hOzF#%]ՍɉY!6b_bHSjsmݯD9F))rTM&n&IE<siM`MISU Q`Xt>C=Wv֔V w^H$ޏ>@)Z :RDHSJj `Kͩ $(42qk W*UܽRK&EiQoϭ6CȰ@pU7y]L7aTL&Q|s(-w EnLjr|n,1D f冡H JG}Ԓ4(z}wg̈́F2aS<:7D 5V׺Y>'+tS{թ毶lwX ^?(J3f}K#9gw/=6aD'PڠYqA Q3!2v8xӅNF6: endstream endobj 53 0 obj << /Length1 1376 /Length2 6157 /Length3 0 /Length 7101 /Filter /FlateDecode >> stream xڍVT]$id$RPSjf`aȡQJ@RARZPBBI %U@w޵]Z8{<F'倪&2@0XB !q?(FbrͮCqwᦏAu|< DZ"#`9br@5//~Ug⊻8_KLTFH ԇ\ᨋaP)B+)AQbsX_3W@( 11?6 p~P,xx apE^ 4z8 D tEJD`'DH8PCO Bοޘx/up]8l ^Ww0,-!WKVG;bP(8 U ]zX?4D;#~ 2G#|jy\@18(ed@sJn m/{b<H{C}@ .H40g1y,x|A<[G ҷ40M** ^T\(*.B @E?AU;Ve{qK* % A?s`.  Mp[vw_Yg=> E!=_w}}̅j #W}3VmBh_"[w6B`7%0$nFE!`.Tshx_ ~!aK\Jb_준xȅ  1E{@ 5O)q B 犅#8 ^(7. p8 05ݼV}A2(CAQ爆,MEVv_9㒺4)~%YtSqqЉCɻf5=\fJ+A^Ačm:^>7h5kzfFM,֥<){/`o^<Ɵd'E.İO;?ʐ3|έ, H(̋'~zf&fE5Z|IlOQv;͐E [U}WW\~nQ6 E4c*ӟVxIrB\e u({]^~z*޴/4.[z(51ːuC(yO *#&Ũ@SpB"c_^{Ddg.>^{pk(ZG "k")+aޞ-cRSq3qQ$wĥ9n&+9Xa[* mJņ>ʯۗ-XZٟ|}Y`ngĆhb#i(fu7C?3&=xA;υ,*90\ Q8.شW&a2)YѸjgYۺ;~y_R& '}SwID;m. W.md(&x:;$s׈喐on>>L`NZxa=& ֆ( \ 9 Fx{#*s# x#yiu"ɽ17{!B~MgO׶x_FȖ$Q$(R&&}-ʇCc+kW(./YJ1POy oj|nUqkY}e1֞p]6꺑[&2\ kt\Vah$6 m·uN.a2p,/g̃G0yJ?nh~/蓒~\,iN3:P;ebfCC;EڭDٷKUCi i%k(b m3Kh :X?v=TƎ$Vgs%v23毾|&t(_!q {IXګ^Iպ 6YoHpH0MuJa]Ys~4F~a:z 6"<"͇^ՠ{Tx}^3#ɛڟHrgӑ~tuu׉ˊz?/۔sYٕEAb1b&(I&&?F !]q[ʂ:A[ pFrў n3fV@/>:d7o(V{T.{MU}ep2.<sjb<|Mo#dY) ? ؗ[t2̘M~K+1e+i.9k غjTW&=mwЎNwUiZ%&/8m_}t`W2Zhtsw&ۡR6Wx[m7molKl-ߖT ۭ1G[l[ʗ9#Pyvn,ڀ;[I 6WmֶMCIRsh*Xj7^>GЀղ4ZÃ"?uOrAA'v6Vg >{WZX)f0S_?NX[8 %ӨpRN޿'e`ODo06*hBR\UY {WB6W@z \iTrW"uTFWnI$R`|pi53F$|ʡVJ@CHHO/1 v8L&R*d`Oxv1+y0$dZ`xLzuHf{AmŚ;2w-!;ϩn-Ac)uZr?~}I=G|7wVWE7Ks.:ɔTCI$oPr"С&{iZ%%LsX' ? O9Mhye %YT:-aQa ȯg5K5ss|0FlmG p9\aaKj8sxqnepZH|0U|vu0WCKӐ434V]~7ӎQ?7=KeoۆwOU Orɤql6IiK=̊Tè<ȒE;]lݮe2ylhRF7y[3 r:sTeAaȶ9Es/:K[ڳ7-sxڻJT;`"BADJ2XS6 ӵ;'}段vʲ g%u [UF;Q.G`{o|)-1f|QQw z6N_i'  >͓ ʧCs_ w+e=AL]ziJ(1Ռ]TcRUӝ|OѶs۾a|ad욎ɶ5SZo%u(%h8/'Cfv+ȱ^@]6ڢhcRhd>&R;ћ7J5~NJ[kӥ&iLud* Ss krfˬ%41]I"SQҪSHvS'mZ~ff\M+߮rU8_`Fiڳ,)BȣOE|Na=o;yK4wk~$foDx MDYiO}i pnyRSYN6 2٢X5>rՔ+Lu񫶛#X&O1>63$g (yBi|ODŽ߾HwOɂh:WL}rրos wl[iT͝V)ĸoAH|kϣu) o35jCZ 'm>5Wpd5u/5U{zA pL׊OvcJ73h[;:Bi5jG4Ux=i$D'W菧V(Q>%0S\(g@EʛSB$}J62ƁyՅcf:R~Gy?URx~/GB :$YwJ%+gq~{a?_ACT Nahp 4pW h}mFg%[f:bF wnZݥS"'N\B Jft("%j!Gbr9Wُm {&_坣6~BZh Ype-BѽAYM1OȾFINXtY@z;RV}g Ft P Wbk4¸ٕuV/KRܚ )`tLu+kv7jLl:rttQdS`?NAw稖\ƤJDv{/ٌ] A,#δXOO Df6w0W1;6&@„;gdnVgswvHr]TJMTq%4NHr'sO GVR'Lx@O*b8$bl<^Rk t|kb3=(7j)rZ&x2ns4F h2#圹 쪊=bHo ^ 0k6^ Rm'~MWuPbڻSp*A_1x0) 6iI̒R|y0j((zGڝb}x|ȳ%{+HGYϔy>04vϠ%kbihA.#Al@qw4bfEtƚES'0_la*鷾{ IR+djlפbp;B˒Dti /Iܸv>(s 웋lP d0ٻ32iݪo%PpλfnzX wޅ&Oљ6rq~$)o*eOh/r[~-BoxJLceQU=*R`)sc0(!-Q>3i@D5NǵȏAm1SGm%&3&Gb?oW*&>lu@`ޢm~RxstD3^ѽGL1xHCxlVh9K8)kpԁaaҝ%ֻ>Rc4*[8a'ϫkZCE>kǪgno`"j-Dms荻_e&^„d N~i!F}w{ᩍ4W 8} endstream endobj 55 0 obj << /Length1 1396 /Length2 5966 /Length3 0 /Length 6925 /Filter /FlateDecode >> stream xڍx4\ڶF{'ä5z.D'A5BѢE 7ɛ_֬g?}ykϬfh"t#hAPgb@Q! PCqCP0$B?*^P0cS1@=$ D qi4"`@OD@QD*HO/+S[Pz `@vz`*Bp G{^._` EA|N_#? qL]a&Hg/ 0„x#^Lu. @gs !п @`? pu]!ZF8($&u%#3P/'%(+ fN*H("՟* 컿uG }a'_c8y{ !`Z0ѿm.P4@ (%..@~W_L=͘=g`3E@h/ohp:"N0u!c:Ɯ`0'$o#757S3H?@@PJ@ 1@?aX-3 W}e?#^?s#1̅xM{@1 s;_YWwGpo?_ 0FcThP _Ճ:=۫cԠp0ZtWx/; :׿X0z`aDq\Z~cOdE幕.v&o_8$概Dl3C(YJJt^(T .qy*WIJdsBչhdφakΕ gUI{/DEKӉ`8ʅ-l0yJ|n+=錢@h>Y;%m3|KI1w!IkX3r.=%~8ɈVsx!`̪x_ϫ ,Ŋ{9锲ˆIOFC|Rg9]*NV="%|H-Pd銥ibvGq6&Z̧iNҨĥiONNwc&(T]?n)g_\(D8s/=yh aLo@0H玠tC'ꮻ93@Z!R2'w9|ql޳>|1>=snWɱ-$<3y+2pښo.f({`aq6˄X&y;$HI8G\YbcN4'a=j꼊VL0$80vjpXh&!S.u<^\o8XM_uʉVsTj{_JYk~c|(\k{0+@.g4鵯7y(b1g3<*KLYuv<;?Emf u@>(R8Ҷv)1@CE3<T˶ݐ5&8ZflEϮ]Ijw˜k6{Z_` 38$,0&#b/%xy=i@R/`jmpzRț~!EXGYQ "*{7 @JskFɳ*Ip)R% .OUDTE}ymƬ>}v"@lX d; Uw6iFP'a<RZҟ Iz|'Mop3f=|Ε&47zPiG`]C<3AQm3X.eBV=F엕xZD3uWw @껣іzr} ч[u#To $ Tlg6l=QZ%m|J?ߪ n׼.2 C0G5=B˝˻|fJV&'9?šj`=q'[A=eN?Ms=>=Z_Ee>$pe{ MX~ԁh $3#AYH:}3*?vc_઺F8a-iqU >T3H4H",OM#/Ȥ@[jy87\&c ۱f +{4.p ?s M0&{4z0K^E7Jyo"&n5ΚvfP,ꎯjة*mʏQZEzپP/\ae&f$T+"+A+λY).Tg@H?1 dƮ0|u\?Op/[{r쥛m%DR^8@aAu?TN"F#91><$1J Z@Iz/IZۅmi(qc ͞@4a 9B4[ wwwۻ{u4G:\,ѓg:V{h>yd~[}JǬEZb-@ٞB0uě ڦ'_h[@eg܏ D)MjJ7={jJo^ l2ʥ' z̡^6KSu؜AQ2\GxРr Yμk&V\P#Sڀi[7eg2n8*洹;W8oⓅWJ/\>AzkK6,Z,*KN;&mA/CMÚEg&̚v3’*YpUKFʹ4 t3ΙƳ4)Ě^ƇzUpؑt-W'7 3aEsIdg2:.wߟL%ȼei`5ztUABvDp:P=P~ L$}dfJn/ jgy?mPPhQhU#-Eًڧ/%P6G LTĮ4\p$='})JvuO/2#=MnP8^ιˢ[5][EFoZs9,y~G|z/)%t=Wѱ QW +KZO"\NojoI+3SM !n"*6ek $-.JG-#0ʚU<(tr[D#%'ι%k mD#ů&pO 8`{V Ck yaǻ$2'VVYg?X}T;9l9j ndRkO^+$,$(%T~S%x5H-k6`A)#MFhh,]7plzn^tN]e F3VL7k2P,K _!A7GDchv\;3Www z o9EǴl/v. bhtz2GT44lC3ZdEI;pY~3@3@x)_Y(C37ZqZ|v򔫉5e2)θm#r|7 j8 ÍH>85`<βh(7? j<2r?F!5P*{a7 }s!Ll]SҹV/&0bw)ӒVZlߛ˙[HGnˏE8 |`}_gj÷= bdzk<2cpK\?BJ{;z[S?7ubO~ȿ v^[x] y<E_[r^ݐ{ o;,vdJ>7 |ZDn\n._ݰ Hjg9+}vR L9ݐLW4l+>M!H`֑D/O*a&S1b=1O!#A4l߁Mt}Ofe |aVo0w)Bn\Dz{5hWe2u亣0]5<,@![;Wq^z/{\/ffF&9y=.^Vi&V]/ԣ**fr5zpzTo,r83c/= O]u~NK 5ӣCRR iC?JO3-Lb߽Ng'1hapu*.Av!H$ƊjU]= 9Mgv#Z+ WosVIl(&}퍀)c- r, ٷ=V,.7{U8-7x\T {+{}0Qf;,X( qdBk1pbQ*sph8ED t'SȾ$0ģ)VuCyZnk PqD xMa+U{Eʗ̼S\x!6Ϲ$ij<&T}|,MpMFި6 *c8M /x%KO > stream xlzc&mӚ1m۶mi۶mۘm۶m{_čsW\UU$6NvZzN%QAuFzzzZ&!@ `L#``$CB dk`njD@nD$ 33/?'3@HN^CBV\LV@ `p0"w627"678(Ll3 016'Gq88C֚@FDY@TNV@DNYƘ@Z6NFnZ5DVO?UD0 FNSs&acbKKr?RLA;[YXȅl휝2A[+c7'1hks+/"$ C2w5w˛;#?R;fӊ}5r Y̿ѩJ S ]1561%PrGQI,o`=E2NnZ2 F:%(hID@JL9;8l-?kvF0k˶F\A!>"T*T +S7ZHfzWXXyp3<;MD(0eb Sw0k0ULk#Y;yI̜jIR$1(>pՅG1 2 o8>UYp!Hh^. YAafA;c5ЈmF{[T oO؎f.$b() ajCkff6Ydɼ. U)xmiN}em)|tPv#yu2ؘVy},k10{*3?b.'v{iqڣZD.k$b9[46Zj4\I&Ы0T<<<٦z၈FQg.'P8kiPOk #54`s_ ?PSdiCZuNn1 |AǶHwoC5G>~B87NUƕ09DAK@ j7+>ͳ' ɉKS졐^REDî I?k-7%.Š@N`]sK߰r-G0w1W1%H*FkŚ܁.n-ׁa[xZL;6bŰz6`{n=QXݨGY^|RznQ_ e+p'\wJ~Lȿ#SWuUʽm}~e*8${Ad^ԝ;E9f!6R[OG3h8JU`ކOFfZD<ZF{bGuT =F}gwTυp& qGoyy1lD0+Q~:qd C*hǚ9oi}:>qeT+XϿFC e~K| Yo_FWD~wp#a) #zV#+dDwr [[6R@Ckv~x]H`¢XHs>"ǃ]BOd$qiȘ_?&8hϻqIc|"N*N$ S9Uw%H> .0{}!.E XFߦ<%9VSɊ}.]U })u5>=F U>ډ4Ŀex޴T" f]أ!hНº Zo3J/?Silӿi. ڽxEMWGk|@zJ52Xc 0vG c ە%Ǜ0Pйlft8¿]`nPt첗 ڏ6^3HtEEP|پ3ξt4/ @raIЭR2d/-rP2H&(Yn:Uji#.)j!8ƍT}Cu(bUDǿ5^Wu ![TNdqC+̂)duJ]*۹^s}+W~bv/q*bN=|Ips9B W#awDNR$$,SN@xK,i-;v򒬪EQ8e!k"&*"v>TrW붼70?_Q[zTwy8l6|k AWZin+`)VhgGeDf(_ErcH)?%(t@dNWGS!b;R瓭o5[z˙5M2iSaJtLMB&'S5.bD)hJS,;z;7.ׇ5oAU1b/n8Y.|A;哀/_nS&/\nWY@vEH[ I-ݼL#(e\+V +qI'HL-)<3vذ׵D&;w,1Hµ(BKFqFҍ4BKrz@fEqbtU^yb7 QIyGVJ1wl_G67Gm @+\kJ3a?Z ^ eճqLKF\/m9ckR/WMsش롟ܴ֏/U7fm mkJLd e(.eV߮vNĂxk&Dh@bF=j> .!?+\ N+Jji!n R̪aw(//R)б<6\&Pu,w6ۏxh5/5'V)MpksJL3?^|[ '=t@A|%~b j„࿙ VvJK~l`#N;⻤s3C>`6 xxOJDmRZ_7N)N?yEy1:8,hHiKw:s/ W~p/d)^Q/{_oٟ.ϻ1iJnΐ"fXOO/hgD D?!5řmTXo ͼ -H?T=R̈Fnm[~Q:C7O*@L0,)4faP nv3+>L6Mt([c -ɰ{؅E[upoCSV=tg\Įׅ>ԥAͫj"q hKЕNSŘ0d,'&M?J%3iavG71r(rEѝ5Rw7= ] '^ձ^}[x֡eJ# ԸaCdΒ({G C^de;̃j(̧^cpm$u[Ʉ=Ыn?}ʼU56*5&~WP zˍf)_|/-]_8O2MBgQݲb0v%%*G6WQqlyVhń3J ovrϷi>c"ˁ"%d0Eb LiVcC@_V1 {Tf2z$c_s!&9ӳֺ Bz?u$ m*++5U1: k*VlW]xw48iП(c&Fr;I:!Sn/oAE7A?J05:Et2ğnTM)|8bЅʼ.XٖŽ~q /q`Z,Qu.[%-<@0b~TemG_DUPj"\,Լ.ލ،# G, S߮Y&ͳ,~SU3>1j3],']o*v6?U͑[sgcS'?n(սۭ׭e/ q=V4^[. j2S#*_ $f ؉kKF-Ќ,tBDjӥtmTU! 8MJ$RXljz>ٟoWcSV$|$%Ö,lJ#F35 eM6+P~=S%F!^'Ʒ) Yw!󌈘!Q-?w3?\zK&N<ˀA֒ýgvyW%Z[T&CF2 sOh%~ާ2 .=6/,C}cgbMTTⱅx}:1?g&r@M EEaY'>HhoXMg TVܞ6FoK>Kk$ACP=98:nBD 4Nieِٲ5\rеkOf5i!QѦ0_r6.w |ݍt!Q+VUnx4Zc9WlI1 Smr?kn`| ip߭ES9[ߊ a<3ı\:ŷ>]矈 3t|Y'XK]_gLKv!|_? ҁfZϋANazQe52}CM†+ az(7 =<~ZvFcWZ0'9ppb~_>hߵ0Nozi*=|%%640Mm ?cLiE#/cf6tiSfXJ!REn'fW}ŧl)i7/ElfO \C{y;&9sΙR6#D<9R Saj4-h$uI+ZFIh>TMyLX2[Tz|C+W|[VO~BwWq"OO6jlukH|][wvmRy4DpI/a85 '%be {:_xQj>Jfj-zeZᨶ*?b8@0 ȯS|i)Ћ;-M >Q%xO t8p^["0 yGd³aו @By޳S7&O0Zr=eU9/a=Z K O#z!8W;Y+TTW\VJ_G DJ׾M㸴\nv[՟xh2ňkkx+w,F|n|t;pB{ {{k ^'⯤aFEm~+HKWi+EI@ݼh!WPeGYS]ۂ5 afM04(]MjdbIqz3hepo_|X}d Q39flL6'|mt|,İ e"hz.@Pyo׳cVxKCuC $ZDbA0G 4j`Oܵ#*wY?aw]VX~P:x5=6ЎO/8Ȉ:ӠMY#e[Ibst,|ӟFf%ӎ3ϑG2|`{tX٤ /{;0uѲ7w$mCYryaus׌&1yؠIm hrám`l8 NS?iA: "sIVgnY.h+\ApqD#cA-~$􎱌{8n-@uhDLo PR+]; RP;kRAk6˔FO*PEe[q#G-3 3.&7CZ9%Ӱ䢍hCP񎁞x+!..i,@@bP9(o\Cia\Dod}"3N6&|]L:3p<ֱ*Ck]ŊM1Qq\aA62'vB>0}IP iBhѩB,p*xc dX"@[̱#ssW;SUU0dLqY-"9ʊc -i fjWgL"@"LPzNN*6)8;P9 X)QWs 9w(-7=|Ǜih XYu0:Ib6-0vJԊ4"-xRo/!ha7tK}SHR~fF!Yt%c .yKUOeq>jZ,'& ۏJt^.ы"r3!U̓\ē淣_ͫ.@1pI'p̢3=S"C6t {c.4 ư~z8D \-B )'l| eœ\P0L]4([L>Őd_i:-"ob:98<Ǣ iHuPRd,mH3= pdolE1Ӝ»uDj9lO[/z!#!Sh^fOZS=YUBaY?uȻ(񹭇kcP|*F@^h4Y v[QF`T&vg, ^v  b ?8{q۱&W:'PBDMsJAH4NH9X})Q/W'}rdF5al4"_|3pen#Y{Vtjol{3}ia$6$xttU8}gR݌鐷(Aavg#9^S1"gfއǽj~S/}6fD El)5X4_ݙ|V ;ܝCࡿH2v';g1jjBMA>+J UoDӾE KZ5!k, =[S% bJ3%s»l"6]pi_:)(tAh9X 0m\">LzKܰ~sY  CO\>xi#Wtڋ#v >I>w<$DPr1 eA`U`߳sU l! Bz9xHz6![4i0e"t)`sP mbF g{zO,Ң06WPԪ2v’ gƠ^>0陜:VU;o-XqMLz o?3!A0fT(f K᱈ݜnWq@Z@=4Gμ~ jwѓml융[~Gm\uitq!6UtlYƚʭڤ\&Pj֋j]J.-U)*O縝u^U* ciy;fI>hcWnfs~6n?#7q]iۣ f;%3q]$&qzA<^H $rH7O. ׻7: y,u֌7z'ϷKͩPeo|'h jLC~yx)&![Ss] Bq1F_7|N%}ƻK6L;bcT NM8+"mY^E]p߯6Y@P m<M|4.ZHs EKR Kvtr+*(ST( Z,O'4aMf}olSbi=« 5޶ 2AL*+h%!WEnY< a-/luTC/)wj=SJKyzS3!7䴊$-z3eL2?}&Q1#eBk}KSNjFt2u84Fmh?Ȍ9!yt_?!]ԪY/c Ԧ_I\@Hckw4E;.b/_5!ʭRaywPH$%Du?ұRZbvQa%fÊ'Q'57R$IyCXU}wDf02qF嶀Dpk頶]F[T6آFe~(~~ڟ(FfM=jk \<^nrZJP4D7!>w\ѹ9uSъd|`:ӂjRSxero"rq;4K"OwnPS|&kٍ 6U ]T*&R҂R-C2O-y 겒0XԷ"Ŷt軁e\<}`s X$hE}a^%FgSz 7X`\ VIXM)`!]E@/s9*['fǐ# ex?:=3D"Hi?6o;$#`W#}"‚CYn:s/fJP>+b0:vp>,3Sa$b]ƺZaϽhyιë^/ TtH o8sU XG^tŇaEFw}I砹MYLSiuKi02c¥5EI"c^85);LaHV t.eB1Sh].r6tO+՚Zxoe!^wР.U{0G.gSt׿f`@E]D8]8Z~"4A/9RXvd) hjXt16 .N: CKR7miI4l?}lɝ>=$)>˙ Cisa@n#5 { ZtvdcîZ n [vyyc 7#ԕdOeN'-#~^RwHǧpH3wuAUǻ1HV1Iḷ̇hCYQ挔OP*6ئ# m۹Zq*a r1XX4{RO->ERK)V՝F+i٭ -C9-.a&5nDK_y%{Yq*P)|C?)FQSPq ٓ8pa9׶QIH\|?:ɺU% 3M)I6ohgY|> axD:c3%^LjL9_kȩ^amsMWS؛$ H%H(zm`YDF(39 &67[CÉ-Vt *9nL VWUH,Ն E;̓zF= ߊDKMިh ԧj$ +BL]i?>Y-mypw *B<[n8K>3L&owzvcbWC;e7 e@!{#y2i6ެHıƑQ$Nm,%IK?/mQHZo{2۩nFu$ZikfO93kht_5܇ZctX.1Ta PzOv_xXswnח:esöƀEΌ1'_N0 ji3mPpz#eFhB퐱f]I@hTP𢾅ŬzS烝t (4dIM,WGsMG'^,VaR7F!c?saHլ%"CI-唽xr[(Ԁ0 tr3a1cؓTɩs7fRHAemcT%#V+go$g W7 #ì=M~O I](3ŧ>=әԖkP{mM_l AXCx(?{,d,VM恱wLehQ n]Ԗ6|5I1rF5 ~A^34wQjʚj<h8S9s%PAĞPY/tzÖ,ՑU %P/0)p~/AqK}-cdq>$`D zEaikv^f5t?CzI,1\V0fhL8FN ,JEvSfL(0=;N9>Cvp'Îu[DVܗpDN}ŷʻo20~68aF ]j%%f0cIPm]Hj&n"vyL%Y=Qϕkd/:V.ß-`0d4H?̐ʳp;Y9C-K0gUN2~n)z OORKhgl&5B^M_E#mƴX`"RwZf`*l=/h Gr[%, C+ rcTPZ&h*@ D5&oD'ZDNV Y=.$nZF4 M ̉Uu۵?Ρn5iQw˦2 => stream xmwcp&mly2mMl?Ll۶db۶I2q|nիݛDYLP`fcc01‘: -l <u @h`f011ÑD=-̝Tjֆ&65;k;W cs $e lZX *IyU$hh Pt10ZmS;Gſ091 @N\EXBA^ .ʨ" 05JMuv 46rWwtJֆ+o`ba 0Y17i[S;ǿ&.q/hkkyC J3:DM@[ m,=!la[ 2d$a4QpK_}?*쿥 mrrZʒJv&fe翌:[3() -L H7JD݋ @``gp3/.@[h tí-YV@N.3LhZ ^ݫ!^H73"OLF.=VlӻPõ1 sN__ Pd9q(Gd2(Dَ[]x֥DDV̚\ >סּ"ܐj?v`oڀؾ YsF8id 0ýqQrjx3UB;vFKUMS`chm!p3;Z+]qLz[ |EXR֘"X(_u.ĸVcu89'lU3Zi~l|5I(:`D{iDpu)wBU!;yx7}-[LwNS&x#;$3ghHZVa'{M,A8f)ӫ)w>RyR0an @ICMy`;D  G J0n.g4Qpg!Z';.(S NoIqPuG {.P2B}3?0WފBXD_F?٣!=Ғ|%3Y9f6_TaU}?:t 9%,q[[qF[ s uF&FvE^1mʗaq- Z$ݘf|_e$@"3xo°jc /Hotiy*9Yk޻l%'f(XLglGwДϓ:V?|ozo$԰ٚ\?{iZ߈ Օ>"nρI^Sg><,γϙft[pFuKY&=f5\sGVM/x;VA:VF?POwhtN/;-vKLZ.1ͥ}֙:mVnojսĹYZC%mGt{#g`;%&0vg ,/$\bx} TnY3t`w^XV20 "Q*תxLx ƙ{h*~ߢ/i(wV#ߦ"m׫Ҝ~Mk[8]T!U_]N֯iEW9{2zQy{{ N- h{ f<1O'6:1JtbngZy Vr?/ZCqVۡ^]cUre~|x&#I#9i5tJ$w+ixrtF+eA= Pp)K~L%XHٳzm5g^S d3pH:4=X' WdpmGPr@?`ŒQ~) PSXz "85 N FbGD* :AvH? աJ=w߈x{% y:P;Fr,$TuGS_U?CA^o;/Uoz~zhI˦<yp S ;wZjXa /PCwZE>1a9r*Hn/6hUj=1zLG[Z!t?j.T^%y9n) !OzWH?*̻t .E৖"ŧRAȏu #xt\#D )p:oFeLvHbߺ$Ml;*(חg[$|gS+'CEU172.ɈSWΘٺez #&KuZG0Y8ܶ+xim\]B 6ac^4XfҊKd,9"/Ƿkyp8b |V-ՃJ9O$h`!=(f: t]'w1Mg!7OPiō"t)?7jjtUt3Qޜ"aJwvMmF%i>ka|-{.Ǹ~Hy3U Wtvj6d}%% 9o{"~c~92~be5eG,@%TL= Dѽ=dBf䖇rڎ_ RrRE lA(%p/cc5&I\E-V3oie1.!cziʜ4іI1y+!z l1Pdi95[o#g0ۈ^qiqd!H2=kb ոs=| 9)&`*#J2q N+s9e( ~Kkj@* Aݻ76;X !}_O ݓ{b[# \?"c$f3vh/; AJxN9IM#T> pGbѵzm{^BVd;'%Nu/z`봍<ܜ!%녠uG ԚР}x!K4""sW&_ GTK| <|C&褒eOWypvu qNƛNlSfM_ oCr?Lkisߚ.!6#b$RZlma UL?O_yڗ')YָPZJo1UlDgϦV_:x;;qJ0J;ޅKu:ӏ~W0]:#ol?Qkטw#J 5 h~V*.{ \0yw9k pO4_|?r5f>wZriJh %5DˋUCR/G4u!EnMqP )L0k0UÞ@=sFplY^7&kx4VlZgad Wa@jK@2.}PxzI z/nI  D8F%Fh5f"%6_,W[-. tVSAK ^@R q >ƪ,`@9Ewl?Ԏg!X0 g΁Y>_8i![=; ñCeXx;Øy 6WK @:٪S&i.a2vR8c{+SDDbeWO.fzd-M߀in*>(b{UpA?ͷ\)K0h޵Xo1%8xS%乺x5͸˜g4W t|Ɲ`Leog]m*ْNmk}oE4lcÂ(LCktY%KO]t,j,pKTcQ[rp7=S+,;zH1cȪ. T]Oejexa3r6"3V4MQVsJvzdbX߄.h7qpn|9jtbSh9;άQޜh';tOܴǪmAB/ v!?)S案h-~ ^2M_ 5K06 F#Z&B^*D+,3! eax*< /2WRc.Rn&țjgP.5qk<ewYУiW?SIɮ~KDNiL]/5Դj x1UhׅOgsogJ/"֪ܥ'r Lok)Ť$yHWҲv~%sC'&3!9etFM m?`A-zOI*Ny\;mڧճ xB?μGo%=hj-" ²pgr&]GyЊ ZUu+߲4Tg/VnRyK\Yxv=Q))s?zYauL_tUӇH'8;}Tl@]S=y]0zVBhPt_#{1yb-[=),w?LF(_uUє',6 tD'(!,dhx@ T.`d(^o (W6[x^&_WˈHԥI 8D(# G^:Q ^bwo;wk{f ?̅ 8'gX~*1 @t#(:L˛Ѐt ^I:s1++Պ?WDA%p_^x5 ݛYVl3Dbn&uYg8o,ɖ>xL,-. ;dM n%?TO k:i_2dVQh%uF'|ynJ^?@XMA9;?jǯU 2EU+Z/\מ hbQcٺcDU^p9@Ի)"?*չ5 -˴ rrqDf7Zq|8loqT^ZwJիP\ch1m>Ԑ'fmԏS=]Z]AiE5- G cӜ{Fۄƌqjx٨>ݾdcZj-'> cjFƵ Lti+K`kKkS+  fx|?f RZ+(FrQ'zk{Ύ{sKMrޖP\KuMU Ah;fՋRAeAYO\*dƃEdMtFlW'b_e@85Oۛk3?H t>1\4ڵtB%lζܴ[D Q؍4UE7zVw(dNbܪ;  ߭,4ԞDkE4LlHʂ)SN9֟ 5aF0Ҵ|%Ʊcw 8E ',-՚6? tYI)EJ[(+-()9g4[QFܥ;&е5OC]#7{fY+6?@OНiGyᎽ b=䆒N,/AվvA.ݩk9Mñ $o.Ks&X1y }n6OՉd ggHCh|SQE3D,C;9/dwr'GŹJ/'`_k臶&-_F,(gJ,M԰h ^Y<d|_l.B82d] 4.yf<qWJ gXG'37wb^-BԞ? e&Ku<QgIrcltY^"AJ`Yv&٣7P7!! +io['gcC9蕿QJ/:y ҧ%YT&r3Oy¢2ux]XӼ_3 ؊&fcY!G+C;cQt?pĝWW-N\O`? PYBA wy>ȣan G*<,Bد'iI9ԈsF,޳.^'A(ܵ=9ŖZc].M8qqw\$`&oV=%@rD ֨A82$^:W޴8H+iQ sDgۭ/MY)4U!X#'фDZ>PR.:b^ i{N!ny?6E:s'K E|{H$,uk\XXPÔ$h3OVTĩrWT`Q~,1 zQY1r=խ bqKi+"ʣe@12a7jQya%ĔȒ4cyUt[ i%f#щ^-Q3@J1",xEG&R!^<' !4 ;C$4l|x|d$@YGƢiC67g< H>/pg[ɕ I(.+"zyVA҂pseL"5,+Xeo6Rбi0D|#EHI0, 6{x&<ӣR>NbԿFY?\axHy~t+;b_LP|Dz>[64Vsw=O!ZSLpv]zӷ\*6bra#:h zA|zh -4BaPoQDJ@*;N1G)wlxUIχ Ew5(hʒ^FbQFOtKѪ$JШ7&aE G1` -#!@9^G $Bj$q'MsaqNejN P cL5i%/l8#<7<5|]L߅d3U]]>Rav] O;isaKcS&g`ԏ.>X}JinjGCwIX]җE-N KlB.Z5E<񽌞x.^  $Ƹ1laq<LcxE+gH>f})c?74hC "A[ cM'NUĮ?B8TedU׌Έ5mc&~b%=ԁ?fsv+WlZ'#М QРʔߵtYg`/ 8S0\ը5*J;1>╠Sshezv6${ZQfkQb@!Mm8gr&)ςiPQF\IBeH2.!_QO ᙈ \1}bHaOt"tL:/X*$rDU]Zn34,=bݒhy V0^tZɎÎrڴ.s{=MdTuBnX|7Hи_PFOC$SZ/سSA[A^Di/\DTs<"Z W0.0Ex,L7Wf<$k Tv|Z՗`p' ġ7؅p?k/lN3]َRhQYv-7A 2L'L;;.\Ǖ^9+ƨOeǞ4m쨑;83cj <7VO0mGYǻk3Ƙ*C+#![: Jhkiڍw Ij6aF[e 5\b/!+`(`3T+VrR B<2RaY8^VQVBWTsQ"PMWgJ~cOi@y'BhUGsN"fż` ޚqT|[|}|;}ƉI?[@Dg޹f|5Lb#뒩PrfYsF[YNQF;0FqkY AF͡2!9tִQ'kXZoXmSCSN55}JumH,?(tVirW  .fTOBȴAfS{YhNnRaM0ʾ)ש/ذ+@l4G|7׸jToCCjRS v4yջT*B92RW=CErFɺ*l\6qDYd^ю&ȲObWw9+aHdˈ2Ӫ{Ʈ th\tI}WcUϫiܗT?w[J1:NiR_T ֲ%*0-Br$dcw?jg7lVNzH[^"۞1%׮R5 P)Q%9yv|qDA(eGH!kPH0 Mc){*e"uofIƕ9)u~X]e湲ͳ,}>,&}K*I!jjNWhJ *}7vbpk\WmOzQ$W[A/V$Li{x Rz0H;!:2`f n 4Ѭ{:DCi6# s) =[$t!_EC?<^翯F@x@7,+u I0IfG#6U #F¶^`<7 Z 'RT$`LC*ɝρҹơ, ySm)Da  IdNZ Ndlf 9FL@-FQowb~0 7屓"edcXu01*׼0f&JdUSdc8=:lvT'`"N= 9dӽKZfηAɧAWposQp?ɚ_ݪ6h4ڱCyѼ3u׭uFi겜X@J /:P{ ljo.l}>t5g2kh6"g,á"3ϾuEG%ם+M!Y՜* Uԟ&/ϥ֓'ZfN;[\*%մl/z@^QFs:"4'*Q$wV΅g#BǭHdK8[AWOh`Ȇq6mXrw;]2y+uڞSms,06-VE?81?JASc[<ҧ8T0Hޏ 8'~j=o1b_hV`AM[<> endstream endobj 61 0 obj << /Length1 721 /Length2 22494 /Length3 0 /Length 23085 /Filter /FlateDecode >> stream xlSp],ڶm۶k۶m۶m۶m_g̜87KUVV\^6) #-# #3)g.5c%{F&&F(Ra;{G 3sg #6vvF<N.t.|t4)8ZXkHȊPɪؚ8XȻZ[H[:P9X'!05&' u5qtG 2001?N\Mm5 "kEWutODH`laL`hbfa E/$lM P!%?lkkY a;{gG;cG[E; l,=y!lf8IXM ߸E?&ύZ{#˨JiJPo]535#PrKG.Xg{+DsgG w-ÿF:%$dEI@HF5rqt4uߋdbnbjglZ+R4_ J=?B6{h1lnw|;`Ȼ'Bdgs:5MVB\i[P{ic$ rq(O5͒{Q+PƋ(_9d+FCcrH]0oRxcvsKg^X`` gh)i%JuzyY\fA:CƵ4rLf}g `KโpeTɥLu[tgetH/jճGGG6 !FZT&[yPH}NaaM':Z= '&\V8N~0QU ]ZQWTN.,؍^b+f$`=9XY#/h& b7)m@ř)mD6qjQw2xf͛ 3qYFNW7t5)Lo “Ȱt.m;p:a=Sg4hljQ_ywO%i?WSO <> SO8Czv{VgKtrtk_Twdu As .(QoI7yQX!oK0@_hJ<eϋsצŨT#Wk1(E!D?"ivpVěr||HHcβ54JsD^,tS{ !RU"0x}2G#=ii" z` 2rnN;YI~ I^(QrY*Wqӟ$`!^-(㋱K\gCc#*X:BDT%94;~O i]>Pꍶ>CYS^N1,d!W%Nza:.){1p?v5!(QNGB}񢫖|Uqgo޺BdYAE8?xq5q=<7\aUVv;ϼjx ѳ_"lRe{~=AnX6"-9:iִq‡*#l٣R?8'h))tHVH=) T@epf @# IjrGMݤnB3EB[4[ΛwLBQ;8Ǣ.{rJO61JEVUk6:p~$^)P^+``bS o%4l.<Qz|;zXGagҺzڞfZV=BUÞ +d̈x#nngUo ~3y8zPB>7@t/lBB_z$EQ ,wj{^ Xiscͯ\֔Vcq_DFL##lݿ% 5N`BiûmS4`\l`1@TFК21#|jadʗNx2.}zAsxLd)N'm3T_uHu0$y~ ^עf<$bGte>ayzTLYlDyπ!M_8 O1wo1,!DteFFgS:I`ےdE` l#uWs]}Vվ1vn*K ŗsU:0OYqJ MO?E 7ͫzc/LV)Z(cg4x-oL!vj6*/C)2{)aIrSJ,*E׎AɁ Y(,c*.j̖\vBZP m2J{Nȍ0T㷄M泲z`@Fg>m*U6 ʞ(a4Ih=HTR${2c;AJw rYӯ#-C UO)O ]"]W $VCʼn="}W%荖3dN+O_ӢoWt-0f1^G-b͡Eg 7UM&\ -ҝ<}z7Xs~SQ+v}V_,p#ktN+qZVHұE;2 NP"wÞ6qOl'Ɯnf&>'\SJQ+HRJ8[aw2beJ$HˋE>oC&yB`{A%>kۖT8wHUuʎ B5d6S,$YFw&#p*$~?h2d&ɋ&.F%NmhV;x_ BbG SIDP>=$6h8cxbb<6XkF;sbe6fɀZvC&CSORD܀?`uPc2YN1Mc׌Q|4Et*'rV"{.F} ׀ Plv3<)A RLlV|󵣍RpfaeP~Bq+D;\]p}3bmK\.Y<%=ssl"'fBq,;]~}%"I nm}-)B8G"vQ%5zE6e8DM|eJ*gUCh I֡FL%4.oGk!Oퟻ "iQj~@VyJחYj$T;wu%tv{YjM8L aX73Ejc a@"~|}9n'dO A#klLdO.(lmRXEG@$=0^ؑ -4\*I@u)&Z:nhS֍"(Vڐ4ǘpTzSNPcicIK` Gutg?㮮 7%7 ªHnz&f%>Tf;xѯcdW&\15#LOk Fu;6RIjwKۨLj'meϽ() BVg͛0 &F5U( c8:UdHRVL6-';L-C]A2g16ܾ}CXZpi,+"^؇m`߻bf'zsJ 3b̢Rٳ~iv=#q}#puC:4)PEb&${' !d!;ʉWULA? $|୩ކ@ʒ4I/=Ɩ?M 7"-myw2$90@Q=cOz.trQN|@* bm7JkͬeGgrw1`ˌj/TS<_^^KWV؁+= GCÌ"¯oLH=u752pk-;8~*b2LԶW\csy '\N@o)F 譣م">at4ao|$(dP:91]%PP>~ExŎt.]WGnnj[nZkt#P@j3ؚ+W1\gA\CDŒV]C E a&ҪqLeC^,\Uj;!d.IВ1RV$'تy\:l.tCh;->6q_#au7;YOJuAs.5r};Yhw|?sQY4mL )CkX6Ӂ™4 _eWr;B~efPőx=+pzs{^龎tŸ$ɂM'@& c/7 1;Sb/EEN&*¿LtcUTu~T-Fq5վ~vBI/;t ]-kc`2Ղf'/,Q d1UP l2t@x<1d茸y7l| fۺ [S | #qJ`%Dǂ|°E>)[MEʺDIؤjDLA&QXȠ+tꩶmO џUq&A@b#%O懀w#^-ۇ|knVU >r#,cI٢30 ET+$jwDw'[ˢ™7A*V]r){>Ip=p +C IĻŨlWuH~j(S8ӏjo8Py@ZIs"1}a+'OьU4Ihv*7%BNi fMwp0L'yxpӃ2W' w@͋"#^+@K'|FSe`uhSꧯ{q<]ce֣!n;rTi?o-`0o}sڰ.)a۷G?EkO6p49 NFtkŨ;nq70ANqP/ˑF`!j1Mn ZR&403SW`$o{TKJX=S 30%[o`M3[M2ݜ \%ڏQG:\c ,#@ju'ҭ?{w:`qۥF0I>U d`V vWlbIY6Bm$V_6 OznUes<?cF`ijf S;! 7.r|yn>$9O~b!n7ߎlљ㗿bNހ6E"FR58U]ڹ ]2N`07iIe2`Sb6g&B e[nShh_oi3MZP+DiH`5Wb QKas=8 f/2{Y:CÂdz@ qiJف{yɮ{74alÂVLgFS(:B ti'-Ƀ̖; mkRXDj]gn}5 l /=DKg˃.up&)p%]x]~H@ȀHқFXXNJHppɿ&n+YaY.}أ-yF )1n˧NSGKw+9t.v'=J7:^벼<]˪$2*嵖7ja4C<)wIHvT=\"R5t|xe`+X+g`xsIz3WqO:~J*d J@ɶ ri38,~3۵b}X 2~/TFn5άTG Tq;X~Y*Xe,GzܐT]֚iЉ!Rw8jh8/hLA)EI}Z'hcM٥Bct 5EVی) \L>gI B?aQ-YN{u)Z!!%9< AbsuweG'T as^̮l*y\B%PҊm ^z"R&zT:?w-G,L|}'xbԙ{Ƿ֘E,f+U0#nbq0*͞ǥǐVۻ—<M]\̼5E~c>ȼ o5M|ӟȃ $BMgGbFX>RU(itA?65@>%}ԩ+焱%Rz(u 7m-֧xqEt/6ŃvWyjoFߺoL;r(0'uۨWBo{<> +pJ -_Z9MyLyq[1!`T‘O {~T*j3M`[ww'(BϢcWP=`1r> 靮2sRs ISՎ(@H|D;n8EHR>&2(o>'X'Գޛet~uj.I~g3']%̆ -$ӥ"všG*?Mv%٦:m]7J  aʞY:8.68bu{waZ3bM6#ɶҧ Q "%y&/qPA6x/RZVQd<|;lBr+HއZiBE*rr :tnXՉ![MY 6,zY*kUfynaqT?!8Z[츋9Joɼ(Iey>Mpn#U#W|%+|pߪo^<[%~tJ82 }'Z]xč (4 Z̬gS`L 0Q7wXR8|А,;Րч՝(o LFs}Em浮x\cnxm8i2Ƕ>TZ _?v*YOkc}h}+p'Ӝp=bY!ŵYB/X*'ܱRK1v/GBaO;ЉlUq=61 Mkn*#@?kd:tD'I@jCl#AEq ccm{%zϚqvWg%NcF;S t-}buIj؈cC)חD o,nk$Ft#h^JY UXF}A"[N^aez˅K#%XiQQNӱϔlےy#d ͜J18Ymblˣf3қDv -Ci_"?.`!bĹSqtq$?2 98 QM1HX "D0S\v"9+.Jb|! )Rt[cY oM~Pϖ%cv`Q$r)FiwMę*P!}FmlHl壔L.N!byr9%֥ JP8^ t~v 4xv#۔$ϢwE#t)X4f ^G*{WaI2T:68(M6S-[f+KTPl |N.b~k&IRO7T} 6\oe}J':q١[4|xrDPwЧ';ī(^geA7@~qkz夲 .:"9iG'Q4k˜b&9-X;ſ$I]1fqi -!-RTo$yXqJv =ڮ7 KizrR j.1335߭;n tY&UaIÖZ?',/1W -M\VM+"a@hLf:ՐV$YZKs9z~ `F#Sx7[UM%]Mz3f`71\ $94G=>ςiLw=PE/2 8+S|B2Pn08 pVf w\4^j+`bX Y<${P+D'ߑ4o\s ML6%W5h`zMn","Hh 9cOv^;-I-학=^|_XnȺe$Iic`F[̡,hW/ ܌}\JqQjslyrZ@u֪"*ۧg[\v$w|G̲t:^cz0i)_FH=|[S;#g*W1}ߣUxQOGBd` gGt<\Ojk{M!ń0Փn$[6Z6ᓬܖ{Ct zP1D `ȫN X!Y!TRda*iIFpI b-l\XlLu c3mGQ^ 2FQX pLP~oCi5~=fyB\f߮MyvƍNy~1((AR]/~B:˰~v~ :QJ M?u@#58GC;o,:Utfi _-a!\,ݍ>?ï dlc-$4Q6oؙã N (^EZc3-Y{W8ཿE7K½uBOv5sVFE﭅fd|dQ(;&4#n?F'ٜ1G, M >=0qe &xInyoZ$ubaT`;0sn ] M 6U!rс2"A8nOJRpmO;խv۫P&IY+0gğf/ɊRmW1@kO9IXV-*V 4QJC EZ 3ydʹ0"g?(13q>wqnal>iy%6?`';x(`y6}ܑ Ф\@?nt>jLo9--HycDz7okkaln1/DcsI#^?f]Dgyjۘ?pdڽ%dbȕ@HSFv-+K+E̐ dw|yn|Lk|3rK_%wA.kiƕ6sqSNVX񻶛DJ3RDdAA+!(At VO& MtLQ?M/.;Ugc+2 :/Is]ozо\lh5NPHQb[ET7pmv.Q1̅}#RoX7Ə(SrD>v30bP= tRuc;1} 2m10q\F9QK+_ e08K]U?pP \Y$klR+/qyH4˫(p _ 6wfDy4͏|1%Da{UUG"qbZD: cPG(6#3+ ֌Hy|8bq\tExPlaIv@lNA}Oߵ{9;b-ԫsEBΏR ,KE YVSяxnAtYӻ,RB9/R\;`m@la Q ٲ`;׸-o+_j ghNh]v8?_3{/ZD@\\=?MMn'!6 Ŕ8HyȤ\Qœ8RR;kW[ucx\?MH:m Пnކ镞w bˋPK+N]k!3đL$]z%4~-?{ZD[h¿7fKN8y3Yb&"r~Yc'yy7?|m85ZEv" UߣpB[JĽξqY+< =UA.Ttb/M;8Pt,[+ëorW-%g 5qWcM L!j6yN8֙#[YNa|b!Mj\l/X*ri/&\:)@P 7a"fzc͂6E(eռUeG*  % PND4್'Z!zNzBRj "ֿ(-~'z,E6[1Cl'_qBy~VfrmޫM;+;axOpgtHS oHY?t. G型؀rc.hV/~bo# ES΀)Ч T|Hk8b+]F.wo}L [է/_F9 O8b~B6gQ b:Qd>u30"?`Hj1T# ޸Vѝht1Onn,% dU·B3a!*}i-: ~WJd+)ɋ.;bEtFJ ˨cw NBPńuW*p.E~ l ]]agR@_,KTM.{*Z=` BBg á"Fh79wr9oչ_f)NP꒶6(=MB~se$E;zn9lO )鎁"g:Oh|ʹ\~J qm #rܛ^_GF eW˱OXsNb3nZ4+ca |h "h04=l%Osjpdi&N>M)`&Upx\I5 ָ+|'tI3gBCN_4iȷ?9𦆐 Pb&%!uЋ}XA(rQ߆i$5xŃJ\~ْTu~sx].sйG3vnnW??QYUh C:#K-wx(iꡳ8t9+yZOG qCҐ:{/۩b5LXD0oML$za1?ȶ_9)_d3nAKUrC<};P Ȭx -{4o}8碻{\D =`Av u<c\IKM=tpk HwzOS ;;e Ue0.:Bw?r$Bt[\ ˤyxJ鳤j4'u1j8\R-E2½"ݐ-/zaDuDs`koq^6$q@|4 5G4DLh=,s3R+eW@ "|;)Վ%eGbK%l- T';x͒mk!ڰ0Ymo,ReHGBes>RF)*-=$FaVtZ#)ŨI[ @2ȱYч9C4* ='c.rف@4Ɏ%tѼg=hEN{\6z75HӍCf $|P\l?5.7ӂ`&lgtqkkY^P So{+Fي۞ՏlzkזLj2vn>͂t6QXI%œaRY9FuQgD};*1ѐ,zݒ+"PsF4&:h|j0M4kz݉ aM&O@s(s٪ @׶$Qgܧ}9aXD njUexGR -HYG^ ;@^QnbOpXcd5sN~3Y&,N]֟xZ!eX`n(:0%Y6NЬ'ml`8 9R Jut$q?;ROyB"2KI ͸~)<zbڥ]dwLaA(h,D\YyMMnwS>Gu4YE;D.Ȕ/e%a<ʐ@Wx 5= lpzwA[,{8 JyY$~o0a\"pݐݒH.DBf̄a.̤M`N`.FV}_5O s7<dۦx c]Y Wq7=YL5SzJFQ d+oT\pK;W‡>C:ԲGXsJ~07CdYϔx 򰻜Wz?iWӴIL3E{HMc.,ǹe@ouv$Q*1Ntyyրiren}/O2K҉7"%߬LBqC8҅juDH8k"X:5z:$SQrf[XF*2OHpE4%Ew}UƘF>u qI@_<Ye 1H LcJcض3/z]B؛T RNE#Ig ꆦsrwYu*}, ̟R++"@]|Y,AFn4ݜ%Lb΍yǍq>gebQ5C%fS΀FcpzzE9ûɡX(/6/VJOo-\̶ͭC*x#_jGsu6,fL2N R=t"VSu06ddy{lp,Uh4Eَ(RQn;QA,`l?{x...hX/&Oҿ'΍QQexghمzok0X[Ej ."ۘ,|@ʝy+_G&eo HGf}/by5-X h$t1-Inţᇿۈ )"SRyy.J, yc5N8"?ԕFO,#w*ObV˜O1;,Ia{K \EsY;ܶoCkˀQ@n'j_d_s`1I,hlCwuMFN&,?Y>A?e:k[S:C2[ghzMwd`ǐSrw9zDWSSEHFLtDcz#7j>a?U:>HQW <#u1˦paF>~;ܫPPa[[I؛"o]oV tв6qkj@(O3_Ѐ@ӱE9}H5aĒ(XtY<_e[@BwŊr}x#p4iPPU 7]f–$h0D^& O[2jt9@ f,tum}n*пk"?c)j2øp>++=Kа;U %l}mXPhK*vpKme<q-F +; k=Hބ6;,1Uɣ$v2(oCX[[ ٰMQ[Wѫ Sh@iP:uvCc?H2bU@DK[Hd]h|"4kY8CaHo[hqXSP-Y+L_7<'Xbip\)+h,_U_gvqWQCu"'1#][oHĆ$yV(gUt L|Pdi}/']Bomm2JaDw@z r4hY6˿[Jc8?~(3ga6N_Tw&$R*Go3Z d^H5I7ܡ-Ɵx[Xq~#4.<|~po5Qj[I40BR ¦A& 3!x?!.iV.EnGm;i!sLcR"<^%jJQȺtKbM(hMa!5UBoQTࢦ~CL3phF ]^ $e U_B5zۗ?{fK6;shG@U e3Jd+yKZp)UTu[V[71Pp { q&vEC~0 =̧( I~U϶Y;_́ZSKE`ك_,òKspaוٍnn킱KBʐYM"4dwL0|kF\~[rE]q#eztV'8oV(\ Fjϒ1?\SÜJ4- 7g,H־4KEmf&ڀ2݆UZ=dpF /\~;*'rG68 EA: g73}h(gk2yQH8%;!{6<4g̅?n]I4ꯑ-rGQ;j5<6ڄǰsMKf((U\ڈ{Y9v:}}}39ZXir!FP2G+pXL*G5nI&7 $Q(oV4g+vVL_ph7cj!q~ |6SpJOFe؞4~=7X_0VSF)irwkT!뇫+"]kIʀyh.r7'kd_0J @%xommaȀHgļH?P4@St=::rZa;5ngcn$ 誷 i鯏?__b Y?A^*KY,Iص,cv-Bc0M,o7@< =A` u/'hڪ>UW:0CL>{hU b 7ynV_0`r\rʂO>($-~7ej=*ۊg\f/Q}Ng]tFVʲ\ze#f_mIr- I{>@xG/2Դȷ.ZpOH *c|>w Mwn7r-?JA-ߴd,wv.sM8 endstream endobj 63 0 obj << /Length1 737 /Length2 32520 /Length3 0 /Length 33113 /Filter /FlateDecode >> stream xl/5'0a1;c{K;s?:wX?`3/gdf׸0K=t,ܘX8,]cW''S;?2505Y_7 Jk -V4W N37cJ:sl9dats?`ķw+uBlg wr*(\w組wpk05l[cy;@E)֬Zi2>d h~pG; *] Lo89k#u=2l}Š0++mMy.IN߈Y3:.3; b0산Gޣܹ2.\k(0# ߝlvɄ!\6M/ A6MNdENCNۥ*Xށ a,oLg :[R($D,Y2cVU.|֐%/i*R#<*Fnji%eCI!!tlHK -D `~rB/)2V(mYuqhXʵVZx93v9\, lb/4&i#Kµz:%]+5ȶ2 ز|+ꬄuI6kǼBnC)_ЈV ίDA}/j_MvY)yye)B&ؿXR>>rDZ`w궚$͔3'0#JVGa]@a;&вoa=DlLMkN@?&к`Eyi enI!ex?T^*Q H0Sk7|(.0{X7s >D/^pa1S@1Y_5EvJ|+H͐QP0'~&9EEπ9H~VN`R_!Uc4ݳ|eX ׹ͮ3&1'3mBٙwG/oFBLb('}6_A n0H}_l D5o))>^tA*wkpW֖./ҩ~O@g@W[!CFKD*ZUdb7/fLSڴ\ruqHR)B/"4BEWdL &T䍣MO{ f=U9>w1?=tbk[W!Qr_?fdN v|!Hت\n8J Zy>GOԑu" $ (DCw"nė^15>|sFR~G٨{W*#d#R,<$f| -@N:q]mNwtV ڐ\WUpE ՘MLp`GK1WIqsBI*3 0OЍMal{lo˫+0*xDB(4ceC)_BOGLߡ~bQ3 fTi1V& )y8sx!19 / xT!ﶾz 9n-j;z]l x6ȼ#c㡍h*yMbWZb >U*?17)kMzh>nL$t)2"oL#zn:Fuk&XsN, $WxtPԸhsO'ȡcuOSom٨ls'9ԛ =P*VsqحwߕȠPTy6^Ef] Y/)k)RT/6VLKۓryc36ntuFI'I84xqfb.n<*{R7J7zff,j3W8o8(bKt !}7Aۃi[kdM :Oo_ gl_TAyYDLV1܊硯dZP˲C{IvO#sUV%߫ ~`%.˿]V}OA1 O !9'wC_|ߌ?9򏨠tjjbo 9Ǽ\Vm[,0߸HC)jJIM#bpOmW` ;3xEX,h|VC\M̕Ux2l?S9'4a5g;NZw2P7wJxRw֙bҾ)./G*(v| &ܚhk#y-Xdc `Fi(ypYyC;ΰ'o!x Bn*$+q?A JG~ŕ̛t-aԚqe*&}LyQ??NqnXS+LrDdT̾1ruԎ@C#)b!0C]P/]b]5)}L^deCP!=9j=Z\C]ƔvMȌSpe559AیMBl lSR-\.EcnP@w'(ܟy=ӅPPnQ@e bTmWmZ`%RO:T:$6wY;߫MI=mk~?/=)fjZ𷶖ܞDtENi(&4>c/~ )Ca.#?I^&*o`mBcLh4,fo]TM3c+փ>})uDiȖߣ}M᳸2fcb1ˎZW{8 A0` ;|*jÌ.7bx}{˱Cܵ 5=b<Q>JwI_ S54+qR-;; ? 6ޜ9 IXszb}BǾEOAaG:X7eY(Ҋ `OG"עa-ds"qwҧ|? >[J;Z^){SVon7@8fǏ=eؚ=[lh1t@#< -'GUSLIPxɉ#5&9iq =IF0*ِn>LOSi?sDfM~ghc/=Ps#*U}q?%l=55-2C|U'~F`QP~WwO9#%/om%׮n4Hg mt $D+ wMoTAc?%/4h֘@O 8QHzaAп{+8%jKOPKCc\wXDWFC>ok fxsz ,B[/8H˜2:#Q4h_yɜ(¼8UiU~&!8\e+s!G}=KB>\1PW)Xb8M\>A0$ :\[4xV p4 VٔK-3zyru?:Kㇻt㎥{Q﵅aQk '✀M$vC^~[5_3uKʺ53'kr? 0O[₯KdBh\ &9칠j.Ң,XICo(i[i҈+k0R ]pJJ 4㕌R"")Y0O+voZX 9F. U>HhU=sR>N&+~P},>P pAp2@$gf|bե,ULcI"&nQ`:m^ 7X,lM>Vw [?i !T#(NQy7 N~CG DƝ–b 5;_fy )r;8gDf^%tדop0"J^ߎ"57TƓ =(A[Y/PX$]:zߐZ>c Y|<-j.sJ&OgwI~tqD >w5,?8vn{ōXJE_W[g!uB0#z^\,^M؝&0̄Lgԑh<1gn*ꖃeBr =?z`ueaw0;m0_T]Em"[zޥ1u0 I3Z%I&k\ulrݠe>})Lͳ,MQ5 05wr\6-?z? 0'5&.ǐPJ|X!Jk>1>& B1~ HO8L=KB]A%RYXq!^oh} 1[6(2A]} IHѹ D ژ8̏:2U╴CHU`V,~6;IL ƶ3O[/oԝ3ïUUGxoRC^ݝ5;Ձ0a ioqGuJ63Fm-%~u EU.Լ ~`׊wc˯Imt2«+ie20_32fծ^Nmv%[_,3 lO*Ǿ4NKePDE򡉆v:ॼ^?"~8:iG^;¤kWGI=`ZGbm} Io);HNx/I>û"Q7"r^$,+^VGP-Z7dJ hո52@̐GߪWtJ OHe,Sg~ c:u=hU^֑bqEsO,oSwzmtT.jUrW_,nkdf}jt~Ot|>NʟMN*.3 o~ &תYw1 z*-~ C0j!tE"b\g^SVxAӻ Rť`&425sO܁#_+t^gY\QQ /1ڤȁq!NIt4"ȂũM~WD?{~7ķ+Z6GEj)Zz˥Vw8ff?_ŰWA5{#Sy4%L!VfG1cy?XW=&'J]h덱k>Gfk ͡3A8)tlftОœۅR%531?xcBVnY]6Di8ez?'Bݗq\Φ)%5BHRn]RS$Ne&yNnͺY<8[bq~PV݀+_&) @ ?Q#Q} ƲMo{a5j{cK7-`tXT:/IA Nno~IS'o[%#-hJ9\MvwE+lbaF&/\gn*e0ttc~dKM*".?,Iis'JAn euP2@l>Y " V94r 27ӾABb|"s64l?llWΝ$Qr"i 0e(6Z)`Gu_59ydh&2P+0ETP{czt2Mf=gNh`NPTfB$aVi@uWYX_ 2V4 r[!|T`EKonWPN< /rcTx0c\h<G7iU_pU#]}7"tŃB:n&c!_mj &_ۋ|16Mt_ a) q[}n!g,(+G Gw훟.0A2UNxyzl~$KjXXx~vRm&]Ai% ݨJmo&+USZ E)-=ϭG IQʩYDx`[dɂ ?n='\/襀9XSꩬ`FL jo!#7B[(Z RĎۭ!N.f7{{j+ę}1x3oN>%SK֗?9A =qߙJ{s* 9KGUk# 2a ^M6To-Yǣ)NHYc,$3" K}788){Ä*lubA"C.bU!"K^'ثR9p;~D#v+F2x)r:QyGǥQln90\̥%C/2^_Kj~ƭ+B αASCiZ`8@"}o>iOD hrkx>kv~z* iǦ;~{㺩5RXeυ;IƟeϫ` 1[wsb%dI0+W@;dDŽdKҠN^l2M֨?IuF |- ?^,H-z 铻2QP.$LF\̪EtM!JXL;hV I?z,,;˨SM;iIpRPSDC PʃS2˜-wHU/YŻc6eP`O `(\l[nY;6Da'/45ȏNo+϶O>d+e諀H)Kףd`ҩdS۷>,QsҦ>n!{fe@+VAfL{#dfC!tV{e"^o7Ct_ZWZBjc<2/ /_[Ek,NBOo1ޜ,R"2),+upEb;@xX|Jٛ)Â~.dF;'7.45ׁ-5IG_|stAbU>}ɂVڑWa}+XU+/~& \"_justBHpqn7`2@-+Jˠ{sĠx]H ?:96yTs3?N7/}p> Δ՝3 ,CwFj[̻?Iũ|d:)&ALm ec .Op f'ŞZl ҵ#lu :'iu6HñJԔJ3GR0$s2˞:繯t%SLOj׳\]8Wjx,J n&ZX(kaH9X^Eҋ#Ce9' Dc%rN 8=(L_QSl^U'ڟh)xhLFAG?.r| oB 3; [FewFj V|[[L>Ki1pzPvJ} ?iD9gkV;DlSMDyѦwu5i.j.іǐ߃(ɺ3LVcx MN0TLeL (e>+5>A͛aaswb`,29\o6Cap^%Xؔcfo_BTR$n {kM'ZߓrY&x({|2GcS'茕=ekj1<þݕeyjVR?.KU,MI Kp?窴^d'\EvC2b{|vuYOfP:M^R]STҪ0#oݔ=) H g ZMKj^Q@2C[B&`26`U ˴LJTЁ+L,p>cBe1O{z*ţ2ȗz eW"rx[i]z;CnrX[>\ʼPX,i"jDr)my%`']a2BeK|aӮ`{91\fg_\_by8WBJ܌@sڨjjϹ\.)4;Q3G9[*VsnqqbR6qiw/F]2drր mAz3eTP]넺ՖeW0!3\-Go׈*eA exw*c4JП+gu`'?˶@j/q1Ājkʬ-I;*' uM5"$Al]jL|'!:?{ JK>JODk'R "xXgbt_¢@b$fquJ]o8!cT3v2Lܲӷ} .#'>=w58am:d C4qlrS HlpMWP?Nv/Ȣr+ oow:7:nkY|u$bTǤyP["X8{T"hvuItper`6zkd=|1>"Iš#.RSbν?QVr"0 ӵ(KȊMˉZsQ{54 NqB.чoߨj:SƨMwi&f@u(AV_P`Mꇦ?`l M9_0`.󩌇_cFIC0)3dM ZfyKAm>ep ~{:LYa{``{:qR8 m>O`h6}FG~dݝcIA8#Y+ιL+]nCΟ.>vt[jBoӫLm~&lwN"IWBN; OtJ6;c,9|Nkg^?dbn.[5T x@\}9[?!cYj LjZUIz.D̝Ūم& V0xgG-=A0Buv`#|j&޺o UH !z 4K\/trAxoHb9tȀɒ6 F)W2V[kZos#ȻZDP)~~/lk[7 љ+gMSVvD7l>&yKa GQ^:2AMyO<4uUs}0֥iRq;jyKr]Q[66b Wd>#eF靚q͔274"&r=0Z؎ʛ h2VYH*ŋ "S$_ B:Re8DUH.4 X\ ,~e+YexN /?yޣ+g=3*yڄhv0~a UG%h$@4^u0|&? p9׀[8PTCrn.GZ[OT{;6v;m>c5b*l.]0# SlwaJ0y~;hH@_Vy`&FK.Kd鿉IxTּ?CzJS{~_ 2D@>*)/{Њ"Q3!;M`jn@fM-NSxh`rA+K7Z4).Q5s{A9r4K0<~LN13WRo@ODbP$Y0$tvOnΙ@֖L+]t281Ѡh)%06׌, ?(NXR9Mo3.ECBΫVwƍ 12n;l6 Tդ&s AznN|gFbl5;btLԽ@#6N'){u&%B[Gp!&^4=u7޽MvXǛT=Kw ')[` d![}I_n*#%~_ %LcE[dA^Зuch$yTSJGC4hWn³D+UfWB5| ŕi5܄nmRN5f0x=]~(! Ś4mvdSdQىȅK5X;~0ܶ kGu:U`*@tOe`$#jb`@WqSx)iJcm#]L}W:H-Y}lio$.)O^qoW|c3N Q>Ow?.y̆mA/jic〚@rGL?;p{m(y2,Qg#"[qL>ۮE&r .Dr  |p+7/5llG KNF/c,`R'nԹ| S stY7 Ȕa5NoU{%W5>2Q=!6bx8X_}?4- yt|U?o-<3ZD Qx/9Ty9pvyA7xeG*S0 $!Ԛ@Og1mX,zUqvף-I%3=Aސ̸u|DqqOͳ3y{פVs"^r%D.kݕ1~xmu R*!K^OW-:Tt|f$J˂ZY?f`f3CJZ&Kp챮qgHƨ>^V%%i<%8*S &~F W$qAX茁,TR k!8#k*sl^ܺ l |_`^X˃vdӔdr²V 3?<4P=vc & ]e(bX--cnҸ GmO P `|rVNuJw n+!iIZ~d槛ӹ9sAk::3wߦcP)u9[8o~z0 R%QvG[ eP"K **XDz3XSLY gnV~rgͼ=%N1Z[{I-U7Pr2P4\nM* z;a~5/upקWr!I3 >UUdiiI$t&:QZb,4l% K6X^N,S_lgF޸+ߊ IL1H2UcKWqU@0c!4-xh0 ~aL1Acnc~}>Fw8YEb Ft3ܾV8UӌkɆ0"lp90XlW]% &B%n-)3h G]pgG،+WbY0jvߟl'bSU=1!.u6-u@k[pơ\bLlB?0T!<{D'2M@ho(Es 2> ? _ ɯ@!(oDճki &᦭+DӨCH5q2 R'W]ֹgjt8iy#n%>7<+K~#6ҘZk}H3IneQ4T`=s~She~!Y}F ~t 5O"|saX_/J(2Ra\f#}YB,q,HR64U.'=e4F3-BVncu+nqpgqd3~.li3 G)/ %hhk! r @{#X5´o""+̐Bx#oW`yNq@AGy/wJ̕O)j\x5q֌PlBnY{A# # zM_4J\% ֧߃T]AhgWL@9lSGcJ)t)+i_J*(V폈1`5Ɏa`YS;,/m\aSTKW駇 ε~MuMHCkN=Ezyc#`ϔW&z9YDj@g'm?,j\ dUdr޲dUcKWqU@0c@sfu {~/T*8*$%CsׇH4NzRmIԷP:RnbM5*Ħ͗Q\!j ŝ 3Ɔ?P]r74  $DV&9wΛX5ԯ` *B^nBXW .#³~7QK]"O#ot@tBG}E߫D2+yc: 2j-^x lჼC/gC 808y& tK]LoU:-bُ#x_yCjq--E)8 /zsm~EtclPD*˘]̺VHw5Ly}njaOnTHH[S3x 8:5gg/}!r #A Rٯ d?̣ėw`(D|%f5f,כ_ x&I(bެ~N k<TV`KGzd&Nwк6(.4+&ܽ) w{GȽ\0Ȁ1? ѺΫ,9Q7Oe+POD&vY?I&(I@u.-+]8%Yu]3A៣E#hX/ \|ZO%s u#k C*V,0}@T~(nh6*(a)Ƕx¹zMH75?ݘJ<yg_BB!Uxҋ4kNKAA vHDE]:7&Vq<7^AIWb?dnQ0dt;7}K.P.fDY[cy8o=2ʸ|j ׹؍Fb[~GE} ti™XNn50 h6' 8^] aN+Spe)-ZKHh{ 2nh;~Oob4NJE͹noE;VXi#M",))8cA[D+ԑ\[E>]#&8L@bԃ֚"G~3Дt4Ŏʡ+t+Q /Ɵ?WR_=Y;p fee2_n;z XVȝ-~$2=08FMfA1c,痐ȅ|COt`5bes^ =Qrs5yA?hBԍ iQcᙵP"9gܦY$*$dKvoPv]f9`!߀ZrR4J~'ʴй28Qll rkyj>Ǧ%"Z!r:9SD)D s! !Yc| Yww"7HnEBh\h ΉJ;f„ 5P ˃E痎߹)MYNfbj$c|'(XX"]p\>\ʨYه# )o-c 7!/nn1] H:ꬺ>i~i&Xj\bE_C,޺"c4ܙF1@)Doms>ke sSӧ@͸bW>zCaʨ-Y4u=Z&ϰp]F&eUPsq6!m !(H6V_˙\7?Y秈9?Eсg_ږ!ICMY%y˜|+в=hk8BU4{xvR 7G13~FPW#Le΃wS.oU-I֘>mA J:(2=:O[{\2"؟ٴBlJ{`E1Ua<-=( P~*fQJ,xqӡV7lM(Xw~ENl:([xؑ'Zmvn1u8O'SK'=Й2Y-BxirB{<7-~c=t/1x)>]^4V}#Km-{#9{#=^#Z sR4-|ffn58KTN)zJTE‰l3yK7ydꐬqή#pUWHM ,  E(Cc<:ceYt92M#] U>0NqOQ%О7WX: RAU?_q,<ˇWs0;V\3@7C)z*4#"jd8FM#$)lCB;j;tNH!HeEE`Q)MWCm 6Zmk%CvLXľFIibLG4)o* ߶Xvuұ)0.N`.* _Ц{,NOSiJ;[dϺOI+[hzHT?6)8R;u+XY2$TK99G5* >X.2EO[oxx){ߘg=hDBbLl;T3(EQh_3aFm ËVז!XG':ת12 $*+ᩢJ@Srg9[U` R7*WBRט8lj\t3$3,K $;i:gUS)_4>j J_Qo@ۭd?FLس|誶$^S lbRsZeG1t%E&x|&@\**3\[ԝgfH& Wӫ<}7 uY?)# ,}#"y{:j Zn5!8(mW 1 YAG8 <%s!>O#.`.{B0\ >+]j Xb-rx _aA$m=Oi$C '(9cTl(n4#zllϨ2ofۥތLLaT+::hƚ{EHiE 97Cmp Qs~$zLm̃K㞲;[8k)?_ō-ߚS5_zs-|>@:bdb}?,ApQ͸|ʛ4/)pѤ޻}(B~#߆x8V?$6?+:GN2 k h7ņ;l,U@儫kug )עs&4ڱc pD6K/AK,&pȶeto}ә&MW \M,`H?!bVxns5WNkv&V?7#10օf*ca`p(ےwbj?CrmLHB9x0s|i; #Sw {[;e!>,}-gA{<욧a+Q2()hWxu?AK #N/@7 {w0dcnLGBc2IZU7}?%mۚg̤dKqy\#}g>cP dj7/J$&.-F DP=lU|NG'دUiD[iOUF?ns‡luTf,.^\/em-}zH9Jjg$J L-#h J6!;JWPQ.JKlV|Tⓤ']9ͅq`77ZYApՖk]~"FXnB?d}Q7 ,Ɣ|~L '<ܠ]^ dsҧ=zvb4KoU2sG$)7͐/ru'1TKѡG3'`9^ne|'CQjSG3R?0n,JC!NkK hzw~_z$CM23VˮY^#—dhnY{RJC~qX/t>KV1r- 76&JI+%kr(6eݫ?贈,ۧd ua nZJ/P''\6c1A~aم9gl- ]*1!-r@*_tѫq&-}c $R"B`8lcA6}RI! }^QI[#{-]"_W@YnH?t԰wvh`֘ŷq$J6r^.nx($l,M01m)Bjm52 Ąa7{4J5'{tz; c!F;o;~*٦R9;Poڑ.t,yi8tjVa1z?Ө+ҖHz,Wdr-~!h(%W7P#s.NrLMTɯ L[aCg3Hv;t/Eצcd>0LKá+:k#=2$C4 ءLo akmA,W6"i?\]Z9? uAJį?J ^y1E9eʪoro2U8A3Ep@c~ pggQC@&QhOx|eE7K܌\pNʂ$a i'-38'%& Qr:-j~טgknׯ>|Zψ'3o?uDdR8i}Ȑ\ZY8jEu$EK{8H ip;.Q%L].V>Q۩o"x2ϼn(mQK`A6r$ƍr&tq L)LP]iUÔ_V(AИ9g$1I[2˔UNNR\J3ilU^iI׊Ʋj3YD_m6꩘L6y#LmcD|=2/ h^:w(b 1|C,DbFmY}pZhI񿔺Ng17& $c63;@P(\:lWB?#H*g= / (æmbReacqfԎ Zx-Ub=%+Ā{1c֐a<łV .~| =ZBT0l:>+ !ڥ|l>rfHB-1XztdI_p4z:O+Wh("t(O`9rMcK"h?L0NKi(!?BW""~gInIK2OVmrleMܭ˲t~  6[Do?ZVZIdTʠ821!<ġaA-v&|C/ucѯ9QݫqWo<M[^:.]Eτ$NrYik(y;DLU@ ؉ j  /4㈾H@n`OBr6V} i}h@ts't;B%q_'0S/y-1ATy6L5A[jHrLDSrOw/ lD)4XsMnSM `#hůTʖ06bJٓLʉxISܾ߶ K*517ԍEὦoxбXg~Ot3 VcN1#VߔDWOBJzzP\t?WlLjlTGE5@!]9m]L' !u`HKl>41b/N?!~7'$=6 Vx4F|Xէl aީcbwYW^%iI^Ft!A>D%[_%&P飁kI ¥a%9[_h*/с:[>΀ 8Y!v[eTa[f/a } d}V7Ddˍ$eSj W];w/w @ )þNLs,@y[*<~ು_%bTԮYFrXW/ʨyU >5c( O*g!af? O޹~`0x!nYv Oώnu,MO5лqiKNH\_s0@:h l6|L F^o1ƀ^ZZV"`4itKAli-VEQ P9E0[ zpԘaqq~o*!6:;lX+[H\+SN~QXuEk,`R,Bɯ0[9a"11z^/мs seS=M[SNP R'ƌypD;~>"a^nZq6 pRmoL*}"i X>C ~$L4[?oaYj=Mv#k B`gnrY#*.A8˔]b V&~e a\ ? -U:@#qf={<4̏']h&JPV$,hrb3%gQz-ƕX娐ɣm_֠RY,U$vot<0֎֡QZ,>KU)>)v:3^B?:! RAZg* +]j#FngRllRFhu&~h ϾyOz{*]' C݊eG3j5S@ӏa6 e8ĜNG:cFo-1 }UGAr¯?/$9YԁrPjݭfR,GP;o>"rPUY8|ۃNv::|94ZwH`KV^U}GjbXV|kZ;(|=bX04xڛT(Aeb.:--gBi p>꼡~IH5ԏFw0;)y WNr8۶uf fBdYdmb#94}N$^B)wȨ`Z6~{&vTagUt9uYͮl$HS^0A9u{6'0_Js5ݥ]"##AY 3l=?F]v`K> 3XwX CGTx(GW*+&C5 V,w0`хc;c>*0رaYf+O\ki2컛w_ݙl9Eޠ܉ARvx|؛뼧M|_iS"pB4#ώqHQR:v$m\4 tI1|v{A/a[Z#i&"eӓ=9(W|zkLWB^I8M\/ĞhU#a7]߼Fh }"op 5 f1 ([v;%nݗ]CP]!)Ňf"RCH󿥩$hm E [ Y8=WV+e^=GM Y QrG8tzﭒ1u.#CEv֍9s)ybjD=_l-%YkqdSj<1u[q("b>dmͱ-+@^<{e‚l#6=qVY;TOqhRi/b0ta Yl+՜;3 UbpJOO[jK1$z׉Dff5vIF1 F(Q [t1e "ܐu^7 w>f4w7*cQ]{a5(9Pu ĭIw¤B]6)i, ~OLr.,8 QF)3|?ܘ^3֧_ړ};bQRA-s(!=zfB7Q/KAO`xUh *r~ӽv>}RLIBBL$hmk"cODꥳ${B}ʻ3qj*ɺӏ}8Iqiƭr 0uxjW-,@..=3{/{aAJE)]hiYD1H'^ ȫ@A2DՑÙwJm}-Y .<L< *+Ħ̗b =7TUnx< *'vYk42ңԊMJv) z~QRY?Lkkz`1Z+9Re ٔ0Nփgh@' oe| 'a SJ> ?3]7х47įGڀ0S3b] *xWMGq6_vJlb1<4xb8>DiYE^cܕ4ʶ~˯G'+N3@KyFF\FX  %cjզJF  %f0Om~a\(=/!2;Y^3;(:+&2H8gY::*_2ڠt1@5| DQW\h.a+o7(NfD h$IoHYl~r mc5$y_{J#]QG箬KDqz0!'68 aa܁]= #-tOcVE*;c ruJ䯵'ꭲWJQc Ӓ׿d^ PPq+i[Ԙ=$M*LS0F!Tm 4,ɪี@SR}=@3Ѓ؞{Bg"w#Ay_"7tp2,ZTu1^2kcEPwa*O&Kg%4&Ws풆7쁽,gQA+d'PZvȗtS,c)QW_ O#%ژj" W;~],3|h84ɹMf(GgPJ[@ (fRT*t/xZsD|Nt$^DdoM25:ڥQ$`Ik>SkӢBdLA.ZѭF~rzl1IS5P&^?}Z@DoE8Ԅjx_.:jXr)i8/~A֘qPXp>YUNȋ+_"|3uN_jH7Ξh6)`!x-Jp4,pEZyZ ?#lȽvP+b}\'P]A>ZG,$ Cj{+ll NGtJsk]u ~V#H> YhuQU j6tɑ(Zu]Q$-޸|єc;}䁬6j-0r36sC[쑁ls[9Șa7):5ǔLlN2YH6Pѻ?`\vSJK\k~%(wE_dY5)%`^KrXm5Gafܒw elK+h[Ɂ<{3vZq-~nHOӞyъ%yU=)ʀ.ְxʛVɫyo1xo;8D䅈*awߧ8qOGYh5E3]:\P9$  {~'?9وV"hTp8&%i5VZ}7́i{p]mxa=q=\)\ص=PdCbXbTҾ*0f7ĿyGHms6e h?W?VLJue l%\o8M{9"(褤K8Ä~1|ȕyPF[~"Y9{`oqLsՇvxt)|`?w{"sJt>6(~g$λx1SdX:ƀBoO6$ir }bUMŭ"4A`2 q~C~ $+*QLfFm endstream endobj 65 0 obj << /Length1 726 /Length2 22702 /Length3 0 /Length 23230 /Filter /FlateDecode >> stream xlctf.vضm۶ٱAضmV|ϻyo?WUycU(H$Խ, ,̼5Iufff3# A ԀNV+33 @ @mF/#@hamfpugtqdgp,1%eE)@ t1(Ỳ@ breW@pq(HH*)$Ę&y;YOvHv&L#o`nm0ZZ;1o2ݝO@C9=@-ht(8]2n&d0{k;_A.`u ,[JZ{͕!e{w+쫹Yd*_n 3GskK?/ku7k/3 <+_/ach]\nbTWW5^@3G30JHc [=T!+skuO`S]fRD TI k Ye@J[} &"m[z2{NEsKqyG?$Ώ')*lݿ(sVE7B`{FVENG::lP'L71|9BM>;aK&k gnC#/G|H+Ոo;Ow>S/B+ :ɡ$I j7ZWc,ŕH8b_?doM4x{'FI  i"z-ב$1oWvST b`"T6Cגfk~S"? %Gmn x|BE8%*ƣ?$GJfE1v1rA=Bܲcsc0KMeMÄ$AoU_Zɶ~xx2)eDn1_4اqnf=]cY4 ybs29ᨛnNA]Ip W/+`5?| Z ֻm8MUQ uW>QbOq*T+:z0 F#I'2Q*\W֝XJ3emt5U-@1ECtlCK_ _1Uvt̟֙P=[gjsW'W|sm,ZUˇFez̏qK3rrR%*v;n"١Xl xFC&*sLDWƘP.f5z"L]E(P0XѸ~B'xT^ʼnUL~r4ZxSxu6穲EU孯bIޕ7?W028qP' ~]8{QqPàÌcޙ>A{d& +.4ri,_,Ю&w%1w0qX E_LFa_Br9! swrغe0'4chIsU9S/{K.1OU$GI@Ly*9Q.YJ "`1GzbZy/JKX^ug!ʦF\<|Bt54TftFlPWtip!7fY9Yk{ozJU*'Xԫj,^҂=1 < M17$B j#P@wS9 )Zc z#)?Qoϋaƴ/vӗ[ZClh'mjrhR/ *,JVǶ6{4,25d%Umr4i14 ?{ Jci8}YB O!@͹{(Ew}]Cc.'l0jU}B @Q'9t-{㯄w%& "qD%~X=]ty# W%;OZ0ђʼO8͂=;.NV}Ju eӮ3GGjn͠bZ-eֈS-_ˣB ?k9{={9 Fc9[YXe')6yڼo$;r#I]"RNl>=cAdyyw0,he/m_vCc҄,Fmh,WL@ZIЗTw^'zLSD}oUeqgƤ]a/ecUbQ5#,;Ig "Vs; :dC8򶼏QwvaGJl3'/fzo1 g>D'O˽f< d:|Cm ;~x|! -ԓiB23Ώw'tvO, 4N,arJZS PM \[6hv3pmN'|fD I?L oG.[U֕>bdN~S˟:,^<`kÂK*=M]FxCvfޤt<HqI~Ŀ6s>F:r\r?iB#I95zNbbWoe(f$h{D#R-:\SI(GAXP[`~Y.;mo7!_!=#E$D<yuklmb{DL#|!LkT򭦆T0|'Q-13y{QƊmPSĎ[.{쑓$UA[nb8#39Fϑ -3W n% b*tKRg%--,)=l"R(_ß  5C<9CaI9!lWJI{3LV\܇Y>-x"W䪊Qf" 89m,fm'!RQk^ x Q U陋%?<5 0R{=񫼜Ϧ9q=\?L,̽Hb?]>5lnO7srRB,t{kڊ&'Pb]JaqɾZKWEA 0mFL#̞ztrVE+QKat8 wmUc-'$N4@->!^D&YZe.B;5Tכ{Wr N4l_TwW^("l#φ<wC#41}WKoA[|ߞhes IN-2L*Md$h^f+ܽGl})Wdق$~!KWh g1/>n6ʠ곖?&YSI5V΅ghȍc%jpz ߧOo<̜e.Yrb o!JT:JR7|15d?Dp]ҭSu*yJ O<)KC`A +Kc8b&@Ѕ͊dWm(&\2$1Kڌ68HɆ+ݡC3 ޽j+L,ӣcc'J/{A7%`ҍFOS˱V]TkwU|? ;ӏBudc7z׋1n]X4|,nU`>s}cܔ'ASCSwK"e? AYE#芽P)tæƲh '[AMHh~A$j-Ksuٿv~;dQ-y]@uEĞj۪=uC_ Nh,ywE=!O\uml֕DZ^9ϺU!_ȴxHZؔ8m;AL(2]sFw˽'Ce>+һUbac/2YG'fԟq=S@:g5NٺVLiԶ7#حB9,7b ?mCR֭ w۔.lCa7OBo y\讨 8!?DfW7!޲9W`FEg܇WʄQEz.2Yh3_<i>G#oNA%)Ǜ4Ҫw&7 RƢ(5q9ɿ^3S' (w(DDe$-)=wA=f6v32Zǝ[CaS~n[7]nmlmi&TTc iM.ߓXC o&NpnŹF-ބW_{[sfs&dqbss&'6|('%U9:<^ngD"F&^p&<-|< SPpV#Xƃ:Ȉ[mI0thJ.|quGS.5}#Z{됟jweA> !tuS?CqXAWd.;\ρm8ݙ_Hת7cgr yTt8n`  cӤi !d>j$m\-'T[ڛo+ $nJGCY:x(8@c};tOt}QV x#?yɰyMJ fGŠ~Wǽaɍ@\/* Y2`e]b&X8EA6t ]ʲyX@-7ldW#,JA'a2oi}0p9Ê/0%3Wftsسq[1xLR <9$bLN!jl71rv+8\_0|*v qe0y}+fq|Jk蚣jZj疺V7<(so)ģMܧ[̴5&`bkJ,F8hǑNlixcWS,ڷv3g;d0m[ʱRޣkj +f3brnV~bFyjm9#jYS[ o^PiSsPAWm]XfړN6r0ppqON>^/KIYl#K ^КN')O癮I/~UǓ@ޫ0գ@뚴$-결B毷>z>.=tʲ*)^k]R! t6ؽNݜi3K@g,QTJ;53pXwe\ ϕ]Q +ڠJ|&7}rYǙuPw1~?~I -RiB\[1NPi7RvzMJ-C!QWcm1olbF=&U}LɼwPhk/Z6e~zN"h@UkZBəר޳t]uu[4̔XC#wc`sSrZqVgws4zUg0~ :'|_Ffhf-=Gpkung5PmǒDЮqW&6VXHLA)N2M{Y\Mx{yJPHB[[0gA @IdY*:%2G`Kji.-LtVP&`>)T̛jZ@嫣FąDړk&2)77|` Ae~7جBNE9:u" +CtW-zsnU-t:Pq'//.D.Ύʧ orKJ݈!V֋=Өz睓GynK<硥p?R.[oٍ}7=s)L/"^ukٶn5BEH@ePc@M^% 2j$4/>fPye&SAi1 ׺_b]CL 4gmqm1֦ 2p՛$ʇKIYG=-{ʓVhY0/"~-XZ,Tb0 s!n/>,#N> >QR_}TlS! WzT|Z;K[?ຘjNKa\]k4N95Țm2(YEI+ ǖ,t7EHJ}a3v`Ui0uu}&|zK 1R\!-WЊz;Z\v(`E r]|uwh..1%)ybԒ,°; ]OCgNA2ƵjS|}YYʑĀAC89`Z>؝yϩKm&6Fqc[ c 9pˠPg 98a[H̉(ZI=8Z,r[xU&w5a(`5baJSD I |_Tf|XLS!$s9D&[ܗ.Xo= EU,eK& nҼ ⠌J@C@nbw4w@9|SɣjB?&T+Dor )TbgY NIDpɐeGR/8WUPɊ&Wwku!7btTHM 1l2C? QN6"51foV=;~t[LO2Z&K˶lM "E{͌P3})R<LhHϮUx j9>nG{=䫤G6=XG`;L,{75?]zFw'QƁɞ_Nl.xeN[uzZHK+k7B–H68Ua\s @_*}*T?LNV.ΥLixtƸc1y{Kcl)JU| VJ]dn/Ō 9}%Ll-z`QOS{mJދWt ]v!.{fy,zT!g(_RTma%-Jp=$8%ç1t /3mA6i@0hz,t:jR][qVk~]$J@g7XJܦ)Er*hKfa k6%2b;I̚t9V .7-tbNK׍5x*Kw|!5bo ?ɤ7=nj5zCx.;&c6HunN1gշۚA +6ULSI8I4~LyY~87#WoȺ/Wf98h W!+VBcAߦ\E U]4t|]).)tzix (<3^0by%c ? Z@ c׀ހs'aOQums1px~cb.KK $z?BL!;bڗxa\+dŤE:~udSL #|~gq/PvR8­r:eg8r E2}c'«`Nf0gbjW5u&Ww*'åE;MouE>5hʮEb,>{S?BM0Y͢F{a.IK{OΗ|*EDż.6)Rp].A u-0c5?:](zXd33Lא˔,l ?'ٗˠ )V;6 [k}=E#sC d tьk1i޵A(EWԥ6ɲ{ ^s$DLwh#E,2G+^D3V39T'j zɠ^; Kw;liSonﯔ8Ԣp(JYg-hYd~q1.Ч?[dX`{8DLR ?ʦz>6 4a#:,F }Sh95$^p[@wK}Ec"GE L=L:EIr6)84erP;x6<pe Xfsl2}ެoђ۷M/;&OD);smqb2չnEe 4Yɖ\TcA<9L47gE2 x/Pq< qgX[Xoa}A7~8` K t\%F<Le%\Ȱ0Tpu`bÅEG0 ot49SQH%W*E:g ?D{r .F선 +KǰFTsoT&]'!J=Vo5G jHE<\\Sz[KR)Qz!'>}CIޮ_3H щ 2rNrT.., $^[״w usXb￷ ;If ֩+Ct338)~K`%-SQIVh>-jaN1O1Ya>w.>`tBA˴74HRop HGi7Ɣpi$V:,)4Vݽ;M:Sy⪆֬GѩUP" w͂Ll5D&oaxO _4,Md X҅=3՟ , QM{[dH]HD"%ó ކ鈫;L󏗷Ǩ%F.qH2F`ͤώMj @&(X eӉ{ Z#Ŏq*P|:Y7tW10g`5Z5zRXI7 ֙!b;{ b8#5%^9%W,LDi<"fIA{_5m>溋 E2^QoAnQ-CWl Rjyu)jpN-*s02 ,jWGkN/ d9e)ZܯHOp p\)vj{K?xɦŦM m%i)TMxH_Ż6?ON׭J#VTĂPK}X"{%tމ{ ֶ=yA,Ē)BxYn(;Aܛd;IN{V b}~BXGğ ɒxYLl[;$8r$M Yϖb쇻Zg\*XgT5xڄyejto;}u!JIא6/s`s{v;XQ:l椝?%Q~<wNNCXgc}N*/d,Jb_xZL|H .eP=QwEzjNw\j'"Qv(Ž+˸β%hQLoܾ8Y Z[ea jy;5ȿ8ESxՠ!t? f) `ͦ@w'&="W&EE|JTN};=jc)e=N-E70šl 5.s\peZNS=D@erEΓdK#"=, $sdo/ ~T!pVsTZ5Nt/ gW,>}5hz:&|.QUOLXj@kY)@Ԉ֊dzL˜MQ %jױ@O(s(JLNi|PW9 Yn*'[^ɖs3Jht*2ۻLZCq| s[ ³gWy# $*~s4 ($&y_ʤK4ӻ=fܪװLM @ޒ®2W{k\P@{7+sQVOV%YQ-s$?[N:Lj92ʕ}z^[p锋P%mO/CQ% HR!=4t0}U><:aiP<+khyE5p~ JNJqHS֜YLGQbC>XDž>+'Y\cvTʤ* 7+B\%9m&~\̬Ȳަu6>4o>% IE ]ĭckIMׇpr̠QLҵrN \ٳFь ]4+52w3A_VcN4 ye}-orIWU+P$`yS4Aobx71"\I~fV] IwT%,-^Ʌ%7j;)%d<:7)Zt _͑`/gUfoTK\Xt ̊*Ad^ ٚċG-;@eK3j|% An̺?ژO/`'o/Ulx谓DC7c rPhPxu-7UZE=-$J 䍴I|YarcY+`c r#XW!% dxTc+vܣ_}݆(X4@lHɕYukjFl)Y @2n@L:wБH{.iU U`2vL.tܚ ΎM2h8y×6~l,Hrϙӕk NA{AeW ;_"qUCvX)l Ɯ>ɸ'K Ձ3o,:V"G.MPi(Nqۑ'Qv #([3DΣҘG)xi KE)o\uȔy[.H|wU8.^CgqYΥ"[ߧy~p[.+ޢȎ?;akʂnUu6w5+H4Sm8*+U9_j$2]Q2+v=ʘ0B$LMN碽5Z2ozcEl[80:S9kGZQaF{'ܜ D2NzZHW ?9E-רt"Iѓ<T>u&X?wNDKa].-y)گ>P[Tג|ZH1e+<.T!d͕֞eۀ DSC%NM u^ /BHr])ΚgK9^,:)1M+.4RqwZUT@2g( h_iϊËm2_K}l|[.g'CR"= ]ZNXshS+1#} ߹_hڒu#wXz:]iR)F(kvLCPښE=X(ŭL+=sNJ&/{RRxi$Rtԅݒڹ YƘY&n{|.׋AsZ/̫P^b\R#bm2_ yYqӔj~L(݁VHg[}523fd$Dh9 eƄnvn\HO+TcYs:[ Fx[bL6$ђHVqhq佮k8[]$$i-q3L<9$J o2(d:M)1'wJQ&( uan9(pمQ0%1WMҪa“XIHGAS=Lܦ/2U^!vREПBItfQ-'ʊRd ښ{c+I>\jcձH}6o]uzg+l4ܐG ^T::#Uc5HH]uqpSD]@=t< jZym@Kne9nﮢWza`g <d;$ L8i*!童DE5XX7Ə#z1X 23iD5)$K)3Re@d6Ap"=/:bq{;%5@8*0&-kG)6t=&y_;-t>7 Ar ;\d>yښ|3J n_:it){J*!!)Zv#ߦU/BDFٶD;nɓz2a]3/㰣} KwG,Bb͵HN\6E@.ӈvZ)|ˢ DL:C~?bjAe=^ND>ƐJ񂑐#2L%zA1;#,0X tnV @rep `/Z$ѝA =5qJ#r%3e!PFR&%y_;b#n<FXs5E,K=M*A"n24F䐿/xCT4~b C4W?<Q}?2C[we[ּРRonؑGk. \|H$1rΩFZݗV6|pQ$ܾ鮁[-,42=:yZjA=qksaY 2?qnjB׆'uj)WGaE)³!X1#C &M(5J#=d".MPOTC|7UqL؏Øtҩ^k_}ƀVZHwv}Y&/$UE9lbw7t.//дb ,,`gNeudY*N< yNjC3=FsTBZsF$Nd8*GCɺd^`'͠.E`&[\I_1|z^Z\8I f>$]fYijCHop.V0h?PzD 8D>9,^bc^:pt!@;l-rpCBچLؗW8Vȯ %Y(`DFVylӿGL-!pK%}R{Jo(u$cRTP|yn[-L(FO^Oel!7gh5-8F+уo!y̔/5Nhx"'m+t-NxvE 6t T24Зy |}&ll뷾=_IgYBdmXĥ"[+" U,WNB'I F?p}aShG`l^=lo,gcB,epF+[pM͵RCmb RCxsDuL "V׳ (H!w(1;:A{+J؇{Wk*f>VD*HIzd]#eWjzNX;J3(3EUIUw,v#/bBT2a2aCE` =Bkt7aKޜxAqO E,Y~-8.'#/ZbR),`8(DLڿ&t@p81XpaK! ;УVC2 ?K_9Lӂk8N Xv 2 oN;Ps,ϰs{'c|M,'.L" @_pv}PC"*J =_wqiR|{p?cHP{WWAΆ2Q4 2ɏɋ%&מ>g# few%JX rwc42w)Sn|1ڒ)͓55$ .KN<7BS[ ]Yf s3DT?P ݤyM1 {Qh6f)^鈋1+fly&a HݟAYP\[(EPe;.gc@[lxixChם/כ |뜙rN ~r ~ endstream endobj 67 0 obj << /Length1 725 /Length2 31196 /Length3 0 /Length 31769 /Filter /FlateDecode >> stream xlsp.dbxb[۶m'ỎmMlv&1mgޯYP}իɉ]T=͘*⪪LLL&VXrr7@b`f011ÒD=V.*+FVvV@%MٕO `nekQPԒPIȫ$̀FEWc[+5``oj/L `ft9 '*$ aTٛd% wq_dk?HczGbefZ,aś?jSW>?RLv73P89rf@{wZr1 !{cYT]fQk'7rfVv 7j`ogg)QYXY[\f1{S+{ ?|MGo*X[Q73rZytU"L+?/aaozzV&3+''O4w7 ڲ Or? iC5efЕk?wy3|;Rw$FqfGz˸/Ť~5oJW8v&.k4l9g5N|(цe[ z_f{ȳ!W/k`ayA;8a":Zs]X:88[n6.Yw!6܅t!# y6n?d*y"6*:}#vfܞ54t)"])H`9Gvse\d zs[@!(:M0$jL9n +A5?(a47^V$fM: 䁈qp"P >R ql}/ٙ}/RbTĊ5)v[]@(icLռܗ~E\<5`!@o'Ei^ş$UU=^A #!}7ۯI!s%S&[#rB0Yp.S=kwt Zn<9a: yPw(EO}rxYC|Oɓ8.اb3ER^{Yq DSeS0\ G5nCs4HD{*4DCs-Ah7aX6DpÉ$Rj>AI 4gRaHE*Tx'eөY[lvspӶ;5p,z]DJXUX@Z'sL0yۨAo$9j`ND+S0YarR+G?d%T &+;`Pg>D m!EjQօf\G'[Gg|ZcDb)IzKzc2AAx>c"!h~b+Rfkv9ڮ,E&/EھS^KA(HDze0+jx]È7+Fa.l>忣30.b S&Ӫf[]hOXP^&Kq[hn )k^VJ2hy2/03F󢡿H&)+ՃE^.;P1Gޯ׶u`7T;%~'{&..O%J֮ru?y8 N>M9/=9ŮO(_ܮimIGE.o2?sO,ϯ*Pg>_:ӣwYAB!=-h?X0/hA(mshɺ95vj!5)[F{eTJ+d=xU- /C($dMeX#{v"h6l VeLSQQE"zͧlVi[9E1_]%d! n/`fb`xc,p(Y )t':jgڵj]avϧ5J?*Ѵ?*<@X0/GV5,ؔɲKH`y+GEU\m8 ?4fHRG9cwhQ,2݃ iJ@D2)dXǰya^~r|V9|1jUnx2[zwQXv_d?ʋ|OR eqEtN!!]f3{1g錣"$Cz:͵9co C4Y;s2J$9ܐ+ԁE 6b?{$P:5i$' JXǐ3XiAӐTQ̢h.;']c4uK@*l8yR/E^f& :MV,+tt r4En}̛ Df`53zXR~B55/&=yt"֦4s~݂>{AE:Fsx`zR{7-{Fw3izTSƙE|TgvaShO g$Z`y9e~DyOC$Mr:(sŹ5#*Q/oZo({Y3lztr;NOJ^E]!>tv 2m P' Bt;i1Z*6'π+Cd+18_2g`O(~\z%z_鿅¢h!RYި˖U@ o^L~ir&ξF|2vΓʦ"ǛmHцú0wr2kKZ3 m<_uozkv%P2)0o{H|?2.Dz ĿQ5qŘȌn.בG>ī c+*]to㻣N:")~Ts[5zeDHr$% EqOAx ^ 7t]/^>pܢ 6oלѻyXYF?eG{6:,YRǹ Y`s6cO)_V.JҨh| |pwaɵ ]J<nHtq1.%')s5E^o%b)Ԅ/ EȌ}=fZ:Dq&9(pPSrÞ~YLTܫ&ɃK KܾsM˘T';aik"%Hx$[k%pW^KYQ)hUS=7<afƐ#Do̲G@-u|kC0܋g$ ע]l#WN@+W2YO;0r\/s5PO%s J&. Mp_\kx%/Gfʾx zm`RJ_;,u&&5]c}+Q=pcKҗ=S!Օ^)!r(SRHmD?j.ҵRj~&GW[\E#l { ͚{ 0d!%?J"+zK"qzz {Ʉ;OO:JJ 9w3eåP4hX`znBk{zEy*߂_M*? ?TWDh˖Ouߕ;{ւM2R^`la04kP3bQ:?a*wv5 ,O'x蜶R1$)ѱ0eҷ}c|ӣWCI99zŜq VIrQG#B@_Br(m 7s`ET"N$Xgs v¢8|*_sЮ~A#h\_jMigeҪ63:6| _>v}Jcds~h.@) bޅkW]E: Jbx v!XM46pN3 `sXt,;"j[{.=NfPV#?~ eJB*ǵ▝[)* 2j92'$ /<~zOZ㇟JmPe싒R{}u%<%.o-lZ>ڗ{8kri;i˖ cﺎ=}ӭoVJHL:asBH;;25ֶf5*72cx| "_I9%4"Li7M!9}'3Z;C0z> 񒩃RʸE"DJ )[I#8Aٛ`Rú&Hx|6CFi׼Nd!. '/hW(fgjB-,;1@c5e9椰N;a@3)ٕ:>>hВKIoOҫݺ,|snϾ(;tDJyzoRġCҸR]X'%-~aJj˙ϚQ{&3hQDZ+槼φv^WaZNސPL#+L?4DXрgiE߈6NϢARUAɺkxx~b21֝\Cל 68nu*qDS0Vy$;Z<; :}qIxj}E/coۈtX(.]Hn{ NQZ8L0.v#;+haiVn6ޒثc*eSUsӖ'*pWY iU;R [#R$dXd#My-DS=K<~A=cJE`-QIgu0b:/ p5HΓFY1+7 \5h13fKܩ~S Tփ޽ 5_H9i5LUyg-|6 9! jA3I]8 ioPbي?_ITϵ ?F[ /"qTeY L4ofa{mj$<^ }STdBDGI\HZIT v Yx ']Qk rhzu>״lZ!.DkGugkFLȉ</eaEo\uI:[4?U  /~tz9'3fkNitixf*eS5-"rMĂp1ѳ{Ȼ#^[35sP '4elnB@}bޜ < /fNNOŇQ 1]euQ>g}In!:s9%f}}Q pƽT%ۊ #ll>SZ>R*rkI]HEXmu]t}ϋ{>u6c(|r!??[~3o fbapآoe?ѝ~WW"-d/ZJ>[1YJh I.m^n+%p>:HV6?\ecs7U9eV_nxqK譕2錋Ĩw~lwæc AC#H]&&Szާ/հlN 4귋AA5sZ7:iiBa1Hn2x\W8-HGlX;X{%:GRK WY^gWq׻)Uu ]l-c^Яؐ#.#%u8Lm0r>΃ػ#oMXR3a𸛕J~ed,w>&å LPBqVh{īT(oi)K‡ Q3|ᓿޘ zHl|cOB{YQ Ś9h ƾE0n4Οb-=$ m1fu^D{7ODN'YqgE(Չ"a >qi;?)MĈ!jgZi9BBcd %"7yoElB-<ݵ/:w'*%6AxS9WmfTx#<2o2gRzEaإ8#puK{ EǷ0/g3eDWK:N2)w1AU~k>鵣vZ >ҵg? h61$JǀA9 p&CwX"ki%12sa7 5K֪vlLjN'gUo'$y O˭HRMa"<}P70j߯u+ނfr,)Ai:us:Ђcے¶~5N\m6Q=MODs wT×>(WTcdxvg)M 4޹cJrؒ2*D%*潦se|,5݇qy<'asEfRCC]6# b ymK[}cC-"<Ŗ]1^LdܲfdY{ )Ft]LTDqz$_Uѷ3o̾@,CFfGcQdF0w҃fC.\?>7%j6Ϋ#D#8\PW/f4jL*:~ƕ1Fi4Ķv\3?ܹϠb:0:kx^'"avMk"a-C(`_ZI~q}L6َ_ ueDt4%BG05lh-;XӃ?*IGR;d0swQ_;g XAIkϽ 6$.,6K;]ŞFET= {r,Oms *]1!bexsR6 Rn}X9kA0Ss2B F[ *:y&m~vO&wywB)0]JFLQt7t^hr+c'huZZk-%QbPU\yw} ǯ2fcv`d d{wO_ZM>e %KF'MԯHJ[߄_oo'V8{ʾl1m򁬩%A9v䖰YMk84f}aCO ,|+:ZpCMy<цw堷m5Q+|z+QQܬQY,y)~CBm,?~&nQH\K-.Ͽ _6c;37zX:r;y-C8P{!Zr%?]2O`2C96ޚ/@Y, XB3O3&ac6,8Q#.xќTEl@ eF!@ia?'yP"$s-?Zlsœ}.@ΈQ?M7&xs,3'ư$WӺe<܆6k"J.bNMˡoQ .Muo3cdF=aգuaN4k'Oef}T{-bNqlc3Ðin@Uw²RQUNFfwR'*"J<OߟIqXcQg3u3*F 8P}(@0gL<!\!䡨|yx-m8^$],]J©ˆ ZӐdFҟh8|P]ePh};}$AP;5ɺтi!I"C/oR4b0XxPYGOЫ&s-MerO8p hBn(ʍwy=cߴ`"~ZT8e6Di"㑻Y| 9͡nC XOSSAaPdcu2]NCW&}h"3L7DѢ^6+fyUIax )]b) 6F}\iɉAl<8=dly0E[7{?(Ɩ4NiXVھS7k>كo>f}.5GK[hSKԡv1U"h?` f-[lMOn^ߓ'd[ŭXar3b%F )~YY99x_U6$&DOÉLH 'Z&PB]kHy]ڌylhVխP!_YS9EJZn7O)u',1KJa8 Rބ{ypiW5˹ ]gy6^9 dHEp{K1#Ŀe.`91ܾ?9P\/M\Vآ KH,!*B|Mj+j;QͽuSK.Fh(2R> ˩'#u{qT%ehť_Yvb+mAEV$dݛWl*qHURJ[Ncb*XiǕ{t?35-((JWfX߱g>?GtB՛wpp R2Kb8:!L(>QB`욹1J Ҧ18! NFJj]D F(+93bw6{-fC=/\Dl}=B 7ѳ9+$=֋14d¶JG+l!( 2Վ> :72ɴR凡5dCABX7l_z'8XgspnG4|wQT#8p a]6M6Krsm(煆=][:1bC P$~1% d77}]:FFWD(2! W1o֤}?S {5e =`u L {8n@f7or:!%'AsZ+Ll|mJN7L mT3UQ8i(4JSO`SLrqVKԊ5hSÓfN hpcxC֫'V 4@,hwӜ { !Ѽ QIqlŴ^櫜x[ CxB};"h>bfoT%OރAD2aT'jPlmV+TVBUL#jxln3|]:JW[D~DΌLqAuif~KSx4LG&4 yL#t;(=J fEXGMѷH6В)2|5oK5ZD B2C.K *XdgpXLS ;RwS$sdtx B'(:m{E2&=}L!rdMa:mT[߰` ViZ#*!^v=^oݪxJ,ƹ4ټLיBA/'b{{Bҕ1Tivqq0/\hG/!eBi<9| Z`2{؇xPDI.9Rñ-$ipP$H,"$FobcYr'W~~eǾF0pjuz|b;/.(nʖ| }7-%=ijX0*V&OH?w }'jR^&'z3ې·WcE^)(}+ OLer=RKy4^(!lD7PIUHhmYX3Ep.t}<1Ex&"=(}-VG O+tglILdN+5IBv=b-qVhBgV-Lg%s5ӧT̴c4BJvo'UN:d7OH }T|3ڛLg*q@[*vI{@uvovaO[66dc;3ο|0O\B FmTeye^$5DYm871w*HAn6&`t: |bѐ]C _;X͋!+K3)u 0&v]R'Ɓ<^aNHOmUӂdB{&- FSz²'h0 E3vJg3L]c;1F5,o5y+Yp5ӽ%+1Be o6{xw;=!>0ڲI-58]&<]]1|z^no<۹hB{4 Z{_FH#9E 8(7P\1RܬAy`}l ARwY @vz6`5EXHb:^LKB!.y:<9n;J تݢ ɧ4`߈5r4XR :6% ^=[#O'ojYK }Cߑ BŰvنSPi<.[|?']ADw[3N``haG26mYB|)p=]aӗ%yce׶Tڂ/~pcʹ) &>R569gAG1JJkf0W0+[`?MQRQ)cŋT YWY4vcyk +:+/Ή0F;*ۑMPd-t;\u^,O9U7v#m-1H$XÃE<`7W Un#JzpVau\6P@tm:u?VcV yx~eQʄA͟>>Wj)Zgڐ/ɿѡ>}W>Џs'y['{?eLC6 ڲI/{fzPmA9>5D & V_EyϪCde9$(%Oȡ5a >}/0E(Wк6Aqx^:*Xi^LL޿#"3 /11d/?==8^YK S>~<56`֗aɉ5GWj|~> u*Ǚ.pq"YD Kw3bqہ&B. 8FU3h)󤳌u۞gHM޷%w!U}W:(n?P)8eg[/~ðCjI:'gV-HpWh Ům5Cؼ!+NґЃ?R9{Kbo_ط1RD;+ Au (#}j)i@@Ao.hL|IY~ e^q)Y OU,Hf < Z-euV=G#8 ۛpgگѽj34c#罽'ª£~|4ՆY#1Uc(T'Q;s{ ) 䦍AMS`k+پYM 3KR4b >aOf-{ÇqLpj~^/Z39d<- ށC@\⛪fznmo,G O cn!])eM\$Nq*IRFS4uU8}R$XKqiL:bYDSI\cF-B2u!ӉֶcF_p,oÔ^L<9 n/9BXץ8:{ʅ@o ]|\༹{4]f g|mIO}@8׷PoT.RC*ruS<;LhQLdqǴŰKʥ!M=}liiP ie3p~ax,FYU ؞-¾s FBLd Y$J.mJRWϷmDF5HDn(WsmTʻ ϹBRt+ŀhgZZ_kJqSOO=bw(Gm 353{( 46#_措6p~ t̍e37z퓋><x{87W)Ţ׮Fc tldH[rr%%362*G N2! 繍pKa J`Ro{i]:]s݁fRVDWhY # XJjɤ/1v;P0)KBF0ފ@W0 !̰Pq7 LEC8tEi^=a8CyUP,&I6`f㩹pAIsh~nbȭ.4G:h[R(\meQ,Nԟ,X-vE$޽u1P,R2$%8͂Ӆ-hrn9ぱ ~5jxγNN脔EyS#^TvT "g`Ye) '_g"!yȲG[m*e|j9vjH$mFV)AnM._xlq:w($L=9B~ۯa1Id+T'wMRX`rH`M@!7Ws"k2'P3t{ tHq-& N닺8ZujQ29hxq(s*_|6emJ֜yZ^!4-xh=Y]4~BK 8`L>M0->}p2dv] Dk'LX4ѺJ9|]6BCF4؎I_`Pu+ ZCo)9ĐqJ[è /}K4``x:2l6m`llhP( 4P hr +z$fn 7U\OkKL\]/7VҪ*_/WqU7# CVFyMz%EҘ bvjcN0$%ytpT [w ڈ:YȚY 7rcz@[.\B=i>݆jXɡLSRπ9gF>^e/Sm,BǮDT-!ㇼFF8Me]D߳ !f0tɑC0وaLBEւtV?Կ"-Pk8|.XQlBSiN/N(mȅaڶ_4`t5Ɣÿ  6l.E=~bN_o&| lA^a ׌OҩhL##M0LQ0+!\v 6eoK닮Ql&wUյ.7]*͎. ٰ̍IB̏\PxX rm@\\X)ʐ;5Њf;=H 5i0>(9.JQ$7FDG[fRt!ZXmETy (4T裺x6IRAt3ѱ3!v jPMeGPI3km2c䈢3ԔK)"E-O;CߟQzm/|‘0tsb/B4p8 '  UgCbu[FՏ[V3Y_zfGNaM-<8r]C{ >1] ed7-MsPYvtk a~QhAE`6ж[fVĢqĖ{)*Y+Hl?K885PeF맯Tԛ?'\s=SCL~̾5vt=@ںּS7%C`{y։yzjA%y\]ԺE(3t2m]FT$8f&o nw0;%܅L!gU"ȈpNq5FO,|n~&_GeUjM6WF !=c6 I5$Q 'v'98 r:KYL }` Y&ęXknֻaDgfT$D-ֺx*~4OHAKL\ G^9S#ń/!!R{a SܫoEreUne}gReL=[I/)3<ŗe{i:Js~tj\ZMΦ6`|ֶLc)H{}p"}{ : ~GeС_H@ ! Lv툥pq C e-gP08a.bl|*KSq}u(IHC°PɋoX (Hf nBrYt:]~.iZq\^* srWP߸*7k#1=CU4Zj|y(#Mt wlg-六@i2CVÈ /IHamwִtBKfCUv0릈8}d]X% !XN{0Voӱ :#-SLJ~}^)a#da:;΋/kNE}BސAce]㠇+0'J\+sch%:^jHh=YUi9Ob ~kQ{A[8 w]mn/R]i.X\ә|CF8-<@R2ko!qS}fhx0(sxȼD .U_7`rM iÁl4'2 8 .Hf QG(pE^x3<YE, `.!o9,$J:rקbXXRJHl &gk&|XiԆ;&\>c~YPRS߬lW MCnbWݒ5&+$زL + aEUy,C/[N^~jcw2˥uBtP{QN+"+i7Rf(V_eƉϒxIU? x}3a:lEb7l4igOl;Dv2(h!a spk.(?M#{ʭ/ydwY(v'G9et?҇ս.\d HXJh)%ڀYҢCڍɅUY27Ϡk>ף0%JWÃԟ3Ƴ!r'A8;.C5;YP›l3Djrk&E{ߦ1N,NjxFհFN 0bCnn>.2u2_13Ge u ߒKt*AsfqIYO_{.e}7!gTTC\v蒲N XU\][<0E옎Ee˃ ]W+kџgtcMYeN_*]b(3V 9Pcթ4+&pH:ZIɬySNevi<<~\5>ڥXeE a @Mb |~}6lUxFF?,]C7X(i<{"|Z?MxAVN(EmmnQ&k"l㪶AH5q^> ɽxK1ìfB}jM^5d\9$?-$]D6tXF }onm#=Gu'ft3)jXYz#e+CDPNکpFN5ʌTU#0G|lY{jc( eJ. !g$?W!V-@:$a3iD _\OhU6lEBa9ΡsI`ґRlso;wwٓTn|Sې{C k630N+{?IFn(%-Pf򣶕4ܯd*G<a3rb^-aFa/VxefKPK{Gx*2C3ߒ@=2yjҠ ^R꾺ЊPW UXـGuv1eI\U6^E-͡#)TTӻDʣ .|aKUBJJxopokq[dtq #D nJ~ LpdbG{°$I54se+/pt#[)iUpH! x&U[7gR'Цp{',lJϚq9boty#,#Ϧh !]g#/{*>Y&HX5.UAKعkV֐gP%#>O`ċ]_0JQBA_Kfry_7пwTæ¤c@D(HdDX~aV Z[ Qì΅ɩ͵17my% ,D;r,k1ŽK՗?oz6~k1Hp"??R0˭4l ynkڭŮ?yDWڄUˍnqVBd1MR[mKyC~@Q٬ $9o#h<dO̗u#kݎ!.Osa&\UrKI5Lo*C=SYY?Unȱ ׾GE:{sLȓ4bS|6$wa/-se' Q{ mR@1aߵ'к H*#wB񻢴4zŵ8ۍ:}V귿R4t5KGQ8υzS6N}|mƾoR?B_Wձ_Zhv 27e*2Q`*juZY&߇%*En}B;?`A Ou>g)7[[ 3ӻ h):/U*#$ (&l.6mk2AN9?AE/\6;<3hv5z&y]G z4Sflb*lJY"u,ә,&]oP6{~Fsux/Jy93QMߗŠ8SEjeB^`}QY$M|G-8G٠cӝ)fbloI@",aټcHu@zyuDHCsoNvwMVXc+v@"3/4;KՄwR;B)6TP[)Fϕ_Mx }`GA=ވ@)^Y VM,`ՙq{Ԓ֌M7S"^` *7u&aEr! 6->Vsff33%5FEnds[!ɺKjhgׁe/tH$vBvoE,X,N#p:MϑThSvZM \sQۯtmBuS9aVyD"p\>R(\+OP+iQ2Ԗ=RũGPLs!u<{vJ\D_ "z|xDZ(EoYJ%*ŧ yH{dVN]XbsǍ0ckPu0x~| W#G\X̹;1xi-:*8 J0e+rLBm\i7IL~ \XduݖM︗; Z DK'/JRXh!i3*|W > 8oe~yO򈕑&ж:|.p'& ѝQIo9{o^#Zˆ&"B6@q%ZTz J$諿P-i78 q/]hr o=mW&K/̨O 2Q{3/x@if (pkX%54qZjN&Uv/ZʠTW`G0fч?tzpZ.a$&6uY"ΣuQҳ {e}`v7* nW%)a͌'{x.Etd>rRf#/egQ 14ޭ&5xLr̺b܎˦xKXf HHW鉘 u4p?o؀i^.()",Oٽa&`!Oϭݘ"6aE$~') PgO G\+ݫJTRQhE& ;ul09;h҄N^Yr.zݸ".YAL)>]a# r2 me%ђ7 I9ԈO!b=U>" J Il[`WGmyy]Z{L ϵ Tm&݉Y,>r j{[11 OWf*bqa.9?Dn ?yV3d{8Rԭ9'09k˺Gݲ Έ(4oG}I:Q>Ɇۍ{KR4mVSTy|~JFq%25[`-֫& YU2{WGOTOu<m[:0ShIyǞe;PADCbmd~~ XFg^ %v A lkm~>.;ӵ\?8tvvWw1< [fFCy}ly>pwiiu}zQzX2#VKggcH&?(FPQi$f,'sf6w ЇG 13`z؂ѪG9h9ȒC $C5ݿ'ښyXТn{*2^c=f2.im^_T˄6DwgZ-.& RgHAJL`SKJi/|{% Hq2||bs۩XxY9=?QBWJ Hye%|UWOE|'s6 K'l 7ʗB?0C.y<'Gu.ƺw)0Ϲ=Zm/X?ϵߧ}5A=M:HP "6D0n:4V#az }TdkTo2q_6$ɞN,(~+'Nl17"4fADWu v#4kvlQЄ`)#vïCYx\nC&n$ "X/kզńS^q>93",ljJ;;O8: + w4߂cPU2g_J!ē6`В*g隻0ݰxߎ_J6;j ' t'|&= /|ن:|^Sڤ3~17S J.mo*A0F@Bg:~2 d5wӐyUvyfX [h owIoShPfT "9wuf6nw]ܝbv']ĞX/U&߆T]v[ ҄$?v*)ϔ6:}S_ !c xA#7PcDMIc}% pá>b'#ܴgaFu|}52|{L 4!a!6l;"?J2~4&9<)v'(g & O~3S~| eR twp.O>,ztJ&g,e  i)9CN8K/U M"L\AʆR(2=`*6?.p`%G7<)@TiM"/䦠YrtU1D>5 [=[5lwۉW\~ƕFIhFH2Q&AcZvl5CpM`j膍LAvy:X^P$6FbwpdU64_ MI .k4c uv򞓜Py9]]݆tж&A?GO#T^@:~r"Hsײa_KG$s} ^,<8fݢPL*en\,<ʍ(Y | |FƷ-vm1N8Q[ē:`Ͱ F@bNv>Kfu-ւ\TQC)R1MKօ/w^ ;L'whVv%s-Ɍj#z߭u^}e Y3閷 p>\hڢadC3 y15\<.Okg 푙=0 Č#n_lv- :dW19C3 @]^3ͮ6 TV lΣ 5! 9 6jDw(j_lƖAF_ɣѦjh~ċLpd9&1!ۮ4y;2iK㆘D^yϾUhDWk1P4v4t 5qKV9Ops\R}Z]*/R`"yxkOBwߨoMZ~_fsuɨ#Ljev7TDLN]}`k]<17m\ፚGȅ HG DifP=0#uXm~gv>}ac}WyB45nnre+ﷵs# ,~W!'8ˀ$+ԝqOHHO/qN (:z̬+ʃb&?VޞL5EV]\즏;| HB Px"n6U=zPTk""Ș=mZ kύdA[ ]|Y3 r4S&[Ǭ.w1cR=1Œ'iWptF'8n.u,9&z4֭ endstream endobj 69 0 obj << /Length1 725 /Length2 17896 /Length3 0 /Length 18401 /Filter /FlateDecode >> stream xlsp6Ml;Ỏ3mxb۶͉m۶mk߻SVꫯ{u"'uV2dg(02Y`Ʌv"@.: 01`vf*c9jֆ&65;k;W cs ?@9`ja +hJʉT@[5@ ca uRLlM,غfhgUS 3 mM2Luv'h?8Dz6e?QG[LL cgx5Cr&@] m*a;{g#@h -ooɐt6 A[cIh`Ύ.Ձ>YͿS5?+ *!F+,jklgbakPvOCG8V0* DzΎmF|k,!!;wL,L,Fku4]_3Lk --A;7cJ:sb1dnt{O0`Ļw+yLb?&]})&0~S׻Pñ1uXTbS#Pʋ"mPy'~mVZ5S x@[#aK1foa ZqHR6hKUٚTV&J_F1{8˹c\GEe7u2!'y~:: $ es! LRXYۨJHͅ)!RQ RZ (:2)LF **b0.!_zG0O)/N%<9>ՎEob5f۾5PR?7 sԺ $F Sػ EK2]_hv(5k@{ +Td /ԡ25p0^sj eL?$* Q;VuHQ_ ?x۩Ŏ{l-"՞aljf ԥ dLF =c-3`S[]ʐj !Lim , $1YJ1]@$wh MD>ơ{0ۋGDIb[/%A9"B| O`D߉eypFwIjCëdd$X1t吲s~ 5v<0팅dĒѫ.9~MD=',:z"gÑڗ}iP[ 'Bu5t:&-D ,RV|LInYOj䳊#7qZFKfg%ds[yL@uT\ʝypzm%̑u BU,gsa +V_u$aTv 9gǢ͙CL]0Uf\]J|4*XWJ |?mj~N1aƖOB iu*+qF`^6RLK8M"X goKW[p?-f2k顦[F][xprAgjn/=HAmZYTlvǁ]ݽ1ق%b^Yl4Dk8i)W(W俲x^lR<}MZj3=v澔#:m6: B; =Shr.g$kjggBZ :"O:[vTF^{?;8~V#Au5GjP+ZN{o\;)#X o*g/9ZbIWEf<" 䌿ᳩN "8誏|ƅv@4qLy kz_#ڹT}"#vs jftPmCa#ߝxSWeВh2.񋅙^`lg[DㅮGCw5^ί%鈴ڈ ]ARshu;@~KZ$cg<^S'ǵc9mIǾ]F=BV|d؅Ei.æ>B=v ۓӈpanNQh}2?ĭPyG%xA|1/$/t>$(}Fl=@0!d8\R&10٬^grf}Yo}pp*- pWjB<(O.]Q: ;<"}0PdGeٶ'ZF}Ye(+3-/*Y>ǩeCm]+vN)"C~og)rN%O  2fR^Rg3ـ\SMo"V%!ntJ?y4U~Zp(mKq=ذk2ر~p@9*-~U/Kz qɩ Oc/*_U]TFRPSSqt`qlN"~Ǥa[|#2v^ Q䗇()dR8tn42hTV-qCQ[FV2kegMhAfӸ|cnp)tpytSp³iZR7]q#;Cpl%U~sbWUh/6`NBI&x9;͑krz1Ϡ>bQgG'QH]FGT1YOAN㭤`Z ɶO9Fȳ_]y>Qܯ8xgp7zqKo|߹H1UW5N3:Z 6Au}s׈rȎ몠&%xH4vRj8'Jz)kJ׉DXz)bҞxRm^)[#$@Y7@''p 7,ktoL&4rs*!K+S 765Yg1T&B+ӫ{|znaq[vr]_ m1XWQF5QTp7}GLcHuQ_9B!~,o3rʱG"_Iw-bRc4Zi2k6_t ekW#fjT8B,fh'p{4~;v](4w2(lۄR'6 Sb3؃tQaH H&2YVE*CҰB8dLҴ:;I{>c^r#}bKho*)fp?S os6KD`˩NV޶P䂼nN3tͤ!.WܢZK_#xl9jVAG-Pv}쓄bc}).}:]/b5$Q(ŵQi>l­vH@${0JNwL;<9l {gOOůJ0b ,H*."Bm63SfSk.vig)Tu1EW`c8fv'3?f%/;r|\!pcĕMb.+*6\N!pih5L ;Gd aIkXz3>56VZrpQɹՅ8#b#s GL^yQ3[Cyr$>cۯ3jk4/APГ瀢_9gZB&,zPB[6A;M8 oNksqχ:iLׁ)H7] mc/yۂ|fzp'ȕ\'k.<ƌǵ_֤T\Df|߫K؞ǡjFz> y5,hoM+yQA~2HaFWlf(T.>$GFncGG4{р8› VıÁثQ -m˄8FYv.ii ˾wnur_҄ >h}X֌/S~[TLT۔%S5=\/s4- HsKDȼI֥et-WDD9%Xx:PP <"9j$;rk94>S|s.2$yHqӉ_ 5q!=ynrǠx׾OT3CJDΛn=,{P $SÐ * uE>Fgu'W$DʆW]Xp+hpQ# oܣ跫\g~3A)3]If}O.k0 '*4w͈#HtATl+βcדe.¢QLmZFBINtٜ*dMB]雋'߾; ɠZtYv:yNX| 1d,n*8?o+E|dqϦGg+كm})脠~#nN# mc e嵘=2^_mptqmV+|jC^7ZtW8U`aӰqo'ԓe<}C^ikqH "b1}Wz? U^ۃ 1S-(rdXaMI?mA{vrf$kd(ȓ`sM>Ih"z˯|N"~-DH:VofD>D>TfT3Yd{Pa+2u*K#XFoR yܺx>76(H%QF.2 K//ш޷cWr|Uwxs9#S:a}9 D+;W&Ɏ6=op~`j }xI$- Ǧ]@';6jߨ7u$K#_"Qp/a81mo+dQ_8VP0҆&0:Ba$HL}n=νw6)ҨA:`P =+\NU9_gK ;nJH6KۑၑZ?ڂ7I:yr 9d'r]j-G2hӬǹ8򋍃XI$׭@dφŽd8 rd3a_?,w(@;=pHcWoIPhB=}s?]VhjkMr9wX!V,mw( ×5e QNa4/7VGgXzS?:8 (V}˻s&NE0O-lr(ǘ?>چuS$r kly!"~aQGO8SnզxLʌ~;F9/VixUNjoz39 db^0@[(:3LL4NW%_po1Et9Q5`toõJ9m\c/+a%qUh*(Fp%-FDo,|Z[=GʩrQ8&dvY2{w{U_>j.@͒ɋ.J/vHyHS"(߸!%h|s2ގ蕗xd4KnէP`oo [T*WtN얂b*O&Zd/{v yJFLk(/ g}dO"7yQ|l鍚dHh7hʸ,-,9P"kDL:v/E Yq`JF;09y*ҧƺӜt\-bRC9ұƺcy5Xw73$<]V \H4 o"Fܡ2y2៾xƿP:N_ k?GwуTf- }wIPnYb j.vrř,7췅:-9I,rS 5ndeWծz ]+ ;mou͓T"bE#~˿Z9+cjㅳᏬX dRj>of8[6F@ǟ|LIW2Mn_*WQed£,Dt"?='vXZkA?H*]rLҀP^B&:weJ͌1kAPr)#Wý.L`rȳ|3M5,{5q?;vkŸ%u++iU!~BkhrZdԝG С⼂l8+AoC,H~$iX[Hd ?KCqHwG;+ذP9K}⚨cS[1P")h^Q]~?!z1 fm'ga;Yl| kLig]syBEc@r$CYT.p 2j Ud=rϦo;u]aIkvd9f.i a=& &Y1BڸصtT=0zeIsߍ64) Lz*^uTyp OE@L*Úp7WTshPYBQN$7/ˆD)Q>A0P8?Yqz%8ꂐeT#Da >( _'F*ɱ8 `D*ƚ1ꦼfU#qHTfp[*TffC|>MwBɼD^_Z5vЬgjGoyGHO] Ghp/E:(3{ߏWu/ HNGKY̌Pw4HI=`#,A4GM{,c;apl1}r LaJm. Ui&OMrs1ɻB[ft½3ˮK-֥ \."OpѺ mVCDpuIONDUy$Rghg o/\vYG^ϤPfF$1vMk@mfѕZ|4w[vq00|5ypRT},iww`LH]jw5Sh$PglZA-#Ǐ$05g1/L?,s!::я%HlxA)tY L#b5d ʰ=.f&^(ȇ|ˮe;>hq'P^V!m!w@͖yF,]uz/̲{(i9[~_uTB&c08"GPȩF@.IwUM=>X/b3-p &Z]b\l6mc>I\|s@XX~G"5IG1&vW؏jS8c(mq=MQPܠ)bYMCi/!yJ1rMdƶ./=$2yum'д*1os,0k j)}]7 *˙KʑeCsxNOP`H5]}qs s~Z >%FTuS d[SS &eJsWTCRuk@ʛM絛j\"MH9]>GV&4 T `iXX]M0< #O }D)9:0 uCYS䕈"W Jn>z d5#M@jk!9!tXꔠx =cU lo3sawȐ|k窦,.Fjҵ`T͜(q2"*i n}ڶ!oթsСK t*/؆lzX[`nU"MDF-H$i9eJ<_cAAD7c_Fa! TaзUm p$ќHkkZ@ilyWf`C7|}pPк:;E-.%:ZyxpHry Jv /~%Ly4Bu 1kKX \[JYfO/:v"l> opF\s Ѐ^K7+vaeW(__Ρ'?ͼa~rx4(? t,M1syHv¯&^B M}z+qk4ƭZKMkn_WӇ^m+fHŻkiVJƐJ7րAf%y/+m%`)GnQ) /c1gR= Oss&c2qm>^#œGj"X5 먷N|+|q@>=Ѥ3%6̿;}ӝ+[ȸ.mGycAV#2P1٘ W3U{saUeJN,"uziyuZYmb cZ,mĵ v)DZQH{D֝~I!JD2r,fBW11.K{Yۯ9 ܋;p7pԦ)z뱎\[3tJh <@jXs/-` &(rHC[갴rawoH֭`l|$ԾbvIu 6`7UNT1඾'uUtqOJ'9Z>VM~qQ}E:m)d ё_mЙ }T:pƮ^9Lb\ԙ4Mw&/ccbҥԌbu6 Zbq_UDqI?2٤TA~A1i`$-RB2&v2j q_/+SU"[I`MviIIXT]/d\۲ε)~@=1QoyY Xֻχ=6u>V'z Ce"Rz=~rgHUɔS [E_|(GȬ.>\H0B m n5dr5ꃑ@M/u6-ech]~(CzؠT-7w<3's t"ZY4cBiڽ?21K'|,AHDx].a7][Z0C9EniD-1 gl W\\˭E& `+Dp_6Reу:aYQϺ D\v͓A߈lj(g fqwߋKd#4!UxBrDCQ ]Ћ ` JLxYY|/wwO_h/C[G׹d܃1IX/ۻܦP >[x3^8RM|6γ 43Ǜҝ G3,Sչ'+}Aw"3/;eW 7mɐw$%Je~l 7Ǿ@KWLpI CZ(bE:_6+5{]o|Jw6Nt4YJ 4.u>9bwnTnXTuȠ{@b(>3il8b`_2Q al{ݶeR|\9*8(u!EP -8:51FHhrrXAZ2*GE7yG}ߨA 8W`Tc!edp ifWň;ƺ&7 n^*uYR |\9%^: %ӧ#l;ݖT}S!ipaGr#>6Ŗc11{r {ŭP4 3șw*^;@C/;Wܓd\)2lG\D>[b]ΧemE|]nòޏn҉<SWNZD1)Z>(R.랔n_u0 !x2]52~2p!Ω&ݥ09۫mh+%:862.Z J8@984B-H By ЈTQJZCc&)Tm'̈Ûr8p%D)ܵت mXǥxPRÀz\Xߢ6kZ>=_=)HT3bi IBu1^MZ.V/!'ߋ V|s3Yl>eVICM_|͛#P`| lÜ=` !NyT5fz2di(/#Il{H}%@Jy'N=0_LyHC;X "kQϿ <~!S[Oy~]m[>,Y 㰏DNss[Bja@e^5=88"#pewuܑOޗ&ppGz"^UP!`cGh,hCE)>|TQ집ؐ1|_ V<:Ʋ X^%4T^+Z.'kb7,A t'3q"fBA^i@AL=:)+DOzDGn~o+ZU!t3*eӛ&?^ bi\&KWEpYF[×9LSɛ &f]]ɭ.W)\ʐ>(kݡoM1BDaWP<7 =50MNREhkN!M)/ƫ_X7QbFLsΤj~.AO0L)t w>_.tQr52 <$8},YPqXTbT[ܹҫ-$CRUW`{hhTvz/0Ժb %LjATA2%v~aH\0^n z_-k_+~,.$msW!+g$VU.>ewU2ԱM ?кp+b̈B,,uH퉇ª'4 3{C}2[j4}Q5LiO S}' DM6{.I1:y't٢嫯Q;)H^ B֮nېUupG#7s(c :sTߕ;8T"gHbpP] ̝$jpVs&easZ)M>3 gio3䉞$ ܨd%'%PM T{Y`m45\+f轗x)f̮cgep-ڌm#HxlT1igp /$>^geX%GlK#m,wؘV0TU ry46X,՜Z endstream endobj 72 0 obj << /Producer (pdfTeX-1.40.22) /Creator (TeX) /CreationDate (D:20250322085408-07'00') /ModDate (D:20250322085408-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021) kpathsea version 6.3.3) >> endobj 17 0 obj << /Type /ObjStm /N 53 /First 418 /Length 3115 /Filter /FlateDecode >> stream xZYSG~Wn8+B@830 j` KGtAxETו_մJh0Y脱;cDA '/*ֈl솵Bu7ka^)˴(F$LN p0%e+ဳJ@ 6J D Y',)+/B)2!R1 \B!E v7'e]0t P##@R5BcC#Gkq ?D-tPjH&pJjɓ yuڐ;iu= ;ڐGdt;W#uTg,:~vރē'BfGA0n`鵠`0 MS#J ԬnOpr&<6)_Gj*>lfB>PuTom|( nc SyqAf Yit w?ISB(P:a6JpPb@9PH4'fe,B-5Sr%- CgqKh%+c3xUO(] pÔӲ~gsVȢw3lV9TCcx[k5l?:K>B!Mpgq. P6-΍W=;3Vm9ܐ,@<bpp!򔘰"S\<-2AT($PHXoY(\%2t ej9xLQcKdצ-YL)PVAМ͔m0&'G3ñi92\lN aڢ({X61kiGpA~8ς+`e[{ 1!TPQ?;+*%AKiMדƀGY0)μC  I4gDa[Žc;>3%kLKIkd*q #Y3ի8뚝G?%#͙B܁[clrNǬTCmnc3&)iO2\9p)Ƣ@QG1\#d%G0MR\ ?i*Y;9CW)Țr$u͈oTJA,-r͔D)SOBn( ;_zD|`(@K,s9:Ol҆bF(0ÖVfs9:~M4xӇUxp3ݫ?ٯ? pGTc,GRlPnrt]|=ΧRc뽣=xMl,#(W+L:N/UQȮЫ_ _Gؤ4+ߪ=)1P7WIGțW=};| B+cŦ2񥥺jeu/KR)J]'_#y,{Te4]3YA y)/\VCy%H`tʉ[E)v Y+|8~!qn$:5xg!ك䊐$ KG^|Ɗԛ- 7B~ުRr[n\"@"&'-һ3gһ>+?Ӗeu}֛\KũaLv'ǥ>M!MhxeR}'r2DQKUGZ9oz'wXԦ,eOV:sf~^vC[o q S@vy:+Z)6?WY7y`gk~1zhj2a:r턼`7nL;znI~hj7Ⱦa 5⬺O8bgN:܎>DR uq1!s>pH1Лnfڃ/p}vNt\ =x{n_ypGFׇ0Oa] tM81 r< ǾP۾(%)4%gJttCB:(Ywط$1e()`^e/]w.Rȱ.ڌPK;JcAh㋣ӽhs?-:6p7˟7 8O 铢x2E)mxx;8^NƥAuk4肷Veg>w+j7kA0O3B17m:sXs*;3%ܮy9{Tءx7kdoԌl?] k8,s,xuy|.!b:'O^ ZArpN7l#ȳX$u ܆Ʌ<3o.Os'm-eߐ/gp.͆?^6m!G;wzP.6x endstream endobj 73 0 obj << /Type /XRef /Index [0 74] /Size 74 /W [1 3 1] /Root 71 0 R /Info 72 0 R /ID [<5A33036806BEB3F8F432B07ECD4F900D> <5A33036806BEB3F8F432B07ECD4F900D>] /Length 208 /Filter /FlateDecode >> stream x̹NBaE8 uDNco9lhlmL,W=i,|:͗uN%iAʢp=8؃}88B V. d7 SV>s V!sh,C`JP%2@r˫U7jzE[ 'jnFլgTQ veD5QNϯQ-F\:~% endstream endobj startxref 217620 %%EOF GPArotation/inst/doc/GPArotateDF.Stex0000644000176200001440000002402114404451600017065 0ustar liggesusers%\VignetteIndexEntry{Derivative-Free Gradient Projection: the GPArotateDF package} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{cubimax} %\VignetteKeyword{forced simple structure} \documentclass[english, 10pt]{article} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=FALSE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Derivative-Free Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotateDF} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \subsection*{Principle of derivative-Free Factor Rotation} The gradient projection algorithm package \emph{GPArotation} consists of wrapper functions and functions that compute the gradients, $G_q$, needed for minimization. For all functions included in the {\em GPArotation} package, the gradients are included in the \texttt{vgQ} routines. For example, the \texttt{vgQ.quartimax} function provides the gradients $G_q$ for quartimax rotation. Examples of gradient derivations, computations are provided in \cite{gpa.1} and \cite{gpa.2}, as well as \cite{gpa.rotate}. However, the derivation of the gradient can be quite involved for complex rotation criteria. In such cases, a derivative free version of gradient projection algorithm can be used for minimization of the criterion function, {\em without} the need for a gradient. Details of the methods are described in \cite{gpa.df}. The method is implemented in the package \emph{GPArotateDF} that may be downloaded and installed. To perform derivative-free rotation, the main algorithms \texttt{GPForth.df} and \texttt{GPFoblq.df} are available for orthogonal and oblique rotation, respectively. Both are minimization algorithms. The algorithms differ from the regular algorithms by the inclusion of the numerical derivates $G_f$ for the rotation criteria in \texttt{GPForth.df} and \texttt{GPFoblq.df}. The algorithms require: an initial loadings matrix \texttt{A} and a rotation method. Optional are initial rotation matrix \texttt{Tmat} (default is the identity matrix). Other arguments needed for individual rotations are applied in the same way as in the {\em GPArotation} package. The rotation method is provided between quotation marks, and refers to the name of the ff-function. For example, the \texttt{method = "varimax"} through \texttt{GPForth.df} calls the \texttt{ff.varimax} function. The ff-functions are the derivative-free analogues of the GPArotation vgQ functions. The output of \texttt{ff.varimax} is the rotation criteria value, \texttt{f}, and the \texttt{Method} name, e.g. \texttt{DF-Varimax}. New rotation functions need to be programmed as \texttt{ff.newmethod}. The only required input is an initial loadings matrix \texttt{A}, and any potential additional arguments. The output consist of the value \texttt{f} of the criterion, and the \texttt{Method} name (the \texttt{GPForth.df} and \texttt{GPFoblq.df} algorithms expect this included in the result). \subsection*{Derivative-free quartimax rotation} As an example, consider quartimax rotation. Gradient projection quartimax orthogonal rotation seeks to minimize the sum of all loadings raised to power 4. Thus, using the notation of \cite{gpa.rotate} (page 682), the criterion $f$ for minimization is calculated as \[ f = Q(\Lambda) = -\frac{1}{4}\sum_{i} \sum_{r} \lambda_{ir}^{4}. \] Derivative-free quartimax rotation using \texttt{ff.quartimax} is then very simple \begin{Scode} library(GPArotateDF) ff.quartimax<- function(L){ f = -sum(L^4) / 4 list(f = f, Method = "DF-Quartimax") } data(Harman, package="GPArotation") GPForth.df(Harman8, method="quartimax") \end{Scode} Of course, for quartimax, the gradient is easy to derive and regular rotation is a better choice. \subsection*{Rotation when the derivative is complicated: cubimax} Sometimes the gradient is hard to derive. For example, a criterion that seeks to minimize loadings to the power 3, the absolute value is needed for a meaningful result. \[ f = Q(\Lambda) = -\sum_{i} \sum_{r} | \lambda_{ir}^{3} |. \] While the gradient may be complicated, the derivative-free function for minimization is straightforward. \begin{Scode} ff.cubimax<- function(L){ f = -sum(abs(L^3)) list(f = f, Method = "DF-Cubimax") } GPForth.df(Harman8, method="cubimax") \end{Scode} Results differ from quartimax and varimax rotation \cite{mulaik} describes Absolmin, the sum of absolute factor loadings minimized. Minimizing the criterion is straightforward using the {\em GPArotateDF} package. \subsection*{Rotation when an algorithm is involved: Forced Simple Structure} In certain cases the derivate is so poorly defined that deriving the \texttt{vgQ} function is a non-starter. For example, an algorithm that updates a weight matrix that, when multiplied with the loadings matrix provides a rotation criterion to be minimized. The algorithm Forced Simple Structure chooses a weight matrix focused on the lowest loadings. The rotation criterion value $f$ is minimized representing a rotated factor pattern which many low loadings, restricted to each factor having at least some salient loadings. In each iteration, the weight matrix \texttt{Imat} gets weight 1 at the lowest factor loadings, and 0 elsewhere. Assume we have \texttt{p} items, and \texttt{m} factors (for a \texttt{p x m} loadings matrix). In each iteration, first the lowest loadings get weight 1. Next, for each pair \texttt{(i,j)} of factors, lowest loadings get weight 1 until there are at least \texttt{(m + kij)} items with weight 1 on a single factor \texttt{i} or \texttt{j} (but not the other factor), or not enough loadings are left to get weight 1. Possible values for \texttt{kij = (0, ..., [p - m] )} and defaults to 2. Forced Simple Structure is most effective when \texttt{kij} has a lower value. For each increase of 1, an additional \texttt{(m)} loadings get weight~1. The criterion \texttt{f} minimizes the squared loadings for low loadings (``non-salient''). Salient loadings are therefor increased as the sum of squared non-salient loadings is minimized. \begin{Scode} ff.fss <- function(L, kij=2){ m <- ncol(L) p <- nrow(L) zm <- m + kij Imat <- matrix(0, p, m) for (j in 1:m){ Imat[abs(L[,j]) <= sort(abs(L[,j]))[zm],j] <- 1 } for (i in 1:(m-1)){ for (j in (i+1):m){ nz <- sum( (Imat[,i] + Imat[,j]) ==1) while (nz < zm && sum(Imat[ ,c(i,j)]) < m * 2){ tbc <- c(abs(L[,i]), abs(L[,j])) tbcs <- sort(tbc [c(Imat[,i], Imat[,j])==0])[1] Imat[abs(L) == tbcs] <- 1 nz <- sum( (Imat[,i] + Imat[,j]) ==1) } } } Method <- paste("DF-Forced Simple Structure (kij = ",kij,")", sep="") f <- sum(Imat*L^2) list(f = f, Imat = Imat, Method = Method) } data(WansbeekMeijer, package = "GPArotation") z <- factanal(covmat = NetherlandsTV, factors = 3, rotation = "none") fssT.df(loadings(z), kij = 3) # which loadings get weight 1 in the first iteration? ff.fss(loadings(z), kij = 3)$Imat \end{Scode} The added \texttt{sum(Imat) < m * 2} requirement was added to avoid infinite looping. It is useful to consider random starts as the rotation tends to have many local minima. The method works both orthogonal and oblique. \subsection*{Examples of other ff-functions} Writing ff-functions is straightforward because only the criterion value is needed. Here are a few additional examples of ff-functions. For all vgQ-functions exist and are preferable to be used. The oblique rotation criterion for oblimax \begin{Scode} ff.oblimax <- function(L){ f <- -(log(sum(L^4))-2*log(sum(L^2))) list(f = f, Method = "DF-Oblimax") } \end{Scode} Entropy criterion for orthogonal rotation \begin{Scode} ff.entropy <- function(L){ f <- -sum(L^2 * log(L^2 + (L^2==0)))/2 list(f = f, Method = "DF-Entropy") } \end{Scode} Simplimax that works well in oblique rotation \begin{Scode} ff.simplimax <- function(L,k=nrow(L)){ # k: Number of close to zero loadings Imat <- sign(L^2 <= sort(L^2)[k]) f <- sum(Imat*L^2) list(f = f, Method = "DF-Simplimax") } \end{Scode} Target rotation. Requires both a weight matrix and a target matrix. Target rotation can be both orthogonal and oblique. \begin{Scode} ff.pst <- function(L,W,Target){ # Needs weight matrix W with 1's at specified values, 0 otherwise # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). # When W has only 1's this is procrustes rotation # Needs a Target matrix Target with hypothesized factor loadings. # e.g. Target = matrix(0,8,2) Btilde <- W * Target f <- sum((W*L-Btilde)^2) list(f = f, Method = "DF-PST") } \end{Scode} \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock doi: 10.1177/0013164404272507 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2001}]{gpa.1} Jennrich, R. I. (2002). \newblock A simple general procedure for orthogonal rotation. \newblock{\em Psychometrika}, 66(3), 289--306. \newblock doi: 10.1007/BF02294840 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2002}]{gpa.2} Jennrich, R. I. (2002). \newblock A simple general method for oblique rotation. \newblock{\em Psychometrika}, 67(3), 7--19. \newblock doi: 10.1007/BF02294706 \bibitem[\protect\citeauthoryear{Jennrich}{Jennrich}{2004}]{gpa.df} Jennrich, R. I. (2004). \newblock Derivative free gradient projection algorithms for rotation. \newblock{\em Psychometrika}, 69(3), 475--480. \newblock doi: 10.1007/BF02295647 \bibitem[\protect\citeauthoryear{Jennrich}{Mulaik}{2010}]{mulaik} Mulaik, S.A. (2010). \newblock {\em Foundations of factor analysis} \newblock (2nd ed.). Chapman and Hall/CRC Press, Taylor \& Francis Group. \newblock doi: 10.1201/b15851 \end{thebibliography} \end{document}GPArotation/inst/doc/GPAguide.Stex0000644000176200001440000002700314557733750016477 0ustar liggesusers%\VignetteIndexEntry{Gradient Projection Factor Rotation} %\VignettePackage{GPArotation} %\VignetteDepends{GPArotation} %\VignetteKeyword{factor rotation} %\VignetteKeyword{gradient projection} %\VignetteKeyword{varimax} %\VignetteKeyword{oblimin} \documentclass[english, 10pt]{article} \usepackage{hyperref} \bibstyle{apacite} \bibliographystyle{apa} \usepackage{natbib} \usepackage{geometry} \geometry{letterpaper} \begin{document} \SweaveOpts{eval=TRUE,echo=TRUE,results=hide,fig=FALSE} \begin{Scode}{echo=FALSE,results=hide} options(continue=" ") \end{Scode} \begin{center} \section*{Gradient Projection Factor Rotation \\ ~~\\The \texttt{GPArotation} Package} \end{center} \begin{center} Author: Coen A. Bernaards \end{center} \section*{GPArotation Functions} In R, the functions in this package are made available with \begin{Scode} library("GPArotation") \end{Scode} The most complete reference for the software is: Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. Educational and Psychological Measurement. A mirror of the original repository that is referenced in the paper, with additional material is available here: \href{https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html} {https://optimizer.r-forge.r-project.org/GPArotation\_www/indexOriginal.html}. Rotations can be performed by providing an orthogonal matrix to the gradient projection function. Orthogonal matrix for rotation can be obtained by extracting an unrotated factor loadings matrix. A rotation is done by calling the rotation name directly, or by calling one of the wrapper functions \texttt{GPFRSorth} or \texttt{GPFRSoblq}, for orthogonal and oblique rotation, respectively. Under the hood, rotations are computed using the Gradient Projection Algorithm code, which can be called directly. The key functionality of the algorithm is included in the \texttt{GPForth} and \texttt{GPFoblq} functions for orthogonal and oblique rotation, respectively. Calling these functions directly works as it always has (the codes have not changed). The rotated loadings matrix is the pattern matrix. The structure matrix may be obtained using the \texttt{summary} command. \subsection*{GPArotation Functions with \texttt{factanal}} The {\em GPArotation} can be used in conjunction with the built-in R \texttt{factanal} function. It is recommended to rotate outside of \texttt{factanal}. \begin{Scode} data(ability.cov) z <- factanal(factors = 2, covmat = ability.cov, rotation = "none") # quartimax rotation GPFRSorth(loadings(z), method = "quartimax") quartimax(z$loadings) # oblimin rotation GPFRSoblq(z$loadings, method = "oblimin") oblimin(loadings(z)) \end{Scode} {\bf Important note}: \texttt{factanal} allows for calling a rotation directly from the \texttt{factanal} call. However, due to a \texttt{factanal} calculation error in the computation of the correlation matrix, the produced correlation matrix {\em may} be wrong for oblique rotation (orthogonal rotation are not affected). However, the correlation matrix \texttt{Phi} produced by {\em GPArotation} is the correct correlation matrix when performing rotation outside of \texttt{factanal}. \subsection*{Recovery of The Unrotated Loadings Matrix} Recovery of the unrotated loadings matrix is consistent with the definitions used in \cite{gpa.rotate} (page 678). For example, the unrotated matrix $A$ may be recovered as follows. \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none") y.quart <- quartimax(y$loadings) max( loadings(y.quart) %*% t(y.quart$Th) - loadings(y) ) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max( loadings(y.obli) %*% t(y.obli$Th) - loadings(y) ) # last equation on Page 678 max( loadings(y.obli) - loadings(y) %*% solve(t(y.obli$Th)) ) \end{Scode} By the same definitions logic, the factor correlation matrix is calculated as \cite{gpa.rotate} (page 695), \begin{Scode} y <- factanal(factors=3, covmat=ability.cov, rotation = "none", randomStarts=15) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max(abs(y.obli$Phi - t(y.obli$Th) %*% y.obli$Th)) \end{Scode} \subsection*{Random Starts} If multiple random starts are desired then the \texttt{randomStarts} option may be utilized. For example, 100 random starts of the oblique infomax rotation, \begin{Scode} data(Thurstone, package = "GPArotation") infomaxQ(box26, randomStarts = 100) # 100 random starts infomaxQ(box26, Tmat=Random.Start(3)) # a single random start infomaxQ(box26, randomStarts = 1) # also a single random start \end{Scode} The loadings that are output have the lowest complexity value \texttt{f}. While the lowest local minimum may be the global minimum solution, technically it can not be guaranteed that the lowest local minimum is in fact the global minimum. To further investigate the local minima it is recommended to use the {\em fungible} package using the \texttt{faMain} function. When in doubt, trying random initial rotation matrix is advised. For a detailed discussion, consult \cite{nguwall}. Additional algorithmic considerations are in \cite{gpa.rotate} (page 680). \subsection*{An Example of Target Rotation} \cite{fisfon} describe measuring self-reported extra-role behavior in samples of British and East German employees. They publish rotation matrices for two samples, and investigate the structural equivalence of the loadings matrices. Additional context is available in the manuscript. Structural equivalence includes target rotation, as well as calculation of a number of agreement coefficients. The table lists the varimax rotated loadings matrices. Performing target rotation of one loadings matrix to the other can help in interpreting assessing equivalence. \begin{tabular}{l c c c c} \hline & \multicolumn{2}{c}{Britain} & \multicolumn{2}{c}{East Germany} \\ & Factor 1& Factor 2 & Factor 1& Factor 2\\ \hline\hline I am always punctual.&.783&-.163& .778 &-.066\\ I do not take extra breaks.&.811&.202&.875&.081\\ I follow work rules and instructions &.724&.209&.751&.079\\ ~~~ with extreme care.& & & & \\ I never take long lunches or breaks.&.850&.064&.739&.092\\ I search for causes for something .&-.031&.592&.195&.574\\ ~~~ that did not function properly.& & & & \\ I often motivate others to express &-.028&.723&-.030&.807\\\ ~~~ their ideas and opinions.& & & & \\ During the last year I changed &.388&.434&-.135&.717\\ ~~~ something. in my work.& & & & \\ I encourage others to speak up at meetings.&.141&.808&.125& .738\\ I continuously try to submit suggestions&.215&.709& .060&.691\\ ~~~ to improve my work.& & & & \\ \hline \end{tabular} \\ The varimax rotations for each of the samples may be expected to be similar because the two loadings matrices are from different samples measuring the same constructs. Below are target rotation of the East German loadings matrix towards the Britain one, followed by calculation of agreement coefficients. \cite{fisfon} note that coefficients generally should be ``beyond the commonly accepted value of 0.90.'' \begin{Scode} origdigits <- options("digits") options(digits = 2) trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) # orthogonal rotation of trGermany towards trBritain trx <- targetT(trGermany, Target = trBritain) # Factor loadings after target rotation trx # Differences between loadings matrices after rotation y <- trx$loadings - trBritain print(y, digits = 1) # Square Root of the mean squared difference per item sqrt(apply((y^2), 1, mean)) # Square Root of the mean squared difference per factor sqrt(apply((y^2), 2, mean)) # Identity coefficient per factor after rotation 2 * colSums(trx$loadings*trBritain)/( colSums(trx$loadings^2)+colSums(trBritain^2)) # Additivity coefficient per factor after rotation diag(2 * cov(trx$loadings, trBritain) ) / diag(var(trx$loadings)+var(trBritain)) # Proportionality coefficient per factor after rotation colSums(trBritain * trx$loadings)/sqrt(colSums(trBritain^2)*colSums(trx$loadings^2)) # Correlation for each factor per factor after rotation diag(cor(trBritain, trx$loadings)) options(digits = origdigits$digits) \end{Scode} \subsection*{An Example of Partially Specified Target Rotation } \cite{browne} reported an initial loadings matrix and a partially specified target to rotated towards. In {\em GPArotation} the partially specified target matrix is of the same dimension as the initial matrix \texttt{A}, and with \texttt{NA} in the matrix entries that are not pre-specified. Both procedures target rotation and partially specified target rotation can be used to reproduce \cite{browne} results. In this orthogonal rotation example, \texttt{targetT} includes a \texttt{Target} matrix with \texttt{NA} in entries not used in target rotation. With \texttt{pst} no missing values are present in the \texttt{Target} matrix, and the weight matrix \texttt{W} includes weight 0 for entries not used, and 1 for entries included in the rotation. \begin{Scode} A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) # using targetT SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) xt <- targetT(A, Target=SPA) # using pstT SPApst <- matrix(c(rep(0, 6), .7,.0,.7, rep(0,3), rep(0, 7), 0, 0, 0, 0, rep(0, 4)), ncol=3) SPAW <- matrix(c(rep(0, 6), rep(1, 6), rep(0, 7), 1, 1, 0, 1, rep(0, 4)), ncol=3) xpst <- pstT(A, Target = SPApst, W = SPAW) max(abs(loadings(xt)- loadings(xpst))) \end{Scode} Note that convergence tables are identical for both methods. Additional examples are available in the help pages of \texttt{GPFoblq} and \texttt{rotations}. \begin{thebibliography}{} \bibitem[\protect\citeauthoryear{Bernaards \& Jennrich}{Bernaards \& Jennrich}{2005}]{gpa.rotate} Bernaards, C. A., \& Jennrich, R. I. (2005). \newblock Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \newblock{\em Educational and Psychological Measurement}, 65(5), 676--696. \newblock\href{https://doi.org/10.1177/0013164404272507}{https://doi.org/10.1177/0013164404272507} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Browne}{1972}]{browne} Browne, M.W. (1972). \newblock Orthogonal rotation to a partially specified target. \newblock\textit{British Journal of Mathematical and Statistical Psychology}, 25(1), 115--120. \newblock\href{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x}{https://doi.org/10.1111/j.2044-8317.1972.tb00482.x} \bibitem[\protect\citeauthoryear{Fischer \& Fontaine}{Fischer \& Fontaine}{2010}]{fisfon} Fischer, R., \& Fontaine, J. (2010). \newblock Methods for investigating structural equivalence. \newblock In D. Matsumoto, \& F. van de Vijver (Eds.), {\em Cross-Cultural Research Methods in Psychology} (179--215). \newblock Cambridge University press. \newblock \href{https://doi.org/10.1017/CBO9780511779381.010}{https://doi.org/10.1017/CBO9780511779381.010} \bibitem[\protect\citeauthoryear{Nguyen \& Waller}{Nguyen \& Waller}{2022}]{nguwall} Nguyen, H. V., \& Waller, N. G. (2022). \newblock Local minima and factor rotations in exploratory factor analysis. \newblock\textit{Psychological Methods}. Advance online publication. \newblock\href{https://doi.org/10.1037/met0000467}{https://doi.org/10.1037/met0000467} \end{thebibliography} \end{document} GPArotation/inst/doc/GPAguide.R0000644000176200001440000001023014767556437015757 0ustar liggesusers### R code from vignette source 'GPAguide.Stex' ################################################### ### code chunk number 1: GPAguide.Stex:21-22 ################################################### options(continue=" ") ################################################### ### code chunk number 2: GPAguide.Stex:35-36 ################################################### library("GPArotation") ################################################### ### code chunk number 3: GPAguide.Stex:67-75 ################################################### data(ability.cov) z <- factanal(factors = 2, covmat = ability.cov, rotation = "none") # quartimax rotation GPFRSorth(loadings(z), method = "quartimax") quartimax(z$loadings) # oblimin rotation GPFRSoblq(z$loadings, method = "oblimin") oblimin(loadings(z)) ################################################### ### code chunk number 4: GPAguide.Stex:88-95 ################################################### y <- factanal(factors=3, covmat=ability.cov, rotation = "none") y.quart <- quartimax(y$loadings) max( loadings(y.quart) %*% t(y.quart$Th) - loadings(y) ) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max( loadings(y.obli) %*% t(y.obli$Th) - loadings(y) ) # last equation on Page 678 max( loadings(y.obli) - loadings(y) %*% solve(t(y.obli$Th)) ) ################################################### ### code chunk number 5: GPAguide.Stex:98-101 ################################################### y <- factanal(factors=3, covmat=ability.cov, rotation = "none", randomStarts=15) y.obli <- oblimin(y$loadings, normalize=TRUE, randomStarts=15) max(abs(y.obli$Phi - t(y.obli$Th) %*% y.obli$Th)) ################################################### ### code chunk number 6: GPAguide.Stex:111-115 ################################################### data(Thurstone, package = "GPArotation") infomaxQ(box26, randomStarts = 100) # 100 random starts infomaxQ(box26, Tmat=Random.Start(3)) # a single random start infomaxQ(box26, randomStarts = 1) # also a single random start ################################################### ### code chunk number 7: GPAguide.Stex:162-188 ################################################### origdigits <- options("digits") options(digits = 2) trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) # orthogonal rotation of trGermany towards trBritain trx <- targetT(trGermany, Target = trBritain) # Factor loadings after target rotation trx # Differences between loadings matrices after rotation y <- trx$loadings - trBritain print(y, digits = 1) # Square Root of the mean squared difference per item sqrt(apply((y^2), 1, mean)) # Square Root of the mean squared difference per factor sqrt(apply((y^2), 2, mean)) # Identity coefficient per factor after rotation 2 * colSums(trx$loadings*trBritain)/( colSums(trx$loadings^2)+colSums(trBritain^2)) # Additivity coefficient per factor after rotation diag(2 * cov(trx$loadings, trBritain) ) / diag(var(trx$loadings)+var(trBritain)) # Proportionality coefficient per factor after rotation colSums(trBritain * trx$loadings)/sqrt(colSums(trBritain^2)*colSums(trx$loadings^2)) # Correlation for each factor per factor after rotation diag(cor(trBritain, trx$loadings)) options(digits = origdigits$digits) ################################################### ### code chunk number 8: GPAguide.Stex:201-215 ################################################### A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) # using targetT SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) xt <- targetT(A, Target=SPA) # using pstT SPApst <- matrix(c(rep(0, 6), .7,.0,.7, rep(0,3), rep(0, 7), 0, 0, 0, 0, rep(0, 4)), ncol=3) SPAW <- matrix(c(rep(0, 6), rep(1, 6), rep(0, 7), 1, 1, 0, 1, rep(0, 4)), ncol=3) xpst <- pstT(A, Target = SPApst, W = SPAW) max(abs(loadings(xt)- loadings(xpst))) GPArotation/inst/doc/GPAguide.pdf0000644000176200001440000061540314767556437016344 0ustar liggesusers%PDF-1.5 % 8 0 obj << /Length 2224 /Filter /FlateDecode >> stream xڕXYoF~_!x@61, 8OIEbIY!EbWݻkbREbu[8aҫ(XF|[oeѬ%FFqpc&Ŷ/ۆTٶo o~̓2qJ,Jj#5K"M|G*t\h&57f]0wHV)K#;OeD8իCo͇F)| EhP&L-iZ2JQHJ>:f4at֡ A5ONF~ .;≯:=i-i +rcrOJ5kBUyo2s=ᅽ42h|Lp^\bu$m[?VEhnC!W_]FڊeWExL%G,Y1qݲO aM7TyԀ8kWCkuGkш]P=s_mߎ͏ 0eF{^9(Я:ueǬ\J*!( Mw"UPn-`ڙqpEԥ1h`lwD##p)m] ڞ\^}Y_ nGȢE|xY }]-ޟ&OqpOiK]DG}OntC˼lc|%y>b @ZM"&(ӓh<wk=O. 0:}5n'z.!@ߓ +޿U˺!] `lh1SKmSp) V.<%4Yyi 1 9g&ThkAGј=RfݼI1J%UDB!Y! ,S1 A{WGSaknǸq__,zoM9Okz9D˘EϽkx%]Iȩo'P=2:ttqP^! u^ߺ wq0 .!&)VTG,kCRǽk'Z h("Qq\bf[r:J%vY'+8 Ch,1a2La)gq^ he=,!ܚIxGb X,t %RG8Lwi7m՚ϋ /iBު( M;VێdCc_^i{) C¶+_N Rz{zh)'Dw9l)թ:`)(_@1zȨݡA\1<rH (bN^A4hȖqHtljF&XD# F4PCCa0.g A&Oxaaoc cYk伸$,rpMlam.Jm?> r|6G17c]4BVoLE> m',]lkMA@I-ń+x+xiwj&-,+& "ȍl̳>ĐݗP Nl-0W-t (:{߶O5Z(5>9o(r uD% p˯xӮ.^3.~KGlWa 6եs΢]т]P)_Cecx0iB.}~!TC1 xVLqU|X @>П*;merMH&R30EXqogښ诔T3 JHSCJozC F$4)Km$:Ql{?j?IRЊj'̶ CukL1G2YۘRᡂqE1'%}R@Q:[3bxAѴO>&%}Es' ߎPJ79x(N<L+8M|yQRФ˥)PdsKC#2=[sk~/(80q̰GrCݼ}#wJ}ZY}_P%&PU߹Dǻw` endstream endobj 36 0 obj << /Length 2021 /Filter /FlateDecode >> stream xێ6=_1h@^ĪnHb[>l@KMT=%.uxH~{ջqX17UQ|yXUͮwA&&M7۴`p$WZ4GZ}ϵwv}.)oʓF7۸ Ӳx%% jenY&y0,XAG, ,-QZH^ԧtkf[V"{\5KTΨִN5rCi("P_g~DiE[feYnShK܊WtoXvIQF+w(=Qod^1ݥ|Sm1$fi1&UY6dˆ7-Kr[!5y:<6}Epo^R߇5.v,zRd-uyET=ɹ|E-{ĀrpŤr$EXVF\H %'qt7S%F(G  >V@#FI =1IEیp*\W-"d2l(=-{~Zx=枏&y[:17Јyb@1f.`IxD.%6.p1*vI"׿UƷ_;&O5`+ُH~?% >}05,2޲vz@1L,ζpam'MO q8Sw=MI ΄D*{ny?1}%gE5Z'FerPjZMׂ,\Vaar=`8$[԰r=Lj P%UΦh C4+ޜG,fDžaHaKD1avԄ*=,z 4X|kz&JN a  V"w!|Rf(0ߞ_* {7ILA'`NaSppL[yN>Ϯ}ae@*+J| "Py+ b~C(X´ hCIz$뽩B3=8D1eAcC~ak,2~ : C6Ȋe\'#Qj`io.rĮD,lI+W Fhc=#eEZ1ćYpMlt%A3R2I쳛U :\?yeiX#Dʾn+mĻXf^tX5NDZOoܵp#;*!:jjX0kiKW 5^f/WIKyT43ߙepfBTv& "KXQ@y l ._&awv~fvQg%4gw8e q@tmmH=/O) endstream endobj 48 0 obj << /Length 1916 /Filter /FlateDecode >> stream xWYo6~@=E22x1y>h-$)jl!ڲ,ţX_}u"DY'ͼȫag/ ΋4$ixwZxeXf4v{/NhHˊޯ;(mRqˎZw?{[Y{[B2MMRB'-QDNOfyAS r&)3H]%I sJ,#aQc!Gq0k'~fK f̍og".ԑKkΫ'fqis|đQnl=7Ԧ쒝Lgܰ<('.K4cH>X*jAK^@('8c?NAaNA7L^'I7OxUdkx`}lX'l8W Jh 7u'`ǬrkaV @a *$Ji\fѪV ׈ V#`sN`J63f3Z3(78Kn(fgC6 6qj 8jo5;/^V)Q_ʠogSk'M=tn(IE;6</|p3v: zw/ m`.:w.aQo('[#sް_T{{f`$CLw[v&[۩׭Y7)/0e}ҙh_6⋺j{ŊiquϹt S =}> /ߗ*/iITQ>q)夎cunx7ꢊnEuA#pfFd\[,+i^?Im;GhY,/GۨaךEdj&P^5w%~`EVԻU#8vI3i,,aC#ڑ? #;C]7tﱨv&K˂5nvԿ^[4: q'(Yۼ Qȿ֞x# 4څ> .`hfq% @zlLVGxɐ d!kLӅuP5H)\=eX[$v %z endstream endobj 60 0 obj << /Length 2344 /Filter /FlateDecode >> stream xڝX[s۸~ϯL;jFqi:cIL$Zef$RKR"-jD\sp.}s^eVڳۇL3Ze0:OHԻn\]:yfrMЉo?L#~/B9{ʷMA73%|y;Xp2 뢣ˑ[b@ |2::g2MWpT&ADgߏ4 $uq8W-yΦWenͻ|q}vp He3?_ެZzQz(AHH>̜K.f`ffDrZy&eZq]Rn(v@. 4[NpPE al tq6߆֪UlnV-{r qIT18%)2g qbsÙT".Vh$eEy!uWmah#ef4r#c+6[XcDeM)~jgca`laA JQJ&y=`[qO7:{ܩ-1x'ʂHe^jvߒ:U lz džYi^1ncpĦGB1SKN] f4B9fL̮RXj ]tl[Y E{$0Zh4г vކ ^.ip(\/ط{_"fC)3:Ox0ûAb诐;'A[i.>\x N!|5IFC\ 1iF}JC7?f$i: KRbqiObPks={3 aI`RG+Zp؍L}t\ʨH@ cNGΙmR?*/%y8нjYoުB7!XC?ђ}[–x7/3`ů7#;ވs}X,'м\h~oߏk+ IAM}ܝ[PO#wRFy; `Kt?t~Z`c7j})qwaJHW%WF^cQ&㻗Swr{}Ҥ X^SߎĂ+b*Uń74>:B*Twb O$4- } '(V0GX"wyܴzSόL%nhs{X: @BdENZ@5b5|@H**,ZI$"|]|Un0v.Νs.Zs-4љQwq()@쎡ݨ!HAhi4b ;)c) K~ق1N&}LP P(h|c@<;GoGir}FW, Wض$}@afH[0„x(B2E<ރe&)Ĝ7*ߢMZ e/w4:pOK\5J q\ l$JW%?3? hؿ|@!| 8)OZ%۲*9+LBiȞpN"dAs!8_Hq%.q6e|7ą=9dr~Rgb KYR`3oݾX endstream endobj 77 0 obj << /Length1 1393 /Length2 6081 /Length3 0 /Length 7030 /Filter /FlateDecode >> stream xڍwT6ҡ"2* !0CwwJ0 003tH7ҝ]" **H79=Yk{_}g=z cA<@1*y@>RVV} Bp@ȣ` V`8@ @b@ @n0&@ IYHODEr:CQ0c،@A1.a xyyhNBPe5RV= ۠ŸQPV@h+`T5H(7X7@<3  `' n9AJ<#nvB#`70 l* P A4Gޟa׬G8;C4`(({Gýlap۟mظ"y 0W VEo.Ğg}O$S@lm@}aP7`PP_4S"60` IG<@,@ϿNX N1c=Y%m?-('xsP@ =3c0O E~Y/ lXBfP~|(+"%W'_v߀v9yA`& A&VU p;,A<@zZ y @94# eGco]9̯Aإgp (ؓ;|$a^8u`{"P?-$"GX+ ؜˿6 BH?N# u5 ܫ#i|#Ę^q˥2= % N-9{+ JvqW&O<7J+VyEE"mc׫JC;uZHVGWMi ϺVU}ڇc"R2Kt&WS,R0 Ps \a P*0*U<-qʠV%e[r\ke~=K*6넉9K-+Xƌ/x6N93=xj:H3Abo#7Pܶ5)+\4YA},Nٳ!F|"iRųmƔ7`iU7yz{ \utȱ͸TGcg}2 nxjF{;/T{,k;#|+ڝ-|0 i_i'nn NV^~>D;?[i@n@EPxP~/GW?ReMӭ\4m+TK!0Qͤmr{_[؟Hn]o͍☨evT2Gŗ}~;,ɛ̦WW;r }CDMF4egYn (9 Ymv[ݣ^VNH(ʎ5I*Ȯ&a'Ⱥ5K caSb㵻I6Ĕ 萴Sg#ylGz+͞N9$/{|v>$ep2[-v2Y@e9q|RⓍ`<͢3B-GccYD['dx ɆB,7oP͞f('5jD"β#:<m߫ԟ2+v:#[YdQ9x& s3U4k//"((hԍD=svZk6qt5 GB^)K*(FuD-Dn(^2|ӶIbAc<39E2wOPGSٓ4 wyϦrf_ ~W1E:h-fcF^Dotl |ʃ'hW)*q"22 \ȸ/b-r4l 9# W!9*4a?'-F w}cGұekN_cKUQs2󖅲w=i"~Ģ %qT0ūA TD&^8z}uB] GVV+bdwp_]r 1}k[[J\}͂spƄpJaUje9瓀WEC;ul|R7M鼤 K$0ht >oxS!5aNg%43OӸela`A.!o`Q,KPRqX3_OPY]e:Y=]ȧn|~1&[śCʒ=693[a$"2h;W<[GO;T/+Q϶DmPPBr=%^[fHe-֖jAfd>٧7]}>Ӆ"P6w_G'lGn־n5"8DN9 1S4\{OINBA}8gP96\47SqKP1:΋2pm8}%k_B "P{x:h?ɶ.};(Ppk*9{YF4^LF#j!JYx(jH"⥾NW|IR_=\Ĕ0UfnĽ3z,\SnKXD3HU@gOW}<(kG m"CmnMYV &NP|S=jL!HaF3 jɃ=yWnQ-81iCLktܨH4BS8/Zi*D\n.OF~+Ժi0m5sq~)HP@U "+MlV$"ab͢kU{E"s@?Fwo|stUn_nԤN%@\/<[ to$ o:C}ڍOetv{[ Ù;#B^;L@f)q`b-)i"P*=N~<v.{U(/ZV3'|liH2m}{{:I[ 5=ͧ UWOLM g vʉo#!}|mQT=(_IF*uf(Bs+%U6"JvJ2؞O؈ 㙱/}@ܦB8 i{JVt}9e;HoS}RI\*]ȹ_ny$}}^TE1slf{AيS>0X^K@ݬinwMZ]Q%s 2zy>jdW*f Ծ $8R 3/ 'I˺gN&OMs\I+!>\x̍[#"wa'YHsiùKW ZxVf8- &:Yھud1a"cpщ .V)1$)ڹuEmQ37>[J^%R\pVk>7#[ $a܀els!m0e/ /cpM{*$}$r6*|`JqTZ$솁>>+nl}}Y5OWh>Ov xc1cdd{\4M*: 7kOS6gA6FHUOqKdYnʣԾ5&߽ գ0Ά )FvQ/]F$yk;@J?/2ix&X,pȺV/ =z`")PZar2BI \kZқ뇧=Lsf:$uX yS5Yw D?xA2%%n)}dQ2ד\څMbN}raq:߈0nb}bv\:1!zF2:5o4EKTƓ3s2*rv?| (-P|x6MF+4a{jyFJ:N T?Dº 5Ϗ`􍴭Bwx=X<> stream xmxcf]uFm۶m۶mۮ.۶evz޹37nO2ryv9$v& 4 %QAuzzz=-4 '@db``03@=-̝F6U;k;W #s+ /$%  $'!!+ Uؚ8X] -F&N&S;Gſ09/0SG;2@DNY``k n7_Q+YW2_%Zh3_IؚXm6v/ 9v51 ػ88dMmvwg[cDXX{'B/fIX/ǮfWg-{vSo3V535(9e@LW1pvphkHG(AA;w/FF + 8::C^3N&&&F+vF\i-"EsTs3*T+ =ZHCzXwXn<~K5ęONE`Un)6^bɂ]G:glSN1gJ: 1Dw#į->؊Q!gj~S Ι^쭃^`Vi4\Z i -j1 9WҲMUV?w3mXquxo!V*$ ?iklWAtd⛘.G}\8 [WRQ;לK([TfJ̶-{8^j\@7ѭ|:DD P\f"Ń!_Ã05ݷ+,bQi*C ij{mX n["cF&7 jm!4.kܾy_|{.?V999[WRٜ'?4AS%0_\8Ŧ:2;fEPka]E F\O+!.0ʞ귡 J-\>l{G+zE3c{fM# wj)+{ mnJKG[qKo&#?MN']Jn;>B,^)# |0R@B:jܴ1䞘!kRiFv'&ldq{4t;Ha93տS(JdHaRJ󱻋 vŊϺ&'~ 7G*5йD(uUqj%Ձ~dfRmt3HN40T&= >pkM=-Dv8SsS\dHl⾓acZN[\)D^|럝/R/o|J`2R2]n7ƯobeD G_y9|Z:l7;\ w$▣/{'Esj#L`Ng]Qt WV&0 j]OY95QH8Vl 4N6  +%4w!*'445H7#~I&t ݂+P-j4'_2xm[ƁwFh 9]` Ҥ J1b= GRsi_IiAnUޟhnȿm^p޽߾O٭N5/o%~uҬ`8Tӹ7WϺEg9HYs{D3)2'tZ26+f3hjD9ISg_Hg Œ(P T (,_:wI2>Q +OJZXW%`ب>.>Jkk: νjLiG>l9+)?:KsecW,K+$41˯,dG Ôש<\)q 3*nc8q'q$x/Wb}\y@ۈdϪ ?]^Lw7io'U\K_%­&_LivmF(=B?U]h;sE8ܴn쇟 ?y bu/(A?:S@S;x,>Nמ*([IeOaZ=>e tb @' Ij9Րq`[X?һ<#0`sKyneb<(}(aX a,{_Yv/8CJ _掲ML 8i{|:_>i[ӂ>=oM1goa27ZN$N:?ْ;wڦ8G5Ôot`Y^7^W}תĶشЏ/ĕKƮM|ҳhq}f^=ҿUP?9RB4%^SPMZw V1O ޅPJ5Rv=bVVxTR e/{Oڮu$𑿰LnX<{g``rQ8'NF94!T0[K\'I!YfgJ0mHܮ45O 7Koa?ݨ"R@ZvבD5M>1 8~Mx!p9U}QS]ŧH[٩ڕmw=r, (j5LGt%Yd Ke¨IAJ6 0 'F`P@5>uI3R>Ifsx3E"= zmJ|FܩKRNaH\.t̂fQJen'+JlSM_D7cJ^p )BdWG)h6p] 2МM4t!"ԡi8"q\ T˛5Rd0Xd\ H~4ZI!^ܤ8\MxtBH(wp.ۛd[sŃ|Ak! A9Ū'߷t)#тUzݘ"1< ݒGߕGVk)EOua_ V@'p4Kv]te~6&]Uj8uS`2Ѫ6KY9{y\8uV,jm(uk^Vʋ( *fndVUwN*jWLRtH ALMs\\+8t㽈NFP8qmcRߟ}ŀab1Tj99DK @ۀ\JDn` ~?$E=Oo&L³y%Hhi/fMK сHX_O@ˌ9Ul>!>PJKs8Es5,^l/26^c H$^ {j-Ը'a !:À}aJGL_dA`e^ɠz!N} %[YUmPY1TkC]FO#tcbZ،I+|?P=ER-3r/Y:NQ_ ND_E+fو;F-#Xp%.QWyh?*&|=ky-χ|0󛫰Asovq۲H)ayP5?ЫSvy>-J>T7) ܽ ղ=A˄)-lze?"WӴgrÿo|;F9Go57[W39Bď9B\qaGJI'LU3)[?l㗎%ե E# V͖'$ #Pܐkًh0/G^K8oRV;6ҵ+ OӸ>&d=1|)tDZew\eA%ձ_tsHZޅ|r\i>L;M0RMFig9sCPړg(<ehPP!zjK_η^9ܿ,D5OyxcϮ#ܼ"B485s9+'N"߱`8//: x`SPi֏n#fMXQS1+rc %bsu3&cwXw(g<-doGԉ&΄hZQQ(7W UiT% , c=^ ELjQ4Qt\ eD~|HTB;M/̫Gk 6upw6A1Ԗ;%[S/QÙ0oSxNcg1(7pq*Xuh h):*-ͧ4ޓl~S~kE,NNG~!o ޜBhg96!Ѝ-P9\7B)8W@t*V65YRLUt$ kCf )h>߽zwz kt z @(8wJBl51li֏R E$'Ǚ~hucGwWVYT]$Ң[ C~PA3J.UVO|4d6Q2_AWOJT'yP{ ƯcC v<{oLV0>q)"ӭ4Dܪ~ַ G7NMg[v6xI Y [͒G DDq0LI =.m Y!vQduP2;zl0UH+\~XHP: "SvE}pK (qn$9\j4s`J&\(vk3)ץi1HA/?Eo䂊3Z*@+uH1<H909ҙM2ֽF mR@{>u.`{2&,݀91 ]5|G$QP46[vƀ2!O'ğA3\ano5?\A_=GBg w⚌ud%Y-IG0vF6>򷑑7>GSٿ1郚 2ʧQLV_.És'A%<Л X(! ϻQl?6]&:fr)%J$wsv/ed AW#ށ mD ILXU-#hxVm|A<<Ч ~kJ)Sq FV(m>~tUh%=2MK }*@6Ӟh ˺S:5]kUJ{cȢTu-mtZ N% kO`$1`C8Ppۡ5AkUԌ}ʸZcڑ1N4ڬSGZ]howЈ'U>d$7mHw&ȧ4=IMOi ,TSO_0퍴0jvZy;~V@b<ZXYM08 bqgy,:1r*dء騂.xq Jj[JWF5s^5i*&2xQ?fl; X̀h2Eg6XBYyɮ.K!ь,X@{V2bvW|2UzsaC.*LO}4ژ 2lSWj֟Ҟ}0.< ! R 52FV2O.ޔ.+*\Є}*Z>@ʞ`"Œh' J!ܝY! ?ci##U7w+<%}6%OAp%@{OZVE57t~+qh dF`1YBRȊklwDq-O\].o8>ӷ䍊Lѣ9P'bdҷ,^:DS!`2 Ƹu67)N.T| ҷ\ID{݀}/(@-~܏9ab +ӕ'cA#W{Fc*6CqK[D5>?Voiր?L(roXo cӭu)ņ=ʮYz6umX2o$Ԯ o ˙y[1]̕U"@_R*J AsfXN(zJHM#coۧp+X}_Eu'Ncke^PojbJHLxzij=mA]̷_1tlڞöȞ~.qMLcd]q>WHG![s.KƤ0>z كSv5JfhL0 r 6 I8a3鏢M:cgG V.,a}o?*3jd/ޗSđ&,&`)n ]:&ܗXnPg~MB#>؞ҖQ:36w'hety<#LpOt""d+IcעNҙn8!j[ D AuF, O8G . }jb@Tu;YzKE)Ek:nBVa*Pn6 HxNmU7qc% 7#* uuQ`q(,$Q u*^Wպ:GAp*mwдWz>jMN>IӢkX@74 {4nZ[ 89ĐH]hE rclnR];Ms0龭3N10ȝnb"$ώ/s+ vxjfEZ^.lƺC%&2|#Z @l:aS~]Cq5cl!&B o_|EPߊdTs>MYZcMyhSDŽ>l?bR[8_}xR󾨶>E_dBfeGCF*n6APU$Ne:\ZØX]΋d]ueKR$+LSGAJ=ߜaj ?=3|= oJ0n}bO)~_YR"D_hobV``4{g!u_ +chS!NA?yTMIʌ\]5,Z%(jǨUZ}@zӑ'S;X7 }}USz pմnᐓ%~祌*9'L'ȕ5#:5c JqP`}r bWAኸI%;W/Egvnog6Lc1Y08T K]QZcG&ղ/OX!wqs;ɀ(Q:kbC1Ce*3Pgo'('6,ij=IxdNءW[ tޖf#wKIxuE9Ue<K#S~@SW%b他L}3W~鏄!.谶Fa,֝®?.N4t̑oV+2@GG쬀<jۂv sXi2xKTdҔʶLRY&:*Xzv6E( a͍Zv V?C1DA00{Uc7hmK=G)#i]'7@K:\G|7d<tR%1bޭIH?4V6RPڈW=̢u ZEQvQօ5?A> $0mт#b^6ZsٲGJ^ǁQ.z*䄙DƦ/ sr 'U[y*~*PYEe&ctm1?VÖi7}Nhˤ2S)xvpRѵ&aOV:di IH1v]$~{| 7JRxįIWQ#0 hW<{H}7˦Q1_CxFFȠgGkQ~g~A)۬G!{Yv:r. lV{(؃jׅ z(8?9'6B;-"tZ '@u 3iCa*+nBmv^<%ޒ*wPw `ď]y^ Uwٛtb T_P)pH_KjT>i4y=mmujrXn_)JqǣJS@]O irveȇHFk}E o  aK_hCA 6VN!#kL^㌹z/:%?TAl1^'NCPĐ|Pu_ң1/ZǩoM/͟}ybob^L`;:u1,-_1*k^$H% m%/cŵG-Sf3V;&SQ#; - )g*Z$;S 7y *.]Ҏ`+N_0ἇ3nH Avrn3.E;1N:Mߕ ]gYn=&ಕS)I`V#|dW$9/BXr 8͢ p޷th0*f-. )/%miRN$?Zr|,[MDč#cqO2Z]Js[,j,{=V{gOA& G9l~d񱬂;A\z>گWjt p*Gc_T1fCTZ7pl}4PO36s8iZBtrjYxDE g;O|m/`ngץC~QgA|@:,$:tr_3XzfOZ,Oл_pOGG v⟟0v_JZ BZV{U 6n%$}f8,Kf̿,<7''rϦ2JVeB?J~M z]F _,\Nۭ`i7w?80{|D}]9­94(aIphBzaGl)i=JJVJ:7aqGTcrpmMn]OzY||V ˊsWaPT;@vIfE-+N~t=Tё.UT̛ݡ^s.x\e9(, [5b-b^W5?zS-UD1c0rKjD=҇ߦ}S:Me Nђ&oب|LK< endstream endobj 81 0 obj << /Length1 727 /Length2 17220 /Length3 0 /Length 17808 /Filter /FlateDecode >> stream xlspom-/m۶m۶mĶNl۶s{gܺU_Wk?j۹x:221pń4 t0N.v".\uS  ldin0wfchbikPw4𸹹 99Slj p0Yژ4%rqS;S'C1@ٔ`f03'gs3ur-@VTEPL^N *L" 03HSn_\g2,:FF _Iڙ6quoBP!%)B kobd1zڙmhki?I3GA,,=LM,]W7i\Ml`]Jn[Hn۸b=7cuQL]0Ota^VC̻t=oJtY\tE 7GF#"w9":mHB3 FHtRCq`vuGmpT&M9r6۠[ij@V,!Bݎ<,Wٜ (Oz reQ,YEsoވTsuCȩrϖc+ +P‰ zPNQ*rQJr]ʛxՁ ٞ ?EbPAp&bGNܼ|G c>YMoIXԥoeZ99#݁B27fNg݊5}$ :Ǧ&ZgL0^" ИT1>](\t[%vd;X D4]SMX_o"bH }]瓐X Kycu2K,eDV=,UܣJ ﰙ i9Uay~-|InEe.NYw!o!4]D?@b\"Tq} KUDz78۝g^եTJ,k/ؖfZi$Rׂezǃ}9ӡ{(d#%y2i5K``{O4l $d{ùǹ4v"e}|N3[oDJF8ipbt3TlB=0¦ֶO>|'P*W,~zptaYϾmLhv ,/&ց6ΜtJA&,W ~6j3]Qg<):C }E(0<>t8ǮZŦ>L8m)O6%GrDzFxHK:s]s7@}NuY9.a=VЌyC?59Ѵ n$| ~E{ꃀ_8kq#LsL=az6X۵Gp7AhFsLUTqhsF %s\H9j`}'H~5{]fJioc١tt}4=;m) ~ >[AH;{iލihy[RxZNiDNiqm\wse~~EQ͝@irT2 (.(])4| `EpRDi)$Z u^ҙE )+ pѯ'O ^2#&gg >Å*'p{O,~bY#8>E. gՊ( P_A#G)De4!yܥLX i co8¤:5H\4M ?kOWC0[-Sg.es]sd7Hj*F$ԌWt?P>U^ 7湻u-cBaT J\U7RcC^D0kԭJ`1zv_S376,0%JIHG` \ʰ4U 5$\odN>0Uc6rH s贆z3?mp$/٨"xnưK Z-jBf c'd`q#׼nD'7Džv5uŒM@QsslhMAU&~qZъ2;nt ln9!rk]H.M%b2qFqK*GhHjYڐhS l-\v~Hݴ1QqyBdJ ˙Y lM8җo' +=[gFY7hk9OӬo8ϦQITZONqX%XΪ$ 8zeH&T E~4KD83F%oʧڑg ,Mx݌/z}2m,:gU++n& 7+bgN!3)q@%P^UUߎ"l]iJZΜ<'dTY/lV뱺:hsJX,Fw0=˿t~#tr#$owbDōF]vtpkH*RG ۿ.Zz >BZ0番!p=}~b\93 -/D@crUcsK6)kbTn]xW sz8i);hvNHi^޻.yV7GmקJlS#A ?cί^]cas ONi>Gg4=^1mdAb%N5jHO ?$=p$ jTMy1[Tmƍi !6.qI 1hu )*sT/ݰ#Y-\rtd8 OQq _*wpϩg_ #)eu'I:s1r$qx:\~ݳԄUH`K9l>X,qn -ٵ}@t'Dh4r_5Y  <P?}Pm'bP3Tf6ng"x^7ձ1v2YrW{Pt\O2^ %8$,wa1|QM(yi y vpI^wx_U0,XR#O4{c9=Sqe7 ɡSR}L :J^t! 0F *ObbyS蠟?svCEDҬ"D[mh|3_3^d r9pVWJp9]DN _^rZ Ct-箾:)5_N 5>K&뻑6 Zk`AQ<})k=t:ƛ}+MNι;GP_ klklcb絅ws;}^^Kٔۋ}%Nx 1j]~[+޾d?p߽0xߢQKWNrɠI2Vzh>3a 4'FU`.-A+\0D#zX9-d\\U_~s67K@>SI>,Y\ʴz M4A ӬHr Im\%S55; 9!\%9Woz\ N<}b,~0˭_47sBSPݷS3_56"pxnG3!x^V(W&>觗}kaO"d)O)K0%Gȗg ॷB#Y}~wpSx(J@?6jkOoqR]߸(9N޽ 'iL|- ~-xL񫆮~}Щc-זL$ #6}!s~, y~$>Zk!C{.;AgKu| ցrVkؕH{Z\͜°uPq.ϳTQRd5zYel\7QRۤyE+d_l7ɓ!Sa]Y{-$l/= DR@Yu cyzܶH,ew9׽7݉v|uRO=,h{.qL@՗ų3Mp*՞>@i\͑iaܠW)g^q9C9mV`g&0kK 1¢)*8=soAn;ibY߱mN3Me) RBY~k9Y]'4t&T?\{{m*>\}_2;~efAvhzU0$Hrv]u``+˱ ^Ag %9&c)MY,QSXk.@tQw (9h(&(gBJb"m01f]W3>kKr6OKWҌlHC眻29 yQ.H(BT%͋R[@hFp9q!a+}efkb۸/=-}p_ '׍B@OA3liޗ%GepM}U@r/U!\D^iq&S %ӭb!8QdnmAhU?1c좀$W[kf~%--l7Ic;Kv]/GUkQ8*c^^݄oQPuKm*h`"`w|ǻ=Cxݳ[\y)7sG~woc2V-3=6( imv@c]w AJrKs {ab,0r&tL2|?*2ط_^_.+pF1U13Dg5("\Υ ڤ7SïZSm4ڹElޚ·ca `jZd?&(~=ղHo7%BK>ٵM!'"VPYwxoh\zdt Kb"A/(A>q (mן7 !y]JHԫ/kfE۪4t{GZMQV>k.`WR" MEpoP!׆/hEW+ouOMSW㠌wLO0u؃ w G:HJVX#MDhU >Iز;)CRot2UƉE 3ʇ꿷!+%djӉQN[Q *6(| gmYplD8=9 +T"ꓽJhhAփ6-UXt2Q}Oyx K'kUp4';Djrsq$@YV 'RC/qW{pxč0 ]qމaNp8bΌV8"X5||[x_]Yj36ߵ d4;of-DA (҂Ft $g(^-Q3rJ6TlGT MjM ]m j7`;EzW nG<ɽz/givLתEeT։1"j3(̖Y$R5 j-Nu|o0;@VѧGbۤ=$7yM 2b&"ըoOך ԁ{vSJgS0A%T{xxE X/-# K>҄-E0z@Mu2{4`ǡPys$gW;ȡnb*k `lsp0j"1[ۮce>EIWN2mZHmsZu(F_[fD K3;A l(1f >X-$v&)+Ȳ_;jBKv;'e=^H߱&:̮^bCu– rz]zi*dbh.?]mI%|dDI"DPNE8grT+nIA8V}? J>n6GĹ@}|b X^_0Kwbo ڕMҞhw>2 iQ s~.ùqXV+ 1jtɠ`ds,/pB nb,a;) >3܀Sk;)kh {b\qcc% mqjsDC)V͜"G89t.0Dpx%$-"*Mx13<˜q"R+OqG iEKMOo,K} uI64ù/^+Kdc8Ä}J}g)4 ߰Hky+RO,-;//9eRk~F;ɺo^\j9D8a3!FHv[*)S:NV 6s`&gcb1MSAvkգ~=2g2(NjEv86ŹϊÎ`KrYSz_B&NsIwjrAԎpUbl۫-'*Q][s!l'ۄL*  \#4rǪ7y㖲ivi60A"'`*;<v:},\.`| հeqEu1j5:<$\.25g 4I+NZ~)n=iR+Cx5OeĻYqZy˲|w$z51jj/.Vk}V`3,rHLqDkhovmĨrfT"/N`B+J G2+8[%XJ8qͻhDn(1d5~WMQTD\Cj.Re7zmTgfdfs֥<_$v~[T[x±&÷|ٔ3'̶h1sbwɩd:56T#$PŇL@ A {_8h_RiOX\G"Cꔧ]Gz1Y|mTLHcX|PQ<F>^>bxؿ%P:G khĽ;ujP YzɧU/4R'PښFknGSc,ddVPo/7Yg ;) Wr%RǢdYv@sPP\ R,73`yTgq)]" dN[/oݠ"p.j:Fk̠\7 i[bow(#ϒCAQ.Pxz>yU/Ӡܒ"GqW1~1>)jqGڿ«2ݪ89Rt2dKe`y$F~'Ze50Mk+c$c|>0eŜ^JM~huLԦ8C\<'J'ؘe tvg!Mc )cNےZCOAĩJW:uԴ2Mr7~Z:K&'B7>3epPf(D l1)4&O_Zׄ]?(k6 Q`JJp.[@,A6ümx"[ 4a{@P;TÚG2q䬖{s|&7_iԽ(A%# C$~m. 7 ō$}czPޑz{B[κ)+}d?gWrS3LeK-tS^Km !ce?1%bW8OZ-/?/*9Jgr-TLoZp}HqğX/jBµ uyW2Dg h6bR9P2G7'`;cMo- @QMZc)Ni)9ߚQ'gBR2iBOC.=!~՘< lbi!U[d~_y܌\V.:4\GۥS֍LJJW(恿AL6cQ;>L<q*dp!~ai9Q7_OJl ͼ =-aufM*c ½$ (+6bqK{f_& mf]s#rN!KOSP%쳮dĹD`O܎d P>0j.Ymlqt+h)mFBW5I1bp-j:rfcch ?qJВt& jjP~7u(&]C1d`f.8Rnd:}u8`$h 7% н R~2[rM7ayd*Bo;٭i!t b2yRl*Hm4wʟ{,9~&Z{)] ))HK)z ^*rhyIOҔ`h%jQm]Cν=Za-+qrAvK -yi5n[M_F\Κ\E%n`oxypHґ# 5M(bv< *uvQD/Ogu&ԣblR]\bsC "a929TN¥3f`yo\(BߤA_- ޿qy]Wp?P>C%p樦N"]{՞1msIWQ-% eU0^aPBX `j)7sgY 0\ i4mS̵b1(X$VBzaE 3ǼBىs2)\_Y+ {+HNoa)?p3jR=1n-ػ}~?v=! BE?!zZڪ)ׂ0kmy+osl+?GnHk^Gyj^FW+'[I2k \WHb|J-4vL-ŷ;Rg օƺ^ow4K)[p (4icA3ۉeK טd5qjVqhqCsh5e%~'.\5?!Fk{3Rq(Oׇ.2ZL#!a=Sak0Mm1Ms H?R4ϖaTv]k¸qLe퐰 NDnx7^X^0:v^{^"/ụETNQ=bs3١8aykG鱳$vI;W RJ/f!ʼ=rԱt͓sp3v8 }SAy|6Hԩ,)|՞V2YtxiTWf WN6={%:䍲nwT1v('[mJ NF׽};--m77KV 2n*sFT$9@^)ElPx`P\|7/$ڔ= ol>ĝ?Ot쇊Cq9Xz şY.jnoFÈy9?`9 T+i͚t?[7w䍍۬!5kQ$VO{Xc䬋&sPgd3V4r+XZgXka{&K(J1ჿQj]C9XGW`Q Td3\XW3a=m9XU#hkT']l)[G\(+s@ۿ C_"C Y_z5MI;Χ{.3=T_l`MUqrS enD T#4U ]ok8[)ޱ6ZZe d-ݸUFP P)=8Q#^naͩE.͝EeU@y #F )s:h{ ͋:Rټ(,". #k@7TV"YqOa ܼo)_(nvLW0:j+&LߪwtV΅|zgAK EG5 pb&k'+JK-@Fo˯[#`ӘφڶȣÎkIz@_W.0 VH!Jqaz0O:*KR`Ux]_4ҡYV nx:НLێQ S3#ArތTV$Ds2> stream xmspfo5tl;yÎm۶mm۶m۶ѱaf޺uXuSX`DD Pdbee0ҳ8XE\̸f33  bdea4wnkdjegPw2𺹹 9;Sbfp4[ٚD%j 3-@ kebt6;lL쁦VLo@73';TUb" "#)@Vr3?h3cMG pek?NaV&.c3 + ,ÿt6uuoBP!R gojۚ<\̀fEYzB=(?#տf95O?=4gcVUU;-47ZT\QCߝ0__H%a_K=阙8t,L&v6f߲3 暽 Ouz[XXBœlߝՈ *n;<|1/Vx35R* w·8v&?"]26gX.P$b +!6Tz?Qij,Cn^6 q ,*õ 0/Ch@ƁNl94OU5`?88K57BxkUu~M$n^g⥱B_!pcbګݰMۊt6[(^Er笒t9$V1knܷ+;b <&\ߦA^KC$Q}JOUMDr#B?hВ% =ꎾsD4C5,\{ʹK.0]zAӧ\_2a5(ʴ2x,bOJNYm `.Яb".t40!$X>) :Q ']UIzoAs1֍?JKEN|B,yfI4f3lY}v ! FtNZO>"8ϜHX쎙H177a/_yKޙg\%A M1yM@` -ImY(;҄&j=x0簾gű+"-좮\"E+w^zi|m ڝ^.x k ]ێ ד=!j>ϴ8X?Ofg 7p6~M[9{LpIG7 ZoE # Yf~xjgjBЊ&kW8|}ī!Qi9&a<+)WO0EΧ.v+o˗yS G> >Z—5$A˱!;}k󬺷T(E҃kn[er;3JNAХ/p$tee[ F%%B]MH@qtz~H`.,\h)APL?S~Y|qr͐9ml,ÆU4I v :,jEŻ:e"u8U/&`S#_ WMkdxXZ;<`hUm|K!^r(^nYf __y*:!$PJգ:542)#2kvAUA&]\>[Uʞ/+n8kVPaK(v/ H--;=w/=5oV4s_eW-qѕWQ.{S?Aޚ1M~Ǜk/#EԖۆ7NILRrV@ ѫ7<O0#޾| I닲2RG7߯&x>UƧ<^&NU^'À T?FRͺ-w5ź]\6+9&T e46cz㢝 Q]'RjK]]n P|!5W}Q=ՋPwΫ.j=)0b؏&1GHeUz.K9:Q3cF6T!|O:skO(vk*'G z?,$CTGt, a],P (ъw0,5&V`zmR, S U:9@%XXJ+9D8^vY&/C% uȌ@O, wK[5-{FkުvRHI `{-zD4#Eyu)505i=M9jF;lk.)3y0%jNjK ,Kj]ڿ:첾VtK_?]Mp4}GTȯ+l') wY͂V\L"㏄qdI9J5Тl]nUiA a2!3ţB[դ'ITK+YϤ+a{1r*:EFoju)ɝYbD5Ҷ8XݬA?bJ3șŕ+W 8;N4rH}8 oW:s>Y\5^~$@UddmAjF/'2gECurbj+|w[ioXe5$q<z Z.>30CG;yTsY]:.+NMl&g Gp\)D V$SzWb&L*g_$ z${R"Mp1?\Hy$]E ]WY&1`Xw,&(5mF?ufl|TILS;v޸ؓd>ؙ O%8Fzzك*Lwš m3Hu50t^?hL3x4eC;&,UKM)e?F)Gb p=(@?U o5e)))%[/<1C~({qP8P%A,s\Y / 4b#`!Hq&gn~#2Lt\zc 2<=Mu~+:l<[77%64.^VMYFG\u |o{)"Hp2S9&o{Ы;dBR5ɍ V \vdC|Tőޫ $j4]W;(ҋCtk ~ _jӐبqpg1F_&tPRޅOboX純$sK2첾hO,`xS %9 yŰyX|MuQ+ȶ8XNFN`{;ٺh]ѓYHǟ-z4l\LqQ52 ^EWt[Z(~}U1\Y80|9T_%u#N\)"Pk?8Uif<ɺx"zG0Kaera ˦lSD7swR|ڀcޥTdmS ot5Sb"_ymct/aY6|k/6Yj7p;(-kVG3*Z]V&KWvdity`(y~FYr9m`H̍tISֲTCg߃}1j]ڍ&b#XP$3U#$]G!ߟaSooEIF\YV$8YlMI`=]s3713l %Ծq4S3@X':mqYҝ`DZ!b>8Ri8[pz_:.㽎'a\Ļ%t.J{ }dՠ&/bf-P2G5}T/EW_> a=CS)qHVLEyhJRX)x8S5CbsbR370:_A3.KMuѵ2e7s#PGBI _yEqJvAiWY yӵґ]nDbKLZk#$ 6ҳ"[/㯫Jn9c?z |fA1SJh8TMd(PƩkrŐm3sCI&!xi.$?7KeG >ԉ#y$֧ɷ:mϘ>L˿7GU8cʲdxL L x9g P,<Pbאcڤ=O\A/S+']M̢i0ϹӷѼ@'(42f<,4 "?vB?d1K\.^ZR8t˻mF\H7WMJq*<.q^cxyjo<_;ٿ_(+FG5t`0G.l; ))젉s|\eRI`?=, 6AXXk87"%}+gؽfEdp v`տt\GeiZҖ)qᏺfp( x/ilwkK>YkSnb}ZX $`3jxOsr brɗzG1K߂pyXlV`i|ªff(-h*a籡8 H߈!WS#@(hLx;jùPGl@؜2[z6t 6iF0~x nߏKGH^nJ2kԿ5A_|LU!'?x`ml$,%Z=kX(mukZ9Ŕ1XG cQL@\k˼ 7O_Q5]E$8244rqY'P<~3Q̗&2ox@ij ~q J?/Nxa/𓗿zVQ@X4"ÈAbY[W9Kw>v91Nbl ԄY[:pk8IވSw:,CrЍBkƏ>.I7 l.Jhd sR?RN{1 fO谍M,?лMGlN~j٫d:5H-nȁo#Ax B\J%:1}Hʫ_Pϙ@>'~(>BLUʢAnցO%m_^d"WD._ .Z9ǧX4XEƭ&|FC |F^/_`_XE&  E jZjĤޠ_FT]ksJ+ǪTϚдSw Vs5d${>+ck!_ܔ[_fHǡv1:[=G`;jߝØhb7&E zƊ_ūS{DzyF}<^s{>,cE6X %, vC &eބVcTVizs']ZkT'Q&6=Bo\^[Oئ{S?#FY=;&'T%\}9HaYdV[_#|[+4oH9>;tU,sxm&TOyZ ,ME')yMku2ĵ}}u\}XjLpUpj$DL)NY6!N`Zގ*!{7g|TmzV;^O2Z ݊{<Na{ ]{HR1t['w[?Z'/1׋&RdBkH_%o\x_{<̤@=%{AW#?7K,m9NH~x|"CoiMk`ѪrKċ¸:$* (*> N%6Sio3,-֏E^n4dgTԆ7H`7) O CIF@ExC TP-H E]_iݹ+Du `H馄r>0PfcfIԍV4w9wP\ -*,Ru8g Eeq U +_{c~)"qFG !E!xoS-LdKMC~? ubB<7Sz|'Z jZKUMlE`u>z{G3s@M[=?AUB]yӗwc>'p'vx7U۴F7@:ȸ,PR*ٵݱShN0ΆYV4 &Sl&ÓpYl%ؓ&,&Ďє^+T Bl ȷԇmM{e?YG-ޏλg&1wcs?6 ʩ=Az!fPhStD"/̈h/= /np@WԌ?kՌaϾ>y6 A2dacAmCRpE7Yԧzk'Ìuest c8=r񋯞b[g< c*5ڳCL._p*.`s< ^E0 D܅ H%1OVNܳA[Ry3Y&`DJMZWPDsYsⱳDPb0NKfm;](W|{~LPܷt՟Xe0l ߏd? O /b|J#q&9]k͡($fQ_˫[pfƩ[V?Do9K&Eaz}كkrrDX<#H!WY~CY^A^W; lOiFiROu0YU>܌ ~2&K1_ܳ"'`sfrzfvL߃]eo7& e2m0SQkG>Դ%CɽWXh_ؐGUnɑ5ַ]? d'6X%Cu醹ƈ̲ρQ0'a8 H@)U˪zi3=9=Ԛ/S.E I_GY:4 A7a9'DA0ch*Hm^(NƎKH{;1r{i~b'}qݢW8/d0;@ 7# @ '~Ȉt( dbΆvĮy]EO1a4;P8vxI\ [vkz$aԻ ؑ;ֿ.J{ˎC!<~vQ 7 ܾ4a꫹çy^ TbST5<%*/9~D<г[р 9P5JlS} R[hEgd!Gc<9o3Rr[T Eo/^<1k(bwۼrQ6lH}FgD2p4˴]))V-q՛d17|\ X8OϦu)v!o cxIwB钦Y"el lXm{Kj \dB&+;AUr.BBH5|Zw  *bAש>pW/{ ]8uų,l(#NV|ZZ]N9 Q;V@ pp}`,GPG&~E/D^b%aW7B[k$/"ʕdNn88bV87!+EQ R`可xIݸNnӠuE3#!#dE~K#j'6;83ZX'=7 }A@Υ5hW "l 0,}烀FlA8UyP~?s endstream endobj 85 0 obj << /Length1 721 /Length2 23090 /Length3 0 /Length 23682 /Filter /FlateDecode >> stream xlSp],ڶm۶m۶m۶m}m׶mwf̉qcԪʊ+C۹x:221p()220001C ;Xۉr(:020100B;x:Y[PS;IfchbikDfocfilA&JGO)))8* 1)%v&LovnN.3s%US!W&03!ř!p_dcN L,]L-=&-C~hHI`bjF֔Bԉ@Ɏ@(C[K_AH?1'e,faj`f6HZE?ϋx+˪*K+IS.ۛXڙ(.+Z=%_70߳6ݿ￑E {x2rp2310p2?t]L\-?o#zCsXUWRR2j#Z[]c=`]J>ƛœLMFbWm(7F݀aˁF޻dn)PMd]I9"3Tt"$]z%G1=E-1_ /ރ%Է( t8 |0aV5s^tϒ:9<,xAH)$!XVZc9"s}T@#|XZAֶR҇:ܩg6>n{b ڥ#X'- bگaI:'Ѵ!ÓX~י[ /xDMs)vn'yߟ큨*ԩ}r|^ow _ƌ(C w@U\Uٖۨ (8Ihr@Sov=:ez,T*Wt~*^'0A6XJ3"9Ř OCf[jWOyW`ͬ.߈1c<;"~RrOd4m"6go–}0eh4oD|ye!N=Uv on.+6rem8;]]Kդ O""йK|Dw_T \՘Ϝia[$jQK~i*sD@{O=ErϺ@[|R3HhqJp|ܯq[X#% ԯH1LZEA b EFޥU@&Ū:XSJ4Q |\cþ@>r|*~ QțZxd$@|s7 ptrȚHAT4=z1hRJnⴱ$ޖgaSJ|Ss?Minan2ruPN;=nȶ/qկ'tN\GKQFU rFUp#=īϽlRk~kO l%]NJ zs\^xltls$[R}[l:.=֛ fq 0Bo @qx  ~j/H8hp(Éa4'Qu[n,9F5v;d<$Ri-1l;\Vޜ"`QFf+b&` H\|)prm SI`x'o+kȅuUxIa%:`o3zL&Y:KqK:jPa KgzQn:*:Č0'enI~WKʄ,2(6Y_Ȫ!p@qѤ65AdAMd_ℓtAfWT2͆Њ} Ԧ 9Uag^ηKsd!C`ǏMxAxmn0_n%?5.P9wQlM]5n0Is+;gсEL/i=?}О!c7,5 IMk:HC`1s&"أ7>8'h)s(tEjVon䟔@*Q 3D0\YHC$:F?Q6Ќzw݁13BQ(jʟ2R #&ux%FQ[99mI- >W T! 5u?XDJ,"ř-_G98cԕ&^XlŬH?lU4_W8~n|Ԩn6p T7"M,fE+>>ъ;w{|KlLhx$<3ǫXw$Qk?Ù'>n٤M:< aPHT+L.#xQH>ɂiQaDIe b(5ZLN᯹.-9 LŬS';N-8B{ чb=wN%sN8[q)@1\`#cm6L55>Ȥ@y8 _uB rIy"" Jg-[Ew*?4\Yka\GY3)K@-ԧQ,=!%losVߣn)Up'lzc/j3KӻaT7ޯeS,|:ÊS(6~%~p xW> 8>aH8tRBOmya.ƜҬ@4DF%hJPeQ]s"! 9!i;;- D@nh^ZFGwL-} 3~UZ{87# 3~{9B'; IZvu%YrB@ܟI{ 7bq=QV=.i:'mlՐi-^t);4:P\0F#ov#XZq U5O:# dǂ09pA }c/ֳiWCbB6k]d> w;TC/ud \vwSmhG{q/p5T|5x0 d>4gWǩ y&?q9@BF`ę LanAfn|\DxrFZZGjFx6Av}8WݡfKV@KNv/殟fE7] VN- nM B= zĭg<'mV;Fy/ȚAO ]bpF(ޢ4<_LB`}AJ 1\YXV _AfmN.S-IyS.#Q0'# LpL r)5}Zo4i5Izdřd{4\N4]0Fd/J?EFz%j"c]pS01c 9yq3@{ {?¡S.굉Bdd1B&!V8ԝ(fwΕ |;J$ҟB,=+8v)e'թ <q9Y_%Te-zÖ 9 \ l$},9 M,7lr_Rnhҭ`l\;iY~3xWk7h1MgT7Rz]Xu&c9g!̹nx ;GJA}[ۗ& gQSrȑ]@VMUnz1uO@{S MÜj[[dpt!łR᫞ݭjCdZ E`0^4o1y5fieUSmGTNe5'Z5Q'٫5ILZQ5ay]C8_]׹ףS]%>4NmeE`4]^!-VJ/ʼiUii21E`bʥ6VaLsoެ9&y#";ּmJ2JR<Ԑvlu+B5)D+^%qQ B<3òΓ /+G6zP&@8Qk>-*:SP2(p(7R=:uAdR#vI@1k[bXk0 gB0@:PЄetF dV׃ A#-3{r~B(KȄ{muE)é{[nٜ {4蕮 HUii/~_-(Q0fCna+jAm(#h,3`:t$jDtR8vTB2vp+g;bZDf2b[ aA ;95Q,M"W69E+.3(e7HӈWa %hGKʏ37oyT׈o0 f^%?MY-"^=RɒՆP.+bbuZ ߌRsTvӾ\^x #&0v62?dY1ρ? nX&il҈;9pHSQqiXUr%󣁰vd{ \x9oC/tAH匌;]0UYX3I^wq[ Z+$͔ 68]³P9S! j,XWk3+OI]ϲZxАjA>Coe3}c#M1G0F.k**}\}IUke9JXGZm0|vm2s9DՕy A/ 0Z]`lVTk0; ZV< q&-H&kή|lJ="G :aU% FI3 TE'#dvLgv1ySaRVvT+cEUu .ن h2֙G3JX.ɕ)=}E" 菑CYnҧ0ƤWL,pscGXdz 6S{49_gI0qI㚩92WLHU B r%uZNE0~h~`8p!4e2m#fxt^ &^-KG'Q5B=[1tGlhRnP [l%g:"Z0u 5jmznOP6@mX/gWHU\;(Cus3Vµ[4фsdw5+k_9c!!;p)Qc!WJloQLL& kC+WMUzb?)80RPK ;>&m?MqwA :}6N oS*^Ss`!vcə 1qv=n>RhV)J^U9ti*#/&)N,OFaӋ?d*:RQ)С ȽED^iD/Qf%c<&DfZzA0kԼh端]̓wY eКQ!b"*;պ_MAt+vn{]Aķؤnc.|_9rGR!졟gjlNC+90v)X,T Ccbm9-04Bh}1şqOAÞC$ȢD|8,NI߷Re{8ߑz*e*T7ih--o &srWz 4IC9a<;FFXKנF>G=-4usUuKX>v1S DZJc7-}SxZ2^^5eE`i)DvafQ.[/;E(Bc7fN/X.ό{R5 {}yGe$<@hT>yޣ*(w#ZS*;2~RߟesK͖RT&?#zdwvX4$% MӒ !/9fz Ar3N-Zf2T$)y3yN=pK-n `|S+. u(D8+uXjRjX/ >7UfRڬm>r`酼%TI3\&ԅ5dـ0/m$WW8@X$cFp$,'u5I^%vc` @`ϜO_b^cU9vk6sjNJkB0 tqnI 8iVWCr"FT,-K8#6^7ByM| ҄b t,DwE&m,j D,djbR%ޣ|Dmr  ^4~N -&;B:/aA=t#\9¯-d .y+>Q.DK:D`>bOIƘP(PgHnvJ/b')ݬHYpL~' LNP蕭:JakiQ֫k8xxgvhGȡzLu4~)рTKeaJD y^/<@nJ"{b!U yo.e`*GCfՒE]'s {{ZA#G/ށ-9364@kKPƸP%~vJf2NRxc;'L#=$kDPF˖-%dIsD/Y]2~5MLx݁|E4Qax  4gq/Z$nQ߹gaAK](?{4$0sZT4_-R ׎n{z!T u:ѷOX(֒`+: wՂb$A^Ċ惻M=sd:(L\f q^Sdqfδ1^NHo@€|]~>=^k#{޿Pvq=yg#`p.O@|F 7#Xww>sqfl Ez0-/?՞ljv0wF m}"єEʕD,&SBF&m$*yF5Υ/Bc`_?@3[:Y?d{NK}+6D{e'9.2ʓ?F;i?dy]Ƙ$o_HԵZ(ZrPjӕ-VY: r~:pO|ͲNau*{nb_ WC/,Y~.DgOlP-kW[6nONvJjzjl`kmzldĪe:>5*=+)ƕ2\LbYBtHT( M4A9 8a&톥_K苞WFx#tY$cDk8~dU6j ̠%aXڢ&dyP>ssk[a>d I?Zc;>JlDW᪤뷞K2XfW&H7'4G/2W" %h5Kr-8 iZ2ta^9NGZf0WJڃu:mL%W}-9A {gC`r~`ԯ"1-Ȅָ79A"z:¼KH 'IMB1˖VE6G\pK$o*ņ-}{@[R1?= `\< EURBlI|BW[P*v[v.4I8Ec' 3cy31=,U:-C"_imnf%6j##3 O2J:LhZUq8Bd${ '~BtU9n90Mj؄Elv(ڨd\O)Ë5s&/'P[@/CWxv z/&4ۚ"4^n}n氢kS4T qFXʁk'>!l+i'uY\h?JGSd; ZyŗatǻL9oh"0{*ͼ0oVb-mBm%md3^o QA3g4@:VKUҁX`LsO1doBE{;?%>b )tA Gd>|&o{&`M1R F :U+qq%!^xlJmL{J!ѦV݀5\/knȓ :s*˺$gQ㡭 jCqч][i GDRfRSg̲v遍S?e;6T'w "E[fb>Yױ{̆3=#'|?h ޛaYb 4-{dWv}z&jٰP"/PHܯav? g%\R(s^YQxvBt. )2][Z~AW GX6ӲxyD!m a ȼ}1^'PE P{$zVH\߅|Qx|N7Ҏq@.;Lza_5Ry`w)~7#m {Xz5Le'!nM ,M{pBN]3hp@JGSNlBoe[67sksy <4i)*Ճ8"v1SOr዁Mx_du. qE.Od1f" WGR$ Sƪly`/jnr(fVL[ST\(L o,vlb? Z RES9c3 t2esȬeICPw.K2k 0UGV[ qHֿ-%"zk^W;AE_Pf LlfZɁ|q]r@z*.?SCMHzat]hy)"Sb:)66wH.v No56 NwT OH✎T\piFe(ٱUa}X Vۍ+ANys}{Ub<1-%DGL^fJBtL, LzB0yIQ&S]7Bӣ_ 2޷O0BAVRȮ 5QL#}Ns\61wHQUwQ;rrDt|8!t@@S)DٻNL(CObw%436A)SkUX\&zҲZ-I}=*~\cJIR?dE pruy@~ׯC6yG!t\/xgA.il4ŕP ǁ\Ҥ^dl15V⢈ 3=X99_ v6nzSeZt}'m_]x;umFa-pvf/S%$p=qҤ}*)b S6~ E򣪺>Fq>X unXV]vj4/=|^-0ޝE/KRve7&߀Yn`z6yQu>)=]љH ѫ$8S1H l{R&Q0HH!vO_X @ݥ4&nF4u)zD \Hf=Bw8{NDzR׍BWk¢ IAbHn3׃U)^&q&BIL "tFv}bxS"ۦۛQhe<]~]J΍;WR- UIxu7kIŋ+꩷g^ֵ/pQl+H[#)${NB J \&1@5 aA/`h Yg5*s mRDд 5Q=S"N*RKwEc*6`acI!h/6A.p¬ڕ=I (6Q kAB YD"s4da*}H)"憠 r 4͘RK`C1VﯻTAاߐ{}1;#Ԭq*)OM,I+ I =C@.#pSndI]bՏ:\mSڛF촨בy8ҙZ:'4u9}c%ГGJ.JeDg3^[ġ [sV`U2r+ڼ+9G)ϼfwUiph`AzcҨ ,pT3EaF~ϔ#J!k@N1@"`G<[uh|D/?& K=|퓒k(mrsr=`ņqBg,^ǏEA.Ӟ%oJu4R/sPeo,C&6FӬtA ,E9)]y,OEyK]@dItзs='A"K@WpBݖ3vo<#)XuSxFuZ,?6ς,$;ך@'Cg#OP:ԧ@͎EQOכLlR[vM423v[/&X{|%@X0lH<7:K Q V`DH/k2$c#P@{ 0i#W6[:CڭCX愗g\u'Z [<:P47" br?k#-kBhX65#u^l-K SLay^V'-4H8CRE629Սm$ ~ /`/Ŷ=pPFPp \HN.v$?G^e_岬xGٜ5mMJ(1O(ΜsH{dqg>'X<.<ϒVq"P4a*Ϳ$ꉶF'ɯ6RS\.,,lϰ[$R2A"ɀ};w- Uk#'&;06}YC*63g|24|s%)ؔ ƞw|]Wn3H;s5:&;ڽ!G /Q4Kr\AvP%&n )ϲՌ}Y0Jahpw?;#3A&*)JYM?Bb1lƳ3Tv~E!L e14d40.Hכtμfd\K qBX($/Ū'0Dibn<Q` ~AϊT*4nI Q V!KG4Ӛlr jXL4@v=}ya* %!fOd쾐K+};VCArC@?|@X8_v0m"ꭎt;9"+]g zf{&]22U*6FpTzȷ>-2z@ߞ,'Eչ3%+bMo94WVSH UjH#,6N(7l'G5!]H Nͺ3nPumJc(-j;3`U0vRM^L=$sʎ(;'8G (tѓvL[@|zݯfIJݥϻZNxm[^JO씋{ҴҎݼ,;"(bt܏S 1.ބÅɢz"HQ?\ }Ƞ=αUOlwtΤcf)@*.>-|Ufz͍Jd巑 p^Pk:Xgbz}lsUoP131| E@M& G;"}Pǒ_&OG#.Z1mIDߗ]>yu5'JN0M,0B |?O(O'"*m2GwR֞Ȼ϶;|ješ\P@D/wJNs>!|_:2=%!y gBVj4[(5eVm9 rqI{XM0 oфaZ{C˄wR\{z:[C \$}H14 b6RKj:"eanqbS  )JA7 Pl-͖O覹dOudY;$m2bXTم%?*6zb}*ogaÐs ~@-VPZ/Hov}$9,,wVw9PK8)A59-j8{Ij0 ˁF *݁-tJ5E\\D59R -\ޔ+G$8f댔v+L[ ?U}Oo,3V?BI'$ g1?znю҂&Y?uລlCԼShpxY"L" ,&7?u"e[Ie@;0x̿_̙`x_7( i o$C 1?Vqjf8P@pq3J{Ij/=Z ԫ$dUZgp%,vՍ`e3DF"7 1(w+&DdzTFCτf$,W/@֎iAS> ܇O7bLC+7u+ſE> ۅZ22|Hsٗ/zM6FZޏ5.24\Zhg!H4yy`)+ Um5bUi)|!lndi\@{A5>=zMjqhJAˊ6r呸y޻a[b} RܢIhiKDVX}S= B6U2ݝ%%Lܸ! =v zxO=Z֡C:~'Eތ\x\$ !MJM|P"8V .P;m̧cMD)=>2ڃΛs wbƭ U`9Υ:rfA}d4tSrP`] f繺Cuv .UqA)vm{gGcq ̐\Ϻ#P/VRpo<z.{7۹G$@ʆ>z QKE!=|֗ݑLn_7)9 adܝ=IU<3ʃ#'W+FI,N~gG+\D|YG ny2 #{Ǖ!)P{M88 ;gRih&}:9B4e𠼉 E57q-EwW0e=5]G0Y! Ď8O2P@X5⽎nP,݅s<HKo,L&dO+7lN믤d:RuTe<A~^7<, {iUf*Ct& ]^o1 ZUs0 L[lЙ;I{-͉JcBW 싸*iW-p%_roy|^lGJcF8՛ڥת,3$0SpJF&d;݇k7)siID Jn~ KhY- eH/期eOC]rpND{ҷD5&,9wsЧBߏNSZ{DG\6gxDI\k^Tpn'3ARRn{( T}DW bjN %E1ϫW|9d Qy AJܳ|s' ~NRb 3ncnP| X4#E5(VLϗ~!:;)V]ң,Ԯ'{X%U6 -Ǫa?4OˊМq% \|A4E}U˼XN ӒykJm8j1|2Ӄ敆;̢{dC_6Z8"@`$*XÐX:~s`8i[q/؎ͤmTm)+c KA605jĞ| p5 v{rjHw8`LY%kwp߷zY_PX0,[B˻cckhb7&GW&FM~a޷yϑ{ DtE&Tley搴.vԅ ĠYڷD-6LJ=OX6?LYv~E}RtdϘ.s(;!5$܂R/tƶD.ݷGF”p?s'.= i44-2FpvPz$La X[)½WYTܸ,7.ZFK ͣ)y<dC[X':ksFImc Լ>q4r3ٽPaccΨr0nvpYxb,f}ӛaw/`R1)C`T4k>i}d2c|l.c06+wYC[dYM05A)L5 D{WAߦ#B>(H}U7GHwauSJؕw>p -A{D'Swܗ6rؽ%*;MJ螡p|f&y1:XpF}dIj&Dp^YIm]|"7A*Z%5#Žp0_ Id2cB(-v ,ƕ-<ѲiܩTS~}Lh^w)7{GJpa\4' T3X3/#X\`$g4ܖ$VSYvȚF;WEPT 5 7iɟI댠P8xQf٢[ O( Q Yj"^(X>2ι]ѯoXì%b&>IKh(C cH5^:?|'I/dsA$Ҵ beZIF8rHF4b:nvw&Qt(<Ж롍P$0eqQ.x*=aO@_?Pv-0s9ew1KV'ߌ/b J0!jh\ +(;D-Ӯ[o|Jj++2iHɔŁővJ `<۾{9Ph%aXU6Utƻ|4qi2° ?b<gM߽G|z#As4jaeʦU ̉Ёeϗ[౶0ҟ 'ȅ֥M@.S[`p;S5( 7eOO`wx`=M:CNxƷʚ*e9jXziұYNS3Z#e FrO6FBk<(y'L Kv&0TC>J٬ tm h;|[ZEBm^R\Do ?3XБ\ FѾPxt.\g)s^MFMDB弒IgӉ%0M*xJ7 L 1ց/#2 ]3z m}GnPHH- 8  q"d#d1_181y>@{[\WQu(#sw@\wӢ.:+fXJKƥ>vgK6IBA!$$WT7(*(ad߇R\>cA?Q}Cw/.5zH'y\U[{yfqʀ91KU}r'3<Ƶ`0Huh>9aZ#}}aAQSq_M#f/P+jExבz9q :<_WQڳN{9QDjMfV># כșAK02kYF*M-C ߭̍Tn45fz v_= jэW`U^~nT1g;g XY=7/ -{ek~'L23wd^'!,:dnȷ`] k|`I#/nN7Zi@b/e8MoCu_{5/Y* ZOZ=D,D : X n‹:/1ʅ 2OBuJm>cInF[KHD\#O73 ._Q,:V42H[IdUʟ]405 Ay~ PȐS%h3XYS.T-#9}`($5D0P{< @(J8 RQt95DwzS46,~ endstream endobj 87 0 obj << /Length1 737 /Length2 31291 /Length3 0 /Length 31847 /Filter /FlateDecode >> stream xlp.ɊĶm۶m۶6׊m۶}޽ﯿ3WwTϐ۹x:221pŔU t0N.v".\uS C wt4pPS[P14tػY[xܜ]\qR65X,mL r q9U @ ciljlJ 0wۙX+&g`fO`fNYQA1y900 #񏻝3?֦.ʎ_j #v bރt\>yʧ*œ)]ţRy;7C2M-5/J&{=bǕ."!=ι1BWay*lɜ7-/¶bFJK,q!O]qVy8\sTK("+_9-Bp״ϣ^\ Л29q ,gy|2)ֿ<{hb`;dXXkLݪ.ܫ~;j1ME{-R y_v G@7oݳ n 3q$љb.I@v݃WʪLh<;%W+U8NtW2a/Ii=/̊zBc8-ђ=DEq='w-֞GTCcAj ePA)Pr﫿[hNgS@<'SUvO} OXWiu I8o2FN֋ccC.(a}|K2۩3Xq➾i_9< On> E|A9\ (5V즸iYgB13{=V,>_ƺ$.`h|0BPM f],ǕIl#R~0GhϪGDG( 7 gQl,Ѱa:p0~ݟ:Z꫗"+NDʤH- &[h 4<0H%_3.yic\.0T}8P_DU^Ug M~~ig4 ku(.' +|QjXJi)N$`D ,;ΐ8G+T$Fl>!!VvB^G}[y(lcj2Ts2?8 JΰY=$`7 Cܓ],B)={S_H))Ro@*\ӿ@q%7d及@9hH͋5=JAS< Ԭq,:SE]nFz6xl)HEy@OJ9fг7J|w|c 7+&k)`v501eI4r9uP^6gKq5t=N糑( Jw_E"+o0/|KF}jVS#决xIw!5`:K:Ͼ0EBk=>dLxUEvVQ"QR.:MjV19ET||[\1@M푽-z\<H!1☧o2%ge\ u33XͶc6|W"B̳1*2i&2I]uؗ"OUX_i_ڞ~[w C-T?SŶ6B2@̮=gGzqP!odm.ILɔW޼64g;W!`{CC)\fPfMX,IG O=p\#kzzEG ;2;c @U%anT=:~mL :Y%+eEJ߰y𜞫P*X׭_G-HDmV1H*,#kfe)3 ׸}W<:=-h^s+j%-SRl< ~.aeig6挌sۅT!ۀ7*|iޗ^,3Qe]+(o'ϭDy-róY4,Hs$gB٩Zm'D;Z̪$X/kk4M%ⷪA]R$qu3jb0X~`kjoRKc-@$3D7,Ri*B_[˗'qw_oWhm&5Cj8GKȤH]_6Fx)Ixq<^J4H ĸ&)f2HE3~E}$ =E+6"OΚQP]03]80;u{+K\c2hNXȀ<o*¼nIV4LU0ݷf\ooGdQcHhYO&嗝h8-Ff">QIg(Iہt{`$Q$P1kEnInYq5k$M<Bw'R!C}hV({R1$A]O_pnԮ&t>.(5Ws2z.W)9 +̘Z14Slv'^ޏZ؞FM6]R%D0kz/ /1rWmGK@/Qe$㲊B Yc;6,}=A9b,|KVY%F\2ӗ)v8ij(3\[%131?xeY=O;^m6${wm%f_V!?ć]|&SiL$s:iG!+(%)kZ3hxaN~*ⱗfg1  ݥ#F }f/ػ^JǺ.V0?-c3PG*FN'CKgM8WTuD ]1ׁ(M罿+L9݆؂-%=-\JSX3Y!*ڡwx>DSN*(c9JmoM5u#;ݏ At|nuʆ`Ud-K :ZEfg0+ %J_Rҳ,~h;?9#_o̬M/U ,H!;gȺS*PeK\WvQKx@Pv[h][+a taXK"bge7`$LګUYFJ!ZyƎBJEzZ'Oz0UlDMNᯪYF3 .pؙ@"DZ,ֿyaҰƞNj} L϶q-Zǟ/6803 bu:aS䤉,'բW4+ +a%uTn)a8/ׁޔ >U"qO$ϯv mOZs7߰ 3u,-@dlj ]6QbSj("Jֿ. *Tam˿W!bԯe2e&ޟzv9n}'1TØ;b%tB<)KK+n3AODYKfOa 4m#3F * J0QLm|2l|Z!T ӹNęͯ8 tϭ It>^1*DgB=S9: B< 6ٱew1Pv((fOSM;B ՛ l;^bV\E*YgHt#2|[-{z]ʓ社FfىT~H2YJȏph<~/>t; 4>Nd2qreA'f-"GҤ4;#VМ~-`7EYqt0PKh2,?-'X軲𹱹ʏ $r4P@qߣx~EjdCX?aT)%,Gີ ӧ2N ҉F81w9vSԖ+ඨqQ1IL-lN6f;#%ke0!x1sűv=-y">Aͧ0;y@wDDY]#SP }ȮIs(JxbۦҼ4'AW稐 o hU Kicp2#~O+Pdf i0r<{F9yѱgݭ^[g}9=@}]w[" Ԟn ˯tF{l1ҕS P5=ChF@;>G1[m%=vFg4-!ܽ02qSKRCLP n`ʨKHsL0]+nz> 5#Xք.WѭԳ?%GX;ڌklbuӜ9KM[TBcG-Mp|=]q\# :Y.S Q.۰n9:%\o>Vndw(G&8 yZj#""#F%2M~Dq jݡ<&QpH]V?HضA\N{Hdi OVTufDo!."ʅJZe:5Y@A+2+njH }_p{*bhjIڹe}H(†!91F*\S[h R1EW9T$„&JTtdY)xsٝ!l^h S_|f$&M-%"ҟӝax 5r`:aB񱋻!up_uh]Nٲ_N

Nr9 m4^UN LfvLYBֵ]V]ex-b)ù߾s Ha+XvMb+zx,T4F]v|CH&f?!q!1Q+cu_7ˁįwUDeٗohn͒ɘ&"f+ 6 }.#;#螦@R-!od/K%3k!֞9YQhe~^F4@V/\TsF<RȻ ];?mwh 0W@F X+߄Z;Y}Ϙ](4JF6(;Z|IaiYڭݰvgW5>jL aTzSjQ6j8cb]UOkuS4jҶA}7)/4 *s%q _[d6.,,4Ƒ -#`y\0Kb^E0SPmVhȦ^?<ΊY=r\] <#* *r"ܩxkؙ UY +lj`wNHuS;%38E8U%G-' MܯKǥ aP,SY@zR*a]Fq.-ATIdR+{Dl셱{ .OS\ epu.j<$K*0R ?S PuwT^4'*N/l/]:o6* d8XkOɁ? pr\ C$ H|#J8)&FtPHY70XZhkj7ύ!pphHl{vn~QoUFUWNT/?Wb,>7l<%A=U_氇C4-%%≮aWMF23[3~H99{%.5zzM5͵\Xx_?ΨFG {bLQ \rkdV\De߃ᙡ>ӛp|Ҥmǜy7 Jtλ ;kgBztϊAMhp̿vg.Uc^#6JD}IR 2$\wmql 5 Np&-LNU[ҽ+Fػht/H=jzLlOƫPj|>w(w>$")Yy 9|~}^_d!RWߛȉPOBKF;V (widdߏ^v\5ZYA(e_]^7#|ŞABi<ӿW0V,HDRY6pk04n]Q2i? 㢔9R[^QBƛfq\ݵPsӳq.sh6 4FH-GfAé!;04%zqvi'*.IjtF jgb8定ۼ9+'pW3+$AʖvҌp3yJHr[O8Qʜdl}Lѽ0 dOh X៻w^}qoip|y88ҵ,ߋHJ}`g.:6`:b< ci\O#ѹwO&δ LRsE~|%.NET.ZYM@!!AM[)XM=K'pVvxe)X"Dm. iwvԔA/B5F} 453nY#ag$H&*5_ynWKMhgQhlۆ]Et夛]gOm WG;T\ }I^F\}w- +%4tFQѦM#֥q#n"8dg’ϢARG d!MP;+c uU1﹎t}Avu=ˉ>j'TGbIcfw5Xc&"Ze9ɽG|iu*.Shv~>+Vr<{7Njv^?Ϗp8}l.q }*BnϼVy1{ٛ3x2S,g[r6c-ʋ0鐇xV~_@Z;6rxoMSX5kv,"Ͼ\(5^>0f]h7ڙ~RoC20O7}s rBq:oZOI1KbJ(驰>am^(h}0#pKIͧQ|Oe= @&X F\%b^'G[hnY)Q%ǡŒ!(fo qi8z %{G'ۻ&哳C'̗sgYLKO-UpdD&+w nui4Xw%~՘]M"JZIԇVuh?*\40ۆm@q*ls !wF/[bׂsβ<5h9J {lB:NK#9*Ji8"/~F"9an}gh[ei: :W~ֶ<k!6wT Bn;khWۢ1 r=H>8lk yBW'vz@4o}cX5bFtKN8nd8VyUy6eô֢L6(v?.u7EȵpK<*7`9*Ȓ-]x{)c6W#E(I*dSdhոGЌrv; s|IY4qzܠdX%ﯚlNOMq^S祴ƞB\NK&O_GUACqt`iAuÜxHola&z(Nro k.ʢ;(YK﫛y-ϻƄ7r&_Y|ЮkH!m\n=N?HU#6mj@9X@Cydʱk›MiiElcMWAwXZ/NlAuCUJ.DJ95<}jjFx'phw!]Y5⭶Z3-^3n,ΦJrևcwp\`nh4I;Bj8$+n> s>"x¥&ʽlLc1,qh%Df6j>r1Wy΂6huӘBoTv%df>$\L weWdm^Ł3A㬞].&}Fu&pk(fb#YuJhc*]A_FPuAb&]se+4D{Vl%2H*f+SG|q 8yY / XeD*{ia*?)@UW#Q SuQFg!Dl$V0W𤇐P[gqڶЋg_Swq4pgHR츝1ƃ7K[K`}|< %u}% _=;vOTvISlUڨ'<;4;D\\)XN)ӏ*8kN <؁4^eu{0Qd0GL*VaeL͎i󊔟_ S&փ ;O$(oe6>~z5Ugמ4϶JHG&:!6:ۢǀ%ן4OO}\/)(|^rɥUg/"hV]rBL}-IyxЪrpL^Y;e 5iEU7I0;x:?cߨ|BmHE2^fb,P,7e1P]ia|eH #)AWSMiI٨Ty+v4ҧղcY#h'V`Aĝ';oK\XgruˡVHt+y p)|X; պV}Nc5;ڲgcjӳe~GtҷoU]Zm4F?4cT@Bؙ> Jd@P-]+czC =-۹x L?o4/pvԋ퓞VĿ2$;镁 q = _AFhi@THz {w!J|2HK>ԉ@fQ^ʧD)ܽ62N6XdȎu]~9@-$E^.ݲAѦ3 0| $]g٬ Ip9`:*: N:GŀcgnI_Z s?QY e5g -d`N0v/i##@Dxu>Q;z6 xx"Zgxq#c:$lCjdi!Rs+ǐa]<-ǟO ŰE*)ĻaH{Q; wХ;}DVוQʵ|ZjlJ] Y C&?"Б`Fx mߑ^Dc/h Τ:6ЈfNܪ! r L]q!sQ6 hh_瘃vZ"ojV:ɪu;s'Λ`{tSi&x&T靋Ybd=_]nV~=HFoɼ@B,m_x C`RՀpFS5)-g2I/aSˆ񝐞'%ĩYf~u8.D 7``S*X&b ${bŃ&١B#hvSM{SrS-\5qqeժ;G /)[r$;7Z%ȍ-pق2MIKBڅD <R^ln7-l<4! %c#/y&/::펛6S]0S- X ôFMֳOY(1ۘSj b"ZcI!bԤIňwLfcf~v98p/medrbZcֺ~hf[RMY>H6={NvVr3I@rho(RlNIxXcׯx)?!O%cRjEHKeCFhts-q*16#yNBؐ ̭&>̒{ؘے8?jqb7ԟS 1oFn!(E<6LD]FlG;ǁN-<e&5\BAÛZ(̠uIz8 E@XX*yi79g(3CuB{ s=I6hBx(G$`o>|5xkUXc;c|x5HGS{%=,ejpucx3y~ذJ_2 ު$pzp+&zj2qn>aSV)ǡ=4>SVi^z#JF-s1϶G{#;`r9ej$h@LSd 5{AM~tXU=UErYbE?YY5LF%[z~8֚?)?CЌ[[إWZC棣?_ Cᲄ`)F\1~6 )iؖDVz˛&SѬ?fz,@7Yzx!wG45/W(%`yq^VYDjAQqvkaHp-YGh7fm̉)RS>;q!l{%oŃ_F̣a ݛhK*#\ںor cſ*e?K'La$2jp3cOWfן]YX7rY\H9s.1*G3w:DmX28CKŸTGB셳R#[kL8;B6S`%K2ߠ/;,7LA Ax'#(0hd[&Z&4vR½'W\-ET~C搅JX6д&3 ҦwLX;%jfEی?AdOpGᘮ׳uan-V(eߔab8\S[uYx5  Q1FԌsK EK,YhթR͹sMS["xԸyk.v ]Չ4#ndS$xԵŽmYoȀNSrsDX#w@F6Y _f-L9*x2JӬ$&C2(; uȽVO^$`ܮӨMOV > ~yaJC{nBDhRoHYa?,w]Ia;͏7Oof5q6b=0ZRi R(ݜ 3j\yIBC2}VB4ў۹. iIӎHAkbCQ6xT SU)d-TEtrPoE=|vXd۲^CP'P=t.* ğX1^{KoqfTTQxŦhe[,?;DFP><@i1i 2/F/%|z]Dr;Q@yc?4i1@DS-&msJ9Z)աrn|qSK> dXcعЫB A!8WyFbӼiVQzW2 b 1 ]4 u kd\jUtuW:n^<+(#J-W49KȍvETD*R \,i1/2kVv_B>. )?St-!:xn>\qYvY7?IV §pp҈xU)fpY`3TSۡv­̰ Kpdib Zr|\Qw$S3LܶPY6.4K;(Eoze xB&.WiK(*>+gH >'oAҾ]x,XcwsBY"QbH1YEk= xu {=/=mDîv4KXuN3 G䖼uj@߷2RZ&/ ?PuCM(݈ayNœjX@u}J+Ɔ7TEnʾ`μPy^u3ƯqHo"=焐؞Ѥh.Nߤ٩4ȏԪ}poC9kbPؗi8CSq'Ц׭ʾ\;G;x[qVIV$oנ&ZFOo0`Lנ##c a &[-WNIDZ=kj\J+"6DR$~bL~G̈́pt;)<Җr$T"rBLf*Vt+/8<gG=7U#IdL%:(,zS9Lf3v;Rf4bw{M+1`kxgg64gcshQ+6>]!NJNf&RP@;GwqҭLa"%rA1Hh_ۗrD/.10%5-4c?`CT!`]Kg >lZW"׀FpFL?(=1f7FR \ i@M~hW*P6wC7ҨF C%Zψܮ u^ފf&>'L:Ƴ4.[Ry@grЈx Մٗ%~xPG.RGdݶSyK쨙 }FU JS ,Bs(`= -] Vcz^jMTN<Lb fY@q C~' A p/SV:vQܽro5|0ɖMg7 ð`b[Lkԥ>E'PG-CBd6?ۆ0;&nRUt$4 P|~qS7v? (Zej^d*-)ׇ ܸ#Ws'ho`|Pp>o+,==}#\C4NA_;Z0r?R)pvW̎aN]n+9xq.3qx\Yy \:Zȕ!{Uҥ<# *iN}Q"P>2Es(,e _NJŘ0olc> m%A ?1D=zG l7+!kmcyޯ#wryȿ&[_O (I2C,e00=$>;W#[#E0 ~iQ51;8:x.&˷U^Պ 7#pwՈ 1w1_ڑnIGҧj5 03b.`p6ʭ¦0RTK,W\)fJ!o[+}W_\%$b4oݏmjt 66\w?Dmچˍk +u^qw*t{Jghjm 8P א$+ftuM)CA!X{ ٕ45,c}EUz S%Xť'/,E6$Bw{i26tXW1h_n*<VJm$aBS,4ldl$ͱSƄ_u ֍\ul<TVlwZ[@Zo.[Qsw(1 WM^/xtg`%ݠͬ6j3؜np`9pdKloa, yCᙣ)QF Iudqӣc)vc*x$[N-ޓKg$ǵQ]S^8I<-A8 ~Z(Y :dSjUȋJk'P<]k[C o~Y1Lyb{\ic|]j}=T:&ؑR $[ bۨ:o~yU"03G]vo X)£N@1Yza&o\3i qP]Ȋ\ R/wɽg{B1~@h|7?xV[ |OABlXe+4抚a-Y23%cs'8J ZLlj<){e , í} h=k{I> |c7So 9( K3ΩzzNeƀAR:3grE;d( 9SQD˝6 l|!gPuux@)_j~FgP( e2l::;%WuIS|UR$;+\qH˖:Pt<ϧ{=(Rz [(!pKq zAӅu밃ɋZH8 ̏d"s%qj A n6p io%^T&때⧥q躽 BKȈq){AE`9zf&$?BQ05o&4nfIripU?40tZ9IhR[+jia֘'ˀ&}ObRĸ">NpU29LčQNzٱNZ˘LM'9- #;@0!W7аK5D+|Ō`^PIe3XEAIm\?Y:Og)(1,)[^=CQ1EГcGEC{- 0gm2+Y$fM=ϷD #p d$' jrfTQS-i3(uWY<Υ'9[/K xtUtWn?אpfV l| KmyÎ=Y;>8X!hGXCDm]gq/s!HF&uvNm2EW 3BA8AP`t)FѴG y6d[8 YߢQ9 P62nӥ&I$/W4@i?U"b"ů6my" D3`AGD^44Ӿy0TE,n.nJpD qjk+rtq )F U$ݶc <6hH@bEڐ$mt}uЏKuۧ~&v i-,ۼgKmtm ==OG$HCzjRϏ0,s>`o^(4Wrie߫]*SlP O*1)1îQ@ )[>\aŽ=/5 [>cdR=AQ瀐Uݍ@TR`boW.auYgb -u=_ r!Qp>hzGLQ:6`D!okcM O5ׁdem_ KJbUf0g4=o-8$nPEyˠw)i,MȂEhz/4,b&)ZϺ [,` ߷xavHl(M"X]DמrQC\XFh 0On]*E- YГʶ.>HJA2M$@]˞cc!eȡVH(_d8 xs-Q"sēzbסLsa{|_@:66V :;=ׂk\k0[%u}2?8:),C'e;98 0r_ٍB QMJ$㲅bl|6§FK"q:ݾg*XLSzl^ߎd#zcC !MPnLNez}4S t:U&R2`3L"tJ`:$I-y/BٝhDDGj?~Pb[˹K&؟ :\$3Tz6PIF\_~xJD1713q!F^(3ñ O$)-_*$W0d4yfZq3ECYREqoś^ AN88cAZjvxu;?.b%k/-WYB6BR93^@TO}(h@+MnQxȸY n'z}`la*6(wBAq7 ;NTT\R|>/W5ggQ iUcqp'?:^r#Άkg! 袽gX̌gn__N_`t67bo{`ň4oPC\J)J.@7vKYO1YƖc r"PNۚ=LRf`!AOd4k7|]]g2.-Xook1zh殁ߙh7bW6CuBvh XɎm'7bdL5䕑Qe(U0'EcŮfJ_}uxŦ%)G Ylvx7w,l z𤈫&ni,Tsk1&=" NtcnRdxTq7w_t3eXz[ i4l<4Vzܶ7~NΊ ,Yc]< ԹqVdW~ Tn$/cGa ĮdV4 ;.RZMMrRwD/Y%ZJa^;;o!%T(^߿7x'%T8t Sd9(D+f#ibyޜkr,`k0~C9ۇj|ݾt A@o=>D'%@iUT Х)c>6YiztQӓՓ_gSȈ  +=GΫRM6I3z7 ']%i*yFrAX'>P|{fKا@sPP-%3RP']SQm,%-M 9'"#a^|zz!Q>V d+kQU.Z!L&bzo]QU$ /1V ͹?ndsyھ'0 @W+s"vZwHgߛ4%}NzgF1A/͂b٪Đ5H-t]-#\oYSg^eI *_Zo2ki 3w{Ozzθⅰ,T)*[f+?<.ctا|'VN0m4˶׭x"ï(s͑7x~3$N"P}iŴWD|f]W↭4U*X*2;u]ZR&;MdAkwfcx=o{%RףOOVׁ#,=R1JH :wuM_ e$U{Ƥ4o#it3ˡj'Os)J)i🥔'dwmlEPqUKwj:\nICyLJ ̆ Ehx Fxi~"Ş4@o~|VjUX/S@%^hf>Eb` Fv& XY }a9*ȁg< xi/dl57S嗬RoJT~&I?Q{zINK]+y< g& LH8{Oac\y~SMx }`(ԅ4Sh+! 녙l-.[A݆ōBE#'} D+u8[ˤ+ dyE ۯhc,:Y8 jFHr&D/_^}1ٮ,MvԐ7J `M1sfn>lX󿦎wTVC%OrDA1uX|LO^IhS̛ IZ$=xW0bd=F{e[sMۓE(SqBXѧKZoXf&56X j|`Qb-s̤B7'1`Q<5ؚf&D%ĕ^W W&`Ӷ,Qm茉D@T;VNpQ5B6H&J{.R>ҝ7(xZY=h.Q`U(~X a J9IOt7rXYr.A\#(gC8#DWO)5pvLue>N{%x}*ZǴ:?=EEMq1ądbSFk=aˈi$]/KG&\oY3L 賹s2=WM8&BA>f丧$U&'+`E%a Kұw*+bJ•2$JI|mElzs4O vu0\ܔ 'D!ep@&UﺎO_u?TҜ(S5IK07Ј;#ŝ:?;6DY Q(K v\0,@<=yJ&7Z_[R1 [wF99)*0VBh9= =_gT\*ܙq߆k'GEMr!_lܻ~:)C&~+mȈЬ 9Anpw S9@/~9V{$wG" 7V.HG.Ym(a2zJ#lDָysoQ롽eAq:|4,6z]glm_mwrJސҶ xz|=06Ua砼#|Uc$.UdQu_^)DZ/cQn[i]ň 57p.ĞMCw}}/'bĈ?wnƘe}wnzFo? pgeS Ob#@ o L69ٮ+EFئSJv^\s|kWH;QD6"t-ݼ5vTZTm~ Gf7&1hj/aQ@v)٥Fp8M3|} ctHrKz Rl/SoM 6+mҡ4 Tm[*!E39^1IZfۑ]-`A:4Ũg)1_kU H*hv~R5u2>lF@aGTgJ,֦;+YVJC9NUN{62ArNp4D|rd䯚k W8̔n:' FH,:1*<³ݫUl`&y7BmE;TS /CÂL6gaOcy[!=&ϒD0HL׎DWuí%b!X-R)()}x sDP Z=IS+UJKžO}3ܹI*d= X;-GhZ檃z#LWz^0lSW.x_dl@ <$Bzt[`3D3Fw$7B^)E8^_(W}L32m"8sl(\xs%so<\-&TDw|&tQ~xɚ6uEZ\mP?}?v~$XY6.|ggtpifIoZ"e'&23rzxo[m w@ꬤiO pxHK> stream xlxspml7m۶ƶm;${b۶3L;|un=W盛ɉ%]ռ L<U 5if&&&+93^Tf 39@@eJ$@`nej swwrwqcpv`g*p̭lQE%miI:@ht6(ZL.@j3?_\-0sg;@\QM`log ?lտ 02u-围?i37)#}<a*Tvn@g jOgC3/?ta{gfc`bOEhdƶ&?#4r&\^m4SUEh;.ۛ:Y[T]q쿉3C1ƮV]& ӿAqfг0٘ܬ,~ZS7gg]=M@'nm7:%_hvnHvtuFj ^ /p6ȄrG8p<9KRBZi_\Zip kgqus}-Xf-;FƩU9֨e;⃽$>%Zpe2Deo ,"?dв"0BN{Bԍ؂1n:&E){$Tb̞܋Je`f7[3\d2Q1/}ݑd!\Lq\iRFsn1pRzV$gvH@4OB&8'gS]O4ilɡImHjWTg9{h>Λ5NJXno_1ؓ2BtICD`eiw_ $.<9T6|1d3@Q4ˊ:G3#7F+z0JJ`uJZe$nh e0e`}J trQ \'#|ƅFK}p\k*Ww\\޳0ӳt Han| Ey)ʋ3Ifon2;L1/2zD{(R jyʻ(8W၎ R9WMlg∎w69j[.z:ri!9(^fm=rH)#`RYue|*3biI&;M4:;|3Rqۖ8Du?O(u;/Jo IZ6e\֚fWR;=L\YagT$o'ee]6CY}W:OSɐs6,ާ kILpOAr P2:Jb yWGwi cv_ ~Bo<%^?Ҋ>P(3~x=.M֝Q<Ώ0.\.Z-<@2Ք2% VpPxqZQHf30M;OVvi1.7$'`2J+IlcP:!*yo6%~=G:ַZD 49_x7d/:YUԆt릅j=sSGGi`QDvWm?fJi2&xpZbMj~/i`rH}SG |v9dQUxk'k&'isgric b&Å}G8&(M$c`P@vT-.֖5 [(>vbU(t+<}:j@jF π\|6VlGFIBlž4vcc9򹴑&l?8Än&Q8 rTx;,S U85]ğQ#b(#*`zrRA$@0 ϣ vծ̘!3B]TΈ&#I!٥z`E[!&ق{1Y#DAf=גRUwe%Gӂ 1]ev^ep~HDy=YP ?BoěL9 eՕ3~Rqmj|$Itx+!t'/F" G/Wo7N^ET^9H &rrW#7%؁ynTE_`*\ކ V;(#5uH]Az/XW~o'Tdc`9(ܘYZX"ϖ1z:<:m`ئOTlL[Z DԌdl4>G\ʰSj'FEDStC&:]􋆀݂=C^#gItLۂ!D~;Vۍry|6BN G_:_1B_AiZQү\\Y-[n'c %j -1Z=sΩN_:}bVN7=&i %/>*<5u?8MO 7%/Pŕ@^%D /u4j6;}H ;B{Zk Z1AHqIIwibrz {w\V>oWU _w"!!7+ʨBn.GFR.L;i9~ ^j%qljOH} B< /ŝ? TN/Zk,͖% EH ڥcumKp#$͒p{NθG Za/4)OR`@OegB{ۯܒs^| с >vZWΨժsLci-W9\ոQU[*Yh Q}q֭AL4 ^6 >*lm(aTЌI``3I@- iiDp7~jh'K?!0J"|t~_v 8UZn}zj]zq#ҰSk_Ybwl+].:@\ ݷ⸪:n\  t v94&;D\)3q丆DǎXc;D=caCd@Jo|tc Z~ͳ_s FQxQ_p^=<+G/sLmqP4oB [ZV?fiH.Ъ{QFc܁ 9.Wb)@$5;_%荇SZ$u1Zf/͞J`jSoTC3?=ї9Ȥs#Y /Lש;fW2v&W,dKMØ(k7K׸d:lJg UԌ%*y><}C^2ovCu!཰dƘ9xZbxrrUR6(]8(F9nq||ڈ+>9 [$5N90x -A'l 0i;/ [U*s)g\}'?A٘ ~k9ApB[/U# %Ճ1?i'}=r" oҒm}'sQ}s-~e~>M̽Us¶p.Vs7{D5e>~NWG/uT(j+{ 7PvԵɏx8´]}e~V}5WADѶb=@ ݘ$GbX| |LL8p`Ne~8#a{CA[>s9o.Q&oZdgqOc!&-J'I FE-\lkȈCZ(1 n82i}׮Er$ArhR&7ơS Cy,}چJ|eYVXy (8'`jBW%I %{)`^lY>\ E + yb,'ォ[q9Zo*?ָ.G]:.rC;|mNa\laZK-uh{FnB3n{vNn:@r [5_^K|'P[a>MGY_qlW+١c#8bJ@y ,"v;=vB2'[2hQnsPp/_> QڲsR"1"AI(}5%ϥ(Lr% lA^0i >Tқ* 5Ly&A; &8A΂ N_a1+xlQyQ1r:穠|}v 1n˧o-'GD tגnl cqyjǜ;q_1'u9VK2D'L sB P[sŧp`OmI1ڸpxu l)E]#qgΜB{u$&N'.3!hҥekI |-"M U ^fkO@05|X\!˯6SA`2:!a1p&vsZg1ηGVEJ.xy1+ Wc-֓BzVu zøsn5X6 Y;#B[ta!יrXRKOBBâK g׺$Mf⢜d&S|Ȧ7395f5+!7 90 goug fJA4l=뭖4FfW'8I}H_򒚊o6>A~qj5 +h+CڪM6[ܰSne$}BȔlŠGg4 O[-߯íXGM#)Հ^? cns,o 5 c=@yM1q_k_v} mDC70KG ,ЏV :D ʻ\p&A' N4M}R+<|$XN$đesz7*3^}{$t~%ZD YLg<-;#=01*3Go9KڼEU_ATXgPC0'?`EuSWFUoPvM7D%N%Zzi:7,N񊎩b.$ VDMJN:c`@ YLrf}p< )OE⇳8wtk67[j7oV& VjŹ .z/Ќ7hq'ST|HЁۘ_4qlB oR3|\.R9<Ӽt@ͫ큰;`or6YlvċvY8{(A :g7hsW5`^` yVSejd 5]Bf1̲҂Y]$?s`.Z6٬2}ĖBoml'IfTokU"&~I8bwUL"5`d9Z渐2s%0laVO*4=B~޾EڌgGo7HKmCsBNÌ]FYx̘ G_0~9O%]SJ>̯,Df|lV2#wU+=y6rASKF͐[}?*1}x? ]ṔFb_&41L%5JbfAOyI_9>S֩6? @F_Jb-N1]@v}o~; sB ]ٵ/҆?h ej;{%aJ !:?%-n܆ʌȭUXx1Dљtt*E<#[̟Q{Vw'6d>e[v"Scjnƻ+W-E?[@]n@gw0",԰|>zA>,Uy/-kz<) s3Ga%H|ٵhS$J:†;P2dW/  XY]hs6k:Sd]Q'o\tֺ岻S=Haܖ#n^[3cjDR?8u5]F_B;ǒORC4\.&'rIiTk Jdz73E#0xIr{<ڜ+ ]V^w3NDlxwq>uʕ3<9Z6Q'/2@{%z K0 ):+h MXj~m%GKV/d|(Ra}t"nvJU U\ y[:7ݔP;xQd,uL?m~ V)WrRiB2_ZˇUJg`WW\ӣwT?Ș_&A"~i`/{#~AĆ|ǜNU^OXuT`|7LTA.(% u3w+\Gj97P|ƈ73-&@^=QAX(5-"Q˿TGlx6L)44syZFޕA:ZiK,xJT&_T@S8 C:[ev8,*aXsXbXz0=k5hcK'ʚ+8H${2;3ȝNyVwXWjDNl  pˌ񉧝J~lBh)OLLJHvȊ-ti XjL4*y-0w0:p5Ő>ȏȻHݖD]k.GSTB,=m2":.~ P6<\ѵs}^';)Cj[hU_/zuN(|x #uH̉>?-˯ˬdcҔxӒ un ҩ_8nfb%?`vi?k`7Hb$PnM ;2>uB'E9佹n$|#1Ӱb3;SS_C H{a[!ijI7 = ?wwA3$MBe%˔76&zVdb{Zضߙs ?QRؾ^ ]] }KCE_S_Lڇ2ry ٰU VSI`\kA!+ '靧q~Go`~%{>4J @sbru*mxZI)G,qY*b;2:&wk^`#AIH!&PP FfdH&-Q%RnTy E<) J6:9\&Q!/W@yD!{ K\\qVf^X d(vGȣU7>,܍g̩oOu1&iG5Y#rul2em4YN3Ԕ)n6E1,Ny8tec+[ŷ^/ũSAQ9'q8f!4f /v0$G8rǸ蔱4-*^Z-]_~ 6tAI-]xf6fWw;S$n=*%{^T] IoAm#ySQ|-7xMj<%,NR+P?"74e\9SWM6$+AU8w٨6?6bj߈s"KC] yX#=J˔K.ڮ7z3suQ.:#H_5"vPZƽ\ I~e cRc,Ʈ,W,/8k/NJ#*e4sBt湌i(:k"oye6gza.u;#߿^pf'OB^"vb9v(AHwl Q-G0A g$!4$̅VR2ha%AEkgdǩMI 9oz[/)Ԥ3*_כl  _"0x~:ZߊЕxO7g,ƹFS [ۍ晵G 6*}q aɯP^5 Ɩ*ƽ#y*~CY:'EflؔoF)K0'HW3<Ue4~MyZquCjҿٺ!~ 9=`r< qFjS-HqE$ ۘT3 :л }Xs`'`6v'kaȰŦI7\A3g+:~5*7p`/ NWǎ*)[Y/yYbw<{#Si%r/ċ)G*z~pXJߴޣJ͊OGe`|,3FDCn}.a6O+S (-?s1aOpԗ8|!R+B] !kmD7Ajcpw<:d\2)zImeϊϻC>DžY}P=xng3HBбc$B<5aM M}gCzFɋc4 |Mj;CcZyZPnTڔ[p3P`V:$a`>z2^AîKebKi ؄-_@nκ%ki`. BY Bl?Hs?0 IŤw@שL"FL{(.hM‰LMZ8`ӟ󠉇%̚z!-F6ؙYAl`X+bNuea Bdu澻X0 :~adCiOJAǎ]C]I0erҝ`O235ThMחrry(y).Nl{G[C,|JYGAU + h$mN5FheNSY7klȵ\iVQZӅ[6z(ϑ'I4|t_B }^9#v:]YGOgz7?$BYGL y1{J_8*9,7Pe`M F^IT95j<T9IOmJBgw{A/qWK\zD#+ȲU=Zb*EʻަpMբ4IΏ+U9]-y_]'ctG->Baܨpwo,,،>! 'K 33wI*7AVRޙ,-cTʟ: zV |nuu%u~ڊ*hBս$K?f=2H2:IM`#&hO#MT@wM.=z{ok"aa$ Cr_\co8B>tO^9rYI8W>D)@噊}>^HnjcOFv7sӖxHXRo& -Sg 9J\2]4Ÿ? l2Wf8 1cH7Po \s+ Cl\2“ƌ'ij~r?␚fh\H#մ@p$UXa~`j6&U++/5bjv.FQnc8n>Oq򜚼??OJ,o=316 5m)ҽ7(hjg&dqAGk*sqpY1F=6pSnISn RoV5AzA qsCy˔be2K]8ϞaEk?iJmC v{#2 i9LXF7tI#q Z e\hS[ﺛUI&Jy5Fn*d6W[%&c, : g'Z9lPU" 8X]@*D3=NuF 4>oas/A&) lGXen e+hT12"([-Í'eyXf=m,2Ӆ5;ErR?9^~H8YGZJBjO+AKOX*zD@"E t,'\0 vy _Q Y$pP Je),L~=p$AwMM)?2apFhg$?DX ^"ٜβ^7o,&(OQ&stshJ]#T@o?҈ 'ۏ`uǠL3*}؀u:cQQmşC[N,c 9t$^ȣ p3 C'T1vΛ_5!QEa _٫c4uFf”/qQDHA~M| }Zt$ 'nCNvnqZoԞ$eC\S:6\2tr~,ވQ_pvoupPh-D(. a DpfUH་ʒ * aB.L~y zeHPߝɖI$#>4gʙthV# Ih4g)2fVsW!Xb| ۂ!63i(JssռW|MԹ,g&xyqQN/PqPũM"Kz4.:"!$5v]9\1z_è(δNL[s L׷ěK;ξzf#~~a]X πD~|n2v6T݌|Fqߜ{}}oq2؇M|ܢ9KtN;ۼL!sR!j%=ײk{?V.-J l<]pʈoa7KQه((<ոR;@K˦nav9#+@MZ5yQQnޡzV,l(zmJX16$gUwq sZt\dz,>6îq H0 ׀su{WoӤݝ\N"yeWDY#EGvg`>NԓMK}RKy *3Vͻjo5fye]*78QiX7o}pc4[H[fH A&0RS| վgUO r$>Kȼ)ɡ2]5rؔge`k9V~cb7Qh Nҙ%iOXG1vC.|}ӗS`di ˵ـoo@'2ՃY4Qh.>.YEQPƽ0 8VED7;T*B k'_ ] 8Xh}!q*^"Wi_ 3uE~zep^rMoݣ}|D֯P$Cn2ԬKԣl0&mGސK攢4b%(ŕDyCN0!Jz`!R?5.N`qLܳ&W]Gۖami7'pZj1A$Wu.gvXhqg@tpiruZZrЀA%Q8uk(գ  3Cf4g?$˞#0 髤oz+Am >>%0g?mW; &jU1@˭El XEMYMaDGR=J~_- vզD3|k@{TA"w 8❀=z0L%+\C .f"yjKMSqHSIrIVSqCv-b#h@eCaA}ETFc\_3w[}J-omYru+Nu@i$+Awj՟p"6ZfpѧWcvr5\m=}`nnXcG٦w̽ GofNoNfxr)T,MY!w endstream endobj 91 0 obj << /Length1 725 /Length2 28539 /Length3 0 /Length 29084 /Filter /FlateDecode >> stream xlstf-tl[O̎m۶6;vұtرm۶m9ԚU׬j]cMN,nodD PWUebdd0ҳ8\,D\ )@`b0322D<,-\T&6FNu{{7K ++??I*@ 0D%Tj jlci49fN,&vLovn@'9TUb" "#;S?v.DM\\U,,zX&& hni/ݤM]?ThH 0jc#od P:r@';wwoor1G !;cd,n4UtGo'W` Fhjjt?sv6wJ~ vٙ؛ZڙT\,Aߎ0@_-뿖 `b`pp/&NN@;O-]@ ꒽ /Ԧr?™?3SjKL˓]:(wyӁ|;Rw$Fq@hp%ܗb?~՛oJqlM#n]Vi&X3sΪKP$ z!W$(Q3{U3!W/?wq , ´^Zh)2uHbnRldمLpZ:}c,U #PZU|cq37K2E%='F.w=l˝&".=Ƶ>JCPny4`IR4wtZgM.n*^fnhw /+r 3r"_@?bgg!8 rߋ&jotfKdhbJZf;S5'g8"y@[q`7-]=uX7@Њ*MnFQ L|~ #!}7ۯ !𞥙s%SkF_C&G( pπsa?_[դ3{pۯ4W,5c@槿AX = Q d1 <Ƀ? r>H/htI\%TA@XKiHWa4װGFk>!z`pnم"] v5/);bCwWT+U=% Oi82pE>H﬋) ;Be^yTHOodBחϝ"_etʢ~/5P_ȨAhClj=rZȄb}.rO*Q],3y5` bR12:@Zf(¬ڏze B2`:܋ Y@RȺP/-#vJO@a qHIA@5%E*n'(x%a<泺&wvixAǶu թ5$c`C]LX-dF rSGa8i0WKS>/3Hپq+¼-,{a>~LXX+3F`hQ!_u9!-KIj)tw1ދ_G.! t \!D(BDcVgu<}t3i6ɠzͤq E|WsptL_qNau+Ÿ:""s8TerN׫&w6%?N++wO$y 'K`jN]fp3X[sle;Goe⽛Ɩ;!BcN1h5p%q;+Z2%[dcpm,}ܘF\{VԟAE(RڮHt mk㐀xo;VT| S[?q;a|t#7)\]djmMT6ӞrӖYCژ6C|&MS/(B,K3S&LSV(1S:-w4z:?]\%c\|&3: ~٠ lNGՍΰie 9$UbTGagUW8.oPݫa y4?؉H6P 8h׎>E=+Ubta{A|NKZ{-q B'1-.|*th-sք *5vxg F V(q$CğB)zy6.7&S6ȏƠvɺt_(r3 ʵ5 P;ok |/b븋-BO8qr;k͍Bpi/Dwg'] URw̏[lXڔ(ј0M|*K:~{nѼG9nthGw gQZ}GFKgu|\c:I8>G7G)n {yW%w0>EiH}eǿbCUow9z# ge̜ڑP>0h9.k lr6^Q}šr16w#2^&+_R'/ѓHZjٙEHdƐ6bWxl_n,QJDEs_4\nu06 EW=cQ ӻxD{OlF:s@<0&'CJ*ԭNQUuE-"cfmLɸ,ab{ɻaުqRzEj@!eڑ&JºU U=5*(5Vvr"}w ~[*>n~Wx!z5 qinCnQ Yq&B1z ĵwKz l}${D7[s8>D|xt333B8eGT ΅"u%N/h)hC0E ]_ClF 9ntnMr Rx7q" ^f@%=/+Q5/AWvO S -SǗw$Xq]ٽUHï 1RvŠihW}U@b pqkhjขۑ/Fi]:x 3:[o^JOL}̴0qd?t~7)axڣv>6ޤXeFoe>@:@d],D  a 2.G4>VNe6ܚcC$w*;1" %$([ږ:R^YZ%iU;E7kwJ{=QL2 )&)̽) 7! Q|D\Mүbbi'PQ$.E4Q0[^| ^ ƲbGׂGG[ht8O)3l(maXF@W\D R#lnږD խynw'ޯʛ0 S_+@$YX*w,(J'HCt8jbe[,`.KzCE'h˝ʮ`kpu4ql~Ŗ4&DPi@?rƓԎjؔ/ ˥=O&-8)Ijlj~/wM7o<9 oFs8qu@: OY޿} !}XŠou; ]|P%ɪ`0H{#PF4]K\ΐI\hEIH"8ng5K>Vb|wa0%K肿zڜTS2Sq"B/ yu m,INHҽ٤Y SWO {DgXrs().2}y7& Vc<Odwhֆ@/~춘EcFj܉L!-)춡UD]צW`͹oʸHmϗ\*cЄ[cP5K- 8a_uH@*ŽaX>Am:| 9NK5:e7>]MuAq_Ј"+#'x(6s[Q`?oOkbӧb3;g$*?IfX< it$2]!IՍ o ;j ނh[)D>~{NpꈄGBx.IGMqh.5zv9Q/& RJHrn!{4ԼE *zȆ;^&XZ3Вɫa\"z{($۫ĩsRn#145 oJtM#~;|j#kFcu8kBSQ^xȽmŔ_ԝMO3#)Y*;ܺ\")?CO3=wy4() Oy:jTYL[ϑf0๶k$ëkSpk/Y{@F}eIjln&,~C'z'Ya*Vn 7EV♟ʭ_ O\=sm15Go%m N7 X*ĂڌaĽ⚫BJWM+j`9i< x1'hZGcO5j'͊}Ee܊1c.uXN:G7RCtHW;)v9H;]H.%]{Ǯw~`HKa{Ae+=x~ON}ss_#C3CH WiL#VuWԘQ |%glUAIM !~9cّWʨ*|(x]K+.;l9JbLb30eh\=<ނD`a66CE\|E\ ']k|`g/#qnخx=2rLF_#Z$&4!5gaã ;xUD%&8Gx UY& qҝl2$_ȋ R@#$ Yb p^PSѧYMU|+>j&`ʕnTzYNzU)~.K(V|{}v_6 -C5I{-EW*Jx}qC^i$za%jǥ;q5NX*(q;RhR?K7yzdJ\]^0'mh.jq&lp?ٻQBbV2 tXfaY̸8c,Ygjf@^YU`eq!M+K`z~ShK$< }2ΔN4/lgrYCrJ^ #u䉒sGlBc[醿] >Sj [j񓞄<^eVҚ_"]Vwkwak-A8236biN 2 )}&#WޥTz%n8>D2fƒJ-U/ý3a.#R䙵Qę*tsٌs >~B́r,Cyc&㹿 E|xE.NM*T4<\ҦԪj(bc,LLhJ(^X,9wMKM#yK6|u$ԯLv~L wfCA QOqI7y\M89,tmeqtj;WQTFPR'R.~}>o#s + 9J 7U [bٹ 7]D4(K)@7 eR8!iy,_-~`F!ep71Կ$?nl(k>e_j !d0EGCL)$+^, D "ĈC%5Z^bh°&!Qf'ԼSBI(M؊`0^#,2;NQB`Ar?)v8',VuPDqOe'"Eɝzqg`J|q!I`!(&qǻ&ɀ>^:DS`%tא3<"h~6.X  NlBdxKiU"J ڡX[v2UNE9CM̑B!\ΆH(]|}Ã+o)_cYcW`hfhW0һw<8ĺH91V] q^)aJwhY۱Wo`ڷ8L{'u1Q ,m왝 q|l|Gq4UY&CqVf] Ui̕ER|B{HOܦ;ٱZ@F.Z+_ؐE[I쵣}jd(%O'C#1y~9edߛ[:}ʼn f Z+Y0#ogag=i:3nU/40:kןF%G_[E&?N+nU <{F-JQb {Nmn-6gR]-!ҍeC&s o2%[uszp*@ڗL!CO!5KQWGQYvF\*&VSSnuL+97ڥV']zq2!>Ɠ~|dwC|Krye/ζ3L4Ԥ[9V?Km?En듲ʿC/+;F;P'ٴ*5G T9?5d7c @vBevK:Μ_I[аlA} r4O'wSns'ЅI6* 7DKxrwaw3 l7эzIx]DD(c~a3?6~lJUm/̋V 0[nD3`gHR-Ts{2T'WpY!"/U=IPV q*x) h3pYwl(YaB1QNfx#Z8Waअfև?>=plA͡bj-%%F$gz(!GCop09JSel.nw#G|/|> y)[u~~G}ZA~y"':]a3nto8W946-y@Rę{ nd;BK9w(lo3ysk=>G̜֯E&&sԦLV \=˰_qy.x%FMZ%޻%SNy9ǞR|̟0F(01"П砹zeRXń'shf B Fc,rF).d3aLnqYlXwV|&c/ ԜwI|ͿDZEb F4=2 o{n$ïoH!S;f9rX"[j@R[ЋLU:_ask&Vg TL2S\(ulL@zysUM$I XHt~Xe^Dʔ&2%Ik%dx HqrWbe }tl#l##htd1֪~w<#lyIppݠ$ԡ,AEEx!/Ǫ湪dxl yhC1D ~Q$Bt]ѻN~ՠrEcZcz:~veH?-L<(̿;*Xz\N6 +K谙<e6;2V*yY\0T"%j9 E7!ֶYn7EҼ %AZ0ҽo&ch]#:fMBzC'-0JZh"RIدm P8eEy҂W/:Zm~Kf]ב3=| .q<Զcw~<=T0Mo HY^E@5zR$A1X@b'8+|hY3p d︜Ku5;az ϗU<+:-Ĕ{r IWͨ3}O5қNt91p)E>ΓH*gPyլXW ;_#0=*fźb J5'˨Yvp)L]0wYϭaNp~&!T[/jrD Fv?=+Dfͻ>.KU;mwht:[svt'P˘'p^L_'g \?wr';i7W]3Mk ~G=VxztI^Ǣ")<>f:@$x,6ϯecCyЖLȕ݊ 9eqytswxײ&պ[aԧ39٪@*w kEpPޮBuZ&p7A%>waٓYQq \>Ul\p~NZwv(FT܅a gĎ;9kفu+3qf:bSh˥,5T24oźlЏ ^C".х&YPڦpwY 7Յw([E D\ _j?l*`9WK<lX@lUl dB|{.ԴK /+Z;󫴋!zqߍw%4v^C7kO90A;PmٻEqk5+AnR01ɐ ni; :IoWw&H_RRZs~:n4XrF X5C] ZL` pReBF>u4~InpY><'H;B;Ew 4pc:t˙O!+`3/ܳ ?ogZ~"//[6a?J6j,^h?lp}'CW&hV}^A`toxn1~'W:̿GEqnUoc-׺gٿZ\i=܌KT`Υ;\.({B,drH aB0Fҽh"JEFuyP^QkBOv"`s5'sbO`\#i󹖦ZFg Z:4mphVw!ZG{B-Elڎf| vf`z<]T!L8H6l˔Uq_8DyСE.K&hBַo1 ɔޱFe)2ڈS肚Ì.ZO W/,Ke6܊.>cdM[`UA2-~j~b8"i+̎{ /8d( EiTFUk,>e- S@`+n،/;')ɾGbCeG)â۸jy#J!;něB q&@F('R-!:YVbpEG$JpszʭNO̡hA@wf`@z$`z!|11)87i`uaf`f|}J TnOܦ4 zhUs4G9t #рh^M?ڇwBP>\]'$@@Wqk]$2GLNpB72^2rl;@ei̪{HW^'kEUY-jH>޴fXK9좡Cj`ݐt?p8ş1@ye]YzJb翠RC:+`a:t=V.ȜʹJ KL5+cq2 4C?]\yNdWh܆iؤ*21d-/(E Άce=]A4:a6>KFFY/h<{a"qw+حu)]gYlFo3IRvRJLlx'"dʛ{ȓtY|-gFE2OUc3d$~^`_r#7i[gdu_lRBʑ<U[]j3TlEUZN ѐV1TԠUo`O0{{RTm(F'f!t 龺cJ8z\Q׵%sGjۉ^s2-% 0549g;HΔ~&fBQ+Xe;gjv 03,rZۡ@8ǃ,1|7աIP{LqU`hYB#:/;Gm 0mIx<oyO9 &R˙5]?__pO}5D8XH?`elP.zG~vL./΋GKӘ1sᚿQ!w7kRhNsh Fם6eWZW Ȍ |N jb<8;_nZCkdi߼WwM޼!>ǴbB.7Аk SGG_` x0w!w 4&C?sp,,h&g}HZR?b&5c#UN6gr)-& a&2ef&oEϘ52 |:h5+Zy˥TTOS4_OׄxBQvFT1 rEp~U^:4ģ;ӮmUV ? /=ݒ~2N"cxC<)Cy'ҎؽYj'70qhk\.tqD>5^S]@I%aFЌ%<߲/0㢋Ch`S 94\LV J].MiR5ē`W[@i.ݬx`]=yG8xJS@C$>E0.Yw2az") I]Қ3ӿ3@r#DOww|wӰ8o5|x"23vVJh.x\^bVAL^z9{m V9׽ڗ06?#?u;_-e2Hղ3'P` Ӡ2ϕ"st(VOJg|X0`wS{dOd;\\WH@ 4Gʿ^epoϭ$^kqpﻂT@5+J]WQNA6&(-mkf\iL|ɪM/8S9mqKV.YPiz.-8eodtz) z4,K }p'Awl{L:4e7Z$?+ǝھԃMB7C3OE Q\%Tݡ R9u+p,7d:xL'e >NFd5yCHR^>AJd}D&d)r3BOdk7%FVJVI*WM.Dl:gз3LTz*ˤ = j5+ҿT6Σbgjat 0cl#`1tHm4ծӞ 7ѻr+۶oBʣ_^(]>w2L06$5s۱ʙ ĸ%GB eNW\m; thpmœcfPTDLCh'犴jCؚ/y.&ĹZk&\{H(&l󯂵ձU^XcpKÏq}¼)?_yqzX n#rч1R"h"yQ%*[X(<5re2M+CFMy '~|Ņr:%.i8d6~90UsqM-oPvZFl9dP7dUfB^8 z?Yah%VϟbAXy!B5+ܓML!208C@P/kGm59K֛{)'/']<"9vIF'}GlyZ&_r˟375M~&˯G?jUTN6?!1"3.G)BU%Q%][~ IVR%~{X 1f/P?brNRlTG2,t(PԈ,[2yezL+*7ԗ`yX[K>J\6l E-T{[:\7j10XΜ[}8wֳ#w(~=!-#̙07Ps܋ F'#eBP"dMt+0Q뗵xukpF16FW9H|8yȒu$ا;6#1Ac2jNQvW}Mn`PDyNV.'H\wQMmep*/N5uywIb3ٮ XfGPkvңr3qmYzNSV`L\x;'3W# ò~yrdїreb=2T7! 0|q1%FK4^[@{( K^ *hk٤l]lw >m9ֿz׵:!waq-9Βe#FE7;lt [5%yVLA [|DXGsg]{q J]o 50@!@&)ث+.O^p5-[ #EXPnQW,q<)ͼҠ5kяɘF( Q;CP&%o,LWL@2glQ'_@z}e2w1uB uyzkT3%шF4HDubyKR'+LMʅkfiW>NJDM8gC,^Sߕ0"[cdTc; ^Hr,4ay7%}`jVk47t=&KIG1^\ºy, wRHԹ`"7۳LDm U,Vէ0m5"+ @l}8;1<9/\O8:ٝDD#M>U |΍dQቀL})(fY$u7mQ\hykMܮ.pA1Tܴ3l K0tغ|?b;yNntt8 V<XyAUpl7o 5,Eg&HYH$<笶譜I|?L^r:cq B~ZA08TW&Kp 遑4An gV7; m!p|w2C=j CK7n[RG$"%Cb+j}lI |,mlP6h sllP\Ga1qP/_ђ Ѭ!7IɇԮ"'i tS U=/*H.)@u=k698ydj&џҷVٗh$v«ȣO@'E`Vh/d#:wޥCW=I#]u PJs~z!v0}/R$Kkb0BL%Ly:RK/_uAM@hԼEbuXGBPj|BWWD+#hGhIgfŎ8 s!֖h`Xp~aGUB0AХ,]R[,K}͟=(5#&-'NP7 q-Uɾ|DM/khȳV&&C&ߌIż1S9S=w—9c43+g($PGٜzWl.U:$lIwrQBiczDJ ,v!+mPIt$ox\J)&2en0d_)oBa"|i>l6΁Prr;!^wcB"&lu"2*w{O[6@t$c%PX/[mc)ܣ،[MIvlfb.N/,n(DYL Skðǔ蚶B4 toL ԙ.ZC|[ruvgGsiZ35Y |lVG{П';+oV$Ŷ߸-z/x7TM-$G8}^]W( H|_% yƎ[, O[s߂xLVtkNU5WkcC/yzF`_g`"{M}Qf qEQvs_ PAj&!5jA+=vteBr\Ej!q/y:s*³\y_;C%gY=֟Q+F#%ovjn;G3 vPƈ2\k]q;J-$G#_-޸_M<poUühd?Ij)ІY z3ybeS;3h*#9}ȇ`Z^CQ^-as>p)ݍ7 ;pFHs؄gmpÚ}a,BV2Y3R~^ S;@~ڊA s*Qs'gx1£7XU8.P+?EޓF^(ig)4|&'+4?1}vGʯ#|Hذ޼ @{=Y^'1b":ao뗊%q `E[t^*9Ϧp`-QۂƑΪD eFQS_>CJ+%dnojtzc8OEnBۆpJx*IoiԣU{TO$xNOKTu'^ݢft5= O">¿{g2: GBq+k9UG.eG#xPIV v⬫0s1BAk{Y^4gs@5ʃVO,8Ny&pvuQ Fo ESa؋0a֌RզVViJcٛBw'2$jk~-Z{lc/{g`S>.44Nxl^5VF9mjod`b꧚׈RŰt]qz_b g &׍we;ǡ#(/2x|ݪ--4P EFYgPs#8SN@ Wc#o#+j0e˯lelŏ=ߐ"Sɿ&PAKg2Lew-Qn"DJ3l~MC9*AA*D#nToU<ǁaR1`U.c_uL&GrEɪPnYg7 R ̩55>|$gm'f 8bBXQ89;lj7VnN*ա<]V6bmp"}6E<؇y,ME yNz{"HHgN$踫pL{(> ͟JҕCg kƏ]1G"y%Q閲؝DWVZHCɾv(K"y躚'>G4H]6lƲE>K2/W1(6,aLPYzETT>e3IY @A5Uik0bV 4hvD=SnQEs m:DۧM{*67N*PYuS <rBii ĝD [P .*0ڷ0>r95`iP]ؑޟn4:̉B р_/ɦd^&NO*@1݁_N<6J ; l;$)S9VYȨ:9 'jw/G){'S|\{ٖ6N@w8L1㓷 }:VKXuvRdQ#n8+gzfjߵZ2M˝n}TnFhׇ 3N_QImSizXQ'(2@4P D%|FM37ؾT`SEJ˕>˛ϰQ:L04VmJQ` N4 5H1=M(nX5p}P+w'E Hx쥤v0hSkJ]M1hDcd!>&̘0˿dk5ɓY_4QȉG~Q%48*Pjkj oc6Ӹ\mY1H z#E/Bd~~K`X"EΨνz\V=ߚ C7z9GZlU&_LK[DaVHxH8vJwGAtb jH jZuk8SgAo.O~ez,G[[@Sv|(.v?e~{Ŗhhpi]; C/em,joH/51S&,u!VyO fo&b 2E>)*S2jdݻY5+Wtwͬ82*퉠݋_i[ A/-E\j8!HKj=հ)L;Ҽc1aFt+ҴeB҉Ħ ؋eca|1tjcsHanQط5;YGy< 2_J(I6~,N&ȑfLffP\3'ۤ"xdX}Ɔ_Ha>l܊ m"3U~S}Yd)؝ ر@dQŏ||^^/zZm2©wlˆ.%=L{բ?G>hVHR \)I(Q20 Jx9zV'bE29vG2oIUs4zh͚<e^ҴY}S Uk0(S@S0}t*P}CMQ~LɖqXH>g,3B*s!m jBUM (yf EK9*-o|;p5{11VZH˖1\W6O>^lTzMqr !Zkk^'c^mkwñYNcov|զN7$:$O{=06"~F&K߮៧b q!y $pQ6 HDoہЦ'bOJD",.;.1Pt[G#)>:'S~;2f 'qCWR8A&0qP=Dh"(ygk3{]+\pu|"3$%~w ; $xoi\dݬy_Ov9]6^AۨrIvtؠ<[k:i }R*W?P #Cj[q;ƓزO#[i|Lc8sSz { Ṋ4ᢇR9Ӑl;M_9 .'_H\ȕ٣h(B[&PCw6 iD,f uĥ\JJ۸|ѩyJQ`*]vtn9o߁΅Nȳ:?vJH>rZTxry톓DT^8S` PQ:Qr G" i/oDBp<0K 0)7/bQ^/腢KOveGV?Yn{ĭ_g_}Q[cDht(zIMSۺ1F֚7 l6T[VB*ئ=_ܤ1xCRj9&l8]u@` evϩJ3 BQ+R:ܚ>y-7,ENYdyZ[oN|G.'G4G`w1$nٖmkXeF6`1|EKY٬`G<肧/zP?1&ςH)!uDZgfވ#{ɖ"~4&u$[&Ec߅9вLx3ZKźtnT)tSeBtc@Xf N^@$%Ehbbg >_MIa?0//v$i:—IyXK4E3hv^=>SiYZ&*ms9=fT+a@VCQ0Wĸ>Gv!t2FUSUչ3䀠x.ˍ`/0O͂w*I)}.&;8|ܵL ?FVww*Q0i42u5 (l~x!n,F­iഊeK]Ø٪Z)Ays̤8\ 7'lķMZX<|5dm({^ȓr)o֗^3qIOADD 8M9ߏ<}{=u=@4GOg*Q]M:5 =B&Mxr؎̼uSp.sG9p9q[C (/ mZ/9:Mx] &bP7+dr np ktXZ|L_x{O[=E!T)A4=mb,Ano|nSwT1 ؒor@5ia" 1Xl9D%} !l&:qBi^-:­ӊB?>]fաZ\\HhC')~yvX- i6 @G1E& VK nހCNs 4De #TZB=(oQw endstream endobj 93 0 obj << /Length1 725 /Length2 16182 /Length3 0 /Length 16730 /Filter /FlateDecode >> stream xl{spfmmNv2͉mۚض;un^gMN,aodgf`H201XEF.vbF.@t00ÑD<,-\T&mL-m-6n&>777!7gW'W UP)/ WHNF6EWcK H 0wۙZ3ÿ ع\!fdo WPW2LR sq 4qqWutl2_N&.ctigfORr @Ю66F@ go tK{w'"5/⧋?bۙ#B@SEKvqr'M-]m3&R=6v&v4r2ӊF?`@_-,gabгr0Y٘\,ܾE ho74[]7 Jk -/6T]f;sb9hajpp{O0˘b]3Qhb28\oܯfM)>\ BD:%cm5Pf-3FڱE9ڰy+~eNZkKkd2Eyiaeq{%,rƅ Џr1F}r^8+-DzC~aiJf}ƶ^jߘntA#^`^d!y#sx="ڼ5a\tԋTTA%37g);xBM\<[62xfj|>1VŝW*frß!Nļ{|lӰ˺^,:@fc7iNyoFLM^ODbwY9{Ӊx06Ow9O{,ۈSڽn:VD=?U&=E`L Xt3B$%K;m圕Ʉ l2"PA6_>˃IX*odp߯/F3hY1!Qw4Ks[(Tj AlaD8nQg/ĉPU S8Ps+lRS/2ǮC^/+h*jwbe0c_N-$Q4z9OFOFC퇰dOjLY?|EkXl?K. ̷^z1cn0iVOkx ~g]?&tn Cr|@$}tN/#El4\ ƛ֚)Q)ٍcM9|&#䖆Fđ#k?+"cZ7Am; 1=M|$a!k&m>آ;J_ ~\gR~ 7~"u}Gj+dOJ2ܓ 4ÉafҶG|᪘Z1bdm{UUY Wn{` \3LG=)8nܙ$]W|SJnL^7ٱ`B.bA;ߍ8JG9_64߆4|fk#o e#w|HHYYҥ ٠Qam̯&bGou]_A j'}jZvQV[P }Ay7{?eZ`lJׁ*73 18a?؛!he, _ͩB=G /-9^3yd !(9$Qm?`|Wy/?JG&j4Z,Av,_O uLwcJ2Mx1ԧh1M+<1/nKD5R)'߄Ԕ #g)r^ ͦ!c_]G\R`q]v,n@~^a樝9D"妳qgaLZdVL+fqgڧTYkuʇ1 @2 Pݹ Ivt?ܡI'M;';~h.E!7 R\񌦆^)LKeۨ% LKR܃XqТwbc3uIѸZM"x& zbvƯNo9+R'!:Rl@kĝU<Cn]"/QU}> &.V AŎr71^aB[b}UfPv&9v j(Չێ3PmYd䢂M% ptOAqk5BrOtrD*\M$i΂;a>tN?n΋dJO1U6gYOy֛+7e=ܶh3dm|@Uȥ/O7o=^ m;jTN[Y2ׯC9Jؤ'j,"gd"a{1I+1%Qq9فblyS ,:iI4-ҁZ/-7t轐qvq\N8ݘǂua1y[7ɵ;} Ϫ;86U{XAobiZ"PdU_Ql5W:wWX>˩LDM Sk1 DosN1kk>ٱy_I}ɿ@vC'\. dNO]HShS}y ^5zsD0U<罷fR"Ri߇ǐݿ#%9a*Hba']MgMH?e.oYOqPozPT'l<=mI*Hz/ E/GC][}= LV$E"rN og'|˘D=`ۛ8t y- f5h'%qtCxTFTfҌlS9ΣzJĵWooaR23΄<$67) ^!D(BDce([~ڰ _>tB 9S%U7Myea)kn\fva?ˎBjC%%8(7INKg"k !>pKFвCN_PJ&qil~SDT?w$:uIlQUi?_b8WQvy(0!U%nIYT㯜nVh<xXa;6!˛p75XFZW%F=Zۊ%'Ӊ 'jH-Ijpmpj~^=AK¬Anr&$)3Mݎ22t1̞- :+>"":x/ LD'غ#&G3ԐƷ_fdٶ`R&4%}DOwbQT.w-}~ "};A vn)V AQ{OB-KJ3P.O\ᇉuI wmmu$ lVGU"3n{Еꀕ 8Nr*9Mf'8#?+c='%sWMՙ]v_C9(z,4vJL6Ńkq1hm I g 2]qrcwl] $*{m<$:JI+$E<)%fUx  Q2w4/l69`"J Uu ?/1Yy%f12zi)tt$ Ϝy)JOuHorASwg5apxhWE Upfu(I;fGQTܦ$RhS@vN'lNMEٴߒMլf rn~vج{",s=CΣ퐕h3+<ǰiÆ|vZͬo_[Kd8ߴF_Ū-z#<<tKU_A׵T.Noejx)GݸoH`VO-BHcmaXwI$eh!;a}?{M;eHQtb\;EWoibޥnk i׳m8$LZ6; CIx3TQ3I BHhS&(/~8L X%'̃>F&.rT\A"a7[a5wmډvG{œfgb-gX-z9^IiL]IBTclm장/E`X& ̨Y4Z޶}o% Ԭ;VAg|=fŪdU@=3J}HcA,ߜ.znEϋtFpPDA-v"B*3m4`5@XvCL2O!Uw+n_T9+~!ŅaH3qv0"4~%pFL7fl؁uka2$wVGDi1~/W.5aTGyfa%4"qzrE۟\sbh{]!b7ԮYq L"k%|ׁCd5+Bg^s08Hi^DPcפ(@38'. jᇿ~=vSnwJny(Hi!!*+(_S Rr c+`DJ3Xl*k-xHW.*MeWU&Juk4XuyݧbdzaÐ!ڡq&ܻ"0NDU#f̳vMeٓ@.?ش 2KJC7[> 3z.:bv {D7S_AgWPo61%ɺ46"SzX? %9IJu+XaӱZZ>i.E `s; LX-2rˁHi*ܫYҵfH:<: W>SʜU;:)Cӽ"r.mCU 9G 3aSڏrOtkjTV7~J#eg HYX1N4ztGbX:rB!+AD8fģdG}UWp%ABUAszrQhɞc"5d) +JZSĺHqkx-TwƪF%sEw)44raj0ѕ +$K~aЁрd|!b9Z3k. (؈rn:X"O,V.[1ؼ9Ap]Zt ^=wIzx )Om0vBϐ-j& 6ߦ@ ZC- g5k/甂jrz:'A J^Dt, 1Teլ;|11;%'/ ep} ӣ'4- khM\Rv#.gqxFor?ϻ痠Sֵ2bus - kVk/]jըj⻦ `LCE\&amQ0d'ePh_$|¨5>ұMej<o-6]Op⒢#5M \8 -D )^nԠߔVadAo! ?SPN>Q۫3E-Gܱӧ̰m@ǔͻ€!CAVlŋ h,%{3S+0}..'82 x[SCk%4kWD tm{haM alWr-c^)DxSvo1-qPHJ8,br=N #n88IJc)ZTWeC "Y7g6l3 z,z*`Cʟ~k^;R~<, xyM +`IO]``o G.taǜ#lF&~}7L E5=n=  Y׮,6HZd!psK%$`S< 7Q x|T}\414tzMHdWBll`nS0ƛ+')Yvd!Fmr%Vj4 CЩ=%Hv˦7)[n;E䄦* pƷeݼ/VuUgȖN hl*C> H0P;"HkY.8.u活mZIA ,ZCg;u[qYbP±)R.SP7\ٴ*3+(H6c!MrJf Cp s`@jcPˑX"λ;;{[0g*BQ-& \rnxZ)N KAȘ9qw"=ZSA$7hBcM(a5c] 6XEzY5j:gm8Q-Ř LkaD-SZ ʘcKaǎgy+bJ+*:d$c.s K*V@)vM׫XYp,b_IZ ӥP&)ѿj}n!AtS,JT>|{\!&U_Mk ^ byu#:^E3WSL{=GfӵĮ ymUP`k s8E ;ԥg̗Z!"@ D~3c \hǿ> `նwc&A \|{^CƼ_b>?KGɵP8?UJN;=z^EwhobFV^︭fЖZǺKL9+K>zcJyyΡʀ BB&9^ʇWٍ! t;9ŕG딠!vBPKjmHu2О?[~l){ͅo^}ʼnYz2{Xr˗Z? yZ'-͊mڎFPoTެw=}h7[kvOtؗRH~Hf b~ZqglZ$| .W'?}MW|MnYE_'Iii!V%?|Ton=wJ%>Dl vh">56:űt NxL bߗtֻu;I7@`jT3w -` Zc=!fo~;-gjFgFO@/NGGb;H8$J@ ]̲hÖOd~L&2&yMtn/?= I7x{GzwT:ij LU}âOcи}*YG^M;Ddxn8'v\P @EƀQmޟ0jX|'Ptz)7 KZ&(ǀ4nU1Bu\_AK)[QEq(Q'KC$Y~-kaҡ~)c+1Uwa4#PЭ9'fǘKrE>qW7M/#ǃT{j"dT\l~K]rJOn-?dq^íʓy2y1{{HJ?Q=样dxxUA"Gs)fZhyU_q$XFRa~϶o[ؘ5:#bhtR 7`#:R?a[5ܜ|%+A|E]hՀ:o@J1K{+O@Le\7o8G^ c=N%-5u$]tz& nc 1e4\jz [~頃} љ1ӱOpH|<§J;;J:#yDc}Kd*>*ZcW5|=A[G,MPL`Y@4W]f&o0Cum ?u'T'ިhC}rכ.7pPfua 66mߠ/dV!Wt6xҗECzƔ|vƻo}%*}Ln #Z[{cg;SKJ"l")bdD[ꢅ A = x!J\3z]1dC,N6+J-0WUHOw|9j#voF=@)Y}X}2p.7W%:  Y8oE|܏Xᄌ_yf"؃Ex[*½c #==K_pKsԽaI.ҿ~!kx(>C1l'F6p/s3;NPrNs`4.1cDܕ"Eȇ[:0$3 c$KJ1DI|QRLgxy 娬 )^;HŦHs ]W)vL9 X4'< tvo7V`녁.(8d5"GwH1?RB*z*6tb$5gާc>`[qnD- ] jf ?MyJ38i]]!B٤φ:lV07XYVI_ ;ᳰp+TlNd1 yUtpYu,u{~iĝy*iVs R.=_y}!S "*ۥmYP='^i-_%n׷f-CUi+s<#omNV=dRcvU=<,k*9^kcE-Qirpx?OʑcĔ7؝Wez-GS׽Ƭ<ϳQZ]HK.jk1Z {xF͑HGmD2,0JW!g:}\>!dz@yc˓H{>ל|KC'n~S%ZL(  :ruzk Qܚތ1irEs?wa܋m2y ^_q.]؂pa-L۝ Q07 Ba}haXՏ+FxuBA= |=M]mr6Oe銈h %d}{!WU6ğqe-mk6:ẏ\{We%d { )7wfI)M7<DQ4v+#7 lOՓAMRy#I\+lMp|a` v6|{~c} j,RuF_= u [Z\ voQ7ժ&8.21{#Ǵ#T nd dqBJEZSL rn6iQmq!aj?OKd+^v@0@ɕ.u˙C9$GvP|QpïhbFۙ-iz<vi# ײKPvVvQ0:jn|<1݌> jyFjXNrGk(OկSE !' % z1R(NW=o(u,#NBtg8͟کpr#'ٜ,{VWyf;P'N0 Es QoK= ^7rm.oH(]sLDl2jM~dkҼ=ƎT{'hTf- D Dv%֑| 4cm5 Ea?ϫK?ZLSs-!~r01 h9SMrZ՗x.|9e]42De8QNwk]/>i0[UT~7nc:[.GZlH8pYFXeYwJ`I{I &VVPqmGpL_Fg̋RK@"UUP|Ec-Uy]#%/roR#rLj1;7 /d?赫tg ?jRd [@{HSуu@+[ͥ,.1m{!+wR^3Xrrㅻ0uk'.+f0)9ZmަVqΨkziZfh$g|B']f$ݲܕE-bo[Dwxlʬ%,$QH͊/$tRI)fEeu"StHmoO& 苛54SYFkLqnF04p<]8WF{=J6+q|?لXx52[!ЄR`+[2C[ fq\X( 5 O S V$/A̖/C=&vv#nHܐW ,]pD'SqɃvCFR(=+''J]d Pu`r'nCjW ;Gk<_ CsS{ e#lr/BUWMy\=T:G.J=ƾ?$z]5"]w#?1.R#̑fOk31S۵{mW!هOy^}9Ln6t!q3"޷7\-NH{ʘ2xrt6]AWk,L1IL%Pxf$%ߏG[M9&%u\Sճ" g[nPY҉ I32STx#5r -a>y ըᷰN:F#dۣ3r*_V(T3FʌYS7!-*5RVݓH;!^!&vz#hAhU`qē6gmd|Ü#zg{z9淝!u{#YR^J2&Ȝxv]]_[{uj O㡘.(Z}ʡdGf,Fހg>noXJzD]7ZLItG(UVBlTD@XCa-'j\ 5'`n,KWϳ(1˸6EkOzR6Ch\gBZ{_Ů {Os;^af Zy$CDV'Yɇxd<~TGʵ~0LM5(9]rAb:WM yd8\jgs6@g8^`Pz>Kv:ݽqlcҸfEi8+&QTz3',il<gEˀ =ү;]z{0?51Z}@HM{M]m^yʰ=74̩'5U/Z(D"pt*YPcQor^ wn@JH_A|Z^_<tWw!/:`Z+ϫO;Yl3?9rad\y^@6֛{";*#3Jc5"!cQ4 R`+^g8|O7֕G1GUzD҆.B]'VM]0ׄvﻗ LO#VP/&&a*5SW4Ni#o 5 +:HSũnG]S$Ns=6.SPQjn6PxLAb]5}9kEF'>-Yl Ā{tPM,nGp>Wʩ[dՃu~Dl4iwWYJ.`𻓂lAݒveB%^f*:i %z$u|| Omĉh㈮xgm }ǖaO&FtQ\ʑrHRlqEf'Z55f]Qn5! ѐTx $],ͥ F!@G`潲ڹLl!'Xy]684^~WScSf( UX4+̺j8XIipt %ԊzfǢṼs!2:PxX>ʮD` \ɚCMObaS [kȖ~}e-"ny{e(l+ Sֈ%iaq^H"4828lYm- 8%4H7K;x9QJOC ]X;U]8c~hxڍ 0N5Ϟ0}0Gp".˜| U;>+go{<8RkgSn3qLi}[aUVƸWPt0Y5?O/_:Q+E`Vemdj^M;Uލ*2%BI'W/YJ(~~tgMy4Knfml;MIru7 [كVx ~ɡyGp+\b\fY!3O_+枎B --C7#) 5DhΚ~ *S<>8Iy lE*ˇ.rgkK/@UsMirJh_D RkBGٕpKIW~)KKDȥ3!W{ $DBLޢMn9=nLu9őqP{JQ ]ZP%]hT?&[S<\m bvMku! G|UOةIٝ*b}?`C)T"3E<nr?He5q lVOv@8.B;9l1FL|JT{hϹ4-8A7:y0Wn endstream endobj 95 0 obj << /Length1 725 /Length2 17745 /Length3 0 /Length 18270 /Filter /FlateDecode >> stream xlcpf>ۚ8OlOlքOlgLl۶m۶m9_YW^kvΪ@fzf&n*ϟL&V8rrG34,&&f8r3ʘF#@*O p6L-E-)y @h t4(Y[d-N@j#? _ 0SG;*@LQU`hk'ىh󿪣8dmIut 00v,lś?fu|8Mv7Dl]9;-_ܻ9ZcPC kr6 ![cIhhΎ.5?8&vs?<K}m]G/;uQB|د~[ ,H1_55DXpZi)") ~i-¿^B,1bS[]Ȓj #NjήCrALD$L˭L#JJЬ ǣl`yX=" p}\lv'SQFu#ÞGzJAwrN1L1!RmcVk!ȃ1璨يW->A! ׁ2Dy1|吴}v 9z28و5uQGCj06.zOCElgx3#;Z=)2X054.G:ؔ }C0pEߙ"sQ9d/jO ż(5nL>Mg/W(:  gbڔ1VAJb5?; 찭g>;V#^f T]Q7woqא1`(9c_ 6|B1rxsv(ѭ"کd\)1[)|XƜX0C6-]WfRj%UUbp˟=SmMqXcW|G3Kӡ5 SJ- | ܊89 35>wtY6 x-(J6;,1`WL1 1 ۫1| o$L"5GWgE$1G[9;U(D]B`MZ+J^nN*"$3Ek*cbV2OHc4K 4Ƃxt@cNq!9'[=4@nN/u,Fˊ?5"\,r*п3>nŚڸ/ErK\O >}ޓ? /<}=7S@Utq]GiUwLlb>Hh+@s_]k+4H!wfzD&,r.pA(0Yt2cҚ^~ OrX0[A7r ]TTpoL{ TS2uW'*<0%f\ね̬Ͼwl9[)&'~{O2 ~Qƈ'WEkxg.kDBaDZG<3`V/38^GZ4BxՀ~`;)jz.PmϿb D,#(?6vWE0dϔ;-eI!Ct k[TP0LP3ܹDY}ׂ&5nr[ {CЧ{U"LjTM0wۋ"x:{Y>*н6yԚ:n/#F{t/z_En8p,tnΥӃ,]b;aq ox.:@wJuյqGrw?:p&r hAvc|u1h?A [NoT0@)晨vV= -$@ODDש_v)`!x>,HaX$՛Zk\3A8Q45EyzL, g+}r X'ȯO"Z!R *|m:a- -ȟ k}=̺֫1mp^۸d1~ԁ ZȞXH=0 Z܌`BķTybeqncS2W.zh.NK䚷D䐏PP1u(B1x5}JYSi7DOeڙBs?}*ۼf>ԇ]65|`bWSa#aӋyGܲV\\ʣ-!ix6`eKtUu<:_CS[b18"]N$G fBL>tWjVܦfp@&-a)EJ,>1y ڰє 7qjn91 O/ClA?jS@iz3q H`P˼T =!nδvkMP U5nq"=*S%_C8zP%$n8W|%UQ۩);E3G|PzX RkSo٘l){ZJ-(u?^],MMGmG& vw#I#iy?eKxYjn:;3ҝ[[3 <.`.{LKsğ){ ڄ? 2p%Jr(Ғ/yrBI0?mbpk麱yn)maD! z[] k>Cj8q pZ}_\RۛEv`>ZDha]1&FZKN\8m(wl7m"z9RxkWS0XqY+E6ھXSm;7<Jo̜z 1E@e\Pand^ub sKє(desy \$Ju/]UY eH|"⧊ u{0=}!M]Q.lHXb޽A"͓@kt.U ~P5coHCFߜ A[_׭Kv l] /|CG Aٗ%dtKjxe񪴶 ҽ-ŐO1-)H^eK7;pvLݢTڔ$w4jXCZ$1{['|bܳ̌!6yǪ?h~KaAtq)@$c4qb!YCCŗA[ ~y}5m ćhdl&Дb;䏞 k0 $M9a)Bpvn3 $Vѷg8Gwl5Q rW(`+F432QqP4aZ@g i@7gPNYGoB;q?~9iv)MwF|BnPm"X[eCZ+Q\U.[6J$QKZ C>f9=1>l}#P`!1W ;ڀx*G&r0;Y$`ߨz{Mx%6A۬Fdl-Ls9?kiP #wl 8KI|'@aEͷ$5TH^!K8Y\ΫAR~bƈW}L29?j?+!hR6lA/fŒ{Q&7SBN5XHV#qa*# .nIg6AM,:@OWsXe"b F!a0m)H|jZRE:($)r ;2Y|jivWeڿ<q99=6{~qw>6O=ž1OաCG&[Mwu#I$n$|T4JF;.)Ac/Ԩt`uzIzneȝ 3 J<LҎF8Y_!vL8P | Kn^khv O8W7:Jdw5iE-[,uȸpmOI:K`d\Y^=Nf֍$^#yv)1٥ * {,6 K"R>JQK[Ӣ@2[Sg.döR%7zԦM}_8KB-q;Ԭᦢ^}jх.Z($bQCfG\hqf=Z%1(#MqE$>m x!rg{%y]1$G=VcmGWYL}}rY[IP0+7Y"uyFH|QDw_@s*75Q8* cFgb'K*DPO#. Uvtp3"LLbMQq8].= v 0gb!]Gnyb򬔨v3}բd @ 1 6f=z]_CP=bIf'%ICXTU g.G,L].cmMosZ5_)x!dh\s}bƕV>{ / eQTnwWD)(ڐ/f c-]U(UO#-RM5niS?h)LsȂn x, d2~J ?ʊwT6*IMJ{Dc3ll: T% ;Q!|B^A5A>U`_!Ni`kEc%Eoba1ŝqgK69pV@G3'B[t4SomKrP1LuEiK*@{{"F6`x l%mɑw\ԪƘ{oSg Y_o b>YRn.5ұsA pqs0p;uEIZy Rq˖qPKδacjQ9 v˃ yr-7 Q*V2M6 qRX"9{qh\T98#v!}˭kXƋəv(8|sl&xއTeh8ICVrH H zJ[庡Pd@NēLħδ_'7sv.Sת=Z3jGcy!Bw^Hߥu.{Ks&g.:TMɏN%ղPԠXħ'"£oyHpf4ʪ-T=r8ޓ`ad.|Wsӧo^jB[C_u=)mb_=$mZ'e-il=AV #O&AM>Eޱbɽq&ΛUoy£ʖά\Wu$0cʝfZD3ךÆG:;վ=!ݚOY7xGf1OP۵%bX.ɔZm=߲Y8iм۳8Zڠ;{OE(dQ{23ג}U>uց N¥8M ,' Ȫeb3ȹKr]_VO.&_ _o0=DK<'oXf6 GȘ .XW]m:8]تCr 5vYxWu)ZMs#]캃4zp)\:ҙu 4 Y ҷ|(~-t! D$[]*\$6K._jjƻ bb1'ef*3c Œs&7 vR٘YNe?kD"vw( +>w:1HEU LnidDTd`q-xV5(ni>}!V$$ MjLYy^>RB\xGmˏ滧KZa`<54~rTLOu0A>p7΄;wU%Z78r.Z:}Y)qO;\f:)+ a Tdž.[2Ƙ}?}]sǓN. e,^o؇V嚡t >!Q<]g '=3I8yOgfWNw6N+̌?wx#D 4O]5caWLS7CЗ~ֹJ|!y)<``>L$9+;Z3B/Z]tw̑7(Ʉ'/!M&rbGՖ|!"C9Q;ŚY.{W) 4 /<gL_[f#n `?m [OO-vumcCxF#)yu(xd|a}xoO!th';ՆI!vbMHDc/5*|,ؿ/_ԩ [Hc3HEUyto-6$@Aw}{PE9qi(v(hN/ޞ_{؜ƦODȩra1.Ȗ (c*Z!!ntMԷ?j7^:>o",Q%&VZ져*Z/6@ث FY=^De=P˪WMx~}Q&3Weȹ{?H9XDGW%KoAIÍ]F*4ژhk<3M| $}jHB׈CzP .4o~Muf ' wrû箖Dg(o% 1$|-uߺC }?'o |`@T7/(W"tiKeϠKMf=_GeꉊiT>kt{Cҁoo؞GųJŜ|P$$2겨<]ff%N򱡲/.O f`; X!=3t6j \'YA}L,, ]!v],t,%jWEzd$ 'ɹ?GbX_ FڻBZՖ7W[U?IlWԩ[9 N?VK X 1xpFcM#̮f hN' }C6˞\߿e4{"LX%gSxA6NIܶ#=̼ zsH.5ݠ!~>ދxJLN>üج>I)XHe0QC Nf|`LUx[o' l}ΰħ=}( 1chGVQj[XOr+-2c,,"6.6ya~#9.wuvڨyq (׋bf51R&[M9P)_vڔy_@ Ӑ;HIǐ1^4 qt_x`a#0f×鯶 9:[bs4C5I8VB)31~GTa<qcKT"r(S~›(SH-[V ?TѾ,(1~e)g& )@HܢnoY==G5LL72BkA449$$OP"GWzMy\ J,V:?Bl0[Jm.;z*rd.v:|3l8Ti_+7t%t}haL*շ 'A_1Y3RT}>R>G¦HUkG^dyW̒fmN"e{ws/Xxe*WK?(ȉT+U }9ʒn[t # XC%Ia==u={FMǍF<ӡ2Bm`}if9蜎wmdǒq6 :HEN"vW^G^Q݋+ZFV]bz;zu9A+$AkD%[u/I РeqZJːbv*[ '#szD͸HzRU/XJqւB; W='0JX 9c1N )P'[&N;GKK%+nȗlA`9@?7`1MQY4o w $qў[LhO;QFt_\ eD)jίsYԪUؾPb֕`}fOg@}G?aT Kی]~d&wvgH$q߯Wbe{T'Kxo0B"4C+M4kU,&p:KTij֫,ͫ64%_Lʿr6֚t1Z_֣l6GΘބ;(Wg1YU#-u#@7iRկ2N(bWQSH@if}oRZ ^PdCw\y5u=e6 Uܰ&C5ؽ!Ki)bU UGSO0ńAyweU6Fݼ63g; Gůz7ϮyE #nʩOrsMsfTk>@%A8L! idMr W?GAn i 4yf娋nf nrFRyZ6S#.̫>ż̥#2Sj֐<^72e .fޢ׾4 up7wPAHkKY,.p%aYؑAIq75d`s5ezlq,5wQv)` m9x-BK.Kv2ż?[#-Ml7b6+٭蛺1HuxҮ-$Bp3JIL WECX[pF0=m 'xWOawg Ot V WNS2 7ᨆ- I퀶Un/OR~܄,ra.BM淪$BOOX9NlPlQv!lG{ rhbbB2 2l(ŸPT{=,8OC&U >7G)"! M 9aikbxtsXÇBnBn'۟0Ru)ɞR(e`%6ɒ7EM*e]LE|#/2/t.Ďd5@Ď-jXEEM<'6B#(V!F` v+&+tV(LS鎈ri dt5|DCd`Yńy Sfkʆd'BzHN/b_9#}1澺_\4 } B^BkKhKC쌳 .0X**G8-xۦYGʁH #!I r1urqNPEjWmXh7Y"o2lf+)jΧu)c . -ъy̑Q!~(0lYZ]QEY{Srl&6mZ,  ׸WmP/4G~Nh)]ɍw. Y|) 'u{^a<2r]cʬ?]HCr%#~p)c4$!㺎 ,0{H oQ+3包}^JcaNHOtKӎ(I`.big4ZMyL!w+O/!2K?R.#nS`  y ۅ>|JXy>usaGyj1$=K-K!ĨYSSSa4,D,Ʋzedt.;=g ݘKэA!89U bd;N`'C1t)b' ;MKp8|XN~6p0b4AwW(gNsm¨;tU>y(kms^Woh%1˅̟x##]0pmx0d'WJ\]ž4EGlTE uSSn 1CY9䕸KG'K~kR eCK(~]ASOݼ&ILSDL,$UFK gYOtˣW_),Z/ٝpk h|L\F|F[{iZ47~=3!4Iq0 .E׶V;-~rR8?0]s^]UܬJD*|ʮsÞ2Vq2vpzt)g:muع8s&x}1XؒKI*ΐv"8@͍8r_\i* !i=|HXX!OLnĦSS[@*\q35J"S!{;JS! K!T:~wrF(?&ae?i~C:jO5ܪe{c,J7]tѣ,j,p܁5U~zq8̌!YBφx/PzRj.P Dg+yL׽:ֽdw3+Q;2tM)C _*DQJKļա ItiP0$n'P}vSUJӲT~f@>sjXm?6yp-A+:`s*iì_u)Aݣz߽u>5c${>79dp|;|mJ9L;wn4 tlnNlgqXXMDg#fg {d!g! n=Mҋr8ӺGvXR əп:[AM>/w@v?*dܩwƂdu$ :;.0"g갂wT2wDtJ*+0 JVֳi5·j]}PRB kF8dt`kMc|Q4QUPD:
r[wTs=WgJOaڟ}\xMqZO+V@p^'bsEkQzŒ\u㞝-=!T,Q kuQVwc۸rFNӺݺiȘNLo3mSHPl9dcx7 endstream endobj 103 0 obj << /Producer (pdfTeX-1.40.22) /Author()/Title()/Subject()/Creator(LaTeX with hyperref)/Keywords() /CreationDate (D:20250322085407-07'00') /ModDate (D:20250322085407-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021) kpathsea version 6.3.3) >> endobj 2 0 obj << /Type /ObjStm /N 87 /First 701 /Length 3738 /Filter /FlateDecode >> stream x[[S9~cحT L dd2CQ[igf_ߑvƄ[}Hʑґt.9jdLKfԑE jh IeK&R3ϬL[Xa'F8M%m4F1v$SI/)LaX.vRXH{L!Xh7,:5M4A*iJ>Z1rag-J;)0E0h$ >0o1cXό9g%(M`c`B9eǑB !x8!5H$ >`C"( ::VgtE zX&jKFyadQ"hQJH6PUc@,#Jy )HjP"a"̏^xQ0yT$ B'ld(AjF3h fhXRHy)AϞ1Asv=Ʒtw_%oeoփq9X +GW԰SWݍ ;hpGu!13`PcCZ3Vzx\D& -Cؠ M XDh Q(Aq9v5?{VqU>?9/FἾW>=%/X#wi?̫qewXVn8 I#L~ ) )l;VPD+"@} )7TBl_66y-؟fto&\5urMfҮ M]ẩ{B}S]a/aq"j"DFmbCg<*rj|*LpZ^,t5sppbVɂ3qtlag?U8 ~$`cm 钯*˼Iѩ/( &VQ? @H "&da#-"vH(5e40טgpz?Lqψ4Ȗ6.6'!3#83f\(34^EQ j^NI=x@ QrʈF#f=֎܄x[蛑* " `F|-OY3[9!㰌tƃ[Sv-8gNR2]HJg :뻣ѝX4 w2@mƠ6kpfƭHL$M q8ȸ4C&G`)Vh]! ɋ`6NB+x F)#NeVOGJF];UlT"s!d3^Y̪0/<$U6RԸ'R %yثbQUܝi@I:؂$Tih+4O .B|sc7z8"Hؗ@,\AoTamTݓYy95B'PsMH=k)#h љR~ +oBe_\M*!¯LيdnK;>z zh㦆m>t-a!"ttudE@& .b )qpC.=z11Ź 60M3kʳftn#rl1td> "J%yhA[OߥUo?ȥm\I3HiIr-tҹ%-xeJ ec"޽K+p MfK3U.@C0)S#YBQSεܚgؔ?km#N5MẗZz:kC2ZinN%2"%MIrps=xI--2Ӵ{u?Դ>e3.-\BmgHIr鮭\mYr&26c3F/=fjƶ3 қTOkfz* i/ \hcJkm+̞!OQGS -#p&<:z=׽H̙eMܵgOe^]yC{HO>I&LL>44'].=oVД*f2]*@"cJ[ NmuƌL |m9rph̝httD8KZ.e -8I¤)-a$eh#Vw>Ja%W̄$(CMyMS3\JB ѴorFiPcrkSM!aKӚYn۴2mjK$?FL)sACjR«}p:!y9 q=̇7sz%7wv^I~tLHo`TYLbHG^m}ԣ.r{N4e{*ոۯz~40ThF-1YwH''|}-/UaϷM\mn{kb4JETpLG9qq8b"-ai wyxNx^mSӍpt+*-[/o}~A?'W%__mmy+Snoڠ gWkC6,] ]Z: Ј?';_f{;.!h; o/boM@) m=J_Qwp?BA_PwwGgT<}*a_gQjF֠n_П8׆iV[tYD0y4dQ'U_OllX|j/W rXƠ?|k  z_eP) JS*>߆9^6g)ԵlӯLOwfjjy}x{R5V"L}}3WEIpeI`j5[/wVm&j0o N's0[u7oX+㯌S몔*[R‚]_ zq58I gB7&H0%sa Qs TLiN<4Tr/:ŲGmg޲#G|"y˒kMN6N4\Dv)7nG"9>`s;h,6Vh=hz?S# ^K9=tAdƄ/lD0׭|Θ_=սn.-0:#, QG)?^>??ZHgg9k^-CjG1rҷzYŏ~~y~]au=~xVr _g ѯ^<?wހ7{{XH|]&$i67 󥅩+\O׽pF#Uδ'7m5o6`koWx ]f5٬L\E 6#Baɪm%֦}X^ioͭo-Xj&1b'^fhAG\[f `}^_GܩKnT^s݋ro i?rccG endstream endobj 104 0 obj << /Type /XRef /Index [0 105] /Size 105 /W [1 3 1] /Root 102 0 R /Info 103 0 R /ID [<0A7D54625B3DAA3890327E7B986F677F> <0A7D54625B3DAA3890327E7B986F677F>] /Length 250 /Filter /FlateDecode >> stream x%GNAFgؘ=UVo`HF R2i5P p 'Ppppp1d MfhVH -Kg-m P).Y쳽( 0A,8L$L,",2*:l /~馬%{ eWQU*^;guvWA{)x{|x5-=<(L}o?I  endstream endobj startxref 203017 %%EOF GPArotation/inst/doc/GPArotateDF.R0000644000176200001440000000710014767556440016366 0ustar liggesusers### R code from vignette source 'GPArotateDF.Stex' ################################################### ### code chunk number 1: GPArotateDF.Stex:19-20 (eval = FALSE) ################################################### ## options(continue=" ") ################################################### ### code chunk number 2: GPArotateDF.Stex:76-83 (eval = FALSE) ################################################### ## library(GPArotateDF) ## ff.quartimax<- function(L){ ## f = -sum(L^4) / 4 ## list(f = f, Method = "DF-Quartimax") ## } ## data(Harman, package="GPArotation") ## GPForth.df(Harman8, method="quartimax") ################################################### ### code chunk number 3: GPArotateDF.Stex:95-100 (eval = FALSE) ################################################### ## ff.cubimax<- function(L){ ## f = -sum(abs(L^3)) ## list(f = f, Method = "DF-Cubimax") ## } ## GPForth.df(Harman8, method="cubimax") ################################################### ### code chunk number 4: GPArotateDF.Stex:127-155 (eval = FALSE) ################################################### ## ff.fss <- function(L, kij=2){ ## m <- ncol(L) ## p <- nrow(L) ## zm <- m + kij ## Imat <- matrix(0, p, m) ## for (j in 1:m){ ## Imat[abs(L[,j]) <= sort(abs(L[,j]))[zm],j] <- 1 } ## for (i in 1:(m-1)){ ## for (j in (i+1):m){ ## nz <- sum( (Imat[,i] + Imat[,j]) ==1) ## while (nz < zm && sum(Imat[ ,c(i,j)]) < m * 2){ ## tbc <- c(abs(L[,i]), abs(L[,j])) ## tbcs <- sort(tbc [c(Imat[,i], Imat[,j])==0])[1] ## Imat[abs(L) == tbcs] <- 1 ## nz <- sum( (Imat[,i] + Imat[,j]) ==1) ## } ## } ## } ## Method <- paste("DF-Forced Simple Structure (kij = ",kij,")", sep="") ## f <- sum(Imat*L^2) ## list(f = f, Imat = Imat, ## Method = Method) ## } ## data(WansbeekMeijer, package = "GPArotation") ## z <- factanal(covmat = NetherlandsTV, factors = 3, rotation = "none") ## fssT.df(loadings(z), kij = 3) ## # which loadings get weight 1 in the first iteration? ## ff.fss(loadings(z), kij = 3)$Imat ################################################### ### code chunk number 5: GPArotateDF.Stex:167-172 (eval = FALSE) ################################################### ## ff.oblimax <- function(L){ ## f <- -(log(sum(L^4))-2*log(sum(L^2))) ## list(f = f, ## Method = "DF-Oblimax") ## } ################################################### ### code chunk number 6: GPArotateDF.Stex:175-180 (eval = FALSE) ################################################### ## ff.entropy <- function(L){ ## f <- -sum(L^2 * log(L^2 + (L^2==0)))/2 ## list(f = f, ## Method = "DF-Entropy") ## } ################################################### ### code chunk number 7: GPArotateDF.Stex:183-190 (eval = FALSE) ################################################### ## ff.simplimax <- function(L,k=nrow(L)){ ## # k: Number of close to zero loadings ## Imat <- sign(L^2 <= sort(L^2)[k]) ## f <- sum(Imat*L^2) ## list(f = f, ## Method = "DF-Simplimax") ## } ################################################### ### code chunk number 8: GPArotateDF.Stex:194-205 (eval = FALSE) ################################################### ## ff.pst <- function(L,W,Target){ ## # Needs weight matrix W with 1's at specified values, 0 otherwise ## # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8). ## # When W has only 1's this is procrustes rotation ## # Needs a Target matrix Target with hypothesized factor loadings. ## # e.g. Target = matrix(0,8,2) ## Btilde <- W * Target ## f <- sum((W*L-Btilde)^2) ## list(f = f, ## Method = "DF-PST") ## } GPArotation/build/0000755000176200001440000000000014767556440013562 5ustar liggesusersGPArotation/build/vignette.rds0000644000176200001440000000052014767556440016116 0ustar liggesusersPN0MBE (X:bHⴰ-IX^㸎繎;pALjy;ϖæ1O> 8'%XBY *g!Z![<}(l6D Wjhwv"N v]iKYu xC ;Te{vQ,6zZ7̸֩dwE{(RfE X ]!g9Ϡ4#zL= 2.0.0)\cr License: \tab GPL Version 2.\cr URL: \tab https://optimizer.r-forge.r-project.org/GPArotation_www/\cr } Index of functions: Wrapper functions that include random starts option \cr \tabular{ll}{ \code{\link{GPFRSorth}} \tab Orthogonal rotation with random starts \cr \code{\link{GPFRSorth}} \tab Oblique rotation with random starts \cr } Gradient Projection Rotation Algorithms (code unchanged since 2008)\cr \tabular{ll}{ \code{\link{GPForth}} \tab Orthogonal rotation function \cr \code{\link{GPForth}} \tab Oblique rotation function \cr } Utility functions\cr \tabular{ll}{ \code{\link{print.GPArotation}}\tab Print results (S3 level function) \cr \code{\link{summary.GPArotation}} \tab Summary of results (S3 level function) \cr \code{\link{Random.Start}} \tab Generate random a starting matrix \cr \code{\link{NormalizingWeight}} \tab Kaiser normalization (not exported from NAMESPACE) \cr \code{\link{GPForth.lp}} \tab Single start \eqn{L^p}{Lp} orthogonal rotation function \cr \code{\link{GPFoblq.lp}} \tab Single start \eqn{L^p}{Lp} oblique rotation function \cr } Rotations using the gradient projection algorithm\cr \tabular{ll}{ \code{\link{oblimin}} \tab Oblimin rotation \cr \code{\link{quartimin}} \tab Quartimin rotation \cr \code{\link{targetT}} \tab Orthogonal Target rotation \cr \code{\link{targetQ}} \tab Oblique Target rotation \cr \code{\link{pstT}} \tab Orthogonal Partially Specified Target rotation \cr \code{\link{pstQ}} \tab Oblique Partially Specified Target rotation \cr \code{\link{oblimax}} \tab Oblimax rotation \cr \code{\link{entropy}} \tab Minimum Entropy rotation \cr \code{\link{quartimax}} \tab Quartimax rotation \cr \code{\link{Varimax}} \tab Varimax rotation \cr \code{\link{simplimax}} \tab Simplimax rotation \cr \code{\link{bentlerT}} \tab Orthogonal Bentler's Invariant Pattern Simplicity rotation \cr \code{\link{bentlerQ}} \tab Oblique Bentler's Invariant Pattern Simplicity rotation \cr \code{\link{tandemI}} \tab The Tandem Criteria Principle I rotation \cr \code{\link{tandemII}} \tab The Tandem Criteria Principle II rotation \cr \code{\link{geominT}} \tab Orthogonal Geomin rotation \cr \code{\link{geominQ}} \tab Oblique Geomin rotation \cr \code{\link{bigeominT}} \tab Orthogonal Bi-Geomin rotation \cr \code{\link{bigeominQ}} \tab Oblique Bi-Geomin rotation \cr \code{\link{cfT}} \tab Orthogonal Crawford-Ferguson Family rotation \cr \code{\link{cfQ}} \tab Oblique Crawford-Ferguson Family rotation \cr \code{\link{equamax}} \tab Equamax rotation \cr \code{\link{parsimax}} \tab Parsimax rotation \cr \code{\link{infomaxT}} \tab Orthogonal Infomax rotation \cr \code{\link{infomaxQ}} \tab Oblique Infomax rotation \cr \code{\link{mccammon}} \tab McCammon Minimum Entropy Ratio rotation \cr \code{\link{varimin}} \tab Varimin rotation \cr \code{\link{bifactorT}} \tab Orthogonal Bifactor rotation \cr \code{\link{bifactorQ}} \tab Oblique Bifactor rotation \cr \code{\link{lpT}} \tab orthogonal \eqn{L^p}{Lp} rotation \cr \code{\link{lpQ}} \tab oblique \eqn{L^p}{Lp} rotation \cr } Other rotations\cr \tabular{ll}{ \code{\link{eiv}} \tab Errors-in-Variables rotation \cr \code{\link{echelon}} \tab Echelon rotation \cr \code{\link[stats]{varimax}} \tab varimax [The R Stats Package] \cr \code{\link[stats]{promax}} \tab promax [The R Stats Package] \cr } vgQ routines to compute value and gradient of the criterion (not exported from NAMESPACE)\cr \tabular{ll}{ \code{\link{vgQ.oblimin}} \tab Oblimin vgQ \cr \code{\link{vgQ.quartimin}}\tab Quartimin vgQ \cr \code{\link{vgQ.target}}\tab Target vgQ \cr \code{\link{vgQ.pst}}\tab Partially Specified Target vgQ \cr \code{\link{vgQ.oblimax}} \tab Oblimax vgQ \cr \code{\link{vgQ.entropy}} \tab Minimum Entropy vgQ \cr \code{\link{vgQ.quartimax}}\tab Quartimax vgQ \cr \code{\link{vgQ.varimax}}\tab Varimax vgQ \cr \code{\link{vgQ.simplimax}}\tab Simplimax vgQ \cr \code{\link{vgQ.bentler}}\tab Bentler's Invariant Pattern Simplicity vgQ \cr \code{\link{vgQ.tandemI}}\tab The Tandem Criteria Principle I vgQ \cr \code{\link{vgQ.tandemII}}\tab The Tandem Criteria Principle II vgQ \cr \code{\link{vgQ.geomin}}\tab Geomin vgQ \cr \code{\link{vgQ.bigeomin}}\tab Bi-Geomin vgQ \cr \code{\link{vgQ.cf}} \tab Crawford-Ferguson Family vgQ \cr \code{\link{vgQ.infomax}} \tab Infomax vgQ \cr \code{\link{vgQ.mccammon}}\tab McCammon Minimum Entropy Ratio vgQ \cr \code{\link{vgQ.varimin}}\tab Varimin vgQ \cr \code{\link{vgQ.bifactor}}\tab Bifactor vgQ \cr \code{\link{vgQ.lp.wls}}\tab Weighted Least Squares vgQ for \eqn{L^p}{Lp} rotation\cr } Data sets included in the GPArotation package \cr \tabular{ll}{ \code{\link{Harman}}\tab Initial factor loading matrix Harman8 for Harman's 8 physical variables \cr \code{\link{Thurstone}} \tab box20 and box26 initial factor loadings matrices \cr \code{\link{WansbeekMeijer}} \tab NetherlandsTV dataset \cr } } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert. Code is modified from original source \file{splusfunctions.net} available at \url{https://optimizer.r-forge.r-project.org/GPArotation_www/}. } \references{ The software reference is Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. Theory of gradient projection algorithms may be found in: Jennrich, R.I. (2001). A simple general procedure for orthogonal rotation. \emph{Psychometrika}, \bold{66}, 289--306. Jennrich, R.I. (2002). A simple general method for oblique rotation. \emph{Psychometrika}, \bold{67}, 7--19. } \keyword{ package } \seealso{ \code{\link{GPFRSorth}}, \code{\link{GPFRSoblq}}, \code{\link{rotations}}, \code{\link{vgQ}} } GPArotation/man/eiv.Rd0000644000176200001440000001012514747523013014273 0ustar liggesusers\encoding{UTF-8} \name{eiv} \alias{eiv} \title{Errors-in-Variables Rotation} \usage{ eiv(L, identity=seq(NCOL(L)), ...) } \arguments{ \item{L}{a factor loading matrix} \item{identity}{indicates rows which should be identity matrix.} \item{...}{additional arguments discarded.} } \value{A GPArotation object which is a list with elements \item{loadings}{The new loadings matrix.} \item{Th}{The rotation.} \item{method}{A string indicating the rotation objective function ("eiv").} \item{orthogonal}{For consistency with other rotation results. Always FALSE.} \item{convergence}{For consistency with other rotation results. Always TRUE.} \item{Phi}{The covariance matrix of the rotated factors.} } \description{ Rotate to errors-in-variables representation. } \details{ This function rotates to an errors-in-variables representation. The optimization is not iterative and does not use the GPA algorithm. The function can be used directly or the function name can be passed to factor analysis functions like \code{factanal}. The loadings matrix is rotated so the \eqn{k}{k} rows indicated by \code{identity} form an identity matrix, and the remaining \eqn{M-k}{M-k} rows are free parameters. \eqn{\Phi}{Phi} is also free. The default makes the first \eqn{k}{k} rows the identity. If inverting the matrix of the rows indicated by \code{identity} fails, the rotation will fail and the user needs to supply a different choice of rows. Not all authors consider this representation to be a rotation. Viewed as a rotation method, it is oblique, with an explicit solution: given an initial loadings matrix \eqn{L}{L} partitioned as \eqn{L = (L_1^T, L_2^T)^T}{L = rbind(L1, L2)}, then (for the default \code{identity}) the new loadings matrix is \eqn{(I, (L_2 L_1^{-1})^T)^T}{rbind(I, L2 \%*\% solve(L1))} and \eqn{\Phi = L_1 L_1^T}{Phi = L1 \%*\% t(L1)}, where \eqn{I}{I} is the \eqn{k}{k} by \eqn{k}{k} identity matrix. It is assumed that \eqn{\Phi = I}{Phi = I} for the initial loadings matrix. One use of this parameterization is for obtaining good starting values (so it looks a little strange to rotate towards this solution afterwards). It has a few other purposes: (1) It can be useful for comparison with published results in this parameterization; (2) The S.E.s are more straightfoward to compute, because it is the solution to an unconstrained optimization (though not necessarily computed as such); (3) One may have an idea about which reference variables load on only one factor, but not impose restrictive constraints on the other loadings, so, in a nonrestrictive way, it has similarities to CFA; (4) For some purposes, only the subspace spanned by the factors is important, not the specific parameterization within this subspace; (5) The back-predicted indicators (explained portion of the indicators) do not depend on the rotation method. Combined with the greater ease to obtain correct standard errors of this method, this allows easier and more accurate prediction-standard errors. } \examples{ data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, rotation="none") fa.eiv <- eiv(fa.unrotated$loadings) fa.eiv2 <- factanal(factors = 2, covmat=NetherlandsTV, rotation="eiv") cbind(loadings(fa.unrotated), loadings(fa.eiv), loadings(fa.eiv2)) fa.eiv3 <- eiv(fa.unrotated$loadings, identity=6:7) cbind(loadings(fa.unrotated), loadings(fa.eiv), loadings(fa.eiv3)) } \seealso{ \code{\link{echelon}}, \code{\link{rotations}}, \code{\link{GPForth}}, \code{\link{GPFoblq}} } \references{ \enc{Gösta}{Gosta} \enc{Hägglund}{Haggland}. (1982). "Factor Analysis by Instrumental Variables Methods." \emph{Psychometrika}, 47, 209--222. Sock-Cheng Lewin-Koh and Yasuo Amemiya. (2003). "Heteroscedastic factor analysis." \emph{Biometrika}, 90, 85--97. Tom Wansbeek and Erik Meijer (2000) \emph{Measurement Error and Latent Variables in Econometrics}, Amsterdam: North-Holland. } \author{Erik Meijer and Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/man/Random.Start.Rd0000644000176200001440000000455214766677323016052 0ustar liggesusers\name{Random.Start} \alias{Random.Start} \title{Generate a Random Orthogonal Rotation} \usage{ Random.Start(k) } \arguments{ \item{k}{An integer indicating the dimension of the square matrix.} } \description{ Random orthogonal rotation to use as Tmat matrix to start \code{GPFRSorth}, \code{GPFRSoblq}, \code{GPForth}, or \code{GPFoblq}. } \value{An orthogonal matrix.} \details{ The random start function produces an orthogonal matrix with columns of length one based on the QR decompostion. This randomization procedures follows the logic of Stewart(1980) and Mezzari(2007), as of GPArotation version 2024.2-1. } \seealso{ \code{\link{GPFRSorth}}, \code{\link{GPFRSoblq}}, \code{\link{GPForth}}, \code{\link{GPFoblq}}, \code{\link{rotations}} } \examples{ # Generate a random ortogonal matrix of dimension 5 x 5 Random.Start(5) # function for generating orthogonal or oblique random matrix Random.Start <- function(k = 2L,orthogonal=TRUE){ mat <- matrix(rnorm(k*k),k) if (orthogonal){ qr.out <- qr(matrix(rnorm(k * k), nrow = k, ncol = k)) Q <- qr.Q(qr.out) R <- qr.R(qr.out) R.diag <- diag(R) R.diag2 <- R.diag/abs(R.diag) ans <- t(t(Q) * R.diag2) ans } else{ ans <- mat \%*\% diag(1/sqrt(diag(crossprod(mat)))) } ans } data("Thurstone", package="GPArotation") simplimax(box26,Tmat = Random.Start(3, orthogonal = TRUE)) simplimax(box26,Tmat = Random.Start(3, orthogonal = FALSE)) # covariance matrix is Phi = t(Th) \%*\% Th rms <- Random.Start(3, FALSE) t(rms) \%*\% rms # covariance matrix because oblique rms rms <- Random.Start(3, TRUE) t(rms) \%*\% rms # identity matrix because orthogonal rms } \references{ Stewart, G. W. (1980). The Efficient Generation of Random Orthogonal Matrices with an Application to Condition Estimators. \emph{SIAM Journal on Numerical Analysis}, \bold{17}(3), 403--409. http://www.jstor.org/stable/2156882 Mezzadri, F. (2007). How to generate random matrices from the classical compact groups. \emph{Notices of the American Mathematical Society}, \bold{54}(5), 592--604. https://arxiv.org/abs/math-ph/0609050 } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert. Additional input from Yves Rosseel. } \concept{rotation} \keyword{multivariate} GPArotation/man/Thurstone.Rd0000644000176200001440000000110014323662111015465 0ustar liggesusers\name{Thurstone} \docType{data} \alias{Thurstone} \alias{box20} \alias{box26} \title{Example Data from Thurstone} \description{ box20 and box26 are initial factor loading matrices. } \usage{ data(Thurstone) } \details{ The objects box20 and box26 are loaded from the data file Thurstone. } \format{ The objects box20 and box26 are matrices. } \source{ Thurstone, L.L. (1947). \emph{Multiple Factor Analysis}. Chicago: University of Chicago Press. } \seealso{ \code{\link{GPForth}}, \code{\link{Harman}}, \code{\link{WansbeekMeijer}} } \keyword{datasets} GPArotation/man/WansbeekMeijer.Rd0000644000176200001440000000117714323662111016403 0ustar liggesusers\name{WansbeekMeijer} \docType{data} \alias{WansbeekMeijer} \alias{NetherlandsTV} \title{Factor Example from Wansbeek and Meijer} \description{ Netherlands TV viewership example p 171, Wansbeek and Meijer (2000) } \usage{ data(WansbeekMeijer) } \details{ The object NetherlandsTV is loaded from the data file WansbeekMeijer. } \format{ The object NetherlandsTV is a correlation matrix. } \source{ Tom Wansbeek and Erik Meijer (2000) \emph{Measurement Error and Latent Variables in Econometrics}, Amsterdam: North-Holland. } \seealso{ \code{\link{GPForth}}, \code{\link{Thurstone}}, \code{\link{Harman}} } \keyword{datasets} GPArotation/man/NormalizingWeight.Rd0000644000176200001440000000100414342727133017145 0ustar liggesusers\name{NormalizingWeight} \alias{NormalizingWeight} \title{Internal Utility for Normalizing Weights} \usage{ NormalizingWeight(A, normalize=FALSE) } \arguments{ \item{A}{A loading matrix.} \item{normalize}{An indication of if/how the matrix should be normalized.} } \description{ See GPFRSoblq and GPFRSorth. } \value{A matrix. This function is not exported in the NAMESPACE, and is only used by the GP rotation functions. See \link{GPFRSorth} for an example of its use.} \keyword{internal} GPArotation/man/GPA.Rd0000644000176200001440000002551614766700562014140 0ustar liggesusers\name{GPA} \alias{GPFRSorth} \alias{GPFRSoblq} \alias{GPForth} \alias{GPFoblq} \title{Rotation Optimization} \usage{ GPFRSorth(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL, randomStarts=0) GPFRSoblq(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL, randomStarts=0) GPForth(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="varimax", methodArgs=NULL) GPFoblq(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, method="quartimin", methodArgs=NULL) } \arguments{ \item{A}{initial factor loadings matrix for which the rotation criterian is to be optimized.} \item{Tmat}{initial rotation matrix.} \item{normalize}{see details.} \item{eps}{convergence is assumed when the norm of the gradient is smaller than eps.} \item{maxit}{maximum number of iterations allowed in the main loop.} \item{method}{rotation objective criterian.} \item{methodArgs}{a list ofmethodArgs arguments passed to the rotation objective} \item{randomStarts}{number of random starts (\code{GPFRSorth} and \code{GPFRSoblq})} } \description{ Gradient projection rotation optimization routine used by various rotation objective. } \value{A \code{GPArotation} object which is a list with elements \item{loadings}{The rotated loadings, one column for each factor. If randomStarts were requested then this is the rotated loadings matrix with the lowest criterion value.} \item{Th}{The rotation matrix, loadings \code{\%*\% t(Th) = A}.} \item{Table}{A matrix recording the iterations of the rotation optimization.} \item{method}{A string indicating the rotation objective function.} \item{orthogonal}{A logical indicating if the rotation is orthogonal.} \item{convergence}{A logical indicating if convergence was obtained.} \item{Phi}{\code{t(Th) \%*\% Th}. The covariance matrix of the rotated factors. This will be the identity matrix for orthogonal rotations so is omitted (\code{NULL}) for the result from \code{GPFRSorth} and \code{GPForth}.} \item{Gq}{The gradient of the objective function at the rotated loadings.} \item{randStartChar}{A vector with characteristics of random starts (GPFRSorth and GPFRSoblq only; omitted if \code{randomStarts =< 1}).} } \details{ Gradient projection (GP) rotation optimization routines developed by Jennrich (2001, 2002) and Bernaards and Jennrich (2005). These functions can be used directly to rotate a loadings matrix, or indirectly through a rotation objective passed to a factor estimation routine such as \code{\link{factanal}}. A rotation of a matrix \code{A} is defined as \code{A \%*\% solve(t(Th))}. In case of orthogonal rotation, the factors the rotation matrix \code{Tmat} is orthonormal, and the rotation simplifies to \code{A \%*\% Th}. The rotation matrix \code{Th} is computed by GP rotation. The \code{GPFRsorth} and \code{GPFRSoblq} functions are the primary functions for orthogonal and oblique rotations, respectively. These two functions serve as wrapper functions for \code{GPForth} and \code{GPFoblq}, with the added functionality of multiple random starts. \code{GPForth} is the main GP algorithm for orthogonal rotation. \code{GPFoblq} is the main GP algorithm for oblique rotation. The \code{GPForth} and \code{GPFoblq} may be also be called directly. Arguments in the wrapper functions \code{GPFRsorth} and \code{GPFRSoblq} are passed to GP algorithms. Functions require an initial loadings matrix \code{A} which fixes the equivalence class over which the optimization is done. It must be the solution to the orthogonal factor analysis problem as obtained from \code{factanal} or other factor estimation routines. The initial rotation matrix is given by the \code{Tmat}. By default the GP algorithm use the identity matrix as the initial rotation matrix. For some rotation criteria local minima may exist. To start from random initial rotation matrices, the \code{randomStarts} argument is available in \code{GPFRSorth} and \code{GPFRSoblq}. The returned object includes the rotated loadings matrix with the lowest criterion value \code{f} among attemnpted starts.Technically, this does not have to be the global minimum. The \code{randomStarts} argument is not available \code{GPForth} and \code{GPFoblq}. However, for \code{GPForth} and \code{GPFoblq} a single random initial rotation matrix may be set by \code{Tmat = \link{Random.Start}(ncol(A))}. The argument \code{method} can be used to specify a string indicating the rotation objective. Oblique rotation defaults to "\code{quartimin}" and orthogonal rotation defaults to "\code{varimax}". Available rotation objectives are "\code{oblimin}", "\code{quartimin}", "\code{target}", "\code{pst}", "\code{oblimax}", "\code{entropy}", "\code{quartimax}", "\code{Varimax}", "\code{simplimax}", "\code{bentler}", "\code{tandemI}", "\code{tandemII}", "\code{geomin}", "\code{cf}", "\code{infomax}", "\code{mccammon}", "\code{bifactor}", "\code{lp}" and "\code{varimin}". The string is prefixed with "\code{vgQ.}" to give the actual function call. See \code{\link{vgQ}} for details. Some rotation criteria ("\code{oblimin}", "\code{target}", "\code{pst}", "\code{simplimax}", "\code{geomin}", "\code{cf}", \code{"lp"}) require one or more additional arguments. See \code{\link{rotations}} for details and default values, if applicable. Note that "\code{lp}" rotation uses a modeified version of the GPA rotation algorithm; for details on the use of \eqn{L^p}{Lp} rotation, please see \link{Lp rotation}. For examples of the indirect use see \code{\link{rotations}}. The argument \code{normalize} gives an indication of if and how any normalization should be done before rotation, and then undone after rotation. If \code{normalize} is \code{FALSE} (the default) no normalization is done. If \code{normalize} is \code{TRUE} then Kaiser normalization is done. (So squared row entries of normalized \code{A} sum to 1.0. This is sometimes called Horst normalization.) If \code{normalize} is a vector of length equal to the number of indicators (= number of rows of \code{A}) then the colums are divided by \code{normalize} before rotation and multiplied by \code{normalize} after rotation. If \code{normalize} is a function then it should take \code{A} as an argument and return a vector which is used like the vector above. See Nguyen and Waller (2022) for detailed investigation of normalization on factor rotations, including potential effect on qualitative interpretation of loadings. } \seealso{ \code{\link{Random.Start}}, \code{\link[stats]{factanal}}, \code{\link{oblimin}}, \code{\link{quartimin}}, \code{\link{targetT}}, \code{\link{targetQ}}, \code{\link{pstT}}, \code{\link{pstQ}}, \code{\link{oblimax}}, \code{\link{entropy}}, \code{\link{quartimax}}, \code{\link{Varimax}}, \code{\link[stats]{varimax}}, \code{\link{simplimax}}, \code{\link{bentlerT}}, \code{\link{bentlerQ}}, \code{\link{tandemI}}, \code{\link{tandemII}}, \code{\link{geominT}}, \code{\link{geominQ}}, \code{\link{bigeominT}}, \code{\link{bigeominQ}}, \code{\link{cfT}}, \code{\link{cfQ}}, \code{\link{equamax}}, \code{\link{parsimax}}, \code{\link{infomaxT}}, \code{\link{infomaxQ}}, \code{\link{mccammon}}, \code{\link{varimin}}, \code{\link{bifactorT}}, \code{\link{bifactorQ}}, \code{\link{lpT}}, \code{\link{lpQ}}, } \examples{ # see rotations for more examples data(Harman, package = "GPArotation") GPFRSorth(Harman8, method = "quartimax") quartimax(Harman8) GPFRSoblq(Harman8, method = "quartimin", normalize = TRUE) loadings( quartimin(Harman8, normalize = TRUE) ) # using random starts data("WansbeekMeijer", package = "GPArotation") fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, normalize=TRUE, rotation="none") GPFRSoblq(loadings(fa.unrotated), normalize = TRUE, method = "oblimin", randomStarts = 100) oblimin(loadings(fa.unrotated), randomStarts=100) data(Thurstone, package = "GPArotation") geominQ(box26, normalize = TRUE, randomStarts=100) # displaying results of factor analysis rotation output origdigits <- options("digits") Abor.unrotated <- factanal(factors = 2, covmat = ability.cov, rotation = "none") Abor <- oblimin(loadings(Abor.unrotated), randomStarts = 20) Abor print(Abor) print(Abor, sortLoadings=FALSE) #this matches the output passed to factanal print(Abor, Table=TRUE) print(Abor, rotateMat=TRUE) print(Abor, digits=2) # by default provides the structure matrix for oblique rotation summary(Abor) summary(Abor, Structure=FALSE) options(digits = origdigits$digits) # GPArotation output does sort loadings, but use print to obtain if needed set.seed(334) xusl <- quartimin(Harman8, normalize = TRUE, randomStarts=100) # loadings without ordering (default) loadings(xusl) max(abs(print(xusl)$loadings - xusl$loadings)) == 0 # FALSE # output sorted loadings via print (not default) xsl <- print(xusl) max(abs(print(xsl)$loadings - xsl$loadings)) == 0 # TRUE # Kaiser normalization is used when normalize=TRUE factanal(factors = 2, covmat = ability.cov, rotation = "oblimin", control=list(rotate=list(normalize = TRUE))) # Cureton-Mulaik normalization can be done by passing values to the rotation # may result in convergence problems NormalizingWeightCM <- function (L) { Dk <- diag(sqrt(diag(L \%*\% t(L)))^-1) \%*\% L wghts <- rep(0, nrow(L)) fpls <- Dk[, 1] acosi <- acos(ncol(L)^(-1/2)) for (i in 1:nrow(L)) { num <- (acosi - acos(abs(fpls[i]))) dem <- (acosi - (function(a, m) ifelse(abs(a) < (m^(-1/2)), pi/2, 0))(fpls[i], ncol(L))) wghts[i] <- cos(num/dem * pi/2)^2 + 0.001 } Dv <- wghts * sqrt(diag(L \%*\% t(L)))^-1 Dv } quartimin(Harman8, normalize = NormalizingWeightCM(Harman8), randomStarts=100) quartimin(Harman8, normalize = TRUE, randomStarts=100) } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert} \references{ Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. Jennrich, R.I. (2001). A simple general procedure for orthogonal rotation. \emph{Psychometrika}, \bold{66}, 289--306. Jennrich, R.I. (2002). A simple general method for oblique rotation. \emph{Psychometrika}, \bold{67}, 7--19. Nguyen, H.V. and Waller, N.G. (2022). Local minima and factor rotations in exploratory factor analysis. \emph{ Psychological Methods}. Advance online publication. https://doi.org/10.1037/met0000467 } \concept{rotation} \keyword{multivariate} GPArotation/man/Harman.Rd0000644000176200001440000000106414323662111014711 0ustar liggesusers\name{Harman} \docType{data} \alias{Harman} \alias{Harman8} \title{Example Data from Harman} \description{ Harman8 is initial factor loading matrix for Harman's 8 physical variables. } \usage{ data(Harman) } \details{ The object Harman8 is loaded from the data file Harman. } \format{ The object Harman8 is a matrix. } \source{ Harman, H. H. (1976) \emph{Modern Factor Analysis}, Third Edition Revised, University of Chicago Press. } \seealso{ \code{\link{GPForth}}, \code{\link{Thurstone}}, \code{\link{WansbeekMeijer}} } \keyword{datasets} GPArotation/man/echelon.Rd0000644000176200001440000000752314747516733015150 0ustar liggesusers\name{echelon} \alias{echelon} \title{Echelon Rotation} \usage{ echelon(L, reference=seq(NCOL(L)), ...) } \arguments{ \item{L}{a factor loading matrix} \item{reference}{indicates rows of loading matrix that should be used to determine the rotation transformation.} \item{...}{additional arguments discarded.} } \value{A GPArotation object which is a list with elements \item{loadings}{The new loadings matrix.} \item{Th}{The rotation.} \item{method}{A string indicating the rotation objective function ("echelon").} \item{orthogonal}{For consistency with other rotation results. Always TRUE.} \item{convergence}{For consistency with other rotation results. Always TRUE.} } \description{ Rotate to an echelon parameterization. } \details{ The loadings matrix is rotated so the \eqn{k}{k} rows of the loading matrix indicated by \code{reference} are the Cholesky factorization given by \code{t(chol(L[reference,] \%*\% t(L[reference,])))}. This defines the rotation transformation, which is then also applied to other rows to give the new loadings matrix. The optimization is not iterative and does not use the GPA algorithm. The function can be used directly or the function name can be passed to factor analysis functions like \code{factanal}. An orthogonal solution is assumed (so \eqn{\Phi}{Phi} is identity). The default uses the first \eqn{k}{k} rows as the reference. If the submatrix of \code{L} indicated by reference is singular then the rotation will fail and the user needs to supply a different choice of rows. One use of this parameterization is for obtaining good starting values (so it may appear strange to rotate towards this solution afterwards). It has a few other purposes: (1) It can be useful for comparison with published results in this parameterization. (2) The S.E.s are more straightforward to compute, because it is the solution to an unconstrained optimization (though not necessarily computed as such). (3) The models with k and (k+1) factors are nested, so it is more straightforward to test the k-factor model versus the (k+1)-factor model. In particular, in addition to the LR test (which does not depend on the rotation), now the Wald test and LM test can be used as well. For these, the test of a k-factor model versus a (k+1)-factor model is a joint test whether all the free parameters (loadings) in the (k+1)st column of \code{L} are zero. (4) For some purposes, only the subspace spanned by the factors is important, not the specific parameterization within this subspace. (5) The back-predicted indicators (explained portion of the indicators) do not depend on the rotation method. Combined with the greater ease to obtain correct standard errors of this method, this allows easier and more accurate prediction-standard errors. (6) This parameterization and its standard errors can be used to detect identification problems (McDonald, 1999, pp. 181-182). } \examples{ data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, rotation="none") fa.ech <- echelon(fa.unrotated$loadings) fa.ech2 <- factanal(factors = 2, covmat=NetherlandsTV, rotation="echelon") cbind(loadings(fa.unrotated), loadings(fa.ech), loadings(fa.ech2)) fa.ech3 <- echelon(fa.unrotated$loadings, reference=6:7) cbind(loadings(fa.unrotated), loadings(fa.ech), loadings(fa.ech3)) } \seealso{ \code{\link{eiv}}, \code{\link{rotations}}, \code{\link{GPForth}}, \code{\link{GPFoblq}} } \references{ Roderick P. McDonald (1999) \emph{Test Theory: A Unified Treatment}, Mahwah, NJ: Erlbaum. Tom Wansbeek and Erik Meijer (2000) \emph{Measurement Error and Latent Variables in Econometrics}, Amsterdam: North-Holland. } \author{Erik Meijer and Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/man/print.GPArotation.Rd0000644000176200001440000000323614557556406017052 0ustar liggesusers\name{print.GPArotation} \alias{print.GPArotation} \alias{summary.GPArotation} \alias{print.summary.GPArotation} \title{Print and Summary Methods for GPArotation} \usage{ \method{print}{GPArotation}(x, digits=3, sortLoadings=TRUE, rotateMat=FALSE, Table=FALSE, ...) \method{summary}{GPArotation}(object, digits=3, Structure=TRUE, ...) \method{print}{summary.GPArotation}(x, ...) } \arguments{ \item{x}{a GPArotation object to summarize.} \item{object}{a summary.GPArotation to print.} \item{digits}{precision of printed numbers.} \item{sortLoadings}{display sorted loadings of a GPArotation object.} \item{rotateMat}{display the rotation matrix of a GPArotation object.} \item{Table}{display the convergence table of a GPArotation object.} \item{Structure}{display the structure matrix of a summary.GPArotation object of a oblique rotation.} \item{...}{further arguments passed to other methods.} } \value{The object printed or a summary object.} \description{ Print an object or summary of an object returned by \code{GPFRSorth}, \code{GPFRSoblq}, \code{GPForth}, or \code{GPFoblq}. A GPArotation object by default to will print sorted loadings, Phi, and rotation matrix (if requested). Output includes contributions of factors \code{SS loadings} (Sum of Squared loadings), (see e.g. Harman 1976, sections 2.4 and 12.4). } \details{ For examples of print and summary functions, see \code{\link{GPForth}}. } \references{ Harman, H.H. (1976). \emph{Modern Factor Analysis.} The University of Chicago Press. }\seealso{ \code{\link{GPForth}}, \code{\link[base]{summary}} } \concept{rotation} \keyword{internal} GPArotation/man/vgQ.Rd0000644000176200001440000001133014766701703014251 0ustar liggesusers\name{vgQ} \alias{vgQ} \alias{vgQ.oblimin} \alias{vgQ.quartimin} \alias{vgQ.target} \alias{vgQ.pst} \alias{vgQ.oblimax} \alias{vgQ.entropy} \alias{vgQ.quartimax} \alias{vgQ.varimax} \alias{vgQ.simplimax} \alias{vgQ.bentler} \alias{vgQ.tandemI} \alias{vgQ.tandemII} \alias{vgQ.geomin} \alias{vgQ.bigeomin} \alias{vgQ.cf} \alias{vgQ.infomax} \alias{vgQ.mccammon} \alias{vgQ.bifactor} \alias{vgQ.varimin} \alias{vgQ.lp.wls} \title{Rotations} \usage{ vgQ.oblimin(L, gam=0) vgQ.quartimin(L) vgQ.target(L, Target=NULL) vgQ.pst(L, W=NULL, Target=NULL) vgQ.oblimax(L) vgQ.entropy(L) vgQ.quartimax(L) vgQ.varimax(L) vgQ.simplimax(L, k=nrow(L)) vgQ.bentler(L) vgQ.tandemI(L) vgQ.tandemII(L) vgQ.geomin(L, delta=.01) vgQ.bigeomin(L, delta=.01) vgQ.cf(L, kappa=0) vgQ.infomax(L) vgQ.mccammon(L) vgQ.varimin(L) vgQ.bifactor(L) vgQ.lp.wls(L,W) } \arguments{ \item{L}{a factor loading matrix} \item{gam}{0=Quartimin, .5=Biquartimin, 1=Covarimin.} \item{Target}{rotation target for objective calculation.} \item{W}{weighting of each element in target and in irls.} \item{k}{number of close to zero loadings.} \item{delta}{constant added to \eqn{\Lambda^2}{Lambda^2} in objective calculation.} \item{kappa}{see details.} } \value{A list (which includes elements used by \code{GPForth} and \code{GPFoblq}) with: \item{f}{The value of the criterion at L.} \item{Gq}{The gradient at L.} \item{Method}{A string indicating the criterion.} } \description{ vgQ routines to compute value and gradient of the criterion (not exported from NAMESPACE) } \details{ The \code{vgQ.*} versions of the code are called by the optimization routine and would typically not be used directly, so these methods are not exported from the package NAMESPACE. (They simply return the function value and gradient for a given rotation matrix.) These functions can be printed through,for example, \code{GPArotation:::vgQ.oblimin} to view the function \code{vgQ.oblimin}. The \code{T} or \code{Q} ending on function names should be omitted for the \code{vgQ.*} versions of the code so, for example, use \code{GPArotation:::vgQ.target} to view the target criterion calculation which is used in both orthogonal and oblique rotation. \tabular{lll}{ \code{vgQ.oblimin} \tab orthogonal or oblique \tab oblimin family\cr \code{vgQ.quartimin} \tab oblique \tab \cr \code{vgQ.target} \tab orthogonal or oblique \tab target rotation \cr \code{vgQ.pst} \tab orthogonal or oblique \tab partially specified target rotation \cr \code{vgQ.oblimax} \tab oblique \tab \cr \code{vgQ.entropy} \tab orthogonal \tab minimum entropy \cr \code{vgQ.quartimax} \tab orthogonal \tab \cr \code{vgQ.varimax} \tab orthogonal \tab \cr \code{vgQ.simplimax} \tab oblique \tab \cr \code{vgQ.bentler} \tab orthogonal or oblique \tab Bentler's invariant pattern simplicity criterion\cr \code{vgQ.tandemI} \tab orthogonal \tab Tandem principle I criterion \cr \code{vgQ.tandemII} \tab orthogonal \tab Tandem principle II criterion \cr \code{vgQ.geomin} \tab orthogonal or oblique \tab \cr \code{vgQ.bigeomin} \tab orthogonal or oblique \tab \cr \code{vgQ.cf} \tab orthogonal or oblique \tab Crawford-Ferguson family \cr \code{vgQ.cubimax} \tab orthogonal \tab \cr \code{vgQ.infomax} \tab orthogonal or oblique \tab \cr \code{vgQ.mccammon} \tab orthogonal \tab McCammon minimum entropy ratio \cr \code{vgQ.varimin} \tab orthogonal \tab varimin criterion \cr \code{vgQ.bifactor} \tab orthogonal or oblique \tab bifactor/biquartimin rotation\cr \code{vgQ.lp.wls} \tab orthoginal or oblique \tab iterative reweighted least squares for \eqn{L^p}{Lp} rotation\cr } See \link{rotations} for use of arguments. New rotation methods can be programmed with a name "\code{vgQ.newmethod}". The inputs are the matrix \code{L}, and optionally any additional arguments. The output should be a list with elements \code{f}, \code{Gq}, and \code{Method}. Gradient projection \emph{without} derivatives can be performed using the \code{GPArotateDF} package; type \code{vignette("GPArotateDF", package = "GPArotation")} at the command line. } \examples{ GPArotation:::vgQ.oblimin getAnywhere(vgQ.oblimax) } \seealso{ \code{\link{rotations}} } \references{ Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/man/lp.Rd0000644000176200001440000001270314767556430014142 0ustar liggesusers\encoding{UTF-8} \name{lp} \alias{Lp rotation} \alias{GPForth.lp} \alias{GPFoblq.lp} \title{\eqn{L^p}{Lp} Rotation} \usage{ GPForth.lp(A, Tmat=diag(rep(1, ncol(A))), p=1, normalize=FALSE, eps=1e-05, maxit=10000, gpaiter=5) GPFoblq.lp(A, Tmat=diag(rep(1, ncol(A))), p=1, normalize=FALSE, eps=1e-05, maxit=10000, gpaiter=5) } \arguments{ \item{A}{Initial factor loadings matrix to be rotated.} \item{Tmat}{Initial rotation matrix.} \item{p}{Component-wise \eqn{L^p}{Lp} where 0 < p \eqn{=<}{=<} 1.} \item{normalize}{Not recommended for \eqn{L^p}{Lp} rotation.} \item{eps}{Convergence is assumed when the norm of the gradient is smaller than eps.} \item{maxit}{Maximum number of iterations allowed in the main loop.} \item{gpaiter}{Maximum iterations for GPA rotation. The goal is to decrease the objective value, not optimize the inner loop. Warnings may appear, but they can be ignored if the main loop converges.} } \value{ A \code{GPArotation} object, which is a list containing: \item{loadings}{Rotated loadings matrix, with one column per factor. If \code{randomStarts} were used, this contains the loadings with the lowest criterion value.} \item{Th}{Rotation matrix, satisfying \code{loadings \%*\% t(Th) = A}.} \item{Table}{Matrix recording iteration details during optimization.} \item{method}{String indicating the rotation objective function.} \item{orthogonal}{Logical indicating whether the rotation is orthogonal.} \item{convergence}{Logical indicating whether convergence was achieved. Convergence is controlled element-wise by tolerance.} \item{Phi}{Covariance matrix of rotated factors, \code{t(Th) \%*\% Th}.} } \description{ Performs \eqn{L^p}{Lp} rotation to obtain sparse loadings. } \details{ These functions optimize an \eqn{L^p}{L^p} rotation objective, where \code{0 < p =< 1}. A smaller \code{p} promotes sparsity in the loading matrix but increases computational difficulty. For guidance on choosing \code{p}, see the Concluding Remarks in the references. Since the \eqn{L^p}{Lp} function is nonsmooth, a different optimization method is required compared to smooth rotation criteria. Two new functions, \code{GPForth.lp} and \code{GPFoblq.lp}, replace \code{GPForth} and \code{GPFoblq} for orthogonal and oblique \eqn{L^p}{Lp} rotations, respectively. The optimization method follows an iterative reweighted least squares (IRLS) approach. It approximates the nonsmooth objective function with a smooth weighted least squares function in the main loop and optimizes it using GPA in the inner loop. Normalization is not recommended for \eqn{L^p}{Lp} rotation. Its use may have unexpected results. } \examples{ data("WansbeekMeijer", package = "GPArotation") fa.unrotated <- factanal(factors = 2, covmat = NetherlandsTV, rotation = "none") options(warn = -1) # Orthogonal rotation # Single start from random position fa.lpT1 <- GPForth.lp(loadings(fa.unrotated), p = 1) # 10 random starts fa.lpT <- lpT(loadings(fa.unrotated), Tmat=Random.Start(2), p = 1, randomStarts = 10) print(fa.lpT, digits = 5, sortLoadings = FALSE, Table = TRUE, rotateMat = TRUE) p <- 1 # Oblique rotation # Single start fa.lpQ1 <- GPFoblq.lp(loadings(fa.unrotated), p = p) # 10 random starts fa.lpQ <- lpQ(loadings(fa.unrotated), p = p, randomStarts = 10) summary(fa.lpQ, Structure = TRUE) # this functions ensures consistent ordering of factors of a # GPArotation object for cleaner comparison # Inspired by fungible::orderFactors and fungible::faSort functions sortFac <- function(x){ # Only works for object of class GPArotation if (!inherits(x, "GPArotation")) {stop("argument not of class GPArotation")} cln <- colnames(x$loadings) # ordering for oblique slightly different from orthogonal ifelse(x$orthogonal, vx <- colSums(x$loadings^2), vx <- diag(x$Phi \%*\% t(x$loadings) \%*\% x$loadings) ) # sort by squared loadings from high to low vxo <- order(vx, decreasing = TRUE) vx <- vx[vxo] # maintain the right sign Dsgn <- diag(sign(colSums(x$loadings^3))) [ , vxo] x$Th <- x$Th \%*\% Dsgn x$loadings <- x$loadings \%*\% Dsgn if (match("Phi", names(x))) { # If factor is negative, reverse corresponding factor correlations x$Phi <- t(Dsgn) \%*\% x$Phi \%*\% Dsgn } colnames(x$loadings) <- cln x } # seed set to see the results of sorting set.seed(1020) fa.lpQ1 <- lpQ(loadings(fa.unrotated),p=1,randomStarts=10) fa.lpQ0.5 <- lpQ(loadings(fa.unrotated),p=0.5,randomStarts=10) fa.geo <- geominQ(loadings(fa.unrotated), randomStarts=10) # with ordered factor loadings res <- round(cbind(sortFac(fa.lpQ1)$loadings, sortFac(fa.lpQ0.5)$loadings, sortFac(fa.geo)$loadings),3) print(c("oblique- Lp 1 Lp 0.5 geomin")); print(res) # without ordered factor loadings res <- round(cbind(fa.lpQ1$loadings, fa.lpQ0.5$loadings, fa.geo$loadings),3) print(c("oblique- Lp 1 Lp 0.5 geomin")); print(res) options(warn = 0) } \seealso{ \code{\link{lpT}}, \code{\link{lpQ}}, \code{\link{vgQ.lp.wls}} } \references{ Liu, X., Wallin, G., Chen, Y., & Moustaki, I. (2023). Rotation to sparse loadings using \eqn{L^p}{Lp} losses and related inference problems. \emph{Psychometrika}, \bold{88}(2), 527--553. } \author{Xinyi Liu, with minor modifications for GPArotation by C. Bernaards} \concept{rotation} \keyword{multivariate}GPArotation/man/rotations.Rd0000644000176200001440000003117514766676400015554 0ustar liggesusers\name{rotations} \alias{rotations} \alias{oblimin} \alias{quartimin} \alias{targetT} \alias{targetQ} \alias{pstT} \alias{pstQ} \alias{oblimax} \alias{entropy} \alias{quartimax} \alias{Varimax} \alias{simplimax} \alias{bentlerT} \alias{bentlerQ} \alias{tandemI} \alias{tandemII} \alias{geominT} \alias{geominQ} \alias{bigeominT} \alias{bigeominQ} \alias{cfT} \alias{cfQ} \alias{equamax} \alias{parsimax} \alias{infomaxT} \alias{infomaxQ} \alias{mccammon} \alias{varimin} \alias{bifactorT} \alias{bifactorQ} \alias{lpT} \alias{lpQ} \title{Rotations} \usage{ oblimin(A, Tmat=diag(ncol(A)), gam=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) quartimin(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) targetT(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) targetQ(A, Tmat=diag(ncol(A)), Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) pstT(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) pstQ(A, Tmat=diag(ncol(A)), W=NULL, Target=NULL, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0, L=NULL) oblimax(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) entropy(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) quartimax(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5,maxit=1000,randomStarts=0) Varimax(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) simplimax(A, Tmat=diag(ncol(A)), k=nrow(A), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bentlerT(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bentlerQ(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) tandemI(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) tandemII(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) geominT(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) geominQ(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bigeominT(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bigeominQ(A, Tmat=diag(ncol(A)), delta=.01, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) cfT(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) cfQ(A, Tmat=diag(ncol(A)), kappa=0, normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) equamax(A, Tmat=diag(ncol(A)), kappa=ncol(A)/(2*nrow(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) parsimax(A, Tmat=diag(ncol(A)), kappa=(ncol(A)-1)/(ncol(A)+nrow(A)-2), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts = 0) infomaxT(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) infomaxQ(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) mccammon(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) varimin(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000, randomStarts=0) bifactorT(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000,randomStarts=0) bifactorQ(A, Tmat=diag(ncol(A)), normalize=FALSE, eps=1e-5, maxit=1000,randomStarts=0) lpT(A, Tmat=diag(ncol(A)), p=1, normalize=FALSE, eps=1e-05, maxit=1000, randomStarts=0, gpaiter=5) lpQ(A, Tmat=diag(ncol(A)), p=1, normalize=FALSE, eps=1e-05, maxit=1000, randomStarts=0, gpaiter=5) } \arguments{ \item{A}{an initial loadings matrix to be rotated.} \item{Tmat}{initial rotation matrix.} \item{gam}{0=Quartimin, .5=Biquartimin, 1=Covarimin.} \item{Target}{rotation target for objective calculation.} \item{W}{weighting of each element in target.} \item{k}{number of close to zero loadings.} \item{delta}{constant added to \eqn{\Lambda^2}{Lambda^2} in the objective calculation.} \item{kappa}{see details.} \item{normalize}{parameter passed to optimization routine (GPForth or GPFoblq).} \item{eps}{parameter passed to optimization routine (GPForth or GPFoblq).} \item{maxit}{parameter passed to optimization routine (GPForth or GPFoblq).} \item{randomStarts}{parameter passed to optimization routine (GPFRSorth or GPFRSoblq).} \item{L}{provided for backward compatibility in target rotations only. Use A going forward.} \item{p}{Component-wise \eqn{L^p}{Lp}, where 0 < p \eqn{=<}{=<} 1. } \item{gpaiter}{Maximum iterations for GPA rotation loop in \eqn{L^p}{Lp} rotation.} } \value{A \code{GPArotation} object which is a list with elements (includes elements used by \code{factanal}) with: \item{loadings}{Lh from \code{GPFRSorth} or \code{GPFRSoblq}.} \item{Th}{Th from \code{GPFRSorth} or \code{GPFRSoblq}.} \item{Table}{Table from \code{GPForth} or \code{GPFoblq}.} \item{method}{A string indicating the rotation objective function.} \item{orthogonal}{A logical indicating if the rotation is orthogonal.} \item{convergence}{Convergence indicator from \code{GPFRSorth} or \code{GPFRSoblq}.} \item{Phi}{\code{t(Th) \%*\% Th}. The covariance matrix of the rotated factors. This will be the identity matrix for orthogonal rotations so is omitted (NULL) for the result from \code{GPFRSorth} and \code{GPForth}.} \item{randStartChar}{Vector indicating results from random starts from \code{GPFRSorth} or \code{GPFRSoblq}} } \description{ Optimize factor loading rotation objective. } \details{ These functions optimize a rotation objective. They can be used directly or the function name can be passed to factor analysis functions like \code{factanal}. Several of the function names end in T or Q, which indicates if they are orthogonal or oblique rotations (using \code{GPFRSorth} or \code{GPFRSoblq} respectively). Rotations which are available are \tabular{lll}{ \code{oblimin} \tab oblique \tab oblimin family \cr \code{quartimin} \tab oblique \tab \cr \code{targetT} \tab orthogonal \tab target rotation \cr \code{targetQ} \tab oblique \tab target rotation \cr \code{pstT} \tab orthogonal \tab partially specified target rotation \cr \code{pstQ} \tab oblique \tab partially specified target rotation \cr \code{oblimax} \tab oblique \tab \cr \code{entropy} \tab orthogonal \tab minimum entropy \cr \code{quartimax} \tab orthogonal \tab \cr \code{varimax} \tab orthogonal \tab \cr \code{simplimax} \tab oblique \tab \cr \code{bentlerT} \tab orthogonal \tab Bentler's invariant pattern simplicity criterion\cr \code{bentlerQ} \tab oblique \tab Bentler's invariant pattern simplicity criterion\cr \code{tandemI} \tab orthogonal \tab Tandem principle I criterion \cr \code{tandemII} \tab orthogonal \tab Tandem principle II criterion \cr \code{geominT} \tab orthogonal \tab \cr \code{geominQ} \tab oblique \tab \cr \code{bigeominT} \tab orthogonal \tab \cr \code{bigeominQ} \tab oblique \tab \cr \code{cfT} \tab orthogonal \tab Crawford-Ferguson family \cr \code{cfQ} \tab oblique \tab Crawford-Ferguson family \cr \code{equamax} \tab orthogonal \tab Crawford-Ferguson family \cr \code{parsimax} \tab orthogonal \tab Crawford-Ferguson family \cr \code{infomaxT} \tab orthogonal \tab \cr \code{infomaxQ} \tab oblique \tab \cr \code{mccammon} \tab orthogonal \tab McCammon minimum entropy ratio \cr \code{varimin} \tab orthogonal \tab \cr \code{bifactorT} \tab orthogonal \tab Jennrich and Bentler bifactor rotation\cr \code{bifactorQ} \tab oblique \tab Jennrich and Bentler biquartimin rotation\cr \code{lpT} \tab orthogonal \tab \eqn{L^p}{Lp} rotation \cr \code{lpQ} \tab oblique \tab \eqn{L^p}{Lp} rotation \cr } Note that \code{Varimax} defined here uses \code{vgQ.varimax} and is not \code{varimax} defined in the \code{stats} package. \code{stats:::varimax} does Kaiser normalization by default whereas \code{Varimax} defined here does not. The argument \code{kappa} parameterizes the family for the Crawford-Ferguson method. If \code{m} is the number of factors and \code{p} is the number of indicators then \code{kappa} values having special names are \eqn{0=}{0=}Quartimax, \eqn{1/p=}{1/p=}Varimax, \eqn{m/(2*p)=}{m/(2*p)=}Equamax, \eqn{(m-1)/(p+m-2)=}{(m-1)/(p+m-2)=}Parsimax, \eqn{1=}{1=}Factor parsimony. Bifactor rotations, bifactorT and bifactorQ are called bifactor and biquartimin in Jennrich, R.I. and Bentler, P.M. (2011). The argument \code{p} is needed for \eqn{L^p}{Lp} rotation. See \link{Lp rotation} for details on the rotation method. } \examples{ # see GPFRSorth and GPFRSoblq for more examples # getting loadings matrices data("Harman", package="GPArotation") qHarman <- GPFRSorth(Harman8, Tmat=diag(2), method="quartimax") qHarman <- quartimax(Harman8) loadings(qHarman) - qHarman$loadings #2 ways to get the loadings # factanal loadings used in GPArotation data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 2, covmat=NetherlandsTV, normalize=TRUE, rotation="none") quartimax(loadings(fa.unrotated), normalize=TRUE) geominQ(loadings(fa.unrotated), normalize=TRUE, randomStarts=100) # passing arguments to factanal (See vignette for a caution) # vignette("GPAguide", package = "GPArotation") data(ability.cov) factanal(factors = 2, covmat = ability.cov, rotation="infomaxT") factanal(factors = 2, covmat = ability.cov, rotation="infomaxT", control=list(rotate=list(normalize = TRUE, eps = 1e-6))) # when using factanal for oblique rotation it is best to use the rotation command directly # instead of including it in the factanal command (see Vignette). fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, normalize=TRUE, rotation="none") quartimin(loadings(fa.unrotated), normalize=TRUE) # oblique target rotation of 2 varimax rotated matrices towards each other # See vignette for additional context and computation, trBritain <- matrix( c(.783,-.163,.811,.202,.724,.209,.850,.064, -.031,.592,-.028,.723,.388,.434,.141,.808,.215,.709), byrow=TRUE, ncol=2) trGermany <- matrix( c(.778,-.066, .875,.081, .751,.079, .739,.092, .195,.574, -.030,.807, -.135,.717, .125,.738, .060,.691), byrow=TRUE, ncol = 2) trx <- targetQ(trGermany, Target = trBritain) # Difference between rotated loadings matrix and target matrix y <- trx$loadings - trBritain # partially specified target; See vignette for additional method A <- matrix(c(.664, .688, .492, .837, .705, .82, .661, .457, .765, .322, .248, .304, -0.291, -0.314, -0.377, .397, .294, .428, -0.075,.192,.224, .037, .155,-.104,.077,-.488,.009), ncol=3) SPA <- matrix(c(rep(NA, 6), .7,.0,.7, rep(0,3), rep(NA, 7), 0,0, NA, 0, rep(NA, 4)), ncol=3) targetT(A, Target=SPA) # using random starts data("WansbeekMeijer", package="GPArotation") fa.unrotated <- factanal(factors = 3, covmat=NetherlandsTV, normalize=TRUE, rotation="none") # single rotation with a random start oblimin(loadings(fa.unrotated), Tmat=Random.Start(3)) oblimin(loadings(fa.unrotated), randomStarts=1) # multiple random starts oblimin(loadings(fa.unrotated), randomStarts=100) # assessing local minima for box26 data data(Thurstone, package = "GPArotation") infomaxQ(box26, normalize = TRUE, randomStarts = 150) geominQ(box26, normalize = TRUE, randomStarts = 150) # for detailed investigation of local minima, consult package 'fungible' # library(fungible) # faMain(urLoadings=box26, rotate="geominQ", rotateControl=list(numberStarts=150)) # library(psych) # package 'psych' with random starts: # faRotations(box26, rotate = "geominQ", hyper = 0.15, n.rotations = 150) } \seealso{ \code{\link[stats]{factanal}}, \code{\link{GPFRSorth}}, \code{\link{GPFRSoblq}}, \code{\link{vgQ}}, \code{\link{Harman}}, \code{\link{box26}}, \code{\link{WansbeekMeijer}}, } \references{ Bernaards, C.A. and Jennrich, R.I. (2005) Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis. \emph{Educational and Psychological Measurement}, \bold{65}, 676--696. Jennrich, R.I. and Bentler, P.M. (2011) Exploratory bi-factor analysis. \emph{Psychometrika}, \bold{76}. } \author{Coen A. Bernaards and Robert I. Jennrich with some R modifications by Paul Gilbert.} \concept{rotation} \keyword{multivariate} GPArotation/DESCRIPTION0000644000176200001440000000175714776556662014211 0ustar liggesusersPackage: GPArotation Version: 2025.3-1 Title: Gradient Projection Factor Rotation Authors@R: c( person("Coen", "Bernaards", email = "cab.gparotation@gmail.com", role = c("aut","cre")), person("Paul", "Gilbert", email = "pgilbert.ttv9z@ncf.ca", role = "aut"), person("Robert", "Jennrich", role = "aut") ) Depends: R (>= 2.0.0) Description: Gradient Projection Algorithms for Factor Rotation. For details see ?GPArotation. When using this package, please cite Bernaards and Jennrich (2005) 'Gradient Projection Algorithms and Software for Arbitrary Rotation Criteria in Factor Analysis'. LazyData: yes Imports: stats License: GPL (>= 2) URL: https://optimizer.r-forge.r-project.org/GPArotation_www/ NeedsCompilation: no Packaged: 2025-03-22 15:54:08 UTC; coen Author: Coen Bernaards [aut, cre], Paul Gilbert [aut], Robert Jennrich [aut] Maintainer: Coen Bernaards Repository: CRAN Date/Publication: 2025-04-12 21:40:02 UTC