/*----------------------------1.1.c--------------------------*/
/* Celobrojni tipovi podataka */

/* Pogledati na primer: http://home.att.net/~jackklein/c/inttypes.html */

// C99 kod - npr. deklaracije nisu iskljucivo na pocetku bloka, komentari //, koristi se long long 

#include <stdio.h>
#include <limits.h>

int main() {
  /*
    Osnovni tipovi su odredjeni duzinom i nacinom zapisa:
    ---------------------------------------------------------------
                    | oznaceni (signed)    | neoznaceni (unsigned)
    ---------------------------------------------------------------
    karakteri       | signed char          | unsigned char
    (char)          |                      |
    ---------------------------------------------------------------
    kratki          | signed short int     | unsigned short int
    (short int)     |                      |
    ---------------------------------------------------------------
    dugacki         | signed long int      | unsigned long int
    (long int)      |                      |
    ---------------------------------------------------------------
    veoma dugacki   | signed long long int | unsigned long long int
    (long long int) |                      |
    - samo C99 -    |                      |
    ---------------------------------------------------------------


    Uobicajeni zapisi (vazi i na x86 + gcc platformi na kojoj radimo):
    ------------------------------------------------------------------
                    | oznaceni (signed)        | neoznaceni (unsigned)
    ------------------------------------------------------------------
    karakteri       | 1B = 8b                  | 1B = 8b
                    | [-2^7, 2^7-1] =          | [0, 2^8-1] = 
    (char)          | [-128, 127]              | [0, 255]
    ------------------------------------------------------------------
    kratki          | 2B = 16b                 | 2B = 16b
    (short int)     | [-32K, 32K-1] =          | [0, 64K-1] = 
                    | [-2^15, 2^15-1] =        | [0, 2^16-1] = 
                    | [-32768, 32767]          | [0, 65535]
    ------------------------------------------------------------------
    dugacki         | 4B = 32b                 | 4B = 32b
    (long int)      | [-2G, 2G-1] =            | [0, 4G-1] = 
                    | [-2^31, 2^31-1] =        | [0, 2^32-1] = 
                    | [-2147483648,2147483647] | [0, 4294967295]
    ------------------------------------------------------------------
    veoma dugacki   | 8B = 64b                 | 8B = 64b
    (long long int) | [-2^63, 2^63-1] =        | [0, 2^64-1] = 
                    | [-9.2*10^18, 9.2*10^18]  | [0, 1.84*10^19]
    ------------------------------------------------------------------

    Moguci su i izvedeni (skraceni) tipovi:

    char                 - podudara se sa signed char ili sa unsigned char - 
    nedefinisano standardom
                
    int se moze izostaviti:
    short                - isto sto i short int
    signed short         - isto sto i signed short int
    unsigned short       - isto sto i unsigned short int
    long                 - isto sto i long int
    signed long          - isto sto i signed long int
    unsigned long        - isto sto i unsigned long int
    long long            - isto sto i long long int
    signed long long     - isto sto i singed long long int
    unsigned long long   - isto sto i unsinged long long int
    signed               - isto sto i signed int
    unsinged             - isto sto i unsigned int

    signed/unsigned se moze izostaviti za int i tada se podrazumeva signed:
    int                  - isto sto i signed int
    short int            - isto sto i signed short int
    long int             - isto sto i signed long int
    long long int        - isto sto i signed long long int
 
    short/long/long long se moze izostaviti i tada se podrazumeva nativna duzina
    za masinu (obicno long):
    signed int           - obicno se podudara sa signed long int
    unsigned int         - obicno se podudara sa unsigned long int

    Iako se char cesto podudara sa signed char, to su razliciti tipovi podataka.
    Slicno vazi i za tipove int i long int.
  */

  /* Formati za funkciju printf koji se koriste u nastavku koda: 
     %d - dekadni oznaceni broj, %u - dekadni neoznaceni broj,  
     %x - heksadekadni neoznaceni broj, %c - karakter
     %l - dugacki, %ll veoma dugacki
  */
  
  /* Rasponi su definisani simbolickim konstantama u <limits.h> (???_MIN, ???_MAX) */

  signed char sc_min = SCHAR_MIN;
  signed char sc_max = SCHAR_MAX;
  printf("signed char: [%d, %d]\n", sc_min, sc_max);
  unsigned char uc_min = 0;
  unsigned char uc_max = UCHAR_MAX;
  printf("unsigned char: [%d, %d]\n", uc_min, uc_max);
  char c_min = CHAR_MIN;
  char c_max = CHAR_MAX;
  printf("char: [%d, %d]\n", c_min, c_max);
  printf("Number of bits in a character: %d\n", CHAR_BIT);
  printf("Size of character is %d byte\n", sizeof(char));

  int i_min = INT_MIN;
  int i_max = INT_MAX;
  printf("int: [%d, %d]\n", i_min, i_max);
  printf("Size of int is %d bytes\n", sizeof(int));

  signed short int ssi_min = SHRT_MIN;
  signed short int ssi_max = SHRT_MAX;
  printf("signed short int: [%d, %d]\n", ssi_min, ssi_max);
  unsigned short int usi_min = 0;
  unsigned short int usi_max = USHRT_MAX;
  printf("unsigned short int: [%u, %u]\n", usi_min, usi_max);
  printf("Size of short is %d bytes\n", sizeof(short));

  signed long int sli_min = LONG_MIN;
  signed long int sli_max = LONG_MAX;
  printf("signed long int: [%ld, %ld]\n", sli_min, sli_max);
  unsigned long int uli_min = 0;
  unsigned long int uli_max = ULONG_MAX;
  printf("unsigned long int: [%lu, %lu]\n", uli_min, uli_max);
  printf("Size of long is %d bytes\n", sizeof(long));

  signed long long int slli_min = LLONG_MIN;
  signed long long int slli_max = LLONG_MAX;
  printf("signed long long int: [%lld, %lld]\n", slli_min, slli_max);
  unsigned long long int ulli_min = 0;
  unsigned long long int ulli_max = ULLONG_MAX;
  printf("unsigned long long int: [%llu, %llu]\n", ulli_min, ulli_max);
  printf("Size of long long is %d bytes\n", sizeof(long long));

  
  /* Oznaceni brojevi. Obicno se koristi samo int. */
  int i1 = 10;          /* Dekadni celobrojni literal */
  printf("int i1 = 10; --> i1 = %d\n", i1);
  int i2 = 010;         /* Oktalni celobrojni literal */
  printf("int i2 = 010; --> i2 = %d\n", i2);
  int i3 = 0x10;        /* Heksadekadni celobrojni literal */
  printf("int i3 = 0x10; --> i3 = %d\n", i3);
  int i4 = -234;        /* Negativna vrednost */
  printf("int i4 = -234; %%d --> i4 = %d\n", i4);
  printf("int i4 = -234; %%u --> i4 = %u\n", i4);   /* Pogresno navedeno %u umesto %d */
  int i5 = INT_MAX + 1; /* Vece od najvece moguce vrednosti - prekoracenje */
  printf("int i5 = INT_MAX + 1; --> i5 = %d\n", i5);
  int i6 = 14+5*7;      /* Promenljiva se moze inicijalizovati i
                           konstantnim izrazom */
  printf("int i6 = 14+5*7; --> i6 = %d\n", i6);

  /* Neoznaceni brojevi. Obicno se koristi samo unsigned. */
  unsigned u1 = 10u;         /* Dekadni neoznaceni celobrojni literal */
  printf("unsigned u1 = 10u; --> u1 = %u\n", u1);
  unsigned u2 = 10U;         /* u ili U, svejedno je.*/
  printf("unsigned u2 = 10U; --> u2 = %u\n", u2);
  unsigned u3 = 10;         /* nekad moze i da se ne navede */
  printf("unsigned u3 = 10; --> u3 = %u\n", u3);
  unsigned u4 = 0xffffffffu; /* Heksadekadni neoznaceni celobrojni literal */
  printf("unsigned u4 = 0xffffffffu; %%u --> u3 = %u\n", u4);
  printf("unsigned u4 = 0xffffffffu; %%x --> u3 = %x\n", u4);
  printf("unsigned u4 = 0xffffffffu; %%d --> u3 = %d\n", u4);
  unsigned long u4 = 123456789ul; /* l (ili L) oznacava eksplicitno da
                                     je u pitanju dugacki broj*/
  printf("unsigned long u4 = 123456789ul; --> u4 = %lu\n", u4);

  /* Neoznaceni kratki brojevi - primeri prekoracenja i konverzija */
  unsigned short us1 = 1234u;    /* U redu */
  unsigned short us2 = 123456u;  /* Preveliki literal - ne moze da se smesti u unsigned short */
  unsigned short us3 = -1;      /* Negativan literal - ne moze da se smesti u unsigned short */
  unsigned short us4 = u1;      /* U redu - konverzija bez gubitka */
  unsigned short us5 = u3;      /* Konverzija sa gubitkom informacije */

  printf("unsigned short us1 = 1234; --> us1 = %u\n", us1);
  printf("unsigned short us2 = 123456; --> us2 = %u\n", us2);
  printf("unsigned short us3 = -1; --> us3 = %u\n", us3);
  printf("unsigned short us4 = u1; --> us4 = %u\n", us4);
  printf("unsigned short us5 = u3; --> us5 = %u\n", us5);

  /* Karakteri - prikaz sa %d i %c */
  char c1 = 65;
  printf("char c1 = 65; %%d --> c1 = %d\n", c1);
  printf("char c1 = 65; %%c --> c1 = %c\n", c1);

  char c2 = 'A';
  printf("char c2 = 'A'; %%d --> c1 = %d\n", c2);
  printf("char c2 = 'A'; %%c --> c1 = %c\n", c2);

  char c3 = '\x41';
  printf("char c3 = '\\x41'; %%d --> c1 = %d\n", c3);
  printf("char c3 = '\\x41'; %%c --> c1 = %c\n", c3);

  // Nema nikakve razlike izmedju c1, c2 i c3

  /* Specijalne sekvence: \n, \\, \t, ... */
  char c4 = '\n';
  printf("char c4 = '\\n'; %%d --> c4 = %d\n", c4);
  printf("char c4 = '\\n'; %%c --> c4 = %c\n", c4);

  char c5 = '\\';
  printf("char c5 = '\\\\'; %%d --> c4 = %d\n", c5);
  printf("char c5 = '\\\\'; %%c --> c4 = %c\n", c5);

  return 0;
}

/*----------------------------1.2.c--------------------------*/
/* Realni tipovi podataka */

/* C99 kod - npr. mesanje deklaracija i koda */

#include <stdio.h>
#include <float.h>

int main() {
  /* Rasponi */
  float f_min = FLT_MIN;
  float f_max = FLT_MAX;
  printf("float [%g, %g]\n", f_min, f_max);
  printf("float [%f, %f]\n", f_min, f_max);
  printf("float - decimal digits: %d\n", FLT_DIG);
  printf("Size of float is %d bytes\n", sizeof(float));

  double d_min = DBL_MIN;
  double d_max = DBL_MAX;
  printf("double [%g, %g]\n", d_min, d_max);
  printf("double [%f, %f]\n", d_min, d_max);
  printf("double - decimal digits: %d\n", DBL_DIG);
  printf("Size of double is %d bytes\n", sizeof(double));

  long double ld_min = LDBL_MIN;
  long double ld_max = LDBL_MAX;
  printf("long double [%Lg, %Lg]\n", ld_min, ld_max);
  printf("long double [%Lf, %Lf]\n", ld_min, ld_max);
  printf("long double - decimal digits: %d\n", LDBL_DIG);
  printf("Size of long double is %d bytes\n", sizeof(long double));

  float f1 = 1.3f;      /* f naglasava da je float literal u pitanju */
  printf("float f1 = 1.3f; --> f1 = %f\n", f1);
  float f2 = .3e7f;     /* Literali se mogu zapisivati i u naucnoj notaciji */
  printf("float f2 = .3e7f; --> f2 = %f\n", f2);
  float f3 = 34.56f;    /* Vrednosti se predstavljaju samo do na odredjenu preciznost */
  printf("float f3 = 34.56f; --> f3 = %f\n", f3);
  float f4 = 123456789;         /* Konverzija celobrojnog literala - opet problem sa preciznoscu */
  printf("float f4 = 123456789; --> f4 = %f\n", f4);
  float f5 = 0.1;       /* f nije navedeno - konverzija double literala */
  printf("float f5 = 0.1f; --> f5 = %f\n", f5);

  double d1 = 34.56;    /* Double ima vecu preciznost */
  printf("double d1 = 34.56f; --> d1 = %f\n", d1);
  double d2 = 123456789;
  printf("double d2 = 123456789; --> d2 = %f\n", d2);

}

/*----------------------------1.3.c--------------------------*/
/* Logicki tip - 0 - netacno, razlicito od 0 - tacno*/

// C99 kod - npr. mesanje deklaracija i koda, komentari oblika //, zaglavlje <stdbool.h>

#include <stdio.h>
#include <stdbool.h>

int main() {
  int a;

  printf("Unesite ceo broj: ");
  scanf("%d", &a);
  if (a)
	printf("Logicka vrednost broja %d je: tacno\n", a);
  else
	printf("Logicka vrednost broja %d je: netacno\n", a);

  /* C99 uvodi tip bool i konstante true i false - potrebno je ukljuciti <stdbool.h> */
  bool b1 = false, b2 = true;
  if (b1) printf("b1 je tacno\n");
  if (b2) printf("b2 je tacno\n");

  /* True ima vrednost 1, a false 0 */
  if (b1 == 0)
	printf("b1 = 0\n");
  if (b2 == 1) 
	printf("b2 = 1\n");
  if (b2 == 3) 
	printf("b2 = 3\n");
}


/*----------------------------1.4a.c--------------------------*/
/* Aritmeticki operatori -  sabiranje, oduzimanje, mnozenje, celobrojno i realno deljenje */

#include <stdio.h> 

int main()  {
  int a, b;
  printf("Unesi prvi broj : ");
  scanf("%d",&a);

  printf("Unesi drugi broj : ");
  scanf("%d",&b);

  printf("Zbir a+b je : %d\n",a+b); 
  printf("Razlika a-b je : %d\n",a-b); 
  printf("Proizvod a*b je : %d\n",a*b); 
  printf("Celobrojni kolicnik a/b je : %d\n", a/b);
  printf("Pogresan pokusaj racunanja realnog kolicnika a/b je : %f\n", a/b);
  printf("Realni kolicnik a/b je : %f\n", (float)a/(float)b);
  printf("Ostatak pri deljenju a/b je : %d\n", a%b);
}



/*----------------------------1.4b.c--------------------------*/
/* Aritmeticki operatori - celobrojno i realno deljenje */
#include <stdio.h>

main() {
  int a = 5;
  int b = 2;
  int d = a/b;    /* Celobrojno deljenje - rezultat je 2 */
  float c = a/b;  /* Iako je c float, vrsi se celobrojno deljenje jer su i a i b celi */

  /* Neocekivani rezultat 3.000000 */
  printf("c = %f\n",c); 
  printf("Uzrok problema : 5/2 = %f\n", 5/2); 
  printf("Popravljeno : 5.0/2.0 = %f\n", 5.0/2.0); 
  printf("Moze i : 5/2.0 = %f i 5.0/2 = %f \n", 5/2.0, 5.0/2); 
  printf("Za promenjive mora da se primeni operator konverzije tipa (cast): %f\n", (float)a/(float)b);
}

/*----------------------------1.4c.c--------------------------*/
/* Aritmeticki operatori - prefiksni i postfiksni operator ++ */

/* Operator ++ uvecava vrednost promenjive za 1. 
   Operator -- je potpuno analogan operatoru ++ osim sto umanjuje vrednost za 1 */

#include <stdio.h>
main()
{
  int x, y;
  int a = 0, b = 0;
 
  printf("Na pocetku : \na = %d\nb = %d\n", a, b);
  
  /* Ukoliko se vrednost izraza ne koristi, prefiksni i 
	 postfiksni operator se ne razlikuju */
  a++;
  ++b;
  printf("Posle : a++; ++b; \na = %d\nb = %d\n", a, b);
  
  /* Prefiksni operator uvecava promenjivu, i rezultat 
	 je uvecana vrednost */
  x = ++a;
  
  /* Postfiksni operator uvecava promenjivu, i rezultat je 
	 stara (neuvecana) vrednost */
  y = b++;
  
  printf("Posle : x = ++a; \na = %d\nx = %d\n", a, x);
  printf("Posle : y = b++; \nb = %d\ny = %d\n", b, y);
}

/*----------------------------1.5a.c--------------------------*/
/* Logicki i relacijski operatori - 
      manje(<), vece(>), jednako(==), razlicito(!=), manje jednako(<=), vece jednako(>=), i(&&), ili(||), ne(!)
*/
#include <stdio.h>

main() {
  /* Rezultat primene relacijskih operatora su logicke vrednosti koje
     se predstavljaju vrednoscu nula i nekom vrednoscu razlicitom od
     nule (najcesce je to 1) */
  printf("5<3 - %d\n5>3 - %d\n3==5 - %d\n3!=5 - %d\n", 5<3, 5>3, 3==5, 3!=5);

  /* Konjunkcija, disjunkcija i negacija su definisane nad logickim
     vrednostima */
  printf("Konjunkcija : 3>5 && 5>3 - %d\n", 3>5 && 5>3);
  printf("Disjunkcija : 3>5 || 5>3 - %d\n", 3>5 || 5>3);
  printf("Negacija : !(3>5) - %d\n", !(3>5));

  /* Aritmeticki operatori imaju veci prioritet u odnosu na
     relacijske, a ovi u odnosu na logicke pa zagrade u prethodnim
     izrazima kao ni u narednom izrazu nisu neophodne */
  printf("2+3 <= 4+5 && 1+2 >= 3+5 = %d\n", 2+3 <= 4+5 && 1+2 >= 3+5); 

  /* Ipak, negacija (kao unarni operator) ima prioritet u odnosu na
     sve binarne operatore pa je zagrada u izrazu !(3>5) neophodna */
}

/*----------------------------1.5b.c--------------------------*/
/* Logicki operatori - lenjo izracunavanje */

/* Prilikom izracunavanja izraza - A && B, ukoliko je A netacno, izraz B se ne izracunava.
   Prilikom izracunavanja izraza - A || B, ukoliko je A tacno, izraz B se ne izracunava.
*/

#include <stdio.h>

int b;

/* Funkcija ispisuje da je pozvana i uvecava promenjivu b.
   Funkcija uvek vraca vrednost 1 (tacno)
*/
int izracunaj() {
  printf("Pozvano izracunaj()\n");
  b++;
  return 1;
}

main() {
  int a;
  
  /* Funkcija izracunaj() ce se pozivati samo za parne vrednosti a */
  b = 0;
  for (a = 0; a < 10; a++)
	if (a%2 == 0 && izracunaj())
	  printf("Uslov ispunjen : a = %d, b = %d\n", a, b);
	else 
	  printf("Uslov nije ispunjen : a = %d, b = %d\n", a, b);

  printf("----------------------------\n");
  
  /* Funkcija izracunaj() ce se pozivati samo za neparne vrednosti a */
  b = 0;
  for (a = 0; a < 10; a++)
	if (a%2 == 0 || izracunaj())
	  printf("Uslov ispunjen : a = %d, b = %d\n", a, b);
	else 
	  printf("Uslov nije ispunjen : a = %d, b = %d\n", a, b);

}

/*----------------------------1.6a.c--------------------------*/
/* Operatori dodele - =, += */

#include <stdio.h>

int main() {
  /* Inicijalizacija je slicna dodeli, ali postoje razlike */
  int a = 3;
  printf("a = %d\n", a);

  /* Dodela je specijalni operator. Dodela je izraz, a ako se zavrsi
     sa ; postaje i iskaz (koji se veoma cesto koristi). */ 
  a = 3 + 5; /* a se postavlja na 8 */
  printf("a = %d\n", a);

  /* Proizvoljni izraz (ne samo dodela) moze da postane iskaz, ali
     uglavnom dodele (i izrazi sa bocnim efektima) imaju smisla */
  3 + 5; /* Ovaj izkaz nema puno smisla, ali je korektan */

  a = a + 3;  /* a se uvecava za 3 */
  printf("a = %d\n", a);

  a += 3;  /* a se uvecava za 3 - operator += */
  printf("a = %d\n", a);

  /* X += Y ne mora uvek da bude isto sto i X = X + Y;
     Npr. 
     a[f()] += 3           - garantuje se da se izraz f() jednom izracunava
     a[f()] = a[f()] + 3   - izraz f() se najverovatnije dva puta izracunava i 
                             moze da ima razlicitu vrednost pri svakom 
                             izracunavanju
  */
}

/*----------------------------1.6b.c--------------------------*/
/* Operator dodele - vrednost izraza dodele */

#include <stdio.h>

int main() {
  int a, b, c;

  /* Vrednost izraza dodele je vrednost njegove desne strane, pri cemu
     se, kao bocni efekat, ta vrednost smesta na adresu (u
     promenljivu) odredjenu njegovom levom stranom. */

  /* Ovo omogucava ulancavanje (pri cemu je zagrade moguce izostaviti
     zbog ugradjene desne asocijativnosti. */
  a = b = c = 3;
  printf("a = %d, b = %d, c = %d\n", a, b, c);

  /* Vrednost dodele se moze, na primer, iskoristiti da se veoma
     kompaktno iskaze citanje karaktera do pojave nekog terminirajuceg
     karaktera */
  while((c = getchar()) != '\n') 
    putchar(c); 

  /* Operatori dodele su veoma niskog prioriteta - nizeg od
     relacijskih. Zato su zagrade u izrazu (c = getchar()) != '\n'
     neophodne */

  /* Prethodni kod je ekvivalentan sa:
    c = getchar();
    while (c != '\n') {
       putchar(c);
       c = getchar();
    }
  */
}

/*----------------------------1.6c.c--------------------------*/
/* Odnos operatora dodele i poredjenja jednakosti */ 
#include <stdio.h>

int main()
{
  int b;
  printf("Unesi ceo broj : ");
  scanf("%d", &b);

  /* Obratiti paznju na = umesto == 
     Analizirati rad programa za razlicite ulaze. */
  if (b = 0)
	printf("Broj je nula\n");
  else if (b < 0)
	printf("Broj je negativan\n");
  else
	printf("Broj je pozitivan\n");
  return 0;
}


/*----------------------------1.7.c--------------------------*/
/* Uslovni ternarni operator - ?: */

#include <stdio.h>

int main() {
  int a = 3, b = 5;
  printf("min(%d, %d) = %d\n", a, b, a < b ? a : b);
  printf("max(%d, %d) = %d\n", a, b, a > b ? a : b);
}