macroshock 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Patricio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: macroshock
3
+ Version: 0.1.0
4
+ Author-email: Patricio Arias <patoariaslopez2@gmail.com>
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 Patricio
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Requires-Python: >=3.10
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: numpy>=1.24.0
31
+ Requires-Dist: pandas>=2.0.0
32
+ Requires-Dist: scipy>=1.10.0
33
+ Requires-Dist: matplotlib>=3.7.0
34
+ Dynamic: license-file
35
+
36
+ # Macro-Shock
37
+ Macro Shock is a library designed to solve Structural Vector Auto Regressive (SVAR) models.
@@ -0,0 +1,2 @@
1
+ # Macro-Shock
2
+ Macro Shock is a library designed to solve Structural Vector Auto Regressive (SVAR) models.
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "macroshock"
7
+ version = "0.1.0"
8
+ requires-python = ">=3.10"
9
+ dependencies = [
10
+ "numpy>=1.24.0",
11
+ "pandas>=2.0.0",
12
+ "scipy>=1.10.0",
13
+ "matplotlib>=3.7.0",
14
+ ]
15
+ authors = [
16
+ {name = "Patricio Arias", email = "patoariaslopez2@gmail.com"},
17
+ ]
18
+ readme = "README.md"
19
+ license = { file = "LICENSE"}
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ from .macroshock import SVAR
@@ -0,0 +1,259 @@
1
+ # bootstrap.py
2
+ from __future__ import annotations
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ from scipy.linalg import solve_triangular
7
+ from .var import estimate_var
8
+ from .fc import compute_forecast
9
+
10
+
11
+ def bootstrap_svar(model, typ: str, shock: int = 1):
12
+ """
13
+ Replica la lógica original de SVAR.Bootstrap, pero fuera de la clase.
14
+
15
+ Parameters
16
+ ----------
17
+ model : objeto tipo SVAR
18
+ Debe tener los mismos atributos que usaba el método Bootstrap original.
19
+ typ : {'IR', 'FC'}
20
+ Tipo de bootstrap: IRFs o Forecasts.
21
+ shock : int
22
+ Índice (1-based) del shock para IRFs.
23
+
24
+ Returns
25
+ -------
26
+ draws : dict[str, np.ndarray]
27
+ Diccionario {variable: draws} con shape:
28
+ - IR: (reps, steps)
29
+ - FC: (reps, past + horizon)
30
+ """
31
+ # BEGIN BOOTSTRAP
32
+ b = 0
33
+
34
+ if typ == "IR":
35
+ draws = {var: np.zeros((model.reps, model.steps)) for var in model.variables}
36
+ elif typ == "FC":
37
+ draws = {
38
+ var: np.zeros((model.reps, model.past + model.horizon))
39
+ for var in model.variables
40
+ }
41
+ else:
42
+ raise ValueError("typ must be 'IR' or 'FC'.")
43
+
44
+ while True:
45
+ # progreso
46
+ if b in list(
47
+ range(int(model.reps / 10) - 1, model.reps, int(model.reps / 10))
48
+ ):
49
+ print(str(b + 1) + "/" + str(model.reps))
50
+
51
+ # ---------- tipo de remuestreo ----------
52
+ if model.resampling == 1: # normal
53
+ resampled_resid = model.resid.copy()
54
+ for i in range(model.n_obs):
55
+ rnd = np.random.randint(0, model.n_obs)
56
+ resampled_resid[i] = model.resid[rnd]
57
+ elif model.resampling == 2: # wild
58
+ rsigns = np.random.choice([-1, 1], size=model.resid.shape[0])
59
+ # Nota: igual que el código original, esto modifica model.resid en sitio
60
+ resampled_resid = model.resid
61
+ for i in range(model.resid.shape[0]):
62
+ resampled_resid[i, :] = model.resid[i, :] * rsigns[i]
63
+ else:
64
+ raise ValueError("resampling must be 1 (normal) or 2 (wild).")
65
+
66
+ # ---------- condición inicial para recursión hacia adelante ----------
67
+ Y_sim = model.Y.copy()
68
+ Y_0 = np.zeros((model.n_vars * model.lags,))
69
+
70
+ for i in range(model.lags):
71
+ Y_0[i * model.n_vars : (i + 1) * model.n_vars] = model.data[
72
+ model.lags - i - 1, :
73
+ ]
74
+
75
+ # ---------- generar datos simulados ----------
76
+ for i in range(model.n_obs):
77
+ u = np.zeros((model.n_vars * model.lags,))
78
+ u[0 : model.n_vars] = resampled_resid[i]
79
+
80
+ if model.const == 0:
81
+ FY_0 = model.F @ Y_0
82
+ Y_0 = FY_0 + u
83
+ elif model.const == 1:
84
+ beta_const = np.zeros((model.n_vars * model.lags,))
85
+ beta_const[0 : model.n_vars] = model.beta[:, 0]
86
+ FY_0 = model.F @ Y_0
87
+ Y_0 = beta_const + FY_0 + u
88
+ elif model.const == 2:
89
+ beta_const = np.zeros((model.n_vars * model.lags,))
90
+ beta_const[0 : model.n_vars] = model.beta[:, 0]
91
+ beta_trend = np.zeros((model.n_vars * model.lags,))
92
+ beta_trend[0 : model.n_vars] = model.beta[:, 1]
93
+ FY_0 = model.F @ Y_0
94
+ Y_0 = beta_const + ((i + model.lags) * beta_trend) + FY_0 + u
95
+ elif model.const == 3:
96
+ beta_const = np.zeros((model.n_vars * model.lags,))
97
+ beta_const[0 : model.n_vars] = model.beta[:, 0]
98
+ beta_trend = np.zeros((model.n_vars * model.lags,))
99
+ beta_trend[0 : model.n_vars] = model.beta[:, 1]
100
+ beta_sqrd = np.zeros((model.n_vars * model.lags,))
101
+ beta_sqrd[0 : model.n_vars] = model.beta[:, 2]
102
+ FY_0 = model.F @ Y_0
103
+ Y_0 = (
104
+ beta_const
105
+ + (i * beta_trend)
106
+ + ((i**2) * beta_sqrd)
107
+ + FY_0
108
+ + u
109
+ )
110
+
111
+ if model.DUM is not None:
112
+ Y_0[0 : model.n_vars] = Y_0[0 : model.n_vars] + model.beta[
113
+ :,
114
+ model.const
115
+ + (model.n_vars * model.lags)
116
+ + model.n_ex : model.const
117
+ + (model.n_vars * model.lags)
118
+ + model.n_ex
119
+ + model.n_dum,
120
+ ] @ model.D[i, :]
121
+
122
+ Y_sim[i] = Y_0[0 : model.n_vars]
123
+
124
+ Y_sim = np.vstack([model.data[0 : model.lags, :], Y_sim])
125
+
126
+ # ---------- remuestreo del instrumento (método IV) ----------
127
+ if model.method == "IV":
128
+ iv = model.z
129
+ if model.resampling == 2 and model.iv is not None:
130
+ for i in range(model.n_iv):
131
+ for j in range(len(iv[i])):
132
+ iv[i][j] = iv[i][j] * rsigns[-len(iv[i]) :][j]
133
+
134
+ # ---------- construir Ysim y Xsim ----------
135
+ Ysim = Y_sim[model.lags :]
136
+ Xsim_list = []
137
+
138
+ for i in range(model.lags):
139
+ Xsim_list.append(
140
+ Y_sim[(model.lags - i - 1) : model.n_obs + model.lags - i - 1]
141
+ )
142
+ Xsim = np.hstack(Xsim_list)
143
+
144
+ if model.const == 1:
145
+ Xsim = np.hstack([np.ones((model.n_obs, 1)), Xsim])
146
+ elif model.const == 2:
147
+ trend = np.arange(1, model.n_obs + 1).reshape(-1, 1)
148
+ Xsim = np.hstack([np.ones((model.n_obs - model.lags, 1)), trend, Xsim])
149
+ elif model.const == 3:
150
+ trend = np.arange(1, model.n_obs + 1).reshape(-1, 1)
151
+ Xsim = np.hstack(
152
+ [np.ones((model.n_obs, 1)), trend, trend**2, Xsim]
153
+ )
154
+
155
+ if model.EXOGdata is not None:
156
+ if len(model.EXOG) == model.n_obs:
157
+ Xsim_exog = model.EXOGdata
158
+ else:
159
+ Xsim_exog = model.EXOGdata[-model.n_obs :]
160
+ if len(Xsim_exog) != model.n_obs:
161
+ print("Exogenous variables in different domain")
162
+ Xsim = np.hstack([Xsim, Xsim_exog])
163
+
164
+ if model.DUM is not None:
165
+ Xsim = np.hstack([Xsim, model.D[: model.n_obs, :]])
166
+
167
+ # ---------- asegurar shapes ----------
168
+ if isinstance(Ysim, pd.Series):
169
+ Ysim = Ysim.values.reshape(-1, 1)
170
+ if isinstance(Xsim, pd.Series):
171
+ Xsim = Xsim.values.reshape(-1, 1)
172
+
173
+ Ysim = Ysim.reshape(-1, 1) if Ysim.ndim == 1 else Ysim
174
+ Xsim = Xsim.reshape(-1, 1) if Xsim.ndim == 1 else Xsim
175
+
176
+ Xsim = np.array(Xsim, dtype=float)
177
+ Ysim = np.array(Ysim, dtype=float)
178
+
179
+ # ---------- estimar VAR simulado ----------
180
+ simres=estimate_var(Ysim, Xsim, model.R)
181
+
182
+ XXsim = Xsim.T @ Xsim
183
+ XXsim_inv = np.linalg.inv(XXsim)
184
+ betasim = simres.beta
185
+ sigma_usim = simres.sigma_u
186
+ residsim = simres.resid
187
+
188
+ beta_stdsim = np.zeros((model.n_vars, model.n_cols))
189
+ for i in range(model.n_vars):
190
+ beta_stdsim[i, :] = np.sqrt(
191
+ np.diagonal(sigma_usim[i, i] * XXsim_inv)
192
+ )
193
+
194
+ # ---------- construir Fsim ----------
195
+ topsim = betasim[:, -(model.n_vars * model.lags) :]
196
+ bottomsim = np.zeros(
197
+ ((model.lags - 1) * model.n_vars, model.lags * model.n_vars)
198
+ )
199
+ for i in range((model.lags - 1) * model.n_vars):
200
+ bottomsim[i, i] = 1
201
+ Fsim = np.vstack([topsim, bottomsim])
202
+
203
+ # ---------- identificar Bsim ----------
204
+ if model.method == "short":
205
+ Bsim = np.linalg.cholesky(sigma_usim)
206
+
207
+ elif model.method == "long":
208
+ M1 = np.eye(model.n_vars)
209
+ for k in range(model.lags):
210
+ M1 = M1 - Fsim[
211
+ 0 : model.n_vars, k * model.n_vars : (k + 1) * model.n_vars
212
+ ]
213
+ M1 = np.linalg.inv(M1)
214
+ D = np.linalg.cholesky(M1 @ sigma_usim @ M1.T)
215
+ Bsim = np.linalg.solve(M1, D)
216
+
217
+ elif model.method == "IV":
218
+ usim = residsim
219
+ for j in range(model.n_iv):
220
+ XX_invfssim = (np.var(iv[j])) ** (-1)
221
+ betafssim = XX_invfssim * (iv[j].T @ usim[-len(iv[j]) :, j])
222
+ ujhatsim = iv[j] * betafssim
223
+ Bsim = np.zeros((model.n_vars, model.n_vars))
224
+ Bsim[j, j] = 1
225
+ for i in range(model.n_vars):
226
+ XX_invsssim = (np.var(ujhatsim)) ** (-1)
227
+ if i != j:
228
+ betasssim = XX_invsssim * (
229
+ ujhatsim.T.T @ usim[-len(iv[j]) :, i]
230
+ )
231
+ Bsim[i, j] = betasssim
232
+ Csim = np.linalg.cholesky(sigma_usim)
233
+ q = solve_triangular(Csim, Bsim[:, j], lower=True)
234
+ vsim = np.linalg.norm(q)
235
+ # vsim = np.linalg.norm(Csim[:,j])
236
+ Bsim[:, j] = Bsim[:, j] / vsim
237
+
238
+ # ---------- almacenar draws ----------
239
+ if typ == "IR":
240
+ ir_b = model.IR(Bsim, Fsim, shock)
241
+
242
+ if np.any(Bsim != 0):
243
+ if model.method in ("short", "long", "IV"):
244
+ for k in range(model.n_vars):
245
+ draws[model.variables[k]][b, :] = ir_b[:, k]
246
+ b += 1
247
+
248
+ if typ == "FC":
249
+ fc_b = compute_forecast(model, Fsim)
250
+ if np.any(Bsim != 0):
251
+ if model.method in ("short", "long", "IV"):
252
+ for k in range(model.n_vars):
253
+ draws[model.variables[k]][b, :] = fc_b[:, k]
254
+ b += 1
255
+
256
+ if b >= model.reps:
257
+ break
258
+
259
+ return draws
@@ -0,0 +1,111 @@
1
+ #data.py
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+ from dataclasses import dataclass
6
+
7
+ @dataclass
8
+ class YXR:
9
+ Y: np.ndarray
10
+ X: np.ndarray
11
+ R: np.ndarray
12
+
13
+ def YX(model) -> YXR:
14
+
15
+ model.n_obs, _ = model.data.shape
16
+
17
+ Y = model.data[model.lags:,:]
18
+ X = []
19
+
20
+ for i in range(model.lags):
21
+ X.append(model.data[(model.lags-i-1) : model.n_obs - model.lags + (model.lags-i-1),:])
22
+ X = np.hstack(X)
23
+
24
+ if model.const == 1:
25
+ X = np.hstack([np.ones((model.n_obs - model.lags, 1)), X])
26
+ elif model.const == 2:
27
+ trend = np.arange(1, model.n_obs - model.lags + 1).reshape(-1, 1)
28
+ X = np.hstack([np.ones((model.n_obs - model.lags, 1)), trend, X])
29
+ elif model.const == 3:
30
+ trend = np.arange(1, model.n_obs - model.lags + 1).reshape(-1, 1)
31
+ X = np.hstack([np.ones((model.n_obs - model.lags, 1)), trend, trend**2, X])
32
+
33
+ model.n_obs, _ = X.shape
34
+
35
+ if model.EXOGdata is not None:
36
+ if model.n_obsex==model.n_obs:
37
+ X_exog=model.EXOGdata
38
+ else:
39
+ X_exog=model.EXOGdata[-model.n_obs:]
40
+ if len(X_exog)!=model.n_obs:
41
+ print('Exogenous variables in different domain')
42
+ X=np.hstack([X,X_exog])
43
+
44
+ if model.DUM is not None:
45
+ if model.DUM=='Q':
46
+ model.dummies=['Q'+f"{i+1}" for i in range(4)]
47
+ model.n_dum=3
48
+ model.D=np.zeros((model.n_obs+model.horizon,4))
49
+ for j in range(model.n_obs+model.horizon):
50
+ for x in range(4):
51
+ if j in range(x,model.n_obs+model.horizon,4):
52
+ model.D[j,x]=1
53
+ if model.DUM=='M':
54
+ model.dummies=['M'+f"{i+1}" for i in range(12)]
55
+ model.n_dum=11
56
+ model.D=np.zeros((model.n_obs+model.horizon,12))
57
+ for j in range(model.n_obs+model.horizon):
58
+ for x in range(12):
59
+ if j in range(x,model.n_obs+model.horizon,12):
60
+ model.D[j,x]=1
61
+ model.D=model.D[:,:model.n_dum]
62
+ model.Dhorizon=model.D[model.n_obs:model.n_obs+model.horizon,:]
63
+ X=np.hstack([X,model.D[0:model.n_obs,:]])
64
+ else:
65
+ model.n_dum=0
66
+
67
+ model.Y=Y
68
+ model.X=X
69
+
70
+ if isinstance(model.Y, pd.Series):
71
+ model.Y=model.Y.values.reshape(-1, 1)
72
+ if isinstance(model.X, pd.Series):
73
+ model.X=model.X.values.reshape(-1, 1)
74
+
75
+ model.Y = model.Y.reshape(-1, 1) if model.Y.ndim == 1 else model.Y
76
+ model.X = model.X.reshape(-1, 1) if model.X.ndim == 1 else model.X
77
+
78
+ _ , model.n_vars=model.Y.shape
79
+ model.n_obs, model.n_cols= model.X.shape
80
+
81
+ model.X = np.array(model.X, dtype=float)
82
+ model.Y = np.array(model.Y, dtype=float)
83
+
84
+ if model.restrictions is None:
85
+ model.R=np.eye(model.n_cols*model.n_vars)
86
+
87
+ elif model.restrictions=='zeros':
88
+ RR=np.eye(model.n_cols*model.n_vars)
89
+ drop=[]
90
+
91
+ for i in range(model.n_vars):
92
+ for j in range(model.n_vars):
93
+ if model.R[i][j]==0:
94
+ for k in range(model.lags):
95
+ drop.append(model.const*model.n_vars+i+(model.n_vars*j)+(k*model.n_vars*model.n_vars))
96
+
97
+ model.R=np.delete(RR, drop, axis=1)
98
+
99
+ elif model.restrictions=='custom zeros':
100
+ RR=model.R.ravel(order='F')
101
+ RRR=np.eye(len(RR))
102
+ drop=[]
103
+
104
+ for i in range(len(RR)):
105
+ if RR[i]==0:
106
+ drop.append(i)
107
+
108
+ model.R=np.delete(RRR, drop, axis=1)
109
+
110
+ return YXR(Y=model.Y, X=model.X, R=model.R)
111
+
@@ -0,0 +1,68 @@
1
+ #fc.py
2
+ import numpy as np
3
+
4
+ def compute_forecast(modelo, F):
5
+
6
+ # 1. Initialize State Vector (start)
7
+ start = np.zeros((modelo.n_vars * modelo.lags, 1))
8
+ for i in range(modelo.lags):
9
+ # Stacking lags: y_t, y_{t-1}, ...
10
+ start[i*modelo.n_vars : (i+1)*modelo.n_vars, :] = \
11
+ modelo.Y[modelo.n_obs - i - 1, :].reshape(-1, 1)
12
+
13
+ # 2. Initialize Forecast Array
14
+ Yfc = np.zeros((modelo.horizon + modelo.past, modelo.n_vars))
15
+ Yfc[0:modelo.past, :] = modelo.Y[-modelo.past:, :]
16
+
17
+ # 3. Pre-compute Deterministic Vectors (Optimization)
18
+ # Moving this outside the loop prevents creating new arrays 120 times
19
+ beta_const = np.zeros((modelo.n_vars * modelo.lags, 1))
20
+ beta_trend = np.zeros((modelo.n_vars * modelo.lags, 1))
21
+ beta_sqrd = np.zeros((modelo.n_vars * modelo.lags, 1))
22
+
23
+ if modelo.const >= 1:
24
+ beta_const[0:modelo.n_vars, 0] = modelo.beta[:, 0]
25
+ if modelo.const >= 2:
26
+ beta_trend[0:modelo.n_vars, 0] = modelo.beta[:, 1]
27
+ if modelo.const == 3:
28
+ beta_sqrd[0:modelo.n_vars, 0] = modelo.beta[:, 2]
29
+
30
+ # 4. Forecast Loop
31
+ for i in range(modelo.horizon):
32
+
33
+ # A. Transition Step
34
+ FYs = F @ start
35
+
36
+ # B. Add Deterministic Terms (Const, Trend, etc)
37
+ # Note: Ensure your trend logic (i + lags) matches your estimation definition
38
+ if modelo.const == 1:
39
+ FYs += beta_const
40
+ elif modelo.const == 2:
41
+ FYs += beta_const + ((i + modelo.lags) * beta_trend)
42
+ elif modelo.const == 3:
43
+ FYs += beta_const + (i * beta_trend) + ((i**2) * beta_sqrd)
44
+
45
+ # C. Add Dummies (if present)
46
+ if modelo.DUM is not None:
47
+ # Calculate dummy index offset for readability
48
+ idx_start = modelo.const + (modelo.lags * modelo.n_vars) + modelo.n_ex
49
+
50
+ if modelo.DUM == 'Q':
51
+ dummy_coefs = modelo.beta[:, idx_start : idx_start+3]
52
+ dummy_vals = modelo.Dhorizon[i, :].reshape(-1, 1)
53
+ FYs[0:modelo.n_vars, :] += dummy_coefs @ dummy_vals
54
+
55
+ elif modelo.DUM == 'M':
56
+ dummy_coefs = modelo.beta[:, idx_start : idx_start+11]
57
+ dummy_vals = modelo.Dhorizon[i, :].reshape(-1, 1)
58
+ FYs[0:modelo.n_vars, :] += dummy_coefs @ dummy_vals
59
+
60
+ # D. Store Forecast (THE FIX)
61
+ # We take the first 'n_vars' rows from the 'FYs' column vector
62
+ # and flatten them to fit into the Yfc row.
63
+ Yfc[modelo.past + i, :] = FYs[0:modelo.n_vars, 0]
64
+
65
+ # E. Update state for next iteration
66
+ start = FYs
67
+
68
+ return Yfc