Skip to content

Commit

Permalink
Improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
NicChr committed Sep 20, 2024
1 parent a213f1d commit b963003
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 89 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export(unlisted_length)
export(unused_levels)
export(used_levels)
export(val_count)
export(vector_length)
export(which_)
export(which_na)
export(which_not_na)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# cheapr (Development version)

* New function `na_insert` to randomly insert `NA` values into a vector.

* New function `vector_length` as a hybrid between `length` and `nrow`.

* Limited support added for 'integer64' objects. This applies to the
`NA` related functions as well as the GCD and LCM functions.

Expand Down
4 changes: 2 additions & 2 deletions R/cpp11.R
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ cpp_numeric_to_int64 <- function(x) {
.Call(`_cheapr_cpp_numeric_to_int64`, x)
}

cpp_format_double_as_int64 <- function(x) {
.Call(`_cheapr_cpp_format_double_as_int64`, x)
cpp_format_numeric_as_int64 <- function(x) {
.Call(`_cheapr_cpp_format_numeric_as_int64`, x)
}

cpp_which_ <- function(x, invert) {
Expand Down
8 changes: 8 additions & 0 deletions R/extras.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
#' `sample_()` is an alternative to `sample()` that natively samples
#' data frame rows through `sset()`. It also does not have a special case when
#' `length(x)` is 1. \cr
#' `na_insert` inserts `NA` values randomly into your vector.
#' Useful for generating missing data. \cr
#' `vector_length` behaves mostly like `NROW()` except
#' for matrices in which it matches `length()`.
#'
#' @details
#' `intersect_()` and `setdiff_()` are faster and more efficient
Expand Down Expand Up @@ -207,6 +211,10 @@ na_insert <- function(x, n = NULL, prop = NULL){
}
x
}
#' @export
#' @rdname extras
vector_length <- cpp_vec_length

# head_ <- function(x, n = 1L){
# check_length(n, 1L)
# N <- cpp_vec_length(x)
Expand Down
8 changes: 7 additions & 1 deletion R/factors.R
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ factor_ <- function(
} else if (is_int64){
# fct_lvls <- formatC(lvls, format = "f", drop0trailing = TRUE)
# fct_lvls <- format(lvls, scientific = FALSE, trim = TRUE)
fct_lvls <- cpp_format_double_as_int64(lvls)
fct_lvls <- cpp_format_numeric_as_int64(lvls)
} else {
fct_lvls <- as.character(lvls)
}
Expand Down Expand Up @@ -222,3 +222,9 @@ levels_reorder <- function(x, order_by, decreasing = FALSE){
factor_(x, levels = ordered_levels)
}
}
# Generic factor conversion to data representation
factor_as_type <- function(x, type){
check_length(type, 1)

do.call(paste0("as.", type), list(levels(x)))[unclass(x)]
}
7 changes: 7 additions & 0 deletions man/extras.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions src/cheapr_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@
#define cheapr_is_na_int64(x) ((bool) (x == NA_INTEGER64))
#endif

#ifndef CHEAPR_INT_TO_INT64
#define CHEAPR_INT_TO_INT64(x) ((long long int) (x == NA_INTEGER ? NA_INTEGER64 : x))
#endif
#ifndef CHEAPR_DBL_TO_INT64
#define CHEAPR_DBL_TO_INT64(x) ((long long int) (x != x ? NA_INTEGER64 : x))
#endif
#ifndef CHEAPR_INT64_TO_INT
#define CHEAPR_INT64_TO_INT(x) ((int) (x == NA_INTEGER64 ? NA_INTEGER : x))
#endif
#ifndef CHEAPR_INT64_TO_DBL
#define CHEAPR_INT64_TO_DBL(x) ((double) (x == NA_INTEGER64 ? NA_REAL : x))
#endif

#ifndef CHEAPR_OMP_THRESHOLD
#define CHEAPR_OMP_THRESHOLD 100000
#endif
Expand Down
138 changes: 69 additions & 69 deletions src/cpp11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,10 @@ extern "C" SEXP _cheapr_cpp_numeric_to_int64(SEXP x) {
END_CPP11
}
// utils.cpp
SEXP cpp_format_double_as_int64(SEXP x);
extern "C" SEXP _cheapr_cpp_format_double_as_int64(SEXP x) {
SEXP cpp_format_numeric_as_int64(SEXP x);
extern "C" SEXP _cheapr_cpp_format_numeric_as_int64(SEXP x) {
BEGIN_CPP11
return cpp11::as_sexp(cpp_format_double_as_int64(cpp11::as_cpp<cpp11::decay_t<SEXP>>(x)));
return cpp11::as_sexp(cpp_format_numeric_as_int64(cpp11::as_cpp<cpp11::decay_t<SEXP>>(x)));
END_CPP11
}
// which.cpp
Expand Down Expand Up @@ -470,72 +470,72 @@ extern "C" SEXP _cheapr_cpp_which_not_na(SEXP x) {

extern "C" {
static const R_CallMethodDef CallEntries[] = {
{"_cheapr_compact_seq_data", (DL_FUNC) &_cheapr_compact_seq_data, 1},
{"_cheapr_cpp_all_na", (DL_FUNC) &_cheapr_cpp_all_na, 3},
{"_cheapr_cpp_any_na", (DL_FUNC) &_cheapr_cpp_any_na, 2},
{"_cheapr_cpp_character_compare", (DL_FUNC) &_cheapr_cpp_character_compare, 3},
{"_cheapr_cpp_col_all_na", (DL_FUNC) &_cheapr_cpp_col_all_na, 2},
{"_cheapr_cpp_col_any_na", (DL_FUNC) &_cheapr_cpp_col_any_na, 2},
{"_cheapr_cpp_col_na_counts", (DL_FUNC) &_cheapr_cpp_col_na_counts, 2},
{"_cheapr_cpp_count_val", (DL_FUNC) &_cheapr_cpp_count_val, 3},
{"_cheapr_cpp_dbl_sequence", (DL_FUNC) &_cheapr_cpp_dbl_sequence, 3},
{"_cheapr_cpp_df_col_na_counts", (DL_FUNC) &_cheapr_cpp_df_col_na_counts, 1},
{"_cheapr_cpp_df_row_na_counts", (DL_FUNC) &_cheapr_cpp_df_row_na_counts, 1},
{"_cheapr_cpp_drop_null", (DL_FUNC) &_cheapr_cpp_drop_null, 2},
{"_cheapr_cpp_format_double_as_int64", (DL_FUNC) &_cheapr_cpp_format_double_as_int64, 1},
{"_cheapr_cpp_gcd", (DL_FUNC) &_cheapr_cpp_gcd, 5},
{"_cheapr_cpp_gcd2", (DL_FUNC) &_cheapr_cpp_gcd2, 4},
{"_cheapr_cpp_gcd2_vectorised", (DL_FUNC) &_cheapr_cpp_gcd2_vectorised, 4},
{"_cheapr_cpp_int64_to_double", (DL_FUNC) &_cheapr_cpp_int64_to_double, 1},
{"_cheapr_cpp_int_sequence", (DL_FUNC) &_cheapr_cpp_int_sequence, 3},
{"_cheapr_cpp_is_na", (DL_FUNC) &_cheapr_cpp_is_na, 1},
{"_cheapr_cpp_lag", (DL_FUNC) &_cheapr_cpp_lag, 5},
{"_cheapr_cpp_lag2", (DL_FUNC) &_cheapr_cpp_lag2, 6},
{"_cheapr_cpp_lag_sequence", (DL_FUNC) &_cheapr_cpp_lag_sequence, 3},
{"_cheapr_cpp_lcm", (DL_FUNC) &_cheapr_cpp_lcm, 3},
{"_cheapr_cpp_lcm2", (DL_FUNC) &_cheapr_cpp_lcm2, 4},
{"_cheapr_cpp_lcm2_vectorised", (DL_FUNC) &_cheapr_cpp_lcm2_vectorised, 4},
{"_cheapr_cpp_lead_sequence", (DL_FUNC) &_cheapr_cpp_lead_sequence, 3},
{"_cheapr_cpp_lengths", (DL_FUNC) &_cheapr_cpp_lengths, 2},
{"_cheapr_cpp_list_as_df", (DL_FUNC) &_cheapr_cpp_list_as_df, 1},
{"_cheapr_cpp_matrix_col_na_counts", (DL_FUNC) &_cheapr_cpp_matrix_col_na_counts, 1},
{"_cheapr_cpp_matrix_row_na_counts", (DL_FUNC) &_cheapr_cpp_matrix_row_na_counts, 1},
{"_cheapr_cpp_new_list", (DL_FUNC) &_cheapr_cpp_new_list, 2},
{"_cheapr_cpp_num_na", (DL_FUNC) &_cheapr_cpp_num_na, 2},
{"_cheapr_cpp_numeric_to_int64", (DL_FUNC) &_cheapr_cpp_numeric_to_int64, 1},
{"_cheapr_cpp_r_unnested_length", (DL_FUNC) &_cheapr_cpp_r_unnested_length, 1},
{"_cheapr_cpp_row_na_counts", (DL_FUNC) &_cheapr_cpp_row_na_counts, 2},
{"_cheapr_cpp_sequence", (DL_FUNC) &_cheapr_cpp_sequence, 3},
{"_cheapr_cpp_sequence_id", (DL_FUNC) &_cheapr_cpp_sequence_id, 1},
{"_cheapr_cpp_set_abs", (DL_FUNC) &_cheapr_cpp_set_abs, 1},
{"_cheapr_cpp_set_add", (DL_FUNC) &_cheapr_cpp_set_add, 2},
{"_cheapr_cpp_set_add_attr", (DL_FUNC) &_cheapr_cpp_set_add_attr, 3},
{"_cheapr_cpp_set_add_attributes", (DL_FUNC) &_cheapr_cpp_set_add_attributes, 3},
{"_cheapr_cpp_set_ceiling", (DL_FUNC) &_cheapr_cpp_set_ceiling, 1},
{"_cheapr_cpp_set_change_sign", (DL_FUNC) &_cheapr_cpp_set_change_sign, 1},
{"_cheapr_cpp_set_divide", (DL_FUNC) &_cheapr_cpp_set_divide, 2},
{"_cheapr_cpp_set_exp", (DL_FUNC) &_cheapr_cpp_set_exp, 1},
{"_cheapr_cpp_set_floor", (DL_FUNC) &_cheapr_cpp_set_floor, 1},
{"_cheapr_cpp_set_log", (DL_FUNC) &_cheapr_cpp_set_log, 2},
{"_cheapr_cpp_set_multiply", (DL_FUNC) &_cheapr_cpp_set_multiply, 2},
{"_cheapr_cpp_set_pow", (DL_FUNC) &_cheapr_cpp_set_pow, 2},
{"_cheapr_cpp_set_rm_attr", (DL_FUNC) &_cheapr_cpp_set_rm_attr, 2},
{"_cheapr_cpp_set_rm_attributes", (DL_FUNC) &_cheapr_cpp_set_rm_attributes, 1},
{"_cheapr_cpp_set_round", (DL_FUNC) &_cheapr_cpp_set_round, 2},
{"_cheapr_cpp_set_sqrt", (DL_FUNC) &_cheapr_cpp_set_sqrt, 1},
{"_cheapr_cpp_set_subtract", (DL_FUNC) &_cheapr_cpp_set_subtract, 2},
{"_cheapr_cpp_set_trunc", (DL_FUNC) &_cheapr_cpp_set_trunc, 1},
{"_cheapr_cpp_sset_df", (DL_FUNC) &_cheapr_cpp_sset_df, 2},
{"_cheapr_cpp_sset_range", (DL_FUNC) &_cheapr_cpp_sset_range, 4},
{"_cheapr_cpp_val_replace", (DL_FUNC) &_cheapr_cpp_val_replace, 4},
{"_cheapr_cpp_vec_length", (DL_FUNC) &_cheapr_cpp_vec_length, 1},
{"_cheapr_cpp_which_", (DL_FUNC) &_cheapr_cpp_which_, 2},
{"_cheapr_cpp_which_na", (DL_FUNC) &_cheapr_cpp_which_na, 1},
{"_cheapr_cpp_which_not_na", (DL_FUNC) &_cheapr_cpp_which_not_na, 1},
{"_cheapr_cpp_which_val", (DL_FUNC) &_cheapr_cpp_which_val, 3},
{"_cheapr_cpp_window_sequence", (DL_FUNC) &_cheapr_cpp_window_sequence, 4},
{"_cheapr_is_compact_seq", (DL_FUNC) &_cheapr_is_compact_seq, 1},
{"_cheapr_r_copy", (DL_FUNC) &_cheapr_r_copy, 1},
{"_cheapr_compact_seq_data", (DL_FUNC) &_cheapr_compact_seq_data, 1},
{"_cheapr_cpp_all_na", (DL_FUNC) &_cheapr_cpp_all_na, 3},
{"_cheapr_cpp_any_na", (DL_FUNC) &_cheapr_cpp_any_na, 2},
{"_cheapr_cpp_character_compare", (DL_FUNC) &_cheapr_cpp_character_compare, 3},
{"_cheapr_cpp_col_all_na", (DL_FUNC) &_cheapr_cpp_col_all_na, 2},
{"_cheapr_cpp_col_any_na", (DL_FUNC) &_cheapr_cpp_col_any_na, 2},
{"_cheapr_cpp_col_na_counts", (DL_FUNC) &_cheapr_cpp_col_na_counts, 2},
{"_cheapr_cpp_count_val", (DL_FUNC) &_cheapr_cpp_count_val, 3},
{"_cheapr_cpp_dbl_sequence", (DL_FUNC) &_cheapr_cpp_dbl_sequence, 3},
{"_cheapr_cpp_df_col_na_counts", (DL_FUNC) &_cheapr_cpp_df_col_na_counts, 1},
{"_cheapr_cpp_df_row_na_counts", (DL_FUNC) &_cheapr_cpp_df_row_na_counts, 1},
{"_cheapr_cpp_drop_null", (DL_FUNC) &_cheapr_cpp_drop_null, 2},
{"_cheapr_cpp_format_numeric_as_int64", (DL_FUNC) &_cheapr_cpp_format_numeric_as_int64, 1},
{"_cheapr_cpp_gcd", (DL_FUNC) &_cheapr_cpp_gcd, 5},
{"_cheapr_cpp_gcd2", (DL_FUNC) &_cheapr_cpp_gcd2, 4},
{"_cheapr_cpp_gcd2_vectorised", (DL_FUNC) &_cheapr_cpp_gcd2_vectorised, 4},
{"_cheapr_cpp_int64_to_double", (DL_FUNC) &_cheapr_cpp_int64_to_double, 1},
{"_cheapr_cpp_int_sequence", (DL_FUNC) &_cheapr_cpp_int_sequence, 3},
{"_cheapr_cpp_is_na", (DL_FUNC) &_cheapr_cpp_is_na, 1},
{"_cheapr_cpp_lag", (DL_FUNC) &_cheapr_cpp_lag, 5},
{"_cheapr_cpp_lag2", (DL_FUNC) &_cheapr_cpp_lag2, 6},
{"_cheapr_cpp_lag_sequence", (DL_FUNC) &_cheapr_cpp_lag_sequence, 3},
{"_cheapr_cpp_lcm", (DL_FUNC) &_cheapr_cpp_lcm, 3},
{"_cheapr_cpp_lcm2", (DL_FUNC) &_cheapr_cpp_lcm2, 4},
{"_cheapr_cpp_lcm2_vectorised", (DL_FUNC) &_cheapr_cpp_lcm2_vectorised, 4},
{"_cheapr_cpp_lead_sequence", (DL_FUNC) &_cheapr_cpp_lead_sequence, 3},
{"_cheapr_cpp_lengths", (DL_FUNC) &_cheapr_cpp_lengths, 2},
{"_cheapr_cpp_list_as_df", (DL_FUNC) &_cheapr_cpp_list_as_df, 1},
{"_cheapr_cpp_matrix_col_na_counts", (DL_FUNC) &_cheapr_cpp_matrix_col_na_counts, 1},
{"_cheapr_cpp_matrix_row_na_counts", (DL_FUNC) &_cheapr_cpp_matrix_row_na_counts, 1},
{"_cheapr_cpp_new_list", (DL_FUNC) &_cheapr_cpp_new_list, 2},
{"_cheapr_cpp_num_na", (DL_FUNC) &_cheapr_cpp_num_na, 2},
{"_cheapr_cpp_numeric_to_int64", (DL_FUNC) &_cheapr_cpp_numeric_to_int64, 1},
{"_cheapr_cpp_r_unnested_length", (DL_FUNC) &_cheapr_cpp_r_unnested_length, 1},
{"_cheapr_cpp_row_na_counts", (DL_FUNC) &_cheapr_cpp_row_na_counts, 2},
{"_cheapr_cpp_sequence", (DL_FUNC) &_cheapr_cpp_sequence, 3},
{"_cheapr_cpp_sequence_id", (DL_FUNC) &_cheapr_cpp_sequence_id, 1},
{"_cheapr_cpp_set_abs", (DL_FUNC) &_cheapr_cpp_set_abs, 1},
{"_cheapr_cpp_set_add", (DL_FUNC) &_cheapr_cpp_set_add, 2},
{"_cheapr_cpp_set_add_attr", (DL_FUNC) &_cheapr_cpp_set_add_attr, 3},
{"_cheapr_cpp_set_add_attributes", (DL_FUNC) &_cheapr_cpp_set_add_attributes, 3},
{"_cheapr_cpp_set_ceiling", (DL_FUNC) &_cheapr_cpp_set_ceiling, 1},
{"_cheapr_cpp_set_change_sign", (DL_FUNC) &_cheapr_cpp_set_change_sign, 1},
{"_cheapr_cpp_set_divide", (DL_FUNC) &_cheapr_cpp_set_divide, 2},
{"_cheapr_cpp_set_exp", (DL_FUNC) &_cheapr_cpp_set_exp, 1},
{"_cheapr_cpp_set_floor", (DL_FUNC) &_cheapr_cpp_set_floor, 1},
{"_cheapr_cpp_set_log", (DL_FUNC) &_cheapr_cpp_set_log, 2},
{"_cheapr_cpp_set_multiply", (DL_FUNC) &_cheapr_cpp_set_multiply, 2},
{"_cheapr_cpp_set_pow", (DL_FUNC) &_cheapr_cpp_set_pow, 2},
{"_cheapr_cpp_set_rm_attr", (DL_FUNC) &_cheapr_cpp_set_rm_attr, 2},
{"_cheapr_cpp_set_rm_attributes", (DL_FUNC) &_cheapr_cpp_set_rm_attributes, 1},
{"_cheapr_cpp_set_round", (DL_FUNC) &_cheapr_cpp_set_round, 2},
{"_cheapr_cpp_set_sqrt", (DL_FUNC) &_cheapr_cpp_set_sqrt, 1},
{"_cheapr_cpp_set_subtract", (DL_FUNC) &_cheapr_cpp_set_subtract, 2},
{"_cheapr_cpp_set_trunc", (DL_FUNC) &_cheapr_cpp_set_trunc, 1},
{"_cheapr_cpp_sset_df", (DL_FUNC) &_cheapr_cpp_sset_df, 2},
{"_cheapr_cpp_sset_range", (DL_FUNC) &_cheapr_cpp_sset_range, 4},
{"_cheapr_cpp_val_replace", (DL_FUNC) &_cheapr_cpp_val_replace, 4},
{"_cheapr_cpp_vec_length", (DL_FUNC) &_cheapr_cpp_vec_length, 1},
{"_cheapr_cpp_which_", (DL_FUNC) &_cheapr_cpp_which_, 2},
{"_cheapr_cpp_which_na", (DL_FUNC) &_cheapr_cpp_which_na, 1},
{"_cheapr_cpp_which_not_na", (DL_FUNC) &_cheapr_cpp_which_not_na, 1},
{"_cheapr_cpp_which_val", (DL_FUNC) &_cheapr_cpp_which_val, 3},
{"_cheapr_cpp_window_sequence", (DL_FUNC) &_cheapr_cpp_window_sequence, 4},
{"_cheapr_is_compact_seq", (DL_FUNC) &_cheapr_is_compact_seq, 1},
{"_cheapr_r_copy", (DL_FUNC) &_cheapr_r_copy, 1},
{NULL, NULL, 0}
};
}
Expand Down
5 changes: 0 additions & 5 deletions src/gcd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ template<typename T> T cpp_sign(T x) {
return (x > 0) - (x < 0);
}

#define CHEAPR_INT_TO_INT64(x) ((long long int) (x == NA_INTEGER ? NA_INTEGER64 : x))
#define CHEAPR_DBL_TO_INT64(x) ((long long int) (x != x ? NA_INTEGER64 : x))
#define CHEAPR_INT64_TO_INT(x) ((int) (x == NA_INTEGER64 ? NA_INTEGER : x))
#define CHEAPR_INT64_TO_DBL(x) ((double) (x == NA_INTEGER64 ? NA_REAL : x))

[[cpp11::register]]
double cpp_gcd2(double x, double y, double tol, bool na_rm){
double zero = 0.0;
Expand Down
64 changes: 52 additions & 12 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,24 +140,64 @@ std::string string_format( const std::string& format, Args ... args){
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}

// Simple format large integers
// same as `format(x, scientific = FALSE, trim = TRUE)`

[[cpp11::register]]
SEXP cpp_format_double_as_int64(SEXP x){
SEXP cpp_format_numeric_as_int64(SEXP x){
R_xlen_t n = Rf_xlength(x);

SEXP out = Rf_protect(Rf_allocVector(STRSXP, n));
double *p_x = REAL(x);
SEXP na_char = Rf_protect(Rf_mkChar("NA"));
for (R_xlen_t i = 0; i < n; ++i){
if (cheapr_is_na_dbl(p_x[i])){
SET_STRING_ELT(out, i, na_char);
switch (TYPEOF(x)){
case INTSXP: {
SEXP out = Rf_protect(Rf_allocVector(STRSXP, n));
int *p_x = INTEGER(x);

for (R_xlen_t i = 0; i < n; ++i){
if (cheapr_is_na_int(p_x[i])){
SET_STRING_ELT(out, i, NA_STRING);
} else {
long long temp = p_x[i];
std::string s = string_format("%lld", temp);
SET_STRING_ELT(out, i, Rf_mkChar(s.c_str()));
}
}
Rf_unprotect(1);
return out;
}
case REALSXP: {
SEXP out = Rf_protect(Rf_allocVector(STRSXP, n));
if (is_int64(x)){
long long *p_x = INTEGER64_PTR(x);

for (R_xlen_t i = 0; i < n; ++i){
if (cheapr_is_na_int64(p_x[i])){
SET_STRING_ELT(out, i, NA_STRING);
} else {
long long temp = p_x[i];
std::string s = string_format("%lld", temp);
SET_STRING_ELT(out, i, Rf_mkChar(s.c_str()));
}
}
} else {
long long temp = p_x[i];
std::string s = string_format("%lld", temp);
SET_STRING_ELT(out, i, Rf_mkChar(s.c_str()));
double *p_x = REAL(x);

for (R_xlen_t i = 0; i < n; ++i){
if (cheapr_is_na_dbl(p_x[i])){
SET_STRING_ELT(out, i, NA_STRING);
} else {
long long temp = p_x[i];
std::string s = string_format("%lld", temp);
SET_STRING_ELT(out, i, Rf_mkChar(s.c_str()));
}
}
}
Rf_unprotect(1);
return out;
}
default: {
Rf_error("%s cannot handle an object of type %s", __func__, Rf_type2char(TYPEOF(x)));
}
}
Rf_unprotect(2);
return out;
}

// Potentially useful for rolling calculations
Expand Down

0 comments on commit b963003

Please sign in to comment.