Index: src/libbn/tests/CMakeLists.txt
===================================================================
--- src/libbn/tests/CMakeLists.txt	(revision 63699)
+++ src/libbn/tests/CMakeLists.txt	(working copy)
@@ -19,6 +19,7 @@
 BRLCAD_ADDEXEC(tester_bn_complex bn_complex.c "libbu;libbn" NO_INSTALL)
 BRLCAD_ADDEXEC(tester_bn_mat bn_mat.c "libbu;libbn" NO_INSTALL)
 BRLCAD_ADDEXEC(tester_bn_tabdata bn_tabdata.c "libbu;libbn" NO_INSTALL)
+BRLCAD_ADDEXEC(tester_bn_qmath bn_qmath.c "libbu;libbn" NO_INSTALL)
 CMAKEFILES(bn_chull_test003_bot.asc)
 CMAKEFILES(bn_chull_test004_bot.asc)
 CMAKEFILES(bn_poly_cubic_roots.c bn_poly_quartic_roots.c bn_obr.c)
@@ -1000,8 +1001,145 @@
 
 add_test(bn_tabdata_get_constval tester_bn_tabdata 20 5,0,20,40,60,80,100 3 3,3,3,3,3)
 
+#
+#  *************** qmath.c tests ***************
+#
+# For tester_bn_qmath, input is as follows:
+# tester_bn_qmath <function_number> <args>
+
+# For function #1 (quat_mat2quat), the <args> format is as follows:
+#
+# mat expected_quat
+#
+# where mat is a sequence of 16 comma-separated numbers representing a
+# mat_t matrix, and expected_quat is a sequence of 4 comma-separated
+# numbers representing a quaternion (X, Y, Z, W).
+
+add_test(bn_quat_mat2quat_identity tester_bn_qmath 1 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 0,0,0,1)
+add_test(bn_quat_mat2quat_trace_0_1 tester_bn_qmath 1 1,0,0,0,0,-1,0,0,0,0,-1,0,0,0,0,-1 1,0,0,0)
+add_test(bn_quat_mat2quat_trace_0_2 tester_bn_qmath 1 -1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,-1 0,1,0,0)
+add_test(bn_quat_mat2quat_trace_0_3 tester_bn_qmath 1 -1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,-1 0,0,1,0)
+
+# For function #2 (quat_quat2mat), the <args> format is as follows:
+#
+# quat expected_mat
+#
+# where quat is a sequence of 4 comma-separated numbers representing a
+# quaternion (X, Y, Z, W), and expected_mat is a sequence of 16
+# comma-separated numbers representing a mat_t matrix.
+
+add_test(bn_quat_quat2mat_identity tester_bn_qmath 2 0,0,0,1 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)
+add_test(bn_quat_quat2mat_trace_0_1 tester_bn_qmath 2 1,0,0,0 1,0,0,0,0,-1,0,0,0,0,-1,0,0,0,0,1)
+add_test(bn_quat_quat2mat_trace_0_2 tester_bn_qmath 2 0,1,0,0 -1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1)
+add_test(bn_quat_quat2mat_trace_0_3 tester_bn_qmath 2 0,0,1,0 -1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1)
+
+# For function #3 (quat_distance), the <args> format is as follows:
+#
+# q1 q2 expected_distance
+#
+# where q1 and q2 are sequences of 4 comma-separated numbers each
+# representing a quaternion (X, Y, Z, W), and expected_distance is the
+# correct distance between q1 and q2.
+
+add_test(bn_quat_distance_0 tester_bn_qmath 3 1,2,0,3 1,2,0,3 0)
+add_test(bn_quat_distance_1 tester_bn_qmath 3 1,2,0,3 -1,-2,0,-3 7.483)
+add_test(bn_quat_distance_2 tester_bn_qmath 3 4,2,3,1 0,0,2,3 5)
+
+# For function #4 (quat_double), the <args> format is as follows:
+#
+# q1 q2 expected_quat
+#
+# where q1, q2, and expected_quat are sequences of 4 comma-separated
+# numbers each representing a quaternion (X, Y, Z, W).
+
+add_test(bn_quat_double_0 tester_bn_qmath 4 0,2,0,1 0,2,0,1 0,0.894,0,0.447)
+add_test(bn_quat_double_1 tester_bn_qmath 4 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 0.900,0.177,0.355,0.179)
+
+# For function #5 (quat_bisect), the <args> format is as follows:
+#
+# q1 q2 expected_quat
+#
+# where q1, q2, and expected_quat are sequences of 4 comma-separated
+# numbers each representing a quaternion (X, Y, Z, W).
+
+add_test(bn_quat_bisect_0 tester_bn_qmath 5 0,0.894,0,0.447 0,0.894,0,0.447 0,0.894,0,0.447)
+add_test(bn_quat_bisect_1 tester_bn_qmath 5 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 0.657,0.327,0.653,0.187)
+
+# For function #6 (quat_slerp), the <args> format is as follows:
+#
+# q1 q2 f expected_quat
+#
+# where q1, q2, and expected_quat are sequences of 4 comma-separated
+# numbers each representing a quaternion (X, Y, Z, W), and f is a
+# single number which is the fraction between q1 and q2 to interpolate
+# for.
+
+add_test(bn_quat_slerp_same_0 tester_bn_qmath 6 0,0.894,0,0.447 0,0.894,0,0.447 0 0,0.894,0,0.447)
+add_test(bn_quat_slerp_same_0.5 tester_bn_qmath 6 0,0.894,0,0.447 0,0.894,0,0.447 0.5 0,0.894,0,0.447)
+add_test(bn_quat_slerp_same_0.783 tester_bn_qmath 6 0,0.894,0,0.447 0,0.894,0,0.447 0.783 0,0.894,0,0.447)
+add_test(bn_quat_slerp_same_1 tester_bn_qmath 6 0,0.894,0,0.447 0,0.894,0,0.447 1 0,0.894,0,0.447)
+add_test(bn_quat_slerp_diff_0 tester_bn_qmath 6 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 0 0.548,0.365,0.730,0.183)
+add_test(bn_quat_slerp_diff_0.5 tester_bn_qmath 6 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 0.5 0.657,0.327,0.653,0.187)
+add_test(bn_quat_slerp_diff_0.783 tester_bn_qmath 6 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 0.783 0.713,0.302,0.604,0.188)
+add_test(bn_quat_slerp_diff_1 tester_bn_qmath 6 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 1 0.753,0.282,0.564,0.188)
+
+# For function #7 (quat_sberp), the <args> format is as follows:
+#
+# q1 qa qb q2 f expected_quat
+#
+# where q1, qa, qb, q2, and expected_quat are sequences of 4
+# comma-separated numbers representing a quaternion (X, Y, Z, W), and
+# f is a single number which is the fraction between q1 and q2 to
+# interpolate for.
+
+add_test(bn_quat_sberp_same_0 tester_bn_qmath 7 0,0.894,0,0.447 1,0,0,0 0.5,0.5,0.5,0.5  0,0.894,0,0.447 0 0,0.894,0,0.447)
+add_test(bn_quat_sberp_same_0.5 tester_bn_qmath 7 0,0.894,0,0.447 1,0,0,0 0.5,0.5,0.5,0.5 0,0.894,0,0.447 0.5 0.723,0.529,0.230,0.380)
+add_test(bn_quat_sberp_same_0.783 tester_bn_qmath 7 0,0.894,0,0.447 1,0,0,0 0.5,0.5,0.5,0.5 0,0.894,0,0.447 0.783 0.391,0.740,0.241,0.491)
+add_test(bn_quat_sberp_same_1 tester_bn_qmath 7 0,0.894,0,0.447 1,0,0,0 0.5,0.5,0.5,0.5 0,0.894,0,0.447 1 0,0.894,0,0.447)
+add_test(bn_quat_sberp_diff_0 tester_bn_qmath 7 0.548,0.365,0.730,0.183 1,0,0,0 0.5,0.5,0.5,0.5 0.753,0.282,0.564,0.188 0 0.548,0.365,0.730,0.183)
+add_test(bn_quat_sberp_diff_0.5 tester_bn_qmath 7 0.548,0.365,0.730,0.183 1,0,0,0 0.5,0.5,0.5,0.5 0.753,0.282,0.564,0.188 0.5 0.828,0.302,0.392,0.264)
+add_test(bn_quat_sberp_diff_0.783 tester_bn_qmath 7 0.548,0.365,0.730,0.183 1,0,0,0 0.5,0.5,0.5,0.5 0.753,0.282,0.564,0.188 0.783 0.724,0.358,0.501,0.310)
+add_test(bn_quat_sberp_diff_1 tester_bn_qmath 7 0.548,0.365,0.730,0.183 1,0,0,0 0.5,0.5,0.5,0.5 0.753,0.282,0.564,0.188 1 0.753,0.282,0.564,0.188)
+
+# For function #8 (quat_make_nearest), the <args> format is as follows:
+#
+# q1 q2 f expected_result
+#
+# where q1, q2, and expected_result are sequences of 4 comma-separated
+# numbers, each representing a quaternion (X, Y, Z, W).
+
+add_test(bn_quat_make_nearest_same_0 tester_bn_qmath 8 0,0.894,0,0.447 0,0.894,0,0.447 0,0.894,0,0.447)
+add_test(bn_quat_make_nearest_same_1 tester_bn_qmath 8 0,-0.894,0,-0.447 0,0.894,0,0.447 0,0.894,0,0.447)
+add_test(bn_quat_make_nearest_diff_0 tester_bn_qmath 8 0.548,0.365,0.730,0.183 0.753,0.282,0.564,0.188 0.548,0.365,0.730,0.183)
+add_test(bn_quat_make_nearest_diff_1 tester_bn_qmath 8 -0.548,-0.365,-0.730,-0.183 0.753,0.282,0.564,0.188 0.548,0.365,0.730,0.183)
+
+# For function #9 (quat_exp), the <args> format is as follows:
+#
+# in expected_out
+#
+# where in and expected_out are sequences of 4 comma-separated
+# numbers, each representing a quaternion (X, Y, Z, W).
+
+add_test(bn_quat_exp_0 tester_bn_qmath 9 0,0,0,0 0,0,0,1)
+add_test(bn_quat_exp_1 tester_bn_qmath 9 0.577,0.577,0.577,0 0.486,0.486,0.486,0.541)
+add_test(bn_quat_exp_2 tester_bn_qmath 9 0.365,0.730,0.183,0 0.324,0.648,0.162,0.670)
+
+# For function #10 (quat_exp), the <args> format is as follows:
+#
+# in expected_out
+#
+# where in and expected_out are sequences of 4 comma-separated
+# numbers, each representing a quaternion (X, Y, Z, W).
+
+add_test(bn_quat_log_0 tester_bn_qmath 10 1,0,0,0 1.571,0,0,0)
+add_test(bn_quat_log_1 tester_bn_qmath 10 0.5,0.5,0.5,0.5 0.605,0.605,0.605,0)
+add_test(bn_quat_log_2 tester_bn_qmath 10 0.548,0.365,0.730,0.183 0.773,0.515,1.030,0)
+
+
 CMAKEFILES(bntester.dat)
 
+
+
 # Local Variables:
 # tab-width: 8
 # mode: cmake
Index: src/libbn/tests/bn_qmath.c
===================================================================
--- src/libbn/tests/bn_qmath.c	(revision 0)
+++ src/libbn/tests/bn_qmath.c	(working copy)
@@ -0,0 +1,255 @@
+/*                      B N _ Q M A T H . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2014 United States Government as represented by
+ * the U.S. Army Research Laboratory.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; see the file named COPYING for more
+ * information.
+ */
+
+#include <stdio.h>
+
+#include "vmath.h"
+#include "bn/tol.h"
+#include "bn/qmath.h"
+#include "bu/log.h"
+
+int
+test_quat_mat2quat(int argc, const char *argv[])
+{
+    mat_t mat;
+    quat_t expected_quat, actual_quat;
+
+    if (argc != 2) {
+	bu_exit(1, "ERROR: input format is: mat expected_quat\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 16, &(mat[0]), &(mat[1]), &(mat[2]), &(mat[3]), &(mat[4]), &(mat[5]), &(mat[6]), &(mat[7]), &(mat[8]), &(mat[9]), &(mat[10]), &(mat[11]), &(mat[12]), &(mat[13]), &(mat[14]), &(mat[15]));
+    bu_scan_fastf_t(NULL, argv[1], "," , 4, &(expected_quat[X]), &(expected_quat[Y]), &(expected_quat[Z]), &(expected_quat[W]));
+
+    quat_mat2quat(actual_quat, mat);
+
+    return !HNEAR_EQUAL(expected_quat, actual_quat, BN_TOL_DIST);
+}
+
+int
+test_quat_quat2mat(int argc, const char *argv[])
+{
+    quat_t quat;
+    mat_t expected_mat, actual_mat;
+    int i;
+
+    if (argc != 2) {
+	bu_exit(1, "ERROR: input format is: quat expected_mat\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], "," , 4, &(quat[X]), &(quat[Y]), &quat[Z], &(quat[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 16, &(expected_mat[0]), &(expected_mat[1]), &(expected_mat[2]), &(expected_mat[3]), &(expected_mat[4]), &(expected_mat[5]), &(expected_mat[6]), &(expected_mat[7]), &(expected_mat[8]), &(expected_mat[9]), &(expected_mat[10]), &(expected_mat[11]), &(expected_mat[12]), &(expected_mat[13]), &(expected_mat[14]), &(expected_mat[15]));
+
+    quat_quat2mat(actual_mat, quat);
+
+    for (i = 0; i < 16; i++) {
+	if (!NEAR_EQUAL(expected_mat[i], actual_mat[i], BN_TOL_DIST)) { return 1; }
+    }
+    return 0;
+}
+
+int
+test_quat_distance(int argc, const char *argv[])
+{
+    quat_t q1, q2;
+    double expected_dist, actual_dist;
+
+    if (argc != 3) {
+	bu_exit(1, "ERROR: input format is q1 q2 expected_dist\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q1[X]), &(q1[Y]), &(q1[Z]), &(q1[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(q2[X]), &(q2[Y]), &(q2[Z]), &(q2[W]));
+    bu_sscanf(argv[2], "%lf", &expected_dist);
+
+    actual_dist = quat_distance(q1, q2);
+    return !(NEAR_EQUAL(actual_dist, expected_dist, BN_TOL_DIST));
+}
+
+int
+test_quat_double(int argc, const char *argv[])
+{
+    quat_t q1, q2;
+    quat_t expected_quat, actual_quat;
+
+    if (argc != 3) {
+	bu_exit(1, "ERROR: input format is q1 q2 expected_quat\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q1[X]), &(q1[Y]), &(q1[Z]), &(q1[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(q2[X]), &(q2[Y]), &(q2[Z]), &(q2[W]));
+    bu_scan_fastf_t(NULL, argv[2], ",", 4, &(expected_quat[X]), &(expected_quat[Y]), &(expected_quat[Z]), &(expected_quat[W]));
+
+    quat_double(actual_quat, q1, q2);
+
+    return !HNEAR_EQUAL(expected_quat, actual_quat, BN_TOL_DIST);
+}
+
+int test_quat_bisect(int argc, const char *argv[])
+{
+    quat_t q1, q2;
+    quat_t expected_quat, actual_quat;
+
+    if (argc != 3) {
+	bu_exit(1, "ERROR: input format is q1 q2 expected_quat\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q1[X]), &(q1[Y]), &(q1[Z]), &(q1[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(q2[X]), &(q2[Y]), &(q2[Z]), &(q2[W]));
+    bu_scan_fastf_t(NULL, argv[2], ",", 4, &(expected_quat[X]), &(expected_quat[Y]), &(expected_quat[Z]), &(expected_quat[W]));
+
+    quat_bisect(actual_quat, q1, q2);
+
+    return !HNEAR_EQUAL(expected_quat, actual_quat, BN_TOL_DIST);
+}
+
+int test_quat_slerp(int argc, const char *argv[])
+{
+    quat_t q1, q2;
+    double f;
+    quat_t expected_quat, actual_quat;
+
+    if (argc != 4) {
+	bu_exit(1, "ERROR: input format is q1 q2 f expected_quat\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q1[X]), &(q1[Y]), &(q1[Z]), &(q1[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(q2[X]), &(q2[Y]), &(q2[Z]), &(q2[W]));
+    bu_sscanf(argv[2], "%lf", &f);
+    bu_scan_fastf_t(NULL, argv[3], ",", 4, &(expected_quat[X]), &(expected_quat[Y]), &(expected_quat[Z]), &(expected_quat[W]));
+
+    quat_slerp(actual_quat, q1, q2, f);
+
+    return !HNEAR_EQUAL(expected_quat, actual_quat, BN_TOL_DIST);
+}
+
+int test_quat_sberp(int argc, const char *argv[])
+{
+    quat_t q1, qa, qb, q2;
+    double f;
+    quat_t expected_quat, actual_quat;
+
+    if (argc != 6) {
+	bu_exit(1, "ERROR: input format is q1 qa qb q2 f expected_quat\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q1[X]), &(q1[Y]), &(q1[Z]), &(q1[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(qa[X]), &(qa[Y]), &(qa[Z]), &(qa[W]));
+    bu_scan_fastf_t(NULL, argv[2], ",", 4, &(qb[X]), &(qb[Y]), &(qb[Z]), &(qb[W]));
+    bu_scan_fastf_t(NULL, argv[3], ",", 4, &(q2[X]), &(q2[Y]), &(q2[Z]), &(q2[W]));
+    bu_sscanf(argv[4], "%lf", &f);
+    bu_scan_fastf_t(NULL, argv[5], ",", 4, &(expected_quat[X]), &(expected_quat[Y]), &(expected_quat[Z]), &(expected_quat[W]));
+
+    quat_sberp(actual_quat, q1, qa, qb, q2, f);
+
+    return !HNEAR_EQUAL(expected_quat, actual_quat, BN_TOL_DIST);
+}
+
+int test_quat_make_nearest(int argc, const char *argv[])
+{
+    quat_t q1, q2, expected_result;
+
+    if (argc != 3) {
+	bu_exit(1, "ERROR: input format is q1 q2 expected_result\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q1[X]), &(q1[Y]), &(q1[Z]), &(q1[W]));
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(q2[X]), &(q2[Y]), &(q2[Z]), &(q2[W]));
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(expected_result[X]), &(expected_result[Y]), &(expected_result[Z]), &(expected_result[W]));
+
+    quat_make_nearest(q1, q2);
+
+    return !HNEAR_EQUAL(expected_result, q1, BN_TOL_DIST);
+}
+
+int test_quat_exp(int argc, const char *argv[])
+{
+    quat_t in;
+    quat_t expected_out, actual_out;
+
+    if (argc != 2) {
+	bu_exit(1, "ERROR: input format is in expected_out\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(in[X]), &(in[Y]), &(in[Z]), &(in[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(expected_out[X]), &(expected_out[Y]), &(expected_out[Z]), &(expected_out[W]));
+
+    quat_exp(actual_out, in);
+
+    return !HNEAR_EQUAL(expected_out, actual_out, BN_TOL_DIST);
+}
+
+int test_quat_log(int argc, const char *argv[])
+{
+    quat_t in;
+    quat_t expected_out, actual_out;
+
+    if (argc != 2) {
+	bu_exit(1, "ERROR: input format is in expected_out\n");
+    }
+    bu_scan_fastf_t(NULL, argv[0], ",", 4, &(in[X]), &(in[Y]), &(in[Z]), &(in[W]));
+    bu_scan_fastf_t(NULL, argv[1], ",", 4, &(expected_out[X]), &(expected_out[Y]), &(expected_out[Z]), &(expected_out[W]));
+
+    quat_log(actual_out, in);
+
+    return !HNEAR_EQUAL(expected_out, actual_out, BN_TOL_DIST);
+}
+
+int
+main(int argc, const char *argv[])
+{
+  int function_num = 0;
+
+  if (argc < 3) {
+      bu_exit(1, "ERROR: input format is function_num function_test_args [%s]\n", argv[0]);
+  }
+
+  sscanf(argv[1], "%d", &function_num);
+
+  switch (function_num) {
+	case 1:
+	    return test_quat_mat2quat(argc - 2, argv + 2);
+	case 2:
+	    return test_quat_quat2mat(argc - 2, argv + 2);
+	case 3:
+	    return test_quat_distance(argc - 2, argv + 2);
+	case 4:
+	    return test_quat_double(argc - 2, argv + 2);
+	case 5:
+	    return test_quat_bisect(argc - 2, argv + 2);
+	case 6:
+	    return test_quat_slerp(argc - 2, argv + 2);
+	case 7:
+	    return test_quat_sberp(argc - 2, argv + 2);
+	case 8:
+	    return test_quat_make_nearest(argc - 2, argv + 2);
+	case 9:
+	    return test_quat_exp(argc - 2, argv + 2);
+	case 10:
+	    return test_quat_log(argc - 2, argv + 2);
+	default:
+	    bu_exit(1, "ERROR: Unrecognized function_num\n");
+  }
+
+  return 1;
+}
+
+
+/** @} */
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */
