Definir aritmética teórica (+ e *) [fechada]

10

Aritmética Teórica do Conjunto

Premissa

Já existem alguns desafios que envolvem a multiplicação sem o operador de multiplicação ( aqui e aqui ) e esse desafio está na mesma linha (mais semelhante ao segundo link).

Esse desafio, diferentemente dos anteriores, utilizará uma definição teórica definida dos números naturais ( N ):

insira a descrição da imagem aqui

e

insira a descrição da imagem aqui

por exemplo,

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

e assim por diante.

O desafio

Nosso objetivo é usar operações definidas (veja abaixo), para adicionar e multiplicar números naturais. Para esse fim, todas as entradas estarão no mesmo 'idioma definido', cujo intérprete está abaixo . Isso fornecerá consistência e pontuação mais fácil.

Este intérprete permite manipular números naturais como conjuntos. Sua tarefa será escrever dois corpos de programa (veja abaixo), um dos quais adiciona números naturais e o outro os multiplica.

Notas preliminares sobre conjuntos

Os conjuntos seguem a estrutura matemática usual. Aqui estão alguns pontos importantes:

  • Os conjuntos não são pedidos.
  • Nenhum conjunto contém a si próprio
  • Os elementos estão em um conjunto ou não, isso é booleano. Portanto, os elementos do conjunto não podem ter multiplicidades (ou seja, um elemento não pode estar em um conjunto várias vezes).

Intérprete e detalhes

Um 'programa' para esse desafio é escrito em 'linguagem definida' e consiste em duas partes: um cabeçalho e um corpo.

Cabeçalho

O cabeçalho é muito simples. Diz ao intérprete qual programa você está resolvendo. O cabeçalho é a linha de abertura do programa. Começa com o caractere +ou *, seguido por dois números inteiros, delimitados por espaço. Por exemplo:

+ 3 5

ou

* 19 2

são cabeçalhos válidos. O primeiro indica que você está tentando resolver 3+5, o que significa que sua resposta deve ser 8. O segundo é semelhante, exceto com multiplicação.

Corpo

O corpo é onde estão suas instruções reais para o intérprete. Isso é o que realmente constitui seu programa de "adição" ou "multiplicação". Sua resposta terá dois órgãos do programa, um para cada tarefa. Você mudará os cabeçalhos para realmente executar os casos de teste.

Sintaxe e Instruções

As instruções consistem em um comando seguido por zero ou mais parâmetros. Para os fins das demonstrações a seguir, qualquer caractere de alfabeto é o nome de uma variável. Lembre-se de que todas as variáveis ​​são conjuntos. labelé o nome de um rótulo (rótulos são palavras seguidas de ponto e vírgula (ou seja main_loop:)), inté um número inteiro. A seguir estão as instruções válidas:

Controle de fluxo:
  1. jump labelpule incondicionalmente para o rótulo. Um rótulo é uma 'palavra' seguida de ponto e vírgula: por exemplo, main_loop:é um rótulo.
  2. je A label pule para o rótulo se A estiver vazio
  3. jne A label pule para o rótulo se A não estiver vazio
  4. jic A B label pule para o rótulo se A contiver B
  5. jidc A B label pule para o rótulo se A não contiver B
Atribuição variável
  1. assign A Bou assign A int insira a descrição da imagem aquiou
    onde set(int)é a representação definida deint
Definir operações
  1. union A B C insira a descrição da imagem aqui
  2. intersect A B C
  3. difference A B C insira a descrição da imagem aqui
  4. add A B insira a descrição da imagem aqui
  5. remove A B insira a descrição da imagem aqui
Depuração
  1. print A imprime o valor verdadeiro de A, onde {} é o conjunto vazio
  2. printi variable imprime a representação inteira de A, se existir, caso contrário, gera erro.
Comentários
  1. ; O ponto e vírgula indica que o restante da linha é um comentário e será ignorado pelo intérprete

Mais informações

No início do programa, existem três variáveis ​​pré-existentes. Eles são set1,set2 e ANSWER. set1pega o valor do primeiro parâmetro do cabeçalho. set2pega o valor do segundo. ANSWERé inicialmente o conjunto vazio. Após a conclusão do programa, o intérprete verifica se ANSWERé a representação inteira da resposta para o problema aritmético definido no cabeçalho. Se estiver, indica isso com uma mensagem para stdout.

O intérprete também exibe o número de operações usadas. Toda instrução é uma operação. Iniciar uma etiqueta também custa uma operação (as etiquetas podem ser iniciadas apenas uma vez).

Você pode ter no máximo 20 variáveis ​​(incluindo as 3 variáveis ​​predefinidas) e 20 rótulos.

Código do intérprete

NOTAS IMPORTANTES SOBRE ESTE INTERPRETADOR

As coisas são muito lentas ao usar números grandes (> 30) neste intérprete. Vou descrever as razões para isso.

  • A estrutura dos conjuntos é tal que, ao aumentar um número natural, você efetivamente dobra o tamanho da estrutura do conjunto. O n º número natural tem 2 ^ n conjunto vazio dentro dela (por isto quero dizer que se você olhar n como uma árvore, existem n conjunto vazio. Observe apenas conjunto vazio pode ser folhas.) Isso significa que lidar com 30 é significativamente mais caro do que lidar com 20 ou 10 (você está vendo 2 ^ 10 vs 2 ^ 20 vs 2 ^ 30).
  • As verificações de igualdade são recursivas. Como os sets são supostamente desordenados, essa parecia a maneira natural de lidar com isso.
  • Existem dois vazamentos de memória que não consegui descobrir como corrigir. Eu sou ruim em C / C ++, desculpe. Como lidamos apenas com pequenos números e a memória alocada é liberada no final do programa, isso não deve ser realmente um problema. (Antes que alguém diga alguma coisa, sim, eu sei std::vector; estava fazendo isso como um exercício de aprendizado. Se você souber como corrigi-lo, avise-me e farei as edições; caso contrário, como funciona, deixarei como é.)

Observe também o caminho de inclusão set.hno interpreter.cpparquivo. Sem mais delongas, o código fonte (C ++):

set.h

using namespace std;

//MEMORY LEAK IN THE ADD_SELF METHOD
class set {

    private:
        long m_size;
        set* m_elements;
        bool m_initialized;
        long m_value;

    public:
        set() {

            m_size =0;
            m_initialized = false;
            m_value=0;
        }

        ~set() {
            if(m_initialized) {
                //delete[] m_elements;
            }
        }

        void init() {
            if(!m_initialized) {
                m_elements = new set[0];

                m_initialized = true;
            }
        }

        void uninit() {
            if(m_initialized) {
                //delete[] m_elements;
            }
        }

        long size() {
            return m_size;
        }

        set* elements() {
            return m_elements;
        }

        bool is_empty() {
            if(m_size ==0) {return true;}
            else {return false;}
        }

        bool is_eq(set otherset) {
            if( (*this).size() != otherset.size() ) {
                return false;
            }
            else if ( (*this).size()==0 && otherset.size()==0 ) { 
                return true;
            }
            else {
                for(int i=0;i<m_size;i++) {
                    bool matched = false;
                    for(int j=0;j<otherset.size();j++) {

                        matched = (*(m_elements+i)).is_eq( *(otherset.elements()+j) );
                        if( matched) {
                            break;
                        }
                    }
                    if(!matched) {
                        return false;
                    }
                }
                return true;
            } 
        }

        bool contains(set set1) {
            for(int i=0;i<m_size;i++) {
                if( (*(m_elements+i)).is_eq(set1) ) {
                    return true;
                }
            }
            return false;
        }

        void add(set element) {
            (*this).init();

            bool alreadythere = false;
            for(int i=0;i<m_size;i++) {
                if( (*(m_elements+i)).is_eq(element) ) { 
                    alreadythere=true;
                }
            }
            if(!alreadythere) {
                set *temp = new set[m_size+1];
                for(int i=0; i<m_size; i++) {
                    *(temp+i)= *(m_elements+i);
                }
                *(temp+m_size)=element;

                m_size++;
                delete[] m_elements;
                m_elements = new set[m_size];

                for(int i=0;i<m_size;i++) {
                    *(m_elements+i) = *(temp+i);
                }
                delete[] temp;
            }
        }

        void add_self() {

            set temp_set;
            for(int i=0;i<m_size;i++) {
                temp_set.add( *(m_elements+i) );
            }
            (*this).add(temp_set);
            temp_set.uninit();
        }

        void remove(set set1) {
            (*this).init();
            for(int i=0;i<m_size;i++) {
                if(  (*(m_elements+i)).is_eq(set1) ) {

                    set* temp = new set[m_size-1];
                    for(int j=0;j<m_size;j++) {

                        if(j<i) {
                            *(temp+j)=*(m_elements+j);
                        }
                        else if(j>i) {
                            *(temp+j-1)=*(m_elements+j);
                        }
                    }
                    delete[] m_elements;
                    m_size--;
                    m_elements = new set[m_size];
                    for(int j=0;j<m_size;j++) {
                        *(m_elements+j)= *(temp+j);
                    }
                    delete[] temp;
                    break;
                }
            }
        }

        void join(set set1) {
            for(int i=0;i<set1.size();i++) {
                (*this).add( *(set1.elements()+i) );
            }
        }

        void diff(set set1) {
            for(int i=0;i<set1.size();i++) {
                (*this).remove( *(set1.elements()+i) );
            }
        }

        void intersect(set set1) {
             for(int i=0;i<m_size;i++) {

                bool keep = false;
                 for(int j=0;j<set1.size();j++) {
                     if(  (*(m_elements+i)).is_eq( *(set1.elements()+j) ) ) {
                         keep = true;
                         break;
                     }
                 }
                 if(!keep) {
                    (*this).remove( *(m_elements+i) );
                 }
             }
         }


        void natural(long number) {
            ////////////////////////// 
            //MEMORY LEAK?
            //delete[] m_elements;
            /////////////////////////
            m_size = 0;
            m_elements = new set[m_size];

            for(long i=1;i<=number;i++) {
                (*this).add_self();
            }
            m_value = number;
        }

        void disp() {
            if( m_size==0) {cout<<"{}";}
            else {
                cout<<"{";
                for(int i=0; i<m_size; i++) {
                    (*(m_elements+i)).disp();
                    if(i<m_size-1) {cout<<", ";}
                    //else{cout<<" ";}
                }
                cout<<"}";
            }
        }

        long value() {
            return m_value;
        }

};
const set EMPTY_SET;

interpreter.cpp

#include<fstream>
#include<iostream>
#include<string>
#include<assert.h>
#include<cmath>
#include "headers/set.h"
using namespace std;
string labels[20];
int jump_points[20];
int label_index=0;
const int max_var = 20;
set* set_ptrs[max_var];
string set_names[max_var];
long OPERATIONS = 0;

void assign_var(string name, set other_set) {
    static int index = 0;
    bool exists = false;
    int i = 0;
    while(i<index) {
        if(name==set_names[i]) {
            exists = true;
            break;
        }
        i++;
    }
    if(exists && index<max_var) {
        *(set_ptrs[i]) = other_set;
    }
    else if(!exists && index<max_var) {
        set_ptrs[index] = new set;
        *(set_ptrs[index]) = other_set;
        set_names[index] = name;
        index++;
    }
}

int getJumpPoint(string str) {
    for(int i=0;i<label_index;i++) {
        //cout<<labels[i]<<"\n";
        if(labels[i]==str) {
            //cout<<jump_points[i];
            return jump_points[i];
        }
    }
    cerr<<"Invalid Label Name: '"<<str<<"'\n";
    //assert(0);
    return -1;
}

long strToLong(string str) { 
    long j=str.size()-1;
    long value = 0;
    for(long i=0;i<str.size();i++) {
        long x = str[i]-48;
        assert(x>=0 && x<=9);  // Crash if there was a non digit character
        value+=x*floor( pow(10,j) );
        j--;
    }
    return value;
}

long getValue(string str) {
    for(int i=0;i<max_var;i++) {
        if(set_names[i]==str) {
            set set1;
            set1.natural( (*(set_ptrs[i])).size() );
            if( set1.is_eq( *(set_ptrs[i]) )   ) {
                return (*(set_ptrs[i])).size();
            }
            else {
                cerr<<"That is not a valid integer construction";
                return 0;
            }
        }
    }
    return strToLong(str);
}

int main(int argc, char** argv){
    if(argc<2){std::cerr<<"No input file given"; return 1;}
    ifstream inf(argv[1]);
    if(!inf){std::cerr<<"File open failed";return 1;}
    assign_var("ANSWER", EMPTY_SET);
    int answer;
    string str;
    inf>>str; 
    if(str=="*") { 
        inf>>str;
        long a = strToLong(str);
        inf>>str;
        long b = strToLong(str);
        answer = a*b;
        set set1; set set2;
        set1.natural(a); set2.natural(b);
        assign_var("set1", set1);
        assign_var("set2",set2);
        //cout<<answer;
    }
    else if(str=="+") {
        inf>>str;
        long a = strToLong(str);
        inf>>str;
        long b = strToLong(str);
        answer = a+b;
        set set1; set set2;
        set1.natural(a); set2.natural(b);
        assign_var("set1", set1);
        assign_var("set2",set2);
        //cout<<answer;
    }
    else{ 
         cerr<<"file must start with '+' or '*'"; 
        return 1;
    }

    // parse for labels
    while(inf) {
        if(inf) {   
            inf>>str;
            if(str[str.size()-1]==':') {
                str.erase(str.size()-1);
                labels[label_index] = str; 
                jump_points[label_index] = inf.tellg();
                //cout<<str<<": "<<jump_points[label_index]<<"\n";
                label_index++;
                OPERATIONS++;
            }
        }
    }

    inf.clear();
    inf.seekg(0,ios::beg);
    // parse for everything else

    while(inf) {
        if(inf) {
            inf>>str;

            if(str==";") {
                getline(inf, str,'\n');
            }

            // jump label
            if(str=="jump") {    
                inf>>str;
                inf.seekg( getJumpPoint(str),ios::beg);
                OPERATIONS++;
            }

            // je set label
            if(str=="je") {        
                inf>>str;
                for(int i=0;i<max_var;i++) {
                    if( set_names[i]==str) {
                        if( (*(set_ptrs[i])).is_eq(EMPTY_SET) ) {
                            inf>>str;
                            inf.seekg( getJumpPoint(str),ios::beg);
                            OPERATIONS++; 
                        }
                        break;
                    }
                }
            }

            // jne set label
            if(str=="jne") {
                inf>>str;
                for(int i=0;i<max_var;i++) {
                    if( set_names[i]==str) {
                        if(! (*(set_ptrs[i])).is_eq(EMPTY_SET) ) {
                            inf>>str;
                            inf.seekg( getJumpPoint(str),ios::beg);
                            OPERATIONS++; 
                        }
                        break;
                    }
                }
            }

            // jic set1 set2 label 
            // jump if set1 contains set2
            if(str=="jic") {
                inf>>str;
                string str2;
                inf>>str2;
                set set1;
                set set2;
                for(int i=0;i<max_var;i++) {
                    if( set_names[i]==str ) {
                        set1 = *(set_ptrs[i]);
                    }
                    if(set_names[i]==str2) {
                        set2 = *(set_ptrs[i]);
                    }
                }
                if( set1.contains(set2) ) {
                    inf>>str;
                    inf.seekg( getJumpPoint(str),ios::beg);
                    OPERATIONS++; 
                }
                else {inf>>str;}
            }

            // jidc set1 set2 label
            // jump if set1 doesn't contain set2
            if(str=="jidc") {
                inf>>str;
                string str2;
                inf>>str2;
                set set1;
                set set2;
                for(int i=0;i<max_var;i++) {
                    if( set_names[i]==str ) {
                        set1 = *(set_ptrs[i]);
                    }
                    if(set_names[i]==str2) {
                        set2 = *(set_ptrs[i]);
                    }
                }
                if( !set1.contains(set2) ) {
                    inf>>str;
                    inf.seekg( getJumpPoint(str),ios::beg);
                    OPERATIONS++; 
                }
                else {inf>>str;}
            }

            // assign variable set/int
            if(str=="assign") {
                inf>>str;
                string str2;
                inf>>str2;
                set set1;
                set1.natural( getValue(str2) );
                assign_var(str,set1);
                OPERATIONS++;

            }

            // union set1 set2 set3
            // set1 = set2 u set3
            if(str=="union") {
                inf>>str;
                int i=0;
                while(i<max_var) {
                    if( set_names[i] == str ) {
                        break;
                    }
                    i++;
                }

                set set1;
                set set2;
                string str1;
                inf>>str1;
                string str2;
                inf>>str2;
                for(int j=0;j<max_var;j++) {
                    if( str1 == set_names[j] ) {
                        set1= *(set_ptrs[j]); 
                    }
                    if( str2 == set_names[j] ) {
                        set2= *(set_ptrs[j]);
                    }
                }
                set1.join(set2);
                if(i==max_var) {
                    assign_var(str,set1);
                }
                else {
                    set_names[i]= str;
                    set_ptrs[i] = new set;
                    *(set_ptrs[i]) = set1;
                }
                OPERATIONS++;

            }

            // intersect set1 set2 set3
            // set1 = set2^set3
            if(str == "intersect") {
                inf>>str;
                int i=0;
                while(i<max_var) {
                    if( set_names[i] == str ) {
                        break;
                    }
                    i++;
                }

                set set1;
                set set2;
                string str1;
                inf>>str1;
                string str2;
                inf>>str2;
                for(int j=0;j<max_var;j++) {
                    if( str1 == set_names[j] ) {
                        set1= *(set_ptrs[j]); 
                    }
                    if( str2 == set_names[j] ) {
                        set2= *(set_ptrs[j]);
                    }
                }
                set1.intersect(set2);
                if(i==max_var) {
                    assign_var(str,set1);
                }
                else {
                    set_names[i]= str;
                    set_ptrs[i] = new set;
                    *(set_ptrs[i]) = set1;
                }
                OPERATIONS++;
            }


            // difference set1 set2 set3
            // set1 = set2\set3
            if(str == "difference") {
                inf>>str;
                int i=0;
                while(i<max_var) {
                    if( set_names[i] == str ) {
                        break;
                    }
                    i++;
                }

                set set1;
                set set2;
                string str1;
                inf>>str1;
                string str2;
                inf>>str2;
                for(int j=0;j<max_var;j++) {
                    if( str1 == set_names[j] ) {
                        set1= *(set_ptrs[j]); 
                    }
                    if( str2 == set_names[j] ) {
                        set2= *(set_ptrs[j]);
                    }
                }
                set1.diff(set2);
                if(i==max_var) {
                    assign_var(str,set1);
                }
                else {
                    set_names[i]= str;
                    set_ptrs[i] = new set;
                    *(set_ptrs[i]) = set1;
                }
                OPERATIONS++;
            }

            // add set1 set2
            // put set2 in set 1
            if(str=="add") {
                inf>>str;
                int i = 0; int j =0;
                while(i<max_var) {
                    if(set_names[i]==str) {
                        break;
                    }
                    i++;
                }
                inf>>str;
                while(j<max_var) {
                    if(set_names[j]==str) {
                    break;
                    }   
                    j++;             
                }
                set set2 = *(set_ptrs[j]);
                if( ! (*(set_ptrs[i])).is_eq(set2) ){
                    (*(set_ptrs[i])).add(set2);
                }
                else {
                    (*(set_ptrs[i])).add_self();
                }
                OPERATIONS++;
            }

            // remove set1 set2
            // remove set2 from set1
            if(str=="remove") {
                inf>>str;
                int i = 0; int j =0;
                while(i<max_var) {
                    if(set_names[i]==str) {
                        break;
                    }
                    i++;
                }
                inf>>str;
                while(j<max_var) {
                    if(set_names[j]==str) {
                    break;
                    }   
                    j++;             
                }
                set set2 = *(set_ptrs[j]);
                (*(set_ptrs[i])).remove(set2);
                OPERATIONS++;
            }

            // print set
            // prints true representation of set
            if(str=="print") {
                inf>>str;
                for(int i=0;i<max_var;i++) {
                    if(set_names[i]==str) {
                        (*(set_ptrs[i])).disp();
                    }
                }
                cout<<"\n";
            }

            // printi set
            // prints integer representation of set, if exists.
            if(str=="printi") {
                inf>>str;
                cout<<getValue(str);
                cout<<"\n";
            }
        }
    }

    cout<<"You used "<<OPERATIONS<<" operations\n";
    set testset;
    testset.natural(answer);
    switch( testset.is_eq( *(set_ptrs[0]) ) ) {
        case 1:
            cout<<"Your answer is correct, the set 'ANSWER' is equivalent "<<answer<<".\n";
            break;
        case 0:
            cout<<"Your answer is incorrect\n";
    }
   // cout<<"\n";
    return 0;
}

Condição vencedora

Vocês dois escrevem dois ORGANISMOS do programa , um dos quais multiplica os números nos cabeçalhos, o outro adiciona os números nos cabeçalhos.

Este é um desafio de . O que for mais rápido será determinado pelo número de operações usadas para resolver dois casos de teste para cada programa. Os casos de teste são as seguintes linhas de cabeçalho:

Para adição:

+ 15 12

e

+ 12 15

e para multiplicação

* 4 5

e

* 5 4

Uma pontuação para cada caso é o número de operações utilizadas (o intérprete indicará esse número após a conclusão do programa). A pontuação total é a soma das pontuações para cada caso de teste.

Veja minha entrada de exemplo para obter um exemplo de entrada válida.

Um envio vencedor satisfaz o seguinte:

  1. contém dois órgãos do programa, um que se multiplica e outro que adiciona
  2. tem a menor pontuação total (soma das pontuações nos casos de teste)
  3. Com tempo e memória suficientes, funciona para qualquer número inteiro que possa ser tratado pelo intérprete (~ 2 ^ 31)
  4. Não exibe erros ao executar
  5. Não usa comandos de depuração
  6. Não explora falhas no intérprete. Isso significa que seu programa atual deve ser válido como pseudocódigo e também como um programa interpretável em 'idioma definido'.
  7. Não explora brechas padrão (isso significa que não há casos de teste codificados).

Por favor, veja meu exemplo para implementação de referência e uso de exemplo da linguagem.

Liam
fonte
@ Calvin'sHobbies Pensei que era apenas o meu navegador. Existe um lugar fácil para tirar as fotos?
Liam
@LiamNoronha: cuidei disso. $$...$$funciona no Meta, mas não no Main. Eu usei o CodeCogs para gerar as imagens.
El'endia Starman 17/11/2015
Obrigado @ El'endiaStarman por corrigir a notação de marcação
Liam
3
não há espaço suficiente para otimização
Liam
4
Eu estou votando para fechar esta questão como off-topic, porque não há espaço suficiente para otimização
Liam

Respostas:

1

Resposta de exemplo, operações 1323

Observe que este é um exemplo, não uma entrada real.

Corpo de adição

Observe que esse corpo não será executado sem um cabeçalho.

Os comentários não são necessários em uma resposta real, mas existem para ajudar a ensinar o básico do idioma.

assign ANSWER set2                  ; ANSWER = set2
main:                               ; create label 'main'
    add ANSWER ANSWER               ; Answer union {Answer}, i.e. ANSWER++
    assign looper1 0
    assign looper2 0
    jump dec
    continue:
        intersect set1 set1 looper2 ; set1 = set1 intersect looper2, i.e. set1 = min(set1,looper2)
        jne set1 main
        jump end
dec:
    add looper1 looper1             ; looper1++
    jidc set1 looper1 continue      ; jump if looper >= set1    
    add looper2 looper2             ; looper2++
    jump dec
end:

Para caso de teste

+ 15 12

usa 440 operationse para caso de teste

+ 12 15

usos 299 operations.

Corpo de multiplicação

assign mult_loop 0
main:
    jic set1 mult_loop addition    
    jump end

addition:
    assign temp2 set2
    main_add:
        add ANSWER ANSWER
        assign looper1 0
        assign looper2 0
        jump dec
        cont_add:
            intersect temp2 temp2 looper2
            jne temp2 main_add
            jump end_add
    dec:
        add looper1 looper1
        jidc temp2 looper1 cont_add
        add looper2 looper2
        jump dec
    end_add:
        add mult_loop mult_loop
        jump main

end:

Para caso de teste

* 4 5

usa 305 operationse para caso de teste

* 5 4

usos 279 operations.

Portanto, minha pontuação total é440+299+305+279 = 1323

Liam
fonte
Infelizmente, a única melhoria que consigo pensar é classificar as entradas mine maxusá- las unione intersection, para que as duas adições e as duas multiplicações obtenham a mesma pontuação (mais baixa). Isso não parece uma melhoria suficiente para eliminar o restante desta solução de referência. ;)
Martin Ender
@ MartinBüttner Hah Acabei de assumir que minhas primeiras tentativas seriam terríveis. Bem, se esse for o caso, podemos muito bem perto a questão então
Liam
Eh, só porque não consigo pensar em nada melhor não significa que existem abordagens muito melhores. Vamos ver ...;) #
9788 Martin Ender
@ MartinBüttner Eu estava com medo de que algo assim pudesse acontecer, mas, como dediquei muito pouco às soluções, presumi que elas seriam fáceis de derrotar. Vou dar uma semana mais ou menos.
Liam