Jump to content

Números muito grandes com a biblioteca GMP


PsySc0rpi0n

Recommended Posts

Boas.

Estou com algumas dificuldades em perceber como fazer aqui umas cenas...

Tenho 3 funcções em que uma delas vai chamar as outras duas em condições específicas mas todas ou quase todas as variáveis estão criadas na função main().

Pretendo também perceber se há alguma maneira mais elegante de fazer este tipo de coisa. Uma função chamar outras duas e enviar-lhes parâmetros por referência.

As 3 funções que tenho são:

ecdsa_add(gmp_coord pointA, gmp_coord pointB, gmp_coord* newPointAplusB, mpz_t opA, mpz_t opP);
ecdsa_double(gmp_coord point, gmp_coord* pointDouble, mpz_t opA, mpz_t opP);
ecdsa_doubleadd(gmp_coord pointG, gmp_coord* newPoint, gmp_coord* newPointdoubleadd,mpz_t opA, mpz_t opP);

A variável gmp_coord é definida por mim como:

typedef struct{
  	mpz_t x;
	mpz_t y;
}gmp_coord;

Na função main() tenho declaradas todas aquelas variáveis que estão nos parâmetros das 3 funções:

gmp_coord pointG, pointP, pointQ, pointG;
gmp_coord* pointAdd, pointDouble, pointDoubleAdd;
mpz_t  opA, opP;

 

Depois é suposto chamar estas 3 funções uma de cada vez, para já só para testar, embora dentro da ecdsa_doubleAdd() eu chame as outras duas:

ecdsa_add(pointP, pointQ, &pointAdd, a, P);
ecdsa_double(pointP, &pointDouble, a, P);
ecdsa_doubleAdd(pointG, &pointDoubleP, &pointDoubleAdd, privKey, a, P);

 

Agora penso que em termos de apontadores, está tudo correcto, já que eu quero alterar os seus valores na função main(), onde elas foram declaradas.

A minha pergunta é:

quando enviamos uma destas variáveis para dentro de uma função que por sua vez são enviados para dentro de outra função dentro da primeira função, ter as variáveis na função main() declaradas apenas como apontadores para um tipo de dados é suficiente ou tenho que as declarar como apontadores para apontadores?

 

E há alguma forma mais elegante de "montar" estas 3 funções? É qe não sei se é muito comum enviar parâmetros para dentro de uma função que directamente ela não vai precisar deles. É só para os enviar para as funções que esta função vai chamar dentro dela!

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Link to comment
Share on other sites

Usando aqui um exmplo mais simples que acho que consegue mostrar o mesmo que no meu post original:
 

#include <stdio.h>

void change(int* a, int* b){
    *a += 1;
    *b += 1;
}

int mult(int* a, int* b){
    change(a, b);
    return *a * (*b);
}

int main(){
    int x = 2, y = 3;
    printf("a * b = %d\n", mult(&x, &y));
    return 0;
}

Neste exemplo, eu não conseguiria enviar os endereços de memória das variáveis x e y para a função change() se não os tivesse enviado primeiro para a função mult(), certo?

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Link to comment
Share on other sites

Se o teu objectivo é alterar o valor the x e y, então precisas de passar as variáveis por referência para a função mult.  Contudo, se o que te interessa é apenas o resultado da função mult, então podias passar os valores directamente para a função mult, e depois por referência para a função change, e o resultado seria o mesmo:

#include <stdio.h>

void change(int* a, int* b){
    *a += 1;
    *b += 1;
}

int mult(int a, int b){
    change(&a, &b);
    return a * b;
}

int main(void) {
    int x = 2, y = 3;
    printf("a * b = %d\n", mult(x, y));
    return 0;
}

Ou seja, a questão que deves colocar é até que ponto queres tornar determinadas alterações de variáveis visíveis, para depois decidires onde é que começas a passar os argumentos por referência.

 

Relativamente à questão original, o código que partilhaste parece ter vários erros, e não deve sequer compilar, pelo que é difícil ajudar.

Link to comment
Share on other sites

On 10/29/2023 at 1:42 PM, Rui Carlos said:

Se o teu objectivo é alterar o valor the x e y, então precisas de passar as variáveis por referência para a função mult.  Contudo, se o que te interessa é apenas o resultado da função mult, então podias passar os valores directamente para a função mult, e depois por referência para a função change, e o resultado seria o mesmo:

#include <stdio.h>

void change(int* a, int* b){
    *a += 1;
    *b += 1;
}

int mult(int a, int b){
    change(&a, &b);
    return a * b;
}

int main(void) {
    int x = 2, y = 3;
    printf("a * b = %d\n", mult(x, y));
    return 0;
}

Ou seja, a questão que deves colocar é até que ponto queres tornar determinadas alterações de variáveis visíveis, para depois decidires onde é que começas a passar os argumentos por referência.

 

Relativamente à questão original, o código que partilhaste parece ter vários erros, e não deve sequer compilar, pelo que é difícil ajudar.

 

Ok, podia ter pensado nisso sim. Mas no caso do meu código inicial, todas as variáveis estão declaradas na função mian(), logo, para alterar essas variáveis numa função que já é chamada por outra, eu tenho que passar as referências das variáveis na funçãomain(), de função em função, certo?

É esta a minha dúvida.

Ou seja, eu tenho a função main() e tenho a funcA() e a funcB(). Na função main() declaro uma variável (ponteiro para...). Só vou precisar desta variável alterada dentro da funcB() que por sua vez é chamada pela funcA(). Neste cenário, eu tenho que passar o endereço de memória da variável declarada na main() para a funcA() e depois para a funcB(), certo?

 

Quanto ao código inicial, não tem erros nenhuns. Eu é que só ali coloquei partes do código!

Entretanto, coloquei o código todo no Gitlab. Se tiveres curiosidade e se quiseres ajudar quando eu precisar, é mais fácil!

https://gitlab.com/PsySc0rpi0n/bitcoinexp/-/blob/master/gmp_functions.c

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Link to comment
Share on other sites

Em 30/10/2023 às 00:48, PsySc0rpi0n disse:

Ok, podia ter pensado nisso sim. Mas no caso do meu código inicial, todas as variáveis estão declaradas na função mian(), logo, para alterar essas variáveis numa função que já é chamada por outra, eu tenho que passar as referências das variáveis na função main(), de função em função, certo?

Sim.

Em 30/10/2023 às 00:48, PsySc0rpi0n disse:

Quanto ao código inicial, não tem erros nenhuns. Eu é que só ali coloquei partes do código!

Entretanto, coloquei o código todo no Gitlab. Se tiveres curiosidade e se quiseres ajudar quando eu precisar, é mais fácil!

https://gitlab.com/PsySc0rpi0n/bitcoinexp/-/blob/master/gmp_functions.c

O meu C está um pouco enferrujado, mas:

  • O código que partilhaste inicialmente tinha gmp_Coord*, o que não devia compilar.
  • Tens a declaração mpz_t  opA, opP, mas depois usas variáveis a e P que não estão no código das declarações.
  • Tens gmp_coord* pointAdd, e ecdsa_add(pointP, pointQ, &pointAdd, a, P).  O terceiro parâmetro da função ecdsa_add devia ser do tipo gmp_coord*, mas &pointAdd é do tipo gmp_coord**.

 

No código do GitLab nenhum destes problemas está presente 🙂 

 

Para terminar (e relembro mais uma vez que há muito anos que não uso C com regularidade), não sei até que ponto declarar certas variáveis como gmp_coord* e outras como gmp_coord é uma boa ideia.  Estou tentado a dizer que no teu caso declarar tudo como gmp_coord deixava o código mais consistente, e evitava uns mallocs (e se usares mallocs, é recomendado não fazer cast dos resultados).

Link to comment
Share on other sites

On 10/30/2023 at 7:32 PM, Rui Carlos said:

Sim.

O meu C está um pouco enferrujado, mas:

  • O código que partilhaste inicialmente tinha gmp_Coord*, o que não devia compilar.
  • Tens a declaração mpz_t  opA, opP, mas depois usas variáveis a e P que não estão no código das declarações.
  • Tens gmp_coord* pointAdd, e ecdsa_add(pointP, pointQ, &pointAdd, a, P).  O terceiro parâmetro da função ecdsa_add devia ser do tipo gmp_coord*, mas &pointAdd é do tipo gmp_coord**.

 

No código do GitLab nenhum destes problemas está presente 🙂 

 

Para terminar (e relembro mais uma vez que há muito anos que não uso C com regularidade), não sei até que ponto declarar certas variáveis como gmp_coord* e outras como gmp_coord é uma boa ideia.  Estou tentado a dizer que no teu caso declarar tudo como gmp_coord deixava o código mais consistente, e evitava uns mallocs (e se usares mallocs, é recomendado não fazer cast dos resultados).

Sim, tens razão na primeira e na terceira situações. Foram coisas que entretanto corrigi e não me lembrei de corrigir aqui no post, excepto a do gmp_Coord que corrigi ainda ontem.

Quando às variáveis a, p, opA e opP, essas estão correctas. O a e P na declaração das funções não tem que ser igual ao nome das variáveis declaradas na função main().

Sobre a declaração das variáveis do tipo de dados que eu criei, acho que tinha que ser assim pois eu pretendo enviar os endereços de memória destas variáveis para depois as outras funções alterarem os seus valores no scope da função main().

 

Por fim, a declaração que fiz sem apontadores é porque esses valores são constantes. Não serão alteradas durante a execução do código. Já as outras, como referi a cima, é para os seus valores serem alterados pelas outras funções no scope da função main().

 

Agora, tenho outra dúvida que ainda não consegui perceber. E esta é quase existencial. Mas que imagem mental podemos fazer de um array que contem words ?

Ou seja, pegando num exemplo específico, digamos que eu tenho o número 1000 (decimal). A sua representação hexadecimal é 0x3e8 e em binário é 0011 1110 1000

No código do Gitlab, na linha #96, a variábel binary_array[] é suposto conter words deste número 1000. Mas eu ainda não percebi o que é suposto estar neste array em cada índice! Porque a ideia que eu tinha era por exemplo, se eu definisse o tamanho da word como 1 (do sizeof(unsigned char)), como preciso de 10 bits para representar 1000 (decimal), então teria 10 words. Uma word por cada bit. Se definisse o tamanho da word como 4, estava à espera de obter 3 words de 4 bits cada (12 bits), embora neste caso tivesse os dois MSB a zero e os pudesse descartar, fazendo os 10 bits necessários para representar 1000 em binário! Mas acho que não estoua ver isto bem e aina não percebi como "visualizar" isto.

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Link to comment
Share on other sites

Em 31/10/2023 às 02:09, PsySc0rpi0n disse:

Quando às variáveis a, p, opA e opP, essas estão correctas. O a e P na declaração das funções não tem que ser igual ao nome das variáveis declaradas na função main().

Referia-me às variáveis utilizadas na invocação das funções, que não são as declaradas (e como tal, não era sequer possível saber o tipo das mesmas).

Em 31/10/2023 às 02:09, PsySc0rpi0n disse:

Sobre a declaração das variáveis do tipo de dados que eu criei, acho que tinha que ser assim pois eu pretendo enviar os endereços de memória destas variáveis para depois as outras funções alterarem os seus valores no scope da função main().

Podias usar o operador &.

Em 31/10/2023 às 02:09, PsySc0rpi0n disse:

Agora, tenho outra dúvida que ainda não consegui perceber. E esta é quase existencial. Mas que imagem mental podemos fazer de um array que contem words ?

Ou seja, pegando num exemplo específico, digamos que eu tenho o número 1000 (decimal). A sua representação hexadecimal é 0x3e8 e em binário é 0011 1110 1000

No código do Gitlab, na linha #96, a variábel binary_array[] é suposto conter words deste número 1000. Mas eu ainda não percebi o que é suposto estar neste array em cada índice! Porque a ideia que eu tinha era por exemplo, se eu definisse o tamanho da word como 1 (do sizeof(unsigned char)), como preciso de 10 bits para representar 1000 (decimal), então teria 10 words. Uma word por cada bit. Se definisse o tamanho da word como 4, estava à espera de obter 3 words de 4 bits cada (12 bits), embora neste caso tivesse os dois MSB a zero e os pudesse descartar, fazendo os 10 bits necessários para representar 1000 em binário! Mas acho que não estoua ver isto bem e aina não percebi como "visualizar" isto.

Cada "word" parece ser um byte (8 bits) do número (o ciclo entre 0 e 7 dá essa ideia).

Link to comment
Share on other sites

On 10/31/2023 at 8:08 PM, Rui Carlos said:

Referia-me às variáveis utilizadas na invocação das funções, que não são as declaradas (e como tal, não era sequer possível saber o tipo das mesmas).

Podias usar o operador &.

Cada "word" parece ser um byte (8 bits) do número (o ciclo entre 0 e 7 dá essa ideia).

Ok, ignorando o code do primeiro post que parece que tinha de facto vários erros que eu entretanto fui corrigindo e passei para o Gitlab, vou-me referir, a partir de agora apenas ao código do gitlab.

Não percebo porque é que a função desta biblioteca me diz que o número 723080195092 precisa de 40 words para ser escrito em binário.

number_of_words = mpz_sizeinbase(pKey, 2);
printf("Number of words: %d\n", number_of_words);

Isto resulta em 40. Mas convertendo este número em binário no RapidTables, obtenho 1010100001011010111100000010110000010100. São 40 dígitos. Não sei se é coincidência, mas o número de words também é 40, o que me leva a pensar que mesmo que eu defina a word size para outro valor qualquer, a função mpz_sizeinbase() considera sempre words de 1 bit de tamanho.

Significa que a variável binary_array[] vai ter 40 índices, de 0 a 39.

Depois dei-me ao trabalho de mandar imprimir alguns indexes da variável binary_array[] e obtive resultados que não sei interpretar mas posso mostrar algo como isto:

20231101-020928.jpg

Não percebo aqueles valores!

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Link to comment
Share on other sites

Anyway, acho que já percebi algumas coisas, nomeadamente como se parece aquele array de words, dependendo do parâmetro size.

Fiz no Calc (LibreOffice) uma visualiozação mental do que me parece que está naquele array, para size = 1 e size = 8.

image.png

 

Agora outra questãoa me assola! 😛

Quando uso este pequeno programa para testar a minha visualização do array, não obtenho o que estava à espera.

Por exemplo este código

#include <stdio.h>
#include <gmp.h>
#include <inttypes.h>
#include <stdlib.h>

int main(void){
    uint8_t* bin_array;
    uint8_t order = 1, endian = 0;
    size_t countp = 0, size = 1 , nails = 0;
    size_t numb = 8 * size - nails;
    size_t count = 0;
    mpz_t op;

    mpz_init(op);
    mpz_set_str(op, "1000", 0);
    countp = mpz_sizeinbase(op, 2);
    count = (countp + numb - 1) / numb;
    if((bin_array = (uint8_t*) malloc(count * size)) == NULL)
        return -1;
    mpz_export(bin_array, &countp, order, size, endian, nails, op);
    printf("word size = %zu\n"
            "return of mpz_sizeinbase() =  %zu\n"
            "number of words produced = %zu\n",
            size, count, countp);
    for(uint8_t i = 0; i < count; i++)
        for(int8_t j = 7; j >= 0; j--)
        printf("bin_arr[%d]: %d\n", i, (bin_array[i] >> j) & 1);
    mpz_clear(op);
    return 0;
}

usando size = 1, obtenho ist:

Quote

$ ./test
word size = 1
return of mpz_sizeinbase() =  2
number of words produced = 2
bin_arr[0]: 0
bin_arr[0]: 0
bin_arr[0]: 0
bin_arr[0]: 0
bin_arr[0]: 0
bin_arr[0]: 0
bin_arr[0]: 1
bin_arr[0]: 1
bin_arr[1]: 1
bin_arr[1]: 1
bin_arr[1]: 1
bin_arr[1]: 0
bin_arr[1]: 1
bin_arr[1]: 0
bin_arr[1]: 0
bin_arr[1]: 0

usando size = 8, obtenho:

Quote

$ ./test
word size = 8
return of mpz_sizeinbase() =  1
number of words produced = 1
bin_arr[0]: 1
bin_arr[0]: 1
bin_arr[0]: 1
bin_arr[0]: 0
bin_arr[0]: 1
bin_arr[0]: 0
bin_arr[0]: 0
bin_arr[0]: 0

 

Não percebo porque é que com size = 8, obtenho um stream incompleto para o número 1000.

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.