import%20marimo%0A%0A__generated_with%20%3D%20%220.18.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20return%20go%2C%20mo%2C%20np%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20def%20normalize(vec%3A%20np.ndarray)%20-%3E%20np.ndarray%3A%0A%20%20%20%20%20%20%20%20%23%20minimizes%20vec%20(or%20array)%20along%20last%20direction%0A%20%20%20%20%20%20%20%20return%20vec%20%2F%20np.linalg.norm(vec%2C%20axis%3D-1)%0A%0A%0A%20%20%20%20def%20compute_candidate_normals(tri_a%2C%20tri_b%2C%20eps%3D1e-6)%20-%3E%20list%5Bnp.ndarray%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Computes%20all%20%22candidate%20normal%20vectors%22%20of%20A%20-%20B%20for%20two%20triangles%20A%2CB.%0A%20%20%20%20%20%20%20%20These%20are%20guaranteed%20to%20be%20a%20superset%20of%20the%20actual%20normals%20(outside%20of%20numerical%20issues)%0A%20%20%20%20%20%20%20%20and%20further%20are%20guaranteed%20to%20have%20norm%201.%0A%0A%20%20%20%20%20%20%20%20For%20further%20details%20on%20the%20args%20also%20see%20%5Bminimal_translation%5D.%0A%0A%20%20%20%20%20%20%20%20%23%20Note%0A%20%20%20%20%20%20%20%20It's%20possible%20to%20statically%20bound%20the%20size%20of%20the%20returned%20list%20by%202%20%2B%209%20%3D%2011%20so%20this%20doesn't%20need%20dynamic%20allocation.%20And%20since%20we%20don't%20even%20need%20all%20candidates%20at%20once%20we%20could%20also%20use%20a%20generator.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%20computes%20a%20new%203%20by%203%20array%20where%20edges_a%5Bi%2C%20%3A%5D%20is%20the%20vector%20from%20tri_a%5Bi%2C%20%3A%5D%20to%20tri_a%5B(i%2B1)%20%25%203%2C%20%3A%5D%0A%20%20%20%20%20%20%20%20edges_a%20%3D%20np.roll(tri_a%2C%20-1%2C%20axis%3D0)%20-%20tri_a%0A%20%20%20%20%20%20%20%20%23%20same%20thing%20for%20second%20tri%0A%20%20%20%20%20%20%20%20edges_b%20%3D%20np.roll(tri_b%2C%20-1%2C%20axis%3D0)%20-%20tri_b%0A%20%20%20%20%20%20%20%20candidate_normals%20%3D%20%5B%5D%0A%0A%20%20%20%20%20%20%20%20normal_to_a%20%3D%20normalize(np.cross(edges_a%5B0%5D%2C%20edges_a%5B1%5D))%0A%20%20%20%20%20%20%20%20normal_to_b%20%3D%20normalize(np.cross(edges_b%5B0%5D%2C%20edges_b%5B1%5D))%0A%0A%20%20%20%20%20%20%20%20candidate_normals.append(normal_to_a)%0A%20%20%20%20%20%20%20%20candidate_normals.append(normal_to_b)%0A%0A%20%20%20%20%20%20%20%20for%20edge_a%20in%20edges_a%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20edge_b%20in%20edges_b%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n%20%3D%20np.cross(edge_a%2C%20edge_b)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(norm_n%20%3A%3D%20np.linalg.norm(n))%20%3E%20eps%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20candidate_normals.append(n%20%2F%20norm_n)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20the%20two%20edges%20are%20numerically%20(approximately)%20parallel.%20So%20we%20skip%20this%20normal%20as%20a%20candidate%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20%23%20TODO%3A%20could%20deduplicate%20here%20which%20*might*%20save%20some%20work%20(or%20not)%0A%20%20%20%20%20%20%20%20return%20candidate_normals%0A%0A%0A%20%20%20%20def%20determine_minimal_translation_vector(%0A%20%20%20%20%20%20%20%20tri_a%3A%20np.ndarray%2C%20tri_b%3A%20np.ndarray%0A%20%20%20%20)%20-%3E%20np.ndarray%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Args%0A%20%20%20%20%20%20%20%20*%20tri_a%3A%20some%20triangle%0A%20%20%20%20%20%20%20%20*%20tri_b%3A%20some%20other%20triangle%20intersecting%20tri_a%0A%0A%20%20%20%20%20%20%20%20%23%20Note%3A%0A%20%20%20%20%20%20%20%20The%20triangles%20are%20each%20given%20as%202-dim%20arrays%20of%20shape%20(3%2C3)%20with%20the%20first%20dimension%20ranging%20over%20the%20three%20vertices%20of%20the%20triangle%2C%20and%20the%20second%20over%20space.%0A%0A%20%20%20%20%20%20%20%20%23%20Returns%20a%20vector%20v%20of%20minimal%20norm%20such%20that%20-v%20is%20on%20the%20boundary%20of%20A-B%2C%20i.e.%20a%20minimal%20v%20such%20that%20(A%20%2B%20(1%2Beps)v)%20and%20B%20are%20disjoint%20for%20all%20eps%20%3E%200%2C%20but%20(A%2Bv)%20%2B%20B%20still%20intersect.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20candidates%20%3D%20compute_candidate_normals(tri_a%2C%20tri_b)%0A%0A%20%20%20%20%20%20%20%20%23%20fold%20over%20all%20candidates%2C%20keeping%20track%20of%20the%20the%20one%20that%20requires%20the%20least%20shift.%0A%20%20%20%20%20%20%20%20current_best_distance%20%3D%20np.inf%0A%20%20%20%20%20%20%20%20current_best_vec%20%3D%20None%0A%20%20%20%20%20%20%20%20for%20n%20in%20candidates%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20this%20computes%20the%20dot%20product%20of%20n%20with%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20each%20vertex%20of%20tri_a%2C%20storing%20the%20result%20in%20a%201-dim%20array%0A%20%20%20%20%20%20%20%20%20%20%20%20proj_a%20%3D%20np.dot(tri_a%2C%20n)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20same%20thing%20for%20b%0A%20%20%20%20%20%20%20%20%20%20%20%20proj_b%20%3D%20np.dot(tri_b%2C%20n)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20supp_a_pos%20%3D%20np.amax(proj_a)%20%20%23%20h_A(n)%0A%20%20%20%20%20%20%20%20%20%20%20%20supp_a_neg%20%3D%20-np.amax(-proj_a)%20%20%23%20-h_A(-n)%0A%20%20%20%20%20%20%20%20%20%20%20%20supp_b_pos%20%3D%20np.amax(proj_b)%20%20%23%20h_B(n)%0A%20%20%20%20%20%20%20%20%20%20%20%20supp_b_neg%20%3D%20-np.amax(-proj_b)%20%20%23%20-h_B(-n)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20d_pos%20%3D%20np.abs(supp_a_pos%20%2B%20supp_b_neg)%0A%20%20%20%20%20%20%20%20%20%20%20%20d_neg%20%3D%20np.abs(supp_a_neg%20%2B%20supp_b_pos)%0A%20%20%20%20%20%20%20%20%20%20%20%20distance_for_n%20%3D%20min(d_pos%2C%20d_neg)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20distance_for_n%20%3C%20current_best_distance%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20update%20current%20best%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20current_best_distance%20%3D%20distance_for_n%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20the%20float%20comparison%20is%20fine%20here%20since%20we%20really%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20must%20have%20a%20bit-by-bit%20copy%20of%20either%20d_pos%20or%20d_neg.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20vec_for_n%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20distance_for_n%20*%20n%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20d_pos%20%3D%3D%20distance_for_n%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20-distance_for_n%20*%20n%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20current_best_vec%20%3D%20vec_for_n%0A%20%20%20%20%20%20%20%20return%20-current_best_vec%0A%20%20%20%20return%20(determine_minimal_translation_vector%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A%20small%20example%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20add_a_sub_b_to_fig%2C%0A%20%20%20%20add_tri_to_fig%2C%0A%20%20%20%20determine_minimal_translation_vector%2C%0A%20%20%20%20go%2C%0A%20%20%20%20np%2C%0A)%3A%0A%20%20%20%20%23%20define%20two%20tris%0A%20%20%20%20tri_a%20%3D%20np.array(%5B%5B0%2C%200%2C%200%5D%2C%20%5B2%2C%200%2C%200%5D%2C%20%5B0%2C%202%2C%200%5D%5D)%0A%20%20%20%20tri_b%20%3D%20np.array(%5B%5B0.5%2C%200.5%2C%20-1%5D%2C%20%5B1.5%2C%200.5%2C%201%5D%2C%20%5B0.5%2C%201.5%2C%200%5D%5D)%0A%0A%20%20%20%20fig%20%3D%20go.Figure()%0A%20%20%20%20add_a_sub_b_to_fig(fig%2C%20tri_a%2C%20tri_b%2C%20color%3D%22lightgreen%22)%0A%20%20%20%20add_tri_to_fig(fig%2C%20tri_a%2C%20%22Triangle%20A%22%2C%20%22blue%22)%0A%20%20%20%20add_tri_to_fig(fig%2C%20tri_b%2C%20%22Triangle%20B%22%2C%20%22orange%22)%0A%0A%20%20%20%20mtv_a%20%3D%20determine_minimal_translation_vector(tri_a%2C%20tri_b)%0A%20%20%20%20add_tri_to_fig(fig%2C%20tri_a%20%2B%20mtv_a%2C%20%22Triangle%20A%20translated%22%2C%20%22deepskyblue%22)%0A%20%20%20%20mtv_b%20%3D%20determine_minimal_translation_vector(tri_b%2C%20tri_a)%0A%20%20%20%20add_tri_to_fig(fig%2C%20tri_b%20%2B%20mtv_b%2C%20%22Triangle%20B%20translated%22%2C%20%22coral%22)%0A%20%20%20%20return%20mtv_a%2C%20mtv_b%2C%20tri_a%2C%20tri_b%0A%0A%0A%40app.cell%0Adef%20_(mtv_a%2C%20mtv_b)%3A%0A%20%20%20%20%23%20note%20that%20these%20are%20just%20negatives%20of%20one%20another%0A%20%20%20%20mtv_a%2C%20mtv_b%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Same%20example%20just%20a%20little%20extra%0A%0A%20%20%20%20You%20could%20also%20translate%20%60A%60%20by%20%60t%20*%20mtv_a%60%20and%20(simultaneously)%20%60B%60%20by%20%60(1-t)%20*%20mtv_b%60%20to%20%22resolve%22%20the%20collision.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20t_slider%20%3D%20mo.ui.slider(%0A%20%20%20%20%20%20%20%20start%3D0%2C%20stop%3D1%2C%20step%3D0.01%2C%20label%3D%22Distribution%20%24t%24%22%2C%20value%3D0.5%0A%20%20%20%20)%0A%20%20%20%20return%20(t_slider%2C)%0A%0A%0A%40app.cell%0Adef%20_(add_tri_to_fig%2C%20go%2C%20mo%2C%20mtv_a%2C%20mtv_b%2C%20t_slider%2C%20tri_a%2C%20tri_b)%3A%0A%20%20%20%20def%20plot_collision_resolution(t)%3A%0A%20%20%20%20%20%20%20%20fig%20%3D%20go.Figure()%0A%0A%20%20%20%20%20%20%20%20add_tri_to_fig(fig%2C%20tri_a%2C%20%22Start%20A%22%2C%20%22gray%22)%0A%20%20%20%20%20%20%20%20add_tri_to_fig(fig%2C%20tri_b%2C%20%22Start%20B%22%2C%20%22gray%22)%0A%0A%20%20%20%20%20%20%20%20curr_a%20%3D%20tri_a%20%2B%20t%20*%20mtv_a%0A%20%20%20%20%20%20%20%20curr_b%20%3D%20tri_b%20%2B%20(1%20-%20t)%20*%20mtv_b%0A%0A%20%20%20%20%20%20%20%20add_tri_to_fig(fig%2C%20curr_a%2C%20%22Resolved%20A%22%2C%20%22deepskyblue%22)%0A%20%20%20%20%20%20%20%20add_tri_to_fig(fig%2C%20curr_b%2C%20%22Resolved%20B%22%2C%20%22coral%22)%0A%0A%20%20%20%20%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3Df%22Collision%20Resolution%20(t%3D%7Bt%3A.2f%7D)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20scene%3Ddict(aspectmode%3D%22data%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20margin%3Ddict(l%3D0%2C%20r%3D0%2C%20b%3D0%2C%20t%3D40)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20fig%0A%0A%0A%20%20%20%20mo.vstack(%5Bt_slider%2C%20plot_collision_resolution(t_slider.value)%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Some%20plotting%20helper%20functions%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20scipy.spatial%20import%20ConvexHull%0A%20%20%20%20return%20(ConvexHull%2C)%0A%0A%0A%40app.cell%0Adef%20_(ConvexHull%2C%20go%2C%20np)%3A%0A%20%20%20%20def%20add_a_sub_b_to_fig(fig%2C%20tri_a%2C%20tri_b%2C%20color)%3A%0A%20%20%20%20%20%20%20%20%23%20compute%20A%20-%20B%20as%20the%20convex%20hull%0A%20%20%20%20%20%20%20%20%23%20of%20the%20differences%20of%20the%20vertices%20of%20A%20and%20B%0A%20%20%20%20%20%20%20%20minkowski_cloud%20%3D%20(tri_a%5B%3A%2C%20None%2C%20%3A%5D%20-%20tri_b%5BNone%2C%20%3A%2C%20%3A%5D).reshape(-1%2C%203)%0A%20%20%20%20%20%20%20%20hull%20%3D%20ConvexHull(minkowski_cloud)%0A%20%20%20%20%20%20%20%20x_hull%20%3D%20minkowski_cloud%5B%3A%2C%200%5D%0A%20%20%20%20%20%20%20%20y_hull%20%3D%20minkowski_cloud%5B%3A%2C%201%5D%0A%20%20%20%20%20%20%20%20z_hull%20%3D%20minkowski_cloud%5B%3A%2C%202%5D%0A%0A%20%20%20%20%20%20%20%20fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20go.Mesh3d(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3Dx_hull%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Dy_hull%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20z%3Dz_hull%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20i%3Dhull.simplices%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20j%3Dhull.simplices%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20k%3Dhull.simplices%5B%3A%2C%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Minkowski%20Difference%20A-B%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcolor%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20opacity%3D0.3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alphahull%3D0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20for%20simplex%20in%20hull.simplices%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pts%20%3D%20minkowski_cloud%5Bsimplex%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20pts%20%3D%20np.vstack(%5Bpts%2C%20pts%5B0%5D%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20go.Scatter3d(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3Dpts%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Dpts%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20z%3Dpts%5B%3A%2C%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20line%3Ddict(color%3D%22black%22%2C%20width%3D2)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20return%20fig%0A%20%20%20%20return%20(add_a_sub_b_to_fig%2C)%0A%0A%0A%40app.cell%0Adef%20_(go%2C%20np)%3A%0A%20%20%20%20def%20add_tri_to_fig(fig%2C%20tri%2C%20label%2C%20color)%3A%0A%20%20%20%20%20%20%20%20tri_plot%20%3D%20np.vstack(%5Btri%2C%20tri%5B0%5D%5D)%0A%20%20%20%20%20%20%20%20fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20go.Scatter3d(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3Dtri_plot%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Dtri_plot%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20z%3Dtri_plot%5B%3A%2C%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%2Bmarkers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3Dlabel%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20line%3Ddict(color%3Dcolor%2C%20width%3D4)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20marker%3Ddict(size%3D4)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20go.Mesh3d(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3Dtri%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Dtri%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20z%3Dtri%5B%3A%2C%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20i%3D%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20j%3D%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20k%3D%5B2%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcolor%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20opacity%3D0.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20fig%0A%20%20%20%20return%20(add_tri_to_fig%2C)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
f876f465ac960c420d055ad9cbce927e