Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 135 additions & 11 deletions c/tests/test_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

#include "testlib.h"
#include <math.h>
#include <tskit/stats.h>

#include <unistd.h>
Expand Down Expand Up @@ -602,7 +603,7 @@ verify_pair_coalescence_rates(tsk_treeseq_t *ts)
epochs[B] = DBL_MAX;
ret = tsk_treeseq_pair_coalescence_rates(ts, P, sample_set_sizes, sample_sets, I,
index_tuples, T, breakpoints, B, node_bin_map, epochs, 0, C);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_TIME_WINDOWS);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_TIME_WINDOWS_END);
epochs[B] = INFINITY;

node_bin_map[0] = (tsk_id_t) B;
Expand Down Expand Up @@ -868,6 +869,84 @@ verify_one_way_stat_func_errors(tsk_treeseq_t *ts, one_way_sample_stat_method *m
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_WINDOWS);
}

// Temporary definition for time_windows in tsk_treeseq_allele_frequency_spectrum
typedef int one_way_sample_stat_method_tw(const tsk_treeseq_t *self,
tsk_size_t num_sample_sets, const tsk_size_t *sample_set_sizes,
const tsk_id_t *sample_sets, tsk_size_t num_windows, const double *windows,
tsk_size_t num_time_windows, const double *time_windows, tsk_flags_t options,
double *result);

// Temporary duplicate for time-windows-having methods
static void
verify_one_way_stat_func_errors_tw(
tsk_treeseq_t *ts, one_way_sample_stat_method_tw *method)
{
int ret;
tsk_id_t num_nodes = (tsk_id_t) tsk_treeseq_get_num_nodes(ts);
tsk_id_t samples[] = { 0, 1, 2, 3 };
tsk_size_t sample_set_sizes = 4;
double windows[] = { 0, 0, 0 };
double time_windows[] = { -1, 0.5, INFINITY };
double result;

ret = method(ts, 0, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_INSUFFICIENT_SAMPLE_SETS);

samples[0] = TSK_NULL;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_NODE_OUT_OF_BOUNDS);
samples[0] = -10;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_NODE_OUT_OF_BOUNDS);
samples[0] = num_nodes;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_NODE_OUT_OF_BOUNDS);
samples[0] = num_nodes + 1;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_NODE_OUT_OF_BOUNDS);

samples[0] = num_nodes - 1;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_SAMPLES);

samples[0] = 1;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_DUPLICATE_SAMPLE);

samples[0] = 0;
sample_set_sizes = 0;
ret = method(ts, 1, &sample_set_sizes, samples, 0, NULL, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_EMPTY_SAMPLE_SET);

sample_set_sizes = 4;
/* Window errors */
ret = method(ts, 1, &sample_set_sizes, samples, 0, windows, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_NUM_WINDOWS);

ret = method(ts, 1, &sample_set_sizes, samples, 2, windows, 0, NULL, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_WINDOWS);

/* Time window errors */
ret = method(
ts, 1, &sample_set_sizes, samples, 0, NULL, 0, time_windows, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_TIME_WINDOWS_DIM);

ret = method(
ts, 1, &sample_set_sizes, samples, 0, NULL, 2, time_windows, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_TIME_WINDOWS);

time_windows[0] = 0.1;
ret = method(
ts, 1, &sample_set_sizes, samples, 0, NULL, 2, time_windows, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_TIME_WINDOWS);

time_windows[0] = 0;
time_windows[1] = 0;
ret = method(
ts, 1, &sample_set_sizes, samples, 0, NULL, 2, time_windows, 0, &result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_BAD_TIME_WINDOWS);
}

static void
verify_two_way_stat_func_errors(
tsk_treeseq_t *ts, general_sample_stat_method *method, tsk_flags_t options)
Expand Down Expand Up @@ -1180,6 +1259,7 @@ verify_afs(tsk_treeseq_t *ts)
int ret;
tsk_size_t n = tsk_treeseq_get_num_samples(ts);
tsk_size_t sample_set_sizes[2];
double time_windows[] = { 0, 1 };
const tsk_id_t *samples = tsk_treeseq_get_samples(ts);
double *result = tsk_malloc(n * n * sizeof(*result));

Expand All @@ -1188,23 +1268,28 @@ verify_afs(tsk_treeseq_t *ts)
sample_set_sizes[0] = n - 2;
sample_set_sizes[1] = 2;
ret = tsk_treeseq_allele_frequency_spectrum(
ts, 2, sample_set_sizes, samples, 0, NULL, 0, result);
ts, 2, sample_set_sizes, samples, 0, NULL, 0, NULL, 0, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_allele_frequency_spectrum(
ts, 2, sample_set_sizes, samples, 0, NULL, TSK_STAT_POLARISED, result);
ts, 2, sample_set_sizes, samples, 0, NULL, 0, NULL, TSK_STAT_POLARISED, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_allele_frequency_spectrum(ts, 2, sample_set_sizes, samples, 0,
NULL, TSK_STAT_POLARISED | TSK_STAT_SPAN_NORMALISE, result);
NULL, 0, NULL, TSK_STAT_POLARISED | TSK_STAT_SPAN_NORMALISE, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_allele_frequency_spectrum(ts, 2, sample_set_sizes, samples, 0,
NULL, TSK_STAT_BRANCH | TSK_STAT_POLARISED | TSK_STAT_SPAN_NORMALISE, result);
NULL, 0, NULL, TSK_STAT_BRANCH | TSK_STAT_POLARISED | TSK_STAT_SPAN_NORMALISE,
result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_allele_frequency_spectrum(ts, 2, sample_set_sizes, samples, 0,
NULL, TSK_STAT_BRANCH | TSK_STAT_SPAN_NORMALISE, result);
NULL, 0, NULL, TSK_STAT_BRANCH | TSK_STAT_SPAN_NORMALISE, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_allele_frequency_spectrum(ts, 2, sample_set_sizes, samples, 0,
NULL, 1, time_windows, TSK_STAT_BRANCH | TSK_STAT_SPAN_NORMALISE, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

free(result);
Expand Down Expand Up @@ -2398,21 +2483,26 @@ test_paper_ex_afs_errors(void)
tsk_size_t sample_set_sizes[] = { 2, 2 };
tsk_id_t samples[] = { 0, 1, 2, 3 };
double result[10]; /* not thinking too hard about the actual value needed */
double time_windows[] = { 0, 1 };
int ret;

tsk_treeseq_from_text(&ts, 10, paper_ex_nodes, paper_ex_edges, NULL, paper_ex_sites,
paper_ex_mutations, paper_ex_individuals, NULL, 0);

verify_one_way_stat_func_errors(&ts, tsk_treeseq_allele_frequency_spectrum);
verify_one_way_stat_func_errors_tw(&ts, tsk_treeseq_allele_frequency_spectrum);

ret = tsk_treeseq_allele_frequency_spectrum(
&ts, 2, sample_set_sizes, samples, 0, NULL, TSK_STAT_NODE, result);
&ts, 2, sample_set_sizes, samples, 0, NULL, 0, NULL, TSK_STAT_NODE, result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_UNSUPPORTED_STAT_MODE);

ret = tsk_treeseq_allele_frequency_spectrum(&ts, 2, sample_set_sizes, samples, 0,
NULL, TSK_STAT_BRANCH | TSK_STAT_SITE, result);
NULL, 0, NULL, TSK_STAT_BRANCH | TSK_STAT_SITE, result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_MULTIPLE_STAT_MODES);

ret = tsk_treeseq_allele_frequency_spectrum(&ts, 2, sample_set_sizes, samples, 0,
NULL, 1, time_windows, TSK_STAT_SITE, result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_UNSUPPORTED_STAT_MODE);

tsk_treeseq_free(&ts);
}

Expand All @@ -2430,14 +2520,14 @@ test_paper_ex_afs(void)
/* we have two singletons and one tripleton */

ret = tsk_treeseq_allele_frequency_spectrum(
&ts, 1, sample_set_sizes, samples, 0, NULL, 0, result);
&ts, 1, sample_set_sizes, samples, 0, NULL, 0, NULL, 0, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_EQUAL_FATAL(result[0], 0);
CU_ASSERT_EQUAL_FATAL(result[1], 3.0);
CU_ASSERT_EQUAL_FATAL(result[2], 0);

ret = tsk_treeseq_allele_frequency_spectrum(
&ts, 1, sample_set_sizes, samples, 0, NULL, TSK_STAT_POLARISED, result);
&ts, 1, sample_set_sizes, samples, 0, NULL, 0, NULL, TSK_STAT_POLARISED, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_EQUAL_FATAL(result[0], 0);
CU_ASSERT_EQUAL_FATAL(result[1], 2.0);
Expand Down Expand Up @@ -2465,6 +2555,39 @@ test_paper_ex_divergence_matrix(void)
tsk_treeseq_free(&ts);
}

static void
test_unary_ex_afs(void)
{
tsk_treeseq_t ts;
tsk_id_t samples[] = { 0, 2, 3 };
tsk_size_t sample_set_sizes[] = { 3, 0 };
double result[25];
int ret;

tsk_treeseq_from_text(&ts, 100, unary_ex_nodes, unary_ex_edges, NULL, unary_ex_sites,
unary_ex_mutations, NULL, NULL, 0);
/* we have a singleton and a doubleton */

ret = tsk_treeseq_allele_frequency_spectrum(
&ts, 1, sample_set_sizes, samples, 0, NULL, 0, NULL, TSK_STAT_POLARISED, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_EQUAL_FATAL(result[0], 0);
CU_ASSERT_EQUAL_FATAL(result[1], 1.0);
CU_ASSERT_EQUAL_FATAL(result[2], 1.0);
CU_ASSERT_EQUAL_FATAL(result[3], 0.0);

ret = tsk_treeseq_allele_frequency_spectrum(&ts, 1, sample_set_sizes, samples, 0,
NULL, 0, NULL, TSK_STAT_BRANCH | TSK_STAT_POLARISED, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_TRUE_FATAL(result[0] > 0);
CU_ASSERT_TRUE_FATAL(result[1] > 0);
CU_ASSERT_TRUE_FATAL(result[2] > 0);
CU_ASSERT_EQUAL_FATAL(result[3], 0.0);

verify_afs(&ts);
tsk_treeseq_free(&ts);
}

static void
test_nonbinary_ex_ld(void)
{
Expand Down Expand Up @@ -4000,6 +4123,7 @@ main(int argc, char **argv)
{ "test_paper_ex_afs", test_paper_ex_afs },
{ "test_paper_ex_divergence_matrix", test_paper_ex_divergence_matrix },

{ "test_unary_ex_afs", test_unary_ex_afs },
{ "test_nonbinary_ex_ld", test_nonbinary_ex_ld },
{ "test_nonbinary_ex_mean_descendants", test_nonbinary_ex_mean_descendants },
{ "test_nonbinary_ex_genealogical_nearest_neighbours",
Expand Down
12 changes: 6 additions & 6 deletions c/tests/test_trees.c
Original file line number Diff line number Diff line change
Expand Up @@ -5543,9 +5543,9 @@ test_unary_multi_tree(void)
{
// clang-format off
tsk_id_t parents[] = {
6, 5, 7, 5, TSK_NULL, 6, 8, 8, TSK_NULL,
6, 5, 4, 4, 5, 6, 8, TSK_NULL, TSK_NULL,
7, 5, 4, 4, 5, 7, TSK_NULL, TSK_NULL, TSK_NULL,
6, 5, 7, 5, TSK_NULL, 6, 8, 8, TSK_NULL, 5,
6, 5, 4, 4, 5, 6, 8, TSK_NULL, TSK_NULL, 5,
7, 5, 4, 4, 5, 7, TSK_NULL, TSK_NULL, TSK_NULL, 5,
};
// clang-format on
tsk_treeseq_t ts;
Expand Down Expand Up @@ -8026,13 +8026,13 @@ test_time_uncalibrated(void)
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_allele_frequency_spectrum(
&ts2, 2, sample_set_sizes, samples, 0, NULL, TSK_STAT_SITE, result);
&ts2, 2, sample_set_sizes, samples, 0, NULL, 0, NULL, TSK_STAT_SITE, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);
ret = tsk_treeseq_allele_frequency_spectrum(
&ts2, 2, sample_set_sizes, samples, 0, NULL, TSK_STAT_BRANCH, result);
&ts2, 2, sample_set_sizes, samples, 0, NULL, 0, NULL, TSK_STAT_BRANCH, result);
CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_TIME_UNCALIBRATED);
ret = tsk_treeseq_allele_frequency_spectrum(&ts2, 2, sample_set_sizes, samples, 0,
NULL, TSK_STAT_BRANCH | TSK_STAT_ALLOW_TIME_UNCALIBRATED, result);
NULL, 0, NULL, TSK_STAT_BRANCH | TSK_STAT_ALLOW_TIME_UNCALIBRATED, result);
CU_ASSERT_EQUAL_FATAL(ret, 0);

sigma = tsk_calloc(tsk_treeseq_get_num_nodes(&ts2), sizeof(double));
Expand Down
56 changes: 31 additions & 25 deletions c/tests/testlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,19 @@ const char *paper_ex_individuals = "0 0.2,1.5 -1,-1\n"

/*** An example of a nonbinary tree sequence ***/
/*
0.41┊ 12 ┊ 12 ┊
┊ ┃ ┊ ┃ ┊
0.28┊ ┃ ┊ ┃ ┊
┊ ┃ ┊ ┃ ┊
0.13┊ 10 ┊ 10 ┊
┊ ┏━╋━┓ ┊ ┏┻┓ ┊
0.07┊ ┃ ┃ ┃ ┊ ┃ ┃ ┊
┊ ┃ ┃ ┃ ┊ ┃ ┃ ┊
0.01┊ ┃ ┃ ┃ ┊ ┃ ┃ ┊
┊ ┃ ┃ ┃ ┊ ┃ ┃ ┊
0.00┊ 0 1 2 3 4 5 7 6 ┊ 0 1 2 3 4 7 5 6 ┊
0.41┊ 12 ┊ 12 ┊
┊ ┏━━┻━━┓ ┊ ┏━┻━━┓ ┊
0.28┊ ┃ ┃ ┊ 11 ┃ ┊
┊ ┃ ┃ ┊ ┏━┻━┓ ┃ ┊
0.13┊ ┃ 10 ┊ ┃ ┃ 10 ┊
┊ ┃ ┏━╋━┓ ┊ ┃ ┃ ┏┻┓ ┊
0.07┊ 9 ┃ ┃ ┃ ┊ 9 ┃ ┃ ┃ ┊
┊ ┏━━┻━┓ ┃ ┃ ┃ ┊ ┏━━┻━┓ ┃ ┃ ┃ ┊
0.01┊ 8 ┃ ┃ ┃ ┃ ┊ 8 ┃ ┃ ┃ ┃ ┊
┊ ┏━┳┻┳━┓ ┃ ┃ ┃ ┃ ┊ ┏━┳┻┳━┓ ┃ ┃ ┃ ┃ ┊
0.00┊ 0 1 2 3 6 4 5 7 ┊ 0 1 2 3 6 5 4 7 ┊
0 17 100

*/
const char *nonbinary_ex_nodes = "1 0 0 -1\n"
"1 0 0 -1\n"
Expand Down Expand Up @@ -136,20 +138,21 @@ const char *nonbinary_ex_sites = "1 0\n"
const char *nonbinary_ex_mutations = "0 2 1\n"
"1 11 1";

/*** An example of a tree sequence with unary nodes. ***/
/*** An example of a tree sequence with unary nodes
* and also a non-sample leaf (node 9). ***/
/*
0.25┊ 8 ┊ 8 ┊ ┊
┊ ┏━┻━┓ ┊ ┃ ┊ ┊
0.20┊ ┃ 7 ┊ ┃ ┊ 7 ┊
┊ ┃ ┃ ┊ ┃ ┊ ┏━┻━
0.17┊ 6 ┃ ┊ 6 ┊ ┃
┊ ┏━┻┓ ┃ ┊ ┏━┻━┓ ┊ ┃
0.09┊ ┃ 5 ┃ ┊ ┃ 5 ┊ ┃ 5
┊ ┃ ┏┓ ┃ ┊ ┃ ┏━┻┓ ┊ ┃ ┏━┻┓
0.07┊ ┃ ┃ ┃ ┃ ┊ ┃ ┃ 4 ┊ ┃ ┃ 4 ┊
┊ ┃ ┃ ┃ ┃ ┊ ┃ ┃ ┏┻┓ ┊ ┃ ┃ ┏┻┓ ┊
0.00┊ 0 1 3 2 ┊ 0 1 2 3 ┊ 0 1 2 3 ┊
0.00 2.00 7.00 10.00
0.25┊ 8 ┊ 8 ┊ ┊
┊ ┏━━┻━━┓ ┊ ┃ ┊ ┊
0.20┊ ┃ 7 ┊ ┃ ┊ 7
┊ ┃ ┃ ┊ ┃ ┊ ┏━┻━━┓ ┊
0.17┊ 6 ┃ ┊ 6 ┊ ┃ ┃ ┊
┊ ┏━┻ ┃ ┊ ┏━┻━ ┊ ┃ ┃ ┊
0.09┊ ┃ 5 ┃ ┊ ┃ 5 ┊ ┃ 5 ┊
┊ ┃ ┏━╋━┓ ┃ ┊ ┃ ┏━━╋━━┓ ┊ ┃ ┏━━╋━━┓ ┊
0.07┊ ┃ ┃ ┃ ┃ ┊ ┃ ┃ 4 ┊ ┃ ┃ 4 ┃ ┊
┊ ┃ ┃ ┃ ┃ ┊ ┃ ┃ ┏┻┓ ┊ ┃ ┃ ┏┻┓ ┃ ┊
0.00┊ 0 1 3 9 2 ┊ 0 1 2 3 9 ┊ 0 1 2 3 9 ┊ 0 1 2 3 ┊
0 2 7 10 100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a non-sample leaf to this example, which we needed to get better coverage.

*/
const char *unary_ex_nodes = "1 0 0 -1\n"
"1 0 0 -1\n"
Expand All @@ -159,11 +162,13 @@ const char *unary_ex_nodes = "1 0 0 -1\n"
"0 0.090 0 -1\n"
"0 0.170 0 -1\n"
"0 0.202 0 -1\n"
"0 0.253 0 -1\n";
"0 0.253 0 -1\n"
"0 0 0 -1\n";
const char *unary_ex_edges = "2 10 4 2,3\n"
"0 10 5 1\n"
"0 2 5 3\n"
"2 10 5 4\n"
"0 10 5 9\n"
"0 7 6 0,5\n"
"7 10 7 0\n"
"0 2 7 2\n"
Expand All @@ -177,6 +182,7 @@ const char *unary_ex_sites = "1.0 0\n"
"8.5 0\n";
const char *unary_ex_mutations = "0 2 1\n"
"1 6 1\n"
"1 9 0\n"
"2 5 1\n";

/* An example of a simple tree sequence with multiple marginal trees. */
Expand Down
6 changes: 5 additions & 1 deletion c/tskit/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,13 @@ tsk_strerror_internal(int err)
"(TSK_ERR_BAD_SAMPLE_PAIR_TIMES)";
break;
case TSK_ERR_BAD_TIME_WINDOWS:
ret = "Time windows must be strictly increasing and end at infinity. "
ret = "Time windows must start at zero and be strictly increasing. "
"(TSK_ERR_BAD_TIME_WINDOWS)";
break;
case TSK_ERR_BAD_TIME_WINDOWS_END:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we needed to disambiguate the various notions of "bad" as different stats have different requirements now

ret = "Time windows must end at infinity for this method. "
"(TSK_ERR_BAD_TIME_WINDOWS_END)";
break;
case TSK_ERR_BAD_NODE_TIME_WINDOW:
ret = "Node time does not fall within assigned time window. "
"(TSK_ERR_BAD_NODE_TIME_WINDOW)";
Expand Down
8 changes: 6 additions & 2 deletions c/tskit/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,13 +746,17 @@ Sample times do not all equal the start of first time window
*/
#define TSK_ERR_BAD_SAMPLE_PAIR_TIMES -923
/**
Time windows are not strictly increasing ending at infinity
Time windows are not strictly increasing
*/
#define TSK_ERR_BAD_TIME_WINDOWS -924
/**
Time windows do not end at infinity
*/
#define TSK_ERR_BAD_TIME_WINDOWS_END -925
/**
Node time does not fall within assigned time window
*/
#define TSK_ERR_BAD_NODE_TIME_WINDOW -925
#define TSK_ERR_BAD_NODE_TIME_WINDOW -926
/** @} */

/**
Expand Down
Loading
Loading