declassify some projects

This commit is contained in:
osmarks 2023-06-19 14:09:54 +01:00
parent 307c513a9b
commit 30a1e0e358
40 changed files with 2828 additions and 8 deletions

4
.gitignore vendored
View File

@ -1 +1,3 @@
out.wav
out.wav
*/target/*
code-guessing/analytics/people

View File

@ -39,4 +39,14 @@ This comes with absolutely no guarantee of support or correct function, although
* `length_terminated_strings.c` - a revolution in computer science, combining the efficient `strlen` of null-terminated strings with the... inclusion of length? of length-prefixed/fat-pointer strings. A length-terminated string has its length at the *end*, occupying 1 to 8 bytes. To find its length, simply traverse the string until the data at the end matches the length traversed so far. Yes, this implementation might slightly have a bit of undefined behaviour.
* `discord-message-dump.py`, which reads a GDPR data dump from Discord and copies all the messages in public channels to a CSV file. I used this for training of a GPT-2 instance on my messages (available on request).
* `spudnet-http.py` - connect to the SPUDNET backend underlying [PotatOS](https://git.osmarks.net/osmarks/potatOS/)'s ~~backdoors~~ remote debugging system via the convenient new HTTP long-polling-based API.
* `fractalart-rs` - [this](https://github.com/TomSmeets/FractalArt/) in Rust and faster, see its own README for more details.
* `fractalart-rs` - [this](https://github.com/TomSmeets/FractalArt/) in Rust and faster, see its own README for more details.
* `arbtt_wayland_toplevel.py` - interfaces [arbtt](https://arbtt.nomeata.de/) with Wayland foreign toplevel protocol and idle notifications.
* `fractalize_image.py` - used for making a profile picture for someone once.
* `goose2function.py` - converts goose neck profiles extracted from images of geese into activation functions for machine learning.
* `histretention.py` - dump Firefox `places.sqlite3` into a separate database (Firefox clears out old history internally for performance reasons or something like that) for later search.
* `memeticize.py` - the script I use to process memes from a large directory of heterogenous files.
* `rng_trainer.html` - a very unfinished attempt to implement a paper I found on training high-quality random number generation.
* `smtp2rss.py` - bridge SMTP (inbound emails) to RSS.
* `yearbox.html` - DokuWiki-type yearbox prototype for Minoteaur (I think this actually contains an off-by-one error somewhere; it isn't what's actually in use).
* `arbitrary-politics-graphs` - all you need to run your own election campaign.
* `heavbiome` - some work on biome generation with Perlin noise.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<style>
.outer {
display: flex;
}
.inner {
width: 50%;
}
img {
width: 30em;
}
h1 {
font-style: italic;
font-size: 4em;
font-weight: 400;
margin-top: 0;
}
h2 {
font-style: italic;
margin-top: 0;
font-size: 3em;
font-weight: 400;
}
</style>
<div class="outer">
<div class="inner">
<h1>if I am elected</h1>
<h2>good things</h2>
<img src="g1.png">
<h2>bad things</h2>
<img src="g2.png">
</div>
<div class="inner">
<h1>otherwise</h1>
<h2>good things</h2>
<img src="g4.png">
<h2>bad things</h2>
<img src="g3.png">
</div>
</div>

128
arbtt_wayland_toplevel.py Executable file
View File

@ -0,0 +1,128 @@
#!/usr/bin/env python3
from functools import partial
from datetime import datetime, timezone
from wl_framework.network.connection import WaylandConnection
from wl_framework.protocols.base import UnsupportedProtocolError
from wl_framework.protocols.foreign_toplevel import ForeignTopLevel
from wl_framework.protocols.data_control import DataControl
from wl_framework.protocols.idle_notify import ( IdleNotifyManager, IdleNotifier as _IdleNotifier )
import asyncio.subprocess as subprocess
import orjson
class ForeignTopLevelMonitor(ForeignTopLevel):
def __init__(self, *args, **kwargs):
self.tlwindows = {}
super().__init__(*args, **kwargs)
def on_toplevel_created(self, toplevel):
self.tlwindows[toplevel.obj_id] = {}
def on_toplevel_synced(self, toplevel):
self.tlwindows[toplevel.obj_id]["app_id"] = toplevel.app_id
self.tlwindows[toplevel.obj_id]["title"] = toplevel.title
self.tlwindows[toplevel.obj_id]["states"] = toplevel.states
self.tlwindows[toplevel.obj_id]["outputs"] = { x.name for x in toplevel.outputs }
def on_toplevel_closed(self, toplevel):
del self.tlwindows[toplevel.obj_id]
# not actually using this
class ClipboardMonitor(DataControl):
def on_new_selection(self, offer):
self._print_selection(offer)
self._receive(offer)
def on_new_primary_selection(self, offer):
self._print_selection(offer, is_primary=True)
self._receive(offer, is_primary=True)
# Internal
def _receive(self, offer, is_primary=False):
if offer is None:
return
for mime in (
'text/plain;charset=utf-8',
'UTF8_STRING',
):
if mime in offer.get_mime_types():
offer.receive(mime, partial(self._on_received, is_primary=is_primary))
break
def _print_selection(self, offer, is_primary=False):
_selection = 'primary' if is_primary else 'main'
if offer is None:
self.log(f"{_selection.capitalize()} selection cleared")
return
self.log(f"New {_selection} selection offers:")
for mime_type in offer.get_mime_types():
self.log(f" {mime_type}")
def _on_received(self, mime_type, data, is_primary=False):
if data:
data = data.decode('utf-8')
self.log(f"Received {' primary' if is_primary else 'main'} selection: '{data}'")
class IdleNotifier(_IdleNotifier):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.idle_start = None
def on_idle(self):
self.idle_start = datetime.now(tz=timezone.utc)
def on_resume(self):
self.idle_start = None
class WlMonitor(WaylandConnection):
def on_initial_sync(self, data):
super().on_initial_sync(data)
self.toplevels = ForeignTopLevelMonitor(self)
#self.clipboard = ClipboardMonitor(self)
self.idle = IdleNotifyManager(self, IdleNotifier)
self.idle_notifier = self.idle.get_idle_notifier(0, self.display.seat)
INTERVAL_MS = 60_000
def generate_log_entry(wl: WlMonitor()):
entry = {"desktop": ""}
now = datetime.now(timezone.utc)
entry["date"] = now.isoformat()
entry["rate"] = INTERVAL_MS
if wl.idle_notifier.idle_start:
entry["inactive"] = int((now.timestamp() - wl.idle_notifier.idle_start.timestamp()) * 1000)
else:
entry["inactive"] = 0
def arbitrary_output(window):
s = window["outputs"]
try:
return s.pop()
except KeyError:
return ""
entry["windows"] = [ { "title": x["title"], "program": x["app_id"], "active": "activated" in x["states"], "hidden": "minimized" in x["states"] } for x in wl.toplevels.tlwindows.values() ]
return entry
if __name__ == '__main__':
import sys
import asyncio
from wl_framework.loop_integrations import AsyncIOIntegration
async def init():
arbtt_importer = await subprocess.create_subprocess_exec("arbtt-import", "-a", "-t", "JSON", stdin=subprocess.PIPE)
loop = AsyncIOIntegration()
try:
app = WlMonitor(eventloop_integration=loop)
await asyncio.sleep(1)
while True:
entry = generate_log_entry(app)
arbtt_importer.stdin.write(orjson.dumps(entry))
arbtt_importer.stdin.write(b"\n")
await asyncio.sleep(INTERVAL_MS / 1000)
except RuntimeError as e:
print(e)
sys.exit(1)
try:
asyncio.run(init())
except KeyboardInterrupt:
print()

27
beep.py
View File

@ -56,10 +56,27 @@ def save_wav(file_name):
wav_file.close()
return
BAD_INTERVALS = [6, 8, 10, 11, 13]
NOTE_FACTOR = pow(2, 1/12)
LOG_NOTE_FACTOR = math.log(NOTE_FACTOR)
BASE = 440
MIN = math.ceil(math.log(200/440) / LOG_NOTE_FACTOR)
MAX = math.floor(math.log(2000/440) / LOG_NOTE_FACTOR)
import random
freq = 6
for i in range(160):
append_sinewave(volume=1.0, freq=math.exp(freq), duration_milliseconds=50)
freq += random.uniform(-0.2, 0.2)
freq = max(4.5, min(freq, 9))
for i in range(10):
"""
for i in range(3):
append_sinewave(800, 100)
append_silence(100)
append_sinewave(600, 500)
append_silence()
"""
note = random.randint(MIN, MAX)
while MIN <= note <= MAX:
interval = random.choice(BAD_INTERVALS)
time = random.randint(50, 1000)
append_sinewave(BASE * pow(NOTE_FACTOR, note), time)
note += interval if random.choice((True, False)) else -interval
save_wav("output.wav")

View File

@ -0,0 +1,28 @@
from bs4 import BeautifulSoup
import requests
import os
import os.path
people = {
}
for round_id in range(6, 13):
print("round", round_id)
m = BeautifulSoup(requests.get(f"https://cg.esolangs.gay/{round_id}/").text, features="lxml")
for file_link in m.select("details > summary > a"):
name = file_link.parent.parent.previous_sibling.previous_sibling.previous_sibling.previous_sibling
if "impersonating" in name.text:
name = name.previous_sibling.previous_sibling
assert "written by" in name.text, f"oh no {name.text}"
name = name.text.split("written by ")[-1]
name = people.get(name, name).lower()
href = file_link.attrs["href"]
filename = str(round_id) + "-" + href.split(f"/{round_id}/")[-1]
full_href = f"https://cg.esolangs.gay{href}"
os.makedirs(os.path.join("people", name), exist_ok=True)
with open(os.path.join("people", name, filename), "wb") as f:
with requests.get(full_href, stream=True) as r:
r.raise_for_status()
for chunk in r.iter_content(chunk_size=(2<<18)):
f.write(chunk)

View File

@ -0,0 +1,6 @@
import os
import os.path
for person in os.listdir("people"):
for submission in os.listdir(os.path.join("people", person)):
print(person, submission)

257
code-guessing/entry-rs/Cargo.lock generated Normal file
View File

@ -0,0 +1,257 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "entry-rs"
version = "0.1.0"
dependencies = [
"pyo3",
]
[[package]]
name = "indoc"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8"
dependencies = [
"indoc-impl",
"proc-macro-hack",
]
[[package]]
name = "indoc-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
"unindent",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "libc"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
[[package]]
name = "lock_api"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
"scopeguard",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "paste"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
dependencies = [
"paste-impl",
"proc-macro-hack",
]
[[package]]
name = "paste-impl"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
dependencies = [
"proc-macro-hack",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "pyo3"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35100f9347670a566a67aa623369293703322bb9db77d99d7df7313b575ae0c8"
dependencies = [
"cfg-if",
"indoc",
"libc",
"parking_lot",
"paste",
"pyo3-build-config",
"pyo3-macros",
"unindent",
]
[[package]]
name = "pyo3-build-config"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d12961738cacbd7f91b7c43bc25cfeeaa2698ad07a04b3be0aa88b950865738f"
dependencies = [
"once_cell",
]
[[package]]
name = "pyo3-macros"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0bc5215d704824dfddddc03f93cb572e1155c68b6761c37005e1c288808ea8"
dependencies = [
"pyo3-macros-backend",
"quote",
"syn",
]
[[package]]
name = "pyo3-macros-backend"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71623fc593224afaab918aa3afcaf86ed2f43d34f6afde7f3922608f253240df"
dependencies = [
"proc-macro2",
"pyo3-build-config",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "unindent"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -0,0 +1,13 @@
[package]
name = "entry-rs"
version = "0.1.0"
edition = "2021"
[lib]
name = "entry_rs"
crate-type = ["cdylib"]
[dependencies.pyo3]
version = "0.14.5"
features = ["extension-module"]

View File

@ -0,0 +1,85 @@
pub fn entry(s: &str) -> i32 {
let digit = |c: u8| (c as i32) - 48;
let mut acc = 0;
let mut pos = 0;
let b = s.as_bytes();
loop {
match b[pos] {
b'+' => {
acc += {
pos += 1;
let mut acc = 0;
while (pos + 1) < b.len() && (b[pos + 1] == b'/' || b[pos + 1] == b'*') {
println!("DIV or MUL {} {} {}", b[pos], b[pos + 1], b[pos + 2]);
if acc == 0 {
acc = digit(b[pos])
}
if b[pos + 1] == b'/' {
acc /= digit(b[pos + 2])
} else {
acc *= digit(b[pos + 2])
}
pos += 2;
}
if acc == 0 {
digit(b[pos])
} else {
acc
}
};
pos += 1;
},
b'-' => {
acc -= {
pos += 1;
let mut acc = -1;
while (pos + 1) < b.len() && (b[pos + 1] == b'/' || b[pos + 1] == b'*') {
println!("DIV or MUL {} {} {}", b[pos], b[pos + 1], b[pos + 2]);
if acc == -1 {
acc = digit(b[pos])
}
if b[pos + 1] == b'/' {
acc /= digit(b[pos + 2])
} else {
acc *= digit(b[pos + 2])
}
pos += 2;
}
if acc == -1 {
digit(b[pos])
} else {
acc
}
};
pos += 1;
},
x => {
acc += {
let mut acc = 0;
while (pos + 1) < b.len() && (b[pos + 1] == b'/' || b[pos + 1] == b'*') {
println!("DIV or MUL {} {} {}", b[pos], b[pos + 1], b[pos + 2]);
if acc == 0 {
acc = digit(b[pos])
}
if b[pos + 1] == b'/' {
acc /= digit(b[pos + 2])
} else {
acc *= digit(b[pos + 2])
}
pos += 2;
}
if acc == 0 {
digit(b[pos])
} else {
acc
}
};
pos += 1
}
}
if pos >= b.len() {
break
}
}
acc
}

View File

@ -0,0 +1,16 @@
use pyo3::prelude::*;
mod entry_impl;
use entry_impl::entry;
#[pyfunction]
fn wrapped_entry(s: &str) -> PyResult<i32> {
Ok(entry(s))
}
#[pymodule]
fn entry_rs(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(wrapped_entry, m)?)?;
Ok(())
}

53
code-guessing/gif.s Normal file
View File

@ -0,0 +1,53 @@
; a hand-made GIF containing valid JavaScript code
; abusing header to start a JavaScript comment
; inspired by Saumil Shah's Deadly Pixels presentation
; Ange Albertini, BSD Licence 2013
; yamal gifjs.asm -o img.gif
WIDTH equ 10799 ; equivalent to 2f2a, which is '/*' in ASCII, thus starting an opening comment
HEIGHT equ 10799 ; just to make it easier to spot
db 'GIF89a'
dw WIDTH, HEIGHT
db 0 ; GCT
db -1 ; background color
db 0 ; default aspect ratio
;db 0fch, 0feh, 0fch
;times COLORS db 0, 0, 0
; no need of Graphic Control Extension
; db 21h, 0f9h
; db GCESIZE ; size
; gce_start:
; db 0 ; transparent background
; dw 0 ; delay for anim
; db 0 ; other transparent
; GCESIZE equ $ - gce_start
; db 0 ; end of GCE
db 02ch ; Image descriptor
dw 0, 0 ; NW corner
dw WIDTH, HEIGHT ; w/h of image
db 0 ; color table
db 2 ; lzw size
;db DATASIZE
;data_start:
; db 00, 01, 04, 04
; DATASIZE equ $ - data_start
db 0
db 3bh ; GIF terminator
; end of the GIF
db '*/' ; closing the comment
db '=1;' ; creating a fake use of that GIF89a string
db 'alert("haxx");'

View File

@ -0,0 +1,2 @@
<img src="./img.gif">
<script src="./img.js"></script>

BIN
code-guessing/img.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 B

1
code-guessing/img.js Symbolic link
View File

@ -0,0 +1 @@
img.gif

185
code-guessing/test12.py Normal file
View File

@ -0,0 +1,185 @@
import sys
import importlib
import subprocess
import ctypes
import random
import traceback
import textwrap
import json
import os
import shutil
try:
from tqdm import tqdm, trange
except ImportError:
print("`tqdm` not found. there will be no progress bars")
def tqdm(x):
return x
trange = range
filename = sys.argv[1]
if filename.endswith(".py"):
print("importing as Python...")
module = importlib.import_module(filename.removesuffix(".py"))
print("done.")
try:
entry = module.entry
except AttributeError:
print("module is missing entrypoint `entry`. aborting.")
sys.exit(1)
elif filename.endswith(".c"):
print("compiling as C with `gcc`...")
obj = "./" + filename.removesuffix(".c") + ".so"
rc = subprocess.call(["gcc", "-shared", "-fPIC", *sys.argv[2:], filename, "-o", obj])
if rc != 0:
print("compilation failed. aborting.")
sys.exit(rc)
lib = ctypes.CDLL(obj)
try:
entry = lib.entry
except AttributeError:
print("library is missing entrypoint `entry`. aborting.")
sys.exit(1)
elif filename.endswith(".rs"):
print("compiling as Rust...")
os.makedirs("./entry-rs/src", exist_ok=True)
with open("./entry-rs/Cargo.toml", "w") as f:
f.write("""
[package]
name = "entry-rs"
version = "0.1.0"
edition = "2021"
[lib]
name = "entry_rs"
crate-type = ["cdylib"]
[dependencies.pyo3]
version = "0.14.5"
features = ["extension-module"]
""")
with open("./entry-rs/src/lib.rs", "w") as f:
f.write("""
use pyo3::prelude::*;
mod entry_impl;
use entry_impl::entry;
#[pyfunction]
fn wrapped_entry(s: &str) -> PyResult<i32> {
Ok(entry(s))
}
#[pymodule]
fn entry_rs(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(wrapped_entry, m)?)?;
Ok(())
}
""")
shutil.copyfile(filename, "./entry-rs/src/entry_impl.rs")
os.chdir("entry-rs")
rc = subprocess.call(["cargo", "build", "--release"])
os.chdir("..")
if rc != 0:
print("compilation failed. aborting.")
sys.exit(1)
sys.path.append("./entry-rs/target/release")
os.rename("./entry-rs/target/release/libentry_rs.so", "./entry-rs/target/release/entry_rs.so")
module = importlib.import_module("entry_rs")
entry = module.wrapped_entry
else:
print("unrecognized file extension")
sys.exit(1)
tests = [
("zero", "0", 0),
("add", "1+2", 3),
("sub", "2-1", 1),
("mul", "2*3", 6),
("div", "4/2", 2),
("floor", "4/3", 1),
("neg", "0-4", -4),
("chain", "4/2/2", 1),
("olivia", "1+7/4-0*4", 2),
("gollario", "4-4-4-4+8", 0)
]
print("beginning testing suite")
failures = 0
for test_name, value, result in tqdm(tests):
print(f"`{test_name}`... ", end="")
try:
r = entry(value)
except BaseException:
print("error")
traceback.print_exc()
print(f"in test `{test_name}`:")
print(textwrap.indent(value, " "*2))
failures += 1
continue
if r == result:
print("ok")
else:
failures += 1
print("failed")
print(f"for test `{test_name}`:")
print(textwrap.indent(value, " "*2))
print(f"entry returned {r} when {result} was expected\n")
if not failures:
print("test suite finished, all ok")
else:
print(f"test suite finished. {failures} tests failed\n\n")
print("skipping randomized testing because your program is clearly broken and the output from those isn't very helpful for finding bugs")
sys.exit(2)
def gen_random_json():
s = random.choice("0123456789")
for _ in range(random.randint(0, 10)):
s += random.choice("+-*/")
s += random.choice("0123456789")
return s
print("beginning randomized testing.")
random_failures = 0
for _ in trange(100):
while True:
j = gen_random_json()
try:
tr = eval(j.replace("/", "//"))
except ZeroDivisionError:
continue
else:
break
try:
r = entry(j)
except BaseException:
print("error")
traceback.print_exc()
print("in randomized test case:")
print(textwrap.indent(j, " "*2))
random_failures += 1
continue
if r != tr:
print("randomized test case failed:")
print(textwrap.indent(j, " "*2))
print(f"entry returned {r} when {tr} was expected\n")
random_failures += 1
if not random_failures:
print("randomized testing finished. all ok\n\n")
else:
print(f"randomized testing finished with {random_failures} failures\n\n")
print("overall report:")
overall = failures + random_failures
if not overall:
print("no failures detected. all seems well!")
else:
print(f"{overall} failures detected overall. you have some bugs to fix")
sys.exit(2)

View File

@ -0,0 +1,14 @@
import os, sys, string
count = 0
words = 0
characters = 0
for dirpath, dirnames, filenames in os.walk(sys.argv[1]):
for filename in filenames:
with open(os.path.join(dirpath, filename)) as f:
content = f.read()
characters += len([c for c in content if c in string.ascii_letters])
words += len([thing for thing in content.split() if thing.strip()])
count += 1
print(words / count, characters / count)

19
cycdec.py Normal file
View File

@ -0,0 +1,19 @@
perm = [
[1, 2, 3, 4, 5, 6, 7, 8],
[5, 7, 8, 2, 3, 6, 4, 1]
]
perm = dict(zip(*perm))
vals = set(perm)
print(perm)
cycs = []
while vals:
nxt = vals.pop()
seen = []
while nxt not in seen:
lnxt = nxt
nxt = perm[nxt]
seen.append(lnxt)
cycs.append(seen)
vals -= set(seen)
print(cycs)

25
cyclcm.py Normal file
View File

@ -0,0 +1,25 @@
import math, itertools
count = 0
for permutation in itertools.permutations(range(7)):
perm = [
list(range(7)),
list(permutation)
]
perm = dict(zip(*perm))
vals = set(perm)
print(perm)
cycs = []
while vals:
nxt = vals.pop()
seen = []
while nxt not in seen:
lnxt = nxt
nxt = perm[nxt]
seen.append(lnxt)
cycs.append(seen)
vals -= set(seen)
if math.lcm(*map(len, cycs)) == 4:
count += 1
print(count)

View File

@ -148,6 +148,7 @@ li, h2 {
<li>heavpoot is osmarks</li>
<li>second #staff-only channel</li>
<li>colin</li>
<li>hactar secretly planning takeover</li>
<li>less than 10% of users are frequently active</li>
<li>cyber PC diagram</li>
</ul>
@ -175,4 +176,4 @@ li, h2 {
</ul>
</div>
</div>
</body></html>
</body></html>

41
fractalize_image.py Normal file
View File

@ -0,0 +1,41 @@
from PIL import Image
from functools import cache
from collections import namedtuple
Region = namedtuple("Region", ["left", "upper", "right", "lower"])
glasses = Image.open("./pv.webp")
glasses = glasses.crop(glasses.getbbox())
def scale(i, x):
if i.size[0] // x == 0 or i.size[1] // x == 0:
return False
return i.resize((int(i.size[0] // x), int(i.size[1] // x)))
output = Image.new("RGBA", (256, 256))
def paste_at_centre(src: Image, x, y):
#dc = Region(*dc)
left = x - src.size[0] // 2
upper = y - src.size[1] // 2
output.alpha_composite(src, (left, upper))
paste_at_centre(glasses, output.size[0] // 2, output.size[1] // 2)
def do_transpositions(im, parity, count):
if count == 0: return im
if parity:
return do_transpositions(im.transpose(Image.Transpose.ROTATE_90), parity, count - 1)
else:
return do_transpositions(im.transpose(Image.Transpose.ROTATE_270), parity, count - 1)
k = 1
while True:
#output.show()
s = 2**k
bounds = Region(*output.getbbox())
print(bounds)
if not scale(output, s):
break
g = do_transpositions(scale(output, s), True, k)
h = do_transpositions(scale(output, s), False, k)
paste_at_centre(g, bounds.left, bounds.upper)
paste_at_centre(h, bounds.right, bounds.upper)
paste_at_centre(g, bounds.left, bounds.lower)
paste_at_centre(h, bounds.right, bounds.lower)
k += 1
output.show()
output.save("./pvf.webp")

31
goose2function.py Normal file
View File

@ -0,0 +1,31 @@
import cv2, numpy, matplotlib.pyplot as plt, scipy.interpolate, sys
from collections import defaultdict
img = cv2.imread(sys.argv[1])
H, S, L = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
img_blur = cv2.GaussianBlur(H, (15,15), 0)
thresh = cv2.threshold(img_blur, 55, 255, cv2.THRESH_BINARY)[1]
edges = cv2.Canny(image=thresh, threshold1=100, threshold2=200)
c, hier = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
best = max(c, key=lambda x: x.shape[0])[:, 0, :]
min_x, max_x = min(best[:, 0]), max(best[:, 0])
min_y, max_y = min(best[:, 1]), max(best[:, 1])
xrange = max_x - min_x
yrange = max_y - min_y
coords = [[((x - min_x) / xrange) * 2 - 1, ((y - min_y) / yrange) * 2 - 1] for x, y in best ]
coords.sort(key=lambda x: x[0])
coords2 = defaultdict(list)
for x, y in coords:
coords2[x].append(y)
coords3x = []
coords3y = []
last = -1
for x, ys in coords2.items():
coords3y.append(min(ys, key=lambda x: abs(x - last)))
coords3x.append(x)
last = coords3y[-1]
i = scipy.interpolate.CubicSpline(coords3x, coords3y, extrapolate=True)
xs = numpy.arange(-1.0, 1.0, 0.02)
plt.plot(coords3x, coords3y, label='data')
plt.plot(xs, i(xs))
plt.show()

93
heavbiome/main.lua Normal file
View File

@ -0,0 +1,93 @@
local map = {}
local w = 300
local h = 300
local seed = 349259
require("perlin") -- not mine.
-- it uses globals. spiteful, but i do not care enough about code quality for this.
local biomecols = {
empty = {0.5, 0.5, 0.5},
forest = { 0, 0.5, 0},
rainforest = { 0, 0.5, 0.5},
ocean = { 0, 0, 1},
plains = { 0, 1, 0},
desert = { 1, 1, 0},
}
local PERSISTENCE = 0.3
local ITERS = 4
local function oct_noise(x, y, seed, gen)
local total = 0
local frequency = 1
local amplitude = 1
local norm = 0
for i = 1, ITERS do
total = total + gen:noise(x * frequency, y * frequency, seed + i) * amplitude
norm = norm + amplitude
frequency = frequency * 2
amplitude = amplitude * PERSISTENCE
end
return total
end
local sf = 1/20 -- scaling factor.
local function pick_biome(x, y, gen)
local islandicity = gen:noise(x*sf/10, y*sf/10, 302382359)/2+1/2
islandicity = math.min(math.max(0,islandicity*3),1)
local mainland_modifier = gen:noise(x*sf/20, y*sf/20, 302382359)/2
local is_ocean = gen:noise(x*sf/4, y*sf/4, 555575)/2+1/2 + gen:noise(x*sf, y*sf, 555575)/(25-islandicity*10) + mainland_modifier
local humidity = oct_noise(x*sf/2, y*sf/2, 10000, gen)/2+1/2
local temp = oct_noise(x*sf/6, y*sf/6, 20000, gen)/2+1/2
--return { is_ocean, humidity, temp }
--
if is_ocean > 0.45 then return "ocean" end
if is_ocean > 0.43 and humidity < 0.6 then return "desert" end -- coast?
if temp > 0.75 then return "desert" end
if humidity < 0.5 then
if temp > 0.5 then
return "desert"
else
return "plains"
end
else
if humidity > 0.75 then
return "rainforest"
else
if temp < 0.6 then
return "forest"
else
return "plains"
end
end
end
return "empty"
--]]
end
function love.load()
local noise = perlin(seed)
local gen = noise
for x=1, w do
map[x] = {}
for y=1, h do
map[x][y] = 0
end
end
for x=1, w do
for y=1, h do
local biome = pick_biome(x, y, noise)
--map[x][y] = biome
map[x][y] = biomecols[biome]
end
end
end
function love.draw()
for x=1, w do
for y=1, h do
love.graphics.setColor(unpack(map[x][y]))
love.graphics.rectangle("fill",x*2, y*2, 2, 2)
end
end
end

134
heavbiome/perlin.lua Normal file
View File

@ -0,0 +1,134 @@
local defaultSeed = 1337
local dot_product = {
[0x0]=function(x,y,z) return x + y end,
[0x1]=function(x,y,z) return -x + y end,
[0x2]=function(x,y,z) return x - y end,
[0x3]=function(x,y,z) return -x - y end,
[0x4]=function(x,y,z) return x + z end,
[0x5]=function(x,y,z) return -x + z end,
[0x6]=function(x,y,z) return x - z end,
[0x7]=function(x,y,z) return -x - z end,
[0x8]=function(x,y,z) return y + z end,
[0x9]=function(x,y,z) return -y + z end,
[0xA]=function(x,y,z) return y - z end,
[0xB]=function(x,y,z) return -y - z end,
[0xC]=function(x,y,z) return y + x end,
[0xD]=function(x,y,z) return -y + z end,
[0xE]=function(x,y,z) return y - x end,
[0xF]=function(x,y,z) return -y - z end
}
local grad = function(hash, x,y,z)
return dot_product[hash % 0x10](x,y,z)
end
local fade = function(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end
local lerp = function(t,a,b)
return a + t * (b - a)
end
local generatePermutation = function(seed)
math.randomseed(seed)
local permutation = {0}
for i=1,255 do
table.insert(permutation,math.random(1,#permutation+1),i)
end
local p = {}
for i=0,255 do
p[i] = permutation[i+1]
p[i+256] = permutation[i+1]
end
return p
end
perlin = {}
perlin.__index = perlin
perlin.noise = function(self,x,y,z)
y = y or 0
z = z or 0
local xi = math.floor(x) % 0x100
local yi = math.floor(y) % 0x100
local zi = math.floor(z) % 0x100
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)
local u = fade(x)
local v = fade(y)
local w = fade(z)
local A, AA, AB, AAA, ABA, AAB, ABB, B, BA, BB, BAA, BBA, BAB, BBB
A = self.p[xi ] + yi
AA = self.p[A ] + zi
AB = self.p[A+1 ] + zi
AAA = self.p[ AA ]
ABA = self.p[ AB ]
AAB = self.p[ AA+1 ]
ABB = self.p[ AB+1 ]
B = self.p[xi+1] + yi
BA = self.p[B ] + zi
BB = self.p[B+1 ] + zi
BAA = self.p[ BA ]
BBA = self.p[ BB ]
BAB = self.p[ BA+1 ]
BBB = self.p[ BB+1 ]
return lerp(w,
lerp(v,
lerp(u,
grad(AAA,x,y,z),
grad(BAA,x-1,y,z)
),
lerp(u,
grad(ABA,x,y-1,z),
grad(BBA,x-1,y-1,z)
)
),
lerp(v,
lerp(u,
grad(AAB,x,y,z-1), grad(BAB,x-1,y,z-1)
),
lerp(u,
grad(ABB,x,y-1,z-1), grad(BBB,x-1,y-1,z-1)
)
)
)
end
setmetatable(perlin,{
__call = function(self,seed)
seed = seed or defaultSeed
return setmetatable({
seed = seed,
p = generatePermutation(seed),
},self)
end
})
--[[
EXAMPLE
local p1 = perlin(1338)
local p2 = perlin(1337)
local x,y,z = 1.0,2.0,3.0
print(p1:noise(x,y,z))
print(p2:noise(x,y,z))
>> 0.23456
>> 0.47598
--]]

36
histretention.py Normal file
View File

@ -0,0 +1,36 @@
import sqlite3
longterm = sqlite3.connect("/data/archive/lthist.sqlite3")
longterm.executescript(f"""
CREATE TABLE IF NOT EXISTS places (
guid TEXT PRIMARY KEY,
url TEXT,
title TEXT,
visit_count INTEGER DEFAULT 0,
last_visit_date INTEGER,
description TEXT,
preview_image_url TEXT
);
CREATE TABLE IF NOT EXISTS bookmarks (
guid TEXT PRIMARY KEY,
bookmark TEXT NOT NULL REFERENCES places(guid),
title TEXT,
dateAdded INTEGER,
lastModified INTEGER
);
CREATE TABLE IF NOT EXISTS historyvisits (
id TEXT PRIMARY KEY,
place TEXT NOT NULL REFERENCES places(guid),
date INTEGER NOT NULL,
type INTEGER NOT NULL
);
""")
longterm.execute("ATTACH DATABASE '/tmp/places.sqlite' AS transient;")
longterm.execute("""INSERT INTO places SELECT guid, url, title, visit_count, last_visit_date, description, preview_image_url FROM moz_places WHERE true
ON CONFLICT DO UPDATE SET visit_count = excluded.visit_count, last_visit_date = excluded.last_visit_date, title = excluded.title, description = excluded.description, preview_image_url = excluded.preview_image_url;""")
longterm.execute("""INSERT INTO bookmarks SELECT moz_bookmarks.guid, moz_places.guid, moz_bookmarks.title, dateAdded, lastModified FROM moz_bookmarks JOIN moz_places ON moz_places.id = moz_bookmarks.fk WHERE true
ON CONFLICT DO UPDATE SET lastModified = excluded.lastModified, title = excluded.title;""")
# TODO: possibly wrong with new profile, might need to increment historyvisits or something
longterm.execute("INSERT INTO historyvisits SELECT (moz_historyvisits.id || '/' || visit_date), moz_places.guid, visit_date, visit_type FROM moz_historyvisits JOIN moz_places ON moz_places.id = moz_historyvisits.place_id ON CONFLICT DO NOTHING;")
longterm.commit()

25
hyperplane.py Normal file
View File

@ -0,0 +1,25 @@
def step(x):
return ((x * 1039) + (x * 6177) + 1605 + (x * 4253)) % 8041
points = set()
a, b, c = None, None, 417
for n in range(100000):
a, b, c = b, c, step(c)
if a is not None and b is not None:
points.add((a, b, c))
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(projection="3d")
def unzip(l):
ls = []
for x in l:
for i, v in enumerate(x):
if len(ls) <= i:
ls.append([])
ls[i].append(v)
return ls
ax.scatter(*unzip(points))
plt.show()

22
memeticize.py Normal file
View File

@ -0,0 +1,22 @@
import os, sys, subprocess, datetime
dt_threshold = datetime.datetime(2022, 11, 27).timestamp()
_, indir, outdir = sys.argv
for x in os.listdir(indir):
inpath = os.path.join(indir, x)
if os.stat(inpath).st_mtime > dt_threshold:
if subprocess.run(("feh", inpath)).returncode == 0:
newname = input(x + ": ")
if newname:
ctr = 1
while True:
newpath = os.path.join(outdir, newname + (f"-{ctr}" if ctr > 1 else "") + os.path.splitext(x)[1])
if os.path.exists(newpath):
print("already in use")
ctr += 1
else:
os.rename(inpath, newpath)
break
else:
print("keeping")

BIN
output.wav Normal file

Binary file not shown.

61
randomlookingthing.html Normal file
View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<h1>changed version</h1>
<input type="number" id="w" min="0" value="8" placeholder="width">
<input type="number" id="h" min="0" value="8" placeholder="height">
<input type="range" id="d" min="0" max="1" step="0.0001" value="0.1" placeholder="density">
<button id="run">run</button>
<br>
<canvas id="out"></canvas>
<script>
const pixels = 32
const zipWith = (f, xs, ys) => xs.map((x, i) => f(x, ys[i]))
const sum = xs => xs.reduce((a, y) => a + y, 0)
const vecAdd = (a, b) => zipWith((x, y) => x + y, a, b)
const hadamardProduct = (a, b) => zipWith((x, y) => x * y, a, b)
const scalarMult = (a, n) => a.map(x => x * n)
const dotProduct = (a, b) => sum(hadamardProduct(a, b))
const vecLength = a => Math.sqrt(sum(a.map(x => x ** 2)))
const normalize = a => scalarMult(a, 1/vecLength(a))
const vsub = (x, y) => vecAdd(x, scalarMult(y, -1))
const clampDim = (a, i, min, max) => {
if (a[i] >= max) {
a[i] = min + a[i] - max
}
if (a[i] <= min) {
a[i] = max - (min - a[i])
}
}
run.onclick = () => {
const width = parseInt(w.value)
const height = parseInt(h.value)
const geomean = Math.sqrt(width * height)
const rdist = 1 / geomean
const density = parseFloat(d.value)
out.width = width * pixels
out.height = height * pixels
const ctx = out.getContext("2d")
/*
let points = Array(Math.floor(width * height * density)).fill(null).map(x => [Math.random(), Math.random()])
for (let i = 0; i < 100; i++) {
for (let j = 0; j < points.length; j++) {
const p = points[j]
const v = points.filter((_, k) => k !== j).map(q => {
const direction = normalize(vsub(p, q))
const distance = vecLength(vsub(p, q))
const magnitude = distance < rdist ? -5 : distance ** -3
return scalarMult(direction, magnitude)
}).reduce(vecAdd, [0, 0])
clampDim(p, 0, 0, 1)
clampDim(p, 1, 0, 1)
points[j] = vecAdd(p, scalarMult(v, 0.025))
}
}
console.log(points)*/
for (let x = 0; x < width; x++) {
for (let y = 0; y < width; y++) {
ctx.fillStyle = /*points.filter(([p, q]) => p >= (x / width) && p <= ((x + 1) / width) && q >= (y / height) && q <= ((y + 1) / height)).length > 0*/ Math.random() < density ? "green" : "black"
ctx.fillRect(x * pixels, y * pixels, pixels, pixels)
}
}
}
</script>

View File

@ -0,0 +1,116 @@
<body>
<script src="https://unpkg.com/mithril/mithril.js"></script>
<script>
let people = 3
let info = []
let pointsForRank = ""
const regenerateInfo = () => {
if (info.length > people) {
info = info.slice(0, people)
}
if (info.length < people) {
info = JSON.parse(JSON.stringify(info.concat(new Array(people - info.length).fill({ rank: "", weighting: 1 }))))
}
}
regenerateInfo()
const changeCount = ev => {
people = parseInt(ev.target.value)
regenerateInfo()
}
const permutations = xs => {
if (xs.length === 0) { return [[]] }
const result = []
for (let i = 0; i < xs.length; i++) {
const listWithoutIth = xs.slice(0, i).concat(xs.slice(i + 1))
for (const perm of permutations(listWithoutIth)) {
result.push([xs[i]].concat(perm))
}
}
return result
}
const range = n => new Array(n).fill(null).map((x, i) => i)
let result = ""
const targetPerms = 5
const solve = () => {
const points = pointsForRank.split(",").map(parseFloat)
if (points.length !== people) {
throw "There must be as many points defined as ranks."
} else if (!points.every(x => !isNaN(x))) {
throw "Invalid number"
}
const pointsForPerson = []
for (let i = 0; i < info.length; i++) {
const inf = info[i]
const rankOrder = inf.rank.split(",").map(parseFloat)
const sortedCopy = Array.from(rankOrder)
sortedCopy.sort((a, b) => a - b)
console.log(JSON.stringify(sortedCopy), JSON.stringify(range(people).map(x => x + 1)))
if (rankOrder.length !== people) {
throw `Person ${i + 1}'s ranking is too short/long.`
} else if (!rankOrder.every(x => !isNaN(x))) {
throw `Person ${i + 1}'s ranking contains an invalid number.`
} else if (JSON.stringify(sortedCopy) !== JSON.stringify(range(people).map(x => x + 1))) {
throw `Person ${i + 1}'s ranking is not a valid permutation.`
}
const newx = new Array(people).fill(null)
for (let j = 0; j < people; j++) {
const thingWithJthRank = rankOrder[j] - 1
newx[thingWithJthRank] = inf.weighting * points[j]
}
pointsForPerson.push(newx)
}
console.log("got", points, pointsForPerson)
let bestPerms = []
for (const perm of permutations(range(people))) { // ith person gets perm[i]th thing person
const score = perm.map((person, thingIndex) => pointsForPerson[person][thingIndex]).reduce((a, b) => a + b, 0)
for (let i = 0; i < bestPerms.length; i++) {
const [otherPerm, otherScore] = bestPerms[i]
if (otherScore < score) {
bestPerms.splice(i, 1)
break
}
}
if (bestPerms.length < targetPerms) {
bestPerms.push(([perm, score]))
}
}
bestPerms.sort((a, b) => b[1] - a[1])
return m("", [
"Solutions found:",
m("ul", bestPerms.map(([bestPerm, bestScore]) => m("li", bestPerm.map((person, thingIndex) => `Person ${person + 1} gets ${thingIndex + 1}`).join("; ") + ` (${bestScore} points).`)))
])
}
const solveWrapper = () => {
try {
result = solve()
} catch(e) {
console.warn(e)
result = e.toString()
}
}
m.mount(document.body, {
view: function() {
return [
m("p", "Rank orders must be given as comma-separated one-indexed integers. The points for each rank must be given as comma-separated real numbers."),
m("input", { value: pointsForRank, placeholder: "Points for ranks", oninput: ev => { pointsForRank = ev.target.value } }),
m("", [ m("label", "People: "), m("input[type=number]", { value: people, oninput: changeCount }) ]),
m("", info.map((i, index) => m("", [
m("label", `Person ${index + 1}: `),
m("input", { value: i.rank, placeholder: "Ranking", oninput: ev => { i.rank = ev.target.value } }),
m("label", " Weighting: "), m("input[type=number]", { value: i.weighting, oninput: ev => { i.weighting = parseFloat(ev.target.value) }, step: 0.1 })
]))),
m("button", { onclick: solveWrapper }, "Solve"),
m("", result)
]
}
})
</script>
</body>

61
rng_trainer.html Normal file
View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<!-- https://www.researchgate.net/publication/232494603_Can_People_Behave_Randomly_The_Role_of_Feedback -->
<meta charset="utf8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#buttons {
width: 100%;
display: flex;
}
#buttons button {
height: 20rem;
width: 100%;
margin: 2rem;
font-size: 3em;
}
button {
border-radius: 0;
border: 1px solid blue;
padding: 0.5rem;
}
</style>
<div id="buttons">
<button id="l">L</button>
<button id="r">R</button>
</div>
<div id="other-controls">
<div id="qty"></div>
<button id="restart">Restart</button>
</div>
<div id="seq"></div>
<script>
var working = true
const FINALSEQLEN = 100
const tests = {
"RNG1": []
}
var seq = []
const push = val => {
if (working) {
seq.push(val)
qty.innerText = `${seq.length}/${FINALSEQLEN}`
if (seq.length === FINALSEQLEN) {
working = false
qty.innerText = "Done"
}
}
}
restart.onclick = () => {
working = true
seq = []
}
l.onclick = () => push("L")
r.onclick = () => push("R")
window.onkeypress = ev => {
if (ev.key.toLowerCase() == "l" || ev.key == "1") {
push("L")
} else if (ev.key.toLowerCase() == "r" || ev.key == "2") {
push("R")
}
}
</script>

175
smtp2rss.py Normal file
View File

@ -0,0 +1,175 @@
import asyncio
from aiosmtpd.controller import UnthreadedController
from aiosmtpd.smtp import SMTP, syntax
from email.message import Message, EmailMessage
from email import message_from_bytes
from email.header import Header, decode_header, make_header
import aiosqlite
from datetime import datetime, timezone
from aiohttp import web
import re
import json
import feedparser.sanitizer
import rfeed
import base64
def now(): return datetime.now(tz=timezone.utc)
def decode_mime(subject): return str(make_header(decode_header(subject)))
def handle_addr(a):
if a:
if x := re.search("<(.*)>$", a.strip()):
return x.group(1)
else:
return a.strip()
async def open_connection():
conn = await aiosqlite.connect("./smtp2rss.sqlite3")
conn.row_factory = aiosqlite.Row
await conn.execute("PRAGMA journal_mode = WAL")
await conn.executescript("""
CREATE TABLE IF NOT EXISTS mails (
id INTEGER PRIMARY KEY,
timestamp REAL NOT NULL,
full_mail BLOB NOT NULL,
from_addr TEXT,
to_addr TEXT,
subject TEXT
);
""")
await conn.commit()
return conn
routes = web.RouteTableDef()
import dominate
from dominate.tags import *
def base_template(title, content, err=None):
doc = dominate.document(title=title)
with doc.head:
meta(name="viewport", content="width=device-width, initial-scale=1.0")
style("""
* {
box-sizing: border-box;
}
h1, h2, h3 {
margin-top: 0;
border-bottom: 1px solid gray;
font-weight: normal;
}
.mails .entry {
border: 1px solid gray;
margin: 0.5em;
padding: 0.5em;
}
""")
with doc:
if err: div(err, cls="error")
h1(title, cls="title")
m = main()
m += content
return web.Response(text=doc.render(), content_type="text/html")
preference = {
"text/html": 2,
"text/plain": 1
}
def email_to_html(emsg, debug_info=False):
if isinstance(emsg, Message):
payload = emsg.get_payload()
if isinstance(payload, list):
if not debug_info and emsg.get_content_type() == "multipart/alternative":
payload.sort(key=lambda x: preference.get(x.get_content_type(), 0))
return email_to_html(payload[-1], debug_info)
else:
html = [ email_to_html(thing, debug_info) for thing in payload ]
else:
try:
payload = emsg.get_payload(decode=True).decode("utf-8")
except:
payload = emsg.get_payload(decode=True).decode("latin1")
if emsg.get_content_subtype() == "html":
html = div(dominate.util.raw(feedparser.sanitizer._sanitize_html(payload.replace("<!doctype html>", ""), "utf-8", "text/html")))
else:
html = pre(payload)
else:
html = [ email_to_html(thing, debug_info) for thing in emsg.get_body(list(preference.keys())) ]
return div([
pre([ f"{header}: {value}\n" for header, value in emsg.items() ]) if debug_info else "",
html
], cls="entry")
async def run():
accessed_feeds = {}
loop = asyncio.get_event_loop()
db = await open_connection()
class Handler:
async def handle_DATA(handler, server, session, envelope):
mail = message_from_bytes(envelope.content)
print("got mail", handle_addr(mail["From"]), handle_addr(mail["To"]), mail["Subject"])
await db.execute_insert("INSERT INTO mails (timestamp, full_mail, from_addr, to_addr, subject) VALUES (?, ?, ?, ?, ?)",
(now().timestamp(), envelope.content, handle_addr(mail["From"]), handle_addr(mail["To"]), decode_mime(mail["Subject"])))
await db.commit()
return "250 OK"
controller = UnthreadedController(Handler(), loop=loop, hostname="127.0.0.1")
srv = await controller._create_server()
controller.server = srv
print(controller.hostname, controller.port)
@routes.get("/")
async def index(req):
page = int(req.query.get("page", 0))
exclude = [ feed for feed, time in accessed_feeds.items() if (time.timestamp() > (now().timestamp() - 3600)) ]
items = await db.execute_fetchall("SELECT * FROM mails WHERE from_addr NOT IN (SELECT value FROM json_each(?)) ORDER BY timestamp DESC LIMIT 25 OFFSET ?", (json.dumps(exclude), page * 25))
def display_mail(row):
data = message_from_bytes(row["full_mail"])
return div([
div([ datetime.fromtimestamp(row["timestamp"], tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S"), " / ", f"{row['from_addr'] or '[from addr missing]'}{row['to_addr'] or '[to addr missing]'}", " / ", row["subject"] or "[no subject]" ]),
email_to_html(data, True)
], cls="entry")
return base_template("Unused Mails", div([
display_mail(mail) for mail in items
], cls="mails"))
@routes.get("/feed/{from}")
async def feed(req):
accessed_feeds[req.match_info["from"]] = now()
items = []
for mail in await db.execute_fetchall("SELECT * FROM mails WHERE from_addr = ? ORDER BY timestamp DESC LIMIT 20", (req.match_info["from"],)):
data = message_from_bytes(mail["full_mail"])
content = email_to_html(data, debug_info=False).render()
items.append(rfeed.Item(
title=mail["subject"],
guid=rfeed.Guid(f"smtp2rss-{mail['id']}"),
pubDate=datetime.fromtimestamp(mail["timestamp"], tz=timezone.utc),
author=req.match_info["from"],
description=content.strip()
))
return web.Response(text=rfeed.Feed(
title=f"{req.match_info['from']} via SMTP2RSS",
lastBuildDate=now(),
link="http://localhost:3394",
description="",
items=items
).rss())
app = web.Application()
app.router.add_routes(routes)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "100.64.0.2", 3394)
await site.start()
loop = asyncio.get_event_loop_policy().get_event_loop()
loop.run_until_complete(run())
loop.run_forever()

BIN
spectro/1200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

777
spectro/AColorResources.py Normal file
View File

@ -0,0 +1,777 @@
const unsigned char specColormap[256][3] = [
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 1, 0],
[ 0, 1, 1],
[ 1, 1, 1],
[ 1, 2, 2],
[ 1, 2, 2],
[ 1, 3, 3],
[ 1, 4, 4],
[ 2, 5, 4],
[ 2, 6, 5],
[ 2, 7, 6],
[ 2, 8, 8],
[ 2, 9, 9],
[ 2, 10, 10],
[ 2, 11, 12],
[ 2, 13, 13],
[ 2, 14, 15],
[ 2, 15, 16],
[ 2, 16, 18],
[ 2, 18, 19],
[ 2, 19, 21],
[ 2, 20, 22],
[ 1, 21, 24],
[ 1, 22, 26],
[ 1, 23, 27],
[ 1, 25, 29],
[ 0, 26, 31],
[ 0, 27, 33],
[ 0, 28, 34],
[ 0, 29, 36],
[ 0, 30, 38],
[ 0, 31, 40],
[ 0, 32, 42],
[ 0, 33, 44],
[ 0, 34, 46],
[ 0, 35, 48],
[ 0, 36, 50],
[ 0, 37, 52],
[ 0, 38, 55],
[ 0, 39, 57],
[ 0, 40, 59],
[ 0, 41, 61],
[ 0, 42, 64],
[ 0, 42, 66],
[ 0, 43, 69],
[ 0, 44, 71],
[ 0, 45, 73],
[ 0, 46, 76],
[ 0, 46, 79],
[ 0, 47, 81],
[ 0, 48, 84],
[ 0, 48, 86],
[ 1, 49, 89],
[ 3, 49, 92],
[ 5, 50, 94],
[ 7, 50, 97],
[ 10, 51, 100],
[ 13, 51, 102],
[ 16, 51, 105],
[ 19, 52, 108],
[ 22, 52, 111],
[ 25, 52, 113],
[ 27, 53, 116],
[ 30, 53, 119],
[ 33, 53, 121],
[ 35, 53, 124],
[ 38, 53, 127],
[ 41, 53, 129],
[ 43, 53, 132],
[ 46, 53, 134],
[ 49, 53, 137],
[ 52, 53, 139],
[ 54, 52, 142],
[ 57, 52, 144],
[ 60, 52, 146],
[ 62, 52, 148],
[ 65, 51, 151],
[ 68, 51, 153],
[ 70, 51, 155],
[ 73, 50, 157],
[ 76, 50, 159],
[ 78, 49, 161],
[ 81, 49, 162],
[ 84, 48, 164],
[ 86, 48, 166],
[ 89, 47, 167],
[ 92, 46, 169],
[ 94, 46, 170],
[ 97, 45, 171],
[ 99, 45, 172],
[102, 44, 173],
[105, 44, 174],
[107, 43, 175],
[110, 42, 176],
[112, 42, 177],
[115, 41, 178],
[117, 41, 178],
[120, 40, 179],
[122, 40, 179],
[124, 39, 179],
[127, 39, 180],
[129, 38, 180],
[132, 38, 180],
[134, 37, 180],
[136, 37, 180],
[139, 37, 179],
[141, 37, 179],
[143, 36, 179],
[146, 36, 178],
[148, 36, 178],
[150, 36, 177],
[152, 36, 176],
[155, 36, 176],
[157, 36, 175],
[159, 36, 174],
[161, 36, 173],
[163, 37, 172],
[165, 37, 171],
[167, 37, 170],
[170, 38, 169],
[172, 38, 167],
[174, 39, 166],
[176, 39, 165],
[178, 40, 163],
[180, 41, 162],
[182, 41, 160],
[183, 42, 159],
[185, 43, 157],
[187, 44, 155],
[189, 45, 154],
[191, 45, 152],
[193, 46, 150],
[195, 47, 149],
[196, 48, 147],
[198, 49, 145],
[200, 50, 143],
[202, 51, 141],
[203, 53, 139],
[205, 54, 138],
[207, 55, 136],
[208, 56, 134],
[210, 57, 132],
[211, 59, 130],
[213, 60, 128],
[214, 61, 126],
[216, 63, 124],
[217, 64, 122],
[219, 65, 120],
[220, 67, 118],
[222, 68, 116],
[223, 70, 114],
[224, 71, 112],
[226, 72, 110],
[227, 74, 108],
[228, 75, 106],
[230, 77, 104],
[231, 78, 102],
[232, 80, 100],
[233, 81, 98],
[234, 83, 96],
[236, 84, 94],
[237, 86, 92],
[238, 87, 91],
[239, 89, 89],
[240, 91, 87],
[241, 92, 85],
[242, 94, 83],
[243, 96, 81],
[244, 97, 79],
[245, 99, 77],
[246, 101, 75],
[247, 102, 73],
[248, 104, 71],
[249, 106, 69],
[249, 108, 67],
[250, 110, 65],
[251, 111, 63],
[251, 113, 61],
[252, 115, 59],
[253, 117, 57],
[253, 119, 55],
[254, 121, 53],
[254, 123, 51],
[254, 125, 49],
[255, 127, 48],
[255, 129, 46],
[255, 131, 44],
[255, 134, 43],
[255, 136, 41],
[255, 138, 40],
[255, 140, 39],
[255, 142, 38],
[255, 145, 37],
[255, 147, 36],
[255, 149, 35],
[255, 151, 35],
[254, 154, 35],
[254, 156, 36],
[253, 158, 36],
[253, 161, 37],
[252, 163, 38],
[252, 166, 40],
[251, 168, 41],
[250, 170, 43],
[249, 173, 45],
[248, 175, 48],
[248, 177, 50],
[247, 180, 53],
[246, 182, 56],
[245, 185, 59],
[244, 187, 63],
[242, 189, 66],
[241, 192, 70],
[240, 194, 73],
[239, 196, 77],
[238, 198, 81],
[237, 200, 85],
[236, 203, 90],
[234, 205, 94],
[233, 207, 99],
[232, 209, 103],
[231, 211, 108],
[230, 213, 112],
[229, 215, 117],
[228, 217, 122],
[227, 219, 127],
[227, 220, 132],
[226, 222, 137],
[226, 224, 142],
[225, 225, 147],
[225, 227, 152],
[225, 228, 157],
[225, 230, 163],
[225, 231, 168],
[225, 233, 173],
[226, 234, 178],
[226, 235, 183],
[227, 236, 188],
[228, 237, 192],
[229, 238, 197],
[230, 239, 202],
[232, 240, 206],
[233, 241, 211],
[235, 242, 215],
[236, 243, 219],
[238, 244, 223],
[240, 245, 227],
[242, 245, 231],
[244, 246, 235],
[247, 247, 238],
[249, 248, 241],
[251, 248, 245],
[253, 249, 247],
[255, 250, 250],
];
const unsigned char selColormap[256][3] = [
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 78, 77],
[ 77, 78, 78],
[ 77, 78, 78],
[ 77, 79, 79],
[ 78, 80, 79],
[ 78, 80, 80],
[ 78, 81, 81],
[ 78, 82, 82],
[ 78, 83, 83],
[ 78, 84, 84],
[ 78, 85, 85],
[ 78, 86, 86],
[ 78, 87, 87],
[ 78, 88, 88],
[ 78, 89, 89],
[ 78, 90, 91],
[ 78, 91, 92],
[ 78, 91, 93],
[ 78, 92, 94],
[ 78, 93, 96],
[ 77, 94, 97],
[ 77, 95, 98],
[ 77, 96, 100],
[ 77, 97, 101],
[ 76, 98, 103],
[ 76, 99, 104],
[ 76, 100, 105],
[ 75, 100, 107],
[ 75, 101, 109],
[ 75, 102, 110],
[ 74, 103, 112],
[ 74, 104, 113],
[ 74, 105, 115],
[ 73, 105, 117],
[ 73, 106, 118],
[ 72, 107, 120],
[ 72, 108, 122],
[ 72, 108, 124],
[ 72, 109, 126],
[ 72, 110, 127],
[ 72, 110, 129],
[ 72, 111, 131],
[ 72, 112, 133],
[ 72, 112, 135],
[ 72, 113, 137],
[ 73, 113, 139],
[ 74, 114, 141],
[ 75, 115, 143],
[ 76, 115, 146],
[ 77, 116, 148],
[ 79, 116, 150],
[ 80, 116, 152],
[ 82, 117, 154],
[ 85, 117, 156],
[ 87, 117, 158],
[ 90, 118, 161],
[ 92, 118, 163],
[ 94, 118, 165],
[ 96, 118, 167],
[ 98, 119, 169],
[101, 119, 171],
[103, 119, 173],
[105, 119, 176],
[107, 119, 178],
[109, 119, 180],
[111, 119, 182],
[113, 119, 184],
[116, 119, 186],
[118, 119, 188],
[120, 118, 190],
[122, 118, 192],
[124, 118, 193],
[126, 118, 195],
[128, 118, 197],
[131, 117, 199],
[133, 117, 200],
[135, 117, 202],
[137, 116, 203],
[139, 116, 205],
[141, 115, 206],
[143, 115, 208],
[146, 115, 209],
[148, 114, 210],
[150, 114, 211],
[152, 113, 212],
[154, 113, 213],
[156, 112, 214],
[158, 112, 215],
[160, 111, 216],
[162, 111, 217],
[164, 110, 217],
[166, 110, 218],
[168, 110, 219],
[170, 109, 219],
[172, 109, 219],
[174, 108, 220],
[176, 108, 220],
[178, 107, 220],
[180, 107, 220],
[182, 107, 220],
[184, 106, 220],
[186, 106, 220],
[187, 106, 220],
[189, 106, 220],
[191, 106, 219],
[193, 105, 219],
[195, 105, 219],
[197, 105, 218],
[198, 105, 218],
[200, 105, 217],
[202, 105, 216],
[204, 105, 216],
[205, 106, 215],
[207, 106, 214],
[209, 106, 213],
[210, 106, 212],
[212, 107, 211],
[214, 107, 210],
[215, 108, 209],
[217, 108, 208],
[219, 108, 207],
[220, 109, 206],
[222, 110, 205],
[223, 110, 203],
[225, 111, 202],
[226, 111, 201],
[228, 112, 200],
[229, 113, 198],
[231, 114, 197],
[232, 114, 195],
[234, 115, 194],
[235, 116, 193],
[236, 117, 191],
[238, 118, 190],
[239, 119, 188],
[240, 120, 187],
[242, 120, 185],
[243, 121, 183],
[244, 122, 182],
[246, 123, 180],
[247, 124, 179],
[248, 126, 177],
[249, 127, 176],
[250, 128, 174],
[252, 129, 173],
[253, 130, 171],
[254, 131, 169],
[255, 132, 168],
[255, 133, 166],
[255, 134, 165],
[255, 136, 163],
[255, 137, 162],
[255, 138, 160],
[255, 139, 158],
[255, 140, 157],
[255, 142, 155],
[255, 143, 154],
[255, 144, 152],
[255, 145, 150],
[255, 146, 149],
[255, 148, 147],
[255, 149, 146],
[255, 150, 144],
[255, 152, 143],
[255, 153, 141],
[255, 154, 139],
[255, 156, 138],
[255, 157, 136],
[255, 158, 135],
[255, 160, 133],
[255, 161, 131],
[255, 163, 130],
[255, 164, 128],
[255, 166, 127],
[255, 167, 125],
[255, 169, 124],
[255, 170, 122],
[255, 172, 120],
[255, 173, 119],
[255, 175, 117],
[255, 177, 116],
[255, 178, 115],
[255, 180, 113],
[255, 182, 112],
[255, 183, 111],
[255, 185, 109],
[255, 187, 108],
[255, 189, 107],
[255, 190, 107],
[255, 192, 106],
[255, 194, 105],
[255, 196, 105],
[255, 198, 105],
[255, 200, 105],
[255, 201, 105],
[255, 203, 105],
[255, 205, 106],
[255, 207, 107],
[255, 209, 108],
[255, 211, 109],
[255, 213, 111],
[255, 215, 113],
[255, 217, 115],
[255, 218, 117],
[255, 220, 119],
[255, 222, 121],
[255, 224, 124],
[255, 226, 127],
[255, 228, 129],
[255, 230, 132],
[255, 232, 135],
[255, 233, 138],
[255, 235, 142],
[255, 237, 145],
[255, 239, 148],
[255, 240, 152],
[255, 242, 155],
[255, 244, 159],
[255, 245, 163],
[255, 247, 166],
[255, 248, 170],
[255, 250, 174],
[255, 251, 178],
[255, 253, 182],
[255, 254, 186],
[255, 255, 190],
[255, 255, 194],
[255, 255, 198],
[255, 255, 202],
[255, 255, 207],
[255, 255, 211],
[255, 255, 215],
[255, 255, 219],
[255, 255, 223],
[255, 255, 227],
[255, 255, 230],
[255, 255, 234],
[255, 255, 238],
[255, 255, 242],
[255, 255, 245],
[255, 255, 249],
[255, 255, 252],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
];
const unsigned char freqSelColormap[256][3] = [
[ 62, 65, 89],
[ 63, 65, 91],
[ 64, 66, 92],
[ 65, 66, 94],
[ 66, 66, 95],
[ 67, 67, 97],
[ 68, 67, 98],
[ 70, 67, 100],
[ 71, 68, 101],
[ 72, 68, 103],
[ 74, 68, 104],
[ 75, 69, 106],
[ 76, 69, 107],
[ 78, 69, 109],
[ 79, 69, 110],
[ 81, 69, 112],
[ 82, 70, 113],
[ 84, 70, 115],
[ 86, 70, 116],
[ 87, 70, 118],
[ 89, 70, 119],
[ 91, 70, 120],
[ 92, 70, 122],
[ 94, 70, 123],
[ 96, 70, 124],
[ 98, 70, 126],
[100, 70, 127],
[102, 70, 128],
[104, 70, 129],
[106, 69, 130],
[108, 69, 132],
[110, 69, 133],
[112, 69, 134],
[114, 69, 135],
[116, 68, 136],
[118, 68, 137],
[120, 68, 138],
[122, 67, 138],
[124, 67, 139],
[126, 66, 140],
[129, 66, 141],
[131, 65, 141],
[133, 65, 142],
[135, 64, 142],
[138, 64, 142],
[140, 63, 143],
[142, 63, 143],
[144, 62, 143],
[147, 61, 143],
[149, 61, 143],
[151, 60, 143],
[153, 59, 143],
[156, 59, 142],
[158, 58, 142],
[160, 57, 141],
[162, 57, 141],
[165, 56, 140],
[167, 55, 139],
[169, 55, 138],
[171, 54, 137],
[173, 53, 136],
[176, 53, 135],
[178, 52, 134],
[180, 51, 133],
[182, 51, 131],
[184, 50, 130],
[186, 50, 128],
[188, 49, 127],
[190, 49, 125],
[192, 49, 123],
[194, 48, 121],
[196, 48, 119],
[198, 48, 117],
[200, 48, 115],
[202, 48, 113],
[203, 47, 110],
[205, 47, 108],
[207, 48, 106],
[209, 48, 103],
[210, 48, 101],
[212, 48, 98],
[214, 48, 96],
[215, 49, 93],
[217, 49, 91],
[218, 50, 88],
[220, 50, 85],
[221, 51, 83],
[223, 52, 80],
[224, 53, 77],
[225, 54, 74],
[226, 55, 71],
[228, 56, 68],
[229, 57, 65],
[230, 58, 62],
[231, 59, 59],
[232, 60, 56],
[233, 62, 53],
[234, 63, 49],
[235, 64, 46],
[236, 66, 42],
[237, 67, 38],
[238, 69, 35],
[239, 71, 30],
[239, 72, 26],
[240, 74, 20],
[241, 76, 14],
[241, 77, 5],
[242, 79, 0],
[243, 81, 0],
[243, 83, 0],
[244, 85, 0],
[244, 87, 0],
[244, 89, 0],
[245, 91, 0],
[245, 93, 0],
[245, 95, 0],
[245, 97, 0],
[245, 99, 0],
[245, 101, 0],
[245, 103, 0],
[245, 105, 0],
[245, 108, 0],
[245, 110, 0],
[245, 112, 0],
[245, 114, 0],
[244, 116, 0],
[244, 119, 0],
[243, 121, 0],
[243, 123, 0],
[242, 126, 0],
[242, 128, 0],
[241, 130, 0],
[241, 132, 0],
[240, 135, 0],
[239, 137, 0],
[238, 139, 0],
[237, 142, 0],
[236, 144, 0],
[235, 146, 0],
[234, 149, 0],
[233, 151, 0],
[232, 154, 0],
[230, 156, 0],
[229, 158, 0],
[227, 161, 0],
[226, 163, 0],
[224, 165, 0],
[223, 168, 0],
[221, 170, 0],
[219, 173, 0],
[217, 175, 0],
[215, 177, 0],
[213, 180, 0],
[211, 182, 0],
[209, 184, 0],
[207, 187, 0],
[205, 189, 0],
[202, 191, 0],
[200, 193, 0],
[197, 196, 0],
[195, 198, 0],
[192, 200, 0],
[189, 203, 0],
[186, 205, 0],
[183, 207, 0],
[180, 209, 0],
[177, 211, 0],
[174, 214, 0],
[170, 216, 0],
[167, 218, 0],
[163, 220, 0],
[159, 222, 0],
[155, 225, 0],
[151, 227, 0],
[147, 229, 0],
[142, 231, 0],
[137, 233, 10],
[132, 235, 24],
[127, 237, 34],
[121, 239, 42],
[116, 241, 50],
[109, 243, 56],
[103, 245, 63],
[ 95, 247, 69],
[ 88, 249, 75],
[ 79, 251, 80],
[ 69, 253, 86],
[ 57, 255, 91],
[ 42, 255, 97],
[ 18, 255, 102],
[ 0, 255, 107],
[ 0, 255, 113],
[ 0, 255, 118],
[ 0, 255, 123],
[ 0, 255, 129],
[ 0, 255, 134],
[ 0, 255, 139],
[ 0, 255, 144],
[ 0, 255, 149],
[ 0, 255, 155],
[ 0, 255, 160],
[ 0, 255, 165],
[ 0, 255, 170],
[ 0, 255, 175],
[ 0, 255, 180],
[ 0, 255, 185],
[ 0, 255, 190],
[ 0, 255, 195],
[ 0, 255, 200],
[ 0, 255, 205],
[ 0, 255, 210],
[ 0, 255, 214],
[ 0, 255, 219],
[ 0, 255, 223],
[ 0, 255, 228],
[ 0, 255, 232],
[ 0, 255, 237],
[ 0, 255, 241],
[ 0, 255, 245],
[ 0, 255, 249],
[ 0, 255, 253],
[ 0, 255, 255],
[ 0, 255, 255],
[ 21, 255, 255],
[ 50, 255, 255],
[ 68, 255, 255],
[ 82, 255, 255],
[ 95, 255, 255],
[106, 255, 255],
[116, 255, 255],
[126, 255, 255],
[135, 255, 255],
[143, 255, 255],
[151, 255, 255],
[159, 255, 255],
[166, 255, 255],
[173, 255, 255],
[180, 255, 255],
[187, 255, 255],
[193, 255, 255],
[199, 255, 255],
[205, 255, 255],
[211, 255, 255],
[216, 255, 255],
[222, 255, 255],
[227, 255, 255],
[232, 255, 255],
[237, 255, 255],
[241, 255, 255],
[246, 255, 255],
[250, 255, 255],
[254, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 253],
];

303
spectro/run.py Normal file
View File

@ -0,0 +1,303 @@
colorscheme = [
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 77, 77],
[ 77, 78, 77],
[ 77, 78, 78],
[ 77, 78, 78],
[ 77, 79, 79],
[ 78, 80, 79],
[ 78, 80, 80],
[ 78, 81, 81],
[ 78, 82, 82],
[ 78, 83, 83],
[ 78, 84, 84],
[ 78, 85, 85],
[ 78, 86, 86],
[ 78, 87, 87],
[ 78, 88, 88],
[ 78, 89, 89],
[ 78, 90, 91],
[ 78, 91, 92],
[ 78, 91, 93],
[ 78, 92, 94],
[ 78, 93, 96],
[ 77, 94, 97],
[ 77, 95, 98],
[ 77, 96, 100],
[ 77, 97, 101],
[ 76, 98, 103],
[ 76, 99, 104],
[ 76, 100, 105],
[ 75, 100, 107],
[ 75, 101, 109],
[ 75, 102, 110],
[ 74, 103, 112],
[ 74, 104, 113],
[ 74, 105, 115],
[ 73, 105, 117],
[ 73, 106, 118],
[ 72, 107, 120],
[ 72, 108, 122],
[ 72, 108, 124],
[ 72, 109, 126],
[ 72, 110, 127],
[ 72, 110, 129],
[ 72, 111, 131],
[ 72, 112, 133],
[ 72, 112, 135],
[ 72, 113, 137],
[ 73, 113, 139],
[ 74, 114, 141],
[ 75, 115, 143],
[ 76, 115, 146],
[ 77, 116, 148],
[ 79, 116, 150],
[ 80, 116, 152],
[ 82, 117, 154],
[ 85, 117, 156],
[ 87, 117, 158],
[ 90, 118, 161],
[ 92, 118, 163],
[ 94, 118, 165],
[ 96, 118, 167],
[ 98, 119, 169],
[101, 119, 171],
[103, 119, 173],
[105, 119, 176],
[107, 119, 178],
[109, 119, 180],
[111, 119, 182],
[113, 119, 184],
[116, 119, 186],
[118, 119, 188],
[120, 118, 190],
[122, 118, 192],
[124, 118, 193],
[126, 118, 195],
[128, 118, 197],
[131, 117, 199],
[133, 117, 200],
[135, 117, 202],
[137, 116, 203],
[139, 116, 205],
[141, 115, 206],
[143, 115, 208],
[146, 115, 209],
[148, 114, 210],
[150, 114, 211],
[152, 113, 212],
[154, 113, 213],
[156, 112, 214],
[158, 112, 215],
[160, 111, 216],
[162, 111, 217],
[164, 110, 217],
[166, 110, 218],
[168, 110, 219],
[170, 109, 219],
[172, 109, 219],
[174, 108, 220],
[176, 108, 220],
[178, 107, 220],
[180, 107, 220],
[182, 107, 220],
[184, 106, 220],
[186, 106, 220],
[187, 106, 220],
[189, 106, 220],
[191, 106, 219],
[193, 105, 219],
[195, 105, 219],
[197, 105, 218],
[198, 105, 218],
[200, 105, 217],
[202, 105, 216],
[204, 105, 216],
[205, 106, 215],
[207, 106, 214],
[209, 106, 213],
[210, 106, 212],
[212, 107, 211],
[214, 107, 210],
[215, 108, 209],
[217, 108, 208],
[219, 108, 207],
[220, 109, 206],
[222, 110, 205],
[223, 110, 203],
[225, 111, 202],
[226, 111, 201],
[228, 112, 200],
[229, 113, 198],
[231, 114, 197],
[232, 114, 195],
[234, 115, 194],
[235, 116, 193],
[236, 117, 191],
[238, 118, 190],
[239, 119, 188],
[240, 120, 187],
[242, 120, 185],
[243, 121, 183],
[244, 122, 182],
[246, 123, 180],
[247, 124, 179],
[248, 126, 177],
[249, 127, 176],
[250, 128, 174],
[252, 129, 173],
[253, 130, 171],
[254, 131, 169],
[255, 132, 168],
[255, 133, 166],
[255, 134, 165],
[255, 136, 163],
[255, 137, 162],
[255, 138, 160],
[255, 139, 158],
[255, 140, 157],
[255, 142, 155],
[255, 143, 154],
[255, 144, 152],
[255, 145, 150],
[255, 146, 149],
[255, 148, 147],
[255, 149, 146],
[255, 150, 144],
[255, 152, 143],
[255, 153, 141],
[255, 154, 139],
[255, 156, 138],
[255, 157, 136],
[255, 158, 135],
[255, 160, 133],
[255, 161, 131],
[255, 163, 130],
[255, 164, 128],
[255, 166, 127],
[255, 167, 125],
[255, 169, 124],
[255, 170, 122],
[255, 172, 120],
[255, 173, 119],
[255, 175, 117],
[255, 177, 116],
[255, 178, 115],
[255, 180, 113],
[255, 182, 112],
[255, 183, 111],
[255, 185, 109],
[255, 187, 108],
[255, 189, 107],
[255, 190, 107],
[255, 192, 106],
[255, 194, 105],
[255, 196, 105],
[255, 198, 105],
[255, 200, 105],
[255, 201, 105],
[255, 203, 105],
[255, 205, 106],
[255, 207, 107],
[255, 209, 108],
[255, 211, 109],
[255, 213, 111],
[255, 215, 113],
[255, 217, 115],
[255, 218, 117],
[255, 220, 119],
[255, 222, 121],
[255, 224, 124],
[255, 226, 127],
[255, 228, 129],
[255, 230, 132],
[255, 232, 135],
[255, 233, 138],
[255, 235, 142],
[255, 237, 145],
[255, 239, 148],
[255, 240, 152],
[255, 242, 155],
[255, 244, 159],
[255, 245, 163],
[255, 247, 166],
[255, 248, 170],
[255, 250, 174],
[255, 251, 178],
[255, 253, 182],
[255, 254, 186],
[255, 255, 190],
[255, 255, 194],
[255, 255, 198],
[255, 255, 202],
[255, 255, 207],
[255, 255, 211],
[255, 255, 215],
[255, 255, 219],
[255, 255, 223],
[255, 255, 227],
[255, 255, 230],
[255, 255, 234],
[255, 255, 238],
[255, 255, 242],
[255, 255, 245],
[255, 255, 249],
[255, 255, 252],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
];
from PIL import Image
import numpy as np
import wave, math
import os, pickle
cols = np.array(colorscheme)
def nearest_color(x):
return np.argmin(np.linalg.norm(x - cols, axis=1))
im = np.array(Image.open("1200.png"))[:-1, ..., :3]
if not os.path.exists("save.pkl"):
spectro = np.apply_along_axis(nearest_color, -1, im)
pickle.dump(spectro, open("save.pkl", "wb"))
else:
spectro = pickle.load(open("save.pkl", "rb"))
print(spectro)
samples = 48000
actual_samples = samples * 4 # 4 seconds of audio time
print(im.shape)
spectro = spectro.transpose() # axis 0 is horizontal in image and axis 1 is vertical, probably
spectro = spectro.astype(np.float)
spectro -= spectro.min()
out = np.zeros(actual_samples)
ω = np.linspace(0, 10_000, num=im.shape[0]) * 2 * math.pi
for i in range(actual_samples):
index = i / actual_samples * (im.shape[1] - 1)
p, n = math.floor(index), math.ceil(index)
d = index - p
spec = spectro[p] * (1-d) + spectro[n] * d
θ = ω * (i / samples)
a = np.sin(θ * spec)
out[i] = sum(a)
if i % 1000 == 0: print(i)
print(out)
out /= max(out)
out *= 16384
print(out)
data = out.astype("<i2")
with wave.open("out.wav", "wb") as w:
w.setsampwidth(2)
w.setnchannels(1)
w.setframerate(samples)
w.writeframes(data.tobytes())

43
yearbox.html Normal file
View File

@ -0,0 +1,43 @@
<div id="table"></div>
<script>
const year = 2023
const months = [31, 28 + (year % 4 === 0 ? (year % 100 === 0 ? (year % 400 === 0 ? 1 : 0) : 1) : 0), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
const weekdays = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
const monthShortnames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nev", "Dec"]
const shiftWeekday = x => {
if (x === 0) x = 7
x -= 1
return x
}
const getWeekday = (y, m, d) => shiftWeekday(new Date(Date.parse(`${y}-${m}-${d}`)).getUTCDay())
let cols = 0
for (let month = 1; month <= 12; month++) {
const len = months[month - 1]
const start = getWeekday(year, month, 1)
cols = Math.max(cols, len + start)
}
let html = `<table>`
html += `<tr><th>${year}</th>`
for (let i = 0; i < cols; i++) {
html += `<th>${weekdays[i % 7]}</th>`
}
html += `</tr>`
for (let month = 1; month <= 12; month++) {
html += `<tr><th>${monthShortnames[month - 1]}</th>`
let start = getWeekday(year, month, 1)
for (let i = 0; i < start; i++) {
html += `<td></td>`
}
let mlen = months[month - 1]
for (let i = 0; i < mlen; i++) {
html += `<td>${(i + 1).toString().padStart(2, "0")}</td>`
}
for (let i = 0; i < cols - mlen - start; i++) {
html += `<td></td>`
}
html += `</tr>`
}
html += `</table>`
window.table.innerHTML = html
</script>