Index: src/libbn/tests/CMakeLists.txt
===================================================================
--- src/libbn/tests/CMakeLists.txt	(revision 59196)
+++ src/libbn/tests/CMakeLists.txt	(working copy)
@@ -13,6 +13,7 @@
 BRLCAD_ADDEXEC(tester_bn_plane_isect bn_plane_isect.c "libbu;libbn" NO_INSTALL)
 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)
 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)
@@ -703,6 +704,153 @@
 
 add_test(bn_mat_dup              tester_bn_mat 27 0)
 
+
+#
+#  *************** tabdata.c tests ***************
+#
+# For tester_bn_tabdata, input is as follows:
+# tester_bn_tabdata <function_number> <args>
+
+# For function #1 (bn_table_make_uniform), the <args> format is as follows:
+#
+# first last num expected
+#
+# where first last and num will be used to make a uniform table, and
+# expected is the expected result table.  A table is a list of
+# comma-separated numbers, where the first one is the number of
+# entries: nx,x0,x1,...,x(nx).
+
+add_test(bn_table_make_uniform tester_bn_tabdata 1 0 100 10 10,0,10,20,30,40,50,60,70,80,90,100)
+
+# For function #2 (bn_tabdata_add), the <args> format is as follows:
+#
+# table tabdata1 tabdata2 expected_tabdata
+#
+# where tabdata1 and tabdata2 will be added, and table is a table that
+# tabdata1 and tabdata2 use.  A table is a list of comma-separated
+# numbers, where the first one is the number of entries:
+# nx,x0,x1,...,x(nx); a tabdata is a list of comma-separated numbers
+# with the same length as the table to which it refers; if the table
+# entry is nx,x0,x1,...,x(nx), then the tabdata entry is
+# y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_add tester_bn_tabdata 2 5,0,20,40,60,80,100 1,2,3,4,5 8,3,6,2,9 9,5,9,6,14)
+
+# For function #3 (bn_tabdata_mul), the <args> format is as follows:
+#
+# table tabdata1 tabdata2 expected_tabdata
+#
+# where tabdata1 and tabdata2 will be multiplied, and table is a table
+# that tabdata1 and tabdata2 use.  A table is a list of
+# comma-separated numbers, where the first one is the number of
+# entries: nx,x0,x1,...,x(nx); a tabdata is a list of comma-separated
+# numbers with the same length as the table to which it refers; if the
+# table entry is nx,x0,x1,...,x(nx), then the tabdata entry is
+# y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_mul tester_bn_tabdata 3 5,0,20,40,60,80,100 1,2,3,4,5 8,3,6,2,9 8,6,18,8,45)
+
+# For function #4 (bn_tabdata_mul3), the <args> format is as follows:
+#
+# table tabdata1 tabdata2 tabdata3 expected_tabdata
+#
+# where tabdata1, tabdata2, and tabdata3 will be multiplied, and table
+# is a table that tabdata1, tabdata2, and tabdata3 use.  A table is a
+# list of comma-separated numbers, where the first one is the number
+# of entries: nx,x0,x1,...,x(nx); a tabdata is a list of
+# comma-separated numbers with the same length as the table to which
+# it refers; if the table entry is nx,x0,x1,...,x(nx), then the
+# tabdata entry is y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_mul3 tester_bn_tabdata 4 5,0,20,40,60,80,100 1,2,3,4,5 8,3,6,2,9 8,6,18,8,45 64,36,324,64,2025)
+
+
+# For function #5 (bn_tabdata_incr_mul3_scale), the <args> format is as follows:
+#
+# table tabdataout_orig tabdata1 tabdata2 tabdata3 scale expected_tabdata
+#
+# where tabdata1, tabdata2, and tabdata3, and scale will be
+# multiplied, and the result will be added to tabdataout_orig; table
+# is a table that tabdata1, tabdata2, and tabdata3 use.  A table is a
+# list of comma-separated numbers, where the first one is the number
+# of entries: nx,x0,x1,...,x(nx); a tabdata is a list of
+# comma-separated numbers with the same length as the table to which
+# it refers; if the table entry is nx,x0,x1,...,x(nx), then the
+# tabdata entry is y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_incr_mul3_scale tester_bn_tabdata 5 5,0,20,40,60,80,100 5,4,3,2,1 1,2,3,4,5 8,3,6,2,9 8,6,18,8,45 2 133,76,651,130,4051)
+
+
+# For function #6 (bn_tabdata_incr_mul2_scale), the <args> format is as follows:
+#
+# table tabdataout_orig tabdata1 tabdata2 scale expected_tabdata
+#
+# where tabdata1 and tabdata2, and scale will be multiplied, and the
+# result will be added to tabdataout_orig; table is a table that
+# tabdata and tabdata2 use.  A table is a list of comma-separated
+# numbers, where the first one is the number of entries:
+# nx,x0,x1,...,x(nx); a tabdata is a list of comma-separated numbers
+# with the same length as the table to which it refers; if the table
+# entry is nx,x0,x1,...,x(nx), then the tabdata entry is
+# y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_incr_mul2_scale tester_bn_tabdata 6 5,0,20,40,60,80,100 5,4,3,2,1 1,2,3,4,5 8,3,6,2,9 2 21,16,39,18,91)
+
+
+# For function #7 (bn_tabdata_scale), the <args> format is as follows:
+#
+# table tabdata scale expected_tabdata
+#
+# where tabdata will be scaled by scale.  A table is a list of
+# comma-separated numbers, where the first one is the number of
+# entries: nx,x0,x1,...,x(nx); a tabdata is a list of comma-separated
+# numbers with the same length as the table to which it refers; if the
+# table entry is nx,x0,x1,...,x(nx), then the tabdata entry is
+# y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_scale tester_bn_tabdata 7 5,0,20,40,60,80,100 1,2,3,4,5 2 2,4,6,8,10)
+
+
+# For function #8 (bn_table_scale), the <args> format is as follows:
+#
+# table scale expected_table
+#
+# where table will be scaled by scale.  A table is a list of
+# comma-separated numbers, where the first one is the number of
+# entries: nx,x0,x1,...,x(nx).
+
+add_test(bn_table_scale tester_bn_tabdata 8 5,0,20,40,60,80,100 2 5,0,40,80,120,160,200)
+
+
+# For function #9 (bn_tabdata_join1), the <args> format is as follows:
+#
+# table tabdata1 tabdata2 scale expected_tabdata
+#
+# where tabdata1, tabdata2, and scale will be combined via
+# bn_tabdata_join1, and the result will be checked against
+# expected_tabdata. A table is a list of comma-separated numbers,
+# where the first one is the number of entries: nx,x0,x1,...,x(nx); a
+# tabdata is a list of comma-separated numbers with the same length as
+# the table to which it refers; if the table entry is
+# nx,x0,x1,...,x(nx), then the tabdata entry is y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_join1 tester_bn_tabdata 9 5,0,20,40,60,80,100 1,2,3,4,5 5,4,3,2,1 2 11,10,9,8,7)
+
+
+# For function #10 (bn_tabdata_join2), the <args> format is as follows:
+#
+# table tabdata1 tabdata2 tabdata3 scale1 scale2 expected_tabdata
+#
+# where tabdata1, tabdata2, and scale will be combined via
+# bn_tabdata_join2, and the result will be checked against
+# expected_tabdata. A table is a list of comma-separated numbers,
+# where the first one is the number of entries: nx,x0,x1,...,x(nx); a
+# tabdata is a list of comma-separated numbers with the same length as
+# the table to which it refers; if the table entry is
+# nx,x0,x1,...,x(nx), then the tabdata entry is y0,y1,...y(nx-1)
+
+add_test(bn_tabdata_join2 tester_bn_tabdata 10 5,0,20,40,60,80,100 1,2,3,4,5 5,4,3,2,1 2,4,6,8,10 2 3 17,22,27,32,37)
+
 # Local Variables:
 # tab-width: 8
 # mode: cmake
Index: src/libbn/tests/bn_tabdata.c
===================================================================
--- src/libbn/tests/bn_tabdata.c	(revision 0)
+++ src/libbn/tests/bn_tabdata.c	(working copy)
@@ -0,0 +1,430 @@
+/*                      B N _ T A B D A T A . C
+ * BRL-CAD
+ *
+ * Copyright (c) 2013 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 "common.h"
+
+#include <math.h>
+#include <string.h>
+#include "bio.h"
+
+#include "bu.h"
+#include "vmath.h"
+#include "bn.h"
+
+static int
+tab_equal(const struct bn_table *a, const struct bn_table *b)
+{
+    unsigned int i;
+
+    if (a->nx != b->nx) {
+	return 1;
+    }
+
+    for (i = 0; i < a->nx; i++) {
+	if (!NEAR_EQUAL(a->x[i], b->x[i], BN_TOL_DIST)) {
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+
+static int
+tabdata_equal(const struct bn_tabdata *a, const struct bn_tabdata *b)
+{
+    unsigned int i;
+
+    if (a->ny != b->ny) {
+	return 1;
+    }
+
+    for (i = 0; i < a->ny; i++) {
+	if (!NEAR_EQUAL(a->y[i], b->y[i], BN_TOL_DIST)) {
+	    return 0;
+	}
+    }
+
+    return 1;
+}
+
+/* This function expects the string that is passed in to hold a table
+ * of the form nx,x0,x1,x2,...,x(nx) (note: there are nx+1 items after nx).
+ */
+static void
+scan_tab_args(char *in, struct bn_table **tab_in)
+{
+    int i;
+    int nx;
+    int offset;
+    int new_offset;
+    struct bn_table *tab;
+
+    sscanf(in, "%d%n", &nx, &offset);
+    new_offset = offset;
+
+    BN_GET_TABLE(tab, nx);
+
+    for (i = 0; i <= nx; i++) {
+	sscanf(in+offset, ",%lg%n", &((tab->x)[i]), &new_offset);
+	offset += new_offset;
+    }
+    *tab_in = tab;
+}
+
+/* This function expects the string that is passed in to hold a tabdata
+ * of the form y0,y1,y2,...,y(nx-1). The number of ys is determined by
+ * the table argument's nx.
+ */
+static void
+scan_tabdata_args(char *in, struct bn_tabdata **tabdata_in, struct bn_table *table)
+{
+    int i;
+    struct bn_tabdata *tabdata;
+    int new_offset;
+    int offset;
+
+    BN_GET_TABDATA(tabdata, table);
+
+    sscanf(in, "%lg%n", &((tabdata->y)[0]), &offset);
+    for (i = 1; i <= (int)tabdata->ny; i++) {
+	sscanf(in+offset, ",%lg%n", &((tabdata->y)[i]), &new_offset);
+	offset += new_offset;
+    }
+    *tabdata_in = tabdata;
+}
+
+static int
+test_bn_table_make_uniform(int argc, char *argv[])
+{
+    struct bn_table *expected;
+    struct bn_table *actual;
+    double first, last;
+    unsigned int num;
+
+    if (argc != 6) {
+	bu_exit(1, "<args> format: first last num expected [%s]\n", argv[0]);
+    }
+
+    sscanf(argv[2], "%lf", &first);
+    sscanf(argv[3], "%lf", &last);
+    sscanf(argv[4], "%u", &num);
+    scan_tab_args(argv[5], &expected);
+
+    actual = bn_table_make_uniform(num, first, last);
+
+    bn_pr_table("Result", actual);
+
+    return !tab_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_add(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+
+    if (argc != 6) {
+	bu_exit(1, "<args> format: table tabdata1 tabdata2 expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &td_in1, tab_in);
+    scan_tabdata_args(argv[4], &td_in2, tab_in);
+    scan_tabdata_args(argv[5], &expected, tab_in);
+    BN_GET_TABDATA(actual, tab_in);
+
+    bn_tabdata_add(actual, td_in1, td_in2);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_mul(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+
+    if (argc != 6) {
+	bu_exit(1, "<args> format: table tabdata1 tabdata2 expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &td_in1, tab_in);
+    scan_tabdata_args(argv[4], &td_in2, tab_in);
+    scan_tabdata_args(argv[5], &expected, tab_in);
+    BN_GET_TABDATA(actual, tab_in);
+
+    bn_tabdata_mul(actual, td_in1, td_in2);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_mul3(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *td_in3;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+
+    if (argc != 7) {
+	bu_exit(1, "<args> format: table tabdata1 tabdata2 tabdata3 expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &td_in1, tab_in);
+    scan_tabdata_args(argv[4], &td_in2, tab_in);
+    scan_tabdata_args(argv[5], &td_in3, tab_in);
+    scan_tabdata_args(argv[6], &expected, tab_in);
+    BN_GET_TABDATA(actual, tab_in);
+
+    bn_tabdata_mul3(actual, td_in1, td_in2, td_in3);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_incr_mul3_scale(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *td_in3;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+    fastf_t scale;
+
+    if (argc != 9) {
+	bu_exit(1, "<args> format: table tabdataout_orig tabdata1 tabdata2 tabdata3 scale expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &actual, tab_in);
+    scan_tabdata_args(argv[4], &td_in1, tab_in);
+    scan_tabdata_args(argv[5], &td_in2, tab_in);
+    scan_tabdata_args(argv[6], &td_in3, tab_in);
+    sscanf(argv[7], "%lg", &scale);
+    scan_tabdata_args(argv[8], &expected, tab_in);
+
+    bn_tabdata_incr_mul3_scale(actual, td_in1, td_in2, td_in3, scale);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_incr_mul2_scale(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+    fastf_t scale;
+
+    if (argc != 8) {
+	bu_exit(1, "<args> format: table tabdataout_orig tabdata1 tabdata2 scale expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &actual, tab_in);
+    scan_tabdata_args(argv[4], &td_in1, tab_in);
+    scan_tabdata_args(argv[5], &td_in2, tab_in);
+    sscanf(argv[6], "%lg", &scale);
+    scan_tabdata_args(argv[7], &expected, tab_in);
+
+    bn_tabdata_incr_mul2_scale(actual, td_in1, td_in2, scale);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_scale(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+    fastf_t scale;
+
+    if (argc != 6) {
+	bu_exit(1, "<args> format: table tabdata scale expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &td, tab_in);
+    sscanf(argv[4], "%lg", &scale);
+    scan_tabdata_args(argv[5], &expected, tab_in);
+    BN_GET_TABDATA(actual, tab_in);
+
+    bn_tabdata_scale(actual, td, scale);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_table_scale(int argc, char *argv[])
+{
+    struct bn_table *expected;
+    struct bn_table *actual;
+    fastf_t scale;
+
+    if (argc != 5) {
+	bu_exit(1, "<args> format: table scale expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &actual);
+    sscanf(argv[3], "%lg", &scale);
+    scan_tab_args(argv[4], &expected);
+
+    bn_table_scale(actual, scale);
+
+    bn_pr_table("Result", actual);
+
+    return !tab_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_join1(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+    fastf_t scale;
+
+    if (argc != 7) {
+	bu_exit(1, "<args> format: table tabdata1 tabdata2 scale expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &td_in1, tab_in);
+    scan_tabdata_args(argv[4], &td_in2, tab_in);
+    sscanf(argv[5], "%lg", &scale);
+    scan_tabdata_args(argv[6], &expected, tab_in);
+    BN_GET_TABDATA(actual, tab_in);
+
+    bn_tabdata_join1(actual, td_in1, scale, td_in2);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+static int
+test_bn_tabdata_join2(int argc, char *argv[])
+{
+    struct bn_table *tab_in;
+    struct bn_tabdata *td_in1;
+    struct bn_tabdata *td_in2;
+    struct bn_tabdata *td_in3;
+    struct bn_tabdata *expected;
+    struct bn_tabdata *actual;
+    fastf_t scale_1, scale_2;
+
+    if (argc != 9) {
+	bu_exit(1, "<args> format: table tabdata1 tabdata2 tabdata3 scale1 scale2 expected_tabdata [%s]\n", argv[0]);
+    }
+
+    scan_tab_args(argv[2], &tab_in);
+    scan_tabdata_args(argv[3], &td_in1, tab_in);
+    scan_tabdata_args(argv[4], &td_in2, tab_in);
+    scan_tabdata_args(argv[5], &td_in3, tab_in);
+    sscanf(argv[6], "%lg", &scale_1);
+    sscanf(argv[7], "%lg", &scale_2);
+    scan_tabdata_args(argv[8], &expected, tab_in);
+    BN_GET_TABDATA(actual, tab_in);
+
+    bn_tabdata_join2(actual, td_in1, scale_1, td_in2, scale_2, td_in3);
+
+    bn_pr_tabdata("Result", actual);
+
+    return !tabdata_equal(expected, actual);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    int function_num = 0;
+
+    if (argc < 3) {
+	bu_exit(1, "Argument format: <function_number> <args> [%s]\n", argv[0]);
+    }
+
+    sscanf(argv[1], "%d", &function_num);
+
+    switch (function_num) {
+    case 1:
+	return test_bn_table_make_uniform(argc, argv);
+    case 2:
+	return test_bn_tabdata_add(argc, argv);
+    case 3:
+	return test_bn_tabdata_mul(argc, argv);
+    case 4:
+	return test_bn_tabdata_mul3(argc, argv);
+    case 5:
+	return test_bn_tabdata_incr_mul3_scale(argc, argv);
+    case 6:
+	return test_bn_tabdata_incr_mul2_scale(argc, argv);
+    case 7:
+	return test_bn_tabdata_scale(argc, argv);
+    case 8:
+	return test_bn_table_scale(argc, argv);
+    case 9:
+	return test_bn_tabdata_join1(argc, argv);
+    case 10:
+	return test_bn_tabdata_join2(argc, argv);
+    }
+
+    bu_log("ERROR: function_num %d is not valid [%s]\n", function_num, argv[0]);
+    return 1;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-file-style: "stroustrup"
+ * End:
+ * ex: shiftwidth=4 tabstop=8
+ */
