Revision: 32
Author: kim.john.burgess
Date: Thu May 20 01:02:36 2010
Log: - Added random() function to the math library
- Added test_utils library (WIP)
- Added beginnings of a math library test suite (very much still WIP)
- Reduced math library default precision
- Removed whitespace
http://code.google.com/p/amx-netlinx-common/source/detail?r=32
Added:
/trunk/test
/trunk/test/test_math.axi
/trunk/test_utils.axi
Modified:
/trunk/io.axi
/trunk/math.axi
=======================================
--- /dev/null
+++ /trunk/test/test_math.axi Thu May 20 01:02:36 2010
@@ -0,0 +1,139 @@
+/* The contents of this file are subject to the Mozilla Public License
Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is a test suite for the NetLinx common libraries math
+ * library.
+ *
+ * The Initial Developer of the Original Code is Queensland Department of
+ * Justice and Attorney-General.
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kim Burgess <
kim.b...@justice.qld.gov.au>
+ *
+ * $Id: Math.axi 23 2010-05-12 16:29:43Z trueamx $
+ * tab-width: 4 columns: 80
+ */
+
+program_name='test_math'
+#if_not_defined __NCL_LIB_TEST_MATH
+#define __NCL_LIB_TEST_MATH
+
+
+include 'math'
+include 'io'
+include 'test_utils'
+
+
+define_constant
+
+long TEST_MATH_ITERATIONS = 1000 // number of times to execute each
+ // test
+
+
+/**
+ * Test functionality and execution speed of the random() function.
+ *
+ * @return a boolean reflecting success (speed and functionality)
+ */
+define_function char test_random()
+{
+ stack_var double res[TEST_MATH_ITERATIONS]
+ stack_var long i
+ stack_var long err_cnt
+ stack_var long total_time
+
+ // Test for execution speed
+ test_timer_start()
+ for (i = max_length_array(res); i; i--) {
+ res[i] = random()
+ }
+ total_time = test_timer_stop()
+
+ return test_report_double_range('random()', res, 0.0, 1.0, total_time)
+}
+
+/**
+ * Test functionality and execution speed of the sqrt() function.
+ *
+ * @return a boolean reflecting success (speed and functionality)
+ */
+define_function char test_sqrt()
+{
+ stack_var double test_data[TEST_MATH_ITERATIONS]
+ stack_var double res[TEST_MATH_ITERATIONS]
+ stack_var double tmp[TEST_MATH_ITERATIONS]
+ stack_var long i
+ stack_var long total_time
+
+ for (i = max_length_array(test_data); i; i--) {
+ test_data[i] = random() * 100000.0
+ }
+
+ test_timer_start()
+ for (i = max_length_array(res); i; i--) {
+ res[i] = sqrt(test_data[i])
+ }
+ total_time = test_timer_stop()
+
+ for (i = max_length_array(res); i; i--) {
+ tmp[i] = res[i] * res[i]
+ }
+
+ test_report_double('sqrt()', tmp, test_data, 0.001, total_time)
+}
+
+
+/**
+ * Test functionality and execution speed of the fast_sqrt() function.
+ *
+ * @return a boolean reflecting success (speed and functionality)
+ */
+define_function char test_fast_sqrt()
+{
+ stack_var float test_data[TEST_MATH_ITERATIONS]
+ stack_var double res[TEST_MATH_ITERATIONS]
+ stack_var double tmp[TEST_MATH_ITERATIONS]
+ stack_var long i
+ stack_var long total_time
+
+ for (i = max_length_array(test_data); i; i--) {
+ test_data[i] = type_cast(random() * 100000.0)
+ }
+
+ test_timer_start()
+ for (i = max_length_array(res); i; i--) {
+ res[i] = fast_sqrt(test_data[i])
+ }
+ total_time = test_timer_stop()
+
+ for (i = max_length_array(res); i; i--) {
+ tmp[i] = res[i] * res[i]
+ }
+
+ return test_report_double('fast_sqrt()', tmp, test_data, 0.2, total_time)
+}
+
+/**
+ * Test functionality and execution speed of the entire math library.
+ */
+define_function test_math()
+{
+ println("'.............................................................'")
+ println("'Running math library test suite. This may take a while...'")
+ test_random()
+ test_sqrt()
+ test_fast_sqrt()
+ println("'Math library testing complete.'")
+ println("'.............................................................'")
+}
+
+#end_if
=======================================
--- /dev/null
+++ /trunk/test_utils.axi Thu May 20 01:02:36 2010
@@ -0,0 +1,200 @@
+/* The contents of this file are subject to the Mozilla Public License
Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is a collection of test and code benchmarking
utilities.
+ *
+ * The Initial Developer of the Original Code is Queensland Department of
+ * Justice and Attorney-General.
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kim Burgess <
kim.b...@justice.qld.gov.au>
+ *
+ *
+ * $Id: Math.axi 23 2010-05-12 16:29:43Z trueamx $
+ * tab-width: 4 columns: 80
+ */
+
+program_name='test_utils'
+#if_not_defined __NCL_LIB_TEST_UTILS
+#define __NCL_LIB_TEST_UTILS
+
+
+include 'io'
+
+
+define_constant
+
+long TEST_TL = 1 // timeline to user for execution
+ // speed test timer
+
+
+/**
+ * Start a timer for use in execution speed tests.
+ *
+ * @return boolean specifying successful timer creation
+ */
+define_function char test_timer_start()
+{
+ stack_var integer res
+ stack_var long triggers[1]
+ triggers[1] = 1000000 // This is completely arbitary as we are
+ if (timeline_active(TEST_TL)) { // only using the timeline as a timer, not
+ return false // triggering events.
+ } else {
+ res = timeline_create(TEST_TL,
+ triggers,
+ 1,
+ TIMELINE_RELATIVE,
+ TIMELINE_REPEAT)
+ return (res <> 0)
+ }
+}
+
+/**
+ * Stops the speed execution test timer.
+ *
+ * @return long containing the time (ms) the timer was run for
+ */
+define_function long test_timer_stop()
+{
+ stack_var long elapsed
+ if (timeline_active(TEST_TL)) {
+ elapsed = timeline_get(TEST_TL)
+ timeline_kill(TEST_TL)
+ }
+ return elapsed
+}
+
+/**
+ * Calculates the percentage error.
+ *
+ * @return the percentage error (0.0 <= x <= 100.0)
+ */
+define_function double test_error(double estimate, double actual)
+{
+ return abs_value(estimate - actual) / actual * 100.0
+}
+
+/**
+ * Generate a test report for a group of data which should lay between the
+ * bounds of min and max.
+ *
+ * @param name name of the function undergoing testing
+ * @param results array of doubles containing the test results
+ * @param min minimum result value for pass condition (inclusive)
+ * @param max maximum result value for pass condition (exclusive)
+ * @param total_time total time (ms) it took to run all iterations
+ * @return boolean representing test pass status
+ */
+define_function char test_report_double_range(char name[], double
results[],
+ double min, double max, long total_time)
+{
+ stack_var long i
+ stack_var long iterations
+ stack_var long errs
+
+ iterations = max_length_array(results)
+
+ for (i = iterations; i; i--) {
+ if (results[i] < min || results[i] > max) {
+ errs++
+ }
+ }
+
+ return test_report(name, (errs == 0), iterations, total_time,
+ "itoa(errs), ' errors'", "", "")
+}
+
+/**
+ * Generates a test report for a doubles that should site within max_error
of
+ * the expected results.
+ *
+ * @param name name of the function undergoing testing
+ * @param results array of doubles containing the actual results
+ * @param expected array of doubles containing the expected results
+ * @param max_error maximum % err for pass condition
+ * @param total_time total time (ms) it took to run all iterations
+ */
+define_function char test_report_double(char name[], double results[],
+ double expected[], float max_error, long total_time)
+{
+ stack_var long i
+ stack_var long iterations
+ stack_var double avg_speed
+ stack_var double avg_error
+
+ iterations = max_length_array(results)
+
+ for (i = iterations; i; i--) {
+ avg_error = avg_error + test_error(results[i], expected[i])
+ }
+ avg_error = avg_error / iterations
+
+ return test_report(name, (max_error > avg_error), iterations, total_time,
+ "'avg. error ', format('%1.3f', avg_error), '%'", "", "")
+}
+
+/**
+ * Outputs a test report and returns success / failure status.
+ *
+ * @param name name of the function undergoing testing
+ * @param pass boolean indication success
+ * @param iterations long containing the number of iterations tested
+ * @param total_time long containing time (ms) the test ran for
+ * @param param1 misc result info
+ * @param param2 misc result info
+ * @param param3 misc result info
+ * @return boolean indicating success
+ */
+define_function char test_report(char name[], char pass, long iterations,
+ long total_time, char param1[], char param2[], char param3[])
+{
+ stack_var char result[4]
+ stack_var char params[200]
+ stack_var double avg_speed
+
+ switch (pass) {
+ case true: result = "'PASS'"
+ case false: result = "'FAIL'"
+ }
+
+ avg_speed = 1.0 * total_time / iterations
+
+ if (param1 <> "") {
+ params = "', ', param1"
+ }
+
+ select {
+ active (params <> "" && param2 <> ""): {
+ params = "params, ', ', param2"
+ }
+ active (param2 <> ""): {
+ params = param2
+ }
+ }
+
+ select {
+ active (params <> "" && param3 <> ""): {
+ params = "params, ', ', param3"
+ }
+ active (param3 <> ""): {
+ params = param3
+ }
+ }
+
+ println("result, ': ', name, ' - avg. speed ', format('%1.3f', avg_speed),
+ 'ms', params, ' (', itoa(iterations), ' iterations)'")
+
+ return pass
+}
+
+#end_if
=======================================
--- /trunk/io.axi Sun May 16 23:53:19 2010
+++ /trunk/io.axi Thu May 20 01:02:36 2010
@@ -56,7 +56,7 @@
stack_var integer start
stack_var integer end
stack_var integer min_len
-
+
if (io_out_mtu && len > io_out_mtu) {
min_len = type_cast(io_out_mtu / 2)
start = offset + 1
@@ -70,11 +70,11 @@
}
end--
}
-
+
if (end <= start + min_len) {
end = min_value(start + io_out_mtu, offset + len + 1)
}
-
+
write(buf, start - 1, end - start)
start = end
}
=======================================
--- /trunk/math.axi Sun May 16 23:57:02 2010
+++ /trunk/math.axi Thu May 20 01:02:36 2010
@@ -34,7 +34,7 @@
// Precision required for processor intensive math functions. If accuracy
is
// not integral to their use this may be increased to improve performance.
-double MATH_PRECISION = 1.0e-13
+double MATH_PRECISION = 1.0e-6
define_variable
@@ -250,6 +250,33 @@
{
return floor(x + 0.5)
}
+
+/**
+ * Returns a double value with a positive sign, greater than or equal to
0.0
+ * and less than 1.0.
+ *
+ * @return a pseudorandom double greater than or equal to 0.0 and
+ * less than 1.0
+ */
+define_function double random()
+{
+ stack_var char i
+ stack_var long hi
+ stack_var long low
+
+ // Create a (psuedo) random mantissa
+ for (i = 32; i; i--) {
+ low = low + (random_number(2) << (i - 1))
+ }
+ for (i = 20; i; i--) {
+ hi = hi + (random_number(2) << (i - 1))
+ }
+
+ // Add in an exponent of 0 to make sure we get full resolution
+ hi = hi + (1023 << 20)
+
+ return math_build_double(hi, low) - 1
+}
/**
* Calculate the square root of the passed number.
@@ -265,12 +292,6 @@
stack_var long hi
stack_var long low
stack_var double tmp
- if (x == 0 ||
- x == MATH_NEGATIVE_INFINITY ||
- x == MATH_POSITIVE_INFINITY ||
- x == MATH_NaN) {
- return x
- }
tmp = math_rshift_double(x)
hi = (1 << 29) + math_double_high_to_bits(tmp) - (1 << 19)
low = math_double_low_to_bits(tmp)
@@ -303,7 +324,7 @@
* Approximate the square root of the passed number based on the inverse
square
* root algorithm in mathInvSqrt(x). This is MUCH faster than sqrt(x) and
* recommended over sqrt() for use anywhere a precise square root is not
- * required. Error is approx +/-0.15%.
+ * required. Error is approx +/-0.17%.
*
* @param x the float to find the square root of
* @return a float containing an approximation of the square root