반응형

상자 17개를 넣는 가장 작은 정사각형 공간의 길이는?

반듯하고 대칭인 배치가 정답일 줄 알았는데

 


"교주, 정사각형 안에 정사각형 17개를 집어넣으려면 이 방법이 가장 효율적이란 거 알고 있어?"

네, 오늘은 이 물음에 답해봅니다.
(2026년 오스카 피츠하딩이 공개한 Lean 증명까지 따라가 봅니다.)

 

0. 4×4에 16개라면, 17개에는 얼마나 더 필요할까요?

한 변이 1cm인 작은 상자가 있습니다. 위에서 보면 정사각형입니다. 이것을 정사각형인 큰 상자에 담아 봅시다. 작은 상자는 옮기거나 돌릴 수 있고, 가장자리끼리 닿아도 됩니다. 겹치거나 큰 상자 밖으로 나가면 안 됩니다.

4×4cm에는 16개가 꽉 찹니다. 작은 상자들의 넓이만으로 16cm²를 다 쓰니, 비틀어 보아도 17개째가 들어갈 수는 없습니다.

그럼 큰 상자를 얼마나 키워야 할까요? 5×5cm로 늘리면 쉽습니다. 스물다섯 자리 중 열일곱 자리만 쓰면 되니까요. 하지만 상자 하나를 더 넣으려고 여덟 자리를 비워 두는 것은 아깝습니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig,axes=plt.subplots(1,2,figsize=(13.4,7.0),facecolor=WHITE)
title(fig,'16개 다음, 17개를 넣으려면 얼마나 더 커야 할까?',
      'From 16 to 17 boxes: how much more space is needed?',18)
for ax in axes:setup(ax,xmax=5.55,ymax=5.55)
border(axes[0],4)
for k in range(16):
    x=k%4;y=k//4
    axes[0].add_patch(Rectangle((x,y),1,1,facecolor=BLUE,edgecolor=INK,lw=1))
axes[0].add_patch(Rectangle((4.3,1.5),1,1,facecolor=ORANGE,edgecolor=INK,lw=1.4))
label(axes[0],4.8,2,'17',13,ha='center',va='center')
label(axes[0],2,4.40,'4×4에는 16개가 꽉 찬다',13,ha='center')
label(axes[0],2,4.12,'4 × 4 holds exactly 16',11,ha='center',color=MUTED)
dimension(axes[0],4)
border(axes[1],5)
for k in range(25):
    x=k%5;y=k//5
    axes[1].add_patch(Rectangle((x,y),1,1,facecolor=BLUE if k<17 else '#f4e7d6',
                               edgecolor=INK if k<17 else '#baa88d',lw=1 if k<17 else .6,
                               linestyle='-' if k<17 else '--'))
label(axes[1],2.5,5.28,'5×5에는 여덟 자리가 남는다',13,ha='center')
dimension(axes[1],5)
fig.text(.5,.045,'작은 상자 한 변은 모두 1cm · 두 그림은 같은 축척\n'
         'Every small box has side 1 cm; both panels use the same scale',
         fontproperties=F,fontsize=12,ha='center',color=MUTED)
fig.subplots_adjust(top=.79,bottom=.15,left=.035,right=.99,wspace=.12)
plt.show()

 

 

작은 상자는 모두 한 변 1cm다. 4×4cm에는 16개가 꽉 차고, 5×5cm에는 17개를 놓고도 여덟 자리가 남는다.

4cm보다는 크고, 5cm보다는 작은 길이를 찾아봅시다. 반듯하게 네 개씩 줄을 맞추면 열여섯 개 뒤에 하나가 남습니다. 그렇다면 줄을 그대로 늘리는 대신, 상자를 조금 비틀어 보면 어떨까요?




1. 전부 45도로 세우면, 5cm 아래로 들어갑니다

우선 가장 단순하게 해 봅시다. 작은 상자를 전부 45도 돌려서 세우고, 다음 줄을 옆으로 어긋나게 놓습니다. 위아래 꼭짓점 사이로 옆줄의 상자가 들어갑니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678

import json
def _av(q):
    c=math.cos(q['angle']);s=math.sin(q['angle'])
    return np.array([[q['x']+(a*c-b*s)/2,q['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])
def _at(ax,x,y,text,size=12,**kw):
    return ax.text(x,y,text,fontproperties=F,fontsize=size,color=kw.pop('color',INK),**kw)
def _aa(ax,x,y):
    ax.set_xlim(*x);ax.set_ylim(*y);ax.set_aspect('equal');ax.axis('off')
def _ad(ax,data,ids=False):
    for q in data['squares']:
        ax.add_patch(Polygon(_av(q),facecolor=[BLUE,ORANGE,TEAL][q['group']],edgecolor=INK,lw=1.05))
        if ids:_at(ax,q['x'],q['y'],str(q['id']),10,ha='center',va='center')
    ax.add_patch(Rectangle((0,0),data['side'],data['side'],fill=False,edgecolor=INK,lw=2))
def _dim(ax,a,b,text,offset=(0,0),size=11):
    a=np.asarray(a);b=np.asarray(b)
    ax.add_patch(FancyArrowPatch(a,b,arrowstyle='<->',mutation_scale=10,color=INK,lw=1.2,zorder=5))
    p=(a+b)/2+np.asarray(offset)
    _at(ax,p[0],p[1],text,size,ha='center',va='center',zorder=6,
        bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.95,pad=1.5))

ATTEMPTS=json.loads(r'''[{"key":"all45","label":"A: 작은 상자 17개를 모두 45도로","side":4.949747468305832,"squares":[{"id":1,"x":2.1213203435596424,"y":0.7071067811865475,"angle":0.7853981633974483,"group":1},{"id":2,"x":3.5355339059327373,"y":0.7071067811865475,"angle":0.7853981633974483,"group":1},{"id":3,"x":1.414213562373095,"y":1.414213562373095,"angle":0.7853981633974483,"group":1},{"id":4,"x":2.82842712474619,"y":1.414213562373095,"angle":0.7853981633974483,"group":1},{"id":5,"x":4.242640687119285,"y":1.414213562373095,"angle":0.7853981633974483,"group":1},{"id":6,"x":0.7071067811865475,"y":2.1213203435596424,"angle":0.7853981633974483,"group":1},{"id":7,"x":2.1213203435596424,"y":2.1213203435596424,"angle":0.7853981633974483,"group":1},{"id":8,"x":3.5355339059327373,"y":2.1213203435596424,"angle":0.7853981633974483,"group":1},{"id":9,"x":1.414213562373095,"y":2.82842712474619,"angle":0.7853981633974483,"group":1},{"id":10,"x":2.82842712474619,"y":2.82842712474619,"angle":0.7853981633974483,"group":1},{"id":11,"x":4.242640687119285,"y":2.82842712474619,"angle":0.7853981633974483,"group":1},{"id":12,"x":0.7071067811865475,"y":3.5355339059327373,"angle":0.7853981633974483,"group":1},{"id":13,"x":2.1213203435596424,"y":3.5355339059327373,"angle":0.7853981633974483,"group":1},{"id":14,"x":3.5355339059327373,"y":3.5355339059327373,"angle":0.7853981633974483,"group":1},{"id":15,"x":1.414213562373095,"y":4.242640687119285,"angle":0.7853981633974483,"group":1},{"id":16,"x":2.82842712474619,"y":4.242640687119285,"angle":0.7853981633974483,"group":1},{"id":17,"x":4.242640687119285,"y":4.242640687119285,"angle":0.7853981633974483,"group":1}],"side_exact":"7/sqrt(2)","h":0.7071067811865475,"construction":"i,j=0..5, i+j even, remove (0,0); centers=(h(i+1),h(j+1)); angle=45 degrees","minimum_scope":"이 6줄 교차 격자 구조에서","minimum_argument":"중심 위치의 가로·세로 범위가 각각 h부터 6h이다. 양끝 꼭짓점까지 각각 h가 더 필요하므로 한 변은 7h이다.","claim":"제시한 구조의 길이이며 모든 45도 배치의 최소라는 주장이 아님"},{"key":"four_2x2_and_center","label":"B: 네 모서리에 4개씩, 가운데 기울인 1개","side":4.707106781186548,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0,"group":0},{"id":2,"x":0.5,"y":1.5,"angle":0,"group":0},{"id":3,"x":1.5,"y":0.5,"angle":0,"group":0},{"id":4,"x":1.5,"y":1.5,"angle":0,"group":0},{"id":5,"x":0.5,"y":4.207106781186548,"angle":0,"group":0},{"id":6,"x":0.5,"y":3.207106781186548,"angle":0,"group":0},{"id":7,"x":1.5,"y":4.207106781186548,"angle":0,"group":0},{"id":8,"x":1.5,"y":3.207106781186548,"angle":0,"group":0},{"id":9,"x":4.207106781186548,"y":0.5,"angle":0,"group":0},{"id":10,"x":4.207106781186548,"y":1.5,"angle":0,"group":0},{"id":11,"x":3.207106781186548,"y":0.5,"angle":0,"group":0},{"id":12,"x":3.207106781186548,"y":1.5,"angle":0,"group":0},{"id":13,"x":4.207106781186548,"y":4.207106781186548,"angle":0,"group":0},{"id":14,"x":4.207106781186548,"y":3.207106781186548,"angle":0,"group":0},{"id":15,"x":3.207106781186548,"y":4.207106781186548,"angle":0,"group":0},{"id":16,"x":3.207106781186548,"y":3.207106781186548,"angle":0,"group":0},{"id":17,"x":2.353553390593274,"y":2.353553390593274,"angle":0.7853981633974483,"group":1}],"side_exact":"4+1/sqrt(2)","h":0.7071067811865475,"construction":"Four axis-aligned 2x2 corner groups and one central 45-degree small box","minimum_scope":"네 모서리 2×2와 중앙 45도라는 대칭 구조에서","minimum_argument":"안쪽 꼭짓점 (2,2)에서 중앙까지 가로·세로 거리는 각각 (L-4)/2. 두 거리의 합 L-4가 중앙 작은 상자의 반대각선 h 이상이어야 하므로 L>=4+h. 등호 배치가 실제로 들어간다.","contact":{"corner":[2,2],"center":[2.353553390593274,2.353553390593274],"x_plus_y":"L-4=h"},"claim":"A/B/C 세 후보 중 가장 작지만 전체 최적성은 주장하지 않음"},{"key":"four_L_and_cross","label":"C: 네 모서리에 3개씩, 가운데 5개","side":4.914213562373095,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0,"group":0},{"id":2,"x":0.5,"y":1.5,"angle":0,"group":0},{"id":3,"x":1.5,"y":0.5,"angle":0,"group":0},{"id":4,"x":0.5,"y":4.414213562373095,"angle":0,"group":0},{"id":5,"x":0.5,"y":3.414213562373095,"angle":0,"group":0},{"id":6,"x":1.5,"y":4.414213562373095,"angle":0,"group":0},{"id":7,"x":4.414213562373095,"y":0.5,"angle":0,"group":0},{"id":8,"x":4.414213562373095,"y":1.5,"angle":0,"group":0},{"id":9,"x":3.414213562373095,"y":0.5,"angle":0,"group":0},{"id":10,"x":4.414213562373095,"y":4.414213562373095,"angle":0,"group":0},{"id":11,"x":4.414213562373095,"y":3.414213562373095,"angle":0,"group":0},{"id":12,"x":3.414213562373095,"y":4.414213562373095,"angle":0,"group":0},{"id":13,"x":2.4571067811865475,"y":2.4571067811865475,"angle":0,"group":2},{"id":14,"x":2.4571067811865475,"y":3.664213562373095,"angle":0.7853981633974483,"group":1},{"id":15,"x":3.664213562373095,"y":2.4571067811865475,"angle":0.7853981633974483,"group":1},{"id":16,"x":2.4571067811865475,"y":1.25,"angle":0.7853981633974483,"group":1},{"id":17,"x":1.25,"y":2.4571067811865475,"angle":0.7853981633974483,"group":1}],"side_exact":"7/2+sqrt(2)","h":0.7071067811865475,"central_offset":1.2071067811865475,"construction":"Four axis-aligned L groups, one central axis-aligned small box, four 45-degree neighbors at offsets (0,+/-a),(+/-a,0), a=1/2+h","minimum_scope":"네 모서리 L자와 중앙 십자라는 대칭 구조에서","minimum_argument":"중앙 작은 상자와 위쪽 기울인 작은 상자가 닿으려면 중심 간격 a=1/2+h. 오른위 모서리 안쪽 꼭짓점 (L-2,L-1)에서 위쪽 기울인 작은 상자 중심까지 거리 성분의 합은 L-3-a. 이것이 h 이상이어야 하므로 L>=7/2+2h. 등호 배치가 실제로 들어간다.","contact":{"corner":[2.914213562373095,3.914213562373095],"center":[2.4571067811865475,3.664213562373095],"x_plus_y":"L-3-a=h"},"claim":"중앙을 더 장식해도 B보다 큰 큰 상자가 필요한 실제 반례"}]''')

fig=plt.figure(figsize=(12.2,8.1),facecolor=WHITE)
fig.suptitle('모두 45도로 기울여 교차하는 줄에 놓아 보자\nA candidate with all 17 small boxes tilted by 45°',fontproperties=F,fontsize=19,color=INK,y=.97)
data=ATTEMPTS[0];h=data['h'];L=data['side']
ax=fig.add_axes([.035,.15,.60,.68]);_aa(ax,(-.25,L+.65),(-.55,L+.2));_ad(ax,data,True)
_dim(ax,(0,-.25),(L,-.25),'7h ≈ 4.94975 cm',offset=(0,-.15),size=12)
ax.plot([0,L],[h,h],color=MUTED,lw=.7,ls=':');ax.plot([0,L],[2*h,2*h],color=MUTED,lw=.7,ls=':')
_dim(ax,(L+.22,h),(L+.22,2*h),'h',offset=(.16,0))
unit=fig.add_axes([.70,.34,.265,.35]);_aa(unit,(-1.0,1.0),(-1.1,1.05))
q=dict(x=0,y=0,angle=math.pi/4)
unit.add_patch(Polygon(_av(q),facecolor=ORANGE,edgecolor=INK,lw=1.4))
_dim(unit,(-h,0),(h,0),'√2 cm',offset=(0,-.18),size=12)
_dim(unit,(0,0),(0,h),'h',offset=(.14,0),size=12)
_at(unit,-.50,.50,'1 cm',11,ha='center',rotation=45)
_at(unit,0,-.91,'h = √2 / 2 cm',12,ha='center')
unit.set_title('작은 상자 한 개의 실제 길이\nDimensions of one small box',fontproperties=F,fontsize=12,color=INK,pad=10)
fig.text(.82,.22,'중심은 h 간격의 줄에 교대로 놓는다\nCenters alternate on rows spaced h apart',fontproperties=F,fontsize=11,color=MUTED,ha='center')
fig.text(.5,.045,'18자리 중 하나를 비워 17개 · 양끝 꼭짓점까지 포함하면 한 변은 7h\n17 of 18 staggered positions; the outer vertices require a side of 7h',fontproperties=F,fontsize=11,color=MUTED,ha='center')
plt.show()

 

전부 45도로 세운 17개를 엇갈린 여섯 줄에 놓았다. 이 배치에 필요한 한 변은 약 4.94975cm다.

이렇게 한 줄에 세 개씩 여섯 줄을 만들면 열여덟 자리가 생깁니다. 그중 한 자리를 비우면 17개입니다. 큰 상자 한 변은 약 4.94975cm입니다. 조금이지만 5cm보다 작아졌습니다.

길이도 셀 수 있습니다. 한 변 1cm인 작은 상자의 대각선은 약 1.41421cm이고, 그 절반은 약 0.70711cm입니다. 이 절반 길이를 $h$라고 부르겠습니다. 그림의 줄 간격은 $h$이고, 여섯 줄의 첫 중심부터 마지막 중심까지는 다섯 간격입니다. 아래와 위의 뾰족한 끝까지 각각 $h$씩 더하면

$$ h+5h+h=7h\approx4.94975\text{cm} $$

입니다. 가로로도 같은 길이가 필요합니다. 이제 ‘그냥 돌려 보았다’에서 한 걸음 나아갔습니다. 17개가 들어가는 배치를 만들었고, 그 배치에 필요한 길이도 구했습니다.




2. 가운데 하나만 비트는 쪽이 더 작고, 더 반듯합니다

전부 비틀 필요는 없을지도 모릅니다. 네 모서리에 작은 상자를 네 개씩, 2×2로 놓아 봅시다. 그러면 16개입니다. 남은 하나만 가운데에서 45도로 세웁니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678

import json
def _av(q):
    c=math.cos(q['angle']);s=math.sin(q['angle'])
    return np.array([[q['x']+(a*c-b*s)/2,q['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])
def _at(ax,x,y,text,size=12,**kw):
    return ax.text(x,y,text,fontproperties=F,fontsize=size,color=kw.pop('color',INK),**kw)
def _aa(ax,x,y):
    ax.set_xlim(*x);ax.set_ylim(*y);ax.set_aspect('equal');ax.axis('off')
def _ad(ax,data,ids=False):
    for q in data['squares']:
        ax.add_patch(Polygon(_av(q),facecolor=[BLUE,ORANGE,TEAL][q['group']],edgecolor=INK,lw=1.05))
        if ids:_at(ax,q['x'],q['y'],str(q['id']),10,ha='center',va='center')
    ax.add_patch(Rectangle((0,0),data['side'],data['side'],fill=False,edgecolor=INK,lw=2))
def _dim(ax,a,b,text,offset=(0,0),size=11):
    a=np.asarray(a);b=np.asarray(b)
    ax.add_patch(FancyArrowPatch(a,b,arrowstyle='<->',mutation_scale=10,color=INK,lw=1.2,zorder=5))
    p=(a+b)/2+np.asarray(offset)
    _at(ax,p[0],p[1],text,size,ha='center',va='center',zorder=6,
        bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.95,pad=1.5))

ATTEMPTS=json.loads(r'''[{"key":"all45","label":"A: 작은 상자 17개를 모두 45도로","side":4.949747468305832,"squares":[{"id":1,"x":2.1213203435596424,"y":0.7071067811865475,"angle":0.7853981633974483,"group":1},{"id":2,"x":3.5355339059327373,"y":0.7071067811865475,"angle":0.7853981633974483,"group":1},{"id":3,"x":1.414213562373095,"y":1.414213562373095,"angle":0.7853981633974483,"group":1},{"id":4,"x":2.82842712474619,"y":1.414213562373095,"angle":0.7853981633974483,"group":1},{"id":5,"x":4.242640687119285,"y":1.414213562373095,"angle":0.7853981633974483,"group":1},{"id":6,"x":0.7071067811865475,"y":2.1213203435596424,"angle":0.7853981633974483,"group":1},{"id":7,"x":2.1213203435596424,"y":2.1213203435596424,"angle":0.7853981633974483,"group":1},{"id":8,"x":3.5355339059327373,"y":2.1213203435596424,"angle":0.7853981633974483,"group":1},{"id":9,"x":1.414213562373095,"y":2.82842712474619,"angle":0.7853981633974483,"group":1},{"id":10,"x":2.82842712474619,"y":2.82842712474619,"angle":0.7853981633974483,"group":1},{"id":11,"x":4.242640687119285,"y":2.82842712474619,"angle":0.7853981633974483,"group":1},{"id":12,"x":0.7071067811865475,"y":3.5355339059327373,"angle":0.7853981633974483,"group":1},{"id":13,"x":2.1213203435596424,"y":3.5355339059327373,"angle":0.7853981633974483,"group":1},{"id":14,"x":3.5355339059327373,"y":3.5355339059327373,"angle":0.7853981633974483,"group":1},{"id":15,"x":1.414213562373095,"y":4.242640687119285,"angle":0.7853981633974483,"group":1},{"id":16,"x":2.82842712474619,"y":4.242640687119285,"angle":0.7853981633974483,"group":1},{"id":17,"x":4.242640687119285,"y":4.242640687119285,"angle":0.7853981633974483,"group":1}],"side_exact":"7/sqrt(2)","h":0.7071067811865475,"construction":"i,j=0..5, i+j even, remove (0,0); centers=(h(i+1),h(j+1)); angle=45 degrees","minimum_scope":"이 6줄 교차 격자 구조에서","minimum_argument":"중심 위치의 가로·세로 범위가 각각 h부터 6h이다. 양끝 꼭짓점까지 각각 h가 더 필요하므로 한 변은 7h이다.","claim":"제시한 구조의 길이이며 모든 45도 배치의 최소라는 주장이 아님"},{"key":"four_2x2_and_center","label":"B: 네 모서리에 4개씩, 가운데 기울인 1개","side":4.707106781186548,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0,"group":0},{"id":2,"x":0.5,"y":1.5,"angle":0,"group":0},{"id":3,"x":1.5,"y":0.5,"angle":0,"group":0},{"id":4,"x":1.5,"y":1.5,"angle":0,"group":0},{"id":5,"x":0.5,"y":4.207106781186548,"angle":0,"group":0},{"id":6,"x":0.5,"y":3.207106781186548,"angle":0,"group":0},{"id":7,"x":1.5,"y":4.207106781186548,"angle":0,"group":0},{"id":8,"x":1.5,"y":3.207106781186548,"angle":0,"group":0},{"id":9,"x":4.207106781186548,"y":0.5,"angle":0,"group":0},{"id":10,"x":4.207106781186548,"y":1.5,"angle":0,"group":0},{"id":11,"x":3.207106781186548,"y":0.5,"angle":0,"group":0},{"id":12,"x":3.207106781186548,"y":1.5,"angle":0,"group":0},{"id":13,"x":4.207106781186548,"y":4.207106781186548,"angle":0,"group":0},{"id":14,"x":4.207106781186548,"y":3.207106781186548,"angle":0,"group":0},{"id":15,"x":3.207106781186548,"y":4.207106781186548,"angle":0,"group":0},{"id":16,"x":3.207106781186548,"y":3.207106781186548,"angle":0,"group":0},{"id":17,"x":2.353553390593274,"y":2.353553390593274,"angle":0.7853981633974483,"group":1}],"side_exact":"4+1/sqrt(2)","h":0.7071067811865475,"construction":"Four axis-aligned 2x2 corner groups and one central 45-degree small box","minimum_scope":"네 모서리 2×2와 중앙 45도라는 대칭 구조에서","minimum_argument":"안쪽 꼭짓점 (2,2)에서 중앙까지 가로·세로 거리는 각각 (L-4)/2. 두 거리의 합 L-4가 중앙 작은 상자의 반대각선 h 이상이어야 하므로 L>=4+h. 등호 배치가 실제로 들어간다.","contact":{"corner":[2,2],"center":[2.353553390593274,2.353553390593274],"x_plus_y":"L-4=h"},"claim":"A/B/C 세 후보 중 가장 작지만 전체 최적성은 주장하지 않음"},{"key":"four_L_and_cross","label":"C: 네 모서리에 3개씩, 가운데 5개","side":4.914213562373095,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0,"group":0},{"id":2,"x":0.5,"y":1.5,"angle":0,"group":0},{"id":3,"x":1.5,"y":0.5,"angle":0,"group":0},{"id":4,"x":0.5,"y":4.414213562373095,"angle":0,"group":0},{"id":5,"x":0.5,"y":3.414213562373095,"angle":0,"group":0},{"id":6,"x":1.5,"y":4.414213562373095,"angle":0,"group":0},{"id":7,"x":4.414213562373095,"y":0.5,"angle":0,"group":0},{"id":8,"x":4.414213562373095,"y":1.5,"angle":0,"group":0},{"id":9,"x":3.414213562373095,"y":0.5,"angle":0,"group":0},{"id":10,"x":4.414213562373095,"y":4.414213562373095,"angle":0,"group":0},{"id":11,"x":4.414213562373095,"y":3.414213562373095,"angle":0,"group":0},{"id":12,"x":3.414213562373095,"y":4.414213562373095,"angle":0,"group":0},{"id":13,"x":2.4571067811865475,"y":2.4571067811865475,"angle":0,"group":2},{"id":14,"x":2.4571067811865475,"y":3.664213562373095,"angle":0.7853981633974483,"group":1},{"id":15,"x":3.664213562373095,"y":2.4571067811865475,"angle":0.7853981633974483,"group":1},{"id":16,"x":2.4571067811865475,"y":1.25,"angle":0.7853981633974483,"group":1},{"id":17,"x":1.25,"y":2.4571067811865475,"angle":0.7853981633974483,"group":1}],"side_exact":"7/2+sqrt(2)","h":0.7071067811865475,"central_offset":1.2071067811865475,"construction":"Four axis-aligned L groups, one central axis-aligned small box, four 45-degree neighbors at offsets (0,+/-a),(+/-a,0), a=1/2+h","minimum_scope":"네 모서리 L자와 중앙 십자라는 대칭 구조에서","minimum_argument":"중앙 작은 상자와 위쪽 기울인 작은 상자가 닿으려면 중심 간격 a=1/2+h. 오른위 모서리 안쪽 꼭짓점 (L-2,L-1)에서 위쪽 기울인 작은 상자 중심까지 거리 성분의 합은 L-3-a. 이것이 h 이상이어야 하므로 L>=7/2+2h. 등호 배치가 실제로 들어간다.","contact":{"corner":[2.914213562373095,3.914213562373095],"center":[2.4571067811865475,3.664213562373095],"x_plus_y":"L-3-a=h"},"claim":"중앙을 더 장식해도 B보다 큰 큰 상자가 필요한 실제 반례"}]''')

fig=plt.figure(figsize=(14.2,10.7),facecolor=WHITE)
fig.suptitle('0도와 45도를 섞어 만든 두 대칭 후보\nTwo symmetric candidates combining 0° and 45°',fontproperties=F,fontsize=19,color=INK,y=.975)
labels=[('B · 모서리마다 4개 + 가운데 1개','Four groups of four, plus one central box'),
        ('C · 모서리마다 3개 + 가운데 5개','Four groups of three, plus five central boxes')]
for k,(data,(ko,en)) in enumerate(zip(ATTEMPTS[1:],labels)):
    x0=.045+.50*k
    ax=fig.add_axes([x0,.425,.405,.405]);_aa(ax,(-.18,5.15),(-.4,5.15));_ad(ax,data)
    ax.set_title(ko+'\n'+en,fontproperties=F,fontsize=11.5,color=INK,pad=11)
    L=data['side'];P=np.array(data['contact']['corner']);K=np.array(data['contact']['center'])
    ax.scatter([P[0]],[P[1]],s=35,color=RED,zorder=7)
    _dim(ax,(0,-.18),(L,-.18),f'{L:.8f} cm',offset=(0,-.13),size=11)
    detail=fig.add_axes([x0+.115,.175,.175,.19])
    lo=np.minimum(P,K)-.2;hi=np.maximum(P,K)+.2
    _aa(detail,(lo[0],hi[0]),(lo[1],hi[1]));_ad(detail,data)
    elbow=np.array([K[0],P[1]])
    detail.plot([P[0],elbow[0],K[0]],[P[1],elbow[1],K[1]],color=RED,lw=1.35,ls='--',zorder=5)
    detail.scatter([P[0],K[0]],[P[1],K[1]],s=28,color=RED,zorder=7)
    _at(detail,P[0]-.07,P[1]-.08,'P',11,ha='right',zorder=8)
    _at(detail,K[0]+.06,K[1]+.06,'K',11,ha='left',zorder=8)
    detail.set_title('실제로 닿는 부분 확대\nActual corner contact',fontproperties=F,fontsize=10.5,color=INK,pad=9)
    formula='L − 4 = h  →  L = 4 + h' if k==0 else 'L − 3 − (1/2 + h) = h'
    fig.text(x0+.2025,.105,formula,fontproperties=F,fontsize=12,color=INK,ha='center')
    if k==1:fig.text(x0+.2025,.077,'L = 7/2 + 2h',fontproperties=F,fontsize=12,color=INK,ha='center')
fig.text(.5,.026,'h = √2/2 cm · 같은 축척 · A/B/C 중 가장 작은 것은 B\nP: 접촉점 · K: 기울인 상자의 중심 / P: contact; K: tilted-box centre',fontproperties=F,fontsize=11,color=MUTED,ha='center')
plt.show()

 

가운데 하나만 비튼 왼쪽은 약 4.70711cm, 주변 네 개를 비튼 오른쪽은 약 4.91421cm가 필요하다. 두 그림은 같은 축척이다.

왼쪽 배치는 위아래도 같고, 좌우도 같습니다. 가운데 상자의 네 변이 모서리 묶음의 안쪽 끝에 딱 닿습니다. 이때 한 변은

$$ 2+h+2=4+h\approx\mathbf{4.70711\text{cm}} $$

입니다. 모서리의 묶음이 각각 2cm를 차지하고, 그 사이에 필요한 폭이 $h$인 것입니다. 이 폭보다 좁히면 가운데 상자가 네 묶음을 파고듭니다. 네 묶음과 가운데 상자를 이 규칙대로 놓는 한, 여기서 더 줄일 수 없습니다.

그럼 가운데 대신 그 주변의 네 상자를 비틀면 어떨까요? 오른쪽처럼 가운데는 반듯하게 두고, 위·아래·왼쪽·오른쪽에 기울인 상자를 하나씩 붙였습니다. 네 모서리에는 세 개씩 놓았습니다. 이것도 $1+4+12=17$개입니다.

그런데 이번에는 한 변이 약 4.91421cm나 필요합니다. 더 많이 비틀었다고 더 작아지지는 않았습니다. 우리가 만든 세 후보 중에서는 가운데 하나만 비튼 4.70711cm가 가장 짧습니다. 보기에도 이쪽이 답 같지 않나요?

두 대칭 배치의 길이를 더 정확히 계산하기

작은 상자 한 변이 1cm이므로 대각선의 절반은 $h=1/\sqrt2$cm입니다.

왼쪽에서 가운데 상자의 네 변의 중점을 이으면 한 변이 $h$인 모양이 생깁니다. 이 중점들이 네 모서리 묶음의 안쪽 끝에 닿으므로, 묶음 사이의 폭은 $h$입니다. 따라서 $L=4+h$입니다.

오른쪽은 큰 상자 중심을 기준으로 계산합니다. 가운데 상자 위에 붙인 기울어진 상자의 중심 높이는 $1/2+h$입니다. 오른쪽 위 묶음의 왼쪽 아래 끝은 가로 $L/2-2$, 세로 $L/2-1$에 있습니다. 그 끝이 기울어진 상자의 45도 변에 닿을 때

$$ \left(\frac L2-2\right)+\left(\frac L2-1-\frac12-h\right)=h. $$

따라서 $L=3.5+2h=3.5+\sqrt2\approx4.91421$cm입니다. 이보다 작으면 해당 모서리가 기울어진 상자 안으로 들어갑니다. 위 길이에서는 실제로 17개 모두 겹치지 않고 들어갑니다. 여기서 구한 최소는 각각의 대칭 배치를 만드는 규칙 안에서의 최소입니다.




3. 45도라는 규칙을 지켜도, 배치를 바꾸면 더 줄어듭니다

가운데 하나만 비튼 배치에서 멈출 이유는 없습니다. 모서리에 네 개씩 모아 놓는 규칙도 바꿔 볼 수 있습니다. 반듯한 상자와 45도로 기울인 상자를 다른 순서로 섞으면 어떨까요?

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678

import json
def _av(q):
    c=math.cos(q['angle']);s=math.sin(q['angle'])
    return np.array([[q['x']+(a*c-b*s)/2,q['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])
def _at(ax,x,y,text,size=12,**kw):
    return ax.text(x,y,text,fontproperties=F,fontsize=size,color=kw.pop('color',INK),**kw)
def _aa(ax,x,y):
    ax.set_xlim(*x);ax.set_ylim(*y);ax.set_aspect('equal');ax.axis('off')
def _ad(ax,data,ids=False):
    for q in data['squares']:
        ax.add_patch(Polygon(_av(q),facecolor=[BLUE,ORANGE,TEAL][q['group']],edgecolor=INK,lw=1.05))
        if ids:_at(ax,q['x'],q['y'],str(q['id']),10,ha='center',va='center')
    ax.add_patch(Rectangle((0,0),data['side'],data['side'],fill=False,edgecolor=INK,lw=2))
def _dim(ax,a,b,text,offset=(0,0),size=11):
    a=np.asarray(a);b=np.asarray(b)
    ax.add_patch(FancyArrowPatch(a,b,arrowstyle='<->',mutation_scale=10,color=INK,lw=1.2,zorder=5))
    p=(a+b)/2+np.asarray(offset)
    _at(ax,p[0],p[1],text,size,ha='center',va='center',zorder=6,
        bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.95,pad=1.5))

HISTORIC=json.loads(r'''{"key":"hamalainen_1980","label":"Pertti Hämäläinen의 1980년 실제 배치","side":4.6903559372884915,"side_exact":"(7+5*sqrt(2))/3","squares":[{"id":1,"x":4.1903559372884915,"y":4.1903559372884915,"angle":0.0,"group":0},{"id":2,"x":0.5,"y":4.1903559372884915,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":5,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":4.1903559372884915,"y":2.5,"angle":0.0,"group":0},{"id":7,"x":4.1903559372884915,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.1903559372884915,"y":0.5,"angle":0.0,"group":0},{"id":9,"x":3.1903559372884915,"y":0.5,"angle":0.0,"group":0},{"id":10,"x":2.3451779686442458,"y":1.3619288125423017,"angle":0.7853981633974483,"group":1},{"id":11,"x":1.6380711874576983,"y":2.069035593728849,"angle":0.7853981633974483,"group":1},{"id":12,"x":0.9309644062711508,"y":2.7761423749153966,"angle":0.7853981633974483,"group":1},{"id":13,"x":2.983249156101944,"y":2.1380711874576983,"angle":0.7853981633974483,"group":1},{"id":14,"x":2.2761423749153966,"y":2.8451779686442458,"angle":0.7853981633974483,"group":1},{"id":15,"x":1.5690355937288492,"y":3.5522847498307932,"angle":0.7853981633974483,"group":1},{"id":16,"x":3.259391531017341,"y":3.2761423749153966,"angle":0.7853981633974483,"group":1},{"id":17,"x":2.5522847498307932,"y":3.983249156101944,"angle":0.7853981633974483,"group":1}],"source_rows_degrees":[[4.1903559372884915,4.1903559372884915,0.0],[0.5,4.1903559372884915,0.0],[0.5,1.5,0.0],[1.5,0.5,0.0],[0.5,0.5,0.0],[4.1903559372884915,2.5,0.0],[4.1903559372884915,1.5,0.0],[4.1903559372884915,0.5,0.0],[3.1903559372884915,0.5,0.0],[2.3451779686442458,1.3619288125423017,45.0],[1.6380711874576983,2.069035593728849,45.0],[0.9309644062711508,2.7761423749153966,45.0],[2.983249156101944,2.1380711874576983,45.0],[2.2761423749153966,2.8451779686442458,45.0],[1.5690355937288492,3.5522847498307932,45.0],[3.259391531017341,3.2761423749153966,45.0],[2.5522847498307932,3.983249156101944,45.0]],"provenance":{"catalogue":"https://kingbird.myphotos.cc/packing/squares_in_squares__compared.html","original_svg":"https://kingbird.myphotos.cc/packing/square-17_r2.svg","retained_coordinates":"https://raw.githubusercontent.com/evand/square-packing/main/site/www/data/p/square-17_r2.json","conversion_source":"https://github.com/evand/square-packing/blob/main/site/tools/parse_svg.py","archive_method":"https://github.com/evand/square-packing/blob/main/search/packer/pk/kingbird.py","original_svg_retrieval":"The original SVG URL was identified in the catalogue HTML. Its XML could not be downloaded in this session: HTTP 429. No claim of a fresh XML parse is made.","coordinate_retrieval":"The public Atlas JSON explicitly names square-17_r2.svg and supplies these 17 center/angle triples. Its SVG parser and archival method were read.","conversion":"Atlas parser expands SVG entities and nested transforms at 50 decimal digits, then converts to y-up coordinates and degree angles modulo 90. This module preserves its published center coordinates and converts degree angles to radians only.","attribution":"David Ellsworth, Squares in Squares, after Erich Friedman; coordinate transcription by Evan Daniel, Square Packing Atlas."},"contact":{"P":[1,2],"R":[3.6903559372884915,3],"Q":[2.5522847498307932,4.6903559372884915]},"length_derivation":{"first":"At P=(1,2), x+y=3. Three parallel strips of unit boxes at 45 degrees add 3*sqrt(2), so the top vertex Q=(x,L) satisfies x+L=3+3*sqrt(2).","second":"At R=(L-1,3), y-x=4-L. Two unit widths toward Q add 2*sqrt(2), so L-x=4-L+2*sqrt(2).","sum":"Adding the two contact equations gives 3*L=7+5*sqrt(2).","scope":"A geometric derivation of this actual construction, not a lower bound for all 45-degree packings."},"claim":"실제 1980년 구성의 길이이며 모든 45도 배치의 최소라는 주장이 아님"}''')

fig=plt.figure(figsize=(12.6,8.3),facecolor=WHITE)
fig.suptitle('실제 1980년 배치는 0도와 45도로 더 작게 담았다\nHämäläinen’s 1980 packing improves the symmetric candidates',fontproperties=F,fontsize=18,color=INK,y=.975)
data=HISTORIC;L=data['side'];r=math.sqrt(2)
ax=fig.add_axes([.035,.15,.56,.69]);_aa(ax,(-.22,L+.3),(-.55,L+.35));_ad(ax,data,True)
xx=np.linspace(0,L,400)
for yy,col in [(3-xx,RED),(3+3*r-xx,RED),(xx+4-L,TEAL),(xx+4-L+2*r,TEAL)]:
    ok=(yy>=0)&(yy<=L)
    ax.plot(xx[ok],yy[ok],color=col,lw=1.5,ls=(0,(4,3)),zorder=4)
for name,offset in [('P',(-.13,-.12)),('R',(.13,-.10)),('Q',(.08,.15))]:
    x,y=data['contact'][name];ax.scatter([x],[y],s=40,color=RED,edgecolor=WHITE,zorder=7)
    _at(ax,x+offset[0],y+offset[1],name,12,ha='center',zorder=8,
        bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.9,pad=1))
_dim(ax,(0,-.24),(L,-.24),'≈ 4.69035594 cm',offset=(0,-.15),size=12)
fig.text(.79,.74,'P에서 세 줄을 건너 Q까지\nAcross three strips from P to Q',fontproperties=F,fontsize=12,color=INK,ha='center')
fig.text(.79,.665,'x + L = 3 + 3√2',fontproperties=F,fontsize=16,color=RED,ha='center')
fig.text(.79,.55,'R에서 두 칸을 건너 Q까지\nAcross two unit widths from R to Q',fontproperties=F,fontsize=12,color=INK,ha='center')
fig.text(.79,.475,'L − x = 4 − L + 2√2',fontproperties=F,fontsize=15,color=TEAL,ha='center')
fig.text(.79,.34,'두 식을 더하면\nAdding the contact equations',fontproperties=F,fontsize=12,color=INK,ha='center')
fig.text(.79,.255,'L = (7 + 5√2) / 3 cm',fontproperties=F,fontsize=16,color=INK,ha='center')
fig.text(.5,.045,'작은 상자 9개는 0도, 8개는 45도 · Q=(x,L) · 공개 원본 square-17_r2.svg의 좌표를 재현\n9 small boxes at 0°, 8 at 45°; redrawn from the published coordinate transcription',fontproperties=F,fontsize=10.5,color=MUTED,ha='center')
plt.show()

 

해멜래이넨의 실제 1980년 배치. 0도와 45도만으로 17개를 한 변 약 4.69036cm에 담는다.

페르티 해멜래이넨이 1980년에 찾은 배치입니다. 반듯한 작은 상자 아홉 개와 45도로 기울인 작은 상자 여덟 개가 들어 있습니다. 큰 상자 한 변은 약 4.69036cm입니다. 가운데 하나만 비튼 4.70711cm보다 조금 더 줄었습니다. 배치와 정확한 길이, 그림에 사용한 좌표 자료

이 45도 배치의 4.69036cm는 어떻게 나오나요?

그림의 빨간 평행선 사이에 기울어진 작은 상자 세 개의 폭이 이어집니다. 초록 평행선 사이에는 두 개의 폭이 이어집니다. 45도에서는 한 칸의 폭을 건널 때 가로 위치와 세로 위치의 합 또는 차이가 $\sqrt2$cm만큼 바뀝니다.

기울기가 -1인 직선 $y=-x+b$ 위에서는 $x+y=b$로 두 좌표의 합이 일정합니다. 기울기가 1인 직선 $y=x+b$ 위에서는 $y-x=b$로 두 좌표의 차가 일정합니다.

이 배치에서 기울어진 변은 수평선과 45도를 이루므로, 좌표의 합과 차를 이용해 평행선의 위치를 나타낼 수 있습니다. 두 평행선에서 이 값이 얼마나 달라지는지 알면 그 사이의 폭도 계산할 수 있습니다.

다만 두 절편의 부호는 기울기에 따라 다릅니다. $y=-x+b$의 x절편과 y절편은 모두 $b$이지만, $y=x+b$의 x절편은 $-b$, y절편은 $b$입니다. 두 경우 모두 절편의 절댓값은 같고, 원점을 지나는 직선에서는 두 절편이 모두 0입니다.

큰 상자 한 변을 $L$, 위쪽 벽에 닿은 점 Q의 가로 위치를 $x$라 하면, 실제 접촉점 P·R·Q에서 다음 두 식을 얻습니다.

$$\begin{aligned} (x_Q+y_Q)-(x_P+y_P) &= 3\sqrt2\\ (x+L)-(1+2) &= 3\sqrt2\\ x+L&=3+3\sqrt2 \end{aligned} $$
$$\begin{aligned} (y_Q-x_Q)-(y_R-x_R) &= 2\sqrt2\\ (L-x)-(3-(L-1)) &=2\sqrt2\\ L-x&=4-L+2\sqrt2 \end{aligned} $$

두 식을 더하면 $x$가 없어져 $3L=7+5\sqrt2$입니다. 따라서

$$ L=\frac{7+5\sqrt2}{3}\approx4.69036\text{cm}. $$

이것은 그림에 제시한 배치의 길이 계산입니다.

지금까지의 기울기는 모두 익숙한 45도입니다. 하지만 우리가 정한 각도가 45도일 뿐, 문제에 그런 규칙은 없습니다. 45도에서도 조금 더 돌리거나 덜 돌리면 어떨까요?




4. 그런데 가장 작은 배치는 이렇게 생겼습니다

1998년, 존 비드웰이 앞의 배치를 더 작게 만들었습니다. 당시 하와이대학교 학부생이었습니다. 먼저 그 결과를 보겠습니다. 발견 경위를 기록한 프리드먼의 조사 논문

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678

def _poly(item):
    a=item['angle'];c=math.cos(a);s=math.sin(a)
    return np.array([[item['x']+(u*c-v*s)/2,item['y']+(u*s+v*c)/2] for u,v in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def _text(ax,x,y,text,size=12,**kw):
    return ax.text(x,y,text,fontproperties=F,fontsize=size,color=kw.pop('color',INK),**kw)

def _axis(ax,xlim,ylim):
    ax.set_xlim(*xlim);ax.set_ylim(*ylim);ax.set_aspect('equal');ax.axis('off')

def _draw(ax,data,selected=None,number=True):
    for q in data['squares']:
        active=selected is None or q['id'] in selected
        fill=[BLUE,ORANGE,TEAL][q['group']] if active else '#eff2f5'
        edge=INK if active else '#d7dfe6'
        ax.add_patch(Polygon(_poly(q),facecolor=fill,edgecolor=edge,lw=1.15 if active else .7,zorder=2))
        if number and active:_text(ax,q['x'],q['y'],str(q['id']),11,ha='center',va='center',zorder=6)

def _border(ax,L):
    ax.add_patch(Rectangle((0,0),L,L,facecolor='none',edgecolor=INK,lw=2,zorder=4))

def _heading(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.975)

def _foot(fig,ko,en,y=.035,size=11):
    fig.text(.5,y,ko+'\n'+en,fontproperties=F,fontsize=size,color=MUTED,ha='center',va='bottom')

def _arrow(ax,a,b,text=None,offset=(0,0),size=11,color=None):
    a=np.asarray(a);b=np.asarray(b);col=INK if color is None else color
    ax.add_patch(FancyArrowPatch(a,b,arrowstyle='<->',mutation_scale=11,color=col,lw=1.25,zorder=7))
    if text is not None:
        p=(a+b)/2+np.asarray(offset)
        _text(ax,p[0],p[1],text,size,ha='center',va='center',zorder=8,
              bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.94,pad=1.5))

fig=plt.figure(figsize=(10.5,8.1),facecolor=WHITE)
_heading(fig,'17개가 들어가고도 빈틈이 남는 실제 배치','The actual packing of 17 small boxes')
ax=fig.add_axes([.035,.15,.70,.69]);L=D['side']
_axis(ax,(-.3,L+.3),(-.55,L+.2));_draw(ax,D);_border(ax,L)
_arrow(ax,(0,-.25),(L,-.25),'≈ 4.67553 cm',offset=(0,-.13),size=13)
unit=fig.add_axes([.77,.40,.19,.25]);_axis(unit,(-.2,1.2),(-.4,1.3))
unit.add_patch(Rectangle((0,0),1,1,facecolor=BLUE,edgecolor=INK,lw=1.6))
_arrow(unit,(0,-.17),(1,-.17),'1 cm',offset=(0,-.13))
_text(unit,.5,1.17,'작은 상자\nSmall box',12,ha='center',va='bottom')
fig.text(.855,.27,'17개 모두 같은 크기\n17 equal small boxes',fontproperties=F,fontsize=11,color=MUTED,ha='center')
_foot(fig,'큰 상자 안의 빈 넓이 ≈ 4.86cm² · John Bidwell, 1998','Unused area inside the big box: approximately 4.86 cm²')
plt.show()

 

비드웰이 1998년에 찾은 배치. 서로 다른 세 방향의 작은 상자 17개가 한 변 약 4.67553cm에 들어간다.

큰 상자 한 변은 약 4.67553cm입니다. 가운데 하나만 비튼 대칭 배치보다도, 앞의 45도 배치보다도 작습니다.

그런데 생김새가 묘합니다. 반듯한 상자 열 개, 약 39.805도로 기울어진 상자 여섯 개, 반대쪽으로 약 36.624도 기울어진 상자 한 개입니다. 좌우가 맞지도 않고, 기울기도 같지 않습니다. 안에는 작은 상자 네 개의 넓이보다 많은 빈틈까지 남아 있습니다.

공개된 Lean 최적성 증명이 내놓는 결론은 더 놀랍습니다. 이것이 더 작은 큰 상자에는 17개를 담을 수 없는 최소 배치라는 것입니다. 작은 상자들을 자유롭게 옮기고 돌리는 경우까지 다룹니다. 그러면 이제 증명을 따라가 봅시다. 먼저 이 배치를 조금 고치면 어디가 막히는지 보고, 그다음 전혀 다른 배치까지 어떻게 다루는지 살펴보겠습니다.




5. 가운데를 더 기울이면 작아지지만, 마지막 상자가 이웃과 겹칩니다

오른쪽 위의 16번 작은 상자를 보세요. 혼자 다른 방향으로 기울어져 있습니다. 이 상자는 위의 15번, 오른쪽 위의 8번, 오른쪽의 17번, 왼쪽 아래의 12번에 닿습니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678

def _poly(item):
    a=item['angle'];c=math.cos(a);s=math.sin(a)
    return np.array([[item['x']+(u*c-v*s)/2,item['y']+(u*s+v*c)/2] for u,v in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def _text(ax,x,y,text,size=12,**kw):
    return ax.text(x,y,text,fontproperties=F,fontsize=size,color=kw.pop('color',INK),**kw)

def _axis(ax,xlim,ylim):
    ax.set_xlim(*xlim);ax.set_ylim(*ylim);ax.set_aspect('equal');ax.axis('off')

def _draw(ax,data,selected=None,number=True):
    for q in data['squares']:
        active=selected is None or q['id'] in selected
        fill=[BLUE,ORANGE,TEAL][q['group']] if active else '#eff2f5'
        edge=INK if active else '#d7dfe6'
        ax.add_patch(Polygon(_poly(q),facecolor=fill,edgecolor=edge,lw=1.15 if active else .7,zorder=2))
        if number and active:_text(ax,q['x'],q['y'],str(q['id']),11,ha='center',va='center',zorder=6)

def _border(ax,L):
    ax.add_patch(Rectangle((0,0),L,L,facecolor='none',edgecolor=INK,lw=2,zorder=4))

def _heading(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.975)

def _foot(fig,ko,en,y=.035,size=11):
    fig.text(.5,y,ko+'\n'+en,fontproperties=F,fontsize=size,color=MUTED,ha='center',va='bottom')

def _arrow(ax,a,b,text=None,offset=(0,0),size=11,color=None):
    a=np.asarray(a);b=np.asarray(b);col=INK if color is None else color
    ax.add_patch(FancyArrowPatch(a,b,arrowstyle='<->',mutation_scale=11,color=col,lw=1.25,zorder=7))
    if text is not None:
        p=(a+b)/2+np.asarray(offset)
        _text(ax,p[0],p[1],text,size,ha='center',va='center',zorder=8,
              bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.94,pad=1.5))

fig,ax=plt.subplots(figsize=(9.8,8.2),facecolor=WHITE)
_heading(fig,'작은 상자 16은 네 이웃에 동시에 맞닿는다','Small box 16 meets boxes 15, 8, 17 and 12',18)
data=construct(endpointTheta);L=data['side']
_axis(ax,(1.25,5.0),(1.9,4.92));_draw(ax,data,[8,12,15,16,17]);_border(ax,L)
points=data.get('contactpoints')
if points is None:
    q15=data['squares'][14];q16=data['squares'][15];q17=data['squares'][16]
    d=math.cos(q16['angle']);e=-math.sin(q16['angle'])
    points={'C':[q15['x']+.5,L-1],'D':[L-1,L-1],'E':[L-1,q17['y']+.5],
            'P':[q16['x']-(d+e)/2,q16['y']+(e-d)/2]}
offsets={'C':(-.12,.14),'D':(.12,.12),'E':(.13,-.11),'P':(-.17,-.12)}
for key in ('C','D','E','P'):
    x,y=points[key];dx,dy=offsets[key]
    ax.scatter([x],[y],s=65,color=RED,edgecolor=WHITE,lw=.8,zorder=7)
    _text(ax,x+dx,y+dy,key,13,ha='center',va='center',zorder=8,
          bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.95,pad=1.5))
fig.subplots_adjust(left=.08,right=.95,top=.80,bottom=.17)
_foot(fig,'C: 15번 · D: 8번 · E: 17번 · P: 12번과의 접촉','Four actual contact points around small box 16',size=12)
plt.show()

 

16번 작은 상자는 15번·8번·17번·12번에 동시에 닿는다. 빨간 점이 네 접촉점이다.

가운데 여섯 상자의 각도를 바꾸면 그 주변의 자리도 바뀝니다. 16번만 따로 돌려서는 네 이웃 사이에 맞출 수 없습니다. 이웃들의 자리와 16번의 기울기를 함께 바꿔야 합니다.

아래에서는 16번이 15번·8번·17번에 닿도록 17개의 자리를 다시 계산합니다. 남은 12번과는 틈이 남는지, 닿는지, 겹치는지를 확인합니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678

def _poly(item):
    a=item['angle'];c=math.cos(a);s=math.sin(a)
    return np.array([[item['x']+(u*c-v*s)/2,item['y']+(u*s+v*c)/2] for u,v in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def _text(ax,x,y,text,size=12,**kw):
    return ax.text(x,y,text,fontproperties=F,fontsize=size,color=kw.pop('color',INK),**kw)

def _axis(ax,xlim,ylim):
    ax.set_xlim(*xlim);ax.set_ylim(*ylim);ax.set_aspect('equal');ax.axis('off')

def _draw(ax,data,selected=None,number=True):
    for q in data['squares']:
        active=selected is None or q['id'] in selected
        fill=[BLUE,ORANGE,TEAL][q['group']] if active else '#eff2f5'
        edge=INK if active else '#d7dfe6'
        ax.add_patch(Polygon(_poly(q),facecolor=fill,edgecolor=edge,lw=1.15 if active else .7,zorder=2))
        if number and active:_text(ax,q['x'],q['y'],str(q['id']),11,ha='center',va='center',zorder=6)

def _border(ax,L):
    ax.add_patch(Rectangle((0,0),L,L,facecolor='none',edgecolor=INK,lw=2,zorder=4))

def _heading(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.975)

def _foot(fig,ko,en,y=.035,size=11):
    fig.text(.5,y,ko+'\n'+en,fontproperties=F,fontsize=size,color=MUTED,ha='center',va='bottom')

def _arrow(ax,a,b,text=None,offset=(0,0),size=11,color=None):
    a=np.asarray(a);b=np.asarray(b);col=INK if color is None else color
    ax.add_patch(FancyArrowPatch(a,b,arrowstyle='<->',mutation_scale=11,color=col,lw=1.25,zorder=7))
    if text is not None:
        p=(a+b)/2+np.asarray(offset)
        _text(ax,p[0],p[1],text,size,ha='center',va='center',zorder=8,
              bbox=dict(facecolor=WHITE,edgecolor='none',alpha=.94,pad=1.5))

from matplotlib.patches import ConnectionPatch
def _intersection(subject,clipper):
    output=[np.asarray(p,dtype=float) for p in subject]
    for k in range(len(clipper)):
        if not output:break
        a=np.asarray(clipper[k]);b=np.asarray(clipper[(k+1)%len(clipper)]);edge=b-a
        def side(p):return edge[0]*(p[1]-a[1])-edge[1]*(p[0]-a[0])
        incoming=output;output=[];last=incoming[-1];lv=side(last)
        for point in incoming:
            pv=side(point)
            if (lv>=-1e-13)!=(pv>=-1e-13):
                output.append(last+(point-last)*lv/(lv-pv))
            if pv>=-1e-13:output.append(point)
            last,lv=point,pv
    return np.asarray(output)

fig=plt.figure(figsize=(14.4,10.3),facecolor=WHITE)
_heading(fig,'내부 배치를 다시 맞추며 줄이면, 12번과 16번이 만난다','Recomputed internal arrangements reach the 12–16 contact',18)
angles=[39.5,endpointTheta,39.82]
statuses=[('아직 떨어져 있음','A gap remains'),('정확히 맞닿음','Contact at the endpoint'),('내부가 겹침','Interiors overlap')]
for k,(theta,(ko,en)) in enumerate(zip(angles,statuses)):
    data=construct(theta);x0=.035+k*.325
    ax=fig.add_axes([x0,.43,.285,.36]);_axis(ax,(1.65,4.80),(1.95,4.87))
    _draw(ax,data,[8,12,15,16,17]);_border(ax,data['side'])
    a=data['squares'][11];b=data['squares'][15]
    d=math.cos(b['angle']);e=-math.sin(b['angle'])
    p=np.array([b['x']-(d+e)/2,b['y']+(e-d)/2])
    overlap=_intersection(_poly(a),_poly(b))
    if len(overlap)>=3:
        ax.add_patch(Polygon(overlap,facecolor=RED,edgecolor=RED,lw=1,zorder=5))
    angle_label='≈39.80496°' if k==1 else f'{theta:g}°'
    ax.set_title(angle_label+f"\nL ≈ {data['side']:.8f} cm",fontproperties=F,fontsize=12,color=INK,pad=10)
    radius=.11
    ax.add_patch(Rectangle(p-radius,2*radius,2*radius,facecolor='none',edgecolor=MUTED,lw=1.1,ls='--',zorder=8))
    detail=fig.add_axes([x0+.045,.145,.20,.20]);_axis(detail,(p[0]-radius,p[0]+radius),(p[1]-radius,p[1]+radius))
    for q in (a,b):
        detail.add_patch(Polygon(_poly(q),facecolor=ORANGE if q['id']==12 else TEAL,edgecolor=INK,lw=1.4,zorder=2))
    if len(overlap)>=3:
        detail.add_patch(Polygon(overlap,facecolor=RED,edgecolor=RED,lw=.8,zorder=5))
    if k==1:detail.scatter([p[0]],[p[1]],s=34,color=RED,edgecolor=WHITE,zorder=6)
    for spine in detail.spines.values():spine.set_visible(True);spine.set_color('#b6c2cd')
    detail.add_patch(Rectangle((p[0]-radius,p[1]-radius),2*radius,2*radius,facecolor='none',edgecolor='#b6c2cd',lw=1,zorder=8))
    fig.add_artist(ConnectionPatch(xyA=(p[0],p[1]-radius),coordsA=ax.transData,
                                  xyB=(p[0],p[1]+radius),coordsB=detail.transData,
                                  arrowstyle='->',mutation_scale=10,color=MUTED,lw=.9))
    fig.text(x0+.145,.105,ko+'\n'+en,fontproperties=F,fontsize=11.5,color=RED if k==2 else INK,ha='center')
_foot(fig,'각 패널은 작은 상자 17개의 위치를 접촉식으로 다시 계산 · 아래는 같은 축척의 접촉부 확대','All 17 positions are recomputed in each panel; the lower details use the same scale',y=.025,size=11)
plt.show()

 

17개의 위치를 함께 바꾸며 더 작게 만든 후보들. 가운데의 접촉 순간을 지나면 오른쪽처럼 12번과 16번이 겹친다.

왼쪽의 39.5도 배치에는 한 변 약 4.67928cm가 필요합니다. 가운데의 약 39.805도 배치에서는 4.67553cm로 줄었습니다. 두 배치에는 모두 17개가 들어갑니다. 가운데 배치에서 16번의 왼쪽 끝이 12번에 닿습니다.

조금만 더 바꿔 39.82도로 만들면 계산된 길이는 약 4.67535cm로 더 작아집니다. 하지만 확대도의 빨간 부분처럼 12번과 16번이 겹칩니다. 이 방향으로 줄이던 방법은 바로 여기서 막힙니다.

아래에서 ‘39.82°’를 눌러 보세요. 가운데 각도 하나를 바꾸면 다른 상자들의 자리와 16번의 기울기도 함께 바뀝니다. 전체 그림에서 거의 보이지 않는 겹침이 확대도에 나타납니다.

기울기를 바꾸면 마지막 자리는 어떻게 될까요?

작은 상자 한 변은 1cm입니다. 가운데 작은 상자 6개의 각도를 바꾸면 이 접촉 방식에 맞춰 17개의 위치와 큰 상자의 길이가 함께 계산됩니다.

큰 상자의 한 변 cm · 16번 작은 상자의 기울기 °

17개 전체

12번과 16번 확대

· 벽 밖 개 · 겹친 쌍 쌍

빨간색은 겹치거나 벽 밖으로 나간 작은 상자입니다. 확대 그림에서 진한 빨간색은 실제 겹친 부분입니다. 이 접촉 구조 안에서 각도를 바꾸는 실험이며, 전혀 다른 배치까지 증명하는 것은 아닙니다.

 

 

이것은 비드웰의 실제 발상 순서를 재연한 것이 아닙니다. 발견 당시의 상세한 작업 기록은 확인한 자료에 없고, 위 계산은 완성된 배치의 접촉 관계에서 거꾸로 읽은 것입니다. 여기서 확인한 것은 이 접촉 관계를 따라 더 줄이려 하면 어디서 충돌하는가입니다. 계산에 사용한 실제 배치식

이제 더 큰 질문이 남습니다. 12번과 16번의 관계부터 바꾸거나, 17개 전부를 아예 다른 모양으로 놓으면 어떨까요? 최적성 증명은 그 경우까지 막아야 합니다.




6. 모든 배치를 다루려면, 먼저 중심이 갈 수 있는 곳을 찾습니다

작은 상자 17개를 마음대로 놓는다고 생각해 봅시다. 중심은 어디든 움직일 수 있고 각도도 바꿀 수 있으니, 가능한 그림은 끝없이 많습니다. 증명은 이것들을 중심의 위치에 따라 묶는 데서 시작합니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig, axes = plt.subplots(1, 2, figsize=(13.8, 7.5), facecolor=WHITE)
title(fig, '작은 상자의 중심은 어느 벽에서도 0.5cm 이상 떨어진다',
      'The inscribed disk gives a centre bound for every rotation', 18)

left, right = axes
left.set_xlim(-0.05, 2.25); left.set_ylim(-0.05, 2.3)
left.set_aspect('equal'); left.axis('off')
item = {'x': 1.0, 'y': 1.0, 'angle': math.radians(29)}
left.add_patch(Polygon(vertices(item), facecolor='#e7eff7', edgecolor=BLUE, lw=2.3))
left.add_patch(Circle((1, 1), 0.5, facecolor=GREEN, edgecolor=TEAL, lw=2.0, alpha=0.9))
left.scatter([1], [1], s=35, color=INK, zorder=5)
left.add_patch(FancyArrowPatch((1, 1), (1.5, 1), arrowstyle='<->',
                             mutation_scale=13, color=INK, lw=1.4))
label(left, 1.25, 1.12, '0.5cm', 13, ha='center')
label(left, 1.0, 1.93, '작은 상자 한 변 1cm', 15, ha='center')
label(left, 1.0, 1.73, 'A small box has side 1 cm', 12, ha='center', color=MUTED)
label(left, 1.0, 0.12, '돌려도 반지름 0.5cm인 원은 안에 남는다', 12, ha='center')
label(left, 1.0, -0.08, 'The radius-0.5 cm disk stays inside', 11, ha='center', color=MUTED)

U = COVER['cover_frame']['side_float']
right.set_xlim(-0.45, 5.08); right.set_ylim(-0.65, 5.0)
right.set_aspect('equal'); right.axis('off')
right.add_patch(Rectangle((0, 0), U, U, facecolor='#f1f2f4', edgecolor=INK, lw=2.0))
right.add_patch(Rectangle((0.5, 0.5), U-1, U-1, facecolor=GREEN,
                          edgecolor=TEAL, lw=1.8, linestyle='--'))
right.add_patch(Circle((0.25, U/2), 0.5, facecolor=RED, edgecolor=RED, alpha=0.22, lw=2))
right.scatter([0.25], [U/2], s=36, color=RED, zorder=4)
right.add_patch(FancyArrowPatch((0, 0.24), (0.5, 0.24), arrowstyle='<->',
                              mutation_scale=11, color=INK, lw=1.1))
label(right, 0.25, -0.03, '0.5cm', 11, ha='center', va='top')
label(right, U/2, 3.17, '중심의 필요한 범위', 14, ha='center')
label(right, U/2, 2.89, 'Necessary centre range', 12, ha='center', color=MUTED)
label(right, U/2, 2.38, '0.5 ≤ x, y ≤ 4.176', 14, ha='center')
label(right, 1.02, 1.47, '벽 가까이에서는 원부터 벽을 넘는다', 11, ha='left', color=RED)
label(right, 1.02, 1.22, 'Too close: even the disk crosses the wall', 10, ha='left', color=RED)
label(right, U/2, -0.42, '덮개 틀 한 변 4.676cm / Cover frame: 4.676 cm', 11, ha='center')
fig.text(0.5, 0.055, '중심 범위는 필요조건이다. 이 범위의 모든 위치·각도가 가능한 것은 아니다.\n'
         'The bound is necessary; it does not make every position and angle feasible.',
         fontproperties=F, fontsize=11, color=MUTED, ha='center')
fig.subplots_adjust(top=0.79, bottom=0.14, left=0.045, right=0.985, wspace=0.19)
plt.show()

 

작은 상자를 돌려도 반지름 0.5cm인 원은 안에 남는다. 원이 벽을 넘지 않으려면 중심은 벽에서 0.5cm 이상 떨어져야 한다.

작은 상자 하나 안에 중심이 같고 반지름이 0.5cm인 원을 그립니다. 작은 상자를 돌려도 이 원은 상자 안에 그대로 들어 있습니다. 따라서 중심이 벽에서 0.5cm보다 가까워지면 원부터 벽을 넘습니다. 작은 상자의 중심은 어느 벽에서도 적어도 0.5cm 떨어져야 합니다. 각도를 미리 정하지 않고 얻은 조건입니다.

원문의 증명은 한 변 4.676cm인 틀을 씁니다. 답인 4.67553cm보다 아주 조금 넓은 틀입니다. 이보다 작은 큰 상자를 틀 안에 놓으면, 그 안의 모든 중심도 같은 좌표로 살펴볼 수 있습니다. 실제 큰 상자의 한 변은 따로 유지합니다.

틀의 왼쪽 아래에서 잰 가로·세로 위치는 모두 0.5cm부터 4.176cm까지입니다. 이 범위 밖은 살펴볼 필요가 없어졌습니다. 중심 범위와 좌표틀을 정하는 증명




7. 중심이 갈 수 있는 곳을 24개 영역으로 빠짐없이 덮습니다

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig, ax = plt.subplots(figsize=(10.0, 9.5), facecolor=WHITE)
title(fig, '어떤 중심도 빠지지 않는 실제 24개 영역 덮개',
      'The actual 24-region closed cover of all possible centres', 18)
U = COVER['cover_frame']['side_float']
ax.set_xlim(-0.35, 5.02); ax.set_ylim(-0.56, 5.05)
ax.set_aspect('equal'); ax.axis('off')
for cell in COVER['cells']:
    poly = np.array(cell['vertices_float'])
    fill = '#e7edf5' if cell['class'] == 'wall' else '#dcece8'
    ax.add_patch(Polygon(poly, facecolor=fill, edgecolor=INK, lw=1.1, alpha=0.86))
    centre = poly.mean(axis=0)
    label(ax, centre[0], centre[1], 'R'+str(cell['reader_label']), 11,
          ha='center', va='center', bbox=dict(facecolor=WHITE, alpha=0.85,
                                            edgecolor='none', pad=1.2))
border(ax, U, edgecolor=MUTED, linestyle='--', lw=1.4)
ax.add_patch(Rectangle((0.5, 0.5), U-1, U-1, fill=False, edgecolor=BLUE, lw=2.4))
ax.add_patch(FancyArrowPatch((0, -0.22), (U, -0.22), arrowstyle='<->',
                            mutation_scale=12, color=INK, lw=1.2))
label(ax, U/2, -0.40, '덮개 틀 한 변 4.676cm / Cover-frame side: 4.676 cm', 12, ha='center')
label(ax, U/2, 4.86, '중심 범위 [0.5, 4.176] × [0.5, 4.176] cm', 12, ha='center')
fig.text(0.5, 0.078, 'R1–R24는 영역 번호다. 작은 상자 번호가 아니다.\n'
         'R1–R24 label centre regions, not the 17 small boxes.',
         fontproperties=F, fontsize=12, color=INK, ha='center')
fig.text(0.5, 0.026, '영역끼리 겹치는 곳도 있다. 중심마다 속한 영역 하나를 고른다.\n'
         'Regions may overlap; select one containing region for each centre.',
         fontproperties=F, fontsize=10.8, color=MUTED, ha='center')
fig.subplots_adjust(top=0.82, bottom=0.16, left=0.055, right=0.98)
plt.show()

 

실제 증명에 쓰이는 24개 중심 영역. 파란 테두리 안의 어느 중심 위치도 적어도 한 영역에 속한다.

원문에서 쓰는 실제 영역들입니다. 바깥쪽 열여섯 개와 안쪽 여덟 개, 합쳐서 스물네 개입니다. 작은 상자의 모양을 그린 것이 아니라, 중심이 어느 영역에 들어가는지 분류할 지도입니다. R1부터 R24는 영역 번호입니다.

이 지도가 증명에 쓰이려면 두 가지가 필요합니다.

어느 중심도 지도에서 빠지면 안 되고, 한 영역에는 작은 상자의 중심 두 개가 들어갈 수 없어야 합니다.

각 영역이 중심을 최대 하나만 받는다는 사실과 비둘기집 원리를 함께 쓰면, 이 틀 안의 작은 상자는 최대 24개라는 상한을 얻습니다. 하지만 이것만으로 17개가 들어갈 수 없는 것은 아닙니다. 뒤에서는 24개 영역 중 어느 17개를 쓰는지 나누고, 각 경우에 더 작은 배치가 가능한지 따로 확인합니다.

먼저 빠진 곳이 없는지 보겠습니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig, axes = plt.subplots(1, 2, figsize=(13.8, 8.3), facecolor=WHITE)
title(fig, '실제 첫 세로띠에서는 다섯 영역이 빈틈없이 이어진다',
      'Five actual regions cover the first vertical slab without a gap', 17)
left, right = axes
U = COVER['cover_frame']['side_float']
left.set_xlim(0.0, 4.75); left.set_ylim(-0.08, 5.05)
left.set_aspect('equal'); left.axis('off')
for cell in COVER['cells']:
    left.add_patch(Polygon(cell['vertices_float'], facecolor='#e9eff2',
                           edgecolor=MUTED, lw=0.8, alpha=0.65))
for x in COVER['coverage_certificate']['x_breaks_float']:
    left.plot([x, x], [0.5, U-0.5], color=MUTED, lw=0.5, alpha=0.32)
left.add_patch(Rectangle((0.5, 0.5), 0.79, U-1, facecolor=ORANGE,
                         edgecolor=RED, lw=2.0, alpha=0.37))
label(left, 2.338, 4.78, '실제 덮개와 22개 세로띠', 13, ha='center')
label(left, 2.338, 4.53, 'The cover and its 22 vertical slabs', 11, ha='center', color=MUTED)
left.add_patch(FancyArrowPatch((0.5, 0.22), (1.29, 0.22), arrowstyle='<->',
                             mutation_scale=11, color=INK, lw=1.2))
label(left, 0.90, -0.015, 'x: 0.5–1.29cm', 11, ha='center')

right.set_xlim(-0.05, 3.25); right.set_ylim(-0.08, 5.05)
right.set_aspect('equal'); right.axis('off')
palette = ['#d8e5f2', '#f2dfc6', '#d7ebe5', '#f2dfc6', '#d8e5f2']
for row, fill in zip(COVER['first_strip']['bottom_to_top'], palette):
    low, high = row['y_interval_float']
    right.add_patch(Rectangle((0.5, low), 0.79, high-low, facecolor=fill, edgecolor=INK, lw=1.2))
    label(right, 0.895, (low+high)/2, 'R'+str(row['reader_label']), 14, ha='center', va='center')
yb = [0.5] + [row['y_interval_float'][1] for row in COVER['first_strip']['bottom_to_top']]
values = ['0.5', '1.29', '1.995333…', '2.680666…', '3.386', '4.176']
for y, value in zip(yb, values):
    right.plot([1.29, 1.52], [y, y], color=MUTED, lw=0.9)
    label(right, 1.62, y, value+' cm', 11, va='center')
label(right, 1.64, 4.78, '끝점 = 다음 시작점', 13, ha='center')
label(right, 1.64, 4.53, 'Each endpoint meets the next', 11, ha='center', color=MUTED)
fig.text(0.5, 0.13, '1.995333… = 2993/1500,   2.680666… = 4021/1500\n'
         '닫힌 구간이므로 경계의 높이도 빠지지 않는다. / Closed intervals include every shared boundary.',
         fontproperties=F, fontsize=11.5, ha='center', color=INK)
fig.text(0.5, 0.045, '원문은 나머지 21개 띠도 확인한다. 전체는 22개 띠·114개 닫힌 사다리꼴이다.\n'
         'The source checks all 22 slabs using 114 closed trapezoids; this panel spells out the first slab.',
         fontproperties=F, fontsize=10.8, ha='center', color=MUTED)
fig.subplots_adjust(top=0.79, bottom=0.23, left=0.04, right=0.985, wspace=0.19)
plt.show()

 

실제 덮개의 첫 세로 부분에서는 다섯 영역의 끝과 시작이 이어진다. 원문은 나머지 부분도 빠짐없이 확인한다.

왼쪽의 가느다란 세로 부분을 확대했습니다. 아래에서부터 R1, R7, R11, R15, R3이 이어집니다. 앞 영역이 끝나는 높이에서 다음 영역이 시작합니다. 각 영역의 가로 폭도 이 세로 부분을 덮습니다. 그러니 이 안의 어느 높이를 골라도 지도에서 빠지지 않습니다.

원문은 전체를 세로로 스물두 부분으로 나누어 같은 확인을 합니다. 안쪽의 기울어진 경계에서는 사다리꼴을 사용합니다. 모서리만 영역 안에 드는 것이 아니라 그 사이까지 전부 영역 안에 들어감을 확인합니다. 모두 합쳐 114개의 사다리꼴로 중심 범위를 덮습니다. 몇 군데를 찍어 보는 검사와 달리, 선과 면 전체를 다루는 것입니다. 실제 덮개 자료, 빠짐없음을 보이는 증명

경계 위의 중심도 포함합니다. 영역끼리 겹치는 곳도 있습니다. 중심이 여러 영역에 속하면 그중 한 영역을 골라 배정하면 됩니다. 어느 영역에도 속하지 않는 위치는 없습니다.




8. 안쪽 영역에는 중심 두 개를 넣을 수 없습니다

이제 지도의 두 번째 조건을 보겠습니다. 안쪽의 R19를 확대해 봅시다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig, axes = plt.subplots(1, 2, figsize=(14.6, 8.1), facecolor=WHITE)
title(fig, '영역 19에 중심 두 개를 놓으면 작은 상자가 반드시 겹친다',
      'Two centres in actual region 19 force overlap for every rotation', 17)
poly = np.array(COVER['cells'][18]['vertices_float'])
left, right = axes
left.set_xlim(1.13, 2.65); left.set_ylim(1.65, 3.00)
left.set_aspect('equal'); left.axis('off')
left.add_patch(Polygon(poly, facecolor=GREEN, edgecolor=TEAL, lw=2.1))
for point, name in zip(poly, 'ABCDE'):
    left.scatter([point[0]], [point[1]], color=INK, s=20)
    offset = {'A': (-0.075, -0.08), 'B': (0.03, -0.075), 'C': (0.08, 0),
              'D': (0.03, 0.07), 'E': (-0.075, 0.07)}[name]
    label(left, point[0]+offset[0], point[1]+offset[1], name, 13, ha='center')
A, C = poly[0], poly[2]
left.plot([A[0], C[0]], [A[1], C[1]], color=RED, lw=2.1)
left.plot([A[0], C[0], C[0]], [A[1], A[1], C[1]], color=MUTED, lw=1.1, linestyle='--')
label(left, (A[0]+C[0])/2, A[1]-0.16, '0.908cm', 12, ha='center')
label(left, C[0]+0.08, (A[1]+C[1])/2-0.03, '0.34cm', 11, ha='left')
label(left, 1.90, 2.92, '실제 영역 19 / Actual region R19', 13, ha='center')
label(left, 1.90, 1.70, '가장 먼 거리도 약 0.96957cm < 1cm', 11, ha='center')

example = COVER['interior_source18']['example_two_centres']
P = np.array(example['P_float']); Q = np.array(example['Q_float'])
M = np.array(example['M_midpoint_float'])
right.set_xlim(0.75, 3.14); right.set_ylim(1.24, 3.37)
right.set_aspect('equal'); right.axis('off')
right.add_patch(Polygon(poly, facecolor='#edf4ef', edgecolor=TEAL, lw=1.2, zorder=0))
for centre, angle, color in [(P, 0, BLUE), (Q, math.pi/6, ORANGE)]:
    item = {'x': float(centre[0]), 'y': float(centre[1]), 'angle': angle}
    right.add_patch(Polygon(vertices(item), facecolor=color, edgecolor=color, alpha=0.18, lw=2.2))
    right.add_patch(Polygon(vertices(item), fill=False, edgecolor=color, lw=1.9))
    right.add_patch(Circle(centre, 0.5, fill=False, edgecolor=color, lw=2.2))
right.plot([P[0], Q[0]], [P[1], Q[1]], color=INK, lw=1.2, linestyle=':')
for point, name, dy in [(P, 'P', -0.17), (Q, 'Q', 0.16)]:
    right.scatter([point[0]], [point[1]], s=30, color=INK, zorder=6)
    label(right, point[0], point[1]+dy, name, 13, ha='center')
right.scatter([M[0]], [M[1]], s=55, color=RED, zorder=7)
label(right, M[0], M[1]+0.18, '중점 M / Midpoint', 11, ha='center', color=RED)
label(right, 1.94, 3.27, '영역 안의 가상 후보 P, Q', 13, ha='center')
label(right, 1.94, 3.08, 'A hypothetical pair within R19', 11, ha='center', color=MUTED)
label(right, 1.94, 1.32, '한 변 1cm · 원 반지름 0.5cm', 11, ha='center')
fig.text(0.26, 0.125, 'AC² = 0.908² + 0.34² = 0.940064 < 1\n'
         '영역 안의 모든 두 점에 적용된다.\nAll point pairs obey this bound.',
         fontproperties=F, fontsize=11.5, color=INK, ha='center')
fig.text(0.76, 0.125, 'PQ² = 5/8 < 1,  PM² = QM² = 5/32 < 1/4\n'
         'M은 두 작은 상자 안쪽에 있다.\nM is inside both small boxes.',
         fontproperties=F, fontsize=11.5, color=INK, ha='center')
fig.text(0.5, 0.035, '오른쪽은 유효한 배치가 아니다. 같은 영역에 중심 둘을 넣으려는 가정의 실패를 보여 준다.\n'
         'The right panel is an excluded candidate, not part of a valid packing. All small-box angles are covered by the proof.',
         fontproperties=F, fontsize=10.5, color=MUTED, ha='center')
fig.subplots_adjust(top=0.76, bottom=0.24, left=0.025, right=0.985, wspace=0.13)
plt.show()

 

R19 안의 두 점은 항상 1cm보다 가깝다. 두 중심의 중점 M이 양쪽 작은 상자 안에 동시에 들어가므로 겹침을 피할 수 없다.

이 영역에서 가장 멀리 떨어진 두 점의 거리도 약 0.96957cm입니다. 1cm보다 짧습니다. 그림 왼쪽의 빨간 선이 그 최대 길이 중 하나입니다. 모양이 안쪽으로 패이지 않은 다각형이므로, 가장 먼 두 점은 꼭짓점들 사이에서 찾으면 됩니다. 원문은 모든 꼭짓점 쌍의 거리를 확인합니다.

그 안에 작은 상자들의 중심 P와 Q를 둘 다 놓았다고 해 봅시다. 두 중심 사이 거리는 반드시 1cm보다 짧습니다. P와 Q의 딱 중간인 M은 각 중심에서 0.5cm보다 가까이 있습니다.

그런데 작은 상자마다 반지름 0.5cm인 원이 들어 있었습니다. 그러니 M은 두 원의 안쪽에 동시에 들어가고, 따라서 두 작은 상자의 안쪽에도 동시에 들어갑니다. 두 상자가 겹친다는 뜻입니다. 하나를 돌려 피하려 해도 원은 상자 안에 남으므로 소용없습니다.

따라서 R19에는 작은 상자의 중심을 두 개 둘 수 없습니다. 같은 거리 계산이 안쪽 여덟 영역 모두에 적용됩니다. 이로써 여덟 영역은 각자 중심을 최대 하나씩만 받을 수 있습니다. 거리와 중점으로 겹침을 증명한 원문

0.96957cm가 나오는 실제 길이 계산

빨간 선의 가로 차이는 0.908cm, 세로 차이는 0.34cm입니다. 피타고라스 정리로

$$ \text{길이}^2=0.908^2+0.34^2=0.940064<1 $$

입니다. 따라서 길이는 약 0.96957cm입니다. 실제 R19의 나머지 꼭짓점 쌍도 이 길이를 넘지 않습니다.

영역 안의 두 점을 더 멀리 떼려면 한 점을 바깥 경계까지 밀 수 있습니다. 그 점이 변 중간에 있다면 변 양 끝 중 더 먼 끝을 고릅니다. 다른 점에도 같은 일을 하면 꼭짓점 두 개가 남습니다. 그러므로 꼭짓점 쌍의 최대거리로 영역 전체의 최대거리를 알 수 있습니다.




9. 벽 쪽 영역에서는 ‘돌려 피하기’도 벽에 막힙니다

바깥쪽 열여섯 영역에는 방금 계산을 그대로 쓸 수 없습니다. 예를 들어 왼쪽 아래 R1은 가로·세로가 0.79cm여서, 대각선 길이는 1cm보다 깁니다. 여기서는 두 작은 상자 모두 벽을 넘으면 안 된다는 조건도 써야 합니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig,axes=plt.subplots(1,2,figsize=(13.4,7.8),facecolor=WHITE)
title(fig,'벽에 붙은 작은 상자는 돌아갈 수 있는 방향도 제한된다',
      'Walls restrict the rotations: one exact corner obstruction',17)
P=np.array([.5,.5]);Q=np.array([1.29,1.29])
closest=np.array([1.,1.]);distance=float(np.linalg.norm(Q-closest))
assert distance<.5
for ax in axes:
    ax.set_xlim(-.27,2.18);ax.set_ylim(-.45,2.12);ax.set_aspect('equal');ax.axis('off')
    ax.plot([0,0,2.1],[2.1,0,0],color=INK,lw=2.6)
    ax.add_patch(Rectangle((.5,.5),.79,.79,facecolor=GREEN,edgecolor=TEAL,lw=1.6,alpha=.8))
    ax.add_patch(Rectangle((0,0),1,1,facecolor=BLUE,edgecolor=INK,lw=1.8,alpha=.4))
    ax.scatter([P[0]],[P[1]],s=35,color=INK,zorder=6)
    label(ax,.5,.34,'P',13,ha='center')
label(axes[0],1.55,.75,'영역 R1',13,ha='center')
label(axes[0],1.55,.54,'[0.5, 1.29]²',11,ha='center')
axes[0].add_patch(FancyArrowPatch((0,-.14),(1,-.14),arrowstyle='<->',mutation_scale=12,color=INK))
label(axes[0],.5,-.32,'1cm',12,ha='center')
label(axes[0],.94,1.91,'P=(0.5, 0.5)이면',13,ha='center')
label(axes[0],.94,1.71,'첫 작은 상자는 두 벽에 붙는다',11,ha='center')
axes[1].add_patch(Circle(Q,.5,facecolor=ORANGE,edgecolor=ORANGE,alpha=.45,lw=2))
axes[1].scatter([Q[0]],[Q[1]],s=35,color=INK,zorder=6)
label(axes[1],Q[0]+.15,Q[1]+.07,'Q',13,ha='center')
axes[1].plot([closest[0],Q[0]],[closest[1],Q[1]],color=RED,lw=2)
axes[1].scatter([1],[1],s=35,color=RED,zorder=7)
label(axes[1],.94,1.91,'가장 먼 모서리의 Q조차',13,ha='center')
label(axes[1],.94,1.71,'원이 첫 작은 상자를 파고든다',11,ha='center')
label(axes[1],1.38,.72,'≈ 0.41012cm < 0.5cm',10,ha='center',color=RED)
fig.text(.5,.13,'Q에서 첫 작은 상자까지의 거리는 최대 √(0.29²+0.29²) ≈ 0.41012cm\n'
         'Every possible Q in R1 lies less than 0.5 cm from the first small box.',
         fontproperties=F,fontsize=11.5,color=INK,ha='center')
fig.text(.5,.035,'P가 모서리에 붙은 특수 경우의 완전한 증명이다. R1 전체에 대한 일반 증명은 벽 조건을 함께 계산한다.\n'
         'This proves the corner-fixed case. The general wall-cell proof also checks both centres and all rotations.',
         fontproperties=F,fontsize=10.5,color=MUTED,ha='center')
fig.subplots_adjust(top=.78,bottom=.25,left=.035,right=.985,wspace=.13)
plt.show()

 

첫 중심 P가 두 벽에서 각각 0.5cm 떨어진 특수 경우. 다른 중심 Q를 R1에서 가장 먼 모서리에 놓아도 내접원이 첫 작은 상자 안으로 파고든다.

먼저 한 중심 P가 두 벽에서 각각 0.5cm 떨어진 자리에 있다고 해 봅시다. 이 작은 상자는 두 벽에 반듯하게 붙어야 합니다. 같은 중심에서 조금 기울이면 세로 높이가 1cm보다 커져 아래 꼭짓점이 벽을 넘기 때문입니다.

다른 중심 Q도 R1 안에 넣으려 합니다. 첫 작은 상자로부터 가로로 떨어질 수 있는 거리는 최대 0.29cm, 세로도 최대 0.29cm입니다. 둘 다 최대인 가장 먼 모서리로 가도, 첫 작은 상자까지의 거리는 약 0.41012cm입니다.

두 번째 작은 상자 안에는 반지름 0.5cm인 원이 있습니다. 0.41012cm는 그보다 짧습니다. 그래서 그림처럼 원이 첫 작은 상자 안으로 파고듭니다. Q를 어디에 놓거나 두 번째 작은 상자를 어떻게 돌려도 겹침을 피할 수 없습니다.

이제 P도 더 안쪽으로 옮기면 어떨까요? 그러면 첫 작은 상자도 기울일 수 있으므로 추가 계산이 필요합니다. 다만 아래쪽 작은 상자를 기울일수록 바닥을 피하기 위해 중심을 올려야 합니다. 그만큼 위쪽 작은 상자와 벌어질 높이가 줄어듭니다. 옆으로 피할 수 있는 폭도 영역의 폭 안으로 제한됩니다.

원문의 벽 증명은 이 가로 폭, 남은 높이, 각 작은 상자의 기울기를 함께 계산합니다. 방금 그림은 P가 모서리에 붙은 경우를 끝까지 보인 것이고, 일반 정리는 P와 Q가 영역 안 어디에 있든 처리합니다. 대칭을 이용해 세 종류의 크기를 확인하면 열여섯 벽 영역 모두에서 중심 두 개가 불가능해집니다. 벽 조건을 묶는 기하학, 실제 열여섯 영역에 적용하는 증명

모서리 영역 전체를 막는 부등식은 어떻게 확인할까요?

원문의 기하학 보조정리는 두 작은 상자가 떨어져 있을 수 있으려면, 어떤 $a,b\ge0$, $a^2+b^2=1$에 대해 다음 둘 중 하나는 성립해야 한다고 보입니다. $a,b$는 두 작은 상자 사이를 가르는 직선의 수직 방향을 나타냅니다. 이 방향까지 자유롭게 두어 모든 기울기를 포함합니다.

$$ 1\le0.79a+1.29b-\frac{ab+b^2}{2}\tag{1} $$

또는

$$ 0.5\le0.29(a+b).\tag{2} $$

(2)는 $a+b\le\sqrt2$여서 오른쪽이 최대 약 0.41012입니다. 0.5에 도달할 수 없습니다.

(1)도 불가능합니다. $R=(0.79-b/2)a$, $T=1-1.29b+b^2/2$라 두면 (1)은 $T\le R$입니다. 그런데 $R\ge0$이고,

$$ T-0.21=(1-b)(0.79-b/2)\ge0 $$

이므로 $T>0$입니다. 한편 정확히 전개하면

$$ T^2-R^2= \frac12\left(b^2-\frac{52}{25}b+\frac{179}{208}\right)^2 +\frac3{208}b^2+\frac{303047}{54080000}>0. $$

제곱한 수들은 0 이상이고 마지막 상수는 양수입니다. 따라서 $T>R$이고 (1)도 모순입니다. 이것으로 두 필요조건이 모두 막힙니다.

이 제곱합 표현은 원문의 다항식을 따로 전개해 정리한 것입니다. 원문은 같은 양성을 다른 정확한 계수 검사로 확인합니다. 두 필요조건을 얻는 일반 기하학은 위 원문에, 나머지 두 크기의 벽 영역 계산은 조건 배제 증명에 있습니다.

지금의 계산을 요약하면 다음과 같습니다.

  • 가정: 큰 상자의 벽을 넘지 않는 작은 상자 두 개의 중심을 R1 안에 두고, 두 작은 상자가 겹치지 않게 배치할 수 있다.
  • 두 작은 상자를 분리하는 방향 $(a,b)$가 존재해야 한다.
  • 주어진 기하학적 필요조건에 의해 (1) 또는 (2)가 성립해야 한다.
  • (1)은 $T\le R$을 요구하지만, 위 계산에서 $R\ge0$, $T>0$, $T^2-R^2>0$이므로 $R<T$이다. 따라서 (1)은 불가능하다.
  • (2)는 오른쪽이 최대 $0.29\sqrt2<0.5$이므로 불가능하다.
  • 두 필요조건이 모두 막히므로 처음 가정은 모순이다. R1에는 겹치지 않는 작은 상자 두 개의 중심을 함께 둘 수 없다.

이것이 모서리 영역 R1의 중심 개수를 제한하는 논리입니다.




10. 무한히 많은 배치를 ‘어느 17영역을 쓰는가’로 묶습니다

이제 지도가 쓸모를 갖습니다. 모든 중심은 24개 영역 중 어딘가에 들어가고, 같은 영역에 두 중심을 둘 수 없습니다. 그러므로 17개 중심에 서로 다른 17영역을 배정할 수 있습니다.

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
ASSIGNED_SOURCE = [0, 4, 6, 2, 1, 12, 7, 3, 14, 5, 18, 20, 8, 21, 9, 13, 11]
UNASSIGNED_SOURCE = [10, 15, 16, 17, 19, 22, 23]
fig, axes = plt.subplots(1, 2, figsize=(14.6, 8.9), facecolor=WHITE)
title(fig, '실제 중심 17개가 서로 다른 17개 영역에 배정된다',
      'The 17 witness centres select 17 distinct actual cover regions', 17)
U = COVER['cover_frame']['side_float']
delta = (U-D['side'])/2
shifted = dict(D)
shifted['squares'] = [dict(q, x=q['x']+delta, y=q['y']+delta) for q in D['squares']]
left, right = axes
for ax in axes:
    ax.set_xlim(-0.10, 4.80); ax.set_ylim(-0.10, 5.22)
    ax.set_aspect('equal'); ax.axis('off')
for cell in COVER['cells']:
    left.add_patch(Polygon(cell['vertices_float'], facecolor=WHITE, edgecolor=MUTED,
                           lw=0.65, alpha=0.32, zorder=0))
boxes(left, data=shifted, alpha=0.15, ids=False)
border(left, U, edgecolor=MUTED, linestyle='--', lw=0.9)
for q in shifted['squares']:
    left.scatter([q['x']], [q['y']], s=150, color=WHITE, edgecolor=BLUE, lw=1.1, zorder=5)
    label(left, q['x'], q['y'], str(q['id']), 9, ha='center', va='center', zorder=6)
label(left, U/2, 4.99, '중심 번호: 작은 상자 1–17', 13, ha='center')
label(left, U/2, 4.76, 'Actual small-box centre IDs: 1–17', 11, ha='center', color=MUTED)

for cell in COVER['cells']:
    poly = np.array(cell['vertices_float'])
    selected = cell['source_index'] in ASSIGNED_SOURCE
    fill = '#dcece3' if selected else '#f0cda4'
    right.add_patch(Polygon(poly, facecolor=fill, edgecolor=INK, lw=1.0, alpha=0.90))
    centre = poly.mean(axis=0)
    label(right, centre[0], centre[1], 'R'+str(cell['reader_label']), 10,
          ha='center', va='center', bbox=dict(facecolor=WHITE, alpha=0.75,
                                            edgecolor='none', pad=0.8))
label(right, U/2, 4.99, '초록 17영역 · 주황 7영역', 13, ha='center')
label(right, U/2, 4.76, '17 selected regions; 7 unassigned', 11, ha='center', color=MUTED)
fig.text(0.5, 0.18, '좌표틀 옮김 δ = (4.676 − L)/2 ≈ 0.0002349532cm\n'
         'Shift the witness centres by the same δ in both coordinates; no centre is snapped to a grid point.',
         fontproperties=F, fontsize=11, color=INK, ha='center')
remaining = ', '.join('R'+str(i+1) for i in UNASSIGNED_SOURCE)
fig.text(0.5, 0.11, '중심을 배정하지 않은 7영역: '+remaining+'\n'
         'Unassigned means no centre was assigned there; it does not mean the whole region is empty space.',
         fontproperties=F, fontsize=11, color=MUTED, ha='center')
fig.text(0.5, 0.035, '분류는 영역만 정한다. 각 후보의 정확한 좌표와 각도는 그 안에서 계속 따진다.\n'
         'The classification fixes region membership, while continuous centre positions and angles remain to be checked.',
         fontproperties=F, fontsize=10.6, color=MUTED, ha='center')
fig.subplots_adjust(top=0.79, bottom=0.27, left=0.035, right=0.985, wspace=0.10)
plt.show()

 

비드웰 배치의 실제 중심 17개를 서로 다른 17영역에 배정했다. 주황색 일곱 영역은 중심을 배정하지 않은 영역이다.

왼쪽은 실제 비드웰 배치의 중심들이고, 오른쪽은 각 중심에 배정한 영역입니다. 이 배치에서는 초록색 열일곱 영역을 쓰고 주황색 일곱 영역을 배정하지 않았습니다. 여기서 주황색은 작은 상자가 전혀 차지하지 않는 빈 공간이라는 뜻이 아닙니다. 중심의 배정을 표시한 것입니다.

다른 배치를 만들면 선택되는 영역들도 바뀔 수 있습니다. 하지만 고를 것은 스물네 영역 중 열일곱입니다. 달리 말하면 일곱 영역을 남겨 두는 선택입니다. 그 수는 346,104가지로 유한합니다. 회전하거나 거울에 비친 것처럼 같은 경우를 묶으면 43,593가지가 남습니다. 실제 배치에서 영역 선택으로 넘어가는 정리, 대칭에 따른 경우 계산

중심을 영역의 한가운데로 옮긴 것은 아닙니다. 중심의 정확한 위치와 상자의 각도는 여전히 바뀔 수 있습니다. 무한히 많은 그림을 유한한 묶음으로 나누었을 뿐, 묶음 안의 가능성을 버리지 않았습니다.




끝없이 움직이는 중심을 몇 가지로 묶는다는 말이 아직 막연할 수 있습니다. 아래에서 중심을 배정할 열일곱 영역을 직접 바꿔 보세요. 같은 선택을 돌리고 뒤집었을 때 어떤 것이 중복되는지도 확인할 수 있습니다.

끝없이 움직이는 상자를 어떻게 43,593묶음으로 셀까요?

주황색은 중심을 배정하지 않은 영역입니다. 지도나 R 버튼을 눌러 일곱 영역을 골라 보세요. 나머지 열일곱 영역에 중심을 하나씩 배정하는 경우를 나타냅니다.

 
 

 

 

 

‘대칭인 선택’을 누르면 여덟 그림 중 같은 선택이 반복됩니다. 따라서 전체 346,104가지를 단순히 8로 나누는 대신, 같은 선택끼리 묶어 43,593묶음을 셉니다. 이 실험은 중심이 쓸 영역만 분류합니다. 실제로 상자 17개가 들어가는지는 다음 절의 벽·겹침 조건으로 따로 확인해야 합니다.

 

 




11. 각 묶음에서 가능한 위치와 각도를 끝까지 좁힙니다

이제 ‘비드웰 배치의 길이보다 작은 큰 상자에 17개가 들어간다’고 가정합니다. 4.67553cm라는 짧은 소수는 반올림한 표시이고, 증명에서는 그 배치의 정확한 길이를 씁니다. 그 배치도 방금 만든 묶음 중 하나에 반드시 속합니다. 남은 일은 어느 묶음에서도 이 가정을 만족시킬 수 없음을 보이는 것입니다.

각 묶음에는 벽을 넘지 않는 조건과 서로 겹치지 않는 조건을 모두 적습니다. 어떤 상자의 자리를 옮기려면 다른 상자와 떨어져 있어야 하고, 돌리려면 그 각도에서도 벽 안에 있어야 합니다. 이 조건들을 함께 적용하여 중심이 갈 수 있는 범위와 각도 범위를 줄입니다.

계산에서 중요한 차이가 있습니다. 예를 들어 각도가 어떤 범위 안에 있다면 그 안의 몇 각도만 시험하지 않습니다. 그 범위 전체에서 조건이 성립하는지 부등식으로 확인합니다. 앞에서 두 중심 사이의 모든 거리를 한 번에 제한했듯이, 한 번의 계산이 점 하나가 아니라 가능한 위치·각도 전체를 다룹니다.

범위를 더 나눠야 하는 경우에는 나뉜 부분들이 원래 범위를 전부 덮는지 함께 확인합니다. 어느 쪽에도 속하지 않는 틈을 남기면 안 됩니다.

실제 원문은 먼저 일부 영역의 조합만으로 불가능이 판정되는 경우들을 한꺼번에 제외합니다. 남은 목록은 일반적인 경우들, 별도 계산이 필요한 네 가지 조합, 비드웰 배치와 같은 영역을 쓰는 경우로 나눕니다. 앞의 두 종류에는 각각의 배제 계산을 적용합니다. 마지막 종류는 비드웰 배치 가까이로 들어감을 증명한 뒤, 그 근처를 다루는 정리로 보냅니다. 이 분류와 연결도 증명의 일부입니다. 실제 경우 분류를 연결하는 원문

그 근처를 다루는 정리는 작은 상자들의 위치와 각도를 바꾸더라도 큰 상자 한 변을 그 값 아래로 줄일 수 없음을 보입니다. 일부 상자가 움직일 여유까지 없어야 한다는 뜻은 아닙니다.

이 계산은 길어서 공개 증명에는 많은 경우표와 검산 자료가 들어 있습니다. Lean은 경우가 빠지지 않았다는 연결과, 각 경우를 막는 계산을 함께 확인합니다. ‘많이 놓아 보니 더 좋은 것이 없었다’가 최소성의 근거가 되는 것은 아닙니다. 경우 배제와 마지막 근방을 잇는 증명, 근방에서 더 줄일 수 없다는 증명

이 글에서 거리·중점·덮개·모서리의 계산은 직접 풀어 보았습니다. 전체 경우표의 계산은 연결한 원문에 있습니다. 공개된 최종 정리는 그 계산들을 모아 비드웰 배치의 정확한 한 변 이상일 때에만 17개를 담을 수 있다고 결론짓습니다. 최종 최적성 정리




12. 빈틈이 남아도, 그보다 작은 상자에 담는 방법은 없습니다

그림을 만든 Python 코드
import json, math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle, Circle, FancyArrowPatch
from matplotlib.font_manager import FontProperties
F=FontProperties(family=['Malgun Gothic','DejaVu Sans'])
INK='#24384b';BLUE='#5e8bb6';ORANGE='#dca66b';TEAL='#7eaaa4';RED='#c64f5b';GREEN='#deeee2';WHITE='#ffffff';MUTED='#607386'
D=json.loads(r'''{"side":4.675530093604551,"squares":[{"id":1,"x":0.5,"y":0.5,"angle":0.0,"group":0},{"id":2,"x":1.5,"y":0.5,"angle":0.0,"group":0},{"id":3,"x":0.5,"y":1.5,"angle":0.0,"group":0},{"id":4,"x":0.5,"y":4.175530093604551,"angle":0.0,"group":0},{"id":5,"x":4.175530093604551,"y":0.5,"angle":0.0,"group":0},{"id":6,"x":3.1384500780148925,"y":0.5,"angle":0.0,"group":0},{"id":7,"x":4.175530093604551,"y":1.5,"angle":0.0,"group":0},{"id":8,"x":4.175530093604551,"y":4.175530093604551,"angle":0.0,"group":0},{"id":9,"x":0.7042021556575175,"y":2.7042021556575175,"angle":0.694727592818231,"group":1},{"id":10,"x":1.4360434939982247,"y":3.3880434332633627,"angle":0.694727592818231,"group":1},{"id":11,"x":1.556737163170879,"y":2.112935871840352,"angle":0.694727592818231,"group":1},{"id":12,"x":2.2885785015115863,"y":2.7967771494461973,"angle":0.694727592818231,"group":1},{"id":13,"x":2.310482784237222,"y":1.40844754586514,"angle":0.694727592818231,"group":1},{"id":14,"x":3.027127784522231,"y":2.110524825272585,"angle":0.694727592818231,"group":1},{"id":15,"x":2.347324826510285,"y":4.175530093604551,"angle":0.0,"group":0},{"id":16,"x":3.245073712656966,"y":3.372493861493854,"angle":-0.6392056569382095,"group":2},{"id":17,"x":4.175530093604551,"y":2.6134601325125635,"angle":0.0,"group":0}],"source":"https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean"}''')
COVER=json.loads(r'''{"schema":"sq17-actual-closed-cover-v1","source_url":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCoverData.lean","additional_source_urls":{"cover_proof":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactCellCover.lean","interior_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/InteriorCellCapacity.lean","symmetry":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/CellSymmetry.lean","wall_capacity":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/ExactWallCapacity.lean","wall_obstructions":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityObstructions.lean","wall_polynomials":"https://raw.githubusercontent.com/ofitzharding/sq17-lean/main/Sq17Lean/WallCapacityBounds.lean"},"unit":"cm; every small box has side 1 cm","geometry_kind":"closed convex polygon cover, not a disjoint partition","boundary_overlap_qualifier":"A centre may belong to several cells at a common boundary or overlap. Select one containing cell. Capacity one proves that two disjoint small boxes cannot be assigned the same cell.","numbering":{"source_index_base":0,"reader_label_base":1,"formula":"reader_label = source_index + 1","warning":"These are centre-region labels, not the 17 small-box labels."},"cover_frame":{"side_exact":"1169/250","side_float":4.676,"note":"Rational cover frame U is slightly larger than the exact optimum; it is not the optimal side."},"centre_box":{"x_interval_exact":["1/2","522/125"],"y_interval_exact":["1/2","522/125"],"x_interval_float":[0.5,4.176],"y_interval_float":[0.5,4.176]},"coverage_certificate":{"vertical_slab_count":22,"closed_trapezoid_count":114,"x_breaks_exact":["1/2","129/100","1411/1000","143/100","3707059/1922600","2993/1500","91867/46000","999/500","22981/11500","1048947/519200","39044849/19226000","1169/500","50855927/19226000","6894161/2596000","30793/11500","1339/500","123229/46000","4021/1500","26415093/9613000","1623/500","653/200","1693/500","522/125"],"x_breaks_float":[0.5,1.29,1.411,1.43,1.9281488609175075,1.9953333333333334,1.997108695652174,1.998,1.9983478260869565,2.0203139445300464,2.0308357952772287,2.338,2.645164204722771,2.6556860554699537,2.6776521739130437,2.678,2.678891304347826,2.6806666666666668,2.7478511390824925,3.246,3.265,3.386,4.176],"note":"Only the first slab is spelled out here. Full coverage uses all_slab_checks and centre_box_covered from the cited source, not area sums or sampled pixels."},"cells":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","class":"wall","vertices_exact":[["1/2","1/2"],["129/100","1/2"],["129/100","129/100"],["1/2","129/100"]],"vertices_float":[[0.5,0.5],[1.29,0.5],[1.29,1.29],[0.5,1.29]]},{"source_index":1,"reader_label":2,"source_name":"corner-SE","class":"wall","vertices_exact":[["1693/500","1/2"],["522/125","1/2"],["522/125","129/100"],["1693/500","129/100"]],"vertices_float":[[3.386,0.5],[4.176,0.5],[4.176,1.29],[3.386,1.29]]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","class":"wall","vertices_exact":[["1/2","1693/500"],["129/100","1693/500"],["129/100","522/125"],["1/2","522/125"]],"vertices_float":[[0.5,3.386],[1.29,3.386],[1.29,4.176],[0.5,4.176]]},{"source_index":3,"reader_label":4,"source_name":"corner-NE","class":"wall","vertices_exact":[["1693/500","1693/500"],["522/125","1693/500"],["522/125","522/125"],["1693/500","522/125"]],"vertices_float":[[3.386,3.386],[4.176,3.386],[4.176,4.176],[3.386,4.176]]},{"source_index":4,"reader_label":5,"source_name":"side-S0","class":"wall","vertices_exact":[["129/100","1/2"],["2993/1500","1/2"],["2993/1500","1411/1000"],["129/100","1411/1000"]],"vertices_float":[[1.29,0.5],[1.9953333333333334,0.5],[1.9953333333333334,1.411],[1.29,1.411]]},{"source_index":5,"reader_label":6,"source_name":"side-N0","class":"wall","vertices_exact":[["129/100","653/200"],["2993/1500","653/200"],["2993/1500","522/125"],["129/100","522/125"]],"vertices_float":[[1.29,3.265],[1.9953333333333334,3.265],[1.9953333333333334,4.176],[1.29,4.176]]},{"source_index":6,"reader_label":7,"source_name":"side-W0","class":"wall","vertices_exact":[["1/2","129/100"],["1411/1000","129/100"],["1411/1000","2993/1500"],["1/2","2993/1500"]],"vertices_float":[[0.5,1.29],[1.411,1.29],[1.411,1.9953333333333334],[0.5,1.9953333333333334]]},{"source_index":7,"reader_label":8,"source_name":"side-E0","class":"wall","vertices_exact":[["653/200","129/100"],["522/125","129/100"],["522/125","2993/1500"],["653/200","2993/1500"]],"vertices_float":[[3.265,1.29],[4.176,1.29],[4.176,1.9953333333333334],[3.265,1.9953333333333334]]},{"source_index":8,"reader_label":9,"source_name":"side-S1","class":"wall","vertices_exact":[["2993/1500","1/2"],["4021/1500","1/2"],["4021/1500","143/100"],["2993/1500","143/100"]],"vertices_float":[[1.9953333333333334,0.5],[2.6806666666666668,0.5],[2.6806666666666668,1.43],[1.9953333333333334,1.43]]},{"source_index":9,"reader_label":10,"source_name":"side-N1","class":"wall","vertices_exact":[["2993/1500","1623/500"],["4021/1500","1623/500"],["4021/1500","522/125"],["2993/1500","522/125"]],"vertices_float":[[1.9953333333333334,3.246],[2.6806666666666668,3.246],[2.6806666666666668,4.176],[1.9953333333333334,4.176]]},{"source_index":10,"reader_label":11,"source_name":"side-W1","class":"wall","vertices_exact":[["1/2","2993/1500"],["143/100","2993/1500"],["143/100","4021/1500"],["1/2","4021/1500"]],"vertices_float":[[0.5,1.9953333333333334],[1.43,1.9953333333333334],[1.43,2.6806666666666668],[0.5,2.6806666666666668]]},{"source_index":11,"reader_label":12,"source_name":"side-E1","class":"wall","vertices_exact":[["1623/500","2993/1500"],["522/125","2993/1500"],["522/125","4021/1500"],["1623/500","4021/1500"]],"vertices_float":[[3.246,1.9953333333333334],[4.176,1.9953333333333334],[4.176,2.6806666666666668],[3.246,2.6806666666666668]]},{"source_index":12,"reader_label":13,"source_name":"side-S2","class":"wall","vertices_exact":[["4021/1500","1/2"],["1693/500","1/2"],["1693/500","1411/1000"],["4021/1500","1411/1000"]],"vertices_float":[[2.6806666666666668,0.5],[3.386,0.5],[3.386,1.411],[2.6806666666666668,1.411]]},{"source_index":13,"reader_label":14,"source_name":"side-N2","class":"wall","vertices_exact":[["4021/1500","653/200"],["1693/500","653/200"],["1693/500","522/125"],["4021/1500","522/125"]],"vertices_float":[[2.6806666666666668,3.265],[3.386,3.265],[3.386,4.176],[2.6806666666666668,4.176]]},{"source_index":14,"reader_label":15,"source_name":"side-W2","class":"wall","vertices_exact":[["1/2","4021/1500"],["1411/1000","4021/1500"],["1411/1000","1693/500"],["1/2","1693/500"]],"vertices_float":[[0.5,2.6806666666666668],[1.411,2.6806666666666668],[1.411,3.386],[0.5,3.386]]},{"source_index":15,"reader_label":16,"source_name":"side-E2","class":"wall","vertices_exact":[["653/200","4021/1500"],["522/125","4021/1500"],["522/125","1693/500"],["653/200","1693/500"]],"vertices_float":[[3.265,2.6806666666666668],[4.176,2.6806666666666668],[4.176,3.386],[3.265,3.386]]},{"source_index":16,"reader_label":17,"source_name":"interior-SW","class":"interior","vertices_exact":[["1411/1000","1411/1000"],["91867/46000","1411/1000"],["39044849/19226000","3707059/1922600"],["1048947/519200","1048947/519200"],["3707059/1922600","39044849/19226000"],["1411/1000","91867/46000"]],"vertices_float":[[1.411,1.411],[1.997108695652174,1.411],[2.0308357952772287,1.9281488609175075],[2.0203139445300464,2.0203139445300464],[1.9281488609175075,2.0308357952772287],[1.411,1.997108695652174]]},{"source_index":17,"reader_label":18,"source_name":"interior-NW","class":"interior","vertices_exact":[["1411/1000","123229/46000"],["3707059/1922600","50855927/19226000"],["1048947/519200","6894161/2596000"],["39044849/19226000","26415093/9613000"],["91867/46000","653/200"],["1411/1000","653/200"]],"vertices_float":[[1.411,2.678891304347826],[1.9281488609175075,2.645164204722771],[2.0203139445300464,2.6556860554699537],[2.0308357952772287,2.7478511390824925],[1.997108695652174,3.265],[1.411,3.265]]},{"source_index":18,"reader_label":19,"source_name":"interior-W","class":"interior","vertices_exact":[["143/100","999/500"],["1048947/519200","1048947/519200"],["1169/500","1169/500"],["1048947/519200","6894161/2596000"],["143/100","1339/500"]],"vertices_float":[[1.43,1.998],[2.0203139445300464,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.6556860554699537],[1.43,2.678]]},{"source_index":19,"reader_label":20,"source_name":"interior-S","class":"interior","vertices_exact":[["999/500","143/100"],["1339/500","143/100"],["6894161/2596000","1048947/519200"],["1169/500","1169/500"],["1048947/519200","1048947/519200"]],"vertices_float":[[1.998,1.43],[2.678,1.43],[2.6556860554699537,2.0203139445300464],[2.338,2.338],[2.0203139445300464,2.0203139445300464]]},{"source_index":20,"reader_label":21,"source_name":"interior-N","class":"interior","vertices_exact":[["999/500","1623/500"],["1048947/519200","6894161/2596000"],["1169/500","1169/500"],["6894161/2596000","6894161/2596000"],["1339/500","1623/500"]],"vertices_float":[[1.998,3.246],[2.0203139445300464,2.6556860554699537],[2.338,2.338],[2.6556860554699537,2.6556860554699537],[2.678,3.246]]},{"source_index":21,"reader_label":22,"source_name":"interior-E","class":"interior","vertices_exact":[["1169/500","1169/500"],["6894161/2596000","1048947/519200"],["1623/500","999/500"],["1623/500","1339/500"],["6894161/2596000","6894161/2596000"]],"vertices_float":[[2.338,2.338],[2.6556860554699537,2.0203139445300464],[3.246,1.998],[3.246,2.678],[2.6556860554699537,2.6556860554699537]]},{"source_index":22,"reader_label":23,"source_name":"interior-SE","class":"interior","vertices_exact":[["50855927/19226000","3707059/1922600"],["123229/46000","1411/1000"],["653/200","1411/1000"],["653/200","91867/46000"],["26415093/9613000","39044849/19226000"],["6894161/2596000","1048947/519200"]],"vertices_float":[[2.645164204722771,1.9281488609175075],[2.678891304347826,1.411],[3.265,1.411],[3.265,1.997108695652174],[2.7478511390824925,2.0308357952772287],[2.6556860554699537,2.0203139445300464]]},{"source_index":23,"reader_label":24,"source_name":"interior-NE","class":"interior","vertices_exact":[["50855927/19226000","26415093/9613000"],["6894161/2596000","6894161/2596000"],["26415093/9613000","50855927/19226000"],["653/200","123229/46000"],["653/200","653/200"],["123229/46000","653/200"]],"vertices_float":[[2.645164204722771,2.7478511390824925],[2.6556860554699537,2.6556860554699537],[2.7478511390824925,2.645164204722771],[3.265,2.678891304347826],[3.265,3.265],[2.678891304347826,3.265]]}],"first_strip":{"source_slab_index":0,"x_interval_exact":["1/2","129/100"],"x_interval_float":[0.5,1.29],"top_to_bottom_reader_labels":[3,15,11,7,1],"bottom_to_top":[{"source_index":0,"reader_label":1,"source_name":"corner-SW","y_interval_exact":["1/2","129/100"],"y_interval_float":[0.5,1.29]},{"source_index":6,"reader_label":7,"source_name":"side-W0","y_interval_exact":["129/100","2993/1500"],"y_interval_float":[1.29,1.9953333333333334]},{"source_index":10,"reader_label":11,"source_name":"side-W1","y_interval_exact":["2993/1500","4021/1500"],"y_interval_float":[1.9953333333333334,2.6806666666666668]},{"source_index":14,"reader_label":15,"source_name":"side-W2","y_interval_exact":["4021/1500","1693/500"],"y_interval_float":[2.6806666666666668,3.386]},{"source_index":2,"reader_label":3,"source_name":"corner-NW","y_interval_exact":["1693/500","522/125"],"y_interval_float":[3.386,4.176]}],"continuity":"Every upper y endpoint equals the next lower y endpoint. All intervals are closed."},"interior_source18":{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]],"source_name":"interior-W","diameter_bound_float":0.9695689764013699,"difference_vector_exact":["227/250","17/50"],"difference_vector_float":[0.908,0.34],"proof":"Every pair in this closed polygon is at distance squared at most 29377/31250<1. Two radius-1/2 inscribed open disks then share the midpoint, forcing overlap of the small-box interiors.","example_two_centres":{"P_exact":["3/2","52/25"],"Q_exact":["9/4","233/100"],"M_midpoint_exact":["15/8","441/200"],"P_float":[1.5,2.08],"Q_float":[2.25,2.33],"M_midpoint_float":[1.875,2.205],"PQ_squared_exact":"5/8","PM_squared_exact":"5/32","suggested_box_angles_radians":[0,0.5235987755982988],"note":"A hypothetical pair in actual region 19 demonstrates forced overlap, not a valid 17-box packing. The proof applies to all angles, not only these drawing angles.","exact_membership_and_midpoint_checks":true}},"interior_all_eight_vertex_checks":[{"source_index":16,"reader_label":17,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[0,3]]},{"source_index":17,"reader_label":18,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":18,"reader_label":19,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":19,"reader_label":20,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,3],[1,3]]},{"source_index":20,"reader_label":21,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[2,4]]},{"source_index":21,"reader_label":22,"max_distance_squared_exact":"29377/31250","max_distance_squared_float":0.940064,"farthest_vertex_pairs_zero_based":[[0,2],[0,3]]},{"source_index":22,"reader_label":23,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[2,5]]},{"source_index":23,"reader_label":24,"max_distance_squared_exact":"2502024804841/3369608000000","max_distance_squared_float":0.7425269659975285,"farthest_vertex_pairs_zero_based":[[1,4]]}],"symmetries":[{"source_index":0,"name":"identity","formula":"(x,y)","permutation_source_indices":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"permutation_reader_labels":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"fixed_seven_cell_subsets":346104},{"source_index":1,"name":"reflection-x","formula":"(U-x,y)","permutation_source_indices":[1,0,3,2,12,13,7,6,8,9,11,10,4,5,15,14,22,23,21,19,20,18,16,17],"permutation_reader_labels":[2,1,4,3,13,14,8,7,9,10,12,11,5,6,16,15,23,24,22,20,21,19,17,18],"fixed_seven_cell_subsets":660},{"source_index":2,"name":"reflection-y","formula":"(x,U-y)","permutation_source_indices":[2,3,0,1,5,4,14,15,9,8,10,11,13,12,6,7,17,16,18,20,19,21,23,22],"permutation_reader_labels":[3,4,1,2,6,5,15,16,10,9,11,12,14,13,7,8,18,17,19,21,20,22,24,23],"fixed_seven_cell_subsets":660},{"source_index":3,"name":"half-turn","formula":"(U-x,U-y)","permutation_source_indices":[3,2,1,0,13,12,15,14,9,8,11,10,5,4,7,6,23,22,21,20,19,18,17,16],"permutation_reader_labels":[4,3,2,1,14,13,16,15,10,9,12,11,6,5,8,7,24,23,22,21,20,19,18,17],"fixed_seven_cell_subsets":0},{"source_index":4,"name":"diagonal-reflection","formula":"(y,x)","permutation_source_indices":[0,2,1,3,6,7,4,5,10,11,8,9,14,15,12,13,16,22,19,18,21,20,17,23],"permutation_reader_labels":[1,3,2,4,7,8,5,6,11,12,9,10,15,16,13,14,17,23,20,19,22,21,18,24],"fixed_seven_cell_subsets":660},{"source_index":5,"name":"quarter-turn","formula":"(U-y,x)","permutation_source_indices":[1,3,0,2,7,6,12,13,11,10,8,9,15,14,4,5,22,16,19,21,18,20,23,17],"permutation_reader_labels":[2,4,1,3,8,7,13,14,12,11,9,10,16,15,5,6,23,17,20,22,19,21,24,18],"fixed_seven_cell_subsets":0},{"source_index":6,"name":"inverse-quarter-turn","formula":"(y,U-x)","permutation_source_indices":[2,0,3,1,14,15,5,4,10,11,9,8,6,7,13,12,17,23,20,18,21,19,16,22],"permutation_reader_labels":[3,1,4,2,15,16,6,5,11,12,10,9,7,8,14,13,18,24,21,19,22,20,17,23],"fixed_seven_cell_subsets":0},{"source_index":7,"name":"other-diagonal-reflection","formula":"(U-y,U-x)","permutation_source_indices":[3,1,2,0,15,14,13,12,11,10,9,8,7,6,5,4,23,17,20,21,18,19,22,16],"permutation_reader_labels":[4,2,3,1,16,15,14,13,12,11,10,9,8,7,6,5,24,18,21,22,19,20,23,17],"fixed_seven_cell_subsets":660}],"finite_count":{"unreduced_17_cell_subsets":346104,"orbit_count":43593,"burnside_fixed_counts":[346104,660,660,0,660,0,0,660],"scope":"Independent exact combinatorial check of these permutations; not a replay of census membership certificates or case exclusions."},"corner_wall_algebra":{"width_exact":"79/100","depth_exact":"79/100","parameter_domain":"a>=0,b>=0,a^2+b^2=1","necessary_alternatives":["1 <= (79/100)*a+(129/100)*b-(a*b+b^2)/2","1/2 <= (29/100)*a+(29/100)*b"],"Q_coefficients_constant_first":["3759/10000","-179/100","15191/5000","-52/25","1/2"],"SOS_square_outer_coefficient":"1/2","SOS_square_inside_coefficients_constant_first":["179/208","-52/25","1"],"SOS_extra_b_squared_coefficient":"3/208","SOS_positive_constant":"303047/54080000","SOS_origin":"Independent exact algebraic rewrite of source wallPoly. The Lean source uses positive Bernstein coefficients instead.","scope":"The scalar alternatives are impossible. Obtaining them from arbitrary rotated small boxes uses the geometric wall-capacity lemma; this algebra is not the entire geometric proof."},"local_verification":{"all_8_symmetries_map_all_24_vertex_sets_exactly":true,"all_8_interior_vertex_diameters_at_most_29377_over_31250":true,"first_strip_exact_coverage":true,"corner_quartic_SOS_identity_exact":true,"full_Lean_rebuild":false,"remaining_21_slab_certificates_replayed_locally":false}}''')

def label(ax,x,y,value,size=13,**kwargs):
    return ax.text(x,y,value,fontproperties=F,fontsize=size,color=kwargs.pop('color',INK),**kwargs)

def vertices(item):
    t=item['angle'];c=math.cos(t);s=math.sin(t)
    return np.array([[item['x']+(a*c-b*s)/2,item['y']+(a*s+b*c)/2] for a,b in [(-1,-1),(1,-1),(1,1),(-1,1)]])

def setup(ax,xmax=5.55,ymax=5.55):
    ax.set_xlim(-.35,xmax);ax.set_ylim(-.7,ymax);ax.set_aspect('equal');ax.axis('off')

def border(ax,side,**kwargs):
    ax.add_patch(Rectangle((0,0),side,side,fill=False,edgecolor=kwargs.pop('edgecolor',INK),lw=kwargs.pop('lw',2.3),**kwargs))

def boxes(ax,data=D,alpha=1,ids=True,color=None):
    for item in data['squares']:
        fill=color or [BLUE,ORANGE,TEAL][item['group']]
        ax.add_patch(Polygon(vertices(item),facecolor=fill,edgecolor=INK,lw=1.1,alpha=alpha))
        if ids:label(ax,item['x'],item['y'],str(item['id']),10,ha='center',va='center')

def dimension(ax,side,y=-.28):
    ax.add_patch(FancyArrowPatch((0,y),(side,y),arrowstyle='<->',mutation_scale=11,color=INK,lw=1.2))
    value=f'{side:g}' if side in (4,5) else f'≈ {side:.5f}'
    label(ax,side/2,y-.16,value+' cm',12,ha='center',va='top')

def title(fig,ko,en,size=19):
    fig.suptitle(ko+'\n'+en,fontproperties=F,fontsize=size,color=INK,y=.98)

SOURCE='https://github.com/ofitzharding/sq17-lean/blob/main/Sq17Lean/EndpointGeometry.lean'
def packing(theta_deg: float, branch: str = "upper") -> dict:
    """Return all 17 freshly computed poses, lengths in cm and angles in radians.

    ``upper`` selects the larger real b = tan(beta/2) root of F, continuous
    through the published packing. ``lower`` selects the smaller root.
    The root branch describes a contact solution; it does not promise that
    all boxes are disjoint. In particular 39.81 degrees causes boxes 12 and
    16 to overlap. If F has no real root, ValueError is raised rather than
    inventing a seventeenth box pose (for example at 45 degrees).
    """
    theta_deg = float(theta_deg)
    if not math.isfinite(theta_deg) or not 0 < theta_deg < 90:
        raise ValueError("기울기는 0도보다 크고 90도보다 작아야 합니다.")
    if branch not in ("upper", "lower"):
        raise ValueError("branch must be 'upper' or 'lower'")

    t = math.tan(math.radians(theta_deg) / 2)
    q2 = -3*t + t*t - t**3 - t**4
    q1 = 1 - t - t**4 + t**5
    q0 = t - 3*t*t - t**3 - t**4
    discriminant = q1*q1 - 4*q2*q0
    if discriminant < 0:
        raise ValueError("이 기울기에서는 작은 상자 16의 접촉 조건 F=0에 실수 해가 없습니다.")
    root_delta = math.sqrt(discriminant)
    roots = sorted(((-q1-root_delta)/(2*q2), (-q1+root_delta)/(2*q2)))
    b = roots[-1] if branch == "upper" else roots[0]
    if not 0 < b < 1:
        raise ValueError("선택한 해가 이 구성에서 사용하는 작은 상자 16의 각도 범위를 벗어납니다.")

    c, s = (1-t*t)/(1+t*t), 2*t/(1+t*t)
    d, e = (1-b*b)/(1+b*b), 2*b/(1+b*b)
    side = (6+4*t)/(1+2*t-t*t)
    half_width = (c+s)/2
    X = .5+(2+c*s+3*s-s*side)/c
    Y = 1.5+(2+3*c-c*side)/s
    A16 = d*(X+.5)-e*(side-1)+.5
    B16 = (d+e)*(side-1)-.5
    u10, v10 = 1.5+c*s+2*s, c*(side-1)-s-.5
    u11, v11 = c+2*s+.5, 2*c+(c*c-s*s)/2-1
    u13 = 2*c+s+.5
    slack = c*(c+4-side)

    def rotate(u: float, v: float) -> tuple[float, float]:
        return c*u-s*v, s*u+c*v

    centers = [
        (.5, .5), (1.5, .5), (.5, 1.5), (.5, side-.5),
        (side-.5, .5), (side-1.5-slack/(3*s), .5),
        (side-.5, 1.5), (side-.5, side-.5),
        (half_width, 2+half_width), rotate(u10, v10), rotate(u11, v11),
        rotate(u11+1, v10-1), rotate(u13, v11-1-slack/3),
        rotate(u13+1, v10-2), (X, side-.5),
        (d*A16+e*B16, -e*A16+d*B16), (side-.5, Y),
    ]
    directions = [(1, 0)]*8 + [(c, s)]*6 + [(1, 0), (d, -e), (1, 0)]
    boxes = [
        {
            "id": i+1, "label": f"작은 상자 {i+1}",
            "x": center[0], "y": center[1],
            "angle": math.atan2(direction[1], direction[0]),
            "group": 0 if i < 8 or i in (14, 16) else 2 if i == 15 else 1,
        }
        for i, (center, direction) in enumerate(zip(centers, directions))
    ]
    return {
        "side": side, "squares": boxes,
        "theta_deg": theta_deg, "beta_deg": math.degrees(2*math.atan(b)),
        "branch": branch, "t": t, "b": b,
        "root_discriminant": discriminant,
        "length_unit": "cm", "small_box_side": 1,
        "source": SOURCE,
        "contactpoints": {
            "A": [1, side-1],
            "B": [side-1, 2],
            "C": [X+.5, side-1],
            "D": [side-1, side-1],
            "E": [side-1, Y+.5],
            "P": [centers[15][0]-(d+e)/2, centers[15][1]+(e-d)/2],
        },
    }

construct=packing
endpointTheta=39.8049589797678
fig, ax = plt.subplots(figsize=(14.1, 5.9), facecolor=WHITE)
title(fig, '들어가는 배치와 더 작게는 불가능하다는 증명이 같은 경계를 정한다',
      'A construction and a global lower bound meet at the included endpoint', 17)
L = D['side']
ax.set_xlim(3.94, 5.16); ax.set_ylim(-1.18, 1.03); ax.axis('off')
ax.plot([4.00, L], [0, 0], color=RED, lw=13, solid_capstyle='butt')
ax.plot([L, 5.09], [0, 0], color='#4e9477', lw=13, solid_capstyle='butt')
ax.scatter([L], [0], s=205, facecolor='#4e9477', edgecolor=INK, lw=1.9, zorder=6)
for x, text in [(4, '4cm'), (5, '5cm')]:
    ax.plot([x, x], [-0.13, 0.13], color=INK, lw=1.0)
    label(ax, x, -0.22, text, 12, ha='center', va='top')
label(ax, 4.30, 0.59, '더 작은 큰 상자는 불가능', 14, ha='center', color=RED)
label(ax, 4.30, 0.34, 'No arrangement fits below the endpoint', 11, ha='center', color=MUTED)
label(ax, 4.94, 0.59, '이 배치가 들어간다', 14, ha='center', color='#33775d')
label(ax, 4.94, 0.34, 'The explicit 17-box witness fits', 11, ha='center', color=MUTED)
label(ax, L+0.07, -0.49, '최소 한 변 ≈4.67553cm', 15, ha='center', va='top')
label(ax, L+0.07, -0.74, 'Minimum side; the endpoint is included', 11, ha='center', va='top', color=MUTED)
label(ax, 4.28, -1.10, '근거: 더 작은 모든 배치를 배제한 증명', 11, ha='center', color=RED)
label(ax, 4.94, -1.10, '근거: 실제 배치의 좌표', 11, ha='center', color='#33775d')
fig.text(0.5, 0.055, '초록 점은 최소값 자체에서도 17개가 들어간다는 뜻이다.\n'
         'The green endpoint is feasible; displayed decimals approximate the exact algebraic boundary.',
         fontproperties=F, fontsize=11, color=MUTED, ha='center')
fig.subplots_adjust(top=0.74, bottom=0.18, left=0.045, right=0.985)
plt.show()

 

약 4.67553cm에는 실제 배치가 들어간다. 그보다 작은 길이의 모든 배치는 최적성 증명에서 배제된다.

처음에는 가장 반듯하고 대칭적인 배치가 답일 것 같았습니다. 실제로 가운데 하나만 비틀면 4.70711cm까지 줄일 수 있었습니다. 45도 배치를 더 바꾸면 4.69036cm, 각도 제한까지 풀면 비드웰의 4.67553cm에 도달합니다. 익숙한 규칙을 하나씩 바꿀 때마다 더 작은 길이가 나왔습니다.

하지만 답을 정하는 마지막 단계는 생김새가 아닙니다. 그 길이에 들어가는 배치가 있고, 그보다 작은 길이에는 어떤 배치도 들어가지 않는다는 두 사실이 만납니다. 앞의 그림은 첫 번째를 보여 주고, 모든 중심의 위치와 각도를 빠짐없이 다룬 증명은 두 번째를 보입니다.

약 4.86cm²의 빈틈은 실제로 남습니다. 다만 17개의 자리를 전부 바꿀 자유까지 주어도, 그 빈틈을 이용해 더 작은 큰 상자에 들어가는 배치를 만들 수는 없습니다. 대칭도 없고 빈틈도 많은 저 모양이 답인 이유입니다.

 

 

 

 

반응형

+ Recent posts