diff --git a/src/lib.rs b/src/lib.rs index ea200ac..0a611bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,9 +85,9 @@ impl Kid { #[allow(clippy::fallible_impl_from)] impl From for Kid { fn from(pid: cargo_metadata::PackageId) -> Self { - let repr = pid.repr; + let mut repr = pid.repr; - let gen = || { + let mut gen = || { let components = if repr.contains(' ') { let name = (0, repr.find(' ')?); let version = (name.1 + 1, repr[name.1 + 1..].find(' ')? + name.1 + 1); @@ -101,13 +101,79 @@ impl From for Kid { [name, version, source] } else { - let vmn = repr.rfind('#')?; + let mut vmn = repr.rfind('#')?; let (name, version) = if let Some(split) = repr[vmn..].find('@') { ((vmn + 1, vmn + split), (vmn + split + 1, repr.len())) } else { let begin = repr.rfind('/')? + 1; let end = if repr.starts_with("git+") { - repr[begin..].find('?').map_or(vmn, |q| q + begin) + // Unfortunately the stable format percent encodes the source url in the metadata, and since + // git branches/tags can contain various special characters, notably '/', we need to decode them + // to be able to match against the non-encoded url used...everywhere else + let end = repr[begin..].rfind('?').map_or(vmn, |q| q + begin); + + if repr[end..].contains('%') { + // https://en.wikipedia.org/wiki/Percent-encoding + // ␣ ! " # $ % & ' ( ) * + , / : ; = ? @ [ ] + // %20 %21 %22 %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D + let mut decoded = String::new(); + let mut encoded = &repr[end..]; + let before = encoded.len(); + + loop { + let Some(pi) = encoded.find('%') else { + decoded.push_str(encoded); + break; + }; + + decoded.push_str(&encoded[..pi]); + + let Some(encoding) = encoded.get(pi + 1..pi + 3) else { + // This _should_ never happen, but just in case + panic!("invalid percent encoding in '{}', '{}' should be exactly 3 digits long", &repr[end..], &encoded[pi..]); + }; + + let c = match encoding { + // By far the most likely one + "2F" | "2f" => '/', + "20" => ' ', + "21" => '!', + "22" => '"', + "23" => '#', + "24" => '$', + "25" => '%', + "26" => '&', + "27" => '\'', + "28" => '(', + "29" => ')', + "2A" | "2a" => '*', + "2B" | "2b" => '+', + "2C" | "2c" => ',', + "3A" | "3a" => ':', + "3B" | "3b" => ';', + "3D" | "3d" => '=', + "3F" | "3f" => '?', + "40" => '@', + "5B" | "5b" => '[', + "5D" | "5d" => ']', + _ => panic!( + "unknown percent encoding '%{encoding}' in '{}'", + &repr[end..] + ), + }; + + decoded.push(c); + encoded = &encoded[pi + 3..]; + } + + repr.truncate(end); + repr.push_str(&decoded); + + // move the version string back to account for the now shorter decoded repr + vmn -= before - decoded.len(); + } + + end } else { vmn }; diff --git a/tests/snapshots/misc__finds_duplicates.snap b/tests/snapshots/misc__finds_duplicates.snap index dd7c16b..74648ec 100644 --- a/tests/snapshots/misc__finds_duplicates.snap +++ b/tests/snapshots/misc__finds_duplicates.snap @@ -6,400 +6,521 @@ digraph { 0 [ label = "crate bitflags 2.4.2" ] 1 [ label = "crate bumpalo 3.14.0" ] 2 [ label = "crate bytes 1.5.0" ] - 3 [ label = "crate cfg-if 1.0.0" ] - 4 [ label = "crate deranged 0.3.11" ] - 5 [ label = "crate fnv 1.0.7" ] - 6 [ label = "crate futures-core 0.3.30" ] - 7 [ label = "crate futures-task 0.3.30" ] - 8 [ label = "crate futures-util 0.3.30" ] - 9 [ label = "crate getrandom 0.1.16" ] - 10 [ label = "crate getrandom 0.2.12" ] - 11 [ label = "crate http 0.2.11" ] - 12 [ label = "crate http 1.0.0" ] - 13 [ label = "crate http-body 0.4.6" ] - 14 [ label = "crate http-body 1.0.0" ] - 15 [ label = "crate http-body-util 0.1.0" ] - 16 [ label = "crate http-range-header 0.3.1" ] - 17 [ label = "crate itoa 1.0.10" ] - 18 [ label = "crate js-sys 0.3.67" ] - 19 [ label = "crate libc 0.2.152" ] - 20 [ label = "crate log 0.4.20" ] - 21 [ label = "crate objc-sys 0.2.0-beta.2 git+https://github.com/madsmtm/objc2?rev=65de002" ] - 22 [ label = "crate objc-sys 0.3.2" ] - 23 [ label = "crate objc2 0.3.0-beta.3.patch-leaks.2 git+https://github.com/madsmtm/objc2?rev=65de002" ] - 24 [ label = "crate objc2 0.3.0-beta.5" ] - 25 [ label = "crate objc2 0.5.0" ] - 26 [ label = "crate objc2-encode 2.0.0-pre.2 git+https://github.com/madsmtm/objc2?rev=65de002" ] - 27 [ label = "crate objc2-encode 2.0.0-pre.4" ] - 28 [ label = "crate objc2-encode 4.0.0" ] - 29 [ label = "crate once_cell 1.19.0" ] - 30 [ label = "crate pid 0.1.0 path+file:///krates/tests/pid" ] - 31 [ label = "crate pin-project-lite 0.2.13" ] - 32 [ label = "crate pin-utils 0.1.0" ] - 33 [ label = "crate powerfmt 0.2.0" ] - 34 [ label = "crate proc-macro2 1.0.78" ] - 35 [ label = "crate quote 1.0.35" ] - 36 [ label = "crate syn 2.0.48" ] - 37 [ label = "crate time 0.3.31" ] - 38 [ label = "crate time-core 0.1.2" ] - 39 [ label = "crate tower-http 0.4.4" ] - 40 [ label = "crate tower-http 0.5.1" ] - 41 [ label = "crate tower-layer 0.3.2" ] - 42 [ label = "crate tower-service 0.3.2" ] - 43 [ label = "crate unicode-ident 1.0.12" ] - 44 [ label = "crate wasi 0.11.0+wasi-snapshot-preview1" ] - 45 [ label = "crate wasi 0.9.0+wasi-snapshot-preview1" ] - 46 [ label = "crate wasm-bindgen 0.2.90" ] - 47 [ label = "crate wasm-bindgen-backend 0.2.90" ] - 48 [ label = "crate wasm-bindgen-macro 0.2.90" ] - 49 [ label = "crate wasm-bindgen-macro-support 0.2.90" ] - 50 [ label = "crate wasm-bindgen-shared 0.2.90" ] - 51 [ label = "feature alloc" ] - 52 [ label = "feature alloc" ] - 53 [ label = "feature default" ] - 54 [ label = "feature default" ] - 55 [ label = "feature default" ] - 56 [ label = "feature default" ] - 57 [ label = "feature default" ] - 58 [ label = "feature alloc" ] - 59 [ label = "feature alloc" ] - 60 [ label = "feature apple" ] - 61 [ label = "feature std" ] - 62 [ label = "feature alloc" ] - 63 [ label = "feature apple" ] - 64 [ label = "feature std" ] - 65 [ label = "feature alloc" ] - 66 [ label = "feature apple" ] - 67 [ label = "feature std" ] - 68 [ label = "feature alloc" ] - 69 [ label = "feature std" ] - 70 [ label = "feature alloc" ] - 71 [ label = "feature std" ] - 72 [ label = "feature wasm-bindgen" ] - 73 [ label = "feature js" ] + 3 [ label = "crate camino 1.1.7" ] + 4 [ label = "crate cargo-platform 0.1.8" ] + 5 [ label = "crate cargo_metadata 0.18.1" ] + 6 [ label = "crate cfg-expr 0.15.8" ] + 7 [ label = "crate cfg-if 1.0.0" ] + 8 [ label = "crate deranged 0.3.11" ] + 9 [ label = "crate equivalent 1.0.1" ] + 10 [ label = "crate fixedbitset 0.4.2" ] + 11 [ label = "crate fnv 1.0.7" ] + 12 [ label = "crate futures-core 0.3.30" ] + 13 [ label = "crate futures-task 0.3.30" ] + 14 [ label = "crate futures-util 0.3.30" ] + 15 [ label = "crate getrandom 0.1.16" ] + 16 [ label = "crate getrandom 0.2.12" ] + 17 [ label = "crate hashbrown 0.14.5" ] + 18 [ label = "crate http 0.2.11" ] + 19 [ label = "crate http 1.0.0" ] + 20 [ label = "crate http-body 0.4.6" ] + 21 [ label = "crate http-body 1.0.0" ] + 22 [ label = "crate http-body-util 0.1.0" ] + 23 [ label = "crate http-range-header 0.3.1" ] + 24 [ label = "crate indexmap 2.2.6" ] + 25 [ label = "crate itoa 1.0.10" ] + 26 [ label = "crate js-sys 0.3.67" ] + 27 [ label = "crate krates 0.16.10 git+https://github.com/EmbarkStudios/krates?branch=branch/test" ] + 28 [ label = "crate krates 0.16.10 git+https://github.com/EmbarkStudios/krates?tag=0.16.10" ] + 29 [ label = "crate libc 0.2.152" ] + 30 [ label = "crate log 0.4.20" ] + 31 [ label = "crate objc-sys 0.2.0-beta.2 git+https://github.com/madsmtm/objc2?rev=65de002" ] + 32 [ label = "crate objc-sys 0.3.2" ] + 33 [ label = "crate objc2 0.3.0-beta.3.patch-leaks.2 git+https://github.com/madsmtm/objc2?rev=65de002" ] + 34 [ label = "crate objc2 0.3.0-beta.5" ] + 35 [ label = "crate objc2 0.5.0" ] + 36 [ label = "crate objc2-encode 2.0.0-pre.2 git+https://github.com/madsmtm/objc2?rev=65de002" ] + 37 [ label = "crate objc2-encode 2.0.0-pre.4" ] + 38 [ label = "crate objc2-encode 4.0.0" ] + 39 [ label = "crate once_cell 1.19.0" ] + 40 [ label = "crate petgraph 0.6.5" ] + 41 [ label = "crate pid 0.1.0 path+file:///krates/tests/pid" ] + 42 [ label = "crate pin-project-lite 0.2.13" ] + 43 [ label = "crate pin-utils 0.1.0" ] + 44 [ label = "crate powerfmt 0.2.0" ] + 45 [ label = "crate proc-macro2 1.0.78" ] + 46 [ label = "crate quote 1.0.35" ] + 47 [ label = "crate ryu 1.0.16" ] + 48 [ label = "crate semver 1.0.23" ] + 49 [ label = "crate serde 1.0.195" ] + 50 [ label = "crate serde_derive 1.0.195" ] + 51 [ label = "crate serde_json 1.0.111" ] + 52 [ label = "crate smallvec 1.13.2" ] + 53 [ label = "crate syn 2.0.48" ] + 54 [ label = "crate thiserror 1.0.61" ] + 55 [ label = "crate thiserror-impl 1.0.61" ] + 56 [ label = "crate time 0.3.31" ] + 57 [ label = "crate time-core 0.1.2" ] + 58 [ label = "crate tower-http 0.4.4" ] + 59 [ label = "crate tower-http 0.5.1" ] + 60 [ label = "crate tower-layer 0.3.2" ] + 61 [ label = "crate tower-service 0.3.2" ] + 62 [ label = "crate unicode-ident 1.0.12" ] + 63 [ label = "crate wasi 0.11.0+wasi-snapshot-preview1" ] + 64 [ label = "crate wasi 0.9.0+wasi-snapshot-preview1" ] + 65 [ label = "crate wasm-bindgen 0.2.90" ] + 66 [ label = "crate wasm-bindgen-backend 0.2.90" ] + 67 [ label = "crate wasm-bindgen-macro 0.2.90" ] + 68 [ label = "crate wasm-bindgen-macro-support 0.2.90" ] + 69 [ label = "crate wasm-bindgen-shared 0.2.90" ] + 70 [ label = "feature derive" ] + 71 [ label = "feature default" ] + 72 [ label = "feature serde1" ] + 73 [ label = "feature serde" ] 74 [ label = "feature default" ] - 75 [ label = "feature default" ] + 75 [ label = "feature unbounded_depth" ] 76 [ label = "feature default" ] - 77 [ label = "feature parsing" ] - 78 [ label = "feature default" ] - 79 [ label = "feature sensitive-headers" ] + 77 [ label = "feature alloc" ] + 78 [ label = "feature alloc" ] + 79 [ label = "feature default" ] 80 [ label = "feature default" ] - 81 [ label = "feature proc-macro" ] - 82 [ label = "feature proc-macro" ] - 83 [ label = "feature powerfmt" ] - 84 [ label = "feature default" ] - 85 [ label = "feature spans" ] + 81 [ label = "feature default" ] + 82 [ label = "feature default" ] + 83 [ label = "feature default" ] + 84 [ label = "feature alloc" ] + 85 [ label = "feature raw" ] 86 [ label = "feature default" ] 87 [ label = "feature default" ] 88 [ label = "feature default" ] - 89 [ label = "feature default" ] - 90 [ label = "feature full" ] - 91 [ label = "feature default" ] - 92 [ label = "feature spans" ] - 93 [ label = "feature visit" ] - 94 [ label = "feature spans" ] - 95 [ label = "feature std" ] - 96 [ label = "feature std" ] + 89 [ label = "feature alloc" ] + 90 [ label = "feature apple" ] + 91 [ label = "feature std" ] + 92 [ label = "feature alloc" ] + 93 [ label = "feature apple" ] + 94 [ label = "feature std" ] + 95 [ label = "feature alloc" ] + 96 [ label = "feature apple" ] 97 [ label = "feature std" ] - 98 [ label = "feature bindgen" ] - 99 [ label = "feature js-sys" ] - 100 [ label = "feature bindgen" ] - 101 [ label = "feature wasm-bindgen" ] - 102 [ label = "feature js-sys" ] - 103 [ label = "feature std" ] - 104 [ label = "feature std" ] - 105 [ label = "feature alloc" ] - 106 [ label = "feature foundation" ] - 107 [ label = "feature apple" ] - 108 [ label = "feature std" ] - 109 [ label = "feature alloc" ] - 110 [ label = "feature apple" ] - 111 [ label = "feature std" ] - 112 [ label = "feature alloc" ] - 113 [ label = "feature apple" ] - 114 [ label = "feature std" ] - 115 [ label = "feature alloc" ] - 116 [ label = "feature race" ] - 117 [ label = "feature time03" ] - 118 [ label = "feature time03" ] - 119 [ label = "feature default" ] - 120 [ label = "feature quote" ] - 121 [ label = "feature proc-macro" ] - 122 [ label = "feature printing" ] - 123 [ label = "feature parsing" ] - 124 [ label = "feature derive" ] - 125 [ label = "feature clone-impls" ] - 126 [ label = "feature std" ] - 127 [ label = "feature std" ] - 128 [ label = "feature spans" ] - 4 -> 33 [ label = "" ] - 8 -> 6 [ label = "" ] - 8 -> 51 [ label = "" ] - 8 -> 7 [ label = "" ] - 8 -> 52 [ label = "" ] - 8 -> 31 [ label = "" ] - 8 -> 32 [ label = "" ] - 9 -> 3 [ label = "" ] - 9 -> 18 [ label = " 'wasm32-unknown-unknown'" ] - 9 -> 19 [ label = " 'cfg(unix)'" ] - 9 -> 53 [ label = " 'cfg(target_os = \"wasi\")'" ] - 9 -> 54 [ label = " 'wasm32-unknown-unknown'" ] - 10 -> 3 [ label = "" ] - 10 -> 18 [ label = " 'cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))'" ] - 10 -> 19 [ label = " 'cfg(unix)'" ] - 10 -> 44 [ label = " 'cfg(target_os = \"wasi\")'" ] - 10 -> 46 [ label = " 'cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))'" ] - 11 -> 55 [ label = "" ] - 11 -> 56 [ label = "" ] - 11 -> 17 [ label = "" ] - 12 -> 55 [ label = "" ] - 12 -> 56 [ label = "" ] - 12 -> 17 [ label = "" ] - 13 -> 55 [ label = "" ] - 13 -> 11 [ label = "" ] - 13 -> 31 [ label = "" ] - 14 -> 55 [ label = "" ] - 14 -> 57 [ label = "" ] - 15 -> 55 [ label = "" ] - 15 -> 58 [ label = "" ] - 15 -> 57 [ label = "" ] - 15 -> 14 [ label = "" ] - 15 -> 31 [ label = "" ] - 18 -> 54 [ label = "" ] - 23 -> 21 [ label = "" ] - 23 -> 59 [ label = "" ] - 23 -> 60 [ label = "" ] - 23 -> 61 [ label = "" ] - 23 -> 26 [ label = "" ] - 23 -> 62 [ label = "" ] - 23 -> 63 [ label = "" ] - 23 -> 64 [ label = "" ] - 24 -> 22 [ label = "" ] - 24 -> 65 [ label = "" ] - 24 -> 66 [ label = "" ] - 24 -> 67 [ label = "" ] - 24 -> 27 [ label = "" ] - 24 -> 68 [ label = "" ] - 24 -> 69 [ label = "" ] - 25 -> 22 [ label = "" ] - 25 -> 65 [ label = "" ] - 25 -> 66 [ label = "" ] - 25 -> 67 [ label = "" ] - 25 -> 28 [ label = "" ] - 25 -> 70 [ label = "" ] - 25 -> 71 [ label = "" ] - 26 -> 21 [ label = "" ] - 26 -> 59 [ label = "" ] - 26 -> 60 [ label = "" ] - 26 -> 61 [ label = "" ] - 30 -> 72 [ label = "" ] - 30 -> 73 [ label = "" ] - 30 -> 74 [ label = "" ] - 30 -> 75 [ label = "" ] - 30 -> 76 [ label = "" ] - 30 -> 77 [ label = "" ] - 30 -> 78 [ label = "" ] - 30 -> 79 [ label = "" ] - 30 -> 80 [ label = "" ] - 34 -> 43 [ label = "" ] - 35 -> 34 [ label = "" ] - 35 -> 81 [ label = "" ] - 36 -> 34 [ label = "" ] - 36 -> 81 [ label = "" ] - 36 -> 35 [ label = "" ] - 36 -> 82 [ label = "" ] - 36 -> 43 [ label = "" ] - 37 -> 83 [ label = "" ] - 37 -> 33 [ label = "" ] - 37 -> 38 [ label = "" ] - 39 -> 0 [ label = "" ] - 39 -> 55 [ label = "" ] - 39 -> 84 [ label = "" ] - 39 -> 8 [ label = "" ] - 39 -> 11 [ label = "" ] - 39 -> 13 [ label = "" ] - 39 -> 16 [ label = "" ] - 39 -> 31 [ label = "" ] - 39 -> 41 [ label = "" ] - 39 -> 42 [ label = "" ] - 40 -> 0 [ label = "" ] - 40 -> 55 [ label = "" ] - 40 -> 57 [ label = "" ] - 40 -> 14 [ label = "" ] - 40 -> 15 [ label = "" ] - 40 -> 31 [ label = "" ] - 40 -> 41 [ label = "" ] - 40 -> 42 [ label = "" ] - 46 -> 3 [ label = "" ] - 46 -> 48 [ label = "" ] - 46 -> 85 [ label = "" ] - 47 -> 86 [ label = "" ] - 47 -> 20 [ label = "" ] - 47 -> 87 [ label = "" ] - 47 -> 88 [ label = "" ] - 47 -> 89 [ label = "" ] - 47 -> 90 [ label = "" ] - 47 -> 91 [ label = "" ] - 47 -> 50 [ label = "" ] - 48 -> 89 [ label = "" ] + 98 [ label = "feature alloc" ] + 99 [ label = "feature std" ] + 100 [ label = "feature alloc" ] + 101 [ label = "feature std" ] + 102 [ label = "feature default" ] + 103 [ label = "feature wasm-bindgen" ] + 104 [ label = "feature js" ] + 105 [ label = "feature default" ] + 106 [ label = "feature default" ] + 107 [ label = "feature default" ] + 108 [ label = "feature default" ] + 109 [ label = "feature default" ] + 110 [ label = "feature parsing" ] + 111 [ label = "feature default" ] + 112 [ label = "feature sensitive-headers" ] + 113 [ label = "feature default" ] + 114 [ label = "feature proc-macro" ] + 115 [ label = "feature default" ] + 116 [ label = "feature default" ] + 117 [ label = "feature default" ] + 118 [ label = "feature default" ] + 119 [ label = "feature std" ] + 120 [ label = "feature proc-macro" ] + 121 [ label = "feature powerfmt" ] + 122 [ label = "feature default" ] + 123 [ label = "feature spans" ] + 124 [ label = "feature default" ] + 125 [ label = "feature default" ] + 126 [ label = "feature full" ] + 127 [ label = "feature spans" ] + 128 [ label = "feature visit" ] + 129 [ label = "feature spans" ] + 130 [ label = "feature std" ] + 131 [ label = "feature serde" ] + 132 [ label = "feature std" ] + 133 [ label = "feature std" ] + 134 [ label = "feature bindgen" ] + 135 [ label = "feature js-sys" ] + 136 [ label = "feature bindgen" ] + 137 [ label = "feature wasm-bindgen" ] + 138 [ label = "feature js-sys" ] + 139 [ label = "feature std" ] + 140 [ label = "feature std" ] + 141 [ label = "feature std" ] + 142 [ label = "feature alloc" ] + 143 [ label = "feature foundation" ] + 144 [ label = "feature apple" ] + 145 [ label = "feature std" ] + 146 [ label = "feature alloc" ] + 147 [ label = "feature apple" ] + 148 [ label = "feature std" ] + 149 [ label = "feature alloc" ] + 150 [ label = "feature apple" ] + 151 [ label = "feature std" ] + 152 [ label = "feature alloc" ] + 153 [ label = "feature race" ] + 154 [ label = "feature stable_graph" ] + 155 [ label = "feature matrix_graph" ] + 156 [ label = "feature graphmap" ] + 157 [ label = "feature time03" ] + 158 [ label = "feature time03" ] + 159 [ label = "feature default" ] + 160 [ label = "feature std" ] + 161 [ label = "feature serde_derive" ] + 162 [ label = "feature std" ] + 163 [ label = "feature quote" ] + 164 [ label = "feature proc-macro" ] + 165 [ label = "feature printing" ] + 166 [ label = "feature parsing" ] + 167 [ label = "feature derive" ] + 168 [ label = "feature clone-impls" ] + 169 [ label = "feature std" ] + 170 [ label = "feature std" ] + 171 [ label = "feature spans" ] + 3 -> 70 [ label = "" ] + 3 -> 71 [ label = "" ] + 4 -> 71 [ label = "" ] + 5 -> 72 [ label = "" ] + 5 -> 4 [ label = "" ] + 5 -> 73 [ label = "" ] + 5 -> 74 [ label = "" ] + 5 -> 70 [ label = "" ] + 5 -> 71 [ label = "" ] + 5 -> 75 [ label = "" ] + 5 -> 76 [ label = "" ] + 5 -> 54 [ label = "" ] + 6 -> 52 [ label = "" ] + 8 -> 44 [ label = "" ] + 14 -> 12 [ label = "" ] + 14 -> 77 [ label = "" ] + 14 -> 13 [ label = "" ] + 14 -> 78 [ label = "" ] + 14 -> 42 [ label = "" ] + 14 -> 43 [ label = "" ] + 15 -> 7 [ label = "" ] + 15 -> 26 [ label = " 'wasm32-unknown-unknown'" ] + 15 -> 29 [ label = " 'cfg(unix)'" ] + 15 -> 79 [ label = " 'cfg(target_os = \"wasi\")'" ] + 15 -> 80 [ label = " 'wasm32-unknown-unknown'" ] + 16 -> 7 [ label = "" ] + 16 -> 26 [ label = " 'cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))'" ] + 16 -> 29 [ label = " 'cfg(unix)'" ] + 16 -> 63 [ label = " 'cfg(target_os = \"wasi\")'" ] + 16 -> 65 [ label = " 'cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))'" ] + 18 -> 81 [ label = "" ] + 18 -> 82 [ label = "" ] + 18 -> 25 [ label = "" ] + 19 -> 81 [ label = "" ] + 19 -> 82 [ label = "" ] + 19 -> 25 [ label = "" ] + 20 -> 81 [ label = "" ] + 20 -> 18 [ label = "" ] + 20 -> 42 [ label = "" ] + 21 -> 81 [ label = "" ] + 21 -> 83 [ label = "" ] + 22 -> 81 [ label = "" ] + 22 -> 84 [ label = "" ] + 22 -> 83 [ label = "" ] + 22 -> 21 [ label = "" ] + 22 -> 42 [ label = "" ] + 24 -> 9 [ label = "" ] + 24 -> 85 [ label = "" ] + 26 -> 80 [ label = "" ] + 27 -> 4 [ label = "" ] + 27 -> 86 [ label = "" ] + 27 -> 87 [ label = "" ] + 27 -> 88 [ label = "" ] + 27 -> 74 [ label = "" ] + 28 -> 4 [ label = "" ] + 28 -> 86 [ label = "" ] + 28 -> 87 [ label = "" ] + 28 -> 88 [ label = "" ] + 28 -> 74 [ label = "" ] + 33 -> 31 [ label = "" ] + 33 -> 89 [ label = "" ] + 33 -> 90 [ label = "" ] + 33 -> 91 [ label = "" ] + 33 -> 36 [ label = "" ] + 33 -> 92 [ label = "" ] + 33 -> 93 [ label = "" ] + 33 -> 94 [ label = "" ] + 34 -> 32 [ label = "" ] + 34 -> 95 [ label = "" ] + 34 -> 96 [ label = "" ] + 34 -> 97 [ label = "" ] + 34 -> 37 [ label = "" ] + 34 -> 98 [ label = "" ] + 34 -> 99 [ label = "" ] + 35 -> 32 [ label = "" ] + 35 -> 95 [ label = "" ] + 35 -> 96 [ label = "" ] + 35 -> 97 [ label = "" ] + 35 -> 38 [ label = "" ] + 35 -> 100 [ label = "" ] + 35 -> 101 [ label = "" ] + 36 -> 31 [ label = "" ] + 36 -> 89 [ label = "" ] + 36 -> 90 [ label = "" ] + 36 -> 91 [ label = "" ] + 40 -> 10 [ label = "" ] + 40 -> 102 [ label = "" ] + 41 -> 103 [ label = "" ] + 41 -> 104 [ label = "" ] + 41 -> 105 [ label = "" ] + 41 -> 106 [ label = "" ] + 41 -> 107 [ label = "" ] + 41 -> 108 [ label = "" ] + 41 -> 109 [ label = "" ] + 41 -> 110 [ label = "" ] + 41 -> 111 [ label = "" ] + 41 -> 112 [ label = "" ] + 41 -> 113 [ label = "" ] + 45 -> 62 [ label = "" ] + 46 -> 45 [ label = "" ] + 46 -> 114 [ label = "" ] 48 -> 49 [ label = "" ] - 48 -> 92 [ label = "" ] - 49 -> 88 [ label = "" ] - 49 -> 89 [ label = "" ] - 49 -> 93 [ label = "" ] - 49 -> 90 [ label = "" ] - 49 -> 91 [ label = "" ] - 49 -> 47 [ label = "" ] - 49 -> 94 [ label = "" ] - 49 -> 50 [ label = "" ] - 86 -> 1 [ label = "" ] - 95 -> 2 [ label = "" ] - 55 -> 2 [ label = "" ] - 55 -> 95 [ label = "" ] - 83 -> 4 [ label = "" ] - 83 -> 33 [ label = "" ] - 96 -> 5 [ label = "" ] - 56 -> 5 [ label = "" ] - 56 -> 96 [ label = "" ] - 97 -> 6 [ label = "" ] - 97 -> 51 [ label = "" ] - 84 -> 6 [ label = "" ] - 84 -> 97 [ label = "" ] - 51 -> 6 [ label = "" ] - 52 -> 7 [ label = "" ] - 58 -> 8 [ label = "" ] - 58 -> 51 [ label = "" ] - 58 -> 52 [ label = "" ] - 72 -> 9 [ label = "" ] - 72 -> 98 [ label = "" ] - 72 -> 99 [ label = "" ] - 99 -> 9 [ label = "" ] - 99 -> 18 [ label = "" ] - 98 -> 9 [ label = "" ] - 98 -> 100 [ label = "" ] - 101 -> 10 [ label = "" ] - 101 -> 46 [ label = "" ] - 102 -> 10 [ label = "" ] - 102 -> 18 [ label = "" ] - 73 -> 10 [ label = "" ] - 73 -> 101 [ label = "" ] - 73 -> 102 [ label = "" ] - 103 -> 12 [ label = "" ] - 57 -> 12 [ label = "" ] - 57 -> 103 [ label = "" ] - 61 -> 21 [ label = "" ] - 61 -> 59 [ label = "" ] - 60 -> 21 [ label = "" ] + 49 -> 115 [ label = "" ] + 50 -> 116 [ label = "" ] + 50 -> 117 [ label = "" ] + 50 -> 118 [ label = "" ] + 51 -> 25 [ label = "" ] + 51 -> 47 [ label = "" ] + 51 -> 49 [ label = "" ] + 51 -> 119 [ label = "" ] + 53 -> 45 [ label = "" ] + 53 -> 114 [ label = "" ] + 53 -> 46 [ label = "" ] + 53 -> 120 [ label = "" ] + 53 -> 62 [ label = "" ] + 54 -> 55 [ label = "" ] + 55 -> 116 [ label = "" ] + 55 -> 117 [ label = "" ] + 55 -> 118 [ label = "" ] + 56 -> 121 [ label = "" ] + 56 -> 44 [ label = "" ] + 56 -> 57 [ label = "" ] + 58 -> 0 [ label = "" ] + 58 -> 81 [ label = "" ] + 58 -> 122 [ label = "" ] + 58 -> 14 [ label = "" ] + 58 -> 18 [ label = "" ] + 58 -> 20 [ label = "" ] + 58 -> 23 [ label = "" ] + 58 -> 42 [ label = "" ] + 58 -> 60 [ label = "" ] + 58 -> 61 [ label = "" ] + 59 -> 0 [ label = "" ] + 59 -> 81 [ label = "" ] + 59 -> 83 [ label = "" ] 59 -> 21 [ label = "" ] - 67 -> 22 [ label = "" ] - 67 -> 65 [ label = "" ] - 66 -> 22 [ label = "" ] - 65 -> 22 [ label = "" ] - 104 -> 23 [ label = "" ] - 104 -> 105 [ label = "" ] - 104 -> 64 [ label = "" ] - 104 -> 61 [ label = "" ] - 106 -> 23 [ label = "" ] - 74 -> 23 [ label = "" ] - 74 -> 104 [ label = "" ] - 74 -> 107 [ label = "" ] - 74 -> 106 [ label = "" ] - 107 -> 23 [ label = "" ] - 107 -> 60 [ label = "" ] - 107 -> 63 [ label = "" ] - 105 -> 23 [ label = "" ] - 105 -> 62 [ label = "" ] - 105 -> 59 [ label = "" ] - 108 -> 24 [ label = "" ] - 108 -> 109 [ label = "" ] - 108 -> 69 [ label = "" ] - 108 -> 67 [ label = "" ] - 75 -> 24 [ label = "" ] - 75 -> 108 [ label = "" ] - 75 -> 110 [ label = "" ] - 110 -> 24 [ label = "" ] - 110 -> 66 [ label = "" ] - 109 -> 24 [ label = "" ] - 109 -> 68 [ label = "" ] - 109 -> 65 [ label = "" ] - 111 -> 25 [ label = "" ] - 111 -> 112 [ label = "" ] - 111 -> 71 [ label = "" ] - 111 -> 67 [ label = "" ] - 76 -> 25 [ label = "" ] - 76 -> 111 [ label = "" ] - 76 -> 113 [ label = "" ] - 113 -> 25 [ label = "" ] - 113 -> 66 [ label = "" ] - 112 -> 25 [ label = "" ] - 112 -> 70 [ label = "" ] - 112 -> 65 [ label = "" ] - 64 -> 26 [ label = "" ] - 64 -> 62 [ label = "" ] - 64 -> 61 [ label = "" ] - 63 -> 26 [ label = "" ] - 63 -> 60 [ label = "" ] - 62 -> 26 [ label = "" ] - 62 -> 59 [ label = "" ] - 69 -> 27 [ label = "" ] - 69 -> 68 [ label = "" ] - 68 -> 27 [ label = "" ] - 71 -> 28 [ label = "" ] - 71 -> 70 [ label = "" ] - 70 -> 28 [ label = "" ] - 114 -> 29 [ label = "" ] - 114 -> 115 [ label = "" ] - 116 -> 29 [ label = "" ] - 87 -> 29 [ label = "" ] - 87 -> 114 [ label = "" ] - 115 -> 29 [ label = "" ] - 115 -> 116 [ label = "" ] - 117 -> 30 [ label = "" ] - 117 -> 118 [ label = "" ] - 119 -> 30 [ label = "" ] - 119 -> 117 [ label = "" ] - 81 -> 34 [ label = "" ] - 88 -> 34 [ label = "" ] - 88 -> 81 [ label = "" ] - 82 -> 35 [ label = "" ] - 82 -> 81 [ label = "" ] - 89 -> 35 [ label = "" ] - 89 -> 82 [ label = "" ] + 59 -> 22 [ label = "" ] + 59 -> 42 [ label = "" ] + 59 -> 60 [ label = "" ] + 59 -> 61 [ label = "" ] + 65 -> 7 [ label = "" ] + 65 -> 67 [ label = "" ] + 65 -> 123 [ label = "" ] + 66 -> 124 [ label = "" ] + 66 -> 30 [ label = "" ] + 66 -> 125 [ label = "" ] + 66 -> 116 [ label = "" ] + 66 -> 117 [ label = "" ] + 66 -> 126 [ label = "" ] + 66 -> 118 [ label = "" ] + 66 -> 69 [ label = "" ] + 67 -> 117 [ label = "" ] + 67 -> 68 [ label = "" ] + 67 -> 127 [ label = "" ] + 68 -> 116 [ label = "" ] + 68 -> 117 [ label = "" ] + 68 -> 128 [ label = "" ] + 68 -> 126 [ label = "" ] + 68 -> 118 [ label = "" ] + 68 -> 66 [ label = "" ] + 68 -> 129 [ label = "" ] + 68 -> 69 [ label = "" ] + 124 -> 1 [ label = "" ] + 130 -> 2 [ label = "" ] + 81 -> 2 [ label = "" ] + 81 -> 130 [ label = "" ] + 72 -> 3 [ label = "" ] + 72 -> 131 [ label = "" ] + 131 -> 3 [ label = "" ] + 131 -> 49 [ label = "" ] + 86 -> 5 [ label = "" ] + 87 -> 6 [ label = "" ] + 121 -> 8 [ label = "" ] + 121 -> 44 [ label = "" ] + 132 -> 11 [ label = "" ] + 82 -> 11 [ label = "" ] + 82 -> 132 [ label = "" ] + 133 -> 12 [ label = "" ] + 133 -> 77 [ label = "" ] + 122 -> 12 [ label = "" ] + 122 -> 133 [ label = "" ] + 77 -> 12 [ label = "" ] + 78 -> 13 [ label = "" ] + 84 -> 14 [ label = "" ] + 84 -> 77 [ label = "" ] + 84 -> 78 [ label = "" ] + 103 -> 15 [ label = "" ] + 103 -> 134 [ label = "" ] + 103 -> 135 [ label = "" ] + 135 -> 15 [ label = "" ] + 135 -> 26 [ label = "" ] + 134 -> 15 [ label = "" ] + 134 -> 136 [ label = "" ] + 137 -> 16 [ label = "" ] + 137 -> 65 [ label = "" ] + 138 -> 16 [ label = "" ] + 138 -> 26 [ label = "" ] + 104 -> 16 [ label = "" ] + 104 -> 137 [ label = "" ] + 104 -> 138 [ label = "" ] + 85 -> 17 [ label = "" ] + 139 -> 19 [ label = "" ] + 83 -> 19 [ label = "" ] + 83 -> 139 [ label = "" ] + 140 -> 24 [ label = "" ] + 102 -> 24 [ label = "" ] + 102 -> 140 [ label = "" ] + 105 -> 27 [ label = "" ] + 106 -> 28 [ label = "" ] + 91 -> 31 [ label = "" ] + 91 -> 89 [ label = "" ] + 90 -> 31 [ label = "" ] + 89 -> 31 [ label = "" ] + 97 -> 32 [ label = "" ] + 97 -> 95 [ label = "" ] + 96 -> 32 [ label = "" ] + 95 -> 32 [ label = "" ] + 141 -> 33 [ label = "" ] + 141 -> 142 [ label = "" ] + 141 -> 94 [ label = "" ] + 141 -> 91 [ label = "" ] + 143 -> 33 [ label = "" ] + 107 -> 33 [ label = "" ] + 107 -> 141 [ label = "" ] + 107 -> 144 [ label = "" ] + 107 -> 143 [ label = "" ] + 144 -> 33 [ label = "" ] + 144 -> 90 [ label = "" ] + 144 -> 93 [ label = "" ] + 142 -> 33 [ label = "" ] + 142 -> 92 [ label = "" ] + 142 -> 89 [ label = "" ] + 145 -> 34 [ label = "" ] + 145 -> 146 [ label = "" ] + 145 -> 99 [ label = "" ] + 145 -> 97 [ label = "" ] + 108 -> 34 [ label = "" ] + 108 -> 145 [ label = "" ] + 108 -> 147 [ label = "" ] + 147 -> 34 [ label = "" ] + 147 -> 96 [ label = "" ] + 146 -> 34 [ label = "" ] + 146 -> 98 [ label = "" ] + 146 -> 95 [ label = "" ] + 148 -> 35 [ label = "" ] + 148 -> 149 [ label = "" ] + 148 -> 101 [ label = "" ] + 148 -> 97 [ label = "" ] + 109 -> 35 [ label = "" ] + 109 -> 148 [ label = "" ] + 109 -> 150 [ label = "" ] + 150 -> 35 [ label = "" ] + 150 -> 96 [ label = "" ] + 149 -> 35 [ label = "" ] + 149 -> 100 [ label = "" ] + 149 -> 95 [ label = "" ] + 94 -> 36 [ label = "" ] + 94 -> 92 [ label = "" ] + 94 -> 91 [ label = "" ] 93 -> 36 [ label = "" ] - 120 -> 36 [ label = "" ] - 120 -> 35 [ label = "" ] - 121 -> 36 [ label = "" ] - 121 -> 81 [ label = "" ] - 121 -> 82 [ label = "" ] - 122 -> 36 [ label = "" ] - 122 -> 120 [ label = "" ] - 123 -> 36 [ label = "" ] - 90 -> 36 [ label = "" ] - 124 -> 36 [ label = "" ] - 91 -> 36 [ label = "" ] - 91 -> 124 [ label = "" ] - 91 -> 123 [ label = "" ] - 91 -> 122 [ label = "" ] - 91 -> 125 [ label = "" ] - 91 -> 121 [ label = "" ] - 125 -> 36 [ label = "" ] - 77 -> 37 [ label = "" ] - 78 -> 39 [ label = "" ] - 79 -> 40 [ label = "" ] - 80 -> 40 [ label = "" ] - 126 -> 45 [ label = "" ] - 53 -> 45 [ label = "" ] - 53 -> 126 [ label = "" ] - 127 -> 46 [ label = "" ] - 128 -> 46 [ label = "" ] - 128 -> 85 [ label = "" ] - 54 -> 46 [ label = "" ] - 54 -> 128 [ label = "" ] - 54 -> 127 [ label = "" ] - 94 -> 47 [ label = "" ] - 85 -> 48 [ label = "" ] - 85 -> 92 [ label = "" ] - 92 -> 49 [ label = "" ] - 92 -> 94 [ label = "" ] + 93 -> 90 [ label = "" ] + 92 -> 36 [ label = "" ] + 92 -> 89 [ label = "" ] + 99 -> 37 [ label = "" ] + 99 -> 98 [ label = "" ] + 98 -> 37 [ label = "" ] + 101 -> 38 [ label = "" ] + 101 -> 100 [ label = "" ] + 100 -> 38 [ label = "" ] + 151 -> 39 [ label = "" ] + 151 -> 152 [ label = "" ] + 153 -> 39 [ label = "" ] + 125 -> 39 [ label = "" ] + 125 -> 151 [ label = "" ] + 152 -> 39 [ label = "" ] + 152 -> 153 [ label = "" ] + 154 -> 40 [ label = "" ] + 155 -> 40 [ label = "" ] + 156 -> 40 [ label = "" ] + 88 -> 40 [ label = "" ] + 88 -> 156 [ label = "" ] + 88 -> 154 [ label = "" ] + 88 -> 155 [ label = "" ] + 157 -> 41 [ label = "" ] + 157 -> 158 [ label = "" ] + 159 -> 41 [ label = "" ] + 159 -> 157 [ label = "" ] + 114 -> 45 [ label = "" ] + 116 -> 45 [ label = "" ] + 116 -> 114 [ label = "" ] + 120 -> 46 [ label = "" ] + 120 -> 114 [ label = "" ] + 117 -> 46 [ label = "" ] + 117 -> 120 [ label = "" ] + 160 -> 48 [ label = "" ] + 73 -> 48 [ label = "" ] + 73 -> 49 [ label = "" ] + 74 -> 48 [ label = "" ] + 74 -> 160 [ label = "" ] + 119 -> 49 [ label = "" ] + 161 -> 49 [ label = "" ] + 161 -> 50 [ label = "" ] + 70 -> 49 [ label = "" ] + 70 -> 161 [ label = "" ] + 71 -> 49 [ label = "" ] + 71 -> 119 [ label = "" ] + 115 -> 50 [ label = "" ] + 75 -> 51 [ label = "" ] + 162 -> 51 [ label = "" ] + 162 -> 119 [ label = "" ] + 76 -> 51 [ label = "" ] + 76 -> 162 [ label = "" ] + 128 -> 53 [ label = "" ] + 163 -> 53 [ label = "" ] + 163 -> 46 [ label = "" ] + 164 -> 53 [ label = "" ] + 164 -> 114 [ label = "" ] + 164 -> 120 [ label = "" ] + 165 -> 53 [ label = "" ] + 165 -> 163 [ label = "" ] + 166 -> 53 [ label = "" ] + 126 -> 53 [ label = "" ] + 167 -> 53 [ label = "" ] + 118 -> 53 [ label = "" ] + 118 -> 167 [ label = "" ] + 118 -> 166 [ label = "" ] + 118 -> 165 [ label = "" ] + 118 -> 168 [ label = "" ] + 118 -> 164 [ label = "" ] + 168 -> 53 [ label = "" ] + 110 -> 56 [ label = "" ] + 111 -> 58 [ label = "" ] + 112 -> 59 [ label = "" ] + 113 -> 59 [ label = "" ] + 169 -> 64 [ label = "" ] + 79 -> 64 [ label = "" ] + 79 -> 169 [ label = "" ] + 170 -> 65 [ label = "" ] + 171 -> 65 [ label = "" ] + 171 -> 123 [ label = "" ] + 80 -> 65 [ label = "" ] + 80 -> 171 [ label = "" ] + 80 -> 170 [ label = "" ] + 129 -> 66 [ label = "" ] + 123 -> 67 [ label = "" ] + 123 -> 127 [ label = "" ] + 127 -> 68 [ label = "" ] + 127 -> 129 [ label = "" ] } -