#include <print>
#include <iostream>
#include <concepts>

// Anforderung: Objekt muessen die Operationen + und += definieren
template <typename T>
concept Add = requires (T op) { op + op; op += op; };

// Funktionstemplate zur Addition von zwei Objekten
// Erfordert dass die Objekte die Operation + und += definiert haben
template <typename T>
    requires Add<T>
T Sum(const T& obj1, const T& obj2)
{
    T temp = obj1;
    temp += obj2;
    return temp;
}

// Klassentemplate fuer komplexe Zahlen
// Der Real- und Imaginaereanteil muss ein
// Gleitkomma-Datentyp sein
template <std::floating_point T>
class Complex
{
    T real;     // Realanteil
    T imag;     // Imaginaeranteil
public:
    // ctor, initialisiert Daten
    Complex(T _real, T _imag) : real(_real), imag(_imag)
    {
    }
    // Ueberladener Operator << fuer die Datenausgabe
    friend std::ostream& operator << (std::ostream& os,
        const Complex& obj)
    {
        os << std::format("{}+{}i", obj.real, obj.imag);
        return os;
    }
    // Operator +, wird fuer Funktionstemplate Add<T> benoetigt
    Complex operator + (const Complex& rhs)
    {
        Complex temp = *this;
        temp.real += rhs.real;
        temp.imag += rhs.imag;
        return temp;
    }
    // Operator +=, wird fuer Funktionstemplate Add<T> benoetigt
    Complex& operator += (const Complex& rhs)
    {
        real += rhs.real;
        imag += rhs.imag;
        return *this;
    }
};

int main()
{
    // Summe zweier int-Variablen ausgeben
    int ival1 = 10, ival2 = 5;
    std::println("Summe von {} und {} ist: {}",
        ival1, ival2, Sum(ival1, ival2));
    // Summe zweier komplexer Zahlen ausgeben
    Complex comp1{ 1.2,3.4 }, comp2{ 4.2,6.4 };
    std::cout << "Summe von " << comp1 << " und " << comp2
        << " ist: " << Sum(comp1, comp2) << '\n';
}
