changing frameworks

This commit is contained in:
2025-11-25 16:37:04 +00:00
parent ada53b87e2
commit 34bd66dd0d
67 changed files with 3125 additions and 491 deletions

26
nginx/vue/src/App.vue Normal file
View File

@@ -0,0 +1,26 @@
<script setup>
import { RouterLink, RouterView } from "vue-router";
</script>
<template>
<header>
<img
alt="Vue logo"
class="logo"
src="@/assets/logo.svg"
width="125"
height="125"
/>
<div class="wrapper">
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template>
<style scoped></style>

View File

@@ -0,0 +1,347 @@
function integerDigits(n, b = 10, length = null) {
// Get the list of digits in base b
const digits = [];
while (n > 0) {
digits.push(n % b);
n = Math.floor(n / b);
}
digits.reverse(); // Reverse the list to get digits in big endian order
// Pad with zeros if length is specified
if (length !== null) {
const padding = Array(Math.max(0, length - digits.length)).fill(0);
return padding.concat(digits);
}
return digits;
}
function* cartesianProduct(...arrays) {
// Generator for cartesian product
if (arrays.length === 0) {
yield [];
return;
}
const [first, ...rest] = arrays;
if (rest.length === 0) {
for (const item of first) {
yield [item];
}
} else {
for (const item of first) {
for (const combo of cartesianProduct(...rest)) {
yield [item, ...combo];
}
}
}
}
function tuplesFromList(lst, n) {
const arrays = Array(n).fill(lst);
return Array.from(cartesianProduct(...arrays));
}
function tuplesFromMultipleLists(...lists) {
return Array.from(cartesianProduct(...lists));
}
function flattenTuples(tuples) {
return tuples.flat();
}
function partition(lst, n) {
const result = [];
for (let i = 0; i < lst.length; i += n) {
result.push(lst.slice(i, i + n));
}
return result;
}
function pick(pickList, lst) {
const trues = [];
const falses = [];
for (let i = 0; i < pickList.length; i++) {
if (pickList[i]) {
trues.push(lst[i]);
} else {
falses.push(lst[i]);
}
}
return [trues, falses];
}
function factorial(n) {
if (n < 0) return NaN;
if (n === 0 || n === 1) return 1;
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}
function unrankPermutation(r, lst) {
const n = lst.length;
r -= 1; // Convert r to 0-indexed
const permutation = [];
const availableElements = [...lst];
for (let i = n; i > 0; i--) {
const fact = factorial(i - 1); // (n-1)!
const index = Math.floor(r / fact); // Find the index of the current element
permutation.push(availableElements.splice(index, 1)[0]); // Add the element and remove it from available
r %= fact; // Update r to find the next element
}
return permutation;
}
export function toMaRule(sn, dn, n, k) {
if (n < 1 || n % 2 === 0) {
throw new Error("n must be >= 1 and odd");
}
const inputs = tuplesFromList([...Array(k).keys()], n);
const directions = integerDigits(dn, 2, Math.pow(k, n)).map((x) =>
Math.pow(-1, x),
);
const snDigits = integerDigits(sn, k, n * Math.pow(k, n));
const outputs = partition(snDigits, n);
const rules = {};
for (let i = 0; i < inputs.length; i++) {
rules[JSON.stringify(inputs[i])] = [outputs[i], directions[i]];
}
return rules;
}
export function toReversibleMaRule(bn, pn, n, k) {
if (n < 1 || n % 2 === 0) {
throw new Error("n must be >= 1 and odd");
}
const inputs = tuplesFromList([...Array(k).keys()], n);
const blockers = tuplesFromList([...Array(k).keys()], n - 2);
const blockSelect = pick(integerDigits(bn, 2, Math.pow(k, n - 2)), blockers);
const rightBlockers = blockSelect[0];
const leftBlockers = blockSelect[1];
const twoFair = tuplesFromList([...Array(k).keys()], 2);
const leftOutputs = tuplesFromMultipleLists(leftBlockers, twoFair).map(
(x) => [flattenTuples(x), -1],
);
const rightOutputs = tuplesFromMultipleLists(twoFair, rightBlockers).map(
(x) => [flattenTuples(x), 1],
);
const outputs = [...leftOutputs, ...rightOutputs];
const rankedOutputs = unrankPermutation(pn, outputs);
const rules = {};
for (let i = 0; i < inputs.length; i++) {
rules[JSON.stringify(inputs[i])] = rankedOutputs[i];
}
return rules;
}
export function maStep(rules, state, r) {
/**
* Apply one step of the mobile automaton rules
*
* Args:
* rules (object): Dictionary of rules where key is input tuple and value is [output_tuple, direction]
* state (array): [list, head] where list is current state and head is current position
* r (number): Radius of the neighborhood (window size = 2r + 1)
*
* Returns:
* array: [new_list, new_head] or [[], -1] if out of bounds
*/
const [currentList, head] = state;
// Check bounds
if (head - r <= 0 || head + r >= currentList.length) {
return [[], -1];
}
// Get the window of elements centered at head
const window = currentList.slice(head - r, head + r + 1);
// Apply rule
const ruleKey = JSON.stringify(window);
const [newWindow, direction] = rules[ruleKey];
// Create new list with replaced elements
const newList = [...currentList];
for (let i = 0; i < newWindow.length; i++) {
newList[head - r + i] = newWindow[i];
}
return [newList, head + direction];
}
export function ma(rules, initialState, t) {
/**
* Perform t steps of the mobile automaton
*
* Args:
* rules (object): Dictionary of rules
* initialState (array): Initial [list, head] state
* t (number): Number of steps to perform
*
* Returns:
* array: List of states at each time step
*/
// Calculate radius from first rule key length
const firstKey = Object.keys(rules)[0];
const r = JSON.parse(firstKey).length / 2;
const states = [initialState];
let currentState = initialState;
for (let i = 0; i < t; i++) {
currentState = maStep(rules, currentState, r);
states.push(currentState);
// Stop if we hit an invalid state
if (currentState[0].length === 0) {
break;
}
}
return states;
}
export function cyclicMaStep(rules, state, r) {
/**
* Cyclic version: indexing wraps around the array.
*/
const [currentList, head] = state;
const n = currentList.length;
// --- Cyclic window extraction ---
const window = [];
for (let i = -r; i <= r; i++) {
window.push(currentList[(head + i + n) % n]);
}
// Apply rule
const ruleKey = JSON.stringify(window);
const [newWindow, direction] = rules[ruleKey];
// --- Cyclic writeback ---
const newList = [...currentList];
for (let offset = 0; offset < newWindow.length; offset++) {
newList[(head - r + offset + n) % n] = newWindow[offset];
}
// Move head cyclically
const newHead = (head + direction + n) % n;
return [newList, newHead];
}
export function cyclicMa(rules, initialState, t) {
/**
* Perform t steps of the mobile automaton
*
* Args:
* rules (object): Dictionary of rules
* initialState (array): Initial [list, head] state
* t (number): Number of steps to perform
*
* Returns:
* array: List of states at each time step
*/
// Calculate radius from first rule key length
const firstKey = Object.keys(rules)[0];
const r = JSON.parse(firstKey).length / 2;
const states = [initialState];
let currentState = initialState;
for (let i = 0; i < t; i++) {
currentState = cyclicMaStep(rules, currentState, r);
states.push(currentState);
// Stop if we hit an invalid state
if (currentState[0].length === 0) {
break;
}
}
return states;
}
// export function renderToCanvas(canvas, width, height) {
// let states = Array.from({ length: height }, () =>
// Array.from({ length: width }, () => Math.round(Math.random() + 0.4)),
// );
// }
export function renderMaToCanvas(canvas, width, height, sn = 0, dn = 0) {
if (sn == 0) {
const min = 1500000;
const max = 2000000;
sn = Math.floor(Math.random() * (max - min + 1)) + min;
}
if (dn == 0) {
const min = 100;
const max = 200;
dn = Math.floor(Math.random() * (max - min + 1)) + min;
}
const r = 1;
const n = 2 * r + 1;
const rules = toMaRule(sn, dn, n, 2);
let states = Array.from({ length: height }, () =>
Array.from({ length: width }, () => Math.round(Math.random() + 0.4)),
);
let head = Math.floor(width / 2) % width;
let row_num = 0;
const ctx = canvas.getContext("2d");
const img = ctx.createImageData(width, height);
const data = img.data;
const colorOn = [10, 60, 130]; // dark blue (active cell)
const colorOff = [10, 70, 110]; // darker blue (inactive cell)
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = (y * width + x) * 4;
const color = states[y][x] ? colorOn : colorOff;
data[idx] = color[0]; // R
data[idx + 1] = color[1]; // G
data[idx + 2] = color[2]; // B
data[idx + 3] = 255; // A
}
}
ctx.putImageData(img, 0, 0);
function step() {
// calculate new state
let [newState, newHead] = cyclicMaStep(rules, [states[row_num], head], r);
states[row_num] = newState;
// write changed cells to ImageData
for (let x = head - r; x <= head + r; x++) {
const idx = (row_num * width + x) * 4;
const val = newState[x] ? colorOn : colorOff;
data[idx] = val[0]; // R
data[idx + 1] = val[1]; // G
data[idx + 2] = val[2]; // B
data[idx + 3] = 255; // A
}
// update canvas (only this row)
ctx.putImageData(img, head - r, row_num, 0, 0, n, 1);
// advance row and head
row_num = (row_num + 1) % height;
head = newHead;
requestAnimationFrame(step);
}
requestAnimationFrame(step);
}

11
nginx/vue/src/main.js Normal file
View File

@@ -0,0 +1,11 @@
import "./assets/main.css";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
const app = createApp(App);
app.use(router);
app.mount("#app");

View File

@@ -0,0 +1,23 @@
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/cv",
name: "cv",
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import("../views/CV.vue"),
},
],
});
export default router;

359
nginx/vue/src/views/CV.vue Normal file
View File

@@ -0,0 +1,359 @@
<template>
<div>
<RouterLink class="no-print" to="/">Home</RouterLink>
<div
class="no-print"
style="width: 100%; text-align: center; margin: 20px 0"
>
<h1>Page 1</h1>
</div>
<div class="a4page">
<div class="contact">
<h1>Adam French</h1>
<!-- <a href="covers.html"><img width=25 height=50 src="img/rune.png"></a> -->
<div class="contact-details">
<p>+447563266931</p>
<p>adam.a.french@outlook.com</p>
</div>
</div>
<h2>Profile</h2>
<p>
Passionate about developing robust, efficient software with a
strong focus on maintainability, scalability, and long-term
performance. I take pride in my ability to perform under
pressure, adapt quickly, and contribute effectively in
collaborative, fast-paced environments.
</p>
<p>
My ideal role involves designing and building scalable systems
that balance creativity with problem-solving. I aim to cultivate
meaningful professional connections and contribute to projects
that deliver a clear altruistic impact.
</p>
<h2>Education</h2>
<table>
<thead>
<tr>
<th>Location</th>
<th>Date</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>The University of Leeds</td>
<td>
<!-- <div style="display: flex; flex-direction: column; align-items: center;"> -->
<!-- <span>2021</span> -->
<!-- <span>to</span> -->
<!-- <span>2025</span> -->
<!-- </div> -->
2021-2025
</td>
<td class="row-leftalign">
<strong
>BSc Computer Science with Mathematics
(International)</strong
><br />
<strong
>Average:
81.1%&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;(First
Class Honours) </strong
><br />
<strong>Relevant Courses: </strong>
Procedural Programming, Object Oriented Programming,
Algorithms and Data Structures I & II, Databases,
Computer Processors, Compiler Design and
Construction, Formal Languages and Finite Automata,
Probability and Statistics I, Machine Learning,
Graph Algorithms & Complexity Theory
</td>
</tr>
<tr>
<td>The University of Waterloo</td>
<td>
<!-- <div style="display: flex; flex-direction: column; align-items: center;"> -->
<!-- <span>2023</span> -->
<!-- <span>to</span> -->
<!-- <span>2024</span> -->
<!-- </div> -->
2023-2024
</td>
<td class="row-leftalign">
<strong>Average: 74.5%</strong>
<br />
<strong>Relevant Courses:</strong>
Applied Cryptography, Introduction to Computer
Graphics, Introduction to Rings and Fields with
Applications<br /><br />
</td>
</tr>
</tbody>
</table>
<h2>Experience</h2>
<table>
<thead>
<tr>
<th>Role</th>
<th>Location</th>
<th>Date</th>
<th>Duties</th>
</tr>
</thead>
<tbody>
<tr>
<td>Student</td>
<td>Wolfram Summer School</td>
<td>2024</td>
<td class="row-leftalign">
Designed and completed a time-constrained research
project exploring Mobile Automata and conditions for
computational reversibility. Communicated findings
through visualizations and presentations.
</td>
</tr>
<tr>
<td>Bartender</td>
<td>Belgrave Music Hall</td>
<td>2022-2025</td>
<td class="row-leftalign">
Delivered heartfelt customer service in various
fast-paced, high-pressure hospitality environments.
</td>
</tr>
<tr>
<td>Cashier Assistant</td>
<td>To The Rise Bakery</td>
<td>Summer 2022</td>
<td class="row-leftalign">
Prepared coffee, served customers, presented goods,
cleaned bakery equipment, and made toasties.
</td>
</tr>
<tr>
<td>Waiter</td>
<td>BFI Bar and Kitchen</td>
<td>Summer 2020</td>
<td class="row-leftalign">
Managed bookings, allocated tables, handled
complaints, ran food and drinks, and maintained BOH
cleanliness.
</td>
</tr>
</tbody>
</table>
</div>
<div
class="no-print"
style="width: 100%; text-align: center; margin: 20px 0"
>
<h1>Page 2</h1>
</div>
<div class="a4page">
<h2>Personal Projects</h2>
<table>
<thead>
<tr>
<th>Project</th>
<th>Skills</th>
<th>Date</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mobile Automata</td>
<td>Mathematica, JS, Logic, Analysis</td>
<td>2024</td>
<td class="row-leftalign">
Designed experiments and analysis tools to identify
pattern similarities among automata. Investigated
computational properties by defining specific
phenomena and observing emergent behaviors through
custom simulations.
</td>
</tr>
<tr>
<td>Computer Graphics</td>
<td>Rust, Linear Algebra, Multi-threading</td>
<td>2023</td>
<td class="row-leftalign">
Developed a multi-threaded, recursive ray tracer as
part of a University of Waterloo project. Explored
advanced ray-surface intersection techniques,
including experimental rendering of
higher-dimensional geometries.
</td>
</tr>
<tr>
<td>Arduino Programming & Circuits</td>
<td>C++, Soldering, Embedded Systems</td>
<td>2022 - 2025</td>
<td class="row-leftalign">
Created room decorations using salvaged components
from discarded electronics.
</td>
</tr>
<tr>
<td>Memory Palace Website</td>
<td>TS, Rust, React, Redux, SQLite</td>
<td>2025</td>
<td class="row-leftalign">
Full-stack web application implementing the memory
palace memorization technique. Built with a
React/Redux frontend, Rust-based Actix backend, and
SQLite database.
</td>
</tr>
<tr>
<td>Personal Websites</td>
<td>HTML, JS, Design, UI/UX</td>
<td>Ongoing</td>
<td class="row-leftalign">
Continuously evolving my personal site and designing
other creative websites. Experimented with Svelte,
Vue, and React/Redux using libraries such as P5 and
Three.js.
</td>
</tr>
<tr>
<td>3D Printing</td>
<td>FreeCAD</td>
<td>Ongoing</td>
<td class="row-leftalign">
Designing and manufacturing household objects and
repairs, including replacement window handles, desk
organizers, and 3D scans.
</td>
</tr>
</tbody>
<tbody></tbody>
</table>
<h2>Commitments</h2>
<table>
<thead>
<tr>
<th>Activity</th>
<th>Date</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td>Learning Mandarin</td>
<td>Ongoing</td>
<td class="row-leftalign">
Aiming to complete HSK 3 proficiency exam by
December 2026
</td>
</tr>
<!-- <tr> -->
<!-- <td>Cybersecurity Training</td> -->
<!-- <td>Ongoing</td> -->
<!-- <td class="row-leftalign"> -->
<!-- Using <em>pwn.college, tryhackme.com</em> to learn pentesting techniques.</td> -->
<!-- </tr> -->
<tr>
<td>Sports Activities</td>
<td>Ongoing</td>
<td class="row-leftalign">
Run weekly, active gym attendee, regularly go
hiking.
</td>
</tr>
<tr>
<td>Construction and Landscaping</td>
<td>Ongoing</td>
<td class="row-leftalign">
Involved in building a house in Bulgaria.
</td>
</tr>
<tr>
<td>University of Waterloo Film Club</td>
<td>2023-2024</td>
<td class="row-leftalign">
Worked on student films <em>Moon King</em> and
<em>HAM</em>, available online.
</td>
</tr>
<tr>
<td>Socratica</td>
<td>2023-2024</td>
<td class="row-leftalign">
Worked with like-minded individuals exploring
innovative tech.
</td>
</tr>
<tr>
<td>University of Leeds Hockey Club</td>
<td>2022-2023</td>
<td class="row-leftalign">
Played for the University of Leeds Hockey Club.
</td>
</tr>
<tr>
<td>Royal Air Force Air Cadets</td>
<td>2017-2020</td>
<td class="row-leftalign">
Achieved the role of Sergeant and Best Cadet"
award.
</td>
</tr>
</tbody>
</table>
<!-- <div class="interests"> -->
<!-- <table> -->
<!-- <tr><th>Personal qualities</th></tr> -->
<!-- <tr><td>Intuitive</td></tr> -->
<!-- <tr><td>Communicative</td></tr> -->
<!-- <tr><td>Adaptable</td></tr> -->
<!-- <tr><td>Versatile</td></tr> -->
<!-- <tr><td>Diligent</td></tr> -->
<!-- </table> -->
<!-- <table> -->
<!-- <tr><th>Interests</th></tr> -->
<!-- <tr><td>Neuroscience</td></tr> -->
<!-- <tr><td>Bouldering</td></tr> -->
<!-- <tr><td>Science Fiction</td></tr> -->
<!-- <tr><td>Mathematics</td></tr> -->
<!-- <tr><td>Hiking</td></tr> -->
<!-- </table> -->
<!-- <table> -->
<!-- <tr><th>Languages</th></tr> -->
<!-- <tr><td>Rust</td></tr> -->
<!-- <tr><td>HTML/JS</td></tr> -->
<!-- <tr><td>C/C++</td></tr> -->
<!-- <tr><td>React/Vue</td></tr> -->
<!-- <tr><td>Python</td></tr> -->
<!-- </table> -->
<!-- </div> -->
</div>
<div
class="no-print"
style="width: 100%; text-align: center; margin: 20px 0"
>
<h1>END</h1>
</div>
</div>
</template>
<style>
@import "/css/cv_styles.css";
@media print {
@page {
size: A4 portrait;
margin: 0;
}
}
</style>

View File

@@ -0,0 +1,47 @@
<script setup></script>
<template>
<main>
<h1>Welcome</h1>
<h2>whoami?</h2>
<p>Hi im Adam</p>
<h2>cv</h2>
<RouterLink to="/cv">CV</RouterLink>
<h2>bookmarks</h2>
<a href="/pages/bookmarks.html">bookmarks</a>
<h2>Listening to:</h2>
<div
x-data="spotifyPlayer()"
x-init="fetchNowPlaying(); setInterval(fetchNowPlaying, 60000)"
class="spotify-card"
>
<img :src="album_image" class="album-img" alt="" />
<div class="spotify-info">
<div x-text="song_name || 'No song playing'"></div>
<div x-text="artist_name"></div>
<div
x-text="playing ? 'Playing' : ''"
:class="{ playing: playing }"
></div>
<a :href="song_url"></a>
</div>
</div>
<!--<h2> </h2>
<p>
Sometimes there's this fire that sends shivers down my back.
It'll come when I'm lis
</p>
-->
<!--<h2>Shrines</h1>
<a href="/pages/shrines/evangelion.html">Evangelion</a>
<a href="/pages/shrines/skipskipbenben.html">Skip skip ben ben</a>
<a href="/pages/shrines/demoman.html">demoman</a>-->
<!--<a href="pages/shrines/gto.html">GTO</a>-->
</main>
</template>