

/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* Copyright (c) Schrodinger, LLC. 
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information. 
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-* 
-* 
-*
Z* -------------------------------------------------------------------
*/
#include"os_predef.h"
#include"os_std.h"

#include"Base.h"
#include"Vector.h"
#include"Matrix.h"

#include <cassert>

#define MASK_01010101 (((unsigned long)(-1))/3)
#define MASK_00110011 (((unsigned long)(-1))/5)
#define MASK_00001111 (((unsigned long)(-1))/17)
#define MASK_11111111 (((unsigned long)(-1))/257)

#define MASK_01010101010101010101010101010101 MASK_01010101
#define MASK_00110011001100110011001100110011 MASK_00110011
#define MASK_00001111000011110000111100001111 MASK_00001111
#define MASK_00000000111111110000000011111111 MASK_11111111
#define MASK_00000000000000001111111111111111 (((unsigned long)(-1))/65537)

short countBits(unsigned long bits){
  unsigned long n = bits;
  n = (n & MASK_01010101010101010101010101010101) + ((n >> 1) & MASK_01010101010101010101010101010101) ;
  n = (n & MASK_00110011001100110011001100110011) + ((n >> 2) & MASK_00110011001100110011001100110011) ;
  n = (n & MASK_00001111000011110000111100001111) + ((n >> 4) & MASK_00001111000011110000111100001111) ;
  n = (n & MASK_00000000111111110000000011111111) + ((n >> 8) & MASK_00000000111111110000000011111111) ;
  n = (n & MASK_00000000000000001111111111111111) + ((n >> 16) & MASK_00000000000000001111111111111111) ;
  return (n % 255);
}
short countBitsInt(int bits){
  /* This could be improved to not splitting 
     the bits into 4 variables in a loop which are 16 bits each */
  unsigned long n = bits & 65535;
  short nbits = 0;
  n = (n & MASK_01010101) + ((n >> 1) & MASK_01010101) ;
  n = (n & MASK_00110011) + ((n >> 2) & MASK_00110011) ;
  n = (n & MASK_00001111) + ((n >> 4) & MASK_00001111) ;
  nbits += (n % 255);
  return nbits;
}

#ifndef R_SMALL
#define R_SMALL 0.000000001
#endif

#ifndef R_MED
#define R_MED 0.00001
#endif

static const float _0 = 0.0F;
static const float _1 = 1.0F;
static const double _d0 = 0.0;
static const double _d1 = 1.0;

void mix3f(const float *v1, const float *v2, const float fxn, float *v3)
{
  float fxn_1 = 1.0F - fxn;
  v3[0] = v1[0] * fxn_1 + v2[0] * fxn;
  v3[1] = v1[1] * fxn_1 + v2[1] * fxn;
  v3[2] = v1[2] * fxn_1 + v2[2] * fxn;
}

void mix3d(const double *v1, const double *v2, const double fxn, double *v3)
{
  double fxn_1 = 1.0F - fxn;
  v3[0] = v1[0] * fxn_1 + v2[0] * fxn;
  v3[1] = v1[1] * fxn_1 + v2[1] * fxn;
  v3[2] = v1[2] * fxn_1 + v2[2] * fxn;
}

unsigned int optimizer_workaround1u(unsigned int value)
{
  return value;
}

float get_random0to1f()
{
  return rand() / (1.0 + RAND_MAX);
}

int pymol_roundf(float f)
{
  if(f > 0.0F)
    return (int) (f + 0.49999F);
  else
    return (int) (f - 0.49999F);
}

void dump3i(const int *v, const char *prefix)
{                               /* for debugging */
  printf("%s %8i %8i %8i\n", prefix, v[0], v[1], v[2]);
}

void dump3f(const float *v, const char *prefix)
{                               /* for debugging */
  printf("%s %8.3f %8.3f %8.3f\n", prefix, v[0], v[1], v[2]);
}

void dump2f(const float *v, const char *prefix)
{                               /* for debugging */
  printf("%s %8.3f %8.3f\n", prefix, v[0], v[1]);
}

void dump3d(const double *v, const char *prefix)
{                               /* for debugging */
  printf("%s %8.3f %8.3f %8.3f\n", prefix, v[0], v[1], v[2]);
}

void dump4f(const float *v, const char *prefix)
{                               /* for debugging */
  printf("%s %8.3f %8.3f %8.3f %8.3f\n", prefix, v[0], v[1], v[2], v[3]);
}

void dump33f(const float *m, const char *prefix)
{                               /* for debugging */
  if(m) {
    printf("%s:0 %8.3f %8.3f %8.3f\n", prefix, m[0], m[1], m[2]);
    printf("%s:1 %8.3f %8.3f %8.3f\n", prefix, m[3], m[4], m[5]);
    printf("%s:2 %8.3f %8.3f %8.3f\n", prefix, m[6], m[7], m[8]);
  } else {
    printf("%s: (null matrix pointer)\n", prefix);
  }
}

void dump44f(const float *m, const char *prefix)
{                               /* for debugging */
  if(m) {
    if(prefix) {
      printf("%s:0 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[0], m[1], m[2], m[3]);
      printf("%s:1 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[4], m[5], m[6], m[7]);
      printf("%s:2 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[8], m[9], m[10], m[11]);
      printf("%s:3 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[12], m[13], m[14], m[15]);
    } else {
    }
  } else {
    printf("%s: (null matrix pointer)\n", prefix);
  }

}

void dump44d(const double *m, const char *prefix)
{                               /* for debugging */
  if(m) {
    printf("%s:0 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[0], m[1], m[2], m[3]);
    printf("%s:1 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[4], m[5], m[6], m[7]);
    printf("%s:2 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[8], m[9], m[10], m[11]);
    printf("%s:3 %8.3f %8.3f %8.3f %8.3f\n", prefix, m[12], m[13], m[14], m[15]);
  } else {
    printf("%s: (null matrix pointer)\n", prefix);
  }
}

void dump33d(const double *m, const char *prefix)
{                               /* for debugging */
  printf("%s:0 %8.3f %8.3f %8.3f\n", prefix, m[0], m[1], m[2]);
  printf("%s:1 %8.3f %8.3f %8.3f\n", prefix, m[3], m[4], m[5]);
  printf("%s:2 %8.3f %8.3f %8.3f\n", prefix, m[6], m[7], m[8]);
}

void get_divergent3f(const float *src, float *dst)
{
  if(src[0] != _0) {
    *(dst++) = -*(src++);
    *(dst++) = *(src++) + 0.1F;
    *(dst++) = *(src++);
  } else if(src[1] != _0) {
    *(dst++) = *(src++) + 0.1F;
    *(dst++) = -*(src++);
    *(dst++) = *(src++);
  } else {
    *(dst++) = *(src++) + 0.1F;
    *(dst++) = *(src++);
    *(dst++) = -*(src++);
  }
}

int equal3f(const float *v1, const float *v2)
{
  return ((fabs(v1[0] - v2[0]) < R_SMALL) &&
          (fabs(v1[1] - v2[1]) < R_SMALL) && (fabs(v1[2] - v2[2]) < R_SMALL));
}

void get_random3f(float *x)
{                               /* this needs to be fixed as in Tinker */
  x[0] = 0.5F - get_random0to1f();
  x[1] = 0.5F - get_random0to1f();
  x[2] = 0.5F - get_random0to1f();
  normalize3f(x);
}

void get_system3f(float *x, float *y, float *z)
{                               /* make random system */
  get_random3f(x);
  get_divergent3f(x, y);
  cross_product3f(x, y, z);
  normalize3f(z);
  cross_product3f(z, x, y);
  normalize3f(y);
  normalize3f(x);
}

void get_system1f3f(float *x, float *y, float *z)
{                               /* make system in direction of x */
  get_divergent3f(x, y);
  cross_product3f(x, y, z);
  normalize3f(z);
  cross_product3f(z, x, y);
  normalize3f(y);
  normalize3f(x);
}

void get_system2f3f(float *x, float *y, float *z)
{                               /* make system in direction of x */
  cross_product3f(x, y, z);
  normalize3f(z);
  cross_product3f(z, x, y);
  normalize3f(y);
  normalize3f(x);
}

void extrapolate3f(const float *v1, const float *unit, float *result)
{
  float lsq = lengthsq3f(v1);
  float dp = dot_product3f(v1, unit);
  if(dp != 0.0F) {
    float l2 = lsq / dp;
    scale3f(unit, l2, result);
  }
}

void scatter3f(float *v, float weight)
{
  float r[3];
  get_random3f(r);
  scale3f(r, weight, r);
  add3f(r, v, v);
  normalize3f(v);
}

void wiggle3f(float *v, const float *p, const float *s)
{
  float q[3];
  q[0] = (float) cos((p[0] + p[1] + p[2]) * s[1]);
  q[1] = (float) cos((p[0] - p[1] + p[2]) * s[1]);
  q[2] = (float) cos((p[0] + p[1] - p[2]) * s[1]);
  scale3f(q, s[0], q);
  add3f(q, v, v);
  normalize3f(v);

}

void copy3d3f(const double *v1, float *v2)
{
  v2[0] = (float) v1[0];
  v2[1] = (float) v1[1];
  v2[2] = (float) v1[2];
}

void copy3f3d(const float *v1, double *v2)
{
  v2[0] = (double) v1[0];
  v2[1] = (double) v1[1];
  v2[2] = (double) v1[2];
}

void min3f(const float *v1, const float *v2, float *v3)
{
  (v3)[0] = ((v1)[0] < (v2)[0] ? (v1)[0] : (v2)[0]);
  (v3)[1] = ((v1)[1] < (v2)[1] ? (v1)[1] : (v2)[1]);
  (v3)[2] = ((v1)[2] < (v2)[2] ? (v1)[2] : (v2)[2]);
}

void max3f(const float *v1, const float *v2, float *v3)
{
  (v3)[0] = ((v1)[0] > (v2)[0] ? (v1)[0] : (v2)[0]);
  (v3)[1] = ((v1)[1] > (v2)[1] ? (v1)[1] : (v2)[1]);
  (v3)[2] = ((v1)[2] > (v2)[2] ? (v1)[2] : (v2)[2]);
}

void identity33f(float *m)
{
  m[0] = _1;
  m[1] = _0;
  m[2] = _0;
  m[3] = _0;
  m[4] = _1;
  m[5] = _0;
  m[6] = _0;
  m[7] = _0;
  m[8] = _1;
}

void identity33d(double *m)
{
  m[0] = _d1;
  m[1] = _d0;
  m[2] = _d0;
  m[3] = _d0;
  m[4] = _d1;
  m[5] = _d0;
  m[6] = _d0;
  m[7] = _d0;
  m[8] = _d1;
}

void identity44f(float *m1)
{
  int a;
  for(a = 0; a < 16; a++)
    m1[a] = _0;
  for(a = 0; a < 16; a = a + 5)
    m1[a] = _1;
}

void identity44d(double *m1)
{
  int a;
  for(a = 0; a < 16; a++)
    m1[a] = _d0;
  for(a = 0; a < 16; a = a + 5)
    m1[a] = _d1;
}

/**
 * Check a nxn matrix for identity
 */
bool is_identityf(int n, const float *m, float threshold)
{
  int n_sq = n * n, stride = n + 1;
  for (int a = 0; a < n_sq; a++) {
    float e = (a % stride) ? _0 : _1;
    if (fabsf(m[a] - e) > threshold)
      return false;
  }
  return true;
}

/**
 * Check if two matrices are the same. The two matrices may have different
 * number of rows and columns, in that case only the overlaying upper left
 * (nrow x min(ncol1, ncol2)) submatrix is compared.
 */
bool is_allclosef(int nrow,
    const float *m1, int ncol1,
    const float *m2, int ncol2, float threshold)
{
  int ncol = (ncol1 < ncol2) ? ncol1 : ncol2;
  for (int i = 0; i < nrow; i++) {
    for (int j = 0; j < ncol; j++) {
      int a1 = i * ncol1 + j, a2 = i * ncol2 + j;
      if (fabsf(m1[a1] - m2[a2]) > threshold)
        return false;
    }
  }
  return true;
}

/**
 * Check a nxm matrix is a diagonal matrix (non-diagonal elements are zero)
 */
bool is_diagonalf(int nrow,
    const float *m, int ncol, float threshold)
{
  if (!ncol)
    ncol = nrow;
  for (int i = 0; i < nrow; ++i) {
    for (int j = 0; j < ncol; ++j) {
      if (i != j && fabsf(m[i * ncol + j]) > threshold)
        return false;
    }
  }
  return true;
}

/**
 * Determinant of the upper left 3x3 submatrix.
 */
double determinant33f(const float *m, int ncol)
{
  int &a = ncol, b = ncol * 2;
  return
    m[0] * (m[a + 1] * (double) m[b + 2] - m[a + 2] * (double) m[b + 1]) -
    m[1] * (m[a + 0] * (double) m[b + 2] - m[a + 2] * (double) m[b + 0]) +
    m[2] * (m[a + 0] * (double) m[b + 1] - m[a + 1] * (double) m[b + 0]);
}

void glOrtho44f(float *m1, 
		GLfloat left, GLfloat right, 
		GLfloat bottom, GLfloat top,
		GLfloat nearVal, GLfloat farVal){
  int a;
  /* this is set in column major order */
  for(a = 0; a < 16; a++)
    m1[a] = _0;
  m1[0] = 2.f / (right-left);
  m1[5] = 2.f / (top-bottom);
  m1[10] = -2.f/(farVal-nearVal);
  m1[12] = - (right+left) / (right-left);
  m1[13] = - (top+bottom) / (top-bottom);
  m1[14] = - (farVal+nearVal) / (farVal-nearVal);
  m1[15] = _1;
}

void glFrustum44f(float *m1,
		  GLfloat left, GLfloat right, 
		  GLfloat bottom, GLfloat top,
		  GLfloat nearVal, GLfloat farVal){
  int a;
  /* this is set in column major order */
  for(a = 0; a < 16; a++)
    m1[a] = _0;
  m1[0] = 2.f * nearVal / (right-left);
  m1[5] = 2.f * nearVal / (top-bottom);
  m1[8] = (right+left) / (right-left);
  m1[9] = (top+bottom) / (top-bottom);
  m1[10] = -(farVal+nearVal) / (farVal-nearVal);
  m1[11] = -1.f;
  m1[14] = -2.f * (farVal * nearVal) / (farVal - nearVal);
}

void copy44f(const float *src, float *dst)
{
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
}

void copy44d(const double *src, double *dst)
{
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
}

void copy44d33f(const double *src, float *dst)
{
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  src++;

  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  src++;

  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  src++;
}

void copy44f33f(const float *src, float *dst)
{
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  src++;

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  src++;

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  src++;
}

void copy44f44d(const float *src, double *dst)
{
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);

  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);

  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);

  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
}

void copy44d44f(const double *src, float *dst)
{
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);

  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);

  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);

  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
  *(dst++) = (float) *(src++);
}

void copy33f44d(const float *src, double *dst)
{
  const double _0 = 0.0;
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = _0;

  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = _0;

  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = (double) *(src++);
  *(dst++) = _0;

  *(dst++) = _0;
  *(dst++) = _0;
  *(dst++) = _0;
  *(dst++) = _1;
}

void copy33f44f(const float *src, float *dst)
{
  const float _0 = 0.0;
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = _0;

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = _0;

  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = *(src++);
  *(dst++) = _0;

  *(dst++) = _0;
  *(dst++) = _0;
  *(dst++) = _0;
  *(dst++) = _1;
}
/* Transformation: MatMult( (3x3), (3x1) = (3x1) ) */
void transform33f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0];
  float m2r1 = m2[1];
  float m2r2 = m2[2];
  m3[0] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2;
  m3[1] = m1[3] * m2r0 + m1[4] * m2r1 + m1[5] * m2r2;
  m3[2] = m1[6] * m2r0 + m1[7] * m2r1 + m1[8] * m2r2;
}

void transpose33f33f(const float *m1, float *m2)
{
  assert(m1 != m2);

  m2[0] = m1[0];
  m2[1] = m1[3];
  m2[2] = m1[6];
  m2[3] = m1[1];
  m2[4] = m1[4];
  m2[5] = m1[7];
  m2[6] = m1[2];
  m2[7] = m1[5];
  m2[8] = m1[8];
}

void transpose33d33d(const double *m1, double *m2)
{
  assert(m1 != m2);

  m2[0] = m1[0];
  m2[1] = m1[3];
  m2[2] = m1[6];
  m2[3] = m1[1];
  m2[4] = m1[4];
  m2[5] = m1[7];
  m2[6] = m1[2];
  m2[7] = m1[5];
  m2[8] = m1[8];
}

void transpose44f44f(const float *m1, float *m2)
{
  assert(m1 != m2);

  m2[0] = m1[0];
  m2[1] = m1[4];
  m2[2] = m1[8];
  m2[3] = m1[12];

  m2[4] = m1[1];
  m2[5] = m1[5];
  m2[6] = m1[9];
  m2[7] = m1[13];

  m2[8] = m1[2];
  m2[9] = m1[6];
  m2[10] = m1[10];
  m2[11] = m1[14];

  m2[12] = m1[3];
  m2[13] = m1[7];
  m2[14] = m1[11];
  m2[15] = m1[15];
}

void transpose44d44d(const double *m1, double *m2)
{
  assert(m1 != m2);

  m2[0] = m1[0];
  m2[1] = m1[4];
  m2[2] = m1[8];
  m2[3] = m1[12];

  m2[4] = m1[1];
  m2[5] = m1[5];
  m2[6] = m1[9];
  m2[7] = m1[13];

  m2[8] = m1[2];
  m2[9] = m1[6];
  m2[10] = m1[10];
  m2[11] = m1[14];

  m2[12] = m1[3];
  m2[13] = m1[7];
  m2[14] = m1[11];
  m2[15] = m1[15];
}

void transform33Tf3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0];
  float m2r1 = m2[1];
  float m2r2 = m2[2];
  m3[0] = m1[0] * m2r0 + m1[3] * m2r1 + m1[6] * m2r2;
  m3[1] = m1[1] * m2r0 + m1[4] * m2r1 + m1[7] * m2r2;
  m3[2] = m1[2] * m2r0 + m1[5] * m2r1 + m1[8] * m2r2;
}
/* multiply the upper-left 3x3 of a 4x4, m, by m2 and put result into m3:
 * m3 = A x m2 */
void transform44f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0];
  float m2r1 = m2[1];
  float m2r2 = m2[2];
  m3[0] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2 + m1[3];
  m3[1] = m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2 + m1[7];
  m3[2] = m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2 + m1[11];
}
/* Multi the upper left 3x3 of a 4x4 by a 3x1 vector; so effectively, [3x3]*[3x1] = [3x1] */
void transform44d3f(const double *m1, const float *m2, float *m3)
{
  double m2r0 = m2[0];
  double m2r1 = m2[1];
  double m2r2 = m2[2];
  m3[0] = (float) (m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2 + m1[3]);
  m3[1] = (float) (m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2 + m1[7]);
  m3[2] = (float) (m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2 + m1[11]);
}

void transform44d3d(const double *m1, const double *m2, double *m3)
{
  double m2r0 = m2[0];
  double m2r1 = m2[1];
  double m2r2 = m2[2];
  m3[0] = (float) (m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2 + m1[3]);
  m3[1] = (float) (m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2 + m1[7]);
  m3[2] = (float) (m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2 + m1[11]);
}

void transform44d3fas33d3f(const double *m1, const float *m2, float *m3)
{
  double m2r0 = m2[0];
  double m2r1 = m2[1];
  double m2r2 = m2[2];
  m3[0] = (float) (m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2);
  m3[1] = (float) (m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2);
  m3[2] = (float) (m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2);
}

// same as MatrixInvTransformC44fAs33f3f
void transform44f3fas33f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0];
  float m2r1 = m2[1];
  float m2r2 = m2[2];
  m3[0] = (m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2);
  m3[1] = (m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2);
  m3[2] = (m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2);
}

void inverse_transformC44f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0] - m1[12];
  float m2r1 = m2[1] - m1[13];
  float m2r2 = m2[2] - m1[14];
  m3[0] = (float) (m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2);
  m3[1] = (float) (m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2);
  m3[2] = (float) (m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2);
}

void inverse_transform44f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0] - m1[3];
  float m2r1 = m2[1] - m1[7];
  float m2r2 = m2[2] - m1[11];
  m3[0] = (float) (m1[0] * m2r0 + m1[4] * m2r1 + m1[8] * m2r2);
  m3[1] = (float) (m1[1] * m2r0 + m1[5] * m2r1 + m1[9] * m2r2);
  m3[2] = (float) (m1[2] * m2r0 + m1[6] * m2r1 + m1[10] * m2r2);
}

void inverse_transform44d3f(const double *m1, const float *m2, float *m3)
{
  double m2r0 = m2[0] - m1[3];
  double m2r1 = m2[1] - m1[7];
  double m2r2 = m2[2] - m1[11];
  m3[0] = (float) (m1[0] * m2r0 + m1[4] * m2r1 + m1[8] * m2r2);
  m3[1] = (float) (m1[1] * m2r0 + m1[5] * m2r1 + m1[9] * m2r2);
  m3[2] = (float) (m1[2] * m2r0 + m1[6] * m2r1 + m1[10] * m2r2);
}

void inverse_transform44d3d(const double *m1, const double *m2, double *m3)
{
  double m2r0 = m2[0] - m1[3];
  double m2r1 = m2[1] - m1[7];
  double m2r2 = m2[2] - m1[11];
  m3[0] = (float) (m1[0] * m2r0 + m1[4] * m2r1 + m1[8] * m2r2);
  m3[1] = (float) (m1[1] * m2r0 + m1[5] * m2r1 + m1[9] * m2r2);
  m3[2] = (float) (m1[2] * m2r0 + m1[6] * m2r1 + m1[10] * m2r2);
}

void transform44f4f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0];
  float m2r1 = m2[1];
  float m2r2 = m2[2];
  float m2r3 = m2[3];
  m3[0] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2 + m1[3] * m2r3;
  m3[1] = m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2 + m1[7] * m2r3;
  m3[2] = m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2 + m1[11] * m2r3;
  m3[3] = m1[12] * m2r0 + m1[13] * m2r1 + m1[14] * m2r2 + m1[15] * m2r3;
}

void initializeTTT44f(float *m)
{
  int a;
  for(a = 0; a < 16; a++)
    m[a] = _0;
  for(a = 0; a < 4; a++)
    m[4 * a + a] = _1;
}

void combineTTT44f44f(const float *m1, const float *m2, float *m3)


/* WARNING: this routine is ill-conceived and essentially broken */

/* NOTE: this is NOT equivalent to 4x4 matrix multiplication.
   TTTs are designed for easily creating movies of rotating 
   bodies! */
{
  float m1_homo[16];
  float m2_homo[16];
  const float *src;
  float *dst;
  float pre[3], post[3];

  /* convert the existing TTT into a homogenous transformation matrix */

  convertTTTfR44f(m1, m1_homo);
  convertTTTfR44f(m2, m2_homo);

  /* combine the matrices */

  left_multiply44f44f(m1_homo, m2_homo);

  /* now use the origin from the most recent TTT */

  src = m1 + 12;
  invert3f3f(src, pre);

  transform44f3fas33f3f(m2_homo, pre, post);

  m2_homo[3] += post[0];
  m2_homo[7] += post[1];
  m2_homo[11] += post[2];

  dst = m2_homo + 12;

  copy3f(src, dst);
  copy44f(m2_homo, m3);

}

void convertTTTfR44d(const float *ttt, double *homo)
{
  /* takes the PyMOL-specific TTT matrix and 
     makes a homogenous 4x4 txf matrix homo of it */

  double ttt_3 = (double) ttt[3];
  double ttt_7 = (double) ttt[7];
  double ttt_11 = (double) ttt[11];
  double ttt_12 = (double) ttt[12];
  double ttt_13 = (double) ttt[13];
  double ttt_14 = (double) ttt[14];

  /*  dump44f(ttt,"ttt"); */

  homo[0] = (double) ttt[0];
  homo[1] = (double) ttt[1];
  homo[2] = (double) ttt[2];
  homo[4] = (double) ttt[4];
  homo[5] = (double) ttt[5];
  homo[6] = (double) ttt[6];
  homo[8] = (double) ttt[8];
  homo[9] = (double) ttt[9];
  homo[10] = (double) ttt[10];

  homo[3] = (homo[0] * ttt_12) + (homo[1] * ttt_13) + (homo[2] * ttt_14) + ttt_3;
  homo[7] = (homo[4] * ttt_12) + (homo[5] * ttt_13) + (homo[6] * ttt_14) + ttt_7;
  homo[11] = (homo[8] * ttt_12) + (homo[9] * ttt_13) + (homo[10] * ttt_14) + ttt_11;

  homo[12] = 0.0;
  homo[13] = 0.0;
  homo[14] = 0.0;
  homo[15] = 1.0;

  /*  dump44d(homo, "homo"); */

}

void convertTTTfR44f(const float *ttt, float *homo)
{
  /* takes the PyMOL-specific TTT matrix and 
     makes a homogenous 4x4 txf matrix homo of it */

  float ttt_3 = ttt[3];
  float ttt_7 = ttt[7];
  float ttt_11 = ttt[11];
  float ttt_12 = ttt[12];
  float ttt_13 = ttt[13];
  float ttt_14 = ttt[14];

  homo[0] = ttt[0];
  homo[1] = ttt[1];
  homo[2] = ttt[2];
  homo[4] = ttt[4];
  homo[5] = ttt[5];
  homo[6] = ttt[6];
  homo[8] = ttt[8];
  homo[9] = ttt[9];
  homo[10] = ttt[10];

  homo[3] = (homo[0] * ttt_12) + (homo[1] * ttt_13) + (homo[2] * ttt_14) + ttt_3;
  homo[7] = (homo[4] * ttt_12) + (homo[5] * ttt_13) + (homo[6] * ttt_14) + ttt_7;
  homo[11] = (homo[8] * ttt_12) + (homo[9] * ttt_13) + (homo[10] * ttt_14) + ttt_11;

  homo[12] = 0.0;
  homo[13] = 0.0;
  homo[14] = 0.0;
  homo[15] = 1.0;

}

void convert44d44f(const double *dbl, float *flt)
{
  flt[0] = (float) dbl[0];
  flt[1] = (float) dbl[1];
  flt[2] = (float) dbl[2];
  flt[3] = (float) dbl[3];
  flt[4] = (float) dbl[4];
  flt[5] = (float) dbl[5];
  flt[6] = (float) dbl[6];
  flt[7] = (float) dbl[7];
  flt[8] = (float) dbl[8];
  flt[9] = (float) dbl[9];
  flt[10] = (float) dbl[10];
  flt[11] = (float) dbl[11];
  flt[12] = (float) dbl[12];
  flt[13] = (float) dbl[13];
  flt[14] = (float) dbl[14];
  flt[15] = (float) dbl[15];
}

void convert44f44d(const float *flt, double *dbl)
{
  dbl[0] = (double) flt[0];
  dbl[1] = (double) flt[1];
  dbl[2] = (double) flt[2];
  dbl[3] = (double) flt[3];
  dbl[4] = (double) flt[4];
  dbl[5] = (double) flt[5];
  dbl[6] = (double) flt[6];
  dbl[7] = (double) flt[7];
  dbl[8] = (double) flt[8];
  dbl[9] = (double) flt[9];
  dbl[10] = (double) flt[10];
  dbl[11] = (double) flt[11];
  dbl[12] = (double) flt[12];
  dbl[13] = (double) flt[13];
  dbl[14] = (double) flt[14];
  dbl[15] = (double) flt[15];
}

void convertR44dTTTf(const double *homo, float *ttt)
{
  /* nowadays, homogeneous matrices with (0,0,0,1) in 4th row are TTT
     compatible */
  convert44d44f(homo, ttt);
}

void multiply44d44d44d(const double *left, const double *right, double *product)
{
  double rA = right[0];
  double rB = right[4];
  double rC = right[8];
  double rD = right[12];

  product[0] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[4] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[8] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[12] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[1];
  rB = right[5];
  rC = right[9];
  rD = right[13];

  product[1] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[5] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[9] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[13] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[2];
  rB = right[6];
  rC = right[10];
  rD = right[14];

  product[2] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[6] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[10] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[14] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[3];
  rB = right[7];
  rC = right[11];
  rD = right[15];

  product[3] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[7] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[11] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[15] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;
}

void left_multiply44d44d(const double *left, double *right)
{
  double rA = right[0];
  double rB = right[4];
  double rC = right[8];
  double rD = right[12];

  right[0] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[4] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[8] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[12] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[1];
  rB = right[5];
  rC = right[9];
  rD = right[13];

  right[1] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[5] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[9] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[13] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[2];
  rB = right[6];
  rC = right[10];
  rD = right[14];

  right[2] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[6] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[10] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[14] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[3];
  rB = right[7];
  rC = right[11];
  rD = right[15];

  right[3] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[7] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[11] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[15] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;
}

void right_multiply44d44d(double *left, const double *right)
{
  double cA = left[0];
  double cB = left[1];
  double cC = left[2];
  double cD = left[3];

  left[0] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[1] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[2] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[3] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

  cA = left[4];
  cB = left[5];
  cC = left[6];
  cD = left[7];

  left[4] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[5] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[6] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[7] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

  cA = left[8];
  cB = left[9];
  cC = left[10];
  cD = left[11];

  left[8] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[9] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[10] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[11] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

  cA = left[12];
  cB = left[13];
  cC = left[14];
  cD = left[15];

  left[12] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[13] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[14] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[15] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

}

void multiply44f44f44f(const float *left, const float *right, float *product)
{
  float rA = right[0];
  float rB = right[4];
  float rC = right[8];
  float rD = right[12];

  product[0] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[4] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[8] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[12] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[1];
  rB = right[5];
  rC = right[9];
  rD = right[13];

  product[1] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[5] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[9] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[13] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[2];
  rB = right[6];
  rC = right[10];
  rD = right[14];

  product[2] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[6] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[10] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[14] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[3];
  rB = right[7];
  rC = right[11];
  rD = right[15];

  product[3] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  product[7] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  product[11] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  product[15] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;
}

void left_multiply44f44f(const float *left, float *right)
{
  float rA = right[0];
  float rB = right[4];
  float rC = right[8];
  float rD = right[12];

  right[0] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[4] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[8] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[12] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[1];
  rB = right[5];
  rC = right[9];
  rD = right[13];

  right[1] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[5] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[9] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[13] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[2];
  rB = right[6];
  rC = right[10];
  rD = right[14];

  right[2] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[6] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[10] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[14] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;

  rA = right[3];
  rB = right[7];
  rC = right[11];
  rD = right[15];

  right[3] = left[0] * rA + left[1] * rB + left[2] * rC + left[3] * rD;
  right[7] = left[4] * rA + left[5] * rB + left[6] * rC + left[7] * rD;
  right[11] = left[8] * rA + left[9] * rB + left[10] * rC + left[11] * rD;
  right[15] = left[12] * rA + left[13] * rB + left[14] * rC + left[15] * rD;
}

void right_multiply44f44f(float *left, const float *right)
{
  float cA = left[0];
  float cB = left[1];
  float cC = left[2];
  float cD = left[3];

  left[0] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[1] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[2] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[3] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

  cA = left[4];
  cB = left[5];
  cC = left[6];
  cD = left[7];

  left[4] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[5] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[6] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[7] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

  cA = left[8];
  cB = left[9];
  cC = left[10];
  cD = left[11];

  left[8] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[9] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[10] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[11] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

  cA = left[12];
  cB = left[13];
  cC = left[14];
  cD = left[15];

  left[12] = cA * right[0] + cB * right[4] + cC * right[8] + cD * right[12];
  left[13] = cA * right[1] + cB * right[5] + cC * right[9] + cD * right[13];
  left[14] = cA * right[2] + cB * right[6] + cC * right[10] + cD * right[14];
  left[15] = cA * right[3] + cB * right[7] + cC * right[11] + cD * right[15];

}

void invert_special44d44d(const double *orig, double *inv)
{
  /* inverse of the rotation matrix */

  inv[0] = orig[0];
  inv[1] = orig[4];
  inv[2] = orig[8];
  inv[4] = orig[1];
  inv[5] = orig[5];
  inv[6] = orig[9];
  inv[8] = orig[2];
  inv[9] = orig[6];
  inv[10] = orig[10];

  /* invert the translation portion */

  inv[3] = -(orig[3] * orig[0] + orig[7] * orig[4] + orig[11] * orig[8]);
  inv[7] = -(orig[3] * orig[1] + orig[7] * orig[5] + orig[11] * orig[9]);
  inv[11] = -(orig[3] * orig[2] + orig[7] * orig[6] + orig[11] * orig[10]);

  inv[12] = 0.0;
  inv[13] = 0.0;
  inv[14] = 0.0;
  inv[15] = 1.0;

}

void invert_special44f44f(const float *orig, float *inv)
{
  assert(orig != inv);

  /* inverse of the rotation matrix */

  inv[0] = orig[0];
  inv[1] = orig[4];
  inv[2] = orig[8];
  inv[4] = orig[1];
  inv[5] = orig[5];
  inv[6] = orig[9];
  inv[8] = orig[2];
  inv[9] = orig[6];
  inv[10] = orig[10];

  /* invert the translation portion */

  inv[3] = -(orig[3] * orig[0] + orig[7] * orig[4] + orig[11] * orig[8]);
  inv[7] = -(orig[3] * orig[1] + orig[7] * orig[5] + orig[11] * orig[9]);
  inv[11] = -(orig[3] * orig[2] + orig[7] * orig[6] + orig[11] * orig[10]);

  inv[12] = 0.0F;
  inv[13] = 0.0F;
  inv[14] = 0.0F;
  inv[15] = 1.0F;

}

static void normalize3dp(double *v1, double *v2, double *v3)
{
  double vlen = sqrt1d((v1[0] * v1[0]) + (v2[0] * v2[0]) + (v3[0] * v3[0]));
  if(vlen > R_SMALL) {
    v1[0] /= vlen;
    v2[0] /= vlen;
    v3[0] /= vlen;
  } else {
    // FIXME This looks wrong, should be all index 0!?
    v1[0] = _0;
    v2[1] = _0;
    v3[2] = _0;
  }
}

void reorient44d(double *matrix)
{
  double tmp[16];
  int a;

  /* restore orthogonality and recondition */

  for(a = 0; a < 3; a++) {
    normalize3d(matrix);
    normalize3d(matrix + 4);
    normalize3d(matrix + 8);
    cross_product3d(matrix + 4, matrix + 8, tmp);
    cross_product3d(matrix + 8, matrix, tmp + 4);
    cross_product3d(matrix, matrix + 4, tmp + 8);
    normalize3d(tmp);
    normalize3d(tmp + 4);
    normalize3d(tmp + 8);
    scale3d(tmp, 2.0, tmp);
    scale3d(tmp + 4, 2.0, tmp + 4);
    scale3d(tmp + 8, 2.0, tmp + 8);
    add3d(matrix, tmp, tmp);
    add3d(matrix + 4, tmp + 4, tmp + 4);
    add3d(matrix + 8, tmp + 8, tmp + 8);
    copy3d(tmp, matrix);
    copy3d(tmp + 4, matrix + 4);
    copy3d(tmp + 8, matrix + 8);
  }

  normalize3d(matrix);
  normalize3d(matrix + 4);
  normalize3d(matrix + 8);

  copy3d(matrix, tmp);
  pymol::remove_component3(matrix + 4, tmp, tmp + 4);
  cross_product3d(tmp, tmp + 4, tmp + 8);
  normalize3d(tmp + 4);
  normalize3d(tmp + 8);

  recondition44d(tmp);

  copy3d(tmp, matrix);
  copy3d(tmp + 4, matrix + 4);
  copy3d(tmp + 8, matrix + 8);

}

void recondition33d(double *matrix)
{
  normalize3d(matrix);
  normalize3d(matrix + 3);
  normalize3d(matrix + 6);
  normalize3dp(matrix + 0, matrix + 3, matrix + 6);
  normalize3dp(matrix + 1, matrix + 4, matrix + 7);
  normalize3dp(matrix + 2, matrix + 5, matrix + 8);
  normalize3d(matrix);
  normalize3d(matrix + 3);
  normalize3d(matrix + 6);
  normalize3dp(matrix + 0, matrix + 3, matrix + 6);
  normalize3dp(matrix + 1, matrix + 4, matrix + 7);
  normalize3dp(matrix + 2, matrix + 5, matrix + 8);
  normalize3d(matrix);
  normalize3d(matrix + 3);
  normalize3d(matrix + 6);
}

void recondition44d(double *matrix)
{
  normalize3d(matrix);
  normalize3d(matrix + 4);
  normalize3d(matrix + 8);
  normalize3dp(matrix + 0, matrix + 4, matrix + 8);
  normalize3dp(matrix + 1, matrix + 5, matrix + 9);
  normalize3dp(matrix + 2, matrix + 6, matrix + 10);
  normalize3d(matrix);
  normalize3d(matrix + 4);
  normalize3d(matrix + 8);
  normalize3dp(matrix + 0, matrix + 4, matrix + 8);
  normalize3dp(matrix + 1, matrix + 5, matrix + 9);
  normalize3dp(matrix + 2, matrix + 6, matrix + 10);
  normalize3d(matrix);
  normalize3d(matrix + 4);
  normalize3d(matrix + 8);
}

void invert_rotation_only44d44d(const double *orig, double *inv)
{
  /* inverse of the rotation matrix */

  inv[0] = orig[0];
  inv[1] = orig[4];
  inv[2] = orig[8];
  inv[4] = orig[1];
  inv[5] = orig[5];
  inv[6] = orig[9];
  inv[8] = orig[2];
  inv[9] = orig[6];
  inv[10] = orig[10];

  inv[3] = 0.0;
  inv[7] = 0.0;
  inv[11] = 0.0;

  inv[12] = 0.0;
  inv[13] = 0.0;
  inv[14] = 0.0;
  inv[15] = 1.0;

}

void transformTTT44f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0] + m1[12];
  float m2r1 = m2[1] + m1[13];
  float m2r2 = m2[2] + m1[14];
  m3[0] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2 + m1[3];
  m3[1] = m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2 + m1[7];
  m3[2] = m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2 + m1[11];
}

void transform_normalTTT44f3f(const float *m1, const float *m2, float *m3)
{
  float m2r0 = m2[0];
  float m2r1 = m2[1];
  float m2r2 = m2[2];
  m3[0] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2;
  m3[1] = m1[4] * m2r0 + m1[5] * m2r1 + m1[6] * m2r2;
  m3[2] = m1[8] * m2r0 + m1[9] * m2r1 + m1[10] * m2r2;
}

/**
 * Multiplies two row-major 3x3 matrices:
 *
 *     m3 = m1 * m2;
 *
 * m2 and m3 can be the same matrix (same memory buffer)
 */
void multiply33f33f(const float *m1, const float *m2, float *m3)
{
  int a;
  float m2r0, m2r1, m2r2;
  for(a = 0; a < 3; a++) {
    m2r0 = m2[a];
    m2r1 = m2[3 + a];
    m2r2 = m2[6 + a];
    m3[a] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2;
    m3[3 + a] = m1[3] * m2r0 + m1[4] * m2r1 + m1[5] * m2r2;
    m3[6 + a] = m1[6] * m2r0 + m1[7] * m2r1 + m1[8] * m2r2;
  }
}

void multiply33d33d(const double *m1, const double *m2, double *m3)
{                               /* m2 and m3 can be the same matrix */
  int a;
  double m2r0, m2r1, m2r2;
  for(a = 0; a < 3; a++) {
    m2r0 = m2[a];
    m2r1 = m2[3 + a];
    m2r2 = m2[6 + a];
    m3[a] = m1[0] * m2r0 + m1[1] * m2r1 + m1[2] * m2r2;
    m3[3 + a] = m1[3] * m2r0 + m1[4] * m2r1 + m1[5] * m2r2;
    m3[6 + a] = m1[6] * m2r0 + m1[7] * m2r1 + m1[8] * m2r2;
  }
}

void matrix_multiply33f33f(Matrix33f m1, Matrix33f m2, Matrix33f m3)
{
  multiply33f33f((float *) m1, (float *) m2, (float *) m3);
}

void matrix_multiply33d33d(Matrix33d m1, Matrix33d m2, Matrix33d m3)
{
  multiply33d33d((double *) m1[0], (double *) m2, (double *) m3);
}

float deg_to_rad(float angle)
{
  return ((float) ((angle * cPI) / 180.0));
}

float rad_to_deg(float angle)
{
  return ((float) (180.0 * (angle / cPI)));
}

void get_rotation_about3f3fTTTf(float angle, const float *dir, const float *origin, float *ttt)
{
  float rot[9];
  rotation_matrix3f(angle, dir[0], dir[1], dir[2], rot);
  ttt[0] = rot[0];
  ttt[1] = rot[1];
  ttt[2] = rot[2];
  ttt[4] = rot[3];
  ttt[5] = rot[4];
  ttt[6] = rot[5];
  ttt[8] = rot[6];
  ttt[9] = rot[7];
  ttt[10] = rot[8];
  ttt[12] = -origin[0];
  ttt[13] = -origin[1];
  ttt[14] = -origin[2];
  ttt[3] = origin[0];
  ttt[7] = origin[1];
  ttt[11] = origin[2];
  ttt[15] = 1.0F;
}

void rotation_to_matrix33f(const float *axis, float angle, Matrix33f mat)
{
  rotation_matrix3f(angle, axis[0], axis[1], axis[2], &mat[0][0]);
}

void rotation_matrix3f(float angle, float x, float y, float z, float *m)
{
  /* returns a row-major rotation matrix */

  int a, b;

  /* This function contributed by Erich Boleyn (erich@uruk.org) */
  float mag, s, c;
  float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;

  s = (float) sin(angle);
  c = (float) cos(angle);

  mag = (float) sqrt1f(x * x + y * y + z * z);
  if(mag >= R_SMALL) {
    x /= mag;
    y /= mag;
    z /= mag;

#define M(row,col)  m[row*3+col]

    xx = x * x;
    yy = y * y;
    zz = z * z;
    xy = x * y;
    yz = y * z;
    zx = z * x;
    xs = x * s;
    ys = y * s;
    zs = z * s;
    one_c = _1 - c;

    M(0, 0) = (one_c * xx) + c;
    M(0, 1) = (one_c * xy) - zs;
    M(0, 2) = (one_c * zx) + ys;

    M(1, 0) = (one_c * xy) + zs;
    M(1, 1) = (one_c * yy) + c;
    M(1, 2) = (one_c * yz) - xs;

    M(2, 0) = (one_c * zx) - ys;
    M(2, 1) = (one_c * yz) + xs;
    M(2, 2) = (one_c * zz) + c;
  } else {
    for(a = 0; a < 3; a++)
      for(b = 0; b < 3; b++)
        M(a, b) = 0;
    M(0, 0) = _1;
    M(1, 1) = _1;
    M(2, 2) = _1;
  }

}

#define get_angle USED_TO_RETURN_DEGREES

float get_dihedral3f(const float *v0, const float *v1, const float *v2, const float *v3)
{
  Vector3f d01, d21, d32, dd1, dd3, pos_d;
  float result = _0;

  subtract3f(v2, v1, d21);
  subtract3f(v0, v1, d01);
  subtract3f(v3, v2, d32);
  if(length3f(d21) < R_SMALL) {
    result = get_angle3f(d01, d32);
  } else {
    cross_product3f(d21, d01, dd1);
    cross_product3f(d21, d32, dd3);
    if((length3f(dd1) < R_SMALL) || (length3f(dd3) < R_SMALL)) {        /* degenerate cases */
      result = get_angle3f(d01, d32);   /* fall back to angle between vectors */
    } else {
      result = get_angle3f(dd1, dd3);
      cross_product3f(d21, dd1, pos_d);
      if(dot_product3f(dd3, pos_d) < _0)
        result = -result;
    }
  }
  return (result);
}

float get_angle3f(const float *v1, const float *v2)
{
  double denom;
  double result;
  double arg1, arg2;
  arg1 = ((v1[0] * (double)v1[0]) + (v1[1] * (double)v1[1]) + (v1[2] * (double)v1[2]));
  arg2 = ((v2[0] * (double)v2[0]) + (v2[1] * (double)v2[1]) + (v2[2] * (double)v2[2]));
  denom = sqrt1d(arg1) * sqrt1d(arg2);

  if(denom > R_SMALL){
    arg1 = (v1[0] * (double)v2[0] + v1[1] * (double)v2[1] + v1[2] * (double)v2[2]);
    result = arg1 / denom;
  } else
    result = _0;
  if(result < -_1)
    result = -_1;
  else if(result > _1)
    result = _1;

  return acosf(result);
}

void normalize23f(const float *v1, float *v2)
{
  double vlen;
  vlen = length3f(v1);
  if(vlen > R_SMALL) {
    v2[0] = (float) (v1[0] / vlen);
    v2[1] = (float) (v1[1] / vlen);
    v2[2] = (float) (v1[2] / vlen);
  } else {
    v2[0] = _0;
    v2[1] = _0;
    v2[2] = _0;
  }
}

void clamp3f(float *v1)
{
  if(v1[0] < _0)
    v1[0] = _0;
  if(v1[0] > _1)
    v1[0] = _1;
  if(v1[1] < _0)
    v1[1] = _0;
  if(v1[1] > _1)
    v1[1] = _1;
  if(v1[2] < _0)
    v1[2] = _0;
  if(v1[2] > _1)
    v1[2] = _1;
}

void normalize2f(float *v1)
{
  double vlen;
  vlen = length2f(v1);
  if(vlen > R_SMALL) {
    v1[0] /= vlen;
    v1[1] /= vlen;
  } else {
    v1[0] = _0;
    v1[1] = _0;
  }
}

void normalize4f(float *v1)
{
  v1[0] /= v1[3];
  v1[1] /= v1[3];
  v1[2] /= v1[3];
  v1[3] = 1.f;
}

double distance_line2point3f(const float *base, const float *normal, const float *point,
                             float *alongNormalSq)
{
  float hyp[3], adj[3];
  double result;

  hyp[0] = point[0] - base[0];
  hyp[1] = point[1] - base[1];
  hyp[2] = point[2] - base[2];

  project3f(hyp, normal, adj);

  (*alongNormalSq) = ((adj[0] * adj[0]) + (adj[1] * adj[1]) + (adj[2] * adj[2]));
  result = ((hyp[0] * hyp[0]) + (hyp[1] * hyp[1]) + (hyp[2] * hyp[2]))
    - (*alongNormalSq);
  if(result <= _0)
    return (_0);
  else
    return (sqrt1d(result));

}

double distance_halfline2point3f(const float *base, const float *normal, const float *point,
                                 float *alongNormalSq)
{
  float hyp[3], adj[3];
  double result;

  hyp[0] = point[0] - base[0];
  hyp[1] = point[1] - base[1];
  hyp[2] = point[2] - base[2];

  if(project3f(hyp, normal, adj) > _0) {
    (*alongNormalSq) = ((adj[0] * adj[0]) + (adj[1] * adj[1]) + (adj[2] * adj[2]));
    result = ((hyp[0] * hyp[0]) + (hyp[1] * hyp[1]) + (hyp[2] * hyp[2]))
      - (*alongNormalSq);
    if(result <= _0)
      return (_0);
    else
      return (sqrt1d(result));
  } else {
    return FLT_MAX;
  }
}

void matrix_transform33f3f(const Matrix33f m1, const float *v1, float *v2)
{
  v2[0] = m1[0][0] * v1[0] + m1[0][1] * v1[1] + m1[0][2] * v1[2];
  v2[1] = m1[1][0] * v1[0] + m1[1][1] * v1[1] + m1[1][2] * v1[2];
  v2[2] = m1[2][0] * v1[0] + m1[2][1] * v1[1] + m1[2][2] * v1[2];
}

void matrix_inverse_transform33f3f(const Matrix33f m1, const float *v1, float *v2)
{
  v2[0] = m1[0][0] * v1[0] + m1[1][0] * v1[1] + m1[2][0] * v1[2];
  v2[1] = m1[0][1] * v1[0] + m1[1][1] * v1[1] + m1[2][1] * v1[2];
  v2[2] = m1[0][2] * v1[0] + m1[1][2] * v1[1] + m1[2][2] * v1[2];
}

#if 0
double matdiffsq(float *v1, oMatrix5f m, float *v2)
{
  double dx, dy, dz;
  float vx, vy, vz;

  dx = v2[0] - m[3][0];
  dy = v2[1] - m[3][1];
  dz = v2[2] - m[3][2];

  vx = m[0][0] * dx + m[0][1] * dy + m[0][2] * dz;
  vy = m[1][0] * dx + m[1][1] * dy + m[1][2] * dz;
  vz = m[2][0] * dx + m[2][1] * dy + m[2][2] * dz;

  dx = (v1[0] - (vx + m[4][0]));
  dy = (v1[1] - (vy + m[4][1]));
  dz = (v1[2] - (vz + m[4][2]));

  return (dx * dx + dy * dy + dz * dz);

}
#endif

void transform5f3f(const oMatrix5f m, const float *v1, float *v2)
{
  double dx, dy, dz;
  double vx, vy, vz;

  dx = v1[0] - m[3][0];
  dy = v1[1] - m[3][1];
  dz = v1[2] - m[3][2];

  vx = m[0][0] * dx + m[0][1] * dy + m[0][2] * dz;
  vy = m[1][0] * dx + m[1][1] * dy + m[1][2] * dz;
  vz = m[2][0] * dx + m[2][1] * dy + m[2][2] * dz;

  v2[0] = (((float) vx) + m[4][0]);
  v2[1] = (((float) vy) + m[4][1]);
  v2[2] = (((float) vz) + m[4][2]);

}

void transform3d3f(const oMatrix3d m1, const float *v1, float *v2)
{
  int b;
  for(b = 0; b < 3; b++)
    v2[b] = m1[b][0] * v1[0] + m1[b][1] * v1[1] + m1[b][2] * v1[2];
}

void transform33d3f(const Matrix33d m1, const float *v1, float *v2)
{
  int b;
  for(b = 0; b < 3; b++)
    v2[b] = (float) (m1[b][0] * v1[0] + m1[b][1] * v1[1] + m1[b][2] * v1[2]);
}


/*

void matcopy  ( oMatrix5f to,oMatrix5f from )
{
  int a,b;
  for(a=0;a<5;a++)
	 for(b=0;b<3;b++)
		to[a][b] = from[a][b];
}

void mattran ( oMatrix5f nm, oMatrix5f om, int axis, float dist )
{
  matcopy(nm,om);
  nm[4][axis] += dist;
}

void matrot ( oMatrix5f nm, oMatrix5f om, int axis, float angle )
{
  oMatrix5f rm;

  float ca,sa;
  int a,b;

  ca = cos(angle);
  sa = sin(angle);

  switch(axis)
	 {
	 case 0:
		rm[0][0] = _1;		rm[0][1] = _0;		rm[0][2] = _0;
		rm[1][0] = _0;		rm[1][1] =  ca;		rm[1][2] =  sa;
		rm[2][0] = _0;		rm[2][1] = -sa;		rm[2][2] =  ca;
		break;
	 case 1:
		rm[0][0] =  ca;		rm[0][1] = _0;		rm[0][2] = -sa;
		rm[1][0] = _0;		rm[1][1] = _1;		rm[1][2] = _0;
		rm[2][0] =  sa;		rm[2][1] = _0;		rm[2][2] =  ca;
		break;
	 case 2:
		rm[0][0] =  ca;		rm[0][1] =  sa;		rm[0][2] = _0;
		rm[1][0] = -sa;		rm[1][1] =  ca;		rm[1][2] = _0;
		rm[2][0] = _0;		rm[2][1] = _0;		rm[2][2] = _1;
		break;
	 }
  for(a=0;a<3;a++)
	 {
		nm[3][a] = om[3][a];
		nm[4][a] = om[4][a];
		for(b=0;b<3;b++)
		  nm[a][b] = 
			 rm[a][0]*om[0][b] + 
			 rm[a][1]*om[1][b] +
			 rm[a][2]*om[2][b];
	 }
  
  normalize3f(nm[0]);
  normalize3f(nm[1]);
  normalize3f(nm[2]);

}
*/

void rotation_to_matrix(Matrix53f rot, const float *axis, float angle)
{
  rotation_matrix3f(angle, axis[0], axis[1], axis[2], &rot[0][0]);
}

static void find_axis(Matrix33d a, float *axis)
{
  double at[3][3], v[3][3], vt[3][3];
  double wr[3], wi[3];
  /*p[3][3]; */
  int x, y;

  recondition33d(&a[0][0]);     /* IMPORTANT! */

  for(x = 0; x < 3; x++) {
    for(y = 0; y < 3; y++) {
      at[y][x] = a[x][y];
    }
  }

  MatrixEigensolveC33d(nullptr,
      &at[0][0],  // input matrix
      wr,         // out: real component of eigenvalues
      wi,         // out: imag component of eigenvalues
      &vt[0][0]); // out: eigenvectors

  for(x = 0; x < 3; x++) {
    for(y = 0; y < 3; y++) {
      v[y][x] = vt[x][y];
    }
  }

  axis[0] = 0.0F;
  axis[1] = 0.0F;
  axis[2] = 0.0F;

  {
    double max_real = 0.0F, test_real;
    double min_imag = 1.0F, test_imag;
    float test_inp[3], test_out[3];

    for(x = 0; x < 3; x++) {    /* looking for an eigvalue of (1,0) */
      /*      printf("wr %8.3f wi %8.3f\n",wr[x],wi[x]);
         printf("%8.3f %8.3f %8.3f\n",
         v[0][x],v[1][x],v[2][x]); */
      test_real = fabs(wr[x]);
      test_imag = fabs(wi[x]);

      if((test_real >= max_real) && (test_imag <= min_imag)) {
        for(y = 0; y < 3; y++)
          test_inp[y] = (float) v[y][x];
        transform33d3f(a, test_inp, test_out);  /* confirm that axis is invariant to rotation */
        test_out[0] -= test_inp[0];
        test_out[1] -= test_inp[1];
        test_out[2] -= test_inp[2];
        if((test_out[0] * test_out[0] +
            test_out[1] * test_out[1] + test_out[2] * test_out[2]) < 0.1) {
          for(y = 0; y < 3; y++)
            axis[y] = test_inp[y];
          max_real = test_real;
          min_imag = test_imag;
        }
      } else {
        /*for(y=0;y<3;y++)
           v[y][x]=_0; */
      }
    }
  }
  /*
     printf("eigenvectors\n%8.3f %8.3f %8.3f\n",v[0][0],v[0][1],v[0][2]);
     printf("%8.3f %8.3f %8.3f\n",v[1][0],v[1][1],v[1][2]);
     printf("%8.3f %8.3f %8.3f\n",v[2][0],v[2][1],v[2][2]);

     printf("eigenvalues\n%8.3f %8.3f %8.3f\n",wr[0],wr[1],wr[2]);
     printf("%8.3f %8.3f %8.3f\n",wi[0],wi[1],wi[2]);
   */

  /*    matrix_multiply33d33d(a,v,p);

     printf("invariance\n");
     printf("%8.3f %8.3f %8.3f\n",p[0][0],p[0][1],p[0][2]);
     printf("%8.3f %8.3f %8.3f\n",p[1][0],p[1][1],p[1][2]);
     printf("%8.3f %8.3f %8.3f\n",p[2][0],p[2][1],p[2][2]);
   */

}

void matrix_to_rotation(Matrix53f rot, float *axis, float *angle)
{
  float perp[3], tmp[3], rperp[3], dirck[3];
  Matrix33d rot3d;
  Matrix53f rotck;
  int a, b;

#ifdef MATCHK
  printf("starting matrix\n");
  for(a = 0; a < 3; a++)
    printf("%8.3f %8.3f %8.3f\n", rot[a][0], rot[a][1], rot[a][2]);
#endif

  for(a = 0; a < 3; a++)
    for(b = 0; b < 3; b++)
      rot3d[a][b] = (double) rot[a][b];

  find_axis(rot3d, axis);

  /* find a perpendicular vector */

  perp[0] = axis[1] * axis[0] - axis[2] * axis[2];
  perp[1] = axis[2] * axis[1] - axis[0] * axis[0];
  perp[2] = axis[0] * axis[2] - axis[1] * axis[1];

  if(length3f(perp) < R_SMALL) {
    tmp[0] = axis[0];
    tmp[1] = -2 * axis[1];
    tmp[2] = axis[2];
    cross_product3f(axis, tmp, perp);
  }

  normalize3f(perp);

  transform33d3f(rot3d, perp, rperp);

  *angle = get_angle3f(perp, rperp);

  cross_product3f(perp, rperp, dirck);
  if(((dirck[0] * axis[0]) + (dirck[1] * axis[1]) + (dirck[2] * axis[2])) < _0)
    *angle = -*angle;

  /*  printf("angle %8.3f \n",*angle); */

  rotation_to_matrix(rotck, axis, *angle);

#ifdef MATCHK
  printf("reconstructed matrix: \n");
  for(a = 0; a < 3; a++)
    printf("%8.3f %8.3f %8.3f\n", rotck[a][0], rotck[a][1], rotck[a][2]);
  printf("\n");
#endif

}
void mult3f(const float *vsrc, const float val, float *vdest){
  vdest[0] = vsrc[0] * val;
  vdest[1] = vsrc[1] * val;
  vdest[2] = vsrc[2] * val;
}

void mult4f(const float *vsrc, const float val, float *vdest){
  vdest[0] = vsrc[0] * val;
  vdest[1] = vsrc[1] * val;
  vdest[2] = vsrc[2] * val;
  vdest[3] = vsrc[3] * val;
}

float max3(float val1, float val2, float val3){
  if (val1>val2){
    if (val1>val3){
      return val1;
    } else {
      return val3;
    }
  } else {
    if (val2>val3){
      return val2;
    } else {
      return val3;
    }
  }
}
float ave3(float val1, float val2, float val3){
  return ((val1+val2+val3)/3.f);
}
float ave2(float val1, float val2){
  return ((val1+val2)/2.f);
}

void white4f(float *rgba, float value){
  rgba[0] = value;
  rgba[1] = value;
  rgba[2] = value;
  rgba[3] = 1.0F;
}
void add4f(const float *v1, const float *v2, float *v3)
{
  v3[0] = v1[0] + v2[0];
  v3[1] = v1[1] + v2[1];
  v3[2] = v1[2] + v2[2];
  v3[3] = v1[3] + v2[3];
}

int countchrs(const char *str, char ch){
  int cnt = 0;
  const char *tmp = str;
  while((tmp = strchr(tmp, ch))) {
    cnt++;
    tmp++;
  }
  return cnt;
}

/**
 * sigmoid smoothstep function with
 * smooth(0) = 0
 * smooth(1) = 1
 * smooth'(0) = 0
 * smooth'(1) = 0
 */
float smooth(float x, float power)
{

  if(x <= 0.5F) {
    if(x <= 0.0F)
      return 0.0F;
    return 0.5F * powf(2.0F * x, power);
  }
  if(x >= 1.0F)
    return 1.0F;
  return 1.0F - (0.5F * powf(2.0F * (1.0F - x), power));
}

/**
 * Divides the unit circle radially into n segments with n >= 3.
 */
void subdivide(int n, float *x, float *y)
{
  int a;
  if(n < 3) {
    n = 3;
  }
  for(a = 0; a <= n; a++) {
    x[a] = (float) cos(a * 2 * PI / n);
    y[a] = (float) sin(a * 2 * PI / n);
  }
}

namespace pymol {
/**
 * Compute the arithmetic mean of an Nx3 array along the first axis.
 * @param data Flat array data with N*3 elements
 * @param[out] out 3-element output vector
 */
void meanNx3(float const* data, size_t N, float* out)
{
  double accum[3] = {};
  for (auto const data_end = data + N * 3; data != data_end; data += 3) {
    pymol::add3(accum, data, accum);
  }
  pymol::scale3(accum, 1.0 / N, out);
}
}
