absfuyu 5.0.0__py3-none-any.whl → 6.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (103) hide show
  1. absfuyu/__init__.py +5 -3
  2. absfuyu/__main__.py +3 -3
  3. absfuyu/cli/__init__.py +13 -2
  4. absfuyu/cli/audio_group.py +98 -0
  5. absfuyu/cli/color.py +30 -14
  6. absfuyu/cli/config_group.py +9 -2
  7. absfuyu/cli/do_group.py +23 -6
  8. absfuyu/cli/game_group.py +27 -2
  9. absfuyu/cli/tool_group.py +81 -11
  10. absfuyu/config/__init__.py +3 -3
  11. absfuyu/core/__init__.py +12 -8
  12. absfuyu/core/baseclass.py +929 -96
  13. absfuyu/core/baseclass2.py +44 -3
  14. absfuyu/core/decorator.py +70 -4
  15. absfuyu/core/docstring.py +64 -41
  16. absfuyu/core/dummy_cli.py +3 -3
  17. absfuyu/core/dummy_func.py +19 -6
  18. absfuyu/dxt/__init__.py +2 -2
  19. absfuyu/dxt/base_type.py +93 -0
  20. absfuyu/dxt/dictext.py +204 -16
  21. absfuyu/dxt/dxt_support.py +2 -2
  22. absfuyu/dxt/intext.py +151 -34
  23. absfuyu/dxt/listext.py +969 -127
  24. absfuyu/dxt/strext.py +77 -17
  25. absfuyu/extra/__init__.py +2 -2
  26. absfuyu/extra/audio/__init__.py +8 -0
  27. absfuyu/extra/audio/_util.py +57 -0
  28. absfuyu/extra/audio/convert.py +192 -0
  29. absfuyu/extra/audio/lossless.py +281 -0
  30. absfuyu/extra/beautiful.py +3 -2
  31. absfuyu/extra/da/__init__.py +72 -0
  32. absfuyu/extra/da/dadf.py +1600 -0
  33. absfuyu/extra/da/dadf_base.py +186 -0
  34. absfuyu/extra/da/df_func.py +181 -0
  35. absfuyu/extra/da/mplt.py +219 -0
  36. absfuyu/extra/ggapi/__init__.py +8 -0
  37. absfuyu/extra/ggapi/gdrive.py +223 -0
  38. absfuyu/extra/ggapi/glicense.py +148 -0
  39. absfuyu/extra/ggapi/glicense_df.py +186 -0
  40. absfuyu/extra/ggapi/gsheet.py +88 -0
  41. absfuyu/extra/img/__init__.py +30 -0
  42. absfuyu/extra/img/converter.py +402 -0
  43. absfuyu/extra/img/dup_check.py +291 -0
  44. absfuyu/extra/pdf.py +87 -0
  45. absfuyu/extra/rclone.py +253 -0
  46. absfuyu/extra/xml.py +90 -0
  47. absfuyu/fun/__init__.py +7 -20
  48. absfuyu/fun/rubik.py +442 -0
  49. absfuyu/fun/tarot.py +2 -2
  50. absfuyu/game/__init__.py +2 -2
  51. absfuyu/game/game_stat.py +2 -2
  52. absfuyu/game/schulte.py +78 -0
  53. absfuyu/game/sudoku.py +2 -2
  54. absfuyu/game/tictactoe.py +2 -3
  55. absfuyu/game/wordle.py +6 -4
  56. absfuyu/general/__init__.py +4 -4
  57. absfuyu/general/content.py +4 -4
  58. absfuyu/general/human.py +2 -2
  59. absfuyu/general/resrel.py +213 -0
  60. absfuyu/general/shape.py +3 -8
  61. absfuyu/general/tax.py +344 -0
  62. absfuyu/logger.py +806 -59
  63. absfuyu/numbers/__init__.py +13 -0
  64. absfuyu/numbers/number_to_word.py +321 -0
  65. absfuyu/numbers/shorten_number.py +303 -0
  66. absfuyu/numbers/time_duration.py +217 -0
  67. absfuyu/pkg_data/__init__.py +2 -2
  68. absfuyu/pkg_data/deprecated.py +2 -2
  69. absfuyu/pkg_data/logo.py +1462 -0
  70. absfuyu/sort.py +4 -4
  71. absfuyu/tools/__init__.py +28 -2
  72. absfuyu/tools/checksum.py +144 -9
  73. absfuyu/tools/converter.py +120 -34
  74. absfuyu/tools/generator.py +461 -0
  75. absfuyu/tools/inspector.py +752 -0
  76. absfuyu/tools/keygen.py +2 -2
  77. absfuyu/tools/obfuscator.py +47 -9
  78. absfuyu/tools/passwordlib.py +89 -25
  79. absfuyu/tools/shutdownizer.py +3 -8
  80. absfuyu/tools/sw.py +718 -0
  81. absfuyu/tools/web.py +10 -13
  82. absfuyu/typings.py +138 -0
  83. absfuyu/util/__init__.py +114 -6
  84. absfuyu/util/api.py +41 -18
  85. absfuyu/util/cli.py +119 -0
  86. absfuyu/util/gui.py +91 -0
  87. absfuyu/util/json_method.py +43 -14
  88. absfuyu/util/lunar.py +2 -2
  89. absfuyu/util/package.py +124 -0
  90. absfuyu/util/path.py +702 -82
  91. absfuyu/util/performance.py +122 -7
  92. absfuyu/util/shorten_number.py +244 -21
  93. absfuyu/util/text_table.py +481 -0
  94. absfuyu/util/zipped.py +8 -7
  95. absfuyu/version.py +79 -59
  96. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/METADATA +52 -11
  97. absfuyu-6.1.2.dist-info/RECORD +105 -0
  98. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
  99. absfuyu/extra/data_analysis.py +0 -1078
  100. absfuyu/general/generator.py +0 -303
  101. absfuyu-5.0.0.dist-info/RECORD +0 -68
  102. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
  103. {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
absfuyu/fun/rubik.py ADDED
@@ -0,0 +1,442 @@
1
+ """
2
+ Absfuyu: Fun
3
+ ------------
4
+ Rubik
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+
9
+
10
+ Version added: 5.4.0
11
+ """
12
+
13
+ # Module level
14
+ # ---------------------------------------------------------------------------
15
+ __all__ = ["RubikNotations", "Rubik3x3"]
16
+
17
+
18
+ # Library
19
+ # ---------------------------------------------------------------------------
20
+ from typing import ClassVar, Self, cast, final
21
+
22
+ # Data
23
+ # ---------------------------------------------------------------------------
24
+ PLL_PIC = {
25
+ "Aa": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAICSURBVHhe7do9TsNAEIZhhwNQQEtJxf3PQkVJCwUXSPKFjFhZrD3e3/l7JUQiRFg9HlsOm9OyLOfrV7TRDen88/ukR6fH6x9Q/voP98fRRoHEyAXS99vr/VFZbiapBsoF0tP7x+17KZSbSaqBYt8C1J7X0iK0vdzeAnCBKDc3k3QmHAbyMkmlQNThScpdm3ILkDBJWHMxkJdJygEBL3fQ06qRsIDSo6SlaiTOkdBeMVI6PdahqibJC9RhpPU1yANU9TUJWYdqgoQsQzVDQlahmiIhi1DNkVAK9fWiH6rrvlsK9Pz5ByclWt/e2rr/qyQ95dIJa1XNG2ha29a6hrzBTY+S1mtUdySk/WI+BAlphhqGhLRCDUVCGqGGIyFtUFOQkCaoaUhIC9RUJKQBajoSkg4lAglJhhKDhKRCiUJCEqHEISFpUCKRkCQosUhICpRoJCQBSjwSmg2lAgnNhFKDhGZBqUJCM6DUIaEUasS+Xtd9t97V7uuJ2Xfr/cHS9JRLJ4wT/e7W75n4YOmIfT31SKj3xdwEEuoJZQYJ9YIyhYR6QJlDQq2hTCKhFKo2s0gIUOupyk3W1s9MI60jsDUGPc9Nn/o77pLX/29iskAW7rhLWoPkgCiXSIhg9oCQy9PtSG5Pt6MFEqNAYhRIjAKJUSAxCiRGgcQokBip3ncb07JcAGos+ItnN7h+AAAAAElFTkSuQmCC",
26
+ "Ab": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHuSURBVHhe7dtNTsMwEIbhlAOwgC1LVtz/LKxYsoUFFyj90o4URYkztmN7/l4JkQqlVE+dUVTDZZqm6+0rSjQjXf/uD1p0eb79AuXP//Q4jhIFEqNAYhRIjAKJUSAxCiRGgcQokBi5Rvr9eH8cpXOLREAcKJdIa5gjKHdIBPLy+TV/p1JQrpD2gOjxHpTpj0r2UKijnyPTH5WkLp/cTCItgVKrhJs5pLOBkCmkFkDIDFIrIGQCqSUQUo/089YWCKned1sCvX7nA9H5R+eqvZk84xKj50idr/ZmcglUsoJyU4fUekhvpQppBBBSgzQKCKlAGgmExCONBkKikSQAIbFIUoCQSCRJQEgckjQgJApJIhASgyQVCIlAkgyEhiNJB0JDkTQAoWFIWoDQECRNQKg7kjYg1BVJIxDqhqQVCHVB0gyEmiP12BdrXdN9t9p9sdYN33frdYnV7OvRa0y9vmb7bksgiSsot9ORtA/prU5FsgiETkOyCoROQbIMhLKRALJEsQ6EqlaSByBUjOQFCFXPJOtAqBppPaMsVo2kob03EVcB50ow/Q84FCGVjAa1fzOZG+GUjgX2SrI0d3JWlJuVtC73DXcxk6iS2eRqJdUMb1e3ACVAyM1KKgVCrmZSSa5mUk2BxGi+3O6H0XbT9A9hSvCXpdP6AwAAAABJRU5ErkJggg==",
27
+ "E": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFrSURBVHhe7dkxbsIwGEBhwwEYysrI1PufhYmxKx16gUCBf4mK8xwH1VbeJ1UlqhT/ehLUJpuU0nD7UcY90vDzuHiHze62QOf33z5fK8NIgJEAIwFGAowEFEX6/jw+X/Vp7vw4UizQa6ia+VGk8Y17C1U7/2SkuOHH6Xz/HXoJtcT82UivFojr1kMtNX/R2e3Vojktnd3mzl/0322tjAQYCTASYCTASICRACMBRgKMBBQ9d7scHtv6/Rff1rdk7vye3SZ4doOMBBgJMBJgJMBIgJEAIwFGAowEGAkwEmAkwEiAkQAjAUYCjAQYCTASYCTASIDP3QCfu03wuRtkJMBIgJEAIwFGAowEGAkwEpCN9LtDjV3qWO5vrVhq/myk2L6PbxbXJdv7/7DU/Ojs9ldxvEADZ7fa+dFn0viGdIFW1M6PP7jjxr0FCjXzF31VMkcLb7ca+O22dkYCjAQYCTASYCTASEDRc7d1SukKxuWXT5bSG9QAAAAASUVORK5CYII=",
28
+ "F": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAF7SURBVHhe7dmxTcNAHEbxCwNQMAMVNQMwBUMyBQNQUzEDBQuEyPjK+N53EpL/zvtJKE5z+J590jk+tdbOlz9tWCKdf/6+/IfT/eUfFB//bj3WBiMBRgKMBBgJmIr0/fS4HtUzc+5xpMqBunQOUaQ++MPn1/JZUT/3JBSOdIRAXRoK7bjT27OarQuPd9xHuHuuIXOLnt1mltwen92SecTPbn3Qystv5kLHW4Bk8L1K5+BPJQPxcrtVRgKMBBgJMBJgJMBIgO/dADeTA24mISMBRgKMBBgJmIr08vqxHtUzc+5xpMqBunQOUaQ++Pvb8/JZUT/3JBSOdIRAXRoK7bjT27OarQuPd9xHuHuuIXOLnt1mltwen92SecTPbn3Qystv5kLHW4Bk8L1K5+BPJQPxcrtVRgKMBBgJMBJgJMBIgO/dADeTA24mISMBRgKMBBgJMBJgJMBIgJEAIwFGAowEGAkwEmAkwEiAkQAjAUYCjAQYCTAS4Hu3odZ+AfiyiXn4ccLwAAAAAElFTkSuQmCC",
29
+ "Ga": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALwSURBVHhe7Zq9jtQwFEazPAAFtJRUiHJ7KKkpEI+IKKgpoadEVFvSQsEL7O43k6u9O9jJ/bNjJz7Saj3SjJM5ud+Nk8zVNE2393+DBU6Sbv+dX5Tg6un9Bjqf/8k8HiwwJAkYkgQcQtLfVy/nkY3DVJJHVJNnt7cffsyjx3z7fD2PHpDOT5Ke/bo5/ZeC+U2SNEfl+e+b6c8L3VF8//rTPHrMl58f59EDlvk1orpZAkBOSpAVbfSaiRtFDJFKxS0VNaCJsyVyokryNDwpkh6Ue48Ua08Ci5Jo4pKi+JdPVUuEKI8gkI1bSoxlI0txWBPEyb1XEjd8F7OgXNxy5iMrSiMIeCrKKoj4T1JOEL2OEKUVRHhEeVg9u+WkSbmMg1UQh8/x/et1Nm7efQeis1skEYIA/+ybd+mKimwN1SRFCSKWoscFefsRqCIpWhCREhUtCBSXxOMQKYhATyL4wYgSBIpKKlVBl/C5cXEcKQgUk1RLEEDE+AUw33YERSTxneRxKE2qR0VwWiedh2noXg3u20jgPaimIE70PoQuJlMRk1xbWaD9wsFLzR8V99DFZNROSeCn+RyR0QuRtJWgteqOEuWW1KogIkKUShI2wjfUuiDCK8pcSb0IIjyiTJJ6E0RYRbl6Uk+CCMs+q9ZJOftLG7auk6SCSq3DiOo33aSUqCCg7UVE9du3a2gFaeYnSZrINVdJpSqIIDnaimpGUmlBhEWUOm7WXLfKWvSabdy1kPamTRt3RMS0JwZt8960kmr1II7l7AY2kbSFIEIrCFSX1JsgUFXSloI8VJPUqyBQRVLPgkBxSfznwz0KAqd10nmYRvvcjcMFWT7fCsUWk7Uipl1Maim2mOSCeq4gIlxS7006RaikPQoCYZL2KgiESNqzIKCWBCFcyt4FAVclHUEQMEs6iiDg7kl7FwTcki571B5xSzoCzT3B1dLttdveWK0kL6OSDsKQJGBIEjAkCRiSBAxJAoYkAUOSgNXnboNpugNSNscojmPPkQAAAABJRU5ErkJggg==",
30
+ "Gb": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALbSURBVHhe7ZqxbtwwDIadPECGZM3YqciYPR07dyj6iEWHzBmTvWORqWPWdugLXPJfTIAQTjZFUTLp6gMOp/MBsv2JpKXTnU3TdHh7DRY4Sjr8e//QgrOLtxME7/98bg8WGJIEDEkCqiT9/fhhbu0btaT/RRBQSSJBl8+/j+97p3gKED2CSge2eArQUtCXm+9zq4yrl/bRrI4k6YhIJnufvv6cW9P0+ON2br1D36XHCZeTSZJjFVVcEEg/E7njtUjuQ1W4rUSlN07Rwo/zCLIWRde/dh/qKUDtk41uOE2jXqJSMUuiXCxwc8JSTonT1KRUCAacjqWDr6pJW2IRUTkZSyWkWhI6PdVxK2pFQUYqiMh9ZxZJW4m6+2xTo5aolsTNR4qoEkwiae+izNJtK1FPD+1FmUkCe40oU0lgj6LMJQEu6s91fFFN9924oKWfNOgxzutLDXxaYNFn82UJTzkeYRwadR4JUnLLEh5JuX7p2nLXBbosS3gERa1RzSWB6MW8iyQQWVQ3SSCqqK6SgDdR2IBYE9ddEogWUZtIAlxUT7go6TbWZpKAB1ESNpUEeqce0qy0eG8uKaVnjZLiarfk/te34zuQpqJmt4SgAVk6l7vdkl6pV5py7tKttajSegTcSQKtRHFBJU84l5KAtSitIOBWErASVSMIuJYEakXVCgLuJQGtKAtBIIQkUCrKShAIIwlIRVkKAqEkgTVR1oJAOEmAi+K0EASa7rtJsdh3s95r44T6O+Ap0D8XVNIHpWsuMoG7Ba4GLoj/imBJaEm8BpGgknmUlLCS0iJdOo8qIaQkLogX6Vaiwklae8y3EBVK0pogIicKbY04t5IghEuRCiIsIypEJJUKIqxEuZekFURwUVrC1CSNIG0NSgkjKa1RPXGxdjtFTkgaUdr+AUXZUkqif7eSpAxJAnr0H6YmbcmQJGBIEjAkCRiSBAxJAoYkAUOSABf7br6ZpleZlqpZ7/HSYgAAAABJRU5ErkJggg==",
31
+ "Gc": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAL8SURBVHhe7Zq/jtQwEIdzPAAFtJRUiJIeSmoKxCOerqCmhJ4SUVHSQsELHPvL7uhGudiZv9k48SetNtFqbefzzKyd7M0wDPenV6fCKOn+3/kkg5unpw4ab//J5bhToUsS0CUJ6JIEdEkCuiQBXZKALknAoqS/r16OryPTI0nA4raEoujZz1/jeymq6PMpfVtyENySEEGlKNoLbklHKOpmSTx69i7KFUlHEaWWNK1BRxDlrklg76JCJIE9iwqTBPYqKlQS2KOocEmAi/rzon1Ri8/d6CKf/364cClckOX7W0G9wdXCU87aRo1dbHB5BLVao9IlgdaL+SqSQMuiVpMEWhW1qiRwTVHvPn6/HOlYXRJoTdRVJIFriPp692Z814pSr5OsIbtFSFqNwz8IkE54+opbuiLmKafpy7LiJjnNRdJaNUojiNhUumWLsggCqnQr5XCtU0s6aFJP0z7Grxa0pXTjZEWUVhDhkoROrR0vkZl6iKhSVszhkqTpyEKGKMuYTZJ49LQkio9VkwHmSGpNlFUQcC8mlzq3/LqV4IJoPJL2XYIift22HlEeQYRbEtiqqAhBIEQSuJaoElGCwFiTzofzaJ+7vX3/MLhvX3yDsxI9hpS7AHwWMciowj0HCitNJMYYGUEgbVvCB8dnNZtoQUSKJLB2jfrw+vZyFisIpEkCa4nibX/+8Um8PJCSKgnwwpkhiqczn5RIUemSQFZE8baoD806SsoqkkC0qDlBRLSo1SSBKFH8u6V1kGbJssQjSTBfsl/7TIpXVC2CpkBUhKxHkqjRqQw6j+jUKkojKJLZdCuJihBEaEV5BHmjv1iTpkIiBRElUTienhPWCPKIqhZuEpMhiFiKqAhBNH6rqKokkCmIKF18hCDCI2rxLsAcmo5wi0X7X26+DwPYapSwtA+kk592FyAaSJuK86DNDlMkaZDcqJ8yV5vAXMpJ26foVws6tb9JSRok7VsFAbTfRLpFYBFEHCKSPBwqkjx0SQK6JAFjTTofduYZhv+H7r9QKUdxcgAAAABJRU5ErkJggg==",
32
+ "Gd": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALLSURBVHhe7dmxbtRAEMZxhweggJaSCqVMDyU1BeIREQU1ZdJTIipKWih4gSTfcSMNy+16ZnbWu2PvX0Lnu6AL/G68ts9Xy7LcP/6ZFToh3f/5+6RFV08ff0Hw939y3p4VmkiCJpKgQyD9fvXyvGXrMJNUA3UIpGfff5werVCmU4Da8e0doUna5BTg+U/5P4j37vrjecs/7Yfc9WTyzfuvp8fbTzenR4peR+nP0jQnk4TjPkmtdi0OwUtfz/09bRYgqohEb+wNlZsUer00WZZqgFAWKYXxgtIC0fNaKCsQurgmcfkUR/vL+JqRA7pUDi1NsyZZurgm5UaTnlsnSgM0Wv8hASM3LaWfleoFhA/U+qHyVo9utb1+2w/Iq6ZIPSeIskx+WjOkvQChJkgc6O5LbCDkjrSnCaJckfYIhNyQegH9etEWCLncd+OHea81iN6z9H4cyPqVjKTVr0polHOf0toEWS8b6H1zU9l6F6NEX5WUGmENajlBlBlpr4v0pUxIRwJCaqSjAaEiEkD4F/JHBELiSToqEBIh8WnaEojXCwip1qQjAiEVEnY5vtu1aoRdjKdC2iIONErVlyVraS5LOBCtg2u7eJe7Jbla72qj7WI89e7WAmpkICRG4mPvCTU6EFJNkjdUBCCk3t28oKIAITUSqoWKBIRMSMgKFQ0ImZGQFioiEKpCQlKoqECoGgmtQUUGQi5ISDJREYGQy303ntc9OMl9t61qcoHLJwn/yRb33aihLnA1ff724bz172RFzR2JJo9DlY56EXJFSo9i2vOoUXNDyh3m+cIbFcoFae08KPpEqZEAwlHWgKjIUFWTJAWiclDYHhnOjKQFoiJOVPWapAGiokFVI6VrlDQONXrVSJZGX4PSutyczAFZpmuLa7eh7uBaGgKptj0gdVmTojWRBE0kQRNJ0EQSNJEETSRBE0mQ+323/bUsD7Leq1kMq2/FAAAAAElFTkSuQmCC",
33
+ "H": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGJSURBVHhe7dlBboJAHEZx7AF6mu7bZU/QQ3oCl+2+p+kFrKizIYJvJlE+4L3EVNJE5ScY/syu67rj6WETnZGOf5eNR7R7Pb3Bwl//5frcJhIJJBJIJJBIIJFAUUgfX7/XZ1nFIBWgRKgIpCFMGtTsSAXke/92/ltKgpoVaQyobKdARc1uY2hTObuFJBJIJJBIIJFAIoFEAokEEgkkEihq3e398zKW/Bz4WPKMnN3u5OwGEwkkEkgkkEggkUAigaqRklYxamv97FVISwYqtewDRmq5Gk6rdakKjSVrOIJuRb5wNJasFaiP7lvVkdRyqiUOuDXvgwfc1nM5sZYvAv9wrwGqBaiv6hKg9sUTa9mHKqS+JUO1fvZqpC0mEkgkkEggkUCuu4Fcd7sTHku2nkggkUAigUQCiQQSCSQSSCTQrEj9FfbY7eCp/z27WZHK+DHEKNspd0EjZrdbRwwF2szsNgRJOYJKMT/cBSYNqC/qVklLmznd0hMJJBJIJJBIIJFAIoGi1t0y67p/nuaai10GTQQAAAAASUVORK5CYII=",
34
+ "Ja": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHlSURBVHhe7dkhT8NAGMbxjg+AAItEESQeJBpB+IgEgUaCRxLUJBYEX2Db07VJt2zrc2/fdnnvnl9COARt9+/ddllnVVUtVj9yQB1p8b/+Ywyz09UJgh//pBnLAYpEUCSCIhEUiaBIBEUimCL9XV02oxiGXm9ypGiBWkOuO2nH3Z7o7Hte/2b07YjvHj+b0ab3l5tmdBi747ZcO+D4dCTrnTj/mVe/F/v/9+H6uRltev16akb+Um8ytdzGXGKIMWaQXVJfT/JMskzXvuPvWnLeyw0sr4GeSdAe2HtWdQOxYSysNxmSPt28Q0UIBKbvk3BS9oT7loNXIGa5pVzvNhz/KF+6ec4gJtIQOL5pxz3EVEvM06SRIgaCySJFDQSTRIocCEaPdHsfOxDUn27rob9uoI+3mIFgtC3AVEss7BagGyjyDGq5R4r+Jr2La6QcA4FbpFwDgUuknAPB4Ei5B4JBkUoIBOZIpQQCKhKCdKOUFAiSZ1JpgSApUomBwPSeVFIgMEXafo/KnSlSaY7ytMRTlk9LIlIkgiIRFImgSARFIigSYdTnbrnQZrKHNpMkRSIoEkGRCIpEUCSCIhEUiaBIBEUiKBJBkQiKRFAkgiIRFImgSARFIigSQZEIikRQJIKeu/WqqiUCWr67jAaMcgAAAABJRU5ErkJggg==",
35
+ "Jb": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHJSURBVHhe7dsxTgMxEIXhDQeggJaSipoeSk6AOCMFNSX0lIiKkhYKLhAyiS0ZKzbPG5sdj98vIbyKBNIn72ayC6tpmtabL5Zpi7T+3h20aHW8+QWd//wjt2aZiAREJCAiAREJiEhARAIiEhCRgIZC+ro4d6uyhkHyQHOghkCKYUqhzCN5kJO39+13XwmUaaQUkD9GoYa6VZJCy8VbJWBEAiISEJGAiAREJCAiAQ313O3zbDcnnX7gc5LEYfKPOEyCEQmISEBEAiISEJGAiAREJCAiAZmfuK9vX9zqd0/3l26VjxM3mHkk2THorkk1xE5KnXJo5pFCoIfXO7cqyzRSCHTIKWf23W0fEO8nBdXaQT5zSLWBJFNILYAkM0itgCQTSC2BpO6Rrm7aAkldP3cLgZ4f2wBJ3c5Jc06xoeakEKjlDvJ1h9T6Ir2vrpCWAJK6QVoKSOoCaUkgST3S0kCSaiQNQJJaJC1AkgokAQlRNAFJ6nZSTSCZsP2UHZd7LU4VUu0dlPpHG3+MfjxReU2qeYqloEo+v6lEiq9RhxaDlABJKpFa5GFKgaSh/kR5TmYfKdWOSEBEAiISEJGAiAREJKCun7v9T9P0A4HGuqkeGiXTAAAAAElFTkSuQmCC",
36
+ "Na": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIqSURBVHhe7dsxTsQwEIXhLAeggJaSipoeSk7AITkBJfTUVJS0UHCBZR8kYojk7Njx2G8c/xIi0UogfbK9Sby7G4Zhf/jpLfSDtP/6PbFod3r4B87//sl43FuoIynqSIo6kqKOpKgjKepIilTXSZ9Xl+PR/85e38ajcLHXMbf3L8PTw/V4drzNXScBSP5mKQkJI0gzimKawzBBJSGFpl9qE8h8mrFARSHJ0ZMLKgQ0nTNARS3cE5IEOjbtUhZupF28aRbu+RpkMaKYS3532xJUMhLaCtQqJLQFqNVIqHWoLEioZahsSKhVqKxIqEWo7EhIQn1c+Icy3XeTQOfvf3BL3dz9XnE/P+ofl1hnvu8mp5wcYaHc3pasSY4gr2uUORLyvpgXQUKeoYohIa9QRZGQR6jiSMgbVBUk5AmqGhLyAlUVCXmAqo6EJBRjFEiIGYoGCbFOPSqkeSxQ1EiIAYoWiWnqUY8kFij66cYARY+EakO5QEI1odwgoVpQrpBQDSh3SEhCldjXM913Sylm3y1lXy8l8303648DyiknR1iuiuy7WVdiX889ErJezJtAQpZQzSAhK6imkJAFVHNIKDdUk0goJ1SzSCgEheMYuKaRUI4R1TwSWgtVHQm3IaGvay29FpuEiq06Uuh7bdO59h5uqdg1aB7FdAtB5QDKEdVTAImkBWri07cxTTAsI2iK7nlSbJsbSax1JEUdSVFHUtSRFHUkRXT7bnwNwzcsDAXYOLVy1wAAAABJRU5ErkJggg==",
37
+ "Nb": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIgSURBVHhe7dsxTsMwFMbxlAMwwMrIxMwOIyfgkJyAEXZmJkZWGLhA6UdjyURJ+uL42d9z3l+qSIoUpJ9sK8Htruu6/eHlzfSHtP85nmi0Oz/8AePXP+uPvZkcSZAjCXIkQY4kyJEEOZIguvuk+8e37uXptj87nfT63zfX/dH/Lt4/+qPx6O6TABT/ZIkGaQiTGwoj5tSomYoCaQgSpltuqKkpd6rqSAFiuA7lhoqBlo4oqoV7CmwuyfXHgMJ75hZujaZGEI6lI6pppDVTLK5ZpFxAqEmknECoOaTcQKgpJA0g1AySFhBqAkkTCJlH+rrSBUJU+253D8c77tdn2R13DHT5qQOEzD6WaE+xkNnHkhhIcwSFzCGVGkFxppBqACEzSLWAkAmkmkCIHqk2EKJGYgBCtEgsQIgSiQkIUU83BiBEi8QChGiQ2KZYHAVSDMRYdSR2IFQViXmKxVVDsgKEqiBZAkLFkawBoaJIFoFQMSSrQKgIkmUgpI5UYl9MO9V9t6X7Ykv33Uqltu+WMsW0Pg64JrV9txioxL6YdtmRrC/SY2VFahEIZUNqFQhlQWoZCK1Gah0IrULaAhASIwEkRtkKEEoaSVsCQouRtgaEktekXEB4FJn6utbc70qWjDRco1Kb+l5bOF/yHKdVMlLOhhBMQIjy07dICmT2vwCpBRiWERSiGkkpbW4kseZIghxJkCMJciRBjiRIdd+tjbruF/dWBdg2pAhsAAAAAElFTkSuQmCC",
38
+ "Ra": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHFSURBVHhe7do7UsMwFIVhhwVQsAYq6vRQsgIWyQoooadOxRoo2EDICfagMNg+V7ItXen8MwymIc6Xa+EHu67rjqcvNdEZ6fj188Ma7a5PL+D891/122oiIREJiUhIREIiEhKRkIiikD7vbvstH6XurxnJG9BQyn6bkIYXujl8nL97adjfWCj6ssTrBP2X5UOmL0tqAkLW92OeJOuhVtIFbsx7MF3gph7XuYv9kJFp4fYKlQKEzKcAsS+Uu5T91k23mUxrUssJiUhIREIiEhKRnrsR6RRgJp0CkAmJSEhEQiISEpGQiIRERJ0nPTy991uXvT7v+63xdJ7USFFImCBmimopCmns8Ks1E1I4PS1BmSepRajouwAh0NT61PRft5YmKhoJtQKVhIRagEpGQrVDLYKEaoZaDAnVCrUoEqoRanEkFELdP/qHWvW5Wwj09vIL563Vn7uFh1w4YUtVxf2kcIK8rlGrIyHvi/kmSMgz1GZIyCvUpkjII9TmSMgbVBYkNAaF7dLgsiEhLxOVFQl5gMqOhEKoEsuOVOIa9LciJqn09I+lM21ygVtDQiISEpGQiIREJCQiIREJiUhIRKs+d6ujrvsGdDW0Lec+UFYAAAAASUVORK5CYII=",
39
+ "Rb": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAG9SURBVHhe7dqxUcNAEIVhmQIIqIGImBxCKqBIKiCEnJiIGghowPgZ3czhsa13K521e/f+GQaRIPnzSj4hNsMwbHdf6kx7pO3P3w812lzvdhD891+N2+pMQiISEpGQiIREJCQiIRHNQvq+ux23YmQ9XjNSNKCU5bhNSGlHN59f++9RSsdbClV8WxJ1go7FvMnFtyUtASH29ZgniT3VPN7glrwG0w2u9bz2UumbjEwX7qhQFiBkXgKU7shLluPWH90mMl2TekxIREIiEhKRkIj03I1IS4CJtAQgExKRkIiERCQkIiERCYmom3XS4/PHuPW/t5f7cet4WieRdYOEiZmamlN1NUmnTrmpukHKgUonqgukOUCo+U+32UCtf7rNBUo1i7QUEGoSaUkg1BzS0kCoKaQaQKgZpFpAqAmkmkAoPNLDU10gFPq5Ww70/loHCIVdcdc+xVJhV9w5UM0JSoVDutQE5YVCWgMIhUFaCwiFQFoTCLlHWhsIuUICSI7iAQi5nSQvQMglkicg5Pqa5AEIuUY6vEatlWskL+kfSydq/pHSUgmJSEhEQiISEpGQiIREJCQiIRGFfu52mYbhFyeGtC1UL+rcAAAAAElFTkSuQmCC",
40
+ "T": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGRSURBVHhe7dkxTsNAEEBRhwNQQEtJRc0BOAWH5BQcgDoVJS0UXCDETqaxYu+fFUh49z8pii0kR/tjI0+8G4bhcHxpxRTp8H3a+Qu76+MHbPz4V+dtrTASYCTASICRACMB3UT6erg/b+V1ESkC1YZqPtI8TE2opiNFkJv9+/QesqGajbQUKPYzobqZ3ZailTi7QUYCjAQYCTASYCTASICRACMBRgK6ee72eXcaS24/cmPJyNmtwNkNMhJgJMBIgJEAIwFVkZ6e385b20bXkY7USqBA1pOKFAd8fXmc3rcu1lEKhSO1FiiQUGgsIadkK+YnAR5LWjt7liytMzXg1lxy/33ALa0pPeDGgVq5/OiXnr4FyJxFW0DW4+9JBenLrVdGAowEGAkwEmAkwOdugPdJBd4nQUYCjAQYCTASYCTASICRACMBzUYa77DjLntu7W+XNBspxo95jNjPjCfNz26XzphUoB5mt3mQ7IA76uIfd4SpCTTq5qeSWl1cbr/BSICRACMBRgKMBBgJ6Oa5W71h+AGr6qDhlFnaCQAAAABJRU5ErkJggg==",
41
+ "Ua": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHySURBVHhe7dnLUcMwFIVhhwKohj0sqYAiqYAl7KmGBpIcJxo0mTg6V7562eebYXA2RPNbko19mKbpeP6RB+ZIx7/LhxIOz+cvGPzvP12P5QFFIigSQZEIikRQJIIiERSJoEgERSIoEkGRCIpEUCSCIhEUiaBIBEUiKBJBkQiKRFAkgt67EfTeLUHv3UiKRFAkgiIRsiK9ffxej/bBHGlvgcAUKQT6/nyZf+8FfZ+0pRlkOcn0fdIel1jMPJOsS21Xd9whjjbuhL2GyvoHF5HYZdd6uS2dUMv4zfdJYN2XRkdHwhkZcZnhhK49qeaZNGKotWM2X91gpFDxWHNnVNbVDUYI5REIsq9uQerLW13d3AJ5XN16nFFegYKsSNBrKO9AkB0JegtVIhDMe9LlMN/r+//gfr78BmdRcgxZG/c9S2exxsYdB/KcQZC9cd/TaumVDBS4RYLaoUrtQbdcI0GtULUCgXskiAcdLwcvcaAaF4oikaDUjKo5g4JikcA7VItAUDQSxMthTahWgaB4JFg7o1oGgiqRIDdU60BQLRJYQ/UQCKpGgqVQOL79HLQMBNUjQWpG9RQImkSCVCjoIRC4PQVYknoKcBvIGqbGU4ZmMwlxlmZQb5pFGknz5bbWppfbSBSJoEgERSIoEsHlvdu2TdMJn8jmJ8fDhbIAAAAASUVORK5CYII=",
42
+ "Ub": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHySURBVHhe7drBUcMwFIRhhwKohjscqYAiUwFHuFMNDYSsEw2aTGzvk/QkOdpvhsE5EIk/kmNiDtM0nc5fsmKOdPq9PPBweD4PsPPnf7oeywpFIigSQZEIikRQJIIiERSJoEgERSIoEkGRCIpEUCSCIhEUiaBIBEUiKBJBkQiKRFAkgu67EXTfbYPuu5EUiaBIBEUiDBXp7ePnemQz3EpKCTVUpK/jy/zdGsp8nZS6ZHsUoq0Z/jqJfcGHvOIOcbSSFlgCBUNeAlgCgWm7Le3htUF72m6YvznQaNvNGijIioRBUwduCStqaVfckxXJMlAvUuacFClePXsKFc/VsgOSV9LeQqUGguyLya3Be3h3ywpU4t2t9xWVEyjIjgS9hioRCIpEgt5ClQoE8znpcljG6/v/5L4/8yaXqvQcXD4FiF9FTLLmibvkCgK3P0viycWvqrfSgQKXSFD7HOUVCNwiQa1QnoHANRLEJ06PUPF29ggE7pHAa0V5r6CgSiQoHapWIKgWCUqFin+2xrVY1UiQG6rmCgqqR4LUUC0CQZNIYA3VKhA0iwRLoXB8+zioHQiaRoKtFdU6EDSPBEu/fA+BoMv/BbhdUWuBUp7fwu1TgNIQ7d5WrGUXkVrrcrtZaLt1QpEIikRQJIIiEYrfd3s80/QHRtfoJ68+66IAAAAASUVORK5CYII=",
43
+ "V": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIWSURBVHhe7doxTgMxEIXhDQeggJaSipoeSk7AITlBSuipqShpoeACIS/sKkPErsbrGXvGnl+KtDQgPo29FmYzDMNu/4kWOiDtvn+/0Ghzvv8Bzr//2fgcLRRIjAKJUSAxCiRGgcQokBiZOCfdP76OT397frodn+YzdU76urn+99NDJpcbJogzRaVajXTx9n74aDS3/Gq1GkljqdHpsQSVjESnpxco9tttApmQKNDSslv79qFAS/tTibdb1hGAA5XzS3CgSiBlvd16WXrZR4AeoLKRUOtQIkioZSgxJNQqlCgSahFKHAlRqM8rXai7B30o1Xs3CnT5cYSTigK9bI9w0qn/PYkuOTphUtElRydMquzDJCc6QRp7FJ0grT1KHQl538yLICHPUMWQkFeookjII1RxJOQNqgoS8gRVDQl5gaqKhGpB4ZkLVx0JWZ8oE0jIMpQZJFQSKiVTSEgLKmUPOs0cEtKeqNRM/OvNXBSIwtHM37tpZ2WiTCMhC1DmkVBtKBdIqCaUGyRUC8oVEqoB5Q4JUSiNe73TVO/dtNO+15syfZjkRJccnTCpzB8mOWnf6yH3SEh7M28CCWlCNYOEtKCaQkIaUM0hIWmoJpGQJFSzSEgKqmkkNAeFZy5c80god6K6QEI5UN0gIQqVUjdIKXvQaV1N0trc/6mk+3s3KwUSo0BiFEiMAolRIDEKJEau793KNAw/I0AwLpLA1aEAAAAASUVORK5CYII=",
44
+ "Y": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIvSURBVHhe7do9TsNAEIZhhwNQQEtJRU0PJSdAnJGCmhJ6SkRFSQsFFwj5gldMItuZ3ZnFM+t5pYilCdKj8fqPVdd1680nmmiLtP7+/aVGq+PNH3D+/Uf9OpookBgFEqNAYhRIjAKJUSAxYl0nfV2c96vdTt7e+9V43OuY69uXfrXb0/1lvxourpOMVISECeJMUU6YmENTM1dFSGOHn7SxQ27uspDo9GhDUSBrE5U9STWgLAOh4qcAFGhqfzp09pECmT67aUyU9QlKFSMhCZQXICRCQiVQnoCQGAnlQHkDQipIiAPlEQipIaEpKK9ASBUJDUF5BkLqSIhCXd34BkJV37tRoOdHn0Co2ns3eog9vN5tf9IJ08rt8yQKRCeIex1lLXWkoU2ac3lgOVWkqbOYZyg1JM5p3iuUChIHKOURSoyUA5TyBpWFBBCKUgKU8gRVPEkSoJQXqCIkDaCUByjRniQFSlmHEiHt71GSLEOJkLSzCmXyH0spEIUbyu0NrjRrE2USCVmCMouErECZRkIWoMwjobmhXCChOaHcIKG5oFwhoTmg3CEhCvV5Vh+q6nu32lGg048/OO1M3pbkRA85OmFamb0tyYlOUK09yj0Sqr2ZN4GEakI1g4RqQTWFhGpANYeEtKGaREKaUM0ioTEorHPgmkZCGhPVPBKSQi0CCVGo3BaBlLsH7beYSZLk/inAYl9OWiuQGAUSo0BiFEiMAolRIDEKJEau37v9T133A/cUFsjTSnS4AAAAAElFTkSuQmCC",
45
+ "Z": "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcwvcAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHcSURBVHhe7drNTcNAEMVxhwKohjscqYAiqYAj3KmGBkJe4pUW5MQT79d74/lLSCYHY/0ytldaDtM0HU8/0Y3OSMefyy8tOjye/oD4+R/m4+hGgWQokAwFkqFAMhRIhgLJEM066eXtez762+f703y0XKyTSKJBwsSsTc2oqCbp2i03OhqkHIhtoiiQmIHQ8LdbKZD7txv7BKWGIakAoSFISkCoO5IaEOqKpAiEuiGpAqEuSMpAqDnS86s2EGq675YDfX1oAqFmK+5et5jsijsHUp6gVHUk9Yf0UlWRPAKhakhegVAVJM9AqBjJOxC6CwkgOcoegNBd66SEAhArUI91jOX8+fXmrX25m9dJe5mgVNEzSQkI17r1eouQMFHXxpixrdda/HZTqeQRQfMPE1uznH8JKH22Bobzu0cqmSCE87u+3UqBUm6RagEhl0g1gZA7pNpAyBVSCyDkBqkVEHKB1BIIySP12Ndruu/Wul77erIr7ta3WEp2xZ0D9djXk0PqNUF5UkgjgJAM0iggJIE0EgjRI40GQlRIAMlRGIAQ7SSxACFKJCYgRP1MYgBC1Ej/n1GjokZiaRf7biW531KqVSAZCiRDgWQokAwFkqFAMhRIhgLJkPS+W5+m6RfQoucBXsykVgAAAABJRU5ErkJggg==",
46
+ }
47
+
48
+
49
+ # Class
50
+ # ---------------------------------------------------------------------------
51
+ class RubikNotation:
52
+ """
53
+ Rubik Notation
54
+
55
+ Parameters
56
+ ----------
57
+ notation : str
58
+ Notation (abbreviated name)
59
+
60
+ is_counter_clockwise : bool | None, optional
61
+ Is counter clockwise, by default ``False``
62
+
63
+ note : str | None, optional
64
+ Note, by default ``None``
65
+ """
66
+
67
+ _BASIC_NOTATION: ClassVar[dict[str, str]] = {
68
+ "L": "Left",
69
+ "R": "Right",
70
+ "U": "Up",
71
+ "D": "Down",
72
+ "F": "Front",
73
+ "B": "Back",
74
+ "M": "Middle",
75
+ "E": "Equator",
76
+ "S": "Side",
77
+ "X": "x axis",
78
+ "Y": "y axis",
79
+ "Z": "z axis",
80
+ }
81
+ _COUNTER_CLOCKWISE_SYMBOL: ClassVar[str] = "'"
82
+
83
+ def __init__(
84
+ self,
85
+ notation: str,
86
+ is_counter_clockwise: bool = False,
87
+ /,
88
+ *,
89
+ note: str | None = None,
90
+ ) -> None:
91
+ """
92
+ Rubik Notation
93
+
94
+ Parameters
95
+ ----------
96
+ notation : str
97
+ Notation (abbreviated name)
98
+
99
+ is_counter_clockwise : bool | None, optional
100
+ Is counter clockwise, by default ``False``
101
+
102
+ note : str | None, optional
103
+ Note, by default ``None``
104
+ """
105
+
106
+ self.notation = notation.upper().strip()
107
+ self.is_counter_clockwise = is_counter_clockwise
108
+
109
+ self.direction = (
110
+ "Counter Clockwise" if self.is_counter_clockwise else "Clockwise"
111
+ )
112
+
113
+ long_notation_name = self._BASIC_NOTATION.get(self.notation[0], self.notation)
114
+ self.full_notation = long_notation_name + (
115
+ f" ({self.direction})" if is_counter_clockwise else ""
116
+ )
117
+
118
+ self.note = None if note is None else note
119
+
120
+ def __str__(self) -> str:
121
+ # return f"{self.__class__.__name__}({self.notation})"
122
+ notation = (
123
+ f"{self.notation}{self._COUNTER_CLOCKWISE_SYMBOL}"
124
+ if self.is_counter_clockwise
125
+ else self.notation
126
+ )
127
+ return notation
128
+
129
+ def __repr__(self) -> str:
130
+ return self.__str__()
131
+
132
+ @classmethod
133
+ def from_str(cls, string: str) -> Self:
134
+ if cls._COUNTER_CLOCKWISE_SYMBOL in string:
135
+ return cls(string[:-1].upper(), True)
136
+ return cls(string)
137
+
138
+
139
+ @final
140
+ class RubikNotations:
141
+ """
142
+ Constant Rubik Notations
143
+ """
144
+
145
+ U = RubikNotation("U")
146
+ D = RubikNotation("D")
147
+ L = RubikNotation("L")
148
+ R = RubikNotation("R")
149
+ F = RubikNotation("F")
150
+ B = RubikNotation("B")
151
+
152
+ M = RubikNotation("M", note=f"Rotate according to {L} face")
153
+ E = RubikNotation("E", note=f"Rotate according to {D} face")
154
+ S = RubikNotation("S", note=f"Rotate according to {F} face")
155
+
156
+ U_REV = RubikNotation("U", True)
157
+ D_REV = RubikNotation("D", True)
158
+ L_REV = RubikNotation("L", True)
159
+ R_REV = RubikNotation("R", True)
160
+ F_REV = RubikNotation("F", True)
161
+ B_REV = RubikNotation("B", True)
162
+
163
+ M_REV = RubikNotation("M", True, note=f"Rotate according to {L} face")
164
+ E_REV = RubikNotation("E", True, note=f"Rotate according to {D} face")
165
+ S_REV = RubikNotation("S", True, note=f"Rotate according to {F} face")
166
+
167
+ X = RubikNotation("x", note=f"Rotate according to {R} face")
168
+ Y = RubikNotation("y", note=f"Rotate according to {U} face")
169
+ Z = RubikNotation("z", note=f"Rotate according to {F} face")
170
+
171
+ X_REV = RubikNotation("x", True, note=f"Rotate according to {R} face")
172
+ Y_REV = RubikNotation("y", True, note=f"Rotate according to {U} face")
173
+ Z_REV = RubikNotation("z", True, note=f"Rotate according to {F} face")
174
+
175
+
176
+ class RubikAlgorithm:
177
+ """
178
+ Rubik Algorithm
179
+
180
+ Parameters
181
+ ----------
182
+ case : str
183
+ Case name
184
+
185
+ solution : str | list[str] | list[RubikNotation]
186
+ Solution/Algorithm
187
+
188
+ other_solutions : None | list[list[str]] | list[list[RubikNotation]] | list[Self], optional
189
+ Other Solution/Algorithm, by default ``None``
190
+
191
+ note : str | None, optional
192
+ Note, by default ``None``
193
+
194
+ picture : str | None, optional
195
+ Picture of case (in base64 format), by default ``None``
196
+ """
197
+
198
+ def __init__(
199
+ self,
200
+ case: str,
201
+ solution: str | list[str] | list[RubikNotation],
202
+ other_solutions: (
203
+ None | list[list[str]] | list[list[RubikNotation]] | list[Self]
204
+ ) = None,
205
+ note: str | None = None,
206
+ picture: str | None = None,
207
+ ) -> None:
208
+ """
209
+ Rubik Algorithm
210
+
211
+ Parameters
212
+ ----------
213
+ case : str
214
+ Case name
215
+
216
+ solution : str | list[str] | list[RubikNotation]
217
+ Solution/Algorithm
218
+
219
+ other_solutions : None | list[list[str]] | list[list[RubikNotation]] | list[Self], optional
220
+ Other Solution/Algorithm, by default ``None``
221
+
222
+ note : str | None, optional
223
+ Note, by default ``None``
224
+
225
+ picture : str | None, optional
226
+ Picture of case (in base64 format), by default ``None``
227
+ """
228
+
229
+ self.case = case.strip()
230
+ self.solution = self._convert_solution(solution)
231
+
232
+ self.other_solutions = other_solutions
233
+ self.note = note
234
+ self.picture = picture
235
+
236
+ def __str__(self) -> str:
237
+ # return f"{self.__class__.__name__}({self.case})"
238
+ # return f"{self.solution}"
239
+ return f"{self.__class__.__name__}({self.case} - {self.solution})"
240
+
241
+ def __repr__(self) -> str:
242
+ return self.__str__()
243
+
244
+ def _convert_solution(
245
+ self, solution: str | list[str] | list[RubikNotation], /
246
+ ) -> list[RubikNotation]:
247
+ if isinstance(solution, str):
248
+ sols: list[str] = solution.replace("(", "").replace(")", "").split()
249
+ return [RubikNotation.from_str(x) for x in sols]
250
+
251
+ if isinstance(solution, list) and len(solution) > 0:
252
+ if isinstance(solution[0], RubikNotation):
253
+ return cast(list[RubikNotation], solution)
254
+ else:
255
+ return [RubikNotation.from_str(x) for x in solution] # type: ignore[arg-type]
256
+
257
+ raise ValueError("Wrong value type")
258
+
259
+
260
+ class Cross(RubikAlgorithm):
261
+ """Cross Algorithm"""
262
+
263
+ pass
264
+
265
+
266
+ class F2L(RubikAlgorithm):
267
+ """F2L Algorithm"""
268
+
269
+ pass
270
+
271
+
272
+ class OLL(RubikAlgorithm):
273
+ """OLL Algorithm"""
274
+
275
+ pass
276
+
277
+
278
+ class PLL(RubikAlgorithm):
279
+ """PLL Algorithm"""
280
+
281
+ pass
282
+
283
+
284
+ class PLLs:
285
+ """
286
+ Collection of PLLs
287
+ """
288
+
289
+ Aa = PLL(
290
+ "Aa",
291
+ "x (R' U R') D2 (R U' R') D2 R2",
292
+ other_solutions=[
293
+ PLL(
294
+ "Aa",
295
+ "(r U r' U') (r' F r2 U') r' U' (r U r' F')",
296
+ note="Headlight at Left (solve like T perm but is `r` instead of `R`)",
297
+ )
298
+ ],
299
+ note="Headlight at Back",
300
+ picture=PLL_PIC["Aa"],
301
+ )
302
+
303
+ Ab = PLL(
304
+ "Ab",
305
+ "x (R2 D2) (R U R') D2 (R U' R)",
306
+ note="Headlight at Right",
307
+ picture=PLL_PIC["Ab"],
308
+ )
309
+
310
+ E = PLL(
311
+ "E", "x' (R U' R' D) (R U R' D') (R U R' D) (R U' R' D')", picture=PLL_PIC["E"]
312
+ )
313
+
314
+ Ua = PLL(
315
+ "Ua",
316
+ "(R U' R U) R U (R U' R' U') R2",
317
+ note="Finished face at Back",
318
+ picture=PLL_PIC["Ua"],
319
+ )
320
+
321
+ Ub = PLL(
322
+ "Ub",
323
+ "R2 U (R U R' U') R' U' (R' U R')",
324
+ note="Finished face at Back",
325
+ picture=PLL_PIC["Ub"],
326
+ )
327
+
328
+ Z = PLL(
329
+ "Z",
330
+ "M2 U' M2 U' M' U2 M2 U2 M' U2",
331
+ other_solutions=[
332
+ PLL(
333
+ "Z",
334
+ "M' U' M2 U' M2 U' M' U2 M2 U",
335
+ note="Switch edge at Right and Front",
336
+ )
337
+ ],
338
+ note="Switch edge at Left and Front",
339
+ picture=PLL_PIC["Z"],
340
+ )
341
+
342
+ H = PLL("H", "(M2 U' M2) U2 (M2 U' M2)", picture=PLL_PIC["H"])
343
+
344
+ T = PLL(
345
+ "T",
346
+ "(R U R' U') (R' F R2 U') R' U' (R U R' F')",
347
+ note="Headlight at Left",
348
+ picture=PLL_PIC["T"],
349
+ )
350
+
351
+ F = PLL(
352
+ "F",
353
+ "R' U' F' (R U R' U') (R' F R2 U') (R' U' R U) (R' U R)",
354
+ note="Finished face at Left",
355
+ picture=PLL_PIC["F"],
356
+ )
357
+
358
+ Ja = PLL(
359
+ "Ja",
360
+ "(R' U L' U2) (R U' R' U2 R) L U'",
361
+ note="Finished face at Front",
362
+ picture=PLL_PIC["Ja"],
363
+ )
364
+
365
+ Jb = PLL(
366
+ "Jb",
367
+ "(R U R' F') (R U R' U') R' F R2 U' R' U'",
368
+ note="Finished face at Left",
369
+ picture=PLL_PIC["Jb"],
370
+ )
371
+
372
+ Ra = PLL(
373
+ "Ra",
374
+ "(R U R' F') (R U2' R' U2') (R' F R U) (R U2 R' U')",
375
+ note="Headlight at Left",
376
+ picture=PLL_PIC["Ra"],
377
+ )
378
+
379
+ Rb = PLL(
380
+ "Rb",
381
+ "(R' U2 R U2') R' F (R U R' U') R' F' R2 U'",
382
+ note="Headlight at Front",
383
+ picture=PLL_PIC["Rb"],
384
+ )
385
+
386
+ V = PLL(
387
+ "V",
388
+ "(R' U R' d') (R' F' R2 U') (R' U R' F) R F",
389
+ note="Block at Left Front",
390
+ picture=PLL_PIC["V"],
391
+ )
392
+
393
+ Y = PLL(
394
+ "Y",
395
+ "F (R U' R' U') (R U R' F') (R U R' U') (R' F R F')",
396
+ note="2 pieces have same color at Front and Right",
397
+ picture=PLL_PIC["Y"],
398
+ )
399
+
400
+ Na = PLL(
401
+ "Na",
402
+ "(R U R' U) (R U R' F') (R U R' U') (R' F R2 U') R' U2 (R U' R')",
403
+ note="Edges need to swap at Left and Right",
404
+ picture=PLL_PIC["Na"],
405
+ )
406
+
407
+ Nb = PLL(
408
+ "Nb",
409
+ "z R' U' R D' R2 U R' D U' R D' R2 U R' D z'",
410
+ note="Edges need to swap at Left and Right",
411
+ picture=PLL_PIC["Nb"],
412
+ )
413
+
414
+ Ga = PLL("Ga", "R2 u R' U R' U' R u' R2 y' R' U R", picture=PLL_PIC["Ga"])
415
+
416
+ Gb = PLL(
417
+ "Gb",
418
+ "(R' U' R) y R2 u (R' U R U' R) u' R2",
419
+ other_solutions=[
420
+ PLL("Gb", "R' U' R U D' R2 U R' U R U' R U' R2 D", note="Headlight at Left")
421
+ ],
422
+ note="Headlight at Left",
423
+ picture=PLL_PIC["Gb"],
424
+ )
425
+
426
+ Gc = PLL(
427
+ "Gc",
428
+ "R2 F2 R U2 R U2 R' F (R U R' U') R' F R2",
429
+ note="Headlight at Right",
430
+ picture=PLL_PIC["Gc"],
431
+ )
432
+
433
+ Gd = PLL(
434
+ "Gd",
435
+ "R U R' U' D R2 U' R U' R' U R' U R2 D'",
436
+ note="Headlight at Left",
437
+ picture=PLL_PIC["Gd"],
438
+ )
439
+
440
+
441
+ class Rubik3x3:
442
+ PLL = PLLs()
absfuyu/fun/tarot.py CHANGED
@@ -4,8 +4,8 @@ Absfuyu: Tarot
4
4
  Tarot stuff
5
5
 
6
6
 
7
- Version: 1.0.4
8
- Date updated: 14/11/2024 (dd/mm/yyyy)
7
+ Version: 6.1.1
8
+ Date updated: 30/12/2025 (dd/mm/yyyy)
9
9
 
10
10
  Usage:
11
11
  ------
absfuyu/game/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Game
3
3
  -------------
4
4
  Contain some game that can be played on terminal
5
5
 
6
- Version: 1.1.0
7
- Date updated: 14/04/2024 (dd/mm/yyyy)
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/game/game_stat.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Game: Game Stat
3
3
 
4
- Version: 1.0.0
5
- Date updated: 14/04/2024 (dd/mm/yyyy)
4
+ Version: 6.1.1
5
+ Date updated: 30/12/2025 (dd/mm/yyyy)
6
6
  """
7
7
 
8
8
  # Module level
@@ -0,0 +1,78 @@
1
+ """
2
+ Game: Schulte
3
+ -------------
4
+
5
+ Version: 6.1.1
6
+ Date updated: 30/12/2025 (dd/mm/yyyy)
7
+ """
8
+
9
+ # Module level
10
+ # ---------------------------------------------------------------------------
11
+ __all__ = ["SchulteTable"]
12
+
13
+
14
+ # Library
15
+ # ---------------------------------------------------------------------------
16
+ from absfuyu.core.baseclass import BaseClass
17
+ from absfuyu.dxt import ListExt
18
+ from absfuyu.util.text_table import BoxStyle, get_box_drawing_character
19
+
20
+
21
+ # Function
22
+ # ---------------------------------------------------------------------------
23
+ def draw_grid(data: list[list[int]], style: BoxStyle = "normal"):
24
+ chars = get_box_drawing_character(style=style)
25
+ rows = len(data)
26
+ cols = len(data[0])
27
+
28
+ # find max width for padding
29
+ cell_width = max(len(str(x)) for row in data for x in row) + 2
30
+
31
+ def horizontal_border(left: str, middle: str, right: str) -> str:
32
+ return left + (chars.HORIZONTAL * cell_width + middle) * (cols - 1) + chars.HORIZONTAL * cell_width + right
33
+
34
+ # top border
35
+ print(horizontal_border(chars.UPPER_LEFT_CORNER, chars.HORIZONTAL_DOWN, chars.UPPER_RIGHT_CORNER))
36
+
37
+ for i, row in enumerate(data):
38
+ # content line
39
+ line = chars.VERTICAL + "".join(f"{str(val):^{cell_width}}" + chars.VERTICAL for val in row)
40
+ print(line)
41
+
42
+ # middle or bottom border
43
+ if i < rows - 1:
44
+ print(horizontal_border(chars.VERTICAL_RIGHT, chars.CROSS, chars.VERTICAL_LEFT))
45
+ else:
46
+ print(horizontal_border(chars.LOWER_LEFT_CORNER, chars.HORIZONTAL_UP, chars.LOWER_RIGHT_CORNER))
47
+
48
+
49
+ # Class
50
+ # ---------------------------------------------------------------------------
51
+ class SchulteTable(BaseClass):
52
+ """
53
+ A Schulte Table is a cognitive training tool consisting of a grid filled
54
+ with randomly placed numbers. The task is to find and select all the numbers
55
+ in ascending order as quickly as possible. This exercise helps improve
56
+ visual attention, focus, processing speed, mental flexibility,
57
+ and peripheral vision.
58
+ """
59
+
60
+ def __init__(self, size: int = 5) -> None:
61
+ self.size = max(size, 1)
62
+
63
+ def make_table(self) -> None:
64
+ data = ListExt(range(1, self.size**2 + 1)).shuffle().split_chunk(self.size)
65
+ draw_grid(data)
66
+
67
+ # def play(self):
68
+ # """GUI"""
69
+ # from absfuyu.util.gui import CustomTkinterApp
70
+
71
+ # class Schulte(CustomTkinterApp):
72
+ # def __init__(self, title: str | None = None, size: tuple[int, int] | None = None) -> None:
73
+ # super().__init__(title=title, size=size)
74
+
75
+
76
+ if __name__ == "__main__":
77
+ t = SchulteTable(5)
78
+ print(t)
absfuyu/game/sudoku.py CHANGED
@@ -3,8 +3,8 @@ Game: Sudoku
3
3
  ------------
4
4
  Sudoku 9x9 Solver
5
5
 
6
- Version: 1.0.3
7
- Date updated: 15/11/2024 (dd/mm/yyyy)
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
8
 
9
9
  Credit:
10
10
  -------
absfuyu/game/tictactoe.py CHANGED
@@ -2,11 +2,10 @@
2
2
  Game: Tic Tac Toe
3
3
  -----------------
4
4
 
5
- Version: 2.0.4
6
- Date updated: 15/11/2024 (dd/mm/yyyy)
5
+ Version: 6.1.1
6
+ Date updated: 30/12/2025 (dd/mm/yyyy)
7
7
  """
8
8
 
9
-
10
9
  # Module level
11
10
  # ---------------------------------------------------------------------------
12
11
  __all__ = ["TicTacToe", "GameMode"]
absfuyu/game/wordle.py CHANGED
@@ -2,8 +2,8 @@
2
2
  Game: Wordle Solver
3
3
  -------------------
4
4
 
5
- Version: 1.1.4
6
- Date updated: 14/04/2024 (dd/mm/yyyy)
5
+ Version: 6.1.1
6
+ Date updated: 30/12/2025 (dd/mm/yyyy)
7
7
 
8
8
  Usage:
9
9
  ------
@@ -27,8 +27,6 @@ import string
27
27
  from collections import Counter
28
28
  from itertools import chain
29
29
 
30
- import requests
31
-
32
30
  # Class
33
31
  # ---------------------------------------------------------------------------
34
32
  OFFLINE_WORDS = """\
@@ -123,6 +121,8 @@ class Wordle:
123
121
  Try to fetch words list from online source
124
122
  """
125
123
  try:
124
+ import requests
125
+
126
126
  dict_link = "https://raw.githubusercontent.com/dwyl/english-words/master/words_dictionary.json"
127
127
  res = requests.get(dict_link)
128
128
  word_list: list[str] = list(res.json().keys())
@@ -134,6 +134,8 @@ class Wordle:
134
134
  and set(word) < self._ALLOWABLE_CHARACTERS
135
135
  }
136
136
  )
137
+ except ImportError:
138
+ raise ImportError("Please install requests package")
137
139
  except Exception:
138
140
  pass
139
141
 
@@ -3,8 +3,8 @@ Absfuyu: General
3
3
  ----------------
4
4
  Collection of useful classes
5
5
 
6
- Version: 1.1.4
7
- Date updated: 15/03/2024 (dd/mm/yyyy)
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
@@ -15,9 +15,9 @@ Features:
15
15
 
16
16
  # Module level
17
17
  # ---------------------------------------------------------------------------
18
- __all__ = ["content", "generator", "human"]
18
+ __all__ = ["content", "shape", "human"]
19
19
 
20
20
 
21
21
  # Libary
22
22
  # ---------------------------------------------------------------------------
23
- from absfuyu.general import content, generator, human
23
+ from absfuyu.general import content, human, shape