/*--------------------------------------------------------------------
 *	$Id: libspotter.c,v 1.2.4.2 2002/01/17 17:42:52 pwessel Exp $
 *
 *   Copyright (c) 1999-2002 by P. Wessel
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; version 2 of the License.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   Contact info: www.soest.hawaii.edu/wessel
 *--------------------------------------------------------------------*/
/*
 * SPOTTER: functions for moving points along small circles on a sphere.
 *
 * Paul Wessel, University of Hawaii
 * June 09, 1999
 * Version 1.0
 *
 * The user-callable functions in this library are:
 *
 * spotter_init			: Load stage poles from file
 * spotter_backtrack		: Trace track from seamount to hotspot
 * spotter_forthtrack		: Trace track from hotspot to seamount
 *
 * programs must first call spotter_init() which reads a file of
 * backward stage poles.  Given the right flag it can convert these
 * to forward stage poles.
 *
 * Then to draw a hotspot track the program can:
 *	1. Draw FROM the hotspot TO a seamount: Use spotter_forthtrack
 *	2. Draw FROM a seamount BACK TO a hotspot: Use spotter_backtrack
 *
 * To draw crustal flowlines (seamounts motion over mantle) do select
 * flowline = TRUE when calling spotter_init and then:
 *	1. Draw FROM a hotspot TO a seamount: Use spotter_backtrack
 *	2. Draw FROM a seamount TO a hotspot (and beyond): Use spotter_forthtrack
 */

#include "spotter.h"

/* Internal functions */

void spotter_forward_poles (struct EULER p[], int ns);
void spotter_rotate_fwd (double lon, double lat, double *tlon, double *tlat, struct EULER *p);
void spotter_rotate_inv (double *lon, double *lat, double tlon, double tlat, struct EULER *p);
void matrix_to_pole (double T[3][3], double *plon, double *plat, double *w);
void matrix_transpose (double At[3][3], double A[3][3]);
void matrix_mult (double a[3][3], double b[3][3], double c[3][3]);
void make_rot_matrix (double lonp, double latp, double w, double R[3][3]);
void finite_to_stages (struct EULER p[], int n, int flag);


int spotter_init (char *file, struct EULER **p, int flowline, int finite, double *t_max)
{
	/* file;	Name of file with backward stage poles */
	/* p;		Pointer to stage pole array */
	/* flowline;	TRUE if flowlines rather than hotspot-tracks are needed */
	/* finite;	TRUE for finite (total construction poles) files [Default is stage poles] */
	/* t_max;	Extend earliest stage pole back to this age */
	FILE *fp;
	struct EULER *e;
	char  buffer[BUFSIZ];
	int n, nf, i = 0, n_alloc = GMT_SMALL_CHUNK;
	double x, y, last_t;

	e = (struct EULER *) GMT_memory (VNULL, n_alloc, sizeof (struct EULER), "libspotter");

	if ((fp = fopen (file, "r")) == NULL) {
		fprintf (stderr, "EULER: Cannot open stage pole file: %s\n", file);
		exit (EXIT_FAILURE);
	}

	last_t = (finite) ? 0.0 : DBL_MAX;
	while (fgets (buffer, 512, fp) != NULL) { /* Expects lon lat t0 t1 ccw-angle */
		if (buffer[0] == '#' || buffer[0] == '\n') continue;

		nf = sscanf (buffer, "%lf %lf %lf %lf %lf", &e[i].lon, &e[i].lat, &e[i].t_start, &e[i].t_stop, &e[i].omega);

		if (finite && nf == 4) e[i].omega = e[i].t_stop, e[i].t_stop = 0.0;	/* Only got 4 columns */
		
		if (e[i].t_stop >= e[i].t_start) {
			fprintf (stderr, "libspotter: ERROR: Stage rotation %d has start time younger than stop time\n", i);
			exit (EXIT_FAILURE);
		}
		e[i].duration = e[i].t_start - e[i].t_stop;
		if (finite) {
			if (e[i].t_start < last_t) {
				fprintf (stderr, "libspotter: ERROR: Finite rotations must go from youngest to oldest\n");
				exit (EXIT_FAILURE);
			}
			last_t = e[i].t_start;
		}
		else {
			if (e[i].t_stop > last_t) {
				fprintf (stderr, "libspotter: ERROR: Stage rotations must go from oldest to youngest\n");
				exit (EXIT_FAILURE);
			}
			last_t = e[i].t_stop;
			e[i].omega /= e[i].duration;	/* For finite, omega is opening angle, not rate; this will change in finite_to_stages */
		}
		
		e[i].omega_r = e[i].omega * D2R;
		e[i].sin_lat = sin (e[i].lat * D2R);
		e[i].cos_lat = cos (e[i].lat * D2R);
		x = e[i].lon * D2R;	y = e[i].lat * D2R;
		e[i].lon_r = x;		e[i].lat_r = y;
		i++;
		if (i == n_alloc) {
			n_alloc += GMT_SMALL_CHUNK;
			e = (struct EULER *) GMT_memory ((void *)e, n_alloc, sizeof (struct EULER), "libspotter");
		}
	}
	fclose (fp);
	
	n = i;

	if (finite) finite_to_stages (e, n, finite-1);	/* Convert finite poles to backward stage poles */
	
	/* Extend oldest stage pole back to t_max Ma */

	if ((*t_max) > 0.0 && e[0].t_start < (*t_max)) {
		fprintf (stderr, "Extending oldest stage pole back to %lg Ma\n", (*t_max));

		e[0].t_start = (*t_max);
		e[0].duration = e[0].t_start - e[0].t_stop;
	}
	else
		(*t_max) = e[0].t_start;

	e = (struct EULER *) GMT_memory ((void *)e, n, sizeof (struct EULER), "libspotter");
	if (flowline) spotter_forward_poles (e, n);
	*p = e;

	return (n);
}

void spotter_forward_poles (struct EULER p[], int ns)
{
	int i, j;
	double d_lon, tlon, tlat;

	for (i = ns-1; i > 0; i--) {	/* From current to previous stages */
		for (j = 0; j < i; j++) {	/* Rotate the older stage poles */
			d_lon = p[i].omega_r * p[i].duration;
			spotter_rotate_fwd (p[j].lon_r, p[j].lat_r, &tlon, &tlat, &p[i]);
			tlon += d_lon;
			spotter_rotate_inv (&p[j].lon_r, &p[j].lat_r, tlon, tlat, &p[i]);
		}
	}
	for (i = 0; i < ns; i++) {
		p[i].lon = p[i].lon_r * R2D;
		p[i].lat = p[i].lat_r * R2D;
		p[i].sin_lat = sin (p[i].lat_r);
		p[i].cos_lat = cos (p[i].lat_r);
		p[i].omega = -p[i].omega;
		p[i].omega_r = -p[i].omega_r;
	}
}

/* spotter_backtrack: Given a seamount location and age, trace the
 *	hotspot-track between this seamount and a seamount of 
 *	age t_zero.  For t_zero = 0 this means the hotspot
 */

int spotter_backtrack (double xp[], double yp[], double tp[], int np, struct EULER p[], int ns, double d_km, double t_zero, BOOLEAN do_time, double **c)
/* xp, yp;	Points, in RADIANS */
/* tp;		Age of feature in m.y. */
/* np;		# of points */
/* p;		Stage poles */
/* ns;		# of stage poles */
/* d_km;	Create track point every d_km km.  If == -1.0, return bend points only */
/* t_zero;	Backtrack up to this age */
/* do_time;	TRUE if we want to interpolate and return time along track, 2 if we just want stage # */
/* **c;		Pointer to return trac vector */
{
	int i, j, k, kk = 0, start_k, nd = 1, nn, n_alloc = 2 * GMT_CHUNK;
	BOOLEAN path, bend;
	double t, tt, dt, d_lon, tlon, dd, i_km, xnew, xx, yy, *track;
	double s_lat, c_lat, s_lon, c_lon, cc, ss, cs, i_nd;

	bend = (d_km <= (SMALL - 1.0));
	path = (bend || d_km > SMALL);

	if (path) {
		track = (double *) GMT_memory (VNULL, n_alloc, sizeof (double), "libspotter");
		i_km = EQ_RAD / d_km;
	}

	if (p[ns-1].t_stop > t_zero) t_zero = p[ns-1].t_stop;	/* In case we dont go all the way to zero */

	for (i = 0; i < np; i++) {

		if (path) {
			start_k = kk++;
			if (kk == n_alloc) {
				n_alloc += BIG_CHUNK;
				track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
			}
		}
		nn = 0;
			
		t = tp[i];
		while (t > t_zero) {	/* As long as we're not back at zero age */

			j = 0;
			while (j < ns && t <= p[j].t_stop) j++;	/* Find first applicable stage pole */
			if (j == ns) {
				fprintf (stderr, "libspotter: (spotter_backtrack) Ran out of stage poles for t = %lg\n", t);
				exit (EXIT_FAILURE);
			}
			dt = MIN (p[j].duration, t - MAX(p[j].t_stop, t_zero));
			d_lon = p[j].omega_r * dt;

			/* spotter_rotate_fwd (xp[i], yp[i], &tlon, &tlat, &p[j]); */

			xnew = xp[i] - p[j].lon_r;
			sincos (yp[i], &s_lat, &c_lat);
			sincos (xnew, &s_lon, &c_lon);
			cc = c_lat * c_lon;
			tlon = d_atan2 (c_lat * s_lon, p[j].sin_lat * cc - p[j].cos_lat * s_lat);
			s_lat = p[j].sin_lat * s_lat + p[j].cos_lat * cc;
			c_lat = sqrt (1.0 - s_lat * s_lat);
			ss = p[j].sin_lat * s_lat;
			cs = p[j].cos_lat * s_lat;

			if (path) {
				if (!bend) {
					nd = (int) ceil ((fabs (d_lon) * c_lat) * i_km);
					i_nd = 1.0 / nd;
					dd = d_lon * i_nd;
					tt = dt * i_nd;
				}
				track[kk++] = xp[i];
				if (kk == n_alloc) {
					n_alloc += BIG_CHUNK;
					track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
				}
				track[kk++] = yp[i];
				if (kk == n_alloc) {
					n_alloc += BIG_CHUNK;
					track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
				}
				if (do_time) {
					track[kk++] = (do_time == 2) ? (double)(ns - j) : t;
					if (kk == n_alloc) {
						n_alloc += BIG_CHUNK;
						track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
					}
				}
				for (k = 1; k < nd; k++) {
					/* spotter_rotate_inv (&xx, &yy, tlon + k * dd, tlat, &p[j]); */

					xnew = tlon + k * dd;
					sincos (xnew, &s_lon, &c_lon);
					cc = c_lat * c_lon;
					yy = d_asin (ss - p[j].cos_lat * cc);
					xx = p[j].lon_r + d_atan2 (c_lat * s_lon, p[j].sin_lat * cc + cs);

					if (xx < 0.0) xx += TWO_PI;
					if (xx >= TWO_PI) xx -= TWO_PI;
					track[kk++] = xx;
					if (kk == n_alloc) {
						n_alloc += BIG_CHUNK;
						track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
					}
					track[kk++] = yy;
					if (kk == n_alloc) {
						n_alloc += BIG_CHUNK;
						track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
					}
					if (do_time) {
						track[kk++] = (do_time == 2) ? (double)(ns - j) : t - k * tt;
						if (kk == n_alloc) {
							n_alloc += BIG_CHUNK;
							track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
						}
					}
				}
				nn += nd;
			}
			/* spotter_rotate_inv (&xp[i], &yp[i], tlon + d_lon, tlat, &p[j]); */
			xnew = tlon + d_lon;
			sincos (xnew, &s_lon, &c_lon);
			cc = c_lat * c_lon;
			yp[i] = d_asin (ss - p[j].cos_lat * cc);
			xp[i] = p[j].lon_r + d_atan2 (c_lat * s_lon, p[j].sin_lat * cc + cs);

			if (xp[i] < 0.0) xp[i] += TWO_PI;
			if (xp[i] >= TWO_PI) xp[i] -= TWO_PI;
			t -= dt;
		}
		if (path) {
			track[kk++] = xp[i];
			if (kk == n_alloc) {
				n_alloc += BIG_CHUNK;
				track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
			}
			track[kk++] = yp[i];
			if (kk == n_alloc) {
				n_alloc += BIG_CHUNK;
				track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
			}
			if (do_time) {
				track[kk++] = (do_time == 2) ? (double)(ns - j) : t;
				if (kk == n_alloc) {
					n_alloc += BIG_CHUNK;
					track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
				}
			}
			track[start_k] = nn+1;
		}
	}
	if (path) {
		track = (double *) GMT_memory ((void *)track, (size_t)kk, sizeof (double), "libspotter");
		*c = track;
		return (kk);
	}

	return (np);
}

/* spotter_forthtrack: Given a hotspot location and final age, trace the
 *	hotspot-track between the seamount created at t_zero and a
 *	seamount of age tp.  For t_zero = 0 this means from the hotspot.
 */

int spotter_forthtrack (double xp[], double yp[], double tp[], int np, struct EULER p[], int ns, double d_km, double t_zero, BOOLEAN do_time, double **c)
/* xp, yp;	Points, in RADIANS */
/* tp;		Age of feature in m.y. */
/* np;		# of points */
/* p;		Stage poles */
/* ns;		# of stage poles */
/* d_km;	Create track point every d_km km.  If == -1.0, return bend points only */
/* t_zero;	Foretrack from this age forward */
/* do_time;	TRUE if we want to interpolate and return time along track */
/* c;		Pointer to return trac vector */
{
	int i, j, k, kk = 0, start_k, nd = 1, nn, n_alloc = 2 * GMT_CHUNK;
	BOOLEAN path, bend;
	double t, tt, dt, d_lon, tlon, dd, i_km, xnew, xx, yy, *track;
	double s_lat, c_lat, s_lon, c_lon, cc, ss, cs, i_nd;

	bend = (d_km <= (SMALL - 1.0));
	path = (bend || d_km > SMALL);

	if (path) {
		track = (double *) GMT_memory (VNULL, n_alloc, sizeof (double), "libspotter");
		i_km = EQ_RAD / d_km;
	}

	if (p[ns-1].t_stop > t_zero) t_zero = p[ns-1].t_stop;	/* In case we dont go all the way to zero */

	for (i = 0; i < np; i++) {

		if (path) {
			start_k = kk++;
			if (kk == n_alloc) {
				n_alloc += BIG_CHUNK;
				track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
			}
		}
		nn = 0;

		t = t_zero;
		while (t < tp[i]) {	/* As long as we're not back at zero age */

			j = 0;
			while (j < ns && t < p[j].t_stop) j++;	/* Find first applicable stage pole */
			if (j == ns) {
				fprintf (stderr, "libspotter: (spotter_forthtrack) Ran out of stage poles for t = %lg\n", t);
				exit (EXIT_FAILURE);
			}
			dt = MIN (tp[i], p[j].t_start) - t;	/* Time interval to rotate */
			d_lon = p[j].omega_r * dt;		/* Rotation angle (radians) */
			/* spotter_rotate_fwd (xp[i], yp[i], &tlon, &tlat, &p[j]); */

			xnew = xp[i] - p[j].lon_r;
			sincos (yp[i], &s_lat, &c_lat);
			sincos (xnew, &s_lon, &c_lon);
			cc = c_lat * c_lon;
			tlon = d_atan2 (c_lat * s_lon, p[j].sin_lat * cc - p[j].cos_lat * s_lat);
			s_lat = p[j].sin_lat * s_lat + p[j].cos_lat * cc;
			c_lat = sqrt (1.0 - s_lat * s_lat);
			ss = p[j].sin_lat * s_lat;
			cs = p[j].cos_lat * s_lat;

			if (path) {
				if (!bend) {
					nd = (int) ceil ((fabs (d_lon) * c_lat) * i_km);
					i_nd = 1.0 / nd;
					dd = d_lon * i_nd;
					tt = dt * i_nd;
				}
				track[kk++] = xp[i];
				if (kk == n_alloc) {
					n_alloc += BIG_CHUNK;
					track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
				}
				track[kk++] = yp[i];
				if (kk == n_alloc) {
					n_alloc += BIG_CHUNK;
					track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
				}
				if (do_time) {
					track[kk++] = (do_time == 2) ? (double)(ns - j) : t;
					if (kk == n_alloc) {
						n_alloc += BIG_CHUNK;
						track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
					}
				}
				for (k = 1; k < nd; k++) {
					/* spotter_rotate_inv (&xx, &yy, tlon - k * dd, tlat, &p[j]); */
					xnew = tlon - k * dd;
					sincos (xnew, &s_lon, &c_lon);
					cc = c_lat * c_lon;
					yy = d_asin (ss - p[j].cos_lat * cc);
					xx = p[j].lon_r + d_atan2 (c_lat * s_lon, p[j].sin_lat * cc + cs);

					if (xx < 0.0) xx += TWO_PI;
					if (xx >= TWO_PI) xx -= TWO_PI;
					track[kk++] = xx;
					if (kk == n_alloc) {
						n_alloc += BIG_CHUNK;
						track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
					}
					track[kk++] = yy;
					if (kk == n_alloc) {
						n_alloc += BIG_CHUNK;
						track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
					}
					if (do_time) {
						track[kk++] = (do_time == 2) ? (double)(ns - j) : t + k * tt;
						if (kk == n_alloc) {
							n_alloc += BIG_CHUNK;
							track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
						}
					}
				}
				nn += nd;
			}
			/* spotter_rotate_inv (&xp[i], &yp[i], tlon - d_lon, tlat, &p[j]); */
			xnew = tlon - d_lon;
			sincos (xnew, &s_lon, &c_lon);
			cc = c_lat * c_lon;
			yp[i] = d_asin (ss - p[j].cos_lat * cc);
			xp[i] = p[j].lon_r + d_atan2 (c_lat * s_lon, p[j].sin_lat * cc + cs);

			if (xp[i] < 0.0) xp[i] += TWO_PI;
			if (xp[i] >= TWO_PI) xp[i] -= TWO_PI;
			t += dt;
		}
		if (path) {
			track[kk++] = xp[i];
			if (kk == n_alloc) {
				n_alloc += BIG_CHUNK;
				track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
			}
			track[kk++] = yp[i];
			if (kk == n_alloc) {
				n_alloc += BIG_CHUNK;
				track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
			}
			if (do_time) {
				track[kk++] = (do_time == 2) ? (double)(ns - j) : t;
				if (kk == n_alloc) {
					n_alloc += BIG_CHUNK;
					track = (double *) GMT_memory ((void *)track, (size_t)n_alloc, sizeof (double), "libspotter");
				}
			}
			track[start_k] = nn+1;
		}
	}
	if (path) {
		track = (double *) GMT_memory ((void *)track, (size_t)kk, sizeof (double), "libspotter");
		*c = track;
		return (kk);
	}

	return (np);
}

void spotter_rotate_fwd (double lon, double lat, double *tlon, double *tlat, struct EULER *p)
{
	/* Given the pole position in p, oblique coordinates
	 * are computed from geographical coordinates assuming a spherical earth.
	 * All values in RADIANS
	 */
	 
	double dlon, cc, test, s_lat, c_lat, c_lon, s_lon;
	 
	dlon = lon - p->lon_r;
	sincos (lat, &s_lat, &c_lat);
	sincos (dlon, &s_lon, &c_lon);
	cc = c_lat * c_lon;
	test = p->sin_lat * s_lat + p->cos_lat * cc;
	*tlat = d_asin (test);
	*tlon = d_atan2 (c_lat * s_lon, p->sin_lat * cc - p->cos_lat * s_lat);
}
	 
void spotter_rotate_inv (double *lon, double *lat, double tlon, double tlat, struct EULER *p)
{
	/* Given the pole position in project_info, geographical coordinates 
	 * are computed from oblique coordinates assuming a spherical earth.
	 * All values in RADIANS
	 */
	 
	double dlon, test, s_lat, c_lat, c_lon, s_lon, cc;
	 
	dlon = tlon;
	sincos (tlat, &s_lat, &c_lat);
	sincos (dlon, &s_lon, &c_lon);
	cc = c_lat * c_lon;
	test = p->sin_lat * s_lat - p->cos_lat * cc;
	*lat = d_asin (test);
	*lon = p->lon_r + d_atan2 (c_lat * s_lon, p->sin_lat * cc + p->cos_lat * s_lat);
}

/* Converts a set of total reconstruction poles to stage poles
 *
 * Based partly on Cox and Hart, 1986
 */

void finite_to_stages (struct EULER p[], int n, int flag)
{
	int i, j;
	double *elon, *elat, *ew, t_old;
	double R_new[3][3], R_old[3][3], R_stage[3][3];
	struct EULER e_tmp;

	/* Expects total reconstruction models to have youngest poles first */
	
	elon = (double *) GMT_memory (VNULL, (size_t)n, sizeof (double), "libspotter");
	elat = (double *) GMT_memory (VNULL, (size_t)n, sizeof (double), "libspotter");
	ew   = (double *) GMT_memory (VNULL, (size_t)n, sizeof (double), "libspotter");
	
	memset ((void *)R_new, 0, (size_t)(9 * sizeof (double)));
	R_new[0][0] = R_new[1][1] = R_new[2][2] = 1.0;
	
	t_old = 0.0;
	for (i = 0; i < n; i++) {
		make_rot_matrix (p[i].lon, p[i].lat, p[i].omega, R_old);
		matrix_mult (R_new, R_old, R_stage);
		matrix_to_pole (R_stage, &elon[i], &elat[i], &ew[i]);
		if (elon[i] > 180.0) elon[i] -= 360.0;
		matrix_transpose (R_new, R_old);
		p[i].t_stop = t_old;
		t_old = p[i].t_start;
	}
	
	/* Time to put back */
	
	for (i = 0; i < n; i++) {
		p[i].lon = elon[i];
		p[i].lat = elat[i];
		p[i].duration = p[i].t_start - p[i].t_stop;
		p[i].omega = ew[i];
		if (flag == 0) p[i].omega /= p[i].duration;	/* get rates */
		p[i].omega_r = p[i].omega * D2R;
		p[i].sin_lat = sin (p[i].lat * D2R);
		p[i].cos_lat = cos (p[i].lat * D2R);
		p[i].lon_r = p[i].lon * D2R;	
		p[i].lat_r = p[i].lat * D2R;
	}

	GMT_free ((void *)elon);
	GMT_free ((void *)elat);
	GMT_free ((void *)ew);
	
	/* Flip order */
	
	for (i = 0; i < n/2; i++) {
		j = n - i - 1;
		if (i != j) {
			e_tmp = p[i];
			p[i] = p[j];
			p[j] = e_tmp;
		}
	}
}

void make_rot_matrix (double lonp, double latp, double w, double R[3][3])
{
/*	lonp, latp	Euler pole in degrees
 *	w		angular rotation in degrees
 *
 *	R		the rotation matrix
 */

	double E[3], sin_w, cos_w, c, E_x, E_y, E_z, E_12c, E_13c, E_23c;
	
	sincos (w * D2R, &sin_w, &cos_w);
        GMT_geo_to_cart (&latp, &lonp, E, TRUE);
	c = 1 - cos_w;

	E_x = E[0] * sin_w;
	E_y = E[1] * sin_w;
	E_z = E[2] * sin_w;
	E_12c = E[0] * E[1] * c;
	E_13c = E[0] * E[2] * c;
	E_23c = E[1] * E[2] * c;

	R[0][0] = E[0] * E[0] * c + cos_w;
	R[0][1] = E_12c - E_z;
	R[0][2] = E_13c + E_y;

	R[1][0] = E_12c + E_z;
	R[1][1] = E[1] * E[1] * c + cos_w;
	R[1][2] = E_23c - E_x;

	R[2][0] = E_13c - E_y;
	R[2][1] = E_23c + E_x;
	R[2][2] = E[2] * E[2] * c + cos_w;
}

void matrix_mult (double a[3][3], double b[3][3], double c[3][3])
{	/* C = A * B */
	int i, j, k;
	
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			c[i][j] = 0.0;
			for (k = 0; k < 3; k++) c[i][j] += a[i][k] * b[k][j];
		}
	}
}

void matrix_transpose (double At[3][3], double A[3][3])
{
	/* Computes the matrix transpose */

	int i, j;
	for (j = 0; j < 3; j++) {
		for (i = 0; i < 3; i++) {
			At[i][j] = A[j][i];
		}
	}
}

void matrix_to_pole (double T[3][3], double *plon, double *plat, double *w)
{
	double T13_m_T31, T32_m_T23, T21_m_T12, L, H, tr;
	
	T13_m_T31 = T[0][2] - T[2][0];
	T32_m_T23 = T[2][1] - T[1][2];
	T21_m_T12 = T[1][0] - T[0][1];
	H = T32_m_T23 * T32_m_T23 + T13_m_T31 * T13_m_T31;
	L = sqrt (H + T21_m_T12 * T21_m_T12);
	H = sqrt (H);
	tr = T[0][0] + T[1][1] + T[2][2];

	*plon = atan2 (T13_m_T31, T32_m_T23) * R2D;
	if (*plon < 0.0) (*plon) += 360.0;
	*plat = atan2 (T21_m_T12, H) * R2D;
	*w = atan2 (L, (tr - 1.0)) * R2D;
	if (*plat < 0.0) {	/* Make N hemisphere pole */
		*plat = -(*plat);
		*(plon) += 180.0;
		if (*plon > 360.0) *plon -=-360.0;
		*w = -(*w);
	}
}
