<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-CA">
	<id>http://wiki.maxthomaslang.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mxl</id>
	<title>max.wiki - User contributions [en-ca]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.maxthomaslang.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mxl"/>
	<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w/Special:Contributions/Mxl"/>
	<updated>2026-06-02T00:43:38Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.42.4</generator>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Snapcast_Server_Setup&amp;diff=150</id>
		<title>Snapcast Server Setup</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Snapcast_Server_Setup&amp;diff=150"/>
		<updated>2026-02-22T14:57:20Z</updated>

		<summary type="html">&lt;p&gt;Mxl: status -&amp;gt; restart&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Notes for &#039;&#039;&#039;setting up Snapcast&#039;&#039;&#039; (https://github.com/badaix/snapcast) for multi-device audio playback.&lt;br /&gt;
&lt;br /&gt;
== PipeWire as Player ==&lt;br /&gt;
&lt;br /&gt;
To configure PipeWire as a player for Snapcast, create the file &amp;lt;code&amp;gt;10-snapcast-pipe-sink.conf&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt; (or in &amp;lt;code&amp;gt;~/.config/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;context.modules = [&lt;br /&gt;
    {&lt;br /&gt;
        name = libpipewire-module-pipe-tunnel&lt;br /&gt;
        args = {&lt;br /&gt;
            tunnel.mode = sink&lt;br /&gt;
            pipe.filename = &amp;quot;/tmp/snapfifo&amp;quot;&lt;br /&gt;
            audio.format = S16LE&lt;br /&gt;
            audio.rate = 48000&lt;br /&gt;
            audio.channels = 2&lt;br /&gt;
            audio.position = [ FL FR ]&lt;br /&gt;
            stream.props = {&lt;br /&gt;
                node.name = Snapcast&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, restart PipeWire: &amp;lt;code&amp;gt;systemctl --user restart wireplumber pipewire pipewire-pulse&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
* PipeWire wiki on virtual devices: https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Virtual-Devices#pipe-devices&lt;br /&gt;
* PipeWire documentation on pipe tunnel module: https://docs.pipewire.org/page_module_pipe_tunnel.html&lt;br /&gt;
* Snapcast docs on player setup: https://github.com/badaix/snapcast/blob/develop/doc/player_setup.md#pulseaudio&lt;br /&gt;
&lt;br /&gt;
[[Category:System Configuration]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=149</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=149"/>
		<updated>2026-02-22T14:50:50Z</updated>

		<summary type="html">&lt;p&gt;Mxl: fix link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:TransTUM.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]] and others&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[File:GE_Rapid_Clean_II_Name.svg|x20px|frameless|link=GE Rapid Clean II (seven-segment display font)]]: font from an oven&#039;s seven-segment display&lt;br /&gt;
** [[File:Exzellenz.svg|x16px|frameless|link=Exzellenz (Font)]]: an especially excellent font&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Configuring mDNS for Easy Access to Devices]]&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;br /&gt;
** [[Snapcast Server Setup]]&lt;br /&gt;
* [[:Category:Recipes|Recipes]]&lt;br /&gt;
** [[Apfelkuchen Rezept von Oma]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Exzellenz_(Font)&amp;diff=148</id>
		<title>Exzellenz (Font)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Exzellenz_(Font)&amp;diff=148"/>
		<updated>2026-02-20T11:48:54Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Exzellenz Proof.svg|thumb|right|alt=Preview of font showing text &amp;quot;EXZELLENZ EXCELLENT&amp;quot;.|Preview of Exzellenz font]]&lt;br /&gt;
&lt;br /&gt;
An especially &#039;&#039;&#039;excellent font&#039;&#039;&#039;. Completely and wholly unrelated to the logo of the Technical University of Munich.&lt;br /&gt;
&lt;br /&gt;
The font is available on GitHub at [https://github.com/just-max/exzellenz github.com/just-max/exzellenz]. I also put together a web page that generates PNG/SVG proofs here: [https://just-max.github.io/exzellenz/generator.html?text=Hello%20World just-max.github.io/exzellenz/generator.html].&lt;br /&gt;
&lt;br /&gt;
[[Category:Fonts]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=147</id>
		<title>Configuring mDNS for Easy Access to Devices</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=147"/>
		<updated>2026-02-20T11:48:30Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.&lt;br /&gt;
&lt;br /&gt;
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, and the host with the given name (typically the device&#039;s hostname) answers it (also multicast, so all devices can update their caches). My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I&#039;ve collected the steps I took to get it working more reliably. The irony of having to configure Zeroconf is not lost on me.&lt;br /&gt;
&lt;br /&gt;
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.&lt;br /&gt;
&lt;br /&gt;
== Analyzing the Network ==&lt;br /&gt;
&lt;br /&gt;
To get a more or less complete list of DNS-SD services on the network, use the command &amp;lt;code&amp;gt;avahi-browse --resolve --terminate --all&amp;lt;/code&amp;gt;. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.&lt;br /&gt;
&lt;br /&gt;
To use mDNS to resolve a hostname, use &amp;lt;code&amp;gt;avahi-resolve-host-name &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass &amp;lt;code&amp;gt;-4&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-6&amp;lt;/code&amp;gt;. Alternatively, an purpose-built app that bundles the binary would work. Either approach would of course conflict with the built-in mdnsd if it ever starts, as both listen on port 5353.&lt;br /&gt;
&lt;br /&gt;
== Becoming Discoverable ==&lt;br /&gt;
&lt;br /&gt;
While some devices are easily made accessible to mDNS queries, some need more work.&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a &amp;quot;workstation&amp;quot; service, effectively advertising only that the host exists, a change is required in the config file (probably &amp;lt;code&amp;gt;/etc/avahi/avahi-daemon.conf&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 publish-workstation=yes&lt;br /&gt;
&lt;br /&gt;
This [https://superuser.com/a/1311455 supposedly used to be the default] in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!&lt;br /&gt;
&lt;br /&gt;
Restart the Daemon afterwards with &amp;lt;code&amp;gt;sudo systemctl restart avahi-daemon.service&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Support for responding to mDNS queries on Android devices [https://android.stackexchange.com/a/242004 is poor]. A daemon &amp;lt;code&amp;gt;mdnsd&amp;lt;/code&amp;gt; exists, but the address is hard-coded to &amp;lt;code&amp;gt;Android.local&amp;lt;/code&amp;gt; and the daemon only starts if an app registers some service.&lt;br /&gt;
&lt;br /&gt;
One idea [https://stackoverflow.com/a/70343905 is to patch mdnsd] to make the advertized hostname configurable, then run the binary at startup with Termux:Boot. But given the general direction of Android, the chance that this stops working in a future update [https://github.com/termux/termux-app/issues/2155 is always high].&lt;br /&gt;
&lt;br /&gt;
== Discovering Others ==&lt;br /&gt;
&lt;br /&gt;
=== Linux (Avahi/NSS) ===&lt;br /&gt;
&lt;br /&gt;
Resolution of mDNS addresses on Linux is provided by Avahi via a plugin to the Nameserver Switch (NSS). Multicast DNS is distinct from plain DNS, and is thus enabled by its own entry under &amp;lt;code&amp;gt;hosts:&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. On my Fedora system, this contains &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which means mDNS is consulted over IPv4. Most applications will consult the NSS (via glibc) and thus be able to resolve &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; addresses.&lt;br /&gt;
&lt;br /&gt;
As a matter of principle, I would also like mDNS over IPv6. This can be accomplished by changing &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which tries both IPv6 and IPv4.&lt;br /&gt;
&lt;br /&gt;
On my system, &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt; is managed by authselect (&amp;lt;code&amp;gt;man(8) authselect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;man(5) authselect-profiles&amp;lt;/code&amp;gt;). From &amp;lt;code&amp;gt;authselect current&amp;lt;/code&amp;gt;, the current profile is called &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, while &amp;lt;code&amp;gt;authselect list-features local&amp;lt;/code&amp;gt; lists the available feature &amp;lt;code&amp;gt;with-mdns6&amp;lt;/code&amp;gt;. Enabling the feature with &amp;lt;code&amp;gt;authselect enable-feature with-mdns6&amp;lt;/code&amp;gt; makes the appropriate change to &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To check that the change worked, we should find some host that advertises an IPv6 address ([[#Analyzing the Network]]). Then we can use &amp;lt;code&amp;gt;getent ahosts &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, which should return that IPv6 addresses. Unlike the Avahi browse/resolve tools, &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; consults the NSS and should thus be indicative of what a normal application sees when making DNS queries. &lt;br /&gt;
&lt;br /&gt;
==== Caveat on IPv6 Support ====&lt;br /&gt;
&lt;br /&gt;
Consulting &amp;lt;code&amp;gt;man(5) nsswitch.conf&amp;lt;/code&amp;gt;, additional services are implemented as shared libraries named &amp;lt;code&amp;gt;libnss_SERVICE.so.X&amp;lt;/code&amp;gt;. Looking in the appropriate places (&amp;lt;code&amp;gt;man(8) ldconfig&amp;lt;/code&amp;gt;), we can list available services:&lt;br /&gt;
&lt;br /&gt;
 find /lib/ /lib64/ /usr/lib/ /usr/lib64/ -name &#039;libnss_*.so.*&#039; -print0&lt;br /&gt;
 | xargs --null basename --multiple | sort | uniq&lt;br /&gt;
&lt;br /&gt;
Among other things, this finds the library &amp;lt;code&amp;gt;/lib64/libnss_mdns4.so.2&amp;lt;/code&amp;gt; which on my system is provided by the package [https://github.com/avahi/nss-mdns nss-mdns]. From the README, IPv6 support is as simple as changing &amp;lt;code&amp;gt;mdns4_minimal&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, it comes with the following warning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Due to the fact that most mDNS responders only register local IPv4 addresses via mDNS, most people will want to use &amp;lt;code&amp;gt;libnss_mdns4.so.2&amp;lt;/code&amp;gt; exclusively. Using &amp;lt;code&amp;gt;libnss_mdns.so.2&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;libnss_mdns6.so.2&amp;lt;/code&amp;gt; in such a situation causes long timeouts when resolving hosts since most modern Unix/Linux applications check for IPv6 addresses first, followed by a lookup for IPv4.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indeed, the only hosts advertising an IPv6 address on my home network are my Linux laptop and my Linux server. However, by using the &amp;lt;code&amp;gt;libnss_mdns_minimal&amp;lt;/code&amp;gt; libraries, non-mDNS addresses will by immediately rejected, so that the timeout won&#039;t affect us when not resolving mDNS addresses.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Unlike the poor support for &#039;&#039;responding&#039;&#039; to queries, Android [https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution supports resolving mDNS addresses directly] as of November 2021. This should be transparent to applications, as it is built in to &amp;lt;code&amp;gt;getaddrinfo()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Category:System_Configuration]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=146</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=146"/>
		<updated>2026-02-20T11:47:45Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:TransTUM.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]] and others&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[File:GE_Rapid_Clean_II_Name.svg|x20px|frameless|link=GE Rapid Clean II]]: font from an oven&#039;s seven-segment display&lt;br /&gt;
** [[File:Exzellenz.svg|x16px|frameless|link=Exzellenz (Font)]]: an especially excellent font&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Configuring mDNS for Easy Access to Devices]]&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;br /&gt;
** [[Snapcast Server Setup]]&lt;br /&gt;
* [[:Category:Recipes|Recipes]]&lt;br /&gt;
** [[Apfelkuchen Rezept von Oma]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:GE_Rapid_Clean_II_Name.svg&amp;diff=145</id>
		<title>File:GE Rapid Clean II Name.svg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:GE_Rapid_Clean_II_Name.svg&amp;diff=145"/>
		<updated>2026-02-20T11:45:41Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:Exzellenz.svg&amp;diff=144</id>
		<title>File:Exzellenz.svg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:Exzellenz.svg&amp;diff=144"/>
		<updated>2026-02-20T11:34:51Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Mxl uploaded a new version of File:Exzellenz.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:Exzellenz.svg&amp;diff=143</id>
		<title>File:Exzellenz.svg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:Exzellenz.svg&amp;diff=143"/>
		<updated>2026-02-20T11:27:59Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Exzellenz_(Font)&amp;diff=142</id>
		<title>Exzellenz (Font)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Exzellenz_(Font)&amp;diff=142"/>
		<updated>2026-02-20T11:25:36Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;Preview of Exzellenz font  An especially &amp;#039;&amp;#039;&amp;#039;excellent font&amp;#039;&amp;#039;&amp;#039;. Completely and wholly unrelated to the logo of the Technical University of Munich.  The font is available on GitHub at [https://github.com/just-max/exzellenz github.com/just-max/exzellenz]. I also put together a web page that generates PNG/SVG proofs here: [https://just-max.github.io/exzellenz/generator.html?text=...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Exzellenz Proof.svg|thumb|right|alt=Preview of font showing text &amp;quot;EXZELLENZ EXCELLENT&amp;quot;.|Preview of Exzellenz font]]&lt;br /&gt;
&lt;br /&gt;
An especially &#039;&#039;&#039;excellent font&#039;&#039;&#039;. Completely and wholly unrelated to the logo of the Technical University of Munich.&lt;br /&gt;
&lt;br /&gt;
The font is available on GitHub at [https://github.com/just-max/exzellenz github.com/just-max/exzellenz]. I also put together a web page that generates PNG/SVG proofs here: [https://just-max.github.io/exzellenz/generator.html?text=Hello%20World just-max.github.io/exzellenz/generator.html].&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:Exzellenz_Proof.svg&amp;diff=141</id>
		<title>File:Exzellenz Proof.svg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:Exzellenz_Proof.svg&amp;diff=141"/>
		<updated>2026-02-20T11:17:18Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=140</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=140"/>
		<updated>2026-02-20T09:58:54Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:TransTUM.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]] and others&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[GE Rapid Clean II (seven-segment display font)|GE Rapid Clean II]], font from an oven&#039;s seven-segment display&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Configuring mDNS for Easy Access to Devices]]&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;br /&gt;
** [[Snapcast Server Setup]]&lt;br /&gt;
* [[:Category:Recipes|Recipes]]&lt;br /&gt;
** [[Apfelkuchen Rezept von Oma]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=139</id>
		<title>Configuring mDNS for Easy Access to Devices</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=139"/>
		<updated>2026-02-20T09:58:15Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.&lt;br /&gt;
&lt;br /&gt;
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, and the host with the given name (typically the device&#039;s hostname) answers it (also multicast, so all devices can update their caches). My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I&#039;ve collected the steps I took to get it working more reliably. The irony of having to configure Zeroconf is not lost on me.&lt;br /&gt;
&lt;br /&gt;
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.&lt;br /&gt;
&lt;br /&gt;
== Analyzing the Network ==&lt;br /&gt;
&lt;br /&gt;
To get a more or less complete list of DNS-SD services on the network, use the command &amp;lt;code&amp;gt;avahi-browse --resolve --terminate --all&amp;lt;/code&amp;gt;. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.&lt;br /&gt;
&lt;br /&gt;
To use mDNS to resolve a hostname, use &amp;lt;code&amp;gt;avahi-resolve-host-name &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass &amp;lt;code&amp;gt;-4&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-6&amp;lt;/code&amp;gt;. Alternatively, an purpose-built app that bundles the binary would work. Either approach would of course conflict with the built-in mdnsd if it ever starts, as both listen on port 5353.&lt;br /&gt;
&lt;br /&gt;
== Becoming Discoverable ==&lt;br /&gt;
&lt;br /&gt;
While some devices are easily made accessible to mDNS queries, some need more work.&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a &amp;quot;workstation&amp;quot; service, effectively advertising only that the host exists, a change is required in the config file (probably &amp;lt;code&amp;gt;/etc/avahi/avahi-daemon.conf&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 publish-workstation=yes&lt;br /&gt;
&lt;br /&gt;
This [https://superuser.com/a/1311455 supposedly used to be the default] in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!&lt;br /&gt;
&lt;br /&gt;
Restart the Daemon afterwards with &amp;lt;code&amp;gt;sudo systemctl restart avahi-daemon.service&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Support for responding to mDNS queries on Android devices [https://android.stackexchange.com/a/242004 is poor]. A daemon &amp;lt;code&amp;gt;mdnsd&amp;lt;/code&amp;gt; exists, but the address is hard-coded to &amp;lt;code&amp;gt;Android.local&amp;lt;/code&amp;gt; and the daemon only starts if an app registers some service.&lt;br /&gt;
&lt;br /&gt;
One idea [https://stackoverflow.com/a/70343905 is to patch mdnsd] to make the advertized hostname configurable, then run the binary at startup with Termux:Boot. But given the general direction of Android, the chance that this stops working in a future update [https://github.com/termux/termux-app/issues/2155 is always high].&lt;br /&gt;
&lt;br /&gt;
== Discovering Others ==&lt;br /&gt;
&lt;br /&gt;
=== Linux (Avahi/NSS) ===&lt;br /&gt;
&lt;br /&gt;
Resolution of mDNS addresses on Linux is provided by Avahi via a plugin to the Nameserver Switch (NSS). Multicast DNS is distinct from plain DNS, and is thus enabled by its own entry under &amp;lt;code&amp;gt;hosts:&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. On my Fedora system, this contains &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which means mDNS is consulted over IPv4. Most applications will consult the NSS (via glibc) and thus be able to resolve &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; addresses.&lt;br /&gt;
&lt;br /&gt;
As a matter of principle, I would also like mDNS over IPv6. This can be accomplished by changing &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which tries both IPv6 and IPv4.&lt;br /&gt;
&lt;br /&gt;
On my system, &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt; is managed by authselect (&amp;lt;code&amp;gt;man(8) authselect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;man(5) authselect-profiles&amp;lt;/code&amp;gt;). From &amp;lt;code&amp;gt;authselect current&amp;lt;/code&amp;gt;, the current profile is called &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, while &amp;lt;code&amp;gt;authselect list-features local&amp;lt;/code&amp;gt; lists the available feature &amp;lt;code&amp;gt;with-mdns6&amp;lt;/code&amp;gt;. Enabling the feature with &amp;lt;code&amp;gt;authselect enable-feature with-mdns6&amp;lt;/code&amp;gt; makes the appropriate change to &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To check that the change worked, we should find some host that advertises an IPv6 address ([[#Analyzing the Network]]). Then we can use &amp;lt;code&amp;gt;getent ahosts &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, which should return that IPv6 addresses. Unlike the Avahi browse/resolve tools, &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; consults the NSS and should thus be indicative of what a normal application sees when making DNS queries. &lt;br /&gt;
&lt;br /&gt;
==== Caveat on IPv6 Support ====&lt;br /&gt;
&lt;br /&gt;
Consulting &amp;lt;code&amp;gt;man(5) nsswitch.conf&amp;lt;/code&amp;gt;, additional services are implemented as shared libraries named &amp;lt;code&amp;gt;libnss_SERVICE.so.X&amp;lt;/code&amp;gt;. Looking in the appropriate places (&amp;lt;code&amp;gt;man(8) ldconfig&amp;lt;/code&amp;gt;), we can list available services:&lt;br /&gt;
&lt;br /&gt;
 find /lib/ /lib64/ /usr/lib/ /usr/lib64/ -name &#039;libnss_*.so.*&#039; -print0&lt;br /&gt;
 | xargs --null basename --multiple | sort | uniq&lt;br /&gt;
&lt;br /&gt;
Among other things, this finds the library &amp;lt;code&amp;gt;/lib64/libnss_mdns4.so.2&amp;lt;/code&amp;gt; which on my system is provided by the package [https://github.com/avahi/nss-mdns nss-mdns]. From the README, IPv6 support is as simple as changing &amp;lt;code&amp;gt;mdns4_minimal&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, it comes with the following warning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Due to the fact that most mDNS responders only register local IPv4 addresses via mDNS, most people will want to use &amp;lt;code&amp;gt;libnss_mdns4.so.2&amp;lt;/code&amp;gt; exclusively. Using &amp;lt;code&amp;gt;libnss_mdns.so.2&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;libnss_mdns6.so.2&amp;lt;/code&amp;gt; in such a situation causes long timeouts when resolving hosts since most modern Unix/Linux applications check for IPv6 addresses first, followed by a lookup for IPv4.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indeed, the only hosts advertising an IPv6 address on my home network are my Linux laptop and my Linux server. However, by using the &amp;lt;code&amp;gt;libnss_mdns_minimal&amp;lt;/code&amp;gt; libraries, non-mDNS addresses will by immediately rejected, so that the timeout won&#039;t affect us when not resolving mDNS addresses.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Unlike the poor support for &#039;&#039;responding&#039;&#039; to queries, Android [https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution supports resolving mDNS addresses directly] as of November 2021. This should be transparent to applications, as it is built in to &amp;lt;code&amp;gt;getaddrinfo()&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=138</id>
		<title>Configuring mDNS for Easy Access to Devices</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=138"/>
		<updated>2026-02-20T09:56:22Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.&lt;br /&gt;
&lt;br /&gt;
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, and the host with the given name (typically the device&#039;s hostname) answers it (also multicast, so all devices can update their caches). My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I&#039;ve collected the steps I took to get it working more reliably.&lt;br /&gt;
&lt;br /&gt;
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.&lt;br /&gt;
&lt;br /&gt;
== Analyzing the Network ==&lt;br /&gt;
&lt;br /&gt;
To get a more or less complete list of DNS-SD services on the network, use the command &amp;lt;code&amp;gt;avahi-browse --resolve --terminate --all&amp;lt;/code&amp;gt;. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.&lt;br /&gt;
&lt;br /&gt;
To use mDNS to resolve a hostname, use &amp;lt;code&amp;gt;avahi-resolve-host-name &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass &amp;lt;code&amp;gt;-4&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-6&amp;lt;/code&amp;gt;. Alternatively, an purpose-built app that bundles the binary would work. Either approach would of course conflict with the built-in mdnsd if it ever starts, as both listen on port 5353.&lt;br /&gt;
&lt;br /&gt;
== Becoming Discoverable ==&lt;br /&gt;
&lt;br /&gt;
While some devices are easily made accessible to mDNS queries, some need more work.&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a &amp;quot;workstation&amp;quot; service, effectively advertising only that the host exists, a change is required in the config file (probably &amp;lt;code&amp;gt;/etc/avahi/avahi-daemon.conf&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 publish-workstation=yes&lt;br /&gt;
&lt;br /&gt;
This [https://superuser.com/a/1311455 supposedly used to be the default] in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!&lt;br /&gt;
&lt;br /&gt;
Restart the Daemon afterwards with &amp;lt;code&amp;gt;sudo systemctl restart avahi-daemon.service&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Support for responding to mDNS queries on Android devices [https://android.stackexchange.com/a/242004 is poor]. A daemon &amp;lt;code&amp;gt;mdnsd&amp;lt;/code&amp;gt; exists, but the address is hard-coded to &amp;lt;code&amp;gt;Android.local&amp;lt;/code&amp;gt; and the daemon only starts if an app registers some service.&lt;br /&gt;
&lt;br /&gt;
One idea [https://stackoverflow.com/a/70343905 is to patch mdnsd] to make the advertized hostname configurable, then run the binary at startup with Termux:Boot. But given the general direction of Android, the chance that this stops working in a future update [https://github.com/termux/termux-app/issues/2155 is always high].&lt;br /&gt;
&lt;br /&gt;
== Discovering Others ==&lt;br /&gt;
&lt;br /&gt;
=== Linux (Avahi/NSS) ===&lt;br /&gt;
&lt;br /&gt;
Resolution of mDNS addresses on Linux is provided by Avahi via a plugin to the Nameserver Switch (NSS). Multicast DNS is distinct from plain DNS, and is thus enabled by its own entry under &amp;lt;code&amp;gt;hosts:&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. On my Fedora system, this contains &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which means mDNS is consulted over IPv4. Most applications will consult the NSS (via glibc) and thus be able to resolve &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; addresses.&lt;br /&gt;
&lt;br /&gt;
As a matter of principle, I would also like mDNS over IPv6. This can be accomplished by changing &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which tries both IPv6 and IPv4.&lt;br /&gt;
&lt;br /&gt;
On my system, &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt; is managed by authselect (&amp;lt;code&amp;gt;man(8) authselect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;man(5) authselect-profiles&amp;lt;/code&amp;gt;). From &amp;lt;code&amp;gt;authselect current&amp;lt;/code&amp;gt;, the current profile is called &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, while &amp;lt;code&amp;gt;authselect list-features local&amp;lt;/code&amp;gt; lists the available feature &amp;lt;code&amp;gt;with-mdns6&amp;lt;/code&amp;gt;. Enabling the feature with &amp;lt;code&amp;gt;authselect enable-feature with-mdns6&amp;lt;/code&amp;gt; makes the appropriate change to &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To check that the change worked, we should find some host that advertises an IPv6 address ([[#Analyzing the Network]]). Then we can use &amp;lt;code&amp;gt;getent ahosts &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, which should return that IPv6 addresses. Unlike the Avahi browse/resolve tools, &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; consults the NSS and should thus be indicative of what a normal application sees when making DNS queries. &lt;br /&gt;
&lt;br /&gt;
==== Caveat on IPv6 Support ====&lt;br /&gt;
&lt;br /&gt;
Consulting &amp;lt;code&amp;gt;man(5) nsswitch.conf&amp;lt;/code&amp;gt;, additional services are implemented as shared libraries named &amp;lt;code&amp;gt;libnss_SERVICE.so.X&amp;lt;/code&amp;gt;. Looking in the appropriate places (&amp;lt;code&amp;gt;man(8) ldconfig&amp;lt;/code&amp;gt;), we can list available services:&lt;br /&gt;
&lt;br /&gt;
 find /lib/ /lib64/ /usr/lib/ /usr/lib64/ -name &#039;libnss_*.so.*&#039; -print0&lt;br /&gt;
 | xargs --null basename --multiple | sort | uniq&lt;br /&gt;
&lt;br /&gt;
Among other things, this finds the library &amp;lt;code&amp;gt;/lib64/libnss_mdns4.so.2&amp;lt;/code&amp;gt; which on my system is provided by the package [https://github.com/avahi/nss-mdns nss-mdns]. From the README, IPv6 support is as simple as changing &amp;lt;code&amp;gt;mdns4_minimal&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, it comes with the following warning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Due to the fact that most mDNS responders only register local IPv4 addresses via mDNS, most people will want to use &amp;lt;code&amp;gt;libnss_mdns4.so.2&amp;lt;/code&amp;gt; exclusively. Using &amp;lt;code&amp;gt;libnss_mdns.so.2&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;libnss_mdns6.so.2&amp;lt;/code&amp;gt; in such a situation causes long timeouts when resolving hosts since most modern Unix/Linux applications check for IPv6 addresses first, followed by a lookup for IPv4.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indeed, the only hosts advertising an IPv6 address on my home network are my Linux laptop and my Linux server. However, by using the &amp;lt;code&amp;gt;libnss_mdns_minimal&amp;lt;/code&amp;gt; libraries, non-mDNS addresses will by immediately rejected, so that the timeout won&#039;t affect us when not resolving mDNS addresses.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Unlike the poor support for &#039;&#039;responding&#039;&#039; to queries, Android [https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution supports resolving mDNS addresses directly] as of November 2021. This should be transparent to applications, as it is built in to &amp;lt;code&amp;gt;getaddrinfo()&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=137</id>
		<title>Configuring mDNS for Easy Access to Devices</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=137"/>
		<updated>2026-02-20T07:57:00Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.&lt;br /&gt;
&lt;br /&gt;
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, and the host with the given name (typically the device&#039;s hostname) answers it. My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I&#039;ve collected the steps I took to get it working more reliably.&lt;br /&gt;
&lt;br /&gt;
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.&lt;br /&gt;
&lt;br /&gt;
== Analyzing the Network ==&lt;br /&gt;
&lt;br /&gt;
To get a more or less complete list of DNS-SD services on the network, use the command &amp;lt;code&amp;gt;avahi-browse --resolve --terminate --all&amp;lt;/code&amp;gt;. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.&lt;br /&gt;
&lt;br /&gt;
To use mDNS to resolve a hostname, use &amp;lt;code&amp;gt;avahi-resolve-host-name &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass &amp;lt;code&amp;gt;-4&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-6&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Becoming Discoverable ==&lt;br /&gt;
&lt;br /&gt;
While some devices are easily made accessible to mDNS queries, some need more work.&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a &amp;quot;workstation&amp;quot; service, effectively advertising only that the host exists, a change is required in the config file (probably &amp;lt;code&amp;gt;/etc/avahi/avahi-daemon.conf&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 publish-workstation=yes&lt;br /&gt;
&lt;br /&gt;
This [https://superuser.com/a/1311455 supposedly used to be the default] in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!&lt;br /&gt;
&lt;br /&gt;
Restart the Daemon afterwards with &amp;lt;code&amp;gt;sudo systemctl restart avahi-daemon.service&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Support is poor: the address is hard-coded to &amp;lt;code&amp;gt;Android.local&amp;lt;/code&amp;gt; and the daemon only starts if some service is registered.&lt;br /&gt;
&lt;br /&gt;
== Discovering Others ==&lt;br /&gt;
&lt;br /&gt;
=== Linux (Avahi/NSS) ===&lt;br /&gt;
&lt;br /&gt;
Resolution of mDNS addresses on Linux is provided by Avahi via a plugin to the Nameserver Switch (NSS). Multicast DNS is distinct from plain DNS, and is thus enabled by its own entry under &amp;lt;code&amp;gt;hosts:&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. On my Fedora system, this contains &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which means mDNS is consulted over IPv4. Most applications will consult the NSS (via glibc) and thus be able to resolve &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; addresses.&lt;br /&gt;
&lt;br /&gt;
As a matter of principle, I would also like mDNS over IPv6. This can be accomplished by changing &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which tries both IPv6 and IPv4.&lt;br /&gt;
&lt;br /&gt;
On my system, &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt; is managed by authselect (&amp;lt;code&amp;gt;man(8) authselect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;man(5) authselect-profiles&amp;lt;/code&amp;gt;). From &amp;lt;code&amp;gt;authselect current&amp;lt;/code&amp;gt;, the current profile is called &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, while &amp;lt;code&amp;gt;authselect list-features local&amp;lt;/code&amp;gt; lists the available feature &amp;lt;code&amp;gt;with-mdns6&amp;lt;/code&amp;gt;. Enabling the feature with &amp;lt;code&amp;gt;authselect enable-feature with-mdns6&amp;lt;/code&amp;gt; makes the appropriate change to &amp;lt;code&amp;gt;nsswitch.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Caveat on IPv6 Support ====&lt;br /&gt;
&lt;br /&gt;
Consulting &amp;lt;code&amp;gt;man(5) nsswitch.conf&amp;lt;/code&amp;gt;, additional services are implemented as shared libraries named &amp;lt;code&amp;gt;libnss_SERVICE.so.X&amp;lt;/code&amp;gt;. Looking in the appropriate places (&amp;lt;code&amp;gt;man(8) ldconfig&amp;lt;/code&amp;gt;), we can list available services:&lt;br /&gt;
&lt;br /&gt;
 find /lib/ /lib64/ /usr/lib/ /usr/lib64/ -name &#039;libnss_*.so.*&#039; -print0&lt;br /&gt;
 | xargs --null basename --multiple | sort | uniq&lt;br /&gt;
&lt;br /&gt;
Among other things, this finds the library &amp;lt;code&amp;gt;/lib64/libnss_mdns4.so.2&amp;lt;/code&amp;gt; which on my system is provided by the package [https://github.com/avahi/nss-mdns nss-mdns]. From the README, IPv6 support is as simple as changing &amp;lt;code&amp;gt;mdns4_minimal&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;mdns_minimal&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, it comes with the following warning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Due to the fact that most mDNS responders only register local IPv4 addresses via mDNS, most people will want to use &amp;lt;code&amp;gt;libnss_mdns4.so.2&amp;lt;/code&amp;gt; exclusively. Using &amp;lt;code&amp;gt;libnss_mdns.so.2&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;libnss_mdns6.so.2&amp;lt;/code&amp;gt; in such a situation causes long timeouts when resolving hosts since most modern Unix/Linux applications check for IPv6 addresses first, followed by a lookup for IPv4.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indeed, the only hosts advertising an IPv6 address on my home network are my Linux laptop and my Linux server. However, by using the &amp;lt;code&amp;gt;libnss_mdns_minimal&amp;lt;/code&amp;gt; libraries, non-mDNS addresses will by immediately rejected, so that the timeout won&#039;t affect us when not resolving mDNS addresses.&lt;br /&gt;
&lt;br /&gt;
== mDNS and Android ==&lt;br /&gt;
&lt;br /&gt;
As of November 2021, Android supports resolving mDNS addresses directly.&lt;br /&gt;
https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution&lt;br /&gt;
&lt;br /&gt;
https://old.reddit.com/r/synology/comments/sk8dzz/cannot_ping_mdns_address_servernamelocal_on/hvk0v5k/&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=136</id>
		<title>Configuring mDNS for Easy Access to Devices</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=136"/>
		<updated>2026-02-20T07:23:24Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.&lt;br /&gt;
&lt;br /&gt;
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, and the host with the given name (typically the device&#039;s hostname) answers it. My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I&#039;ve collected the steps I took to get it working more reliably.&lt;br /&gt;
&lt;br /&gt;
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.&lt;br /&gt;
&lt;br /&gt;
== Analyzing the Network ==&lt;br /&gt;
&lt;br /&gt;
To get a more or less complete list of DNS-SD services on the network, use the command &amp;lt;code&amp;gt;avahi-browse --resolve --terminate --all&amp;lt;/code&amp;gt;. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.&lt;br /&gt;
&lt;br /&gt;
To use mDNS to resolve a hostname, use &amp;lt;code&amp;gt;avahi-resolve-host-name &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass &amp;lt;code&amp;gt;-4&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-6&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Becoming Discoverable ==&lt;br /&gt;
&lt;br /&gt;
While some devices are easily made accessible to mDNS queries, some need more work.&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a &amp;quot;workstation&amp;quot; service, effectively advertising only that the host exists, a change is required in the config file (probably &amp;lt;code&amp;gt;/etc/avahi/avahi-daemon.conf&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 publish-workstation=yes&lt;br /&gt;
&lt;br /&gt;
This [https://superuser.com/a/1311455 supposedly used to be the default] in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!&lt;br /&gt;
&lt;br /&gt;
Restart the Daemon afterwards with &amp;lt;code&amp;gt;sudo systemctl restart avahi-daemon.service&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
Support is poor: the address is hard-coded to &amp;lt;code&amp;gt;Android.local&amp;lt;/code&amp;gt; and the daemon only starts if some service is registered.&lt;br /&gt;
&lt;br /&gt;
== Discovering Others ==&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
With Avahi installed, mDNS addresses can be resolved. Multicast DNS is separate from plain DNS, and will be used by typical applications if enabled by an appropriate entry under &amp;lt;code&amp;gt;hosts:&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. On my Fedora system, this contains &amp;lt;code&amp;gt;mdns4_minimal [NOTFOUND=return]&amp;lt;/code&amp;gt;, which means mDNS is consulted over IPv4.&lt;br /&gt;
&lt;br /&gt;
As a matter of principle, I would also like mDNS over IPv6 to run. Consulting &amp;lt;code&amp;gt;man(5) nsswitch.conf&amp;lt;/code&amp;gt;, additional services are implemented as shared libraries named &amp;lt;code&amp;gt;libnss_SERVICE.so.X&amp;lt;/code&amp;gt;. Looking in the appropriate places (&amp;lt;code&amp;gt;man(8) ldconfig&amp;lt;/code&amp;gt;), we can list available services:&lt;br /&gt;
&lt;br /&gt;
 find /lib/ /lib64/ /usr/lib/ /usr/lib64/ -name &#039;libnss_*.so.*&#039; -print0&lt;br /&gt;
 | xargs --null basename --multiple | sort | uniq&lt;br /&gt;
&lt;br /&gt;
== mDNS and Android ==&lt;br /&gt;
&lt;br /&gt;
As of November 2021, Android supports resolving mDNS addresses directly.&lt;br /&gt;
https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution&lt;br /&gt;
&lt;br /&gt;
https://old.reddit.com/r/synology/comments/sk8dzz/cannot_ping_mdns_address_servernamelocal_on/hvk0v5k/&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=135</id>
		<title>Configuring mDNS for Easy Access to Devices</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Configuring_mDNS_for_Easy_Access_to_Devices&amp;diff=135"/>
		<updated>2026-02-20T06:27:52Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://en.wikipedia.org/wiki/Zero-configuration_networking Zeroconf] consists of a number of related concepts with the common objective of making simple networked services &amp;quot;just work&amp;quot;. Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation [https://en.wikipedia.org/wiki/Avahi_(software) Avahi] is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.&lt;br /&gt;
&lt;br /&gt;
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;, and the host with the given name (typically the device&#039;s hostname) answers it. My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I&#039;ve collected the steps I took to get it working more reliably.&lt;br /&gt;
&lt;br /&gt;
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.&lt;br /&gt;
&lt;br /&gt;
== Analyzing the Network ==&lt;br /&gt;
&lt;br /&gt;
To get a more or less complete list of DNS-SD services on the network, use the command &amp;lt;code&amp;gt;avahi-browse --resolve --terminate --all&amp;lt;/code&amp;gt;. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.&lt;br /&gt;
&lt;br /&gt;
To use mDNS to resolve a hostname, use &amp;lt;code&amp;gt;avahi-resolve-host-name &amp;lt;name&amp;gt;.local&amp;lt;/code&amp;gt;. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass &amp;lt;code&amp;gt;-4&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-6&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Becoming Discoverable ==&lt;br /&gt;
&lt;br /&gt;
While some devices are easily accessible, some need more work.&lt;br /&gt;
&lt;br /&gt;
=== Avahi (Linux) ===&lt;br /&gt;
&lt;br /&gt;
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a &amp;quot;workstation&amp;quot; service, effectively advertising only that the host exists, a change is required in the config file (probably &amp;lt;code&amp;gt;/etc/avahi/avahi-daemon.conf&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 publish-workstation=yes&lt;br /&gt;
&lt;br /&gt;
This [https://superuser.com/a/1311455 supposedly used to be the default] in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!&lt;br /&gt;
&lt;br /&gt;
Restart the Daemon afterwards with &amp;lt;code&amp;gt;sudo systemctl restart avahi-daemon.service&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Android ===&lt;br /&gt;
&lt;br /&gt;
https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution&lt;br /&gt;
&lt;br /&gt;
https://old.reddit.com/r/synology/comments/sk8dzz/cannot_ping_mdns_address_servernamelocal_on/hvk0v5k/&lt;br /&gt;
&lt;br /&gt;
As of Android 12, there is support for mDNS.&lt;br /&gt;
&lt;br /&gt;
== mDNS and IPv6 ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== mDNS and Android ==&lt;br /&gt;
&lt;br /&gt;
As of Android 12, Android supports mDNS to some extent.&lt;br /&gt;
&lt;br /&gt;
== Sources ==&lt;br /&gt;
&lt;br /&gt;
https://askubuntu.com/a/1105895/254818&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Goopworld&amp;diff=134</id>
		<title>Goopworld</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Goopworld&amp;diff=134"/>
		<updated>2025-11-21T18:54:36Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;== Coordinates ==  * warden? 940, -31, -36&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Coordinates ==&lt;br /&gt;
&lt;br /&gt;
* warden? 940, -31, -36&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Snapcast_Server_Setup&amp;diff=133</id>
		<title>Snapcast Server Setup</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Snapcast_Server_Setup&amp;diff=133"/>
		<updated>2025-08-31T20:54:59Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Category&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Notes for &#039;&#039;&#039;setting up Snapcast&#039;&#039;&#039; (https://github.com/badaix/snapcast) for multi-device audio playback.&lt;br /&gt;
&lt;br /&gt;
== PipeWire as Player ==&lt;br /&gt;
&lt;br /&gt;
To configure PipeWire as a player for Snapcast, create the file &amp;lt;code&amp;gt;10-snapcast-pipe-sink.conf&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt; (or in &amp;lt;code&amp;gt;~/.config/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;context.modules = [&lt;br /&gt;
    {&lt;br /&gt;
        name = libpipewire-module-pipe-tunnel&lt;br /&gt;
        args = {&lt;br /&gt;
            tunnel.mode = sink&lt;br /&gt;
            pipe.filename = &amp;quot;/tmp/snapfifo&amp;quot;&lt;br /&gt;
            audio.format = S16LE&lt;br /&gt;
            audio.rate = 48000&lt;br /&gt;
            audio.channels = 2&lt;br /&gt;
            audio.position = [ FL FR ]&lt;br /&gt;
            stream.props = {&lt;br /&gt;
                node.name = Snapcast&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, restart PipeWire: &amp;lt;code&amp;gt;systemctl --user status wireplumber pipewire pipewire-pulse&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
* PipeWire wiki on virtual devices: https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Virtual-Devices#pipe-devices&lt;br /&gt;
* PipeWire documentation on pipe tunnel module: https://docs.pipewire.org/page_module_pipe_tunnel.html&lt;br /&gt;
* Snapcast docs on player setup: https://github.com/badaix/snapcast/blob/develop/doc/player_setup.md#pulseaudio&lt;br /&gt;
&lt;br /&gt;
[[Category:System Configuration]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=132</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=132"/>
		<updated>2025-08-11T11:57:50Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Progress tracking for master&#039;s thesis &#039;&#039;&#039;Translating Time Bounds of Functional Programs to a Deeply-Embedded Language&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;br /&gt;
&lt;br /&gt;
=== May 12th–26th ===&lt;br /&gt;
&lt;br /&gt;
Better rule for &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;. Gather running time into constraint for each terminal (tail-call or assignment to return register), &amp;quot;run&amp;quot; function, rearrange constraint to find lower bound on c, take the maximum of all lower bounds as suitable c.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
* Prove that the running time of compiled functions always has the assumed form. Use the definition of the compiler (Kappelmann et al.) and the definition of generated HOL running times.&lt;br /&gt;
* From here, argue why or why not an SMT solver could be used to solve the resulting constraints.&lt;br /&gt;
* Adapt the &amp;lt;code&amp;gt;running&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;gather&amp;lt;/code&amp;gt; method to (tail) recursive functions.&lt;br /&gt;
&lt;br /&gt;
=== May 26th–June 6th ===&lt;br /&gt;
&lt;br /&gt;
Formalized a semantics for HOL-TCNat, such that &amp;lt;code&amp;gt;T_f x1 ... xn = [[e]]_(x1, ..., xn) + 1&amp;lt;/code&amp;gt; for a function &amp;lt;code&amp;gt;f x1 ... xn = e&amp;lt;/code&amp;gt; and its generated running time function &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt;. The big-step predicate has the form: &amp;lt;code&amp;gt;(tail-call term :: HOL_TCNat, argument vector :: nat list) |- (term :: HOL_TCNat, let-bound variables :: Nat list) =&amp;gt;^(running time :: nat) (result :: nat)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
So we define:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(f,xs) |- (Number n,bs) =&amp;gt;^0 n&amp;lt;/code&amp;gt; (and similar for &amp;lt;code&amp;gt;LetBound n&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Arg n&amp;lt;/code&amp;gt;, by accessing &amp;lt;code&amp;gt;bs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;xs&amp;lt;/code&amp;gt; respectively).&lt;br /&gt;
* If &amp;lt;code&amp;gt;forall i. (f,xs) |- (ts_i,bs) =&amp;gt;^zs_i vs_i&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;g vs = v&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;z&#039; = sum(zs) + T_g vs&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;(f,xs) |- (Call g ts,bs) =&amp;gt;^z&#039; v&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Then, for the body &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; of a HOL-TCN function &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; assuming:&lt;br /&gt;
* there are correctness lemmas available for each non-primitive auxiliary function, and&lt;br /&gt;
* each primitive aux. function has constant running time,&lt;br /&gt;
we can show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
forall r_0. forall t, r.&lt;br /&gt;
 exist fs. fs_i is called in t --&amp;gt; exist cs, k.&lt;br /&gt;
  forall xs, b.&lt;br /&gt;
   forall s. s arg_f,i = xs_i --&amp;gt;&lt;br /&gt;
    exist ss&#039;.&lt;br /&gt;
     (f,xs) |- (t,b) =&amp;gt;^(sum_i(fs_i ss&#039;_i) z for some z&lt;br /&gt;
     and [[f]]_(r_0)^([]) |- ([[t]]_r^b,s)&lt;br /&gt;
         =&amp;gt;^(k + sum_i(cs_i * fs_i s&#039;s_i)) s&#039; for some s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proof is an induction on the rule of &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;, followed by an induction on the term &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;Let&amp;lt;/code&amp;gt;-case requires a lemma about substitution.&lt;br /&gt;
&lt;br /&gt;
Thus the running time always has the assumed form, and approach from last time (almost) works: for tail-recursive functions, the procedure for finding a lower bound on c would try and instantiate c with a term containing c... This can be fixed. In general, we have to show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case where one of the &amp;lt;code&amp;gt;fs_i&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;?c * T_f s&#039;&amp;lt;/code&amp;gt;, we rearrange to:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i) - ?c * T_f s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
removing c_f, T_f, s&#039; from the vectors on the LHS. On the RHS, one of the fs/s&#039;s is c_f/T_f, which cancels out the subtracted term.&lt;br /&gt;
&lt;br /&gt;
Then we can rearrange to find a lower bound on ?c, as before.&lt;br /&gt;
&lt;br /&gt;
=== June 6th–June 23rd ===&lt;br /&gt;
&lt;br /&gt;
Finished writing up the proof from last week.&lt;br /&gt;
&lt;br /&gt;
==== Discussion ====&lt;br /&gt;
&lt;br /&gt;
Problem: when doing induction over the structure of the term t, in the recursive call case, there is no subterm to apply the induction hypothesis to. Instead, we can do the proof by rule induction over the semantics of HOL-TCN. This means we are assuming that there is an inference for the term in the semantics (i.e. it terminates with a time+value); the justification for this assumption is that the HOL function has a termination order, from which we can construct a termination order for the HOL-TCN term.&lt;br /&gt;
&lt;br /&gt;
A certified translation of Coq functions into deeply-embedded lambda terms: https://drops.dagstuhl.de/storage/00lipics/lipics-vol141-itp2019/LIPIcs.ITP.2019.17/LIPIcs.ITP.2019.17.pdf. Question: what about the step from lambda terms to an imperative language?&lt;br /&gt;
&lt;br /&gt;
==== To-do for next week ====&lt;br /&gt;
&lt;br /&gt;
Write-up proof in TeX. Start implementation in ML.&lt;br /&gt;
&lt;br /&gt;
=== June 23rd–July 21st ===&lt;br /&gt;
&lt;br /&gt;
Idea from last time doesn&#039;t work. Need to explicitly calculate called functions of HOL-TCN term, then relate to called IMP programs of compiled IMP-TC program. See PDF (Zulip).&lt;br /&gt;
&lt;br /&gt;
==== To-do for next time ====&lt;br /&gt;
&lt;br /&gt;
Talk about &amp;quot;traces&amp;quot; instead of &amp;quot;running to leaves&amp;quot;, use bigstep notation (time is replaced by trace, end state/value by pre-tail state).&lt;br /&gt;
&lt;br /&gt;
Instead of putting the trace semantics into one function/predicate, one could investigate an alternative approach. The trace semantics and the subsequent interpretation as running time can essentially be split into three steps: for a given state, 1. compute a linear &amp;quot;list&amp;quot; of instructions by resolving all branches from the given state, 2. from the linearization, extract the list of calls/constants that actually compose the trace, 3. interpret the trace into a concrete (i.e. just an integer) running time. Steps 1. and 2. are currently combined into one predicate, but could be split. This should be investigated, as it could lead to (potentially more but) easier/simpler proofs.&lt;br /&gt;
&lt;br /&gt;
=== July 21st–August 11th ===&lt;br /&gt;
&lt;br /&gt;
Formalized (in Isabelle) the trace semantics that were presented last time, along with the HOL-TCN to IMP-TC compiler. Proved the compiler correct (for terms without recursive calls), and formalized the proof that the traces of the compiled program are related to the traces of the HOL-TCN term.&lt;br /&gt;
&lt;br /&gt;
==== To-do for next time ====&lt;br /&gt;
&lt;br /&gt;
A few (smaller?) lemmas are still required, finish those. Prove the theorems relating traces and running times for HOL-TCN and IMP-TC. Prove the corollary of related traces that running times are also related.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=131</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=131"/>
		<updated>2025-07-21T05:19:56Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Progress tracking for master&#039;s thesis &#039;&#039;&#039;Translating Time Bounds of Functional Programs to a Deeply-Embedded Language&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;br /&gt;
&lt;br /&gt;
=== May 12th–26th ===&lt;br /&gt;
&lt;br /&gt;
Better rule for &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;. Gather running time into constraint for each terminal (tail-call or assignment to return register), &amp;quot;run&amp;quot; function, rearrange constraint to find lower bound on c, take the maximum of all lower bounds as suitable c.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
* Prove that the running time of compiled functions always has the assumed form. Use the definition of the compiler (Kappelmann et al.) and the definition of generated HOL running times.&lt;br /&gt;
* From here, argue why or why not an SMT solver could be used to solve the resulting constraints.&lt;br /&gt;
* Adapt the &amp;lt;code&amp;gt;running&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;gather&amp;lt;/code&amp;gt; method to (tail) recursive functions.&lt;br /&gt;
&lt;br /&gt;
=== May 26th–June 6th ===&lt;br /&gt;
&lt;br /&gt;
Formalized a semantics for HOL-TCNat, such that &amp;lt;code&amp;gt;T_f x1 ... xn = [[e]]_(x1, ..., xn) + 1&amp;lt;/code&amp;gt; for a function &amp;lt;code&amp;gt;f x1 ... xn = e&amp;lt;/code&amp;gt; and its generated running time function &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt;. The big-step predicate has the form: &amp;lt;code&amp;gt;(tail-call term :: HOL_TCNat, argument vector :: nat list) |- (term :: HOL_TCNat, let-bound variables :: Nat list) =&amp;gt;^(running time :: nat) (result :: nat)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
So we define:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(f,xs) |- (Number n,bs) =&amp;gt;^0 n&amp;lt;/code&amp;gt; (and similar for &amp;lt;code&amp;gt;LetBound n&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Arg n&amp;lt;/code&amp;gt;, by accessing &amp;lt;code&amp;gt;bs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;xs&amp;lt;/code&amp;gt; respectively).&lt;br /&gt;
* If &amp;lt;code&amp;gt;forall i. (f,xs) |- (ts_i,bs) =&amp;gt;^zs_i vs_i&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;g vs = v&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;z&#039; = sum(zs) + T_g vs&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;(f,xs) |- (Call g ts,bs) =&amp;gt;^z&#039; v&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Then, for the body &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; of a HOL-TCN function &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; assuming:&lt;br /&gt;
* there are correctness lemmas available for each non-primitive auxiliary function, and&lt;br /&gt;
* each primitive aux. function has constant running time,&lt;br /&gt;
we can show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
forall r_0. forall t, r.&lt;br /&gt;
 exist fs. fs_i is called in t --&amp;gt; exist cs, k.&lt;br /&gt;
  forall xs, b.&lt;br /&gt;
   forall s. s arg_f,i = xs_i --&amp;gt;&lt;br /&gt;
    exist ss&#039;.&lt;br /&gt;
     (f,xs) |- (t,b) =&amp;gt;^(sum_i(fs_i ss&#039;_i) z for some z&lt;br /&gt;
     and [[f]]_(r_0)^([]) |- ([[t]]_r^b,s)&lt;br /&gt;
         =&amp;gt;^(k + sum_i(cs_i * fs_i s&#039;s_i)) s&#039; for some s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proof is an induction on the rule of &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;, followed by an induction on the term &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;Let&amp;lt;/code&amp;gt;-case requires a lemma about substitution.&lt;br /&gt;
&lt;br /&gt;
Thus the running time always has the assumed form, and approach from last time (almost) works: for tail-recursive functions, the procedure for finding a lower bound on c would try and instantiate c with a term containing c... This can be fixed. In general, we have to show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case where one of the &amp;lt;code&amp;gt;fs_i&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;?c * T_f s&#039;&amp;lt;/code&amp;gt;, we rearrange to:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i) - ?c * T_f s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
removing c_f, T_f, s&#039; from the vectors on the LHS. On the RHS, one of the fs/s&#039;s is c_f/T_f, which cancels out the subtracted term.&lt;br /&gt;
&lt;br /&gt;
Then we can rearrange to find a lower bound on ?c, as before.&lt;br /&gt;
&lt;br /&gt;
=== June 6th–June 23rd ===&lt;br /&gt;
&lt;br /&gt;
Finished writing up the proof from last week.&lt;br /&gt;
&lt;br /&gt;
==== Discussion ====&lt;br /&gt;
&lt;br /&gt;
Problem: when doing induction over the structure of the term t, in the recursive call case, there is no subterm to apply the induction hypothesis to. Instead, we can do the proof by rule induction over the semantics of HOL-TCN. This means we are assuming that there is an inference for the term in the semantics (i.e. it terminates with a time+value); the justification for this assumption is that the HOL function has a termination order, from which we can construct a termination order for the HOL-TCN term.&lt;br /&gt;
&lt;br /&gt;
A certified translation of Coq functions into deeply-embedded lambda terms: https://drops.dagstuhl.de/storage/00lipics/lipics-vol141-itp2019/LIPIcs.ITP.2019.17/LIPIcs.ITP.2019.17.pdf. Question: what about the step from lambda terms to an imperative language?&lt;br /&gt;
&lt;br /&gt;
==== To-do for next week ====&lt;br /&gt;
&lt;br /&gt;
Write-up proof in TeX. Start implementation in ML.&lt;br /&gt;
&lt;br /&gt;
=== June 23rd–July 21st ===&lt;br /&gt;
&lt;br /&gt;
Idea from last time doesn&#039;t work. Need to explicitly calculate called functions of HOL-TCN term, then relate to called IMP programs of compiled IMP-TC program. See PDF (Zulip).&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=130</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=130"/>
		<updated>2025-06-23T15:19:11Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Progress tracking for master&#039;s thesis &#039;&#039;&#039;Translating Time Bounds of Functional Programs to a Deeply-Embedded Language&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;br /&gt;
&lt;br /&gt;
=== May 12th–26th ===&lt;br /&gt;
&lt;br /&gt;
Better rule for &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;. Gather running time into constraint for each terminal (tail-call or assignment to return register), &amp;quot;run&amp;quot; function, rearrange constraint to find lower bound on c, take the maximum of all lower bounds as suitable c.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
* Prove that the running time of compiled functions always has the assumed form. Use the definition of the compiler (Kappelmann et al.) and the definition of generated HOL running times.&lt;br /&gt;
* From here, argue why or why not an SMT solver could be used to solve the resulting constraints.&lt;br /&gt;
* Adapt the &amp;lt;code&amp;gt;running&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;gather&amp;lt;/code&amp;gt; method to (tail) recursive functions.&lt;br /&gt;
&lt;br /&gt;
=== May 26th–June 6th ===&lt;br /&gt;
&lt;br /&gt;
Formalized a semantics for HOL-TCNat, such that &amp;lt;code&amp;gt;T_f x1 ... xn = [[e]]_(x1, ..., xn) + 1&amp;lt;/code&amp;gt; for a function &amp;lt;code&amp;gt;f x1 ... xn = e&amp;lt;/code&amp;gt; and its generated running time function &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt;. The big-step predicate has the form: &amp;lt;code&amp;gt;(tail-call term :: HOL_TCNat, argument vector :: nat list) |- (term :: HOL_TCNat, let-bound variables :: Nat list) =&amp;gt;^(running time :: nat) (result :: nat)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
So we define:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(f,xs) |- (Number n,bs) =&amp;gt;^0 n&amp;lt;/code&amp;gt; (and similar for &amp;lt;code&amp;gt;LetBound n&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Arg n&amp;lt;/code&amp;gt;, by accessing &amp;lt;code&amp;gt;bs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;xs&amp;lt;/code&amp;gt; respectively).&lt;br /&gt;
* If &amp;lt;code&amp;gt;forall i. (f,xs) |- (ts_i,bs) =&amp;gt;^zs_i vs_i&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;g vs = v&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;z&#039; = sum(zs) + T_g vs&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;(f,xs) |- (Call g ts,bs) =&amp;gt;^z&#039; v&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Then, for the body &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; of a HOL-TCN function &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; assuming:&lt;br /&gt;
* there are correctness lemmas available for each non-primitive auxiliary function, and&lt;br /&gt;
* each primitive aux. function has constant running time,&lt;br /&gt;
we can show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
forall r_0. forall t, r.&lt;br /&gt;
 exist fs. fs_i is called in t --&amp;gt; exist cs, k.&lt;br /&gt;
  forall xs, b.&lt;br /&gt;
   forall s. s arg_f,i = xs_i --&amp;gt;&lt;br /&gt;
    exist ss&#039;.&lt;br /&gt;
     (f,xs) |- (t,b) =&amp;gt;^(sum_i(fs_i ss&#039;_i) z for some z&lt;br /&gt;
     and [[f]]_(r_0)^([]) |- ([[t]]_r^b,s)&lt;br /&gt;
         =&amp;gt;^(k + sum_i(cs_i * fs_i s&#039;s_i)) s&#039; for some s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proof is an induction on the rule of &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;, followed by an induction on the term &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;Let&amp;lt;/code&amp;gt;-case requires a lemma about substitution.&lt;br /&gt;
&lt;br /&gt;
Thus the running time always has the assumed form, and approach from last time (almost) works: for tail-recursive functions, the procedure for finding a lower bound on c would try and instantiate c with a term containing c... This can be fixed. In general, we have to show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case where one of the &amp;lt;code&amp;gt;fs_i&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;?c * T_f s&#039;&amp;lt;/code&amp;gt;, we rearrange to:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i) - ?c * T_f s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
removing c_f, T_f, s&#039; from the vectors on the LHS. On the RHS, one of the fs/s&#039;s is c_f/T_f, which cancels out the subtracted term.&lt;br /&gt;
&lt;br /&gt;
Then we can rearrange to find a lower bound on ?c, as before.&lt;br /&gt;
&lt;br /&gt;
=== June 6th–June 23rd ===&lt;br /&gt;
&lt;br /&gt;
Finished writing up the proof from last week.&lt;br /&gt;
&lt;br /&gt;
==== Discussion ====&lt;br /&gt;
&lt;br /&gt;
Problem: when doing induction over the structure of the term t, in the recursive call case, there is no subterm to apply the induction hypothesis to. Instead, we can do the proof by rule induction over the semantics of HOL-TCN. This means we are assuming that there is an inference for the term in the semantics (i.e. it terminates with a time+value); the justification for this assumption is that the HOL function has a termination order, from which we can construct a termination order for the HOL-TCN term.&lt;br /&gt;
&lt;br /&gt;
A certified translation of Coq functions into deeply-embedded lambda terms: https://drops.dagstuhl.de/storage/00lipics/lipics-vol141-itp2019/LIPIcs.ITP.2019.17/LIPIcs.ITP.2019.17.pdf. Question: what about the step from lambda terms to an imperative language?&lt;br /&gt;
&lt;br /&gt;
==== To-do for next week ====&lt;br /&gt;
&lt;br /&gt;
Write-up proof in TeX. Start implementation in ML.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=129</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=129"/>
		<updated>2025-06-06T12:31:18Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Progress tracking for master&#039;s thesis &#039;&#039;&#039;Translating Time Bounds of Functional Programs to a Deeply-Embedded Language&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;br /&gt;
&lt;br /&gt;
=== May 12th–26th ===&lt;br /&gt;
&lt;br /&gt;
Better rule for &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;. Gather running time into constraint for each terminal (tail-call or assignment to return register), &amp;quot;run&amp;quot; function, rearrange constraint to find lower bound on c, take the maximum of all lower bounds as suitable c.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
* Prove that the running time of compiled functions always has the assumed form. Use the definition of the compiler (Kappelmann et al.) and the definition of generated HOL running times.&lt;br /&gt;
* From here, argue why or why not an SMT solver could be used to solve the resulting constraints.&lt;br /&gt;
* Adapt the &amp;lt;code&amp;gt;running&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;gather&amp;lt;/code&amp;gt; method to (tail) recursive functions.&lt;br /&gt;
&lt;br /&gt;
=== May 26th–June 6th ===&lt;br /&gt;
&lt;br /&gt;
Formalized a semantics for HOL-TCNat, such that &amp;lt;code&amp;gt;T_f x1 ... xn = [[e]]_(x1, ..., xn) + 1&amp;lt;/code&amp;gt; for a function &amp;lt;code&amp;gt;f x1 ... xn = e&amp;lt;/code&amp;gt; and its generated running time function &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt;. The big-step predicate has the form: &amp;lt;code&amp;gt;(tail-call term :: HOL_TCNat, argument vector :: nat list) |- (term :: HOL_TCNat, let-bound variables :: Nat list) =&amp;gt;^(running time :: nat) (result :: nat)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
So we define:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(f,xs) |- (Number n,bs) =&amp;gt;^0 n&amp;lt;/code&amp;gt; (and similar for &amp;lt;code&amp;gt;LetBound n&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Arg n&amp;lt;/code&amp;gt;, by accessing &amp;lt;code&amp;gt;bs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;xs&amp;lt;/code&amp;gt; respectively).&lt;br /&gt;
* If &amp;lt;code&amp;gt;forall i. (f,xs) |- (ts_i,bs) =&amp;gt;^zs_i vs_i&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;g vs = v&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;z&#039; = sum(zs) + T_g vs&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;(f,xs) |- (Call g ts,bs) =&amp;gt;^z&#039; v&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Then, for the body &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; of a HOL-TCN function &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; assuming:&lt;br /&gt;
* there are correctness lemmas available for each non-primitive auxiliary function, and&lt;br /&gt;
* each primitive aux. function has constant running time,&lt;br /&gt;
we can show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
forall r_0. forall t, r.&lt;br /&gt;
 exist fs. fs_i is called in t --&amp;gt; exist cs, k.&lt;br /&gt;
  forall xs, b.&lt;br /&gt;
   forall s. s arg_f,i = xs_i --&amp;gt;&lt;br /&gt;
    exist ss&#039;.&lt;br /&gt;
     (f,xs) |- (t,b) =&amp;gt;^(sum_i(fs_i ss&#039;_i) z for some z&lt;br /&gt;
     and [[f]]_(r_0)^([]) |- ([[t]]_r^b,s)&lt;br /&gt;
         =&amp;gt;^(k + sum_i(cs_i * fs_i s&#039;s_i)) s&#039; for some s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proof is an induction on the rule of &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;, followed by an induction on the term &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;Let&amp;lt;/code&amp;gt;-case requires a lemma about substitution.&lt;br /&gt;
&lt;br /&gt;
Thus the running time always has the assumed form, and approach from last time (almost) works: for tail-recursive functions, the procedure for finding a lower bound on c would try and instantiate c with a term containing c... This can be fixed. In general, we have to show:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the case where one of the &amp;lt;code&amp;gt;fs_i&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;?c * T_f s&#039;&amp;lt;/code&amp;gt;, we rearrange to:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
k + sum_i(cs_i * fs_i s&#039;s_i) &amp;lt;= ?c * sum_i(fs_i s&#039;s_i) - ?c * T_f s&#039;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
removing c_f, T_f, s&#039; from the vectors on the LHS. On the RHS, one of the fs/s&#039;s is c_f/T_f, which cancels out the subtracted term.&lt;br /&gt;
&lt;br /&gt;
Then we can rearrange to find a lower bound on ?c, as before.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=128</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=128"/>
		<updated>2025-06-06T11:39:49Z</updated>

		<summary type="html">&lt;p&gt;Mxl: progress 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Progress tracking for master&#039;s thesis &#039;&#039;&#039;Translating Time Bounds of Functional Programs to a Deeply-Embedded Language&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;br /&gt;
&lt;br /&gt;
=== May 12th–26th ===&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
* Prove that the running time of compiled functions always has the assumed form. Use the definition of the compiler (Kappelmann et al.) and the definition of generated HOL running times.&lt;br /&gt;
* From here, argue why or why not an SMT solver could be used to solve the resulting constraints.&lt;br /&gt;
* Adapt the &amp;lt;code&amp;gt;running&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;gather&amp;lt;/code&amp;gt; method to (tail) recursive functions.&lt;br /&gt;
&lt;br /&gt;
=== May 26th–June 6th ===&lt;br /&gt;
&lt;br /&gt;
Formalized a semantics for HOL-TCNat, such that the running time of a term &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; obtained from &amp;lt;code&amp;gt;f x1 ... xn = e&amp;lt;/code&amp;gt; corresponds to &amp;lt;code&amp;gt;T_f x1 ... xn&amp;lt;/code&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Apfelkuchen_Rezept_von_Oma&amp;diff=127</id>
		<title>Apfelkuchen Rezept von Oma</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Apfelkuchen_Rezept_von_Oma&amp;diff=127"/>
		<updated>2025-05-27T15:51:29Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Grammatik&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Rezept für einen &#039;&#039;&#039;Apfelkuchen&#039;&#039;&#039; (oder alternativ, indem man stattdessen Rhabarber nimmt, einen &#039;&#039;&#039;Rhabarberkuchen&#039;&#039;&#039;) den ich mal von meiner Oma am Telefon übermittelt bekommen habe.&lt;br /&gt;
&lt;br /&gt;
== Rezept ==&lt;br /&gt;
&lt;br /&gt;
=== Mürbteig ===&lt;br /&gt;
&lt;br /&gt;
Für den Boden &amp;lt;u&amp;gt;180&amp;amp;nbsp;g Mehl&amp;lt;/u&amp;gt;, &amp;lt;u&amp;gt;100&amp;amp;nbsp;g Zucker&amp;lt;/u&amp;gt;, &amp;lt;u&amp;gt;180&amp;amp;nbsp;g Butter&amp;lt;/u&amp;gt;, und &amp;lt;u&amp;gt;1 Ei&amp;lt;/u&amp;gt; zu einem Teig verarbeiten. Den Teig anschließend mindestens 30 Minuten im Kühlschrank ruhen lassen, dann ausrollen und in einer Springform bringen.&lt;br /&gt;
&lt;br /&gt;
Den Teig verarbeitet man am besten mit den Händen, da er sehr zäh ist. Die Butter sollte möglichst gleichmäßig verteilt sein, passt man nicht auf, neigt sie dazu kleine Klumpen zu bilden.&lt;br /&gt;
&lt;br /&gt;
=== Füllung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Apfelfüllung etwa &amp;lt;u&amp;gt;5–6 Äpfel&amp;lt;/u&amp;gt; schälen und dünn schneiden. Den Kuchen damit dicht belegen.&lt;br /&gt;
&lt;br /&gt;
Für eine Rhabarberfüllung &amp;lt;u&amp;gt;800&amp;amp;nbsp;g Rhabarber&amp;lt;/u&amp;gt; in etwa 1–2&amp;amp;nbsp;cm große Stücke schneiden und den Kuchen damit belegen. Der Rhabarber sollte eine augenscheinlich zu große Häufung bilden, schrumpft aber deutlich beim Backen.&lt;br /&gt;
&lt;br /&gt;
=== Guss ===&lt;br /&gt;
&lt;br /&gt;
Für den Guss &amp;lt;u&amp;gt;3 Eier&amp;lt;/u&amp;gt; trennen und das Eiweiß zu Schnee schlagen. Das Eigelb mit &amp;lt;u&amp;gt;2&amp;amp;nbsp;EL Zucker&amp;lt;/u&amp;gt; cremig rühren, dann &amp;lt;u&amp;gt;knapp 1&amp;amp;nbsp;EL Mehl&amp;lt;/u&amp;gt; und &amp;lt;u&amp;gt;½ Becher süße Sahne&amp;lt;/u&amp;gt; einrühren. Den Eierschnee vorsichtig untermischen und den Guss auf dem Kuchen gießen.&lt;br /&gt;
&lt;br /&gt;
Ein Becher Sahne sind je nach Marke etwa 200&amp;amp;nbsp;mL, hier kann man also 100&amp;amp;nbsp;mL nehmen. Gemeint ist ganz normale süße Sahne (15&amp;amp;nbsp;%), &amp;quot;süß&amp;quot; als Gegensatz zu &amp;quot;saurer Sahne&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Backen ===&lt;br /&gt;
&lt;br /&gt;
Den Kuchen bei 180&amp;amp;nbsp;&amp;amp;deg; Ober-/Unterhitze (ohne Umluft) circa 40–45 Minuten backen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Recipes]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Category:Recipes&amp;diff=126</id>
		<title>Category:Recipes</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Category:Recipes&amp;diff=126"/>
		<updated>2025-05-27T15:50:16Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;Recipes.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Recipes.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Apfelkuchen_Rezept_von_Oma&amp;diff=125</id>
		<title>Apfelkuchen Rezept von Oma</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Apfelkuchen_Rezept_von_Oma&amp;diff=125"/>
		<updated>2025-05-27T15:50:01Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Category&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Rezept für einen &#039;&#039;&#039;Apfelkuchen&#039;&#039;&#039; (oder alternativ, indem man stattdessen Rhabarber nimmt, einen &#039;&#039;&#039;Rhabarberkuchen&#039;&#039;&#039;) den ich mal von meiner Oma am Telefon übermittelt bekommen habe.&lt;br /&gt;
&lt;br /&gt;
== Rezept ==&lt;br /&gt;
&lt;br /&gt;
=== Mürbteig ===&lt;br /&gt;
&lt;br /&gt;
Für den Boden &amp;lt;u&amp;gt;180&amp;amp;nbsp;g Mehl&amp;lt;/u&amp;gt;, &amp;lt;u&amp;gt;100&amp;amp;nbsp;g Zucker&amp;lt;/u&amp;gt;, &amp;lt;u&amp;gt;180&amp;amp;nbsp;g Butter&amp;lt;/u&amp;gt;, und &amp;lt;u&amp;gt;1 Ei&amp;lt;/u&amp;gt; zu einem Teig verarbeiten. Den Teig anschließend mindestens 30 Minuten im Kühlschrank ruhen lassen, dann ausrollen und in einer Springform bringen.&lt;br /&gt;
&lt;br /&gt;
Den Teig verarbeitet man am besten mit den Händen, da er sehr zäh ist. Die Butter sollte möglichst gleichmäßig verteilt sein, passt man nicht auf, neigt sie dazu kleine Klumpen zu bilden.&lt;br /&gt;
&lt;br /&gt;
=== Füllung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Apfelfüllung etwa &amp;lt;u&amp;gt;5–6 Äpfel&amp;lt;/u&amp;gt; schälen und dünn schneiden. Den Kuchen damit dicht belegen.&lt;br /&gt;
&lt;br /&gt;
Für eine Rhabarberfüllung &amp;lt;u&amp;gt;800&amp;amp;nbsp;g Rhabarber&amp;lt;/u&amp;gt; in etwa 1–2&amp;amp;nbsp;cm große Stücke schneiden und den Kuchen damit belegen. Der Rhabarber sollte einen augenscheinlich zu große Häufung bilden, schrumpft aber deutlich beim Backen.&lt;br /&gt;
&lt;br /&gt;
=== Guss ===&lt;br /&gt;
&lt;br /&gt;
Für den Guss &amp;lt;u&amp;gt;3 Eier&amp;lt;/u&amp;gt; trennen und das Eiweiß zu Schnee schlagen. Das Eigelb mit &amp;lt;u&amp;gt;2&amp;amp;nbsp;EL Zucker&amp;lt;/u&amp;gt; cremig rühren, dann &amp;lt;u&amp;gt;knapp 1&amp;amp;nbsp;EL Mehl&amp;lt;/u&amp;gt; und &amp;lt;u&amp;gt;½ Becher süße Sahne&amp;lt;/u&amp;gt; einrühren. Den Eierschnee vorsichtig untermischen und den Guss auf dem Kuchen gießen.&lt;br /&gt;
&lt;br /&gt;
Ein Becher Sahne sind je nach Marke etwa 200&amp;amp;nbsp;mL, hier kann man also 100&amp;amp;nbsp;mL nehmen. Gemeint ist ganz normale süße Sahne (15&amp;amp;nbsp;%), &amp;quot;süß&amp;quot; als Gegensatz zu &amp;quot;saurer Sahne&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Backen ===&lt;br /&gt;
&lt;br /&gt;
Den Kuchen bei 180&amp;amp;nbsp;&amp;amp;deg; Ober-/Unterhitze (ohne Umluft) circa 40–45 Minuten backen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Recipes]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=124</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=124"/>
		<updated>2025-05-27T15:49:30Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Recipes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:TransTUM.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]] and others&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[GE Rapid Clean II (seven-segment display font)|GE Rapid Clean II]], font from an oven&#039;s seven-segment display&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;br /&gt;
** [[Snapcast Server Setup]]&lt;br /&gt;
* [[:Category:Recipes|Recipes]]&lt;br /&gt;
** [[Apfelkuchen Rezept von Oma]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Apfelkuchen_Rezept_von_Oma&amp;diff=123</id>
		<title>Apfelkuchen Rezept von Oma</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Apfelkuchen_Rezept_von_Oma&amp;diff=123"/>
		<updated>2025-05-27T15:48:19Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Oapfelkuchen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Rezept für einen &#039;&#039;&#039;Apfelkuchen&#039;&#039;&#039; (oder alternativ, indem man stattdessen Rhabarber nimmt, einen &#039;&#039;&#039;Rhabarberkuchen&#039;&#039;&#039;) den ich mal von meiner Oma am Telefon übermittelt bekommen habe.&lt;br /&gt;
&lt;br /&gt;
== Rezept ==&lt;br /&gt;
&lt;br /&gt;
=== Mürbteig ===&lt;br /&gt;
&lt;br /&gt;
Für den Boden &amp;lt;u&amp;gt;180&amp;amp;nbsp;g Mehl&amp;lt;/u&amp;gt;, &amp;lt;u&amp;gt;100&amp;amp;nbsp;g Zucker&amp;lt;/u&amp;gt;, &amp;lt;u&amp;gt;180&amp;amp;nbsp;g Butter&amp;lt;/u&amp;gt;, und &amp;lt;u&amp;gt;1 Ei&amp;lt;/u&amp;gt; zu einem Teig verarbeiten. Den Teig anschließend mindestens 30 Minuten im Kühlschrank ruhen lassen, dann ausrollen und in einer Springform bringen.&lt;br /&gt;
&lt;br /&gt;
Den Teig verarbeitet man am besten mit den Händen, da er sehr zäh ist. Die Butter sollte möglichst gleichmäßig verteilt sein, passt man nicht auf, neigt sie dazu kleine Klumpen zu bilden.&lt;br /&gt;
&lt;br /&gt;
=== Füllung ===&lt;br /&gt;
&lt;br /&gt;
Für eine Apfelfüllung etwa &amp;lt;u&amp;gt;5–6 Äpfel&amp;lt;/u&amp;gt; schälen und dünn schneiden. Den Kuchen damit dicht belegen.&lt;br /&gt;
&lt;br /&gt;
Für eine Rhabarberfüllung &amp;lt;u&amp;gt;800&amp;amp;nbsp;g Rhabarber&amp;lt;/u&amp;gt; in etwa 1–2&amp;amp;nbsp;cm große Stücke schneiden und den Kuchen damit belegen. Der Rhabarber sollte einen augenscheinlich zu große Häufung bilden, schrumpft aber deutlich beim Backen.&lt;br /&gt;
&lt;br /&gt;
=== Guss ===&lt;br /&gt;
&lt;br /&gt;
Für den Guss &amp;lt;u&amp;gt;3 Eier&amp;lt;/u&amp;gt; trennen und das Eiweiß zu Schnee schlagen. Das Eigelb mit &amp;lt;u&amp;gt;2&amp;amp;nbsp;EL Zucker&amp;lt;/u&amp;gt; cremig rühren, dann &amp;lt;u&amp;gt;knapp 1&amp;amp;nbsp;EL Mehl&amp;lt;/u&amp;gt; und &amp;lt;u&amp;gt;½ Becher süße Sahne&amp;lt;/u&amp;gt; einrühren. Den Eierschnee vorsichtig untermischen und den Guss auf dem Kuchen gießen.&lt;br /&gt;
&lt;br /&gt;
Ein Becher Sahne sind je nach Marke etwa 200&amp;amp;nbsp;mL, hier kann man also 100&amp;amp;nbsp;mL nehmen. Gemeint ist ganz normale süße Sahne (15&amp;amp;nbsp;%), &amp;quot;süß&amp;quot; als Gegensatz zu &amp;quot;saurer Sahne&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Backen ===&lt;br /&gt;
&lt;br /&gt;
Den Kuchen bei 180&amp;amp;nbsp;&amp;amp;deg; Ober-/Unterhitze (ohne Umluft) circa 40–45 Minuten backen.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=122</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=122"/>
		<updated>2025-05-16T14:34:04Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Progress tracking for master&#039;s thesis &#039;&#039;&#039;Translating Time Bounds of Functional Programs to a Deeply-Embedded Language&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;br /&gt;
&lt;br /&gt;
=== May 12th–26th ===&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== To-do for Next Week ====&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Automated_Running_Time_Equivalences_(Thesis_Project_Tracking)&amp;diff=121</id>
		<title>Automated Running Time Equivalences (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Automated_Running_Time_Equivalences_(Thesis_Project_Tracking)&amp;diff=121"/>
		<updated>2025-05-16T14:26:45Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Mxl moved page Automated Running Time Equivalences (Thesis Project Tracking) to Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking): Better title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=120</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=120"/>
		<updated>2025-05-16T14:26:45Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Mxl moved page Automated Running Time Equivalences (Thesis Project Tracking) to Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking): Better title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To do for next week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=119</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=119"/>
		<updated>2025-05-14T23:46:58Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;br /&gt;
&lt;br /&gt;
==== To do for next week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
The condition of an if/else may depend on the input beyond just determining which case of the original function is being called, i.e. cannot necessarily be solved directly based on the inductive case being considered. Thus we cannot assume that we can solve the if/else if we introduce one into our timing constraint. The natural idea to use a maximum of both branches&#039; running time won&#039;t work, consider for example the simple expression &amp;lt;code&amp;gt;if b then 1 else g y&amp;lt;/code&amp;gt;; then, taking the maximum on the running time would result in a constraint like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * T_f (s &amp;quot;x&amp;quot;)&amp;lt;/code&amp;gt;, which upon expanding &amp;lt;code&amp;gt;T_f&amp;lt;/code&amp;gt; in the right-hand-side and taking the lower bound of the resulting if/else, would yield something like &amp;lt;code&amp;gt;max 3 ((LEAST c. ...) * T_g (s&#039; &amp;quot;y&amp;quot;)) &amp;lt;= ?c * 1&amp;lt;/code&amp;gt;, which is obviously nonsense. Instead, we could try creating one inequality constraint for each branch.&lt;br /&gt;
&lt;br /&gt;
Investigate the use of SMT-solvers or other automated solvers for the resulting inequality constraints.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=118</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=118"/>
		<updated>2025-05-14T21:17:22Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Summarize preliminary readings&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== March 24th–April 28th ===&lt;br /&gt;
&lt;br /&gt;
Read through [https://arxiv.org/pdf/2503.02975 Proof-Producing Translation of Functional Programs into a Time &amp;amp; Space Reasonable Model] which describes existing work on functional correctness.&lt;br /&gt;
&lt;br /&gt;
Read [https://www.chargueraud.org/research/2018/bigo_credits/bigo_credits.pdf A Fistful of Dollars: Formalizing Asymptotic Complexity Claims via Deductive Program Verification] which develops a framework for interactive proofs of running time intended as part of an interface specification. The method first recursively descends through the function to calculate its running time up to recursive calls, e.g. &amp;lt;code&amp;gt;T_f(x) = 2 + T_f(x - 1)&amp;lt;/code&amp;gt;. The user is asked to provide a &amp;quot;shape&amp;quot; of the running time bound they wish to specify, e.g. &amp;lt;code&amp;gt;a * x&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; is left as a variable to solve for. Then induction is performed on the goal e.g. &amp;lt;code&amp;gt;2 + T_f(x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, which through induction then becomes &amp;lt;code&amp;gt;2 + a * (x - 1) &amp;lt;= a * x&amp;lt;/code&amp;gt;, thus providing a constraint on a suitable &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. This is essentially the [https://enos.itcollege.ee/~japoia/algorithms/GT/Introduction_to_algorithms-3rd%20Edition.pdf#page=104 &amp;quot;substitution method&amp;quot; of Cormen et al.].&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To do for next week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=117</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=117"/>
		<updated>2025-05-14T18:22:24Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:TransTUM.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]] and others&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[GE Rapid Clean II (seven-segment display font)|GE Rapid Clean II]], font from an oven&#039;s seven-segment display&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;br /&gt;
** [[Snapcast Server Setup]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:TransTUM.svg&amp;diff=116</id>
		<title>File:TransTUM.svg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:TransTUM.svg&amp;diff=116"/>
		<updated>2025-05-14T18:20:58Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Font is Helvetica.&lt;br /&gt;
&lt;br /&gt;
[[Category:Stickers]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:TransTUM.svg&amp;diff=115</id>
		<title>File:TransTUM.svg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:TransTUM.svg&amp;diff=115"/>
		<updated>2025-05-14T18:20:10Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Font is Helvetica.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Font is Helvetica.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=114</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=114"/>
		<updated>2025-05-14T11:21:52Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Fixes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== April 14th–28th ===&lt;br /&gt;
&lt;br /&gt;
=== April 14th–28th ===&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a constant. For this, we use Isabelle&#039;s LEAST operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schematic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LEAST c. ...) * T_aux ... term just like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To do for next week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=113</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=113"/>
		<updated>2025-05-12T11:03:33Z</updated>

		<summary type="html">&lt;p&gt;Mxl: April 28 - May 12&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== April 14th–28th ===&lt;br /&gt;
&lt;br /&gt;
=== April 14th–28th ===&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
Investigated how to generate running time constraints in Isabelle by recursive tactics on the structure of the IMP-Tailcall term, as in previous work on correctness. The issue to overcome is that we must work with a predicate of the form \exists c. \forall s. T_f_IMP s &amp;lt;= c * T_f (s &amp;quot;x&amp;quot;), which requires providing a single witness c for the linear factor. Previous work on correctness split the single goal recursively at sequences and conditionals into goals for each sub-term, but this approach doesn&#039;t work directly with an existential quantor: the &#039;&#039;&#039;same&#039;&#039;&#039; constant must be a witness in both subterms.&lt;br /&gt;
&lt;br /&gt;
Instead of working directly with the existential, we can &amp;quot;provide&amp;quot; a witness up-front in the form of a schematic variable, and instantiate the schematic step-by-step. Thus we work with a predicate that bounds the running time by e.g. c * T_f (s &amp;quot;x&amp;quot;), without the existential quantor, and compute suitable constraints on the way down. Once we have computed a suitable c, it is obviously a witness for the existential quantor and we can close the goal.&lt;br /&gt;
&lt;br /&gt;
This poses an additional challenge when previously verified functions are called, as the scheme devised above requires a concrete running time bound, not quantified with the existence of a consant. For this, we use Isabelle&#039;s LESS operator to materialize the existential c from the running time of the auxiliary function&#039;s bound. (The obvious approach of &amp;quot;obtain&amp;quot;ing the constant with Isabelle&#039;s proof functionality does not work, as the schenatic witness c of the function being analyzed can not depend on a value obtained later). We can then work with this (LESS c. ...) * T_aux ... term jist like any other bound.&lt;br /&gt;
&lt;br /&gt;
After analyzing the term, we can instantiate and apply an induction hypothesis where applicable, this essentially corresponds to Cormen&#039;s &amp;quot;substitution method&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We are left with constraints on the constant c which must be solved, then c can be instantiated and the goals closed.&lt;br /&gt;
&lt;br /&gt;
==== To do for next week ====&lt;br /&gt;
&lt;br /&gt;
Examine more complex functions which:&lt;br /&gt;
* call functions with non-constant running time&lt;br /&gt;
* have preconditions on the well-formedness of their inputs&lt;br /&gt;
* use data types (this will only work to relate to HOL-Nat)&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
Should we investigate other approaches? For example, computing the running time up-front.&lt;br /&gt;
&lt;br /&gt;
How can we automatically solve for suitable constants from the resulting constraints?&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=112</id>
		<title>Translating Time Bounds of Functional Programs to a Deeply-Embedded Language (Thesis Project Tracking)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Translating_Time_Bounds_of_Functional_Programs_to_a_Deeply-Embedded_Language_(Thesis_Project_Tracking)&amp;diff=112"/>
		<updated>2025-05-12T10:39:02Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;== Progress ==  === April 14th–28th ===  === April 14th–28th ===  === April 28th–May 12th ===  ==== Recap Last Week ====  ==== To do for next week ====  ==== To Discuss ====  ==== Notes ====&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Progress ==&lt;br /&gt;
&lt;br /&gt;
=== April 14th–28th ===&lt;br /&gt;
&lt;br /&gt;
=== April 14th–28th ===&lt;br /&gt;
&lt;br /&gt;
=== April 28th–May 12th ===&lt;br /&gt;
&lt;br /&gt;
==== Recap Last Week ====&lt;br /&gt;
&lt;br /&gt;
==== To do for next week ====&lt;br /&gt;
&lt;br /&gt;
==== To Discuss ====&lt;br /&gt;
&lt;br /&gt;
==== Notes ====&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=111</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=111"/>
		<updated>2025-04-27T13:16:33Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]]&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[GE Rapid Clean II (seven-segment display font)|GE Rapid Clean II]], font from an oven&#039;s seven-segment display&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;br /&gt;
** [[Snapcast Server Setup]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Snapcast_Server_Setup&amp;diff=110</id>
		<title>Snapcast Server Setup</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Snapcast_Server_Setup&amp;diff=110"/>
		<updated>2025-04-27T13:16:10Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;Notes for &amp;#039;&amp;#039;&amp;#039;setting up Snapcast&amp;#039;&amp;#039;&amp;#039; (https://github.com/badaix/snapcast) for multi-device audio playback.  == PipeWire as Player ==  To configure PipeWire as a player for Snapcast, create the file &amp;lt;code&amp;gt;10-snapcast-pipe-sink.conf&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt; (or in &amp;lt;code&amp;gt;~/.config/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt;):   &amp;lt;nowiki&amp;gt;context.modules = [     {         name = libpipewire-module-pipe-tunnel         args = {             tunnel.mode = sink...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Notes for &#039;&#039;&#039;setting up Snapcast&#039;&#039;&#039; (https://github.com/badaix/snapcast) for multi-device audio playback.&lt;br /&gt;
&lt;br /&gt;
== PipeWire as Player ==&lt;br /&gt;
&lt;br /&gt;
To configure PipeWire as a player for Snapcast, create the file &amp;lt;code&amp;gt;10-snapcast-pipe-sink.conf&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;/etc/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt; (or in &amp;lt;code&amp;gt;~/.config/pipewire/pipewire.conf.d/&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;context.modules = [&lt;br /&gt;
    {&lt;br /&gt;
        name = libpipewire-module-pipe-tunnel&lt;br /&gt;
        args = {&lt;br /&gt;
            tunnel.mode = sink&lt;br /&gt;
            pipe.filename = &amp;quot;/tmp/snapfifo&amp;quot;&lt;br /&gt;
            audio.format = S16LE&lt;br /&gt;
            audio.rate = 48000&lt;br /&gt;
            audio.channels = 2&lt;br /&gt;
            audio.position = [ FL FR ]&lt;br /&gt;
            stream.props = {&lt;br /&gt;
                node.name = Snapcast&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, restart PipeWire: &amp;lt;code&amp;gt;systemctl --user status wireplumber pipewire pipewire-pulse&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
* PipeWire wiki on virtual devices: https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Virtual-Devices#pipe-devices&lt;br /&gt;
* PipeWire documentation on pipe tunnel module: https://docs.pipewire.org/page_module_pipe_tunnel.html&lt;br /&gt;
* Snapcast docs on player setup: https://github.com/badaix/snapcast/blob/develop/doc/player_setup.md#pulseaudio&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=109</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Main_Page&amp;diff=109"/>
		<updated>2025-01-15T03:39:14Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to &amp;lt;strong&amp;gt;max.wiki&amp;lt;/strong&amp;gt;.&lt;br /&gt;
[[File:Haj45.png|thumb|right|alt=blahaj]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
* Collection of [[:Category:Stickers|printable stickers]]&lt;br /&gt;
** [[File:Safetyears.svg|x22px|frameless]] [[File:ACAB_Discounter.svg|x22px|frameless]] [[File:Trans_Pride_Spezi.svg|x22px|frameless|link=Trans Pride Spezi Sticker]] [[File:TransKIT.svg|x22px|frameless]] [[File:Spezi_Pride.svg|x22px|frameless]]&lt;br /&gt;
* [[:Category:Fonts|Fonts]], traced, copied, or self-designed&lt;br /&gt;
** [[GE Rapid Clean II (seven-segment display font)|GE Rapid Clean II]], font from an oven&#039;s seven-segment display&lt;br /&gt;
* [[:Category:System Configuration|System Configuration]], notes on configuring Linux systems and software&lt;br /&gt;
** [[Managing and Adding APT Repositories]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Category:System_Configuration&amp;diff=108</id>
		<title>Category:System Configuration</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Category:System_Configuration&amp;diff=108"/>
		<updated>2025-01-15T03:37:34Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;Best practices, references, and guides.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Best practices, references, and guides.&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Managing_and_Adding_APT_Repositories&amp;diff=107</id>
		<title>Managing and Adding APT Repositories</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Managing_and_Adding_APT_Repositories&amp;diff=107"/>
		<updated>2025-01-15T03:36:43Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Best practices, references, and helpers for &#039;&#039;&#039;managing custom APT repositories&#039;&#039;&#039; on Debian-derived systems.&lt;br /&gt;
&lt;br /&gt;
The general advice is to put source lists in &amp;lt;code&amp;gt;/etc/apt/sources.list.d/&amp;lt;/code&amp;gt; and keys in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt;, referenced by &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; entries. The use of DEB822 Source Format is encouraged, as it simplifies managing repositories manually.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
* Debian Wiki&#039;s general instructions on third-party repositories: https://wiki.debian.org/DebianRepository/UseThirdParty&lt;br /&gt;
* Repolib&#039;s documentation of the DEB822 Source Format: https://repolib.readthedocs.io/en/latest/deb822-format.html&lt;br /&gt;
* Man page for APT &amp;lt;code&amp;gt;sources.list&amp;lt;/code&amp;gt;: https://manpages.debian.org/unstable/apt/sources.list.5.en.html&lt;br /&gt;
&lt;br /&gt;
== Converting list format to DEB822 ==&lt;br /&gt;
&lt;br /&gt;
Given a line in the traditional list format &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;deb [arch=arch1,arch2,... signed-by=/path/to/key] https://repo.url distribution component1 component2 ...&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, the corresponding DEB822 sources entry becomes:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Enabled: yes&lt;br /&gt;
Types: deb&lt;br /&gt;
URIs: https://repo.url&lt;br /&gt;
Suites: distribution ...&lt;br /&gt;
Components: component1 component2 ...&lt;br /&gt;
Architectures: arch1 arch2 ...&lt;br /&gt;
Signed-By: /path/to/key&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the &amp;lt;code&amp;gt;distribution&amp;lt;/code&amp;gt; in the traditional entry becomes (one of) the &amp;lt;code&amp;gt;Suites&amp;lt;/code&amp;gt; in the DEB822 entry.&lt;br /&gt;
&lt;br /&gt;
== Signing Keys ==&lt;br /&gt;
&lt;br /&gt;
Signing keys should be placed in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt; and then by referenced by their file path in the &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; option in the source list entry.&lt;br /&gt;
&lt;br /&gt;
Alternatively, keys can be ASCII-armored and referenced inline in a DEB822 source list:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Signed-By:&lt;br /&gt;
  -----BEGIN PGP PUBLIC KEY BLOCK-----&lt;br /&gt;
  .&lt;br /&gt;
  mQINBGdCz4IBEACqA2UybPzUDw81EG0nXNUJ4Fk64pRkKqC5FwWUg7dPA4rtdMao&lt;br /&gt;
  -----END PGP PUBLIC KEY BLOCK-----&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that a single dot &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; must be used to replace the empty line, otherwise the empty line will split the file into multiple entries.&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
See:&lt;br /&gt;
- General instructions (above)&lt;br /&gt;
- https://askubuntu.com/a/1307181&lt;br /&gt;
TODO: link to why trusted keys are bad and why /etc/apt/keyrings is correct &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pinning ==&lt;br /&gt;
&amp;lt;!-- TODO: flesh out section if useful --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add a &amp;lt;code&amp;gt;.pref&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/etc/apt/preferences.d/&amp;lt;/code&amp;gt; to allow only specified packages to be installed by using pinning.&lt;br /&gt;
&lt;br /&gt;
See &amp;quot;Standard pinning&amp;quot; in the linked Debian Wiki page.&lt;br /&gt;
&lt;br /&gt;
For example, to disable packages from &amp;lt;code&amp;gt;contrib&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;non-free&amp;lt;/code&amp;gt;, but allow installation of &amp;lt;code&amp;gt;libdvd-pkg&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Explanation: Disable packages from debian contrib and non-free components by default&lt;br /&gt;
Package: *&lt;br /&gt;
Pin: release o=Debian,a=/^(stable|stable-updates|stable-security)$/,l=/^(Debian|Debian-Security)$/,c=/^(contrib|non-free)$/&lt;br /&gt;
Pin-Priority: -1&lt;br /&gt;
&lt;br /&gt;
Explanation: Install libdvd-pkg from contrib&lt;br /&gt;
Package: libdvd-pkg&lt;br /&gt;
Pin: release o=Debian,a=stable,l=Debian,c=contrib&lt;br /&gt;
Pin-Priority: 500&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or alternatively &amp;lt;code&amp;gt;Pin: origin repo.url&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:System Configuration]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Managing_and_Adding_APT_Repositories&amp;diff=106</id>
		<title>Managing and Adding APT Repositories</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Managing_and_Adding_APT_Repositories&amp;diff=106"/>
		<updated>2025-01-15T03:35:10Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Best practices, references, and helpers for &#039;&#039;&#039;managing custom APT repositories&#039;&#039;&#039; on Debian-derived systems.&lt;br /&gt;
&lt;br /&gt;
The general advice is to put source lists in &amp;lt;code&amp;gt;/etc/apt/sources.list.d/&amp;lt;/code&amp;gt; and keys in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt;, referenced by &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; entries. The use of DEB822 Source Format is encouraged, as it simplifies managing repositories manually.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
* Debian Wiki&#039;s general instructions on third-party repositories: https://wiki.debian.org/DebianRepository/UseThirdParty&lt;br /&gt;
* Repolib&#039;s documentation of the DEB822 Source Format: https://repolib.readthedocs.io/en/latest/deb822-format.html&lt;br /&gt;
* Man page for APT &amp;lt;code&amp;gt;sources.list&amp;lt;/code&amp;gt;: https://manpages.debian.org/unstable/apt/sources.list.5.en.html&lt;br /&gt;
&lt;br /&gt;
== Converting list format to DEB822 ==&lt;br /&gt;
&lt;br /&gt;
Given a line in the traditional list format &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;deb [arch=arch1,arch2,... signed-by=/path/to/key] https://repo.url distribution component1 component2 ...&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, the corresponding DEB822 sources entry becomes:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Enabled: yes&lt;br /&gt;
Types: deb&lt;br /&gt;
URIs: https://repo.url&lt;br /&gt;
Suites: distribution ...&lt;br /&gt;
Components: component1 component2 ...&lt;br /&gt;
Architectures: arch1 arch2 ...&lt;br /&gt;
Signed-By: /path/to/key&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the &amp;lt;code&amp;gt;distribution&amp;lt;/code&amp;gt; in the traditional entry becomes (one of) the &amp;lt;code&amp;gt;Suites&amp;lt;/code&amp;gt; in the DEB822 entry.&lt;br /&gt;
&lt;br /&gt;
== Signing Keys ==&lt;br /&gt;
&lt;br /&gt;
Signing keys should be placed in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt; and then by referenced by their file path in the &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; option in the source list entry.&lt;br /&gt;
&lt;br /&gt;
Alternatively, keys can be ASCII-armored and referenced inline in a DEB822 source list:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Signed-By:&lt;br /&gt;
  -----BEGIN PGP PUBLIC KEY BLOCK-----&lt;br /&gt;
  .&lt;br /&gt;
  mQINBGdCz4IBEACqA2UybPzUDw81EG0nXNUJ4Fk64pRkKqC5FwWUg7dPA4rtdMao&lt;br /&gt;
  -----END PGP PUBLIC KEY BLOCK-----&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that a single dot &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; must be used to replace the empty line, otherwise the empty line will split the file into multiple entries.&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
See:&lt;br /&gt;
- General instructions (above)&lt;br /&gt;
- https://askubuntu.com/a/1307181&lt;br /&gt;
TODO: link to why trusted keys are bad and why /etc/apt/keyrings is correct &lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pinning ==&lt;br /&gt;
&amp;lt;!-- TODO: flesh out section if useful --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add a &amp;lt;code&amp;gt;.pref&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/etc/apt/preferences.d/&amp;lt;/code&amp;gt; to allow only specified packages to be installed by using pinning.&lt;br /&gt;
&lt;br /&gt;
See &amp;quot;Standard pinning&amp;quot; in the linked Debian Wiki page.&lt;br /&gt;
&lt;br /&gt;
For example, to disable packages from &amp;lt;code&amp;gt;contrib&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;non-free&amp;lt;/code&amp;gt;, but allow installation of &amp;lt;code&amp;gt;libdvd-pkg&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Explanation: Disable packages from debian contrib and non-free components by default&lt;br /&gt;
Package: *&lt;br /&gt;
Pin: release o=Debian,a=/^(stable|stable-updates|stable-security)$/,l=/^(Debian|Debian-Security)$/,c=/^(contrib|non-free)$/&lt;br /&gt;
Pin-Priority: -1&lt;br /&gt;
&lt;br /&gt;
Explanation: Install libdvd-pkg from contrib&lt;br /&gt;
Package: libdvd-pkg&lt;br /&gt;
Pin: release o=Debian,a=stable,l=Debian,c=contrib&lt;br /&gt;
Pin-Priority: 500&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or alternatively &amp;lt;code&amp;gt;Pin: origin repo.url&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=Managing_and_Adding_APT_Repositories&amp;diff=105</id>
		<title>Managing and Adding APT Repositories</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=Managing_and_Adding_APT_Repositories&amp;diff=105"/>
		<updated>2025-01-15T03:33:08Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Created page with &amp;quot;Best practices, references, and helpers for &amp;#039;&amp;#039;&amp;#039;managing custom APT repositories&amp;#039;&amp;#039;&amp;#039; on Debian-derived systems.  The general advice is to put source lists in &amp;lt;code&amp;gt;/etc/apt/sources.list.d/&amp;lt;/code&amp;gt; and keys in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt;, referenced by &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; entries. The use of DEB822 Source Format is encouraged, as it simplifies managing repositories manually.  == Resources ==  * Debian Wiki&amp;#039;s general instructions on third-party repositories: https://wi...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Best practices, references, and helpers for &#039;&#039;&#039;managing custom APT repositories&#039;&#039;&#039; on Debian-derived systems.&lt;br /&gt;
&lt;br /&gt;
The general advice is to put source lists in &amp;lt;code&amp;gt;/etc/apt/sources.list.d/&amp;lt;/code&amp;gt; and keys in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt;, referenced by &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; entries. The use of DEB822 Source Format is encouraged, as it simplifies managing repositories manually.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
* Debian Wiki&#039;s general instructions on third-party repositories: https://wiki.debian.org/DebianRepository/UseThirdParty&lt;br /&gt;
* Repolib&#039;s documentation of the DEB822 Source Format: https://repolib.readthedocs.io/en/latest/deb822-format.html&lt;br /&gt;
* Man page for APT &amp;lt;code&amp;gt;sources.list&amp;lt;/code&amp;gt;: https://manpages.debian.org/unstable/apt/sources.list.5.en.html&lt;br /&gt;
&lt;br /&gt;
== Converting list format to DEB822 ==&lt;br /&gt;
&lt;br /&gt;
Given a line in the traditional list format &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;deb [arch=arch1,arch2,... signed-by=/path/to/key] https://repo.url distribution component1 component2 ...&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, the corresponding DEB822 sources entry becomes:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Enabled: yes&lt;br /&gt;
Types: deb&lt;br /&gt;
URIs: https://repo.url&lt;br /&gt;
Suites: distribution ...&lt;br /&gt;
Components: component1 component2 ...&lt;br /&gt;
Architectures: arch1 arch2 ...&lt;br /&gt;
Signed-By: /path/to/key&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the &amp;lt;code&amp;gt;distribution&amp;lt;/code&amp;gt; in the traditional entry becomes (one of) the &amp;lt;code&amp;gt;Suites&amp;lt;/code&amp;gt; in the DEB822 entry.&lt;br /&gt;
&lt;br /&gt;
== Signing Keys ==&lt;br /&gt;
&lt;br /&gt;
Signing keys should be placed in &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt; and then by referenced by their file path in the &amp;lt;code&amp;gt;Signed-By&amp;lt;/code&amp;gt; option in the source list entry.&lt;br /&gt;
&lt;br /&gt;
Alternatively, keys can be ASCII-armored and referenced inline in a DEB822 source list:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Signed-By:&lt;br /&gt;
  -----BEGIN PGP PUBLIC KEY BLOCK-----&lt;br /&gt;
  .&lt;br /&gt;
  mQINBGdCz4IBEACqA2UybPzUDw81EG0nXNUJ4Fk64pRkKqC5FwWUg7dPA4rtdMao&lt;br /&gt;
  -----END PGP PUBLIC KEY BLOCK-----&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that a single dot &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; must be used to replace the empty line, otherwise the empty line will split the file into multiple entries.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
See:&lt;br /&gt;
- General instructions (above)&lt;br /&gt;
- https://askubuntu.com/a/1307181&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: link to why trusted keys are bad and why /etc/apt/keyrings is correct --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Pinning ==&lt;br /&gt;
&lt;br /&gt;
Add a &amp;lt;code&amp;gt;.pref&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/etc/apt/preferences.d/&amp;lt;/code&amp;gt; to allow only specified packages to be installed by using pinning.&lt;br /&gt;
&lt;br /&gt;
See &amp;quot;Standard pinning&amp;quot; in the linked Debian Wiki page.&lt;br /&gt;
&lt;br /&gt;
For example, to disable packages from &amp;lt;code&amp;gt;contrib&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;non-free&amp;lt;/code&amp;gt;, but allow installation of &amp;lt;code&amp;gt;libdvd-pkg&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Explanation: Disable packages from debian contrib and non-free components by default&lt;br /&gt;
Package: *&lt;br /&gt;
Pin: release o=Debian,a=/^(stable|stable-updates|stable-security)$/,l=/^(Debian|Debian-Security)$/,c=/^(contrib|non-free)$/&lt;br /&gt;
Pin-Priority: -1&lt;br /&gt;
&lt;br /&gt;
Explanation: Install libdvd-pkg from contrib&lt;br /&gt;
Package: libdvd-pkg&lt;br /&gt;
Pin: release o=Debian,a=stable,l=Debian,c=contrib&lt;br /&gt;
Pin-Priority: 500&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or alternatively &amp;lt;code&amp;gt;Pin: origin repo.url&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: flesh out section if useful --&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=GE_Rapid_Clean_II_(seven-segment_display_font)&amp;diff=104</id>
		<title>GE Rapid Clean II (seven-segment display font)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=GE_Rapid_Clean_II_(seven-segment_display_font)&amp;diff=104"/>
		<updated>2025-01-06T03:04:19Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:GE Rapid Clean II Proof.svg|thumb|right|alt=Preview of font showing digits 0 through 9, alphabet, and a collection of words.|Preview of GE Rapid Clean II font]]&lt;br /&gt;
&lt;br /&gt;
A digital recreation of the &#039;&#039;&#039;font&#039;&#039;&#039; used by the seven-segment display in the &#039;&#039;&#039;General Electric Rapid Clean II&#039;&#039;&#039; stove, which was installed in the kitchen at my mother&#039;s house.&lt;br /&gt;
&lt;br /&gt;
First, the segment shapes were recreated in [https://solvespace.com SolveSpace]. The exported vector graphic was then imported into [https://fontforge.org/ FontForge] and split into &amp;quot;virtual&amp;quot; glyphs, which the real glyphs then reference.&lt;br /&gt;
&lt;br /&gt;
== Download ==&lt;br /&gt;
&lt;br /&gt;
* Latest FontForge export: [[Media:GE Rapid Clean II.otf|GE Rapid Clean II (OpenType font, .otf)]]&lt;br /&gt;
&lt;br /&gt;
== Files ==&lt;br /&gt;
&lt;br /&gt;
The design files (SolveSpace, FontForge) are in the repository [https://github.com/just-max/ge-rapid-clean-ii-font just-max/ge-rapid-clean-ii-font] on GitHub.&lt;br /&gt;
&lt;br /&gt;
== Reference Images ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
GE Rapid Clean II (character 8).jpg|Close-up of all segments lit&lt;br /&gt;
GE Rapid Clean II (8-34).jpg|Showing the time 8:34&lt;br /&gt;
GE_Rapid_Clean_II_(9-09).jpg|Showing the time 9:09&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Fonts]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:GE_Rapid_Clean_II_(9-02).jpg&amp;diff=103</id>
		<title>File:GE Rapid Clean II (9-02).jpg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:GE_Rapid_Clean_II_(9-02).jpg&amp;diff=103"/>
		<updated>2025-01-06T03:03:57Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Mxl moved page File:GE Rapid Clean II (9-02).jpg to File:GE Rapid Clean II (9-09).jpg: Misspelled title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[File:GE Rapid Clean II (9-09).jpg]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=File:GE_Rapid_Clean_II_(9-09).jpg&amp;diff=102</id>
		<title>File:GE Rapid Clean II (9-09).jpg</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=File:GE_Rapid_Clean_II_(9-09).jpg&amp;diff=102"/>
		<updated>2025-01-06T03:03:57Z</updated>

		<summary type="html">&lt;p&gt;Mxl: Mxl moved page File:GE Rapid Clean II (9-02).jpg to File:GE Rapid Clean II (9-09).jpg: Misspelled title&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
	<entry>
		<id>http://wiki.maxthomaslang.de/w?title=GE_Rapid_Clean_II_(seven-segment_display_font)&amp;diff=101</id>
		<title>GE Rapid Clean II (seven-segment display font)</title>
		<link rel="alternate" type="text/html" href="http://wiki.maxthomaslang.de/w?title=GE_Rapid_Clean_II_(seven-segment_display_font)&amp;diff=101"/>
		<updated>2025-01-03T05:24:50Z</updated>

		<summary type="html">&lt;p&gt;Mxl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:GE Rapid Clean II Proof.svg|thumb|right|alt=Preview of font showing digits 0 through 9, alphabet, and a collection of words.|Preview of GE Rapid Clean II font]]&lt;br /&gt;
&lt;br /&gt;
A digital recreation of the &#039;&#039;&#039;font&#039;&#039;&#039; used by the seven-segment display in the &#039;&#039;&#039;General Electric Rapid Clean II&#039;&#039;&#039; stove, which was installed in the kitchen at my mother&#039;s house.&lt;br /&gt;
&lt;br /&gt;
First, the segment shapes were recreated in [https://solvespace.com SolveSpace]. The exported vector graphic was then imported into [https://fontforge.org/ FontForge] and split into &amp;quot;virtual&amp;quot; glyphs, which the real glyphs then reference.&lt;br /&gt;
&lt;br /&gt;
== Download ==&lt;br /&gt;
&lt;br /&gt;
* Latest FontForge export: [[Media:GE Rapid Clean II.otf|GE Rapid Clean II (OpenType font, .otf)]]&lt;br /&gt;
&lt;br /&gt;
== Files ==&lt;br /&gt;
&lt;br /&gt;
The design files (SolveSpace, FontForge) are in the repository [https://github.com/just-max/ge-rapid-clean-ii-font just-max/ge-rapid-clean-ii-font] on GitHub.&lt;br /&gt;
&lt;br /&gt;
== Reference Images ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
GE Rapid Clean II (character 8).jpg|Close-up of all segments lit&lt;br /&gt;
GE Rapid Clean II (8-34).jpg|Showing the time 8:34&lt;br /&gt;
GE_Rapid_Clean_II_(9-02).jpg|Showing the time 9:09&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Fonts]]&lt;/div&gt;</summary>
		<author><name>Mxl</name></author>
	</entry>
</feed>