Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2015-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "Colvar.h"
23 : #include "core/PlumedMain.h"
24 : #include "core/ActionRegister.h"
25 : #include "tools/Torsion.h"
26 :
27 : namespace PLMD {
28 : namespace colvar {
29 :
30 : //+PLUMEDOC COLVAR PUCKERING
31 : /*
32 : Calculate sugar pseudorotation coordinates.
33 :
34 : This command can be used to calculate pseudorotations for the rings in sugars (puckers). It works for both
35 : 5-membered and 6-membered rings. Notice that there are two different implementations depending if
36 : one passes 5 or 6 atoms in the ATOMS keyword. This input tells plumed to print the puckering phase angle of the
37 : second nucleotide of a RNA molecule on file COLVAR.
38 :
39 : ```plumed
40 : #SETTINGS MOLFILE=regtest/basic/rt65/AA.pdb
41 : MOLINFO STRUCTURE=regtest/basic/rt65/AA.pdb MOLTYPE=rna
42 : puck: PUCKERING ATOMS=@sugar-2
43 : PRINT ARG=puck.phs FILE=COLVAR
44 : ```
45 :
46 : For 5-membered rings the implementation is the one discussed in the first of the papers in the bibliography below.
47 : This implementation is simple and can be used in RNA to distinguish C2'-endo and C3'-endo conformations.
48 : Both the polar coordinates (phs and amp) and the Cartesian coordinates (Zx and Zy) are provided.
49 : C2'-endo conformations have negative Zx, whereas C3'-endo conformations have positive Zy.
50 : The notation is consistent with the notation in that first paper.
51 : The five atoms should be provided as C4',O4',C1',C2',C3'.
52 : Notice that this is the same order that can be obtained using the [MOLINFO](MOLINFO.md) syntax (see example below).
53 :
54 : For 6-membered rings the implementation is the general Cremer-Pople one that is discussed in the second and third
55 : papers in the bibliography.
56 : This implementation provides both a triplet with Cartesian components (qx, qy, and qz)
57 : and a triplet of polar components (amplitude, phi, and theta).
58 : Applications of this particular implementation are yet to be published (paper in preparation).
59 :
60 : > [!NOTE]
61 : > The 6-membered ring implementation distributed with previous versions of PLUMED lead to
62 : > qx and qy values that had an opposite sign with respect to those originally defined in the
63 : > thid reference in the bibliograph below. The bug was fixed in version 2.5.
64 :
65 : */
66 : //+ENDPLUMEDOC
67 :
68 : class Puckering : public Colvar {
69 :
70 : public:
71 : explicit Puckering(const ActionOptions&);
72 : void calculate() override;
73 : static void registerKeywords(Keywords& keys);
74 : void calculate5m();
75 : void calculate6m();
76 : };
77 :
78 : PLUMED_REGISTER_ACTION(Puckering,"PUCKERING")
79 :
80 58 : void Puckering::registerKeywords(Keywords& keys) {
81 58 : Colvar::registerKeywords( keys );
82 116 : keys.remove("NOPBC");
83 58 : keys.add("atoms","ATOMS","the five or six atoms of the sugar ring in the proper order");
84 116 : keys.addOutputComponent("phs","default","scalar","Pseudorotation phase (5 membered rings)");
85 116 : keys.addOutputComponent("amp","default","scalar","Pseudorotation amplitude (5 membered rings)");
86 116 : keys.addOutputComponent("Zx","default","scalar","Pseudorotation x Cartesian component (5 membered rings)");
87 116 : keys.addOutputComponent("Zy","default","scalar","Pseudorotation y Cartesian component (5 membered rings)");
88 116 : keys.addOutputComponent("phi","default","scalar","Pseudorotation phase (6 membered rings)");
89 116 : keys.addOutputComponent("theta","default","scalar","Theta angle (6 membered rings)");
90 116 : keys.addOutputComponent("amplitude","default","scalar","Pseudorotation amplitude (6 membered rings)");
91 116 : keys.addOutputComponent("qx","default","scalar","Cartesian component x (6 membered rings)");
92 116 : keys.addOutputComponent("qy","default","scalar","Cartesian component y (6 membered rings)");
93 116 : keys.addOutputComponent("qz","default","scalar","Cartesian component z (6 membered rings)");
94 58 : keys.addDOI("10.1021/ct401013s");
95 58 : keys.addDOI("10.1021/ja00839a011");
96 58 : keys.addDOI("10.1021/ja068411o");
97 58 : }
98 :
99 56 : Puckering::Puckering(const ActionOptions&ao):
100 56 : PLUMED_COLVAR_INIT(ao) {
101 : std::vector<AtomNumber> atoms;
102 112 : parseAtomList("ATOMS",atoms);
103 56 : if(atoms.size()!=5 && atoms.size()!=6) {
104 1 : error("only for 5 or 6-membered rings");
105 : }
106 55 : checkRead();
107 :
108 55 : if(atoms.size()==5) {
109 54 : log.printf(" between atoms %d %d %d %d %d\n",atoms[0].serial(),atoms[1].serial(),atoms[2].serial(),atoms[3].serial(),atoms[4].serial());
110 1 : } else if(atoms.size()==6) {
111 1 : log.printf(" between atoms %d %d %d %d %d %d\n",atoms[0].serial(),atoms[1].serial(),atoms[2].serial(),atoms[3].serial(),atoms[4].serial(),atoms[5].serial());
112 : } else {
113 0 : error("ATOMS should specify 5 atoms");
114 : }
115 :
116 55 : if(atoms.size()==5) {
117 108 : addComponentWithDerivatives("phs");
118 108 : componentIsPeriodic("phs","-pi","pi");
119 108 : addComponentWithDerivatives("amp");
120 108 : componentIsNotPeriodic("amp");
121 108 : addComponentWithDerivatives("Zx");
122 108 : componentIsNotPeriodic("Zx");
123 108 : addComponentWithDerivatives("Zy");
124 108 : componentIsNotPeriodic("Zy");
125 1 : } else if(atoms.size()==6) {
126 2 : addComponentWithDerivatives("qx");
127 2 : componentIsNotPeriodic("qx");
128 2 : addComponentWithDerivatives("qy");
129 2 : componentIsNotPeriodic("qy");
130 2 : addComponentWithDerivatives("qz");
131 2 : componentIsNotPeriodic("qz");
132 2 : addComponentWithDerivatives("phi");
133 2 : componentIsPeriodic("phi","0","2pi");
134 2 : addComponentWithDerivatives("theta");
135 2 : componentIsNotPeriodic("theta");
136 2 : addComponentWithDerivatives("amplitude");
137 2 : componentIsNotPeriodic("amplitude");
138 : }
139 :
140 55 : log<<" Bibliography ";
141 55 : if(atoms.size()==5) {
142 108 : log<<plumed.cite("Huang, Giese, Lee, York, J. Chem. Theory Comput. 10, 1538 (2014)");
143 : }
144 55 : if(atoms.size()==6) {
145 3 : log<<plumed.cite("Cremer and Pople, J. Am. Chem. Soc. 97, 1354 (1975)");
146 : }
147 55 : log<<"\n";
148 :
149 55 : requestAtoms(atoms);
150 57 : }
151 :
152 : // calculator
153 397 : void Puckering::calculate() {
154 397 : makeWhole();
155 397 : if(getNumberOfAtoms()==5) {
156 294 : calculate5m();
157 : } else {
158 103 : calculate6m();
159 : }
160 397 : }
161 :
162 294 : void Puckering::calculate5m() {
163 :
164 294 : Vector d0,d1,d2,d3,d4,d5;
165 :
166 294 : d0=delta(getPosition(2),getPosition(1));
167 294 : d1=delta(getPosition(3),getPosition(2));
168 294 : d2=delta(getPosition(4),getPosition(3));
169 294 : d3=delta(getPosition(4),getPosition(3));
170 294 : d4=delta(getPosition(0),getPosition(4));
171 294 : d5=delta(getPosition(1),getPosition(0));
172 :
173 294 : Vector dd0,dd1,dd2,dd3,dd4,dd5;
174 :
175 : PLMD::Torsion t;
176 :
177 294 : double v1=t.compute(d0,d1,d2,dd0,dd1,dd2);
178 294 : double v3=t.compute(d3,d4,d5,dd3,dd4,dd5);
179 :
180 294 : double Zx=(v1+v3)/(2.0*std::cos(4.0*pi/5.0));
181 294 : double Zy=(v1-v3)/(2.0*std::sin(4.0*pi/5.0));
182 294 : double phase=std::atan2(Zy,Zx);
183 294 : double amplitude=std::sqrt(Zx*Zx+Zy*Zy);
184 :
185 1764 : Vector dZx_dR[5];
186 1764 : Vector dZy_dR[5];
187 :
188 294 : dZx_dR[0]=(dd5-dd4);
189 294 : dZx_dR[1]=(dd0-dd5);
190 294 : dZx_dR[2]=(dd1-dd0);
191 294 : dZx_dR[3]=(dd2+dd3-dd1);
192 294 : dZx_dR[4]=(dd4-dd3-dd2);
193 :
194 294 : dZy_dR[0]=(dd4-dd5);
195 294 : dZy_dR[1]=(dd0+dd5);
196 294 : dZy_dR[2]=(dd1-dd0);
197 294 : dZy_dR[3]=(dd2-dd3-dd1);
198 294 : dZy_dR[4]=(dd3-dd4-dd2);
199 :
200 1764 : for(unsigned j=0; j<5; j++) {
201 1470 : dZx_dR[j]*=(1.0/(2.0*std::cos(4.0*pi/5.0)));
202 : }
203 1764 : for(unsigned j=0; j<5; j++) {
204 1470 : dZy_dR[j]*=(1.0/(2.0*std::sin(4.0*pi/5.0)));
205 : }
206 :
207 1764 : Vector dphase_dR[5];
208 1764 : for(unsigned j=0; j<5; j++) {
209 1470 : dphase_dR[j]=(1.0/(Zx*Zx+Zy*Zy))*(-Zy*dZx_dR[j] + Zx*dZy_dR[j]);
210 : }
211 :
212 1764 : Vector damplitude_dR[5];
213 1764 : for(unsigned j=0; j<5; j++) {
214 1470 : damplitude_dR[j]=(1.0/amplitude)*(Zx*dZx_dR[j] + Zy*dZy_dR[j]);
215 : }
216 :
217 588 : Value* vzx=getPntrToComponent("Zx");
218 : vzx->set(Zx);
219 294 : setAtomsDerivatives (vzx,0, dZx_dR[0]);
220 294 : setAtomsDerivatives (vzx,1, dZx_dR[1]);
221 294 : setAtomsDerivatives (vzx,2, dZx_dR[2]);
222 294 : setAtomsDerivatives (vzx,3, dZx_dR[3]);
223 294 : setAtomsDerivatives (vzx,4, dZx_dR[4]);
224 294 : setBoxDerivativesNoPbc(vzx);
225 :
226 588 : Value* vzy=getPntrToComponent("Zy");
227 : vzy->set(Zy);
228 294 : setAtomsDerivatives (vzy,0, dZy_dR[0]);
229 294 : setAtomsDerivatives (vzy,1, dZy_dR[1]);
230 294 : setAtomsDerivatives (vzy,2, dZy_dR[2]);
231 294 : setAtomsDerivatives (vzy,3, dZy_dR[3]);
232 294 : setAtomsDerivatives (vzy,4, dZy_dR[4]);
233 294 : setBoxDerivativesNoPbc(vzy);
234 :
235 :
236 588 : Value* vph=getPntrToComponent("phs");
237 : vph->set(phase);
238 294 : setAtomsDerivatives (vph,0, dphase_dR[0]);
239 294 : setAtomsDerivatives (vph,1, dphase_dR[1]);
240 294 : setAtomsDerivatives (vph,2, dphase_dR[2]);
241 294 : setAtomsDerivatives (vph,3, dphase_dR[3]);
242 294 : setAtomsDerivatives (vph,4, dphase_dR[4]);
243 294 : setBoxDerivativesNoPbc(vph);
244 :
245 588 : Value* vam=getPntrToComponent("amp");
246 : vam->set(amplitude);
247 294 : setAtomsDerivatives (vam,0, damplitude_dR[0]);
248 294 : setAtomsDerivatives (vam,1, damplitude_dR[1]);
249 294 : setAtomsDerivatives (vam,2, damplitude_dR[2]);
250 294 : setAtomsDerivatives (vam,3, damplitude_dR[3]);
251 294 : setAtomsDerivatives (vam,4, damplitude_dR[4]);
252 294 : setBoxDerivativesNoPbc(vam);
253 :
254 :
255 294 : }
256 :
257 103 : void Puckering::calculate6m() {
258 :
259 103 : std::vector<Vector> r(6);
260 721 : for(unsigned i=0; i<6; i++) {
261 618 : r[i]=getPosition(i);
262 : }
263 :
264 103 : std::vector<Vector> R(6);
265 103 : Vector center;
266 721 : for(unsigned j=0; j<6; j++) {
267 618 : center+=r[j]/6.0;
268 : }
269 721 : for(unsigned j=0; j<6; j++) {
270 618 : R[j]=(r[j]-center);
271 : }
272 :
273 103 : Vector Rp,Rpp;
274 721 : for(unsigned j=0; j<6; j++) {
275 618 : Rp +=R[j]*std::sin(2.0/6.0*pi*j);
276 : }
277 721 : for(unsigned j=0; j<6; j++) {
278 618 : Rpp+=R[j]*std::cos(2.0/6.0*pi*j);
279 : }
280 :
281 103 : Vector n=crossProduct(Rp,Rpp);
282 103 : Vector nhat=n/modulo(n);
283 :
284 103 : Tensor dn_dRp=dcrossDv1(Rp,Rpp);
285 103 : Tensor dn_dRpp=dcrossDv2(Rp,Rpp);
286 :
287 103 : Tensor dnhat_dn= 1.0/modulo(n)*( Tensor::identity() - extProduct(nhat,nhat));
288 103 : Tensor dnhat_dRp=matmul(dnhat_dn,dn_dRp);
289 103 : Tensor dnhat_dRpp=matmul(dnhat_dn,dn_dRpp);
290 :
291 103 : std::vector<double> z(6);
292 721 : for(unsigned j=0; j<6; j++) {
293 618 : z[j]=dotProduct(R[j],nhat);
294 : }
295 :
296 103 : std::vector<std::vector<Vector> > dz_dR(6);
297 721 : for(unsigned j=0; j<6; j++) {
298 618 : dz_dR[j].resize(6);
299 : }
300 :
301 721 : for(unsigned i=0; i<6; i++)
302 4326 : for(unsigned j=0; j<6; j++) {
303 3708 : if(i==j) {
304 618 : dz_dR[i][j]+=nhat;
305 : }
306 3708 : dz_dR[i][j]+=matmul(R[i],dnhat_dRp)*std::sin(2.0/6.0*pi*j);
307 3708 : dz_dR[i][j]+=matmul(R[i],dnhat_dRpp)*std::cos(2.0/6.0*pi*j);
308 : }
309 :
310 : double B=0.0;
311 721 : for(unsigned j=0; j<6; j++) {
312 618 : B+=z[j]*std::cos(4.0/6.0*pi*j);
313 : }
314 :
315 103 : std::vector<Vector> dB_dR(6);
316 721 : for(unsigned i=0; i<6; i++)
317 4326 : for(unsigned j=0; j<6; j++) {
318 3708 : dB_dR[i]+=dz_dR[j][i]*std::cos(4.0/6.0*pi*j);
319 : }
320 103 : Vector Bsum;
321 721 : for(unsigned j=0; j<6; j++) {
322 618 : Bsum+=dB_dR[j];
323 : }
324 721 : for(unsigned j=0; j<6; j++) {
325 618 : dB_dR[j]-=Bsum/6.0;
326 : };
327 :
328 : double A=0.0;
329 721 : for(unsigned j=0; j<6; j++) {
330 618 : A+=z[j]*std::sin(4.0/6.0*pi*j);
331 : }
332 :
333 103 : std::vector<Vector> dA_dR(6);
334 721 : for(unsigned i=0; i<6; i++)
335 4326 : for(unsigned j=0; j<6; j++) {
336 3708 : dA_dR[i]+=dz_dR[j][i]*std::sin(4.0/6.0*pi*j);
337 : }
338 103 : Vector Asum;
339 721 : for(unsigned j=0; j<6; j++) {
340 618 : Asum+=dA_dR[j];
341 : }
342 721 : for(unsigned j=0; j<6; j++) {
343 618 : dA_dR[j]-=Asum/6.0;
344 : };
345 :
346 : double C=0.0;
347 721 : for(unsigned j=0; j<6; j++) {
348 618 : C+=z[j]*Tools::fastpow(-1.0,(j));
349 : }
350 :
351 103 : std::vector<Vector> dC_dR(6);
352 721 : for(unsigned i=0; i<6; i++)
353 4326 : for(unsigned j=0; j<6; j++) {
354 3708 : dC_dR[i]+=dz_dR[j][i]*Tools::fastpow(-1.0,(j));
355 : }
356 :
357 103 : Vector Csum;
358 721 : for(unsigned j=0; j<6; j++) {
359 618 : Csum+=dC_dR[j];
360 : }
361 721 : for(unsigned j=0; j<6; j++) {
362 618 : dC_dR[j]-=Csum/6.0;
363 : };
364 :
365 :
366 : // qx
367 103 : double qx = -A/std::sqrt(3);
368 :
369 : // qx derivaties
370 103 : std::vector<Vector> dqx_dR(6);
371 721 : for(unsigned j=0; j<6; j++) {
372 618 : dqx_dR[j]=-1/std::sqrt(3) * dA_dR[j];
373 : }
374 :
375 206 : Value* vqx=getPntrToComponent("qx");
376 : vqx->set(qx);
377 103 : setAtomsDerivatives (vqx,0, dqx_dR[0] );
378 103 : setAtomsDerivatives (vqx,1, dqx_dR[1] );
379 103 : setAtomsDerivatives (vqx,2, dqx_dR[2] );
380 103 : setAtomsDerivatives (vqx,3, dqx_dR[3] );
381 103 : setAtomsDerivatives (vqx,4, dqx_dR[4] );
382 103 : setAtomsDerivatives (vqx,5, dqx_dR[5] );
383 103 : setBoxDerivativesNoPbc(vqx);
384 :
385 : // qy
386 103 : double qy = B/std::sqrt(3);
387 :
388 : // qy derivatives
389 103 : std::vector<Vector> dqy_dR(6);
390 721 : for(unsigned j=0; j<6; j++) {
391 618 : dqy_dR[j]=1/std::sqrt(3) * dB_dR[j];
392 : }
393 :
394 206 : Value* vqy=getPntrToComponent("qy");
395 : vqy->set(qy);
396 103 : setAtomsDerivatives (vqy,0, dqy_dR[0] );
397 103 : setAtomsDerivatives (vqy,1, dqy_dR[1] );
398 103 : setAtomsDerivatives (vqy,2, dqy_dR[2] );
399 103 : setAtomsDerivatives (vqy,3, dqy_dR[3] );
400 103 : setAtomsDerivatives (vqy,4, dqy_dR[4] );
401 103 : setAtomsDerivatives (vqy,5, dqy_dR[5] );
402 103 : setBoxDerivativesNoPbc(vqy);
403 :
404 : // qz
405 103 : double qz = C/std::sqrt(6);
406 :
407 : // qz derivatives
408 103 : std::vector<Vector> dqz_dR(6);
409 721 : for(unsigned j=0; j<6; j++) {
410 618 : dqz_dR[j]=1/std::sqrt(6) * dC_dR[j];
411 : }
412 :
413 206 : Value* vqz=getPntrToComponent("qz");
414 : vqz->set(qz);
415 103 : setAtomsDerivatives (vqz,0, dqz_dR[0] );
416 103 : setAtomsDerivatives (vqz,1, dqz_dR[1] );
417 103 : setAtomsDerivatives (vqz,2, dqz_dR[2] );
418 103 : setAtomsDerivatives (vqz,3, dqz_dR[3] );
419 103 : setAtomsDerivatives (vqz,4, dqz_dR[4] );
420 103 : setAtomsDerivatives (vqz,5, dqz_dR[5] );
421 103 : setBoxDerivativesNoPbc(vqz);
422 :
423 :
424 : // PHASE
425 103 : double phi=std::atan2(-A,B);
426 :
427 : // PHASE DERIVATIVES
428 103 : std::vector<Vector> dphi_dR(6);
429 721 : for(unsigned j=0; j<6; j++) {
430 618 : dphi_dR[j]=1.0/(A*A+B*B) * (-B*dA_dR[j] + A*dB_dR[j]);
431 : }
432 :
433 206 : Value* vphi=getPntrToComponent("phi");
434 : vphi->set(phi);
435 103 : setAtomsDerivatives (vphi,0, dphi_dR[0] );
436 103 : setAtomsDerivatives (vphi,1, dphi_dR[1] );
437 103 : setAtomsDerivatives (vphi,2, dphi_dR[2] );
438 103 : setAtomsDerivatives (vphi,3, dphi_dR[3] );
439 103 : setAtomsDerivatives (vphi,4, dphi_dR[4] );
440 103 : setAtomsDerivatives (vphi,5, dphi_dR[5] );
441 103 : setBoxDerivativesNoPbc(vphi);
442 :
443 : // AMPLITUDE
444 103 : double amplitude=std::sqrt((2*(A*A+B*B)+C*C)/6);
445 :
446 : // AMPLITUDE DERIVATIES
447 103 : std::vector<Vector> damplitude_dR(6);
448 721 : for (unsigned j=0; j<6; j++) {
449 618 : damplitude_dR[j]=0.5*std::sqrt(2.0/6.0)/(std::sqrt(A*A+B*B+0.5*C*C)) * (2*A*dA_dR[j] + 2*B*dB_dR[j] + C*dC_dR[j]);
450 : }
451 :
452 206 : Value* vamplitude=getPntrToComponent("amplitude");
453 : vamplitude->set(amplitude);
454 103 : setAtomsDerivatives (vamplitude,0, damplitude_dR[0] );
455 103 : setAtomsDerivatives (vamplitude,1, damplitude_dR[1] );
456 103 : setAtomsDerivatives (vamplitude,2, damplitude_dR[2] );
457 103 : setAtomsDerivatives (vamplitude,3, damplitude_dR[3] );
458 103 : setAtomsDerivatives (vamplitude,4, damplitude_dR[4] );
459 103 : setAtomsDerivatives (vamplitude,5, damplitude_dR[5] );
460 103 : setBoxDerivativesNoPbc(vamplitude);
461 :
462 : // THETA
463 103 : double theta=std::acos( C / std::sqrt(2.*(A*A+B*B) +C*C ) );
464 :
465 : // THETA DERIVATIVES
466 103 : std::vector<Vector> dtheta_dR(6);
467 721 : for(unsigned j=0; j<6; j++) {
468 618 : dtheta_dR[j]=1.0/(3.0*std::sqrt(2)*amplitude*amplitude) * (C/(std::sqrt(A*A+B*B)) * (A*dA_dR[j] + B*dB_dR[j]) - std::sqrt(A*A+B*B)*dC_dR[j]);
469 : }
470 206 : Value* vtheta=getPntrToComponent("theta");
471 : vtheta->set(theta);
472 103 : setAtomsDerivatives (vtheta,0, dtheta_dR[0] );
473 103 : setAtomsDerivatives (vtheta,1, dtheta_dR[1] );
474 103 : setAtomsDerivatives (vtheta,2, dtheta_dR[2] );
475 103 : setAtomsDerivatives (vtheta,3, dtheta_dR[3] );
476 103 : setAtomsDerivatives (vtheta,4, dtheta_dR[4] );
477 103 : setAtomsDerivatives (vtheta,5, dtheta_dR[5] );
478 103 : setBoxDerivativesNoPbc(vtheta);
479 206 : }
480 :
481 :
482 : }
483 : }
484 :
485 :
486 :
|