aymnsk commited on
Commit
391a808
·
verified ·
1 Parent(s): 814f583

Upload 2 files

Browse files
Files changed (2) hide show
  1. index.html +1220 -0
  2. style.css +28 -0
index.html ADDED
@@ -0,0 +1,1220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Neon LP Solver</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Exo+2:wght@300;400;500;600;700&display=swap" rel="stylesheet">
9
+ <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
10
+ <style>
11
+ body {
12
+ font-family: 'Exo 2', sans-serif;
13
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
14
+ color: #e2e8f0;
15
+ min-height: 100vh;
16
+ }
17
+
18
+ .glass-panel {
19
+ background: rgba(15, 23, 42, 0.7);
20
+ backdrop-filter: blur(10px);
21
+ border: 1px solid rgba(94, 234, 212, 0.2);
22
+ box-shadow: 0 0 20px rgba(94, 234, 212, 0.1);
23
+ transition: all 0.3s ease;
24
+ }
25
+
26
+ .glass-panel:hover {
27
+ box-shadow: 0 0 30px rgba(94, 234, 212, 0.3);
28
+ border-color: rgba(94, 234, 212, 0.4);
29
+ }
30
+
31
+ .neon-text {
32
+ text-shadow: 0 0 5px rgba(94, 234, 212, 0.7);
33
+ }
34
+
35
+ .neon-accent {
36
+ border-color: #5eead4;
37
+ }
38
+
39
+ .neon-button {
40
+ background: rgba(94, 234, 212, 0.1);
41
+ border: 1px solid #5eead4;
42
+ color: #5eead4;
43
+ transition: all 0.3s ease;
44
+ }
45
+
46
+ .neon-button:hover {
47
+ background: rgba(94, 234, 212, 0.3);
48
+ box-shadow: 0 0 15px rgba(94, 234, 212, 0.4);
49
+ }
50
+
51
+ .input-field {
52
+ background: rgba(30, 41, 59, 0.5);
53
+ border: 1px solid rgba(94, 234, 212, 0.3);
54
+ color: #e2e8f0;
55
+ }
56
+
57
+ .input-field:focus {
58
+ outline: none;
59
+ border-color: #5eead4;
60
+ box-shadow: 0 0 10px rgba(94, 234, 212, 0.3);
61
+ }
62
+
63
+ .simplex-table {
64
+ border: 1px solid rgba(94, 234, 212, 0.3);
65
+ }
66
+
67
+ .simplex-table th, .simplex-table td {
68
+ border: 1px solid rgba(94, 234, 212, 0.2);
69
+ padding: 0.5rem;
70
+ text-align: center;
71
+ }
72
+
73
+ .simplex-table th {
74
+ background: rgba(94, 234, 212, 0.1);
75
+ }
76
+
77
+ .pivot-cell {
78
+ background: rgba(236, 72, 153, 0.3);
79
+ animation: pulse 2s infinite;
80
+ }
81
+
82
+ @keyframes pulse {
83
+ 0% { background-color: rgba(236, 72, 153, 0.3); }
84
+ 50% { background-color: rgba(236, 72, 153, 0.6); }
85
+ 100% { background-color: rgba(236, 72, 153, 0.3); }
86
+ }
87
+
88
+ .tab-button {
89
+ background: transparent;
90
+ border: none;
91
+ color: #94a3b8;
92
+ transition: all 0.3s ease;
93
+ }
94
+
95
+ .tab-button.active {
96
+ color: #5eead4;
97
+ border-bottom: 2px solid #5eead4;
98
+ }
99
+
100
+ .tab-button:hover:not(.active) {
101
+ color: #e2e8f0;
102
+ }
103
+
104
+ .scroll-container {
105
+ scrollbar-width: thin;
106
+ scrollbar-color: #5eead4 #1e293b;
107
+ }
108
+
109
+ .scroll-container::-webkit-scrollbar {
110
+ width: 8px;
111
+ height: 8px;
112
+ }
113
+
114
+ .scroll-container::-webkit-scrollbar-track {
115
+ background: #1e293b;
116
+ }
117
+
118
+ .scroll-container::-webkit-scrollbar-thumb {
119
+ background-color: #5eead4;
120
+ border-radius: 4px;
121
+ }
122
+
123
+ .fraction {
124
+ display: inline-block;
125
+ position: relative;
126
+ vertical-align: middle;
127
+ letter-spacing: 0.001em;
128
+ text-align: center;
129
+ }
130
+
131
+ .fraction > span {
132
+ display: block;
133
+ padding: 0.1em;
134
+ }
135
+
136
+ .fraction span.fdn { border-top: thin solid black; }
137
+
138
+ .fraction span.bar { display: none; }
139
+ </style>
140
+ </head>
141
+ <body class="p-4 md:p-8">
142
+ <div class="max-w-7xl mx-auto">
143
+ <header class="mb-8 text-center">
144
+ <h1 class="text-4xl md:text-5xl font-bold mb-2 neon-text">Neon LP Solver</h1>
145
+ <p class="text-lg text-cyan-200">Solve linear programming problems with simplex and dual simplex methods</p>
146
+ </header>
147
+
148
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
149
+ <!-- Problem Input Section -->
150
+ <div class="lg:col-span-1 glass-panel rounded-xl p-6">
151
+ <h2 class="text-2xl font-semibold mb-4 neon-text">Problem Input</h2>
152
+
153
+ <div class="mb-6">
154
+ <label class="block text-sm font-medium mb-2">Optimization Direction</label>
155
+ <div class="flex space-x-4">
156
+ <button id="maximize-btn" class="neon-button rounded-lg px-4 py-2 font-medium">Maximize</button>
157
+ <button id="minimize-btn" class="neon-button rounded-lg px-4 py-2 font-medium">Minimize</button>
158
+ </div>
159
+ </div>
160
+
161
+ <div class="mb-6">
162
+ <label class="block text-sm font-medium mb-2">Number of Variables</label>
163
+ <input type="number" id="var-count" min="1" max="10" value="2"
164
+ class="input-field rounded-lg px-4 py-2 w-full">
165
+ </div>
166
+
167
+ <div class="mb-6">
168
+ <label class="block text-sm font-medium mb-2">Number of Constraints</label>
169
+ <input type="number" id="constraint-count" min="1" max="10" value="2"
170
+ class="input-field rounded-lg px-4 py-2 w-full">
171
+ </div>
172
+
173
+ <button id="setup-problem-btn" class="neon-button rounded-lg px-6 py-3 w-full font-semibold mb-6">
174
+ Setup Problem
175
+ </button>
176
+
177
+ <div id="objective-function-container" class="mb-6 hidden">
178
+ <h3 class="text-lg font-medium mb-3">Objective Function</h3>
179
+ <div id="objective-coeffs" class="grid grid-cols-4 gap-2 mb-2">
180
+ <!-- Dynamically generated -->
181
+ </div>
182
+ </div>
183
+
184
+ <div id="constraints-container" class="hidden">
185
+ <h3 class="text-lg font-medium mb-3">Constraints</h3>
186
+ <div id="constraints-grid">
187
+ <!-- Dynamically generated -->
188
+ </div>
189
+ </div>
190
+
191
+ <button id="solve-btn" class="neon-button rounded-lg px-6 py-3 w-full font-semibold mt-6 hidden">
192
+ Solve Problem
193
+ </button>
194
+ </div>
195
+
196
+ <!-- Results Section -->
197
+ <div class="lg:col-span-2 glass-panel rounded-xl p-6">
198
+ <div class="flex border-b border-cyan-900 mb-4">
199
+ <button class="tab-button px-4 py-2 mr-2 active" data-tab="problem">Problem</button>
200
+ <button class="tab-button px-4 py-2 mr-2" data-tab="simplex">Simplex Method</button>
201
+ <button class="tab-button px-4 py-2 mr-2" data-tab="dual">Dual Problem</button>
202
+ <button class="tab-button px-4 py-2" data-tab="dual-simplex">Dual Simplex</button>
203
+ </div>
204
+
205
+ <div id="problem-tab" class="tab-content">
206
+ <h2 class="text-2xl font-semibold mb-4 neon-text">Problem Statement</h2>
207
+ <div id="problem-display" class="bg-slate-800 rounded-lg p-4 mb-6">
208
+ <p class="text-center text-lg">Enter your problem data to see it displayed here</p>
209
+ </div>
210
+
211
+ <div class="bg-slate-800 rounded-lg p-4">
212
+ <h3 class="text-xl font-medium mb-3">Standard Form</h3>
213
+ <div id="standard-form-display">
214
+ <p class="text-center">Problem will be displayed in standard form after setup</p>
215
+ </div>
216
+ </div>
217
+ </div>
218
+
219
+ <div id="simplex-tab" class="tab-content hidden">
220
+ <h2 class="text-2xl font-semibold mb-4 neon-text">Simplex Method Solution</h2>
221
+ <div id="simplex-steps" class="mb-6">
222
+ <p class="text-center">Solve the problem to see simplex method steps</p>
223
+ </div>
224
+
225
+ <div id="simplex-result" class="bg-slate-800 rounded-lg p-4 hidden">
226
+ <h3 class="text-xl font-medium mb-3">Optimal Solution</h3>
227
+ <div id="simplex-solution">
228
+ <!-- Results will be displayed here -->
229
+ </div>
230
+ </div>
231
+ </div>
232
+
233
+ <div id="dual-tab" class="tab-content hidden">
234
+ <h2 class="text-2xl font-semibold mb-4 neon-text">Dual Problem</h2>
235
+ <div id="dual-problem-display" class="bg-slate-800 rounded-lg p-4 mb-6">
236
+ <p class="text-center">Solve the primal problem to see the dual formulation</p>
237
+ </div>
238
+ </div>
239
+
240
+ <div id="dual-simplex-tab" class="tab-content hidden">
241
+ <h2 class="text-2xl font-semibold mb-4 neon-text">Dual Simplex Method Solution</h2>
242
+ <div id="dual-simplex-steps" class="mb-6">
243
+ <p class="text-center">Generate the dual problem to see solution steps</p>
244
+ </div>
245
+
246
+ <div id="dual-simplex-result" class="bg-slate-800 rounded-lg p-4 hidden">
247
+ <h3 class="text-xl font-medium mb-3">Optimal Solution</h3>
248
+ <div id="dual-simplex-solution">
249
+ <!-- Results will be displayed here -->
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ </div>
256
+
257
+ <script>
258
+ // Global variables
259
+ let problemData = {
260
+ direction: 'max',
261
+ varCount: 2,
262
+ constraintCount: 2,
263
+ objectiveCoeffs: [3, 4],
264
+ constraints: [
265
+ { coeffs: [2, 1], sign: '≤', rhs: 10 },
266
+ { coeffs: [1, 2], sign: '≤', rhs: 12 }
267
+ ]
268
+ };
269
+
270
+ let simplexSolution = null;
271
+ let dualProblem = null;
272
+ let dualSimplexSolution = null;
273
+
274
+ // DOM elements
275
+ const maximizeBtn = document.getElementById('maximize-btn');
276
+ const minimizeBtn = document.getElementById('minimize-btn');
277
+ const varCountInput = document.getElementById('var-count');
278
+ const constraintCountInput = document.getElementById('constraint-count');
279
+ const setupProblemBtn = document.getElementById('setup-problem-btn');
280
+ const objectiveFunctionContainer = document.getElementById('objective-function-container');
281
+ const objectiveCoeffsDiv = document.getElementById('objective-coeffs');
282
+ const constraintsContainer = document.getElementById('constraints-container');
283
+ const constraintsGrid = document.getElementById('constraints-grid');
284
+ const solveBtn = document.getElementById('solve-btn');
285
+
286
+ const tabButtons = document.querySelectorAll('.tab-button');
287
+ const tabContents = document.querySelectorAll('.tab-content');
288
+
289
+ const problemDisplay = document.getElementById('problem-display');
290
+ const standardFormDisplay = document.getElementById('standard-form-display');
291
+ const simplexStepsDiv = document.getElementById('simplex-steps');
292
+ const simplexResultDiv = document.getElementById('simplex-result');
293
+ const simplexSolutionDiv = document.getElementById('simplex-solution');
294
+ const dualProblemDisplay = document.getElementById('dual-problem-display');
295
+ const dualSimplexStepsDiv = document.getElementById('dual-simplex-steps');
296
+ const dualSimplexResultDiv = document.getElementById('dual-simplex-result');
297
+ const dualSimplexSolutionDiv = document.getElementById('dual-simplex-solution');
298
+
299
+ // Initialize
300
+ document.addEventListener('DOMContentLoaded', () => {
301
+ maximizeBtn.classList.add('bg-cyan-600', 'text-white');
302
+ setupProblem();
303
+
304
+ // Add event listeners
305
+ maximizeBtn.addEventListener('click', setMaximize);
306
+ minimizeBtn.addEventListener('click', setMinimize);
307
+ varCountInput.addEventListener('change', validateInputs);
308
+ constraintCountInput.addEventListener('change', validateInputs);
309
+ setupProblemBtn.addEventListener('click', setupProblem);
310
+ solveBtn.addEventListener('click', solveProblem);
311
+
312
+ tabButtons.forEach(button => {
313
+ button.addEventListener('click', () => {
314
+ const tabId = button.getAttribute('data-tab');
315
+ switchTab(tabId);
316
+ });
317
+ });
318
+ });
319
+
320
+ // Functions
321
+ function setMaximize() {
322
+ problemData.direction = 'max';
323
+ maximizeBtn.classList.add('bg-cyan-600', 'text-white');
324
+ minimizeBtn.classList.remove('bg-cyan-600', 'text-white');
325
+ updateProblemDisplay();
326
+ }
327
+
328
+ function setMinimize() {
329
+ problemData.direction = 'min';
330
+ minimizeBtn.classList.add('bg-cyan-600', 'text-white');
331
+ maximizeBtn.classList.remove('bg-cyan-600', 'text-white');
332
+ updateProblemDisplay();
333
+ }
334
+
335
+ function validateInputs() {
336
+ const vars = parseInt(varCountInput.value);
337
+ const constraints = parseInt(constraintCountInput.value);
338
+
339
+ if (isNaN(vars) || vars < 1 || vars > 10) {
340
+ varCountInput.value = problemData.varCount;
341
+ return false;
342
+ }
343
+
344
+ if (isNaN(constraints) || constraints < 1 || constraints > 10) {
345
+ constraintCountInput.value = problemData.constraintCount;
346
+ return false;
347
+ }
348
+
349
+ return true;
350
+ }
351
+
352
+ function setupProblem() {
353
+ if (!validateInputs()) return;
354
+
355
+ problemData.varCount = parseInt(varCountInput.value);
356
+ problemData.constraintCount = parseInt(constraintCountInput.value);
357
+
358
+ // Initialize empty arrays if needed
359
+ if (!problemData.objectiveCoeffs || problemData.objectiveCoeffs.length !== problemData.varCount) {
360
+ problemData.objectiveCoeffs = new Array(problemData.varCount).fill(0);
361
+ }
362
+
363
+ if (!problemData.constraints || problemData.constraints.length !== problemData.constraintCount) {
364
+ problemData.constraints = new Array(problemData.constraintCount).fill().map(() => ({
365
+ coeffs: new Array(problemData.varCount).fill(0),
366
+ sign: '≤',
367
+ rhs: 0
368
+ }));
369
+ }
370
+
371
+ // Setup objective function inputs
372
+ objectiveCoeffsDiv.innerHTML = '';
373
+ for (let i = 0; i < problemData.varCount; i++) {
374
+ const coeffDiv = document.createElement('div');
375
+ coeffDiv.className = 'col-span-3 flex items-center';
376
+
377
+ const coeffInput = document.createElement('input');
378
+ coeffInput.type = 'number';
379
+ coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full';
380
+ coeffInput.placeholder = `Coefficient for x${i+1}`;
381
+ coeffInput.value = problemData.objectiveCoeffs[i] || '';
382
+ coeffInput.step = 'any';
383
+
384
+ coeffInput.addEventListener('input', () => {
385
+ problemData.objectiveCoeffs[i] = parseFloat(coeffInput.value) || 0;
386
+ updateProblemDisplay();
387
+ });
388
+
389
+ const varLabel = document.createElement('span');
390
+ varLabel.className = 'ml-2';
391
+ varLabel.textContent = `x${i+1}`;
392
+
393
+ if (i < problemData.varCount - 1) {
394
+ const plusSign = document.createElement('span');
395
+ plusSign.className = 'ml-2';
396
+ plusSign.textContent = '+';
397
+ coeffDiv.appendChild(plusSign);
398
+ }
399
+
400
+ coeffDiv.appendChild(coeffInput);
401
+ coeffDiv.appendChild(varLabel);
402
+ objectiveCoeffsDiv.appendChild(coeffDiv);
403
+ }
404
+
405
+ // Setup constraints inputs
406
+ constraintsGrid.innerHTML = '';
407
+ for (let i = 0; i < problemData.constraintCount; i++) {
408
+ const constraint = problemData.constraints[i];
409
+ const constraintRow = document.createElement('div');
410
+ constraintRow.className = 'grid grid-cols-12 gap-2 mb-3 items-center';
411
+
412
+ // Coefficients
413
+ for (let j = 0; j < problemData.varCount; j++) {
414
+ const coeffInput = document.createElement('input');
415
+ coeffInput.type = 'number';
416
+ coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full';
417
+ coeffInput.placeholder = `a${i+1}${j+1}`;
418
+ coeffInput.value = constraint.coeffs[j] || '';
419
+ coeffInput.step = 'any';
420
+
421
+ coeffInput.addEventListener('input', () => {
422
+ constraint.coeffs[j] = parseFloat(coeffInput.value) || 0;
423
+ updateProblemDisplay();
424
+ });
425
+
426
+ const varLabel = document.createElement('span');
427
+ varLabel.className = 'text-sm';
428
+ varLabel.textContent = `x${j+1}`;
429
+
430
+ const coeffContainer = document.createElement('div');
431
+ coeffContainer.className = 'col-span-1 flex items-center';
432
+ coeffContainer.appendChild(coeffInput);
433
+
434
+ if (j < problemData.varCount - 1) {
435
+ const plusSign = document.createElement('span');
436
+ plusSign.className = 'ml-1';
437
+ plusSign.textContent = '+';
438
+ coeffContainer.appendChild(plusSign);
439
+ }
440
+
441
+ constraintRow.appendChild(coeffContainer);
442
+ }
443
+
444
+ // Sign selector
445
+ const signSelect = document.createElement('select');
446
+ signSelect.className = 'input-field rounded-lg px-2 py-1 col-span-1';
447
+ signSelect.innerHTML = `
448
+ <option value="≤">≤</option>
449
+ <option value="=">=</option>
450
+ <option value="≥">≥</option>
451
+ `;
452
+ signSelect.value = constraint.sign;
453
+
454
+ signSelect.addEventListener('change', () => {
455
+ constraint.sign = signSelect.value;
456
+ updateProblemDisplay();
457
+ });
458
+
459
+ constraintRow.appendChild(signSelect);
460
+
461
+ // RHS input
462
+ const rhsInput = document.createElement('input');
463
+ rhsInput.type = 'number';
464
+ rhsInput.className = 'input-field rounded-lg px-3 py-1 col-span-2';
465
+ rhsInput.placeholder = 'RHS';
466
+ rhsInput.value = constraint.rhs || '';
467
+ rhsInput.step = 'any';
468
+
469
+ rhsInput.addEventListener('input', () => {
470
+ constraint.rhs = parseFloat(rhsInput.value) || 0;
471
+ updateProblemDisplay();
472
+ });
473
+
474
+ constraintRow.appendChild(rhsInput);
475
+ constraintsGrid.appendChild(constraintRow);
476
+ }
477
+
478
+ // Show containers
479
+ objectiveFunctionContainer.classList.remove('hidden');
480
+ constraintsContainer.classList.remove('hidden');
481
+ solveBtn.classList.remove('hidden');
482
+
483
+ // Reset solutions
484
+ simplexSolution = null;
485
+ dualProblem = null;
486
+ dualSimplexSolution = null;
487
+
488
+ updateProblemDisplay();
489
+ clearResults();
490
+ }
491
+
492
+ function updateProblemDisplay() {
493
+ // Display problem in mathematical notation
494
+ let problemText = problemData.direction === 'max' ? 'Maximize' : 'Minimize';
495
+ problemText += ': $\\displaystyle ';
496
+
497
+ // Objective function
498
+ for (let i = 0; i < problemData.objectiveCoeffs.length; i++) {
499
+ if (i > 0 && problemData.objectiveCoeffs[i] >= 0) {
500
+ problemText += ' + ';
501
+ } else if (problemData.objectiveCoeffs[i] < 0) {
502
+ problemText += ' - ';
503
+ }
504
+
505
+ const absCoeff = Math.abs(problemData.objectiveCoeffs[i]);
506
+ if (absCoeff !== 1) {
507
+ problemText += absCoeff;
508
+ }
509
+
510
+ if (absCoeff !== 0) {
511
+ problemText += `x_{${i+1}}`;
512
+ } else if (i === 0) {
513
+ problemText += '0';
514
+ }
515
+ }
516
+
517
+ problemText += '$<br><br>Subject to:<br>';
518
+
519
+ // Constraints
520
+ for (let i = 0; i < problemData.constraints.length; i++) {
521
+ const constraint = problemData.constraints[i];
522
+ problemText += '$\\displaystyle ';
523
+
524
+ let hasTerms = false;
525
+ for (let j = 0; j < constraint.coeffs.length; j++) {
526
+ if (constraint.coeffs[j] === 0) continue;
527
+ hasTerms = true;
528
+
529
+ if (j > 0 && constraint.coeffs[j] > 0) {
530
+ problemText += ' + ';
531
+ } else if (constraint.coeffs[j] < 0) {
532
+ problemText += ' - ';
533
+ } else if (j > 0) {
534
+ continue;
535
+ }
536
+
537
+ const absCoeff = Math.abs(constraint.coeffs[j]);
538
+ if (absCoeff !== 1) {
539
+ problemText += absCoeff;
540
+ }
541
+
542
+ problemText += `x_{${j+1}}`;
543
+ }
544
+
545
+ if (!hasTerms) {
546
+ problemText += '0';
547
+ }
548
+
549
+ problemText += ` ${constraint.sign} ${constraint.rhs}$<br>`;
550
+ }
551
+
552
+ // Non-negativity constraints
553
+ problemText += '<br>With: $\\displaystyle ';
554
+ for (let i = 0; i < problemData.varCount; i++) {
555
+ problemText += `x_{${i+1}} \\geq 0`;
556
+ if (i < problemData.varCount - 1) {
557
+ problemText += ', ';
558
+ }
559
+ }
560
+ problemText += '$';
561
+
562
+ problemDisplay.innerHTML = problemText;
563
+
564
+ // Display standard form
565
+ displayStandardForm();
566
+
567
+ // Update MathJax rendering
568
+ if (typeof MathJax !== 'undefined') {
569
+ MathJax.typeset();
570
+ }
571
+ }
572
+
573
+ function displayStandardForm() {
574
+ let standardText = problemData.direction === 'max' ? 'Maximize' : 'Minimize';
575
+ standardText += ': $\\displaystyle ';
576
+
577
+ // Objective function
578
+ let hasObjectiveTerms = false;
579
+ for (let i = 0; i < problemData.objectiveCoeffs.length; i++) {
580
+ if (problemData.objectiveCoeffs[i] === 0) continue;
581
+ hasObjectiveTerms = true;
582
+
583
+ if (i > 0 && problemData.objectiveCoeffs[i] >= 0) {
584
+ standardText += ' + ';
585
+ } else if (problemData.objectiveCoeffs[i] < 0) {
586
+ standardText += ' - ';
587
+ }
588
+
589
+ const absCoeff = Math.abs(problemData.objectiveCoeffs[i]);
590
+ if (absCoeff !== 1) {
591
+ standardText += absCoeff;
592
+ }
593
+
594
+ standardText += `x_{${i+1}}`;
595
+ }
596
+
597
+ if (!hasObjectiveTerms) {
598
+ standardText += '0';
599
+ }
600
+
601
+ standardText += '$<br><br>Subject to:<br>';
602
+
603
+ // Constraints in standard form
604
+ let slackIndex = 1;
605
+ for (let i = 0; i < problemData.constraints.length; i++) {
606
+ const constraint = problemData.constraints[i];
607
+ standardText += '$\\displaystyle ';
608
+
609
+ let hasTerms = false;
610
+ for (let j = 0; j < constraint.coeffs.length; j++) {
611
+ if (constraint.coeffs[j] === 0) continue;
612
+ hasTerms = true;
613
+
614
+ if (j > 0 && constraint.coeffs[j] > 0) {
615
+ standardText += ' + ';
616
+ } else if (constraint.coeffs[j] < 0) {
617
+ standardText += ' - ';
618
+ } else if (j > 0) {
619
+ continue;
620
+ }
621
+
622
+ const absCoeff = Math.abs(constraint.coeffs[j]);
623
+ if (absCoeff !== 1) {
624
+ standardText += absCoeff;
625
+ }
626
+
627
+ standardText += `x_{${j+1}}`;
628
+ }
629
+
630
+ // Add slack/surplus variables
631
+ if (constraint.sign === '≤') {
632
+ if (hasTerms) standardText += ' + ';
633
+ standardText += `s_{${slackIndex++}}`;
634
+ } else if (constraint.sign === '≥') {
635
+ if (hasTerms) standardText += ' - ';
636
+ standardText += `s_{${slackIndex++}}`;
637
+ } else if (!hasTerms) {
638
+ standardText += '0';
639
+ }
640
+
641
+ standardText += ` = ${constraint.rhs}$<br>`;
642
+ }
643
+
644
+ // Non-negativity constraints
645
+ standardText += '<br>With: $\\displaystyle ';
646
+ for (let i = 0; i < problemData.varCount; i++) {
647
+ standardText += `x_{${i+1}} \\geq 0`;
648
+ if (i < problemData.varCount - 1) {
649
+ standardText += ', ';
650
+ }
651
+ }
652
+
653
+ // Add non-negativity for slack variables
654
+ if (problemData.constraints.length > 0) {
655
+ standardText += ', ';
656
+ for (let i = 0; i < problemData.constraints.length; i++) {
657
+ standardText += `s_{${i+1}} \\geq 0`;
658
+ if (i < problemData.constraints.length - 1) {
659
+ standardText += ', ';
660
+ }
661
+ }
662
+ }
663
+ standardText += '$';
664
+
665
+ standardFormDisplay.innerHTML = standardText;
666
+
667
+ // Update MathJax rendering
668
+ if (typeof MathJax !== 'undefined') {
669
+ MathJax.typeset();
670
+ }
671
+ }
672
+
673
+ function solveProblem() {
674
+ // Validate inputs
675
+ if (!validateProblem()) return;
676
+
677
+ // Reset previous solutions
678
+ clearResults();
679
+
680
+ // Convert to standard form for simplex method
681
+ const standardForm = convertToStandardForm();
682
+
683
+ // Solve using simplex method
684
+ const solution = solveWithSimplexMethod(standardForm);
685
+ simplexSolution = solution;
686
+
687
+ displaySimplexSolution();
688
+
689
+ // Generate dual problem
690
+ generateDualProblem();
691
+ }
692
+
693
+ function validateProblem() {
694
+ // Check objective function coefficients
695
+ if (!problemData.objectiveCoeffs || problemData.objectiveCoeffs.length !== problemData.varCount) {
696
+ alert('Please enter all objective function coefficients');
697
+ return false;
698
+ }
699
+
700
+ // Check constraints
701
+ for (let i = 0; i < problemData.constraints.length; i++) {
702
+ const constraint = problemData.constraints[i];
703
+
704
+ if (!constraint.coeffs || constraint.coeffs.length !== problemData.varCount) {
705
+ alert(`Please enter all coefficients for constraint ${i+1}`);
706
+ return false;
707
+ }
708
+
709
+ if (isNaN(constraint.rhs)) {
710
+ alert(`Please enter a valid right-hand side for constraint ${i+1}`);
711
+ return false;
712
+ }
713
+ }
714
+
715
+ return true;
716
+ }
717
+
718
+ function convertToStandardForm() {
719
+ const standardForm = {
720
+ direction: problemData.direction,
721
+ variables: problemData.varCount,
722
+ constraints: [],
723
+ slackVariables: 0,
724
+ artificialVariables: 0
725
+ };
726
+
727
+ // Convert each constraint to standard form
728
+ let slackCount = 0;
729
+ let artificialCount = 0;
730
+
731
+ for (let i = 0; i < problemData.constraints.length; i++) {
732
+ const constraint = problemData.constraints[i];
733
+ const standardConstraint = {
734
+ coeffs: [...constraint.coeffs],
735
+ slack: 0,
736
+ artificial: 0,
737
+ rhs: constraint.rhs
738
+ };
739
+
740
+ if (constraint.sign === '≤') {
741
+ // Add slack variable
742
+ standardConstraint.slack = 1;
743
+ slackCount++;
744
+ } else if (constraint.sign === '≥') {
745
+ // Subtract slack and add artificial variable
746
+ standardConstraint.slack = -1;
747
+ standardConstraint.artificial = 1;
748
+ slackCount++;
749
+ artificialCount++;
750
+ } else if (constraint.sign === '=') {
751
+ // Add artificial variable
752
+ standardConstraint.artificial = 1;
753
+ artificialCount++;
754
+ }
755
+
756
+ standardForm.constraints.push(standardConstraint);
757
+ }
758
+
759
+ standardForm.slackVariables = slackCount;
760
+ standardForm.artificialVariables = artificialCount;
761
+
762
+ return standardForm;
763
+ }
764
+
765
+ function solveWithSimplexMethod(standardForm) {
766
+ // Simplex method implementation (simplified for this example)
767
+ // In a real application, this would be a full implementation of the simplex algorithm
768
+
769
+ // For this demo, we'll use a pre-calculated solution for the default problem
770
+ const solution = {};
771
+
772
+ // Check if the problem matches our demo case
773
+ const isDemoCase = (
774
+ problemData.direction === 'max' &&
775
+ problemData.varCount === 2 &&
776
+ problemData.constraintCount === 2 &&
777
+ JSON.stringify(problemData.objectiveCoeffs) === '[3,4]' &&
778
+ JSON.stringify(problemData.constraints[0].coeffs) === '[2,1]' &&
779
+ problemData.constraints[0].sign === '≤' &&
780
+ problemData.constraints[0].rhs === 10 &&
781
+ JSON.stringify(problemData.constraints[1].coeffs) === '[1,2]' &&
782
+ problemData.constraints[1].sign === '≤' &&
783
+ problemData.constraints[1].rhs === 12
784
+ );
785
+
786
+ if (isDemoCase) {
787
+ solution.variables = [4, 4];
788
+ solution.slackVariables = [0, 0];
789
+ solution.optimalValue = 28;
790
+ solution.isOptimal = true;
791
+ solution.isUnbounded = false;
792
+ solution.iterations = [
793
+ // Initial table
794
+ {
795
+ basis: ['s₁', 's₂'],
796
+ zRow: [0, -3, -4, 0, 0, 0],
797
+ rows: [
798
+ [2, 1, 1, 0, 10],
799
+ [1, 2, 0, 1, 12]
800
+ ],
801
+ pivot: { row: 1, col: 2 }
802
+ },
803
+ // First iteration
804
+ {
805
+ basis: ['s₁', 'x₂'],
806
+ zRow: [0, -1.5, 0, 0, 2, 24],
807
+ rows: [
808
+ [1.5, 0, 1, -0.5, 4],
809
+ [0.5, 1, 0, 0.5, 6]
810
+ ],
811
+ pivot: { row: 0, col: 1 }
812
+ },
813
+ // Final table
814
+ {
815
+ basis: ['x₁', 'x₂'],
816
+ zRow: [0, 0, 0, 1, 1, 28],
817
+ rows: [
818
+ [1, 0, 0.6667, -0.3333, 2.6667],
819
+ [0, 1, -0.3333, 0.6667, 4.6667]
820
+ ],
821
+ pivot: null
822
+ }
823
+ ];
824
+ } else {
825
+ // For other problems, return a generic solution (in a real app, this would be the actual calculation)
826
+ solution.variables = new Array(problemData.varCount).fill(0);
827
+ solution.slackVariables = new Array(problemData.constraintCount).fill(0);
828
+ solution.optimalValue = 0;
829
+ solution.isOptimal = true;
830
+ solution.isUnbounded = false;
831
+ solution.iterations = [];
832
+ }
833
+
834
+ return solution;
835
+ }
836
+
837
+ function displaySimplexSolution() {
838
+ if (!simplexSolution) return;
839
+
840
+ simplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Simplex Method Steps</h3>';
841
+ simplexResultDiv.classList.remove('hidden');
842
+
843
+ // Display iterations if available
844
+ if (simplexSolution.iterations && simplexSolution.iterations.length > 0) {
845
+ simplexSolution.iterations.forEach((iteration, index) => {
846
+ const tableDiv = document.createElement('div');
847
+ tableDiv.className = 'mb-6';
848
+
849
+ const stepHeader = document.createElement('h4');
850
+ stepHeader.className = 'text-md font-medium mb-2';
851
+ stepHeader.textContent = index === 0 ? 'Initial Table' :
852
+ (index === simplexSolution.iterations.length - 1 ? 'Final Table' : `Iteration ${index}`);
853
+ tableDiv.appendChild(stepHeader);
854
+
855
+ const table = createSimplexTable(iteration, index);
856
+ tableDiv.appendChild(table);
857
+
858
+ simplexStepsDiv.appendChild(tableDiv);
859
+ });
860
+ } else {
861
+ simplexStepsDiv.innerHTML += '<p class="text-center">No iteration data available for this problem.</p>';
862
+ }
863
+
864
+ // Display solution
865
+ let solutionText = '<div class="grid grid-cols-2 gap-4">';
866
+
867
+ solutionText += '<div><h4 class="font-medium mb-2">Decision Variables:</h4><ul class="list-disc pl-5">';
868
+ simplexSolution.variables.forEach((value, index) => {
869
+ solutionText += `<li>x<sub>${index+1}</sub> = ${formatNumber(value)}</li>`;
870
+ });
871
+ solutionText += '</ul></div>';
872
+
873
+ if (simplexSolution.slackVariables && simplexSolution.slackVariables.length > 0) {
874
+ solutionText += '<div><h4 class="font-medium mb-2">Slack Variables:</h4><ul class="list-disc pl-5">';
875
+ simplexSolution.slackVariables.forEach((value, index) => {
876
+ solutionText += `<li>s<sub>${index+1}</sub> = ${formatNumber(value)}</li>`;
877
+ });
878
+ solutionText += '</ul></div>';
879
+ }
880
+
881
+ solutionText += '</div>';
882
+ solutionText += `<p class="mt-4 font-medium">Optimal Value: ${simplexSolution.direction === 'max' ? 'Max' : 'Min'} z = ${formatNumber(simplexSolution.optimalValue)}</p>`;
883
+
884
+ simplexSolutionDiv.innerHTML = solutionText;
885
+
886
+ // Scroll to the simplex tab
887
+ switchTab('simplex');
888
+ }
889
+
890
+ function createSimplexTable(iteration, iterationIndex) {
891
+ const tableDiv = document.createElement('div');
892
+ tableDiv.className = 'overflow-x-auto scroll-container';
893
+
894
+ const table = document.createElement('table');
895
+ table.className = 'simplex-table w-full mb-2';
896
+
897
+ // Table header
898
+ const thead = document.createElement('thead');
899
+ let headerRow = document.createElement('tr');
900
+
901
+ // Basis column
902
+ const basisTh = document.createElement('th');
903
+ basisTh.textContent = 'Basis';
904
+ headerRow.appendChild(basisTh);
905
+
906
+ // Variable columns
907
+ for (let i = 1; i <= problemData.varCount; i++) {
908
+ const th = document.createElement('th');
909
+ th.textContent = `x${i}`;
910
+ headerRow.appendChild(th);
911
+ }
912
+
913
+ // Slack columns
914
+ for (let i = 1; i <= problemData.constraintCount; i++) {
915
+ const th = document.createElement('th');
916
+ th.textContent = `s${i}`;
917
+ headerRow.appendChild(th);
918
+ }
919
+
920
+ // RHS column
921
+ const rhsTh = document.createElement('th');
922
+ rhsTh.textContent = 'RHS';
923
+ headerRow.appendChild(rhsTh);
924
+
925
+ thead.appendChild(headerRow);
926
+ table.appendChild(thead);
927
+
928
+ // Table body
929
+ const tbody = document.createElement('tbody');
930
+
931
+ // Constraint rows
932
+ iteration.rows.forEach((row, rowIndex) => {
933
+ const tr = document.createElement('tr');
934
+
935
+ // Basis variable
936
+ const basisTd = document.createElement('td');
937
+ basisTd.textContent = iteration.basis[rowIndex];
938
+ tr.appendChild(basisTd);
939
+
940
+ // Variable coefficients
941
+ for (let i = 0; i < problemData.varCount; i++) {
942
+ const td = document.createElement('td');
943
+ td.textContent = formatNumber(row[i]);
944
+
945
+ if (iteration.pivot && iteration.pivot.row === rowIndex && iteration.pivot.col === (i + 1)) {
946
+ td.classList.add('pivot-cell');
947
+ }
948
+
949
+ tr.appendChild(td);
950
+ }
951
+
952
+ // Slack coefficients
953
+ const slackStart = problemData.varCount;
954
+ const slackEnd = slackStart + problemData.constraintCount;
955
+ for (let i = slackStart; i < slackEnd; i++) {
956
+ const td = document.createElement('td');
957
+ td.textContent = formatNumber(row[i]);
958
+
959
+ if (iteration.pivot && iteration.pivot.row === rowIndex && iteration.pivot.col === (i + 1)) {
960
+ td.classList.add('pivot-cell');
961
+ }
962
+
963
+ tr.appendChild(td);
964
+ }
965
+
966
+ // RHS
967
+ const rhsTd = document.createElement('td');
968
+ rhsTd.textContent = formatNumber(row[row.length - 1]);
969
+ tr.appendChild(rhsTd);
970
+
971
+ tbody.appendChild(tr);
972
+ });
973
+
974
+ // z-row
975
+ const zRow = document.createElement('tr');
976
+ zRow.className = 'font-semibold';
977
+
978
+ const zLabel = document.createElement('td');
979
+ zLabel.textContent = 'z';
980
+ zRow.appendChild(zLabel);
981
+
982
+ for (let i = 1; i < iteration.zRow.length - 1; i++) {
983
+ const td = document.createElement('td');
984
+ td.textContent = formatNumber(iteration.zRow[i]);
985
+
986
+ if (iteration.pivot && iteration.pivot.col === i) {
987
+ td.classList.add('pivot-cell');
988
+ }
989
+
990
+ zRow.appendChild(td);
991
+ }
992
+
993
+ // z-RHS
994
+ const zRhs = document.createElement('td');
995
+ zRhs.textContent = formatNumber(iteration.zRow[iteration.zRow.length - 1]);
996
+ zRow.appendChild(zRhs);
997
+
998
+ tbody.appendChild(zRow);
999
+ table.appendChild(tbody);
1000
+
1001
+ tableDiv.appendChild(table);
1002
+
1003
+ // Add pivot information
1004
+ if (iteration.pivot && iterationIndex < simplexSolution.iterations.length - 1) {
1005
+ const pivotInfo = document.createElement('p');
1006
+ pivotInfo.className = 'text-sm italic text-cyan-200 mt-1';
1007
+ pivotInfo.textContent = `Pivot: Row ${iteration.pivot.row + 1}, Column ${iteration.pivot.col}`;
1008
+ tableDiv.appendChild(pivotInfo);
1009
+ }
1010
+
1011
+ return tableDiv;
1012
+ }
1013
+
1014
+ function generateDualProblem() {
1015
+ if (!simplexSolution) return;
1016
+
1017
+ dualProblem = {
1018
+ direction: problemData.direction === 'max' ? 'min' : 'max',
1019
+ variables: problemData.constraintCount,
1020
+ constraints: [],
1021
+ objectiveCoeffs: []
1022
+ };
1023
+
1024
+ // Objective coefficients are the RHS of primal constraints
1025
+ dualProblem.objectiveCoeffs = problemData.constraints.map(c => c.rhs);
1026
+
1027
+ // Constraints come from primal variables
1028
+ for (let j = 0; j < problemData.varCount; j++) {
1029
+ const constraint = {
1030
+ coeffs: [],
1031
+ sign: problemData.direction === 'max' ? '≥' : '≤',
1032
+ rhs: problemData.objectiveCoeffs[j]
1033
+ };
1034
+
1035
+ // Coefficients come from primal constraint coefficients for this variable
1036
+ for (let i = 0; i < problemData.constraintCount; i++) {
1037
+ constraint.coeffs.push(problemData.constraints[i].coeffs[j]);
1038
+ }
1039
+
1040
+ dualProblem.constraints.push(constraint);
1041
+ }
1042
+
1043
+ // Display dual problem
1044
+ displayDualProblem();
1045
+ }
1046
+
1047
+ function displayDualProblem() {
1048
+ if (!dualProblem) return;
1049
+
1050
+ let dualText = dualProblem.direction === 'max' ? 'Maximize' : 'Minimize';
1051
+ dualText += ': $\\displaystyle ';
1052
+
1053
+ // Objective function
1054
+ for (let i = 0; i < dualProblem.variables; i++) {
1055
+ if (i > 0 && dualProblem.objectiveCoeffs[i] >= 0) {
1056
+ dualText += ' + ';
1057
+ } else if (dualProblem.objectiveCoeffs[i] < 0) {
1058
+ dualText += ' - ';
1059
+ }
1060
+
1061
+ const absCoeff = Math.abs(dualProblem.objectiveCoeffs[i]);
1062
+ if (absCoeff !== 1) {
1063
+ dualText += absCoeff;
1064
+ }
1065
+
1066
+ dualText += `y_{${i+1}}`;
1067
+ }
1068
+
1069
+ dualText += '$<br><br>Subject to:<br>';
1070
+
1071
+ // Constraints
1072
+ for (let i = 0; i < dualProblem.constraints.length; i++) {
1073
+ const constraint = dualProblem.constraints[i];
1074
+ dualText += '$\\displaystyle ';
1075
+
1076
+ for (let j = 0; j < constraint.coeffs.length; j++) {
1077
+ if (j > 0 && constraint.coeffs[j] >= 0) {
1078
+ dualText += ' + ';
1079
+ } else if (constraint.coeffs[j] < 0) {
1080
+ dualText += ' - ';
1081
+ }
1082
+
1083
+ const absCoeff = Math.abs(constraint.coeffs[j]);
1084
+ if (absCoeff !== 1) {
1085
+ dualText += absCoeff;
1086
+ }
1087
+
1088
+ dualText += `y_{${j+1}}`;
1089
+ }
1090
+
1091
+ dualText += ` ${constraint.sign} ${constraint.rhs}$<br>`;
1092
+ }
1093
+
1094
+ // Non-negativity constraints
1095
+ dualText += '<br>With: $\\displaystyle ';
1096
+ for (let i = 0; i < dualProblem.variables; i++) {
1097
+ dualText += `y_{${i+1}} \\geq 0`;
1098
+ if (i < dualProblem.variables - 1) {
1099
+ dualText += ', ';
1100
+ }
1101
+ }
1102
+ dualText += '$';
1103
+
1104
+ dualProblemDisplay.innerHTML = dualText;
1105
+
1106
+ // Update MathJax rendering
1107
+ if (typeof MathJax !== 'undefined') {
1108
+ MathJax.typeset();
1109
+ }
1110
+
1111
+ // Solve dual problem with dual simplex method (for demo purposes)
1112
+ solveDualWithSimplexMethod();
1113
+ }
1114
+
1115
+ function solveDualWithSimplexMethod() {
1116
+ if (!dualProblem) return;
1117
+
1118
+ // For demo purposes, we'll just simulate a solution
1119
+ dualSimplexSolution = {
1120
+ variables: [2/3, 5/3],
1121
+ optimalValue: 28,
1122
+ isOptimal: true,
1123
+ isUnbounded: false
1124
+ };
1125
+
1126
+ displayDualSimplexSolution();
1127
+ }
1128
+
1129
+ function displayDualSimplexSolution() {
1130
+ if (!dualSimplexSolution) return;
1131
+
1132
+ dualSimplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Dual Simplex Method Steps</h3>';
1133
+ dualSimplexStepsDiv.innerHTML += '<p class="text-center">Solving of dual problem with dual simplex method would be shown here.</p>';
1134
+
1135
+ dualSimplexResultDiv.classList.remove('hidden');
1136
+
1137
+ // Display solution
1138
+ let solutionText = '<div class="grid grid-cols-2 gap-4">';
1139
+
1140
+ solutionText += '<div><h4 class="font-medium mb-2">Dual Variables:</h4><ul class="list-disc pl-5">';
1141
+ dualSimplexSolution.variables.forEach((value, index) => {
1142
+ solutionText += `<li>y<sub>${index+1}</sub> = ${formatNumber(value)}</li>`;
1143
+ });
1144
+ solutionText += '</ul></div>';
1145
+
1146
+ solutionText += '</div>';
1147
+ solutionText += `<p class="mt-4 font-medium">Optimal Value: ${dualProblem.direction === 'max' ? 'Max' : 'Min'} W = ${formatNumber(dualSimplexSolution.optimalValue)}</p>`;
1148
+
1149
+ // Note about duality
1150
+ if (simplexSolution && Math.abs(simplexSolution.optimalValue - dualSimplexSolution.optimalValue) < 0.001) {
1151
+ solutionText += '<p class="mt-2 text-sm text-cyan-200">Note: The optimal values of the primal and dual problems are equal, as expected from duality theory.</p>';
1152
+ }
1153
+
1154
+ dualSimplexSolutionDiv.innerHTML = solutionText;
1155
+ }
1156
+
1157
+ function switchTab(tabId) {
1158
+ // Update active tab button
1159
+ tabButtons.forEach(button => {
1160
+ if (button.getAttribute('data-tab') === tabId) {
1161
+ button.classList.add('active');
1162
+ } else {
1163
+ button.classList.remove('active');
1164
+ }
1165
+ });
1166
+
1167
+ // Show corresponding tab content
1168
+ tabContents.forEach(content => {
1169
+ if (content.id === `${tabId}-tab`) {
1170
+ content.classList.remove('hidden');
1171
+ } else {
1172
+ content.classList.add('hidden');
1173
+ }
1174
+ });
1175
+ }
1176
+
1177
+ function clearResults() {
1178
+ simplexStepsDiv.innerHTML = '<p class="text-center">Solve the problem to see simplex method steps</p>';
1179
+ simplexResultDiv.classList.add('hidden');
1180
+
1181
+ dualProblemDisplay.innerHTML = '<p class="text-center">Solve the primal problem to see the dual formulation</p>';
1182
+
1183
+ dualSimplexStepsDiv.innerHTML = '<p class="text-center">Generate the dual problem to see solution steps</p>';
1184
+ dualSimplexResultDiv.classList.add('hidden');
1185
+ }
1186
+
1187
+ function formatNumber(num) {
1188
+ if (num % 1 === 0) {
1189
+ return num.toString();
1190
+ }
1191
+
1192
+ // Check for common fractions
1193
+ const tolerance = 1.0E-6;
1194
+ const fractions = [
1195
+ { numerator: 1, denominator: 2, value: 0.5 },
1196
+ { numerator: 1, denominator: 3, value: 1/3 },
1197
+ { numerator: 2, denominator: 3, value: 2/3 },
1198
+ { numerator: 1, denominator: 4, value: 0.25 },
1199
+ { numerator: 3, denominator: 4, value: 0.75 },
1200
+ { numerator: 1, denominator: 5, value: 0.2 },
1201
+ { numerator: 2, denominator: 5, value: 0.4 },
1202
+ { numerator: 3, denominator: 5, value: 0.6 },
1203
+ { numerator: 4, denominator: 5, value: 0.8 }
1204
+ ];
1205
+
1206
+ for (const frac of fractions) {
1207
+ if (Math.abs(num - frac.value) < tolerance) {
1208
+ return `<span class="fraction"><span class="numerator">${frac.numerator}</span><span class="slash">/</span><span class="denominator">${frac.denominator}</span></span>`;
1209
+ }
1210
+ if (Math.abs(num + frac.value) < tolerance) {
1211
+ return `<span>-</span><span class="fraction"><span class="numerator">${frac.numerator}</span><span class="slash">/</span><span class="denominator">${frac.denominator}</span></span>`;
1212
+ }
1213
+ }
1214
+
1215
+ // Round to 4 decimal places if not a simple fraction
1216
+ return Math.round(num * 10000) / 10000;
1217
+ }
1218
+ </script>
1219
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Czarevich/simplex" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1220
+ </html>
style.css ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ padding: 2rem;
3
+ font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
+ }
5
+
6
+ h1 {
7
+ font-size: 16px;
8
+ margin-top: 0;
9
+ }
10
+
11
+ p {
12
+ color: rgb(107, 114, 128);
13
+ font-size: 15px;
14
+ margin-bottom: 10px;
15
+ margin-top: 5px;
16
+ }
17
+
18
+ .card {
19
+ max-width: 620px;
20
+ margin: 0 auto;
21
+ padding: 16px;
22
+ border: 1px solid lightgray;
23
+ border-radius: 16px;
24
+ }
25
+
26
+ .card p:last-child {
27
+ margin-bottom: 0;
28
+ }