LINUX.ORG.RU

Метапрограммирование - проблемы и пути их решения.

 , ,


12

6

Пичал я тут на днях токенайзел для C++-кода, но всё это меня добило я решил поделится.

Проблема - мне надо было вычислять сложные значения для таблицы в компилтайме, да можно на сишке сделать через жопу(енумы) и макросы, но мне стало лень. Да можно сгинерить, но мне тоже лень.

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

Чтобы не быть голословным пишем что-то типа

constexpr uint64_t f(uint64_t a, uint64_t b) {
  return a + b; 
}
Всё ок, но пишем что-то сложнее, аля:

uint64_t m[] = {0, 1, 2, 3, 4};
constexpr uint64_t f(uint64_t a, uint64_t b) {
  return m[a] + m[b]; 
}

Бида( или это моё неосиляторство плюсов?), дак зачем они запилили эту фичу, если она может лишь галимую примитивщину? Шаблоны ещё ущербней. В чем приемущество? Зачем?

А теперь у меня вопрос к вам, уважаемы батьки и отцы - что мне делать? Я хочу запонять массивы написав генератор, причем и в компилтайме тоже. Я хочу юзать libc, я хочу всё, а у меня нет ничего, почему?

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

У меня есть 3 пути: терпеть, пилить свой язык и конпелятор самому( что долго и нудно) и ваш совет.

Ответ на: комментарий от drBatty

Окей, это число 1431655765. Какое отношение оно имеет к 1/3?

То есть как его складывать, перемножать? Особенно интересует его умножение на 3.

aedeph_ ★★
()
Ответ на: комментарий от aedeph_

Окей, это число 1431655765. Какое отношение оно имеет к 1/3?

попробуй разделить его на 4294967296. Только не спрашивая, при чём тут unsigned.

То есть как его складывать, перемножать? Особенно интересует его умножение на 3.

складывать — как обычно. 0x55555555+0x55555555 == 0xAAAAAAAA (⅔).

Умножать тоже как обычно, получится 4294967296(3*⅓), что тоже самое, что и 1. Младшие 32 бита — остаток, только его надо ещё на множитель умножить. Ну или считай это дробной частью. Компилятор gcc в курсе, и если его попросить разделить на 3, он умножит на 0x55555555.

drBatty ★★
()
Ответ на: комментарий от aedeph_

1431655765 * 3 != 4294967296

читай выше про погрешности. В данном случае, я лишку откинул, вот и ошибка вылезла на 1 единичку младшего разряда. Что ты хочешь-то? Многие калькуляторы тоже пишут 1/3*3 == 0.9999999, ты же не ругаешься?

drBatty ★★
()
Ответ на: комментарий от drBatty

Я-то про них знаю. Я не тебе вопросы писал, ты зачем вообще отвечать полез?

aedeph_ ★★
()
Ответ на: комментарий от quasimoto

Тот же алгоритм (Тейлор экспоненты называется, совсем не так как нужно, но просто чтобы показать что мы считаем)

Блин, ну просил же... спалили всю малину;-(

Таки ладно, я готов померятся честным вычислением ряда с ТС-ом. Если он конечно покажет свой код;-)

А так - голый exp(double) без всяких sse у меня занимает 25 таков. «А я че, я ниче, я сама офигела!»(ц)

Кстати, разложение спецфункций в ряд ручками таки используется (если число членов разложения невелико). Например, потовро на малый угол в 3D лучше делать не через матрицу Эйлера а через разложение поворота в Тейлора - там только векторыне проиведение считай да складывай.

AIv ★★★★★
()
Ответ на: комментарий от beastie

Нет, хотя и это тоже;-)

Пусть в 3D пр-ве вектор r поворачивается вокруг вектора n/|n| на угол |n|. Такой поворот можно записать через матрицу Эйлера, и это займет ~170 тактов. А можно разложить в ряд Тейлора (т.н. матричная экспонента как мне сказали), он очень похож на ряд для обычной экспоненты только произведения векторные и (-1)^n. Один член считается за 8-10 тактов, для небольших углов достаточно неск членов + это все отлично ложится на sse.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Таки ладно, я готов померятся честным вычислением ряда с ТС-ом. Если он конечно покажет свой код;-)

ы?

я полагал, что это и предполагалось с самого начала

сообщаю свой результат: одноядерное без-sse вычисление на моей машинке дает менее 38 тактов на всю функцию superhackkiller1997_super_fail; компиляция g++ -O3 -g; точность 10 знаков или больше, если я не ошибся где-нить; юзаются сложение, вычитание, умножение, деление и НИКАКИХ внешних библиотек или интринсиков!

и еще — *не разрешается* пользоваться тем, что значения х идут последовательно и возрастают на одну и ту же величину — функция должна работать так же быстро при значениях х, заданных в полном беспорядке

насчет sse я уверен на 99% — я бегло глянул objdump -S

код теста вот такой:

#include <iostream>
#include <iomanip>

inline double superhackkiller1997_super_fail(double x){
  /// сложение, вычитание, умножение, деление и НИКАКИХ внешних библиотек или интринсиков!
}

int main() {
  double res=0;
  const double step=double(1)/1000/1000/50;
  for( double x=-10; x<=10; x+=step ) {
    res += superhackkiller1997_super_fail(x);
  }
  std::cout << std::setprecision(20) << res << ' ' << superhackkiller1997_super_fail(10) ;
  std::cout << " iterations=" << 20/step;
  return 0;
}

user@host$ g++ -O3 -g float_exp4.cxx
user@host$ time ./a.out 
1101323298430.4301758 22026.465794782285229 iterations=1000000000
real    0m13.407s
user    0m13.409s
sys     0m0.000s

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от AIv

Пусть в 3D пр-ве вектор r поворачивается вокруг вектора n/|n| на угол |n|. Такой поворот можно записать через матрицу Эйлера, и это займет ~170 тактов. А можно разложить в ряд Тейлора (т.н. матричная экспонента как мне сказали), он очень похож на ряд для обычной экспоненты только произведения векторные и (-1)^n. Один член считается за 8-10 тактов, для небольших углов достаточно неск членов + это все отлично ложится на sse.

интересно, че за формула-то? это аналог формулы эйлера для кватернионов?

для двумерки все просто: point(x,y)=exp(iφ)*point(1,0) и можно дифференцировать матрицу exp(iφ) по φ и получить ряд тейлора

комплексное число z с модулем 1 может рассматриваться как матрица поворота, соответствующего умножению на z

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от superhackkiller1997

Нет, дело не в этом. Ты оптимизируешь ненужность для ненужности.

Т.е. посчитать всё это можно изначально проще, но ты считаешь дфу, которые избыточны и потом оптимизируешь их.

бугага, в твоем детсадике экспонента не нужна

ну ладно, а хоть геймдев в твоем детсадике нужен? всякие там пыщь-пышь в трехмерном мире?

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от superhackkiller1997

Нет, дело не в этом. Ты оптимизируешь ненужность для ненужности.

заодно, насчет ненужности экспоненты — допустим, у нас на плите есть кастрюля с горячей водой; мы выключаем газ — кастрюля постепенно охлаждается примерно по экспоненте

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от superhackkiller1997

ну и наконец — ты ж там че-то проповедовал про int64; у меня получилось 38 тактов без особого вылизывания, и это значит, что на твоих интах но с sse есть шансы получить меньше

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

а для тебя польза в том, что ты начинаешь видеть перспективу и куда тебе можно расти

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

А для какого N? Тащем то я ту же цифирь могу тактов за 50 получить (суммарно, все 10...0 итераций;-P)

для двумерки все просто: point(x,y)=exp(iφ)*point(1,0) и можно дифференцировать матрицу exp(iφ) по φ и получить ряд тейлора

Ну это один из вариантов вывода. Мне больше нравится через дифур

Че то подумалось - ТС тут обижаится на нас, что дескать мы его чморим за отсутствие «образования». На самом деле же, я как то привык к постулату «у доски все равны, и студенты и академики» - т.е. он и тута слил. Кабы пошел в нормальный вуз, то знал бы что бьют по морде а не по диплому, и корочки твои никого акромя отдела кадров не интересуют. Все претензии то к бедолаге из за его тотальной анскильнности... люди странные жЫвотные.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Тащем то я ту же цифирь могу тактов за 50 получить (суммарно, все 10...0 итераций;-P)

какую цифирь? если речь про 1101323298430, то очевидно можешь (тактов за 100), т.к. экспонента суммируется в явном виде

даже если бы она не суммировалась явно, я бы не страдал фигней, а юзал бы формулу суммирования эйлера

1101323298430 там по следующим причинам:

1. time ./a.out надежнее и точнее rdtsc (особенно, если используется if и время может меняться (впрочем, у меня не используется))

2. чтобы оптимизатор не выкинул ненужные с его точки зрения действия

3. для контроля — скажем, в одной из реализаций на интах погрешность была 10% (гы-гы)

А для какого N?

я не знаю что такое N — я вычисляю exp(x) для любого -10<=x<=10 с относительной погрешностью 1е-10 или даже меньше; там не совсем тейлор, и простор для улучшения

тейлором тебе придется брать примерно 43 члена и это будет раза в 2-3 тормознее

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

люди странные жЫвотные

барсук пришел на полянку с криками про свою охрененую крутизну; на крик подтянулись зубры, и объяснили, что он не прав — ниче странного, обычный рабочий процесс

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 2)
Ответ на: комментарий от superhackkiller1997

а пока ты думаешь над тем, нужен ли геймдев, или это тоже «ненужность для ненужности» (бугага), а то и вдруг, чем черт не шутит, все же пытаешься выкатить оптимизированный вариант функции, я поотвечаю на некоторые твои высказывания — там ты хотя и не прав, но ставишь интересные вопросы (как и int vs. float кстати)

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

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от beastie

Всё верно, а где ошибка?

что ты этим хотел сказать?

drBatty ★★
()
Ответ на: комментарий от www_linux_org_ru

ты ж там че-то проповедовал про int64; у меня получилось 38 тактов без особого вылизывания

что-то много. А это с учётом переполнений и т.д.? У меня больше получалось, но у меня только 32х битные регистры были.

drBatty ★★
()
Ответ на: комментарий от www_linux_org_ru

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

с НЁХ нужно бороться её же методами, можешь например попробовать подмешать в флоат рандомные 2 младших разряда. Это сотрёт небольшую детерминированость, которая там есть к сожалению. Т.е. величина коррекции float сильно(несколько %%) отличается от ½ для некоторых значений.

(разве что интервальной арифметикой, но это уже дольше считать)

float и так интервальный, только несколько упрощённый. ЕМНИП сложение и выражение там выполняется «точно» (в интервальном смысле), а вот деление с ошибкой. Истинное деление в интервалах занимает слишком много времени, и, ЕМНИП, неоднозначно.

drBatty ★★
()
Ответ на: комментарий от www_linux_org_ru

, чего на флоатах я не знаю как добиться (разве что интервальной арифметикой, но это уже дольше считать)

google://Geometric Numerical Integration: Structure Preservihg Alrogithims for Ordinary Differential Equations.

методы есть, много, не простые.

qnikst ★★★★★
()
Ответ на: комментарий от drBatty

38 тактов это на даблах (а не на инт64), и да, это очень много, но я юзал исключительно + — / *

на интах чего мне не хватает — это PMULHUW (Multiply Packed Unsigned Integers and Store High Result), только для умножения 32-битных или 64-битных операндов (или даже 128-битных); я не говорю что его нет — но манулы по sse мне влоооооооооом рыть

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

38 тактов это на даблах (а не на инт64), и да, это очень много, но я юзал исключительно + — / *

а если меньше делений? Оно раньше _сильно_ тормозило.

на интах чего мне не хватает — это PMULHUW (Multiply Packed Unsigned Integers and Store High Result), только для умножения 32-битных или 64-битных операндов (или даже 128-битных); я не говорю что его нет — но манулы по sse мне влоооооооооом рыть

яхз. Мне тоже лень.

drBatty ★★
()
Ответ на: комментарий от www_linux_org_ru

про что и речь — непрофильному специалисту инты могут оказаться более удобной заменой

это только на первый взгляд. IRL с ними хлопот куда как больше.

drBatty ★★
()
Ответ на: комментарий от www_linux_org_ru

time ./a.out надежнее и точнее rdtsc

Да я че то вообще omp_get_wtime() юзаю...

В Тейлоре от делений я бы увернулся, введя глобальную табличку обратных факториалов, ТС кстати это сообразил;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

Я долго пытался понять, что эта байда делает. В конечном итоге немного благодаря пациентам до меня допёрло, что это e^x.

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

superhackkiller1997
() автор топика
Ответ на: комментарий от www_linux_org_ru

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

qnikst ★★★★★
()
Ответ на: комментарий от AIv

Кстати, ты знаешь реализации псевдостепени быстрее, чем 1умножение на разряд?

Да, я даже даже должен признать, что твоя математика немного интересна и я так и не передумал у тебя обучаться.

Сегодня у меня почти небыло времени пилить и флудить, но я пилю с намного большим приорететом, чем флужу.

superhackkiller1997
() автор топика
Ответ на: комментарий от superhackkiller1997

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

AIv ★★★★★
()
Ответ на: комментарий от superhackkiller1997

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

AIv ★★★★★
()
Ответ на: комментарий от AIv

Особенно для астрономических.

Полагаю, астрономические задачи списаны как неинтересные практически. Ну или int128_t, ага.

tailgunner ★★★★★
()
Ответ на: комментарий от AIv

Я полагаю, что для прикладной астрономии (т.е. тела, летающие в пределах Солнечной системы), расчетов в нано{секундах,метрах,граммах} хватит :) Астрофизика же практической ценностью не имеет, это очевидно.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

Астрофизика (та что солнцем занимается) очевидно тоже весьма практична.

Сама астрономия - астронавигационные системы навскидку из практики. Ну и наконец надо бы слетать на альфа-центавру, ознакомиться с проектом строительства гиперпространственной развязки;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

Ну я даже графики рисовал, глядел, считал, википедию курил - я хороший.

Я придумал стопицот способов, но так и не придумал чего-то, что требудет менее 1умножения на разряд.

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

Давай придумаем с тобой на 10тактов.

superhackkiller1997
() автор топика
Ответ на: комментарий от AIv

Если тебе 64бит не хватит, то тебе и дабла не хватит. А если им не хватит 64бит, то запилить что-то больше не особо проблемно и даже будет не особо тормазнее.

superhackkiller1997
() автор топика
Ответ на: комментарий от superhackkiller1997

Давай придумаем с тобой на 10тактов.

Без меня. Фихед пойнт мне неинтересен.

Если тебе 64бит не хватит, то тебе и дабла не хватит.

Неправда. Вы до сих пор не поняли что есть точность. Например на флоатинг пойнт:

10+1 - относительная ошибка почти не меняется
10-9 - относительная ошибка возрастает в десять раз
10*1 - относительная ошибка почти не меняется
10/1 - относительная ошибка почти не меняется

На фихед пойнт (считаем в нанопопугаях):

10+1 - относительная ошибка почти не меняется
10-9 - относительная ошибка возрастает в десять раз
10*1 - относительная ошибка возрастает в десять раз
10/1 - относительная ошибка возрастает в десять раз

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

ДЖаст фор фан подобные извращения м.б. интересны (мне нет), но практического смысла очевидно не имеют. Эта тема была раскрыта целиком и полностью более 30ти лет назад, когда компьютеры были большими.

Хочете интересную задачу на битиках, приносящую пользу - попробуйте сделать вот это [C/C++] перетасовка бит в целом числе

Мы в итоге остановились на реализации через числа Мортона http://stackoverflow.com/questions/1024754/how-to-compute-a-3d-morton-number-...

AIv ★★★★★
()
Ответ на: комментарий от AIv

Нет, я не про фикседпоинт - будем юзать даблы, если хотите.

Я прозрел, я понял, что такое логарифм, зачем нужны граффики, что такое производная. Понял про ускорение, понял зачем оно нужно и т.п.

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

Ничего небыло раскрыто - на эту тему просто забили.

Погляжу.

superhackkiller1997
() автор топика
Ответ на: комментарий от superhackkiller1997

Я прозрел,

Ну чего, круто если так! Раз за Вас, без всякого сарказма...

Ничего небыло раскрыто - на эту тему просто забили.

Ээээ...были например всякие экзотические решения, типа использования логарифмической шкалы вместо обычной равномерной. Тогда */ превращается в сложения, степень в умножение, с +- правда сложности. Но для узких интервалов больших чисел опять таки валится точность.

Один оочень квалифицированный дядечка, пиливший матаппарат для новой линейки наших советских компов в незапамятные совейские времена, рассказывал как после окончания разработки (а делали они именно то что Вы хотите запилить, представления чисел, всякие степени и проч.), решил он это дело оформить в виде докторской, потому как получилось «хорошо и хорошо весьма». Почитал он амерские журналы по этой тематике... и понял что амеры все это (и много чего еще) уже сделали лет 10 назад, а мы отстали хрен знает на сколько. Тогда забил он на докторскую и пошел решать прикладные задачи.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Ну чего, круто если так! Раз за Вас, без всякого сарказма...

Я никогда не против был делать так, как вы хотите. Юзать то, что вы хотите и т.п. Я просто считаю, что некоторые вещи не правильны и не более.

Один оочень квалифицированный дядечка, пиливший матаппарат для новой линейки наших советских компов в незапамятные совейские времена, рассказывал как после окончания разработки (а делали они именно то что Вы хотите запилить, представления чисел, всякие степени и проч.), решил он это дело оформить в виде докторской, потому как получилось «хорошо и хорошо весьма». Почитал он амерские журналы по этой тематике... и понял что амеры все это (и много чего еще) уже сделали лет 10 назад, а мы отстали хрен знает на сколько. Тогда забил он на докторскую и пошел решать прикладные задачи.

Только он не учёл одну вещь, да амеры всё это уже запиливали, но... Это всё похороненно не из-за того, что это фуфло и это пройдено, а из-за того, что это не выгодно.

В тот момент, когда все эти разработки были на пике - пришла популяризация и тотальное удешевление( ведь с ЯП получилось так же) и небыло смысла спонсировать какой-то сопроцессор, когда как вы уже сами говорили - проще купить ещё 20-30камней( кстати, это ложное утверждение. Да, запил сопроцессора для одной ваше задачи будет дороже, но запил его для всех - будет намного дешевле, чем камни, которые дешевые как грязь).

Точно так же забили амеры на свои разработки, только не из-за того, что русские их запилили, а из-за того, что все их послали.

superhackkiller1997
() автор топика
Ответ на: комментарий от superhackkiller1997

Флоатинг пойнт таки оптимально подходит для большей части задач, а поскольку современное железо дешево именно из за своей массовости, никто не будет делать какие то узкозаточенные вещи для тех задач где флоатинг пойнт не совсем оптимален - там профит от перехода на фихед ИМНО не так уж и велик. Хотя вон же приводили пример бульдозера.

Для самых радикальных товарищей есть FPGA, м.б. Вам их поковырять? Вот где простор для творчества... Для массовых инженерных расчетов они ИМНО слишком дороги, но есть области где FPGA вне конкуренции.

Кстати, в формате seg-Y (международный стандарт для сейсмических данных) в описании есть один флоатинг пойнт ИБМ и 3 что ли варианта фихед пойнтов... а сейчас все данные в этом формате всегда ходят в ИЕЕЕ флоатинг пойнт;-)

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

AIv ★★★★★
()
Ответ на: комментарий от AIv

Возможно.

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

Суть не в том, что это ваукакаяштука, а суть в том «почему это не юзают?». Придумать проще, чем найти( особенно в наш век), а уж найти инфу про то почему эта штука не взлетела, причемо бъективную с шансом почти 0 искать не смысла.

Про задание - я пока ничего лучше не придумал, чем 2^(x*lb(e)). Я думал, что 2 в степени n посчитать будет удобней, но тут всякие не целые числа с которыми тысячи мороки.

Сейчас пытаюсь как-то опсилить это.

superhackkiller1997
() автор топика
Ответ на: комментарий от drBatty

float и так интервальный, только несколько упрощённый. ЕМНИП сложение и выражение там выполняется «точно» (в интервальном смысле)

он не интервальный

он мог бы быть интервальным, если бы оперировал денормализованными числами в штатном режиме, хотя это все равно костыль, а не полноценная интервальная арифметика

хинт:

«от 3 до 4» + «от -3 до -4» = «от -1 до +1», что в иеее представления не имеет

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.