Как-то сидя на работе, и занимаясь написанием кода на Go, я вспомнил про старую задачку, суть которой заключалась в том, что нам необходимо реализовать деление любого числа на заранее заданное в условии задачи. Казалось бы просто, но было два ограничения:
1. Нельзя использовать числа в явном виде, кроме 0.
2. Количество разрешённых математических операций тоже было ограничено.
Повторять её на Go я не стал, но решил применить один из вариантов решения, для отображения строки. Идея эта не нова, и на полноправное авторство не претендую, просто решил поделиться.
Суть в том, что строка разбивается на байты, а каждый байт преобразуется в последовательность побитовых сдвигов, и операций XOR и OR с единицей.
Выглядит это примерно так:
EAX = uint8(unsafe.Sizeof(true)) (((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX<<EAX^EAX)<<EAX)
И так начнём. Модуль unsafe тут нужен лишь для функции sizeof. Если кто знает как в Go получить размер структуры другими способами, буду рад если поделитесь.
import ( "fmt" "math/rand" "time" "unsafe" ) const ( EAX = uint8(unsafe.Sizeof(true)) ONE = "EAX" )
Далее нужна функция, которая будет для заданного байта, находить и генерировать такую комбинацию. Наиболее простым и очевидным вариантом было просчитать сначала шаблон, вида [0,1,1,0,1,1,…]. Где 0 — означает что число четное, 1 — число нечётное. Уменьшая каждый раз число, выполняя побитовый сдвиг вправо на 1.
func getNumber(n byte) (buf string) { var arr []byte for n > EAX { if n%2 == EAX { arr = append(arr, EAX) } else { arr = append(arr, 0) } n = n >> EAX } }
Таким образом длина среза arr у нас будет равна количеству шагов, которые необходимо выполнить, для получения исходного числа.
buf = ONE rand.Seed(time.Now().Unix()) for i := len(arr) - 1; i >= 0; i-- { buf = fmt.Sprintf("%s<<%s", buf, ONE) if arr[i] == EAX { if rand.Intn(2) == 0 { buf = fmt.Sprintf("(%s^%s)", buf, ONE) } else { buf = fmt.Sprintf("(%s|%s)", buf, ONE) } } }
Пробегая по срезу arr мы генерируем строку «сдвигов», и если встречаем нечётное число, то рандомно выполняем над этим числом операцию OR или XOR с 1.
Осталось написать функцию, которая собственно и будет заданную строку превращать в последовательность таких сдвигов. В качестве примера, я решил выводить код, исполняя который можно получить заданную строку.
func TextToCode(txt string) string { b := []byte(txt) tmp := "var str []byte\n" for _, item := range b { tmp = fmt.Sprintf("%s\nstr = append(str, %s)", tmp, getNumber(item)) } tmp += "\nfmt.Println(string(str))" return tmp }
Вот собственно и всё. После запуска, для строки: «Author: GH0st3rs» получаем следующий результат:
var str []byte str = append(str, (EAX<<EAX<<EAX<<EAX<<EAX<<EAX<<EAX^EAX)) str = append(str, ((((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX^EAX)<<EAX<<EAX^EAX)) str = append(str, (((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX^EAX)<<EAX<<EAX) str = append(str, ((EAX<<EAX^EAX)<<EAX<<EAX|EAX)<<EAX<<EAX<<EAX) str = append(str, (((((EAX<<EAX^EAX)<<EAX<<EAX|EAX)<<EAX^EAX)<<EAX^EAX)<<EAX|EAX)) str = append(str, (((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX<<EAX^EAX)<<EAX) str = append(str, (((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX^EAX)<<EAX) str = append(str, EAX<<EAX<<EAX<<EAX<<EAX<<EAX) str = append(str, EAX<<EAX<<EAX<<EAX<<EAX<<EAX<<EAX) str = append(str, (((EAX<<EAX<<EAX<<EAX<<EAX^EAX)<<EAX|EAX)<<EAX^EAX)) str = append(str, (EAX<<EAX<<EAX<<EAX^EAX)<<EAX<<EAX<<EAX) str = append(str, (EAX<<EAX^EAX)<<EAX<<EAX<<EAX<<EAX) str = append(str, ((((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX<<EAX^EAX)<<EAX^EAX)) str = append(str, (((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX^EAX)<<EAX<<EAX) str = append(str, (((EAX<<EAX^EAX)<<EAX<<EAX<<EAX|EAX)<<EAX^EAX)) str = append(str, (((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX<<EAX^EAX)<<EAX) str = append(str, ((((EAX<<EAX^EAX)<<EAX|EAX)<<EAX<<EAX<<EAX^EAX)<<EAX^EAX)) fmt.Println(string(str))
» Исходный код проекта доступен на GitHub
ссылка на оригинал статьи https://habrahabr.ru/post/313486/
Добавить комментарий