исследования по обратимыМ вычисленияМ

Биткоин — криптовалюта на основе алгоритма хэширования SHA256. Новые монеты вводятся майнерами (добытчиками, шахтёрами), майнинг биткоинов бывает одиночным (SOLO) и групповым (POOL). Для добычи одной монеты, ценой в несколько сотен или тысячи долларов, нужно найти магическое число, вместе со служебными данными заголовка блока дающее хэш-значение меньше текущей сложности добычи. Если такое число найдено, то получен новый биткоин, который можно эммитировать.

Просто говоря, магическое число склеивается в строку с данными заголовка блока и от этой строки считается значение хэш-функции, то есть сложнообратимой функции. Полученное хэш-значение, читаемое как большое число, должно оказаться меньше заданного, именуемого текущей сложностью. Это достигается нулевыми битами хэш-значения.

Соло-майнинг биткоинов запрещён законом Украины, статьёй о незаконной эмиссии электронных денежных знаков. Но не запрещён законами многих других стран. При соло майнинге добытчик сам вводит добытые биткоины в оборот, через связь с электронным кошельком биткоинов. Это и есть эмиссия электронных денег, которая запрещена частным лицам законом, без договоров с банками.

Пул-майнинг биткоинов не подпадает под запрет, если добытчик участвует в пуле компании, находящейся в стране без банковского регулирования биткоинов, например в Чехии. При пул-майнинге перебор биткоинов ведётся многими, соединёнными в сеть, добытчиками одновременно. Добытые биткоины делятся между добытчиками и хозяином пула. При пул-майнинге по протоколу Stratum добытчики не эмитируют биткоины самостоятельно, этого нет в сетевом протоколе и функциях программ-майнеров. Добытые биткоины вводит в оборот программное обеспечение хозяина пула. Добытчики лишь предоставляют вычислительные мощности пулу, а уже пул эмитирует добытые электронные деньги.

Так как эмиссия биткоинов в Чехии не подлежит банковскому регулированию, то получается законная схема работы на чешскую компанию, занимающуюся законной деятельностью — майнингом биткоинов. Украинские майнеры учавствуют в законой деятельности чешской компании, не эмитируя электронных денег вообще. Эмитирует, вполне законно, чешская компания. Легально, как двухпалатный парламент.

Добыча биткоинов обычно осуществляется перебором возможных значений магического числа. Каждая проба значения делается в надежде, что полученный хэш будет меньше сложности. После неудачной пробы берётся следующее значение и тоже пробуется. И так — миллиарды, а то и триллионы раз, с максимально — возможной скоростью.

Устройства добычи биткоинов эволюционировали от обычных программок для персональных компьютеров до программ для быстрых графических видеокарт, потом до ASIC-майнеров (Application Specific Integrated Curcuits, электронные схемы, ориентированные специально на приложение, на счёт хэш-функции). Я работал и на компанию, у которой стойками со специальными процессорами для добычи биткоинов были заставлены несколько больших цехов крупнейшего предприятия в городе. Это — одна из крупнейших биткоин-ферм в мире, несколько собранных украинскими спецами суперкомпьютеров.

Алгоритм добычи биткоинов, использующийся в майнерах, предельно прост: полный перебор возможных значений магического числа. Полный перебор это самый «тупой» алгоритм в информатике. В проекте Black Emulator делалась попытка решать задачу добычи биткоинов не «тупо», перебором, а «интеллектуально», обратным логическим выводом.

Обратный логический вывод тут заключался в проведении расчётов хэш-фукции в обратную сторону, от хэш-значения, результата, до аргументов, магического числа, которое надо найти для эмиссии биткоина. Проведение расчётов от результата к аргументам называется обратными вычислениями.

Обратимые вычисления это вычисления, которые проводятся в обе стороны: от аргументов к результату, прямо, и от результата к аргументам, обратно. Логически обратимые вычисления проводятся на обычных необратимых компьютерах. Обратимые же процессоры компилируются в схемы из обратимых логических вентилей, реализуются по-другому на схемотехническом уровне, могут проводить вычисления прямо и обратно на уровне схемы процессора. Обратимые процессоры проектировались из-за почти нулевого выделения ими тепла и потребления электроэнергии, что важно например в суперкомпьютерах с десятками тысяч процессоров и миллионными счетами за электроэнергию.

Идея проекта Black Emulator заключалась в проектировании алгоритма обратного вычисления магического числа, обратным выполнением функции хэширования SHA256, без полного перебора возможных значений магического числа. Корректное обратное выполнение функции хэширования позолило бы находить новый биткоин за миллисекунды, на ноутбуке, вместо двадцати минут пулом майнеров.

Для этого нужно было выполнять программы в обратную сторону. Вместо выполнения сверху-вниз надо выполнять снизу вверх. Вместо выполнения слева-направо надо выполнять справо-налево. Вместо перехода вверх надо делать переход вниз. И так далее. Для этого проектировались и обратимые ассемблеры, то есть простые языки программирования, программы на которых — логически обратимы.

Архитектура обратимого эмулятора основана на двух режимах работы каждой инструкции обратимого ассемблера. Режим прямой работы не отличим от обычного ассемблера. Режим обратной работы используется при обратном выполнении программы и выполняет операцию наоборот, по сравнению с прямой работой. То есть если в прямой работе инструкция складывает, то в обратной — вычитает. Если в прямой работе инструкция умножает, то в обратной — делит. И так далее, для всех возможных математических действий и основных машинных операций.

Обратимый эмулятор выполняет программу инструкция за инструкцией, выполняя прямое действие каждой инструкции при прямом выполнении, и обратное действие инструкций при обратной работе.

Обратимый ассемблер расширяет язык программирования Си-Шарп своими коммандами. Программы на обратимом ассемблере — обратимы.

Обратимый отладчик позволяет находить ошибки на этапе написания обратимых программ. Кроме прямого выполнения программы он поддерживает обратное выполнение любых фрагментов программы, выполнение фрагмента «снизу-вверх».

Это — все технические идеи проекта Black Emulator, опустившего криптовалюту BitCoin этим летом. После этого технического вступления я перейду к художественной части статьи, переключившись с технического языка на более простой и понятный читателю.

Начало, 3 июня. Двойная норма психостимуляторов Pit Bull, кофеина и витаминов, литр вместо половины, пол пачки сигарет, идея выполнять программы в обратную сторону, от результата к аргументам. Это позволит считать хэши от результата к аргументам и добывать биткоины на ноутбуке. Быстро иду домой и начинаю проектирование алгоритмов.

Изучаю компьютерную схемотехнику за три дня по книге Харрис Харрис Компьютерная Схемотехника — давно не читал маха мантры Харе Кришна, из-за дефектов в этике кришнаитов. Продумываю идеи вплоть до компиляции программ в обратимые схемы из обратимых двоичных вентилей. Остановился на логически-обратимых вычислениях и программах на обратимом ассемблере. Взял ассемблер процессора с мягким ядром Lattice Mico 32, так как в нём мало инструкций и его проще перепроектировать в обратимый. Заменил психодепрессанты на более слабые, сославшись на надобность работать программистом. Вместо блокирования половины мозга они блокировали треть

3 июня — начало летней катастрофы на курсе биткоин в долларах. Бумажный контракт запоздал на несколько недель, первую версию с подпиской о неразглашении скрытым тесктом я порвал, похуярили кулаками в стену ругаясь со слюной, но принудить подписать не смогли — нельзя подписать то, чего нет

По второй версии контракта с десятками пунктов и шифров я должен был исполнять программы по четыре часа в день, ну типа робота, давно привык и да и все тут так, живём в киберобществе. Вторая версия — вроде без подписки, подписал, отправил, на недорогую жизнь в сумме со всем остальным хватает. Необходимое есть.

Редко смотрю на курс биткоина, чтобы не убить валюту, а заработать на обратимом майнере. Программирую по 12 часов в день, с перерывами только на покурить и попить чаю. Спроектировал обратимый ассемблер, исписав четыреста листов A4 формулами. Плохо смотрится. Беру другую архитектуру процессора и переписываю обратимый ассемблер, вторая версия. Тоже получается не очень.

Прошло уже два месяца, формулы и код, по 12 часов в день. Ежедневно заходит на чай местный районный бомж, приставленный стукачок психиатрической службы, психолог — самоучка. Говорю, что ищу число. Так как чая, как и унитаза, у него нет, он ссыт в трёхлитровую банку, то уходит довольным, и строчит положительные отчёты, меня никто не трогает.

Вторая версия обратимого ассемблера тоже не очень, но уже что-то работает. Проектирую третий ассемблер, устав исписывать тетради формулами. Решил подглядывать в англоязычную книгу Intoduction to Reversible Computing – введение в обратимые вычисления. По совету выбрал понятные мнемоники ассемблерных команд, встраивание в язык Си Шарп вместо отдельного компилятора. Иногда программирую по 14 часов в день, до тысячи строк кода в день. Мне сообщают, что результаты докладываются на конференции по компьютерной безопасности.

Третья версия получилась обратимой, даёт оценки добычи биткоина в 10 миллисекунд, вместо двадцати минут пулом с тысячами участников.

3 сентября, боль в голове, первая атака. Не могу стоять на ногах, падаю с ног. Сутки боли в голове и начало допросов внутренним голосом. Конец обвала биткоина на графике.

Резка пяток на кровати в комнате, телемедицинские надрезы с вопросами дознавателя, с утра и до вечера, так — неделя. Следствие — дистанционное, следователи выходят из своего тела и входят в моё, как в техниках выхода из тела школы инструктора Радуги. Ходят в моём теле по квартире, говорят моим ртом, кривляются в моём теле перед зеркалом. Непрерывно допрашивают и глумятся.

Теленадрезы шейных позвонков для передавливания сосудов, идущих к мозгу, вместо кулька на голове. Мозг без кислорода частично отмирает, появляются глубокие морщины на лбу, тупею и мне достаточно страшно. Несколько дней непрерывной телемедицинской резки шейных позвонков. Частичная травма спинного мозга в результате, я парализован и без оператора в моём теле не могу пошевелится. Оператор ходит в моём теле, полностью чувствует себя мной и управляет моим телом. Я старею из-за полученного эндогенного повреждения головного мозга, лоб — в глубоких морщинах, я тупой, не могу писать без ошибок, ошибки в каждом слове, гарантированное повреждение мозга. Месяц пыток и допросов закончен.

Судебно-психиатрическая экспертиза тоже дистанционная, психиатр-эксперт выходит из своего тела и входит в моё. Ходит по комнате раздетым в моём теле и дрочит, изучает пункт об извращении полового инстинкта. Раздвигает в моём теле жопу, наклонившись перед зеркалом, проверяет, может меня это возбуждает. Замеряет диаметр жопы в сантиметрах, может я – пидор.

Пугают тесты на диссоциативное растройство личности, когда несколько личностей управляют моими действиями и говорят моим ртом и между собой. Программы, чат-боты. Пугают тесты на деперсонализацию, когда полностью понимаешь, что оператор пребывает в твоём теле и каждое движение твоих рук — его, а не твоё, ты — его кукла. В книгах по внетелесным переживаниям этого нет Я — подъэкспертный, он — эксперт, хоть и пидор.

Компьютер забэкдорен, как бывший хакер я это вижу, в искусственно-вызванном состоянии безумия вынуждают покрасить программу, модификация психологического теста Люшера. Выбираю фруктовые и крашу в красный и жёлтый, в состоянии безумия ничего другого не остаётся. Проверки десятков психопатологий, в искусственном полубезумном состоянии и деперсонализированным. Зима, три месяца экспертизы окончены. Когда падаю с ног, меня откачивают телемедики.

Иду в дневной стационар добровольно, чтобы получить меньший термин, семь видов депрессантов в сумме, с трудом говорю. Ссусь по ночам от депрессантов, не реагирую, уменьшают дозу. В названии одного из депрессантов — нарушения цветовосприятия скрытым текстом. Результаты экспертизы передали заведующей стационара, ставит эндогенное повреждение могза, хотя эндогенное из-за экзогенного. Лечусь вместе с музыкантшой и изобретателем-придурком. Нормально, без клеток.

Ставлю незапоминаемые пароли на сайты с проектом и киберобществом, чтобы не удалили. С трудом, но уже пишу на русском, нормальные статьи. Ёбнутые на голову родители заявляют: «тебе надо в психо-неврологический стационар, ты работал после него». Спорить с санитарами бригады скорой помощи в теле отца и метери не берусь, всё-равно симитируют приступ и отвезут, соглашаюсь на условии, что сигареты - на каждый день. Минимальный срок при добровольном согласии — в два раза меньше.

Крыша:

- Кем хочешь заехать?

- Авторитетом.

- Ладно.

Отец привозит на машине, врач:

- Чо тебя привезли?

- Не знаю.

- Будешь принимать снотворное, выйдешь через десять дней.

Вышел бы через десять дней, но на рентгене нашли бронхит курильщика, лечили, в итоге вышел через 18 дней.

Первые шаги по клетке — достаточно страшные. Идиот то-ли танцует, крича, то-ли бегает размахивая руками, мрачно и страшно. Пачка сигарет в кармане, иду находить общий язык с авторитетами.

- Дима, не тупой по-английски, перестановка букв.

- Медведь, сигареты не раздавай.

- Ясно.

- Мне банк десять лямов должен. Сможешь перевести?

- Такую сумму не потяну. Я финансами сейчас не занимаюсь, жёстко ебут.

- СБУ?

- Да.

- Молчи.

Утром — мантры, физические упражнения. Отжимаюсь от бетона по четыре подхода, качаю пресс, отжимаюсь между железными кроватями, качаю бицепсы железной кроватью. Три недели в наблюдательной палате. Мне отдают свою кашу, меняю борщь с депрессантами на хлеб. Читаю свой рэп речетативом на курилке. Меняю пачка-на-пачку сигареты. Всё нормально.

Идиот-нарцисс. Между беганьем с размахиванием руками и воплями раздевается догола без одеяла и мастурбирует с гримасами. Развлекает меня этим порно на кровати напротив. Иногда начинает отколупывать штукатурку со стены метрами и есть.

Красавец-педофил-педераст. Всё время меряет чужие модные вещи. Ложится к пациентам в кровати полежать, потрогать, трогает «за писю» четырнадцатилетнего безпризорника.

Буйный. Крассивый, молодой, спортивный, но без мозгов и с компьютерной речью и чипом в затылке. Всех доёбывает и заёбывает своими примитивными придирками. Его привязывают и колят. У него вылазит язык, течёт кровь изо рта, вылазят из орбит глаза от уколов, синеют руки. Он коченеет и лежит, выгнувшись вверх и окоченевши. После, с вылезшим изо рта языком и стекающей слюной от уколов и таблеток, он мне пишет на бумаге, так как не может говорить.

Наркоман. Наглотавшись таблеток, купленных через решётку, всю ночь орёт: «Есть я и галлюцинация»

Малолетний олигофрен. Взят беспризорником с вокзала, заехал аж из хмельницкой области, побираясь в вагонах. Изогнут сколиозом. Интелект мартышки.

Опущенные черти. Становятся на колени, прося окурок. Ходят со спущенными штанами, в зимних шапках-ушанках мужиков.

Мужики. Ходят весной в зимних шапках-ушанках, изображая мужиков.

Сидельцы. По 17 лет в дурдоме, кто из-за чего, кто квартиру продал, кто — ещё чего, постарели в дурдоме. Жалуются на чипы и эксперименты на мозге.

18 дней. Освобождаюсь, родители забирают на машине. Врач настаивает на депрессантах вместо стимуляторов Поддакиваю, и меня выписывают.

Сейчас сижу за ноутбуком с бритой головой, восстанавливаюсь после следствия и экспертизы, как могу, набираю эту повествовательную статью. Может быть, Вы хотели бы знать, есть ли КУКЛ? Посмотрите на улицу.

троичная логика

Исторически, логика развивалась как наука о суждениях, их истинности и алгоритмах словесного мышления. Изучались правила вывода новых истинных суждений из имеющихся, критики сомнительных суждений, построения доказательств, выдвижения гипотез, не точные методы логического вывода и искусства риторики и полемики.

В классической логике высказываний каждому суждению приписывалось значение истинности, 1 = истина, 0 = ложь. Кроме суждений с известными значениями истинности рассматривались логические связки AND, OR, XOR, IMPLY, для построения сложных суждений из простых. Изучались около десяти законов логики - правил вывода истинных суждений из имеющихся истинных, записываемых в нотации с большой горизонтальной чертой. Демонстрации доказательств записывались в виде дерева логического вывода, листья которого соответствовали фактам, истинным суждениям, а корень дерева - окончательному выводу из фактов. Связям между вершинами дерева логического вывода соответствовали правила вывода, аналогично состояниям и действиям в графе состояний в искусственном интеллекте.

В современной логике атомарным объектом является не суждение, а понятие. Суждение получается из понятий при помощи теоретико-множественных операций над объёмами понятий. Это даёт формальную модель суждения, нужную для программ искусственного интеллекта, как то говорящие чат-боты с алгоритмами логического вывода. Имея модели объёмов понятий и суждений и используя операции теории множеств, можно как устанавливать логическое следование между суждениями и понятиями, так и порождать новые суждения и понятия при помощи операций теории множеств. Поэтому, современная логика основывается на теории множеств и является логикой классов, где класс есть синонимом термина множество.

Более примитивная математическая логика нашла приложение в схемотехнике и компьютерных науках. В этой логике значениями истинности являются 0 и 1, это известная всем программистам булева логика. Она используется и в построении выражения в компьютерных программах и в прототипировании цифровых схем по конечным автоматам в цифровой схемотехнике. Это одна из самых простых логик, из-за чего в ней не получить весомых логических результатов, так-же и и-за ошибки в модели логического следования. Например, в этой логике по воскресеньям истинно суждение "если сегодня понедельник, то сегодня вторник". То есть A влечёт не A и не выполняется закон исключённого третьего. Ошибочна не вся эта логика, а лишь модель импликации - логического следования одного суждения из другого. Однако, несмотря на этот логический дефект, булева логика получила всеобщее распространение и применение и с этим историческим недоумением приходится сосуществовать в мире схемотехники и программирования.

Троичная логика обратимых программ расширяет булеву логику третьим значением истинности - неопределённостью между 0 и 1, что можно обозначить как целочисленный интервал [0;1], множеством двух значений {0;1} и просто третьей константой u, от слова undefined. Определения операций с участием третьего значения истинности строятся по принципу не противоречия действиям над двумя значениями истинности, ведь третье значение истинности состоит из первых двух. Итак, троичная логика обратимых программ просто расширяет булеву логику, двоичную логику, третьим значением истинности и переносит действия булевой логики на комбинации аргументов с третьим значением истинности [0;1]. Далее идут прямая и обратная троичные логики, нужные для прямого и обратного выполнения программ в обратимых вычислениях. Заметим, что возможно не единственное построение трёхзначной логики, таких логик может быть столько, сколько и их авторов. Это - вопросы формализации и построений, не противоречащих физике, математике или здравому смыслу. Краткий словарь логических терминов можно найти в брошюре автора по логике надобности, доступной для скачивания в конце страницы.

ПРЯМАЯ ТРОИЧНАЯ АРИФМЕТИКА

Прямая троичная логика оперирует с тремя значениями истинности: 0, 1, [0;1], где последнее значение истинности обозначает неопределённость, любое значение истинности. Как и в двоичной логике, логические связки, побитовые операции, задаются для всех возможных комбинаций аргументов и для них назначается результат действия. Это - табличное определение функции, где для каждой возможной пары аргументов задаётся результат функции. Прямая троичная арифметика используется при прямом выполнении обратимых программ, при выполнении программы сверху-вниз. Ниже приводится прямая троичная арифметика для базисных функций логики.

NOT
~0 = 1
~1 = 0
~[0;1] = [0;1]
AND
0 & 0 = 0
0 & [0;1] = 0
0 & 1 = 0
[0;1] & 0 = 0
[0;1] & [0;1] = [0;1]
[0;1] & 1 = [0;1]
1 & 0 = 0
1 & [0;1] = [0;1]
1 & 1 = 1
OR
0 | 0 = 0
0 | [0;1] = [0;1]
0 | 1 = 1
[0;1] | 0 = [0;1]
[0;1] | [0;1] = [0;1]
[0;1] | 1 = 1
1 | 0 = 1
1 | [0;1] = 1
1 | 1 = 1
XOR
0 ^ 0 = 0
0 ^ [0;1] = [0;1]
0 ^ 1 = 1
[0;1] ^ 0 = [0;1]
[0;1] ^ [0;1] = [0;1]
[0;1] ^ 1 = [0;1]
1 ^ 0 = 1
1 ^ [0;1] = [0;1]
1 ^ 1 = 0

ОБРАТНАЯ ТРОИЧНАЯ АРИФМЕТИКА

Обратная троичная арифметика используется при обратном выполнении программ, выполнении программ снизу-вверх. В двоичном коде для операции (x &= A) = B по результату двоичного оператора B и аргументу действия A надо восстановить предыдущее состояние переменной x. Такое восстановление делается на каждой инструкции обратимой программы при обратном выполнении. Далее приводится табличное задание базисных функций обратной троичной логики, нужное для обратного выполнении обратимых программ, и позволяющее восстановление предыдущих значений переменных при проходе снизу-вверх.

NOT
~x = 0  x = 1
~x = 1  x = 0
~x = [0;1]  x = [0;1]
AND
x & 0 = 0  x = [0;1]
x & [0;1] = 0  x = 0
x & 1 = 0  x = 0
x & 0 = [0;1]  x = [0;1]
x & [0;1] = [0;1]  x = [0;1]
x & 1 = [0;1]  x = [0;1]
x & 0 = 1  x = [0;1]
x & [0;1] = 1  x = 1
x & 1 = 1  x = 1
OR
x | 0 = 0  x = 0
x | [0;1] = 0  x = 0
x | 1 = 0  x = [0;1]
x | 0 = [0;1]  x = [0;1]
x | [0;1] = [0;1]  x = [0;1]
x | 1 = [0;1]  x = [0;1]
x | 0 = 1  x = 1
x | [0;1] = 1  x = 1
x | 1 = 1  x = [0;1]
XOR
x ^ 0 = 0  x = 0
x ^ [0;1] = 0  x = [0;1]
x ^ 1 = 0  x = 1
x ^ 0 = [0;1]  x = [0;1]
x ^ [0;1] = [0;1]  x = [0;1]
x ^ 1 = [0;1]  x = [0;1]
x ^ 0 = 1  x = 1
x ^ [0;1] = 1  x = [0;1]
x ^ 1 = 1  x = 0

ПРИМЕР СОКРАЩЕНИЯ ПЕРЕБОРА С ИСПОЛЬЗОВАНИЕМ ТРОИЧНОЙ ЛОГИКИ

В этом примере будет показано, как сократить перебор ключа для получения нужного хэш-значения модифицированной хэш-функции Дженкинсона FAQ6. Эта хэш-функция была выбрана для примера из-за её простоты и немного модифицирована, а именно операция сложения ADD была заменена на операцию побитового исключающего или XOR, из-за того, что библиотека троичной логики пока не поддерживает суммирование. Эта модификация почти ничего не изменила в поведении хэш-функции, ведь и сложение ADD и XOR меняют 0 на 1 и 1 на 0 почти одинаковым образом, если не рассматривать бит переноса из предыдущего разряда при сложении. То есть качество примера после модификации хэш-функции осталось тем-же. Ниже следует код рассматриваемой хэш-функции на языке C#, написанный с использованием вызовов библиотеки троичной логики и обратимых вычислений Ternary Logic Library. Приведённый метод строит в памяти компьютера обратимый код хэш-функции вызовом метода Emulator.Compile(). В коде - четыре тридцати-двух битных слова ключа, по которым рассчитывается хэш-значение с использование вспомогательных переменных, то есть ключ имеет длину 128 битов.

public override void Code()
        {
            Name("FAQ6_modified");
            SetInput("key0", new Mask(128u));
            SetInput("key1", new Mask(129u));
            SetInput("key2", new Mask(130u));
            SetInput("key3", new Mask(131u));
            MoveConst("hash", 0);
            for (int i = 0; i < 4; i++)
            {
                Move("KeyCopy" + i, "key" + i);
                Xor("KeyCopy" + i, "hash");
                Move("store" + i, "hash");
                Move("hash", "KeyCopy" + i);
                Copy("tmp"+i, "hash");
                ShiftLeftConst("tmp"+i, 10);
                Xor("hash", "tmp"+i);
                Copy("tmp2"+i, "hash");
                ShiftRightConst("tmp2"+i, 6);
                Xor("hash", "tmp2"+i);
            }
            Copy("tmp30", "hash");
            ShiftLeftConst("tmp30", 3);
            Xor("hash", "tmp30");
            Copy("tmp31", "hash");
            ShiftRightConst("tmp31", 11);
            Xor("hash", "tmp31");
            Copy("tmp32", "hash");
            ShiftLeftConst("tmp32", 15);
            Xor("hash", "tmp32");
        }

Для сокращения перебора функция выполняется обратно, снизу - вверх. При обратном выполнении предыдущие состояние переменных рассчитываются, по текущим состояниям переменных, по правилам обратной троичной арифметики. Зная текущее состояние переменной, действие и аргумент действия, рассчитывается предыдущее состояние переменной. При этом часть битов получают определённые значения, а часть битов - неопределённые значения. Для обратного выполнения хэш-функции задаются хэш-значение и временные переменные на выходе функции и обратным выполнением восстанавливаются входы функции. Далее приводится код вызовов прямого и обратного выполнения модифицированной функции Дженкинсона.

static void Main(string[] args)
        {
            // создадим эмулятор с обратимым кодом
            Emulator emul = new Program();
            emul.Compile();
            // выведем обратимый код на экран
            Console.WriteLine(emul.Text);
            // выполним код прямо, сверху-вниз
            emul.RunStraight();
            Console.WriteLine(emul.Variables);
            // установим известное хэш-значение на выходе
            emul.SetOutput("hash", new Mask(0xffffffff));
            // установим значения вспомагательных переменных
            emul.SetVariables(new string[] { "tmp30", "tmp31", "tmp32" },
                new Mask[] { new Mask(11111111u), new Mask(22222222u), new Mask(33333333u)});
            emul.SetVariables(new string[] { "tmp0", "tmp20" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
            emul.SetVariables(new string[] { "tmp1", "tmp21" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
            emul.SetVariables(new string[] { "tmp2", "tmp22" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
            emul.SetVariables(new string[] { "tmp3", "tmp23" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
            emul.SetVariables(new string[] { "store0" }, new Mask[] { new Mask(0u) });
            emul.SetVariables(new string[] { "store1" }, new Mask[] { new Mask(1u) });
            emul.SetVariables(new string[] { "store2" }, new Mask[] { new Mask(2u) });
            emul.SetVariables(new string[] { "store3" }, new Mask[] { new Mask(3u) });
            // выполним программу обратно, снизу-вверх
            emul.RunBackStepByStep();
            // распечатаем найденную маску битов ключа, сокращающую перебор
            Console.WriteLine(emul.Variables);
            Console.WriteLine("cracked successfully");
        }

Обратное выполнение хэш-функции, по заданному выходу - хэш-значению, рассчитывает вход - ключ хэш-функции. Ниже приводится результат такого обратного выполнения модифицированной хэш-функции Дженкинсона FAQ6, для установленного на выходе хэш-значения 0xffffffffu.

VARIABLES:

key0 = uuuuuuuuuu0000000000000000uuuuuu

key1 = uuuuuuuuuu0000000000000000uuuuuu

key2 = uuuuuuuuuu0000000000000000uuuuuu

key3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

В полученной маске входа известны 48 битов, нулевые значения, их не надо перебирать, перебирая лишь неизвестные биты. Обратный логический вывод известных значений битов сократил перебор в 2^48 раз (два в степени сорок восемь раз), то есть упростил задачу перебора в 260 триллионов раз!

Ниже приводятся обратимый код модифицированной хэш-функции Дженкинсона и трасса её обратного выполнения, отладочный вывод библиотеки Ternary Logic Library.

CODE:
0)  FAQ6_modified:                
1) hash <- 0                     
2) KeyCopy0 <- key0              
3) KeyCopy0 ^= hash              
4) store0 <- hash                
5) hash <- KeyCopy0              
6) tmp0 := hash                  
7) tmp0 <<= 10                   
8) hash ^= tmp0                  
9) tmp20 := hash                 
10) tmp20 >>= 6                   
11) hash ^= tmp20                 
12) KeyCopy1 <- key1              
13) KeyCopy1 ^= hash              
14) store1 <- hash                
15) hash <- KeyCopy1              
16) tmp1 := hash                  
17) tmp1 <<= 10                   
18) hash ^= tmp1                  
19) tmp21 := hash                 
20) tmp21 >>= 6                   
21) hash ^= tmp21                 
22) KeyCopy2 <- key2              
23) KeyCopy2 ^= hash              
24) store2 <- hash                
25) hash <- KeyCopy2              
26) tmp2 := hash                  
27) tmp2 <<= 10                   
28) hash ^= tmp2                  
29) tmp22 := hash                 
30) tmp22 >>= 6                   
31) hash ^= tmp22                 
32) KeyCopy3 <- key3              
33) KeyCopy3 ^= hash              
34) store3 <- hash                
35) hash <- KeyCopy3              
36) tmp3 := hash                  
37) tmp3 <<= 10                   
38) hash ^= tmp3                  
39) tmp23 := hash                 
40) tmp23 >>= 6                   
41) hash ^= tmp23                 
42) tmp30 := hash                 
43) tmp30 <<= 3                   
44) hash ^= tmp30                 
45) tmp31 := hash                 
46) tmp31 >>= 11                  
47) hash ^= tmp31                 
48) tmp32 := hash                 
49) tmp32 <<= 15                  
50) hash ^= tmp32      
           
Running back...

50)  hash ^= tmp32                   hash = 11111110000000110101111110101010
49) tmp32 <<= 15                    tmp32 = uuuuuuuuuuuuuuu00000001111111001 
48) tmp32 := hash                   hash = uuuuuuuuuuuuuuuu0u0uuu111u1u10uu
47) hash ^= tmp31                   hash = uuuuuuuuuuuuuuuu0u0uuu100u1u01uu
46) tmp31 >>= 11                    tmp31 = 100110001010110001110uuuuuuuuuuu 
45) tmp31 := hash                   hash = uuuuuuuuuuuuuuuu0uuuuuuuuuuuuuuu
44) hash ^= tmp30                   hash = uuuuuuuuuuuuuuuu1uuuuuuuuuuuuuuu
43) tmp30 <<= 3                     tmp30 = uuu00000000101010011000101011000 
42) tmp30 := hash                   hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
41) hash ^= tmp23                   hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
40) tmp23 >>= 6                     tmp23 = 00000000000000000000000000uuuuuu 
39) tmp23 := hash                   hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
38) hash ^= tmp3                    hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
37) tmp3 <<= 10                     tmp3 = uuuuuuuuuu0000000000000000000000 
36) tmp3 := hash                    hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
35) hash <- KeyCopy3                KeyCopy3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
34) store3 <- hash                  hash = 00000000000000000000000000000011
33) KeyCopy3 ^= hash                KeyCopy3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
32) KeyCopy3 <- key3                key3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
31) hash ^= tmp22                   hash = 00000000000000000000000000000011
30) tmp22 >>= 6                     tmp22 = 00000000000000000000000000uuuuuu 
29) tmp22 := hash                   hash = 00000000000000000000000000uuuuuu
28) hash ^= tmp2                    hash = 00000000000000000000000000uuuuuu
27) tmp2 <<= 10                     tmp2 = uuuuuuuuuu0000000000000000000000 
26) tmp2 := hash                    hash = uuuuuuuuuu0000000000000000uuuuuu
25) hash <- KeyCopy2                KeyCopy2 = uuuuuuuuuu0000000000000000uuuuuu
24) store2 <- hash                  hash = 00000000000000000000000000000010
23) KeyCopy2 ^= hash                KeyCopy2 = uuuuuuuuuu0000000000000000uuuuuu
22) KeyCopy2 <- key2                key2 = uuuuuuuuuu0000000000000000uuuuuu
21) hash ^= tmp21                   hash = 00000000000000000000000000000010
20) tmp21 >>= 6                     tmp21 = 00000000000000000000000000uuuuuu 
19) tmp21 := hash                   hash = 00000000000000000000000000uuuuuu
18) hash ^= tmp1                    hash = 00000000000000000000000000uuuuuu
17) tmp1 <<= 10                     tmp1 = uuuuuuuuuu0000000000000000000000 
16) tmp1 := hash                    hash = uuuuuuuuuu0000000000000000uuuuuu
15) hash <- KeyCopy1                KeyCopy1 = uuuuuuuuuu0000000000000000uuuuuu
14) store1 <- hash                  hash = 00000000000000000000000000000001
13) KeyCopy1 ^= hash                KeyCopy1 = uuuuuuuuuu0000000000000000uuuuuu
12) KeyCopy1 <- key1                key1 = uuuuuuuuuu0000000000000000uuuuuu
11) hash ^= tmp20                   hash = 00000000000000000000000000000001
10) tmp20 >>= 6                     tmp20 = 00000000000000000000000000uuuuuu 
9) tmp20 := hash                   hash = 00000000000000000000000000uuuuuu
8) hash ^= tmp0                    hash = 00000000000000000000000000uuuuuu
7) tmp0 <<= 10                     tmp0 = uuuuuuuuuu0000000000000000000000 
6) tmp0 := hash                    hash = uuuuuuuuuu0000000000000000uuuuuu
5) hash <- KeyCopy0                KeyCopy0 = uuuuuuuuuu0000000000000000uuuuuu
4) store0 <- hash                  hash = 00000000000000000000000000000000
3) KeyCopy0 ^= hash                KeyCopy0 = uuuuuuuuuu0000000000000000uuuuuu
2) KeyCopy0 <- key0                key0 = uuuuuuuuuu0000000000000000uuuuuu
1) hash <- 0                       
0) FAQ6_modified:

Итак, в этом не сложном примере показано, как обратное выполнение хэш-функции, в троичной логике, позволяет сократить множество перебираемых ключей в сотни триллионов раз. Обратное выполнение хэш-функции восстанавливает некоторые биты её входа, оставляя другие биты неопределёнными.

обратимые вычисления для инженеров и криптографов

Обратимые вычисления это вычисления, проводимые в обе стороны, от аргументов - к результату функции, прямо, и от результатов - к аргументам функции, обратно.

Например, в задаче восстановления ключа по хэш-значению, проводя вычисления функции хэширования снизу-вверх, от хэш-значения, и выполняя обратное действие каждой команды, во входных переменных функции получаем известные биты ключа и сокращаем перебор ключа в сотни триллионов раз.

В другом примере, добычи биткоинов, проводя вычисления обратно, от хэш-значения, меньшего текущей сложности пула, до входного аргумента функции, числа nonce, получаем известные биты nonce, сокращающие перебор и позволяющие добычу биткоинов на смартбуке.

Физики и инженеры доказали, что при обратимых вычислениях обратимый процессор не рассеивает энергию, не выделяет тепла и является адиабатическим. Это позволяет снизить расходы на электричество в суперкомпьютерных цехах, например ферм по добыче биткоинов, с миллионными счетами за электричество при десятках тысяч специализированных процессоров. В физике данный факт называется принципом Ландауэра.

Обратимые вычисления могут быть организованы на схемотехническом и логическом уровнях.

На схемотехническом уровне обратимые процессоры строятся из обратимых логических вентилей. Например, обратимых логических вентилей Тоффоли и Фредкина. Программа-интерпретатор процессора компилируется с языка описания аппаратуры типа VHDL в схему из обратимых логических вентилей, по которой изготовляется обратимый процессор из полупроводникового материала. Смотри, например, обратимый процессор Pendulum.

Логически-обратимые вычисления организовываются поверх необратимой компьютерной архитектуры, на обычных необратимых процессорах. Это становится возможным благодаря эмуляции обратимого процессора на необратимом процессоре.

Обратимый эмулятор это программа-интерпретатор обратимого ассемблера, могущая выполнять каждую инструкцию прямо и обратно, выполнять код обратимой программы сверху-вниз и снизу-вверх. Если обратимый язык программирования содержит в себе условия и циклы, то кроме выполнения программы снизу-вверх интерпретатор выполняет код и справа-налево. Смотри, например, обратимый ассемблер BLACK Assembler исследовательских проектов Digital Coin Debugger и Black Emulator.

Обратимый компилятор или компилирует программу на обратимом языке программирования, или строит обратную программу по исходной, как компилятор академического обратимого языка программирования Janus.

Обратимый язык программирования это такой язык программирования, программы на котором обратимы и позволяют выполнение сверху-вниз и снизу-вверх. Обратимыми могут быть ассемблеры, алгоритмические и функциональные языки программирования. Автору не знакомы обратимые логические языки программирования типа Пролога и Mercury.

Обратимый отладчик это отладчик обратимого языка программирования, позволяющий выполнять пошагово код сверху-вниз и снизу-вверх. Например, в обратимом отладчике Digital Coin Debugger можно отметить начало и конец блока кода и выполнить этот блок кода снизу-вверх, инструкция за инструкцией.

ОБРАТИМАЯ ЭМУЛЯЦИЯ

Обратимый ассемблер проектируется для прямого и обратного выполнения программ специальным образом. Процессор это интерпретатор ассемблера, скомпилированный с языка VHDL в кремний. Он выполняет команды ассемблера инструкция за инструкцией, при этом каждая команда меняет содержимое регистров или ячеек памяти. Аналогично, эмулятор выполняет команды ассемблера инструкция за инструкцией, но код этого интерпретатора компилируется не в кремний, а в исполняемый файл операционной системы - это обычная программа.

Обратимый эмулятор имеет по две функции для каждой команды обратимого ассемблера

Функция прямой работы выполняет инструкцию ассемблера обычным образом, считая по входному состоянию задействованных переменных выходное состояние.

Функция обратной работы выполняет инструкцию ассемблера обратным образом, рассчитывая предыдущее состояние переменных по текущему состоянию.

Обратимая программа представляется массивом инструкций обратимого ассемблера. Эмулятор выполняет ту инструкцию прямо или обратно, на которую указывает указатель инструкции.

Прямая работа программы состоит в выполнении инструкций сверху-вниз, вызовами функции прямой работы текущей инструкции.

Обратная работа программы состоит в выполнении инструкций снизу-вверх, вызовами функции обратной работы текущей инструкции.

Прямая работа заканчивается, когда указатель команд указывает на последнюю инструкцию. Обратная работа программы заканчивается, когда указатель команд указывает на нулевую инструкцию. Прямая работа считает результаты по аргументам, а обратная работа считает аргументы по результатам.

Если обратимый ассемблер встраивается в высокоуровневый язык программирования, то можно не писать транслятор ассемблера, а команды ассемблера оформлять вызовами функций, добавляющими команду в массив инструкций, типа Plus(string a, string b), где a и b это имена переменных. Тогда фаза компиляции во время выполнения состоит в выполнении функций языка, добавляющих инструкции в массив, а фаза прямого или обратного выполнения состоит в интерпретации полученного массива инструкций.

Для быстрого доступа к переменным переменные хранятся в хэш-таблице, в которой ключом является имя переменной, а значением является значение переменной.

Обратимый ассемблер может поддерживать только часть общепринятых команд. Если обратимый ассемблер не поддерживает инструкций переходов и сравнения, то такой язык является линейным кодом. Линейный код чрезвычайно прост для интерпретации, а функция интерпретации занимает пол экрана.

Двоичный код обратимого ассемблера это код с двумя операндами, типа a += b. Он называется двоичным из-за двух переменных в инструкции и прост для обращения. Например, для инструкции a += b обратной будет инструкция a -= b.

Троичный код обратимого ассемблера это код с тремя операндами, типа a = b + c. Обратимый троичный код намного сложнее для построения и требует тысячи листов бумаги формата A4 с решением уравнений на этапе проектирования обратимого ассемблера.

Логически-обратимые вычисления не имеют особой сложности программирования, обратимый эмулятор может быть написан и спроектирован опытным программистом за одну неделю. На сегодня не известно, рассеивается ли тепло при логически-обратимых вычислениях на необратимых процессорах. Современное состояние логически-обратимых вычислений соcтоит в корректном выполнении хэш-функции SHA256 сверху-вниз и снизу-вверх на обратимом эмуляторе Reverse Coin, написанному автором за одну неделю на языке C#.

исходники альфа-версии библиотеки обратимых вычислений reverse coin

Обратимый эмулятор для добычи криптовалюты обратным выводом из хэш-значения

//
// Alpha version of reversible emulator ReverseCoin, suitable to run back SHA256 from bottom to the top in the bitcoin mining.
//
// This C# code is freeware licensed. You can do with it all you want.
//
// Dmitry Negius (Talomir) from Botting Technologies, february 2018.
//
// Special thanks to all who worked on the ideas of reversible computing in cryptoanalyses and artificial intelligence together with me.
//
//---------- Emulator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class Emulator
    {
        public List<Instruction> code = new List<Instruction>();

        public Dictionary<string, Variable> vars = new Dictionary<string, Variable>();
        public bool debug = false;

        public List<string> output = new List<string>();
        public Dictionary<string, Variable> OutputValues = new Dictionary<string, Variable>();

        public int ip = 0;

        public void RunDirect(bool Outputs = true, int StartIP = -1, int FinishIP = -1)
        {
            if (StartIP == -1) StartIP = 0;
            if (FinishIP == -1) FinishIP = code.Count - 1;

            if (debug) Console.WriteLine("\r\nRunning direct...");
            for (ip = StartIP; ip <= FinishIP; ip++)
            {
                code[ip].DirectWork(vars);
                PrintDirectInstruction(ip);
            }
            if (Outputs)
            {
                SaveOutputs();
                PrintOutputs();
                //PrintOutputsHex();
            }
        }

        protected void SaveOutputs()
        {
            OutputValues = new Dictionary<string, Variable>();
            foreach (string name in output)
                OutputValues[name] = new Variable(vars[name]);
        }

        protected void PrintOutputs()
        {
            if (debug)
            {
                Console.WriteLine("\r\nOutput values: ");
                foreach (Variable v in OutputValues.Values)
                    Console.WriteLine(v.name + " = " + v);
            }
        }
        public void PrintOutputsHex()
        {
            if (debug)
            {
                Console.WriteLine("\r\nOutput values: ");
                foreach (Variable v in OutputValues.Values)
                    Console.WriteLine("{0} = {1:x}", v.name, v.Value());
            }
        }

        protected void CheckOutputs(Dictionary<string, Variable> OutputValues)
        {
            Emulator emul = this.Copy();
            emul.RunDirect();
            foreach (Variable v in emul.OutputValues.Values)
                if (!v.CompareTo(OutputValues[v.name]))
                    throw new Exception("Output value of " + v.name + " mismatch: " +
                        v + " != " + OutputValues[v.name]);
        }


        protected Dictionary<string, Variable> CopyVariables(Dictionary<string, Variable> vars)
        {
            Dictionary<string, Variable> v = new Dictionary<string, Variable>();
            foreach (Variable x in vars.Values) v[x.name] = new Variable(x);
            return v;
        }


        public Emulator Copy()
        {
            Emulator emul = new Emulator();
            emul.code = this.code;
            emul.vars = CopyVariables(this.vars);
            emul.debug = this.debug;
            emul.ip = this.ip;
            foreach (string s in output) emul.output.Add(s);
            foreach (Variable v in OutputValues.Values)
                emul.OutputValues[v.name] = v;
            return emul;
        }


        protected void PrintReverseInstruction(int ip)
        {
            if (debug)
            {
                try
                {
                    if (code[ip].op == Operation.MoveConst || code[ip].op == Operation.Copy)
                        Console.WriteLine("{0,8}:\t{1,-30}", ip, code[ip]);
                    else if (code[ip].op == Operation.Move)
                        Console.WriteLine("{0,8}:\t{1,-30}\t{2,-30}", ip, code[ip], vars[code[ip].right]);
                    else
                        Console.WriteLine("{0,8}:\t{1,-30}\t{2,-30}", ip, code[ip], vars[code[ip].left]);
                }
                catch { }
            }
        }

        protected void PrintDirectInstruction(int ip)
        {
            //if (debug) Console.WriteLine("" + ip + ":\t" + code[ip] + "\t\t  " + vars[Instruction.IndexOf(code[ip].left, vars)]);
            if (debug)
                Console.WriteLine("{0,8}:\t{1,-30}\t{2,-30}", ip, code[ip], vars[code[ip].left]);
        }


        public void RunReverse(int FromIP = -1)
        {
            if (FromIP == -1) FromIP = code.Count - 1;

            if (debug) Console.WriteLine("\r\nRunning reverse...");
            for (ip = FromIP; ip >= 0; ip--)
            {
                code[ip].ReverseWork(vars);
                PrintReverseInstruction(ip);
            }
            //CheckOutputs(OutputValues);
        }

        public void RunReverseStepByStep(Dictionary<string, Variable> control = null)
        {
            if (control == null) control = OutputValues;

            Dictionary<string, Variable> tmp = new Dictionary<string, Variable>();
            foreach (Variable v in control.Values)
                tmp[v.name] = new Variable(v);


            if (debug) Console.WriteLine("\r\nRunning back step by step...");

            for (int ip = code.Count - 1; ip >= 0; ip--)
            {
                Variable bp;
                bp = new Variable(vars[code[ip].left]);

                code[ip].ReverseWork(vars);
                PrintReverseInstruction(ip);

                code[ip].DirectWork(vars);
                PrintDirectInstruction(ip);

                Variable bp2;
                bp2 = new Variable(vars[code[ip].left]);

                if (code[ip].op != Operation.Copy && code[ip].op != Operation.MoveByteExtend
                    && code[ip].op != Operation.MoveConst)
                if (!bp.CompareTo(bp2)) throw new Exception("Direct after reverse variable "+bp.name+" mismatch: before "+bp+", after "+bp2);

                code[ip].ReverseWork(vars);
                PrintReverseInstruction(ip);
            }
            CheckOutputs(tmp);
        }


        public delegate bool InputPraedicate(Emulator emul);
        public delegate bool OutputPraedicate(Emulator emul);

        public int FillInputStep = 250;

        public Variable FillInput(Variable input, int pos, InputPraedicate InputP, OutputPraedicate OutputP)
        {
            Variable inp = vars[input.name];
            inp.bits = input.bits;

            if (!debug) Console.Write(" pos={0} input={1}", pos, inp);

            Variable v;

            if (pos >= input.Length) return null;

            bool finishOk = false;
            bool startOk = false;
            Emulator ne = null, emul = null;
            
            if (!startOk || !finishOk)
            {
                finishOk = false;
                inp.bits[pos] = 0;
                for (int i = pos + 1; i < inp.Length; i++) inp.bits[i] = 0;

                ne = this.Copy();

                int IPend = FillInputStep;

                for (int IP = 0; IP < code.Count; IP = IPend)
                {
                    if (!debug) Console.Write(".");
                    IPend = IP + FillInputStep;
                    if (IPend >= code.Count) IPend = code.Count;

                    ne.RunDirect(false, IP, IPend - 1);
                    if (IPend == code.Count) finishOk = OutputP(ne);
                    emul = ne.Copy();
                    emul.RunReverse(IPend-1);
                    startOk = InputP(emul);
                    if (!startOk) break;
                }
            }

            if (startOk && finishOk) return emul.vars[inp.name];

            v = FillInput(inp, pos + 1, InputP, OutputP);
            if (v != null) return v;

            if (!startOk || !finishOk)
            {
                finishOk = false;
                inp.bits[pos] = 1;
                for (int i = pos + 1; i < inp.Length; i++) inp.bits[i] = 0;

                ne = this.Copy();

                int IPend = FillInputStep;

                for (int IP = 0; IP < code.Count; IP = IPend)
                {
                    if (!debug) Console.Write(".");
                    IPend = IP + FillInputStep;
                    if (IPend >= code.Count) IPend = code.Count;

                    ne.RunDirect(false, IP, IPend-1);
                    if (IPend == code.Count) finishOk = OutputP(ne);
                    emul = ne.Copy();
                    emul.RunReverse(IPend-1);
                    startOk = InputP(emul);
                    if (!startOk) break;
                }
            }
            if (startOk && finishOk) return emul.vars[inp.name];

            v = FillInput(inp, pos + 1, InputP, OutputP);

            if (v != null) return v;

            if (!startOk || !finishOk)
            {
                finishOk = false;
                inp.bits[pos] = -1;
                for (int i = pos + 1; i < inp.Length; i++) inp.bits[i] = 0;

                ne = this.Copy();

                int IPend = FillInputStep;

                for (int IP = 0; IP < code.Count; IP = IPend)
                {
                    if (!debug) Console.Write(".");
                    IPend = IP + FillInputStep;
                    if (IPend >= code.Count) IPend = code.Count;

                    ne.RunDirect(false, IP, IPend-1);
                    if (IPend == code.Count) finishOk = OutputP(ne);
                    emul = ne.Copy();
                    emul.RunReverse(IPend-1);
                    startOk = InputP(emul);
                    if (!startOk) break;
                }
            }
            if (startOk && finishOk) return emul.vars[inp.name];

            v = FillInput(inp, pos + 1, InputP, OutputP);
            if (v != null) return v;

            return null;
        }


        protected Random rnd = new Random();


        protected void RandomizeVariables(Dictionary<string, Variable> vars,
            string[] DoNotChange)
        {
            double MutProb = rnd.NextDouble();

            List<string> keys = new List<string>(vars.Keys.ToArray<string>());

            foreach (string key in keys)
            {
                bool no = false;

                foreach (string s in DoNotChange)
                    if (s.Equals(key))
                    {
                        no = true;
                        break;
                    }

                if (no) continue;

                double p = rnd.NextDouble();

                if (p < MutProb) vars[key] = new Variable(key, (uint)rnd.Next(0, 32));
            }
        }


        protected void InitVariables(Dictionary<string, Variable> vars,
            string[] DoNotChange)
        {
            List<string> keys = new List<string>(vars.Keys.ToArray<string>());

            foreach (string key in keys)
            {
                bool no = false;

                foreach (string s in DoNotChange)
                    if (s.Equals(key))
                    {
                        no = true;
                        break;
                    }

                if (no) continue;

                vars[key] = new Variable(key, (uint)rnd.Next(0, 32));
                
            }
        }


        public Emulator GeneticRunReverse(params string[] DoNotChangeVariables)
        {
            Dictionary<string, Variable> BestVariables = CopyVariables(this.vars);
            InitVariables(BestVariables, DoNotChangeVariables);
            int BestDistance = 100000000;

            Emulator ne = null;

            while (BestDistance > 0)
            {
                if (debug) Console.Write(".");
                ne = this.Copy();
                ne.vars = CopyVariables(BestVariables);
                RandomizeVariables(ne.vars, DoNotChangeVariables);

                bool exception = false;

                int IP = ne.code.Count - 1;

                for (; IP >= 0; IP --)
                {
                    try
                    {
                        ne.code[IP].ReverseWork(ne.vars);
                    } catch { exception = true; break; }
                }

                if (exception)
                {
                    if (IP < BestDistance)
                    {
                        BestDistance = IP;
                        BestVariables = CopyVariables(ne.vars);
                        /*if (debug) */Console.WriteLine("\r\nExecuted reverse till {0}", IP);
                    }
                }
                else break;
            }

            if (debug) Console.WriteLine("\r\nSUCCESS");
            return ne;
        }


        public void SetInput(string name, uint value)
        {
            if (vars.ContainsKey(name)) throw new Exception("variable " + name + " exist");
            vars[name] = new Variable(name, value);
        }

        public void SetInput(string name, ushort value)
        {
            if (vars.ContainsKey(name)) throw new Exception("variable " + name + " exist");
            vars[name] = new Variable(name, value);
        }

        public void SetInput(string name, byte value)
        {
            if (vars.ContainsKey(name)) throw new Exception("variable " + name + " exist");
            vars[name] = new Variable(name, value);
        }

        public void SetOutput(string name)
        {
            foreach (string s in output) if (s.Equals(name))
                    throw new Exception("variable " + name + " exist");
            output.Add(name);
        }

        public void Move(string left, string right)
        {
            code.Add(new InstructionMove(left, right));
        }

        public void Move(string left, uint imm)
        {
            code.Add(new InstructionMoveConst(left, imm));
        }

        public void Move(string left, byte imm)
        {
            code.Add(new InstructionMoveConst(left, imm));
        }

        public void Move(string left, ushort imm)
        {
            code.Add(new InstructionMoveConst(left, imm));
        }

        public void MoveByteExtend(string left, string right)
        {
            code.Add(new InstructionMoveByteExtend(left, right));
        }

        public void Copy(string left, string right)
        {
            code.Add(new InstructionCopy(left, right));
        }

        public void And(string left, string right)
        {
            code.Add(new InstructionAnd(left, right));
        }

        public void Or(string left, string right)
        {
            code.Add(new InstructionOr(left, right));
        }

        public void Xor(string left, string right)
        {
            code.Add(new InstructionXor(left, right));
        }

        public void Not(string left)
        {
            code.Add(new InstructionNot(left));
        }

        public void ShiftLeft(string left, string right)
        {
            code.Add(new InstructionShiftLeft(left, right));
        }

        public void ShiftRight(string left, string right)
        {
            code.Add(new InstructionShiftRight(left, right));
        }

        public void Plus(string left, string right)
        {
            code.Add(new InstructionPlus(left, right));
        }

        public void Exchange(string left, string right)
        {
            code.Add(new InstructionExchange(left, right));
        }

        public void GetByte(string left, string right, uint number)
        {
            code.Add(new InstructionGetByte(left, right, number));
        }
    }
}

//---------- Instruction.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public enum Operation
    {
        Move,
        MoveConst,
        MoveByteExtend,
        Copy,
        Exchange,
        GetByte,
        Plus,
        Minus,
        And,
        Or,
        Xor,
        Not,
        ShiftLeft,
        ShiftRight
    }


    public abstract class Instruction
    {
        public string left, right;
        public Operation op;
        public bool IsConstArg = false;
        public uint imm;

        public Instruction(string left, Operation op, string right, uint imm, bool IsConstArg = false)
        {
            this.left = left;
            this.right = right;
            this.op = op;
            this.IsConstArg = IsConstArg;
            this.imm = imm;
            if (left.Equals(right)) throw new Exception("Left argument is equal to right argument: " + this);
        }

        public abstract void DirectWork(Dictionary<string, Variable> vars);
        public abstract void ReverseWork(Dictionary<string, Variable> vars);

        public override string ToString()
        {
            if (op != Operation.Not)
            {
                string res = left;
                res += " " + OpToString(op) + " ";
                if (IsConstArg) res += imm;
                else res += right;
                return res;
            }
            else return "not(" + left + ")";
        }

        protected string OpToString(Operation op)
        {
            switch (op)
            {
                case Operation.Plus: return "+=";
                case Operation.Minus: return "-=";
                case Operation.And: return "&=";
                case Operation.Or: return "|=";
                case Operation.Xor: return "^=";
                case Operation.ShiftLeft: return "<<=";
                case Operation.ShiftRight: return ">>=";
                case Operation.Move:
                case Operation.MoveConst: return "<-";
                case Operation.MoveByteExtend: return "<<-";
                case Operation.Copy: return ":=";
                case Operation.Exchange: return "<->";
                case Operation.GetByte: return "B<-";
                default: return "und";
            }
        }
    }
}

//---------- InstructionAnd.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionAnd : Instruction
    {
        public InstructionAnd(string left, string right) :
            base(left, Operation.And, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            for (int i = 0; i < b.Length; i++)
            {
                if (a.bits[i] == 0)
                {
                    if (b.bits[i] == 0) a.bits[i] = 0;
                    else if (b.bits[i] == -1) a.bits[i] = -1;
                    else a.bits[i] = 0;
                } else 
                if (a.bits[i] == -1)
                {
                    //a.bits[i] = -1;
                } else
                if (a.bits[i] == 1)
                {
                    if (b.bits[i] == 0) a.bits[i] = 0;
                    else if (b.bits[i] == -1) a.bits[i] = -1;
                    else a.bits[i] = 1;
                }
                
            }
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            for (int i = 0; i < b.Length; i++)
            {
                if (a.bits[i] == 0)
                {
                    if (b.bits[i] == 0) a.bits[i] = -1;
                    else if (b.bits[i] == 1) a.bits[i] = 0;
                    else if (b.bits[i] == -1) a.bits[i] = 0;
                }
                else if (a.bits[i] == -1)
                {
                    //a.bits[i] = -1;
                }
                else if (a.bits[i] == 1)
                {
                    if (b.bits[i] == 0) a.bits[i] = -1;
                    else if (b.bits[i] == -1) a.bits[i] = -1;
                    else if (b.bits[i] == 1) a.bits[i] = 1;
                }
            }
        }
    }
}
//---------- InstructionCopy.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionCopy : Instruction
    {
        public InstructionCopy(string left, string right) :
            base(left, Operation.Copy, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            if (vars.ContainsKey(left)) throw new Exception("variable "+left+" exist");

            Variable v = new Variable(vars[right]);
            v.name = left;
            vars[left] = v;            
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            vars.Remove(left);
        }
    }
}
//---------- InstructionExchange.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionExchange : Instruction
    {
        public InstructionExchange(string left, string right) :
            base(left, Operation.Exchange, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable a = vars[left];
            Variable b = vars[right];

            sbyte[] bits = a.bits;
            a.bits = b.bits;
            b.bits = bits;
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable a = vars[left];
            Variable b = vars[right];

            sbyte[] bits = a.bits;
            a.bits = b.bits;
            b.bits = bits;
        }
    }
}
//---------- InstructionGetByte.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionGetByte : Instruction
    {
        public InstructionGetByte(string left, string right, uint number) :
            base(left, Operation.GetByte, right, number)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            if (vars.ContainsKey(left)) throw new Exception("variable " + left + " exist");

            Variable v = new Variable(left, (byte)0);

            for (uint i = 0, j = 8 * imm; i < 8; i++, j++) v.bits[i] = vars[right].bits[j];

            vars[left] = v;
        }


        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            for (uint i = 0, j = 8 * imm; i < 8; i++, j++)
                vars[right].bits[j] = vars[left].bits[i];

            vars.Remove(left);
        }
    }
}
//---------- InstructionMove.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionMove : Instruction
    {
        public InstructionMove(string left, string right) :
            base(left, Operation.Move, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            if (vars.ContainsKey(left)) throw new Exception("variable "+left+" exist");

            Variable r = vars[right];
            vars.Remove(right);
            r.name = left;
            vars[left] = r;
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable l = vars[left];
            vars.Remove(left);
            l.name = right;
            vars[right] = l;
        }
    }
}
//---------- InstructionMoveByteExtend.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionMoveByteExtend : Instruction
    {
        public InstructionMoveByteExtend(string left, string right) :
            base(left, Operation.MoveByteExtend, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            if (vars.ContainsKey(left)) throw new Exception("variable " + left + " exist");

            Variable r = vars[right];
            vars.Remove(right);

            sbyte[] bits = new sbyte[32];
            for (int i = 0; i < 8; i++) bits[i] = r.bits[i];

            r.name = left;
            r.bits = bits;
            vars[left] = r;
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable l = vars[left];
            vars.Remove(left);

            sbyte[] bits = new sbyte[8];

            for (int i = 0; i < 8; i++) bits[i] = l.bits[i];

            l.bits = bits;
            l.name = right;
            vars[right] = l;
        }

    }

}
//---------- InstructionMoveConst.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionMoveConst : Instruction
    {
        bool IsByte = false;
        bool IsShort = false;

        byte ImmByte = 0;
        ushort ImmShort = 0;

        public InstructionMoveConst(string left, uint imm) :
            base(left, Operation.MoveConst, "", imm, true)
        { }

        public InstructionMoveConst(string left, byte imm) :
            base(left, Operation.MoveConst, "", imm, true)
        {
            IsByte = true;
            ImmByte = imm;
        }

        public InstructionMoveConst(string left, ushort imm) :
            base(left, Operation.MoveConst, "", imm, true)
        {
            IsShort = true;
            ImmShort = imm;
        }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            if (vars.ContainsKey(left)) throw new Exception("variable "+left+" exist");

            if (IsByte) vars[left] = new Variable(left, ImmByte);
            else if (IsShort) vars[left] = new Variable(left, ImmShort);
            else vars[left] = new Variable(left, imm);
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            vars.Remove(left);
        }
    }
}
//---------- InstructionNot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionNot : Instruction
    {
        public InstructionNot(string left) :
            base(left, Operation.Not, "", 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable a = vars[left];

            for (int i = 0; i < a.Length; i++)
            {
                if (a.bits[i] == 0) a.bits[i] = 1;
                else if (a.bits[i] == 1) a.bits[i] = 0;
                else if (a.bits[i] == -1) a.bits[i] = -1;
            }
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable a = vars[left];

            for (int i = 0; i < a.Length; i++)
            {
                if (a.bits[i] == 1) a.bits[i] = 0;
                else if (a.bits[i] == 0) a.bits[i] = 1;
                else if (a.bits[i] == -1) a.bits[i] = -1;
            }
        }
    }
}
//---------- InstructionOr.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionOr : Instruction
    {
        public InstructionOr(string left, string right) :
            base(left, Operation.Or, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            for (int i = 0; i < b.Length; i++)
            {
                if (a.bits[i] == 0)
                {
                    if (b.bits[i] == 0) a.bits[i] = 0;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = 1;
                }
            }
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            for (int i = 0; i < b.Length; i++)
            {
                if (a.bits[i] == 0)
                {
                    if (b.bits[i] == 0) a.bits[i] = 0;
                    else if (b.bits[i] == -1) a.bits[i] = -1;
                    else if (b.bits[i] == 1) a.bits[i] = -1;
                }
                else if (a.bits[i] == -1)
                {
                    //a.bits[i] = -1;
                }
                else if (a.bits[i] == 1)
                {
                    if (b.bits[i] == 1) a.bits[i] = -1;
                    //a.bits[i] = 1;
                }
            }
        }


    }
}
//---------- InstructionPlus.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionPlus : Instruction
    {
        public InstructionPlus(string left, string right) :
            base(left, Operation.Plus, right, 0)
        { }

        protected void Sum(sbyte a, sbyte b, sbyte cin, out sbyte c, out sbyte cout)
        {
            c = 0; cout = 0;

            if (a == 0 && b == 0 && cin == 0) { c = 0; cout = 0; }
            else if (a == 0 && b == 0 && cin == 1) { c = 1; cout = 0; }
            else if (a == 0 && b == 0 && cin == -1) { c = -1; cout = 0; }
            else if (a == 0 && b == 1 && cin == 0) { c = 1; cout = 0; }
            else if (a == 0 && b == 1 && cin == 1) { c = 0; cout = 1; }
            else if (a == 0 && b == 1 && cin == -1) { c = -1; cout = -1; }
            else if (a == 1 && b == 0 && cin == 0) { c = 1; cout = 0; }
            else if (a == 1 && b == 0 && cin == 1) { c = 0; cout = 1; }
            else if (a == 1 && b == 0 && cin == -1) { c = -1; cout = -1; }
            else if (a == 1 && b == 1 && cin == 0) { c = 0; cout = 1; }
            else if (a == 1 && b == 1 && cin == 1) { c = 1; cout = 1; }
            else if (a == 1 && b == 1 && cin == -1) { c = -1; cout = -1; }
            else if (a == 0 && b == -1 && cin == 0) { c = -1; cout = 0; }
            else if (a == 0 && b == -1 && cin == 1) { c = -1; cout = -1; }
            else if (a == 0 && b == -1 && cin == -1) { c = -1; cout = -1; }
            else if (a == -1 && b == 0 && cin == 0) { c = -1; cout = 0; }
            else if (a == -1 && b == 0 && cin == 1) { c = -1; cout = -1; }
            else if (a == -1 && b == 0 && cin == -1) { c = -1; cout = -1; }
            else if (a == -1 && b == -1 && cin == 0) { c = -1; cout = -1; }
            else if (a == -1 && b == -1 && cin == 1) { c = -1; cout = -1; }
            else if (a == -1 && b == -1 && cin == -1) { c = -1; cout = -1; }
        }

        protected bool Equal(sbyte a, sbyte b)
        {
            if (a != -1 && b != -1)
                if (a != b) return false;
            return true;
        }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            sbyte cin = 0, cout = 0, c = 0;


            for (int i = 0; i < b.Length; i++)
            {
                Sum(a.bits[i], b.bits[i], cin, out c, out cout);
                a.bits[i] = c;
                cin = cout;
            }
        }

        protected bool FillRecursive(Variable a, Variable b, Variable OutA, int pos, sbyte cin)
        {
            if (pos >= b.Length) return true;

            sbyte c = 0, cout = 0;

            bool ok = false;

            Sum(0, b.bits[pos], cin, out c, out cout);

            if (Equal(a.bits[pos], c))
            {
                OutA.bits[pos] = 0;
                ok = FillRecursive(a, b, OutA, pos + 1, cout);
                if (ok) return true;
            }

            Sum(1, b.bits[pos], cin, out c, out cout);

            if (Equal(a.bits[pos], c))
            {
                OutA.bits[pos] = 1;
                ok = FillRecursive(a, b, OutA, pos + 1, cout);
                if (ok) return true;
            }

            Sum(-1, b.bits[pos], cin, out c, out cout);

            if (Equal(a.bits[pos], c))
            {
                OutA.bits[pos] = -1;
                ok = FillRecursive(a, b, OutA, pos + 1, cout);
                if (ok) return true;
            }

            return false;
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];
            Variable OutA = new Variable(a);

            bool ok = FillRecursive(a, b, OutA, 0, 0);

            if (ok) a.bits = OutA.bits;
            else throw new Exception("can't solve " + this + " comparison");
        }


    }
}
//---------- InstructionShiftLeft.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionShiftLeft : Instruction
    {
        public InstructionShiftLeft(string left, string right) :
            base(left, Operation.ShiftLeft, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            uint shift = b.Value();

            if (shift > 0x1f) throw new Exception("too big shift operand " + shift);

            for (uint i = (uint)a.Length-1; i >= shift; i--)
                a.bits[i] = a.bits[i - shift];
            for (uint i = 0; i < shift; i++) a.bits[i] = 0;
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            uint shift = b.Value();

            for (uint i = 0; i < a.Length - shift; i++) a.bits[i] = a.bits[i + shift];
            for (uint i = (uint)a.Length - shift; i < a.Length; i++) a.bits[i] = -1;
        }
    }
}
//---------- InstructionShiftRight.cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionShiftRight : Instruction
    {
        public InstructionShiftRight(string left, string right) :
            base(left, Operation.ShiftRight, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            uint shift = b.Value();

            if (shift > 0x1f) throw new Exception("too big shift value " + shift);

            for (uint i = 0; i < a.Length - shift; i++)
                a.bits[i] = a.bits[i + shift];
            for (uint i = (uint)a.Length - shift; i < a.Length; i++)
                a.bits[i] = 0;
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            uint shift = b.Value();

            for (uint i = (uint)a.Length - 1; i >= shift; i--)
                a.bits[i] = a.bits[i - shift];
            for (uint i = 0; i < shift; i++)
                a.bits[i] = -1;
        }
    }
}
//---------- InstructionXor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class InstructionXor : Instruction
    {
        public InstructionXor(string left, string right) :
            base(left, Operation.Xor, right, 0)
        { }

        public override void DirectWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            for (int i = 0; i < b.Length; i++)
            {
                if (a.bits[i] == 0)
                {
                    if (b.bits[i] == 0) a.bits[i] = 0;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = 1;
                }
                else if (a.bits[i] == -1)
                {
                    if (b.bits[i] == 0) a.bits[i] = -1;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = -1;
                }
                else if (a.bits[i] == 1)
                {
                    if (b.bits[i] == 0) a.bits[i] = 1;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = 0;
                }
            }
        }

        public override void ReverseWork(Dictionary<string, Variable> vars)
        {
            Variable b = vars[right];
            Variable a = vars[left];

            for (int i = 0; i < b.Length; i++)
            {
                if (a.bits[i] == 0)
                {
                    if (b.bits[i] == 0) a.bits[i] = 0;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = 1;
                }
                else if (a.bits[i] == -1)
                {
                    if (b.bits[i] == 0) a.bits[i] = -1;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = -1;
                }
                else if (a.bits[i] == 1)
                {
                    if (b.bits[i] == 0) a.bits[i] = 1;
                    if (b.bits[i] == -1) a.bits[i] = -1;
                    if (b.bits[i] == 1) a.bits[i] = 0;
                }
            }
        }
    }
}
//---------- Variable.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseCoin
{
    public class Variable
    {
        public sbyte[] bits;
        public string name = "XXX";

        public int Length
        {
            get { return bits.Length; }
        }

        public override string ToString()
        {
            string res = "";
            for (int i = bits.Length-1; i >= 0; i--)
                if (bits[i] == 0) res += "0";
                else if (bits[i] == 1) res += "1";
                else res += "u";
            return res;
        }

        public Variable(string name, uint u)
        {
            this.name = name;

            bits = new sbyte[32];

            for (int i = 0; i < 32; i++)
                bits[i] = (sbyte)((u & (1u << i)) >> i);
        }

        public Variable(string name, byte b)
        {
            this.name = name;

            bits = new sbyte[8];

            for (int i = 0; i < 8; i++)
                bits[i] = (sbyte)((b & (1u << i)) >> i);
        }

        public Variable(string name, ushort b)
        {
            this.name = name;

            bits = new sbyte[16];

            for (int i = 0; i < 16; i++)
                bits[i] = (sbyte)((b & (1u << i)) >> i);
        }
        /*
        public Variable(string name, int length)
        {
            this.name = name;

            bits = new sbyte[length];
            for (int i = 0; i < length; i++) bits[i] = -1;
        }
        */
        public Variable(Variable a)
        {
            bits = new sbyte[a.Length];
            for (int i = 0; i < bits.Length; i++) bits[i] = a.bits[i];
            name = a.name;
        }

        public bool CompareTo(Variable other)
        {
            for (int i = 0; i < Length; i++)
                if (bits[i] != -1 && other.bits[i] != -1)
                    if (bits[i] != other.bits[i])
                        return false;
            return true;
        }

        public uint Value()
        {
            uint u = 0;

            for (int i = 0; i < Length; i++)
                if (bits[i] == -1) ;//throw new Exception("undefined value of " + name);
                else u |= (((uint)bits[i]) << i);

            return u;
        }
    }
}
//---------- ReverseCoin.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{8841733F-614A-436F-BBE3-CFEB93A9A2C0}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>ReverseCoin</RootNamespace>
    <AssemblyName>ReverseCoin</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Emulator.cs" />
    <Compile Include="Instruction.cs" />
    <Compile Include="InstructionAnd.cs" />
    <Compile Include="InstructionCopy.cs" />
    <Compile Include="InstructionExchange.cs" />
    <Compile Include="InstructionGetByte.cs" />
    <Compile Include="InstructionMove.cs" />
    <Compile Include="InstructionMoveConst.cs" />
    <Compile Include="InstructionMoveByteExtend.cs" />
    <Compile Include="InstructionNot.cs" />
    <Compile Include="InstructionOr.cs" />
    <Compile Include="InstructionPlus.cs" />
    <Compile Include="InstructionShiftLeft.cs" />
    <Compile Include="InstructionShiftRight.cs" />
    <Compile Include="InstructionXor.cs" />
    <Compile Include="Variable.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
//---------- Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReverseCoin;
using Sha256;
using System.Security.Cryptography;

namespace testSHA256
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] header76 = new byte[76];

            for (int i = 0; i < header76.Length; i++) header76[i] = (byte)i;

            SHA p = new SHA();
            p.sha256_main(header76);
            p.debug = true;
            p.RunDirect();
            p.RunReverseStepByStep();
            p.RunDirect();
            p.PrintOutputsHex();

            SHA256 sha = SHA256Managed.Create();

            byte[] buffer = new byte[80];

            for (int i = 0; i < 76; i++) buffer[i] = (byte)i;

            byte[] hash = sha.ComputeHash(buffer);

            Console.WriteLine();

            for (int i = 0; i < hash.Length; i++) Console.Write("{0:x} ", hash[i]);

            Console.WriteLine();
        }
    }
}
//---------- testSHA256.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{A7ADE99D-52B9-4331-ADC2-EC34D18E9A21}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>testSHA256</RootNamespace>
    <AssemblyName>testSHA256</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\ReverseCoin\ReverseCoin.csproj">
      <Project>{8841733f-614a-436f-bbe3-cfeb93a9a2c0}</Project>
      <Name>ReverseCoin</Name>
    </ProjectReference>
    <ProjectReference Include="..\Sha256\Sha256.csproj">
      <Project>{cb056900-40bf-43a4-89ea-d9748468c192}</Project>
      <Name>Sha256</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
//---------- SHA.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReverseCoin;
using System.Security.Cryptography;

namespace Sha256
{
    public class SHA : Emulator
    {
        public void sha256_main(byte[] header76)
        {
            sha256_init();
            sha256_update(header76);
            sha256_final();
        }

        protected string tmp;

        protected void sha256_init()
        {
            Move("CtxBitlenHigh", (uint)0);
            Move("CtxBitlenLow", (uint)0);
            Move("CtxState0", (uint)0x6a09e667);
            Move("CtxState1", (uint)0xbb67ae85);
            Move("CtxState2", (uint)0x3c6ef372);
            Move("CtxState3", (uint)0xa54ff53a);
            Move("CtxState4", (uint)0x510e527f);
            Move("CtxState5", (uint)0x9b05688c);
            Move("CtxState6", (uint)0x1f83d9ab);
            Move("CtxState7", (uint)0x5be0cd19);
        }

        /*
         * 76 bytes header in data
         */
        protected void sha256_update(byte[] data)
        {
            for (int i = 0; i < 64; i++)
                Move("CtxData" + i, (byte)data[i]);

            Move("CtxDatalen", (uint)64);

            sha256_transform1();
            
            tmp = TMP;
            Move(tmp, (uint)512);
            Plus("CtxBitlenLow", tmp);

            for (int i = 64, j = 0; i < 76; i++, j++)
                Move("Ctx2Data" + j, (byte)data[i]);

            SetInput("nonce", (uint)0);

            GetByte("Ctx2Data12", "nonce", 0);
            GetByte("Ctx2Data13", "nonce", 1);
            GetByte("Ctx2Data14", "nonce", 2);
            GetByte("Ctx2Data15", "nonce", 3);

            //SetInput("Ctx2Data12", (byte)0);
            //SetInput("Ctx2Data13", (byte)0);
            //SetInput("Ctx2Data14", (byte)0);
            //SetInput("Ctx2Data15", (byte)0);

            Move("Ctx2Datalen", (uint)16);
        }


        protected void sha256_final()
        {
            
            Move("Ctx2Data16", (byte)0x80);
            for (int i = 17; i < 56; i++) Move("Ctx2Data" + i, (byte)0);
            
            Move("Ctx2BiltlenLow", (uint)(512 + 16 * 8));
            Move("Ctx2BitlenHigh", (uint)0);

            ulong bitlen = 512 + 8 * 16;

            Move("Ctx2Data63", (uint)bitlen);
            Move("Ctx2Data62", (uint)(bitlen >> 8));
            Move("Ctx2Data61", (uint)(bitlen >> 16));
            Move("Ctx2Data60", (uint)(bitlen >> 24));
            Move("Ctx2Data59", (uint)(bitlen >> 32));
            Move("Ctx2Data58", (uint)(bitlen >> 40));
            Move("Ctx2Data57", (uint)(bitlen >> 48));
            Move("Ctx2Data56", (uint)(bitlen >> 56));
            
            sha256_transform2();

            // hash is here
            SetOutput("CtxState0");
            SetOutput("CtxState1");
            SetOutput("CtxState2");
            SetOutput("CtxState3");
            SetOutput("CtxState4");
            SetOutput("CtxState5");
            SetOutput("CtxState6");
            SetOutput("CtxState7");
        }


        protected void sha256_transform1()
        {
            for (int i = 0, j = 0; i < 16; i++, j += 4)
            {
                MoveByteExtend("m" + i, "CtxData" + (j + 3));
                tmp = TMP;
                MoveByteExtend(tmp, "CtxData" + j);
                string aaa = TMP;
                Move(aaa, (uint)24);
                ShiftLeft(tmp, aaa);
                Or("m" + i, tmp);
                tmp = TMP;
                MoveByteExtend(tmp, "CtxData" + (j + 1));
                aaa = TMP;
                Move(aaa, (uint)16);
                ShiftLeft(tmp, aaa);
                Or("m" + i, tmp);
                tmp = TMP;
                MoveByteExtend(tmp, "CtxData" + (j + 2));
                aaa = TMP;
                Move(aaa, (uint)8);
                ShiftLeft(tmp, aaa);
                Or("m" + i, tmp);
            }

            for (int i = 16; i < 64; i++)
            {
                Copy("m" + i, "m" + (i - 7));
                Plus("m" + i, "m" + (i - 16));
                Plus("m" + i, SIG1("m" + (i - 2)));
                Plus("m" + i, SIG0("m" + (i - 15)));
            }

            // FIX
            //for (int i = 0; i < 64; i++) SetOutput("m" + i);
            
            
            string a = TMP;
            string b = TMP;
            string c = TMP;
            string d = TMP;
            string e = TMP;
            string f = TMP;
            string g = TMP;
            string h = TMP;

            Copy(a, "CtxState" + 0);
            Copy(b, "CtxState" + 1);
            Copy(c, "CtxState" + 2);
            Copy(d, "CtxState" + 3);
            Copy(e, "CtxState" + 4);
            Copy(f, "CtxState" + 5);
            Copy(g, "CtxState" + 6);
            Copy(h, "CtxState" + 7);

            for (int i = 0; i < 64; i++)
            {
                string t1 = TMP;
                Move(t1, "m" + i);
                Plus(t1, h);
                string aaa = TMP;
                Move(aaa, k[i]);
                Plus(t1, aaa);
                string e1 = EP1(e);
                Plus(t1, e1);
                string c1 = CH(e, f, g);
                Plus(t1, c1);

                string t2 = TMP;
                string e0 = EP0(a);
                Move(t2, e0);
                string m0 = MAJ(a, b, c);
                Plus(t2, m0);

                h = TMP;
                Move(h, g);

                g = TMP;
                Move(g, f);

                f = TMP;
                Move(f, e);

                e = TMP;
            Move(e, d);
                Plus(e, t1);

                d = TMP;
                Move(d, c);

                c = TMP;
                Move(c, b);

                b = TMP;
                Move(b, a);

                a = TMP;
                Move(a, t1);
                Plus(a, t2);

                // FIX
                /*    
                if (i == 0)
                {
                    SetOutput(t2);
                    SetOutput(a);
                    SetOutput(b);
                    SetOutput(c);
                    SetOutput(d);
                    SetOutput(e);
                    SetOutput(f);
                    SetOutput(g);
                    SetOutput(h);
                    break;
                }
                */
            }

            Exchange(a, "CtxState0");
            Plus("CtxState0", a);
            Exchange(b, "CtxState1");
            Plus("CtxState1", b);
            Exchange(c, "CtxState2");
            Plus("CtxState2", c);
            Exchange(d, "CtxState3");
            Plus("CtxState3", d);
            Exchange(e, "CtxState4");
            Plus("CtxState4", e);
            Exchange(f, "CtxState5");
            Plus("CtxState5", f);
            Exchange(g, "CtxState6");
            Plus("CtxState6", g);
            Exchange(h, "CtxState7");
            Plus("CtxState7", h);

            // FIX
            //for (int i = 0; i < 8; i++) SetOutput("CtxState" + i);
        }

        protected void sha256_transform2()
        {
            for (int i = 0, j = 0; i < 16; i++, j += 4)
            {
                MoveByteExtend("_m" + i, "Ctx2Data" + (j + 3));
                tmp = TMP;
                MoveByteExtend(tmp, "Ctx2Data" + j);
                string aaa = TMP;
                Move(aaa, (uint)24);
                ShiftLeft(tmp, aaa);
                Or("_m" + i, tmp);
                tmp = TMP;
                MoveByteExtend(tmp, "Ctx2Data" + (j + 1));
                aaa = TMP;
                Move(aaa, (uint)16);
                ShiftLeft(tmp, aaa);
                Or("_m" + i, tmp);
                tmp = TMP;
                MoveByteExtend(tmp, "Ctx2Data" + (j + 2));
                aaa = TMP;
                Move(aaa, (uint)8);
                ShiftLeft(tmp, aaa);
                Or("_m" + i, tmp);
            }

            for (int i = 16; i < 64; i++)
            {
                Copy("_m" + i, "_m" + (i - 7));
                Plus("_m" + i, "_m" + (i - 16));
                Plus("_m" + i, SIG1("_m" + (i - 2)));
                Plus("_m" + i, SIG0("_m" + (i - 15)));
            }

            // FIX
            //for (int i = 0; i < 64; i++) SetOutput("_m" + i);
            string a = TMP;
            string b = TMP;
            string c = TMP;
            string d = TMP;
            string e = TMP;
            string f = TMP;
            string g = TMP;
            string h = TMP;

            Copy(a, "CtxState" + 0);
            Copy(b, "CtxState" + 1);
            Copy(c, "CtxState" + 2);
            Copy(d, "CtxState" + 3);
            Copy(e, "CtxState" + 4);
            Copy(f, "CtxState" + 5);
            Copy(g, "CtxState" + 6);
            Copy(h, "CtxState" + 7);

            for (int i = 0; i < 64; i++)
            {
                string t1 = TMP;
                Move(t1, "_m" + i);
                Plus(t1, h);
                string aaa = TMP;
                Move(aaa, k[i]);
                Plus(t1, aaa);
                string e1 = EP1(e);
                Plus(t1, e1);
                string c1 = CH(e, f, g);
                Plus(t1, c1);

                string t2 = TMP;
                string e0 = EP0(a);
                Move(t2, e0);
                string m0 = MAJ(a, b, c);
                Plus(t2, m0);

                h = TMP;
                Move(h, g);

                g = TMP;
                Move(g, f);

                f = TMP;
                Move(f, e);

                e = TMP;
                Move(e, d);
                Plus(e, t1);

                d = TMP;
                Move(d, c);

                c = TMP;
                Move(c, b);

                b = TMP;
                Move(b, a);

                a = TMP;
                Move(a, t1);
                Plus(a, t2);

                // FIX
                /*    
                if (i == 0)
                {
                    SetOutput(t2);
                    SetOutput(a);
                    SetOutput(b);
                    SetOutput(c);
                    SetOutput(d);
                    SetOutput(e);
                    SetOutput(f);
                    SetOutput(g);
                    SetOutput(h);
                    break;
                }
                */
            }

            Exchange(a, "CtxState0");
            Plus("CtxState0", a);
            Exchange(b, "CtxState1");
            Plus("CtxState1", b);
            Exchange(c, "CtxState2");
            Plus("CtxState2", c);
            Exchange(d, "CtxState3");
            Plus("CtxState3", d);
            Exchange(e, "CtxState4");
            Plus("CtxState4", e);
            Exchange(f, "CtxState5");
            Plus("CtxState5", f);
            Exchange(g, "CtxState6");
            Plus("CtxState6", g);
            Exchange(h, "CtxState7");
            Plus("CtxState7", h);

            // FIX
            //for (int i = 0; i < 8; i++) SetOutput("CtxState" + i);
        }

        protected string ROTRIGHT(string a, uint b)
        {
            tmp = TMP;
            Copy(tmp, a);
            string aaa = TMP;
            Move(aaa, (uint)(32 - b));
            ShiftLeft(tmp, aaa);
            aaa = TMP;
            Move(aaa, (uint)b);

            string bbb = TMP;
            Copy(bbb, a);

            ShiftRight(bbb, aaa);
            Or(bbb, tmp);
            return bbb;
        }

        protected string SIG0(string x)
        {
            string x1 = TMP;
            Copy(x1, x);

            string x2 = TMP;
            Copy(x2, x);

            string x3 = TMP;
            Copy(x3, x);

            string r1 = ROTRIGHT(x1, 7);
            string r2 = ROTRIGHT(x2, 18);

            string b = TMP;
            Move(b, (uint)3);
            ShiftRight(x3, b);

            Xor(x3, r1);
            Xor(x3, r2);

            return x3;
        }

        protected string SIG1(string x)
        {
            string x1 = TMP;
            Copy(x1, x);

            string x2 = TMP;
            Copy(x2, x);

            string x3 = TMP;
            Copy(x3, x);

            string r1 = ROTRIGHT(x1, 17);
            string r2 = ROTRIGHT(x2, 19);

            string b = TMP;
            Move(b, (uint)10);
            ShiftRight(x3, b);

            Xor(x3, r1);
            Xor(x3, r2);

            return x3;
        }

        protected string CH(string x, string y, string z)
        {
            string X = TMP;
            Copy(X, x);

            string X2 = TMP;
            Copy(X2, X);

            And(X, y);
            Not(X2);
            And(X2, z);
            Xor(X, X2);

            return X;
        }

        protected string MAJ(string x, string y, string z)
        {
            string X = TMP;
            Copy(X, x);

            string Y = TMP;
            Copy(Y, y);

            string X2 = TMP;
            Copy(X2, X);

            And(X, y);
            And(X2, z);
            And(Y, z);

            Xor(X, X2);
            Xor(X, Y);

            return X;
        }

        protected string EP0(string x)
        {
            string r1 = ROTRIGHT(x, 2);
            string r2 = ROTRIGHT(x, 13);
            string r3 = ROTRIGHT(x, 22);
            string r4 = TMP;
            Copy(r4, r3);
            Xor(r4, r2);
            Xor(r4, r1);
            return r4;
        }

        protected string EP1(string x)
        {
            string r1 = ROTRIGHT(x, 6);
            string r2 = ROTRIGHT(x, 11);
            string r3 = ROTRIGHT(x, 25);
            string r4 = TMP;
            Copy(r4, r3);
            Xor(r4, r2);
            Xor(r4, r1);
            return r4;
        }




        protected static uint[] k = {
        0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
        0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
        0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
        0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
        0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
        0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
        0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
        0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
        };

        protected static int VariableNumber = 0;

        protected string TMP
        {
            get { string result = "TMP" + VariableNumber; VariableNumber++; return result; }
        }
    }
}
//---------- Sha256.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{CB056900-40BF-43A4-89EA-D9748468C192}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Sha256</RootNamespace>
    <AssemblyName>Sha256</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="SHA.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\ReverseCoin\ReverseCoin.csproj">
      <Project>{8841733f-614a-436f-bbe3-cfeb93a9a2c0}</Project>
      <Name>ReverseCoin</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Об авторе: Дмитрий Негиус (Таломир), PhD, специалист в области защиты информации, криптографии, искусственного интеллекта и игрового боттинга.

cybernetic.anticontrol@gmail.com