// =====================================================================
//
//    FUNCTII & BIBLIOTECI in C++        InfoBook.ro · Clasa a IX-a
//
//    Exerseaza EXACT functiile din lectie:
//       <cmath>      sqrt, pow, ceil, floor, round, trunc, fmod, log10, log2
//       <algorithm>  min, max, swap, sort, count, reverse, fill
//       <climits>    INT_MAX, INT_MIN
//       <iomanip>    fixed, setprecision, setw
//
//    CUM IL RULEZI:
//       g++ Functii_Biblioteci_CPP.cpp -o exe
//       ./exe
//    (in VS Code: Run > Start Without Debugging)
//
// =====================================================================

#include <iostream>
#include <cmath>       // functii matematice
#include <algorithm>   // functii pentru vectori
#include <climits>     // limitele tipurilor
#include <iomanip>     // formatarea afisarii
#include <string>      // pentru tabelul cu nume
#include <cstdlib>     // rand(), srand()  — pentru jocul de la final
#include <ctime>       // time()           — pentru jocul de la final
using namespace std;

int main() {

// =====================================================================
//  PROBLEMA 1 — TRIUNGHIUL DREPTUNGHIC SI ARIA LUI
// ---------------------------------------------------------------------
//  Cerinta:        Se dau 3 laturi. Verifica daca formeaza un triunghi
//                  dreptunghic si calculeaza aria triunghiului.
//  Date de intrare:  a = 3, b = 4, c = 5
//  Date de iesire:   dreptunghic: DA
//                    aria: 6.00
//  Functii folosite: pow(), sqrt()
//
//  POVESTE:  Formula ariei de mai jos se numeste "formula lui Heron",
//            dupa HERON DIN ALEXANDRIA (secolul I d.Hr.), inginer si
//            matematician grec. El a construit primul motor cu abur din
//            istorie (aeolipila) si usi automate pentru temple, actionate
//            de aer incalzit. Formula lui e speciala pentru ca afla aria
//            DOAR din lungimile laturilor — nu ai nevoie de inaltime!
// =====================================================================

    cout << "===== PROBLEMA 1: triunghi dreptunghic + aria =====\n\n";

    double a = 3, b = 4, c = 5;

    // Teorema lui Pitagora: patratul ipotenuzei = suma patratelor catetelor
    bool dreptunghic = (pow(c, 2) == pow(a, 2) + pow(b, 2));

    // Formula lui Heron: p = semiperimetrul
    double p = (a + b + c) / 2;
    double arie = sqrt(p * (p - a) * (p - b) * (p - c));

    cout << "   laturile:     " << a << ", " << b << ", " << c << "\n";
    cout << "   dreptunghic:  " << (dreptunghic ? "DA" : "NU") << "\n";
    cout << "   aria (Heron): " << fixed << setprecision(2) << arie << "\n";
    cout << "   ipotenuza unui triunghi cu catetele 5 si 12: "
         << sqrt(pow(5, 2) + pow(12, 2)) << "\n\n";


// =====================================================================
//  PROBLEMA 2 — CATE CIFRE ARE UN NUMAR? E PUTERE A LUI 2?
// ---------------------------------------------------------------------
//  Cerinta:        Afla numarul de cifre al lui n fara sa-l imparti la
//                  10 in bucla. Apoi verifica daca un numar e putere a
//                  lui 2 (2, 4, 8, 16, 32, ...).
//  Date de intrare:  n = 47382 ;  valorile 64, 100, 1024, 30
//  Date de iesire:   47382 are 5 cifre
//                    64 -> DA, 100 -> NU, 1024 -> DA, 30 -> NU
//  Functii folosite: log10(), log2(), floor()
//
//  POVESTE:  Logaritmii au fost inventati in 1614 de scotianul JOHN NAPIER,
//            ca sa usureze calculele astronomilor: cu ei, o INMULTIRE grea
//            devine o ADUNARE simpla. Timp de 350 de ani, oamenii au calculat
//            cu "rigla de calcul", bazata pe logaritmi — inclusiv inginerii
//            care au trimis Apollo 11 pe Luna. Aici ii folosim ca sa aflam
//            instant cate cifre are un numar.
// =====================================================================

    cout << "===== PROBLEMA 2: numarul de cifre + puteri ale lui 2 =====\n\n";

    int n = 47382;
    // Trucul: un numar de k cifre are log10 intre k-1 si k
    cout << "   " << n << " are " << (int)log10(n) + 1 << " cifre\n\n";

    int valori[] = {64, 100, 1024, 30};
    for (int i = 0; i < 4; i++) {
        double l = log2(valori[i]);
        // daca log2 e numar INTREG, atunci valoarea e o putere exacta a lui 2
        bool putere = (floor(l) == l);
        cout << "   " << setw(5) << valori[i] << " putere a lui 2? "
             << (putere ? "DA" : "NU") << "\n";
    }
    cout << "\n";


// =====================================================================
//  PROBLEMA 3 — CELE PATRU ROTUNJIRI, FATA IN FATA
// ---------------------------------------------------------------------
//  Cerinta:        Compara ceil, floor, round si trunc pe aceleasi valori,
//                  pozitive SI negative. Unde difera si de ce?
//  Date de intrare:  3.2, 3.8, -3.2, -3.8
//  Date de iesire:   un tabel cu cele 4 rezultate pentru fiecare valoare
//  Functii folosite: ceil(), floor(), round(), trunc(), setw()
//
//  DE RETINUT:  la numere NEGATIVE, floor si trunc dau raspunsuri diferite!
//               floor(-3.8) = -4  (coboara mereu)
//               trunc(-3.8) = -3  (taie zecimalele, merge spre zero)
// =====================================================================

    cout << "===== PROBLEMA 3: tabelul rotunjirilor =====\n\n";

    double teste[] = {3.2, 3.8, -3.2, -3.8};

    cout << "   " << left << setw(9) << "x" << setw(9) << "ceil"
         << setw(9) << "floor" << setw(9) << "round" << setw(9) << "trunc" << "\n";
    cout << "   ---------------------------------------------\n";
    for (int i = 0; i < 4; i++) {
        double x = teste[i];
        cout << "   " << setw(9) << x
             << setw(9) << ceil(x)  << setw(9) << floor(x)
             << setw(9) << round(x) << setw(9) << trunc(x) << "\n";
    }
    cout << right << "\n";


// =====================================================================
//  PROBLEMA 4 — CATE AUTOCARE SUNT NECESARE?
// ---------------------------------------------------------------------
//  Cerinta:        O scoala pleaca in excursie. Un autocar are un numar
//                  fix de locuri. De cate autocare e nevoie?
//  Date de intrare:  130 de elevi, 45 de locuri intr-un autocar
//  Date de iesire:   3 autocare
//  Functii folosite: ceil()
//
//  ATENTIE:  130 / 45 = 2.88...  Daca folosesti round() obtii 3 (corect din
//            intamplare), dar la 100 de elevi / 45 locuri: 2.22 -> round da 2,
//            si 10 copii raman in ploaie! Raspunsul corect e MEREU ceil().
// =====================================================================

    cout << "===== PROBLEMA 4: cate autocare? =====\n\n";

    int elevi = 130, locuri = 45;
    cout << "   " << elevi << " elevi, " << locuri << " locuri/autocar\n";
    cout << "   impartire exacta: " << (double)elevi / locuri << "\n";
    cout << "   necesare:         " << (int)ceil((double)elevi / locuri) << " autocare\n";
    cout << "   (cu round ai gresi la 100 elevi: "
         << (int)round(100.0 / 45) << " in loc de "
         << (int)ceil(100.0 / 45) << ")\n\n";


// =====================================================================
//  PROBLEMA 5 — STATISTICI PE UN VECTOR
// ---------------------------------------------------------------------
//  Cerinta:        Pentru un vector de numere, afla minimul, maximul,
//                  de cate ori apare o valoare, pozitia unei valori
//                  cautate si mediana (elementul din mijloc, dupa sortare).
//  Date de intrare:  17 4 23 8 15 4 42 4      (8 valori)
//  Date de iesire:   minim = 4 ;  maxim = 42
//                    valoarea 4 apare de 3 ori
//                    valoarea 23 se afla pe pozitia 2
//                    sortat: 4 4 4 8 15 17 23 42
//                    mediana = 11.50
//  Functii folosite: min(), max(), count(), sort()
// =====================================================================

    cout << "===== PROBLEMA 5: statistici pe vector =====\n\n";

    int v[] = {17, 4, 23, 8, 15, 4, 42, 4};
    int m = 8;

    cout << "   vectorul: ";
    for (int i = 0; i < m; i++) cout << v[i] << " ";
    cout << "\n\n";

    // Minim si maxim: pornim de la limitele extreme si comparam pe rand
    int minim = INT_MAX, maxim = INT_MIN;
    for (int i = 0; i < m; i++) {
        minim = min(minim, v[i]);      // min() din <algorithm>
        maxim = max(maxim, v[i]);      // max() din <algorithm>
    }
    cout << "   minim = " << minim << ",  maxim = " << maxim << "\n";

    // count() numara aparitiile unei valori si intoarce un numar intreg
    cout << "   valoarea 4 apare de " << count(v, v + m, 4) << " ori\n";

    // Cautare secventiala: parcurgem si retinem pozitia (fara pointeri)
    int cautat = 23, pozitie = -1;
    for (int i = 0; i < m; i++)
        if (v[i] == cautat) {
            pozitie = i;
            break;                     // l-am gasit, iesim din bucla
        }
    if (pozitie != -1)
        cout << "   valoarea " << cautat << " se afla pe pozitia " << pozitie << "\n";
    else
        cout << "   valoarea " << cautat << " nu exista in vector\n";

    // Sortam crescator, apoi mediana = elementul din mijloc
    sort(v, v + m);
    cout << "   sortat: ";
    for (int i = 0; i < m; i++) cout << v[i] << " ";
    double mediana;
    if (m % 2 == 1)
        mediana = v[m / 2];                        // numar impar de elemente
    else
        mediana = (v[m / 2 - 1] + v[m / 2]) / 2.0; // par: media celor doua din mijloc
    cout << "\n   mediana = " << fixed << setprecision(2) << mediana << "\n\n";


// =====================================================================
//  PROBLEMA 6 — VECTOR PALINDROM, REVERSE, SWAP SI FILL
// ---------------------------------------------------------------------
//  Cerinta:        Inverseaza un vector. Verifica daca un vector se
//                  citeste la fel de la stanga si de la dreapta.
//                  Interschimba doua valori. Umple un vector cu zerouri.
//  Date de intrare:  {1,2,3,4,5} pentru reverse
//                    {1,2,3,2,1} pentru palindrom
//                    x = 10, y = 99 pentru swap
//  Date de iesire:   dupa reverse: 5 4 3 2 1
//                    palindrom: DA
//                    dupa swap: x = 99, y = 10
//                    fr[7] = 0
//  Functii folosite: reverse(), swap(), fill()
// =====================================================================

    cout << "===== PROBLEMA 6: reverse, palindrom, swap, fill =====\n\n";

    int w[] = {1, 2, 3, 4, 5};
    reverse(w, w + 5);                 // inverseaza ordinea elementelor
    cout << "   dupa reverse: ";
    for (int i = 0; i < 5; i++) cout << w[i] << " ";
    cout << "\n";

    // Palindrom: comparam primul cu ultimul, al doilea cu penultimul...
    int t[] = {1, 2, 3, 2, 1};
    bool palindrom = true;
    for (int i = 0; i < 5 / 2; i++)
        if (t[i] != t[5 - 1 - i]) palindrom = false;
    cout << "   {1,2,3,2,1} este palindrom? " << (palindrom ? "DA" : "NU") << "\n";

    // swap interschimba doua variabile, fara variabila auxiliara scrisa de noi
    int x = 10, y = 99;
    swap(x, y);
    cout << "   dupa swap: x = " << x << ", y = " << y << "\n";

    // fill umple tot vectorul cu aceeasi valoare (util la vectori de frecventa)
    int fr[10];
    fill(fr, fr + 10, 0);
    cout << "   vector de frecventa umplut cu 0 -> fr[7] = " << fr[7] << "\n\n";


// =====================================================================
//  PROBLEMA 7 — DE CE INT_MAX? (initializarea corecta a minimului)
// ---------------------------------------------------------------------
//  Cerinta:        Afla minimul si maximul dintr-un sir, pornind corect.
//  Date de intrare:  45 12 78 33
//  Date de iesire:   minim = 12,  maxim = 78
//  Functii folosite: INT_MAX, INT_MIN, min(), max()
//
//  GRESEALA CLASICA:  "int minim = 0;"  Daca toate valorile sunt pozitive,
//                     minimul ramane 0 — un numar care nici macar nu e in sir!
//                     Solutia: pornesti de la cea mai MARE valoare posibila
//                     (INT_MAX), ca sigur prima valoare reala s-o inlocuiasca.
//
//  DE CE 2147483647?  Un int are 32 de biti; unul e pentru semn, raman 31.
//                     2^31 - 1 = 2147483647. Iar INT_MIN este -2147483648.
// =====================================================================

    cout << "===== PROBLEMA 7: INT_MAX si INT_MIN =====\n\n";

    int date[] = {45, 12, 78, 33};
    int mn = INT_MAX, mx = INT_MIN;
    for (int i = 0; i < 4; i++) {
        mn = min(mn, date[i]);
        mx = max(mx, date[i]);
    }
    cout << "   INT_MAX = " << INT_MAX << "\n";
    cout << "   INT_MIN = " << INT_MIN << "\n";
    cout << "   minim = " << mn << ",  maxim = " << mx << "\n\n";


// =====================================================================
//  PROBLEMA 8 — CATALOG FORMATAT FRUMOS
// ---------------------------------------------------------------------
//  Cerinta:        Afiseaza un tabel aliniat cu numele elevilor si media
//                  lor, cu exact doua zecimale.
//  Date de intrare:  Ana 9.456 ; Bogdan 8.2 ; Cristina 10.0
//  Date de iesire:   un tabel aliniat pe coloane
//  Functii folosite: setw(), left, right, fixed, setprecision()
//
//  DE RETINUT:  setw(n) rezerva n caractere pentru coloana urmatoare.
//               left / right spun daca textul se lipeste la stanga sau dreapta.
// =====================================================================

    cout << "===== PROBLEMA 8: catalog formatat =====\n\n";

    string nume[]  = {"Ana", "Bogdan", "Cristina"};
    double medii[] = {9.456, 8.2, 10.0};

    cout << "   " << left << setw(12) << "ELEV" << right << setw(8) << "MEDIA" << "\n";
    cout << "   --------------------\n";
    for (int i = 0; i < 3; i++)
        cout << "   " << left  << setw(12) << nume[i]
             << right << setw(8) << fixed << setprecision(2) << medii[i] << "\n";

    cout << "\n=====================================================\n";
    cout << "  GATA! Ai exersat toate bibliotecile lectiei.\n";
    cout << "=====================================================\n";


// =====================================================================
//  JOC FINAL — GHICESTE NUMARUL (1 - 100) in cel mult 7 incercari
// ---------------------------------------------------------------------
//  Cerinta:          Calculatorul alege un numar secret intre 1 si 100.
//                    Tu incerci sa-l ghicesti. Dupa fiecare incercare iti
//                    spune daca numarul tau e prea mic sau prea mare.
//                    Ai la dispozitie 7 incercari.
//  Date de intrare:  numerele tale, cate unul pe rand
//  Date de iesire:   "mai MARE" / "mai MIC" / "BRAVO"
//  Functii folosite: rand(), srand(), time()
//
//  DE CE EXACT 7 INCERCARI?
//     Daca la fiecare pas ghicesti MIJLOCUL intervalului ramas, injumatatesti
//     de fiecare data numarul de variante posibile:
//         100 -> 50 -> 25 -> 13 -> 7 -> 4 -> 2 -> 1
//     Sunt necesari cel mult 7 pasi, pentru ca 2^7 = 128 > 100.
//     Matematic: ceil(log2(100)) = 7  (ai calculat log2 la Problema 2!).
//     Strategia se numeste CAUTARE BINARA si o studiezi in clasa a X-a.
// =====================================================================

    cout << "\n\n=====================================================\n";
    cout << "       JOC: GHICESTE NUMARUL (1 - 100)\n";
    cout << "=====================================================\n\n";

    // srand(time(0)) porneste generatorul de la ceasul calculatorului,
    // ca sa obtii alt numar la fiecare rulare. Se scrie O SINGURA DATA.
    srand(time(0));

    int secret = rand() % 100 + 1;      // rand() % 100 da 0..99, deci +1 -> 1..100
    int MAX_INCERCARI = 7;
    int incercari = 0;
    bool ghicit = false;

    cout << "M-am gandit la un numar intre 1 si 100.\n";
    cout << "Ai " << MAX_INCERCARI << " incercari. Sfat: incearca mereu MIJLOCUL!\n\n";

    while (incercari < MAX_INCERCARI) {
        incercari++;
        int n;
        cout << "Incercarea " << incercari << "/" << MAX_INCERCARI << " - numarul tau: ";
        cin >> n;

        if (n == secret) {
            cout << "   BRAVO! Ai ghicit din " << incercari << " incercari!\n";
            ghicit = true;
            break;                      // iesim din bucla, jocul s-a terminat
        }
        else if (n < secret)
            cout << "   Numarul meu e mai MARE decat " << n << "\n";
        else
            cout << "   Numarul meu e mai MIC decat " << n << "\n";

        int ramase = MAX_INCERCARI - incercari;
        if (ramase > 0)
            cout << "   (ti-au ramas " << ramase << " incercari)\n";
        cout << "\n";
    }

    if (!ghicit)
        cout << "\n   Ai terminat incercarile. Numarul meu era: " << secret << "\n";

    cout << "\n=====================================================\n";
    return 0;
}
