Domanda Esercizi Palindroma in C

k35p0n33

Utente Iron
22 Febbraio 2022
16
4
2
18
Salve ragazzi, avrei bisogno di realizzare un programma in c che verifica in un file di stringhe (con punteggiatura) se ci sono parole palindrome.
Devo creare una funzione che verifica ciò.
Il mio problema è di capire come ignorare la punteggiatura e salvare tutte le parole palindrome.
 
Inizia a postare qualcosa di tuo e poi vediamo di aggiustartelo. Se il tuo problema è solo la punteggiatura posta una soluzione che funziona senza punteggiatura.
Non volevo pubblicarlo più che altro perchè mi sono fermato qui ed ho fatto una confusione pazzesca ahahah, il mio intento era di salvare le parole palindrome in un secondo file.

C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX 512
#define lungMaxParola 20
#define SI 1
#define NO 0

int main(int argc, const char * argv[]) {
    FILE* fp;
    FILE* fp2;
    char nomefile[FILENAME_MAX];
    char nomefile2[FILENAME_MAX];
    char temp[MAX];
    char s[MAX];
    int i,j;
    
    
    
    
    // Richiesta nome file
    puts("Inserire nome file1:");
    gets(nomefile);
    puts("Inserire nome file2:");
    gets(nomefile2);
    
    
    
    
    // Apertura lettura file1 + verifica errore
    if((fp=fopen(nomefile,"r"))==NULL){
        printf("Errore nella lettura di %s", nomefile);
        return EXIT_FAILURE;
    }
    // Apertura scrittura file2 + verifica errore
    if((fp=fopen(nomefile,"w"))==NULL){
        printf("Errore nella lettura di %s", nomefile2);
        return EXIT_FAILURE;
    }
    
    
    
    
    // Ciclo while affinchè fine file
    while(!feof(fp)){
        fscanf(fp, "%s", &s[MAX]);
        palindroma(s[MAX]);
    }
    
    
    
    
    
    fclose(fp);
    fclose(fp2);

    return EXIT_SUCCESS;
}



// Creo la funzione
int palindroma(char s[]){
    
    int i,palindroma;
    long lung;
    char temp[lungMaxParola];
    char parolePalindrome[MAX];


    lung=strlen(s);
    palindroma=SI;
    
    for(i=0;i<lung+1;i++)
        temp[i]=tolower(s[i]);
    
    for(i=0;i<lung/2;i++){
        if(temp[i]!=temp[lung-i])
            palindroma=NO;
        else{
            for(i=0;i<lung;i++){
                parolePalindrome[i]=temp[i];
            }
        }
        
}
    return palindroma;
}
 
Ultima modifica:
Non volevo pubblicarlo più che altro perchè mi sono fermato qui ed ho fatto una confusione pazzesca ahahah, il mio intento era di salvare le parole palindrome in un secondo file.
Aggiustare è meglio che rifare da zero e, soprattutto, aiutare è più piacevole che svolgere i compiti degli altri: per quanto tu possa aver fatto confusione, si posta il codice per far vedere che si hanno buone intenzioni ;) In generale, si inizia da una parte semplice e poi la si complica man mano che la si vede funzionare correttamente, quindi aspetta prima di complicarti la vita con i file.

Prima fai una funzione che capisce se una parola è palindroma. Una parola non è palindroma se il primo carattere è diverso dall'ultimo carattere, se il secondo carattere è diverso dal penultimo, etc... altrimenti è palindroma. Il confronto dev'essere case insensitive.
C:
#include <stdbool.h>
#include <stdio.h>

char lower(char x) {
  return 'A' <= x && x <= 'Z' ? x - 'A' + 'a' : x;
}

bool palindrome(const char *word, size_t length) {
  for (size_t i = 0; i < length / 2; i++)
    if (lower(word[i]) != lower(word[length - i - 1]))
      return false;
  return true;
}

int main() {
  if (palindrome("steve", 5)) printf("steve is palindrome\n");
  if (palindrome("anna", 4)) printf("anna is palindrome\n");
  return 0;
}

Poi impariamo a dividere un testo in parole: una parola è una sequenza di caratteri alfabetica. Questo è un po' più difficile.
C:
#include <stdio.h>

const char *next(const char *s) {
  while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) s++;
  return s;
}

int main() {
  const char *text = "The quick, brown, fox. Jumps 123 over the lazy dog.";

  for (const char *word = text; *word;) {
    const char *end = next(word);
    size_t length = end - word;
    if (length > 0) printf("%.*s\n", length, word);
    word = end + 1;
  }

  return 0;
}

Combiniamo le due cose e abbiamo il nostro programma che stampa le parole palindrome in una stringa.
C:
#include <stdbool.h>
#include <stdio.h>

char lower(char x) { return 'A' <= x && x <= 'Z' ? x - 'A' + 'a' : x; }

bool palindrome(const char *word, size_t length) {
  for (size_t i = 0; i < length / 2; i++)
    if (lower(word[i]) != lower(word[length - i - 1]))
      return false;
  return true;
}

const char *next(const char *s) {
  while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) s++;
  return s;
}

int main() {
  const char *text = "The quick, brown, anna. Jumps 123 over the lazy racecar.";

  for (const char *word = text; *word;) {
    const char *end = next(word);
    size_t length = end - word;
    if (length > 1 && palindrome(word, length))
      printf("%.*s\n", length, word);
    word = end + 1;
  }

  return 0;
}

Ah, ma tu volevi leggere il testo da un file e salvarlo su un altro file. Facciamolo.
C:
#include <stdbool.h>
#include <stdio.h>

char lower(char x) { return 'A' <= x && x <= 'Z' ? x - 'A' + 'a' : x; }

bool palindrome(const char *word, size_t length) {
  for (size_t i = 0; i < length / 2; i++)
    if (lower(word[i]) != lower(word[length - i - 1]))
      return false;
  return true;
}

const char *next(const char *s) {
  while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) s++;
  return s;
}

int main(int argc, const char *argv[]) {
  if (argc != 3) {
    fprintf(stderr, "Usage: ./%s <input file> <output file>\n", argv[0]);
    return 1;
  }

  FILE *in = fopen(argv[1], "r");
  FILE *out = fopen(argv[2], "w");

  if (!in || !out) {
    fprintf(stderr, "Error: Can't read or write on file\n");
    return 1;
  }

  for (char buffer[512]; fgets(buffer, sizeof(buffer), in);) {
    for (const char *word = buffer; *word;) {
      const char *end = next(word);
      size_t length = end - word;
      if (length > 1 && palindrome(word, length))
        fprintf(out, "%.*s\n", length, word);
      word = end + 1;
    }
  }

  fclose(in);
  fclose(out);

  return 0;
}

Fine. Penso che sia tutto abbastanza chiaro, ma se c'è qualcosa che non hai capito chiedi pure.
 
  • Mi piace
  • Incredibile
Reazioni: 0xbro e k35p0n33
Ultima modifica:
Aggiustare è meglio che rifare da zero e, soprattutto, aiutare è più piacevole che svolgere i compiti degli altri: per quanto tu possa aver fatto confusione, si posta il codice per far vedere che si hanno buone intenzioni ;) In generale, si inizia da una parte semplice e poi la si complica man mano che la si vede funzionare correttamente, quindi aspetta prima di complicarti la vita con i file.

Prima fai una funzione che capisce se una parola è palindroma. Una parola non è palindroma se il primo carattere è diverso dall'ultimo carattere, se il secondo carattere è diverso dal penultimo, etc... altrimenti è palindroma. Il confronto dev'essere case insensitive.
C:
#include <stdbool.h>
#include <stdio.h>

char lower(char x) {
  return 'A' <= x && x <= 'Z' ? x - 'A' + 'a' : x;
}

bool palindrome(const char *word, size_t length) {
  for (size_t i = 0; i < length / 2; i++)
    if (lower(word[i]) != lower(word[length - i - 1]))
      return false;
  return true;
}

int main() {
  if (palindrome("steve", 5)) printf("steve is palindrome\n");
  if (palindrome("anna", 4)) printf("anna is palindrome\n");
  return 0;
}

Poi impariamo a dividere un testo in parole: una parola è una sequenza di caratteri alfabetica. Questo è un po' più difficile.
C:
#include <stdio.h>

const char *next(const char *s) {
  while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) s++;
  return s;
}

int main() {
  const char *text = "The quick, brown, fox. Jumps 123 over the lazy dog.";

  for (const char *word = text; *word;) {
    const char *end = next(word);
    size_t length = end - word;
    if (length > 0) printf("%.*s\n", length, word);
    word = end + 1;
  }

  return 0;
}

Combiniamo le due cose e abbiamo il nostro programma che stampa le parole palindrome in una stringa.
C:
#include <stdbool.h>
#include <stdio.h>

char lower(char x) { return 'A' <= x && x <= 'Z' ? x - 'A' + 'a' : x; }

bool palindrome(const char *word, size_t length) {
  for (size_t i = 0; i < length / 2; i++)
    if (lower(word[i]) != lower(word[length - i - 1]))
      return false;
  return true;
}

const char *next(const char *s) {
  while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) s++;
  return s;
}

int main() {
  const char *text = "The quick, brown, anna. Jumps 123 over the lazy racecar.";

  for (const char *word = text; *word;) {
    const char *end = next(word);
    size_t length = end - word;
    if (length > 1 && palindrome(word, length))
      printf("%.*s\n", length, word);
    word = end + 1;
  }

  return 0;
}

Ah, ma tu volevi leggere il testo da un file e salvarlo su un altro file. Facciamolo.
C:
#include <stdbool.h>
#include <stdio.h>

char lower(char x) { return 'A' <= x && x <= 'Z' ? x - 'A' + 'a' : x; }

bool palindrome(const char *word, size_t length) {
  for (size_t i = 0; i < length / 2; i++)
    if (lower(word[i]) != lower(word[length - i - 1]))
      return false;
  return true;
}

const char *next(const char *s) {
  while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z')) s++;
  return s;
}

int main(int argc, const char *argv[]) {
  if (argc != 3) {
    fprintf(stderr, "Usage: ./%s <input file> <output file>\n", argv[0]);
    return 1;
  }

  FILE *in = fopen(argv[1], "r");
  FILE *out = fopen(argv[2], "w");

  if (!in || !out) {
    fprintf(stderr, "Error: Can't read or write on file\n");
    return 1;
  }

  for (char buffer[512]; fgets(buffer, sizeof(buffer), in);) {
    for (const char *word = buffer; *word;) {
      const char *end = next(word);
      size_t length = end - word;
      if (length > 1 && palindrome(word, length))
        fprintf(out, "%.*s\n", length, word);
      word = end + 1;
    }
  }

  fclose(in);
  fclose(out);

  return 0;
}

Fine. Penso che sia tutto abbastanza chiaro, ma se c'è qualcosa che non hai capito chiedi pure.
Seguendo il tuo consiglio ho provato a fare a step, costruendo prima funzione e corpo insomma, ho preso un pò spunto però ho cercato di farlo da solo aiutandomi con quello che sono riuscito senza utilizzare la libreria stdbool dato che non la conosco.
Il mio intento è di pulire la frase dalla punteggiatura. Solo che il codice mi scrive la punteggiatura nel file di out. :rofl:
Quindi voglio scriverci solo le parole palindrome.
Per il momento tuo pubblico il codice:wtf:

C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN_PAROLA 30
#define RIGALENMAX 512
#define PAROLAMAX 30
#define SI 1
#define NO 0


int main(int argc, const char * argv[]) {
    FILE* fpin;
    FILE* fpout;
    int i,j;
    int rovesciate=0;
    char nomeFileIn[FILENAME_MAX];
    char nomeFileOut[FILENAME_MAX];
    char riga[RIGALENMAX];
    char parola[PAROLAMAX];
 
    // Chiedo nome file
    puts("Inserire nome File Input:");
    gets(nomeFileIn);
    puts("Inserire nome File Output:");
    gets(nomeFileOut);
 
 
    // Apertura in Lettura e Scrittura + verifica errore
    if( (fpin=fopen(nomeFileIn, "r")) && (fpout=fopen(nomeFileOut,"w")) == NULL){
        printf("Impossibile leggere/scrivere i file %s-%s", nomeFileIn,nomeFileOut);
        return EXIT_FAILURE;
    }
 
    // Elaborazione
    while(fgets(riga, RIGALENMAX, fpin) != NULL){
        i=0;
        while(riga[i]!= '\n' && riga[i]!='\0'){
            while(riga[i]!='\n' && riga[i]!='\0' && !isalpha(riga[i])){
                fputc(riga[i],fpout);
                i++;
            }
         
         
         
            j=0;
            while(isalpha(riga[i])){
                parola[j]=riga[i];
                i++;
                j++;
            }
            parola[j]='\0';  // Terminiamo la stringa con '\0'


            if(j>0)  // Se è vero allora c'è un carattere nella parola
               {
                for(j=0;j<strlen(parola);j++){
                    parola[j]=parola[strlen(parola)-j]; // Rovescio la parola
                }
             
                fputs(parola, fpout);
                rovesciate++;
            }
        }
     
    }
 
    fclose(fpin);
    fclose(fpout);
 
    return EXIT_SUCCESS;
}

int palindroma(char s[]){
    int i,len,palindroma;
    char appoggio[MAXLEN_PAROLA];
    len=strlen(s);
    palindroma=SI;
    for(i=0;i<len+1;i++){
        appoggio[i]=tolower(s[i]);
 
        for(i=0;i<len/2 && palindroma;i++){
            if(appoggio[i]!=appoggio[len-i-1])
                palindroma=NO;
        }
    }
    return palindroma;
}
 
senza utilizzare la libreria stdbool dato che non la conosco
Quell'header è equivalente al tuo SI e NO, solo che è standard e usa _Bool (un intero che contiene almeno 0 e 1) al posto di int. Se vuoi capire come funziona puoi tranquillamente guardare il codice sorgente, sostanzialmente sono tre righe:
C:
#define bool _Bool
#define true 1
#define false 0
Puoi fare a meno di usarla, ma è di una banalità disarmante e rende il codice più esplicito rispetto ad usare gli int quando vuoi un valore di verità.

Seguendo il tuo consiglio ho provato a fare a step... l'unico problema è che eseguendo il programma, il file in output esce vuoto.
Sei sicuro che quello sia proprio l'unico problema? Proviamo a fare step by step. Il mio primo step controllava se steve e anna sono palindrome. Funzionerà?
C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN_PAROLA 30
#define RIGALENMAX 512
#define PAROLAMAX 30
#define SI 1
#define NO 0

int palindroma(char s[]){
    int i,len,palindroma;
    char appoggio[MAXLEN_PAROLA];
    len=strlen(s);
    palindroma=SI;
    for(i=0;i<len+1;i++){
        appoggio[i]=tolower(s[i]);

        for(i=0;i<len/2 && palindroma;i++){
            if(appoggio[i]!=appoggio[len-i-1])
                palindroma=NO;
        }
    }
    return palindroma;
}

int main() {
    if (palindroma("steve") == SI) {
      printf("steve is palindrome\n");
    } else {
      printf("steve is not palindrome\n");
    }

    return 0;
}

Loop infinito. Inizia a sistemare il primo step e poi pensiamo ai successivi.
 
Ultima modifica:
Quell'header è equivalente al tuo SI e NO, solo che è standard e usa _Bool (un intero che contiene almeno 0 e 1) al posto di int. Se vuoi capire come funziona puoi tranquillamente guardare il codice sorgente, sostanzialmente sono tre righe:
C:
#define bool _Bool
#define true 1
#define false 0
Puoi fare a meno di usarla, ma è di una banalità disarmante e rende il codice più esplicito rispetto ad usare gli int quando vuoi un valore di verità.


Sei sicuro che quello sia proprio l'unico problema? Proviamo a fare step by step. Il mio primo step controllava se steve e anna sono palindrome. Funzionerà?
C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN_PAROLA 30
#define RIGALENMAX 512
#define PAROLAMAX 30
#define SI 1
#define NO 0

int palindroma(char s[]){
    int i,len,palindroma;
    char appoggio[MAXLEN_PAROLA];
    len=strlen(s);
    palindroma=SI;
    for(i=0;i<len+1;i++){
        appoggio[i]=tolower(s[i]);

        for(i=0;i<len/2 && palindroma;i++){
            if(appoggio[i]!=appoggio[len-i-1])
                palindroma=NO;
        }
    }
    return palindroma;
}

int main() {
    if (palindroma("steve") == SI) {
      printf("steve is palindrome\n");
    } else {
      printf("steve is not palindrome\n");
    }

    return 0;
}

Loop infinito. Inizia a sistemare il primo step e poi pensiamo ai successivi.
Ciao Steve, ho provato a rifare il codice in questa maniera:
-Leggo il file in input;
-Lo pulisco dalla punteggiatura riscrivendolo nel file di output;
-Leggo il file di output;
-Leggo se c'è una parola maggiore di 3 char
se è vero allora la passo alla funzione palindroma;
-Se la funzione mi ritorna true allora la parola è palindroma e la stampo a video;

C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define RIGALENMAX 512+1
#define PAROLAMAX 30
#define SI 1
#define NO 0

// Funzione palindroma
int palindroma(char s[]){
    
    int i,palindroma;
    unsigned long len;
    
        char appoggio[PAROLAMAX];
        len=strlen(s);
        palindroma=SI;
        for(i=0;i<len+1;i++)
            appoggio[i]=tolower(s[i]);
        for(i=0;i<len/2 && palindroma;i++)
            if(appoggio[i]!=appoggio[len-i-1])
                palindroma=NO;
    
    return palindroma;
}


int main() {
    FILE* fpin;
    FILE* fpout;
    
    int i,j;
    int palin=1;
    
    char nomeFileIn[FILENAME_MAX];
    char nomeFileOut[FILENAME_MAX];
    char riga[RIGALENMAX];
    char parola[PAROLAMAX];
  
    
    // Chiedo nome file
    puts("Inserire nome File Input:");
    gets(nomeFileIn);
    puts("Inserire nome File Output:");
    gets(nomeFileOut);
    
    
    // Apertura in Lettura e Scrittura + verifica errore
    if( (fpin=fopen(nomeFileIn, "r")) && (fpout=fopen(nomeFileOut,"w")) == NULL){
        printf("Impossibile leggere/scrivere i file [%s] - [%s]", nomeFileIn,nomeFileOut);
        return EXIT_FAILURE;
    }
    
    
    // Elaborazione 1 - pulisco il file dalla punteggiatura
    while (fgets(riga, RIGALENMAX, fpin) != NULL){
        
        i=0;
        while(riga[i]!='\n' && riga[i]!='\0'){
            if(ispunct(riga[i]))
                riga[i]=' ';
            i++;
        }
        fputs(riga, fpout);
        
    }

    // Chiusura dei file
    fclose(fpin);
    fclose(fpout);
    
    
    
    /*__________________________________________________________*/
    
    
    
    // Riapertura del file output in lettura + verifica errore
    if( (fpout=fopen(nomeFileOut,"r")) == NULL){
        printf("Impossibile leggere il file [%s]", nomeFileOut);
        return EXIT_FAILURE;
    }
    
    
    
    // Elaborazione 2 - leggo il file pulito dalla punteggiatura
    
    while( fgets(riga, RIGALENMAX, fpout) != feof ){
        
        i=0;
        j=0;
        while(isalpha(riga[i])){
            parola[j]=riga[i];
            i++;
            j++;
        }
        
        
        if(j>=3){
            if( palindroma(parola) ){
                printf("[%d].%s\n", palin,parola);
                palin++;
            }
        }
        
    
    }

Scusa il ritardo ma ho avuto da fare con il lavoro.
 
Non hai descritto i problemi del codice che hai riportato, ma leggendolo noto un paio di stranzze:

C:
    // Apertura in Lettura e Scrittura + verifica errore
    if( (fpin=fopen(nomeFileIn, "r")) && (fpout=fopen(nomeFileOut,"w")) == NULL){
        printf("Impossibile leggere/scrivere i file [%s] - [%s]", nomeFileIn,nomeFileOut);
        return EXIT_FAILURE;
    }
Nel codice riportato qui sopra, stai utilizzando il valore di fpin come un booleano, che viene combinato all'espressione "è fpout uguale a 0" tramite una AND, però non va bene così, consideriamo il seguente scenario:

L'apertura di fpin fallisce, fopen ritorna false, quindi abbiamo if (false && (fpout...)), anche se l'apertura di fpout avesse successo, siccome li stai combinando con una AND, e sapendo che uno dei parametri di quella AND è un false, senza neanche bisogno di valutare la prossima espressione sapremo a priori che il contenuto dell'if non verrà eseguito.

Quindi, in quale scenario si ha che il contenuto dell'if viene eseguito? lo si ha quando:

l'apertura di fpin ha successo, quindi verrà valutato in true, e l'aperura di fpout fallisce, perchè: fopen imposterà la variabile fpout a NULL, verrà comparata con NULL, il che risulterà in true, ora abbiamo che tutte le condizioni date in input alla AND sono vere, il che risulterà in un true finale.

Un piccolo consiglio, sia io che i miei amici quando programmiamo, nelle condizioni non utilizziamo mai gli operatori di assegnazione, perchè rendono il codice meno leggibile, quello che hai scritto tu io li scrivere in questo modo:
C:
    // Apertura in Lettura e Scrittura + verifica errore
    fpin = fopen(nomeFileIn, "r");
    fpout = fopen(nomeFileOut, "w");
    if (fpin && fpout == NULL)
    {
        printf("Impossibile leggere/scrivere i file [%s] - [%s]", nomeFileIn, nomeFileOut);
        return EXIT_FAILURE;
    }
Ora la condizione nell'if è molto più comprensibile, però è sempre da riconsiderare siccome non funziona come vuoi.

Poi, c'è questo:
C:
// Elaborazione 2 - leggo il file pulito dalla punteggiatura
    
    while( fgets(riga, RIGALENMAX, fpout) != feof ){
        
        i=0;
        j=0;
        while(isalpha(riga[i])){
            parola[j]=riga[i];
            i++;
            j++;
        }
La funzione fgets restituisce un puntatore alla stringa letta, ovvero, un puntatore a riga, e poi lo stai comparando a... a cosa? Lo stai comparando al puntatore della funzione feof, qui c'è un errore di una certa importanza, nel caso non lo sapessi, anche le funzioni come le variabili hanno i loro puntatori, però, i puntatori alle variabili solitamente puntano a regioni di memoria con le caratteristiche di lettura e scrittura, mentre i puntatori a funzione su regioni di con le caratteristiche di lettura ed esecuzione, spero tu abbia capito un pochettino cosa stai facendo.

Per essere sicuro di aver letto tutto il possibile dal file pui fare 2 cose:

1) Dando un'occhiata alla documentazione della Microsft sulla funzione fgets:

Valore restituito​


Ognuna di queste funzioni restituisce str. NULL viene restituito per indicare una condizione di errore o di fine file. Utilizzare feof o ferror per determinare se si è verificato un errore. Se str o stream è un puntatore null o numChars è minore o uguale a zero, questa funzione richiama il gestore dei parametri non validi, come descritto in Convalida dei parametri. Se l'esecuzione può continuare, errno viene impostato su EINVAL e la funzione restituisce NULL.

Per _doserrnoaltre informazioni errnosu _sys_errlistquesti _sys_nerr e altri codici di errore, vedere , , e .
Sappiamo che la funzione fgets ritorna NULL quando per qualsiasi errore non riesce più a leggere il contenuto del file.

2) Sempre nella documentazione sopra citata, possiamo utilizzare la funzione feof o ferror, anche se nel tuo caso non ne hai bisogno dato che il valore di ritorno di fgets è sufficiente.

Potrebbero esserci anche altri errori nel codice che hai riportato, ma mi sono suffermato su questi essendo facilmente individuabili, ed ovviamente l'ho fatto apposta a non scriverti direttamente la soluzione ma questo messaggio al quanto corposo, del resto solo anche facili da risolvere ;)