/* campionamento di un file 
 * carica il campione su array
 * versione "senza reimbussolamento", ovvero un record puo' essere preso piu' volte
 * Il caricamento del campione su array  giustificato soltanto se:
 * 1. il campione che si vuole estrarre non e' troppo grande
 * 2. l'elaborazione che si intende svolgere sul campione richiede pi accessi 
 *    a ogni dato
 *
 * - acquisisce nome e apre file da campionare in modalita lettura binaria
 * - acquisisce origine sequenza random
 * - si posiziona in coda al file per calcolarne la dimensione N
 * - input della frazione da campionare n e verifica di capienza dell'array
 * - ripeti volte la generazione di un numero fra 0 e N 
 *     quando genera un numero di record non ancora letto  legge 
 *      il record corrispondente nell'array e tiene traccia della lettura
 * - elabora il campione
 */

#define dimCAMPIONE 50000
#define DIMBUF 80
#include <stdio.h>
#include <stdlib.h>
#include "random.h" /* procedura esterna per la generazione di numeri random */

typedef long r; /* a titolo di esempio si considera un file di interi lunghi */

void elabora(r *campione,long n);
long cercaLong(long target, long *a, long n);

int main(){
 r camp[dimCAMPIONE]; /* array di campioni */
    long estratti[dimCAMPIONE]; // array estratti
 long N,    /* numero di r nel file */
      n,    /* dimensione campione corrente */
      i,    /* indice */
      seme, /* seme per rand */
      t,    /* posizione nell'array di estratti */
      p;    /* posizione di campionamento corrente */
 float f;   /* frazione da estrarre */
 FILE *fd;
 char nomeF[64], linea[DIMBUF];
 /* intro e' un array di 4 stringhe di 64 caratteri ciascuna, utilizzato per mandare
  * all'utente un messaggio introduttivo
  */
 char intro[4][64]={"Programma per il campionamento di record da un file\n",
                " Richiede il nome del file, il seme della sequenza random e\n",
                " la frazione di record da campionare.\n",
                " Versione con caricamento del campione in memoria\n"};
 
 for (i=0;i<4;i++) /* visualizza intro */
  printf("%s",intro[i]);
 /* inizializzazione */
 printf("\nNome file da campionare\n");
 fgets(linea,DIMBUF,stdin);
 sscanf(linea,"%s",nomeF);
 if ((fd=fopen(nomeF,"rb"))==NULL){
  printf("File %s non trovato\n",nomeF);
  return 1;
 }
 printf("Inserire origine sequenza random \n");
 fgets(linea,DIMBUF,stdin);
 sscanf(linea,"%ld",&seme);
 srand(seme);
 fseek(fd,0,SEEK_END);
 N=ftell(fd)/sizeof(r);
 printf("Inserire frazione da campionare (da 0 a 0.5) \n");
 fgets(linea,DIMBUF,stdin);
 sscanf(linea,"%f",&f);
 n=f*N;
 printf("Nel file ci sono %ld records, ne campiono %ld\n",N,n);
 if (n>dimCAMPIONE){
  printf("Campione troppo grande\n");
  return 1;
 }
 /* fine inizializzazione */
 i=0;
 while (i<n){         /* ripete n volte il campionamento */
     p=randN(N);                        /* genera la posizione del record da campionare */
     t = cercaLong(p,estratti,i);
     if (t==i){ // p non era stato estratto -> aggiorno estratti[i]
         estratti[i++]=p; // e incremento i
         fseek(fd,p*sizeof(r),SEEK_SET);         /* si posiziona per la lettura */
         fread(&(camp[i]),sizeof(r),1,fd);        /* legge il record nell'array */
     }
     // se p era gi stato estratto non fa nulla e al prossimo giro 
     // si tenta una nuova estrazione
 } // quando si esce il campione e' popolato
 fclose(fd);
 elabora(camp,n); /* elabora l'array con il campione */
 return 0;
} /* fine main */

/*
 * elabora: esempio di elaborazione dell'array con il campione
 */
void elabora(r *campione, long n){ /* a titolo di esempio si calcola la media */
 float media;
 long i;
 media=campione[0];
 
 for (i=1;i<n;i++)
  media = (media*(i)+campione[i])/(i+1);
  
 printf("valore medio %f\n",media);
} /* fine elabora */

/*
 * cercaLong: cerca un target in un array di n long
 * se non trovato restituisce n, altrimenti restituisce la posizione trovata 
 * a partire da 0 fino a n-1
 */
long cercaLong(long target, long *a, long n){
    long i=0;
    while (i<n)
        if (target==a[i])
            return i;
        else
            i++;
    return i;
} // cercaLong