int
main(int argc, char **argv)
{
    /* static to prevent longjmp clobber warning */
    static FILE *stream = NULL;
    static unsigned long line_num = 0;
    static unsigned long failed_cnt = 0;
    static unsigned long bomb_cnt = 0;
    static unsigned long success_cnt = 0;
    static int ret = 0;

    char buf[BUFSIZ];
    FILE *fp_in = NULL;
    char *endp = NULL;
    size_t string_length;
    int argv_idx;
    int c;
    char dt_fmt[50];  /* data type format string */
    char *buf_p1;
    char *buf_p;
    struct bn_tol tol;

    /* command line parameters */
    static unsigned long test_case_line_num = 0; /* static due to longjmp */
    static unsigned long function_num = 0; /* static due to longjmp */
    struct bu_vls input_file_name = BU_VLS_INIT_ZERO;
    struct bu_vls output_file_name = BU_VLS_INIT_ZERO;

    /* function parameter arrays */
    int i[50] = {0};
    long l[50] = {0};
    fastf_t d[50] = {0.0};
    unsigned long u[50] = {0};

    /* boolean variables */
    static int output_file_name_defined = 0; /* static due to longjmp */
    static int process_single_test_case = 0; /* static due to longjmp */
    static int process_single_function = 0; /* static due to longjmp */
    int input_file_name_defined = 0;
    int valid_function_number = 0;
    int process_test_case = 0;
    int early_exit = 0;
    static int found_eof = 0; /* static due to longjmp */

    /* set initial values in tol structure */
    tol.magic = BN_TOL_MAGIC;
    tol.dist = BN_TOL_DIST;
    tol.dist_sq = tol.dist * tol.dist;
    tol.perp = 1e-6;
    tol.para = 1.0 - tol.perp;


    if (argc < 2) {
	bu_log("Too few parameters, %d specified, at least 1 required\n", argc - 1);
	bu_exit(EXIT_FAILURE, USAGE);
    }

    while ((c = bu_getopt(argc, argv, "l:f:i:o:")) != -1) {
	switch (c) {
	    case 'l': /* test case line number */
		errno = 0;
		test_case_line_num = strtoul(bu_optarg, &endp, 10);
		if (errno) {
		    bu_log("Invalid test case line number '%s' '%s'\n", bu_optarg, strerror(errno));
		    bu_exit(EXIT_FAILURE, USAGE);
		}
		if ((*endp != '\0') || (bu_optarg == endp) || (strchr(bu_optarg, '-') != '\0')) {
		    bu_log("Invalid test case line number '%s'\n", bu_optarg);
		    bu_exit(EXIT_FAILURE, USAGE);
		}
		process_single_test_case = 1;
		break;
	    case 'f': /* function number */
		errno = 0;
		function_num = strtoul(bu_optarg, &endp, 10);
		if (errno) {
		    bu_log("Invalid function number '%s' '%s'\n", bu_optarg, strerror(errno));
		    bu_exit(EXIT_FAILURE, USAGE);
		}
		if ((*endp != '\0') || (bu_optarg == endp) || (strchr(bu_optarg, '-') != '\0')) {
		    bu_log("Invalid function number '%s'\n", bu_optarg);
		    bu_exit(EXIT_FAILURE, USAGE);
		}
		process_single_function = 1;
		break;
	    case 'i': /* input file name */
		string_length = strlen(bu_optarg);
		if (string_length >= BUFSIZ) {
		    bu_log("Input file name too long, length was %d but must be less than %d\n",
			   string_length, BUFSIZ);
		    bu_exit(EXIT_FAILURE, USAGE);
		}
		bu_vls_strcpy(&input_file_name, bu_optarg);
		input_file_name_defined = 1;
		break;
	    case 'o': /* output file name */
		string_length = strlen(bu_optarg);
		if (string_length >= BUFSIZ) {
		    bu_log("Output file name too long, length was %d but must be less than %d\n",
			   string_length, BUFSIZ);
		    bu_exit(EXIT_FAILURE, USAGE);
		}
		bu_vls_strcpy(&output_file_name, bu_optarg);
		output_file_name_defined = 1;
		break;
	    default:
		bu_log("Invalid option '%c'.\n", c);
		bu_exit(EXIT_FAILURE, USAGE);
		break;
	}
    }

    if (process_single_test_case && process_single_function) {
	bu_log("Can not specify both test case line number and function number.\n");
	early_exit = 1;
    }

    if (!input_file_name_defined) {
	bu_log("Input file name is required but was not specified.\n");
	early_exit = 1;
    }

    if (early_exit) {
	bu_vls_free(&input_file_name);
	bu_vls_free(&output_file_name);
	bu_exit(EXIT_FAILURE, USAGE);
    }

    if ((fp_in = fopen(bu_vls_addr(&input_file_name), "r")) == NULL) {
	bu_log("Cannot open input file (%s)\n", bu_vls_addr(&input_file_name));
	bu_vls_free(&input_file_name);
	bu_vls_free(&output_file_name);
	return EXIT_FAILURE;
    }


    if (output_file_name_defined) {
	if ((stream = fopen(bu_vls_addr(&output_file_name), "w")) == NULL) {
	    bu_log("Cannot create output file (%s)\n", bu_vls_addr(&output_file_name));
	    if (fclose(fp_in) != 0) {
		bu_log("Unable to close input file.\n");
	    }
	    bu_vls_free(&input_file_name);
	    bu_vls_free(&output_file_name);
	    return EXIT_FAILURE;
	}
    } else {
	stream = stderr;
    }

    /* all output after this point is sent to stream */

    fprintf(stream, "Command line parameters: bntester ");
    for (argv_idx = 1 ; argv_idx < argc ; argv_idx++) {
	fprintf(stream, "%s ", argv[argv_idx]);
    }
    fprintf(stream, "\n");

    if (process_single_test_case) {
	fprintf(stream, "Processing only test case on line number: %lu\n", test_case_line_num);
    }

    if (process_single_function) {
	fprintf(stream, "Processing all test cases for function number: %lu\n", function_num);
    }

    if (!process_single_test_case && !process_single_function) {
	fprintf(stream, "Processing all test cases.\n");
    }


    while (!found_eof) {
	if (line_num == ULONG_MAX) {
	    fprintf(stream, "ERROR: Input data file exceeded max %lu number of lines.\n", ULONG_MAX);
	    if (fclose(fp_in) != 0) {
		fprintf(stream, "Unable to close input file.\n");
	    }
	    if (output_file_name_defined) {
		if (fclose(stream) != 0) {
		    bu_log("Unable to close output file.\n");
		}
	    }
	    bu_vls_free(&input_file_name);
	    bu_vls_free(&output_file_name);
	    return EXIT_FAILURE;
	}
	line_num++;
	if (bu_fgets(buf, BUFSIZ, fp_in) == NULL) {
	    if (feof(fp_in)) {
		found_eof = 1;
		continue;
	    }
	    if (ferror(fp_in)) {
		perror("ERROR: Problem reading file, system error message");
		if (fclose(fp_in) != 0) {
		    fprintf(stream, "Unable to close input file.\n");
		}
	    } else {
		perror("Oddness reading input file");
	    }
	    bu_vls_free(&input_file_name);
	    bu_vls_free(&output_file_name);
	    return EXIT_FAILURE;
	} else {
	    /* Skip input data file lines which start with a '#' character
	     * or a new line character.
	     */
	    if ((buf[0] != '#') && (buf[0] != '\n')) {
		buf_p1 = strtok(buf, "\n");
		buf_p = strtok(buf_p1, ", ");

		/* The 1st parameter of the test case is always an unsigned
		 * long int which represents the function number. This logic
		 * validates the test case function number to ensure it is
		 * an unsigned long int.
		 */
		valid_function_number = 1;
		errno = 0;
		u[0] = strtoul(buf_p, &endp, 10);
		if (errno) {
		    fprintf(stream, "Read function number failed, line %lu error msg: '%s' string '%s'\n",
				  line_num, strerror(errno), buf_p);
		    valid_function_number = 0;
		} else if ((*endp != '\0') || (buf_p == endp) || (strchr(buf_p, '-') != '\0')) {
		    fprintf(stream, "Read function number failed, line %lu string '%s'\n", line_num, buf_p);
		    valid_function_number = 0;
		}

		/* This logic restricts processing of the test case(s) to
		 * only those specified by the bntester input parameters.
		 */
		process_test_case = 0;
		if (valid_function_number && process_single_test_case && (test_case_line_num == line_num)) {
		    process_test_case = 1;
		} else if (valid_function_number && process_single_function && (function_num == u[0])) {
		    process_test_case = 1;
		} else if (valid_function_number && !process_single_test_case && !process_single_function) {
		    process_test_case = 1;
		}

		if (process_test_case) {
		    /* Each case within this switch corresponds to each
		     * function to be tested.
		     */
		    switch (u[0]) {
			case 1: /* function 'bn_distsq_line3_pt3' */
			    bu_strlcpy(dt_fmt, "dddddddddd", sizeof(dt_fmt)); /* defines parameter data types */
			    if (parse_case(buf_p, i, l, d, u, dt_fmt, line_num, stream)) {
				/* Parse failed, skipping test case */
				ret = 1;
			    } else {
				double result;
				if (!BU_SETJUMP) {
				    /* try */
				    result = bn_distsq_line3_pt3(&d[0], &d[3], &d[6]);
				    if (!NEAR_EQUAL(result, d[9], VUNITIZE_TOL)) {
					ret = 1;
					failed_cnt++;
					fprintf(stream, "Failed function %lu test case on line %lu expected = %.15f result = %.15f\n",
						      u[0], line_num, d[9], result);
				    } else {
					success_cnt++;
				    }
				} else {
				    /* catch */
				    BU_UNSETJUMP;
				    ret = 1;
				    bomb_cnt++;
				    fprintf(stream, "Failed function %lu test case on line %lu bu_bomb encountered.\n", u[0], line_num);
				} BU_UNSETJUMP;
			    }
			    break;
			case 2: /* function 'bn_2line3_colinear' */
			    bu_strlcpy(dt_fmt, "ddddddddddddduddddi", sizeof(dt_fmt));
			    if (parse_case(buf_p, i, l, d, u, dt_fmt, line_num, stream)) {
				/* Parse failed, skipping test case */
				ret = 1;
			    } else {
				int result;
				if (!BU_SETJUMP) {
				    /* try */
				    tol.magic = u[1];
				    tol.dist = d[13];
				    tol.dist_sq = d[14];
				    tol.perp = d[15];
				    tol.para = d[16];
				    result = bn_2line3_colinear(&d[0], &d[3], &d[6], &d[9], d[12], &tol);
				    if (result != i[0]) {
					ret = 1;
					failed_cnt++;
					fprintf(stream, "Failed function %lu test case on line %lu expected = %d result = %d\n",
						      u[0], line_num, i[0], result);
				    } else {
					success_cnt++;
				    }
				} else {
				    /* catch */
				    BU_UNSETJUMP;
				    ret = 1;
				    bomb_cnt++;
				    fprintf(stream, "Failed function %lu test case on line %lu bu_bomb encountered.\n", u[0], line_num);
				} BU_UNSETJUMP;
			    }
			    break;
			case 3: /* function 'bn_isect_line3_line3' */
			    bu_strlcpy(dt_fmt, "dddddddddddddduddddi", sizeof(dt_fmt));
			    if (parse_case(buf_p, i, l, d, u, dt_fmt, line_num, stream)) {
				/* Parse failed, skipping test case */
				ret = 1;
			    } else {
				int result;
				fastf_t t_out = 0.0;
				fastf_t u_out = 0.0;
				int t_fail = 0;
				int u_fail = 0;

				if (!BU_SETJUMP) {
				    /* try */
				    tol.magic = u[1];
				    tol.dist = d[14];
				    tol.dist_sq = d[15];
				    tol.perp = d[16];
				    tol.para = d[17];
				    result = bn_isect_line3_line3(&t_out, &u_out, &d[2], &d[5], &d[8], &d[11], &tol);
				    if (result != i[0]) {
					ret = 1;
					failed_cnt++;
					fprintf(stream, "Failed function %lu test case on line %lu expected = %d result = %d\n",
						      u[0], line_num, i[0], result);
				    } else if (result == 0) {
					if (!NEAR_EQUAL(t_out, d[0], tol.dist)) {
					    ret = 1;
					    failed_cnt++;
					    fprintf(stream, "Failed function %lu test case on line %lu result = %d expected t = %.15f result t = %.15f\n",
							  u[0], line_num, result, d[0], t_out);
					} else {
					    success_cnt++;
					}
				    } else if (result == 1) {
					t_fail = !NEAR_EQUAL(t_out, d[0], tol.dist);
					u_fail = !NEAR_EQUAL(u_out, d[1], tol.dist);
					if (t_fail) {
					    fprintf(stream, "Failed function %lu test case on line %lu result = %d expected t = %.15f result t = %.15f\n",
							  u[0], line_num, result, d[0], t_out);
					}
					if (u_fail) {
					    fprintf(stream, "Failed function %lu test case on line %lu result = %d expected u = %.15f result u = %.15f\n",
							  u[0], line_num, result, d[1], u_out);
					}
					if (t_fail || u_fail) {
					    ret = 1;
					    failed_cnt++;
					} else {
					    /* No other output to validate when result matches expected and
					     * result is not 0 and not 1.
					     */
					    success_cnt++;
					}
				    } else {
					success_cnt++;
				    }
				} else {
				    /* catch */
				    BU_UNSETJUMP;
				    ret = 1;
				    bomb_cnt++;
				    fprintf(stream, "Failed function %lu test case on line %lu bu_bomb encountered.\n", u[0], line_num);
				} BU_UNSETJUMP;
			    }
			    break;
			case 4: /* function 'bn_isect_lseg3_lseg3' */
			    bu_strlcpy(dt_fmt, "dddddddddddddduddddi", sizeof(dt_fmt));
			    if (parse_case(buf_p, i, l, d, u, dt_fmt, line_num, stream)) {
				/* Parse failed, skipping test case */
				ret = 1;
			    } else {
				int result;
				fastf_t dist[2] = {0.0, 0.0};
				int d0_fail = 0;
				int d1_fail = 0;

				if (!BU_SETJUMP) {
				    /* try */
				    tol.magic = u[1];
				    tol.dist = d[14];
				    tol.dist_sq = d[15];
				    tol.perp = d[16];
				    tol.para = d[17];
				    result = bn_isect_lseg3_lseg3(&dist[0], &d[2], &d[5], &d[8], &d[11], &tol);
				    if (result != i[0]) {
					ret = 1;
					failed_cnt++;
					fprintf(stream, "Failed function %lu test case on line %lu expected = %d result = %d\n",
						      u[0], line_num, i[0], result);
				    } else if (result == 0 || result == 1) {
					d0_fail = !NEAR_EQUAL(dist[0], d[0], VUNITIZE_TOL);
					d1_fail = !NEAR_EQUAL(dist[1], d[1], VUNITIZE_TOL);
					if (d0_fail) {
					    fprintf(stream, "Failed function %lu test case on line %lu result = %d expected dist[0] = %.15f result dist[0] = %.15f\n",
							  u[0], line_num, result, d[0], dist[0]);
					}
					if (d1_fail) {
					    fprintf(stream, "Failed function %lu test case on line %lu result = %d expected dist[1] = %.15f result dist[1] = %.15f\n",
							  u[0], line_num, result, d[1], dist[1]);
					}
					if (d0_fail || d1_fail) {
					    ret = 1;
					    failed_cnt++;
					} else {
					    /* No other output to validate when result matches expected and
					     * result is not 0 and not 1.
					     */
					    success_cnt++;
					}
				    } else {
					success_cnt++;
				    }
				} else {
				    /* catch */
				    BU_UNSETJUMP;
				    ret = 1;
				    bomb_cnt++;
				    fprintf(stream, "Failed function %lu test case on line %lu bu_bomb encountered.\n", u[0], line_num);
				} BU_UNSETJUMP;
			    }
			    break;
			default:
			    fprintf(stream, "ERROR: Unknown function number %lu test case on line %lu, skipping test case.\n", u[0], line_num);
			    bu_vls_free(&input_file_name);
			    bu_vls_free(&output_file_name);
			    return EXIT_FAILURE;
			    break;
		    } /* End of function number switch */
		}
	    } /* End of if statement skipping lines starting with '#' or new line */
	}
    } /* End of while loop reading lines from data file */

    fprintf(stream, "Summary: %lu total test cases success.\n", success_cnt);
    fprintf(stream, "Summary: %lu total test cases failed.\n", failed_cnt);
    fprintf(stream, "Summary: %lu total test cases bomb.\n", bomb_cnt);

    if (output_file_name_defined) {
	bu_log("Summary: %lu total test cases success.\n", success_cnt);
	bu_log("Summary: %lu total test cases failed.\n", failed_cnt);
	bu_log("Summary: %lu total test cases bomb.\n", bomb_cnt);
    }

    fprintf(stream, "Done.\n");

    if (output_file_name_defined) {
	bu_log("Done.\n");
    }

    if (fclose(fp_in) != 0) {
	fprintf(stream, "Unable to close input file.\n");
    }

    if (output_file_name_defined) {
	if (fclose(stream) != 0) {
	    bu_log("Unable to close output file.\n");
	}
    }

    bu_vls_free(&input_file_name);
    bu_vls_free(&output_file_name);

    return ret;
}
Ejemplo n.º 2
0
int
bn_math_cmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
{
    void (*math_func)();
    struct bu_vls result;

    math_func = (void (*)())clientData; /* object-to-function cast */
    bu_vls_init(&result);

    if (math_func == bn_mat_mul) {
	mat_t o, a, b;
	if (argc < 3 || bn_decode_mat(a, argv[1]) < 16 ||
	    bn_decode_mat(b, argv[2]) < 16) {
	    bu_vls_printf(&result, "usage: %s matA matB", argv[0]);
	    goto error;
	}
	bn_mat_mul(o, a, b);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_inv || math_func == bn_mat_trn) {
	mat_t o, a;

	if (argc < 2 || bn_decode_mat(a, argv[1]) < 16) {
	    bu_vls_printf(&result, "usage: %s mat", argv[0]);
	    goto error;
	}
	(*math_func)(o, a);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_matXvec) {
	mat_t m;
	hvect_t i, o;
	if (argc < 3 || bn_decode_mat(m, argv[1]) < 16 ||
	    bn_decode_hvect(i, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s mat hvect", argv[0]);
	    goto error;
	}
	bn_matXvec(o, m, i);
	bn_encode_hvect(&result, o);
    } else if (math_func == bn_mat4x3pnt) {
	mat_t m;
	point_t i, o;
	if (argc < 3 || bn_decode_mat(m, argv[1]) < 16 ||
	    bn_decode_vect(i, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s mat point", argv[0]);
	    goto error;
	}
	bn_mat4x3pnt(o, m, i);
	bn_encode_vect(&result, o);
    } else if (math_func == bn_mat4x3vec) {
	mat_t m;
	vect_t i, o;
	if (argc < 3 || bn_decode_mat(m, argv[1]) < 16 ||
	    bn_decode_vect(i, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s mat vect", argv[0]);
	    goto error;
	}
	bn_mat4x3vec(o, m, i);
	bn_encode_vect(&result, o);
    } else if (math_func == bn_hdivide) {
	hvect_t i;
	vect_t o;
	if (argc < 2 || bn_decode_hvect(i, argv[1]) < 4) {
	    bu_vls_printf(&result, "usage: %s hvect", argv[0]);
	    goto error;
	}
	bn_hdivide(o, i);
	bn_encode_vect(&result, o);
    } else if (math_func == bn_vjoin1) {
	point_t o;
	point_t b, d;
	fastf_t c;

	if (argc < 4) {
	    bu_vls_printf(&result, "usage: %s pnt scale dir", argv[0]);
	    goto error;
	}
	if ( bn_decode_vect(b, argv[1]) < 3) goto error;
	if (Tcl_GetDouble(interp, argv[2], &c) != TCL_OK) goto error;
	if ( bn_decode_vect(d, argv[3]) < 3) goto error;

	VJOIN1( o, b, c, d );	/* bn_vjoin1( o, b, c, d ) */
	bn_encode_vect(&result, o);

    } else if ( math_func == bn_vblend) {
	point_t a, c, e;
	fastf_t b, d;

	if ( argc < 5 ) {
	    bu_vls_printf(&result, "usage: %s scale pnt scale pnt", argv[0]);
	    goto error;
	}

	if ( Tcl_GetDouble(interp, argv[1], &b) != TCL_OK) goto error;
	if ( bn_decode_vect( c, argv[2] ) < 3) goto error;
	if ( Tcl_GetDouble(interp, argv[3], &d) != TCL_OK) goto error;
	if ( bn_decode_vect( e, argv[4] ) < 3) goto error;

	VBLEND2( a, b, c, d, e )
	    bn_encode_vect( &result, a );

    } else if (math_func == bn_mat_ae) {
	mat_t o;
	double az, el;

	if (argc < 3) {
	    bu_vls_printf(&result, "usage: %s azimuth elevation", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &az) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[2], &el) != TCL_OK) goto error;

	bn_mat_ae(o, (fastf_t)az, (fastf_t)el);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_ae_vec) {
	fastf_t az, el;
	vect_t v;

	if (argc < 2 || bn_decode_vect(v, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s vect", argv[0]);
	    goto error;
	}

	bn_ae_vec(&az, &el, v);
	bu_vls_printf(&result, "%g %g", az, el);
    } else if (math_func == bn_aet_vec) {
	fastf_t az, el, twist, accuracy;
	vect_t vec_ae, vec_twist;

	if (argc < 4 || bn_decode_vect(vec_ae, argv[1]) < 3 ||
	    bn_decode_vect(vec_twist, argv[2]) < 3 ||
	    sscanf(argv[3], "%lf", &accuracy) < 1) {
	    bu_vls_printf(&result, "usage: %s vec_ae vec_twist accuracy",
			  argv[0]);
	    goto error;
	}

	bn_aet_vec(&az, &el, &twist, vec_ae, vec_twist, accuracy);
	bu_vls_printf(&result, "%g %g %g", az, el, twist);
    } else if (math_func == bn_mat_angles) {
	mat_t o;
	double alpha, beta, ggamma;

	if (argc < 4) {
	    bu_vls_printf(&result, "usage: %s alpha beta gamma", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &alpha) != TCL_OK)  goto error;
	if (Tcl_GetDouble(interp, argv[2], &beta) != TCL_OK)   goto error;
	if (Tcl_GetDouble(interp, argv[3], &ggamma) != TCL_OK) goto error;

	bn_mat_angles(o, alpha, beta, ggamma);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_eigen2x2) {
	fastf_t val1, val2;
	vect_t vec1, vec2;
	double a, b, c;

	if (argc < 4) {
	    bu_vls_printf(&result, "usage: %s a b c", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &a) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[2], &c) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[3], &b) != TCL_OK) goto error;

	bn_eigen2x2(&val1, &val2, vec1, vec2, (fastf_t)a, (fastf_t)b,
		    (fastf_t)c);
	bu_vls_printf(&result, "%g %g {%g %g %g} {%g %g %g}", val1, val2,
		      V3ARGS(vec1), V3ARGS(vec2));
    } else if (math_func == bn_mat_fromto) {
	mat_t o;
	vect_t from, to;

	if (argc < 3 || bn_decode_vect(from, argv[1]) < 3 ||
	    bn_decode_vect(to, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s vecFrom vecTo", argv[0]);
	    goto error;
	}
	bn_mat_fromto(o, from, to);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_xrot || math_func == bn_mat_yrot ||
	       math_func == bn_mat_zrot) {
	mat_t o;
	double s, c;
	if (argc < 3) {
	    bu_vls_printf(&result, "usage: %s sinAngle cosAngle", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[1], &s) != TCL_OK) goto error;
	if (Tcl_GetDouble(interp, argv[2], &c) != TCL_OK) goto error;

	(*math_func)(o, s, c);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_lookat) {
	mat_t o;
	vect_t dir;
	int yflip;
	if (argc < 3 || bn_decode_vect(dir, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s dir yflip", argv[0]);
	    goto error;
	}
	if (Tcl_GetBoolean(interp, argv[2], &yflip) != TCL_OK) goto error;

	bn_mat_lookat(o, dir, yflip);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_vec_ortho || math_func == bn_vec_perp) {
	vect_t ov, vec;

	if (argc < 2 || bn_decode_vect(vec, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s vec", argv[0]);
	    goto error;
	}

	(*math_func)(ov, vec);
	bn_encode_vect(&result, ov);
    } else if (math_func == bn_mat_scale_about_pt_wrapper) {
	mat_t o;
	vect_t v;
	double scale;
	int status;

	if (argc < 3 || bn_decode_vect(v, argv[1]) < 3) {
	    bu_vls_printf(&result, "usage: %s pt scale", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[2], &scale) != TCL_OK) goto error;

	bn_mat_scale_about_pt_wrapper(&status, o, v, scale);
	if (status != 0) {
	    bu_vls_printf(&result, "error performing calculation");
	    goto error;
	}
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_xform_about_pt) {
	mat_t o, xform;
	vect_t v;

	if (argc < 3 || bn_decode_mat(xform, argv[1]) < 16 ||
	    bn_decode_vect(v, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s xform pt", argv[0]);
	    goto error;
	}

	bn_mat_xform_about_pt(o, xform, v);
	bn_encode_mat(&result, o);
    } else if (math_func == bn_mat_arb_rot) {
	mat_t o;
	point_t pt;
	vect_t dir;
	double angle;

	if (argc < 4 || bn_decode_vect(pt, argv[1]) < 3 ||
	    bn_decode_vect(dir, argv[2]) < 3) {
	    bu_vls_printf(&result, "usage: %s pt dir angle", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[3], &angle) != TCL_OK)
	    return TCL_ERROR;

	bn_mat_arb_rot(o, pt, dir, (fastf_t)angle);
	bn_encode_mat(&result, o);
    } else if (math_func == quat_mat2quat) {
	mat_t mat;
	quat_t quat;

	if (argc < 2 || bn_decode_mat(mat, argv[1]) < 16) {
	    bu_vls_printf(&result, "usage: %s mat", argv[0]);
	    goto error;
	}

	quat_mat2quat(quat, mat);
	bn_encode_quat(&result, quat);
    } else if (math_func == quat_quat2mat) {
	mat_t mat;
	quat_t quat;

	if (argc < 2 || bn_decode_quat(quat, argv[1]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat", argv[0]);
	    goto error;
	}

	quat_quat2mat(mat, quat);
	bn_encode_mat(&result, mat);
    } else if (math_func == bn_quat_distance_wrapper) {
	quat_t q1, q2;
	double d;

	if (argc < 3 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(q2, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s quatA quatB", argv[0]);
	    goto error;
	}

	bn_quat_distance_wrapper(&d, q1, q2);
	bu_vls_printf(&result, "%g", d);
    } else if (math_func == quat_double || math_func == quat_bisect ||
	       math_func == quat_make_nearest) {
	quat_t oqot, q1, q2;

	if (argc < 3 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(q2, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s quatA quatB", argv[0]);
	    goto error;
	}

	(*math_func)(oqot, q1, q2);
	bn_encode_quat(&result, oqot);
    } else if (math_func == quat_slerp) {
	quat_t oq, q1, q2;
	double d;

	if (argc < 4 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(q2, argv[2]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat1 quat2 factor", argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[3], &d) != TCL_OK) goto error;

	quat_slerp(oq, q1, q2, d);
	bn_encode_quat(&result, oq);
    } else if (math_func == quat_sberp) {
	quat_t oq, q1, qa, qb, q2;
	double d;

	if (argc < 6 || bn_decode_quat(q1, argv[1]) < 4 ||
	    bn_decode_quat(qa, argv[2]) < 4 || bn_decode_quat(qb, argv[3]) < 4 ||
	    bn_decode_quat(q2, argv[4]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat1 quatA quatB quat2 factor",
			  argv[0]);
	    goto error;
	}
	if (Tcl_GetDouble(interp, argv[5], &d) != TCL_OK) goto error;

	quat_sberp(oq, q1, qa, qb, q2, d);
	bn_encode_quat(&result, oq);
    } else if (math_func == quat_exp || math_func == quat_log) {
	quat_t qout, qin;

	if (argc < 2 || bn_decode_quat(qin, argv[1]) < 4) {
	    bu_vls_printf(&result, "usage: %s quat", argv[0]);
	    goto error;
	}

	(*math_func)(qout, qin);
	bn_encode_quat(&result, qout);
    } else if (math_func == (void (*)())bn_isect_line3_line3) {
	double t, u;
	point_t pt, a;
	vect_t dir, c;
	int i;
	static const struct bn_tol tol = {
	    BN_TOL_MAGIC, 0.005, 0.005*0.005, 1e-6, 1-1e-6
	};
	if (argc != 5) {
	    bu_vls_printf(&result,
			  "Usage: bn_isect_line3_line3 pt dir pt dir (%d args specified)",
			  argc-1);
	    goto error;
	}

	if (bn_decode_vect(pt, argv[1]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(dir, argv[2]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no dir: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(a, argv[3]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no a pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(c, argv[4]) < 3) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no c dir: %s\n", argv[0]);
	    goto error;
	}
	i = bn_isect_line3_line3(&t, &u, pt, dir, a, c, &tol);
	if (i != 1) {
	    bu_vls_printf(&result, "bn_isect_line3_line3 no intersection: %s\n", argv[0]);
	    goto error;
	}

	VJOIN1(a, pt, t, dir);
	bn_encode_vect(&result, a);

    } else if (math_func == (void (*)())bn_isect_line2_line2) {
	double dist[2];
	point_t pt, a;
	vect_t dir, c;
	int i;
	static const struct bn_tol tol = {
	    BN_TOL_MAGIC, 0.005, 0.005*0.005, 1e-6, 1-1e-6
	};

	if (argc != 5) {
	    bu_vls_printf(&result,
			  "Usage: bn_isect_line2_line2 pt dir pt dir (%d args specified)",
			  argc-1);
	    goto error;
	}

	/* i = bn_isect_line2_line2 {0 0} {1 0} {1 1} {0 -1} */

	VSETALL(pt, 0.0);
	VSETALL(dir, 0.0);
	VSETALL(a, 0.0);
	VSETALL(c, 0.0);

	if (bn_decode_vect(pt, argv[1]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(dir, argv[2]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no dir: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(a, argv[3]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no a pt: %s\n", argv[0]);
	    goto error;
	}
	if (bn_decode_vect(c, argv[4]) < 2) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no c dir: %s\n", argv[0]);
	    goto error;
	}
	i = bn_isect_line2_line2(dist, pt, dir, a, c, &tol);
	if (i != 1) {
	    bu_vls_printf(&result, "bn_isect_line2_line2 no intersection: %s\n", argv[0]);
	    goto error;
	}

	VJOIN1(a, pt, dist[0], dir);
	bu_vls_printf(&result, "%g %g", a[0], a[1]);

    } else {
	bu_vls_printf(&result, "libbn/bn_tcl.c: math function %s not supported yet\n", argv[0]);
	goto error;
    }

    Tcl_AppendResult(interp, bu_vls_addr(&result), (char *)NULL);
    bu_vls_free(&result);
    return TCL_OK;

 error:
    Tcl_AppendResult(interp, bu_vls_addr(&result), (char *)NULL);
    bu_vls_free(&result);
    return TCL_ERROR;
}