/*----------------------------4.1.c--------------------------*/
/* Pokazivaci - osnovni pojam */
#include <stdio.h>
main() {
  int x = 3;
  
  /* Adresu promenjive x zapamticemo u novoj promeljivoj.
	 Nova promenljiva je tipa pokazivaca na int (int*) */
  int* px; 
  /* isto je i int *px; */
  /* int* px, py; -> tumaci se kao int *px, py; */
        

  printf("Adresa promenljive x je : %p\n", &x);
  printf("Vrednost promenljive x je : %d\n", x);
  
     
  px = &x;
  printf("Vrednost promenljive px je (tj. px) : %p\n", px);
  printf("Vrednost promenljive na koju ukazuje px (tj. *px) je : %d\n", *px);
  
  /* Menjamo vrednost promenljive na koju ukazuje px */
  *px = 6;
  printf("Vrednost promenljive na koju ukazuje px (tj. *px) je : %d\n", *px);
  
  /* Posto px sadrzi adresu promenljive x, ona ukazuje na x tako da je 
	 posredno promenjena i vrednost promenljive x */
  printf("Vrednost promenljive x je : %d\n", x);
  

}


/*----------------------------4.2a.c--------------------------*/
/* Demonstrira prenos argumenata preko pokazivaca - swap */
#include <stdio.h>

/* Pogresna verzija funkcije swap. Zbog prenosa po vrednosti, funkcija 
   razmenjuje kopije promenljivih iz main-a, a ne samih promenljivih */
void swap_wrong(int x, int y)
{
	int tmp;
	
	printf("swap_wrong: ");
	printf("Funkcija menja vrednosti promenljivim na adresama : \n");
	printf("x : %p\n", &x);
	printf("y : %p\n", &y);
	
	
	tmp = x;
	x = y;
	y = tmp;
}

/* Resenje je prenos argumenata preko pokazivaca */
void swap(int* px, int* py)
{
	int tmp;
	
	printf("swap : Funkcija menja vrednosti promenljivim na adresama : \n");
	printf("px = %p\n", px);
	printf("py = %p\n", py);
	
	tmp = *px;
	*px = *py;
	*py = tmp;
}

main()
{
	int x = 3, y = 5;
	printf("Adresa promenljive x je %p\n", &x);
	printf("Vrednost promenljive x je %d\n", x);
	printf("Adresa promenljive y je %p\n", &y);
	printf("Vrednost promenljive y je %d\n", y);	
	
	/* Pokusavamo zamenu koristeci pogresnu verziju funkcije */
	swap_wrong(x, y);
	
	printf("Posle swap_wrong:\n");
	printf("Vrednost promenljive x je %d\n", x);
	printf("Vrednost promenljive y je %d\n", y);
	
	/* Vrsimo ispravnu zamenu. Funkciji swap saljemo adrese promenljvih 
	   x i y, a ne njihove vrednosti */
	swap(&x, &y);
	
	printf("Posle swap:\n");
	printf("Vrednost promenljive x je %d\n", x);
	printf("Vrednost promenljive y je %d\n", y);
}


/*----------------------------4.2b.c--------------------------*/
/* Demonstrira vise povratnih vrednosti funkcije koristeci prenos preko 
   pokazivaca */

/* Funkcija istovremeno vraca dve vrednosti - kolicnik i ostatak dva
   data broja.  Ovo se postize tako sto se funkciji predaju vrednosti
   dva broja (x i y) koji se dele i adrese dve promenljive na koje ce
   se smestiti rezultati */
void div_and_mod(int x, int y, int* div, int* mod) {
  printf("Kolicnik postavljam na adresu : %p\n", div);
  printf("Ostatak postavljam na adresu : %p\n", mod);
  *div = x / y;
  *mod = x % y;
}

int main() {
  int div, mod;
  printf("Adresa promenljive div je %p\n", &div);
  printf("Adresa promenljive mod je %p\n", &mod);

  /* Pozivamo funkciju tako sto joj saljemo vrednosti dva broja (5 i 2)
	 i adrese promenljvih div i mod na koje ce se postaviti rezultati */
  div_and_mod(5, 2, &div, &mod);
	
  printf("Vrednost promenljive div je %d\n", div);
  printf("Vrednost promenljive mod je %d\n", mod);
	
  return 0;
}


/*----------------------------4.3.c--------------------------*/
/* Demonstrira pokazivacku aritmetiku */

#include <stdio.h>

int main() {
  char s[] = "abcde";
  int t[] = {1, 2, 3, 4, 5};
	
  /* Inicijalizujmo pokazivace ps i pt na pocetke nizova s i t */
  char* ps = &s[0];
  int* pt =  &t[0];
	
  /* Pokazivace je moguce sabirati sa celim brojevima i 
	 od njih je moguce oduzimati cele brojeve*/
  /* Ispisimo vrednosti pokazivaca */
  printf("ps = %p\n", ps);
  printf("ps+1 = %p\n", ps+1);
  printf("ps+2 = %p\n", ps+2);
  printf("ps-1 = %p\n", ps-1);
  printf("ps-2 = %p\n", ps-2);

	
  /* Prilikom sabiranja pokazivaca i celih brojeva, 
	 dodaje se velicina odgovarajuceg tipa. */
  printf("pt = %p\n", pt);
  printf("pt+1 = %p\n", pt+1);
  printf("pt+2 = %p\n", pt+2);
  printf("pt-1 = %p\n", pt-1);
  printf("pt-2 = %p\n", pt-2);
	
  /* Na pokazivace je moguce primenjivati i operatore ++ i -- */
  for (ps = s; *ps; ps++)
	putchar(*ps);
  putchar('\n');
	
  /* Slicno, dva pokazivaca istog tipa se mogu oduzimati. Prilikom sracunavanja
	 rezultata, uzima se u obzir velicina tipa. */
  ps = &s[3];
  printf("s = %p\n", s);
  printf("ps = %p\n", ps);
  printf("ps - s = %d\n", ps - s);
  pt = &t[3];
  printf("t = %p\n", t);
  printf("pt = %p\n", pt);
  printf("pt - t = %d\n", pt - t);	

  return 0;
}


/*----------------------------4.4.c--------------------------*/
/* Demonstrira vezu izmedju pokazivaca i nizova */

#include <stdio.h>

void print_array(int* pa, int n);

int main() {
  int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  int num_of_elements = sizeof(a)/sizeof(int);
  int* pa;

  /* Niz je isto sto i adresa njegovog prvog elementa */
  printf("Niz a : %p\n", a);
  printf("Adresa prvog elementa niza a (&a[0]) : %p\n", &a[0]);
	
  /* Moguce je dodeliti niz pokazivacu odgovarajuceg tipa */
  pa = a;
	
  printf("Pokazivac pa ukazuje na adresu : %p\n", pa);
	
  /* Nizu nije moguce dodeljivati vrednost
	 (nizove mozemo smatrati KONSTANTNIM pokazivacima na prvi element) */
  /* a = pa; */

  /* Niz je moguce koristiti kao pokazivac tj. vaze pravila pokazivacke aritmetike */
  printf("a + 3 = %p\n", a + 3);

  /* Vazi da je 
	 a + i =  &a[i]  
	 odnosno 
	 *(a + i) = a[i]
  */
  printf("&a[3] = %p\n", &a[3]);

  /* Identiteti
	 a + i =  &a[i]  
	 odnosno 
	 *(a + i) = a[i]
	 vazi i za pokazivace i za nizove 
  */
	
  /* Pokazivace je na osnovu prethodnog moguce indeksirati kao nizove */
  printf("pa[5] = %d\n", pa[5]);
  printf("*(pa + 5) = %d\n", *(pa+5));
	
	
  /* Medjutim, sizeof(pa) je samo velicina pokazivaca, a ne niza */
  printf("sizeof(a) = %d\n", sizeof(a));
  printf("sizeof(pa) = %d\n", sizeof(pa));
	
  /* Pozivamo funkciju za stampanje niza i saljemo joj niz */
  print_array(a, num_of_elements);
  /* Pozivamo funkciju za stampanje niza i saljemo joj pokazivac na pocetak niza */
  print_array(pa, num_of_elements);

  return 0;
}

/* Proslednjivanje niza u funkciju 
   	void print_array(int pa[], int n);
   je ekvivalentno prosledjivanju pokazivaca u funkciju 
   	void print_array(int* pa, int n);
   Izmedju ovih konstrukcija nema nikakve razlike.
*/
void print_array(int* pa, int n)
{
	int i;
	for (i = 0; i<n; i++)
		printf("%d ", pa[i]);
	putchar('\n');
}

/*----------------------------4.5a.c--------------------------*/
/* strlen, strcpy, strcat, strcmp, strchr, strstr - 
   verzije sa nizovima */
/* Vezbe radi, implementirane su funkcije biblioteke string.h */
#include <stdio.h>

/* Izracunava duzinu stringa */
int string_length(char s[])
{
	int i;
	for (i = 0; s[i]; i++)
		;
	return i;
}


/* Kopira string src u string dest. 
  Pretpostavlja da u dest ima dovoljno prostora. */
void string_copy(char dest[], char src[])
{
	/* Kopira karakter po karakter, sve dok nije iskopiran karakter '\0' */
	int i;
	for (i = 0; (dest[i]=src[i]) != '\0'; i++)
		;

	/* Uslov != '\0' se, naravno, moze izostaviti :
	
	for (i = 0; dest[i]=src[i]; i++)
		;
	*/
	
}

/* Nadovezuje string t na kraj stringa s. 
   Pretpostavlja da u s ima dovoljno prostora. */
void string_concatenate(char s[], char t[])
{
	int i, j;
	/* Pronalazimo kraj stringa s */
	for (i = 0; s[i]; i++)
		;
	
	/* Vrsi se kopiranje, slicno funkciji string_copy */
	for (j = 0; s[i] = t[j]; j++, i++)
		;
}

/* Vrsi leksikografsko poredjenje dva stringa. 
    Vraca :
        0 - ukoliko su stringovi jednaki
        <0 - ukoliko je s leksikografski ispred t
        >0 - ukoliko je s leksikografski iza t
*/
int string_compare(char s[], char t[])
{
	/* Petlja tece sve dok ne naidjemo na prvi razliciti karakter */
	int i;
	for (i = 0; s[i]==t[i]; i++)
		if (s[i] == '\0') /* Naisli smo na kraj oba stringa, 
				     a nismo nasli razliku */
			return 0;

	/* s[i] i t[i] su prvi karakteri u kojima se niske razlikuju. 
	   Na osnovu njihovog odnosa, odredjuje se odnos stringova */
	return s[i] - t[i];
}

/* Pronalazi prvu poziciju karaktera c u stringu s, odnosno -1 
   ukoliko s ne sadrzi c */
int string_char(char s[], char c)
{
	int i;
	for (i = 0; s[i]; i++)
		if (s[i] == c)
			return i;
		/* nikako 
		else 
			return -1;
		*/			
	/* Nije nadjeno */				
	return -1;
}

/* Pronalazi poslednju poziciju karaktera c u stringu s, odnosno -1 
   ukoliko s ne sadrzi c */
int string_last_char(char s[], char c)
{
	/* Pronalazimo kraj stringa s */
	int i;
	for (i = 0; s[i]; i++)
		;
	
	/* Krecemo od kraja i trazimo c unazad */
	for (i--; i>=0; i--)
		if (s[i] == c)
			return i;
			
	/* Nije nadjeno */				
	return -1;
	
	/*
	Koristeci string_length :
		
	for (i = string_length(s) - 1; i>0; i--)
		if (s[i] == c)
			return i;
			
	return -1;	 
	*/
}

/* Proverava da li string str sadrzi string sub.
   Vraca poziciju na kojoj sub pocinje, odnosno -1 ukoliko ga nema 
*/
int string_string(char str[], char sub[])
{
	int i, j;
	/* Proveravamo da li sub pocinje na svakoj poziciji i */
	for (i = 0; str[i]; i++)
		/* Poredimo sub sa str pocevsi od poziciji i 
		   sve dok ne naidjemo na razliku */
		for (j = 0; str[i+j] == sub[j]; j++)
			/* Nismo naisli na razliku a ispitali smo 
			   sve karaktere niske sub */
			if (sub[j+1]=='\0') 
				return i;
	/* Nije nadjeno */				
	return -1;
}

main()
{
	char s[100];
	char t[] = "Zdravo";
	char u[] = " svima";
	
	string_copy(s, t);
	printf("%s\n", s);
	
	string_concatenate(s, u);
	printf("%s\n", s);
	
	printf("%d\n",string_char("racunari", 'n'));
	printf("%d\n",string_last_char("racunari", 'a'));
	
	printf("%d\n",string_string("racunari", "rac"));
	printf("%d\n",string_string("racunari", "ari"));
	printf("%d\n",string_string("racunari", "cun"));
	printf("%d\n",string_string("racunari", "cna"));
	
	
}

/*----------------------------4.5b.c--------------------------*/
/* strlen, strcpy, strcat, strcmp, strchr, strstr - 
   verzije sa pokazivacima */
/* Vezbe radi, implementirane su funkcije biblioteke string.h */
#include <stdio.h>
#include <stdlib.h> /* Zbog NULL */

/* Izracunava duzinu stringa */
int string_length(char *s)
{
	char* t;
	for (t = s; *t; t++)
		;
	return t - s;
}


/* Kopira string src u string dest. Pretpostavlja da u dest ima dovoljno prostora. */
void string_copy(char *dest, char *src)
{
	/* Kopira karakter po karakter, sve dok nije iskopiran karakter '\0' */
	while(*dest++ = *src++)
		;
	
}

/* Nadovezuje string t na kraj stringa s. Pretpostavlja da u s ima dovoljno prostora. */
void string_concatenate(char *s, char *t)
{
	/* Pronalazimo kraj stringa s */
	while (*s)
		s++;

	/* Vrsi se kopiranje, slicno funkciji string_copy */
	while (*s++ = *t++)
		;
}

/* Vrsi leksikografsko poredjenje dva stringa. 
    Vraca :
        0 - ukoliko su stringovi jednaki
        <0 - ukoliko je s leksikografski ispred t
        >0 - ukoliko je s leksikografski iza t
*/
int string_compare(char *s, char *t)
{
	/* Petlja tece sve dok ne naidjemo na prvi razliciti karakter */
	for (; *s == *t; s++, t++)
		if (*s == '\0') /* Naisli smo na kraj oba stringa, a nismo nasli razliku */
			return 0;


	/* *s i *t su prvi karakteri u kojima se niske razlikuju. 
	   Na osnovu njihovog odnosa, odredjuje se odnos stringova */
	return *s - *t;
}

/* Pronalazi prvu poziciju karaktera c u stringu s, i vraca pokazivac na nju, 
   odnosno NULL ukoliko s ne sadrzi c */
char* string_char(char *s, char c)
{
	for (; *s; s++)
		if (*s == c)
			return s;
			
	/* Nije nadjeno */				
	return NULL;
}

/* Pronalazi poslednju poziciju karaktera c u stringu s, i vraca pokazivac na nju,
   odnosno NULL ukoliko s ne sadrzi c */
char* string_last_char(char *s, char c)
{
	char *t = s;
	/* Pronalazimo kraj stringa s */
	while (*t++)
		;
	
	/* Krecemo od kraja i trazimo c unazad */
	for (t--; t >= s; t--)
		if (*t == c)
			return t;
			
	/* Nije nadjeno */				
	return NULL;
	
}

/* Proverava da li string str sadrzi string sub.
   Vraca poziciju na kojoj sub pocinje, odnosno -1 ukoliko ga nema 
*/
char* string_string(char *str, char *sub)
{
	char *s, *t;
	/* Proveravamo da li sub pocinje na svakoj poziciji i */
	for (; *str; str++)
		/* Poredimo sub sa str pocevsi od poziciji i sve dok ne naidjemo na razliku */
		for (s = str, t = sub; *s == *t; s++, t++)
			/* Nismo naisli na razliku a ispitali smo sve karaktere niske sub */
			if (*(t+1) == '\0') 
				return str;
	/* Nije nadjeno */				
	return NULL;
}

int main() {
	char s[100];
	char t[] = "Zdravo";
	char u[] = " svima";
	char r[] = "racunari";
	
	string_copy(s, t);
	printf("%s\n", s);
	
	string_concatenate(s, u);
	printf("%s\n", s);
	
	printf("%d\n",string_char(r, 'n') - r);
	printf("%d\n",string_last_char(r, 'a') - r);
	
	printf("%d\n",string_string(r, "rac") - r);
	printf("%d\n",string_string(r, "ari") - r);
	printf("%d\n",string_string(r, "cun") - r);
	printf("%p\n",string_string(r, "cna"));

	return 0;
}

/*----------------------------4.6.c--------------------------*/
/* Demonstrira pokazivace na funkcije */
#include <stdio.h>

int kvadrat(int n) {   
  return n*n;
}

int kub(int n) {
  return n*n*n;
}

int parni_broj(int n) {
  return 2*n;
}


/* Funkcija izracunava sumu \sum_{i=1}^n f(i), gde je f data funkcija 
   prvi argument funkcije sumiraj je 
		int (*f) (int) 
   sto je pokazivac na funkciju koja ima jedan argument tipa int i 
   vraca vrednost tipa int
*/
int sumiraj(int (*f) (int), int n) {
  int i, suma=0;
  for (i=1; i<=n; i++)
	suma += (*f)(i);

  return suma;
}

int main() {
   printf("Suma kvadrata brojeva od jedan do 3 je %d\n",
          sumiraj(kvadrat,3));
   printf("Suma kubova brojeva od jedan do 3 je %d\n",
          sumiraj(kub,3));
   printf("Suma prvih pet parnih brojeva je %d\n",
          sumiraj(parni_broj,5));

   return 0;
}