# انیمیشنهای بخش هفتم. این سلول را فقط یک بار اجرا کنید؛ همان پخشکنندهی بخش پنجم و ششم است.
# ============================================================
# پخشکنندهی تعاملی الگوریتمها (Algorithm Player)
# ============================================================
import json, re, hashlib, html as _html, uuid
from IPython.display import HTML
_AP_CSS = r''':host{all:initial;display:block;max-width:900px;margin:8px auto 18px}
*{box-sizing:border-box}
.card{--card:#fff;--panel:#f6f8fb;--bd:#e2e7ef;--tx:#182230;--mut:#6a7688;--acc:#4f46e5;--acc-soft:rgba(79,70,229,.10);
--stage:#fbfcfe;--dot:#dfe4ee;--code-bg:#fafbfd;--hl:rgba(79,70,229,.12);--ln:#a3adbd;--sh:0 1px 2px rgba(16,24,40,.06),0 14px 30px -18px rgba(16,24,40,.28);
--sb:rgba(100,116,139,.45);--sb-h:rgba(79,70,229,.65);color-scheme:light;--i-bg:#eef2ff;--i-bd:#98a6cf;--i-tx:#182230;--out-bg:#f6f8fa;--out-fg:#116329;--out-bd:#d0d7de;
--cur-bg:#fde68a;--cur-bd:#d9a20a;--cur-tx:#4a3200;--cur-gl:rgba(250,204,21,.38);
--cmp-bg:#fbcfe8;--cmp-bd:#e07bb0;--cmp-tx:#4a1230;--cmp-gl:rgba(236,72,153,.28);
--ok-bg:#bbf7d0;--ok-bd:#2fa863;--ok-tx:#0a3d1f;--ok-gl:rgba(34,197,94,.30);
--sorted-bg:#bfdbfe;--sorted-bd:#4f8ad9;--sorted-tx:#0f2a52;
--min-bg:#fecaca;--min-bd:#e05a5a;--min-tx:#501414;--min-gl:rgba(239,68,68,.28);
--key-bg:#ddd6fe;--key-bd:#7c5be0;--key-tx:#26155a;--key-gl:rgba(139,92,246,.30);
--dim-bg:#f0f2f6;--dim-bd:#d3d9e3;--dim-tx:#9aa4b3;
--vis-bg:#e0f2fe;--vis-bd:#7db9de;--vis-tx:#123a55;
--row-bg:#dbeafe;--row-bd:#8fb4ea;--col-bg:#fef3c7;--col-bd:#e6c66a;--rc-bg:#fdba74;--rc-bd:#e08a2c;
--k-kw:#cf222e;--k-ty:#0550ae;--k-num:#0550ae;--k-str:#0a3069;--k-cm:#6e7781;--k-fn:#8250df;--k-pp:#953800;--k-tx:#1f2328;
position:relative;font:14px/1.5 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--tx);background:var(--card);
border:1px solid var(--bd);border-radius:16px;box-shadow:var(--sh);overflow:hidden;direction:ltr;text-align:left;outline:none;transition:background .3s,border-color .3s,color .3s}
.card[data-theme=dark]{--card:#0f1522;--panel:#141b2b;--bd:#242e45;--tx:#e5eaf4;--mut:#8b97ae;--acc:#8b93ff;--acc-soft:rgba(139,147,255,.14);
--stage:#0c111d;--dot:#1a2236;--code-bg:#0c111c;--hl:rgba(139,147,255,.16);--ln:#4a5670;--sh:0 1px 2px rgba(0,0,0,.4),0 18px 36px -18px rgba(0,0,0,.7);
--sb:rgba(148,163,184,.32);--sb-h:rgba(139,147,255,.7);color-scheme:dark;--i-bg:#1a2238;--i-bd:#3c4b74;--i-tx:#e5eaf4;--out-bg:#0a0f1a;--out-fg:#7ee787;--out-bd:#1d2740;
--cur-bg:#6b4c06;--cur-bd:#f0b429;--cur-tx:#ffe9a8;--cur-gl:rgba(240,180,41,.30);
--cmp-bg:#5a1d42;--cmp-bd:#e879b9;--cmp-tx:#ffd6ec;--cmp-gl:rgba(232,121,185,.28);
--ok-bg:#14532d;--ok-bd:#4ade80;--ok-tx:#d9fbe4;--ok-gl:rgba(74,222,128,.26);
--sorted-bg:#1e3a6e;--sorted-bd:#60a5fa;--sorted-tx:#dbeafe;
--min-bg:#6b1d1d;--min-bd:#f87171;--min-tx:#fee2e2;--min-gl:rgba(248,113,113,.28);
--key-bg:#43307a;--key-bd:#a78bfa;--key-tx:#ede9fe;--key-gl:rgba(167,139,250,.30);
--dim-bg:#121826;--dim-bd:#252f45;--dim-tx:#566178;
--vis-bg:#10324a;--vis-bd:#38a9d8;--vis-tx:#d3f0ff;
--row-bg:#16305a;--row-bd:#3d68a8;--col-bg:#4a3a0c;--col-bd:#a9852a;--rc-bg:#6b430f;--rc-bd:#d18a2e;
--k-kw:#ff7b72;--k-ty:#79c0ff;--k-num:#79c0ff;--k-str:#a5d6ff;--k-cm:#8b949e;--k-fn:#d2a8ff;--k-pp:#ffa657;--k-tx:#e6edf3}
.card:focus-visible{box-shadow:0 0 0 3px var(--acc-soft),var(--sh);border-color:var(--acc)}
.hr{display:flex;align-items:center;gap:10px;flex:none}
.btn.fsb{width:30px;height:30px;border-radius:9px}
.btn.fsb svg{width:15px;height:15px}
:host(.ap-full){max-width:none;margin:0;width:100%;height:100%;background:var(--fs-bg,#fff)}
:host(.ap-pseudo){position:fixed;inset:0;z-index:2147483000;width:100vw;height:100vh}
.card.full{height:100%;border-radius:0;border:0;display:flex;flex-direction:column}
.card.full .stage{flex:none;display:flex;align-items:center;justify-content:center}
.card.full .split{flex:1;min-height:0}
.card.full .codep{display:flex;flex-direction:column;min-height:0}
.card.full .cwrap{flex:1;min-height:0}
.card.full .cbody{max-height:none;height:100%}
.card.full .info{overflow:auto;min-height:0}
.hd{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--bd);
background:linear-gradient(180deg,var(--panel),var(--card))}
.ttl{display:flex;align-items:center;gap:10px;min-width:0}
.dot{flex:none;width:10px;height:10px;border-radius:50%;background:var(--acc);box-shadow:0 0 0 4px var(--acc-soft)}
.tt{font-weight:700;font-size:15px;letter-spacing:.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bdg{flex:none;font:600 10.5px/1 'JetBrains Mono',ui-monospace,Consolas,monospace;letter-spacing:.08em;color:var(--acc);
background:var(--acc-soft);padding:6px 10px;border-radius:999px;text-transform:uppercase}
.stage{position:relative;overflow:hidden;background-color:var(--stage);background-image:radial-gradient(var(--dot) 1px,transparent 1.3px);background-size:18px 18px;border-bottom:1px solid var(--bd)}
.it{position:absolute;left:0;top:0;display:flex;align-items:center;justify-content:center;border:2px solid var(--i-bd);background:var(--i-bg);color:var(--i-tx);
border-radius:12px;font-family:'JetBrains Mono',ui-monospace,Consolas,monospace;font-weight:700;line-height:1;z-index:2;
box-shadow:0 1px 0 rgba(0,0,0,.05),0 8px 14px -10px rgba(15,23,42,.45);transition:background-color .28s,border-color .28s,color .28s,box-shadow .28s;will-change:transform}
.it[data-c=cur]{background:var(--cur-bg);border-color:var(--cur-bd);color:var(--cur-tx);box-shadow:0 0 0 5px var(--cur-gl);z-index:4}
.it[data-c=cmp]{background:var(--cmp-bg);border-color:var(--cmp-bd);color:var(--cmp-tx);box-shadow:0 0 0 5px var(--cmp-gl);z-index:4}
.it[data-c=ok]{background:var(--ok-bg);border-color:var(--ok-bd);color:var(--ok-tx);box-shadow:0 0 0 5px var(--ok-gl);z-index:4}
.it[data-c=min]{background:var(--min-bg);border-color:var(--min-bd);color:var(--min-tx);box-shadow:0 0 0 5px var(--min-gl);z-index:3}
.it[data-c=key]{background:var(--key-bg);border-color:var(--key-bd);color:var(--key-tx);box-shadow:0 0 0 5px var(--key-gl),0 14px 18px -10px rgba(76,29,149,.5);z-index:5}
.it[data-c=sorted]{background:var(--sorted-bg);border-color:var(--sorted-bd);color:var(--sorted-tx)}
.it[data-c=vis]{background:var(--vis-bg);border-color:var(--vis-bd);color:var(--vis-tx)}
.it[data-c=dim]{background:var(--dim-bg);border-color:var(--dim-bd);color:var(--dim-tx);box-shadow:none}
.it[data-c=row]{background:var(--row-bg);border-color:var(--row-bd)}
.it[data-c=col]{background:var(--col-bg);border-color:var(--col-bd);color:var(--cur-tx)}
.card[data-theme=dark] .it[data-c=col]{color:#ffeaa7}
.it[data-c=rc]{background:var(--rc-bg);border-color:var(--rc-bd);color:#3a1d00}
.card[data-theme=dark] .it[data-c=rc]{color:#ffe1bd}
.it.nul{border-style:dashed}
.ix{position:absolute;left:0;top:0;text-align:center;font:500 12px/16px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--mut);z-index:1}
.hdr{position:absolute;left:0;top:0;display:flex;align-items:center;justify-content:center;font:600 11.5px/1 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--mut);
border-radius:8px;transition:background-color .28s,color .28s;z-index:1}
.hdr.on{background:var(--acc-soft);color:var(--acc)}
.win{position:absolute;left:0;top:0;border:2px dashed var(--acc);border-radius:16px;background:var(--acc-soft);opacity:0;z-index:1;
transition:transform .5s cubic-bezier(.4,0,.2,1),width .5s cubic-bezier(.4,0,.2,1),opacity .3s}
.pt{position:absolute;left:0;top:0;width:0;height:0;z-index:6;pointer-events:none;transition:transform .5s cubic-bezier(.4,0,.2,1),opacity .28s;--pc:#6366f1}
.pt.off{opacity:0}
.pin{position:absolute;left:0;top:0;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center}
.ar{width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:8px solid var(--pc)}
.lb{margin-top:1px;padding:0 9px;border-radius:999px;background:var(--pc);color:#fff;font:700 12px/20px 'JetBrains Mono',ui-monospace,Consolas,monospace;white-space:nowrap;box-shadow:0 4px 10px -4px var(--pc)}
.pt[data-t=i],.pt[data-t=count]{--pc:#6366f1}.pt[data-t=j]{--pc:#0ea5a4}.pt[data-t=low]{--pc:#16a34a}.pt[data-t=mid]{--pc:#d98a06}
.pt[data-t=high],.pt[data-t=min]{--pc:#e5484d}.pt[data-t=key]{--pc:#8b5cf6}
.card[data-theme=dark] .pt[data-t=i],.card[data-theme=dark] .pt[data-t=count]{--pc:#7c83ff}.card[data-theme=dark] .pt[data-t=j]{--pc:#2dd4bf}
.card[data-theme=dark] .pt[data-t=low]{--pc:#22c55e}.card[data-theme=dark] .pt[data-t=mid]{--pc:#f59e0b}
.card[data-theme=dark] .pt[data-t=high],.card[data-theme=dark] .pt[data-t=min]{--pc:#f87171}.card[data-theme=dark] .pt[data-t=key]{--pc:#a78bfa}
.lg{display:flex;flex-wrap:wrap;gap:6px 16px;padding:9px 16px;border-bottom:1px solid var(--bd);background:var(--card);direction:rtl;font:500 12.5px/1.2 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--mut)}
.lg span{display:inline-flex;align-items:center;gap:6px}
.lg i{width:12px;height:12px;border-radius:4px;border:1.5px solid var(--i-bd);background:var(--i-bg)}
.split{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr)}
.card.narrow .split{grid-template-columns:minmax(0,1fr)}
.codep{min-width:0;background:var(--code-bg);border-right:1px solid var(--bd)}
.card.narrow .codep{border-right:0;border-bottom:1px solid var(--bd)}
.card.narrow .cbody{font-size:10.5px}
.chd{display:flex;align-items:center;gap:8px;padding:9px 14px;border-bottom:1px solid var(--bd);font:700 10.5px/1 'JetBrains Mono',ui-monospace,Consolas,monospace;letter-spacing:.1em;color:var(--mut)}
.chd s{width:8px;height:8px;border-radius:50%;background:#ff5f57;text-decoration:none;box-shadow:14px 0 0 #febc2e,28px 0 0 #28c840;margin-right:32px}
.cwrap{position:relative}
.sbt{position:absolute;top:0;right:3px;width:5px;height:100%;pointer-events:none;display:none}
.sbt i{display:block;width:5px;border-radius:99px;background:var(--sb);opacity:.9}
.cbody{position:relative;padding:8px 0 10px;overflow:auto;max-height:300px;overscroll-behavior:contain;font:500 13px/22px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--k-tx)}
.lines{position:relative;min-width:max-content}
.hl{position:absolute;left:0;right:0;top:0;height:22px;background:var(--hl);border-left:3px solid var(--acc);transition:transform .38s cubic-bezier(.4,0,.2,1),height .38s cubic-bezier(.4,0,.2,1);z-index:0}
.ln{position:relative;display:flex;height:22px;white-space:pre;z-index:1}
.no{flex:none;width:40px;padding-right:12px;text-align:right;color:var(--ln);user-select:none;transition:color .2s}
.tx{flex:1;padding-right:16px}
.ln.on .no{color:var(--acc);font-weight:700}
.k{color:var(--k-kw)}.t{color:var(--k-ty)}.n{color:var(--k-num)}.s{color:var(--k-str)}.c{color:var(--k-cm);font-style:italic}.f{color:var(--k-fn)}.p{color:var(--k-pp)}
.info{min-width:0;display:flex;flex-direction:column;gap:14px;padding:14px 16px;background:var(--card);direction:rtl;text-align:right}
.bt{margin-bottom:8px;font:700 12px/1 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--mut)}
.vars{display:flex;flex-wrap:wrap;gap:8px;direction:rtl;min-height:32px}
.chip{direction:ltr;display:flex;align-items:baseline;gap:7px;padding:5px 11px;border-radius:10px;background:var(--panel);border:1px solid var(--bd);font:600 13px/1.25 'JetBrains Mono',ui-monospace,Consolas,monospace}
.chip b{color:var(--mut);font-weight:600}.chip span{color:var(--tx);font-weight:700;min-width:1ch}
.chip.na{opacity:.45}.chip.chg{animation:pop .7s ease-out}
@keyframes pop{0%{background:var(--acc-soft);border-color:var(--acc);transform:scale(1.07)}100%{transform:scale(1)}}
.note{direction:rtl;text-align:right;min-height:72px;padding:11px 13px;border-radius:12px;background:var(--panel);border:1px solid var(--bd);font:500 13.5px/1.65 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif}
.note.in{animation:rise .35s ease-out}
@keyframes rise{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
.note code{font:600 12.5px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--acc);background:var(--acc-soft);padding:1px 6px;border-radius:6px}
.out{direction:ltr;text-align:left}.out .bt{direction:rtl;text-align:right}.out pre{margin:0;min-height:58px;max-height:150px;overflow:hidden;white-space:pre-wrap;padding:10px 12px;border-radius:12px;background:var(--out-bg);color:var(--out-fg);border:1px solid var(--out-bd);font:500 12.5px/1.6 'JetBrains Mono',ui-monospace,Consolas,monospace;white-space:pre-wrap}
.out pre::after{content:"\2588";margin-left:2px;color:var(--out-fg);animation:blink 1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
.ctl{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:11px 14px;border-top:1px solid var(--bd);background:var(--panel)}
.bts{display:flex;align-items:center;gap:6px}
.btn{display:grid;place-items:center;width:34px;height:34px;padding:0;border-radius:10px;border:1px solid var(--bd);background:var(--card);color:var(--tx);cursor:pointer;transition:background .15s,border-color .15s,color .15s,transform .1s}
.btn:hover{background:var(--acc-soft);border-color:var(--acc);color:var(--acc)}
.btn:active{transform:scale(.94)}.btn:disabled{opacity:.35;cursor:default}
.btn:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.btn svg{width:16px;height:16px;fill:currentColor}
.btn.play{width:42px;height:42px;border-radius:50%;background:var(--acc);border-color:var(--acc);color:#fff;box-shadow:0 8px 16px -8px var(--acc)}
.btn.play:hover{filter:brightness(1.08);color:#fff}
.btn.play svg{width:18px;height:18px}
.btn.on{background:var(--acc-soft);border-color:var(--acc);color:var(--acc)}
.sl{flex:1;min-width:120px;height:22px;margin:0;background:transparent;-webkit-appearance:none;appearance:none;cursor:pointer;--p:0%}
.sl::-webkit-slider-runnable-track{height:6px;border-radius:99px;background:linear-gradient(to right,var(--acc) var(--p),var(--bd) var(--p))}
.sl::-moz-range-track{height:6px;border-radius:99px;background:var(--bd)}
.sl::-moz-range-progress{height:6px;border-radius:99px;background:var(--acc)}
.sl::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;margin-top:-5px;border-radius:50%;background:#fff;border:3px solid var(--acc);box-shadow:0 2px 6px rgba(0,0,0,.25)}
.sl::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:#fff;border:3px solid var(--acc)}
.sl:focus-visible{outline:2px solid var(--acc);outline-offset:3px;border-radius:6px}
.cnt{flex:none;min-width:58px;text-align:center;font:700 12px/1 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--mut)}
.spd{flex:none;height:34px;padding:0 8px;border-radius:10px;border:1px solid var(--bd);background:var(--card);color:var(--tx);font:600 12px 'JetBrains Mono',ui-monospace,Consolas,monospace;cursor:pointer}
.spd:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.card.narrow .sl{order:5;flex-basis:100%}
@media (prefers-reduced-motion:reduce){.it,.pt,.win,.hl{transition:none!important}.note.in,.chip.chg{animation:none}}
.world{position:relative}.stage{overflow:hidden}
.nt,.nt *{transition:none!important;animation:none!important}
.lg i[data-c=cur]{background:var(--cur-bg);border-color:var(--cur-bd)}.lg i[data-c=cmp]{background:var(--cmp-bg);border-color:var(--cmp-bd)}
.lg i[data-c=ok]{background:var(--ok-bg);border-color:var(--ok-bd)}.lg i[data-c=min]{background:var(--min-bg);border-color:var(--min-bd)}
.lg i[data-c=key]{background:var(--key-bg);border-color:var(--key-bd)}.lg i[data-c=sorted]{background:var(--sorted-bg);border-color:var(--sorted-bd)}
.lg i[data-c=vis]{background:var(--vis-bg);border-color:var(--vis-bd)}.lg i[data-c=dim]{background:var(--dim-bg);border-color:var(--dim-bd)}
.mc{position:absolute;left:0;top:0;display:flex;align-items:center;justify-content:center;border:1.5px solid var(--i-bd);background:var(--i-bg);color:var(--i-tx);border-radius:8px;
font:700 12px 'JetBrains Mono',ui-monospace,Consolas,monospace;transition:background-color .25s,border-color .25s,color .25s,box-shadow .25s}
.mc[data-c=cur]{background:var(--cur-bg);border-color:var(--cur-bd);color:var(--cur-tx);box-shadow:0 0 0 4px var(--cur-gl)}
.mc[data-c=vis]{background:var(--vis-bg);border-color:var(--vis-bd);color:var(--vis-tx)}
.mlab{position:absolute;left:0;top:0;font:600 12px/1 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--mut);direction:rtl;white-space:nowrap}
.mlab code{font:600 11.5px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--acc)}
.mx{position:absolute;left:0;top:0;text-align:center;font:500 10.5px/14px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--mut)}
.corner{position:absolute;left:0;top:0;font:600 11px/1 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--mut);opacity:.8}
*{scrollbar-width:none;-ms-overflow-style:none}
*::-webkit-scrollbar{display:none;width:0;height:0}
.sub{position:absolute;left:0;top:0;text-align:center;font:600 11.5px/16px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--k-fn);z-index:1;white-space:nowrap;transition:color .25s,transform .25s}
.sub.on{color:var(--acc);font-weight:700}
.it[data-c=hid]{opacity:.18;box-shadow:none;border-style:dashed;background:transparent}
.hint{margin-inline-start:auto;font:500 12px 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--mut);direction:rtl}
.cap{padding:13px 18px;border-top:1px solid var(--bd);direction:rtl;text-align:right;font:500 13.5px/1.95 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;background:var(--card)}
.cap code{font:600 12.5px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--acc);background:var(--acc-soft);padding:1px 6px;border-radius:6px;direction:ltr;unicode-bidi:isolate}
.chartw{background-color:var(--stage);background-image:radial-gradient(var(--dot) 1px,transparent 1.3px);background-size:18px 18px;padding:4px 0 0}
.chartw svg{display:block;max-width:100%;height:auto}
.gl{stroke:var(--bd);stroke-width:1;stroke-dasharray:3 5}
.ax{stroke:var(--mut);stroke-width:1.4}
.tk{fill:var(--mut);font:500 11.5px 'JetBrains Mono',ui-monospace,Consolas,monospace}
.axl{fill:var(--mut);font:600 12px 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif}
.cl-area{fill:url(#gf);opacity:0;transition:opacity 1s ease .9s}
.cl-line{fill:none;stroke:var(--acc);stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:var(--len);stroke-dashoffset:var(--len);transition:stroke-dashoffset 1.5s cubic-bezier(.4,0,.2,1)}
.cl-pt{fill:var(--card);stroke:var(--acc);stroke-width:3;opacity:0;transform-box:fill-box;transform-origin:center;transform:scale(.3)}
.cl-lb{fill:var(--tx);font:700 12px 'JetBrains Mono',ui-monospace,Consolas,monospace;opacity:0}
.run .cl-area{opacity:1}
.run .cl-line{stroke-dashoffset:0}
.run .cl-pt{animation:ptin .42s cubic-bezier(.3,1.4,.5,1) forwards;animation-delay:calc(var(--i)*.13s + .15s)}
.run .cl-lb{animation:lbin .4s ease-out forwards;animation-delay:calc(var(--i)*.13s + .25s)}
@keyframes ptin{from{opacity:0;transform:scale(.3)}to{opacity:1;transform:scale(1)}}
@keyframes lbin{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
.sum{display:flex;flex-direction:column;direction:rtl;background:var(--card)}
.srow{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px 22px;align-items:center;padding:16px 18px;border-bottom:1px solid var(--bd)}
.sname b{display:block;font:700 15px/1.4 'JetBrains Mono',ui-monospace,Consolas,monospace;direction:ltr;text-align:right}
.sname span{display:block;font:500 11.5px/1.5 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--mut);direction:ltr;text-align:right}
.sarr{display:flex;gap:6px;direction:ltr}
.sbx{flex:1 1 0;min-width:0;max-width:58px;aspect-ratio:1;display:grid;place-items:center;border-radius:11px;border:2px solid var(--sorted-bd);background:var(--sorted-bg);color:var(--sorted-tx);font:700 15px 'JetBrains Mono',ui-monospace,Consolas,monospace;opacity:0;transform:scale(.4)}
.run .sbx{animation:ptin .45s cubic-bezier(.3,1.4,.5,1) forwards;animation-delay:calc(var(--i)*80ms + .1s)}
.smet{display:flex;flex-direction:column;gap:9px}
.m{display:grid;grid-template-columns:4.8rem minmax(24px,1fr) auto auto;gap:8px;align-items:center;font:600 12.5px 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--mut)}
.mt{height:10px;border-radius:99px;background:var(--bd);overflow:hidden;direction:ltr}
.mt i{display:block;height:100%;width:0;border-radius:inherit;transition:width .9s cubic-bezier(.4,0,.2,1) .45s}
.run .mt i{width:var(--w)}
.m[data-k="0"] .mt i{background:var(--cmp-bd)}.m[data-k="1"] .mt i{background:var(--ok-bd)}
.m b{font:700 14px 'JetBrains Mono',ui-monospace,Consolas,monospace;color:var(--tx);min-width:2ch;direction:ltr;text-align:left}
.m em{font-style:normal;font:600 11px 'Vazirmatn',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:var(--mut);background:var(--panel);border:1px solid var(--bd);padding:1px 7px;border-radius:99px;white-space:nowrap;font-size:10.5px}
.nt.run .cl-pt,.nt.run .cl-lb,.nt.run .sbx{opacity:1;transform:none}
.nt.run .cl-line{stroke-dashoffset:0;transition:none}
.nt.run .cl-area{opacity:1}
.nt.run .mt i{width:var(--w)}
.lg i[data-c=hid]{background:transparent;border-style:dashed;opacity:.6}
'''
_AP_JS = r'''(function(){
if(window.__AP&&window.__AP.v==="__VER__")return;
var CSS=__CSS__;
var IC={first:'M18.41 16.59 13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z',prev:'M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z',next:'M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z',last:'M5.59 7.41 10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z',play:'M8 5v14l11-7z',pause:'M6 19h4V5H6v14zm8-14v14h4V5h-4z',replay:'M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z',fs:'M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z',fsx:'M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z',loop:'M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z'};
function svg(p){return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="'+p+'"/></svg>'}
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c]})}
function isDark(){
var d=document.documentElement,b=document.body||d;
if(d.classList.contains('dark'))return true;
if(d.classList.contains('light'))return false;
var th=(d.getAttribute('data-theme')||'')+' '+(b.getAttribute&&b.getAttribute('data-vscode-theme-kind')||'');
if(/dark/i.test(th))return true; if(/light/i.test(th))return false;
var bc=b.className||'';
if(/vscode-high-contrast-light|vscode-light/.test(bc))return false;
if(/vscode-dark|vscode-high-contrast/.test(bc))return true;
if(/\bjp-mod-dark\b|theme-dark/.test(bc))return true;
return !!(window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches);
}
var PRI=['i','j','low','mid','high','min','key','count'];
function mount(host,S){
if(host.__ap)return; host.__ap=1;
var root=host.attachShadow?host.attachShadow({mode:'open'}):host;
var F=S.frames,N=F.length,isGrid=!!S.grid,n=S.n,LH=22;
var codeH='';for(var q=0;q<S.code.length;q++)codeH+='<div class="ln"><span class="no">'+(q+1)+'</span><span class="tx">'+S.code[q]+'</span></div>';
var lg='';S.legend.forEach(function(l){lg+='<span><i data-c="'+l[0]+'"></i>'+l[1]+'</span>'});
var btn=function(id,ic,label,cls){return '<button class="btn '+(cls||'')+'" data-a="'+id+'" aria-label="'+label+'" title="'+label+'">'+svg(ic)+'</button>'};
root.innerHTML='<style>'+CSS+'</style><div class="card nt" tabindex="0" role="group" aria-label="'+esc(S.title)+'">'+
'<div class="hd"><div class="ttl"><i class="dot"></i><span class="tt">'+esc(S.title)+'</span></div><div class="hr"><span class="bdg">'+esc(S.badge||'Interactive')+'</span>'+btn('fs',IC.fs,'تمامصفحه','fsb')+'</div></div>'+
'<div class="stage"><div class="world"></div></div>'+
'<div class="lg">'+lg+'</div>'+
'<div class="split"><div class="codep"><div class="chd"><s></s>'+esc(S.codeTitle)+'</div><div class="cwrap"><div class="cbody"><div class="lines"><div class="hl"></div>'+codeH+'</div></div><div class="sbt"><i></i></div></div></div>'+
'<div class="info"><div><div class="bt">متغیرها</div><div class="vars"></div></div>'+
'<div><div class="bt">این مرحله چه میکند؟</div><div class="note" aria-live="polite"></div></div>'+
(S.showOut?'<div class="out"><div class="bt">خروجی برنامه</div><pre></pre></div>':'')+'</div></div>'+
'<div class="ctl"><div class="bts">'+btn('first',IC.first,'اول')+btn('prev',IC.prev,'قبلی')+btn('play',IC.play,'پخش','play')+btn('next',IC.next,'بعدی')+btn('last',IC.last,'آخر')+'</div>'+
'<input class="sl" type="range" min="0" max="'+(N-1)+'" value="0" aria-label="مرحله">'+
'<span class="cnt"></span>'+
'<select class="spd" aria-label="سرعت"><option value="0.5">0.5×</option><option value="1" selected>1×</option><option value="1.5">1.5×</option><option value="2">2×</option><option value="3">3×</option></select>'+
btn('loop',IC.loop,'تکرار خودکار')+'</div></div>';
var $=function(s){return root.querySelector(s)};
var card=$('.card'),stage=$('.stage'),world=$('.world'),hl=$('.hl'),cbody=$('.cbody'),sbt=$('.sbt'),sbi=$('.sbt i'),lines=root.querySelectorAll('.ln'),
varsEl=$('.vars'),noteEl=$('.note'),outPre=$('.out pre'),sl=$('.sl'),cnt=$('.cnt'),bPlay=$('[data-a=play]'),bLoop=$('[data-a=loop]'),spd=$('.spd');
function sb(){if(!sbt||!cbody)return;var h=cbody.clientHeight,t=cbody.scrollHeight;if(t<=h+1){sbt.style.display='none';return}sbt.style.display='block';var th=Math.max(26,h*h/t);sbi.style.height=th+'px';sbi.style.transform='translateY('+(4+(cbody.scrollTop/(t-h))*(h-th-8))+'px)'}
if(cbody)cbody.addEventListener('scroll',sb);if(cbody&&window.ResizeObserver){try{new ResizeObserver(sb).observe(cbody)}catch(e){}}setTimeout(sb,60);setTimeout(sb,400);
var its=[],ixs=[],subs=[],pts={},win=null,rowH=[],colH=[],mcs=[],mlab=null,mx=[],corner=null,G={};
var st={k:0,vars:{},playing:false,timer:null,loop:false,speed:1,lastK:-1};
var d,i,id;
for(id=0;id<n;id++){d=document.createElement('div');d.className='it'+((S.nul&&S.nul.indexOf(id)>=0)?' nul':'');d.textContent=S.vals[id];world.appendChild(d);its.push(d)}
if(S.win){win=document.createElement('div');win.className='win';world.appendChild(win)}
if(!isGrid){for(i=0;i<n;i++){d=document.createElement('div');d.className='ix';d.textContent=i;world.appendChild(d);ixs.push(d);
if(S.sub){var sd=document.createElement('div');sd.className='sub';sd.textContent=S.sub[i];world.appendChild(sd);subs.push(sd)}}}
else{
for(i=0;i<S.grid.r;i++){d=document.createElement('div');d.className='hdr';d.textContent=(S.grid.rl?S.grid.rl[i]:'i='+i);world.appendChild(d);rowH.push(d)}
for(i=0;i<S.grid.c;i++){d=document.createElement('div');d.className='hdr';d.textContent=(S.grid.cl?S.grid.cl[i]:'j='+i);world.appendChild(d);colH.push(d)}
if(S.mem){
mlab=document.createElement('div');mlab.className='mlab';mlab.innerHTML='چیدمان واقعی در حافظه (Row-Major): خانهی <code dir="ltr">scores[i][j]</code> در جایگاه <code dir="ltr">i × COLS + j</code>';world.appendChild(mlab);
for(i=0;i<n;i++){d=document.createElement('div');d.className='mc';d.textContent=S.vals[i];world.appendChild(d);mcs.push(d);
var e=document.createElement('div');e.className='mx';e.textContent=i;world.appendChild(e);mx.push(e)}
}
}
function ensurePt(nm){
if(pts[nm])return pts[nm];
var p=document.createElement('div');p.className='pt off';p.setAttribute('data-t',nm);
p.innerHTML='<div class="pin"><div class="ar"></div><div class="lb">'+esc(nm)+'</div></div>';world.appendChild(p);pts[nm]=p;return p;
}
function layout(){
var W=stage.clientWidth;if(!W)return false;
var padX=W<500?14:26,size,gap,tw,ww;
if(!isGrid){
size=Math.max(26,Math.min(card.classList.contains('full')?120:80,Math.floor((W-2*padX)/n/1.16)));gap=Math.round(size*.16);
tw=n*size+(n-1)*gap;ww=Math.max(W,tw+2*padX);
G={W:ww,size:size,gap:gap,padL:(ww-tw)/2,padT:40};
G.ixY=G.padT+size+8;G.subY=G.ixY+19;G.ptrY=G.padT+size+(S.sub?54:32);G.H=G.ptrY+(S.lanes||1)*30+12;
}else{
var R=S.grid.r,C=S.grid.c,rh=52;
size=Math.max(30,Math.min(card.classList.contains('full')?84:58,Math.floor((W-2*padX-rh)/C/1.16)));gap=Math.round(size*.16);
tw=rh+C*size+(C-1)*gap;ww=Math.max(W,tw+2*padX);
G={W:ww,size:size,gap:gap,padL:(ww-tw)/2+rh,padT:36,rh:rh};
var gh=R*size+(R-1)*gap;
if(S.mem){
var tot=R*C,m=Math.max(22,Math.min(38,Math.floor((ww-2*padX-(R-1)*10)/tot)-5));
G.m=m;G.mg=5;var mw=tot*m+(tot-1)*5+(R-1)*10;G.mx=(ww-mw)/2;G.ml=G.padT+gh+22;G.my=G.ml+24;G.H=G.my+m+34;
}else G.H=G.padT+gh+18;
}
world.style.width=G.W+'px';world.style.height=G.H+'px';
var fs=Math.max(11,Math.round(size*(isGrid?.36:.37)))+'px',br=Math.round(size*.2)+'px';
its.forEach(function(el){el.style.width=el.style.height=size+'px';el.style.fontSize=fs;el.style.borderRadius=br});
if(!isGrid){ixs.forEach(function(el,p){el.style.width=size+'px';el.style.transform='translate('+(G.padL+p*(size+gap))+'px,'+G.ixY+'px)'});
subs.forEach(function(el,p){el.style.width=(size+gap)+'px';el.style.marginLeft=(-gap/2)+'px';el.style.transform='translate('+(G.padL+p*(size+gap))+'px,'+G.subY+'px)'})}
else{
rowH.forEach(function(el,r){el.style.width=(G.rh-12)+'px';el.style.height=size+'px';el.style.transform='translate('+(G.padL-G.rh+4)+'px,'+(G.padT+r*(size+gap))+'px)'});
colH.forEach(function(el,c){el.style.width=size+'px';el.style.height='24px';el.style.transform='translate('+(G.padL+c*(size+gap))+'px,'+(G.padT-32)+'px)'});
if(S.mem){
mlab.style.transform='translate('+G.mx+'px,'+G.ml+'px)';
mcs.forEach(function(el,k){var rr=Math.floor(k/S.grid.c);var x=G.mx+k*(G.m+G.mg)+rr*(10-0)-0;el.style.width=el.style.height=G.m+'px';el.style.transform='translate('+x+'px,'+G.my+'px)';
mx[k].style.width=G.m+'px';mx[k].style.transform='translate('+x+'px,'+(G.my+G.m+4)+'px)'});
}
}
return true;
}
function itemXY(id,f){
if(isGrid){var r=Math.floor(id/S.grid.c),c=id%S.grid.c;return [G.padL+c*(G.size+G.gap),G.padT+r*(G.size+G.gap)]}
var lift=f.lift&&f.lift.indexOf(id)>=0;
return [G.padL+f.pos[id]*(G.size+G.gap),G.padT+(lift?-18:0)];
}
function setPtr(p,x,y,instant){
if(instant||p.classList.contains('off')){p.style.transition='none';p.style.transform='translate('+x+'px,'+y+'px)';void p.offsetWidth;p.style.transition=''}
else p.style.transform='translate('+x+'px,'+y+'px)';
}
function render(k){
var f=F[k],pr=f.ptr||{};
for(id=0;id<n;id++){var el=its[id];el.setAttribute('data-c',f.cls[id]);if(f.vals&&f.vals[id]!==undefined)el.textContent=f.vals[id];var xy=itemXY(id,f);el.style.transform='translate('+xy[0]+'px,'+xy[1]+'px)'}
if(!isGrid){
subs.forEach(function(el,p){el.classList.toggle('on',f.subOn===p)});
var groups={};
Object.keys(pr).forEach(function(nm){var v=pr[nm];if(v!=null&&v>=0&&v<n){(groups[v]=groups[v]||[]).push(nm)}});
Object.keys(pts).forEach(function(nm){var v=pr[nm];if(!(v!=null&&v>=0&&v<n))pts[nm].classList.add('off')});
Object.keys(groups).forEach(function(ix){
groups[ix].sort(function(a,b){return PRI.indexOf(a)-PRI.indexOf(b)}).forEach(function(nm,r){
var p=ensurePt(nm),x=G.padL+(+ix)*(G.size+G.gap)+G.size/2,y=G.ptrY+r*30;
setPtr(p,x,y);p.classList.remove('off');
});
});
if(win){
if(f.win){var lo=f.win[0],hi=f.win[1];win.style.opacity=1;win.style.width=((hi-lo+1)*G.size+(hi-lo)*G.gap+18)+'px';win.style.height=(G.size+22)+'px';
win.style.transform='translate('+(G.padL+lo*(G.size+G.gap)-9)+'px,'+(G.padT-11)+'px)'}
else win.style.opacity=0;
}
}else{
rowH.forEach(function(el,r){el.classList.toggle('on',f.row===r)});
colH.forEach(function(el,c){el.classList.toggle('on',f.col===c)});
if(S.mem){mcs.forEach(function(el,q){el.setAttribute('data-c',q===f.mem?'cur':(q<(f.memv||0)?'vis':'i'))})}
}
var a=f.ln[0],b=f.ln[1];
hl.style.transform='translateY('+((a-1)*LH)+'px)';hl.style.height=((b-a+1)*LH)+'px';
for(i=0;i<lines.length;i++)lines[i].classList.toggle('on',i+1>=a&&i+1<=b);
var topY=(a-1)*LH,botY=b*LH+16;
if(topY<cbody.scrollTop+8||botY>cbody.scrollTop+cbody.clientHeight-8){cbody.scrollTo({top:Math.max(0,topY-cbody.clientHeight/2+LH*1.5),behavior:card.classList.contains('nt')?'auto':'smooth'})}
var h='',prev=st.vars,nv={};
(f.vars||[]).forEach(function(v){nv[v[0]]=v[1];var ch=prev[v[0]]!==undefined&&prev[v[0]]!==v[1];h+='<div class="chip'+(v[1]==='—'?' na':'')+(ch?' chg':'')+'"><b>'+esc(v[0])+'</b><span>'+esc(v[1])+'</span></div>'});
varsEl.innerHTML=h;st.vars=nv;
if(st.lastK!==k){noteEl.innerHTML=f.note;noteEl.classList.remove('in');void noteEl.offsetWidth;noteEl.classList.add('in')}
if(outPre){outPre.textContent=f.out||'';outPre.scrollTop=outPre.scrollHeight}
sl.value=k;sl.style.setProperty('--p',(N>1?k/(N-1)*100:0)+'%');cnt.textContent=(k+1)+' / '+N;
root.querySelector('[data-a=first]').disabled=root.querySelector('[data-a=prev]').disabled=(k===0);
root.querySelector('[data-a=next]').disabled=root.querySelector('[data-a=last]').disabled=(k===N-1);
bPlay.innerHTML=svg(st.playing?IC.pause:(k===N-1?IC.replay:IC.play));
var pl=st.playing?'توقف':(k===N-1?'پخش دوباره':'پخش');bPlay.setAttribute('aria-label',pl);bPlay.title=pl;
st.lastK=k;
}
function go(k){st.k=Math.max(0,Math.min(N-1,k));render(st.k)}
function stop(){st.playing=false;clearTimeout(st.timer);render(st.k)}
function tick(){
if(!st.playing)return;
if(st.k<N-1)go(st.k+1);else if(st.loop)go(0);else{stop();return}
if(st.k===N-1&&!st.loop){stop();return}
st.timer=setTimeout(tick,(S.interval||1200)/st.speed);
}
function play(){
if(st.k===N-1)go(0);
st.playing=true;render(st.k);clearTimeout(st.timer);st.timer=setTimeout(tick,(S.interval||1200)/st.speed);
}
root.querySelector('.ctl').addEventListener('click',function(e){
var t=e.target.closest&&e.target.closest('[data-a]');if(!t)return;
var a=t.getAttribute('data-a');
if(a==='play'){st.playing?stop():play()}
else if(a==='loop'){st.loop=!st.loop;bLoop.classList.toggle('on',st.loop)}
else{if(st.playing)stop();go(a==='first'?0:a==='last'?N-1:a==='prev'?st.k-1:st.k+1)}
});
sl.addEventListener('input',function(){if(st.playing)stop();go(+sl.value)});
spd.addEventListener('change',function(){st.speed=+spd.value;if(st.playing){clearTimeout(st.timer);st.timer=setTimeout(tick,(S.interval||1200)/st.speed)}});
card.addEventListener('keydown',function(e){
var k=e.key;if(e.target&&e.target.tagName==='INPUT'&&(k==='ArrowLeft'||k==='ArrowRight'))return;
if(k===' '||k==='Spacebar'){if(e.target&&e.target.tagName==='BUTTON')return;e.preventDefault();st.playing?stop():play()}
else if(k==='ArrowRight'){e.preventDefault();if(st.playing)stop();go(st.k+1)}
else if(k==='ArrowLeft'){e.preventDefault();if(st.playing)stop();go(st.k-1)}
else if(k==='Home'){e.preventDefault();go(0)}else if(k==='End'){e.preventDefault();go(N-1)}
});
function theme(){card.setAttribute('data-theme',isDark()?'dark':'light')}
function quiet(fn){card.classList.add('nt');fn();requestAnimationFrame(function(){requestAnimationFrame(function(){card.classList.remove('nt')})})}
function relayout(){
card.classList.toggle('narrow',card.clientWidth<640);
quiet(function(){if(layout()){Object.keys(pts).forEach(function(nm){pts[nm].classList.add('off')});st.lastK=-1;render(st.k)}});
}
var bFs=$('[data-a=fs]'),pseudo=false;
function fsOn(){return document.fullscreenElement===host||pseudo}
function syncFs(){var on=fsOn();card.classList.toggle('full',on);host.classList.toggle('ap-full',on);host.classList.toggle('ap-pseudo',pseudo);
if(bFs){bFs.innerHTML=svg(on?IC.fsx:IC.fs);bFs.title=bFs.ariaLabel=on?'خروج از تمامصفحه':'تمامصفحه'}
setTimeout(function(){if(host.isConnected)relayout()},60);setTimeout(function(){if(host.isConnected)relayout()},300)}
function toggleFs(){
if(fsOn()){if(pseudo){pseudo=false;syncFs()}else if(document.exitFullscreen)document.exitFullscreen();return}
var go2=function(){pseudo=true;syncFs()};
try{if(host.requestFullscreen){var pr=host.requestFullscreen();if(pr&&pr.catch)pr.catch(go2)}else go2()}catch(_){go2()}
}
if(bFs)bFs.addEventListener('click',toggleFs);
document.addEventListener('fullscreenchange',function(){if(host.isConnected)syncFs()});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&pseudo){pseudo=false;syncFs()}});
card.addEventListener('keydown',function(e){if((e.key==='f'||e.key==='F')&&!(e.target&&e.target.tagName==='INPUT')){toggleFs()}});
theme();
try{
new MutationObserver(function(){if(!host.isConnected)return;theme()}).observe(document.documentElement,{attributes:true,attributeFilter:['class','data-theme']});
if(document.body)new MutationObserver(function(){theme()}).observe(document.body,{attributes:true,attributeFilter:['class','data-vscode-theme-kind']});
if(window.matchMedia)matchMedia('(prefers-color-scheme: dark)').addEventListener('change',theme);
}catch(_){}
if(window.ResizeObserver){new ResizeObserver(function(){if(host.isConnected)relayout()}).observe(card)}
else window.addEventListener('resize',relayout);
relayout();
}
function watchTheme(host,card){
function theme(){card.setAttribute('data-theme',isDark()?'dark':'light')}
theme();
try{
new MutationObserver(function(){if(host.isConnected)theme()}).observe(document.documentElement,{attributes:true,attributeFilter:['class','data-theme']});
if(document.body)new MutationObserver(function(){theme()}).observe(document.body,{attributes:true,attributeFilter:['class','data-vscode-theme-kind']});
if(window.matchMedia)matchMedia('(prefers-color-scheme: dark)').addEventListener('change',theme);
}catch(_){}
}
function shell(host,S,inner,withReplay){
var root=host.attachShadow?host.attachShadow({mode:'open'}):host;
root.innerHTML='<style>'+CSS+'</style><div class="card nt" tabindex="0" role="group" aria-label="'+esc(S.title)+'">'+
'<div class="hd"><div class="ttl"><i class="dot"></i><span class="tt">'+esc(S.title)+'</span></div><span class="bdg">'+esc(S.badge||'Interactive')+'</span></div>'+inner+
'<div class="ctl"><div class="bts"><button class="btn" data-a="replay" aria-label="پخش دوباره" title="پخش دوباره">'+svg(IC.replay)+'</button></div><span class="hint">برای دیدن دوبارهی انیمیشن، دکمهی پخش دوباره را بزنید</span></div></div>';
return root;
}
function runAnim(card){card.classList.remove('run');void card.offsetWidth;card.classList.add('run')}
function startAnim(card){card.classList.remove('run');requestAnimationFrame(function(){requestAnimationFrame(function(){card.classList.remove('nt');runAnim(card)})})}
function mountChart(host,S){
if(host.__ap)return; host.__ap=1;
var root=shell(host,S,'<div class="chartw"></div><div class="cap">'+S.caption+'</div>');
var card=root.querySelector('.card'),wrap=root.querySelector('.chartw'),W0=0;
function draw(){
var W=wrap.clientWidth;if(!W)return false;
var H=Math.round(Math.max(230,Math.min(340,W*.46))),m={l:58,r:22,t:34,b:52},iw=W-m.l-m.r,ih=H-m.t-m.b;
var P=S.points,xmax=P[P.length-1][0],ymax=S.ymax;
function X(x){return m.l+x/xmax*iw} function Y(y){return m.t+ih-y/ymax*ih}
var h='<svg viewBox="0 0 '+W+' '+H+'" width="'+W+'" height="'+H+'" role="img" aria-label="'+esc(S.title)+'">'+
'<defs><linearGradient id="gf" x1="0" y1="0" x2="0" y2="1"><stop offset="0" style="stop-color:var(--acc);stop-opacity:.28"/><stop offset="1" style="stop-color:var(--acc);stop-opacity:0"/></linearGradient></defs>';
S.yticks.forEach(function(t){h+='<line class="gl" x1="'+m.l+'" x2="'+(W-m.r)+'" y1="'+Y(t)+'" y2="'+Y(t)+'"/><text class="tk" x="'+(m.l-10)+'" y="'+(Y(t)+4)+'" text-anchor="end">'+t+'</text>'});
P.forEach(function(p){h+='<text class="tk" x="'+X(p[0])+'" y="'+(H-m.b+20)+'" text-anchor="middle">'+p[0]+'</text>'});
h+='<line class="ax" x1="'+m.l+'" x2="'+(W-m.r)+'" y1="'+Y(0)+'" y2="'+Y(0)+'"/>';
var d='',len=0,px=null,py=null;
P.forEach(function(p,k){var x=X(p[0]),y=Y(p[1]);d+=(k?'L':'M')+x.toFixed(1)+' '+y.toFixed(1);if(k)len+=Math.hypot(x-px,y-py);px=x;py=y});
h+='<path class="cl-area" d="'+d+' L'+X(xmax).toFixed(1)+' '+Y(0)+' L'+X(0).toFixed(1)+' '+Y(0)+'Z"/>';
h+='<path class="cl-line" d="'+d+'" style="--len:'+Math.ceil(len)+'"/>';
P.forEach(function(p,k){var x=X(p[0]),y=Y(p[1]);
h+='<text class="cl-lb" style="--i:'+k+'" x="'+x+'" y="'+(y-12)+'" text-anchor="middle">'+p[1]+'</text>'+
'<circle class="cl-pt" style="--i:'+k+'" cx="'+x+'" cy="'+y+'" r="5.5"><title>'+esc((S.tip||'گام')+' '+p[0]+': '+p[1])+'</title></circle>'});
h+='<text class="axl" x="'+(m.l+iw/2)+'" y="'+(H-8)+'" text-anchor="middle">'+esc(S.xlabel)+'</text>';
h+='<text class="axl" transform="translate(14 '+(m.t+ih/2)+') rotate(-90)" text-anchor="middle">'+esc(S.ylabel)+'</text></svg>';
wrap.innerHTML=h;return true;
}
watchTheme(host,card);
function full(){if(draw())startAnim(card)}
root.querySelector('[data-a=replay]').addEventListener('click',function(){if(draw()){card.classList.add('nt');startAnim(card)}});
if(window.ResizeObserver){new ResizeObserver(function(){if(!host.isConnected)return;var w=wrap.clientWidth;if(w&&w!==W0){var first=!W0;W0=w;if(first)full();else{card.classList.add('nt');if(draw()){card.classList.add('run')}}}}).observe(wrap)}
else full();
}
function mountSummary(host,S){
if(host.__ap)return; host.__ap=1;
var h='<div class="sum">';
S.rows.forEach(function(r){
h+='<div class="srow"><div class="sname"><b>'+esc(r.name)+'</b><span>'+esc(r.sub||'')+'</span></div><div class="sarr">';
r.final.forEach(function(v,k){h+='<div class="sbx" style="--i:'+k+'">'+esc(v)+'</div>'});
h+='</div><div class="smet">';
r.metrics.forEach(function(m,k){
h+='<div class="m" data-k="'+k+'"><span class="ml">'+esc(m.label)+'</span><div class="mt"><i style="--w:'+(m.max?Math.max(m.val/m.max*100,m.val?4:0):0)+'%"></i></div><b>'+m.val+'</b>'+(m.tag?'<em>'+esc(m.tag)+'</em>':'')+'</div>'});
h+='</div></div>';
});
h+='</div><div class="lg">'+S.legend.map(function(l){return '<span><i data-c="'+l[0]+'"></i>'+l[1]+'</span>'}).join('')+'</div><div class="cap">'+S.caption+'</div>';
var root=shell(host,S,h);
var card=root.querySelector('.card');
watchTheme(host,card);
root.querySelector('[data-a=replay]').addEventListener('click',function(){card.classList.add('nt');startAnim(card)});
startAnim(card);
}
function mountAny(host,S){
if(S.kind==='chart')return mountChart(host,S);
if(S.kind==='summary')return mountSummary(host,S);
return mount(host,S);
}
window.__AP={mount:mountAny,v:"__VER__"};
})();
'''
_KW = {"if","else","for","while","do","return","break","continue","switch","case","default","sizeof","struct","typedef","const","static"}
_TY = {"int","char","void","float","double","long","short","unsigned","signed","size_t"}
_TOK = re.compile(r'(//.*$)|("(?:\\.|[^"\\])*")|(\'(?:\\.|[^\'\\])*\')|(#\s*\w+)|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_]\w*\b)')
def _c_hl(line):
out, pos = [], 0
for m in _TOK.finditer(line):
out.append(_html.escape(line[pos:m.start()]))
t = m.group(0)
if m.group(1): cls = "c"
elif m.group(2) or m.group(3): cls = "s"
elif m.group(4): cls = "p"
elif m.group(5): cls = "n"
else:
w = t
after = line[m.end():].lstrip()[:1]
if w in _KW: cls = "k"
elif w in _TY: cls = "t"
elif after == "(" : cls = "f"
elif len(w) > 1 and w.upper() == w and w.isalpha() or "_" in w and w.upper() == w: cls = "p"
else: cls = None
e = _html.escape(t)
out.append(f'<span class="{cls}">{e}</span>' if cls else e)
pos = m.end()
out.append(_html.escape(line[pos:]))
s = "".join(out)
if line.lstrip().startswith("#include"):
s = re.sub(r'(<[^&]*>)', r'<span class="s">\1</span>', s)
return s
def _c(x):
return f'<code dir="ltr">{_html.escape(str(x))}</code>'
def _widget(scene):
sid = "ap" + uuid.uuid4().hex[:8]
_ver = hashlib.md5((_AP_CSS + _AP_JS).encode("utf8")).hexdigest()[:10]
js_lib = _AP_JS.replace("__CSS__", json.dumps(_AP_CSS)).replace("__VER__", _ver)
data = json.dumps(scene, ensure_ascii=False, separators=(",", ":")).replace("</", "<\\/")
return HTML(
f'<div class="ap-root" data-ap="1" id="{sid}" style="display:block"></div>'
f'<script>{js_lib}\n(function(){{var S={data};var id="{sid}";var tries=0;'
f'var host=(document.currentScript&&document.currentScript.previousElementSibling)||document.getElementById(id);'
f'(function go(){{host=document.getElementById(id)||host;if(host&&host.isConnected){{window.__AP.mount(host,S)}}'
f'else if(tries++<200){{setTimeout(go,50)}}}})();}})();</script>'
)
def _scene(title, code, code_title, vals, frames, legend, **kw):
s = dict(title=title, code=[_c_hl(l) for l in code], codeTitle=code_title, vals=[str(v) for v in vals],
n=len(vals), frames=frames, legend=legend, badge="Step by step")
s.update(kw)
return s
# ============================================================
# صحنههای انیمیشن بخش هفتم: ساختارها و فایلها
# ============================================================
def _ln(code, text, nth=1):
k = 0
for i, l in enumerate(code):
if text in l:
k += 1
if k == nth:
return i + 1
raise ValueError(text)
def _fr(ln, cls, note, vars_, vals=None, ptr=None, out="", **kw):
a, b = (ln, ln) if isinstance(ln, int) else ln
d = dict(ln=[a, b], pos=list(range(len(cls))), cls=list(cls), ptr=ptr or {},
vars=[list(v) for v in vars_], note=note, out=out)
if vals is not None:
d["vals"] = [str(v) for v in vals]
d.update(kw)
return d
# ------------------------------------------------------------------ فیلدهای یک struct
_CODE_S1 = [
'#include <stdio.h>', '#include <string.h>', '',
'struct Student {', ' int id;', ' char name[20];', ' double score;', '};', '',
'int main(void)', '{', ' struct Student s;', '',
' s.id = 4001;', ' strcpy(s.name, "Sara");', ' s.score = 18.5;', '',
' printf("%d %s %.1f\\n", s.id, s.name, s.score);', ' return 0;', '}']
def scene_struct_basic(title="A struct: one variable, several fields"):
C = _CODE_S1; fr = []; L = lambda t, n=1: _ln(C, t, n)
fr.append(_fr(L("struct Student s;"), ['i', 'i', 'i'],
f"متغیر {_c('s')} ساخته شد: یک بسته با سه فیلد که پشتسرهم در حافظه قرار میگیرند. هنوز هیچکدام مقدار ندارند.",
[("s.id", "?"), ("s.name", "?"), ("s.score", "?")], vals=["?", "?", "?"]))
fr.append(_fr(L("s.id = 4001"), ['ok', 'i', 'i'],
f"با نقطه به هر فیلد میرسیم: {_c('s.id')} فقط فیلد شمارهی دانشجویی است.",
[("s.id", 4001), ("s.name", "?"), ("s.score", "?")], vals=["4001", "?", "?"]))
fr.append(_fr(L("strcpy"), ['i', 'ok', 'i'],
f"فیلد {_c('name')} یک آرایهی {_c('char')} است و با {_c('=')} پر نمیشود؛ از {_c('strcpy')} استفاده میکنیم.",
[("s.id", 4001), ("s.name", "Sara"), ("s.score", "?")], vals=["4001", "Sara", "?"]))
fr.append(_fr(L("s.score = 18.5"), ['i', 'i', 'ok'],
f"{_c('s.score = 18.5')}: هر فیلد نوع و خانهی خودش را دارد.",
[("s.id", 4001), ("s.name", "Sara"), ("s.score", 18.5)], vals=["4001", "Sara", "18.5"]))
fr.append(_fr(L("printf"), ['cur', 'cur', 'cur'],
"هر سه فیلد را از یک متغیر میخوانیم.",
[("s.id", 4001), ("s.name", "Sara"), ("s.score", 18.5)], vals=["4001", "Sara", "18.5"],
out="4001 Sara 18.5\n"))
fr.append(_fr(L("return 0"), ['vis', 'vis', 'vis'], f"{_c('return 0')}: برنامه تمام شد.",
[("s.id", 4001), ("s.name", "Sara"), ("s.score", 18.5)], vals=["4001", "Sara", "18.5"],
out="4001 Sara 18.5\n"))
lg = [["ok", "فیلدی که مقدار گرفت"], ["i", "فیلد خالی"], ["cur", "در حال خواندن"]]
return _scene(title, C, "C · struct_basic.c", ["?", "?", "?"], fr, lg,
lanes=1, showOut=True, interval=1500, sub=["s.id", "s.name", "s.score"])
# ------------------------------------------------------------------ کپی یا آدرس
_CODE_S4 = [
'void bonus_copy(Student s)', '{', ' s.score += 1;', '}', '',
'void bonus_ptr(Student *p)', '{', ' p->score += 1;', '}', '',
'int main(void)', '{', ' Student a = {4001, "Sara", 18.5};', '',
' bonus_copy(a);', ' printf("after bonus_copy: %.1f\\n", a.score);', '',
' bonus_ptr(&a);', ' printf("after bonus_ptr : %.1f\\n", a.score);', ' return 0;', '}']
def scene_struct_copy(title="Struct by value and by pointer"):
C = _CODE_S4; fr = []; L = lambda t, n=1: _ln(C, t, n)
o1 = "after bonus_copy: 18.5\n"; o2 = o1 + "after bonus_ptr : 19.5\n"
fr.append(_fr(L("Student a ="), ['cur', 'hid'],
f"در {_c('main')} یک دانشجو داریم. برای سادگی فقط فیلد {_c('score')} را نشان میدهیم.",
[("a.score", 18.5)], vals=["18.5", ""]))
fr.append(_fr(L("bonus_copy(a)"), ['i', 'key'],
f"تابع {_c('bonus_copy')} ساختار را <b>بهصورت کپی</b> میگیرد: پارامتر {_c('s')} یک نسخهی کامل و جدا از {_c('a')} است.",
[("a.score", 18.5), ("s.score", 18.5)], vals=["18.5", "18.5"]))
fr.append(_fr(L("s.score += 1"), ['i', 'ok'],
"فقط کپی تغییر میکند.",
[("a.score", 18.5), ("s.score", 19.5)], vals=["18.5", "19.5"]))
fr.append(_fr(L("}"), ['cur', 'hid'],
f"تابع تمام شد و کپی از بین رفت. مقدار {_c('a.score')} هیچ تغییری نکرده است.",
[("a.score", 18.5)], vals=["18.5", ""]))
fr.append(_fr(L("after bonus_copy"), ['cur', 'hid'], f"چاپ میکنیم: {_c(18.5)}.",
[("a.score", 18.5)], vals=["18.5", ""], out=o1))
fr.append(_fr(L("bonus_ptr(&a)"), ['key', 'hid'],
f"این بار آدرسِ {_c('a')} را میفرستیم. {_c('p')} به خودِ {_c('a')} اشاره میکند، نه به کپی.",
[("a.score", 18.5), ("p", "&a")], vals=["18.5", ""], ptr={"p": 0}, out=o1))
fr.append(_fr(L("p->score"), ['ok', 'hid'],
f"{_c('p->score')} یعنی فیلدِ {_c('score')} از ساختاری که {_c('p')} نشان میدهد؛ همان {_c('a.score')} است.",
[("a.score", 19.5), ("p", "&a")], vals=["19.5", ""], ptr={"p": 0}, out=o1))
fr.append(_fr(L("after bonus_ptr"), ['cur', 'hid'], f"حالا مقدار اصلی عوض شده است: {_c(19.5)}.",
[("a.score", 19.5)], vals=["19.5", ""], out=o2))
fr.append(_fr(L("return 0"), ['vis', 'hid'], f"{_c('return 0')}: برنامه تمام شد.",
[("a.score", 19.5)], vals=["19.5", ""], out=o2))
lg = [["cur", "ساختار اصلی"], ["key", "کپی یا اشارهگر"], ["ok", "مقدار تغییر کرد"]]
return _scene(title, C, "C · struct_by_value_pointer.c", ["18.5", ""], fr, lg,
lanes=1, showOut=True, interval=1500, sub=["a.score", "s.score (copy)"])
# ------------------------------------------------------------------ آرایهای از ساختار
_CODE_S5 = [
'Student cls[4] = {', ' {4001, "Sara", 18.5},', ' {4002, "Ali", 15},',
' {4003, "Mina", 20},', ' {4004, "Reza", 12.5}', '};',
'double sum = 0;', 'Student *best = &cls[0];', '',
'for (int i = 0; i < 4; i++) {', ' sum += cls[i].score;',
' if (cls[i].score > best->score)', ' best = &cls[i];', '}', '',
'printf("average = %.2f\\n", sum / 4);',
'printf("best = %s (%.1f)\\n", best->name, best->score);']
def scene_struct_array(title="Array of structs: average and best"):
C = _CODE_S5; fr = []; L = lambda t, n=1: _ln(C, t, n)
names = ["Sara", "Ali", "Mina", "Reza"]; sc = [18.5, 15, 20, 12.5]
sub = ["18.5", "15", "20", "12.5"]
fr.append(_fr((1, 6), ['i'] * 4,
"آرایهای از چهار ساختار. هر خانه یک دانشجو است و نمرهی او زیرش نوشته شده است.",
[], vals=names))
fr.append(_fr((L("double sum"), L("Student *best")), ['key', 'i', 'i', 'i'],
f"{_c('best')} در ابتدا به اولین دانشجو اشاره میکند و {_c('sum')} صفر است.",
[("sum", 0), ("best->name", "Sara")], vals=names, ptr={"best": 0}))
best, s = 0, 0.0
for i in range(4):
s += sc[i]
moved = sc[i] > sc[best]
if moved: best = i
cls = []
for k in range(4):
if k == i: cls.append('ok' if moved else 'cur')
elif k == best: cls.append('key')
elif k < i: cls.append('vis')
else: cls.append('i')
if i == 0: why = f"نمرهی {_c('cls[0]')} با خودش مقایسه میشود؛ {_c('best')} همانجا میماند."
elif moved: why = f"نمرهی {_c(f'cls[{i}]')} ({sc[i]:g}) از نمرهی {_c('best')} بیشتر است، پس {_c('best')} به این خانه میرود."
else: why = f"نمرهی {_c(f'cls[{i}]')} ({sc[i]:g}) بیشتر از نمرهی {_c('best')} نیست؛ {_c('best')} عوض نمیشود."
fr.append(_fr((L("for (int i"), L("best = &cls[i]")), cls, why,
[("i", i), ("sum", f"{s:g}"), ("best->name", names[best])], vals=names,
ptr={"best": best, "i": i}))
o1 = "average = 16.50\n"
fr.append(_fr(L("average"), ['vis', 'vis', 'key', 'vis'],
f"{_c('sum')} برابر ۶۶ است و میانگین ۶۶ ÷ ۴ = ۱۶٫۵۰ میشود.",
[("sum", 66), ("best->name", "Mina")], vals=names, ptr={"best": 2}, out=o1))
fr.append(_fr(L("best ="), ['vis', 'vis', 'ok', 'vis'],
f"{_c('best->name')} و {_c('best->score')} از ساختاری خوانده میشود که {_c('best')} به آن اشاره میکند.",
[("sum", 66), ("best->name", "Mina")], vals=names, ptr={"best": 2},
out=o1 + "best = Mina (20.0)\n"))
lg = [["cur", "در حال بررسی"], ["ok", "بهترین تازه پیدا شد"], ["key", "بهترین تا اینجا"], ["vis", "بررسیشده"]]
return _scene(title, C, "C · struct_array_best.c", names, fr, lg,
lanes=2, showOut=True, interval=1400, sub=sub)
# ------------------------------------------------------------------ نوشتن و خواندن فایل متنی
_CODE_F2 = [
'FILE *f = fopen("scores.txt", "w");',
'fprintf(f, "%d\\n", 18);', 'fprintf(f, "%d\\n", 15);', 'fprintf(f, "%d\\n", 20);',
'fclose(f);', '',
'f = fopen("scores.txt", "r");', 'int x;',
'while (fscanf(f, "%d", &x) == 1)', ' printf("%d\\n", x);', 'fclose(f);']
def scene_file_text(title="Writing and reading a text file"):
C = _CODE_F2; fr = []; L = lambda t, n=1: _ln(C, t, n)
data = ["18", "15", "20"]; N = 4
fr.append(_fr(L('"w"'), ['hid'] * N,
f"حالت {_c('w')}: فایل خالی ساخته میشود (و اگر از قبل بود، محتوایش پاک میشود). {_c('pos')} مکانِ بعدیِ نوشتن را نشان میدهد.",
[("f", "scores.txt (w)")], vals=[""] * N, ptr={"pos": 0}))
v = [""] * N
for k, ln_text in enumerate(['fprintf(f, "%d\\n", 18)', 'fprintf(f, "%d\\n", 15)', 'fprintf(f, "%d\\n", 20)']):
v[k] = data[k]
cls = ['i'] * (k) + ['ok'] + ['hid'] * (N - k - 1)
fr.append(_fr(L(ln_text), cls,
f"هر {_c('fprintf')} یک خط به انتهای فایل اضافه میکند. این بار {_c(data[k])} نوشته شد.",
[("f", "scores.txt (w)")], vals=list(v), ptr={"pos": k + 1}))
fr.append(_fr(L("fclose(f)"), ['i', 'i', 'i', 'hid'],
f"{_c('fclose')} فایل را میبندد و مطمئن میشود همهی دادهها واقعاً روی دیسک نوشته شده است.",
[("f", "closed")], vals=list(v)))
v[3] = ""
fr.append(_fr(L('"r"'), ['i', 'i', 'i', 'hid'],
f"حالت {_c('r')}: فایل برای خواندن باز میشود. مکاننمای خواندن در ابتدای فایل است.",
[("f", "scores.txt (r)")], vals=list(v), ptr={"pos": 0}))
out = ""
for k in range(3):
out += data[k] + "\n"
cls = ['vis'] * k + ['cur'] + ['i'] * (3 - k - 1) + ['hid']
fr.append(_fr((L("while (fscanf"), L('printf("%d\\n", x)')), cls,
f"{_c('fscanf')} یک عدد خواند ({_c(data[k])}) و {_c(1)} برگرداند، پس حلقه ادامه مییابد و عدد چاپ میشود.",
[("x", data[k]), ("fscanf", 1)], vals=list(v), ptr={"pos": k}, out=out))
v[3] = "EOF"
fr.append(_fr(L("while (fscanf"), ['vis', 'vis', 'vis', 'cmp'],
f"به انتهای فایل رسیدیم. {_c('fscanf')} چیزی نمیخواند و {_c('EOF')} برمیگرداند که برابر {_c(1)} نیست؛ حلقه تمام میشود.",
[("x", data[2]), ("fscanf", "EOF")], vals=list(v), ptr={"pos": 3}, out=out))
fr.append(_fr(L("fclose(f)", 2), ['vis', 'vis', 'vis', 'vis'],
"فایل بسته شد.", [("f", "closed")], vals=list(v), out=out))
lg = [["ok", "خط تازهنوشته"], ["cur", "خطی که خوانده میشود"], ["vis", "خواندهشده"], ["cmp", "انتهای فایل"]]
return _scene(title, C, "C · file_text.c", [""] * N, fr, lg,
lanes=1, showOut=True, interval=1300, sub=["line 1", "line 2", "line 3", "end"])
# ------------------------------------------------------------------ حالتهای a و w
_CODE_F3 = [
'FILE *f = fopen("names.txt", "a");', 'fprintf(f, "Reza\\n");', 'fclose(f);', '',
'f = fopen("names.txt", "w");', 'fprintf(f, "Neda\\n");', 'fclose(f);']
def scene_file_modes(title="Append mode and write mode"):
C = _CODE_F3; fr = []; L = lambda t, n=1: _ln(C, t, n)
base = ["Ali", "Sara", "Mina", ""]
fr.append(_fr(L('"a"'), ['i', 'i', 'i', 'hid'],
f"فرض کنید {_c('names.txt')} از قبل سه خط دارد. حالت {_c('a')} (append) از <b>انتهای</b> فایل شروع به نوشتن میکند و محتوای قبلی میماند.",
[("mode", "a")], vals=base, ptr={"pos": 3}))
fr.append(_fr(L("Reza"), ['i', 'i', 'i', 'ok'],
f"{_c('Reza')} بعد از خطهای قبلی اضافه شد.",
[("mode", "a")], vals=["Ali", "Sara", "Mina", "Reza"], ptr={"pos": 3}))
fr.append(_fr(L("fclose(f)"), ['vis', 'vis', 'vis', 'vis'],
"فایل حالا چهار خط دارد.", [("mode", "closed")], vals=["Ali", "Sara", "Mina", "Reza"]))
fr.append(_fr(L('"w"'), ['min'] * 4,
f"حالت {_c('w')} (write) همان لحظهی باز کردن، <b>همهی محتوای قبلی را پاک میکند</b>، حتی اگر هیچچیز ننویسیم.",
[("mode", "w")], vals=["—"] * 4, ptr={"pos": 0}))
fr.append(_fr(L("Neda"), ['ok', 'hid', 'hid', 'hid'],
f"فقط {_c('Neda')} در فایل مانده است.",
[("mode", "w")], vals=["Neda", "", "", ""], ptr={"pos": 1}))
fr.append(_fr(L("fclose(f)", 2), ['vis', 'hid', 'hid', 'hid'],
f"درس مهم: برای اضافه کردن به فایل از {_c('a')} استفاده کنید؛ {_c('w')} همیشه از اول شروع میکند.",
[("mode", "closed")], vals=["Neda", "", "", ""]))
lg = [["i", "محتوای قبلی"], ["ok", "نوشتهی تازه"], ["min", "پاکشده"]]
return _scene(title, C, "C · file_modes.c", base, fr, lg,
lanes=1, showOut=False, interval=1700, sub=["line 1", "line 2", "line 3", "line 4"])
# ------------------------------------------------------------------ فایل باینری
_CODE_F4 = [
'Student s;', 'FILE *f = fopen("students.bin", "wb");',
'fwrite(cls, sizeof(Student), 3, f);', 'fclose(f);', '',
'f = fopen("students.bin", "rb");',
'while (fread(&s, sizeof(Student), 1, f) == 1)',
' printf("%d %s %.1f\\n", s.id, s.name, s.score);', 'fclose(f);']
def scene_file_binary(title="Binary file: whole records with fwrite and fread"):
C = _CODE_F4; fr = []; L = lambda t, n=1: _ln(C, t, n)
names = ["Sara", "Ali", "Mina"]
rows = ["4001 Sara 18.5", "4002 Ali 15.0", "4003 Mina 20.0"]; ids = [4001, 4002, 4003]
fr.append(_fr(L('"wb"'), ['hid'] * 3,
f"حالت {_c('wb')}: فایل باینری برای نوشتن. در فایل باینری، دادهها همانطور که در حافظهاند ذخیره میشوند.",
[("f", "students.bin (wb)")], vals=[""] * 3, ptr={"pos": 0}))
fr.append(_fr(L("fwrite"), ['ok'] * 3,
f"یک {_c('fwrite')} هر سه رکورد را پشتسرهم مینویسد. هر رکورد دقیقاً {_c('sizeof(Student)')} بایت است، بدون جداکننده.",
[("written", 3)], vals=names))
fr.append(_fr(L("fclose(f)"), ['i'] * 3, "فایل بسته شد.", [("f", "closed")], vals=names))
fr.append(_fr(L('"rb"'), ['i'] * 3,
f"حالت {_c('rb')}: خواندن باینری از ابتدای فایل.",
[("f", "students.bin (rb)")], vals=names, ptr={"pos": 0}))
out = ""
for k in range(3):
out += rows[k] + "\n"
cls = ['vis'] * k + ['cur'] + ['i'] * (3 - k - 1)
fr.append(_fr((L("while (fread"), L("printf")), cls,
f"{_c('fread')} یک رکورد کامل را در {_c('s')} ریخت و {_c(1)} برگرداند (تعداد رکوردهای خواندهشده).",
[("s.id", ids[k]), ("s.name", names[k]), ("fread", 1)], vals=names, ptr={"pos": k}, out=out))
fr.append(_fr(L("while (fread"), ['vis'] * 3,
f"{_c('fread')} صفر رکورد خواند و {_c(0)} برگرداند؛ فایل تمام شد و حلقه پایان مییابد.",
[("fread", 0)], vals=names, out=out))
fr.append(_fr(L("fclose(f)", 2), ['vis'] * 3, "فایل بسته شد.", [("f", "closed")], vals=names, out=out))
lg = [["ok", "رکورد نوشتهشده"], ["cur", "رکوردی که خوانده میشود"], ["vis", "خواندهشده"]]
return _scene(title, C, "C · file_binary.c", [""] * 3, fr, lg,
lanes=1, showOut=True, interval=1400, sub=["record 1", "record 2", "record 3"])
def show_struct_basic(): return _widget(scene_struct_basic())
def show_struct_copy(): return _widget(scene_struct_copy())
def show_struct_array(): return _widget(scene_struct_array())
def show_file_text(): return _widget(scene_file_text())
def show_file_modes(): return _widget(scene_file_modes())
def show_file_binary(): return _widget(scene_file_binary())