{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# **!!!_14a_Testing on the Concrete Strength dataset + Time**"
      ],
      "metadata": {
        "id": "P7OgXyQi9RYB"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# **Ksi = 0**"
      ],
      "metadata": {
        "id": "KW5swhig9h9u"
      }
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "59vQNvjhJwuR"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "8RATn7ot7Jp0",
        "outputId": "da3f88ad-e1c4-4a5e-90aa-233eb8c3b291"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Collecting pyod\n",
            "  Downloading pyod-3.5.2-py3-none-any.whl.metadata (58 kB)\n",
            "\u001b[?25l     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/58.0 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K     \u001b[91m━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m30.7/58.0 kB\u001b[0m \u001b[31m3.2 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.0/58.0 kB\u001b[0m \u001b[31m744.7 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hRequirement already satisfied: joblib>=1.5 in /usr/local/lib/python3.12/dist-packages (from pyod) (1.5.3)\n",
            "Requirement already satisfied: matplotlib in /usr/local/lib/python3.12/dist-packages (from pyod) (3.10.0)\n",
            "Requirement already satisfied: numpy>=1.19 in /usr/local/lib/python3.12/dist-packages (from pyod) (2.0.2)\n",
            "Requirement already satisfied: numba>=0.51 in /usr/local/lib/python3.12/dist-packages (from pyod) (0.60.0)\n",
            "Requirement already satisfied: scipy>=1.5.1 in /usr/local/lib/python3.12/dist-packages (from pyod) (1.16.3)\n",
            "Requirement already satisfied: scikit-learn>=0.22.0 in /usr/local/lib/python3.12/dist-packages (from pyod) (1.6.1)\n",
            "Requirement already satisfied: llvmlite<0.44,>=0.43.0dev0 in /usr/local/lib/python3.12/dist-packages (from numba>=0.51->pyod) (0.43.0)\n",
            "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.12/dist-packages (from scikit-learn>=0.22.0->pyod) (3.6.0)\n",
            "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (1.3.3)\n",
            "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (0.12.1)\n",
            "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (4.62.1)\n",
            "Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (1.5.0)\n",
            "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (26.1)\n",
            "Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (11.3.0)\n",
            "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (3.3.2)\n",
            "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.12/dist-packages (from matplotlib->pyod) (2.9.0.post0)\n",
            "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.7->matplotlib->pyod) (1.17.0)\n",
            "Downloading pyod-3.5.2-py3-none-any.whl (396 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m396.0/396.0 kB\u001b[0m \u001b[31m4.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hInstalling collected packages: pyod\n",
            "Successfully installed pyod-3.5.2\n",
            "================================================================================\n",
            "CONCRETE STRENGTH DATASET - OUTLIER DETECTION COMPARISON (WITH TIMING)\n",
            "================================================================================\n",
            "Program started at: 2026-05-25 02:18:33\n",
            "\n",
            "============================================================\n",
            "CREATING 'CONCRETE STRENGTH' DATASET WITH OUTLIERS\n",
            "============================================================\n",
            "Dataset loading time: 2.12 seconds\n",
            "Original dataset loaded.\n",
            "Size: 1030 rows, 9 columns\n",
            "Strength range: 2.33 – 82.60 MPa\n",
            "\n",
            "============================================================\n",
            "STEP 1: FIND MAXIMUM, MINIMUM AND AVERAGE STRENGTH VALUES:\n",
            "============================================================\n",
            "Maximum strength (Smax): 82.60 MPa\n",
            "Minimum strength (Smin): 2.33 MPa\n",
            "Average strength (Savg): 42.47 MPa\n",
            "\n",
            "============================================================\n",
            "STEP 2: SORT DATASET IN DESCENDING ORDER BY STRENGTH\n",
            "============================================================\n",
            "Sorted dataset created (descending order).\n",
            "\n",
            "============================================================\n",
            "STEPS 3 & 4: REPLACE STRENGTH VALUES WITH AVERAGE (CREATING 20 OUTLIERS)\n",
            "============================================================\n",
            "  • First 10 observations (highest strength): replaced with Savg (42.47 MPa)\n",
            "  • Last 10 observations (lowest strength): replaced with Savg (42.47 MPa)\n",
            "\n",
            "🔴 REPLACING FIRST 10 OBSERVATIONS (HIGHEST STRENGTH) WITH AVERAGE:\n",
            "\n",
            "   Outlier #1:\n",
            "   Row index: 0\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 82.60 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #2:\n",
            "   Row index: 1\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 81.75 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #3:\n",
            "   Row index: 2\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 80.20 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #4:\n",
            "   Row index: 3\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 79.99 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #5:\n",
            "   Row index: 4\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 79.40 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   ... and 5 more replacements (rows 6-10)\n",
            "\n",
            "✅ First 10 observations replaced with Savg\n",
            "\n",
            "🔴 REPLACING LAST 10 OBSERVATIONS (LOWEST STRENGTH) WITH AVERAGE:\n",
            "\n",
            "   Outlier #11:\n",
            "   Row index: 1020\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 6.81 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #12:\n",
            "   Row index: 1021\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 6.47 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #13:\n",
            "   Row index: 1022\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 6.28 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #14:\n",
            "   Row index: 1023\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 6.27 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   Outlier #15:\n",
            "   Row index: 1024\n",
            "   📊 Changes:\n",
            "      • CompressiveStrength: 4.90 → 42.47 MPa (replaced with average value)\n",
            "   ✅ Other features unchanged\n",
            "\n",
            "   ... and 5 more replacements (rows 1026 to 1029)\n",
            "\n",
            "✅ Last 10 observations replaced with Savg\n",
            "\n",
            "============================================================\n",
            "STEP 5: ASSIGN OUTLIER STATUS\n",
            "============================================================\n",
            "  • First 10 observations: marked as OUTLIERS\n",
            "  • Last 10 observations: marked as OUTLIERS\n",
            "  • Total outliers: 20\n",
            "\n",
            "============================================================\n",
            "FINAL STATISTICS OF THE CREATED DATASET:\n",
            "============================================================\n",
            "  • Total observations: 1030\n",
            "  • Outliers: 20 (1.942%)\n",
            "    - Type H→A (were the highest strength, became average): 10\n",
            "    - Type L→A (were the lowest strength, became average): 10\n",
            "  • Normal: 1010\n",
            "\n",
            "✅ Dataset saved to 'concrete_labeled.csv'\n",
            "\n",
            "============================================================\n",
            "STEP 6: SHUFFLING DATA BEFORE TESTING\n",
            "============================================================\n",
            "✅ Dataset shuffled.\n",
            "Dataset size: 1030 records\n",
            "Outliers: 20 (1.94%)\n",
            "\n",
            "============================================================\n",
            "TESTING OUTLIER DETECTION METHODS\n",
            "============================================================\n",
            "\n",
            "📊 Number of features for detectors: 8\n",
            "📊 Outliers created by replacing extreme values with average (highest→average, lowest→average)\n",
            "📊 Total outliers: 20 (1.94% of data)\n",
            "\n",
            "\n",
            "============================================================\n",
            "OUTLIER DETECTION PARAMETER SETUP\n",
            "============================================================\n",
            "The dataset contains 20 true outliers (proportion 1.94%)\n",
            "✅ ALL methods will search for exactly 20 outliers (1.942%)\n",
            "✅ Parameter Ksi = 0 (neuron search range)\n",
            "\n",
            "============================================================\n",
            "RUNNING NNFA ALGORITHM (AVERAGING OVER $N$)\n",
            "============================================================\n",
            "Q = 1030, N_x = 8, N_y = 1\n",
            "N_min = 11.3961, N_max = 143.2778\n",
            "Ksi = 0, N_lim = 11.3961\n",
            "Loop over $N$ from 12 to 12 inclusive\n",
            "This may take some time...\n",
            "  Completed N = 12\n",
            "\n",
            "==================================================\n",
            "NNFA RESULTS\n",
            "==================================================\n",
            "Number of detected outliers: 20 (expected 20)\n",
            "Threshold: 0.295044\n",
            "NNFA execution time: 6.18 seconds\n",
            "\n",
            "================================================================================\n",
            "METHODS ANALYZING ONLY INPUT FEATURES (PyOD)\n",
            "================================================================================\n",
            "  ABOD (pyod) successfully trained (time: 2.43s)\n",
            "  HBOS (pyod) successfully trained (time: 3.82s)\n",
            "  IsolationForest (pyod) successfully trained (time: 0.90s)\n",
            "  kNN (pyod) successfully trained (time: 0.05s)\n",
            "  LOF (pyod) successfully trained (time: 0.13s)\n",
            "  OCSVM (pyod) successfully trained (time: 0.19s)\n",
            "  PCA (pyod) successfully trained (time: 0.02s)\n",
            "  COPOD (pyod) successfully trained (time: 0.56s)\n",
            "\n",
            "================================================================================\n",
            "METHODS ANALYZING INPUT FEATURES + TARGET VARIABLE\n",
            "================================================================================\n",
            "\n",
            "1. Random Forest Regressor...\n",
            "   Completed in 0.68 seconds\n",
            "2. Neural Network Regressor...\n",
            "   Completed in 1.33 seconds\n",
            "3. Autoencoder (input feature reconstruction error)...\n",
            "    Autoencoder: epoch 50/300, loss = 0.219953\n",
            "    Autoencoder: epoch 100/300, loss = 0.143213\n",
            "    Autoencoder: epoch 150/300, loss = 0.118373\n",
            "    Autoencoder: epoch 200/300, loss = 0.100079\n",
            "    Autoencoder: epoch 250/300, loss = 0.086610\n",
            "    Autoencoder: epoch 300/300, loss = 0.080436\n",
            "   Completed in 0.43 seconds\n",
            "4. Combined method (Random Forest + Autoencoder)...\n",
            "   Completed in 0.00 seconds\n",
            "5. One-Class SVM (with y added)...\n",
            "   Completed in 0.07 seconds\n",
            "6. Isolation Forest (with y added)...\n",
            "   Completed in 0.18 seconds\n",
            "7. LOF (with y added)...\n",
            "   Completed in 0.02 seconds\n",
            "\n",
            "================================================================================\n",
            "OUTLIER DETECTION METHOD COMPARISON\n",
            "================================================================================\n",
            "\n",
            "                       Method  Precision  Recall   F1  TP  FP  FN\n",
            "                         NNFA       0.75    0.75 0.75  15   5   5\n",
            "        Random Forest (error)       0.55    0.55 0.55  11   9   9\n",
            "           ABOD (pyod) (pyod)       0.20    0.20 0.20   4  16  16\n",
            "            LOF (pyod) (pyod)       0.15    0.15 0.15   3  17  17\n",
            "       Neural Network (error)       0.05    0.05 0.05   1  19  19\n",
            "             Combined (RF+AE)       0.05    0.05 0.05   1  19  19\n",
            "IsolationForest (pyod) (pyod)       0.00    0.00 0.00   0  20  20\n",
            "           HBOS (pyod) (pyod)       0.00    0.00 0.00   0  20  20\n",
            "          COPOD (pyod) (pyod)       0.00    0.00 0.00   0  20  20\n",
            "            PCA (pyod) (pyod)       0.00    0.00 0.00   0  20  20\n",
            "          OCSVM (pyod) (pyod)       0.00    0.00 0.00   0  20  20\n",
            "            kNN (pyod) (pyod)       0.00    0.00 0.00   0  20  20\n",
            " Autoencoder (reconstruction)       0.00    0.00 0.00   0  20  20\n",
            "       One-Class SVM (with y)       0.00    0.00 0.00   0  20  20\n",
            "    Isolation Forest (with y)       0.00    0.00 0.00   0  20  20\n",
            "                 LOF (with y)       0.00    0.00 0.00   0  20  20\n",
            "\n",
            "================================================================================\n",
            "VERIFICATION: Number of outliers detected by each method\n",
            "================================================================================\n",
            "✓ ABOD (pyod) (pyod)                           :  20 outliers (expected 20)\n",
            "✓ HBOS (pyod) (pyod)                           :  20 outliers (expected 20)\n",
            "✓ IsolationForest (pyod) (pyod)                :  20 outliers (expected 20)\n",
            "✓ kNN (pyod) (pyod)                            :  20 outliers (expected 20)\n",
            "✓ LOF (pyod) (pyod)                            :  20 outliers (expected 20)\n",
            "✓ OCSVM (pyod) (pyod)                          :  20 outliers (expected 20)\n",
            "✓ PCA (pyod) (pyod)                            :  20 outliers (expected 20)\n",
            "✓ COPOD (pyod) (pyod)                          :  20 outliers (expected 20)\n",
            "✓ Random Forest (error)                        :  20 outliers (expected 20)\n",
            "✓ Neural Network (error)                       :  20 outliers (expected 20)\n",
            "✓ Autoencoder (reconstruction)                 :  20 outliers (expected 20)\n",
            "✓ Combined (RF+AE)                             :  20 outliers (expected 20)\n",
            "✓ One-Class SVM (with y)                       :  20 outliers (expected 20)\n",
            "✓ Isolation Forest (with y)                    :  20 outliers (expected 20)\n",
            "✓ LOF (with y)                                 :  20 outliers (expected 20)\n",
            "✓ NNFA                                         :  20 outliers (expected 20)\n",
            "\n",
            "================================================================================\n",
            "EXECUTION TIME SUMMARY (seconds)\n",
            "================================================================================\n",
            "                      Method  Time (seconds)\n",
            "                        NNFA        6.177460\n",
            "                 HBOS (pyod)        3.819377\n",
            "                 ABOD (pyod)        2.425518\n",
            "      Neural Network (error)        1.332851\n",
            "      IsolationForest (pyod)        0.901088\n",
            "       Random Forest (error)        0.678039\n",
            "                COPOD (pyod)        0.561534\n",
            "Autoencoder (reconstruction)        0.425909\n",
            "                OCSVM (pyod)        0.191978\n",
            "   Isolation Forest (with y)        0.183190\n",
            "                  LOF (pyod)        0.125281\n",
            "      One-Class SVM (with y)        0.069333\n",
            "                  kNN (pyod)        0.053440\n",
            "                  PCA (pyod)        0.022808\n",
            "                LOF (with y)        0.020383\n",
            "            Combined (RF+AE)        0.000357\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1200x600 with 1 Axes>"
            ],
            "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAJOCAYAAABm7rQwAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAArLBJREFUeJzs3Xl4U2Xax/Ff2tKWrRRkKwiySSm4sAkiIIgIIwoyLijqACqbyuiIDuIICm4MLojiAuiIjuKACqLvqCCiqCCOtFQWKXshKGUplELpRpPz/lESGpK2SZu938915erpyVmeO+fkLHee5zkmwzAMAQAAAAAAAH4UEegCAAAAAAAAoOohKQUAAAAAAAC/IykFAAAAAAAAvyMpBQAAAAAAAL8jKQUAAAAAAAC/IykFAAAAAAAAvyMpBQAAAAAAAL8jKQUAAAAAAAC/IykFAAAAAAAAvyMpBQAAEOJWr14tk8mk1atX28eNGjVKLVq08Mv6W7RooVGjRtn/f/fdd2UymZScnOyX9fft21d9+/b1y7oAAID3kJQCAAAesSUcYmNj9ccffzi937dvX1100UUO41q0aCGTyaS//vWvTtPbEiqffPKJ0zpcvSZPnuww/xtvvCGTyaTu3bt7KcKqa+vWrZo2bZr27t0b6KI4CeayAQCAiokKdAEAAEBoKigo0D//+U/NmTPH7XneeustPfbYY2rSpIlb0z/11FNq2bKlw7hzE14LFy5UixYt9Msvv2jXrl1q06aN2+UJZ2+99ZasVqtH82zdulXTp09X3759PapltX37dkVE+Pa3zrLK9vXXX/t03QAAwDeoKQUAACqkY8eOeuutt3TgwAG3pu/QoYMsFov++c9/ur2Oa6+9VnfeeafDq2PHjvb309PT9dNPP2nWrFlq0KCBFi5c6GkYFXLq1CmX461Wq/Lz832ybE9Vq1ZNMTExXlmWK4ZhKC8vT5IUExOjatWq+Wxd5YmOjlZ0dHTA1g8AACqGpBQAAKiQf/zjHx4lmVq0aKERI0Z4lMgqz8KFC1W3bl1dd911uvnmmz1OSn311Vfq3bu3atasqdq1a+u6667Tb7/95jDNqFGjVKtWLe3evVuDBg1S7dq1dccdd0iSTCaTJkyYoIULF6pDhw6KiYnR8uXLJUmpqam69tprFRcXp1q1aunqq6/Wzz//7LBsWzPF77//Xvfdd58aNmyo888/v8wy//777xo6dKhq1qyphg0b6qGHHlJBQYHTdK76lFq0aJG6dOmi2rVrKy4uThdffLFeeeUVe1luueUWSdJVV11lby5p66eqRYsWuv7667VixQp17dpV1atX17x58+zvlexTyiY3N1fjxo3Teeedp7i4OI0YMUJZWVkO05hMJk2bNs1p3pLLLK9srvqUOnz4sO655x41atRIsbGxuvTSS/Xee+85TLN3716ZTCa9+OKLmj9/vlq3bq2YmBhddtllWr9+vVOZAACAd9F8DwAAVEjLli3tSabJkye71STv8ccf17///W/985//1Kuvvlru9NnZ2crMzHQYV79+ffvwwoULdeONNyo6OlrDhw/Xm2++qfXr1+uyyy4rd9nvv/++Ro4cqYEDB2rmzJnKzc3Vm2++qV69eik1NdUhoVNUVKSBAweqV69eevHFF1WjRg37e99++60++ugjTZgwQfXr11eLFi3022+/qXfv3oqLi9OkSZNUrVo1zZs3T3379tX333/v1P/VfffdpwYNGuiJJ54os6ZUXl6err76apnNZj3wwANq0qSJ3n//fX377bflxrty5UoNHz5cV199tWbOnClJSktL09q1a/Xggw/qyiuv1AMPPKBXX31V//jHP5SUlCRJ9r9ScTO94cOHa9y4cRozZowSExPLXOeECRMUHx+vadOmafv27XrzzTe1b98+ez9i7nKnbCXl5eWpb9++2rVrlyZMmKCWLVvq448/1qhRo3T8+HE9+OCDDtN/+OGHOnnypMaNGyeTyaTnn39eN954o/bs2RPQGmAAAIQ7klIAAKDCbEmmmTNn2mvclKVVq1b6y1/+Yu9bKiEhoczp+/fv7zTOMAxJUkpKirZt22bv06pXr146//zztXDhwnKTUjk5OXrggQc0evRozZ8/3z5+5MiRSkxM1HPPPecwvqCgQLfccotmzJjhtKzt27dr8+bNat++vX3cn//8Z50+fVpr1qxRq1atJEkjRoxQYmKiJk2apO+//95hGfXq1dOqVasUGRlZZrnnz5+vHTt26KOPPrLXHBozZowuvfTSMueTpC+++EJxcXFasWKFy/W0atVKvXv31quvvqprrrnG5dPsdu3apeXLl2vgwIHlrk8qbla3atUqe2Lnggsu0KRJk/R///d/GjJkiFvLcLdsJc2fP19paWn64IMP7LXaxo8frz59+mjKlCm6++67Vbt2bfv0ZrNZO3fuVN26dSVJiYmJuuGGG7RixQpdf/31bpcTAAB4huZ7AACgwmxJpvnz5ysjI8OteaZMmaKioiK3mv29/vrrWrlypcPLZuHChWrUqJGuuuoqScXNwG699VYtWrRIFoulzOWuXLlSx48f1/Dhw5WZmWl/RUZGqnv37vruu++c5rn33ntdLqtPnz4OCSmLxaKvv/5aQ4cOtSekJCkhIUG333671qxZoxMnTjgsY8yYMeUmpCTpyy+/VEJCgm6++Wb7uBo1amjs2LHlzhsfH69Tp045fIaeatmypdsJKUkaO3asQ02je++9V1FRUfryyy8rXAZ3fPnll2rcuLGGDx9uH1etWjU98MADysnJcUoK3nrrrfaElCT17t1bkrRnzx6flhMAgKqOpBQAAKgUT5JMkmeJrG7duql///4OL6k48bNo0SJdddVVSk9P165du7Rr1y51795dhw4d0qpVq8pc7s6dOyVJ/fr1U4MGDRxeX3/9tQ4fPuwwfVRUVKl9PZ37dMAjR44oNzfXZdO2pKQkWa1W7d+/v8xllGbfvn1q06aNU9O38prRScVNBNu2batrr71W559/vu6++257/1fucrecNhdeeKHD/7Vq1VJCQoL27t3r0XI8tW/fPl144YVOTwS0Nffbt2+fw/jmzZs7/G9LUJ3b/xUAAPAumu8BAIBKadWqle68807Nnz9fkydPdmuexx9/XO+//75mzpypoUOHerzOb7/9VhkZGVq0aJEWLVrk9P7ChQs1YMCAUue3Wq2SivuVaty4sdP7UVGOl0gxMTFOCQ6b6tWre1J0ny2jPA0bNtSvv/6qFStW6KuvvtJXX32lBQsWaMSIEU4dgJfGH+W0Ka+2mzeVVkvN1lQUAAD4BkkpAABQaVOmTNEHH3xg70C7PK1bt9add96pefPmOXX67Y6FCxeqYcOGev31153eW7p0qT799FPNnTu31CRK69atJRUnalz1W1UZDRo0UI0aNbR9+3an97Zt26aIiAg1a9asQsu+4IILtGXLFhmG4VBbytW6XImOjtbgwYM1ePBgWa1W3XfffZo3b56mTp3qsgZWZe3cudPevFIq7ssrIyNDgwYNso+rW7eujh8/7jBfYWGhUy06T8p2wQUXaNOmTbJarQ7JxG3bttnfBwAAgUfzPQAAUGklk0wHDx50a54pU6bo9OnTev755z1aV15enpYuXarrr79eN998s9NrwoQJOnnypD7//PNSlzFw4EDFxcXpueee0+nTp53eP3LkiEdlKikyMlIDBgzQZ5995tBM7dChQ/rwww/Vq1cvxcXFVWjZgwYN0oEDB/TJJ5/Yx+Xm5jp0yl6ao0ePOvwfERGhSy65RFJxR+6SVLNmTUlyShJV1Pz58x0+3zfffFNFRUW69tpr7eNat26tH374wWm+c2tKeVK2QYMG6eDBg1q8eLF9XFFRkebMmaNatWqpT58+FQkHAAB4GTWlAACAV9ia5G3fvl0dOnQod3pbIsvdpmM2n3/+uU6ePFnq09suv/xyNWjQQAsXLtStt97qcpq4uDi9+eab+stf/qLOnTvrtttuU4MGDWQ2m/XFF1+oZ8+eeu211zwqV0nPPPOMVq5cqV69eum+++5TVFSU5s2bp4KCAo+TcCWNGTNGr732mkaMGKGUlBQlJCTo/fffV40aNcqdd/To0Tp27Jj69eun888/X/v27dOcOXPUsWNHe19LHTt2VGRkpGbOnKns7GzFxMSoX79+atiwYYXKW1hYqKuvvlrDhg3T9u3b9cYbb6hXr14O22706NEaP368brrpJl1zzTXauHGjVqxYofr16zssy5OyjR07VvPmzdOoUaOUkpKiFi1a6JNPPtHatWs1e/ZshyfvAQCAwKGmFAAA8Io2bdrozjvv9GieKVOmuPXUuZIWLlyo2NhYXXPNNS7fj4iI0HXXXafly5c71Q4q6fbbb9eqVavUtGlTvfDCC3rwwQe1aNEidezYUXfddZdHZTpXhw4d9OOPP+qiiy7SjBkzNH36dF1wwQX67rvvKtRc0aZGjRpatWqVBgwYoDlz5uiZZ55Rr1693Ep03XnnnYqNjdUbb7yh++67T++9955uvfVWffXVV/Ymbo0bN9bcuXN1+PBh3XPPPRo+fLi2bt1a4fK+9tprSkpK0hNPPKF3331Xw4cP12effebQFG/MmDF69NFH9cMPP+jhhx9Wenq6Vq5caa8ZZeNJ2apXr67Vq1frjjvu0HvvvaeHH35Yx44d04IFC/Tggw9WOB4AAOBdJoMeHAEAAAAAAOBn1JQCAAAAAACA35GUAgAAAAAAgN+RlAIAAAAAAIDfkZQCAAAAAACA35GUAgAAAAAAgN+RlAIAAAAAAIDfkZQCAMADzz//vNq1ayer1erzdb377rsymUzau3evz9dVlbn6nPv27au+ffsGrEzwr2nTpslkMgW6GAGxd+9emUwmvfvuu/ZxkydPVvfu3QNXKABAlUFSCgAAN504cUIzZ87Uo48+qoiI0D+FPvfcc1q2bFmgi+E3VS1e+MeHH36o2bNn+3w9b7zxhkPiyJf+9re/aePGjfr888/9sj4AQNUV+lfUAAD4yTvvvKOioiINHz480EXxiqqWpPEk3q+//lpff/21bwuEsBCOSanGjRvrhhtu0IsvvuiX9QEAqi6SUgAAuGnBggUaMmSIYmNjy5yuqKhIhYWFfiqVf5w6dSrQRfCr6OhoRUdHe2VZVqtV+fn5XlmWt1S17QnPDRs2TGvWrNGePXsCXRQAQBgjKQUAgBvS09O1adMm9e/f32G8rT+WF198UbNnz1br1q0VExOjrVu3SpK2bdumm2++WfXq1VNsbKy6du3qsknMb7/9pn79+ql69eo6//zz9cwzz/i03yqTyaRTp07pvffek8lkkslk0qhRoySd7V9n69atuv3221W3bl316tVLUul9LY0aNUotWrRwGGe1WjV79mx16NBBsbGxatSokcaNG6esrCy3yvjtt9+qd+/eqlmzpuLj43XDDTcoLS2t3PWWjMGdeF1xFWdBQYGefPJJtWnTRjExMWrWrJkmTZqkgoICh+lMJpMmTJighQsXqkOHDoqJidHy5cslSYsWLVKXLl1Uu3ZtxcXF6eKLL9Yrr7zi1ufhyscff6wuXbqoevXqql+/vu6880798ccfDtOMGjVKtWrV0u7duzVo0CDVrl1bd9xxhyTpxx9/1C233KLmzZvbY3rooYeUl5fnchl//PGHhg4dqlq1aqlBgwZ65JFHZLFYHKY9evSo/vKXvyguLk7x8fEaOXKkNm7c6NRvkeT+98Ob1qxZo8suu0yxsbFq3bq15s2bV+q0H3zwgf3zrVevnm677Tbt37/f/n7fvn31xRdfaN++ffb9quT+6O4+Y1tXt27dVKNGDdWtW1dXXnmlvbZeixYt9Ntvv+n777+3r6fk/nn8+HH97W9/U7NmzRQTE6M2bdpo5syZTseQ48ePa9SoUapTp4592xw/ftxl7LZj3WeffVbeRwoAQIVFBboAAACEgp9++kmS1LlzZ5fvL1iwQPn5+Ro7dqxiYmJUr149/fbbb+rZs6eaNm2qyZMnq2bNmvroo480dOhQLVmyRH/+858lSQcPHtRVV12loqIi+3Tz589X9erVfRbP+++/r9GjR6tbt24aO3asJKl169YO09xyyy268MIL9dxzz8kwDI/XMW7cOL377ru666679MADDyg9PV2vvfaaUlNTtXbtWlWrVq3Ueb/55htde+21atWqlaZNm6a8vDzNmTNHPXv21IYNG1wmoiobb1msVquGDBmiNWvWaOzYsUpKStLmzZv18ssva8eOHU7NAr/99lt99NFHmjBhgurXr68WLVpo5cqVGj58uK6++mrNnDlTkpSWlqa1a9fqwQcf9CgeSfbP9rLLLtOMGTN06NAhvfLKK1q7dq1SU1MVHx9vn7aoqEgDBw5Ur1699OKLL6pGjRqSipNaubm5uvfee3Xeeefpl19+0Zw5c/T777/r448/dlifxWLRwIED1b17d7344ov65ptv9NJLL6l169a699577Z/T4MGD9csvv+jee+9Vu3bt9Nlnn2nkyJFO5Xf3++FNmzdv1oABA9SgQQNNmzZNRUVFevLJJ9WoUSOnaZ999llNnTpVw4YN0+jRo3XkyBHNmTNHV155pf3zffzxx5Wdna3ff/9dL7/8siSpVq1a9s/C3X1m+vTpmjZtmq644go99dRTio6O1v/+9z99++23GjBggGbPnq2//vWvqlWrlh5//HFJspc5NzdXffr00R9//KFx48apefPm+umnn/TYY48pIyPD3rTQMAzdcMMNWrNmjcaPH6+kpCR9+umnLreNJNWpU0etW7fW2rVr9dBDD3lrEwAA4MgAAADlmjJliiHJOHnypMP49PR0Q5IRFxdnHD582OG9q6++2rj44ouN/Px8+zir1WpcccUVxoUXXmgf97e//c2QZPzvf/+zjzt8+LBRp04dQ5KRnp7uk5hq1qxpjBw50mn8k08+aUgyhg8f7vRenz59jD59+jiNHzlypHHBBRfY///xxx8NScbChQsdplu+fLnL8efq2LGj0bBhQ+Po0aP2cRs3bjQiIiKMESNGlLrec2MoqbR4FyxY4PQ5nxvn+++/b0RERBg//vijw7xz5841JBlr1661j5NkREREGL/99pvDtA8++KARFxdnFBUVlRW6WwoLC42GDRsaF110kZGXl2cf/9///teQZDzxxBP2cSNHjjQkGZMn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nsjISA0cOFDLli2T2Wy2j09LS9OKFSscpr3xxhsVGRmp6dOnO9UMNAzDYVvWrFnTZRNad/eZoUOHKiIiQk899ZRT7bSS6y5t/x02bJjWrVvnFIMkHT9+XEVFRZKKjzNFRUV688037e9bLBbNmTPHaT6puPnm7t27dcUVV7h8HwAAb6CmFAAAbmjVqpUuuugiffPNN7r77rvdmuf1119Xr169dPHFF2vMmDFq1aqVDh06pHXr1un333/Xxo0bJUmTJk3S+++/rz/96U968MEHVbNmTc2fP18XXHCBQ7LF27p06aJvvvlGs2bNUpMmTdSyZUuXfVuVdPfdd2vWrFkaOHCg7rnnHh0+fFhz585Vhw4ddOLECft0ffr00bhx4zRjxgz9+uuvGjBggKpVq6adO3fq448/1iuvvKKbb7651PW88MILuvbaa9WjRw/dc889ysvL05w5c1SnTh1NmzbNPt1tt92mRx99VH/+85/1wAMPKDc3V2+++abatm2rDRs2VDpem7/85S/66KOPNH78eH333Xfq2bOnLBaLtm3bpo8++kgrVqxQ165dy1zG6NGjdezYMfXr10/nn3++9u3bpzlz5qhjx45KSkqyT9eiRQtJKrMZWrVq1TRz5kzddddd6tOnj4YPH65Dhw7plVdeUYsWLfTQQw+VG1O7du3UunVrPfLII/rjjz8UFxenJUuWOPUt5YmhQ4eqW7duevjhh7Vr1y61a9dOn3/+uY4dOybJsbaPu98Pb5o+fbqWL1+u3r1767777lNRUZHmzJmjDh06OHzXWrdurWeeeUaPPfaY9u7dq6FDh6p27dpKT0/Xp59+qrFjx+qRRx6RVLxfLV68WBMnTtRll12mWrVqafDgwW7vM23atNHjjz+up59+Wr1799aNN96omJgYrV+/Xk2aNNGMGTPs63nzzTf1zDPPqE2bNmrYsKH69eunv//97/r88891/fXXa9SoUerSpYtOnTqlzZs365NPPtHevXtVv359DR48WD179tTkyZO1d+9etW/fXkuXLi21T7pvvvlGhmHohhtu8Pp2AADALiDP/AMAIATNmjXLqFWrlpGbm2sfl56ebkgyXnjhBZfz7N692xgxYoTRuHFjo1q1akbTpk2N66+/3vjkk08cptu0aZPRp08fIzY21mjatKnx9NNPG//6178MSUZ6erpP4tm2bZtx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p+666y4tXbrUPs3cuXPVsmVLvfTSS0pKStKECRN088036+WXXy4ztiVLlqhDhw6KiYlRixYt9NJLLzm8bzKZtGzZModx8fHx9nhbtmwpSerUqZNMJpP69u3rcj3nNt8rKCjQI488oqZNm6pmzZrq3r27Vq9ebX/f1nzz888/V/v27RUTEyOz2azVq1erW7duqlmzpuLj49WzZ0/t27fPPt/gwYP1+eefKy8vr8y4K4OkFAAAAAAAfvT444/rpZdeUnJysqKionT33Xc7vL9r1y4tWbJES5cutTfluuWWW3T48GF99dVXSklJUefOnXX11Vfr2LFjkqQ77rhD559/vtavX6+UlBRNnjxZ1apVsy8zNzdXL774ot5//3398MMPMpvNeuSRR+zvv/LKK3rppZf04osvatOmTRo4cKCGDBminTt3uoxh//79uvHGGzV48GD9+uuvGj16tCZPnlxu7D/++KO6du1q///WW2/Vww8/rA4dOigjI0MZGRm69dZbZbVadcMNN+jYsWP6/vvvtXLlSu3Zs0e33nqr02f10Ucf6f/+7/+0fPlypaam6r777iu3HCVlZ2c71D5at26d+vfv7zDNwIEDtW7dulKXkZKSomHDhum2227T5s2bNW3aNE2dOtWjZn+//PKLJOmbb75RRkaGQ6KsLBMmTNC6deu0aNEibdq0Sbfccov+9Kc/OWy73NxczZw5U2+//bZ+++031atXT0OHDlWfPn20adMmrVu3TmPHjnV4ol7Xrl1VVFSk//3vf27H4Cma7wEAAAAAQsOsWcWv8nTuLH3+ueO4IUOkDRvKn3fixOKXDz377LPq06ePJGny5Mm67rrrlJ+fb689VFhYqH//+99q0KCBpOImb7/88osOHz6smJgYSdKLL76oZcuW6ZNPPtHYsWNlNpv197//Xe3atZMkXXjhhQ7rPH36tObOnavWrVtLKk5kPPXUU/b3X3zxRT366KO67bbbJEkzZ87Ud999p9mzZ+v11193iuHNN99U69at7bWBEhMTtXnzZs2cObPM2Pft26cmTZrY/69evbpq1aqlqKgoNW7c2D5+5cqV2rx5s9LT09WsWTNJ0r///W916NBB69ev12WXXSZJys/P17///W81bdpUkjRnzhxdd911eumllxyWV5qffvpJixcv1hdffGEfd/DgQTVq1MhhukaNGunEiRPKy8tT9erVnZYza9YsXX311Zo6daokqW3bttq6dateeOEFjRo1qtxySLJv7/POO8+tskvFtckWLFggs9ls/1wfeeQRLV++XAsWLNBzzz0nqXj7v/HGG7r00kslSceOHVN2drauv/56+z6RlJTksOwaNWqoTp06DrWnvI2kFAAAAAAgNJw4If3xR/nTnUliODhyxL15T5zwvFweuuSSS+zDCQkJkqTDhw+refPmkqQLLrjAnqCQipvm5eTk6LzzznNYTl5ennbv3i1JmjhxokaPHq33339f/fv31y233GJPNkjFCYaS/yckJOjw4cOSpBMnTujAgQPq2bOnw/J79uzp0ESwpLS0NHXv3t1hXI8ePcqNPS8vz558K0taWpqaNWtmT0hJUvv27RUfH6+0tDR7Uqp58+b2hJStDFarVdu3by83sbNlyxbdcMMNevLJJzVgwIByy1ReeW+44QaHcT179tTs2bNlsVjsnbp72+bNm2WxWNS2bVuH8QUFBQ77S3R0tMN+V69ePY0aNUoDBw7UNddco/79+2vYsGH2/dGmevXqys3N9UnZJZJSAAAAAIBQERcnlUhAlKpEQsdhnDvzxsV5Xi4PlWxWZ2suZbVa7eNq1qzpMH1OTo4SEhIc+gmyiY+Pl1Tcv9Ltt9+uL774Ql999ZWefPJJLVq0SH/+85+d1mlbr2EY3gjHI/Xr11dWVpbf13uurVu36uqrr9bYsWM1ZcoUh/caN27s0HeVJB06dEhxcXEua0m5y9Vnfvr06QovTyreNyIjI5WSkuKU+CrZj1n16tUdmuZJ0oIFC/TAAw9o+fLlWrx4saZMmaKVK1fq8ssvt09z7NgxhwSpt5GUAgAAAACEhso0rTu3OV8I6dy5sw4ePKioqCi1aNGi1Onatm2rtm3b6qGHHtLw4cO1YMECe1KqLHFxcWrSpInWrl1rb1YoSWvXrlW3bt1czpOUlKTPz/lMf/7553LX1alTJ23dutVhXHR0tCwWi9Py9+/fr/3799trS23dulXHjx9X+/bt7dOZzWYdOHDA3nTt559/VkREhBITE0stw2+//aZ+/fpp5MiRevbZZ53e79Gjh7788kuHcStXriyzJlhSUpLWrl3rMG7t2rVq27atPVnUoEEDh87ld+7c6VALyfaUxXM/i7J06tRJFotFhw8frtBT8jp16qROnTrpscceU48ePfThhx/ak1K7d+9Wfn6+OnXq5PFy3UVH5wAAAAAABLH+/furR48eGjp0qL7++mvt3btXP/30kx5//HElJycrLy9PEyZM0OrVq7Vv3z6tXbtW69evd+ojqCx///vfNXPmTC1evFjbt2/X5MmT9euvv+rBBx90Of348eO1c+dO/f3vf9f27dv14YcfutWp98CBA7VmzRqHcS1atFB6erp+/fVXZWZmqqCgQP3799fFF1+sO+64Qxs2bNAvv/yiESNGqE+fPg4dpcfGxmrkyJHauHGjfvzxRz3wwAMaNmxYqU33tmzZoquuukoDBgzQxIkTdfDgQR08eFBHjhxxiG3Pnj2aNGmStm3bpjfeeEMfffSRHnrooVLjevjhh7Vq1So9/fTT2rFjh9577z299tprDp3J9+vXT6+99ppSU1OVnJys8ePHO9Rga9iwoapXr67ly5fr0KFDZT5F0KZt27a64447NGLECC1dulTp6en65ZdfNGPGDId+ss6Vnp6uxx57TOvWrdO+ffv09ddfa+fOnQ77zI8//qhWrVo5NPv0NpJSAAD4S0aGNG1a8V8AAAA3mUwmffnll7ryyit11113qW3btrrtttu0b98+NWrUSJGRkTp69KhGjBihtm3batiwYbr22ms1ffp0t9fxwAMPaOLEiXr44Yd18cUXa/ny5fr888+dOky3ad68uZYsWaJly5bp0ksv1dy5c+2dapfljjvu0G+//abt27fbx910003605/+pKuuukoNGjTQf/7zH5lMJn322WeqW7eurrzySvXv31+tWrXS4sWLHZbXpk0b3XjjjRo0aJAGDBigSy65RG+88Uap6//kk0905MgRffDBB0pISLC/bH1USVLLli31xRdfaOXKlbr00kv10ksv6e2339bAgQNLXW7nzp310UcfadGiRbrooov0xBNP6KmnnnLo5Pyll15Ss2bN1Lt3b91+++165JFHVKNGDfv7UVFRevXVVzVv3jw1adLEqY+q0ixYsEAjRozQww8/rMTERA0dOlTr16+391HmSo0aNbRt2zbddNNNatu2rcaOHav7779f48aNs0/zn//8R2PGjHGrDBVlMgLRiDSATpw4oTp16ig7O1txfmgrDACA3YYNUpcuUkpK8VOBAACAk/z8fKWnp6tly5ZudYiN0PP3v/9dJ06c0Lx58yq1nGnTpmnZ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Dxd79uwRv/zyi2jVqpUYOnSoN7JEVCXDhw8XISEh4vvvvxfHjx8X33zzjWjUqJF4++235XlYJuhOd+XKFXHgwAFx4MABAUAsXLhQHDhwQPz5559CCPeUgeLiYhEUFCReeuklcfjwYbF27Vrh5+cnEhISPJ7f2opBqVpqyZIlIjQ0VBgMBtGtWzexe/dubyeJqFoAUPx88cUX8jzXrl0To0ePFvXr1xd+fn7i2WefFYWFhRbLOXHihOjXr5/w9fUVjRo1EuPHjxelpaUezg1R9bAOSrFM0N3ou+++Ex06dBBGo1G0bdtWJCYmWkwvLy8X06ZNE0FBQcJoNIonn3xS5OXlWcxz4cIFMXToUFGnTh0REBAgRowYIa5cueLJbBC5xeXLl0V8fLwIDQ0VPj4+omXLluLdd9+1eG09ywTd6bZv3654HTF8+HAhhPvKQHZ2tujZs6cwGo0iJCREzJ0711NZvCNIQgjhnTZaRERERERERER0t2KfUkRERERERERE5HEMShERERERERERkccxKEVERERERERERB7HoBQREREREREREXkcg1JERERERERERORxDEoREREREREREZHHMShFREREREREREQex6AUERERERERERF5HINSREREVCs1b94cixYt8nYy3CYtLQ2SJKG4uNjbSak2jz/+OMaOHevtZBAREVENwaAUERER1SgnT57EyJEj0aRJExgMBjRr1gzx8fG4cOGCt5PmNkrBmUcffRSFhYWoV6+edxJFRERE5GEMShEREVGN8ccff6Br167Iz8/HmjVrcOzYMaxYsQKpqamIiIjAxYsXvZa2srIylJeXV9vyDQYDgoODIUlStf3Gnai6twsRERFVHwaliIiIqMaIi4uDwWDAzz//jF69eiE0NBT9+vXD1q1bcerUKbz77rsW81+5cgVDhw6Fv78/QkJCsGzZMnmaEAIzZ85EaGgojEYjmjRpgjfffFOefuPGDUyYMAEhISHw9/dH9+7dkZaWJk9ftWoVAgMD8e2336J9+/YwGo1YuXIlfHx8bB6xi4+PR+/evQEAFy5cwNChQxESEgI/Pz+EhYVhzZo18rwvv/wy0tPTsXjxYkiSBEmScOLECcXH9zZu3IgHH3wQRqMRzZs3x4IFCyx+t3nz5vjwww8xcuRI1K1bF6GhoUhMTHS4jh9//HG8+eabePvtt9GgQQMEBwdj5syZ8vQTJ05AkiQcPHhQHldcXAxJkuT1Y0rrli1bEB4eDl9fX/Tu3Rtnz57F5s2b0a5dOwQEBOCFF17A1atXLX7/5s2bGDNmDOrVq4dGjRph2rRpEEJUabsUFBQ4zDMRERHVTAxKERERUY1w8eJFbNmyBaNHj4avr6/FtODgYMTGxmLdunUWAYz58+ejU6dOOHDgACZPnoz4+HikpKQAuBXQ+fjjj5GQkID8/Hxs2rQJYWFh8nfHjBmDjIwMrF27Fjk5ORg8eDD69u2L/Px8eZ6rV6/io48+wsqVK3HkyBHExsYiMDAQGzdulOcpKyvDunXrEBsbCwC4fv06unTpgh9++AGHDx/GqFGj8NJLL2Hv3r0AgMWLFyMiIgKvvvoqCgsLUVhYiKZNm9qsj6ysLMTExOD555/HoUOHMHPmTEybNg2rVq2ymG/BggXo2rUrDhw4gNGjR+ONN95AXl6ew3X95Zdfwt/fH3v27MG8efPw/vvvy+vNFTNnzsTSpUuxa9cunDx5EjExMVi0aBFWr16NH374AT///DOWLFli89s6nQ579+7F4sWLsXDhQqxcuVKeXpnt0rhxY5fTTkRERDWAICIiIqoBdu/eLQCI5ORkxekLFy4UAMSZM2eEEEI0a9ZM9O3b12KeIUOGiH79+gkhhFiwYIFo3bq1KCkpsVnWn3/+KbRarTh16pTF+CeffFJMmTJFCCHEF198IQCIgwcPWswTHx8vevfuLf+/ZcsWYTQaRVFRkd28RUVFifHjx8v/9+rVS8THx1vMs337dgFAXs4LL7wgnnrqKYt5Jk6cKNq3by//36xZM/Hiiy/K/5eXl4vGjRuL5cuX201Lr169RM+ePS3GPfzww2LSpElCCCGOHz8uAIgDBw7I04uKigQAsX37dou0bt26VZ5nzpw5AoD4/fff5XGvvfaaiIyMtPjtdu3aifLycnncpEmTRLt27YQQVdsuREREVPuwpRQRERHVKMKsJZQzERERNv/n5uYCAAYPHoxr166hZcuWePXVV5GcnIybN28CAA4dOoSysjK0bt0aderUkT/p6en4/fff5eUZDAZ07NjR4jdiY2ORlpaGv/76CwCQlJSEqKgoBAYGArjVcmrWrFkICwtDgwYNUKdOHWzZssXlR8xyc3PRo0cPi3E9evRAfn4+ysrK5HHm6ZMkCcHBwTh79qzDZVvn6d5773X6HWfLCQoKgp+fH1q2bGkxznq5jzzyiEW/WREREXKeqrJdiIiIqPbReTsBRERERADwwAMPQJIk5Obm4tlnn7WZnpubi/r16+Oee+5RtbymTZsiLy8PW7duRUpKCkaPHo358+cjPT0df//9N7RaLbKysqDVai2+V6dOHXnY19fXpuPxhx9+GPfffz/Wrl2LN954A8nJyRaP1M2fPx+LFy/GokWLEBYWBn9/f4wdOxYlJSUurA319Hq9xf+SJDnt+NvRdzSaW/cszYODpaWlTpcjSVKl0mKuKtuFiIiIah8GpYiIiKhGaNiwIZ566il8+umneOuttyz6lTp9+jSSkpIwbNgwi2DE7t27LZaxe/dutGvXTv7f19cX/fv3R//+/REXF4e2bdvi0KFDCA8PR1lZGc6ePYvHHnvM5bTGxsYiKSkJ9913HzQaDaKiouRpO3fuxIABA/Diiy8CAMrLy3H06FG0b99ensdgMFi0dlLSrl077Ny502Lczp070bp1a5uAjTuZgn6FhYUIDw8HAItOz6tqz549Fv/v3r0brVq1glarrfJ2ISIiotqFj+8RERFRjbF06VLcuHEDkZGR2LFjB06ePImffvoJTz31FEJCQjB79myL+Xfu3Il58+bh6NGjWLZsGdavX4/4+HgAt97S9t///heHDx/GH3/8ga+++gq+vr5o1qwZWrdujdjYWAwbNgzffPMNjh8/jr1792LOnDn44YcfnKYzNjYW+/fvx+zZs/Hcc8/BaDTK01q1aoWUlBTs2rULubm5eO2113DmzBmL7zdv3hx79uzBiRMncP78ecXWROPHj0dqaipmzZqFo0eP4ssvv8TSpUsxYcKEyqxa1Xx9ffHII49g7ty5yM3NRXp6OqZOneq25RcUFGDcuHHIy8vDmjVrsGTJEnmbVXW7EBERUe3CoBQRERHVGK1atcK+ffvQsmVLxMTE4P7778eoUaPwxBNPICMjAw0aNLCYf/z48di3bx/Cw8PxwQcfYOHChYiMjAQABAYG4rPPPkOPHj3QsWNHbN26Fd999x0aNmwIAPjiiy8wbNgwjB8/Hm3atMHAgQORmZmJ0NBQp+l84IEH0K1bN+Tk5Mhv3TOZOnUqOnfujMjISDz++OMIDg7GwIEDLeaZMGECtFot2rdvj3vuuUexv6nOnTvj66+/xtq1a9GhQwdMnz4d77//Pl5++WUX1mjlfP7557h58ya6dOmCsWPH4oMPPnDbsocNG4Zr166hW7duiIuLQ3x8PEaNGiVPr8p2ISIiotpFEq70JkpEREREREREROQGbClFREREREREREQex6AUERERERERERF5HINSRERERERERETkcQxKERERERERERGRxzEoRUREREREREREHsegFBEREREREREReRyDUkRERERERERE5HEMShERERERERERkccxKEVERERERERERB7HoBQREREREREREXkcg1JERERERERERORxDEoREREREREREZHH/T82Y7ZqxKUkgwAAAABJRU5ErkJggg==\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "================================================================================\n",
            "PROGRAM EXECUTION TIMING SUMMARY\n",
            "================================================================================\n",
            "Program started:  2026-05-25 02:18:33\n",
            "Program ended:    2026-05-25 02:18:54\n",
            "Total runtime:    21.10 seconds (0.35 minutes)\n",
            "================================================================================\n",
            "\n",
            "============================================================\n",
            "CONCLUSIONS:\n",
            "============================================================\n",
            "✓ Outliers created by replacing extreme values with average value (42.47 MPa)\n",
            "  - Type H→A: 10 observations (were the highest strength, became average)\n",
            "  - Type L→A: 10 observations (were the lowest strength, became average)\n",
            "✓ Total outliers: 20 (1.942% of data)\n",
            "✓ ALL methods return exactly 20 outliers (top-K approach)\n",
            "✓ These are structural outliers that violate the relationship between features and target\n",
            "✓ NNFA successfully detects outliers because it models the relationship y = f(X)\n",
            "✓ Methods using prediction error can also detect outliers\n",
            "✓ PyOD methods (input features only) DO NOT detect structural outliers\n",
            "\n",
            "📊 Fastest method: Combined (RF+AE) (0.00s)\n",
            "📊 Slowest method: NNFA (6.18s)\n",
            "\n",
            "============================================================\n",
            "TESTING COMPLETED.\n",
            "============================================================\n"
          ]
        }
      ],
      "source": [
        "# *******************************************************\n",
        "#      Concrete_Strength_(1%_Outliers) with Timing\n",
        "# *******************************************************\n",
        "import sys\n",
        "!{sys.executable} -m pip install pyod\n",
        "\n",
        "import time\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import math\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.optim as optim\n",
        "import matplotlib.pyplot as plt\n",
        "from sklearn.preprocessing import StandardScaler\n",
        "from sklearn.metrics import precision_score, recall_score, f1_score\n",
        "from scipy import stats\n",
        "import warnings\n",
        "warnings.filterwarnings('ignore')\n",
        "\n",
        "# PyOD detectors (work only with input features)\n",
        "from pyod.models import abod, hbos, iforest, knn, lof, ocsvm, pca, copod\n",
        "\n",
        "# Detectors that can take the target variable into account\n",
        "from sklearn.ensemble import IsolationForest as SklearnIForest\n",
        "from sklearn.svm import OneClassSVM\n",
        "from sklearn.covariance import EllipticEnvelope\n",
        "from sklearn.neighbors import LocalOutlierFactor\n",
        "from sklearn.ensemble import RandomForestRegressor\n",
        "from sklearn.neural_network import MLPRegressor\n",
        "\n",
        "# Autoencoder for outlier detection (PyTorch)\n",
        "import torch.nn.functional as F\n",
        "\n",
        "# ==========================================================\n",
        "# START TIMER FOR ENTIRE PROGRAM\n",
        "# ==========================================================\n",
        "program_start_time = time.time()\n",
        "program_start_str = time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime(program_start_time))\n",
        "\n",
        "print(\"=\"*80)\n",
        "print(\"CONCRETE STRENGTH DATASET - OUTLIER DETECTION COMPARISON (WITH TIMING)\")\n",
        "print(\"=\"*80)\n",
        "print(f\"Program started at: {program_start_str}\\n\")\n",
        "\n",
        "# ==========================================================\n",
        "# PART 1: CREATING THE \"CONCRETE STRENGTH\" DATASET WITH OUTLIERS\n",
        "# ==========================================================\n",
        "print(\"=\"*60)\n",
        "print(\"CREATING 'CONCRETE STRENGTH' DATASET WITH OUTLIERS\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "# Loading the original dataset from UCI Machine Learning Repository\n",
        "start_load = time.time()\n",
        "url = \"https://archive.ics.uci.edu/ml/machine-learning-databases/concrete/compressive/Concrete_Data.xls\"\n",
        "df = pd.read_excel(url)\n",
        "load_time = time.time() - start_load\n",
        "print(f\"Dataset loading time: {load_time:.2f} seconds\")\n",
        "\n",
        "# Assigning meaningful column names\n",
        "df.columns = [\n",
        "    'Cement', 'BlastFurnaceSlag', 'FlyAsh', 'Water',\n",
        "    'Superplasticizer', 'CoarseAggregate', 'FineAggregate',\n",
        "    'Age', 'CompressiveStrength'\n",
        "]\n",
        "\n",
        "print(\"Original dataset loaded.\")\n",
        "print(f\"Size: {df.shape[0]} rows, {df.shape[1]} columns\")\n",
        "print(f\"Strength range: {df['CompressiveStrength'].min():.2f} – {df['CompressiveStrength'].max():.2f} MPa\\n\")\n",
        "\n",
        "# Step 1: Find maximum and minimum strength values\n",
        "Smax = df['CompressiveStrength'].max()\n",
        "Smin = df['CompressiveStrength'].min()\n",
        "\n",
        "# Calculate average value\n",
        "Savg = (Smax + Smin) / 2\n",
        "\n",
        "print(\"=\"*60)\n",
        "print(\"STEP 1: FIND MAXIMUM, MINIMUM AND AVERAGE STRENGTH VALUES:\")\n",
        "print(\"=\"*60)\n",
        "print(f\"Maximum strength (Smax): {Smax:.2f} MPa\")\n",
        "print(f\"Minimum strength (Smin): {Smin:.2f} MPa\")\n",
        "print(f\"Average strength (Savg): {Savg:.2f} MPa\\n\")\n",
        "\n",
        "# Step 2: Sort dataset in descending order by CompressiveStrength\n",
        "df_sorted = df.sort_values('CompressiveStrength', ascending=False).reset_index(drop=True)\n",
        "\n",
        "print(\"=\"*60)\n",
        "print(\"STEP 2: SORT DATASET IN DESCENDING ORDER BY STRENGTH\")\n",
        "print(\"=\"*60)\n",
        "print(f\"Sorted dataset created (descending order).\\n\")\n",
        "\n",
        "# Steps 3 & 4: Replace values with average (Savg) for 20 outliers (10 highest + 10 lowest)\n",
        "n_outliers_per_group = 10\n",
        "total_outliers = n_outliers_per_group * 2  # = 20\n",
        "print(\"=\"*60)\n",
        "print(f\"STEPS 3 & 4: REPLACE STRENGTH VALUES WITH AVERAGE (CREATING {total_outliers} OUTLIERS)\")\n",
        "print(\"=\"*60)\n",
        "print(f\"  • First {n_outliers_per_group} observations (highest strength): replaced with Savg ({Savg:.2f} MPa)\")\n",
        "print(f\"  • Last {n_outliers_per_group} observations (lowest strength): replaced with Savg ({Savg:.2f} MPa)\")\n",
        "print()\n",
        "\n",
        "df_labeled = df_sorted.copy()\n",
        "outlier_indices = []\n",
        "\n",
        "# Replace first n_outliers_per_group rows (highest strength) with Savg\n",
        "print(f\"🔴 REPLACING FIRST {n_outliers_per_group} OBSERVATIONS (HIGHEST STRENGTH) WITH AVERAGE:\")\n",
        "for i in range(n_outliers_per_group):\n",
        "    original_strength = df_labeled.loc[i, 'CompressiveStrength']\n",
        "    df_labeled.loc[i, 'CompressiveStrength'] = Savg\n",
        "    outlier_indices.append(i)\n",
        "    if i < 5:\n",
        "        print(f\"\\n   Outlier #{i+1}:\")\n",
        "        print(f\"   Row index: {i}\")\n",
        "        print(f\"   📊 Changes:\")\n",
        "        print(f\"      • CompressiveStrength: {original_strength:.2f} → {Savg:.2f} MPa (replaced with average value)\")\n",
        "        print(f\"   ✅ Other features unchanged\")\n",
        "    elif i == 5:\n",
        "        print(f\"\\n   ... and {n_outliers_per_group-5} more replacements (rows 6-{n_outliers_per_group})\")\n",
        "\n",
        "print(f\"\\n✅ First {n_outliers_per_group} observations replaced with Savg\")\n",
        "\n",
        "# Replace last n_outliers_per_group rows (lowest strength) with Savg\n",
        "print(f\"\\n🔴 REPLACING LAST {n_outliers_per_group} OBSERVATIONS (LOWEST STRENGTH) WITH AVERAGE:\")\n",
        "last_n_start = len(df_labeled) - n_outliers_per_group\n",
        "for i in range(last_n_start, len(df_labeled)):\n",
        "    original_strength = df_labeled.loc[i, 'CompressiveStrength']\n",
        "    df_labeled.loc[i, 'CompressiveStrength'] = Savg\n",
        "    outlier_indices.append(i)\n",
        "    if i < last_n_start + 5:\n",
        "        print(f\"\\n   Outlier #{i - last_n_start + n_outliers_per_group + 1}:\")\n",
        "        print(f\"   Row index: {i}\")\n",
        "        print(f\"   📊 Changes:\")\n",
        "        print(f\"      • CompressiveStrength: {original_strength:.2f} → {Savg:.2f} MPa (replaced with average value)\")\n",
        "        print(f\"   ✅ Other features unchanged\")\n",
        "    elif i == last_n_start + 5:\n",
        "        print(f\"\\n   ... and {n_outliers_per_group-5} more replacements (rows {last_n_start+6} to {len(df_labeled)-1})\")\n",
        "\n",
        "print(f\"\\n✅ Last {n_outliers_per_group} observations replaced with Savg\")\n",
        "\n",
        "# Step 5: Assign outlier status\n",
        "df_labeled['is_outlier'] = 0\n",
        "df_labeled.loc[outlier_indices, 'is_outlier'] = 1\n",
        "\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"STEP 5: ASSIGN OUTLIER STATUS\")\n",
        "print(\"=\"*60)\n",
        "print(f\"  • First {n_outliers_per_group} observations: marked as OUTLIERS\")\n",
        "print(f\"  • Last {n_outliers_per_group} observations: marked as OUTLIERS\")\n",
        "print(f\"  • Total outliers: {len(outlier_indices)}\")\n",
        "\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"FINAL STATISTICS OF THE CREATED DATASET:\")\n",
        "print(\"=\"*60)\n",
        "print(f\"  • Total observations: {len(df_labeled)}\")\n",
        "print(f\"  • Outliers: {df_labeled['is_outlier'].sum()} ({df_labeled['is_outlier'].mean()*100:.3f}%)\")\n",
        "print(f\"    - Type H→A (were the highest strength, became average): {n_outliers_per_group}\")\n",
        "print(f\"    - Type L→A (were the lowest strength, became average): {n_outliers_per_group}\")\n",
        "print(f\"  • Normal: {len(df_labeled) - df_labeled['is_outlier'].sum()}\")\n",
        "\n",
        "# Save for backup\n",
        "df_labeled.to_csv('concrete_labeled.csv', index=False)\n",
        "print(\"\\n✅ Dataset saved to 'concrete_labeled.csv'\")\n",
        "\n",
        "# ==========================================================\n",
        "# STEP 6: SHUFFLING DATA BEFORE TESTING\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"STEP 6: SHUFFLING DATA BEFORE TESTING\")\n",
        "print(\"=\"*60)\n",
        "df = df_labeled.sample(frac=1, random_state=42).reset_index(drop=True)\n",
        "print(\"✅ Dataset shuffled.\")\n",
        "print(f\"Dataset size: {len(df)} records\")\n",
        "print(f\"Outliers: {df['is_outlier'].sum()} ({df['is_outlier'].mean()*100:.2f}%)\")\n",
        "\n",
        "# ==========================================================\n",
        "# PART 2: TESTING OUTLIER DETECTION METHODS\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"TESTING OUTLIER DETECTION METHODS\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "# Separate features, target variable, and true labels\n",
        "X = df.drop(['CompressiveStrength', 'is_outlier'], axis=1).values.astype(np.float32)\n",
        "y = df['CompressiveStrength'].values.astype(np.float32).reshape(-1, 1)\n",
        "true_outliers = df['is_outlier'].values.astype(int)\n",
        "\n",
        "Q = X.shape[0]\n",
        "N_x = X.shape[1]      # 8 input features\n",
        "N_y = 1                # one output variable (concrete strength)\n",
        "\n",
        "print(f\"\\n📊 Number of features for detectors: {N_x}\")\n",
        "print(f\"📊 Outliers created by replacing extreme values with average (highest→average, lowest→average)\")\n",
        "print(f\"📊 Total outliers: {true_outliers.sum()} ({(true_outliers.sum()/Q)*100:.2f}% of data)\\n\")\n",
        "\n",
        "# ==========================================================\n",
        "# PARAMETERS - ALL METHODS WILL SEARCH FOR EXACTLY total_outliers OUTLIERS\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"OUTLIER DETECTION PARAMETER SETUP\")\n",
        "print(\"=\"*60)\n",
        "n_true_outliers = true_outliers.sum()\n",
        "print(f\"The dataset contains {n_true_outliers} true outliers (proportion {n_true_outliers/Q*100:.2f}%)\")\n",
        "\n",
        "# Use default values for NNFS\n",
        "Ksi = 0\n",
        "# All methods will be configured to detect exactly n_true_outliers outliers\n",
        "n_outliers_desired = n_true_outliers  # = 20\n",
        "contamination = n_outliers_desired / Q\n",
        "contamination_percent = contamination * 100\n",
        "\n",
        "print(f\"✅ ALL methods will search for exactly {n_outliers_desired} outliers ({contamination_percent:.3f}%)\")\n",
        "print(f\"✅ Parameter Ksi = {Ksi} (neuron search range)\")\n",
        "\n",
        "# ==========================================================\n",
        "# Helper function: Take exactly top-K outliers from scores\n",
        "# ==========================================================\n",
        "def get_top_k_outliers(scores, k):\n",
        "    \"\"\"Return binary array with 1 for top-k scores\"\"\"\n",
        "    outlier_indices = np.argsort(scores)[-k:]\n",
        "    outliers = np.zeros(len(scores), dtype=int)\n",
        "    outliers[outlier_indices] = 1\n",
        "    return outliers\n",
        "\n",
        "# ==========================================================\n",
        "# NNFA ALGORITHM (Neural Network Ensemble) - averaging errors over N\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"RUNNING NNFA ALGORITHM (AVERAGING OVER $N$)\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "def scale_to_minus1_1(data):\n",
        "    min_val = data.min(axis=0)\n",
        "    max_val = data.max(axis=0)\n",
        "    range_val = max_val - min_val\n",
        "    range_val[range_val == 0] = 1.0\n",
        "    scaled = 2.0 * (data - min_val) / range_val - 1.0\n",
        "    return scaled, min_val, max_val\n",
        "\n",
        "X_scaled_nnf, x_min, x_max = scale_to_minus1_1(X)\n",
        "y_scaled_nnf, y_min, y_max = scale_to_minus1_1(y)\n",
        "\n",
        "X_tensor = torch.tensor(X_scaled_nnf, dtype=torch.float32)\n",
        "y_tensor = torch.tensor(y_scaled_nnf, dtype=torch.float32)\n",
        "\n",
        "# Calculate hidden layer neuron count bounds\n",
        "log2q = math.log2(Q)\n",
        "N_min = (N_y * Q) / ((1 + log2q) * (N_x + N_y)) + 1\n",
        "N_max = (N_y / (N_x + N_y)) * ((Q / N_x + 1) * (N_x + N_y + 1) + 1) - 1\n",
        "\n",
        "# Limit maximum N value\n",
        "if N_max > Q:\n",
        "    N_max = min(Q // 2, 20)\n",
        "\n",
        "N_lim = N_min + Ksi * (N_max - N_min)\n",
        "N_start = max(1, int(np.ceil(N_min)))\n",
        "N_end = max(N_start, int(np.ceil(N_lim)))\n",
        "\n",
        "print(f\"Q = {Q}, N_x = {N_x}, N_y = {N_y}\")\n",
        "print(f\"N_min = {N_min:.4f}, N_max = {N_max:.4f}\")\n",
        "print(f\"Ksi = {Ksi}, N_lim = {N_lim:.4f}\")\n",
        "print(f\"Loop over $N$ from {N_start} to {N_end} inclusive\")\n",
        "print(\"This may take some time...\")\n",
        "\n",
        "# Store errors for each sample across all N values\n",
        "error_matrix = []\n",
        "torch.manual_seed(42)\n",
        "\n",
        "nnfa_start_time = time.time()\n",
        "for N in range(N_start, N_end + 1):\n",
        "    model = nn.Sequential(\n",
        "        nn.Linear(N_x, N),\n",
        "        nn.Tanh(),\n",
        "        nn.Linear(N, N_y),\n",
        "    )\n",
        "    criterion = nn.MSELoss()\n",
        "    optimizer = optim.Adam(model.parameters(), lr=0.01)\n",
        "\n",
        "    model.train()\n",
        "    for epoch in range(500):\n",
        "        optimizer.zero_grad()\n",
        "        outputs = model(X_tensor)\n",
        "        loss = criterion(outputs, y_tensor)\n",
        "        loss.backward()\n",
        "        optimizer.step()\n",
        "\n",
        "    model.eval()\n",
        "    with torch.no_grad():\n",
        "        predictions = model(X_tensor).numpy().flatten()\n",
        "        errors = (predictions - y_scaled_nnf.flatten()) ** 2\n",
        "    error_matrix.append(errors)\n",
        "    print(f\"  Completed N = {N}\")\n",
        "\n",
        "nnfa_end_time = time.time()\n",
        "nnfa_total_time = nnfa_end_time - nnfa_start_time\n",
        "\n",
        "# Convert to numpy array and average across N (rows)\n",
        "error_matrix = np.array(error_matrix)  # shape: (num_N, Q)\n",
        "E_avg_per_sample = np.mean(error_matrix, axis=0)  # shape: (Q,)\n",
        "\n",
        "# NNFA: take exactly n_outliers_desired most anomalous points (top-K)\n",
        "nnf_outlier_indices = np.argsort(E_avg_per_sample)[-n_outliers_desired:]\n",
        "nnf_pred = np.zeros(Q, dtype=int)\n",
        "nnf_pred[nnf_outlier_indices] = 1\n",
        "threshold = E_avg_per_sample[nnf_outlier_indices[0]] if len(nnf_outlier_indices) > 0 else np.inf\n",
        "\n",
        "print(\"\\n\" + \"=\"*50)\n",
        "print(\"NNFA RESULTS\")\n",
        "print(\"=\"*50)\n",
        "print(f\"Number of detected outliers: {nnf_pred.sum()} (expected {n_outliers_desired})\")\n",
        "print(f\"Threshold: {threshold:.6f}\")\n",
        "print(f\"NNFA execution time: {nnfa_total_time:.2f} seconds\")\n",
        "\n",
        "# ==========================================================\n",
        "# PREPARE DATA FOR DETECTORS\n",
        "# ==========================================================\n",
        "scaler = StandardScaler()\n",
        "X_scaled = scaler.fit_transform(X)\n",
        "\n",
        "# ==========================================================\n",
        "# 1. METHODS THAT ANALYZE ONLY INPUT FEATURES (PyOD)\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*80)\n",
        "print(\"METHODS ANALYZING ONLY INPUT FEATURES (PyOD)\")\n",
        "print(\"=\"*80)\n",
        "\n",
        "detectors_pyod = {\n",
        "    'ABOD (pyod)': abod.ABOD(),\n",
        "    'HBOS (pyod)': hbos.HBOS(),\n",
        "    'IsolationForest (pyod)': iforest.IForest(random_state=42),\n",
        "    'kNN (pyod)': knn.KNN(),\n",
        "    'LOF (pyod)': lof.LOF(),\n",
        "    'OCSVM (pyod)': ocsvm.OCSVM(),\n",
        "    'PCA (pyod)': pca.PCA(),\n",
        "    'COPOD (pyod)': copod.COPOD()\n",
        "}\n",
        "\n",
        "results_pyod = {}\n",
        "scores_pyod = {}\n",
        "method_times = {}\n",
        "\n",
        "for name, model in detectors_pyod.items():\n",
        "    start_time_method = time.time()\n",
        "    try:\n",
        "        model.fit(X_scaled)\n",
        "        scores = model.decision_scores_\n",
        "        results_pyod[name] = get_top_k_outliers(scores, n_outliers_desired)\n",
        "        scores_pyod[name] = scores\n",
        "        elapsed = time.time() - start_time_method\n",
        "        method_times[name] = elapsed\n",
        "        print(f\"  {name} successfully trained (time: {elapsed:.2f}s)\")\n",
        "    except Exception as e:\n",
        "        elapsed = time.time() - start_time_method\n",
        "        method_times[name] = elapsed\n",
        "        print(f\"  Error training {name}: {e} (time: {elapsed:.2f}s)\")\n",
        "        results_pyod[name] = np.zeros(Q, dtype=int) - 1\n",
        "        scores_pyod[name] = np.zeros(Q) - 1\n",
        "\n",
        "# ==========================================================\n",
        "# 2. METHODS THAT CAN TAKE THE TARGET VARIABLE INTO ACCOUNT\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*80)\n",
        "print(\"METHODS ANALYZING INPUT FEATURES + TARGET VARIABLE\")\n",
        "print(\"=\"*80)\n",
        "\n",
        "# 2.1. Random Forest (prediction error)\n",
        "print(\"\\n1. Random Forest Regressor...\")\n",
        "start_time_method = time.time()\n",
        "rf = RandomForestRegressor(n_estimators=100, random_state=42)\n",
        "rf.fit(X_scaled_nnf, y.ravel())\n",
        "rf_errors = (rf.predict(X_scaled_nnf) - y.ravel()) ** 2\n",
        "rf_pred = get_top_k_outliers(rf_errors, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['Random Forest (error)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# 2.2. Neural Network (prediction error)\n",
        "print(\"2. Neural Network Regressor...\")\n",
        "start_time_method = time.time()\n",
        "mlp = MLPRegressor(hidden_layer_sizes=(20, 10), random_state=42, max_iter=500)\n",
        "mlp.fit(X_scaled_nnf, y.ravel())\n",
        "mlp_errors = (mlp.predict(X_scaled_nnf) - y.ravel()) ** 2\n",
        "mlp_pred = get_top_k_outliers(mlp_errors, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['Neural Network (error)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# 2.3. Autoencoder on PyTorch (input feature reconstruction error)\n",
        "print(\"3. Autoencoder (input feature reconstruction error)...\")\n",
        "start_time_method = time.time()\n",
        "class Autoencoder(nn.Module):\n",
        "    def __init__(self, input_dim, encoding_dim=4):\n",
        "        super(Autoencoder, self).__init__()\n",
        "        self.encoder = nn.Sequential(\n",
        "            nn.Linear(input_dim, 16),\n",
        "            nn.ReLU(),\n",
        "            nn.Linear(16, encoding_dim)\n",
        "        )\n",
        "        self.decoder = nn.Sequential(\n",
        "            nn.Linear(encoding_dim, 16),\n",
        "            nn.ReLU(),\n",
        "            nn.Linear(16, input_dim)\n",
        "        )\n",
        "\n",
        "    def forward(self, x):\n",
        "        encoded = self.encoder(x)\n",
        "        decoded = self.decoder(encoded)\n",
        "        return decoded\n",
        "\n",
        "X_tensor_ae = torch.tensor(X_scaled, dtype=torch.float32)\n",
        "ae = Autoencoder(input_dim=X_scaled.shape[1])\n",
        "optimizer_ae = optim.Adam(ae.parameters(), lr=0.01)\n",
        "criterion_ae = nn.MSELoss()\n",
        "\n",
        "ae.train()\n",
        "for epoch in range(300):\n",
        "    optimizer_ae.zero_grad()\n",
        "    reconstructed = ae(X_tensor_ae)\n",
        "    loss = criterion_ae(reconstructed, X_tensor_ae)\n",
        "    loss.backward()\n",
        "    optimizer_ae.step()\n",
        "    if (epoch+1) % 50 == 0:\n",
        "        print(f\"    Autoencoder: epoch {epoch+1}/300, loss = {loss.item():.6f}\")\n",
        "\n",
        "ae.eval()\n",
        "with torch.no_grad():\n",
        "    reconstructed = ae(X_tensor_ae)\n",
        "    ae_errors = torch.mean((reconstructed - X_tensor_ae) ** 2, dim=1).numpy()\n",
        "ae_pred = get_top_k_outliers(ae_errors, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['Autoencoder (reconstruction)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# 2.4. Combined method: Random Forest + Autoencoder\n",
        "print(\"4. Combined method (Random Forest + Autoencoder)...\")\n",
        "start_time_method = time.time()\n",
        "combined_errors = (rf_errors / np.max(rf_errors) + ae_errors / np.max(ae_errors)) / 2\n",
        "combined_pred = get_top_k_outliers(combined_errors, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['Combined (RF+AE)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# 2.5. One-Class SVM with y added\n",
        "print(\"5. One-Class SVM (with y added)...\")\n",
        "start_time_method = time.time()\n",
        "X_with_y = np.column_stack((X_scaled, y.ravel()))\n",
        "ocsvm_with_y = OneClassSVM(kernel='rbf', gamma='auto')\n",
        "ocsvm_with_y.fit(X_with_y)\n",
        "ocsvm_with_y_scores = -ocsvm_with_y.decision_function(X_with_y)\n",
        "ocsvm_with_y_pred = get_top_k_outliers(ocsvm_with_y_scores, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['One-Class SVM (with y)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# 2.6. Isolation Forest with y added\n",
        "print(\"6. Isolation Forest (with y added)...\")\n",
        "start_time_method = time.time()\n",
        "iforest_with_y = SklearnIForest(random_state=42)\n",
        "iforest_with_y.fit(X_with_y)\n",
        "iforest_with_y_scores = -iforest_with_y.decision_function(X_with_y)\n",
        "iforest_with_y_pred = get_top_k_outliers(iforest_with_y_scores, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['Isolation Forest (with y)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# 2.7. LOF with y added\n",
        "print(\"7. LOF (with y added)...\")\n",
        "start_time_method = time.time()\n",
        "lof_with_y = LocalOutlierFactor(novelty=True)\n",
        "lof_with_y.fit(X_with_y)\n",
        "lof_with_y_scores = -lof_with_y.score_samples(X_with_y)\n",
        "lof_with_y_pred = get_top_k_outliers(lof_with_y_scores, n_outliers_desired)\n",
        "elapsed = time.time() - start_time_method\n",
        "method_times['LOF (with y)'] = elapsed\n",
        "print(f\"   Completed in {elapsed:.2f} seconds\")\n",
        "\n",
        "# Add NNFA time to method_times\n",
        "method_times['NNFA'] = nnfa_total_time\n",
        "\n",
        "# ==========================================================\n",
        "# COLLECT RESULTS\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*80)\n",
        "print(\"OUTLIER DETECTION METHOD COMPARISON\")\n",
        "print(\"=\"*80)\n",
        "\n",
        "all_methods = {\n",
        "    **{f\"{name} (pyod)\": results_pyod[name] for name in detectors_pyod},\n",
        "    'Random Forest (error)': rf_pred,\n",
        "    'Neural Network (error)': mlp_pred,\n",
        "    'Autoencoder (reconstruction)': ae_pred,\n",
        "    'Combined (RF+AE)': combined_pred,\n",
        "    'One-Class SVM (with y)': ocsvm_with_y_pred,\n",
        "    'Isolation Forest (with y)': iforest_with_y_pred,\n",
        "    'LOF (with y)': lof_with_y_pred,\n",
        "    'NNFA': nnf_pred\n",
        "}\n",
        "\n",
        "# Collect all anomaly scores for ranking\n",
        "all_scores = {\n",
        "    **{f\"{name} (pyod)\": scores_pyod[name] for name in detectors_pyod},\n",
        "    'Random Forest (error)': rf_errors,\n",
        "    'Neural Network (error)': mlp_errors,\n",
        "    'Autoencoder (reconstruction)': ae_errors,\n",
        "    'Combined (RF+AE)': combined_errors,\n",
        "    'One-Class SVM (with y)': ocsvm_with_y_scores,\n",
        "    'Isolation Forest (with y)': iforest_with_y_scores,\n",
        "    'LOF (with y)': lof_with_y_scores,\n",
        "    'NNFA': E_avg_per_sample\n",
        "}\n",
        "\n",
        "results = []\n",
        "for name, pred in all_methods.items():\n",
        "    if np.all(pred == -1):\n",
        "        results.append({'Method': name, 'Precision': -1, 'Recall': -1, 'F1': -1, 'TP': 0, 'FP': 0, 'FN': 0})\n",
        "    else:\n",
        "        tp = np.sum((pred == 1) & (true_outliers == 1))\n",
        "        fp = np.sum((pred == 1) & (true_outliers == 0))\n",
        "        fn = np.sum((pred == 0) & (true_outliers == 1))\n",
        "        precision = tp / (tp + fp) if (tp + fp) > 0 else 0\n",
        "        recall = tp / (tp + fn) if (tp + fn) > 0 else 0\n",
        "        f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0\n",
        "        results.append({'Method': name, 'Precision': precision, 'Recall': recall, 'F1': f1, 'TP': tp, 'FP': fp, 'FN': fn})\n",
        "\n",
        "df_results = pd.DataFrame(results).round(3)\n",
        "df_results = df_results.sort_values('F1', ascending=False).reset_index(drop=True)\n",
        "print(\"\\n\" + df_results.to_string(index=False))\n",
        "\n",
        "# ==========================================================\n",
        "# VERIFICATION: Each method detected exactly n_outliers_desired outliers\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*80)\n",
        "print(\"VERIFICATION: Number of outliers detected by each method\")\n",
        "print(\"=\"*80)\n",
        "for name, pred in all_methods.items():\n",
        "    if np.all(pred != -1):\n",
        "        detected = pred.sum()\n",
        "        status = \"✓\" if detected == n_outliers_desired else \"✗\"\n",
        "        print(f\"{status} {name:45s}: {detected:3d} outliers (expected {n_outliers_desired})\")\n",
        "\n",
        "# ==========================================================\n",
        "# EXECUTION TIME SUMMARY TABLE\n",
        "# ==========================================================\n",
        "print(\"\\n\" + \"=\"*80)\n",
        "print(\"EXECUTION TIME SUMMARY (seconds)\")\n",
        "print(\"=\"*80)\n",
        "time_df = pd.DataFrame(list(method_times.items()), columns=['Method', 'Time (seconds)'])\n",
        "time_df = time_df.sort_values('Time (seconds)', ascending=False).reset_index(drop=True)\n",
        "print(time_df.to_string(index=False))\n",
        "\n",
        "# ==========================================================\n",
        "# NNFA ERROR VISUALIZATION\n",
        "# ==========================================================\n",
        "plt.figure(figsize=(12, 6))\n",
        "examples = np.arange(1, Q+1)\n",
        "bars = plt.bar(examples, E_avg_per_sample, color='skyblue', edgecolor='black')\n",
        "for i in range(Q):\n",
        "    if true_outliers[i] == 1:\n",
        "        bars[i].set_color('red')\n",
        "    elif nnf_pred[i] == 1:\n",
        "        bars[i].set_color('orange')\n",
        "plt.axhline(y=threshold, color='red', linestyle='--', linewidth=2, label=f'Threshold (top {n_outliers_desired} outliers)')\n",
        "plt.xlabel('Observation number')\n",
        "plt.ylabel('Averaged squared error')\n",
        "plt.title(f'NNFA error distribution\\n(red – true outliers, orange – detected)')\n",
        "plt.legend()\n",
        "plt.grid(axis='y', linestyle=':', alpha=0.7)\n",
        "plt.tight_layout()\n",
        "plt.show()\n",
        "\n",
        "# ==========================================================\n",
        "# FINAL PROGRAM TIMING\n",
        "# ==========================================================\n",
        "program_end_time = time.time()\n",
        "program_end_str = time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime(program_end_time))\n",
        "total_program_time = program_end_time - program_start_time\n",
        "\n",
        "print(\"\\n\" + \"=\"*80)\n",
        "print(\"PROGRAM EXECUTION TIMING SUMMARY\")\n",
        "print(\"=\"*80)\n",
        "print(f\"Program started:  {program_start_str}\")\n",
        "print(f\"Program ended:    {program_end_str}\")\n",
        "print(f\"Total runtime:    {total_program_time:.2f} seconds ({total_program_time/60:.2f} minutes)\")\n",
        "print(\"=\"*80)\n",
        "\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"CONCLUSIONS:\")\n",
        "print(\"=\"*60)\n",
        "print(f\"✓ Outliers created by replacing extreme values with average value ({Savg:.2f} MPa)\")\n",
        "print(f\"  - Type H→A: {n_outliers_per_group} observations (were the highest strength, became average)\")\n",
        "print(f\"  - Type L→A: {n_outliers_per_group} observations (were the lowest strength, became average)\")\n",
        "print(f\"✓ Total outliers: {n_true_outliers} ({n_true_outliers/Q*100:.3f}% of data)\")\n",
        "print(f\"✓ ALL methods return exactly {n_outliers_desired} outliers (top-K approach)\")\n",
        "print(\"✓ These are structural outliers that violate the relationship between features and target\")\n",
        "print(\"✓ NNFA successfully detects outliers because it models the relationship y = f(X)\")\n",
        "print(\"✓ Methods using prediction error can also detect outliers\")\n",
        "print(\"✓ PyOD methods (input features only) DO NOT detect structural outliers\")\n",
        "print(f\"\\n📊 Fastest method: {time_df.iloc[-1]['Method']} ({time_df.iloc[-1]['Time (seconds)']:.2f}s)\")\n",
        "print(f\"📊 Slowest method: {time_df.iloc[0]['Method']} ({time_df.iloc[0]['Time (seconds)']:.2f}s)\")\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"TESTING COMPLETED.\")\n",
        "print(\"=\"*60)"
      ]
    }
  ]
}