Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for different weight vector types #250

Merged
merged 50 commits into from
May 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a323fc9
Initial work on adding weight vector types with bias correction.
rofinn Apr 25, 2017
457f919
Added corrected option to many stats methods.
rofinn Apr 26, 2017
7cd959d
Fixed style issues.
rofinn Apr 27, 2017
d0653db
Added re-added `WeightVec` only as a deprecated type and deprecated `…
rofinn Apr 27, 2017
e886066
Cleaned up docstrings for weight bias methods (mostly latex equations).
rofinn Apr 27, 2017
6710bcb
Updated the "default" bias correction code to iteratively compute the…
rofinn Apr 27, 2017
e662b9d
Updated `FrequecyWeights` to accept `Real`s
rofinn Apr 27, 2017
e7f2ce9
Removed some unnecessary v0.5 pre-release code and version checks.
rofinn Apr 27, 2017
15d8100
Converted a ternary to an if statement to help with readability.
rofinn Apr 27, 2017
520c061
Replaced all internal (and test) calls to `weights(...)` with `fweigh…
rofinn Apr 27, 2017
a23b8cb
Changed function name `bias` -> `cfactor`.
rofinn Apr 28, 2017
25f69f3
Removed corrected flag from `skewness` and `kurtosis` and set default…
rofinn Apr 28, 2017
096548c
Fixed 0.6 `abstract type ... end ` deprecation warnings using compat.
rofinn Apr 28, 2017
616408d
Removed `corrected` option from `moment`s, but kept it for `_moment2`…
rofinn Apr 28, 2017
b73a00a
Renamed cfactor -> varcorrection
rofinn Apr 28, 2017
a9485db
Fixed comments in export.
rofinn Apr 28, 2017
7f179ef
Fixed more style issues.
rofinn Apr 28, 2017
c9437ab
Reverted dispatching on corrected value.
rofinn Apr 28, 2017
7072eae
Updated weights docstrings.
rofinn Apr 28, 2017
46a1aa9
Reworked `var` and `std` definitions and doc strings.
rofinn Apr 29, 2017
e831d15
Removed a couple unnecessary functions from deprecates.
rofinn Apr 29, 2017
4ade783
Updated mean_and_x docstrings to mention `corrected` and point to the…
rofinn Apr 29, 2017
7bdc112
Added documentation about `corrected` argument in `cov` and `mean_and…
rofinn Apr 29, 2017
f6f3b38
Added deprecation tests and fixed a few bugs with our deprecations.
rofinn Apr 30, 2017
ad49920
Fixed analytic weights `varcorrection` equations.
rofinn Apr 30, 2017
def70e2
Added an extra deprecation test and fixed --depwarn=no check.
rofinn May 1, 2017
06ec7f9
Updated test/moments.jl to use `@testset` so that we can test against…
rofinn May 1, 2017
1765f67
Updated test/weights.jl to use `@testset` so that we can test against…
rofinn May 1, 2017
4b42570
Updated test/cov.jl to use `@testset` and added a few extra tests.
rofinn May 1, 2017
dd9e94e
Switched positional corrected back to a keyword with `Union{Bool, Voi…
rofinn May 2, 2017
a627e01
Added generic `Weights` type and deprecated `WeightVec` to it.
rofinn May 3, 2017
34c88f2
Removed eweights and fixed up test cases.
rofinn May 3, 2017
1f01bc0
Added testing of all weights to test/cov.jl
rofinn May 3, 2017
bdec9e1
Removed unnecessary 0 mean condition from `var`
rofinn May 3, 2017
c0f6488
Reverted changes to skewness and kurtosis.
rofinn May 3, 2017
a8624cd
Updated docs to refer to `AbstractWeighs` vs `WeightVec` and included…
rofinn May 3, 2017
745c419
More random fixes. Mostly to docstrings.
rofinn May 3, 2017
8d85af7
More doc fixes.
rofinn May 4, 2017
05a3cd7
Removed `fweights` from tests in favour of `weights` (to reduces PR s…
rofinn May 4, 2017
2370595
Moved description of different weight types in an Implementations sec…
rofinn May 4, 2017
3f84e71
Moved `Weights` description later in the docs.
rofinn May 4, 2017
8bcf448
Removed two argument example from weightvec docs.
rofinn May 4, 2017
dedb155
Moved description of weight vector benefits to the top of the file.
rofinn May 4, 2017
281654d
Not sure how much this helped, but tried to minimize the amount of `@…
rofinn May 4, 2017
5bdf58b
Added comment about unsupported bias correction for the `Weights` typ…
rofinn May 4, 2017
b991667
Removed deprecation tests and corresponding hacks.
rofinn May 4, 2017
926678e
Removed more deprecation test hacks and convert `wv` -> `w` in the ap…
rofinn May 4, 2017
85ace2a
Missing depcheck on a `stdm` call.
rofinn May 5, 2017
a0a2ad6
Updated the rst scalarstats and cov docs with the updated docstrings.
rofinn May 6, 2017
6087b7f
More rst docstring updates.
rofinn May 6, 2017
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
2 changes: 1 addition & 1 deletion docs/source/counts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Counting over an Integer Range

.. function:: counts(x, a:b[, wv])

Count the number of times (or total weights if a weight vector ``wv`` is given) values in ``a:b`` appear in array ``x``. Here, the optional argument ``wv`` should be a weight vector of type ``WeightVec`` (see :ref:`weightvec`).
Count the number of times (or total weights if a weight vector ``wv`` is given) values in ``a:b`` appear in array ``x``. Here, the optional argument ``wv`` should be a weight vector of type ``AbstractWeights`` (see :ref:`weightvec`).
Copy link
Member

Choose a reason for hiding this comment

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

Is weightvec still the right ReST reference?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I didn't rename the file. I assume that's what the :ref: is for (I really don't know restructured text though)?

Copy link
Member Author

@rofinn rofinn May 4, 2017

Choose a reason for hiding this comment

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

I can confirm that the link still works (it's just pointing to the weightvec.rst file).

Copy link
Member

Choose a reason for hiding this comment

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

Awesome, thanks for checking! I don't know how it works either. 🙃


This function returns a vector ``r`` of length ``n``, with ``n = length(a:b) = b-a+1``. In particular, we have

Expand Down
19 changes: 13 additions & 6 deletions docs/source/cov.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,21 @@ This package implements functions for computing scatter matrix, as well as weigh

.. function:: scatter(X, wv[; vardim=..., mean=...])

Weighted scatter matrix. The weights are given by a weight vector ``wv`` of type ``WeightVec`` (see :ref:`weightvec`).
Weighted scatter matrix. The weights are given by a weight vector ``wv`` of type ``AbstractWeights`` (see :ref:`weightvec`).

.. function:: cov(X, wv[; vardim=..., mean=...])
.. function:: cov(X, w[; vardim=..., mean=..., corrected=...])

Weighted covariance matrix.
Compute the weighted covariance matrix. Similar to ``var`` and ``std`` the biased covariance matrix (``corrected=false``) is computed by multiplying ``scattermat(X, w)`` by :math:`\frac{1}{\sum{w}}` to normalize.
However, the unbiased covariance matrix (``corrected=true``) is dependent on the type of weights used:

**Note:** By default, the covariance is normalized by the sum of weights, that is, ``cov(X, wv)`` is equal to ``scatter(X, wv) / sum(wv)``.
* ``AnalyticWeights``: :math:`\frac{1}{\sum w - \sum {w^2} / \sum w}`
* ``FrequencyWeights``: :math:`\frac{1}{\sum{w} - 1}`
* ``ProbabilityWeights``: :math:`\frac{n}{(n - 1) \sum w}` where ``n`` equals ``count(!iszero, w)``
* ``Weights``: ``ArgumentError`` (bias correction not supported)

.. function:: mean_and_cov(x[, wv][; vardim=...])
.. function:: mean_and_cov(x[, wv][; vardim=..., corrected=...])

Jointly compute the mean and covariance of ``x``.
Jointly compute the mean and covariance matrix as a tuple.
A weighting vector ``wv`` can be specified. ``vardim`` that designates whether the variables are columns in the matrix (``1``) or rows (``2``).
Finally, bias correction is applied to the covariance calculation if ``corrected=true``.
See ``cov`` documentation for more details.
11 changes: 5 additions & 6 deletions docs/source/empirical.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ Histograms can be fitted to data using the ``fit`` method.

**Arguments:**

``data``
``data``
is either a vector (for a 1-dimensional histogram), or a tuple of
vectors of equal length (for an *n*-dimensional histogram).

``weight``
is an optional ``:ref:`weightvec` WeightVec``` (of the same length as the
is an optional ``:ref:`weightvec` AbstractWeights``` (of the same length as the
data vectors), denoting the weight each observation contributes to the
bin. If no weight vector is supples, each observation has weight 1.

Expand All @@ -30,7 +30,7 @@ Histograms can be fitted to data using the ``fit`` method.

**Keyword arguments:**

``closed=:left/:right``
``closed=:left/:right``
determines whether the bin intervals are left-closed [a,b), or right-closed
(a,b] (default = ``:right``).

Expand All @@ -48,7 +48,7 @@ Histograms can be fitted to data using the ``fit`` method.
h = fit(Histogram, rand(100), weights(rand(100)), 0:0.1:1.0)
h = fit(Histogram, [20], 0:20:100)
h = fit(Histogram, [20], 0:20:100, closed=:left)

# Multivariate
h = fit(Histogram, (rand(100),rand(100)))
h = fit(Histogram, (rand(100),rand(100)),nbins=10)
Expand All @@ -60,7 +60,6 @@ Empirical Cumulative Distribution Function

.. function:: ecdf(x)

Return an empirical cumulative distribution function based on a vector of samples given in ``x``.
Return an empirical cumulative distribution function based on a vector of samples given in ``x``.

**Note:** this is a higher-level function that returns a function, which can then be applied to evaluate CDF values on other samples.

4 changes: 2 additions & 2 deletions docs/source/means.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The package provides functions to compute means of different kinds.

.. function:: mean(x, w)

The ``mean`` function is also extended to accept a weight vector of type ``WeightVec`` (see :ref:`weightvec`) to compute weighted mean.
The ``mean`` function is also extended to accept a weight vector of type ``AbstractWeights`` (see :ref:`weightvec`) to compute weighted mean.

**Examples:**

Expand All @@ -43,7 +43,7 @@ The package provides functions to compute means of different kinds.

.. function:: mean(x, w, dim)

Compute weighted means of ``x`` along a certain dimension (specified by an integer ``dim``). The weights are given by a weight vector ``w`` (of type ``WeightVec``).
Compute weighted means of ``x`` along a certain dimension (specified by an integer ``dim``). The weights are given by a weight vector ``w`` (of type ``AbstractWeights``).

.. function:: mean!(dst, x, w, dim)

Expand Down
29 changes: 13 additions & 16 deletions docs/source/sampling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ The package provides functions for sampling from a given population (with or wit
.. function:: sample([rng], a)

Randomly draw an element from an array ``a``.

Optionally specify a random number generator ``rng`` as the first argument (defaults to ``Base.GLOBAL_RNG``).

.. function:: sample([rng], a, n[; replace=true, ordered=false])
.. function:: sample([rng], a, n[; replace=true, ordered=false])

Randomly draw ``n`` elements from ``a``.
Randomly draw ``n`` elements from ``a``.

Optionally specify a random number generator ``rng`` as the first argument (defaults to ``Base.GLOBAL_RNG``).

Expand All @@ -26,14 +25,13 @@ The package provides functions for sampling from a given population (with or wit
.. function:: sample!([rng], a, x[; replace=true, ordered=false])

Draw ``length(x)`` elements from ``a`` and write them to a pre-allocated array ``x``.

Optionally specify a random number generator ``rng`` as the first argument (defaults to ``Base.GLOBAL_RNG``).

.. function:: sample([rng], wv)
.. function:: sample([rng], wv)

Draw an integer in ``1:length(wv)`` with probabilities proportional to the weights given in ``wv``.
Draw an integer in ``1:length(wv)`` with probabilities proportional to the weights given in ``wv``.

Here, ``wv`` should be a weight vector of type ``WeightVec`` (see :ref:`weightvec`).
Here, ``wv`` should be a weight vector of type ``AbstractWeights`` (see :ref:`weightvec`).

Optionally specify a random number generator ``rng`` as the first argument (defaults to ``Base.GLOBAL_RNG``).

Expand All @@ -52,7 +50,7 @@ The package provides functions for sampling from a given population (with or wit
**Keyword arguments**

- ``replace``: indicates whether to have replacement (default = ``true``).
- ``ordered``: indicates whether to arrange the samples in ascending order (default = ``false``).
- ``ordered``: indicates whether to arrange the samples in ascending order (default = ``false``).

.. function:: sample!([rng], a, wv, x[; replace=true, ordered=false])

Expand All @@ -74,7 +72,7 @@ Here are a list of algorithms implemented in the package. The functions below ar

- ``a``: source array representing the population
- ``x``: the destination array
- ``wv``: the weight vector (of type ``WeightVec``), for weighted sampling
- ``wv``: the weight vector (of type ``AbstractWeights``), for weighted sampling
- ``n``: the length of ``a``
- ``k``: the length of ``x``. For sampling without replacement, ``k`` must not exceed ``n``.
- ``rng``: optional random number generator (defaults to ``Base.GLOBAL_RNG``)
Expand Down Expand Up @@ -108,7 +106,7 @@ All following functions write results to ``x`` (pre-allocated) and return ``x``.

.. function:: fisher_yates_sample!([rng], a, x)

*Fisher-Yates shuffling* (with early termination).
*Fisher-Yates shuffling* (with early termination).

Pseudo-code ::

Expand All @@ -118,15 +116,15 @@ All following functions write results to ``x`` (pre-allocated) and return ``x``.
swap inds[i] with a random one in inds[i:n]
set x[i] = a[inds[i]]
end


This algorithm consumes ``k`` random numbers. It uses an integer array of length ``n`` internally to maintain the shuffled indices. It is considerably faster than Knuth's algorithm especially when ``n`` is greater than ``k``.

.. function:: self_avoid_sample!([rng], a, x)

Use a set to maintain the index that has been sampled. Each time draw a new index, if the index has already been sampled, redraw until it draws an unsampled one.
Use a set to maintain the index that has been sampled. Each time draw a new index, if the index has already been sampled, redraw until it draws an unsampled one.

This algorithm consumes about (or slightly more than) ``k`` random numbers, and requires ``O(k)`` memory to store the set of sampled indices. Very fast when ``n >> k``.
This algorithm consumes about (or slightly more than) ``k`` random numbers, and requires ``O(k)`` memory to store the set of sampled indices. Very fast when ``n >> k``.

However, if ``k`` is large and approaches ``n``, the rejection rate would increase drastically, resulting in poorer performance.

Expand All @@ -153,7 +151,7 @@ All following functions write results to ``x`` (pre-allocated) and return ``x``.

*Direct sampling.*

Draw each sample by scanning the weight vector.
Draw each sample by scanning the weight vector.

This algorithm: (1) consumes ``k`` random numbers; (2) has time complexity ``O(n k)``, as scanning the weight vector each time takes ``O(n)``; and (3) requires no additional memory space.

Expand All @@ -173,5 +171,4 @@ All following functions write results to ``x`` (pre-allocated) and return ``x``.

It makes a copy of the weight vector at initialization, and sets the weight to zero when the corresponding sample is picked.

This algorithm consumes ``O(k)`` random numbers, and has overall time complexity ``O(n k)``.

This algorithm consumes ``O(k)`` random numbers, and has overall time complexity ``O(n k)``.
63 changes: 41 additions & 22 deletions docs/source/scalarstats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,73 @@ The package implements functions for computing various statistics over an array
Moments
---------

.. function:: var(x, wv[; mean=...])
.. function:: var(x, w, [dim][; mean=..., corrected=...])

Compute weighted variance.
Compute the variance of a real-valued array ``x``, optionally over a dimension ``dim``.
Observations in ``x`` are weighted using weight vector ``w``.
The uncorrected (when ``corrected=false``) sample variance is defined as:

One can set the keyword argument ``mean``, which can be either ``nothing`` (to compute the mean value within the function), ``0``, or a pre-computed mean value.
:math:`\frac{1}{\sum{w}} \sum_{i=1}^n {w_i\left({x_i - m}\right)^2 }`

**Note:** the result is normalized by ``sum(wv)`` without correction.
where ``n`` is the length of the input and ``m`` is the mean.
The unbiased estimate (when ``corrected=true``) of the population variance is computed by
replacing :math:`\frac{1}{\sum{w}}` with a factor dependent on the type of weights used:

.. function:: var(x, wv, dim[; mean=...])
* ``AnalyticWeights``: :math:`\frac{1}{\sum w - \sum {w^2} / \sum w}`
* ``FrequencyWeights``: :math:`\frac{1}{\sum{w} - 1}`
* ``ProbabilityWeights``: :math:`\frac{n}{(n - 1) \sum w}` where ``n`` equals ``count(!iszero, w)``
* ``Weights``: ``ArgumentError`` (bias correction not supported)

Weighted variance along a specific dimension.
.. function:: std(v, w, [dim][; mean=..., corrected=...])

.. function:: std(x, wv[; mean=...])
Compute the standard deviation of a real-valued array ``x``, optionally over a dimension ``dim``.
Observations in ``x`` are weighted using weight vector ``w``.
The uncorrected (when ``corrected=false``) sample standard deviation is defined as:

Compute weighted standard deviation.
:math:`\sqrt{\frac{1}{\sum{w}} \sum_{i=1}^n {w_i\left({x_i - m}\right)^2 }}`

One can set the keyword argument ``mean``, which can be either ``nothing`` (to compute the mean value within the function), ``0``, or a pre-computed mean value.
where ``n`` is the length of the input and ``m`` is the mean.
The unbiased estimate (when ``corrected=true``) of the population standard deviation is
computed by replacing :math:`\frac{1}{\sum{w}}` with a factor dependent on the type of
weights used:

.. function:: std(x, wv, dim[; mean=...])
* ``AnalyticWeights``: :math:`\frac{1}{\sum w - \sum {w^2} / \sum w}`
* ``FrequencyWeights``: :math:`\frac{1}{\sum{w} - 1}`
* ``ProbabilityWeights``: :math:`\frac{n}{(n - 1) \sum w}` where ``n`` equals ``count(!iszero, w)``
* ``Weights``: ``ArgumentError`` (bias correction not supported)

Weighted standard deviation along a specific dimension.
.. function:: mean_and_var(x[, w][, dim][; corrected=...])

.. function:: mean_and_var(x[, wv][, dim])
Jointly compute the mean and variance of a real-valued array ``x``, optionally over a dimension ``dim``, as a tuple.
Observations in ``x`` can be weighted using weight vector ``w``.
Finally, bias correction is be applied to the variance calculation if ``corrected=true``.
See ``var`` documentation for more details.

Jointly compute the mean and variance of ``x``.
.. function:: mean_and_std(x[, w][, dim][; corrected=...])

.. function:: mean_and_std(x[, wv][, dim])

Jointly compute the mean and standard deviation of ``x``.
Jointly compute the mean and standard deviation of a real-valued array ``x``, optionally over a dimension ``dim``, as a tuple.
A weighting vector ``w`` can be specified to weight the estimates.
Finally, bias correction is applied to the standard deviation calculation if ``corrected=true``.
See ``std`` documentation for more details.

.. function:: skewness(x[, wv])

Compute the (standardized) `skewness <http://en.wikipedia.org/wiki/Skewness>`_ of ``x``.

One can optionally supply a weight vector of type ``WeightVec`` (see :ref:`weightvec`).
One can optionally supply a weight vector of type ``AbstractWeights`` (see :ref:`weightvec`).

.. function:: kurtosis(x[, wv])

Compute the (excessive) `kurtosis <http://en.wikipedia.org/wiki/Kurtosis>`_ of ``x``.

One can optionally supply a weight vector of type ``WeightVec`` (see :ref:`weightvec`).
One can optionally supply a weight vector of type ``AbstractWeights`` (see :ref:`weightvec`).

.. function:: moment(x, k[, m][, wv])

Compute the ``k``-th order central moment of the values in `x`. It is the sample mean of
``(x - mean(x)).^k``.

One can optionally supply the center ``m``, and/or a weight vector of type ``WeightVec`` (see :ref:`weightvec`).
One can optionally supply the center ``m``, and/or a weight vector of type ``AbstractWeights`` (see :ref:`weightvec`).


Measurements of Variation
Expand Down Expand Up @@ -160,7 +179,7 @@ Quantile and Friends

.. function:: median(x, w)

Compute the weighted median of ``x``, using weights given by a weight vector ``w`` (of type ``WeightVec``). The weight and data vectors must have the same length. The weighted median :math:`x_k` is the element of ``x`` that satisfies :math:`\sum_{x_i < x_k} w_i \le \frac{1}{2} \sum_{j} w_j` and :math:`\sum_{x_i > x_k} w_i \le \frac{1}{2} \sum_{j} w_j`. If a weight has value zero, then its associated data point is ignored. If none of the weights are positive, an error is thrown. ``NaN`` is returned if ``x`` contains any ``NaN`` values. An error is raised if ``w`` contains any ``NaN`` values.
Compute the weighted median of ``x``, using weights given by a weight vector ``w`` (of type ``AbstractWeights``). The weight and data vectors must have the same length. The weighted median :math:`x_k` is the element of ``x`` that satisfies :math:`\sum_{x_i < x_k} w_i \le \frac{1}{2} \sum_{j} w_j` and :math:`\sum_{x_i > x_k} w_i \le \frac{1}{2} \sum_{j} w_j`. If a weight has value zero, then its associated data point is ignored. If none of the weights are positive, an error is thrown. ``NaN`` is returned if ``x`` contains any ``NaN`` values. An error is raised if ``w`` contains any ``NaN`` values.

**Examples:**

Expand All @@ -171,8 +190,8 @@ Quantile and Friends

.. function:: quantile(x, w, p)

Compute the weighted quantiles of a vector ``x`` at a specified set of probability values ``p``, using weights given by a weight vector ``w`` (of type ``WeightVec``). Weights must not be negative. The weights and data vectors must have the same length. The quantile for :math:`p` is defined as follows. Denoting :math:`S_k = (k-1)w_k + (n-1) \sum_{i<k}w_i`, define :math:`x_{k+1}` the smallest element of ``x`` such that :math:`S_{k+1}/S_{n}` is strictly superior to :math:`p`. The function returns :math:`(1-\gamma) x_k + \gamma x_{k+1}` with :math:`\gamma = (pS_n- S_k)/(S_{k+1}-S_k)`. This corresponds to R-7, Excel, SciPy-(1,1), Maple-6 when ``w`` is one (see https://en.wikipedia.org/wiki/Quantile).
Compute the weighted quantiles of a vector ``x`` at a specified set of probability values ``p``, using weights given by a weight vector ``w`` (of type ``AbstractWeights``). Weights must not be negative. The weights and data vectors must have the same length. The quantile for :math:`p` is defined as follows. Denoting :math:`S_k = (k-1)w_k + (n-1) \sum_{i<k}w_i`, define :math:`x_{k+1}` the smallest element of ``x`` such that :math:`S_{k+1}/S_{n}` is strictly superior to :math:`p`. The function returns :math:`(1-\gamma) x_k + \gamma x_{k+1}` with :math:`\gamma = (pS_n- S_k)/(S_{k+1}-S_k)`. This corresponds to R-7, Excel, SciPy-(1,1), Maple-6 when ``w`` is one (see https://en.wikipedia.org/wiki/Quantile).

Mode and Modes
---------------

Expand Down
Loading