루비
배경
무한 치수로 확장되는 일반 폴리 토프 제품군에는 세 가지가 있습니다.
사면체가 구성원 인 단면 (단순이라는 용어가 더 정확하지만 여기서는 종종 정사면체라고 부릅니다) {3,3,...,3,3}
큐브가 멤버 인 n- 큐브 그들의 schlafi 기호는 형태입니다{4,3,...,3,3}
정팔면체는 정팔면체가 멤버입니다 (저는 종종 정팔면체라고 부릅니다) 그들의 상징 기호는 형태입니다 {3,3,...,3,4}
일반 폴리 토프의 추가 무한 패밀리가 있습니다. {m}
2 개의 폴리곤 가 있으며, 이는 임의의 개수의 에지 (m)를 가질 수있다.
이에 더하여, 규칙적인 폴리 토프의 다른 5 가지 특별한 경우가있다 : 3 차원 정 이십 면체 {3,5}
와 십이 면체 {5,3}
; 그들의 4 차원 유사체 600- 셀 {3,3,5}
및 120- 셀 {5,3,3}
; 다른 4 차원 폴리 토프 인 24 셀{3,4,3}
(3 차원에서 가장 가까운 유사체는 육면체와 그 이중 마름모꼴 십이 면체).
주요 기능
다음은 polytope
schlafi 기호를 해석 하는 주요 기능입니다. 숫자 배열을 예상하고 다음과 같이 여러 배열을 포함하는 배열을 반환합니다.
모든 정점의 배열로, 각각 n 요소의 좌표 배열로 표현됩니다 (여기서 n은 차원 수입니다).
모든 모서리의 배열로 각각 정점 색인의 2 요소로 표시됩니다.
모든면의 배열로, 각각 정점 색인의 m- 요소로 표시됩니다 (여기서 m은 면당 정점의 수임)
치수의 수에 따라 적절하게 등등.
2d 폴리 토프 자체를 계산하고 3 개의 무한 치수 패밀리에 대한 함수를 호출하고 5 개의 특수한 경우에 대해 조회 테이블을 사용합니다. 위에 선언 된 함수와 테이블을 찾을 것으로 예상됩니다.
include Math
#code in subsequent sections of this answer should be inserted here
polytope=->schl{
if schl.size==1 #if a single digit calculate and return a polygon
return [(1..schl[0]).map{|i|[sin(PI*2*i/schl[0]),cos(PI*2*i/schl[0])]},(1..schl[0]).map{|i|[i%schl[0],(i+1)%schl[0]]}]
elsif i=[[3,5],[5,3]].index(schl) #if a 3d special, lookup from tables
return [[vv,ee,ff],[uu,aa,bb]][i]
elsif i=[[3,3,5],[5,3,3],[3,4,3]].index(schl) #if a 4d special. lookup fromm tables
return [[v,e,f,g],[u,x,y,z],[o,p,q,r]][i]
elsif schl.size==schl.count(3) #if all threes, call tetr for a hypertetrahedron
return tetr[schl.size+1]
elsif schl.size-1==schl.count(3) #if all except one number 3
return cube[schl.size+1] if schl[0]==4 #and the 1st digit is 4, call cube for a hypercube
return octa[schl.size+1] if schl[-1]==4 #and the last digit is 4, call octa for a hyperoctahedron
end
return "error" #in any other case return an error
}
4 면체, 정육면체 및 8 면체 가족을위한 함수
https://en.wikipedia.org/wiki/Simplex
https://ko.wikipedia.org/wiki/5-cell (4D 심플 렉스)
http://mathworld.wolfram.com/Simplex.html
정사각형 가족 설명-좌표
n- 차원 심플 렉스 / 육면체는 n + 1 포인트를 갖는다. n + 1 차원의 n 차원 심플 렉스의 정점을 제공하는 것은 매우 쉽습니다.
따라서 (1,0,0),(0,1,0),(0,0,1)
3 차원에 포함 된 2 차원 삼각형과 (1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)
4 차원에 포함 된 3 차원 4 면체를 설명합니다. 이것은 꼭짓점 사이의 모든 거리가 sqrt (2)인지 확인하여 쉽게 확인할 수 있습니다.
n 차원 공간에서 n 차원 심플 렉스의 꼭짓점을 찾기 위해 인터넷에 다양한 복잡한 알고리즘이 제공됩니다. 이 답변 /mathpro//a/38725 에 대한 Will Jagy의 의견에서 놀랍도록 간단한 것을 발견했습니다 . 마지막 지점은 다른 지점 p=q=...=x=y=z
에서 sqrt (2) 거리 에있는 선 에 있습니다. 따라서 위의 삼각형은 (-1/3,-1/3,-1/3)
또는에 점을 추가하여 4 면체로 변환 할 수 있습니다 (1,1,1)
. 마지막 점에 대한이 두 가지 가능한 좌표 값은 (1-(1+n)**0.5)/n
및(1+(1+n)**0.5)/n
질문에 따르면 n-tope의 크기는 중요하지 않으므로 n을 곱하고 t = 단순화 의 최종 지점 (n,0,0..0)
까지 좌표 를 사용하는 것이 좋습니다.(0..0,0,n)
(t,t,..,t,t)
1-(1+n)**0.5
이 4 면체의 중심이 원점에 있지 않기 때문에 모든 좌표에 대한 수정 s.map!{|j|j-((1-(1+n)**0.5)+n)/(1+n)}
은 중심이 원점에서 얼마나 떨어져 있는지 찾아서 빼는 선으로 이루어져야 합니다. 나는 이것을 별도의 작업으로 유지했다. 그러나 배열을 초기화 할 때 여기에 올바른 오프셋을 배치하고 끝이 아닌 처음에 중심 조정을 수행 할 수 있다는 사실을 암시하기 위해 s[i]+=n
어디에서 s[i]=n
할 것인지를 사용 s=[0]*n
했습니다.
사면체 가족 설명-그래프 토폴로지
심플 렉스의 그래프는 완전한 그래프입니다. 모든 정점은 다른 모든 정점에 정확히 한 번 연결됩니다. 만약 우리가 n 개의 단면을 가지고 있다면, 정점을 제거하여 삼각형이나 가장자리가있는 지점까지 n-1의 단면을 줄 수 있습니다.
따라서 우리는 카탈로그에 총 2 ** (n + 1) 개의 항목을 가지고 있으며 각각은 이진수로 표시됩니다. 이것은 0
무의미에 대한 모든 것에서부터 1
정점에 대한 하나 와 1
가장자리에 대한 2 개의 1
것부터 완전한 폴리 토프에 대한 모든 것까지 다양 합니다.
각 크기의 요소를 저장하기 위해 빈 배열의 배열을 설정했습니다. 그런 다음 0에서 (2 ** n + 1)까지 반복하여 가능한 정점의 각 하위 집합을 생성하고 각 하위 집합의 크기에 따라 배열에 저장합니다.
우리는 가장자리보다 작은 것 (정점 또는 0)이나 완전한 폴리 토프 (문제의 예에서 완전한 큐브가 제공되지 않기 때문에)에 관심이 없으므로 tg[2..n]
이러한 원치 않는 요소를 제거하기 위해 돌아갑니다 . 돌아 오기 전에 정점 좌표가 포함 된 [tv]를 시작 부분에 붙입니다.
암호
tetr=->n{
#Tetrahedron Family Vertices
tv=(0..n).map{|i|
s=[0]*n
if i==n
s.map!{(1-(1+n)**0.5)}
else
s[i]+=n
end
s.map!{|j|j-((1-(1+n)**0.5)+n)/(1+n)}
s}
#Tetrahedron Family Graph
tg=(0..n+1).map{[]}
(2**(n+1)).times{|i|
s=[]
(n+1).times{|j|s<<j if i>>j&1==1}
tg[s.size]<<s
}
return [tv]+tg[2..n]}
cube=->n{
#Cube Family Vertices
cv=(0..2**n-1).map{|i|s=[];n.times{|j|s<<(i>>j&1)*2-1};s}
#Cube Family Graph
cg=(0..n+1).map{[]}
(3**n).times{|i| #for each point
s=[]
cv.size.times{|j| #and each vertex
t=true #assume vertex goes with point
n.times{|k| #and each pair of opposite sides
t&&= (i/(3**k)%3-1)*cv[j][k]!=-1 #if the vertex has kingsmove distance >1 from point it does not belong
}
s<<j if t #add the vertex if it belongs
}
cg[log2(s.size)+1]<<s if s.size > 0
}
return [cv]+cg[2..n]}
octa=->n{
#Octahedron Family Vertices
ov=(0..n*2-1).map{|i|s=[0]*n;s[i/2]=(-1)**i;s}
#Octahedron Family Graph
og=(0..n).map{[]}
(3**n).times{|i| #for each point
s=[]
ov.size.times{|j| #and each vertex
n.times{|k| #and each pair of opposite sides
s<<j if (i/(3**k)%3-1)*ov[j][k]==1 #if the vertex is located in the side corresponding to the point, add the vertex to the list
}
}
og[s.size]<<s
}
return [ov]+og[2..n]}
큐브 및 팔면체 가족 설명-좌표
n- 큐브에는 2**n
각각 n 1
과 -1
s 의 배열로 표현되는 꼭짓점이 있습니다 (모든 가능성이 허용됩니다). 모든 정점 목록의 색인 0
을 반복 2**n-1
하고 각 정점의 비트를 반복하여 각 꼭짓점에 대한 배열을 만듭니다. 인덱스 및 배열 추가 -1
또는 1
배열 (최하위 비트에서 최상위 비트). 따라서 이항 1101
은 4d 포인트가 [1,-1,1,1]
됩니다.
n-octahedron 또는 n-orthoplex는 2n
꼭짓점을 가지며 , 모든 좌표는 0을 제외하고는 0 1
이거나 또는 -1
입니다. 생성 된 배열의 꼭짓점 순서는 [[1,0,0..],[-1,0,0..],[0,1,0..],[0,-1,0..],[0,0,1..],[0,0,-1..]...]
입니다. 8 면체는 입방체의 이중이므로, 8 면체의 꼭짓점은 그것을 둘러싸는 입방체면의 중심에 의해 정의됩니다.
큐브 및 8 면체 가족 설명-그래프 토폴로지
하이퍼 큐브 측면 에서 약간의 영감을 얻었으며 하이퍼 팔면체가 하이퍼 큐브의 이중이라는 사실이 있습니다.
n- 큐브의 경우 3**n
카탈로그 할 항목 이 있습니다. 예를 들어, 3 큐브에는 3**3
= 27 개의 요소가 있습니다. 중심점 1 개,면 6 개, 모서리 12 개, 꼭짓점 8 개로 총 27 개의 루빅스 큐브를 연구하면 알 수 있습니다. .. 큐브의 반대쪽에없는 모든 정점을 반환합니다. 따라서 큐브의 중심점은 2 ** n 정점을 모두 반환하고 축을 따라 한 단위를 중심에서 멀어지면 정점 수가 절반으로 줄어 듭니다.
4 면체 패밀리와 마찬가지로 빈 배열 배열을 생성하여 시작하여 요소 당 정점 수에 따라 채 웁니다. 꼭지점의 수는 가장자리,면, 큐브 등을 따라 올라갈 때 2 ** n만큼 다양하므로 log2(s.size)+1
간단히 대신 사용 합니다 s.size
. 다시 함수에서 복귀하기 전에 하이퍼 큐브 자체와 꼭짓점이 2 개 미만인 모든 요소를 제거해야합니다.
8 면체 / 직교 이중 패밀리는 큐브 패밀리의 이중 체이므로 다시 3**n
카탈로그 할 항목 이 있습니다. 여기서 우리 -1,0,1
는 모든 차원에 대해 반복 하고 정점의 0이 아닌 좌표가 해당 점의 해당 좌표와 같으면 해당 점에 해당하는 목록에 정점이 추가됩니다. 따라서 모서리는 0이 아닌 좌표가 2 개인 점, 삼각형이 0이 아닌 좌표가있는 점, 4 면체가 0이 아닌 접점이있는 점 (4d 공간)에 해당합니다.
각 점에 대한 결과 정점 배열은 다른 경우와 같이 큰 배열에 저장되므로 반환하기 전에 정점이 2 개 미만인 요소를 제거해야합니다. 그러나이 경우 알고리즘이 기록하지 않기 때문에 전체 부모 n-tope을 제거 할 필요가 없습니다.
큐브의 코드 구현은 가능한 한 유사하게 설계되었습니다. 이것은 어떤 우아함을 가지고 있지만, 동일한 원리에 기반한보다 효율적인 알고리즘이 고안 될 수 있습니다.
https://en.wikipedia.org/wiki/Hypercube
http://mathworld.wolfram.com/Hypercube.html
https://ko.wikipedia.org/wiki/Cross-polytope
http://mathworld.wolfram.com/CrossPolytope.html
3D 특수 사례에 대한 테이블 생성 코드
마지막 치수와 평행 한 5 중 대칭 축으로 정 이십 면체 / 십이 면체를 갖는 방향이 사용되었으며, 이는 부품의 가장 일관된 라벨링을 위해 만들어졌습니다. 정 이십 면체의 정점과면의 번호는 코드 주석의 다이어그램에 따르며 12 면체의 경우에는 반대입니다.
https://en.wikipedia.org/wiki/Regular_icosahedron 에 따르면 정 이십 면체의 10 개의 비극성 정점의 위도는 +/- arctan (1/2)입니다 정 이십 면체의 첫 10 개의 정점의 좌표는 이것은 xy 평면으로부터 +/- 2 거리에있는 반경 2의 두 원에 있습니다. 이렇게하면 전체 반경이 sqrt (5)입니다. 마지막 두 정점이 (0,0, + /-sqrt (2))에 있습니다.
정 십이 면체의 정점의 좌표는 그것들을 둘러싸는 3 면체 정점의 좌표를 합함으로써 계산된다.
=begin
TABLE NAMES vertices edges faces
icosahedron vv ee ff
dodecahedron uu aa bb
10
/ \ / \ / \ / \ / \
/10 \ /12 \ /14 \ /16 \ /18 \
-----1-----3-----5-----7-----9
\ 0 / \ 2 / \ 4 / \ 6 / \ 8 / \
\ / 1 \ / 3 \ / 5 \ / 7 \ / 9 \
0-----2-----4-----6-----8-----
\11 / \13 / \15 / \17 / \19 /
\ / \ / \ / \ / \ /
11
=end
vv=[];ee=[];ff=[]
10.times{|i|
vv[i]=[2*sin(PI/5*i),2*cos(PI/5*i),(-1)**i]
ee[i]=[i,(i+1)%10];ee[i+10]=[i,(i+2)%10];ee[i+20]=[i,11-i%2]
ff[i]=[(i-1)%10,i,(i+1)%10];ff[i+10]=[(i-1)%10,10+i%2,(i+1)%10]
}
vv+=[[0,0,-5**0.5],[0,0,5**0.5]]
uu=[];aa=[];bb=[]
10.times{|i|
uu[i]=(0..2).map{|j|vv[ff[i][0]][j]+vv[ff[i][1]][j]+vv[ff[i][2]][j]}
uu[i+10]=(0..2).map{|j|vv[ff[i+10][0]][j]+vv[ff[i+10][1]][j]+vv[ff[i+10][2]][j]}
aa[i]=[i,(i+1)%10];aa[i+10]=[i,(i+10)%10];aa[i+20]=[(i-1)%10+10,(i+1)%10+10]
bb[i]=[(i-1)%10+10,(i-1)%10,i,(i+1)%10,(i+1)%10+10]
}
bb+=[[10,12,14,16,18],[11,13,15,17,19]]
4d 특수 사례에 대한 테이블을 생성하기위한 코드
이것은 약간의 해킹입니다. 이 코드는 실행하는 데 몇 초가 걸립니다. 출력을 파일에 저장하고 필요에 따라로드하는 것이 좋습니다.
600 셀에 대한 120 개의 정점 좌표 목록은 http://mathworld.wolfram.com/600-Cell.html에 있습니다. 황금 비율을 특징으로하지 않는 24 개의 정점 좌표는 24 셀의 정점을 형성합니다. Wikipedia의 구성은 동일하지만이 24 개의 좌표와 다른 96 개의 상대적인 배율에 오류가 있습니다.
#TABLE NAMES vertices edges faces cells
#600 cell (analogue of icosahedron) v e f g
#120 cell (analogue of dodecahedron) u x y z
#24 cell o p q r
#600-CELL
# 120 vertices of 600cell. First 24 are also vertices of 24-cell
v=[[2,0,0,0],[0,2,0,0],[0,0,2,0],[0,0,0,2],[-2,0,0,0],[0,-2,0,0],[0,0,-2,0],[0,0,0,-2]]+
(0..15).map{|j|[(-1)**(j/8),(-1)**(j/4),(-1)**(j/2),(-1)**j]}+
(0..95).map{|i|j=i/12
a,b,c,d=1.618*(-1)**(j/4),(-1)**(j/2),0.618*(-1)**j,0
h=[[a,b,c,d],[b,a,d,c],[c,d,a,b],[d,c,b,a]][i%12/3]
(i%3).times{h[0],h[1],h[2]=h[1],h[2],h[0]}
h}
#720 edges of 600cell. Identified by minimum distance of 2/phi between them
e=[]
120.times{|i|120.times{|j|
e<<[i,j] if i<j && ((v[i][0]-v[j][0])**2+(v[i][1]-v[j][1])**2+(v[i][2]-v[j][2])**2+(v[i][3]-v[j][3])**2)**0.5<1.3
}}
#1200 faces of 600cell.
#If 2 edges share a common vertex and the other 2 vertices form an edge in the list, it is a valid triangle.
f=[]
720.times{|i|720.times{|j|
f<< [e[i][0],e[i][1],e[j][1]] if i<j && e[i][0]==e[j][0] && e.index([e[i][1],e[j][1]])
}}
#600 cells of 600cell.
#If 2 triangles share a common edge and the other 2 vertices form an edge in the list, it is a valid tetrahedron.
g=[]
1200.times{|i|1200.times{|j|
g<< [f[i][0],f[i][1],f[i][2],f[j][2]] if i<j && f[i][0]==f[j][0] && f[i][1]==f[j][1] && e.index([f[i][2],f[j][2]])
}}
#120 CELL (dual of 600 cell)
#600 vertices of 120cell, correspond to the centres of the cells of the 600cell
u=g.map{|i|s=[0,0,0,0];i.each{|j|4.times{|k|s[k]+=v[j][k]/4.0}};s}
#1200 edges of 120cell at centres of faces of 600-cell. Search for pairs of tetrahedra with common face
x=f.map{|i|s=[];600.times{|j|s<<j if i==(i & g[j])};s}
#720 pentagonal faces, surrounding edges of 600-cell. Search for sets of 5 tetrahedra with common edge
y=e.map{|i|s=[];600.times{|j|s<<j if i==(i & g[j])};s}
#120 dodecahedral cells surrounding vertices of 600-cell. Search for sets of 20 tetrahedra with common vertex
z=(0..119).map{|i|s=[];600.times{|j|s<<j if [i]==([i] & g[j])};s}
#24-CELL
#24 vertices, a subset of the 600cell
o=v[0..23]
#96 edges, length 2, found by minimum distances between vertices
p=[]
24.times{|i|24.times{|j|
p<<[i,j] if i<j && ((v[i][0]-v[j][0])**2+(v[i][1]-v[j][1])**2+(v[i][2]-v[j][2])**2+(v[i][3]-v[j][3])**2)**0.5<2.1
}}
#96 triangles
#If 2 edges share a common vertex and the other 2 vertices form an edge in the list, it is a valid triangle.
q=[]
96.times{|i|96.times{|j|
q<< [p[i][0],p[i][1],p[j][1]] if i<j && p[i][0]==p[j][0] && p.index([p[i][1],p[j][1]])
}}
#24 cells. Calculates the centre of the cell and the 6 vertices nearest it
r=(0..23).map{|i|a,b=(-1)**i,(-1)**(i/2)
c=[[a,b,0,0],[a,0,b,0],[a,0,0,b],[0,a,b,0],[0,a,0,b],[0,0,a,b]][i/4]
s=[]
24.times{|j|t=v[j]
s<<j if (c[0]-t[0])**2+(c[1]-t[1])**2+(c[2]-t[2])**2+(c[3]-t[3])**2<=2
}
s}
https://ko.wikipedia.org/wiki/600-cell
http://mathworld.wolfram.com/600-Cell.html
https://ko.wikipedia.org/wiki/120-cell
http://mathworld.wolfram.com/120-Cell.html
https://ko.wikipedia.org/wiki/24-cell
http://mathworld.wolfram.com/24-Cell.html
사용 및 출력 예
cell24 = polytope[[3,4,3]]
puts "vertices"
cell24[0].each{|i|p i}
puts "edges"
cell24[1].each{|i|p i}
puts "faces"
cell24[2].each{|i|p i}
puts "cells"
cell24[3].each{|i|p i}
vertices
[2, 0, 0, 0]
[0, 2, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 2]
[-2, 0, 0, 0]
[0, -2, 0, 0]
[0, 0, -2, 0]
[0, 0, 0, -2]
[1, 1, 1, 1]
[1, 1, 1, -1]
[1, 1, -1, 1]
[1, 1, -1, -1]
[1, -1, 1, 1]
[1, -1, 1, -1]
[1, -1, -1, 1]
[1, -1, -1, -1]
[-1, 1, 1, 1]
[-1, 1, 1, -1]
[-1, 1, -1, 1]
[-1, 1, -1, -1]
[-1, -1, 1, 1]
[-1, -1, 1, -1]
[-1, -1, -1, 1]
[-1, -1, -1, -1]
edges
[0, 8]
[0, 9]
[0, 10]
[0, 11]
[0, 12]
[0, 13]
[0, 14]
[0, 15]
[1, 8]
[1, 9]
[1, 10]
[1, 11]
[1, 16]
[1, 17]
[1, 18]
[1, 19]
[2, 8]
[2, 9]
[2, 12]
[2, 13]
[2, 16]
[2, 17]
[2, 20]
[2, 21]
[3, 8]
[3, 10]
[3, 12]
[3, 14]
[3, 16]
[3, 18]
[3, 20]
[3, 22]
[4, 16]
[4, 17]
[4, 18]
[4, 19]
[4, 20]
[4, 21]
[4, 22]
[4, 23]
[5, 12]
[5, 13]
[5, 14]
[5, 15]
[5, 20]
[5, 21]
[5, 22]
[5, 23]
[6, 10]
[6, 11]
[6, 14]
[6, 15]
[6, 18]
[6, 19]
[6, 22]
[6, 23]
[7, 9]
[7, 11]
[7, 13]
[7, 15]
[7, 17]
[7, 19]
[7, 21]
[7, 23]
[8, 9]
[8, 10]
[8, 12]
[8, 16]
[9, 11]
[9, 13]
[9, 17]
[10, 11]
[10, 14]
[10, 18]
[11, 15]
[11, 19]
[12, 13]
[12, 14]
[12, 20]
[13, 15]
[13, 21]
[14, 15]
[14, 22]
[15, 23]
[16, 17]
[16, 18]
[16, 20]
[17, 19]
[17, 21]
[18, 19]
[18, 22]
[19, 23]
[20, 21]
[20, 22]
[21, 23]
[22, 23]
faces
[0, 8, 9]
[0, 8, 10]
[0, 8, 12]
[0, 9, 11]
[0, 9, 13]
[0, 10, 11]
[0, 10, 14]
[0, 11, 15]
[0, 12, 13]
[0, 12, 14]
[0, 13, 15]
[0, 14, 15]
[1, 8, 9]
[1, 8, 10]
[1, 8, 16]
[1, 9, 11]
[1, 9, 17]
[1, 10, 11]
[1, 10, 18]
[1, 11, 19]
[1, 16, 17]
[1, 16, 18]
[1, 17, 19]
[1, 18, 19]
[2, 8, 9]
[2, 8, 12]
[2, 8, 16]
[2, 9, 13]
[2, 9, 17]
[2, 12, 13]
[2, 12, 20]
[2, 13, 21]
[2, 16, 17]
[2, 16, 20]
[2, 17, 21]
[2, 20, 21]
[3, 8, 10]
[3, 8, 12]
[3, 8, 16]
[3, 10, 14]
[3, 10, 18]
[3, 12, 14]
[3, 12, 20]
[3, 14, 22]
[3, 16, 18]
[3, 16, 20]
[3, 18, 22]
[3, 20, 22]
[4, 16, 17]
[4, 16, 18]
[4, 16, 20]
[4, 17, 19]
[4, 17, 21]
[4, 18, 19]
[4, 18, 22]
[4, 19, 23]
[4, 20, 21]
[4, 20, 22]
[4, 21, 23]
[4, 22, 23]
[5, 12, 13]
[5, 12, 14]
[5, 12, 20]
[5, 13, 15]
[5, 13, 21]
[5, 14, 15]
[5, 14, 22]
[5, 15, 23]
[5, 20, 21]
[5, 20, 22]
[5, 21, 23]
[5, 22, 23]
[6, 10, 11]
[6, 10, 14]
[6, 10, 18]
[6, 11, 15]
[6, 11, 19]
[6, 14, 15]
[6, 14, 22]
[6, 15, 23]
[6, 18, 19]
[6, 18, 22]
[6, 19, 23]
[6, 22, 23]
[7, 9, 11]
[7, 9, 13]
[7, 9, 17]
[7, 11, 15]
[7, 11, 19]
[7, 13, 15]
[7, 13, 21]
[7, 15, 23]
[7, 17, 19]
[7, 17, 21]
[7, 19, 23]
[7, 21, 23]
cells
[0, 1, 8, 9, 10, 11]
[1, 4, 16, 17, 18, 19]
[0, 5, 12, 13, 14, 15]
[4, 5, 20, 21, 22, 23]
[0, 2, 8, 9, 12, 13]
[2, 4, 16, 17, 20, 21]
[0, 6, 10, 11, 14, 15]
[4, 6, 18, 19, 22, 23]
[0, 3, 8, 10, 12, 14]
[3, 4, 16, 18, 20, 22]
[0, 7, 9, 11, 13, 15]
[4, 7, 17, 19, 21, 23]
[1, 2, 8, 9, 16, 17]
[2, 5, 12, 13, 20, 21]
[1, 6, 10, 11, 18, 19]
[5, 6, 14, 15, 22, 23]
[1, 3, 8, 10, 16, 18]
[3, 5, 12, 14, 20, 22]
[1, 7, 9, 11, 17, 19]
[5, 7, 13, 15, 21, 23]
[2, 3, 8, 12, 16, 20]
[3, 6, 10, 14, 18, 22]
[2, 7, 9, 13, 17, 21]
[6, 7, 11, 15, 19, 23]