from flask import Flask, render_template_string, request, jsonify
import csv, io, time
app = Flask(__name__)
# ================= SORTING LOGIC =====================
def bubble_sort(a):
arr = a[:]; ops = []; comps = swaps = 0
n = len(arr)
for i in range(n-1):
for j in range(n-i-1):
ops.append({"type":"compare","i":j,"j":j+1}); comps += 1
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
ops.append({"type":"swap","i":j,"j":j+1}); swaps += 1
return arr, ops, comps, swaps
def insertion_sort(a):
arr = a[:]; ops = []; comps = swaps = 0
for i in range(1,len(arr)):
key = arr[i]; j = i-1
while j >= 0:
ops.append({"type":"compare","i":j,"j":i}); comps += 1
if arr[j] > key:
arr[j+1] = arr[j]
ops.append({"type":"overwrite","i":j+1,"value":arr[j]})
swaps += 1; j -= 1
else: break
arr[j+1] = key
ops.append({"type":"overwrite","i":j+1,"value":key})
return arr, ops, comps, swaps
def quick_sort(a):
arr=a[:]; ops=[]; comps=swaps=0
def partition(l,h):
nonlocal comps,swaps
pivot=arr[h]; i=l-1
for j in range(l,h):
ops.append({"type":"compare","i":j,"j":h}); comps+=1
if arr[j]<pivot:
i+=1; arr[i],arr[j]=arr[j],arr[i]
ops.append({"type":"swap","i":i,"j":j}); swaps+=1
arr[i+1],arr[h]=arr[h],arr[i+1]
ops.append({"type":"swap","i":i+1,"j":h}); swaps+=1
return i+1
def q(l,h):
if l<h:
p=partition(l,h); q(l,p-1); q(p+1,h)
if len(arr)>0:q(0,len(arr)-1)
return arr,ops,comps,swaps
def merge_sort(a):
arr=a[:]; ops=[]; comps=swaps=0
def merge(l,m,r):
nonlocal comps,swaps
L,R=arr[l:m+1],arr[m+1:r+1]; i=j=0; k=l
while i<len(L) and j<len(R):
ops.append({"type":"compare","i":l+i,"j":m+1+j}); comps+=1
if L[i]<=R[j]:
arr[k]=L[i]; ops.append({"type":"overwrite","i":k,"value":L[i]}); i+=1
else:
arr[k]=R[j]; ops.append({"type":"overwrite","i":k,"value":R[j]}); j+=1
swaps+=1; k+=1
while i<len(L):
arr[k]=L[i]; ops.append({"type":"overwrite","i":k,"value":L[i]})
i+=1; k+=1; swaps+=1
while j<len(R):
arr[k]=R[j]; ops.append({"type":"overwrite","i":k,"value":R[j]})
j+=1; k+=1; swaps+=1
def m(l,r):
if l<r:
mid=(l+r)//2; m(l,mid); m(mid+1,r); merge(l,mid,r)
if len(arr)>0:m(0,len(arr)-1)
return arr,ops,comps,swaps
THEORY = {
"bubble":{"time":"O(n²)","space":"O(1)","stable":"Yes"},
"insertion":{"time":"O(n)","space":"O(1)","stable":"Yes"},
"quick":{"time":"O(n log n)","space":"O(log n)","stable":"No"},
"merge":{"time":"O(n log n)","space":"O(n)","stable":"Yes"}
}
# ================= FLASK ROUTES =====================
@app.route("/")
def index():
return render_template_string("""
<!DOCTYPE html><html><head><meta charset="utf-8">
<title>Adaptive Sorting Visualizer (CSV Upload)</title>
<style>
body{background:#0f172a;color:#f8fafc;text-align:center;font-family:Arial}
#bars{display:flex;align-items:flex-end;justify-content:center;height:400px;margin:20px;gap:4px}
.bar-container{display:flex;flex-direction:column;align-items:center;justify-content:flex-end}
.bar{background:#38bdf8;width:12px;transition:all .1s}
.bar-value{color:#f8fafc;font-size:10px;margin-top:2px}
.bar.comp{background:#facc15}
.bar.swap{background:#ef4444}
input,button{padding:8px;margin:5px;border:none;border-radius:6px}
button{background:#38bdf8;font-weight:bold;cursor:pointer}
.stats{margin-top:10px;font-size:14px}
#addForm{margin-top:20px}
</style></head><body>
<h2>🧩 Adaptive Sorting Visualizer (Upload CSV)</h2>
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="file" accept=".csv" required>
<label><input type="checkbox" id="stable"> Prefer Stable Sort </label>
<button type="submit">Upload & Analyze</button>
</form>
<div id="bars"></div>
<div class="stats" id="stats"></div>
<div id="addForm" style="display:none;">
<h3>➕ Add New Data Point</h3>
<input type="number" id="newVal" placeholder="Enter number">
<button onclick="addData()">Add & Re-sort</button>
</div>
<script>
let arr=[],ops=[],index=0,maxVal=1,lastAlgo="insertion";
document.getElementById("uploadForm").addEventListener("submit",async e=>{
e.preventDefault();
const f=new FormData(e.target);
f.set("stable",document.getElementById("stable").checked?'true':'false');
const res=await fetch("/upload",{method:"POST",body:f});
const data=await res.json();
arr=data.original_array;ops=data.operations;maxVal=Math.max(...arr);
lastAlgo=data.algorithm;
draw();index=0;animate();
showStats(data);
document.getElementById("addForm").style.display='block';
});
function draw(){
const bars=document.getElementById("bars");bars.innerHTML='';
arr.forEach(v=>{
const cont=document.createElement('div');
cont.className='bar-container';
const b=document.createElement('div');
b.className='bar';
b.style.height=(v/maxVal*380)+'px';
b.style.width=(600/arr.length)+'px';
const val=document.createElement('div');
val.className='bar-value';
val.textContent=v;
cont.appendChild(b);
cont.appendChild(val);
bars.appendChild(cont);
});
}
function animate(){
if(index>=ops.length)return;
const o=ops[index++];const bars=document.querySelectorAll('.bar');
const values=document.querySelectorAll('.bar-value');
bars.forEach(x=>x.classList.remove('comp','swap'));
if(o.type==='compare'){bars[o.i].classList.add('comp');bars[o.j].classList.add('comp');}
if(o.type==='swap'){
let h=bars[o.i].style.height;bars[o.i].style.height=bars[o.j].style.height;bars[o.j].style.height=h;
let tmpVal=values[o.i].textContent;values[o.i].textContent=values[o.j].textContent;values[o.j].textContent=tmpVal;
bars[o.i].classList.add('swap');bars[o.j].classList.add('swap');
}
if(o.type==='overwrite'){
bars[o.i].style.height=(o.value/maxVal*380)+'px';
values[o.i].textContent=o.value;
bars[o.i].classList.add('swap');
}
setTimeout(animate,20);
}
function showStats(d){
document.getElementById("stats").innerHTML=
`Algorithm: <b>${d.algorithm.toUpperCase()}</b> | Size: ${d.size} | Comparisons: ${d.comparisons} | Swaps/Writes: ${d.swaps_writes} | Time: ${d.time_taken.toFixed(4)}s<br>
Stable: ${d.theoretical.stable} | Time Complexity: ${d.theoretical.time} | Space: ${d.theoretical.space}`;
}
async function addData(){
const val=parseFloat(document.getElementById("newVal").value);
if(isNaN(val))return alert("Enter valid number!");
arr.push(val);
const res=await fetch("/add_data",{method:"POST",headers:{'Content-Type':'application/json'},body:JSON.stringify({data:arr})});
const data=await res.json();
arr=data.original_array;ops=data.operations;maxVal=Math.max(...arr);
draw();index=0;animate();showStats(data);
}
</script></body></html>
""")
# ================= API ROUTES =====================
@app.route("/upload", methods=["POST"])
def upload():
file = request.files.get("file")
prefer_stable = request.form.get("stable") == "true"
if not file: return jsonify({"error": "No file uploaded"}), 400
stream = io.StringIO(file.stream.read().decode("utf-8"))
reader = csv.reader(stream)
arr=[]
for row in reader:
for val in row:
try: arr.append(float(val))
except: pass
n=len(arr)
if n==0: return jsonify({"error": "No valid numbers"}),400
if n <= 30:
algo = "bubble"
elif prefer_stable:
algo = "merge"
else:
algo = "quick"
start=time.time()
if algo=="bubble": sorted_arr,ops,comps,swaps=bubble_sort(arr)
elif algo=="merge": sorted_arr,ops,comps,swaps=merge_sort(arr)
elif algo=="insertion": sorted_arr,ops,comps,swaps=insertion_sort(arr)
else: sorted_arr,ops,comps,swaps=quick_sort(arr)
elapsed=time.time()-start
return jsonify({
"algorithm":algo,
"size":n,
"original_array":arr,
"operations":ops,
"comparisons":comps,
"swaps_writes":swaps,
"time_taken":elapsed,
"theoretical":THEORY[algo]
})
@app.route("/add_data", methods=["POST"])
def add_data():
data = request.get_json().get("data", [])
if not data: return jsonify({"error":"No data"}),400
start=time.time()
sorted_arr,ops,comps,swaps=insertion_sort(data)
elapsed=time.time()-start
return jsonify({
"algorithm":"insertion",
"size":len(sorted_arr),
"original_array":sorted_arr,
"operations":ops,
"comparisons":comps,
"swaps_writes":swaps,
"time_taken":elapsed,
"theoretical":THEORY["insertion"]
})
if __name__=="__main__":
app.run(debug=True)
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)