derive.rs
22.5 kB · rust · 735 lines
1use crate::root_floor;2use crate::transfer::{levels, masks_seen, rung, show};34// PATTERN LAW56fn mask_of(steps: [i64; 4], reach: usize) -> u16 {7 let mut mask = 0u16;8 for column in 0..reach.min(3) {9 let low = steps[column + 1].max(0);10 let high = steps[column].min(2);11 let mut row = low;12 while row <= high {13 mask |= 1 << (3 * column as u16 + row as u16);14 row += 1;15 }16 }17 mask18}1920fn window(steps: [i64; 4]) -> Option<(i64, i64)> {21 let mut low = i64::MIN;22 let mut high = i64::MAX;23 for near in 0..4usize {24 for far in 0..4usize {25 if far > near {26 let gap = (far - near) as i64;27 low = low.max(6 * (steps[near] - steps[far] - 1) / gap);28 }29 if far < near {30 let gap = (near - far) as i64;31 high = high.min(6 * (steps[far] - steps[near] + 1) / gap);32 }33 }34 }35 let floor = low.max(0);36 if high > floor {37 Some((floor, high))38 } else {39 None40 }41}4243// LINE ALPHABET4445fn line_alphabet(span: i64) -> Vec<(u16, i64, i64)> {46 let mut found: Vec<Option<(i64, i64)>> = vec![None; 512];47 for a in -span..=span {48 for b in -span..=span {49 for c in -span..=span {50 for d in -span..=span {51 let steps = [a, b, c, d];52 let Some((low, high)) = window(steps) else {53 continue;54 };55 let mask = mask_of(steps, 3);56 if mask == 0 {57 continue;58 }59 let slot = &mut found[mask as usize];60 match slot {61 None => *slot = Some((low, high)),62 Some(range) => {63 range.0 = range.0.min(low);64 range.1 = range.1.max(high);65 }66 }67 }68 }69 }70 }71 (0..512)72 .filter_map(|mask| found[mask].map(|range| (mask as u16, range.0, range.1)))73 .collect()74}7576// SHELL AGAINST THE LINE7778fn compare(label: &str, radius: u64) {79 let deep = levels(radius);80 let seen = masks_seen(radius, 1, deep - 3);81 let all = masks_seen(radius, 1, deep);82 let alphabet = line_alphabet(9);83 let line: Vec<u16> = alphabet.iter().map(|entry| entry.0).collect();84 let outside: Vec<u16> = seen.iter().cloned().filter(|m| !line.contains(m)).collect();85 let missing: Vec<u16> = line.iter().cloned().filter(|m| !seen.contains(m)).collect();86 let curved: Vec<u16> = all.iter().cloned().filter(|m| !line.contains(m)).collect();87 assert!(outside.is_empty() && missing.is_empty());88 assert_eq!(curved.len(), 1);89 println!(90 "alphabet {} r={} line={} shell={} shell_outside_line=[{}] line_missing_from_shell=[{}] every_level_outside_line=[{}]",91 label,92 radius,93 line.len(),94 seen.len(),95 show(&outside),96 show(&missing),97 show(&curved)98 );99}100101// FROZEN SLOPE102103fn frozen(label: &str, radius: u64) {104 let deep = levels(radius);105 for level in 0..deep.saturating_sub(1) {106 let below = rung(radius, level);107 let here = rung(radius, level + 1);108 let scale = 3u64.pow(level) as f64;109 let far = (radius * radius) as f64;110 let top_here = here.high.len() - 2;111 let top_below = below.high.len() - 2;112 let mut columns = 0u64;113 let mut column_hits = 0u64;114 let mut boxes = 0u64;115 let mut faults = 0u64;116 let mut chord_faults = 0u64;117 let mut shallow = 0u64;118 let mut shallow_faults = 0u64;119 let mut worst = 0usize;120 for i in 0..=top_here {121 columns += 1;122 let edge = (3 * i) as f64 * scale;123 let height = (far - edge * edge).max(0.0).sqrt() / scale;124 if height <= 0.0 {125 continue;126 }127 let slope = (3 * i) as f64 / height;128 let far_edge = (3 * (i + 1)) as f64 * scale;129 let end = (far - far_edge * far_edge).max(0.0).sqrt() / scale;130 let chord = (height - end) / 3.0;131 let mut guess = [0i64; 4];132 let mut rope = [0i64; 4];133 guess[0] = below.high[3 * i] as i64;134 rope[0] = guess[0];135 for k in 1..4usize {136 guess[k] = (height - k as f64 * slope).floor() as i64;137 rope[k] = (height - k as f64 * chord).floor() as i64;138 }139 let reach = (top_below + 1).saturating_sub(3 * i).min(3);140 let mut exact = true;141 for k in 1..4usize {142 if 3 * i + k <= top_below + 1 && guess[k] != below.high[3 * i + k] as i64 {143 exact = false;144 }145 }146 if exact {147 column_hits += 1;148 }149 let low = here.high[i + 1];150 let high = here.high[i];151 for y in low..=high {152 boxes += 1;153 let mut truth = [0i64; 4];154 for k in 0..4usize {155 let column = 3 * i + k;156 truth[k] = if column <= top_below + 1 {157 below.high[column] as i64 - 3 * y as i64158 } else {159 -1160 };161 }162 let shifted = [163 guess[0] - 3 * y as i64,164 guess[1] - 3 * y as i64,165 guess[2] - 3 * y as i64,166 guess[3] - 3 * y as i64,167 ];168 let roped = [169 rope[0] - 3 * y as i64,170 rope[1] - 3 * y as i64,171 rope[2] - 3 * y as i64,172 rope[3] - 3 * y as i64,173 ];174 let seen = mask_of(truth, reach);175 if seen != mask_of(shifted, reach) {176 faults += 1;177 worst = worst.max(i);178 }179 let missed = seen != mask_of(roped, reach);180 if missed {181 chord_faults += 1;182 }183 if slope <= 1.0 {184 shallow += 1;185 if missed {186 shallow_faults += 1;187 }188 }189 }190 }191 println!(192 "frozen {} r={} level={} columns={} column_exact={} boxes={} tangent_faults={} tangent_rate={:.6} chord_faults={} chord_rate={:.6} shallow_boxes={} shallow_chord_faults={} shallow_rate={:.6} last_fault_column={} of {}",193 label,194 radius,195 level,196 columns,197 column_hits,198 boxes,199 faults,200 faults as f64 / boxes.max(1) as f64,201 chord_faults,202 chord_faults as f64 / boxes.max(1) as f64,203 shallow,204 shallow_faults,205 shallow_faults as f64 / shallow.max(1) as f64,206 worst,207 top_here208 );209 }210}211212// CENTRE SEAT213214fn centre(label: &str, radius: u64) {215 let deep = levels(radius);216 for level in 0..deep.saturating_sub(1) {217 let below = rung(radius, level);218 let here = rung(radius, level + 1);219 let top_here = here.high.len() - 2;220 let top_below = below.high.len() - 2;221 let mut boxes = 0u64;222 let mut seated = 0u64;223 for i in 0..=top_here {224 let low = here.high[i + 1];225 let high = here.high[i];226 for y in low..=high {227 boxes += 1;228 let column = 3 * i + 1;229 if column <= top_below230 && below.high[column + 1] <= 3 * y + 1231 && 3 * y + 1 <= below.high[column]232 {233 seated += 1;234 }235 }236 }237 println!(238 "centre {} r={} level={} boxes={} centre_crossed={} rate={:.6} derived={:.6} ratio={:.6}",239 label,240 radius,241 level,242 boxes,243 seated,244 seated as f64 / boxes as f64,245 1.0 / 3.0,246 3.0 * seated as f64 / boxes as f64247 );248 }249}250251// STRAIGHT LINE LADDER252253fn ones(value: u64, depth: u32) -> u32 {254 let mut mask = 0u32;255 let mut left = value;256 for slot in 0..depth {257 if left % 3 == 1 {258 mask |= 1 << slot;259 }260 left /= 3;261 }262 mask263}264265fn ladder(name: &str, num: i128, den: i128, off: i128, depth: u32) {266 let width = 3i128.pow(depth);267 let height = |x: i128| -> i128 { (num * (width - x) + off) / den };268 let mut total = 0u64;269 let mut alive = 0u64;270 let mut hits = vec![0u64; depth as usize];271 for x in 0..width {272 let column = ones(x as u64, depth);273 let low = height(x + 1);274 let high = height(x);275 for z in low..=high {276 total += 1;277 let both = column & ones(z as u64, depth);278 if both == 0 {279 alive += 1;280 }281 let mut left = both;282 while left != 0 {283 let slot = left.trailing_zeros() as usize;284 hits[slot] += 1;285 left &= left - 1;286 }287 }288 }289 let mut product = 1.0f64;290 for slot in 0..depth as usize {291 product *= 1.0 - hits[slot] as f64 / total as f64;292 }293 let survival = alive as f64 / total as f64;294 let psi = survival / product;295 let independent = (8.0f64 / 9.0).powi(depth as i32);296 println!(297 "line {} slope={:.12} shift={:.6} depth={} cells={} alive={} survival={:.9} marginal={:.9} psi={:.9} logpsi={:.6} rate={:.9} eight_ninths={:.9}",298 name,299 num as f64 / den as f64,300 off as f64 / den as f64,301 depth,302 total,303 alive,304 survival,305 product,306 psi,307 psi.ln(),308 survival.powf(1.0 / depth as f64),309 independent.powf(1.0 / depth as f64)310 );311}312313// RESONANCE TRACKING314315fn coprime(a: i128, b: i128) -> bool {316 let mut x = a;317 let mut y = b;318 while y != 0 {319 let t = x % y;320 x = y;321 y = t;322 }323 x == 1324}325326fn steeper(u: i128, r: i128, p: i128, q: i128) -> bool {327 if p < 0 {328 return true;329 }330 u * u * (q * q + p * p) > p * p * r * r331}332333fn shallower(u: i128, r: i128, p: i128, q: i128) -> bool {334 u * u * (q * q + p * p) < p * p * r * r335}336337fn cap_levels(r: i128, b: i128) -> u32 {338 let mut count = 0u32;339 let mut scale = 1i128;340 while scale * b * b < 2 * r {341 count += 1;342 scale *= 3;343 }344 count345}346347fn floor_levels(r: i128, a: i128, b: i128) -> u32 {348 if a < 1 || a * b + 1 > b * b {349 return 0;350 }351 let mut count = 0u32;352 let mut scale = 1i128;353 while 8 * scale * scale * b * b * b * b < r * r {354 count += 1;355 scale *= 3;356 }357 count358}359360fn track(radius: u64, a: i128, b: i128) -> (u32, u64, [u64; 3]) {361 let r = radius as i128;362 let low = a * b - 1;363 let high = a * b + 1;364 let den = b * b;365 let deep = levels(radius);366 let mut run = 0u32;367 let mut boxes = 0u64;368 let mut seats = [0u64; 3];369 for level in 0..deep {370 let scale = 3i128.pow(level);371 let top = r / scale;372 let mut here = 0u64;373 let mut mine = [0u64; 3];374 for i in 0..=top {375 let left = scale * i;376 let right = scale * (i + 1);377 if 2 * right * right > r * r {378 break;379 }380 if steeper(left, r, low, den) && shallower(right, r, high, den) {381 here += 1;382 mine[(i % 3) as usize] += 1;383 }384 }385 if here > 0 {386 assert_eq!(run, level);387 run = level + 1;388 boxes = here;389 seats = mine;390 }391 }392 (run, boxes, seats)393}394395fn resonance(label: &str, radius: u64) -> u64 {396 let r = radius as i128;397 let deep = levels(radius);398 let mut rows = 0u64;399 let mut live = 0u64;400 let mut at_cap = 0u64;401 let mut at_floor = 0u64;402 let mut budget = vec![0i128; deep as usize];403 for b in 1..=30i128 {404 for a in 0..=b {405 if !coprime(a, b) {406 continue;407 }408 let cap = cap_levels(r, b);409 let low = floor_levels(r, a, b);410 let (run, boxes, seats) = track(radius, a, b);411 assert!(run <= cap);412 assert!(run >= low);413 rows += 1;414 if cap < deep {415 live += 1;416 }417 if run == cap {418 at_cap += 1;419 }420 if low > 0 && run == low {421 at_floor += 1;422 }423 for level in 0..run as usize {424 budget[level] = budget[level].max(b);425 }426 println!(427 "track {label} r={radius} slope={a}/{b} eps=1/{den} run={run} cap={cap} floor={low} boxes={boxes} seats_mod3={s0},{s1},{s2}",428 den = b * b,429 s0 = seats[0],430 s1 = seats[1],431 s2 = seats[2]432 );433 }434 }435 println!(436 "track {label} r={radius} levels={deep} slopes={rows} live_caps={live} run_equals_cap={at_cap} run_equals_floor={at_floor}"437 );438 for level in 0..deep as usize {439 let scale = 3i128.pow(level as u32);440 let seen = budget[level];441 assert!(seen * seen * scale < 2 * r);442 println!(443 "budget {label} r={radius} level={level} rank={rank} biggest_denominator={seen} denominator_square_times_scale={weight} twice_r={twice}",444 rank = deep as usize - 1 - level,445 weight = seen * seen * scale,446 twice = 2 * r447 );448 }449 rows + deep as u64 + 1450}451452fn secant(label: &str, radius: u64) -> u64 {453 let r = radius as i128;454 let square = r * r;455 let deep = levels(radius);456 let mut best = 0u32;457 let mut witness = 0i128;458 for level in 1..deep {459 let width = 3i128.pow(level);460 let step = (width - 1) / 2;461 let top = r / width;462 for i in 0..=top {463 let u = width * i;464 if u + 2 * step >= r {465 break;466 }467 let v0 = root_floor((square - u * u) as u64) as i128;468 let v1 = root_floor((square - (u + step) * (u + step)) as u64) as i128;469 let v2 = root_floor((square - (u + 2 * step) * (u + 2 * step)) as u64) as i128;470 if (v0 - 2 * v1 + v2).abs() <= 1 {471 best = level;472 witness = u;473 break;474 }475 }476 }477 let step = (3i128.pow(best) - 1) / 2;478 let next = (3i128.pow(best + 1) - 1) / 2;479 assert!(step * step <= 3 * r);480 assert!(next * next > 3 * r);481 println!(482 "secant {label} r={radius} levels={deep} line_depth={best} column={witness} spacing={step} spacing_square={sq} three_r={three} next_spacing={next} next_square={nsq}",483 sq = step * step,484 three = 3 * r,485 nsq = next * next486 );487 1488}489490fn blind(sigma_num: i128, sigma_den: i128, back: u32) -> u64 {491 let scale = 3i128.pow(back);492 assert_eq!(scale % sigma_den, 0);493 let lift = scale / sigma_den;494 let slow = sigma_num * lift;495 let fast = slow + 1;496 let mut differ = 0i128;497 let samples = 3 * scale;498 for t in 0..samples {499 let mut a = [0i64; 4];500 let mut b = [0i64; 4];501 for k in 0..4i128 {502 let one = 2 * t + 1 - 2 * k * slow;503 let two = 2 * t + 1 - 2 * k * fast;504 a[k as usize] = one.div_euclid(2 * scale) as i64;505 b[k as usize] = two.div_euclid(2 * scale) as i64;506 }507 if mask_of(a, 3) != mask_of(b, 3) {508 differ += 1;509 }510 }511 assert!(differ * scale <= 6 * samples);512 println!(513 "blind slope={sigma_num}/{sigma_den} eps=1/{scale} samples={samples} differ={differ} fraction={frac:.9} bound={bound:.9}",514 frac = differ as f64 / samples as f64,515 bound = 6.0 / scale as f64516 );517 1518}519520// BOX AGAINST BLOCK521522fn heights(radius: u64) -> Vec<i128> {523 let r = radius as i128;524 let square = r * r;525 let mut out = Vec::with_capacity(radius as usize + 2);526 for x in 0..=r + 1 {527 if x > r {528 out.push(0);529 } else {530 out.push(root_floor((square - x * x) as u64) as i128);531 }532 }533 out534}535536fn line_bracket(v: &[i128]) -> Option<((i128, i128), (i128, i128))> {537 let span = v.len() as i128;538 let mut low: Option<(i128, i128)> = None;539 let mut high: Option<(i128, i128)> = None;540 for a in 0..span {541 for b in 0..span {542 if a == b {543 continue;544 }545 let gap = b - a;546 let rise = v[b as usize] - v[a as usize] + 1;547 if gap > 0 {548 high = Some(match high {549 None => (rise, gap),550 Some(seen) => {551 if rise * seen.1 < seen.0 * gap {552 (rise, gap)553 } else {554 seen555 }556 }557 });558 } else {559 low = Some(match low {560 None => (-rise, -gap),561 Some(seen) => {562 if -rise * seen.1 > seen.0 * -gap {563 (-rise, -gap)564 } else {565 seen566 }567 }568 });569 }570 }571 }572 let (a, b) = (low?, high?);573 if a.0 * b.1 < b.0 * a.1 {574 Some((a, b))575 } else {576 None577 }578}579580fn finest(radius: i128, left: i128, right: i128) -> Option<(i128, i128)> {581 let mut best: Option<(i128, i128)> = None;582 for b in 1..=30i128 {583 for a in 0..=b {584 if !coprime(a, b) {585 continue;586 }587 if steeper(left, radius, a * b - 1, b * b) && shallower(right, radius, a * b + 1, b * b)588 {589 if best.is_none_or(|seen| b > seen.1) {590 best = Some((a, b));591 }592 }593 }594 }595 best596}597598fn blocks(label: &str, radius: u64, level: u32) -> u64 {599 let r = radius as i128;600 let scale = 3i128.pow(level);601 let v = heights(radius);602 let side = (r / scale + 2) as usize;603 let mut extent = vec![(i128::MAX, i128::MIN, i128::MAX, i128::MIN); side * side];604 for x in 0..=r {605 let top = v[x as usize];606 let bottom = v[(x + 1) as usize];607 let column = x / scale;608 let mut row = bottom;609 while row <= top {610 let band = row / scale;611 let slot = &mut extent[column as usize * side + band as usize];612 slot.0 = slot.0.min(x);613 slot.1 = slot.1.max(x);614 slot.2 = slot.2.min(row);615 slot.3 = slot.3.max(row);616 row = (band + 1) * scale;617 }618 }619 let mut boxes = 0u64;620 let mut across = 0u64;621 let mut short = 0u64;622 let mut lines = 0u64;623 let mut rows = 0u64;624 for column in 0..side as i128 {625 for band in 0..side as i128 {626 let slot = extent[column as usize * side + band as usize];627 if slot.1 < slot.0 {628 continue;629 }630 boxes += 1;631 let wide = slot.1 - slot.0 + 1;632 let tall = slot.3 - slot.2 + 1;633 let step = (wide - 1) / 2;634 let lift = (tall - 1) / 2;635 if wide == scale {636 across += 1;637 }638 if step * step <= 3 * r && lift * lift <= 3 * r {639 short += 1;640 }641 let stop = (slot.1 + 1).min(r);642 let cut: Vec<i128> = (slot.0..=stop).map(|x| v[x as usize]).collect();643 let Some((low, high)) = line_bracket(&cut) else {644 continue;645 };646 lines += 1;647 assert!(step * step <= 3 * r);648 let window = finest(r, slot.0, slot.1);649 let (a, b) = window.unwrap();650 let reach = t_of(r, slot.1) - t_of(r, slot.0);651 println!(652 "boxline {label} r={radius} level={level} box={column},{band} columns={from}..{to} wide={wide} tall={tall} t_in=({sl:.7},{sh:.7}) span={reach:.9} block_floor={floor:.9} share={share:.6} finest_window={a}/{b}",653 from = slot.0,654 to = slot.1,655 sl = -(high.0 as f64) / high.1 as f64,656 sh = -(low.0 as f64) / low.1 as f64,657 floor = scale as f64 / r as f64,658 share = reach / (scale as f64 / r as f64)659 );660 rows += 1;661 }662 }663 println!(664 "boxes {label} r={radius} level={level} boxes={boxes} side_to_side={across} both_extents_short={short} line_boxes={lines} block_cap_level={cap}",665 cap = 3i128.pow(level)666 );667 rows + 1668}669670fn t_of(r: i128, u: i128) -> f64 {671 u as f64 / ((r * r - u * u) as f64).sqrt()672}673674// PASS675676pub fn derive() {677 let alphabet = line_alphabet(9);678 let masks: Vec<u16> = alphabet.iter().map(|entry| entry.0).collect();679 for span in [12i64, 15] {680 let other: Vec<u16> = line_alphabet(span).iter().map(|entry| entry.0).collect();681 assert_eq!(other, masks);682 }683 println!(684 "line_alphabet states={} list=[{}]",685 masks.len(),686 show(&masks)687 );688 for radius in [6560u64, 19682, 12345] {689 compare("carpet", radius);690 }691 for radius in [6560u64, 19682] {692 frozen("carpet", radius);693 centre("carpet", radius);694 }695 let unit: i128 = 300000000000;696 let slopes: [(&str, i128, i128); 10] = [697 ("half", unit / 2, 0),698 ("half_shifted", unit / 2, 123456789012),699 ("third", unit / 3, 0),700 ("third_shifted", unit / 3, 123456789012),701 ("third_again", unit / 3, 271828182845),702 ("seventh", unit / 7, 0),703 ("seventh_shifted", unit / 7, 123456789012),704 ("root2m1", 124264068712, 123456789012),705 ("golden", 185410196625, 123456789012),706 ("quarterpi", 235619449020, 123456789012),707 ];708 for (name, num, off) in slopes {709 for depth in 4..=12u32 {710 ladder(name, num, unit, off, depth);711 }712 }713 for step in 2..=9u32 {714 let drift = unit / 3i128.pow(step);715 for depth in 4..=12u32 {716 ladder(717 &format!("near_third_3^-{step}"),718 unit / 3 + drift,719 unit,720 123456789012,721 depth,722 );723 }724 }725 let mut rows = 0u64;726 for radius in [728u64, 2186, 6560, 19682] {727 rows += resonance("carpet", radius);728 rows += secant("carpet", radius);729 }730 for back in 2..=7u32 {731 rows += blind(1, 3, back);732 }733 rows += blocks("carpet", 19682, 6);734 println!("track carpet rows={rows}");735}