माणिक
पृष्ठभूमि
अनंत आयामों में फैले हुए नियमित पॉलीटोप के तीन परिवार हैं:
सिम्प्लेक्स, जिनमें से टेट्राहेड्रॉन एक सदस्य है (मैं अक्सर उन्हें यहां हाइपरटेट्रैड्रा के रूप में संदर्भित करूंगा, हालांकि सिम्प्लेक्स शब्द अधिक सही है।) उनके स्कोलाफ़ी प्रतीक रूप के हैं। {3,3,...,3,3}
n- क्यूब्स, जिनमें से क्यूब एक सदस्य है। उनके शल्लिी प्रतीक रूप के हैं{4,3,...,3,3}
ऑर्थोप्लेक्स, जिनमें से ऑक्टाहेड्रोन एक सदस्य है (मैं अक्सर उन्हें हाइपरक्टाएक्ट्रा के रूप में यहां संदर्भित करूंगा) उनके स्कोलाफ़ी प्रतीक रूप के हैं {3,3,...,3,4}
नियमित रूप से पॉलीटॉप का एक और अनंत परिवार है, प्रतीक {m}, 2 आयामी बहुभुज, जिसमें किसी भी संख्या में किनारों एम हो सकते हैं।
इसके अलावा, नियमित पॉलीटोप के सिर्फ पांच अन्य विशेष मामले हैं: 3-आयामी आइकोसैहेड्रोन {3,5} और डोडेकाहेड्रोन {5,3}; उनके 4-आयामी एनालॉग्स 600-सेल {3,3,5}और 120-सेल {5,3,3}; और एक अन्य 4 आयामी पॉलीटोप, 24-सेल {3,4,3}(जिसके 3 आयामों में सबसे निकटतम एनालॉग क्यूबक्टाहेड्रोन हैं और इसके डूमोम्बिक डोडेकेर्रॉन हैं)।
मुख्य कार्य
नीचे मुख्य है polytope कार्य है जो स्कलाफी प्रतीक की व्याख्या करता है। यह संख्या की एक सरणी की उम्मीद करता है, और एक सरणी देता है जिसमें निम्नानुसार सरणियों का एक गुच्छा होता है:
सभी कोने की एक सरणी, प्रत्येक को निर्देशांक के n- तत्व सरणी के रूप में व्यक्त किया जाता है (जहां n आयामों की संख्या है)
सभी किनारों की एक सरणी, प्रत्येक को वर्टेक्स इंडेक्स के 2-तत्व के रूप में व्यक्त किया गया है
सभी चेहरों की एक सरणी, प्रत्येक को वर्टेक्स इंडेक्स के एक एम-तत्व के रूप में व्यक्त किया गया है (जहां एम चेहरे पर वर्टिकल की संख्या है)
और इसलिए आयामों की संख्या के लिए उपयुक्त है।
यह स्वयं 2d पॉलीटॉप्स की गणना करता है, 3 अनंत आयामी परिवारों के लिए फ़ंक्शन कॉल करता है, और पांच विशेष मामलों के लिए लुकअप तालिकाओं का उपयोग करता है। यह इसके ऊपर घोषित कार्यों और तालिकाओं को खोजने की अपेक्षा करता है।
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
}
टेट्राहेड्रोन, क्यूब और ऑक्टाहेड्रॉन परिवारों के लिए कार्य
https://en.wikipedia.org/wiki/Simplex
https://en.wikipedia.org/wiki/5-cell (4d सिंप्लेक्स)
http://mathworld.wolfram.com/Simplex.html
टेट्राहेड्रोन परिवार स्पष्टीकरण - निर्देशांक
एन-डायमेंशनल सिम्प्लेक्स / हाइपरटेट्राहेड्रोन में n + 1 अंक हैं। N + 1 आयाम में n- आयामी सिम्प्लेक्स के कोने देना बहुत आसान है।
इस प्रकार (1,0,0),(0,1,0),(0,0,1)3 आयामों में एम्बेडेड 2d त्रिकोण का (1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)वर्णन करता है और 4 आयामों में एम्बेडेड 3 डी टेट्राहेड्रोन का वर्णन करता है। यह पुष्टि करके आसानी से सत्यापित किया जाता है कि कोने के बीच की सभी दूरी sqrt (2) हैं।
एन-डायमेंशनल स्पेस में एन-डायमेंशनल सिम्प्लेक्स के लिए वर्टिकल खोजने के लिए विभिन्न जटिल एल्गोरिदम इंटरनेट पर दिए गए हैं। मुझे इस जवाब /mathpro//a/38725 पर विल जागी की टिप्पणियों में एक सरल रूप से सरल मिला । अंतिम बिंदु p=q=...=x=y=zदूसरों से sqrt (2) की दूरी पर लाइन पर स्थित है। इस प्रकार ऊपर दिए गए त्रिकोण को बिंदु पर जोड़कर (-1/3,-1/3,-1/3)या तो टेट्राहेड्रॉन में परिवर्तित किया जा सकता है (1,1,1)। अंतिम बिंदु के लिए निर्देशांक के ये 2 संभावित मूल्य (1-(1+n)**0.5)/nऔर द्वारा दिए गए हैं(1+(1+n)**0.5)/n
सवाल कहते हैं एन-टोपे के आकार बात नहीं है, मैं n द्वारा के माध्यम से गुणा करना पसंद करते हैं और निर्देशांक का उपयोग (n,0,0..0)करने के लिए (0..0,0,n)पर अंतिम बिंदु के साथ (t,t,..,t,t)जहां टी = 1-(1+n)**0.5सादगी के लिए।
जैसा कि इस टेट्राहेड्रोन का केंद्र मूल में नहीं है, सभी निर्देशांक में सुधार लाइन द्वारा किया जाना चाहिए s.map!{|j|j-((1-(1+n)**0.5)+n)/(1+n)}जो पाता है कि केंद्र मूल से कितना दूर है और इसे घटाता है। मैंने इसे एक अलग ऑपरेशन के रूप में रखा है। हालांकि मैं का इस्तेमाल किया है s[i]+=n, जहां s[i]=nइस तथ्य का उल्लेख करने के लिए है कि जब सरणी द्वारा initialised है, क्या करना होगा s=[0]*nहम सही यहाँ में ऑफसेट के बजाय शुरू में नहीं बल्कि अंत की तुलना में डाल दिया और केंद्रित सुधार कर सकता है।
टेट्राहेड्रोन परिवार स्पष्टीकरण - ग्राफ टोपोलॉजी
सिम्प्लेक्स का ग्राफ पूरा ग्राफ है: हर वर्टेक्स एक बार हर दूसरे वर्टेक्स से जुड़ा होता है। यदि हमारे पास एक एन सिम्प्लेक्स है, तो हम उस बिंदु पर नीचे n-1 सिम्प्लेक्स देने के लिए किसी भी शीर्ष को हटा सकते हैं, जहां हमारे पास एक त्रिकोण या यहां तक कि एक किनारे है।
इसलिए हमारे पास कैटलॉग में कुल 2 ** (n + 1) आइटम हैं, प्रत्येक बाइनरी नंबर द्वारा दर्शाया गया है। यह 0कुछ भी नहीं के 1लिए है, एक शीर्ष के लिए एक और 1एक किनारे के लिए दो एस के माध्यम से होता है , 1पूरे पॉलीटोप के लिए सभी एस तक।
हम प्रत्येक आकार के तत्वों को संग्रहीत करने के लिए खाली सरणियों की एक सरणी सेट करते हैं। तब हम शून्य से लेकर (2 ** n + 1) तक लूप बनाते हैं, ताकि प्रत्येक उपसमुच्चय के उपसमुच्चय को उत्पन्न किया जा सके और प्रत्येक उपसमूह के आकार के अनुसार उन्हें स्टोर किया जा सके।
हम एक किनारे (एक शीर्ष या एक शून्य) से छोटे किसी भी चीज में दिलचस्पी नहीं रखते हैं और न ही पूर्ण पॉलीटोप में (जैसा कि प्रश्न में उदाहरण में पूर्ण घन नहीं दिया गया है), इसलिए हम tg[2..n]इन अवांछित तत्वों को हटाने के लिए लौटते हैं। लौटने से पहले, हम शुरुआत से पहले निर्देशांक निर्देशांक वाले [टीवी] से निपटते हैं।
कोड
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]}
क्यूब और ऑक्टाहेड्रोन परिवारों की व्याख्या - निर्देशांक
एन-घन है 2**nकोने, प्रत्येक एन की एक सरणी का प्रतिनिधित्व करती 1है और -1(सभी संभावनाओं की अनुमति है।) हम पुनरावृति अनुक्रमित के माध्यम से रों 0को 2**n-1सभी कोने की सूची के लिए, और के टुकड़े के माध्यम से बार-बार दोहराना द्वारा प्रत्येक शिखर के लिए एक सरणी का निर्माण सूचकांक और जोड़ -1या 1सरणी में (सबसे महत्वपूर्ण बिट के लिए कम से कम महत्वपूर्ण बिट।) इस प्रकार बाइनरी 11014 डी बिंदु बन जाता है [1,-1,1,1]।
N-octahedron या n-orthoplex में 2nकोने होते हैं, जिसमें एक को छोड़कर सभी निर्देशांक शून्य होते हैं, जो एक 1या होता है -1। उत्पन्न सरणी में कोने का क्रम है [[1,0,0..],[-1,0,0..],[0,1,0..],[0,-1,0..],[0,0,1..],[0,0,-1..]...]। ध्यान दें कि जैसे कि ऑक्टाहेड्रन क्यूब का दोहरी है, ऑक्टाहेड्रोन के शीर्ष को क्यूब के चेहरों के केंद्र द्वारा परिभाषित किया गया है जो इसे घेरता है।
क्यूब और ऑक्टाहेड्रोन परिवारों की व्याख्या - ग्राफ टोपोलॉजी
कुछ प्रेरणा हाइपरक्यूब पक्षों से ली गई थी और यह तथ्य कि हाइपरओक्टाहेड्रोन हाइपरक्यूब का दोहरी है।
एन-क्यूब के लिए, 3**nकैटलॉग के लिए आइटम हैं । उदाहरण के लिए, 3 घन में है3**3 27 तत्व हैं। यह एक रूबिक क्यूब का अध्ययन करके देखा जा सकता है, जिसमें 27 के कुल के लिए 1 केंद्र, 6 चेहरे, 12 किनारों और 8 कोने हैं। हम सभी आयामों में -1,0 और -1 से पुनरावृत्ति करते हैं, जो कि साइड-स्पीड 2xxx2 के एन-क्यूब को परिभाषित करता है। .. और सभी कोने लौटें जो घन के विपरीत दिशा में नहीं हैं। इस प्रकार घन का केंद्र बिंदु सभी 2 ** n कोने को वापस कर देता है, और किसी भी अक्ष के साथ केंद्र से एक इकाई दूर जाने पर कोने की संख्या आधे से कम हो जाती है।
टेट्राहेड्रॉन परिवार के साथ, हम एरे की एक खाली सरणी उत्पन्न करके शुरू करते हैं और इसे अपने तत्व के संख्या के अनुसार आबाद करते हैं। ध्यान दें कि क्योंकि कोने की संख्या 2 ** n के रूप में बदलती है क्योंकि हम किनारों, चेहरों, क्यूब्स आदि के माध्यम से ऊपर जाते हैं, हम log2(s.size)+1बस के बजाय उपयोग करते हैं s.size। फिर, हमें फ़ंक्शन से लौटने से पहले हाइपरक्यूब को और 2 तत्वों से कम के सभी तत्वों को निकालना होगा।
ऑक्टाहेड्रोन / ऑर्थोप्लेक्स परिवार क्यूब परिवार के दोहरे हैं, इसलिए फिर 3**nसे कैटलॉग करने के लिए आइटम हैं । यहां हम -1,0,1सभी आयामों के लिए पुनरावृति करते हैं और यदि नॉनस्टेरो किसी वर्टेक्स का समन्वय बिंदु के संबंधित समन्वय के बराबर है, तो उस बिंदु के अनुरूप सूची में वर्टेक्स जोड़ा जाता है। इस प्रकार एक बढ़त दो नॉनज़ेरो निर्देशांक के साथ एक बिंदु से मेल खाती है, 3 नॉनज़ेरो निर्देशांक के साथ एक बिंदु के लिए एक त्रिकोण और 4 नॉनज़ेरो संपर्कों के साथ एक बिंदु पर एक टेट्राहेड्रोन (4 डी अंतरिक्ष में)।
प्रत्येक बिंदु के लिए शीर्ष के परिणामी सरणियों को अन्य मामलों की तरह बड़े सरणी में संग्रहीत किया जाता है, और हमें लौटने से पहले 2 तत्वों से कम किसी भी तत्व को निकालना होगा। लेकिन इस मामले में हमें पूर्ण मूल n-tope को निकालने की आवश्यकता नहीं है क्योंकि एल्गोरिथ्म इसे रिकॉर्ड नहीं करता है।
क्यूब के लिए कोड के कार्यान्वयन को यथासंभव समान रूप से डिजाइन किया गया था। हालांकि इसमें एक निश्चित लालित्य है, यह संभावना है कि समान सिद्धांतों के आधार पर अधिक कुशल एल्गोरिदम को तैयार किया जा सकता है।
https://en.wikipedia.org/wiki/Hypercube
http://mathworld.wolfram.com/Hypercube.html
https://en.wikipedia.org/wiki/Cross-polytope
http://mathworld.wolfram.com/CrossPolytope.html
3 डी विशेष मामलों के लिए तालिकाओं को बनाने के लिए कोड
पिछले आयाम के समानांतर पांच गुना समरूपता अक्ष के साथ उन्मुख icosahedron / dodecahedron के साथ एक अभिविन्यास का उपयोग किया गया था, क्योंकि यह भागों के सबसे सुसंगत लेबलिंग के लिए बनाया गया था। ऑक्सोसहेड्रोन के लिए वर्टिस और चेहरों की संख्या कोड टिप्पणियों में आरेख के अनुसार है, और डोडेकेहेड्रॉन के लिए उलट है।
Https://en.wikipedia.org/wiki/Regular_icosahedron के अनुसार icosahedron के 10 गैर-ध्रुवीय कोने का अक्षांश +/- arctan (1/2) है। icosahedron के पहले ऊपरी सिरे के निर्देशांक की गणना की जाती है। यह, xy समतल से दूरी +/- 2 पर त्रिज्या 2 के दो वृत्तों पर है। यह परिधि के वर्गमीटर (5) का कुल त्रिज्या बनाता है, इसलिए अंतिम 2 कोने (0,0, + / - sqrt (2)) पर हैं।
डोडेकाहेड्रोन के कोने के निर्देशांक की गणना तीन आईसीओसहेड्रोन कोने के निर्देशांक द्वारा की जाती है जो उन्हें घेरते हैं।
=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]]
4 डी विशेष मामलों के लिए तालिकाओं को बनाने के लिए कोड
यह थोड़ा हैक है। इस कोड को चलने में कुछ सेकंड लगते हैं। बेहतर होगा कि आउटपुट को किसी फाइल में स्टोर करें और उसे आवश्यकतानुसार लोड करें।
600 कोश के लिए 120 शीर्ष निर्देशांक की सूची http://mathworld.wolfram.com/600-Cell.html से है । 24 शीर्ष निर्देशांक जो एक सुनहरे अनुपात की सुविधा नहीं देते हैं, एक 24-सेल के कोने बनाते हैं। विकिपीडिया की एक ही योजना है लेकिन इन 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://en.wikipedia.org/wiki/600-cell
http://mathworld.wolfram.com/600-Cell.html
https://en.wikipedia.org/wiki/120-cell
http://mathworld.wolfram.com/120-Cell.html
https://en.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]