├── .gitignore ├── do-less ├── do-less.pdf ├── d19-test.txt ├── d19-prod.txt ├── d19-prod-2.txt └── d19.rs ├── lifetimes ├── slides.pdf ├── meta.md ├── code │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs └── slides.rs ├── semicolons └── semi-colons.pdf ├── compiler-bug └── compiler-bug.pdf └── stack-vs-reg ├── mcanvas_stack.ts ├── stack_vs_reg.rs └── mcanvas_cpu.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target/ 3 | 4 | -------------------------------------------------------------------------------- /do-less/do-less.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leddoo/vids/HEAD/do-less/do-less.pdf -------------------------------------------------------------------------------- /lifetimes/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leddoo/vids/HEAD/lifetimes/slides.pdf -------------------------------------------------------------------------------- /semicolons/semi-colons.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leddoo/vids/HEAD/semicolons/semi-colons.pdf -------------------------------------------------------------------------------- /compiler-bug/compiler-bug.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leddoo/vids/HEAD/compiler-bug/compiler-bug.pdf -------------------------------------------------------------------------------- /lifetimes/meta.md: -------------------------------------------------------------------------------- 1 | - capture: 2 | - 6x zoom. 3 | - full screenshot. 4 | - `convert in.png -crop 2024x1000+176+234 out.png` 5 | - paste onto empty slide. 6 | 7 | -------------------------------------------------------------------------------- /lifetimes/code/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "lifetimes" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /lifetimes/code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lifetimes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /do-less/d19-test.txt: -------------------------------------------------------------------------------- 1 | Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian. 2 | Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian. 3 | -------------------------------------------------------------------------------- /lifetimes/code/src/main.rs: -------------------------------------------------------------------------------- 1 | // introduce lifetime vs region. 2 | // and liveness. 3 | fn example_1() { 4 | let r; 5 | 6 | { 7 | let x = 42; 8 | r = &x; 9 | } 10 | 11 | //println!("{}", *r); 12 | } 13 | 14 | 15 | // assignment resets borrows. 16 | // expand upon liveness. 17 | fn example_2() { 18 | let foo = 69; 19 | let mut r; 20 | 21 | { 22 | let x = 42; 23 | r = &x; 24 | 25 | println!("{}", *r); 26 | } 27 | 28 | r = &foo; 29 | 30 | println!("{}", *r); 31 | } 32 | 33 | 34 | // control flow & regions as sets. 35 | fn example_3() { 36 | let a = "hello".to_string(); 37 | let b = "hi".to_string(); 38 | 39 | let r = 40 | if a.len() > b.len() { &a } 41 | else { &b }; 42 | 43 | //drop(a); 44 | 45 | println!("{}", r); 46 | } 47 | 48 | 49 | fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { 50 | if a.len() > b.len() { a } 51 | else { b } 52 | } 53 | 54 | // abstraction. 55 | fn example_4() { 56 | let a = "hello".to_string(); 57 | let b = "hi".to_string(); 58 | 59 | let r = longest(&a, &b); 60 | 61 | //drop(a); 62 | 63 | println!("{}", r); 64 | } 65 | 66 | 67 | // independent sets. 68 | fn swap<'a, 'b>(a: &'a str, b: &'b str) -> (&'b str, &'a str) { 69 | (b, a) 70 | } 71 | 72 | fn example_5() { 73 | let a = "hello".to_string(); 74 | let b = "hi".to_string(); 75 | 76 | let (rb, _ra) = swap(&a, &b); 77 | 78 | drop(a); 79 | 80 | println!("{}", rb); 81 | } 82 | 83 | 84 | // subsets: swap with `(a, b)`. 85 | // and interpreting the error messages. 86 | 87 | 88 | fn main() { 89 | example_1(); 90 | example_2(); 91 | example_3(); 92 | example_4(); 93 | example_5(); 94 | } 95 | 96 | 97 | fn longest2<'s1, 's2, 'out>(s1: &'s1 str, s2: &'s2 str) -> &'out str 98 | where 's1: 'out, 's2: 'out 99 | { 100 | if s1.len() > s2.len() { 101 | return s1; 102 | } 103 | else { 104 | return s2; 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /do-less/d19-prod.txt: -------------------------------------------------------------------------------- 1 | Blueprint 1: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 10 clay. Each geode robot costs 2 ore and 7 obsidian. 2 | Blueprint 2: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 12 clay. Each geode robot costs 3 ore and 8 obsidian. 3 | Blueprint 3: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 17 clay. Each geode robot costs 2 ore and 13 obsidian. 4 | Blueprint 4: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 2 ore and 14 obsidian. 5 | Blueprint 5: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 5 clay. Each geode robot costs 3 ore and 12 obsidian. 6 | Blueprint 6: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 11 clay. Each geode robot costs 3 ore and 8 obsidian. 7 | Blueprint 7: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 17 clay. Each geode robot costs 3 ore and 7 obsidian. 8 | Blueprint 8: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 2 ore and 16 obsidian. 9 | Blueprint 9: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 9 clay. Each geode robot costs 3 ore and 7 obsidian. 10 | Blueprint 10: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 9 clay. Each geode robot costs 4 ore and 16 obsidian. 11 | Blueprint 11: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 4 ore and 16 obsidian. 12 | Blueprint 12: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 17 clay. Each geode robot costs 3 ore and 19 obsidian. 13 | Blueprint 13: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 10 clay. Each geode robot costs 3 ore and 14 obsidian. 14 | Blueprint 14: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 13 clay. Each geode robot costs 2 ore and 10 obsidian. 15 | Blueprint 15: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 2 ore and 12 obsidian. 16 | Blueprint 16: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 3 ore and 9 obsidian. 17 | Blueprint 17: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 11 clay. Each geode robot costs 2 ore and 10 obsidian. 18 | Blueprint 18: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 2 ore and 11 obsidian. 19 | Blueprint 19: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 3 ore and 8 obsidian. 20 | Blueprint 20: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 4 ore and 14 obsidian. 21 | Blueprint 21: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 17 obsidian. 22 | Blueprint 22: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 15 clay. Each geode robot costs 4 ore and 16 obsidian. 23 | Blueprint 23: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 17 clay. Each geode robot costs 3 ore and 11 obsidian. 24 | Blueprint 24: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 13 clay. Each geode robot costs 3 ore and 12 obsidian. 25 | Blueprint 25: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 3 ore and 17 obsidian. 26 | Blueprint 26: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 12 clay. Each geode robot costs 3 ore and 17 obsidian. 27 | Blueprint 27: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 3 ore and 7 obsidian. 28 | Blueprint 28: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 14 clay. Each geode robot costs 4 ore and 10 obsidian. 29 | Blueprint 29: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 3 ore and 9 obsidian. 30 | Blueprint 30: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 7 clay. Each geode robot costs 4 ore and 13 obsidian. 31 | -------------------------------------------------------------------------------- /do-less/d19-prod-2.txt: -------------------------------------------------------------------------------- 1 | Blueprint 1: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 17 obsidian. 2 | Blueprint 2: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 11 clay. Each geode robot costs 4 ore and 12 obsidian. 3 | Blueprint 3: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 4 ore and 15 obsidian. 4 | Blueprint 4: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 10 obsidian. 5 | Blueprint 5: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 18 clay. Each geode robot costs 2 ore and 19 obsidian. 6 | Blueprint 6: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 2 ore and 16 obsidian. 7 | Blueprint 7: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 3 ore and 7 obsidian. 8 | Blueprint 8: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 17 clay. Each geode robot costs 2 ore and 13 obsidian. 9 | Blueprint 9: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 12 clay. Each geode robot costs 3 ore and 17 obsidian. 10 | Blueprint 10: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 3 ore and 9 obsidian. 11 | Blueprint 11: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 2 ore and 18 obsidian. 12 | Blueprint 12: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 15 clay. Each geode robot costs 2 ore and 8 obsidian. 13 | Blueprint 13: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 11 clay. Each geode robot costs 3 ore and 15 obsidian. 14 | Blueprint 14: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 9 clay. Each geode robot costs 3 ore and 7 obsidian. 15 | Blueprint 15: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 2 ore and 8 obsidian. 16 | Blueprint 16: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 16 clay. Each geode robot costs 3 ore and 15 obsidian. 17 | Blueprint 17: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 2 ore and 8 obsidian. 18 | Blueprint 18: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 13 clay. Each geode robot costs 3 ore and 11 obsidian. 19 | Blueprint 19: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 12 clay. Each geode robot costs 2 ore and 10 obsidian. 20 | Blueprint 20: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 3 ore and 19 obsidian. 21 | Blueprint 21: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 3 ore and 10 obsidian. 22 | Blueprint 22: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 16 clay. Each geode robot costs 2 ore and 11 obsidian. 23 | Blueprint 23: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 10 clay. Each geode robot costs 3 ore and 10 obsidian. 24 | Blueprint 24: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 15 clay. Each geode robot costs 4 ore and 16 obsidian. 25 | Blueprint 25: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 5 clay. Each geode robot costs 2 ore and 10 obsidian. 26 | Blueprint 26: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 18 clay. Each geode robot costs 4 ore and 16 obsidian. 27 | Blueprint 27: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 3 ore and 14 obsidian. 28 | Blueprint 28: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 7 clay. Each geode robot costs 4 ore and 13 obsidian. 29 | Blueprint 29: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 3 ore and 14 obsidian. 30 | Blueprint 30: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 2 ore and 8 obsidian. 31 | -------------------------------------------------------------------------------- /lifetimes/slides.rs: -------------------------------------------------------------------------------- 1 | 2 | // example from rust book. 3 | 4 | fn example_1() { 5 | let r; 6 | 7 | { 8 | let x = 42; 9 | r = &x; 10 | } 11 | 12 | println!("{}", *r); 13 | } 14 | 15 | 16 | // explains lifetimes like so: 17 | 18 | fn example_1() { // 19 | let r; // ----------+-- 'r 20 | // | 21 | { // | 22 | let x = 42; // --+-- 'x | 23 | r = &x; // | | 24 | } // --+ | 25 | // | 26 | println!("{}", *r); // | 27 | } // ----------+ 28 | 29 | 30 | // but doesn't make sense, 31 | // cause nothing changes, 32 | // when remove the print, 33 | // even though this is valid code. 34 | 35 | fn example_1() { // 36 | let r; // ----------+-- 'r 37 | // | 38 | { // | 39 | let x = 42; // --+-- 'x | 40 | r = &x; // | | 41 | } // --+ | 42 | // | 43 | } // ----------+ 44 | 45 | 46 | // so this is in fact not what lifetimes are. 47 | // lifetimes are regions of code, 48 | // that a reference must be valid for. 49 | // ie: assignment -> last use. 50 | // aka: liveness. 51 | 52 | fn example_1() { // 53 | let r; // ----------+-- 'r 54 | // | 55 | { // | 56 | let x = 42; // | 57 | r = &x; // | 58 | } // | 59 | // | 60 | println!("{}", *r); // | 61 | } // ----------+ 62 | 63 | fn example_1() { // 64 | let r; // 65 | // 66 | { // 67 | let x = 42; // 68 | r = &x; // ----------+-- 'r 69 | } // | 70 | // | 71 | println!("{}", *r); // | 72 | } // ----------+ 73 | 74 | fn example_1() { // 75 | let r; // 76 | // 77 | { // 78 | let x = 42; // 79 | r = &x; // ----------+-- 'r 80 | } // | 81 | // | 82 | println!("{}", *r); // ----------+ 83 | } // 84 | 85 | 86 | // no use -> empty lifetime. 87 | 88 | fn example_1() { // 89 | let r; // 90 | // 91 | { // 92 | let x = 42; // 93 | r = &x; // ------------- 'r 94 | } // 95 | // 96 | } // 97 | 98 | 99 | 100 | // to illustrate this idea of liveness, 101 | // here is another example. 102 | 103 | fn example_2() { 104 | let foo = 69; 105 | let mut r; 106 | 107 | { 108 | let x = 42; 109 | r = &x; 110 | 111 | println!("{}", *r); 112 | } 113 | 114 | r = &foo; 115 | 116 | println!("{}", *r); 117 | } 118 | 119 | 120 | // and the lifetime. 121 | 122 | fn example_2() { // 123 | let foo = 69; // 124 | let mut r; // 125 | // 126 | { // 127 | let x = 42; // 128 | r = &x; // ----------+-- 'r 129 | // | 130 | println!("{}", *r); // ----------+ 131 | } // 132 | // 133 | r = &foo; // ----------+-- 'r 134 | // | 135 | println!("{}", *r); // ----------+ 136 | } // 137 | 138 | 139 | // but actually, knowing 'r isn't enough. 140 | // we also need to know what r is referencing. 141 | 142 | fn example_2() { // 143 | let foo = 69; // 144 | let mut r; // 145 | // 146 | { // 147 | let x = 42; // 148 | r = &x; // ----------+-- 'r = { &x } 149 | // | 150 | println!("{}", *r); // ----------+ 151 | } // 152 | // 153 | r = &foo; // ----------+-- 'r = { &foo } 154 | // | 155 | println!("{}", *r); // ----------+ 156 | } // 157 | 158 | 159 | // now if we remove the reassignment: 160 | 161 | fn example_2() { // 162 | let foo = 69; // 163 | let mut r; // 164 | // 165 | { // 166 | let x = 42; // 167 | r = &x; // ----------+-- 'r = { &x } 168 | // | 169 | println!("{}", *r); // | 170 | } // | 171 | // | 172 | println!("{}", *r); // ----------+ 173 | } // 174 | 175 | 176 | // or if we made it conditional: 177 | 178 | fn example_2() { // 179 | let foo = 69; // 180 | let mut r; // 181 | // 182 | { // 183 | let x = 42; // 184 | r = &x; // ----------+-- 'r = { &x } 185 | // | 186 | println!("{}", *r); // | 187 | } // | 188 | // | 189 | if random_bool() { // | 190 | r = &foo; // +-- 'r = { &x, &foo } 191 | } // | 192 | // | 193 | println!("{}", *r); // ----------+ 194 | } // 195 | 196 | 197 | 198 | // - separate liveness from regions. 199 | // - introduce the set idea. 200 | 201 | 202 | 203 | fn longest<'a>( 204 | s1: &'a str, 205 | s2: &'a str) 206 | -> &'a str 207 | { 208 | if s1.len() > s2.len() { 209 | return s1; 210 | } 211 | else { 212 | "hi" 213 | return s2; 214 | } 215 | } 216 | 217 | 218 | 219 | fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {} 220 | 221 | 222 | fn main() { 223 | let x: &'x str = "hi"; 224 | let y: String = "hello".into(); 225 | let z: &'z str = "hey"; 226 | 227 | let l1: &'l1 str = longest(x, &y); // 'y 228 | let l2: &'l2 str = longest(l1, z); 229 | } 230 | 231 | 232 | 233 | fn foo<'a, 'b>(a: &'a i32, b: &'b i32) 234 | where 'a: 'b 235 | {} 236 | 237 | 238 | 239 | fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { 240 | if s1.len() > s2.len() { 241 | return s1; 242 | } 243 | else { 244 | return s2; 245 | } 246 | } 247 | 248 | fn longest<'s1, 's2, 'out>(s1: &'s1 str, s2: &'s2 str) -> &'out str { 249 | if s1.len() > s2.len() { 250 | return s1; 251 | } 252 | else { 253 | return s2; 254 | } 255 | } 256 | 257 | fn longest<'s1, 's2, 'out>(s1: &'s1 str, s2: &'s2 str) -> &'out str 258 | where 'x: 'y, ... 259 | { 260 | if s1.len() > s2.len() { 261 | return s1; 262 | } 263 | else { 264 | return s2; 265 | } 266 | } 267 | 268 | fn longest<'s1, 's2, 'out>(s1: &'s1 str, s2: &'s2 str) -> &'out str 269 | where 's1: 'out, 's2: 'out 270 | { 271 | if s1.len() > s2.len() { 272 | return s1; 273 | } 274 | else { 275 | return s2; 276 | } 277 | } 278 | 279 | 280 | fn longest<'s1, 's2, 'out>(s1: &'s1 str, s2: &'s2 str) -> &'out str 281 | where 's1: 'out, 's2: 'out 282 | {} 283 | fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str 284 | 285 | {} 286 | 287 | -------------------------------------------------------------------------------- /stack-vs-reg/mcanvas_stack.ts: -------------------------------------------------------------------------------- 1 | import {Layout, LayoutProps} from "@motion-canvas/2d/lib/components" 2 | import {Rect, Vector2} from "@motion-canvas/core/lib/types"; 3 | import {property} from "@motion-canvas/2d/lib/decorators" 4 | import {all, sequence} from "@motion-canvas/core/lib/flow"; 5 | import {Signal, SignalValue} from "@motion-canvas/core/lib/utils"; 6 | import {drawRect} from "@motion-canvas/2d/lib/utils"; 7 | import {easeOutCubic, linear} from "@motion-canvas/core/lib/tweening"; 8 | import {decorate, threadable} from "@motion-canvas/core/lib/decorators" 9 | 10 | 11 | const ITEM_WIDTH = 300; 12 | const ITEM_HEIGHT = 110; 13 | const ITEM_MARGIN = 18; 14 | const ITEM_GAP = 6; 15 | 16 | 17 | export class Stack extends Layout { 18 | items: StackItem[]; 19 | base: number; 20 | 21 | public constructor(props?: LayoutProps) { 22 | super(props); 23 | this.items = []; 24 | this.setWidth(400); 25 | this.setHeight(500); 26 | this.base = 250 - ITEM_HEIGHT/2 - 30; 27 | } 28 | 29 | protected override draw(ctx: CanvasRenderingContext2D) { 30 | ctx.fillStyle = "#FFB454"; 31 | 32 | let w = this.getWidth(); 33 | let h = this.getHeight(); 34 | ctx.beginPath(); 35 | ctx.rect(-w/2, h/2 - 10, w, 10); 36 | ctx.fill(); 37 | 38 | super.draw(ctx); 39 | } 40 | 41 | item_pos(index: number): number { 42 | return this.base - index*(ITEM_HEIGHT + ITEM_GAP); 43 | } 44 | 45 | idx_rev(index: number): StackItem { 46 | return this.items[this.items.length - 1 - index]; 47 | } 48 | 49 | public new_item(value: string): StackItem { 50 | let item = new StackItem({value}); 51 | this.items.push(item); 52 | this.add(item); 53 | 54 | item.position.y(this.item_pos(this.items.length - 1)); 55 | return item; 56 | } 57 | 58 | public remove_item(idx: number): StackItem { 59 | let result = this.items[idx]; 60 | this.items.splice(idx, 1); 61 | return result; 62 | } 63 | 64 | public* remove_item_fade(idx: number) { 65 | let item = this.remove_item(idx); 66 | yield* item.opacity(0, 0.2); 67 | item.remove(); 68 | } 69 | 70 | public* replace_items(new_values: string[]) { 71 | let items = this.items; 72 | this.items = []; 73 | yield* all( 74 | ...items.map(item => item.opacity(0, 0.2)), 75 | sequence(0.1, ...new_values.map(value => this.drop_new(value))), 76 | ) 77 | } 78 | 79 | public* highlight_item(idx: number) { 80 | let item = this.items[idx]; 81 | yield* item.scale(1.15, 0.15); 82 | yield* item.scale(1.00, 0.15); 83 | } 84 | 85 | public clear() { 86 | for(const item of this.items) { 87 | item.remove(); 88 | } 89 | this.items.length = 0; 90 | } 91 | 92 | public* drop_new(value: string) { 93 | let item = this.new_item(value); 94 | let y = item.position.y(); 95 | item.position.y(y - 800); 96 | yield* item.position.y(y, 0.3, linear); 97 | } 98 | 99 | public* load(index: number) { 100 | let original = this.items[index]; 101 | let item = this.new_item(original.value()); 102 | 103 | let y0 = original.position.y(); 104 | let y1 = item.position.y(); 105 | item.position.y(y0); 106 | yield* item.position.x(ITEM_WIDTH + 30, 0.2); 107 | yield* item.position.y(y1, 0.2); 108 | yield* item.position.x(0, 0.2); 109 | } 110 | 111 | public* store(index: number) { 112 | let target = this.items[index]; 113 | let item = this.items.pop(); 114 | 115 | let y1 = target.position.y(); 116 | yield* item.position.x(ITEM_WIDTH + 30, 0.2); 117 | yield* item.position.y(y1, 0.2); 118 | yield* item.position.x(0, 0.2); 119 | 120 | this.items[index] = item; 121 | target.remove(); 122 | } 123 | 124 | public* dup() { 125 | let src = this.idx_rev(0); 126 | let item = this.new_item(src.value()); 127 | 128 | let y = item.position.y(); 129 | item.position.y(src.position.y()); 130 | yield* item.position.y(y, 0.2); 131 | } 132 | 133 | public* rot() { 134 | let a = this.idx_rev(2); 135 | let b = this.idx_rev(1); 136 | let c = this.idx_rev(0); 137 | 138 | let bot = a.position.y(); 139 | let mid = b.position.y(); 140 | let top = c.position.y(); 141 | 142 | yield* a.position.x(ITEM_WIDTH + 30, 0.2); 143 | yield* all( 144 | a.position.y(top, 0.2), 145 | b.position.y(bot, 0.2), 146 | c.position.y(mid, 0.2), 147 | ); 148 | yield* a.position.x(0, 0.2); 149 | 150 | this.items.length = this.items.length - 3; 151 | this.items.push(b); 152 | this.items.push(c); 153 | this.items.push(a); 154 | } 155 | 156 | public* bin_op_stack(result: string) { 157 | let b = this.idx_rev(0); 158 | let a = this.idx_rev(1); 159 | 160 | let y = b.position.y(); 161 | 162 | yield* b.position(new Vector2( 200, y - 30), 0.2, easeOutCubic); 163 | yield* a.position(new Vector2(-200, y - 30), 0.2, easeOutCubic); 164 | yield* all( 165 | a.position.x(0, 0.2), 166 | b.position.x(0, 0.2), 167 | ); 168 | 169 | let bi = this.items.pop(); 170 | bi.remove(); 171 | let ai = this.items.pop(); 172 | ai.remove(); 173 | 174 | let r = this.new_item(result); 175 | let ry = r.position.y(); 176 | r.position.y(y - 30); 177 | yield* r.position.y(ry, 0.2); 178 | } 179 | 180 | make_clone(item: StackItem): StackItem { 181 | let r = new StackItem({value: item.value()}); 182 | this.add(r); 183 | r.position(item.position()); 184 | return r; 185 | } 186 | 187 | public* copy(dst: number, src: number) { 188 | let d = this.items[dst]; 189 | let s = this.items[src]; 190 | 191 | let sc = this.make_clone(s); 192 | 193 | yield* sc.position.x(ITEM_WIDTH + 30, 0.2); 194 | yield* sc.position.y(d.position.y(), 0.2); 195 | yield* sc.position.x(0, 0.2); 196 | 197 | sc.remove(); 198 | d.value(s.value()); 199 | } 200 | 201 | public* bin_op_reg(dst: number, src1: number, src2: number, result: string, side: number = 1) { 202 | if(dst == this.items.length) { 203 | let top = this.new_item(""); 204 | top.opacity(0); 205 | } 206 | 207 | let d = this.items[dst]; 208 | let s1 = this.items[src1]; 209 | let s2 = this.items[src2]; 210 | 211 | let s1c = this.make_clone(s1); 212 | let s2c = this.make_clone(s2); 213 | 214 | yield* s1c.position.x(side*(ITEM_WIDTH + 30), 0.2); 215 | if(src1 == src2) { yield* s1c.position.y(s1c.position.y() - s1c.size.y()*1.1, 0.2); } 216 | 217 | yield* s2c.position.x(side*(ITEM_WIDTH + 30), 0.2); 218 | 219 | let y = (s1c.position.y() + s2c.position.y()) * 0.5; 220 | yield* all( 221 | s1c.position.y(y, 0.2), 222 | s2c.position.y(y, 0.2), 223 | ); 224 | 225 | s2c.remove(); 226 | s1c.value(result); 227 | 228 | yield* s1c.position.y(d.position.y(), 0.2); 229 | yield* s1c.position.x(0, 0.2); 230 | 231 | s1c.remove(); 232 | d.value(result); 233 | d.opacity(1); 234 | } 235 | } 236 | 237 | decorate(Stack.prototype.remove_item_fade, threadable()); 238 | decorate(Stack.prototype.replace_items, threadable()); 239 | decorate(Stack.prototype.highlight_item, threadable()); 240 | decorate(Stack.prototype.drop_new, threadable()); 241 | decorate(Stack.prototype.load, threadable()); 242 | decorate(Stack.prototype.store, threadable()); 243 | decorate(Stack.prototype.dup, threadable()); 244 | decorate(Stack.prototype.rot, threadable()); 245 | decorate(Stack.prototype.bin_op_stack, threadable()); 246 | decorate(Stack.prototype.copy, threadable()); 247 | decorate(Stack.prototype.bin_op_reg, threadable()); 248 | 249 | 250 | export interface StackItemProps extends LayoutProps { 251 | value: SignalValue; 252 | } 253 | 254 | export class StackItem extends Layout { 255 | @property() 256 | public declare value: Signal; 257 | 258 | @property() 259 | public declare bg: Signal; 260 | 261 | public constructor(props?: StackItemProps) { 262 | super(props); 263 | 264 | this.setWidth(ITEM_WIDTH); 265 | this.setHeight(ITEM_HEIGHT); 266 | this.value(props.value); 267 | this.bg("#0D1017"); 268 | } 269 | 270 | protected override draw(ctx: CanvasRenderingContext2D) { 271 | ctx.fillStyle = this.bg(); 272 | ctx.strokeStyle = "#FFB454"; 273 | ctx.lineWidth = 10; 274 | 275 | const rect = Rect.fromSizeCentered(this.size().sub(new Vector2(ITEM_MARGIN))); 276 | ctx.beginPath(); 277 | drawRect(ctx, rect); 278 | ctx.fill(); 279 | ctx.stroke(); 280 | 281 | ctx.fillStyle = "#D2A6FF"; 282 | ctx.textAlign = "center"; 283 | ctx.font = "52px Source Code Pro"; 284 | ctx.fillText(this.value(), 0, 15); 285 | } 286 | } 287 | 288 | -------------------------------------------------------------------------------- /stack-vs-reg/stack_vs_reg.rs: -------------------------------------------------------------------------------- 1 | 2 | const SPEEEEEED: bool = 1==1; 3 | 4 | 5 | pub mod reg { 6 | 7 | #[derive(Clone, Copy, Debug)] 8 | #[repr(align(4))] 9 | pub enum Instruction { 10 | LoadInt { dst: u8, value: i16 }, 11 | Copy { dst: u8, src: u8 }, 12 | Add { dst: u8, src1: u8, src2: u8 }, 13 | Sub { dst: u8, src1: u8, src2: u8 }, 14 | Mul { dst: u8, src1: u8, src2: u8 }, 15 | Jump { target: u8 }, 16 | SetCounter { src: u8 }, 17 | GetCounter { dst: u8 }, 18 | Loop { target: u8 }, 19 | LoopLe { target: u8, src1: u8, src2: u8 }, 20 | Return { src: u8 }, 21 | } 22 | 23 | pub struct Vm { 24 | registers: Vec, 25 | } 26 | 27 | struct State<'a> { 28 | vm: &'a mut Vm, 29 | code: &'a [Instruction], 30 | pc: usize, 31 | pcp: *const Instruction, 32 | counter: u32, 33 | } 34 | 35 | impl Vm { 36 | pub fn new() -> Self { 37 | Vm { registers: vec![0.0; 256] } 38 | } 39 | 40 | #[inline(never)] 41 | pub fn run(&mut self, code: &[Instruction], args: &[f64]) -> f64 { 42 | let mut s = State { 43 | vm: self, 44 | code, 45 | pc: 0, 46 | pcp: core::ptr::null(), 47 | counter: 0, 48 | }; 49 | 50 | s.jump(0); 51 | for (i, arg) in args.iter().enumerate() { 52 | s.vm.registers[i] = *arg; 53 | } 54 | 55 | loop { 56 | let instr = s.next_instr(); 57 | 58 | use Instruction::*; 59 | match instr { 60 | LoadInt { dst, value } => { 61 | *s.reg(dst) = value as f64; 62 | } 63 | 64 | Copy { dst, src } => { 65 | *s.reg(dst) = *s.reg(src); 66 | } 67 | 68 | Add { dst, src1, src2 } => { 69 | *s.reg(dst) = *s.reg(src1) + *s.reg(src2); 70 | } 71 | 72 | Sub { dst, src1, src2 } => { 73 | *s.reg(dst) = *s.reg(src1) - *s.reg(src2); 74 | } 75 | 76 | Mul { dst, src1, src2 } => { 77 | *s.reg(dst) = *s.reg(src1) * *s.reg(src2); 78 | } 79 | 80 | Jump { target } => { 81 | s.jump(target); 82 | } 83 | 84 | SetCounter { src } => { 85 | s.counter = *s.reg(src) as u32; 86 | } 87 | 88 | GetCounter { dst } => { 89 | *s.reg(dst) = s.counter as f64; 90 | } 91 | 92 | Loop { target } => { 93 | if s.counter > 0 { 94 | s.counter -= 1; 95 | s.jump(target); 96 | } 97 | } 98 | 99 | LoopLe { target, src1, src2 } => { 100 | let a = *s.reg(src1); 101 | let b = *s.reg(src2); 102 | if a <= b && s.counter > 0 { 103 | s.counter -= 1; 104 | s.jump(target); 105 | } 106 | } 107 | 108 | Return { src } => { 109 | let result = *s.reg(src); 110 | return result; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | impl<'a> State<'a> { 118 | #[inline(always)] 119 | fn next_instr(&mut self) -> Instruction { 120 | if super::SPEEEEEED { 121 | unsafe { 122 | let result = *self.pcp; 123 | self.pcp = self.pcp.add(1); 124 | result 125 | } 126 | } 127 | else { 128 | let result = self.code[self.pc]; 129 | self.pc += 1; 130 | result 131 | } 132 | } 133 | 134 | #[inline(always)] 135 | fn jump(&mut self, target: u8) { 136 | if super::SPEEEEEED { 137 | unsafe { 138 | self.pcp = self.code.as_ptr().add(target as usize); 139 | } 140 | } 141 | else { 142 | self.pc = target as usize; 143 | } 144 | } 145 | 146 | #[inline(always)] 147 | fn reg(&mut self, index: u8) -> &mut f64 { 148 | if super::SPEEEEEED { 149 | unsafe { self.vm.registers.get_unchecked_mut(index as usize) } 150 | } 151 | else { 152 | &mut self.vm.registers[index as usize] 153 | } 154 | } 155 | } 156 | 157 | 158 | pub const FIB: &[Instruction] = { use Instruction::*; &[ 159 | SetCounter { src: 0 }, 160 | LoadInt { dst: 1, value: 0 }, 161 | LoadInt { dst: 2, value: 1 }, 162 | Jump { target: 7 }, 163 | Add { dst: 3, src1: 1, src2: 2 }, 164 | Copy { dst: 1, src: 2 }, 165 | Copy { dst: 2, src: 3 }, 166 | Loop { target: 4 }, 167 | Return { src: 1 }, 168 | ]}; 169 | 170 | 171 | pub const MANDEL: &[Instruction] = { use Instruction::*; let (x0, y0, n, x, y, t0, t1) = (0, 1, 2, 3, 4, 5, 6); &[ 172 | LoadInt { dst: x, value: 0 }, 173 | LoadInt { dst: y, value: 0 }, 174 | 175 | // 2 176 | SetCounter { src: n }, 177 | 178 | Jump { target: 13 }, 179 | 180 | // 4 181 | // let xtemp = x*x - y*y + x0; 182 | Mul { dst: t0, src1: x, src2: x }, 183 | Mul { dst: t1, src1: y, src2: y }, 184 | Sub { dst: t0, src1: t0, src2: t1 }, 185 | Add { dst: t0, src1: t0, src2: x0 }, 186 | // y = x*y*2.0 + y0; 187 | Mul { dst: y, src1: x, src2: y }, 188 | LoadInt { dst: t1, value: 2 }, 189 | Mul { dst: y, src1: y, src2: t1 }, 190 | Add { dst: y, src1: y, src2: y0 }, 191 | // x = xtemp 192 | Copy { dst: x, src: t0 }, 193 | 194 | // check x*x + y*y <= 2*2 195 | // 13 196 | Mul { dst: t0, src1: x, src2: x }, 197 | Mul { dst: t1, src1: y, src2: y }, 198 | Add { dst: t0, src1: t0, src2: t1 }, 199 | LoadInt { dst: t1, value: 4 }, 200 | LoopLe { target: 4, src1: t0, src2: t1 }, 201 | 202 | // 18 203 | GetCounter { dst: t1 }, 204 | Sub { dst: t0, src1: n, src2: t1 }, 205 | Return { src: t0 }, 206 | ]}; 207 | 208 | 209 | pub const ADD_CHAIN: &[Instruction] = { use Instruction::*; &[ 210 | Add { dst: 0, src1: 0, src2: 1 }, 211 | Add { dst: 0, src1: 0, src2: 2 }, 212 | Add { dst: 0, src1: 0, src2: 3 }, 213 | Add { dst: 0, src1: 0, src2: 4 }, 214 | Add { dst: 0, src1: 0, src2: 5 }, 215 | Add { dst: 0, src1: 0, src2: 6 }, 216 | Add { dst: 0, src1: 0, src2: 7 }, 217 | Add { dst: 0, src1: 0, src2: 8 }, 218 | Add { dst: 0, src1: 0, src2: 9 }, 219 | Add { dst: 0, src1: 0, src2: 10 }, 220 | Add { dst: 0, src1: 0, src2: 11 }, 221 | Add { dst: 0, src1: 0, src2: 12 }, 222 | Add { dst: 0, src1: 0, src2: 13 }, 223 | Add { dst: 0, src1: 0, src2: 14 }, 224 | Add { dst: 0, src1: 0, src2: 15 }, 225 | Return { src: 0 }, 226 | ]}; 227 | 228 | pub const ADD_PAIRS: &[Instruction] = { use Instruction::*; &[ 229 | Add { dst: 0, src1: 0, src2: 1 }, 230 | Add { dst: 2, src1: 2, src2: 3 }, 231 | Add { dst: 4, src1: 4, src2: 5 }, 232 | Add { dst: 6, src1: 6, src2: 7 }, 233 | Add { dst: 8, src1: 8, src2: 9 }, 234 | Add { dst: 10, src1: 10, src2: 11 }, 235 | Add { dst: 12, src1: 12, src2: 13 }, 236 | Add { dst: 14, src1: 14, src2: 15 }, 237 | Add { dst: 0, src1: 0, src2: 2 }, 238 | Add { dst: 4, src1: 4, src2: 6 }, 239 | Add { dst: 8, src1: 8, src2: 10 }, 240 | Add { dst: 12, src1: 12, src2: 14 }, 241 | Add { dst: 0, src1: 0, src2: 4 }, 242 | Add { dst: 8, src1: 8, src2: 12 }, 243 | Add { dst: 0, src1: 0, src2: 8 }, 244 | Return { src: 0 }, 245 | ]}; 246 | } 247 | 248 | 249 | pub mod stack { 250 | 251 | #[derive(Clone, Copy, Debug)] 252 | #[repr(align(2))] 253 | pub enum Instruction { 254 | Load { src: u8 }, 255 | Store { dst: u8 }, 256 | LoadInt { value: i8 }, 257 | Add, 258 | Sub, 259 | Mul, 260 | Pop, 261 | Dup, 262 | Rot, 263 | Swap, 264 | Jump { target: u8 }, 265 | SetCounter, 266 | GetCounter, 267 | Loop { target: u8 }, 268 | LoopLe { target: u8 }, 269 | Return, 270 | Nop, 271 | } 272 | 273 | pub struct Vm { 274 | stack: Vec, 275 | } 276 | 277 | struct State<'a> { 278 | vm: &'a mut Vm, 279 | code: &'a [Instruction], 280 | pc: usize, 281 | pcp: *const Instruction, 282 | counter: u32, 283 | base: *mut f64, 284 | top: *mut f64, 285 | } 286 | 287 | impl Vm { 288 | pub fn new() -> Self { 289 | Vm { stack: Vec::with_capacity(256) } 290 | } 291 | 292 | #[inline(never)] 293 | pub fn run(&mut self, code: &[Instruction], args: &[f64]) -> f64 { 294 | let base = self.stack.as_mut_ptr(); 295 | let base = ((base as usize + 63) / 64 * 64) as *mut f64; 296 | 297 | let mut s = State { 298 | code, 299 | pc: 0, 300 | pcp: core::ptr::null(), 301 | base, 302 | top: base, 303 | counter: 0, 304 | vm: self, 305 | }; 306 | 307 | s.jump(0); 308 | for arg in args { 309 | s.push(*arg); 310 | } 311 | 312 | loop { 313 | let instr = s.next_instr(); 314 | 315 | use Instruction::*; 316 | match instr { 317 | Load { src } => { 318 | let value = *s.get(src); 319 | s.push(value); 320 | } 321 | 322 | Store { dst } => { 323 | let value = s.pop(); 324 | *s.get(dst) = value; 325 | } 326 | 327 | LoadInt { value } => { 328 | s.push(value as f64); 329 | } 330 | 331 | Add => { 332 | *s.get_top(1) = *s.get_top(1) + *s.get_top(0); 333 | s.pop(); 334 | } 335 | 336 | Sub => { 337 | *s.get_top(1) = *s.get_top(1) - *s.get_top(0); 338 | s.pop(); 339 | } 340 | 341 | Mul => { 342 | *s.get_top(1) = *s.get_top(1) * *s.get_top(0); 343 | s.pop(); 344 | } 345 | 346 | Pop => { 347 | s.pop(); 348 | } 349 | 350 | Dup => { 351 | let value = *s.get_top(0); 352 | s.push(value); 353 | } 354 | 355 | Rot => { 356 | let a = *s.get_top(2); 357 | *s.get_top(2) = *s.get_top(1); 358 | *s.get_top(1) = *s.get_top(0); 359 | *s.get_top(0) = a; 360 | } 361 | 362 | Swap => { 363 | let a = *s.get_top(0); 364 | *s.get_top(0) = *s.get_top(1); 365 | *s.get_top(1) = a; 366 | } 367 | 368 | Jump { target } => { 369 | s.jump(target); 370 | } 371 | 372 | SetCounter => { 373 | let value = s.pop(); 374 | s.counter = value as u32; 375 | } 376 | 377 | GetCounter => { 378 | s.push(s.counter as f64); 379 | } 380 | 381 | Loop { target } => { 382 | if s.counter > 0 { 383 | s.counter -= 1; 384 | s.jump(target); 385 | } 386 | } 387 | 388 | LoopLe { target } => { 389 | let b = s.pop(); 390 | let a = s.pop(); 391 | 392 | if a <= b && s.counter > 0 { 393 | s.counter -= 1; 394 | s.jump(target); 395 | } 396 | } 397 | 398 | Return => { 399 | let result = s.pop(); 400 | s.clear(); 401 | return result; 402 | } 403 | 404 | Nop => {} 405 | } 406 | } 407 | } 408 | } 409 | 410 | impl<'a> State<'a> { 411 | #[inline(always)] 412 | fn next_instr(&mut self) -> Instruction { 413 | if super::SPEEEEEED { 414 | unsafe { 415 | let result = *self.pcp; 416 | self.pcp = self.pcp.add(1); 417 | result 418 | } 419 | } 420 | else { 421 | let result = self.code[self.pc]; 422 | self.pc += 1; 423 | result 424 | } 425 | } 426 | 427 | #[inline(always)] 428 | fn jump(&mut self, target: u8) { 429 | if super::SPEEEEEED { 430 | unsafe { 431 | self.pcp = self.code.as_ptr().add(target as usize); 432 | } 433 | } 434 | else { 435 | self.pc = target as usize; 436 | } 437 | } 438 | 439 | #[inline(always)] 440 | fn get(&mut self, index: u8) -> &mut f64 { 441 | if super::SPEEEEEED { 442 | unsafe { 443 | &mut *self.base.add(index as usize) 444 | } 445 | } 446 | else { 447 | &mut self.vm.stack[index as usize] 448 | } 449 | } 450 | 451 | #[inline(always)] 452 | fn get_top(&mut self, index: u8) -> &mut f64 { 453 | if super::SPEEEEEED { 454 | unsafe { 455 | &mut *self.top.sub(1).sub(index as usize) 456 | } 457 | } 458 | else { 459 | let len = self.vm.stack.len(); 460 | &mut self.vm.stack[len - 1 - index as usize] 461 | } 462 | } 463 | 464 | #[inline(always)] 465 | fn push(&mut self, value: f64) { 466 | if super::SPEEEEEED { 467 | unsafe { 468 | *self.top = value; 469 | self.top = self.top.add(1); 470 | } 471 | } 472 | else { 473 | self.vm.stack.push(value) 474 | } 475 | } 476 | 477 | #[inline(always)] 478 | fn pop(&mut self) -> f64 { 479 | if super::SPEEEEEED { 480 | unsafe { 481 | self.top = self.top.sub(1); 482 | *self.top 483 | } 484 | } 485 | else { 486 | self.vm.stack.pop().unwrap() 487 | } 488 | } 489 | 490 | #[inline(always)] 491 | fn clear(&mut self) { 492 | if super::SPEEEEEED { 493 | self.top = self.base; 494 | } 495 | else { 496 | self.vm.stack.clear(); 497 | } 498 | } 499 | } 500 | 501 | 502 | pub const FIB_SMART: &[Instruction] = { use Instruction::*; &[ 503 | SetCounter, 504 | LoadInt { value: 0 }, 505 | LoadInt { value: 1 }, 506 | Jump { target: 7 }, 507 | Dup, 508 | Rot, 509 | Add, 510 | Loop { target: 4 }, 511 | Pop, 512 | Return, 513 | ]}; 514 | 515 | pub const FIB_NAIVE: &[Instruction] = { use Instruction::*; &[ 516 | SetCounter, 517 | LoadInt { value: 0 }, 518 | LoadInt { value: 1 }, 519 | Jump { target: 10 }, 520 | // 4 521 | Load { src: 0 }, 522 | Load { src: 1 }, 523 | Add, 524 | Load { src: 1 }, 525 | Store { dst: 0 }, 526 | Store { dst: 1 }, 527 | // 10 528 | Loop { target: 4 }, 529 | Pop, 530 | Return, 531 | ]}; 532 | 533 | 534 | pub const MANDEL_SMART: &[Instruction] = { use Instruction::*; let (x0, y0, n, x, y) = (0, 1, 2, 3, 4); &[ 535 | Load { src: n }, 536 | SetCounter, 537 | 538 | LoadInt { value: 0 }, 539 | LoadInt { value: 0 }, 540 | 541 | Jump { target: 21 }, 542 | 543 | // 5 544 | // let xtemp = x*x - y*y + x0; 545 | Load { src: x }, 546 | Dup, 547 | Mul, 548 | Load { src: y }, 549 | Dup, 550 | Mul, 551 | Sub, 552 | Load { src: x0 }, 553 | Add, 554 | 555 | // 14 556 | // stack: x0, y0, n, x, y, xtemp 557 | Swap, 558 | // stack: x0, y0, n, x, xtemp, y 559 | Rot, 560 | // stack: x0, y0, n, xtemp, y, x 561 | 562 | // 16 563 | // y = x*y*2.0 + y0; 564 | Mul, 565 | LoadInt { value: 2 }, 566 | Mul, 567 | Load { src: y0 }, 568 | Add, 569 | 570 | // check x*x + y*y <= 2*2 571 | // 21 572 | Load { src: x }, 573 | Dup, 574 | Mul, 575 | Load { src: y }, 576 | Dup, 577 | Mul, 578 | Add, 579 | LoadInt { value: 4 }, 580 | LoopLe { target: 5 }, 581 | 582 | 583 | // 30 584 | Load { src: n }, 585 | GetCounter, 586 | Sub, 587 | Return, 588 | ]}; 589 | 590 | pub const MANDEL_NAIVE: &[Instruction] = { use Instruction::*; let (x0, y0, n, x, y, xtemp) = (0, 1, 2, 3, 4, 5); &[ 591 | Load { src: n }, 592 | SetCounter, 593 | 594 | LoadInt { value: 0 }, 595 | LoadInt { value: 0 }, 596 | LoadInt { value: 0 }, 597 | // stack: x0, y0, n, x, y, xtemp 598 | 599 | Jump { target: 26 }, 600 | 601 | // 6 602 | // let xtemp = x*x - y*y + x0; 603 | Load { src: x }, 604 | Load { src: x }, 605 | Mul, 606 | Load { src: y }, 607 | Load { src: y }, 608 | Mul, 609 | Sub, 610 | Load { src: x0 }, 611 | Add, 612 | Store { dst: xtemp }, 613 | 614 | // 16 615 | // y = x*y*2.0 + y0; 616 | Load { src: x }, 617 | Load { src: y }, 618 | Mul, 619 | LoadInt { value: 2 }, 620 | Mul, 621 | Load { src: y0 }, 622 | Add, 623 | Store { dst: y }, 624 | 625 | // 24 626 | // x = xtemp 627 | Load { src: xtemp }, 628 | Store { dst: x }, 629 | 630 | // check x*x + y*y <= 2*2 631 | // 26 632 | Load { src: x }, 633 | Load { src: x }, 634 | Mul, 635 | Load { src: y }, 636 | Load { src: y }, 637 | Mul, 638 | Add, 639 | LoadInt { value: 4 }, 640 | LoopLe { target: 6 }, 641 | 642 | 643 | // 35 644 | Load { src: n }, 645 | GetCounter, 646 | Sub, 647 | Return, 648 | ]}; 649 | 650 | 651 | pub const MANDEL_SMART_NOPS_SLOW: &[Instruction] = { use Instruction::*; let (x0, y0, n, x, y) = (0, 1, 2, 3, 4); &[ 652 | Load { src: n }, 653 | SetCounter, 654 | 655 | LoadInt { value: 0 }, 656 | LoadInt { value: 0 }, 657 | 658 | Jump { target: 21 + 3 }, 659 | 660 | // 5 661 | // let xtemp = x*x - y*y + x0; 662 | Load { src: x }, 663 | Dup, 664 | Mul, 665 | Load { src: y }, 666 | Nop, Nop, Nop, 667 | Dup, 668 | Mul, 669 | Sub, 670 | Load { src: x0 }, 671 | Add, 672 | 673 | // 14 + 3 674 | // stack: x0, y0, n, x, y, xtemp 675 | Swap, 676 | // stack: x0, y0, n, x, xtemp, y 677 | Rot, 678 | // stack: x0, y0, n, xtemp, y, x 679 | 680 | // 16 + 3 681 | // y = x*y*2.0 + y0; 682 | Mul, 683 | LoadInt { value: 2 }, 684 | Mul, 685 | Load { src: y0 }, 686 | Add, 687 | 688 | // check x*x + y*y <= 2*2 689 | // 21 + 3 690 | Load { src: x }, 691 | Dup, 692 | Mul, 693 | Load { src: y }, 694 | Dup, 695 | Mul, 696 | Add, 697 | LoadInt { value: 4 }, 698 | LoopLe { target: 5 }, 699 | 700 | 701 | // 30 + 3 702 | Load { src: n }, 703 | GetCounter, 704 | Sub, 705 | Return, 706 | ]}; 707 | 708 | pub const MANDEL_SMART_NOPS_SAME: &[Instruction] = { use Instruction::*; let (x0, y0, n, x, y) = (0, 1, 2, 3, 4); &[ 709 | Load { src: n }, 710 | SetCounter, 711 | 712 | LoadInt { value: 0 }, 713 | LoadInt { value: 0 }, 714 | 715 | Jump { target: 21 + 3 }, 716 | 717 | // 5 718 | // let xtemp = x*x - y*y + x0; 719 | Load { src: x }, 720 | Dup, 721 | Mul, 722 | Load { src: y }, 723 | Dup, 724 | Mul, 725 | Sub, 726 | Load { src: x0 }, 727 | Add, 728 | 729 | // 14 730 | // stack: x0, y0, n, x, y, xtemp 731 | Swap, 732 | // stack: x0, y0, n, x, xtemp, y 733 | Rot, 734 | // stack: x0, y0, n, xtemp, y, x 735 | Nop, Nop, Nop, 736 | 737 | // 16 + 3 738 | // y = x*y*2.0 + y0; 739 | Mul, 740 | LoadInt { value: 2 }, 741 | Mul, 742 | Load { src: y0 }, 743 | Add, 744 | 745 | // check x*x + y*y <= 2*2 746 | // 21 + 3 747 | Load { src: x }, 748 | Dup, 749 | Mul, 750 | Load { src: y }, 751 | Dup, 752 | Mul, 753 | Add, 754 | LoadInt { value: 4 }, 755 | LoopLe { target: 5 }, 756 | 757 | 758 | // 30 + 3 759 | Load { src: n }, 760 | GetCounter, 761 | Sub, 762 | Return, 763 | ]}; 764 | 765 | pub const MANDEL_SMART_NO_DUP: &[Instruction] = { use Instruction::*; let (x0, y0, n, x, y) = (0, 1, 2, 3, 4); &[ 766 | Load { src: n }, 767 | SetCounter, 768 | 769 | LoadInt { value: 0 }, 770 | LoadInt { value: 0 }, 771 | 772 | Jump { target: 21 }, 773 | 774 | // 5 775 | // let xtemp = x*x - y*y + x0; 776 | Load { src: x }, 777 | Load { src: x }, 778 | Mul, 779 | Load { src: y }, 780 | Load { src: y }, 781 | Mul, 782 | Sub, 783 | Load { src: x0 }, 784 | Add, 785 | 786 | // 14 787 | // stack: x0, y0, n, x, y, xtemp 788 | Swap, 789 | // stack: x0, y0, n, x, xtemp, y 790 | Rot, 791 | // stack: x0, y0, n, xtemp, y, x 792 | 793 | // 16 794 | // y = x*y*2.0 + y0; 795 | Mul, 796 | LoadInt { value: 2 }, 797 | Mul, 798 | Load { src: y0 }, 799 | Add, 800 | 801 | // check x*x + y*y <= 2*2 802 | // 21 803 | Load { src: x }, 804 | Load { src: x }, 805 | Mul, 806 | Load { src: y }, 807 | Load { src: y }, 808 | Mul, 809 | Add, 810 | LoadInt { value: 4 }, 811 | LoopLe { target: 5 }, 812 | 813 | 814 | // 30 815 | Load { src: n }, 816 | GetCounter, 817 | Sub, 818 | Return, 819 | ]}; 820 | } 821 | 822 | 823 | 824 | #[inline(never)] 825 | pub fn fib(n: f64) -> f64 { 826 | let mut a = 0.0; 827 | let mut b = 1.0; 828 | for _ in 0..n as u32 { 829 | (a, b) = (b, a+b); 830 | } 831 | a 832 | } 833 | 834 | #[inline(never)] 835 | pub fn mandel(x0: f64, y0: f64, limit: f64) -> f64 { 836 | let mut x = 0.0; 837 | let mut y = 0.0; 838 | let mut i = 0; 839 | while x*x + y*y <= 4.0 && i < limit as u32 { 840 | let xtemp = x*x - y*y + x0; 841 | y = x*y*2.0 + y0; 842 | x = xtemp; 843 | i += 1; 844 | } 845 | return i as f64; 846 | } 847 | 848 | 849 | #[cfg(test)] 850 | mod tests { 851 | use super::*; 852 | 853 | fn test_fib f64>(mut f: F) { 854 | for i in 0..1000 { 855 | assert_eq!(fib(i as f64), f(i as f64)); 856 | } 857 | } 858 | 859 | fn test_mandel f64>(mut f: F) { 860 | let limit = 10000.0; 861 | for (x, y) in [(0.0, 0.0), (1.0, 1.0), (0.239, -0.981), (-0.648, 0.129), (-0.812, -0.021), (0.687, 0.387), (-0.950786716408015, 0.7448839625558739), (-0.41795412221209083, 0.3891104473889062), (-0.30454550287093674, 0.3662807658368674), (-0.38954007305330274, 0.6099259700840349), (0.07342221713511665, -0.8427951153696895), (-0.11064854851199213, -0.926639764442595), (-0.7596206479056045, 0.048924374342336874), (-0.49581061588344877, -0.12784717276741908), (0.6437608077269181, 0.4495234224170379), (-0.35902711244632446, -0.27337416802046066), (0.46119571367001844, 0.6369625077084937), (-0.8337757474334115, 0.30803404049145233), (0.7567477274895906, 0.7425733436366662), (-0.05666196234341658, -0.7153313926145124), (-0.1712571411242514, -0.7293648513842237), (-0.18029887642403808, 0.9337582385391014), (0.15025609901930093, -0.2562979396981093), (-0.8740713363980595, -0.3026974138730323), (0.734282601227346, 0.44824566299076163), (-0.6450519022478793, 0.8973350411345271), (-0.12951887416193664, -0.9675733335426315), (-0.8496395992141339, -0.45856363991261984), (-0.05006755838234733, 0.08815703999052182), (-0.0819349209740825, 0.392640373018607), (0.38818643312858203, 0.017802320175963837), (-0.5804782726092039, -0.8114288891880093), (0.21174287430534067, -0.9779934844282805), (0.48485403501831414, -0.32072813452059323), (0.11816231997762872, 0.9901345882876014), (-0.32987818756568665, -0.790041653198585), (-0.751877131490938, 0.7158915912037904), (-0.27952689192889313, -0.990237696155138), (0.27114775350097875, -0.4153077703274628), (0.058473091300088376, -0.9049579783144228), (0.4753437616586973, 0.14657146392303133), (-0.1745580736849639, 0.4775850047960255), (-0.7249298752680955, -0.9641325799319125), (0.12624851616576604, -0.44348534069122003), (-0.973539445295293, 0.832103245702291), (-0.6743590940233655, 0.9387520845818487), (-0.16662572477960436, -0.900765313197615), (-0.5912590709584258, -0.5102679290062448), (0.8503235254831967, -0.33485304549647044), (0.08977059985414226, -0.5524679311716958), (0.9092630698742039, 0.651495043750099), (0.06998115038035468, -0.1554190026368456), (-0.7399797760579021, -0.006137159260573455), (-0.2757987315085746, -0.537347842353135), (-0.30389255510677926, 0.542418339351705), (0.05627319272720821, 0.6292518136210365), (-0.6203030673427403, 0.7900418292632594), (-0.7909573091413067, -0.396979443474341), (0.24824386339155158, 0.3388202675220937), (-0.0513868729451854, 0.17817438126698026), (0.9462416973938339, 0.8676690260189799), (-0.1139783605452167, 0.0038380988757436008), (-0.23121289597591965, -0.47531036160660034), (0.5828474701609332, -0.7839471493639378), (0.9770948454943438, -0.4683608606405316), (0.47740801560355584, -0.2883965096412553), (0.34009761202328015, 0.6546012541731507), (-0.4924202923168335, 0.7856942391002899), (0.6230983780135113, -0.19678697882493723), (-0.140144286728481, 0.9028641035510641), (0.02303038331068996, -0.3161713074917263), (-0.6435921111392493, 0.6404964303777942), (-0.7066891000784838, 0.45541585282911434), (-0.24137952675687369, -0.7447054863100193), (0.19991366412404066, -0.23703689667205108), (-0.9570794031819638, -0.9884389720144522), (-0.6797569127393104, 0.2082269559276082), (0.769667914938132, 0.3787028736840006), (0.5848065627210588, 0.21127658721982723), (0.4844247997855926, 0.7800864760973392), (0.4583131215187135, 0.8734790105541512), (-0.3952706913645596, 0.633963083692719), (-0.9486119273737694, 0.7587301881016015), (-0.46070283891613584, 0.5041607377115895), (0.995619007269551, 0.3803371649267848), (-0.2797833504752094, 0.9908037027712009), (-0.9167995403904445, -0.807580777212757), (-0.18027266265314745, -0.851839465543321), (0.6128426674227088, -0.09875532852024493), (0.011860446602936614, -0.8681653691688065), (-0.7107796408969822, 0.02980060781663707), (-0.06413027397564885, -0.13037851125697242), (0.11553944723254239, -0.4890274430377839), (0.3479886739255793, -0.8699677560350583), (0.38333167839236126, 0.4218707211211812), (0.638723023074957, -0.9683866946207558), (0.46354649116031754, 0.07563976922668769), (-0.06351773294699603, 0.8144404275239079), (0.5119372264249904, -0.9096216627089817), (-0.09883485075616494, 0.3297820865529715), (0.10358292605721098, -0.36692635243618077), (0.15047436546885562, -0.9806781334753645), (0.7617574165547447, 0.4763568144395216), (0.9160725920362325, 0.9823994209157814), (0.7548032671745575, 0.812465693071418), (0.3246690244662118, -0.3065870163715265), (-0.560268464119319, 0.9221103278210057), (0.4626072017685068, 0.15317773756585118), (0.6545368464007859, 0.9506033905866516), (0.5098893892275631, 0.7830254619622639), (-0.9966341637112501, 0.6861534690011151), (-0.385961778663223, 0.09296319784275209), (-0.6439053469798957, 0.8752161182375295), (0.5684180435556756, -0.7391524569031711), (-0.5741612839751691, -0.7694325902986678), (0.8870349468029142, -0.0626416154235423), (-0.7931632997873719, -0.1847117421834228), (-0.4170616485992771, -0.4058127574957917), (-0.012491529971446091, -0.7246396095112151), (-0.7011675969925535, -0.28220086603407024), (-0.1863646808060755, -0.9348786412235945), (0.7010535378320495, -0.8136524427155922), (-0.62997318656867, -0.5673706697790437), (0.20571346964486614, 0.8494676030862429), (-0.9582906806422553, 0.13304666328969184), (-0.9017636413609094, -0.7840101289980281), (-0.06844213395069287, 0.4449811706731899), (-0.6396613633959511, 0.7560570032865803), (0.17821628814178592, -0.6343776437447937), (0.644101079419162, -0.10231890186410197), (-0.9538502098940163, -0.4005239063329318), (-0.45575562738990283, 0.6260262571336148), (-0.022746028755935566, 0.7273548892777673), (-0.5843548196344637, -0.8970791745762465), (-0.134489546848358, -0.23508372044200576), (-0.4426578957700551, -0.4140837629408358), (-0.8960196125459656, 0.9648402032597405), (-0.5559247435005124, -0.9627298282745853), (-0.9816649175072354, 0.23566904569481584), (0.37830338720570555, 0.9659962089961276), (0.07032093120792404, -0.8448450554291562), (-0.043415679985484124, 0.2982047225662403), (-0.7426172167006169, -0.9450540467855513), (-0.7833001490152167, 0.8587880458484556), (0.5753231270739165, -0.3550369532737825), (0.9847762265520275, 0.5601827115776938), (0.7547992831124333, 0.28584939475098947), (-0.8448259708273849, -0.35476136665194846), (0.879827174886626, 0.88500567042747), (0.00876529646860802, -0.30836856894337306), (0.7747669072600987, 0.03129425244220663), (-0.9181318043271416, -0.1982212204694076), (-0.06470118131158964, 0.6372280272430135), (0.6586665563523673, -0.5780624300151294), (0.19158793464893065, -0.8718738170981424), (-0.6405141431119539, -0.5399480154588656), (-0.014859791537689349, 0.5613188554256261), (-0.5781514716971152, 0.2559171658208903), (-0.09436027264047842, -0.6637963328398158), (-0.6217035983366301, -0.4498016587418536), (-0.02539730686860331, -0.8160511451622021), (-0.1819750814557659, -0.48686881610529165), (-0.8311143734208255, 0.7345232437803397), (0.35720487022736136, 0.446537629591925), (0.040582999596036506, 0.27151188053807496), (-0.8582374284322447, -0.37397514967056456), (-0.2234253044314234, -0.5021431610668297), (0.061836579223409016, 0.2334741235096851), (-0.80899619273474, 0.061122116008547556), (0.09888582378400912, 0.13157005798444055), (0.4445239732731163, -0.00797318519075918), (-0.9232548892900476, 0.015610893892384903), (0.17258779115188272, -0.25500953936981885), (0.21398456462176796, 0.6500598840741447), (-0.3659869812873422, 0.29484229817933216), (-0.2749147404367873, -0.48538050541310906), (-0.5870409692233305, 0.282757960020392), (-0.009340854254250353, -0.5661727070520584), (0.715684675641828, 0.675955746635732), (-0.8905081791246903, 0.7234796547395257), (0.6429947842459898, 0.67193501065015), (0.42730640534452435, -0.925032998389832), (0.7829803203729666, 0.8067771780949617), (-0.9775718658097963, 0.5710827388382336), (0.0038854322067862768, 0.9691541535883614), (-0.7354493698452671, -0.7740512298723263), (0.6066203299748345, 0.7907036360462232), (-0.2224438094868928, -0.5466373525288557), (-0.6613459909460426, 0.17482481697589058), (-0.14091009854697045, -0.25741985123563094), (-0.6049343628632007, 0.7132474164128548), (0.6767791588126548, 0.46222825969908365), (-0.019881898612260862, 0.42921013485612036), (-0.024289173227493688, -0.6326047825520571), (0.9273609755463252, 0.9650020725623369), (0.43880148036486677, 0.9648468957926457), (0.7820211869090097, 0.7143859917652413), (0.10713771400332006, 0.87644702975162), (0.3603460364845872, 0.9734868379754906), (0.4646237172759058, -0.6227490350204024), (0.8685337181342596, -0.9486561516837511), (-0.4290526472599958, -0.07105881127723457), (-0.43449703896808, -0.43932628835061993), (0.8619314767156487, 0.959378844974651), (0.2524793886536818, -0.5086760933393686), (-0.968926808165882, -0.48180443592302513), (-0.9344049892159925, -0.9719354066164145), (0.8417012429814341, -0.846162566469103), (0.605011478012363, 0.6877711581719606), (0.29583324991339044, 0.3897339588911992), (0.3063194062122856, -0.4969091145392086), (-0.07403093613837086, -0.06210765636399951), (-0.8870943179841004, 0.13186549816749604), (-0.13905452148322728, -0.14200541685720025), (0.9059296265527592, -0.8674413586878444), (-0.8134297959876413, 0.8602852560727625), (0.5574039666928619, 0.5935991790218782), (-0.05988992995905518, 0.8331438388387209), (-0.15690479005758462, -0.09787464921091593), (-0.3499683268803646, -0.07036877228129979), (-0.7348170188722913, 0.6489097570964577), (0.08030663152612805, -0.4062556913602422), (-0.3948116107741906, -0.8419118545265418), (-0.7080619975080553, -0.9532463704924592), (0.9525099193607636, 0.7770440519594752), (0.8356696335520295, 0.11139106868318094), (0.37389056109299457, -0.10176149244871913), (-0.5179332268046919, 0.906755738195657), (-0.5101578900967876, -0.9795688509682434), (0.15559279752437916, -0.6175284856819807), (-0.3563748416756034, -0.7785942522281912), (-0.19025374281021157, 0.6459693559382012), (0.8948354100256368, -0.7186710154824598), (0.44374924585090136, 0.5837918431600173), (-0.4672648256848859, -0.3637024717244548), (-0.14026461181970284, 0.40814454352232743), (-0.8978006636593574, -0.7145974937988178), (0.11539804649202101, 0.14383105313034195), (-0.6062938753740512, 0.7502189488775537), (-0.4391572025223398, 0.9752356885866993), (-0.38715366747667934, -0.7985315162371476), (0.1255993226373906, 0.34908952025487716), (0.4225405126612505, 0.09137785183737068), (-0.13177079487908627, 0.22058823667211525), (-0.28381252837930226, 0.7711425341318037), (0.12869352408940005, -0.00020271374035063516), (-0.1448983949760465, -0.9880872282380235), (-0.0465272602088036, 0.3825699981736381), (-0.9077389357297825, 0.9009893307505754), (0.8110194938755062, -0.9800467040477308), (-0.01036226648551164, -0.20791141520235312), (-0.8491795910760915, -0.5161174572537066), (0.21510754077334227, 0.046435915914714965), (0.7015051124679705, -0.7139484315828417), (-0.8270226510406857, -0.037067448340805775), (0.3284454053998058, 0.7011277797513877), (0.8237931165115819, 0.7651378342874593), (0.3254212701250152, 0.9467579439573748), (0.6551253150336631, 0.09542055528534776), (0.059721730531613115, -0.2171872986083485), (0.4025247537451424, -0.23519298808428113), (-0.3656975388853696, 0.9488462674799432), (0.10234697465954268, -0.034042905991529704), (-0.021076834862308047, -0.30834904994034873), (-0.5425800701779875, -0.7901297323360688), (-0.5163096338241866, -0.0910731448043538), (0.775907526479902, -0.029927810692431223), (-0.5787010634781413, -0.17724734524515395), (-0.686139712926781, -0.6582853023191224), (-0.8201124220854916, 0.5324206558841762), (-0.5127613921846674, -0.46650367860717123), (0.4891824637526496, 0.061346961080255724), (-0.5571482675377373, 0.9411510539379417), (0.8707373419055771, 0.6207284256515975), (0.8219858954429966, -0.8779285711624081), (0.45726631268327833, 0.03270982726025018), (0.1853046782842407, -0.22408852515290034), (0.4255401852644969, -0.4449354754646797), (0.4317054766054196, -0.4691969281855446), (-0.5141023626569792, 0.7586320148155574), (0.7059121498036909, -0.2683408346536418), (0.09009019999661194, 0.7579800601346505), (0.747078544161313, -0.26381189782764225), (0.5335162579135664, -0.8201395593255578), (0.4399092773156905, 0.4001922617899951), (-0.2749366699470286, 0.22086561231547597), (0.09771281478875804, -0.8128675949424682), (0.8172959884714706, -0.22236236268899234), (-0.17248615077637086, 0.7974885426480312), (0.842252396378558, 0.7288571906820647), (0.7334957992325437, 0.07938913059765351), (0.39793866023322955, 0.8688245906412282), (0.7870291797356017, 0.06165030599614352), (-0.21473486896046423, 0.4392857648561843), (-0.5073085444740155, 0.4678281268067612), (-0.03340450051571797, 0.3363381514347139), (-0.30550362154772936, 0.7072959211525238), (-0.08235208900128632, 0.45302815156489107), (0.14964118143127214, -0.7335083814629508), (0.6494909995782758, -0.7605196537643031), (0.37417634836018854, -0.2745582477672208), (0.24402671028084932, -0.9659474167888), (0.6850340610798742, 0.3156075945350234), (-0.3353094019784082, 0.15936722855440655), (-0.35383706533582737, -0.4898161541205872), (0.08096129676569741, 0.984231777908692), (-0.09225205063861064, 0.11924130015978296), (-0.19117673582023098, -0.4570527844157015), (0.04484730422003991, -0.20431919075758143), (-0.214123335641055, -0.9923780601705212), (-0.5994170582611429, 0.6971508827916248), (0.8125161673575663, -0.5904108880645373), (-0.34462253292283207, 0.39677814793807964), (-0.4666775475043421, 0.7563498846928809), (-0.5353939698802537, 0.9372234589613415), (0.816330849123519, 0.7980310088153351), (-0.2442766721743026, 0.18660398148145663), (-0.795812123557933, -0.8439443207812483), (-0.7316221860345711, -0.00679059803926263), (0.029322252210048916, -0.039863229769652175), (0.8415655075406308, 0.2438708303677739), (-0.9738347839788026, 0.1820873912774983), (0.5812432844185678, -0.41807701493178806), (-0.11121009276117788, 0.09108995663689967), (0.4947780413182141, 0.731341348626044), (0.7549316153846815, 0.9940007248455749), (0.22703057664382742, -0.5973739903480808), (-0.5443322898224618, -0.9174453695604308), (0.32356085335906437, -0.4888945103497353), (0.359539113819318, -0.7408168135541069), (-0.22338680684947865, 0.28696110757103854), (-0.6034832533179653, -0.17871677245046125), (-0.1922451022511551, 0.6350068107028115), (-0.26205534723658164, -0.9428261933544171), (0.7931239883719934, 0.32715467498864514), (-0.37506617261760544, -0.1408871705323862), (0.060770083585807155, 0.8060976545229288), (-0.7736828407722232, -0.38511545538424485), (-0.4145664314854487, 0.5137234572891332), (0.05234994257136738, 0.832142969883906), (0.6007597711309947, 0.2405601651487772), (0.27450568752981797, -0.4408499884659134), (0.3389752585537307, 0.9808145570890736), (-0.5241924228584112, 0.2669518809982916), (-0.8763341953698638, -0.21111615960052754), (0.33981530567566987, -0.09232472231112476), (-0.8508778459163162, -0.2587109005842909), (0.8737211808432022, -0.5648439845189517), (0.8083427960826393, 0.11234062682554247), (-0.2712476783355593, -0.8774439655390605)] { 862 | assert_eq!(mandel(x, y, limit), f(x, y, limit)); 863 | } 864 | } 865 | 866 | 867 | #[test] 868 | fn reg_fib() { 869 | let mut vm = reg::Vm::new(); 870 | test_fib(|n| { 871 | vm.run(reg::FIB, &[n]) 872 | }); 873 | } 874 | 875 | #[test] 876 | fn reg_mandel() { 877 | let mut vm = reg::Vm::new(); 878 | test_mandel(|x, y, n| { 879 | vm.run(reg::MANDEL, &[x, y, n]) 880 | }); 881 | } 882 | 883 | #[test] 884 | fn stack_fib_smart() { 885 | let mut vm = stack::Vm::new(); 886 | test_fib(|n| { 887 | vm.run(stack::FIB_SMART, &[n]) 888 | }); 889 | } 890 | 891 | #[test] 892 | fn stack_fib_naive() { 893 | let mut vm = stack::Vm::new(); 894 | test_fib(|n| { 895 | vm.run(stack::FIB_NAIVE, &[n]) 896 | }); 897 | } 898 | 899 | #[test] 900 | fn stack_mandel_smart() { 901 | let mut vm = stack::Vm::new(); 902 | test_mandel(|x, y, n| { 903 | vm.run(stack::MANDEL_SMART, &[x, y, n]) 904 | }); 905 | } 906 | 907 | #[test] 908 | fn stack_mandel_naive() { 909 | let mut vm = stack::Vm::new(); 910 | test_mandel(|x, y, n| { 911 | vm.run(stack::MANDEL_NAIVE, &[x, y, n]) 912 | }); 913 | } 914 | 915 | #[test] 916 | fn stack_mandel_smart_nops_slow() { 917 | let mut vm = stack::Vm::new(); 918 | test_mandel(|x, y, n| { 919 | vm.run(stack::MANDEL_SMART_NOPS_SLOW, &[x, y, n]) 920 | }); 921 | } 922 | 923 | #[test] 924 | fn stack_mandel_smart_nops_same() { 925 | let mut vm = stack::Vm::new(); 926 | test_mandel(|x, y, n| { 927 | vm.run(stack::MANDEL_SMART_NOPS_SAME, &[x, y, n]) 928 | }); 929 | } 930 | 931 | #[test] 932 | fn stack_mandel_smart_no_dup() { 933 | let mut vm = stack::Vm::new(); 934 | test_mandel(|x, y, n| { 935 | vm.run(stack::MANDEL_SMART_NO_DUP, &[x, y, n]) 936 | }); 937 | } 938 | 939 | #[test] 940 | fn reg_add_chain() { 941 | let add_regs: [f64; 16] = core::array::from_fn(|i| i as f64); 942 | let mut vm = reg::Vm::new(); 943 | assert_eq!(vm.run(®::ADD_CHAIN, &add_regs), (16*15/2) as f64); 944 | } 945 | 946 | #[test] 947 | fn reg_add_fast() { 948 | let add_regs: [f64; 16] = core::array::from_fn(|i| i as f64); 949 | let mut vm = reg::Vm::new(); 950 | assert_eq!(vm.run(®::ADD_PAIRS, &add_regs), (16*15/2) as f64); 951 | } 952 | } 953 | 954 | -------------------------------------------------------------------------------- /stack-vs-reg/mcanvas_cpu.ts: -------------------------------------------------------------------------------- 1 | import {Layout, LayoutProps} from "@motion-canvas/2d/lib/components" 2 | import {Rect, Vector2} from "@motion-canvas/core/lib/types"; 3 | import {initial, property} from "@motion-canvas/2d/lib/decorators" 4 | import {all, delay, sequence, waitFor} from "@motion-canvas/core/lib/flow"; 5 | import {Signal} from "@motion-canvas/core/lib/utils"; 6 | import {drawRoundRect} from "@motion-canvas/2d/lib/utils"; 7 | import {linear} from "@motion-canvas/core/lib/tweening"; 8 | import {decorate, threadable} from "@motion-canvas/core/lib/decorators" 9 | import {ThreadGenerator} from "@motion-canvas/core/lib/threading"; 10 | 11 | 12 | export class Cpu extends Layout { 13 | public code_view: CodeView; 14 | public controller: Controller; 15 | public decoder: Decoder; 16 | public math: MathUnit; 17 | public registers: Registers; 18 | public memory: MemoryUnit; 19 | 20 | public program: Instruction[] 21 | public decode_counter: number; 22 | public blocked: boolean; 23 | 24 | public decode_limit: number; 25 | public anim_decode_duration: number; 26 | public anim_read_duration: number; 27 | public anim_exec_duration: number; 28 | 29 | public constructor(props?: LayoutProps) { 30 | super(props); 31 | 32 | this.setWidth(1700); 33 | this.setHeight(800); 34 | 35 | let x0 = -850; 36 | let y0 = -400; 37 | 38 | this.code_view = new CodeView({ 39 | x: x0, y: y0, 40 | width: 350, height: 475, 41 | }); 42 | 43 | this.decoder = new Decoder({ 44 | x: x0 + 400, y: y0, 45 | width: 300, height: 320, 46 | }); 47 | this.registers = new Registers({ 48 | x: x0 + 400, y: y0 + 370, 49 | width: 300, height: 430, 50 | }) 51 | this.controller = new Controller({ 52 | x: x0 + 750, y: y0, 53 | width: 450, height: 800, 54 | }); 55 | this.math = new MathUnit({ 56 | x: x0 + 1250, y: y0, 57 | width: 450, height: 320, 58 | }); 59 | this.memory = new MemoryUnit({ 60 | x: x0 + 1250, y: y0 + 370, 61 | width: 450, height: 430, 62 | }); 63 | this.add([this.code_view, this.controller, this.decoder, this.math, this.registers, this.memory]); 64 | 65 | this.program = []; 66 | this.decode_counter = 0; 67 | this.blocked = false; 68 | 69 | this.decode_limit = 4; 70 | this.anim_decode_duration = 0.6; 71 | this.anim_read_duration = 0.4; 72 | this.anim_exec_duration = 0.75; 73 | } 74 | 75 | 76 | public* decoded_to_controller() { 77 | if(this.blocked) { 78 | return; 79 | } 80 | 81 | let ctrl = this.controller; 82 | let slots_remaining = ctrl.slots.length - ctrl.in_flight; 83 | 84 | let instructions: Instruction[] = []; 85 | for(const s of this.decoder.slots) { 86 | let i = s.instruction; 87 | if(i && slots_remaining) { 88 | instructions.push(i); 89 | s.instruction = null; 90 | slots_remaining -= 1; 91 | 92 | if(i.jump_kind) { 93 | break; 94 | } 95 | } 96 | } 97 | 98 | yield* this.controller.add_instructions(instructions); 99 | } 100 | 101 | public* dispatch() { 102 | let regs: (Register | Instruction)[] = this.registers.registers.slice(); 103 | 104 | let anims: ThreadGenerator[] = []; 105 | 106 | this.controller.slots.forEach(s => { 107 | if(!s.instruction) { return } 108 | 109 | let i = s.instruction; 110 | 111 | let blocked = false; 112 | let values = i.reads.map(r => regs[r]); 113 | values.forEach(v => { 114 | if(v instanceof Instruction && !v.done) { 115 | blocked = true; 116 | } 117 | }); 118 | i.blocked = blocked; 119 | i.read_values = values; 120 | i.invalidate(); 121 | 122 | if(i.writes !== null) { 123 | console.assert(!i.done || !!i.wb, i); 124 | regs[i.writes] = i; 125 | } 126 | 127 | if(!blocked && !i.done) { 128 | if(i.unit == "math") { 129 | let slot = this.math.slots.find(s => !s.instruction); 130 | if(slot) { 131 | let o = new ProgressOverlay(slot); 132 | o.max_opacity(0.9); 133 | slot.instruction = i; 134 | anims.push(this.dispatch_instruction(s, slot)); 135 | } 136 | } 137 | else if(i.unit == "memory") { 138 | console.assert(!"unimplemented"); 139 | } 140 | else if(i.unit == "none") { 141 | let o = new ProgressOverlay(s); 142 | o.max_opacity(0.9); 143 | } 144 | else { 145 | console.assert(false); 146 | } 147 | } 148 | }); 149 | 150 | yield* all(waitFor(0.2), ...anims); 151 | } 152 | 153 | public* execute() { 154 | let overlays: ProgressOverlay[] = []; 155 | let instructions: Instruction[] = []; 156 | 157 | this.controller.slots.forEach(s => { 158 | let i = s.instruction; 159 | if(i && i.unit == "none" && !i.done && !i.blocked) { 160 | overlays.push(s.overlay); 161 | i.run(); 162 | instructions.push(i); 163 | } 164 | }); 165 | 166 | this.math.slots.forEach(s => { 167 | let i = s.instruction; 168 | if(i && !i.done && !i.blocked) { 169 | overlays.push(s.overlay); 170 | i.run(); 171 | instructions.push(i); 172 | } 173 | }); 174 | 175 | yield* all( 176 | ...overlays.map(o => o?.run(this.anim_exec_duration)), 177 | ...instructions.map(i => i.anim_reads(this.anim_read_duration)), 178 | ); 179 | } 180 | 181 | public* gather() { 182 | yield* all(...this.math.slots.map(s => { 183 | let i = s.instruction; 184 | if(i && i.done) { 185 | return this.gather_instruction(s); 186 | } 187 | })); 188 | } 189 | 190 | public* write_back() { 191 | let slots = this.controller.slots; 192 | 193 | let n = slots.findIndex(s => { 194 | let i = s.instruction; 195 | return !i || !i.done 196 | }); 197 | 198 | yield* sequence(0.15, 199 | ...this.controller.slots.slice(0, n) 200 | .map(s => { 201 | let i = s.instruction; 202 | if(i.done && i.wb) { 203 | return this.write_back_instruction(i); 204 | } 205 | })); 206 | } 207 | 208 | public* retire() { 209 | let slots = this.controller.slots; 210 | 211 | let n = slots.findIndex(s => { 212 | let i = s.instruction; 213 | return !i || !i.done 214 | }); 215 | 216 | this.blocked = true; 217 | let end = this.controller.in_flight; 218 | if(end > 0) { 219 | let last = slots[end - 1].instruction; 220 | this.decode_counter = last.pc + 1; 221 | 222 | if(last.jump_kind) { 223 | let kind = last.jump_kind; 224 | if(kind == "jump") { 225 | this.blocked = false; 226 | this.decode_counter = last.jump_target; 227 | } 228 | else { 229 | kind = kind.slice(1); 230 | let negate = false; 231 | if(kind.startsWith("n")) { 232 | negate = true; 233 | kind = kind.slice(1); 234 | } 235 | 236 | if(last.done) { 237 | let v = last.read_values[0]; 238 | let value = v instanceof Register ? v.value : v.wb.value; 239 | this.blocked = false; 240 | if(value == kind && !negate || value != kind && negate) { 241 | this.decode_counter = last.jump_target; 242 | } 243 | } 244 | } 245 | } 246 | else { 247 | this.blocked = false; 248 | } 249 | } 250 | 251 | yield* this.controller.retire_instructions(n); 252 | 253 | this.code_view.set_counters( 254 | this.controller.slots[0].instruction?.pc 255 | ?? this.decode_counter, 256 | this.decode_counter, 257 | this.decode_limit 258 | ); 259 | } 260 | 261 | public* run(max_iters: number = 1000) { 262 | let iters = 0; 263 | while(iters < max_iters) { 264 | iters += 1; 265 | 266 | if(this.blocked 267 | && this.decode_counter >= this.program.length 268 | && this.controller.in_flight == 0 269 | ) { break } 270 | 271 | let new_instructions = this.program.slice( 272 | this.decode_counter, 273 | this.decode_counter + this.decode_limit, 274 | ); 275 | 276 | yield* this.decoder.decode(new_instructions, 277 | this.decode_limit, 278 | this.anim_decode_duration 279 | ); 280 | 281 | yield* this.decoded_to_controller(); 282 | yield* waitFor(0.2); 283 | 284 | yield* this.dispatch(); 285 | 286 | yield* this.execute(); 287 | yield* waitFor(0.1); 288 | 289 | yield* this.gather(); 290 | yield* waitFor(0.2); 291 | 292 | yield* this.write_back(); 293 | yield* this.retire(); 294 | } 295 | } 296 | 297 | 298 | public* dispatch_instruction(from: BasicSlot, to: BasicSlot) { 299 | to.tag = from.tag; 300 | to.instruction = from.instruction; 301 | 302 | yield* all( 303 | to.instruction.position(position_in_parent(to, -40), 0.4), 304 | delay(0.2, to.tag_opacity(1, 0.2)), 305 | ); 306 | } 307 | 308 | 309 | public* gather_instruction(from: BasicSlot) { 310 | let to = this.controller.slots.find(s => s.tag == from.tag); 311 | from.instruction = null; 312 | 313 | yield* all( 314 | to.instruction.position(position_in_parent(to, -40), 0.4), 315 | from.tag_opacity(0, 0.2), 316 | ); 317 | from.tag = ""; 318 | } 319 | 320 | public* write_back_instruction(instruction: Instruction) { 321 | let wb = instruction.wb; 322 | console.assert(!!wb); 323 | instruction.wb = null; 324 | 325 | let pos = instruction.position().add(wb.position()); 326 | wb.remove(); 327 | this.add(wb); 328 | wb.position(pos); 329 | 330 | let register = this.registers.registers[wb.register]; 331 | 332 | let target = position_in_parent(register); 333 | yield* all( 334 | wb.position(target, 0.4), 335 | delay(0.3, wb.opacity(0, 0.1)), 336 | delay(0.3, register.set_value(wb.value)), 337 | ); 338 | wb.remove(); 339 | } 340 | 341 | public parse(asm: string): Instruction[] { 342 | let pc = 0; 343 | let labels: Record = {}; 344 | 345 | let lines = asm.trim().split("\n").map(line => line.trim()); 346 | 347 | lines.forEach(line => { 348 | if(line.length < 1) { return } 349 | 350 | if(line.endsWith(":")) { 351 | let label = line.split(":")[0]; 352 | labels[label] = pc; 353 | } 354 | else { 355 | pc += 1; 356 | } 357 | }); 358 | 359 | let result: Instruction[] = []; 360 | lines.forEach(line => { 361 | if(line.length < 1 || line.endsWith(":")) { return } 362 | 363 | let asm = line.split(" ").filter(s => s.length > 0).join(" "); 364 | 365 | let parts = line.replace(",", "").split(" ").filter(part => part.length > 0); 366 | 367 | let op = parts[0]; 368 | if(op == "set") { 369 | let r = parseInt(parts[1].slice(1)); 370 | let v = parseFloat(parts[2].slice(1)); 371 | result.push(new Instruction({ 372 | asm, pc: result.length, 373 | reads: [], 374 | writes: r, 375 | exec: exec_set(v), 376 | unit: "none", 377 | })); 378 | } 379 | else if(op == "copy") { 380 | let dst = parseInt(parts[1].slice(1)); 381 | let src = parseInt(parts[2].slice(1)); 382 | result.push(new Instruction({ 383 | asm, pc: result.length, 384 | reads: [src], 385 | writes: dst, 386 | exec: exec_copy, 387 | unit: "none", 388 | })); 389 | } 390 | else if(op == "add") { 391 | let dst = parseInt(parts[1].slice(1)); 392 | let src1 = parseInt(parts[2].slice(1)); 393 | if(parts[3].startsWith("#")) { 394 | let src2 = parseFloat(parts[3].slice(1)); 395 | result.push(new Instruction({ 396 | asm, pc: result.length, 397 | reads: [src1], 398 | writes: dst, 399 | exec: exec_add_imm(src2), 400 | unit: "math", 401 | })); 402 | } 403 | else { 404 | let src2 = parseInt(parts[3].slice(1)); 405 | result.push(new Instruction({ 406 | asm, pc: result.length, 407 | reads: [src1, src2], 408 | writes: dst, 409 | exec: exec_add, 410 | unit: "math", 411 | })); 412 | } 413 | } 414 | else if(op == "cmp") { 415 | let src1 = parseInt(parts[1].slice(1)); 416 | let src2 = parseInt(parts[2].slice(1)); 417 | result.push(new Instruction({ 418 | asm, pc: result.length, 419 | reads: [src1, src2], 420 | writes: 11, 421 | exec: exec_cmp, 422 | unit: "math", 423 | })); 424 | } 425 | else if(op == "jump" || op == "jlt" || op == "jnlt") { 426 | let jump_target = labels[parts[1]]; 427 | console.assert(typeof jump_target === "number"); 428 | 429 | let reads = op == "jump" ? [] : [11]; 430 | result.push(new Instruction({ 431 | asm, pc: result.length, 432 | reads, 433 | writes: null, 434 | exec: exec_none, 435 | unit: "none", 436 | jump_kind: op, jump_target, 437 | })); 438 | } 439 | else { 440 | console.assert(!"invalid asm", line); 441 | } 442 | }); 443 | return result; 444 | } 445 | 446 | public set_code(asm: string) { 447 | this.program = this.parse(asm); 448 | 449 | let lines = asm.split("\n"); 450 | lines = lines.slice(lines.findIndex(line => line.length > 0)); 451 | while(lines.length && lines[lines.length-1].trim().length < 1) { 452 | lines.pop(); 453 | } 454 | 455 | this.code_view.set_code(lines); 456 | this.code_view.set_counters(0, 0, this.decode_limit); 457 | this.decode_counter = 0; 458 | this.blocked = false; 459 | } 460 | 461 | public set_regs(values: number[]) { 462 | let regs = this.registers.registers; 463 | values.slice(0, 10).forEach((v, i) => { 464 | regs[i].value = v.toString(); 465 | }); 466 | } 467 | } 468 | decorate(Cpu.prototype.decoded_to_controller, threadable()); 469 | decorate(Cpu.prototype.dispatch, threadable()); 470 | decorate(Cpu.prototype.execute, threadable()); 471 | decorate(Cpu.prototype.gather, threadable()); 472 | decorate(Cpu.prototype.retire, threadable()); 473 | decorate(Cpu.prototype.run, threadable()); 474 | decorate(Cpu.prototype.dispatch_instruction, threadable()); 475 | decorate(Cpu.prototype.gather_instruction, threadable()); 476 | decorate(Cpu.prototype.write_back_instruction, threadable()); 477 | 478 | 479 | let exec_set = (value: number) => (values: number[]) => value; 480 | 481 | let exec_copy = (values: number[]) => values[0]; 482 | 483 | let exec_add = (values: number[]) => values[0] + values[1]; 484 | 485 | let exec_add_imm = (imm: number) => (values: number[]) => values[0] + imm; 486 | 487 | let exec_cmp = (values: number[]) => { 488 | let [a, b] = values 489 | if(a == b) { return "eq" } 490 | if(a < b) { return "lt" } 491 | if(a > b) { return "gt" } 492 | return "ne"; 493 | }; 494 | 495 | let exec_none = (values: number[]) => null as number | null; 496 | 497 | 498 | 499 | function position_in_parent(node: Layout, dx?: number, dy?: number): Vector2 { 500 | let parent = node.parent() as Layout; 501 | let x = parent.position.x(); 502 | let y = parent.position.y(); 503 | return new Vector2( 504 | x + node.position.x() + (dx ?? 0), 505 | y + node.position.y() + (dy ?? 0)); 506 | } 507 | 508 | function draw_component(name: string, rect: Rect, ctx: CanvasRenderingContext2D) { 509 | ctx.beginPath(); 510 | drawRoundRect(ctx, rect, 9); 511 | ctx.closePath(); 512 | 513 | ctx.save(); 514 | ctx.fillStyle = "#333949"; 515 | ctx.shadowBlur = 7; 516 | ctx.shadowOffsetY = 3; 517 | ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; 518 | ctx.fill(); 519 | ctx.restore(); 520 | 521 | ctx.save(); 522 | ctx.strokeStyle = "#394051"; 523 | ctx.lineWidth = 2; 524 | ctx.stroke(); 525 | ctx.restore(); 526 | 527 | ctx.font = "bold 24px Source Code Pro" 528 | ctx.fillStyle = "#7D8391"; 529 | ctx.fillText(name, rect.x + 20, rect.y + 36); 530 | } 531 | 532 | 533 | export interface InstructionProps extends LayoutProps { 534 | asm: string; 535 | reads: number[]; 536 | writes: number | null; 537 | exec: (values: number[]) => number | string | null; 538 | unit: "math" | "memory" | "none"; 539 | pc: number; 540 | jump_kind?: string; 541 | jump_target?: number; 542 | } 543 | 544 | export class Instruction extends Layout { 545 | public asm: string; 546 | public reads: number[]; 547 | public read_values: (Register | Instruction)[]; 548 | public writes: number | null; 549 | public exec: (values: number[]) => number | string | null; 550 | public unit: "math" | "memory" | "none"; 551 | public pc: number; 552 | public jump_kind: string | null; 553 | public jump_target: number | null; 554 | public tag: string; 555 | public wb: WriteBack | null; 556 | public blocked: boolean; 557 | public done: boolean; 558 | @initial(0) @property() 559 | public declare state: Signal; 560 | 561 | public constructor(props?: InstructionProps) { 562 | super(props); 563 | this.setWidth(225); 564 | this.setHeight(25); 565 | this.asm = props.asm; 566 | this.reads = props.reads; 567 | this.writes = props.writes; 568 | this.read_values = []; 569 | this.exec = props.exec; 570 | this.unit = props.unit; 571 | this.pc = props.pc; 572 | this.jump_kind = props.jump_kind ?? null; 573 | this.jump_target = props.jump_target ?? null; 574 | this.tag = ""; 575 | this.wb = null; 576 | this.blocked = false; 577 | this.done = false; 578 | } 579 | 580 | protected override draw(ctx: CanvasRenderingContext2D): void { 581 | let _ = this.state(); 582 | let x = -this.size().x/2; 583 | let fill = "#BFBDB6"; 584 | if(this.done) { fill = "#858684" } 585 | if(this.blocked) { fill = "#D9461E" } 586 | ctx.fillStyle = fill; 587 | ctx.font = (this.done ? "italic " : "") + "24px Source Code Pro" 588 | ctx.textBaseline = "middle"; 589 | ctx.fillText(this.asm, x, 2); 590 | super.draw(ctx); 591 | } 592 | 593 | public run(): WriteBack | null { 594 | console.assert(!this.blocked); 595 | console.assert(!this.done); 596 | 597 | let values = this.read_values.map(v => { 598 | let value = v instanceof Register ? v.value : v.wb.value; 599 | return parseFloat(value); 600 | }); 601 | 602 | let result = this.exec(values); 603 | this.done = true; 604 | this.invalidate() 605 | 606 | if(this.writes === null) { 607 | return null; 608 | } 609 | 610 | // this is so cursed, lol 611 | let value = typeof result === "number" 612 | ? parseFloat(result.toFixed(2)).toString() 613 | : result; 614 | 615 | let wb = new WriteBack({ register: this.writes, value }); 616 | wb.position.x(this.size.x()/2); 617 | wb.opacity(0.0); 618 | this.wb = wb; 619 | this.add(wb); 620 | return wb; 621 | } 622 | 623 | public* anim_reads(duration: number) { 624 | yield* all(...this.read_values.map((r, index) => { 625 | let pos, text; 626 | if(r instanceof Register) { 627 | pos = position_in_parent(r); 628 | text = r.value; 629 | } 630 | else { 631 | pos = position_in_parent(r.wb, r.wb.size().x/2); 632 | text = r.wb.value; 633 | } 634 | 635 | let read = new RegRead({ 636 | x: pos.x, 637 | y: pos.y, 638 | text, 639 | }); 640 | this.parent().add(read); 641 | 642 | let tx = this.position.x() + this.size.x()/2 + index*45 + 30; 643 | let ty = this.position.y(); 644 | return read.move_and_die([tx, ty], duration); 645 | })); 646 | } 647 | 648 | public invalidate() { 649 | this.state(this.state() + 1); 650 | } 651 | } 652 | decorate(Instruction.prototype.anim_reads, threadable()); 653 | 654 | 655 | export interface WriteBackProps extends LayoutProps { 656 | register: number; 657 | value: string; 658 | } 659 | 660 | export class WriteBack extends Layout { 661 | public text: string; 662 | public register: number; 663 | public value: string; 664 | 665 | public constructor(props?: WriteBackProps) { 666 | super(props); 667 | this.offset([-1, 0]); 668 | this.setWidth(100); 669 | this.setHeight(25); 670 | let reg = props.register; 671 | let name = reg == 11 ? "cmp" : "r"+reg; 672 | this.text = name + " = " + props.value, 673 | this.register = props.register; 674 | this.value = props.value; 675 | } 676 | 677 | protected override draw(ctx: CanvasRenderingContext2D): void { 678 | ctx.fillStyle = "#BFBDB6"; 679 | ctx.font = "24px Source Code Pro" 680 | ctx.textBaseline = "middle"; 681 | ctx.fillText(this.text, -this.getWidth()/2, 2); 682 | } 683 | } 684 | 685 | 686 | export interface RegReadProps extends LayoutProps { 687 | text: string; 688 | } 689 | 690 | export class RegRead extends Layout { 691 | public text: string; 692 | 693 | public constructor(props?: RegReadProps) { 694 | super(props); 695 | this.offset([-1, 0]); 696 | this.setWidth(100); 697 | this.setHeight(25); 698 | this.text = props.text; 699 | } 700 | 701 | protected override draw(ctx: CanvasRenderingContext2D): void { 702 | ctx.fillStyle = "#BFBDB6"; 703 | ctx.font = "24px Source Code Pro" 704 | ctx.textBaseline = "middle"; 705 | ctx.fillText(this.text, -this.getWidth()/2, 2); 706 | } 707 | 708 | public* move_and_die(target: [number, number], duration: number) { 709 | yield* this.position(target, duration); 710 | this.remove(); 711 | } 712 | } 713 | decorate(RegRead.prototype.move_and_die, threadable()); 714 | 715 | 716 | export class BasicSlot extends Layout { 717 | @initial(0.0) 718 | @property() 719 | public declare tag_opacity: Signal; 720 | 721 | public tag: string; 722 | public instruction: Instruction | null; 723 | 724 | public overlay: ProgressOverlay | null; 725 | 726 | public constructor(props?: LayoutProps) { 727 | super(props); 728 | this.tag = ""; 729 | this.instruction = null; 730 | this.overlay = null; 731 | } 732 | 733 | protected override draw(ctx: CanvasRenderingContext2D) { 734 | let rect = Rect.fromSizeCentered(this.size()); 735 | 736 | ctx.fillStyle = "#2A303D"; 737 | ctx.beginPath(); 738 | drawRoundRect(ctx, rect, 5); 739 | ctx.fill(); 740 | 741 | let w = this.size.x(); 742 | ctx.save() 743 | ctx.fillStyle = "#7D8391"; 744 | ctx.globalAlpha = this.tag_opacity(); 745 | ctx.font = "bold 20px Source Code Pro" 746 | ctx.textBaseline = "middle"; 747 | ctx.fillText(this.tag, -w/2 + 12, 2); 748 | ctx.restore(); 749 | } 750 | } 751 | 752 | 753 | export class ProgressOverlay extends Layout { 754 | @initial(0.0) 755 | @property() 756 | public declare progress: Signal; 757 | 758 | @initial(1.0) 759 | @property() 760 | public declare max_opacity: Signal; 761 | 762 | public slot: BasicSlot; 763 | public task: any; 764 | 765 | public constructor(slot: BasicSlot) { 766 | super({}); 767 | this.task = null; 768 | this.slot = slot; 769 | console.assert(slot.overlay === null); 770 | this.opacity(0); 771 | this.position(position_in_parent(slot)); 772 | this.size(slot.size()); 773 | slot.parent().parent().add(this); 774 | slot.overlay = this; 775 | } 776 | 777 | public* run(duration: number) { 778 | let fade = Math.min(0.1*duration, 0.1); 779 | this.opacity(0); 780 | this.progress(0); 781 | yield* all( 782 | this.opacity(this.max_opacity, fade), 783 | this.progress(1, duration, linear), 784 | delay(duration - fade, all( 785 | this.opacity(0, fade), 786 | this.slot.instruction?.wb?.opacity(1, fade), 787 | )), 788 | ); 789 | 790 | this.slot.overlay = null; 791 | this.remove(); 792 | } 793 | 794 | protected override draw(ctx: CanvasRenderingContext2D) { 795 | let rect = Rect.fromSizeCentered(this.size()); 796 | 797 | ctx.fillStyle = "#232833"; 798 | ctx.beginPath(); 799 | drawRoundRect(ctx, rect, 5); 800 | ctx.fill(); 801 | 802 | ctx.save(); 803 | ctx.strokeStyle = "#97B0DB"; 804 | ctx.lineWidth = 3; 805 | ctx.beginPath(); 806 | ctx.arc(0, 0, 10, -Math.PI/2, -Math.PI/2 + 2*Math.PI*this.progress()); 807 | ctx.stroke(); 808 | ctx.restore(); 809 | } 810 | } 811 | decorate(ProgressOverlay.prototype.run, threadable()); 812 | 813 | 814 | 815 | export interface ModuleProps extends LayoutProps { 816 | x: number; 817 | y: number; 818 | width: number; 819 | height: number; 820 | } 821 | 822 | 823 | export class Decoder extends Layout { 824 | public slots: BasicSlot[]; 825 | 826 | public constructor(props?: ModuleProps) { 827 | props.x += props.width/2; 828 | props.y += props.height/2; 829 | super(props); 830 | 831 | let y0 = -props.height/2 + 90; 832 | let width = 250; 833 | let height = 50; 834 | this.slots = [ 835 | new BasicSlot({ y: y0 + 0*60, width, height }), 836 | new BasicSlot({ y: y0 + 1*60, width, height }), 837 | new BasicSlot({ y: y0 + 2*60, width, height }), 838 | new BasicSlot({ y: y0 + 3*60, width, height }), 839 | ]; 840 | this.add(this.slots); 841 | } 842 | 843 | public set_instructions(instructions: Instruction[]) { 844 | this.slots.forEach(s => { 845 | if(s.instruction) { 846 | s.instruction.remove(); 847 | s.instruction = null; 848 | } 849 | }); 850 | 851 | for(let i = 0; i < instructions.length; i += 1) { 852 | let instr = instructions[i]; 853 | let instruction = new Instruction({ 854 | asm: instr.asm, reads: instr.reads, writes: instr.writes, 855 | exec: instr.exec, unit: instr.unit, 856 | pc: instr.pc, 857 | jump_kind: instr.jump_kind, jump_target: instr.jump_target, 858 | }); 859 | this.parent().add(instruction); 860 | 861 | let slot = this.slots[i]; 862 | instruction.position(position_in_parent(slot)); 863 | slot.instruction = instruction; 864 | } 865 | } 866 | 867 | public* decode(instructions: Instruction[], limit: number, duration: number) { 868 | this.set_instructions(instructions); 869 | this.slots.forEach(s => s.instruction?.opacity(0)); 870 | 871 | let overlays = this.slots.slice(0, limit).map(s => new ProgressOverlay(s)); 872 | 873 | yield* all( 874 | ...overlays.map(o => o.run(duration)), 875 | delay(0.4, all( 876 | ...this.slots.map(s => s.instruction?.opacity(1, 0.1)))), 877 | ); 878 | } 879 | 880 | protected override draw(ctx: CanvasRenderingContext2D) { 881 | let rect = Rect.fromSizeCentered(this.size()); 882 | draw_component("decoder", rect, ctx); 883 | 884 | super.draw(ctx); 885 | } 886 | } 887 | decorate(Decoder.prototype.decode, threadable()); 888 | 889 | 890 | 891 | export class Controller extends Layout { 892 | public slots: BasicSlot[]; 893 | public in_flight: number; 894 | 895 | public constructor(props?: ModuleProps) { 896 | props.x += props.width/2; 897 | props.y += props.height/2; 898 | super(props); 899 | 900 | let y0 = -props.height/2 + 90; 901 | let width = 400; 902 | let height = 50; 903 | let gap = 10; 904 | this.slots = []; 905 | for(let i = 0; i < 12; i += 1) { 906 | let slot = new BasicSlot({ y: y0 + i*(height + gap), width, height }); 907 | slot.tag = String.fromCharCode(97 + i); 908 | slot.tag_opacity(1.0); 909 | this.slots.push(slot); 910 | } 911 | this.add(this.slots); 912 | 913 | this.in_flight = 0; 914 | } 915 | 916 | protected override draw(ctx: CanvasRenderingContext2D) { 917 | let rect = Rect.fromSizeCentered(this.size()); 918 | draw_component("controller", rect, ctx); 919 | 920 | super.draw(ctx); 921 | } 922 | 923 | public* add_instructions(instructions: Instruction[]) { 924 | let n = instructions.length; 925 | 926 | let begin = this.slots.findIndex(s => !s.instruction); 927 | 928 | let anims = []; 929 | for(let i = 0; i < n; i += 1) { 930 | let slot = this.slots[begin + i]; 931 | if(!slot) { break } 932 | 933 | let instruction = instructions[i]; 934 | anims.push(instruction.position(position_in_parent(slot, -40), 0.4)); 935 | slot.instruction = instruction; 936 | } 937 | yield* all(...anims); 938 | 939 | this.in_flight = 0; 940 | this.slots.forEach(s => { 941 | if(s.instruction) { 942 | this.in_flight += 1; 943 | } 944 | }); 945 | } 946 | 947 | public* retire_instructions(count: number) { 948 | let top_slots = this.slots.slice(0, count); 949 | let bottom_slots = this.slots.slice(count); 950 | 951 | yield* all(...top_slots.map(s => s.instruction.opacity(0, 0.2))); 952 | top_slots.forEach(s => { 953 | s.instruction.remove(); 954 | s.instruction = null; 955 | }); 956 | 957 | let shift_up = count*60; 958 | let shift_down = (this.slots.length - count)*60; 959 | 960 | yield* all( 961 | ...top_slots.map(s => s.opacity(0, 0.2)), 962 | ...top_slots.map(s => s.position.y(s.position.y() + shift_down, 0.4)), 963 | ...top_slots.map(s => delay(0.2, s.opacity(1, 0.2))), 964 | ...bottom_slots.map(s => s.position.y(s.position.y() - shift_up, 0.4)), 965 | ...bottom_slots.filter(s => s.instruction) 966 | .map(s => { 967 | let i = s.instruction; 968 | return i.position.y(i.position.y() - shift_up, 0.4); 969 | }), 970 | ); 971 | 972 | bottom_slots.push(...top_slots); 973 | this.slots = bottom_slots; 974 | 975 | this.in_flight = 0; 976 | this.slots.forEach(s => { 977 | if(s.instruction) { 978 | this.in_flight += 1; 979 | } 980 | }); 981 | } 982 | } 983 | decorate(Controller.prototype.add_instructions, threadable()); 984 | decorate(Controller.prototype.retire_instructions, threadable()); 985 | 986 | 987 | 988 | export class Registers extends Layout { 989 | public registers: Register[]; 990 | 991 | public constructor(props?: ModuleProps) { 992 | props.x += props.width/2; 993 | props.y += props.height/2; 994 | super(props); 995 | 996 | this.registers = [ 997 | new Register({ name: "r0", x: -65, y: -135 + 0*60 }), 998 | new Register({ name: "r1", x: 65, y: -135 + 0*60 }), 999 | new Register({ name: "r2", x: -65, y: -135 + 1*60 }), 1000 | new Register({ name: "r3", x: 65, y: -135 + 1*60 }), 1001 | new Register({ name: "r4", x: -65, y: -135 + 2*60 }), 1002 | new Register({ name: "r5", x: 65, y: -135 + 2*60 }), 1003 | new Register({ name: "r6", x: -65, y: -135 + 3*60 }), 1004 | new Register({ name: "r7", x: 65, y: -135 + 3*60 }), 1005 | new Register({ name: "r8", x: -65, y: -135 + 4*60 }), 1006 | new Register({ name: "r9", x: 65, y: -135 + 4*60 }), 1007 | new Register({ name: "r10", x: -65, y: -135 + 5*60 }), 1008 | new Register({ name: "cmp", x: 65, y: -135 + 5*60 }), 1009 | ]; 1010 | this.registers.forEach(r => this.add(r)); 1011 | 1012 | let cmp = this.registers[11]; 1013 | cmp.color = "#F29668"; 1014 | cmp.value = "eq"; 1015 | } 1016 | 1017 | protected override draw(ctx: CanvasRenderingContext2D) { 1018 | let rect = Rect.fromSizeCentered(this.size()); 1019 | draw_component("registers", rect, ctx); 1020 | super.draw(ctx); 1021 | } 1022 | } 1023 | 1024 | 1025 | export interface RegisterProps extends LayoutProps { 1026 | name: string; 1027 | } 1028 | 1029 | export class Register extends Layout { 1030 | public name: string; 1031 | public value: string; 1032 | public color: string; 1033 | 1034 | public constructor(props?: RegisterProps) { 1035 | super(props); 1036 | this.setWidth(120); 1037 | this.setHeight(50); 1038 | this.name = props.name; 1039 | this.value = "0"; 1040 | this.color = "#D2A6FF"; 1041 | } 1042 | 1043 | public* set_value(new_value: string) { 1044 | yield* this.scale(1.2, 0.1); 1045 | this.value = new_value; 1046 | yield* this.scale(1, 0.1); 1047 | } 1048 | 1049 | protected override draw(ctx: CanvasRenderingContext2D) { 1050 | let rect = Rect.fromSizeCentered(this.size()); 1051 | 1052 | ctx.fillStyle = "#2A303D"; 1053 | ctx.beginPath(); 1054 | drawRoundRect(ctx, rect, 5); 1055 | ctx.fill(); 1056 | 1057 | let w = this.size.x(); 1058 | ctx.save() 1059 | ctx.textBaseline = "middle"; 1060 | 1061 | ctx.fillStyle = "#7D8391"; 1062 | ctx.font = "bold 20px Source Code Pro" 1063 | ctx.fillText(this.name, -w/2 + 12, 2); 1064 | 1065 | ctx.fillStyle = this.color; 1066 | ctx.font = "20px Source Code Pro" 1067 | ctx.textAlign = "end"; 1068 | ctx.fillText(this.value, w/2 - 18, 2); 1069 | ctx.restore(); 1070 | } 1071 | } 1072 | decorate(Register.prototype.set_value, threadable()); 1073 | 1074 | 1075 | 1076 | export class MathUnit extends Layout { 1077 | public slots: BasicSlot[]; 1078 | 1079 | public constructor(props?: ModuleProps) { 1080 | props.x += props.width/2; 1081 | props.y += props.height/2; 1082 | super(props); 1083 | 1084 | let y0 = -props.height/2 + 90; 1085 | let width = 400; 1086 | let height = 50; 1087 | let gap = 10; 1088 | this.slots = []; 1089 | for(let i = 0; i < 4; i += 1) { 1090 | this.slots.push(new BasicSlot({ y: y0 + i*(height + gap), width, height })); 1091 | } 1092 | this.add(this.slots); 1093 | } 1094 | 1095 | protected override draw(ctx: CanvasRenderingContext2D) { 1096 | let rect = Rect.fromSizeCentered(this.size()); 1097 | draw_component("math unit", rect, ctx); 1098 | super.draw(ctx); 1099 | } 1100 | } 1101 | 1102 | 1103 | 1104 | export class MemoryUnit extends Layout { 1105 | public load_slots: BasicSlot[]; 1106 | public store_slots: BasicSlot[]; 1107 | 1108 | public constructor(props?: ModuleProps) { 1109 | props.x += props.width/2; 1110 | props.y += props.height/2; 1111 | super(props); 1112 | 1113 | let y0 = -props.height/2 + 90; 1114 | let width = 400; 1115 | let height = 50; 1116 | let gap = 10; 1117 | y0 += 20; 1118 | this.load_slots = [ 1119 | new BasicSlot({ y: y0 + 0*(height + gap), width, height }), 1120 | new BasicSlot({ y: y0 + 1*(height + gap), width, height }), 1121 | new BasicSlot({ y: y0 + 2*(height + gap), width, height }), 1122 | ]; 1123 | y0 += 30; 1124 | this.store_slots = [ 1125 | new BasicSlot({ y: y0 + 3*(height + gap), width, height }), 1126 | new BasicSlot({ y: y0 + 4*(height + gap), width, height }), 1127 | ]; 1128 | this.add(this.load_slots); 1129 | this.add(this.store_slots); 1130 | } 1131 | 1132 | protected override draw(ctx: CanvasRenderingContext2D) { 1133 | let rect = Rect.fromSizeCentered(this.size()); 1134 | draw_component("memory unit", rect, ctx); 1135 | super.draw(ctx); 1136 | 1137 | ctx.font = "italic 22px Source Code Pro" 1138 | ctx.fillStyle = "#7D8391"; 1139 | 1140 | let s = this.load_slots[0]; 1141 | let x = s.position.x() - s.size.x()/2; 1142 | let y = s.position.y() - s.size.y()/2; 1143 | x += 8; 1144 | y -= 12; 1145 | ctx.fillText("load", x, y); 1146 | 1147 | s = this.store_slots[0]; 1148 | y = s.position.y() - s.size.y()/2; 1149 | y -= 12; 1150 | ctx.fillText("store", x, y); 1151 | } 1152 | } 1153 | 1154 | 1155 | export class CodeView extends Layout { 1156 | public code: string[]; 1157 | public pc_to_line: number[]; 1158 | public program_index: number; 1159 | public decode_indices: number[]; 1160 | 1161 | public constructor(props?: ModuleProps) { 1162 | props.x += props.width/2; 1163 | props.y += props.height/2; 1164 | super(props); 1165 | this.code = []; 1166 | this.pc_to_line = [0]; 1167 | this.program_index = 0; 1168 | this.decode_indices = []; 1169 | } 1170 | 1171 | public set_code(code: string[]) { 1172 | let pc_to_line: number[] = []; 1173 | code.forEach((line, index) => { 1174 | line = line.trim(); 1175 | if(line.length > 0 && !line.endsWith(":")) { 1176 | pc_to_line.push(index); 1177 | } 1178 | }); 1179 | pc_to_line.push(code.length); 1180 | this.code = code; 1181 | this.pc_to_line = pc_to_line; 1182 | 1183 | let y = this.position.y() - this.getHeight()/2; 1184 | let h = 110 + code.length*26; 1185 | this.position.y(y + h/2); 1186 | this.setHeight(h); 1187 | } 1188 | 1189 | public set_counters(pc: number, dc: number, decode_limit: number) { 1190 | this.program_index = this.pc_to_line[pc]; 1191 | 1192 | this.decode_indices.length = 0; 1193 | let dc_end = Math.min(this.pc_to_line.length - 1, dc + decode_limit); 1194 | for(let i = dc; i < dc_end; i += 1) { 1195 | this.decode_indices.push(this.pc_to_line[i]); 1196 | } 1197 | if(this.decode_indices.length == 0) { 1198 | this.decode_indices.push(this.code.length); 1199 | } 1200 | } 1201 | 1202 | protected override draw(ctx: CanvasRenderingContext2D) { 1203 | let rect = Rect.fromSizeCentered(this.size()); 1204 | draw_component("assembly code", rect, ctx); 1205 | 1206 | let x0 = -this.getWidth()/2 + 36; 1207 | let y0 = -this.getHeight()/2 + 70; 1208 | let gap = 26; 1209 | 1210 | ctx.fillStyle = "#BFBDB6"; 1211 | ctx.font = "22px Source Code Pro" 1212 | ctx.textBaseline = "top"; 1213 | this.code.forEach((line, index) => { 1214 | ctx.fillText(line, x0, y0 + gap*index + 2); 1215 | }); 1216 | 1217 | ctx.fillStyle = "#7D8391"; 1218 | ctx.beginPath(); 1219 | for(const di of this.decode_indices) { 1220 | ctx.ellipse(x0 + 19, y0 + gap*(di + 0.5), 3, 3, 0, 0, 2*Math.PI); 1221 | ctx.closePath(); 1222 | } 1223 | ctx.fill(); 1224 | 1225 | let pi = this.program_index; 1226 | ctx.fillStyle = "yellow"; 1227 | ctx.strokeStyle = "#232833"; 1228 | ctx.lineWidth = 1; 1229 | ctx.beginPath(); { 1230 | let x = x0 - 5; 1231 | let y = y0 + gap*(pi + 0.5); 1232 | ctx.moveTo(x, y); 1233 | ctx.lineTo(x, y + 4); 1234 | ctx.lineTo(x + 7, y + 4); 1235 | ctx.lineTo(x + 7, y + 10); 1236 | ctx.lineTo(x + 17, y); 1237 | ctx.lineTo(x + 7, y - 10); 1238 | ctx.lineTo(x + 7, y - 4); 1239 | ctx.lineTo(x, y - 4); 1240 | ctx.closePath(); 1241 | } 1242 | ctx.fill(); 1243 | ctx.stroke(); 1244 | 1245 | super.draw(ctx); 1246 | } 1247 | } 1248 | 1249 | -------------------------------------------------------------------------------- /do-less/d19.rs: -------------------------------------------------------------------------------- 1 | /* results 2 | 3 | baseline 4 | part 1 result: 994 in 145.5780515s 5 | memo refs: 834619249 6 | memo hits: 398856779 (48%) 7 | states visited: 834619249 8 | 9 | dp earlier hit 10 | part 1 result: 994 in 73.725909042s 11 | memo refs: 570205591 12 | memo hits: 272534745 (48%) 13 | states visited: 570205591 14 | 15 | u8 16 | part 1 result: 994 in 61.4443895s 17 | memo refs: 570205591 18 | memo hits: 272534745 (48%) 19 | states visited: 570205591 20 | 21 | u8 as u64 22 | part 1 result: 994 in 50.229123s 23 | memo refs: 570205591 24 | memo hits: 272534745 (48%) 25 | states visited: 570205591 26 | 27 | thonk geodes first 28 | part 1 result: 994 in 27.130415375s 29 | memo refs: 388886719 30 | memo hits: 187664247 (48%) 31 | states visited: 388886719 32 | 33 | thonk max_result 34 | part 1 result: 994 in 4.466372916s 35 | memo refs: 82181447 36 | memo hits: 37532477 (46%) 37 | states visited: 82181447 38 | 39 | thonk enough bots 40 | part 1 result: 994 in 544.071625ms 41 | memo refs: 12631450 42 | memo hits: 4919665 (39%) 43 | states visited: 12631450 44 | 45 | thonk don't idle 46 | part 1 result: 994 in 29.940291ms 47 | memo refs: 566020 48 | memo hits: 25144 (4%) 49 | states visited: 566020 50 | 51 | thonk no memo 52 | part 1 result: 994 in 2.720334ms 53 | memo refs: 0 54 | memo hits: 0 (NaN%) 55 | states visited: 674356 56 | 57 | 58 | baseline 59 | part 1 result: 994 in 139.228871125s 60 | dp earlier hit 61 | part 1 result: 994 in 72.762682833s 62 | u8 63 | part 1 result: 994 in 60.486425875s 64 | u8 as u64 65 | part 1 result: 994 in 45.978252375s 66 | thonk geodes first 67 | part 1 result: 994 in 26.093973166s 68 | thonk max_result 69 | part 1 result: 994 in 4.468414709s 70 | thonk enough bots 71 | part 1 result: 994 in 533.643333ms 72 | thonk don't idle 73 | part 1 result: 994 in 29.961834ms 74 | thonk no memo 75 | part 1 result: 994 in 2.635ms 76 | 77 | baseline 78 | part 1 result: 994 in 133.571285333s 79 | dp earlier hit 80 | part 1 result: 994 in 70.392901708s 81 | u8 82 | part 1 result: 994 in 58.661585875s 83 | u8 as u64 84 | part 1 result: 994 in 44.474583s 85 | thonk geodes first 86 | part 1 result: 994 in 25.377575s 87 | thonk max_result 88 | part 1 result: 994 in 4.358779459s 89 | thonk enough bots 90 | part 1 result: 994 in 528.285125ms 91 | thonk don't idle 92 | part 1 result: 994 in 29.662875ms 93 | thonk no memo 94 | part 1 result: 994 in 2.617959ms 95 | 96 | 101,468,602,368 97 | */ 98 | 99 | 100 | use std::cell::UnsafeCell; 101 | 102 | 103 | struct Stats { 104 | memo_refs: u64, 105 | memo_hits: u64, 106 | states_visited: u64, 107 | states_skipped: u128, 108 | } 109 | 110 | impl Stats { 111 | #[inline] 112 | const fn new() -> Self { 113 | Stats { memo_refs: 0, memo_hits: 0, states_visited: 0, states_skipped: 0 } 114 | } 115 | 116 | fn reset(&mut self) { 117 | *self = Self::new(); 118 | } 119 | 120 | fn print(&self) { 121 | println!("memo refs: {}", self.memo_refs); 122 | println!("memo hits: {} ({:.0}%)", self.memo_hits, self.memo_hits as f64 / self.memo_refs as f64 * 100.0); 123 | println!("states visited: {}", self.states_visited); 124 | println!("states skipped: {}", self.states_skipped); 125 | println!(); 126 | } 127 | } 128 | 129 | struct StatsNonsense { 130 | inner: UnsafeCell, 131 | } 132 | 133 | impl StatsNonsense { 134 | fn with(&self, f: F) { 135 | if DO_STATS { 136 | f(unsafe { &mut *self.inner.get() }) 137 | } 138 | } 139 | } 140 | 141 | unsafe impl Sync for StatsNonsense {} 142 | 143 | 144 | const DO_STATS: bool = 0==1; 145 | static STATS: StatsNonsense = StatsNonsense { inner: UnsafeCell::new(Stats::new()) }; 146 | 147 | 148 | mod baseline { 149 | use regex::Regex; 150 | 151 | use super::STATS; 152 | 153 | #[derive(Clone, Copy, Debug)] 154 | pub struct Blueprint { 155 | pub id: u32, 156 | pub ore_robot: u32, 157 | pub clay_robot: u32, 158 | pub obsidian_robot: (u32, u32), 159 | pub geode_robot: (u32, u32), 160 | } 161 | 162 | pub fn parse(input: &str) -> Vec { 163 | let mut result = Vec::with_capacity(128); 164 | 165 | let re = Regex::new(r"\d+").unwrap(); 166 | for line in input.lines() { 167 | let mut numbers = re.find_iter(line); 168 | let mut next = || -> u32 { 169 | let number = numbers.next().unwrap(); 170 | number.as_str().parse().unwrap() 171 | }; 172 | 173 | let id = next(); 174 | let ore_robot = next(); 175 | let clay_robot = next(); 176 | let obsidian_robot = (next(), next()); 177 | let geode_robot = (next(), next()); 178 | result.push(Blueprint { 179 | id, 180 | ore_robot, 181 | clay_robot, 182 | obsidian_robot, 183 | geode_robot, 184 | }); 185 | assert!(numbers.next().is_none()); 186 | } 187 | 188 | result 189 | } 190 | 191 | 192 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 193 | struct State { 194 | minute: u8, 195 | ore_robot: u32, 196 | clay_robot: u32, 197 | obsidian_robot: u32, 198 | geode_robot: u32, 199 | ore: u32, 200 | clay: u32, 201 | obsidian: u32, 202 | geode: u32, 203 | } 204 | 205 | impl State { 206 | fn new() -> Self { 207 | State { 208 | minute: 0, 209 | ore_robot: 1, 210 | clay_robot: 0, 211 | obsidian_robot: 0, 212 | geode_robot: 0, 213 | ore: 0, 214 | clay: 0, 215 | obsidian: 0, 216 | geode: 0, 217 | } 218 | } 219 | 220 | #[inline] 221 | fn can_build_ore_robot(&self, bp: &Blueprint) -> bool { 222 | self.ore >= bp.ore_robot 223 | } 224 | 225 | #[inline] 226 | fn can_build_clay_robot(&self, bp: &Blueprint) -> bool { 227 | self.ore >= bp.clay_robot 228 | } 229 | 230 | #[inline] 231 | fn can_build_obsidian_robot(&self, bp: &Blueprint) -> bool { 232 | self.ore >= bp.obsidian_robot.0 233 | && self.clay >= bp.obsidian_robot.1 234 | } 235 | 236 | #[inline] 237 | fn can_build_geode_robot(&self, bp: &Blueprint) -> bool { 238 | self.ore >= bp.geode_robot.0 239 | && self.obsidian >= bp.geode_robot.1 240 | } 241 | 242 | #[inline] 243 | fn build_ore_robot(self, bp: &Blueprint) -> Self { 244 | let mut result = self; 245 | result.ore -= bp.ore_robot; 246 | result.ore_robot += 1; 247 | return result; 248 | } 249 | 250 | #[inline] 251 | fn build_clay_robot(self, bp: &Blueprint) -> Self { 252 | let mut result = self; 253 | result.ore -= bp.clay_robot; 254 | result.clay_robot += 1; 255 | return result; 256 | } 257 | 258 | #[inline] 259 | fn build_obsidian_robot(self, bp: &Blueprint) -> Self { 260 | let mut result = self; 261 | result.ore -= bp.obsidian_robot.0; 262 | result.clay -= bp.obsidian_robot.1; 263 | result.obsidian_robot += 1; 264 | return result; 265 | } 266 | 267 | #[inline] 268 | fn build_geode_robot(self, bp: &Blueprint) -> Self { 269 | let mut result = self; 270 | result.ore -= bp.geode_robot.0; 271 | result.obsidian -= bp.geode_robot.1; 272 | result.geode_robot += 1; 273 | return result; 274 | } 275 | 276 | #[inline] 277 | fn step(self) -> Self { 278 | let mut this = self; 279 | this.minute += 1; 280 | this.ore += this.ore_robot; 281 | this.clay += this.clay_robot; 282 | this.obsidian += this.obsidian_robot; 283 | this.geode += this.geode_robot; 284 | return this; 285 | } 286 | } 287 | 288 | 289 | pub mod v1 { 290 | use super::{Blueprint, State}; 291 | 292 | pub fn solve(bp: &Blueprint, limit: u8) -> u32 { 293 | let mut state = State::new(); 294 | for _ in 0..limit { 295 | if state.can_build_geode_robot(bp) { 296 | state = state.step().build_geode_robot(bp); 297 | } 298 | else if state.can_build_obsidian_robot(bp) { 299 | state = state.step().build_obsidian_robot(bp); 300 | } 301 | else if state.can_build_clay_robot(bp) { 302 | state = state.step().build_clay_robot(bp); 303 | } 304 | else if state.can_build_ore_robot(bp) { 305 | state = state.step().build_ore_robot(bp); 306 | } 307 | else { 308 | state = state.step(); 309 | } 310 | } 311 | 312 | state.geode 313 | } 314 | } 315 | 316 | pub mod v2 { 317 | use super::{STATS, Blueprint, State}; 318 | 319 | fn solution(state: State, bp: &Blueprint, limit: u8) -> u32 { 320 | STATS.with(|s| { s.states_visited += 1 }); 321 | STATS.with(|s| { 322 | if s.states_visited % (128*1024*1024) == 0 { 323 | println!("{}", s.states_visited); 324 | } 325 | }); 326 | 327 | if state.minute == limit { 328 | return state.geode; 329 | } 330 | 331 | let mut result = 0; 332 | 333 | if state.can_build_geode_robot(bp) { 334 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit)); 335 | } 336 | 337 | if state.can_build_obsidian_robot(bp) { 338 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit)); 339 | } 340 | 341 | if state.can_build_clay_robot(bp) { 342 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit)); 343 | } 344 | 345 | if state.can_build_ore_robot(bp) { 346 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit)); 347 | } 348 | 349 | result = result.max(solution(state.step(), bp, limit)); 350 | 351 | return result; 352 | } 353 | 354 | pub fn solve(bp: &Blueprint, limit: u8) -> u32 { 355 | solution(State::new(), bp, limit) 356 | } 357 | } 358 | 359 | pub mod v3 { 360 | use std::collections::HashMap; 361 | 362 | use super::{STATS, Blueprint, State}; 363 | 364 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap) -> u32 { 365 | STATS.with(|s| { 366 | s.memo_refs += 1; 367 | s.states_visited += 1; 368 | }); 369 | 370 | if let Some(result) = memo.get(&state).copied() { 371 | STATS.with(|s| { s.memo_hits += 1 }); 372 | return result; 373 | } 374 | 375 | if state.minute == limit { 376 | let result = state.geode; 377 | memo.insert(state, result); 378 | return result; 379 | } 380 | 381 | let mut result = 0; 382 | 383 | if state.can_build_geode_robot(bp) { 384 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo)); 385 | } 386 | 387 | if state.can_build_obsidian_robot(bp) { 388 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo)); 389 | } 390 | 391 | if state.can_build_clay_robot(bp) { 392 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo)); 393 | } 394 | 395 | if state.can_build_ore_robot(bp) { 396 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo)); 397 | } 398 | 399 | result = result.max(solution(state.step(), bp, limit, memo)); 400 | 401 | memo.insert(state, result); 402 | 403 | return result; 404 | } 405 | 406 | pub fn solve(bp: &Blueprint, limit: u8) -> u32 { 407 | let mut memo = HashMap::new(); 408 | solution(State::new(), bp, limit, &mut memo) 409 | } 410 | } 411 | 412 | pub mod survivor { 413 | use std::collections::HashMap; 414 | 415 | use super::{STATS, Blueprint, State}; 416 | 417 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap) -> (u32, u128) { 418 | STATS.with(|s| { 419 | s.memo_refs += 1; 420 | s.states_visited += 1; 421 | }); 422 | 423 | if let Some(result) = memo.get(&state).copied() { 424 | STATS.with(|s| { 425 | s.memo_hits += 1; 426 | s.states_skipped += result.1; 427 | }); 428 | return result; 429 | } 430 | 431 | if state.minute == limit { 432 | let result = state.geode; 433 | memo.insert(state, (result, 1)); 434 | return (result, 1); 435 | } 436 | 437 | let mut result = 0; 438 | let mut children = 0; 439 | 440 | if state.can_build_geode_robot(bp) { 441 | let (r, c) = solution(state.step().build_geode_robot(bp), bp, limit, memo); 442 | result = result.max(r); 443 | children += c; 444 | } 445 | 446 | if state.can_build_obsidian_robot(bp) { 447 | let (r, c) = solution(state.step().build_obsidian_robot(bp), bp, limit, memo); 448 | result = result.max(r); 449 | children += c; 450 | } 451 | 452 | if state.can_build_clay_robot(bp) { 453 | let (r, c) = solution(state.step().build_clay_robot(bp), bp, limit, memo); 454 | result = result.max(r); 455 | children += c; 456 | } 457 | 458 | if state.can_build_ore_robot(bp) { 459 | let (r, c) = solution(state.step().build_ore_robot(bp), bp, limit, memo); 460 | result = result.max(r); 461 | children += c; 462 | } 463 | 464 | { 465 | let (r, c) = solution(state.step(), bp, limit, memo); 466 | result = result.max(r); 467 | children += c; 468 | } 469 | 470 | memo.insert(state, (result, children)); 471 | 472 | return (result, children); 473 | } 474 | 475 | pub fn solve(bp: &Blueprint, limit: u8) -> u32 { 476 | let mut memo = HashMap::new(); 477 | let (r, c) = solution(State::new(), bp, limit, &mut memo); 478 | println!("children: {c}"); 479 | return r; 480 | } 481 | } 482 | 483 | pub mod printer { 484 | use super::{STATS, Blueprint, State}; 485 | 486 | fn solution(state: State, bp: &Blueprint, limit: u8) { 487 | println!("{:?}", state); 488 | 489 | if state.minute == limit { 490 | return; 491 | } 492 | 493 | #[inline] 494 | fn indent(state: State) { 495 | for _ in 0..state.minute + 1 { 496 | print!(" "); 497 | } 498 | } 499 | 500 | if state.can_build_geode_robot(bp) { 501 | indent(state); 502 | print!("geode "); 503 | solution(state.step().build_geode_robot(bp), bp, limit); 504 | } 505 | 506 | if state.can_build_obsidian_robot(bp) { 507 | indent(state); 508 | print!("obsidian "); 509 | solution(state.step().build_obsidian_robot(bp), bp, limit); 510 | } 511 | 512 | if state.can_build_clay_robot(bp) { 513 | indent(state); 514 | print!("clay "); 515 | solution(state.step().build_clay_robot(bp), bp, limit); 516 | } 517 | 518 | if state.can_build_ore_robot(bp) { 519 | indent(state); 520 | print!("ore "); 521 | solution(state.step().build_ore_robot(bp), bp, limit); 522 | } 523 | 524 | indent(state); 525 | print!("wait "); 526 | solution(state.step(), bp, limit); 527 | } 528 | 529 | pub fn tree(bp: &Blueprint, limit: u8) { 530 | solution(State::new(), bp, limit); 531 | } 532 | } 533 | 534 | pub fn part_1 u32>(bps: &[Blueprint], f: F) { 535 | super::STATS.with(|s| s.reset()); 536 | let t0 = std::time::Instant::now(); 537 | let mut result = 0; 538 | for bp in bps { 539 | let geodes = f(bp, 24); 540 | // println!("geodes: {}", geodes); 541 | result += bp.id as u32 * geodes as u32; 542 | } 543 | println!("part 1 result: {} in {:?}", result, t0.elapsed()); 544 | super::STATS.with(|s| s.print()); 545 | } 546 | } 547 | 548 | mod pack { 549 | pub use super::baseline::{Blueprint, parse, part_1}; 550 | 551 | use super::STATS; 552 | 553 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 554 | struct Pack { 555 | ore_robot: u32, 556 | clay_robot: u32, 557 | obsidian_robot: u32, 558 | geode_robot: u32, 559 | ore: u32, 560 | clay: u32, 561 | obsidian: u32, 562 | geode: u32, 563 | } 564 | 565 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 566 | struct State { 567 | minute: u8, 568 | pack: Pack, 569 | } 570 | 571 | impl State { 572 | fn new() -> Self { 573 | State { 574 | minute: 0, 575 | pack: Pack { 576 | ore_robot: 1, 577 | clay_robot: 0, 578 | obsidian_robot: 0, 579 | geode_robot: 0, 580 | ore: 0, 581 | clay: 0, 582 | obsidian: 0, 583 | geode: 0, 584 | }, 585 | } 586 | } 587 | 588 | #[inline] 589 | fn can_build_ore_robot(&self, bp: &Blueprint) -> bool { 590 | self.pack.ore >= bp.ore_robot 591 | } 592 | 593 | #[inline] 594 | fn can_build_clay_robot(&self, bp: &Blueprint) -> bool { 595 | self.pack.ore >= bp.clay_robot 596 | } 597 | 598 | #[inline] 599 | fn can_build_obsidian_robot(&self, bp: &Blueprint) -> bool { 600 | self.pack.ore >= bp.obsidian_robot.0 601 | && self.pack.clay >= bp.obsidian_robot.1 602 | } 603 | 604 | #[inline] 605 | fn can_build_geode_robot(&self, bp: &Blueprint) -> bool { 606 | self.pack.ore >= bp.geode_robot.0 607 | && self.pack.obsidian >= bp.geode_robot.1 608 | } 609 | 610 | #[inline] 611 | fn build_ore_robot(self, bp: &Blueprint) -> Self { 612 | let mut result = self; 613 | result.pack.ore -= bp.ore_robot; 614 | result.pack.ore_robot += 1; 615 | return result; 616 | } 617 | 618 | #[inline] 619 | fn build_clay_robot(self, bp: &Blueprint) -> Self { 620 | let mut result = self; 621 | result.pack.ore -= bp.clay_robot; 622 | result.pack.clay_robot += 1; 623 | return result; 624 | } 625 | 626 | #[inline] 627 | fn build_obsidian_robot(self, bp: &Blueprint) -> Self { 628 | let mut result = self; 629 | result.pack.ore -= bp.obsidian_robot.0; 630 | result.pack.clay -= bp.obsidian_robot.1; 631 | result.pack.obsidian_robot += 1; 632 | return result; 633 | } 634 | 635 | #[inline] 636 | fn build_geode_robot(self, bp: &Blueprint) -> Self { 637 | let mut result = self; 638 | result.pack.ore -= bp.geode_robot.0; 639 | result.pack.obsidian -= bp.geode_robot.1; 640 | result.pack.geode_robot += 1; 641 | return result; 642 | } 643 | 644 | #[inline] 645 | fn step(self) -> Self { 646 | let mut this = self; 647 | this.minute += 1; 648 | this.pack.ore += this.pack.ore_robot; 649 | this.pack.clay += this.pack.clay_robot; 650 | this.pack.obsidian += this.pack.obsidian_robot; 651 | this.pack.geode += this.pack.geode_robot; 652 | return this; 653 | } 654 | } 655 | 656 | 657 | pub mod v1 { 658 | use std::collections::HashMap; 659 | 660 | use super::STATS; 661 | 662 | use super::{Blueprint, Pack, State}; 663 | 664 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap) -> u32 { 665 | STATS.with(|s| { 666 | s.memo_refs += 1; 667 | s.states_visited += 1; 668 | }); 669 | 670 | if let Some((result, minute)) = memo.get(&state.pack).copied() { 671 | if state.minute >= minute { 672 | STATS.with(|s| { s.memo_hits += 1 }); 673 | 674 | return result; 675 | } 676 | } 677 | 678 | #[inline] 679 | fn insert(memo: &mut HashMap, state: &State, result: u32) { 680 | // note: for some reason, this is equivalent to simply overriding the result. 681 | // ie: `memo.insert(pack, result)` 682 | // could be, cause we're writing the results on the way up (going backwards in time). 683 | // but not sure how that would work with "sibling branches". 684 | // in any case, this isn't measurably slower, so who cares. 685 | memo.entry(state.pack) 686 | .and_modify(|(old_result, old_minute)| { 687 | if state.minute < *old_minute { 688 | *old_result = result; 689 | *old_minute = state.minute; 690 | } 691 | }) 692 | .or_insert((result, state.minute)); 693 | } 694 | 695 | if state.minute == limit { 696 | let result = state.pack.geode; 697 | insert(memo, &state, result); 698 | return result; 699 | } 700 | 701 | let mut result = 0; 702 | 703 | if state.can_build_geode_robot(bp) { 704 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo)); 705 | } 706 | 707 | if state.can_build_obsidian_robot(bp) { 708 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo)); 709 | } 710 | 711 | if state.can_build_clay_robot(bp) { 712 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo)); 713 | } 714 | 715 | if state.can_build_ore_robot(bp) { 716 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo)); 717 | } 718 | 719 | result = result.max(solution(state.step(), bp, limit, memo)); 720 | 721 | insert(memo, &state, result); 722 | 723 | return result; 724 | } 725 | 726 | pub fn solve(bp: &Blueprint, limit: u8) -> u32 { 727 | let mut memo = HashMap::new(); 728 | solution(State::new(), bp, limit, &mut memo) 729 | } 730 | 731 | pub fn solve_stats(bp: &Blueprint, limit: u8) -> u32 { 732 | let mut memo = HashMap::new(); 733 | let result = solution(State::new(), bp, limit, &mut memo); 734 | 735 | println!("visited {} states", memo.len()); 736 | let mut max_ore = 0; 737 | let mut max_clay = 0; 738 | let mut max_obsidian = 0; 739 | let mut max_geode = 0; 740 | for (pack, _) in memo.iter() { 741 | max_ore = max_ore.max(pack.ore); 742 | max_clay = max_clay.max(pack.clay); 743 | max_obsidian = max_obsidian.max(pack.obsidian); 744 | max_geode = max_geode.max(pack.geode); 745 | } 746 | println!("max ore: {}", max_ore); 747 | println!("max clay: {}", max_clay); 748 | println!("max obsidian: {}", max_obsidian); 749 | println!("max geode: {}", max_geode); 750 | 751 | return result; 752 | } 753 | } 754 | } 755 | 756 | mod pack_u8 { 757 | use regex::Regex; 758 | 759 | use super::STATS; 760 | 761 | #[derive(Clone, Copy, Debug)] 762 | pub struct Blueprint { 763 | pub id: u8, 764 | pub ore_robot: u8, 765 | pub clay_robot: u8, 766 | pub obsidian_robot: (u8, u8), 767 | pub geode_robot: (u8, u8), 768 | } 769 | 770 | pub fn parse(input: &str) -> Vec { 771 | let mut result = Vec::with_capacity(128); 772 | 773 | let re = Regex::new(r"\d+").unwrap(); 774 | for line in input.lines() { 775 | let mut numbers = re.find_iter(line); 776 | let mut next = || -> u8 { 777 | let number = numbers.next().unwrap(); 778 | number.as_str().parse().unwrap() 779 | }; 780 | 781 | let id = next(); 782 | let ore_robot = next(); 783 | let clay_robot = next(); 784 | let obsidian_robot = (next(), next()); 785 | let geode_robot = (next(), next()); 786 | result.push(Blueprint { 787 | id, 788 | ore_robot, 789 | clay_robot, 790 | obsidian_robot, 791 | geode_robot, 792 | }); 793 | assert!(numbers.next().is_none()); 794 | } 795 | 796 | result 797 | } 798 | 799 | 800 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 801 | struct Pack { 802 | pub ore_robot: u8, 803 | pub clay_robot: u8, 804 | pub obsidian_robot: u8, 805 | pub geode_robot: u8, 806 | pub ore: u8, 807 | pub clay: u8, 808 | pub obsidian: u8, 809 | pub geode: u8, 810 | } 811 | 812 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 813 | struct State { 814 | pub minute: u8, 815 | pub pack: Pack, 816 | } 817 | 818 | impl State { 819 | pub fn new() -> Self { 820 | State { 821 | minute: 0, 822 | pack: Pack { 823 | ore_robot: 1, 824 | clay_robot: 0, 825 | obsidian_robot: 0, 826 | geode_robot: 0, 827 | ore: 0, 828 | clay: 0, 829 | obsidian: 0, 830 | geode: 0, 831 | }, 832 | } 833 | } 834 | 835 | #[inline] 836 | pub fn can_build_ore_robot(&self, bp: &Blueprint) -> bool { 837 | self.pack.ore >= bp.ore_robot 838 | } 839 | 840 | #[inline] 841 | pub fn can_build_clay_robot(&self, bp: &Blueprint) -> bool { 842 | self.pack.ore >= bp.clay_robot 843 | } 844 | 845 | #[inline] 846 | pub fn can_build_obsidian_robot(&self, bp: &Blueprint) -> bool { 847 | self.pack.ore >= bp.obsidian_robot.0 848 | && self.pack.clay >= bp.obsidian_robot.1 849 | } 850 | 851 | #[inline] 852 | pub fn can_build_geode_robot(&self, bp: &Blueprint) -> bool { 853 | self.pack.ore >= bp.geode_robot.0 854 | && self.pack.obsidian >= bp.geode_robot.1 855 | } 856 | 857 | #[inline] 858 | pub fn build_ore_robot(self, bp: &Blueprint) -> Self { 859 | let mut result = self; 860 | result.pack.ore -= bp.ore_robot; 861 | result.pack.ore_robot += 1; 862 | return result; 863 | } 864 | 865 | #[inline] 866 | pub fn build_clay_robot(self, bp: &Blueprint) -> Self { 867 | let mut result = self; 868 | result.pack.ore -= bp.clay_robot; 869 | result.pack.clay_robot += 1; 870 | return result; 871 | } 872 | 873 | #[inline] 874 | pub fn build_obsidian_robot(self, bp: &Blueprint) -> Self { 875 | let mut result = self; 876 | result.pack.ore -= bp.obsidian_robot.0; 877 | result.pack.clay -= bp.obsidian_robot.1; 878 | result.pack.obsidian_robot += 1; 879 | return result; 880 | } 881 | 882 | #[inline] 883 | pub fn build_geode_robot(self, bp: &Blueprint) -> Self { 884 | let mut result = self; 885 | result.pack.ore -= bp.geode_robot.0; 886 | result.pack.obsidian -= bp.geode_robot.1; 887 | result.pack.geode_robot += 1; 888 | return result; 889 | } 890 | 891 | #[inline] 892 | pub fn step(self) -> Self { 893 | let mut this = self; 894 | this.minute += 1; 895 | this.pack.ore += this.pack.ore_robot; 896 | this.pack.clay += this.pack.clay_robot; 897 | this.pack.obsidian += this.pack.obsidian_robot; 898 | this.pack.geode += this.pack.geode_robot; 899 | return this; 900 | } 901 | } 902 | 903 | 904 | pub mod v1 { 905 | use std::collections::HashMap; 906 | 907 | use super::{STATS, Blueprint, Pack, State}; 908 | 909 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap) -> u8 { 910 | STATS.with(|s| { 911 | s.memo_refs += 1; 912 | s.states_visited += 1; 913 | }); 914 | 915 | if let Some((result, minute)) = memo.get(&state.pack).copied() { 916 | if state.minute >= minute { 917 | STATS.with(|s| { s.memo_hits += 1 }); 918 | return result; 919 | } 920 | } 921 | 922 | #[inline] 923 | fn insert(memo: &mut HashMap, state: &State, result: u8) { 924 | // note: for some reason, this is equivalent to simply overriding the result. 925 | // ie: `memo.insert(pack, result)` 926 | // could be, cause we're writing the results on the way up (going backwards in time). 927 | // but not sure how that would work with "sibling branches". 928 | // in any case, this isn't measurably slower, so who cares. 929 | memo.entry(state.pack) 930 | .and_modify(|(old_result, old_minute)| { 931 | if state.minute < *old_minute { 932 | *old_result = result; 933 | *old_minute = state.minute; 934 | } 935 | }) 936 | .or_insert((result, state.minute)); 937 | } 938 | 939 | if state.minute == limit { 940 | let result = state.pack.geode; 941 | insert(memo, &state, result); 942 | return result; 943 | } 944 | 945 | let mut result = 0; 946 | 947 | if state.can_build_geode_robot(bp) { 948 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo)); 949 | } 950 | 951 | if state.can_build_obsidian_robot(bp) { 952 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo)); 953 | } 954 | 955 | if state.can_build_clay_robot(bp) { 956 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo)); 957 | } 958 | 959 | if state.can_build_ore_robot(bp) { 960 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo)); 961 | } 962 | 963 | result = result.max(solution(state.step(), bp, limit, memo)); 964 | 965 | insert(memo, &state, result); 966 | 967 | return result; 968 | } 969 | 970 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 971 | let mut memo = HashMap::new(); 972 | solution(State::new(), bp, limit, &mut memo) 973 | } 974 | } 975 | 976 | pub mod v2 { 977 | use std::collections::HashMap; 978 | 979 | use super::{STATS, Blueprint, State}; 980 | 981 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap) -> u8 { 982 | STATS.with(|s| { 983 | s.memo_refs += 1; 984 | s.states_visited += 1; 985 | }); 986 | 987 | let pack_64 = unsafe { core::mem::transmute(state.pack) }; 988 | 989 | if let Some((result, minute)) = memo.get(&pack_64).copied() { 990 | if state.minute >= minute { 991 | STATS.with(|s| { s.memo_hits += 1 }); 992 | return result; 993 | } 994 | } 995 | 996 | #[inline] 997 | fn insert(memo: &mut HashMap, state: &State, result: u8) { 998 | // note: for some reason, this is equivalent to simply overriding the result. 999 | // ie: `memo.insert(pack, result)` 1000 | // could be, cause we're writing the results on the way up (going backwards in time). 1001 | // but not sure how that would work with "sibling branches". 1002 | // in any case, this isn't measurably slower, so who cares. 1003 | memo.entry(unsafe { core::mem::transmute(state.pack) }) 1004 | .and_modify(|(old_result, old_minute)| { 1005 | if state.minute < *old_minute { 1006 | *old_result = result; 1007 | *old_minute = state.minute; 1008 | } 1009 | }) 1010 | .or_insert((result, state.minute)); 1011 | } 1012 | 1013 | if state.minute == limit { 1014 | let result = state.pack.geode; 1015 | insert(memo, &state, result); 1016 | return result; 1017 | } 1018 | 1019 | let mut result = 0; 1020 | 1021 | if state.can_build_geode_robot(bp) { 1022 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo)); 1023 | } 1024 | 1025 | if state.can_build_obsidian_robot(bp) { 1026 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo)); 1027 | } 1028 | 1029 | if state.can_build_clay_robot(bp) { 1030 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo)); 1031 | } 1032 | 1033 | if state.can_build_ore_robot(bp) { 1034 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo)); 1035 | } 1036 | 1037 | result = result.max(solution(state.step(), bp, limit, memo)); 1038 | 1039 | insert(memo, &state, result); 1040 | 1041 | return result; 1042 | } 1043 | 1044 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 1045 | let mut memo = HashMap::new(); 1046 | solution(State::new(), bp, limit, &mut memo) 1047 | } 1048 | } 1049 | 1050 | pub fn part_1 u8>(bps: &[Blueprint], f: F) { 1051 | super::STATS.with(|s| s.reset()); 1052 | let t0 = std::time::Instant::now(); 1053 | let mut result = 0; 1054 | for bp in bps { 1055 | let geodes = f(bp, 24); 1056 | // println!("geodes: {}", geodes); 1057 | result += bp.id as u32 * geodes as u32; 1058 | } 1059 | println!("part 1 result: {} in {:?}", result, t0.elapsed()); 1060 | super::STATS.with(|s| s.print()); 1061 | } 1062 | } 1063 | 1064 | mod thonk { 1065 | use regex::Regex; 1066 | 1067 | use super::STATS; 1068 | 1069 | #[derive(Clone, Copy, Debug)] 1070 | pub struct Blueprint { 1071 | pub id: u8, 1072 | pub ore_robot: u8, 1073 | pub clay_robot: u8, 1074 | pub obsidian_robot: (u8, u8), 1075 | pub geode_robot: (u8, u8), 1076 | pub max_ore_cost: u8, 1077 | } 1078 | 1079 | impl Blueprint { 1080 | #[inline] 1081 | fn max_ore_cost(&self) -> u8 { 1082 | self.max_ore_cost 1083 | } 1084 | 1085 | #[inline] 1086 | fn max_clay_cost(&self) -> u8 { 1087 | self.obsidian_robot.1 1088 | } 1089 | 1090 | #[inline] 1091 | fn max_obsidian_cost(&self) -> u8 { 1092 | self.geode_robot.1 1093 | } 1094 | } 1095 | 1096 | pub fn parse(input: &str) -> Vec { 1097 | let mut result = Vec::with_capacity(128); 1098 | 1099 | let re = Regex::new(r"\d+").unwrap(); 1100 | for line in input.lines() { 1101 | let mut numbers = re.find_iter(line); 1102 | let mut next = || -> u8 { 1103 | let number = numbers.next().unwrap(); 1104 | number.as_str().parse().unwrap() 1105 | }; 1106 | 1107 | let id = next(); 1108 | let ore_robot = next(); 1109 | let clay_robot = next(); 1110 | let obsidian_robot = (next(), next()); 1111 | let geode_robot = (next(), next()); 1112 | result.push(Blueprint { 1113 | id, 1114 | ore_robot, 1115 | clay_robot, 1116 | obsidian_robot, 1117 | geode_robot, 1118 | max_ore_cost: ore_robot.max(clay_robot).max(obsidian_robot.0).max(geode_robot.0), 1119 | }); 1120 | assert!(numbers.next().is_none()); 1121 | } 1122 | 1123 | result 1124 | } 1125 | 1126 | 1127 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1128 | struct Pack { 1129 | pub ore_robot: u8, 1130 | pub clay_robot: u8, 1131 | pub obsidian_robot: u8, 1132 | pub geode_robot: u8, 1133 | pub ore: u8, 1134 | pub clay: u8, 1135 | pub obsidian: u8, 1136 | pub geode: u8, 1137 | } 1138 | 1139 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1140 | struct State { 1141 | pub minute: u8, 1142 | pub pack: Pack, 1143 | } 1144 | 1145 | impl State { 1146 | pub fn new() -> Self { 1147 | State { 1148 | minute: 0, 1149 | pack: Pack { 1150 | ore_robot: 1, 1151 | clay_robot: 0, 1152 | obsidian_robot: 0, 1153 | geode_robot: 0, 1154 | ore: 0, 1155 | clay: 0, 1156 | obsidian: 0, 1157 | geode: 0, 1158 | }, 1159 | } 1160 | } 1161 | 1162 | #[inline] 1163 | pub fn can_build_ore_robot(&self, bp: &Blueprint) -> bool { 1164 | self.pack.ore >= bp.ore_robot 1165 | } 1166 | 1167 | #[inline] 1168 | pub fn can_build_clay_robot(&self, bp: &Blueprint) -> bool { 1169 | self.pack.ore >= bp.clay_robot 1170 | } 1171 | 1172 | #[inline] 1173 | pub fn can_build_obsidian_robot(&self, bp: &Blueprint) -> bool { 1174 | self.pack.ore >= bp.obsidian_robot.0 1175 | && self.pack.clay >= bp.obsidian_robot.1 1176 | } 1177 | 1178 | #[inline] 1179 | pub fn can_build_geode_robot(&self, bp: &Blueprint) -> bool { 1180 | self.pack.ore >= bp.geode_robot.0 1181 | && self.pack.obsidian >= bp.geode_robot.1 1182 | } 1183 | 1184 | #[inline] 1185 | pub fn build_ore_robot(self, bp: &Blueprint) -> Self { 1186 | let mut result = self; 1187 | result.pack.ore -= bp.ore_robot; 1188 | result.pack.ore_robot += 1; 1189 | return result; 1190 | } 1191 | 1192 | #[inline] 1193 | pub fn build_clay_robot(self, bp: &Blueprint) -> Self { 1194 | let mut result = self; 1195 | result.pack.ore -= bp.clay_robot; 1196 | result.pack.clay_robot += 1; 1197 | return result; 1198 | } 1199 | 1200 | #[inline] 1201 | pub fn build_obsidian_robot(self, bp: &Blueprint) -> Self { 1202 | let mut result = self; 1203 | result.pack.ore -= bp.obsidian_robot.0; 1204 | result.pack.clay -= bp.obsidian_robot.1; 1205 | result.pack.obsidian_robot += 1; 1206 | return result; 1207 | } 1208 | 1209 | #[inline] 1210 | pub fn build_geode_robot(self, bp: &Blueprint) -> Self { 1211 | let mut result = self; 1212 | result.pack.ore -= bp.geode_robot.0; 1213 | result.pack.obsidian -= bp.geode_robot.1; 1214 | result.pack.geode_robot += 1; 1215 | return result; 1216 | } 1217 | 1218 | #[inline] 1219 | pub fn step(self) -> Self { 1220 | let mut this = self; 1221 | this.minute += 1; 1222 | this.pack.ore += this.pack.ore_robot; 1223 | this.pack.clay += this.pack.clay_robot; 1224 | this.pack.obsidian += this.pack.obsidian_robot; 1225 | this.pack.geode += this.pack.geode_robot; 1226 | return this; 1227 | } 1228 | } 1229 | 1230 | 1231 | pub mod v1 { 1232 | use std::collections::HashMap; 1233 | 1234 | use super::{STATS, Blueprint, State}; 1235 | 1236 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap, max_result: &mut u8) -> u8 { 1237 | STATS.with(|s| { 1238 | s.memo_refs += 1; 1239 | s.states_visited += 1; 1240 | }); 1241 | 1242 | let pack_64 = unsafe { core::mem::transmute(state.pack) }; 1243 | 1244 | if let Some((result, minute)) = memo.get(&pack_64).copied() { 1245 | if state.minute >= minute { 1246 | STATS.with(|s| { s.memo_hits += 1 }); 1247 | 1248 | *max_result = (*max_result).max(result); 1249 | return result; 1250 | } 1251 | } 1252 | 1253 | #[inline] 1254 | fn insert(memo: &mut HashMap, state: &State, result: u8) { 1255 | // note: for some reason, this is equivalent to simply overriding the result. 1256 | // ie: `memo.insert(pack, result)` 1257 | // could be, cause we're writing the results on the way up (going backwards in time). 1258 | // but not sure how that would work with "sibling branches". 1259 | // in any case, this isn't measurably slower, so who cares. 1260 | memo.entry(unsafe { core::mem::transmute(state.pack) }) 1261 | .and_modify(|(old_result, old_minute)| { 1262 | if state.minute < *old_minute { 1263 | *old_result = result; 1264 | *old_minute = state.minute; 1265 | } 1266 | }) 1267 | .or_insert((result, state.minute)); 1268 | } 1269 | 1270 | // done? 1271 | if state.minute == limit { 1272 | let result = state.pack.geode; 1273 | insert(memo, &state, result); 1274 | *max_result = (*max_result).max(result); 1275 | return result; 1276 | } 1277 | 1278 | // can we even beat max_result anymore? 1279 | { 1280 | // number of turns remaining. 1281 | let remaining = (limit - state.minute) as u32; 1282 | 1283 | let max_yield = 1284 | // future yield of current geode bots. 1285 | remaining * state.pack.geode_robot as u32 1286 | // max future yield, if we build one geode bot 1287 | // on all future turns. 1288 | + remaining*(remaining-1)/2; 1289 | 1290 | if state.pack.geode as u32 + max_yield <= *max_result as u32 { 1291 | // doesn't matter what we insert, 1292 | // we already have a better result. 1293 | insert(memo, &state, 0); 1294 | return 0; 1295 | } 1296 | } 1297 | 1298 | let mut result = 0; 1299 | 1300 | if state.can_build_geode_robot(bp) { 1301 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo, max_result)); 1302 | } 1303 | 1304 | if state.can_build_obsidian_robot(bp) { 1305 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo, max_result)); 1306 | } 1307 | 1308 | if state.can_build_clay_robot(bp) { 1309 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo, max_result)); 1310 | } 1311 | 1312 | if state.can_build_ore_robot(bp) { 1313 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo, max_result)); 1314 | } 1315 | 1316 | result = result.max(solution(state.step(), bp, limit, memo, max_result)); 1317 | 1318 | insert(memo, &state, result); 1319 | 1320 | return result; 1321 | } 1322 | 1323 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 1324 | let mut memo = HashMap::new(); 1325 | let mut max_result = 0; 1326 | solution(State::new(), bp, limit, &mut memo, &mut max_result) 1327 | } 1328 | } 1329 | 1330 | pub mod v2 { 1331 | use std::collections::HashMap; 1332 | 1333 | use super::{STATS, Blueprint, State}; 1334 | 1335 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap, max_result: &mut u8) -> u8 { 1336 | STATS.with(|s| { 1337 | s.memo_refs += 1; 1338 | s.states_visited += 1; 1339 | }); 1340 | 1341 | let pack_64 = unsafe { core::mem::transmute(state.pack) }; 1342 | 1343 | if let Some((result, minute)) = memo.get(&pack_64).copied() { 1344 | if state.minute >= minute { 1345 | STATS.with(|s| { s.memo_hits += 1 }); 1346 | 1347 | *max_result = (*max_result).max(result); 1348 | return result; 1349 | } 1350 | } 1351 | 1352 | #[inline] 1353 | fn insert(memo: &mut HashMap, state: &State, result: u8) { 1354 | // note: for some reason, this is equivalent to simply overriding the result. 1355 | // ie: `memo.insert(pack, result)` 1356 | // could be, cause we're writing the results on the way up (going backwards in time). 1357 | // but not sure how that would work with "sibling branches". 1358 | // in any case, this isn't measurably slower, so who cares. 1359 | memo.entry(unsafe { core::mem::transmute(state.pack) }) 1360 | .and_modify(|(old_result, old_minute)| { 1361 | if state.minute < *old_minute { 1362 | *old_result = result; 1363 | *old_minute = state.minute; 1364 | } 1365 | }) 1366 | .or_insert((result, state.minute)); 1367 | } 1368 | 1369 | // done? 1370 | if state.minute == limit { 1371 | let result = state.pack.geode; 1372 | insert(memo, &state, result); 1373 | *max_result = (*max_result).max(result); 1374 | return result; 1375 | } 1376 | 1377 | // can we even beat max_result anymore? 1378 | { 1379 | // number of turns remaining. 1380 | let remaining = (limit - state.minute) as u32; 1381 | 1382 | let max_yield = 1383 | // future yield of current geode bots. 1384 | remaining * state.pack.geode_robot as u32 1385 | // max future yield, if we build one geode bot 1386 | // on all future turns. 1387 | + remaining*(remaining-1)/2; 1388 | 1389 | if state.pack.geode as u32 + max_yield <= *max_result as u32 { 1390 | // doesn't matter what we insert, 1391 | // we already have a better result. 1392 | insert(memo, &state, 0); 1393 | return 0; 1394 | } 1395 | } 1396 | 1397 | let mut result = 0; 1398 | 1399 | if state.can_build_geode_robot(bp) { 1400 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo, max_result)); 1401 | } 1402 | 1403 | if state.can_build_obsidian_robot(bp) { 1404 | // can only build one bot per turn. 1405 | // don't need more bots, if we're producing enough, 1406 | // so we can build the most expensive bot on each turn. 1407 | if state.pack.obsidian_robot < bp.max_obsidian_cost() { 1408 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo, max_result)); 1409 | } 1410 | } 1411 | 1412 | if state.can_build_clay_robot(bp) { 1413 | if state.pack.clay_robot < bp.max_clay_cost() { 1414 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo, max_result)); 1415 | } 1416 | } 1417 | 1418 | if state.can_build_ore_robot(bp) { 1419 | if state.pack.ore_robot < bp.max_ore_cost() { 1420 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo, max_result)); 1421 | } 1422 | } 1423 | 1424 | result = result.max(solution(state.step(), bp, limit, memo, max_result)); 1425 | 1426 | insert(memo, &state, result); 1427 | 1428 | return result; 1429 | } 1430 | 1431 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 1432 | let mut memo = HashMap::new(); 1433 | let mut max_result = 0; 1434 | solution(State::new(), bp, limit, &mut memo, &mut max_result) 1435 | } 1436 | } 1437 | 1438 | pub mod v3 { 1439 | use std::collections::HashMap; 1440 | 1441 | use super::{STATS, Blueprint, State}; 1442 | 1443 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap, max_result: &mut u8, 1444 | can_ore: bool, can_clay: bool, can_obsidian: bool 1445 | ) -> u8 { 1446 | STATS.with(|s| { 1447 | s.memo_refs += 1; 1448 | s.states_visited += 1; 1449 | }); 1450 | 1451 | let pack_64 = unsafe { core::mem::transmute(state.pack) }; 1452 | 1453 | if let Some((result, minute)) = memo.get(&pack_64).copied() { 1454 | if state.minute >= minute { 1455 | STATS.with(|s| { s.memo_hits += 1 }); 1456 | 1457 | *max_result = (*max_result).max(result); 1458 | return result; 1459 | } 1460 | } 1461 | 1462 | #[inline] 1463 | fn insert(memo: &mut HashMap, state: &State, result: u8) { 1464 | // note: for some reason, this is equivalent to simply overriding the result. 1465 | // ie: `memo.insert(pack, result)` 1466 | // could be, cause we're writing the results on the way up (going backwards in time). 1467 | // but not sure how that would work with "sibling branches". 1468 | // in any case, this isn't measurably slower, so who cares. 1469 | memo.entry(unsafe { core::mem::transmute(state.pack) }) 1470 | .and_modify(|(old_result, old_minute)| { 1471 | if state.minute < *old_minute { 1472 | *old_result = result; 1473 | *old_minute = state.minute; 1474 | } 1475 | }) 1476 | .or_insert((result, state.minute)); 1477 | } 1478 | 1479 | // done? 1480 | if state.minute == limit { 1481 | let result = state.pack.geode; 1482 | insert(memo, &state, result); 1483 | *max_result = (*max_result).max(result); 1484 | return result; 1485 | } 1486 | 1487 | // can we even beat max_result anymore? 1488 | { 1489 | // number of turns remaining. 1490 | let remaining = (limit - state.minute) as u32; 1491 | 1492 | let max_yield = 1493 | // future yield of current geode bots. 1494 | remaining * state.pack.geode_robot as u32 1495 | // max future yield, if we build one geode bot 1496 | // on all future turns. 1497 | + remaining*(remaining-1)/2; 1498 | 1499 | if state.pack.geode as u32 + max_yield <= *max_result as u32 { 1500 | // doesn't matter what we insert, 1501 | // we already have a better result. 1502 | insert(memo, &state, 0); 1503 | return 0; 1504 | } 1505 | } 1506 | 1507 | let mut result = 0; 1508 | 1509 | if state.can_build_geode_robot(bp) { 1510 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo, max_result, true, true, true)); 1511 | } 1512 | 1513 | let mut new_can_obsidian = true; 1514 | if state.can_build_obsidian_robot(bp) { 1515 | new_can_obsidian = false; 1516 | 1517 | // can only build one bot per turn. 1518 | // don't need more bots, if we're producing enough, 1519 | // so we can build the most expensive bot on each turn. 1520 | if can_obsidian && state.pack.obsidian_robot < bp.max_obsidian_cost() { 1521 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo, max_result, true, true, true)); 1522 | } 1523 | } 1524 | 1525 | let mut new_can_clay = true; 1526 | if state.can_build_clay_robot(bp) { 1527 | new_can_clay = false; 1528 | 1529 | if can_clay && state.pack.clay_robot < bp.max_clay_cost() { 1530 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo, max_result, true, true, true)); 1531 | } 1532 | } 1533 | 1534 | let mut new_can_ore = true; 1535 | if state.can_build_ore_robot(bp) { 1536 | new_can_ore = false; 1537 | 1538 | if can_ore && state.pack.ore_robot < bp.max_ore_cost() { 1539 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo, max_result, true, true, true)); 1540 | } 1541 | } 1542 | 1543 | result = result.max(solution(state.step(), bp, limit, memo, max_result, new_can_ore, new_can_clay, new_can_obsidian)); 1544 | 1545 | insert(memo, &state, result); 1546 | 1547 | return result; 1548 | } 1549 | 1550 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 1551 | let mut memo = HashMap::new(); 1552 | let mut max_result = 0; 1553 | solution(State::new(), bp, limit, &mut memo, &mut max_result, true, true, true) 1554 | } 1555 | } 1556 | 1557 | pub mod v4 { 1558 | use std::collections::HashMap; 1559 | 1560 | use super::{STATS, Blueprint, State}; 1561 | 1562 | fn solution(state: State, bp: &Blueprint, limit: u8, memo: &mut HashMap, max_result: &mut u8, 1563 | can_ore: bool, can_clay: bool, can_obsidian: bool 1564 | ) -> u8 { 1565 | STATS.with(|s| { 1566 | s.memo_refs += 1; 1567 | s.states_visited += 1; 1568 | }); 1569 | 1570 | let pack_64 = unsafe { core::mem::transmute(state.pack) }; 1571 | 1572 | if let Some((result, minute)) = memo.get(&pack_64).copied() { 1573 | if state.minute >= minute { 1574 | STATS.with(|s| { s.memo_hits += 1 }); 1575 | 1576 | *max_result = (*max_result).max(result); 1577 | return result; 1578 | } 1579 | } 1580 | 1581 | #[inline] 1582 | fn insert(memo: &mut HashMap, state: &State, result: u8) { 1583 | // note: for some reason, this is equivalent to simply overriding the result. 1584 | // ie: `memo.insert(pack, result)` 1585 | // could be, cause we're writing the results on the way up (going backwards in time). 1586 | // but not sure how that would work with "sibling branches". 1587 | // in any case, this isn't measurably slower, so who cares. 1588 | memo.entry(unsafe { core::mem::transmute(state.pack) }) 1589 | .and_modify(|(old_result, old_minute)| { 1590 | if state.minute < *old_minute { 1591 | *old_result = result; 1592 | *old_minute = state.minute; 1593 | } 1594 | }) 1595 | .or_insert((result, state.minute)); 1596 | } 1597 | 1598 | // done? 1599 | if state.minute == limit { 1600 | let result = state.pack.geode; 1601 | insert(memo, &state, result); 1602 | *max_result = (*max_result).max(result); 1603 | return result; 1604 | } 1605 | 1606 | // can we even beat max_result anymore? 1607 | { 1608 | // number of turns remaining. 1609 | let remaining = (limit - state.minute) as u32; 1610 | 1611 | let max_yield = 1612 | // future yield of current geode bots. 1613 | remaining * state.pack.geode_robot as u32 1614 | // max future yield, if we build one geode bot 1615 | // on all future turns. 1616 | + remaining*(remaining-1)/2; 1617 | 1618 | if state.pack.geode as u32 + max_yield <= *max_result as u32 { 1619 | // doesn't matter what we insert, 1620 | // we already have a better result. 1621 | insert(memo, &state, 0); 1622 | return 0; 1623 | } 1624 | } 1625 | 1626 | let mut result = 0; 1627 | 1628 | // building a geode bot is the best thing we can do. 1629 | // the proof is left as an exercise for the reader :P 1630 | if state.can_build_geode_robot(bp) { 1631 | result = result.max(solution(state.step().build_geode_robot(bp), bp, limit, memo, max_result, true, true, true)); 1632 | } 1633 | else { 1634 | let mut new_can_obsidian = true; 1635 | if state.can_build_obsidian_robot(bp) { 1636 | new_can_obsidian = false; 1637 | 1638 | // can only build one bot per turn. 1639 | // don't need more bots, if we're producing enough, 1640 | // so we can build the most expensive bot on each turn. 1641 | if can_obsidian && state.pack.obsidian_robot < bp.max_obsidian_cost() { 1642 | result = result.max(solution(state.step().build_obsidian_robot(bp), bp, limit, memo, max_result, true, true, true)); 1643 | } 1644 | } 1645 | 1646 | let mut new_can_clay = true; 1647 | if state.can_build_clay_robot(bp) { 1648 | new_can_clay = false; 1649 | 1650 | if can_clay && state.pack.clay_robot < bp.max_clay_cost() { 1651 | result = result.max(solution(state.step().build_clay_robot(bp), bp, limit, memo, max_result, true, true, true)); 1652 | } 1653 | } 1654 | 1655 | let mut new_can_ore = true; 1656 | if state.can_build_ore_robot(bp) { 1657 | new_can_ore = false; 1658 | 1659 | if can_ore && state.pack.ore_robot < bp.max_ore_cost() { 1660 | result = result.max(solution(state.step().build_ore_robot(bp), bp, limit, memo, max_result, true, true, true)); 1661 | } 1662 | } 1663 | 1664 | result = result.max(solution(state.step(), bp, limit, memo, max_result, new_can_ore, new_can_clay, new_can_obsidian)); 1665 | } 1666 | 1667 | insert(memo, &state, result); 1668 | 1669 | return result; 1670 | } 1671 | 1672 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 1673 | let mut memo = HashMap::new(); 1674 | let mut max_result = 0; 1675 | solution(State::new(), bp, limit, &mut memo, &mut max_result, true, true, true) 1676 | } 1677 | } 1678 | 1679 | pub mod v5 { 1680 | use super::{STATS, Blueprint, State}; 1681 | 1682 | fn solution(state: State, bp: &Blueprint, limit: u8, max_result: &mut u8, 1683 | can_ore: bool, can_clay: bool, can_obsidian: bool 1684 | ) { 1685 | STATS.with(|s| { 1686 | s.states_visited += 1; 1687 | }); 1688 | 1689 | // done? 1690 | if state.minute == limit { 1691 | let result = state.pack.geode; 1692 | *max_result = (*max_result).max(result); 1693 | return; 1694 | } 1695 | 1696 | // can we even beat max_result anymore? 1697 | { 1698 | // number of turns remaining. 1699 | let remaining = (limit - state.minute) as u32; 1700 | 1701 | let max_yield = 1702 | // future yield of current geode bots. 1703 | remaining * state.pack.geode_robot as u32 1704 | // max future yield, if we build one geode bot 1705 | // on all future turns. 1706 | + remaining*(remaining-1)/2; 1707 | 1708 | if state.pack.geode as u32 + max_yield <= *max_result as u32 { 1709 | return; 1710 | } 1711 | } 1712 | 1713 | // building a geode bot is the best thing we can do. 1714 | // the proof is left as an exercise for the reader :P 1715 | if state.can_build_geode_robot(bp) { 1716 | solution(state.step().build_geode_robot(bp), bp, limit, max_result, true, true, true); 1717 | } 1718 | else { 1719 | let mut new_can_obsidian = true; 1720 | if state.can_build_obsidian_robot(bp) { 1721 | new_can_obsidian = false; 1722 | 1723 | // can only build one bot per turn. 1724 | // don't need more bots, if we're producing enough, 1725 | // so we can build the most expensive bot on each turn. 1726 | if can_obsidian && state.pack.obsidian_robot < bp.max_obsidian_cost() { 1727 | solution(state.step().build_obsidian_robot(bp), bp, limit, max_result, true, true, true); 1728 | } 1729 | } 1730 | 1731 | let mut new_can_clay = true; 1732 | if state.can_build_clay_robot(bp) { 1733 | new_can_clay = false; 1734 | 1735 | if can_clay && state.pack.clay_robot < bp.max_clay_cost() { 1736 | solution(state.step().build_clay_robot(bp), bp, limit, max_result, true, true, true); 1737 | } 1738 | } 1739 | 1740 | let mut new_can_ore = true; 1741 | if state.can_build_ore_robot(bp) { 1742 | new_can_ore = false; 1743 | 1744 | if can_ore && state.pack.ore_robot < bp.max_ore_cost() { 1745 | solution(state.step().build_ore_robot(bp), bp, limit, max_result, true, true, true); 1746 | } 1747 | } 1748 | 1749 | solution(state.step(), bp, limit, max_result, new_can_ore, new_can_clay, new_can_obsidian); 1750 | } 1751 | } 1752 | 1753 | pub fn solve(bp: &Blueprint, limit: u8) -> u8 { 1754 | let mut max_result = 0; 1755 | solution(State::new(), bp, limit, &mut max_result, true, true, true); 1756 | max_result 1757 | } 1758 | } 1759 | 1760 | pub fn part_1 u8>(bps: &[Blueprint], f: F) { 1761 | super::STATS.with(|s| s.reset()); 1762 | let t0 = std::time::Instant::now(); 1763 | let mut result = 0; 1764 | for bp in bps { 1765 | let geodes = f(bp, 24); 1766 | // println!("geodes: {}", geodes); 1767 | result += bp.id as u32 * geodes as u32; 1768 | } 1769 | println!("part 1 result: {} in {:?}", result, t0.elapsed()); 1770 | super::STATS.with(|s| s.print()); 1771 | } 1772 | 1773 | pub fn part_1_ex u8>(bps: &[Blueprint], f: F, n: u8) { 1774 | super::STATS.with(|s| s.reset()); 1775 | let t0 = std::time::Instant::now(); 1776 | let mut result = 0; 1777 | for bp in bps { 1778 | let geodes = f(bp, n); 1779 | // println!("geodes: {}", geodes); 1780 | result += bp.id as u32 * geodes as u32; 1781 | } 1782 | //println!("part 1 n: {}, result: {} in {:?}", n, result, t0.elapsed()); 1783 | println!("({}, {}), ", n, t0.elapsed().as_secs_f64()); 1784 | super::STATS.with(|s| s.print()); 1785 | } 1786 | } 1787 | 1788 | 1789 | pub fn main() { 1790 | //let input = include_str!("d19-test.txt"); 1791 | let input = include_str!("d19-prod.txt"); 1792 | //let input = include_str!("d19-prod-2.txt"); 1793 | 1794 | // baseline 1795 | if 0==1 { 1796 | use baseline::*; 1797 | 1798 | let input = parse(input); 1799 | 1800 | //printer::tree(&input[0], 5); 1801 | 1802 | //part_1(&input, survivor::solve); 1803 | 1804 | //println!("baseline"); 1805 | //part_1(case, v2::solve); 1806 | println!("baseline"); 1807 | part_1(&input, v3::solve); 1808 | } 1809 | 1810 | // pack 1811 | if 0==1 { 1812 | use pack::*; 1813 | 1814 | let input = parse(input); 1815 | 1816 | println!("dp earlier hit"); 1817 | part_1(&input, v1::solve); 1818 | } 1819 | 1820 | // u8 1821 | if 0==1 { 1822 | use pack_u8::*; 1823 | 1824 | let input = parse(input); 1825 | 1826 | println!("u8"); 1827 | part_1(&input, v1::solve); 1828 | println!("u8 as u64"); 1829 | part_1(&input, v2::solve); 1830 | } 1831 | 1832 | // thonk 1833 | if 1==1 { 1834 | use thonk::*; 1835 | 1836 | let input = parse(input); 1837 | 1838 | println!("thonk max_result"); 1839 | part_1(&input, v1::solve); 1840 | println!("thonk enough bots"); 1841 | part_1(&input, v2::solve); 1842 | println!("thonk don't idle"); 1843 | part_1(&input, v3::solve); 1844 | println!("thonk geodes first"); 1845 | part_1(&input, v4::solve); 1846 | println!("thonk no memo"); 1847 | part_1(&input, v5::solve); 1848 | 1849 | for i in 10..100 { 1850 | part_1_ex(&input, v5::solve, i); 1851 | } 1852 | } 1853 | } 1854 | 1855 | 1856 | --------------------------------------------------------------------------------