Spaces:
Runtime error
Runtime error
| {% extends "base.html" %} | |
| {% block title %}Graph Algorithms | USSU Algorithm Analyzer v4.0{% endblock %} | |
| {% block extra_head %} | |
| <style> | |
| #graph-viz { width: 100%; height: 520px; border-radius: 16px; background: rgba(15,23,42,0.6); border: 1px solid rgba(255,255,255,0.08); position: relative; } | |
| .graph-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; } | |
| @media (max-width: 900px) { .graph-controls { grid-template-columns: 1fr; } } | |
| .graph-overlay { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: #64748b; pointer-events: none; } | |
| .graph-legend { display: flex; gap: 16px; margin-top: 12px; flex-wrap: wrap; } | |
| .legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #94a3b8; } | |
| .legend-dot { width: 10px; height: 10px; border-radius: 50%; } | |
| .result-metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 16px; } | |
| @media (max-width: 768px) { .result-metric-grid { grid-template-columns: repeat(2, 1fr); } } | |
| .result-detail-card { background: rgba(30,41,59,0.6); border: 1px solid rgba(255,255,255,0.06); border-radius: 10px; padding: 12px; } | |
| .result-detail-label { font-family: 'Orbitron', sans-serif; font-size: 10px; color: var(--cyan); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 4px; } | |
| .result-detail-value { font-family: 'Orbitron', sans-serif; font-size: 18px; color: #f8fafc; font-weight: 700; } | |
| .btn-small { padding: 8px 16px; font-size: 11px; } | |
| .process-flow { display: flex; align-items: center; gap: 8px; margin: 16px 0; flex-wrap: wrap; } | |
| .process-node { background: linear-gradient(135deg, rgba(6,182,212,0.15), rgba(139,92,246,0.15)); border: 1px solid rgba(6,182,212,0.2); border-radius: 8px; padding: 8px 12px; font-size: 12px; color: #e2e8f0; } | |
| .process-arrow { color: var(--cyan); font-size: 16px; } | |
| </style> | |
| {% endblock %} | |
| {% block content %} | |
| <div class="page-header animate-in"> | |
| <h1>🕸️ GRAPH ALGORITHMS</h1> | |
| <p>Interactive graph traversal, shortest path, MST, and DAG analysis with real-time visualization.</p> | |
| <div class="header-line"></div> | |
| </div> | |
| <div class="graph-controls"> | |
| <div class="glass-card animate-in delay-1"> | |
| <h3 style="font-family: 'Orbitron'; color: var(--bright-cyan); margin-bottom: 16px;">Create Graph</h3> | |
| <form method="POST" id="graphForm"> | |
| <input type="hidden" name="action" value="create"> | |
| <div class="form-group"> | |
| <label class="form-label">Vertices</label> | |
| <input type="number" name="n" value="5" min="2" max="50" class="form-input"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label">Edge Probability (0-1)</label> | |
| <input type="number" name="p" value="0.4" step="0.1" min="0.1" max="1" class="form-input"> | |
| </div> | |
| <div class="form-group" style="display: flex; gap: 20px;"> | |
| <label style="display: flex; align-items: center; gap: 6px; color: #94a3b8; font-size: 14px; cursor: pointer;"> | |
| <input type="checkbox" name="directed"> Directed | |
| </label> | |
| <label style="display: flex; align-items: center; gap: 6px; color: #94a3b8; font-size: 14px; cursor: pointer;"> | |
| <input type="checkbox" name="weighted" checked> Weighted | |
| </label> | |
| </div> | |
| <div style="display: flex; gap: 10px;"> | |
| <button type="submit" class="btn-liquid">Generate Random Graph</button> | |
| <button type="button" class="btn-liquid btn-small" onclick="fillRandomEdges()">⚡ Random Demo</button> | |
| </div> | |
| </form> | |
| <div class="section-divider" style="margin-top: 24px;"> | |
| <h3>▸ OR CUSTOM EDGES</h3> | |
| <div class="line"></div> | |
| </div> | |
| <form method="POST" style="margin-top: 12px;"> | |
| <input type="hidden" name="action" value="custom"> | |
| <div class="form-group"> | |
| <label class="form-label">Edges (one per line: u v [w])</label> | |
| <textarea name="edges" id="customEdges" class="form-textarea" style="min-height: 100px;" placeholder="0 1 5 1 2 3 2 0 2 0 3 1 3 4 7"></textarea> | |
| </div> | |
| <div class="form-group" style="display: flex; gap: 20px;"> | |
| <label style="display: flex; align-items: center; gap: 6px; color: #94a3b8; font-size: 14px; cursor: pointer;"> | |
| <input type="checkbox" name="directed" id="customDirected"> Directed | |
| </label> | |
| <label style="display: flex; align-items: center; gap: 6px; color: #94a3b8; font-size: 14px; cursor: pointer;"> | |
| <input type="checkbox" name="weighted" id="customWeighted" checked> Weighted | |
| </label> | |
| </div> | |
| <div style="display: flex; gap: 10px;"> | |
| <button type="submit" class="btn-liquid">Build Custom Graph</button> | |
| <button type="button" class="btn-liquid btn-small" onclick="fillRandomEdges()">⚡ Auto-Fill Random</button> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="glass-card animate-in delay-2"> | |
| <h3 style="font-family: 'Orbitron'; color: var(--bright-cyan); margin-bottom: 16px;">Run Algorithm</h3> | |
| <form method="POST" id="runForm"> | |
| <input type="hidden" name="action" value="run"> | |
| <input type="hidden" name="graph_json" id="graphJson" value='{% if graph_data %}{{ graph_data|tojson|safe }}{% endif %}'> | |
| <div class="form-group"> | |
| <label class="form-label">Algorithm</label> | |
| <select name="algo" class="form-select" id="algoSelect"> | |
| <option value="bfs" {% if algo == 'bfs' %}selected{% endif %}>BFS Traversal</option> | |
| <option value="dfs_iter" {% if algo == 'dfs_iter' %}selected{% endif %}>DFS Iterative</option> | |
| <option value="dfs_rec" {% if algo == 'dfs_rec' %}selected{% endif %}>DFS Recursive</option> | |
| <option value="dijkstra" {% if algo == 'dijkstra' %}selected{% endif %}>Dijkstra Shortest Path</option> | |
| <option value="bellman_ford" {% if algo == 'bellman_ford' %}selected{% endif %}>Bellman-Ford</option> | |
| <option value="floyd_warshall" {% if algo == 'floyd_warshall' %}selected{% endif %}>Floyd-Warshall</option> | |
| <option value="prim" {% if algo == 'prim' %}selected{% endif %}>Prim MST</option> | |
| <option value="kruskal" {% if algo == 'kruskal' %}selected{% endif %}>Kruskal MST</option> | |
| <option value="topological" {% if algo == 'topological' %}selected{% endif %}>Topological Sort</option> | |
| <option value="kosaraju" {% if algo == 'kosaraju' %}selected{% endif %}>Kosaraju SCC</option> | |
| <option value="astar" {% if algo == 'astar' %}selected{% endif %}>A* Search</option> | |
| <option value="longest_path_dag" {% if algo == 'longest_path_dag' %}selected{% endif %}>Longest Path (DAG)</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label">Start Node</label> | |
| <input type="number" name="start" value="0" min="0" class="form-input"> | |
| </div> | |
| <div class="form-group" id="targetGroup"> | |
| <label class="form-label">Target Node (optional for traversal)</label> | |
| <input type="number" name="target" value="" class="form-input" placeholder="Leave empty for traversal only"> | |
| </div> | |
| <button type="submit" class="btn-liquid btn-liquid-glow" id="runBtn">Execute Algorithm</button> | |
| </form> | |
| {% if graph_data %} | |
| <div class="graph-legend"> | |
| <div class="legend-item"><div class="legend-dot" style="background:#06b6d4;"></div> Node</div> | |
| <div class="legend-item"><div class="legend-dot" style="background:#8b5cf6;"></div> Edge</div> | |
| <div class="legend-item"><div class="legend-dot" style="background:#10b981;"></div> Visited</div> | |
| <div class="legend-item"><div class="legend-dot" style="background:#f59e0b;"></div> Highlight</div> | |
| </div> | |
| {% endif %} | |
| </div> | |
| </div> | |
| {% if graph_data %} | |
| <div class="glass-card animate-in delay-3" style="margin-top: 24px;"> | |
| <h3 style="font-family: 'Orbitron'; color: var(--bright-cyan); margin-bottom: 12px;">Graph Visualization</h3> | |
| <div id="graph-viz"> | |
| <div class="graph-overlay" id="graphOverlay"> | |
| <div style="font-size: 32px; margin-bottom: 8px;">🕸️</div> | |
| <div>Initializing graph engine...</div> | |
| </div> | |
| </div> | |
| <p id="graph-status" style="text-align:center; color:#64748b; margin-top:8px; font-size:12px;"></p> | |
| <div style="text-align: center; margin-top: 8px;"> | |
| <button onclick="fitGraph()" class="btn-liquid btn-small">Fit View</button> | |
| <button onclick="togglePhysics()" class="btn-liquid btn-small">Toggle Physics</button> | |
| <button onclick="stabilizeGraph()" class="btn-liquid btn-small">Stabilize</button> | |
| </div> | |
| </div> | |
| {% else %} | |
| <div class="glass-card animate-in delay-3" style="margin-top: 24px; text-align: center; padding: 60px 20px;"> | |
| <div style="font-size: 48px; margin-bottom: 16px; animation: float 3s ease-in-out infinite;">🕸️</div> | |
| <h3 style="font-family: 'Orbitron'; color: var(--bright-cyan); margin-bottom: 8px;">No Graph Generated Yet</h3> | |
| <p style="color: #64748b;">Create a random graph, use "Auto-Fill Random" for quick demo, or enter custom edges.</p> | |
| </div> | |
| {% endif %} | |
| {% if img_data %} | |
| <div class="glass-card animate-in" style="margin-top: 24px;"> | |
| <h3 style="font-family: 'Orbitron'; color: var(--bright-cyan); margin-bottom: 12px;">Result Visualization</h3> | |
| <img src="data:image/png;base64,{{ img_data }}" style="width: 100%; border-radius: 12px; border: 1px solid rgba(255,255,255,0.06);"> | |
| </div> | |
| {% endif %} | |
| {% if result %} | |
| <div class="glass-card animate-in" style="margin-top: 24px;"> | |
| <h3 style="font-family: 'Orbitron'; color: var(--bright-cyan); margin-bottom: 12px;">Algorithm Result</h3> | |
| {% if result.status is defined %} | |
| <div class="result-metric-grid"> | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Status</div> | |
| <div class="result-detail-value" style="color: {% if result.status == 'ok' %}var(--green){% elif result.status == 'error' %}var(--red){% else %}var(--yellow){% endif %}">{{ result.status|upper }}</div> | |
| </div> | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Message</div> | |
| <div class="result-detail-value" style="font-size: 14px;">{{ result.message }}</div> | |
| </div> | |
| {% if result.vertices is defined %} | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Vertices</div> | |
| <div class="result-detail-value">{{ result.vertices }}</div> | |
| </div> | |
| {% endif %} | |
| {% if result.edges is defined %} | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Edges</div> | |
| <div class="result-detail-value">{{ result.edges }}</div> | |
| </div> | |
| {% endif %} | |
| </div> | |
| {% endif %} | |
| {% if result.execution_time_ms is defined %} | |
| <div class="result-metric-grid" style="margin-top: 12px;"> | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Execution Time</div> | |
| <div class="result-detail-value">{{ "%.4f"|format(result.execution_time_ms) }} <span style="font-size: 12px; color: #64748b;">ms</span></div> | |
| </div> | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Memory Used</div> | |
| <div class="result-detail-value">{{ "%.2f"|format(result.memory_used_kb) }} <span style="font-size: 12px; color: #64748b;">KB</span></div> | |
| </div> | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Comparisons</div> | |
| <div class="result-detail-value">{{ result.comparisons }}</div> | |
| </div> | |
| <div class="result-detail-card"> | |
| <div class="result-detail-label">Accesses</div> | |
| <div class="result-detail-value">{{ result.accesses }}</div> | |
| </div> | |
| </div> | |
| {% endif %} | |
| {% if result.algorithm is defined and result.execution_time_ms is defined %} | |
| <div class="process-flow" style="margin-top: 16px;"> | |
| <div class="process-node">{{ result.algorithm }}</div> | |
| <div class="process-arrow">→</div> | |
| <div class="process-node">{{ result.time_complexity }}</div> | |
| <div class="process-arrow">→</div> | |
| <div class="process-node">{{ result.space_complexity }}</div> | |
| </div> | |
| {% endif %} | |
| <div class="code-block" style="margin-top: 16px; max-height: 400px; overflow-y: auto;">{{ result|tojson_pretty }}</div> | |
| </div> | |
| {% endif %} | |
| {% endblock %} | |
| {% block extra_scripts %} | |
| <script type="text/javascript"> | |
| var network = null; | |
| var graphData = null; | |
| function fillRandomEdges() { | |
| var n = 6; | |
| var edges = []; | |
| var seen = new Set(); | |
| for (var i = 0; i < n; i++) { | |
| for (var j = i + 1; j < n; j++) { | |
| if (Math.random() < 0.5) { | |
| var w = Math.floor(Math.random() * 20) + 1; | |
| edges.push(i + " " + j + " " + w); | |
| seen.add(i + "-" + j); | |
| } | |
| } | |
| } | |
| // Ensure connected | |
| for (var i = 0; i < n - 1; i++) { | |
| if (!seen.has(i + "-" + (i+1)) && !seen.has((i+1) + "-" + i)) { | |
| edges.push(i + " " + (i+1) + " " + (Math.floor(Math.random() * 20) + 1)); | |
| } | |
| } | |
| document.getElementById('customEdges').value = edges.join("\n"); | |
| // Auto-check weighted | |
| document.getElementById('customWeighted').checked = true; | |
| // Visual feedback | |
| var ta = document.getElementById('customEdges'); | |
| ta.style.borderColor = 'var(--cyan)'; | |
| setTimeout(function() { ta.style.borderColor = ''; }, 1000); | |
| } | |
| function initGraph() { | |
| var container = document.getElementById('graph-viz'); | |
| var overlay = document.getElementById('graphOverlay'); | |
| var statusEl = document.getElementById('graph-status'); | |
| {% if graph_data %} | |
| graphData = {{ graph_data|tojson|safe }}; | |
| {% else %} | |
| graphData = null; | |
| {% endif %} | |
| if (!graphData || !container) { | |
| if (statusEl) statusEl.textContent = 'No graph data available'; | |
| return; | |
| } | |
| if (typeof vis === 'undefined') { | |
| if (overlay) overlay.innerHTML = '<div style="color:var(--red)">⚠ vis.js failed to load.<br>Check internet connection.</div>'; | |
| if (statusEl) statusEl.innerHTML = '<span style="color:var(--red)">Error: vis.js library not loaded</span>'; | |
| return; | |
| } | |
| try { | |
| var nodes = new vis.DataSet(graphData.nodes || []); | |
| var edges = new vis.DataSet(graphData.edges || []); | |
| var data = { nodes: nodes, edges: edges }; | |
| var options = { | |
| nodes: { | |
| shape: 'dot', | |
| size: 28, | |
| font: { size: 16, color: '#f8fafc', face: 'Orbitron', strokeWidth: 0 }, | |
| borderWidth: 2, | |
| shadow: { enabled: true, color: 'rgba(6,182,212,0.5)', size: 10, x: 0, y: 0 }, | |
| color: { background: '#06b6d4', border: '#22d3ee', highlight: { background: '#10b981', border: '#22d3ee' } } | |
| }, | |
| edges: { | |
| width: 2, | |
| color: { color: '#8b5cf6', highlight: '#22d3ee', hover: '#22d3ee' }, | |
| font: { color: '#f59e0b', size: 12, align: 'middle', strokeWidth: 0 }, | |
| smooth: { type: 'continuous', roundness: 0.2 }, | |
| shadow: { enabled: true, color: 'rgba(139,92,246,0.3)', size: 5, x: 0, y: 0 }, | |
| arrows: { to: { enabled: graphData.directed === true, scaleFactor: 0.8 } } | |
| }, | |
| physics: { | |
| enabled: true, | |
| stabilization: { | |
| enabled: true, | |
| iterations: 200, | |
| updateInterval: 25, | |
| onlyDynamicEdges: false, | |
| fit: true | |
| }, | |
| barnesHut: { | |
| gravitationalConstant: -4000, | |
| centralGravity: 0.3, | |
| springLength: 140, | |
| springConstant: 0.04, | |
| damping: 0.09, | |
| avoidOverlap: 0.2 | |
| } | |
| }, | |
| interaction: { | |
| hover: true, | |
| tooltipDelay: 200, | |
| hideEdgesOnDrag: true, | |
| navigationButtons: true, | |
| keyboard: true | |
| }, | |
| layout: { randomSeed: 42 } | |
| }; | |
| network = new vis.Network(container, data, options); | |
| network.on("stabilizationIterationsDone", function() { | |
| if (overlay) overlay.style.display = 'none'; | |
| network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } }); | |
| if (statusEl) statusEl.innerHTML = '<span style="color:var(--green)">✓</span> Graph ready — ' + (graphData.nodes||[]).length + ' nodes, ' + (graphData.edges||[]).length + ' edges'; | |
| }); | |
| network.on("stabilizationProgress", function(params) { | |
| if (overlay && overlay.style.display !== 'none') { | |
| overlay.innerHTML = '<div style="color:var(--cyan)">Stabilizing... ' + Math.round(params.iterations/200*100) + '%</div>'; | |
| } | |
| }); | |
| var runForm = document.getElementById('runForm'); | |
| if (runForm) { | |
| runForm.addEventListener('submit', function(e) { | |
| document.getElementById('graphJson').value = JSON.stringify(graphData); | |
| }); | |
| } | |
| } catch (err) { | |
| console.error('Graph render error:', err); | |
| if (overlay) overlay.innerHTML = '<div style="color:var(--red)">⚠ Render error: ' + err.message + '</div>'; | |
| if (statusEl) statusEl.innerHTML = '<span style="color:var(--red)">Error: ' + err.message + '</span>'; | |
| } | |
| } | |
| function fitGraph() { | |
| if (network) network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } }); | |
| } | |
| function togglePhysics() { | |
| if (network) { | |
| var enabled = network.physics.physicsEnabled; | |
| network.setOptions({ physics: { enabled: !enabled } }); | |
| } | |
| } | |
| function stabilizeGraph() { | |
| if (network) { | |
| network.stabilize(); | |
| network.fit(); | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', function() { | |
| var algoSelect = document.getElementById('algoSelect'); | |
| var targetGroup = document.getElementById('targetGroup'); | |
| if (algoSelect && targetGroup) { | |
| var noTarget = ['bfs', 'dfs_iter', 'dfs_rec', 'floyd_warshall', 'topological', 'kosaraju', 'prim', 'kruskal', 'bellman_ford', 'longest_path_dag']; | |
| function updateTarget() { | |
| targetGroup.style.display = noTarget.includes(algoSelect.value) ? 'none' : 'block'; | |
| } | |
| algoSelect.addEventListener('change', updateTarget); | |
| updateTarget(); | |
| } | |
| initGraph(); | |
| }); | |
| </script> | |
| {% endblock %} | |