NextDest allows your device that has a public IPv6 address to be automatically registered and updated its DNS record.

With more and more ISPs offering public IPv6 addresses to their users, we can directly access our devices that have a public IPv6 address. But the public IPv6 address of your device can change from time to time and it's extremely hard to memorise an IPv6 address. 

Therefore we have DNS, a solution from a long time ago. Yet it's also boring and painful if we have to set the DNS record by hand.

That's why I wrote NextDest. The client-side detects the public IPv6 address on your device (by sending a request to the server-side, so everything is in your hand). Once it finds any available public IPv6 address, it will send a registration request to the server-side along with predefined device name (so that the server-side can decide which domain name should be updated). If everything works, the client-side will display the latest DNS record and the public IPv6 address of your device.

However, this is NOT the end of the story. If NextDest exposes your public IPv6 address to the Internet, your device could be attacked. 

So NextDest supports 4 modes:

1. direct mode

Internet <-> Endpoint

The real IPv6 on that device will be used for DNS resolving. 

All ports can be accessed from the Internet.

2. proxy mode

Internet <-> nextdest <-> Endpoint

An available IP will be used from your IP pool (defined in CIDR format, in the server-side config file).

Cloudflare (or other) only acts like a DNS, i.e, the real IP will show up in DNS record.

Network traffic is forwarded by NextDest.

Only ports defined in ports (in the server-side config file) can be accessed from the Internet.

3. cdn mode

Internet <-> Cloudflare <-> Endpoint

The real IPv6 on that device will be only used for DNS zone settings

All network traffic will be proxied by Cloudflare (or any equivalent), i.e, only CDN's IP will show up in DNS record.

Only 80 or 443 or other CDN provider allowed ports can be accessed from the Internet.

4. hybrid mode

It is the combination of proxy mode and cdn mode.

Project code

NextDest server-side source code: https://goth lab.com/ryza-cocoa/nextdest-server

NextDest client-side server code: https://gitlab.com/ryza-cocoa/nextdest

macOS version of Twitter Image Saver

A few days ago, I wrote a Twitter image saver with Rust, but it's a cli application, which makes it not so easy for daily use (I have to type some command to start or stop it, also need to edit the JSON config file manually). Therefore I rewrote it in GUI (to be accurate, a status bar application) on macOS.

It would be pretty easy to set up, click the Settings menu, fill in the consumer key and secret, and then select the directory where you'd like to save the images. Finally, add some accounts (without @) you interested into the list! The total number and the total size of images saved for the current session will be displayed in the drop-down menu.

Furthermore, this application's icon is the combination plus some modifications of two original ones; they were made by Anton Kalashnyk and Fauzan Adiima.

The source code of this project is on my GitLab.

Rust Learning from Zero (26) —— Save Twitter Images

I followed a few Twitter accounts that post yuri photos, and I saved some of these images when I saw them. 女の子になりたい!

However, it would be better if there is a bot that could do all this for me. And of course, applying for a Twitter developer account is a must. When your developer account is ready, you will need to generate a consumer key & secret pair.

This program will be really simple and straightforward. It reads from a config file that contains the consumer key & secret pair, as well as a list of usernames you interested in, and the path to the directory where you'd like to save images. A sample of the config file is shown below.

  "img_dir": "imgs",
  "users": [

Substitute your own consumer key & secret, and then list some users you interested in in the users array.

Also, I don't want the program to be too complex, so I decide to not use any database. If you want to add more users to the list while the program is running, then just add them into the JSON file and save it. The program will try to reload the user list before the next round of polling data.

And this program is on OwO now! https://owo.ryza.moe/. You can download and install it on Linux with only one line command. (For more details about OwO, you can refer to this post, Rust Learning from Zero (23) —— The OwO software distribution system!)

bash <(curl -sSLf https://owo.ryza.moe/twitter-img-saver/install.sh)
Continue reading Rust Learning from Zero (26) —— Save Twitter Images

Notes on Differential Equations (4) —— Step Function $H(t)$ and Delta Function $\delta(t)$

The step function $H(t)$ is always 0 until a time point $t=0$, it becomes a constant and then stay at that constant forever.

\begin{align}H(t)=\left\{\begin{aligned} 0 &\, &t \lt 0\\ 1 &\, &t \ge 0 \end{aligned}\right.\end{align}

And we can shift the $H(t)$ by $T$ time, that is $H(t-T)$.

The delta function $$\delta (t) is always 0, except for a single time point $t=0$, it has a value 1. Thus delta function is all in one instant, an impulse. Also, the delta function can be shifted by $T$ time, that is $\delta (t-T)$.

Although this is not a continuous function, this is what we do in real life. Because the deposits are always made at some specific instants.

Continue reading Notes on Differential Equations (4) —— Step Function $H(t)$ and Delta Function $\delta(t)$

Notes on Differential Equations (3) —— $\frac{dy}{dt}=ay+q(t)$

The general form for any input (or source term) with first order differential equations can be written as below, and it starts at $y(0)$.


One way to interpret this equation is to think it as a bank account, where $a$ is the interest rate and $q(t)$ is the new deposits.

$$\frac{dy}{dt}=\underbrace{ay}_\textrm{interest added}+\underbrace{q(t)}_\textrm{new deposits}$$

The general formula of the solution is

$$y(t)=\underbrace{y(0)e^{at}}_\textrm{null solution}+\underbrace{\int_{s=0}^{s=t}e^{a(t-s)}q(s)ds}_\textrm{particular solution}$$

where $s$ is the running clock, and $t$ is the time we look at it. Why the integral term has $(t-s)$ inside? Because the deposits $q(s)$ is made at time point $s$, and you only get interests after you made the deposits.

Continue reading Notes on Differential Equations (3) —— $\frac{dy}{dt}=ay+q(t)$

Notes on Differential Equations (1) —— $\frac{dy}{dx}=ay+e^{st}$

This series will be my personal notes on differential equations (including ordinary differential equation (ODE), partial differential equation (PDE) and stochastic differential equation (SDE)). These posts may contain mistakes.

The first a few posts will be different solutions for different forms of first order ODE.

The first form is $\frac{dy}{dx}=ay+e^{st}$.

Continue reading Notes on Differential Equations (1) —— $\frac{dy}{dx}=ay+e^{st}$

Rust Learning from Zero (25) —— Handle Continuous Features in Decision Tree with Rust

This post has two main purposes, 1) serves as personal notes for handling continuous features in decision tree; 2) try to use trait to add more computation operations to vectors, because the original Vec shipped with Rust is nowhere near the numpy in Python when it comes to scientific computation. (Though I know that Vec may not be designed to do handle such task.)

There are many ways to handle continuous descriptive features, such as discretions. This post will exploit weighted variance as a measurement for splitting continuous feature at a node.

The weighted variance is computed by the following equation, where $n$ is the number of rows in the data, $\mathcal{L}$ is the set of all unique labels, $D$ is the column with continuous features ($n$ rows), $p^*$ denotes the best split position in $D$.

$p^*=argmin_{p\in [1, n)} \sum_{l\in \mathcal{L}}\frac{\|\mathcal{L}_{i=l}\|}{\|\mathcal{L}\|} [var(D_{[0, p)}, \mathcal{L}_{i=l}) + var(D_{[p, n)}, \mathcal{L}_{i=l})]$

Once the algorithm decides the best split position of $D$, we can apply divide and conquer! For example, if $p^*$ has been computed, then we recursively apply this mechanism to both $D[0 .. p^*]$ and $D[p^* ..]$. When the split position arrays, $S_i$ and $S_j$, of $D[0 .. p^*]$ and $D[p^* ..]$ return, $S_i$ and $S_j$ will be merged and sorted as final value.

Let's try this algorithm on this small dataset,

Continuous DataLabel

Looks good to me! And code goes below.

Continue reading Rust Learning from Zero (25) —— Handle Continuous Features in Decision Tree with Rust

Rust Learning from Zero (24) —— Travelling Salesperson Problem using Evolutionary Approach with Rust

I believe that for almost anyone studying in computer science would know this famous problem below.

The travelling salesperson problem asks the following question: "Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city exactly once and returns to the origin city?" Wikipedia

I know that there are thousands if not millions approaches that we can use to solve this problem. But just for personal notes and demonstration, I will solve TSP using one evolutionary approach with Rust.

The idea of today's evolutionary approach (EA) is rather simple:

  1. Randomly initialise some solutions and compute cost (described as fitness in EA) respectively.
  2. Mutate one of them, compute the fitness of mutated solution. If the new fitness is worser than the last one, go step 3. Otherwise go step 4.
  3. Reject this mutation and go step 2.
  4. Accept it as a solution, discard the worst solution and repeat step 2.

For $n$ nodes in a path, there are maximum $n!$ (actually, $(n-1)!$) paths to select from. Therefore, if what we want if the global optimal, then this algorithm has to iterate all possible paths, and that gives us $O(n!)$ performance. However, EA could actually gives us a quite good suboptimal in much less time because of the way it works. One use case of EA is when we only have limited time, computation performance and we can accept suboptimal as a solution.

For example, if we have the following adjacency matrix in TSP,


Then we solve this problem with EA, the output could be as below.

Initial solution [0]: fitness => 33, seq => CABED
Initial solution [1]: fitness => 32, seq => CADBE
Initial solution [2]: fitness => 34, seq => CDEAB
Initial solution [3]: fitness => 32, seq => ABDEC
Initial solution [4]: fitness => 33, seq => ADEBC
[0] Reject mutation: fitness => 38, seq => DBEAC
[1] Reject mutation: fitness => 34, seq => CDAEB
[2] Accept mutation: fitness => 33, seq => CEADB
[2] Worst solution dropped: fitness => 34
[3] Accept mutation: fitness => 28, seq => EDABC
[3] Worst solution dropped: fitness => 33
[4] Reject mutation: fitness => 33, seq => CBDAE
[5] Reject mutation: fitness => 38, seq => ACDBE
Best solution so far: fitness => 28, seq => EDABC

As you can see, EA found quite a good solution with fitness 28, which is EDABC. Also, this is in fact the global optimal and EA actually found the global optimal in the 3rd mutation!

Continue reading Rust Learning from Zero (24) —— Travelling Salesperson Problem using Evolutionary Approach with Rust

Gambling Problem

The problem is described below:

For the match Limp Stoners vs Exmouth Breathers the two bookmakers A and C offer different odds,

Stoners WinDrawBreathers Win
A4/1 (5.00)3/1 (4.00)2/3 (1.67)
C3/1 (4.00)2/1 (3.00)1/1 (2.00)

You have £100. How do you have to place your bets in order to maximize guaranteed profit no matter what the outcome of the game?


  • One is allowed to put money on different outcomes simultaneously. So, you can bet £50 on Stoners and £20 on Draw at A, and £30 on Breathers at C.
  • The numbers for the odds mean that, for example, if you bet £1 on Stoners at BrokeLads you will get £5.00 back (your own £1 and £4 winning). The fraction format specifies how much you would win if you bet £1, the decimal format specifies how much you would get back if you bet £1 (so, it’s the same as the fraction + 1.
Continue reading Gambling Problem