Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
mcglm
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
leg
mcglm
Commits
07928bb7
Commit
07928bb7
authored
9 years ago
by
Walmes Marques Zeviani
Browse files
Options
Downloads
Patches
Plain Diff
Put in one file all S3 generic methods.
parent
45e6af2a
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
R/mc_S3_methods.R
+512
-0
512 additions, 0 deletions
R/mc_S3_methods.R
with
512 additions
and
0 deletions
R/mc_S3_methods.R
0 → 100644
+
512
−
0
View file @
07928bb7
#' @title ANOVA method for McGLMs.
#' @name anova.mcglm
#'
#' @description ANOVA method for object of class McGLMS.
#'
#' @param object an object of class \code{mcglm}, usually, a result of a
#' call to \code{mcglm()}.
#' @param ... additional arguments affecting the summary produced. Note
#' that there is no extra options for mcglm object class.
#'
#' @return A \code{data.frame} with Chi-square statistic to test the
#' null hypothesis of a parameter, or a set of parameters, be
#' zero. The Wald test based on the observed covariance matrix of
#' the parameters is used.
#'
#' @method anova mcglm
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#' @export
anova.mcglm
<-
function
(
object
,
...
)
{
n_resp
<-
length
(
object
$
mu_list
)
n_beta
<-
lapply
(
object
$
list_X
,
ncol
)
idx.list
<-
list
()
for
(
i
in
1
:
n_resp
)
{
idx.list
[[
i
]]
<-
rep
(
i
,
n_beta
[
i
])
}
vv
<-
vcov
(
object
)
n_par
<-
dim
(
vv
)[
1
]
idx.vec
<-
do.call
(
c
,
idx.list
)
n_cov
<-
n_par
-
length
(
idx.vec
)
idx.vec
<-
c
(
idx.vec
,
rep
(
0
,
n_cov
))
temp.vcov
<-
list
()
temp.beta
<-
list
()
for
(
i
in
1
:
n_resp
)
{
idx.id
=
idx.vec
==
i
temp.vcov
[[
i
]]
<-
vv
[
idx.id
,
idx.id
]
temp.beta
[[
i
]]
<-
coef
(
object
,
type
=
"beta"
,
response
=
i
)
$
Estimates
}
saida
<-
list
()
for
(
i
in
1
:
n_resp
)
{
idx
<-
attr
(
object
$
list_X
[[
i
]],
"assign"
)
names
<-
colnames
(
object
$
list_X
[[
i
]])
if
(
names
[
1
]
==
"(Intercept)"
)
{
idx
<-
idx
[
-1
]
names
<-
names
[
-1
]
temp.beta
[[
i
]]
<-
temp.beta
[[
i
]][
-1
]
temp.vcov
[[
i
]]
<-
temp.vcov
[[
i
]][
-1
,
-1
]
}
n_terms
<-
length
(
unique
(
idx
))
X2.resp
<-
list
()
for
(
j
in
1
:
n_terms
)
{
idx.TF
<-
idx
==
j
temp
<-
as.numeric
(
t
(
temp.beta
[[
i
]][
idx.TF
])
%*%
solve
(
as.matrix
(
temp.vcov
[[
i
]])[
idx.TF
,
idx.TF
])
%*%
temp.beta
[[
i
]][
idx.TF
])
nbeta.test
<-
length
(
temp.beta
[[
i
]][
idx.TF
])
X2.resp
[[
j
]]
<-
data.frame
(
Covariate
=
names
[
idx.TF
][
1
],
Chi.Square
=
round
(
temp
,
4
),
Df
=
nbeta.test
,
p.value
=
round
(
pchisq
(
temp
,
nbeta.test
,
lower.tail
=
FALSE
),
4
))
}
saida
[[
i
]]
<-
do.call
(
rbind
,
X2.resp
)
}
cat
(
"Wald test for fixed effects\n"
)
return
(
saida
)
}
#' @title Extract model coefficients for mcglm class
#' @name coef.mcglm
#'
#' @description \code{coef.mcglm} is a function which extracts model
#' coefficients from objects of \code{mcglm} class.
#'
#' @param object An object of \code{mcglm} class.
#' @param std.error Logical. If \code{TRUE} returns the standard errors
#' of the estimates. Default is \code{FALSE}.
#' @param response A numeric vector specyfing for which response
#' variables the coefficients should be returned.
#' @param type A string vector (can be 1 element length) specyfing which
#' coefficients should be returned. Options are \code{"beta"},
#' \code{"tau"}, \code{"power"}, \code{"tau"} and
#' \code{"correlation"}.
#' @param ... additional arguments affecting the summary produced. Note
#' that there is no extra options for mcglm object class.
#'
#' @return A \code{data.frame} with parameters names, estimates,
#' response number and parameters type.
#'
#' @method coef mcglm
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#' @export
coef.mcglm
<-
function
(
object
,
std.error
=
FALSE
,
response
=
c
(
NA
,
1
:
length
(
object
$
beta_names
)),
type
=
c
(
"beta"
,
"tau"
,
"power"
,
"correlation"
),
...
)
{
n_resp
<-
length
(
object
$
beta_names
)
cod_beta
<-
list
()
cod_power
<-
list
()
cod_tau
<-
list
()
type_beta
<-
list
()
type_power
<-
list
()
type_tau
<-
list
()
resp_beta
<-
list
()
resp_power
<-
list
()
resp_tau
<-
list
()
response_for
<-
1
:
n_resp
for
(
i
in
response_for
)
{
cod_beta
[[
i
]]
<-
paste0
(
paste0
(
"beta"
,
i
),
0
:
c
(
object
$
Information
$
n_betas
[[
i
]]
-
1
))
type_beta
[[
i
]]
<-
rep
(
"beta"
,
length
(
cod_beta
[[
i
]]))
resp_beta
[[
i
]]
<-
rep
(
response_for
[
i
],
length
(
cod_beta
[[
i
]]))
if
(
object
$
Information
$
n_power
[[
i
]]
!=
0
|
object
$
power_fixed
[[
i
]]
==
FALSE
)
{
cod_power
[[
i
]]
<-
paste0
(
paste0
(
"power"
,
i
),
1
:
object
$
Information
$
n_power
[[
i
]])
type_power
[[
i
]]
<-
rep
(
"power"
,
length
(
cod_power
[[
i
]]))
resp_power
[[
i
]]
<-
rep
(
response_for
[
i
],
length
(
cod_power
[[
i
]]))
}
if
(
object
$
Information
$
n_power
[[
i
]]
==
0
)
{
cod_power
[[
i
]]
<-
rep
(
1
,
0
)
type_power
[[
i
]]
<-
rep
(
1
,
0
)
resp_power
[[
i
]]
<-
rep
(
1
,
0
)
}
cod_tau
[[
i
]]
<-
paste0
(
paste0
(
"tau"
,
i
),
1
:
object
$
Information
$
n_tau
[[
i
]])
type_tau
[[
i
]]
<-
rep
(
"tau"
,
length
(
cod_tau
[[
i
]]))
resp_tau
[[
i
]]
<-
rep
(
response_for
[
i
],
length
(
cod_tau
[[
i
]]))
}
rho_names
<-
c
()
if
(
n_resp
!=
1
)
{
combination
<-
combn
(
n_resp
,
2
)
for
(
i
in
1
:
dim
(
combination
)[
2
])
{
rho_names
[
i
]
<-
paste0
(
paste0
(
"rho"
,
combination
[
1
,
i
]),
combination
[
2
,
i
])
}
}
type_rho
<-
rep
(
"correlation"
,
length
(
rho_names
))
resp_rho
<-
rep
(
NA
,
length
(
rho_names
))
cod
<-
c
(
do.call
(
c
,
cod_beta
),
rho_names
,
do.call
(
c
,
Map
(
c
,
cod_tau
)))
type_cod
<-
c
(
do.call
(
c
,
type_beta
),
type_rho
,
do.call
(
c
,
Map
(
c
,
type_tau
)))
response_cod
<-
c
(
do.call
(
c
,
resp_beta
),
resp_rho
,
do.call
(
c
,
Map
(
c
,
resp_tau
)))
if
(
length
(
cod_power
)
!=
0
)
{
cod
<-
c
(
do.call
(
c
,
cod_beta
),
rho_names
,
do.call
(
c
,
Map
(
c
,
cod_power
,
cod_tau
)))
type_cod
<-
c
(
do.call
(
c
,
type_beta
),
type_rho
,
do.call
(
c
,
Map
(
c
,
type_power
,
type_tau
)))
response_cod
<-
c
(
do.call
(
c
,
resp_beta
),
resp_rho
,
do.call
(
c
,
Map
(
c
,
resp_power
,
resp_tau
)))
}
Estimates
<-
c
(
object
$
Regression
,
object
$
Covariance
)
coef_temp
<-
data.frame
(
Estimates
=
Estimates
,
Parameters
=
cod
,
Type
=
type_cod
,
Response
=
response_cod
)
if
(
std.error
==
TRUE
)
{
coef_temp
<-
data.frame
(
Estimates
=
Estimates
,
Std.error
=
sqrt
(
diag
(
object
$
vcov
)),
Parameters
=
cod
,
Type
=
type_cod
,
Response
=
response_cod
)
}
output
<-
coef_temp
[
which
(
coef_temp
$
Response
%in%
response
&
coef_temp
$
Type
%in%
type
),
]
return
(
output
)
}
#' @title Confidence Intervals for mcglm
#' @name confint.mcglm
#'
#' @description Computes confidence intervals for parameters in a fitted
#' \code{mcglm} model.
#'
#' @param object a fitted \code{mcglm} object.
#' @param parm a specification of which parameters are to be given
#' confidence intervals, either a vector of number or a vector of
#' strings. If missing, all parameters are considered.
#' @param level the nominal confidence level.
#' @param ... additional arguments affecting the confidence interval
#' produced. Note that there is no extra options for \code{mcglm}
#' object class.
#'
#' @return A \code{data.frame} with confidence intervals, parameters
#' names, response number and parameters type.
#'
#' @method confint mcglm
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#' @export
confint.mcglm
<-
function
(
object
,
parm
,
level
=
0.95
,
...
)
{
temp
<-
coef
(
object
,
std.error
=
TRUE
)
if
(
missing
(
parm
))
{
parm
<-
1
:
length
(
temp
$
Estimates
)
}
a
<-
(
1
-
level
)
/
2
a
<-
c
(
a
,
1
-
a
)
fac
<-
qnorm
(
a
)
ci
<-
temp
$
Estimates
+
temp
$
Std.error
%o%
fac
colnames
(
ci
)
<-
paste0
(
format
(
a
,
2
),
"%"
)
rownames
(
ci
)
<-
temp
$
Parameters
return
(
ci
[
parm
])
}
#' @title Extract Model Fitted Values of McGLM
#' @name fitted.mcglm
#'
#' @description Extract fitted values for objects of \code{mcglm} class.
#'
#' @param object An object of \code{mcglm} class.
#' @param ... additional arguments affecting the summary produced. Note
#' that there is no extra options for \code{mcglm} object class.
#'
#' @return Depending on the number of response variable, the function
#' \code{fitted.mcglm} returns a vector (univariate models) or a
#' matrix (multivariate models) of fitted values.
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#'
#' @method fitted mcglm
#' @export
fitted.mcglm
<-
function
(
object
,
...
)
{
n_resp
<-
length
(
object
$
beta_names
)
output
<-
Matrix
(
object
$
fitted
,
ncol
=
n_resp
,
nrow
=
object
$
n_obs
)
return
(
output
)
}
#' @title Default Multivariate Covariance Generalized Linear models plotting
#' @name plot.mcglm
#'
#' @description takes a fitted \code{mcglm} object and do plots based on
#' residuals, influence diagnostic measures and algorithm check.
#'
#' @param x a fitted \code{mcglm} object.
#' @param type Specify which graphical analysis will be performed.
#' Options are: \code{"residuals"}, \code{"influence"} and
#' \code{"algorithm"}.
#' @param ... additional arguments affecting the plot produced. Note
#' that there is no extra options for mcglm object class.
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#'
#' @method plot mcglm
#' @export
plot.mcglm
<-
function
(
x
,
type
=
"residuals"
,
...
)
{
object
=
x
n_resp
<-
length
(
object
$
beta_names
)
if
(
type
==
"residuals"
)
{
par
(
mar
=
c
(
2.6
,
2.5
,
0.1
,
0.1
),
mgp
=
c
(
1.6
,
0.6
,
0
),
mfrow
=
c
(
2
,
n_resp
))
for
(
i
in
1
:
n_resp
)
{
res
<-
residuals
(
object
,
type
=
"pearson"
)[,
i
]
fit_values
<-
fitted
(
object
)[,
i
]
plot
(
res
~
fit_values
,
ylab
=
"Pearson residuals"
,
xlab
=
"Fitted values"
)
temp
<-
loess.smooth
(
fitted
(
object
)[,
i
],
residuals
(
object
,
type
=
"pearson"
)[,
i
])
lines
(
temp
$
x
,
temp
$
y
)
qqnorm
(
res
)
qqline
(
res
)
}
}
if
(
type
==
"algorithm"
)
{
n_iter
<-
length
(
na.exclude
(
object
$
IterationCovariance
[,
1
]))
par
(
mar
=
c
(
2.6
,
2.5
,
0.1
,
0.1
),
mgp
=
c
(
1.6
,
0.6
,
0
),
mfrow
=
c
(
2
,
2
))
matplot
(
object
$
IterationRegression
[
1
:
c
(
n_iter
+
5
),
],
type
=
"l"
,
lty
=
2
,
ylab
=
"Regression"
,
xlab
=
"Iterations"
)
matplot
(
object
$
IterationCovariance
[
1
:
c
(
n_iter
+
5
),
],
type
=
"l"
,
lty
=
2
,
ylab
=
"Covariance"
,
xlab
=
"Iterations"
)
matplot
(
object
$
ScoreRegression
[
1
:
c
(
n_iter
+
5
),
],
type
=
"l"
,
lty
=
2
,
ylab
=
"Quasi-score Regression"
,
xlab
=
"Iterations"
)
matplot
(
object
$
ScoreCovariance
[
1
:
c
(
n_iter
+
5
),
],
type
=
"l"
,
lty
=
2
,
ylab
=
"Quasi-score Covariance"
,
xlab
=
"Iterations"
)
}
if
(
type
==
"partial_residuals"
)
{
list_beta
<-
mc_updateBeta
(
list_initial
=
object
$
list_initial
,
betas
=
object
$
Regression
,
n_resp
=
n_resp
,
information
=
object
$
Information
)
comp_X
<-
list
()
for
(
i
in
1
:
n_resp
)
{
comp_X
[[
i
]]
<-
as.matrix
(
object
$
list_X
[[
i
]])
*
as.numeric
(
list_beta
$
regression
[[
i
]])
}
for
(
i
in
1
:
n_resp
)
{
res
<-
residuals
(
object
,
type
=
"pearson"
)[,
i
]
dev.new
()
n_cov
<-
dim
(
comp_X
[[
i
]])[
2
]
par
(
mar
=
c
(
2.6
,
2.5
,
0.5
,
0.5
),
mgp
=
c
(
1.6
,
0.6
,
0
),
mfrow
=
c
(
1
,
c
(
n_cov
-
1
)))
for
(
j
in
2
:
n_cov
)
{
p1
<-
comp_X
[[
i
]][,
j
]
+
res
plot
(
p1
~
object
$
list_X
[[
i
]][,
j
],
xlab
=
object
$
beta_names
[[
i
]][
j
],
ylab
=
"Partial residuals "
)
}
}
}
}
#' @title Print method for Multivariate Covariance Generalized Linear
#' Model
#' @name print.mcglm
#'
#' @description The default print method for a \code{mcglm} object.
#'
#' @param x fitted model objects of class mcglm as produced by mcglm().
#' @param ... further arguments passed to or from other methods.
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#'
#' @rdname print.mcglm
#' @method print mcglm
#' @export
print.mcglm
<-
function
(
x
,
...
)
{
object
<-
x
n_resp
<-
length
(
object
$
beta_names
)
regression
<-
mc_updateBeta
(
list_initial
=
list
(),
betas
=
object
$
Regression
,
information
=
object
$
Information
,
n_resp
=
n_resp
)
for
(
i
in
1
:
n_resp
)
{
cat
(
"Call: "
)
print
(
object
$
linear_pred
[[
i
]])
cat
(
"\n"
)
cat
(
"Link function:"
,
object
$
link
[[
i
]])
cat
(
"\n"
)
cat
(
"Variance function:"
,
object
$
variance
[[
i
]])
cat
(
"\n"
)
cat
(
"Covariance function:"
,
object
$
covariance
[[
i
]])
cat
(
"\n"
)
names
(
regression
[[
1
]][[
i
]])
<-
object
$
beta_names
[[
i
]]
cat
(
"Regression:\n"
)
print
(
regression
[[
1
]][[
i
]])
cat
(
"\n"
)
cat
(
"Dispersion:\n"
)
tau_temp
<-
coef
(
object
,
response
=
i
,
type
=
"tau"
)
$
Estimate
names
(
tau_temp
)
<-
rep
(
""
,
length
(
tau_temp
))
print
(
tau_temp
)
cat
(
"\n"
)
power_temp
<-
coef
(
object
,
response
=
i
,
type
=
"power"
)
$
Estimate
if
(
length
(
power_temp
)
!=
0
)
{
names
(
power_temp
)
<-
""
cat
(
"Power:\n"
)
print
(
power_temp
)
cat
(
"\n"
)
}
}
}
#' @title Residuals for Multivariate Covariance Generalized Linear
#' Models (McGLM)
#' @name residuals.mcglm
#'
#' @description Compute residuals based on fitting \code{mcglm} models.
#'
#' @param object An of class \code{mcglm}, typically the result of a
#' call to \code{mcglm}.
#' @param type the type of residuals which should be returned. The
#' alternatives are: \code{"raw"} (default), \code{"pearson"} and
#' \code{"standardized"}.
#' @param ... additional arguments affecting the residuals
#' produced. Note that there is no extra options for mcglm object
#' class.
#'
#' @return Depending on the number of response variable the function
#' \code{residuals.mcglm} returns a vector (univariate models) or a
#' matrix (multivariate models) of residuals values.
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#'
#' @method residuals mcglm
#' @export
residuals.mcglm
<-
function
(
object
,
type
=
"raw"
,
...
)
{
n_resp
<-
length
(
object
$
beta_names
)
output
<-
Matrix
(
object
$
residuals
,
ncol
=
n_resp
,
nrow
=
object
$
n_obs
)
if
(
type
==
"standardized"
)
{
output
<-
Matrix
(
as.numeric
(
object
$
residuals
%*%
chol
(
object
$
inv_C
)),
ncol
=
n_resp
,
nrow
=
object
$
n_obs
)
}
if
(
type
==
"pearson"
)
{
output
<-
Matrix
(
as.numeric
(
object
$
residuals
/
sqrt
(
diag
(
object
$
C
))),
ncol
=
n_resp
,
nrow
=
object
$
n_obs
)
}
return
(
output
)
}
#' @title Summarizing Multivariate Covariance Generalized Linear Models
#' fits.
#' @name summary.mcglm
#'
#' @description Summary for McGLMs objects.
#'
#' @param object an object of class \code{mcglm}, usually, a result of a
#' call to \code{mcglm}.
#' @param ... additional arguments affecting the summary produced. Note
#' the there is no extra options for mcglm object class.
#'
#' @return Print an \code{mcglm} object.
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#'
#' @method summary mcglm
#' @export
summary.mcglm
<-
function
(
object
,
...
)
{
n_resp
<-
length
(
object
$
beta_names
)
output
<-
list
()
for
(
i
in
1
:
n_resp
)
{
cat
(
"Call: "
)
print
(
object
$
linear_pred
[[
i
]])
cat
(
"\n"
)
cat
(
"Link function:"
,
object
$
link
[[
i
]])
cat
(
"\n"
)
cat
(
"Variance function:"
,
object
$
variance
[[
i
]])
cat
(
"\n"
)
cat
(
"Covariance function:"
,
object
$
covariance
[[
i
]])
cat
(
"\n"
)
cat
(
"Regression:\n"
)
tab_beta
<-
coef
(
object
,
std.error
=
TRUE
,
response
=
i
,
type
=
"beta"
)[,
1
:
2
]
tab_beta
$
"Z value"
<-
tab_beta
[,
1
]
/
tab_beta
[,
2
]
rownames
(
tab_beta
)
<-
object
$
beta_names
[[
i
]]
output
[
i
][[
1
]]
$
Regression
<-
tab_beta
print
(
tab_beta
)
cat
(
"\n"
)
tab_power
<-
coef
(
object
,
std.error
=
TRUE
,
response
=
i
,
type
=
"power"
)[,
1
:
2
]
tab_power
$
"Z value"
<-
tab_power
[,
1
]
/
tab_power
[,
2
]
rownames
(
tab_power
)
<-
NULL
if
(
dim
(
tab_power
)[
1
]
!=
0
)
{
cat
(
"Power:\n"
)
print
(
tab_power
)
output
[
i
][[
1
]]
$
Power
<-
tab_power
cat
(
"\n"
)
}
cat
(
"Dispersion:\n"
)
tab_tau
<-
coef
(
object
,
std.error
=
TRUE
,
response
=
i
,
type
=
"tau"
)[,
1
:
2
]
tab_tau
$
"Z value"
<-
tab_tau
[,
1
]
/
tab_tau
[,
2
]
rownames
(
tab_tau
)
<-
NULL
output
[
i
][[
1
]]
$
tau
<-
tab_tau
print
(
tab_tau
)
cat
(
"\n"
)
}
tab_rho
<-
coef
(
object
,
std.error
=
TRUE
,
response
=
NA
,
type
=
"correlation"
)[,
c
(
3
,
1
,
2
)]
tab_rho
$
"Z value"
<-
tab_rho
[,
2
]
/
tab_rho
[,
3
]
if
(
dim
(
tab_rho
)[
1
]
!=
0
)
{
cat
(
"Correlation matrix:\n"
)
print
(
tab_rho
)
cat
(
"\n"
)
}
names
(
object
$
con
$
correct
)
<-
""
iteration_cov
<-
length
(
na.exclude
(
object
$
IterationCovariance
[,
1
]))
names
(
iteration_cov
)
<-
""
names
(
object
$
con
$
method
)
<-
""
cat
(
"Algorithm:"
,
object
$
con
$
method
)
cat
(
"\n"
)
cat
(
"Correction:"
,
object
$
con
$
correct
)
cat
(
"\n"
)
cat
(
"Number iterations:"
,
iteration_cov
)
}
#' @title Calculate Variance-Covariance matrix for a fitted McGLM
#' object.
#' @name vcov.mcglm
#'
#' @description Returns the variance-covariance matrix for all
#' parameters of a \code{mcglm} fitted model object.
#'
#' @param object a fitted model \code{mcglm} object.
#' @param ... additional arguments affecting the summary produced. Note
#' that there is no extra options for mcglm object class.
#'
#' @return A variance-covariance matrix.
#'
#' @author Wagner Hugo Bonat, \email{wbonat@@ufpr.br}
#'
#' @method vcov mcglm
#' @export
vcov.mcglm
<-
function
(
object
,
...
)
{
cod
<-
coef
(
object
)
$
Parameters
colnames
(
object
$
vcov
)
<-
cod
rownames
(
object
$
vcov
)
<-
cod
return
(
object
$
vcov
)
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment