Compare commits

...

246 Commits

Author SHA1 Message Date
v2ray
dde47290d7 completely remove json folder in v2ray 2016-01-17 21:43:10 +01:00
Darien Raymond
9af80c834b Merge branch 'master' of https://github.com/v2ray/v2ray-core 2016-01-17 15:22:08 +00:00
Darien Raymond
ae8121e633 remove json packages in app folder 2016-01-17 15:20:49 +00:00
Darien Raymond
0210e35c61 Merge pull request #69 from kxjhlele/patch-7
Update install.sh
2016-01-17 15:02:04 +01:00
kxjhlele
76e5df4ea8 Update install.sh
go1.5.3
2016-01-17 20:59:32 +08:00
v2ray
48f1d34ca5 fix config in http 2016-01-16 13:08:50 +01:00
v2ray
2d233295e6 simplify stringlist 2016-01-15 15:23:12 +01:00
v2ray
a403859105 move network to net 2016-01-15 14:34:33 +01:00
v2ray
393a64820f simplify port range 2016-01-15 13:39:36 +01:00
v2ray
5ceac7a6e2 move host from net/json to net 2016-01-15 13:17:04 +01:00
v2ray
1f1f79b90a simplify address 2016-01-15 13:16:07 +01:00
v2ray
8c08248418 enable json test in travis 2016-01-15 13:14:08 +01:00
v2ray
e5fa96f814 massive refactoring against json config parsing 2016-01-15 12:43:06 +01:00
Darien Raymond
f87ab4713c simplify v2ray.service 2016-01-13 10:13:37 +00:00
v2ray
9bc68f3575 update repo 2016-01-13 01:05:37 +01:00
v2ray
e09363cb96 check file exists before executing 2016-01-13 01:05:27 +01:00
v2ray
e433779dfc install unzip and daemon for yum and apt-get 2016-01-13 00:52:26 +01:00
v2ray
a6b662b173 add systemd related config 2016-01-12 23:35:45 +01:00
v2ray
04e7dc87a8 log request content as debug 2016-01-12 18:28:00 +01:00
v2ray
c4b787e637 more china sites 2016-01-12 18:25:51 +01:00
v2ray
7881330644 remove unnecessary memory allocation in vmess 2016-01-12 13:39:17 +01:00
v2ray
f7a152b871 remove unnecessary memory allocation in id generation 2016-01-12 13:39:02 +01:00
v2ray
1958a874cd remove unnecessary memory allocation in uuid generation 2016-01-12 13:38:47 +01:00
v2ray
48ff2a5ae8 use [16]byte instead of string for faster hashing 2016-01-12 13:02:05 +01:00
Darien Raymond
bed5235772 remove proxy/vmess/protocol/user 2016-01-12 10:52:40 +00:00
Darien Raymond
dc1fbecdfb remove fuzzing tests 2016-01-12 10:45:38 +00:00
Darien Raymond
02a803262f update version for 1.4 2016-01-12 10:41:59 +00:00
v2ray
349b02084c rewrite hashing logic in vmess 2016-01-12 11:38:43 +01:00
v2ray
c31c49d11f more china sites 2016-01-12 10:40:44 +01:00
v2ray
559eadcaf1 test case for sized queue 2016-01-11 23:04:24 +01:00
v2ray
ea080b2ebf rewrite id hash queue 2016-01-11 22:51:35 +01:00
v2ray
8f1c4b7f71 simplify code 2016-01-11 12:35:36 +01:00
v2ray
47e2d957d1 don't process malformed request 2016-01-11 12:35:28 +01:00
v2ray
8daea1dc06 close inbound ray when there is an error 2016-01-11 01:01:52 +01:00
v2ray
adf5820286 futher reduce memory usage of uuid 2016-01-11 00:22:59 +01:00
v2ray
774095568f lazy initialization of uuid string 2016-01-10 23:00:40 +01:00
v2ray
0107ea1636 update release version 2016-01-10 22:38:02 +01:00
v2ray
c061cf9f34 update official config 2016-01-10 21:38:37 +01:00
v2ray
57709a5cdb more china sites 2016-01-10 15:23:07 +01:00
v2ray
e0b56528fc leverage sync.Pool for allocation boost 2016-01-10 10:50:36 +01:00
v2ray
4773a8f4ae exit faster in cleanup thread 2016-01-10 10:31:27 +01:00
v2ray
add1197cc9 typo 2016-01-10 10:24:21 +01:00
v2ray
2c892e7b9d more china sites 2016-01-10 09:34:31 +01:00
v2ray
d1fecba6e4 improve timed queue performance 2016-01-10 09:11:46 +01:00
v2ray
8f9b23a6ab fix arch detection in install release script 2016-01-09 21:50:27 +01:00
v2ray
89175198ee remove unnecessary cleanup routine 2016-01-09 20:36:23 +01:00
v2ray
ca71c0300f avoid infinite loop in next uuid generation 2016-01-09 01:23:38 +01:00
v2ray
242624de96 fix test break 2016-01-09 00:22:50 +01:00
v2ray
9d95b9ebae simplify official config settings 2016-01-09 00:13:16 +01:00
v2ray
4ca43c3121 alternative user ids 2016-01-09 00:10:57 +01:00
v2ray
43f76d4704 doc for InboundConnectionHandler 2016-01-06 22:39:56 +01:00
v2ray
5b1854f842 simplify connection handler registration 2016-01-06 16:23:54 +01:00
v2ray
350b31cad9 First step to reduce number of bytes for response validation 2016-01-06 01:37:53 +01:00
v2ray
0921470341 use packet filter in point 2016-01-05 21:54:23 +01:00
v2ray
b9c3f2cb75 Fix for empty packets 2016-01-05 12:08:16 +01:00
v2ray
7c64093a7a Output debug info when parsing VMess request 2016-01-05 00:08:00 +01:00
v2ray
3e76c3f307 fix a typo 2016-01-04 23:00:14 +01:00
v2ray
43a06e52de init timeout value 2016-01-04 22:57:17 +01:00
v2ray
1239b1a57b fix buffer usage in vmess out 2016-01-04 22:02:22 +01:00
v2ray
84464e863d more china sites 2016-01-04 22:02:06 +01:00
v2ray
c74091453c no op when new timeout setting is the same as previous 2016-01-04 22:01:46 +01:00
Darien Raymond
30ed9fdddc more china sites 2016-01-04 17:01:51 +00:00
Darien Raymond
e6ad1d8518 format code 2016-01-04 14:16:52 +00:00
Darien Raymond
27b521d2aa fix buffer usage in VMess 2016-01-04 12:01:32 +00:00
v2ray
5df40c31f0 more china sites 2016-01-04 09:37:41 +01:00
v2ray
5f6d932c03 fixes for mutex usage 2016-01-04 08:41:01 +01:00
v2ray
4271e619fe fixes for mutex usage 2016-01-04 08:40:24 +01:00
v2ray
59a5f832f6 fixes for sync logic 2016-01-04 01:19:27 +01:00
v2ray
4a7f45113a check accepting in http 2016-01-04 01:13:43 +01:00
v2ray
e2623e65dd more china sites 2016-01-04 01:12:38 +01:00
v2ray
24adb769a6 more china sites 2016-01-04 01:05:32 +01:00
v2ray
56c5302367 close method for point server 2016-01-04 00:33:25 +01:00
v2ray
201481a82c close method for inbound connection handler 2016-01-03 23:30:37 +01:00
v2ray
8a132643ef more china sites 2016-01-03 15:38:09 +01:00
v2ray
0780db7999 move connhandler to proxy 2016-01-02 23:32:18 +01:00
v2ray
54ce82fbfa Move unnecessary functions to internal 2016-01-02 23:08:36 +01:00
v2ray
1c4c9bffad Move proxy/common/config to proxy/internal/config 2016-01-02 17:40:51 +01:00
v2ray
dc86eb76da update version and code name 2016-01-01 23:48:20 +01:00
v2ray
3baa1f5bc5 interface for inbound connection handler manager 2016-01-01 23:44:11 +01:00
v2ray
6344369eb4 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2016-01-01 22:34:59 +01:00
Darien Raymond
e483075941 Merge pull request #63 from adoot/master
http bug fixes & disable keep-alive
2016-01-01 22:34:19 +01:00
adoot
09bf6def69 http bug fixes & disable keep-alive
This patch defers Conn.Close call until all responses from server
has been written to the client. It should fix many of the hanging
issues we have with plain HTTP requests.
2016-01-01 13:08:53 -08:00
v2ray
dfa158677a Update copyright 2016-01-01 00:08:46 +01:00
v2ray
e19d8002a8 Refresh rate of a inbound detour allocation 2015-12-29 20:52:35 +01:00
v2ray
71c70416f5 config for on demand inbound detour 2015-12-28 23:17:38 +01:00
v2ray
96053d9e12 description for tools package 2015-12-28 10:30:21 +01:00
v2ray
f9e017d7c4 Move docker files to tools 2015-12-28 10:28:14 +01:00
v2ray
639bcad78d Output SHA1 checksum for released packages 2015-12-27 23:27:39 +01:00
V2 Ray
ed5be19dbe Merge pull request #62 from adoot/master
docker build for v2ray
2015-12-27 21:07:23 +01:00
adoot
ffa2f49621 add: docker file 2015-12-25 02:27:13 +00:00
adoot
3bbb1c6a93 minor: ignore error on mkdir in install.sh 2015-12-25 02:11:21 +00:00
v2ray
20b64ecdfe more china sites 2015-12-25 01:24:08 +01:00
v2ray
8d6fdd014a dns cache command 2015-12-25 01:07:42 +01:00
v2ray
367d17b44c more china sites 2015-12-23 09:58:18 +01:00
v2ray
4a8ec6926b dialer 2015-12-17 01:19:04 +01:00
v2ray
6543facd51 converting from host to address directly 2015-12-17 00:58:45 +01:00
v2ray
34a0cb0b70 move port out of address 2015-12-16 23:53:38 +01:00
v2ray
4be7cd7908 Fix test flakiness 2015-12-16 16:42:54 +01:00
v2ray
e3a00d1400 improve http proxy effeciency 2015-12-16 16:37:32 +01:00
v2ray
b3ec97058e refind http proxy 2015-12-16 15:52:40 +01:00
v2ray
655f8cd150 more china sites 2015-12-16 15:49:09 +01:00
v2ray
ac8d0bbd1c more china sites 2015-12-16 00:57:04 +01:00
v2ray
51c6ef63f4 more china sites 2015-12-16 00:08:46 +01:00
v2ray
8d6874a7da more china sites 2015-12-15 23:04:44 +01:00
v2ray
05397c5bde more china sites 2015-12-15 22:55:06 +01:00
v2ray
34f34bb25d refine http proxy 2015-12-15 22:13:09 +01:00
v2ray
24653aef21 still close connection agressively 2015-12-15 16:38:25 +01:00
v2ray
e12e5a0ecb barely working http proxy 2015-12-15 16:00:47 +01:00
v2ray
d0a75ce9f3 handle connect request in http proxy 2015-12-15 00:53:40 +01:00
v2ray
e2acec745c Add a bytes method to alloc.buffer 2015-12-15 00:53:27 +01:00
Darien Raymond
05caf04eee unfinished http proxy 2015-12-14 16:26:29 +00:00
Darien Raymond
f0c1695db4 send request without waiting for the first packet. 2015-12-14 14:51:48 +00:00
Darien Raymond
86f97616fc add cache to router 2015-12-14 13:40:13 +00:00
Darien Raymond
9e84519134 validity map 2015-12-14 13:14:10 +00:00
Darien Raymond
56976e2d1b more chinasites 2015-12-14 10:52:18 +00:00
Darien Raymond
969336de9c remove no-close option from start-stop-daemon 2015-12-14 09:34:25 +00:00
v2ray
543478a01e Move code to init 2015-12-14 01:12:29 +01:00
v2ray
d3422597d6 more china sites 2015-12-14 01:07:45 +01:00
v2ray
1e6aa73781 chinasites 2015-12-14 01:01:57 +01:00
v2ray
09f7233e9d update version 2015-12-13 17:31:22 +01:00
v2ray
1f584f1502 interface for secondary id 2015-12-13 00:10:35 +01:00
v2ray
a63670311e use uuid in vmess id 2015-12-12 21:40:16 +01:00
v2ray
ce32d54a5f Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-12 21:12:15 +01:00
v2ray
1c5c1a7aef debug info 2015-12-12 20:57:47 +01:00
v2ray
88851f2bc9 Allow IP address is passed as domain in socks5 protocol 2015-12-12 20:57:39 +01:00
v2ray
cf56b89898 more router test cases 2015-12-12 20:47:29 +01:00
v2ray
b4504f9510 more china sites 2015-12-12 20:47:17 +01:00
v2ray
a0fb5c2e19 more china sites 2015-12-12 17:53:57 +01:00
Darien Raymond
eae3906858 update version 2015-12-12 16:24:52 +00:00
v2ray
164465e60d more test case 2015-12-12 17:17:23 +01:00
v2ray
b87cc5baf1 fix a bug in fieldrule 2015-12-12 17:15:29 +01:00
v2ray
fed5697dc3 SwitchAccount command 2015-12-12 13:11:49 +01:00
v2ray
47f35b1c3e uuid 2015-12-12 11:32:40 +01:00
Darien Raymond
b6ed26aedf pubsub 2015-12-11 14:56:10 +00:00
Darien Raymond
a540d7dc99 allow dns modification only from trusted tags 2015-12-11 11:08:07 +00:00
Darien Raymond
dd81fc6f6a hide space implementations from interfaces 2015-12-11 11:01:20 +00:00
v2ray
46ab9c45cc space with context 2015-12-10 23:55:39 +01:00
v2ray
5b3e84ede6 dns config 2015-12-10 22:08:36 +01:00
v2ray
95c8910f87 fix v2ray tmp path 2015-12-10 15:45:51 +01:00
v2ray
e57132a36e update to 1.1.3 2015-12-10 15:10:15 +01:00
v2ray
aa83ea35ca really fix test break 2015-12-10 15:03:55 +01:00
v2ray
ff84b9b5f9 update to 1.1.2 2015-12-10 12:44:29 +01:00
v2ray
7d2d536184 Fix test break 2015-12-10 12:44:06 +01:00
v2ray
9a9543dcdb Update to 1.1.1 2015-12-10 12:41:19 +01:00
v2ray
7b059c2aec make v2ray executable 2015-12-10 12:40:16 +01:00
v2ray
b2ac03531f update path in systemv script 2015-12-10 12:32:21 +01:00
v2ray
36e51773fd remove dns detour example 2015-12-09 22:38:24 +01:00
v2ray
ae7a6c4d24 more domestic sites 2015-12-09 22:37:22 +01:00
Darien Raymond
ed09b4ea6a support domain as host in VMess outbound config. 2015-12-09 11:20:59 +00:00
v2ray
bc144248e2 Increase dns cleanup interval to 60 seconds 2015-12-09 00:30:58 +01:00
v2ray
6afeaccc5a Update chinaip generator 2015-12-08 23:12:12 +01:00
v2ray
9d51bd2985 test case for chinaip 2015-12-08 18:19:40 +01:00
V2 Ray
161d3dbf01 Merge pull request #58 from kxjhlele/patch-6
Update install-release.sh
2015-12-08 17:36:47 +01:00
Darien Raymond
c19341ffab chinaip 2015-12-08 16:31:31 +00:00
kxjhlele
596ffebff6 Update install-release.sh 2015-12-08 21:23:47 +08:00
Darien Raymond
0ccce0ff7e include systemv script in linux package 2015-12-08 11:41:20 +00:00
v2ray
e2b13bc104 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-08 12:03:06 +01:00
Darien Raymond
062e98b4ae start script for sysv 2015-12-08 10:59:46 +00:00
v2ray
2ba35ed5f8 update install release script 2015-12-08 01:01:36 +01:00
v2ray
3a1c134f84 add tag for inbound detour connection 2015-12-08 00:54:45 +01:00
v2ray
641e5db851 rename RouterConfig to Config 2015-12-08 00:04:02 +01:00
v2ray
aab774e78f simplify router config 2015-12-07 22:47:47 +01:00
Darien Raymond
a7c2425c9c Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-07 19:32:48 +00:00
Darien Raymond
af8412175e re-org vmess content 2015-12-07 19:32:38 +00:00
v2ray
a1ba0eb413 update test file to not generate temp files in source folder 2015-12-07 13:09:38 +01:00
V2 Ray
0a4d9fb037 Delete error.log 2015-12-07 12:34:57 +01:00
Darien Raymond
08d4b7793d update whitelist 2015-12-07 11:34:23 +00:00
Darien Raymond
74c7810957 lowercase domain matching 2015-12-07 11:10:27 +00:00
Darien Raymond
96aa94002a regexp support in domain matching 2015-12-07 10:58:43 +00:00
v2ray
46071b9dac Update to v1.1 2015-12-06 21:26:40 +01:00
v2ray
c54c0218e0 simplify test 2015-12-06 20:38:01 +01:00
v2ray
092217182a simplify config directory 2015-12-06 18:21:15 +01:00
v2ray
8bee0c4a7b cleanup shell/point/config 2015-12-06 16:41:41 +01:00
v2ray
e1c58fae2b dns cache 2015-12-06 11:00:10 +01:00
V2 Ray
59030814cb Merge pull request #57 from kxjhlele/patch-5
Update install-release.sh
2015-12-06 08:23:10 +01:00
V2 Ray
4325f273a0 Merge pull request #56 from kxjhlele/patch-4
Update install.sh
2015-12-06 07:09:37 +01:00
kxjhlele
ef9d32bf16 Update install-release.sh
uuid 变更,port 不知道为啥运行失败,
2015-12-06 12:25:21 +08:00
kxjhlele
d86af91c94 Update install.sh 2015-12-06 10:38:30 +08:00
v2ray
b083773411 test case for log 2015-12-05 23:47:23 +01:00
v2ray
15e6e6d80c introduce app.Space 2015-12-05 22:55:45 +01:00
v2ray
db7d48e48f Refine log settings 2015-12-05 21:10:14 +01:00
v2ray
32c3565681 typo 2015-12-05 01:49:03 +01:00
v2ray
06f9b65ec4 receiver manager 2015-12-05 01:16:21 +01:00
v2ray
7a01d644e4 update install-release.sh 2015-12-04 22:53:56 +01:00
v2ray
003fac7d2a doc 2015-12-04 22:53:31 +01:00
v2ray
86f800693e Update to golang 1.5.2 2015-12-04 20:53:48 +01:00
v2ray
08eaca8a2c Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-04 20:50:30 +01:00
Darien Raymond
5ec30c3ad0 more test cases 2015-12-04 16:32:42 +00:00
Darien Raymond
30be218c62 fix test break 2015-12-04 16:01:39 +00:00
Darien Raymond
3327cb0b80 format code 2015-12-04 15:49:45 +00:00
Darien Raymond
1a25931944 advanced end 2 end test 2015-12-04 15:49:10 +00:00
Darien Raymond
223ff7d561 add error message 2015-12-04 14:30:41 +00:00
Darien Raymond
3942679371 doc 2015-12-04 12:13:35 +00:00
Darien Raymond
6bb53251e9 handle response command in vmess outbound. 2015-12-04 11:42:56 +00:00
Darien Raymond
11220a4952 split vmess inbound and outbound 2015-12-04 11:07:32 +00:00
v2ray
7958a16844 Update version for next release 2015-12-04 10:37:24 +01:00
v2ray
36b7a3fe07 test case for socks protocol 2015-12-03 23:31:04 +01:00
v2ray
f69b83f3e6 add interface for socks config 2015-12-03 22:41:06 +01:00
v2ray
73807390f1 Fix a bug in inbound detour handler 2015-12-03 20:57:33 +01:00
v2ray
e42510aca5 more literals 2015-12-03 16:57:23 +01:00
v2ray
f3f1c09e49 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-03 15:19:45 +01:00
v2ray
88d75fd1c0 doc 2015-12-03 15:18:43 +01:00
Darien Raymond
9c1a31c30e Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-03 14:12:55 +00:00
Darien Raymond
f2c3d41f2d comments 2015-12-03 14:12:47 +00:00
V2Ray
301f1755b2 add a script to install from release 2015-12-03 10:58:31 +01:00
Darien Raymond
9b830fc432 update end 2 end test 2015-12-02 23:41:34 +00:00
Darien Raymond
5003afc85c test case for vmess outbound config 2015-12-02 21:30:15 +00:00
Darien Raymond
d22fed67ad format code 2015-12-02 20:58:46 +00:00
Darien Raymond
ae056714db refactor common/net.Port 2015-12-02 20:44:01 +00:00
Darien Raymond
fa7c1069bc more update 2015-12-02 16:27:55 +00:00
Darien Raymond
ec31c1fa7f remove common/net unit test back. 2015-12-02 16:18:12 +00:00
Darien Raymond
ab875000e0 fix build break 2015-12-02 15:59:15 +00:00
Darien Raymond
36848af749 leveral serial.String in code 2015-12-02 15:49:34 +00:00
Darien Raymond
3c12469ae5 leverage serial.String in assert 2015-12-02 15:41:19 +00:00
Darien Raymond
7960b97c8e fix test break 2015-12-02 15:26:11 +00:00
Darien Raymond
091f047ebb add assertions for net utilities 2015-12-02 15:19:39 +00:00
Darien Raymond
0a2e4343bc massive refactoring against unit test lib 2015-12-02 14:27:18 +00:00
V2Ray
cee85bdf26 Add Port as a type 2015-12-02 12:47:54 +01:00
V2Ray
e8e6cf1429 doc for retry 2015-12-02 09:58:00 +01:00
V2Ray
d45b867f98 Rename RetryStrategy to Strategy as suggested by lint 2015-12-02 09:46:19 +01:00
Darien Raymond
2038cfa692 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-12-01 13:55:14 +00:00
Darien Raymond
cab29a7bc9 breaking change: update the way in vmess response validation 2015-12-01 13:54:49 +00:00
V2Ray
a04cfb726d Remove specs in favor of wiki 2015-11-30 20:36:34 +01:00
V2 Ray
1a7fbd6d1b Update README.md 2015-11-30 20:35:41 +01:00
V2 Ray
1928854542 Update README.md 2015-11-30 20:35:21 +01:00
Darien Raymond
56a79a2190 remove DetourTag and add more test cases 2015-11-30 15:14:38 +00:00
Darien Raymond
f6c486327f faster check in field rule 2015-11-30 12:46:09 +00:00
Darien Raymond
dc69dfb3e1 remove redundent user in vmess_freedom 2015-11-30 10:12:18 +00:00
V2Ray
07582c5b60 Move point to shell 2015-11-29 14:45:32 +01:00
V2Ray
cc61e88eca Log timestamp 2015-11-28 23:13:45 +01:00
V2Ray
10b5f5cc5e Rename vmess in & out file 2015-11-28 20:50:44 +01:00
V2Ray
c2904383b3 FIx a bug that mutex is passed by value. 2015-11-28 20:29:42 +01:00
V2Ray
29488087a8 format code 2015-11-28 20:29:27 +01:00
V2Ray
6626c22a8d refactor 2015-11-28 20:11:23 +01:00
V2Ray
7720d6bff6 version function 2015-11-28 16:23:46 +01:00
V2Ray
3f0ada1bc8 add response op in vmess protocol 2015-11-28 10:11:56 +01:00
V2Ray
9a88e8696a format code 2015-11-27 21:57:15 +01:00
V2Ray
4046ee968c refactor code 2015-11-27 21:50:28 +01:00
V2Ray
a8d07ce931 refactor code 2015-11-27 12:29:20 +01:00
V2Ray
916403dd46 Ready for 1.0 2015-11-27 12:11:17 +01:00
V2Ray
0fc9abf266 Happy birthday! 2015-11-27 11:39:17 +01:00
V2Ray
f4e8b4aa32 update server ip 2015-11-26 16:23:58 +01:00
V2Ray
ed52fe5af4 official server configuration 2015-11-26 13:36:54 +01:00
Darien Raymond
91af039412 update server config as well 2015-11-26 11:29:58 +00:00
Darien Raymond
759efb0cfa field domain and ip accepts list of strings 2015-11-26 11:22:34 +00:00
V2 Ray
d4dcbbc734 Workaround travis brekage
See: https://github.com/travis-ci/travis-ci/issues/5145
2015-11-23 12:08:10 +01:00
275 changed files with 13771 additions and 4219 deletions

View File

@@ -1,7 +1,7 @@
language: go
go:
- 1.5.1
- 1.5.2
before_install:
- go get github.com/axw/gocov/gocov
@@ -9,13 +9,15 @@ before_install:
- go get github.com/mattn/goveralls
script:
- go test github.com/v2ray/v2ray-core/...
- ./testing/coverage/coverall
- go test -tags json github.com/v2ray/v2ray-core/...
- ./testing/coverage/coverall
env:
secure: "nnEiGTuS8SsSFRpxUsPh6hmx/OfPDA6AO3OX7IpjPCbkQnjuFEsm3DfPEZOHTiYra00Ea3bNw9ePLPqyWdLAPmcLoUBhWRyfevt9m3jrbyrT8O9MCXNDY6i1ll3E43nx8kxRDF1QbYaSK4jppa8Vu2vyZH5+GiS0JVcw/7CfX8TyUMbDuyaQXYQ1kwixlThQZH43xxQ7u5CFF1pBFdwpkuDm2/wKUE+0CGZOeWzUYI8h1in4eY0Vse4Zt4GqS2eywf4rw12bBysZ21UnT/xu990m5nzAScbUGjloplJNpdZGtD8SBTGujZBMMUzKi/E0zJkdEf37MZ9rsarfv6v9khdwTp/8VoAiZxpG1IDISIu0TClAjw8NYO5hmuF01/oJexMuxqRhMPwbk1nPsustlBKEG3zjvW5yILMkiaRl67Pj+9h21zMGYmwPGka7Ollh9ki2h17RFNuj9Yxm5XX2xvZDIvtxL1a8S50vv7VZTT9rrrFMhGP7ty9gj2opjJ7QBgwi2W/eVGhfshfnZJjJ7Bqf+gl1MDOYcTlqmFe5JvKdIO9mN7gskJizmeFxh31LrZ2sCY71/iicGj7gZJ4e/u2/dI2OVfFWTTbHoiIyCLvvrA18zBF9Sky1Y3QDj85GdRtC0SI6TXJLAIa7jNgF6Nr2mAg044DaX9VW0VH0VnE="
before_deploy: "./release/release.sh"
before_deploy:
- rvm 1.9.3 do gem install mime-types -v 2.6.2
- "./release/release.sh"
deploy:
provider: releases
@@ -29,6 +31,7 @@ deploy:
- "$GOPATH/bin/v2ray-linux-32.zip"
- "$GOPATH/bin/v2ray-linux-arm.zip"
- "$GOPATH/bin/v2ray-linux-arm64.zip"
- "$GOPATH/bin/metadata.txt"
skip_cleanup: true
on:
tags: true

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015 V2Ray
Copyright (c) 2015-2016 V2Ray
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -5,11 +5,11 @@
[![Coverage Status](https://coveralls.io/repos/v2ray/v2ray-core/badge.svg?branch=master&service=github)](https://coveralls.io/github/v2ray/v2ray-core?branch=master)
[![GoDoc](https://godoc.org/github.com/v2ray/v2ray-core?status.svg)](https://godoc.org/github.com/v2ray/v2ray-core)
[中文](https://github.com/V2Ray/v2ray-core/blob/master/README.md) | [English](https://github.com/V2Ray/v2ray-core/blob/master/spec/en/README.md)
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。
[官方网站](https://www.v2ray.com/)
[官方网站](https://www.v2ray.com/) | [Wiki](https://github.com/v2ray/v2ray.github.io/wiki)
V2Ray provides building blocks for network proxy development. Read our [Wiki](https://github.com/v2ray/v2ray.github.io/wiki/en-us:Home) for more information.
## License
[The MIT License (MIT)](https://raw.githubusercontent.com/v2ray/v2ray-core/master/LICENSE)

View File

@@ -0,0 +1,41 @@
package controller
import (
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/app/internal"
)
// A SpaceController is supposed to be used by a shell to create Spaces. It should not be used
// directly by proxies.
type SpaceController struct {
packetDispatcher internal.PacketDispatcherWithContext
dnsCache internal.DnsCacheWithContext
pubsub internal.PubsubWithContext
inboundHandlerManager internal.InboundHandlerManagerWithContext
}
func New() *SpaceController {
return new(SpaceController)
}
func (this *SpaceController) Bind(object interface{}) {
if packetDispatcher, ok := object.(internal.PacketDispatcherWithContext); ok {
this.packetDispatcher = packetDispatcher
}
if dnsCache, ok := object.(internal.DnsCacheWithContext); ok {
this.dnsCache = dnsCache
}
if pubsub, ok := object.(internal.PubsubWithContext); ok {
this.pubsub = pubsub
}
if inboundHandlerManager, ok := object.(internal.InboundHandlerManagerWithContext); ok {
this.inboundHandlerManager = inboundHandlerManager
}
}
func (this *SpaceController) ForContext(tag string) app.Space {
return internal.NewSpace(tag, this.packetDispatcher, this.dnsCache, this.pubsub, this.inboundHandlerManager)
}

11
app/dns.go Normal file
View File

@@ -0,0 +1,11 @@
package app
import (
"net"
)
// A DnsCache is an internal cache of DNS resolutions.
type DnsCache interface {
Get(domain string) net.IP
Add(domain string, ip net.IP)
}

14
app/dns/config.go Normal file
View File

@@ -0,0 +1,14 @@
package dns
import (
"github.com/v2ray/v2ray-core/common/serial"
)
type CacheConfig struct {
TrustedTags map[serial.StringLiteral]bool
}
func (this *CacheConfig) IsTrustedSource(tag serial.StringLiteral) bool {
_, found := this.TrustedTags[tag]
return found
}

23
app/dns/config_json.go Normal file
View File

@@ -0,0 +1,23 @@
// +build json
package dns
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/serial"
)
func (this *CacheConfig) UnmarshalJSON(data []byte) error {
var strlist serial.StringLiteralList
if err := json.Unmarshal(data, strlist); err != nil {
return err
}
config := &CacheConfig{
TrustedTags: make(map[serial.StringLiteral]bool, strlist.Len()),
}
for _, str := range strlist {
config.TrustedTags[str.TrimSpace()] = true
}
return nil
}

62
app/dns/dns.go Normal file
View File

@@ -0,0 +1,62 @@
package dns
import (
"net"
"time"
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/common/collect"
"github.com/v2ray/v2ray-core/common/serial"
)
type entry struct {
domain string
ip net.IP
validUntil time.Time
}
func newEntry(domain string, ip net.IP) *entry {
this := &entry{
domain: domain,
ip: ip,
}
this.Extend()
return this
}
func (this *entry) IsValid() bool {
return this.validUntil.After(time.Now())
}
func (this *entry) Extend() {
this.validUntil = time.Now().Add(time.Hour)
}
type DnsCache struct {
cache *collect.ValidityMap
config *CacheConfig
}
func NewCache(config *CacheConfig) *DnsCache {
cache := &DnsCache{
cache: collect.NewValidityMap(3600),
config: config,
}
return cache
}
func (this *DnsCache) Add(context app.Context, domain string, ip net.IP) {
callerTag := context.CallerTag()
if !this.config.IsTrustedSource(serial.StringLiteral(callerTag)) {
return
}
this.cache.Set(serial.StringLiteral(domain), newEntry(domain, ip))
}
func (this *DnsCache) Get(context app.Context, domain string) net.IP {
if value := this.cache.Get(serial.StringLiteral(domain)); value != nil {
return value.(*entry).ip
}
return nil
}

33
app/dns/dns_test.go Normal file
View File

@@ -0,0 +1,33 @@
package dns_test
import (
"net"
"testing"
"github.com/v2ray/v2ray-core/app/dns"
apptesting "github.com/v2ray/v2ray-core/app/testing"
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
"github.com/v2ray/v2ray-core/common/serial"
v2testing "github.com/v2ray/v2ray-core/testing"
)
func TestDnsAdd(t *testing.T) {
v2testing.Current(t)
domain := "v2ray.com"
cache := dns.NewCache(&dns.CacheConfig{
TrustedTags: map[serial.StringLiteral]bool{
serial.StringLiteral("testtag"): true,
},
})
ip := cache.Get(&apptesting.Context{}, domain)
netassert.IP(ip).IsNil()
cache.Add(&apptesting.Context{CallerTagValue: "notvalidtag"}, domain, []byte{1, 2, 3, 4})
ip = cache.Get(&apptesting.Context{}, domain)
netassert.IP(ip).IsNil()
cache.Add(&apptesting.Context{CallerTagValue: "testtag"}, domain, []byte{1, 2, 3, 4})
ip = cache.Get(&apptesting.Context{}, domain)
netassert.IP(ip).Equals(net.IP([]byte{1, 2, 3, 4}))
}

9
app/handlers.go Normal file
View File

@@ -0,0 +1,9 @@
package app
import (
"github.com/v2ray/v2ray-core/proxy"
)
type InboundHandlerManager interface {
GetHandler(tag string) (proxy.InboundConnectionHandler, int)
}

9
app/internal/context.go Normal file
View File

@@ -0,0 +1,9 @@
package internal
type contextImpl struct {
callerTag string
}
func (this *contextImpl) CallerTag() string {
return this.callerTag
}

25
app/internal/dns.go Normal file
View File

@@ -0,0 +1,25 @@
package internal
import (
"net"
"github.com/v2ray/v2ray-core/app"
)
type DnsCacheWithContext interface {
Get(context app.Context, domain string) net.IP
Add(contaxt app.Context, domain string, ip net.IP)
}
type contextedDnsCache struct {
context app.Context
dnsCache DnsCacheWithContext
}
func (this *contextedDnsCache) Get(domain string) net.IP {
return this.dnsCache.Get(this.context, domain)
}
func (this *contextedDnsCache) Add(domain string, ip net.IP) {
this.dnsCache.Add(this.context, domain, ip)
}

19
app/internal/handlers.go Normal file
View File

@@ -0,0 +1,19 @@
package internal
import (
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/proxy"
)
type InboundHandlerManagerWithContext interface {
GetHandler(context app.Context, tag string) (proxy.InboundConnectionHandler, int)
}
type inboundHandlerManagerWithContext struct {
context app.Context
manager InboundHandlerManagerWithContext
}
func (this *inboundHandlerManagerWithContext) GetHandler(tag string) (proxy.InboundConnectionHandler, int) {
return this.manager.GetHandler(this.context, tag)
}

View File

@@ -0,0 +1,20 @@
package internal
import (
"github.com/v2ray/v2ray-core/app"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/transport/ray"
)
type PacketDispatcherWithContext interface {
DispatchToOutbound(context app.Context, packet v2net.Packet) ray.InboundRay
}
type contextedPacketDispatcher struct {
context app.Context
packetDispatcher PacketDispatcherWithContext
}
func (this *contextedPacketDispatcher) DispatchToOutbound(packet v2net.Packet) ray.InboundRay {
return this.packetDispatcher.DispatchToOutbound(this.context, packet)
}

23
app/internal/pubsub.go Normal file
View File

@@ -0,0 +1,23 @@
package internal
import (
"github.com/v2ray/v2ray-core/app"
)
type PubsubWithContext interface {
Publish(context app.Context, topic string, message app.PubsubMessage)
Subscribe(context app.Context, topic string, handler app.TopicHandler)
}
type contextedPubsub struct {
context app.Context
pubsub PubsubWithContext
}
func (this *contextedPubsub) Publish(topic string, message app.PubsubMessage) {
this.pubsub.Publish(this.context, topic, message)
}
func (this *contextedPubsub) Subscribe(topic string, handler app.TopicHandler) {
this.pubsub.Subscribe(this.context, topic, handler)
}

75
app/internal/space.go Normal file
View File

@@ -0,0 +1,75 @@
package internal
import (
"github.com/v2ray/v2ray-core/app"
)
type Space struct {
packetDispatcher PacketDispatcherWithContext
dnsCache DnsCacheWithContext
pubsub PubsubWithContext
inboundHandlerManager InboundHandlerManagerWithContext
tag string
}
func NewSpace(tag string, packetDispatcher PacketDispatcherWithContext, dnsCache DnsCacheWithContext, pubsub PubsubWithContext, inboundHandlerManager InboundHandlerManagerWithContext) *Space {
return &Space{
tag: tag,
packetDispatcher: packetDispatcher,
dnsCache: dnsCache,
pubsub: pubsub,
inboundHandlerManager: inboundHandlerManager,
}
}
func (this *Space) HasPacketDispatcher() bool {
return this.packetDispatcher != nil
}
func (this *Space) PacketDispatcher() app.PacketDispatcher {
return &contextedPacketDispatcher{
packetDispatcher: this.packetDispatcher,
context: &contextImpl{
callerTag: this.tag,
},
}
}
func (this *Space) HasDnsCache() bool {
return this.dnsCache != nil
}
func (this *Space) DnsCache() app.DnsCache {
return &contextedDnsCache{
dnsCache: this.dnsCache,
context: &contextImpl{
callerTag: this.tag,
},
}
}
func (this *Space) HasPubsub() bool {
return this.pubsub != nil
}
func (this *Space) Pubsub() app.Pubsub {
return &contextedPubsub{
pubsub: this.pubsub,
context: &contextImpl{
callerTag: this.tag,
},
}
}
func (this *Space) HasInboundHandlerManager() bool {
return this.inboundHandlerManager != nil
}
func (this *Space) InboundHandlerManager() app.InboundHandlerManager {
return &inboundHandlerManagerWithContext{
manager: this.inboundHandlerManager,
context: &contextImpl{
callerTag: this.tag,
},
}
}

View File

@@ -1,39 +0,0 @@
package config
import (
routerconfig "github.com/v2ray/v2ray-core/app/router/config"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type DetourTag string
type ConnectionConfig interface {
Protocol() string
Settings() interface{}
}
type LogConfig interface {
AccessLog() string
}
type InboundDetourConfig interface {
Protocol() string
PortRange() v2net.PortRange
Settings() interface{}
}
type OutboundDetourConfig interface {
Protocol() string
Tag() DetourTag
Settings() interface{}
}
type PointConfig interface {
Port() uint16
LogConfig() LogConfig
RouterConfig() routerconfig.RouterConfig
InboundConfig() ConnectionConfig
OutboundConfig() ConnectionConfig
InboundDetours() []InboundDetourConfig
OutboundDetours() []OutboundDetourConfig
}

View File

@@ -1,36 +0,0 @@
package json
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/log"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
proxyjson "github.com/v2ray/v2ray-core/proxy/common/config/json"
)
type ConnectionConfig struct {
ProtocolString string `json:"protocol"`
SettingsMessage json.RawMessage `json:"settings"`
Type proxyconfig.Type `json:"-"`
}
func (c *ConnectionConfig) Protocol() string {
return c.ProtocolString
}
func (c *ConnectionConfig) Settings() interface{} {
return loadConnectionConfig(c.SettingsMessage, c.Protocol(), c.Type)
}
func loadConnectionConfig(message json.RawMessage, protocol string, cType proxyconfig.Type) interface{} {
configObj := proxyjson.CreateConfig(protocol, cType)
if configObj == nil {
panic("Unknown protocol " + protocol)
}
err := json.Unmarshal(message, configObj)
if err != nil {
log.Error("Unable to parse connection config: %v", err)
panic("Failed to parse connection config.")
}
return configObj
}

View File

@@ -1,27 +0,0 @@
package json
import (
"encoding/json"
v2net "github.com/v2ray/v2ray-core/common/net"
v2netjson "github.com/v2ray/v2ray-core/common/net/json"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
)
type InboundDetourConfig struct {
ProtocolValue string `json:"protocol"`
PortRangeValue *v2netjson.PortRange `json:"port"`
SettingsValue json.RawMessage `json:"settings"`
}
func (this *InboundDetourConfig) Protocol() string {
return this.ProtocolValue
}
func (this *InboundDetourConfig) PortRange() v2net.PortRange {
return this.PortRangeValue
}
func (this *InboundDetourConfig) Settings() interface{} {
return loadConnectionConfig(this.SettingsValue, this.ProtocolValue, proxyconfig.TypeInbound)
}

View File

@@ -1,93 +0,0 @@
package json
import (
"encoding/json"
"io/ioutil"
"os"
"github.com/v2ray/v2ray-core/app/point/config"
routerconfig "github.com/v2ray/v2ray-core/app/router/config"
routerconfigjson "github.com/v2ray/v2ray-core/app/router/config/json"
"github.com/v2ray/v2ray-core/common/log"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
)
// Config is the config for Point server.
type Config struct {
PortValue uint16 `json:"port"` // Port of this Point server.
LogConfigValue *LogConfig `json:"log"`
RouterConfigValue *routerconfigjson.RouterConfig `json:"routing"`
InboundConfigValue *ConnectionConfig `json:"inbound"`
OutboundConfigValue *ConnectionConfig `json:"outbound"`
InboundDetoursValue []*InboundDetourConfig `json:"inboundDetour"`
OutboundDetoursValue []*OutboundDetourConfig `json:"outboundDetour"`
}
func (config *Config) Port() uint16 {
return config.PortValue
}
func (config *Config) LogConfig() config.LogConfig {
if config.LogConfigValue == nil {
return nil
}
return config.LogConfigValue
}
func (this *Config) RouterConfig() routerconfig.RouterConfig {
if this.RouterConfigValue == nil {
return nil
}
return this.RouterConfigValue
}
func (config *Config) InboundConfig() config.ConnectionConfig {
if config.InboundConfigValue == nil {
return nil
}
return config.InboundConfigValue
}
func (config *Config) OutboundConfig() config.ConnectionConfig {
if config.OutboundConfigValue == nil {
return nil
}
return config.OutboundConfigValue
}
func (this *Config) InboundDetours() []config.InboundDetourConfig {
detours := make([]config.InboundDetourConfig, len(this.InboundDetoursValue))
for idx, detour := range this.InboundDetoursValue {
detours[idx] = detour
}
return detours
}
func (this *Config) OutboundDetours() []config.OutboundDetourConfig {
detours := make([]config.OutboundDetourConfig, len(this.OutboundDetoursValue))
for idx, detour := range this.OutboundDetoursValue {
detours[idx] = detour
}
return detours
}
func LoadConfig(file string) (*Config, error) {
fixedFile := os.ExpandEnv(file)
rawConfig, err := ioutil.ReadFile(fixedFile)
if err != nil {
log.Error("Failed to read server config file (%s): %v", file, err)
return nil, err
}
jsonConfig := &Config{}
err = json.Unmarshal(rawConfig, jsonConfig)
if err != nil {
log.Error("Failed to load server config: %v", err)
return nil, err
}
jsonConfig.InboundConfigValue.Type = proxyconfig.TypeInbound
jsonConfig.OutboundConfigValue.Type = proxyconfig.TypeOutbound
return jsonConfig, err
}

View File

@@ -1,73 +0,0 @@
package json_test
import (
"path/filepath"
"testing"
"github.com/v2ray/v2ray-core/app/point/config/json"
_ "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json"
_ "github.com/v2ray/v2ray-core/proxy/freedom/config/json"
_ "github.com/v2ray/v2ray-core/proxy/socks/config/json"
_ "github.com/v2ray/v2ray-core/proxy/vmess/config/json"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestClientSampleConfig(t *testing.T) {
assert := unit.Assert(t)
// TODO: fix for Windows
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
pointConfig, err := json.LoadConfig(filepath.Join(baseDir, "vpoint_socks_vmess.json"))
assert.Error(err).IsNil()
assert.Uint16(pointConfig.Port()).Positive()
assert.Pointer(pointConfig.InboundConfig()).IsNotNil()
assert.Pointer(pointConfig.OutboundConfig()).IsNotNil()
assert.String(pointConfig.InboundConfig().Protocol()).Equals("socks")
assert.Pointer(pointConfig.InboundConfig().Settings()).IsNotNil()
assert.String(pointConfig.OutboundConfig().Protocol()).Equals("vmess")
assert.Pointer(pointConfig.OutboundConfig().Settings()).IsNotNil()
}
func TestServerSampleConfig(t *testing.T) {
assert := unit.Assert(t)
// TODO: fix for Windows
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
pointConfig, err := json.LoadConfig(filepath.Join(baseDir, "vpoint_vmess_freedom.json"))
assert.Error(err).IsNil()
assert.Uint16(pointConfig.Port()).Positive()
assert.Pointer(pointConfig.InboundConfig()).IsNotNil()
assert.Pointer(pointConfig.OutboundConfig()).IsNotNil()
assert.String(pointConfig.InboundConfig().Protocol()).Equals("vmess")
assert.Pointer(pointConfig.InboundConfig().Settings()).IsNotNil()
assert.String(pointConfig.OutboundConfig().Protocol()).Equals("freedom")
assert.Pointer(pointConfig.OutboundConfig().Settings()).IsNotNil()
}
func TestDetourConfig(t *testing.T) {
assert := unit.Assert(t)
// TODO: fix for Windows
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
pointConfig, err := json.LoadConfig(filepath.Join(baseDir, "vpoint_dns_detour.json"))
assert.Error(err).IsNil()
detours := pointConfig.InboundDetours()
assert.Int(len(detours)).Equals(1)
detour := detours[0]
assert.String(detour.Protocol()).Equals("dokodemo-door")
assert.Uint16(detour.PortRange().From()).Equals(uint16(28394))
assert.Uint16(detour.PortRange().To()).Equals(uint16(28394))
assert.Pointer(detour.Settings()).IsNotNil()
}

View File

@@ -1,9 +0,0 @@
package json
type LogConfig struct {
AccessLogValue string `json:"access"`
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}

View File

@@ -1,26 +0,0 @@
package json
import (
"encoding/json"
"github.com/v2ray/v2ray-core/app/point/config"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
)
type OutboundDetourConfig struct {
ProtocolValue string `json:"protocol"`
TagValue string `json:"tag"`
SettingsValue json.RawMessage `json:"settings"`
}
func (this *OutboundDetourConfig) Protocol() string {
return this.ProtocolValue
}
func (this *OutboundDetourConfig) Tag() config.DetourTag {
return config.DetourTag(this.TagValue)
}
func (this *OutboundDetourConfig) Settings() interface{} {
return loadConnectionConfig(this.SettingsValue, this.ProtocolValue, proxyconfig.TypeOutbound)
}

View File

@@ -1,105 +0,0 @@
package mocks
import (
"github.com/v2ray/v2ray-core/app/point/config"
routerconfig "github.com/v2ray/v2ray-core/app/router/config"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type ConnectionConfig struct {
ProtocolValue string
SettingsValue interface{}
}
func (config *ConnectionConfig) Protocol() string {
return config.ProtocolValue
}
func (config *ConnectionConfig) Settings() interface{} {
return config.SettingsValue
}
type LogConfig struct {
AccessLogValue string
}
type PortRange struct {
FromValue uint16
ToValue uint16
}
func (this *PortRange) From() uint16 {
return this.FromValue
}
func (this *PortRange) To() uint16 {
return this.ToValue
}
type InboundDetourConfig struct {
ConnectionConfig
PortRangeValue *PortRange
}
func (this *InboundDetourConfig) PortRange() v2net.PortRange {
return this.PortRangeValue
}
type OutboundDetourConfig struct {
ConnectionConfig
TagValue config.DetourTag
}
func (this *OutboundDetourConfig) Tag() config.DetourTag {
return this.TagValue
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}
type Config struct {
PortValue uint16
LogConfigValue *LogConfig
RouterConfigValue routerconfig.RouterConfig
InboundConfigValue *ConnectionConfig
OutboundConfigValue *ConnectionConfig
InboundDetoursValue []*InboundDetourConfig
OutboundDetoursValue []*OutboundDetourConfig
}
func (config *Config) Port() uint16 {
return config.PortValue
}
func (config *Config) LogConfig() config.LogConfig {
return config.LogConfigValue
}
func (this *Config) RouterConfig() routerconfig.RouterConfig {
return this.RouterConfigValue
}
func (config *Config) InboundConfig() config.ConnectionConfig {
return config.InboundConfigValue
}
func (config *Config) OutboundConfig() config.ConnectionConfig {
return config.OutboundConfigValue
}
func (this *Config) InboundDetours() []config.InboundDetourConfig {
detours := make([]config.InboundDetourConfig, len(this.InboundDetoursValue))
for idx, detour := range this.InboundDetoursValue {
detours[idx] = detour
}
return detours
}
func (this *Config) OutboundDetours() []config.OutboundDetourConfig {
detours := make([]config.OutboundDetourConfig, len(this.OutboundDetoursValue))
for idx, detour := range this.OutboundDetoursValue {
detours[idx] = detour
}
return detours
}

View File

@@ -1,56 +0,0 @@
package point
import (
"github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/common/log"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
)
type InboundConnectionHandlerWithPort struct {
port uint16
handler connhandler.InboundConnectionHandler
}
type InboundDetourHandler struct {
point *Point
config config.InboundDetourConfig
ich []*InboundConnectionHandlerWithPort
}
func (this *InboundDetourHandler) Initialize() error {
ichFactory := connhandler.GetInboundConnectionHandlerFactory(this.config.Protocol())
if ichFactory == nil {
log.Error("Unknown inbound connection handler factory %s", this.config.Protocol())
return config.BadConfiguration
}
ports := this.config.PortRange()
this.ich = make([]*InboundConnectionHandlerWithPort, 0, ports.From()-ports.To()+1)
for i := ports.From(); i <= ports.To(); i++ {
ichConfig := this.config.Settings()
ich, err := ichFactory.Create(this.point, ichConfig)
if err != nil {
log.Error("Failed to create inbound connection handler: %v", err)
return err
}
this.ich = append(this.ich, &InboundConnectionHandlerWithPort{
port: i,
handler: ich,
})
}
return nil
}
func (this *InboundDetourHandler) Start() error {
for _, ich := range this.ich {
return retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
err := ich.handler.Listen(ich.port)
if err != nil {
return err
}
return nil
})
}
return nil
}

View File

@@ -1,149 +0,0 @@
package point
import (
"github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/app/router"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/transport/ray"
)
// Point is an single server in V2Ray system.
type Point struct {
port uint16
ich connhandler.InboundConnectionHandler
och connhandler.OutboundConnectionHandler
idh []*InboundDetourHandler
odh map[config.DetourTag]connhandler.OutboundConnectionHandler
router router.Router
}
// NewPoint returns a new Point server based on given configuration.
// The server is not started at this point.
func NewPoint(pConfig config.PointConfig) (*Point, error) {
var vpoint = new(Point)
vpoint.port = pConfig.Port()
ichFactory := connhandler.GetInboundConnectionHandlerFactory(pConfig.InboundConfig().Protocol())
if ichFactory == nil {
log.Error("Unknown inbound connection handler factory %s", pConfig.InboundConfig().Protocol())
return nil, config.BadConfiguration
}
ichConfig := pConfig.InboundConfig().Settings()
ich, err := ichFactory.Create(vpoint, ichConfig)
if err != nil {
log.Error("Failed to create inbound connection handler: %v", err)
return nil, err
}
vpoint.ich = ich
ochFactory := connhandler.GetOutboundConnectionHandlerFactory(pConfig.OutboundConfig().Protocol())
if ochFactory == nil {
log.Error("Unknown outbound connection handler factory %s", pConfig.OutboundConfig().Protocol())
return nil, config.BadConfiguration
}
ochConfig := pConfig.OutboundConfig().Settings()
och, err := ochFactory.Create(ochConfig)
if err != nil {
log.Error("Failed to create outbound connection handler: %v", err)
return nil, err
}
vpoint.och = och
detours := pConfig.InboundDetours()
if len(detours) > 0 {
vpoint.idh = make([]*InboundDetourHandler, len(detours))
for idx, detourConfig := range detours {
detourHandler := &InboundDetourHandler{
point: vpoint,
config: detourConfig,
}
err := detourHandler.Initialize()
if err != nil {
return nil, err
}
vpoint.idh[idx] = detourHandler
}
}
outboundDetours := pConfig.OutboundDetours()
if len(outboundDetours) > 0 {
vpoint.odh = make(map[config.DetourTag]connhandler.OutboundConnectionHandler)
for _, detourConfig := range outboundDetours {
detourFactory := connhandler.GetOutboundConnectionHandlerFactory(detourConfig.Protocol())
if detourFactory == nil {
log.Error("Unknown detour outbound connection handler factory %s", detourConfig.Protocol())
return nil, config.BadConfiguration
}
detourHandler, err := detourFactory.Create(detourConfig.Settings())
if err != nil {
log.Error("Failed to create detour outbound connection handler: %v", err)
return nil, err
}
vpoint.odh[detourConfig.Tag()] = detourHandler
}
}
routerConfig := pConfig.RouterConfig()
if routerConfig != nil {
r, err := router.CreateRouter(routerConfig.Strategy(), routerConfig.Settings())
if err != nil {
log.Error("Failed to create router: %v", err)
return nil, config.BadConfiguration
}
vpoint.router = r
}
return vpoint, nil
}
// Start starts the Point server, and return any error during the process.
// In the case of any errors, the state of the server is unpredicatable.
func (vp *Point) Start() error {
if vp.port <= 0 {
log.Error("Invalid port %d", vp.port)
return config.BadConfiguration
}
err := retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
err := vp.ich.Listen(vp.port)
if err != nil {
return err
}
log.Warning("Point server started on port %d", vp.port)
return nil
})
if err != nil {
return err
}
for _, detourHandler := range vp.idh {
err := detourHandler.Start()
if err != nil {
return err
}
}
return nil
}
func (p *Point) DispatchToOutbound(packet v2net.Packet) ray.InboundRay {
direct := ray.NewRay()
dest := packet.Destination()
if p.router != nil {
tag, err := p.router.TakeDetour(dest)
if err == nil {
handler, found := p.odh[tag]
if found {
go handler.Dispatch(packet, direct)
return direct
}
}
}
go p.och.Dispatch(packet, direct)
return direct
}

9
app/pubsub.go Normal file
View File

@@ -0,0 +1,9 @@
package app
type PubsubMessage []byte
type TopicHandler func(PubsubMessage)
type Pubsub interface {
Publish(topic string, message PubsubMessage)
Subscribe(topic string, handler TopicHandler)
}

64
app/pubsub/pubsub.go Normal file
View File

@@ -0,0 +1,64 @@
package pubsub
import (
"sync"
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/app/internal"
)
type TopicHandlerList struct {
sync.RWMutex
handlers []app.TopicHandler
}
func NewTopicHandlerList(handlers ...app.TopicHandler) *TopicHandlerList {
return &TopicHandlerList{
handlers: handlers,
}
}
func (this *TopicHandlerList) Add(handler app.TopicHandler) {
this.Lock()
this.handlers = append(this.handlers, handler)
this.Unlock()
}
func (this *TopicHandlerList) Dispatch(message app.PubsubMessage) {
this.RLock()
for _, handler := range this.handlers {
go handler(message)
}
this.RUnlock()
}
type Pubsub struct {
topics map[string]*TopicHandlerList
sync.RWMutex
}
func New() internal.PubsubWithContext {
return &Pubsub{
topics: make(map[string]*TopicHandlerList),
}
}
func (this *Pubsub) Publish(context app.Context, topic string, message app.PubsubMessage) {
this.RLock()
list, found := this.topics[topic]
this.RUnlock()
if found {
list.Dispatch(message)
}
}
func (this *Pubsub) Subscribe(context app.Context, topic string, handler app.TopicHandler) {
this.Lock()
defer this.Unlock()
if list, found := this.topics[topic]; found {
list.Add(handler)
} else {
this.topics[topic] = NewTopicHandlerList(handler)
}
}

38
app/pubsub/pubsub_test.go Normal file
View File

@@ -0,0 +1,38 @@
package pubsub_test
import (
"testing"
"time"
"github.com/v2ray/v2ray-core/app"
. "github.com/v2ray/v2ray-core/app/pubsub"
apptesting "github.com/v2ray/v2ray-core/app/testing"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestPubsub(t *testing.T) {
v2testing.Current(t)
messages := make(map[string]app.PubsubMessage)
pubsub := New()
pubsub.Subscribe(&apptesting.Context{}, "t1", func(message app.PubsubMessage) {
messages["t1"] = message
})
pubsub.Subscribe(&apptesting.Context{}, "t2", func(message app.PubsubMessage) {
messages["t2"] = message
})
message := app.PubsubMessage([]byte("This is a pubsub message."))
pubsub.Publish(&apptesting.Context{}, "t2", message)
<-time.Tick(time.Second)
_, found := messages["t1"]
assert.Bool(found).IsFalse()
actualMessage, found := messages["t2"]
assert.Bool(found).IsTrue()
assert.StringLiteral(string(actualMessage)).Equals(string(message))
}

6
app/router/config.go Normal file
View File

@@ -0,0 +1,6 @@
package router
type Config struct {
Strategy string
Settings interface{}
}

View File

@@ -1,6 +0,0 @@
package config
type RouterConfig interface {
Strategy() string
Settings() interface{}
}

View File

@@ -1,26 +0,0 @@
package json
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/log"
)
type RouterConfig struct {
StrategyValue string `json:"strategy"`
SettingsValue json.RawMessage `json:"settings"`
}
func (this *RouterConfig) Strategy() string {
return this.StrategyValue
}
func (this *RouterConfig) Settings() interface{} {
settings := CreateRouterConfig(this.Strategy())
err := json.Unmarshal(this.SettingsValue, settings)
if err != nil {
log.Error("Failed to load router settings: %v", err)
return nil
}
return settings
}

View File

@@ -1,9 +1,15 @@
package json
package router
type ConfigObjectCreator func() interface{}
import (
"errors"
)
type ConfigObjectCreator func([]byte) (interface{}, error)
var (
configCache map[string]ConfigObjectCreator
ErrorRouterNotFound = errors.New("Router not found.")
)
func RegisterRouterConfig(strategy string, creator ConfigObjectCreator) error {
@@ -12,12 +18,12 @@ func RegisterRouterConfig(strategy string, creator ConfigObjectCreator) error {
return nil
}
func CreateRouterConfig(strategy string) interface{} {
func CreateRouterConfig(strategy string, data []byte) (interface{}, error) {
creator, found := configCache[strategy]
if !found {
return nil
return nil, ErrorRouterNotFound
}
return creator()
return creator(data)
}
func init() {

28
app/router/config_json.go Normal file
View File

@@ -0,0 +1,28 @@
// +build json
package router
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/log"
)
func (this *Config) UnmarshalJSON(data []byte) error {
type JsonConfig struct {
Strategy string `json:"strategy"`
Settings json.RawMessage `json:"settings"`
}
jsonConfig := new(JsonConfig)
if err := json.Unmarshal(data, jsonConfig); err != nil {
return err
}
settings, err := CreateRouterConfig(jsonConfig.Strategy, []byte(jsonConfig.Settings))
if err != nil {
log.Error("Router: Failed to load router settings: %v", err)
return err
}
this.Strategy = jsonConfig.Strategy
this.Settings = settings
return nil
}

View File

@@ -3,7 +3,6 @@ package router
import (
"errors"
"github.com/v2ray/v2ray-core/app/point/config"
v2net "github.com/v2ray/v2ray-core/common/net"
)
@@ -12,7 +11,7 @@ var (
)
type Router interface {
TakeDetour(v2net.Destination) (config.DetourTag, error)
TakeDetour(v2net.Destination) (string, error)
}
type RouterFactory interface {

31
app/router/router_test.go Normal file
View File

@@ -0,0 +1,31 @@
package router_test
import (
"net"
"path/filepath"
"testing"
. "github.com/v2ray/v2ray-core/app/router"
_ "github.com/v2ray/v2ray-core/app/router/rules"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/shell/point"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestRouter(t *testing.T) {
v2testing.Current(t)
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
pointConfig, err := point.LoadConfig(filepath.Join(baseDir, "vpoint_socks_vmess.json"))
assert.Error(err).IsNil()
router, err := CreateRouter(pointConfig.RouterConfig.Strategy, pointConfig.RouterConfig.Settings)
assert.Error(err).IsNil()
dest := v2net.TCPDestination(v2net.IPAddress(net.ParseIP("120.135.126.1")), 80)
tag, err := router.TakeDetour(dest)
assert.Error(err).IsNil()
assert.StringLiteral(tag).Equals("direct")
}

6356
app/router/rules/chinaip.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
package rules
import (
"net"
"testing"
v2net "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func makeDestination(ip string) v2net.Destination {
return v2net.TCPDestination(v2net.IPAddress(net.ParseIP(ip)), 80)
}
func TestChinaIP(t *testing.T) {
v2testing.Current(t)
rule := NewIPv4Matcher(chinaIPNet)
assert.Bool(rule.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
assert.Bool(rule.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com
assert.Bool(rule.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com
assert.Bool(rule.Apply(makeDestination("120.135.126.1"))).IsTrue()
assert.Bool(rule.Apply(makeDestination("8.8.8.8"))).IsFalse()
}

View File

@@ -0,0 +1,371 @@
// +build json
package rules
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type ChinaSitesCondition struct {
}
func (this *ChinaSitesCondition) Apply(dest v2net.Destination) bool {
for _, cond := range chinaSitesConds {
if cond.Apply(dest) {
return true
}
}
return false
}
func parseChinaSitesRule(data []byte) (*Rule, error) {
rawRule := new(JsonRule)
err := json.Unmarshal(data, rawRule)
if err != nil {
log.Error("Router: Invalid router rule: %v", err)
return nil, err
}
return &Rule{
Tag: rawRule.OutboundTag,
Condition: &ChinaSitesCondition{},
}, nil
}
const (
anySubDomain = "^(.*\\.)?"
dotAm = "\\.am$"
dotCc = "\\.cc$"
dotCn = "\\.cn$"
dotCom = "\\.com$"
dotIo = "\\.io$"
dotLa = "\\.la$"
dotMe = "\\.me$"
dotNet = "\\.net$"
dotOrg = "\\.org$"
dotTv = "\\.tv$"
)
var (
chinaSitesConds []Condition
)
func init() {
regexpDomains := []string{
dotCn,
"\\.xn--fiqs8s$", /* .中国 */
anySubDomain + "10010" + dotCom,
anySubDomain + "115" + dotCom,
anySubDomain + "123juzi" + dotCom,
anySubDomain + "123juzi" + dotNet,
anySubDomain + "123u" + dotCom,
anySubDomain + "126" + dotCom,
anySubDomain + "126" + dotNet,
anySubDomain + "127" + dotNet,
anySubDomain + "163" + dotCom,
anySubDomain + "17173" + dotCom,
anySubDomain + "17cdn" + dotCom,
anySubDomain + "1905" + dotCom,
anySubDomain + "21cn" + dotCom,
anySubDomain + "2288" + dotOrg,
anySubDomain + "2345" + dotCom,
anySubDomain + "3322" + dotOrg,
anySubDomain + "35" + dotCom,
anySubDomain + "360doc" + dotCom,
anySubDomain + "360buy" + dotCom,
anySubDomain + "360buyimg" + dotCom,
anySubDomain + "360safe" + dotCom,
anySubDomain + "36kr" + dotCom,
anySubDomain + "39" + dotNet,
anySubDomain + "3dmgame" + dotCom,
anySubDomain + "4399" + dotCom,
anySubDomain + "50bang" + dotOrg,
anySubDomain + "51" + dotLa,
anySubDomain + "51cto" + dotCom,
anySubDomain + "51job" + dotCom,
anySubDomain + "51jobcdn" + dotCom,
anySubDomain + "55bbs" + dotCom,
anySubDomain + "58" + dotCom,
anySubDomain + "71" + dotAm,
anySubDomain + "7k7k" + dotCom,
anySubDomain + "9718" + dotCom,
anySubDomain + "abchina" + dotCom,
anySubDomain + "acfun" + dotTv,
anySubDomain + "aicdn" + dotCom,
anySubDomain + "alibaba" + dotCom,
anySubDomain + "alicdn" + dotCom,
anySubDomain + "aliimg.com" + dotCom,
anySubDomain + "alipay" + dotCom,
anySubDomain + "alipayobjects" + dotCom,
anySubDomain + "aliyun" + dotCom,
anySubDomain + "aliyuncdn" + dotCom,
anySubDomain + "aliyuncs" + dotCom,
anySubDomain + "allyes" + dotCom,
anySubDomain + "amap" + dotCom,
anySubDomain + "anjuke" + dotCom,
anySubDomain + "anquan" + dotOrg,
anySubDomain + "appinn" + dotCom,
anySubDomain + "babytree" + dotCom,
anySubDomain + "baidu" + dotCom,
anySubDomain + "baiducontent" + dotCom,
anySubDomain + "baidustatic" + dotCom,
anySubDomain + "baifendian" + dotCom,
anySubDomain + "baifubao" + dotCom,
anySubDomain + "baihe" + dotCom,
anySubDomain + "baike" + dotCom,
anySubDomain + "baixing" + dotCom,
anySubDomain + "baixing" + dotNet,
anySubDomain + "bankcomm" + dotCom,
anySubDomain + "bankofchina" + dotCom,
anySubDomain + "bcy" + dotNet,
anySubDomain + "bdimg" + dotCom,
anySubDomain + "bdstatic" + dotCom,
anySubDomain + "bilibili" + dotCom,
anySubDomain + "bitauto" + dotCom,
anySubDomain + "bitautoimg" + dotCom,
anySubDomain + "bobo" + dotCom,
anySubDomain + "btcfans" + dotCom,
anySubDomain + "ccb" + dotCom,
anySubDomain + "cctv" + dotCom,
anySubDomain + "cctvpic" + dotCom,
anySubDomain + "cdn20" + dotCom,
anySubDomain + "ch" + dotCom,
anySubDomain + "che168" + dotCom,
anySubDomain + "china" + dotCom,
anySubDomain + "chinacache" + dotCom,
anySubDomain + "chinacache" + dotNet,
anySubDomain + "chinahr" + dotCom,
anySubDomain + "chinamobile" + dotCom,
anySubDomain + "chinaz" + dotCom,
anySubDomain + "chouti" + dotCom,
anySubDomain + "chuangxin" + dotCom,
anySubDomain + "clouddn" + dotCom,
anySubDomain + "cloudxns" + dotCom,
anySubDomain + "cmbchina" + dotCom,
anySubDomain + "cnbeta" + dotCom,
anySubDomain + "cnbetacdn" + dotCom,
anySubDomain + "cnblogs" + dotCom,
anySubDomain + "cnepub" + dotCom,
anySubDomain + "cnzz" + dotCom,
anySubDomain + "coding" + dotNet,
anySubDomain + "csbew" + dotCom,
anySubDomain + "csdn" + dotNet,
anySubDomain + "ctrip" + dotCom,
anySubDomain + "cubead" + dotCom,
anySubDomain + "dangdang" + dotCom,
anySubDomain + "daocloud" + dotIo,
anySubDomain + "dedecms" + dotCom,
anySubDomain + "diandian" + dotCom,
anySubDomain + "dianping" + dotCom,
anySubDomain + "docin" + dotCom,
anySubDomain + "donews" + dotCom,
anySubDomain + "douban" + dotCom,
anySubDomain + "doubanio" + dotCom,
anySubDomain + "dpfile" + dotCom,
anySubDomain + "duoshuo" + dotCom,
anySubDomain + "duowan" + dotCom,
anySubDomain + "eastday" + dotCom,
anySubDomain + "ecitic" + dotCom,
anySubDomain + "emarbox" + dotCom,
anySubDomain + "etao" + dotCom,
anySubDomain + "fanli" + dotCom,
anySubDomain + "fengniao" + dotCom,
anySubDomain + "fhldns" + dotCom,
anySubDomain + "geekpark" + dotNet,
anySubDomain + "getui" + dotCom,
anySubDomain + "hao123" + dotCom,
anySubDomain + "hao123img" + dotCom,
anySubDomain + "haosou" + dotCom,
anySubDomain + "hdslb" + dotCom,
anySubDomain + "hexun" + dotCom,
anySubDomain + "hichina" + dotCom,
anySubDomain + "huanqiu" + dotCom,
anySubDomain + "hunantv" + dotCom,
anySubDomain + "huochepiao" + dotCom,
anySubDomain + "hupu" + dotCom,
anySubDomain + "huxiu" + dotCom,
anySubDomain + "iask" + dotCom,
anySubDomain + "iciba" + dotCom,
anySubDomain + "idqqimg" + dotCom,
anySubDomain + "ifanr" + dotCom,
anySubDomain + "ijinshan" + dotCom,
anySubDomain + "imedao" + dotCom,
anySubDomain + "imgo" + dotTv,
anySubDomain + "ipinyou" + dotCom,
anySubDomain + "ipip" + dotNet,
anySubDomain + "iqiyi" + dotCom,
anySubDomain + "it168" + dotCom,
anySubDomain + "itjuzi" + dotCom,
anySubDomain + "jandan" + dotNet,
anySubDomain + "jd" + dotCom,
anySubDomain + "jb51" + dotCom,
anySubDomain + "jia" + dotCom,
anySubDomain + "jianshu" + dotCom,
anySubDomain + "jiasuhui" + dotCom,
anySubDomain + "jiayuan" + dotCom,
anySubDomain + "jisuanke" + dotCom,
anySubDomain + "jstv" + dotCom,
anySubDomain + "jyimg" + dotCom,
anySubDomain + "kaixin001" + dotCom,
anySubDomain + "kanimg" + dotCom,
anySubDomain + "kankanews" + dotCom,
anySubDomain + "kf5" + dotCom,
anySubDomain + "kimiss" + dotCom,
anySubDomain + "kouclo" + dotCom,
anySubDomain + "koudai8" + dotCom,
anySubDomain + "ku6" + dotCom,
anySubDomain + "ku6cdn" + dotCom,
anySubDomain + "ku6img" + dotCom,
anySubDomain + "lady8844" + dotCom,
anySubDomain + "leiphone" + dotCom,
anySubDomain + "leju" + dotCom,
anySubDomain + "leturich" + dotOrg,
anySubDomain + "letv" + dotCom,
anySubDomain + "letvcdn" + dotCom,
anySubDomain + "letvimg" + dotCom,
anySubDomain + "lietou" + dotCom,
anySubDomain + "linkvans" + dotCom,
anySubDomain + "lightonus" + dotCom,
anySubDomain + "lofter" + dotCom,
anySubDomain + "lu" + dotCom,
anySubDomain + "lufax" + dotCom,
anySubDomain + "lufaxcdn" + dotCom,
anySubDomain + "lvmama" + dotCom,
anySubDomain + "lxdns" + dotCom,
anySubDomain + "ly" + dotCom,
anySubDomain + "mechina" + dotOrg,
anySubDomain + "mediav" + dotCom,
anySubDomain + "meika360" + dotCom,
anySubDomain + "meilishuo" + dotCom,
anySubDomain + "meishij" + dotNet,
anySubDomain + "meituan" + dotCom,
anySubDomain + "meizu" + dotCom,
anySubDomain + "mgtv" + dotCom,
anySubDomain + "mi" + dotCom,
anySubDomain + "miaozhen" + dotCom,
anySubDomain + "mmstat" + dotCom,
anySubDomain + "mop" + dotCom,
anySubDomain + "mydrivers" + dotCom,
anySubDomain + "netease" + dotCom,
anySubDomain + "ngacn" + dotCc,
anySubDomain + "ntalker" + dotCom,
anySubDomain + "oeeee" + dotCom,
anySubDomain + "ol-img" + dotCom,
anySubDomain + "onlinesjtu" + dotCom,
anySubDomain + "oschina" + dotNet,
anySubDomain + "paipai" + dotCom,
anySubDomain + "pchome" + dotNet,
anySubDomain + "pingan" + dotCom,
anySubDomain + "pingplusplus" + dotCom,
anySubDomain + "pps" + dotTv,
anySubDomain + "pubyun" + dotCom,
anySubDomain + "qbox" + dotMe,
anySubDomain + "qhimg" + dotCom,
anySubDomain + "qidian" + dotCom,
anySubDomain + "qingcloud" + dotCom,
anySubDomain + "qiniu" + dotCom,
anySubDomain + "qiniudn" + dotCom,
anySubDomain + "qiniudns" + dotCom,
anySubDomain + "qiyi" + dotCom,
anySubDomain + "qiyipic" + dotCom,
anySubDomain + "qtmojo" + dotCom,
anySubDomain + "qq" + dotCom,
anySubDomain + "qqmail" + dotCom,
anySubDomain + "qunar" + dotCom,
anySubDomain + "qunarzz" + dotCom,
anySubDomain + "qzone" + dotCom,
anySubDomain + "renren" + dotCom,
anySubDomain + "ruby-china" + dotOrg,
anySubDomain + "sandai" + dotNet,
anySubDomain + "sanguosha" + dotCom,
anySubDomain + "sanwen" + dotNet,
anySubDomain + "segmentfault" + dotCom,
anySubDomain + "shutcm" + dotCom,
anySubDomain + "sina" + dotCom,
anySubDomain + "sinaapp" + dotCom,
anySubDomain + "sinaedge" + dotCom,
anySubDomain + "sinaimg" + dotCom,
anySubDomain + "sinajs" + dotCom,
anySubDomain + "szzfgjj" + dotCom,
anySubDomain + "smzdm" + dotCom,
anySubDomain + "sohu" + dotCom,
anySubDomain + "sogou" + dotCom,
anySubDomain + "soso" + dotCom,
anySubDomain + "sspai" + dotCom,
anySubDomain + "staticfile" + dotOrg,
anySubDomain + "stockstar" + dotCom,
anySubDomain + "suning" + dotCom,
anySubDomain + "t1y5" + dotCom,
anySubDomain + "tanx" + dotCom,
anySubDomain + "tao123" + dotCom,
anySubDomain + "taobao" + dotCom,
anySubDomain + "taobaocdn" + dotCom,
anySubDomain + "tencent" + dotCom,
anySubDomain + "tenpay" + dotCom,
anySubDomain + "tiexue" + dotNet,
anySubDomain + "tmall" + dotCom,
anySubDomain + "tmcdn" + dotNet,
anySubDomain + "tudou" + dotCom,
anySubDomain + "tudouui" + dotCom,
anySubDomain + "tuicool" + dotCom,
anySubDomain + "u17" + dotCom,
anySubDomain + "unionpay" + dotCom,
anySubDomain + "unionpaysecure" + dotCom,
anySubDomain + "upyun" + dotCom,
anySubDomain + "upaiyun" + dotCom,
anySubDomain + "v2ex" + dotCom,
anySubDomain + "vamaker" + dotCom,
anySubDomain + "vancl" + dotCom,
anySubDomain + "vip" + dotCom,
anySubDomain + "wandoujia" + dotCom,
anySubDomain + "wdjimg" + dotCom,
anySubDomain + "webterren" + dotCom,
anySubDomain + "weibo" + dotCom,
anySubDomain + "weiyun" + dotCom,
anySubDomain + "wonnder" + dotCom,
anySubDomain + "wrating" + dotCom,
anySubDomain + "wscdns" + dotCom,
anySubDomain + "xiachufang" + dotCom,
anySubDomain + "xiami" + dotCom,
anySubDomain + "xiaomi" + dotCom,
anySubDomain + "xinhuanet" + dotCom,
anySubDomain + "xinshipu" + dotCom,
anySubDomain + "xnpic" + dotCom,
anySubDomain + "xueqiu" + dotCom,
anySubDomain + "xunlei" + dotCom,
anySubDomain + "xywy" + dotCom,
anySubDomain + "yaolan" + dotCom,
anySubDomain + "yccdn" + dotCom,
anySubDomain + "yesky" + dotCom,
anySubDomain + "yigao" + dotCom,
anySubDomain + "yihaodian" + dotCom,
anySubDomain + "yihaodianimg" + dotCom,
anySubDomain + "yingjiesheng" + dotCom,
anySubDomain + "yhd" + dotCom,
anySubDomain + "youboy" + dotCom,
anySubDomain + "youku" + dotCom,
anySubDomain + "yunba" + dotIo,
anySubDomain + "yunshipei" + dotCom,
anySubDomain + "yupoo" + dotCom,
anySubDomain + "yy" + dotCom,
anySubDomain + "zastatic" + dotCom,
anySubDomain + "zbjimg" + dotCom,
anySubDomain + "zhenai" + dotCom,
anySubDomain + "zhanqi" + dotTv,
anySubDomain + "zhihu" + dotCom,
anySubDomain + "zhimg" + dotCom,
anySubDomain + "zjstv" + dotCom,
anySubDomain + "zhubajie" + dotCom,
}
chinaSitesConds = make([]Condition, len(regexpDomains))
for idx, pattern := range regexpDomains {
matcher, err := NewRegexpDomainMatcher(pattern)
if err != nil {
panic(err)
}
chinaSitesConds[idx] = matcher
}
}

View File

@@ -0,0 +1,27 @@
// +build json
package rules
import (
"testing"
v2net "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func makeDomainDestination(domain string) v2net.Destination {
return v2net.TCPDestination(v2net.DomainAddress(domain), 80)
}
func TestChinaSites(t *testing.T) {
v2testing.Current(t)
rule := new(ChinaSitesCondition)
assert.Bool(rule.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("www.163.com"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("ngacn.cc"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("12306.cn"))).IsTrue()
assert.Bool(rule.Apply(makeDomainDestination("v2ray.com"))).IsFalse()
}

View File

@@ -0,0 +1,144 @@
package rules
import (
"net"
"regexp"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/serial"
)
type Condition interface {
Apply(dest v2net.Destination) bool
}
type ConditionChan []Condition
func NewConditionChan() *ConditionChan {
var condChan ConditionChan = make([]Condition, 0, 8)
return &condChan
}
func (this *ConditionChan) Add(cond Condition) *ConditionChan {
*this = append(*this, cond)
return this
}
func (this *ConditionChan) Apply(dest v2net.Destination) bool {
for _, cond := range *this {
if !cond.Apply(dest) {
return false
}
}
return true
}
func (this *ConditionChan) Len() int {
return len(*this)
}
type PlainDomainMatcher struct {
pattern serial.StringLiteral
}
func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher {
return &PlainDomainMatcher{
pattern: serial.StringLiteral(pattern),
}
}
func (this *PlainDomainMatcher) Apply(dest v2net.Destination) bool {
if !dest.Address().IsDomain() {
return false
}
domain := serial.StringLiteral(dest.Address().Domain())
return domain.Contains(this.pattern)
}
type RegexpDomainMatcher struct {
pattern *regexp.Regexp
}
func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) {
r, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
return &RegexpDomainMatcher{
pattern: r,
}, nil
}
func (this *RegexpDomainMatcher) Apply(dest v2net.Destination) bool {
if !dest.Address().IsDomain() {
return false
}
domain := serial.StringLiteral(dest.Address().Domain())
return this.pattern.MatchString(domain.ToLower().String())
}
type CIDRMatcher struct {
cidr *net.IPNet
}
func NewCIDRMatcher(ipnet string) (*CIDRMatcher, error) {
_, cidr, err := net.ParseCIDR(ipnet)
if err != nil {
return nil, err
}
return &CIDRMatcher{
cidr: cidr,
}, nil
}
func (this *CIDRMatcher) Apply(dest v2net.Destination) bool {
if !dest.Address().IsIPv4() && !dest.Address().IsIPv6() {
return false
}
return this.cidr.Contains(dest.Address().IP())
}
type IPv4Matcher struct {
ipv4net *v2net.IPNet
}
func NewIPv4Matcher(ipnet *v2net.IPNet) *IPv4Matcher {
return &IPv4Matcher{
ipv4net: ipnet,
}
}
func (this *IPv4Matcher) Apply(dest v2net.Destination) bool {
if !dest.Address().IsIPv4() {
return false
}
return this.ipv4net.Contains(dest.Address().IP())
}
type PortMatcher struct {
port v2net.PortRange
}
func NewPortMatcher(portRange v2net.PortRange) *PortMatcher {
return &PortMatcher{
port: portRange,
}
}
func (this *PortMatcher) Apply(dest v2net.Destination) bool {
return this.port.Contains(dest.Port())
}
type NetworkMatcher struct {
network *v2net.NetworkList
}
func NewNetworkMatcher(network *v2net.NetworkList) *NetworkMatcher {
return &NetworkMatcher{
network: network,
}
}
func (this *NetworkMatcher) Apply(dest v2net.Destination) bool {
return this.network.HasNetwork(v2net.Network(dest.Network()))
}

View File

@@ -0,0 +1,33 @@
package rules
import (
v2net "github.com/v2ray/v2ray-core/common/net"
)
type Rule struct {
Tag string
Condition Condition
}
func (this *Rule) Apply(dest v2net.Destination) bool {
return this.Condition.Apply(dest)
}
type RouterRuleConfig struct {
rules []*Rule
}
func NewRouterRuleConfig() *RouterRuleConfig {
return &RouterRuleConfig{
rules: make([]*Rule, 0, 16),
}
}
func (this *RouterRuleConfig) Add(rule *Rule) *RouterRuleConfig {
this.rules = append(this.rules, rule)
return this
}
func (this *RouterRuleConfig) Rules() []*Rule {
return this.rules
}

View File

@@ -1,93 +0,0 @@
package json
import (
"encoding/json"
"errors"
"net"
"strings"
v2net "github.com/v2ray/v2ray-core/common/net"
v2netjson "github.com/v2ray/v2ray-core/common/net/json"
)
type FieldRule struct {
Rule
Domain string
IP *net.IPNet
Port v2net.PortRange
Network v2net.NetworkList
}
func (this *FieldRule) Apply(dest v2net.Destination) bool {
address := dest.Address()
if len(this.Domain) > 0 {
if !address.IsDomain() || !strings.Contains(address.Domain(), this.Domain) {
return false
}
}
if this.IP != nil {
if !(address.IsIPv4() || address.IsIPv6()) || !this.IP.Contains(address.IP()) {
return false
}
}
if this.Port != nil {
port := address.Port()
if port < this.Port.From() || port > this.Port.To() {
return false
}
}
if this.Network != nil {
if !this.Network.HasNetwork(v2net.Network(dest.Network())) {
return false
}
}
return true
}
func (this *FieldRule) UnmarshalJSON(data []byte) error {
type RawFieldRule struct {
Rule
Domain string `json:"domain"`
IP string `json:"ip"`
Port *v2netjson.PortRange `json:"port"`
Network *v2netjson.NetworkList `json:"network"`
}
rawFieldRule := RawFieldRule{}
err := json.Unmarshal(data, &rawFieldRule)
if err != nil {
return err
}
this.Type = rawFieldRule.Type
this.OutboundTag = rawFieldRule.OutboundTag
hasField := false
if len(rawFieldRule.Domain) > 0 {
this.Domain = rawFieldRule.Domain
hasField = true
}
if len(rawFieldRule.IP) > 0 {
_, ipNet, err := net.ParseCIDR(rawFieldRule.IP)
if err != nil {
return errors.New("Invalid IP range in router rule: " + err.Error())
}
this.IP = ipNet
hasField = true
}
if rawFieldRule.Port != nil {
this.Port = rawFieldRule.Port
hasField = true
}
if rawFieldRule.Network != nil {
this.Network = rawFieldRule.Network
hasField = true
}
if !hasField {
return errors.New("This rule has no effective fields.")
}
return nil
}

View File

@@ -1,71 +0,0 @@
package json
import (
"testing"
v2net "github.com/v2ray/v2ray-core/common/net"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestDomainMatching(t *testing.T) {
assert := unit.Assert(t)
rule := &FieldRule{
Domain: "v2ray.com",
}
dest := v2net.NewTCPDestination(v2net.DomainAddress("www.v2ray.com", 80))
assert.Bool(rule.Apply(dest)).IsTrue()
}
func TestPortMatching(t *testing.T) {
assert := unit.Assert(t)
rule := &FieldRule{
Port: &v2nettesting.PortRange{
FromValue: 0,
ToValue: 100,
},
}
dest := v2net.NewTCPDestination(v2net.DomainAddress("www.v2ray.com", 80))
assert.Bool(rule.Apply(dest)).IsTrue()
}
func TestIPMatching(t *testing.T) {
assert := unit.Assert(t)
rawJson := `{
"type": "field",
"ip": "10.0.0.0/8",
"tag": "test"
}`
rule := parseRule([]byte(rawJson))
dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}, 80))
assert.Bool(rule.Apply(dest)).IsTrue()
}
func TestPortNotMatching(t *testing.T) {
assert := unit.Assert(t)
rawJson := `{
"type": "field",
"port": "80-100",
"tag": "test"
}`
rule := parseRule([]byte(rawJson))
dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}, 79))
assert.Bool(rule.Apply(dest)).IsFalse()
}
func TestDomainNotMatching(t *testing.T) {
assert := unit.Assert(t)
rawJson := `{
"type": "field",
"domain": "google.com",
"tag": "test"
}`
rule := parseRule([]byte(rawJson))
dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{10, 0, 0, 1}, 79))
assert.Bool(rule.Apply(dest)).IsFalse()
}

View File

@@ -1,47 +0,0 @@
package json
import (
"encoding/json"
v2routerconfigjson "github.com/v2ray/v2ray-core/app/router/config/json"
"github.com/v2ray/v2ray-core/app/router/rules/config"
"github.com/v2ray/v2ray-core/common/log"
)
type RouterRuleConfig struct {
RuleList []json.RawMessage `json:"rules"`
}
func parseRule(msg json.RawMessage) config.Rule {
rule := new(Rule)
err := json.Unmarshal(msg, rule)
if err != nil {
log.Error("Invalid router rule: %v", err)
return nil
}
if rule.Type == "field" {
fieldrule := new(FieldRule)
err = json.Unmarshal(msg, fieldrule)
if err != nil {
log.Error("Invalid field rule: %v", err)
return nil
}
return fieldrule
}
log.Error("Unknown router rule type: %s", rule.Type)
return nil
}
func (this *RouterRuleConfig) Rules() []config.Rule {
rules := make([]config.Rule, len(this.RuleList))
for idx, rawRule := range this.RuleList {
rules[idx] = parseRule(rawRule)
}
return rules
}
func init() {
v2routerconfigjson.RegisterRouterConfig("rules", func() interface{} {
return new(RouterRuleConfig)
})
}

View File

@@ -1,19 +0,0 @@
package json
import (
"github.com/v2ray/v2ray-core/app/point/config"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type Rule struct {
Type string `json:"type"`
OutboundTag string `json:"outboundTag"`
}
func (this *Rule) Tag() config.DetourTag {
return config.DetourTag(this.OutboundTag)
}
func (this *Rule) Apply(dest v2net.Destination) bool {
return false
}

View File

@@ -1,5 +0,0 @@
package config
type RouterRuleConfig interface {
Rules() []Rule
}

View File

@@ -1,11 +0,0 @@
package config
import (
"github.com/v2ray/v2ray-core/app/point/config"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type Rule interface {
Tag() config.DetourTag
Apply(dest v2net.Destination) bool
}

View File

@@ -2,48 +2,92 @@ package rules
import (
"errors"
"time"
pointconfig "github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/app/router"
"github.com/v2ray/v2ray-core/app/router/rules/config"
"github.com/v2ray/v2ray-core/app/router/rules/config/json"
"github.com/v2ray/v2ray-core/common/collect"
v2net "github.com/v2ray/v2ray-core/common/net"
)
var (
InvalidRule = errors.New("Invalid Rule")
NoRuleApplicable = errors.New("No rule applicable")
EmptyTag = pointconfig.DetourTag("")
)
type Router struct {
rules []config.Rule
type cacheEntry struct {
tag string
err error
validUntil time.Time
}
func (this *Router) TakeDetour(dest v2net.Destination) (pointconfig.DetourTag, error) {
func newCacheEntry(tag string, err error) *cacheEntry {
this := &cacheEntry{
tag: tag,
err: err,
}
this.Extend()
return this
}
func (this *cacheEntry) IsValid() bool {
return this.validUntil.Before(time.Now())
}
func (this *cacheEntry) Extend() {
this.validUntil = time.Now().Add(time.Hour)
}
type Router struct {
rules []*Rule
cache *collect.ValidityMap
}
func NewRouter() *Router {
return &Router{
rules: make([]*Rule, 0, 16),
cache: collect.NewValidityMap(3600),
}
}
func (this *Router) AddRule(rule *Rule) *Router {
this.rules = append(this.rules, rule)
return this
}
func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, error) {
for _, rule := range this.rules {
if rule.Apply(dest) {
return rule.Tag(), nil
return rule.Tag, nil
}
}
return EmptyTag, NoRuleApplicable
return "", NoRuleApplicable
}
func (this *Router) TakeDetour(dest v2net.Destination) (string, error) {
rawEntry := this.cache.Get(dest)
if rawEntry == nil {
tag, err := this.takeDetourWithoutCache(dest)
this.cache.Set(dest, newCacheEntry(tag, err))
return tag, err
}
entry := rawEntry.(*cacheEntry)
return entry.tag, entry.err
}
type RouterFactory struct {
}
func (this *RouterFactory) Create(rawConfig interface{}) (router.Router, error) {
config := rawConfig.(*json.RouterRuleConfig)
config := rawConfig.(*RouterRuleConfig)
rules := config.Rules()
router := NewRouter()
for _, rule := range rules {
if rule == nil {
return nil, InvalidRule
}
router.AddRule(rule)
}
return &Router{
rules: rules,
}, nil
return router, nil
}
func init() {

View File

@@ -0,0 +1,129 @@
// +build json
package rules
import (
"encoding/json"
"errors"
"strings"
router "github.com/v2ray/v2ray-core/app/router"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/serial"
)
type JsonRule struct {
Type string `json:"type"`
OutboundTag string `json:"outboundTag"`
}
func parseFieldRule(msg json.RawMessage) (*Rule, error) {
type RawFieldRule struct {
JsonRule
Domain *serial.StringLiteralList `json:"domain"`
IP *serial.StringLiteralList `json:"ip"`
Port *v2net.PortRange `json:"port"`
Network *v2net.NetworkList `json:"network"`
}
rawFieldRule := new(RawFieldRule)
err := json.Unmarshal(msg, rawFieldRule)
if err != nil {
return nil, err
}
conds := NewConditionChan()
if rawFieldRule.Domain != nil && rawFieldRule.Domain.Len() > 0 {
for _, rawDomain := range *(rawFieldRule.Domain) {
var matcher Condition
if strings.HasPrefix(rawDomain.String(), "regexp:") {
rawMatcher, err := NewRegexpDomainMatcher(rawDomain.String()[7:])
if err != nil {
return nil, err
}
matcher = rawMatcher
} else {
matcher = NewPlainDomainMatcher(rawDomain.String())
}
conds.Add(matcher)
}
}
if rawFieldRule.IP != nil && rawFieldRule.IP.Len() > 0 {
for _, ipStr := range *(rawFieldRule.IP) {
cidrMatcher, err := NewCIDRMatcher(ipStr.String())
if err != nil {
log.Error("Router: Invalid IP range in router rule: %v", err)
return nil, err
}
conds.Add(cidrMatcher)
}
}
if rawFieldRule.Port != nil {
conds.Add(NewPortMatcher(*rawFieldRule.Port))
}
if rawFieldRule.Network != nil {
conds.Add(NewNetworkMatcher(rawFieldRule.Network))
}
if conds.Len() == 0 {
return nil, errors.New("Router: This rule has no effective fields.")
}
return &Rule{
Tag: rawFieldRule.OutboundTag,
Condition: conds,
}, nil
}
func parseRule(msg json.RawMessage) *Rule {
rawRule := new(JsonRule)
err := json.Unmarshal(msg, rawRule)
if err != nil {
log.Error("Router: Invalid router rule: %v", err)
return nil
}
if rawRule.Type == "field" {
fieldrule, err := parseFieldRule(msg)
if err != nil {
log.Error("Invalid field rule: %v", err)
return nil
}
return fieldrule
}
if rawRule.Type == "chinaip" {
chinaiprule, err := parseChinaIPRule(msg)
if err != nil {
log.Error("Router: Invalid chinaip rule: %v", err)
return nil
}
return chinaiprule
}
if rawRule.Type == "chinasites" {
chinasitesrule, err := parseChinaSitesRule(msg)
if err != nil {
log.Error("Invalid chinasites rule: %v", err)
return nil
}
return chinasitesrule
}
log.Error("Unknown router rule type: %s", rawRule.Type)
return nil
}
func init() {
router.RegisterRouterConfig("rules", func(data []byte) (interface{}, error) {
type JsonConfig struct {
RuleList []json.RawMessage `json:"rules"`
}
jsonConfig := new(JsonConfig)
if err := json.Unmarshal(data, jsonConfig); err != nil {
return nil, err
}
config := NewRouterRuleConfig()
for _, rawRule := range jsonConfig.RuleList {
rule := parseRule(rawRule)
config.Add(rule)
}
return config, nil
})
}

View File

@@ -0,0 +1,24 @@
package rules_test
import (
"testing"
. "github.com/v2ray/v2ray-core/app/router/rules"
v2net "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestSimpleRouter(t *testing.T) {
v2testing.Current(t)
router := NewRouter().AddRule(
&Rule{
Tag: "test",
Condition: NewNetworkMatcher(v2net.Network("tcp").AsList()),
})
tag, err := router.TakeDetour(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80))
assert.Error(err).IsNil()
assert.StringLiteral(tag).Equals("test")
}

22
app/space.go Normal file
View File

@@ -0,0 +1,22 @@
package app
// Context of a function call from proxy to app.
type Context interface {
CallerTag() string
}
// A Space contains all apps that may be available in a V2Ray runtime.
// Caller must check the availability of an app by calling HasXXX before getting its instance.
type Space interface {
HasPacketDispatcher() bool
PacketDispatcher() PacketDispatcher
HasDnsCache() bool
DnsCache() DnsCache
HasPubsub() bool
Pubsub() Pubsub
HasInboundHandlerManager() bool
InboundHandlerManager() InboundHandlerManager
}

9
app/testing/space.go Normal file
View File

@@ -0,0 +1,9 @@
package testing
type Context struct {
CallerTagValue string
}
func (this *Context) CallerTag() string {
return this.CallerTagValue
}

View File

@@ -1,7 +1,7 @@
package alloc
import (
"time"
"sync"
)
// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
@@ -40,6 +40,10 @@ func (b *Buffer) Append(data []byte) *Buffer {
return b
}
func (b *Buffer) Bytes() []byte {
return b.Value
}
// Slice cuts the buffer at the given position.
func (b *Buffer) Slice(from, to int) *Buffer {
b.Value = b.Value[from:to]
@@ -69,21 +73,20 @@ func (b *Buffer) Write(data []byte) (int, error) {
}
type bufferPool struct {
chain chan []byte
bufferSize int
buffers2Keep int
chain chan []byte
allocator *sync.Pool
}
func newBufferPool(bufferSize, buffers2Keep, poolSize int) *bufferPool {
func newBufferPool(bufferSize, poolSize int) *bufferPool {
pool := &bufferPool{
chain: make(chan []byte, poolSize),
bufferSize: bufferSize,
buffers2Keep: buffers2Keep,
chain: make(chan []byte, poolSize),
allocator: &sync.Pool{
New: func() interface{} { return make([]byte, bufferSize) },
},
}
for i := 0; i < buffers2Keep; i++ {
for i := 0; i < poolSize; i++ {
pool.chain <- make([]byte, bufferSize)
}
go pool.cleanup(time.Tick(1 * time.Second))
return pool
}
@@ -92,7 +95,7 @@ func (p *bufferPool) allocate() *Buffer {
select {
case b = <-p.chain:
default:
b = make([]byte, p.bufferSize)
b = p.allocator.Get().([]byte)
}
return &Buffer{
head: b,
@@ -105,28 +108,13 @@ func (p *bufferPool) free(buffer *Buffer) {
select {
case p.chain <- buffer.head:
default:
p.allocator.Put(buffer.head)
}
}
func (p *bufferPool) cleanup(tick <-chan time.Time) {
for range tick {
pSize := len(p.chain)
if pSize > p.buffers2Keep {
<-p.chain
continue
}
for delta := p.buffers2Keep - pSize; delta > 0; delta-- {
select {
case p.chain <- make([]byte, p.bufferSize):
default:
}
}
}
}
var smallPool = newBufferPool(1024, 64, 512)
var mediumPool = newBufferPool(8*1024, 256, 2048)
var largePool = newBufferPool(64*1024, 128, 1024)
var smallPool = newBufferPool(1024, 256)
var mediumPool = newBufferPool(8*1024, 512)
var largePool = newBufferPool(64*1024, 128)
// NewSmallBuffer creates a Buffer with 1K bytes of arbitrary content.
func NewSmallBuffer() *Buffer {

View File

@@ -3,11 +3,12 @@ package alloc
import (
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestBufferClear(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
buffer := NewBuffer().Clear()
defer buffer.Release()
@@ -21,7 +22,7 @@ func TestBufferClear(t *testing.T) {
}
func TestBufferIsFull(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
buffer := NewBuffer()
defer buffer.Release()

View File

@@ -0,0 +1,24 @@
package collect
type SizedQueue struct {
elements []interface{}
nextPos int
}
func NewSizedQueue(size int) *SizedQueue {
return &SizedQueue{
elements: make([]interface{}, size),
nextPos: 0,
}
}
// Put puts a new element into the queue and pop out the first element if queue is full.
func (this *SizedQueue) Put(element interface{}) interface{} {
res := this.elements[this.nextPos]
this.elements[this.nextPos] = element
this.nextPos++
if this.nextPos == len(this.elements) {
this.nextPos = 0
}
return res
}

View File

@@ -0,0 +1,18 @@
package collect_test
import (
"testing"
"github.com/v2ray/v2ray-core/common/collect"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestSizedQueue(t *testing.T) {
v2testing.Current(t)
queue := collect.NewSizedQueue(2)
assert.Pointer(queue.Put(1)).IsNil()
assert.Pointer(queue.Put(2)).IsNil()
assert.Int(queue.Put(3).(int)).Equals(1)
}

View File

@@ -1,94 +0,0 @@
package collect
import (
"container/heap"
"sync"
"time"
)
type timedQueueEntry struct {
timeSec int64
value interface{}
}
type timedQueueImpl []*timedQueueEntry
func (queue timedQueueImpl) Len() int {
return len(queue)
}
func (queue timedQueueImpl) Less(i, j int) bool {
return queue[i].timeSec < queue[j].timeSec
}
func (queue timedQueueImpl) Swap(i, j int) {
queue[i], queue[j] = queue[j], queue[i]
}
func (queue *timedQueueImpl) Push(value interface{}) {
entry := value.(*timedQueueEntry)
*queue = append(*queue, entry)
}
func (queue *timedQueueImpl) Pop() interface{} {
old := *queue
n := len(old)
v := old[n-1]
old[n-1] = nil
*queue = old[:n-1]
return v
}
type TimedQueue struct {
queue timedQueueImpl
access sync.RWMutex
removed chan interface{}
}
func NewTimedQueue(updateInterval int) *TimedQueue {
queue := &TimedQueue{
queue: make([]*timedQueueEntry, 0, 256),
removed: make(chan interface{}, 16),
access: sync.RWMutex{},
}
go queue.cleanup(time.Tick(time.Duration(updateInterval) * time.Second))
return queue
}
func (queue *TimedQueue) Add(value interface{}, time2Remove int64) {
queue.access.Lock()
heap.Push(&queue.queue, &timedQueueEntry{
timeSec: time2Remove,
value: value,
})
queue.access.Unlock()
}
func (queue *TimedQueue) RemovedEntries() <-chan interface{} {
return queue.removed
}
func (queue *TimedQueue) cleanup(tick <-chan time.Time) {
for now := range tick {
nowSec := now.Unix()
for {
queue.access.RLock()
queueLen := queue.queue.Len()
queue.access.RUnlock()
if queueLen == 0 {
break
}
queue.access.RLock()
entry := queue.queue[0]
queue.access.RUnlock()
if entry.timeSec > nowSec {
break
}
queue.access.Lock()
heap.Pop(&queue.queue)
queue.access.Unlock()
queue.removed <- entry.value
}
}
}

View File

@@ -1,60 +0,0 @@
package collect
import (
"testing"
"time"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestTimedQueue(t *testing.T) {
assert := unit.Assert(t)
removed := make(map[string]bool)
nowSec := time.Now().Unix()
q := NewTimedQueue(2)
go func() {
for {
entry := <-q.RemovedEntries()
removed[entry.(string)] = true
}
}()
q.Add("Value1", nowSec)
q.Add("Value2", nowSec+5)
v1, ok := removed["Value1"]
assert.Bool(ok).IsFalse()
v2, ok := removed["Value2"]
assert.Bool(ok).IsFalse()
tick := time.Tick(4 * time.Second)
<-tick
v1, ok = removed["Value1"]
assert.Bool(ok).IsTrue()
assert.Bool(v1).IsTrue()
removed["Value1"] = false
v2, ok = removed["Value2"]
assert.Bool(ok).IsFalse()
<-tick
v2, ok = removed["Value2"]
assert.Bool(ok).IsTrue()
assert.Bool(v2).IsTrue()
removed["Value2"] = false
<-tick
assert.Bool(removed["Values"]).IsFalse()
q.Add("Value1", time.Now().Unix()+10)
<-tick
v1, ok = removed["Value1"]
assert.Bool(ok).IsTrue()
assert.Bool(v1).IsFalse()
}

View File

@@ -0,0 +1,71 @@
package collect
import (
"sync"
"time"
"github.com/v2ray/v2ray-core/common/serial"
)
type Validity interface {
IsValid() bool
}
type entry struct {
key string
value Validity
}
type ValidityMap struct {
sync.RWMutex
cache map[string]Validity
cleanupIntervalSec int
}
func NewValidityMap(cleanupIntervalSec int) *ValidityMap {
instance := &ValidityMap{
cache: make(map[string]Validity),
cleanupIntervalSec: cleanupIntervalSec,
}
go instance.cleanup()
return instance
}
func (this *ValidityMap) cleanup() {
for range time.Tick(time.Duration(this.cleanupIntervalSec) * time.Second) {
entry2Remove := make([]entry, 0, 128)
this.RLock()
for key, value := range this.cache {
if !value.IsValid() {
entry2Remove = append(entry2Remove, entry{
key: key,
value: value,
})
}
}
this.RUnlock()
for _, entry := range entry2Remove {
if !entry.value.IsValid() {
this.Lock()
delete(this.cache, entry.key)
this.Unlock()
}
}
}
}
func (this *ValidityMap) Set(key serial.String, value Validity) {
this.Lock()
this.cache[key.String()] = value
this.Unlock()
}
func (this *ValidityMap) Get(key serial.String) Validity {
this.RLock()
defer this.RUnlock()
if value, found := this.cache[key.String()]; found {
return value
}
return nil
}

View File

@@ -1,12 +1,5 @@
package log
import (
"log"
"os"
"github.com/v2ray/v2ray-core/common/platform"
)
// AccessStatus is the status of an access request from clients.
type AccessStatus string
@@ -15,16 +8,9 @@ const (
AccessRejected = AccessStatus("rejected")
)
type accessLogger interface {
Log(from, to string, status AccessStatus, reason string)
}
type noOpAccessLogger struct {
}
func (logger *noOpAccessLogger) Log(from, to string, status AccessStatus, reason string) {
// Swallow
}
var (
accessLoggerInstance logWriter = &noOpLogWriter{}
)
type accessLog struct {
From string
@@ -33,60 +19,27 @@ type accessLog struct {
Reason string
}
type fileAccessLogger struct {
queue chan *accessLog
logger *log.Logger
file *os.File
func (this *accessLog) String() string {
return this.From + " " + string(this.Status) + " " + this.To + " " + this.Reason
}
func (logger *fileAccessLogger) close() {
logger.file.Close()
}
func (logger *fileAccessLogger) Log(from, to string, status AccessStatus, reason string) {
select {
case logger.queue <- &accessLog{
From: from,
To: to,
Status: status,
Reason: reason,
}:
default:
// We don't expect this to happen, but don't want to block main thread as well.
}
}
func (logger *fileAccessLogger) Run() {
for entry := range logger.queue {
logger.logger.Println(entry.From + " " + string(entry.Status) + " " + entry.To + " " + entry.Reason)
}
}
func newFileAccessLogger(path string) accessLogger {
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
log.Printf("Unable to create or open file (%s): %v%s", path, err, platform.LineSeparator())
return nil
}
return &fileAccessLogger{
queue: make(chan *accessLog, 16),
logger: log.New(file, "", log.Ldate|log.Ltime),
file: file,
}
}
var accessLoggerInstance accessLogger = &noOpAccessLogger{}
// InitAccessLogger initializes the access logger to write into the give file.
func InitAccessLogger(file string) {
logger := newFileAccessLogger(file)
if logger != nil {
go logger.(*fileAccessLogger).Run()
accessLoggerInstance = logger
func InitAccessLogger(file string) error {
logger, err := newFileLogWriter(file)
if err != nil {
Error("Failed to create access logger on file (%s): %v", file, err)
return err
}
accessLoggerInstance = logger
return nil
}
// Access writes an access log.
func Access(from, to string, status AccessStatus, reason string) {
accessLoggerInstance.Log(from, to, status, reason)
accessLoggerInstance.Log(&accessLog{
From: from,
To: to,
Status: status,
Reason: reason,
})
}

View File

@@ -3,15 +3,16 @@ package log
import (
"io/ioutil"
"os"
"strings"
"testing"
"time"
"github.com/v2ray/v2ray-core/testing/unit"
"github.com/v2ray/v2ray-core/common/serial"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestAccessLog(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
filename := "/tmp/test_access_log.log"
InitAccessLogger(filename)
@@ -21,14 +22,15 @@ func TestAccessLog(t *testing.T) {
Access("test_from", "test_to", AccessAccepted, "test_reason")
<-time.After(2 * time.Second)
accessLoggerInstance.(*fileAccessLogger).close()
accessLoggerInstance = &noOpAccessLogger{}
accessLoggerInstance.(*fileLogWriter).close()
accessLoggerInstance = &noOpLogWriter{}
content, err := ioutil.ReadFile(filename)
assert.Error(err).IsNil()
assert.Bool(strings.Contains(string(content), "test_from")).IsTrue()
assert.Bool(strings.Contains(string(content), "test_to")).IsTrue()
assert.Bool(strings.Contains(string(content), "test_reason")).IsTrue()
assert.Bool(strings.Contains(string(content), "accepted")).IsTrue()
contentStr := serial.StringLiteral(content)
assert.String(contentStr).Contains(serial.StringLiteral("test_from"))
assert.String(contentStr).Contains(serial.StringLiteral("test_to"))
assert.String(contentStr).Contains(serial.StringLiteral("test_reason"))
assert.String(contentStr).Contains(serial.StringLiteral("accepted"))
}

View File

@@ -2,10 +2,6 @@ package log
import (
"fmt"
"io"
"os"
"github.com/v2ray/v2ray-core/common/platform"
)
const (
@@ -15,41 +11,30 @@ const (
ErrorLevel = LogLevel(3)
)
type logger interface {
WriteLog(prefix, format string, v ...interface{})
type errorLog struct {
prefix string
format string
values []interface{}
}
type noOpLogger struct {
}
func (l *noOpLogger) WriteLog(prefix, format string, v ...interface{}) {
// Swallow
}
type streamLogger struct {
writer io.Writer
}
func (l *streamLogger) WriteLog(prefix, format string, v ...interface{}) {
func (this *errorLog) String() string {
var data string
if v == nil || len(v) == 0 {
data = format
if len(this.values) == 0 {
data = this.format
} else {
data = fmt.Sprintf(format, v...)
data = fmt.Sprintf(this.format, this.values...)
}
l.writer.Write([]byte(prefix + data + platform.LineSeparator()))
return this.prefix + data
}
var (
noOpLoggerInstance logger = &noOpLogger{}
streamLoggerInstance logger = &streamLogger{
writer: os.Stdout,
}
noOpLoggerInstance logWriter = &noOpLogWriter{}
streamLoggerInstance logWriter = newStdOutLogWriter()
debugLogger = noOpLoggerInstance
infoLogger = noOpLoggerInstance
warningLogger = noOpLoggerInstance
errorLogger = noOpLoggerInstance
warningLogger = streamLoggerInstance
errorLogger = streamLoggerInstance
)
type LogLevel int
@@ -76,22 +61,48 @@ func SetLogLevel(level LogLevel) {
}
}
func InitErrorLogger(file string) error {
logger, err := newFileLogWriter(file)
if err != nil {
Error("Failed to create error logger on file (%s): %v", file, err)
return err
}
streamLoggerInstance = logger
return nil
}
// Debug outputs a debug log with given format and optional arguments.
func Debug(format string, v ...interface{}) {
debugLogger.WriteLog("[Debug]", format, v...)
debugLogger.Log(&errorLog{
prefix: "[Debug]",
format: format,
values: v,
})
}
// Info outputs an info log with given format and optional arguments.
func Info(format string, v ...interface{}) {
infoLogger.WriteLog("[Info]", format, v...)
infoLogger.Log(&errorLog{
prefix: "[Info]",
format: format,
values: v,
})
}
// Warning outputs a warning log with given format and optional arguments.
func Warning(format string, v ...interface{}) {
warningLogger.WriteLog("[Warning]", format, v...)
warningLogger.Log(&errorLog{
prefix: "[Warning]",
format: format,
values: v,
})
}
// Error outputs an error log with given format and optional arguments.
func Error(format string, v ...interface{}) {
errorLogger.WriteLog("[Error]", format, v...)
errorLogger.Log(&errorLog{
prefix: "[Error]",
format: format,
values: v,
})
}

View File

@@ -2,13 +2,15 @@ package log
import (
"bytes"
"log"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestLogLevelSetting(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
assert.Pointer(debugLogger).Equals(noOpLoggerInstance)
SetLogLevel(DebugLevel)
@@ -20,16 +22,17 @@ func TestLogLevelSetting(t *testing.T) {
}
func TestStreamLogger(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
logger := &streamLogger{
writer: buffer,
infoLogger = &stdOutLogWriter{
logger: log.New(buffer, "", 0),
}
logger.WriteLog("TestPrefix: ", "Test %s Format", "Stream Logger")
assert.Bytes(buffer.Bytes()).Equals([]byte("TestPrefix: Test Stream Logger Format\n"))
Info("Test %s Format", "Stream Logger")
assert.Bytes(buffer.Bytes()).Equals([]byte("[Info]Test Stream Logger Format\n"))
buffer.Reset()
logger.WriteLog("TestPrefix: ", "Test No Format")
assert.Bytes(buffer.Bytes()).Equals([]byte("TestPrefix: Test No Format\n"))
errorLogger = infoLogger
Error("Test No Format")
assert.Bytes(buffer.Bytes()).Equals([]byte("[Error]Test No Format\n"))
}

77
common/log/log_writer.go Normal file
View File

@@ -0,0 +1,77 @@
package log
import (
"io"
"log"
"os"
"github.com/v2ray/v2ray-core/common/platform"
"github.com/v2ray/v2ray-core/common/serial"
)
func createLogger(writer io.Writer) *log.Logger {
return log.New(writer, "", log.Ldate|log.Ltime)
}
type logWriter interface {
Log(serial.String)
}
type noOpLogWriter struct {
}
func (this *noOpLogWriter) Log(serial.String) {
// Swallow
}
type stdOutLogWriter struct {
logger *log.Logger
}
func newStdOutLogWriter() logWriter {
return &stdOutLogWriter{
logger: createLogger(os.Stdout),
}
}
func (this *stdOutLogWriter) Log(log serial.String) {
this.logger.Print(log.String() + platform.LineSeparator())
}
type fileLogWriter struct {
queue chan serial.String
logger *log.Logger
file *os.File
}
func (this *fileLogWriter) Log(log serial.String) {
select {
case this.queue <- log:
default:
// We don't expect this to happen, but don't want to block main thread as well.
}
}
func (this *fileLogWriter) run() {
for entry := range this.queue {
this.logger.Print(entry.String() + platform.LineSeparator())
}
}
func (this *fileLogWriter) close() {
this.file.Close()
}
func newFileLogWriter(path string) (*fileLogWriter, error) {
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return nil, err
}
logger := &fileLogWriter{
queue: make(chan serial.String, 16),
logger: log.New(file, "", log.Ldate|log.Ltime),
file: file,
}
go logger.run()
return logger, nil
}

View File

@@ -2,7 +2,6 @@ package net
import (
"net"
"strconv"
"github.com/v2ray/v2ray-core/common/log"
)
@@ -10,10 +9,8 @@ import (
// Address represents a network address to be communicated with. It may be an IP address or domain
// address, not both. This interface doesn't resolve IP address for a given domain.
type Address interface {
IP() net.IP // IP of this Address
Domain() string // Domain of this Address
Port() uint16 // Port of this Address
PortBytes() []byte // Port in bytes, network byte order
IP() net.IP // IP of this Address
Domain() string // Domain of this Address
IsIPv4() bool // True if this Address is an IPv4 address
IsIPv6() bool // True if this Address is an IPv6 address
@@ -32,26 +29,22 @@ func allZeros(data []byte) bool {
}
// IPAddress creates an Address with given IP and port.
func IPAddress(ip []byte, port uint16) Address {
func IPAddress(ip []byte) Address {
switch len(ip) {
case net.IPv4len:
return IPv4Address{
PortAddress: PortAddress{port: port},
ip: [4]byte{ip[0], ip[1], ip[2], ip[3]},
}
var addr IPv4Address = [4]byte{ip[0], ip[1], ip[2], ip[3]}
return &addr
case net.IPv6len:
if allZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff {
return IPAddress(ip[12:16], port)
return IPAddress(ip[12:16])
}
return IPv6Address{
PortAddress: PortAddress{port: port},
ip: [16]byte{
ip[0], ip[1], ip[2], ip[3],
ip[4], ip[5], ip[6], ip[7],
ip[8], ip[9], ip[10], ip[11],
ip[12], ip[13], ip[14], ip[15],
},
var addr IPv6Address = [16]byte{
ip[0], ip[1], ip[2], ip[3],
ip[4], ip[5], ip[6], ip[7],
ip[8], ip[9], ip[10], ip[11],
ip[12], ip[13], ip[14], ip[15],
}
return &addr
default:
log.Error("Invalid IP format: %v", ip)
return nil
@@ -59,108 +52,85 @@ func IPAddress(ip []byte, port uint16) Address {
}
// DomainAddress creates an Address with given domain and port.
func DomainAddress(domain string, port uint16) Address {
return DomainAddressImpl{
domain: domain,
PortAddress: PortAddress{port: port},
}
func DomainAddress(domain string) Address {
var addr DomainAddressImpl = DomainAddressImpl(domain)
return &addr
}
type PortAddress struct {
port uint16
type IPv4Address [4]byte
func (addr *IPv4Address) IP() net.IP {
return net.IP(addr[:])
}
func (addr PortAddress) Port() uint16 {
return addr.port
}
func (addr PortAddress) PortBytes() []byte {
return []byte{byte(addr.port >> 8), byte(addr.port)}
}
type IPv4Address struct {
PortAddress
ip [4]byte
}
func (addr IPv4Address) IP() net.IP {
return net.IP(addr.ip[:])
}
func (addr IPv4Address) Domain() string {
func (addr *IPv4Address) Domain() string {
panic("Calling Domain() on an IPv4Address.")
}
func (addr IPv4Address) IsIPv4() bool {
func (addr *IPv4Address) IsIPv4() bool {
return true
}
func (addr IPv4Address) IsIPv6() bool {
func (addr *IPv4Address) IsIPv6() bool {
return false
}
func (addr IPv4Address) IsDomain() bool {
func (addr *IPv4Address) IsDomain() bool {
return false
}
func (addr IPv4Address) String() string {
return addr.IP().String() + ":" + strconv.Itoa(int(addr.PortAddress.port))
func (this *IPv4Address) String() string {
return this.IP().String()
}
type IPv6Address struct {
PortAddress
ip [16]byte
type IPv6Address [16]byte
func (addr *IPv6Address) IP() net.IP {
return net.IP(addr[:])
}
func (addr IPv6Address) IP() net.IP {
return net.IP(addr.ip[:])
}
func (addr IPv6Address) Domain() string {
func (addr *IPv6Address) Domain() string {
panic("Calling Domain() on an IPv6Address.")
}
func (addr IPv6Address) IsIPv4() bool {
func (addr *IPv6Address) IsIPv4() bool {
return false
}
func (addr IPv6Address) IsIPv6() bool {
func (addr *IPv6Address) IsIPv6() bool {
return true
}
func (addr IPv6Address) IsDomain() bool {
func (addr *IPv6Address) IsDomain() bool {
return false
}
func (addr IPv6Address) String() string {
return "[" + addr.IP().String() + "]:" + strconv.Itoa(int(addr.PortAddress.port))
func (this *IPv6Address) String() string {
return "[" + this.IP().String() + "]"
}
type DomainAddressImpl struct {
PortAddress
domain string
}
type DomainAddressImpl string
func (addr DomainAddressImpl) IP() net.IP {
func (addr *DomainAddressImpl) IP() net.IP {
panic("Calling IP() on a DomainAddress.")
}
func (addr DomainAddressImpl) Domain() string {
return addr.domain
func (addr *DomainAddressImpl) Domain() string {
return string(*addr)
}
func (addr DomainAddressImpl) IsIPv4() bool {
func (addr *DomainAddressImpl) IsIPv4() bool {
return false
}
func (addr DomainAddressImpl) IsIPv6() bool {
func (addr *DomainAddressImpl) IsIPv6() bool {
return false
}
func (addr DomainAddressImpl) IsDomain() bool {
func (addr *DomainAddressImpl) IsDomain() bool {
return true
}
func (addr DomainAddressImpl) String() string {
return addr.domain + ":" + strconv.Itoa(int(addr.PortAddress.port))
func (this *DomainAddressImpl) String() string {
return this.Domain()
}

View File

@@ -0,0 +1,26 @@
// +build json
package net
import (
"encoding/json"
"net"
)
type AddressJson struct {
Address Address
}
func (this *AddressJson) UnmarshalJSON(data []byte) error {
var rawStr string
if err := json.Unmarshal(data, &rawStr); err != nil {
return err
}
ip := net.ParseIP(rawStr)
if ip != nil {
this.Address = IPAddress(ip)
} else {
this.Address = DomainAddress(rawStr)
}
return nil
}

View File

@@ -0,0 +1,37 @@
// +build json
package net_test
import (
"encoding/json"
"net"
"testing"
. "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestIPParsing(t *testing.T) {
v2testing.Current(t)
rawJson := "\"8.8.8.8\""
var address AddressJson
err := json.Unmarshal([]byte(rawJson), &address)
assert.Error(err).IsNil()
assert.Bool(address.Address.IsIPv4()).IsTrue()
assert.Bool(address.Address.IsDomain()).IsFalse()
assert.Bool(address.Address.IP().Equal(net.ParseIP("8.8.8.8"))).IsTrue()
}
func TestDomainParsing(t *testing.T) {
v2testing.Current(t)
rawJson := "\"v2ray.com\""
var address AddressJson
err := json.Unmarshal([]byte(rawJson), &address)
assert.Error(err).IsNil()
assert.Bool(address.Address.IsIPv4()).IsFalse()
assert.Bool(address.Address.IsDomain()).IsTrue()
assert.StringLiteral(address.Address.Domain()).Equals("v2ray.com")
}

View File

@@ -1,29 +1,30 @@
package net
package net_test
import (
"net"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
v2net "github.com/v2ray/v2ray-core/common/net"
v2netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestIPv4Address(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
ip := []byte{byte(1), byte(2), byte(3), byte(4)}
port := uint16(80)
addr := IPAddress(ip, port)
addr := v2net.IPAddress(ip)
assert.Bool(addr.IsIPv4()).IsTrue()
assert.Bool(addr.IsIPv6()).IsFalse()
assert.Bool(addr.IsDomain()).IsFalse()
v2netassert.Address(addr).IsIPv4()
v2netassert.Address(addr).IsNotIPv6()
v2netassert.Address(addr).IsNotDomain()
assert.Bytes(addr.IP()).Equals(ip)
assert.Uint16(addr.Port()).Equals(port)
assert.String(addr.String()).Equals("1.2.3.4:80")
assert.String(addr).Equals("1.2.3.4")
}
func TestIPv6Address(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
ip := []byte{
byte(1), byte(2), byte(3), byte(4),
@@ -31,38 +32,33 @@ func TestIPv6Address(t *testing.T) {
byte(1), byte(2), byte(3), byte(4),
byte(1), byte(2), byte(3), byte(4),
}
port := uint16(443)
addr := IPAddress(ip, port)
addr := v2net.IPAddress(ip)
assert.Bool(addr.IsIPv6()).IsTrue()
assert.Bool(addr.IsIPv4()).IsFalse()
assert.Bool(addr.IsDomain()).IsFalse()
v2netassert.Address(addr).IsIPv6()
v2netassert.Address(addr).IsNotIPv4()
v2netassert.Address(addr).IsNotDomain()
assert.Bytes(addr.IP()).Equals(ip)
assert.Uint16(addr.Port()).Equals(port)
assert.String(addr.String()).Equals("[102:304:102:304:102:304:102:304]:443")
assert.String(addr).Equals("[102:304:102:304:102:304:102:304]")
}
func TestDomainAddress(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
domain := "v2ray.com"
port := uint16(443)
addr := DomainAddress(domain, port)
addr := v2net.DomainAddress(domain)
assert.Bool(addr.IsDomain()).IsTrue()
assert.Bool(addr.IsIPv4()).IsFalse()
assert.Bool(addr.IsIPv6()).IsFalse()
assert.String(addr.Domain()).Equals(domain)
assert.Uint16(addr.Port()).Equals(port)
assert.String(addr.String()).Equals("v2ray.com:443")
v2netassert.Address(addr).IsDomain()
v2netassert.Address(addr).IsNotIPv6()
v2netassert.Address(addr).IsNotIPv4()
assert.StringLiteral(addr.Domain()).Equals(domain)
assert.String(addr).Equals("v2ray.com")
}
func TestNetIPv4Address(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
ip := net.IPv4(1, 2, 3, 4)
port := uint16(80)
addr := IPAddress(ip, port)
assert.Bool(addr.IsIPv4()).IsTrue()
assert.String(addr.String()).Equals("1.2.3.4:80")
addr := v2net.IPAddress(ip)
v2netassert.Address(addr).IsIPv4()
assert.String(addr).Equals("1.2.3.4")
}

View File

@@ -4,66 +4,86 @@ package net
type Destination interface {
Network() string // Protocol of communication (tcp / udp)
Address() Address // Address of destination
String() string // String representation of the destination
Port() Port
String() string // String representation of the destination
NetAddr() string
IsTCP() bool // True if destination is reachable via TCP
IsUDP() bool // True if destination is reachable via UDP
}
// NewTCPDestination creates a TCP destination with given address
func NewTCPDestination(address Address) Destination {
return TCPDestination{address: address}
// TCPDestination creates a TCP destination with given address
func TCPDestination(address Address, port Port) Destination {
return &tcpDestination{address: address, port: port}
}
// NewUDPDestination creates a UDP destination with given address
func NewUDPDestination(address Address) Destination {
return UDPDestination{address: address}
// UDPDestination creates a UDP destination with given address
func UDPDestination(address Address, port Port) Destination {
return &udpDestination{address: address, port: port}
}
type TCPDestination struct {
type tcpDestination struct {
address Address
port Port
}
func (dest TCPDestination) Network() string {
func (dest *tcpDestination) Network() string {
return "tcp"
}
func (dest TCPDestination) Address() Address {
func (dest *tcpDestination) Address() Address {
return dest.address
}
func (dest TCPDestination) String() string {
return "tcp:" + dest.address.String()
func (dest *tcpDestination) NetAddr() string {
return dest.address.String() + ":" + dest.port.String()
}
func (dest TCPDestination) IsTCP() bool {
func (dest *tcpDestination) String() string {
return "tcp:" + dest.NetAddr()
}
func (dest *tcpDestination) IsTCP() bool {
return true
}
func (dest TCPDestination) IsUDP() bool {
func (dest *tcpDestination) IsUDP() bool {
return false
}
type UDPDestination struct {
address Address
func (dest *tcpDestination) Port() Port {
return dest.port
}
func (dest UDPDestination) Network() string {
type udpDestination struct {
address Address
port Port
}
func (dest *udpDestination) Network() string {
return "udp"
}
func (dest UDPDestination) Address() Address {
func (dest *udpDestination) Address() Address {
return dest.address
}
func (dest UDPDestination) String() string {
return "udp:" + dest.address.String()
func (dest *udpDestination) NetAddr() string {
return dest.address.String() + ":" + dest.port.String()
}
func (dest UDPDestination) IsTCP() bool {
func (dest *udpDestination) String() string {
return "udp:" + dest.NetAddr()
}
func (dest *udpDestination) IsTCP() bool {
return false
}
func (dest UDPDestination) IsUDP() bool {
func (dest *udpDestination) IsUDP() bool {
return true
}
func (dest *udpDestination) Port() Port {
return dest.port
}

View File

@@ -1,25 +1,28 @@
package net
package net_test
import (
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
v2net "github.com/v2ray/v2ray-core/common/net"
v2netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestTCPDestination(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
dest := NewTCPDestination(IPAddress([]byte{1, 2, 3, 4}, 80))
assert.Bool(dest.IsTCP()).IsTrue()
assert.Bool(dest.IsUDP()).IsFalse()
assert.String(dest.String()).Equals("tcp:1.2.3.4:80")
dest := v2net.TCPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}), 80)
v2netassert.Destination(dest).IsTCP()
v2netassert.Destination(dest).IsNotUDP()
assert.String(dest).Equals("tcp:1.2.3.4:80")
}
func TestUDPDestination(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
dest := NewUDPDestination(IPAddress([]byte{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}, 53))
assert.Bool(dest.IsTCP()).IsFalse()
assert.Bool(dest.IsUDP()).IsTrue()
assert.String(dest.String()).Equals("udp:[2001:4860:4860::8888]:53")
dest := v2net.UDPDestination(v2net.IPAddress([]byte{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}), 53)
v2netassert.Destination(dest).IsNotTCP()
v2netassert.Destination(dest).IsUDP()
assert.String(dest).Equals("udp:[2001:4860:4860::8888]:53")
}

97
common/net/ipnet.go Normal file
View File

@@ -0,0 +1,97 @@
package net
import (
"net"
)
var (
onesCount = make(map[byte]byte)
)
type IPNet struct {
cache map[uint32]byte
}
func NewIPNet() *IPNet {
return NewIPNetInitialValue(make(map[uint32]byte, 1024))
}
func NewIPNetInitialValue(data map[uint32]byte) *IPNet {
return &IPNet{
cache: data,
}
}
func ipToUint32(ip net.IP) uint32 {
value := uint32(0)
for _, b := range []byte(ip) {
value <<= 8
value += uint32(b)
}
return value
}
func ipMaskToByte(mask net.IPMask) byte {
value := byte(0)
for _, b := range []byte(mask) {
value += onesCount[b]
}
return value
}
func (this *IPNet) Add(ipNet *net.IPNet) {
ipv4 := ipNet.IP.To4()
if ipv4 == nil {
// For now, we don't support IPv6
return
}
value := ipToUint32(ipv4)
mask := ipMaskToByte(ipNet.Mask)
existing, found := this.cache[value]
if !found || existing > mask {
this.cache[value] = mask
}
}
func (this *IPNet) Contains(ip net.IP) bool {
ipv4 := ip.To4()
if ipv4 == nil {
return false
}
originalValue := ipToUint32(ipv4)
if entry, found := this.cache[originalValue]; found {
if entry == 0 {
return true
}
}
mask := uint32(0)
for maskbit := byte(1); maskbit <= 32; maskbit++ {
mask += 1 << uint32(32-maskbit)
maskedValue := originalValue & mask
if entry, found := this.cache[maskedValue]; found {
if entry == maskbit {
return true
}
}
}
return false
}
func (this *IPNet) Serialize() []uint32 {
content := make([]uint32, 0, 2*len(this.cache))
for key, value := range this.cache {
content = append(content, uint32(key), uint32(value))
}
return content
}
func init() {
value := byte(0)
for mask := byte(1); mask <= 8; mask++ {
value += 1 << byte(8-mask)
onesCount[value] = mask
}
}

42
common/net/ipnet_test.go Normal file
View File

@@ -0,0 +1,42 @@
package net_test
import (
"net"
"testing"
. "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func parseCIDR(str string) *net.IPNet {
_, ipNet, err := net.ParseCIDR(str)
assert.Error(err).IsNil()
return ipNet
}
func TestIPNet(t *testing.T) {
v2testing.Current(t)
ipNet := NewIPNet()
ipNet.Add(parseCIDR(("0.0.0.0/8")))
ipNet.Add(parseCIDR(("10.0.0.0/8")))
ipNet.Add(parseCIDR(("100.64.0.0/10")))
ipNet.Add(parseCIDR(("127.0.0.0/8")))
ipNet.Add(parseCIDR(("169.254.0.0/16")))
ipNet.Add(parseCIDR(("172.16.0.0/12")))
ipNet.Add(parseCIDR(("192.0.0.0/24")))
ipNet.Add(parseCIDR(("192.0.2.0/24")))
ipNet.Add(parseCIDR(("192.168.0.0/16")))
ipNet.Add(parseCIDR(("198.18.0.0/15")))
ipNet.Add(parseCIDR(("198.51.100.0/24")))
ipNet.Add(parseCIDR(("203.0.113.0/24")))
ipNet.Add(parseCIDR(("8.8.8.8/32")))
assert.Bool(ipNet.Contains(net.ParseIP("192.168.1.1"))).IsTrue()
assert.Bool(ipNet.Contains(net.ParseIP("192.0.0.0"))).IsTrue()
assert.Bool(ipNet.Contains(net.ParseIP("192.0.1.0"))).IsFalse()
assert.Bool(ipNet.Contains(net.ParseIP("0.1.0.0"))).IsTrue()
assert.Bool(ipNet.Contains(net.ParseIP("1.0.0.1"))).IsFalse()
assert.Bool(ipNet.Contains(net.ParseIP("8.8.8.7"))).IsFalse()
assert.Bool(ipNet.Contains(net.ParseIP("8.8.8.8"))).IsTrue()
}

View File

@@ -1,46 +0,0 @@
package json
import (
"encoding/json"
"errors"
"strings"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type NetworkList []string
func NewNetworkList(networks []string) NetworkList {
list := NetworkList(make([]string, len(networks)))
for idx, network := range networks {
list[idx] = strings.ToLower(strings.TrimSpace(network))
}
return list
}
func (this *NetworkList) UnmarshalJSON(data []byte) error {
var strList []string
err := json.Unmarshal(data, &strList)
if err == nil {
*this = NewNetworkList(strList)
return nil
}
var str string
err = json.Unmarshal(data, &str)
if err == nil {
strList := strings.Split(str, ",")
*this = NewNetworkList(strList)
return nil
}
return errors.New("Unknown format of network list: " + string(data))
}
func (this *NetworkList) HasNetwork(network v2net.Network) bool {
for _, value := range *this {
if value == string(network) {
return true
}
}
return false
}

View File

@@ -1,28 +0,0 @@
package json
import (
"encoding/json"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestArrayNetworkList(t *testing.T) {
assert := unit.Assert(t)
var list NetworkList
err := json.Unmarshal([]byte("[\"Tcp\"]"), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork("tcp")).IsTrue()
assert.Bool(list.HasNetwork("udp")).IsFalse()
}
func TestStringNetworkList(t *testing.T) {
assert := unit.Assert(t)
var list NetworkList
err := json.Unmarshal([]byte("\"TCP, ip\""), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork("tcp")).IsTrue()
assert.Bool(list.HasNetwork("udp")).IsFalse()
}

View File

@@ -1,12 +1,36 @@
package net
import (
"github.com/v2ray/v2ray-core/common/serial"
)
const (
TCPNetwork = Network("tcp")
UDPNetwork = Network("udp")
)
type Network string
type Network serial.StringLiteral
type NetworkList interface {
HasNetwork(Network) bool
func (this Network) AsList() *NetworkList {
list := NetworkList([]Network{this})
return &list
}
type NetworkList []Network
func NewNetworkList(networks serial.StringLiteralList) NetworkList {
list := NetworkList(make([]Network, networks.Len()))
for idx, network := range networks {
list[idx] = Network(network.TrimSpace().ToLower())
}
return list
}
func (this *NetworkList) HasNetwork(network Network) bool {
for _, value := range *this {
if string(value) == string(network) {
return true
}
}
return false
}

View File

@@ -0,0 +1,18 @@
// +build json
package net
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/serial"
)
func (this *NetworkList) UnmarshalJSON(data []byte) error {
var strlist serial.StringLiteralList
if err := json.Unmarshal(data, &strlist); err != nil {
return err
}
*this = NewNetworkList(strlist)
return nil
}

View File

@@ -0,0 +1,32 @@
// +build json
package net_test
import (
"encoding/json"
"testing"
. "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestArrayNetworkList(t *testing.T) {
v2testing.Current(t)
var list NetworkList
err := json.Unmarshal([]byte("[\"Tcp\"]"), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork(Network("tcp"))).IsTrue()
assert.Bool(list.HasNetwork(Network("udp"))).IsFalse()
}
func TestStringNetworkList(t *testing.T) {
v2testing.Current(t)
var list NetworkList
err := json.Unmarshal([]byte("\"TCP, ip\""), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork(Network("tcp"))).IsTrue()
assert.Bool(list.HasNetwork(Network("udp"))).IsFalse()
}

32
common/net/port.go Normal file
View File

@@ -0,0 +1,32 @@
package net
import (
"github.com/v2ray/v2ray-core/common/serial"
)
type Port serial.Uint16Literal
func PortFromBytes(port []byte) Port {
return Port(uint16(port[0])<<8 + uint16(port[1]))
}
func (this Port) Value() uint16 {
return uint16(this)
}
func (this Port) Bytes() []byte {
return []byte{byte(this >> 8), byte(this)}
}
func (this Port) String() string {
return serial.Uint16Literal(this).String()
}
type PortRange struct {
From Port
To Port
}
func (this PortRange) Contains(port Port) bool {
return this.From <= port && port <= this.To
}

View File

@@ -1,4 +1,6 @@
package json
// +build json
package net
import (
"encoding/json"
@@ -13,19 +15,6 @@ var (
InvalidPortRange = errors.New("Invalid port range.")
)
type PortRange struct {
from uint16
to uint16
}
func (this *PortRange) From() uint16 {
return this.from
}
func (this *PortRange) To() uint16 {
return this.to
}
func (this *PortRange) UnmarshalJSON(data []byte) error {
var maybeint int
err := json.Unmarshal(data, &maybeint)
@@ -34,8 +23,8 @@ func (this *PortRange) UnmarshalJSON(data []byte) error {
log.Error("Invalid port [%s]", string(data))
return InvalidPortRange
}
this.from = uint16(maybeint)
this.to = uint16(maybeint)
this.From = Port(maybeint)
this.To = Port(maybeint)
return nil
}
@@ -49,8 +38,8 @@ func (this *PortRange) UnmarshalJSON(data []byte) error {
log.Error("Invalid from port %s", pair[0])
return InvalidPortRange
}
this.from = uint16(value)
this.to = uint16(value)
this.From = Port(value)
this.To = Port(value)
return nil
} else if len(pair) == 2 {
from, err := strconv.Atoi(pair[0])
@@ -58,17 +47,17 @@ func (this *PortRange) UnmarshalJSON(data []byte) error {
log.Error("Invalid from port %s", pair[0])
return InvalidPortRange
}
this.from = uint16(from)
this.From = Port(from)
to, err := strconv.Atoi(pair[1])
if err != nil || to <= 0 || to >= 65535 {
log.Error("Invalid to port %s", pair[1])
return InvalidPortRange
}
this.to = uint16(to)
this.To = Port(to)
if this.from > this.to {
log.Error("Invalid port range %d -> %d", this.from, this.to)
if this.From > this.To {
log.Error("Invalid port range %d -> %d", this.From, this.To)
return InvalidPortRange
}
return nil

View File

@@ -1,25 +1,29 @@
package json
// +build json
package net_test
import (
"encoding/json"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
. "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestIntPort(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
var portRange PortRange
err := json.Unmarshal([]byte("1234"), &portRange)
assert.Error(err).IsNil()
assert.Uint16(portRange.from).Equals(uint16(1234))
assert.Uint16(portRange.to).Equals(uint16(1234))
assert.Uint16(portRange.From.Value()).Equals(uint16(1234))
assert.Uint16(portRange.To.Value()).Equals(uint16(1234))
}
func TestOverRangeIntPort(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
var portRange PortRange
err := json.Unmarshal([]byte("70000"), &portRange)
@@ -30,29 +34,29 @@ func TestOverRangeIntPort(t *testing.T) {
}
func TestSingleStringPort(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"1234\""), &portRange)
assert.Error(err).IsNil()
assert.Uint16(portRange.from).Equals(uint16(1234))
assert.Uint16(portRange.to).Equals(uint16(1234))
assert.Uint16(portRange.From.Value()).Equals(uint16(1234))
assert.Uint16(portRange.To.Value()).Equals(uint16(1234))
}
func TestStringPairPort(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"1234-5678\""), &portRange)
assert.Error(err).IsNil()
assert.Uint16(portRange.from).Equals(uint16(1234))
assert.Uint16(portRange.to).Equals(uint16(5678))
assert.Uint16(portRange.From.Value()).Equals(uint16(1234))
assert.Uint16(portRange.To.Value()).Equals(uint16(5678))
}
func TestOverRangeStringPort(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"65536\""), &portRange)

View File

@@ -1,6 +0,0 @@
package net
type PortRange interface {
From() uint16
To() uint16
}

View File

@@ -0,0 +1,73 @@
package assert
import (
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/serial"
"github.com/v2ray/v2ray-core/testing/assert"
)
func Address(value v2net.Address) *AddressSubject {
return &AddressSubject{value: value}
}
type AddressSubject struct {
assert.Subject
value v2net.Address
}
func (subject *AddressSubject) Named(name string) *AddressSubject {
subject.Subject.Named(name)
return subject
}
func (subject *AddressSubject) DisplayString() string {
return subject.Subject.DisplayString(subject.value.String())
}
func (subject *AddressSubject) Equals(another v2net.Address) {
if subject.value.IsIPv4() && another.IsIPv4() {
IP(subject.value.IP()).Equals(another.IP())
} else if subject.value.IsIPv6() && another.IsIPv6() {
IP(subject.value.IP()).Equals(another.IP())
} else if subject.value.IsDomain() && another.IsDomain() {
assert.StringLiteral(subject.value.Domain()).Equals(another.Domain())
} else {
subject.Fail(subject.DisplayString(), "equals to", another)
}
}
func (subject *AddressSubject) IsIPv4() {
if !subject.value.IsIPv4() {
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("an IPv4 address"))
}
}
func (subject *AddressSubject) IsNotIPv4() {
if subject.value.IsIPv4() {
subject.Fail(subject.DisplayString(), "is not", serial.StringLiteral("an IPv4 address"))
}
}
func (subject *AddressSubject) IsIPv6() {
if !subject.value.IsIPv6() {
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("an IPv6 address"))
}
}
func (subject *AddressSubject) IsNotIPv6() {
if subject.value.IsIPv6() {
subject.Fail(subject.DisplayString(), "is not", serial.StringLiteral("an IPv6 address"))
}
}
func (subject *AddressSubject) IsDomain() {
if !subject.value.IsDomain() {
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("a domain address"))
}
}
func (subject *AddressSubject) IsNotDomain() {
if subject.value.IsDomain() {
subject.Fail(subject.DisplayString(), "is not", serial.StringLiteral("a domain address"))
}
}

View File

@@ -0,0 +1,49 @@
package assert
import (
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/serial"
"github.com/v2ray/v2ray-core/testing/assert"
)
func Destination(value v2net.Destination) *DestinationSubject {
return &DestinationSubject{value: value}
}
type DestinationSubject struct {
assert.Subject
value v2net.Destination
}
func (this *DestinationSubject) Named(name string) *DestinationSubject {
this.Subject.Named(name)
return this
}
func (this *DestinationSubject) DisplayString() string {
return this.Subject.DisplayString(this.value.String())
}
func (this *DestinationSubject) IsTCP() {
if !this.value.IsTCP() {
this.Fail(this.DisplayString(), "is", serial.StringLiteral("a TCP destination"))
}
}
func (this *DestinationSubject) IsNotTCP() {
if this.value.IsTCP() {
this.Fail(this.DisplayString(), "is not", serial.StringLiteral("a TCP destination"))
}
}
func (this *DestinationSubject) IsUDP() {
if !this.value.IsUDP() {
this.Fail(this.DisplayString(), "is", serial.StringLiteral("a UDP destination"))
}
}
func (this *DestinationSubject) IsNotUDP() {
if this.value.IsUDP() {
this.Fail(this.DisplayString(), "is not", serial.StringLiteral("a UDP destination"))
}
}

View File

@@ -0,0 +1,39 @@
package assert
import (
"bytes"
"net"
"github.com/v2ray/v2ray-core/common/serial"
"github.com/v2ray/v2ray-core/testing/assert"
)
func IP(value net.IP) *IPSubject {
return &IPSubject{value: value}
}
type IPSubject struct {
assert.Subject
value net.IP
}
func (subject *IPSubject) Named(name string) *IPSubject {
subject.Subject.Named(name)
return subject
}
func (subject *IPSubject) DisplayString() string {
return subject.Subject.DisplayString(subject.value.String())
}
func (subject *IPSubject) IsNil() {
if subject.value != nil {
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("nil"))
}
}
func (subject *IPSubject) Equals(ip net.IP) {
if !bytes.Equal([]byte(subject.value), []byte(ip)) {
subject.Fail(subject.DisplayString(), "equals to", ip)
}
}

View File

@@ -0,0 +1,49 @@
package assert
import (
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/serial"
"github.com/v2ray/v2ray-core/testing/assert"
)
func Port(value v2net.Port) *PortSubject {
return &PortSubject{value: value}
}
type PortSubject struct {
assert.Subject
value v2net.Port
}
func (subject *PortSubject) Named(name string) *PortSubject {
subject.Subject.Named(name)
return subject
}
func (subject *PortSubject) DisplayString() string {
return subject.Subject.DisplayString(subject.value.String())
}
func (subject *PortSubject) Equals(expectation v2net.Port) {
if subject.value.Value() != expectation.Value() {
subject.Fail(subject.DisplayString(), "is equal to", expectation)
}
}
func (subject *PortSubject) GreaterThan(expectation v2net.Port) {
if subject.value.Value() <= expectation.Value() {
subject.Fail(subject.DisplayString(), "is greater than", expectation)
}
}
func (subject *PortSubject) LessThan(expectation v2net.Port) {
if subject.value.Value() >= expectation.Value() {
subject.Fail(subject.DisplayString(), "is less than", expectation)
}
}
func (subject *PortSubject) IsValid() {
if subject.value == 0 {
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("a valid port"))
}
}

View File

@@ -2,12 +2,14 @@ package testing
import (
"sync/atomic"
v2net "github.com/v2ray/v2ray-core/common/net"
)
var (
port = int32(30000)
)
func PickPort() uint16 {
return uint16(atomic.AddInt32(&port, 1))
func PickPort() v2net.Port {
return v2net.Port(atomic.AddInt32(&port, 1))
}

View File

@@ -1,14 +0,0 @@
package testing
type PortRange struct {
FromValue uint16
ToValue uint16
}
func (this *PortRange) From() uint16 {
return this.FromValue
}
func (this *PortRange) To() uint16 {
return this.ToValue
}

View File

@@ -19,6 +19,7 @@ type TimeOutReader struct {
func NewTimeOutReader(timeout int, connection net.Conn) *TimeOutReader {
reader := &TimeOutReader{
connection: connection,
timeout: -100,
}
reader.SetTimeOut(timeout)
return reader
@@ -33,6 +34,9 @@ func (reader *TimeOutReader) GetTimeOut() int {
}
func (reader *TimeOutReader) SetTimeOut(value int) {
if value == reader.timeout {
return
}
reader.timeout = value
if value > 0 {
reader.worker = &timedReaderWorker{

View File

@@ -1,4 +1,4 @@
package net
package net_test
import (
"bytes"
@@ -8,11 +8,13 @@ import (
"testing"
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/testing/unit"
v2net "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestReaderAndWrite(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
size := 1024 * 1024
buffer := make([]byte, size)
@@ -25,11 +27,11 @@ func TestReaderAndWrite(t *testing.T) {
transportChan := make(chan *alloc.Buffer, 1024)
err = ReaderToChan(transportChan, readerBuffer)
err = v2net.ReaderToChan(transportChan, readerBuffer)
assert.Error(err).Equals(io.EOF)
close(transportChan)
err = ChanToWriter(writerBuffer, transportChan)
err = v2net.ChanToWriter(writerBuffer, transportChan)
assert.Error(err).IsNil()
assert.Bytes(buffer).Equals(writerBuffer.Bytes())
@@ -127,22 +129,22 @@ func runBenchmarkTransport(size int) {
finishB := make(chan bool)
go func() {
ChanToWriter(writerA, transportChanA)
v2net.ChanToWriter(writerA, transportChanA)
close(finishA)
}()
go func() {
ReaderToChan(transportChanA, readerA)
v2net.ReaderToChan(transportChanA, readerA)
close(transportChanA)
}()
go func() {
ChanToWriter(writerB, transportChanB)
v2net.ChanToWriter(writerB, transportChanB)
close(finishB)
}()
go func() {
ReaderToChan(transportChanB, readerB)
v2net.ReaderToChan(transportChanB, readerB)
close(transportChanB)
}()

View File

@@ -6,10 +6,12 @@ import (
)
var (
RetryFailed = errors.New("All retry attempts failed.")
errorRetryFailed = errors.New("All retry attempts failed.")
)
type RetryStrategy interface {
// Strategy is a way to retry on a specific function.
type Strategy interface {
// On performs a retry on a specific function, until it doesn't return any error.
On(func() error) error
}
@@ -17,6 +19,7 @@ type retryer struct {
NextDelay func(int) int
}
// On implements Strategy.On.
func (r *retryer) On(method func() error) error {
attempt := 0
for {
@@ -26,14 +29,15 @@ func (r *retryer) On(method func() error) error {
}
delay := r.NextDelay(attempt)
if delay < 0 {
return RetryFailed
return errorRetryFailed
}
<-time.After(time.Duration(delay) * time.Millisecond)
attempt++
}
}
func Timed(attempts int, delay int) RetryStrategy {
// Timed returns a retry strategy with fixed interval.
func Timed(attempts int, delay int) Strategy {
return &retryer{
NextDelay: func(attempt int) int {
if attempt >= attempts {

View File

@@ -5,15 +5,16 @@ import (
"testing"
"time"
"github.com/v2ray/v2ray-core/testing/unit"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
var (
TestError = errors.New("This is a fake error.")
errorTestOnly = errors.New("This is a fake error.")
)
func TestNoRetry(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
startTime := time.Now().Unix()
err := Timed(10, 100000).On(func() error {
@@ -26,14 +27,14 @@ func TestNoRetry(t *testing.T) {
}
func TestRetryOnce(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
startTime := time.Now()
called := 0
err := Timed(10, 1000).On(func() error {
if called == 0 {
called++
return TestError
return errorTestOnly
}
return nil
})
@@ -44,14 +45,14 @@ func TestRetryOnce(t *testing.T) {
}
func TestRetryMultiple(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
startTime := time.Now()
called := 0
err := Timed(10, 1000).On(func() error {
if called < 5 {
called++
return TestError
return errorTestOnly
}
return nil
})
@@ -62,19 +63,19 @@ func TestRetryMultiple(t *testing.T) {
}
func TestRetryExhausted(t *testing.T) {
assert := unit.Assert(t)
v2testing.Current(t)
startTime := time.Now()
called := 0
err := Timed(2, 1000).On(func() error {
if called < 5 {
called++
return TestError
return errorTestOnly
}
return nil
})
duration := time.Since(startTime)
assert.Error(err).Equals(RetryFailed)
assert.Error(err).Equals(errorRetryFailed)
assert.Int64(int64(duration / time.Millisecond)).AtLeast(1900)
}

23
common/serial/bytes.go Normal file
View File

@@ -0,0 +1,23 @@
package serial
type Bytes interface {
Bytes() []byte
}
type BytesLiteral []byte
func (this BytesLiteral) Value() []byte {
return []byte(this)
}
func (this BytesLiteral) Int64Value() int64 {
value := this.Value()
return int64(value[0])<<56 +
int64(value[1])<<48 +
int64(value[2])<<40 +
int64(value[3])<<32 +
int64(value[4])<<24 +
int64(value[5])<<16 +
int64(value[6])<<8 +
int64(value[7])
}

57
common/serial/numbers.go Normal file
View File

@@ -0,0 +1,57 @@
package serial
import (
"strconv"
)
type Uint16 interface {
Value() uint16
}
type Uint16Literal uint16
func (this Uint16Literal) String() string {
return strconv.Itoa(int(this))
}
func (this Uint16Literal) Value() uint16 {
return uint16(this)
}
type Int interface {
Value() int
}
type IntLiteral int
func (this IntLiteral) String() string {
return strconv.Itoa(int(this))
}
func (this IntLiteral) Value() int {
return int(this)
}
type Int64Literal int64
func (this Int64Literal) String() string {
return strconv.FormatInt(this.Value(), 10)
}
func (this Int64Literal) Value() int64 {
return int64(this)
}
func (this Int64Literal) Bytes() []byte {
value := this.Value()
return []byte{
byte(value >> 56),
byte(value >> 48),
byte(value >> 40),
byte(value >> 32),
byte(value >> 24),
byte(value >> 16),
byte(value >> 8),
byte(value),
}
}

36
common/serial/string.go Normal file
View File

@@ -0,0 +1,36 @@
package serial
import (
"strings"
)
// An interface for any objects that has string presentation.
type String interface {
String() string
}
type StringLiteral string
func NewStringLiteral(str String) StringLiteral {
return StringLiteral(str.String())
}
func (this StringLiteral) Contains(str String) bool {
return strings.Contains(this.String(), str.String())
}
func (this StringLiteral) String() string {
return string(this)
}
func (this StringLiteral) ToLower() StringLiteral {
return StringLiteral(strings.ToLower(string(this)))
}
func (this StringLiteral) ToUpper() StringLiteral {
return StringLiteral(strings.ToUpper(string(this)))
}
func (this StringLiteral) TrimSpace() StringLiteral {
return StringLiteral(strings.TrimSpace(string(this)))
}

View File

@@ -0,0 +1,15 @@
package serial
type StringLiteralList []StringLiteral
func NewStringLiteralList(raw []string) *StringLiteralList {
list := StringLiteralList(make([]StringLiteral, len(raw)))
for idx, str := range raw {
list[idx] = StringLiteral(str)
}
return &list
}
func (this *StringLiteralList) Len() int {
return len(*this)
}

View File

@@ -0,0 +1,25 @@
// +build json
package serial
import (
"encoding/json"
"errors"
"strings"
)
func (this *StringLiteralList) UnmarshalJSON(data []byte) error {
var strarray []string
if err := json.Unmarshal(data, &strarray); err == nil {
*this = *NewStringLiteralList(strarray)
return nil
}
var rawstr string
if err := json.Unmarshal(data, &rawstr); err == nil {
strlist := strings.Split(rawstr, ",")
*this = *NewStringLiteralList(strlist)
return nil
}
return errors.New("Unknown format of a string list: " + string(data))
}

104
common/uuid/uuid.go Normal file
View File

@@ -0,0 +1,104 @@
package uuid
import (
"bytes"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"errors"
)
var (
byteGroups = []int{8, 4, 4, 4, 12}
InvalidID = errors.New("Invalid ID.")
)
type UUID [16]byte
func (this *UUID) String() string {
return bytesToString(this.Bytes())
}
func (this *UUID) Bytes() []byte {
return this[:]
}
func (this *UUID) Equals(another *UUID) bool {
if this == nil && another == nil {
return true
}
if this == nil || another == nil {
return false
}
return bytes.Equal(this.Bytes(), another.Bytes())
}
// Next generates a deterministic random UUID based on this UUID.
func (this *UUID) Next() *UUID {
md5hash := md5.New()
md5hash.Write(this.Bytes())
md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))
newid := new(UUID)
for {
md5hash.Sum(newid[:0])
if !newid.Equals(this) {
return newid
}
md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2"))
}
}
func bytesToString(bytes []byte) string {
result := hex.EncodeToString(bytes[0 : byteGroups[0]/2])
start := byteGroups[0] / 2
for i := 1; i < len(byteGroups); i++ {
nBytes := byteGroups[i] / 2
result += "-"
result += hex.EncodeToString(bytes[start : start+nBytes])
start += nBytes
}
return result
}
func New() *UUID {
uuid := new(UUID)
rand.Read(uuid.Bytes())
return uuid
}
func ParseBytes(b []byte) (*UUID, error) {
if len(b) != 16 {
return nil, InvalidID
}
uuid := new(UUID)
copy(uuid[:], b)
return uuid, nil
}
func ParseString(str string) (*UUID, error) {
text := []byte(str)
if len(text) < 32 {
return nil, InvalidID
}
uuid := new(UUID)
b := uuid.Bytes()
for _, byteGroup := range byteGroups {
if text[0] == '-' {
text = text[1:]
}
_, err := hex.Decode(b[:byteGroup/2], text[:byteGroup])
if err != nil {
return nil, err
}
text = text[byteGroup:]
b = b[byteGroup/2:]
}
return uuid, nil
}

69
common/uuid/uuid_test.go Normal file
View File

@@ -0,0 +1,69 @@
package uuid_test
import (
"testing"
. "github.com/v2ray/v2ray-core/common/uuid"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
func TestParseBytes(t *testing.T) {
v2testing.Current(t)
str := "2418d087-648d-4990-86e8-19dca1d006d3"
bytes := []byte{0x24, 0x18, 0xd0, 0x87, 0x64, 0x8d, 0x49, 0x90, 0x86, 0xe8, 0x19, 0xdc, 0xa1, 0xd0, 0x06, 0xd3}
uuid, err := ParseBytes(bytes)
assert.Error(err).IsNil()
assert.String(uuid).Equals(str)
}
func TestParseString(t *testing.T) {
v2testing.Current(t)
str := "2418d087-648d-4990-86e8-19dca1d006d3"
expectedBytes := []byte{0x24, 0x18, 0xd0, 0x87, 0x64, 0x8d, 0x49, 0x90, 0x86, 0xe8, 0x19, 0xdc, 0xa1, 0xd0, 0x06, 0xd3}
uuid, err := ParseString(str)
assert.Error(err).IsNil()
assert.Bytes(uuid.Bytes()).Equals(expectedBytes)
}
func TestNewUUID(t *testing.T) {
v2testing.Current(t)
uuid := New()
uuid2, err := ParseString(uuid.String())
assert.Error(err).IsNil()
assert.StringLiteral(uuid.String()).Equals(uuid2.String())
assert.Bytes(uuid.Bytes()).Equals(uuid2.Bytes())
}
func TestRandom(t *testing.T) {
v2testing.Current(t)
uuid := New()
uuid2 := New()
assert.StringLiteral(uuid.String()).NotEquals(uuid2.String())
assert.Bytes(uuid.Bytes()).NotEquals(uuid2.Bytes())
}
func TestEquals(t *testing.T) {
v2testing.Current(t)
var uuid *UUID = nil
var uuid2 *UUID = nil
assert.Bool(uuid.Equals(uuid2)).IsTrue()
assert.Bool(uuid.Equals(New())).IsFalse()
}
func TestNext(t *testing.T) {
v2testing.Current(t)
uuid := New()
uuid2 := uuid.Next()
assert.Bool(uuid.Equals(uuid2)).IsFalse()
}

249
core.go
View File

@@ -8,13 +8,256 @@ import (
)
var (
version = "0.14"
version = "1.4"
build = "Custom"
codename = "Post Apocalypse"
intro = "A stable and unbreakable connection for everyone."
codename = "New Order"
intro = "An unified platform for anti-censorship."
)
func Version() string {
return version
}
func PrintVersion() {
fmt.Printf("V2Ray %s (%s) %s%s", version, codename, build, platform.LineSeparator())
fmt.Printf("%s%s", intro, platform.LineSeparator())
}
/*
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:::::::::::::::::::::::ccc:::cccc::::::c:::::cccc::::cc::::::::::::::::::::::::ccccccc::cccccccccc::cc:::;:::::::::::::::::::::::::::ccc:::cccc:ccc::::::c:::::::::::::::::::::::::::cccc:cccccccccccccccccccccc:;;::::::::::::::::::c::::ccc::::::ccc:::ccc::cccccc::::cc:cc:::ccccccccccccccccccccccccccccccccccccccc
:::;::::::::::::::::::::::::::::::::::::::::::::::cc:::::::::::::::::::::::::::::::::::::ccc::cc:::::c:::::::::::ccc::ccc:::::ccc::::::cc:::::cccc:::::::::::::::::::::cccccccccccccccccccccccccc:::::::::::::::::::::::::cc::ccc::::::::::::::c::c::::::::::::c::::::::c::::::cccccccccccccccccccccccccc:;:::::::::::::::ccc::cccccccc::c:cccc::cc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc::::::::cc::::::::::::ccccc:::::ccccccccc::cccc::ccccccc:::c::::::::c:c:::::cccccccccccccccccccccccccc::::::::::::::::::::::::cc:::::ccc:::c::::::::::::::::c::::::::::::::::::::::ccccccccccccccccccccccccccc:::::::::::::::::::::cccccc:ccccc::ccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::;;::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:::::::::::::::::::::cc::::::::::::::::c:::cc::::c::::::::cccccc::::cccccccccc::ccccccc::c::::::::c::::::::ccccccccccccccccccccccccc:::::::::::::::::::::::::c::::cccc:::cc:::cc:::::::::::::::::::::::::::c::::::ccccccccccccccccccccccccccc::::::::::::::::::::cccc::cccccc:ccc:cccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc:::::::::::::::ccc:::::c:::cc::::::::::::::::::::::cc:::c::cccc::::::::::cccccccccccc:ccc::c:::::::ccc:::::::cccccccccccccccccccccccccc:::::::::::::::::::cccccccccccccc::::::::ccc::c::cc:::cc::::::::::::::cc:::::cccccccccccccccccccccccccccc::::::::::::::::::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::::::::::::::::::::::::::::::::::::::::::::::::::::::c::::::::::::::::::::c::::::::::::::::::::ccccc::::::::::::ccc::cc:::::cccc::::cccccccccccc::::cccccc:::ccccc:::::cccccccccccccccccccccccccc::::::::::::::::::::c:cc:::cc::::::::::::cc::cc:::::::cccc::::::c:::::cc::::::ccccccccccccccccccccccccccc:::::::::ccc::::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc:::::::::::::::ccccccc:::::ccccc::ccc::ccccccccccccccccccccccccccccccc:cccc::::cccccc::::cccccccccccccccccccccccccc::::::::::::::::c:::::cc::::::::cc:::cccc::::cc::::ccc:ccc::c:::::::::cccc::::ccccccccccccccccccccccccccc::::::::::cc::::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::::::::::::::::::::::::::::::::::::::::::::::::::::c::::::::cc::::::::::::::cc:::::::ccccc:::c:::::ccc::cccc:::cccccc:ccccccccccccccccccccccccccccccccccc::::ccccccc:::cccccccccccccccccccccccccc:::::::::cc:::::::::::::cc:::cc::cccccccc::cccccc::ccccccccc:c::::::::ccc:::::cccccccccccccccccccccccccccc:::::::::ccc:::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::::::::::::::::::::::::::::::::::::::::::::cc:::::::::ccc:::::c::::::cc:::cccc:::c:::ccccc::::ccc::cccccccccc::cccccc::ccccccccccccccccccccccccccccccccccc:ccccccccc::ccccccccccccccccccccccccccc:::::::::::::::::::::::::cc:cccc::cccccccccccc:ccc::cccccc:ccc::::::::cccc:::::ccccccccccccccccccccccccccc:::::::::ccc:::::c:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::::::::::::::::c:::::::c:::::c:::::cc::::::::::::cccccc::::ccccccccc:::::::ccccccccccccccccccccccc:cccccccccccccccccccccccllccccccccccccccccccccc::cccccccccccccccccccccccccccc:::::::::c:::::::::::ccccccccccccccccccccccc::cc:::::cccccccc:::cc:::ccccc::::ccccccccccccccccccccccccccc:::::::::cccc::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::::::::::cc::cc:::::::c:::::::::::::c:::cc:::::::cccccc::::cccccccc:cc:::ccccccccccccccccccccccccccccccccccccccccccccccoxk00Okxddoolllllloodxkkxdoloolccccccccccccccccccccccccc:::::::::c:::::::::cccccccc::cc::ccccc:::cccccc:ccc:ccccccccc:ccccc:::cc:cc:::ccccccccccccccccccccccccccc:::::::::ccccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::::::::::::::::::c::::::::ccc:::cc:::::::c::::cc::c::::c:::ccc:::ccc::::::ccc:::ccccccccccccccccccccccccccccccccccccccccccccloxxkOO0OkkkkOKXK00000000KKK0KKXXNNWWWWNXKKK0kdoolclloooddollccccccccc:::::::::cc:::::cccccccccccc:ccccccccc::cccccccccccccc:cccccc:ccccc:::cc:cc:::cccccccccccccccccccccccccccc:::::::cccccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::::::::::::::::::::c::ccc:::cc::cc::ccc:::::cc:::cc::::ccc::cccc:cccccccccccccccccccccccccccccccccccccccdkO0KKK0000OOkkOKXXXXXXXXXNNXKXNNNNNNNNNNNXXXXXKKKOO0KKKKKKK0Oxxoolllcc:::::::::cc::::ccc::cccc:cccccccc:ccccccccccccccccccccccccccccccc:cc:::cc:::::ccccccccccccccccccccccccccccc::::::::c::c:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::::::::::ccc::::c:::c:ccc::c::ccccc:ccc::::ccccccccc:cc:cccccccccccccc:ccccccccccccccccccccccccccccccccccccccclxOOOO00KKKKKKK00KKXXNNNNXNNNNXXXXXNNNNNNNNNXXKKXXXXXXXXXXXKKXXNXXK00KOdl::::::::ccc:::::cccccccccccc:cccc::ccccccccccccccccccccccccccccc::cc:::ccc:cc::ccccccccccccccccccccccccccccc:::::::cccc::::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::::::::::::::::::ccccccccc:cc::cccc::ccc:ccccc:ccc:::ccccccccccccccccccccccccc:ccccccccccccccccccccccccccccccccccclok000O0XNNNNNXK00KKXK0K0xxk000KXXXXXNNNNNNNXNNNNNNXXXXXNNXXXXXKX0dodxxxxddxOOxl::::::::cccc::::ccccccccccccccccccccc::cccccccccccccccccccccccccccccc::cccccc:::cccccccccccccccccccccccccccc::::::::ccccc::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:::::::::::::c::::cccc:::c::ccccccccc::cc:ccccc:ccc::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclx0XNNNXNXXXNNNXKKKKXXX0OOxooddoodkKXKKKKXNXXNXXXXKKKKXX00KXKKXXNNXK0kko:;;::cccccc::::::::ccc::::ccccccccccccccccccccccccccccccccccccccccccccccccccccc::cccccc:::cccccccccccccccccccccccccccc::::::::cccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::::::::::::::cc:ccc::ccccc:cccccccccc::cccccccccccccccccccccccccccccc:cccccccccccccccccccccccccccccccccccccccclxKNNNNXOO0KOkO0KXXNXXXXK0kololllol:lO0kkxkOKXXXXK0kkxddxO000OOkOO0KKXNNNXOxl:::coolc::::::::ccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccc:::cccccc:::ccccccccccccccccccccccccccccc::::ccccccccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
:ccc::::::::::cc:c:::cccccccccccccccccccccccccccc:ccccccccccccccccccccccccccccccccccccccccc:ccccccccccccccccccoOXNWWNN0dcllldO0KKXXXXXXXXKxllldkkkxxdlc;;lOK00OOOd:;;;:cdk0OxkxoodxxkOOOOO0Okl,cO0kl::::::::ccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccc::cccccc::ccccccccccccccccccccccccccccc:::cc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
::cc:::::::::c::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldO00KNNNXKOddk0KXXXXXXXXXXKK0OOOOO0KKxc;,,.;oxxddxkOx:;,;loddxxolddlc::ccccc:;cooox0KKkl:::::ccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccccccccccccccccccccccccccc:::::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
c:cc:::c::cccccc::ccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldkkxdxkKK000kxkkOO0000OOOOOkdloOKKO0K0dc,...;ccc:lxO0dccclllccloolllc:;;;:cc:;.,:cok0Okkkl::::cccccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccccccccccccccccccccccccccc:::::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
ccc::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclx0KK0OxdxOOOxl::codxxdoooolccoxO0K00kdoodl;''',;;;:lxOOdollccc::cc:;;;;,'',;;;;'','';:coxxl::::cccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccc:cccccc::cccccccccccccccccccccccccccccc::c::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllccccccccccccccccccccc
cc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldxOKXNXKOxxdl:;;,,,,,,,ckOx;:k00Okkxolc;;;:;,''',,'',:ok0kocc::,.',,;;;;,''',;,;,...',;:ccc::::::ccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccclcccclcccccccccccccccccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllcc
ccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccooloOKKKK0Oo:;,''..'.. ;xO0OxxOkxdddddoll:;,..,,,,;'.'',:ll:,,,,'..'''',,;;,,,,;;;,',,;;::cc:cccc:ccccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccllccccccclcclllcccccccclcc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllccccccccccccc
cccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclollxOOOO0Oxl:;'''',;;;cdxodxdoolllldk00Od:,'..,;,;;;,,'.....'',,,,,'''.''''.....,,,''',::ccc:cccc::ccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:::cccclcccllcclcccllcccccccclcc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllcclcclcccccccccccccccc
ccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclxkxdddxO0Oxdoc;'',;:ldxdlc:coolcc::ldxolc;;::,..,,;;;;;;'...'';cccc::;,,,,,,,'....,',;:oolccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::cclcccccllccccclccclcllllclccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllccccclllcccccccccccccccccccccl
cc::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclxOOxddxkkkdoool:;,.';ccc:::::;::::::cc;;;:cdddo:'''',;;;;;;,,;:codddolc:;;,,,;,,''',,,,;:llllc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllclllcccccccclllllllllllcccccccccccccccccccccccccccccccccccccccccccccccccccllccllccccccllccccccccccccllcccccccllcccccc
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclxkkkkO00koc;,'.';;,...;;::;::;,,;:::::::;;;::::::;'''',,,,;,;;:loxkkOkxdolc:;;;,,,'...',:lllclccccc::cccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:cclllllllclllcccclllllllllllcccccccccccccccccccccccccccccccccccccccccccccccccccllcclllcccccccccccccccccllclccllccclccccccc
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldddkOOO00OOOOxl,.';;'...,;;;;;;,;;;;:::;;::;,'',;;,',,,,,,,,;;:loodxxxxxxxddoolc:;;,...;cdkkdlllc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllcllllllccclllllllclllllcccccccccccccccccccccccllccccccccccclccclcccllccccllcccccccccccccccllcclllccclccllccccccccccc
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccloodOOkkkOOOOOOkxc,;:,. ..',;;;;;;'',,,,,,,;,...,;,,;;;,,,;;;:clllllcc::clodxxddolc;'',;:ccodlccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:cclllllllllllllllllllllllllllc:ccccccccccccccccccccllccclllcccccccccllcccccccccccccccccclccccccllcccccccclccccccccccccccc
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclollxOkxxxxxxxddddoc;'.... ..,;;;,'....';,.','.',,;::::;;;;;::cloddddooc:;::cclddolc:;:::::cllllc:cccccccclcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclccccc:ccllllllllllllllllllllllllcllc:cccccccccccccccccccccccccllccccccccccccccccccccllccccccllllcccccccccccccccccccccccccccccll
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclccdkkxdooollccc:::;'.......... ..,,,;,''',;,.''',,,;:ccc:::::::::::cccclccc::::::cloolllc:ccccccllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclccccc::cllllllllllllllllllllllllllllc:cccccccccccccccccccccccccccccccccccccccclllllccccclllccccccccccccccccccccccccccllllllllll
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccoxdxkdl::::::cc;,,''.. .....',,,;;,,,,,'',;;:cloodollc::;;;;:;;,,;;;;::::::c:looooolcclllllllcccccccccccccccccccccccccclllcccccccccccccccccccccccccccclccclccccccccccccccccccccccllllllllllllllllllllllllllllc:cccccccccccccccccccccccccccccclllcclllllllllllcccccccccccccccccccccccccclllllllllllllllll
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllloooc:;,'',;;,,;:..... ....'',,,,,,,,;;;:ccooddxxxxxdol:;;;:ccc::;;;,;;;;;;clollllllllllllllcccccccccccccccccccclccccclccccccccccccccccccccccclcccccccccllcccclcccccccccccccccccllllllllllllllllllllllllllllccccccccccclcccccccccccclllclllllllllllccclllccccccccccccccccccccccllllllllllllllllllllllll
cccccccccccccccclccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::;,,;:;.. ';;';c;'';;'. ....'',,,;;;;;;:clodddxxxxkkkkkkxdoddxxdol:,,,,;;,;:oxdlllllllllllllllcccccccccccccccclcccccccclccccclllcccccccccllcccclcccccccclllccccccccccccccccccccccclllllllllllllllllllllllllllcccccccccccccccllcccccccccccclcccccccccccccccccccccccccccccclllllllllllllllllllllllllllllll
ccc::cccccccccccccccccccclccccllcccccccccccclcccccccccccccccccccccccccccccccccccccccccccccccccclccol:;,,''''....','';:;,:;. ...',,,,;;;;;:ccloddxxxxxkkOOOO000Okxddool;'',,;;:loxOkoclllllllllllllcccccccccclccccccccccccccccclccllllllcccccccclcccccccccccclllcccccccccccccccllccccccllllllllllllllllllllllllllllccccccccccccccllccccccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllll
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclcccclllccccccccccccccccclllllo:;,,,',,,,,,,,'';::::,. ..',,;;;::::cccooddxxxxkkkOOO00000Okxolllc;;:::coxkO00dllllllllllllllccccccccccccccccccccccllllcclcclllllllcccccclllccccccccccclcccclcccccccccccccccccccclllllllllllllllllllllllllllllcccccccccllccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllll
ccccccllccllccccccccccccccccccccccccccccccccccccccccclccccccccccccccccccccclllcclccccccllcccclcclccc:;;;,,,,,,;;;::;;:c::;'. .....',;;:::::ccllloddxxxxxkkkkOOO000000OkxdolooddxkOOO00Oolllllllllllllcccccccccccccccllllcclllllcllcclllllllcllccllllcccccccccccccccccccccccccccccclllcccccllllllllllllllllllllllllllllc:cccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllll
ccccccccccccccclccllccllllllcccclccccccccccccccccccccccccccccccccccccccclllllllllcccllcclllllllcllcc:::::;;,',;;;:cl::lc:;;,,,,'...,;:::ccclollllooddddddxxxxkkkkOO00000OOOOOOO00OOOOO00xlllllllllllllccccccccccclcccclllcclllclccccclccccccccccccccccccccccccccccccccccccccccllcllllllccccllllllllllllllllllllllllllllc:cccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
ccccccccccccccccccccclcccllcccclllclllclllllccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:::;''...,;;;::;;:clcc:::::;;,;:::cccloooooooooooodddddddxxxkkOOOOO0000000KKKK00O0KOollllllllllllccccccccccccccccccccccccccccccccccccccccccccccccccllllllllllllllllllllcccccclcccccccclllllllllllllllllllllllllllllc:ccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
cccccccccccccccccccccccccccccccccccccccccclllclccclcccllllllllcclllccccccccccccccccccccccccccccccccccc:;,.....,;;;:;;,,;;::clllllcc::ccllllllloooooooooooddddddxxkkkOOOOOOOOOOOOOO00K0KKKxllllllllllllcccccccccccccccccccccccccllllcclllllllllllcclllllllllllllcccllccllllllclccccccccccccccllllllllllllllllllllllllllllccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
cccccccccccccccccccccccccccccccccccccccccccccccccccccccllcccccccccccllcccccclllccllllllllllllllllllllcc:;;,..';:::;;,,'.'',,;::cc:::clloooolloddodddoooooddddddxxxkkOOOOOOOkxodxkO00KKKXXOolllllllllllccccccclccclllllllllllcclllcllllcllllllllllllcccccllllcccccccccccccccllcccccccccccc::clllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllool
llllllllllllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllclllllllcc:;,..,;;;,,,,,''...',,;;;;;:loooooddddddddddddoooddddddxxxkkkkkkkkkxddOKXXXXKKXXXKxllllllllllllcccccccccccccccllcclllcllccclccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
llllllllllllllllllllllllllllcllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllccc:,'';:;,,,,,,;,'...',,,,;;;clooddddoodddddddooddooodddddxxxxkkkkkkkOkxkkxdxxkkxxkOkolllllllllllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllolllllllllllllllllllllllllllooloooloo
llllllllllllllllllllllllllllllllllllllllllllllcllcccccccccccccccccccccccccccccccccccccccccccccccccccccc:,.,cllc:;;;;;;;,,'',;;;;;;:llooddddodddddddddddooooodddddxxxkkkkkO0KOdc;..'',:cccoollllllllllllccccccccccccccccccccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllloollllllllllllllollllolooooooooo
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllcccccccccccccccccccccccccccccccccc::;;cllol:;;;:::;,'',;;;;;;:clloooodddooddddddoooooodddddddxxxkkOOO00K0ko:;;;:;clllllllllllllllllc:ccccccccccccccccccccclccclllcllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollllllllllllloooolllllloooooooooollolll
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllcllollcccc:;,'',;;,,,,;;;;;:::ccllloooooooooooooooooooodddddddxxkkkkkOO0KK0kdlclolllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollllllllllllllllllllllllllllllllllllllllllllllllllllllllloooollllloollooolllloooooooollooooooloolllllcc
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollool::;,;;;:;'..',,,,;;;::::::ccclllllllooooooooooooooddddddddxxxxxdddxO0000Oxllollllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllolllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollllllloollloollloooooooooollooollolllllcccc::::::
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllldo:;;:cllll:'...;;;::ccccccccccccccllllllllloooooooooooodddddddddolclooollloollllllllllllllllllllllollllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooolllloooollllllloooollloooloooooolooooooooolllllcccc::::::::::::::
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc:clooooollcc:;..';::clllllolcccccccccccllllllllllllloooooooooddddxkkkxdxxxxoclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllloollllloollllllolllllllllllllllollllllllloooolllooooooolllllllloolloolloooloooolllllcccc::::;:::::::::::::::::
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc:::::::c:::;,',;;:clooooddolcccccccccccccccllllllllloooooooddddxkOOOkdlloddooloollllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllloollllllloolloollllollllllllllllllollooollllllllllloooloooooooolllloollllllloooooolllllllccc::::::;::::::::::::::::cccccccc
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc:::::::::;;,'''.,:cloodddddolcccccccccccllcclccclllllloooooddxxkOOOOkdoccclooooolollllllllllolllllllllllllllllollllllllllllllllllllllllllllllllllllllllllllllllllllllllllloolllllllllllllollooooooooooooolllloooooollllooooollllloooollooooooooooooooooollllllccccc::::::::::::::::::::::::ccccccclllllllo
lllllllllllllllllllllllllllllllllllllllllolllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc::;:::;;::cc:;'.,:coodddxxddolc:cccllllllllllllclllllllllooodxxkO000K00kdoooolllllllllollllllllolllllllllolllloolloolllllllllllllllllllllllolllllllllllllllloolllllllllllllloollllooooolloooooooooooooooooooooooooolllloollooooooooooollooooooollllllcccc:::::::;:::::::::::::::::ccccccclllllllllllllllll
ooollllllllloolllllllllllllllllllllllllooollllllllllllllllllllllllllllllllllllllllllllllllllllllllloolc:;;;:::cclllllc:clodddxxxxxdolc::cccllllllllllllllllllllllooddxkO00KKKKK0kdooollllooloooooollooollllllllloolllllllllllllllllllllllllllllllllllllllllllooolllollllllllllllllllolloollooolooloolooooooooooooooooooooooolooollooooooollllllllccccc::::::::;:::::::::::::::ccccccccllllllllloolllllllllccc:::
lloooolllollloollllllllllllllllllllllllllooollllllllolllllllllllloolllllllllllolloolllllllllllllllllollc:;;:::::::::cclllodddxxxxxxddlc::::ccccccclllllllllllllllllloodxxkOOOOkkxdolooooooooooooolllooolllllllllooollloolllllllloolllooloollloolllllloollllooooollllollloollloolooloooooolloollooooooloooooooooooooooooooooooooollllllccccc:::::::;::::::::::::::::::::ccccccclllllllllloollllllllcccc::::cccccl
ooooolllllllllollllllolllllllolllooolllllloooollllllllllllollllllllllolllllllllllllllllllllllllloolllllccc:c::::::;;::lloodddxxxxkkxxdolcc::ccccccclcclllllllllllllllllloooddddoddoooooooooooooooooooooooooooooooloooooollllllllooollllllooollooolllooooooooolllloooooolllllloolllooooolooooollooooooooooooooooooooooooooooolc:::;;;;;;:;;;::::::::::::::::cccccccclllllllllllloollllllllcccc:::::ccccclllllllll
lllllllloooooolllooooollollooolllooooooolllooooolllllllllloooooooollooooooollooooollllooooolllooolloooolcccccc:::::::clooodddxxxkkkkkxdddoollllcccccclllllllllllloooollllloooooooooooooooooooooooooooooooooooooooollloooooooolllooolllllllllloooooooooooooooollooloooooooooooollloooooolooooooooooooooooooooooooooooooooooool:;,,;;;;;::::::::::cccccccccclllllllllloollolllllllccccc:::::ccccclllllllooolloollo
:::c:cccccclllllllooooooooooooolloooooollooooooolooolllooooollooolllooooooolooooooolooooollooooooloooooolc::::::;;;:clooooodddxxxxkkkkkkxxxdddolllcccccclllllllloooooooooooddddoooooooooooooooooooooooooooooooooooooooooooooooolooooolooolloooooooooooooooooooooooooooooooooooolloooooooollllllloooooooooooooooooooooooooooolc:;;;;:::::cccccccllllllllllloollllllllllccccc::cc:cccccclllllllloooooooooooooooolo
::::::::::::::::::cccccccclllllllllllooooooooooooooollloooooolooooooooooooooooooooooooooolooooooooooooool:::::::;;;cllooooodddxxxxkkkkkkkkkxxxddoolllcccllllloooooooodddddddddoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooolllllllllllcccccc::::::cloooooooooooooooooooooooooooolcccccccllllllllllllooolllllllllccccc::::cccccclllllllllooooooooooooooooooooooolooo
:::::::::::::::::::::::::::::::::::cccccccllllllllllllllllloooooooooooooooooooooooooooooooooooooooooooollc::c::::;:loooooooddddxxxkkkkkkkkkkxxxdddooollccccllllllooodddooooooooooooooooooooooooooooooooooooooooodxxkkOOOkkkkkxdooooooooooooooooooooooolollllllllllccccccc:::::::::::::::::::::::loooooooooooooooooooooooooooolllllllllllllolllllllccccccccccccccccclllllllooollooooolooooooooooooooooloooooooooo
lllllccccccccc:c:::::::::::::::::::::::::::::::::::::::cccccccccccllllllllllooooooooooooooooooooooooooooolc:::::::clooooooooddddxxxkkkkkkkkkkxxxxxddddoollcclllloooooooooooooooooooooooooooooooooooooooooooodxkO0KKK000OOOOOOOOkkkxolllllllllccccccccc::::::::::::::::::::::::::::::::::::::::::loooooooooooooooooooooooooooollllllllllllool::::::cccccccllllllllooooooooloooooooooooooooooooooooooooooooooooooo
loolooollollllllllllllccccccccc:::::::::::::::::::::::::::::::::::::::::::::cccccccccccccclllcllllllllllllc::::::cloooooooooddddxxxkkkkkkkkkxxxxxxxxxxxxxddollllooloooooooooooooooooooooooooooooooooooooolloO0OO0KNNNNNXK0OOOOOOOOkkxlc::::::::::::::::::::::::::::::::::::::cccccccccccllllllcclooooooooooooooooooooooooooooolllllllllollolccclllllllooolloooooollloolloooooolooooooooooooooooooooooooooooooooo
llllooooooooooooooooooooolllllllllllllcccccccccccccccc::c::::::::::::::::::::::::::::::::::::::::::::::::::::::;;:loodddoooooddddxxxkkkkkkkxxxxxxxxxxxxxxxxdolllolcllccccclooooooooooooooooooooooooooooooloxkxdxkO0KXXNNNNX0OxxkOOOOO0ko::::::::::::::ccccccccccccccccccllllllllllllllllolooollllloooooooooooooooooooooooooooolllllllloollollllllloooloooooooooooooooooolooooooooooooooooooooooooooooooooooooooo
::::ccccccllllllllllllloooloooooooooooloooollllllllllllllllllcccccccccccccccccccccccccccc::::::::::::::::::::::::cloooddoooooddddxxxkkkkkkkxxxxxdxxxxxxxxddddoollccll:::::cooooooooooooooooooooooooooooodddollllodxkkO0KXXNNNK0xddxkO00Odccccccccclllllllllllllllllloooolloooooooolllollllloollllloooooooooooooooooooooooooooollllllllollooolllllloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
llccccccccccc::cccccccccccllllllllloooooooooooooooooooooooooooooooolllllllllllllllllllllllllcclccccccccccccccc:::clooddddoooooddddxxxkkkkkkxxxddddxxdddddddddooollclolc:::coooooooooooooooooooooooooooxkkxlllllllllooxkO00KXXNNX0xooddddxdollllllooooooooooooooooooooolloolllllllcclcccclllloollllooooooooooooooooooooooooooooolllloloooooooollooloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
lllloooolllllllllllccccccccccccccccccccccclllllllllllllooooooooooooooooooooooooooooooooooooooooooooollllllllollcccooddddddooooodddxxxkkkkkkkxxxxxxxxxdddddddoooollllodollclooooooooooooooooooooooooodxxxxollc:;,;:clllodxkO0KXXNNNKOxdoooxdoolooooooooollllllllllllcccccccc::cc:ccccccccloolllllllooooooooooooooooooooooooooooolllollooooooolllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooolllllllllllllcccccccccccccccccccccccccllllllllllllllllooooooooooooooooooooooooooooooooooloodddddddddoooddddxxkkkkkkkkkkkkkkkxxxddooooooolllldxdollloooooooooooooooooooooodddlclll:;:;,'''',;cclllodxO0KXNNNNXKOdoddlclccccccccccccccccccccccccccclllllllllllllllloooooollloooooooooooooooooooooooooooooollllooolooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooolllollllllllllllccccccccccccccccccccccccccccccclllllllllllllllllllllloddddddddddddddddddxxkkkkkkkkkkkkkkkkkxxddoooooolllodkxolllooooooooooooooooooooddol::::::::cccc:;,'''',:llloxOO00KKXXXXKOxolcclcclclllllllllllllloooooooooooooooolllllllloooooollllooooooooooooooooooooooooooooolloooooooooooolooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooolllllllllllllllllllcccclccccccccccccccccldxxddxxxdddddddddddxxkkkkkkkkkkkxxxxxkkkxxddddooooodxkkdlllooooooooooooooooooddolc:ccc:::lxkOkkkxddoc:;,;codk000OOOKKKKXXKOdooooooooooooooooooooooooooooooooooooooooooollooooooolllooooooooooooooooooooooooooooolllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxxxxxxxxxxdddxddddddxkkOOOkkkxxxxxxxxxxxxxddxxddddddxkkdooodxxdooooooooooooddolcllllccccloxkkkkOO00KKKKK00KKK00000OOKKKKKXXX0kdoooooooooooooooooooooooooooooooooooooooollloooooolllooooooooooooooooooooooooooooolllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooxkkkkkxxxxxxxxxxxxddddxkOOOOOkkxxxdddddddddddxxxxxddxxkkOOOkdxO00OOkxxdooooooolllllllllccclloddddxxxkOKKKXXXK0OOO0KKK00KXXKKXXXXKOxoooooooooooooooooooooooooooooooooooooollloooooollloooooooooooooooooooooooooooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkOkkkkkkkkkkkkkkkxxdddxkOO0OOkxxddddddddooddxkkkkkxxkkkkOO00xloxkO0KKK0Okxollllllllllllcccclodooooooodxxxxk0KK0OOO00KK00KKKKKKXXXXX0kdoooooooooooooooooooooooooooooooooooolloooooollloooooooooooooooooooooooooooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkOOOOOkkkkkkkOkkkkkkxxdddxOO00OOxxdddooooooddkkkOOOkkkkkOOOOO0Oolooddxkkxxddoollooollllllcc:::clllccclloolccldk0KK00000000O0KKKKKKKKKXX0xooooooooooooooooooooooooooooooooooollooooooollooooooooooooooooddoooooooooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkOO00000OOOOOOOOOOOOOOkkxxddxkO000OkxddoooooddxkOOOOOOOOOOOOOOOO00d:cooooooooddooooooolllllllccc:::::,,,'':olccccldk00000O000OO00KKKKKKKKKX0xooooooooooooooooooooooooooooooooooolooooooolllooodoooooooodddddooooooooooooolooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooddxkOO00000000000OOOO0000000OOkkxxxxkO000OkdddddddxkkO00000OOOOOOO00OO000xl:cloddddddddoooollllllllllccc:cclcclodxxollllllodxO0000000OO0KKKKKKKXXXKKOdooooooooooooooooooooooooooooooooolooooooolllodoodoooooooddoooooooddddooooolloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkkO00KKKKKKKKKKK00000000000KKK00OkkkxxkO0KK0kxdddxxkOO00KKK00OOOO0000000KK0kdc::coddddddooooolllllllllllccccoddxkkkxxxdoodddddoxO0000000O0KKKKKKKXXXXXK0kdoooooooooooooooooooooooooooooooloooooooollooooddoodddddddddddoodoooooooolloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooood
ooooooooooooooooooooooooooooooooodooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkkO0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKXKKK00OOkkxk0KKKOOkkOOO000KKXXKK0OO00KKKKK0KKXK0kol::clooooooolllllllllllllllllllodddoooddxdodddxxdddkO00000000KKKKKXXXXXXKXKOdooooooooooooooooooooooooooooooooooooooollooooodoodddddddddddoodooooodoolloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooodoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkO0KKKKKKKKKKKKKKKKKKKKXKKXXKKKKKKKXXXXXXXK00OkkkOKXK00000KKKKKXXXXKKKKKXXXXXXXXXXXXX0kdlccccllllllllllllllllllllllllllcclllllloddxxxdxdddddkO0000000KKKKKXXKXXXKKK0xoooooooooooooooooooooooooooooooooooooollooddddoodddddddddddddddddddddoolodooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodddoooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooddddddoodkO0KKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXNNXNNNXKK00OkO0KXKKKXXXXXXXXNNXXXXNNNNNNNNXXXNNNNNX0kddocccccccccclllllllllllllloddoooollllllooxkxxxdddddxkO00000000KKKKKKKKKKKKOdoooooooooooooooooooooooooooooooooooooolooddddddddddooddddddddoddooddooloooooooooooooooooooooooooooooooooooooooooooooooooooooddddooodddooddooooooood
oooooooooooooooooooooooooooooooooodooooooooooooooooooooooodddooododddddooddooddxkOKXXKXKKKKKKKKKKKKKXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNXXXKKKXXXXXXNNXXXXNNNNNNNNNNNNNNNNNNNNNNNNNXK0kdol:;;;:::::ccccccllcccldkkOkxddooooooodxkkkxxxxxxxxkOOOOO00000KKKKKKKKKKOdooooooooooooooooooooooooooooooooooooolloddddddodddooddddddddddddddddooooooooooooooooooooooooooooooooooooooooodddooodddoooddddddddddddddddddooooodd
oooooodddddoooddoooooddooooooooooooooooodddoooddoooooooooooooddddddoooddxxkkO0KXXXXXXXXXXXXXXKXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNXXXNNNNNNNNNNNNNNNNNNNWWWNNNNNNNNNNNNNXX0Okdolc;;;;;;::::::ccccccldxxxdoooodddddddxxxxxxxxxxxdxxkkOOOOOOO00000KKKKK0kdoooooooooooooooooooooooooooooooooooolloddddddddddddddddddddddddodddooooooooooooodoooooooooooooddooddoooooooooodooodddooodddddddddddddddddddoodddo
dooooddoodddddddoooooooodddddooooooooooooooooodoodddoodoodddddoooddxkO0KXXXXXXXXXXXXXXXXXXKKKK00KK00KKKKKXXXXXXXXXXXNNNNNNNNNNWWWWWWNNNNNNNNNNNNNWWNNNNNNNNNNWWNWWWWWWWNNWNNNNNNXK0kxdolc:;;;;;;;;;;:::ccc:codoooollooooddddddddddddddddddxxkkkkkkOOOOO000KKK0xoooooooooooooooooooooooooooooooodooooloddddddddddddddddddddddddddddoolodoooooddddooodoooooooooddoooooddoodoooddddodddddddddddddoddddddddddddddddo
ddoooooddddddddddddddddddddddddddddddddddoooooddddddooddddddodxkO0KXXXXXXXXXXXXXXXXXXXXXXKKK0Okkkxxk00KKXXXXXXXXNNNNNNNNNWWNNNNWWWWWWWWWWWWWWWNWWWWWNNNNNWWWWNNNWWWWWWWWWWWNNNNNXK0Okxdoolc:;;,,,,,,;;:clldO00OdoooolllllllllllooooooooodoodddxxxxxkkkkOOO0KK0Oxoddooooooooooooooooooooooooooooooooolodddddddddddddddddddddddddddddoooddddddooddooddooooooooooooddodddodddddddddooddddddddddddodddddoddddddddddd
odddddddddddddddddddddddddddddddddddoodddddddddddddddddddodxO0KXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKK0000KKKXXXXXXXXNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWNNNNNNNNNNNWWWWWWWWWWWWWWNNXXXK0Okxxddolc:;;,,,,;cdkO0KXXNX0xoooolllllllllllllllllllollllooddddxxxkkOO000000xoodddooooooooooooooooooooooodddddoolodddddddddddddddddddddddddddddoooddooooooddoooodooddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddoddddddddddddddoodddoddddddddddddddddddddk0KXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKXXXXXXXXXNNNNNNNNNNNNNNNNWWWWWWWWWWWNNNNWWWWWWWNNNNNNNNNNNNNNNWWWWWWNNNNNXXXK00Okkxxddolc:;;;;;:okOO0KXXXXKkdooollllllccccccccccccccclllllooddddxxkOOO000KKkdoddooodddoooodddooooooooooddooooolooddddddddddddddddddddddddddddoooddoooooddddooodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddoddddddddddddddddddddddk0KKKKXXKXXXXXXKXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKXXXXXXXXXXNNNNNNNNNNNNNNNNNWWWNNNNNNNNNNNNNNNNNNNNNNNXXXXXXNNNNNNNNNNNNNNNNXXXK00OOkkxxxdolc:::::lxkO0KKXXXXX0kdooolllllllccccccccccccccclllloooodddxkkOOO0KKOdoddoooddooooooooooooodoooodoodooooodddddddddddddddddddddddddddddooododddddddddoddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddddddddxO0KKKKKKKKKKKKKKXXXXXXXXXKKXXXXXXXXKKXXXXXXXXKKXXXXXXXXNNNNNNNNNNNNNNNNWNNNWWWWWWWWWWWNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWNNNNXXKK000OOkkkxxddolcc:codxO0KKKXKXXKKkdoooollllllcccclccccccccccccclllloodxxkOOOOOOkdodddoddddoooodddooooooooodddddoooodddddddddddddddddddddddddddddooodddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddddddddddddddddddddddxO000KKKKKKKKKKKKKKKKKKKKKKKKXXXKKXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWNNNNNWWWWWWWWWWWNWWWWWWNNNNXXXKK000OOOOkkxxxddolclodxO00KKKKKKKK0kooooolllllcccccccccccccccc:ccllllodxxxxdoodxxdddooddddooddddddddoooooodddddoolodddddddddddddddddddddddddddddooodddddddddddoddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddddddxO0000KKKKKKKKKKKKKKKKKKKKKKKKKKKKKXKKXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWWNWWWWWWWWWWWWWWWWNNNNNNNXXXK0000OOOOkkkkxxddolloxkO0000KKKKKK0Oxdoloollllcccccccccccccc::::cccllllolllccclxxdododddooddddddddooddoooodddddolodddddddddddddddddddddddddddddoooddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddddddddddddddddddddxkOO00000KKKKKKKKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWNNNNNNNNNWWWWWWWWNNNNNNNXXXKK0000OOOOkkkkxxddooddxkO00000KKK00Okxxddolllllllllllcccccccccccclllcccc::cccccdkxddddddooodddddddddddoooddddddooodddddddddddddddddddddddddddddolodddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddddxkO000000KKKKKKKKKKKKKKK0KKKKKKKKKKKXXXXXXXXNNXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWNNNNNNNNNWWWWWWWWNNNNNNNNNNNNNNNNNNNNNNNNNNXXXXKK000OOOOOkkkkkxddoddxkOO00000KKKOkxxkOOkkxdoollllllccccccccccccccc::;;::ccllldxkxddddddodddddddddddddooddddddoooddddddddddddddddddddddddddddddooddddddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddddxkOO00000KKKKKKKKKKK0000000KKKKKKKXXXXXXXXXXXXXXXXXXNXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNWWNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXXXNNNNNNNNNNXXXXKKK0000OOOOOkkkkxdddddxkkOO00000KK0kxxkOO000KK0Oxdlllllcccc::::::::;;;;;;;:clooodxxddddddddddddddddddddooddddddoooodddddddddddddddddddddddddddddooddddddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddddddddddddddddddxkOOO000000000000000000000000000KKXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXXXXKKKKKKKKKKKKKKKKXXXXXNNNNNNNNNXXXXXXKKK000OOOOOOkkkxxddddxxkkOO000KKKKOxxkO00KKKKKXXK0kdlllcccc::::;;;;;;;;;;:cloodddxxdddddddddddddddddddooddddddoooodddddddddddddddddddddddddddddooddddddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddxkkOOO00000000000000000000000000KKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXXXKK00O000000KKKXXXXXNNNNNNNNNNXXXXXXXKKK00000OOOkkkkxxxddddxxkOO0000KKK0kkO0KKKKKKXXXXXXKxllccc:::::::;;;;;;;;;:clodddxkkddddddddddddddddddooddddddooooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddxkOOOOO000000000000000000000000KKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXNXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXKKK000KKXXXXNNNNNNNNNNNNNNXXXXXXKKKKK0000OOOOkkkxxxxxdddxkOOO00K000K0O0KKKKKKKXXXXXXXKxllccc::::::::;;;;;;;::coddxxkOkdddddddddddddddddoodddddddoooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddddddddddddddddxkkOOOOOOO0000000000000000000KKKKKKKKXXXXXXKKKKKKKXXXXXXXXXXXXNNXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWNNXXXXXXXXXXXNNNNNNNNNNNNXXXXXXXXKKKK0000OOOOkkkkxxxxdddxxkOO000000KK00KKKKKKKKKXXXXXNKxlccc:::::::::;;;;;;;:clodxxkkkkdddddddddddddddddoddddddddooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddddddddddddddddxkkkOOOOO0000000000000000000000000KKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWNNNNNNXXXXXXXXXXNNNNNXXNNNXXXXXXXXKKKK0000OOOOkkkkkxxxxxxxxkOOO0000000000KKKKKKKKKXXXXXXKxllc:::::::::;;;:;;;;:codxkkkkkkddddddddddddddddoodddddddooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddx
dddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOO000000000000000OO000000000000000KKKKKKKKKKKKXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXXKKKKXXXXXXXXLOVEXYOUXFOREVERK0000OOOOkkkkxxxxxxxxxkkOOOO000000000000KKKKKKXXXXXX0dllc:::::::::;;;;;;;;:lodxkkkkkkxddddddddddddddoodddddddooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOO000000000O0OOOOOOOOOOOOOOOOOO00000000000KKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNXXNNNXKK00KKKXXXXXXXXXXXXXXXXKKKKKKKK0000OOOkkkkkxxxxxxxxkkOOOOO00000O0000000KKKKKKXXXXKxllcc::::;;;;::::;;;;::codxxxxkOkddddddddddddddoodddddddooddddddddddddddddddddddddddddddooddddddddddddddddddddddddddddddddddddddddddddddddddddddxxdddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddxxkkkOOOOO000000000OOOOOOOOkkkxxkkkkkkkkOOOOOOO000000KKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNXNNNNXKK0KKKXXXXXXXXXXXXXXXXKKKKKKKK00000OOOOkkkkxxxxxxxxkkkOOOOOOOOkkOOOOO0000KKKKKKKKKxllccc::::;;:::;;;;;;;::cloodxxkkxddddddddddddddddddddddooodddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddxxxkkOOOOOO0000000OOOOOOOOOkkxdddxxxxddxxkkkOOOOO000000KKKKKKKXXXXXKXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKK0000OOOOkkkxxxxxxxxxxxkOOkkOkkkddxkkkkOO000000KKKKKklllccc::::::::;;;;;;;;;;:cloddxkkxddddddddddddddddddddddoodddddddddddddddddddddddddxddxdodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
ddddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOOO000OOOOOOOOOOkkxollllooolodxxxxkkOOOOO000000KKKKKKKKXKKKKKXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXXXXXXXXKKKKKKKKK0000OOOOOkkkxxxxxdlccoxkkkkkkxxdlldxxxxkkOOO0000000Kkollccc:::::::::::;;;;;;;;;:codxkkkxddddddddddddoddddddddoodddddddddddddddddddddddddxxxxdooddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOOOOOO000000OOOOkkdlcccllccloddxxxkkkkOOO0000000KKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNXXKKKXXXXXXXXXKKKKKKKKKKKKKKK0000OOOOkkkkxxxxdol:codxkkkkxdo:;clodddxxkkOO0000KKK0dlllcc:::;;:::::::;;;;;;;;;:cldxkOkxdddddddddddoddddddddoodddddddddddddddddddddddddxxxxdooddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
dddddddddddxxddddddddddddddddddddddddddddddddddxxxxkkkkOOOOOO000000OOOOOOOkdcccccccloodddxxxkkkkOOOOOO00000KKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNXXKKKKKKKKXKKKKKKKKKKKKKKKK0000000OOOkkkxxxxxddollodxxxxxdl:',;cclooddxxkkOO000KKKkollccc:::::;;;;::;;;;;;;,;;;cldxkOOkdddddddddddddddddddoodddddddddddddddddddddddddddxxdooddddddddddddddddddddddddddddddxxxxddddddddddddddddddddddddddddddddxxxxx
ddddddddddddddxxdddddddddddxdddddddxdddddxdooddddxxxkkkkkOOOO000000OOO000OOkocccccclooddddxxxxkkkkkkOOOOO00KKKKKXKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNXXXKKKKKKKKKKKKKKKKKKKKK000000000OOOOkkkkxxxddxdoooddxxxxdl;'',;;:cclooodxxkkOO000K0dllcccc::::::::;;;;;;;;;;;;;;:ldkO00kddddddddddddddddddoodddxxdxdddddddddddddxxxdddddxdoodxdddddddddxddddddddddddddddddddddddddddddddddddddddddddxxddxxxxxxxxxxx
dddddddddddddxxdddddddddddddddddddxxdddddddoooodddxxxkkkkkOOOO0000000000000Oxoccc:clllooodddxxxkkkkkkkkkkkkO0KK000000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXXNNXXXNNXKK000KKKKKKKKKKKKKKKK00000000OOOOOkkkkxxddddddoooddddddl;'.',,;::ccllooodxxkkkOO00koccccc::::::::;;;:;;;;;;;;;;;:ldkO00Oxddddddddddddddddooddddxxddddddxxddxddxxxxxxxxdxddodxxdddddddddddddddddddxddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxx
xdddxdddxxddddddddddddddddddddxxxxxxdddddxdooooddddxxxxkkkOOOO00000000KKKK00Oxl::ccllllooooodddxxxxxxxkxxxdooooxkkOOO000000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXNNXXK0000000KKKKKKKKKKK00000OOOOOOOOkkkxxxdddddooooooodddl'...',;;;::cccllloodddxxkkOOdlcccc::::::;;;;:::;;;;;;;;;;;:ldkOO0Oxddddddddddddddddodxxdxxdxxxxxxxxxxxxxxxdxxxxxxddodxxddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxdddxxxxxdddddddddddddddxxxddddddddddddooooodddddxxxkkOOOOO00000KKKKKKK00koc::ccllllllllooodddddxxxxxdoc::ldkOOO000000000KKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXNXXXK00000000000000000000OOOOOOkkkkkkxxdddddoooooooodool,....',;;;;::cccccclllooddxxkxocccc::::::;;;;::;;;;;;;;;;;;;:lxkOOOOkdddddddddddddddoodxdxxxxxdxxxxxxxxxxxxxxxxxxxxdodxxdddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ddddddxxxxxxxxxxxxdxxxxxxxxxxxxxxxxxxxxddddooooooodddxxxkkkkOOOO000KKKKKKKKK00xl:;:ccllccccclllooodddddxxxdlcldkOOO0O00OOOO000000000KKKKKKKKKKKKKKKKKKKKKKXXNNNXKK000OO00000000000000OOOOkkkkkkkxxxdddooooooooooooo:',;''',;;;;:::ccccccccllloddxxocccc::::::::;;:;::;;;;;;;;;;;;:lxkkkOOkxdddddddddddddoodxxxxxxdddxxxxxxxxxxxxxxxxxxxdooxxddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
dddddxxxddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdolloooooddxxxkkkkkOOO000KKKKKKKKKK0kd:,;:ccccccccccclloooodddddddodxkkkkOOOOOOOOOOOOOO000000000KKKKKKKKKKKKKKKKXXNNNNXXK00OOOOOOOO0OO000OOOOOOkkkkkxxxxdddooooollllloool;':oc;,,;;::::::cccccccccllooddocccc:::::::::;;;;;;;;;;;;;;;;;;coxxkkkOkxddddddddddddoodxdxxxxxxxxxxxxxxxxxxxxxxxxxxxddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxdxxxxxxxxxxxddxxxxxxxxxxxxxxdxxxdxxxxxxxdollooooooddxxxkkkkOOO00KKKKKKKKKKK0Oxc,',:ccccccccccccllllooooooodddxxxxkkkkkkkkkOOOOOO00000000000KKKKKKKKKKKKXNNNNNNNXXXK00OOOOOO0OOOO0000OOOOkkkkkxxxddddooooooolooodl,':ddl:;;;::::::ccccccccllllooolcccccc::::::;;;;;;;;;;;;;;;;;;;:loodxxkkkddxdddddddddoodddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxdxxxxxxxxxxxxxxxdxxxxxxxxxxxdolllooooooddxxxkkOO0000KKKKKKKKKKK0Oxl;..';::ccccccccccccllllloooooddddxxxxxxxkkkkkOOOOO0000000000KKKKKK0KKKXXNNNNNNNNNNNNNXXKKKKKKKKKKKKKK0000OOOOOkkxxddddddooooooodddc,cddddl:;:::::cccccccccclllloolcccccc::::::::;;;;;;;::;;;;;;;;;:ccloddxkxdddddddddddddxxxxxxxxxxxxxxxxxxxxxxxdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdxxxxxxxxxdollloooooooddxkOOO000KKKKKKKKKKKK00Oko:,..',;::::ccccccccccccllllooooddddxxxxxxxkkkkOOOOO0000KKKKKKKKXXXXXNNNNNWNNNNNNNNNNNNNXXXXXXXXXXXXXKKKKK0000OOOkkxxxdddddodddddxko:cdxdddoc::::cccccccccccllloddllccccc::::::::;;;;;;;::;;;;;;;;;;::cllodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolllooodddddxkO0000KKKKKKKKKKKKKK00Oko:,...'',;;::ccccccccccccccllllooooddddxxxxxkkkOOOO000KKKKXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXKKKKKK00000OOkkkxxxxddddxxxkOklcdxdddddlc::cccccccccccclodxdlcccccc:::::::;;:;;;;;;;;;;;;;;;;;;;;:cloodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdooooddxxxkkOO000KKKKKKKKKKKK00K000Okdc,...'',,;;:cccccccccccccccccllllloodddxxxkkkOO000KKKKXXXXXXXXNNNNNNNNNNNNNNNNWWNNNNNNNNXXXXXXXXXXXXXKKKKKKK00000OOOkkkkxxxxxxxkkOOdldxdddddddlccccccccccccllodxdlccccc::::;;;;;::;;;;;;;;;;;;;;;;;;;;;:ccloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdddddxkkOO00000K0KKKKKKKKKK0K000000Oxdc,...',,,,;;::cccccccccccccccllloooddxxkkOOOO0000KKKKKKKKXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNXNNXXXXXXXXKKKKKKK000000OOOOkkkkkkkkkxkkkkxodxxxxxxxxxdlccllccccccllodddlcccccc::::::::::;;;;;;;;;;;;;;;::;;;;;;:codxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkOOO00K00KKKKKKKKKKKK00K00000OOxdc,..''',,,;;::cccccccccclllllooodxxkkOOOO000000000KKKKKKKXXXXNNNNNNNNNWWWWNNNNNNNNXXXNXXXXNNNNNXXXXKKKKKKK000000OOOOOOkkkkkxkkxxxkkddxxxxxxxxxxxxdollllcccccllooolccccccc:::::::::;;;;;;;;;;;;;;;;::;;,,;;:ldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkOOOO000000KKKKKKKK000000000000Okxoc'..''',,,;;::cccccclllllooodddxkkOOO0000000000KKKKKKXXXXXXNNNNNNNWNNNWWNNNNXXXXXXXKXXXXXXXXXXXXXXXKKKKKKK0000000OOOOOOkkkxxxxxxxxxxdxxxxxxxxxxxxxxdollllcccclllolcccccccc::::::::::::;;;;;;;;;;;;;::;;;;;:cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOOOO00000000KKKKKKK0000000000000Okxo:'.''',,,;;:::ccclllooooodddxxkkOOOO000000000KKKKXXXXXXXXXXXNNNNNNNNNNNNNXXXXKKKKKKKK00KKKKKKKKKKKKKKKKKKKKK0000000OOOkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxdolllcccllllllccccccc:::::::::;;;;;;;;;;;;;;;;;::;;;;:cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOO00000000000KKKKKKKKK0000000000OOkxo:'.''',,,;;::cclllooooddddxxxkkkkOOOO0000KKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXKK000OOOO000000K0000KKKKKKKKKKKKKK000000OOOkkkkxxxxxxxxxkkxxxxxxxxxxxxxxxxxxdollllccllllcccccccccc:::::::;;;;;;;;;;;;;;;;:::ccloodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkOO0000000000000000K00KK000000000OOkkxl;,'''',,;;::cclloooooddddxxxxxkkkkOOOO000KKKKKKKKXXXXXKKKKKKKKKKKKKKKKKKKKKK0000OOOOO00000KK0000000K000KKKK000000OOOOkkkkkxxxxxxxxxkkxxxxxxxxxxxxxxxxxxxxddolccclccccccccccc:::::::::;;;;;;;;;;;;;;;::clodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOO000000000000000000000000000000OOOkdl;;,,,,,,;;::ccloooooodddddxxxxxxxkkOOOOO0000000KKKKKKKKKKKKKKKKKK0KKKKKKKK0000KKKK0000KKKKKKKK000000000000000000OOOOOkkkkkkxxxxxxxxkkxxxxxxxxxxxxxxxxxxxxxxxollcccccccccccc:::::::::::::;;;;;;;;;;;;::clloxkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOO000000000000000000000000000000OOkxdl::;;,,,;;;::cloooooooddddddddxxxxkkkkkOOOO0000000000000000000000000KKKKKKKKKKKKXXKKKKKKKKKKKKKKKK0000000000OOOOOOOOkkkkkkkkkxxxxxxkkkxxxxxxxxxxxxxxxxxxxxxxxxdolccccccccc::::::::::::::::;;;;;;;;;;;;::ccloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddoool
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkOOO000OOOOO00000000000000000000OOOkxdlc::;;;;;;;::clooooooooddddddddddxxkkkkkOOOO0000OOOOOOOOOOOOO000000KKKKKKKXXXXXXXXXXKKKKKKKKKKKKKKKK0000000OOOOOkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdocccccccc::cccc:::::::::;;;;;;;;;;;;;;;;:cloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddooollllllcccc
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddxkkOOOOOOOOOOO0000000000000000000OOOkxdolcc::;;;;;:cllloooddddddxxxxxxxxxxxxkkkkOO0000OOOOOOkkkkkkOOO0000KKKKKKKXXXXXXXXXXXKKKKKKKKKKKKKKKKK0000000OOOOOkkOkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolccccccccc::::::::::::;;;;;;;;;;,;;;;;;:clodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddoooolllllcccccccccccccl
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdddxkkkOOOOOOOOOO00000000000K000000OOOkxdxdlcc::;;;;:cclloooddddxxxxkkxxxxxxxxkkkOOOOOO000OOOOOkkkOOOO0000KKKKKKKKKKXXXXXXXKKKKKKKKKKK0000000000000000OOOOOOOOOkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdlccccccc::::::::::::;;;;;;;;;;;;;;;;;;;:coxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxxxxxxxxxddddoooolllllllccccccccccclllllllloooo
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooodxxkkkOkkkkOOOOO00000000KK000000OOkxdxxxdocc:::;::ccclllooddxxxkkkkkkkkkkkkkkkkOOOOOOOOOOOOOOOOOO000000000KKKKKKKKKKKKKKKKKKK000000000000000000000OOOOOOkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdllccccccc:::::;;;::::;;;;;;;;;;;;;;;;;cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxxxxxdddddoooolllllccccccccccccllllllllllooooodddooooooo
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooodxxkkkkkkkkkOOOOO00000000000000OOOkxdxxxxxolc::::::ccclloodddxxkkkkkkOOOOOkkkkkkkkkkkkkkkkkkkOOOO00000000000000000KKKKKKKKKK00000000000000000000OOOOOOkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdollccc::::::::;;:;:;;;;;;;;;;;;;;;;cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdddddooooolllllllcccccccccccllllllllooooddddddooooooooooooodddd
kkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooodxxkkkkkkkkkOOOOO00000000000000OOOkxdxxxxxxdlc:c:::cccclloodddxxxxkkkOOOOOkkxxxxxxxxxkxxxxxxxkkOOOOOOOOOOO000000000000000000OOkOOOOOOO00000000OOOOOkkkkkxxxxxxxxxxxdddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolccc::::::::::::::;;;;;;;;;;::;cdxxxxxxxxxxxxxxxxxxxxxxxkxxxxkxxxxxxxxxxddddooooolllllllcccccccccccllllllllloooooodddddooooooooooooooodddxxxkkkkkkkk
xxxxxxxkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxdoddxxkkkkkkkkkOOOOO0000000000000OOOkkxdxxxxxxxxolcc::ccllllloooodddxxxkkkOOOkkxxxddddddddddddxxxkkOOOOOOOOOOOOOOOOOOOOOO000OOkkkxxkkkkOOOOOOOOOOOOOOkkkxxxddoooodddddddoooodddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdolccc::::::::::::;;;;;;;;;;::cdxxxxxxxxxkxxxxxxxxxxxxxddddddooooollllllllccccccccccclccllllllllloooodddddddooooooooooooodddddxxxxkkkkkkkkkkkkkkkkkk
llllooooooddddddddxxxxxxxxkkkkkkkkxkkkxdddxxxkkkkkkkkkOOOOOOO00000000000OOkkxdxxxxxxxxxxdocc:clllllllllooooddxxxxkkkkkxxddoooooooooddxxxkkkkkkkkkkOOOOOkkOOOOOOOOOOOkkkxxxxxxxkkkkkkOOOOOkkkkxxxdddoollooooooooollooodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdolc::::::::::::::;;;;;;;;:coxxxxxxxxddddoooooooollllllllcccccccccccclclllllllllllooooddddddooooooooooooooooddddxxxkkkkkkkkkkkkkkkkkkkkkkkkkkxxxdd
llllllcccclllllllllllooooooooddddddddddddxxxkkkkkkkkkkOOOOOOOO00000000OOOkkkxdxxxxxxxxxxxxoccccllllllllllllooodddxxxxxddooolllllllooddxxxkkkkkkkkkkkkkkkkkkkkkkkOOOOOkkxxxxxxxkkkkkkkkkkkkxxxxdddoooolllllloooooooooodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolc:::::::::::::;;::;;;:cloooolllllllccccccccclcclcllccllllllllloooooodddddddooooooooooooooddddddxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxddooollcccc
dddddooooooooollllllllllcccccllllllllooddxxxxkkkkkkkkkOOOOOOOOOO000000OOOOkxdxxxxkkkkkkkkkxoccclcccccllllllllllooooooooolllllcccclloddxxxxkkkkkkkkkkkkkkkkkkkkkkkOOOOOkkkkkkkkkkkkkOOkkkkkxxxdddddoooolllllloooooodddxkkxxxkkxxkkxkkkkxxxxxxxxxxxxxxxxxddddddddddoolcccc::::::::::;;;;;;:cccccccccllllllllllloooooooooodddddddddoooooooooooooodddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxddddooolllccc::::::::::
ddddddddxxxxxxxdddddooooooooooooooollodddxxxxkkkkkkkkkOOOOOOOOOOOO000OOkkkxxdoodddddddddddddlcccclcclllllllllllllllllollllllccccclooddxxxxkkkkkkkkkkxkkkkkkkkOOOOOOOOOOOkkkOOOOOOOOOOOOkkkkxxxxdddooodddoooooodddddddxxxxxxxxdddddddddoooooooooooolllllllllllllllclccllcccccccccc:;;::cllooooooooooodddddddddddddooooooooodddodddddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxddddoolllccc::::::::::::::::::::::
dddddddddddddddddoooodddddddddxxddxxddddxxxxkkkkkkkkkkkOkOOOOOOOOO000OOkkkxxolcllllllllllllllcccclllllllllllooooooooooooooolllcclloodddxxxkkkkkkkkkkkkkOOOOOOOOOOOOOOOOOOOOO000000OOOOOOOOOkkkxxddooddxxxxxdddddddddollllllllllllllllllllllccccllcccllllllllllllloolooooooooooodoooooodxxxddddooooooooooodddddddddddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddoolllcccc:::::::::::::::::::::::::::::::::
kkkkkkkkkkkkkxxxxxdddddddddddddddddddoddxxxxkkkkkkkkkkkkkkOOOOOOOOOOOOkkxxxddoooooooooooooollllccclllllllllooooooooddddxddddoolloooddddxxxxxxxkkkkkkkOOOO00O000000000000000KKK000000000OOOOOkkkkxxddddddxxxxddddddddollllllllllooooooooooooooooooooooooodddddddddddddddddddooooooooooooddddddddddddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddooolllccccc::::::::::::::::::::::::::::::::::::::::::::
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddxxxxkkkkOOOkkxxxkkkkkOOOOOOOkkkkxxddddxdddxxxxxxxxxdddolccllllllllooooooodddddddxxdddddddddddddxxxxxxxxkkkOOOO0000000000KK00KKKKKKKKKKKKKKK000000O0OOOOkkkxxddooddddddddddooddddddxxddxxxxxxxxxxddddddddoooddoooooooooodddddddddddddddddddxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddoooolllcccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:
xxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdddxxxkkkOOOOOkkkxxxxxkkkkkkkkkkkkkxxddddddddddddddddddddoolccllllllloooooodddddddddddddddddddddddddxxxxxkkOO0000KKKKKKKKKKKKKKKXXKKKKKKKKKKKKK0000000OOOOkkkkxxdololloooooooloddoooooooododdddddddddddddddddddddddxdxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxdddoooollllcccccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ccccccc::
cllllloooodddddxxxxxkkkkkkkkkkkkkkkkxdddxxxkkOOOOOOOkkkxxxxxkkkkkkkkxxxxdddxkkkkkkkkkkxxxxxxxxxxdllllloooooooddddddddddddddoooooododdddddxxkkOO00KKKKKKXXXXXXXXXXXXXXXXKKKKKKKKKKKKK000000OOOOOkkkkxxdolllclclllllodddddddddxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxdddddooollllccccccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:::cccccccccccccccccc::cc::::
::::::::::::ccccccccclllloooooddddxxdoddxxkOOOOO000OOOkxxxxxxkkkkkkxxxxddddkkkkkkkkkkkkkkkkkkkkkkdolloodddxxxxxxddddddddooooooooddoodddxkkOO00KKKKKKXXXXXXXXXXXXXXXXKKKKKKKXKKKKKKKKK00000OOOOOkkkxxxddoollccccclloxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxddddoooolllllcccccccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cccccccccccccccccc::::::::::ccccccccc
::::::::::::::::::::::::::::::::::cclodxxkkOO00000000OOkxxxxxxxkkkkxxxddodxkkkkkkkkkkkkkkkkkkkkkkkxoloddxxxkkkOOkkkkkkkxxxxxxxxxxxxxxkkOO00KKKKKKKKKKKKKXXXXXXXXXXKKKKKKKKKKKKKKKKKKK00000OOOOkkkkxxddoooollccllllokkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxddddddoooooolllllccccccc:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ccc::ccccccccccccccccccccc::::::::cc:::cccccccccccccccc
:::::::::::::::::::::::::::::::::::clddxkkOO000KKKKKK00OkxxxxxxkkkxxxddolllloolooooooodddddddddxxxxdoodxxkkkO000000000000000000OOkkkkOO000KKKKKKKKKKKKKKKKXXXXXXKKKKKKKKKKKKKKKKKKKKKK0000OOOOkkkxxxddoooolllllllloxxxxxxdddxdddddddddoooolllllllllllccccccccccc:::::::::::::::::c:c::::::::::::::::::::::::::::::::::::::::::::::::::::cccccccccccccccccccccccccccccccc:::::::::::cccccccccccccccccccccccccc:::
:::::::::::::::::::::::::::::::::::clddxkO000KKKKKKKKKK0Okxxxxxxkkkxxddocc:::::::::::::::::cccccccccldxxkOOO00KKKKKKKKKXXXXXXXKK00OOOO000KKKKKKKXKKKXXXXKKKXXXXKKKKKKKKKKKKKKKKKKKKKKKKK000OOkkkxxxxxdddooollllclccccccccccc:::::cc:::::::::::::c::::::::::::::::cc::::::::::::::::::::::::::::::::::::::::::::::cc:::::::ccccc:ccccccccccccccccccccccccc::::::::ccc::ccccccccccccccccccccccccccc:::::::::::::::
ccccccccccc::::::c::::::::::::::::ccodxkO0000KKKKKKXXXKK0Okxxxxxxkkxddol:::::::::::::::::::::::::cc:ldxkOO000KKKKKKXXXXXXXXXXXXXKK00000KKKKXXXXXXXXXXXXXXXXXXXXKKKK000KKXXXXXXXXXXXXXKKKKK00Okkkxxxxxxxxxddoollllc::::::ccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc::c::::cc:ccccccccccccccccccccccccccccccccccc:::ccccccccccccccccccccccccccccccccccccc::cc::::::::::::cccccccccccc
ccccccccccccccccccccccccccccccccccccodkO00KKKKKXXXXXXXXKK0Okxxxxxkxxdolc:::::::::::::::::::::::::c::lxkOO000KKKKKKXXXXXXXXNNNNNXXXKKKKKKXXXXXXXXXXXXXXXXXXXXXKKKK00000KXXXXXNNNNXXXXXXXXKKK00OOkxxxxkkkkkkxxddoolc:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ccccccccccccccccccccccccccccccccccccccccccc::ccccccccccccccccccccccccccccccccccccc:cc:ccc:c::::::::cccccccccllllllllllllooo
cccccc:ccccccc::cccccccccccccccccccloxO0KKKKKXXXXXXXXXXXKKOkkkkkkkxxdoccc:::::::c::c:::::::::::ccc:lxOOO00KKKKKKKXXXXXXXXXXNNNNXXXKKKXXXXXXXXXXXXXXXXNNNNNNNXXXXK0OO0KKXXXXNNNNNNXXXXXXXXKKK00OOkkkkOOOOkkkkxxddocc:::::::::::::c::::::::::::::::c::::::::cc::cccccccccccccccccccccccccccccccccccccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccc:cc:::c::::::::ccccccccclllclllllllloooooooooooooodd
cccccccccccccccccccccccccccccccccccldkO00KKKKXXXXXXXXXXXXK0kkkkkkkxxolccccccccccccccccccccccccccccokO0000KKKKKKKXXXXXXXXXXXXNNXXXXKKKKKKXXXXXXXXXXXXXXNNNNNNNNNXXXXXXXXXXXXXNNNNNNNXXXXXXXKKK00OOkkOOOOOOOOkkkxxoccccccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::cccccccccccccccccccccccccccccccccccccclcccccccccccccc::cc::c:::cccc::ccccccccccclllllllllllllooooooooooooooddddddddddddddd
ccccccccccclccllcccllcccccccccccccloxkO000KKKKXXXXXXXXXXXKK0OOOOOkkxocccccccccccccccccccccccccccldkO0000KKKKKKKKXXXXXXXXXXXXXXXXXKKKKKKKKKKKKXXXXXXXXXXXXXNNNXXXXK0OOOO0KXXXXXXXXXXXXXXXXKKKK000OOOO000000OOOkkkdlcccccccccccccccccccccccccccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccllcccccccccccccccccccccccccccccccccccccccc::cccccccclccllllllllloooooooooooooodddddddddddddddddddddxxxxxxxxx
cccccccccccccccccccccccccccccclcclloxkOO00KKKKXXXXNNXXXXXXXKK0000OOxlcccccccccccccccccccccccccccok0000KKKKKKKKKKXXXXXXXXXXXXXXXXXKKK0KKKKKKKKKKKKKKKXXXXXXXXXXXXXNXOddxkOKXXXXXXXXXXKXXXKKKKK000OOO0000000OOOOOOxocccccccccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclclllllllllllloooooooooooodddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxx
llllllllllcccccccccccccccccccccccclooxkO000KKKXXXXXXXXXXXXXXXXXXK0Odlllllcccccccccccccccccccccldk0000KKKKKKKKKXXXXXXXXXXXXXXXXXXXKKKK00KKKK0000KKKKKKKKKKXXXXXNNNNNXKKXNNNNXXXXKKKKKKKKKKKKKK000OOO0000000OOOOOOkdlcccccccclcccccccccccccccccccccccccccclccccccclllcccccccccccccccccccccccccccccccccccccccccccccccllllcclllllllllloooooooooooooooooddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxk
ddddddddoooooollolllllllllllllllllllloxkO00KKKKKXXXXXXXXXXXXNNXXK0OdlllllccllccllllllllllllllldO00KKKKKKKXXXXXXXXXXXXXXXXNNNNNXXXXKKKKKK000000000KKKKKKKKKKXXXXXNNNNNNNNNNXXXKKKKKKKKKKKKKKK000OOOO0000000OOOOOOOkolllllllcllllcclllccclcccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllloooooooooodddooodddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxxxxxkkkkkkkkkkkkkkkkk
kkxxxxxxxxxxxxxxxxxxdddddddddddoooollldxkO000KKKXXXXXXXXXXXXXXKKKK0dllllllllllllllllllllllllldOO00KKKKKKXXXXXXXXXXXXXNXXNNNNNNNXXXXKKKKK000000000000KKKKKKKKKKXXXXXNNNXXXXXKKKKKKK0KKKKKK00000OOOOO00000000OOOOOkkdlccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllloooooooooooooooddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxkkkkkxkkkkxxkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkxkxkxxxxxxxxxxxxdollodxO0000KKKXXXXXXXXXXXXXXKKK0xoooooooollollllllllllllldkO000000KKKKXXXXXXXXNNNNNNNNNNNNNXXXXXXKKKKK00000000000000000KKKKKKXXXXXXXXXKKKKKKK0000000000000OOOOOO00K00000OOOOOkkxolllllllllllllllllllllllllllllllllllloooooooooooooooooooooddddddddddddddddddddddddxdddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxxxkkxxkkkkkkxkkkkkkkkkxxxxkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdooodxkOO000KKKXXXXXXXXXXXXXXKKKOxxxxxxxxddddddddddddddodxkO0000000KKKKXXXXXXXXXNNNNNNNNNNNXXXXXXXKKKKK000000000000000000KKKKKKKKKKKKKKKKK0000000000000OOOOOOOOO00KK0000OOOOOOkkdoooooooooooooooooooodddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdoodxxkkkO000KKKXXXXXXXXXXXXXXKK0OkkkkkkkkxkkxxxxxxxxxxxxkkOOO00000000KKKKKKXXXXXXXXNNNNNNXXXXXXXKKKKKK0000000000000000000000000KKKKKK0000000OOOOOO0OOOOOOOOOOO00KKKK00000OOOOOkxxxxdddddxxxxdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkkkxxkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkddddxxxkkkOO000KKXXXXXXXXXXXXXXXKKOkkkkkkkkkkkkkkkkkkkkkkxkkkOOOOOO000000000KKKKKXXXXXXXXXXXXXXKKKKKKKKKK0000000000000000000000000KK000000000OOOOOOOOOOOOOOOOOOO00KKKKK000000OOOkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkx
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdddxxkkkOOO0000KKKKKXXXXXXXXXXXXXK0kkkkkkkkkkkkkkkkkkkkkxxxkkkkkkkOOOOOOOOOO000KKKKKKKKKXXXXXKKKKKKKKKKKKKKKK00000000000000000000000000000OOOOOOOOOOOOOOOOOOOOOO0KKKKKKK000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxdd
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxddxkkkOOOO0000KKKKKKXXXXXXXXXXXXK0kkkkkkkkkkkkkkkkkkkkkxxxxxkkkkkkkOOOOOOOOOOO0000KKKKKKXXXKKKKKKKKKKKKKKKKK0000000000000OO00000000000000OOOOOOOOOOOOOOOOOOOOO000KKKKKK0000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxdddooooooooooo
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxddxkkOOOO00000KKKKKXXXKKXXXXXXXXXKOkkkkkkkkkkkkkkkkkkkkxddxxxkkkkkkkkkkkkkkkkOOOO00000KKKKKKKK000000000K00000000000000000O00000000000000OOOOOOOOOOOOOOOOOOO000000KKKK000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxdddooooooooooooooooddddxxxx
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdxxkkOOO000000KKKKKKKKXKKXXXXXXXXK0OkkkkkkkkkkkkkkkkkkkxxxxxkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000000000000000000000000000000000000000000OOOOOOOOOOOOOOOOOOO000000KKKK0000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxddddoooooooooooooooooddddxxxxxxxxxxkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdddxkkOOO0000KKKKKKKXXXXKKKKXXXXXXK0OkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkxxxdxxxxxxkkkOOOOOOOOOOOO00000000000000OO0000000000000000OOOOOOOOOOOOOOOOOOOO00000000KKK0000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxddddddooooooddooooooooooddddxxxxxxxxxxxxkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdodxkkOOOOOO000KKKKXXXXXXKKKKKXXKKKK0kkkkkkkkkkkkkkkkkkkkxkkkkOOOkkkkkkkkkkkkkkxxxdddodddddxxxxkkkOOOOOO00OOOO0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO00000000000000000000000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxddddddddooooodddddddddddddddddddxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
xxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkxdodxxkkOOOOO0000KKKXXXXXKKKKKKKKKKKK0OkkkkkkkkkkkkkkkkkkxxkkOOOOOOOOOOOkkkkkkkkkkkxxdoooooddxxxxxkkkkkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO000000000000000000000000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkxxxxxxxxxxxxddddddddoooodddddddddddddddddddddxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
ddddddddddddddddddddxxxxxxxxxxxxxxdoddxkOOOOOO0000KKKKXXXKKKKKKKKKKKKK0OkkkkkkkkkkkkkkkkkkkkOOOO000000000OOOkkxxxxxxxxddooodddxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkOOOOOOOOOOOOOkkOOOOOOOOOOOOO00000000OOOOOOO0000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxkxxxxxxxxxxxxxdddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
xxxxxxxxxxxxxxxdddddddddddddddxdddooodxkOOOOOOO0000KKKKKKKKKKKKKKKKKKK0OkkkkkkkkkkkkkkkkkkkOOOO000000000000OOOkkxxxdddddddddddxxxkkxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOOOOO0000OOOOOOOO00000000000OOOkxkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxddddddddddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxkxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkx
kkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxdoodxkkOOO0OO000000KKKKKKKKKKKKKKKK00kdddddddddddddxxxxkOOO00000000000000000000OOkkxxxxxxxxxxddxkkxxxxxxxxxxxxxkkkkkxxkxxxxkkkkxxxkkkkkkkkkkkkkkkkkkkOOOOOOOOOOkkkkOO0000000000OOOkkxxxxxxxxdxxxxdddddddddddddddddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxddddoooolloo
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdooodxkOOOOOO000000KKKKKKKKKKKKKKK000kxxxxdddddddddddxkkOOO0000000000000000KKKKKK000OOOOOkkkxxddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxxkkkkkkkkOOOOOOOkkkkkkOO000000000OOOkkxxdddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxddooooollllllloooodddxxx
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxolodxkkOOOO0000000KKKKKKKKKKKKKKK000OkkkkkkkkxkxxkxxxkkOOO0000000000000KKKKKKKKKKKKKKKK000OkxxddddxxddxxddxxxxxxddddddddxddddddxxxxxdddxxxxxxxkkkkkkkkkkkkkkkxxxkkkOOOOOOOOOOkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxdddooooooolllloooooodddxxxxkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxooodxxkkOOO0000000KKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkOOO00000000000K0000KKKKKKKKKKKKKKKKK0Okxddddddddddddddddddddddddddddddddddddddddddddxxxxxkkkkkkkkkkkkkxxxkkxxxkkkkkkxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxddddooooooooooolooooooodddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdooodxxkOOO000000KKKKKXXXXKKKKKK000OkkkkkkkkkkkkkkkkkkkOOO0000000000K00000000KKKKKKKKKKKKKKK0Okddooooooooooodddddddddddddddddddddddddddddddddddxxxxxxkkkkkkkkxxxxxdoooooddxxxxxkkxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxdddddoooooooooooooooooddddxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
dxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkdoooodxkkOOO0000KKKKXXXXXXXKKKKK00OOkkkkkkkkkkkkkkxkkkkOOO000000000KKKKK0000000KKKKKKKKKKKKKK0Okdooooooodoooooddoooodddddddoooooooooooooooooddddddxxxxxxxxxxxxxxxdollllodxkkkkkxxddxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxddddddoooooooollooooooooddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
ddddddddddddddddddddddddxxxxxxkkkkkxoooodxxkkOOO000KKKKXXXXXXXKKKKK00OkkkkkkkkkkkkkkkxxkkOOOO000000000KKKKKK00000KKKKKKKKKKKKKKKK0Okddooooooooooooooooooooooooddoooooooooooooooooooddddddxxxxxxdddddollloodxxxxxddddddxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxddddddoooooooooooolloooooooooddddxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkxxxxxxxddddddddddddddddooooddxxkkOO0000KKKKXXXKKKKKKKK00OkkkkkkkkkkkkkkkxxkkOOOO0000000000KKK0000KKKKKXXKKKKK000KKKKK00kxdoooooooooooooooolooooodkOOOOOkkxoooooooooooooooodddddddddddoolllodddddddddxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxdddddddddooooooooooooooooooooooooooodddddxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdoooodxxkkkOO0000KKKKKKKKKKKKKK0OxddddddddddddddddxkkkOOOO0000000000000000KKKKXXXXXXXXKKKKKKKKK00OxooooooolllllllllloodxkkxO00000kkxdoooooooooooloooooooooooooooodxkkkkkkkkkkkOOOOkxxxdddddddddddddoooooooooooooooooooooooooooooooooddddddddxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoooodxxxkkkOOO000KKKKKKKKKKKKK00kkkkkkxxxxxxxxxxdxxkkOOOOO0000000000000000KKKKXXXXXXXXKKKKKKK0000kxoloollllllllllooodxO00kxdodddkkO0Okxolooooollllllllloooooooxk000000O00000000O0Oxdoooooooddddddddddddxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdlooddxxxkkkkOO00KKKKKKKKKKKK00OkkkkkkkkkkkkkkOkxxkkkOOOOO000000000000000KKKKKKKKKKKKKXXKXXKK0000OkxolllllllllloodxkxdxkOkdlcccldkO0OOOxdoooollllllclllooooodxO000000000000KKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoloddxxkkkkkkOO000KKKKKKKKKK00OkkkkkkkkkkkkkkkkxxkkkOOOOOOO000000000000KK00KKKKKKKKKKKKKXXXXK0OkkOkxdlccccloddxxdoodxxk0KKKkddO0KK0OkxxxddoolllllllllllloodxO0000000000KKKKKK0000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdloodxxkkkkkkOOO00KKKKKKKKKK00OkkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000000000KKKKKKKKK0KKKKKXXXXK0kxkkkkxolcclodxkkkkkkkO0OkkkkxxO000kdodkkkxoollllllllllloodxO0000KKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxooodxkkkkkOOOOOO000KKKKKKKK0OOkkkkkkkkkkkkkkkkkkOOkkkkOOO0000000000000000KKKKKKKKKK000KKKKKXXXKkddxkxxdlclllll::ododxxdolllllodddxddxdodooollllllllllloxkOO000KKKKKKKKKKKKKKK0000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdlllodkkkOOOOOOOO000KKKKKKK0OOkkkkkkkkkkkkkkkkkOOOOkkkOOO0000000000000000KKKKKKKKKK00KKKKKKKKXXKOxddddddoloooolloolllcccccccclllloocloodxdollllllllodxkOOO0000KKKKKKKKKKKKKKK0000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkoloodkkOOO00000000KKKKKKKK0OkkkkkkkkkkkkkkkkkOOOOOkkkOOO0000000000000000000KKKKKKKKKKKKKKKKKXXXXKOdooooolclloolc:ccccc::;;;::ccccllooolollccclllodxkOOO00000KKKKKKKKKKKKKKKKK000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOxloodxkO000KKKK00KKKKKKKXK0OkkkkkkkkkkkkkkkkkOOOOOkkkOOOO000000000000000000KKKKKKKKKKKKKKKKKKKXKKK0kolllccccccc:::cc:;,,,,,,,:ccclcccccclcllodxxkOOOO0000KKKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkdoodxkOO00KKKKKKKKKKKXXXK0OkkkkkkkkkkkkkkkkkkkkkkkkkkOOOO0000000000000000000KKKKKKKKKKKKKKKKKKKKKKKOdlccccccccc:::;;,;loxxo:;::ccccc::clodxkkOkkkOO000KKKXXXKKKKKKKKKKKKKKK0000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxx
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoodxxkO00KKKKKKKKKKKKXXXKOkkkkkkkkkkkkkxxkkkkkkkkkkkkOOOO0000000000000000000KKKKKKKKKKKKKKKKKKKKKK00xl:ccccccccc:::ldOKXXK0xlc:cccc:cldxkkkkkkkkOO00KXXXXXXXXXXXKKKKKKKKK00000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxdd
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdodkkkO00KKKKKKKKKKKKXXXK0kkkkkkkkkkkkkxxxkkkkkkkkkkkkOOOO0000000000000000000KKKKKKKKKKKKKKKKKKKKKKK0kl::ccccccccclokOKXXXXXOocccccloddxxxddddkO0KKXXXXNNXXXXXXXXXXXXXKKKK0000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxddddddddddooooo
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxodkkkO00KKKKKKKKKKKKKXXXKOkkkkkkkkkkkxddxxxxkxxxxxkkkkOOOOOO00000O000000000000KKKKKKKKK00KKKKKKKKKKK0ko:;:c::clldooxO0KXXXKKkllccloodooooodkOKXXXNXXXNNNNNNXXNXXXXXXXXKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxdddddddddddddoooollllllllllooll
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdoxkkOO000KKKKKXXXKKKKXXX0OkkkkkkkkkkxdddxxxxxxxxxxxkkkOOOOOOOOOOOO000OOO00000KK00KKKKKK00KKKKKKKKKKK0Oo;,;:::ldOxldkO0XXXKKOxolllooolodxOKKKXXXNNNNNNNNNNNNNNNXXXXXXXKKKK0000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxddddddddddoodoooolllllllllllloollloooooodddddd
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkdoxkOOO00OO0KKKKKKKKK0KKXX0OkkkkkkkkkdoodddxxxxxxxxxkkkOOOOOOOOOOO00OOOOO0000000000KKKK0000K000KKKKKK00Oo;',;:okKOllxO0KKXXK00kllolloxO0KKKKXXXXXXNNNNNNNNNNNNNNNXXXXXXKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkddddddddddddddddddooooolllllllllllllloooooooooddddddddddddoooooooolo
xxxxxxxxxxxxxxxxxxkkkkkkkkkkkOOkkkkkkkOOkkkxddkkkOOOOOOOO0000000000KKKOkkkkkkkkkdooodddddddddxxxkkkkOOOOOOOOO0OOOOO000000000000KKK000000000KKKKK000ko,..:x0XKdlodxk0KK0OO0klloxO0KKKKKXXXXXXXNNNNNNNNNNNNNNNXXXXXXXKKKK00OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkxoooolllllllllloooolooooooooodddddddddddooooooooooooooooooooddddxx
ddddddddddddddddxdxxxxxxxxxxxxxxxxxxxxxxxxxxxdddxxkkkxxxxkkkOOOOkkOO0K0OOOkkkkOkdlooooddddddddxxxkkkkOOOOOOOOOOOOOO000000000O00000000000K000000K000Oko,'d0KKKKkolloxxxkxOXKxxO000KKKKKXXXXXXXNNNNNNNNNNNNNNNXXXXXXXKKKK00OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddddddddxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkdoooloooooddddddddddooooooooooooooooooooooodddddxxxkkkkkkkkkkkkkk
lllllllllllllllloooooooodddddddddddddddddddxxxdoooddddddddddxxkOOOOkO00OkxxxxxxxolloooddddddddxxxxkkkkkkkkOOOOOOOOO0000000O0000000000000000000000000OkooO00KKX0o;::ccccoOXX0000KKKKKKKXXXXXNNNNNNNXXXNNNNNNNNXXXXXXKKKKK00Okkkxxkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddddddddddddddddddddddddoooooolllllllldkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxdoooooooooooooooooooodddddxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
xxdddddddddoooooooooolooolllllllllllllllllloodoollooooddddodddxxkkOOkO00OxddddddollllooodddddddddxxkkkkkkkkkOOOOOOOOOO000O000000000000000000000000000OkkkkkOO0Odc,'''';dOKK000KKKKKKXXXXXXXNNNNNNXXXXXXNNNNNNXXXXXXKKKKKK0Okxxxxdddddddddddddddddddddddddddddddddoooooooollllllllllllllllllllllllloolloooooddxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxdooodddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
oooooooodooodddddddddxxxxxxxxxxxxddddddoooolccccccllooodddddxxxxxxkkkkO0KOxdollllllllooodddddddddxxxxkkkkkkkkkkOOOOOOOOOOOOOOO00000000000000000000000Oxxddodddddo:;,,:lxkOOOO0KKKXXXXXXXXXXXNNNNNXXXXXXXXXXNNXXXXXXXXXKKK00koooooooollllllllllllllllllllllllllllollloooolloooooooooodddddddddddddddddddddxkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
xxxxxxxxddddddddddoodddddooooooooooooooooool:::::cccllloddddxxkkkkkkOOOO0KXKK0kxollllooodddddxddxxxxxxkkkkkkkkkkOOOOOOOOOOOOOO0000000000000000000000Oxooolcccclll:;;;clooddddk0KKXXXXXXXXXXXXNNNNXXXXXXXXXXXNXXXXXXXXXXKKK0Odoooooooooooooooddoddddddddddddxxddddddddddddoooooooooooooooooddddddddddddddddxkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxdddddddddddoc::::::::cclooooodxkkOOO00O00KKKXXK0xollloooddxxxxxxxxxxxkkkxkkkkkkkOOOOOOOOOOOOOO00000000000OOOO00000OOxccllc:ccccc:::::ccccllok0KKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKK0Oxdddddddddoodoooooooooooddoodddddoooodddoddddddddddddddxxxxxxkkkkkkkkkkkkkkkxkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkoc::;;;;;:::cllcccodxkkOO00000KKKKKXK0xoloooddxxxxxxxxxxxkkkkkkkkkkkkkkOOOOOOOOOOOO0000000000OOOOOOOOOOOxc,;c:::cccc:::::::ccclokO0KKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKK0kdxdddddddddddddxxdxxxxxxxxxkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxc::;;:;;;:::clllcclodxkOOOO000KKKKKKKKOdoooddxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkOOkOOOOO000000000OOOOOOOOOOkxl,',:::::::::::::ccccloxO0KKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKK0OOOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkl:::;;;;;::::ccloollodxxkkOO000KKKKKKKKKkdoddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOO00000000OOOkkkOOkkkxo:'.....''',;:::ccc::cdxO0KKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKK0Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::;;;;;;;;:::ccllcclodxxkOO0000000K00KK0kdddxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000OOkkkkkkkkxoc;. ...',,;,;,,:oxkO0000KKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::;;;;;;;;;;:::ccclllodxxxkOOOOO0000000KK0xdxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000OOOOkkkkkkxoc;' ...'''',,,:odxkOO0000KKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKXKKKOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::;;;;;;;;;;::::::cllooddxxxkkOOO000OO00KKKOxxxkkxkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOOO000000000OOOkkkkkxdol:,. ..'',,,,;;:ldxkkOO000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKK0OkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdcc::;;;;;;;;;;:::::::looodddddxkOOO0OO0OO00O0OxxxxxkkkkkkkkkkxxkkkkkkkkkkkkkkkkkOOOOOOOO000000OOkkkkxxdol:,. ..',,,;;;:lddxxkOO0000KKKKKKKKKKKKXXXKXXXXXXXXXXXXXXXXXXXXXXXXXKKKK0OkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxlc::;;;;;;;;;;;:::::::cloooooodxkOOOO0OkkkkkOOkxxxxkkkkkkkkkkxxxkkkxxxkkkkkkkkkkkOOOOOOOOO000OOOkkkxxddolc;. ..',,;:::lodxxxkOOO00000000KKKKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXK0Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoc::;;;;;;;;;;;:::::::::llooolloxxkkOOdloxxkO0OkxxkkkkkkkkkkxxxxxxxxxxxxxxxkkkkkkOOOOOOOOOO00OOOkxxxxddolc:' ..'',;;::clodxxxkkOOO0000000KKKKKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXKK0OkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdl:::;;;;;;;;;;;;::::::;:clddddooddxOOdcldxxkO0OkkkkkkkkkkkkxxxxxxxxxxxxxxxxkkkkkOOkkOOOOOOO00OOkxxxdddolc:' .'',;;:cloddxxxkkOOOO0000000K00KKK00KKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXKKK0OkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkoc::;;;;;;;;;;;;;;::cccccloddoooddxkOkolodxkO00OkkkkOkkkkkkxxxxxxxxxxxxxxxxxxkkkkkkkkOOOOOO0OOOkkxddddolc:,. ..',;;:cllodddxxkkkOOOO0000000000000000KKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXKK0OkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoc::;;;;;;;;;;;;:c::ccc::::cllodxkOO0kocodxkO00OkkkkkkkkxxxxxxxxxxxxxxxxxxxkkkkkkkkkOOOOOOOOOOOkxxddoolc:,. .',;;:cllodddxxxkkkOOOO000000000000000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXK0OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::;;;;;;;;;;;;::;;;;;;;::ldkkkkOKKKOocldxkO00OkkkkkkxxxxxxxxxxxxxxxxxxxxkkkkkkkkOOOOOOOOOOOOkxxddoolc:,. .',;:ccllloddddxxkkkOOOOO00000000000000KKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXKK0OkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc:::;;;;;;;;;;;::::::::::::ldkOOOO000OocdkkO000OkxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkOOOOOOOOOOOOOkxxddoolc:,. ..,;:cclllodddxxxxxkOOOOOOO000000000000KKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXKKK0OOOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkOko:::;;;;;;;;;;;:cccccc::::::coxxxxkkO0xcokkxxkkkkxxxxxxxxddxxxdxxxxxxxkkxxxxxkkkkOOOOOOOOOOOkkxxddoolc:,. ..';::cllloodddxxxxkkkOOOOOOOOOO00000000KKKKKKKKKKKKKKKKXXXXXXXXXXXXXXKKK0OOOOkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::::::;;;;;;;;:lloolc:;::;;:loxxxkkOkccoxxolodxxxdddddddxxxxxxxxxxxxxkkkkkkkkkkkkkOOOOOOOOkkxxdoooll:;. ..';::clllloodddxxxxkkkkkOOOOOOOOO0000000000KKKKKKKKKKKKKXXXXXXXXXXXXXXKKK0OOkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkdc::::::;;;;;;;;:cccclc;;;;;;;:ldxxkOOkl:loxxxxxxxxdddddddxxxxddddxxxxxxkkkkkkkkkkkkkkkOOkkkkkxxdoollc:;. ..,;::ccllloooddddxxxxkkkkkkOOOOOOO0000000000KKKKKKKKKKKKKKKXXXXXXXXXXXXKK00OkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::::::;;;;;;;;;:::::;;;,,,;:coxkOO0Oo;:lodxddxxxddddddddxxdddddxxxxxxxkkkkkkkkkkkkkkkkkkkkxxddoollc:;. ..';::cccllloodddddxxxxxkkkkkOOOOOO0000000000000000KKKKKKKKKKKKXXXXXXXXXKKK0OOOOkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdolc::::;;;;;;;;;;;;;;,'..,;:oxOO00KOl;:codxkOOxdxdddddddddddddxxxxxxxkkkkkkkkkkkkkkkkkkkkxxddoollc:;'....',::cccllllooodddddxxxxxkkkkkkkOOOOO0000000000000KKKKKKKKKKKKKKKKXXXXXKKK00OOOkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkOOkkkkkkkkkkkkkkkkOOkkkxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoc:::;;;;;;;;;:::;;'..';cldkOO00Kx:;ccoxkOOkxdddddddddddddddxxxxxxxxxkkkkkkkkkkkkkkxxxxxddoolcc::;'...',;:ccclllllooodddddxxxxxxxxxkkkOOOOO0000000000000KKKKKKKKKKKKXXKKXKKKKKKK0OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkxxkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkxdolc:::;;;:::ccc:,...,:codxOO000l,;:codxxdolodddddddddddddddxxxxxxxxkxxxxxkkkkkkkxxxxxddollcc:coc''',,;::cclllllooooodddddxxxxxxxxkkkkOOO00OOOOO0000000KKKKKKKKKKXXXKKXKKKKKKK00OOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::;;::::cc:,....;:cldkkO00x;',,;:c:;:clooooooddddddddddxxxxxxxxxxxkkkkkkkkxxxxxxdoollcc:cxkl,,,,;:::ccccllllooooddddddxxxxxxxkkkkOOOOOOOOO00000000KKKKKKKKKXXKKXXXKKKKK000OOOkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkxkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::;:::::c:;,'..';:clodkOOOd:;'....';clooooooddddddddddxxxxxxxxxxxkkkkkkkxxxxxxxddoolcc:lkOxl;,;;:::ccccllllllooooddddddxxxxxkkkkOOOOOOO00000000000KKKKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOxoc::::::::::;,..',;:clldxkkOOOkdl,',:clooooooooodddddddxxxxxxxxxxxxkkkkxxxxxxxxxddoolc::dOkkxc;;;:::cccccllllloooooodddddxxxxxkkkkOOOOOOOOO000000000KKKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkdl:::::;:::;,''.,;;:codxkkxkOO00x:,:cllloooooooooooddddxxxxxxxxxxxkkkxxxxxxxxxdddolcc;cxOkkkxc;;::::cccccclllllloooodddddxxxxxkkkkOOOOOOOOO00000000000KKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkocc::::::;;;,,;:::clooodooodddl;,;:cllloooooooooddddddddddxxxxxxxkkxxxxxxxxxxddooc::okkkkkkxl::::ccccccccccllllooooodddddxxxkkkOOOOOOOOOO0000000000KKKKKKKKKKKKKKKKKKKK000OkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkxkkkkkOkkkkkkkOkkkkkkkkkkOOOkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkOOOkkkkkOkkkkkkkkkkkkkkkkkkkOkkxxdl::::;;;,;cc:;;;::::::::;;,,;::cclllooooooooodddddddddxxxxxxxxxxxxxxxxxxxddoc:cxkkkkkkkxl:::ccccccccccclllloooooddddxxxxkkOOOkkkOOOO00000000000KKKKKKKKKKKKKKKKKKKK00OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkOkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkOkkkkkOkkkkkkkOOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::::;;;:c::;;;;;;;,'.'',;;::ccllllloollooooooooodddddxxxxxxxxxxxxxxxxxxdoc:okkkkkkkkOxl:ccccccccccccllllllloooooddxxxkkOOOkkOOOOOOOO00000000KKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkOOkkkkkkkkkkkkkkkkkkkOkkkkkkkkkOOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkOkkkkkOOOOkkkkOkkkkkkkkkkOOOkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkkdl::::::::::;;,,,,,,'...',;;;:ccclllllllllooooooooodddddddxxxxxxxxxxxxkxxdoccdOkkkkkkkkkxlcccccccccccccllllllooooodddxxkkkOOOOOOOOOOOO00000000000KKKKKKKKKKKKKKK0000OOOOkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkOOkkkkkOkkkkkkkkkkkkkkOOkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkOOOOOkkkOOOOkkkkOOOOkkkOOOkkOOkkkkkOOkkkkkkkkOOkkkkOkkkkkkkkkOOOkkkkOkkkxdoc:::::::::;;;;;;,,,,;;;;::ccllllllllllllooooooodddddddddxxxddxxxkkkxxoclkOkkkkkkkkkOkoccccccccccccclllllllloodddxxkkkOOOOOOOO0OO0000000000000KKKKKKKKKKKKKK00000OOOkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkOkkkkkkkkkOOkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
OkkkkkOOOkkkkOOkkkkkkkOOkkkOOOOOkkkkkkkkOkkkkkkOOOOOOOOOOOOOOkkkOkkOOkkkkkkkkkOkkxdolc:::cllllc::;;;::;;:::cccccllllllllooooooooddddddddxxxxxxxxkkkkxocdkkkkkkkOkkOkkkdcccccc::ccccccllllllooooddxxkkkOOOOOOO0000000000000000KKKKKKKKKKKKKK00000OOOkkOOOOOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkOOOkkkkkkkkkkkkkkkkkkkOOOOOOOOkkkkkkOOOOOkkkkkkOOOOkkkkkOOOOkkkkOOkkkkkkkkkkkkkkkxxxxkkkkxoc:::::::;:::ccccccllllllllooooooooooddddddddxxxxkkkkkkdlxOkkkkkkkkkOkkkkxlcccccc:ccccccclllllooooddxxkkkkOOOOO00000000000000000KKKKKKKKKKKK0000000OOkOOkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkOOOOOOkkkOOOOOOOkkkkOOOOkkkkkkkOkkkkkOOOkkkkkOOOkkkkkkkOkkOkkkkdlc::c:::;:::::cccclllllllllllllloooooodddddddxxxkkkkkkdoxOkkkkkkkkkkkkkOkxocccccccccccccclllloooooddxxkkkkkOOO0000000000000000KKK00KKKKKKKKK000000OOOOOkkkOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkOOkkkkkkkkOOOOOOkkkOOOkkkkkkkkkkOkkkOkkkkkkkkkkkkkkkOkkkOkkkkkxlccc:::::::::cccccclllllllllllllllloooooodddddxxkkkkkkxdkkkkkkkkkkkkkOOOkkkdlccccccccccccccllllloooddxxkkkkkOOOO00000000000KKKKK0KKKKKKKKKKK0000000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkxkkkkkkkOOkkkkkkkkkkkOOOkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkdccc:::::::::cccccccclllclllllllllllooooodddddxxxkkkkkxxkkkkkkkkkkkkkkkOkkkkxoccccccccccccccllllloooddxxkkkkOOOOOO00000000K0000K0KKKKKKKK0000000000OOkkkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxocc::::::::::ccccccccccccllllllllllllooooooddxxxkkkkkkkkkkkkkkkkkkkkkkOkkkkkxolclcccccccccccllllloooddxxxkkkkkOOO0000000000KKKKKKKKKKK000000000000OOOkkkkkkkkkkkkkkkkkkkkkkOOOOOkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkOOOkkkOOkkkkkkkkkkkkkkkkOkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkdlcc::::::::::cccccccccccccccclllllllllooooddxxxkkkkkxxkkkkkkkkkkkkkkkOkkkkkxkxocccccccccccccllllllooddxxxkkkkkOO000000000KKKKKKKKKK000000000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kOOkkkOkkkkkkkkkkkkkkkkkkkkkkkkkOkkOOOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkoccc:::::::::cc::ccccccccccccccccllllllloooddxxxxxkkxxkkkOkkkkkkkkOkkOkkkkkxkkxdllccccccc:ccclllllloodddxxkkkkOOO00000000KKKKKKKKK0000000000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOkkkkkkkkkkkkkkkkkkkkkkkkdcccc::::::::ccc:cccccccccccccccccclllllloodddxxxxkkxxxkkkOOkkkOkOOOOOkkkkkxkkkkxolccccccccccccclllloodddxxkkkOOOO000000000KKKKK000000000000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkOkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkkkkkkkkOOOOOOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkOkkOkkkkkkkkkkkkkkkkkkkkOkocccc::::::::::::::::::::ccccccccccclllloooddxxxxkkxxxkkkkkkkkkkkkOOOkxkkkxkkxkOkxolcccccccccccclllooodddxxkkkOOOO00000000KKKKKKKK00000000000000OOOOkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkkkOOkkkkkkkOOOOOOOkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkOOOkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkxlcccc::::::::::::::::::::::::c::ccccllloooddddxxxxxxxkkkkkkkkkkkkkkOkkkkkxkkkkkOOkdlcccccccccccclllooooddxxkkOOOO00000000KKKKKKKK00000000000000OOOkkOkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
kkkkkkkkkkkkkOOOkkkkkkkOOOOOOOOOOkkkkkkkkkkkkkkkkkOOOOOkkkkOOOOOkkkOkkkOkkkkkkkkkkkkkkkkkkkkkkkkkOkoccccc::::::::::::::::::::::::::ccccllllooddddxxxxxdxxkkkkkkkkkkkkOOkkkkkxkkkkkOOOOxllcccccccccccllloooddxxkkOOOO0000000000000KKK00000000OOOOOOOOkkkOkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkkxkkkkkkkkkkOkkkkkOkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
*/

Some files were not shown because too many files have changed in this diff Show More