commit be437c2858cf03b4c00aea8265b183412292d52d Author: Antonio De Lucreziis Date: Fri Aug 16 22:09:47 2024 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81ca160 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Local files +.env +*.local* + +# NodeJS +node_modules/ + +# Binaries +bin/ +.out/ +out/ +dist/ + +# Editors +.vscode/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..d2403f1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +# gem "rails" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..1491436 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,12 @@ +GEM + remote: https://rubygems.org/ + specs: + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + +BUNDLED WITH + 2.5.17 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a986257 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Ricerca Operativa + +Un piccolo sito con alcuni degli algoritmi presentati nel corso di Ricerca Operativa. Gli algoritmi sono scritti in Ruby e +eseguiti nel browser con WASM (uso il Ruby perché è l'unico linguaggio con i numeri razionali e buon supporto per WASM). + +## Algoritmi + +### Programmazione Lineare + +- [ ] Metodo del simplesso + + - [ ] Metodo del simplesso (con i conti) + + - [ ] Metodo del simplesso (con disegnini) + +- [ ] Metodo del simplesso duale + + - [ ] Metodo del simplesso duale (con i conti) + + - [ ] Metodo del simplesso duale (con disegnini) + +### Programmazione Lineare Intera + +- [ ] Branch and Bound + +- [ ] Branch and Cut + +- [ ] Branch and Price + +### Programmazione Dinamica + +- [ ] Algoritmo di Bellman + +- [ ] Algoritmo di Dijkstra + +- [ ] Algoritmo di Floyd + +### Altri + +- [ ] Algoritmo di Kruskal + +## Usage + +```bash +bun install +``` + +```bash +bun run index.ts +``` diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..cc27caf Binary files /dev/null and b/bun.lockb differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..3d5d1f4 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + + Visualizzazioni di Ricerca Operativa + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..8936432 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "ricerca-operativa", + "module": "index.ts", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "@preact/preset-vite": "^2.9.0", + "@types/bun": "latest", + "@types/katex": "^0.16.7", + "@types/lodash": "^4.17.7", + "vite": "^5.4.1" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@fontsource/inter": "^5.0.20", + "@ruby/3.3-wasm-wasi": "^2.6.2", + "@ruby/wasm-wasi": "^2.6.2", + "katex": "^0.16.11", + "lodash": "^4.17.21", + "vite-plugin-wasm": "^3.3.0" + } +} diff --git a/src/algorithms/simplesso-primale-algebrico/View.jsx b/src/algorithms/simplesso-primale-algebrico/View.jsx new file mode 100644 index 0000000..0c5ee2c --- /dev/null +++ b/src/algorithms/simplesso-primale-algebrico/View.jsx @@ -0,0 +1,29 @@ +import { Steps } from '../../components/Steps.jsx' +import { evalRuby } from '../../ruby.js' +import algorithmCode from './algorithm.rb?raw' + +import { useEffect, useState } from 'preact/hooks' + +export const metadata = { + group: 'Programmazione Lineare', + title: 'Simplesso Primale Algebrico', + description: 'Algoritmo principale della programmazione lineare', +} + +export const View = ({}) => { + const [steps, setSteps] = useState([]) + + useEffect(async () => { + const { outputs } = await evalRuby(algorithmCode) + setSteps(outputs) + }, []) + + return ( + <> +

Simplesso Primale Algebrico

+

TODO

+ + + + ) +} diff --git a/src/algorithms/simplesso-primale-algebrico/algorithm.rb b/src/algorithms/simplesso-primale-algebrico/algorithm.rb new file mode 100644 index 0000000..76483cd --- /dev/null +++ b/src/algorithms/simplesso-primale-algebrico/algorithm.rb @@ -0,0 +1,30 @@ +require "json" +require "js" + +def print_step(label, state) + puts "Ruby: #{label} #{state}" + JS.global[:rubyOutputs].push({ + label: label, + state: state + }) +end + +def run(config) + i = config["i"] || 1 + j = config["j"] || 1 + n = config["n"] || 10 + + print_step "Initial", { i: i, j: j, n: n } + + n.times do + m = i + j + i = j + j = m + + print_step "Step", { i: i, j: j } + end + + return i +end + +run({ "n" => 10 }) \ No newline at end of file diff --git a/src/components/KaTeX.jsx b/src/components/KaTeX.jsx new file mode 100644 index 0000000..e011167 --- /dev/null +++ b/src/components/KaTeX.jsx @@ -0,0 +1,16 @@ +import 'katex/dist/katex.min.css' +import katex from 'katex' + +export const KaTeX = ({ source }) => { + console.log(source) + + return ( +
{ + if (!el) return + katex.render(source.toString(), el, { throwOnError: false }) + }} + >
+ ) +} diff --git a/src/components/Steps.jsx b/src/components/Steps.jsx new file mode 100644 index 0000000..78f02e5 --- /dev/null +++ b/src/components/Steps.jsx @@ -0,0 +1,18 @@ +import { KaTeX } from './KaTeX.jsx' + +export const Steps = ({ steps }) => { + return ( +
+ {steps.map((step, i) => ( +
+
{step.label}
+
+ {Object.entries(step.state).map(([key, value]) => ( + + ))} +
+
+ ))} +
+ ) +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..c330965 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,105 @@ +import './style.css' +import '@fontsource/inter/latin.css' + +import _ from 'lodash' + +import { render } from 'preact' +import { useState } from 'preact/hooks' + +const ViewRegistry = Object.fromEntries( + Object.entries( + import.meta.glob('./algorithms/*/View.jsx', { + eager: true, + }) + /* fix for broken syntax highlighting */ + ).map(([path, module]) => { + const [id] = path.match(/(?<=\/)[^/]*(?=\/View\.jsx$)/) + return [id, { id, ...module }] + }) +) + +const NewAlgorithmBox = ({ title, description, onClick }) => ( +
+

{title}

+

{description}

+
+) + +const AlgorithmChooserView = ({ setCurrentView }) => { + const sections = _.groupBy(ViewRegistry, 'metadata.group') + + return ( + <> +

Algoritmi

+ {Object.entries(sections).map(([group, algorithms]) => ( +
+

{group}

+
+ {algorithms.map(({ id, metadata }) => ( + setCurrentView(id)} + /> + ))} +
+
+ ))} +
+

Flussi su Grafi

+
+ + + + +
+
+
+

Flussi su Grafi

+
+ + + + +
+
+
+

Flussi su Grafi

+
+ + + + +
+
+ + ) +} + +const Main = ({}) => { + const [currentView, setCurrentView] = useState(null) + + if (!currentView) { + return + } + + const View = ViewRegistry[currentView].View + + return +} + +const App = ({}) => { + return ( + <> +
Visualizzazioni di Ricerca Operativa
+ +
+
+
+ + ) +} + +render(, document.body) diff --git a/src/ruby.js b/src/ruby.js new file mode 100644 index 0000000..5924442 --- /dev/null +++ b/src/ruby.js @@ -0,0 +1,26 @@ +import rubyWasmModuleUrl from '@ruby/3.3-wasm-wasi/dist/ruby+stdlib.wasm?url' +import { DefaultRubyVM } from '@ruby/wasm-wasi/dist/browser' + +window.rubyOutputs = [] +let vmInstance = null + +async function preloadRuby() { + if (vmInstance) return + + const response = await fetch(rubyWasmModuleUrl) + const module = await WebAssembly.compileStreaming(response) + const { vm } = await DefaultRubyVM(module) + vmInstance = vm +} + +export async function evalRuby(code) { + await preloadRuby() + + window.rubyOutputs = [] + const result = vmInstance.eval(code) + + return { + result, + outputs: window.rubyOutputs, + } +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..6839564 --- /dev/null +++ b/src/style.css @@ -0,0 +1,176 @@ +*, +*::before, +*::after { + font-family: inherit; + padding: 0; + margin: 0; + box-sizing: border-box; +} + +html, +body { + min-height: 100%; + height: 100%; +} + +body { + width: 100vw; + height: 100vh; + + position: fixed; + overflow: hidden; + + user-select: none; +} + +img { + display: block; +} + +/* Typography */ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: 'Inter', sans-serif; + font-weight: 500; +} + +/* Components */ + +.boxes { + display: flex; + flex-direction: row; + gap: 1rem; + + align-items: start; + + .algorithm-box { + display: grid; + grid-template-rows: auto 1fr; + + gap: 0.25rem; + padding: 1rem; + border-radius: 0.75rem; + + background: #fff; + border: 2px solid #333; + + width: 10rem; + min-height: calc(10rem * 3 / 4); + + p { + color: #444; + } + + cursor: pointer; + + &:hover { + background: #f4f4f4; + } + } +} + +.steps { + display: grid; + grid-template-columns: 1fr; + + place-self: center; + + gap: 1rem; + + .step { + display: grid; + grid-template-rows: auto 1fr; + + gap: 0.5rem; + padding: 1rem; + border-radius: 0.75rem; + + background: #fff; + border: 2px solid #333; + + min-width: 20rem; + + & > .label { + font-weight: 500; + font-size: 15px; + } + + & > .state { + display: grid; + grid-template-columns: 1fr; + gap: 0.25rem; + } + } +} + +/* Structure */ + +body { + display: grid; + grid-template-columns: 20% 1fr; + grid-template-rows: auto 1fr; + + font-family: 'Inter', sans-serif; + font-weight: 400; + font-size: 16px; + + background: #f0f0f0; + + & > header, + aside, + main, + footer { + display: grid; + place-content: center; + + padding: 0 1rem; + } + + & > header { + height: 3rem; + + grid-column: span 2; + + border-bottom: 2px solid #333; + background: #fff; + + font-weight: 300; + font-size: 1.25rem; + } + + & > aside { + border-right: 2px solid #333; + background: #e0e0e0; + + position: sticky; + height: 100vh; + + padding: 1rem; + + align-content: start; + } + + & > main { + display: grid; + grid-template-columns: auto; + + padding: 6rem 1rem; + gap: 1.5rem; + + place-content: center; + align-content: start; + + overflow: auto; + + & > section { + display: grid; + grid-template-columns: auto; + gap: 0.5rem; + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..dfef62d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..1b0bb46 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' + +import wasm from 'vite-plugin-wasm' +import preactPlugin from '@preact/preset-vite' + +export default defineConfig({ + build: { + outDir: 'out', + emptyOutDir: true, + }, + server: { + port: 3000, + }, + plugins: [preactPlugin(), wasm()], +})