TD-jayadeera commited on
Commit
26ee80c
·
1 Parent(s): 80505f5

Implement initial project structure and setup

Browse files
.gitignore ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ ENV/
26
+ env/
27
+
28
+ # IDE
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+ *.swo
33
+
34
+ # Project specific
35
+ output/
36
+ *.gif
37
+ .env
38
+
39
+ # OS specific
40
+ .DS_Store
41
+ Thumbs.db
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
app.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import gradio as gr
4
+ from openai import OpenAI
5
+
6
+ # Get API key from environment
7
+ api_key = os.environ.get("NEBIUS_API_KEY")
8
+ if not api_key:
9
+ print("Warning: NEBIUS_API_KEY environment variable not set. LLM features will be disabled.")
10
+ client = None
11
+ else:
12
+ client = OpenAI(
13
+ base_url="https://api.studio.nebius.com/v1/",
14
+ api_key=api_key,
15
+ )
16
+
17
+ # Import visualization tools and helper
18
+ from tools.visualizer_linked_list import generate_linked_list_gif
19
+ from tools.visualizer_sort import generate_bubble_sort_gif
20
+ from tools.utility_helpers import ensure_output_dir
21
+ from tools.code_analyzer import CodeAnalyzer
22
+
23
+ ensure_output_dir() # make sure ./output/ exists
24
+ code_analyzer = CodeAnalyzer()
25
+
26
+ PLACEHOLDER_IMG = os.path.join("output", "no_visualization.gif")
27
+
28
+ def create_placeholder_gif():
29
+ # Create a simple placeholder GIF if it doesn't exist
30
+ if not os.path.exists(PLACEHOLDER_IMG):
31
+ from PIL import Image, ImageDraw
32
+ img = Image.new('RGB', (400, 100), color='white')
33
+ d = ImageDraw.Draw(img)
34
+ d.text((10, 40), "No visualization available", fill=(0, 0, 0))
35
+ img.save(PLACEHOLDER_IMG, format='GIF')
36
+
37
+ create_placeholder_gif()
38
+
39
+ def explain_algorithm_with_llm(code_snippet: str, code_info: dict = None):
40
+ """
41
+ Send a prompt to Nebius-hosted LLM (Meta-Llama-3.1-70B-Instruct).
42
+ Returns the model's explanation text.
43
+ """
44
+ if not client:
45
+ return "LLM features are disabled. Please set the NEBIUS_API_KEY environment variable to enable AI explanations."
46
+
47
+ if code_info:
48
+ prompt = code_analyzer.get_explanation_prompt(code_info)
49
+ prompt += f"\n\nCode:\n{code_snippet}"
50
+ else:
51
+ prompt = f"Please explain this code step by step:\n\n{code_snippet}"
52
+
53
+ response = client.chat.completions.create(
54
+ model="meta-llama/Meta-Llama-3.1-70B-Instruct",
55
+ messages=[{"role": "user", "content": prompt}],
56
+ temperature=0.6
57
+ )
58
+ return response.choices[0].message.content
59
+
60
+ def extract_array_from_code(code: str):
61
+ import re
62
+ # Try to find Python list or comma-separated numbers
63
+ array_match = re.search(r'\[([\d\s,]+)\]', code)
64
+ if array_match:
65
+ try:
66
+ return [int(x.strip()) for x in array_match.group(1).split(',') if x.strip()]
67
+ except Exception:
68
+ pass
69
+ # Try to find comma-separated numbers in the code
70
+ csv_match = re.search(r'(\d+(?:\s*,\s*\d+)+)', code)
71
+ if csv_match:
72
+ try:
73
+ return [int(x.strip()) for x in csv_match.group(1).split(',') if x.strip()]
74
+ except Exception:
75
+ pass
76
+ return None
77
+
78
+ def extract_linked_list_ops_from_code(code: str):
79
+ import re
80
+ operations = []
81
+ for line in code.split('\n'):
82
+ if 'insert' in line.lower():
83
+ num_match = re.search(r'insert\s+(\d+)', line.lower())
84
+ if num_match:
85
+ operations.append(f"insert {num_match.group(1)}")
86
+ elif 'delete' in line.lower():
87
+ operations.append("delete")
88
+ return operations if operations else None
89
+
90
+ def generate_visualization(code_info: dict, code: str) -> str:
91
+ """
92
+ Generate appropriate visualization based on code analysis.
93
+ Returns the path to the generated GIF.
94
+ """
95
+ gif_name = f"{uuid.uuid4()}.gif"
96
+ output_path = os.path.join("output", gif_name)
97
+
98
+ # Extract values for visualization from the code
99
+ try:
100
+ # For sorting algorithms, try to find array/list in the code
101
+ if any('sort' in algo for algo in code_info['patterns']['algorithms']):
102
+ values = extract_array_from_code(code)
103
+ if values:
104
+ generate_bubble_sort_gif(values, output_path)
105
+ return output_path
106
+
107
+ # For linked list operations
108
+ if any('linked_list' in ds for ds in code_info['patterns']['data_structures']):
109
+ operations = extract_linked_list_ops_from_code(code)
110
+ if operations:
111
+ generate_linked_list_gif(operations, output_path)
112
+ return output_path
113
+ except Exception as e:
114
+ print(f"Error generating visualization: {e}")
115
+
116
+ return PLACEHOLDER_IMG
117
+
118
+ def process_code(code_snippet: str):
119
+ """
120
+ Process the input code:
121
+ 1. Analyze the code
122
+ 2. Generate visualization
123
+ 3. Get LLM explanation
124
+ """
125
+ # Analyze the code
126
+ code_info = code_analyzer.analyze_code(code_snippet)
127
+ debug_log = []
128
+ if code_info.get('type') == 'error':
129
+ return f"❌ Code analysis failed: {code_info.get('error')}", PLACEHOLDER_IMG
130
+
131
+ # Generate visualization
132
+ visualization_path = generate_visualization(code_info, code_snippet)
133
+ no_visual = (visualization_path == PLACEHOLDER_IMG)
134
+
135
+ # Get LLM explanation
136
+ explanation = explain_algorithm_with_llm(code_snippet, code_info)
137
+
138
+ # Create a beautiful markdown output
139
+ markdown_output = f"""
140
+ # 📊 Code Analysis Report
141
+
142
+ ## 🎯 Code Type & Name
143
+ - **Type:** {code_info['type'].title()}
144
+ {f"- **Name:** `{code_info['name']}`" if code_info['name'] else ""}
145
+ - **Complexity:** `{code_info['complexity']}`
146
+
147
+ ## 🔍 Detected Patterns
148
+ """
149
+
150
+ if code_info['patterns']['algorithms']:
151
+ markdown_output += "\n### 🧮 Algorithms\n"
152
+ for algo in code_info['patterns']['algorithms']:
153
+ markdown_output += f"- {algo.replace(':', ': ').title()}\n"
154
+
155
+ if code_info['patterns']['data_structures']:
156
+ markdown_output += "\n### 📚 Data Structures\n"
157
+ for ds in code_info['patterns']['data_structures']:
158
+ markdown_output += f"- {ds.replace(':', ': ').title()}\n"
159
+
160
+ markdown_output += "\n## 📝 Code Structure\n"
161
+ structure = code_info['structure']
162
+ markdown_output += f"- {'✅' if structure['has_recursion'] else '❌'} Recursion\n"
163
+ markdown_output += f"- {'✅' if structure['has_classes'] else '❌'} Classes\n"
164
+ markdown_output += f"- {'✅' if structure['has_functions'] else '❌'} Functions\n"
165
+ markdown_output += f"- {'✅' if structure['has_loops'] else '❌'} Loops\n"
166
+
167
+ if no_visual:
168
+ markdown_output += """
169
+ ## ⚠️ Visualization Note
170
+ No visualization could be generated for this code.
171
+
172
+ **Supported Visualizations:**
173
+ - Bubble Sort (with array)
174
+ - Linked List (with 'insert N'/'delete' lines)
175
+ """
176
+
177
+ markdown_output += "\n## 📖 Explanation\n"
178
+ markdown_output += explanation
179
+
180
+ return markdown_output, visualization_path
181
+
182
+ # Custom CSS
183
+ css = """
184
+ .code-input {
185
+ font-family: 'Consolas', 'Monaco', monospace !important;
186
+ font-size: 14px !important;
187
+ }
188
+ .explanation-output {
189
+ font-size: 14px !important;
190
+ line-height: 1.6 !important;
191
+ }
192
+ .visualization-output {
193
+ border-radius: 8px !important;
194
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
195
+ }
196
+ """
197
+
198
+ # Gradio UI
199
+ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
200
+ gr.Markdown("""
201
+ # 🧠 CodeViz: Visual Learning of Algorithms
202
+
203
+ Enter your code below to get:
204
+ 1. A detailed explanation of how it works
205
+ 2. A visualization of the algorithm/data structure
206
+ 3. Analysis of its complexity and patterns
207
+
208
+ Examples:
209
+ ```python
210
+ # Bubble Sort
211
+ def bubble_sort(arr):
212
+ n = len(arr)
213
+ for i in range(n):
214
+ for j in range(0, n-i-1):
215
+ if arr[j] > arr[j+1]:
216
+ arr[j], arr[j+1] = arr[j+1], arr[j]
217
+ return arr
218
+
219
+ # Test with array
220
+ arr = [64, 34, 25, 12, 22, 11, 90]
221
+ ```
222
+
223
+ ```python
224
+ # Linked List Insert
225
+ class Node:
226
+ def __init__(self, data):
227
+ self.data = data
228
+ self.next = None
229
+
230
+ def insert_at_end(head, data):
231
+ new_node = Node(data)
232
+ if head is None:
233
+ return new_node
234
+ current = head
235
+ while current.next:
236
+ current = current.next
237
+ current.next = new_node
238
+ return head
239
+ ```
240
+ """)
241
+
242
+ with gr.Row():
243
+ with gr.Column(scale=1):
244
+ code_input = gr.Textbox(
245
+ label="Enter your code",
246
+ lines=10,
247
+ placeholder="Paste your code here...",
248
+ elem_classes=["code-input"],
249
+ type="text"
250
+ )
251
+ run_btn = gr.Button("Visualize & Explain", variant="primary")
252
+
253
+ with gr.Column(scale=1):
254
+ explanation_output = gr.Markdown(
255
+ label="Explanation & Analysis",
256
+ elem_classes=["explanation-output"]
257
+ )
258
+ visualization_output = gr.Image(
259
+ label="Visualization",
260
+ type="filepath",
261
+ elem_classes=["visualization-output"]
262
+ )
263
+
264
+ run_btn.click(
265
+ fn=process_code,
266
+ inputs=[code_input],
267
+ outputs=[explanation_output, visualization_output],
268
+ api_name="process_code"
269
+ )
270
+
271
+ if __name__ == "__main__":
272
+ demo.queue().launch(
273
+ server_name="0.0.0.0",
274
+ server_port=7860,
275
+ share=True, # Enable sharing for easier access
276
+ show_error=True # Show detailed error messages
277
+ )
environment.yml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: codeviz3
2
+ channels:
3
+ - conda-forge
4
+ - defaults
5
+ dependencies:
6
+ - python=3.10
7
+ - pip
8
+ - pip:
9
+ - gradio>=4.19.2
10
+ - openai
11
+ - pillow
12
+ - matplotlib
13
+ - networkx
14
+ - imageio
15
+ - uvicorn
16
+ - fastapi
17
+ - pydantic
18
+ - astroid
19
+ - tree-sitter
20
+ - tree-sitter-python
21
+ - jedi
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.19.2
2
+ mcp
3
+ openai
4
+ pillow
5
+ matplotlib
6
+ networkx
7
+ imageio
8
+ uvicorn
tools/code_analyzer.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import astroid
3
+ from typing import Dict, List, Tuple, Optional
4
+ import re
5
+
6
+ class CodeAnalyzer:
7
+ def __init__(self):
8
+ self.algorithm_patterns = {
9
+ 'sort': ['bubble', 'quick', 'merge', 'insertion', 'selection'],
10
+ 'search': ['binary', 'linear', 'depth', 'breadth'],
11
+ 'graph': ['dijkstra', 'floyd', 'bellman', 'kruskal', 'prim'],
12
+ 'tree': ['binary', 'avl', 'red-black', 'b-tree'],
13
+ 'dynamic': ['knapsack', 'longest', 'shortest', 'matrix']
14
+ }
15
+
16
+ self.data_structure_patterns = {
17
+ 'linked_list': ['node', 'next', 'prev', 'head', 'tail'],
18
+ 'tree': ['node', 'left', 'right', 'root', 'leaf'],
19
+ 'graph': ['vertex', 'edge', 'adjacent', 'neighbor'],
20
+ 'stack': ['push', 'pop', 'peek', 'top'],
21
+ 'queue': ['enqueue', 'dequeue', 'front', 'rear'],
22
+ 'heap': ['heapify', 'sift', 'priority']
23
+ }
24
+
25
+ def analyze_code(self, code: str) -> Dict:
26
+ """
27
+ Analyze the given code and return information about its type and structure.
28
+ """
29
+ try:
30
+ # Parse the code using ast
31
+ tree = ast.parse(code)
32
+
33
+ # Get basic code information
34
+ info = {
35
+ 'type': 'unknown',
36
+ 'name': self._get_function_name(tree),
37
+ 'complexity': self._analyze_complexity(tree),
38
+ 'patterns': self._find_patterns(code),
39
+ 'structure': self._analyze_structure(tree)
40
+ }
41
+
42
+ # Determine the type of code
43
+ info['type'] = self._determine_code_type(info['patterns'])
44
+
45
+ return info
46
+ except Exception as e:
47
+ return {
48
+ 'type': 'error',
49
+ 'error': str(e)
50
+ }
51
+
52
+ def _get_function_name(self, tree: ast.AST) -> Optional[str]:
53
+ """Extract the main function name from the code."""
54
+ for node in ast.walk(tree):
55
+ if isinstance(node, ast.FunctionDef):
56
+ return node.name
57
+ return None
58
+
59
+ def _analyze_complexity(self, tree: ast.AST) -> str:
60
+ """Analyze the time complexity of the code."""
61
+ # This is a simplified version - in practice, you'd want more sophisticated analysis
62
+ loops = 0
63
+ for node in ast.walk(tree):
64
+ if isinstance(node, (ast.For, ast.While)):
65
+ loops += 1
66
+
67
+ if loops > 2:
68
+ return "O(n³) or worse"
69
+ elif loops == 2:
70
+ return "O(n²)"
71
+ elif loops == 1:
72
+ return "O(n)"
73
+ else:
74
+ return "O(1)"
75
+
76
+ def _find_patterns(self, code: str) -> Dict[str, List[str]]:
77
+ """Find patterns in the code that indicate its type."""
78
+ patterns = {
79
+ 'algorithms': [],
80
+ 'data_structures': []
81
+ }
82
+
83
+ # Check for algorithm patterns
84
+ for category, keywords in self.algorithm_patterns.items():
85
+ for keyword in keywords:
86
+ if keyword.lower() in code.lower():
87
+ patterns['algorithms'].append(f"{category}:{keyword}")
88
+
89
+ # Check for data structure patterns
90
+ for structure, keywords in self.data_structure_patterns.items():
91
+ for keyword in keywords:
92
+ if keyword.lower() in code.lower():
93
+ patterns['data_structures'].append(f"{structure}:{keyword}")
94
+
95
+ return patterns
96
+
97
+ def _analyze_structure(self, tree: ast.AST) -> Dict:
98
+ """Analyze the code structure and return relevant information."""
99
+ structure = {
100
+ 'has_recursion': False,
101
+ 'has_classes': False,
102
+ 'has_functions': False,
103
+ 'has_loops': False
104
+ }
105
+
106
+ for node in ast.walk(tree):
107
+ if isinstance(node, ast.ClassDef):
108
+ structure['has_classes'] = True
109
+ elif isinstance(node, ast.FunctionDef):
110
+ structure['has_functions'] = True
111
+ # Check for recursion: if the function calls itself
112
+ for child in ast.walk(node):
113
+ if isinstance(child, ast.Call) and isinstance(child.func, ast.Name):
114
+ if child.func.id == node.name:
115
+ structure['has_recursion'] = True
116
+ elif isinstance(node, (ast.For, ast.While)):
117
+ structure['has_loops'] = True
118
+
119
+ return structure
120
+
121
+ def _determine_code_type(self, patterns: Dict) -> str:
122
+ """Determine the type of code based on found patterns."""
123
+ if patterns['algorithms']:
124
+ return 'algorithm'
125
+ elif patterns['data_structures']:
126
+ return 'data_structure'
127
+ else:
128
+ return 'general'
129
+
130
+ def get_explanation_prompt(self, code_info: Dict) -> str:
131
+ """Generate a prompt for the LLM based on the code analysis."""
132
+ prompt = f"This code appears to be a {code_info['type']} "
133
+
134
+ if code_info['name']:
135
+ prompt += f"named '{code_info['name']}' "
136
+
137
+ prompt += f"with {code_info['complexity']} time complexity.\n\n"
138
+
139
+ if code_info['patterns']['algorithms']:
140
+ prompt += "It implements the following algorithms: " + ", ".join(code_info['patterns']['algorithms']) + ".\n"
141
+
142
+ if code_info['patterns']['data_structures']:
143
+ prompt += "It uses the following data structures: " + ", ".join(code_info['patterns']['data_structures']) + ".\n"
144
+
145
+ prompt += "\nPlease explain how this code works step by step, focusing on:\n"
146
+ prompt += "1. The main purpose and functionality\n"
147
+ prompt += "2. The key components and how they work together\n"
148
+ prompt += "3. The time and space complexity\n"
149
+ prompt += "4. Any important edge cases or considerations\n"
150
+
151
+ return prompt
tools/utility_helpers.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ def ensure_output_dir():
4
+ """
5
+ Make sure the './output' directory exists.
6
+ """
7
+ if not os.path.exists("output"):
8
+ os.makedirs("output")
tools/visualizer_linked_list.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ from io import BytesIO
3
+ from PIL import Image, ImageDraw, ImageFont
4
+
5
+ def draw_linked_list_frame(nodes):
6
+ """
7
+ Given a list of node values (strings), return a PIL Image representing the current state.
8
+ """
9
+ width = max(600, len(nodes) * 120)
10
+ img = Image.new('RGB', (width, 100), color='white')
11
+ draw = ImageDraw.Draw(img)
12
+ font = ImageFont.load_default()
13
+
14
+ for i, val in enumerate(nodes):
15
+ x0 = i * 100 + 10
16
+ # Draw node rectangle
17
+ draw.rectangle([x0, 30, x0 + 80, 70], outline='black', width=2)
18
+ draw.text((x0 + 25, 40), val, fill='black', font=font)
19
+ # Draw arrow to next node if exists
20
+ if i < len(nodes) - 1:
21
+ draw.line((x0 + 80, 50, x0 + 100, 50), fill='black', width=2)
22
+ return img
23
+
24
+ def generate_linked_list_gif(operations, output_path):
25
+ """
26
+ Given a list of operations like ["insert 5", "insert 10", "delete"], build a sequence of frames,
27
+ then save as an animated GIF to output_path.
28
+ """
29
+ frames = []
30
+ nodes = []
31
+ for op in operations:
32
+ parts = op.split()
33
+ if len(parts) >= 2 and parts[0] == "insert":
34
+ nodes.append(parts[1])
35
+ elif parts[0] == "delete":
36
+ if nodes:
37
+ nodes.pop(0) # delete head
38
+ # Draw current state
39
+ img = draw_linked_list_frame(nodes)
40
+ frames.append(img)
41
+
42
+ # Save frames as GIF
43
+ frames[0].save(
44
+ output_path,
45
+ save_all=True,
46
+ append_images=frames[1:],
47
+ duration=800,
48
+ loop=0
49
+ )
50
+ return output_path
tools/visualizer_sort.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import matplotlib.pyplot as plt
2
+ from io import BytesIO
3
+ import imageio
4
+ import numpy as np
5
+
6
+ def generate_bubble_sort_gif(values, output_path):
7
+ """
8
+ Given a list of integer values (e.g. [5,3,8,1]), produce an animated GIF showing
9
+ each comparison + swap step in bubble sort. Save the GIF to output_path.
10
+ """
11
+ if not values:
12
+ # Create a single frame with empty array if no values provided
13
+ fig, ax = plt.subplots()
14
+ ax.bar([], [], color="skyblue")
15
+ ax.set_title("Bubble Sort: No values provided")
16
+ buf = BytesIO()
17
+ plt.savefig(buf, format='png')
18
+ plt.close(fig)
19
+ buf.seek(0)
20
+ imageio.mimsave(output_path, [imageio.imread(buf)], format='GIF', fps=1)
21
+ return output_path
22
+
23
+ images = []
24
+ arr = values.copy()
25
+ n = len(arr)
26
+
27
+ # Always add initial state
28
+ fig, ax = plt.subplots()
29
+ ax.bar(range(len(arr)), arr, color="skyblue")
30
+ ax.set_title("Bubble Sort: Initial State")
31
+ buf = BytesIO()
32
+ plt.savefig(buf, format='png')
33
+ plt.close(fig)
34
+ buf.seek(0)
35
+ images.append(imageio.imread(buf))
36
+
37
+ # Perform bubble sort and capture each step
38
+ for i in range(n):
39
+ for j in range(0, n - i - 1):
40
+ if arr[j] > arr[j + 1]:
41
+ arr[j], arr[j + 1] = arr[j + 1], arr[j]
42
+ # Plot current array state as a bar chart
43
+ fig, ax = plt.subplots()
44
+ ax.bar(range(len(arr)), arr, color="skyblue")
45
+ ax.set_title(f"Bubble Sort: pass {i}, compare {j}")
46
+ buf = BytesIO()
47
+ plt.savefig(buf, format='png')
48
+ plt.close(fig)
49
+ buf.seek(0)
50
+ images.append(imageio.imread(buf))
51
+
52
+ # Add final state if no swaps were made
53
+ if len(images) == 1:
54
+ fig, ax = plt.subplots()
55
+ ax.bar(range(len(arr)), arr, color="skyblue")
56
+ ax.set_title("Bubble Sort: Final State")
57
+ buf = BytesIO()
58
+ plt.savefig(buf, format='png')
59
+ plt.close(fig)
60
+ buf.seek(0)
61
+ images.append(imageio.imread(buf))
62
+
63
+ # Save as GIF
64
+ imageio.mimsave(output_path, images, format='GIF', fps=1)
65
+ return output_path