ランキングの計算について
修学支援新制度では,GPA等下位1/4に該当する場合は警告対象とし,2回連続して警告対象となった場合は支援打ち切りとなります.したがって,大学は学部や学科等の学年ごとにGPA下位1/4の対象者を毎年選定する必要があります.
ここで問題になるのが,対象集団の学生数が4の倍数ではない場合です.以下,実際にシミュレーションしてみます.
まず,以下の4パターンを考えます.ここでnは対象集団の学生数です. \[
n = 4k
\] \[
n = 4k + 1
\] \[
n = 4k + 2
\] \[
n = 4k + 3
\]
\[
これらに,今回は k=20として,n = 80,81,82,83の4パターンを使いシミュレーションすることにします
\]
## Warning: `as.tibble()` is deprecated, use `as_tibble()` (but mind the new semantics).
## This warning is displayed once per session.
これで各パターンのデータ生成ができました.ここで問題になるのが,各パターンにおける最大警告対象者数です.
\[
n = 4k であれば,割り切れますので当然最大対象者数は20名ということになります.しかしながら,他のパターンを用いると余りが出るので
\]
であれば,割り切れますので当然最大対象者数は20名ということになります.しかしながら,他のパターンを用いると余りが出るので,
## [1] 20.25
## [1] 20.5
## [1] 20.75
## [1] 20
以上のような結果になります.ですがヒト1人は割れません・・・.なので学生優位で考えるといずれの場合も最大対象者数は20名としたい.(ここは大学ごとに判断が分かれるかもしれません)
次に各関数を使って,どのようになるのか計算してみます
実際の計算(四分位数,ヒンジ数,%ランク,累積ランク)
まずこの問題を解く方法として一番はじめに思いつくのが,四分位数を用いた方法です.以下,下側ヒンジ,パーセントランク,累積割合に対応する関数を確認します.
## 0% 25% 50% 75% 100%
## 0.190 1.770 2.525 3.040 4.620
## [1] 0.190 1.750 2.525 3.040 4.620
## [1] 1.00000000 0.98734177 0.96202532 0.96202532 0.94936709 0.93670886
## [7] 0.92405063 0.91139241 0.89873418 0.88607595 0.86075949 0.86075949
## [13] 0.84810127 0.83544304 0.81012658 0.81012658 0.79746835 0.78481013
## [19] 0.77215190 0.74683544 0.74683544 0.73417722 0.72151899 0.70886076
## [25] 0.69620253 0.68354430 0.67088608 0.65822785 0.63291139 0.63291139
## [31] 0.62025316 0.60759494 0.59493671 0.56962025 0.56962025 0.54430380
## [37] 0.54430380 0.53164557 0.51898734 0.50632911 0.49367089 0.48101266
## [43] 0.45569620 0.45569620 0.44303797 0.43037975 0.41772152 0.40506329
## [49] 0.39240506 0.37974684 0.36708861 0.35443038 0.32911392 0.32911392
## [55] 0.30379747 0.30379747 0.29113924 0.27848101 0.26582278 0.25316456
## [61] 0.24050633 0.22784810 0.21518987 0.20253165 0.18987342 0.16455696
## [67] 0.16455696 0.15189873 0.13924051 0.12658228 0.11392405 0.10126582
## [73] 0.08860759 0.07594937 0.06329114 0.05063291 0.03797468 0.02531646
## [79] 0.01265823 0.00000000
## [1] 1.0000 0.9875 0.9750 0.9750 0.9500 0.9375 0.9250 0.9125 0.9000 0.8875
## [11] 0.8750 0.8750 0.8500 0.8375 0.8250 0.8250 0.8000 0.7875 0.7750 0.7625
## [21] 0.7625 0.7375 0.7250 0.7125 0.7000 0.6875 0.6750 0.6625 0.6500 0.6500
## [31] 0.6250 0.6125 0.6000 0.5875 0.5875 0.5625 0.5625 0.5375 0.5250 0.5125
## [41] 0.5000 0.4875 0.4750 0.4750 0.4500 0.4375 0.4250 0.4125 0.4000 0.3875
## [51] 0.3750 0.3625 0.3500 0.3500 0.3250 0.3250 0.3000 0.2875 0.2750 0.2625
## [61] 0.2500 0.2375 0.2250 0.2125 0.2000 0.1875 0.1875 0.1625 0.1500 0.1375
## [71] 0.1250 0.1125 0.1000 0.0875 0.0750 0.0625 0.0500 0.0375 0.0250 0.0125
quantile,fivenumを使用した場合は代表値が算出され,percent_rankおよびcume_distを使用した場合は一つのデータごとにランクの割合が計算結果として得られます.
次は各データセットに対して,上記4つの方法を用いて下位1/4の対象者を計算させます
#sample_0に対して
sample_0 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
group_by(funcs) %>%
summarise(n_target = sum(target),cutline = max(GPA[target==1]))
## # A tibble: 4 x 3
## funcs n_target cutline
## <chr> <dbl> <dbl>
## 1 cs 20 1.71
## 2 hs 20 1.71
## 3 ps 20 1.71
## 4 qs 20 1.71
#sample_1に対して
sample_1 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
group_by(funcs) %>%
summarise(n_target = sum(target),cutline = max(GPA[target==1]))
## # A tibble: 4 x 3
## funcs n_target cutline
## <chr> <dbl> <dbl>
## 1 cs 19 1.66
## 2 hs 21 1.74
## 3 ps 21 1.74
## 4 qs 21 1.74
#sample_2に対して
sample_2 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
group_by(funcs) %>%
summarise(n_target = sum(target),cutline = max(GPA[target==1]))
## # A tibble: 4 x 3
## funcs n_target cutline
## <chr> <dbl> <dbl>
## 1 cs 20 1.85
## 2 hs 21 1.87
## 3 ps 21 1.87
## 4 qs 21 1.87
#sample_3に対して
sample_3 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
group_by(funcs) %>%
summarise(n_target = sum(target),cutline = max(GPA[target==1]))
## # A tibble: 4 x 3
## funcs n_target cutline
## <chr> <dbl> <dbl>
## 1 cs 20 1.74
## 2 hs 21 1.78
## 3 ps 21 1.78
## 4 qs 21 1.78
最初に指摘したように,すべてのデータセットのパターンで,警告対象者は20名になるようにしたいところですが,すべてのパターンで対象者が20名になっているものがありません....ちょっと見やすく整理します.
rbind(
sample_0 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
mutate(dataset = "sample_0"),
sample_1 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
mutate(dataset = "sample_1"),
sample_2 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
mutate(dataset = "sample_2"),
sample_3 %>%
mutate(qs = if_else(GPA <= quantile(GPA)[2],1,0)) %>%
mutate(hs = if_else(GPA <= fivenum(GPA)[2],1,0)) %>%
mutate(ps = if_else(percent_rank(GPA)<=0.25,1,0)) %>%
mutate(cs = if_else(cume_dist(GPA)<=0.25,1,0)) %>%
gather(key = funcs, value = target,-GPA) %>%
mutate(dataset = "sample_3")
) %>%
mutate(funcs = fct_inorder(funcs)) %>%
group_by(funcs, dataset) %>%
summarise(n_target = sum(target)) %>%
spread(key = funcs, value = n_target)
## # A tibble: 4 x 5
## dataset qs hs ps cs
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 sample_0 20 20 20 20
## 2 sample_1 21 21 21 19
## 3 sample_2 21 21 21 20
## 4 sample_3 21 21 21 20
上記の計算結果デーブルから方法cs,つまり累積割合を求める“cume_dist”関数を使った結果だけが,n <=20となっています.sample_1の結果はn = 19となっており,最大警告対象者を下回ってしますが,おそらくカットラインに同点者がいたためであると考えられます.以下で確認してみます.
## # A tibble: 11 x 6
## rowname GPA qs hs ps cs
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 55 1.94 0 0 0 0
## 2 56 1.9 0 0 0 0
## 3 57 1.88 0 0 0 0
## 4 58 1.84 0 0 0 0
## 5 59 1.78 0 0 0 0
## 6 60 1.77 0 0 0 0
## 7 61 1.74 1 1 1 0
## 8 62 1.74 1 1 1 0
## 9 63 1.66 1 1 1 1
## 10 64 1.6 1 1 1 1
## 11 65 1.59 1 1 1 1
予想通り,カットラインにGPA1.74の学生が2名いました.この学生たちを2名とも警告対象者とすると,合計の警告対象者は21名となってしまいますので,これで目的に合致した対象者計算が行われていることが確認できました.
注意点
以上のように,cume_dist関数を使用すると,簡単に計算ができたわけですが,ひとつ注意点があります.それはこの関数のバグのようなもので,上記のようにif_elseのカッコ内で計算すれば問題ないのですが,一度計算結果を新規列に追加してから,if_elseで対象者の二値計算をすると,割り切れたはずの数値が対象とならないという問題です.
わかりにくいので以下に例示します.
## # A tibble: 11 x 5
## rowname GPA cs_inc cum_ratio cs_out
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 55 2.13 0 0.325 0
## 2 56 2.13 0 0.325 0
## 3 57 2.12 0 0.3 0
## 4 58 1.88 0 0.288 0
## 5 59 1.87 0 0.275 0
## 6 60 1.79 0 0.263 0
## 7 61 1.71 1 0.25 0
## 8 62 1.51 1 0.238 1
## 9 63 1.48 1 0.225 1
## 10 64 1.47 1 0.213 1
## 11 65 1.41 1 0.2 1
このように一度cum_ratio列にcume_distの結果を追加してから,if_elseでcs_outに対象者を計算すると,0.25以下の条件のはずが,0.25の累積割合を持つレコードが対象となりません.おそらくバグだと思われます.
ここに記載したプログラム等は2次利用していただいてOKです.ただし,実際の大学業務に使用する際には,自己責任でお願いします.プログラムを使用したことによる不利益等は責任を負いかねますので予めご了承ください.
また,不備等もあろうかと思いますので,ご質問等は西山(k.nis80[at]gmail.com)までお願いします.
LS0tCnRpdGxlOiAiUmFuayBmdW5jdGlvbnMgY29tcGFyaXNvbiIKYXV0aG9yOiAiS2VpdGEgTmlzaGl5YW1hIgpkYXRlOiAiMTIvNy8yMDE5IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgIyBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKCiPplqLmlbDjgpLoh6rkvZzjgZfjgablrprnvqktLS0tCmFjY3VtcmFuayA8LSBmdW5jdGlvbiAoeCkgCnsKICByYW5rKHgsIHRpZXMubWV0aG9kID0gIm1heCIsIG5hLmxhc3QgPSAia2VlcCIpL3N1bSghaXMubmEoeCkpCn0KCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIOODqeODs+OCreODs+OCsOOBruioiOeul+OBq+OBpOOBhOOBpgoK44CA5L+u5a2m5pSv5o+05paw5Yi25bqm44Gn44Gv77yMR1BB562J5LiL5L2NMS8044Gr6Kmy5b2T44GZ44KL5aC05ZCI44Gv6K2m5ZGK5a++6LGh44Go44GX77yM77yS5Zue6YCj57aa44GX44Gm6K2m5ZGK5a++6LGh44Go44Gq44Gj44Gf5aC05ZCI44Gv5pSv5o+05omT44Gh5YiH44KK44Go44Gq44KK44G+44GZ77yO44GX44Gf44GM44Gj44Gm77yM5aSn5a2m44Gv5a2m6YOo44KE5a2m56eR562J44Gu5a2m5bm044GU44Go44GrR1BB5LiL5L2NMS8044Gu5a++6LGh6ICF44KS5q+O5bm06YG45a6a44GZ44KL5b+F6KaB44GM44GC44KK44G+44GZ77yOICAK44CA44GT44GT44Gn5ZWP6aGM44Gr44Gq44KL44Gu44GM77yM5a++6LGh6ZuG5Zuj44Gu5a2m55Sf5pWw44GMNOOBruWAjeaVsOOBp+OBr+OBquOBhOWgtOWQiOOBp+OBme+8juS7peS4i++8jOWun+mam+OBq+OCt+ODn+ODpeODrOODvOOCt+ODp+ODs+OBl+OBpuOBv+OBvuOBme+8jiAgCuOBvuOBmu+8jOS7peS4i+OBru+8lOODkeOCv+ODvOODs+OCkuiAg+OBiOOBvuOBme+8juOBk+OBk+OBp27jga/lr77osaHpm4blm6Pjga7lrabnlJ/mlbDjgafjgZnvvI4KJCQKbiA9IDRrIAokJAokJApuID0gNGsgKyAxCiQkCiQkCm4gPSA0ayArIDIKJCQKJCQKbiA9IDRrICsgMwokJAoKJCQK44GT44KM44KJ44Gr77yM5LuK5Zue44GvIGs9MjDjgajjgZfjgabvvIxuID0gODAsODEsODIsODPjga7vvJTjg5Hjgr/jg7zjg7PjgpLkvb/jgYTjgrfjg5/jg6Xjg6zjg7zjgrfjg6fjg7PjgZnjgovjgZPjgajjgavjgZfjgb7jgZkgCiQkCmBgYHtyIHNhbWxwbGUgZGF0YX0KI+ato+imj+WIhuW4g+OCkuS9v+OBo+OBn+OCteODs+ODl+ODq+ODh+ODvOOCv+OBrueUn+aIkOOBqOS4puOBs+abv+OBiOOBquOBqS0tLS0KCiMjODAKcm5vcm0oODAsMi41LDEpICU+JSByb3VuZChkaWdpdHMgPTIpICU+JSAKICBhcy50aWJibGUoKSAlPiUgCiAgcmVuYW1lKEdQQSA9IHZhbHVlKSAlPiUgCiAgYXJyYW5nZShkZXNjKEdQQSkpLT4gc2FtcGxlXzAKCiMjODEKcm5vcm0oODEsMi41LDEpICU+JSByb3VuZChkaWdpdHMgPTIpICU+JSAKICBhcy50aWJibGUoKSAlPiUgCiAgcmVuYW1lKEdQQSA9IHZhbHVlKSAlPiUgCiAgYXJyYW5nZShkZXNjKEdQQSkpLT4gc2FtcGxlXzEKCiMjODIKcm5vcm0oODIsMi41LDEpICU+JSByb3VuZChkaWdpdHMgPTIpICU+JSAKICBhcy50aWJibGUoKSAlPiUgCiAgcmVuYW1lKEdQQSA9IHZhbHVlKSAlPiUgCiAgYXJyYW5nZShkZXNjKEdQQSkpLT4gc2FtcGxlXzIKCiMjODMKcm5vcm0oODMsMi41LDEpICU+JSByb3VuZChkaWdpdHMgPTIpICU+JSAKICBhcy50aWJibGUoKSAlPiUgCiAgcmVuYW1lKEdQQSA9IHZhbHVlKSAlPiUgCiAgYXJyYW5nZShkZXNjKEdQQSkpLT4gc2FtcGxlXzMKCiPjgZPjga7jg5XjgqHjgqTjg6vjgafjga7oqIjnrpfntZDmnpzjgpLlronlrprjgZXjgZvjgovjgZ/jgoHjgavvvIzkuIroqJg044Gk44Gu44OH44O844K/44K744OD44OI44KS44Ot44O844Kr44Or44GL44KJ44OA44Km44Oz44Ot44O844OJ77yO5YCL5Yil55Kw5aKD44Gn5YaN54++44KS6KGM44GG6Zqb44Gv44Kz44Oh44Oz44OI44Ki44Km44OI44GX44Gm44GP44Gg44GV44GE77yOCmxvYWQoInNhbXBsZS5SRGF0YSIpCgpgYGAKCuOBk+OCjOOBp+WQhOODkeOCv+ODvOODs+OBruODh+ODvOOCv+eUn+aIkOOBjOOBp+OBjeOBvuOBl+OBn++8juOBk+OBk+OBp+WVj+mhjOOBq+OBquOCi+OBruOBjO+8jOWQhOODkeOCv+ODvOODs+OBq+OBiuOBkeOCi+acgOWkp+itpuWRiuWvvuixoeiAheaVsOOBp+OBme+8jgoKJCQKbiA9IDRrIOOBp+OBguOCjOOBsO+8jOWJsuOCiuWIh+OCjOOBvuOBmeOBruOBp+W9k+eEtuacgOWkp+WvvuixoeiAheaVsOOBrzIw5ZCN44Go44GE44GG44GT44Go44Gr44Gq44KK44G+44GZ77yO44GX44GL44GX44Gq44GM44KJ77yM5LuW44Gu44OR44K/44O844Oz44KS55So44GE44KL44Go5L2Z44KK44GM5Ye644KL44Gu44GnCiQkCgrjgafjgYLjgozjgbDvvIzlibLjgorliIfjgozjgb7jgZnjga7jgaflvZPnhLbmnIDlpKflr77osaHogIXmlbDjga8yMOWQjeOBqOOBhOOBhuOBk+OBqOOBq+OBquOCiuOBvuOBme+8juOBl+OBi+OBl+OBquOBjOOCie+8jOS7luOBruODkeOCv+ODvOODs+OCkueUqOOBhOOCi+OBqOS9meOCiuOBjOWHuuOCi+OBruOBp++8jAoKYGBge3IgZGl2aXNpb259CjgxLzQKODIvNAo4My80CjgwLzQKYGBgCgrku6XkuIrjga7jgojjgYbjgarntZDmnpzjgavjgarjgorjgb7jgZnvvI7jgafjgZnjgYzjg5Ljg4jvvJHkurrjga/libLjgozjgb7jgZvjgpPjg7vjg7vjg7vvvI7jgarjga7jgaflrabnlJ/lhKrkvY3jgafogIPjgYjjgovjgajjgYTjgZrjgozjga7loLTlkIjjgoLmnIDlpKflr77osaHogIXmlbDjga8yMOWQjeOBqOOBl+OBn+OBhO+8ju+8iOOBk+OBk+OBr+Wkp+WtpuOBlOOBqOOBq+WIpOaWreOBjOWIhuOBi+OCjOOCi+OBi+OCguOBl+OCjOOBvuOBm+OCk++8iSAgCuasoeOBq+WQhOmWouaVsOOCkuS9v+OBo+OBpu+8jOOBqeOBruOCiOOBhuOBq+OBquOCi+OBruOBi+ioiOeul+OBl+OBpuOBv+OBvuOBmQoKCiMjIOWun+mam+OBruioiOeul++8iOWbm+WIhuS9jeaVsO+8jOODkuODs+OCuOaVsO+8jO+8heODqeODs+OCr++8jOe0r+epjeODqeODs+OCr++8iQoK44CA44G+44Ga44GT44Gu5ZWP6aGM44KS6Kej44GP5pa55rOV44Go44GX44Gm5LiA55Wq44Gv44GY44KB44Gr5oCd44GE44Gk44GP44Gu44GM77yM5Zub5YiG5L2N5pWw44KS55So44GE44Gf5pa55rOV44Gn44GZ77yO5Lul5LiL77yM5LiL5YG044OS44Oz44K477yM44OR44O844K744Oz44OI44Op44Oz44Kv77yM57Sv56mN5Ymy5ZCI44Gr5a++5b+c44GZ44KL6Zai5pWw44KS56K66KqN44GX44G+44GZ77yOCgpgYGB7ciBmdW5jdGlvbnN9CiNxdWFudGlsZXPjgpLnlKjjgYTjgZ/oqIjnrpcKcXVhbnRpbGUoc2FtcGxlXzAkR1BBKQoKI+ODkuODs+OCuOaVsApmaXZlbnVtKHNhbXBsZV8wJEdQQSkKCiMl44Op44Oz44KvCnBlcmNlbnRfcmFuayhzYW1wbGVfMCRHUEEpCgoj57Sv56mNCmN1bWVfZGlzdChzYW1wbGVfMCRHUEEpCmBgYAoKcXVhbnRpbGXvvIxmaXZlbnVt44KS5L2/55So44GX44Gf5aC05ZCI44Gv5Luj6KGo5YCk44GM566X5Ye644GV44KM77yMcGVyY2VudF9yYW5r44GK44KI44GzY3VtZV9kaXN044KS5L2/55So44GX44Gf5aC05ZCI44Gv5LiA44Gk44Gu44OH44O844K/44GU44Go44Gr44Op44Oz44Kv44Gu5Ymy5ZCI44GM6KiI566X57WQ5p6c44Go44GX44Gm5b6X44KJ44KM44G+44GZ77yOICAK44CA5qyh44Gv5ZCE44OH44O844K/44K744OD44OI44Gr5a++44GX44Gm77yM5LiK6KiY77yU44Gk44Gu5pa55rOV44KS55So44GE44Gm5LiL5L2NMS8044Gu5a++6LGh6ICF44KS6KiI566X44GV44Gb44G+44GZCmBgYHtyIGNvbXBhcmVkfQojc2FtcGxlXzDjgavlr77jgZfjgaYKc2FtcGxlXzAgJT4lIAogIG11dGF0ZShxcyA9IGlmX2Vsc2UoR1BBIDw9IHF1YW50aWxlKEdQQSlbMl0sMSwwKSkgJT4lIAogIG11dGF0ZShocyA9IGlmX2Vsc2UoR1BBIDw9IGZpdmVudW0oR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKHBzID0gaWZfZWxzZShwZXJjZW50X3JhbmsoR1BBKTw9MC4yNSwxLDApKSAlPiUgCiAgbXV0YXRlKGNzID0gaWZfZWxzZShjdW1lX2Rpc3QoR1BBKTw9MC4yNSwxLDApKSAlPiUgCiAgZ2F0aGVyKGtleSA9IGZ1bmNzLCB2YWx1ZSA9IHRhcmdldCwtR1BBKSAlPiUgCiAgZ3JvdXBfYnkoZnVuY3MpICU+JSAKICBzdW1tYXJpc2Uobl90YXJnZXQgPSBzdW0odGFyZ2V0KSxjdXRsaW5lID0gbWF4KEdQQVt0YXJnZXQ9PTFdKSkKCiNzYW1wbGVfMeOBq+WvvuOBl+OBpgpzYW1wbGVfMSAlPiUgCiAgbXV0YXRlKHFzID0gaWZfZWxzZShHUEEgPD0gcXVhbnRpbGUoR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKGhzID0gaWZfZWxzZShHUEEgPD0gZml2ZW51bShHUEEpWzJdLDEsMCkpICU+JSAKICBtdXRhdGUocHMgPSBpZl9lbHNlKHBlcmNlbnRfcmFuayhHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBtdXRhdGUoY3MgPSBpZl9lbHNlKGN1bWVfZGlzdChHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBnYXRoZXIoa2V5ID0gZnVuY3MsIHZhbHVlID0gdGFyZ2V0LC1HUEEpICU+JSAKICBncm91cF9ieShmdW5jcykgJT4lIAogIHN1bW1hcmlzZShuX3RhcmdldCA9IHN1bSh0YXJnZXQpLGN1dGxpbmUgPSBtYXgoR1BBW3RhcmdldD09MV0pKQoKI3NhbXBsZV8y44Gr5a++44GX44GmCnNhbXBsZV8yICU+JSAKICBtdXRhdGUocXMgPSBpZl9lbHNlKEdQQSA8PSBxdWFudGlsZShHUEEpWzJdLDEsMCkpICU+JSAKICBtdXRhdGUoaHMgPSBpZl9lbHNlKEdQQSA8PSBmaXZlbnVtKEdQQSlbMl0sMSwwKSkgJT4lIAogIG11dGF0ZShwcyA9IGlmX2Vsc2UocGVyY2VudF9yYW5rKEdQQSk8PTAuMjUsMSwwKSkgJT4lIAogIG11dGF0ZShjcyA9IGlmX2Vsc2UoY3VtZV9kaXN0KEdQQSk8PTAuMjUsMSwwKSkgJT4lIAogIGdhdGhlcihrZXkgPSBmdW5jcywgdmFsdWUgPSB0YXJnZXQsLUdQQSkgJT4lIAogIGdyb3VwX2J5KGZ1bmNzKSAlPiUgCiAgc3VtbWFyaXNlKG5fdGFyZ2V0ID0gc3VtKHRhcmdldCksY3V0bGluZSA9IG1heChHUEFbdGFyZ2V0PT0xXSkpCgojc2FtcGxlXzPjgavlr77jgZfjgaYKc2FtcGxlXzMgJT4lIAogIG11dGF0ZShxcyA9IGlmX2Vsc2UoR1BBIDw9IHF1YW50aWxlKEdQQSlbMl0sMSwwKSkgJT4lIAogIG11dGF0ZShocyA9IGlmX2Vsc2UoR1BBIDw9IGZpdmVudW0oR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKHBzID0gaWZfZWxzZShwZXJjZW50X3JhbmsoR1BBKTw9MC4yNSwxLDApKSAlPiUgCiAgbXV0YXRlKGNzID0gaWZfZWxzZShjdW1lX2Rpc3QoR1BBKTw9MC4yNSwxLDApKSAlPiUgCiAgZ2F0aGVyKGtleSA9IGZ1bmNzLCB2YWx1ZSA9IHRhcmdldCwtR1BBKSAlPiUgCiAgZ3JvdXBfYnkoZnVuY3MpICU+JSAKICBzdW1tYXJpc2Uobl90YXJnZXQgPSBzdW0odGFyZ2V0KSxjdXRsaW5lID0gbWF4KEdQQVt0YXJnZXQ9PTFdKSkKCmBgYAoK5pyA5Yid44Gr5oyH5pGY44GX44Gf44KI44GG44Gr77yM44GZ44G544Gm44Gu44OH44O844K/44K744OD44OI44Gu44OR44K/44O844Oz44Gn77yM6K2m5ZGK5a++6LGh6ICF44GvMjDlkI3jgavjgarjgovjgojjgYbjgavjgZfjgZ/jgYTjgajjgZPjgo3jgafjgZnjgYzvvIzjgZnjgbnjgabjga7jg5Hjgr/jg7zjg7Pjgaflr77osaHogIXjgYwyMOWQjeOBq+OBquOBo+OBpuOBhOOCi+OCguOBruOBjOOBguOCiuOBvuOBm+OCk++8ju+8ju+8ju+8juOBoeOCh+OBo+OBqOimi+OChOOBmeOBj+aVtOeQhuOBl+OBvuOBme+8jgoKYGBge3IgdGFibGV9CnJiaW5kKApzYW1wbGVfMCAlPiUgCiAgbXV0YXRlKHFzID0gaWZfZWxzZShHUEEgPD0gcXVhbnRpbGUoR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKGhzID0gaWZfZWxzZShHUEEgPD0gZml2ZW51bShHUEEpWzJdLDEsMCkpICU+JSAKICBtdXRhdGUocHMgPSBpZl9lbHNlKHBlcmNlbnRfcmFuayhHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBtdXRhdGUoY3MgPSBpZl9lbHNlKGN1bWVfZGlzdChHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBnYXRoZXIoa2V5ID0gZnVuY3MsIHZhbHVlID0gdGFyZ2V0LC1HUEEpICU+JSAKICBtdXRhdGUoZGF0YXNldCA9ICJzYW1wbGVfMCIpLApzYW1wbGVfMSAlPiUgCiAgbXV0YXRlKHFzID0gaWZfZWxzZShHUEEgPD0gcXVhbnRpbGUoR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKGhzID0gaWZfZWxzZShHUEEgPD0gZml2ZW51bShHUEEpWzJdLDEsMCkpICU+JSAKICBtdXRhdGUocHMgPSBpZl9lbHNlKHBlcmNlbnRfcmFuayhHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBtdXRhdGUoY3MgPSBpZl9lbHNlKGN1bWVfZGlzdChHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBnYXRoZXIoa2V5ID0gZnVuY3MsIHZhbHVlID0gdGFyZ2V0LC1HUEEpICU+JSAKICBtdXRhdGUoZGF0YXNldCA9ICJzYW1wbGVfMSIpLApzYW1wbGVfMiAlPiUgCiAgbXV0YXRlKHFzID0gaWZfZWxzZShHUEEgPD0gcXVhbnRpbGUoR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKGhzID0gaWZfZWxzZShHUEEgPD0gZml2ZW51bShHUEEpWzJdLDEsMCkpICU+JSAKICBtdXRhdGUocHMgPSBpZl9lbHNlKHBlcmNlbnRfcmFuayhHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBtdXRhdGUoY3MgPSBpZl9lbHNlKGN1bWVfZGlzdChHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBnYXRoZXIoa2V5ID0gZnVuY3MsIHZhbHVlID0gdGFyZ2V0LC1HUEEpICU+JSAKICBtdXRhdGUoZGF0YXNldCA9ICJzYW1wbGVfMiIpLApzYW1wbGVfMyAlPiUgCiAgbXV0YXRlKHFzID0gaWZfZWxzZShHUEEgPD0gcXVhbnRpbGUoR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKGhzID0gaWZfZWxzZShHUEEgPD0gZml2ZW51bShHUEEpWzJdLDEsMCkpICU+JSAKICBtdXRhdGUocHMgPSBpZl9lbHNlKHBlcmNlbnRfcmFuayhHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBtdXRhdGUoY3MgPSBpZl9lbHNlKGN1bWVfZGlzdChHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBnYXRoZXIoa2V5ID0gZnVuY3MsIHZhbHVlID0gdGFyZ2V0LC1HUEEpICU+JSAKICBtdXRhdGUoZGF0YXNldCA9ICJzYW1wbGVfMyIpCikgJT4lIAogIG11dGF0ZShmdW5jcyA9IGZjdF9pbm9yZGVyKGZ1bmNzKSkgJT4lIAogIGdyb3VwX2J5KGZ1bmNzLCBkYXRhc2V0KSAlPiUgCiAgc3VtbWFyaXNlKG5fdGFyZ2V0ID0gc3VtKHRhcmdldCkpICU+JSAKICBzcHJlYWQoa2V5ID0gZnVuY3MsIHZhbHVlID0gbl90YXJnZXQpCmBgYAoKCuS4iuiomOOBruioiOeul+e1kOaenOODh+ODvOODluODq+OBi+OCieaWueazlWNz77yM44Gk44G+44KK57Sv56mN5Ymy5ZCI44KS5rGC44KB44KLImN1bWVfZGlzdCLplqLmlbDjgpLkvb/jgaPjgZ/ntZDmnpzjgaDjgZHjgYzvvIxuIDw9MjDjgajjgarjgaPjgabjgYTjgb7jgZnvvI5zYW1wbGVfMeOBrue1kOaenOOBr24gPSAxOeOBqOOBquOBo+OBpuOBiuOCiu+8jOacgOWkp+itpuWRiuWvvuixoeiAheOCkuS4i+WbnuOBo+OBpuOBl+OBvuOBmeOBjO+8jOOBiuOBneOCieOBj+OCq+ODg+ODiOODqeOCpOODs+OBq+WQjOeCueiAheOBjOOBhOOBn+OBn+OCgeOBp+OBguOCi+OBqOiAg+OBiOOCieOCjOOBvuOBme+8juS7peS4i+OBp+eiuuiqjeOBl+OBpuOBv+OBvuOBme+8jgoKYGBge3IgY2hlY2t9CnNhbXBsZV8xICU+JSAKICAgIG11dGF0ZShxcyA9IGlmX2Vsc2UoR1BBIDw9IHF1YW50aWxlKEdQQSlbMl0sMSwwKSkgJT4lIAogIG11dGF0ZShocyA9IGlmX2Vsc2UoR1BBIDw9IGZpdmVudW0oR1BBKVsyXSwxLDApKSAlPiUgCiAgbXV0YXRlKHBzID0gaWZfZWxzZShwZXJjZW50X3JhbmsoR1BBKTw9MC4yNSwxLDApKSAlPiUgCiAgbXV0YXRlKGNzID0gaWZfZWxzZShjdW1lX2Rpc3QoR1BBKTw9MC4yNSwxLDApKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoKT49NTUgJiByb3dfbnVtYmVyKCkgPD02NSApCmBgYArkuojmg7PpgJrjgorvvIzjgqvjg4Pjg4jjg6njgqTjg7PjgatHUEExLjc044Gu5a2m55Sf44GM77yS5ZCN44GE44G+44GX44Gf77yO44GT44Gu5a2m55Sf44Gf44Gh44KS77yS5ZCN44Go44KC6K2m5ZGK5a++6LGh6ICF44Go44GZ44KL44Go77yM5ZCI6KiI44Gu6K2m5ZGK5a++6LGh6ICF44GvMjHlkI3jgajjgarjgaPjgabjgZfjgb7jgYTjgb7jgZnjga7jgafvvIzjgZPjgozjgafnm67nmoTjgavlkIjoh7TjgZfjgZ/lr77osaHogIXoqIjnrpfjgYzooYzjgo/jgozjgabjgYTjgovjgZPjgajjgYznorroqo3jgafjgY3jgb7jgZfjgZ/vvI4KCiMjIOazqOaEj+eCueOAgArku6XkuIrjga7jgojjgYbjgavvvIxjdW1lX2Rpc3TplqLmlbDjgpLkvb/nlKjjgZnjgovjgajvvIznsKHljZjjgavoqIjnrpfjgYzjgafjgY3jgZ/jgo/jgZHjgafjgZnjgYzvvIzjgbLjgajjgaTms6jmhI/ngrnjgYzjgYLjgorjgb7jgZnvvI7jgZ3jgozjga/jgZPjga7plqLmlbDjga7jg5DjgrDjga7jgojjgYbjgarjgoLjga7jgafvvIzkuIroqJjjga7jgojjgYbjgatpZl9lbHNl44Gu44Kr44OD44Kz5YaF44Gn6KiI566X44GZ44KM44Gw5ZWP6aGM44Gq44GE44Gu44Gn44GZ44GM77yM5LiA5bqm6KiI566X57WQ5p6c44KS5paw6KaP5YiX44Gr6L+95Yqg44GX44Gm44GL44KJ77yMaWZfZWxzZeOBp+WvvuixoeiAheOBruS6jOWApOioiOeul+OCkuOBmeOCi+OBqO+8jOWJsuOCiuWIh+OCjOOBn+OBr+OBmuOBruaVsOWApOOBjOWvvuixoeOBqOOBquOCieOBquOBhOOBqOOBhOOBhuWVj+mhjOOBp+OBme+8jiAgCuOCj+OBi+OCiuOBq+OBj+OBhOOBruOBp+S7peS4i+OBq+S+i+ekuuOBl+OBvuOBme+8jgoKYGBge3IgcHJvYmxlbX0Kc2FtcGxlXzAgJT4lIAogIG11dGF0ZShjc19pbmMgPSBpZl9lbHNlKGN1bWVfZGlzdChHUEEpPD0wLjI1LDEsMCkpICU+JSAKICBtdXRhdGUoY3VtX3JhdGlvID0gY3VtZV9kaXN0KEdQQSkpICU+JSAKICBtdXRhdGUoY3Nfb3V0ID0gaWZfZWxzZShjdW1fcmF0aW8gPD0gMC4yNSwxLDApKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKCk+PTU1ICYgcm93X251bWJlcigpIDw9NjUgKQpgYGAKCuOBk+OBruOCiOOBhuOBq+S4gOW6pmN1bV9yYXRpb+WIl+OBq2N1bWVfZGlzdOOBrue1kOaenOOCkui/veWKoOOBl+OBpuOBi+OCie+8jGlmX2Vsc2Xjgadjc19vdXTjgavlr77osaHogIXjgpLoqIjnrpfjgZnjgovjgajvvIwwLjI15Lul5LiL44Gu5p2h5Lu244Gu44Gv44Ga44GM77yMMC4yNeOBrue0r+epjeWJsuWQiOOCkuaMgeOBpOODrOOCs+ODvOODieOBjOWvvuixoeOBqOOBquOCiuOBvuOBm+OCk++8juOBiuOBneOCieOBj+ODkOOCsOOBoOOBqOaAneOCj+OCjOOBvuOBme+8jgoKIOOBk+OBk+OBq+iomOi8ieOBl+OBn+ODl+ODreOCsOODqeODoOetieOBr++8kuasoeWIqeeUqOOBl+OBpuOBhOOBn+OBoOOBhOOBpk9L44Gn44GZ77yO44Gf44Gg44GX77yM5a6f6Zqb44Gu5aSn5a2m5qWt5YuZ44Gr5L2/55So44GZ44KL6Zqb44Gr44Gv77yM6Ieq5bex6LKs5Lu744Gn44GK6aGY44GE44GX44G+44GZ77yO44OX44Ot44Kw44Op44Og44KS5L2/55So44GX44Gf44GT44Go44Gr44KI44KL5LiN5Yip55uK562J44Gv6LKs5Lu744KS6LKg44GE44GL44Gt44G+44GZ44Gu44Gn5LqI44KB44GU5LqG5om/44GP44Gg44GV44GE77yOICAK44CACuOAgOOBvuOBn++8jOS4jeWCmeetieOCguOBguOCjeOBhuOBi+OBqOaAneOBhOOBvuOBmeOBruOBp++8jOOBlOizquWVj+etieOBr+ilv+WxsShrLm5pczgwW2F0XWdtYWlsLmNvbSnjgb7jgafjgYrpoZjjgYTjgZfjgb7jgZnvvI4=