]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGMatrix.cpp
Sync with current JSBSim devel code.
[flightgear.git] / src / FDM / JSBSim / FGMatrix.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3 Module: FGMatrix.cpp
4 Author: Originally by Tony Peden [formatted here (and broken??) by JSB]
5 Date started: 1998
6 Purpose: FGMatrix class
7 Called by: Various
8
9 FUNCTIONAL DESCRIPTION
10 --------------------------------------------------------------------------------
11
12 HISTORY
13 --------------------------------------------------------------------------------
14 ??/??/??   TP   Created
15 03/16/2000 JSB  Added exception throwing
16
17 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
18 INCLUDES
19 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
20
21 #include "FGMatrix.h"
22
23 static const char *IdSrc = "$Header$";
24 static const char *IdHdr = ID_MATRIX;
25
26 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 CLASS IMPLEMENTATION
28 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
29
30 double** FGalloc(int rows, int cols)
31 {
32   double **A;
33
34   A = new double *[rows+1];
35   if (!A)  return NULL;
36
37   for (int i=0; i <= rows; i++){
38     A[i] = new double [cols+1];
39     if (!A[i]) return NULL;
40   }
41   return A;
42 }
43
44 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45
46 void dealloc(double **A, int rows)
47 {
48   for(int i=0;i<= rows;i++) delete[] A[i];
49   delete[] A;
50 }
51
52 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53
54 FGMatrix::FGMatrix(const unsigned int r, const unsigned int c) : rows(r), cols(c)
55 {
56   data = FGalloc(rows,cols);
57   InitMatrix();
58   rowCtr = colCtr = 1;
59 }
60
61 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
63 FGMatrix::FGMatrix(const FGMatrix& M)
64 {
65   data  = NULL;
66   rowCtr = colCtr = 1;
67   *this = M;
68 }
69
70 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71
72 FGMatrix::~FGMatrix(void)
73 {
74   dealloc(data,rows);
75   rowCtr = colCtr = 1;
76   rows = cols = 0;
77 }
78
79 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80
81 ostream& operator<<(ostream& os, const FGMatrix& M)
82 {
83   for (unsigned int i=1; i<=M.Rows(); i++) {
84     for (unsigned int j=1; j<=M.Cols(); j++) {
85       if (i == M.Rows() && j == M.Cols())
86         os << M.data[i][j];
87       else
88         os << M.data[i][j] << ", ";
89     }
90   }
91   return os;
92 }
93
94 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95
96 FGMatrix& FGMatrix::operator<<(const float ff)
97 {
98   data[rowCtr][colCtr] = ff;
99   if (++colCtr > Cols()) {
100     colCtr = 1;
101     if (++rowCtr > Rows())
102       rowCtr = 1;
103   }
104   return *this;
105 }
106
107 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108
109 istream& operator>>(istream& is, FGMatrix& M)
110 {
111   for (unsigned int i=1; i<=M.Rows(); i++) {
112     for (unsigned int j=1; j<=M.Cols(); j++) {
113       is >> M.data[i][j];
114     }
115   }
116   return is;
117 }
118
119 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120
121 FGMatrix& FGMatrix::operator=(const FGMatrix& M)
122 {
123   if (&M != this) {
124     if (data != NULL) dealloc(data,rows);
125
126     width  = M.width;
127     prec   = M.prec;
128     delim  = M.delim;
129     origin = M.origin;
130     rows   = M.rows;
131     cols   = M.cols;
132
133     data = FGalloc(rows,cols);
134     for (unsigned int i=0; i<=rows; i++) {
135       for (unsigned int j=0; j<=cols; j++) {
136         data[i][j] = M.data[i][j];
137       }
138     }
139   }
140   return *this;
141 }
142
143 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144
145 unsigned int FGMatrix::Rows(void) const
146 {
147   return rows;
148 }
149
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151
152 unsigned int FGMatrix::Cols(void) const
153 {
154   return cols;
155 }
156
157 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158
159 void FGMatrix::SetOParams(char delim,int width,int prec,int origin)
160 {
161   FGMatrix::delim  = delim;
162   FGMatrix::width  = width;
163   FGMatrix::prec   = prec;
164   FGMatrix::origin = origin;
165 }
166
167 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168
169 void FGMatrix::InitMatrix(double value)
170 {
171   if (data) {
172     for (unsigned int i=0;i<=rows;i++) {
173       for (unsigned int j=0;j<=cols;j++) {
174         operator()(i,j) = value;
175       }
176     }
177   }
178 }
179
180 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181
182 void FGMatrix::InitMatrix(void)
183 {
184   this->InitMatrix(0);
185 }
186
187 // *****************************************************************************
188 // binary operators ************************************************************
189 // *****************************************************************************
190
191 FGMatrix FGMatrix::operator-(const FGMatrix& M)
192 {
193   if ((Rows() != M.Rows()) || (Cols() != M.Cols())) {
194     MatrixException mE;
195     mE.Message = "Invalid row/column match in Matrix operator -";
196     throw mE;
197   }
198
199   FGMatrix Diff(Rows(), Cols());
200
201   for (unsigned int i=1; i<=Rows(); i++) {
202     for (unsigned int j=1; j<=Cols(); j++) {
203       Diff(i,j) = data[i][j] - M(i,j);
204     }
205   }
206   return Diff;
207 }
208
209 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210
211 void FGMatrix::operator-=(const FGMatrix &M)
212 {
213   if ((Rows() != M.Rows()) || (Cols() != M.Cols())) {
214     MatrixException mE;
215     mE.Message = "Invalid row/column match in Matrix operator -=";
216     throw mE;
217   }
218
219   for (unsigned int i=1; i<=Rows(); i++) {
220     for (unsigned int j=1; j<=Cols(); j++) {
221       data[i][j] -= M(i,j);
222     }
223   }
224 }
225
226 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
227
228 FGMatrix FGMatrix::operator+(const FGMatrix& M)
229 {
230   if ((Rows() != M.Rows()) || (Cols() != M.Cols())) {
231     MatrixException mE;
232     mE.Message = "Invalid row/column match in Matrix operator +";
233     throw mE;
234   }
235
236   FGMatrix Sum(Rows(), Cols());
237
238   for (unsigned int i=1; i<=Rows(); i++) {
239     for (unsigned int j=1; j<=Cols(); j++) {
240       Sum(i,j) = data[i][j] + M(i,j);
241     }
242   }
243   return Sum;
244 }
245
246 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247
248 void FGMatrix::operator+=(const FGMatrix &M)
249 {
250   if ((Rows() != M.Rows()) || (Cols() != M.Cols())) {
251     MatrixException mE;
252     mE.Message = "Invalid row/column match in Matrix operator +=";
253     throw mE;
254   }
255
256   for (unsigned int i=1; i<=Rows(); i++) {
257     for (unsigned int j=1; j<=Cols(); j++) {
258       data[i][j]+=M(i,j);
259     }
260   }
261 }
262
263 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264
265 FGMatrix operator*(double scalar, FGMatrix &M)
266 {
267   FGMatrix Product(M.Rows(), M.Cols());
268
269   for (unsigned int i=1; i<=M.Rows(); i++) {
270     for (unsigned int j=1; j<=M.Cols(); j++) {
271       Product(i,j) = scalar*M(i,j);
272     }
273   }
274   return Product;
275 }
276
277 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278
279 void FGMatrix::operator*=(const double scalar)
280 {
281   for (unsigned int i=1; i<=Rows(); i++) {
282     for (unsigned int j=1; j<=Cols(); j++) {
283       data[i][j] *= scalar;
284     }
285   }
286 }
287
288 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
289
290 FGMatrix FGMatrix::operator*(const FGMatrix& M)
291 {
292   if (Cols() != M.Rows()) {
293     MatrixException mE;
294     mE.Message = "Invalid row/column match in Matrix operator *";
295     throw mE;
296   }
297
298   FGMatrix Product(Rows(), M.Cols());
299
300   for (unsigned int i=1; i<=Rows(); i++) {
301     for (unsigned int j=1; j<=M.Cols(); j++)  {
302       Product(i,j) = 0;
303       for (unsigned int k=1; k<=Cols(); k++) {
304          Product(i,j) += data[i][k] * M(k,j);
305       }
306     }
307   }
308   return Product;
309 }
310
311 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312
313 void FGMatrix::operator*=(const FGMatrix& M)
314 {
315   if (Cols() != M.Rows()) {
316     MatrixException mE;
317     mE.Message = "Invalid row/column match in Matrix operator *=";
318     throw mE;
319   }
320
321   double **prod;
322
323   prod = FGalloc(Rows(), M.Cols());
324   for (unsigned int i=1; i<=Rows(); i++) {
325     for (unsigned int j=1; j<=M.Cols(); j++) {
326       prod[i][j] = 0;
327       for (unsigned int k=1; k<=Cols(); k++) {
328         prod[i][j] += data[i][k] * M(k,j);
329       }
330     }
331   }
332   dealloc(data, Rows());
333   data = prod;
334   cols = M.cols;
335 }
336
337 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338
339 FGMatrix FGMatrix::operator/(const double scalar)
340 {
341   FGMatrix Quot(Rows(), Cols());
342
343   if(scalar != 0) {
344     for (unsigned int i=1; i<=Rows(); i++) {
345       for (unsigned int j=1; j<=Cols(); j++)  {
346          Quot(i,j) = data[i][j]/scalar;
347       }
348     }
349     
350   } else
351     cerr << "Attempt to divide by zero in method FGMatrix::operator/(const double scalar), object at " << this << endl; 
352   return Quot;  
353 }
354
355 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
356
357 void FGMatrix::operator/=(const double scalar)
358 {
359   
360   if(scalar != 0) {
361     for (unsigned int i=1; i<=Rows(); i++)  {
362       for (unsigned int j=1; j<=Cols(); j++) {
363         data[i][j]/=scalar;
364       }
365     }
366   } else 
367     cerr << "Attempt to divide by zero in method FGMatrix::operator/=(const double scalar), object " << this << endl; 
368 }
369
370 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371
372 void FGMatrix::T(void)
373 {
374   if (rows==cols)
375     TransposeSquare();
376   else
377     TransposeNonSquare();
378 }
379
380 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381
382 FGColumnVector FGMatrix::operator*(const FGColumnVector& Col)
383 {
384   FGColumnVector Product(Col.Rows());
385
386   if (Cols() != Col.Rows()) {
387     MatrixException mE;
388     mE.Message = "Invalid row/column match in Column Vector operator *";
389     throw mE;
390   }
391
392   for (unsigned int i=1;i<=Rows();i++) {
393     Product(i) = 0.00;
394     for (unsigned int j=1;j<=Cols();j++) {
395       Product(i) += Col(j)*data[i][j];
396     }
397   }
398   return Product;
399 }
400
401 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
402
403 void FGMatrix::TransposeSquare(void)
404 {
405   for (unsigned int i=1; i<=rows; i++) {
406     for (unsigned int j=i+1; j<=cols; j++) {
407       double tmp = data[i][j];
408       data[i][j] = data[j][i];
409       data[j][i] = tmp;
410     }
411   }
412 }
413
414 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415
416 void FGMatrix::TransposeNonSquare(void)
417 {
418   double **tran;
419
420   tran = FGalloc(rows,cols);
421
422   for (unsigned int i=1; i<=rows; i++) {
423     for (unsigned int j=1; j<=cols; j++) {
424       tran[j][i] = data[i][j];
425     }
426   }
427
428   dealloc(data,rows);
429
430   data       = tran;
431   unsigned int m = rows;
432   rows       = cols;
433   cols       = m;
434 }
435
436 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
437
438 FGColumnVector::FGColumnVector(void):FGMatrix(3,1) { }
439 FGColumnVector::FGColumnVector(int m):FGMatrix(m,1) { }
440 FGColumnVector::FGColumnVector(const FGColumnVector& b):FGMatrix(b) { }
441 FGColumnVector::~FGColumnVector() { }
442
443 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444
445 double& FGColumnVector::operator()(int m) const
446 {
447   return FGMatrix::operator()(m,1);
448 }
449
450 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451
452 FGColumnVector operator*(const FGMatrix& Mat, const FGColumnVector& Col)
453 {
454   FGColumnVector Product(Col.Rows());
455
456   if (Mat.Cols() != Col.Rows()) {
457     MatrixException mE;
458     mE.Message = "Invalid row/column match in Column Vector operator *";
459     throw mE;
460   }
461
462   for (unsigned int i=1;i<=Mat.Rows();i++) {
463     Product(i) = 0.00;
464     for (unsigned int j=1;j<=Mat.Cols();j++) {
465       Product(i) += Col(j)*Mat(i,j);
466     }
467   }
468
469   return Product;
470 }
471
472 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473
474 FGColumnVector FGColumnVector::operator+(const FGColumnVector& C)
475 {
476   if (Rows() != C.Rows()) {
477     MatrixException mE;
478     mE.Message = "Invalid row/column match in Column Vector operator *";
479     throw mE;
480   }
481
482   FGColumnVector Sum(C.Rows());
483
484   for (unsigned int i=1; i<=C.Rows(); i++) {
485     Sum(i) = C(i) + data[i][1];
486   }
487
488   return Sum;
489 }
490
491 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492
493 FGColumnVector FGColumnVector::operator*(const double scalar)
494 {
495   FGColumnVector Product(Rows());
496
497   for (unsigned int i=1; i<=Rows(); i++) Product(i) = scalar * data[i][1];
498
499   return Product;
500 }
501
502 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
503
504 FGColumnVector FGColumnVector::operator-(const FGColumnVector& V)
505 {
506   if ((Rows() != V.Rows()) || (Cols() != V.Cols())) {
507     MatrixException mE;
508     mE.Message = "Invalid row/column match in Column Vector operator -";
509     throw mE;
510   }
511
512   FGColumnVector Diff(Rows());
513
514   for (unsigned int i=1; i<=Rows(); i++) {
515     Diff(i) = data[i][1] - V(i);
516   }
517
518   return Diff;
519 }
520
521 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522
523 FGColumnVector FGColumnVector::operator/(const double scalar)
524 {
525   FGColumnVector Quotient(Rows());
526   if(scalar != 0) {
527     
528
529     for (unsigned int i=1; i<=Rows(); i++) Quotient(i) = data[i][1] / scalar;
530
531   } else 
532     cerr << "Attempt to divide by zero in method FGColumnVector::operator/(const double scalar), object " << this << endl; 
533   return Quotient;
534   
535     
536 }
537
538 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
539
540 FGColumnVector operator*(const double scalar, const FGColumnVector& C)
541 {
542   FGColumnVector Product(C.Rows());
543
544   for (unsigned int i=1; i<=C.Rows(); i++) {
545      Product(i) = scalar * C(i);
546   }
547
548   return Product;
549 }
550
551 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
552
553 float FGColumnVector::Magnitude(void)
554 {
555   double num=0.0;
556
557   if ((data[1][1] == 0.00) &&
558       (data[2][1] == 0.00) &&
559       (data[3][1] == 0.00))
560   {
561     return 0.00;
562   } else {
563     for (unsigned int i = 1; i<=Rows(); i++) num += data[i][1]*data[i][1];
564     return sqrt(num);
565   }
566 }
567
568 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
569
570 FGColumnVector FGColumnVector::Normalize(void)
571 {
572   double Mag = Magnitude();
573
574   if (Mag != 0) {
575     for (unsigned int i=1; i<=Rows(); i++)
576       for (unsigned int j=1; j<=Cols(); j++)
577         data[i][j] = data[i][j]/Mag;
578   }    
579
580   return *this;
581 }
582
583 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584
585 FGColumnVector FGColumnVector::operator*(const FGColumnVector& V)
586 {
587   if (Rows() != 3 || V.Rows() != 3) {
588     MatrixException mE;
589     mE.Message = "Invalid row count in vector cross product function";
590     throw mE;
591   }
592
593   FGColumnVector Product(3);
594
595   Product(1) = data[2][1] * V(3) - data[3][1] * V(2);
596   Product(2) = data[3][1] * V(1) - data[1][1] * V(3);
597   Product(3) = data[1][1] * V(2) - data[2][1] * V(1);
598
599   return Product;
600 }
601
602 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
603
604 FGColumnVector FGColumnVector::multElementWise(const FGColumnVector& V)
605 {
606   if (Rows() != 3 || V.Rows() != 3) {
607     MatrixException mE;
608     mE.Message = "Invalid row count in vector cross product function";
609     throw mE;
610   }
611
612   FGColumnVector Product(3);
613
614   Product(1) = data[1][1] * V(1);
615   Product(2) = data[2][1] * V(2);
616   Product(3) = data[3][1] * V(3);
617
618   return Product;
619 }
620
621 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%