JSで自然数の加法（ネタ）


#なにこれ

2+3は何故5になるのだろうか？
数は0, 1, 2, 3, 4, 5, 6, 7, 8, 9という10種類の記号で表現されているが
それはいろんな奇跡が重なってそうなっているだけで
例えば A, あ, C, $, %, 2で定義された世界があってもよいのでは？
その場合、加法はどうなるの？そもそも加法ってなんだ？
なんで2+3は5なんだ？んあー
ということで数を足すプログラムを書きました。

なお私は数学初心者なので殴られると死にます。

#んにゃぴ…

加法の性質として、何を足しても足した数自身になる単位元の存在がある。
これをここではZeroと呼ぶことにする。
んで、ある数aには後者の数S(a)が存在する。
後者の数は S(x) = x + One という数式で求められる。
こいつにZeroからスタートして得られた数に再帰的に適用して得られる数列が自然数という（多分）
んで、関数Sを用いて次のように加法が定義されている。

```math
a + Zero = a\\
a + S(b) = S(a) + b
```

すると我々が知るところの自然数では

```math
\begin{align}
2 + 3 &= 2 + S(2)\\
&= S(2) + 2\\
&= 3 + 2\\
&= 3 + S(1)\\
&= S(3) + 1\\
&= 4 + 1\\
&= 4 + S(0)\\
&= S(4) + 0\\
&= 5 + 0\\
&= 5
\end{align}
```
てな感じになる。

上記の流れを実装したのが以下のプログラム

#プログラム

```javascript
function Num(char) {
    this.char = char;
    this.prevNum = null;
    this.nextNum = null; 
}

const nums = {};
let zero = null;
let one = null;

// 絵文字禁止
function initNums(str) {
    if (str.length < 2) throw new Error("2つ以上の数を定義してね。");

    for (let char in nums) delete nums[char];

    for (let i = 0; i < str.length; i++) {
        const char = str[i];
        const num = new Num(char);
        nums[char] = num;

        if (i === 0) zero = num;
        else if (i === 1) one = num;

        if (i !== 0) {
            const prevNum = nums[str[i - 1]];
            num.prevNum = prevNum;
            prevNum.nextNum = nums[char];
        }
    }

    const maxNum = nums[str[str.length - 1]];
    zero.prevNum = maxNum;
    maxNum.nextNum = zero;
}

function addBaseNums(char1, char2) {
    let num1 = nums[char1];
    let num2 = nums[char2];

    if (num1 === zero) return num2.char;
    if (num2 === zero) return num1.char;

    let didCarry = false;
    do {
        num1 = num1.prevNum;
        num2 = num2.nextNum;

        if (num2 === zero) {
            didCarry = true
        }
    }
    while (num1 !== zero);

    return didCarry ? one.char + num2.char : num2.char;
}

function addNums(str1, str2) {
    let index1 = str1.length - 1;
    let index2 = str2.length - 1;

    let sumStr = "";
    let char1 = str1[index1];
    let char2 = str2[index2];
    let carryChar = zero.char;
    while (true) {
        let sumStrPart = addBaseNums(char1, char2);
        if (carryChar !== zero.char) {
            sumStrPart = addNums(sumStrPart, carryChar);
            carryChar = zero.char;
        }
        if (sumStrPart.length === 2) {
            carryChar = sumStrPart[0];
            sumStrPart = sumStrPart[1];
        }
        sumStr = sumStrPart + sumStr;

        if (index1 === 0 && index2 === 0) {
            if (carryChar !== zero.char) {
                sumStr = carryChar + sumStr;
            }
            break;
        }

        char1 = index1 > 0 ? str1[--index1] : zero.char;
        char2 = index2 > 0 ? str2[--index2] : zero.char;
    }

    return sumStr;
}

// ---

initNums("0123456789");

console.log(addNums("123", "456")); // 579
console.log(addNums("57812", "45663")); // 103475
console.log(addNums("999", "1")); // 1000

initNums("0123456789ABCDEF");

console.log(addNums("ABC", "123")); // BDF
console.log(addNums("FFFF", "1")); // 10000

initNums("あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん");

console.log(addNums("おまえのことか", "すきたつたんたよ")); // すさんなるけもろ

```

#チラ裏

ニコニコ大百科に言いたいことが書いてあった。

>もっとも、1,2,3・・・というのは人間の与えた記号であり、十進法で数える必然性もない。お好みならば適当な記数法を開発して自分で勝手に数えるのも一興。要は自然界のものと並べて「一対一で対応が取れる」という性質そのものが重要である。

（引用元：https://dic.nicovideo.jp/a/%E8%87%AA%E7%84%B6%E6%95%B0）

どうでもいいけど、上の加法プログラムはメモ化とか各桁の計算を非同期化すれば速くなるかもね

#参考にしたサイト
[加法(Wikipedia)](https://ja.wikipedia.org/wiki/%E5%8A%A0%E6%B3%95)
[後者関数(Wikipedia)](https://ja.wikipedia.org/wiki/%E5%BE%8C%E8%80%85%E9%96%A2%E6%95%B0)