modelit 0.2.2__tar.gz → 0.2.4__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.
- modelit-0.2.4/CONTRIBUTING.md +53 -0
- {modelit-0.2.2 → modelit-0.2.4}/PKG-INFO +1 -1
- modelit-0.2.4/modelit/templates/cnn/template.py +491 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit.egg-info/PKG-INFO +1 -1
- {modelit-0.2.2 → modelit-0.2.4}/modelit.egg-info/SOURCES.txt +3 -1
- {modelit-0.2.2 → modelit-0.2.4}/.github/workflows/publish.yml +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/.github/workflows/test.yml +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/.gitignore +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/LICENSE +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/MANIFEST.in +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/README.md +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit/__init__.py +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit/__main__.py +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit/cli.py +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit/registry.py +0 -0
- {modelit-0.2.2/modelit/templates/backpropogation → modelit-0.2.4/modelit/templates/backpropagation}/template.py +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit/templates/perceptron/template.py +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit.egg-info/dependency_links.txt +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit.egg-info/entry_points.txt +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/modelit.egg-info/top_level.txt +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/pyproject.toml +0 -0
- {modelit-0.2.2 → modelit-0.2.4}/setup.cfg +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing to ModelIt
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve ModelIt.
|
|
4
|
+
|
|
5
|
+
## Add a new template
|
|
6
|
+
|
|
7
|
+
1. Fork this repo.
|
|
8
|
+
2. Create a new folder:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
modelit/templates/<name>/
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
3. Add your code in:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
modelit/templates/<name>/template.py
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
4. Make sure the folder name is the function name.
|
|
21
|
+
5. Test it locally:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from modelit import <name>
|
|
25
|
+
|
|
26
|
+
<name>()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
modelit create <name>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Rules
|
|
36
|
+
|
|
37
|
+
- Keep templates simple and runnable.
|
|
38
|
+
- Avoid internet-only dependencies.
|
|
39
|
+
- Do not add `metadata.json`.
|
|
40
|
+
- Keep file names lowercase.
|
|
41
|
+
- Prefer clean, beginner-friendly code.
|
|
42
|
+
|
|
43
|
+
## Pull request flow
|
|
44
|
+
|
|
45
|
+
1. Fork the repo.
|
|
46
|
+
2. Create a branch.
|
|
47
|
+
3. Add your template.
|
|
48
|
+
4. Commit and push.
|
|
49
|
+
5. Open a pull request.
|
|
50
|
+
|
|
51
|
+
## Maintainer notes
|
|
52
|
+
|
|
53
|
+
I will review templates for clarity, correctness, and portability before merging.
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# =========================================================
|
|
2
|
+
# CNN FROM SCRATCH + CNN USING KERAS
|
|
3
|
+
# Uses Built-in MNIST Dataset
|
|
4
|
+
# No Kaggle Download Needed
|
|
5
|
+
# =========================================================
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# =========================================================
|
|
9
|
+
# IMPORTS
|
|
10
|
+
# =========================================================
|
|
11
|
+
|
|
12
|
+
import cv2
|
|
13
|
+
import numpy as np
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
|
|
16
|
+
from tensorflow.keras.datasets import mnist
|
|
17
|
+
from tensorflow.keras.models import Sequential
|
|
18
|
+
|
|
19
|
+
from tensorflow.keras.layers import (
|
|
20
|
+
Conv2D,
|
|
21
|
+
MaxPooling2D,
|
|
22
|
+
Flatten,
|
|
23
|
+
Dense
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from sklearn.model_selection import train_test_split
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# =========================================================
|
|
30
|
+
# LOAD SAMPLE IMAGE
|
|
31
|
+
# =========================================================
|
|
32
|
+
|
|
33
|
+
(X_train_full, y_train_full), (X_test_full, y_test_full) = mnist.load_data()
|
|
34
|
+
|
|
35
|
+
image = X_train_full[0]
|
|
36
|
+
|
|
37
|
+
print("Original Image Shape:", image.shape)
|
|
38
|
+
|
|
39
|
+
plt.imshow(image, cmap="gray")
|
|
40
|
+
plt.title("Original Image")
|
|
41
|
+
plt.axis("off")
|
|
42
|
+
plt.show()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# =========================================================
|
|
46
|
+
# CONVOLUTION FROM SCRATCH
|
|
47
|
+
# =========================================================
|
|
48
|
+
|
|
49
|
+
def convolution(image, kernel, strides=1):
|
|
50
|
+
|
|
51
|
+
image_height, image_width = image.shape
|
|
52
|
+
|
|
53
|
+
kernel_height, kernel_width = kernel.shape
|
|
54
|
+
|
|
55
|
+
output_height = (
|
|
56
|
+
(image_height - kernel_height) // strides
|
|
57
|
+
) + 1
|
|
58
|
+
|
|
59
|
+
output_width = (
|
|
60
|
+
(image_width - kernel_width) // strides
|
|
61
|
+
) + 1
|
|
62
|
+
|
|
63
|
+
output = np.zeros((output_height, output_width))
|
|
64
|
+
|
|
65
|
+
for i in range(1, image.shape[0] - 1, strides):
|
|
66
|
+
|
|
67
|
+
for j in range(1, image.shape[1] - 1, strides):
|
|
68
|
+
|
|
69
|
+
val = (
|
|
70
|
+
|
|
71
|
+
image[i - 1, j - 1] * kernel[0, 0] +
|
|
72
|
+
|
|
73
|
+
image[i - 1, j] * kernel[0, 1] +
|
|
74
|
+
|
|
75
|
+
image[i - 1, j + 1] * kernel[0, 2] +
|
|
76
|
+
|
|
77
|
+
image[i, j - 1] * kernel[1, 0] +
|
|
78
|
+
|
|
79
|
+
image[i, j] * kernel[1, 1] +
|
|
80
|
+
|
|
81
|
+
image[i, j + 1] * kernel[1, 2] +
|
|
82
|
+
|
|
83
|
+
image[i + 1, j - 1] * kernel[2, 0] +
|
|
84
|
+
|
|
85
|
+
image[i + 1, j] * kernel[2, 1] +
|
|
86
|
+
|
|
87
|
+
image[i + 1, j + 1] * kernel[2, 2]
|
|
88
|
+
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
out_i = (i - 1) // strides
|
|
92
|
+
|
|
93
|
+
out_j = (j - 1) // strides
|
|
94
|
+
|
|
95
|
+
output[out_i, out_j] = val
|
|
96
|
+
|
|
97
|
+
return output
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# =========================================================
|
|
101
|
+
# EDGE DETECTION KERNEL
|
|
102
|
+
# =========================================================
|
|
103
|
+
|
|
104
|
+
kernel = np.array([
|
|
105
|
+
[-1, -1, -1],
|
|
106
|
+
[-1, 8, -1],
|
|
107
|
+
[-1, -1, -1]
|
|
108
|
+
])
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# =========================================================
|
|
112
|
+
# APPLY CONVOLUTION
|
|
113
|
+
# =========================================================
|
|
114
|
+
|
|
115
|
+
conv_img = convolution(image, kernel, 1)
|
|
116
|
+
|
|
117
|
+
plt.imshow(conv_img, cmap="gray")
|
|
118
|
+
plt.title("Convolution Output")
|
|
119
|
+
plt.axis("off")
|
|
120
|
+
plt.show()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# =========================================================
|
|
124
|
+
# RELU ACTIVATION
|
|
125
|
+
# =========================================================
|
|
126
|
+
|
|
127
|
+
def relu(image):
|
|
128
|
+
|
|
129
|
+
for i in range(0, image.shape[0]):
|
|
130
|
+
|
|
131
|
+
for j in range(0, image.shape[1]):
|
|
132
|
+
|
|
133
|
+
if image[i, j] >= 0:
|
|
134
|
+
|
|
135
|
+
image[i, j] = image[i, j]
|
|
136
|
+
|
|
137
|
+
else:
|
|
138
|
+
|
|
139
|
+
image[i, j] = 0
|
|
140
|
+
|
|
141
|
+
return image
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# =========================================================
|
|
145
|
+
# APPLY RELU
|
|
146
|
+
# =========================================================
|
|
147
|
+
|
|
148
|
+
relu_img = relu(conv_img)
|
|
149
|
+
|
|
150
|
+
plt.imshow(relu_img, cmap="gray")
|
|
151
|
+
plt.title("ReLU Output")
|
|
152
|
+
plt.axis("off")
|
|
153
|
+
plt.show()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# =========================================================
|
|
157
|
+
# MAX POOLING
|
|
158
|
+
# =========================================================
|
|
159
|
+
|
|
160
|
+
def max_pooling(image, pool_size=2, strides=2):
|
|
161
|
+
|
|
162
|
+
image_height, image_width = image.shape
|
|
163
|
+
|
|
164
|
+
output_height = (
|
|
165
|
+
(image_height - pool_size) // strides
|
|
166
|
+
) + 1
|
|
167
|
+
|
|
168
|
+
output_width = (
|
|
169
|
+
(image_width - pool_size) // strides
|
|
170
|
+
) + 1
|
|
171
|
+
|
|
172
|
+
output = np.zeros((output_height, output_width))
|
|
173
|
+
|
|
174
|
+
for i in range(0, image_height - 1, strides):
|
|
175
|
+
|
|
176
|
+
for j in range(0, image_width - 1, strides):
|
|
177
|
+
|
|
178
|
+
val = image[i, j]
|
|
179
|
+
|
|
180
|
+
if image[i, j + 1] > val:
|
|
181
|
+
|
|
182
|
+
val = image[i, j + 1]
|
|
183
|
+
|
|
184
|
+
if image[i + 1, j] > val:
|
|
185
|
+
|
|
186
|
+
val = image[i + 1, j]
|
|
187
|
+
|
|
188
|
+
if image[i + 1, j + 1] > val:
|
|
189
|
+
|
|
190
|
+
val = image[i + 1, j + 1]
|
|
191
|
+
|
|
192
|
+
out_i = i // strides
|
|
193
|
+
|
|
194
|
+
out_j = j // strides
|
|
195
|
+
|
|
196
|
+
if (
|
|
197
|
+
out_i < output_height and
|
|
198
|
+
out_j < output_width
|
|
199
|
+
):
|
|
200
|
+
|
|
201
|
+
output[out_i, out_j] = val
|
|
202
|
+
|
|
203
|
+
return output
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# =========================================================
|
|
207
|
+
# APPLY MAX POOLING
|
|
208
|
+
# =========================================================
|
|
209
|
+
|
|
210
|
+
pool_img = max_pooling(relu_img, 2, 2)
|
|
211
|
+
|
|
212
|
+
plt.imshow(pool_img, cmap="gray")
|
|
213
|
+
plt.title("Max Pooling Output")
|
|
214
|
+
plt.axis("off")
|
|
215
|
+
plt.show()
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# =========================================================
|
|
219
|
+
# FLATTEN
|
|
220
|
+
# =========================================================
|
|
221
|
+
|
|
222
|
+
flat_img = pool_img.flatten()
|
|
223
|
+
|
|
224
|
+
print("\n=================================================")
|
|
225
|
+
|
|
226
|
+
print("Original Image Shape:", image.shape)
|
|
227
|
+
|
|
228
|
+
print("Convolution Shape:", conv_img.shape)
|
|
229
|
+
|
|
230
|
+
print("ReLU Shape:", relu_img.shape)
|
|
231
|
+
|
|
232
|
+
print("Pooling Shape:", pool_img.shape)
|
|
233
|
+
|
|
234
|
+
print("Flatten Shape:", flat_img.shape)
|
|
235
|
+
|
|
236
|
+
print("=================================================")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# =========================================================
|
|
240
|
+
# DENSE LAYER
|
|
241
|
+
# =========================================================
|
|
242
|
+
|
|
243
|
+
weights = np.random.rand(flat_img.shape[0])
|
|
244
|
+
|
|
245
|
+
dense_output = np.dot(flat_img, weights)
|
|
246
|
+
|
|
247
|
+
print("\nDense Layer Output:", dense_output)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# =========================================================
|
|
251
|
+
# CNN USING KERAS
|
|
252
|
+
# =========================================================
|
|
253
|
+
|
|
254
|
+
print("\n=================================================")
|
|
255
|
+
print("CNN USING KERAS")
|
|
256
|
+
print("=================================================")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# =========================================================
|
|
260
|
+
# NORMALIZATION
|
|
261
|
+
# =========================================================
|
|
262
|
+
|
|
263
|
+
X_train_full = X_train_full / 255.0
|
|
264
|
+
|
|
265
|
+
X_test_full = X_test_full / 255.0
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# =========================================================
|
|
269
|
+
# RESHAPE FOR CNN
|
|
270
|
+
# =========================================================
|
|
271
|
+
|
|
272
|
+
X_train_full = X_train_full.reshape(-1, 28, 28, 1)
|
|
273
|
+
|
|
274
|
+
X_test_full = X_test_full.reshape(-1, 28, 28, 1)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# =========================================================
|
|
278
|
+
# TRAIN TEST SPLIT
|
|
279
|
+
# =========================================================
|
|
280
|
+
|
|
281
|
+
X_train, X_val, y_train, y_val = train_test_split(
|
|
282
|
+
|
|
283
|
+
X_train_full,
|
|
284
|
+
y_train_full,
|
|
285
|
+
|
|
286
|
+
test_size=0.2,
|
|
287
|
+
|
|
288
|
+
random_state=42
|
|
289
|
+
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
print("\nTraining Samples:", X_train.shape[0])
|
|
294
|
+
|
|
295
|
+
print("Validation Samples:", X_val.shape[0])
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# =========================================================
|
|
299
|
+
# CREATE CNN MODEL
|
|
300
|
+
# =========================================================
|
|
301
|
+
|
|
302
|
+
model = Sequential()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# =========================================================
|
|
306
|
+
# CONVOLUTION + RELU + POOLING
|
|
307
|
+
# =========================================================
|
|
308
|
+
|
|
309
|
+
model.add(
|
|
310
|
+
|
|
311
|
+
Conv2D(
|
|
312
|
+
32,
|
|
313
|
+
(3, 3),
|
|
314
|
+
activation="relu",
|
|
315
|
+
input_shape=(28, 28, 1)
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
model.add(
|
|
321
|
+
|
|
322
|
+
MaxPooling2D((2, 2))
|
|
323
|
+
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# =========================================================
|
|
328
|
+
# SECOND CNN BLOCK
|
|
329
|
+
# =========================================================
|
|
330
|
+
|
|
331
|
+
model.add(
|
|
332
|
+
|
|
333
|
+
Conv2D(
|
|
334
|
+
64,
|
|
335
|
+
(3, 3),
|
|
336
|
+
activation="relu"
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
model.add(
|
|
342
|
+
|
|
343
|
+
MaxPooling2D((2, 2))
|
|
344
|
+
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# =========================================================
|
|
349
|
+
# THIRD CNN BLOCK
|
|
350
|
+
# =========================================================
|
|
351
|
+
|
|
352
|
+
model.add(
|
|
353
|
+
|
|
354
|
+
Conv2D(
|
|
355
|
+
128,
|
|
356
|
+
(3, 3),
|
|
357
|
+
activation="relu"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
model.add(
|
|
363
|
+
|
|
364
|
+
MaxPooling2D((2, 2))
|
|
365
|
+
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# =========================================================
|
|
370
|
+
# FLATTEN
|
|
371
|
+
# =========================================================
|
|
372
|
+
|
|
373
|
+
model.add(Flatten())
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# =========================================================
|
|
377
|
+
# DENSE LAYER
|
|
378
|
+
# =========================================================
|
|
379
|
+
|
|
380
|
+
model.add(
|
|
381
|
+
|
|
382
|
+
Dense(
|
|
383
|
+
64,
|
|
384
|
+
activation="relu"
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
# =========================================================
|
|
391
|
+
# OUTPUT LAYER
|
|
392
|
+
# MNIST = 10 CLASSES
|
|
393
|
+
# =========================================================
|
|
394
|
+
|
|
395
|
+
model.add(
|
|
396
|
+
|
|
397
|
+
Dense(
|
|
398
|
+
10,
|
|
399
|
+
activation="softmax"
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
# =========================================================
|
|
406
|
+
# MODEL SUMMARY
|
|
407
|
+
# =========================================================
|
|
408
|
+
|
|
409
|
+
model.summary()
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# =========================================================
|
|
413
|
+
# COMPILE MODEL
|
|
414
|
+
# =========================================================
|
|
415
|
+
|
|
416
|
+
model.compile(
|
|
417
|
+
|
|
418
|
+
optimizer="adam",
|
|
419
|
+
|
|
420
|
+
loss="sparse_categorical_crossentropy",
|
|
421
|
+
|
|
422
|
+
metrics=["accuracy"]
|
|
423
|
+
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
# =========================================================
|
|
428
|
+
# TRAIN MODEL
|
|
429
|
+
# =========================================================
|
|
430
|
+
|
|
431
|
+
train_model = model.fit(
|
|
432
|
+
|
|
433
|
+
X_train,
|
|
434
|
+
y_train,
|
|
435
|
+
|
|
436
|
+
epochs=5,
|
|
437
|
+
|
|
438
|
+
validation_data=(X_val, y_val)
|
|
439
|
+
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# =========================================================
|
|
444
|
+
# EVALUATE MODEL
|
|
445
|
+
# =========================================================
|
|
446
|
+
|
|
447
|
+
loss, accuracy = model.evaluate(
|
|
448
|
+
X_test_full,
|
|
449
|
+
y_test_full
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
print("\n=================================================")
|
|
453
|
+
|
|
454
|
+
print("Test Accuracy:", accuracy)
|
|
455
|
+
|
|
456
|
+
print("Test Loss:", loss)
|
|
457
|
+
|
|
458
|
+
print("=================================================")
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
# =========================================================
|
|
462
|
+
# PREDICTION
|
|
463
|
+
# =========================================================
|
|
464
|
+
|
|
465
|
+
prediction = model.predict(
|
|
466
|
+
X_test_full[0].reshape(1, 28, 28, 1)
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
predicted_class = np.argmax(prediction)
|
|
470
|
+
|
|
471
|
+
print("\nPredicted Digit:", predicted_class)
|
|
472
|
+
|
|
473
|
+
print("Actual Digit:", y_test_full[0])
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# =========================================================
|
|
477
|
+
# SHOW TEST IMAGE
|
|
478
|
+
# =========================================================
|
|
479
|
+
|
|
480
|
+
plt.imshow(
|
|
481
|
+
X_test_full[0].reshape(28, 28),
|
|
482
|
+
cmap="gray"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
plt.title(
|
|
486
|
+
f"Predicted: {predicted_class}"
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
plt.axis("off")
|
|
490
|
+
|
|
491
|
+
plt.show()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.gitignore
|
|
2
|
+
CONTRIBUTING.md
|
|
2
3
|
LICENSE
|
|
3
4
|
MANIFEST.in
|
|
4
5
|
README.md
|
|
@@ -14,5 +15,6 @@ modelit.egg-info/SOURCES.txt
|
|
|
14
15
|
modelit.egg-info/dependency_links.txt
|
|
15
16
|
modelit.egg-info/entry_points.txt
|
|
16
17
|
modelit.egg-info/top_level.txt
|
|
17
|
-
modelit/templates/
|
|
18
|
+
modelit/templates/backpropagation/template.py
|
|
19
|
+
modelit/templates/cnn/template.py
|
|
18
20
|
modelit/templates/perceptron/template.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|