शेष रासायनिक समीकरण!


30

बेरंड एक हाई स्कूल के छात्र हैं, जिन्हें रसायन विज्ञान में कुछ समस्याएं हैं। कक्षा में उन्हें कुछ प्रयोगों के लिए रासायनिक समीकरणों को डिजाइन करना पड़ता है, जो वे कर रहे हैं, जैसे कि हेप्टेन का दहन:

C 7 H 16 + 11O 2 → 7CO 2 + 8H 2 O

चूंकि गणित वास्तव में बर्नड का सबसे मजबूत विषय नहीं है, इसलिए अक्सर उसके पास प्रतिक्रिया के समर्थक और उतार-चढ़ाव के बीच सटीक अनुपात खोजने का कठिन समय होता है। चूंकि आप बर्नड के ट्यूटर हैं, इसलिए उसकी मदद करना आपका काम है! एक प्रोग्राम लिखें, जो एक वैध रासायनिक समीकरण प्राप्त करने के लिए आवश्यक प्रत्येक पदार्थ की मात्रा की गणना करता है।

इनपुट

इनपुट बिना मात्रा के एक रासायनिक समीकरण है। शुद्ध ASCII में इसे संभव बनाने के लिए, हम किसी भी सदस्यता को साधारण संख्या के रूप में लिखते हैं। तत्व नाम हमेशा एक कैपिटल लेटर से शुरू होते हैं और एक माइनसकूल के बाद हो सकते हैं। अणुओं को +संकेतों के साथ अलग किया ->जाता है, समीकरण के दोनों किनारों के बीच एक ASCII- कला तीर डाला जाता है:

Al+Fe2O4->Fe+Al2O3

इनपुट को एक नई पंक्ति के साथ समाप्त किया गया है और इसमें कोई स्थान नहीं होगा। यदि इनपुट अमान्य है, तो आपका प्रोग्राम जो चाहें उसे कर सकता है।

आप मान सकते हैं, कि इनपुट 1024 वर्णों से अधिक लंबा नहीं है। आपका प्रोग्राम या तो मानक इनपुट से इनपुट पढ़ सकता है, पहले तर्क से या फिर रनटाइम में कार्यान्वयन परिभाषित तरीके से यदि न तो संभव है।

उत्पादन

आपके प्रोग्राम का आउटपुट अतिरिक्त संख्याओं के साथ संवर्धित इनपुट समीकरण है। प्रत्येक तत्व के लिए परमाणुओं की संख्या तीर के दोनों किनारों पर समान होनी चाहिए। उपरोक्त उदाहरण के लिए, एक मान्य आउटपुट है:

2Al+Fe2O3->2Fe+Al2O3

यदि किसी अणु की संख्या 1 है, तो उसे छोड़ दें। एक नंबर हमेशा एक धनात्मक पूर्णांक होना चाहिए। आपके प्रोग्राम को संख्याएँ ऐसी होनी चाहिए कि उनका योग न्यूनतम हो। उदाहरण के लिए, निम्नलिखित अवैध है:

40Al+20Fe2O3->40Fe+20Al2O3

अगर कोई हल नहीं है, तो प्रिंट करें

Nope!

बजाय। एक नमूना इनपुट जिसका कोई समाधान नहीं है

Pb->Au

नियम

  • यह कोड-गोल्फ है। सबसे छोटा कोड जीतता है।
  • आपका कार्यक्रम सभी उचित इनपुट के लिए उचित समय में समाप्त होना चाहिए।

परीक्षण के मामलों

प्रत्येक परीक्षण मामले में दो लाइनें होती हैं: एक इनपुट और एक सही आउटपुट।

C7H16+O2->CO2+H2O
C7H16+11O2->7CO2+8H2O

Al+Fe2O3->Fe+Al2O3
2Al+Fe2O3->2Fe+Al2O3

Pb->Au
Nope!

1
मैं गलत हो सकता है, लेकिन यह एक प्रोग्रामिंग चुनौती के लिए एक प्राकृतिक उम्मीदवार की तरह लगता है बल्कि कोड गोल्फ।
2

1
मैंने एक बार अपने TI-89 ग्राफिंग कैलकुलेटर पर एक रासायनिक समीकरण सॉल्वर लिखा था, बिल्ट-इन solve(फ़ंक्शन का उपयोग करके और eval(इनपुट की व्याख्या करने के लिए :)
mellamokb

3
@ मल्लमोकब आप इसे पोस्ट क्यों नहीं करते हैं, आपको मौलिकता के लिए मुझ से एक उत्थान मिलेगा
शाफ़्ट फ्रीक

5
"चूंकि आप बर्नड्स ट्यूटर हैं, इसलिए उसकी मदद करना आपका काम है!" - मैंने सोचा होगा कि एक ट्यूटर को बर्न को खुद के लिए सोचने के लिए पढ़ाना चाहिए, बजाय उसके लिए सॉफ्टवेयर लिखने के लिए इसलिए उसे करने की ज़रूरत नहीं है: P
naught101

1
@KuilinLi यह गलत नहीं है, बस अलग है।
फ़ूजएक्सएक्सएल

जवाबों:


7

C, 442 505 वर्ण

// element use table, then once parsed reused as molecule weights
u,t[99];

// molecules
char*s,*m[99]; // name and following separator
c,v[99][99]; // count-1, element vector

i,j,n;

// brute force solver, n==0 upon solution - assume at most 30 of each molecule
b(k){
    if(k<0)for(n=j=0;!n&&j<u;j++)for(i=0;i<=c;i++)n+=t[i]*v[i][j]; // check if sums to zero
    else for(t[k]=0;n&&t[k]++<30;)b(k-1); // loop through all combos of weights
}

main(int r,char**a){
    // parse
    for(s=m[0]=a[1];*s;){
        // parse separator, advance next molecule
        if(*s==45)r=0,s++;
        if(*s<65)m[++c]=++s;
        // parse element
        j=*s++;
        if(*s>96)j=*s+++j<<8;            
        // lookup element index
        for(i=0,t[u]=j;t[i]-j;i++);
        u+=i==u;
        // parse amount
        for(n=0;*s>>4==3;)n=n*10+*s++-48;
        n+=!n;
        // store element count in molecule vector, flip sign for other side of '->'
        v[c][i]=r?n:-n;
    }
    // solve
    b(c);
    // output
    for(i=0,s=n?"Nope!":a[1];*s;putchar(*s++))s==m[i]&&t[i++]>1?printf("%d",t[i-1]):0;
    putchar(10);
}

ऐसे दोड़ो:

./a.out "C7H16+O2->CO2+H2O"
./a.out "Al+Fe2O4->Fe+Al2O3"
./a.out "Pb->Au"

परिणाम:

C7H16+11O2->7CO2+8H2O
8Al+3Fe2O4->6Fe+4Al2O3
Nope!

+1 यह राष्ट्रपति की तुलना में बहुत अधिक सम्मानजनक है। बहस
ardnew

2
घुंघराले ब्रेस से बचने के लिए स्टेटमेंट सेपरेटर के रूप में कॉमा का उपयोग करने का प्रयास करें। कोड को छोटा करने के लिए टर्नरी ऑपरेटरों के साथ तत्कालीन-और-निर्माण का स्थान लेने का प्रयास करें। टी [मैं]> 1 printf (टी "% s", [i]): 0;? एक बाइट कम है इसके अलावा: m [0] * m के समान है।
फ़ूजक्कल

6

गणितज्ञ ५०7

मैंने इसमें वर्णित संवर्धित रासायनिक संरचना मैट्रिक्स दृष्टिकोण को नियोजित किया

LRThorne, रासायनिक-प्रतिक्रिया समीकरणों को संतुलित करने के लिए एक नवीन दृष्टिकोण: एक सरल मैट्रिक्स - मैट्रिक्स नल अंतरिक्ष का निर्धारण करने के लिए उलटा तकनीक। केम्यूड्यूटर , 2010, 15, 304 - 308।

एक मामूली मोड़ जोड़ा गया था: मैंने किसी भी समाधान में पूर्णांक मान सुनिश्चित करने के लिए तत्वों के सबसे बड़े सामान्य भाजक द्वारा नल-अंतरिक्ष वेक्टर के पारगमन को विभाजित किया। मेरा कार्यान्वयन अभी तक उन मामलों को नहीं संभालता है जहां समीकरण को संतुलित करने के लिए एक से अधिक समाधान हैं।

b@t_ :=Quiet@Check[Module[{s = StringSplit[t, "+" | "->"], g = StringCases, k = Length, 
  e, v, f, z, r},
e = Union@Flatten[g[#, _?UpperCaseQ ~~ ___?LowerCaseQ] & /@ s];v = k@e;
s_~f~e_ := If[g[s, e] == {}, 0, If[(r = g[s, e ~~ p__?DigitQ :> p]) == {}, 1, 
   r /. {{x_} :> ToExpression@x}]];z = k@s - v;
r = #/(GCD @@ #) &[Inverse[Join[SparseArray[{{i_, j_} :> f[s[[j]], e[[i]]]}, k /@ {e, s}], 
Table[Join[ConstantArray[0, {z, v}][[i]], #[[i]]], {i, k[#]}]]][[All, -1]] &
   [IdentityMatrix@z]];
Row@Flatten[ReplacePart[Riffle[Partition[Riffle[Abs@r, s], 2], " + "], 
   2 Count[r, _?Negative] -> " -> "]]], "Nope!"]

टेस्ट

b["C7H16+O2->CO2+H2O"]
b["Al+Fe2O3->Fe+Al2O3"]
b["Pb->Au"]

यहाँ छवि विवरण दर्ज करें

विश्लेषण

यह निम्नलिखित रासायनिक संरचना तालिका स्थापित करके काम करता है, तत्वों द्वारा रासायनिक प्रजातियों से मिलकर, जिसमें एक अतिरिक्त शून्य वेक्टर जोड़ा जाता है (संवर्धित रासायनिक संरचना तालिका बन जाती है:

रासायनिक संरचना तालिका

आंतरिक कोशिकाओं को एक मैट्रिक्स के रूप में हटा दिया जाता है और उलटा, उपज होता है।

उलट देना

सही-सबसे कॉलम निकाला जाता है, उपज:

{(1/8), - (11/8), 7/8, 1}

वेक्टर में प्रत्येक तत्व को तत्वों के gcd (1/8) से विभाजित किया गया है:

{-1, -11, 7, 8}

जहां तीर के बाईं ओर नकारात्मक मान रखे जाएंगे। मूल समीकरण को संतुलित करने के लिए आवश्यक पूर्ण संख्याएं इनमें से हैं:

समाधान


विस्मयादिबोधक बिंदु जोड़ने के लिए मत भूलना!
१ .:

:} ठीक है, और मैंने चरित्र गणना को
छीन लिया

मुझे लगता है कि आप दाएं हाथ के कॉलम का मतलब है, बाएं हाथ का नहीं। मैं स्पष्टीकरण (+1) की सराहना करता हूं, लेकिन मुझे आश्चर्य है कि अगर यह मामला नहीं था कि अणुओं की संख्या तत्वों की संख्या से एक अधिक है, तो आप कैसे पैड करते हैं? अब कागज पढ़ने के लिए बंद।
पीटर टेलर

किसी कारण से, मैं केवल आज ही आपकी टिप्पणी पर आया हूं। हां, मेरा मतलब "राइट-हैंड कॉलम" था। इतना समय बीत चुका है जब मैंने इस पर काम किया है कि मैं देख (या याद नहीं) कर सकता हूं जहां पैडिंग का उपयोग किया जाता है। माफ़ कीजिये।
डेविड जेसी

3

पायथन, 880 वर्ण

import sys,re
from sympy.solvers import solve
from sympy import Symbol
from fractions import gcd
from collections import defaultdict

Ls=list('abcdefghijklmnopqrstuvwxyz')
eq=sys.argv[1]
Ss,Os,Es,a,i=defaultdict(list),Ls[:],[],1,1
for p in eq.split('->'):
 for k in p.split('+'):
  c = [Ls.pop(0), 1]
  for e,m in re.findall('([A-Z][a-z]?)([0-9]*)',k):
   m=1 if m=='' else int(m)
   a*=m
   d=[c[0],c[1]*m*i]
   Ss[e][:0],Es[:0]=[d],[[e,d]]
 i=-1
Ys=dict((s,eval('Symbol("'+s+'")')) for s in Os if s not in Ls)
Qs=[eval('+'.join('%d*%s'%(c[1],c[0]) for c in Ss[s]),{},Ys) for s in Ss]+[Ys['a']-a]
k=solve(Qs,*Ys)
if k:
 N=[k[Ys[s]] for s in sorted(Ys)]
 g=N[0]
 for a1, a2 in zip(N[0::2],N[1::2]):g=gcd(g,a2)
 N=[i/g for i in N]
 pM=lambda c: str(c) if c!=1 else ''
 print '->'.join('+'.join(pM(N.pop(0))+str(t) for t in p.split('+')) for p in eq.split('->'))
else:print 'Nope!'

टेस्ट:

python chem-min.py "C7H16+O2->CO2+H2O"
python chem-min.py "Al+Fe2O4->Fe+Al2O3"
python chem-min.py "Pb->Au"

आउटपुट:

C7H16+11O2->7CO2+8H2O
8Al+3Fe2O4->6Fe+4Al2O3
Nope!

880 से बहुत कम हो सकता है, लेकिन मेरी आँखें मुझे पहले से ही मार रही हैं ...


2

पायथन 2, 635 बाइट्स

पिछली बाइट मायने रखती है: 794, 776, 774, 765, 759, 747, 735, 734, 720, 683, 658, 655, 654, 653, 651, 638, 637, 636 बाइट्स।

दूसरा इंडेंटेशन स्तर केवल एक टैब है, तीसरा एक टैब है और फिर एक स्पेस है।

सच कहूँ तो, यह jadkik94 का जवाब है, लेकिन इतने सारे बाइट्स मुंडा थे, मुझे यह करना था। मुझे बताओ अगर मैं किसी भी बाइट्स दाढ़ी कर सकते हैं!

from sympy import*
import sys,re
from sympy.solvers import*
from collections import*
P=str.split
L=map(chr,range(97,123))
q=sys.argv[1]
S,O,a,i,u,v=defaultdict(list),L[:],1,1,'+','->'
w=u.join
for p in P(q,v):
 for k in P(p,u):
     c=L.pop(0)
     for e,m in re.findall('([A-Z][a-z]*)(\d*)',k):
      m=int(m or 1)
      a*=m
      S[e][:0]=[c,m*i],
 i=-1
Y=dict((s,Symbol(s))for s in set(O)-set(L))
Q=[eval(w('%d*%s'%(c[1],c[0])for c in S[s]),{},Y)for s in S]+[Y['a']-a]
k=solve(Q,*Y)
if k:
 N=[k[Y[s]]for s in sorted(Y)]
 g=gcd(N[:1]+N[1::2])
 print v.join(w((lambda c:str(c)*(c!=1))(N.pop(0)/g)+str(t)for t in P(p,u))for p in P(q,v))
else:print'Nope!'

तीन बाइट्स ''.join(map(chr,range(97,122)))
बचाएं

:(, यह काम नहीं करता है। हालांकि, map(chr,range(97,123))12 बाइट्स के लिए काम करता है बचाया।
जकार्ही

अरे हाँ! यह अजगर 2 है!
एलियाकंडिल

1

जावास्क्रिप्ट, 682 बाइट्स

x=>{m=1;x.split(/\D+/g).map(i=>i?m*=i:0);e=new Set(x.replace(/\d+|\+|->/g,"").match(/([A-Z][a-z]*)/g));e.delete``;A=[];for(let z of e){t=x.split`->`;u=[];for(c=1;Q=t.shift();c=-1)Q.split`+`.map(p=>u.push(c*((i=p.indexOf(z))==-1?0:(N=p.substring(i+z.length).match(/^\d+/g))?N[0]:1)));A.push(u)}J=A.length;for(P=0;P<J;P++){for(i=P;!A[i][P];i++);W=A.splice(i,1)[0];W=W.map(t=>t*m/W[P]);A=A.map(r=>r[P]?r.map((t,j)=>t-W[j]*r[P]/m):r);A.splice(P,0,W)}f=e.size;if(!A[0][f])return"Nope!";g=m=-m;_=(a,b)=>b?_(b,a%b):a;c=[];A.map(p=>c.push(t=p.pop())&(g=_(g,t)));c.push(m);j=x.match(/[^+>]+/g);return c.map(k=>k/g).map(t=>(t^1?t:"")+(z=j.shift())+(z.endsWith`-`?">":"+")).join``.slice(0,-1);}

यह कुइलिन के उत्तर का एक बहुत अधिक गोल्फ (दशकों का चरित्र) है। हो सकता है कि गैरकानूनी हो क्योंकि कुछ जेएस सुविधाएँ चुनौती को स्थगित करती हैं।


0

जावास्क्रिप्ट, 705 बाइट्स

(गैर-प्रतिस्पर्धात्मक, कुछ विशेषताएं चुनौती को स्थगित करती हैं)

अन्य समाधानों में सभी में पशु-पालन के तत्व थे। मैंने रेखीय समीकरणों के एक सेट के रूप में रासायनिक समीकरण का प्रतिनिधित्व करते हुए एक अधिक निर्धारक दृष्टिकोण के लिए प्रयास किया, और फिर उस मैट्रिक्स के कम पंक्ति-इकोलोन रूप को लेने के लिए गॉस-जॉर्डन एल्गोरिथ्म का उपयोग करके हल किया। तुच्छ मामले को अलग करने के लिए जहां सब कुछ शून्य है, मेरा मानना ​​है कि तत्वों में से एक एक निरंतर संख्या है - और यह संख्या केवल सभी संख्याओं को एक साथ गुणा करके निर्धारित की जाती है, ताकि अंश न हो। फिर अंतिम चरण के रूप में हम अंतिम शर्त को पूरा करने के लिए प्रत्येक को gcd से विभाजित करेंगे।

Ungolfed:

function solve(x) {
	//firstly we find bigNumber, which will be all numbers multiplied together, in order to assume the last element is a constant amount of that
	bigNumber = 1;
	arrayOfNumbers = new Set(x.split(/\D+/g));
	arrayOfNumbers.delete("");
	for (let i of arrayOfNumbers) bigNumber *= parseInt(i);
	
	//first actual step, we split into left hand side and right hand side, and then into separate molecules
	//number of molecules is number of variables, number of elements is number of equations, variables refer to the coefficients of the chemical equation
	//note, the structure of this is changed a lot in the golfed version since right is the same as negative left
	left = x.split("->")[0].split("+");
	righ = x.split("->")[1].split("+");
	molecules = left.length + righ.length;
	
	//then let's find what elements there are - this will also become how many equations we have, or the columns of our matrix minus one
	//we replace all the non-element characters, and then split based on the uppercase characters
	//this also sometimes adds a "" to the array, we don't need that so we just delete it
	//turn into a set in order to remove repeats
	elems = new Set(x.replace(/\d+|\+|->/g,"").match(/([A-Z][a-z]*)/g));
	elems.delete("");
	
	rrefArray = [];//first index is rows, second index columns - each row is an equation x*(A11)+y*(A21)+z*(A31)=A41 etc etc, to solve for xyz as coefficients
	//loop thru the elements, since for each element we'll have an equation, or a row in the array
	for (let elem of elems) {
		buildArr = [];
		//loop thru the sides
		for (let molecule of left) {
			//let's see how many of element elem are in molecule molecule
			//ASSUMPTION: each element happens only once per molecule (no shenanigans like CH3COOH)
			index = molecule.indexOf(elem);
			if (index == -1) buildArr.push(0);
			else {
				index += elem.length;
				numberAfterElement = molecule.substring(index).match(/^\d+/g);
				if (numberAfterElement == null) buildArr.push(1);
				else buildArr.push(parseInt(numberAfterElement));
			}
		}
		//same for right, except each item is negative
		for (let molecule of righ) {
			index = molecule.indexOf(elem);
			if (index == -1) buildArr.push(0);
			else {
				index += elem.length;
				numberAfterElement = molecule.substring(index).match(/^\d+/g);
				if (numberAfterElement == null) buildArr.push(-1);
				else buildArr.push(parseInt(numberAfterElement)*(-1));
			}
		}
		rrefArray.push(buildArr);
	}
	
	//Gauss-Jordan algorithm starts here, on rrefArray
	for (pivot=0;pivot<Math.min(molecules, elems.size);pivot++) {
		//for each pivot element, first we search for a row in which the pivot is nonzero
		//this is guaranteed to exist because there are no empty molecules
		for (i=pivot;i<rrefArray.length;i++) {
			row = rrefArray[i];
			if (row[pivot] != 0) {
				workingOnThisRow = rrefArray.splice(rrefArray.indexOf(row), 1)[0];
			}
		}
		//then multiply elements so the pivot element of workingOnThisRow is equal to bigNumber we determined above, this is all to keep everything in integer-space
		multiplyWhat = bigNumber / workingOnThisRow[pivot]
		for (i=0;i<workingOnThisRow.length;i++) workingOnThisRow[i] *= multiplyWhat
		//then we make sure the other rows don't have this column as a number, the other rows have to be zero, if not we can normalize to bigNumber and subtract
		for (let i in rrefArray) {
			row = rrefArray[i];
			if (row[pivot] != 0) {
				multiplyWhat = bigNumber / row[pivot]
				for (j=0;j<row.length;j++) {
					row[j] *= multiplyWhat;
					row[j] -= workingOnThisRow[j];
					row[j] /= multiplyWhat;
				}
				rrefArray[i]=row;
			}
		}
		//finally we put the row back
		rrefArray.splice(pivot, 0, workingOnThisRow);
	}
	
	//and finally we're done!
	//sanity check to make sure it succeeded, if not then the matrix is insolvable
	if (rrefArray[0][elems.size] == 0 || rrefArray[0][elems.size] == undefined) return "Nope!";
	
	//last step - get the results of the rref, which will be the coefficients of em except for the last one, which would be bigNumber (1 with typical implementation of the algorithm)
	bigNumber *= -1;
	gcd_calc = function(a, b) {
		if (!b) return a;
		return gcd_calc(b, a%b);
	};
	coEffs = [];
	gcd = bigNumber;
	for (i=0;i<rrefArray.length;i++) {
		num = rrefArray[i][molecules-1];
		coEffs.push(num);
		gcd = gcd_calc(gcd, num)
	}
	coEffs.push(bigNumber);
	for (i=0;i<coEffs.length;i++) coEffs[i] /= gcd;
	
	//now we make it human readable
	//we have left and right from before, let's not forget those!
	out = "";
	for (i=0;i<coEffs.length;i++) {
		coEff = coEffs[i];
		if (coEff != 1) out += coEff;
		out += left.shift();
		if (left.length == 0 && righ.length != 0) {
			out += "->";
			left = righ;
		} else if (i != coEffs.length-1) out += "+";
	}
	return out;
}
console.log(solve("Al+Fe2O4->Fe+Al2O3"));
console.log(solve("Al+Fe2O3->Fe+Al2O3"));
console.log(solve("C7H16+O2->CO2+H2O"));
console.log(solve("Pb->Au"));

golfed

s=x=>{m=1;x.split(/\D+/g).map(i=>i!=""?m*=i:0);e=(new Set(x.replace(/\d+|\+|->/g,"").match(/([A-Z][a-z]*)/g)));e.delete("");A=[];for(let z of e){t=x.split("->");u=[];for(c=1;Q=t.shift();c=-1)Q.split("+").map(p=>u.push(c*((i=p.indexOf(z))==-1?0:(N=p.substring(i+z.length).match(/^\d+/g))?N[0]:1)));A.push(u)}J=A.length;for(P=0;P<J;P++){for(i=P;!A[i][P];i++);W=A.splice(i,1)[0];W=W.map(t=>t*m/W[P]);A=A.map(r=>!r[P]?r:r.map((t,j)=>t-W[j]*r[P]/m));A.splice(P,0,W)}f=e.size;if (!A[0][f])return "Nope!";g=m=-m;_=(a,b)=>b?_(b,a%b):a;c=[];A.map(p=>c.push(t=p.pop())&(g=_(g,t)));c.push(m);j=x.match(/[^+>]+/g);return c.map(k=>k/g).map(t=>(t==1?"":t)+(z=j.shift())+(z.endsWith("-")?">":"+")).join("").slice(0,-1);}

console.log(s("Al+Fe2O4->Fe+Al2O3"));
console.log(s("Al+Fe2O3->Fe+Al2O3"));
console.log(s("C7H16+O2->CO2+H2O"));
console.log(s("Pb->Au"));


1
कुछ सुविधाओं को चुनौती के रूप में नॉनकंपेटिंग।
जकारि

अरे वाह मैंने नोटिस नहीं किया कि यह कितना पुराना था। धन्यवाद!
कुइलिन ली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.