├── 2024 ├── d01p1.rs ├── d01p2.rs ├── d02p1.rs ├── d02p2.rs ├── d03p1.rs ├── d03p2.rs ├── d04p1.rs ├── d04p2.rs ├── d05p1.rs ├── d05p2.rs ├── d06p1.rs ├── d06p2.rs ├── d07p1.rs ├── d07p2.rs ├── d08p1.rs ├── d08p2.rs ├── d09p1.rs ├── d09p2.rs ├── d10p1.rs ├── d10p2.rs ├── d11p1.rs ├── d11p2.rs ├── d12p1.rs ├── d12p2.rs ├── d13p1.rs ├── d13p2.rs ├── d14p1.rs ├── d14p2.rs ├── d15p1.rs ├── d15p2.rs ├── d16p1.rs ├── d16p2.rs ├── d17p1.rs ├── d17p2.rs ├── d18p1.rs ├── d18p2.rs ├── d19p1.rs ├── d19p2.rs ├── d20p1.rs ├── d20p2.rs ├── d21p1.rs ├── d21p2.rs ├── d22p1.rs ├── d22p2.rs ├── d23p1.rs ├── d23p2.rs ├── d24p1.rs ├── d24p2.rs ├── d25p1.rs └── script.sh ├── .gitignore ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ -------------------------------------------------------------------------------- /2024/d01p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: doge 2 | #![feature(portable_simd)] 3 | use std::simd::Simd; 4 | 5 | const N: usize = 1000; 6 | const LANES: usize = 8; // SIMD width for `Simd` 7 | 8 | #[inline(always)] 9 | unsafe fn radix_sort_u17(arr: &mut [u32; N]) { 10 | let mut cnt_lo: [u16; 256] = [0; 256]; 11 | let mut cnt_hi: [u16; 512] = [0; 512]; 12 | 13 | let len = arr.len(); 14 | let chunks = len / LANES * LANES; 15 | 16 | // Counting frequencies using SIMD 17 | for i in (0..chunks).step_by(LANES) { 18 | // Load 8 u32 elements into a SIMD vector 19 | let data = Simd::::from_slice(&arr[i..i + LANES]); 20 | 21 | // Extract lower 8 bits 22 | let lo = data & Simd::splat(0xFFu32); 23 | // Extract higher 9 bits 24 | let hi = data >> Simd::splat(8u32); 25 | 26 | // Update counts arrays 27 | for j in 0..LANES { 28 | *cnt_lo.get_unchecked_mut(lo[j] as usize) += 1; 29 | *cnt_hi.get_unchecked_mut(hi[j] as usize) += 1; 30 | } 31 | } 32 | 33 | // Process any remaining elements 34 | for &x in &arr[chunks..] { 35 | *cnt_lo.get_unchecked_mut((x & 0xFF) as usize) += 1; 36 | *cnt_hi.get_unchecked_mut((x >> 8) as usize) += 1; 37 | } 38 | 39 | // Compute exclusive prefix sums 40 | { 41 | let mut sum = 0u16; 42 | for count in cnt_lo.iter_mut() { 43 | let temp = *count; 44 | *count = sum; 45 | sum += temp; 46 | } 47 | } 48 | { 49 | let mut sum = 0u16; 50 | for count in cnt_hi.iter_mut() { 51 | let temp = *count; 52 | *count = sum; 53 | sum += temp; 54 | } 55 | } 56 | 57 | // First redistribution pass (lower 8 bits) 58 | let mut buf = [0u32; N]; 59 | { 60 | for &x in arr.iter() { 61 | let idx = (x & 0xFF) as usize; 62 | let dest = cnt_lo.get_unchecked_mut(idx); 63 | *buf.get_unchecked_mut(*dest as usize) = x; 64 | *dest += 1; 65 | } 66 | } 67 | 68 | // Second redistribution pass (higher 9 bits) 69 | { 70 | for &x in buf.iter() { 71 | let idx = (x >> 8) as usize; 72 | let dest = cnt_hi.get_unchecked_mut(idx); 73 | *arr.get_unchecked_mut(*dest as usize) = x; 74 | *dest += 1; 75 | } 76 | } 77 | } 78 | 79 | #[inline(always)] 80 | fn parse_5b(s: &[u8]) -> u32 { 81 | // Optimize by unrolling the loop and using direct subtraction to convert ASCII digits to numbers 82 | unsafe { 83 | let s0 = *s.get_unchecked(0) as u32; 84 | let s1 = *s.get_unchecked(1) as u32; 85 | let s2 = *s.get_unchecked(2) as u32; 86 | let s3 = *s.get_unchecked(3) as u32; 87 | let s4 = *s.get_unchecked(4) as u32; 88 | 89 | (s0 * 10000 + s1 * 1000 + s2 * 100 + s3 * 10 + s4) - 533328 90 | } 91 | } 92 | 93 | pub fn run(s: &[u8]) -> i64 { 94 | let mut left = [0; N]; 95 | let mut right = [0; N]; 96 | 97 | for i in 0..N { 98 | left[i] = parse_5b(&s[i * 14..]); 99 | right[i] = parse_5b(&s[i * 14 + 8..]); 100 | } 101 | 102 | unsafe { 103 | radix_sort_u17(&mut left); 104 | radix_sort_u17(&mut right); 105 | } 106 | 107 | left.iter() 108 | .zip(&right) 109 | .map(|(a, &b)| a.abs_diff(b)) 110 | .sum::() as i64 111 | } 112 | 113 | #[test] 114 | fn d1p1() { 115 | assert_eq!(run(include_bytes!("./../input/day1.txt")), 2192892); 116 | } 117 | -------------------------------------------------------------------------------- /2024/d01p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: doge 2 | const N: usize = 1000; 3 | 4 | #[inline(always)] 5 | fn parse_5b(s: &[u8]) -> u32 { 6 | // Optimize by unrolling the loop and using direct subtraction to convert ASCII digits to numbers 7 | unsafe { 8 | let s0 = *s.get_unchecked(0) as u32; 9 | let s1 = *s.get_unchecked(1) as u32; 10 | let s2 = *s.get_unchecked(2) as u32; 11 | let s3 = *s.get_unchecked(3) as u32; 12 | let s4 = *s.get_unchecked(4) as u32; 13 | 14 | (s0 * 10000 + s1 * 1000 + s2 * 100 + s3 * 10 + s4) - 533328 15 | } 16 | } 17 | 18 | pub fn run(s: &[u8]) -> i64 { 19 | let mut assoc = [0; 2048]; 20 | 21 | for i in 0..N { 22 | let right = parse_5b(&s[i * 14 + 8..]); 23 | let mut h = right & 2047; 24 | loop { 25 | let entry = &mut assoc[h as usize]; 26 | if *entry == 0 { 27 | *entry = right | 1 << 20; 28 | break; 29 | } 30 | if (*entry & 0xfffff) == right { 31 | *entry += 1 << 20; 32 | break; 33 | } 34 | h = (h + 1) & 2047; 35 | } 36 | } 37 | 38 | let mut answer = 0; 39 | 40 | for i in 0..N { 41 | let left = parse_5b(&s[i * 14..]); 42 | let mut h = left & 2047; 43 | loop { 44 | let entry = assoc[h as usize]; 45 | if entry == 0 { 46 | break; 47 | } 48 | if (entry & 0xfffff) == left { 49 | answer += left * (entry >> 20); 50 | } 51 | h = (h + 1) & 2047; 52 | } 53 | } 54 | answer as i64 55 | } 56 | 57 | #[test] 58 | fn d1p2() { 59 | assert_eq!(run(include_bytes!("./../input/day1.txt")), 22962826); 60 | } 61 | -------------------------------------------------------------------------------- /2024/d03p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: ameo 2 | #![feature(array_chunks, array_windows, duration_constructors, portable_simd)] 3 | 4 | use std::{ 5 | fmt::Display, 6 | simd::{cmp::SimdPartialEq, u8x32, u8x64}, 7 | }; 8 | 9 | // pub const INPUT: &'static [u8] = include_bytes!("../inputs/day3.txt"); 10 | 11 | fn parse_digit(c: u8) -> usize { 12 | (c - 48) as usize 13 | } 14 | 15 | #[inline(always)] 16 | fn add_num(digits: &[usize]) -> usize { 17 | match digits.len() { 18 | 0 => unreachable!(), 19 | 1 => unsafe { *digits.get_unchecked(0) }, 20 | 2 => 10 * unsafe { *digits.get_unchecked(0) } + unsafe { *digits.get_unchecked(1) }, 21 | 3 => { 22 | 100 * unsafe { *digits.get_unchecked(0) } 23 | + 10 * unsafe { *digits.get_unchecked(1) } 24 | + unsafe { *digits.get_unchecked(2) } 25 | } 26 | _ => unreachable!(), 27 | } 28 | } 29 | 30 | const MUL: [u8; 4] = ['m' as u8, 'u' as u8, 'l' as u8, '(' as u8]; 31 | const DONT: [u8; 7] = [ 32 | 'd' as u8, 'o' as u8, 'n' as u8, '\'' as u8, 't' as u8, '(' as u8, ')' as u8, 33 | ]; 34 | const DO: [u8; 4] = ['d' as u8, 'o' as u8, '(' as u8, ')' as u8]; 35 | // shortest valid mul is `mul(1,1)` so 8 chars 36 | const MIN_VALID_MUL_LEN: usize = 8; 37 | 38 | pub fn parse_and_compute(input: &[u8]) -> usize { 39 | let mut sum = 0usize; 40 | let mut do_state = true; 41 | let mut char_ix = 0usize; 42 | 43 | 'outer: loop { 44 | if char_ix >= input.len() - MIN_VALID_MUL_LEN { 45 | return sum; 46 | } 47 | 48 | // For part 2, when the "do" mode is set to "don't", we only care about finding `d` characters. 49 | // 50 | // Since d's are so much sparser in the inputs than m's, there's a decent chance it will be more 51 | // than 32 chars ahead and the overhead of reading further tends to be worth it. 52 | if ENABLE_DO_STATE && !do_state && char_ix < input.len() - (64 + 1) { 53 | let vector = unsafe { 54 | u8x64::from_slice(std::slice::from_raw_parts(input.as_ptr().add(char_ix), 64)) 55 | }; 56 | 57 | let mask = vector.simd_eq(u8x64::splat('d' as u8)); 58 | let hit_ix = match mask.first_set() { 59 | Some(hit_ix) => hit_ix, 60 | None => { 61 | // no hit in the entire window; skip it completely and move on to the next 62 | char_ix += 64; 63 | continue; 64 | } 65 | }; 66 | 67 | char_ix += hit_ix; 68 | } 69 | // Try to find the first relavant start character in the input by checking 32 at a time and then 70 | // selecting the index of the first match 71 | else if char_ix < input.len() - (32 + 1) { 72 | let vector = unsafe { 73 | u8x32::from_slice(std::slice::from_raw_parts(input.as_ptr().add(char_ix), 32)) 74 | }; 75 | 76 | // Same as before, if we're keeping track of do/don't state and the do flag is not set, we can 77 | // avoid checking for `m` characters entirely and just scan for the next `d`. 78 | let combined_mask = if ENABLE_DO_STATE { 79 | let d_mask = vector.simd_eq(u8x32::splat('d' as u8)); 80 | if !do_state { 81 | d_mask 82 | } else { 83 | let m_mask = vector.simd_eq(u8x32::splat('m' as u8)); 84 | m_mask | d_mask 85 | } 86 | } else { 87 | vector.simd_eq(u8x32::splat('m' as u8)) 88 | }; 89 | let hit_ix = match combined_mask.first_set() { 90 | Some(hit_ix) => hit_ix, 91 | None => { 92 | // no hit in the entire window; skip it completely and move on to the next 93 | char_ix += 32; 94 | continue; 95 | } 96 | }; 97 | 98 | char_ix += hit_ix; 99 | } 100 | // We use char-by-char checks for the remainder of the window 101 | else { 102 | let mut c = unsafe { *input.get_unchecked(char_ix) }; 103 | while c != 'm' as u8 && (!ENABLE_DO_STATE || c != 'd' as u8) { 104 | char_ix += 1; 105 | if char_ix >= input.len() { 106 | return sum; 107 | } 108 | c = unsafe { *input.get_unchecked(char_ix) }; 109 | } 110 | } 111 | 112 | // don't bother parsing out this mul if we're not doing 113 | if (!ENABLE_DO_STATE || do_state) && input.get(char_ix..char_ix + MUL.len()) == Some(&MUL) { 114 | char_ix += MUL.len(); 115 | 116 | // try to fastpath consume nums 117 | let first_num; 118 | let second_num; 119 | 120 | let mut d0; 121 | let mut d1; 122 | let mut d2; 123 | 124 | // first char after `mul(` must be a digit 125 | let mut c = unsafe { *input.get_unchecked(char_ix) }; 126 | char_ix += 1; 127 | if c >= '0' as u8 && c <= '9' as u8 { 128 | d0 = parse_digit(c); 129 | } else { 130 | continue; 131 | } 132 | 133 | // next char `mul(1_` can be either digit or comma 134 | c = unsafe { *input.get_unchecked(char_ix) }; 135 | char_ix += 1; 136 | 137 | if c >= '0' as u8 && c <= '9' as u8 { 138 | d1 = parse_digit(c); 139 | 140 | c = unsafe { *input.get_unchecked(char_ix) }; 141 | char_ix += 1; 142 | 143 | // next char `mul(12_` can also be either digit or comma 144 | if c >= '0' as u8 && c <= '9' as u8 { 145 | d2 = parse_digit(c); 146 | 147 | c = unsafe { *input.get_unchecked(char_ix) }; 148 | char_ix += 1; 149 | 150 | // next char `mul(123_` MUST be a comma if this mul is valid 151 | if c != ',' as u8 { 152 | continue 'outer; 153 | } 154 | 155 | first_num = add_num(&[d0, d1, d2]); 156 | } else if c == ',' as u8 { 157 | first_num = add_num(&[d0, d1]); 158 | } else { 159 | continue 'outer; 160 | } 161 | } else if c == ',' as u8 { 162 | first_num = d0; 163 | } else { 164 | continue 'outer; 165 | } 166 | 167 | c = unsafe { *input.get_unchecked(char_ix) }; 168 | char_ix += 1; 169 | 170 | // next character `mul(123,_` must be a digit 171 | if c >= '0' as u8 && c <= '9' as u8 { 172 | d0 = parse_digit(c); 173 | } else { 174 | continue; 175 | } 176 | 177 | // finish parsing second arg. Assuming that args have at most 3 chars, so take at most two 178 | // more digits followed by a `)` 179 | 180 | c = unsafe { *input.get_unchecked(char_ix) }; 181 | char_ix += 1; 182 | 183 | // next character `mul(123,1_` can be either digit or `)` 184 | if c >= '0' as u8 && c <= '9' as u8 { 185 | d1 = parse_digit(c); 186 | 187 | c = unsafe { *input.get_unchecked(char_ix) }; 188 | char_ix += 1; 189 | 190 | // next char `mul(123,12_` can also be either digit or `)` 191 | if c >= '0' as u8 && c <= '9' as u8 { 192 | d2 = parse_digit(c); 193 | 194 | c = unsafe { *input.get_unchecked(char_ix) }; 195 | char_ix += 1; 196 | 197 | // next char `mul(123,123_` MUST be a `)` if this mul is valid 198 | if c != ')' as u8 { 199 | continue 'outer; 200 | } 201 | 202 | second_num = add_num(&[d0, d1, d2]); 203 | } else if c == ')' as u8 { 204 | second_num = add_num(&[d0, d1]); 205 | } else { 206 | continue 'outer; 207 | } 208 | } else if c == ')' as u8 { 209 | second_num = d0; 210 | } else { 211 | continue 'outer; 212 | } 213 | 214 | sum += first_num * second_num; 215 | } else if ENABLE_DO_STATE 216 | && do_state 217 | && input.get(char_ix..char_ix + DONT.len()) == Some(&DONT) 218 | { 219 | do_state = false; 220 | char_ix += DONT.len(); 221 | } else if ENABLE_DO_STATE 222 | && !do_state 223 | && input.get(char_ix..char_ix + DO.len()) == Some(&DO) 224 | { 225 | do_state = true; 226 | char_ix += DO.len(); 227 | } else { 228 | char_ix += 1; 229 | } 230 | } 231 | } 232 | 233 | // pub fn solve() { 234 | // let out = parse_and_compute::(INPUT); 235 | // println!("Part 1: {out}"); 236 | 237 | // let out = parse_and_compute::(INPUT); 238 | // println!("Part 2: {out}"); 239 | // } 240 | 241 | pub fn run(input: &[u8]) -> impl Display { 242 | parse_and_compute::(input) 243 | } 244 | -------------------------------------------------------------------------------- /2024/d04p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | 5 | use std::ops::Range; 6 | use std::simd::cmp::SimdPartialEq; 7 | use std::simd::u8x64; 8 | 9 | pub fn run(input: &str) -> i64 { 10 | part1(input) as i64 11 | } 12 | 13 | pub fn part1(input: &str) -> u32 { 14 | let input = input.as_bytes(); 15 | 16 | const R1: Range = 0..64; 17 | const R2: Range = 61..125; 18 | const R3: Range = (140 - 64)..140; 19 | const M2V: u64 = u64::MAX & (!0b111) & ((1 << 61) - 1); 20 | const M3: u64 = !((1u64 << (64 - (140 - 122))) - 1); 21 | 22 | #[inline(always)] 23 | fn check_horiz(mut x: u64, m: u64, a: u64, mut s: u64) -> u32 { 24 | if N == 3 { 25 | x &= M3; 26 | s &= M3; 27 | } 28 | let mut tot = 0; 29 | tot |= x & (m << 1) & (a << 2) & (s << 3); 30 | tot |= s & (a << 1) & (m << 2) & (x << 3); 31 | tot.count_ones() 32 | } 33 | 34 | #[inline(always)] 35 | fn check_vert( 36 | pppx: u64, 37 | ppps: u64, 38 | ppm: u64, 39 | ppa: u64, 40 | pm: u64, 41 | pa: u64, 42 | mut cx: u64, 43 | mut cs: u64, 44 | ) -> u32 { 45 | if N == 2 { 46 | cx &= M2V; 47 | cs &= M2V; 48 | } else if N == 3 { 49 | cx &= M3; 50 | cs &= M3; 51 | } 52 | 53 | ((pppx & ppm & pa & cs) | (ppps & ppa & pm & cx)).count_ones() 54 | } 55 | 56 | #[inline(always)] 57 | fn check_diag( 58 | mut pppx: u64, 59 | mut ppps: u64, 60 | ppm: u64, 61 | ppa: u64, 62 | pm: u64, 63 | pa: u64, 64 | mut cx: u64, 65 | mut cs: u64, 66 | ) -> u32 { 67 | if N == 3 { 68 | pppx &= M3; 69 | ppps &= M3; 70 | cx &= M3; 71 | cs &= M3; 72 | } 73 | 74 | let mut dl = 0; 75 | dl |= cx & (pm << 1) & (ppa << 2) & (ppps << 3); 76 | dl |= cs & (pa << 1) & (ppm << 2) & (pppx << 3); 77 | 78 | let mut dr = 0; 79 | dr |= cx & (pm >> 1) & (ppa >> 2) & (ppps >> 3); 80 | dr |= cs & (pa >> 1) & (ppm >> 2) & (pppx >> 3); 81 | 82 | dl.count_ones() + dr.count_ones() 83 | } 84 | 85 | let mut count = 0; 86 | 87 | let line = &input[0 * 141..][..140]; 88 | let ppp1 = u8x64::from_slice(&line[R1]); 89 | let mut pppx1 = ppp1.simd_eq(u8x64::splat(b'X')).to_bitmask(); 90 | let pppm1 = ppp1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 91 | let pppa1 = ppp1.simd_eq(u8x64::splat(b'A')).to_bitmask(); 92 | let mut ppps1 = ppp1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 93 | count += check_horiz::<1>(pppx1, pppm1, pppa1, ppps1); 94 | let ppp2 = u8x64::from_slice(&line[R2]); 95 | let mut pppx2 = ppp2.simd_eq(u8x64::splat(b'X')).to_bitmask(); 96 | let pppm2 = ppp2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 97 | let pppa2 = ppp2.simd_eq(u8x64::splat(b'A')).to_bitmask(); 98 | let mut ppps2 = ppp2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 99 | count += check_horiz::<2>(pppx2, pppm2, pppa2, ppps2); 100 | let ppp3 = u8x64::from_slice(&line[R3]); 101 | let mut pppx3 = ppp3.simd_eq(u8x64::splat(b'X')).to_bitmask(); 102 | let pppm3 = ppp3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 103 | let pppa3 = ppp3.simd_eq(u8x64::splat(b'A')).to_bitmask(); 104 | let mut ppps3 = ppp3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 105 | count += check_horiz::<3>(pppx3, pppm3, pppa3, ppps3); 106 | 107 | let line = &input[1 * 141..][..140]; 108 | let pp1 = u8x64::from_slice(&line[R1]); 109 | let mut ppx1 = pp1.simd_eq(u8x64::splat(b'X')).to_bitmask(); 110 | let mut ppm1 = pp1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 111 | let mut ppa1 = pp1.simd_eq(u8x64::splat(b'A')).to_bitmask(); 112 | let mut pps1 = pp1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 113 | count += check_horiz::<1>(ppx1, ppm1, ppa1, pps1); 114 | let pp2 = u8x64::from_slice(&line[R2]); 115 | let mut ppx2 = pp2.simd_eq(u8x64::splat(b'X')).to_bitmask(); 116 | let mut ppm2 = pp2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 117 | let mut ppa2 = pp2.simd_eq(u8x64::splat(b'A')).to_bitmask(); 118 | let mut pps2 = pp2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 119 | count += check_horiz::<2>(ppx2, ppm2, ppa2, pps2); 120 | let pp3 = u8x64::from_slice(&line[R3]); 121 | let mut ppx3 = pp3.simd_eq(u8x64::splat(b'X')).to_bitmask(); 122 | let mut ppm3 = pp3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 123 | let mut ppa3 = pp3.simd_eq(u8x64::splat(b'A')).to_bitmask(); 124 | let mut pps3 = pp3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 125 | count += check_horiz::<3>(ppx3, ppm3, ppa3, pps3); 126 | 127 | let line = &input[2 * 141..][..140]; 128 | let p1 = u8x64::from_slice(&line[R1]); 129 | let mut px1 = p1.simd_eq(u8x64::splat(b'X')).to_bitmask(); 130 | let mut pm1 = p1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 131 | let mut pa1 = p1.simd_eq(u8x64::splat(b'A')).to_bitmask(); 132 | let mut ps1 = p1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 133 | count += check_horiz::<1>(px1, pm1, pa1, ps1); 134 | let p2 = u8x64::from_slice(&line[R2]); 135 | let mut px2 = p2.simd_eq(u8x64::splat(b'X')).to_bitmask(); 136 | let mut pm2 = p2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 137 | let mut pa2 = p2.simd_eq(u8x64::splat(b'A')).to_bitmask(); 138 | let mut ps2 = p2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 139 | count += check_horiz::<2>(px2, pm2, pa2, ps2); 140 | let p3 = u8x64::from_slice(&line[R3]); 141 | let mut px3 = p3.simd_eq(u8x64::splat(b'X')).to_bitmask(); 142 | let mut pm3 = p3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 143 | let mut pa3 = p3.simd_eq(u8x64::splat(b'A')).to_bitmask(); 144 | let mut ps3 = p3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 145 | count += check_horiz::<3>(px3, pm3, pa3, ps3); 146 | 147 | for line in input[3 * 141..].chunks_exact(141) { 148 | let c1 = u8x64::from_slice(&line[R1]); 149 | let cx1 = c1.simd_eq(u8x64::splat(b'X')).to_bitmask(); 150 | let cm1 = c1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 151 | let ca1 = c1.simd_eq(u8x64::splat(b'A')).to_bitmask(); 152 | let cs1 = c1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 153 | count += check_horiz::<1>(cx1, cm1, ca1, cs1); 154 | count += check_vert::<1>(pppx1, ppps1, ppm1, ppa1, pm1, pa1, cx1, cs1); 155 | count += check_diag::<1>(pppx1, ppps1, ppm1, ppa1, pm1, pa1, cx1, cs1); 156 | (pppx1, ppx1, px1) = (ppx1, px1, cx1); 157 | (ppm1, pm1) = (pm1, cm1); 158 | (ppa1, pa1) = (pa1, ca1); 159 | (ppps1, pps1, ps1) = (pps1, ps1, cs1); 160 | 161 | let c2 = u8x64::from_slice(&line[R2]); 162 | let cx2 = c2.simd_eq(u8x64::splat(b'X')).to_bitmask(); 163 | let cm2 = c2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 164 | let ca2 = c2.simd_eq(u8x64::splat(b'A')).to_bitmask(); 165 | let cs2 = c2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 166 | count += check_horiz::<2>(cx2, cm2, ca2, cs2); 167 | count += check_vert::<2>(pppx2, ppps2, ppm2, ppa2, pm2, pa2, cx2, cs2); 168 | count += check_diag::<2>(pppx2, ppps2, ppm2, ppa2, pm2, pa2, cx2, cs2); 169 | (pppx2, ppx2, px2) = (ppx2, px2, cx2); 170 | (ppm2, pm2) = (pm2, cm2); 171 | (ppa2, pa2) = (pa2, ca2); 172 | (ppps2, pps2, ps2) = (pps2, ps2, cs2); 173 | 174 | let c3 = u8x64::from_slice(&line[R3]); 175 | let cx3 = c3.simd_eq(u8x64::splat(b'X')).to_bitmask(); 176 | let cm3 = c3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 177 | let ca3 = c3.simd_eq(u8x64::splat(b'A')).to_bitmask(); 178 | let cs3 = c3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 179 | count += check_horiz::<3>(cx3, cm3, ca3, cs3); 180 | count += check_vert::<3>(pppx3, ppps3, ppm3, ppa3, pm3, pa3, cx3, cs3); 181 | count += check_diag::<3>(pppx3, ppps3, ppm3, ppa3, pm3, pa3, cx3, cs3); 182 | (pppx3, ppx3, px3) = (ppx3, px3, cx3); 183 | (ppm3, pm3) = (pm3, cm3); 184 | (ppa3, pa3) = (pa3, ca3); 185 | (ppps3, pps3, ps3) = (pps3, ps3, cs3); 186 | } 187 | 188 | count 189 | } 190 | 191 | pub fn part2(input: &str) -> u32 { 192 | let input = input.as_bytes(); 193 | 194 | let mut count = 0; 195 | 196 | const R1: Range = 0..64; 197 | const R2: Range = 62..126; 198 | const R3: Range = (140 - 64)..140; 199 | const MASK_A3: u64 = !((1u64 << (64 - (140 - 125))) - 1); 200 | 201 | let line = &input[..140]; 202 | let pp1 = u8x64::from_slice(&line[R1]); 203 | let mut ppm1 = pp1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 204 | let mut pps1 = pp1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 205 | let pp2 = u8x64::from_slice(&line[R2]); 206 | let mut ppm2 = pp2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 207 | let mut pps2 = pp2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 208 | let pp3 = u8x64::from_slice(&line[R3]); 209 | let mut ppm3 = pp3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 210 | let mut pps3 = pp3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 211 | 212 | let line = &input[141..][..140]; 213 | let p1 = u8x64::from_slice(&line[R1]); 214 | let mut pm1 = p1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 215 | let mut ps1 = p1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 216 | let mut pa1 = p1.simd_eq(u8x64::splat(b'A')).to_bitmask(); 217 | let p2 = u8x64::from_slice(&line[R2]); 218 | let mut pm2 = p2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 219 | let mut ps2 = p2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 220 | let mut pa2 = p2.simd_eq(u8x64::splat(b'A')).to_bitmask(); 221 | let p3 = u8x64::from_slice(&line[R3]); 222 | let mut pm3 = p3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 223 | let mut ps3 = p3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 224 | let mut pa3 = p3.simd_eq(u8x64::splat(b'A')).to_bitmask(); 225 | 226 | #[inline(always)] 227 | fn check_chunk(ppm: u64, pps: u64, pa: u64, cm: u64, cs: u64) -> u32 { 228 | let mut tot = 0; 229 | tot |= (ppm >> 1) & (pps << 1) & pa & (cm >> 1) & (cs << 1); 230 | tot |= (ppm >> 1) & (ppm << 1) & pa & (cs >> 1) & (cs << 1); 231 | tot |= (pps >> 1) & (ppm << 1) & pa & (cs >> 1) & (cm << 1); 232 | tot |= (pps >> 1) & (pps << 1) & pa & (cm >> 1) & (cm << 1); 233 | tot.count_ones() 234 | } 235 | 236 | for line in input[141 * 2..].chunks_exact(141) { 237 | let c1 = u8x64::from_slice(&line[R1]); 238 | let cm1 = c1.simd_eq(u8x64::splat(b'M')).to_bitmask(); 239 | let cs1 = c1.simd_eq(u8x64::splat(b'S')).to_bitmask(); 240 | let ca1 = c1.simd_eq(u8x64::splat(b'A')).to_bitmask(); 241 | count += check_chunk(ppm1, pps1, pa1, cm1, cs1); 242 | (ppm1, pps1, pm1, ps1, pa1) = (pm1, ps1, cm1, cs1, ca1); 243 | 244 | let c2 = u8x64::from_slice(&line[R2]); 245 | let cm2 = c2.simd_eq(u8x64::splat(b'M')).to_bitmask(); 246 | let cs2 = c2.simd_eq(u8x64::splat(b'S')).to_bitmask(); 247 | let ca2 = c2.simd_eq(u8x64::splat(b'A')).to_bitmask(); 248 | count += check_chunk(ppm2, pps2, pa2, cm2, cs2); 249 | (ppm2, pps2, pm2, ps2, pa2) = (pm2, ps2, cm2, cs2, ca2); 250 | 251 | let c3 = u8x64::from_slice(&line[R3]); 252 | let cm3 = c3.simd_eq(u8x64::splat(b'M')).to_bitmask(); 253 | let cs3 = c3.simd_eq(u8x64::splat(b'S')).to_bitmask(); 254 | let ca3 = c3.simd_eq(u8x64::splat(b'A')).to_bitmask(); 255 | count += check_chunk(ppm3, pps3, pa3 & MASK_A3, cm3, cs3); 256 | (ppm3, pps3, pm3, ps3, pa3) = (pm3, ps3, cm3, cs3, ca3); 257 | } 258 | 259 | count 260 | } 261 | -------------------------------------------------------------------------------- /2024/d04p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: bendn 2 | #![feature(portable_simd)] 3 | // type bitset = bitvec::BitArr!(for 140, in u64); 4 | // pub mod set { 5 | // uint::construct_uint! {pub struct bitset(3); } 6 | // } 7 | // use set::bitset; 8 | use cmp::SimdPartialEq; 9 | use std::simd::*; 10 | const SIZE: usize = 140; 11 | 12 | fn bits(input: &[u8], i: usize) -> u64 { 13 | let w = u8x64::from_slice(&input[64 * i..]); 14 | let x = u8x64::from_slice(&input[64 * i + 2..]); 15 | let a = u8x64::from_slice(&input[141 + 64 * i + 1..]); 16 | let y = u8x64::from_slice(&input[141 * 2 + 64 * i..]); 17 | let z = u8x64::from_slice(&input[141 * 2 + 64 * i + 2..]); 18 | 19 | let wz = ((w ^ z) + (x ^ y)).simd_eq(u8x64::splat((b'M' ^ b'S') + (b'M' ^ b'S'))); 20 | // let xy = (x ^ y).simd_eq(u8x64::splat(b'M' ^ b'S')); 21 | let a = a.simd_eq(u8x64::splat(b'A')); 22 | (wz & a).to_bitmask() 23 | } 24 | 25 | pub fn run(input: &[u8]) -> u32 { 26 | assert_eq!(input.len(), 141 * 140); 27 | let mut sum = 0; 28 | for i in 0..304 { 29 | sum += bits(input, i).count_ones(); 30 | } 31 | sum 32 | } 33 | -------------------------------------------------------------------------------- /2024/d05p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | 6 | use std::simd::cmp::{SimdPartialEq, SimdPartialOrd}; 7 | use std::simd::num::SimdInt; 8 | use std::simd::ptr::SimdMutPtr; 9 | use std::simd::{mask8x32, simd_swizzle, u64x4, u8x32, u8x64, usizex4, Simd}; 10 | 11 | pub fn run(input: &str) -> i64 { 12 | part1(input) as i64 13 | } 14 | 15 | pub fn part1(input: &str) -> u32 { 16 | unsafe { inner_part1(input) } 17 | } 18 | 19 | pub fn part2(input: &str) -> u32 { 20 | unsafe { inner_part2(input) } 21 | } 22 | 23 | static NLMASK: [u8; 32] = { 24 | let mut mask = [0; 32]; 25 | mask[0 * 6] = b'\n'; 26 | mask[1 * 6] = b'\n'; 27 | mask[2 * 6] = b'\n'; 28 | mask[3 * 6] = b'\n'; 29 | mask 30 | }; 31 | 32 | static RULE_MUL_BLOCK: [u8; 32] = { 33 | let mut block = [0; 32]; 34 | let mut i = 0; 35 | while i < 4 { 36 | block[6 * i] = 10; 37 | block[6 * i + 1] = 1; 38 | block[6 * i + 3] = 10; 39 | block[6 * i + 4] = 1; 40 | i += 1; 41 | } 42 | block 43 | }; 44 | 45 | static SWIZZLE_BLOCK: [usize; 32] = { 46 | let mut block = [31; 32]; 47 | let mut i = 0; 48 | while i < 4 { 49 | block[6 * i] = 6 * i + 1; 50 | block[6 * i + 3] = 6 * i + 4; 51 | i += 1; 52 | } 53 | block 54 | }; 55 | 56 | static SWIZZLE_LEFT: [usize; 32] = { 57 | let mut block = [31; 32]; 58 | let mut i = 0; 59 | while i < 4 { 60 | block[8 * i] = 6 * i; 61 | i += 1; 62 | } 63 | block 64 | }; 65 | 66 | static SWIZZLE_RIGHT: [usize; 32] = { 67 | let mut block = [31; 32]; 68 | let mut i = 0; 69 | while i < 4 { 70 | block[8 * i] = 6 * i + 3; 71 | i += 1; 72 | } 73 | block 74 | }; 75 | 76 | #[inline(always)] 77 | unsafe fn read_rules(rules: &mut [u128; 100], iter: &mut std::slice::Iter) { 78 | loop { 79 | let block = u8x32::from_slice(iter.as_slice().get_unchecked(..32)); 80 | if block.simd_eq(u8x32::from_slice(&NLMASK)).any() { 81 | break; 82 | } 83 | 84 | let digits = (block - u8x32::splat(b'0')) * u8x32::from_slice(&RULE_MUL_BLOCK); 85 | let nums = digits + simd_swizzle!(digits, SWIZZLE_BLOCK); 86 | let left = std::mem::transmute::<_, usizex4>(simd_swizzle!(nums, SWIZZLE_LEFT)); 87 | let right = std::mem::transmute::<_, u64x4>(simd_swizzle!(nums, SWIZZLE_RIGHT)); 88 | let right_big = right.simd_ge(u64x4::splat(64)); 89 | let idx = left * usizex4::splat(2) + (right_big.to_int().cast() & usizex4::splat(1)); 90 | let ptr = Simd::splat(rules.as_mut_ptr().cast::()).wrapping_add(idx); 91 | let shift_amt = right & u64x4::splat(0b111111); 92 | let to_store = u64x4::splat(1) << shift_amt; 93 | 94 | for (&ptr, &to_store) in std::iter::zip(ptr.as_array(), to_store.as_array()) { 95 | *ptr |= to_store; 96 | } 97 | 98 | *iter = iter.as_slice().get_unchecked(24..).iter(); 99 | } 100 | 101 | while *iter.as_slice().get_unchecked(0) != b'\n' { 102 | let c = iter.as_slice().get_unchecked(..5); 103 | let d1 = ((c[0] - b'0') as usize * 10) + ((c[1] - b'0') as usize); 104 | let d2 = ((c[3] - b'0') as usize * 10) + ((c[4] - b'0') as usize); 105 | *rules.get_unchecked_mut(d1) |= 1 << d2; 106 | *iter = iter.as_slice().get_unchecked(6..).iter(); 107 | } 108 | *iter = iter.as_slice().get_unchecked(1..).iter(); 109 | } 110 | 111 | static UPDATE_MUL_BLOCK: [u8; 32] = { 112 | let mut block = [0; 32]; 113 | let mut i = 0; 114 | while i < 11 { 115 | block[3 * i] = 10; 116 | block[3 * i + 1] = 1; 117 | i += 1; 118 | } 119 | block 120 | }; 121 | 122 | const SWIZZLE_HI: [usize; 32] = { 123 | let mut block = [2; 32]; 124 | let mut i = 0; 125 | while i < 11 { 126 | block[i] = 3 * i; 127 | i += 1; 128 | } 129 | block 130 | }; 131 | const SWIZZLE_LO: [usize; 32] = { 132 | let mut block = [2; 32]; 133 | let mut i = 0; 134 | while i < 11 { 135 | block[i] = 3 * i + 1; 136 | i += 1; 137 | } 138 | block 139 | }; 140 | 141 | static MASKS: [u64; 23] = { 142 | let mut masks = [0; 23]; 143 | let mut i = 0; 144 | while i < 23 { 145 | masks[i] = (1 << i) - 1; 146 | i += 1; 147 | } 148 | masks 149 | }; 150 | 151 | #[inline(always)] 152 | unsafe fn parse11(iter: &mut std::slice::Iter, update: &mut [u8], rem: &mut usize) { 153 | let len = std::cmp::min(*rem, 11); 154 | *rem -= len; 155 | 156 | let block = u8x32::from_slice(iter.as_slice().get_unchecked(..32)); 157 | 158 | let digits = block - u8x32::splat(b'0'); 159 | let nums = digits * u8x32::from_slice(&UPDATE_MUL_BLOCK); 160 | let nums = simd_swizzle!(nums, SWIZZLE_HI) + simd_swizzle!(nums, SWIZZLE_LO); 161 | 162 | let mask = mask8x32::from_bitmask(*MASKS.get_unchecked(len)); 163 | 164 | nums.store_select_unchecked(update, mask); 165 | 166 | *iter = iter.as_slice().get_unchecked(len * 3..).iter(); 167 | } 168 | 169 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 170 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 171 | unsafe fn inner_part1(input: &str) -> u32 { 172 | let mut iter = input.as_bytes().iter(); 173 | 174 | let mut rules = [0u128; 100]; 175 | read_rules(&mut rules, &mut iter); 176 | 177 | let mut tot = 0; 178 | let mut buf = [0; 24]; 179 | let mut buf_len; 180 | let mut mask; 181 | 182 | 'outer: while iter.len() >= 72 { 183 | buf_len = 0; 184 | mask = 0; 185 | 186 | let len = 8 + u8x64::from_slice(iter.as_slice().get_unchecked(8..8 + 64)) 187 | .simd_eq(u8x64::splat(b'\n')) 188 | .first_set() 189 | .unwrap_unchecked(); 190 | 191 | let mut c_iter = iter.as_slice().get_unchecked(..len + 1).iter(); 192 | 193 | iter = iter.as_slice().get_unchecked(len + 1..).iter(); 194 | 195 | while !c_iter.as_slice().is_empty() { 196 | let c = c_iter.as_slice(); 197 | 198 | let n = (*c.get_unchecked(0) - b'0') * 10 + (*c.get_unchecked(1) - b'0'); 199 | *buf.get_unchecked_mut(buf_len) = n; 200 | buf_len += 1; 201 | 202 | if *rules.get_unchecked(n as usize) & mask != 0 { 203 | continue 'outer; 204 | } 205 | 206 | mask |= 1 << n; 207 | c_iter = c_iter.as_slice().get_unchecked(3..).iter(); 208 | } 209 | 210 | tot += *buf.get_unchecked(buf_len / 2) as u32; 211 | } 212 | 213 | buf_len = 0; 214 | mask = 0; 215 | 'outer: while !iter.as_slice().is_empty() { 216 | let c = iter.as_slice(); 217 | 218 | let n = (*c.get_unchecked(0) - b'0') * 10 + (*c.get_unchecked(1) - b'0'); 219 | *buf.get_unchecked_mut(buf_len) = n; 220 | buf_len += 1; 221 | 222 | if *rules.get_unchecked(n as usize) & mask != 0 { 223 | while *iter.as_slice().get_unchecked(2) != b'\n' { 224 | iter = iter.as_slice().get_unchecked(3..).iter(); 225 | } 226 | iter = iter.as_slice().get_unchecked(3..).iter(); 227 | buf_len = 0; 228 | mask = 0; 229 | continue 'outer; 230 | } 231 | 232 | mask |= 1 << n; 233 | 234 | if *c.get_unchecked(2) == b'\n' { 235 | tot += *buf.get_unchecked(buf_len / 2) as u32; 236 | buf_len = 0; 237 | mask = 0; 238 | } 239 | 240 | iter = iter.as_slice().get_unchecked(3..).iter(); 241 | } 242 | 243 | tot 244 | } 245 | 246 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 247 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 248 | unsafe fn inner_part2(input: &str) -> u32 { 249 | let mut iter = input.as_bytes().iter(); 250 | 251 | let mut rules = [0u128; 100]; 252 | read_rules(&mut rules, &mut iter); 253 | 254 | let mut tot = 0; 255 | let mut buf = [0; 24]; 256 | let mut buf_len; 257 | let mut mask; 258 | let mut valid; 259 | 260 | while iter.len() >= 72 { 261 | let bytes_len = 9 + u8x64::from_slice(iter.as_slice().get_unchecked(8..8 + 64)) 262 | .simd_eq(u8x64::splat(b'\n')) 263 | .first_set() 264 | .unwrap_unchecked(); 265 | buf_len = bytes_len / 3; 266 | let mut rem = buf_len; 267 | 268 | while rem != 0 { 269 | let last_idx = buf_len - rem; 270 | parse11(&mut iter, buf.get_unchecked_mut(last_idx..), &mut rem); 271 | } 272 | 273 | valid = true; 274 | mask = 1 << *buf.get_unchecked(0); 275 | for i in 1..buf_len { 276 | let n = *buf.get_unchecked(i); 277 | valid &= *rules.get_unchecked(n as usize) & mask == 0; 278 | mask |= 1 << n; 279 | } 280 | 281 | if !valid { 282 | for i in 0..buf_len { 283 | let succs = *rules.get_unchecked(*buf.get_unchecked(i) as usize) & mask; 284 | if succs.count_ones() == buf_len as u32 / 2 { 285 | tot += *buf.get_unchecked(i) as u32; 286 | break; 287 | } 288 | } 289 | } 290 | } 291 | 292 | buf_len = 0; 293 | mask = 0; 294 | valid = true; 295 | 296 | for c in iter.as_slice().chunks_exact(3) { 297 | let n = (c[0] - b'0') * 10 + (c[1] - b'0'); 298 | *buf.get_unchecked_mut(buf_len) = n; 299 | buf_len += 1; 300 | valid &= *rules.get_unchecked(n as usize) & mask == 0; 301 | mask |= 1 << n; 302 | 303 | if c[2] == b'\n' { 304 | if !valid { 305 | for i in 0..buf_len { 306 | let succs = *rules.get_unchecked(*buf.get_unchecked(i) as usize) & mask; 307 | if succs.count_ones() == buf_len as u32 / 2 { 308 | tot += *buf.get_unchecked(i) as u32; 309 | break; 310 | } 311 | } 312 | } 313 | buf_len = 0; 314 | mask = 0; 315 | valid = true; 316 | } 317 | } 318 | 319 | tot 320 | } 321 | -------------------------------------------------------------------------------- /2024/d05p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | 6 | use std::simd::cmp::{SimdPartialEq, SimdPartialOrd}; 7 | use std::simd::num::SimdInt; 8 | use std::simd::ptr::SimdMutPtr; 9 | use std::simd::{simd_swizzle, u64x4, u8x32, u8x64, usizex4, Simd}; 10 | 11 | pub fn run(input: &str) -> i64 { 12 | part2(input) as i64 13 | } 14 | 15 | pub fn part1(input: &str) -> u32 { 16 | unsafe { inner_part1(input) } 17 | } 18 | 19 | pub fn part2(input: &str) -> u32 { 20 | unsafe { inner_part2(input) } 21 | } 22 | 23 | static NLMASK: [u8; 32] = { 24 | let mut mask = [0; 32]; 25 | mask[0 * 6] = b'\n'; 26 | mask[1 * 6] = b'\n'; 27 | mask[2 * 6] = b'\n'; 28 | mask[3 * 6] = b'\n'; 29 | mask 30 | }; 31 | 32 | static MUL_BLOCK: [u8; 32] = { 33 | let mut block = [0; 32]; 34 | let mut i = 0; 35 | while i < 4 { 36 | block[6 * i] = 10; 37 | block[6 * i + 1] = 1; 38 | block[6 * i + 3] = 10; 39 | block[6 * i + 4] = 1; 40 | i += 1; 41 | } 42 | block 43 | }; 44 | 45 | static SWIZZLE_BLOCK: [usize; 32] = { 46 | let mut block = [31; 32]; 47 | let mut i = 0; 48 | while i < 4 { 49 | block[6 * i] = 6 * i + 1; 50 | block[6 * i + 3] = 6 * i + 4; 51 | i += 1; 52 | } 53 | block 54 | }; 55 | 56 | static SWIZZLE_LEFT: [usize; 32] = { 57 | let mut block = [31; 32]; 58 | let mut i = 0; 59 | while i < 4 { 60 | block[8 * i] = 6 * i; 61 | i += 1; 62 | } 63 | block 64 | }; 65 | 66 | static SWIZZLE_RIGHT: [usize; 32] = { 67 | let mut block = [31; 32]; 68 | let mut i = 0; 69 | while i < 4 { 70 | block[8 * i] = 6 * i + 3; 71 | i += 1; 72 | } 73 | block 74 | }; 75 | 76 | #[inline(always)] 77 | unsafe fn read_rules(rules: &mut [u128; 100], iter: &mut std::slice::Iter) { 78 | loop { 79 | let block = u8x32::from_slice(iter.as_slice().get_unchecked(..32)); 80 | if block.simd_eq(u8x32::from_slice(&NLMASK)).any() { 81 | break; 82 | } 83 | 84 | let digits = (block - u8x32::splat(b'0')) * u8x32::from_slice(&MUL_BLOCK); 85 | let nums = digits + simd_swizzle!(digits, SWIZZLE_BLOCK); 86 | let left = std::mem::transmute::<_, usizex4>(simd_swizzle!(nums, SWIZZLE_LEFT)); 87 | let right = std::mem::transmute::<_, u64x4>(simd_swizzle!(nums, SWIZZLE_RIGHT)); 88 | let right_big = right.simd_ge(u64x4::splat(64)); 89 | let idx = left * usizex4::splat(2) + (right_big.to_int().cast() & usizex4::splat(1)); 90 | let ptr = Simd::splat(rules.as_mut_ptr().cast::()).wrapping_add(idx); 91 | let shift_amt = right & u64x4::splat(0b111111); 92 | let to_store = u64x4::splat(1) << shift_amt; 93 | 94 | for (&ptr, &to_store) in std::iter::zip(ptr.as_array(), to_store.as_array()) { 95 | *ptr |= to_store; 96 | } 97 | 98 | *iter = iter.as_slice().get_unchecked(24..).iter(); 99 | } 100 | 101 | while *iter.as_slice().get_unchecked(0) != b'\n' { 102 | let c = iter.as_slice().get_unchecked(..5); 103 | let d1 = ((c[0] - b'0') as usize * 10) + ((c[1] - b'0') as usize); 104 | let d2 = ((c[3] - b'0') as usize * 10) + ((c[4] - b'0') as usize); 105 | *rules.get_unchecked_mut(d1) |= 1 << d2; 106 | *iter = iter.as_slice().get_unchecked(6..).iter(); 107 | } 108 | *iter = iter.as_slice().get_unchecked(1..).iter(); 109 | } 110 | 111 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 112 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 113 | unsafe fn inner_part1(input: &str) -> u32 { 114 | let mut iter = input.as_bytes().iter(); 115 | 116 | let mut rules = [0u128; 100]; 117 | read_rules(&mut rules, &mut iter); 118 | 119 | let mut tot = 0; 120 | let mut buf = [0; 24]; 121 | let mut buf_len = 0; 122 | let mut mask = 0u128; 123 | 124 | 'outer: while iter.len() >= 72 { 125 | buf_len = 0; 126 | mask = 0; 127 | 128 | let len = 8 + u8x64::from_slice(iter.as_slice().get_unchecked(8..8 + 64)) 129 | .simd_eq(u8x64::splat(b'\n')) 130 | .first_set() 131 | .unwrap_unchecked(); 132 | 133 | let mut c_iter = iter.as_slice().get_unchecked(..len + 1).iter(); 134 | 135 | iter = iter.as_slice().get_unchecked(len + 1..).iter(); 136 | 137 | while !c_iter.as_slice().is_empty() { 138 | let c = c_iter.as_slice(); 139 | 140 | let n = (*c.get_unchecked(0) - b'0') * 10 + (*c.get_unchecked(1) - b'0'); 141 | *buf.get_unchecked_mut(buf_len) = n; 142 | buf_len += 1; 143 | 144 | if *rules.get_unchecked(n as usize) & mask != 0 { 145 | continue 'outer; 146 | } 147 | 148 | mask |= 1 << n; 149 | c_iter = c_iter.as_slice().get_unchecked(3..).iter(); 150 | } 151 | 152 | tot += *buf.get_unchecked(buf_len / 2) as u32; 153 | } 154 | 155 | 'outer: while !iter.as_slice().is_empty() { 156 | let c = iter.as_slice(); 157 | 158 | let n = (*c.get_unchecked(0) - b'0') * 10 + (*c.get_unchecked(1) - b'0'); 159 | *buf.get_unchecked_mut(buf_len) = n; 160 | buf_len += 1; 161 | 162 | if *rules.get_unchecked(n as usize) & mask != 0 { 163 | while *iter.as_slice().get_unchecked(2) != b'\n' { 164 | iter = iter.as_slice().get_unchecked(3..).iter(); 165 | } 166 | iter = iter.as_slice().get_unchecked(3..).iter(); 167 | buf_len = 0; 168 | mask = 0; 169 | continue 'outer; 170 | } 171 | 172 | mask |= 1 << n; 173 | 174 | if *c.get_unchecked(2) == b'\n' { 175 | tot += *buf.get_unchecked(buf_len / 2) as u32; 176 | buf_len = 0; 177 | mask = 0; 178 | } 179 | 180 | iter = iter.as_slice().get_unchecked(3..).iter(); 181 | } 182 | 183 | tot 184 | } 185 | 186 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 187 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 188 | unsafe fn inner_part2(input: &str) -> u32 { 189 | let mut iter = input.as_bytes().iter(); 190 | 191 | let mut rules = [0u128; 100]; 192 | read_rules(&mut rules, &mut iter); 193 | 194 | let mut tot = 0; 195 | let mut buf = [0; 24]; 196 | let mut buf_len = 0; 197 | let mut mask = 0u128; 198 | let mut valid = true; 199 | 200 | for c in iter.as_slice().chunks_exact(3) { 201 | let n = (c[0] - b'0') * 10 + (c[1] - b'0'); 202 | *buf.get_unchecked_mut(buf_len) = n; 203 | buf_len += 1; 204 | valid &= *rules.get_unchecked(n as usize) & mask == 0; 205 | mask |= 1 << n; 206 | 207 | if c[2] == b'\n' { 208 | if !valid { 209 | for i in 0..buf_len { 210 | let succs = *rules.get_unchecked(*buf.get_unchecked(i) as usize) & mask; 211 | if succs.count_ones() == buf_len as u32 / 2 { 212 | tot += *buf.get_unchecked(i) as u32; 213 | break; 214 | } 215 | } 216 | } 217 | buf_len = 0; 218 | mask = 0; 219 | valid = true; 220 | } 221 | } 222 | 223 | tot 224 | } 225 | -------------------------------------------------------------------------------- /2024/d06p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: doge 2 | const WIDTH: usize = 131; 3 | const HEIGHT: usize = 130; 4 | const SIZE: usize = WIDTH * HEIGHT; 5 | 6 | #[inline(always)] 7 | unsafe fn inner(s: &[u8]) -> u32 { 8 | let loc = memchr::memchr(b'^', s).unwrap_unchecked(); 9 | let mut new = [1_u8; SIZE]; 10 | let s_ptr = s.as_ptr(); 11 | let new_ptr = new.as_mut_ptr(); 12 | let slen = s.len(); 13 | let mut total = 0; 14 | let mut loc = loc; 15 | 16 | macro_rules! process_cell { 17 | () => {{ 18 | let cell_ptr = new_ptr.add(loc); 19 | total += *cell_ptr as u32; 20 | *cell_ptr = 0; 21 | }}; 22 | } 23 | 24 | macro_rules! check_bounds { 25 | ($next_expr:expr, $check_expr:expr) => {{ 26 | process_cell!(); 27 | let next = $next_expr; 28 | if $check_expr(next) { 29 | return total; 30 | } 31 | let c = *s_ptr.add(next); 32 | if c == b'#' { 33 | break; 34 | } 35 | loc = next; 36 | }}; 37 | } 38 | 39 | 'outer: loop { 40 | // Up 41 | loop { 42 | check_bounds!(loc.wrapping_sub(WIDTH), |n| n >= slen); 43 | } 44 | // Right 45 | loop { 46 | check_bounds!(loc + 1, |n| *s_ptr.add(n) == b'\n'); 47 | } 48 | // Down 49 | loop { 50 | check_bounds!(loc.wrapping_add(WIDTH), |n| n >= slen); 51 | } 52 | // Left 53 | loop { 54 | check_bounds!(loc.wrapping_sub(1), |n| *s_ptr.add(n) == b'\n'); 55 | } 56 | } 57 | } 58 | 59 | #[inline(always)] 60 | pub fn run(input: &[u8]) -> u32 { 61 | unsafe { inner(input) } 62 | } 63 | 64 | #[test] 65 | fn d6p1() { 66 | assert_eq!(run(include_bytes!("./../input/day6.txt")), 5269); 67 | } 68 | -------------------------------------------------------------------------------- /2024/d08p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | #![allow(clippy::pointers_in_nomem_asm_block)] 3 | #![feature(thread_local, portable_simd, core_intrinsics)] 4 | #![allow( 5 | clippy::erasing_op, 6 | static_mut_refs, 7 | internal_features, 8 | clippy::missing_safety_doc, 9 | clippy::identity_op, 10 | clippy::zero_prefixed_literal 11 | )] 12 | 13 | use std::{ 14 | arch::{ 15 | asm, 16 | x86_64::{ 17 | __m256i, _mm256_madd_epi16, _mm256_maddubs_epi16, _mm256_movemask_epi8, 18 | _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, _mm_maddubs_epi16, 19 | _mm_movemask_epi8, _mm_packus_epi32, _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 20 | }, 21 | }, 22 | fmt::Display, 23 | mem::{offset_of, transmute, MaybeUninit}, 24 | simd::prelude::*, 25 | }; 26 | 27 | // perhaps for later use 28 | macro_rules! black_box { 29 | ($thing:expr) => {{ 30 | let mut thing = $thing; 31 | asm!( 32 | "/*{t}*/", 33 | t = inout(reg) thing, 34 | options(pure, nomem, preserves_flags, nostack) 35 | ); 36 | thing 37 | }}; 38 | } 39 | 40 | unsafe fn process(s: &[u8]) -> u32 { 41 | let r = s.as_ptr_range(); 42 | let mut ptr = r.start; 43 | let mut cy = 0usize; 44 | 45 | #[repr(C, align(32))] 46 | struct Tables { 47 | _padding1: [u8; 16], 48 | antinodes: [u64; 150], 49 | _padding2: [u8; 16], 50 | frequencies: [[[u8; 2]; 4]; 75], 51 | } 52 | 53 | static mut TABLES: Tables = Tables { 54 | _padding1: [0; 16], 55 | antinodes: [0; 150], 56 | _padding2: [0; 16], 57 | frequencies: [[[0; 2]; 4]; 75], 58 | }; 59 | 60 | let Tables { 61 | antinodes, 62 | frequencies, 63 | .. 64 | } = &mut TABLES; 65 | 66 | antinodes[50..100].fill(0); 67 | frequencies.fill(Default::default()); 68 | 69 | loop { 70 | let c1 = ptr.cast::().read_unaligned() + Simd::splat(127 - b'.'); 71 | let c2 = ptr.add(18).cast::().read_unaligned() + Simd::splat(127 - b'.'); 72 | let m1 = c1.simd_ge(Simd::splat(128)).to_bitmask(); 73 | let m2 = c2.simd_ge(Simd::splat(128)).to_bitmask(); 74 | let mut mask = m1 | m2 << 18; 75 | if P2 { 76 | *antinodes.get_unchecked_mut(50 + cy) |= mask; 77 | } 78 | while mask != 0 { 79 | let cx = mask.trailing_zeros() as usize; 80 | let bucket = frequencies 81 | .get_unchecked_mut((ptr.add(cx).read() as usize).unchecked_sub(b'0' as usize)); 82 | let count_bucket = bucket.get_unchecked_mut(3).get_unchecked_mut(0); 83 | let count = *count_bucket as usize; 84 | *count_bucket += 1; 85 | let [nx, ny] = bucket.get_unchecked_mut(count); 86 | *nx = cx as u8; 87 | *ny = cy as u8; 88 | for i in 0..count { 89 | let [sx, sy] = *bucket.get_unchecked(i); 90 | let sx = sx as usize; 91 | let sy = sy as usize; 92 | let dx = cx as isize - sx as isize; 93 | let dy = cy - sy; 94 | let sbit = 1 << sx; 95 | let cbit = 1 << cx; 96 | if dx > 0 { 97 | let dx = dx as usize; 98 | if P2 { 99 | let mut bit = cbit << dx; 100 | let mut idx = cy + dy; 101 | while bit < 1 << 50 && idx < 50 { 102 | *antinodes.get_unchecked_mut(50 + idx) |= bit; 103 | bit <<= dx; 104 | idx += dy; 105 | } 106 | let mut bit = sbit >> dx; 107 | let mut idx = sy as isize - dy as isize; 108 | while bit > 0 && idx >= 0 { 109 | *antinodes.get_unchecked_mut(50 + idx as usize) |= bit; 110 | bit >>= dx; 111 | idx -= dy as isize; 112 | } 113 | } else { 114 | *antinodes.get_unchecked_mut(50 + cy + dy) |= cbit << dx; 115 | *antinodes.get_unchecked_mut(50 + sy - dy) |= sbit >> dx; 116 | } 117 | } else { 118 | let dx = -dx as usize; 119 | if P2 { 120 | let mut bit = cbit >> dx; 121 | let mut idx = cy + dy; 122 | while bit > 0 && idx < 50 { 123 | *antinodes.get_unchecked_mut(50 + idx) |= bit; 124 | bit >>= dx; 125 | idx += dy; 126 | } 127 | let mut bit = sbit << dx; 128 | let mut idx = sy as isize - dy as isize; 129 | while bit < 1 << 50 && idx >= 0 { 130 | *antinodes.get_unchecked_mut(50 + idx as usize) |= bit; 131 | bit <<= dx; 132 | idx -= dy as isize; 133 | } 134 | } else { 135 | *antinodes.get_unchecked_mut(50 + cy + dy) |= cbit >> dx; 136 | *antinodes.get_unchecked_mut(50 + sy - dy) |= sbit << dx; 137 | } 138 | } 139 | } 140 | 141 | mask &= mask - 1; 142 | } 143 | 144 | ptr = ptr.add(51); 145 | cy += 1; 146 | if ptr == r.end { 147 | break; 148 | } 149 | } 150 | 151 | antinodes 152 | .get_unchecked(50..100) 153 | .iter() 154 | .map(|&row| if P2 { row } else { row & 0x3FFFFFFFFFFFF }.count_ones()) 155 | .sum() 156 | } 157 | 158 | unsafe fn inner1(s: &[u8]) -> u32 { 159 | let r = s.as_ptr_range(); 160 | 161 | #[repr(C, align(32))] 162 | struct Tables { 163 | _padding1: [u8; 16], 164 | antinodes: [u64; 150], 165 | _padding2: [u8; 16], 166 | frequencies: [[[u8; 2]; 4]; 75], 167 | } 168 | 169 | static mut TABLES: Tables = Tables { 170 | _padding1: [0; 16], 171 | antinodes: [0; 150], 172 | _padding2: [0; 16], 173 | frequencies: [[[0; 2]; 4]; 75], 174 | }; 175 | 176 | let tables = &mut TABLES; 177 | 178 | tables.antinodes[50..100].fill(0); 179 | tables.frequencies.fill([[255; 2]; 4]); 180 | 181 | asm!( 182 | "21:", 183 | "vpaddb {y1}, {offset}, ymmword ptr[{ptr}]", 184 | "vpaddb {y2}, {offset}, ymmword ptr[{ptr} + 18]", 185 | "vpmovmskb {r1:e}, {y1}", 186 | "vpmovmskb {r2:e}, {y2}", 187 | "shl {r2}, 18", 188 | "or {r1}, {r2}", 189 | "jz 20f", 190 | "23:", 191 | "tzcnt {cx}, {r1}", 192 | "movzx {r2:e}, byte ptr[{ptr} + {cx}]", 193 | "lea {r2}, [{table} + {r2} * 8 + 432]", 194 | "movsx {count:e}, byte ptr[{r2} + 7]", 195 | "inc {count:e}", 196 | "mov byte ptr[{r2} + 7], {count:l}", 197 | "mov byte ptr[{r2} + {count} * 2], {cx:l}", 198 | "mov byte ptr[{r2} + {count} * 2 + 1], {cy:l}", 199 | "jz 22f", 200 | "shlx {cbit}, {one:r}, {cx}", 201 | "26:", 202 | "movzx {sx:e}, byte ptr[{r2} + {count} * 2 - 2]", 203 | "movzx {sy:e}, byte ptr[{r2} + {count} * 2 - 1]", 204 | "shlx {sbit}, {one:r}, {sx}", 205 | "mov {dy:e}, {cy:e}", 206 | "sub {dy}, {sy}", 207 | "mov {dx:e}, {cx:e}", 208 | "sub {sy:e}, {dy:e}", 209 | "sub {dx}, {sx}", 210 | "lea {sx}, [{cy} + {dy}]", 211 | "jbe 24f", 212 | "shlx {dy}, {cbit}, {dx}", 213 | "shrx {sbit}, {sbit}, {dx}", 214 | "jmp 25f", 215 | "24:", 216 | "neg {dx}", 217 | "shrx {dy}, {cbit}, {dx}", 218 | "shlx {sbit}, {sbit}, {dx}", 219 | "25:", 220 | "or qword ptr[{table} + {sx} * 8], {dy}", 221 | "or qword ptr[{table} + {sy} * 8], {sbit}", 222 | "dec {count:e}", 223 | "jnz 26b", 224 | "22:", 225 | "blsr {r1}, {r1}", 226 | "jnz 23b", 227 | "20:", 228 | "add {ptr}, -51", 229 | "dec {cy:e}", 230 | "jns 21b", 231 | y1 = out(ymm_reg) _, 232 | y2 = out(ymm_reg) _, 233 | offset = in(ymm_reg) u8x32::splat(127 - b'.'), 234 | ptr = inout(reg) r.end.sub(51) => _, 235 | r1 = out(reg) _, 236 | r2 = out(reg) _, 237 | count = out(reg) _, 238 | cx = out(reg) _, 239 | cy = inout(reg) 49usize => _, 240 | sx = out(reg) _, 241 | sy = out(reg) _, 242 | dx = out(reg) _, 243 | dy = out(reg) _, 244 | cbit = out(reg) _, 245 | sbit = out(reg) _, 246 | table = in(reg) (tables as *mut Tables).byte_add(offset_of!(Tables, antinodes) + size_of::() * 50), 247 | one = in(reg) 1, 248 | options(nostack), 249 | ); 250 | 251 | tables 252 | .antinodes 253 | .get_unchecked(50..100) 254 | .iter() 255 | .map(|&row| (row & 0x3FFFFFFFFFFFF).count_ones()) 256 | .sum() 257 | } 258 | 259 | pub fn run(s: &str) -> impl Display { 260 | unsafe { inner1(s.as_bytes()) } 261 | } 262 | 263 | unsafe fn inner2(s: &[u8]) -> u32 { 264 | process::(s) 265 | } 266 | 267 | pub fn part2(s: &str) -> impl Display { 268 | unsafe { inner2(s.as_bytes()) } 269 | } 270 | -------------------------------------------------------------------------------- /2024/d09p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | #![feature(thread_local, portable_simd, core_intrinsics)] 3 | #![allow( 4 | clippy::pointers_in_nomem_asm_block, 5 | clippy::erasing_op, 6 | static_mut_refs, 7 | internal_features, 8 | clippy::missing_safety_doc, 9 | clippy::identity_op, 10 | clippy::zero_prefixed_literal 11 | )] 12 | 13 | #[allow(unused)] 14 | use std::{ 15 | arch::{ 16 | asm, 17 | x86_64::{ 18 | __m256i, _mm256_madd_epi16, _mm256_maddubs_epi16, _mm256_movemask_epi8, 19 | _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, _mm_maddubs_epi16, 20 | _mm_movemask_epi8, _mm_packus_epi32, _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 21 | }, 22 | }, 23 | fmt::Display, 24 | mem::{offset_of, transmute, MaybeUninit}, 25 | simd::prelude::*, 26 | }; 27 | 28 | unsafe fn inner1(s: &[u8]) -> usize { 29 | let mut checksum = 0; 30 | 31 | asm!( 32 | "20:", 33 | "movzx {len:e}, byte ptr[{s} + {left} * 2]", 34 | "sub {len:e}, 48", 35 | "lea {scratch:e}, [{len} + {disk_pos} * 2 - 1]", 36 | "imul {scratch}, {left}", 37 | "imul {scratch}, {len}", 38 | "add {checksum}, {scratch}", 39 | "add {disk_pos:e}, {len:e}", 40 | "movzx {rem_dst:e}, byte ptr[{s} + {left} * 2 + 1]", 41 | "inc {left:e}", 42 | "sub {rem_dst:e}, 48", 43 | "jz 20b", 44 | "cmp {left:e}, {right:e}", 45 | "je 50f", 46 | "22:", 47 | "dec {right:e}", 48 | "movzx {rem_src:e}, byte ptr[{s} + {right} * 2]", 49 | "sub {rem_src:e}, 48", 50 | "cmp {rem_dst}, {rem_src}", 51 | "ja 40f", 52 | "21:", 53 | "lea {scratch:e}, [{rem_dst} + {disk_pos} * 2 - 1]", 54 | "jb 30f", 55 | "imul {scratch}, {right}", 56 | "imul {scratch}, {rem_dst}", 57 | "add {checksum}, {scratch}", 58 | "add {disk_pos:e}, {rem_dst:e}", 59 | "cmp {left:e}, {right:e}", 60 | "jne 20b", 61 | "jmp 50f", 62 | "30:", 63 | "imul {scratch}, {right}", 64 | "imul {scratch}, {rem_dst}", 65 | "add {checksum}, {scratch}", 66 | "add {disk_pos:e}, {rem_dst:e}", 67 | "sub {rem_src:e}, {rem_dst:e}", 68 | "31:", 69 | "cmp {left:e}, {right:e}", 70 | "je 60f", 71 | "movzx {len:e}, byte ptr[{s} + {left} * 2]", 72 | "sub {len:e}, 48", 73 | "lea {scratch:e}, [{len} + {disk_pos} * 2 - 1]", 74 | "imul {scratch}, {left}", 75 | "imul {scratch}, {len}", 76 | "add {checksum}, {scratch}", 77 | "add {disk_pos:e}, {len:e}", 78 | "movzx {rem_dst:e}, byte ptr[{s} + {left} * 2 + 1]", 79 | "inc {left:e}", 80 | "sub {rem_dst:e}, 48", 81 | "jz 31b", 82 | "cmp {rem_dst}, {rem_src}", 83 | "jbe 21b", 84 | "40:", 85 | "lea {scratch:e}, [{rem_src} + {disk_pos} * 2 - 1]", 86 | "imul {scratch}, {right}", 87 | "imul {scratch}, {rem_src}", 88 | "add {checksum}, {scratch}", 89 | "add {disk_pos:e}, {rem_src:e}", 90 | "sub {rem_dst:e}, {rem_src:e}", 91 | "cmp {left:e}, {right:e}", 92 | "jne 22b", 93 | "jmp 50f", 94 | "60:", 95 | "lea {scratch:e}, [{rem_src} + {disk_pos} * 2 - 1]", 96 | "imul {scratch}, {right}", 97 | "imul {scratch}, {rem_src}", 98 | "add {checksum}, {scratch}", 99 | "50:", 100 | "shr {checksum}", 101 | checksum = inout(reg) checksum, 102 | s = in(reg) s.as_ptr(), 103 | left = inout(reg) 0usize => _, 104 | right = inout(reg) s.len() / 2 => _, 105 | disk_pos = inout(reg) 0usize => _, 106 | rem_dst = out(reg) _, 107 | rem_src = out(reg) _, 108 | scratch = out(reg) _, 109 | len = out(reg) _, 110 | options(nostack, readonly), 111 | ); 112 | 113 | checksum 114 | } 115 | 116 | pub fn run(s: &str) -> impl Display { 117 | unsafe { inner1(s.as_bytes()) } 118 | } 119 | -------------------------------------------------------------------------------- /2024/d10p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | #![feature(slice_ptr_get)] 6 | #![feature(array_ptr_get)] 7 | 8 | use std::mem::MaybeUninit; 9 | use std::simd::prelude::*; 10 | 11 | pub fn run(input: &str) -> i64 { 12 | part1(input) as i64 13 | } 14 | 15 | #[inline(always)] 16 | pub fn part1(input: &str) -> u64 { 17 | unsafe { inner_part1(input) } 18 | } 19 | 20 | #[allow(unused)] 21 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 22 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 23 | unsafe fn inner_part1(input: &str) -> u64 { 24 | let input = input.as_bytes(); 25 | 26 | let line_len = 1 + u8x64::from_slice(input.get_unchecked(..64)) 27 | .simd_eq(u8x64::splat(b'\n')) 28 | .first_set() 29 | .unwrap_unchecked(); 30 | let len = input.len() - 1; 31 | 32 | let mut chunk_lens = [input.len() / 64 / par::NUM_THREADS * 64; par::NUM_THREADS]; 33 | for i in 0..input.len() / 64 % par::NUM_THREADS { 34 | chunk_lens[i] += 64; 35 | } 36 | chunk_lens[par::NUM_THREADS - 1] += input.len() % 64; 37 | 38 | let mut chunk_pos = [0; par::NUM_THREADS + 1]; 39 | for i in 0..par::NUM_THREADS { 40 | chunk_pos[i + 1] = chunk_pos[i] + chunk_lens[i]; 41 | } 42 | 43 | let acc = std::sync::atomic::AtomicU64::new(0); 44 | par::par(|idx| { 45 | let mut tot = 0; 46 | let mut offset = chunk_pos[idx]; 47 | let end = chunk_pos[idx + 1]; 48 | 49 | loop { 50 | let mut mask = if offset + 64 <= end { 51 | let block = u8x64::from_slice(input.get_unchecked(offset..offset + 64)); 52 | block.simd_eq(u8x64::splat(b'9')).to_bitmask() 53 | } else if offset < end { 54 | let block = u8x64::from_slice(input.get_unchecked(input.len() - 64..)); 55 | block.simd_eq(u8x64::splat(b'9')).to_bitmask() >> (64 - (input.len() - offset)) 56 | } else { 57 | break; 58 | }; 59 | 60 | while mask != 0 { 61 | let o = mask.trailing_zeros(); 62 | mask &= !(1 << o); 63 | 64 | let mut seen = u16x16::from_slice(&[u16::MAX; 16]); 65 | let mut seen_len = 0; 66 | 67 | let mut stack = [MaybeUninit::uninit(); 16]; 68 | let mut stack_len = 0; 69 | 70 | let mut curr_o = offset + o as usize; 71 | let mut c = b'9'; 72 | 73 | loop { 74 | if c == b'0' { 75 | if seen.simd_ne(u16x16::splat(curr_o as u16)).all() { 76 | *seen.as_mut_array().get_unchecked_mut(seen_len) = curr_o as u16; 77 | seen_len += 1; 78 | } 79 | 80 | if stack_len == 0 { 81 | break; 82 | } 83 | stack_len -= 1; 84 | (curr_o, c) = stack.get_unchecked(stack_len).assume_init(); 85 | 86 | continue; 87 | } 88 | 89 | let l = curr_o.wrapping_sub(1); 90 | let r = curr_o.wrapping_add(1); 91 | let t = curr_o.wrapping_sub(line_len); 92 | let b = curr_o.wrapping_add(line_len); 93 | 94 | macro_rules! handle { 95 | ($new_o:expr) => {{ 96 | let new_o = $new_o; 97 | if *input.get_unchecked(new_o) == c - 1 { 98 | *stack.get_unchecked_mut(stack_len).as_mut_ptr() = (new_o, c - 1); 99 | stack_len += 1; 100 | } 101 | }}; 102 | } 103 | 104 | if t < len - 2 * line_len { 105 | handle!(t); 106 | handle!(b); 107 | handle!(l); 108 | } else { 109 | if t < len { 110 | handle!(t); 111 | handle!(l); 112 | if b < len { 113 | handle!(b); 114 | } 115 | } else { 116 | handle!(b); 117 | if l < len { 118 | handle!(l); 119 | } 120 | } 121 | } 122 | if *input.get_unchecked(r) == c - 1 { 123 | (curr_o, c) = (r, c - 1); 124 | } else if stack_len > 0 { 125 | stack_len -= 1; 126 | (curr_o, c) = stack.get_unchecked(stack_len).assume_init(); 127 | } else { 128 | break; 129 | } 130 | } 131 | 132 | tot += seen_len; 133 | } 134 | 135 | offset += 64; 136 | } 137 | 138 | acc.fetch_add(tot as u64, std::sync::atomic::Ordering::Relaxed); 139 | }); 140 | acc.into_inner() 141 | } 142 | 143 | mod par { 144 | use std::sync::atomic::{AtomicPtr, Ordering}; 145 | 146 | pub const NUM_THREADS: usize = 16; 147 | 148 | #[repr(align(64))] 149 | struct CachePadded(T); 150 | 151 | static mut INIT: bool = false; 152 | 153 | static WORK: [CachePadded>; NUM_THREADS] = 154 | [const { CachePadded(AtomicPtr::new(std::ptr::null_mut())) }; NUM_THREADS]; 155 | 156 | #[inline(always)] 157 | fn submit(f: &F) { 158 | unsafe { 159 | if !INIT { 160 | INIT = true; 161 | for idx in 1..NUM_THREADS { 162 | thread_run(idx, f); 163 | } 164 | } 165 | } 166 | 167 | for i in 1..NUM_THREADS { 168 | WORK[i].0.store(f as *const F as *mut (), Ordering::Release); 169 | } 170 | } 171 | 172 | #[inline(always)] 173 | pub fn wait(i: usize) { 174 | loop { 175 | let ptr = WORK[i].0.load(Ordering::Acquire); 176 | if ptr.is_null() { 177 | break; 178 | } 179 | std::hint::spin_loop(); 180 | } 181 | } 182 | 183 | #[inline(always)] 184 | fn wait_all() { 185 | for i in 1..NUM_THREADS { 186 | wait(i); 187 | } 188 | } 189 | 190 | fn thread_run(idx: usize, _f: &F) { 191 | _ = std::thread::Builder::new().spawn(move || unsafe { 192 | let work = WORK.get_unchecked(idx); 193 | 194 | loop { 195 | let data = work.0.load(Ordering::Acquire); 196 | if !data.is_null() { 197 | (&*data.cast::())(idx); 198 | work.0.store(std::ptr::null_mut(), Ordering::Release); 199 | } 200 | std::hint::spin_loop(); 201 | } 202 | }); 203 | } 204 | 205 | pub unsafe fn par(f: F) { 206 | submit(&f); 207 | f(0); 208 | wait_all(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /2024/d11p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | #![feature(slice_ptr_get)] 6 | #![feature(array_ptr_get)] 7 | 8 | pub fn run(input: &str) -> i64 { 9 | part1(input) as i64 10 | } 11 | 12 | #[inline(always)] 13 | pub fn part1(input: &str) -> u32 { 14 | unsafe { inner_part1(input) } 15 | } 16 | 17 | #[inline(always)] 18 | pub fn part2(input: &str) -> u64 { 19 | unsafe { inner_part2(input) } 20 | } 21 | 22 | static mut LUT: [u64; 10_000_000] = [0; 10_000_000]; 23 | 24 | #[cfg_attr(any(target_os = "linux"), link_section = ".text.startup")] 25 | unsafe extern "C" fn __ctor() { 26 | make_d11_lut(); 27 | } 28 | 29 | #[used] 30 | #[cfg_attr(target_os = "linux", link_section = ".init_array")] 31 | #[cfg_attr(windows, link_section = ".CRT$XCU")] 32 | static __CTOR: unsafe extern "C" fn() = __ctor; 33 | 34 | fn make_d11_lut() { 35 | use std::collections::HashMap; 36 | let iters = 25; 37 | 38 | let mut levels = vec![HashMap::new(); iters]; 39 | 40 | fn solve_rec(i: usize, j: usize, levels: &mut [HashMap]) -> usize { 41 | if i == 0 { 42 | return 1; 43 | } 44 | 45 | if let Some(&res) = levels[i - 1].get(&j) { 46 | return res; 47 | } 48 | 49 | let res = if j == 0 { 50 | solve_rec(i - 1, 1, levels) 51 | } else if j.ilog10() % 2 == 1 { 52 | let pow10 = 10usize.pow((j.ilog10() + 1) / 2); 53 | solve_rec(i - 1, j / pow10, levels) + solve_rec(i - 1, j % pow10, levels) 54 | } else { 55 | solve_rec(i - 1, j * 2024, levels) 56 | }; 57 | 58 | levels[i - 1].insert(j, res); 59 | 60 | res 61 | } 62 | 63 | for j in 0..10_000_000 { 64 | (unsafe { &mut LUT })[j] = solve_rec(iters, j, &mut levels) as _; 65 | } 66 | } 67 | 68 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 69 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 70 | unsafe fn inner_part1(input: &str) -> u32 { 71 | type Ty = u64; 72 | 73 | let mut tot = 0u64; 74 | 75 | let lut = LUT.as_ptr().cast::(); 76 | 77 | #[rustfmt::skip] 78 | core::arch::asm!( 79 | "2:", 80 | "movzx {n}, byte ptr [{ptr}]", 81 | "movzx {d}, byte ptr [{ptr} + 1]", 82 | "sub {n}, {b0}", 83 | "sub {d}, {b0}", 84 | "add {ptr}, 2", 85 | "cmp {d}, 9", 86 | "ja 4f", 87 | 88 | "lea {n}, [{n} + 4*{n}]", 89 | "lea {n}, [{d} + 2*{n}]", 90 | "movzx {d}, byte ptr [{ptr}]", 91 | "sub {d}, {b0}", 92 | "inc {ptr}", 93 | "cmp {d}, 9", 94 | "ja 4f", 95 | 96 | "lea {n}, [{n} + 4*{n}]", 97 | "lea {n}, [{d} + 2*{n}]", 98 | "movzx {d}, byte ptr [{ptr}]", 99 | "sub {d}, {b0}", 100 | "inc {ptr}", 101 | "cmp {d}, 9", 102 | "ja 4f", 103 | 104 | "lea {n}, [{n} + 4*{n}]", 105 | "lea {n}, [{d} + 2*{n}]", 106 | "movzx {d}, byte ptr [{ptr}]", 107 | "sub {d}, {b0}", 108 | "inc {ptr}", 109 | "cmp {d}, 9", 110 | "ja 4f", 111 | 112 | "lea {n}, [{n} + 4*{n}]", 113 | "lea {n}, [{d} + 2*{n}]", 114 | "movzx {d}, byte ptr [{ptr}]", 115 | "sub {d}, {b0}", 116 | "inc {ptr}", 117 | "cmp {d}, 9", 118 | "ja 4f", 119 | 120 | "lea {n}, [{n} + 4*{n}]", 121 | "lea {n}, [{d} + 2*{n}]", 122 | "movzx {d}, byte ptr [{ptr}]", 123 | "sub {d}, {b0}", 124 | "inc {ptr}", 125 | "cmp {d}, 9", 126 | "ja 4f", 127 | 128 | "lea {n}, [{n} + 4*{n}]", 129 | "lea {n}, [{d} + 2*{n}]", 130 | "movzx {d}, byte ptr [{ptr}]", 131 | "sub {d}, {b0}", 132 | "inc {ptr}", 133 | "4:", 134 | "add {tot}, qword ptr [{lut} + {s}*{n}]", 135 | "cmp {ptr}, {end}", 136 | "jne 2b", 137 | ptr = in(reg) input.as_ptr(), 138 | end = in(reg) input.as_ptr().add(input.len()), 139 | lut = in(reg) lut, 140 | tot = inout(reg) tot, 141 | n = out(reg) _, 142 | d = out(reg) _, 143 | s = const std::mem::size_of::(), 144 | b0 = const b'0' as u64 145 | ); 146 | 147 | tot as u32 148 | } 149 | 150 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 151 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 152 | unsafe fn inner_part2(input: &str) -> u64 { 153 | type Ty = u64; 154 | 155 | let mut tot = 0; 156 | 157 | let lut = LUT.as_ptr().cast::(); 158 | 159 | #[rustfmt::skip] 160 | core::arch::asm!( 161 | "2:", 162 | "movzx {n}, byte ptr [{ptr}]", 163 | "movzx {d}, byte ptr [{ptr} + 1]", 164 | "sub {n}, {b0}", 165 | "sub {d}, {b0}", 166 | "add {ptr}, 2", 167 | "cmp {d}, 9", 168 | "ja 4f", 169 | "3:", 170 | "lea {n}, [{n} + 4*{n}]", 171 | "lea {n}, [{d} + 2*{n}]", 172 | "movzx {d}, byte ptr [{ptr}]", 173 | "sub {d}, {b0}", 174 | "inc {ptr}", 175 | "cmp {d}, 10", 176 | "jb 3b", 177 | "4:", 178 | "add {tot}, qword ptr [{lut} + {s}*{n}]", 179 | "cmp {ptr}, {end}", 180 | "jne 2b", 181 | ptr = in(reg) input.as_ptr(), 182 | end = in(reg) input.as_ptr().add(input.len()), 183 | lut = in(reg) lut, 184 | tot = inout(reg) tot, 185 | n = out(reg) _, 186 | d = out(reg) _, 187 | s = const std::mem::size_of::(), 188 | b0 = const b'0' as u64 189 | ); 190 | 191 | tot 192 | } 193 | -------------------------------------------------------------------------------- /2024/d11p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | #![feature(slice_ptr_get)] 6 | #![feature(array_ptr_get)] 7 | 8 | pub fn run(input: &str) -> i64 { 9 | part2(input) as i64 10 | } 11 | 12 | #[inline(always)] 13 | pub fn part1(input: &str) -> u32 { 14 | unsafe { inner_part1(input) } 15 | } 16 | 17 | #[inline(always)] 18 | pub fn part2(input: &str) -> u64 { 19 | unsafe { inner_part2(input) } 20 | } 21 | 22 | static mut LUT: [u64; 10_000_000] = [0; 10_000_000]; 23 | 24 | #[cfg_attr(any(target_os = "linux"), link_section = ".text.startup")] 25 | unsafe extern "C" fn __ctor() { 26 | make_d11_lut(); 27 | } 28 | 29 | #[used] 30 | #[cfg_attr(target_os = "linux", link_section = ".init_array")] 31 | #[cfg_attr(windows, link_section = ".CRT$XCU")] 32 | static __CTOR: unsafe extern "C" fn() = __ctor; 33 | 34 | fn make_d11_lut() { 35 | use std::collections::HashMap; 36 | let iters = 75; 37 | 38 | let mut levels = vec![HashMap::new(); iters]; 39 | 40 | fn solve_rec(i: usize, j: usize, levels: &mut [HashMap]) -> usize { 41 | if i == 0 { 42 | return 1; 43 | } 44 | 45 | if let Some(&res) = levels[i - 1].get(&j) { 46 | return res; 47 | } 48 | 49 | let res = if j == 0 { 50 | solve_rec(i - 1, 1, levels) 51 | } else if j.ilog10() % 2 == 1 { 52 | let pow10 = 10usize.pow((j.ilog10() + 1) / 2); 53 | solve_rec(i - 1, j / pow10, levels) + solve_rec(i - 1, j % pow10, levels) 54 | } else { 55 | solve_rec(i - 1, j * 2024, levels) 56 | }; 57 | 58 | levels[i - 1].insert(j, res); 59 | 60 | res 61 | } 62 | 63 | for j in 0..10_000_000 { 64 | (unsafe { &mut LUT })[j] = solve_rec(iters, j, &mut levels) as _; 65 | } 66 | } 67 | 68 | macro_rules! solve { 69 | ($input:ident, $lut:literal, $ty:ident) => {{ 70 | let lut = LUT.as_ptr(); 71 | 72 | let input = $input.as_bytes(); 73 | let mut ptr = input.as_ptr(); 74 | let end = ptr.add(input.len()); 75 | 76 | let mut tot = 0; 77 | 78 | loop { 79 | let mut n = (*ptr - b'0') as usize; 80 | ptr = ptr.add(1); 81 | 82 | loop { 83 | let d = (*ptr).wrapping_sub(b'0'); 84 | ptr = ptr.add(1); 85 | if d >= 10 { 86 | break; 87 | } 88 | n = 10 * n + d as usize; 89 | } 90 | 91 | tot += lut.add(n).read_unaligned(); 92 | 93 | if ptr == end { 94 | break tot; 95 | } 96 | } 97 | }}; 98 | } 99 | 100 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 101 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 102 | unsafe fn inner_part1(input: &str) -> u32 { 103 | solve!(input, "/d11p1.lut", u32) as u32 104 | } 105 | 106 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 107 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 108 | unsafe fn inner_part2(input: &str) -> u64 { 109 | solve!(input, "/d11p2.lut", u64) 110 | } 111 | -------------------------------------------------------------------------------- /2024/d13p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | // . 3 | #![feature(thread_local, portable_simd, core_intrinsics)] 4 | #![allow( 5 | clippy::precedence, 6 | clippy::missing_transmute_annotations, 7 | clippy::pointers_in_nomem_asm_block, 8 | clippy::erasing_op, 9 | static_mut_refs, 10 | internal_features, 11 | clippy::missing_safety_doc, 12 | clippy::identity_op, 13 | clippy::zero_prefixed_literal 14 | )] 15 | 16 | #[allow(unused)] 17 | use std::{ 18 | arch::{ 19 | asm, 20 | x86_64::{ 21 | __m128i, __m256i, _bextr2_u32, _mm256_madd_epi16, _mm256_maddubs_epi16, 22 | _mm256_movemask_epi8, _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, 23 | _mm_maddubs_epi16, _mm_minpos_epu16, _mm_movemask_epi8, _mm_packus_epi32, 24 | _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 25 | }, 26 | }, 27 | array, 28 | fmt::Display, 29 | hint::assert_unchecked, 30 | intrinsics::{likely, unlikely}, 31 | mem::{offset_of, transmute, MaybeUninit}, 32 | ptr, 33 | simd::prelude::*, 34 | slice, 35 | }; 36 | 37 | unsafe fn inner1(s: &[u8]) -> u64 { 38 | static LUT: [i8x16; 128] = { 39 | let mut lut = [[-1i8; 16]; 128]; 40 | let mut y = 3; 41 | while y < 6 { 42 | let mut x = 3; 43 | while x < 6 { 44 | let mut y_end = 16; 45 | let y_start = y_end - y; 46 | let mut x_end = y_start - 4; 47 | let x_start = x_end - x; 48 | let index = (((1 << x_end) - 1 ^ (1 << x_start) - 1) & 0x1FC) / 4; 49 | let entry = &mut lut[index]; 50 | let mut i = 16; 51 | while y_start < y_end { 52 | y_end -= 1; 53 | i -= 1; 54 | entry[i] = y_end; 55 | } 56 | let mut i = 8; 57 | while x_start < x_end { 58 | x_end -= 1; 59 | i -= 1; 60 | entry[i] = x_end; 61 | } 62 | x += 1; 63 | } 64 | y += 1; 65 | } 66 | 67 | unsafe { transmute(lut) } 68 | }; 69 | 70 | let start = s.as_ptr(); 71 | let i = s.len() as isize; 72 | let lut = LUT.as_ptr(); 73 | let mults10 = u8x16::from_array([10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1]); 74 | let mults100 = u16x8::from_array([100, 1, 100, 1, 100, 1, 100, 1]); 75 | let mults10000 = u16x8::from_array([10000, 1, 10000, 1, 10000, 1, 10000, 1]); 76 | let swar_mask = 0xF0_F0_FF_FF_FF_FF_F0_F0u64; 77 | let swar_bextr = 8 | 8 << 8; 78 | let sum; 79 | 80 | asm!( 81 | "20:", 82 | "vpaddb {chunk}, {neg_ascii_zero}, xmmword ptr[{start} + {i} - 17]", 83 | "vpminub {xtmp}, {chunk}, {_9}", 84 | "vpcmpeqb {xtmp}, {xtmp}, {chunk}", 85 | "vpmovmskb {mask}, {xtmp}", 86 | "tzcnt {r1}, {mask}", 87 | "lea {i}, [{i} + {r1} - 69]", 88 | "andn {r2}, {swar_mask}, qword ptr[{start} + {i} + 13]", 89 | "imul {r2}, {r2}, 2561", 90 | "bextr {ax}, {r2}, {swar_bextr:r}", 91 | "shr {r2}, 56", 92 | "andn rax, {swar_mask}, qword ptr[{start} + {i} + 34]", 93 | "imul rax, rax, 2561", 94 | "bextr {bx}, rax, {swar_bextr:r}", 95 | "shr rax, 56", 96 | "imul {r2}, {bx}", 97 | "mov {r1}, rax", 98 | "imul {r1}, {ax}", 99 | "sub {r1}, {r2}", 100 | "jz 21f", 101 | "and {mask}, 0x1FC", 102 | "vpshufb {chunk}, {chunk}, xmmword ptr[{lut} + {mask} * 4]", 103 | "vpmaddubsw {chunk}, {chunk}, {mults10}", 104 | "vpmaddwd {chunk}, {chunk}, {mults100}", 105 | "vpackusdw {chunk}, {chunk}, {chunk}", 106 | "vpmaddwd {chunk}, {chunk}, {mults10000}", 107 | "vmovd {px:e}, {chunk}", 108 | "vpextrd edx, {chunk}, 1", 109 | "imul rax, {px}", 110 | "imul rdx, {bx}", 111 | "sub rax, rdx", 112 | "imul {ax}, rax", 113 | "cqo", 114 | "idiv {r1}", 115 | "test rdx, rdx", 116 | "jnz 21f", 117 | "imul {bx}, {r1}", 118 | "imul {r1}, {px}", 119 | "add {sum_a}, rax", 120 | "mov rax, {r1}", 121 | "sub rax, {ax}", 122 | "cqo", 123 | "idiv {bx}", 124 | "add {sum_b}, rax", 125 | "21:", 126 | "test {i}, {i}", 127 | "jns 20b", 128 | "lea rax, [{sum_a} + {sum_a} * 2]", 129 | "add rax, {sum_b}", 130 | chunk = out(xmm_reg) _, 131 | neg_ascii_zero = in(xmm_reg) u8x16::splat(b'0'.wrapping_neg()), 132 | xtmp = out(xmm_reg) _, 133 | _9 = in(xmm_reg) u8x16::splat(9), 134 | start = in(reg) start, 135 | i = inout(reg) i => _, 136 | mask = out(reg) _, 137 | r1 = out(reg) _, 138 | ax = out(reg) _, 139 | r2 = out(reg) _, // ay 140 | bx = out(reg) _, 141 | out("rax") sum, // by 142 | px = out(reg) _, 143 | out("rdx") _, // py, rem 144 | sum_a = inout(reg) 0u64 => _, 145 | sum_b = inout(reg) 0u64 => _, 146 | swar_mask = in(reg) swar_mask, 147 | swar_bextr = in(reg) swar_bextr, 148 | lut = in(reg) lut, 149 | mults10 = in(xmm_reg) mults10, 150 | mults100 = in(xmm_reg) mults100, 151 | mults10000 = in(xmm_reg) mults10000, 152 | options(nostack), 153 | ); 154 | 155 | sum 156 | } 157 | 158 | unsafe fn inner2(s: &[u8]) -> u64 { 159 | 0 160 | } 161 | 162 | pub fn run(s: &str) -> impl Display { 163 | unsafe { inner1(s.as_bytes()) } 164 | } 165 | 166 | pub fn part2(s: &str) -> impl Display { 167 | unsafe { inner2(s.as_bytes()) } 168 | } 169 | -------------------------------------------------------------------------------- /2024/d13p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | #![feature(slice_ptr_get)] 6 | #![feature(array_ptr_get)] 7 | #![feature(core_intrinsics)] 8 | 9 | pub fn run(input: &str) -> i64 { 10 | part2(input) as i64 11 | } 12 | 13 | #[inline(always)] 14 | pub fn part1(input: &str) -> u64 { 15 | unsafe { inner_part1(input) as u64 } 16 | } 17 | 18 | #[inline(always)] 19 | pub fn part2(input: &str) -> u64 { 20 | unsafe { inner_part2(input) } 21 | } 22 | 23 | macro_rules! parse2 { 24 | ($ptr:ident as $ty:ident) => {{ 25 | let n = (*$ptr as $ty - b'0' as $ty); 26 | let d = (*$ptr.add(1) as $ty).wrapping_sub(b'0' as $ty); 27 | $ptr = $ptr.add(2); 28 | 10 * n + d 29 | }}; 30 | } 31 | macro_rules! parse { 32 | ($ptr:ident as $ty:ident) => {{ 33 | // TODO: SWAR? 34 | let mut n = 100 * (*$ptr as $ty - b'0' as $ty); 35 | n += 10 * (*$ptr.add(1) as $ty - b'0' as $ty); 36 | n += (*$ptr.add(2) as $ty - b'0' as $ty); 37 | let d = *$ptr.add(3) as $ty; 38 | $ptr = $ptr.add(4); 39 | if d >= b'0' as $ty { 40 | n = 10 * n + (d - b'0' as $ty); 41 | let d = *$ptr as $ty; 42 | $ptr = $ptr.add(1); 43 | if d >= b'0' as $ty { 44 | n = 10 * n + (d - b'0' as $ty); 45 | $ptr = $ptr.add(1); 46 | } 47 | } 48 | n 49 | }}; 50 | } 51 | 52 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 53 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 54 | unsafe fn inner_part1(input: &str) -> u64 { 55 | let mut ptr = input.as_ptr().wrapping_sub(1); 56 | let end = input.as_ptr().add(input.len()); 57 | let mut tot = 0; 58 | 59 | loop { 60 | ptr = ptr.add(13); 61 | let dxa = parse2!(ptr as u32) as i32; 62 | ptr = ptr.add(4); 63 | let dya = parse2!(ptr as u32) as i32; 64 | ptr = ptr.add(13); 65 | let dxb = parse2!(ptr as u32) as i32; 66 | ptr = ptr.add(4); 67 | let dyb = parse2!(ptr as u32) as i32; 68 | 69 | ptr = ptr.add(10); 70 | let x = parse!(ptr as u32) as i32; 71 | ptr = ptr.add(3); 72 | let y = parse!(ptr as u32) as i32; 73 | 74 | let det = dxa * dyb - dxb * dya; 75 | 76 | let d1 = x * dyb - dxb * y; 77 | let q1 = std::intrinsics::unchecked_div(d1, det); 78 | let r1 = std::intrinsics::unchecked_rem(d1, det); 79 | 80 | if r1 == 0 { 81 | let d2 = dxa * y - x * dya; 82 | let q2 = std::intrinsics::unchecked_div(d2, det); 83 | let r2 = std::intrinsics::unchecked_rem(d2, det); 84 | if r2 == 0 { 85 | tot += (3 * q1 + q2) as u64; 86 | } 87 | } 88 | 89 | if ptr == end { 90 | break; 91 | } 92 | } 93 | 94 | tot 95 | } 96 | 97 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 98 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 99 | unsafe fn inner_part2(input: &str) -> u64 { 100 | let mut ptr = input.as_ptr().wrapping_sub(1); 101 | let end = input.as_ptr().add(input.len()); 102 | let mut tot = 0; 103 | 104 | loop { 105 | ptr = ptr.add(13); 106 | let dxa = parse2!(ptr as u64) as i64; 107 | ptr = ptr.add(4); 108 | let dya = parse2!(ptr as u64) as i64; 109 | ptr = ptr.add(13); 110 | let dxb = parse2!(ptr as u64) as i64; 111 | ptr = ptr.add(4); 112 | let dyb = parse2!(ptr as u64) as i64; 113 | 114 | ptr = ptr.add(10); 115 | let x = parse!(ptr as u64) as i64 + 10000000000000; 116 | ptr = ptr.add(3); 117 | let y = parse!(ptr as u64) as i64 + 10000000000000; 118 | 119 | let det = dxa * dyb - dxb * dya; 120 | 121 | let d1 = x * dyb - dxb * y; 122 | let q1 = std::intrinsics::unchecked_div(d1, det); 123 | let r1 = std::intrinsics::unchecked_rem(d1, det); 124 | 125 | if r1 == 0 { 126 | let d2 = dxa * y - x * dya; 127 | let q2 = std::intrinsics::unchecked_div(d2, det); 128 | let r2 = std::intrinsics::unchecked_rem(d2, det); 129 | if r2 == 0 { 130 | tot += (3 * q1 + q2) as u64; 131 | } 132 | } 133 | 134 | if ptr == end { 135 | break; 136 | } 137 | } 138 | 139 | tot 140 | } 141 | -------------------------------------------------------------------------------- /2024/d14p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | #![feature(slice_ptr_get)] 6 | #![feature(array_ptr_get)] 7 | #![feature(core_intrinsics)] 8 | #![feature(int_roundings)] 9 | 10 | use std::mem::MaybeUninit; 11 | use std::simd::prelude::*; 12 | 13 | pub fn run(input: &str) -> i64 { 14 | part1(input) as i64 15 | } 16 | 17 | #[inline(always)] 18 | pub fn part1(input: &str) -> u64 { 19 | unsafe { inner_part1(input) as u64 } 20 | } 21 | 22 | #[inline(always)] 23 | pub fn part2(input: &str) -> u64 { 24 | unsafe { inner_part2(input) } 25 | } 26 | 27 | macro_rules! parse_pos { 28 | ($ptr:ident as $ty:ty) => {{ 29 | let mut n = *$ptr as $ty - b'0' as $ty; 30 | $ptr = $ptr.add(1); 31 | if *$ptr as $ty >= b'0' as $ty { 32 | n = 10 * n + *$ptr as $ty - b'0' as $ty; 33 | $ptr = $ptr.add(1); 34 | if *$ptr as $ty >= b'0' as $ty { 35 | n = 10 * n + *$ptr as $ty - b'0' as $ty; 36 | $ptr = $ptr.add(1); 37 | } 38 | } 39 | n 40 | }}; 41 | } 42 | 43 | macro_rules! parse { 44 | ($ptr:ident as $ty:ident - $m:expr) => {{ 45 | if *$ptr == b'-' { 46 | $ptr = $ptr.add(1); 47 | $m as $ty - parse_pos!($ptr as $ty) 48 | } else { 49 | parse_pos!($ptr as $ty) 50 | } 51 | }}; 52 | } 53 | 54 | const W: i64 = 101; 55 | const H: i64 = 103; 56 | 57 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 58 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 59 | unsafe fn inner_part1(input: &str) -> u64 { 60 | let mut counts = [[0; 2]; 2]; 61 | let mut ptr = input.as_ptr().wrapping_sub(1); 62 | let end = ptr.add(input.len()); 63 | 64 | type Ty = u32; 65 | 66 | loop { 67 | ptr = ptr.add(3); 68 | let px = parse_pos!(ptr as Ty); 69 | ptr = ptr.add(1); 70 | let py = parse_pos!(ptr as Ty); 71 | ptr = ptr.add(3); 72 | let vx = parse!(ptr as Ty - W); 73 | ptr = ptr.add(1); 74 | let vy = parse!(ptr as Ty - H); 75 | 76 | let fx = fastdiv::fastmod_w((px + 100 * vx) as _) as Ty; 77 | let fy = fastdiv::fastmod_h((py + 100 * vy) as _) as Ty; 78 | 79 | if fx != W as Ty / 2 && fy != H as Ty / 2 { 80 | counts[(fx < W as Ty / 2) as usize][(fy < H as Ty / 2) as usize] += 1; 81 | } 82 | 83 | if ptr == end { 84 | break; 85 | } 86 | } 87 | 88 | counts[0][0] * counts[0][1] * counts[1][0] * counts[1][1] 89 | } 90 | 91 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 92 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 93 | unsafe fn inner_part2(input: &str) -> u64 { 94 | type Ty = u16; 95 | 96 | #[repr(C, align(32))] 97 | struct Aligned(T); 98 | 99 | let mut robots_x = Aligned([MaybeUninit::::uninit(); 128]); 100 | let mut robots_y = Aligned([MaybeUninit::::uninit(); 128]); 101 | let mut robots_vx = Aligned([MaybeUninit::::uninit(); 128]); 102 | let mut robots_vy = Aligned([MaybeUninit::::uninit(); 128]); 103 | let mut offset = 0; 104 | 105 | let mut ptr = input.as_ptr().wrapping_sub(1); 106 | 107 | loop { 108 | ptr = ptr.add(3); 109 | let px = parse_pos!(ptr as Ty); 110 | *robots_x.0.get_unchecked_mut(offset).as_mut_ptr() = px; 111 | 112 | ptr = ptr.add(1); 113 | let py = parse_pos!(ptr as Ty); 114 | *robots_y.0.get_unchecked_mut(offset).as_mut_ptr() = py; 115 | 116 | ptr = ptr.add(3); 117 | let vx = parse!(ptr as Ty - W); 118 | *robots_vx.0.get_unchecked_mut(offset).as_mut_ptr() = vx; 119 | 120 | ptr = ptr.add(1); 121 | let vy = parse!(ptr as Ty - H); 122 | *robots_vy.0.get_unchecked_mut(offset).as_mut_ptr() = vy; 123 | 124 | offset += 1; 125 | 126 | if offset == 128 { 127 | break; 128 | } 129 | } 130 | 131 | macro_rules! run_loop { 132 | ($p:ident, $v:ident | $s:ident) => {{ 133 | let mut i = 0; 134 | loop { 135 | i += 1; 136 | 137 | let mut sum = u16x16::splat(0); 138 | let mut sum2 = u32x16::splat(0); 139 | 140 | for offset in 0..128 / 16 { 141 | let p = *$p.0.as_mut_ptr().cast::().add(offset); 142 | let v = *$v.0.as_ptr().cast::().add(offset); 143 | 144 | let np = p + v; 145 | let np = np.simd_min(np - u16x16::splat($s as Ty)); 146 | 147 | sum += np; 148 | sum2 += np.cast::() * np.cast::(); 149 | 150 | *$p.0.as_mut_ptr().cast::().add(offset) = np; 151 | } 152 | 153 | let sum = sum.reduce_sum() as u64; 154 | let sum2 = sum2.reduce_sum() as u64; 155 | 156 | let var = sum2 - (sum * sum / 128); 157 | 158 | if var < 540 * 128 { 159 | break i; 160 | } 161 | } 162 | }}; 163 | } 164 | 165 | let mut i = i64::MAX; 166 | let j; 167 | let mut p = &mut robots_x; 168 | let mut v = &robots_vx; 169 | let mut c = W; 170 | 171 | loop { 172 | let n = run_loop!(p, v | c); 173 | if i == i64::MAX { 174 | i = n; 175 | p = &mut robots_y; 176 | v = &robots_vy; 177 | c = H; 178 | } else { 179 | j = n; 180 | break; 181 | } 182 | } 183 | 184 | (51 * (i * H + j * W) % (W * H)) as u64 185 | } 186 | 187 | mod fastdiv { 188 | #[inline(always)] 189 | const fn compute_m_u16(d: u16) -> u32 { 190 | (u32::MAX / d as u32) + 1 191 | } 192 | 193 | #[inline(always)] 194 | const fn mul64_u16(lowbits: u32, d: u16) -> u32 { 195 | (lowbits as u64 * d as u64 >> 32) as u32 196 | } 197 | 198 | #[inline(always)] 199 | const fn fastmod_u16(a: u16, m: u32, d: u16) -> u16 { 200 | let lowbits = m.wrapping_mul(a as u32); 201 | mul64_u16(lowbits, d) as u16 202 | } 203 | 204 | #[inline(always)] 205 | pub fn fastmod_w(a: u16) -> u16 { 206 | use super::W as D; 207 | const M: u32 = compute_m_u16(D as _); 208 | fastmod_u16(a, M, D as _) 209 | } 210 | 211 | #[inline(always)] 212 | pub fn fastmod_h(a: u16) -> u16 { 213 | use super::H as D; 214 | const M: u32 = compute_m_u16(D as _); 215 | fastmod_u16(a, M, D as _) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /2024/d14p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![feature(portable_simd)] 4 | #![feature(avx512_target_feature)] 5 | #![feature(slice_ptr_get)] 6 | #![feature(array_ptr_get)] 7 | #![feature(core_intrinsics)] 8 | #![feature(int_roundings)] 9 | 10 | use std::mem::MaybeUninit; 11 | use std::simd::prelude::*; 12 | 13 | pub fn run(input: &str) -> i64 { 14 | part2(input) as i64 15 | } 16 | 17 | #[inline(always)] 18 | pub fn part1(input: &str) -> u64 { 19 | unsafe { inner_part1(input) as u64 } 20 | } 21 | 22 | #[inline(always)] 23 | pub fn part2(input: &str) -> u64 { 24 | unsafe { inner_part2(input) } 25 | } 26 | 27 | macro_rules! parse_pos { 28 | ($ptr:ident as $ty:ty) => {{ 29 | let mut n = *$ptr as $ty - b'0' as $ty; 30 | $ptr = $ptr.add(1); 31 | if *$ptr as $ty >= b'0' as $ty { 32 | n = 10 * n + *$ptr as $ty - b'0' as $ty; 33 | $ptr = $ptr.add(1); 34 | if *$ptr as $ty >= b'0' as $ty { 35 | n = 10 * n + *$ptr as $ty - b'0' as $ty; 36 | $ptr = $ptr.add(1); 37 | } 38 | } 39 | n 40 | }}; 41 | } 42 | 43 | macro_rules! parse { 44 | ($ptr:ident as $ty:ident - $m:expr) => {{ 45 | if *$ptr == b'-' { 46 | $ptr = $ptr.add(1); 47 | $m as $ty - parse_pos!($ptr as $ty) 48 | } else { 49 | parse_pos!($ptr as $ty) 50 | } 51 | }}; 52 | } 53 | 54 | const W: i64 = 101; 55 | const H: i64 = 103; 56 | 57 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 58 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 59 | unsafe fn inner_part1(input: &str) -> u64 { 60 | let mut counts = [[0; 2]; 2]; 61 | let mut ptr = input.as_ptr().wrapping_sub(1); 62 | let end = ptr.add(input.len()); 63 | 64 | type Ty = u32; 65 | 66 | loop { 67 | ptr = ptr.add(3); 68 | let px = parse_pos!(ptr as Ty); 69 | ptr = ptr.add(1); 70 | let py = parse_pos!(ptr as Ty); 71 | ptr = ptr.add(3); 72 | let vx = parse!(ptr as Ty - W); 73 | ptr = ptr.add(1); 74 | let vy = parse!(ptr as Ty - H); 75 | 76 | let fx = fastdiv::fastmod_w((px + 100 * vx) as _) as Ty; 77 | let fy = fastdiv::fastmod_h((py + 100 * vy) as _) as Ty; 78 | 79 | if fx != W as Ty / 2 && fy != H as Ty / 2 { 80 | counts[(fx < W as Ty / 2) as usize][(fy < H as Ty / 2) as usize] += 1; 81 | } 82 | 83 | if ptr == end { 84 | break; 85 | } 86 | } 87 | 88 | counts[0][0] * counts[0][1] * counts[1][0] * counts[1][1] 89 | } 90 | 91 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 92 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 93 | unsafe fn inner_part2(input: &str) -> u64 { 94 | type Ty = u16; 95 | 96 | #[repr(C, align(32))] 97 | struct Aligned(T); 98 | 99 | let mut robots_x = Aligned([MaybeUninit::::uninit(); 128]); 100 | let mut robots_y = Aligned([MaybeUninit::::uninit(); 128]); 101 | let mut robots_vx = Aligned([MaybeUninit::::uninit(); 128]); 102 | let mut robots_vy = Aligned([MaybeUninit::::uninit(); 128]); 103 | let mut offset = 0; 104 | 105 | let mut ptr = input.as_ptr().wrapping_sub(1); 106 | // let end = ptr.add(input.len()); 107 | 108 | loop { 109 | ptr = ptr.add(3); 110 | let px = parse_pos!(ptr as Ty); 111 | *robots_x.0.get_unchecked_mut(offset).as_mut_ptr() = px; 112 | 113 | ptr = ptr.add(1); 114 | let py = parse_pos!(ptr as Ty); 115 | *robots_y.0.get_unchecked_mut(offset).as_mut_ptr() = py; 116 | 117 | ptr = ptr.add(3); 118 | let vx = parse!(ptr as Ty - W); 119 | *robots_vx.0.get_unchecked_mut(offset).as_mut_ptr() = vx; 120 | 121 | ptr = ptr.add(1); 122 | let vy = parse!(ptr as Ty - H); 123 | *robots_vy.0.get_unchecked_mut(offset).as_mut_ptr() = vy; 124 | 125 | offset += 1; 126 | 127 | if offset == 128 { 128 | break; 129 | } 130 | } 131 | 132 | macro_rules! run_loop { 133 | ($p:ident, $v:ident | $s:ident) => {{ 134 | let mut i = 0; 135 | loop { 136 | i += 1; 137 | 138 | let mut sum = u16x16::splat(0); 139 | let mut sum2 = u32x16::splat(0); 140 | 141 | for offset in 0..128 / 16 { 142 | let p = *$p.0.as_mut_ptr().cast::().add(offset); 143 | let v = *$v.0.as_ptr().cast::().add(offset); 144 | 145 | let np = p + v; 146 | let np = np.simd_min(np - u16x16::splat($s as Ty)); 147 | 148 | sum += np; 149 | sum2 += np.cast::() * np.cast::(); 150 | 151 | *$p.0.as_mut_ptr().cast::().add(offset) = np; 152 | } 153 | 154 | let sum = sum.reduce_sum() as u64; 155 | let sum2 = sum2.reduce_sum() as u64; 156 | 157 | let var = sum2 - (sum * sum / 128); 158 | 159 | if var < 540 * 128 { 160 | break i; 161 | } 162 | } 163 | }}; 164 | } 165 | 166 | let mut i = i64::MAX; 167 | let j; 168 | let mut p = &mut robots_x; 169 | let mut v = &robots_vx; 170 | let mut c = W; 171 | 172 | loop { 173 | let n = run_loop!(p, v | c); 174 | if i == i64::MAX { 175 | i = n; 176 | p = &mut robots_y; 177 | v = &robots_vy; 178 | c = H; 179 | } else { 180 | j = n; 181 | break; 182 | } 183 | } 184 | 185 | (51 * (i * H + j * W) % (W * H)) as u64 186 | } 187 | 188 | mod fastdiv { 189 | #[inline(always)] 190 | const fn compute_m_u16(d: u16) -> u32 { 191 | (u32::MAX / d as u32) + 1 192 | } 193 | 194 | #[inline(always)] 195 | const fn mul64_u16(lowbits: u32, d: u16) -> u32 { 196 | (lowbits as u64 * d as u64 >> 32) as u32 197 | } 198 | 199 | #[inline(always)] 200 | const fn fastmod_u16(a: u16, m: u32, d: u16) -> u16 { 201 | let lowbits = m.wrapping_mul(a as u32); 202 | mul64_u16(lowbits, d) as u16 203 | } 204 | 205 | #[inline(always)] 206 | pub fn fastmod_w(a: u16) -> u16 { 207 | use super::W as D; 208 | const M: u32 = compute_m_u16(D as _); 209 | fastmod_u16(a, M, D as _) 210 | } 211 | 212 | #[inline(always)] 213 | pub fn fastmod_h(a: u16) -> u16 { 214 | use super::H as D; 215 | const M: u32 = compute_m_u16(D as _); 216 | fastmod_u16(a, M, D as _) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /2024/d15p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | #![feature(thread_local, portable_simd, core_intrinsics)] 3 | #![allow( 4 | clippy::precedence, 5 | clippy::missing_transmute_annotations, 6 | clippy::pointers_in_nomem_asm_block, 7 | clippy::erasing_op, 8 | static_mut_refs, 9 | internal_features, 10 | clippy::missing_safety_doc, 11 | clippy::identity_op, 12 | clippy::zero_prefixed_literal 13 | )] 14 | 15 | #[allow(unused)] 16 | use std::{ 17 | arch::{ 18 | asm, 19 | x86_64::{ 20 | __m128i, __m256i, _bextr2_u32, _mm256_madd_epi16, _mm256_maddubs_epi16, 21 | _mm256_movemask_epi8, _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, 22 | _mm_maddubs_epi16, _mm_minpos_epu16, _mm_movemask_epi8, _mm_packus_epi32, 23 | _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 24 | }, 25 | }, 26 | array, 27 | fmt::Display, 28 | hint::assert_unchecked, 29 | intrinsics::{likely, unlikely}, 30 | mem::{offset_of, transmute, MaybeUninit}, 31 | ptr, 32 | simd::prelude::*, 33 | slice, 34 | }; 35 | 36 | #[inline] 37 | unsafe fn inner1(s: &[u8]) -> u32 { 38 | static DIR_TABLE: [i16; 256] = { 39 | let mut dir_table = [0; 256]; 40 | dir_table[b'>' as usize] = 1; 41 | dir_table[b'v' as usize] = 51; 42 | dir_table[b'<' as usize] = -1; 43 | dir_table[b'<' as usize + 1] = -1; 44 | dir_table[b'^' as usize] = -51; 45 | dir_table[b'^' as usize + 1] = -1; 46 | dir_table 47 | }; 48 | static mut MAP: [u8; 2560] = [0; 2560]; 49 | let map = &mut MAP; 50 | map.copy_from_slice(s.get_unchecked(..2560)); 51 | let pos = 24usize * 51 + 24; 52 | *map.get_unchecked_mut(pos) = b'.'; 53 | 54 | asm!( 55 | "jmp 24f", 56 | // # 57 | "21:", 58 | "sub {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 59 | // . 60 | "20:", 61 | "inc {ip}", 62 | "je 99f", 63 | "24:", 64 | "movzx {inst:e}, byte ptr[{instrs} + {ip}]", 65 | "add {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 66 | "cmp byte ptr[{map} + {pos}], 46", 67 | "je 20b", 68 | "jb 21b", 69 | // O 70 | "mov {block_pos:e}, {pos:e}", 71 | "22:", 72 | // O repeats 73 | "add {block_pos:e}, dword ptr[{dir_table} + {inst} * 2]", 74 | "cmp byte ptr[{map} + {block_pos}], 46", 75 | "ja 22b", 76 | "jb 21b", 77 | // O then . 78 | "23:", 79 | "mov byte ptr[{map} + {pos}], 46", 80 | "mov byte ptr[{map} + {block_pos}], 79", 81 | "inc {ip}", 82 | "jne 24b", 83 | "99:", 84 | instrs = in(reg) s.as_ptr_range().end, 85 | ip = inout(reg) -20020isize => _, 86 | map = in(reg) map, 87 | pos = inout(reg) pos => _, 88 | inst = out(reg) _, 89 | block_pos = out(reg) _, 90 | dir_table = inout(reg) &DIR_TABLE => _, 91 | options(nostack), 92 | ); 93 | 94 | let mut map = map.as_ptr().add(52).cast::(); 95 | let mut vec_counts = u32x4::splat(0); 96 | let mut y_mult = i16x8::splat(-100); 97 | for _y in 1..49 { 98 | macro_rules! process { 99 | ($i:expr) => {{ 100 | let c = map.byte_add($i).read_unaligned(); 101 | let c = c.simd_eq(Simd::splat(b'O')); 102 | let x = c.select( 103 | u8x16::from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) 104 | + Simd::splat($i), 105 | Simd::splat(0), 106 | ); 107 | (c.to_int(), x) 108 | }}; 109 | } 110 | let (c1, x1) = process!(0); 111 | let (c2, x2) = process!(16); 112 | let (c3, x3) = process!(32); 113 | let c = c1 + c2 + c3; 114 | let c = _mm_maddubs_epi16(i8x16::splat(1).into(), c.into()); 115 | let c = _mm_madd_epi16(c, y_mult.into()); 116 | vec_counts += u32x4::from(c); 117 | let x = x1 + x2 + x3; 118 | let x = _mm_maddubs_epi16(x.into(), i8x16::splat(1).into()); 119 | let x = _mm_madd_epi16(x, i16x8::splat(1).into()); 120 | vec_counts += u32x4::from(x); 121 | y_mult -= i16x8::splat(100); 122 | map = map.byte_add(51); 123 | } 124 | 125 | vec_counts.reduce_sum() 126 | } 127 | 128 | #[inline] 129 | unsafe fn inner2(s: &[u8]) -> u32 { 130 | static DIR_TABLE: [i16; 256] = { 131 | let mut dir_table = [0; 256]; 132 | dir_table[b'>' as usize] = 1; 133 | dir_table[b'v' as usize] = 128; 134 | dir_table[b'<' as usize] = -1; 135 | dir_table[b'<' as usize + 1] = -1; 136 | dir_table[b'^' as usize] = -128; 137 | dir_table[b'^' as usize + 1] = -1; 138 | dir_table 139 | }; 140 | static mut MAP: [i8; 6400] = [-2; 6400]; 141 | let map = &mut MAP; 142 | 143 | for y in 1..49 { 144 | for x in 0..3 { 145 | let chunk = s 146 | .as_ptr() 147 | .add(y * 51 + x * 16 + 1) 148 | .cast::() 149 | .read_unaligned(); 150 | let chunk = simd_swizzle!( 151 | chunk, 152 | [ 153 | 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 154 | 12, 13, 13, 14, 14, 15, 15 155 | ] 156 | ); 157 | let a = chunk 158 | .simd_eq(Simd::splat(b'#')) 159 | .select(i8x32::splat(-2), i8x32::splat(-1)); 160 | let b = chunk.simd_eq(Simd::splat(b'O')).select( 161 | Simd::from_array([ 162 | 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 163 | 0, 1, 0, 1, 0, 1, 164 | ]), 165 | a, 166 | ); 167 | 168 | map.as_mut_ptr() 169 | .add(y * 128 + x * 32 + 2) 170 | .cast::() 171 | .write_unaligned(b); 172 | } 173 | } 174 | 175 | let pos = 24usize * 128 + 48; 176 | 177 | asm!( 178 | "jmp 24f", 179 | "21:", 180 | "sub {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 181 | "20:", 182 | "inc {ip}", 183 | "je 99f", 184 | "24:", 185 | "movzx {inst:e}, byte ptr[{instrs} + {ip}]", 186 | "add {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 187 | "cmp byte ptr[{map} + {pos}], -1", 188 | "je 20b", // . 189 | "jl 21b", // # 190 | // [] 191 | "mov {bpos:e}, {pos:e}", 192 | "mov {step:e}, dword ptr[{dir_table} + {inst} * 2]", 193 | "cmp {step:l}, -128", 194 | "je 25f", // vertical 195 | // horizontal 196 | "add {step:e}, {step:e}", 197 | "26:", 198 | "add {bpos:e}, {step:e}", 199 | "cmp byte ptr[{map} + {bpos}], -1", 200 | "jg 26b", // [] repeats 201 | "jl 21b", // [] then # // TODO optimize 202 | // [] then . 203 | "cmp byte ptr[{map} + {pos}], 0", 204 | "je 27f", // right 205 | // left 206 | "28:", 207 | "mov word ptr[{map} + {bpos}], 256", 208 | "sub {bpos:e}, {step:e}", 209 | "cmp {bpos:e}, {pos:e}", 210 | "jne 28b", 211 | "mov byte ptr[{map} + {bpos}], -1", 212 | "inc {ip}", 213 | "jne 24b", 214 | "jmp 99f", 215 | "27:", 216 | "mov word ptr[{map} + {bpos} - 1], 256", 217 | "sub {bpos:e}, {step:e}", 218 | "cmp {bpos:e}, {pos:e}", 219 | "jne 27b", 220 | "mov byte ptr[{map} + {bpos}], -1", 221 | "inc {ip}", 222 | "jne 24b", 223 | "jmp 99f", 224 | "31:", 225 | "sub {pos:e}, {step:e}", 226 | "mov rsp, {saved_rsp}", 227 | "inc {ip}", 228 | "jne 24b", 229 | "jmp 99f", 230 | "33:", 231 | "inc {bpos:e}", 232 | "30:", 233 | "sub {bpos:l}, byte ptr[{map} + {bpos}]", // align block position to left 234 | "add {bpos:e}, {step:e}", 235 | "cmp byte ptr[{map} + {bpos}], -1", 236 | "jl 31b", // # 237 | "je 32f", // . 238 | "cmp byte ptr[{map} + {bpos}], 0", 239 | "je 30b", 240 | "push {bpos}", 241 | "call 30b", 242 | "pop {bpos}", 243 | "32:", 244 | "cmp byte ptr[{map} + {bpos} + 1], -1", 245 | "jl 31b", // # 246 | "jg 33b", // [] 247 | // . 248 | "ret", 249 | "35:", 250 | "sub {bpos2:l}, byte ptr[{map} + {bpos2}]", // align block position to left 251 | "mov word ptr[{map} + {bpos2}], -1", 252 | "add {bpos2:e}, {step:e}", 253 | "cmp byte ptr[{map} + {bpos2}], 0", 254 | "push {bpos2}", 255 | "jl 36f", // done 256 | "call 35b", 257 | "mov {bpos2}, qword ptr[rsp]", 258 | "36:", 259 | "inc {bpos2:e}", 260 | "cmp byte ptr[{map} + {bpos2}], 0", 261 | "jl 37f", // done 262 | "call 35b", 263 | "37:", 264 | "pop {bpos2}", 265 | "mov word ptr[{map} + {bpos2}], 256", 266 | "ret", 267 | "25:", 268 | "mov {saved_rsp}, rsp", 269 | "mov {bpos2:e}, {bpos:e}", 270 | "call 30b", // check pushability 271 | "call 35b", // returned normally, so we can push 272 | "inc {ip}", 273 | "jne 24b", 274 | "99:", 275 | instrs = in(reg) s.as_ptr_range().end, 276 | ip = inout(reg) -20020isize => _, 277 | map = in(reg) map, 278 | pos = inout(reg) pos => _, 279 | inst = out(reg) _, 280 | bpos = out(reg) _, 281 | bpos2 = out(reg) _, 282 | step = out(reg) _, 283 | saved_rsp = out(reg) _, 284 | dir_table = inout(reg) &DIR_TABLE => _, 285 | ); 286 | 287 | let mut map = map.as_ptr().add(130).cast::(); 288 | let mut vec_counts = u32x8::splat(0); 289 | let mut y_mult = i16x16::splat(-100); 290 | for _y in 1..49 { 291 | macro_rules! process { 292 | ($i:expr) => {{ 293 | let c = map.byte_add($i).read_unaligned(); 294 | let c = c.simd_eq(Simd::splat(0)); 295 | let x = c.select( 296 | u8x32::from_array([ 297 | 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 298 | 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 299 | ]) + Simd::splat($i), 300 | Simd::splat(0), 301 | ); 302 | (c.to_int(), x) 303 | }}; 304 | } 305 | let (c1, x1) = process!(0); 306 | let (c2, x2) = process!(32); 307 | let (c3, x3) = process!(64); 308 | let c = c1 + c2 + c3; 309 | let c = _mm256_maddubs_epi16(i8x32::splat(1).into(), c.into()); 310 | let c = _mm256_madd_epi16(c, y_mult.into()); 311 | vec_counts += u32x8::from(c); 312 | let x = x1 + x2 + x3; 313 | let x = _mm256_maddubs_epi16(x.into(), i8x32::splat(1).into()); 314 | let x = _mm256_madd_epi16(x, i16x16::splat(1).into()); 315 | vec_counts += u32x8::from(x); 316 | y_mult -= i16x16::splat(100); 317 | map = map.byte_add(128); 318 | } 319 | 320 | vec_counts.reduce_sum() 321 | } 322 | 323 | #[inline] 324 | pub fn run(s: &str) -> impl Display { 325 | unsafe { inner1(s.as_bytes()) } 326 | } 327 | 328 | #[inline] 329 | pub fn part2(s: &str) -> impl Display { 330 | unsafe { inner2(s.as_bytes()) } 331 | } 332 | -------------------------------------------------------------------------------- /2024/d15p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | #![feature(thread_local, portable_simd, core_intrinsics)] 3 | #![allow( 4 | clippy::precedence, 5 | clippy::missing_transmute_annotations, 6 | clippy::pointers_in_nomem_asm_block, 7 | clippy::erasing_op, 8 | static_mut_refs, 9 | internal_features, 10 | clippy::missing_safety_doc, 11 | clippy::identity_op, 12 | clippy::zero_prefixed_literal 13 | )] 14 | 15 | #[allow(unused)] 16 | use std::{ 17 | arch::{ 18 | asm, 19 | x86_64::{ 20 | __m128i, __m256i, _bextr2_u32, _mm256_madd_epi16, _mm256_maddubs_epi16, 21 | _mm256_movemask_epi8, _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, 22 | _mm_maddubs_epi16, _mm_minpos_epu16, _mm_movemask_epi8, _mm_packus_epi32, 23 | _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 24 | }, 25 | }, 26 | array, 27 | fmt::Display, 28 | hint::assert_unchecked, 29 | intrinsics::{likely, unlikely}, 30 | mem::{offset_of, transmute, MaybeUninit}, 31 | ptr, 32 | simd::prelude::*, 33 | slice, 34 | }; 35 | 36 | #[inline] 37 | unsafe fn inner1(s: &[u8]) -> u32 { 38 | static DIR_TABLE: [i16; 256] = { 39 | let mut dir_table = [0; 256]; 40 | dir_table[b'>' as usize] = 1; 41 | dir_table[b'v' as usize] = 51; 42 | dir_table[b'<' as usize] = -1; 43 | dir_table[b'<' as usize + 1] = -1; 44 | dir_table[b'^' as usize] = -51; 45 | dir_table[b'^' as usize + 1] = -1; 46 | dir_table 47 | }; 48 | static mut MAP: [u8; 2560] = [0; 2560]; 49 | let map = &mut MAP; 50 | map.copy_from_slice(s.get_unchecked(..2560)); 51 | let pos = 24usize * 51 + 24; 52 | *map.get_unchecked_mut(pos) = b'.'; 53 | 54 | asm!( 55 | "jmp 24f", 56 | // # 57 | "21:", 58 | "sub {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 59 | // . 60 | "20:", 61 | "inc {ip}", 62 | "je 99f", 63 | "24:", 64 | "movzx {inst:e}, byte ptr[{instrs} + {ip}]", 65 | "add {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 66 | "cmp byte ptr[{map} + {pos}], 46", 67 | "je 20b", 68 | "jb 21b", 69 | // O 70 | "mov {block_pos:e}, {pos:e}", 71 | "22:", 72 | // O repeats 73 | "add {block_pos:e}, dword ptr[{dir_table} + {inst} * 2]", 74 | "cmp byte ptr[{map} + {block_pos}], 46", 75 | "ja 22b", 76 | "jb 21b", 77 | // O then . 78 | "23:", 79 | "mov byte ptr[{map} + {pos}], 46", 80 | "mov byte ptr[{map} + {block_pos}], 79", 81 | "inc {ip}", 82 | "jne 24b", 83 | "99:", 84 | instrs = in(reg) s.as_ptr_range().end, 85 | ip = inout(reg) -20020isize => _, 86 | map = in(reg) map, 87 | pos = inout(reg) pos => _, 88 | inst = out(reg) _, 89 | block_pos = out(reg) _, 90 | dir_table = inout(reg) &DIR_TABLE => _, 91 | options(nostack), 92 | ); 93 | 94 | let mut map = map.as_ptr().add(52).cast::(); 95 | let mut vec_counts = u32x4::splat(0); 96 | let mut y_mult = i16x8::splat(-100); 97 | for _y in 1..49 { 98 | macro_rules! process { 99 | ($i:expr) => {{ 100 | let c = map.byte_add($i).read_unaligned(); 101 | let c = c.simd_eq(Simd::splat(b'O')); 102 | let x = c.select( 103 | u8x16::from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) 104 | + Simd::splat($i), 105 | Simd::splat(0), 106 | ); 107 | (c.to_int(), x) 108 | }}; 109 | } 110 | let (c1, x1) = process!(0); 111 | let (c2, x2) = process!(16); 112 | let (c3, x3) = process!(32); 113 | let c = c1 + c2 + c3; 114 | let c = _mm_maddubs_epi16(i8x16::splat(1).into(), c.into()); 115 | let c = _mm_madd_epi16(c, y_mult.into()); 116 | vec_counts += u32x4::from(c); 117 | let x = x1 + x2 + x3; 118 | let x = _mm_maddubs_epi16(x.into(), i8x16::splat(1).into()); 119 | let x = _mm_madd_epi16(x, i16x8::splat(1).into()); 120 | vec_counts += u32x4::from(x); 121 | y_mult -= i16x8::splat(100); 122 | map = map.byte_add(51); 123 | } 124 | 125 | vec_counts.reduce_sum() 126 | } 127 | 128 | #[inline] 129 | unsafe fn inner2(s: &[u8]) -> u32 { 130 | static DIR_TABLE: [i16; 256] = { 131 | let mut dir_table = [0; 256]; 132 | dir_table[b'>' as usize] = 1; 133 | dir_table[b'v' as usize] = 128; 134 | dir_table[b'<' as usize] = -1; 135 | dir_table[b'<' as usize + 1] = -1; 136 | dir_table[b'^' as usize] = -128; 137 | dir_table[b'^' as usize + 1] = -1; 138 | dir_table 139 | }; 140 | static mut MAP: [i8; 6400] = [-2; 6400]; 141 | let map = &mut MAP; 142 | 143 | for y in 1..49 { 144 | for x in 0..3 { 145 | let chunk = s 146 | .as_ptr() 147 | .add(y * 51 + x * 16 + 1) 148 | .cast::() 149 | .read_unaligned(); 150 | let chunk = simd_swizzle!( 151 | chunk, 152 | [ 153 | 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 154 | 12, 13, 13, 14, 14, 15, 15 155 | ] 156 | ); 157 | let a = chunk 158 | .simd_eq(Simd::splat(b'#')) 159 | .select(i8x32::splat(-2), i8x32::splat(-1)); 160 | let b = chunk.simd_eq(Simd::splat(b'O')).select( 161 | Simd::from_array([ 162 | 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 163 | 0, 1, 0, 1, 0, 1, 164 | ]), 165 | a, 166 | ); 167 | 168 | map.as_mut_ptr() 169 | .add(y * 128 + x * 32 + 2) 170 | .cast::() 171 | .write_unaligned(b); 172 | } 173 | } 174 | 175 | let pos = 24usize * 128 + 48; 176 | 177 | asm!( 178 | "jmp 24f", 179 | "21:", 180 | "sub {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 181 | "20:", 182 | "inc {ip}", 183 | "je 99f", 184 | "24:", 185 | "movzx {inst:e}, byte ptr[{instrs} + {ip}]", 186 | "add {pos:e}, dword ptr[{dir_table} + {inst} * 2]", 187 | "cmp byte ptr[{map} + {pos}], -1", 188 | "je 20b", // . 189 | "jl 21b", // # 190 | // [] 191 | "mov {bpos:e}, {pos:e}", 192 | "mov {step:e}, dword ptr[{dir_table} + {inst} * 2]", 193 | "cmp {step:l}, -128", 194 | "je 25f", // vertical 195 | // horizontal 196 | "add {step:e}, {step:e}", 197 | "26:", 198 | "add {bpos:e}, {step:e}", 199 | "cmp byte ptr[{map} + {bpos}], -1", 200 | "jg 26b", // [] repeats 201 | "jl 21b", // [] then # // TODO optimize 202 | // [] then . 203 | "cmp byte ptr[{map} + {pos}], 0", 204 | "je 27f", // right 205 | // left 206 | "28:", 207 | "mov word ptr[{map} + {bpos}], 256", 208 | "sub {bpos:e}, {step:e}", 209 | "cmp {bpos:e}, {pos:e}", 210 | "jne 28b", 211 | "mov byte ptr[{map} + {bpos}], -1", 212 | "inc {ip}", 213 | "jne 24b", 214 | "jmp 99f", 215 | "27:", 216 | "mov word ptr[{map} + {bpos} - 1], 256", 217 | "sub {bpos:e}, {step:e}", 218 | "cmp {bpos:e}, {pos:e}", 219 | "jne 27b", 220 | "mov byte ptr[{map} + {bpos}], -1", 221 | "inc {ip}", 222 | "jne 24b", 223 | "jmp 99f", 224 | "31:", 225 | "sub {pos:e}, {step:e}", 226 | "mov rsp, {saved_rsp}", 227 | "inc {ip}", 228 | "jne 24b", 229 | "jmp 99f", 230 | "33:", 231 | "inc {bpos:e}", 232 | "30:", 233 | "sub {bpos:l}, byte ptr[{map} + {bpos}]", // align block position to left 234 | "add {bpos:e}, {step:e}", 235 | "cmp byte ptr[{map} + {bpos}], -1", 236 | "jl 31b", // # 237 | "je 32f", // . 238 | "cmp byte ptr[{map} + {bpos}], 0", 239 | "je 30b", 240 | "push {bpos}", 241 | "call 30b", 242 | "pop {bpos}", 243 | "32:", 244 | "cmp byte ptr[{map} + {bpos} + 1], -1", 245 | "jl 31b", // # 246 | "jg 33b", // [] 247 | // . 248 | "ret", 249 | "35:", 250 | "sub {bpos2:l}, byte ptr[{map} + {bpos2}]", // align block position to left 251 | "mov word ptr[{map} + {bpos2}], -1", 252 | "add {bpos2:e}, {step:e}", 253 | "cmp byte ptr[{map} + {bpos2}], 0", 254 | "push {bpos2}", 255 | "jl 36f", // done 256 | "call 35b", 257 | "mov {bpos2}, qword ptr[rsp]", 258 | "36:", 259 | "inc {bpos2:e}", 260 | "cmp byte ptr[{map} + {bpos2}], 0", 261 | "jl 37f", // done 262 | "call 35b", 263 | "37:", 264 | "pop {bpos2}", 265 | "mov word ptr[{map} + {bpos2}], 256", 266 | "ret", 267 | "25:", 268 | "mov {saved_rsp}, rsp", 269 | "mov {bpos2:e}, {bpos:e}", 270 | "call 30b", // check pushability 271 | "call 35b", // returned normally, so we can push 272 | "inc {ip}", 273 | "jne 24b", 274 | "99:", 275 | instrs = in(reg) s.as_ptr_range().end, 276 | ip = inout(reg) -20020isize => _, 277 | map = in(reg) map, 278 | pos = inout(reg) pos => _, 279 | inst = out(reg) _, 280 | bpos = out(reg) _, 281 | bpos2 = out(reg) _, 282 | step = out(reg) _, 283 | saved_rsp = out(reg) _, 284 | dir_table = inout(reg) &DIR_TABLE => _, 285 | ); 286 | 287 | let mut map = map.as_ptr().add(130).cast::(); 288 | let mut vec_counts = u32x8::splat(0); 289 | let mut y_mult = i16x16::splat(-100); 290 | for _y in 1..49 { 291 | macro_rules! process { 292 | ($i:expr) => {{ 293 | let c = map.byte_add($i).read_unaligned(); 294 | let c = c.simd_eq(Simd::splat(0)); 295 | let x = c.select( 296 | u8x32::from_array([ 297 | 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 298 | 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 299 | ]) + Simd::splat($i), 300 | Simd::splat(0), 301 | ); 302 | (c.to_int(), x) 303 | }}; 304 | } 305 | let (c1, x1) = process!(0); 306 | let (c2, x2) = process!(32); 307 | let (c3, x3) = process!(64); 308 | let c = c1 + c2 + c3; 309 | let c = _mm256_maddubs_epi16(i8x32::splat(1).into(), c.into()); 310 | let c = _mm256_madd_epi16(c, y_mult.into()); 311 | vec_counts += u32x8::from(c); 312 | let x = x1 + x2 + x3; 313 | let x = _mm256_maddubs_epi16(x.into(), i8x32::splat(1).into()); 314 | let x = _mm256_madd_epi16(x, i16x16::splat(1).into()); 315 | vec_counts += u32x8::from(x); 316 | y_mult -= i16x16::splat(100); 317 | map = map.byte_add(128); 318 | } 319 | 320 | vec_counts.reduce_sum() 321 | } 322 | 323 | #[inline] 324 | pub fn part1(s: &str) -> impl Display { 325 | unsafe { inner1(s.as_bytes()) } 326 | } 327 | 328 | #[inline] 329 | pub fn run(s: &str) -> impl Display { 330 | unsafe { inner2(s.as_bytes()) } 331 | } 332 | -------------------------------------------------------------------------------- /2024/d16p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | #![feature(thread_local, portable_simd, core_intrinsics)] 3 | #![allow( 4 | clippy::precedence, 5 | clippy::missing_transmute_annotations, 6 | clippy::pointers_in_nomem_asm_block, 7 | clippy::erasing_op, 8 | static_mut_refs, 9 | internal_features, 10 | clippy::missing_safety_doc, 11 | clippy::identity_op, 12 | clippy::zero_prefixed_literal 13 | )] 14 | 15 | #[allow(unused)] 16 | use std::{ 17 | arch::{ 18 | asm, 19 | x86_64::{ 20 | __m128i, __m256i, _bextr2_u32, _mm256_madd_epi16, _mm256_maddubs_epi16, 21 | _mm256_movemask_epi8, _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, 22 | _mm_maddubs_epi16, _mm_minpos_epu16, _mm_movemask_epi8, _mm_packus_epi32, 23 | _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 24 | }, 25 | }, 26 | array, 27 | fmt::Display, 28 | hint::assert_unchecked, 29 | intrinsics::{likely, unlikely}, 30 | mem::{offset_of, transmute, MaybeUninit}, 31 | ptr, 32 | simd::prelude::*, 33 | slice, 34 | }; 35 | 36 | macro_rules! row_len { 37 | () => { 38 | 142 39 | }; 40 | } 41 | 42 | macro_rules! side_len { 43 | () => { 44 | row_len!() - 1 45 | }; 46 | } 47 | 48 | macro_rules! far_edge { 49 | () => { 50 | row_len!() - 3 51 | }; 52 | } 53 | 54 | #[inline] 55 | unsafe fn inner1(s: &[u8]) -> u32 { 56 | static mut CURR: [Node; 65536] = [unsafe { transmute(0) }; 65536]; 57 | static mut NEXT: [Node; 65536] = [unsafe { transmute(0) }; 65536]; 58 | static OFFSET: [i16; 4] = [1, row_len!(), -1, -row_len!()]; 59 | 60 | let mut visited = [0u8; row_len!() * side_len!()]; 61 | let mut curr = &mut CURR; 62 | let mut next = &mut NEXT; 63 | let offset = &OFFSET; 64 | 65 | #[derive(Clone, Copy)] 66 | #[repr(align(4))] 67 | struct Node { 68 | pos: u16, 69 | dir: u8, 70 | cost: u8, 71 | } 72 | 73 | curr[0] = Node { 74 | pos: far_edge!() * row_len!() + 1, 75 | dir: 0, 76 | cost: 0, 77 | }; 78 | curr[1].cost = !0; 79 | 80 | let mut turn_cost = 0; 81 | loop { 82 | let mut i = 0; 83 | let mut k = 0; 84 | loop { 85 | let mut j = i; 86 | let cost = curr.get_unchecked_mut(j).cost; 87 | let next_cost = cost + 1; 88 | loop { 89 | let node = curr.get_unchecked_mut(j); 90 | let mut pos = node.pos; 91 | assert!(*s.get_unchecked(pos as usize) != b'#'); 92 | if pos == row_len!() + far_edge!() { 93 | return turn_cost + cost as u32 * 2; 94 | } 95 | let mut dir = node.dir; 96 | let visit_mask = dir & 1; 97 | 'delete: { 98 | if *visited.get_unchecked(pos as usize) & visit_mask == 0 { 99 | *visited.get_unchecked_mut(pos as usize) |= visit_mask; 100 | dir ^= 1; 101 | { 102 | let pos = pos.wrapping_add_signed(*offset.get_unchecked(dir as usize)); 103 | if *s.get_unchecked(pos as usize) != b'#' { 104 | *next.get_unchecked_mut(k) = Node { 105 | pos: pos 106 | .wrapping_add_signed(*offset.get_unchecked(dir as usize)), 107 | dir, 108 | cost: next_cost, 109 | }; 110 | k += 1; 111 | } 112 | } 113 | dir ^= 2; 114 | { 115 | let pos = pos.wrapping_add_signed(*offset.get_unchecked(dir as usize)); 116 | if *s.get_unchecked(pos as usize) != b'#' { 117 | *next.get_unchecked_mut(k) = Node { 118 | pos: pos 119 | .wrapping_add_signed(*offset.get_unchecked(dir as usize)), 120 | dir, 121 | cost: next_cost, 122 | }; 123 | k += 1; 124 | } 125 | } 126 | dir ^= 3; 127 | pos = pos.wrapping_add_signed(*offset.get_unchecked(dir as usize)); 128 | if *s.get_unchecked(pos as usize) != b'#' { 129 | node.pos = pos.wrapping_add_signed(*offset.get_unchecked(dir as usize)); 130 | node.cost = next_cost; 131 | break 'delete; 132 | } 133 | } 134 | *curr.get_unchecked_mut(j) = *curr.get_unchecked(i); 135 | i += 1; 136 | } 137 | 138 | j += 1; 139 | if curr.get_unchecked(j).cost > cost { 140 | break; 141 | } 142 | } 143 | 144 | if curr.get_unchecked(i).cost == !0 { 145 | break; 146 | } 147 | } 148 | 149 | turn_cost += 1000; 150 | (curr, next) = (next, curr); 151 | curr.get_unchecked_mut(k).cost = !0; 152 | } 153 | 154 | // let mut curr = Vec::::from_iter([Node { 155 | // pos: far_edge!() * row_len!() + 1, 156 | // dir: 0, 157 | // cost: 0, 158 | // }]); 159 | // let mut next = Vec::::from_iter([Node { 160 | // pos: !0, 161 | // dir: !0, 162 | // cost: !0, 163 | // }]); 164 | 165 | // let mut i = 0; 166 | // let mut turn_cost = 0; 167 | 168 | // let mut visited = [0u8; row_len!() * side_len!()]; 169 | 170 | // loop { 171 | // // let mut map = s.to_vec(); 172 | // // for node in &front { 173 | // // map[node.pos as usize] = b">v<^"[node.dir as usize]; 174 | // // } 175 | // // for node in &curr { 176 | // // map[node.pos as usize] = b'*'; 177 | // // } 178 | // // for node in &next { 179 | // // map[node.pos as usize] = b'+'; 180 | // // } 181 | // // for y in 0..side_len!() { 182 | // // println!( 183 | // // "{}", 184 | // // std::str::from_utf8(&map[y * row_len!()..y * row_len!() + side_len!()]).unwrap() 185 | // // ); 186 | // // } 187 | 188 | // let mut node = curr.get_unchecked_mut(i); 189 | // let curr_cost = node.cost; 190 | 191 | // let mut j = i; 192 | // loop { 193 | // let node = curr.get_unchecked_mut(j); 194 | 195 | // if *visited.get_unchecked(node.pos as usize) & 1 << node.dir != 0 { 196 | // *curr.get_unchecked_mut(j) = *curr.get_unchecked(i); 197 | // i += 1; 198 | // } else { 199 | // if node.pos == row_len!() + far_edge!() { 200 | // return turn_cost + node.cost as u32 * 2 + 2; 201 | // } 202 | 203 | // macro_rules! offset { 204 | // ($dir:expr) => { 205 | // *[1i16, row_len!(), -1, -row_len!()].get_unchecked($dir as usize) 206 | // }; 207 | // } 208 | // } 209 | 210 | // if j == curr.len() { 211 | // break; 212 | // } 213 | // } 214 | 215 | // if i == curr.len() { 216 | // (curr, next) = (next, curr); 217 | // i = 0; 218 | // turn_cost += 1000; 219 | // } 220 | 221 | // // front.retain_mut( 222 | // // |&mut Node { 223 | // // pos: ref mut pos_ref, 224 | // // dir, 225 | // // cost: ref mut cost_ref, 226 | // // }| { 227 | // // if found_end { 228 | // // return false; 229 | // // } 230 | 231 | // // let pos = *pos_ref; 232 | // // let cost = *cost_ref + 1; 233 | 234 | // // if pos == row_len!() + far_edge!() { 235 | // // found_end = true; 236 | // // return true; 237 | // // } 238 | 239 | // // macro_rules! offset { 240 | // // ($dir:expr) => { 241 | // // *[1i16, row_len!(), -1, -row_len!()].get_unchecked($dir as usize) 242 | // // }; 243 | // // } 244 | 245 | // // let off = offset!(dir); 246 | // // let mut npos = pos.wrapping_add_signed(off); 247 | // // let retain = if *s.get_unchecked(npos as usize) != b'#' && { 248 | // // npos = npos.wrapping_add_signed(off); 249 | // // *visited.get_unchecked(npos as usize) & 5 << (dir & 1) == 0 250 | // // } { 251 | // // *visited.get_unchecked_mut(npos as usize) |= 1 << dir; 252 | // // *cost_ref = cost; 253 | // // *pos_ref = npos; 254 | // // true 255 | // // } else { 256 | // // false 257 | // // }; 258 | 259 | // // let dir = dir ^ 1; 260 | // // let off = offset!(dir); 261 | // // let mut npos = pos.wrapping_add_signed(off); 262 | // // if *s.get_unchecked(npos as usize) != b'#' && { 263 | // // npos = npos.wrapping_add_signed(off); 264 | // // *visited.get_unchecked(npos as usize) & 5 << (dir & 1) == 0 265 | // // } { 266 | // // *visited.get_unchecked_mut(npos as usize) |= 1 << dir; 267 | // // next.push_back(Node { pos: npos, dir, cost }); 268 | // // } 269 | 270 | // // let dir = dir ^ 2; 271 | // // let off = offset!(dir); 272 | // // let mut npos = pos.wrapping_add_signed(off); 273 | // // if *s.get_unchecked(npos as usize) != b'#' && { 274 | // // npos = npos.wrapping_add_signed(off); 275 | // // *visited.get_unchecked(npos as usize) & 5 << (dir & 1) == 0 276 | // // } { 277 | // // *visited.get_unchecked_mut(npos as usize) |= 1 << dir; 278 | // // next.push_back(Node { pos: npos, dir, cost }); 279 | // // } 280 | 281 | // // retain 282 | // // }, 283 | // // ); 284 | 285 | // // if found_end { 286 | // // return turn_cost + front.back().unwrap_unchecked().cost as u32 * 2; 287 | // // } 288 | // } 289 | } 290 | 291 | #[inline] 292 | unsafe fn inner2(s: &[u8]) -> u32 { 293 | 0 294 | } 295 | 296 | #[inline] 297 | pub fn run(s: &str) -> impl Display { 298 | unsafe { inner1(s.as_bytes()) } 299 | } 300 | 301 | #[inline] 302 | pub fn part2(s: &str) -> impl Display { 303 | unsafe { inner2(s.as_bytes()) } 304 | } 305 | -------------------------------------------------------------------------------- /2024/d17p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | // . 3 | #![feature(thread_local, portable_simd, core_intrinsics)] 4 | #![allow( 5 | clippy::precedence, 6 | clippy::missing_transmute_annotations, 7 | clippy::pointers_in_nomem_asm_block, 8 | clippy::erasing_op, 9 | static_mut_refs, 10 | internal_features, 11 | clippy::missing_safety_doc, 12 | clippy::identity_op, 13 | clippy::zero_prefixed_literal 14 | )] 15 | 16 | #[allow(unused)] 17 | use std::{ 18 | arch::{ 19 | asm, 20 | x86_64::{ 21 | __m128i, __m256i, _bextr2_u32, _mm256_madd_epi16, _mm256_maddubs_epi16, 22 | _mm256_movemask_epi8, _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, 23 | _mm_maddubs_epi16, _mm_minpos_epu16, _mm_movemask_epi8, _mm_packus_epi32, 24 | _mm_shuffle_epi8, _mm_testc_si128, _pext_u32, 25 | }, 26 | }, 27 | array, 28 | fmt::Display, 29 | hint::assert_unchecked, 30 | intrinsics::{likely, unlikely}, 31 | mem::{offset_of, transmute, MaybeUninit}, 32 | ptr, 33 | simd::prelude::*, 34 | slice, 35 | }; 36 | 37 | #[inline] 38 | unsafe fn inner1(s: &[u8]) -> &str { 39 | static mut BUF: [u8; 17] = [b','; 17]; 40 | 41 | let chunk = s.as_ptr().add(12).cast::().read_unaligned(); 42 | let chunk = chunk - Simd::splat(b'0'); 43 | let chunk = _mm_maddubs_epi16( 44 | chunk.into(), 45 | u8x16::from_array([10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1]).into(), 46 | ); 47 | let chunk = _mm_madd_epi16( 48 | chunk, 49 | u16x8::from_array([100, 1, 100, 1, 100, 1, 100, 1]).into(), 50 | ); 51 | let chunk = _mm_packus_epi32(chunk, chunk); 52 | let chunk = _mm_madd_epi16( 53 | chunk, 54 | u16x8::from_array([10000, 1, 10000, 1, 10000, 1, 10000, 1]).into(), 55 | ); 56 | let mut a = u32x4::from(chunk)[0]; 57 | let imm1 = *s.get_unchecked(65) as u32 - b'0' as u32; 58 | let chunk = s.as_ptr().add(64).cast::().read_unaligned(); 59 | let chunk = chunk.simd_eq(Simd::from_array([ 60 | 0, 0, 0, b'1', 0, 0, 0, b'1', 0, 0, 0, b'1', 0, 0, 0, b'1', 61 | ])); 62 | let mask = chunk.to_bitmask() as u32; 63 | let offset = mask.trailing_zeros() as usize; 64 | 65 | let imm2 = *s.get_unchecked(64 + offset + 2) as u32 - b'0' as u32; 66 | 67 | let buf = &mut BUF; 68 | let mut len = s.len(); 69 | loop { 70 | let b = a % 8 ^ imm1; 71 | *buf.get_unchecked_mut(len - 91) = ((a >> b ^ b ^ imm2) % 8 + b'0' as u32) as u8; 72 | a >>= 3; 73 | len += 2; 74 | if a == 0 { 75 | break; 76 | } 77 | } 78 | 79 | std::str::from_utf8_unchecked(buf) 80 | } 81 | 82 | #[inline] 83 | unsafe fn inner2(s: &[u8]) -> u32 { 84 | 0 85 | } 86 | 87 | #[inline] 88 | pub fn run(s: &str) -> &str { 89 | unsafe { inner1(s.as_bytes()) } 90 | } 91 | 92 | #[inline] 93 | pub fn part2(s: &str) -> u32 { 94 | unsafe { inner2(s.as_bytes()) } 95 | } 96 | -------------------------------------------------------------------------------- /2024/d18p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: alion02 2 | // . 3 | #![feature(thread_local, portable_simd, core_intrinsics)] 4 | #![allow( 5 | clippy::precedence, 6 | clippy::missing_transmute_annotations, 7 | clippy::pointers_in_nomem_asm_block, 8 | clippy::erasing_op, 9 | static_mut_refs, 10 | internal_features, 11 | clippy::missing_safety_doc, 12 | clippy::identity_op, 13 | clippy::zero_prefixed_literal 14 | )] 15 | 16 | #[allow(unused)] 17 | use std::{ 18 | arch::{ 19 | asm, 20 | x86_64::{ 21 | __m128i, __m256i, _bextr2_u32, _mm256_madd_epi16, _mm256_maddubs_epi16, 22 | _mm256_movemask_epi8, _mm256_shuffle_epi8, _mm_hadd_epi16, _mm_madd_epi16, 23 | _mm_maddubs_epi16, _mm_minpos_epu16, _mm_movemask_epi8, _mm_packus_epi32, 24 | _mm_shuffle_epi8, _mm_testc_si128, _pdep_u32, _pext_u32, _pext_u64, 25 | }, 26 | }, 27 | array, 28 | fmt::Display, 29 | hint::assert_unchecked, 30 | intrinsics::{likely, unlikely}, 31 | mem::{offset_of, transmute, MaybeUninit}, 32 | ptr, 33 | simd::prelude::*, 34 | slice, 35 | }; 36 | 37 | static LUT: [i8x16; 512] = unsafe { 38 | let mut lut = [[-1i8; 16]; 512]; 39 | 40 | let mut idx = 0; 41 | while idx < 512 { 42 | let shuffle = &mut lut[idx]; 43 | 44 | let mut mask = idx << 2; 45 | if idx & 1 == 0 { 46 | mask |= 2; 47 | } 48 | mask |= 0x800; 49 | 50 | let mut slot = 0; 51 | let mut byte = 0; 52 | while slot < 8 { 53 | let zeros = mask.trailing_zeros(); 54 | match zeros { 55 | 1 => { 56 | shuffle[slot + 1] = byte; 57 | byte += 2; 58 | } 59 | 2 => { 60 | shuffle[slot] = byte; 61 | shuffle[slot + 1] = byte + 1; 62 | byte += 3; 63 | } 64 | _ => break, 65 | } 66 | mask >>= zeros + 1; 67 | slot += 2; 68 | } 69 | 70 | idx += 1; 71 | } 72 | 73 | transmute(lut) 74 | }; 75 | 76 | #[inline] 77 | unsafe fn inner1(s: &[u8]) -> u32 { 78 | let mut ptr = s.as_ptr().cast::(); 79 | let lut = &LUT; 80 | 81 | static mut MAP: [i8; 73 * 72 / 8] = [0; 73 * 72 / 8]; 82 | 83 | let map = MAP.as_mut_ptr(); 84 | for i in 0..23 { 85 | map.add(72 / 8 + i * 72 / 8 * 3) 86 | .cast::() 87 | .write_unaligned(i8x32::from_array([ 88 | !0, !0, !0, !0, !0, !0, !0, !0, !-128, !0, !0, !0, !0, !0, !0, !0, !0, !-128, !0, 89 | !0, !0, !0, !0, !0, !0, !0, !-128, !-1, !-1, !-1, !-1, !-1, 90 | ])); 91 | } 92 | map.add(69 * 72 / 8) 93 | .cast::() 94 | .write_unaligned(i8x32::from_array([ 95 | !0, !0, !0, !0, !0, !0, !0, !0, !-128, !0, !0, !0, !0, !0, !0, !0, !0, !-128, !0, !0, 96 | !0, !0, !0, !0, !0, !0, !-128, !-1, !-1, !-1, !-1, !-1, 97 | ])); 98 | 99 | macro_rules! btr { 100 | ($idx:expr) => { 101 | asm!( 102 | "btr dword ptr[{map} + {offset}], {idx:e}", 103 | map = in(reg) map, 104 | idx = in(reg) $idx, 105 | offset = const 72 / 8, 106 | options(nostack), 107 | ); 108 | }; 109 | } 110 | 111 | for _ in 0..512 { 112 | let chunk = ptr.read_unaligned(); 113 | let chunk = chunk - Simd::splat(b'0' as _); 114 | let mask = chunk.simd_lt(Simd::splat(0)).to_bitmask() as u32; 115 | let step = _pdep_u32(8, mask).trailing_zeros() + 1; 116 | let shuffle = lut.as_ptr().byte_add(((mask & 0x7FC) * 4) as usize).read(); 117 | let chunk = _mm_shuffle_epi8(chunk.into(), shuffle.into()); 118 | let chunk = _mm_maddubs_epi16(chunk, u16x8::splat(u16::from_ne_bytes([10, 1])).into()); 119 | let chunk: u32x4 = _mm_madd_epi16( 120 | chunk, 121 | u16x8::from_array([72, 1, 72, 1, 72, 1, 72, 1]).into(), 122 | ) 123 | .into(); 124 | let p1 = chunk[0]; 125 | let p2 = chunk[1]; 126 | btr!(p1); 127 | btr!(p2); 128 | ptr = ptr.byte_add(step as usize); 129 | } 130 | 131 | static mut FRONT: [u16; 256] = [0; 256]; 132 | 133 | let res: u32; 134 | 135 | asm!( 136 | "30:", 137 | "lea {next:e}, [{pos} + 1]", 138 | "btr dword ptr[{map}], {next:e}", 139 | "mov word ptr[{front} + {j} * 2], {next:x}", 140 | "adc {j:l}, 0", 141 | "lea {next:e}, [{pos} + 72]", 142 | "btr dword ptr[{map}], {next:e}", 143 | "mov word ptr[{front} + {j} * 2], {next:x}", 144 | "adc {j:l}, 0", 145 | "lea {next:e}, [{pos} - 1]", 146 | "btr dword ptr[{map}], {next:e}", 147 | "mov word ptr[{front} + {j} * 2], {next:x}", 148 | "adc {j:l}, 0", 149 | "lea {next:e}, [{pos} - 72]", 150 | "btr dword ptr[{map}], {next:e}", 151 | "mov word ptr[{front} + {j} * 2], {next:x}", 152 | "adc {j:l}, 0", 153 | "cmp {i:l}, {k:l}", 154 | "jne 20f", 155 | "mov {k:e}, {j:e}", 156 | "inc {dist:e}", 157 | "20:", 158 | "movzx {pos:e}, word ptr[{front} + {i} * 2]", 159 | "inc {i:l}", 160 | "cmp {pos:x}, {end}", 161 | "jne 30b", 162 | map = in(reg) map, 163 | pos = in(reg) 72usize, 164 | next = out(reg) _, 165 | front = in(reg) &mut FRONT, 166 | i = inout(reg) 0usize => _, 167 | j = inout(reg) 0usize => _, 168 | k = inout(reg) 0usize => _, 169 | dist = inout(reg) 0 => res, 170 | end = const 72 * 72 - 2, 171 | options(nostack), 172 | ); 173 | 174 | res 175 | } 176 | 177 | #[inline] 178 | unsafe fn inner2(s: &[u8]) -> &str { 179 | "" 180 | } 181 | 182 | #[inline] 183 | pub fn run(s: &str) -> u32 { 184 | unsafe { inner1(s.as_bytes()) } 185 | } 186 | 187 | #[inline] 188 | pub fn part2(s: &str) -> &str { 189 | unsafe { inner2(s.as_bytes()) } 190 | } 191 | -------------------------------------------------------------------------------- /2024/d18p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: caavik 2 | use std::fmt; 3 | 4 | pub fn run(input: &str) -> impl fmt::Display { 5 | unsafe { 6 | const GRID_WIDTH: usize = 71; 7 | const PADDED_WIDTH: usize = GRID_WIDTH + 2; // Don't need bounds checks 8 | const START_ID: usize = PADDED_WIDTH + 1; 9 | const END_ID: usize = GRID_WIDTH * PADDED_WIDTH + GRID_WIDTH; 10 | 11 | struct Coordinate { 12 | x: usize, 13 | y: usize, 14 | } 15 | 16 | impl fmt::Display for Coordinate { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 | write!(f, "{},{}", self.x, self.y) 19 | } 20 | } 21 | 22 | // Initialize data structures equivalent to the C# code 23 | static mut byte_order: [u16; GRID_WIDTH * GRID_WIDTH] = [0u16; GRID_WIDTH * GRID_WIDTH]; 24 | byte_order.fill(0); 25 | static mut order_lookup: [u16; PADDED_WIDTH * PADDED_WIDTH] = 26 | [u16::MAX; PADDED_WIDTH * PADDED_WIDTH]; 27 | order_lookup.fill(u16::MAX); 28 | 29 | // Mark borders as visited 30 | static mut visited: [u64; (PADDED_WIDTH * PADDED_WIDTH + 63) / 64] = 31 | [0u64; (PADDED_WIDTH * PADDED_WIDTH + 63) / 64]; 32 | visited.fill(0); 33 | let visited_len = visited.len(); 34 | let overflow = visited_len * 64 - (PADDED_WIDTH * PADDED_WIDTH); 35 | 36 | // mark borders as visited 37 | *visited.get_unchecked_mut(0) = u64::MAX; 38 | *visited.get_unchecked_mut(1) |= (1u64 << (PADDED_WIDTH - 64)) - 1; 39 | *visited.get_unchecked_mut(visited_len - 1) = u64::MAX; 40 | *visited.get_unchecked_mut(visited_len - 2) |= 41 | u64::MAX << (128 - (PADDED_WIDTH + overflow)); 42 | 43 | for i in 1..PADDED_WIDTH - 1 { 44 | let idx1 = (i * PADDED_WIDTH) / 64; 45 | let bit1 = 1u64 << ((i * PADDED_WIDTH) % 64); 46 | *visited.get_unchecked_mut(idx1) |= bit1; 47 | 48 | let idx2 = (i * PADDED_WIDTH + PADDED_WIDTH - 1) / 64; 49 | let bit2 = 1u64 << ((i * PADDED_WIDTH + PADDED_WIDTH - 1) % 64); 50 | *visited.get_unchecked_mut(idx2) |= bit2; 51 | } 52 | 53 | static mut reachable: [u64; (GRID_WIDTH * GRID_WIDTH + 63) / 64] = 54 | [0u64; (GRID_WIDTH * GRID_WIDTH + 63) / 64]; 55 | reachable.fill(0); 56 | 57 | // Parse the input and populate byte_order and order_lookup arrays 58 | let mut byte_count: u16 = 0; 59 | let input_bytes = input.as_bytes(); 60 | let mut idx = 0; 61 | while idx < input_bytes.len() { 62 | let mut x = (input_bytes.get_unchecked(idx) - b'0') as usize; 63 | idx += 1; 64 | let mut c = *input_bytes.get_unchecked(idx); 65 | idx += 1; 66 | if c != b',' { 67 | x = x * 10 + (c - b'0') as usize; 68 | idx += 1; // Skip ',' 69 | } 70 | 71 | let mut y = (*input_bytes.get_unchecked(idx) - b'0') as usize; 72 | idx += 1; 73 | c = *input_bytes.get_unchecked(idx); 74 | idx += 1; 75 | if c != b'\n' { 76 | y = y * 10 + (c - b'0') as usize; 77 | idx += 1; // Skip '\n' 78 | } 79 | 80 | x += 1; 81 | y += 1; 82 | 83 | let id = (y * PADDED_WIDTH + x) as u16; 84 | 85 | *byte_order.get_unchecked_mut(byte_count as usize) = id; 86 | *order_lookup.get_unchecked_mut(id as usize) = byte_count; 87 | byte_count += 1; 88 | } 89 | 90 | // Mark the start position (0, 0) as the final fallen byte and mark it as reachable/visited 91 | *byte_order.get_unchecked_mut(byte_count as usize) = START_ID as u16; 92 | *order_lookup.get_unchecked_mut(START_ID) = byte_count; 93 | *reachable.get_unchecked_mut((byte_count / 64) as usize) |= 1u64 << (byte_count % 64); 94 | *visited.get_unchecked_mut(START_ID / 64) |= 1u64 << (START_ID % 64); 95 | 96 | // Implement BFS using a manually managed stack 97 | let mut bfs_stack = [0u16; 128]; 98 | let mut ptr = 0usize; 99 | 100 | for i in (0..reachable.len()).rev() { 101 | loop { 102 | let reachable_i = *reachable.get_unchecked(i); 103 | let leading_zeros = reachable_i.leading_zeros() as i32; 104 | let next_in_reach = 63 - leading_zeros; 105 | if next_in_reach == -1 { 106 | break; 107 | } 108 | let max_reachable = 64 * i + next_in_reach as usize; 109 | *reachable.get_unchecked_mut(i) ^= 1u64 << next_in_reach; 110 | let max_byte = *byte_order.get_unchecked(max_reachable); 111 | *bfs_stack.get_unchecked_mut(ptr) = max_byte; 112 | ptr += 1; 113 | 114 | while ptr > 0 { 115 | ptr -= 1; 116 | let next_byte = *bfs_stack.get_unchecked(ptr); 117 | 118 | if next_byte as usize == END_ID { 119 | let result_x = (max_byte as usize % PADDED_WIDTH) - 1; 120 | let result_y = (max_byte as usize / PADDED_WIDTH) - 1; 121 | return Coordinate { 122 | x: result_x, 123 | y: result_y, 124 | }; 125 | } 126 | 127 | // Neighboring positions 128 | let neighbors = [ 129 | next_byte - PADDED_WIDTH as u16, // Up 130 | next_byte - 1, // Left 131 | next_byte + 1, // Right 132 | next_byte + PADDED_WIDTH as u16, // Down 133 | ]; 134 | 135 | for &neighbor in &neighbors { 136 | let bit = 1u64 << (neighbor as usize % 64); 137 | let idx = neighbor as usize / 64; 138 | if (*visited.get_unchecked(idx) & bit) == 0 { 139 | *visited.get_unchecked_mut(idx) |= bit; 140 | let neighbor_order = *order_lookup.get_unchecked(neighbor as usize); 141 | if neighbor_order > max_reachable as u16 { 142 | *bfs_stack.get_unchecked_mut(ptr) = neighbor; 143 | ptr += 1; 144 | } else { 145 | let order_idx = (neighbor_order / 64) as usize; 146 | *reachable.get_unchecked_mut(order_idx) |= 147 | 1u64 << (neighbor_order % 64); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | // If not found, return a default coordinate 156 | Coordinate { x: 0, y: 0 } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /2024/d19p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | 11 | use std::arch::x86_64::*; 12 | use std::simd::prelude::*; 13 | 14 | pub fn run(input: &str) -> i64 { 15 | part1(input) as i64 16 | } 17 | 18 | #[inline(always)] 19 | pub fn part1(input: &str) -> u64 { 20 | unsafe { inner_part1(input) } 21 | } 22 | 23 | #[inline(always)] 24 | pub fn part2(input: &str) -> u64 { 25 | unsafe { inner_part2(input) } 26 | } 27 | 28 | static LUT: [usize; 128] = { 29 | let mut lut = [usize::MAX; 128]; 30 | lut[b'r' as usize] = 0; 31 | lut[b'g' as usize] = 1; 32 | lut[b'b' as usize] = 2; 33 | lut[b'u' as usize] = 3; 34 | lut[b'w' as usize] = 4; 35 | lut 36 | }; 37 | 38 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 39 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 40 | unsafe fn inner_part1(input: &str) -> u64 { 41 | let input = input.as_bytes(); 42 | 43 | let mut tries = [[0u16; 5]; 1024]; 44 | let mut tries_end = [false; 1024]; 45 | let mut tries_len = 1; 46 | 47 | let mut ptr = input.as_ptr(); 48 | loop { 49 | let n = ptr.cast::().read_unaligned(); 50 | let mask = _pext_u64(n, u64::from_ne_bytes([0b00001000; 8]) | (1 << 62)); 51 | let len = mask.trailing_zeros(); 52 | std::hint::assert_unchecked(len > 0 && len <= 8); 53 | let end = ptr.add(len as usize); 54 | 55 | let mut trie = 0; 56 | loop { 57 | // let i = _pext_u64(*ptr as u64 + 13, 0b10010010) - 1; 58 | let i = *LUT.get_unchecked(*ptr as usize); 59 | 60 | let mut next = *tries.get_unchecked(trie).get_unchecked(i as usize); 61 | if next == 0 { 62 | next = tries_len; 63 | tries_len += 1; 64 | } 65 | *tries.get_unchecked_mut(trie).get_unchecked_mut(i as usize) = next; 66 | trie = next as usize; 67 | 68 | ptr = ptr.add(1); 69 | if ptr == end { 70 | break; 71 | } 72 | } 73 | 74 | *tries_end.get_unchecked_mut(trie) = true; 75 | 76 | ptr = ptr.add(2); 77 | if *ptr.sub(2) == b'\n' { 78 | break; 79 | } 80 | } 81 | 82 | let mut lines = [0; 400]; 83 | let mut lines_len = 0; 84 | let mut offset = ptr.offset_from(input.as_ptr()) as usize - 1; 85 | while offset + 32 < input.len() { 86 | let b = u8x32::from_slice(input.get_unchecked(offset..offset + 32)); 87 | let mut m = b.simd_eq(u8x32::splat(b'\n')).to_bitmask(); 88 | while m != 0 { 89 | let pos = m.trailing_zeros(); 90 | m &= !(1 << pos); 91 | *lines.get_unchecked_mut(lines_len) = offset + pos as usize + 1; 92 | lines_len += 1; 93 | } 94 | offset += 32; 95 | } 96 | while offset + 1 < input.len() { 97 | if *input.get_unchecked(offset) == b'\n' { 98 | *lines.get_unchecked_mut(lines_len) = offset + 1; 99 | lines_len += 1; 100 | } 101 | offset += 1; 102 | } 103 | 104 | let sum = std::sync::atomic::AtomicU64::new(0); 105 | let size = 400 / 16; 106 | par::par(|idx| { 107 | let chunk = lines.get_unchecked(size * idx..size * (idx + 1)); 108 | let mut count = 0; 109 | 110 | for &offset in chunk { 111 | let mut queue = 1u64; 112 | let mut to_see = u64::MAX; 113 | let base_ptr = input.as_ptr().add(offset); 114 | 115 | loop { 116 | let pos = 63 - (queue & to_see).leading_zeros(); 117 | to_see &= !(1 << pos); 118 | 119 | let mut ptr = base_ptr.add(pos as usize); 120 | let mut trie = 0; 121 | 122 | loop { 123 | let i = *LUT.get_unchecked(*ptr as usize); 124 | 125 | trie = *tries.get_unchecked(trie).get_unchecked(i) as usize; 126 | if trie == 0 { 127 | break; 128 | } 129 | 130 | ptr = ptr.add(1); 131 | 132 | let b = *tries_end.get_unchecked(trie) as u64; 133 | queue |= b << ptr.offset_from(base_ptr) as u64; 134 | 135 | if *ptr == b'\n' { 136 | break; 137 | } 138 | } 139 | 140 | if *ptr == b'\n' && *tries_end.get_unchecked(trie) { 141 | count += 1; 142 | break; 143 | } 144 | 145 | if queue & to_see == 0 { 146 | break; 147 | } 148 | } 149 | } 150 | 151 | sum.fetch_add(count, std::sync::atomic::Ordering::Relaxed); 152 | }); 153 | 154 | sum.into_inner() 155 | } 156 | 157 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 158 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 159 | unsafe fn inner_part2(input: &str) -> u64 { 160 | let input = input.as_bytes(); 161 | 162 | let mut tries = [[0u16; 5]; 1024]; 163 | let mut tries_end = [false; 1024]; 164 | let mut tries_len = 1; 165 | 166 | let mut ptr = input.as_ptr(); 167 | loop { 168 | let n = ptr.cast::().read_unaligned(); 169 | let mask = _pext_u64(n, u64::from_ne_bytes([0b00001000; 8]) | (1 << 62)); 170 | let len = mask.trailing_zeros(); 171 | let end = ptr.add(len as usize); 172 | 173 | let mut trie = 0; 174 | loop { 175 | // let i = _pext_u64(*ptr as u64 + 13, 0b10010010) - 1; 176 | let i = *LUT.get_unchecked(*ptr as usize); 177 | 178 | let mut next = *tries.get_unchecked(trie).get_unchecked(i as usize); 179 | if next == 0 { 180 | next = tries_len; 181 | tries_len += 1; 182 | } 183 | *tries.get_unchecked_mut(trie).get_unchecked_mut(i as usize) = next; 184 | trie = next as usize; 185 | 186 | ptr = ptr.add(1); 187 | if ptr == end { 188 | break; 189 | } 190 | } 191 | 192 | *tries_end.get_unchecked_mut(trie) = true; 193 | 194 | ptr = ptr.add(2); 195 | if *ptr.sub(2) == b'\n' { 196 | break; 197 | } 198 | } 199 | 200 | let mut lines = [0; 400]; 201 | let mut lines_len = 0; 202 | let mut offset = ptr.offset_from(input.as_ptr()) as usize - 1; 203 | while offset + 32 < input.len() { 204 | let b = u8x32::from_slice(input.get_unchecked(offset..offset + 32)); 205 | let mut m = b.simd_eq(u8x32::splat(b'\n')).to_bitmask(); 206 | while m != 0 { 207 | let pos = m.trailing_zeros(); 208 | m &= !(1 << pos); 209 | *lines.get_unchecked_mut(lines_len) = offset + pos as usize + 1; 210 | lines_len += 1; 211 | } 212 | offset += 32; 213 | } 214 | while offset + 1 < input.len() { 215 | if *input.get_unchecked(offset) == b'\n' { 216 | *lines.get_unchecked_mut(lines_len) = offset + 1; 217 | lines_len += 1; 218 | } 219 | offset += 1; 220 | } 221 | 222 | let sum = std::sync::atomic::AtomicU64::new(0); 223 | let size = 400 / 16; 224 | par::par(|idx| { 225 | let chunk = lines.get_unchecked(size * idx..size * (idx + 1)); 226 | let mut count = 0; 227 | 228 | for &offset in chunk { 229 | let mut queue = [0; 64]; 230 | queue[0] = 1; 231 | let mut pos = 0; 232 | 233 | let base_ptr = input.as_ptr().add(offset); 234 | let mut outer_ptr = base_ptr; 235 | 236 | loop { 237 | let n = *queue.get_unchecked(pos); 238 | 239 | if n != 0 { 240 | let mut ptr = outer_ptr; 241 | let mut trie = 0; 242 | 243 | loop { 244 | let i = *LUT.get_unchecked(*ptr as usize); 245 | 246 | trie = *tries.get_unchecked(trie).get_unchecked(i) as usize; 247 | if trie == 0 { 248 | break; 249 | } 250 | debug_assert!(trie < tries.len()); 251 | 252 | ptr = ptr.add(1); 253 | 254 | if *tries_end.get_unchecked(trie) { 255 | *queue.get_unchecked_mut(ptr.offset_from(base_ptr) as usize) += n; 256 | } 257 | 258 | if *ptr == b'\n' { 259 | break; 260 | } 261 | } 262 | } 263 | 264 | pos += 1; 265 | outer_ptr = outer_ptr.add(1); 266 | 267 | if *outer_ptr == b'\n' { 268 | count += *queue.get_unchecked(pos); 269 | break; 270 | } 271 | } 272 | } 273 | 274 | sum.fetch_add(count, std::sync::atomic::Ordering::Relaxed); 275 | }); 276 | 277 | sum.into_inner() 278 | } 279 | 280 | mod par { 281 | use std::sync::atomic::{AtomicPtr, Ordering}; 282 | 283 | pub const NUM_THREADS: usize = 16; 284 | 285 | #[repr(align(64))] 286 | struct CachePadded(T); 287 | 288 | static mut INIT: bool = false; 289 | 290 | static WORK: [CachePadded>; NUM_THREADS] = 291 | [const { CachePadded(AtomicPtr::new(std::ptr::null_mut())) }; NUM_THREADS]; 292 | 293 | #[inline(always)] 294 | fn submit(f: &F) { 295 | unsafe { 296 | if !INIT { 297 | INIT = true; 298 | for idx in 1..NUM_THREADS { 299 | thread_run(idx, f); 300 | } 301 | } 302 | } 303 | 304 | for i in 1..NUM_THREADS { 305 | WORK[i].0.store(f as *const F as *mut (), Ordering::Release); 306 | } 307 | } 308 | 309 | #[inline(always)] 310 | fn wait() { 311 | for i in 1..NUM_THREADS { 312 | loop { 313 | let ptr = WORK[i].0.load(Ordering::Acquire); 314 | if ptr.is_null() { 315 | break; 316 | } 317 | std::hint::spin_loop(); 318 | } 319 | } 320 | } 321 | 322 | fn thread_run(idx: usize, _f: &F) { 323 | _ = std::thread::Builder::new().spawn(move || unsafe { 324 | let work = WORK.get_unchecked(idx); 325 | 326 | loop { 327 | let data = work.0.load(Ordering::Acquire); 328 | if !data.is_null() { 329 | (&*data.cast::())(idx); 330 | work.0.store(std::ptr::null_mut(), Ordering::Release); 331 | } 332 | std::hint::spin_loop(); 333 | } 334 | }); 335 | } 336 | 337 | pub unsafe fn par(f: F) { 338 | submit(&f); 339 | f(0); 340 | wait(); 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /2024/d19p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | 11 | use std::arch::x86_64::*; 12 | use std::simd::prelude::*; 13 | 14 | pub fn run(input: &str) -> i64 { 15 | part2(input) as i64 16 | } 17 | 18 | #[inline(always)] 19 | pub fn part2(input: &str) -> u64 { 20 | unsafe { inner_part2(input) } 21 | } 22 | 23 | static LUT: [usize; 128] = { 24 | let mut lut = [usize::MAX; 128]; 25 | lut[b'r' as usize] = 0; 26 | lut[b'g' as usize] = 1; 27 | lut[b'b' as usize] = 2; 28 | lut[b'u' as usize] = 3; 29 | lut[b'w' as usize] = 4; 30 | lut 31 | }; 32 | 33 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 34 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 35 | unsafe fn inner_part2(input: &str) -> u64 { 36 | let input = input.as_bytes(); 37 | 38 | let mut tries = [[0u16; 5]; 1024]; 39 | let mut tries_end = [false; 1024]; 40 | let mut tries_len = 1; 41 | 42 | let mut ptr = input.as_ptr(); 43 | loop { 44 | let n = ptr.cast::().read_unaligned(); 45 | let mask = _pext_u64(n, u64::from_ne_bytes([0b00001000; 8]) | (1 << 62)); 46 | let len = mask.trailing_zeros(); 47 | let end = ptr.add(len as usize); 48 | 49 | let mut trie = 0; 50 | loop { 51 | // let i = _pext_u64(*ptr as u64 + 13, 0b10010010) - 1; 52 | let i = *LUT.get_unchecked(*ptr as usize); 53 | 54 | let mut next = *tries.get_unchecked(trie).get_unchecked(i as usize); 55 | if next == 0 { 56 | next = tries_len; 57 | tries_len += 1; 58 | } 59 | *tries.get_unchecked_mut(trie).get_unchecked_mut(i as usize) = next; 60 | trie = next as usize; 61 | 62 | ptr = ptr.add(1); 63 | if ptr == end { 64 | break; 65 | } 66 | } 67 | 68 | *tries_end.get_unchecked_mut(trie) = true; 69 | 70 | ptr = ptr.add(2); 71 | if *ptr.sub(2) == b'\n' { 72 | break; 73 | } 74 | } 75 | 76 | let mut lines = [0; 400]; 77 | let mut lines_len = 0; 78 | let mut offset = ptr.offset_from(input.as_ptr()) as usize - 1; 79 | while offset + 32 < input.len() { 80 | let b = u8x32::from_slice(input.get_unchecked(offset..offset + 32)); 81 | let mut m = b.simd_eq(u8x32::splat(b'\n')).to_bitmask(); 82 | while m != 0 { 83 | let pos = m.trailing_zeros(); 84 | m &= !(1 << pos); 85 | *lines.get_unchecked_mut(lines_len) = offset + pos as usize + 1; 86 | lines_len += 1; 87 | } 88 | offset += 32; 89 | } 90 | while offset + 1 < input.len() { 91 | if *input.get_unchecked(offset) == b'\n' { 92 | *lines.get_unchecked_mut(lines_len) = offset + 1; 93 | lines_len += 1; 94 | } 95 | offset += 1; 96 | } 97 | 98 | let sum = std::sync::atomic::AtomicU64::new(0); 99 | let size = 400 / 16; 100 | par::par(|idx| { 101 | let chunk = lines.get_unchecked(size * idx..size * (idx + 1)); 102 | let mut count = 0; 103 | 104 | for &offset in chunk { 105 | let mut queue = [0; 64]; 106 | queue[0] = 1; 107 | let mut pos = 0; 108 | 109 | let base_ptr = input.as_ptr().add(offset); 110 | let mut outer_ptr = base_ptr; 111 | 112 | loop { 113 | let n = *queue.get_unchecked(pos); 114 | 115 | if n != 0 { 116 | let mut ptr = outer_ptr; 117 | let mut trie = 0; 118 | 119 | loop { 120 | let i = *LUT.get_unchecked(*ptr as usize); 121 | 122 | trie = *tries.get_unchecked(trie).get_unchecked(i) as usize; 123 | if trie == 0 { 124 | break; 125 | } 126 | debug_assert!(trie < tries.len()); 127 | 128 | ptr = ptr.add(1); 129 | 130 | if *tries_end.get_unchecked(trie) { 131 | *queue.get_unchecked_mut(ptr.offset_from(base_ptr) as usize) += n; 132 | } 133 | 134 | if *ptr == b'\n' { 135 | break; 136 | } 137 | } 138 | } 139 | 140 | pos += 1; 141 | outer_ptr = outer_ptr.add(1); 142 | 143 | if *outer_ptr == b'\n' { 144 | count += *queue.get_unchecked(pos); 145 | break; 146 | } 147 | } 148 | } 149 | 150 | sum.fetch_add(count, std::sync::atomic::Ordering::Relaxed); 151 | }); 152 | 153 | sum.into_inner() 154 | } 155 | 156 | mod par { 157 | use std::sync::atomic::{AtomicPtr, Ordering}; 158 | 159 | pub const NUM_THREADS: usize = 16; 160 | 161 | #[repr(align(64))] 162 | struct CachePadded(T); 163 | 164 | static mut INIT: bool = false; 165 | 166 | static WORK: [CachePadded>; NUM_THREADS] = 167 | [const { CachePadded(AtomicPtr::new(std::ptr::null_mut())) }; NUM_THREADS]; 168 | 169 | #[inline(always)] 170 | fn submit(f: &F) { 171 | unsafe { 172 | if !INIT { 173 | INIT = true; 174 | for idx in 1..NUM_THREADS { 175 | thread_run(idx, f); 176 | } 177 | } 178 | } 179 | 180 | for i in 1..NUM_THREADS { 181 | WORK[i].0.store(f as *const F as *mut (), Ordering::Release); 182 | } 183 | } 184 | 185 | #[inline(always)] 186 | fn wait() { 187 | for i in 1..NUM_THREADS { 188 | loop { 189 | let ptr = WORK[i].0.load(Ordering::Acquire); 190 | if ptr.is_null() { 191 | break; 192 | } 193 | std::hint::spin_loop(); 194 | } 195 | } 196 | } 197 | 198 | fn thread_run(idx: usize, _f: &F) { 199 | _ = std::thread::Builder::new().spawn(move || unsafe { 200 | let work = WORK.get_unchecked(idx); 201 | 202 | loop { 203 | let data = work.0.load(Ordering::Acquire); 204 | if !data.is_null() { 205 | (&*data.cast::())(idx); 206 | work.0.store(std::ptr::null_mut(), Ordering::Release); 207 | } 208 | std::hint::spin_loop(); 209 | } 210 | }); 211 | } 212 | 213 | pub unsafe fn par(f: F) { 214 | submit(&f); 215 | f(0); 216 | wait(); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /2024/d20p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | 11 | use std::mem::transmute; 12 | use std::simd::prelude::*; 13 | 14 | pub fn run(input: &str) -> i64 { 15 | part1(input) as i64 16 | } 17 | 18 | #[inline(always)] 19 | pub fn part1(input: &str) -> u64 { 20 | unsafe { inner_part1(input) } 21 | } 22 | 23 | #[inline(always)] 24 | pub fn part2(input: &str) -> u64 { 25 | unsafe { inner_part2(input) } 26 | } 27 | 28 | const LEFT: usize = -1isize as usize; 29 | const RIGHT: usize = 1isize as usize; 30 | const UP: usize = -142isize as usize; 31 | const DOWN: usize = 142isize as usize; 32 | 33 | #[inline(always)] 34 | unsafe fn find_start_end(input: &[u8]) -> (usize, usize) { 35 | let mut offset = 0; 36 | let p1 = loop { 37 | let block = u8x64::from_slice(input.get_unchecked(offset..offset + 64)); 38 | let mask = block.simd_ge(u8x64::splat(b'E')).to_bitmask(); 39 | if mask != 0 { 40 | break offset + mask.trailing_zeros() as usize; 41 | } 42 | offset += 64; 43 | }; 44 | 45 | let b = (b'E' + b'S') - *input.get_unchecked(p1); 46 | 47 | offset = p1 + 1; 48 | let p2 = loop { 49 | let block = u8x64::from_slice(input.get_unchecked(offset..offset + 64)); 50 | let mask = block.simd_eq(u8x64::splat(b)).to_bitmask(); 51 | if mask != 0 { 52 | break offset + mask.trailing_zeros() as usize; 53 | } 54 | offset += 64; 55 | }; 56 | 57 | let (s, e) = if b == b'S' { (p1, p2) } else { (p2, p1) }; 58 | 59 | (s, e) 60 | } 61 | 62 | unsafe fn part1_rec( 63 | input: &[u8; 141 * 142], 64 | seen: &mut [u16; 142 * 143], 65 | curr: usize, 66 | mut n: u16, 67 | mut count: u64, 68 | ) -> u64 { 69 | macro_rules! count { 70 | ($($d:ident),*) => {$( 71 | if $d != -(DIR as isize) as usize { 72 | if *seen.get_unchecked((curr + 142).wrapping_add($d).wrapping_add($d)) >= n + 101 { 73 | count += 1; 74 | } 75 | } 76 | )*}; 77 | } 78 | 79 | count!(LEFT, RIGHT, UP, DOWN); 80 | 81 | *seen.get_unchecked_mut(curr + 142) = n; 82 | n -= 1; 83 | 84 | macro_rules! next { 85 | ($($d:ident),*) => {$( 86 | if $d != -(DIR as isize) as usize { 87 | let cand = curr.wrapping_add($d); 88 | if *input.get_unchecked(cand) != b'#' { 89 | // TODO: use become 90 | return part1_rec::<$d>(input, seen, cand, n, count) 91 | } 92 | } 93 | )*}; 94 | } 95 | 96 | next!(LEFT, RIGHT, UP, DOWN); 97 | 98 | count 99 | } 100 | 101 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 102 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 103 | unsafe fn inner_part1(input: &str) -> u64 { 104 | let input: &[u8; 141 * 142] = input.as_bytes().try_into().unwrap_unchecked(); 105 | 106 | let (s, _e) = find_start_end(input); 107 | 108 | let mut seen = [0; 142 * 143]; 109 | let mut n = u16::MAX - 102; 110 | *seen.get_unchecked_mut(s + 142) = n; 111 | n -= 1; 112 | 113 | macro_rules! next { 114 | ($($d:ident),*) => {$( 115 | let cand = s.wrapping_add($d); 116 | if *input.get_unchecked(cand) != b'#' { 117 | return part1_rec::<$d>(input, &mut seen, cand, n, 0); 118 | } 119 | )*}; 120 | } 121 | 122 | next!(LEFT, RIGHT, UP, DOWN); 123 | 124 | std::hint::unreachable_unchecked() 125 | } 126 | 127 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 128 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 129 | unsafe fn inner_part2(input: &str) -> u64 { 130 | const SLINE: usize = 139 + 28; 131 | 132 | #[inline(always)] 133 | unsafe fn next(input: &[u8], iprev: usize, icurr: usize, scurr: usize) -> (usize, usize) { 134 | const SUP: usize = -(SLINE as isize) as usize; 135 | const SDOWN: usize = SLINE; 136 | 137 | let mut inext = icurr.wrapping_add(LEFT); 138 | let mut snext = scurr.wrapping_add(LEFT); 139 | for (id, sd) in [(RIGHT, RIGHT), (UP, SUP), (DOWN, SDOWN)] { 140 | let cand = icurr.wrapping_add(id); 141 | if *input.get_unchecked(cand) != b'#' && cand != iprev { 142 | inext = icurr.wrapping_add(id); 143 | snext = scurr.wrapping_add(sd); 144 | } 145 | } 146 | (inext, snext) 147 | } 148 | 149 | let input = input.as_bytes(); 150 | 151 | let (s, e) = find_start_end(input); 152 | 153 | let mut seen = [0; 20 + (139 + 40) * SLINE]; 154 | 155 | let mut count = u32x16::splat(0); 156 | let mut iprev = 0; 157 | let mut icurr = s; 158 | let mut scurr = 20 + SLINE * (s / 142 - 1 + 20) + (s % 142 - 1); 159 | let mut n = u16::MAX / 2; 160 | 161 | loop { 162 | debug_assert_eq!(seen[scurr], 255); 163 | *seen.get_unchecked_mut(scurr) = n; 164 | 165 | (iprev, (icurr, scurr)) = (icurr, next(input, iprev, icurr, scurr)); 166 | n -= 1; 167 | 168 | const DISTS: [[u16x16; 3]; 41] = { 169 | let mut dists = [[u16::MAX / 2; 16 * 3]; 41]; 170 | 171 | let mut y = 0; 172 | while y <= 40usize { 173 | let dy = y.abs_diff(20); 174 | let mut x = 0; 175 | while x <= 40usize { 176 | let dx = x.abs_diff(20); 177 | if dx + dy <= 20 { 178 | dists[y][x] = 100 + (dx + dy) as u16; 179 | } 180 | x += 1; 181 | } 182 | y += 1; 183 | } 184 | 185 | unsafe { transmute(dists) } 186 | }; 187 | 188 | let mut offset = scurr - 20 - 20 * SLINE; 189 | let mut tmp_count = u16x16::splat(0); 190 | for line in 0..41 { 191 | for i in 0..3 { 192 | let b = 193 | u16x16::from_slice(seen.get_unchecked(offset + 16 * i..offset + 16 * (i + 1))); 194 | let m = b.simd_ge(u16x16::splat(n) + DISTS[line][i]); 195 | tmp_count += m.to_int().cast::() & u16x16::splat(1); 196 | } 197 | 198 | offset += SLINE; 199 | } 200 | count += tmp_count.cast::(); 201 | 202 | if icurr == e { 203 | break; 204 | } 205 | } 206 | 207 | #[cfg(debug_assertions)] 208 | for y in 0..139 { 209 | for x in 0..139 { 210 | if input[142 * (y + 1) + (x + 1)] == b'#' { 211 | debug_assert_eq!(seen[20 + 20 * SLINE + SLINE * y + x], 0); 212 | } 213 | } 214 | } 215 | 216 | count.cast::().reduce_sum() 217 | } 218 | -------------------------------------------------------------------------------- /2024/d20p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | 11 | use std::mem::transmute; 12 | use std::simd::prelude::*; 13 | 14 | pub fn run(input: &str) -> i64 { 15 | part2(input) as i64 16 | } 17 | 18 | #[inline(always)] 19 | pub fn part2(input: &str) -> u64 { 20 | unsafe { inner_part2(input) } 21 | } 22 | 23 | const LEFT: usize = -1isize as usize; 24 | const RIGHT: usize = 1isize as usize; 25 | const UP: usize = -142isize as usize; 26 | const DOWN: usize = 142isize as usize; 27 | 28 | #[inline(always)] 29 | unsafe fn find_start(input: &[u8]) -> usize { 30 | let mut offset = 0; 31 | loop { 32 | let block = u8x64::from_slice(input.get_unchecked(offset..offset + 64)); 33 | let mask = block.simd_eq(u8x64::splat(b'S')).to_bitmask(); 34 | if mask != 0 { 35 | break offset + mask.trailing_zeros() as usize; 36 | } 37 | offset += 64; 38 | } 39 | } 40 | 41 | const SLINE: usize = 139 + 28; 42 | const SUP: usize = -(SLINE as isize) as usize; 43 | const SDOWN: usize = SLINE; 44 | const SLEFT: usize = LEFT; 45 | const SRIGHT: usize = RIGHT; 46 | 47 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 48 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 49 | unsafe fn inner_part2(input: &str) -> u64 { 50 | unsafe fn part2_rec( 51 | input: &[u8; 141 * 142], 52 | seen: &mut [i16; 20 + (139 + 40) * SLINE], 53 | icurr: usize, 54 | scurr: usize, 55 | mut n: i16, 56 | ) { 57 | *seen.get_unchecked_mut(scurr) = n; 58 | n += 1; 59 | 60 | macro_rules! next { 61 | ($($d1:ident $d2:ident),*) => {$( 62 | if $d1 != -(IDIR as isize) as usize { 63 | let icand = icurr.wrapping_add($d1); 64 | let scand = scurr.wrapping_add($d2); 65 | if *input.get_unchecked(icand) != b'#' { 66 | // TODO: use become 67 | part2_rec::<$d1, $d2>(input, seen, icand, scand, n); 68 | return 69 | } 70 | } 71 | )*}; 72 | } 73 | 74 | next!(LEFT SLEFT, RIGHT SRIGHT, UP SUP, DOWN SDOWN); 75 | } 76 | 77 | let input: &[u8; 141 * 142] = input.as_bytes().try_into().unwrap_unchecked(); 78 | let s = find_start(input); 79 | 80 | let mut seen = [i16::MAX; 20 + (139 + 40) * SLINE]; 81 | let icurr = s; 82 | let scurr = 20 + SLINE * (s / 142 - 1 + 20) + (s % 142 - 1); 83 | let n = 0; 84 | 85 | macro_rules! next { 86 | ($($d1:ident $d2:ident),*) => {$( 87 | let icand = icurr.wrapping_add($d1); 88 | if *input.get_unchecked(icand) != b'#' { 89 | part2_rec::<$d1, $d2>(input, &mut seen, icurr, scurr, n); 90 | } 91 | )*}; 92 | } 93 | 94 | next!(LEFT SLEFT, RIGHT SRIGHT, UP SUP, DOWN SDOWN); 95 | 96 | const THREADS: usize = 16; 97 | const BASE: usize = 143; 98 | const REAL_LEN: usize = 141 * 142 - 2 * BASE + 8; 99 | const STEP: usize = REAL_LEN / THREADS; 100 | const { assert!(REAL_LEN % THREADS == 0) } 101 | 102 | let sum = std::sync::atomic::AtomicU64::new(0); 103 | par::par(|thread| { 104 | let start = BASE + thread * STEP; 105 | let end = start + STEP; 106 | 107 | let mut count = i32x16::splat(0); 108 | 109 | for icurr in start..end { 110 | if *input.get_unchecked(icurr) == b'#' || *input.get_unchecked(icurr) == b'\n' { 111 | continue; 112 | } 113 | 114 | let scurr = 20 + SLINE * (icurr / 142 - 1 + 20) + (icurr % 142 - 1); 115 | let n = *seen.get_unchecked(scurr); 116 | 117 | count += sum_cheats(scurr, n, &seen); 118 | } 119 | 120 | sum.fetch_add( 121 | -count.reduce_sum() as u64, 122 | std::sync::atomic::Ordering::Relaxed, 123 | ); 124 | }); 125 | sum.into_inner() 126 | } 127 | 128 | #[inline(always)] 129 | unsafe fn sum_cheats(scurr: usize, n: i16, seen: &[i16; 20 + (139 + 40) * SLINE]) -> i32x16 { 130 | const fn offset_distances() -> ([usize; 75], [[i16; 16]; 75]) { 131 | let mut offs = [0; 75]; 132 | let mut dists = [[i16::MAX; 16]; 75]; 133 | let mut pos = 0; 134 | 135 | let line = SLINE as isize; 136 | 137 | let (mut ls, mut le) = (-line * 20, -line * 20); 138 | let end = line * 20; 139 | let mut d = 1; 140 | while ls <= end { 141 | let mid = (ls + le) / 2; 142 | let base = (mid / line).abs(); 143 | 144 | let mut ts = ls; 145 | while ts <= le { 146 | offs[pos] = ts as usize; 147 | 148 | let mut i = 0; 149 | while i < 16 && ts + i <= le { 150 | let is = ts + i; 151 | dists[pos][i as usize] = 100 + base as i16 + is.abs_diff(mid) as i16 - 1; 152 | i += 1; 153 | } 154 | 155 | pos += 1; 156 | ts += 16; 157 | } 158 | 159 | if ls == -20 { 160 | d = -1; 161 | } 162 | 163 | ls = ls - d + line; 164 | le = le + d + line; 165 | } 166 | 167 | (offs, dists) 168 | } 169 | 170 | const OFFSETS: [usize; 75] = unsafe { transmute(offset_distances().0) }; 171 | const DISTANCES: [i16x16; 75] = unsafe { transmute(offset_distances().1) }; 172 | 173 | let mut count = i16x16::splat(0); 174 | 175 | macro_rules! handle { 176 | ($i:expr) => {{ 177 | let offset = OFFSETS[$i]; 178 | let dists = DISTANCES[$i]; 179 | let base = scurr.wrapping_add(offset); 180 | let s = seen.get_unchecked(base..base + 16); 181 | let m = (i16x16::splat(n) - dists).simd_gt(i16x16::from_slice(s)); 182 | count += m.to_int(); 183 | }}; 184 | } 185 | 186 | for i in 0..25 { 187 | handle!(i) 188 | } 189 | for i in 25..50 { 190 | handle!(i) 191 | } 192 | for i in 50..75 { 193 | handle!(i) 194 | } 195 | 196 | count.cast::() 197 | } 198 | 199 | mod par { 200 | use std::sync::atomic::{AtomicPtr, Ordering}; 201 | 202 | pub const NUM_THREADS: usize = 16; 203 | 204 | #[repr(align(64))] 205 | struct CachePadded(T); 206 | 207 | static mut INIT: bool = false; 208 | 209 | static WORK: [CachePadded>; NUM_THREADS] = 210 | [const { CachePadded(AtomicPtr::new(std::ptr::null_mut())) }; NUM_THREADS]; 211 | 212 | #[inline(always)] 213 | fn submit(f: &F) { 214 | unsafe { 215 | if !INIT { 216 | INIT = true; 217 | for idx in 1..NUM_THREADS { 218 | thread_run(idx, f); 219 | } 220 | } 221 | } 222 | 223 | for i in 1..NUM_THREADS { 224 | WORK[i].0.store(f as *const F as *mut (), Ordering::Release); 225 | } 226 | } 227 | 228 | #[inline(always)] 229 | fn wait() { 230 | for i in 1..NUM_THREADS { 231 | loop { 232 | let ptr = WORK[i].0.load(Ordering::Acquire); 233 | if ptr.is_null() { 234 | break; 235 | } 236 | std::hint::spin_loop(); 237 | } 238 | } 239 | } 240 | 241 | fn thread_run(idx: usize, _f: &F) { 242 | _ = std::thread::Builder::new().spawn(move || unsafe { 243 | let work = WORK.get_unchecked(idx); 244 | 245 | loop { 246 | let data = work.0.load(Ordering::Acquire); 247 | if !data.is_null() { 248 | (&*data.cast::())(idx); 249 | work.0.store(std::ptr::null_mut(), Ordering::Release); 250 | } 251 | std::hint::spin_loop(); 252 | } 253 | }); 254 | } 255 | 256 | pub unsafe fn par(f: F) { 257 | submit(&f); 258 | f(0); 259 | wait(); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /2024/d22p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | 11 | use std::arch::x86_64::*; 12 | use std::simd::prelude::*; 13 | 14 | pub fn run(input: &str) -> i64 { 15 | part1(input) as i64 16 | } 17 | 18 | #[inline(always)] 19 | pub fn part1(input: &str) -> u64 { 20 | unsafe { inner_part1(input) } 21 | } 22 | 23 | #[inline(always)] 24 | pub fn part2(input: &str) -> u64 { 25 | unsafe { inner_part2(input) } 26 | // super::day22par::part2(input) 27 | } 28 | 29 | #[inline(always)] 30 | pub(crate) fn parse8(n: u64) -> u32 { 31 | use std::num::Wrapping as W; 32 | 33 | let mut n = W(n); 34 | let mask = W(0xFF | (0xFF << 32)); 35 | let mul1 = W(100 + (1000000 << 32)); 36 | let mul2 = W(1 + (10000 << 32)); 37 | 38 | n = (n * W(10)) + (n >> 8); 39 | n = (((n & mask) * mul1) + (((n >> 16) & mask) * mul2)) >> 32; 40 | 41 | n.0 as u32 42 | } 43 | 44 | macro_rules! parse { 45 | ($ptr:ident) => {{ 46 | let n = $ptr.cast::().read_unaligned(); 47 | let len = _pext_u64(n, 0x1010101010101010).trailing_ones(); 48 | let n = (n & 0x0F0F0F0F0F0F0F0F) << (8 * (8 - len)); 49 | $ptr = $ptr.add(len as usize + 1); 50 | parse8(n) 51 | }}; 52 | } 53 | pub(crate) use parse; 54 | 55 | pub(crate) const M: u32 = 16777216 - 1; 56 | 57 | #[inline(always)] 58 | pub(crate) fn next(mut n: u32) -> u32 { 59 | n ^= n << 6; 60 | n ^= (n & M) >> 5; 61 | n ^= n << 11; 62 | n 63 | } 64 | 65 | // static LUT1: [u32; 1 << 24] = 66 | // unsafe { std::mem::transmute(*include_bytes!(concat!(env!("OUT_DIR"), "/d22p1.lut"))) }; 67 | 68 | #[allow(long_running_const_eval)] 69 | static LUT1: [u32; 1 << 24] = { 70 | let mut masks = [0; 24]; 71 | let mut i = 0; 72 | while i < 24 { 73 | let mut n = 1 << i; 74 | let mut j = 0; 75 | while j < 2000 { 76 | n ^= n << 6; 77 | n ^= (n & M) >> 5; 78 | n ^= n << 11; 79 | j += 1; 80 | } 81 | masks[i] = n & M; 82 | i += 1; 83 | } 84 | 85 | let mut lut = [0u32; 1 << 24]; 86 | let mut i = 0; 87 | while i < 24 { 88 | let m = masks[i]; 89 | let mut n = 0; 90 | let mn = 1 << i; 91 | while n < mn { 92 | lut[(1 << i) | n as usize] = lut[n as usize] ^ m; 93 | n += 1; 94 | } 95 | i += 1; 96 | } 97 | lut 98 | }; 99 | 100 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 101 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 102 | unsafe fn inner_part1(input: &str) -> u64 { 103 | let mut ptr = input.as_ptr(); 104 | 105 | let mut sum = 0; 106 | 107 | while ptr <= input.as_ptr().add(input.len() - 8) { 108 | let n = parse!(ptr); 109 | sum += *LUT1.get_unchecked((n & M) as usize) as u64; 110 | } 111 | 112 | if ptr != input.as_ptr().add(input.len()) { 113 | let len = input.as_ptr().add(input.len()).offset_from(ptr) - 1; 114 | let n = input 115 | .as_ptr() 116 | .add(input.len() - 1 - 8) 117 | .cast::() 118 | .read_unaligned(); 119 | let n = (n & 0x0F0F0F0F0F0F0F0F) & (u64::MAX << (8 * (8 - len))); 120 | let n = parse8(n); 121 | sum += *LUT1.get_unchecked((n & M) as usize) as u64; 122 | }; 123 | 124 | sum 125 | } 126 | 127 | #[allow(unused)] 128 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 129 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 130 | unsafe fn inner_part2(input: &str) -> u64 { 131 | let input = input.as_bytes(); 132 | 133 | const COUNTS_LEN: usize = (20usize * 20 * 20 * 20).next_multiple_of(64); 134 | let mut counts = [0u16; COUNTS_LEN]; 135 | 136 | macro_rules! handle { 137 | ($n:expr, $i:expr, $seen:ident) => {{ 138 | let mut n = $n; 139 | 140 | let b1 = fastdiv::fastmod_u32_10(n); 141 | n = next(n) & M; 142 | let b2 = fastdiv::fastmod_u32_10(n); 143 | n = next(n) & M; 144 | let b3 = fastdiv::fastmod_u32_10(n); 145 | n = next(n) & M; 146 | let mut b4 = fastdiv::fastmod_u32_10(n); 147 | 148 | let mut d1 = 9 + b1 - b2; 149 | let mut d2 = 9 + b2 - b3; 150 | let mut d3 = 9 + b3 - b4; 151 | 152 | for _ in 3..2000 { 153 | n = next(n) & M; 154 | let b5 = fastdiv::fastmod_u32_10(n); 155 | 156 | let d4 = 9 + b4 - b5; 157 | 158 | let idx = (d1 + 20 * (d2 + 20 * (d3 + 20 * d4))) as usize; 159 | let s = $seen.get_unchecked_mut(idx); 160 | if *s != $i { 161 | *s = $i; 162 | *counts.get_unchecked_mut(idx) += b5 as u16; 163 | } 164 | 165 | (d1, d2, d3, b4) = (d2, d3, d4, b5); 166 | } 167 | }}; 168 | } 169 | 170 | let mut seen = [0u8; COUNTS_LEN]; 171 | let mut i = 1; 172 | 173 | let mut ptr = input.as_ptr(); 174 | while ptr <= input.as_ptr().add(input.len() - 8) { 175 | let n = parse!(ptr); 176 | handle!(n, i, seen); 177 | i = i.wrapping_add(1); 178 | if i == 0 { 179 | i = 1; 180 | seen = [0u8; COUNTS_LEN]; 181 | } 182 | } 183 | 184 | if ptr != input.as_ptr().add(input.len()) { 185 | let len = input.as_ptr().add(input.len()).offset_from(ptr) - 1; 186 | let n = input 187 | .as_ptr() 188 | .add(input.len() - 1 - 8) 189 | .cast::() 190 | .read_unaligned(); 191 | let n = (n & 0x0F0F0F0F0F0F0F0F) & (u64::MAX << (8 * (8 - len))); 192 | let n = parse8(n); 193 | handle!(n, i, seen); 194 | } 195 | 196 | let mut max = u16x16::splat(0); 197 | for i in 0..COUNTS_LEN / 16 { 198 | let b = u16x16::from_slice(counts.get_unchecked(16 * i..16 * i + 16)); 199 | max = max.simd_max(b); 200 | } 201 | max.reduce_max() as u64 202 | } 203 | 204 | mod fastdiv { 205 | #[inline] 206 | const fn mul128_u32(lowbits: u64, d: u32) -> u64 { 207 | (lowbits as u128 * d as u128 >> 64) as u64 208 | } 209 | 210 | #[inline] 211 | const fn compute_m_u32(d: u32) -> u64 { 212 | (0xFFFFFFFFFFFFFFFF / d as u64) + 1 213 | } 214 | 215 | #[inline] 216 | pub const fn fastmod_u32_10(a: u32) -> u32 { 217 | const D: u32 = 10; 218 | const M: u64 = compute_m_u32(D); 219 | 220 | let lowbits = M.wrapping_mul(a as u64); 221 | mul128_u32(lowbits, D) as u32 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /2024/d22p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: caavik + giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | #![feature(array_chunks)] 11 | 12 | use std::arch::x86_64::*; 13 | use std::simd::prelude::*; 14 | 15 | const WSIZE: usize = 16; 16 | const LEN: usize = (1 << 24) - 1 + 2000 + WSIZE; 17 | static mut NUM_TO_INDEX: [u32; 1 << 24] = [0; 1 << 24]; 18 | static mut DIGITS: [u8; LEN] = [0; LEN]; 19 | static mut DIFFS: [u16; LEN] = [0; LEN]; 20 | static mut LAST_SEEN: [u32; LEN] = [0; LEN]; 21 | 22 | unsafe fn build_tables() { 23 | let mut i = 0; 24 | let mut n = 1; 25 | 26 | let mut next_diff_id = 0; 27 | static mut DIFF_IDS: [u16; 19 * 19 * 19 * 19] = [u16::MAX; 19 * 19 * 19 * 19]; 28 | static mut DIFF_TO_LAST_SEEN: [u32; 40951] = [0; 40951]; 29 | 30 | while i < LEN { 31 | if i < (1 << 24) - 1 { 32 | NUM_TO_INDEX[n] = i as u32; 33 | } 34 | 35 | DIGITS[i] = (n % 10) as u8; 36 | 37 | if i >= 4 { 38 | let d1 = DIGITS[i - 4] as usize; 39 | let d2 = DIGITS[i - 3] as usize; 40 | let d3 = DIGITS[i - 2] as usize; 41 | let d4 = DIGITS[i - 1] as usize; 42 | let d5 = DIGITS[i - 0] as usize; 43 | 44 | let diff = [9 + d2 - d1, 9 + d3 - d2, 9 + d4 - d3, 9 + d5 - d4] 45 | .into_iter() 46 | .fold(0, |a, d| 19 * a + d); 47 | let mut diff_id = DIFF_IDS[diff]; 48 | if diff_id == u16::MAX { 49 | diff_id = next_diff_id; 50 | DIFF_IDS[diff] = next_diff_id; 51 | next_diff_id += 1; 52 | } 53 | DIFFS[i] = diff_id; 54 | LAST_SEEN[i] = DIFF_TO_LAST_SEEN[diff_id as usize]; 55 | DIFF_TO_LAST_SEEN[diff_id as usize] = i as u32; 56 | } 57 | 58 | n ^= (n << 6) & ((1 << 24) - 1); 59 | n ^= n >> 5; 60 | n ^= (n << 11) & ((1 << 24) - 1); 61 | 62 | i += 1; 63 | } 64 | } 65 | 66 | #[cfg_attr(any(target_os = "linux"), link_section = ".text.startup")] 67 | unsafe extern "C" fn __ctor() { 68 | build_tables(); 69 | } 70 | 71 | #[used] 72 | #[cfg_attr(target_os = "linux", link_section = ".init_array")] 73 | #[cfg_attr(windows, link_section = ".CRT$XCU")] 74 | static __CTOR: unsafe extern "C" fn() = __ctor; 75 | 76 | pub fn run(input: &str) -> i64 { 77 | part2(input) as i64 78 | } 79 | 80 | #[inline(always)] 81 | pub fn part2(input: &str) -> u64 { 82 | unsafe { inner_part2(input) } 83 | } 84 | 85 | #[inline(always)] 86 | fn parse8(n: u64) -> u32 { 87 | use std::num::Wrapping as W; 88 | 89 | let mut n = W(n); 90 | let mask = W(0xFF | (0xFF << 32)); 91 | let mul1 = W(100 + (1000000 << 32)); 92 | let mul2 = W(1 + (10000 << 32)); 93 | 94 | n = (n * W(10)) + (n >> 8); 95 | n = (((n & mask) * mul1) + (((n >> 16) & mask) * mul2)) >> 32; 96 | 97 | n.0 as u32 98 | } 99 | 100 | macro_rules! parse { 101 | ($ptr:ident) => {{ 102 | let n = $ptr.cast::().read_unaligned(); 103 | let len = _pext_u64(n, 0x1010101010101010).trailing_ones(); 104 | let n = (n & 0x0F0F0F0F0F0F0F0F) << (8 * (8 - len)); 105 | $ptr = $ptr.add(len as usize + 1); 106 | parse8(n) 107 | }}; 108 | } 109 | 110 | const NUM_SEQUENCES: usize = 19 * 19 * 19 * 19; 111 | 112 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 113 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 114 | unsafe fn inner_part2(input: &str) -> u64 { 115 | static mut NUMS: [u32; 4096] = [0; 4096]; 116 | let nums = &mut NUMS; 117 | let mut nums_len = 0; 118 | 119 | let mut ptr = input.as_ptr(); 120 | while ptr <= input.as_ptr().add(input.len() - 8) { 121 | let n = parse!(ptr); 122 | 123 | *nums.get_unchecked_mut(nums_len) = n; 124 | nums_len += 1; 125 | } 126 | 127 | if ptr != input.as_ptr().add(input.len()) { 128 | let len = input.as_ptr().add(input.len()).offset_from(ptr) - 1; 129 | let n = input 130 | .as_ptr() 131 | .add(input.len() - 1 - 8) 132 | .cast::() 133 | .read_unaligned(); 134 | let n = (n & 0x0F0F0F0F0F0F0F0F) & (u64::MAX << (8 * (8 - len))); 135 | let n = parse8(n); 136 | 137 | *nums.get_unchecked_mut(nums_len) = n; 138 | nums_len += 1; 139 | }; 140 | 141 | const NUM_COUNTS: usize = NUM_SEQUENCES * par::NUM_THREADS + (16 - NUM_SEQUENCES % 16) % 16; 142 | static mut COUNTS: [u8; NUM_COUNTS] = [0; NUM_COUNTS]; 143 | COUNTS.fill(0); 144 | 145 | let nums = nums.get_unchecked_mut(..nums_len); 146 | 147 | let mut chunk_lens = [nums.len() / 8 / par::NUM_THREADS * 8; par::NUM_THREADS]; 148 | for i in 0..nums.len() / 8 % par::NUM_THREADS { 149 | chunk_lens[i] += 8; 150 | } 151 | chunk_lens[par::NUM_THREADS - 1] += nums.len() % 8; 152 | 153 | let mut chunk_pos = [0; par::NUM_THREADS + 1]; 154 | for i in 0..par::NUM_THREADS { 155 | chunk_pos[i + 1] = chunk_pos[i] + chunk_lens[i]; 156 | } 157 | 158 | par::par(|idx| { 159 | let chunk = nums.get_unchecked(chunk_pos[idx]..chunk_pos[idx + 1]); 160 | let counts = &mut *(&raw mut COUNTS).cast::<[u8; NUM_SEQUENCES]>().add(idx); 161 | 162 | for &c in chunk { 163 | let idx = *NUM_TO_INDEX.get_unchecked(c as usize) as usize; 164 | let mut curr = idx + 1; 165 | 166 | macro_rules! handle { 167 | ($min:expr) => {{ 168 | let digits = DIGITS.get_unchecked(curr..curr + WSIZE); 169 | let digits = Simd::::from_slice(digits); 170 | 171 | let diff = DIFFS.get_unchecked(curr..curr + WSIZE); 172 | 173 | let last = LAST_SEEN.get_unchecked(curr..curr + WSIZE); 174 | let last = Simd::::from_slice(last).cast::(); 175 | let mask = last.simd_lt(Simd::splat(idx as i32 + 4)); 176 | let to_sum = digits & mask.to_int().cast(); 177 | 178 | for i in $min..WSIZE { 179 | *counts.get_unchecked_mut(diff[i] as usize) += to_sum[i]; 180 | } 181 | 182 | curr += WSIZE; 183 | }}; 184 | } 185 | 186 | handle!(3); 187 | for _ in 1..2000 / WSIZE { 188 | handle!(0); 189 | } 190 | } 191 | }); 192 | 193 | let mut max = u16x16::splat(0); 194 | 195 | for i in 0..NUM_SEQUENCES.div_ceil(16) { 196 | let mut sum = u16x16::splat(0); 197 | for j in 0..par::NUM_THREADS { 198 | let b = u8x16::from_slice( 199 | COUNTS 200 | .get_unchecked(NUM_SEQUENCES * j + 16 * i..) 201 | .get_unchecked(..16), 202 | ); 203 | sum += b.cast::(); 204 | } 205 | max = max.simd_max(sum); 206 | } 207 | 208 | max.reduce_max() as u64 209 | } 210 | 211 | mod par { 212 | use std::sync::atomic::{AtomicPtr, Ordering}; 213 | 214 | pub const NUM_THREADS: usize = 16; 215 | 216 | #[repr(align(64))] 217 | struct CachePadded(T); 218 | 219 | static mut INIT: bool = false; 220 | 221 | static WORK: [CachePadded>; NUM_THREADS] = 222 | [const { CachePadded(AtomicPtr::new(std::ptr::null_mut())) }; NUM_THREADS]; 223 | 224 | #[inline(always)] 225 | fn submit(f: &F) { 226 | unsafe { 227 | if !INIT { 228 | INIT = true; 229 | for idx in 1..NUM_THREADS { 230 | thread_run(idx, f); 231 | } 232 | } 233 | } 234 | 235 | for i in 1..NUM_THREADS { 236 | WORK[i].0.store(f as *const F as *mut (), Ordering::Release); 237 | } 238 | } 239 | 240 | #[inline(always)] 241 | fn wait() { 242 | for i in 1..NUM_THREADS { 243 | loop { 244 | let ptr = WORK[i].0.load(Ordering::Acquire); 245 | if ptr.is_null() { 246 | break; 247 | } 248 | std::hint::spin_loop(); 249 | } 250 | } 251 | } 252 | 253 | fn thread_run(idx: usize, _f: &F) { 254 | _ = std::thread::Builder::new().spawn(move || unsafe { 255 | let work = WORK.get_unchecked(idx); 256 | 257 | loop { 258 | let data = work.0.load(Ordering::Acquire); 259 | if !data.is_null() { 260 | (&*data.cast::())(idx); 261 | work.0.store(std::ptr::null_mut(), Ordering::Release); 262 | } 263 | std::hint::spin_loop(); 264 | } 265 | }); 266 | } 267 | 268 | pub fn par(f: F) { 269 | submit(&f); 270 | f(0); 271 | wait(); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /2024/d24p2.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | 11 | // pub fn run(input: &str) -> i64 { 12 | // part1(input) as i64 13 | // } 14 | 15 | pub fn run(input: &str) -> &'static str { 16 | part2(input) 17 | } 18 | 19 | #[inline(always)] 20 | pub fn part1(input: &str) -> u64 { 21 | unsafe { inner_part1(input) } 22 | } 23 | 24 | #[inline(always)] 25 | pub fn part2(input: &str) -> &'static str { 26 | unsafe { inner_part2(input) } 27 | } 28 | 29 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 30 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 31 | unsafe fn inner_part1(input: &str) -> u64 { 32 | 0 33 | } 34 | 35 | static mut PART2_OUT: [u8; 8 * 3 + 7] = [b','; 8 * 3 + 7]; 36 | 37 | #[allow(unused)] 38 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 39 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 40 | unsafe fn inner_part2(input: &str) -> &'static str { 41 | let input = input.as_bytes(); 42 | 43 | // TODO: u8 ids 44 | 45 | let mut node_to_id = [u8::MAX; 23 * 26 * 26]; 46 | let mut id_to_node = [u16::MAX; 222]; 47 | let mut next_id = 46; 48 | 49 | static mut XYOPS: [[u8; 2]; 45] = [[u8::MAX; 2]; 45]; 50 | let mut xyops = &mut XYOPS; 51 | 52 | static mut OPS: [[[u8; 2]; 2]; 222] = { 53 | let mut ops = [[[u8::MAX; 2]; 2]; 222]; 54 | 55 | let mut i = 0; 56 | while i < 46 { 57 | ops[i] = [[u8::MAX - 1; 2]; 2]; 58 | i += 1; 59 | } 60 | 61 | ops 62 | }; 63 | let mut ops = &mut OPS; 64 | 65 | macro_rules! get_id { 66 | ($a:ident, $b:ident, $c:ident) => {{ 67 | let node = 68 | 26 * 26 * ($a - b'a' as usize) + 26 * ($b - b'a' as usize) + ($c - b'a' as usize); 69 | let mut id = *node_to_id.get_unchecked(node); 70 | if id == u8::MAX { 71 | id = next_id; 72 | *node_to_id.get_unchecked_mut(node) = id; 73 | *id_to_node.get_unchecked_mut(id as usize) = node as u16; 74 | next_id += 1; 75 | } 76 | id 77 | }}; 78 | } 79 | 80 | let mut ptr = input.as_ptr().add(631); 81 | let end = input.as_ptr().add(input.len()); 82 | loop { 83 | let a = *ptr as usize; 84 | let b = *ptr.add(1) as usize; 85 | let c = *ptr.add(2) as usize; 86 | ptr = ptr.add(4); 87 | 88 | if a == b'x' as usize || a == b'y' as usize { 89 | let n = 10 * (b - b'0' as usize) + (c - b'0' as usize); 90 | let off = (*ptr == b'X') as usize; 91 | 92 | ptr = ptr.add(11); 93 | 94 | let a = *ptr as usize; 95 | let b = *ptr.add(1) as usize; 96 | let c = *ptr.add(2) as usize; 97 | ptr = ptr.add(4); 98 | 99 | let out = if a == b'z' as usize { 100 | (10 * (b - b'0' as usize) + (c - b'0' as usize)) as u8 101 | } else { 102 | get_id!(a, b, c) 103 | }; 104 | 105 | *xyops.get_unchecked_mut(n).get_unchecked_mut(off) = out; 106 | } else { 107 | let n = get_id!(a, b, c); 108 | 109 | let op = *ptr; 110 | ptr = ptr.add(3); 111 | if op != b'O' { 112 | ptr = ptr.add(1); 113 | } 114 | let off = (op == b'X') as usize; 115 | 116 | let a = *ptr as usize; 117 | let b = *ptr.add(1) as usize; 118 | let c = *ptr.add(2) as usize; 119 | ptr = ptr.add(7); 120 | let m = get_id!(a, b, c); 121 | 122 | let a = *ptr as usize; 123 | let b = *ptr.add(1) as usize; 124 | let c = *ptr.add(2) as usize; 125 | ptr = ptr.add(4); 126 | 127 | let out = if a == b'z' as usize { 128 | (10 * (b - b'0' as usize) + (c - b'0' as usize)) as u8 129 | } else { 130 | get_id!(a, b, c) 131 | }; 132 | 133 | if op == b'O' { 134 | *ops.get_unchecked_mut(n as usize).get_unchecked_mut(1) = [u8::MAX; 2]; 135 | *ops.get_unchecked_mut(m as usize).get_unchecked_mut(1) = [u8::MAX; 2]; 136 | } 137 | 138 | *ops.get_unchecked_mut(n as usize).get_unchecked_mut(off) = [m, out]; 139 | *ops.get_unchecked_mut(m as usize).get_unchecked_mut(off) = [n, out]; 140 | } 141 | 142 | if ptr == end { 143 | break; 144 | } 145 | } 146 | 147 | let mut out = [u16::MAX; 8]; 148 | let mut out_len = 0; 149 | 150 | let mut carry = xyops[0][0] as usize; 151 | 152 | for n in 1..45 { 153 | let act_carry_1 = xyops[n][0] as usize; 154 | let act_res = xyops[n][1] as usize; 155 | let exp_res = ops.get_unchecked(carry)[0][0] as usize; 156 | let act_carry_2 = ops.get_unchecked(carry)[0][1] as usize; 157 | let act_z = ops.get_unchecked(carry)[1][1] as usize; 158 | 159 | if act_z >= 46 { 160 | *out.get_unchecked_mut(out_len) = act_z as u16; 161 | *out.get_unchecked_mut(out_len + 1) = n as u16; 162 | out_len += 2; 163 | 164 | debug_assert!(act_z < 222); 165 | debug_assert!(n < 222); 166 | 167 | if ops.get_unchecked(act_carry_1)[1] == [u8::MAX; 2] { 168 | carry = ops.get_unchecked(act_carry_1)[0][1] as usize; 169 | } else { 170 | carry = ops.get_unchecked(act_carry_2)[0][1] as usize; 171 | } 172 | if carry == n { 173 | carry = act_z; 174 | } 175 | } else { 176 | if act_res != exp_res { 177 | *out.get_unchecked_mut(out_len) = act_res as u16; 178 | out_len += 1; 179 | debug_assert!(act_res < 222); 180 | } 181 | 182 | if ops.get_unchecked(act_carry_1)[1] != [u8::MAX; 2] { 183 | *out.get_unchecked_mut(out_len) = act_carry_1 as u16; 184 | out_len += 1; 185 | debug_assert!(act_carry_1 < 222); 186 | } else { 187 | carry = ops.get_unchecked(act_carry_1)[0][1] as usize; 188 | } 189 | 190 | if ops.get_unchecked(act_carry_2)[1] != [u8::MAX; 2] { 191 | *out.get_unchecked_mut(out_len) = act_carry_2 as u16; 192 | out_len += 1; 193 | debug_assert!(act_carry_2 < 222); 194 | } else { 195 | carry = ops.get_unchecked(act_carry_2)[0][1] as usize; 196 | } 197 | 198 | if out_len & 1 != 0 { 199 | *out.get_unchecked_mut(out_len) = carry as u16; 200 | out_len += 1; 201 | debug_assert!(carry < 222); 202 | 203 | carry = *out.get_unchecked(out_len - 2) as usize; 204 | } 205 | } 206 | 207 | if out_len == 8 { 208 | break; 209 | } 210 | } 211 | 212 | debug_assert_eq!(out_len, 8); 213 | 214 | let mut out_chr = [[u8::MAX; 3]; 8]; 215 | for i in 0..8 { 216 | let n = out[i]; 217 | if n < 46 { 218 | out_chr[i] = [b'z', b'0' + n as u8 / 10, b'0' + n as u8 % 10]; 219 | } else { 220 | let n = id_to_node[n as usize]; 221 | out_chr[i] = [ 222 | b'a' + (n / (26 * 26)) as u8, 223 | b'a' + (n / 26 % 26) as u8, 224 | b'a' + (n % 26) as u8, 225 | ]; 226 | } 227 | } 228 | 229 | out_chr.sort_unstable(); 230 | 231 | for i in 0..8 { 232 | PART2_OUT[4 * i + 0] = out_chr[i][0]; 233 | PART2_OUT[4 * i + 1] = out_chr[i][1]; 234 | PART2_OUT[4 * i + 2] = out_chr[i][2]; 235 | } 236 | 237 | std::str::from_utf8_unchecked(&PART2_OUT) 238 | } 239 | -------------------------------------------------------------------------------- /2024/d25p1.rs: -------------------------------------------------------------------------------- 1 | // Original by: giooschi 2 | #![allow(unused_attributes)] 3 | #![allow(static_mut_refs)] 4 | #![feature(portable_simd)] 5 | #![feature(avx512_target_feature)] 6 | #![feature(slice_ptr_get)] 7 | #![feature(array_ptr_get)] 8 | #![feature(core_intrinsics)] 9 | #![feature(int_roundings)] 10 | #![feature(fn_align)] 11 | 12 | use std::simd::prelude::*; 13 | 14 | pub fn run(input: &str) -> i64 { 15 | part1(input) as i64 16 | } 17 | 18 | #[inline(always)] 19 | #[repr(align(64))] 20 | pub fn part1(input: &str) -> u64 { 21 | unsafe { inner_part1(input) } 22 | } 23 | 24 | #[inline(always)] 25 | pub fn part2(_input: &str) -> u64 { 26 | 0 27 | } 28 | 29 | #[target_feature(enable = "popcnt,avx2,ssse3,bmi1,bmi2,lzcnt")] 30 | #[cfg_attr(avx512_available, target_feature(enable = "avx512vl"))] 31 | #[repr(align(64))] 32 | unsafe fn inner_part1(input: &str) -> u64 { 33 | let input = input.as_bytes(); 34 | 35 | #[repr(C, align(64))] 36 | struct Data { 37 | list0: [u32x8; 256 / 8], 38 | list1: [u32x8; 256 / 8], 39 | } 40 | 41 | static mut DATA: Data = Data { 42 | list0: [u32x8::from_array([u32::MAX; 8]); 256 / 8], 43 | list1: [u32x8::from_array([u32::MAX; 8]); 256 / 8], 44 | }; 45 | let data = &mut DATA; 46 | let mut len0 = 0; 47 | let mut len1 = 0; 48 | 49 | let mut ptr = input.as_ptr().add(4); 50 | let end = ptr.add(43 * 500); 51 | loop { 52 | let b = ptr.cast::().read_unaligned(); 53 | let m = b.simd_eq(u8x32::splat(b'#')).to_bitmask() as u32; 54 | 55 | if *ptr == b'#' { 56 | *data.list0.as_mut_ptr().cast::().add(len0) = m; 57 | len0 += 1; 58 | } else { 59 | *data.list1.as_mut_ptr().cast::().add(len1) = m; 60 | len1 += 1; 61 | } 62 | 63 | ptr = ptr.add(43); 64 | if ptr == end { 65 | break; 66 | } 67 | } 68 | 69 | let mut count = i32x8::splat(0); 70 | 71 | for i in 0..2 { 72 | let bs = *data.list0.as_ptr().cast::<[u32x8; 12]>().add(i); 73 | for i in 0..250 { 74 | let m = *data.list1.as_ptr().cast::().add(i); 75 | for b in bs { 76 | count += (b & u32x8::splat(m)).simd_eq(u32x8::splat(0)).to_int(); 77 | } 78 | } 79 | } 80 | let bs = *data.list0.as_ptr().cast::<[u32x8; 8]>().add(3); 81 | for i in 0..250 { 82 | let m = *data.list1.as_ptr().cast::().add(i); 83 | for b in bs { 84 | count += (b & u32x8::splat(m)).simd_eq(u32x8::splat(0)).to_int(); 85 | } 86 | } 87 | 88 | -count.reduce_sum() as u64 89 | } 90 | -------------------------------------------------------------------------------- /2024/script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | get_and_save() { 4 | day=${1} 5 | part=${2} 6 | time=${3} 7 | database=../../ferris-elf/database.db 8 | query="select code from runs where day=${day} and part=${part} AND time=${time} limit 1;" 9 | file="d${day}p${part}.rs" 10 | # Fetch code from DB; 11 | # Change newline to UNIX; 12 | # Save to file. 13 | sqlite3 ${database} "${query}" | sed 's/\r$//' > ${file} 14 | rustfmt ${file} 15 | } 16 | 17 | get_and_save 1 1 9150 18 | get_and_save 1 2 4945 19 | get_and_save 2 1 3274 20 | get_and_save 2 2 3749 21 | get_and_save 3 1 2138 22 | get_and_save 3 2 2391 23 | get_and_save 4 1 3636 24 | get_and_save 4 2 691 25 | get_and_save 5 1 5467 26 | get_and_save 5 2 9440 27 | get_and_save 6 1 5527 28 | get_and_save 6 2 66803 29 | get_and_save 7 1 5413 30 | get_and_save 7 2 7516 31 | get_and_save 8 1 725 32 | get_and_save 8 2 2146 33 | get_and_save 9 1 15850 34 | get_and_save 9 2 49969 35 | get_and_save 10 1 3013 36 | get_and_save 10 2 4908 # 4488 37 | get_and_save 11 1 22 38 | get_and_save 11 2 19 39 | get_and_save 12 1 24238 40 | get_and_save 12 2 25721 41 | get_and_save 13 1 1902 42 | get_and_save 13 2 2181 # 2128 43 | get_and_save 14 1 3540 44 | get_and_save 14 2 2072 45 | get_and_save 15 1 24386 46 | get_and_save 15 2 34862 47 | get_and_save 16 1 43778 48 | get_and_save 16 2 56360 49 | get_and_save 17 1 12 50 | get_and_save 17 2 1 51 | get_and_save 18 1 2865 52 | get_and_save 18 2 12838 53 | get_and_save 19 1 12362 54 | get_and_save 19 2 18610 55 | get_and_save 20 1 16407 56 | get_and_save 20 2 47626 57 | get_and_save 21 1 3 58 | get_and_save 21 2 3 59 | get_and_save 22 1 6703 60 | get_and_save 22 2 423158 61 | get_and_save 23 1 10031 62 | get_and_save 23 2 7357 63 | get_and_save 24 1 1830 64 | get_and_save 24 2 1436 65 | get_and_save 25 1 2335 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 indiv0 and ferris-elf contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aoc-fastest 2 | 3 | *Check below for resources and optimization guides!* 4 | 5 | #### Quick Links to Solutions 6 | 7 | - [D9P2 - **Most Documented**](https://github.com/indiv0/aoc-fastest/blob/c3a2c3fa992441a481e6c15927b2cca28d715040/2024/d09p2.rs) 8 | - [D4P2 -- **Shortest Overall**](https://github.com/indiv0/aoc-fastest/blob/c3a2c3fa992441a481e6c15927b2cca28d715040/2024/d04p2.rs) 9 | - [D17P2 -- **Fastest; LUTs + ASM**](https://github.com/indiv0/aoc-fastest/blob/c3a2c3fa992441a481e6c15927b2cca28d715040/2024/d17p2.rs) 10 | - [D17P1 -- **Shortest w/ X86_64 Intrinsics**](https://github.com/indiv0/aoc-fastest/blob/c3a2c3fa992441a481e6c15927b2cca28d715040/2024/d17p1.rs) 11 | 12 | ## Results 13 | 14 | here's the total of the fastest times for each day so far: 15 | ``` 16 | day | part | time | user | source available 17 | --- | ---- | ------- | --------------- | ---------------- 18 | 1 | 1 | 9150 | doge | yes 19 | 1 | 2 | 4945 | doge | yes 20 | 2 | 1 | 3274 | giooschi | yes 21 | 2 | 2 | 3749 | giooschi | yes 22 | 3 | 1 | 2138 | alion02 | yes 23 | 3 | 2 | 2391 | ameo | yes 24 | 4 | 1 | 3636 | giooschi | yes 25 | 4 | 2 | 691 | bendn | yes 26 | 5 | 1 | 5467 | giooschi | yes 27 | 5 | 2 | 9440 | giooschi | yes 28 | 6 | 1 | 5527 | doge | yes 29 | 6 | 2 | 66803 | giooschi | yes 30 | 7 | 1 | 5413 | giooschi | yes 31 | 7 | 2 | 7516 | giooschi | yes 32 | 8 | 1 | 725 | alion02 | yes 33 | 8 | 2 | 2146 | bendn | yes 34 | 9 | 1 | 15850 | alion02 | yes 35 | 9 | 2 | 49969 | ameo | yes 36 | 10 | 1 | 3013 | giooschi | yes 37 | 10 | 2 | 4488 | _mwlsk | no* 38 | 11 | 1 | 22 | giooschi | yes 39 | 11 | 2 | 19 | giooschi | yes 40 | 12 | 1 | 24238 | giooschi | yes 41 | 12 | 2 | 25721 | giooschi | yes 42 | 13 | 1 | 1902 | alion02 | yes 43 | 13 | 2 | 2128 | goldsteinq | no* 44 | 14 | 1 | 3540 | giooschi | yes 45 | 14 | 2 | 2072 | giooschi | yes 46 | 15 | 1 | 24386 | alion02 | yes 47 | 15 | 2 | 34862 | alion02 | yes 48 | 16 | 1 | 43778 | alion02 | yes 49 | 16 | 2 | 56360 | giooschi | yes 50 | 17 | 1 | 12 | alion02 | yes 51 | 17 | 2 | 1 | alion02 | yes 52 | 18 | 1 | 2865 | alion02 | yes 53 | 18 | 2 | 12838 | caavik | yes 54 | 19 | 1 | 12362 | giooschi | yes 55 | 19 | 2 | 18610 | giooschi | yes 56 | 20 | 1 | 16407 | giooschi | yes 57 | 20 | 2 | 47626 | giooschi | yes 58 | 21 | 1 | 3 | bendn/giooschi | yes 59 | 21 | 2 | 3 | bendn/giooschi | yes 60 | 22 | 1 | 6703 | giooschi | yes 61 | 22 | 2 | 423158 | caavik+giooschi | yes 62 | 23 | 1 | 10031 | giooschi | yes 63 | 23 | 2 | 7357 | giooschi | yes 64 | 24 | 1 | 1830 | giooschi | yes 65 | 24 | 2 | 1436 | giooschi | yes 66 | 25 | 1 | 2335 | giooschi | yes 67 | --------------------------------------------------------- 68 | 988936ns 69 | ``` 70 | for a total of 989us! 71 | 72 | For any entry where source available is `no*`, the next fastest solution is 73 | shown instead because the author of the fastest solution has not yet agreed to 74 | have their code displayed here. 75 | 76 | # Further Reading 77 | 78 | IMO the best way to learn is to participate, which is why I highly encourage people to try to optimize AoC solutions themselves. It's a **fantastic** way to learn SIMD. If you decide to do so, absolutely join the [Rust Programming Language Community discord server](https://discord.gg/rust-lang-community)! It's a wonderful community with incredibly talented and knowledgeable folks who are happy to help you optimize. I've learned about topics like instruction pipelines, cache misses, and SIMD just by following the discussions there! 79 | 80 | In-depth explanations of these topics would be super helpful. I hope to some day write those explanations myself. 81 | 82 | In the meantime, if you would like a more in-depth explanation of some of the optimization techniques used, I highly recommend you check out this article by ameo (one of our participants). It covers the process they used to optimize their solution for Day 9 Part 2, and how they got it to the top of our leaderboard. The article provides incredible information on the process of both high-level and micro optimization: 83 | 84 | - [Optimizing Advent of Code D9P2 with High-Performance Rust](https://cprimozic.net/blog/optimizing-advent-of-code-2024/) 85 | 86 | Also check out the following: 87 | 88 | - [Algorithms for Modern Hardware](https://en.algorithmica.org/hpc/) 89 | - [Optimising my Rust solutions for Advent of Code ](https://nindalf.com/posts/optimising-rust/) 90 | - [500 ⭐ in less than a second (Comment)](https://old.reddit.com/r/adventofcode/comments/1hlyocd/500_in_less_than_a_second/m3pyxdk/) 91 | - [500 ⭐ in less than a second (Repo)](https://github.com/maneatingape/advent-of-code-rust) 92 | - [One Billion Row Challenge](https://curiouscoding.nl/posts/1brc/) 93 | 94 | 95 | # Credits 96 | 97 | - Thank you to the members of the `Rust Programming Language Community` and `Serenity-rs` Discord servers and everyone else who participated in the challenge! 98 | - Thank you to Eric Wastl for hosting AoC every year! 99 | - Thank you to [Noxim](https://github.com/noxime) for writing the original version of our [benchmark bot](https://github.com/indiv0/ferris-elf). 100 | - Extra special thank you to [yuyuko](https://github.com/ultrabear), [bend-n](https://github.com/bend-n/), and [giooschi](https://github.com/SkiFire13/) for their help in maintaining and improving our benchmark bot. 101 | 102 | This repo contains code/optimizations from the following authors: 103 | 104 | - giooschi/skifire13: https://github.com/SkiFire13 105 | - alion02: https://github.com/alion02 106 | - caavik: https://github.com/CameronAavik 107 | - void*/\_\_main\_character\_\_ 108 | - ameo https://github.com/Ameobea/advent-of-code-2024 109 | - See also: https://cprimozic.net/blog/optimizing-advent-of-code-2024/ 110 | - doge 111 | - bend-n https://github.com/bend-n/ 112 | 113 | Thank you so much to these talented individuals for participating in AoC with us! 114 | I highly encourage you to checkout their repos for more details and examples. 115 | These people are incredibly talented at what they do. 116 | 117 | This repo contains code submitted to the https://github.com/indiv0/ferris-elf bot, by multiple users. 118 | Code is only included in this repo if the authors have explicitly provided permission to post their code here, so some solutions may not be present. 119 | 120 | # Background 121 | 122 | This year, some members of the [Rust Programming Language Community Server](https://discord.gg/rust-lang-community) on Discord set out to solve AoC in under 1ms. I'm pleased to announce that through the use of LUTs, SIMD, more-than-questionable `unsafe`, assertions, LLVM intrinsics, and even some inline ASM that goal has been reached! 123 | 124 | If you are interested, join us in #advent-of-code-2024 on the [Discord server](https://discord.gg/rust-lang-community) for further discussion :) 125 | 126 | # Context/Caveats 127 | 128 | - All submissions were run on the same hardware (Ryzen 5950X) to ensure consistency, with the same compiler flags and features available. This was on rustc nightly (updated throughout the course of the contest), and with CPU speed capped at 3400 MHz with boost clock disabled. 129 | - AVX-512 was not available on the machine so none (?) of the solutions utilize that particular set of accelerated instructions, but there is *plenty* of other SIMD in use. 130 | - All submissions were run against the same inputs to ensure consistency. 131 | - Caching anything that has been fed with input was not allowed to prevent cheating and/or trivial solutions like `Map`. 132 | - For the same reason, inputs were not directly available to the participants, and were not provided at compile-time. 133 | - Participants were allowed to use compile-time tricks in their answers. Due to limitations in the benchmark bot, the runtime of these optimizations could not be measured. This was considered acceptable as the compiled binaries were expected to otherwise work correctly for arbitrary inputs. This means that participants are allowed to use look-up tables (LUTs) in their answers, but those LUTs are expected to work for arbitrary inputs, not just specific ones. 134 | - I/O is trivial, and was thus not measured as part of the benchmark. That is, participants were provided with an `&str` or `&[u8]` input (their choice) and expected to provide an `impl Display` as part of their result. Therefore, *input parsing was measured*. 135 | --------------------------------------------------------------------------------