thailint 0.1.5__py3-none-any.whl → 0.5.0__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.
- src/__init__.py +7 -2
- src/analyzers/__init__.py +23 -0
- src/analyzers/typescript_base.py +148 -0
- src/api.py +1 -1
- src/cli.py +1111 -144
- src/config.py +12 -33
- src/core/base.py +102 -5
- src/core/cli_utils.py +206 -0
- src/core/config_parser.py +126 -0
- src/core/linter_utils.py +168 -0
- src/core/registry.py +17 -92
- src/core/rule_discovery.py +132 -0
- src/core/violation_builder.py +122 -0
- src/linter_config/ignore.py +112 -40
- src/linter_config/loader.py +3 -13
- src/linters/dry/__init__.py +23 -0
- src/linters/dry/base_token_analyzer.py +76 -0
- src/linters/dry/block_filter.py +265 -0
- src/linters/dry/block_grouper.py +59 -0
- src/linters/dry/cache.py +172 -0
- src/linters/dry/cache_query.py +61 -0
- src/linters/dry/config.py +134 -0
- src/linters/dry/config_loader.py +44 -0
- src/linters/dry/deduplicator.py +120 -0
- src/linters/dry/duplicate_storage.py +63 -0
- src/linters/dry/file_analyzer.py +90 -0
- src/linters/dry/inline_ignore.py +140 -0
- src/linters/dry/linter.py +163 -0
- src/linters/dry/python_analyzer.py +668 -0
- src/linters/dry/storage_initializer.py +42 -0
- src/linters/dry/token_hasher.py +169 -0
- src/linters/dry/typescript_analyzer.py +592 -0
- src/linters/dry/violation_builder.py +74 -0
- src/linters/dry/violation_filter.py +94 -0
- src/linters/dry/violation_generator.py +174 -0
- src/linters/file_header/__init__.py +24 -0
- src/linters/file_header/atemporal_detector.py +87 -0
- src/linters/file_header/config.py +66 -0
- src/linters/file_header/field_validator.py +69 -0
- src/linters/file_header/linter.py +313 -0
- src/linters/file_header/python_parser.py +86 -0
- src/linters/file_header/violation_builder.py +78 -0
- src/linters/file_placement/config_loader.py +86 -0
- src/linters/file_placement/directory_matcher.py +80 -0
- src/linters/file_placement/linter.py +262 -471
- src/linters/file_placement/path_resolver.py +61 -0
- src/linters/file_placement/pattern_matcher.py +55 -0
- src/linters/file_placement/pattern_validator.py +106 -0
- src/linters/file_placement/rule_checker.py +229 -0
- src/linters/file_placement/violation_factory.py +177 -0
- src/linters/magic_numbers/__init__.py +48 -0
- src/linters/magic_numbers/config.py +82 -0
- src/linters/magic_numbers/context_analyzer.py +247 -0
- src/linters/magic_numbers/linter.py +516 -0
- src/linters/magic_numbers/python_analyzer.py +76 -0
- src/linters/magic_numbers/typescript_analyzer.py +218 -0
- src/linters/magic_numbers/violation_builder.py +98 -0
- src/linters/nesting/__init__.py +6 -2
- src/linters/nesting/config.py +17 -4
- src/linters/nesting/linter.py +81 -168
- src/linters/nesting/typescript_analyzer.py +39 -102
- src/linters/nesting/typescript_function_extractor.py +130 -0
- src/linters/nesting/violation_builder.py +139 -0
- src/linters/print_statements/__init__.py +53 -0
- src/linters/print_statements/config.py +83 -0
- src/linters/print_statements/linter.py +430 -0
- src/linters/print_statements/python_analyzer.py +155 -0
- src/linters/print_statements/typescript_analyzer.py +135 -0
- src/linters/print_statements/violation_builder.py +98 -0
- src/linters/srp/__init__.py +99 -0
- src/linters/srp/class_analyzer.py +113 -0
- src/linters/srp/config.py +82 -0
- src/linters/srp/heuristics.py +89 -0
- src/linters/srp/linter.py +234 -0
- src/linters/srp/metrics_evaluator.py +47 -0
- src/linters/srp/python_analyzer.py +72 -0
- src/linters/srp/typescript_analyzer.py +75 -0
- src/linters/srp/typescript_metrics_calculator.py +90 -0
- src/linters/srp/violation_builder.py +117 -0
- src/orchestrator/core.py +54 -9
- src/templates/thailint_config_template.yaml +158 -0
- src/utils/__init__.py +4 -0
- src/utils/project_root.py +203 -0
- thailint-0.5.0.dist-info/METADATA +1286 -0
- thailint-0.5.0.dist-info/RECORD +96 -0
- {thailint-0.1.5.dist-info → thailint-0.5.0.dist-info}/WHEEL +1 -1
- src/.ai/layout.yaml +0 -48
- thailint-0.1.5.dist-info/METADATA +0 -629
- thailint-0.1.5.dist-info/RECORD +0 -28
- {thailint-0.1.5.dist-info → thailint-0.5.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.1.5.dist-info → thailint-0.5.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
src/__init__.py,sha256=f601zncODr2twrUHqTLS5wyOdZqZi9tMjAe2INhRKqU,2175
|
|
2
|
+
src/analyzers/__init__.py,sha256=fFloZtjkBGwYbAhKTxS3Qy3yDr2_3i3WSfKTw1mAioo,972
|
|
3
|
+
src/analyzers/typescript_base.py,sha256=4I7fAcMOAY9vY1AXh52QpohgFmguBECwOkvBRP4zCS4,5054
|
|
4
|
+
src/api.py,sha256=pJ5l3qxccKBEY-BkANwzTgLAl1ZFq7OP6hx6LSxbhDw,4664
|
|
5
|
+
src/cli.py,sha256=t72ZeeBpXJTIR1Gx_nIak-ZlUJhfXhEORXNNcjdp2-I,51820
|
|
6
|
+
src/config.py,sha256=2ebAjIpAhw4bHbOxViEA5nCjfBlDEIrMR59DBrzcYzM,12460
|
|
7
|
+
src/core/__init__.py,sha256=5FtsDvhMt4SNRx3pbcGURrxn135XRbeRrjSUxiXwkNc,381
|
|
8
|
+
src/core/base.py,sha256=Eklcagi2ktfY4Kytl_ObXov2U49N9OGDpw4cu4PUzGY,7824
|
|
9
|
+
src/core/cli_utils.py,sha256=rYOJz4mnr8RLP-nJdHOy-GJyxGNqkWtK3_rvKriHXj4,6083
|
|
10
|
+
src/core/config_parser.py,sha256=zAY4bDptNlVID0a4JDXN0YlUKXLM92cFqTAwhp_8uGc,4183
|
|
11
|
+
src/core/linter_utils.py,sha256=4jmC2YfpPvGhS_XHlHXa5SBIJh9CQlNj5zuW_GpdPKc,5273
|
|
12
|
+
src/core/registry.py,sha256=IMkic1ukmo8HCX1TY5YoKjwKT_IT-ZmVx6sdntC5X2M,3289
|
|
13
|
+
src/core/rule_discovery.py,sha256=smxJ9PEyMqEAIicsWaHOaSHD1PHUAOeFZT_a3DNRwgE,4163
|
|
14
|
+
src/core/types.py,sha256=dIYLaCDNtCAzVaplx5S5yxywkLIuX0BN9No_l2zCfV0,2887
|
|
15
|
+
src/core/violation_builder.py,sha256=7AQODqxwc3qp_4m1MPzknkeNGlht1LsDjPsTPpWAGJY,4678
|
|
16
|
+
src/linter_config/__init__.py,sha256=_I2VVlZlfKyT-tKukuUA5-aVcHLOe3m6C2cev43AiEc,298
|
|
17
|
+
src/linter_config/ignore.py,sha256=S2Ub0CCOOC-wpU5Y_EodMprciw18fgWcnp4z_h1MYNk,19638
|
|
18
|
+
src/linter_config/loader.py,sha256=HB09W-uVsEcgCbvUwUHS5Jm2n0bqBXA3744vMc4GAqk,2542
|
|
19
|
+
src/linters/__init__.py,sha256=-nnNsL8E5-2p9qlLKp_TaShHAjPH-NacOEU1sXmAR9k,77
|
|
20
|
+
src/linters/dry/__init__.py,sha256=p58tN3z_VbulfTkRm1kLZJ43Bemt66T2sro1teirUY8,826
|
|
21
|
+
src/linters/dry/base_token_analyzer.py,sha256=SgnsX4a4kUzOsA9hTkLkg4yB8I77ewAyiUp6cAybUrg,2684
|
|
22
|
+
src/linters/dry/block_filter.py,sha256=FN1QDXQu6HYhdHo6fkzUDUrMX4TqaLV_myBOQRsmPfk,8242
|
|
23
|
+
src/linters/dry/block_grouper.py,sha256=gBvSPgy8jumChZ53px3P7hDCJfi5PDrKhwxLTgNy7ig,1810
|
|
24
|
+
src/linters/dry/cache.py,sha256=QbFajUNb3v2LVuYbTJXY9ExXjjIsd73DQ5Y1hB-SH1s,5879
|
|
25
|
+
src/linters/dry/cache_query.py,sha256=NTBh4ISy76LJb9tJ6G94fE0R2OiE0eQ-zVvB08WG2bA,1802
|
|
26
|
+
src/linters/dry/config.py,sha256=XaHX5SnbH8cEBHiNKMSuYQJwCbKL-Q9XgZVRcc9UBgQ,5439
|
|
27
|
+
src/linters/dry/config_loader.py,sha256=wikqnigOp6p1h9jaAATV_3bDXSiaIUFaf9xg1jQMDpo,1313
|
|
28
|
+
src/linters/dry/deduplicator.py,sha256=a1TRvldxCszf5QByo1ihXF3W98dpGuyaRT74jPfQftM,3988
|
|
29
|
+
src/linters/dry/duplicate_storage.py,sha256=3OxE2mtoWGAsNNrB8J2c-4JirLUoqZ9ptydO5beM-mg,1971
|
|
30
|
+
src/linters/dry/file_analyzer.py,sha256=ufSQ85ddsGTqGnBHZNTdV_5DGfTpUmJOB58sIdJNV0I,2928
|
|
31
|
+
src/linters/dry/inline_ignore.py,sha256=ASfA-fp_1aPpkakN2e0T6qdTh8S7Jqj89ovxXJLmFlc,4439
|
|
32
|
+
src/linters/dry/linter.py,sha256=XMLwCgGrFX0l0dVUJs1jpsXOfgxeKKDbxOtN5h5Emhk,5835
|
|
33
|
+
src/linters/dry/python_analyzer.py,sha256=jmMUw2YhwkRrAJJDHaHKffGUjCiz5OzWwv_ZJrzNXqw,27367
|
|
34
|
+
src/linters/dry/storage_initializer.py,sha256=ykMALFs4uMUrN0_skEwySDl_t5Dm_LGHllF0OxDhiUI,1366
|
|
35
|
+
src/linters/dry/token_hasher.py,sha256=71njBzUsWvQjIWo38AKeRHQsG8K4jrjLTKuih-i6Gis,5372
|
|
36
|
+
src/linters/dry/typescript_analyzer.py,sha256=ShNoB2KfPe010wKEZoFxn-ZKh0MnRUwgADDQKQtfedI,21627
|
|
37
|
+
src/linters/dry/violation_builder.py,sha256=EUiEQIOZjzAoHEqZiIR8WZP8m4dgqJjcveR5mdMyClI,2803
|
|
38
|
+
src/linters/dry/violation_filter.py,sha256=aTOMz8kXG2sZlSVcf3cAxgxHs7f2kBXInfr1V_04fUQ,3125
|
|
39
|
+
src/linters/dry/violation_generator.py,sha256=cc6aKvTxtHSZm0F7Y-gL1bmD3JUphRmAvcbqk9aUzGg,6128
|
|
40
|
+
src/linters/file_header/__init__.py,sha256=S3a2xrOlxnNWD02To5K2ZwILsNEvSj1IvUAH8RjgOV4,791
|
|
41
|
+
src/linters/file_header/atemporal_detector.py,sha256=Rbjs2GHsgO-1r2kUHUuDRsX3XAX0INFZWVrKoUh5v-8,3104
|
|
42
|
+
src/linters/file_header/config.py,sha256=1pjfa0hIUEiE0riMLTH9FQiETXfEkhAFuv1HBx0SGgI,2073
|
|
43
|
+
src/linters/file_header/field_validator.py,sha256=bR0xZ5DhPHBE8lopbcaNwJr0auZlNDkCwml3Q8_7XOY,2380
|
|
44
|
+
src/linters/file_header/linter.py,sha256=ISNnt-YMgkKQl4psVLefp5P7qTQ6wIGidq4dTkTNbaI,11228
|
|
45
|
+
src/linters/file_header/python_parser.py,sha256=TcnA8OCbHK3YtixmFwSP7OCvxjjGLZBioez9wVukz28,2951
|
|
46
|
+
src/linters/file_header/violation_builder.py,sha256=yDNqsZ-hDnURpph9oZNj2lCkJgd_7hprjk3Fr0iPphs,2653
|
|
47
|
+
src/linters/file_placement/__init__.py,sha256=vJ43GZujcbAk-K3DwfsQZ0J3yP_5G35CKssatLyntXk,862
|
|
48
|
+
src/linters/file_placement/config_loader.py,sha256=Of5sTG2S-04efn3KOlXrSxpMcC1ipBpSvCjtJOMmWno,2640
|
|
49
|
+
src/linters/file_placement/directory_matcher.py,sha256=YaBeLGiT4bgqN_v4FmEmSASOBxkMC1lyEYpL17wLIDY,2607
|
|
50
|
+
src/linters/file_placement/linter.py,sha256=HtQf08UmKCe5YAHoPvVgWmxdUFu6ZeP2qicg-q7BgzM,14438
|
|
51
|
+
src/linters/file_placement/path_resolver.py,sha256=S6g7xOYsoSc0O_RDJh8j4Z2klcwzp16rSUfEAErGOTI,1972
|
|
52
|
+
src/linters/file_placement/pattern_matcher.py,sha256=3HZWYgQKXz_y13z3lO1YHn51khCaiGOrneGxKXGWGw0,1898
|
|
53
|
+
src/linters/file_placement/pattern_validator.py,sha256=eMt5GB5lgJMhhQACOlfDXQFfSfNrOY-wJN1JanGka6Q,3717
|
|
54
|
+
src/linters/file_placement/rule_checker.py,sha256=JONXcaYxZ8CM_7Zg6Th01p5cve1rJ8YkReAUJ44nfUg,7795
|
|
55
|
+
src/linters/file_placement/violation_factory.py,sha256=NkQmBcgpa3g3W2ZdFZNQ5djLVP4x9OKs65d7F1rCKvM,6040
|
|
56
|
+
src/linters/magic_numbers/__init__.py,sha256=17dkCUf0uiYLvpOZF01VDojj92NzxXZMtRhrSBUzsdc,1689
|
|
57
|
+
src/linters/magic_numbers/config.py,sha256=3zV6ZNezouBWUYy4kMw5PUlPNvIWXVwOxTz1moZfRoI,3270
|
|
58
|
+
src/linters/magic_numbers/context_analyzer.py,sha256=cGXozlKll10Zao56c2E8ThIyH2mSQaPaUau_g7ngRLw,8446
|
|
59
|
+
src/linters/magic_numbers/linter.py,sha256=maj4NgrDapv0RurKvaVgOI1BUujixZv4E7UeYy4eGT4,18394
|
|
60
|
+
src/linters/magic_numbers/python_analyzer.py,sha256=0u1cFaaFCqOW5yeW-YbmPoZuVIeN_KtmkFyyxup6aR0,2803
|
|
61
|
+
src/linters/magic_numbers/typescript_analyzer.py,sha256=NTU1XY-Hudse5oxOtEiG6nA0Rs5Icn9HXELnyPj8OZU,7554
|
|
62
|
+
src/linters/magic_numbers/violation_builder.py,sha256=SqIQv3N9lpP2GRC1TC5InrvaEdrAq24V7Ec2Xj5olb0,3308
|
|
63
|
+
src/linters/nesting/__init__.py,sha256=tszmyCEQMpEwB5H84WcAUfRYDQl7jpsn04es5DtAHsM,3200
|
|
64
|
+
src/linters/nesting/config.py,sha256=PfPA2wJn3i6HHXeM0qu6Qx-v1KJdRwlRkFOdpf7NhS8,2405
|
|
65
|
+
src/linters/nesting/linter.py,sha256=-klbXIbg145beICop81CNQ5J1OInQaeycDT8u3Ff2Ww,6236
|
|
66
|
+
src/linters/nesting/python_analyzer.py,sha256=DF2lVnxYstakOpP_Zizox583Ypkb4eUpgQYEpu6x8gk,3078
|
|
67
|
+
src/linters/nesting/typescript_analyzer.py,sha256=wO6p6QNnyW1uaieTCZjaqR56C68AjifAv23tYaSuR2c,3860
|
|
68
|
+
src/linters/nesting/typescript_function_extractor.py,sha256=dDB1otJnFMCo-Pj4mTr4gekKe7V4ArOAtX6gV0dBDc4,4494
|
|
69
|
+
src/linters/nesting/violation_builder.py,sha256=sMHS45F2lrA1WYrG3Uug8HfeRPljnXcyJPHSe2F76Bs,4828
|
|
70
|
+
src/linters/print_statements/__init__.py,sha256=yhvdTFSqBB4UDreeadTHKFzhayxeT6JkF3yxUHMgn1g,1893
|
|
71
|
+
src/linters/print_statements/config.py,sha256=GTYIAVovc6e7i4eTrSl2D0Nmf55U5bpEd7FI6Knx_CE,3133
|
|
72
|
+
src/linters/print_statements/linter.py,sha256=MieQfEdmVJoiRR7Aa2-OW2RnOC4Cheu0jgDezc-ndH8,14572
|
|
73
|
+
src/linters/print_statements/python_analyzer.py,sha256=5bzYWLKqQOW0xnsZYZgUJh6sl3SYDKdQe_BAw93HTug,5543
|
|
74
|
+
src/linters/print_statements/typescript_analyzer.py,sha256=aKTA2E96qxA1PzsRVL5DLYE-abCFM67OGhqU-9iFgzA,5265
|
|
75
|
+
src/linters/print_statements/violation_builder.py,sha256=z01K-2kpR9VnGo_S9hcU0Y6_QMol0XxxnJxj36GdL7E,3426
|
|
76
|
+
src/linters/srp/__init__.py,sha256=GbhaSB2_AYY-mWgG_ThbyAcDXoVZuB5eLzguoShf38w,3367
|
|
77
|
+
src/linters/srp/class_analyzer.py,sha256=wuwviwhN1F_VVPaQ3pZvffmY3e9ToxPNJhv6BjhsgZc,3761
|
|
78
|
+
src/linters/srp/config.py,sha256=hTxrM21HIOmg0sM6eJ_h3hRnuxqRZEgs13Ie97-PDr4,3397
|
|
79
|
+
src/linters/srp/heuristics.py,sha256=Fwrro3I0qOV4o0epM0ZkqgQUU9bK1b2igxoxWjReljU,3185
|
|
80
|
+
src/linters/srp/linter.py,sha256=aXeMKmMf5PC5P2E-mstys2xndkrvpZF6tAq9xRdzOYo,7624
|
|
81
|
+
src/linters/srp/metrics_evaluator.py,sha256=Prk_dPacas_dX7spAzV0g734srmzT5u0t5d4mTG9g2o,1606
|
|
82
|
+
src/linters/srp/python_analyzer.py,sha256=hA1gtiLJISier6B3rHJX5EkimQc8FJapy-XNtR7xaBQ,2621
|
|
83
|
+
src/linters/srp/typescript_analyzer.py,sha256=Wi0P_G1v5AnZYtMN3sNm1iHva84-8Kep2LZ5RmAS4c4,2885
|
|
84
|
+
src/linters/srp/typescript_metrics_calculator.py,sha256=2VLRux_tf1Cw645wwTuol3Z5A6-mkl4cgyW34myy00Q,2728
|
|
85
|
+
src/linters/srp/violation_builder.py,sha256=jaIjVtRYWUTs1SVJVwd0FxCojo0DxhPzfhyfMKmAroM,3881
|
|
86
|
+
src/orchestrator/__init__.py,sha256=XXLDJq2oaB-TpP2Y97GRnde9EkITGuFCmuLrDfxI9nY,245
|
|
87
|
+
src/orchestrator/core.py,sha256=z0YcwsK18uhlztIPi54ux3mOm8fHMREYJoudsJPhC0Q,8857
|
|
88
|
+
src/orchestrator/language_detector.py,sha256=rHyVMApit80NTTNyDH1ObD1usKD8LjGmH3DwqNAWYGc,2736
|
|
89
|
+
src/templates/thailint_config_template.yaml,sha256=vxyhRRi25_xOnHDRx0jzz69dgPqKU2IU5-YFGUoX5lM,4953
|
|
90
|
+
src/utils/__init__.py,sha256=NiBtKeQ09Y3kuUzeN4O1JNfUIYPQDS2AP1l5ODq-Dec,125
|
|
91
|
+
src/utils/project_root.py,sha256=b3YTEGTa9RPcOeHn1IByMMWyRiUabfVlpnlektL0A0o,6156
|
|
92
|
+
thailint-0.5.0.dist-info/METADATA,sha256=anESyXpJOqYeRjeHVOOL38dY56zD0XLPkCMmUNBKfx8,37838
|
|
93
|
+
thailint-0.5.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
94
|
+
thailint-0.5.0.dist-info/entry_points.txt,sha256=l7DQJgU18sVLDpSaXOXY3lLhnQHQIRrSJZTQjG1cEAk,62
|
|
95
|
+
thailint-0.5.0.dist-info/licenses/LICENSE,sha256=kxh1J0Sb62XvhNJ6MZsVNe8PqNVJ7LHRn_EWa-T3djw,1070
|
|
96
|
+
thailint-0.5.0.dist-info/RECORD,,
|
src/.ai/layout.yaml
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
file-placement:
|
|
2
|
-
directories:
|
|
3
|
-
.ai:
|
|
4
|
-
allow:
|
|
5
|
-
- .*\\.(md|yaml|yml)$
|
|
6
|
-
.roadmap:
|
|
7
|
-
allow:
|
|
8
|
-
- .*\\.md$
|
|
9
|
-
docs:
|
|
10
|
-
allow:
|
|
11
|
-
- .*\\.(md|rst|txt|py)$
|
|
12
|
-
deny:
|
|
13
|
-
- ^(?!.*\\.(md|rst|txt|py)$).*
|
|
14
|
-
src:
|
|
15
|
-
allow:
|
|
16
|
-
- .*\\.py$
|
|
17
|
-
deny:
|
|
18
|
-
- test_.*\\.py$
|
|
19
|
-
- .*_test\\.py$
|
|
20
|
-
tests:
|
|
21
|
-
allow:
|
|
22
|
-
- test_.*\\.py$
|
|
23
|
-
- .*_test\\.py$
|
|
24
|
-
- conftest\\.py$
|
|
25
|
-
- __init__\\.py$
|
|
26
|
-
deny:
|
|
27
|
-
- ^(?!test_|.*_test\\.py|conftest\\.py|__init__\\.py).*\\.py$
|
|
28
|
-
global_patterns:
|
|
29
|
-
deny:
|
|
30
|
-
- message: Python files should be in src/, tests/, scripts/, docs/, .ai/, or .roadmap/
|
|
31
|
-
directories
|
|
32
|
-
pattern: ^(?!src/|tests/|scripts/|docs/|\\.ai/|\\.roadmap/).*\\.py$
|
|
33
|
-
ignore:
|
|
34
|
-
- __pycache__/
|
|
35
|
-
- '*.pyc'
|
|
36
|
-
- .git/
|
|
37
|
-
- .venv/
|
|
38
|
-
- venv/
|
|
39
|
-
- .pytest_cache/
|
|
40
|
-
- .mypy_cache/
|
|
41
|
-
- .ruff_cache/
|
|
42
|
-
- htmlcov/
|
|
43
|
-
- '*.egg-info/'
|
|
44
|
-
- dist/
|
|
45
|
-
- build/
|
|
46
|
-
nesting:
|
|
47
|
-
enabled: true
|
|
48
|
-
max_nesting_depth: 3
|