Soluzioni: Lezione 5

../_images/linea.png

5.1 funzione somma templata

  • esercizio01.cpp

/*
c++ -o es1 esercizio01.cpp

Testo:
    Si scriva una funzione somma che, utilizzando la tecnologia template, 
    sia applicabile a qualunque tipo numerico del C++

*/


#include <iostream>

//-------------- Template sum -----------------

template <typename T1, typename T2>
double somma_d (T1 a, T2 b){ //returning double -> somma_b(a,b) = somma_b(b,a) for each c++ type of b and a
    return a + b ;
}

template <typename T1, typename T2>
T2 somma_t (T1 a, T2 b){ //returning T2 -> somma_t(a,b) != somma_t(b,a)
    return a + b ;
}

//--------- Optional: Variadic sum ------------

//Iterative way to sum numbers sum(a,b,c,...)

//base-case (only one number in input or last number in the recursion)
template<typename T>
double sum(T v) {
    return v;
}

//... is called parameter pack of zero or more template of type "Types" parameters
// -> this function allows forwarding of arbitrary numer of parameters to a function
template<typename T, typename... Types>
double sum(T v, Types&&... others) {
    /*
        Recursive sum: suppose we are calling this function as:
        sum(1,2,3,4) -> return 1 + sum(2,3,4);
        sum(2,3,4) -> return 1 + 2 + sum(3,4);
        sum(3,4) -> return 3 + sum(4); Here sum(4) is the identity template function (base-case) of line 24
        All in all:
        return 1+2+3+4;
    */
    return v + sum(others...);
}


int main (int argc, char ** argv)
  {
    int a = 1 ;
    double b = 1.5 ;

    std::cout << "somma_d preserves commutation rule of inputs. somma_d("<<a<<","<<b<<") = " 
                    << somma_d(a, b) << " somma_d("<<b<<","<<a<<") = " << somma_d(b,a) << std::endl;

    std::cout << "\nsomma_t does not preserve commutation rule of inputs. somma_t("<<a<<","<<b<<") = " 
                    << somma_t(a, b) << " somma_t("<<b<<","<<a<<") = " << somma_t(b,a) << std::endl;

    std::cout << "\nsomma_d can waste memory -> always returns double even if we sum integers of size: " << sizeof(somma_d(a, static_cast<int>(b)))
                    << std::endl;

    int somma_int = static_cast<int>(somma_d(a,b));
    float somma_float = static_cast<float>(somma_d(a,b));
    std::cout << "\nWe can always cast the result of somma_d(a,b) = " << somma_d(a,b) << " size: " << sizeof(somma_d(a,b))
                    << " into integer of value: " << somma_int << " size: " << sizeof(somma_int) <<
                    " or float of value: " << somma_float << " size: " << sizeof(somma_float) << std::endl;

    // Example of recursive sum

    double rec_sum = sum(a,b,a,b,a,b); //3*a + 3*b = 3+4.5 = 7.5
    std::cout << "\nRecursive sum sum(a,b,a,b,a,b) -> sum(int,double,...) = " << rec_sum << std::endl;
    

    return 0 ;
  }
../_images/linea.png

5.2 funzione somma templata con specializzazione ai numeri complessi

  • esercizio02.cpp

/*
c++ -o es2 esercizio02.cpp complesso.cc

Testo:
    Si testi la funzione somma sviluppata nell'esercizio precedente sulla classe dei numeri complessi 
    sviluppata durante la Lezione 3, aggiungendo anche una specializzazione per un tipo a piacere.

*/

#include "complesso.h"

template <typename T1, typename T2>
T1 somma_t (T1 a, T2 b){ //returning T2 -> valid for each object supporting + operator overloading
    return a + b ;
}

template<>
complesso somma_t(complesso a, double b){
    std::cout << "Specialization complex + double" << std::endl;
    return a + b;
}

int main(){

    complesso c(3,5); //3 + i*5
    complesso d(1.5,10.7); //1.5 + i*10.7
    int a_i = 5;
    double a_d = 1.4;

    auto s = somma_t(c, a_i);
    s.stampami();
    s = somma_t(c, d);
    s.stampami();
    s = somma_t(c, a_d);
    s.stampami();
    

    return 0;
}
#include <iostream>

#ifndef COMPLESSO_H
#define COMPLESSO_H

class complesso{

public: 
    // Constructors
    complesso();
    complesso(double r); 
    complesso (double r, double i) ; 
    complesso (const complesso & orig);
    // Destructor
    ~complesso () ;

    // Public class methods
    double modulo () ;      
    double fase () ;  
    void stampami();  
    double get_real() { return m_real; };
    double get_imag() { return m_imag; };
    complesso somma(const complesso & orig);
    complesso differenza(const complesso & orig);
    complesso moltiplicazione(const double & fattore);
    complesso moltiplicazione(const complesso & fattore);

    // Operators overloading
    complesso &  operator= (const complesso & orig);
    complesso operator+ (const complesso & addendo);
    void operator+= (const complesso & addendo);
    void operator+= (const double & addendo);
    void operator-= (const complesso & addendo);
    void operator-= (const double & addendo);
    void operator*= (const complesso & fattore);
    void operator*= (const double & fattore);
    void operator/= (const complesso & fattore);
    void operator/= (const double & fattore);
    

private:
    // Private members
    double m_real ;
    double m_imag ;

} ;

#endif // COMPLESSO_H
#include "complesso.h"
#include <cmath>


//--------------Constructors------------------

complesso::complesso ():
  m_real (0.),
  m_imag (0.){}

complesso::complesso (double r):
  m_real (r),
  m_imag (0.){}

complesso::complesso (double r, double i):
  m_real (r),
  m_imag (i){}

complesso::complesso (const complesso & orig):
  m_real (orig.m_real),
  m_imag (orig.m_imag){
      //empty
}

//--------------Destructor------------------

complesso::~complesso (){
    // ~ deletes the "complesso" object and stack allocated memory.
    // This is the place to clean the dinamically allocated memory.
    // In this example we do not have dinamically allocated memory so the destructor is empty.

}

//--------------Public methods---------------

double complesso::modulo (){

    return sqrt(m_real * m_real + m_imag * m_imag) ;

}

double complesso::fase(){

    double mod = modulo();

    if (mod == 0){
        std::cout << "Parte reale e immaginaria = 0. La fase non è definita" << std::endl;
        return -1;
    }

    return acos(m_real/mod);
}


void complesso::stampami (){


    std::cout << this->m_real << " + " << this->m_imag << "i" << std::endl ;
    return ;

}


complesso complesso::somma(const complesso & orig){
  complesso ret(m_real + orig.m_real, m_imag + orig.m_imag);
  return ret;
}


complesso complesso::differenza(const complesso & orig){
  complesso ret(m_real - orig.m_real, m_imag - orig.m_imag);
  return ret;
}

complesso complesso::moltiplicazione(const complesso & fattore){
  const double m_real_copy(m_real), m_imag_copy(m_imag); //definisco copie da usare nelle operazioni
  double new_m_real = (m_real_copy*fattore.m_real) - (m_imag_copy*fattore.m_imag) ;
  double new_m_imag = (m_real_copy*fattore.m_imag) + (m_imag_copy*fattore.m_real) ;

  complesso ret(new_m_real, new_m_imag);
  return ret;
}

complesso complesso::moltiplicazione(const double & fattore){
  complesso ret(fattore*m_real, fattore*m_imag);
  return ret;
}


//--------------Overloading---------------

complesso &  complesso::operator= (const complesso & orig){
  m_real = orig.m_real ;
  m_imag = orig.m_imag ;
  return *this ;
}  

complesso complesso::operator+ (const complesso & addendo){
  complesso ret(m_real, m_imag);
  ret.m_real += addendo.m_real ;
  ret.m_imag += addendo.m_imag ;
  return ret;
}

void complesso::operator+= (const complesso & addendo){
  m_real = m_real + addendo.m_real ;
  m_imag = m_imag + addendo.m_imag ;
  return ;
}

void complesso::operator+= (const double & addendo){
  m_real = m_real + addendo;
  return ;
}

void complesso::operator-= (const complesso & addendo){
  m_real = m_real - addendo.m_real ;
  m_imag = m_imag - addendo.m_imag ;
  return ;
}

void complesso::operator-= (const double & addendo){
  m_real = m_real - addendo;
  return ;
}

void complesso::operator*= (const complesso & fattore){
  const double m_real_copy(m_real), m_imag_copy(m_imag); //definisco copie da usare nelle operazioni
  m_real = (m_real_copy*fattore.m_real) - (m_imag_copy*fattore.m_imag) ;
  m_imag = (m_real_copy*fattore.m_imag) + (m_imag_copy*fattore.m_real) ;
  return ;
}

void complesso::operator*= (const double & fattore){
  m_real = m_real*fattore ;
  m_imag = m_imag*fattore ;
  return ;
}

void complesso::operator/= (const complesso & fattore){

  double denom = (fattore.m_real*fattore.m_real) + (fattore.m_imag*fattore.m_imag); // c^2 + d^2

  //Controlli
  if (denom==0){ 
    if (fattore.m_real){
      m_real = m_real/fattore.m_real;
      return;
    }
    else{
      std::cout << "Divisione per 0, ricontrolla inputs" << std::endl;
      return;
    }
  }

  const double m_real_copy(m_real), m_imag_copy(m_imag); //definisco copie da usare nelle operazioni
  m_real = (m_real_copy*fattore.m_real + m_imag_copy*fattore.m_imag)/denom ;
  m_imag = (m_imag_copy*fattore.m_real - m_real_copy*fattore.m_imag)/denom ;
  return ;
}

void complesso::operator/= (const double & fattore){

  if(fattore == 0){
    std::cout << "Errore: Divisione per 0" << std::endl;
    return;
  }

  m_real = m_real/fattore ;
  m_imag = m_imag/fattore ;
  return ;
}
../_images/linea.png

5.3 implementazione e test della classe `SimpleArray`

  • main.cpp

/*
c++ -o es3 main.cpp SimpleArray.cc ../esercizio02/complesso.cc 

Testo:
    Si costruisca la classe SimpleArray, templata sul tipo degli oggetti che contiene, implementando 
    le funzioni definite nel prototipo presentato a lezione.

    Si ricordi di utilizzare correttamente l'allocazione dinamica della memoria.
    Si aggiungano alla classe anche metodi che permettano di accedere agli elementi della classe in caso vengano 
    chiamati su un oggetto const.
    Si faccia in modo che i metodi di accesso al contenuto del SimpleArray controllino il valore dell'indice prima 
    di accedere all'array salvato in memoria.
    Si verifichi che la classe SimpleArray può essere templata sulla classe dei numeri complessi sviluppata 
    durante la Lezione 3.
*/

#include "SimpleArray.h"
#include "../esercizio02/complesso.h"

int main(){

    int dim = 10;
    SimpleArray<complesso*> arr(10);
    
    for(int i = 0; i < dim; ++i){
        arr[i] = new complesso(i,i);
        arr[i]->stampami();
    }

    return 0;
}
  • SimpleArray.h

#ifndef SIMPLE_ARRAY_H
#define SIMPLE_ARRAY_H
#include <iostream>
#include "../esercizio02/complesso.h"

template <typename T> 
class SimpleArray {
public:
  SimpleArray (const int & elementsNum);
  SimpleArray (const SimpleArray & copy);
  ~SimpleArray () { delete[] elements_p; }; 
  T & element (const int& i);
  T & operator[] (const int& i);
  T operator[] (const int& i) const;

private:

  int elementsNum_p;
  T * elements_p;
} ;


template <typename T> 
SimpleArray<T>::SimpleArray (const int & elementsNum): 
    elementsNum_p(elementsNum), 
    elements_p(elementsNum_p ? new T[elementsNum_p] : nullptr){

}

template <typename T> 
SimpleArray<T>::SimpleArray (const SimpleArray & copy): 
    elementsNum_p(copy.elementsNum_p), 
    elements_p(copy.elementsNum_p ? new T[copy.elementsNum_p] : nullptr){

    if (elements_p){
        for(int i = 0; i < elementsNum_p; i++){
            elements_p[i] = copy.elements_p[i];
        }
    }

}


template <typename T> 
T & SimpleArray<T>::element(const int& i){
    if(i > elementsNum_p || i < 0){
        std::cout << "requested index out of array dimension. return first element" << std::endl;
        return elements_p[0];
    } 
    return elements_p[i];
}

template <typename T> 
T & SimpleArray<T>::operator[](const int& i){
    if(i > elementsNum_p || i < 0){
        std::cout << "requested index out of array dimension. return first element" << std::endl;
        return elements_p[0];
    } 
    return elements_p[i];
}

template <typename T> 
T SimpleArray<T>::operator[](const int& i) const{
    if(i > elementsNum_p || i < 0){
        std::cout << "requested index out of array dimension. return first element" << std::endl;
        return elements_p[0];
    } 
    return elements_p[i];
}

#endif //SIMPLE_ARRAY_H
../_images/linea.png

5.4 implementazione e test della classe `DynamicArray`

  • main.cpp

/*
c++ -o es4 main.cpp

Testo:
    Si implementi una classe templata dal nome DynamicArray che inizialmente non 
    contenga alcun elemento ed abbia un metodo push_back () che permetta di aggiungere un 
    elemento in fondo all'array degli elementi già esistenti.

    Si progetti un meccanismo che rimpiazzi l'array che contiene gli elementi quando è pieno, s
    ostituendolo con uno più capiente.
    Si aggiunga un metodo che permetta anche di svuotare l'oggetto di tipo DynamicArray.
*/

#include <iostream> 
#include "DynamicArray.h"

int main(){

    DynamicArray<int> arr;
    for(int i = 0; i < 10; ++i) arr.push_back(i);

    for(int i = 0; i < 10; ++i){ //this syntax is named "range based for loop".
        std::cout << arr[i] << " ";
    }
    std::cout  <<  std::endl;

    //clearing
    arr.clear();
    for(int i = 10; i < 20; ++i) arr.push_back(i);

    for(int i = 0; i < 10; ++i){ //this syntax is named "range based for loop".
        std::cout << arr[i] << " ";
    }
    std::cout  <<  std::endl;



    return 0;
}
  • DynamicArray.h

#ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H

template <typename T>
class DynamicArray{

    public:
        DynamicArray();
        DynamicArray(int & dim);
        DynamicArray(const DynamicArray& copy);
        ~DynamicArray() { delete[] elements_p; };
        void push_back(const T & val);
        T & operator[] (const int& i);
        void resize();
        void clear();

    private:
        int capacity;
        int current_size;
        T * elements_p;

};

template <typename T>
DynamicArray<T>::DynamicArray(): 
    capacity(4), 
    current_size(0), 
    elements_p(capacity ? new T[capacity] : nullptr){

}

template <typename T>
DynamicArray<T>::DynamicArray(int & dim): 
    capacity(dim), 
    current_size(0), 
    elements_p(capacity ? new T[capacity] : nullptr){

}

template <typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& copy): 
    capacity(copy.capacity), 
    current_size(copy.capacity), 
    elements_p(copy.capacity ? new T[copy.capacity] : nullptr){

        if (elements_p){
            for(int i = 0; i < capacity; ++i){
                elements_p[i] = copy.elements_p[i];
            }
        }

}

template <typename T>
void DynamicArray<T>::push_back(const T & val){

    if(current_size < capacity){
        elements_p[current_size] = val;
        current_size++;
    }
    else{
        std::cout << "augmenting size from " << capacity;
        capacity*=2;
        std::cout << "to " << capacity << std::endl;

        T * buffer = new T[capacity];

        for(int i = 0; i < current_size; ++i){
            buffer[i] = elements_p[i];
        }

        delete[] elements_p;
        elements_p = buffer;

        elements_p[current_size] = val;
        current_size++;

    }

    return;
}

template <typename T>
void DynamicArray<T>::clear(){
    capacity = 4;
    current_size = 0;
    delete[] elements_p;
    elements_p = new T[capacity];

    return;
}

template <typename T> 
T & DynamicArray<T>::operator[](const int& i){
    if(i > current_size-1 || i < 0 ){
        std::cout << "requested index out of array dimension. return first element" << std::endl;
        return elements_p[0];
    } 
    return elements_p[i];
}



#endif // DYNAMIC_ARRAY_H
../_images/linea.png

5.5 implementazione e test della classe `vettore`

  • main.cpp

/*
c++ -o es5 main.cpp

Testo:
    Si implementi la classe templata vettore definita nella lezione.

    Si ricordi di controllare che gli indici passati ai metodi della classe siano entro i 
    limiti della memoria occupata dagli oggetti di tipo vettore
    Si aggiungano gli operatori algebrici necessari per la definizione di uno spazio vettoriale
*/

#include <iostream>
#include "vettore.h"

int main(){

    vettore<10> v;
    for(int i = 0;  i < 10; ++i){
        v.setCoord(i,i);
    }

    std::cout << "Elements:" << std::endl;
    for(int i = 0; i < 10; ++i){
        std::cout << v.at(i) << " ";
    }
    std::cout << std::endl;

    std::cout << "Norm: " << v.norm() << std::endl;

    vettore<10> v2 = v * 2;
    std::cout << "Elements:" << std::endl;
    for(int i = 0; i < 10; ++i){
        std::cout << v2.at(i) << " ";
    }
    std::cout << std::endl;

    std::cout << "Norm: " << v2.norm() << std::endl;

    vettore<10> v3 = v2 + v;
    std::cout << "Elements:" << std::endl;
    for(int i = 0; i < 10; ++i){
        std::cout << v3.at(i) << " ";
    }
    std::cout << std::endl;

    std::cout << "Norm: " << v3.norm() << std::endl;


    return 0;
}
  • vettore.h

#ifndef VETTORE_H
#define VETTORE_H

#include <cmath>

template <int N> 
class vettore{
  public:
    vettore();
    vettore (const vettore<N> & orig);
    ~vettore () {}; 
    void setCoord (int i, double val);
    double norm ();
    double at (int i) const;
    vettore<N> & operator= (const vettore<N> & orig);
    vettore<N> operator+ (const vettore<N> & orig);
    vettore<N> operator* (const double & fac);
    void operator += (const vettore<N> & orig);
    void operator *= (const double & orig);
    double & operator[] (int i);

  private:
    double elementi[N] ;
} ;

template <int N> 
vettore<N>::vettore(){
    for (int i = 0 ; i < N ; ++i) elementi[i] = 0. ;
}

template <int N> 
vettore<N>::vettore (const vettore<N> & orig){ 
    for (int i = 0 ; i < N ; ++i) elementi[i] = orig.elementi[i] ;
}

template <int N> 
void vettore<N>::setCoord (int i, double val){
    if (i < 0 || i > N-1) return ;
    elementi[i] = val ;
    return ;
}

template <int N> 
double vettore<N>::norm(){
    double sum = elementi[0] * elementi[0] ; 
    for (int i = 1 ; i < N ; ++i) sum += elementi[i] * elementi[i] ;
    return sqrt (sum) ;
}

template <int N> 
double vettore<N>::at (int i) const{
    if(i > N-1 || i < 0) {
        std::cout << "requested index out of array dimension. Return 0" << std::endl;
        return 0.;
    }
    return elementi[i] ;
}

template <int N> 
vettore<N> & vettore<N>::operator= (const vettore<N> & orig){
    for (int i = 0 ; i < N ; ++i) elementi[i] = orig.elementi[i] ;
    return *this ;
}

template <int N> 
vettore<N> vettore<N>::operator+ (const vettore<N> & orig){
    vettore<N> v;
    for (int i = 0 ; i < N ; ++i) v[i] = elementi[i] + orig.elementi[i] ;
    return v;
}


template <int N> 
vettore<N> vettore<N>::operator* (const double & fac){
    vettore<N> v;
    for (int i = 0 ; i < N ; ++i) v[i] = elementi[i] * fac ;
    return v ;
}

template <int N> 
void vettore<N>::operator+= (const vettore<N> & orig){
    for (int i = 0 ; i < N ; ++i) elementi[i] += orig.elementi[i] ;
    return;
}

template <int N> 
void vettore<N>::operator*= (const double & fac){
    for (int i = 0 ; i < N ; ++i) elementi[i] *= fac;
    return ;
}

template <int N> 
double & vettore<N>::operator[] (int i){
    return elementi[i] ;
}

#endif
../_images/linea.png

5.6 implementazione e test della classe `matrie`

  • main.cpp

/*
c++ -o es6 main.cpp

Testo:
    Si implementi la classe templata matrice delle matrici quadrate di dimensione N, 
    templata sulla dimensione delle matrici, definendo anche le operazioni fra matrici.
*/
#include "matrice.h"

template <int R, int C>
void fill(Matrice<R,C>& mat, double alpha, int axis){
    /*
        axis = 0 if x, 1 if y, 2 if z
    */  
    for(int i = 0; i < 3; ++i){
        for(int j = 0; j < 3; ++j){
            if (i == j){
                i != axis ? mat.setCoord(i,j, cos(alpha)) : mat.setCoord(i,j, 1);
            }
            else{
                i+j+axis == 3 ? mat.setCoord(i,j, (j-1)*sin(alpha)) : mat.setCoord(i,j, 0);

            }
        }
    }

    return;

}

int main(){

    vettore<3> v;
    for(int i = 0; i < 3; ++i) i==0 ? v.setCoord(i,1) : v.setCoord(i,0); // (1,0,0)

    Matrice <3,3> rot_z;
    
    //-------------------------------------------------------------------------------
    //----------------------- Manual filling -> exact result ------------------------
    /*
    //filling rotation matrix around z-axis [(cos, -sin, 0), (sin, cos, 0), (0, 0, 1)]
    //pi/2 cos = 0, sin = 1
    for(int i = 0; i < 3; ++i){
        for(int j = 0; j < 3; ++j){
            if (i == j){
                i != 2 ? rot_z.setCoord(i,j, 0) : rot_z.setCoord(i,j, 1);
            }
            else{
                i+j == 1 ? rot_z.setCoord(i,j, (i-j)) : rot_z.setCoord(i,j, 0);

            }
        }
    }
    */
    //-------------------------------------------------------------------------------

    fill(rot_z, M_PI/2, 2); // automatic filling -> approx. errors

    std::cout << "Rotation matrix alpha = pi/2" << std::endl;
    rot_z.stampa();

    vettore<3> v_rot = rot_z * v;
    std::cout << "Rotazione pi/2 around z axis of (1,0,0)" << std::endl;
    for(int i = 0; i < 3; ++i) std::cout << v_rot.at(i) << " ";
    std::cout << "\n";

    //-------------------------------------------------------------------------------
    //----------------------- Manual filling -> exact result ------------------------
    /*
    //filling rotation matrix around z-axis [(cos, -sin, 0), (sin, cos, 0), (0, 0, 1)]
    //pi cos = -1, sin = 0
    for(int i = 0; i < 3; ++i){
        for(int j = 0; j < 3; ++j){
            if (i == j){
                i != 2 ? rot_z.setCoord(i,j, -1) : rot_z.setCoord(i,j, 1);
            }
            else{
                rot_z.setCoord(i,j, 0);

            }
        }
    }
    */
    //-------------------------------------------------------------------------------

    fill(rot_z, M_PI, 2); // automatic filling -> approx. errors

    std::cout << "Rotation matrix alpha = pi" << std::endl;
    rot_z.stampa();

    v_rot = rot_z * v;
    std::cout << "Rotazione pi around z axis of (1,0,0)" << std::endl;
    for(int i = 0; i < 3; ++i) std::cout << v_rot.at(i) << " ";
    std::cout << "\n";

    //-------------------------------------------------------------------------------
    //----------------------- Manual filling -> exact result ------------------------
    /*
    //Rotation around each axis returns original vector
    Matrice<3,3> rot_x;
    for(int i = 0; i < 3; ++i){
        for(int j = 0; j < 3; ++j){
            if (i == j){
                i != 0 ? rot_x.setCoord(i,j, -1) : rot_x.setCoord(i,j, 1);
            }
            else{
                rot_x.setCoord(i,j, 0);

            }
        }
    }

    Matrice<3,3> rot_y;
    for(int i = 0; i < 3; ++i){
        for(int j = 0; j < 3; ++j){
            if (i == j){
                i != 1 ? rot_y.setCoord(i,j, -1) : rot_y.setCoord(i,j, 1);
            }
            else{
                rot_y.setCoord(i,j, 0);

            }
        }
    }
    */
    //-------------------------------------------------------------------------------

    Matrice<3,3> rot_x; fill(rot_x, M_PI, 0);
    Matrice<3,3> rot_y; fill(rot_y, M_PI, 1);
    
    Matrice<3,3> par = rot_x*rot_y*rot_z;
    for(int i = 0; i < 3; ++i) v.setCoord(i,1); // (1,1,1)

    std::cout << "Rotation of pi around every axis of (1,1,1) yields" << std::endl;
    vettore<3> v_par = par * v;
    for(int i = 0; i < 3; ++i) std::cout << v_par.at(i) << " ";
    std::cout << "\n";


    return 0;
}
  • matrice.h

#ifndef MATRICE_H
#define MATRICE_H

#include <iostream>
#include <cmath>
#include "../esercizio05/vettore.h"

template <int R, int C>
class Matrice{
    public:

        Matrice ();
        Matrice (const Matrice<R,C>& orig);
        ~Matrice () {};

        void setCoord(const int& i, const int& j, const double& val);
        double at (const int& i, const int& j) const;
        void stampa();
        bool quadrata() const;
        int rango() const;
        bool simmetrica() const;
        double determinante() const;
        Matrice<R-1, C-1> minore (const int& r, const int& c) const;
        Matrice<C, R> inversa () const;
        Matrice<C, R> trasposta () const;
        vettore<R> operator* (const vettore<C>& v);



    private:
        double elementi[R][C];

};

template <int R, int C>
Matrice<R,C>::Matrice(){
    for (int i = 0 ; i < R ; ++i) 
          for (int j = 0 ; j < C ; ++j) 
            elementi[i][j] = 0. ;
}

template <int R, int C>
Matrice<R,C>::Matrice(const Matrice<R,C>& orig){
    for (int i = 0 ; i < R ; ++i) 
          for (int j = 0 ; j < C ; ++j) 
            elementi[i][j] = orig.elementi[i][j] ;
}

template <int R, int C>
void Matrice<R,C>::setCoord(const int& i, const int& j, const double& val){

    if (i < 0 || i > R-1) return ;
    if (j < 0 || j > C-1) return ;
    elementi[i][j] = val ;
    return ;
}

template <int R, int C>
double Matrice<R,C>::at(const int& i, const int& j) const{

    if (i < 0 || i > R-1) return 0.;
    if (j < 0 || j > C-1) return 0.;
    return elementi[i][j];
}

template <int R, int C>
void Matrice<R,C>::stampa(){
    for (int i = 0 ; i < R ; ++i){
        for (int j = 0 ; j < C ; ++j)
            std::cout << this->at (i, j) << "\t" ;
        std::cout << "\n" ;
    }
}

template <int R, int C>
bool Matrice<R,C>::quadrata () const {return (R == C) ;} 

template <int R, int C>
int Matrice<R,C>::rango () const {
    if(quadrata()) return R ;
    else{
        std::cout << "Matrix not square. Returning -1" << std::endl;
        return -1;
    }
}

template <int R, int C>
bool Matrice<R,C>::simmetrica () const{
    if (!this->quadrata ()) return false ;
    for (int i = 0 ; i < R ; ++i){
        for (int j = i+1 ; j < R ; ++j)
        if (fabs (elementi[i][j] - elementi[j][i]) > 0.00001) return false ;
    }
    return true ;
}

template <int R, int C>
double Matrice<R,C>::determinante () const{
    if (!this->quadrata ()){
        std::cout << "Matrix not square. Returning -1" << std::endl;
        return -1;
    }

    double det = 0. ;
    for (int i = 0 ; i < R ; ++i){
        det += elementi[0][i] * pow (-1, i + 2) * this->minore (0,i).determinante () ;
    }

    return det ; 
}

template<>
double Matrice<1,1>::determinante () const { return elementi[0][0] ; }

template <int R, int C>
Matrice<R-1, C-1> Matrice<R,C>::minore (const int& r, const int& c) const{
    Matrice<R-1, C-1> M_out ;
    int i_new = 0 ; 
    for (int i = 0 ; i < R ; ++i){
        if (i == r) continue ;
        int j_new = 0 ;
        for (int j = 0 ; j < C ; ++j){
            if (j == c) continue ;
            M_out.setCoord (i_new, j_new, this->at (i,j)) ;
            ++j_new ;
        }
        ++i_new ;
    }
    return M_out ;  
}

template <int R, int C>
Matrice<C, R> Matrice<R,C>::inversa () const{
    Matrice<C, R> M_out ;

    double invdet = 0.;
    try{
        invdet = this->determinante () ;
    }
    catch (const std::domain_error& de){
        std::cerr << de.what() << std::endl;
        return M_out ;
    }

    if (invdet < 0.000001) return M_out ;

    for (int i = 0 ; i < R ; ++i){
        for (int j = 0 ; j < C ; ++j){
            M_out.setCoord (
                j, i,  // trasposta
                this->minore (i,j).determinante () * pow (-1, i+j+2) // complemento algebrico
                * invdet  // divisione per il determinante
            ) ;
        }
    }

    return M_out ;
}

template <int R, int C>
Matrice<C, R> Matrice<R,C>::trasposta () const{

    Matrice<C, R> M_out ;
    for (int i = 0 ; i < R ; ++i){
        for (int j = 0 ; j < C ; ++j)
        M_out.setCoord (j, i, this->at (i,j)) ;
    }

    return M_out ;  
}


template <int R, int C>
vettore<R> Matrice<R,C>::operator* (const vettore<C>& v){
    vettore<R> w ;

    for (int i = 0 ; i < R ; ++i){
        double res = 0 ;
        for (int j = 0 ; j < C ; ++j) res += (this->at (i,j) * v.at (j));
        w.setCoord (i, res) ;
    }

    return w ;
}

template <int M, int N, int O>
Matrice<M,O> operator* (const Matrice<M,N> & M1, const Matrice<N,O> & M2){
    Matrice<M,O> M_out ;

    for (int i = 0 ; i < M ; ++i){
        for (int k = 0 ; k < O ; ++k){
            double res = 0 ;
            for (int j = 0 ; j < N ; ++j) res += M1.at (i,j) * M2.at (j,k) ;
            M_out.setCoord (i, k, res) ;
        }
    }
    return M_out ;
}

#endif
../_images/linea.png

5.7 la sequenza di Fibonacci in uno `std::vector`

  • esercizio07.cpp

/*
c++ -o es7 esercizio07.cpp

Testo:
    Si crei un std::vector vuoto e, tramite un ciclo for, lo si riempia con i primi 10 numeri 
    della successione di Fibonacci.

    E' conveniente, dal punto di vista dell'esecuzione di questo esercizio, utilizzare una funzione esterna 
    al ciclo per il calcolo dei numeri di Fibonacci?
    Si stampi a schermo il suo contenuto utilizzando un ciclo su un numero intero, oppure utilizzando un iteratore.
*/

#include <iostream>
#include <vector>

void fibonacci(std::vector<int> & v){
    
    if(v.size() >= 2) v.push_back( v.end()[-2] + v.end()[-1] ) ;
    else{
        std::cout << "Fill the vector with the first two Fibonacci numbers" << std::endl;
    }
    return;

}

int main(){

    std::vector<int> v;
    int F_0 = 0; 
    int F_1 = 1;

    //Inline fill
    for(int i = 0; i < 5; ++i){
        v.push_back(F_0);
        v.push_back(F_1);
        F_0 = F_0 + F_1;
        F_1 = F_0 + F_1;
    }

    for (std::vector<int>::iterator it = v.begin() ; it != v.end(); ++it)
        std::cout << *it << ' ';
    std::cout << '\n';

    //External function fill
    v.clear();
    v.push_back(0); v.push_back(1);
    for(int i = 0; i < 8; ++i){
        fibonacci(v);
    }

    for(int i = 0; i < v.size(); ++i) std::cout << v.at(i) << ' ';
    std::cout << '\n';

    return 0;
}
../_images/linea.png

5.8 un `std::vector` di numeri complessi

  • esercizio08.cpp

/*
c++ -o es8 esercizio08.cpp esercizio02/complesso.cc

Testo:
    Si crei un std::vector vuoto e lo si riempia con cinque oggetti del tipo della classe 
    complesso sviluppato durante la Lezione 3.

    Tramiti opportuni messaggi a schermo, si verifichi quando vengono chiamati costruttore, 
    copy constructor, operatore di assegnazione e distruttore della classe complesso
*/

#include "esercizio02/complesso.h"
#include <vector>
#include <memory>

int main(){
    std::vector<complesso> v;

    for(int i = 0; i < 5; ++i){
        complesso a(i,i);
        v.push_back(a);
    }
    for(int i = 0; i < 5; ++i) v[i].stampami();

    std::cout << "-------------" << std::endl;

    std::vector<complesso*> v_;

    for(int i = 0; i < 5; ++i) v_.push_back(new complesso(i,i));
    for(int i = 0; i < 5; ++i) v_[i]->stampami();

    //clean memory
    for(int i = 0; i < 5; ++i) delete v_[i];

    std::cout << "--- Destructors at the end -----" << std::endl;

    return 0;
}
../_images/linea.png

5.9 un esercizio con una `std::map`

  • esercizio09.cpp

/*
c++ -o es9 esercizio09.cpp esercizio02/complesso.cc

Testo:
    Si crei una std::map vuota e la si riempia con una sequenza di numeri complessi, 
    utilizzando la norma dei numeri stessi come chiave di ordinamento della mappa.

    Si stampino quindi a schermo i numeri, ordinati per norma.
*/

#include <iostream>
#include <map>
#include "esercizio02/complesso.h"

static const int seed = 1234567; 

template <typename T>
double rand_range(T min, T max){
    return min + (max - min) * rand () / static_cast<float> (RAND_MAX) ;
}

int main(){

    srand(seed);
    std::map<double, complesso> c_m;
    std::cout << "Filling..." << std::endl;
    for(int i = 0; i < 10; ++i){
        complesso num(rand_range(-10., 10.), rand_range(-10., 10.));
        std::cout << num.modulo() << " ";
        c_m[num.modulo()] = num;
    }
    std::cout << '\n';

    std::cout << "Print sorted based on this->first" << std::endl;
    for(std::map<double, complesso>::iterator it = c_m.begin() ; it != c_m.end(); ++it){
        std::cout << "Norm: " << it->first  << " Number: "; 
        it->second.stampami();
    }

    return 0;
}
../_images/linea.png

5.10 un esercizio con una `std::string`

  • esercizio10.cpp

/*
c++ -o es10 esercizio10.cpp

Testo:
    Si crei una std::string riempita con un periodo scelto a piacere.

    Si contino il numero di parole ed il numero di lettere (spazi esclusi) che compongono il periodo.
    Si divida la stringa in singole parole, ciascuna salvata con una stringa all'interno di un std::vector.
*/
#include <string>
#include <iostream>
#include <vector>

int count_words(std::string const& s){
    bool literal = false;
    int n_words = 0;

    if(s.empty()) return 0;

    std::string::const_iterator it = s.begin();

    do{
        if(*it != ' '){ //char of a word
            ++it;
            literal = true;
        }
        else if (*it == ' ' && literal) { //end of word
            ++it;
            ++n_words;
            literal = false;
        }

        else ++it; //double spaces
        
    }
    while(it != s.end());

    if (literal) ++n_words; //case where s = "abcd" (no spaces but literals)

    return n_words;

}

std::vector<std::string>&  fill_vec(std::vector<std::string>& v, std::string const& s){
    
    if(s.empty()) return v;
    bool literal = false;

    std::string word;
    std::string::const_iterator it = s.begin();

    
    do{
        if(*it != ' '){ //char of a word
            word.push_back(*it);
            ++it;
            literal = true;
        }
        
        else if (*it == ' ' && literal) { //end of word
            v.push_back(word);
            word.clear();
            ++it;
            literal = false;
        }
        
        else ++it; //double spaces


    }while(it != s.end());

    v.push_back(word);
    
    return v;
}

int count_letters(std::string const& s){
    int l_c = 0;
    if(s.empty()) return 0;

    std::string::const_iterator it = s.begin();

    do{
        if(*it != ' ') ++l_c;
        ++it; 
    }
    while(it != s.end());

    return l_c;

}



int main(){

    std::string Lorem_Ipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "   
                            "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
                            "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris "
                            "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " 
                            "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla " 
                            "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " 
                            "culpa qui officia deserunt mollit anim id est laborum.";

    std::cout << "Words: " << count_words(Lorem_Ipsum) << std::endl;
    std::cout << "Char: " << count_letters(Lorem_Ipsum) << std::endl;

    std::string hw = "Hello world!";
    std::cout << "Words: " << count_words(hw) << std::endl;
    std::cout << "Char: " << count_letters(hw) << std::endl;

    hw = "Hello                    world!";
    std::cout << "Words: " << count_words(hw) << std::endl;
    std::cout << "Char: " << count_letters(hw) << std::endl;

    std::vector<std::string> v;
    v = fill_vec(v, Lorem_Ipsum);
    for (std::vector<std::string>::iterator it = v.begin() ; it != v.end(); ++it) std::cout << *it << " ";
    std::cout << std::endl;
    std::cout << "Number of words: " << v.size() << std::endl;

    hw = " ";
    std::cout << "Words: " << count_words(hw) << std::endl;
    std::cout << "Char: " << count_letters(hw) << std::endl;
    
    return 0;
}
../_images/linea.png