ExcludeNodes の設定を半自動化
追記
ExcludeNodes, ExcludeExitNodes の設定は参考サイトから丸パクリして載せていましたが, 消したようなので消しました.
このプログラムは, BadExit の付いたノードが一つも存在しない場合の処理が抜けています.
気が向いたら直しておきます.直しました.
ここから記事スタート
最近 Tails を使っている。 ExcludeNodes に BadExit フラグが付いたノードを追加するのが面倒だったので、半自動化した。 cURL と Python を使用した。
現在のノードのリストはここで確認できる (JSON):
https://onionoo.torproject.org/details
cURL で JSON を取得し、BadExit が付いたノードの fingerprints をすべて torrc に突っ込む。
これを実行すると torrc にそのままコピペできる出力が得られる:
#!/bin/sh URL='https://onionoo.torproject.org/summary?flag=badexit' echo 'ExcludeNodes SlowServer'|tr -d '\n' curl -s ${URL}|python3 -c "import sys;import json;a=['$'+r['f']for r in json.loads(sys.stdin.read())['relays']];print(len(a)and',{}'.format(','.join(a))or'')" echo 'StrictNodes 1'
そのまま使ってもよいが, 各自調整したほうがよい.
参考ウェブサイト
Enigma Machine Emulator in Python
For now, I am not going to translate this article into English because there are many web pages that explain the details about the Enigma machines in English.
The source code below might be uploaded to GitHub.
はじめに
第二次世界大戦中, ナチスドイツ軍が使用していた Enigma 暗号機のエミュレータを Python で書いた (書き直した).
以前のやつは継承とか使っていたが, バラバラにした.
Bombeを書こうと思い始めたので, リファクタリングになっているかわからないがリファクタした.
記事の最後にソースコードを置いてある.
オリジナルのリフレクタとロータの追加機能はまだテストしていない.
オリジナルのリフレクタとロータを動かせるようになった(たぶん).
Enigma に関する英語記事はたくさんあるが, 日本語ではほとんどない. ここで軽い解説を書こうと思う.
後で.
動作
この記事の最後にあるコードを実行すると次の結果が得られる.
test.py
動作確認.
$ python ./test.py Enigma machine emulator version 1.4.8 Registered reflectors: { "A": "EJMZALYXVBWFCRQUONTSPIKHGD", "B": "YRUHQSLDPXNGOKMIEBFZCWVJAT", "C": "FVPJIAOYEDRZXWGCTKUQSBNMHL", "B Thin": "ENKQAUYWJICOPBLMDXZVFTHRGS", "C Thin": "RDOBJNTKVEHMLFCWZAXGYIPSUQ" } Registered rotors: { "I": "EKMFLGDQVZNTOWYHXUSPAIBRCJ", "II": "AJDKSIRUXBLHWTMCQGZNPYFVOE", "III": "BDFHJLCPRTXVZNYEIWGAKMUSQO", "IV": "ESOVPZJAYQUIRHXLNFTGKDCMWB", "V": "VZBRGITYUPSDNHLXAWMJQOFECK", "VI": "JPGVOUMFYQBENHZRDKASXLICTW", "VII": "NZJHGRCXMYSWBOUFAIVLPEKQDT", "VIII": "FKQHTLXOCBJSPDZRAMEWNIUYGV", "Beta": "LEYJVCNIXWPBQMDRTAKZGFUHOS", "Gamma": "FSOKANUERHMBTIYCWLQPZXVGJD" } Registered plugboards: { "Plugs": "KHYDVGFBQRALPNSMIJOTZEWXCU" } wirings: { "reflector": { "name": "C Thin", "wiring": "RDOBJNTKVEHMLFCWZAXGYIPSUQ", "pairs": { "A": "R", "B": "D", "C": "O", "E": "J", "F": "N", "G": "T", "H": "K", "I": "V", "L": "M", "P": "W", "Q": "Z", "S": "X", "U": "Y" } }, "rotors": [ { "name": "Gamma", "wiring": "FSOKANUERHMBTIYCWLQPZXVGJD", "turnover_position": [], "initial_position": "T", "ring_position": "M" }, { "name": "III", "wiring": "BDFHJLCPRTXVZNYEIWGAKMUSQO", "turnover_position": [ "V" ], "initial_position": "P", "ring_position": "S" }, { "name": "VIII", "wiring": "FKQHTLXOCBJSPDZRAMEWNIUYGV", "turnover_position": [ "Z", "M" ], "initial_position": "X", "ring_position": "F" }, { "name": "II", "wiring": "AJDKSIRUXBLHWTMCQGZNPYFVOE", "turnover_position": [ "E" ], "initial_position": "J", "ring_position": "Z" } ], "plugboard": { "name": "Plugs", "wiring": "KHYDVGFBQRALPNSMIJOTZEWXCU", "pairs": { "A": "K", "B": "H", "C": "Y", "E": "V", "F": "G", "I": "Q", "J": "R", "M": "P", "O": "S", "U": "Z" } }, "keeps_initial_state": false } initial positions of the rotors: [ "T", "P", "X", "J" ] plain: I really don't know why it works. formatted: IREALLYDONTKNOWWHYITWORKSX encoded: NHTFFMGYHDEMIQUTGXQSMKBDHG decoded: IREALLYDONTKNOWWHYITWORKSX current positions of the rotors: [ "T", "P", "Y", "J" ]
original.py
オリジナルの設定ができる. 今回は3ロータで配線はランダムにしたので実行毎に結果が変わる.
$ ./original.py 3 wirings: { "reflector": { "name": "original reflector", "wiring": "PDTBJRNXOEYVWGIAUFZCQLMHKS", "pairs": { "E": "J", "K": "Y", "C": "T", "Q": "U", "M": "W", "S": "Z", "A": "P", "F": "R", "L": "V", "G": "N", "H": "X", "I": "O", "B": "D" } }, "rotors": [ { "name": "original rotor 1", "wiring": "IXROUDJVGEHKATFPZMBQSNWLCY", "turnover_position": [ "A", "X" ], "initial_position": "A", "ring_position": "E" }, { "name": "original rotor 2", "wiring": "DQPWVZOYCBTHIMJSUFAERKNGLX", "turnover_position": [ "D", "W" ], "initial_position": "Q", "ring_position": "J" }, { "name": "original rotor 3", "wiring": "HRCNFKAPYTQJSZUOXMGVDWBEIL", "turnover_position": [ "Z", "A" ], "initial_position": "U", "ring_position": "T" } ], "plugboard": { "name": "original plugboard", "wiring": "DYGAJRCIHETVWSXQPFNKZLMOBU", "pairs": { "F": "R", "P": "Q", "C": "G", "K": "T", "B": "Y", "A": "D", "E": "J", "O": "X", "M": "W", "U": "Z", "H": "I", "L": "V", "N": "S" } }, "keeps_initial_state": true } initial positions of the rotors: [ "A", "U", "K" ] plain: Ich weiss wirklich nicht, warum sie funktioniert. formatted: ICHWEISSWIRKLICHNICHTWARUMSIEFUNKTIONIERTX encoded: QNAIBHJYPTWZNUEPUYYIOKFUKTBYAUBIZMCNJMDDKV decoded: ICHWEISSWIRKLICHNICHTWARUMSIEFUNKTIONIERTX
Source code
enigma/__init__.py
# -*- coding: utf-8 -*- """ Enigma machine emulator Copyright (C) 2017, 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from . import component from . import emulator PROG_NAME = 'Enigma machine emulator' VERSION = '1.4.8' AUTHOR = 'Vanaestea' LICENSE = 'MIT'
enigma/component.py
# -*- coding: utf-8 -*- """ Enigma machine emulator Copyright (C) 2017, 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from abc import ABC, abstractmethod from collections import Counter from operator import itemgetter import string _MOD_BASE = len(string.ascii_uppercase) class BaseComponent(ABC): _shifts_in = {} _shifts_out = {} _wirings = {} def __init__(self): self._name = None self._wiring = None self._shift_in = None self._shift_out = None @abstractmethod def _set_shifts(self): pass @abstractmethod def set(self): pass @abstractmethod def add(self): pass @abstractmethod def encode(self): pass @abstractmethod def decode(self): pass @property def wiring(self): return self._wiring @property def name(self): return self._name @classmethod def get_list(cls): return cls._wirings class Rotor(BaseComponent): _wirings = { 'I': 'EKMFLGDQVZNTOWYHXUSPAIBRCJ', 'II': 'AJDKSIRUXBLHWTMCQGZNPYFVOE', 'III': 'BDFHJLCPRTXVZNYEIWGAKMUSQO', 'IV': 'ESOVPZJAYQUIRHXLNFTGKDCMWB', 'V': 'VZBRGITYUPSDNHLXAWMJQOFECK', 'VI': 'JPGVOUMFYQBENHZRDKASXLICTW', 'VII': 'NZJHGRCXMYSWBOUFAIVLPEKQDT', 'VIII': 'FKQHTLXOCBJSPDZRAMEWNIUYGV', 'Beta': 'LEYJVCNIXWPBQMDRTAKZGFUHOS', 'Gamma': 'FSOKANUERHMBTIYCWLQPZXVGJD', } # A turnover position is one character ahead of the real one _turnovers = { 'I': (17,), # R 'II': (5,), # E 'III': (22,), # V 'IV': (10,), # J 'V': (0,), # Z 'VI': (0, 13), # Z+M 'VII': (0, 13), # Z+M 'VIII': (0, 13), # Z+M 'Beta': (), # nothing 'Gamma': (), # nothing } def __init__(self): super().__init__() self._turnover = None self._initial_shift = 0 self._shift = 0 self._ring = 0 self._fixed = False def _set_shifts(self, name): self._shifts_in[name] = tuple(ord(ch) - ord('A') for ch in self._wirings[self._name]) self._shifts_out[name] = tuple(i for i, _ in sorted(enumerate(self._shifts_in[name]), key=itemgetter(1))) def set_with_shift(self, name, initial_shift, ring): if name not in self._wirings or\ initial_shift < 0 or _MOD_BASE <= initial_shift or\ ring < 0 or _MOD_BASE <= ring: return False self._name = name if name not in self._shifts_in: self._set_shifts(name) self._shift_in = self._shifts_in[name] self._shift_out = self._shifts_out[name] self._wiring = self._wirings[name] self._turnover = self._turnovers[name] self._ring = ring self._shift = self._initial_shift = initial_shift self._fixed = True if len(self._turnover) == 0 else False return True def set(self, name, initial_position, ring_position): return self.set_with_shift(name, ord(initial_position) - ord('A'), ord(ring_position) - ord('A')) def add_with_shift(self, name, wiring, turnover): if name not in self._wirings and\ Counter(wiring) == Counter(string.ascii_uppercase): for idx in turnover: if idx < 0 or _MOD_BASE <= idx: break else: self._wirings[name] = wiring self._turnovers[name] = tuple(sorted((i + 1) % _MOD_BASE for i in turnover)) return True return False def add(self, name, wiring, turnover_position): return self.add_with_shift(name, wiring, tuple(ord(pos) - ord('A') for pos in turnover_position)) def encode(self, idx, shift, ring=0): return self._shift_in[(idx + self._shift - self._ring - shift + ring) % _MOD_BASE] def decode(self, idx, shift, ring=0): return self._shift_out[(idx + self._shift - self._ring - shift + ring) % _MOD_BASE] def reset(self): self._shift = self._initial_shift def step(self): if not self._fixed: self._shift = (self._shift + 1) % _MOD_BASE def on_turnover(self, next_to_fast=False): for idx in self._turnover: if next_to_fast: if (self._shift + 1) % _MOD_BASE == idx: break elif self._shift == idx: break else: return False return True @property def initial_shift(self): return self._initial_shift @initial_shift.setter def initial_shift(self, initial_shift): if 0 <= initial_shift < _MOD_BASE: self._initial_shift = initial_shift else: raise ValueError() @property def initial_position(self): return chr(self.initial_shift + ord('A')) @initial_position.setter def initial_position(self, initial_position): self.initial_shift = ord(initial_position) - ord('A') @property def shift(self): return self._shift @shift.setter def shift(self, shift): if 0 <= shift < _MOD_BASE: self._shift = shift else: raise ValueError() @property def position(self): return chr(self.shift + ord('A')) @position.setter def position(self, position): self.shift = ord(position) - ord('A') @property def ring(self): return self._ring @ring.setter def ring(self, ring): if 0 <= ring < _MOD_BASE: self._ring = ring else: raise ValueError() @property def ring_position(self): return chr(self.ring + ord('A')) @ring_position.setter def ring_position(self, ring_position): self.ring = ord(ring_position) - ord('A') # return the real turnover shift @property def turnover(self): return (self._turnover + _MOD_BASE - 1) % _MOD_BASE # return the real turnover position @property def turnover_position(self): return tuple(chr(ord('A') + (idx + _MOD_BASE - 1) % _MOD_BASE) for idx in self._turnover) @property def is_fixed(self): return self._fixed class PairwiseComponent(BaseComponent): _wirings_pairs = {} def __init__(self): super().__init__() self._pairs = None def _is_pairwise(self, pairs): s = set() t = set(string.ascii_uppercase) for k, v in pairs.items(): if k in s or k not in t: break s.add(k) if v in s or v not in t: break s.add(v) else: return True return False def _set_pairs(self, name): shifts = self._shifts_in[name] pairs = {} s = set() for i, e in enumerate(shifts): if i == e or i in s or e in s: continue pairs[chr(i + ord('A'))] = chr(e + ord('A')) s.add(i) s.add(e) self._wirings_pairs[name] = pairs def set(self, name): if name not in self._wirings: return False self._name = name self._wiring = self._wirings[name] if name not in self._shifts_in: self._set_shifts(name) self._shift_in = self._shifts_in[name] self._shift_out = self._shifts_out[name] if name not in self._wirings_pairs: self._set_pairs(name) self._pairs = self._wirings_pairs[name] return True def add(self, name, pairs): if name in self._wirings or not self._is_pairwise(pairs): return False self._wirings_pairs[name] = dict((k, v) if k < v else (v, k) for k, v in tuple(pairs.items())) a = list(string.ascii_uppercase) for k, v in self._wirings_pairs[name].items(): i = ord(k) - ord('A') j = ord(v) - ord('A') a[i], a[j] = a[j], a[i] self._wirings[name] = ''.join(a) return True @property def pairs(self): return self._pairs @classmethod def get_pairs_list(cls): return cls.pairs class Reflector(PairwiseComponent): _wirings = { 'A': 'EJMZALYXVBWFCRQUONTSPIKHGD', 'B': 'YRUHQSLDPXNGOKMIEBFZCWVJAT', 'C': 'FVPJIAOYEDRZXWGCTKUQSBNMHL', 'B Thin': 'ENKQAUYWJICOPBLMDXZVFTHRGS', 'C Thin': 'RDOBJNTKVEHMLFCWZAXGYIPSUQ', } def _set_shifts(self, name): self._shifts_in[name] = tuple(ord(ch) - ord('A') for ch in self._wirings[self._name]) self._shifts_out[name] = None def encode(self, idx, shift, ring=0): return self._shift_in[(idx - shift + ring) % _MOD_BASE] def decode(self, idx, shift, ring=0): return encode(idx, shift, ring) class Plugboard(PairwiseComponent): def _set_shifts(self, name): self._shifts_in[name] = tuple(ord(ch) - ord('A') for ch in self._wirings[name]) self._shifts_out[name] = tuple(i for i, _ in sorted(enumerate(self._shifts_in[name]), key=itemgetter(1))) def encode(self, idx): return self._shift_in[idx] def decode(self, idx, shift, ring=0): return self._shift_out[(idx - shift + ring) % _MOD_BASE]
enigma/emulator.py
# -*- coding: utf-8 -*- """ Enigma machine emulator Copyright (C) 2017, 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from . import component import string class Enigma(object): def __init__(self, keeps_initial_state=True): self._reflector = component.Reflector() self._rotors = [] self._plugboard = component.Plugboard() self._keeps_initial_state = keeps_initial_state def set_reflector(self, name): return self._reflector.set(name) def add_reflector(self, name, wiring): return self._reflector.add(name, wiring) def append_rotor(self, name, initial_position='A', ring_position='A'): rotor = component.Rotor() if rotor.set(name, initial_position, ring_position): self._rotors.append(rotor) return True return False # deprecated def push_rotor(self, name, initial_position='A', ring_position='A'): self.append_rotor(name, initial_position, ring_position) def add_rotor(self, name, wiring, turnover=('A',)): rotor = component.Rotor() return rotor.add(name, wiring, turnover) def replace_rotor(self, index, name, initial_position='A', ring_position='A'): rotor = component.Rotor() if rotor.set(name, initial_position, ring_position): self._rotors[index] = rotor return True return False def insert_rotor(self, index, name, initial_position='A', ring_position='A'): rotor = component.Rotor() if rotor.set(name, initial_position, ring_position): self._rotors.insert(index, rotor) return True return False def clear_rotors(self): self._rotors.clear() def add_plugboard(self, name, pairs): return self._plugboard.add(name, pairs) def set_plugboard(self, name): return self._plugboard.set(name) def add_set_plugboard(self, name, pairs): if self._plugboard.add(name, pairs): return self._plugboard.set(name) return False def reset(self): for rotor in self._rotors: rotor.reset() def set_initial_positions(self, positions): positions = positions.upper() try: for rotor, pos in zip(self._rotors, positions): rotor.initial_position = pos except: return False self.reset() return True def is_text_valid(self, text): if set(text.upper()) <= set(string.ascii_uppercase): return True return False def encode(self, text): if self._keeps_initial_state: self.reset() text = text.upper() if not self.is_text_valid(text): raise ValueError('The text includes invalid characters') o = [] for ch in text: idx = ord(ch) - ord('A') # Step i = len(self._rotors) - 1 self._rotors[i].step() i -= 1 if i > 0 and (self._rotors[i + 1].on_turnover() or\ self._rotors[i].on_turnover(True)): self._rotors[i].step() while i > 0: if self._rotors[i].on_turnover(): i -= 1 self._rotors[i].step() else: break # Encode # Incoming e = self._plugboard.encode(idx) shift = 0 ring = 0 for rotor in reversed(self._rotors): e = rotor.encode(e, shift, ring) shift = rotor.shift ring = rotor.ring e = self._reflector.encode(e, shift, ring) # Outgoing shift = 0 ring = 0 for rotor in self._rotors: e = rotor.decode(e, shift, ring) shift = rotor.shift ring = rotor.ring e = self._plugboard.decode(e, shift, ring) o.append(chr(e + ord('A'))) return ''.join(o) def decode(self, text): return self.encode(text) def get_setting(self): return { 'reflector': { 'name': self._reflector.name, 'wiring': self._reflector.wiring, 'pairs': self._reflector.pairs }, 'rotors': tuple( { 'name': rotor.name, 'wiring': rotor.wiring, 'turnover_position': tuple(e for e in rotor.turnover_position), 'initial_position': rotor.initial_position, 'ring_position': rotor.ring_position, } for rotor in self._rotors ), 'plugboard': { 'name': self._plugboard.name, 'wiring': self._plugboard.wiring, 'pairs': self._plugboard.pairs, }, 'keeps_initial_state': self._keeps_initial_state, } def get_positions(self): return [rotor.position for rotor in self._rotors]
test.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import enigma from enigma import emulator from enigma import component import json def main(): print('{} version {}'.format(enigma.PROG_NAME, enigma.VERSION)) print() e = emulator.Enigma(False) e.set_reflector('C Thin') e.append_rotor('Gamma', 'T', 'M') e.append_rotor('III', 'P', 'S') e.append_rotor('VIII', 'X', 'F') e.append_rotor('II', 'J', 'Z') e.add_set_plugboard('Plugs', {'A':'K', 'B':'H', 'C':'Y', 'E':'V', 'F':'G', 'I':'Q', 'J':'R', 'M':'P', 'O':'S', 'U':'Z'}) print('Registered reflectors:') print(json.dumps(component.Reflector.get_list(), indent=4)) print('Registered rotors:') print(json.dumps(component.Rotor.get_list(), indent=4)) print('Registered plugboards:') print(json.dumps(component.Plugboard.get_list(), indent=4)) print() print('wirings:') print(json.dumps(e.get_setting(), indent=4)) print() print('initial positions of the rotors:') print(json.dumps(e.get_positions(), indent=4)) print() plain = "I really don't know why it works." formatted = plain.replace(' ', '').replace("'", '').replace('.', 'X').upper() encoded = e.encode(formatted) e.reset() decoded = e.decode(encoded) print('plain: {}'.format(plain)) print('formatted: {}'.format(formatted)) print('encoded: {}'.format(encoded)) print('decoded: {}'.format(decoded)) print() print('current positions of the rotors:') print(json.dumps(e.get_positions(), indent=4)) if __name__ == '__main__': main()
original.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import json from random import choice, shuffle from string import ascii_uppercase from enigma import emulator def main(): if len(sys.argv) != 2: sys.exit() n = int(sys.argv[1]) e = emulator.Enigma() a = list(ascii_uppercase) shuffle(a) e.add_reflector('original reflector', {a:b for a, b in zip(a[0::2], a[1::2])}) e.set_reflector('original reflector') shuffle(a) e.add_set_plugboard('original plugboard', {a:b for a, b in zip(a[0::2], a[1::2])}) for i in range(n): name = 'original rotor {}'.format(i + 1) shuffle(a) e.add_rotor(name, ''.join(a), tuple(set((choice(a), choice(a))))) e.append_rotor(name, choice(a), choice(a)) plain = 'Ich weiss wirklich nicht, warum sie funktioniert.' formatted = plain.replace(' ', '').replace(',', '').replace('.', 'X').upper() enc = e.encode(formatted) dec = e.decode(enc) print('wirings:') print(json.dumps(e.get_setting(), indent=4)) print() print('initial positions of the rotors:') print(json.dumps(e.get_positions(), indent=4)) print() print('plain: {}'.format(plain)) print('formatted: {}'.format(formatted)) print('encoded: {}'.format(enc)) print('decoded: {}'.format(dec)) if __name__ == '__main__': main()
Cで再発明せずにデータ構造を使う
たとえば C で単方向連結リストを実装する場合、ふつう入門書では
struct slist { int n; struct slist *next; };
のように実装するが、使いたいデータの型が複数ある場合、
struct slist_int { int n; struct slist_int *next; }; struct slist_double { double n; struct slist_double *next; };
のように型の数と同じだけリストの型も用意する必要がある。
void *
を使って
struct slist_generic { void *p; struct slist_generic *next; };
のようにすれば汎用的に用いることもできるが、扱いづらい。
どこが元ネタか知らないのだが、Linux kernel の struct list_head
や Windows の LIST_ENTRY
などでは少し変わった方法でこれを解決している。
次のような構造体を用いる。
struct slist_head { struct slist_head *next; };
これをリストにしたい構造体に突っ込む。たとえば、
struct slist_int { int n; struct slist_head list; };
サンプルコード:
/* slist.h for C99 or later */ #ifndef SLIST_H #define SLIST_H #include <stddef.h> #include <stdint.h> #include <stdbool.h> struct slist_head { struct slist_head *next; }; static inline void slist_init_head(struct slist_head *head) { head->next = head; } static inline bool slist_empty(struct slist_head *head) { return head->next == head; } static inline void slist_insert_front(struct slist_head *entry, struct slist_head *head) { entry->next = head->next; head->next = entry; } #define slist_entry(ptr, type, member) ((type *)((uintptr_t)ptr - offsetof(type, member))) #define slist_for_each(iter, head) \ for (iter = (head)->next; \ iter != (head); \ iter = iter->next) #define slist_for_each_safe(iter, tmp, head) \ for (iter = (head)->next, tmp = iter->next; \ iter != (head); \ iter = tmp, tmp = iter->next) #endif /* !SLIST_H */
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "slist.h" struct slist_int { int n; struct slist_head list; }; int main(void) { struct slist_head head; struct slist_head *iter, *tmp; struct slist_int *entry; int i; slist_init_head(&head); for (i = 0; i < 10; i++) { struct slist_int *new = malloc(sizeof(*new)); new->n = i; slist_insert_front(&new->list, &head); } if (slist_empty(&head)) puts("never reached"); slist_for_each(iter, &head) { entry = slist_entry(iter, struct slist_int, list); printf("%d -> ", entry->n); } puts("NULL"); slist_for_each_safe(iter, tmp, &head) { entry = slist_entry(iter, struct slist_int, list); free(entry); } return 0; }
解説が必要なのは slist_entry
だと思うのでこれを解説するが、container_of
か CONTAINING_RECORD
でググったほうがわかりやすいかもしれない。
#define slist_entry(ptr, type, member) ((type *)((uintptr_t)ptr - offsetof(type, member))) struct slist_int *ptr_slist_int = slist_entry(ptr_slist_head, struct slist_int, list);
はこんな感じに展開される(括弧は省略):
struct slist_int *ptr_slist_int = (struct slist_int *)((uintptr_t)ptr_slist_head - (size_t)&((struct slist_int *)0)->list);
ptr_slist_head
から struct slist_int
のメンバ struct list_head
のオフセットを引いている。
よって、ptr_slist_head
が struct slist_int
に組み込まれた struct slist_head
へのポインタならば、*ptr_slist_head
をメンバに含む struct slist_int
のアドレスを得ることができる(日本語不自由)。
頭いい。
C++ では std::forward_list
があるらしい。
以上。以下、C89 以前用:
/* slist.h for C89 and earlier */ #ifndef SLIST_H #define SLIST_H #include <stddef.h> struct slist_head { struct slist_head *next; }; #define slist_init_head(head) \ ((head)->next = (head)) #define slist_empty(head) \ ((head)->next == (head)) #define slist_insert_front(entry, head) \ do { \ (entry)->next = (head)->next; \ (head)->next = (entry); \ } while (0) #define slist_entry(ptr, type, member) ((type *)((char *)ptr - offsetof(type, member))) #define slist_for_each(iter, head) \ for (iter = (head)->next; \ iter != (head); \ iter = iter->next) #define slist_for_each_safe(iter, tmp, head) \ for (iter = (head)->next, tmp = iter->next; \ iter != (head); \ iter = tmp, tmp = iter->next) #endif /* !SLIST_H */
もう少しいい感じにマクロが使える言語がほしい(いい感じ とは)。
M_PI は C の標準仕様ではない | M_PI Is Non-Standard in C
GCC または Clang で M_PI
を使う場合、 _XOPEN_SOURCE=500
またはより一般的な機能選択マクロが定義されていなければならない。
デフォルトでは定義されているので問題なく使える。
M_PI
is defined by default with GCC or Clang, so you can use it without any problems.
M_PI
Pi, the ratio of a circle’s circumference to its diameter.
...
These constants come from the Unix98 standard and were also available in 4.4BSD; therefore they are only defined if
_XOPEN_SOURCE=500
, or a more general feature select macro, is defined. The default set of features includes these constants. See Feature Test Macros.
VC++ で M_PI
を使うには、math.h
をインクルードする前に _USE_MATH_DEFINES
を定義する必要がある (ソースコードを引用する方法がわからない):
To use M_PI
with VC++, you must define _USE_MATH_DEFINES
before including math.h
(I don't know how to quote source code):
#define _USE_MATH_DEFINES // for C++ #include <cmath> #define _USE_MATH_DEFINES // for C #include <math.h>
...
Math Constants are not defined in Standard C/C++. To use them, you must first define _USE_MATH_DEFINES and then include cmath or math.h.
まあ、気になるのであれば、こんな感じにすればたぶん大丈夫:
If you are concerned about portability, you may want to define it like:
/* #ifdef M_PI #undef M_PI #endif */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* !M_PI */
宿題で間違ったのでメモ。
The Meaning of My (User)name vanaestea
My name means there's no meaning in it.
米語における関係代名詞主格の which と that の使い分け
関係代名詞主格制限用法で先行詞が人でない場合は which か that を使うことを学校で習うが、フォーマルな米語 (written English) ではつねに that を使うことが推奨されている (例: The Chicago Manual of Style)。
これで制限用法 (that) と非制限用法 (which) が見分けやすくなる。
English relative clauses - Wikipedia (permalink)
自分は無意識に使い分けていたが、米語限定とは知らなかった。
このまま米語でやって行っていこうか迷う。 正直スペルとか表現とか好きなところを自分で選びたいが、向こうの人からしたら変な奴に見えるかもしれない (すでに変な奴なので気にする必要はないのかもしれないが)。