prioritizr / prioritizr
Showing 9 of 197 files from the diff.
Other files ignored by Codecov
docs/index.html has changed.
docs/pkgdown.yml has changed.
man/importance.Rd has changed.
R/importance.R has changed.
docs/404.html has changed.
docs/authors.html has changed.
README.md has changed.
NEWS.md has changed.
README.Rmd has changed.
DESCRIPTION has changed.
cran-comments.md has changed.

@@ -41,8 +41,10 @@
Loading
41 41
#' @export
42 42
simulate_data <- function(x, n, model, transform = identity, ...) {
43 43
  # assert valid arguments
44 +
  #nocov start
44 45
  if (!requireNamespace("RandomFields", quietly = TRUE))
45 46
    stop("the \"RandomFields\" package needs to be installed to simulate data")
47 +
  #nocov end
46 48
  assertthat::assert_that(
47 49
    inherits(x, "RasterLayer"),
48 50
    assertthat::is.number(n),

@@ -23,6 +23,9 @@
Loading
23 23
// calculate euclidean distance
24 24
double distance(double, double, double, double);
25 25
26 +
// check if two numbers are approximately equal
27 +
bool approx_equal(double x, double y);
28 +
26 29
// convert object to string
27 30
template<typename T>
28 31
inline std::string num2str(T number, int precision=10) {

@@ -85,10 +85,12 @@
Loading
85 85
                          assertthat::noNA(first_feasible),
86 86
                          requireNamespace("lpsymphony", quietly = TRUE))
87 87
  # throw warning about bug in lpsymphony
88 +
  #nocov start
88 89
  if (utils::packageVersion("lpsymphony") <= as.package_version("1.4.1"))
89 90
    warning(paste0("The solution may be incorrect due to a bug in ",
90 91
                   "lpsymphony, please verify that it is correct, ",
91 92
                   "or use a different solver to generate solutions"))
93 +
  #nocov end
92 94
  # add solver
93 95
  x$add_solver(pproto(
94 96
    "LpsymphonySolver",
@@ -142,10 +144,14 @@
Loading
142 144
      rt <- system.time({
143 145
        x <- do.call(lpsymphony::lpsymphony_solve_LP, append(model, p))
144 146
      })
147 +
      # manually return NULL to indicate error if no solution
148 +
      #nocov start
145 149
      if (is.null(x$solution) ||
146 150
          names(x$status) %in% c("TM_NO_SOLUTION", "PREP_NO_SOLUTION"))
147 151
        return(NULL)
152 +
      #nocov end
148 153
      # fix floating point issues with binary variables
154 +
      #nocov start
149 155
      b <- which(model$types == "B")
150 156
      if (any(x$solution[b] > 1)) {
151 157
        if (max(x$solution[b]) < 1.01) {
@@ -161,6 +167,7 @@
Loading
161 167
          stop("infeasible solution returned, try relaxing solver parameters")
162 168
        }
163 169
      }
170 +
      #nocov end
164 171
      # fix floating point issues with continuous variables
165 172
      cv <- which(model$types == "C")
166 173
      x$solution[cv] <-

@@ -104,7 +104,7 @@
Loading
104 104
#' @return `logical` value indicating if all checks are passed
105 105
#'   successfully.
106 106
#'
107 -
#' @seealso [problem()], [solve()], <https://www.gurobi.com/documentation/8.1/refman/numerics_gurobi_guidelines.html>, <http://files.gurobi.com/Numerics.pdf>.
107 +
#' @seealso [problem()], [solve()], <https://www.gurobi.com/documentation/9.1/refman/numerics_gurobi_guidelines.html>.
108 108
#'
109 109
#' @examples
110 110
#' # set seed for reproducibility
@@ -251,9 +251,6 @@
Loading
251 251
    out <- FALSE
252 252
    n <- x$row_ids()[r]
253 253
    ### throw warnings
254 -
    if ("n" %in% n)
255 -
    warning("number of neighbors required is very high (> 1e+6)",
256 -
              immediate. = TRUE)
257 254
    if ("budget" %in% n)
258 255
      warning("budget is very high (> 1e+6)",
259 256
              immediate. = TRUE)
@@ -275,6 +272,12 @@
Loading
275 272
    cn <- cn[r]
276 273
    rn <- rn[r]
277 274
    ### throw warnings
275 +
    if ("n" %in% rn) {
276 +
      warning("number of neighbors required is very high (> 1e+6)",
277 +
                immediate. = TRUE)
278 +
      cn <- cn[rn != "n"]
279 +
      rn <- rn[rn != "n"]
280 +
    }
278 281
    if ("budget" %in% rn)
279 282
      warning(paste("planning units with very high (> 1e+6) or very low",
280 283
                    "(< 1e-6) non-zero values"),

@@ -5,10 +5,6 @@
Loading
5 5
#'
6 6
#' Calculate importance scores for planning units selected in
7 7
#' a solution following Ferrier *et al.* (2000).
8 -
#' **Please note that
9 -
#' the mathematical formulation for computing these scores needs verification,
10 -
#' and so this functionality should be considered experimental at this point in
11 -
#' time.**
12 8
#'
13 9
#' @inheritParams eval_replacement_importance
14 10
#'
@@ -18,8 +14,19 @@
Loading
18 14
#' calculated as the sum of the scores for each feature.
19 15
#' Note that this function only works for problems with
20 16
#' a minimum set objective and a single zone.
21 -
#' It will throw an error for other types of problems that do not meet
22 -
#' this specification.
17 +
#' It will throw an error for problems that do not meet this criteria.
18 +
#'
19 +
#' **Please note that
20 +
#' the mathematical formulation for computing these scores needs verification,
21 +
#' and so this functionality should be considered experimental at this point in
22 +
#' time.**
23 +
#'
24 +
#' @section Notes:
25 +
#' In previous versions, the documentation for this function had a warning
26 +
#' indicating that the mathematical formulation for this function required
27 +
#' verification. The mathematical formulation for this function has since
28 +
#' been corrected and verified, so now this function is recommended
29 +
#' for general use.
23 30
#'
24 31
#' @inheritSection eval_cost_summary Solution format
25 32
#'

@@ -16,15 +16,15 @@
Loading
16 16
  /// calculate rij^2
17 17
  arma::sp_mat rij2 = rij;
18 18
  for (auto itr = rij2.begin(); itr != rij2.end(); ++itr)
19 -
    *itr = std::pow(*itr, 2);
19 +
    *itr = Pow<2>(*itr);
20 20
  /// calculate row sums of rij
21 -
  std::vector<double> sum_feature_amount(n_f, 0.0);
21 +
  std::vector<double> sum_feat_amount(n_f, 0.0);
22 22
  for (std::size_t i = 0; i != n_f; ++i)
23 -
    sum_feature_amount[i] = arma::accu(rij.row(i));
23 +
    sum_feat_amount[i] = arma::accu(rij.row(i));
24 24
  /// calculate row sums of rij^2
25 -
  std::vector<double> sum_sq_feature_amount(n_f, 0.0);
25 +
  std::vector<double> sum_sq_feat_amount(n_f, 0.0);
26 26
  for (std::size_t i = 0; i != n_f; ++i)
27 -
    sum_sq_feature_amount[i] = arma::accu(rij2.row(i));
27 +
    sum_sq_feat_amount[i] = arma::accu(rij2.row(i));
28 28
29 29
  // main processing
30 30
  std::size_t i;
@@ -35,7 +35,7 @@
Loading
35 35
    out(i, j) =
36 36
      calculate_feat_unit_irrep_value(
37 37
        n_pu, portfolio_size, mult, wt_include, wt_exclude, rij(i, j),
38 -
        targets[i], sum_feature_amount[i], sum_sq_feature_amount[i]);
38 +
        targets[i], sum_feat_amount[i], sum_sq_feat_amount[i]);
39 39
  }
40 40
41 41
  // return result
@@ -43,36 +43,51 @@
Loading
43 43
}
44 44
45 45
double calculate_feat_unit_irrep_value(
46 -
  double n_pu, double portfolio_size, double mult, double wt_include,
47 -
  double wt_exclude, double feat_amount, double feat_target,
48 -
  double feat_sum_amount, double feat_sum_amount_sqr) {
46 +
  double n_pu,
47 +
  double portfolio_size,
48 +
  double mult,
49 +
  double wt_include,
50 +
  double wt_exclude,
51 +
  double feat_amount,
52 +
  double feat_target,
53 +
  double sum_feat_amount,
54 +
  double sum_sq_feat_amount) {
49 55
  // init
50 -
  double feat_amount_sqr = std::pow(feat_amount, 2);
51 -
  feat_sum_amount = (feat_sum_amount - feat_amount) * mult;
52 -
  feat_sum_amount_sqr = (feat_sum_amount_sqr - feat_amount_sqr) * mult;
53 -
  double mean_feat_amount_per_pu = feat_sum_amount / n_pu;
56 +
  double feat_amount_sq = Pow<2>(feat_amount);
57 +
  sum_feat_amount = (sum_feat_amount - feat_amount) * mult;
58 +
  sum_sq_feat_amount = (sum_sq_feat_amount - feat_amount_sq) * mult;
59 +
  double mean_feat_amount_per_pu = sum_feat_amount / n_pu;
54 60
55 61
  // intermediate calculations
56 62
  double stdev = calculate_standard_dev(
57 -
    feat_sum_amount, feat_sum_amount_sqr, n_pu);
63 +
    sum_feat_amount, sum_sq_feat_amount, n_pu);
64 +
58 65
  double rx_removed = calculate_rx_removed(
59 -
    n_pu, portfolio_size, stdev, feat_amount, feat_target,
60 -
    mean_feat_amount_per_pu, feat_sum_amount);
66 +
    n_pu, portfolio_size, stdev, feat_amount,
67 +
    feat_target, mean_feat_amount_per_pu, sum_feat_amount);
68 +
61 69
  double rx_included = calculate_rx_included(
62 -
    n_pu, portfolio_size, stdev, feat_amount, feat_target,
63 -
    mean_feat_amount_per_pu);
70 +
    n_pu, portfolio_size, stdev, feat_amount,
71 +
    feat_target, mean_feat_amount_per_pu);
72 +
64 73
  double rx_excluded = calculate_rx_excluded(
65 -
    n_pu, portfolio_size, stdev, feat_amount, feat_target, feat_sum_amount,
66 -
    mean_feat_amount_per_pu);
74 +
    n_pu, portfolio_size, stdev, feat_amount,
75 +
    feat_target, sum_feat_amount, mean_feat_amount_per_pu);
67 76
68 -
  // calculate irreplaceability
77 +
  // calculate the irreplaceability value
69 78
  double irrep_value;
70 -
  if ((rx_included + rx_excluded) < 1.0e-15) {
71 -
      irrep_value = 0.0;
79 +
  if (approx_equal(rx_included + rx_excluded, 0.0)) {
80 +
    irrep_value = 0.0;
72 81
  } else {
73 -
    irrep_value = calculate_irr_feature(
74 -
      wt_include, wt_exclude, rx_removed, rx_included, rx_excluded,
75 -
      feat_amount);
82 +
    if (approx_equal(rx_included, 0.0) & (feat_amount > 1.0e-15)) {
83 +
      rx_included = 1.0;
84 +
    }
85 +
    if (approx_equal(rx_included + rx_excluded, 0.0)) {
86 +
      irrep_value = 0.0;
87 +
    } else {
88 +
      irrep_value = ((rx_included - rx_removed) * wt_include) /
89 +
        (rx_included * wt_include + rx_excluded * wt_exclude);
90 +
    }
76 91
  }
77 92
78 93
  // return result
@@ -80,39 +95,32 @@
Loading
80 95
}
81 96
82 97
double calculate_standard_dev(
83 -
  double feat_sum_amount, double feat_sum_amount_sqr, double n_pu) {
84 -
    return std::sqrt(feat_sum_amount_sqr -
85 -
                     ((std::pow(feat_sum_amount, 2)) / n_pu) / n_pu);
86 -
}
87 -
88 -
double calculate_adjusted_portfolio_size(double n_pu, double portfolio_size) {
89 -
  double adjusted_portfolio_size;
90 -
  if (portfolio_size > (n_pu / 2.0)) {
91 -
    adjusted_portfolio_size = std::sqrt(n_pu - portfolio_size) / portfolio_size;
92 -
  } else {
93 -
    adjusted_portfolio_size = std::sqrt(portfolio_size) / portfolio_size;
94 -
  }
95 -
  return adjusted_portfolio_size;
96 -
}
97 -
98 -
bool approx_equal(double x, double y) {
99 -
  bool out = std::abs(x - y) < 1.0e-15;
100 -
  return out;
98 +
  double sum_feat_amount,
99 +
  double sum_sq_feat_amount,
100 +
  double n_pu) {
101 +
  return std::sqrt(
102 +
    sum_sq_feat_amount - ((Pow<2>(sum_feat_amount)) / n_pu) / n_pu
103 +
  );
101 104
}
102 105
103 106
double calculate_rx_removed(
104 -
  double n_pu, double portfolio_size, double stdev, double feat_amount,
105 -
  double feat_target, double mean_feat_amount_per_pu, double feat_sum_amount) {
106 -
  double mean_target_per_portfolio_size = feat_target / (portfolio_size - 1.0);
107 +
  double n_pu,
108 +
  double portfolio_size,
109 +
  double stdev,
110 +
  double feat_amount,
111 +
  double feat_target,
112 +
  double mean_feat_amount_per_pu,
113 +
  double sum_feat_amount) {
107 114
  // init
108 115
  double rx_removed;
109 -
  double z;
110 -
  double adjusted_portfolio_size = calculate_adjusted_portfolio_size(
116 +
  Rcpp::NumericVector z(1);
117 +
  double mean_target_per_portfolio_size =
118 +
    feat_target / (portfolio_size - 1.0);
119 +
  double adj_sd = stdev * calculate_adjusted_portfolio_size(
111 120
    n_pu - 1.0, portfolio_size - 1.0);
112 -
  double adj_sd = stdev * adjusted_portfolio_size;
113 121
  // main
114 -
  if ((feat_sum_amount - feat_amount) < feat_target) {
115 -
      rx_removed = 0.0;
122 +
  if ((sum_feat_amount - feat_amount) < feat_target) {
123 +
    rx_removed = 0.0;
116 124
  } else {
117 125
    if (adj_sd < 1.0e-11) {
118 126
      if (mean_feat_amount_per_pu < mean_target_per_portfolio_size) {
@@ -121,8 +129,9 @@
Loading
121 129
        rx_removed = 1.0;
122 130
      }
123 131
    } else {
124 -
      z = (mean_target_per_portfolio_size - mean_feat_amount_per_pu) / adj_sd;
125 -
      rx_removed = calculate_z_prob(z);
132 +
      z[0] = (mean_target_per_portfolio_size - mean_feat_amount_per_pu) /
133 +
           adj_sd;
134 +
      rx_removed = 1.0 - Rcpp::pnorm(z)[0]; // area under the right tail
126 135
    }
127 136
  }
128 137
  // return result
@@ -130,30 +139,33 @@
Loading
130 139
}
131 140
132 141
double calculate_rx_included(
133 -
  double n_pu, double portfolio_size, double stdev, double feat_amount,
134 -
  double feat_target, double mean_feat_amount_per_pu) {
142 +
  double n_pu,
143 +
  double portfolio_size,
144 +
  double stdev,
145 +
  double feat_amount,
146 +
  double feat_target,
147 +
  double mean_feat_amount_per_pu) {
135 148
  // init
136 149
  double rx_included;
137 -
  double z;
150 +
  Rcpp::NumericVector z(1);
138 151
  double mean_target_per_portfolio_size =
139 152
    (feat_target - feat_amount) / (portfolio_size - 1.0);
140 -
  double adjusted_portfolio_size = calculate_adjusted_portfolio_size(
141 -
      n_pu - 1, portfolio_size - 1.0);
142 -
  double adj_sd = stdev * adjusted_portfolio_size;
153 +
  double adj_sd = stdev * calculate_adjusted_portfolio_size(
154 +
    n_pu - 1.0, portfolio_size - 1.0);
143 155
  // main
144 156
  if (feat_amount >= feat_target) {
145 157
    rx_included = 1.0;
146 158
  } else {
147 159
    if (adj_sd < 1.0e-11) {
148 160
      if (mean_feat_amount_per_pu < mean_target_per_portfolio_size) {
149 -
        rx_included = 0.0;
161 +
        rx_included =  0.0;
150 162
      } else {
151 163
        rx_included = 1.0;
152 164
      }
153 165
    } else {
154 -
      z = (mean_target_per_portfolio_size - mean_feat_amount_per_pu) /
155 -
            adj_sd;
156 -
     rx_included = calculate_z_prob(z);
166 +
      z[0] = (mean_target_per_portfolio_size - mean_feat_amount_per_pu) /
167 +
           adj_sd;
168 +
      rx_included = 1.0 - Rcpp::pnorm(z)[0]; // area on the right tail
157 169
    }
158 170
  }
159 171
  // return result
@@ -161,18 +173,22 @@
Loading
161 173
}
162 174
163 175
double calculate_rx_excluded(
164 -
  double n_pu, double portfolio_size, double stdev, double feat_amount,
165 -
  double feat_target, double feat_sum_amount, double mean_feat_amount_per_pu) {
176 +
  double n_pu,
177 +
  double portfolio_size,
178 +
  double stdev,
179 +
  double feat_amount,
180 +
  double feat_target,
181 +
  double sum_feat_amount,
182 +
  double mean_feat_amount_per_pu) {
166 183
  // init
167 -
  double rx_excluded;
168 -
  double z;
184 +
  double rx_excluded ;
185 +
  Rcpp::NumericVector z(1);
169 186
  double mean_target_per_portfolio_size = feat_target / portfolio_size;
170 -
  double adjusted_portfolio_size = calculate_adjusted_portfolio_size(
171 -
    n_pu - 1.0, portfolio_size);
172 -
  double adj_sd = stdev * adjusted_portfolio_size;
187 +
  double adj_sd = stdev * calculate_adjusted_portfolio_size(
188 +
    n_pu - 1, portfolio_size);
173 189
  // main
174 -
  if ((feat_sum_amount - feat_amount) < feat_target) {
175 -
    rx_excluded = 0.0;
190 +
  if ((sum_feat_amount - feat_amount) < feat_target) {
191 +
    rx_excluded = 0;
176 192
  } else {
177 193
    if (adj_sd < 1.0e-11) {
178 194
      if (mean_feat_amount_per_pu < mean_target_per_portfolio_size) {
@@ -181,70 +197,21 @@
Loading
181 197
        rx_excluded = 1.0;
182 198
      }
183 199
    } else {
184 -
      z = (mean_target_per_portfolio_size - mean_feat_amount_per_pu) /
185 -
            adj_sd;
186 -
      rx_excluded = calculate_z_prob(z);
200 +
      z[0] = (mean_target_per_portfolio_size - mean_feat_amount_per_pu) /
201 +
              adj_sd;
202 +
      rx_excluded = 1.0 - Rcpp::pnorm(z)[0]; // area under the right tail
187 203
    }
188 204
  }
189 205
  // return result
190 206
  return rx_excluded;
191 207
}
192 208
193 -
double calculate_irr_feature(
194 -
  double wt_include, double wt_exclude, double rx_removed, double rx_included,
195 -
  double rx_excluded, double feat_amount) {
196 -
  // init
197 -
  double irr_feature;
198 -
  // prelimniary calculation
199 -
  if ((approx_equal(rx_included, 0)) & (feat_amount > 1.0e-15)) {
200 -
      rx_included = 1.0;
201 -
  }
202 -
  // main calculation
203 -
  if (approx_equal(rx_included + rx_excluded, 0)) {
204 -
    irr_feature = 0.0;
205 -
  } else {
206 -
    irr_feature = ((rx_included - rx_removed) * wt_include) /
207 -
                   (rx_included * wt_include + rx_excluded * wt_exclude);
208 -
  }
209 -
  // return result
210 -
  return irr_feature;
211 -
}
212 -
213 -
double calculate_z_prob(double x) {
214 -
  // init
215 -
  double zprob;
216 -
  double z;
217 -
  double t;
218 -
  double m;
219 -
  double q;
220 -
  bool negative;
221 -
  // preliminary calculations
222 -
  if (x < 0.0) {
223 -
    negative = true;
224 -
    x = 0.0 - x;
225 -
  } else {
226 -
    negative = false;
227 -
  }
228 -
  if (x > 50.0)
229 -
    x = 50.0;
230 -
  // main calculations
231 -
  z = 0.3989 * std::exp((0.0 - std::sqrt(x)) / 2.0);
232 -
  t = 1.0 / (1.0 + 0.23164 * x);
233 -
  m = t;
234 -
  q = 0.31938 * m;
235 -
  m = m * t;
236 -
  q = q - 0.35656 * m;
237 -
  m = m * t;
238 -
  q = q + 1.78148 * m;
239 -
  m = m * t;
240 -
  q = q -1.82126 * m;
241 -
  m = m * t;
242 -
  q = q + 1.33027 * m;
243 -
  // return result
244 -
  if (negative) {
245 -
    zprob = 1.0 - q * z;
209 +
double calculate_adjusted_portfolio_size(double n_pu, double portfolio_size) {
210 +
  double adjusted_portfolio_size;
211 +
  if (portfolio_size > (n_pu / 2.0)) {
212 +
    adjusted_portfolio_size = std::sqrt(n_pu - portfolio_size) / portfolio_size;
246 213
  } else {
247 -
    zprob = q * z;
214 +
    adjusted_portfolio_size = std::sqrt(portfolio_size) / portfolio_size;
248 215
  }
249 -
  return zprob;
216 +
  return adjusted_portfolio_size;
250 217
}

@@ -274,7 +274,7 @@
Loading
274 274
      a <- add_default_solver(a)
275 275
    default_portfolio <- inherits(a$portfolio, "Waiver")
276 276
    if (inherits(a$portfolio, "Waiver"))
277 -
      a <- add_default_portfolio(a)
277 +
      a <- add_default_portfolio(a) #nocov
278 278
    # run presolve check to try to identify potential problems
279 279
    if (run_checks) {
280 280
      ch <- presolve_check(a)
@@ -287,10 +287,12 @@
Loading
287 287
    # solve problem
288 288
    sol <- a$portfolio$run(opt, a$solver)
289 289
    # check that solution is valid
290 +
    #nocov start
290 291
    if (is.null(sol) || is.null(sol[[1]]$x)) {
291 292
      stop("no solution found (e.g. due to problem infeasibility or time ",
292 293
           "limits)")
293 294
    }
295 +
    #nocov end
294 296
    ## format solutions
295 297
    # format solutions into planning unit by zones matrix
296 298
    na_pos <- which(is.na(a$planning_unit_costs()), arr.ind = TRUE)
@@ -374,7 +376,7 @@
Loading
374 376
      })
375 377
      names(ret) <- paste0("solution_", seq_along(sol))
376 378
    } else {
377 -
      stop("planning unit data is of an unrecognized class")
379 +
      stop("planning unit data is of an unrecognized class") # nocov
378 380
    }
379 381
    # if ret is a list of matrices with a single column then convert to numeric
380 382
    if (is.matrix(ret[[1]]) && ncol(ret[[1]]) == 1)

@@ -135,10 +135,14 @@
Loading
135 135
      rt <- system.time({
136 136
        x <- do.call(Rsymphony::Rsymphony_solve_LP, append(model, p))
137 137
      })
138 +
      # manually return NULL to indicate error if no solution
139 +
      #nocov start
138 140
      if (is.null(x$solution) ||
139 141
          names(x$status) %in% c("TM_NO_SOLUTION", "PREP_NO_SOLUTION"))
140 142
        return(NULL)
143 +
      #nocov end
141 144
      # fix floating point issues with binary variables
145 +
      #nocov start
142 146
      b <- which(model$types == "B")
143 147
      if (any(x$solution[b] > 1)) {
144 148
        if (max(x$solution[b]) < 1.01) {
@@ -154,6 +158,7 @@
Loading
154 158
          stop("infeasible solution returned, try relaxing solver parameters")
155 159
        }
156 160
      }
161 +
      #nocov end
157 162
      # fix floating point issues with continuous variables
158 163
      cv <- which(model$types == "C")
159 164
      x$solution[cv] <-

@@ -36,3 +36,7 @@
Loading
36 36
  }
37 37
  return;
38 38
}
39 +
40 +
bool approx_equal(double x, double y) {
41 +
  return (std::abs(x - y) < 1.0e-15);
42 +
}
Files Coverage
R 97.71%
src 98.85%
Project Totals (124 files) 98.06%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading