Index: Python/graminit.c
===================================================================
--- Python/graminit.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Python/graminit.c	(.../radar)	(revision 263)
@@ -1104,67 +1104,77 @@
 	{1, arcs_54_2},
 	{1, arcs_54_3},
 };
-static arc arcs_55_0[7] = {
+static arc arcs_55_0[8] = {
 	{13, 1},
 	{136, 2},
 	{139, 3},
 	{142, 4},
 	{19, 5},
-	{144, 5},
-	{145, 6},
+	{144, 6},
+	{146, 7},
+	{147, 5},
 };
 static arc arcs_55_1[2] = {
-	{135, 7},
+	{135, 8},
 	{15, 5},
 };
 static arc arcs_55_2[2] = {
-	{137, 8},
+	{137, 9},
 	{138, 5},
 };
 static arc arcs_55_3[2] = {
-	{140, 9},
+	{140, 10},
 	{141, 5},
 };
 static arc arcs_55_4[1] = {
-	{143, 10},
+	{143, 11},
 };
 static arc arcs_55_5[1] = {
 	{0, 5},
 };
 static arc arcs_55_6[2] = {
-	{145, 6},
+	{131, 12},
 	{0, 6},
 };
-static arc arcs_55_7[1] = {
+static arc arcs_55_7[2] = {
+	{146, 7},
+	{0, 7},
+};
+static arc arcs_55_8[1] = {
 	{15, 5},
 };
-static arc arcs_55_8[1] = {
+static arc arcs_55_9[1] = {
 	{138, 5},
 };
-static arc arcs_55_9[1] = {
+static arc arcs_55_10[1] = {
 	{141, 5},
 };
-static arc arcs_55_10[1] = {
+static arc arcs_55_11[1] = {
 	{142, 5},
 };
-static state states_55[11] = {
-	{7, arcs_55_0},
+static arc arcs_55_12[1] = {
+	{145, 5},
+};
+static state states_55[13] = {
+	{8, arcs_55_0},
 	{2, arcs_55_1},
 	{2, arcs_55_2},
 	{2, arcs_55_3},
 	{1, arcs_55_4},
 	{1, arcs_55_5},
 	{2, arcs_55_6},
-	{1, arcs_55_7},
+	{2, arcs_55_7},
 	{1, arcs_55_8},
 	{1, arcs_55_9},
 	{1, arcs_55_10},
+	{1, arcs_55_11},
+	{1, arcs_55_12},
 };
 static arc arcs_56_0[1] = {
 	{26, 1},
 };
 static arc arcs_56_1[3] = {
-	{146, 2},
+	{148, 2},
 	{27, 3},
 	{0, 1},
 };
@@ -1190,7 +1200,7 @@
 	{26, 1},
 };
 static arc arcs_57_1[3] = {
-	{147, 2},
+	{149, 2},
 	{27, 3},
 	{0, 1},
 };
@@ -1213,7 +1223,7 @@
 	{2, arcs_57_4},
 };
 static arc arcs_58_0[1] = {
-	{148, 1},
+	{150, 1},
 };
 static arc arcs_58_1[2] = {
 	{23, 2},
@@ -1245,7 +1255,7 @@
 	{15, 5},
 };
 static arc arcs_59_2[1] = {
-	{149, 6},
+	{151, 6},
 };
 static arc arcs_59_3[1] = {
 	{19, 5},
@@ -1269,14 +1279,14 @@
 	{1, arcs_59_6},
 };
 static arc arcs_60_0[1] = {
-	{150, 1},
+	{152, 1},
 };
 static arc arcs_60_1[2] = {
 	{27, 2},
 	{0, 1},
 };
 static arc arcs_60_2[2] = {
-	{150, 1},
+	{152, 1},
 	{0, 2},
 };
 static state states_60[3] = {
@@ -1298,14 +1308,14 @@
 };
 static arc arcs_61_3[3] = {
 	{26, 5},
-	{151, 6},
+	{153, 6},
 	{0, 3},
 };
 static arc arcs_61_4[1] = {
 	{78, 6},
 };
 static arc arcs_61_5[2] = {
-	{151, 6},
+	{153, 6},
 	{0, 5},
 };
 static arc arcs_61_6[1] = {
@@ -1417,7 +1427,7 @@
 	{2, arcs_66_4},
 };
 static arc arcs_67_0[1] = {
-	{153, 1},
+	{155, 1},
 };
 static arc arcs_67_1[1] = {
 	{19, 2},
@@ -1452,7 +1462,7 @@
 	{1, arcs_67_7},
 };
 static arc arcs_68_0[3] = {
-	{154, 1},
+	{156, 1},
 	{28, 2},
 	{29, 3},
 };
@@ -1467,7 +1477,7 @@
 	{26, 6},
 };
 static arc arcs_68_4[4] = {
-	{154, 1},
+	{156, 1},
 	{28, 2},
 	{29, 3},
 	{0, 4},
@@ -1497,7 +1507,7 @@
 };
 static arc arcs_69_1[3] = {
 	{25, 2},
-	{147, 3},
+	{149, 3},
 	{0, 1},
 };
 static arc arcs_69_2[1] = {
@@ -1507,7 +1517,7 @@
 	{0, 3},
 };
 static arc arcs_69_4[2] = {
-	{147, 3},
+	{149, 3},
 	{0, 4},
 };
 static state states_69[5] = {
@@ -1518,8 +1528,8 @@
 	{2, arcs_69_4},
 };
 static arc arcs_70_0[2] = {
-	{146, 1},
-	{156, 1},
+	{148, 1},
+	{158, 1},
 };
 static arc arcs_70_1[1] = {
 	{0, 1},
@@ -1538,10 +1548,10 @@
 	{82, 3},
 };
 static arc arcs_71_3[1] = {
-	{152, 4},
+	{154, 4},
 };
 static arc arcs_71_4[2] = {
-	{155, 5},
+	{157, 5},
 	{0, 4},
 };
 static arc arcs_71_5[1] = {
@@ -1562,7 +1572,7 @@
 	{26, 2},
 };
 static arc arcs_72_2[2] = {
-	{155, 3},
+	{157, 3},
 	{0, 2},
 };
 static arc arcs_72_3[1] = {
@@ -1575,8 +1585,8 @@
 	{1, arcs_72_3},
 };
 static arc arcs_73_0[2] = {
-	{147, 1},
-	{158, 1},
+	{149, 1},
+	{160, 1},
 };
 static arc arcs_73_1[1] = {
 	{0, 1},
@@ -1598,7 +1608,7 @@
 	{26, 4},
 };
 static arc arcs_74_4[2] = {
-	{157, 5},
+	{159, 5},
 	{0, 4},
 };
 static arc arcs_74_5[1] = {
@@ -1619,7 +1629,7 @@
 	{26, 2},
 };
 static arc arcs_75_2[2] = {
-	{157, 3},
+	{159, 3},
 	{0, 2},
 };
 static arc arcs_75_3[1] = {
@@ -1642,175 +1652,241 @@
 	{1, arcs_76_0},
 	{2, arcs_76_1},
 };
-static arc arcs_77_0[1] = {
-	{19, 1},
+static arc arcs_77_0[3] = {
+	{126, 1},
+	{144, 2},
+	{19, 3},
 };
 static arc arcs_77_1[1] = {
-	{0, 1},
+	{144, 2},
 };
-static state states_77[2] = {
-	{1, arcs_77_0},
+static arc arcs_77_2[1] = {
+	{19, 3},
+};
+static arc arcs_77_3[2] = {
+	{119, 4},
+	{0, 3},
+};
+static arc arcs_77_4[2] = {
+	{126, 5},
+	{144, 6},
+};
+static arc arcs_77_5[1] = {
+	{144, 6},
+};
+static arc arcs_77_6[1] = {
+	{0, 6},
+};
+static state states_77[7] = {
+	{3, arcs_77_0},
 	{1, arcs_77_1},
+	{1, arcs_77_2},
+	{2, arcs_77_3},
+	{2, arcs_77_4},
+	{1, arcs_77_5},
+	{1, arcs_77_6},
 };
-static dfa dfas[78] = {
+static arc arcs_78_0[1] = {
+	{161, 1},
+};
+static arc arcs_78_1[3] = {
+	{28, 0},
+	{128, 0},
+	{0, 1},
+};
+static state states_78[2] = {
+	{1, arcs_78_0},
+	{3, arcs_78_1},
+};
+static arc arcs_79_0[1] = {
+	{162, 1},
+};
+static arc arcs_79_1[1] = {
+	{145, 2},
+};
+static arc arcs_79_2[1] = {
+	{0, 2},
+};
+static state states_79[3] = {
+	{1, arcs_79_0},
+	{1, arcs_79_1},
+	{1, arcs_79_2},
+};
+static arc arcs_80_0[1] = {
+	{19, 1},
+};
+static arc arcs_80_1[1] = {
+	{0, 1},
+};
+static state states_80[2] = {
+	{1, arcs_80_0},
+	{1, arcs_80_1},
+};
+static dfa dfas[81] = {
 	{256, "single_input", 0, 3, states_0,
-	 "\004\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\023\002"},
+	 "\004\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\105\010\004"},
 	{257, "file_input", 0, 2, states_1,
-	 "\204\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\023\002"},
+	 "\204\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\105\010\004"},
 	{258, "eval_input", 0, 3, states_2,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{259, "decorator", 0, 7, states_3,
-	 "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{260, "decorators", 0, 2, states_4,
-	 "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{261, "funcdef", 0, 7, states_5,
-	 "\000\010\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\010\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{262, "parameters", 0, 4, states_6,
-	 "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{263, "varargslist", 0, 10, states_7,
-	 "\000\040\010\060\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\040\010\060\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{264, "fpdef", 0, 4, states_8,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{265, "fplist", 0, 3, states_9,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{266, "stmt", 0, 2, states_10,
-	 "\000\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\023\002"},
+	 "\000\050\014\000\000\000\200\012\076\205\011\162\000\002\000\140\010\111\105\010\004"},
 	{267, "simple_stmt", 0, 4, states_11,
-	 "\000\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\105\000\004"},
 	{268, "small_stmt", 0, 2, states_12,
-	 "\000\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\105\000\004"},
 	{269, "expr_stmt", 0, 6, states_13,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{270, "augassign", 0, 2, states_14,
-	 "\000\000\000\000\000\370\177\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\370\177\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{271, "print_stmt", 0, 9, states_15,
-	 "\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{272, "del_stmt", 0, 3, states_16,
-	 "\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{273, "pass_stmt", 0, 2, states_17,
-	 "\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{274, "flow_stmt", 0, 2, states_18,
-	 "\000\000\000\000\000\000\000\000\076\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\076\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{275, "break_stmt", 0, 2, states_19,
-	 "\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{276, "continue_stmt", 0, 2, states_20,
-	 "\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{277, "return_stmt", 0, 3, states_21,
-	 "\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{278, "yield_stmt", 0, 3, states_22,
-	 "\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{279, "raise_stmt", 0, 7, states_23,
-	 "\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{280, "import_stmt", 0, 2, states_24,
-	 "\000\000\000\000\000\000\000\000\000\005\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\005\000\000\000\000\000\000\000\000\000\000\000"},
 	{281, "import_name", 0, 3, states_25,
-	 "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000"},
 	{282, "import_from", 0, 7, states_26,
-	 "\000\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000"},
 	{283, "import_as_name", 0, 4, states_27,
-	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{284, "dotted_as_name", 0, 4, states_28,
-	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{285, "import_as_names", 0, 3, states_29,
-	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{286, "dotted_as_names", 0, 2, states_30,
-	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{287, "dotted_name", 0, 2, states_31,
-	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{288, "global_stmt", 0, 3, states_32,
-	 "\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000"},
 	{289, "exec_stmt", 0, 7, states_33,
-	 "\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000"},
 	{290, "assert_stmt", 0, 5, states_34,
-	 "\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000\000"},
 	{291, "compound_stmt", 0, 2, states_35,
-	 "\000\010\004\000\000\000\000\000\000\000\000\162\000\000\000\000\000\000\000\002"},
+	 "\000\010\004\000\000\000\000\000\000\000\000\162\000\000\000\000\000\000\000\010\000"},
 	{292, "if_stmt", 0, 8, states_36,
-	 "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000"},
 	{293, "while_stmt", 0, 8, states_37,
-	 "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000"},
 	{294, "for_stmt", 0, 10, states_38,
-	 "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"},
 	{295, "try_stmt", 0, 10, states_39,
-	 "\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000"},
 	{296, "except_clause", 0, 5, states_40,
-	 "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"},
 	{297, "suite", 0, 5, states_41,
-	 "\004\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\023\000"},
+	 "\004\040\010\000\000\000\200\012\076\205\011\000\000\002\000\140\010\111\105\000\004"},
 	{298, "test", 0, 4, states_42,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{299, "and_test", 0, 2, states_43,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\005\000\004"},
 	{300, "not_test", 0, 3, states_44,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\005\000\004"},
 	{301, "comparison", 0, 2, states_45,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{302, "comp_op", 0, 4, states_46,
-	 "\000\000\000\000\000\000\000\000\000\000\004\000\000\362\017\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\004\000\000\362\017\000\000\000\000\000\000"},
 	{303, "expr", 0, 2, states_47,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{304, "xor_expr", 0, 2, states_48,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{305, "and_expr", 0, 2, states_49,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{306, "shift_expr", 0, 2, states_50,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{307, "arith_expr", 0, 2, states_51,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{308, "term", 0, 2, states_52,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{309, "factor", 0, 3, states_53,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{310, "power", 0, 4, states_54,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\003\000"},
-	{311, "atom", 0, 11, states_55,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\005\000\004"},
+	{311, "atom", 0, 13, states_55,
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\111\005\000\004"},
 	{312, "listmaker", 0, 5, states_56,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{313, "testlist_gexp", 0, 5, states_57,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{314, "lambdef", 0, 5, states_58,
-	 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\020\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\100\000\000"},
 	{315, "trailer", 0, 7, states_59,
-	 "\000\040\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\001\000\000"},
+	 "\000\040\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\001\000\000\000"},
 	{316, "subscriptlist", 0, 3, states_60,
-	 "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\105\000\004"},
 	{317, "subscript", 0, 7, states_61,
-	 "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\050\000\000\000\000\000\000\100\000\000\000\002\000\140\010\111\105\000\004"},
 	{318, "sliceop", 0, 3, states_62,
-	 "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 	{319, "exprlist", 0, 3, states_63,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\003\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\140\010\111\005\000\004"},
 	{320, "testlist", 0, 3, states_64,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{321, "testlist_safe", 0, 5, states_65,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{322, "dictmaker", 0, 5, states_66,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{323, "classdef", 0, 8, states_67,
-	 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\010\000"},
 	{324, "arglist", 0, 8, states_68,
-	 "\000\040\010\060\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\060\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{325, "argument", 0, 5, states_69,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
 	{326, "list_iter", 0, 2, states_70,
-	 "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000\000"},
 	{327, "list_for", 0, 6, states_71,
-	 "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"},
 	{328, "list_if", 0, 4, states_72,
-	 "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000"},
 	{329, "gen_iter", 0, 2, states_73,
-	 "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000\000"},
 	{330, "gen_for", 0, 6, states_74,
-	 "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"},
 	{331, "gen_if", 0, 4, states_75,
-	 "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"},
+	 "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000"},
 	{332, "testlist1", 0, 2, states_76,
-	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\023\000"},
-	{333, "encoding_decl", 0, 2, states_77,
-	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
+	 "\000\040\010\000\000\000\000\000\000\000\000\000\000\002\000\140\010\111\105\000\004"},
+	{333, "unit", 0, 7, states_77,
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\100\000\000\001\000\000"},
+	{334, "unit_expr", 0, 2, states_78,
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\100\000\000\001\000\000"},
+	{335, "unit_atom", 0, 3, states_79,
+	 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004"},
+	{336, "encoding_decl", 0, 2, states_80,
+	 "\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"},
 };
-static label labels[160] = {
+static label labels[164] = {
 	{0, "EMPTY"},
 	{256, 0},
 	{4, 0},
@@ -1956,7 +2032,9 @@
 	{25, 0},
 	{332, 0},
 	{2, 0},
+	{334, 0},
 	{3, 0},
+	{335, 0},
 	{327, 0},
 	{330, 0},
 	{1, "lambda"},
@@ -1971,10 +2049,12 @@
 	{329, 0},
 	{331, 0},
 	{333, 0},
+	{54, 0},
+	{336, 0},
 };
 grammar _PyParser_Grammar = {
-	78,
+	81,
 	dfas,
-	{160, labels},
+	{164, labels},
 	256
 };
Index: Python/statslogger.c
===================================================================
--- Python/statslogger.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Python/statslogger.c	(.../radar)	(revision 263)
@@ -0,0 +1,46 @@
+/************************************************************
+ * A quick system to log errors to a file for statistical 	*
+ * purposes.												*
+ * Geoffrey Biggs 2005										*
+ ************************************************************/
+
+#ifdef __RADAR_EXT__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "statslogger.h"
+
+void LogError (const char *format, ...)
+{
+#ifdef __RADAR_STATS_FILE__
+	FILE *file = NULL;
+	va_list ap;
+	char timeStr[64];
+	time_t currentTime;
+	
+	va_start (ap, format);
+	
+	// Open the stats file
+	if ((file = fopen (__RADAR_STATS_FILE__, "a")) == NULL)
+	{
+		printf ("WARNING: Could not open stats file for appending!\n");
+		return;
+	}
+	// Write to the stats file
+	currentTime = time (NULL);
+	strftime (timeStr, 64, "%Y%m%d%H%M%S", localtime (&currentTime));
+	if (fprintf (file, "%s\t", timeStr) <= 0)
+		printf ("WARNING: Could not write to stats file!\n");
+	if (vfprintf (file, format, ap) <= 0)
+		printf ("WARNING: Could not write to stats file!\n");
+	// Close the stats file
+	fclose (file);
+	
+	va_end (ap);
+#endif
+}
+
+#endif
Index: Python/statslogger.h
===================================================================
--- Python/statslogger.h	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Python/statslogger.h	(.../radar)	(revision 263)
@@ -0,0 +1,16 @@
+/************************************************************
+ * A quick system to log errors to a file for statistical 	*
+ * purposes.												*
+ * Geoffrey Biggs 2005										*
+ ************************************************************/
+
+#ifndef _STATSLOGGER_H_
+#define _STATSLOGGER_H_
+
+#ifdef __RADAR_EXT__
+
+void LogError (const char *format, ...);
+
+#endif // __RADAR_EXT__
+
+#endif // _STATSLOGGER_H_
Index: Python/pythonrun.c
===================================================================
--- Python/pythonrun.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Python/pythonrun.c	(.../radar)	(revision 263)
@@ -197,6 +197,11 @@
 	PyDict_SetItemString(interp->sysdict, "modules",
 			     interp->modules);
 
+#ifdef __RADAR_EXT__
+	if (!_PyDimension_Init ())
+		Py_FatalError ("Py_Initialize: failure initialising dimension module");
+#endif // __RADAR_EXT__
+
 	_PyImport_Init();
 
 	/* initialize builtin exceptions */
Index: Python/marshal.c
===================================================================
--- Python/marshal.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Python/marshal.c	(.../radar)	(revision 263)
@@ -35,6 +35,10 @@
 #define TYPE_CODE	'c'
 #define TYPE_UNICODE	'u'
 #define TYPE_UNKNOWN	'?'
+#ifdef __RADAR_EXT__
+#define TYPE_DIMENSIONED	'd'
+#define TYPE_UNITS	'U'
+#endif
 
 typedef struct {
 	FILE *fp;
@@ -191,6 +195,24 @@
 		w_string(buf, n, p);
 	}
 #endif
+#ifdef __RADAR_EXT__
+	else if (PyDimensioned_Check (v))
+	{
+		// Write the type character and the metadata for the object
+		w_byte (TYPE_DIMENSIONED, p);
+		// Write the units list
+		w_object (((PyDimensionedObject*) v)->units, p);
+		// Write the floating measure
+		w_object (PyFloat_FromDouble (((PyDimensionedObject*) v)->floatingMeasure), p);
+		// Write the fixed measure
+		w_object (PyLong_FromLong (((PyDimensionedObject*) v)->fixedMeasure), p);
+	}
+	else if (PyUnit_Check (v))
+	{
+		w_byte (TYPE_UNITS, p);
+		w_object (((PyUnitObject*) v)->units, p);
+	}
+#endif
 	else if (PyString_Check(v)) {
 		if (p->strings && PyString_CHECK_INTERNED(v)) {
 			PyObject *o = PyDict_GetItem(p->strings, v);
@@ -409,6 +431,9 @@
 	PyObject *v, *v2;
 	long i, n;
 	int type = r_byte(p);
+#ifdef __RADAR_EXT__
+	PyObject *unitsList = NULL, *floatingMeasure = NULL, *fixedMeasure = NULL;
+#endif
 
 	switch (type) {
 
@@ -514,6 +539,40 @@
 			return PyComplex_FromCComplex(c);
 		}
 #endif
+#ifdef __RADAR_EXT__
+	case TYPE_DIMENSIONED:
+		unitsList = r_object (p);
+		if (unitsList == NULL)
+		{
+			if (!PyErr_Occurred())
+				PyErr_SetString(PyExc_TypeError, "NULL object in marshal data");
+			return NULL;
+		}
+		floatingMeasure = r_object (p);
+		if (floatingMeasure == NULL)
+		{
+			if (!PyErr_Occurred())
+				PyErr_SetString(PyExc_TypeError, "NULL object in marshal data");
+			return NULL;
+		}
+		fixedMeasure = r_object (p);
+		if (fixedMeasure == NULL)
+		{
+			if (!PyErr_Occurred())
+				PyErr_SetString(PyExc_TypeError, "NULL object in marshal data");
+			return NULL;
+		}
+		return DimOb_FromList (unitsList, PyFloat_AsDouble (floatingMeasure), PyLong_AsLong (fixedMeasure));
+	case TYPE_UNITS:
+		unitsList = r_object (p);
+		if (unitsList == NULL)
+		{
+			if (!PyErr_Occurred())
+				PyErr_SetString(PyExc_TypeError, "NULL object in marshal data");
+			return NULL;
+		}
+		return UnitOb_FromList (unitsList);
+#endif
 
 	case TYPE_INTERNED:
 	case TYPE_STRING:
Index: Python/compile.c
===================================================================
--- Python/compile.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Python/compile.c	(.../radar)	(revision 263)
@@ -1688,6 +1688,84 @@
 	}
 }
 
+#ifdef __RADAR_EXT__
+
+static PyObject* parseunit (node *unitNode, int divided)
+{
+	PyObject *result = NULL, *unitName = NULL;
+	int multiplier = 1, unitPower = 1;
+	int doneName = 0, minusFlag = 1, i = 0;
+
+	for (i = 0; i < NCH (unitNode); i++)
+	{
+		if (TYPE (CHILD (unitNode, i)) == MINUS)
+		{	// If minus, this and the next child combined form a number
+			minusFlag = -1;
+			continue;
+		}
+		else if (TYPE (CHILD (unitNode, i)) == NUMBER)
+		{	// Number, so either multiplier or power. Which depends on if we've compiled the name yet
+			if (doneName)
+			{	// Must be power then
+				unitPower = strtol (STR (CHILD (unitNode, i)), NULL, 10) * minusFlag;
+				minusFlag = 1;		// Reset minus flag so it doesn't affect a possible next number
+			}
+			else
+			{	// If not power, can only be multiplier
+				multiplier = strtol (STR (CHILD (unitNode, i)), NULL, 10) * minusFlag;
+				minusFlag = 1;		// Reset minus flag so it doesn't affect a possible next number
+			}
+			continue;
+		}
+		else if (TYPE (CHILD (unitNode, i)) == NAME)
+		{	// Name is obvious what it is
+			unitName = PyString_FromString (STR (CHILD (unitNode, i)));
+			doneName = 1;
+		}
+	}
+
+	// Make sure the power is negative if it needs to be
+	// (do here because powers of 1 not necessarily specified in the syntax)
+	unitPower *= divided;
+	// Put it all into a tuple
+	if ((result = PyTuple_New (3)) == NULL)
+		return NULL;
+	PyTuple_SetItem (result, 0, PyInt_FromLong (multiplier));
+	PyTuple_SetItem (result, 1, unitName);
+	PyTuple_SetItem (result, 2, PyInt_FromLong (unitPower));
+
+	return result;
+}
+
+static PyObject* parseunit_expr (struct compiling *c, node *unitExpr)
+{
+	PyObject *unitList = NULL, *tuple = NULL;
+	int i = 0, divided = 1;
+
+	// Make a new python list to store the units on
+	if ((unitList = PyList_New (0)) == NULL)
+		return NULL;
+
+	// For each child of the unit expression, get a tuple that represents its children and append it to the list
+	for (i = 0; i < NCH (unitExpr); i += 2)
+	{
+		// Check if this unit is after a multiplication or division (doing this now cause easier than later)
+		if (TYPE (CHILD (unitExpr, i - 1)) == SLASH)
+			divided = -1;
+		else
+			divided = 1;
+		
+		if ((tuple = parseunit (CHILD (unitExpr, i), divided)) == NULL)
+			return NULL;
+
+		PyList_Append (unitList, tuple);
+	}
+
+	return unitList;
+}
+
+#endif	// __RADAR_EXT__
+
 static PyObject *
 decode_utf8(char **sPtr, char *end, char* encoding)
 {
@@ -2154,8 +2232,9 @@
 com_atom(struct compiling *c, node *n)
 {
 	node *ch;
-	PyObject *v;
+	PyObject *v, *unitList, *number;
 	int i;
+//	double value;
 	REQ(n, atom);
 	ch = CHILD(n, 0);
 	switch (TYPE(ch)) {
@@ -2186,6 +2265,92 @@
 		com_addbyte(c, UNARY_CONVERT);
 		break;
 	case NUMBER:
+#ifdef __RADAR_EXT__
+		// Check if the 2nd child is a '~' character
+		if (TYPE (CHILD (n, 1)) == TILDE)
+		{	// If it is, then this is a dimension statement (hopefully)
+			// If "dimensioned" isn't in the symbol table, then add it (this could be very bad, but seems to work fine so far)
+			
+			if (PyDict_GetItemString(c->c_globals, "dimensioned") == NULL)
+			{
+				if (PyDict_SetItemString (c->c_globals, "dimensioned", Py_True) < 0)
+				{
+					com_addoparg (c, LOAD_CONST, 255);
+					com_push (c, 1);
+					break;
+				}	
+			}
+			// Push the name of the constructor func into the code
+			com_addop_varname (c, VAR_LOAD, "dimensioned");
+			com_push (c, 1);
+			if ((number = parsenumber (c, STR (CHILD (n, 0)))) == NULL)
+			{
+				i = 255;
+			}
+			else
+			{
+				// Push the numerical value into the code
+				i = com_addconst (c, number);
+				Py_DECREF (number);
+			}
+			com_addoparg (c, LOAD_CONST, i);
+			com_push (c, 1);
+			// Next get the units
+			if ((unitList = parseunit_expr (c, CHILD (n, 2))) == NULL)
+			{
+				i = 255;
+			}
+			else
+			{
+				if ((v = UnitOb_FromList (unitList)) == NULL)
+				{
+					i = 255;
+				}
+				else
+				{
+					i = com_addconst(c, v);
+					Py_DECREF(v);
+				}
+				Py_DECREF (unitList);
+			}
+			com_addoparg (c, LOAD_CONST, i);
+			com_push (c, 1);
+			
+			// Add the function call
+			com_addoparg (c, CALL_FUNCTION, 2);
+			break;
+		}
+			
+/*			if ((number = parsenumber (c, STR (CHILD (n, 0)))) == NULL)
+			{
+				goto DIM_PARSE_FAIL;
+			}
+			if ((unitList = parseunit_expr (c, CHILD (n, 2))) == NULL)
+			{
+				goto DIM_PARSE_FAIL;
+			}
+			if (PyInt_Check (number))
+				value = (double) PyInt_AsLong (number);
+			else if (PyFloat_Check (number))
+				value = PyFloat_AsDouble (number);
+			// Create the actual dimensioned object
+			if ((v = DimOb_FromList (unitList, value, (long) value)) == NULL)
+			{
+DIM_PARSE_FAIL:
+				i = 255;
+			}
+			else
+			{
+				i = com_addconst (c, v);
+				Py_DECREF (v);
+			}
+			com_addoparg (c, LOAD_CONST, i);
+			com_push (c, 1);
+			Py_DECREF (number);
+			Py_DECREF (unitList);
+			break;
+		}*/
+#endif	// __RADAR_EXT__
 		if ((v = parsenumber(c, STR(ch))) == NULL) {
 			i = 255;
 		}
@@ -2213,6 +2378,29 @@
 		com_addop_varname(c, VAR_LOAD, STR(ch));
 		com_push(c, 1);
 		break;
+#ifdef __RADAR_EXT__
+	case unit_atom:
+		if ((unitList = parseunit_expr (c, CHILD (ch, 1))) == NULL)
+		{
+			i = 255;
+		}
+		else
+		{
+			if ((v = UnitOb_FromList (unitList)) == NULL)
+			{
+				i = 255;
+			}
+			else
+			{
+				i = com_addconst(c, v);
+				Py_DECREF(v);
+			}
+		}
+		com_addoparg (c, LOAD_CONST, i);
+		com_push (c, 1);
+		Py_DECREF (unitList);
+		break;
+#endif	// __RADAR_EXT__
 	default:
 		com_error(c, PyExc_SystemError,
 			  "com_atom: unexpected node type");
Index: Python/bltinmodule.c
===================================================================
--- Python/bltinmodule.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Python/bltinmodule.c	(.../radar)	(revision 263)
@@ -2223,6 +2223,17 @@
 	SETBUILTIN("tuple",		&PyTuple_Type);
 	SETBUILTIN("type",		&PyType_Type);
 	SETBUILTIN("xrange",		&PyRange_Type);
+#ifdef __RADAR_EXT__
+	SETBUILTIN("dimensioned",		&PyDimensioned_Type);
+	SETBUILTIN("UnitRange",		&PyDimRange_Type);
+	SETBUILTIN("UnitRange_Include",		Py_DimRange_Include);
+	SETBUILTIN("UnitRange_Exclude",		Py_DimRange_Exclude);
+	SETBUILTIN("UnitRange_Infinite",		Py_DimRange_Infinite);
+	SETBUILTIN("UnitRange_Wrap",		Py_DimRange_Wrap);
+	SETBUILTIN("UnitPrec",		&PyDimPrec_Type);
+	SETBUILTIN("UnitPrec_Floating",		Py_DimPrec_Floating);
+	SETBUILTIN("UnitPrec_Fixed",		Py_DimPrec_Fixed);
+#endif	// __RADAR_EXT__
 
 	/* Note that open() is just an alias of file(). */
 	SETBUILTIN("open",		&PyFile_Type);
Index: Python/dimensionmodule.c
===================================================================
--- Python/dimensionmodule.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Python/dimensionmodule.c	(.../radar)	(revision 263)
@@ -0,0 +1,686 @@
+/************************************************************
+ * Dimensional Analysis module								*
+ * Geoffrey Biggs 2005										*
+ * Supplies the lists and functions for managing dimensions	*
+ * and units used with the dimensioned data type.			*
+ ************************************************************/
+
+#ifdef __RADAR_EXT__
+
+#include "dimensiontypes.h"
+#include "../Python/statslogger.h"
+ 
+#include "Python.h"
+
+// Go on. Guess.
+#define DIM_PI				3.1415926535897931
+
+
+// A linked list of the dimensions currently defined in the system.
+static DimensionList dimensionsList = { NULL, NULL };
+static unsigned char numDimensions = 0;
+// Linked lists of the units currently defined in the system
+static UnitList dimensionUnitsList = { NULL, NULL };
+
+////////////////////////////////////////////////////////////////////////////////
+//	Dimensions management
+////////////////////////////////////////////////////////////////////////////////
+
+///	Define a new dimension.
+///	Call to define a new dimension with the information passed. The dimension
+///	can then be used in code to manage units.
+///	Returns: Py_None on success, NULL on failure.
+static PyObject* DefineDimension (PyObject *self, PyObject *args)
+{
+	Dimension *newDimension = NULL, *currentDim = NULL;
+	char *name;
+	
+	// Unpack the arguments
+	if (!PyArg_ParseTuple (args, "s", &name))
+		return NULL;
+	// Check for an existing dimension with the same name
+	currentDim = dimensionsList.first;
+	while (currentDim != NULL)
+	{
+		if (!strcmp (name, currentDim->name))
+		{
+			LogError ("Attempt to redefine a dimension\n");
+			PyErr_SetString (PyExc_ValueError, "Dimension already defined");
+			return NULL;
+		}
+		currentDim = currentDim->next;
+	}
+
+	if ((newDimension = malloc (sizeof (Dimension))) == NULL)
+	{
+		PyErr_SetString (PyExc_MemoryError, "Insufficient memory to create new dimension");
+		return NULL;
+	}
+	
+	newDimension->name = strdup (name);
+	newDimension->index = ++numDimensions;
+	newDimension->next = NULL;
+	
+	if (dimensionsList.first == NULL)
+		dimensionsList.first = newDimension;
+	if (dimensionsList.last != NULL)
+		dimensionsList.last->next = newDimension;
+	dimensionsList.last = newDimension;
+	
+	Py_INCREF (Py_None);
+	return Py_None;
+}
+
+// Doc string for DefineDimension
+PyDoc_STRVAR (defineDimensionDoc,
+"DefineDimension (name)"
+"Define a new dimension to the system, making it available for use with the\n"
+"dimensioned data type.");
+
+///	Set base units for a dimension.
+///	Receives: The arguments wrapped up in a tuple
+///	Returns:	Py_None on success, NULL on fail
+/*static PyObject* SetBaseUnit (PyObject *self, PyObject *args)
+{
+	char *dimension = NULL, *baseUnitName = NULL;
+	Dimension *currentDim = NULL;
+	PyObject *precision;
+	Unit *newBaseUnit = NULL;
+	int baseUnitFound = 0;
+
+	// Unpack the arguments tuple
+	if (!PyArg_ParseTuple (args, "sOs", &dimension, &precision, &baseUnitName))
+		return NULL;
+		
+	// Search for the requested unit
+	newBaseUnit = dimensionUnitsList.first;
+	while (newBaseUnit != NULL)
+	{
+		if (strcmp (newBaseUnit->name, baseUnitName) == 0)
+		{
+			baseUnitFound = 1;
+			break;
+		}
+		newBaseUnit = newBaseUnit->next;
+	}
+	if (!baseUnitFound)
+	{
+		LogError ("Undefined unit when setting base unit: %s\n", baseUnitName);
+		PyErr_SetString (PyExc_ValueError, "Undefined unit");
+		return NULL;
+	}
+		
+	// Search for the requested dimension
+	currentDim = dimensionsList.first;
+	while (currentDim != NULL)
+	{
+		if (strcmp (currentDim->name, dimension) == 0)
+		{
+			if (PyInt_AsLong (precision) == 0)
+			{	// Floating precision
+				currentDim->baseFloatingUnit = newBaseUnit;
+			}
+			else
+			{	// Fixed precision
+				currentDim->baseFixedUnit = newBaseUnit;
+			}
+			
+			Py_INCREF (Py_None);
+			return Py_None;
+		}
+		currentDim = currentDim->next;
+	}
+
+	// If got to here, then didn't find the dimension we were looking for, so fail
+	LogError ("Undefined dimension when setting base unit: %s\n", dimension);
+	PyErr_SetString (PyExc_ValueError, "Undefined dimension");
+	return NULL;
+}
+
+// Doc string for DefineDimension
+PyDoc_STRVAR (setBaseUnitDoc,
+"SetBaseUnit (dimension, precision, baseUnitName)"
+"Set the base unit for a dimension for either floating or fixed precision units.");*/
+
+///	Get a list of the currently defined dimensions.
+///	Call to get a python list object of tuples, with each tuple containing the
+///	relevant information for one dimension.
+///	Returns: a pointer to a python list, or NULL on failure.
+static PyObject* GetDimensionsList (PyObject *self, PyObject *args)
+{
+	Dimension *currentDim = NULL;
+	PyListObject *result = NULL;
+	
+	if ((result = (PyListObject*) PyList_New (0)) == NULL)
+	{
+		return NULL;
+	}
+	
+	currentDim = dimensionsList.first;
+	while (currentDim != NULL)
+	{
+		if (PyList_Append ((PyObject*) result, PyString_FromString (currentDim->name)) < 0)
+			return NULL;
+		currentDim = currentDim->next;
+	}
+	
+	return (PyObject*) result;
+}
+
+// Doc string for GetDimensionsList
+PyDoc_STRVAR (getDimensionsListDoc,
+"Dimensions ()"
+"Returns a list of tuples, with each tuple representing one defined dimension.");
+
+///	Find a dimension with a certain name.
+///	Receives: A string containing the name to search for
+///	Returns: A pointer to the dimension structure, if found, or Null if not.
+Dimension* Dim_FindDimension (char *dimensionName)
+{
+	Dimension *currentDim = NULL;
+	
+	currentDim = dimensionsList.first;
+	while (currentDim != NULL)
+	{
+		if (strcmp (currentDim->name, dimensionName) == 0)
+			return currentDim;
+		currentDim = currentDim->next;
+	}
+	
+	return currentDim;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//	Units management
+////////////////////////////////////////////////////////////////////////////////
+
+///	Define a new dimension unit.
+///	Call to define a new dimension unit with the information passed. The unit
+///	can then be used in the system.
+///	Returns: Py_None on success, NULL on failure.
+PyObject* DefineDimensionUnit (PyObject *self, PyObject *args)
+{
+	Unit *newUnit = NULL, *currentUnit = NULL;
+	Dimension *currentDimension = NULL;
+	int dimensionExists = 0;
+	char *name = NULL, *dimension = NULL;
+	PyObject *precisionObj = NULL, *rangeObj1 = NULL, *rangeObj2 = NULL, *rangeObj3 = NULL, *rangeObj4 = NULL;
+	double extra = 0.0f;
+	UnitRange *rangeInfo = NULL;
+
+	// Unpack the arguments
+	if (!PyArg_ParseTuple (args, "ssOd|OOOO:defineunit", &dimension, &name, &precisionObj, &extra, &rangeObj1, &rangeObj2, &rangeObj3, &rangeObj4))
+		return NULL;
+		
+	// Check the dimension exists
+	currentDimension = dimensionsList.first;
+	while (currentDimension != NULL)
+	{
+		if (strcmp (dimension, currentDimension->name) == 0)
+		{
+			dimensionExists = 1;
+			break;
+		}
+		currentDimension = currentDimension->next;
+	}
+	if (!dimensionExists)
+	{
+		LogError ("Undefined dimension \"%s\" when trying to define unit: %s\n", dimension, name);
+		PyErr_SetString (PyExc_ValueError, "Dimension does not exist");
+		return NULL;
+	}
+	
+	// Check for an existing unit with the same name
+	currentUnit = dimensionUnitsList.first;
+	while (currentUnit != NULL)
+	{
+		if (strcmp (name, currentUnit->name) == 0)
+		{
+			LogError ("Unit already defined error when defining new unit: %s\n", name);
+			PyErr_SetString (PyExc_ValueError, "Unit already defined");
+			return NULL;
+		}
+		currentUnit = currentUnit->next;
+	}
+	
+	// Check the precision value is the correct type of object
+	if (!PyDimPrec_Check (precisionObj))
+	{
+		PyErr_SetString (PyExc_TypeError, "Illegal precision type");
+		return NULL;
+	}
+	
+	// Set up the range info if needed
+	if (rangeObj1 != NULL)
+	{
+		if ((rangeInfo = malloc (sizeof (UnitRange))) == NULL)
+		{
+			PyErr_SetString (PyExc_MemoryError, "Insufficient memory to create range info structure");
+			return NULL;
+		}
+		// Check if the first object is a range type
+		if (PyDimRange_Check (rangeObj1))
+		{
+			int temp = PyInt_AS_LONG (rangeObj1);
+			if (temp == 0)
+				rangeInfo->minType = DIM_RT_INCLUDE;
+			else if (temp == 1)
+				rangeInfo->minType = DIM_RT_EXCLUDE;
+			else if (temp == 2)
+				rangeInfo->minType = DIM_RT_INFINITE;
+			else if (temp == 3)
+			{
+				rangeInfo->minType = DIM_RT_WRAP;
+				rangeInfo->maxType = DIM_RT_WRAP;
+				rangeInfo->fxRangeMax = rangeInfo->flRangeMax = PyFloat_AsDouble (rangeObj4);
+			}
+			else
+			{
+				PyErr_SetString (PyExc_TypeError, "Illegal min range type");
+				free (rangeInfo);
+				return NULL;
+			}
+	
+			rangeInfo->fxRangeMin = rangeInfo->flRangeMin = PyFloat_AsDouble (rangeObj2);
+		}
+		else
+		{
+			PyErr_SetString (PyExc_TypeError, "Illegal min range type");
+			free (rangeInfo);
+			return NULL;
+		}
+		// Check if the second object exists and is a range type - but only if previous wasn't a wrap range
+		if (rangeObj3 != NULL && PyDimRange_Check (rangeObj3) && rangeInfo->minType != DIM_RT_WRAP)
+		{
+			int temp = PyInt_AS_LONG (rangeObj3);
+			if (temp == 0)
+				rangeInfo->maxType = DIM_RT_INCLUDE;
+			else if (temp == 1)
+				rangeInfo->maxType = DIM_RT_EXCLUDE;
+			else if (temp == 2)
+				rangeInfo->maxType = DIM_RT_INFINITE;
+			else
+			{
+				PyErr_SetString (PyExc_TypeError, "Illegal max range type");
+				free (rangeInfo);
+				return NULL;
+			}
+		
+			rangeInfo->fxRangeMax = rangeInfo->flRangeMax = PyFloat_AsDouble (rangeObj4);
+		}
+		else if (rangeInfo->minType != DIM_RT_WRAP)
+		{
+			PyErr_SetString (PyExc_TypeError, "Illegal max range type");
+			free (rangeInfo);
+			return NULL;
+		}
+	}
+
+	// Allocate and fill in a new unit structure
+	if ((newUnit = malloc (sizeof (Unit))) == NULL)
+	{
+		PyErr_SetString (PyExc_MemoryError, "Insufficient memory to create new unit");
+		if (rangeInfo != NULL)
+			free (rangeInfo);
+		return NULL;
+	}
+	newUnit->name = strdup (name);
+	newUnit->dimension = currentDimension;
+	newUnit->precision = (PyInt_AS_LONG (precisionObj) == 0 ? DIM_UP_FLOATING : DIM_UP_FIXED);
+	newUnit->extra = extra;
+	newUnit->rangeLimits = rangeInfo;
+	newUnit->next = NULL;
+
+	// Add the structure to the list
+	if (dimensionUnitsList.first == NULL)
+		dimensionUnitsList.first = newUnit;
+	if (dimensionUnitsList.last != NULL)
+		dimensionUnitsList.last->next = newUnit;
+	dimensionUnitsList.last = newUnit;
+	
+	Py_INCREF (Py_None);
+	return Py_None;
+}
+
+// Doc string for DefineUnit
+PyDoc_STRVAR (defineDimensionUnitDoc,
+"DefineDimensionUnit (dimension, name, precision, extra, [minRangeType, minRangeValue,"
+"maxRangeType, maxRangeValue])\n"
+"\n"
+"Add a new unit to the list of available units for dimensioned data.\n"
+"\'dimension\' is the name of the dimension to add the unit to.\n"
+"\'name\' is the name to use for the unit - don't make it too long\n"
+"because you will need to type it whenever you create a new value\n"
+"with your unit.\n"
+"\'precision\' is the precision of values used with the unit: either\n"
+"UnitPrec_Floating or UnitPrec_Fixed.\n"
+"\'extra\' depends on the precision. For UnitPrec_Floating, it specifies\n"
+"the ratio between this unit at the base unit in the same dimension/precision\n"
+"For UnitPrec_Fixed it specifies the resolution of the unit. For\n"
+"example, for the default distance/fixed unit \"cell\", this value is 1.0,\n"
+"giving a cell a resolution of 1 metre.\n"
+"\'minRangeType\' and \'maxRangeType are one each of: UnitRange_Include,\n"
+"UnitRange_Exclude, UnitRange_Infinite, UnitRange_Wrap.\n"
+"\'minRangeValue\' and \'maxRangeValue\' specify the values of the range limits.");
+
+
+///	Define a new unit
+///	For internal use only! Does not perform any checking at all for things like conflicting units!
+///	Receives: The dimension to create the unit in, the name for the unit, the precision, extra number, and range limits.
+///	Returns: 1 for success, 0 for failure
+int DefineDimensionUnit_Internal (char *dimension, char *name, DIM_UNITPRECISION precision, double extra, UnitRange *rangeLimits)
+{
+	Unit *newUnit = NULL;
+	Dimension *currentDimension = NULL;
+	int dimensionExists = 0;
+	
+	// Find the dimension this unit should be in
+	currentDimension = dimensionsList.first;
+	while (currentDimension != NULL)
+	{
+		if (strcmp (dimension, currentDimension->name) == 0)
+		{
+			dimensionExists = 1;
+			break;
+		}
+		currentDimension = currentDimension->next;
+	}
+	if (!dimensionExists)
+	{
+		LogError ("(Internal) Undefined dimension \"%s\" when trying to define unit: %s\n", dimension, name);
+		return 0;
+	}
+	
+	// Allocate and fill in a new unit structure
+	if ((newUnit = malloc (sizeof (Unit))) == NULL)
+		return 0;
+	newUnit->name = strdup (name);
+	newUnit->dimension = currentDimension;
+	newUnit->precision = precision;
+	newUnit->extra = extra;
+	if (rangeLimits != NULL)
+	{
+		if ((newUnit->rangeLimits = malloc (sizeof (UnitRange))) == NULL)
+		{
+			free (newUnit);
+			return 0;
+		}
+		memcpy (newUnit->rangeLimits, rangeLimits, sizeof (UnitRange));
+	}
+	else
+		newUnit->rangeLimits = NULL;
+	newUnit->next = NULL;
+
+	// Add the structure to the list
+	if (dimensionUnitsList.first == NULL)
+		dimensionUnitsList.first = newUnit;
+	if (dimensionUnitsList.last != NULL)
+		dimensionUnitsList.last->next = newUnit;
+	dimensionUnitsList.last = newUnit;
+	
+	return 1;
+}
+
+///	Get a list of the currently defined units.
+///	Call to get a python list object of tuples, with each tuple containing the
+///	relevant information for one unit.
+///	Returns: a pointer to a python list, or NULL on failure.
+static PyObject* GetUnitsList (PyObject *self, PyObject *args)
+{
+	Unit *currentUnit = NULL;
+	PyListObject *result = NULL;
+	PyTupleObject *tuple = NULL;
+	PyDimPrecObject *prec = NULL;
+	PyDimRangeObject *minRange = NULL, *maxRange = NULL;
+	
+	if ((result = (PyListObject*) PyList_New (0)) == NULL)
+	{
+		return NULL;
+	}
+	
+	currentUnit = dimensionUnitsList.first;
+	while (currentUnit != NULL)
+	{
+		// Check the unit type and precision
+		prec = (PyDimPrecObject*) (currentUnit->precision == DIM_UP_FLOATING ? Py_DimPrec_Floating : Py_DimPrec_Fixed);
+		Py_XINCREF (prec);
+		
+		if (currentUnit->rangeLimits == NULL)
+		{	// No range limits present
+			if ((tuple = (PyTupleObject*) Py_BuildValue ("ssOd", currentUnit->name, currentUnit->dimension->name, prec, currentUnit->extra)) == NULL)
+				return NULL;
+		}
+		else
+		{	// Range limits present - check them
+			switch (currentUnit->rangeLimits->minType)
+			{
+			case DIM_RT_EXCLUDE:
+				minRange = (PyDimRangeObject*) Py_DimRange_Exclude;
+				break;
+			case DIM_RT_INCLUDE:
+				minRange = (PyDimRangeObject*) Py_DimRange_Include;
+				break;
+			case DIM_RT_INFINITE:
+				minRange = (PyDimRangeObject*) Py_DimRange_Infinite;
+				break;
+			case DIM_RT_WRAP:
+				minRange = (PyDimRangeObject*) Py_DimRange_Wrap;
+				break;
+			}
+			Py_XINCREF (minRange);
+			switch (currentUnit->rangeLimits->maxType)
+			{
+			case DIM_RT_EXCLUDE:
+				maxRange = (PyDimRangeObject*) Py_DimRange_Exclude;
+				break;
+			case DIM_RT_INCLUDE:
+				maxRange = (PyDimRangeObject*) Py_DimRange_Include;
+				break;
+			case DIM_RT_INFINITE:
+				maxRange = (PyDimRangeObject*) Py_DimRange_Infinite;
+				break;
+			case DIM_RT_WRAP:
+				maxRange = (PyDimRangeObject*) Py_DimRange_Wrap;
+				break;
+			}
+			Py_XINCREF (maxRange);
+			if ((tuple = (PyTupleObject*) Py_BuildValue ("ssOdOdOd", currentUnit->name, currentUnit->dimension->name, prec, currentUnit->extra,
+				minRange, (currentUnit->precision == DIM_UP_FLOATING ? currentUnit->rangeLimits->flRangeMin : currentUnit->rangeLimits->fxRangeMin),
+				maxRange, (currentUnit->precision == DIM_UP_FLOATING ? currentUnit->rangeLimits->flRangeMax : currentUnit->rangeLimits->fxRangeMax))) == NULL)
+				return NULL;
+		}
+
+		if (PyList_Append ((PyObject*) result, (PyObject*) tuple) < 0)
+			return NULL;
+		currentUnit = currentUnit->next;
+	}
+	
+	return (PyObject*) result;
+}
+
+// Doc string for GetUnitsList
+PyDoc_STRVAR (getUnitsListDoc,
+"Units ()"
+"Returns a list of tuples, with each tuple representing one defined unit.");
+
+///	Find a unit with a certain name.
+///	Receives: A string containing the name to search for
+///	Returns: A pointer to the unit structure, if found, or Null if not.
+Unit* Dim_FindUnit (char *unitName)
+{
+	Unit *currentUnit = NULL;
+	
+	currentUnit = dimensionUnitsList.first;
+	while (currentUnit != NULL)
+	{
+		if (strcmp (currentUnit->name, unitName) == 0)
+			return currentUnit;
+		currentUnit = currentUnit->next;
+	}
+	
+	return currentUnit;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//	Module Initialisation
+////////////////////////////////////////////////////////////////////////////////
+
+// Keeps track of if the default dimensions and units have been initialised
+static int unitsInited = 0;
+static int dimensionsInited = 0;
+
+///	Define a default set of units in the system.
+///	Called when the module is initialised to define all the default units.
+///	Receives: nothing
+///	Returns: 1 for success, 0 for failure
+int InitDefaultUnits (void)
+{
+	// If the default units have already been initialised, return immediatly
+	if (unitsInited)
+		return 1;
+		
+	// Metres unit
+	if (!DefineDimensionUnit_Internal("distance", "m", DIM_UP_FLOATING, 1.0f, NULL))
+		return 0;
+	// Cells unit
+	if (!DefineDimensionUnit_Internal ("distance", "cell", DIM_UP_FIXED, 1.0f, NULL))
+		return 0;
+	// Millimetres unit
+	if (!DefineDimensionUnit_Internal ("distance", "mm", DIM_UP_FLOATING, 0.001f, NULL))
+		return 0;
+	// Centimetres unit
+	if (!DefineDimensionUnit_Internal ("distance", "cm", DIM_UP_FLOATING, 0.01f, NULL))
+		return 0;
+	// Kilometres unit
+	if (!DefineDimensionUnit_Internal ("distance", "km", DIM_UP_FLOATING, 1000.0f, NULL))
+		return 0;
+	
+	// Radians unit
+	if (!DefineDimensionUnit_Internal ("angle", "rad", DIM_UP_FLOATING, 1.0f, NULL))
+		return 0;
+	// Ticks unit (basically fixed precision degrees)
+	if (!DefineDimensionUnit_Internal ("angle", "tick", DIM_UP_FIXED, 57.29577951309679f, NULL))
+		return 0;
+	// Degrees unit
+	if (!DefineDimensionUnit_Internal ("angle", "deg", DIM_UP_FLOATING, 0.017453292519943295f, NULL))
+		return 0;
+	// Hundreths of degrees unit
+	if (!DefineDimensionUnit_Internal ("angle", "cdeg", DIM_UP_FLOATING, 0.00017453292519943295f, NULL))
+		return 0;
+	// Milli-rads
+	if (!DefineDimensionUnit_Internal ("angle", "mrad", DIM_UP_FLOATING, 0.001f, NULL))
+		return 0;
+	// Arcminutes unit
+	if (!DefineDimensionUnit_Internal ("angle", "arcmin", DIM_UP_FLOATING, 0.95492965855137213f, NULL))
+		return 0;
+	// Arcseconds unit
+	if (!DefineDimensionUnit_Internal ("angle", "arcsec", DIM_UP_FLOATING, 0.015915494309189534f, NULL))
+		return 0;
+	// Wrapped degrees unit
+	UnitRange range = { 0.0f, 360.0f, 0, 360, DIM_RT_WRAP, DIM_RT_WRAP };
+	if (!DefineDimensionUnit_Internal ("angle", "wdeg", DIM_UP_FLOATING, 57.29577951309679f, &range))
+		return 0;
+	// Wrapped radians unit
+	range.flRangeMin = 0.0f;        range.flRangeMax = 2.0f * DIM_PI;
+	range.fxRangeMin = 0;           range.fxRangeMax = 2 * DIM_PI;
+	if (!DefineDimensionUnit_Internal ("angle", "wrad", DIM_UP_FLOATING, 1.0f, &range))
+		return 0;
+
+	if (!DefineDimensionUnit_Internal ("time", "ms", DIM_UP_FLOATING, 0.001f, NULL))
+		return 0;
+	// Seconds unit
+	if (!DefineDimensionUnit_Internal ("time", "s", DIM_UP_FLOATING, 1.0f, NULL))
+		return 0;
+	// Minutes unit
+	if (!DefineDimensionUnit_Internal ("time", "min", DIM_UP_FLOATING, 60.0f, NULL))
+		return 0;
+	// Hours unit
+	if (!DefineDimensionUnit_Internal ("time", "h", DIM_UP_FLOATING, 3600.0f, NULL))
+		return 0;
+		
+	// Grams unit
+	if (!DefineDimensionUnit_Internal ("mass", "g", DIM_UP_FLOATING, 1.0f, NULL))
+		return 0;
+	// Kilograms unit
+	if (!DefineDimensionUnit_Internal ("mass", "kg", DIM_UP_FLOATING, 1.0f, NULL))
+		return 0;
+
+	// All done, let's get out of here
+	unitsInited = 1;
+	return 1;
+}
+
+///	Define a default set of dimensions in the system.
+///	Called when the module is initialised to define all the default dimensions.
+///	Receives: nothing
+///	Returns: 1 for success, 0 for failure
+int InitDefaultDimensions (void)
+{
+	PyTupleObject *tuple = NULL;
+	
+	if (dimensionsInited)
+		return 1;
+	
+	// First, create the dimensions
+	if ((tuple = (PyTupleObject*) Py_BuildValue ("(s)", "distance")) == NULL)
+		return 0;
+	if (DefineDimension (NULL, (PyObject*) tuple) == NULL)
+		return 0;
+	if ((tuple = (PyTupleObject*) Py_BuildValue ("(s)", "angle")) == NULL)
+		return 0;
+	if (DefineDimension (NULL, (PyObject*) tuple) == NULL)
+		return 0;
+	if ((tuple = (PyTupleObject*) Py_BuildValue ("(s)", "time")) == NULL)
+		return 0;
+	if (DefineDimension (NULL, (PyObject*) tuple) == NULL)
+		return 0;
+	if ((tuple = (PyTupleObject*) Py_BuildValue ("(s)", "mass")) == NULL)
+		return 0;
+	if (DefineDimension (NULL, (PyObject*) tuple) == NULL)
+		return 0;
+	
+	// Now add the default units
+	if (!InitDefaultUnits ())
+		return 0;
+	
+	dimensionsInited = 1;
+	return 1;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//	Module setup
+////////////////////////////////////////////////////////////////////////////////
+
+// Doc string for this module
+PyDoc_STRVAR (dimensionModuleDoc,
+"This module provides functions for managing the dimension analysis\n"
+"systems.");
+
+// Methods for the module
+static PyMethodDef dimensionMethods[] =
+{
+	// Dimension methods
+	{ "DefineDimension", DefineDimension, METH_VARARGS, defineDimensionDoc},
+	{ "Dimensions", GetDimensionsList, METH_NOARGS, getDimensionsListDoc },
+	// Unit methods
+	{ "DefineUnit", DefineDimensionUnit, METH_VARARGS, defineDimensionUnitDoc},
+	{ "Units", GetUnitsList, METH_NOARGS, getUnitsListDoc },
+	{ NULL }								// Sentinel
+};
+
+// Module init function
+int _PyDimension_Init (void)
+{
+	PyObject *m;
+	
+	if (!InitDefaultDimensions ())
+		return 0;
+
+	m = Py_InitModule3 ("dimension", dimensionMethods, dimensionModuleDoc);
+	return 1;
+}
+
+#endif // __RADAR_EXT__
Index: configure
===================================================================
--- configure	(.../tags/pure-python-2.4.2)	(revision 263)
+++ configure	(.../radar)	(revision 263)
@@ -312,7 +312,7 @@
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS VERSION SOVERSION CONFIG_ARGS PYTHONFRAMEWORK PYTHONFRAMEWORKDIR PYTHONFRAMEWORKPREFIX PYTHONFRAMEWORKINSTALLDIR MACHDEP SGI_ABI EXTRAPLATDIR EXTRAMACHDEPPATH CONFIGURE_MACOSX_DEPLOYMENT_TARGET CXX MAINOBJ EXEEXT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC OBJEXT CPP EGREP BUILDEXEEXT LIBRARY LDLIBRARY DLLLIBRARY BLDLIBRARY LDLIBRARYDIR INSTSONAME RUNSHARED LINKCC RANLIB ac_ct_RANLIB AR INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN OPT BASECFLAGS OTHER_LIBTOOL_OPT LIBTOOL_CRUFT SO LDSHARED BLDSHARED CCSHARED LINKFORSHARED CFLAGSFORSHARED SHLIBS USE_SIGNAL_MODULE SIGNAL_OBJS USE_THREAD_MODULE LDLAST THREADOBJ DLINCLDIR DYNLOADFILE MACHDEP_OBJS TRUE LIBOBJS HAVE_GETHOSTBYNAME_R_6_ARG HAVE_GETHOSTBYNAME_R_5_ARG HAVE_GETHOSTBYNAME_R_3_ARG HAVE_GETHOSTBYNAME_R HAVE_GETHOSTBYNAME LIBM LIBC UNICODE_OBJS THREADHEADERS SRCDIRS LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS VERSION SOVERSION CONFIG_ARGS PYTHONFRAMEWORK PYTHONFRAMEWORKDIR PYTHONFRAMEWORKPREFIX PYTHONFRAMEWORKINSTALLDIR MACHDEP SGI_ABI EXTRAPLATDIR EXTRAMACHDEPPATH CONFIGURE_MACOSX_DEPLOYMENT_TARGET CXX MAINOBJ EXEEXT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC OBJEXT CPP EGREP BUILDEXEEXT LIBRARY LDLIBRARY DLLLIBRARY BLDLIBRARY LDLIBRARYDIR INSTSONAME RUNSHARED LINKCC RANLIB ac_ct_RANLIB AR INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN OPT BASECFLAGS RADARSUFFIX OTHER_LIBTOOL_OPT LIBTOOL_CRUFT SO LDSHARED BLDSHARED CCSHARED LINKFORSHARED CFLAGSFORSHARED SHLIBS USE_SIGNAL_MODULE SIGNAL_OBJS USE_THREAD_MODULE LDLAST THREADOBJ DLINCLDIR DYNLOADFILE MACHDEP_OBJS TRUE LIBOBJS HAVE_GETHOSTBYNAME_R_6_ARG HAVE_GETHOSTBYNAME_R_5_ARG HAVE_GETHOSTBYNAME_R_3_ARG HAVE_GETHOSTBYNAME_R HAVE_GETHOSTBYNAME LIBM LIBC UNICODE_OBJS THREADHEADERS SRCDIRS LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -862,6 +862,8 @@
   --with-cxx=<compiler>   enable C++ support
   --with-suffix=.exe      set executable suffix
   --with-pydebug          build with Py_DEBUG defined
+  --with-radar            build with RADAR robot programming extensions
+  --with-radar-stats=<file> enable statistics collecting
   --with-libs='lib1 ...'  link against additional libs
   --with-signal-module    disable/enable signal module
   --with-dec-threads      use DEC Alpha/OSF1 thread-safe libraries
@@ -3854,6 +3856,48 @@
   OPT="-DNDEBUG $OPT"
 fi
 
+# Check for --with-radar
+
+echo "$as_me:$LINENO: checking for --with-radar" >&5
+echo $ECHO_N "checking for --with-radar... $ECHO_C" >&6
+
+# Check whether --with-radar or --without-radar was given.
+if test "${with_radar+set}" = set; then
+  withval="$with_radar"
+
+if test "$withval" != no
+then
+  OPT="$OPT -D__RADAR_EXT__"
+  RADARSUFFIX="_radar"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6;
+else echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; RADARSUFFIX=;
+fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi;
+
+# Check for --with-radar-stats
+echo "$as_me:$LINENO: checking for --with-radar-stats" >&5
+echo $ECHO_N "checking for --with-radar-stats... $ECHO_C" >&6
+
+# Check whether --with-radar-stats or --without-radar-stats was given.
+if test "${with_radar_stats+set}" = set; then
+  withval="$with_radar_stats"
+
+if test "$withval" != no
+then
+  OPT="$OPT -D__RADAR_STATS_FILE__=$withval"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6;
+fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi;
+
 if test "$ac_arch_flags"
 then
 	BASECFLAGS="$BASECFLAGS $ac_arch_flags"
@@ -21073,6 +21117,7 @@
 s,@LN@,$LN,;t t
 s,@OPT@,$OPT,;t t
 s,@BASECFLAGS@,$BASECFLAGS,;t t
+s,@RADARSUFFIX@,$RADARSUFFIX,;t t
 s,@OTHER_LIBTOOL_OPT@,$OTHER_LIBTOOL_OPT,;t t
 s,@LIBTOOL_CRUFT@,$LIBTOOL_CRUFT,;t t
 s,@SO@,$SO,;t t
Index: Include/unitobject.h
===================================================================
--- Include/unitobject.h	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Include/unitobject.h	(.../radar)	(revision 263)
@@ -0,0 +1,37 @@
+/* Unit object - header file	*
+ * Geoffrey Biggs 2005				*/
+ 
+#ifndef Py_UNITOBJECT_H
+#define Py_UNITOBJECT_H
+
+#ifdef __RADAR_EXT__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Structure of the unit object */
+typedef struct PyUnitObject
+{
+	PyObject_HEAD
+	PyObject *units;		// A PyListObject containing the units
+	int numUnits;
+} PyUnitObject;
+
+
+#define PyUnit_Check(op) PyObject_TypeCheck(op, &PyUnit_Type)
+#define PyUnit_CheckExact(op) ((op)->ob_type == &PyUnit_Type)
+
+PyAPI_DATA(PyTypeObject) PyUnit_Type;
+
+PyAPI_FUNC(PyObject*) UnitOb_FromList (PyObject *list);
+//PyAPI_FUNC(void) UnitOb_AppendUnit (PyUnitObject *unitOb, struct TokenUnit *newUnit);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	// __RADAR_EXT__
+
+#endif	// Py_UNITOBJECT_H
Index: Include/dimensionobject.h
===================================================================
--- Include/dimensionobject.h	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Include/dimensionobject.h	(.../radar)	(revision 263)
@@ -0,0 +1,53 @@
+/* Dimensioned object - header file	*
+ * Geoffrey Biggs 2005				*/
+ 
+#ifndef Py_DIMENSIONOBJECT_H
+#define Py_DIMENSIONOBJECT_H
+
+#ifdef __RADAR_EXT__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "dimensiontypes.h"
+	
+/* Structure of the dimensioned data object */
+typedef struct
+{
+	PyObject_HEAD
+	DIM_UNITPRECISION precision;		// Precision from the units used.
+	PyObject *units;					// The distance's units (metres, millimetres, etc) if type is DIM_UT_DIMENSION
+	double floatingMeasure;				// The distance value in floating point representation (for floating precision)
+	long fixedMeasure;					// The distance value in integer representation (for fixed precision)
+} PyDimensionedObject;
+
+
+#define PyDimensioned_Check(op) PyObject_TypeCheck(op, &PyDimensioned_Type)
+#define PyDimensioned_CheckExact(op) ((op)->ob_type == &PyDimensioned_Type)
+
+PyAPI_DATA(PyTypeObject) PyDimensioned_Type;
+
+PyAPI_FUNC (PyObject*) DimOb_FromRaw (char *units, double floatingMeasure, long fixedMeasure);
+PyAPI_FUNC (PyObject*) DimOb_FromList (PyObject *units, double floatingMeasure, long fixedMeasure);
+PyAPI_FUNC (void) DimOb_UnitsListToString (char *buf, int bufSize, PyObject *units);
+PyAPI_FUNC (PyObject*) DimOb_ConvertUnits (PyDimensionedObject *dimOb, PyObject *newUnits);
+// Useful function for creating unit lists
+PyAPI_FUNC (PyObject*) DimOb_CreateUnitList (int numArgs, ...);
+
+// Handy macros
+#define DIM_ISFLOATING(d)			(d->precision == DIM_UP_FLOATING)
+#define DIM_ISFIXED(d)				(d->precision == DIM_UP_FIXED)
+#define DIM_ISSINGLE(d)				(d->type == DIM_UT_SINGLE)
+#define DIM_ISDERIVED(d)			(d->type == DIM_UT_DERIVED)
+#define DIM_SAMYPRECISION(d1,d2)	(d1->precision == d2->precision)
+
+	
+#ifdef __cplusplus
+}
+#endif
+
+#endif	// __RADAR_EXT__
+
+#endif	// Py_DIMENSIONOBJECT_H
Index: Include/graminit.h
===================================================================
--- Include/graminit.h	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Include/graminit.h	(.../radar)	(revision 263)
@@ -75,4 +75,7 @@
 #define gen_for 330
 #define gen_if 331
 #define testlist1 332
-#define encoding_decl 333
+#define unit 333
+#define unit_expr 334
+#define unit_atom 335
+#define encoding_decl 336
Index: Include/dimrangeobject.h
===================================================================
--- Include/dimrangeobject.h	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Include/dimrangeobject.h	(.../radar)	(revision 263)
@@ -0,0 +1,31 @@
+/* Distance range object - header file	*
+ * Geoffrey Biggs 2004					*/
+ 
+#ifndef Py_DISTANCERANGEOBJECT_H
+#define Py_DISTANCERANGEOBJECT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+	
+#define PyDimRange_Check(x) ((x)->ob_type == &PyDimRange_Type)
+
+// 4 objects for specifying unit types (easier than strings)
+typedef PyIntObject PyDimRangeObject;
+PyAPI_DATA(PyTypeObject) PyDimRange_Type;
+	
+/* Don't use these directly */
+PyAPI_DATA(PyIntObject) _Py_DimRange_InfiniteStruct, _Py_DimRange_IncludeStruct, _Py_DimRange_ExcludeStruct, _Py_DimRange_WrapStruct;
+
+// Object macros for the 3 range setting objects
+#define Py_DimRange_Include ((PyObject *) &_Py_DimRange_IncludeStruct)
+#define Py_DimRange_Exclude ((PyObject *) &_Py_DimRange_ExcludeStruct)
+#define Py_DimRange_Infinite ((PyObject *) &_Py_DimRange_InfiniteStruct)
+#define Py_DimRange_Wrap	((PyObject *) &_Py_DimRange_WrapStruct)
+	
+#ifdef __cplusplus
+}
+#endif
+
+#endif	// Py_DISTANCERANGEOBJECT_H
Index: Include/token.h
===================================================================
--- Include/token.h	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Include/token.h	(.../radar)	(revision 263)
@@ -62,6 +62,8 @@
 #define OP		51
 #define ERRORTOKEN	52
 #define N_TOKENS	53
+#define DOLLAR	54
+#define UNITKEYWORD	55
 
 /* Special definitions for cooperation with parser */
 
Index: Include/Python.h
===================================================================
--- Include/Python.h	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Include/Python.h	(.../radar)	(revision 263)
@@ -107,6 +107,13 @@
 #include "genobject.h"
 #include "descrobject.h"
 #include "weakrefobject.h"
+#ifdef __RADAR_EXT__
+// RADAR includes
+#include "dimprecobject.h"
+#include "dimrangeobject.h"
+#include "dimensionobject.h"
+#include "unitobject.h"
+#endif // __RADAR_EXT__
 
 #include "codecs.h"
 #include "pyerrors.h"
Index: Include/pythonrun.h
===================================================================
--- Include/pythonrun.h	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Include/pythonrun.h	(.../radar)	(revision 263)
@@ -105,6 +105,9 @@
 PyAPI_FUNC(void) _PyImportHooks_Init(void);
 PyAPI_FUNC(int) _PyFrame_Init(void);
 PyAPI_FUNC(int) _PyInt_Init(void);
+#ifdef __RADAR_EXT__
+PyAPI_FUNC(int) _PyDimension_Init (void);
+#endif // __RADAR_EXT__
 
 /* Various internal finalizers */
 PyAPI_FUNC(void) _PyExc_Fini(void);
Index: Include/dimensiontypes.h
===================================================================
--- Include/dimensiontypes.h	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Include/dimensiontypes.h	(.../radar)	(revision 263)
@@ -0,0 +1,80 @@
+/************************************************************
+ * Dimensional Analysis types								*
+ * Geoffrey Biggs 2005										*
+ * Supplies the lists and functions for managing dimensions	*
+ * and units used with the dimensioned data type.			*
+ ************************************************************/
+
+#ifdef __RADAR_EXT__
+
+#ifndef Py_DIMENSIONTYPES_H
+#define Py_DIMENSIONTYPES_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+// Unit settings
+typedef enum { DIM_RT_INCLUDE, DIM_RT_EXCLUDE, DIM_RT_INFINITE, DIM_RT_WRAP } DIM_RANGETYPE;
+typedef enum { DIM_UT_SINGLE, DIM_UT_DERIVED } DIM_UNITTYPE;
+typedef enum { DIM_UP_FLOATING, DIM_UP_FIXED } DIM_UNITPRECISION;
+
+// A structure to store range information
+typedef struct UnitRange
+{
+	double flRangeMin, flRangeMax;	// Range limits for floating point units
+	long fxRangeMin, fxRangeMax;	// Range limits for fixed point units
+	DIM_RANGETYPE minType, maxType;	// Types of range limits
+} UnitRange;
+
+// Forward declarations
+struct Dimension;
+
+// A structure to store unit information for dimension units
+typedef struct Unit
+{
+	char *name;							// Unit name - used in code, unique to this unit
+	struct Dimension *dimension;		// The dimension this unit is in
+	DIM_UNITPRECISION precision;		// Precision of values that use this unit - floating or fixed
+	double extra;						// If the unit precision is not fixed, this is the ratio between this unit and the unit's dimenion's base unit.
+										// If the unit precision is fixed, this is the size of 1 fixed quantity in equivalent non-fixed (eg, for a
+										// cell on a grid of resolution 1.5m, this would be 1.5).
+	UnitRange *rangeLimits;				// If not NULL, the range limits on data using this unit
+
+	struct Unit *next;			// Next dimension unit in the list
+} Unit;
+
+// A linked-list for storing units defined in the system
+typedef struct UnitList
+{
+	Unit *first, *last;
+} UnitList;
+
+// A structure to store dimension information
+typedef struct Dimension
+{
+	char *name;							// Dimension name
+	unsigned char index;				// Dimension index (useful for quick identification elsewhere)
+	
+	struct Dimension *next;				// Next dimension in the list
+} Dimension;
+
+// A linked-list for storing dimensions defined in the system
+typedef struct DimensionList
+{
+	Dimension *first, *last;
+} DimensionList;
+
+// These are defined in dimensionmodule.c
+Dimension* Dim_FindDimension (char *dimensionName);
+Unit* Dim_FindUnit (char *unitName);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // Py_DIMENSIONTYPES_H
+
+#endif // __RADAR_EXT__
Index: Include/dimprecobject.h
===================================================================
--- Include/dimprecobject.h	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Include/dimprecobject.h	(.../radar)	(revision 263)
@@ -0,0 +1,29 @@
+/* Distance range object - header file	*
+ * Geoffrey Biggs 2004					*/
+ 
+#ifndef Py_DIMPRECOBJECT_H
+#define Py_DIMPRECOBJECT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+	
+#define PyDimPrec_Check(x) ((x)->ob_type == &PyDimPrec_Type)
+	
+// 4 objects for specifying unit types (easier than strings)
+typedef PyIntObject PyDimPrecObject;
+PyAPI_DATA(PyTypeObject) PyDimPrec_Type;
+	
+/* Don't use these directly */
+PyAPI_DATA(PyIntObject) _Py_DimPrec_FloatingStruct, _Py_DimPrec_FixedStruct;
+
+// Object macros for the 2 precision setting objects
+#define Py_DimPrec_Floating ((PyObject *) &_Py_DimPrec_FloatingStruct)
+#define Py_DimPrec_Fixed ((PyObject *) &_Py_DimPrec_FixedStruct)
+	
+#ifdef __cplusplus
+}
+#endif
+
+#endif	// Py_DIMPRECOBJECT_H
Index: Grammar/Grammar
===================================================================
--- Grammar/Grammar	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Grammar/Grammar	(.../radar)	(revision 263)
@@ -86,7 +86,7 @@
 term: factor (('*'|'/'|'%'|'//') factor)*
 factor: ('+'|'-'|'~') factor | power
 power: atom trailer* ['**' factor]
-atom: '(' [testlist_gexp] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+
+atom: '(' [testlist_gexp] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER ['~' unit_expr] | STRING+ | unit_atom
 listmaker: test ( list_for | (',' test)* [','] )
 testlist_gexp: test ( gen_for | (',' test)* [','] )
 lambdef: 'lambda' [varargslist] ':' test
@@ -114,5 +114,9 @@
 
 testlist1: test (',' test)*
 
+unit: [['-'] NUMBER] NAME ['^' ['-'] NUMBER]
+unit_expr: unit (('*' unit) | ('/' unit))*
+unit_atom: '$' unit_expr
+
 # not used in grammar, but may appear in "node" passed from Parser to Compiler
 encoding_decl: NAME
Index: configure.in
===================================================================
--- configure.in	(.../tags/pure-python-2.4.2)	(revision 263)
+++ configure.in	(.../radar)	(revision 263)
@@ -751,6 +751,32 @@
   OPT="-DNDEBUG $OPT"
 fi
 
+# Check for --with-radar
+AC_SUBST(RADARSUFFIX)
+AC_MSG_CHECKING(for --with-radar)
+AC_ARG_WITH(radar,
+            AC_HELP_STRING(--with-radar, build with RADAR robot programming extensions),
+[
+if test "$withval" != no
+then
+  OPT="$OPT -D__RADAR_EXT__"
+  RADARSUFFIX="_radar"
+  AC_MSG_RESULT(yes);
+else AC_MSG_RESULT(no); RADARSUFFIX=;
+fi],
+[AC_MSG_RESULT(no)])
+
+# Check for --with-radar-stats
+AC_MSG_CHECKING(for --with-radar-stats)
+AC_ARG_WITH(radar-stats, AC_HELP_STRING(--with-radar-stats=<file>, enable statistics collecting, saving to the specified file),
+[
+if test "$withval" != no
+then
+  OPT="$OPT -D__RADAR_STATS_FILE__=$withval"
+  AC_MSG_RESULT(yes);
+fi],
+[AC_MSG_RESULT(no)])
+
 if test "$ac_arch_flags"
 then
 	BASECFLAGS="$BASECFLAGS $ac_arch_flags"
Index: setup.py
===================================================================
--- setup.py	(.../tags/pure-python-2.4.2)	(revision 263)
+++ setup.py	(.../radar)	(revision 263)
@@ -313,6 +313,8 @@
         # math library functions, e.g. sin()
         exts.append( Extension('math',  ['mathmodule.c'],
                                libraries=math_libs) )
+        exts.append( Extension('mathd',  ['mathdmodule.c'],
+                               libraries=math_libs) )
         # fast string operations implemented in C
         exts.append( Extension('strop', ['stropmodule.c']) )
         # time operations and variables
Index: Objects/unitobject.c
===================================================================
--- Objects/unitobject.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Objects/unitobject.c	(.../radar)	(revision 263)
@@ -0,0 +1,204 @@
+/************************************************************
+ * Distance object implementation							*
+ * Geoffrey Biggs 2004										*
+ * Borrows from the complex data type by Jim Hugunin and	*
+ * the float data type.										*
+ ************************************************************/
+ 
+#ifdef __RADAR_EXT__
+
+#include "Python.h"
+#include "structmember.h"
+
+#define UNIT_PREC_REPR				15
+#define UNIT_PREC_STR				10
+
+PyObject* UnitOb_FromList (PyObject *list)
+{
+	PyUnitObject *unitOb = NULL;
+
+	if (!PyList_Check (list))
+	{
+		PyErr_SetString(PyExc_TypeError, "Must provide a list when creating a unit object");
+		return NULL;
+	}
+
+	// Inline PyObject_New
+	// Create a new PyUnitObject instance
+	unitOb = (PyUnitObject *) PyObject_MALLOC (sizeof (PyUnitObject));
+	if (unitOb == NULL)
+		return PyErr_NoMemory ();
+	PyObject_INIT (unitOb, &PyUnit_Type);
+
+	unitOb->units = list;
+	Py_INCREF (unitOb->units);
+	unitOb->numUnits = PyList_Size (list);
+	
+	return (PyObject *) unitOb;
+}
+
+
+static void UnitOb_ToString (char *buf, int bufSize, PyUnitObject *unitOb, int precision)
+{
+	DimOb_UnitsListToString (buf, bufSize, unitOb->units);
+}
+
+//	tp_print function - have a guess what this does.
+//	dimOb: The relevant dimensioned object
+//	fp: A file to print to.
+//	flags: I can't remember.
+//	Returns: Nothing but 0, by the looks of things
+static int UnitOb_Print (PyUnitObject *unitOb, FILE *fp, int flags)
+{
+	char buf[1024];
+	
+	UnitOb_ToString (buf, 1024, unitOb, (flags & Py_PRINT_RAW) ? UNIT_PREC_STR : UNIT_PREC_REPR);
+	fputs (buf, fp);
+	
+	return 0;
+}
+
+//	tp_repr function - similar to the tp_print function.
+//	unitOb: The relevant Unit object
+//	Returns: A PyObject pointer to a Python string
+static PyObject* UnitOb_Repr (PyUnitObject *unitOb)
+{
+	char buf[1024];
+	
+	UnitOb_ToString (buf, 1024, unitOb, UNIT_PREC_REPR);
+	return PyString_FromString (buf);
+}
+
+//	tp_str function - similar to the tp_repr function.
+//	dimOb: The relevant dimensioned object
+//	Returns: A PyObject pointer to a Python string
+static PyObject* UnitOb_Str (PyUnitObject *unitOb)
+{
+	char buf[1024];
+	
+	UnitOb_ToString (buf, 1024, unitOb, UNIT_PREC_STR);
+	return PyString_FromString (buf);
+}
+
+//	Rich compare function - called to perform comparisons
+//	o1, o2: Pointers to unit objects
+//	Returns: New ref to result
+static PyObject* UnitOb_RichCompare (PyObject *o1, PyObject *o2, int opid)
+{
+	switch (opid)
+	{
+		case Py_LT:
+			Py_RETURN_FALSE;
+		case Py_LE:
+			Py_RETURN_FALSE;
+		case Py_EQ:
+			if (PyObject_Compare (((PyUnitObject*) o1)->units, ((PyUnitObject*) o2)->units) == 0)
+				Py_RETURN_TRUE;
+			else
+				Py_RETURN_FALSE;
+		case Py_NE:
+			if (PyObject_Compare (((PyUnitObject*) o1)->units, ((PyUnitObject*) o2)->units) == 0)
+				Py_RETURN_FALSE;
+			else
+				Py_RETURN_TRUE;
+		case Py_GT:
+			Py_RETURN_FALSE;
+		case Py_GE:
+			Py_RETURN_FALSE;
+		default:
+			Py_RETURN_FALSE;
+	}
+}
+
+//	Creation function, called to create a new unit object.
+//	type, args, kwds: See the Python API documentation for this type of function
+//	Returns: A PyObject pointer to the new unit object
+static PyObject* UnitOb_New (PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	PyObject *listOb = NULL;
+
+	// Unpack a list from the arguments
+	if (!PyArg_ParseTuple (args, "O", &listOb))
+		return NULL;
+
+	return UnitOb_FromList (listOb);
+}
+
+//	Destructor.
+//	self: A pointer to the unit object
+static void UnitOb_Dealloc (PyUnitObject *self)
+{
+	Py_DECREF (self->units);
+	
+	self->ob_type->tp_free ((PyObject*) self);
+}
+
+//	Hash function, used to use the object in dictionaries, etc.
+//	self: A pointer to the unit object
+///////////////////// FIX THIS ///////////////////////////////////////////////
+static long UnitOb_Hash (PyUnitObject *self)
+{
+	PyObject *longObj;
+	long x;
+
+	// Convert to a Python long and hash that - from object.c
+	if ((longObj = PyLong_FromLong (self->numUnits) ) == NULL)
+	{
+		x = -1;
+		goto finally;
+	}
+	x = PyObject_Hash (longObj);
+finally:
+	Py_XDECREF(longObj);
+	return x;
+}
+
+// Documentation string
+PyDoc_STRVAR (unitObDoc, "Unit object used to represent a unit expression.");
+
+// Type structure
+PyTypeObject PyUnit_Type =
+{
+	PyObject_HEAD_INIT (&PyType_Type)
+	0,								// ob_size 
+	"unit",							// tp_name
+	sizeof (PyUnitObject),			// tp_basicsize
+	0,								// tp_itemsize
+	(destructor) UnitOb_Dealloc,	// tp_dealloc
+	(printfunc) UnitOb_Print,		// tp_print
+    0,								// tp_getattr
+    0,								// tp_setattr
+    0,//(cmpfunc) UnitOb_Compare,	// tp_compare
+    (reprfunc) UnitOb_Repr,			// tp_repr
+    0,								// tp_as_number
+    0,								// tp_as_sequence
+    0,								// tp_as_mapping
+    (hashfunc) UnitOb_Hash,			// tp_hash
+    0,								// tp_call
+    (reprfunc) UnitOb_Str,			// tp_str
+    PyObject_GenericGetAttr,		// tp_getattro
+    0,								// tp_setattro
+    0,								// tp_as_buffer
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES,		// tp_flags
+	unitObDoc,						// tp_doc
+	0,								// tp_traverse
+	0,								// tp_clear
+	(richcmpfunc) UnitOb_RichCompare,	// tp_richcompare
+	0,								// tp_weaklistoffset
+	0,								// tp_iter
+	0,								// tp_iternext
+	0,								// tp_methods
+	0,								// tp_members
+	0,								// tp_getset
+	0,								// tp_base
+	0,								// tp_dict
+	0,								// tp_descr_get
+	0,								// tp_descr_set
+	0,								// tp_dictoffset
+	0,								// tp_init
+	0,								// tp_alloc
+	UnitOb_New,						// tp_new
+	PyObject_Del,           		// tp_free
+};
+
+#endif	// __RADAR_EXT__
Index: Objects/dimensionobject.c
===================================================================
--- Objects/dimensionobject.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Objects/dimensionobject.c	(.../radar)	(revision 263)
@@ -0,0 +1,1779 @@
+/************************************************************
+ * Distance object implementation							*
+ * Geoffrey Biggs 2004										*
+ * Borrows from the complex data type by Jim Hugunin and	*
+ * the float data type.										*
+ ************************************************************/
+ 
+#include "Python.h"
+#include "structmember.h"
+
+#include "../Python/statslogger.h"
+
+#include <stdarg.h>
+
+#ifdef __RADAR_EXT__
+
+//////////////////////////////////////////////////////////////////////////////
+//	Macros and function declarations										//
+//////////////////////////////////////////////////////////////////////////////
+
+// I have no idea what these are anymore
+#define DIM_PREC_REPR				15
+#define DIM_PREC_STR				10
+
+// Go on. Guess.
+#define DIM_PI						3.1415926535897931
+
+
+//////////////////////////////////////////////////////////////////////////////
+//	Unit list functions 													//
+//		Functions that help manage lists of units							//
+//////////////////////////////////////////////////////////////////////////////
+
+///	Create a unit list compatible with those used in the dimensioned data type,
+///	which is useful for converting dimensioned values between lists internally.
+///	Receives:	The number of units to the function (thus the actual number of
+///				arguments divided by 3)
+///				A variable number of args matching the pattern (int, char*, int)
+///	Returns:	A PyListObject containing the units
+PyObject* DimOb_CreateUnitList (int numArgs, ...)
+{
+	PyObject *result = NULL, *newTuple = NULL;
+	va_list ap;
+	int ii = 0, multiplier = 1, power = 1;
+	char *unitName = NULL;
+	
+	if ((result = PyList_New (0)) == NULL)
+		return NULL;
+	
+	va_start (ap, numArgs);
+	
+	for (ii = 0; ii != numArgs; ii++)
+	{
+		multiplier = va_arg (ap, int);
+		unitName = va_arg (ap, char*);
+		power = va_arg (ap, int);
+		
+		// If the multiplier or power is 0, skip this unit
+		if (multiplier == 0 || power == 0)
+			continue;
+		// If the unit can't be found, error
+		if (Dim_FindUnit (unitName) == NULL)
+		{
+			PyErr_SetString (PyExc_ValueError, "Could not find unit");
+			return NULL;
+		}
+		
+		// Make a tuple with the values and append it to the result list
+		if ((newTuple = PyTuple_New (3)) == NULL)
+		{	// Error
+			return NULL;
+		}
+		PyTuple_SetItem (newTuple, 0, PyInt_FromLong (multiplier));
+		PyTuple_SetItem (newTuple, 1, PyString_FromString (unitName));
+		PyTuple_SetItem (newTuple, 2, PyInt_FromLong (power));
+
+		if (PyList_Append (result, newTuple) < 0)
+		{
+			return NULL;
+		}
+	}
+
+	va_end (ap);
+	return result;
+}
+
+//	Formats a list of units into a string for printing out
+//	buf: A buffer to print into
+//	bufSize: The max size to print
+//	units: A PyListObject of tuples representing the units, as found in a dimesioned object
+//	Returns: nothing
+void DimOb_UnitsListToString (char *buf, int bufSize, PyObject *units)
+{
+	int i, offset = 0, mult, pow;
+	char first = 1;
+	PyObject *tuple;
+	
+	for (i = 0; i < PyList_Size (units); i++)
+	{
+		tuple = PyList_GetItem (units, i);
+		
+		mult = PyInt_AsLong (PyTuple_GetItem (tuple, 0));
+		pow = PyInt_AsLong (PyTuple_GetItem (tuple, 2));
+		if (!first)
+		{
+			// Print the multiplication/division symbol
+			if (pow < 0)
+				offset += PyOS_snprintf (&buf[offset], bufSize - offset, "/");
+			else
+				offset += PyOS_snprintf (&buf[offset], bufSize - offset, "*");
+		}
+		// Multiplier next
+		if (mult != 1)
+		{
+			if (mult == -1)
+				// Just print a - symbol
+				offset += PyOS_snprintf (&buf[offset], bufSize - offset, "-");
+			else
+				offset += PyOS_snprintf (&buf[offset], bufSize - offset, "%d", mult);
+		}
+		// Now the unit string
+		offset += PyOS_snprintf (&buf[offset], bufSize - offset, "%s", PyString_AsString (PyTuple_GetItem (tuple, 1)));
+		// Finally, the power - note that because the / should have been inserted, don't need the negative sign (unless first)
+		if (pow != 1)
+		{
+			if (first)
+				offset += PyOS_snprintf (&buf[offset], bufSize - offset, "^%d", pow);
+			else if (pow != -1)
+				offset += PyOS_snprintf (&buf[offset], bufSize - offset, "^%d", abs (pow));
+		}
+		
+		// Set first to off
+		first = 0;
+	}
+}
+
+//	Creates a units list from a string - NOTE: only handles single units at this stage
+//	units: A pointer to the string representing the units to use
+//	Returns: A reference to a Python List
+PyObject* DimOb_UnitsListFromString (char *units)
+{
+	PyObject *newList = NULL, *newTuple = NULL;
+	Unit *newUnit = NULL;
+	
+	// Check the wanted unit exists
+	if ((newUnit = Dim_FindUnit (units)) == NULL)
+	{
+		LogError ("Undefined unit: %s\n", units);
+		PyErr_Format (PyExc_ValueError, "Undefined unit: %s", units);
+		// Unit does not exist error
+		return NULL;
+	}
+	
+	if ((newTuple = PyTuple_New (3)) == NULL)
+		return NULL;
+	PyTuple_SetItem (newTuple, 0, PyInt_FromLong (1));
+	PyTuple_SetItem (newTuple, 1, PyString_FromString (units));
+	PyTuple_SetItem (newTuple, 2, PyInt_FromLong (1));
+
+	if ((newList = PyList_New (0)) == NULL)
+		return NULL;
+	PyList_Append (newList, newTuple);
+
+	return newList;
+}
+
+//	Condenses a unit list by combining multiple occurances of the same unit.
+//	units: A pointer to a PyListObject of PyTupleObject's
+//	Returns: A pointer to a new PyListObject containing the condensed list
+PyObject* DimOb_CondenseUnitsList (PyObject *units)
+{
+	PyObject *newList = NULL, *temp = NULL;
+	PyObject *currentUnit = NULL, *currentTuple = NULL, *newTuple = NULL;	// Yeah, my naming sucks
+	int i, j;
+	char exists = 0;
+	int mult1, mult2, pow1, pow2;
+
+	if ((newList = PyList_New (0)) == NULL)
+		return NULL;
+
+	// Go through the units list
+	for (i = 0; i < PyList_Size (units); i++)
+	{
+		currentUnit = PyList_GetItem (units, i);
+		// For each unit in the list, first check if it is already in the new list
+		for (j = 0; j < PyList_Size (newList); j++)
+		{
+			currentTuple = PyList_GetItem (newList, j);
+			if (strcmp (PyString_AsString (PyTuple_GetItem (currentUnit, 1)), PyString_AsString (PyTuple_GetItem (currentTuple, 1))) == 0)
+			{
+				exists = 1;
+				break;
+			}
+		}
+		// If it exists, then combine
+		if (exists == 1)	// Note that if exists == 1 then currentTuple can't be NULL
+		{
+			// First get the two multipliers and two powers
+			mult1 = PyInt_AsLong (PyTuple_GetItem (currentTuple, 0));
+			pow1 = PyInt_AsLong (PyTuple_GetItem (currentTuple, 2));
+			mult2 = PyInt_AsLong (PyTuple_GetItem (currentUnit, 0));
+			pow2 = PyInt_AsLong (PyTuple_GetItem (currentUnit, 2));
+			// Combine them
+			mult1 *= mult2;
+			pow1 += pow2;
+			// If either has become zero then this unit disappears (woo, fun!)
+			if (mult1 == 0 || pow1 == 0)
+			{
+				if (PyList_SetSlice (newList, j, j + 1, NULL) < 0)
+				{	// Error
+					return NULL;
+				}
+			}
+			else
+			{
+				// Otherwise replace the existing tuple
+				if ((newTuple = PyTuple_New (3)) == NULL)
+				{	// Error
+					return NULL;
+				}
+				temp = PyTuple_GetItem (currentTuple, 1);
+				Py_INCREF (temp);
+				PyTuple_SetItem (newTuple, 0, PyInt_FromLong (mult1));
+				PyTuple_SetItem (newTuple, 1, temp);
+				PyTuple_SetItem (newTuple, 2, PyInt_FromLong (pow1));
+				// Set it at the original's index in the new list
+				PyList_SetItem (newList, j, newTuple);
+			}
+		}
+		// Else append
+		else
+		{
+			Py_INCREF (currentUnit);
+			PyList_Append (newList, currentUnit);
+		}
+		
+		// On to the next one
+		currentTuple = NULL;
+		exists = 0;
+	}
+	
+	return newList;
+}
+
+//	Compares the dimensions used in a unit list to see if they are equal.
+//	units1, units2: A pointer to the PyListObject's containing the units
+//	Returns: 1 if equal, 0 if not
+int DimOb_EqualDimensions (PyObject *units1, PyObject *units2)
+{
+	char dimUseCounts1[256], dimUseCounts2[256];
+	int i;
+	Unit *currentUnit = NULL;
+
+	memset (dimUseCounts1, 0, 256);
+	memset (dimUseCounts2, 0, 256);
+
+//	LogError ("Dimensionality check\n");
+
+	// For each unit, find its dimension and increment that count by the power of that unit
+	for (i = 0; i < PyList_Size (units1); i++)
+	{
+		currentUnit = Dim_FindUnit (PyString_AsString (PyTuple_GetItem (PyList_GetItem (units1, i), 1)));
+		dimUseCounts1[currentUnit->dimension->index] += PyInt_AsLong (PyTuple_GetItem (PyList_GetItem (units1, i), 2));
+	}
+	for (i = 0; i < PyList_Size (units2); i++)
+	{
+		currentUnit = Dim_FindUnit (PyString_AsString (PyTuple_GetItem (PyList_GetItem (units2, i), 1)));
+		dimUseCounts2[currentUnit->dimension->index] += PyInt_AsLong (PyTuple_GetItem (PyList_GetItem (units2, i), 2));
+	}
+
+	// Now check the counts are equal
+	for (i = 0; i < 256; i++)
+		if (dimUseCounts1[i] != dimUseCounts2[i])
+			return 0;
+
+	return 1;
+}
+
+//	Compares the units in a unit list to see if they are equal.
+//	units1, units2: Pointers to the PyListObject's containing the units
+//	Returns: 1 if equal, 0 if not
+int DimOb_EqualUnits (PyObject *units1, PyObject *units2)
+{
+//	LogError ("Unit equality check\n");
+	
+	if (PyObject_Compare (units1, units2) != 0)
+		return 0;
+	return 1;
+}
+
+//	Calculates the total ratio for a unit string to the base units in each individual unit's dimension.
+//	eg, for km/hr, the ratio will be 1000/3600.
+//	units: A pointer to the PyListObject containing the units
+//	Returns: The combined ratio
+double DimOb_CalculateRatio (PyObject *units)
+{
+	Unit *currentUnit = NULL;
+	int i, power = 0;
+	double ratio = 1.0f;
+
+	for (i = 0; i < PyList_Size (units); i++)
+	{
+		power = PyInt_AsLong (PyTuple_GetItem (PyList_GetItem (units, i), 2));
+		currentUnit = Dim_FindUnit (PyString_AsString (PyTuple_GetItem (PyList_GetItem (units, i), 1)));
+		if (power < 0)
+			ratio /= currentUnit->extra;
+		else
+			ratio *= currentUnit->extra;
+	}
+	
+	return ratio;
+}
+
+//	Checks the precision of a units list
+//	If a single floating precision unit is included, it's floating. Else, fixed
+//	units: The list of units
+//	Returns: A DIM_UNITPRECISION value
+DIM_UNITPRECISION DimOb_GetUnitsListPrecision (PyObject *units)
+{
+	Unit *currentUnit = NULL;
+	int i;
+	
+	for (i = 0; i < PyList_Size (units); i++)
+	{
+		currentUnit = Dim_FindUnit (PyString_AsString (PyTuple_GET_ITEM (PyList_GET_ITEM (units, i), 1)));
+		if (currentUnit->precision == DIM_UP_FLOATING)
+			return DIM_UP_FLOATING;
+	}
+	return DIM_UP_FIXED;
+}
+
+//	Multiplies two unit lists together.
+//	units1, units2: Pointers to the PyListObject's containing the units
+//	Returns: A new PyListObject containing the resulting units
+PyObject* DimOb_MultiplyUnits (PyObject *units1, PyObject *units2)
+{
+	PyObject *newList = NULL, *temp = NULL;
+	int i;
+	
+//	LogError ("Unit multiplication\n");
+	
+	newList = PyList_New (0);
+	for (i = 0; i < PyList_Size (units1); i++)
+	{
+		temp = PyList_GetItem (units1, i);
+		Py_INCREF (temp);
+		PyList_Append (newList, temp);
+	}
+	for (i = 0; i < PyList_Size (units2); i++)
+	{
+		temp = PyList_GetItem (units2, i);
+		Py_INCREF (temp);
+		PyList_Append (newList, temp);
+	}
+	return DimOb_CondenseUnitsList(newList);
+}
+
+// 	Divides two unit lists.
+//	units1, units2: Pointers to the PyListObject's containing the units
+//	Returns: A new PyListObject containing the resulting units
+PyObject* DimOb_DivideUnits (PyObject *units1, PyObject *units2)
+{
+	PyObject *currentTuple = NULL, *newTuple = NULL, *newList = NULL, *result = NULL, *temp = NULL;
+	int i, newPower;
+	
+//	LogError ("Unit division\n");
+	
+	// First, negate the powers of the 2nd list, creating a new list with these negated powers
+	if ((newList = PyList_New (0)) == NULL)
+		return NULL;
+	for (i = 0; i < PyList_Size (units2); i++)
+	{
+		currentTuple = PyList_GetItem (units2, i);
+		Py_INCREF (currentTuple);
+		newPower = PyInt_AsLong (PyTuple_GetItem (currentTuple, 2)) * -1;
+		if ((newTuple = PyTuple_New (3)) == NULL)
+			return NULL;
+		temp = PyTuple_GetItem (currentTuple, 0);
+		Py_INCREF (temp);
+		PyTuple_SetItem (newTuple, 0, temp);
+		temp = PyTuple_GetItem (currentTuple, 1);
+		Py_INCREF (temp);
+		PyTuple_SetItem (newTuple, 1, temp);
+		PyTuple_SetItem (newTuple, 2, PyInt_FromLong (newPower));
+		PyList_Append (newList, newTuple);
+		Py_DECREF (currentTuple);
+	}
+	
+	// Now just multiply the two lists
+	result = DimOb_MultiplyUnits (units1, newList);
+	// Dec ref on the temp list
+	Py_DECREF (newList);
+	return result;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//	Helper functions 														//
+//		Random functions that do random things								//
+//////////////////////////////////////////////////////////////////////////////
+
+//	Wrap a floating point number based on a range.
+//	original: The value to wrap
+//	min: Minimum of the range to wrap into
+//	max: Maximum of the range to wrap into
+//	Returns: The wrapped value
+double DimOb_WrapFloating (double original, double min, double max)
+{
+	while (original < min)
+		original += (max - min);
+	while (original >= max)
+		original -= (max - min);		
+	return original;
+}
+
+//	Wrap a fixed point number based on a range.
+//	original: The value to wrap
+//	min: Minimum of the range to wrap into
+//	max: Maximum of the range to wrap into
+//	Returns: The wrapped value
+long DimOb_WrapFixed (long original, long min, long max)
+{
+	while (original < min)
+		original += (max - min);
+	while (original >= max)
+		original -= (max - min);		
+	return original;
+}
+
+
+//	Formats the dimensioned object into a nice human-readable string.
+//	buf: A pointer to the buffer to store the string in
+//	bufSize: Maximum size of the string to create
+//	dimOb: Pointer to the dimensioned object to stringify
+//	precision: Precision to use when printing floating point numbers
+//	Returns: nothing
+static void DimOb_ToString (char *buf, int bufSize, PyDimensionedObject *dimOb, int precision)
+{
+	int offset = 0;
+	
+	if (DIM_ISFLOATING (dimOb))
+	{
+		offset = PyOS_snprintf (buf, bufSize, "%.*g~", precision, dimOb->floatingMeasure);
+		DimOb_UnitsListToString (&buf[offset], bufSize - offset, dimOb->units);
+	}
+	else if (DIM_ISFIXED (dimOb))
+	{
+		offset = PyOS_snprintf (buf, bufSize, "%ld~", dimOb->fixedMeasure);
+		DimOb_UnitsListToString (&buf[offset], bufSize - offset, dimOb->units);
+	}
+	else
+		PyOS_snprintf (buf, bufSize, "Eeee? If it's not floating and not fixed, what is it?");
+}
+
+//	Create a new dimensioned object from raw data.
+//	units: The units to use for this dimensioned value
+//	floatingMeasure: The floating point value of the dimensioned data
+//	fixedMeasure: The fixed point value of the dimensioned data
+//	Returns: A pointer to a new PyObject
+PyObject* DimOb_FromRaw (char *units, double floatingMeasure, long fixedMeasure)
+{
+	register PyDimensionedObject *dimOb;
+	Unit *newUnit = NULL;
+	UnitRange *rangeLimits = NULL;
+	PyObject *unitsList = NULL;
+
+	if ((unitsList = DimOb_UnitsListFromString (units)) == NULL)
+		return NULL;		// Error should already be set
+
+	// Check the wanted unit exists
+	if ((newUnit = Dim_FindUnit (units)) == NULL)
+	{
+		LogError ("Undefined unit: %s\n", units);
+		PyErr_Format (PyExc_ValueError, "Undefined unit: %s", units);
+		// Unit does not exist error
+		return NULL;
+	}
+
+	// Inline PyObject_New
+	// Create a new PyDistanceObject instance
+	dimOb = (PyDimensionedObject *) PyObject_MALLOC (sizeof (PyDimensionedObject));
+	if (dimOb == NULL)
+		return PyErr_NoMemory ();
+	PyObject_INIT (dimOb, &PyDimensioned_Type);
+
+	// Copy in the values
+	dimOb->fixedMeasure = fixedMeasure;
+	dimOb->floatingMeasure = (floatingMeasure == 0 ? fixedMeasure : floatingMeasure);
+	dimOb->precision = newUnit->precision;//(newUnit == NULL) ? newDrvUnit->precision : newUnit->precision;
+	dimOb->units = unitsList;
+
+	rangeLimits = newUnit->rangeLimits;//(newUnit == NULL) ? newDrvUnit->rangeLimits : newUnit->rangeLimits;
+	if (rangeLimits != NULL)
+	{
+		if (DIM_ISFLOATING (dimOb))
+		{
+			// Use the floating range limits
+			if ((rangeLimits->minType == DIM_RT_INCLUDE && dimOb->floatingMeasure < rangeLimits->flRangeMin) ||
+				(rangeLimits->minType == DIM_RT_EXCLUDE && dimOb->floatingMeasure <= rangeLimits->flRangeMin) ||
+				(rangeLimits->maxType == DIM_RT_INCLUDE && dimOb->floatingMeasure > rangeLimits->flRangeMax) ||
+				(rangeLimits->maxType == DIM_RT_EXCLUDE && dimOb->floatingMeasure >= rangeLimits->flRangeMax))
+			{
+				// Range exception
+				LogError ("Range limit for unit exceeded\n");
+				PyErr_SetString (PyExc_OverflowError, "Range limit for unit exceeded");
+				return NULL;
+			}
+			else if (rangeLimits->minType == DIM_RT_WRAP && 
+				(dimOb->floatingMeasure < rangeLimits->flRangeMin || dimOb->floatingMeasure >= rangeLimits->flRangeMax))
+			{
+				dimOb->floatingMeasure = DimOb_WrapFloating (dimOb->floatingMeasure, rangeLimits->flRangeMin, rangeLimits->flRangeMax);
+			}
+		}
+		else
+		{
+			// Use the fixed range limits
+			if ((rangeLimits->minType == DIM_RT_INCLUDE && dimOb->fixedMeasure < rangeLimits->fxRangeMin) ||
+				(rangeLimits->minType == DIM_RT_EXCLUDE && dimOb->fixedMeasure <= rangeLimits->fxRangeMin) ||
+				(rangeLimits->maxType == DIM_RT_INCLUDE && dimOb->fixedMeasure > rangeLimits->fxRangeMax) ||
+				(rangeLimits->maxType == DIM_RT_EXCLUDE && dimOb->fixedMeasure >= rangeLimits->fxRangeMax))
+			{
+				// Range exception
+				LogError ("Range limit for unit exceeded\n");
+				PyErr_SetString (PyExc_OverflowError, "Range limit for unit exceeded");
+				return NULL;
+			}
+			else if (newUnit->rangeLimits->minType == DIM_RT_WRAP && 
+				(dimOb->fixedMeasure < rangeLimits->fxRangeMin || dimOb->fixedMeasure >= rangeLimits->fxRangeMax))
+			{
+				dimOb->fixedMeasure = DimOb_WrapFixed (dimOb->fixedMeasure, rangeLimits->fxRangeMin, rangeLimits->fxRangeMax);
+			}
+		}
+	}
+	
+	return (PyObject*) dimOb;
+}
+
+//	Create a dimensioned object from a unit list and some data.
+//	The unit list is a PyListObject for PyTupleObject's. Each tuple contains 3 members:
+//	an int, a string and an int.
+//	The units list is a *borrowed reference!* This object does not take over 
+//	a reference, it makes its own as needed!
+PyAPI_FUNC(PyObject*) DimOb_FromList (PyObject *units, double floatingMeasure, long fixedMeasure)
+{
+	register PyDimensionedObject *dimOb;
+	PyObject *condensedUnits = NULL;
+	Unit *newUnit = NULL;
+	UnitRange *rangeLimits = NULL;
+	int i, floatingCount = 0;
+
+	// Condense the unit list if there's more than one
+	if (PyList_Size (units) > 1)
+		condensedUnits = DimOb_CondenseUnitsList (units);
+	else
+	{
+		Py_INCREF (units);
+		condensedUnits = units;
+	}
+
+	// Check each unit in the list exists, and look for floating point ones
+	for (i = 0; i < PyList_Size (condensedUnits); i++)
+	{
+		if ((newUnit = Dim_FindUnit (PyString_AsString (PyTuple_GET_ITEM (PyList_GET_ITEM (condensedUnits, i), 1)))) == NULL)
+		{
+			LogError ("Undefined unit: %s\n", PyString_AsString (PyTuple_GET_ITEM (PyList_GET_ITEM (condensedUnits, i), 1)));
+			PyErr_SetString (PyExc_ValueError, "Undefined unit");
+			// Unit does not exist error
+			return NULL;
+		}
+		if (newUnit->precision == DIM_UP_FLOATING)
+			floatingCount++;
+	}
+
+	// Inline PyObject_New
+	// Create a new PyDistanceObject instance
+	dimOb = (PyDimensionedObject *) PyObject_MALLOC (sizeof (PyDimensionedObject));
+	if (dimOb == NULL)
+		return PyErr_NoMemory ();
+	PyObject_INIT (dimOb, &PyDimensioned_Type);
+
+	// Copy in the values
+	dimOb->fixedMeasure = fixedMeasure;
+	dimOb->floatingMeasure = (floatingMeasure == 0 ? fixedMeasure : floatingMeasure);
+	dimOb->precision = (floatingCount == 0) ? DIM_UP_FIXED : DIM_UP_FLOATING;
+	dimOb->units = condensedUnits;	// Ownership of list object should already have been established by this point
+
+	
+	rangeLimits = newUnit->rangeLimits;
+	// Only apply range limits if a single unit is being used - no range limits on derived units
+	if (rangeLimits != NULL && PyList_Size (units) == 1)
+	{
+		if (DIM_ISFLOATING (dimOb))
+		{
+			// Use the floating range limits
+			if ((rangeLimits->minType == DIM_RT_INCLUDE && dimOb->floatingMeasure < rangeLimits->flRangeMin) ||
+				(rangeLimits->minType == DIM_RT_EXCLUDE && dimOb->floatingMeasure <= rangeLimits->flRangeMin) ||
+				(rangeLimits->maxType == DIM_RT_INCLUDE && dimOb->floatingMeasure > rangeLimits->flRangeMax) ||
+				(rangeLimits->maxType == DIM_RT_EXCLUDE && dimOb->floatingMeasure >= rangeLimits->flRangeMax))
+			{
+				// Range exception
+				LogError ("Range limit for unit exceeded\n");
+				PyErr_SetString (PyExc_OverflowError, "Range limit for unit exceeded");
+				return NULL;
+			}
+			else if (rangeLimits->minType == DIM_RT_WRAP && 
+				(dimOb->floatingMeasure < rangeLimits->flRangeMin || dimOb->floatingMeasure >= rangeLimits->flRangeMax))
+			{
+				dimOb->floatingMeasure = DimOb_WrapFloating (dimOb->floatingMeasure, rangeLimits->flRangeMin, rangeLimits->flRangeMax);
+			}
+		}
+		else
+		{
+			// Use the fixed range limits
+			if ((rangeLimits->minType == DIM_RT_INCLUDE && dimOb->fixedMeasure < rangeLimits->fxRangeMin) ||
+				(rangeLimits->minType == DIM_RT_EXCLUDE && dimOb->fixedMeasure <= rangeLimits->fxRangeMin) ||
+				(rangeLimits->maxType == DIM_RT_INCLUDE && dimOb->fixedMeasure > rangeLimits->fxRangeMax) ||
+				(rangeLimits->maxType == DIM_RT_EXCLUDE && dimOb->fixedMeasure >= rangeLimits->fxRangeMax))
+			{
+				// Range exception
+				LogError ("Range limit for unit exceeded\n");
+				PyErr_SetString (PyExc_OverflowError, "Range limit for unit exceeded");
+				return NULL;
+			}
+			else if (newUnit->rangeLimits->minType == DIM_RT_WRAP && 
+				(dimOb->fixedMeasure < rangeLimits->fxRangeMin || dimOb->fixedMeasure >= rangeLimits->fxRangeMax))
+			{
+				dimOb->fixedMeasure = DimOb_WrapFixed (dimOb->fixedMeasure, rangeLimits->fxRangeMin, rangeLimits->fxRangeMax);
+			}
+		}
+	}
+	
+	return (PyObject *) dimOb;
+}
+
+//	Convert from one unit to another. Note that can only convert within dimensions.
+//	dimOb: Pointer to the dimensioned object to convert
+//	newUnits: Pointer to a PyListObject containing the units to convert to
+//	Returns: PyObject pointer pointing to the new dimensioned object created
+PyObject* DimOb_ConvertUnits (PyDimensionedObject *dimOb, PyObject *newUnits)
+{
+	double floatingResult = 0.0f, ratio1 = 0.0f, ratio2 = 0.0f;
+	long fixedResult = 0;
+	DIM_UNITPRECISION precision = DIM_UP_FLOATING;
+	
+//	LogError ("Unit conversion\n");
+	
+	// First, check unit compatibility - can only convert if the dimensions are equal (eg km*deg/hour -> m*rad/s)
+	if (!DimOb_EqualDimensions (dimOb->units, newUnits))
+	{
+		// Conversion error
+		LogError ("Incompatible units - cannot convert between mismatched dimensions\n");
+		PyErr_SetString (PyExc_ValueError, "Incompatible units - cannot convert between mismatched dimensions");
+		return NULL;
+	}
+	
+	// In all cases we need to multiply the value first by the old unit's ratio, then divide by the new ratio
+	// The choice is in which variable to store the result in
+	
+	// So, get the ratios
+	ratio1 = DimOb_CalculateRatio (dimOb->units);
+	ratio2 = DimOb_CalculateRatio (newUnits);
+	
+	// Check the precision of newUnits
+	precision = DimOb_GetUnitsListPrecision (newUnits);
+	
+	// Floating -> floating
+	if (DIM_ISFLOATING (dimOb) && precision == DIM_UP_FLOATING)
+	{
+		floatingResult = (dimOb->floatingMeasure * ratio1) / ratio2;
+	}
+	// Floating -> fixed
+	else if (DIM_ISFLOATING (dimOb) && precision == DIM_UP_FIXED)
+	{
+		fixedResult = (long) ((dimOb->floatingMeasure * ratio1) / ratio2);
+	}
+	// Fixed -> floating
+	else if (DIM_ISFIXED (dimOb) && precision == DIM_UP_FLOATING)
+	{
+		floatingResult = (dimOb->fixedMeasure * ratio1) / ratio2;
+	}
+	// Fixed -> fixed
+	else if (DIM_ISFIXED (dimOb) && precision == DIM_UP_FIXED)
+	{
+		fixedResult = (long) ((dimOb->fixedMeasure * ratio1) / ratio2);
+	}
+	else
+	{
+		// Shouldn't get here...
+		LogError ("Unknown error in unit conversion\n");
+		PyErr_SetString (PyExc_Exception, "Unknown error in unit conversion");
+		return NULL;
+	}
+	
+	return DimOb_FromList (newUnits, floatingResult, fixedResult);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//	Dimensioned object functions												//
+//		Functions that implement the dimensioned object itself				//
+//////////////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////////
+//	Number protocol functions												//
+//		Functions to implement the number protocol, eg add, subtract, etc	//
+//////////////////////////////////////////////////////////////////////////////
+
+//	nb_add function - implements addition.
+//	dimOb1, dimOb2: The two dimensioned objects to add
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Add (PyDimensionedObject *dimOb1, PyDimensionedObject *dimOb2)
+{
+	PyDimensionedObject *temp;
+	double floatingResult = 0.0f;
+	long fixedResult = 0;
+
+	if (!PyDimensioned_Check (dimOb1) || !PyDimensioned_Check (dimOb2))
+	{
+		// Type error
+		LogError ("Incompatible types in addition\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible types in addition");
+		return NULL;
+	}
+	
+	// Check the units are equal, and if they arn't attempt to convert
+	if (!DimOb_EqualUnits (dimOb1->units, dimOb2->units))
+	{
+//		LogError ("Converting units:\taddition\n");
+		// Convert dimOb2 to dimOb1's units - remember temp is a new ref and so needs to be decref'd when done with
+		if ((temp = (PyDimensionedObject*) DimOb_ConvertUnits (dimOb2, dimOb1->units)) == NULL)
+		{
+			// Unit conversion error - error already set
+			return NULL;
+		}
+	}
+	else
+	{
+		temp = dimOb2;
+		Py_INCREF (temp);
+	}
+	
+	if (DIM_ISFLOATING (dimOb1))
+	{
+		PyFPE_START_PROTECT("DimOb_Add", return 0)
+		floatingResult = dimOb1->floatingMeasure + temp->floatingMeasure;
+		PyFPE_END_PROTECT(result)
+	}
+	else if (DIM_ISFIXED (dimOb1))
+	{
+		PyFPE_START_PROTECT("DimOb_Add", return 0)
+		fixedResult = dimOb1->fixedMeasure + temp->fixedMeasure;
+		PyFPE_END_PROTECT(result)
+	}
+	else
+	{
+		LogError ("Unknown unit precision\n");
+		PyErr_SetString (PyExc_ValueError, "Unknown unit precision");
+		return NULL;
+	}
+
+	Py_DECREF (temp);
+	return DimOb_FromList (dimOb1->units, floatingResult, fixedResult);	
+}
+
+//	nb_subtract function - implements subtraction.
+//	dimOb1, dimOb2: The two dimensioned objects to subtract
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Sub (PyDimensionedObject *dimOb1, PyDimensionedObject *dimOb2)
+{	
+	PyDimensionedObject *temp;
+	double floatingResult = 0.0f;
+	long fixedResult = 0;
+
+	if (!PyDimensioned_Check (dimOb1) || !PyDimensioned_Check (dimOb2))
+	{
+		// Type error
+		LogError ("Incompatible types in subtraction\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible types in subtraction");
+		return NULL;
+	}
+	
+	// Check the units are equal, and if they arn't attempt to convert
+	if (!DimOb_EqualUnits (dimOb1->units, dimOb2->units))
+	{
+//		LogError ("Converting units:\tsubtraction\n");
+		// Convert dimOb2 to dimOb1's units
+		if ((temp = (PyDimensionedObject*) DimOb_ConvertUnits (dimOb2, dimOb1->units)) == NULL)
+		{
+			// Unit conversion error - error already set
+			return NULL;
+		}
+	}
+	else
+	{
+		temp = dimOb2;
+		Py_INCREF (temp);
+	}
+	
+	if (DIM_ISFLOATING (dimOb1))
+	{
+		PyFPE_START_PROTECT("DimOb_Sub", return 0)
+		floatingResult = dimOb1->floatingMeasure - temp->floatingMeasure;
+		PyFPE_END_PROTECT(result)
+	}
+	else if (DIM_ISFIXED (dimOb1))
+	{
+		PyFPE_START_PROTECT("DimOb_Sub", return 0)
+		fixedResult = dimOb1->fixedMeasure - temp->fixedMeasure;
+		PyFPE_END_PROTECT(result)
+	}
+	else
+	{
+		PyErr_SetString (PyExc_ValueError, "Unknown unit precision");
+		return NULL;
+	}
+	
+	Py_DECREF (temp);
+	return DimOb_FromList (dimOb1->units, floatingResult, fixedResult);
+}
+
+//	nb_multiply function - implements multiplication
+//	ob1, ob2: One or both of these must be a PyDimensionedObject. The one that isn't must be a number.
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Mul (PyObject *ob1, PyObject *ob2)
+{
+	PyObject *newUnits = NULL, *result = NULL;
+	PyDimensionedObject *dimOb1 = NULL, *dimOb2 = NULL;
+	double floatingResult = 0.0f;
+	long fixedResult = 0;
+	int fixedFlag = 0;
+	
+	// Run through the possible combinations of objects
+	if (PyDimensioned_Check (ob1) && PyDimensioned_Check (ob2))
+	{	
+		// dimensioned * dimensioned
+		dimOb1 = (PyDimensionedObject*) ob1;
+		dimOb2 = (PyDimensionedObject*) ob2;
+		
+		// Multiply the units - don't forget that newUnits is an owned reference and must be decref'd
+		if ((newUnits = DimOb_MultiplyUnits (dimOb1->units, dimOb2->units)) == NULL)
+			return NULL;
+		
+		// Perform the multiplication
+		if (DIM_ISFLOATING (dimOb1))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb1->floatingMeasure * (DIM_ISFLOATING (dimOb2) ? dimOb2->floatingMeasure : dimOb2->fixedMeasure);
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb1->fixedMeasure * (DIM_ISFLOATING (dimOb2) ? dimOb2->floatingMeasure : dimOb2->fixedMeasure);
+			PyFPE_END_PROTECT(result)
+		}
+
+		if (PyList_Size (newUnits) == 0)
+		{
+			Py_DECREF (newUnits);
+			// If the units are all gone, then we have created a scalar
+			if (DIM_ISFLOATING (dimOb1))
+				return PyFloat_FromDouble (floatingResult);
+			else
+				return PyInt_FromLong (fixedResult);
+		}
+
+		result = DimOb_FromList (newUnits, floatingResult, fixedResult);
+		Py_DECREF (newUnits);
+		return result;
+	}
+	else if (PyDimensioned_Check (ob1) && PyNumber_Check (ob2))
+	{	
+		// dimensioned * scalar
+		dimOb1 = (PyDimensionedObject*) ob1;
+
+		if (PyInt_Check (ob2))
+			fixedFlag = 1;
+		else if (!PyFloat_Check (ob2))
+		{
+			PyErr_SetString (PyExc_TypeError, "Scalar operand to multiplication must be integer or float");
+			return NULL;
+		}
+
+		// Perform the multiplication
+		if (DIM_ISFLOATING (dimOb1))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb1->floatingMeasure * (fixedFlag ? PyInt_AsLong (ob2) : PyFloat_AS_DOUBLE (ob2));
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb1->fixedMeasure * (fixedFlag ? PyInt_AsLong (ob2) : PyFloat_AS_DOUBLE (ob2));
+			PyFPE_END_PROTECT(result)
+		}
+
+		return DimOb_FromList (dimOb1->units, floatingResult, fixedResult);
+	}
+	else if (PyNumber_Check (ob1) && PyDimensioned_Check (ob2))
+	{	
+		// scalar * dimensioned
+		dimOb2 = (PyDimensionedObject*) ob2;
+
+		if (PyInt_Check (ob1))
+			fixedFlag = 1;
+		else if (!PyFloat_Check (ob1))
+		{
+			PyErr_SetString (PyExc_TypeError, "Scalar operand to multiplication must be integer or float");
+			return NULL;
+		}
+
+		// Perform the multiplication
+		if (DIM_ISFLOATING (dimOb2))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb2->floatingMeasure * (fixedFlag ? PyInt_AsLong (ob1) : PyFloat_AS_DOUBLE (ob1));
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb2->fixedMeasure * (fixedFlag ? PyInt_AsLong (ob1) : PyFloat_AS_DOUBLE (ob1));
+			PyFPE_END_PROTECT(result)
+		}
+
+		return DimOb_FromList (dimOb2->units, floatingResult, fixedResult);
+	}
+	else
+	{
+		// Type error
+		LogError ("Incompatible operand types in dimensioned multiplication\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible operand types in dimensioned multiplication");
+		return NULL;
+	}
+}
+
+//	nb_divide funcion - implements common division.
+//	dimOb: The dimensioned object to divide
+//	rhs: A Python float or integer if scaling, a dimensioned object if getting a ratio
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Div (PyObject *ob1, PyObject *ob2)
+{
+	PyObject *newUnits = NULL, *result = NULL;
+	PyDimensionedObject *dimOb1 = NULL, *dimOb2 = NULL, *temp = NULL;
+	double floatingResult = 0.0f;
+	long fixedResult = 0;
+	int fixedFlag = 0;
+	
+	// Run through the possible combinations of objects
+	if (PyDimensioned_Check (ob1) && PyDimensioned_Check (ob2))
+	{	
+		// dimensioned / dimensioned
+		dimOb1 = (PyDimensionedObject*) ob1;
+		dimOb2 = (PyDimensionedObject*) ob2;
+		
+		// Check the units are equal, and if they arn't attempt to convert
+		if (DimOb_EqualDimensions (dimOb1->units, dimOb2->units))
+		{
+			// Convert dimOb2 to dimOb1's units - remember temp is a new ref and so needs to be decref'd when done with
+			if ((temp = (PyDimensionedObject*) DimOb_ConvertUnits (dimOb2, dimOb1->units)) == NULL)
+			{
+				// Unit conversion error - error already set
+				return NULL;
+			}
+		}
+		else
+		{
+			temp = dimOb2;
+			Py_INCREF (temp);
+		}
+		
+		// Divide the units
+		if ((newUnits = DimOb_DivideUnits (dimOb1->units, temp->units)) == NULL)
+			return NULL;
+		
+		// Perform the division
+		if (DIM_ISFLOATING (dimOb1))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb1->floatingMeasure / (DIM_ISFLOATING (temp) ? temp->floatingMeasure : temp->fixedMeasure);
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb1->fixedMeasure / (DIM_ISFLOATING (temp) ? temp->floatingMeasure : temp->fixedMeasure);
+			PyFPE_END_PROTECT(result)
+		}
+		
+		if (PyList_Size (newUnits) == 0)
+		{
+			// If the units are all gone, then we have created a scalar
+			if (DIM_ISFLOATING (dimOb1))
+				return PyFloat_FromDouble (floatingResult);
+			else
+				return PyInt_FromLong (fixedResult);
+		}
+
+		result = DimOb_FromList (newUnits, floatingResult, fixedResult);
+		Py_DECREF (newUnits);
+		Py_DECREF (temp);
+		return result;
+	}
+	else if (PyDimensioned_Check (ob1) && PyNumber_Check (ob2))
+	{	
+		// dimensioned / scalar
+		dimOb1 = (PyDimensionedObject*) ob1;
+
+		if (PyInt_Check (ob2))
+			fixedFlag = 1;
+		else if (!PyFloat_Check (ob2))
+		{
+			PyErr_SetString (PyExc_TypeError, "Scalar operand to division must be integer or float");
+			return NULL;
+		}
+
+		// Perform the division
+		if (DIM_ISFLOATING (dimOb1))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb1->floatingMeasure / (fixedFlag ? PyInt_AsLong (ob2) : PyFloat_AS_DOUBLE (ob2));
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb1->fixedMeasure / (fixedFlag ? PyInt_AsLong (ob2) : PyFloat_AS_DOUBLE (ob2));
+			PyFPE_END_PROTECT(result)
+		}
+
+		return DimOb_FromList (dimOb1->units, floatingResult, fixedResult);
+	}
+	else if (PyNumber_Check (ob1) && PyDimensioned_Check (ob2))
+	{	
+		// scalar / dimensioned - UNDEFINED
+		// Type error
+		LogError ("Incompatible operand types in dimensioned division\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible operand types in dimensioned division");
+		return NULL;
+	}
+	else
+	{
+		// Type error
+		LogError ("Incompatible operand types in dimensioned division\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible operand types in dimensioned division");
+		return NULL;
+	}
+}
+
+//	nb_floor_divide function - implements integer division
+//	dimOb: The dimensioned object to divide
+//	rhs: A Python float or integer if scaling, a dimensioned object if getting a ratio
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_FloorDiv (PyObject *ob1, PyObject *ob2)
+{
+	PyObject *newUnits = NULL, *result = NULL;
+	PyDimensionedObject *dimOb1 = NULL, *dimOb2 = NULL, *temp = NULL;
+	double floatingResult = 0.0f;
+	long fixedResult = 0;
+	int fixedFlag = 0;
+	
+	// Run through the possible combinations of objects
+	if (PyDimensioned_Check (ob1) && PyDimensioned_Check (ob2))
+	{	
+		// dimensioned / dimensioned
+		dimOb1 = (PyDimensionedObject*) ob1;
+		dimOb2 = (PyDimensionedObject*) ob2;
+		
+		// Check the units are equal, and if they arn't attempt to convert
+		if (DimOb_EqualDimensions (dimOb1->units, dimOb2->units))
+		{
+			// Convert dimOb2 to dimOb1's units - remember temp is a new ref and so needs to be decref'd when done with
+			if ((temp = (PyDimensionedObject*) DimOb_ConvertUnits (dimOb2, dimOb1->units)) == NULL)
+			{
+				// Unit conversion error - error already set
+				return NULL;
+			}
+		}
+		else
+		{
+			temp = dimOb2;
+			Py_INCREF (temp);
+		}
+		
+		// Divide the units
+		if ((newUnits = DimOb_DivideUnits (dimOb1->units, temp->units)) == NULL)
+			return NULL;
+		
+		// Perform the division
+		if (DIM_ISFLOATING (dimOb1))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb1->floatingMeasure / (DIM_ISFLOATING (temp) ? temp->floatingMeasure : temp->fixedMeasure);
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb1->fixedMeasure / (DIM_ISFLOATING (temp) ? temp->floatingMeasure : temp->fixedMeasure);
+			PyFPE_END_PROTECT(result)
+		}
+		
+		if (PyList_Size (newUnits) == 0)
+		{
+			// If the units are all gone, then we have created a scalar
+			if (DIM_ISFLOATING (dimOb1))
+				return PyFloat_FromDouble (floatingResult);
+			else
+				return PyInt_FromLong (fixedResult);
+		}
+
+		result = DimOb_FromList (newUnits, floor (floatingResult), floor (fixedResult));
+		Py_DECREF (newUnits);
+		Py_DECREF (temp);
+		return result;
+	}
+	else if (PyDimensioned_Check (ob1) && PyNumber_Check (ob2))
+	{	
+		// dimensioned * scalar
+		dimOb1 = (PyDimensionedObject*) ob1;
+
+		if (PyInt_Check (ob2))
+			fixedFlag = 1;
+		else if (!PyFloat_Check (ob2))
+		{
+			PyErr_SetString (PyExc_TypeError, "Scalar operand to division must be integer or float");
+			return NULL;
+		}
+
+		// Perform the multiplication
+		if (DIM_ISFLOATING (dimOb1))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = dimOb1->floatingMeasure / (fixedFlag ? PyInt_AsLong (ob2) : PyFloat_AS_DOUBLE (ob2));
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = dimOb1->fixedMeasure / (fixedFlag ? PyInt_AsLong (ob2) : PyFloat_AS_DOUBLE (ob2));
+			PyFPE_END_PROTECT(result)
+		}
+
+		return DimOb_FromList (dimOb1->units, floor (floatingResult), floor (fixedResult));
+	}
+	else if (PyNumber_Check (ob1) && PyDimensioned_Check (ob2))
+	{	
+		// scalar * dimensioned - UNDEFINED?
+		// Type error
+		LogError ("Incompatible operand types in dimensioned division\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible operand types in dimensioned division");
+		return NULL;
+/*		dimOb2 = (PyDimensionedObject*) ob2;
+
+		if (PyInt_Check (ob1))
+			fixedFlag = 1;
+		else if (!PyFloat_Check (ob1))
+		{
+			PyErr_SetString (PyExc_TypeError, "Scalar operand to multiplication must be integer or float");
+			return NULL;
+		}
+
+		// Perform the multiplication
+		if (DIM_ISFLOATING (dimOb2))
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			floatingResult = (fixedFlag ? PyInt_AsLong (ob1) : PyFloat_AS_DOUBLE (ob1)) / dimOb2->floatingMeasure;
+			PyFPE_END_PROTECT(result)
+		}
+		else
+		{
+			PyFPE_START_PROTECT ("DimOb_Mul", return 0)
+			fixedResult = (fixedFlag ? PyInt_AsLong (ob1) : PyFloat_AS_DOUBLE (ob1))/ dimOb2->fixedMeasure;
+			PyFPE_END_PROTECT(result)
+		}
+
+		return DimOb_FromList (dimOb2->units, floatingResult, fixedResult);*/
+	}
+	else
+	{
+		// Type error
+		LogError ("Incompatible operand types in dimensioned division\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible operand types in dimensioned division");
+		return NULL;
+	}
+}
+
+//	nb_true_divide function - implements true division.
+//	dimOb: The dimensioned object to divide
+//	rhs: A Python float or integer if scaling, a dimensioned object if getting a ratio
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_TrueDivide (PyObject *dimOb, PyObject *rhs)
+{
+	return DimOb_Div (dimOb, rhs);
+}
+
+//	nb_remainder function - calculates the modulus
+//	dimOb: The dimensioned object to divide
+//	rhs: A dimensioned object
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Remainder (PyObject *dimOb, PyObject *rhs)
+{
+	PyObject *result = NULL, *temp = NULL, *ratio = NULL;
+	
+	if (!PyDimensioned_Check (dimOb) && !PyDimensioned_Check (rhs))
+	{
+		// Type error
+		LogError ("Incompatible operands to remainder\n");
+		PyErr_SetString (PyExc_TypeError, "Both operands to remainder must be dimensioned type");
+		return NULL;
+	}
+	
+	ratio = DimOb_Div (dimOb, (PyObject*) rhs);
+	if (ratio == NULL)
+		return NULL;
+	ratio = PyFloat_FromDouble (trunc (PyFloat_AsDouble (ratio)));
+	temp = DimOb_Mul (rhs, ratio);
+	Py_DECREF (ratio);
+	result = DimOb_Sub ((PyDimensionedObject*) dimOb, (PyDimensionedObject*) temp);
+	Py_DECREF (temp);
+	return result;
+}
+
+//	nb_divmod function - calculates the quotient and the remainder.
+//	dimOb1, dimOb2: The two dimensioned objects to add
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Divmod (PyObject *dimOb1, PyObject *dimOb2)
+{
+	if (!PyDimensioned_Check (dimOb1) && !PyDimensioned_Check (dimOb2))
+	{
+		// Type error
+		LogError ("Incompatible operands to divmod\n");
+		PyErr_SetString (PyExc_TypeError, "Both operands to divmod must be dimensioned type");
+		return NULL;
+	}
+	
+	PyDimensionedObject *quotient, *remainder;
+	if ((quotient = (PyDimensionedObject*) DimOb_FloorDiv (dimOb1, (PyObject*) dimOb2)) == NULL)
+		return NULL;
+	if ((remainder = (PyDimensionedObject*) DimOb_Remainder (dimOb1, dimOb2)) == NULL)
+		return NULL;
+	return Py_BuildValue ("OO", quotient, remainder);
+}
+
+//	nb_negative function - negates a dimensioned object.
+//	dimOb: The dimensioned object to negate
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Neg (PyDimensionedObject *dimOb)
+{
+	double floatingResult = 0.0f;
+	long fixedResult = 0;
+	
+	if (DIM_ISFLOATING (dimOb))
+	{
+		PyFPE_START_PROTECT("DimOb_Neg", return 0)
+		floatingResult = dimOb->floatingMeasure * -1.0f;
+		PyFPE_END_PROTECT(result)
+	}
+	else if (DIM_ISFIXED (dimOb))
+	{
+		PyFPE_START_PROTECT("DimOb_Neg", return 0)
+		fixedResult = (long) (dimOb->fixedMeasure * -1);
+		PyFPE_END_PROTECT(result)
+	}
+	else
+	{
+		PyErr_SetString (PyExc_ValueError, "Unknown unit precision");
+		return NULL;
+	}
+
+	return DimOb_FromList (dimOb->units, floatingResult, fixedResult);	
+}
+
+//	nb_positive function - Makes a dimensioned object positive
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Pos (PyDimensionedObject *dimOb)
+{
+	if (PyFloat_CheckExact (dimOb))
+	{
+		Py_INCREF (dimOb);
+		return (PyObject*) dimOb;
+	}
+	else
+	{
+		return DimOb_FromList (dimOb->units, dimOb->floatingMeasure, dimOb->fixedMeasure);	
+	}
+}
+
+//	nb_absolute function - Finds the absolute value of a dimensioned object.
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Abs (PyDimensionedObject *dimOb)
+{
+	return DimOb_FromList (dimOb->units, fabs (dimOb->floatingMeasure), abs (dimOb->fixedMeasure));
+}
+
+//	nb_nonzero function - test if dimensioned object is non-zero.
+//	Returns: 1 if dimensioned object is non-zero, 0 otherwise
+static int DimOb_NonZero (PyDimensionedObject *dimOb)
+{
+	if (DIM_ISFLOATING (dimOb))
+		return dimOb->floatingMeasure != 0.0f;
+	else if (DIM_ISFIXED (dimOb))
+		return dimOb->fixedMeasure != 0;
+	else
+	{
+		PyErr_SetString (PyExc_ValueError, "Unknown unit precision");
+		return 0;
+	}
+}
+
+//	nb_int function - casts the dimensioned object to an integer.
+//	dimOb: The dimensioned object to cast.
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Int (PyDimensionedObject *dimOb)
+{
+	if (DIM_ISFLOATING (dimOb))
+		return PyInt_FromLong ((long) dimOb->floatingMeasure);
+	else
+		return PyInt_FromLong (dimOb->fixedMeasure);
+}
+
+//	nb_long function - casts the dimensioned object to a long.
+//	dimOb: The dimensioned object to cast.
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Long (PyDimensionedObject *dimOb)
+{
+	if (DIM_ISFLOATING (dimOb))
+		return PyLong_FromDouble (dimOb->floatingMeasure);
+	else
+		return PyLong_FromLong (dimOb->fixedMeasure);
+}
+
+//	nb_float function - casts the dimensioned object to a float.
+//	dist: The dimensioned object to cast.
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_Float (PyDimensionedObject *dimOb)
+{
+	if (DIM_ISFLOATING (dimOb))
+		return PyFloat_FromDouble (dimOb->floatingMeasure);
+	else
+		return PyFloat_FromDouble ((double) dimOb->fixedMeasure);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//	Type functions															//
+//		Functions that are methods of the type								//
+//////////////////////////////////////////////////////////////////////////////
+
+//	Converts the dimensioned value from one unit to another.
+//	self: The dimensioned object to convert
+//	args: A python string of the unit to convert to
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_ConvertUnitsMethod (PyDimensionedObject *self, PyObject *args)
+{
+	PyObject *newUnits = NULL;
+	
+	// Get the arguments
+	if (!PyArg_ParseTuple(args, "O", &newUnits))
+		return NULL;
+
+	if (!PyUnit_Check (newUnits))
+	{
+		PyErr_SetString (PyExc_TypeError, "Must pass a unit expression into Convert ()");
+		return NULL;
+	}
+	
+	return DimOb_ConvertUnits (self, ((PyUnitObject*) newUnits)->units);
+}
+
+//	tp_print function - have a guess what this does.
+//	dimOb: The relevant dimensioned object
+//	fp: A file to print to.
+//	flags: I can't remember.
+//	Returns: Nothing but 0, by the looks of things
+static int DimOb_Print (PyDimensionedObject *dimOb, FILE *fp, int flags)
+{
+	char buf[100];
+	
+	DimOb_ToString (buf, 100, dimOb, (flags & Py_PRINT_RAW) ? DIM_PREC_STR : DIM_PREC_REPR);
+	fputs (buf, fp);
+	
+	return 0;
+}
+
+//	tp_repr function - similar to the tp_print function.
+//	dimOb: The relevant dimensioned object
+//	Returns: A PyObject pointer to a Python string
+static PyObject* DimOb_Repr (PyDimensionedObject *dimOb)
+{
+	char buf[100];
+	
+	DimOb_ToString (buf, 100, dimOb, DIM_PREC_REPR);
+	return PyString_FromString (buf);
+}
+
+//	tp_str function - similar to the tp_repr function.
+//	dimOb: The relevant dimensioned object
+//	Returns: A PyObject pointer to a Python string
+static PyObject* DimOb_Str (PyDimensionedObject *dimOb)
+{
+	char buf[100];
+	
+	DimOb_ToString (buf, 100, dimOb, DIM_PREC_STR);
+	return PyString_FromString (buf);
+}
+
+//	tp_cmp function - dimensioned object comparison function.
+//	dimOb1, dimOb2: Values to compare
+//	Returns: -1, 0, or 1 depending on whether dist2 <, == or > dist2
+static int DimOb_Compare (PyDimensionedObject *dimOb1, PyDimensionedObject *dimOb2)
+{
+	PyDimensionedObject *temp = NULL;
+	int result = 0;
+	double flm1, flm2;
+	long fxm1, fxm2;
+
+	// Must be same dimensionality
+	if (!DimOb_EqualDimensions (dimOb1->units, dimOb2->units))
+	{
+		LogError ("Mismatched dimensionality in compare\n");
+		PyErr_SetString (PyExc_ValueError, "Mismatched dimensionality in compare");
+// FIXME
+//		printf ("Caught mismatched dimensionality:\n");
+//		printf ("Object 1:\t%f\t%ld\n", dimOb1->floatingMeasure, dimOb1->fixedMeasure);
+//		printf ("Object 2:\t%f\t%ld\n", dimOb2->floatingMeasure, dimOb2->fixedMeasure);
+		return -2;
+	}
+
+	// If necessary, convert to the same units
+	if (!DimOb_EqualUnits (dimOb1->units, dimOb2->units))
+	{
+		// Convert dimOb2 to dimOb1's units
+		if ((temp = (PyDimensionedObject*) DimOb_ConvertUnits (dimOb2, dimOb1->units)) == NULL)
+		{
+			// Unit conversion error - error already set
+			return -2;
+		}
+	}
+	else
+	{
+		temp = dimOb2;
+		Py_INCREF (temp);
+	}
+
+	flm1 = dimOb1->floatingMeasure;		flm2 = temp->floatingMeasure;
+	fxm1 = dimOb1->fixedMeasure;		fxm2 = temp->fixedMeasure;
+	
+	if (DIM_ISFLOATING (dimOb1))
+		result = ((flm1 < flm2) ? -1 : (flm1 > flm2) ? 1 : 0);
+	else if (DIM_ISFIXED (dimOb1))
+		result = ((fxm1 < fxm2) ? -1 : (fxm1 > fxm2) ? 1 : 0);
+	else
+		result = -1;
+
+	Py_DECREF (temp);
+	return result;
+}
+
+//	tp_richcmp function - dimensioned object comparison function.
+//	dimOb1, dimOb2: Values to compare
+//	opid: the ID of the op to perform
+//	Returns: -1, 0, or 1 depending on whether dist2 <, == or > dist2
+static PyObject* DimOb_RichCompare (PyObject *o1, PyObject *o2, int opid)
+{
+	PyDimensionedObject *dimOb1 = NULL, *dimOb2 = NULL, *temp = NULL;
+	int result = 0;
+	double flm1, flm2;
+	long fxm1, fxm2;
+
+	if (!PyDimensioned_Check (o1) || !PyDimensioned_Check (o2))
+	{
+		// Type error
+		LogError ("Incompatible types in compare\n");
+		PyErr_SetString (PyExc_TypeError, "Incompatible types in compare");
+		return NULL;
+	}
+	
+	dimOb1 = (PyDimensionedObject*) o1;
+	dimOb2 = (PyDimensionedObject*) o2;
+
+	// Must be same dimensionality
+	if (!DimOb_EqualDimensions (dimOb1->units, dimOb2->units))
+	{
+		LogError ("Mismatched dimensionality in compare\n");
+		PyErr_SetString (PyExc_ValueError, "Mismatched dimensionality in compare");
+		return NULL;
+	}
+
+	// If necessary, convert to the same units
+	if (!DimOb_EqualUnits (dimOb1->units, dimOb2->units))
+	{
+		// Convert dimOb2 to dimOb1's units
+		if ((temp = (PyDimensionedObject*) DimOb_ConvertUnits (dimOb2, dimOb1->units)) == NULL)
+		{
+			// Unit conversion error - error already set
+			return NULL;
+		}
+	}
+	else
+	{
+		temp = dimOb2;
+		Py_INCREF (temp);
+	}
+
+	flm1 = dimOb1->floatingMeasure;		flm2 = temp->floatingMeasure;
+	fxm1 = dimOb1->fixedMeasure;		fxm2 = temp->fixedMeasure;
+
+	switch (opid)
+	{
+		case Py_LT:
+			if (DIM_ISFLOATING (dimOb1))
+				result = (flm1 < flm2) ? 1 : 0;
+			else
+				result = (fxm1 < fxm2) ? 1 : 0;
+				break;
+		case Py_LE:
+			if (DIM_ISFLOATING (dimOb1))
+				result = (flm1 <= flm2) ? 1 : 0;
+			else
+				result = (fxm1 <= fxm2) ? 1 : 0;
+				break;
+		case Py_EQ:
+			if (DIM_ISFLOATING (dimOb1))
+				result = (flm1 == flm2) ? 1 : 0;
+			else
+				result = (fxm1 == fxm2) ? 1 : 0;
+				break;
+		case Py_NE:
+			if (DIM_ISFLOATING (dimOb1))
+				result = (flm1 != flm2) ? 1 : 0;
+			else
+				result = (fxm1 != fxm2) ? 1 : 0;
+				break;
+		case Py_GT:
+			if (DIM_ISFLOATING (dimOb1))
+				result = (flm1 > flm2) ? 1 : 0;
+			else
+				result = (fxm1 > fxm2) ? 1 : 0;
+				break;
+		case Py_GE:
+			if (DIM_ISFLOATING (dimOb1))
+				result = (flm1 >= flm2) ? 1 : 0;
+			else
+				result = (fxm1 >= fxm2) ? 1 : 0;
+				break;
+		default:
+			result = 0;
+	}
+
+	Py_DECREF (temp);
+	if (result)
+		Py_RETURN_TRUE;
+	else
+		Py_RETURN_FALSE;
+}
+
+//	Creation function, called to create a new dimensioned object (eg dimensioned(3,'m')).
+//	type, args, kwds: See the Python API documentation for this type of function
+//	Returns: A PyObject pointer to the new dimensioned object
+static PyObject* DimOb_New (PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	PyObject *value = NULL, *units = NULL;
+	static char *kwlist[] = {"value", "unit", NULL};
+	double dblValue = 0.0f;
+	long longValue = 0;
+	
+	if (!PyArg_ParseTupleAndKeywords (args, kwds, "OO:dimensioned", kwlist, &value, &units))
+		return NULL;
+	
+	if (PyInt_Check (value))
+	{
+		longValue = PyInt_AsLong (value);
+		dblValue = (double) longValue;
+	}
+	else if (PyFloat_Check (value))
+	{
+		dblValue = PyFloat_AsDouble (value);
+		longValue = (long) dblValue;
+	}
+	else
+	{
+		PyErr_SetString(PyExc_TypeError, "dimensioned must be supplied an int or float for the value argument");
+		return NULL;
+	}
+	
+	if PyString_Check (units)
+		return DimOb_FromRaw (PyString_AsString (units), dblValue, longValue);
+	else if PyUnit_Check (units)
+		return DimOb_FromList (((PyUnitObject*) units)->units, dblValue, longValue);
+	else
+	{
+		PyErr_SetString(PyExc_TypeError, "dimensioned must be supplied a unit object or a string for the units argument");
+		return NULL;
+	}
+}
+
+//	Destructor.
+//	self: A pointer to the dimensioned object
+static void DimOb_Dealloc (PyDimensionedObject *self)
+{
+	Py_DECREF (self->units);
+	self->ob_type->tp_free ((PyObject*) self);
+}
+
+//	Hash function, used to use the object in dictionaries, etc.
+//	self: A pointer to the dimensioned object
+///////////////////// FIX THIS ///////////////////////////////////////////////
+static long DimOb_Hash (PyDimensionedObject *self)
+{
+	PyObject *longObj;
+	long x;
+
+	if (DIM_ISFLOATING (self))
+		return _Py_HashDouble (self->floatingMeasure);
+	else if (DIM_ISFIXED (self))
+	{
+		// Convert to a Python long and hash that - from object.c
+		if ((longObj = PyLong_FromLong (self->fixedMeasure) ) == NULL)
+		{
+			x = -1;
+			goto finally;
+		}
+		x = PyObject_Hash (longObj);
+finally:
+		Py_XDECREF(longObj);
+		return x;
+	}
+	else
+		return -1;
+}
+
+//	Function to get the value of the dimensioned object as a number 
+//	(similar to the cast functions).
+//	self: A pointer to the dimensioned object
+//	Returns: A PyObject pointer to a Python float or Python integer
+static PyObject* DimOb_GetValue (PyDimensionedObject *self)
+{
+	if (DIM_ISFLOATING (self))
+		return PyFloat_FromDouble (self->floatingMeasure);
+	else if (DIM_ISFIXED (self))
+		return PyInt_FromLong (self->fixedMeasure);
+	else
+		return NULL;
+}
+
+//	Returns the units formated as a string, as seen when the object is printed.
+//	Returns: A pointer to a PyString object
+static PyObject* DimOb_GetUnitString (PyDimensionedObject *self)
+{
+	char buf[100];
+	
+	DimOb_UnitsListToString (buf, 100, self->units);
+	return PyString_FromString (buf);
+}
+
+//	Returns a UnitObject containing the dimensioned value's units
+//	Returns:	A pointer to a PyUnitObject
+static PyObject* DimOb_GetUnitObject (PyDimensionedObject *self)
+{
+	return UnitOb_FromList (self->units);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//	Distance type setup code												//
+//////////////////////////////////////////////////////////////////////////////
+
+// Method definitions
+static PyMethodDef dimObMethods[] =
+{
+	{"Value", (PyCFunction) DimOb_GetValue, METH_NOARGS},
+	{"UnitString", (PyCFunction) DimOb_GetUnitString, METH_NOARGS},
+	{"Units", (PyCFunction) DimOb_GetUnitObject, METH_NOARGS},
+	{"Convert", (PyCFunction) DimOb_ConvertUnitsMethod, METH_VARARGS},
+	{ NULL }		/* Sentinel */
+};
+
+// Member definitions
+static PyMemberDef dimObMembers[] = {
+	{"units", T_OBJECT, offsetof(PyDimensionedObject, units), READONLY, "The units of the dimensioned measure"},
+};
+
+// Documentation string
+PyDoc_STRVAR (dimObDoc, "dimensioned (measure, units) -> dimensioned value\n"
+"\n"
+"Create a dimensioned value using measure quantity of units.\n"
+"For example, dimensioned (1, 'm') = 1m.");
+
+// Number protocol structure
+static PyNumberMethods dimObAsNumber = {
+	(binaryfunc) DimOb_Add, 		// nb_add
+	(binaryfunc) DimOb_Sub, 		// nb_subtract
+	(binaryfunc) DimOb_Mul, 		// nb_multiply
+	(binaryfunc) DimOb_Div,			// nb_divide
+	(binaryfunc) DimOb_Remainder,	// nb_remainder
+	(binaryfunc) DimOb_Divmod,		// nb_divmod
+	0,								// nb_power
+	(unaryfunc) DimOb_Neg,			// nb_negative
+	(unaryfunc) DimOb_Pos,			// nb_positive
+	(unaryfunc) DimOb_Abs,			// nb_absolute
+	(inquiry) DimOb_NonZero,		// nb_nonzero
+	0,								// nb_invert
+	0,								// nb_lshift
+	0,								// nb_rshift
+	0,								// nb_and
+	0,								// nb_xor
+	0,								// nb_or
+	0,								// nb_coerce
+	(unaryfunc) DimOb_Int,			// nb_int
+	(unaryfunc) DimOb_Long,			// nb_long
+	(unaryfunc) DimOb_Float,		// nb_float
+	0,								// nb_oct
+	0,								// nb_hex
+	0,								// nb_inplace_add
+	0,								// nb_inplace_subtract
+	0,								// nb_inplace_multiply
+	0,								// nb_inplace_divide
+	0,								// nb_inplace_remainder
+	0, 								// nb_inplace_power
+	0,								// nb_inplace_lshift
+	0,								// nb_inplace_rshift
+	0,								// nb_inplace_and
+	0,								// nb_inplace_xor
+	0,								// nb_inplace_or
+	(binaryfunc) DimOb_FloorDiv,	// nb_floor_divide
+	(binaryfunc) DimOb_TrueDivide,	// nb_true_divide
+	0,								// nb_inplace_floor_divide
+	0,								// nb_inplace_true_divide
+};
+
+// Type structure
+PyTypeObject PyDimensioned_Type =
+{
+	PyObject_HEAD_INIT (&PyType_Type)
+	0,								// ob_size 
+	"dimensioned",					// tp_name
+	sizeof (PyDimensionedObject),	// tp_basicsize
+	0,								// tp_itemsize
+	(destructor) DimOb_Dealloc,		// tp_dealloc
+	(printfunc) DimOb_Print,		// tp_print
+    0,								// tp_getattr
+    0,								// tp_setattr
+    (cmpfunc) DimOb_Compare,		// tp_compare
+    (reprfunc) DimOb_Repr,			// tp_repr
+    &dimObAsNumber,					// tp_as_number
+    0,								// tp_as_sequence
+    0,								// tp_as_mapping
+    (hashfunc) DimOb_Hash,			// tp_hash
+    0,								// tp_call
+    (reprfunc) DimOb_Str,			// tp_str
+    PyObject_GenericGetAttr,		// tp_getattro
+    0,								// tp_setattro
+    0,								// tp_as_buffer
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES,		// tp_flags
+	dimObDoc,						// tp_doc
+	0,								// tp_traverse
+	0,								// tp_clear
+	(richcmpfunc) DimOb_RichCompare,	// tp_richcompare
+	0,								// tp_weaklistoffset
+	0,								// tp_iter
+	0,								// tp_iternext
+	dimObMethods,					// tp_methods
+	dimObMembers,					// tp_members
+	0,								// tp_getset
+	0,								// tp_base
+	0,								// tp_dict
+	0,								// tp_descr_get
+	0,								// tp_descr_set
+	0,								// tp_dictoffset
+	0,								// tp_init
+	0,								// tp_alloc
+	DimOb_New,						// tp_new
+	PyObject_Del,           		// tp_free
+};
+
+#endif			// __RADAR_EXT__
Index: Objects/dimprecobject.c
===================================================================
--- Objects/dimprecobject.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Objects/dimprecobject.c	(.../radar)	(revision 263)
@@ -0,0 +1,157 @@
+/************************************************************
+ * Distance type object implementation						*
+ * Geoffrey Biggs 2004										*
+ * Borrows from the bool data type.							*
+ ************************************************************/
+ 
+#include "Python.h"
+
+#ifdef __RADAR_EXT__
+
+// We need to define distrange_print to override int_print */
+static int dimprec_print (PyBoolObject *self, FILE *fp, int flags)
+{
+	switch (self->ob_ival)
+	{
+		case 0:
+			fputs ("UnitPrec_Floating", fp);
+			break;
+		case 1:
+			fputs ("UnitPrec_Fixed", fp);
+			break;
+		default:
+			fputs ("UnitPrec_SomeReallyWeirdPrecisionTypeThatShouldn'tExist", fp);
+	}
+	return 0;
+}
+
+// distrange_repr returns a string representing the distance type
+static PyObject *dimprec_floating_str = NULL;
+static PyObject *dimprec_fixing_str = NULL;
+
+static PyObject* dimprec_repr (PyBoolObject *self)
+{
+	PyObject *s;
+
+	switch (self->ob_ival)
+	{
+	case 0:
+		s = dimprec_floating_str ? dimprec_floating_str :
+			(dimprec_floating_str = PyString_InternFromString ("UnitPrec_Floating"));
+		break;
+	case 1:
+		s = dimprec_fixing_str ? dimprec_fixing_str :
+			(dimprec_fixing_str = PyString_InternFromString ("UnitPrec_Fixed"));
+		break;
+
+	default:
+		return NULL;
+	}
+
+	Py_XINCREF(s);
+	return s;
+}
+
+// Create a Py_DimPrec from an integer value between 0 and 1
+PyObject* PyDimPrec_FromLong (long value)
+{
+	PyObject *result;
+
+	switch (value)
+	{
+	case 0:
+		result = Py_DimPrec_Floating;
+		break;
+	case 1:
+		result = Py_DimPrec_Fixed;
+		break;
+	default:
+		return NULL;
+	}
+	
+	Py_INCREF(result);
+	return result;
+}
+
+// We define disttype_new to always return one of:
+// Py_DistLinear, Py_Dist_FixedLinear, Py_Dist_Angular, Py_Dist_FixedAngular
+static PyObject* dimprec_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	static char *kwlist[] = {"x", NULL};
+	long argument = 0;
+
+	if (!PyArg_ParseTupleAndKeywords (args, kwds, "|i", kwlist, &argument))
+		return NULL;
+	if (argument < 0 || argument > 1)
+		return NULL;
+	return PyDimPrec_FromLong (argument);
+}
+
+/****************************************************************************
+ * Type setup stuff - Distance type type									*
+ ***************************************************************************/
+
+PyDoc_STRVAR(dimprec_doc,
+"dim_precision(x) -> dimprec\n\
+\n\
+Provides a dimension precision setting - supply 0 or 1.\n\
+The builtins DimPrec_Floating and DimPrec_Fixed are the only two instances\n\
+of the class dimprec.\n\
+The class dimprec is a subclass of the class int, and cannot be subclassed.");
+
+PyTypeObject PyDimPrec_Type = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,
+	"dimprec",
+	sizeof(PyIntObject),
+	0,
+	0,								/* tp_dealloc */
+	(printfunc)dimprec_print,		/* tp_print */
+	0,								/* tp_getattr */
+	0,								/* tp_setattr */
+	0,								/* tp_compare */
+	(reprfunc)dimprec_repr,		/* tp_repr */
+	0,								/* tp_as_number */
+	0,								/* tp_as_sequence */
+	0,								/* tp_as_mapping */
+	0,								/* tp_hash */
+	0,								/* tp_call */
+	(reprfunc)dimprec_repr,		/* tp_str */
+	0,								/* tp_getattro */
+	0,								/* tp_setattro */
+	0,								/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
+	dimprec_doc,					/* tp_doc */
+	0,								/* tp_traverse */
+	0,								/* tp_clear */
+	0,								/* tp_richcompare */
+	0,								/* tp_weaklistoffset */
+	0,								/* tp_iter */
+	0,								/* tp_iternext */
+	0,								/* tp_methods */
+	0,								/* tp_members */
+	0,								/* tp_getset */
+	&PyInt_Type,					/* tp_base */
+	0,								/* tp_dict */
+	0,								/* tp_descr_get */
+	0,								/* tp_descr_set */
+	0,								/* tp_dictoffset */
+	0,								/* tp_init */
+	0,								/* tp_alloc */
+	dimprec_new,					/* tp_new */
+};
+
+/* The objects representing values DIST_RANGEINFINITE, DIST_RANGEINCLUDE and DIST_RANGEEXCLUDE */
+PyIntObject _Py_DimPrec_FloatingStruct =
+{
+	PyObject_HEAD_INIT (&PyDimPrec_Type)
+	0
+};
+
+PyIntObject _Py_DimPrec_FixedStruct = 
+{
+	PyObject_HEAD_INIT (&PyDimPrec_Type)
+	1
+};
+
+#endif
Index: Objects/dimrangeobject.c
===================================================================
--- Objects/dimrangeobject.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Objects/dimrangeobject.c	(.../radar)	(revision 263)
@@ -0,0 +1,190 @@
+/************************************************************
+ * Distance type object implementation						*
+ * Geoffrey Biggs 2004										*
+ * Borrows from the bool data type.							*
+ ************************************************************/
+ 
+#include "Python.h"
+
+#ifdef __RADAR_EXT__
+
+// We need to define distrange_print to override int_print */
+static int dimrange_print (PyBoolObject *self, FILE *fp, int flags)
+{
+	switch (self->ob_ival)
+	{
+		case 0:
+			fputs ("UnitRange_Include", fp);
+			break;
+		case 1:
+			fputs ("UnitRange_Exclude", fp);
+			break;
+		case 2:
+			fputs ("UnitRange_Infinite", fp);
+			break;
+		case 3:
+			fputs ("UnitRange_Wrap", fp);
+			break;
+		default:
+			fputs ("UnitRange_SomeReallyWeirdRangeTypeThatShouldn'tExist", fp);
+	}
+	return 0;
+}
+
+// distrange_repr returns a string representing the dimensioned type
+static PyObject *dimrange_infinite_str = NULL;
+static PyObject *dimrange_include_str = NULL;
+static PyObject *dimrange_exclude_str = NULL;
+static PyObject *dimrange_wrap_str = NULL;
+
+static PyObject* dimrange_repr (PyBoolObject *self)
+{
+	PyObject *s;
+
+	switch (self->ob_ival)
+	{
+	case 0:
+		s = dimrange_infinite_str ? dimrange_infinite_str :
+			(dimrange_infinite_str = PyString_InternFromString ("UnitRange_RangeInclude"));
+		break;
+	case 1:
+		s = dimrange_include_str ? dimrange_include_str :
+			(dimrange_include_str = PyString_InternFromString ("UnitRange_RangeExclude"));
+		break;
+	case 2:
+		s = dimrange_exclude_str ? dimrange_exclude_str :
+			(dimrange_exclude_str = PyString_InternFromString ("UnitRange _RangeInfinite"));
+		break;
+	case 3:
+		s = dimrange_wrap_str ? dimrange_wrap_str :
+			(dimrange_wrap_str = PyString_InternFromString ("UnitRange_RangeWrap"));
+		break;
+	default:
+		return NULL;
+	}
+
+	Py_XINCREF(s);
+	return s;
+}
+
+// Create a Py_DistType from an integer value between 0 and 3
+PyObject* PyDimRange_FromLong (long value)
+{
+	PyObject *result;
+
+	switch (value)
+	{
+	case 0:
+		result = Py_DimRange_Include;
+		break;
+	case 1:
+		result = Py_DimRange_Exclude;
+		break;
+	case 2:
+		result = Py_DimRange_Infinite;
+		break;
+	case 3:
+		result = Py_DimRange_Wrap;
+		break;
+	default:
+		return NULL;
+	}
+	
+	Py_INCREF(result);
+	return result;
+}
+
+// We define disttype_new to always return one of:
+// Py_DistLinear, Py_Dist_FixedLinear, Py_Dist_Angular, Py_Dist_FixedAngular
+static PyObject* dimrange_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	static char *kwlist[] = {"x", NULL};
+	long argument = 0;
+
+	if (!PyArg_ParseTupleAndKeywords (args, kwds, "|i", kwlist, &argument))
+		return NULL;
+	if (argument < 0 || argument > 3)
+		return NULL;
+	return PyDimRange_FromLong (argument);
+}
+
+/****************************************************************************
+ * Type setup stuff - Distance type type									*
+ ***************************************************************************/
+
+PyDoc_STRVAR(dimrange_doc,
+"dimrange(x) -> dimrange\n\
+\n\
+Provides a dimensioned range setting - supply 0 or 1.\n\
+The builtins Dist_RangeInclude and Dist_RangeExclude are the only two instances\n\
+of the class dimrange.\n\
+The class dimrange is a subclass of the class int, and cannot be subclassed.");
+
+PyTypeObject PyDimRange_Type = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,
+	"dimrange",
+	sizeof(PyIntObject),
+	0,
+	0,								/* tp_dealloc */
+	(printfunc)dimrange_print,		/* tp_print */
+	0,								/* tp_getattr */
+	0,								/* tp_setattr */
+	0,								/* tp_compare */
+	(reprfunc)dimrange_repr,		/* tp_repr */
+	0,								/* tp_as_number */
+	0,								/* tp_as_sequence */
+	0,								/* tp_as_mapping */
+	0,								/* tp_hash */
+	0,								/* tp_call */
+	(reprfunc)dimrange_repr,		/* tp_str */
+	0,								/* tp_getattro */
+	0,								/* tp_setattro */
+	0,								/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
+	dimrange_doc,					/* tp_doc */
+	0,								/* tp_traverse */
+	0,								/* tp_clear */
+	0,								/* tp_richcompare */
+	0,								/* tp_weaklistoffset */
+	0,								/* tp_iter */
+	0,								/* tp_iternext */
+	0,								/* tp_methods */
+	0,								/* tp_members */
+	0,								/* tp_getset */
+	&PyInt_Type,					/* tp_base */
+	0,								/* tp_dict */
+	0,								/* tp_descr_get */
+	0,								/* tp_descr_set */
+	0,								/* tp_dictoffset */
+	0,								/* tp_init */
+	0,								/* tp_alloc */
+	dimrange_new,					/* tp_new */
+};
+
+/* The objects representing values DIST_RANGEINFINITE, DIST_RANGEINCLUDE and DIST_RANGEEXCLUDE */
+PyIntObject _Py_DimRange_IncludeStruct =
+{
+	PyObject_HEAD_INIT (&PyDimRange_Type)
+	0
+};
+
+PyIntObject _Py_DimRange_ExcludeStruct = 
+{
+	PyObject_HEAD_INIT (&PyDimRange_Type)
+	1
+};
+
+PyIntObject _Py_DimRange_InfiniteStruct = 
+{
+	PyObject_HEAD_INIT (&PyDimRange_Type)
+	2
+};
+
+PyIntObject _Py_DimRange_WrapStruct = 
+{
+	PyObject_HEAD_INIT (&PyDimRange_Type)
+	3
+};
+
+#endif
Index: Parser/tokenizer.c
===================================================================
--- Parser/tokenizer.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Parser/tokenizer.c	(.../radar)	(revision 263)
@@ -96,7 +96,8 @@
 	/* This table must match the #defines in token.h! */
 	"OP",
 	"<ERRORTOKEN>",
-	"<N_TOKENS>"
+	"<N_TOKENS>",
+	"DOLLAR"
 };
 
 
@@ -928,6 +929,7 @@
 	case '^':	return CIRCUMFLEX;
 	case '~':	return TILDE;
 	case '@':       return AT;
+	case '$':	return DOLLAR;
 	default:	return OP;
 	}
 }
Index: PC/config.c
===================================================================
--- PC/config.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ PC/config.c	(.../radar)	(revision 263)
@@ -17,6 +17,7 @@
 extern void initimageop(void);
 #endif
 extern void initmath(void);
+extern void initmathd(void);
 extern void initmd5(void);
 extern void initnt(void);
 extern void initoperator(void);
@@ -85,6 +86,7 @@
         {"imageop", initimageop},
 #endif
         {"math", initmath},
+        {"mathd", initmathd},
         {"md5", initmd5},
         {"nt", initnt}, /* Use the NT os functions, not posix */
         {"operator", initoperator},
Index: RADAR_README
===================================================================
--- RADAR_README	(.../tags/pure-python-2.4.2)	(revision 0)
+++ RADAR_README	(.../radar)	(revision 263)
@@ -0,0 +1,213 @@
+Python/RADAR interpreter v0.2
+
+Geoffrey Biggs
+23/11/2005
+
+NOTE: This is not the README for the standard Python interpreter! That file is
+dist/src/README
+
+This is a version of the Python interpreter with some extensions, known as RADAR.
+The extensions provide new functionality to the Python language as described below.
+
+See http://www.python.org/ for information on the Python language and interpreter.
+
+The RADAR extensions use the same license as the standard Python interpreter.
+See the file LICENSE in the Python source archive. If you have just got the
+RADAR patch, you will need to download the Python source from www.python.org and
+apply the patch to it.
+
+
+Installation
+============
+
+The recommended method of installation is downloading the official Python source
+and using the RADAR patch on it, as that may be more up-to-date with pure Python
+than the full source provided here.
+
+I recommend you install the interpreter using a separate prefix to avoid
+conflicts with your standard python install. The installation process is a
+standard case of configure then make install. To include the RADAR extensions in
+the interpreter, you must configure with the option set to on. Here's my
+configure command:
+
+./configure --with-radar --with-pydebug --prefix=/home/geoff
+
+Follow this with "make install" to build and install the interpreter and modules.
+
+Applying the patch:
+If you are using the patch instead of the full source, you will need to apply it
+before installation. Apply it with the following command:
+
+patch -p1 < python_radar.patch
+
+The interpreter's executable will be named python_radar. Make a symbolic link to
+python in a directory in your path if you wish it to take precedence over the
+standard python interpreter. Theoretically it should be perfectly backwards
+compatible with the standard interpreter.
+
+After installation, you may need to set your PYTHONPATH environment variable
+to point to where you installed it. For example,
+
+export PYTHONPATH=/home/geoff/lib/python2.4:/home/geoff/lib/python2.4/site-packages:/home/geoff/lib/python2.4/lib-dynload:$PYTHONPATH
+
+Note for Mac users:
+The files "Mac/IDE scripts/  separator ---" and "Mac/IDE scripts/separator ---"
+have been removed because Eclipse chokes on them when building a project,
+something I find rather annoying. You will need to restore this file yourself
+from the Python source archive if you need them. I don't actually know what
+they do.
+
+
+Dimensional Analysis
+====================
+
+Dimensional analysis is a system for ensuring consistency in data values that
+correspond to real-world values. It is commonly used in physical sciences to
+check consistency of equations. It can be applied to programming languages to
+bring this consistency checking to data dealing with the real world in many
+situations, particularly robotics. The aim of this component is to provide a
+simple system for doing this directly in program code for robot programming.
+
+Dimensional analysis is provided by a module for managing dimensions and units
+and a new syntax for specifying units (both simple and derived).
+
+dimension Module
+----------------
+
+Import this module to get the functions used for managing dimensions and units
+currently defined in the system. New dimensions can be defined. New units can be
+defined in any currently defined dimension. See the __doc__ for each function
+for usage details. The example below shows how to add a new dimension,
+"fasterthanlight", to the system and then define the unit "warp" in this new
+dimension. The unit is defined to have range limits of 0 to 9.9 and floating
+point precision. It is then set as the base unit for the dimension.
+
+import dimension
+dimension.DefineDimension ('fasterthanlight')
+dimension.DefineUnit ('fasterthanlight',
+                      'warp',
+                      UnitPrec_Floating, 1.0,
+                      UnitRange_Exclude, 0,
+                      UnitRange_Include, 9.9)
+
+Units can optionally be set to have range limits (as shown in the above example).
+This will cause any values using those units to be restricted to the given range.
+Values outside the range will cause an exception to be raised, for example:
+
+>>> 10~warp
+OverflowError: Range limit for unit exceeded
+
+Valid range limits are: UnitRange_Include, UnitRange_Exclude, UnitRange_Infinite
+and UnitRange_Wrap.
+Wrapped range limits can be created as in the following example:
+
+dimension.DefineUnit ('fasterthanlight',
+                      'hyper',
+                      UnitPrec_Floating, 1.4,
+                      UnitRange_Wrap,    0,
+                      UnitRange_Wrap,    5.0)
+
+This will create a unit who's values will always fall within the range 0 to <5.
+
+
+Specifying Units in Code
+------------------------
+
+To use units in code (ie to create a dimensioned type) the following syntax is
+used:
+
+(integer | float)~unit_expr
+
+where:
+    unit_expr: unit (('*' unit) | ('/' unit))*
+    unit: [['-'] NUMBER] NAME ['^' ['-'] NUMBER]
+
+So, for example:
+
+5~m
+3~s
+6~m/s
+-2~m/s/s
+15~kg*m/s
+
+You can also call the dimensioned() constructor to create dimensioned data:
+
+>>> dimensioned (5, $m)
+5~m
+>>> dimensioned (15, $kg*m/s)
+15~kg*m/s
+
+This is useful if you are creating dimensioned data from, for example, a float or int.
+
+The following functions can be called on dimensioned data:
+
+>>> a = 5~m/s/s
+>>> a.Value ()
+5.0
+>>> a.UnitString ()
+'m/s^2'
+>>> a.Units ()
+m/s^2
+>>> a.Convert ($m/h/h)
+64800000~m/h^2
+
+Note the use of a unit object in the convert function. The .Units () member
+function returns a unit object, while the .UnitString () member function
+returns a string representation (which could also be obtained by the syntax
+`a.Units ()`).
+
+Dimensioned data can be converted to any units with the same dimensionality as
+the current units. In the example above, m/s/s has the same dimensionality as
+m/h/h (ie, distance/time/time). If an illegal conversion is attempted an
+exception will be thrown. Similarly, exceptions are thrown when incompatible
+units are mixed, for example when trying to add metres and seconds:
+
+>>> 5~m + 3~s
+Traceback (most recent call last):
+  File "<stdin>", line 1, in ?
+ValueError: Incompatible units - cannot convert between mismatched dimensions
+
+
+Defaults
+--------
+
+The system comes with the 4 dimensions commonly found in robotics and a number
+of units pre-defined. These are, as given by dimension.Units ():
+
+('m', 'distance', UnitPrec_Floating, 1.0)
+('cell', 'distance', UnitPrec_Fixing, 1.0)
+('mm', 'distance', UnitPrec_Floating, 0.0010000000474974513)
+('cm', 'distance', UnitPrec_Floating, 0.0099999997764825821)
+('km', 'distance', UnitPrec_Floating, 1000.0)
+('rad', 'angle', UnitPrec_Floating, 1.0)
+('tick', 'angle', UnitPrec_Fixing, 57.295780181884766)
+('deg', 'angle', UnitPrec_Floating, 0.01745329238474369)
+('cdeg', 'angle', UnitPrec_Floating, 0.00017453292093705386)
+('mrad', 'angle', UnitPrec_Floating, 0.0010000000474974513)
+('arcmin', 'angle', UnitPrec_Floating, 0.9549296498298645)
+('arcsec', 'angle', UnitPrec_Floating, 0.015915494412183762)
+('wdeg', 'angle', UnitPrec_Floating, 57.295780181884766, UnitRange_Wrap, 0.0, UnitRange_Wrap, 360.0)
+('wrad', 'angle', UnitPrec_Floating, 1.0, UnitRange_Wrap, 0.0, UnitRange_Wrap, 6.2831853071795862)
+('s', 'time', UnitPrec_Floating, 1.0)
+('min', 'time', UnitPrec_Floating, 60.0)
+('h', 'time', UnitPrec_Floating, 3600.0)
+('g', 'mass', UnitPrec_Floating, 1.0)
+('kg', 'mass', UnitPrec_Floating, 1.0)
+
+
+
+
+Changelog (see dist/src for changes to the standard interpreter)
+
+20050520    Initial release
+20050527    Made the time.sleep () function accept dimensioned data
+20050530    Added the .Units () member function to dimensioned objects
+            Removed the dimtype object type since it isn't needed anymore.
+20050601    Fixed the absolute value function. It no longer rounds the
+            floating value to the nearest whole number. :)
+20050804    System renamed to RADAR.
+20051123    Upgraded the full source tarball to Python 2.4.2
+            Updated RADAR_README
+20060303    Added note about dimensioned() constructor
+20060407    Fixed a bug with use of custom units
+20060428	    Fixed a bug in division of dimensionally equivalent units
Index: Makefile.pre.in
===================================================================
--- Makefile.pre.in	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Makefile.pre.in	(.../radar)	(revision 263)
@@ -24,6 +24,8 @@
 MODLIBS=        _MODLIBS_
 
 # === Variables set by configure
+# RADAR suffix
+RADAR=		@RADARSUFFIX@
 VERSION=	@VERSION@
 srcdir=		@srcdir@
 VPATH=		@srcdir@
@@ -160,8 +162,8 @@
 MACHDEP_OBJS=	@MACHDEP_OBJS@
 UNICODE_OBJS=   @UNICODE_OBJS@
 
-PYTHON=		python$(EXE)
-BUILDPYTHON=	python$(BUILDEXE)
+PYTHON=		python$(RADAR)$(EXE)
+BUILDPYTHON=	python$(RADAR)$(BUILDEXE)
 
 # === Definitions added by makesetup ===
 
@@ -248,6 +250,8 @@
 		Python/traceback.o \
 		Python/getopt.o \
 		Python/pystrtod.o \
+		Python/dimensionmodule.o \
+		Python/statslogger.o \
 		Python/$(DYNLOADFILE) \
 		$(MACHDEP_OBJS) \
 		$(THREADOBJ)
@@ -287,6 +291,10 @@
 		Objects/tupleobject.o \
 		Objects/typeobject.o \
 		Objects/weakrefobject.o \
+		Objects/dimrangeobject.o \
+		Objects/dimprecobject.o \
+		Objects/dimensionobject.o \
+		Objects/unitobject.o \
 		$(UNICODE_OBJS)
 
 
@@ -518,6 +526,11 @@
 		Include/tupleobject.h \
 		Include/unicodeobject.h \
 		Include/weakrefobject.h \
+		Include/dimensiontypes.h \
+		Include/dimrangeobject.h \
+		Include/dimprecobject.h \
+		Include/dimensionobject.h \
+		Include/unitobject.h \
 		pyconfig.h
 
 $(LIBRARY_OBJS) $(MODOBJS) Modules/$(MAINOBJ): $(PYTHON_HEADERS)
Index: Modules/mathdmodule.c
===================================================================
--- Modules/mathdmodule.c	(.../tags/pure-python-2.4.2)	(revision 0)
+++ Modules/mathdmodule.c	(.../radar)	(revision 263)
@@ -0,0 +1,443 @@
+/* Math module -- standard C math library functions, pi and e */
+
+#include "Python.h"
+#include "longintrepr.h"
+
+/* Call is_error when errno != 0, and where x is the result libm
+ * returned.  is_error will usually set up an exception and return
+ * true (1), but may return false (0) without setting up an exception.
+ */
+static int
+is_error(double x)
+{
+	int result = 1;	/* presumption of guilt */
+	assert(errno);	/* non-zero errno is a precondition for calling */
+	if (errno == EDOM)
+		PyErr_SetString(PyExc_ValueError, "math domain error");
+
+	else if (errno == ERANGE) {
+		/* ANSI C generally requires libm functions to set ERANGE
+		 * on overflow, but also generally *allows* them to set
+		 * ERANGE on underflow too.  There's no consistency about
+		 * the latter across platforms.
+		 * Alas, C99 never requires that errno be set.
+		 * Here we suppress the underflow errors (libm functions
+		 * should return a zero on underflow, and +- HUGE_VAL on
+		 * overflow, so testing the result for zero suffices to
+		 * distinguish the cases).
+		 */
+		if (x)
+			PyErr_SetString(PyExc_OverflowError,
+					"math range error");
+		else
+			result = 0;
+	}
+	else
+                /* Unexpected math error */
+		PyErr_SetFromErrno(PyExc_ValueError);
+	return result;
+}
+
+static PyObject* mathd_acos (PyObject *self, PyObject *args)
+{
+	double x;
+	if (!PyArg_ParseTuple (args, "d:mathd_acos", &x))
+		return NULL;
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_acos", return 0)
+	x = acos (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return DimOb_FromRaw ("rad", x, x);
+}
+
+PyDoc_STRVAR(mathd_acos_doc, "acos(x)\n\nReturn the arc cosine (measured in radians) of x.");
+
+static PyObject* mathd_asin (PyObject *self, PyObject *args)
+{
+	double x;
+	if (!PyArg_ParseTuple (args, "d:mathd_asin", &x))
+		return NULL;
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_asin", return 0)
+	x = asin (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return DimOb_FromRaw ("rad", x, x);
+}
+
+PyDoc_STRVAR(mathd_asin_doc, "asin(x)\n\nReturn the arc sine (measured in radians) of x.");
+
+static PyObject* mathd_atan (PyObject *self, PyObject *args)
+{
+	double x;
+	if (!PyArg_ParseTuple (args, "d:mathd_atan", &x))
+		return NULL;
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_atan", return 0)
+	x = atan (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return DimOb_FromRaw ("rad", x, x);
+}
+
+PyDoc_STRVAR(mathd_atan_doc, "atan(x)\n\nReturn the arc tangent (measured in radians) of x.");
+
+static PyObject* mathd_atan2 (PyObject *self, PyObject *args)
+{
+	double x, y;
+	PyDimensionedObject *xObj = NULL, *yObj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "OO:mathd_atan2", &xObj, &yObj))
+	{
+		if (!PyDimensioned_Check (xObj) || !PyDimensioned_Check (yObj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned values to mathd.atan2");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "m", 1);
+			if ((tempObj = DimOb_ConvertUnits (xObj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			if ((tempObj = DimOb_ConvertUnits (yObj, unitsObj)) == NULL)
+				return NULL;
+			y = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_atan2", return 0)
+	x = atan2 (x, y);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return DimOb_FromRaw ("rad", x, x);
+}
+
+PyDoc_STRVAR(mathd_atan2_doc, "atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n"
+    						 "Unlike atan(y/x), the signs of both x and y are considered.");
+
+
+static PyObject* mathd_ceil (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	if (!PyArg_ParseTuple (args, "O:mathd_ceil", &obj))
+		return NULL;
+	if (!PyDimensioned_Check (obj))
+	{
+		PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.ceil");
+		return NULL;
+	}
+	if DIM_ISFLOATING (obj)
+		x = obj->floatingMeasure;
+	else
+		x = obj->fixedMeasure;
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_ceil", return 0)
+	x = ceil (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return DimOb_FromList (obj->units, x, x);
+}
+
+PyDoc_STRVAR(mathd_ceil_doc, "ceil(x)\n\nReturn the ceiling of x as a float.\n"
+      "This is the smallest integral value >= x.");
+
+static PyObject* mathd_cos (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:mathd_cos", &obj))
+	{
+		if (!PyDimensioned_Check (obj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.cos");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "rad", 1);
+			if ((tempObj = DimOb_ConvertUnits (obj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_cos", return 0)
+	x = cos (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return PyFloat_FromDouble (x);
+}
+
+PyDoc_STRVAR(mathd_cos_doc, "cos(x)\n\nReturn the cosine of x (measured in radians).");
+
+static PyObject* mathd_cosh (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:mathd_cosh", &obj))
+	{
+		if (!PyDimensioned_Check (obj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.cosh");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "rad", 1);
+			if ((tempObj = DimOb_ConvertUnits (obj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_cosh", return 0)
+	x = cosh (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return PyFloat_FromDouble (x);
+}
+
+PyDoc_STRVAR(mathd_cosh_doc, "cosh(x)\n\nReturn the hyperbolic cosine of x.");
+
+static PyObject* mathd_floor (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	if (!PyArg_ParseTuple (args, "O:mathd_floor", &obj))
+		return NULL;
+	if (!PyDimensioned_Check (obj))
+	{
+		PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.floor");
+		return NULL;
+	}
+	if DIM_ISFLOATING (obj)
+		x = obj->floatingMeasure;
+	else
+		x = obj->fixedMeasure;
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_floor", return 0)
+	x = floor (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return DimOb_FromList (obj->units, x, x);
+}
+
+PyDoc_STRVAR(mathd_floor_doc, "floor(x)\n\nReturn the floor of x as a float.\n"
+      "This is the largest integral value <= x.");
+
+static PyObject* mathd_sin (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:mathd_sin", &obj))
+	{
+		if (!PyDimensioned_Check (obj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.sin");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "rad", 1);
+			if ((tempObj = DimOb_ConvertUnits (obj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_sin", return 0)
+	x = sin (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return PyFloat_FromDouble (x);
+}
+
+PyDoc_STRVAR(mathd_sin_doc, "sin(x)\n\nReturn the sine of x (measured in radians).");
+
+static PyObject* mathd_sinh (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:mathd_sinh", &obj))
+	{
+		if (!PyDimensioned_Check (obj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.sinh");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "rad", 1);
+			if ((tempObj = DimOb_ConvertUnits (obj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_sinh", return 0)
+	x = sinh (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return PyFloat_FromDouble (x);
+}
+
+PyDoc_STRVAR(mathd_sinh_doc, "sinh(x)\n\nReturn the hyperbolic sine of x.");
+
+static PyObject* mathd_tan (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:mathd_tan", &obj))
+	{
+		if (!PyDimensioned_Check (obj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.tan");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "rad", 1);
+			if ((tempObj = DimOb_ConvertUnits (obj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_tan", return 0)
+	x = tan (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return PyFloat_FromDouble (x);
+}
+
+PyDoc_STRVAR(mathd_tan_doc, "tan(x)\n\nReturn the tangent of x (measured in radians).");
+
+static PyObject* mathd_tanh (PyObject *self, PyObject *args)
+{
+	double x;
+	PyDimensionedObject *obj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:mathd_tanh", &obj))
+	{
+		if (!PyDimensioned_Check (obj))
+		{
+			PyErr_SetString (PyExc_ValueError, "Must supply dimensioned value to mathd.tanh");
+			return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "rad", 1);
+			if ((tempObj = DimOb_ConvertUnits (obj, unitsObj)) == NULL)
+				return NULL;
+			x = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+
+	errno = 0;
+	PyFPE_START_PROTECT ("in mathd_tanh", return 0)
+	x = tanh (x);
+	PyFPE_END_PROTECT (x)
+	Py_SET_ERRNO_ON_MATH_ERROR (x);
+	if (errno && is_error (x))
+		return NULL;
+	else
+		return PyFloat_FromDouble (x);
+}
+
+PyDoc_STRVAR(mathd_tanh_doc, "tanh(x)\n\nReturn the hyperbolic tangent of x.");
+
+//FUNC2(hypot, hypot,
+//      "hypot(x,y)\n\nReturn the Euclidean distance, sqrt(x*x + y*y).")
+//FUNC1(sqrt, sqrt,
+//      "sqrt(x)\n\nReturn the square root of x.")
+
+static PyMethodDef mathd_methods[] = {
+	{"acos",	mathd_acos,	METH_VARARGS,	mathd_acos_doc},
+	{"asin",	mathd_asin,	METH_VARARGS,	mathd_asin_doc},
+	{"atan",	mathd_atan,	METH_VARARGS,	mathd_atan_doc},
+	{"atan2",	mathd_atan2,	METH_VARARGS,	mathd_atan2_doc},
+	{"ceil",	mathd_ceil,	METH_VARARGS,	mathd_ceil_doc},
+	{"cos",		mathd_cos,	METH_VARARGS,	mathd_cos_doc},
+	{"cosh",	mathd_cosh,	METH_VARARGS,	mathd_cosh_doc},
+	{"floor",	mathd_floor,	METH_VARARGS,	mathd_floor_doc},
+//	{"hypot",	mathd_hypot,	METH_VARARGS,	mathd_hypot_doc},
+	{"sin",		mathd_sin,	METH_VARARGS,	mathd_sin_doc},
+	{"sinh",	mathd_sinh,	METH_VARARGS,	mathd_sinh_doc},
+//	{"sqrt",	mathd_sqrt,	METH_VARARGS,	mathd_sqrt_doc},
+	{"tan",		mathd_tan,	METH_VARARGS,	mathd_tan_doc},
+	{"tanh",	mathd_tanh,	METH_VARARGS,	mathd_tanh_doc},
+	{NULL,		NULL}		/* sentinel */
+};
+
+PyDoc_STRVAR (module_doc,
+"This module is always available.  It provides access to the\n"
+"mathematical functions defined by the C standard, customised."
+"to work with the dimensioned data type.");
+
+PyMODINIT_FUNC initmathd (void)
+{
+	Py_InitModule3 ("mathd", mathd_methods, module_doc);
+	return;
+}
Index: Modules/timemodule.c
===================================================================
--- Modules/timemodule.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Modules/timemodule.c	(.../radar)	(revision 263)
@@ -194,8 +194,28 @@
 time_sleep(PyObject *self, PyObject *args)
 {
 	double secs;
-	if (!PyArg_ParseTuple(args, "d:sleep", &secs))
+	PyDimensionedObject *secsObj = NULL;
+	PyObject *unitsObj = NULL, *tempObj = NULL;
+	if (PyArg_ParseTuple (args, "O:sleep", &secsObj))
+	{
+		if (!PyDimensioned_Check (secsObj))
+		{
+			if (!PyArg_ParseTuple(args, "d:sleep", &secs))
+				return NULL;
+		}
+		else
+		{
+			unitsObj = DimOb_CreateUnitList (1, 1, "s", 1);
+			if ((tempObj = DimOb_ConvertUnits (secsObj, unitsObj)) == NULL)
+				return NULL;
+			secs = ((PyDimensionedObject*) tempObj)->floatingMeasure;
+			Py_DECREF (tempObj);
+			Py_DECREF (unitsObj);
+		}
+	}
+	else
 		return NULL;
+
 	if (floatsleep(secs) != 0)
 		return NULL;
 	Py_INCREF(Py_None);
Index: Modules/main.c
===================================================================
--- Modules/main.c	(.../tags/pure-python-2.4.2)	(revision 263)
+++ Modules/main.c	(.../radar)	(revision 263)
@@ -415,6 +415,11 @@
 #else
 	Py_SetProgramName(argv[0]);
 #endif
+
+#ifdef __RADAR_EXT__
+//	printf ("Python/RADAR 0.2\n");
+#endif
+
 	Py_Initialize();
 
 	if (Py_VerboseFlag ||

Property changes on: .
___________________________________________________________________
Name: svn:ignore
   - *.pyc
*.o
.purify
config.log
config.cache
config.status
Makefile
buildno
python
build
Makefile.pre
platform
pyconfig.h
autom4te.cache
.project
libpython2.4.a
.cdtproject
python_sammy


   + *.pyc
*.o
.purify
config.log
config.cache
config.status
Makefile
buildno
python
build
Makefile.pre
platform
pyconfig.h
autom4te.cache
.project
libpython2.4.a
.cdtproject
python_sammy
python_radar
Parser/pgen
Mac/Python
Modules/Setup
Modules/Setup.local
Modules/Setup.config
Modules/config.c



