1 //
2 //  util.h
3 //  pb_solvers_code
4 //
5 /*
6  Copyright (c) 2015, Teresa Head-Gordon, Lisa Felberg, Enghui Yap, David Brookes
7  All rights reserved.
8 
9  Redistribution and use in source and binary forms, with or without
10  modification, are permitted provided that the following conditions are met:
11  * Redistributions of source code must retain the above copyright
12  notice, this list of conditions and the following disclaimer.
13  * Redistributions in binary form must reproduce the above copyright
14  notice, this list of conditions and the following disclaimer in the
15  documentation and/or other materials provided with the distribution.
16  * Neither the name of UC Berkeley nor the
17  names of its contributors may be used to endorse or promote products
18  derived from this software without specific prior written permission.
19 
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS BE LIABLE FOR ANY
24  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #ifndef util_hpp
33 #define util_hpp
34 
35 #ifdef _WIN32
36   #include <iso646.h>
37   #define _USE_MATH_DEFINES
38 #endif
39 
40 #include <math.h>
41 #include <stdio.h>
42 #include <algorithm>
43 #include <complex>
44 #include <iostream>
45 #include <iomanip>
46 #include <unordered_map>
47 #include "MyMatrix.h"
48 #include "MyExpansion.h"
49 
50 #ifdef _WIN32
51   extern double drand48();
52 #endif
53 
54 /*
55  Right scalar multiplication of a matrix
56 */
57 template<typename T>
58 MyMatrix<T> operator*(MyMatrix<T> mat, const T& rhs)
59 {
60   MyMatrix<T> result = MyMatrix<T>(mat.get_nrows(), mat.get_ncols());
61   int i, j;
62   for (i = 0; i < mat.get_nrows(); i++)
63   {
64     for (j= 0; j < mat.get_ncols(); j++)
65     {
66         result.set_val(i, j, rhs * mat(i, j));
67     }
68   }
69   return result;
70 }
71 
72 
73 /*
74  lhs scalar multiplication of a matrix
75  */
76 template<typename T>
77 MyMatrix<T> operator*(const T& lhs, MyMatrix<T> mat)
78 {
79   MyMatrix<T> result = MyMatrix<T>(mat.get_nrows(), mat.get_ncols());
80   int i, j;
81   for (i = 0; i < mat.get_nrows(); i++)
82   {
83     for (j= 0; j < mat.get_ncols(); j++)
84     {
85         result.set_val(i, j, lhs * mat(i, j));
86     }
87   }
88   return result;
89 }
90 
91 /*
92  Class for storing three dimensional points in spherical or
93  euclidean coordinates
94  Should note that THETA = polar angle [ 0 <= THETA <= PI ]
95  and PHI = azimuthal angle [ 0 <= PHI <= 2*PI ]
96  */
97 template<typename T>
98 class Point
99 {
100 protected:
101   T p1_; // x or r
102   T p2_; // y or theta
103   T p3_; // z or phi
104   bool sph_; // whether or not the point is in spherical coordinates
105 
106   // convert between coordinatre systems:
convert_to_spherical()107   void convert_to_spherical()
108   {
109     if (sph_)
110       return; // do nothing if already spherical
111 
112     T theta, phi;
113     T r = sqrt(p1_*p1_ + p2_*p2_ + p3_*p3_);
114 
115     if (r < fabs(p3_))
116       r = fabs(p3_);
117 
118     if (r == 0.0)
119       theta = 0.0;
120     else
121       theta = acos(p3_/r);
122 
123     if ((p1_ == 0.0) && (p2_ == 0.0))
124       phi = 0.0;
125     else
126       phi = atan2(p2_, p1_);
127 
128     p1_ = r;
129     p2_ = theta;
130     p3_ = phi;
131     sph_ = true;
132   }
133 
convert_to_euclidean()134   void convert_to_euclidean()
135   {
136     if (!sph_)
137       return; // do nothing if already euclidean
138 
139     T x, y, z;
140     x = (abs(p1_*sin(p2_)*cos(p3_)) < 1e-15) ? 0 : p1_*sin(p2_)*cos(p3_);
141     y = (abs(p1_*sin(p2_)*sin(p3_)) < 1e-15) ? 0 : p1_*sin(p2_)*sin(p3_);
142     z = (abs(p1_*cos(p2_)) < 1e-15) ? 0 : p1_*cos(p2_);
143 
144     p1_ = x;
145     p2_ = y;
146     p3_ = z;
147     sph_ = false;
148   }
149 
150 public:
151 
152   // constructor given three coordinate values and whether those are spherical
153   // default is to assum euclidean
154   Point(T p1=T(), T p2=T(), T p3=T(), bool sph=false)
p1_(p1)155   :p1_(p1), p2_(p2), p3_(p3), sph_(sph)
156   {
157   }
158 
159   //constructor given a vector
160   Point(MyVector<T> vec, bool sph=false)
p1_(vec[0])161   :p1_(vec[0]), p2_(vec[1]), p3_(vec[2]), sph_(sph)
162   {
163   }
164 
165   Point<T> operator=(Point<T> other)
166   {
167     if (this != &other) // protect against invalid self-assignment
168     {
169       p1_  = other.p1_;
170       p2_  = other.p2_;
171       p3_  = other.p3_;
172       sph_ = other.sph_;
173     }
174     return *this;
175   }
176 
177   T operator[](int i)
178   {
179     if (i == 0) return p1_;
180     else if (i == 1) return p2_;
181     else if (i == 2) return p3_;
182     else return T();
183   }
184 
185   bool operator==(const Point<T> &other) const
186   {
187     return (p1_ == other.p1_ && p2_ == other.p2_ && p3_ == other.p3_);
188   }
189 
set_x(T val)190   void set_x(T val) { convert_to_euclidean(); p1_ = val; }
set_y(T val)191   void set_y(T val) { convert_to_euclidean(); p2_ = val; }
set_z(T val)192   void set_z(T val) { convert_to_euclidean(); p3_ = val; }
193 
set_r(T val)194   void set_r(T val) { convert_to_spherical(); p1_ = val; }
set_theta(T val)195   void set_theta(T val) { convert_to_spherical(); p2_ = val; }
set_phi(T val)196   void set_phi(T val) { convert_to_spherical(); p3_ = val; }
197 
198 //
199   //arithmetic operators:
200 
201   //scalar multiplication
202   Point<T> operator*(T scalar)
203   {
204     Point<T> pout;
205     if (sph_) convert_to_euclidean(); //for simplicity
206     pout.p1_ = p1_ * scalar;
207     pout.p2_ = p2_ * scalar;
208     pout.p3_ = p3_ * scalar;
209     return pout;
210   }
211 
212 
213   Point<T> operator*=(T scalar)
214   {
215     if (sph_) convert_to_euclidean();
216     p1_ *= scalar;
217     p2_ *= scalar;
218     p3_ *= scalar;
219     return *this;
220   }
221 
222   Point<T> operator+=(Point<T>& rhs)
223   {
224     if (sph_) convert_to_euclidean();
225     p1_ += rhs.p1_;
226     p2_ += rhs.p2_;
227     p3_ += rhs.p3_;
228     return *this;
229   }
230 
231   Point<T> operator+(Point<T> other)
232   {
233     Point<T> pout;
234     if (sph_) convert_to_euclidean();
235     pout.p1_ = p1_ + other.p1_;
236     pout.p2_ = p2_ + other.p2_;
237     pout.p3_ = p3_ + other.p3_;
238     return pout;
239   }
240 
241   Point<T> operator-(Point<T> other)
242   {
243     Point<T> pout;
244     if (sph_) convert_to_euclidean(); //for simplicity
245     pout.p1_ = p1_ - other.p1_;
246     pout.p2_ = p2_ - other.p2_;
247     pout.p3_ = p3_ - other.p3_;
248     return pout;
249   }
250 
251   // calculate the distance to another point
dist(Point<T> other)252   double dist(Point<T> other)
253   {
254     convert_to_euclidean();
255     double d = pow(this->x() - other.x(), 2);
256     d += pow(this->y() - other.y(), 2);
257     d += pow(this->z() - other.z(), 2);
258     d = sqrt(d);
259     return d;
260   }
261 
dot(Point<T> & other)262   double dot(Point<T>& other)
263   {
264     convert_to_euclidean();
265     double dot = p1_ * other.x() + p2_ * other.y() + p3_ * other.z();
266     return dot;
267   }
268 
rotate(MyMatrix<T> & rotmat)269   Point<T> rotate(MyMatrix<T>& rotmat)
270   {
271     convert_to_euclidean();
272     T x, y, z;
273     x = rotmat(0, 0) * p1_ + rotmat(0, 1) * p2_ + rotmat(0, 2) * p3_;
274     y = rotmat(1, 0) * p1_ + rotmat(1, 1) * p2_ + rotmat(1, 2) * p3_;
275     z = rotmat(2, 0) * p1_ + rotmat(2, 1) * p2_ + rotmat(2, 2) * p3_;
276     return Point<T>(x, y, z);
277   }
278 
279   // Method to set xyz using numbers 0=x, 1=y, 2=z
set_cart(int dim,T val)280   void set_cart(int dim, T val)
281   {
282     if (sph_) convert_to_euclidean();
283 
284     if (dim == 0)       p1_ = val;
285     else if (dim == 1)  p2_ = val;
286     else                p3_ = val;
287   }
288 
289   // Method to get xyz using numbers 0=x, 1=y, 2=z
get_cart(int val)290   const T& get_cart(int val)
291   {
292     if (sph_) convert_to_euclidean();
293 
294     if (val == 0)       return p1_;
295     else if (val == 1)  return p2_;
296 
297     return p3_;
298   }
299 
get_p1()300   const T& get_p1() const { return p1_; }
get_p2()301   const T& get_p2() const { return p2_; }
get_p3()302   const T& get_p3() const { return p3_; }
303 
304 
305   // Getter methods perform necessary conversions:
x()306   const T& x()
307   {
308     if (sph_) convert_to_euclidean();
309     return p1_;
310   }
y()311   const T& y()
312   {
313     if (sph_) convert_to_euclidean();
314     return p2_;
315   }
z()316   const T& z()
317   {
318     if (sph_) convert_to_euclidean();
319     return p3_;
320   }
r()321   const T& r()
322   {
323     if (!sph_) convert_to_spherical();
324     return p1_;
325   }
theta()326   const T& theta()
327   {
328     if (!sph_) convert_to_spherical();
329     return p2_;
330   }
phi()331   const T& phi()
332   {
333     if (!sph_) convert_to_spherical();
334     return p3_;
335   }
336 
norm2()337   T norm2() { return p1_*p1_ + p2_*p2_ + p3_*p3_; }
338 
norm()339   T norm() { return sqrt(norm2()); }
340 
341 
342 };
343 
344 
345 
346 /*
347  Class for storing quaternions, which are defined as a real part and a vector
348  part
349  */
350 class Quaternion
351 {
352 protected:
353   double w_;  // real part
354 
355   //imaginary coefficients:
356   double a_;
357   double b_;
358   double c_;
359 
360 public:
361   typedef Quaternion Quat;
362 
363   /*
364    Construct a quaternion given all the coefficients explicitly
365    */
366   Quaternion(double w=1, double a=0, double b=0, double c=0, bool norm=true):
w_(w)367   w_(w), a_(a), b_(b), c_(c)
368   {
369     if (norm) normalize();
370   }
371 
372   /*
373    Construct a quarernion given an angle and axis of rotation
374    */
375   Quaternion(double theta, Point<double> axis, bool norm=true)
376   {
377     w_ = cos(theta/2.0);
378     double scal = sin(theta/2.0);
379     a_ = (axis.x()/axis.norm()) * scal;
380     b_ = (axis.y()/axis.norm()) * scal;
381     c_ = (axis.z()/axis.norm()) * scal;
382     if (norm) normalize();
383   }
384 
385   /*
386    Construct a quarernion from another
387    */
Quaternion(const Quaternion & q)388   Quaternion(const Quaternion & q)  { *this = q;  }
389 
390   /*
391    Normalize the quaternion
392    */
normalize()393   void normalize()
394   {
395     double in_norm = 1.0/sqrt(w_*w_ + a_*a_ + b_*b_  + c_*c_);
396     w_ *= in_norm; a_ *= in_norm; b_ *= in_norm; c_ *= in_norm;
397   }
398 
399   // return parts of quaternion
get_w()400   double get_w() { return w_; }
get_a()401   double get_a() { return a_; }
get_b()402   double get_b() { return b_; }
get_c()403   double get_c() { return c_; }
get_imag()404   Point<double> get_imag() { return Point <double> ( a_, b_, c_ ); }
405 
406   // returns the conjugate of this quarternion, negates all complex terms
conj()407   Quat conj()  { return Quat(w_, -a_, -b_, -c_); }
408 
409   /*
410    Quaternion = operator
411    */
412   Quaternion & operator=(const Quaternion & q)
413   {
414     w_ = q.w_; a_ = q.a_; b_ = q.b_; c_ = q.c_;
415     return *this;
416   }
417 
418   /*
419    Right multiply this by another quarternion
420    */
421   Quat operator*(Quat rhs)
422   {
423     double t0, t1, t2, t3; // resulting coefficients
424     double q0, q1, q2, q3; // this coeffs
425     double r0, r1, r2, r3; // rhs coeffs
426 
427     q0=w_; q1=a_; q2=b_; q3=c_;
428     r0=rhs.w_; r1=rhs.a_; r2=rhs.b_; r3=rhs.c_;
429 
430     t0 = q0*r0 - q1*r1 - q2*r2 - q3*r3;
431     t1 = q0*r1 + q1*r0 + q2*r3 - q3*r2;
432     t2 = q0*r2 - q1*r3 + q2*r0 + q3*r1;
433     t3 = q0*r3 + q1*r2 - q2*r1 + q3*r0;
434 
435     return Quat(t0, t1, t2, t3, false);
436   }
437 
chooseRandom()438   Quat chooseRandom()
439   {
440     double s = drand48();
441     double sig1 = sqrt(1.0-s);
442     double sig2 = sqrt(s);
443     double t1 = 2.0*drand48()*M_PI;
444     double t2 = 2.0*drand48()*M_PI;
445     Quat Q(cos(t2)*sig2, sin(t1)*sig1, cos(t1)*sig1, sin(t2)*sig2);
446 
447     return Q;
448   }
449 
450   /*
451    Rotate a point with this quarternion. If the quarternion is q and the point
452    is p, then this returns q*p*conj(q)
453    */
rotate_point(Point<double> pt)454   Point<double> rotate_point(Point<double> pt)
455   {
456     //make the point a quarternion with real part=0
457     Quat p (0, pt.x(), pt.y(), pt.z(), false);
458     Quat q_conj = conj();
459 
460     Quat result = *this * p;
461     result = result * q_conj;
462 
463     Point<double> pp (result.a_, result.b_, result.c_);
464     return pp;
465   }
466 //
467 };
468 
469 typedef complex<double> cmplx;
470 typedef Point<double> Pt;
471 typedef Point<cmplx> Ptx;
472 typedef Quaternion Quat;
473 
474 
475 namespace std {
476   //hash for Point classes:
477   template <>
478   struct hash<Pt>
479   {
480     size_t operator()(const Pt& k) const
481     {
482       size_t h1 = std::hash<double>()(k.get_p1());
483       size_t h2 = std::hash<double>()(k.get_p2());
484       size_t h3 = std::hash<double>()(k.get_p3());
485       return (h1 ^ (h2 << 1)) ^ h3;
486     }
487   };
488 }
489 
490 
491 #endif /* util_h */
492