@@ -8905,6 +8905,7 @@ static void
8905
8905
test_sort_tables_errors (void )
8906
8906
{
8907
8907
int ret ;
8908
+ tsk_id_t ret_id ;
8908
8909
tsk_treeseq_t ts ;
8909
8910
tsk_table_collection_t tables ;
8910
8911
tsk_bookmark_t pos ;
@@ -8968,6 +8969,32 @@ test_sort_tables_errors(void)
8968
8969
ret = tsk_table_collection_sort (& tables , & pos , 0 );
8969
8970
CU_ASSERT_EQUAL_FATAL (ret , TSK_ERR_SORT_OFFSET_NOT_SUPPORTED );
8970
8971
8972
+ /* Test TSK_ERR_MUTATION_PARENT_INCONSISTENT */
8973
+ ret = tsk_table_collection_clear (& tables , 0 );
8974
+ CU_ASSERT_EQUAL_FATAL (ret , 0 );
8975
+ tables .sequence_length = 1.0 ;
8976
+
8977
+ ret_id = tsk_node_table_add_row (& tables .nodes , 0 , 0.0 , TSK_NULL , TSK_NULL , NULL , 0 );
8978
+ CU_ASSERT_FATAL (ret_id >= 0 );
8979
+ ret_id = tsk_site_table_add_row (& tables .sites , 0.0 , "x" , 1 , NULL , 0 );
8980
+ CU_ASSERT_FATAL (ret_id >= 0 );
8981
+
8982
+ ret_id
8983
+ = tsk_mutation_table_add_row (& tables .mutations , 0 , 0 , 2 , 0.0 , "a" , 1 , NULL , 0 );
8984
+ CU_ASSERT_FATAL (ret_id >= 0 );
8985
+ ret_id
8986
+ = tsk_mutation_table_add_row (& tables .mutations , 0 , 0 , 3 , 0.0 , "b" , 1 , NULL , 0 );
8987
+ CU_ASSERT_FATAL (ret_id >= 0 );
8988
+ ret_id
8989
+ = tsk_mutation_table_add_row (& tables .mutations , 0 , 0 , 1 , 0.0 , "c" , 1 , NULL , 0 );
8990
+ CU_ASSERT_FATAL (ret_id >= 0 );
8991
+ ret_id
8992
+ = tsk_mutation_table_add_row (& tables .mutations , 0 , 0 , 2 , 0.0 , "d" , 1 , NULL , 0 );
8993
+ CU_ASSERT_FATAL (ret_id >= 0 );
8994
+
8995
+ ret = tsk_table_collection_sort (& tables , NULL , 0 );
8996
+ CU_ASSERT_EQUAL_FATAL (ret , TSK_ERR_MUTATION_PARENT_INCONSISTENT );
8997
+
8971
8998
tsk_table_collection_free (& tables );
8972
8999
tsk_treeseq_free (& ts );
8973
9000
}
@@ -9032,6 +9059,120 @@ test_sort_tables_mutation_times(void)
9032
9059
tsk_treeseq_free (& ts );
9033
9060
}
9034
9061
9062
+ static void
9063
+ test_sort_tables_mutations (void )
9064
+ {
9065
+ int ret ;
9066
+ tsk_table_collection_t tables ;
9067
+
9068
+ /* Sorting hierarchy:
9069
+ * 1. site
9070
+ * 2. time (when known)
9071
+ * 3. node_time
9072
+ * 4. num_descendants: parent mutations first
9073
+ * 5. node_id
9074
+ * 6. mutation_id
9075
+ */
9076
+
9077
+ const char * sites = "0.0 A\n"
9078
+ "0.5 T\n"
9079
+ "0.75 G\n" ;
9080
+
9081
+ const char * mutations_unsorted =
9082
+ /* Test site criterion (primary) - site 1 should come after site 0 */
9083
+ "1 0 X -1 0.0\n" /* mut 0: site 1, will be sorted after site 0 mutations */
9084
+ "0 0 Y -1 0.0\n" /* mut 1: site 0, will be sorted before site 1 mutations */
9085
+
9086
+ /* Test time criterion - within same site, earlier time first */
9087
+ "0 4 B -1 2.0\n" /* mut 2: site 0, node 4 (time 1.0), time 2.0 (later time)
9088
+ */
9089
+ "0 5 A -1 2.5\n" /* mut 3: site 0, node 5 (time 2.0), time 2.5 (earlier
9090
+ relative) */
9091
+
9092
+ /* Test unknown vs known times - unknown times at site 2, fall back to node_time
9093
+ sorting */
9094
+ "2 4 U2 -1\n" /* mut 4: site 2, node 4 (time 1.0), unknown time - falls back
9095
+ to node_time */
9096
+ "2 4 U3 -1\n" /* mut 5: site 2, node 4 (time 1.0), unknown time - should use
9097
+ mutation_id as tiebreaker */
9098
+ "2 5 U1 -1\n" /* mut 6: site 2, node 5 (time 2.0), unknown time - falls back
9099
+ to node_time */
9100
+
9101
+ /* Test node_time criterion - same site, same mut time, different node times */
9102
+ "0 4 D -1 1.5\n" /* mut 7: site 0, node 4 (time 1.0), mut time 1.5 */
9103
+ "0 5 C -1 2.5\n" /* mut 8: site 0, node 5 (time 2.0), mut time 2.5 - same
9104
+ mut time */
9105
+
9106
+ /* Test num_descendants criterion with mutation parent-child relationships */
9107
+ "0 2 P -1 0.0\n" /* mut 9: site 0, node 2, parent mutation (0 descendants
9108
+ initially) */
9109
+ "0 1 C1 9 0.0\n" /* mut 10: site 0, node 1, child of mut 9 (parent now has
9110
+ 1+ descendants) */
9111
+ "0 1 C2 9 0.0\n" /* mut 11: site 0, node 1, another child of mut 9 (parent
9112
+ now has 2+ descendants) */
9113
+ "0 3 Q -1 0.0\n" /* mut 12: site 0, node 3, no children (0 descendants) */
9114
+ "0 0 C3 10 0.0\n" /* mut 13: site 0, node 0, child of mut 10 (making mut 9 a
9115
+ grandparent) */
9116
+
9117
+ /* Test node and mutation_id criteria for final tiebreaking */
9118
+ "0 0 Z1 -1 0.0\n" /* mut 14: site 0, node 0, no parent, will test node+id
9119
+ ordering */
9120
+ "0 0 Z2 -1 0.0\n" ; /* mut 15: site 0, node 0, no parent, later in input =
9121
+ higher ID */
9122
+
9123
+ const char * mutations_sorted =
9124
+ /* Site 0 mutations - known times first, sorted by time */
9125
+ "0 5 A -1 2.5\n"
9126
+ "0 5 C -1 2.5\n"
9127
+ "0 4 B -1 2.0\n"
9128
+ "0 4 D -1 1.5\n"
9129
+ "0 2 P -1 0.0\n"
9130
+ "0 1 C1 4 0.0\n"
9131
+ "0 0 Y -1 0.0\n"
9132
+ "0 0 C3 5 0.0\n"
9133
+ "0 0 Z1 -1 0.0\n"
9134
+ "0 0 Z2 -1 0.0\n"
9135
+ "0 1 C2 4 0.0\n"
9136
+ "0 3 Q -1 0.0\n"
9137
+
9138
+ /* Site 1 mutations */
9139
+ "1 0 X -1 0.0\n"
9140
+
9141
+ /* Site 2 mutations - unknown times, sorted by node_time then other criteria */
9142
+ "2 5 U1 -1\n"
9143
+ "2 4 U2 -1\n"
9144
+ "2 4 U3 -1\n" ;
9145
+
9146
+ ret = tsk_table_collection_init (& tables , 0 );
9147
+ CU_ASSERT_EQUAL_FATAL (ret , 0 );
9148
+ tables .sequence_length = 1.0 ;
9149
+ parse_nodes (single_tree_ex_nodes , & tables .nodes );
9150
+ parse_edges (single_tree_ex_edges , & tables .edges );
9151
+
9152
+ parse_sites (sites , & tables .sites );
9153
+ CU_ASSERT_EQUAL_FATAL (tables .sites .num_rows , 3 );
9154
+
9155
+ parse_mutations (mutations_unsorted , & tables .mutations );
9156
+ CU_ASSERT_EQUAL_FATAL (tables .mutations .num_rows , 16 );
9157
+
9158
+ ret = tsk_table_collection_sort (& tables , NULL , 0 );
9159
+ CU_ASSERT_EQUAL_FATAL (ret , 0 );
9160
+
9161
+ tsk_table_collection_t expected ;
9162
+ ret = tsk_table_collection_init (& expected , 0 );
9163
+ CU_ASSERT_EQUAL_FATAL (ret , 0 );
9164
+ expected .sequence_length = 1.0 ;
9165
+ parse_nodes (single_tree_ex_nodes , & expected .nodes );
9166
+ parse_edges (single_tree_ex_edges , & expected .edges );
9167
+ parse_sites (sites , & expected .sites );
9168
+ parse_mutations (mutations_sorted , & expected .mutations );
9169
+
9170
+ CU_ASSERT_TRUE (tsk_mutation_table_equals (& tables .mutations , & expected .mutations , 0 ));
9171
+
9172
+ tsk_table_collection_free (& expected );
9173
+ tsk_table_collection_free (& tables );
9174
+ }
9175
+
9035
9176
static void
9036
9177
test_sort_tables_canonical_errors (void )
9037
9178
{
@@ -11608,6 +11749,7 @@ main(int argc, char **argv)
11608
11749
{ "test_sort_tables_errors" , test_sort_tables_errors },
11609
11750
{ "test_sort_tables_individuals" , test_sort_tables_individuals },
11610
11751
{ "test_sort_tables_mutation_times" , test_sort_tables_mutation_times },
11752
+ { "test_sort_tables_mutations" , test_sort_tables_mutations },
11611
11753
{ "test_sort_tables_migrations" , test_sort_tables_migrations },
11612
11754
{ "test_sort_tables_no_edge_metadata" , test_sort_tables_no_edge_metadata },
11613
11755
{ "test_sort_tables_offsets" , test_sort_tables_offsets },
0 commit comments