From 14c86c1285abb1640efa8ebbd702706cff3b9651 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Fri, 21 Mar 2025 16:28:04 +0800
Subject: [PATCH 01/25] Update k-component-graph-heavy-tail.R
Added a parameter w_max to control the maximum value of the weights to avoid strong connections leading to isolated nodes
---
R/k-component-graph-heavy-tail.R | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/R/k-component-graph-heavy-tail.R b/R/k-component-graph-heavy-tail.R
index b0310aa..44814ec 100644
--- a/R/k-component-graph-heavy-tail.R
+++ b/R/k-component-graph-heavy-tail.R
@@ -54,7 +54,8 @@ learn_kcomp_heavytail_graph <- function(X,
maxiter = 10000,
reltol = 1e-5,
verbose = TRUE,
- record_objective = FALSE) {
+ record_objective = FALSE
+ wmax = 0.05) {
X <- scale(as.matrix(X))
# number of nodes
p <- ncol(X)
@@ -105,6 +106,9 @@ learn_kcomp_heavytail_graph <- function(X,
eta <- 1 / (2*rho * (2*p - 1))
wi <- w - eta * grad
wi[wi < 0] <- 0
+
+ # Adding the following simple line of code avoids very strong connections that lead to isolated nodes
+ wi <- pmin(wi, wmax, na.rm = TRUE)
Lwi <- L(wi)
# update U
U <- eigen(Lwi, symmetric = TRUE)$vectors[, (p - k + 1):p]
From c587e0d5d889a8cfdd8dacc7f869399fceb88b42 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:23:37 +0800
Subject: [PATCH 02/25] Update README.md
---
README.md | 54 ++++++++++++++++++++++++++----------------------------
1 file changed, 26 insertions(+), 28 deletions(-)
diff --git a/README.md b/README.md
index e7ab191..ca19577 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,5 @@
-# fingraph
-[](https://app.codecov.io/gh/convexfi/fingraph)
-
-
-This repo contains ADMM implementations to estimate weighted undirected graphs
-(Markov random fields) under Student-t assumptions with applications to financial
-markets.
+This repo, forked from **fingraph**, contains the R code for balanced undirected graph learning from data
+with Student-t distribution applied to financial data clustering.
## Installation
@@ -50,11 +45,11 @@ colnames(crypto_prices)
log_returns <- diff(log(crypto_prices), na.pad = FALSE)
# estimate a weighted, undirected graph (markov random field)
-graph_mrf <- learn_kcomp_heavytail_graph(scale(log_returns),
- k = 8,
- heavy_type = "student",
- nu = fit_mvt(scale(log_returns))$nu,
- verbose = FALSE)
+graph_mrf <- learn_kcomp_heavytail_graph_balanced(scale(log_returns),
+ k = 8,
+ heavy_type = "student",
+ nu = fit_mvt(scale(log_returns))$nu,
+ verbose = FALSE)
# plot network
net <- graph_from_adjacency_matrix(graph_mrf$adjacency,
@@ -110,12 +105,12 @@ colnames(stock_prices)
log_returns <- diff(log(stock_prices), na.pad = FALSE)
# estimate a weighted, undirected graph (markov random field)
-graph_mrf <- learn_kcomp_heavytail_graph(scale(log_returns),
- rho = 10,
- k = 3,
- heavy_type = "student",
- nu = fit_mvt(scale(log_returns))$nu,
- verbose = FALSE)
+graph_mrf <- learn_kcomp_heavytail_graph_balanced(scale(log_returns),
+ rho = 10,
+ k = 3,
+ heavy_type = "student",
+ nu = fit_mvt(scale(log_returns))$nu,
+ verbose = FALSE)
#> Warning in tclass.xts(x): index does not have a 'tclass' attribute
#> Warning in tclass.xts(x): index does not have a 'tclass' attribute
@@ -153,15 +148,18 @@ plot(net, vertex.label = colnames(stock_prices),
## Citation
If you made use of this software please consider citing:
-- [Cardoso JVM](https://mirca.github.io), [Ying J](https://github.com/jxying),
- [Palomar DP](https://www.danielppalomar.com) (2021).
+- ## Citation
+Please cite:
+
+- [A Javaheri](https://javaheriamirhossein.github.io/), [JVM Cardoso](https://mirca.github.io), and
+ [DP Palomar](https://www.danielppalomar.com)
+ [Graph Learning for Balanced Clustering of Heavy-Tailed Data]([https://papers.nips.cc/paper/2021/hash/a64a034c3cb8eac64eb46ea474902797-Abstract.html](https://ieeexplore.ieee.org/abstract/document/10403460)).
+ [2023 IEEE 9th International Workshop on Computational Advances in Multi-Sensor Adaptive Processing]([https://neurips.cc/Conferences/2021](https://ieeexplore.ieee.org/xpl/conhome/10402605/proceeding)) (CAMSAP 2023).
+
+- [JVM Cardoso](https://mirca.github.io), [J Ying](https://github.com/jxying),
+ [DP Palomar](https://www.danielppalomar.com) (2021).
[Graphical Models in Heavy-Tailed Markets](https://papers.nips.cc/paper/2021/hash/a64a034c3cb8eac64eb46ea474902797-Abstract.html).
- [Advances in Neural Information Processing Systems](https://neurips.cc/Conferences/2021) (NeurIPS’21).
-
-## Links
-- [RFinance'23 Slides](https://github.com/mirca/rfinance-talk/blob/main/rfinance.pdf)
-- [NeurIPS’21 Slides](https://palomar.home.ece.ust.hk/papers/2021/CardosoYingPalomar-NeurIPS2021-slides.pdf)
-- [NeurIPS'21 Poster](https://palomar.home.ece.ust.hk/papers/2021/CardosoYingPalomar-NeurIPS2021-poster.png)
-- [NeurIPS'21 Supplementary Material](https://palomar.home.ece.ust.hk/papers/2021/CardosoYingPalomar-NeurIPS2021-supplemental.pdf)
-- [CRAN Package](https://cran.r-project.org/package=fingraph)
+ [Advances in Neural Information Processing Systems](https://neurips.cc/Conferences/2021) (NeurIPS 2021).
+
+
From f45c8351fcce0ea004fdeab451aca79b0e90a59b Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:25:29 +0800
Subject: [PATCH 03/25] Update README.md
---
README.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index ca19577..e27a1f4 100644
--- a/README.md
+++ b/README.md
@@ -151,13 +151,13 @@ If you made use of this software please consider citing:
- ## Citation
Please cite:
-- [A Javaheri](https://javaheriamirhossein.github.io/), [JVM Cardoso](https://mirca.github.io), and
- [DP Palomar](https://www.danielppalomar.com)
+- [A Javaheri](https://javaheriamirhossein.github.io/), [JVM Cardoso](https://mirca.github.io) and
+ [DP Palomar](https://www.danielppalomar.com),
[Graph Learning for Balanced Clustering of Heavy-Tailed Data]([https://papers.nips.cc/paper/2021/hash/a64a034c3cb8eac64eb46ea474902797-Abstract.html](https://ieeexplore.ieee.org/abstract/document/10403460)).
- [2023 IEEE 9th International Workshop on Computational Advances in Multi-Sensor Adaptive Processing]([https://neurips.cc/Conferences/2021](https://ieeexplore.ieee.org/xpl/conhome/10402605/proceeding)) (CAMSAP 2023).
+ [2023 IEEE 9th International Workshop on Computational Advances in Multi-Sensor Adaptive Processing](https://ieeexplore.ieee.org/xpl/conhome/10402605/proceeding) (CAMSAP 2023).
-- [JVM Cardoso](https://mirca.github.io), [J Ying](https://github.com/jxying),
- [DP Palomar](https://www.danielppalomar.com) (2021).
+- [JVM Cardoso](https://mirca.github.io), [J Ying](https://github.com/jxying) and
+ [DP Palomar](https://www.danielppalomar.com),
[Graphical Models in Heavy-Tailed Markets](https://papers.nips.cc/paper/2021/hash/a64a034c3cb8eac64eb46ea474902797-Abstract.html).
[Advances in Neural Information Processing Systems](https://neurips.cc/Conferences/2021) (NeurIPS 2021).
From d267497017f8f93fe1f6f9de5654ac39661e82dc Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:51:35 +0800
Subject: [PATCH 04/25] Update README.md
---
README.md | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/README.md b/README.md
index e27a1f4..f095474 100644
--- a/README.md
+++ b/README.md
@@ -3,18 +3,10 @@ with Student-t distribution applied to financial data clustering.
## Installation
-**fingraph** depends on the development version of **spectralGraphTopology**,
-which can be installed as:
-
```r
> devtools::install_github("convexfi/spectralGraphTopology")
-```
-
-The stable version of **fingraph** can be installed directly from CRAN:
+> devtools::install_github("javaheriamirhossein/Balanced-Financial-Graph")
-```r
-> install.packages("fingraph")
-```
#### Microsoft Windows
On MS Windows environments, make sure to install the most recent version of ``Rtools``.
From 04ada8a22368712d6063104a879621557f29e4f1 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:51:54 +0800
Subject: [PATCH 05/25] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f095474..a19720b 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ with Student-t distribution applied to financial data clustering.
```r
> devtools::install_github("convexfi/spectralGraphTopology")
> devtools::install_github("javaheriamirhossein/Balanced-Financial-Graph")
-
+```
#### Microsoft Windows
On MS Windows environments, make sure to install the most recent version of ``Rtools``.
From 85f3a9ffdaa57969a7ab6af6c2f3daab37545d42 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:52:36 +0800
Subject: [PATCH 06/25] Update README.md
---
README.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/README.md b/README.md
index a19720b..210d975 100644
--- a/README.md
+++ b/README.md
@@ -137,10 +137,8 @@ plot(net, vertex.label = colnames(stock_prices),
-## Citation
-If you made use of this software please consider citing:
-- ## Citation
+## Citation
Please cite:
- [A Javaheri](https://javaheriamirhossein.github.io/), [JVM Cardoso](https://mirca.github.io) and
From c9c9c403f2c40f7a2a8fa19443b65b122cbb060e Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:54:28 +0800
Subject: [PATCH 07/25] Create learn_kcomp_heavytail_graph_balanced.R
---
R/learn_kcomp_heavytail_graph_balanced.R | 230 +++++++++++++++++++++++
1 file changed, 230 insertions(+)
create mode 100644 R/learn_kcomp_heavytail_graph_balanced.R
diff --git a/R/learn_kcomp_heavytail_graph_balanced.R b/R/learn_kcomp_heavytail_graph_balanced.R
new file mode 100644
index 0000000..7697dd9
--- /dev/null
+++ b/R/learn_kcomp_heavytail_graph_balanced.R
@@ -0,0 +1,230 @@
+library(spectralGraphTopology)
+
+#' @title Laplacian matrix of a balanced k-component graph with heavy-tailed data
+#'
+#' Computes the Laplacian matrix of a balanced graph on the basis of an observed data matrix,
+#' where we assume the data to be Student-t distributed.
+#'
+#' @param X an n x p data matrix, where n is the number of observations and p is
+#' the number of nodes in the graph.
+#' @param k the number of components of the graph.
+#' @param heavy_type a string which selects the statistical distribution of the data .
+#' Valid values are "gaussian" or "student".
+#' @param nu the degrees of freedom of the Student-t distribution.
+#' Must be a real number greater than 2.
+#' @param w0 initial vector of graph weights. Either a vector of length p(p-1)/2 or
+#' a string indicating the method to compute an initial value.
+#' @param beta hyperparameter that controls the regularization to obtain a
+#' k-component graph
+#' @param update_beta whether to update beta during the optimization.
+#' @param alpha spare regularization term parameter
+#' @param t upperbound scale parameter
+#' @param d the nodes' degrees. Either a vector or a single value.
+#' @param rho ADMM hyperparameter.
+#' @param update_rho whether or not to update rho during the optimization.
+#' @param maxiter maximum number of iterations.
+#' @param reltol relative tolerance as a convergence criteria.
+#' @param verbose whether or not to show a progress bar during the iterations.
+#' @export
+#' @import spectralGraphTopology
+learn_kcomp_heavytail_graph_balanced <- function(X,
+ alpha = 0.1,
+ k = 1,
+ heavy_type = "gaussian",
+ nu = NULL,
+ w0 = "naive",
+ d = 1,
+ t = 1.2,
+ beta = 1e-8,
+ update_beta = TRUE,
+ early_stopping = FALSE,
+ rho = 1,
+ update_rho = FALSE,
+ maxiter = 10000,
+ reltol = 1e-5,
+ verbose = TRUE,
+ record_objective = FALSE) {
+
+ X <- scale(as.matrix(X))
+ # number of nodes
+ p <- ncol(X)
+
+ t <- t*d/sqrt(p-1)
+ # number of observations
+ n <- nrow(X)
+ LstarSq <- vector(mode = "list", length = n)
+ for (i in 1:n)
+ LstarSq[[i]] <- Lstar(X[i, ] %*% t(X[i, ])) / n
+ # w-initialization
+ if (assertthat::is.string(w0)) {
+ w <- spectralGraphTopology:::w_init(w0, MASS::ginv(cor(X)))
+ A0 <- A(w)
+ A0 <- A0 / rowSums(A0)
+ w <- spectralGraphTopology:::Ainv(A0)
+ }
+ else {
+ w <-w0
+ }
+ # Theta-initilization
+ Lw <- L(w)
+ Aw <- A(w)
+ Theta <- Lw
+ U <- eigen(Lw, symmetric = TRUE)$vectors[, (p - k + 1):p]
+ Y <- matrix(0, p, p)
+ y <- rep(0, p)
+ # Z-initialization
+ Lambda <- matrix(0, p, p)
+ Z <- A0
+
+ # ADMM constants
+ mu <- 2
+ tau <- 2
+ # residual vectors
+ primal_lap_residual <- c()
+ primal_deg_residual <- c()
+ dual_residual <- c()
+ # augmented lagrangian vector
+ lagrangian <- c()
+ beta_seq <- c()
+ if (verbose)
+ pb <- progress::progress_bar$new(format = "<:bar> :current/:total eta: :eta",
+ total = maxiter, clear = FALSE, width = 80)
+ elapsed_time <- c()
+ start_time <- proc.time()[3]
+ for (i in 1:maxiter) {
+
+ for (j in 1:1){
+ # update w
+ LstarLw <- Lstar(Lw)
+ DstarDw <- Dstar(diag(Lw))
+ LstarSweighted <- rep(0, .5*p*(p-1))
+ if (heavy_type == "student") {
+ for (q in 1:n)
+ LstarSweighted <- LstarSweighted + LstarSq[[q]] * compute_student_weights(w, LstarSq[[q]], p, nu)
+ } else if (heavy_type == "gaussian") {
+ for (q in 1:n)
+ LstarSweighted <- LstarSweighted + LstarSq[[q]]
+ }
+ grad <- LstarSweighted + Lstar(beta * crossprod(t(U)) - Y - rho * Theta) + Dstar(y - rho * d) + rho * (LstarLw + DstarDw)
+ grad <- grad + Astar( rho * (Aw-Z) + Lambda )
+ eta <- 1 / (2*rho * (2*p - 1)+ 2*rho)
+ wi <- w - eta * grad
+ thr <- sqrt(2*alpha *eta )
+ wi[wi< thr] <- 0
+ Lwi <- L(wi)
+ Awi <- A(wi)
+ }
+
+ # Update Z
+ Z <- Awi + Lambda/rho
+ for (i in 1:p){
+ norm_i <- norm(Z[i,], type="2")
+ if (norm_i>t) {
+ Z[i,] <- Z[i,]/norm_i *t
+ }
+ }
+
+
+ # update U
+ U <- eigen(Lwi, symmetric = TRUE)$vectors[, (p - k + 1):p]
+
+ # update Theta
+ eig <- eigen(rho * Lwi - Y, symmetric = TRUE)
+ V <- eig$vectors[,1:(p-k)]
+ gamma <- eig$values[1:(p-k)]
+ Thetai <- V %*% diag((gamma + sqrt(gamma^2 + 4 * rho)) / (2 * rho)) %*% t(V)
+
+ # update Y
+ R1 <- Thetai - Lwi
+ Y <- Y + rho * R1
+
+ # update y
+ R2 <- diag(Lwi) - d
+ y <- y + rho * R2
+
+ R3 = Awi - Z
+ Lambda <- Lambda + rho * R3
+ # compute primal, dual residuals, & lagrangian
+ primal_lap_residual <- c(primal_lap_residual, norm(R1, "F"))
+ primal_deg_residual <- c(primal_deg_residual, norm(R2, "2"))
+ dual_residual <- c(dual_residual, rho*norm(Lstar(Theta - Thetai), "2"))
+ lagrangian <- c(lagrangian, compute_augmented_lagrangian_kcomp_ht_balanced(wi, LstarSq, Thetai, U, Y, y, d, heavy_type, n, p, k, rho, beta, nu, alpha, Z, Lambda))
+
+ # update rho
+ if (update_rho) {
+ eig_vals <- spectralGraphTopology:::eigval_sym(Theta)
+ n_zero_eigenvalues <- sum(eig_vals < 1e-9)
+ if (k < n_zero_eigenvalues)
+ rho <- .5 * rho
+ else if (k > n_zero_eigenvalues)
+ rho <- 2 * rho
+ else {
+ if (early_stopping) {
+ has_converged <- TRUE
+ break
+ }
+ }
+ }
+ if (update_beta) {
+ eig_vals <- spectralGraphTopology:::eigval_sym(L(wi))
+ n_zero_eigenvalues <- sum(eig_vals < 1e-9)
+ if (k < n_zero_eigenvalues)
+ beta <- .5 * beta
+ else if (k > n_zero_eigenvalues)
+ beta <- 2 * beta
+ else {
+ if (early_stopping) {
+ has_converged <- TRUE
+ break
+ }
+ }
+ beta_seq <- c(beta_seq, beta)
+ }
+ if (verbose)
+ pb$tick()
+
+ elapsed_time <- c(elapsed_time, proc.time()[3] - start_time)
+ has_converged <- (norm(Lwi - Lw, 'F') / norm(Lw, 'F') < reltol) && (i > 1)
+ if (has_converged)
+ break
+ w <- wi
+ Lw <- Lwi
+ Aw <- Awi
+
+ Theta <- Thetai
+ }
+ results <- list(laplacian = L(wi), adjacency = A(wi), theta = Thetai, maxiter = i,
+ convergence = has_converged, beta_seq = beta_seq,
+ primal_lap_residual = primal_lap_residual,
+ primal_deg_residual = primal_deg_residual,
+ dual_residual = dual_residual,
+ lagrangian = lagrangian,
+ elapsed_time = elapsed_time)
+ return(results)
+}
+
+compute_augmented_lagrangian_kcomp_ht_balanced <- function(w, LstarSq, Theta, U, Y, y, d, heavy_type, n, p, k, rho, beta, nu, alpha, Z, Lambda) {
+ eig <- eigen(Theta, symmetric = TRUE, only.values = TRUE)$values[1:(p-k)]
+ Lw <- L(w)
+ Aw <- A(w)
+ Dw <- diag(Lw)
+ u_func <- 0
+ if (heavy_type == "student") {
+ for (q in 1:n)
+ u_func <- u_func + (p + nu) * log(1 + n * sum(w * LstarSq[[q]]) / nu)
+ } else if (heavy_type == "gaussian"){
+ for (q in 1:n)
+ u_func <- u_func + sum(n * w * LstarSq[[q]])
+ }
+ u_func <- u_func / n
+ return(u_func - sum(log(eig)) + sum(y * (Dw - d)) + sum(diag(Y %*% (Theta - Lw)))
+ + .5 * rho * (norm(Dw - d, "2")^2 + norm(Lw - Theta, "F")^2) + beta * sum(w * Lstar(crossprod(t(U))))
+ + .5 * rho * norm(Aw - Z, "2")^2 + sum(diag(Lambda %*% (Aw - Z)))
+ + alpha * sum(w>0) )
+}
+
+hardThresh <- function(v, thr){
+
+ return( v * (abs(v) > thr) )
+
+}
From 908addd721d880ed04255de0d822a76df4c79cba Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:55:59 +0800
Subject: [PATCH 08/25] Delete README.html
---
README.html | 568 ----------------------------------------------------
1 file changed, 568 deletions(-)
delete mode 100644 README.html
diff --git a/README.html b/README.html
deleted file mode 100644
index d0d7438..0000000
--- a/README.html
+++ /dev/null
@@ -1,568 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-fingraph README
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
fingraph
-

-
This repo contains ADMM implementations to estimate weighted undirected graphs (Markov random fields) under Student-t assumptions with applications to financial markets.
-
-
Installation
-
fingraph depends on the development version of spectralGraphTopology, which can be installed as:
-
> devtools::install_github("convexfi/spectralGraphTopology")
-
The stable version of fingraph can be installed directly from CRAN:
-
> install.packages("fingraph")
-
-
Microsoft Windows
-
On MS Windows environments, make sure to install the most recent version of Rtools.
-
-
-
-
Usage
-
-
Learning a graph of cryptocurrencies
-
library(igraph)
-library(fingraph)
-library(fitHeavyTail)
-library(xts)
-set.seed(123)
-
-# load crypto prices into an xts table
-crypto_prices <- readRDS("examples/crypto/crypto-prices.rds")
-colnames(crypto_prices)
-#> [1] "BTC" "ETH" "USDT" "BNB" "USDC" "XRP"
-#> [7] "ADA" "HEX" "DOGE" "SOL" "MATIC" "DOT"
-#> [13] "TRX" "LTC" "BUSD" "SHIB" "AVAX" "DAI"
-#> [19] "LEO" "LINK" "ATOM" "UNI7083" "XMR" "OKB"
-#> [25] "ETC" "TON11419" "XLM" "BCH" "ICP" "CNX"
-#> [31] "TUSD" "FIL" "HBAR" "CRO" "LDO" "NEAR"
-#> [37] "VET" "QNT" "ALGO" "USDP" "FTM" "GRT6719"
-
-# compute log-returns
-log_returns <- diff(log(crypto_prices), na.pad = FALSE)
-
-# estimate a weighted, undirected graph (markov random field)
-graph_mrf <- learn_kcomp_heavytail_graph(scale(log_returns),
- k = 8,
- heavy_type = "student",
- nu = fit_mvt(scale(log_returns))$nu,
- verbose = FALSE)
-
-# plot network
-net <- graph_from_adjacency_matrix(graph_mrf$adjacency,
- mode = "undirected",
- weighted = TRUE)
-cfg <- cluster_fast_greedy(as.undirected(net))
-la_kcomp <- layout_nicely(net)
-V(net)$label.cex = 1
-plot(cfg, net, vertex.label = colnames(crypto_prices),
- layout = la_kcomp,
- vertex.size = 4.5,
- col = "black",
- edge.color = c("#686de0"),
- vertex.label.family = "Helvetica",
- vertex.label.color = "black",
- vertex.label.dist = 1.25,
- vertex.shape = "circle",
- edge.width = 20*E(net)$weight,
- edge.curved = 0.1)
-

-
-
-
Learning a network of S&P500 stocks
-
library(xts)
-library(igraph)
-library(fingraph)
-library(fitHeavyTail)
-library(readr)
-set.seed(123)
-
-# load table w/ stocks and their sectors
-SP500 <- read_csv("examples/stocks/SP500-sectors.csv")
-
-# load stock prices into an xts table
-stock_prices <- readRDS("examples/stocks/stock-data-2014-2018.rds")
-colnames(stock_prices)
-#> [1] "AEE" "AEP" "AES" "AIV" "AMT" "ARE" "ATO" "ATVI" "AVB"
-#> [10] "AWK" "BXP" "CBRE" "CCI" "CHTR" "CMCSA" "CMS" "CNP" "CTL"
-#> [19] "D" "DIS" "DISCA" "DISCK" "DISH" "DLR" "DRE" "DTE" "DUK"
-#> [28] "EA" "ED" "EIX" "EQIX" "EQR" "ES" "ESS" "ETR" "EVRG"
-#> [37] "EXC" "EXR" "FB" "FE" "FRT" "GOOG" "GOOGL" "HST" "IPG"
-#> [46] "IRM" "KIM" "LNT" "LYV" "MAA" "NEE" "NFLX" "NI" "NRG"
-#> [55] "NWS" "NWSA" "O" "OMC" "PEAK" "PEG" "PLD" "PNW" "PPL"
-#> [64] "PSA" "REG" "SBAC" "SLG" "SO" "SPG" "SRE" "T" "TMUS"
-#> [73] "TTWO" "TWTR" "UDR" "VNO" "VTR" "VZ" "WEC" "WELL" "WY"
-#> [82] "XEL"
-
-# compute log-returns
-log_returns <- diff(log(stock_prices), na.pad = FALSE)
-
-# estimate a weighted, undirected graph (markov random field)
-graph_mrf <- learn_kcomp_heavytail_graph(scale(log_returns),
- rho = 10,
- k = 3,
- heavy_type = "student",
- nu = fit_mvt(scale(log_returns))$nu,
- verbose = FALSE)
-#> Warning in tclass.xts(x): index does not have a 'tclass' attribute
-
-#> Warning in tclass.xts(x): index does not have a 'tclass' attribute
-
-# map stock names and sectors
-stock_sectors <- c(SP500$GICS.Sector[SP500$Symbol %in% colnames(stock_prices)])
-stock_sectors_index <- as.numeric(as.factor(stock_sectors))
-
-# plot network
-net <- graph_from_adjacency_matrix(graph_mrf$adjacency,
- mode = "undirected",
- weighted = TRUE)
-la_kcomp <- layout_nicely(net)
-V(net)$label.cex = 1
-colors <- c("#FD7272", "#55E6C1", "#25CCF7")
-V(net)$color <- colors[stock_sectors_index]
-V(net)$type <- stock_sectors_index
-V(net)$cluster <- stock_sectors_index
-E(net)$color <- apply(as.data.frame(get.edgelist(net)), 1,
- function(x) ifelse(V(net)$cluster[x[1]] == V(net)$cluster[x[2]],
- colors[V(net)$cluster[x[1]]], 'grey'))
-plot(net, vertex.label = colnames(stock_prices),
- layout = la_kcomp,
- vertex.size = 4.5,
- vertex.label.family = "Helvetica",
- vertex.label.dist = 1.25,
- vertex.label.color = "black",
- vertex.shape = "circle",
- edge.width = 20*E(net)$weight,
- edge.curved = 0.1)
-

-
-
-
-
Citation
-
If you made use of this software please consider citing:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 996973be9d67eb088f5ece9893995face5921c68 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:58:24 +0800
Subject: [PATCH 09/25] Delete README.Rmd
---
README.Rmd | 160 -----------------------------------------------------
1 file changed, 160 deletions(-)
delete mode 100644 README.Rmd
diff --git a/README.Rmd b/README.Rmd
deleted file mode 100644
index 098bf5e..0000000
--- a/README.Rmd
+++ /dev/null
@@ -1,160 +0,0 @@
----
-title: "fingraph README"
-output:
- html_document:
- keep_md: true
----
-
-```{r, echo = FALSE}
-library(knitr)
-opts_chunk$set(
- collapse = TRUE,
- comment = "#>",
- fig.path = "man/figures/README-",
- fig.align = "center",
- fig.retina = 2,
- out.width = "75%",
- dpi = 96
-)
-knit_hooks$set(pngquant = hook_pngquant)
-```
-
-# fingraph
-[](https://app.codecov.io/gh/convexfi/fingraph)
-
-
-This repo contains ADMM implementations to estimate weighted undirected graphs
-(Markov random fields) under Student-t assumptions with applications to financial
-markets.
-
-## Installation
-
-**fingraph** depends on the development version of **spectralGraphTopology**,
-which can be installed as:
-```{r, eval = FALSE}
-> devtools::install_github("convexfi/spectralGraphTopology")
-```
-
-The stable version of **fingraph** can be installed directly from CRAN:
-```{r, eval = FALSE}
-> install.packages("fingraph")
-```
-
-#### Microsoft Windows
-On MS Windows environments, make sure to install the most recent version of ``Rtools``.
-
-## Usage
-
-### Learning a graph of cryptocurrencies
-```{r plot_crypto_network, message=FALSE}
-library(igraph)
-library(fingraph)
-library(fitHeavyTail)
-library(xts)
-set.seed(123)
-
-# load crypto prices into an xts table
-crypto_prices <- readRDS("examples/crypto/crypto-prices.rds")
-colnames(crypto_prices)
-
-# compute log-returns
-log_returns <- diff(log(crypto_prices), na.pad = FALSE)
-
-# estimate a weighted, undirected graph (markov random field)
-graph_mrf <- learn_kcomp_heavytail_graph(scale(log_returns),
- k = 8,
- heavy_type = "student",
- nu = fit_mvt(scale(log_returns))$nu,
- verbose = FALSE)
-
-# plot network
-net <- graph_from_adjacency_matrix(graph_mrf$adjacency,
- mode = "undirected",
- weighted = TRUE)
-cfg <- cluster_fast_greedy(as.undirected(net))
-la_kcomp <- layout_nicely(net)
-V(net)$label.cex = 1
-plot(cfg, net, vertex.label = colnames(crypto_prices),
- layout = la_kcomp,
- vertex.size = 4.5,
- col = "black",
- edge.color = c("#686de0"),
- vertex.label.family = "Helvetica",
- vertex.label.color = "black",
- vertex.label.dist = 1.25,
- vertex.shape = "circle",
- edge.width = 20*E(net)$weight,
- edge.curved = 0.1)
-```
-
-
-### Learning a network of S&P500 stocks
-```{r plot_sp500_stocks_network, message=FALSE}
-library(xts)
-library(igraph)
-library(fingraph)
-library(fitHeavyTail)
-library(readr)
-set.seed(123)
-
-# load table w/ stocks and their sectors
-SP500 <- read_csv("examples/stocks/SP500-sectors.csv")
-
-# load stock prices into an xts table
-stock_prices <- readRDS("examples/stocks/stock-data-2014-2018.rds")
-colnames(stock_prices)
-
-# compute log-returns
-log_returns <- diff(log(stock_prices), na.pad = FALSE)
-
-# estimate a weighted, undirected graph (markov random field)
-graph_mrf <- learn_kcomp_heavytail_graph(scale(log_returns),
- rho = 10,
- k = 3,
- heavy_type = "student",
- nu = fit_mvt(scale(log_returns))$nu,
- verbose = FALSE)
-
-# map stock names and sectors
-stock_sectors <- c(SP500$GICS.Sector[SP500$Symbol %in% colnames(stock_prices)])
-stock_sectors_index <- as.numeric(as.factor(stock_sectors))
-
-# plot network
-net <- graph_from_adjacency_matrix(graph_mrf$adjacency,
- mode = "undirected",
- weighted = TRUE)
-la_kcomp <- layout_nicely(net)
-V(net)$label.cex = 1
-colors <- c("#FD7272", "#55E6C1", "#25CCF7")
-V(net)$color <- colors[stock_sectors_index]
-V(net)$type <- stock_sectors_index
-V(net)$cluster <- stock_sectors_index
-E(net)$color <- apply(as.data.frame(get.edgelist(net)), 1,
- function(x) ifelse(V(net)$cluster[x[1]] == V(net)$cluster[x[2]],
- colors[V(net)$cluster[x[1]]], 'grey'))
-plot(net, vertex.label = colnames(stock_prices),
- layout = la_kcomp,
- vertex.size = 4.5,
- vertex.label.family = "Helvetica",
- vertex.label.dist = 1.25,
- vertex.label.color = "black",
- vertex.shape = "circle",
- edge.width = 20*E(net)$weight,
- edge.curved = 0.1)
-```
-
-## Citation
-If you made use of this software please consider citing:
-
-- [Cardoso JVM](https://mirca.github.io), [Ying J](https://github.com/jxying),
- [Palomar DP](https://www.danielppalomar.com) (2021).
- [Graphical Models in Heavy-Tailed Markets](https://papers.nips.cc/paper/2021/hash/a64a034c3cb8eac64eb46ea474902797-Abstract.html).
- [Advances in Neural Information Processing Systems](https://neurips.cc/Conferences/2021) (NeurIPS’21).
-
-## Links
-- [RFinance'23 Slides](https://github.com/mirca/rfinance-talk/blob/main/rfinance.pdf)
-- [NeurIPS’21 Slides](https://palomar.home.ece.ust.hk/papers/2021/CardosoYingPalomar-NeurIPS2021-slides.pdf)
-- [NeurIPS'21 Poster](https://palomar.home.ece.ust.hk/papers/2021/CardosoYingPalomar-NeurIPS2021-poster.png)
-- [NeurIPS'21 Supplementary Material](https://palomar.home.ece.ust.hk/papers/2021/CardosoYingPalomar-NeurIPS2021-supplemental.pdf)
-- [CRAN Package](https://cran.r-project.org/package=fingraph)
-
From fb9a7eb4c045833eaffa760c3d1aeb8a52b0ff4a Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 00:59:59 +0800
Subject: [PATCH 10/25] Update NAMESPACE
---
NAMESPACE | 1 +
1 file changed, 1 insertion(+)
diff --git a/NAMESPACE b/NAMESPACE
index 1cdcb15..54c60b3 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -2,5 +2,6 @@
export(learn_connected_graph)
export(learn_kcomp_heavytail_graph)
+export(learn_kcomp_heavytail_graph_balanced)
export(learn_regular_heavytail_graph)
import(spectralGraphTopology)
From 384680bc83d0d7db72531ee6a1bc3f92b68c2727 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:04:43 +0800
Subject: [PATCH 11/25] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 210d975..5000db8 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ with Student-t distribution applied to financial data clustering.
```r
> devtools::install_github("convexfi/spectralGraphTopology")
-> devtools::install_github("javaheriamirhossein/Balanced-Financial-Graph")
+> devtools::install_github("javaheriamirhossein/balanced-fingraph")
```
#### Microsoft Windows
From 713f72b554debf76afcc24c5d7aeb20376b988ae Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:06:06 +0800
Subject: [PATCH 12/25] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 5000db8..5040c3e 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
This repo, forked from **fingraph**, contains the R code for balanced undirected graph learning from data
with Student-t distribution applied to financial data clustering.
-## Installation
+## Installation in R
```r
-> devtools::install_github("convexfi/spectralGraphTopology")
+> install.packages(spectralGraphTopology)
> devtools::install_github("javaheriamirhossein/balanced-fingraph")
```
From 679473b1b0cc7a7f200068c0229a44b642bea196 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:10:41 +0800
Subject: [PATCH 13/25] Update DESCRIPTION
---
DESCRIPTION | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index 74dca6c..adf4955 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -8,8 +8,7 @@ Description: Learning graphs for financial markets with optimization algorithms.
"Learning graphs in heavy-tailed markets", Advances in Neural Informations Processing Systems (NeurIPS).
Authors@R: c(
person("Ze", "Vinicius", role = c("cre", "aut"), email = "jvmirca@gmail.com"),
- person("Daniel", "Palomar", role = c("cre", "aut"), email = "daniel.p.palomar@gmail.com"),
- )
+ person("Daniel", "Palomar", role = c("cre", "aut"), email = "daniel.p.palomar@gmail.com"))
URL: https://github.com/convexfi/fingraph/
BugReports: https://github.com/convexfi/fingraph/issues
License: GPL-3
From 0a97fd2836182a57606465b8dc79c9a20e8896c2 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:23:00 +0800
Subject: [PATCH 14/25] Update NAMESPACE
---
NAMESPACE | 1 +
1 file changed, 1 insertion(+)
diff --git a/NAMESPACE b/NAMESPACE
index 54c60b3..054b866 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -5,3 +5,4 @@ export(learn_kcomp_heavytail_graph)
export(learn_kcomp_heavytail_graph_balanced)
export(learn_regular_heavytail_graph)
import(spectralGraphTopology)
+importFrom(Rcpp,sourceCpp)
From f02c5797f47c82a8356542d340a7edeb42197cdd Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:28:07 +0800
Subject: [PATCH 15/25] Update k-component-graph-heavy-tail.R
---
R/k-component-graph-heavy-tail.R | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/R/k-component-graph-heavy-tail.R b/R/k-component-graph-heavy-tail.R
index 44814ec..b0310aa 100644
--- a/R/k-component-graph-heavy-tail.R
+++ b/R/k-component-graph-heavy-tail.R
@@ -54,8 +54,7 @@ learn_kcomp_heavytail_graph <- function(X,
maxiter = 10000,
reltol = 1e-5,
verbose = TRUE,
- record_objective = FALSE
- wmax = 0.05) {
+ record_objective = FALSE) {
X <- scale(as.matrix(X))
# number of nodes
p <- ncol(X)
@@ -106,9 +105,6 @@ learn_kcomp_heavytail_graph <- function(X,
eta <- 1 / (2*rho * (2*p - 1))
wi <- w - eta * grad
wi[wi < 0] <- 0
-
- # Adding the following simple line of code avoids very strong connections that lead to isolated nodes
- wi <- pmin(wi, wmax, na.rm = TRUE)
Lwi <- L(wi)
# update U
U <- eigen(Lwi, symmetric = TRUE)$vectors[, (p - k + 1):p]
From ff251cf2283ba94861350ba657d261cb92f4f57b Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:28:56 +0800
Subject: [PATCH 16/25] Update NAMESPACE
---
NAMESPACE | 1 -
1 file changed, 1 deletion(-)
diff --git a/NAMESPACE b/NAMESPACE
index 054b866..54c60b3 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -5,4 +5,3 @@ export(learn_kcomp_heavytail_graph)
export(learn_kcomp_heavytail_graph_balanced)
export(learn_regular_heavytail_graph)
import(spectralGraphTopology)
-importFrom(Rcpp,sourceCpp)
From fb5cc15da1c7fab0998fa7e1e8ba8a6d7fba7afb Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:53:28 +0800
Subject: [PATCH 17/25] Delete Makefile
---
Makefile | 16 ----------------
1 file changed, 16 deletions(-)
delete mode 100644 Makefile
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 320243b..0000000
--- a/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-clean:
- rm -v src/*.so src/*.o
- rm -v R/RcppExports.R
- rm -v src/RcppExports.cpp
-
-build:
- Rscript .roxygenize.R
-
-install:
- R CMD INSTALL ../fingraph
-
-test:
- Rscript -e "devtools::test()"
-
-all:
- make build && make install && make test
From dee71d600cb615429247ef681b8d53e5d6901f5e Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 01:56:54 +0800
Subject: [PATCH 18/25] Update NAMESPACE
---
NAMESPACE | 1 +
1 file changed, 1 insertion(+)
diff --git a/NAMESPACE b/NAMESPACE
index 54c60b3..054b866 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -5,3 +5,4 @@ export(learn_kcomp_heavytail_graph)
export(learn_kcomp_heavytail_graph_balanced)
export(learn_regular_heavytail_graph)
import(spectralGraphTopology)
+importFrom(Rcpp,sourceCpp)
From 37e62171b21619d3269d020c0298833f5f672d13 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 02:05:19 +0800
Subject: [PATCH 19/25] Update DESCRIPTION
---
DESCRIPTION | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index adf4955..940d952 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -8,7 +8,7 @@ Description: Learning graphs for financial markets with optimization algorithms.
"Learning graphs in heavy-tailed markets", Advances in Neural Informations Processing Systems (NeurIPS).
Authors@R: c(
person("Ze", "Vinicius", role = c("cre", "aut"), email = "jvmirca@gmail.com"),
- person("Daniel", "Palomar", role = c("cre", "aut"), email = "daniel.p.palomar@gmail.com"))
+ person("Daniel", "Palomar", role = c("aut"), email = "daniel.p.palomar@gmail.com"))
URL: https://github.com/convexfi/fingraph/
BugReports: https://github.com/convexfi/fingraph/issues
License: GPL-3
From efc5471b8d00fb202e63aa98f9b129981b04fa35 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 02:12:07 +0800
Subject: [PATCH 20/25] Update DESCRIPTION
---
DESCRIPTION | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index 940d952..185ee3d 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -3,14 +3,18 @@ Title: Learning Graphs for Financial Markets
Version: 0.1.0
Date: 2023-02-02
Description: Learning graphs for financial markets with optimization algorithms.
- This package contains implementations of the algorithms described in the paper:
- Cardoso JVM, Ying J, and Palomar DP (2021)
+ This package contains implementations of the algorithms described in these papers:
+ A. Javaheri, J. V. De M. Cardoso and D. P. Palomar (2023)
+ "Graph Learning for Balanced Clustering of Heavy-Tailed Data," 2023 IEEE 9th International Workshop on Computational Advances in Multi-Sensor Adaptive Processing (CAMSAP), 2023
+ J. V. De M. Cardoso, J. Ying, and D. P. Palomar (2021)
"Learning graphs in heavy-tailed markets", Advances in Neural Informations Processing Systems (NeurIPS).
Authors@R: c(
person("Ze", "Vinicius", role = c("cre", "aut"), email = "jvmirca@gmail.com"),
- person("Daniel", "Palomar", role = c("aut"), email = "daniel.p.palomar@gmail.com"))
-URL: https://github.com/convexfi/fingraph/
-BugReports: https://github.com/convexfi/fingraph/issues
+ person("Amirhossein", "Javaheri", role = c("aut"), email = "javaheriamirhosein@gmail.com"),
+ person("Daniel", "Palomar", role = c("aut"), email = "daniel.p.palomar@gmail.com"),
+)
+URL: https://github.com/javaheriamirhossein/balanced-fingraph
+BugReports: https://github.com/javaheriamirhossein/balanced-fingraph/issues
License: GPL-3
Encoding: UTF-8
Depends: spectralGraphTopology
From d7a509bef40f07efccf09f17c7e9a6c6e800d621 Mon Sep 17 00:00:00 2001
From: Amirhossein Javaheri
<46240571+javaheriamirhossein@users.noreply.github.com>
Date: Mon, 26 May 2025 02:13:00 +0800
Subject: [PATCH 21/25] Delete vignettes directory
---
vignettes/talk-rfinance-2023.pdf | Bin 1927655 -> 0 bytes
vignettes/talk-rfinance-2023.pdf.asis | 4 ----
2 files changed, 4 deletions(-)
delete mode 100644 vignettes/talk-rfinance-2023.pdf
delete mode 100644 vignettes/talk-rfinance-2023.pdf.asis
diff --git a/vignettes/talk-rfinance-2023.pdf b/vignettes/talk-rfinance-2023.pdf
deleted file mode 100644
index a12c490237b50bc35ce87c24b34ae4c4786fe49c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1927655
zcmce;2Ut_h)-XB=NeCfei~&P6fq*oTE+A?M#UP-eBZ5NcNC!o*1qi)J5foIUBUMnU
zHFQKN0xBvhiUM{)u$MdWeb0B!z2E)+``!O}&KaL%uk1ZDd#yFA&!7U!)O?+$x-L#(
zbfR|>r>Q~FpoFjka7ISB_4d)B-jwyuJJ!z`uYX(SN*iTHr?
z4)*%{3558#Yih%p|3gIR{M(4o{m&7h_irO&!+(wl{eKq`TK_q5()zcFlNNuC|1&zF
z#b5M)i(+dT{KtsU<{Pbl3lZ8{|2ZP~lKbx=;y-7dwEu0^N#|dQ_}dL_(EEoU`u}!A
zR~;=w9%|sP%{k5cAug)C^8Pw0w)YcH)Oy_QuFRDl|MZGah2>J;-fy)CWB2dBExL{#)(x4bUPjNh5?RPW!RFA?W-k{Dxn>`bUl(zK?6xm1@wtHUKC2%`dJ3%S
z6|bjg@6mPq0cM}8-H~(f(h7cmZ?pLA>Ce~7V!V*$3aVPTP`fM>pVhI6-oMMfe%`*vz0PJHl`*)RooCW;<4d{CzU+mD!FHS3hcD;f*eaU3=cbQ(U80z4
z<+*r`YkANLhHu=PH!(Om#p*13Ti9o@Wa`PZifkIxJH@uSrSw6m%)KngCa*KfB&b<=
z@N%Qn`RYcSPZ1p@Pb$}Zj{hC}%}TIg;NrmfZx57ydOpyqUlzJ_fVZ01pL8qMxqRh^
zLjg~d(YYFp2M-iY-h7sCX=ZE|y*v1XPQI(zocGt0^>LdGE~^dJowh;P$Xcy-=m<@p
z+k5@pp^*KGX<6@8{(19v*s4!SkR9Pql_+%oV!|8D=H`}y
zMcdQ=J?PT?S6BbHGU;mQ{f}e17KfV`#x?Md=2V*lSMOVQbJ6oAkeRH=mFbS_nCgn-
zEd_r$?6vWHgch%irLx_>h}>}9VIIv22oSq(Dm*v-_J=}0PBQx8MqPPrCgKR)VcNW0HzJkL
z{n^{eBKoDV|BGbnENs;APt&A6*y
zvK@q;+U@eVcYSI5a+Ub(T!Om3YT7_4-gbXSYvg8;gcWWty33bNRT^GfyTV*@4sQ}s
zFthhsSC!kv&%vLsY@Isn|M~2xV*+O$>z;p8o9MLn-b|-U`Y_hVQpw#^^4awk2uU#cp=)5rQ_#jn9KPF4D_NVKv!m;x6hsc(z3sLT3
zSEEuCv=0<09O$|Fca%BUXKgff-dUetB^>tGied
zrGLb4)0LkJM@wRUND@?o*k_IXORO!Xlod3?l;f#A7MDU#44YQSh`hY?EC3Z&kIzp@
z3C>aZR4p5{xF%mItor{&lKv+S(jv&i*BiIq$={2gJ80?X;MOy}{eAr+VIrXge=z(b
zBUs+yCLuwgA;I3kkre&^$}|i#b@X&L;MSXj1crp$g?f0xB*)ZyzrUxq?N-J=t-h&=
z{eMe6eoy|M283A+hBIU-vh8jACFzAASuCAfZ2G|=>{MUQ;HHA%>yg35Tj86gd9MwLb^KLkoe=DFGx(0F6eW;M2qKIY6yM6RDcUm{m3&f^uObt=Q}mtURNsQ_S}E2bwlJ
zJWdEFzM3o{si3%4Nm)flS8s#9fuV`1nfX?x#Wp*82S+F8oi3hU-afv5{s9q@`}Rj2
zh>nTpCL|^$r=;fO9?#1^ak8MWw5+_Mvg%xQ&4r82Ev;?s9hbVgdwTo&2W||GjZfUU
zdvEgogQw4Cp1+u#n}7LgVe#YA@~6*VzJBA=g@B9tkM!8P9b@1+Ye&w@Y^_Xn$A;&1wLtiDEprg7WaQd
z*?$rCPr4oeyZ{0&p1?}51uR{Nu&~&UjH4s{dG2;iy(pHz3Ks|I@z$|edcVNX`T)Ah
z#)3oVERP^L9ZHm<=9Yv{ZR(S2!QH&$zXDBH4UYSlC@b#jrB>J(m$Po!N`_SyHG|`}
z&yM3zddGdvwjRf`1eAvKQ#E@LVIAd)`B|d^7Ue_g%9XKLYn?(n3oAi(QE4t|S{b|v
zGglgtC}xlOo=DpTLx=SXS7Q^t8>o%)I89?=Y`9r^!(XA
zrCySwVi!Bz?wle=BGtu3la5vHNw-B97h_hL`^jaEBfeK_Tb1d68EcBI?J7FfUj$Pa
zL66tzsNj{6I96k`=`6Gc&eb+{yAF6Tat&hfok3-x+x>DKY>>h_ML*E1s2pLvj*d}Q
zzFaL-tS&dWar{cBy~+5M?(IDvS4XAp&~PwTJUI4lHcj+J%c1eM)Jj_K`hp?plJIE{
zd-aaIA*%}!tnJ~q{40DEm!vq1=liR=K>IP-<|prdsuYz~_gCzaIw4hX;0p63pEQ#I(V0B*v(k@dQo~t1-y2>(_AgRxXrs;
zH0_?Qyw%vHHtBG&W27nb^5IY8eV-Q$o9%9@i0WN>ept2bwvp4`8uMCB!j97(#{}p1
zcIUrZVmR0283m(!e*`=}(_kT8;(hbr#QplDvZ-%UMn1W^-Cwr!(EB6M6E8}WYsOF+ra2+Z&UPi;d2rE3#v}LF
zSDo=ZeRty}-KtqFqQLc^O92uir_WxwxpH)_utLstn@7|3?w}`q+LL+ut;U
zKHM9zgLu3y`w4!nPVoI_i4S~^-Lf|-64n3l>XwW5^6+CI+w$t{O
z=>=V7x5{@(HOW^a>)Sv4e6F7r@lz$OGYsATo>cw1DDJA`F4lJ3g2Vbq%Y&zUr^+Z{
zrkd-|)p*$Me%bxf=U~BI;c
z(WmppI%#K@$ZhA)+0HZbsPm`D6!kj?poeMe0lWYq!>%(N0rQB
zy~0Bc_tqBd?tNpEddgdBYlYb$wO4W6Q7=_5)k#>awNOv;VzW&f$#uW>p7*lLyFE^l
z4{tfM^XQd;`hM}@!~HwzU8a(n9L=z2EIjYl?K;~YxlXhx7c4>3oZ2XRoR}M$5I^3yhIevcOs7qny?T!j`
zwDapXrM;KJ9@G)Q*9`AHZo>jJ(d^R0bHV6{6NN?hf#GGNvs1>s(W$E^K3%O+Px3i*
z<%7R0GdpyLZfx0(bx*qnc1=x4PF(C%PQU2;@ak6eR-M_g*;_eX&WnOGCUTY%Sl+_q
zJ;HlwrQAo|p*I!xi{<8i@i{*^wf1yvzt7zvCJz5^~94zt7uVp5@_%tnmGFWBCtEbP&gO
z=Vg;_efUaCUp=M0RNES*N+e}X?-R?-$gYW+xNb%d@p!--4jU(Z_wVIxQOBOUsLF7)
zw;hpoe{n+oOB8;*r+W6Q`^WUVVl&S^+5Ebr-}gp=mX>pPX1S(q%fg%Az~;xRi6pps
z=lA^vvLnlDhO2`uCkM}1>Kvd%IIUZ(<9cQV5CxjukwUuBcAn`=o%%mi%6rM1cLyi3
z)`ori4YqZx9F(l3Ah#PnpRMZ&Jw5J~D(`brt9osezKo$mfj#!pGVNE3*>mbIM@sOV
z;H1OsX78F-4eXGj)|rXlz~^F4=;c-_C8lt;{*901`%meE;{Ma!oR|$VTW~LZS)Yc_
z7?XeQrP}Uk{yNriBT(g==-y+i^r}MS$2#rON?!y8d%u#@e%!8Q^~>2{aXkHJKzfW&
z=G%(a=Ebz5tJYk8%~5SmvGduklF&OSX6x4w=kOy-o2K#Cil=uX~GVf_Ik<_9jgF^H*@nL
zQwx#{kIQHq_E<$$Ju=mm%6(b`+k3Ig?`|6QEpQKg`hot+v1RIa!9S;nV
zxG+|GcxT4OH%@D%dbY}Zm8c8!kKCQw^++l@Ww8n;(Wh;7nZfpV5S`D&4$!LSk8V~`
z$Xp=EebtvO)BmYlFY?+;fSB%yV+t--opW>PMQ7|xw4dH*v^-l$FV*VZ>KeXc&v3UFl7QOl*~I*V!9xT{izoO!DB
z!R7jDyFIJ)o5U)Pm^GOPHmelpZ@g;pmNh-2um8Dz^{?C6+wzlFu(q46yJFX4!mM%YjURkc;?)ev2&YRK3>#YLj4B&
zlGhBL(9ZuR7VMV%IZn*@W{kseb9c-ggt>)$@wxoF3)|oHZP&c9<*8}b9*gvWV$s1J
zUu?W@CuzUgLF_?%UD?F*uz%mPog|lW|4oGL;ljC_4-T~&+@Spi%hjdB?%O@n9tu{y
zWQ^}h_>n=^e`(QcZX#=WSih|L`2@)#`*c{LYv#`Yqk~IjYso{vV)1N@=qBX?iO+fXSS#23uuJw6_tb6y%g*(2Q?{0k6-}LQX;j56%c`R~D_f8lhjMc+7lN
z7}sm~n0?2DTD)OztI=;DEPE7pGxOcAAL)I}rEoJ}w=Z40%8joc*?qt6^Md%;1y#=A
zcJM1wCA_7ews(Jwq~V>34=XNuc~#S@`=2_GKH;_?L|+QMe{CP!BGDq8lxcK%Ggv#;
zTBq}6X7Mv{-KSfn!rf=^b?QZA%+~IWb~1Ty7V(zy6liKMH(2uCG`UPsy-|66+c)iP
zFk&}|E_4j-3?C}dFFU`@HI^=S?v~X-Tf$*c=WI3O$o7edvy80
zuz9GGk^8%Hf4K}{=0m!SMP=?mq2@Nyr8zs}Xs~5lXa1*WujsTx>)mQ!
zzi0KgGJV6pdMfvA->xCJ^mA{i{>DRAozKpHC+xgx&otM5w_Ke2&`>=++mcyzO4@37&glGQpWL;R4*L5xM)Rb8RLYm#X&Ow#V>k7i
zxQ=nl7UWB{(px<{mdiVr%VmzPana5l{TZ-rwV-sNhxGTc(=!rV@~CTH(^r0s)6zEA
z9>ry>jv786|3pBdg0|+w0l`h0Lr58pXc)B5vx};JH#Jb7SkbyJpMD1QjVER;xgIs_
zJ+=Q+k%chxzTl{=zSunz{M>s>(+I~NC4ZSAq4nO!bFUQtdUf{o-5Tcn>UF~gbA@$Y
zK1Wtya_D`?aP8mV^?754=9toSc=%nb7H%%eJ-+{{Q=y_GWeHOteR
z3$GmxqO(csPFKG^cDIDmDJ^o^$4Yk7(QjXB6%KU>dmeKMnqo6o`_CKyG+QlJXFg^j
zq*gU6=qGsQH<&97f7Giq%*YCL$lN0Txc26vRn?gWtrcasTD5H-PBtQjj~s!KRQJk+
zDmRy1pA8!)FCCYeUV0P2JazA#_6k-<-Hn-KO_A8l;)T?@*)Eb-7*5J~~>;
zOsXvS{$i{1st?)82=ENXL5b_jp-fwj3!%Z>Qn*=f(zup%+tDon@w?ia-Mn)KpU$-|_RacE2{vv%jVpAP_30@3
zmf7lYb050NpirqGt|?k3)Vm+C!|mGHJx7O{&bAEpnuyfjcyq^g?6Jk_TiMCP(57$S
zf2zieO;B*F&(7_BB&k^?>s%|}UGL(cFtm36w(Pc(VV`3hic>W#1d9AQYLq6U33|(|
z)4m~x91}O@mOOs7x~R0i{#MqHb8p+1JLO6OjU^L?=`)Y>b-TPWV`g(NPKlpd{p`*=
zB*%5tud%rwDnAUHtru@e8l@+$Rs9X_)(K|0$KWz8ZuyZHax_O;Jk>TCMCxWl_gCLX
zG6w>mhgwsoFwW=KBt(2OFR$6ge8|fE6Hfg>+V^al*T*7
z(}~fk1yb$P6O89mrjtflLf_(yzlEh%oO`rNKSc_4_S8#DRClygqd?@7*parFPmk)N
zHFs187&L{+&FSk`PyWb}4%JQ_inH|&f3!1m?>1Tc_`>{D6Hk^~qTy0??AH^UHrk?9
zH9x^3Prj~GLexGPm7UBi=GlFXCOCH%0q{7QnM+^g@T7+5`GCv)a!o&8QZvp7n-|q=
zSH94tkQOHJVmeja0<)xgI%Yik-ciN|e1v0xh{UnIq%p%mV^mo7jc*5kgA7a2vjOh;
z6kOdo7WoZhH!;sCag>iIZCc@JizIFi57)0c`zWKm52Ior
z(R$468AqNXMmyrb_+_QTTp=;!mk^>VygX9jxqIOHgOz=c1npj0`CWdV?L*pp-|NMW
zN`s;OE{ASLRhcQXM5i>CITo=&J!kACRNFP<{ywlJc;-fFX5plV8Wir9$6j&Gz-1q$^`S4!km4u4j^^Sj8X5&juVU-JL#h
zNT=>m^#?+~U(Kk+$qs{&FLqBuFYKGyXO?Q!j=*g$YR+hHeHRlGS-yXAQa)qi?KIN(
zQ?G^Tu2#Kg8zTynlPA?z7`*XcY+Zi)PNnX)97?%!?Z+C1lm>=TeB`Lh#me0`%Z5Yb
zlf?pSG4q3yA#*#!Ej8Oe39hMk#TivR{tAvA|2$KvJg8#xBEKswENWMtZpx7{^v#d7
z3;71i2Ah;yZyzeo{$?9cyw!&zoxK653!1QcL%;Uv_q&8zs8UV^Jt|zF&ZOhQMaEaf
zj*FqReRMZ{9Dh)ie5m)*`k4JRRt%qrF#`{eg)
z^tU*kKIc|O%gxLX
zA|)fjFka>AFd6gkwEOdg*JDY=&kP4cAGp?XFb6(;JBWLINW72f!`-Yu^8Dzx<<4DA
z-|>%6C2mo9Vz2C-KX@5-|9FVbNs$T9cN`&7UO7Mz4lCa#~O8!
z-5qTb>$A#bH|^D%PFXz^YAI?seb3H7&bR#iDuO{e%u?-4w3?G{8m;{57-MC0!}iiF&VKy4*Jt>PWw3Pmp&ri$|68GTlU8?_{R?z8-o>xI>{E_
zVL$KrP9G@G(!<9OuZ;?~HhowynQ_WVrbCQ)Yv9(N?4rI3`}O4TIS*9A>;B)Mp*l|a
zR>D*Jhs~;@#lGiV9Fp%jc_|ZtV38}BtC34kol<;gEP#?xVE1ilX`jp$cOzg3zxzFu4P{K21;~%uGEy6Yzyy&Ty^ORYE%G^8h&aKGrl)fpRC#p5&
z{xjyT?Fmok+Tgvk$wtTDpeScy33=%~UOz+b(4yB+m=|i7AL4uV^J9#?=SIEch7kHa
z9K4M}b>0qL0*_U-}kr7lr{$`$v1EI7$7MmjzsK992yaqWHkvEZ&FxjXT7
z(Q;kEaT|Qy!sVI{!Kl0C8|3Z|J*@6SYum`WO_bfPNtoy}bKZKy+UHRkb02rtbit`w
zs_x{;0w3e}HnQDY*Sn`cdq>;FN3lB9E9G0A@xNN;*LdTw@>$H9s-TqW$XwNjty6f>
z#8|31CHHZ;W`fbDXUEn?misQ$?<`al^-A6x(51QaRK)(8>?1EZYgNo#A`y!Qq{NN4
z+_hg?ZABRv7`(L64o*i*`DBw4Mk*0M&Nbp!zS}?Y;)`SA39;r&8ONQpqHQ)lJ&E_F
zHfq~fxM!9}NtEXf^;ipYNSQ)QYvI;`o&HDF)!QATd`6PXnp>`SB;HBuk>4qeL8qQAv8C7xbmzF{Zir5=y+S0PQ5aD
z>cBTAYtQeO(Y)u4y2CF0*SyT6SGkcNaLmhp6g#Hul-N-H`#$*n4B2R5VzP&2yTjbV
z$_(xX!=2pXK+m8Elm-An!I9x`8=L|+CMkk1018OKO=@kh%EL1v)R@Jx`p5R@f4_dZ
z%irk*V0;~auK)i0e|ty7%R9moZmUvYFS=)FINS(FxIl6yJY=+5ZB2`h^Fb5vCvn1Obmf;{ksSU?152$=e7YZLL4I5O@Xv=*Pc*e^dZK
zFc*NIS-*dOFZuoZXDQstp8|jv{Lk?r#Q^9p!~0qPyhl3=zzQw^tv&y|$36i-TM_`|
z`=K7;9)I4$KOBJcfrk_ps{jz$3BW3NgaEheFTTNN{O6$T9su@`S5`v+6u?6ZioS4e
z*Z&3G{38
z2@>RY;j`ke_Rn2*xC<{0pZv8A|BuK2b-Lj9-+S2v0RoifZ{c$+-Xw>kN2dSjNiw{1
zXMojIlEpbEpi!Nv8yzkGtcP=eB>+s^rHOh<6by)$mc|AmM7cC3hl42Q0_P@Uk!1cc
z3_>x921rV@5_j@6BOwt8V>K{j0isb2;dq57K%;0(q*or6X`I2KqX3hR?y?{vs3;1P
z1DbD5PBM^y{YQy_xLR0kZLD0Pd6xjC7?j+-a3Tjm2h-D>ghUN;BS(!&0RrL(PmhdL
zJR+G$0g|8z*^Lbhq{?xCO1=u7>473wmbc?Ic;sRfc^ZlfLOwmGn8~!D6)I61dr>zk
zh#(G30{ABaK%Gd1M>7~GKv@+*14KerpIIWG6OFycDlDi#iU6B{wCtMTMOR3jxEj?%
zBo)sTG)sXh1Pw=V*B>01*-Z?d9s$z@ig|=Q+g=YL5+RoNAfc<8G_;M=FIf>Gji@?}b63A>pG?!T3E0;vf=XT(v*a`G34x$l-kTbz}6gShT
z0J2pIONmpQg`55$4v&n(hem1ujb0%vl4!w@V#Jg5)ts9~9~RmwWzkqo#)&jKlB9Nk
z$q_;*QDb>YfWn-NZA6icxrio&3KPkNCp{RzouowJ5eb|-ma%kvq$XWT#Jo#fNrzjJ
z!`Rt#lA9HftAnBdxH5#v4n4&Zb^~(~#bF|e6bi@k7AKxSHpURXF!}{epgCq{Be-{j
zD{@Gz#(p$LEi~+cBVq-PL~iPALgH>eRp4YV7-wLN$aHBLFfhr(^a{cC;)=v(vG}iu
zbqDU^fJXy?i#=qNXd=Pg{$w!#k_kNaPGTyw-V`pNqc{km?5RmQiN-`>0V)}Az)=l6
zg+oK&WW^0PE5Q>XeThAZ=+SncJS$fAQ%1bB_#N1LUz7k|K&8h?{ba5o<9NYJkyRq>
z*(`Y0iZwr<-BhZ9Bq8MJAh9RKD}h9y15_GTAkGYpp3*cCC^7?yr6Ln>;uHi%U!RPI)*!e&2W!k2G0-==Cw;B?q?riuV{_{3eQw`S
z0p~bb7n00~@kELrl_~^0jUl&1niQ-wFf>*yk3%jQg$jXabnZ=)2B-{FZ6H&lmxDy5
zkksf9bMgQZO92RHH7{m~ygQM=Fy$apu*_L^AVktfcx0xDZZgETxO2h5+^MQv0-jFXgITOag)BwE~Np-m?9S|m+np9&TqbJFlg3BYd16`qeQp}
zB$SA30P_8PIM81K3J>)6;zZcaKv>y3QJ>5aB2ev{07(rH%0i_fQZLQ?_jj`y%wKw&i
z$6q%RJsoyf`eXC(ca0JHge8C8@r_ldNh_X-w+eXSv>>e(nv(tnNZA(H%1{a_-
z^o^8Q#X`UXi`Qgx2*qR8W`UHo>RZo?tq!)hhg0tBH{0oBU>j%u>{aEXv$vLgdafks
zLeT~;*6w^Y6m>nUS5
zytGUXUVH`IrLiuvjoTcV3^6P&fuN&C2TB@oSRhXJEGvsmm6R7+gAYYyWC&q3;$_4M
zY#y0GN64YL6efVjuZ32H<}e^SSb!uFFx^zdCtu$
z1Cqf32q*#`nK%+feoJ8Vo})U}7XrS{)fc5#s4+leS6XoU#&28Jn54Ct%$hk-^kuys))K`l{J}7*Lu=NoGNCt!P^rSr&ix9#i=?ET1NrPK(>gmm&
zvisNP>J8_Ad1B?zGw7_MC{7MLd~|h)-Sm6iGYnQ^51^nBqg)k#Apv(RlP(Pe@R|rL
zKGcRH;E{pFGuddA61|wG6p2KOVkpi{9)M|#l^{T2XQl`VO9EpUQy@f=L2NOKf==Yy
zE%JQHbZ=^4#qpZSatb#sKpawvK$>8gj2@ur4vOOtIZs4G?hQXK;9X<2&34!}6b_^{
zd{y*oM6R!WzHB&h;+3RzmJORLLNigHT<
zB}xHcvCi3zUGOac$A6W!%r)uH=g(wl#~&(vKNMQw6H^~`KC2K2?s{o(SoHdA%wP)s
zZVgWlO7Y~pJAjT1MWaGz707obnzwILIev5x41f*@?<@csF=_zMYGNcm#R3crHSgTW
zP9f@10G5sx0$;cn?!T!hx@M+>NKL*xyL5R^-b>Q9csx9$97iCO$aTZ%Q8fDWG?GA}
z@Mr?DT(SvU0Aj={1}t~b2v|*QK%Q=rmxq{&q!Q)nct0iwi=_fc4}l}XLFaqL0ai7+
ziH8)%uTp$uEF?}a&bVNwEkvPG0adOG%R<5h#c%%N5%bk$ti@>{WGJxW$U$X)r-8~8
z5T-X7BXjx2Qm-WmdSq0J1c~4-PSe03paClz8g&2!q{X4N0F5YUa7k(!
zD2f*X1}zepBn%gerNm1~0>}?fHUqD<-z#2gItp5IXS9z!y4PQCno3Q@Qq`pG5z0g|
z9ZRLdc`yVI^12WG`xY+lk8-UDx*1%*{~&QZB&RY3Yri{q)8X-f
zV59^RICOU@EXz0zOJ@KY6OBT^7{(p&g)0-I7D-6}fqpP-nT!<|#s^XY*P%GgIcA>Lyl|$=tKma1bUOP
zOsLEZq&P^xijxPjJvjV1+)-jql1tGlpJolx-+i5Wd2`cB?FVw>P|zuUtoHFs2d=$&
z)8GzD=m052A^<`*+YG?d+p*MCDAnQ!0Xp!EOQLy*@VJQtPY<}9nml1?C=r;hm7&?j
zO}2sVe$FsQGDeZJ5gaDN#Aj4s-NjK10MM-#_W&lOl7r+x)p3Rvp2~oeqd5e7{m&&s
zHf>t=6)%UM{kV`VwW(y?=C9JXk!GfOk#DYTxOlLL2cyM6O*WFx2u%uLqp?x|jUhMo
zCQ|&637GXJIR~f)1#w6a?ni<0>56w|cfrJtrpnDCFfgWx1P+!#M?p~sD~T5Pab2eH
zp(PDC7n_?10EGh~zQktratScL1`KqfipZV5tLlD|3yR~GxATU+R+^gBKQo)%&b|Y1
zg{e>Kzg{akajG03;|Xf4zdRoUjUy0H7$un7py4-1n1par-;uh?gyltl)GrrHXEHft
z_C-XeF$YTF##AQWk19upC(18wL%!UJ5C$Bif{>!OE?wAlNh1#L43wzU6g+^+O?Jm3
zV7N@?qX`fmaY9L}-@iz^F6CmacOU*8OD`eW^eF|{PdNSz8CqI8{YVk2gCMjs9R2_T
zxrBlb)Zph=cqG(ckED_tOuV_5aY@Ok%&i6OV*Tz|=VH1~xQ8)MfyzW~%c0S8P(munmUpI-oZNdRTx+q(L!=BPeMiQE7M}&DY{;W>cq$
z30$p#atB4MC=4*-R-yZXsSX`xoC2|%*`pJy!@aU8U|R{GDp3-9xPb2@uvm0r3}lNa
z26XWe0nn?Orjh^(W;y(ZQCh7ipZ;mF=V4Y$q3TRn;5whiWE8I#g$uAba(Ol;Mm;!7
ziOK^AE`bAsG7betBJg8#6AzRU5}E*wZc)({qMcW*ESgFHU=YQGKz65b?8((caSAZV
zvp=bB?aZX`(6(Gy8jyr(u@n&iI$ceB6#6b1yhc3W@Vz~>DnLzxE6V+Y6iw5zX7=@2
zsrS#)s#XS7h!?nOHMhv+J#Xygis_0N-aGa3Wwap{T5b47_(5@_czOhCjEDc})H-$)m*|`ZD01gFKnc!RqMjTd?
zO$XgrO@?VE6nPpV4LS!{JTOjmb5hivIdjFPWmf9qy}=iGL)CbWC1|4BvBqnmtmo#!
zmP(4Kg}o@gAuKdYosgabep?vKbr&0(<{0&L=7&;h`4`1wb}
zLd9dDQb0CUgrflyQaS}SwZs%4K@wnNc_fRIi!lU<2>&N4k&LPc5<4_5u~_ujasOOK
zo!3&o>EQ50(kjOjy*Y&EUA6n~z1|#Ed$f0~n8z1BvODzH2%y0gCeWaOQ|SaXO)5qS
z*$o6bI<#P%nLAvF{?+)bLLvdC3uFO0$CZ{9
zS44T3a+{@KVt!=a>+;Ghe596oij~umRB>Y)y
zc2BA_UIWiz#zL7&gl`0mJsizWkBnqg0$0wILgy6J!6cdhc?S!Icna)*f`Onr{{|IJ
zL1-Ys#-c