Déterminer si un nombre est un multiple de dix ou dans un ensemble particulier de plages


104

J'ai quelques boucles dont j'ai besoin dans mon programme. Je peux écrire le pseudo code, mais je ne suis pas tout à fait sûr de savoir comment les écrire logiquement.

J'ai besoin -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

Ceci est pour un jeu de société serpents et échelles, si cela a plus de sens pour ma question.

J'imagine la première instruction if dont j'ai besoin pour utiliser le module. Serait- if (num == 100%10)ce correct?

Le second, je n'ai aucune idée. Je peux l'écrire comme if (num > 10 && num is < 21 || etc.), mais il doit y avoir quelque chose de plus intelligent que ça.


16
Généralement, la longueur d'un bon code est proportionnelle à la longueur de l'anglais décrivant ce qu'il fait. Ainsi, lorsque votre "spécification" indique 11-20, 31-40, 51-60, 71-80, 91-100, vous pouvez vous attendre à ce que votre code mentionne également ces chiffres. Si ces nombres viennent de quelque part ou ont été générés pour une raison quelconque, voyez si vous pouvez coder la raison plutôt que les nombres.
luqui

39
@ user3419168: Le compilateur ne se soucie pas du tout de la lisibilité de votre code; il le compilera en une fraction de seconde. Mais pour les humains qui lisent votre code, les choix que vous faites peuvent le faire comprendre en quelques secondes, minutes, heures ou jamais. Cela impose un coût; les gens sont payés pour lire et comprendre le code, alors facilitez-leur la tâche. Écrivez toujours du code de production pour maximiser la lisibilité et n'oubliez pas que la concision n'améliore pas nécessairement les performances du code.
Eric Lippert

2
@AmadeusDrZaius - J'ai TRÈS RAREMENT fait la même chose, mais seulement pour les sections critiques pour les performances. La boucle la plus serrée appelée 100 millions de fois se qualifie - l'instruction if dans un jeu de serpents et d'échelles ne le fait pas. Où vous tracez la ligne entre eux est un choix personnel.
Floris

2
Je déteste le dire, mais après avoir fait suffisamment de travail en entreprise, avec des débutants écrivant du vrai code, je devrais recommander le forçage brutal. Parce qu'alors les nouveaux gars comprendront et ne le casseront pas. triste mais vrai - dans certains cas, il est intelligent d'être stupide.
Richard Le Mesurier

22
C'est une question décente, et je ne veux rien enlever de l'affiche, mais cela ne mérite pas plus de 500 points. C'est ainsi que nous nous retrouvons avec certaines des absurdités que nous faisons avec des gens avec des milliers de points qui semblent être des autorités ici. (N'hésitez pas à déplacer ce commentaire s'il appartient ailleurs.)
GaTechThomas

Réponses:


87

Pour le premier, pour vérifier si un nombre est un multiple d'utilisation:

if (num % 10 == 0) // It's divisible by 10

Pour le second:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

Mais c'est plutôt dense, et vous feriez peut-être mieux de simplement énumérer les options explicitement.


Maintenant que vous avez donné une meilleure idée de ce que vous faites, j'écrirais le deuxième comme suit:

   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

C'est la même logique, mais en utilisant la fonction, nous avons une idée plus claire de ce que cela signifie.


79
if((num - 1) / 10) % 2 == 1 && num < 100)- Je pleurerais si je voyais ça.
Daniel Kamil Kozar le

32
@DanielKamilKozar, comme il se doit.
Winston Ewert

2
@ user3419168, rien qu'en lui-même, cela laisse à se demander ce que cela signifie dans le monde. Cela ne donne aucune indication sur ce qu'il essaie de faire dans le monde. C'est pourquoi, dans l'édition, j'ai montré une version qui divise la logique en une fonction qui clarifie ce que font réellement les calculs.
Winston Ewert

3
Il peut être prudent d'affirmer également num >= 11comme (1) que la limite inférieure est proscrite, et (2) %sur un nombre négatif renvoie également un nombre négatif. (Je dois admettre qu'utiliser & 1ici est "plus sûr" mais suppose également des connaissances supplémentaires.)
usr2564301

2
+1 pour le Edit, il entre dans le pourquoi de la liste des plages et le présente de manière lisible. OMI, un pas en avant serait d'envelopper le getRow(num) % 2 == 0dans une fonction ainsi pour indiquer clairement quelle est l'intention. bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
Mr.Mindor

40

if (num est un multiple de 10) {do this}

if (num % 10 == 0) {
  // Do something
}

if (num est compris entre 11-20, 31-40, 51-60, 71-80, 91-100) {do this}

L'astuce ici est de rechercher une sorte de points communs entre les gammes. Bien entendu, vous pouvez toujours utiliser la méthode "force brute":

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

Mais vous remarquerez peut-être que si vous soustrayez 1de num, vous aurez les plages:

10-19, 30-39, 50-59, 70-79, 90-99

En d'autres termes, tous les nombres à deux chiffres dont le premier chiffre est impair. Ensuite, vous devez trouver une formule qui exprime cela. Vous pouvez obtenir le premier chiffre en divisant par 10, et vous pouvez tester que c'est étrange en vérifiant le reste de 1 lorsque vous divisez par 2. Rassemblez tout cela:

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

Étant donné le compromis entre un code plus long mais maintenable et un code "intelligent" plus court, je choisirais plus long et plus clair à chaque fois. À tout le moins, si vous essayez d'être intelligent, veuillez inclure un commentaire qui explique exactement ce que vous essayez d'accomplir.

Cela permet de supposer que le prochain développeur qui travaillera sur le code est armé et sait où vous habitez. :-)


7
J'irais toujours pour le code intelligent, mais le transformer en code maintenable en extrayant des fonctions. Ce serait tout aussi lisible si ce dernier bit était dit && isTensDigitOdd(num), peut-être avec un commentaire avant la définition de la fonction expliquant ce qu'elle fait. Si un tel modèle existe, un commentaire expliquant le raisonnement du modèle est éclairant pour la maintenabilité imo.
chris

3
Chris, c'est une excellente stratégie quand «l'intelligence» a un net avantage: un code beaucoup plus court (ce qui signifie moins de risque de faute de frappe, surtout s'il change), ou une grande amélioration de l'efficacité. Il y a presque toujours un compromis entre brièveté, clarté et efficacité, et trouver un bon compromis est une grande compétence à développer. (Voir stackoverflow.com/a/2151844/29157 pour un ricanement.)
Adam Liss

1
C'est une bien meilleure approche. Tellement plus facile à comprendre que le «code intelligent» et la différence de performances est probablement négligeable.
user1477388

@AdamLiss, Oui, mon avis n'a que peu de valeur car je n'ai pas assez d'expérience pour voir les répercussions de ces décisions. Je suis sûr que je le ferai assez tôt et je ne manquerai pas d'avoir un deuxième avis si nécessaire.
chris

1
Ne vous vendez pas à découvert. Votre instinct est très sensé et vous semblez désireux de continuer à apprendre. Chaque opinion est valable s'il y a une bonne raison derrière elle ... et parfois même si ce n'est pas le cas. Je parierais que tu iras loin.
Adam Liss

30

Si vous utilisez GCC ou tout autre compilateur prenant en charge les plages de cas, vous pouvez le faire, mais votre code ne sera pas portable .

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}

1
Pouvez-vous me dire pourquoi ce code n'est pas portable?
M Sharath Hegde

8
@MSharathHegde car il nécessite l'extension GCC, qui ne fait pas partie du standard et que certains compilateurs ne le supportent pas
Bryan Chen

5
C'est la bonne réponse, car l'intention est immédiatement évidente. Toutes ces réponses «intelligentes» avec modulo sont un cauchemar de maintenance, même avec des commentaires.
Smirkingman

@smirkingman En effet, c'est ce que j'ai dit dans mon commentaire sur la question principale. Il faut juste une certaine expérience des nouveaux codeurs dans un travail en entreprise pour se rendre compte que la manière évidente est souvent bien meilleure que la manière intelligente-ninja.
Richard Le Mesurier

15

Ceci est pour les futurs visiteurs plus qu'un débutant. Pour une solution plus générale, semblable à un algorithme, vous pouvez prendre une liste de valeurs de début et de fin et vérifier si une valeur passée se trouve dans l'une d'elles:

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

Par souci de simplicité, j'ai utilisé un lambda polymorphe (C ++ 14) au lieu d'un pairargument explicite . Cela devrait aussi probablement s'en tenir à l'utilisation <et ==être cohérent avec les algorithmes standard, mais cela fonctionne comme ça tant que cela Elema été <=défini pour cela. Quoi qu'il en soit, il peut être utilisé comme ceci:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

Il y a un exemple en direct ici .


Solution soignée. J'aurais probablement utilisé un seul tableau, car vous pouvez le formater avec 2 nombres par ligne pour représenter des paires.
Kevin Lam

@ HunterGuy2, très bon point. Je vais en fait le changer pour qu'il fonctionne par paires parce que je ne pensais qu'aux itérateurs zip pour une raison quelconque.
chris

Vraiment belle approche stl! Aimer!
higuaro

5

Le premier est facile. Il vous suffit d'appliquer l'opérateur modulo à votre valeur num:

if ( ( num % 10 ) == 0)

Puisque C ++ évalue chaque nombre qui n'est pas 0 comme vrai, vous pouvez également écrire:

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

Pour le second, je pense que c'est plus simple à comprendre:

Le motif se répète tous les 20, vous pouvez donc calculer le modulo 20. Tous les éléments que vous voulez seront dans une rangée sauf ceux qui sont divisibles par 20.

Pour les obtenir également, utilisez simplement num-1 ou mieux num + 19 pour éviter de traiter des nombres négatifs.

if ( ( ( num + 19 ) % 20 ) > 9 )

Cela suppose que le modèle se répète pour toujours, donc pour 111-120, il s'appliquerait à nouveau, et ainsi de suite. Sinon, vous devez limiter les nombres à 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )

5

Avec quelques bons commentaires dans le code, il peut être écrit de manière assez concise et lisible.

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }

2
Le premier commentaire est inutile. Tout programmeur avec un peu d'expérience saura que num % 10 == 0c'est la même chose numqu'un multiple de 10.
Justin

7
oui mais les débutants lisent aussi ce site. Je n'utiliserais normalement pas ce commentaire dans mon propre code, mais cela rend la réponse plus claire pour les débutants qui bénéficieraient de cette question pour débutants.
La-comadreja

2
Merci de ne jamais faire ça. Cela réduit en fait la lisibilité, en ralentissant le lecteur et en le forçant à tout lire deux fois. Tout programmeur qui ne comprend pas cela if (num % 10 == 0)signifie la même chose que // Check if it's a multiple of 10ne devrait pas maintenir votre code. Il s'agit d'un anti-pattern bien connu.
Dawood ibn Kareem

1
@DavidWallace voir le commentaire ci-dessus. Nous ne pouvons pas garantir que les lecteurs de cet article connaîtront cet anti-modèle.
La-comadreja

1
Non, je veux dire que commenter chaque ligne pour dire ce qu'elle fait est un anti-modèle. Je ne veux pas dire que l'utilisation %est un anti-pattern; ce n'est évidemment pas le cas. Vraiment, en supposant que beaucoup de lecteurs de cet article seront des débutants, leur enseigner ce style d'écriture des commentaires apporte une contribution négative à leur développement en tant que programmeurs.
Dawood ibn Kareem

4

Vous avez essentiellement expliqué la réponse vous-même, mais voici le code au cas où.

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}

2
Corrigez x < 41 x > 50et mettez des parenthèses.
101010

1
@ 40two, Techniquement, operator&&a une priorité plus élevée que operator||, donc ça va, mais je suis à peu près sûr que GCC l'avertit quand même.
chris

18
Envisagez de représenter l'inégalité 10 < x < 21comme 10 < x && x < 21plutôt que x > 10 && x < 21. Il est plus facile de lire l'inégalité quand elle est dans le même ordre que vous l'écririez mathématiquement.
Eric Lippert

5
Ce code est assez illisible et en dit peu sur la logique réelle. Je n'aime pas cette réponse.
Dariusz

3
Je vote défavorablement parce que vous avez répondu exactement à ce que le PO a fait.
Bruno Ferreira

3

Vous y réfléchissez peut-être trop.

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

La première ligne if (x % 10)fonctionne car (a) une valeur qui est un multiple de 10 se calcule comme '0', les autres nombres donnent leur reste, (b) une valeur de 0 dans an ifest considérée false, toute autre valeur esttrue .

Éditer:

Pour basculer dans la vingtaine, utilisez la même astuce. Cette fois, le numéro pivot est 10:

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/10renvoie n'importe quel nombre de 0 à 9 comme 0, 10 à 19 comme 1et ainsi de suite. Tester sur pair ou impair - le & 1- vous indique si c'est pair ou impair. Puisque vos plages sont en fait de "11 à 20", soustrayez 1 avant de tester.


1

Un plaidoyer pour la lisibilité

Bien que vous ayez déjà de bonnes réponses, je voudrais vous recommander une technique de programmation qui rendra votre code plus lisible pour certains futurs lecteurs - cela peut être vous dans six mois, un collègue a demandé d'effectuer une révision de code, votre successeur, .. .

C'est pour envelopper toutes les instructions "intelligentes" dans une fonction qui montre exactement (avec son nom) ce qu'elle fait. Bien qu'il y ait un impact minime sur les performances (de la "surcharge d'appel de fonction"), c'est vraiment négligeable dans une situation de jeu comme celle-ci.

En cours de route, vous pouvez nettoyer vos entrées - par exemple, tester les valeurs «illégales». Ainsi, vous pourriez vous retrouver avec un code comme celui-ci - voyez à quel point il est plus lisible? Les "fonctions d'assistance" peuvent être cachées quelque part (elles n'ont pas besoin d'être dans le module principal: leur nom indique clairement ce qu'elles font):

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}

3
N'essaie-t-il pas de pousser trop loin avec YESet NO?
rmobis

@Raphael_ - ça pourrait bien l'être: je montrais juste un "par exemple". De nombreuses personnes utilisent évidemment vrai / faux. Mais je ne me souviens jamais (car différentes langues utilisent des conventions différentes): is is TRUE,True ou true? Et quels fichiers d'en-tête devrais-je inclure, le cas échéant, dans le C ordinaire? Alors j'ai roulé le mien. Je me demande si c'est ce qui a obtenu un vote négatif ...
Floris

1

Pour le premier:

if (x % 10 == 0)

s'appliquera à:

10, 20, 30, .. 100 .. 1000 ...

Pour le second:

if (((x-1) / 10) % 2 == 1)

demandera:

11-20, 31-40, 51-60, ..

Nous faisons d'abord x-1pour obtenir:

10-19, 30-39, 50-59, ..

Ensuite, nous les divisons par 10pour obtenir:

1, 3, 5, ..

Nous vérifions donc si ce résultat est étrange.


1

Vous pouvez essayer ce qui suit:

// Multiple of 10
if ((num % 10) == 0)
{
   // Do something
}
else if (((num / 10) % 2) != 0)
{
    // 11-20, 31-40, 51-60, 71-80, 91-100
}
 else
{
    // Other case
}

Dans la question OP, la vérification du multiple de 10 n'est pas liée à la vérification de la plage, et dans la plage, la vérification 20 doit être sur la même plage de 11, avec votre code ((20/10)% 2) -> ( 2% 2) -> 0
Serpiton

0

Je sais que cette question a tellement de réponses, mais je vais quand même jeter la mienne ici ...

Tiré du code complet de Steve McConnell , 2nd Edition: "Stair-Step Access Tables:

Un autre type d'accès à la table est la méthode en escalier. Cette méthode d'accès n'est pas aussi directe qu'une structure d'index, mais elle ne gaspille pas autant d'espace de données. L'idée générale des structures en escalier, illustrée à la figure 18-5, est que les entrées dans un tableau sont valides pour des plages de données plutôt que pour des points de données distincts.

Entrez la description de l'image ici

Figure 18-5 L'approche en escalier catégorise chaque entrée en déterminant le niveau auquel elle atteint un «escalier». Le «pas» qu'il atteint détermine sa catégorie.

Par exemple, si vous écrivez un programme de notation, la plage d'entrée «B» peut être comprise entre 75% et 90%. Voici une gamme de notes que vous pourriez avoir à programmer un jour:

Entrez la description de l'image ici

Pour utiliser la méthode de l'escalier, vous placez l'extrémité supérieure de chaque plage dans une table, puis écrivez une boucle pour vérifier un score par rapport à l'extrémité supérieure de chaque plage. Lorsque vous trouvez le point auquel le score dépasse pour la première fois le haut d'une fourchette, vous savez quelle est la note. Avec la technique de l'escalier, vous devez faire attention à gérer correctement les extrémités des plages. Voici le code en Visual Basic qui attribue des notes à un groupe d'étudiants en fonction de cet exemple:

Entrez la description de l'image ici

Bien qu'il s'agisse d'un exemple simple, vous pouvez facilement le généraliser pour gérer plusieurs étudiants, plusieurs systèmes de notation (par exemple, différentes notes pour différents niveaux de points sur différents devoirs) et des changements dans le système de notation. "

Code complet , 2e édition, pages 426 à 428 (chapitre 18).


pourquoi pensez-vous que ce soit la mauvaise question? ce n'est pas parce que je n'ai pas fourni d'exemple sur le cas OP que j'ai répondu à la mauvaise question ...
lauCosma

0

Comme d'autres l'ont souligné, rendre les conditions plus concises n'accélérera pas la compilation ou l'exécution, et cela n'aide pas nécessairement non plus la lisibilité.

Cela peut aider à rendre votre programme plus flexible, au cas où vous décideriez plus tard que vous voulez une version du jeu pour tout-petit sur un plateau 6 x 6, ou une version avancée (à laquelle vous pouvez jouer toute la nuit) sur un plateau 40 x 50 .

Je le coderais donc comme suit:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

Oui, c'est verbeux, mais cela montre exactement ce qui se passe sur le plateau de jeu.

Si je développais ce jeu pour l'afficher sur un téléphone ou une tablette, je créerais des variables ROWS et COLUMNS au lieu de constantes, afin qu'elles puissent être définies dynamiquement (au début d'un jeu) pour correspondre à la taille et à l'orientation de l'écran.

J'autoriserais également l'orientation de l'écran à changer à tout moment, en milieu de partie - tout ce que vous avez à faire est de changer les valeurs de ROWS et COLUMNS, tout en laissant tout le reste (le numéro de carré actuel sur lequel chaque joueur est, et le carrés de début / fin de tous les serpents et échelles) inchangés. Ensuite, il vous suffit de bien dessiner le tableau et d'écrire du code pour vos animations (je suppose que c'était le but de vos ifdéclarations) ...


J'ai également voté à la hausse pour la réponse de Floris - un style différent pour obtenir un résultat similaire, que je n'ai pas vu avant d'écrire ma réponse
Laurence Renshaw

2
vous devriez utiliser la fonction en ligne au lieu de#define
Bryan Chen

C'est une bonne pratique lorsque vous utilisez des #defineinstructions de type fonction , pour mettre des parenthèses autour des arguments, là où ils apparaissent dans le développement. Alors au lieu de #define finished(num) (num == lastSquare)vous devriez écrire #define finished(num) ((num) == lastSquare). La raison en est que si vous utilisez une telle instruction avec une expression contenant un opérateur avec une priorité suffisamment faible, vous n'obtiendrez pas la réponse que vous attendez. Dans ce cas, si vous n'utilisez pas les parenthèses supplémentaires, alors se finished(a & b)développe dans (a & b == lastSquare)ce qui n'est certainement pas ce que vous voulez.
Dawood ibn Kareem
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.