//+------------------------------------------------------------------+
//|                                  18_Random_Correlation_Test.mq5   |
//| MQL検証シリーズ #18 系列相関テスト                                    |
//| https://fx-omoshiro-lab.com/mql-verification/18-random-test       |
//+------------------------------------------------------------------+
//| 【差がわかりやすいテスト】                                           |
//| 連続する乱数値 x[n] と x[n+1] をX-Y平面にプロットします。              |
//| 線形合同法（MathRand）は斜めの縞模様が顕著に現れます。                 |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property link      "https://fx-omoshiro-lab.com/"
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0

#include <Canvas\Canvas.mqh>

input int  CanvasSize   = 400;    // キャンバスサイズ（正方形）
input bool UseXorShift  = false;  // true=XorShift(高品質), false=MathRand(低品質)
input int  PointsCount  = 50000;  // 打点数

CCanvas canvas;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   // キャンバス作成
   if(!canvas.CreateBitmapLabel(0, "CorrelationTest", 0, 0, 50, CanvasSize, CanvasSize, COLOR_FORMAT_XRGB_NOALPHA))
   {
      Print("Error creating canvas: ", GetLastError());
      return(INIT_FAILED);
   }
   
   // 背景を黒でクリア
   canvas.Erase(ColorToARGB(clrBlack));
   
   // 乱数シード初期化
   MathSrand(12345); // 固定シードで再現性を確保
   XorShift_Seed(12345);
   
   // 点を打つ
   uint drawColor = ColorToARGB(clrLime);
   
   // 最初の乱数を取得
   int prevRand;
   if(UseXorShift)
      prevRand = XorShift();
   else
      prevRand = MathRand();
   
   for(int i=0; i<PointsCount; i++)
   {
      int currRand;
      
      if(UseXorShift)
      {
         currRand = XorShift();
      }
      else
      {
         currRand = MathRand();
      }
      
      // 系列相関テスト: x[n] vs x[n+1] をプロット
      // X軸 = 前回の乱数値、Y軸 = 今回の乱数値
      int x, y;
      
      if(UseXorShift)
      {
         // XorShiftは0-2147483647なので、スケーリング
         x = (int)((double)prevRand / 2147483647.0 * (CanvasSize - 1));
         y = (int)((double)currRand / 2147483647.0 * (CanvasSize - 1));
      }
      else
      {
         // MathRandは0-32767
         x = (int)((double)prevRand / 32767.0 * (CanvasSize - 1));
         y = (int)((double)currRand / 32767.0 * (CanvasSize - 1));
      }
      
      // 範囲チェック
      if(x >= 0 && x < CanvasSize && y >= 0 && y < CanvasSize)
      {
         canvas.PixelSet(x, y, drawColor);
      }
      
      prevRand = currRand;
   }
   
   // タイトルを描画
   string mode = UseXorShift ? "XorShift (High Quality)" : "MathRand (Low Quality)";
   canvas.FontSet("Arial", 14, FW_BOLD);
   canvas.TextOut(10, 10, mode, ColorToARGB(clrWhite), TA_LEFT);
   canvas.TextOut(10, 30, "Serial Correlation Test: x[n] vs x[n+1]", ColorToARGB(clrYellow), TA_LEFT);
   
   // 画面更新
   canvas.Update();
   
   // Commentにも表示
   Comment("【系列相関テスト】\n",
           "Mode: ", mode, "\n",
           "Points: ", PointsCount, "\n",
           "------\n",
           "MathRandの場合：斜めの線状パターンが見える\n",
           "XorShiftの場合：均一なノイズ状に分布");
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Indicator Deinitialization                                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   canvas.Destroy();
   Comment("");
}

//+------------------------------------------------------------------+
//| XorShift Random Number Generator                                 |
//+------------------------------------------------------------------+
uint x_seed = 123456789;
uint y_seed = 362436069;
uint z_seed = 521288629;
uint w_seed = 88675123;

void XorShift_Seed(int seed)
{
   x_seed = seed;
   y_seed = 362436069;
   z_seed = 521288629;
   w_seed = 88675123;
}

int XorShift()
{
   uint t = x_seed ^ (x_seed << 11);
   x_seed = y_seed; y_seed = z_seed; z_seed = w_seed;
   w_seed = (w_seed ^ (w_seed >> 19)) ^ (t ^ (t >> 8));
   return (int)(w_seed & 0x7FFFFFFF);
}
