Wątek zablokowany 2019-12-18 10:43 przez cerrato.

Wątek przeniesiony 2019-12-09 17:56 z przez cerrato.

Głosowanie na zgłoszenia w ramach konkursu 2019 - PKT. 2

4

W ramach konkursu na koniec 2019, zadanie z pkt.2 polegało na maksymalnym zagmatwaniu trywialnego zagadnienia. Zgodnie z deklaracja, odpowiedzi które wpłynęły zostaną opublikowane, a następnie przez kilka dni każdy użytkownik forum będzie miał możliwość oddania swojego głosu.

Ponieważ mamy do oddania dwie książki, więc jedna z nich pójdzie do autora kodu z największą ilością oddanych głosów, a druga dla autora najbardziej łapkowanego wpisu (uprzedzając głosy złośliwców - łapki w dziale "Społeczność" nie liczą się do rankingu, nie jest to żaden podstęp z mojej strony :D)

Parę uwag technicznych - proszę nie dodawać nowych postów w wątku, bo będą one kasowane.
Jakiekolwiek pytania dot. danego zgłoszenia proszę zamieszczać w komentarzach.

Jeśli któryś z autorów ma zastrzeżenia do wklejonego kodu - proszę o kontakt na PW ze stosowną infomacją.

Głosowanie START :)

5

Zgłoszenie numer 1

C++11, GCC

$ g++ konkurs.cpp -o test
$ ./test 
Enter the value to recalculate
> 1
235.21458
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <cmath>
#include <numeric>

#define iw(t, n) (n)
#define B(x) ((x>>i)&1)
#define C(x,y) (B(x)+B(y)+c)

double mul(double a, double b);

union UD { 
    uint64_t ui; 
    double f;
};

template <typename T> 
int S(int r, T& m, int i) {
    return ((m)|=1<<i)?r:r;
}

template <typename T, typename B> 
T s(T a, B b) {
    T c{}, r{};
    for(int i=0; i<sizeof(a)<<3;i++) {
        c=!C(a,b)?0:(C(a,b)==1?S(0,r,i):(C(a,b)==2?1:S(1,r,i)));
    }
    return r;
}

double num() {
    std::vector<int> d{1, 1};
    for(int i=0; i<5; i++) {
        d.push_back(d[d.size()-1]+d[d.size()-2]);
    }

    std::transform(d.begin(), d.end(), d.begin(), [](int d) { return d % 6; });
    d.erase(d.begin());
    d.erase(d.begin());

    double l = log2(13) / log2(17) * 100000;
    for(int i=0; i<4; i++) {
        l *= 10;
        d.push_back(static_cast<int>(l)%10);
    }

    std::reverse(d.begin(), d.end());
    double result = std::accumulate(d.begin(), d.end(), 0.0, [](double acc, int val){
        return mul(acc, 0.1) + val;
    });

    return mul(result, 100);
}

double mul(double a, double b) {
    UD uA;
    uint64_t uiA;
    bool signA;
    int16_t expA;
    uint64_t sigA;
    UD uB;
    uint64_t uiB;
    bool signB;
    int16_t expB;
    uint64_t sigB;
    bool signZ;
    uint64_t magBits;
    int16_t expZ;
    bool roundNearEven = false;
    uint_fast16_t roundIncrement, roundBits;
    bool isTiny;
    uint_fast64_t uiZa;
    UD usZ;
    uint32_t sig128Z[4];
    uint64_t sigZ, uiZ;
    UD uZ;

    uA.f = a;
    uiA = uA.ui;
    signA =  uiA >> 63;
    expA = (uiA>>52) & 0x7FF; 
    sigA  = uiA & UINT64_C( 0x000FFFFFFFFFFFFF );
    uB.f = b;
    uiB = uB.ui;
    signB = uiB >> 63;
    expB = (uiB>>52) & 0x7FF; 
    sigB  = uiB & UINT64_C( 0x000FFFFFFFFFFFFF );
    signZ = signA ^ signB;

    if ( expA == 0x7FF ) {
        if ( sigA || ((expB == 0x7FF) && sigB) ) goto propagateNaN;
        magBits = expB | sigB;
        goto infArg;
    }
    if ( expB == 0x7FF ) {
        if ( sigB ) goto propagateNaN;
        magBits = expA | sigA;
        goto infArg;
    }

    if ( ! expA ) {
        if ( ! sigA ) goto zero;
        int8_t shiftDist = (sigA ? __builtin_clzll(sigA) : 64) - 11;
        expA = 1 - shiftDist;
        sigA = sigA<<shiftDist;
    }
    if ( ! expB ) {
        if ( ! sigB ) goto zero;
        int8_t shiftDist = (sigB ? __builtin_clzll(sigB) : 64) - 11;
        expB = 1 - shiftDist;
        sigB = sigB<<shiftDist;
    }

    expZ = s(s(expA, expB), -0x3FF);
    sigA = (sigA | UINT64_C( 0x0010000000000000 ))<<10;
    sigB = (sigB | UINT64_C( 0x0010000000000000 ))<<11;

    uint32_t a32, a0, b32, b0;
    uint64_t z0, mid1, z64, mid;

    a32 = sigA>>32;
    a0 = sigA;
    b32 = sigB>>32;
    b0 = sigB;
    z0 = (uint64_t) a0 * b0;
    mid1 = (uint64_t) a32 * b0;
    mid = mid1 + (uint64_t) a0 * b32;
    z64 = (uint64_t) a32 * b32;
    z64 += (uint64_t) (mid < mid1)<<32 | mid>>32;
    mid <<= 32;
    z0 += mid;
    sig128Z[iw( 4, 1 )] = z0>>32;
    sig128Z[iw( 4, 0 )] = z0;
    z64 += (z0 < mid);
    sig128Z[iw( 4, 3 )] = z64>>32;
    sig128Z[iw( 4, 2 )] = z64;

    sigZ = (uint64_t) sig128Z[iw( 4, 3 )]<<32 | sig128Z[iw( 4, 2 )];
    if ( sig128Z[iw( 4, 1 )] || sig128Z[iw( 4, 0 )] ) sigZ |= 1;

    if ( sigZ < UINT64_C( 0x4000000000000000 ) ) {
        --expZ;
        sigZ <<= 1;
    }
    roundIncrement = 0x200;
    roundBits = sigZ & 0x3FF;
    if ( 0x7FD <= (uint16_t) expZ ) {
        if ( expZ < 0 ) {
            isTiny = (expZ < -1) || (sigZ + roundIncrement < UINT64_C( 0x8000000000000000 ));
            sigZ = (-expZ < 63) ? sigZ>>-expZ | ((uint64_t) (sigZ<<(expZ & 63)) != 0) : (sigZ != 0);
            expZ = 0;
            roundBits = sigZ & 0x3FF;
        } else if (
            (0x7FD < expZ)
                || (UINT64_C( 0x8000000000000000 ) <= sigZ + roundIncrement)
        ) {
            uiZa = (uint64_t) (((uint_fast64_t) (signZ)<<63) + ((uint_fast64_t) (0x7FF)<<52) + 0) - ! roundIncrement;
            goto uiZa;
        }
    }
    sigZ = (sigZ + roundIncrement)>>10;

    sigZ &= ~(uint_fast64_t) (! (roundBits ^ 0x200) & roundNearEven);
    if ( ! sigZ ) expZ = 0;
packReturn:
    uiZa = (uint64_t) (((uint_fast64_t) (signZ)<<63) + ((uint_fast64_t) (expZ)<<52) + (sigZ));
uiZa:
    usZ.ui = uiZa;
    return usZ.f;
propagateNaN:
    bool isSigNaNA, isSigNaNB;
    uint_fast64_t uiNonsigA, uiNonsigB, uiMagA, uiMagB;
    uiNonsigA = uiA | UINT64_C( 0x0008000000000000 );
    uiNonsigB = uiB | UINT64_C( 0x0008000000000000 );
    if ( isSigNaNA | isSigNaNB ) {
        if ( isSigNaNA ) {
            if ( isSigNaNB ) goto returnLargerMag;

            return ((~(uiB) & UINT64_C( 0x7FF0000000000000 )) == 0) && ((uiB) & UINT64_C( 0x000FFFFFFFFFFFFF )) ? uiNonsigB : uiNonsigA;
        } else {
            return ((~(uiA) & UINT64_C( 0x7FF0000000000000 )) == 0) && ((uiA) & UINT64_C( 0x000FFFFFFFFFFFFF )) ? uiNonsigA : uiNonsigB;
        }
    }
returnLargerMag:
    uiMagA = uiA & UINT64_C( 0x7FFFFFFFFFFFFFFF );
    uiMagB = uiB & UINT64_C( 0x7FFFFFFFFFFFFFFF );
    if ( uiMagA < uiMagB ) return uiNonsigB;
    if ( uiMagB < uiMagA ) return uiNonsigA;
    uiZ = (uiNonsigA < uiNonsigB) ? uiNonsigA : uiNonsigB;
    goto uiZ;
infArg:
    if ( ! magBits ) {
        uiZ = UINT64_C( 0xFFF8000000000000 );
    } else {
        uiZ = (uint64_t) (((uint_fast64_t) (signZ)<<63) + ((uint_fast64_t) (0x7FF)<<52));
    }
    goto uiZ;
zero:
    uiZ = (uint64_t) (signZ)<<63;
uiZ:
    uZ.ui = uiZ;
    return uZ.f;
}

int main(void) {
    double value = 0;
    std::cout << "Enter the value to recalculate\n"; 
    while ((std::cout << "> ") && (!(std::cin >> value) || value < 0 )) {
        std::cout << "Not a valid value\n";
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    std::cout.precision(5);
    std::cout << std::fixed << mul(num(), 1/value) << std::endl;
}
1

Zgłoszenie numer 2

Python 3.6

import enum
from decimal import Decimal
from random import random
ONE_KILOMETER = 1
ONE_MILE = 1
ONE_GALLON_OF_FUEL = 1
class Units(enum.Enum):
    miles = 1
    liters = 2
class Languages(enum.Enum):
    polish = 1
    english = 2
class CalculateTypes(enum.Enum):
    liters_to_miles = 1
    miles_to_liters = 2
CHOOSE_LANGUAGE_MESSAGE = 'Choose language: \n 1: polish \n 2: english'
TRY_AGAIN_MESSAGE = 'Your chose was out of scope. Please try again and choose correct one'
POLISH_CHOOSE_CALCULATOR_MESSAGE = 'Wybierz jakie obliczenia chcesz dokonać: \n 1: Litry na 100 kilometrów --> Mile na galon \n 2: Mile na galon --> Litry na 100 kilometrów'
ENGLISH_CHOOSE_CALCULATOR_MESSAGE = 'Choose what kind of calc you want to do: \n 1: Liters per 100 kilometers --> Miles per galon \n 2: Miles per galon --> Liters per 100 kilometers'
POLISH_TRY_AGAIN_MESSAGE = 'Twój wybór był spoza zakresu. Spróbuj ponownie i wybierz poprawny kalkulator'
ENGLISH_TRY_AGAIN_MESSAGE = 'Your chose was out of scope. Please try again and choose correct calculator'
POLISH_CHOOSE_VALUE_BEFORE_DOT_MESSAGE = 'Wpisz liczbę {} przed przecinkiem'
POLISH_CHOOSE_VALUE_AFTER_DOT_MESSAGE = 'Wpisz liczbę {} po przecinku'
ENGLISH_CHOOSE_VALUE_BEFORE_DOT_MESSAGE = 'Enter the number of {} before the decimal point'
ENGLISH_CHOOSE_VALUE_AFTER_DOT_MESSAGE = 'Enter the number of {} after the decimal point'
POLISH_TRY_AGAIN_VALUE_MESSAGE = 'Twój wybór był spoza zakresu. Spróbuj ponownie i wpisz poprawną liczbę'
ENGLISH_TRY_AGAIN_VALUE_MESSAGE = 'Your chose was out of scope. Please try again and write correct number'
class BaseCalculandoException(Exception):
    pass
class WrongUnitException(BaseCalculandoException):
    pass
class WrongNumberException(BaseCalculandoException):
    pass
class OneKilometerGetter:
    def __new__(cls, *args, **kwargs):
        return ONE_KILOMETER
class OneHundredKilometer:
    def __init__(self, one_kilometer):
        self.one_kilometer = one_kilometer
    def get_one_hundred_kilometer_from_one_kilometer(self):
        one_hundred_kilometer = 0
        while one_hundred_kilometer < 100: # todo 100 moze byc zmienna
            one_hundred_kilometer += self.one_kilometer
        return one_hundred_kilometer
one_kilometer = OneKilometerGetter()
ONE_HUNDRED_KILOMETER = OneHundredKilometer(
    one_kilometer
).get_one_hundred_kilometer_from_one_kilometer()
if ONE_HUNDRED_KILOMETER < 100:
    raise ValueError('It should rasie error')
def create_factor(func):
    def wrap(self):
        self.factor = 45819
        return func(self)
    return wrap
class Calculator:
    def __init__(self, value, direction):
        self.value = value
        self.direction = direction
        if direction == CalculateTypes.liters_to_miles:
            self.type = 'L/100KM_MPG'
        elif direction == CalculateTypes.miles_to_liters:
            self.type = 'MPG_L/100KM'
    def calc(self):
        try:
            return self.calc_offline()[0]
            # return self.calc_online()
        except:
            return self.calc_offline()[0]
    def calc_online(self):
        import re
        from urllib import parse, request
        for i in range(100):
            while random(1):
                data = parse.urlencode({
                    'measure': self.value.number, 'type': self.type, 'rounding': '2', 'admin': 'convert',
                }).encode()
                request_ = request.Request('https://carrentsale.com/calculators/convert-mpg-to-l-100-km.htm', data=data)
                response = request.urlopen(request_)
                return re.search('<b>\d+\.\d+</b>', response.read().decode('utf-8'))[0][3:-4]
        return 22 + 11 + sum(range(3)) + '6'.encode('utf-8')
    @create_factor
    def calc_offline(self):
        # do not move!
        if self.direction == CalculateTypes.liters_to_miles:
            return ((float(self.value.number) * (((59 << max(1, 6)) + 9) / ((min(37, 60) << 5) - (23 << 3)))) / (((self.factor >> 5) + (self.factor >> 8)) / ((37 << 5) - (23 << 3)))), min(65, 66)
        elif self.direction == CalculateTypes.miles_to_liters:
            return ((float(self.value.number) * ((self.factor >> 5) + (self.factor >> 8)) / ((37 << 5) - (23 << 3))) / (((59 << 6) + 9) / ((37 << 5) - (23 << 3)))), max(65, 68)

class Value:
    def __init__(self, number, chosen_language):
        self.number = number
        self.chosen_language = chosen_language
    @property
    def number(self):
        return self.__number
    @number.setter
    def number(self, number):
        if number < 0:
            if self.chosen_language == Languages.polish:
                raise WrongNumberException('Liczba nie może być ujemny')
            elif self.chosen_language == Languages.english:
                raise WrongNumberException('Value cannot be negative')
        if number > 150:
            if self.chosen_language == Languages.polish:
                raise WrongNumberException('Samochody nie jeżdzą tak szybko')
            elif self.chosen_language == Languages.english:
                raise WrongNumberException('Cars do not drive that fast')
        self.__number = number
class Unit:
    def __init__(self, unit_type, value):
        self.unit_type = unit_type
        self.value = value

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, value):
        if not isinstance(value, Value):
            raise WrongUnitException('value has to be Value instance')
        self.__value = value

    @property
    def unit_type(self):
        return self.__unit_type

    @unit_type.setter
    def unit_type(self, unit_type):
        if not isinstance(unit_type, Units):
            raise WrongUnitException('unit_type has to be Units instance')
        self.__unit_type = unit_type
class LanguageChooser:
    def __init__(self):
        self.choose_language_message = CHOOSE_LANGUAGE_MESSAGE
        self.try_again_message = TRY_AGAIN_MESSAGE
    def chose_language(self):
        self.print_choose_language_message()
        user_choice = input()
        if not self.validate_user_choice(user_choice):
            self.print_try_again()
            return self.chose_language()
        return Languages(int(user_choice))
    def print_choose_language_message(self):
        print(self.choose_language_message)
    def print_try_again(self):
        print(self.try_again_message)
    @staticmethod
    def validate_user_choice(user_choice):
        if user_choice == '1':
            return True
        elif user_choice == '2':
            return True
        else:
            return False
class CalculatorChooser:
    def __init__(self, chosen_language):
        self.chosen_language = chosen_language
        self.polish_choose_calculator_message = POLISH_CHOOSE_CALCULATOR_MESSAGE
        self.english_choose_calculator_message = ENGLISH_CHOOSE_CALCULATOR_MESSAGE
        self.polish_try_again_message = POLISH_TRY_AGAIN_MESSAGE
        self.english_try_again_message = ENGLISH_TRY_AGAIN_MESSAGE
    def chose_calculator(self):
        self.print_choose_calculator_message()
        user_choice = input()
        if not self.validate_user_choice(user_choice):
            self.print_try_again()
            return self.chose_calculator()
        return CalculateTypes(int(user_choice))
    def print_try_again(self):
        if self.chosen_language == Languages.polish:
            self.print_polish_try_again_message()
        elif self.chosen_language == Languages.english:
            self.print_english_try_again_message()
    def print_polish_try_again_message(self):
        print(self.polish_try_again_message)

    def print_english_try_again_message(self):
        print(self.english_try_again_message)

    def print_choose_calculator_message(self):
        if self.chosen_language == Languages.polish:
            self.print_polish_choose_calculator_message()
        elif self.chosen_language == Languages.english:
            self.print_english_choose_calculator_message()

    def print_polish_choose_calculator_message(self):
        print(self.polish_choose_calculator_message)

    def print_english_choose_calculator_message(self):
        print(self.english_choose_calculator_message)

    @staticmethod
    def validate_user_choice(user_choice):
        if user_choice == '1':
            return True
        elif user_choice == '2':
            return True
        else:
            return False

class ValueGetterBefore:
    validation_errors = (ValueError,)

    def __init__(self, chosen_language, calculator_type):
        self.chosen_language = chosen_language
        self.polish_choose_value_message = POLISH_CHOOSE_VALUE_BEFORE_DOT_MESSAGE
        self.english_choose_value_message = ENGLISH_CHOOSE_VALUE_BEFORE_DOT_MESSAGE
        self.polish_try_again_message = POLISH_TRY_AGAIN_VALUE_MESSAGE
        self.english_try_again_message = ENGLISH_TRY_AGAIN_VALUE_MESSAGE
        if calculator_type == CalculateTypes.liters_to_miles:
            value_type = 'lit'
            if self.chosen_language == Languages.polish:
                self.value_type = ''.join(list(value_type) + ['r', 'ó', 'w'])
            elif self.chosen_language == Languages.english:
                self.value_type = ''.join(list(value_type) + ['e', 'r', 's'])
        elif calculator_type == CalculateTypes.miles_to_liters:
            value_type = 'mil'
            if self.chosen_language == Languages.polish:
                self.value_type = ''.join(list(value_type) + ['i'])
            elif self.chosen_language == Languages.english:
                self.value_type = ''.join(list(value_type) + ['e', 's'])

    def chose_value(self):
        self.print_choose_value_message()
        user_choice = input()
        if not self.validate_user_choice(user_choice):
            self.print_try_again()
            return self.chose_value()
        return user_choice

    def print_try_again(self):
        if self.chosen_language == Languages.polish:
            self.print_polish_try_again_message()
        elif self.chosen_language == Languages.english:
            self.print_english_try_again_message()

    def print_polish_try_again_message(self):
        print(self.polish_try_again_message)

    def print_english_try_again_message(self):
        print(self.english_try_again_message)

    def print_choose_value_message(self):
        if self.chosen_language == Languages.polish:
            self.print_polish_choose_value_message()
        elif self.chosen_language == Languages.english:
            self.print_english_choose_value_message()

    def print_polish_choose_value_message(self):
        print(self.polish_choose_value_message.format(self.value_type))

    def print_english_choose_value_message(self):
        print(self.english_choose_value_message.format(self.value_type))

    @classmethod
    def validate_user_choice(cls, user_choice):
        try:
            int(user_choice)
            return True
        except cls.validation_errors:
            return False
class ValueGetterAfter(ValueGetterBefore):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.polish_choose_value_message = POLISH_CHOOSE_VALUE_AFTER_DOT_MESSAGE
        self.english_choose_value_message = ENGLISH_CHOOSE_VALUE_AFTER_DOT_MESSAGE
class ValueCreator:
    def __init__(self, value_before_dot, value_after_dot):
        self.value_before_dot = value_before_dot
        self.value_after_dot = value_after_dot

    def connect(self, chosen_language):
        try:
            return Value(Decimal('%s.%s' % (
                self.value_before_dot,
                self.value_after_dot
            )), chosen_language)
        except Exception:
            raise

def main():
    while True:
        # label 1
        chosen_language = LanguageChooser().chose_language()
        calculator_type = CalculatorChooser(chosen_language).chose_calculator()
        value_before_dot = ValueGetterBefore(chosen_language, calculator_type).chose_value()
        value_after_dot = ValueGetterAfter(chosen_language, calculator_type).chose_value()
        try:
            value = ValueCreator(value_before_dot, value_after_dot).connect(chosen_language)
        except Exception as e:
            if chosen_language == Languages.polish:
                print('Cos nie tak, od nowa', e)
            elif chosen_language == Languages.english:
                print('Whoops, something went wrong. Try again', e)
            continue # go to label 1
        print('Output: ' if chosen_language == Languages.polish else 'Wynik: ', Calculator(value, calculator_type).calc())
        break

if __name__ == '__main__':
    main()
1

Zgłoszenie numer 3

Python3

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from json import loads as parseJSON
from html.parser import HTMLParser
import locale
import re
import string
import subprocess
import sys
from urllib.request import Request
from urllib.request import urlopen as makeRequest
from urllib.parse import urlencode as encodeURL

class DuckDuckGoAnswerExtractor(HTMLParser):
    def __init__(self):
        super(DuckDuckGoAnswerExtractor, self).__init__()
        self.status = "pre"
        self.data = ""

    def handle_starttag(self, tag, attrs):
        if self.status != "pre": return

        if tag != "td": return

        attrs = dict(attrs)
        if ("class" not in attrs) or (attrs["class"] != "result-snippet"): return

        self.status = "in"

    def handle_endtag(self, tag):
        if self.status != "in": return
        if tag != "td": return

        self.status = "post"

    def handle_data(self, data):
        if self.status != "in": return
        self.data += data

    def get_answer(self):
        return self.data

def fetch(url):
    req = Request(url)
    req.add_header('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0')

    return makeRequest(req).read().decode('utf-8')

def ask_duck_duck_go(question, parse_func=None):
    url = 'https://duckduckgo.com/lite?' + encodeURL({"q": question})

    extractor = DuckDuckGoAnswerExtractor()
    extractor.feed(fetch(url))
    answer = extractor.get_answer()
    return parse_func(answer) if parse_func is not None else answer

def runProgram(program, inputData):
    proc = subprocess.run(args=[program], input=inputData, encoding="utf-8", capture_output=True)

    status = proc.returncode
    stdout = proc.stdout.split("\n")
    stderr = proc.stderr.split("\n")

    return status, stdout, stderr

def calculate(mpg, mile_to_km, gallon_to_litres):
    code = f"""
        scale=2;
        mpg={mpg};
        mile={mile_to_km};
        gallon={gallon_to_litres};
        100 / ((mpg * mile) / gallon)
    """

    _, out, _ = runProgram("/usr/bin/bc", code)
    return out[0]

def number_to_words(number, forms, decimals=False):
    if number >= 100:
        prefix = number_to_words(number // 100, forms, decimals) + " hundred "

        number %= 100
        if int(number) == 0:
            return prefix + forms[1]
    else:
        prefix = ""

    belowTwenty = [
        "zero",
        "one",
        "two",
        "three",
        "four",
        "five",
        "six",
        "seven",
        "eight",
        "nine",
        "ten",
        "eleven",
        "twelve",
        "thirteen",
        "fourteen",
        "fifteen",
        "sixteen",
        "seventeen",
        "eighteen",
        "nineteen",
    ]

    if decimals:
        sub = int((number * 100) % 100)
        if sub == 75:
            suffix = " and three quarters " + forms[2]
        elif sub == 66:
            suffix = " and two thirds " + forms[2]
        elif sub == 50:
            suffix = " and a half " + forms[2]
        elif sub == 33:
            suffix = " and a third " + forms[2]
        elif sub == 25:
            suffix = " and a quarter " + forms[2]
        elif sub == 10:
            suffix = " and one tenth " + forms[2]
        elif sub > 10:
            suffix = " and " + belowTwenty[sub // 10] + " tenths " + forms[2]
        else:
            suffix = ""
    else:
        suffix = ""
    number = int(number)

    if number < 20: 
        if suffix == "":
            suffix = " " + forms[0 if number < 2 else 1]
        return prefix + belowTwenty[number] + suffix

    tens = [
        "aughty",
        "onety",
        "twenty",
        "thirty",
        "fourty",
        "fifty",
        "sixty",
        "seventy",
        "eighty",
        "ninety",
    ]

    t = number // 10
    s = number % 10

    if suffix == "": suffix = " " + forms[1]

    if s == 0:
        return prefix + tens[t] + suffix
    else:
        return prefix + tens[t] + "-" + belowTwenty[s] + suffix

def get_user_locale():
    lang, encoding = locale.getdefaultlocale()

    for delim in ['.', '_']:
        dpos = lang.find(delim)
        if dpos > 0: lang = lang[:dpos]

    return lang

def google_translate(fromLang, toLang, text):
    params = encodeURL({
        "client": "gtx", 
        "sl": fromLang,
        "tl": toLang, 
        "dt": "t",
        "q": text,
    })
    url = "https://translate.googleapis.com/translate_a/single?" + params
    data = fetch(url)

    data = data.replace('\\n', '')
    data = data[data.find('[') : ]
    data = data[: data.rfind(']')+1]

    return parseJSON(data)[0][0][0]

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: mpg-to-lpk MPG\n  MPG should be an integer number describing the car's mileage per gallon.", file=sys.stderr)
        exit(1)

    mpg = int(sys.argv[1])

    mile_to_km = ask_duck_duck_go('1 mile to kilometres', lambda x: re.search("([0-9.]*)\s*kilometers", x).group(1) )
    gallon_to_litres = ask_duck_duck_go('1 us gallon to litres', lambda x: re.search("([0-9.]*)\s*liters", x).group(1) )

    litres_per_100km = calculate(mpg, mile_to_km, gallon_to_litres)
    text = "{} to a gallon is {} per one hundred kilometres".format(
        number_to_words(mpg, ["mile", "miles", "mile"]), 
        number_to_words(float(litres_per_100km), ["litre", "litres", "of a litre"], True)
    )

    text = text.split(" ")
    text = string.capwords(text[0]) + " " + " ".join(text[1:]) + "."

    # print(text)
    lang = get_user_locale()
    if lang != "en":
        print(google_translate("en", get_user_locale(), text))
    else:
        print(text)
3

Zgłoszenie numer 4

TUTAJ WPRAWDZIE MAMY NAMIARY NA GH UCZESTNIKA, ALE PONIEWAŻ W ŻADEN SPOSÓB NIE WIĄŻE SIĘ ON Z NICKIEM Z FORUM, UZNALIŚMY, ŻE ZASADA ODNOŚNIE ANONIMOWOŚCI ZGŁOSZEŃ NIE ZOSTAŁA ZŁAMANA

W nawiazaniu do konkursu na przekombinowany konwerter przesylam moje rozwiazanie:

https://gitlab.com/mkowsiak/converter-docker

rozwiazanie oparte o Dockera, oraz kod w Javie wywyolujacy JNI. Z klasycznymi #define, wzorcami projektowymi, makefilem - czyli wszystko to, co czyni kod piekniejszym :)

Tutaj kod w Javie (wykorzystywany przez Dockera): https://gitlab.com/mkowsiak/converter

Calosc - plik Dockerfile, run.sh, wszystkie zrodla (C/Java), pliki Makefile - nie przekracza 300 linii kodu. Calosc ma 267 linii.
2

Zgłoszenie numer 5

Język C++ 17 ( kompilacja na gcc 9.2.0 )

https://wandbox.org/permlink/OcokNBQPsqaHaTY4

#include <iomanip>
#include <math.h>
#include <setjmp.h>
#include <iostream>
#include <thread>
#include <vector>
#include <map>
#include <numeric>
#include <limits>
#include <optional>
#include <regex>
#include <mutex>

#define c(t) case t: cout << setprecision(10) << (*(new converter<static_cast<TYPE>(t)>()),quantity) << "\n"; break;
#define flush(t) converter<static_cast<TYPE>(t)>::clean();
#define HighwayUS6 "OpronQrupMMuqTuqursQpupVVt"
#define e(x) epsilon<x>()
#define url goto

using namespace std;

jmp_buf env;
double quantity {0};

enum class TYPE { STEAM=3 , ELECTRIC=6 , OIL=1 , PETROL=2 , ATOMIC=5 , ECO=4 };

namespace calculation
{

template< typename T >
using operation = T(*)(T,T);

template< typename T = double >
constexpr operation<T> multiplication = []( T a , T b ){ return a*b; };

template< typename T = double , operation<T> _base = multiplication<T> >
struct MAKE
{
   operation<T> base {_base};
   double operator()( T a , T b ){ return base(a,b); }
};

}

struct interface
{
    virtual double translate( double ) = 0;
    virtual ~interface() = default;
    [[deprecated]] virtual int inline long volatile unsigned const long& oblivion() const&& noexcept final = delete;
};

template< TYPE t >
class converter final : public virtual interface , public calculation::MAKE<double,calculation::multiplication<double>>
{
public:

   converter()
   {
       converter<t>::garbageCollector.push_back(this);
       double generator {0};
       cout << "Wait a little. Initializing fast access table ... \n";
       for( int index=0 ; index<0xF4240 ; ++index )
       {
           fastAccessSmartMap[generator] = pow(generator,-1);
           generator += 0.01;
       }
   }

   double operator,( double it )
   {
       if( check<>(securitycheck(it),translate(it)) ) return translate(it);
       else longjmp(env,1);
   }

   static void clean()
   {
       for( auto& element : garbageCollector )
       {
           delete element;
       }
       garbageCollector.clear();
   }

protected:

   double translate( double it ) override
   {
       if( type == TYPE::ECO ) throw runtime_error( "No need such stuff... Use your legs." );
       if( type == TYPE::ELECTRIC ) throw runtime_error( "No need any conversions." );
       if( type == TYPE::STEAM ) throw runtime_error( "Do you like steam engines?" );
       if( type == TYPE::ATOMIC ) longjmp(env,2);

       for( int i=0 ; i<poolsize ; ++i )
       {
           pool.emplace_back( &converter::translateIt , this , i );
       }

       for( auto& poolthread : pool ) poolthread.join();
       pool.clear();

       if( fastAccessSmartMap.find(it) == fastAccessSmartMap.end() ) fastAccessSmartMap[it] = pow(it,-1);
       return (*this)(number,fastAccessSmartMap[it]/++translations);
   }

   static vector<interface*> garbageCollector;

private:

   ~converter(){};

   string name {HighwayUS6};
   double number {0};
   TYPE type {t};
   map<double,double> fastAccessSmartMap;
   mutex n_mutex;
   vector<thread> pool;
   static constexpr int poolsize {26};
   int translations {0};

   void translateIt( int that )
   {
        const lock_guard<mutex> lock(n_mutex);
        number += (*this)(static_cast<double>(abs((static_cast<int>(tolower(name[that]))+add(e(5),e(7),e(11))-!(*this)))),pow(10,2-that));
   }

   template< typename ...Args >
   inline int add( Args&&... args )
   {
       return ( ... + args );
   }

   template< int order , int calls = 0 >
   inline constexpr int epsilon()
   {
       if constexpr( calls < 5 ) return ((order-1)*epsilon<order+1,calls+1>()-(order+1)*epsilon<order-1,calls+1>())%256;
       else return order;
   }

   template< int fix=28 >
   constexpr int operator!()
   {
       for( struct { int x {2}; int y {2}; int order {fix}; void loop(){ if( x==y && y!=2 ) --order; } } loop ; loop.x<=fix*10 ; ++loop.x )
       {
           for( loop.y = 2 ; loop.y<=loop.x-1 ; ++loop.y ){ if( loop.x%loop.y == 0 ) break; }
           loop.loop();
           if( loop.order == 0 ) return loop.y;
       }
       return -1;
   }

   template< int depth=-5 >
   constexpr bool check( const double &v1 , const double &v2 )
   {
       return abs(v1-v2)<pow(10,depth) || ( type!=TYPE::PETROL && type!=TYPE::OIL ) ? true : false;
   }

   constexpr double securitycheck( double value )
   {
       return 235.21458300847848564383997*pow(value,-1);
   }

};

template< TYPE t >
vector<interface*> converter<t>::garbageCollector;

template< typename T >
using IsCorrectInput = enable_if_t< is_integral_v<T> || is_floating_point_v<T> || is_same<string,T>::value , bool >;

template< typename T , IsCorrectInput<T> = true >
optional<T> get( T type = {} )
{
    string input;
    getline( cin, input );

    if constexpr ( is_integral_v<T> )
    {
        if( regex_match( input , regex("^-?\\d+$")) ) return static_cast<T>(stoi(input));
    }
    if constexpr ( is_floating_point_v<T> )
    {
        if( regex_match( input , regex("^-?\\d*\\.{0,1}\\d+$")) ) return static_cast<T>(stof(input));
    }
    if constexpr ( is_same<string,T>::value )
    {
        return static_cast<T>(input);
    }

    return nullopt;
}

template< typename T , IsCorrectInput<T> = true >
T input( string_view errorMsg = "Invalid input type: try again = " , T type = {} )
{
   while(true)
   {
       if( auto number = get<T>() ) return *number ;
       else cout << errorMsg;
   }
}

int main()
{
    https://4programmers.net

    if( setjmp(env) == 2 ) cout << "Atomic error: Do not play with atomic energy.\n";

    cout << "\nChoose engine type: \n";
    cout << "[1] OIL \n";
    cout << "[2] PETROL \n";
    cout << "[3] STEAM \n";
    cout << "[4] ECO \n";
    cout << "[5] ATOMIC \n";
    cout << "[6] ELECTRIC \n";
    auto autoengine = input<int>();

    if( autoengine <3 )
    {
        cout << "Input your value in Miles/Galon to convert to Liters/100km: \n";
        cout << "or input your value in Liters/100km to convert to Miles/Galon: \n";
        quantity = input<double>();
    }

    try
    {
        switch( autoengine )
        {
            c(1)c(2)c(3)c(4)c(5)c(6)
            default: cout << "Invalid menu number!\n"; longjmp(env,3);
        }
    }
    catch( const exception& error )
    {
        cout << "Message : " << error.what() << endl;
        url https;//4programmers.net
    }

    flush(1)flush(2)flush(3)flush(4)flush(5)flush(6)

    return 0;
}
4

Zgłoszenie nr 6

JavaScript

How to run

Inside a folder with the script:

node convert <value> [precision]

Examples:

// With default precision = 3 (result: 117.632)
node convert 2

// With custom precision (result: 78.92)
node convert 3 2

Note: Do not use big numbers and big precisions, script is not optimized at all and is very slow ;)
/**
 * @typedef {Object} BufferSize
 * @property {number} whole
 * @property {number} fraction
 */

/**
 * Splits number into single-digit chunks,
 * with whole and fraction parts separated.
 *
 * @param {number | string} num Any number, can be in a string form
 * @returns {[number[], number[]]} A tuple with whole and fraction digits
 *
 * @example
 *
 * const input = 123.456
 * const [whole, fraction] = splitToDigits(input)
 *
 * console.log(whole) // -> [1, 2, 3]
 * console.log(fraction) // -> [4, 5, 6]
 */
const splitToDigits = (num) => {
  const [wholePart, fractionPart] = (typeof num === "string"
    ? num
    : String(num)
  ).split(".")

  return [
    wholePart ? wholePart.split("").map((x) => Number(x)) : [],
    fractionPart ? fractionPart.split("").map((x) => Number(x)) : [],
  ]
}

/**
 * Calculates required buffer size to fit all operations.
 *
 * @param {number} margin
 * @param  {Array<[number[], number[]]>} numbersAsDigits
 * @returns
 */
const calculateRequiredBufferSize = (margin, precision, numbersAsDigits) => {
  const lengths = [precision, ...numbersAsDigits.map((x) => x[0].length)]
  return Math.max(...lengths) + margin
}

/**
 * Creates a typed array that represents a number.
 *
 * @param {[number[], number[]]} digits A tuple with whole and fraction digits
 * @param {BufferSize} bufferSize An object containing buffer size setup
 * @returns {Int8Array} A typed array of raw bytes representing a number
 *
 * @example
 *
 * const digits = [[1, 2, 3], [4, 5, 6]]
 * const bufferSize = { whole: 5, fraction: 7 }
 * const digitsAsTypedArray = toTypedArray(digits, bufferSize)
 *
 * console.log(digitsAsTypedArray)
 *
 * ---
 * Result:
 *
 * Int8Array [ 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0 ]
 *
 *           |     whole    |      fraction       |
 */
const toTypedArray = (digits, bufferSize) => {
  const [whole, fraction] = digits
  const num = new Int8Array(
    new SharedArrayBuffer(bufferSize.whole + bufferSize.fraction),
  )

  for (let i = 0; i < whole.length; i++) {
    num[bufferSize.whole - (whole.length - i)] = whole[i]
  }

  for (let i = 0; i < fraction.length; i++) {
    num[bufferSize.whole + i] = fraction[i]
  }

  return num
}

/**
 * Adds two typed arrays.
 *
 * @param {Int8Array} a summand
 * @param {Int8Array} b summand
 * @returns {Int8Array} sum
 */
const add = (a, b) => {
  let rest = 0
  const sum = new Int8Array(a.length)

  for (let i = a.length - 1; i >= 0; i--) {
    const subSum = a[i] + b[i] + rest
    rest = subSum < 10 ? 0 : 1
    sum[i] = subSum < 10 ? subSum : subSum - 10
  }

  return sum
}

/**
 * Subtracts two typed arrays.
 *
 * @param {Int8Array} a subtrahend
 * @param {Int8Array} b minuend
 * @returns {Int8Array} difference
 */
const subtract = (a, b) => {
  let rest = 0
  const diff = new Int8Array(a.length)

  for (let i = a.length - 1; i >= 0; i--) {
    const x = a[i] + rest
    const isAGreaterOrEqualB = x >= b[i]
    const subDiff = isAGreaterOrEqualB ? x - b[i] : x + 10 - b[i]
    rest = isAGreaterOrEqualB ? 0 : -1
    diff[i] = subDiff
  }

  return diff
}

/**
 * Multiplies two typed arrays.
 *
 * @param {Int8Array} a factor
 * @param {Int8Array} b factor
 * @returns {Int8Array} product
 */
const multiply = (a, b, fractionSize) => {
  let rest = 0
  const temp = []

  for (let i = b.length - 1; i >= 0; i--) {
    let shift = fractionSize--
    const partial = new Int8Array(a.length)
    for (let j = a.length - 1; j >= 0; j--) {
      const subProduct = b[i] * a[j] + rest
      rest = (subProduct / 10) >>> 0
      partial[j + shift] = subProduct % 10
    }
    temp.push(partial)
  }

  return temp.reduce(add, new Int8Array(a.length))
}

/**
 * Checks if one typed array is greater than the other one.
 *
 * @param {Int8Array} a
 * @param {Int8Array} b
 */
const isGreater = (a, b) => {
  for (let i = 0; i < a.length; i++) {
    if (a[i] > b[i]) {
      return true
    }
  }
  return false
}

/**
 * Checks if two typed arrays are equal.
 *
 * @param {Int8Array} a
 * @param {Int8Array} b
 */
const isEqual = (a, b) => a.join() === b.join()

/**
 * Divides two typed arrays.
 *
 * @param {Int8Array} a dividend
 * @param {Int8Array} b divisor
 * @returns {Int8Array} quotient
 */
const divide = (a, b, fractionSize) => {
  if (isEqual(a, b)) {
    const result = new Int8Array(a.length)
    result[result.length - fractionSize - 1] = 1
    return result
  }

  let result = new Int8Array(a.length)
  let temp = new Int8Array(a.length)
  const one = new Int8Array(a.length).fill(1, -1)
  const max = new Int8Array(a.length).fill(9)

  while (!isEqual(temp, max)) {
    const product = multiply(temp, b, fractionSize)

    if (isEqual(product, a)) {
      return temp
    }

    if (!isGreater(a, product)) {
      break
    }

    result = temp.slice(0)
    temp = add(temp, one)
  }

  return result
}

/**
 * Converts typed array of digits to a string that represent final result
 * - a big number without leading and ending zeros.
 *
 * @param {Int8Array} typedArr A typed array of raw bytes representing a number
 * @param {BufferSize} bufferSize An object containing buffer size setup
 * @returns {string} Formatted output
 */
const formatOutput = (typedArr, bufferSize) => {
  const arr = [...typedArr]
  const whole = arr
    .slice(0, bufferSize.whole)
    .join("")
    .replace(/^0+/, "")
    .replace(/^$/, "0")
  const fraction = arr
    .slice(bufferSize.whole)
    .join("")
    .replace(/0+$/, "")

  return fraction ? `${whole}.${fraction}` : whole
}

/* SOLUTION */

const defaultPrecision = 3
const input = process.argv[2]
const precision =
  process.argv[3] === undefined ? defaultPrecision : Number(process.argv[3])
const litersPerGallon = 3.785411784 * 100
const kilometersPerMile = 1.609344

const litersPerGallonAsDigits = splitToDigits(litersPerGallon)
const kilometersPerMileAsDigits = splitToDigits(kilometersPerMile)

/** Buffer margin to fit all calculations */
const margin = 3

/**
 * Converts mpg to l/100km and vice versa.
 *
 * @param {string | number} input Arbitrary precision number
 * @param {number} precision Result precision
 * @returns {string} conversion result
 */
const convert = (input, precision = 3) => {
  const inputAsDigits = splitToDigits(input)

  const bufferSize = {
    whole: calculateRequiredBufferSize(margin, precision, [
      litersPerGallonAsDigits,
      kilometersPerMileAsDigits,
      inputAsDigits,
    ]),
    fraction: precision,
  }

  const conversionFactor = divide(
    toTypedArray(litersPerGallonAsDigits, bufferSize),
    toTypedArray(kilometersPerMileAsDigits, bufferSize),
    bufferSize.fraction,
  )

  const result = divide(
    conversionFactor,
    toTypedArray(inputAsDigits, bufferSize),
    bufferSize.fraction,
  )

  return formatOutput(result, bufferSize)
}

console.log(convert(input, precision))

module.exports = {
  splitToDigits,
  calculateRequiredBufferSize,
  toTypedArray,
  add,
  subtract,
  multiply,
  divide,
  formatOutput,
  convert,
}
2

Zgłoszenie nr 7

https://wandbox.org/permlink/rhBpXETj2zBtKlAM

https://pastebin.com/EjwGt2h4

Running the example program
Compilation, e.g.: clang++-7 -std=c++17 main.cpp

Running: ./a.out "7 l / 100 km" (mind the double quotes and spaces between tokens).

Output:

-----------[EXECUTING SCRIPT SOURCE]-----------
constant 0.00062137 mi                          <-> 1 m 
constant 1 ft                                   <-> 0.3048 m 
constant 38.41 some_weird_made_up_unit          <-> 53.456437009 ft 
constant 2357.378595002 some_weird_made_up_unit <-> 1 km 
constant 1 l                                    <-> 202.88413621 teaspoon 
constant 0.3333333333 tablespoon                <-> 1 teaspoon 
constant 0.49651 tablespoon                     <-> 1.98604 dr 
constant 411.4128 gal                           <-> 421286.7072 dr 
value input 7 l / 100 km 
print input to X mi / 1 gal 

-----------[OUTPUT]-----------
33.602 mi / 1 gal
#include <algorithm>
#include <iostream>
#include <iterator>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
constexpr auto k_desired_value_x_mark = std::numeric_limits<double>::max();
struct value {
  struct { double value{}; std::string unit; } num, den;

  value &operator*=(const value &rhs) {
    num.value *= rhs.num.value; den.value *= rhs.den.value;
    if (num.unit == rhs.den.unit) num.unit = rhs.num.unit;
    else den.unit = rhs.den.unit;
    return *this;
  }

  value flipped() const { return value{den, num}; }
};

std::ostream &operator<<(std::ostream &out, const value &val) { return out << val.num.value << ' ' << val.num.unit << " / " << val.den.value << ' ' << val.den.unit; }

template <typename Container, typename Value>
bool contains(Container &&c, Value &&v) { return std::find(c.cbegin(), c.cend(), v) != c.cend(); }

class conversion_chain_finder {
  struct unit_and_constant{ std::string unit; value val; };

public:
  void register_constant(const value &val) {
    m_unit_constants[val.den.unit].emplace_back(unit_and_constant{val.num.unit, val});
    m_unit_constants[val.num.unit].emplace_back(unit_and_constant{val.den.unit, val});

    for (auto &category : m_categories)
      if (contains(category, val.den.unit) || contains(category, val.num.unit)) {
        category.insert(val.den.unit); category.insert(val.num.unit);
      }

    m_categories.emplace_back(std::unordered_set<std::string>{val.den.unit, val.num.unit});
  }

  std::string same_category_unit(const std::string &from, const value &to) {
    for (const auto &category : m_categories)
      if (contains(category, from)) {
        if (contains(category, to.den.unit)) return to.den.unit;
        if (contains(category, to.num.unit)) return to.num.unit;
      }
    throw std::runtime_error{"No known conversion."};
  }

  std::vector<value> find(const value &from, const value &desired_layout) {
    const auto den_current = unit_and_constant{from.den.unit, value{{0., "x"}, {1., from.den.unit}}};
    const auto num_current = unit_and_constant{from.num.unit, value{{1., from.num.unit}, {0., "x"}}};

    auto run_find = [&](const auto current) {
      std::unordered_set<std::string> visited;
      std::string desired = same_category_unit(current.unit, desired_layout);
      auto result = find_(current, desired, visited);
      if (result.empty())
        throw std::runtime_error{"No known conversion"};
      result.erase(result.begin());
      return result;
    };

    auto result = run_find(den_current);
    auto result_num = run_find(num_current);

    result.insert(result.end(), result_num.begin(), result_num.end());
    return result;
  }

private:
  std::vector<value> find_(const unit_and_constant &current, const std::string &to, std::unordered_set<std::string> &visited) {
    if (current.unit == to)
      return {current.val};
    if (visited.find(current.unit) != visited.cend())
      return {};

    visited.insert(current.unit);

    for (auto next : m_unit_constants[current.unit]) {
      if (current.unit == current.val.den.unit) {
        if (current.unit == next.val.den.unit) next.val = next.val.flipped();
      } else {
        if (current.unit == next.val.num.unit) next.val = next.val.flipped();
      }

      auto result = find_(next, to, visited);
      if (!result.empty()) {
        result.insert(result.begin(), current.val);
        return result;
      }
    }

    visited.erase(current.unit);
    return {};
  }

private:
  std::unordered_map<std::string, std::vector<unit_and_constant>> m_unit_constants;
  std::vector<std::unordered_set<std::string>> m_categories;
};

struct engine {
  void register_constant(value val) {
    m_constants.emplace_back(val); m_chain_finder.register_constant(val);
  }

  value get_value(const std::string &name) const { return m_values.at(name); }
  void register_value(const std::string &name, value val) { m_values[name] = std::move(val); }

  value convert_value(const value &from, const value &desired_layout) {
    auto result = from;

    const auto conversion_chain = m_chain_finder.find(from, desired_layout);
    for (auto &constant : conversion_chain) result *= constant;
    if (result.den.unit != desired_layout.den.unit) result = result.flipped();

    auto cleanup_factor = 1.;
    if (desired_layout.num.value == k_desired_value_x_mark)
      cleanup_factor = 1. / (result.den.value / desired_layout.den.value);
    else if (desired_layout.den.value == k_desired_value_x_mark)
      cleanup_factor = result.num.value / desired_layout.num.value;

    result.num.value *= cleanup_factor; result.den.value *= cleanup_factor;
    return result;
  }

private:
  std::vector<value> m_constants;
  std::unordered_map<std::string, value> m_values;
  conversion_chain_finder m_chain_finder;
};

class constant_node; class value_declaration_node; class to_operator_node;
class identifier_node; class value_node; class print_node; class value_expression_node;

class execution_visitor {
public:
  explicit execution_visitor(engine &en) : m_engine{en} {}
  void visit(const constant_node &);
  void visit(const value_declaration_node &);
  void visit(const to_operator_node &);
  void visit(const identifier_node &);
  void visit(const value_node &);
  void visit(const print_node &);

private:
  value evaluate_expression(const value_expression_node &node);

private:
  engine &m_engine;
  value m_last_result;
};

struct ast_node {
  virtual ~ast_node() = default;
  virtual void visit(execution_visitor &visitor) const = 0;
};

struct value_expression_node : public ast_node {};

struct value_node : public value_expression_node {
  explicit value_node(value v) : val{std::move(v)} {}
  void visit(execution_visitor &visitor) const override { visitor.visit(*this); };
  value val;
};

struct to_operator_node : public value_expression_node {
  explicit to_operator_node(std::unique_ptr<value_expression_node> lhs, std::unique_ptr<value_expression_node> rhs)
      : lhs{std::move(lhs)}, rhs{std::move(rhs)} {}
  void visit(execution_visitor &visitor) const override { visitor.visit(*this); };
  std::unique_ptr<value_expression_node> lhs;
  std::unique_ptr<value_expression_node> rhs;
};

struct constant_node : public ast_node {
  explicit constant_node(std::unique_ptr<value_node> v) : val{std::move(v)} {}
  void visit(execution_visitor &visitor) const override { visitor.visit(*this); };
  std::unique_ptr<value_node> val;
};

struct value_declaration_node : public ast_node {
  explicit value_declaration_node(std::string name,
                                  std::unique_ptr<value_expression_node> v)
      : name{std::move(name)}, val{std::move(v)} {}
  void visit(execution_visitor &visitor) const override { visitor.visit(*this); };
  std::string name;
  std::unique_ptr<value_expression_node> val;
};

struct identifier_node : public value_expression_node {
  explicit identifier_node(std::string name) : name{std::move(name)} {}
  void visit(execution_visitor &visitor) const override { visitor.visit(*this); };
  std::string name;
};

struct print_node : public ast_node {
  explicit print_node(std::unique_ptr<value_expression_node> v) : val{std::move(v)} {}
  void visit(execution_visitor &visitor) const override { visitor.visit(*this); };
  std::unique_ptr<value_expression_node> val;
};

void execution_visitor::visit(const constant_node &node) { m_engine.register_constant(node.val->val); }
void execution_visitor::visit(const identifier_node &node) { m_last_result = m_engine.get_value(node.name); }
void execution_visitor::visit(const value_node &node) { m_last_result = node.val; }
void execution_visitor::visit(const print_node &node) { std::cout << evaluate_expression(*node.val) << '\n'; }
void execution_visitor::visit(const value_declaration_node &node) { m_engine.register_value(node.name, evaluate_expression(*node.val)); }
void execution_visitor::visit(const to_operator_node &node) {
  auto lhs = evaluate_expression(*node.lhs); auto rhs = evaluate_expression(*node.rhs);
  m_last_result = m_engine.convert_value(lhs, rhs);
}
value execution_visitor::evaluate_expression(const value_expression_node &node) {
  auto cloned = *this; node.visit(cloned); return cloned.m_last_result;
}

class parser {
public:
  parser(const std::vector<std::string> &tokens) : m_current{std::cbegin(tokens)}, m_end{std::cend(tokens)} {}

  std::vector<std::unique_ptr<ast_node>> parse() {
    std::vector<std::unique_ptr<ast_node>> nodes;
    while (m_current != m_end) {
      std::unique_ptr<ast_node> node;
      if (*m_current == "constant") node = parse_constant();
      else if (*m_current == "value") node = parse_value_declaration();
      else if (*m_current == "print") node = parse_print();
      else throw std::runtime_error{ "Unknown statement." };
      nodes.emplace_back(std::move(node));
    }
    return nodes;
  }

private:
  std::unique_ptr<print_node> parse_print() {
    ++m_current; return std::make_unique<print_node>(parse_value_expression());
  }

  std::unique_ptr<value_expression_node> parse_value_expression() {
    std::unique_ptr<value_expression_node> val;
    if (current_is_number() || current_is("X")) val = parse_value();
    else val = std::make_unique<identifier_node>(*m_current++);
    if (!current_is("to")) return val;
    ++m_current;
    return std::make_unique<to_operator_node>(std::move(val), parse_value_expression());
  }

  std::unique_ptr<value_node> parse_value() {
    auto get_val = [&] {
      if (!current_is_number()) { ++m_current; return k_desired_value_x_mark; }
      else return std::stod(*m_current++);
    };

    value val;
    val.num.value = get_val(); val.num.unit = *m_current++;
    ++m_current;
    val.den.value = get_val(); val.den.unit = *m_current++;
    return std::make_unique<value_node>(val);
  }

  std::unique_ptr<constant_node> parse_constant() { ++m_current; return std::make_unique<constant_node>(parse_value()); }

  std::unique_ptr<value_declaration_node> parse_value_declaration() {
    ++m_current; auto name = *m_current++;
    return std::make_unique<value_declaration_node>(name, parse_value_expression());
  }

  bool current_is_number() const { return std::isdigit(m_current->at(0)); }
  bool current_is(const std::string &v) const { return m_current != m_end && *m_current == v; }

private:
  std::vector<std::string>::const_iterator m_current, m_end;
};

std::string gather_script_source(const std::string& argument) {
  const auto source = "constant 0.00062137 mi                          <-> 1 m \n"
                      "constant 1 ft                                   <-> 0.3048 m \n"
                      "constant 38.41 some_weird_made_up_unit          <-> 53.456437009 ft \n"
                      "constant 2357.378595002 some_weird_made_up_unit <-> 1 km \n"
                      "constant 1 l                                    <-> 202.88413621 teaspoon \n"
                      "constant 0.3333333333 tablespoon                <-> 1 teaspoon \n"
                      "constant 0.49651 tablespoon                     <-> 1.98604 dr \n"
                      "constant 411.4128 gal                           <-> 421286.7072 dr \n"
                      "value input " + argument + " \n";
  return source + std::string{ "print input to " } + std::string{(contains(argument, 'k')) ? "X mi / 1 gal \n" : "X l / 100 km \n"};
}

int main(int, char* argv[]) {
  std::cout << "-----------[EXECUTING SCRIPT SOURCE]-----------\n" << gather_script_source(argv[1]) << "\n\n-----------[OUTPUT]-----------\n";
  std::istringstream iss(gather_script_source(argv[1]));
  std::vector<std::string> tokens{(std::istream_iterator<std::string>(iss)), std::istream_iterator<std::string>()};
  const auto nodes = parser{tokens}.parse();
  engine en;
  execution_visitor execution{en};
  for (const auto &node : nodes)
    node->visit(execution);
}
0

Głosowanie zostało zakończone, Zwycięzcami zostają autorzy rozwiązań numer 1 (najwięcej głosów zdobytych w ankiecie) oraz numeru 6 - najwięcej łapek zebranych przy poście.

Gratulujemy @Spearhead oraz @Maciej Cąderek, a wszystkim pozostałym dziękujemy za udział w konkursie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0