Yes, Thank you, I have... Otherwise I would not share my holy wisdom... and it take great efforts, to research the different ideas with Mathematica, in order to get strategies working. And I want our Zorro to be more popular, so that we can all benefit from future versions.

but, Yes, to make it open source is quite challenging, because the developer of the ideas should realize that in our new age of freedom, the idea of money is going to get erased from the world in less than 7 years, so there is not point to keep the idea in code secret.

Code
// Zorro64 C++ Strategy DLL - Alpha12 (FULL OOP Refactor) + OpenCL Advisor
// Compile as x64 DLL, include zorro.h
// ======================================================================
//
// Refactor goals achieved:
// - ALL prior “globals” moved into Alpha12Strategy (and its components).
// - Components: NodePool, DTree, MarkovChain, NetState, Logger, RuntimeManager.
// - No more static state inside functions (seedBar/haveSeed/seedVal moved into members).
// - run() remains a thin C bridge.
//
// OpenCL changes:
// OpenCL advisor uses dynamic loading: LoadLibrary("OpenCL.dll") + GetProcAddress.
// no OpenCL.lib required.
// ======================================================================

#define _CRT_SECURE_NO_WARNINGS

#include <zorro.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#include <stddef.h>   // for size_t

// ======================================================================
// ================= USER CONFIG =================
#define ASSET_SYMBOL "EUR/USD"
#define BAR_PERIOD 5
#define TF_H1 12

#define MC_ACT 0.30         // initial threshold on |CDL| in [-1..1] to accept a pattern
#define PBULL_LONG_TH 0.60  // Markov gate for long
#define PBULL_SHORT_TH 0.40 // Markov gate for short

// Debug toggles
#define ENABLE_PLOTS 0
#define ENABLE_WATCH 0

// ================= ENGINE PARAMETERS =================
#define MAX_BRANCHES 3
#define MAX_DEPTH 4
#define NWIN 256
#define NET_EQNS 100
#define DEGREE 4
#define KPROJ 16
#define REWIRE_EVERY 127
#define CAND_NEIGH 8

// ===== LOGGING CONTROLS =====
#define LOG_EQ_TO_ONE_FILE 1
#define LOG_EXPR_TEXT 0
#define META_EVERY 4
#define LOG_EQ_SAMPLE NET_EQNS
#define EXPR_MAXLEN 512
#define LOG_EVERY 16
#define MC_EVERY 1

// ---- DTREE feature sizes ----
#define ADV_EQ_NF 19
#define ADV_PAIR_NF 12

// ================= Candles ? 122-state Markov =================
#define MC_NPAT 61
#define MC_STATES 123
#define MC_NONE 0

// ================= Runtime Memory / Accuracy Manager =================
#define MEM_BUDGET_MB 50
#define MEM_HEADROOM_MB 5
#define DEPTH_STEP_BARS 16
#define KEEP_CHILDREN_HI 2
#define KEEP_CHILDREN_LO 1
#define RUNTIME_MIN_DEPTH 2

// ===== Chunked rewire settings =====
#define REWIRE_BATCH_EQ_5M 24
#define REWIRE_BATCH_EQ_H1 64
#define REWIRE_MIN_BATCH 8
#define REWIRE_NORM_EVERY 1
#define REWIRE_MEM_SOFT (MEM_BUDGET_MB - 4)
#define REWIRE_MEM_HARD (MEM_BUDGET_MB - 1)

// ===== Chunked update settings =====
#define UPDATE_BATCH_EQ_5M 32
#define UPDATE_BATCH_EQ_H1 96
#define UPDATE_MIN_BATCH 8
#define UPDATE_MEM_SOFT (MEM_BUDGET_MB - 4)
#define UPDATE_MEM_HARD (MEM_BUDGET_MB - 1)

// ======================= Tight-memory switches =======================
#define TIGHT_MEM 1
#ifdef TIGHT_MEM
typedef float fvar;
typedef short i16;
typedef char  i8;
#else
typedef var fvar;
typedef int i16;
typedef int i8;
#endif

// ---------- Candle wrapper name mapping ----------
// Map Alpha12 "canonical" names -> whatever YOUR zorro.h actually provides.
#ifndef A12_CDL_HIGH_WAVE
  #define A12_CDL_HIGH_WAVE()               CDLHignWave()
#endif

#ifndef A12_CDL_STICKSANDWICH
  #define A12_CDL_STICKSANDWICH()           CDLStickSandwhich()
#endif

#ifndef A12_CDL_SEPARATING_LINES
  #define A12_CDL_SEPARATING_LINES()        CDLSeperatingLines()
#endif

#ifndef A12_CDL_CONCEALING_BABY_SWALLOW
  #define A12_CDL_CONCEALING_BABY_SWALLOW() CDLConcealBabysWall()
#endif

// ======================================================================
// Forward declarations
class Alpha12Strategy;
static Alpha12Strategy* gAlpha12 = nullptr;

// ======================================================================
// ========================= NodePool (component) ========================

struct Node {
  var v;
  var r;
  void* c;
  int n;
  int d;
};

struct NodeChunk {
  NodeChunk* next;
  int used;
  int _pad;
  Node nodes[256];
};

class NodePool {
  NodeChunk* head = 0;
  Node* freeList = 0;

public:
  ~NodePool() { freeAll(); }

  Node* allocNode() {
    if(freeList) {
      Node* n = freeList;
      freeList = (Node*)n->c;
      n->c = 0; n->n = 0; n->d = 0; n->v = 0; n->r = 0;
      return n;
    }
    if(!head || head->used >= 256) {
      NodeChunk* ch = (NodeChunk*)malloc(sizeof(NodeChunk));
      if(!ch) quit("Alpha12: OOM allocating NodeChunk");
      memset(ch, 0, sizeof(NodeChunk));
      ch->next = head;
      head = ch;
    }
    if(head->used < 0 || head->used >= 256) quit("Alpha12: Corrupt node pool state");
    return &head->nodes[head->used++];
  }

  void freeNode(Node* u) {
    if(!u) return;
    u->c = (void*)freeList;
    freeList = u;
  }

  void freeAll() {
    NodeChunk* ch = head;
    while(ch) {
      NodeChunk* nx = ch->next;
      free(ch);
      ch = nx;
    }
    head = 0;
    freeList = 0;
  }
};

// ======================================================================
// ========================= MarkovChain (component) =====================

class MarkovChain {
public:
  int* Count = 0;   // [MC_STATES*MC_STATES]
  int* RowSum = 0;  // [MC_STATES]
  int Prev = -1;
  int Cur  = 0;
  var PBullNext = 0.5;
  var Entropy = 1.0;
  var Alpha = 1.0; // Laplace smoothing

public:
  void alloc() {
    int NN = MC_STATES*MC_STATES;
    int bytesMat = NN*sizeof(int);
    int bytesRow = MC_STATES*sizeof(int);

    Count  = (int*)malloc(bytesMat);
    RowSum = (int*)malloc(bytesRow);
    if(!Count || !RowSum) quit("Alpha12: OOM in MarkovChain::alloc");
    memset(Count,0,bytesMat);
    memset(RowSum,0,bytesRow);
    Prev = -1; Cur = 0; PBullNext = 0.5; Entropy = 1.0;
  }

  void freeMem() {
    if(Count) free(Count);
    if(RowSum) free(RowSum);
    Count = RowSum = 0;
    Prev = -1; Cur = 0; PBullNext = 0.5; Entropy = 1.0;
  }

  static int isBull(int s){
    if(s<=0) return 0;
    return ((s-1)%2)==1;
  }

  static int stateFromCDL(var* cdl /*len=61*/, var thr) {
    int i, best=-1;
    var besta=0;
    for(i=0;i<MC_NPAT;i++){
      var a = abs(cdl[i]);
      if(a>besta){ besta=a; best=i; }
    }
    if(best<0) return MC_NONE;
    if(besta < thr) return MC_NONE;
    int bull = (cdl[best] > 0);
    return 1 + 2*best + bull; // 1..122
  }

  int idx(int fr,int to) const { return fr*MC_STATES + to; }

  void update(int sPrev,int sCur){
    if(sPrev<0) return;
    Count[idx(sPrev,sCur)]++;
    RowSum[sPrev]++;
  }

  var prob(int s,int t) const {
    var num = (var)Count[idx(s,t)] + Alpha;
    var den = (var)RowSum[s] + Alpha*MC_STATES;
    if(den<=0) return 1.0/MC_STATES;
    return num/den;
  }

  void rowStats(int s, var* outPBull, var* outEntropy) {
    if(outPBull) *outPBull=0.5;
    if(outEntropy) *outEntropy=1.0;
    if(!Count || !RowSum) return;
    if(!(Alpha > 0)) Alpha = 1.0;
    if(s <= 0 || s >= MC_STATES) return;
    if(RowSum[s] <= 0) return;

    var den = (var)RowSum[s] + Alpha*(var)MC_STATES;
    if(!(den > 0)) return;

    var Z=0, pBull=0;
    int t;
    for(t=1;t<MC_STATES;t++){
      var p = ((var)Count[idx(s,t)] + Alpha) / den;
      Z += p;
      if(isBull(t)) pBull += p;
    }
    if(!(Z>0)) return;

    var H=0;
    var Hmax = log((var)(MC_STATES-1));
    if(!(Hmax > 0)) Hmax = 1.0;
    for(t=1;t<MC_STATES;t++){
      var p = (((var)Count[idx(s,t)] + Alpha) / den) / Z;
      if(p>0) H += -p*log(p);
    }

    if(outPBull) *outPBull = pBull / Z;
    if(outEntropy) *outEntropy = H / Hmax;
  }
};

// ======================================================================
// =========================== Logger (component) ========================

class Alpha12Logger {
  int wroteHeader = 0;

public:
  void writeEqHeaderOnce(){
    if(wroteHeader) return;
    wroteHeader = 1;
    file_append("Log\\Alpha12_eq_all.csv",
      "Bar,Epoch,Ctx,EqCount,i,n1,n2,TreeId,Depth,Rate,Pred,Adv,Prop,Mode,WAdv,WTree,PBull,Entropy,MCState,ExprLen,ExprHash,tanhN,sinN,cosN\n");
  }

  static void strlcat_safe(string dst, string src, int cap) {
    if(!dst || !src || cap <= 0) return;
    int dl = (int)strlen(dst);
    int sl = (int)strlen(src);
    int room = cap - 1 - dl;
    if(room <= 0){ if(cap > 0) dst[cap-1] = 0; return; }
    int i;
    for(i = 0; i < room && i < sl; i++) dst[dl + i] = src[i];
    dst[dl + i] = 0;
  }

  static int countSubStr(string s, string sub){
    if(!s || !sub) return 0;
    int n=0; string p=s; int sublen = (int)strlen(sub); if(sublen<=0) return 0;
    while((p=strstr(p,sub))){ n++; p += sublen; }
    return n;
  }

  static int djb2_hash(string s){
    int h = 5381, c, i = 0;
    if(!s) return h;
    while((c = s[i++])) h = ((h<<5)+h) ^ c;
    return h & 0x7fffffff;
  }

  void appendEqMetaLine(
    int bar, int epoch, int ctx,
    int i, int n1, int n2, int tid, int depth, var rate, var pred, var adv, var prop, int mode,
    var wadv, var wtree, var pbull, var ent, int mcstate, string expr)
  {
    if(i >= LOG_EQ_SAMPLE) return;

    int eLen = 0, eHash = 0, cT = 0, cS = 0, cC = 0;
    if(expr){
      eLen  = (int)strlen(expr);
      eHash = (int)djb2_hash(expr);
      cT    = countSubStr(expr,"tanh(");
      cS    = countSubStr(expr,"sin(");
      cC    = countSubStr(expr,"cos(");
    } else {
      eHash = (int)djb2_hash("");
    }

    file_append("Log\\Alpha12_eq_all.csv",
      strf("%i,%i,%i,%i,%i,%i,%i,%i,%i,%.4f,%.4f,%.4f,%.4f,%i,%.3f,%.3f,%.4f,%.4f,%i,%i,%i,%i,%i,%i\n",
        bar, epoch, ctx, NET_EQNS, i, n1, n2, tid, depth,
        rate, pred, adv, prop, mode, wadv, wtree, pbull, ent,
        mcstate, eLen, eHash, cT, cS, cC));
  }
};

// ======================================================================
// =========================== NetState (component) ======================

class NetState {
public:
  int N = NET_EQNS;
  int D = DEGREE;
  int K = KPROJ;

  var*  State = 0;
  var*  Prev  = 0;
  var*  StateSq = 0;

  i16*  Adj = 0;
  fvar* RP  = 0;
  fvar* Z   = 0;
  i8*   Mode= 0;

  fvar* WSelf=0; fvar* WN1=0; fvar* WN2=0; fvar* WGlob1=0; fvar* WGlob2=0; fvar* WMom=0; fvar* WTree=0; fvar* WAdv=0;

  fvar *A1x=0,*A1lam=0,*A1mean=0,*A1E=0,*A1P=0,*A1i=0,*A1c=0;
  fvar *A2x=0,*A2lam=0,*A2mean=0,*A2E=0,*A2P=0,*A2i=0,*A2c=0;
  fvar *G1mean=0,*G1E=0,*G2P=0,*G2lam=0;

  fvar* TreeTerm = 0;
  i16*  TopEq = 0;
  fvar* TopW  = 0;
  i16*  EqTreeId = 0;

  fvar* TAlpha=0;
  fvar* TBeta =0;

  fvar* PropRaw=0;
  fvar* Prop   =0;

  string* Sym = 0;
  int SymFreed = 0;

  fvar* HitEW = 0;
  int*  HitN  = 0;
  fvar* AdvPrev = 0;
  var   Ret1 = 0;

  int ProjBar = -1;
  int ProjK = -1;

  int Keff = KPROJ;

public:
  void allocate() {
    int n=N, d=D, k=K;

    State   = (var*)malloc(n*sizeof(var));
    Prev    = (var*)malloc(n*sizeof(var));
    StateSq = (var*)malloc(n*sizeof(var));

    Adj  = (i16*)malloc(n*d*sizeof(i16));
    RP   = (fvar*)malloc(k*n*sizeof(fvar));
    Z    = (fvar*)malloc(k*sizeof(fvar));
    Mode = (i8*)malloc(n*sizeof(i8));

    WSelf=(fvar*)malloc(n*sizeof(fvar));
    WN1=(fvar*)malloc(n*sizeof(fvar));
    WN2=(fvar*)malloc(n*sizeof(fvar));
    WGlob1=(fvar*)malloc(n*sizeof(fvar));
    WGlob2=(fvar*)malloc(n*sizeof(fvar));
    WMom=(fvar*)malloc(n*sizeof(fvar));
    WTree=(fvar*)malloc(n*sizeof(fvar));
    WAdv=(fvar*)malloc(n*sizeof(fvar));

    A1x=(fvar*)malloc(n*sizeof(fvar)); A1lam=(fvar*)malloc(n*sizeof(fvar)); A1mean=(fvar*)malloc(n*sizeof(fvar));
    A1E=(fvar*)malloc(n*sizeof(fvar)); A1P=(fvar*)malloc(n*sizeof(fvar)); A1i=(fvar*)malloc(n*sizeof(fvar)); A1c=(fvar*)malloc(n*sizeof(fvar));

    A2x=(fvar*)malloc(n*sizeof(fvar)); A2lam=(fvar*)malloc(n*sizeof(fvar)); A2mean=(fvar*)malloc(n*sizeof(fvar));
    A2E=(fvar*)malloc(n*sizeof(fvar)); A2P=(fvar*)malloc(n*sizeof(fvar)); A2i=(fvar*)malloc(n*sizeof(fvar)); A2c=(fvar*)malloc(n*sizeof(fvar));

    G1mean=(fvar*)malloc(n*sizeof(fvar)); G1E=(fvar*)malloc(n*sizeof(fvar));
    G2P=(fvar*)malloc(n*sizeof(fvar)); G2lam=(fvar*)malloc(n*sizeof(fvar));

    TAlpha=(fvar*)malloc(n*sizeof(fvar));
    TBeta =(fvar*)malloc(n*sizeof(fvar));

    TreeTerm=(fvar*)malloc(n*sizeof(fvar));
    TopEq=(i16*)malloc(n*sizeof(i16));
    TopW =(fvar*)malloc(n*sizeof(fvar));

    PropRaw=(fvar*)malloc(n*sizeof(fvar));
    Prop   =(fvar*)malloc(n*sizeof(fvar));

    EqTreeId=(i16*)malloc(n*sizeof(i16));

    if(LOG_EXPR_TEXT) Sym = (string*)malloc(n*sizeof(char*)); else Sym = 0;

    { int t; for(t=0;t<n*d;t++) Adj[t] = -1; }

    {
      int i;
      for(i=0;i<n;i++){
        State[i]=random(); Prev[i]=State[i]; StateSq[i]=State[i]*State[i];
        Mode[i]=0;

        WSelf[i]=0.5f; WN1[i]=0.2f; WN2[i]=0.2f;
        WGlob1[i]=0.1f; WGlob2[i]=0.1f; WMom[i]=0.05f;
        WTree[i]=0.15f; WAdv[i]=0.15f;

        A1x[i]=1; A1lam[i]=0.1f; A1mean[i]=0; A1E[i]=0; A1P[i]=0; A1i[i]=0; A1c[i]=0;
        A2x[i]=1; A2lam[i]=0.1f; A2mean[i]=0; A2E[i]=0; A2P[i]=0; A2i[i]=0; A2c[i]=0;

        G1mean[i]=1.0f; G1E[i]=0.001f;
        G2P[i]=0.6f; G2lam[i]=0.3f;

        TAlpha[i]=0.8f; TBeta[i]=25.0f;
        TreeTerm[i]=0;
        TopEq[i]=-1; TopW[i]=0;

        PropRaw[i]=1; Prop[i]=(fvar)(1.0/n);

        if(LOG_EXPR_TEXT){
          Sym[i] = (char*)malloc(EXPR_MAXLEN);
          if(Sym[i]) strcpy(Sym[i],"");
        }
      }
    }

    HitEW = (fvar*)malloc(n*sizeof(fvar));
    HitN  = (int*)malloc(n*sizeof(int));
    AdvPrev = (fvar*)malloc(n*sizeof(fvar));
    { int i; for(i=0;i<n;i++){ HitEW[i]=0.5f; HitN[i]=0; AdvPrev[i]=0; } }

    ProjBar = -1; ProjK = -1;
  }

  void freeAll() {
    int i;
    if(State)free(State);
    if(Prev)free(Prev);
    if(StateSq)free(StateSq);
    if(Adj)free(Adj);
    if(RP)free(RP);
    if(Z)free(Z);
    if(Mode)free(Mode);

    if(WSelf)free(WSelf);
    if(WN1)free(WN1);
    if(WN2)free(WN2);
    if(WGlob1)free(WGlob1);
    if(WGlob2)free(WGlob2);
    if(WMom)free(WMom);
    if(WTree)free(WTree);
    if(WAdv)free(WAdv);

    if(A1x)free(A1x); if(A1lam)free(A1lam); if(A1mean)free(A1mean);
    if(A1E)free(A1E); if(A1P)free(A1P); if(A1i)free(A1i); if(A1c)free(A1c);

    if(A2x)free(A2x); if(A2lam)free(A2lam); if(A2mean)free(A2mean);
    if(A2E)free(A2E); if(A2P)free(A2P); if(A2i)free(A2i); if(A2c)free(A2c);

    if(G1mean)free(G1mean); if(G1E)free(G1E);
    if(G2P)free(G2P); if(G2lam)free(G2lam);
    if(TAlpha)free(TAlpha); if(TBeta)free(TBeta);

    if(TreeTerm)free(TreeTerm);
    if(TopEq)free(TopEq);
    if(TopW)free(TopW);
    if(EqTreeId)free(EqTreeId);

    if(PropRaw)free(PropRaw);
    if(Prop)free(Prop);

    if(Sym){
      for(i=0;i<N;i++) if(Sym[i]) free(Sym[i]);
      free(Sym);
    }
    Sym = 0;

    if(HitEW) free(HitEW);
    if(HitN) free(HitN);
    if(AdvPrev) free(AdvPrev);

    State=Prev=StateSq=0; Adj=0; RP=0; Z=0; Mode=0;
    WSelf=WN1=WN2=WGlob1=WGlob2=WMom=WTree=WAdv=0;
    A1x=A1lam=A1mean=A1E=A1P=A1i=A1c=0;
    A2x=A2lam=A2mean=A2E=A2P=A2i=A2c=0;
    G1mean=G1E=G2P=G2lam=0;
    TAlpha=TBeta=0;
    TreeTerm=0; TopEq=0; TopW=0; EqTreeId=0;
    PropRaw=0; Prop=0;
    HitEW=0; HitN=0; AdvPrev=0;
  }

  void randomizeRP(){
    int k=K, n=N;
    int kk,j;
    for(kk=0;kk<k;kk++)
      for(j=0;j<n;j++)
        RP[kk*n+j] = ifelse(random(1) < 0.5, -1.0, 1.0);
  }

  int keffClamped() const {
    int kk = Keff;
    if(kk < 0) kk = 0;
    if(kk > K) kk = K;
    return kk;
  }

  void computeProjection(){
    if(!RP || !Z || !StateSq) return;
    int kk = keffClamped();
    if(ProjBar == Bar && ProjK == kk) return;
    int k, j;
    for(k=0;k<kk;k++){
      var acc=0;
      for(j=0;j<N;j++) acc += (var)RP[k*N + j] * StateSq[j];
      Z[k] = (fvar)acc;
    }
    ProjBar = Bar;
    ProjK = kk;
  }

  void sanitizeAdjacency(){
    if(!Adj) return;
    int i,d;
    for(i=0;i<N;i++){
      for(d=0;d<D;d++){
        i16* p = &Adj[i*D + d];
        if(*p < 0 || *p >= N || *p == i){
          int r = (int)random(N);
          if(r==i) r = (r+1)%N;
          *p = (i16)r;
        }
      }
      if(D >= 2 && Adj[i*D+0] == Adj[i*D+1]){
        int r2 = (Adj[i*D+1] + 1) % N;
        if(r2 == i) r2 = (r2+1)%N;
        Adj[i*D+1] = (i16)r2;
      }
    }
  }

  int adjSafe(int i, int d) const {
    if(!Adj || N<=1 || D<=0) return 0;
    if(d<0) d=0;
    if(d>=D) d = d % D;
    int v = Adj[i*D + d];
    if(v<0 || v>=N || v==i) v = (i+1)%N;
    return v;
  }

  void normalizeProportions(){
    int i;
    var s=0;
    for(i=0;i<N;i++) s += PropRaw[i];
    if(s<=0){
      for(i=0;i<N;i++) Prop[i] = (fvar)(1.0/N);
      return;
    }
    for(i=0;i<N;i++) Prop[i] = (fvar)(PropRaw[i]/s);
  }
};

// ======================================================================
// ============================= DTree (component) =======================

class DTree {
public:
  NodePool* pool = 0;

  Node* Root = 0;

  Node** TreeIdx = 0;
  int TreeN = 0;
  int TreeCap = 0;

  var DTreeExp = 0;
  var* DepthW = 0;
  var DepthExpLast = -1.0;
  Node DummyNode;

  var* PredNode = 0;
  int PredLen = 0;
  int PredCap = 0;
  int PredCacheBar = -1;

  var* EqTheta = 0;
  int LeadEq = -1;
  var LeadTh = 0;
  var CycPh = 0;
  var CycSpd = 0;

public:
  DTree() { memset(&DummyNode,0,sizeof(DummyNode)); }

  void bindPool(NodePool* p){ pool = p; }

  void allocDepthLUT(){
    int sz = MAX_DEPTH + 1;
    if(!DepthW) DepthW = (var*)malloc(sz*sizeof(var));
    if(!DepthW) quit("Alpha12: OOM DepthW");
  }

  void freeAll(){
    if(Root) freeTree(Root);
    Root = 0;
    if(TreeIdx) free(TreeIdx);
    TreeIdx = 0; TreeN=0; TreeCap=0;
    if(DepthW) free(DepthW);
    DepthW=0;
    if(PredNode) free(PredNode);
    PredNode=0; PredLen=0; PredCap=0;
    if(EqTheta) free(EqTheta);
    EqTheta=0;
  }

  int tree_bytes(Node* u){
    if(!u) return 0;
    int SZV = sizeof(var), SZI = sizeof(int), SZP = sizeof(void*);
    int sz_node = 2*SZV + SZP + 2*SZI;
    int total = sz_node;
    if(u->n > 0 && u->c) total += u->n * SZP;
    int i;
    for(i=0;i<u->n;i++) total += tree_bytes(((Node**)u->c)[i]);
    return total;
  }

  void refreshDepthW(){
    if(!DepthW) return;
    int d;
    for(d=0; d<=MAX_DEPTH; d++) DepthW[d] = 1.0 / pow(d+1, DTreeExp);
    DepthExpLast = DTreeExp;
  }

  Node* createNode(int depth){
    if(!pool) quit("Alpha12: DTree pool not bound");
    Node* u = pool->allocNode();
    if(!u) return 0;
    u->v = random();
    u->r = 0.01 + 0.02*depth + random(0.005);
    u->d = depth;
    if(depth > 0){
      u->n = 1 + (int)random(MAX_BRANCHES);
      u->c = (void**)malloc(u->n*sizeof(void*));
      if(!u->c){ u->n=0; u->c=0; return u; }
      int i;
      for(i=0;i<u->n;i++){
        ((Node**)u->c)[i] = createNode(depth-1);
      }
    } else {
      u->n=0; u->c=0;
    }
    return u;
  }

  var evaluateNode(Node* u){
    if(!u) return 0;
    var sum=0; int i;
    for(i=0;i<u->n;i++) sum += evaluateNode(((Node**)u->c)[i]);
    if(DepthExpLast < 0 || abs(DTreeExp - DepthExpLast) > 1e-9) refreshDepthW();
    var phase = sin(u->r * Bar + sum);
    var weight = DepthW[u->d];
    u->v = (1 - weight)*u->v + weight*phase;
    return u->v;
  }

  void freeTree(Node* u){
    if(!u) return;
    int i;
    for(i=0;i<u->n;i++) freeTree(((Node**)u->c)[i]);
    if(u->c) free(u->c);
    pool->freeNode(u);
  }

  void pushTreeNode(Node* u){
    if(TreeN >= TreeCap){
      int newCap = TreeCap*2;
      if(newCap < 64) newCap = 64;
      TreeIdx = (Node**)realloc(TreeIdx, newCap*sizeof(Node*));
      TreeCap = newCap;
    }
    TreeIdx[TreeN++] = u;
  }

  void indexTreeDFS(Node* u){
    if(!u) return;
    pushTreeNode(u);
    int i;
    for(i=0;i<u->n;i++) indexTreeDFS(((Node**)u->c)[i]);
  }

  void ensurePredCache(){
    if(PredCacheBar != Bar){
      if(PredNode){
        int i;
        for(i=0;i<PredLen;i++) PredNode[i] = -2;
      }
      PredCacheBar = Bar;
    }
  }

  var nodePredictability(Node* t){
    if(!t) return 0.5;
    var disp = 0; int n=t->n, i, cnt=0;
    if(t->c){
      for(i=0;i<n;i++){
        Node* c = ((Node**)t->c)[i];
        if(c){ disp += abs(c->v - t->v); cnt++; }
      }
      if(cnt>0) disp /= cnt;
    }
    var depthFac = 1.0/(1 + t->d);
    var rateBase = 0.01 + 0.02*t->d;
    var rateFac = exp(-25.0*abs(t->r - rateBase));
    var p = 0.5*(depthFac + rateFac);
    p = 0.5*p + 0.5*(1.0 + (-disp));
    if(p<0) p=0; if(p>1) p=1;
    return p;
  }

  var predByTid(int tid){
    if(!TreeIdx || tid < 0 || tid >= TreeN || !TreeIdx[tid]) return 0.5;
    ensurePredCache();
    if(PredNode && tid < PredLen && PredNode[tid] > -1.5) return PredNode[tid];
    Node* t = TreeIdx[tid];
    var p = nodePredictability(t);
    if(PredNode && tid < PredLen) PredNode[tid] = p;
    return p;
  }

  Node* treeAt(int tid){
    if(!TreeIdx || tid < 0 || tid >= TreeN || !TreeIdx[tid]) return &DummyNode;
    return TreeIdx[tid];
  }

  int safeTreeIndexFromEq(int eqTreeId, int treeN){
    int denom = ifelse(treeN>0, treeN, 1);
    int tid = eqTreeId;
    if(tid < 0) tid = 0;
    if(denom > 0) tid = tid % denom;
    if(tid < 0) tid = 0;
    return tid;
  }

  void maybeShrinkTreeIdx(){
    if(!TreeIdx) return;
    if(TreeCap > 64 && TreeN < (TreeCap>>1)){
      int newCap = (TreeCap>>1);
      if(newCap < 64) newCap = 64;
      TreeIdx = (Node**)realloc(TreeIdx, newCap*sizeof(Node*));
      TreeCap = newCap;
    }
  }

  void resizePredCacheToTree(){
    PredLen = TreeN; if(PredLen <= 0) PredLen = 1;
    if(PredLen > PredCap){
      if(PredNode) free(PredNode);
      PredNode = (var*)malloc(PredLen*sizeof(var));
      PredCap = PredLen;
    }
    PredCacheBar = -1;
  }

  void refreshEqAngles(const i16* eqTreeId, int eqN){
    if(!EqTheta) EqTheta = (var*)malloc(eqN*sizeof(var));
    if(!EqTheta) quit("Alpha12: OOM EqTheta");
    var twoPi = 2.*3.141592653589793;
    var denom = ifelse(TreeN>0,(var)TreeN,1.0);
    int i;
    for(i=0;i<eqN;i++){
      int tid = safeTreeIndexFromEq((int)eqTreeId[i], TreeN);
      var u = ((var)tid)/denom;
      EqTheta[i] = twoPi*u;
    }
  }

  static var pi(){ return 3.141592653589793; }

  static var wrapPi(var a){
    while(a <= -pi()) a += 2.*pi();
    while(a >  pi()) a -= 2.*pi();
    return a;
  }

  static var angDiff(var a, var b){ return wrapPi(b - a); }

  void updateEquationCycle(const fvar* prop, int N){
    if(!EqTheta){ CycPh = wrapPi(CycPh); return; }
    int i, bestI=0;
    var bestP=-1;
    for(i=0;i<N;i++){
      var p = (var)prop[i];
      if(p > bestP){ bestP=p; bestI=i; }
    }
    var th = EqTheta[bestI];
    var d = angDiff(LeadTh, th);
    CycSpd = 0.9*CycSpd + 0.1*d;
    CycPh = wrapPi(CycPh + CycSpd);
    LeadEq = bestI;
    LeadTh = th;
  }

  var nodeImportance(Node* u){
    if(!u) return 0;
    var amp = abs(u->v); if(amp>1) amp=1;
    var p = nodePredictability(u);
    var depthW = 1.0/(1.0 + u->d);
    var imp = (0.6*p + 0.4*amp) * depthW;
    return imp;
  }

  Node* createLeafDepth(int d){
    Node* u = pool->allocNode();
    if(!u) return 0;
    u->v = random();
    u->r = 0.01 + 0.02*d + random(0.005);
    u->n = 0;
    u->c = 0;
    u->d = d;
    return u;
  }

  void growSelectiveAtDepth(Node* u, int frontierDepth, int addK){
    if(!u) return;
    if(u->d == frontierDepth){
      int want = addK; if(want<=0) return;
      int oldN=u->n, newN=oldN+want;
      Node** Cnew = (Node**)malloc(newN*sizeof(void*));
      if(!Cnew) return;
      if(oldN>0 && u->c) memcpy(Cnew,u->c,oldN*sizeof(void*));
      int i;
      for(i=oldN;i<newN;i++) Cnew[i] = createLeafDepth(frontierDepth-1);
      if(u->c) free(u->c);
      u->c = Cnew;
      u->n = newN;
      return;
    }
    int j;
    for(j=0;j<u->n;j++) growSelectiveAtDepth(((Node**)u->c)[j],frontierDepth,addK);
  }

  void freeChildAt(Node* parent, int idx){
    if(!parent || !parent->c) return;
    Node** C = (Node**)parent->c;
    freeTree(C[idx]);
    int i;
    for(i=idx+1;i<parent->n;i++) C[i-1]=C[i];
    parent->n--;
    if(parent->n==0){ free(parent->c); parent->c=0; }
  }

  void pruneSelectiveAtDepth(Node* u, int targetDepth, int keepK){
    if(!u) return;
    if(u->d == targetDepth-1 && u->n > 0){
      int n=u->n, i;
      int mark[16]; for(i=0;i<16;i++) mark[i]=0;
      int iter;
      for(iter=0; iter<keepK && iter<n; iter++){
        int bestI=-1; var bestImp=-1;
        for(i=0;i<n;i++){
          if(i<16 && mark[i]==1) continue;
          var imp = nodeImportance(((Node**)u->c)[i]);
          if(imp > bestImp){ bestImp=imp; bestI=i; }
        }
        if(bestI>=0 && bestI<16){ mark[bestI]=1; }
      }
      for(i=n-1;i>=0;i--) if(i<16 && mark[i]==0) freeChildAt(u,i);
      return;
    }
    int j; for(j=0;j<u->n;j++) pruneSelectiveAtDepth(((Node**)u->c)[j],targetDepth,keepK);
  }
};

// ======================================================================
// ======================== RuntimeManager (component) ===================

class RuntimeManager {
public:
  int MemFixedBytes = 0;
  int TreeBytesCached = 0;

  int ShedStage = 0;
  int LastDepthActBar = -999999;
  int ChartsOff = 0;
  int LogsOff = 0;
  int RT_TreeMaxDepth = MAX_DEPTH;

  var ACC_mx=0, ACC_my=0, ACC_mx2=0, ACC_my2=0, ACC_mxy=0;
  var AccCorr=0;
  var AccBase=0;
  int HaveBase=0;

  var UtilBefore=0, UtilAfter=0;
  int TunePending=0;
  int TuneStartBar=0;
  int TuneAction=0;
  var DTreeExpStep = 0.05;
  int DTreeExpDir = 1;

public:
  int mem_bytes_est() const { return MemFixedBytes + TreeBytesCached; }
  int mem_mb_est() const { return mem_bytes_est()/(1024*1024); }

  void recalcTreeBytes(DTree& dt){ TreeBytesCached = dt.tree_bytes(dt.Root); }

  void computeMemFixedBytes(const NetState& net, const DTree& dt, int includeExprBytes){
    int N=net.N, D=net.D, K=net.K;
    int SZV=sizeof(var), SZF=sizeof(fvar), SZI16=sizeof(i16), SZI8=sizeof(i8), SZP=sizeof(void*);
    int b=0;

    b += N*SZV*2;
    b += N*SZV;

    b += N*D*SZI16;
    b += N*SZI16;
    b += N*SZI8;

    b += K*N*SZF;
    b += K*SZF;

    b += N*SZF*(8);
    b += N*SZF*(7+7);
    b += N*SZF*(2+2);
    b += N*SZF*(2);
    b += N*SZF*(1);

    b += N*(SZI16 + SZF);
    b += N*SZF*2;

    b += N*SZF;
    b += N*SZF;
    b += N*sizeof(int);

    b += dt.TreeCap*SZP;

    if(includeExprBytes) b += N*EXPR_MAXLEN;

    MemFixedBytes = b;
  }

  void shed_zero_cost_once(){
    if(ShedStage > 0) return;
    set(PLOTNOW|OFF);
    ChartsOff = 1;
    LogsOff = 1;
    ShedStage = 1;
  }

  void acc_update(var x, var y){
    var a=0.01;
    ACC_mx=(1-a)*ACC_mx + a*x;
    ACC_my=(1-a)*ACC_my + a*y;
    ACC_mx2=(1-a)*ACC_mx2 + a*(x*x);
    ACC_my2=(1-a)*ACC_my2 + a*(y*y);
    ACC_mxy=(1-a)*ACC_mxy + a*(x*y);
    var vx = ACC_mx2 - ACC_mx*ACC_mx;
    var vy = ACC_my2 - ACC_my*ACC_my;
    var cv = ACC_mxy - ACC_mx*ACC_my;
    if(vx>0 && vy>0) AccCorr = cv / sqrt(vx*vy);
    else AccCorr = 0;
    if(!HaveBase){ AccBase=AccCorr; HaveBase=1; }
  }

  var util_now(){
    int mb = mem_mb_est();
    var mem_pen = 0;
    if(mb > MEM_BUDGET_MB) mem_pen = (mb - MEM_BUDGET_MB)/(var)MEM_BUDGET_MB;
    return AccCorr - 0.5*mem_pen;
  }

  void depth_manager_runtime(DTree& dt){
    int trigger = MEM_BUDGET_MB - MEM_HEADROOM_MB;
    int mb = mem_mb_est();
    if(mb < trigger) return;

    if(ShedStage == 0) shed_zero_cost_once();
    if(ShedStage <= 1) ShedStage = 2;

    int overBudget = (mb >= MEM_BUDGET_MB);
    if(!overBudget && (Bar - LastDepthActBar < DEPTH_STEP_BARS)) return;

    while(RT_TreeMaxDepth > RUNTIME_MIN_DEPTH) {
      int keepK = ifelse(mem_mb_est() < MEM_BUDGET_MB + 2, KEEP_CHILDREN_HI, KEEP_CHILDREN_LO);
      dt.pruneSelectiveAtDepth(dt.Root, RT_TreeMaxDepth, keepK);
      RT_TreeMaxDepth--;
      mb = mem_mb_est();
      printf("\n[DepthMgr] depth=%i keepK=%i est=%i MB", RT_TreeMaxDepth, keepK, mb);
      if(mb < trigger) break;
    }
    LastDepthActBar = Bar;
  }
};

// ======================================================================
//  Alpha12: OpenCL replacement for CUDA advisor (FULL DROP-IN SECTION)
//  ? No CL/cl.h
//  ? No OpenCL.lib
//  ? Uses LoadLibrary("OpenCL.dll") + GetProcAddress
// ======================================================================

#define A12_USE_OPENCL 1
#define A12_CL_DEBUG   1

#if A12_USE_OPENCL

typedef int                 cl_int;
typedef unsigned int        cl_uint;
typedef unsigned long long  cl_ulong;
typedef unsigned long       cl_bitfield;
typedef cl_bitfield         cl_device_type;
typedef cl_bitfield         cl_mem_flags;
typedef cl_uint             cl_bool;

typedef struct _cl_platform_id*   cl_platform_id;
typedef struct _cl_device_id*     cl_device_id;
typedef struct _cl_context*       cl_context;
typedef struct _cl_command_queue* cl_command_queue;
typedef struct _cl_program*       cl_program;
typedef struct _cl_kernel*        cl_kernel;
typedef struct _cl_mem*           cl_mem;

#define CL_SUCCESS               0
#define CL_TRUE                  1

#define CL_DEVICE_TYPE_ALL       (1ULL << 0)
#define CL_DEVICE_TYPE_GPU       (1ULL << 2)

#define CL_MEM_READ_ONLY         (1ULL << 2)
#define CL_MEM_WRITE_ONLY        (1ULL << 1)

#define CL_PROGRAM_BUILD_LOG     0x1183

typedef cl_int   (__stdcall *PFN_clGetPlatformIDs)(cl_uint, cl_platform_id*, cl_uint*);
typedef cl_int   (__stdcall *PFN_clGetDeviceIDs)(cl_platform_id, cl_device_type, cl_uint, cl_device_id*, cl_uint*);
typedef cl_context (__stdcall *PFN_clCreateContext)(const void*, cl_uint, const cl_device_id*, void*, void*, cl_int*);
typedef cl_command_queue (__stdcall *PFN_clCreateCommandQueue)(cl_context, cl_device_id, cl_bitfield, cl_int*);
typedef cl_program (__stdcall *PFN_clCreateProgramWithSource)(cl_context, cl_uint, const char**, const size_t*, cl_int*);
typedef cl_int   (__stdcall *PFN_clBuildProgram)(cl_program, cl_uint, const cl_device_id*, const char*, void*, void*);
typedef cl_int   (__stdcall *PFN_clGetProgramBuildInfo)(cl_program, cl_device_id, cl_uint, size_t, void*, size_t*);
typedef cl_kernel(__stdcall *PFN_clCreateKernel)(cl_program, const char*, cl_int*);
typedef cl_mem   (__stdcall *PFN_clCreateBuffer)(cl_context, cl_mem_flags, size_t, void*, cl_int*);
typedef cl_int   (__stdcall *PFN_clSetKernelArg)(cl_kernel, cl_uint, size_t, const void*);
typedef cl_int   (__stdcall *PFN_clEnqueueWriteBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, const void*, cl_uint, const void*, void*);
typedef cl_int   (__stdcall *PFN_clEnqueueNDRangeKernel)(cl_command_queue, cl_kernel, cl_uint, const size_t*, const size_t*, const size_t*, cl_uint, const void*, void*);
typedef cl_int   (__stdcall *PFN_clFinish)(cl_command_queue);
typedef cl_int   (__stdcall *PFN_clEnqueueReadBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, void*, cl_uint, const void*, void*);

typedef cl_int   (__stdcall *PFN_clReleaseMemObject)(cl_mem);
typedef cl_int   (__stdcall *PFN_clReleaseKernel)(cl_kernel);
typedef cl_int   (__stdcall *PFN_clReleaseProgram)(cl_program);
typedef cl_int   (__stdcall *PFN_clReleaseCommandQueue)(cl_command_queue);
typedef cl_int   (__stdcall *PFN_clReleaseContext)(cl_context);

static HMODULE gA12CL_Dll = 0;

static PFN_clGetPlatformIDs          pA12_clGetPlatformIDs = 0;
static PFN_clGetDeviceIDs            pA12_clGetDeviceIDs = 0;
static PFN_clCreateContext           pA12_clCreateContext = 0;
static PFN_clCreateCommandQueue      pA12_clCreateCommandQueue = 0;
static PFN_clCreateProgramWithSource pA12_clCreateProgramWithSource = 0;
static PFN_clBuildProgram            pA12_clBuildProgram = 0;
static PFN_clGetProgramBuildInfo     pA12_clGetProgramBuildInfo = 0;
static PFN_clCreateKernel            pA12_clCreateKernel = 0;
static PFN_clCreateBuffer            pA12_clCreateBuffer = 0;
static PFN_clSetKernelArg            pA12_clSetKernelArg = 0;
static PFN_clEnqueueWriteBuffer      pA12_clEnqueueWriteBuffer = 0;
static PFN_clEnqueueNDRangeKernel    pA12_clEnqueueNDRangeKernel = 0;
static PFN_clFinish                  pA12_clFinish = 0;
static PFN_clEnqueueReadBuffer       pA12_clEnqueueReadBuffer = 0;

static PFN_clReleaseMemObject        pA12_clReleaseMemObject = 0;
static PFN_clReleaseKernel           pA12_clReleaseKernel = 0;
static PFN_clReleaseProgram          pA12_clReleaseProgram = 0;
static PFN_clReleaseCommandQueue     pA12_clReleaseCommandQueue = 0;
static PFN_clReleaseContext          pA12_clReleaseContext = 0;

static FARPROC a12_cl_sym(const char* name)
{
  if(!gA12CL_Dll) return 0;
  return GetProcAddress(gA12CL_Dll, name);
}

static int a12_cl_load()
{
  gA12CL_Dll = LoadLibraryA("OpenCL.dll");
  if(!gA12CL_Dll) return 0;

  pA12_clGetPlatformIDs          = (PFN_clGetPlatformIDs)a12_cl_sym("clGetPlatformIDs");
  pA12_clGetDeviceIDs            = (PFN_clGetDeviceIDs)a12_cl_sym("clGetDeviceIDs");
  pA12_clCreateContext           = (PFN_clCreateContext)a12_cl_sym("clCreateContext");
  pA12_clCreateCommandQueue      = (PFN_clCreateCommandQueue)a12_cl_sym("clCreateCommandQueue");
  pA12_clCreateProgramWithSource = (PFN_clCreateProgramWithSource)a12_cl_sym("clCreateProgramWithSource");
  pA12_clBuildProgram            = (PFN_clBuildProgram)a12_cl_sym("clBuildProgram");
  pA12_clGetProgramBuildInfo     = (PFN_clGetProgramBuildInfo)a12_cl_sym("clGetProgramBuildInfo");
  pA12_clCreateKernel            = (PFN_clCreateKernel)a12_cl_sym("clCreateKernel");
  pA12_clCreateBuffer            = (PFN_clCreateBuffer)a12_cl_sym("clCreateBuffer");
  pA12_clSetKernelArg            = (PFN_clSetKernelArg)a12_cl_sym("clSetKernelArg");
  pA12_clEnqueueWriteBuffer      = (PFN_clEnqueueWriteBuffer)a12_cl_sym("clEnqueueWriteBuffer");
  pA12_clEnqueueNDRangeKernel    = (PFN_clEnqueueNDRangeKernel)a12_cl_sym("clEnqueueNDRangeKernel");
  pA12_clFinish                  = (PFN_clFinish)a12_cl_sym("clFinish");
  pA12_clEnqueueReadBuffer       = (PFN_clEnqueueReadBuffer)a12_cl_sym("clEnqueueReadBuffer");

  pA12_clReleaseMemObject        = (PFN_clReleaseMemObject)a12_cl_sym("clReleaseMemObject");
  pA12_clReleaseKernel           = (PFN_clReleaseKernel)a12_cl_sym("clReleaseKernel");
  pA12_clReleaseProgram          = (PFN_clReleaseProgram)a12_cl_sym("clReleaseProgram");
  pA12_clReleaseCommandQueue     = (PFN_clReleaseCommandQueue)a12_cl_sym("clReleaseCommandQueue");
  pA12_clReleaseContext          = (PFN_clReleaseContext)a12_cl_sym("clReleaseContext");

  if(!pA12_clGetPlatformIDs || !pA12_clGetDeviceIDs || !pA12_clCreateContext || !pA12_clCreateCommandQueue ||
     !pA12_clCreateProgramWithSource || !pA12_clBuildProgram || !pA12_clCreateKernel || !pA12_clCreateBuffer ||
     !pA12_clSetKernelArg || !pA12_clEnqueueWriteBuffer || !pA12_clEnqueueNDRangeKernel || !pA12_clFinish ||
     !pA12_clEnqueueReadBuffer ||
     !pA12_clReleaseMemObject || !pA12_clReleaseKernel || !pA12_clReleaseProgram ||
     !pA12_clReleaseCommandQueue || !pA12_clReleaseContext)
  {
    FreeLibrary(gA12CL_Dll);
    gA12CL_Dll = 0;
    return 0;
  }

  return 1;
}

#endif // A12_USE_OPENCL

class OpenCLAdvisor {
public:
#if A12_USE_OPENCL
  int ready = 0;

  cl_platform_id   platform = 0;
  cl_device_id     device   = 0;
  cl_context       context  = 0;
  cl_command_queue queue    = 0;
  cl_program       program  = 0;
  cl_kernel        kernel   = 0;

  cl_mem dX   = 0; // float[maxBatch * nf]
  cl_mem dOut = 0; // float[maxBatch]
  cl_mem dW   = 0; // float[nf]
  cl_mem dB   = 0; // float[1]

  int maxBatch = 0;
  int nf = 0;

  float* hX   = 0;
  float* hOut = 0;
  float* hW   = 0;
  float  hB   = 0;

  const char* kSrc =
  "__kernel void advise_kernel(__global const float* X, __global const float* W, __global const float* B,\n"
  "                            __global float* out, int nf, int batch)\n"
  "{\n"
  "  int i = (int)get_global_id(0);\n"
  "  if(i >= batch) return;\n"
  "  __global const float* x = X + i*nf;\n"
  "  float acc = 0.0f;\n"
  "  for(int k=0;k<nf;k++) acc += x[k]*W[k];\n"
  "  acc += B[0];\n"
  "  float y = acc;\n"
  "  if(y > 5.0f) y = 5.0f;\n"
  "  if(y < -5.0f) y = -5.0f;\n"
  "  float y2 = y*y;\n"
  "  float t = y*(27.0f + y2)/(27.0f + 9.0f*y2);\n"
  "  if(t > 1.0f) t = 1.0f;\n"
  "  if(t < -1.0f) t = -1.0f;\n"
  "  out[i] = t;\n"
  "}\n";

  int isReady() const { return ready; }
  float* hostX()   { return hX; }
  float* hostOut() { return hOut; }
  int cap() const { return maxBatch; }
  int featN() const { return nf; }

  void shutdown() { release_all(); }

  int init(int nfIn, int maxBatchIn)
  {
    shutdown();

    nf = nfIn;
    maxBatch = maxBatchIn;
    if(nf <= 0 || maxBatch <= 0) return 0;

    if(!a12_cl_load()) { release_all(); return 0; }

    cl_int err = CL_SUCCESS;

    cl_uint nPlatforms = 0;
    err = pA12_clGetPlatformIDs(0, 0, &nPlatforms);
    if(err != CL_SUCCESS || nPlatforms == 0) { release_all(); return 0; }

    cl_platform_id plats[8];
    if(nPlatforms > 8) nPlatforms = 8;
    err = pA12_clGetPlatformIDs(nPlatforms, plats, &nPlatforms);
    if(err != CL_SUCCESS) { release_all(); return 0; }

    cl_platform_id chosenP = 0;
    cl_device_id   chosenD = 0;

    for(cl_uint p=0; p<nPlatforms && !chosenD; p++){
      cl_uint nDev = 0;
      if(pA12_clGetDeviceIDs(plats[p], CL_DEVICE_TYPE_GPU, 0, 0, &nDev) == CL_SUCCESS && nDev > 0){
        cl_device_id devs[8];
        if(nDev > 8) nDev = 8;
        if(pA12_clGetDeviceIDs(plats[p], CL_DEVICE_TYPE_GPU, nDev, devs, &nDev) == CL_SUCCESS){
          chosenP = plats[p];
          chosenD = devs[0];
        }
      }
    }
    if(!chosenD){
      for(cl_uint p=0; p<nPlatforms && !chosenD; p++){
        cl_uint nDev = 0;
        if(pA12_clGetDeviceIDs(plats[p], CL_DEVICE_TYPE_ALL, 0, 0, &nDev) == CL_SUCCESS && nDev > 0){
          cl_device_id devs[8];
          if(nDev > 8) nDev = 8;
          if(pA12_clGetDeviceIDs(plats[p], CL_DEVICE_TYPE_ALL, nDev, devs, &nDev) == CL_SUCCESS){
            chosenP = plats[p];
            chosenD = devs[0];
          }
        }
      }
    }

    if(!chosenD) { release_all(); return 0; }

    platform = chosenP;
    device   = chosenD;

    context = pA12_clCreateContext(0, 1, &device, 0, 0, &err);
    if(err != CL_SUCCESS || !context) { release_all(); return 0; }

    queue = pA12_clCreateCommandQueue(context, device, 0, &err);
    if(err != CL_SUCCESS || !queue) { release_all(); return 0; }

    program = pA12_clCreateProgramWithSource(context, 1, &kSrc, 0, &err);
    if(err != CL_SUCCESS || !program) { release_all(); return 0; }

    err = pA12_clBuildProgram(program, 1, &device, 0, 0, 0);
    if(err != CL_SUCCESS){
      if(pA12_clGetProgramBuildInfo){
        char logbuf[4096] = {0};
        size_t logsz = 0;
        pA12_clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, sizeof(logbuf), logbuf, &logsz);
        printf("\n[OpenCL-advise] build failed: %s", logbuf);
      }
      release_all();
      return 0;
    }

    kernel = pA12_clCreateKernel(program, "advise_kernel", &err);
    if(err != CL_SUCCESS || !kernel) { release_all(); return 0; }

    dX = pA12_clCreateBuffer(context, CL_MEM_READ_ONLY,
      sizeof(float)*(size_t)maxBatch*(size_t)nf, 0, &err);
    if(err != CL_SUCCESS || !dX) { release_all(); return 0; }

    dOut = pA12_clCreateBuffer(context, CL_MEM_WRITE_ONLY,
      sizeof(float)*(size_t)maxBatch, 0, &err);
    if(err != CL_SUCCESS || !dOut) { release_all(); return 0; }

    dW = pA12_clCreateBuffer(context, CL_MEM_READ_ONLY,
      sizeof(float)*(size_t)nf, 0, &err);
    if(err != CL_SUCCESS || !dW) { release_all(); return 0; }

    dB = pA12_clCreateBuffer(context, CL_MEM_READ_ONLY,
      sizeof(float), 0, &err);
    if(err != CL_SUCCESS || !dB) { release_all(); return 0; }

    hX   = (float*)malloc(sizeof(float)*(size_t)maxBatch*(size_t)nf);
    hOut = (float*)malloc(sizeof(float)*(size_t)maxBatch);
    hW   = (float*)malloc(sizeof(float)*(size_t)nf);
    if(!hX || !hOut || !hW) { release_all(); return 0; }

    load_weights_from_file("Data\\Alpha12_cuda_w.bin");

    err = pA12_clEnqueueWriteBuffer(queue, dW, CL_TRUE, 0, sizeof(float)*(size_t)nf, hW, 0, 0, 0);
    if(err != CL_SUCCESS) { release_all(); return 0; }

    err = pA12_clEnqueueWriteBuffer(queue, dB, CL_TRUE, 0, sizeof(float), &hB, 0, 0, 0);
    if(err != CL_SUCCESS) { release_all(); return 0; }

    ready = 1;
#if A12_CL_DEBUG
    printf("\n[OpenCL-advise] init OK (nf=%d maxBatch=%d)", nf, maxBatch);
#endif
    return 1;
  }

  int inferBatch(const float* X, int batch, float* out)
  {
    if(!ready) return 0;
    if(!X || !out) return 0;
    if(batch <= 0) return 1;
    if(batch > maxBatch) batch = maxBatch;

    cl_int err = CL_SUCCESS;

    size_t bytesX = sizeof(float)*(size_t)batch*(size_t)nf;
    size_t bytesO = sizeof(float)*(size_t)batch;

    err = pA12_clEnqueueWriteBuffer(queue, dX, CL_TRUE, 0, bytesX, X, 0, 0, 0);
    if(err != CL_SUCCESS) { ready = 0; return 0; }

    int arg = 0;
    err  = pA12_clSetKernelArg(kernel, arg++, sizeof(cl_mem), &dX);
    err |= pA12_clSetKernelArg(kernel, arg++, sizeof(cl_mem), &dW);
    err |= pA12_clSetKernelArg(kernel, arg++, sizeof(cl_mem), &dB);
    err |= pA12_clSetKernelArg(kernel, arg++, sizeof(cl_mem), &dOut);
    err |= pA12_clSetKernelArg(kernel, arg++, sizeof(int), &nf);
    err |= pA12_clSetKernelArg(kernel, arg++, sizeof(int), &batch);
    if(err != CL_SUCCESS) { ready = 0; return 0; }

    size_t global = (size_t)batch;
    size_t local  = 128;
    if(local > global) local = 1;

    err = pA12_clEnqueueNDRangeKernel(queue, kernel, 1, 0, &global, &local, 0, 0, 0);
    if(err != CL_SUCCESS) { ready = 0; return 0; }

    err = pA12_clFinish(queue);
    if(err != CL_SUCCESS) { ready = 0; return 0; }

    err = pA12_clEnqueueReadBuffer(queue, dOut, CL_TRUE, 0, bytesO, out, 0, 0, 0);
    if(err != CL_SUCCESS) { ready = 0; return 0; }

    return 1;
  }

private:
  int load_weights_from_file(const char* path)
  {
    if(!hW) return 0;
    FILE* f = fopen(path, "rb");
    if(!f) {
      for(int i=0;i<nf;i++) hW[i] = 0.0f;
      hB = 0.0f;
      return 0;
    }

    size_t want = (size_t)(nf + 1);
    float* tmp = (float*)malloc(want*sizeof(float));
    if(!tmp) { fclose(f); return 0; }

    size_t got = fread(tmp, sizeof(float), want, f);
    fclose(f);

    if(got >= (size_t)nf) {
      for(int i=0;i<nf;i++) hW[i] = tmp[i];
      if(got >= (size_t)(nf+1)) hB = tmp[nf];
      else hB = 0.0f;
      free(tmp);
      return 1;
    }

    free(tmp);
    return 0;
  }

  void release_all()
  {
    if(dB && pA12_clReleaseMemObject)   { pA12_clReleaseMemObject(dB); dB=0; }
    if(dW && pA12_clReleaseMemObject)   { pA12_clReleaseMemObject(dW); dW=0; }
    if(dOut && pA12_clReleaseMemObject) { pA12_clReleaseMemObject(dOut); dOut=0; }
    if(dX && pA12_clReleaseMemObject)   { pA12_clReleaseMemObject(dX); dX=0; }

    if(kernel && pA12_clReleaseKernel)  { pA12_clReleaseKernel(kernel); kernel=0; }
    if(program && pA12_clReleaseProgram){ pA12_clReleaseProgram(program); program=0; }
    if(queue && pA12_clReleaseCommandQueue){ pA12_clReleaseCommandQueue(queue); queue=0; }
    if(context && pA12_clReleaseContext){ pA12_clReleaseContext(context); context=0; }

    platform = 0; device = 0;

    if(hOut) { free(hOut); hOut=0; }
    if(hX)   { free(hX);   hX=0; }
    if(hW)   { free(hW);   hW=0; }

    ready = 0;

    if(gA12CL_Dll) { FreeLibrary(gA12CL_Dll); gA12CL_Dll=0; }
  }

#else
  int isReady() const { return 0; }
  float* hostX()   { return 0; }
  float* hostOut() { return 0; }
  int cap() const { return 0; }
  int featN() const { return 0; }
  void shutdown() {}
  int init(int, int) { return 0; }
  int inferBatch(const float*, int, float*) { return 0; }
#endif
};

// ======================================================================
// ========================= Alpha12Strategy (owner) =====================

class Alpha12Strategy {
public:
  NodePool        pool;
  DTree           dt;
  NetState        net;
  Alpha12Logger   log;
  RuntimeManager  rt;

  MarkovChain MH;
  MarkovChain ML;
  MarkovChain MR;

  var CDL_L[MC_NPAT];
  var CDL_H[MC_NPAT];

  int ready = 0;
  int Epoch = 0;
  int CtxID = 0;

  var FB_W = 0.70;
  var MC_ACT_dyn = MC_ACT;
  var MC_Alpha = 1.0;
  int CandNeigh = CAND_NEIGH;

  int RewirePos = 0;
  int RewirePasses = 0;
  int UpdatePos = 0;
  int UpdatePasses = 0;

  var LastSig = 0;

  int AdviseMax = 16;

  int seedBar = -1;
  int haveSeed[NET_EQNS];
  var seedVal[NET_EQNS];

  // ---- OpenCL advisor ----
  OpenCLAdvisor ocl;
  int OclReady = 0;

  int  AdvCacheBar = -999999;
  i8   AdvHave[NET_EQNS];
  fvar AdvCache[NET_EQNS];

public:
  Alpha12Strategy() {
    dt.bindPool(&pool);

    memset(haveSeed,0,sizeof(haveSeed));
    memset(seedVal,0,sizeof(seedVal));

    AdvCacheBar = -999999;
    memset(AdvHave,0,sizeof(AdvHave));
    memset(AdvCache,0,sizeof(AdvCache));
  }

  ~Alpha12Strategy(){ cleanup(); }

  // --------------------- utilities ---------------------
  static var randsign(){ return ifelse(random(1) < 0.5, -1.0, 1.0); }
  static var mapUnit(var u,var lo,var hi){
    if(u<-1) u=-1;
    if(u>1) u=1;
    var t=0.5*(u+1.0);
    return lo + t*(hi-lo);
  }
  static var safeNum(var x){ if(invalid(x)) return 0; return clamp(x,-1e100,1e100); }
  static void sanitize(var* A,int n){ int k; for(k=0;k<n;k++) A[k]=safeNum(A[k]); }
  static var sat100(var x){ return clamp(x,-100.,100.); }
  static var nrm_s(var x) { return sat100(100.*tanh(x)); }
  static var nrm_scl(var x, var s) { return sat100(100.*tanh(s*x)); }

  // --------------- Candlestick pattern builder ------------
  int buildCDL_TA61(var* out, string* names)
  {
    int n = 0;
    #define ADD(Name, Call) do{ var v = (Call); if(out) out[n] = v/100.; if(names) names[n] = Name; n++; }while(0)

    ADD("CDL2Crows",              CDL2Crows());
    ADD("CDL3BlackCrows",         CDL3BlackCrows());
    ADD("CDL3Inside",             CDL3Inside());
    ADD("CDL3LineStrike",         CDL3LineStrike());
    ADD("CDL3Outside",            CDL3Outside());
    ADD("CDL3StarsInSouth",       CDL3StarsInSouth());
    ADD("CDL3WhiteSoldiers",      CDL3WhiteSoldiers());
    ADD("CDLAbandonedBaby",       CDLAbandonedBaby(0.3));
    ADD("CDLAdvanceBlock",        CDLAdvanceBlock());
    ADD("CDLBeltHold",            CDLBeltHold());
    ADD("CDLBreakaway",           CDLBreakaway());
    ADD("CDLClosingMarubozu",     CDLClosingMarubozu());

    ADD("CDLConcealingBabySwallow", A12_CDL_CONCEALING_BABY_SWALLOW());

    ADD("CDLCounterAttack",       CDLCounterAttack());
    ADD("CDLDarkCloudCover",      CDLDarkCloudCover(0.3));
    ADD("CDLDoji",                CDLDoji());
    ADD("CDLDojiStar",            CDLDojiStar());
    ADD("CDLDragonflyDoji",       CDLDragonflyDoji());
    ADD("CDLEngulfing",           CDLEngulfing());
    ADD("CDLEveningDojiStar",     CDLEveningDojiStar(0.3));
    ADD("CDLEveningStar",         CDLEveningStar(0.3));
    ADD("CDLGapSideSideWhite",    CDLGapSideSideWhite());
    ADD("CDLGravestoneDoji",      CDLGravestoneDoji());
    ADD("CDLHammer",              CDLHammer());
    ADD("CDLHangingMan",          CDLHangingMan());
    ADD("CDLHarami",              CDLHarami());
    ADD("CDLHaramiCross",         CDLHaramiCross());

    ADD("CDLHighWave",            A12_CDL_HIGH_WAVE());

    ADD("CDLHikkake",             CDLHikkake());
    ADD("CDLHikkakeMod",          CDLHikkakeMod());
    ADD("CDLHomingPigeon",        CDLHomingPigeon());
    ADD("CDLIdentical3Crows",     CDLIdentical3Crows());
    ADD("CDLInNeck",              CDLInNeck());
    ADD("CDLInvertedHammer",      CDLInvertedHammer());
    ADD("CDLKicking",             CDLKicking());
    ADD("CDLKickingByLength",     CDLKickingByLength());
    ADD("CDLLadderBottom",        CDLLadderBottom());
    ADD("CDLLongLeggedDoji",      CDLLongLeggedDoji());
    ADD("CDLLongLine",            CDLLongLine());
    ADD("CDLMarubozu",            CDLMarubozu());
    ADD("CDLMatchingLow",         CDLMatchingLow());
    ADD("CDLMatHold",             CDLMatHold(0.5));
    ADD("CDLMorningDojiStar",     CDLMorningDojiStar(0.3));
    ADD("CDLMorningStar",         CDLMorningStar(0.3));
    ADD("CDLOnNeck",              CDLOnNeck());
    ADD("CDLPiercing",            CDLPiercing());

    ADD("CDLRickshawMan",         CDL_RickshawMan());
    ADD("CDLRiseFall3Methods",    CDL_RiseFall3Methods());

    ADD("CDLSeparatingLines",     A12_CDL_SEPARATING_LINES());

    ADD("CDLShootingStar",        CDL_ShootingStar());
    ADD("CDLShortLine",           CDL_ShortLine());
    ADD("CDLSpinningTop",         CDL_SpinningTop());
    ADD("CDLStalledPattern",      CDL_StalledPattern());

    ADD("CDLStickSandwich",       A12_CDL_STICKSANDWICH());

    ADD("CDLTakuri",              CDL_Takuri());
    ADD("CDLTasukiGap",           CDL_TasukiGap());
    ADD("CDLThrusting",           CDL_Thrusting());
    ADD("CDLTristar",             CDL_Tristar());
    ADD("CDLUnique3River",        CDL_Unique3River());
    ADD("CDLUpsideGap2Crows",     CDLUpsideGap2Crows());
    ADD("CDLXSideGap3Methods",    CDL_XSideGap3Methods());

    #undef ADD
    return n;
  }

  // underscore helpers
  var CDL_RickshawMan()        { return CDLRickshawMan(); }
  var CDL_RiseFall3Methods()   { return CDLRiseFall3Methods(); }
  var CDL_ShootingStar()       { return CDLShootingStar(); }
  var CDL_ShortLine()          { return CDLShortLine(); }
  var CDL_SpinningTop()        { return CDLSpinningTop(); }
  var CDL_StalledPattern()     { return CDLStalledPattern(); }
  var CDL_Takuri()             { return CDLTakuri(); }
  var CDL_TasukiGap()          { return CDLTasukiGap(); }
  var CDL_Thrusting()          { return CDLThrusting(); }
  var CDL_Tristar()            { return CDLTristar(); }
  var CDL_Unique3River()       { return CDLUnique3River(); }
  var CDL_XSideGap3Methods()   { return CDLXSideGap3Methods(); }

  // --------------- Markov relation mapping ----------------
  int relFromHL(int sL, int sH){
    if(sL <= 0 || sH <= 0) return MC_NONE;
    int idxL = (sL - 1)/2; int bullL = ((sL - 1)%2)==1;
    int idxH = (sH - 1)/2; int bullH = ((sH - 1)%2)==1;
    if(idxL == idxH && bullL == bullH) return sL;
    return MC_NONE;
  }

  int is_H1_close(){ return (Bar % TF_H1) == 0; }

  // ------------------ memory estimator integration --------------------
  int mem_mb_est() { return rt.mem_mb_est(); }

  // =================== advice cache helpers ======================
  void resetAdvCacheForBar() {
    if(AdvCacheBar != Bar) {
      AdvCacheBar = Bar;
      for(int k=0;k<net.N;k++) { AdvHave[k]=0; AdvCache[k]=0; }
    }
  }

  // --------------------- Advisor rotation ------------------
  int allowAdvise(int i){
    int groups = net.N / AdviseMax;
    if(groups < 1) groups = 1;
    return ((i / AdviseMax) % groups) == (Bar % groups);
  }

  // EXACT gating behavior preserved
  int adviseGate(int i) {
    if(!allowAdvise(i)) return 0;
    if(is(INITRUN)) return 0;

    int tight = (mem_mb_est() >= MEM_BUDGET_MB - MEM_HEADROOM_MB);
    if(tight) return 0;

    if(net.HitN[i] > 32) {
      var h = (var)net.HitEW[i];
      var gate = 0.40 + 0.15*(1.0 - MH.Entropy);
      if(h < gate) {
        if(random() >= 0.5) return 0;
      }
    }
    return 1;
  }

  // ------------------- Feature builders -------------------
  static var mix01(var a, int salt){
    var z = sin(123.456*a + 0.001*salt) + cos(98.765*a + 0.002*salt);
    return tanh(0.75*z);
  }
  static var mapA(var a,var lo,var hi){ return mapUnit(a,lo,hi); }

  void buildEqFeatures(int i, var lambda, var mean, var energy, var power, var pred, var* S /*ADV_EQ_NF*/) {
    int tid = dt.safeTreeIndexFromEq((int)net.EqTreeId[i], dt.TreeN);
    Node* t = dt.treeAt(tid);

    var th_i = (dt.EqTheta ? dt.EqTheta[i] : 0);
    var dphi = DTree::angDiff(dt.CycPh, th_i);
    var alignC = cos(dphi);
    var alignS = sin(dphi);

    S[0]  = nrm_s(net.State[i]);
    S[1]  = nrm_s(mean);
    S[2]  = nrm_scl(power,0.05);
    S[3]  = nrm_scl(energy,0.01);
    S[4]  = nrm_s(lambda);
    S[5]  = sat100(200.0*(pred-0.5));
    S[6]  = sat100(200.0*((var)t->d/MAX_DEPTH)-100.0);
    S[7]  = sat100(1000.0*t->r);
    S[8]  = nrm_s((var)net.TreeTerm[i]);
    S[9]  = sat100( (200.0/3.0) * (var)((int)net.Mode[i]) - 100.0 );

    S[10] = sat100(200.0*(MH.PBullNext-0.5));
    S[11] = sat100(200.0*(MH.Entropy-0.5));

    S[12] = sat100(200.0*((var)net.HitEW[i] - 0.5));
    S[13] = sat100(100.*alignC);
    S[14] = sat100(100.*alignS);

    S[15] = sat100(200.0*(ML.PBullNext - 0.5));
    S[16] = sat100(200.0*(ML.Entropy   - 0.5));
    S[17] = sat100(200.0*(MR.PBullNext - 0.5));
    S[18] = sat100(200.0*(MR.Entropy   - 0.5));

    sanitize(S,ADV_EQ_NF);
  }

  // Build a batch over [i0,i1), run OpenCL once, populate AdvCache for those i.
  void computeAdviceBatchRange(int i0, int i1, var lambda, var mean, var energy, var power) {
    resetAdvCacheForBar();
    if(!OclReady) return;

    if(i0 < 0) i0 = 0;
    if(i1 > net.N) i1 = net.N;
    if(i0 >= i1) return;

    float* X = ocl.hostX();
    float* O = ocl.hostOut();
    int nf   = ocl.featN();

    int idx[NET_EQNS];
    int bsz = 0;

    for(int i=i0;i<i1;i++) {
      if(AdvHave[i]) continue;

      if(!adviseGate(i)) {
        AdvHave[i]  = 1;
        AdvCache[i] = (fvar)0;
        continue;
      }

      int tid  = dt.safeTreeIndexFromEq((int)net.EqTreeId[i], dt.TreeN);
      var pred = dt.predByTid(tid);

      var S[ADV_EQ_NF];
      buildEqFeatures(i, lambda, mean, energy, power, pred, S);

      for(int k=0;k<ADV_EQ_NF;k++)
        X[bsz*nf + k] = (float)S[k];

      idx[bsz] = i;
      bsz++;

      if(bsz >= ocl.cap()) break;
    }

    if(bsz <= 0) return;

    if(!ocl.inferBatch(X, bsz, O)) {
      OclReady = 0;
      return;
    }

    for(int j=0;j<bsz;j++) {
      int eq = idx[j];
      var p = (var)O[j];
      p = clamp(p, -1.0, 1.0);

      AdvCache[eq] = (fvar)p;
      AdvHave[eq]  = 1;
    }
  }

  // OpenCL-backed adviseEq
  var adviseEq(int i, var lambda, var mean, var energy, var power) {
    if(!adviseGate(i)) return 0;

    resetAdvCacheForBar();
    if(AdvHave[i]) return (var)AdvCache[i];

    if(OclReady) {
      computeAdviceBatchRange(i, i+1, lambda, mean, energy, power);
      if(AdvHave[i]) return (var)AdvCache[i];
    }

    AdvHave[i]  = 1;
    AdvCache[i] = (fvar)0;
    return 0;
  }

  var adviseSeed(int i, var lambda, var mean, var energy, var power) {
    if(seedBar != Bar) {
      for(int k=0;k<net.N;k++) haveSeed[k]=0;
      seedBar = Bar;
    }
    if(i < 0) i = 0;
    if(i >= net.N) i = i % net.N;

    if(!allowAdvise(i)) return 0;

    if(!haveSeed[i]) {
      seedVal[i] = adviseEq(i, lambda, mean, energy, power);
      haveSeed[i] = 1;
    }
    return seedVal[i];
  }

  // --------------------- core tree reindex & mapping -------------------
  void reindexTreeAndMap(){
    dt.TreeN = 0;
    dt.indexTreeDFS(dt.Root);
    if(dt.TreeN <= 0){
      dt.TreeN = 1;
      if(dt.TreeIdx) dt.TreeIdx[0] = dt.Root;
    }
    { int i; for(i=0;i<net.N;i++) net.EqTreeId[i] = (i16)(i % dt.TreeN); }

    dt.resizePredCacheToTree();
    dt.refreshEqAngles(net.EqTreeId, net.N);
    dt.maybeShrinkTreeIdx();

    rt.recalcTreeBytes(dt);

    int includeExpr = (LOG_EXPR_TEXT && net.Sym && !net.SymFreed) ? 1 : 0;
    rt.computeMemFixedBytes(net, dt, includeExpr);

    rt.MemFixedBytes += 3*(MC_STATES*MC_STATES*(int)sizeof(int) + MC_STATES*(int)sizeof(int));
  }

  // --------------------- Markov updates --------------------------
  void updateMarkov_5M(){
    buildCDL_TA61(CDL_L, 0);

    int s = MarkovChain::stateFromCDL(CDL_L, MC_ACT_dyn);

    if(Bar > LookBack) ML.update(ML.Prev, s);
    ML.Prev = s;

    if(s > 0 && s < MC_STATES){
      if(ML.RowSum[s] > 0) ML.rowStats(s, &ML.PBullNext, &ML.Entropy);
      ML.Cur = s;
    }
  }

  void updateMarkov_1H(){
    int saveTF = TimeFrame;
    TimeFrame = TF_H1;

    buildCDL_TA61(CDL_H, 0);

    int sH = MarkovChain::stateFromCDL(CDL_H, MC_ACT_dyn);

    if(Bar > LookBack) MH.update(MH.Prev, sH);
    MH.Prev = sH;

    if(sH > 0 && sH < MC_STATES){
      if(MH.RowSum[sH] > 0) MH.rowStats(sH, &MH.PBullNext, &MH.Entropy);
      MH.Cur = sH;
    }

    TimeFrame = saveTF;
  }

  void updateMarkov_REL(){
    int r = relFromHL(ML.Cur, MH.Cur);
    if(Bar > LookBack) MR.update(MR.Prev, r);
    MR.Prev = r;

    if(r > 0 && r < MC_STATES){
      if(MR.RowSum[r] > 0) MR.rowStats(r, &MR.PBullNext, &MR.Entropy);
      MR.Cur = r;
    }
  }

  void updateAllMarkov(){
    MH.Alpha = MC_Alpha;
    ML.Alpha = MC_Alpha;
    MR.Alpha = MC_Alpha;

    updateMarkov_5M();
    if(is_H1_close()){
      updateMarkov_1H();
      updateMarkov_REL();
    }
  }

  // ------------------- adjacency scoring ------------------
  var scorePairSafe(int i, int j, var lambda, var mean, var energy, var power){
    int ti = dt.safeTreeIndexFromEq((int)net.EqTreeId[i], dt.TreeN);
    int tj = dt.safeTreeIndexFromEq((int)net.EqTreeId[j], dt.TreeN);
    Node* ni = dt.treeAt(ti);
    Node* nj = dt.treeAt(tj);

    var simD = 1.0 / (1.0 + abs((var)ni->d - (var)nj->d));
    var dr = 50.0*abs(ni->r - nj->r);
    var simR = 1.0 / (1.0 + dr);

    var predi = dt.predByTid(ti);
    var predj = dt.predByTid(tj);
    var pred  = 0.5*(predi + predj);

    var score = 0.5*pred + 0.3*simD + 0.2*simR;
    return 2.0*score - 1.0;
  }

  void rewireAdjacency_DTREE_range(int i0,int i1, var lambda, var mean, var energy, var power){
    int i,d,c,best,cand;
    if(i0<0) i0=0; if(i1>net.N) i1=net.N;
    for(i=i0;i<i1;i++){
      for(d=0; d<net.D; d++){
        var bestScore = -2; best=-1;
        for(c=0;c<CandNeigh;c++){
          cand = (int)random(net.N);
          if(cand==i) continue;
          int clash=0,k;
          for(k=0;k<d;k++){
            int prev = net.Adj[i*net.D + k];
            if(prev>=0 && prev==cand){ clash=1; break; }
          }
          if(clash) continue;
          var s = scorePairSafe(i,cand,lambda,mean,energy,power);
          if(s > bestScore){ bestScore=s; best=cand; }
        }
        if(best<0){ do{ best=(int)random(net.N);} while(best==i); }
        net.Adj[i*net.D + d] = (i16)best;
      }
    }
  }

  // ---------------- coefficient synthesis -------------------
  void synthesizeEquationFromDTREE(int i, var lambda, var mean, var energy, var power){
    var seed = adviseSeed(i,lambda,mean,energy,power);

    net.Mode[i] = (int)(abs(1000*seed)) & 3;

    net.WSelf[i]  = (fvar)mapA(mix01(seed, 11), 0.15, 0.85);
    net.WN1[i]    = (fvar)mapA(mix01(seed, 12), 0.05, 0.35);
    net.WN2[i]    = (fvar)mapA(mix01(seed, 13), 0.05, 0.35);
    net.WGlob1[i] = (fvar)mapA(mix01(seed, 14), 0.05, 0.30);
    net.WGlob2[i] = (fvar)mapA(mix01(seed, 15), 0.05, 0.30);
    net.WMom[i]   = (fvar)mapA(mix01(seed, 16), 0.02, 0.15);
    net.WTree[i]  = (fvar)mapA(mix01(seed, 17), 0.05, 0.35);
    net.WAdv[i]   = (fvar)mapA(mix01(seed, 18), 0.05, 0.35);

    net.A1x[i]   = (fvar)(randsign()*mapA(mix01(seed, 21), 0.6, 1.2));
    net.A1lam[i] = (fvar)(randsign()*mapA(mix01(seed, 22), 0.05,0.35));
    net.A1mean[i]= (fvar) mapA(mix01(seed, 23),-0.30,0.30);
    net.A1E[i]   = (fvar) mapA(mix01(seed, 24),-0.0015,0.0015);
    net.A1P[i]   = (fvar) mapA(mix01(seed, 25),-0.30,0.30);
    net.A1i[i]   = (fvar) mapA(mix01(seed, 26),-0.02,0.02);
    net.A1c[i]   = (fvar) mapA(mix01(seed, 27),-0.20,0.20);

    net.A2x[i]   = (fvar)(randsign()*mapA(mix01(seed, 31), 0.6, 1.2));
    net.A2lam[i] = (fvar)(randsign()*mapA(mix01(seed, 32), 0.05,0.35));
    net.A2mean[i]= (fvar) mapA(mix01(seed, 33),-0.30,0.30);
    net.A2E[i]   = (fvar) mapA(mix01(seed, 34),-0.0015,0.0015);
    net.A2P[i]   = (fvar) mapA(mix01(seed, 35),-0.30,0.30);
    net.A2i[i]   = (fvar) mapA(mix01(seed, 36),-0.02,0.02);
    net.A2c[i]   = (fvar) mapA(mix01(seed, 37),-0.20,0.20);

    net.G1mean[i] = (fvar) mapA(mix01(seed, 41), 0.4, 1.6);
    net.G1E[i]    = (fvar) mapA(mix01(seed, 42),-0.004,0.004);
    net.G2P[i]    = (fvar) mapA(mix01(seed, 43), 0.1, 1.2);
    net.G2lam[i]  = (fvar) mapA(mix01(seed, 44), 0.05, 0.7);

    net.TAlpha[i] = (fvar) mapA(mix01(seed, 51), 0.3, 1.5);
    net.TBeta[i]  = (fvar) mapA(mix01(seed, 52), 6.0, 50.0);

    net.PropRaw[i] = (fvar)(0.01 + 0.99*(0.5*(seed+1.0)));

    { var boost = 0.75 + 0.5*(var)net.HitEW[i];
      net.PropRaw[i] = (fvar)((var)net.PropRaw[i] * boost);
    }
  }

  void synthesizeEquation_range(int i0,int i1, var lambda, var mean, var energy, var power){
    if(i0<0) i0=0; if(i1>net.N) i1=net.N;
    int i;
    for(i=0;i<i1;i++){
      if(i < i0) continue;
      synthesizeEquationFromDTREE(i,lambda,mean,energy,power);
    }
  }

  // ------------------- DTREE ensemble term ------------------
  var dtreeTerm(int i, int* outTopEq, var* outTopW){
    int j;
    int tid_i = dt.safeTreeIndexFromEq((int)net.EqTreeId[i], dt.TreeN);
    Node* ti = dt.treeAt(tid_i);
    int di = ti->d; var ri=ti->

Last edited by TipmyPip; 02/05/26 12:01.