From 9314ecd937cdc7fb79ea0b663bd2b2d71cefe3aa Mon Sep 17 00:00:00 2001
From: Zeno Belli
Date: Wed, 29 Apr 2026 15:37:18 +0200
Subject: [PATCH 1/4] feat: add jwks command
---
cmd/certinfo.go | 5 +-
cmd/jwks.go | 67 ++++++++++
cmd/jwtinfo.go | 19 +--
cmd/man.go | 1 +
cmd/requests.go | 3 +-
cmd/root.go | 26 ++--
internal/certinfo/certinfo.go | 3 +-
internal/jwks/jwks.go | 91 ++++++++++++++
internal/jwks/jwks_test.go | 135 ++++++++++++++++++++
internal/jwtinfo/jwtinfo.go | 207 ++++---------------------------
internal/jwtinfo/jwtinfo_test.go | 89 ++++++++-----
internal/style/style.go | 2 +-
12 files changed, 412 insertions(+), 236 deletions(-)
create mode 100644 cmd/jwks.go
create mode 100644 internal/jwks/jwks.go
create mode 100644 internal/jwks/jwks_test.go
diff --git a/cmd/certinfo.go b/cmd/certinfo.go
index e5f8497..03cac8c 100644
--- a/cmd/certinfo.go
+++ b/cmd/certinfo.go
@@ -19,9 +19,8 @@ var (
var certinfoCmd = &cobra.Command{
Use: "certinfo",
- Short: "Shows information about x.509 certificates and keys",
- Long: `
-HTTPS Wrench certinfo: shows information about PEM encoded x.509 certificates and keys.
+ Short: "Inspect and verify x.509 certificates and keys",
+ Long: `Inspect and verify PEM encoded x.509 certificates and keys.
https-wrench certinfo can fetch certificates from a TLS endpoint, read from a PEM bundle file, and check if a
private key matches any of the certificates.
diff --git a/cmd/jwks.go b/cmd/jwks.go
new file mode 100644
index 0000000..0c268bc
--- /dev/null
+++ b/cmd/jwks.go
@@ -0,0 +1,67 @@
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+ "github.com/xenos76/https-wrench/internal/jwks"
+ "github.com/xenos76/https-wrench/internal/style"
+)
+
+var (
+ jwksPublicKeyFile string
+ jwksKID string
+)
+
+var jwksCmd = &cobra.Command{
+ Use: "jwks",
+ Short: "Generate a JSON Web Key Set (JWKS) from a public key",
+ Long: `Generate a pretty-printed JSON Web Key Set (JWKS) from a public key file.
+
+The generated JWKS contains only public key parameters and is safe
+to be exposed (e.g. at a /.well-known/jwks.json endpoint).
+
+Examples:
+ # Generate a public JWKS from an RSA public key
+ https-wrench jwks --public-key-file rsa-public.pem
+
+ # Generate a public JWKS with a custom Key ID (kid)
+ https-wrench jwks --public-key-file ec-public.pem --kid "my-custom-key-id"
+`,
+ Run: func(cmd *cobra.Command, _ []string) {
+ jwksJSON, err := jwks.GenerateJWKS(cmd.Context(), jwksPublicKeyFile, jwksKID)
+ if err != nil {
+ cmd.PrintErrf("Error generating JWKS: %s\n", err)
+
+ return
+ }
+
+ // Print a nice title and then the formatted JSON
+ w := cmd.OutOrStdout()
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, style.LgSprintf(style.Cmd, "Jwks"))
+ fmt.Fprintln(w)
+
+ fmt.Fprint(w, style.CodeSyntaxHighlight("json", jwksJSON))
+ fmt.Fprintln(w)
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(jwksCmd)
+
+ jwksCmd.Flags().StringVar(
+ &jwksPublicKeyFile,
+ "public-key-file",
+ "",
+ "File containing the PEM-encoded public key",
+ )
+ _ = jwksCmd.MarkFlagRequired("public-key-file")
+
+ jwksCmd.Flags().StringVar(
+ &jwksKID,
+ "kid",
+ "",
+ "Optional explicit Key ID (kid) to use. If not provided, a thumbprint-based ID is inferred.",
+ )
+}
diff --git a/cmd/jwtinfo.go b/cmd/jwtinfo.go
index 07ca6ac..21a20c0 100644
--- a/cmd/jwtinfo.go
+++ b/cmd/jwtinfo.go
@@ -29,8 +29,8 @@ var (
var jwtinfoCmd = &cobra.Command{
Use: "jwtinfo",
- Short: "JwtInfo shows data from a JWT token",
- Long: `JwtInfo shows data from a JWT token
+ Short: "Inspect and validate JSON Web Tokens (JWT)",
+ Long: `Inspect and validate JSON Web Tokens (JWT) from files or remote providers.
Examples:
export REQ_URL="https://sample.provider/oauth/token"
@@ -50,11 +50,12 @@ Examples:
https-wrench jwtinfo --request-url $REQ_URL --request-values-json $REQ_VALUES --validation-url $VALIDATION_URL
`,
Run: func(cmd *cobra.Command, _ []string) {
- // TODO: remove global --config option
-
- var err error
- var tokenData jwtinfo.JwtTokenData
+ var (
+ err error
+ tokenData jwtinfo.JwtTokenData
+ )
+ // TODO: remove global --config option
if tokenFile != "" {
tokenData, err = jwtinfo.ReadTokenFromFile(tokenFile)
if err != nil {
@@ -62,6 +63,7 @@ Examples:
"error while reading token value from file: %s",
err,
)
+
return
}
}
@@ -80,6 +82,7 @@ Examples:
"error while reading request's values from file: %s",
err,
)
+
return
}
}
@@ -94,11 +97,13 @@ Examples:
"error while parsing request's values JSON string: %s",
err,
)
+
return
}
}
tokenData, err = jwtinfo.RequestToken(
+ cmd.Context(),
requestURL,
requestValuesMap,
client,
@@ -118,7 +123,7 @@ Examples:
}
if jwksURL != "" {
- err = tokenData.ParseWithJWKS(jwksURL, keyfuncDefOverride)
+ err = tokenData.ParseWithJWKS(cmd.Context(), jwksURL, keyfuncDefOverride)
if err != nil {
cmd.Printf("error while parsing token data: %s\n", err)
return
diff --git a/cmd/man.go b/cmd/man.go
index 41c664a..69da0db 100644
--- a/cmd/man.go
+++ b/cmd/man.go
@@ -27,6 +27,7 @@ var manCmd = &cobra.Command{
Date: &now,
Source: "https-wrench",
}
+
err := doc.GenManTree(rootCmd, rootHeader, manPagesDestDir)
if err != nil {
fmt.Print(err)
diff --git a/cmd/requests.go b/cmd/requests.go
index 3d6469c..13a7e0e 100644
--- a/cmd/requests.go
+++ b/cmd/requests.go
@@ -22,7 +22,7 @@ var (
var requestsCmd = &cobra.Command{
Use: "requests",
- Short: "Make HTTPS requests defined in the YAML configuration file",
+ Short: "Execute YAML-defined HTTPS requests",
Long: `
https-wrench requests is the subcommand that does HTTPS requests according to the configuration
pointed by the --config flag.
@@ -62,6 +62,7 @@ Examples:
if err != nil {
cmd.Printf("\nConfig file not found: %s\n", viper.ConfigFileUsed())
_ = cmd.Help()
+
return
}
diff --git a/cmd/root.go b/cmd/root.go
index fcc1222..1cd977a 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -49,23 +49,27 @@ var (
var rootCmd = &cobra.Command{
Use: "https-wrench",
- Short: "HTTPS Wrench, a tool to make Yaml defined HTTPS requests and inspect x.509 certificates and keys",
+ Short: "HTTPS Wrench, a tool for maintainers of secure HTTP endpoints",
Long: `
-HTTPS Wrench is a tool to make HTTPS requests according to a Yaml configuration file
-and to inspect x.509 certificates and keys.
+HTTPS Wrench is a tool for maintainers of secure HTTP endpoints.
+It enables executing YAML-defined HTTPS requests and performing in-depth
+inspection of x.509 certificates, private keys, and JSON Web Tokens.
-https-wrench has two subcommands: requests and certinfo.
+https-wrench provides several specialized subcommands:
-requests is the subcommand that does HTTPS requests according to the configuration provided
-by the --config flag.
+requests: Execute HTTPS requests according to a structured YAML configuration,
+supporting custom CA bundles and verbose output.
-certinfo is a subcommand that reads information from PEM encoded x.509 certificates and keys. The certificates
-can be read from local files or TLS enabled endpoints.
+certinfo: Inspect PEM-encoded certificates and keys from local files or remote
+TLS endpoints. Verify certificate chains and key pairings.
-certinfo can compare public keys extracted from certificates and private keys to check if they match.
+jwtinfo: Decode, inspect, and validate JSON Web Tokens (JWT) using local files
+or remote JWKS endpoints.
-HTTPS Wrench is distributed with an open source license and available at the following address:
-https://github.com/xenOs76/https-wrench`,
+jwks: Generate pretty-printed JSON Web Key Sets (JWKS) from public keys for
+exposure on well-known endpoints.
+
+Distributed under an open-source license: https://github.com/xenOs76/https-wrench`,
Run: func(cmd *cobra.Command, _ []string) {
showVersion, _ := cmd.Flags().GetBool("version")
diff --git a/internal/certinfo/certinfo.go b/internal/certinfo/certinfo.go
index 0d4e28f..7e308f0 100644
--- a/internal/certinfo/certinfo.go
+++ b/internal/certinfo/certinfo.go
@@ -120,7 +120,8 @@ func (c *CertinfoConfig) SetCertsFromFile(filePath string, fileReader Reader) er
}
// SetPrivateKeyFromFile loads a private key from the specified PEM file.
-// If the key is encrypted, it will attempt to retrieve the passphrase from an environment variable or interactive prompt.
+// If the key is encrypted, it will attempt to retrieve the passphrase from an environment variable or
+// interactive prompt.
func (c *CertinfoConfig) SetPrivateKeyFromFile(
filePath string,
keyPwEnvVar string,
diff --git a/internal/jwks/jwks.go b/internal/jwks/jwks.go
new file mode 100644
index 0000000..463f651
--- /dev/null
+++ b/internal/jwks/jwks.go
@@ -0,0 +1,91 @@
+// Package jwks provides functionality for generating JSON Web Key Sets (JWKS) from public keys.
+package jwks
+
+import (
+ "bytes"
+ "context"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/json"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "os"
+
+ "github.com/MicahParks/jwkset"
+)
+
+// GenerateJWKS reads a public key from a file, parses it, and returns its JSON Web Key Set (JWKS) representation.
+// If kid is provided, it sets the Key ID explicitly; otherwise, jwkset infers a thumbprint-based kid.
+func GenerateJWKS(ctx context.Context, publicKeyFile string, kid string) (string, error) {
+ keyPEM, err := os.ReadFile(publicKeyFile)
+ if err != nil {
+ return "", fmt.Errorf("unable to read public key from %s: %w", publicKeyFile, err)
+ }
+
+ block, _ := pem.Decode(keyPEM)
+ if block == nil {
+ return "", errors.New("failed to decode PEM block from public key file")
+ }
+
+ key, err := jwkset.LoadX509KeyInfer(block)
+ if err != nil {
+ return "", fmt.Errorf("unsupported or invalid public key format: %w", err)
+ }
+
+ // Ensure the key is a public key
+ switch key.(type) {
+ case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
+ // Valid public key types
+ default:
+ return "", errors.New("the provided file does not contain a supported public key " +
+ "(it might be a private key or an unsupported format)")
+ }
+
+ if kid == "" {
+ pubBytes, err := x509.MarshalPKIXPublicKey(key)
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal public key for thumbprint: %w", err)
+ }
+
+ hash := sha256.Sum256(pubBytes)
+ kid = base64.RawURLEncoding.EncodeToString(hash[:])
+ }
+
+ options := jwkset.JWKOptions{
+ Metadata: jwkset.JWKMetadataOptions{
+ KID: kid,
+ },
+ }
+
+ // Create JWK from the parsed public key
+ jwk, err := jwkset.NewJWKFromKey(key, options)
+ if err != nil {
+ return "", fmt.Errorf("failed to create JWK from public key: %w", err)
+ }
+
+ // Initialize in-memory storage for the JWK Set
+ storage := jwkset.NewMemoryStorage()
+
+ err = storage.KeyWrite(ctx, jwk)
+ if err != nil {
+ return "", fmt.Errorf("failed to write key to JWK Set storage: %w", err)
+ }
+
+ jwksBytes, err := storage.JSONPublic(ctx)
+ if err != nil {
+ return "", fmt.Errorf("failed to generate JWKS JSON: %w", err)
+ }
+
+ // Pretty-print the JSON
+ var prettyJWKS bytes.Buffer
+ if err := json.Indent(&prettyJWKS, jwksBytes, "", " "); err != nil {
+ return string(jwksBytes), nil
+ }
+
+ return prettyJWKS.String(), nil
+}
diff --git a/internal/jwks/jwks_test.go b/internal/jwks/jwks_test.go
new file mode 100644
index 0000000..0226e4d
--- /dev/null
+++ b/internal/jwks/jwks_test.go
@@ -0,0 +1,135 @@
+package jwks
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestGenerateJWKS_Success(t *testing.T) {
+ tmpDir := t.TempDir()
+
+ // Helper to create a PEM file
+ createPEM := func(t *testing.T, filename, blockType string, bytes []byte) string {
+ t.Helper()
+
+ path := filepath.Join(tmpDir, filename)
+ block := &pem.Block{
+ Type: blockType,
+ Bytes: bytes,
+ }
+ file, err := os.Create(path)
+ require.NoError(t, err)
+ err = pem.Encode(file, block)
+ require.NoError(t, err)
+ file.Close()
+
+ return path
+ }
+
+ // RSA Setup
+ rsaPriv, err := rsa.GenerateKey(rand.Reader, 2048)
+ require.NoError(t, err)
+ rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPriv.PublicKey)
+ require.NoError(t, err)
+ rsaPubFile := createPEM(t, "rsa_public.pem", "PUBLIC KEY", rsaPubBytes)
+
+ // ECDSA Setup
+ ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ require.NoError(t, err)
+ ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPriv.PublicKey)
+ require.NoError(t, err)
+ ecdsaPubFile := createPEM(t, "ecdsa_public.pem", "PUBLIC KEY", ecdsaPubBytes)
+
+ // Ed25519 Setup
+ edPub, _, err := ed25519.GenerateKey(rand.Reader)
+ require.NoError(t, err)
+ edPubBytes, err := x509.MarshalPKIXPublicKey(edPub)
+ require.NoError(t, err)
+ edPubFile := createPEM(t, "ed25519_public.pem", "PUBLIC KEY", edPubBytes)
+
+ t.Run("RSA", func(t *testing.T) {
+ jwksJSON, err := GenerateJWKS(context.Background(), rsaPubFile, "")
+ require.NoError(t, err)
+ require.Contains(t, jwksJSON, `"kty": "RSA"`)
+ require.Contains(t, jwksJSON, `"kid":`)
+ })
+
+ t.Run("ECDSA", func(t *testing.T) {
+ jwksJSON, err := GenerateJWKS(context.Background(), ecdsaPubFile, "")
+ require.NoError(t, err)
+ require.Contains(t, jwksJSON, `"kty": "EC"`)
+ require.Contains(t, jwksJSON, `"crv": "P-256"`)
+ require.Contains(t, jwksJSON, `"kid":`)
+ })
+
+ t.Run("Ed25519", func(t *testing.T) {
+ jwksJSON, err := GenerateJWKS(context.Background(), edPubFile, "")
+ require.NoError(t, err)
+ require.Contains(t, jwksJSON, `"kty": "OKP"`)
+ require.Contains(t, jwksJSON, `"crv": "Ed25519"`)
+ require.Contains(t, jwksJSON, `"kid":`)
+ })
+
+ t.Run("Explicit KID", func(t *testing.T) {
+ expectedKID := "my-custom-kid"
+ jwksJSON, err := GenerateJWKS(context.Background(), rsaPubFile, expectedKID)
+ require.NoError(t, err)
+ require.Contains(t, jwksJSON, `"kid": "`+expectedKID+`"`)
+ })
+}
+
+func TestGenerateJWKS_Errors(t *testing.T) {
+ tmpDir := t.TempDir()
+
+ t.Run("Invalid file", func(t *testing.T) {
+ _, err := GenerateJWKS(context.Background(), "non-existent-file.pem", "")
+ require.Error(t, err)
+ require.ErrorContains(t, err, "unable to read public key from")
+ })
+
+ t.Run("Invalid PEM", func(t *testing.T) {
+ invalidFile := filepath.Join(tmpDir, "invalid.pem")
+ err := os.WriteFile(invalidFile, []byte("not a pem"), 0644)
+ require.NoError(t, err)
+
+ _, err = GenerateJWKS(context.Background(), invalidFile, "")
+ require.Error(t, err)
+ require.ErrorContains(t, err, "failed to decode PEM block")
+ })
+
+ t.Run("Unsupported block type", func(t *testing.T) {
+ path := filepath.Join(tmpDir, "unsupported.pem")
+ block := &pem.Block{Type: "NOT A KEY", Bytes: []byte("random data")}
+ file, _ := os.Create(path)
+ _ = pem.Encode(file, block)
+ file.Close()
+
+ _, err := GenerateJWKS(context.Background(), path, "")
+ require.Error(t, err)
+ require.ErrorContains(t, err, "unsupported or invalid public key format")
+ })
+
+ t.Run("Private key rejected", func(t *testing.T) {
+ priv, _ := rsa.GenerateKey(rand.Reader, 2048)
+ path := filepath.Join(tmpDir, "private.pem")
+ block := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}
+ file, _ := os.Create(path)
+ _ = pem.Encode(file, block)
+ file.Close()
+
+ _, err := GenerateJWKS(context.Background(), path, "")
+ require.Error(t, err)
+ require.ErrorContains(t, err, "does not contain a supported public key")
+ })
+}
diff --git a/internal/jwtinfo/jwtinfo.go b/internal/jwtinfo/jwtinfo.go
index 0517812..cb05058 100644
--- a/internal/jwtinfo/jwtinfo.go
+++ b/internal/jwtinfo/jwtinfo.go
@@ -49,7 +49,7 @@ type allReader func(io.Reader) ([]byte, error)
// response types.
//
//nolint:revive
-func RequestToken(reqURL string, reqValues map[string]string, client *http.Client, readAll allReader) (JwtTokenData, error) {
+func RequestToken(ctx context.Context, reqURL string, reqValues map[string]string, client *http.Client, readAll allReader) (JwtTokenData, error) {
if reqURL == emptyString {
return JwtTokenData{}, errors.New("empty string provided as request URL")
}
@@ -65,7 +65,8 @@ func RequestToken(reqURL string, reqValues map[string]string, client *http.Clien
urlReqValues.Add(k, v)
}
- req, err := http.NewRequest(
+ req, err := http.NewRequestWithContext(
+ ctx,
"POST",
reqURL,
strings.NewReader(urlReqValues.Encode()),
@@ -177,9 +178,14 @@ func ParseRequestJSONValues(
return nil, fmt.Errorf("unable to parse Json request values: %w", err)
}
- maps.Copy(reqValuesMap, objmap)
+ newMap := maps.Clone(reqValuesMap)
+ if newMap == nil {
+ newMap = make(map[string]string)
+ }
+
+ maps.Copy(newMap, objmap)
- return reqValuesMap, nil
+ return newMap, nil
}
// ReadRequestValuesFile reads request values from a JSON file and merges them
@@ -206,8 +212,7 @@ func ReadRequestValuesFile(
// isValidJSON checks if the provided byte slice contains valid JSON data.
func isValidJSON(data []byte) bool {
- var v any
- return json.Unmarshal(data, &v) == nil
+ return json.Valid(data)
}
// DecodeBase64 decodes the base64-encoded header and claims of the access and
@@ -313,14 +318,11 @@ func (jtd *JwtTokenData) ParseUnverified() error {
// ParseWithJWKS parses and verifies the access token against the JSON Web Key Set (JWKS)
// provided at the given URL.
-func (jtd *JwtTokenData) ParseWithJWKS(jwksURL string, keyfuncOverride keyfunc.Override) error {
+func (jtd *JwtTokenData) ParseWithJWKS(ctx context.Context, jwksURL string, keyfuncOverride keyfunc.Override) error {
if jwksURL == emptyString {
return errors.New("emptyString string provided as JWKS url")
}
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
jwks, err := keyfunc.NewDefaultOverrideCtx(
ctx,
[]string{jwksURL},
@@ -395,7 +397,7 @@ func PrintTokenInfo(jtd JwtTokenData, w io.Writer) error {
fmt.Fprintln(w, style.LgSprintf(style.Title2, "%s", token.name))
fmt.Fprintln(w)
- if token.name == "AccessToken" {
+ if token.name == "AccessToken" && jtd.AccessTokenJwt != nil {
fmt.Fprintln(w, style.LgSprintf(style.ItemKey, "Valid %s", validString))
fmt.Fprintln(w)
}
@@ -417,9 +419,9 @@ func PrintTokenInfo(jtd JwtTokenData, w io.Writer) error {
fmt.Fprintln(w)
fmt.Fprintln(w, style.LgSprintf(style.ItemKey, "Claims"))
- tokenTimeClaims, err := unmarshallTokenTimeClaims(token.claims)
+ tokenTimeClaims, err := unmarshalTokenTimeClaims(token.claims)
if err != nil {
- return fmt.Errorf("unable to unmashall time claims from %s: %w", token.name, err)
+ return fmt.Errorf("unable to unmarshal time claims from %s: %w", token.name, err)
}
cTable := table.New().Border(style.LGDefBorder)
@@ -442,15 +444,15 @@ func PrintTokenInfo(jtd JwtTokenData, w io.Writer) error {
return nil
}
-// unmarshallTokenTimeClaims extracts and converts numeric "iat" and "exp" claims
+// unmarshalTokenTimeClaims extracts and converts numeric "iat" and "exp" claims
// from a JSON byte slice into human-readable date strings.
-func unmarshallTokenTimeClaims(claims []byte) (map[string]string, error) {
+func unmarshalTokenTimeClaims(claims []byte) (map[string]string, error) {
tokenClaims := make(map[string]string)
genericClaims := make(map[string]any)
if err := json.Unmarshal(claims, &genericClaims); err != nil {
- return nil, fmt.Errorf("unable to unmarshall claims: %w", err)
+ return nil, fmt.Errorf("unable to unmarshal claims: %w", err)
}
if _, ok := genericClaims["iat"]; !ok {
@@ -470,174 +472,15 @@ func unmarshallTokenTimeClaims(claims []byte) (map[string]string, error) {
}
for k, v := range genericClaims {
- vi := v
-
- if vf, ok := vi.(float64); ok {
- vInt64 := int64(vf)
- t := time.Unix(vInt64, 0)
- dateUTC := t.UTC().Format(time.UnixDate)
- tokenClaims[k] = fmt.Sprintf("%v", dateUTC)
-
- continue
+ if k == "iat" || k == "exp" || k == "nbf" {
+ if vf, ok := v.(float64); ok {
+ vInt64 := int64(vf)
+ t := time.Unix(vInt64, 0)
+ dateUTC := t.UTC().Format(time.UnixDate)
+ tokenClaims[k] = dateUTC
+ }
}
}
return tokenClaims, nil
}
-
-// func unmarshallTokenClaims(claims []byte) (map[string]string, error) {
-// tokenClaims := make(map[string]string)
-//
-// genericClaims := make(map[string]any)
-//
-// if err := json.Unmarshal(claims, &genericClaims); err != nil {
-// return nil, err
-// }
-//
-// for k, v := range genericClaims {
-// var vi any = v
-//
-// if vs, ok := vi.(map[string]any); ok {
-// tokenClaims[k] = fmt.Sprintf("%s", vs)
-// continue
-// }
-//
-// if vf, ok := vi.(float64); ok {
-// vInt64 := int64(vf)
-// t := time.Unix(vInt64, 0)
-// dateUtc := t.UTC().String()
-//
-// outString := fmt.Sprintf("%v (%s)", int64(vf), dateUtc)
-//
-// tokenClaims[k] = fmt.Sprintf("%v", outString)
-//
-// continue
-// }
-//
-// if vls, ok := vi.([]string); ok {
-// tokenClaims[k] = strings.Join(vls, ",")
-// continue
-// }
-//
-// if vla, ok := vi.([]any); ok {
-// tokenClaims[k] = fmt.Sprintf("%v", vla)
-// continue
-// }
-//
-// if vb, ok := vi.(bool); ok {
-// tokenClaims[k] = fmt.Sprintf("%v", vb)
-// continue
-// }
-//
-// if vs, ok := vi.(string); ok {
-// tokenClaims[k] = vs
-// } else {
-// fmt.Printf("not asserted: %v\n", v)
-// }
-// }
-//
-// return tokenClaims, nil
-// }
-//
-// func unmarshallTokenHeader(header []byte) (map[string]string, error) {
-// tokenHeader := make(map[string]string)
-//
-// if err := json.Unmarshal(header, &tokenHeader); err != nil {
-// return nil, err
-// }
-//
-// return tokenHeader, nil
-// }
-//
-// func getTokenClaimsMap(t *jwt.Token) (map[string]string, error) {
-// m := make(map[string]string)
-//
-// // Mandatory Registered Claims
-// issuer, err := t.Claims.GetIssuer()
-// if err != nil || issuer == emptyString {
-// return nil, fmt.Errorf("unable to get issuer: %w", err)
-// }
-//
-// subject, err := t.Claims.GetSubject()
-// if err != nil || subject == emptyString {
-// return nil, fmt.Errorf("unable to get subject: %w", err)
-// }
-//
-// issuedAt, err := t.Claims.GetIssuedAt()
-// if err != nil || issuedAt == nil {
-// return nil, fmt.Errorf("unable to get issuedAt: %w", err)
-// }
-//
-// expiresAt, err := t.Claims.GetExpirationTime()
-// if err != nil || expiresAt == nil {
-// return nil, fmt.Errorf("unable to get expiration time: %w", err)
-// }
-//
-// audienceElems, err := t.Claims.GetAudience()
-// if err != nil {
-// return nil, fmt.Errorf("unable to get audience: %w", err)
-// }
-//
-// audience := strings.Join(audienceElems, ",")
-//
-// m["iss"] = issuer
-// m["sub"] = subject
-// m["iat"] = issuedAt.UTC().String()
-// m["exp"] = expiresAt.UTC().String()
-// m["aud"] = audience
-//
-// // Optional Registered Claims
-// notBefore, err := t.Claims.GetNotBefore()
-// if err != nil {
-// return nil, fmt.Errorf("unable to get notBefore time: %w", err)
-// }
-//
-// if notBefore != nil {
-// m["nbf"] = notBefore.UTC().String()
-// }
-//
-// return m, nil
-// }
-//
-// func getUnregisteredClaimsMap(t *jwt.Token, existingClaims map[string]string) map[string]string {
-// unregistreredClaims := make(map[string]string)
-//
-// var claimsInt any = t.Claims
-//
-// if claimsMap, ok := claimsInt.(jwt.MapClaims); ok {
-// for ck := range claimsMap {
-// if _, alreadyPresent := existingClaims[ck]; alreadyPresent {
-// continue
-// }
-//
-// cki := claimsMap[ck]
-//
-// if cStringValue, ok := cki.(string); ok {
-// unregistreredClaims[ck] = cStringValue
-// }
-//
-// if cIntList, ok := cki.([]any); ok {
-// unregistreredClaims[ck] = fmt.Sprintf("%s", cIntList)
-// }
-// }
-// }
-//
-// return unregistreredClaims
-// }
-//
-// func getTokenHeadersMap(t *jwt.Token) map[string]string {
-// m := make(map[string]string)
-//
-// for k, v := range t.Header {
-// headerValue := "undefined"
-// i := v
-//
-// if v, ok := i.(string); ok {
-// headerValue = v
-// }
-//
-// m[k] = headerValue
-// }
-//
-// return m
-// }
diff --git a/internal/jwtinfo/jwtinfo_test.go b/internal/jwtinfo/jwtinfo_test.go
index 3a964cf..33f419d 100644
--- a/internal/jwtinfo/jwtinfo_test.go
+++ b/internal/jwtinfo/jwtinfo_test.go
@@ -2,6 +2,7 @@ package jwtinfo
import (
"bytes"
+ "context"
"encoding/base64"
"fmt"
"io"
@@ -281,6 +282,7 @@ func TestRequestToken(t *testing.T) {
}
_, err = RequestToken(
+ context.Background(),
serverJwtEndpoint,
reqValues,
client,
@@ -398,6 +400,7 @@ func TestParseWithJWKS(t *testing.T) {
reqValues["scope"] = tt.scope
td, err := RequestToken(
+ context.Background(),
serverJwtEndpoint,
reqValues,
client,
@@ -442,6 +445,7 @@ func TestParseWithJWKS(t *testing.T) {
)
err = td.ParseWithJWKS(
+ context.Background(),
serverJwksEmptyEndpoint,
keyfuncOverrideTesting,
)
@@ -470,6 +474,7 @@ func TestParseWithJWKS(t *testing.T) {
)
err = td.ParseWithJWKS(
+ context.Background(),
serverJwksFaultyEndpoint,
keyfuncOverrideTesting,
)
@@ -483,6 +488,7 @@ func TestParseWithJWKS(t *testing.T) {
}
err = td.ParseWithJWKS(
+ context.Background(),
serverJwksEndpoint,
keyfuncOverrideTesting,
)
@@ -536,6 +542,7 @@ func TestParseWithJWKS_Errors(t *testing.T) {
td := JwtTokenData{AccessTokenRaw: token}
err = td.ParseWithJWKS(
+ context.Background(),
"",
keyfunc.Override{},
)
@@ -555,6 +562,7 @@ func TestParseWithJWKS_Errors(t *testing.T) {
td := JwtTokenData{AccessTokenRaw: token}
err = td.ParseWithJWKS(
+ context.Background(),
"https://localhost:54321/jkws.wrong.json",
keyfunc.Override{},
)
@@ -574,6 +582,7 @@ func TestParseWithJWKS_Errors(t *testing.T) {
td := JwtTokenData{AccessTokenRaw: token}
err = td.ParseWithJWKS(
+ context.Background(),
"https://loca#$%^/jkws.json",
keyfunc.Override{},
)
@@ -688,8 +697,8 @@ func TestDecodeBase64(t *testing.T) {
}
}
-func TestUnmarshallTokenTimeClaims(t *testing.T) {
- t.Run("unmarshallTokenTimeClaims", func(t *testing.T) {
+func TestUnmarshalTokenTimeClaims(t *testing.T) {
+ t.Run("unmarshalTokenTimeClaims", func(t *testing.T) {
t.Parallel()
var jtd JwtTokenData
@@ -722,7 +731,7 @@ func TestUnmarshallTokenTimeClaims(t *testing.T) {
err = jtd.DecodeBase64()
require.NoError(t, err)
- claimsMap, err := unmarshallTokenTimeClaims(
+ claimsMap, err := unmarshalTokenTimeClaims(
jtd.AccessTokenClaims,
)
require.NoError(t, err)
@@ -740,7 +749,7 @@ func TestUnmarshallTokenTimeClaims(t *testing.T) {
})
}
-func TestUnmarshallTokenTimeClaims_MapErrors(t *testing.T) {
+func TestUnmarshalTokenTimeClaims_MapErrors(t *testing.T) {
invalidJSONClaims := "can not unmarshal"
noIatClaims := "{\"exp\":1}"
@@ -757,7 +766,7 @@ func TestUnmarshallTokenTimeClaims_MapErrors(t *testing.T) {
{
name: "invalid JSON",
claims: []byte(invalidJSONClaims),
- errMsg: "unable to unmarshall claims",
+ errMsg: "unable to unmarshal claims",
},
{
name: "missing Issued At",
@@ -787,7 +796,7 @@ func TestUnmarshallTokenTimeClaims_MapErrors(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- _, err := unmarshallTokenTimeClaims(tt.claims)
+ _, err := unmarshalTokenTimeClaims(tt.claims)
require.ErrorContains(t, err, tt.errMsg)
//nolint:revive
})
@@ -801,13 +810,14 @@ func TestUnmarshallTokenTimeClaims_MapErrors(t *testing.T) {
//nolint:revive
func TestPrintTokenInfo(t *testing.T) {
tests := []struct {
- name string
- user string
- pass string
- scope string
- bodyReader allReader
- expError bool
- expReqError bool
+ name string
+ user string
+ pass string
+ scope string
+ bodyReader allReader
+ skipValidation bool
+ expError bool
+ expReqError bool
}{
{
name: "default case",
@@ -816,6 +826,14 @@ func TestPrintTokenInfo(t *testing.T) {
bodyReader: io.ReadAll,
scope: "default",
},
+ {
+ name: "without validation",
+ user: "test",
+ pass: "known",
+ bodyReader: io.ReadAll,
+ scope: "default",
+ skipValidation: true,
+ },
}
for _, tc := range tests {
@@ -850,6 +868,7 @@ func TestPrintTokenInfo(t *testing.T) {
reqValues["scope"] = tt.scope
td, err := RequestToken(
+ context.Background(),
serverJwtEndpoint,
reqValues,
client,
@@ -857,20 +876,23 @@ func TestPrintTokenInfo(t *testing.T) {
)
require.NoError(t, err)
- keyfuncOverrideTesting := keyfunc.Override{
- Client: server.Client(),
- }
+ if !tt.skipValidation {
+ keyfuncOverrideTesting := keyfunc.Override{
+ Client: server.Client(),
+ }
- err = td.ParseWithJWKS(
- serverJwksEndpoint,
- keyfuncOverrideTesting,
- )
- require.NoError(t, err)
- require.True(
- t,
- td.AccessTokenJwt.Valid,
- "JWT token must be valid",
- )
+ err = td.ParseWithJWKS(
+ context.Background(),
+ serverJwksEndpoint,
+ keyfuncOverrideTesting,
+ )
+ require.NoError(t, err)
+ require.True(
+ t,
+ td.AccessTokenJwt.Valid,
+ "JWT token must be valid",
+ )
+ }
err = td.DecodeBase64()
require.NoError(t, err)
@@ -883,7 +905,6 @@ func TestPrintTokenInfo(t *testing.T) {
stringsToCheck := []string{
"JwtInfo",
- "Valid",
"Header",
"Claims",
"alg",
@@ -896,9 +917,17 @@ func TestPrintTokenInfo(t *testing.T) {
"iat",
}
+ if !tt.skipValidation {
+ stringsToCheck = append(stringsToCheck, "Valid")
+ }
+
for _, outStr := range stringsToCheck {
require.Contains(t, got, outStr)
}
+
+ if tt.skipValidation {
+ require.NotContains(t, got, "Valid")
+ }
})
}
}
@@ -939,7 +968,7 @@ func TestPrintTokenInfo_Errors(t *testing.T) {
//nolint:revive
buffer := bytes.Buffer{}
- // Valid claims so unmarshallTokenTimeClaims succeeds.
+ // Valid claims so unmarshalTokenTimeClaims succeeds.
now := time.Now().Unix()
exp := now + 3600
claimsJSON := fmt.Sprintf(`{"iat": %d, "exp": %d}`, now, exp)
@@ -957,7 +986,7 @@ func TestPrintTokenInfo_Errors(t *testing.T) {
require.Positive(t, buffer.Len())
})
- t.Run("unmarshallTokenTimeClaims error", func(t *testing.T) {
+ t.Run("unmarshalTokenTimeClaims error", func(t *testing.T) {
buffer := bytes.Buffer{}
jtd := JwtTokenData{
@@ -967,6 +996,6 @@ func TestPrintTokenInfo_Errors(t *testing.T) {
err := PrintTokenInfo(jtd, &buffer)
require.Error(t, err)
- require.ErrorContains(t, err, "unable to unmashall time claims from AccessToken")
+ require.ErrorContains(t, err, "unable to unmarshal time claims from AccessToken")
})
}
diff --git a/internal/style/style.go b/internal/style/style.go
index 2a5eb13..ce659d1 100644
--- a/internal/style/style.go
+++ b/internal/style/style.go
@@ -8,7 +8,7 @@ import (
var (
glamourDefStyle = "tokyo-night"
- chromaDefStyle = "dracula"
+ chromaDefStyle = "catppuccin-frappe"
// LGDefBorder is the default hidden border for lipgloss tables.
LGDefBorder = lipgloss.HiddenBorder()
From 900a2f5715edb5bddd9575525ed4230ad5c843a8 Mon Sep 17 00:00:00 2001
From: Zeno Belli
Date: Wed, 29 Apr 2026 15:38:12 +0200
Subject: [PATCH 2/4] docs: update readme and add jwtinfo and jwks commands
---
README.md | 166 +++++++++++++++---
...tps-wrench_jwtinfo_read_validate_token.png | Bin 0 -> 113139 bytes
.../https-wrench_jwtinfo_request_token.png | Bin 0 -> 235798 bytes
3 files changed, 140 insertions(+), 26 deletions(-)
create mode 100644 assets/img/https-wrench_jwtinfo_read_validate_token.png
create mode 100644 assets/img/https-wrench_jwtinfo_request_token.png
diff --git a/README.md b/README.md
index 39bc995..14dbe98 100644
--- a/README.md
+++ b/README.md
@@ -8,10 +8,11 @@
HTTPS Wrench, a wrench not to bench
-**HTTPS Wrench** is a CLI program to make Yaml defined HTTPS requests and to
-inspect x.509 certificates and keys.\
+**HTTPS Wrench** is a tool for maintainers of secure HTTP endpoints.
+It enables executing YAML-defined HTTPS requests, inspecting x.509 certificates, private keys, JSON Web Tokens (JWT), and
+generating JSON Web Key Sets (JWKS).\
**HTTPS Wrench** was born from the desire of a disposable Bash script to become
-a reliable tool for mechanics of the World Wide Web.\
+a reliable companion for mechanics of the World Wide Web.\
`https-wrench` will, one day, take the place of `curl` in the hearts and the
eyes of whoever is about to migrate a DNS record from a webserver to a load
balancer, reverse proxy, Ingress Gateway, CloudFront distribution.
@@ -26,30 +27,37 @@ Check the help:
```plain
❯ https-wrench -h
-HTTPS Wrench is a tool to make HTTPS requests according to a Yaml configuration file and to inspect x.509 certificates and keys.
+HTTPS Wrench is a tool for maintainers of secure HTTP endpoints.
+It enables executing YAML-defined HTTPS requests and performing in-depth
+inspection of x.509 certificates, private keys, and JSON Web Tokens.
-https-wrench has two subcommands: requests and certinfo.
+https-wrench provides several specialized subcommands:
-requests is the subcommand that does HTTPS requests according to the configuration provided
-by the --config flag.
+requests: Execute HTTPS requests according to a structured YAML configuration,
+supporting custom CA bundles and verbose output.
-certinfo is a subcommand that reads information from PEM encoded x.509 certificates and keys. The certificates
-can be read from local files or TLS enabled endpoints.
+certinfo: Inspect PEM-encoded certificates and keys from local files or remote
+TLS endpoints. Verify certificate chains and key pairings.
-certinfo can compare public keys extracted from certificates and private keys to check if they match.
+jwtinfo: Decode, inspect, and validate JSON Web Tokens (JWT) using local files
+or remote JWKS endpoints.
-HTTPS Wrench is distributed with an open source license and available at the following address:
-https://github.com/xenOs76/https-wrench
+jwks: Generate pretty-printed JSON Web Key Sets (JWKS) from public keys for
+exposure on well-known endpoints.
+
+Distributed under an open-source license: https://github.com/xenOs76/https-wrench
Usage:
https-wrench [flags]
https-wrench [command]
Available Commands:
- certinfo Shows information about x.509 certificates and keys
+ certinfo Inspect and verify x.509 certificates and keys
completion Generate the autocompletion script for the specified shell
help Help about any command
- requests Make HTTPS requests defined in the YAML configuration file
+ jwks Generate a JSON Web Key Set (JWKS) from a public key
+ jwtinfo Inspect and validate JSON Web Tokens (JWT)
+ requests Execute YAML-defined HTTPS requests
Flags:
--config string config file (default is $HOME/.https-wrench.yaml)
@@ -71,15 +79,15 @@ Get the help:
```plain
❯ https-wrench requests -h
-https-wrench requests is the subcommand that does HTTPS requests according to the configuration
+https-wrench requests is the subcommand that does HTTPS requests according to the configuration
pointed by the --config flag.
A sample configuration can be generated as a starting point (--show-sample-config).
-The Github repository has more configuration examples:
+The Github repository has more configuration examples:
https://github.com/xenOs76/https-wrench/tree/main/assets/examples
-It also provides a JSON schema that can be used to validate new configuration files:
+It also provides a JSON schema that can be used to validate new configuration files:
https://github.com/xenOs76/https-wrench/blob/main/https-wrench.schema.json
Examples:
@@ -90,7 +98,7 @@ Usage:
https-wrench requests [flags]
Flags:
- --ca-bundle string Path to bundle file with CA certificates
+ --ca-bundle string Path to bundle file with CA certificates
to use for validation
-h, --help help for requests
--show-sample-config Show a sample YAML configuration
@@ -131,16 +139,16 @@ Get the help:
```plain
❯ https-wrench certinfo -h
-HTTPS Wrench certinfo: shows information about PEM certificates and keys.
+Inspect and verify PEM encoded x.509 certificates and keys.
-https-wrench certinfo can fetch certificates from a TLS endpoint, read from a PEM bundle file, and check if a
+https-wrench certinfo can fetch certificates from a TLS endpoint, read from a PEM bundle file, and check if a
private key matches any of the certificates.
-The certificates can be verified against the system root CAs or a custom CA bundle file.
+The certificates can be verified against the system root CAs or a custom CA bundle file.
The validation can be skipped.
-If the private key is password protected, the password can be provided via the CERTINFO_PKEY_PW
+If the private key is password protected, the password can be provided via the CERTINFO_PKEY_PW
environment variable or will be prompted on stdin.
Examples:
@@ -159,13 +167,13 @@ Usage:
https-wrench certinfo [flags]
Flags:
- --ca-bundle string Path to bundle file with CA certificates
+ --ca-bundle string Path to bundle file with CA certificates
to use for validation
--cert-bundle string Path to PEM Certificate bundle file
-h, --help help for certinfo
--key-file string Path to PEM Key file
- --tls-endpoint string TLS enabled endpoint exposing certificates to fetch.
- Forms: 'host:port', '[host]:port'.
+ --tls-endpoint string TLS enabled endpoint exposing certificates to fetch.
+ Forms: 'host:port', '[host]:port'.
IPv6 addresses must be enclosed in square brackets, as in '[::1]:80'
--tls-insecure Skip certificate validation when connecting to a TLS endpoint
--tls-servername string ServerName to use when connecting to an SNI enabled TLS endpoint
@@ -197,10 +205,106 @@ been used to generate the certificate:
❯ https-wrench certinfo --tls-endpoint localhost:9443 --ca-bundle rootCA.pem --key-file key.pem
```
+### HTTPS Wrench jwtinfo
+
+`jwtinfo` allows you to decode and inspect the claims of a JSON Web Token. It can also validate the token signature if a JWKS endpoint is provided.
+
+
+View Jwtinfo Help (`https-wrench jwtinfo -h`)
+
+```plain
+❯ https-wrench jwtinfo -h
+
+Inspect and validate JSON Web Tokens (JWT) from files or remote providers.
+
+Examples:
+ export REQ_URL="https://sample.provider/oauth/token"
+ export REQ_VALUES="{\"login\":\"values\"}"
+ export VALIDATION_URL="https://url.to/jkws.json"
+
+ # Read a JWT token from a local file
+ https-wrench jwtinfo --token-file /var/run/secrets/kubernetes.io/serviceaccount/token
+
+ # Request a JWT token using inline values
+ https-wrench jwtinfo --request-url $REQ_URL --request-values-json $REQ_VALUES
+
+ # Request a JWT token using values file
+ https-wrench jwtinfo --request-url $REQ_URL --request-values-file request-values.json
+
+ # Request and validate a JWT token
+ https-wrench jwtinfo --request-url $REQ_URL --request-values-json $REQ_VALUES --validation-url $VALIDATION_URL
+
+Usage:
+ https-wrench jwtinfo [flags]
+
+Flags:
+ -h, --help help for jwtinfo
+ --request-url string HTTP address to use for the JWT token request
+ --request-values-file string File containing the JSON encoded values to use for the JWT token request
+ --request-values-json string JSON encoded values to use for the JWT token request
+ --token-file string File containing the JWT token
+ --validation-url string Url of the JSON Web Key Set (JWKS) to use for validating the JWT token
+
+Global Flags:
+ --config string config file (default is $HOME/.https-wrench.yaml)
+ --version Display the version
+```
+
+
+
+Decode a token from a file:
+
+```shell
+❯ https-wrench jwtinfo --token-file mytoken.jwt
+```
+
+### HTTPS Wrench jwks
+
+`jwks` generates a public JSON Web Key Set from a PEM-encoded public key. This is useful for exposing your public keys at a `.well-known/jwks.json` endpoint.
+
+
+View Jwks Help (`https-wrench jwks -h`)
+
+```plain
+❯ https-wrench jwks -h
+
+Generate a pretty-printed JSON Web Key Set (JWKS) from a public key file.
+
+The generated JWKS contains only public key parameters and is safe
+to be exposed (e.g. at a /.well-known/jwks.json endpoint).
+
+Examples:
+ # Generate a public JWKS from an RSA public key
+ https-wrench jwks --public-key-file rsa-public.pem
+
+ # Generate a public JWKS with a custom Key ID (kid)
+ https-wrench jwks --public-key-file ec-public.pem --kid "my-custom-key-id"
+
+Usage:
+ https-wrench jwks [flags]
+
+Flags:
+ -h, --help help for jwks
+ --kid string Optional explicit Key ID (kid) to use. If not provided, a thumbprint-based ID is inferred.
+ --public-key-file string File containing the PEM-encoded public key
+
+Global Flags:
+ --config string config file (default is $HOME/.https-wrench.yaml)
+ --version Display the version
+```
+
+
+
+Generate a JWKS with an inferred thumbprint KID:
+
+```shell
+❯ https-wrench jwks --public-key-file public.pem
+```
+
### Sample output
-HTTPS Wrench requests, (long) sample configuration output
+HTTPS Wrench requests, sample configuration output
@@ -219,6 +323,16 @@ been used to generate the certificate:
+
+HTTPS Wrench jwtinfo, request token
+
+
+
+
+HTTPS Wrench jwtinfo, read token and validate
+
+
+
## How to install
diff --git a/assets/img/https-wrench_jwtinfo_read_validate_token.png b/assets/img/https-wrench_jwtinfo_read_validate_token.png
new file mode 100644
index 0000000000000000000000000000000000000000..80eb27f919fb1a510739dee75fe1894be18af94c
GIT binary patch
literal 113139
zcmdqJWl)^m)BhPklHfsu1_;3|!8HU30RjYfcXxLP3=%A8aJRwThQT!q?(RA`1A`1Z
zx$ocppJ$)l+WNoSs=Z!aMN#28r%!i(`_p|A{#{-Q>lNv%Cr_SWNq?14dh+CX`jaOp
zQZLYuKgl|>seSUaxkXw+OvNMpXc^r@MHRt&dh#cq_$$u4A1l^-ps(x^K|x`ArPlhh
z6|L=Vx~>nv)&nEmMI+*;VxLk~_CeR5yl6q@3wP=9e`2LU?jMo~+Ou{C_IhLynmkNlr*6ESBZa~cN}Z)X#RQR{=a$44SmVi4^Mi#AWCK1c8=(>0oBBg}9sz
z?hi0C1Q9LNz3fbh^BpgcC3vQNo0nZh@>1GzuKth@n<^@>R|zUeRoY>3_Bp)Z`2PLh
zFZx31P>RI^%C-LiF(^KBHkKj(7T)Sm|FQ|eh!R5vsGA1HHX_8iNbLmnZW~a1tRS_a
zCO>f0NJ44E)V&=Hs}JH5@S3<;A4`Sf)ReR7GJ>UaGE}xw5Y?m_t)lDUQ0~sd8+s}
zx0u=i1E`JDsfF)o@y{OlP=TCe>Z#_}mZ!(##QfIa;Cc?dvum2n8v-u{oOf-0fXQK)
z#WJavJFiEb6_f+C{ig_*$1}IPE)p-vSQfpon=?F46h7S
zyv`Ejiy7#tG_+;rjSK0eS6w8gyXM$@t(0~fYyxC4KY??|d)yh3xahcH!cw+f>8=%m
zZm@A9mf}5$()Q3Nesg)Df7-ne`fw^Jk-kFD5FRu)2)_dQ$jCdR%>j#(55)!y8zIS+g+1;yIkGP`j%b}N-
zYA>G?3mUk5F=Rzp(BO6o?<+VVymt3Bup(J6ru8H}REo0YO%bFgSJi_fI_qa=
z`-3_8#G9!1@7@;}t_H@#s3NC>2EsV*+a1~Ev!sjNRx&f~IDVHN6YJ}<`&=UM_m)bq
zX3ADWf&q|h?b(JD*9X3IVdBV}AF}f7vzln2mtP`_hIgd?Mw$gDphincVQ@B)@=1V{
zK|pRDHU9gz{{5sHPV$>phO9E52gCE3CS^y<;zWbI0oJ%eRuc8A>ZuiabQ%3lIfu*7
zIQB?QrFY3M@K|^*C8;s%lU%-_6;N}2#3TY%%>05(xh>;TdXoy&mPG%8aP+({9JFeH
zc-)@BPtPwqXcgKc1=GwNye6`%%o@V;Nj9Kol^E|fLovZC1E%Q}h
z$ycUvVmAKC_(?@n*`FLaW3)WvUaEUM?<{@ZUnq6Wu;h4Ge`C8W&!XR4E92$wZ|?(J
zpM3H1Wryf0ob-^Ful-MofM*q^9hKk4DxcR;ZhC{qpc%{U2LF=Ng^4#~(Zco&0Gp_3
z{~OEpKW<2Kq+n~bhhKo%3Bvnem)>3X>r&Rie*CkVWcja^VSKUK)o9ts%yKtB1U}^_
zpeFBIMC2&0%ZP+WWB3n7UYy^H4b=9*#&=S_!*(y)&*X1hPtZWE=rR)%v1fN)1W-7H
zGQHk^;{r01D1LzpqM{`-Iy-qPt+SlfzqXm*7U>9JUtP~b_xWLbU
zY{wJepm=o;`A|Y>eBh{UCdPjYC|USDXD`Hp8WMX?b9-I!zwUJ)q)Z0{e$M
zLF6$~Zn8y~v|X$i0Zs^slh>M)MdF@jc7Asbx0h73{d_vAPSugqZGH-A6;(}p&(VQn
zXef4mQjw8YijtmtaNOB#e$-j`{kukxasR0CCl~jkWNm#r0ov#Cf8*}^F2vOPzIhd<
zz0k$%>!u~U3O+i*@JCO=<2c>%olNvte;9eh<_0d0YlN`e+FIbiJP&ps26v+}My3$f
z{Nm%MK6^_GekXFRWe4Fu@MeaktEhXOUCJK=fUMxoezG@dJl#qfPr&Wt7S4BBu?F?o
z7AH6y=5(~SBU-;#TE(Sq+z*3f(;7RJbbLI;l4`8F1x{Ynd9E3LDw67J5H(^C`S-ijaL-;-G9O<&Auj?_VLX>i=75T{
ztjXH3H(DK55YRn%g-qc3yM~PN6KhLQFl66M|K4U#%US@>CA9I6gp->?`M$*1Z@lrR
z)Mw__a`Zuvu+z6a^vSAZs#f>h&kd_Jsyr;GHzIb)stzG$usyXlJ0k5wl-%(Z6@e$DT*jARP!|p8QRt2{CU)A#^
z4M*=rJbQM(-Ik$i=ezG}CbuK|Hw09e`=3Xh$}3)?#z5k6$G}++kGWd}??8{Q)^-9X
zJ}$iv
zU;&ff9WEXr0BV@%+5z0V$`2@X&=O;D&J{jdit>%hV6xQ21Jcunp>Bx_^}9_h6t=5u+&)STe(e*b=dutxNV
zHjwhMs{z`tI?`&~ueLV{#BWFV$$^rzsl0SY3MT(<{8SSIFiTx*?Ku7wRPB
z7j+AtI-RcJ&3{pI=kB9S${-q~x+Y?f|N2AfE0j)q+7ahvB3RZqdZ8%Xtxz&tby05!
z8Wrh!1Lmq{VQ|GL7sj=E{VUQ?SpRN}IuZ4%naGP5C;vzb4cPm0!120^*+!^MTyS`F
zgl_10eTz#~de^t%5P?a!=Pq@dsanQ2~#yEAt&Ag$=8niw#2ebst53e+@;@^Dcd&Ja2Kr(m
zd3CF||0aIL$$}Q@1Wsl$3$l=|9}@4_eNY8f4Vt7}L(?j$v@Z9smwhW~uHttMU%d!9
zNf8~+#wTL#GXF+w`0medOK!-bRVf#F$UJ5S!s#2!f=vYd6~x|Ob-Q(Eqg2)4kY>SH
zrf@^%&@c${y`Hsn`bZhi_%}+9e}OG$~fhsalP2e!*1OQeOXeFT}N1
zdDXO$w^(2OWgtsQ9
zTeDB}%oDY!!2@6jQ~l>p@@5&TRv|}AKB}r#l8mM*DjxM&6hc0f&ctm2mhk|CzB)V-
zQM+vMjyKcqzaix1oR?GTbK|p|IbXs`Dw
z9vb{y#<7+bIFL;+k+IX8PWI7b{Vc4Cp7z<&tbm%mtbk!;1w&nS%2kQLhKT5n=@rkz
zg);$ankQ;VX^@=ZYNv9Fp!YLmRUq~m&X-7NYP!AIO2s>rH8gD{6%)Y^4bd7}Z2
zzrMd8INGkGYg$ZvmRtD2o%eaB=dGH=mmeFVR1ZIm`ze4wF^ni4VLhxQABu_f{GcC?
z7X2GmhRfM8yo>4ns`d{@SE~3C4}|`V=2z+*J37aSD&ewZyq)Qu%~6FPW#Wg>Vy~ks
zaMunnUo>yJqAcJ0Bwnf+YvPm$;PeMf^m&x9SX}MR^^3?+jZugw#x=bz>#Hek^I~UD
z&UcOgleuI}qo=9MG*l3(kqJ!yu9(^jpGt3$oothhz)e?i*B;yWrSz_#)IPYyZP`wv
zTy4ow+tXaLzL@V8(!}XI-fnAMrn&1wFN;?$&S?(w_z}`jr-1R|R5BT&`1NaSxYlbsFh1>-NPd$lFiJh-EF7clC21#`cWH$%Hy{?+OhK$&%e#jpB~QR`(+4H
z@Ap#y^V!4sSr?;1NA5npg8ZyWuG=Zs*5Cn)lNq!iyz1ILMQRpY6IA`RFHd4NxOQa}
zguQjqE3tKtx^rKMEVqcCJb29CzU#}mSsR627H;Kn?Cj8(+;el>+|0@9M6#z#4xZ_&
z7CAG`+2k8LECh|T^sUvQrnyFST4L}Vk_Jx_*$8|@E5dcACOqUh>PwLBt}`xWy@U(NVa!YRgbYkmKHTK_QE{YW4*vf5Ik#Rud4
zPpTgwCnBNK*6u?SlGT*CmyTx>r5X1wRLrjMo)!;sTb<`=oNFmNo3>b@j
za(8Z%RJjxw6l64w+}QZ5^Vo0yqy&j)kP+0(6lKz%pJEog=js@j&O`~2uAbGP=vpYJ
z-TX|=_`#cn@Z#(JsP&6XU;1b0Sie;DB^4UNSN$TpGE{+w5wKiFu#5QVjrH1MwavHh
z&W3TqYjs3))a!ZIcmlg>C^3|*iv@+1dLaRd#@W|p3zpw?eY{|jUp)zp(ujmxhpDKo
zSDyj`@OOOP`JTP(+F^_B%r6hlm(^~gcrIPAKZVlJnduuLzdVjI#xt{ayY;j$*;#eQ;)X$Rwin#koI*x@>3)^Ntv*wlPt7t*^~&kOE4!*^8Ov98uQ
z8#HIOUcw$1iyf;x*Bf2NgWoSQ!=f%&J3+QMOo$q(
z-EfQRy?B_RQmHboGoJKgOC`curh+zTq@DFCm3ST&kNV%Lepl7J>i6H8KHfW{)9jy$
zo|xeUkFuhUQc0r;Jqjv(eVX_#Hx|Mc{Edi<~Su%>=m3AT~%k5s!0
zIdJ!za?{}sxfoG}9WDG!vuh`#5OC`YNpY5Ioyt+yRus@L}0{Q;X@=*$e|K3U(
z?wk29iUN_nEWout+NV!e9%nI9_~2eH2NM{QA@I2-KUAKwyXkd7-PWs~t6F<;i8NSE
zsf5`VQp8*f(cvtMx@#~Qw;7FH8+_E`h`n*(@udCnB_;{iD+Q67Cn2b)DV)yOUNF*t
z#uX1V5fKrOy9fJfuik41XZQZoxOfxs-AQ_!&HfWaWLulgozKIAw!w^CW1c&>^W2c1
zk(0<;{qd(bI&5xouHm}-TCX#|q;{-ATz$*LT;Rx@OkY%ViNpKp?IqbWc6+M)B0A&A
zTf(_&-}q7=W%0V{bRlRT)8yv_*X`@EYv&b~)rm5cm?V
z2HI;n@kFdzMl2HCwZLczM#Ya^sQBuywC2FG5$9ok6A64^w~a@AKcB
z+d?n=!NhNp*+jbsiRTmFXrafJ^3FeBV#aS*KY*G=WmR!pl$5`@wmq-Jt-&
zO9uz;2$YzC8GPH7Qo%A6qgF{ku8d#zuG?3dP`Ki(qiem$?$*xE+SZvk37-ij(c`IV
z{T-0{jh3W~m$c=^{WIZT@!GlC$t*7W2~qj^PY4KRmN4sd@8tKE-8s6tXdTw?Ey)+a
z&vVxCNVwgYe#INbr6;__N$3g^^5o=5i~0avn2vP3AMoRsyH6K&SK>iY`&F^wB>uUp
z+J2PBX0Zm1l9Dn-2>RYcM@HafAG1BLB4T~MjcW3cTJTja
zynoNSkhav;`!0NQXD5_C%aZ~b466-Cl$sxd#Jf143u$qkAh5sew-le}5w9+e7J=_pr>Zo5ewCB1
z+nxM9DP{89K2E<#o>H;?5u0Ku?}o3~!-lS>KihbhlF!YA%7{I)62@0<`xs7^&~$Tm
zdrM7|%X$l=iugz$lbxUcRUTiIj*jk}j}U&h_0fLdlkX@biqG{v*5^6BOgAJ;c*nu9
zeIg;#K4=1JF)i>KdH(t!P%;y}m!LcBSo;M(ZTNE-bUtVBtn5eX-xI9j^Mq2^jwzaa
zA?k-!DS&xd&pZs;0*waKh``8s9@`hzKaR`lUIyUJHNbfjl9J|%6fzv~gMzwGA9%9{
zpoBPP3Zi!nON|?(o*Q$1aCi@6?Us+Sa{Tsnf?j1pMb-q|b#IDL*c+##5DT1JTs4Mx
zpziMP>hD)jvvr1fl-NEbtPm&cR8AaLFrQ#pL7dHxgX^K91+&lV;bd>7DPKBr^0_$B
zKYJd`X}09@j=qnEpI=PXk70RKCWcvk7cc>J*uIF&zP#Q2^8l5}w3oS1-*;8o_fz!B
zWM0NDm^CjLR&}OO`US3N69MvzCXlPEFFHrlMq&mSvuA$Mf^$w3%knKRm%YF)<^Qna
zpN$oY1q!e(>NVK5cTIMo6m7u)`ajIC#7_Y2{UQu$`!+k;b(Dece`8;KPp!xWwKf{uHLPy07Aa7Q_d|p^eYPZy78GaFl#3w3CMEs6`mA-}-?&)?
zMMVSIQntam)Lh7-9~J%r%R0(f3YP0Cp`9-1t%dM+$lBCqJmWE*A2p@(L7o=zPX=La
zDPeshxfPzPe!*K}cQ7&q+8v+yUW47}5f4~_zu`pPylN@x@d857?PQ7RF`C~WO}FK$
z;lw;gs!}A(YVqYN!am0rT&7)19wYj|^!{6BNEDeGGlZ8Lk}ksNZ_^WEzf-oDzTj^a
z9UNgPIYte$wY6IG-{AAyzlx!lU~jrG1U#;Kz!m31v+Xb6Hm_v)oC;u5EisE~H@fj7
zYs}}r?TZvBq{J0#AJdny$JNefL?2Mks&~&8hYFH+cQ-aQUvk3>kv5N9fU4$cU+cYi
zf!Q~-y{ZO38bf4|c#mLS?(^abA{e}#Z8=EBfTN$tMQez^~x
z^$lBYj2r+T?@`31bd1?iRlPt;Eq;(c3NN|)Wo-|OF59!Y{HKxW+fo0!bEcp(mC7%$
zt1iye>)A(c;BbTGVPU-o{3?dS>WF}R4m@0PA-8{~KI^!BaR0r9qQb`yFG24yy_sES
z@Inq5WI^&w%j`0v&IOA(m$zju%@MN-dArFA5L)xh6KlbWiiX|v2;?RNZ*0(DQn+D7
zQ;Gz9p>l|(NQ{DH3_S~2@bE}5xD4ZB(Hw7?agoB&U+L`a?foj#6t))YU;({vR>rm$
zQc`|R?)8P0i6s_%Cy5jHGzR*t)@*vh3WrS*wKH%!;^$A<^U?czA%d-4YOXTEh)9gM
zwD_p}9zM^v7&HbljQ;r6&eS_~-H%2OKeDrNJp;pkTG7u6a#G8;hg64yJml5Fi*3C}
z%xZP?dLNcbRmtFYJ!j(Bt9Sc%qhqD2`99hz)we^5e|XJdS-|Or$De&lKCtn$^AjFh
zL4$*z%^I}_^zV9ftNeGB;AtG=YTx;M&x;>E8e1|NK{7rUu?c5rHOA%952P8oVke0@
zTv73rEsXXy%-L~R6)$5`>HF7Nya{N&Kw#?Y``_-<
zB#ahqa25%}dA`6s`S)i1Qy#=hWg9b3VX)rL2SMn*gj|fIK1&@qk#H9r>&U3uE?rqx
zahN!9b>lG<(tE1bsTJU-dJ%MsMQ~%=jZWydPCAO9*THd
zj5KBpxF!0Z&;jR`hCw+Q0dUO%l~6CkF+egSxt`=U8iz+F3E
zU7j@XEqML>eHNb|3bC>LFlAYxE-#Me@BOdR(fv8kTeqHj^#TH{?VBXqv48_6A2!zS
zb{8HMu+S=mNjB>dt4oB`rV3t0H_cYbrt%{Yb+tI+&`Qs_IAM7i);gKC+
z(VLv1hVTP=SNqCAL($ge=0%0Vh@OxUwUH+33ppslxqbnbRjg&)T0Fnfk}2x_x<7_r
zuZ_8ZkcE}C)o~$#RCaA$6ZTuHc6Wtwu|b8Nt)a|nRPN%q0_GeS13^i*YTQHs*cwCT
z&-%UIsg}OpIM~!2^elP5sv`%5F=bT(%3ir$j%8Zezc)&YAzumZ6z?CZ+dz9qxzpq5
za6h-r!)UZ@25CX1?f(|Ht1g-AeKmB@qQ|H!T7RJ|GkBJ3@}E?&a99C6!EjYXdT#u`
zo_n1YAu(pT*eI{7PeAGKN}*l>!6~>h;~h)g}KRg$R`A6e&@V&xS2Q-z1mVX(HH%oxS9_Gf6Embo9ezHo(ipj%k
zcl^Vdb6OOI7j>ijhXn^)#5Qf7z<}3^qOG720_q>=ADMJxyZzdGLA0T)y96IV(QZ$s
z*7=^B|7lK6xykdZgnSQp;paLs3=Dg^#TO$t_dw=;^f)d=+`9C-)a8(-q#%%Bv5v
z>B48TsIG-2H|KfOED!q_@92At`~G;6a5%hEV|h^HF`7O9>m5ArSRt!^4}gy0VX{{D
zX&+ktCV%uVKU05Xg7QqIP>9&Ff!eK?Gw
zb;v*lN+yM8628VvP2Di0&1fF>i>hIT-QW9|V##6CR^xK}^GLLL!LF(JxRE!iBmUs%
zXm7Z2bfBAL?nsMA+5)fFiw|rC)C>#+!^td8Unl+7Uhuok*YxTOej~QOVNN2qtoD;h
zARt~`HBoP`h>U()rz1*tjamRdJUFZ%>Q9`w%TB#7KlIAx@n2@9h|f*Uv0b(DY@hC(
z9#;J*$_=0SejUQ>+g_5h^*>Lf@-MKKZdT=`Zy_TxoX~kgmupe+=|?{s7<|vLHLOfX
zZA~ff8J>*z{qgnMNphuTm8OtauD*iQ1lir!
z5s|MbN>`ZZf^yf^IObQrw>`zPAMDW%UGYVu;n(y{%kTM7V}%X6yKGdzm%M63>_7dJ
z>D+n%FGy%K+?&2TzyHzPxD=)7d~9FZWjxF4DK0KeXkM&*bJ*vi}&>}#
zSJqeO&g1@TU+XYm^yRXzh#M=;B7mcAEc)kBLc4aE{_HD0P}Pr{QqNh)!F|k|5N2H5mt`AYPUX>I|44qYWCgx
z9whqAC8sf6wg+cRqo)7XL}R&a&nsl6T_L%@T&6#NT48;o8_
zG&Anm8?2$PGZpnWrFt!iyNT@!Uv=j&U5GlzirGU|;nH~YU1q-l3u=KK&ELaxg_QpBND`Hi_}5^qn14)ik=0X*c}oA=HppDd=SUf$@RS<{XTP`<3W
zKGq%`^ufn!lz`zz&sRj+Q!Dj#dq(ayL+sZ=$%qFa)DpXfXO5g2
zm%VrLqx%1kPQ7FA`q!!N4r}Z=9EY%K@DtmXY*-Ko<)c;aX5R78mi8tjuz)B)FJ!f>
zUa_T%hMdmWnxuU)ul(P&Hn-^R(R{OXrZSRYZjISv$ss5DoF*+uin>x9<2mg+pL@WY
z(c@Aln$Se(4CcbB&!Gy&FS)`}pK+dEVpOvAly0vYPFJc@{Ujxwt@PYP&~kgU#S(ew
zFcRb9hRv?rl9SD{_y5X50747fbByqf$Qr{))mmW`;~*)~u%j0kF+FRB#-@qE;^@G3
zQ@Y#(EDAj}3cE&(QlM4f;cT1ZV;rfF-d7BviiBYZq-#GOz-h5Z!g2?U-q
ze_=NAvEuue&?L#rXBiohgr#aKoH;3OFU-3j3nt9=sXcIji3e
z$XMi^y#<90xH-6ABWZfVhN&=Y3V3>X-X*HXrcxnUDrXoa2f@lp`&_-RaeWpJ6~7o7R;&Ar|2;62FQ%d{%^pN%T=`pZa~GNN
zyO6az|2GUw%r4nV{~xsI?&}u{k5Pqor(@$zLR-Ru)*_9-{>*X#<(~LHY@^TBIC8V;
z@d20%jEjy_L@tPIRvU649R<`gm5f{3;#+`JA8SmBPru{i!}0rZ)ZyPMc@0j-9Qi!&
zpExSr{L&YYUD5mV3w7*CEvdTRsyO`bIyxG353aR4!%v{e1*_ToBp>cck%XYDB$w%ksUXsHI#A>XviGDXihfp65$p|=m6qm2V(IX{(&|cm@olncHVy_2
zHD4XDnPt5y)!ii&=aGoD`XXjkx26sG0cx0hvz5B*9MA?q${)Q(eVq@j&L29*Hgydn5Uj4c6xdF
z+2|g%3~`0jA6`{5gp)X{-l*|~kk75yy4wvlI$&~bp7Ic_P5O}zIo{Q%-ABL5?%+%D
z7~z<;HHWtQCGm5ADk`eB2SlR=Xv4dyxz+e)K)NO`?!Uon3q}qO6leQpbP54iDLeV3
zZWz-@%)kIo#KlNM*6RNbR%f#MFRY#{CMT(r8C4z|RC5>`R@~
zGLR4q?zl6hnDX-r;3V7=n?#6I3g<-3&Mvy!ymogf@V-r_OcR81#rX)k<8ZnpKn}=G
zHsLC(1+kpN>_^+f*T&R5uHfJy=l$(jQ3*paN(4Nnh@xH8<%Z;be>7%2*j>Nx%os3M
zwE9Hobaof0JF}vjcD8MPqIx$UyS};AK8FP3-7e#Oj^}k`z$7W*dfu2c%P_6WJ)^F3%1zuo`1YdGAes?mJ!{gaZHkayKf^!(h(!63_c@Fx(9
z`~#^JHncDK(U%M_&X&^|2Cadvpr3#W_nj(1h9ZzmkA5Gqn0BnDrSxd2lv2;?8&%bnmr)AiXd@c&irrtKXx0!3
zEXgO3>2cs;D%bQ0vg2i%wtC-2-dvHJVP>I$qV#LidI=p#zB|GckZ?h{e&|8UekyU6%;mY-f}TDwZh56SzfPL8&TLGdgWqptgChM=Ju>eN4{k(P-yJv
ze*oORD!ff@|7<+_!{i4Ywa*R*Tbw8;p#!$B#eO8&QpZp&1E#?UmPaCr&9>W}A{bQl
zZ$ca~J2_^ZYcDS=J=l-$AFFm&z@*|Ir*v>TSQ2;paz3Jt0mT{JsJ*--(B!VK`EjFx
zlpM?`=m5h9(tdf%(#ZmAoF6|L`dU_#)b5%Y&OVSVS+P8*cjx*8v~
zEmr*xXIT0-6~GZM`5Ykzv=B_LsAI{>-6K}W6g04Vvlzh_OzQyOlpX8-}=L`a&LAN6g1=Qb9slT=}e5
zY&zhhA_5x=OUh78{tm+h7PkP*RTROMXCCVEDwf>pVlE;n%fFVcP2n=0X
ztS$!6zPYywt0ABki7ac(N02@tAmqS)Yh*Z&*3vu(#p`+g%fq{jP_gD+kDicy)_cmD
zO}5OC_AU{N;w$`u5Tx#tf=>P{C6gSnn@S?4V{<&6FlhPQWI;JWf&L{f
z{H>jLb&Z+o)zjR!r3Z|3>{1kZr2G&{6ugoa;0jM(j;nO-|FEQ~zRN$Nn{F(TS)y
zP%ZAfO$on@aT(kETpS{ri?!f>6uPR0*c+2q`)SSeY&X9ra;^$Stl1)LaQaoV=4A8G
z9tUk2H}E!7H_fyJ^uoi%OSt{+mKMOZK{pU?jLu;)$ES&^Ql|VE*u6a*vfz~nfk+de
zjez?~*81UL%wyX{uFt;mst=I8V0e1tA9uERF3-+xe}k;N{n63TW|`O0g|Fr3%?bn@
zt4(ndct=`Wk!bq4Y;S^gD7QBREnO#By|YDy+Hir=%ZY^b|H
zu^Lt0+;;v;as@z{J2KNeF%+m{-WnVFe2wo5q=g2;wv{p=KyaJ;3d5%xozeN0AzP=Oem
z{T^{o$&HZc8%5cDH@-9~BwziNrF!fko!}Mm{^?2kRS;&K4c#3Qnvw9X3zX@C^7}d}
zcq098$U$B>JprA0r<3kkZ$EZdTLG&!W3HNZ1HpObm3|+-5>8vjzc#i$Qg!RfxBklz
zkk=2ENhiasoss=OJ}(Oekkz~V!uN0A)g=mH|BBy?P5s5N_}bh1Zs5?&sUYA20myWn
ztB9EXQ7;l>mFLo|^^aqb+IBEf`hEB?VY2qyk+ED4!(*e;6(^|tJs*;90&LPIYu&l9
zDduPwMIuULk-bhB7k<&7o`ikpDnF{EGOF5KGM8+d~AT&EmFTJZQoliUD=jCf$JzFK~
zgPsMiLpN!kr=HH37IY?3@Yg9M6-Ou?-!x(DO_VD{frgNw7OQq=i5hsjQ0lE-r2j~L
zzEbU->`cKspZ}izEmvKl&2ZW;0(RL`8kz+fmNr7<5scL=Aquge`khV?^9CsyIy&Ms
zsxP$x(ZB12Vyq^=4YE9l-JWO=d5VifW+{X?)r=v#G
zNN|NxkSR0aco4%pK2wtXyg;x8<{>@h1PTt0I{~qNh1Gi*B^hRvBP%2c8O&Ok6A;6r
zqaqCFy)~=dT)YRym7C;koGIy+6frR3iSonO{iCys*7cD12>0a{)`6bAL-
zd;@fwQXwAyx!%x9nKV1FEXZrr_Xj=)-o-(-K1+w(koEe@ynZ?H&pw)o`=C~~#*4B!
z$EoFO(Wp5L^RtT>)6KOR36*g|v7>Nf)qZDHS~@yKw^UlMGa}G!Vd1;ZQ7|1cRxt&S
z4|#ec>F}GB$Y^H-Y)PGE9ceI)2)&6P=>P~JXqg9`GqsMjqz_1>!b
zt*J3Lw>=m)9)qBhJwk$R$I(3!P&DSo`uc+zP>gSCR+i|3gsX&1ft9^g6VqzCuAMGC
zhf5dP$fXEs<58s8s(Q&|6c)Ct?}_s?Fc5o{nT@IJb;A6&)DQRzw{2U3eq@6T=i=&f!mlGm~1)`%fytBpo*TZ{rW(Z-L7a`GLsHL
zcE|wm8xAmFxy`+;zSI%B%=S1wa^znEczFC79Q;LRor|)2eKjQ5sF-H2
zRl+PfRfrFgh5X)S23B8FaeRgiwHnQDJKn#>HyST>B{=rqdo;X`maBQE^S_|6>I{30
zoc0@sm${;)Ct$dJ{UUOLfcyL!LfThG&SUiK+^N>_C~T7?A(8PPg`OegM{wr2Yg$=h
zyXg9>JJ$s|PMpyS>-$AT(Jw+sL$f#lTXkBLlKZ4{WG-)u6lX86UnX|f+srrBEEw31
zl+q!&$!i>8ST1>5^;>r0@#j+6zJLm+6asDfk1~zoKMe&;`*irf6xv$EpMVixv3t2L
zLOnIkFU99wzOQ1bY&Ao~T(01Q$ApY(2XnlOa%;Wo7%8WsiJBHr=5
zs&k??H(h!z`_nTgHvW>bS*>xHBFgUJ?9|Hm=sDf$fb;$i{CZ<#7V&;mydJJL>!&8O
z??iU+H>2m*zklP9$>*mb@*^UL{2vH;QJyw4upC)
zEoWNY6aoKCvBfCEg1lfWh1G>I5&}E+u(Pyj
zY)8dKw=kuF#p4@h8QDmR()LV#-)F6E_tDnx_1FQ@%^vGcI2}PGxp}T8g5&}ku3!)N
zjmjW18Qxn}OupX8j=HILs=M`jO)|wgAU*QrT&re=mvycdbVv!O6Y+X;3w1peVK3r+*SGYaJe<8aJufMauTSb4{H}WM7W@`-UL()0%(QWB)$9&Wa+O
zSWh=hbr{@Tst;^`Q8rkGXY6>V8?zCqu+r5|8oFco>R)cvtQF6){?1C?X0u99I(NC4
zS{bPaB$p-6Hyk+mp_-Rmg7cHm9
zoQ9@uOUuM`UsQ-4MT91O{9=-XDB*PpC^}#h2?$G5#iI?`fMz68nQZ
zVyZwnnX=|m2NyJdybMAeCSotG|CrlX{GWe9qaX05|PgY%0E{V|Nr^M
zvzC6grm(;gNM3g9U?*Pr?4MEVal-%C)mtq+HCHsltQjp2=wwDQ^d;uwj_JBo=1AV@
z{Fsfr64YJllbPj&;^{~Re6aIPVouoVq2=`x{3f{{FwIhg>eh;<$C{U(`R`cw_;i_K
zq61MO6@XKe(u?=R>bZP*N`!Nqs~dkA5q|c^Pes?Uldb{}gxcu;m?L+mP!ToV^~`#j
z*l!)Qy;=eQ2NiG;2kP1dsDIMNqZANc-8hQ;Fz0e%88W8l{1h$qUsxZ-yY@!_OxgRU
zA5UcM>E5!C<9^R5#}575{A*i>>z7|(AyEu6qCrH^_cXk2!QJk?kAy|2*XcMo;Pcm`
zL!H_3>ZV#V&D;zSA=JlW$0`yGMVI}iZ@Yw36s)|
zwv@bm!_PVBl&TN(OSOIej}aA>3k*BnF!-m*IvKiM2Ttp-0LxM%J`5KUn|F=~l88_*
zuT$u#$)q?Q5YLBBf}tKap;tAd!}HlwI_cr3Pp`#_+{MKfUT*2>06r_0F1Y=`^q7qC
z*^X`Bn^1%s8y|8Nzum6FWa+&{cLiaix!Z5iL8bgQ+z8%5gX}%18m|8i@6*tfO4Kq8
zu1!zq5l*Wl&;^UQgJ(DhO$T7Vh>R(h+Ja0})$zWoVKPacnx&~_z5bqc8tBvi*u~h5Y*V8MQ(fRc}
zZv_i&^iz{3S~dr-JnR)S$&jpA{RVE2+RdU>{QwiV93aH>I2yGY611gzioG8k5RG5@
zN2+c<;ew+{Jta$9Gf^AZi{StFzJYFFfHI1?m%FH@t15>FYUc=$?hlABKapL`KJ^L$+@d$P$8r{J(4Nomu_zlU_ogiIc1)d8;Ua;%*2#`q+>;q7+zy>{Is
zQklp0rrbf_<1P?C3ErAeRf;+(p?(XjHF2OAXhl>ZhxVyVU;NtG?
zu5;gz_v`BF>EBdOP1RI&pT8~zoO|}(>#V)jv)1!$T~@3Cqb+jcpOcQ(%M7mf7PXeI
zrkE%0Gyw?qp>~CF6!~_|VVI%eXPL(T{%K&$5r+P@hRD`!TidVl(>N6u9Q=>8x47Iw
zHS?17ZozSpS!#YNUt!}6)A-MFbHobAH7r%zz+6i@zAd=(byiMK@`FSA^SW36@D;&(
z&CBSdM&C5PIv^tism7*C=OF7a>($p5;7JRM`=#Y~(Chy?gcBR>9>LeVlNw3D#1z4g!@)Ouxv<44;1Wx?~eKE;Y;DJodsW~Jw>j-Y`CV!i(VNvBTy25h)Mr_IrHDb
zX2BUWU}u=<7C0sGyrg5(8<`GmYWx()?u1uE}MB64X~4}aCIHVa2fU_lC888egXxie{>jn=%p
zXjkwt7IQw%<-i>wif~BH_#XUS-$RH|^djU6WgdSgWyhIR+vRmX*Kmz^->P-cZGUMbB=a0v2Rg38&CLEsimTFnyhT@D>rXn||~F
zVWBdQ|ro-5i--F_?N{N)w63l`l}A@DtzOH#1AXLM4xb{0~-dt2oE&6M8SW!SzzT7KVPPa
z_0L7)&Qqyqazuu`DN79PCEe*FP+~8%XFX&VYPgwj{_Dl=-MBn4)?ajiqFF#klBD}z
z-PjGvgd~nw)zcb=lm|Ch^uyKrUw(0maT%X?qvzxUWUyP*U^Ml;52TL!%XhytHJ22E
zdvQXq9_puH-7srOd$UC&dJw9z9A??#kJ#J?b(w@rdUS8ll@=@<#y6t~c2Nte*0Y79
zSEP>Z340r>(kNb^H_KNc&KTb6(zyC}D#5(nJTQsqi`(D_Mop6*E22QS@l-Se=RTF(
z<=d{qAT(tj^N;5Y$QCSO^+6ja%Wn4ba{I722gvQT5@IVx?dbJrS`)V
zMB1hQRm?3kAI|stTbLX~lE5e!QftSVA@eFMIrIXBbh-ig1Uh*e5$!t{PbaL~&M=7G
zA0`u|vNp9gQ*KrG0*+W5GCpb07u4~pR8skIR!=av5%w)ma_W>2(r6)JPS$F?EhKXB
zs^4RmE{z>oG)t+996hXh8*z``nvzenf|YY3Zpd_H0;UGi
z5nv);vT_bcPtxZ&=vaFnMRC3<-8iyBjfmySoNN{m
zdTw2Y`2bCpg+IC#LQ}KsO1>^x&0U$e)@1cF)H=^0zrV`qlXEtp9kB;0Z;{!;4jFn(v#TwmMX$cI
zh~HH=TC;IRYL6eEpH-RsYD)ZRw38e^195CG5fRP
zam%4u;ItaV-4loG&LpjlC{|uS@Ux@UI(JqN%RqDgRJkI9X00lIGBjPqN=YC-4}VwL
zf617Qpbp12tDkt8lbUgG!DrN-x+0O<`Ad@R@NVcyq_(Na^$#nm*b?_SI~n}6p5=+C
zf8KUI8Rz6B!-wga@Tbkun)oznN9vLqP2JWEzy->y*V9-HD<7R(;FCH5iQEdh7*AzTCYy^aB^0
zyZf|S9K(}x{gAk|AoI6nCPuP1K`qvlO;8!>iv)c5qCQqY_
zU>O1n#AF?z#jet87fz8E!Qb>Od;Y)NiLJPh{um?)i9K0@?LYDn;7su|I8*#T;M1jP
zc4Krig2KrY%u7bggRf0d^G)ANg`wK{vd0)ZQAJg76ppbf6a=+{9wqBWym=+YJ2F~<8fkTarfd*q*jT!P(dcD=DA#!s4dFt
z?wH`XdBL+{@{7jnQQZdq&)lMEg(BwrY)aDHc1CB;T(4~;5<|j7Cd{Dk@O}R^cOd6+
z24(NU!&kUPM(YU`%5+kmGNZ2ZJxAmDqVfbGYSj1l9CXJ7Qw(3|RRIq~#d!?&=0&v5
zGUC%$HcQO9evIQ*s;NzvoEk}si-&(G)FC0fGM32f3kL|g%y_GZ>+mQqIF4*O)y>=Z
zKR@KtvOwEbFY@>Zi7jd}$E9_Ge%806bX26{>=qjSjr|;RA*BOKi3z2QPLZU2ZThR%
zd?&xXUCCs>tFCco#do{8~)-)KF
z8EqVR_E_2YI)%}T)9|1wamb~wD2*C}G)6p+8$XzBFdqFeHV=5xA`ALg$XD1XfE&no
zqTT+9DDZ;liTc+-8`TjDAaC@BMh1}*D~0phpF1nCDl$IREOFVVvkTXURO?mTF-|_!
zpk`?NoO0cb)0G7ujqeHJ>fcQo*QV+FvKoM}`L)w}%R%5szi2{zCfML)I=f^s4ilU%
zlb|NA2gEJ_V_xi<0QvR`tKn_pF=H#wW1g2TNkFP5SWbiK`>)^0+`Hz4{jPg?HQ8q)
zes^g(AEEqN)Cs30Gg*t0!ogx4v23Gs8QY(L+9^zR2vL%c@E
zsNad)R14OeVJ2U>w@ba?)4kVZIg8eaIgyyG8{E?n3a8*{yguQj
z&$M@{G~+1s7GFF|gX%WEH^@|p-rgs=4XPh_!|qg4)>WJFXQ_8njKC{>Zsh!Xi5ia1
zx^?`zw!?+umSE1CiNT#U0)`M6hP-jOyfS2IY`ShgtbX-LUtT5iw7&?$m_-yw&+$DC
zyK3vAzq&?j$lY|&>AcXY?E^e1}3-Hb|_-1C9w8#`MUN-Gfpvc=?4
zEjoce1JV*0Ny-TZxcV_Yw5f!T8h8tMFzYY)erDRah^RK=Q$^iKx-U)IoCVG=;r8^U
z+FoA4xD`BZXxcs~d`qfegu=eA>B=-x()(jEjiHPHcPkXm6zVO9j30aURKQv7dGD(8
zMdw4eEjz%%4aAhS_qc0NW?d0~>u2B$XH)%L?K(ec1Wbu1A|BEPmctEl%w)=X&NzzW
zKxO{TW5+ciO9?v6Ql@8;U=x4Np3TL^@1%x&E$RR%z09GIAV!5Nuq|PyP9I@gbMi`I
zdf8s6bkcqeZIy;}e1a*M60d)tKAQgvR2;B&QC3f3wUaZ5)NWXoa3IKJ$Z{x(t2h+q
zM#$%OTo#Xnh^c=HX29(ksHMt_&2>~BDCFnUP0pW5hG;M1FRMWgDmv$!m?;$0uxgVU
zl50phhNYQ*&$4|0Z_-D=_h$yOE%0o^oB?OJ2dmgWeYZ0|;d0kbVJCA!;Qg$;9STWl
zm2{qWI6I`nxl%7+ChBhvJEAOu#YS-%2yPkmln>tqPIQ>dRC$MKi2T`a9SLCoR!(>J
z1!yO}IJZHchummuu!{%3_m0I+HbeRdnkZCw9`6&f9y7w0jgOPTv`9+}2lE?e!=GGr
zW->u>)H%ZszALgZ$O)!g?Y=UU#_a)~B>yuk6$<-?&rsBQrwOtn8
zIJV?{t*YsLZ}^$+H28PV`yWBSOl^0fH!7|dCZd2be{@2G9IcoD0uEMhn;%Kn*6`s2
ztlNL~Fc;DCM9o&m+vCL5M>yFkHw@Zw_L0u}BVRgyJ=%`MM0qMR^43yAws(F;ubTEI
z&Z?*?UdF&l_g`qe&NK7Emigc^b5X$E20KAXZ73>I#P*`(j5oEc7*z-yJrHtCp-10_
z>JOf4lEKcbb9X_6Ckdl!}6wIfH%iBG>?904ZNa;g~!<(-(c~)`*kf&`IC>htaqLgOfFriIJ3&tYCHZG
z#q1>S*3-v7Mr!{lf0&~vnVj_1WDknOU$YFf3D^IG5`zDULxTSUCm?tD#{O|wAq4*a
z%2;sx2T!(#`?r&L6ZPNJy8q+MqWrDNafdx<#UQku(sTq6Kl3VX9d)l}PRP^!4?C!H85@Q3$L&%N_#>$;3if-M
zUv%D3$LW5%mL&qq9V+8VM~iKQ2o%+hKZz9AddJR@RgwkOSH}YzP>LPS%4>4tKEmxOVCUIqGp^`OK&3)V
z^Rn*KCA<;Zp43o4eeXyW{A#2g#yHw~$FEU9d8V$!A#?nO?fKFwIL+Pet|-ccf6*5*{@
z>OZEv?r9{j&Uno16jc#<`CWt$n!w$u;9b*Zc5`nXlNLZ8%`;RlZGKie+d(}LKG&~uj^;6^0!E1J%jnCYr=IzXt{WI|c12=RL
z(L?xQwdlq%wGNlhrqmpMiL8b@L4_Ih76XMHcwa5H546rSnL4K$JK=X6gy*mv*dOtU
z2f6j&q+FLK{`F6NS%t07r7#5UCw&Dit`H&tZ>g}^a6fDxd!cRRuhhj+bKm0)jvk5l
z*l=ksc};-S0~f!zXB?T+_5AC1ck^HUuIY{Yt0V8X12FCs6FIJ)zy7@IFqoxe{`d1m
z`2VUyoT$MMo}8RS&|c<^n)~Y)?jMcVO-9swk#4MmGITzKAzYj;?D%+I)keGy%X-vz
zvab(2HRF~DZ@=j5e&q!vGO|X+W$3SEZzHmMJwas3Qk%))^O@
z_cYe2wc&Ttd`_F1;ILDCRqxdSQ{+~5;)M$fkD`seU{X}vL>l8HioSfS#A+VqUVfMC
zX6vM~7MPE$G5Cufp}SGkSoN6|4|ZbicQ{ip&{p4sHX>*Z&5HX`QMHv4xuTHYR+&;x
zXE`WH77}w5CHwVMl^#F*9fGFI>*7
z$~Zb;L6(x@&*3-monW}`vvV~0;?7kgOOb}kF@iF5EFF}X0n?;I9bZPRBwhdFQMJ0a
z5yk{dJ-(77xZer4_;S?X`lQk)t20kM)KE`(x{9*X4qAdXK~~bul!@MR=&~DQ+g@Gb
z160Fv24XZxOn|=VvXnKBH%83;MEU}@p-#UWI%zkbKjy!?y?RRR{S@+blo_Pvm#E
zC80kU^?=x`2V#`E#;cZbSIRHZ&J;Lw=Ah*As7wiX~+pzk=KaHY(0=p=R0kMAp-8uPX#OO0caKB_n+9NY1bBMaq5+Mv>Jtw-qhDd_-PW{MJ_}ODeXcZ
z571HJ)by%tTkwt6=7mH=R%rs&bL3=~51?DZ&-0Q3B{a~|2-wYeNDMe|huFxn%QV6d
ztw6|gI)9A6@rza<;*RXLtzkS!f-!`M&ieLRi|@PMcu0WBO}5TV^yO^w?eG0Ey{~M(
z3YgQ$b)oeXO1IPZC!JD9QDdXW{3~DSvGQnyeO*w2DT7wayj8yKy5B{62X`n7lNI3E
zX_X{{A--rZdbjeI+RrcO4Sr@-N(Byo9z*F4CG0w)9+K8j@!>i5gM$-=4YW=-zjRhp
zNyFs!)Eb{D2`F6&0c$}P;r9LV7`V#4Vn9JXU$DtRAAcaa27?Mm%0x
zy5qRP^S15=qa}6)v=)LM0m`k~D!&5|^3CmrdiTVJ;RFWeMB+AUGo1?Xj<=wM%u!=j
zgGm>{MO$jm4ytI!gkdgrN6`U`N+wFib3sD8PB6o&=zdBlzq0akh&eLA!pVUmpH!^o
z|1J>+JNIhwf#naBs*4)voWZy{#r3j>XOfJf_oE3w0BzdH>BVK8Tbkc;-hP(wm2iUF
zkr!csMPaSQgUEc#fuQC)5>-nZ(~3teCEcaeL$SnboCVb-zY_ZNs+78;F>KkEY)mFB
zQT@B1J=NB?6ub)2R`i(th+C>cX(w_!)@M7b$?FYMq9_R!C}L2&2`yeLX^F@K$}wiovsB{b+nIOD@o7s3Bbih8@JEYc%5N
zxtGJr9`)ZcVu`9kjN(dkW*yCJ=#1^C0vDCjPkwfl46C698!QwVX~_S19qv6F=^#d(
zn5Olv09@s4Cgh`WJDsM?(0n)a9^Iu|NXVcqM}Mb$Hu!w}DtHrN{-(p++A@jYSz@S<
zUf;$GfF-0?&bvl7wdt*R!`f)!GTO93+FY7#N{S;b7X$CV_uQ>5tz@Jq%2dNFeb^sd
zKt}yeAn#
zrQmESjLa^X51u!GaYMRk1}cJgKb%1PWVq#hs(A(vVMM_Du8zy*g|6~-egzW|(i)cQ
z!TkYiZPH_E_O}*ncI{S&3)4PCz`wjbg)MOdB2O^Su{n>FJ(X
zd_F(Pt6AgP16d)KMjU!n2EbLanjT)7gvJfeF$xwirYnUP>VC+o(W^f)QnD*Lw54F~
z7-5uw>P(g(omeRnbO3cHPo8ymcN>f}Hlg9NT)X)G2}w$Q27i>_W@sNo+B!_@I{${~
zhVB(8k^RU*0)0d0EgO|V+1tbY-tfX2h6*4Z18S33BQQvO_k-7AJ{7eQX
z>ij;ZvOTUxQU@!<$b~E?3rMJC=XwJ=nY03bJRfIIf3zW!5+~c3h4n184T4o&Y2DQ(0gB!8Ib4su5;i#
z%`FDkc8DXuQK}tn|EM1?ZQpOMSTyvLp!?t2)iV}n(L0qDo2Bf(=)=B;_u^VbF3mm}
z6WQM&-?SeP>JF*~3ElPg<!@X2$2|Lw3Bcmn>Q+UlBl36L!Lm84$kOfW)SQ
zNg&g2eWtZM=f1P-LyHzO=C?LB#PuGo>tHp78Fz(0re_LoJ?}d?)`fC_y=+kEzc97z
zJ9E<-p@11>>B|e0~t#C^v6^;`QzZ_lt^uv}D1|HczJt2$BAxLvB~_QIE<^m25%!quaWZ*u?|@yWRHrhJ!Ocjr1O{BY8Z0
zHd!k@Yj@GUOUNAtB+a?5*V?ELN{S@;Kq-#W!CfI_T-JKNj1u#g$ZzbNJWu5vIeh=0
z#<0;;_zAmFNs2mv$9k-J-i`VpfFuVBA7+A8n4qROs-ihriaDp9W1nywz?@h~Im@xL
z&b#eBowqNbi18h55qeb-ZEGW4f
z^nASEYv=ve?x_9Cw>uo0FMP;lo%1dl)oi{daVgS?Ly5S~)o5Sr80XF4j%&^Y8Z-^c
zx)y_upBV57pxm48FYNDp|)ykAJ+2Yd
zdlcGj%aXY_Rg0w)QqiOH-8vwLMb)XrqeMByb{Lyj5NH4NDxrf%YUDrkU|TtNkvC@O
z+;}Q~X@MAO24*9(`>P)JJ0ZXaVk8u2z*x2IT~rIZq)t~hn^g&*6787WHC6n+?|g9o
zXbm&0gAp7AL=J{ZRZu(0qK&8EnDoyZT&65J=#L_~blO|ZmLoCJW?3;*6Mm?LcVn~r%djgm|bMRj*1OLkXlfVSqbvuoOJ1*
z)QD^-TJO^ou^4$HK02*;9i|GNRLB-*S1#ic#MqUC*o>)mZ3))y_JM(Su*>*QT!o4p
zYtJVm{`t|=qXflYeWH;I5lC&pCbNE}Ln!FAH@>q2wRmyDgo=I?{%8O)wb>W8Gzati
z9AVxMRhWC7En*25r*fnQ
zcJGOvCp??)C0W&jt+!|IA3STuNH4BKeYF-t4vv0*G}4|;`fp48$mi=V^uZ)$pN4-~
z5xlR-$*Q<6S+58ox=-@yZ;UIJv2|t=^CdN-=g}kj*0td;KXrZ6BxsyP#WqXz;Ux~Y
zjuxG;J7{{A8{j>+_UstIlgz+?$(%h;srT@)H%Z&FvGGv>M>%RE4spHbymvTe(?DH{
zG|c{GGmp<=hV{b>U>fpg$kCz-VudeOt;C)Xi(7%qSGC$_OQnUWP*
z$KaG~kz}})1uwV32_sib$
zDZ+U3!{4)_tb-^6M?GM7G^+0Q{vh@(caIn00j=mq#X%&hs1zO+76h*R#6e%*h3a
zH+p#Crnf9wZS;ic0q-<7W0bJQYi9igA1t=eR;nN5;0h#m=Z)!0u<32yqg#zQ!p@2sFvk;`uvJXKtq&C_Lg+otwUR=6urp3zdi#bdnH|C$sGNXQ@@
zl?;ves^*QsSeb438IlxP&asjYoHoj7O-0=mVXR{A`j38|3=A(J$@9-~dtrmg$ev_Q1K1IkS+}FF&tfB#bSCBq)pId&l7Xh7602BWtw3)`B$4S?v_h4z+sNPo;A~
z+$*vy$~^2ydKIlWyEAK>>5ScDs2~o^1gcR3G~9>zO4i$8_jU0G*IFdkH>W6ekN|b7
zdr#;Xj%IYBH_A#=5pG5LcE&!WFp&XC6*O?>D%b=IdR~v7{#xaRf&|&7*ROLZrv
zVgEdwk6YKtU_IR?f9TD{)(gAwTxufa0IxrwR7fgs~tN&LV!aQaWyRVO#hj}k?T7tzh$~he39$o>oMLb
zsjKE>ktt&XGB&N;g0hpd84m|aO{)YqdPBY!4=naJ_LkZ^{8ZFWG_M}ApKXm>EGl&)
zpgp-;tBi4WsiHSox1Cwjir}#_kO;yijrFYX>~h?764{ijNHdw%ny7r5GdW=|FWLGa
zo1_t}He_H}&RV&oqc4(Kysi>6V>a__?Dg?2I6yaCXhI4IrF6Sn
zJ7L#()GmS@Y^tRBElZrKygE!MI@p&^2bl+K*<}{ypwsAMny!`?8@iv{cZ0g#ql2n(
zfWj)py7yDZ;+|&@4x1eA`;+#j4@h9#q4U1aM1V_|5D|=+d}Jkq`vaLgSI}Rk0;Q6B
zH}Cw|KU#lBq~x;F0?8LLU(u4cHj!=-S|&sfg^pZ`^sbxUaWTg0I%HpXGWOY2s_);^
zv=thY%%acfquEMzv-Q8vym8iSExvsY_Z_vYY#ZAKjxT$cR_OP(WIH^i_H}BuISTC((XU5&Z`v+WpL}n2hTXSeV-YpLWhv)J
zd8JuDKcawPUty^W!R5CLkfW!cWEzUo$~HCVIE6jNYkRfbCb%(4>Qfq+VMJZ9A~l>w
zC&!?J(6EQZO>dtCS`RMTT3+u+EZO6E;4~TZI~E+m|l?MjoGj;&G|Ry9?IXylDJM
zA|!?Ibw0aX?rna*Sm>(pnpMTF>7Hs^zR$xf~6wB`S^#OqH>?c9OmD>;}J^SGRVIl>#pw
zV?KLcC5HELFPEl|jw9o&Wy^gqFr#?%=P5rbb1P5GFWd9@0HOb4t5V$T!|4d={yxLJ
zV()@w;r41SVTUBv)zRdg^l@bwRc*g{C=KWlRKaLK0TEL|pX}W<-2A9SL2uf5rlpjC
z51$Rcr{dFsYKG8N3?Ut(g`L*@7t0#JNB#%OT7kb9fjFDfVOI**V3khvvv`HZW-)(7
z3VYs6^ZX3DA9OafDYDyj<4xXRQzJxDAF=^99Icb`rBhd}WTzauu23uBOWO=Lut*!D
zjeu0knB6wWr!c@!)Sx^mzhaBm<6K};8y%Js^bUbL(Yrb?vUhf7{I?I2bIwmbq$Js6
z*uk&>r!=H&=3LtXnGuh8AS{k{q`+!Tq>;l5n+AxoPo6|LET
z<#YZ(k_{@L0*Nj6OHWC2T96p*xT3ulrfeV1_MhmKhX3q+{uSbC@vHfEC;LM
zTL|6Vm<(u8UQDyL8gA*V9~P43THNQ)y&9@b=F&&Kn_u!ZFRKs0yI?BNeXFN6c{hTL
zhnj$zHMc)XdzcNyGQQ;^dsw-&-lXv`5VSZwQMo+mefVfmkqhHr+)3fRkm6y4Z(g;_
zECVHa*T3QZxM80hD3y-!-N;2IxL3=l0r#Wj!v(ZrnQ2c%PYfeun~Ge+v$=$)tmsy+;_GNIL`E8&C8
z6LEM%FoA2(^8i?veJ5z>-=?(_Qv%R>&$z&?U2}f$^e3{?no+8=(`oT?rTCe12P->g
zyrN~dlZBYuK>C3xO`AMeI4e|G=0a3U7@zq#FZ?cOkj`yKyZU3*)o4q>ASDF%L_p~-
zmCmI*wLh?P5eD#3JOD(>FJL9Flq3xWs=hpdo(yVh&_OVK_XlRXu)S^U%MaIXjy1?8
zRteT({yai&$mx0&c0}9NN`4$j0&ppqz8RTt9nR}C_#|pZgtw)=Kw?QW>D@MfAD
z5C$%YY%-uGYWHXGCDs=R)@{v+xO(Ds9gsoD7k_3=6tE_&ySNG+mlf
z9MsH54|um`Hy-6$X(2?{ty8zD`bPAiIl}@1`xeH%RZu)3mMS(t6K})oYscnM1BY`b
zY%gpp+)G;ZT|JCx-X;3=w55+#C|;FtuRxn%lWiA=bX6RI6578V`ivgK+@T&pC}2Ym
zwmX8lI1g{bkJu8wN3P&lzqoG81}a@ydq`e$XYAh28C<8Jei}pWs%=ZFoL$Ea_93-R!}?0yu##;a|MLoU?p?OBV>dd-
z5(a;Edormaz62LN%
zQMa2D2=Q099Ue`S@K7U0Vz!(^+)knif>diK*eUw@@mW@MSKP_Feji`_{1syQz>=_^
zEj=_6xgL8|DV~m_QqFwiOJ>16cM>iuH*U?6UeJZp!@hB@GNXtXV67zWIRlhX7pbA99W;wOLVfCDHi(L)tQ^Ts
zRFct&Brx8LRS6e`W{IshN%Lf$xz;_qcb#(^?V#PVpSv#K`m;_{Gy{ZnmXiFV`9@&<
zU)^Wz&@54XAJp%cWcAC9W0wr0km#V*%-cg#4u2uAupIP8RS3~jG&h8Q@B=sqS@=k3CbILi*Vp0
zT{86hOXt`b-Nk>QY4EsQaye)VC(YBq5uD?RLA$
zl|<^hyHPc4_!m?c&&M0|qB{}`Zcvm3{mjBa^<0)|=|QK7jY@=_Hi}7zu!n%%v-TiB
zcpAma`obC|)39eS%W#2w}TsN}#kBf$uD{kL8O)7RY%<>oD%E
zmCg(__c<{_#bLVYp{JXx7@5Ybd%Di??VP~jgO<)nTn+bio0*qqFX^>fTt*8COg?X6
z2Ezx84Yeo&Hdk|tv;&DT$HtK{Zn53IJ--xoN`YUcbGbA
zn&E@@m?}}o2ww&Q3g3aRPo~bLwdn(9V^O}mvJ#I^vAL`5eUT89TK2pV7&FxW?P+v1
z5}v<6*w~hcj_m?YYhnmBTWSq6=rN|Ni2
zB%+rewwgB0aK!*SrPYpXK#&7)VDJf4Wpk%b!@2Nz#>4E=ky3d16@
zYBRY*VPT@ugtN-&sj|WJx$4}=7W1!B>#f9m352`3T7!>H8k>E|^zpywBj
zt~+kwI$k@kuEp!{4>x$>j9(c@{}!04lcueDPlOiO#K!YEBV4jK6@2=uNQxsK2=#j1I6p2
z+G@gNiR?A}Y!Qv8O^-@T;IW@i0XD0r0nB-n@(|Y+e@r?!o9P+g$z>&uj?fS=6s6PO
z#g56vH7nj=M6;WqgNelUCzZms+umYTbW{t%Xs9;V@BK*;hsiuxaYWG2W-!3_+aLO%
z(GYXU0XC%=*c-avm5pSkT1WJ79crBj
z3FV7$>2L+tpyY2%yl7Ii6$RI7|KJ~2P42iP`)J<}8>1F3el9wE`4zw=zc+qu7klUF
za08aItHfD+S!!T+C>JmW2yM@gNH7^M@PFCXSJ5EGDwX(weW)J-msMLm33OQr)nr$T
z2BG}Y>#CENOH!ccdNbG`Cgf4E{U^f<90sXm`>!*0BePX+Mko#WD!zL9x1z#mAWA
z!Td)n1w6{LF5M^*@TXG6hCHMZ{QztomO2FOm+jmFIvP&Qa}+004+Y|Nc}T-d>UnzY
zQNNo(1}kM8WuByn8J~0mOn|hbX6dDy_4allQB8Bqfz-&ksH6RdFzK0(PBD8f9g?2J
zV_uxzNYR}JCG}hB-osj$Ou$IPLH`ez&gMqSZX(LR5brBRBQQAUR(5^@SUIqcv0DLt
zlix5agAHSzu21cHuG=!I?zn-p$NWlm;fI%la&53Jq}tkOsH
zf;x#;#LhbW~o(E9^5DE6L2$6-~5_VNh&Mziofx;ikX684+k%z??)tkX8ve%6=P
z{-6JvBUsV$7u2vMPSP@T`!2y1
z-r(p%KD4AlMxQ6j_(DrpOozliH0IR5#@u}yBU-1SQsmhE93`wc!*#3o<@kP|X1JLJ
zhulRi`e<%?in?!@hJ&Ti02R$P1lcHI1)m%?4jVgrpU-(hls
ztv5|8$Eb^sJKOf(ifeAIAyh-8!8vI22Q~Jl05X@Jk6v=0G?_~7ir#mMspTqpzAQgxKD_lv
z2VF-m@_x}SDX?GATwvMv02kXGDgWujRzur%pkkO;GEO;S;|LM1F>>@
zg#LbeN-K7Zg=~n}#c~v&_=dXnYQ;M?u=4S<=Vd;EiWPonsmmi+lPWv4$0>E~Qy<3C
zV>nQQDY1v-W)hDBm3!&-=k`5+uCvJUMA4)|o@q30+B#`bPZ*I~PdUj*O`3tRO3!VC
znOdHCSawe8_lOV4Uxvn7z1^=qH4>fPB!`4pW6c=l^E+MjzZ45kcCf$FvrtRTgPqPGfLj2#hny;AsP%jB8D9pFvbZKB-n4V!}kiyvIy$
ztJQebTM%ON4yr0C!L%Z;ig94XuiBvvj4perSz!FrmqF5)6*;Fm2?8PEMN+4v?w37d
z{oC<~ZyOX8IAkWMH3C{D`uoSpkkWi@UpcI-tQzMR{*v+89NN`16yD`b__`q^WVN-S
z%=^9NXlrRf0yj#kc;NqSv4q)-<1o2OLNn|gZkT=ScFB@&P~Cny<%-U#%6T%kCjQ$R
zDTynMf+E9xyUQl4?EW?w#hauGu&wu6_6-R+L}rgwFb1D?fk1412RlfYAZh-$!B+?a
z2H+0nhA2^iROw$?+h|f^U#5ridO10e;1H8t-~A~E3kk)J*s`*+_Ai@41_<=IgIE8b
ztH0G(As?il*@m*b?2SPWKJ4+I%QV|pp+vcBp${83`6AQKNl?O}OZDWHVL(zzyE+Wu
zK#BI=S~z#JTjMyq+sp|~{CnOdGt56$4?E&>`_DOLR4}wskp(3sL`B@M!;Cq?9j&cZ
zzWc+C1T!7Q^8X!%!v8~6`hRZ%f`A{sqW!dSvDdoOf6q)DK+K;fMV;JUiO%fDl`UMF
zSO9$=^dFP;z6Uqv;l)&+qJ0`|?%L-mxe{h?+~Tt%gAz6Qx_9Wmx|`hJMk4X50g-J%
zWDC^v`iB`XUjL9{jELOXMgE==;Usa=;X%hYXY2TmRA7BOJL@p6Yv#32NJxcpH9}I2
zEMTPd7>i)xTH>-+g_PM2*LeA!MTm@E_}l0M#h^D~Pv7#7t*%%V5`()$J2EF$tU<88
z(LdLc(A)e(pI-oFY(jn+i$G6pje3-rl}N{b)0w|cnawJdR+rsG{)AMpMmiyae2
zC~$d$s{?<%mlGq6xbCH8vRyG1XGE2U4+Cg(iA56n}EYVrxOV}N5kjN
zd1A~gViK;#>xT}_OGMTtRMz&eKXT!4`kbD?g$(}ydGkC;;?8I8N^2AQxMkT;gO0mqz*^WuchL9Qj
z#DjMMB3(v_X5*<4!db8r~8#6v%zCNRhtf9(-u*sU=w9q79gEbDhJc=bn)
zkgx#C@V1D8y&*%c!Eh6oj)ED1+_SISw9#CSEhV6QyUlw3}D;5L_B`p{JFShC3t=W0b2AW@Vb$E=&@t%*u3vO9-oTz
z1@a?OniQD80jB1Z23^Of1=-0sj%_0$&p&B6?$~jq1QH
zW^TKWi_Kkc$)N>e6NZR%p9Fq_EyGwzdtrt$pNMy@Qi15pV~A_W&AV(D)eiDc1D7!5
z(Cfy_9PT~R@IxMHLCe&@1?|S>ft0%Ew(@fcAO6{D4cd!KNFD~pqE{goM=M5v(p?i;
zY%98T`rG=gj+H?@;o-NsN^AU=PE)G!HYFU?!CZ1pZU#MqFBZuzgjE@FOd8B*}d!xvz9lx}7jMt}%uBiFv1?I9sx
z;iXS*3#pOU(C3ms-JD*vGMN
zh&p0>U9-G)N{7+?A-3|r5;cu-9{)BfrK0?+VfKFx=j{Il&90sjQhLnSm8RvU;foIS
z&_d)Rq)$hr<+B!FK$Unv*NNv!=?xcUoa)n7+mO-b@e`9d!cCOgDAy-wzZhH!zA8+2
ztFiZW_ZMpqXCK8bo%=R8a?ejPq!Id>Kj?Tq-9pD9?N;!%V?>jSu8j)r=mFLny;F&h
zYUKUMWfJGEyyIgrmbH@*Yve=ubPC#orL^j`h3Y9PI6mW;d28FsQOGV$2Ge39&CV=a
zDA7rh4MKlh8q**`kh8W>BIPx!d3k8;>_d`~j`mNjVschyS-shkQZD1e*$;rlAnEly
zxejx0mi@n|d#kXxnl4;0K#-szxLX3j-KB90?(XjH5?mS$5Zv9}g9UdQcMa~rrQ!6K
zeE&aZo;fpfF6L(TWnb*wRkf;Wt#_^UzDp-H;LNdG+3)mE@db)oxd)^&cQ?U6rGU09
zzmqb{0eIO=x9YoLV;qI5Iqkhy9`+AV`0O0nGy%mw-`SfM>bY^-C#vj3x4Av6ZfFLK
zD+Z)s9l*Goo3mZt7@MnNG(psXn|z4#R*ji^Byn~V5ocZQwt_~)Pwb9spRU-GUwcVIqt*$aQ1HL$&bpycIjuP1)PwHfwbz>hP#<7_iby
zi-n&CcYxS(2;7(Osi<5C;7`{#hV3>?7I>Gy2&&YVGG=_3a&lZ63sH6*P^{pNOHn7x
z*+`lnraO*VQ2|04wksKL>_2*VZtCM$S2XE6Kwr36u^I8LIW**AMf++MJb7-@4l63~
zsBUG7uQb`$Wry7_1cN>U;Vb0Pg&!9ghx1?rP)heOR#&=TuPIH=AIpv99@aXJyNyg`RQEcbp*@UeQ4a
zE~@!O#b^OmGo^dQYQs6#UQE3!L>*rIG3-slbaV<~$33SSTiPHoyA^+H3Ja=Bh&+}dNa&m30nfc1mftY?VmLrrOJMA360D{+HlUi
zG)I?Csz^znm1aT#I4DEe&uFD=wdn2!Tb_f{gejDVF(7q3y6#V`Cv~^1IE~Fl@Lw6j);erep|3A2y6H@O~DegF-
z#-_(-)aXn6QY)$Z8`lTgcxq!M25HG2DoA-W7B;vTQa*uO*$68+^aBObY{*e4
zJ>PfC2=mFrZ6tIk$={cWP)Ai|l+XEg_dhq(OS=hxN*8{bL@J0>bSO8;OEu3;49}$v
z#3)`XowR2Y(mEE$bvhYHL~s<5EA8>y`|L(f{EEvU<98geYn!CE^G%!9Gx^NpbFi)a
zFbR70U7+w+;t{Thqk)9@62+~s!`haq^^lE3jwnq^!NTCKf$N7suNLU!w|)t0`r6I*
zc{g%yfH7w8weU@ex51|{T6p1l)`lWaDJud#6XE(pvDRtKGm{vRxm{G&b=eXH?OJBE
z<2^-8OkCkJm6;ku=pR|DgE;6WPe6H3
zbUS?R4fT9sRp4PghAL6ERBRH?DTyju&KLXcKtX%WPxegJ$ljrWa-gi!K~^JEtoBUR
z=mS>VS~4DuCfH-vMV$Nc%#66r(*XKi*P+vsKIH+Jcw?+DN;V&C^h3z5DiA#V}?KW!q5G4d6whBRLyx`#Y
z;EyI;Gb@+t-@`s?H|@RhEi~CIp~e#;4NlxS$He@SSh#Dw^Gw75KexlATWdK;sUX;m
zhD(IaanKpNIlAD+G&JnA2GV?dvUYIRBJ3=V+u3@ys$RZTiYAUD7I|Bq#H0Zi>SdMW
z)DX=StlB2ef7r#8D;B(gBKDAu(2r$z1deh54HB9WA1GgcR`W(+QXeVv4i;K0(q
z!**k7;@A=(V8d)+n+Llw968uELNI0MY!Ja9iF^ol7Wy=hUUx2VhdgZd4$B>)`Cn
zQ|`B65bMvbF1vIX#8hPIR2iKw^2Z5uD3`ra7Y09+bWlV#_EY|e^V@S^CTda+$vt?R
zj;ooP5+jYLeFo%2Fl^@Ssz*I(SWE>1T_YB+CUy=iKEEHd0=gLvk57{G-&v`5~nyx-JpP$52{y<@Iwd4hFmP>C*^>;Wl@>R&fL)ZE}2T2zKI`0-dFxrN`+?{OrO
zcxwM}+tXo$o)}430U6`3{rrfNrUle}YtPg8K}eb^t=(I6-q}r`l0qh5{L4n%vZ>*V
zB*JyCvF9f)lcVuITPu#u5FPd^sVEoNj4wZ-`=&tpnOxEGk9*#>_D9_tj9H;HGef|b
z3|YAxaTAq7Plrl3cq^WECJMGU1QPdwoz0~v_MA7w8`ew|v>)Fm4}_v2aq)Upcy!kF
ztIDNS)0oPMU^bHHJ2RNwm&W6L2$oHP>GeD2_3l#`dD$8Tn}x!4Z@J`|DD2JPI0b=q
z@cQ=mO*U_4ZmJ9uMIxeZ;wl=O!9*zX6W&dJ2ezosCySQPE@muQBjcEZbg1xpG-W@r
z!9(Con+Z37Um6}QHtOR`{Iqk~xpir64G&=p8zrH;DP>NtQNKhZy@vjZm)_VDq~{eM
z#j*heJG$KbUe4u+ZwRYHzvW{wTGt_B_Ydm*Qc~TzA-c5d-8_NII9kxUPE7X{t~&SY
zrp2X=1&wQZ_VPO0V@NEd6iqbF#)bhMaBPkA9-Y&6_9mX!vLrXL5kZ|6y;3!|a3zq^
zvX-?(WP6T?&ws}WX-98i6ma-Q5P*M&L&a_!9XW7vBVpcltbzD3YAwPfdN!W?V>#c!>XL
z1a5o>b05!1uYHC(6dBc&fTh4u->!mki~=A5bEllA-Bk55kpsjvc@WB=f|osUxI8U3J`
zY6N8C+#-%B*>i&RPVwHoQqLqE67f-{A@OV`Vs|bzUz#JtW`}Hso4gI~Bmc+(Tc5lE
zAFYotg4(sE%3VEi>SA!tD<9*q>JGe=Or5<*K}H{{`f*oim^z6Mmticdh51>gGEj|7
z!V?IEqSl_`pNf@(e!hUJb*D-cz$8
zcK#9s)W#|J*DRe20-=CLE|Pc3A^?jm2zr_qu^ouY^+_&F)`Y20qU&KnAmDy1#4ezK
zYWKwViW{qJy*n>kxG=O=;KCH5%`Qlp&|3mP2|FFrpCxoFx6UPLetcxfUPp{br3@U$
zXkP##+jgB4-d)3GkZXT2j^C0&`HcgFVt3OPeQB>846HB!#0MO2V1SI=!)Ob#PQ;JR#()T(akHSfve0fL%H;7LHO+HE?!6{!wcsln+NYW`-r_%Q;D
zjqyT5?~s1Z^t2MEda1d`Ag#a=<=1y(*UNs4quKQ>tzCiB$c%p4a=mA+-xD)0S-PoUQ7)2qd*@La5KAR~Gb#_j10Ijjk
z@lIb(c0?p&mwRi&xf*&Saq?5$7Cr`Ok_C=@m*6DpL8S(@pt%S|NkTSG)ix5TfESAc
zd}0~1hte8S9ztp%C=F~Z7E16<-yfGDl2>LsYZ6tV-Sqf-e<%=;lbF`gzRvEOoq1va
zSYWoaXm(kDM6cRdn!N!(4ZVBk@Z{b=BOq+@TY?o~F5yXWW{G02QTplYZNkfEHfrp`qbB#UEGjNiGFG#o*eaOV76PT2R*F!w^?q^
zaC4C>%Y*VOodA6W4Z^)DFtb&pTd!SxI9YuywX=eoPkXt5{AlCCzKFhlFdndz0sHxo
z*`3sKL@7R=4saV#uH)Pfg{y5H@BPrMK%5zKHo+lEwNK9CNcr2XlS!fK_j|84o;c^F
zW(7wU5xU!ixJJq6GmCwDd@Y(VQ`HrG={P>H(tQ|ZPI-q}4gjTp8drZz%MXxNCLeB`
zzXZ1@Oi?LKJHC}zCo%op@=~#%5w1`>Y1k#d%;RWWjJJ`N78SKj*>JnaB!NTMB<5i_
z<%ZF|!s8g$9|>Rlk*vN+^JK7vi$=z$lnBN0b%e%vp{ByBHuuY0+i7UNPirtjeGOBB
zJ`$zx%a12w5wK`a?YMmH%8UI{3GxE_Ga99haa`~}oyDLzd%v!QqBfun2d^a))3TVq
zRuF?%$1~obI6VfwzU=Bx2R-ZV1E#Em1C}DkUA=S&iDLBk3U+3^IyCMrrDR%C;fhBM6Uk)w%Nt|Hw`5Avj?@LG|2K}a)ahKUMUrBI+*C>*e#xo
zEz8Fb>C=9R)9=@KSuo-@H<0t^VnRTMQ#4o}U3T|3yy-y$c8LT$j$gb8x54ZdM@^)>
ze3EaMkAWR4-v-m4YCHPF%fHdxUyiBxsLTYTmJKAK5MF&*g#?z(*|)(!eir!S*VgA>sOsHIAdRoRB>b^a
z+zSSeKUYW-X!`;M3kRF>esy>0b{$)CTVCdfX#0gwBiT#nJo=Q(`dQeQ_JMR6bMkAa
zY)ejFWaSNye)7h%*|s7`79t;Pp=FDR9NMXKx6eg^)3bZVKHZU0nA5klFDur(0wjm^
zU<<7_tBDDOWGR#3H
zwZi2J;WlQkfxOe!0MqkYziFHsiCFbbf>K2dYyz^-nK}2g{^Yry6SXgm>nB
zMz%_9JOy*~@(aeUKEVq>I4SM$0yuaB_0tPmyWi0rP&RTk0O>aPTiaEIrSRhq{doR
zwDhCtSdhMr7(N=FSh#o-G4KP7?CM0*Hk{r
zHzD-CXY?8la}+fiUQ-~Iz^n0x^-iQpl0ONG1W>`4LnzfRqb>H>j7u;*<0@Qn@7WB;
z+v5+p3T5b+-@jH%OOCmSQcj5X*UHvt@=Uhh`r?I*pp~ENzZROxoq?%nVr_Y1*xj7qZ{0`|{dFqW17>M%$O@VLL?;;XSEi60vn4b+bq$OBR
zv+i!j+O|mRwj5E9KQ;su!V!VvuFvYMAJB8>HrIO#^}xK}4icSK(=j8s<8u)dXh0Tc
z1M^2_KUgDgJ)HpsPIm>O6xuY!aryHm1MRA^i=g>KS6_f6@aOKOqPfisL9z9vmF-H&
zBP46imPKjWy;RNeijlH>97g2MLnr{ayVRY`-|lmBVZ+`k|A5U3tGuThdlVQuf$tC_
z@zfdBYu8^osqYRZOm{>7na3L6bq=U9u5^9a_XT)mbRU~GR%P#t2a{58y?^EAcQ_Fp
zydX8=8$(q98q_zg1mcke?8|oJmBsC(E?FX|P+wYR66c|i-lJ%^izt6m|p2PreOI^|$a%UU>ft`NodgtLuJ
zZ@N@G{BRpxgf#C8^9pU+gq~jxF>PI0O_MWMUdyo>Nzi~vsW2VN-m^#S6dSi+N5U7~
zvmY&f%`I;3x)LAzOR&6=pO6%njsx7XH{}i62)TK@?__3whp5TEZu7g!H)}mMuG2~B
zYkPhX9$YyJFt7KbBehf^qpZ70EkXT8DnqX2M)BK0b^oVYo)p{8SAQTRosUE5+P&oP
z_(rP?HFmW1RE>4{NN&@|wX<>ZuqJo=Kzw2Hr3W1WIN`v{f&OUd$a5Ln7Cp2fSuz>^drX)CQginPhy7Z
z-XUCW)`6&GR#?Q`N`V-<*p4!76I~N>1N4@T_c=>ntI(kle
zusDUDdL)bSJ&wPd+6+z9K#%HR7QVo$irYbRW_e$vxYhrY#>R>{rtH5nIZ7TVT6293
zQaU-d&TeZ$@lQ>N!Ma2jc*oW8;LZlIw1&+$&&3tk)i)iC)u5e+3^tu4%w5;+!$YtJ
z7W&5#e$NI>_{WiswZ#Cye$m$Bug~h;hnBt{s8*loJ-I00P>V6LkGzi|QDB+{WcoZg
zSib|p6D>z;_cI4UAdN+D103R=4YSB?DO8~xzIj5YbNU@{>A1)9LdSn4{eLo
zHPT#r@dojKtybR7>kcKci}hqNUEjdQ0dN*Lj)cVHqhcuwIe1#-(}mLU@-
zwuKDgm$R;3BjWp?TR0zLsgdl&L|0WltKhr1vE{Ly`?4kacnvsLwOl=*hrRb+7Km5|
zAS>gn2ORubQuxfybWpD7b3gz^NM&C11P&ahR_HLmGezCr`qzsTnZ)eXfNk)rOOonCNaV{v!LRTad~It5Clib`
zIe%1r;QVJte=j-~VQ7$HZFuh2psqe|mbZ>h>ck>Zi!5s_H|k56IG
z0?rl4>Qthnv}}@ZOIFI#FJm7ACM{BgWBlpUVxo6%6oyST@XIkE;425ki_-j8@pSjz
z1(-xc%qBct{fSRRZt&8}vMI#Gd?jF^&&LNYu<0MlXCg9Py?m^-Sc)KM%)-*nrDr=4
z>T?m}&PNo^ThSu5kg$4X#YMZm>QQ_NI*eQ;!D3b5LTP|scU<@6%nJnFti9(?2(UYT(fJ8!OdcwemK8#
zX(IQ3g}cH#o5ZEeL>RKXb0&ycAsXnEtZlCCPIHI@8`2Ys4YHCpq*{GPaE7M!3-D)
zls7!&Am{KU#;yR932a;=FI&Z;v@>8)35Z_yG{>i92U?W>$6Rq$`S
z?l9|;{V|d&yg#&Wcwy#(G>7z@sE#7Ih+d1G7WC9Mlcvp-Tt9
zMs-{7QgJlNShB!5X!{SGlXCmhJM`q*9
zha0$moPJ*2$fT2q%EmvBjYGL^=vr?&xB16QCV>#Fa)1KQP5O8AcTo;%xNAglK89Yu
zwB~%+kzI1n_6~h(J^efl8e_h>_A4=Qx?T)}4_M=X$7I1vWJzuLo;Bax(WuM^r;UgH
zF|^~{itANe5>oZyX{7|8L1W9r77%YYXu~hQSlQuk`F)R@`1RV9uI0Cc5x{O4Zn}CFqH>Dh|w3?{U>^
z4h1SdiBi<}wli>IxvhI2<@Rl{1L6ygeyQoW4PHs6)QP^$2%0<`Yt?(3kwn#;Kgi}o
zhAzK?liK!1XzP-z<;LBDW`imwiNqIYwCfh$p`j`UCGvx9=q|Uq-s~kGW0OI*snS{PIRNn2b
z*?62FW=@>eXK*Iq;bmnLL>8jMOMJKvSv3Ht!(Bdb<9UExzd&DJO2sly&LoXUjtT!bE_C5YM#afVsu?IeKlC@ds+-&e`Ql=Y1-#;z8}
zwFY;dEa&ME*qjY=O>mp{2SG?siM*&$&?_0%fz(&_f*LZ|CQjUPK9AesH;r>>dMN|%
zjJ5fN$-jxObvB9LF;W#Zt@o2R1jI8ny8WDW!XI=Opo$sZrOC0!)h*7|G$HX78)=fh
zW`_wV?41vSunHd-)ra2Y=MLF26xCLuz6hNKytFu?jzxdNru<6|mpN3F=%WAe2){9}B-Aun1!`345EfVmBC3wN6$HfoI2nh(wOW
z`_#QTHmwNPZSlCjI4g7_diZNfPs@4bDh0zluc3+?4o2>ZKIN?$^&B^L1hkr1>mm`y
zAoS!bZ(mA#QbcjUGejdmoV<9h*;NK;OMMTIL^JGtVm9Xf)|G
zM}}8EG%S#;u%|g>_k`&GKoR-Uy270AXAltT2kWheE@HjIioMx)c0*3V@u9fa_daXJ
z62}=vVwBm0OU-!~omqa?<|JL@Qv!wa%R(H>juNwrOU>=x78sXS+SI;gr>1?<-+7VV
zi1<*m8o0&)eHS5oR4qf-}%bI@A5fke2MqtWOBhnI#wtH^Q)55y5)eh
zEao)3bRtUHAT=IO-}ISF%%Z?boZpG{x#&zS%Pex~B!M4f$l2?>>NmCdh2*5?cbZ{%
z%>8pl0XcxNgh11r87qt{iH*ujKkEYj^rvYicT|RFy@Ke)Uk)_*ey{J`l{IcWaM>@W!Y%r#lgl!v4^GlQuMTX!+p*~>;=GY^uE6OJ
z&d(X&Y>LBySc52ADU6)_&@bt+&J*nemZqFG3$&sM?Xb3Qz82L0?urVX!>qN0P!nmS
z$NR_zhv?cyY_X^LcB1ALI-^_y{n{n3E`(t${6!*|C49@4(lwrkP7P^rlG;0tBY9wM
z?|%AOXKt=mh|MBXXRfB0JOm>Zt9j6pVz+|xJ?!9Q)iV)z+PA))LDlq;&?*ocaeDmb
zGLUW3XYifjtv{D)z#5S`s!riU`hyvgUkWxowuYv@z;A?*S|@$smX=%pQm4s0=~UHy
zyuh$9R%7(j>K@v{8!iicjY=0b@XhQW)UF_QQ4-JAQjsuXd~{NH5)a$;n?S;MMdYBf
zSI!(frH%gyC#_UEJ_OhB5N%&^B&w0&IETaQA=DJ+kQ1tsnl(I~~^i&5Q3Y!dx?mVj&l)9JS~T;?N=sjU$RRZ-BlHJf6^^5mf4q6c-tB}6Ogf6Agr*hA%^`46mW
z{6qByHbm*RR=+?grhwFc))fU!Z-SDyPycOHdK&VgF;hG7v~=r?@LHJN7wlj(!kG~h
zNz~sGz@_G;=JO(PL&GRkfBK3e-TOT{v&Zt<{}lkLg7%gGBn|rw8Vq9k2_E{bY$)Wy
zr4$HVLq}sG^LQNb$L+&(PwC^&CdzReou>z);E1Gr8DG~uPwSkNbGzu~Gx!UDD*9v{
zw>`c3zHNc^DBq?KrSxZG_2h3QVbA1;gwuTjIpA3qJhS6K#T838q~(r|uH^n}y%k*%
zwMt0&V_G<+YbYy+F79$Ca;}aK<5&@PU(tA&z5y2^+K(+S038ECE^`O)=z6j>N+u1qu!vKEsYxZT
z@F;HZw3?Ck>&Ykgt-QwAoR@1H1xx7utW4d3_@LF>P$Ns^&t~*pB(if?UoXp@y^o{FxwWlPO;nrk-q+MU{2=PCd
zYSALG`41u%*L+BvTU|&MPMW)BgypR(y;LfjpHlw$RA1Bd#7TQeRmtYygHU<{m9f;F
z;Cp6zw)Y!~G*i>YJ{QmCC68i
z@}%=xBcA)o{N`ZaZttx{9LDk!-jV*xC;p)g*B6*dO3akXMV;coe$dvvsT_|$iS34?M^g)$0E}2h^52(3O%%h
zxfme1a32Yu9(~oV*Go|YNgQpk77HO~=tlD&sm8t8
z=N7B%Yq&5_+&9T2fCxL`e3v3}pp@0+*}ja-KacFTxh>oXT$>LH?0^wbTd|7GZ?oW>
zHmerbJruxAgHto0f83Te8Eb{3o?*lS4*xp$UbB5l9QvhWvZuG}R<$?_>=PkBwLFw1
zRdF>!!la@9R;(YX`Q=>5T!I4e&R$UZR=R`@#((_W+=8L!a_u;tdmmpW_B=D){E(v+
zT26k2%2fKU;|a5fz6~Zq`MY50bazhm0FJglMUy?Ay|Hg*;hgoIy%!=Ru#70M9L-fI
z>62~`1Nfk#6t*+37gT4+@2c+*P{*g>;2{DEp;e0*o?A=sC4OY^QrVJy!zcWz+t~YR
z5pRD&w3m!=68r5eAViu&g=4!dSQ0(TZOP_dUh}kuUU5LFpolYp(sZt1P$8R*V68K!
z`W+gkw{WLx8>(y*s9J`XViAqAxWAof6cObET102D(pwj*HoE&I&<7v>PjIx`6k-SJ
zmPbEHkjfo~*q;3)=^vF%uek3PW^O&b^yHt&=xMGU^zhE4BYyroaHz$%&K#x-ECbm9
z$s3P{6IAcTtuXf_LLw%eb!vo7F0L!>iHik!6>A
zpRrdjul*!rC1NuN_)9@XrY47NlBwf>tTKlcwSkq%tFnXlj*Ly54c=>Rki_0qJe~QO
z^vk;&x)0_RJr$;Rn>+rmZ*))BU;8sG$lIuS-rkvf+UA7^uCiqPK^L>qX7MI!_jWRA
z!Y95wFaDv_vta&@Qe*aJoYvpQli%;m{*^ZzqWZ7ig8Y949J2o_;2`i{0f+xL^z*+0
z4*x%R;jb)AL_|b!2gwq9t$#aAaP&f*Csu*AXM5i0#KaNb@)GXX>H$wjT7P9#L=#vy
zkx@{3JJ6mHqEXoI_i6n_m6g}j|I_x9mH}n1;qmsD6L*unCzAfj26(P1@}Hg;2n224
zy|iSBIf);ATW<4LMF?H?_G^89hb*6S?Y}K6_%mPYi;Lk=!-$}ug!0QEMKu3B3^sO9
z=7I*K5MpLb=}$#+>9DOj0Y3wGb{2U4u45V@)V}VWKyYgPUeBRR