C++

Basics

#include <iostream>

// A return value of 0 indicates program success. Ordinarily, a nonzero return indicates what kind of error occurred
int main(int argcchar const *argv[])
{
    std::cout << "Hello World" << std::endl;
    return 1;
}

// Statements starting with # are Preprocessor Statements meaning they are executed before compilation
// by default main function returns 0

// 0 = false and 1 = true;
// #include<iostream> is a header file = means there is file named iostream and include(import) it in the program already, It helps in input, output(cout,etc), etc.
// std is a function present in iostream file
// C++ ignores blank lines
// main() = function which is the start of the program, which gives instructions to other functions
// int in main() means return value of function is integer
// if return value is 0 means program has success fully ran
// Always use ;(Semicolon) at the end of a line
// = Single line comment
// /**/ = Multi line comment
// void func() means the the function will not return anything

// variables and data created on the stack memory are deleted after it goes out of scope

                               Conditionals

#include <iostream>

int main(){
    bool condition = true;
    if (condition){
        // code
    }
    else if (condition)
    {
        /* code */
    }
    else{
        // code
    }
    
    // Short Hand If Else Statement
    // variable = (condition) ? expressionTrue : expressionFalse;

    int expression = 1;    

    switch(expression){
        case 2:
            // code
            break;
        case 3:
            // code
            break;
        case 1:
            // code
            break;
        default:
            // code
    }    
}    

                                Input-Output

#include<iostream>
// to import std(module with fucntions cout,etc)
using namespace std;
int main(int argcchar const *argv[])
{
    int num1num2;
    cout << "Enter num1:\n";
    cin >> num1;
    cout << "Enter num2:\n";
    cin >> num2;
    cout << "The sum is " << num1+num2;

    // OR
    cin.get();

    return 0;
}
// << is called insertion operator
// >> is called extraction operator
// Cout = To print 
// Cin = To get input
// setw is used for spaces setw(7) = 7 spaces in output

                                Maths

// Math functions are in cmath
#include <cmath>
#include <iostream>
using namespace std;

/*
abs(x)  Returns the absolute value of x
acos(x) Returns the arccosine of x, in radians
asin(x) Returns the arcsine of x, in radians
atan(x) Returns the arctangent of x, in radians
cbrt(x) Returns the cube root of x
ceil(x) Returns the value of x rounded up to its nearest integer
cos(x)  Returns the cosine of x, in radians
cosh(x) Returns the hyperbolic cosine of x, in radians
exp(x)  Returns the value of Ex
expm1(x)    Returns ex -1
fabs(x) Returns the absolute value of a floating x
fdim(x, y)  Returns the positive difference between x and y
floor(x)    Returns the value of x rounded down to its nearest integer
hypot(x, y) Returns sqrt(x2 +y2) without intermediate overflow or underflow
fma(x, y, z)    Returns x*y+z without losing precision
fmax(x, y)  Returns the highest value of a floating x and y
fmin(x, y)  Returns the lowest value of a floating x and y
fmod(x, y)  Returns the floating point remainder of x/y
pow(x, y)   Returns the value of x to the power of y
sin(x)  Returns the sine of x (x is in radians)
sinh(x) Returns the hyperbolic sine of a double value
tan(x)  Returns the tangent of an angle
tanh(x) Returns the hyperbolic tangent of a double value
*/

int main(){

    cout << pow(23<< endl;
    cout << sqrt(16<< endl;
    cout << floor(2.8<< endl;
    cout << fmax(57<< endl;
    return 0;
}

                                    Operators

#include <iostream>

using namespace std;
int main(int argcchar const *argv[])
{
    int a = 4;
    cout<<"Operators "<<endl;
    cout<<"Value of a++ "<<a++<<endl;
    cout<<"Value of a-- "<<a--<<endl;
    cout<<"Value of ++a "<<++a<<endl;
    cout<<"Value of --a "<<--a<<endl;
    return 0;
}
// Types of Operators
/*
1) Arithmetic Operators = Mathematical(+,-,*,/,%,++,--,etc)
2) Assignment Operators = Used to assign values to variables
3) Comparison Operators = Used to compare values of variables and constants
Ex - ==,!=,<=,>=,>,<,etc (cout<<(a==b)) 
4) Logical operators = &&(and), ||(or), !(not), etc.
*/
// Increment operator = ++
// Decrement operator = --
// endl = endline (similar to /n)
// arithmetic operator on integers will return integer value only (4/5 = 0)
// sizeof(34.4) = pixel size of a constant

                                    Pointers

#include <iostream>
using namespace std;

// A pointer is a compound type that “points to” another type
// Like references, pointers are used for indirect access to other objects.
// a single pointer can point to several different objects over its lifetime.
// Unlike a reference, a pointer need not be initialized at the time it is defined.
// A pointer holds the address of another object.
// pointers defined at block scope have undefined value if they are not initialized

int main(){
    // A pointer, is a variable that stores the memory address as its value.
    // string* mystring; string *mystring; string * mystring; (1st is preffered)

    int x = 5; // 4
    double y = 5; // 8
    // cout << sizeof(x) << endl << sizeof(y);
    cout << &x << endl; // outputs the memory address of x
    intptr = &x; // A pointer variable, with the name ptr, that stores the address of x
    cout << ptr << endl;
    cout << *ptr; // *  = value at the pointer

    int var = 8;

    // * for a pointer, & for the memory location of that variable
    intvarptr = &var;
    *varptr = 10;  // Dereferencing the pointer, accessing the data

    // It is a nullptr, with no value
    voidasd = nullptr; // Pointer is just an integer, it does not need a type

    // pointer to pointer, double pointer
    int** z = &ptr;
    
    // In this case only x is a pointer and not y
    intxy;
    // Both are pointer now
    intx, *y;

    return 0;
}



                                            Enums

#include <iostream>
#include <string>

// enum is a user-defined data type that can be assigned some limited values. These values are defined by the programmer at the time of declaring the enumerated type.

// By default all the variables are initialized as 0, 1, 2, ...
// If you initialize first variable as 5, next variable will be 6 and so on
// enum by default is 4-byte integers
// enum cannot have float values, it can have integer values only
enum Example
{
    A=5B=2C=7
};

enum Names : char
{
    a = 5b = 4
};

int main(){

    // value can have values A, B, and C
    Example value = B;

    if(value == 2){
        
    }
    else if(value == 5){

    }

    return 0;
}

                            Classes and Struct

#include <iostream>
using namespace std;
#define log(x) cout << x << endl

// Difference between class and struct is that by default class is private and struct is public

// static methods cannot access non-static variables
// Player::variable or Player:method for static variables or methods
// By default, all the variables inside a class in private
// Size of a class is equal to the sum of the sizes of the variables inside the class 
class Player{
    public:
        int x, y, speed;

        // Constructor
        // The Constructor is called whenever an instance of the class is made
        // It is a method with no return type and name the same as that of the class
        // c++ has a default constructor.
        // You can write multiple constructors but they should have different parameters
        Player(){
            x = 0; y = 0; speed = 0;
        }

        Player(int Xint Y){
            x = X; y = Y;
        }

        // To delete the default constructor, so that no object can be made -
        // Player() = delete;
        // OR, write the constructor in the private section

        // Destructor
        // It starts with a ~ and the name of the class
        // It is called when the instance of the class is deleted
        // It does not take any parameter nor does it return anything
        ~Player(){
            log("Destroyed Player");
        }

        // changes the x and y of the object the method is used
        void move(int xaint ya){
            x += xa * speed;
            y += ya * speed;
        }
};

// By default, all the variables inside a class in public
// Never use struct when it comes to inheritance
// Use a struct instead of class when you just to represent some data
// #define struct class -> to use struct as a class
struct Play{
    private:
    // variables and methods, same as a class
};

int main(){
    // Initialising a Object/instance of class Player
    Player player;
    player.x = 0player.y = 0player.speed = 10;
    player.move(1, -1);
    log(player.x); log(player.y);

    // Initializing an instance with an constructor with parameters
    Player p(510);

    return 0;
}

                                Copy Constructor

#include <iostream>
using namespace std;

// Copy Constructors are used when you have to create a copy of an already existing instance
// Compiler has a default Copy Constructor 

class Number{
    int a;
    public:
        Number(){
            a = 0;
        }
        Number(int num){
            a = num;
        }

        // Copy Constructor
        // Takes the Reference of a object as a parameter
        Number(Number &obj){
            a = obj.a;
        }


        void display(){
            cout << "a for this Object is " << a << endl; 
        }
};

int main(){
    Number x, y, z(50), z2;
    x.display(); y.display(); z.display();

    Number z1(z);
    z1.display();

    z2 = z; // Copy Constructor not called

    Number z3 = z;

    return 0;
}    
                                
                                    Stack and Heap

#include <iostream>
using namespace std;
#define log(xcout << x << endl;

struct Vector3{
    float xyz;
};

int main(){

    // stack
    int v = 5
    int array[5];
    Vector3 vector;

    // heap
    inth = new int;
    *h = 5;
    intharray = new int[5];
    Vector3hvector = new Vector3;
}

// Stack - The allocation happens on contiguous blocks of memory. We call it stack memory allocation because the allocation happens in function call stack. The size of memory to be allocated is known to compiler and whenever a function is called, its variables get memory allocated on the stack. And whenever the function call is over, the memory for the variables is deallocated. This all happens using some predefined routines in compiler. Programmer does not have to worry about memory allocation and deallocation of stack variables.

// Heap - The memory is allocated during the execution of instructions written by programmers. Note that the name heap has nothing to do with heap data structure. It is called heap because it is a pile of memory space available to programmers to allocated and de-allocate. If a programmer does not handle this memory well, memory leak can happen in the program.


/*
    KEY DIFFERENCES

The stack is a linear data structure whereas Heap is a hierarchical data structure.

Stack memory will never become fragmented whereas Heap memory can become fragmented as blocks of memory are first allocated and then freed.

Stack accesses local variables only while Heap allows you to access variables globally.

Stack variables can’t be resized whereas Heap variables can be resized.

Stack memory is allocated in a contiguous block whereas Heap memory is allocated in any random order.

Stack doesn’t require de-allocate variables whereas in Heap de-allocation is needed.

Stack allocation and deallocation are done by compiler instructions whereas Heap allocation and deallocation is done by the programmer.

*/


                                Header Files

// all header files = https://en.cppreference.com/w/cpp/header
// Importing a header file
#include <iostream>
#include "Yash.h"

using namespace std;

int main(int argcchar const *argv[])
{
    cout<<"Hello world";
    return 0;
}

// There are two types of Header Files
/*
1 - System header files: comes with the compiler
#include <iostream>
2 - User defined header files: Written by the programmer
#include "Name.h"
*/
// Header extension = .h


                                Variables

#include<iostream>

/*
    Global variables are initialized automatically 
    int - 0, char = '\0', float = 0, double = 0, pointer = NULL
*/
/*
    Defining Constants
    1) Using #define preprocessor
    2) Using const keyword
    // It is good practice to define constants in capitals
*/
/*
lvalue -> Expression that refers to a memory location.
rvalue -> The data value stored at some address in memory.
*/

int main(int argcchar const *argv[])
{
    int a = 4b =5;
    float pi = 3.14;
    long int c = 5456765;
    std::cout<<"Value of a is "<< a << ". \nThe value of b is "<< b << "\n" << "Value of pi is "<< pi;
    return 0;
}

// Variables = Containers to store data
/* Built-in Datatypes:
1) int = 12,5,0,-4
2) float = 0.24,7.8,-9.11
3) char = "a", "R", "y"(only one character)
4) Double = Similar to float but with more precision = to store number with many digits after decimal point 
5) Boolean = true or false(Has value = 1 for True, value = 0 for False
6) long double = greater then double
7) wchar_t = wide character
)
*/
// To create a variable: Datatype_of_Variable  Variable_Name = Value;  Ex - int num = 4;, char a="q", b="";
/* Scope of variables(similar to python): 
Local = declared inside any function and accessed only from there
Global = Declared outside function and can be accessed from anywhere (eg ->var(local), ::var(global))
Local variable has precidece over Global variable for everything 
// Scope resolution operator changes the scope of variable from local to global Ex- cout<<::a; will print global a.
*/
/* Rules for naming Variables:
can range from 1 to 255characters
names must begin with a letter of alphabet or _
It can contain letters, numbers, and _
Variable names are case sensitive
No spaces or symbols in name
*/

/*
All Reserved Keywords in c++:
    https://www.w3schools.in/cplusplus-tutorial/keywords/
*/
                                    Strings

#include <iostream>
#include <string>

using namespace std;

/*
The string is a collection of characters. There are two types of strings commonly used in C++ programming language:

    1. Strings that are objects of string class (The Standard C++ Library string class)
    2. C-strings (C-style Strings) = char str[] = "C++"; char str[4] = "C++";char str[] = {'C','+','+','\0'}; char str[4] = {'C','+','+','\0'}; 
    // a '\0' is added at the end of the string automatically

*/

// strcpy(c1, c2): It is used to copy characters from one string to another string.
// strcat(c1, c2): It is used to add the two given strings.
// strlen(): It is used to find the length of the given string.
// strcmp(): It is used to compare the two given string.

int main(int argcchar const *argv[])
{
    string name ("Yash");
    cout<<name.length()<<endl;
    // cout<<name[1];
    name[2] = 'm'; // Changing the value off certain index in variable
    cout<<name.find("m"0); 
    return 0;
}
// string.length() = length of the string
// std::string str_name ("Yash");
// To access a certain character in a string String_name[index]
// Index starts with 0 
// Name.find("characters", Starting_index_for_finding)
// strlen(s), strcat(s1, s2), 


                                Inline Functions


#include <iostream>
using namespace std;

// InLine Functions paste the code inside where it is used elsewhere
// We should use these because if we create small func, then it will increase run time due to it takes time if we pass arguments and then take the result
// if we make big functions inline then it will further increase run time
// Do not use when - recursion, static variables, large code is used
inline int product(int aint b){
    return a*b;
}

void func(int a,  const int* cint b = 2){
// b has default value 2, it is optional to pass value of b as an argument
// c is constant argument and cannot be changed
}

int main(){
    cout << product(25<< endl
    cout << product(25<< endl;
    return 0;
}


                                Friend Functions

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// A friend function of a class is defined outside that class' scope but it has the right to access all private and protected members of the class. 
// Even though the prototypes for friend functions appear in the class definition, friends are not member functions.
// To declare a function as a friend of a class, precede the function prototype in the class definition with keyword friend

class Complex{
    int ab;
    public:

        // Declaration of the function as a friend.
        friend Complex sumComplex(Complex o1Complex o2);

        void setNumber(int n1int n2){
            a = n1;
            b = n2;
        }
        void printNumber(){
            cout << "Your Number is " << a << " + " << b << "i" << endl ;
        }
};

// This function cannot access a and b because they are private members of Complex class, so we have to declare in Complex that it is a friend.
Complex sumComplex(Complex o1Complex o2){
    Complex o3;
    o3.setNumber((o1.a + o2.a), (o1.b + o2.b));
    return o3;
}

int main(){

    Complex c1c2sum;
    c1.setNumber(14);
    c2.setNumber(58);

    sum = sumComplex(c1c2);
    sum.printNumber();
    
    return 0;
}

                                            Inheritance

#include <iostream>
using namespace std;

// private members of a class cannot be inherited by another class

// The already existing class is called as Base Class.
// The inherited class is called as Derived Class

// Order of Calling of the Functions - Constructor of Parent > Constructor of Child > Destructor of child > Destructor of Parent

/*
    Types of Inheritance - https://www.studytonight.com/cpp/types-of-inheritance.php

    1) Single Inheritance - A derived class with only one base 
    2) Multiple Inheritance - A derived class may derive from two or more base classes
    3) Hierarchical Inheritance - Multiple derived class inherits from a single base class
    4) Multilevel Inheritance - A derived class inherits from some other class, which in turn inherits from some other class.
    5) Hybrid(Virtual) Inheritance - It is a combination of Hierarchical and Multilevel Inheritance.
    
*/

class Entity
{
public:
    float xy;

    void Move(float xafloat ya){
        x += xa;
        y += ya;
    }
};

// Inheritance
// Player now have everything that Entity has
class Player : public Entity
{
public:
    const charName;
    void printName(){
        cout << Name << endl;
    }
};


int main(){
    Player player;
    
    return 0;
}
                                               
                                            New and Delete

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// new is a operator which denotes a request for memory allocation on the heap. 
// If sufficient memory is available. It returns the address of the newly allocated and intialized memory to the pointer variable
// It finds a continous block of memory (row of memory)
// New calles the function malloc() - memory allocation, malloc(sizeof(int)) it returns a ptr

// When you use new, you must use delete. To free the memory.
// new(memory address) - to allocate the variable in the memory address specified

int main(){

    int a = 2;
    intb = new int; // 4 byte integer allocated in the heap

    chare = new(bchar;

    delete b; // To free the memory
    log(e);

    return 0;
}

                                            Arrow

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// Arrow Operator(->) = To access members of a structure through a pointer, It deferences the pointer and then access the member

class Complex{
    public:
        int realimaginary;
        Complex(int aint b){
            real = a;
            imaginary = b;
        }

        Complex(){
            real = 0;
            imaginary = 0;
        }

        void get(){
            cout << "Real: " << real << endl;
            cout << "Imaginary: " << imaginary << endl;
        }
};

int main(){

    Complex c(12);

    Complexptr = &c;
    (*ptr).get(); // . operator has higher precedence over * operator, thus used ()
    // log(ptr->real);

    Complexptr2 = new Complex;

    // log(ptr2->imaginary);
    
    Complexptr1 = new Complex[4];
    (ptr1+2)->real = 5;
    (ptr1+2)->get();

    return 0;
}

                                            This

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// The this pointer is an implicit parameter to all member functions. Therefore, inside a member function, this may be used to refer to the invoking object.
// Friend functions do not have this pointer, because friends are not members of a class. Only member functions have a this pointer.
// this is a keyword which is a pointer which points to the object which invokes the member function

class A{
    int a;
    public:

    void setData(int a){
        this->a = a; // this is like self in python
    }

    A* yash(){
        return this;
    }

    void getData(){
        cout << "The value of a is " << a << endl;
    }
};

int main(){

    A a;
    a.setData(4);
    a.getData();

    Ay = a.yash();
    y->getData(); // or (*y).getData()

    return 0;
}

                                        Overloading

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// Overloading - more than one definition for a function name or an operator in the same scope
// An overloaded declaration is a declaration that is declared with the same name as a previously declared declaration in the same scope, except that both declarations have different arguments and obviously different definition.

// Function Overloading
// The definition of the function must differ from each other by the types and/or the number of arguments in the argument list. 
// You cannot overload function declarations that differ only by return type

void print(int i){
    log(i);
}
void print(float f){
    log(f);
}
void print(string s){
    log(s);
}

int main()
{
    // Call print to print int
    print(5);
    // Call print to print float
    print(2.8f);
    // Call print to print string
    print("Hello");

    return 0;
}
    
                                                Visibility

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// There are 3 basic visibilty modifiers in c++
// public, protected, private

// Default visibility of a class is private
class Entity{

// only this class and a friend function can access private members
private:
    int xy;

// Only this class and all sub-classes can access protected members
protected:
    void Print(){
        log("Hello");
    }

// Every thing can access public members
public:
    static void get(){
        log("Yashovardhan");    
    }
};

class Player : public Entity{
    public:
        Player(){
            Print();
        }
};

// Default visibility of a struct is public

int main(){
    Entity::get();

    return 0;
}

                                            Modifiers

#include <iostream>
using namespace std;
#define log(xcout << x << endl

int main(){

    signed int a = -50; // 2 bytes
    short unsigned int b = 50; // Cannot be Negative, 2 bytes     

    float c = 1.23; // 8 bytes

    log(sizeof(float)); //4
    log(sizeof(double)); // 8
    log(sizeof(long double)); // 12
    log(sizeof(long long int)); // 8

    return 0;
}

/*
    Modifiers - used for char, int and double

    => signed, unsigned, long, short, signed long, signed short, unsigned long, unsigned short
    // signed is used by default


    signed = can contain a sign, i.e, + or -, can store -> (+-2^31) 
    unsigned = no sign (only positive) -> 2^32

    int -> signed, unsigned, long, short
    char -> signed, unsigned
    double -> long

    You can simply use the word unsigned, short, or long, without int to declare unsigned, short and long integers.
*/

                                  Call by Reference and Call by Value

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// Functions can be invoked in two ways: Call by Value or Call by Reference
// The parameters passed to function are called actual parameters whereas the parameters received by function are called as formal parameters

// Call by Reference: Both the actual and formal parameters refer to same locations, so any changes made inside the function are actually reflected in actual parameters of caller.
void swap(int &aint &b){
    int t = a;
    a = b;
    b = t;
}

// Call By Value: In this parameter passing method, values of actual parameters are copied to function’s formal parameters and the two types of parameters are stored in different memory locations. So any changes made inside functions are not reflected in actual parameters of caller.
void vswap(int aint b){
    int t = a;
    a = b;
    b = t;
}

int main(){

    int a = 5b = 8;
    swap(ab);
    log(a);
    log(b);

    return 0;
}

                                            Union

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// Union allocates common memory location for all its members
// The memory occupied by a union will be large enough to hold the largest member of the union
// It stores data of only one member, which is the member specified at last

// Never use the string class
// size of the union will be 24 bytes which are is size of the string
union money{
    int rice;
    char currency
    float pounds;
};

int main(){
    union money m;
    m.rice = 5;
    m.pounds = 8.9;
    log(m.pounds); 
    // now, if you print m.rice use will get a garbage value

    // log(sizeof(money));
}

                                            Const


#include <iostream>
using namespace std;
#define log(xcout << x << endl

// const = declare a constant
// const methods are used because for const objects of a class only const methods can be used

class Entity{

private:
    int xy;
public:
// const after a class method means it will not modify class variables 
// A const function can only access the data members but cannot change them.
    int GetX() const{
        return x;
    }

    void set(int x){
        this->x = x;
    }

    // It will return a pointer that cannot be modified, its values cannot be modified and the function itself cannot modify any of the class members
    const intconst GetY() const{
        return &y;
    }

};

int main(){

    // I cannot change this integer
    const int AGE = 5;
    
    // constant pointer
    // you cannot change the data at the const pointer 
    const inta = new int; // OR int const* a = new int; // const before *
    // *a = 2; // wrong
    a = (int*)&AGE; // write

    // you cannot change the memory address
    intconst b = new int; // const after *
    *b = 3; // write
    // b = (int*)&AGE; // wrong

    // Cannot change anything
    const charconst c = new char;
    // *c = 'd'; // wrong
    // c = (char*)&AGE; // wrong

    log(*a);

    return 0;
}

                                                Mutable

#include <iostream>
#define log(xstd::cout << x << std::endl

/*
                             mutable 
// Mutable data members are those members whose values can be changed in runtime even if the object is of constant type
// It is opposite of constant
// Mutability is very important to manage classes
// It is storage class specifier

*/

class Entity{

private:
    // mutable members can be changed by a const function
    mutable int m_DebugCount = 0;
    std::string m_Name;
public:
    std::string getName() const{
        m_DebugCount++;
        return m_Name;
    }

};

int main(){

    const Entity e;
    e.getName();

    return 0;
}

                                                Lambdas

#include <iostream>
#include <functional>
#define log(xstd::cout << x << std::endl

// C++ 11 introduced lambda expression to allow us to write an inline function that can be used for short snippets of code that are not going to be reuse and not worth naming. 
// Generally return-type in lambda expression are evaluated by the compiler itself and we don’t need to specify that explicitly and -> return-type part can be ignored
// A lambda expression can have more power than an ordinary function by having access to variables from the enclosing scope.
// A lambda with empty capture clause [ ] can access only those variable which is local to it.


/* Syntax used for capturing variables :
      [&] : capture all external variable by reference
      [=] : capture all external variable by value
      [a, &b] : capture a by value and b by reference  */

/*
[ capture clause ] (parameters) -> return-type  
{   
   definition of method   
*/

int main(){
    int a = 5b = 7;
    
    auto f = [](int xint y){return x+y;};
    log(f(ab));

    return 0;
}

                                            auto

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// The auto keyword specifies that the type of the variable that is begin declared will automatically be deduced from its initializer.
// For functions if their return type is auto then that will be evaluated by return type expression at runtime.

// compiler automatically detects the return type of the function by looking at the return statement of the function
inline auto product(int aint b){
    return a*b;
}

int main(){
    // compiler automatically determines the type
    auto a = 5;

    return 0;
}

                                                Ternary

#include <iostream>
using namespace std;
#define log(xcout << x << endl

// Ternary Operator is used for conditional assignment

// Since the Conditional Operator ‘?:’ takes three operands to work, hence they are also called ternary operators.

/* The conditional operator is of the form - 
variable = Expression1 ? Expression2 : Expression3

which is same as - 
if(Expression1)
{
    variable = Expression2;
}
else
{
    variable = Expression3;
}
*/

int main(){
    bool t = true;

    // As t = true, i will be equal to 5
    int i = t ? 5 : 10;
    log(i);

    int a = 2 > 1 ? 2 > 3 ? 15 : 10 : 5;
    /* OR
    int a;
    if(2 > 1 && 2 > 3){
        a = 15;
    }
    else if (2 > 3 || 2 > 1){
        a = 10;
    }
    else{
        a = 5;
    }
    */
    log(a);
    return 0;
}

                                        Vector

#include <iostream>
#include <vector>

// A vector is a collection of objects, all of which have the same type
// A vector is simply a sequence of elements that you can access by an index
// The first ele has index 0, second index 1, and so on
// Every object in the collection has an associated index, which gives access to that object
// vector is a template not a type
// A vector also stores its size

/*  Vector Operations
v.empty() = boolean
v.size() = length
v[n] = reference of the element at position n in v
v.clear() = clear() function is used to remove all the elements of the vector container, thus making it size 0. All elements are destroyed one by one.
v.erase() = erase() function is used to remove elements from a container from the specified position or range.
*/

// std::vector class provides a useful function reserve which helps user specify the minimum size of the vector.It indicates that the vector is created such that it can store at least the number of the specified elements without having to reallocate memory.

class a{};

int main()
{
    std::vector<int> ivec;
    ivec.reserve(4);
    std::vector<std::vector<std::string>> vec; // a vector with elements as vectors with elements as string
    using namespace std;
    vector<a> avec;

    ivec = {157};
    ivec[1] = 4;
    vector<int> ivec2(ivec); // copied all the elements of ivec to ivec2
    vector<int> ivec3 = {012458};

    vector<int> ivec4(10);     // 10 elements, each initialized to 0
    vector<int> ivec5(10-5); // 10 ele, initialized to -5
    ivec5.clear();
    vector<string> svec(10); // ten elements, each an empty string

    // The push_back operation takes a value and “pushes” that value as a new last element onto the “back” of the vector.
    ivec.push_back(12);

    for (int &i : ivec4)
    { // for each element in ivec4
        i += 5;
    }
    for (int x : ivec)
    {
        cout << x << endl;
    }
    cout << ivec4[0] << endl;
    for (int i = 0; i < ivec.size(); i++)
    {
        cout << ivec[i] << endl;
    }

    return 0;
}
                                    decltype

#include <iostream>
#define log(xstd::cout << x << std::endl

// The decltype type specifier yields the type of a specific expression
// decltype(expression)

// It is worth noting that decltype is the only context in which a variable defined as a reference is not treated as a synonym for the object to which it refers.

// decltype(auto) used for parameter forwarding
// decltype(auto) is mainly used to derive the return type of a forwarding function or package, which does not require us to explicitly specify the parameter expression of decltype

std::string l1();

decltype(autol(){
    return l1();
}

int main(){
    int a = 5;
    decltype(ab = 10; // type is int

    const float c = 2;
    decltype(cd = 4; // type is const float
    return 0;
}

                                   Null pointer

#include <iostream>
using namespace std;

// Initialize all pointers

// A null pointer does not point to any object

// To create a null pointer -
intp1 = nullptr; // nullptr has a special type that can be converted to any other pointer type
intp2 = 0; // second method

int main(){
    return 0;
}

                                   Reference

#include <iostream>
using namespace std;

#define log(x) cout << x << endl

// Reference is an alias
// A reference defines an alternate name for an object
// A reference type refers to another type
// When we define a reference, instead of copying the initializer’s value, we bind the reference to its initializer. Once initialized, a reference remains bound to its initial object. There is no way to rebind a reference to refer to a different object. Because there is no way to rebind a reference, references must be initialized.
// Because references are not objects, we may not define a reference to a reference.


void increase(int& value){
    value++;
}

int main(){
    int a = 5;
    // & is used for a creating a reference variable
    int& ref = a;
    increase(a);
    /*  OR
    void increase(int* value){
        (*value)++;
    }
    increase(&a);
    */
    log(a);

    return 0;
}                   

                                    alias

// A type alias is a name that is a synonym to another type
// Type aliases let us simplify complicated type definitions, making those types easier to use

// There are two ways to define a type alias

typedef int integer; // integer is a synonym for int
typedef integer number, *num; // number is a synonym for int and num for *int

// OR
// An alias declaration starts with the keyword using follwed by the alias name and an =
//  The alias declaration defines the name on the left-hand side of the = as an alias for the type that appears on the right-hand side

using two = double; // two is a synonym of double

int main(){
    
    return 0;
}
        
                                          Constexpr   

#include <iostream>

// constexpr is used to make a variable constant, immutable
// A constexpr value must be given a value that is known at compile time
// Newer C++ does not have constexpr

/* Difference between constexpr and const
constexpr - 
const - 
*/

int main(){
    constexpr int i = 5;
    return 0;
}
                                        Pairs

#include <iostream>
using namespace std;

// The pair container is a simple container defined in <utility> header consisting of two data elements or objects.
// The first element is 'first' and the second element as 'second' and the order is fixed(first, second).
// The pair is used to combine together two values which may be different in type.
// Pair can be assigned, copied, and compared.
// To access the elements, we use variable name followed by dot operator followed by the keyword first or second.

int main(){

    pair<intint> p1;
    p1.first = 25;
    p1.second = 7;

    cout << "Sum of " << p1.first << " is " << p1.second << endl;

    pair<floatfloat> p2(7.45.2);
    pair<stringdouble> p3 = make_pair("Yash"2.35);

    // swap()
    pair<charint>pair1 = make_pair('A'1);
    pair<charint>pair2 = make_pair('B'2);

    pair1.swap(pair2);
    cout << pair2.first << endl;

    return 0;
}

                                          Tuples

#include <tuple>
#include <iostream>
using namespace std;

// A tuple is an object that can hold a number of elements.
// The elements can be of different data types.
// get, make_tuple(), tuple_size, swap(), tie(), tuple_cat(), tuple_element()

int main(){
    pair<int, string> p = make_pair(23"Hello");
    cout << p.first << " " << p.second << endl;

    // Creating a Tuple
    tuple<int, string, char> t(32"Yash"'a');

    // Accessing the values of a tuple
    cout << get<0>(t) << endl;
    cout << get<1>(t) << endl;
    cout << get<2>(t) << endl;

    // Changing the values of a tuple
    get<1>(t) = "World";
    cout << get<1>(t) << endl;

    // int i = 0;
    // get<i>(t);  // i must be compile time constant

    tuple<int, string, char> t2; // initialized using default constructor
    t2 = tuple<intstringchar>(12"Cat"'z');
    t2 = make_tuple(12"Cat"'z');

    // tuple can store references
    string st = "Honesty is the best";
    tuple<string&> t3(st);
    // t3 = make_tuple(ref(st));
    get<0>(t3) = "Yashovardhan";
    cout << st << endl;

    // It returns the number of elements present in the tuple.
    cout << "Size: " << tuple_size<decltype(t)>::value << endl;

    // The swap(), swaps the elements of the two different tuples.
    t.swap(t2);
    cout << get<0>(t) << endl;
    cout << get<0>(t2) << endl;

    //The work of tie() is to unpack the tuple values into separate variables. 
    // There are two variants of a tie(), with and without “ignore”, the “ignore” ignores a particular tuple element and stops it from getting unpacked.

    int i;
    float j;
    int k;

    tuple<intfloatint> t4 = make_tuple(75.01);
    tie(i, j, k) = t4;

    cout << i << j << k << endl;

    tie(i, ignore, k) = t4;
    // cout << i << k << endl;

    // tuple_cat()
    tuple<intcharfloat> tup1(20'y'5.9);
    tuple<intcharfloat> tup2(12'a'7.7);
    auto tup3 = tuple_cat(tup1, tup2);
    
    cout << "Values of tup3 is: " << get<0>(tup3) << get<1>(tup3) << get<2>(tup3) << get<3>(tup3) << get<4>(tup3) << get<5>(tup3) << endl;

    // tuple_element()
    auto mytuple = std::make_tuple (100,'x');
    cout << sizeof(tuple_element<1decltype(mytuple)>::type) << endl;
    cout << sizeof(char<< endl;
}

                                        Iterators

#include <iostream>
#include <iterator>
#include <vector>
using namespace std;

// Iterators provide a generic approach to navigate through the elements of a container.
// Iterators are used to point at the memory addresses of STL containers.
// They are used in a sequence of numbers, characters, etc.
// They reduce the complexity and execution time of the program.
// Iterators make the algorithm independent of the type of the container used.

// begin() = This function is used to return the beginning position of the container.
// end() = This function is used to return the after end position of the container.
// advance() = This function is used to increment the iterator position till the specified number mentioned in its arguments.
// next() = This function returns the new iterator that the iterator would point after advancing the positions mentioned in its arguments.
// prev() = This function returns the new iterator that the iterator would point after decrementing the positions mentioned in its arguments.

// container::const_iterator, used for constant containers

class Person{
    string name;
    public:
        Person(string n){
            name = n;
        }
        void getName(){
            cout << name << endl;
        }
};

int main(){
    vector<intv = {12345};
    vector<int>::iterator ptr = v.begin();

    for (ptr = v.begin(); ptr < v.end(); ptr++){
        cout << *ptr << endl;
    }

    ptr = v.begin();
    
    // points to 4 
    advance(ptr3);
    cout << *ptr << endl;

    vector<int>::iterator p1 = v.begin();
    vector<int>::iterator p2 = v.end();

    vector<int>::iterator it1 = next(p13);
    auto it2 = prev(p23);

    cout << *it1 << endl;
    cout << *it2 << endl;

    const vector<inta = {7519};
    vector<int>::const_iterator i = a.begin();

    vector<Personpv = {Person("Yash"), Person("ash"), Person("mary")};
    vector<Person>::iterator pi;
    for (pi = pv.begin(); pi < pv.end(); pi++){
        pi->getName();
    }

    return 0;
}
                    
                                            Lists

#include <iostream>
#include <list>
using namespace std;

// They have a better performance in inserting, moving, and extracting elements from any position.
// The std::list also does better with algorithms that perform such operations intensively.
// Lists are sequence containers that allow non-contiguous memory allocation.

/*
 Function               Description
- insert()          This function inserts a new item before the position the iterator points.
- push_back()       This function add a new item at the list's end.
- push_front()      It adds a new item at the list's front.
- pop_front()       It deletes the list's first item.
- pop_back()        It deletes the list's last item.
- size()            This function determines the number of list elements.
- front()           To determines the list's first items.
- back()            To determines the list's last item.
- reverse()         It reverses the list items.
- merge()           It merges two sorted lists.
- unique()          It deletes duplicate items of the list.
*/

int main(){

    list<intl1 = {1234};
    list<intl2 = {5678}; 

    for(int i : l2){
        cout << i << endl;
    }

    cout << "Size of l2 is " << l2.size() << endl;

    list<intl3 = {0000};
    l3.push_back(1);
    l3.push_front(1);
    l3.pop_back();
    l3.pop_front();
    l3.reverse();

    l1.merge(l2);
    cout << "Size of l1 is " << l1.size() << endl;
    l3.unique();
    cout << l3.size() << endl;
    return 0;
}                                                                    

                                    Arguments to Main

#include <iostream>
using namespace std;

// The arguments can have arbitrary name.
// argc and agrv
// The value of argv is a pointer to the initial element of an array of pointers, one for each argument.
// The value of argc is the number of pointers.  

//  The initial element of that array always represents the name by which the program is called.
// argc is always at least 1.

int factorial(int n){
    if (n == 1 || n == 0){
        return 1;
    }
    return n * factorial(n - 1);
}

int main(int argcchar** argv){
    if (argc > 1){
        // for (int i = 1; i < argc; i++){
            // cout << argv[i] << endl;
        // }
        int f = factorial((int)argv[1]);
        cout << f << endl;
        // cout << argv[1] << endl;
    }

    return 0;
}

                                                Ratio

#include <iostream>
#include <ratio>

/*
ratio arithmetic:
    ratio_add = Add two ratios
    ratio_subtract = Subtract ratios
    ratio_multiply = Multiply two ratios
    ratio_divide = Divide ratios

ratio comparison:
    ratio_equal = Compare ratios
    ratio_not_equal = Compare ratios for inequality
    ratio_less = Compare ratios for less-than inequality
    ratio_less_equal = Compare ratios for equality or less-than inequality
    ratio_greater = Compare ratios for greater than inequality
    ratio_greater_equal = Compare ratios for equality or greater-than inequality
*/

int main(){
    typedef std::ratio<1,3> one_third;
    typedef std::ratio<1,2> one_half;

    std::cout << "Numerator = " << one_half::num << " Denominator: " << one_half::den << std::endl;

    typedef std::ratio_add<one_half,one_third> sum;
    std::cout << sum::num << "/" << sum::den << std::endl;
    std::cout << std::ratio_less<one_third,one_half>::value << std::endl;
    std::cout << std::ratio_equal<one_third,one_half>::value << std::endl;
    return 0;
}

                                    Malloc

#include <iostream>
#include <cstdlib>
using namespace std;

// https://www.programiz.com/cpp-programming/library-function/cstdlib/malloc
// https://www.tutorialspoint.com/malloc-vs-new-in-c-cplusplus

// The function malloc() is used to allocate the requested size of bytes and it returns a pointer to the first byte of allocated memory.
// The malloc() function in C++ allocates a block of uninitialized memory and returns a void pointer to the first byte of the allocated memory block if the allocation succeeds.
// If the size is zero, the value returned depends on the implementation of the library. It may or may not be a null pointer.
// It returns null pointer, if fails.
// malloc() is defined in <cstdlib>
// void* malloc(size_t size);

int main(){
    int* ptr;
    ptr = (int*) malloc(5*sizeof(int));
    for (int i = 0; i < 5; i++){
        ptr[i] = i*2+1;
    }
    for (int i=0; i<5; i++)
    {
        cout << ptr+i << endl;
        cout << *(ptr+i) << endl;
    }
    int* a = &ptr[0];
    free(ptr);

    int* p = (int*) malloc(0);
    if (ptr == nullptr){
        cout << "Null Pointer" << endl;
    }
    else{
        cout << "Address = " << p << endl;
    }
    free(p);
    cout << *a << endl;
    return 0;
}

                                        Function Pointers

#include <iostream>
#include <vector>
using namespace std;

// http://www.dev-hq.net/c++/20--function-pointers
// https://dev.to/djzaamir/introduction-to-c-function-pointers-2ff1

// Function pointers behave differently from normal pointers
// data-type (*pointerName)(parameters);

void one(){cout << "One" << endl;}
void two(){cout << "Two" << endl;}
void add(int a, int b){cout << a+b << endl;}

void PrintValue(const int v){
    cout << "Value: " << v << endl;
}

void ForEach(const vector<int> values, void(*func)(int)){
    for(int value : values)
        func(value);
}

int main(){
    void (*fptr)();

    fptr = one;
    fptr();

    fptr = two;
    fptr();

    void (*fp)(int, int);
    fp = add;
    fp(3, 4);

    void (*fptr2[2])();
    fptr2[0] = one;
    fptr2[1] = two;
    fptr2[0]();
    fptr2[1]();

    auto a = add;
    a(1, 2);
    a(5, 5);

    vector<int> values = {1, 5, 4, 2, 3};
    // ForEach(values, PrintValue);
    ForEach(values, [](int v){cout << "Value: " << v << endl;});

    return 0;
}

                                              Namespaces

#include <iostream>
#include <string>

// Namespaces allow to group entities like classes, objects and functions under a name.
// This way the global scope can be divided in sub-scopes, each one with its own name.
// Namespaces allow us to group named entities that otherwise would have global scope into narrower scopes, giving them namespace scope.
// Using namespace, you can define the context in which names are defined.
// Syntax = Namespace identifier {entities}, Where identifier is any valid identifier and entities is the set of classes, objects and functions that are included within the namespace.

// Namespace declarations appear only at global scope.
// Namespace declarations can be nested within another namespace.
// Namespace declarations don't have access specifiers.
// Multiple namespace blocks with the same name are allowed. All declarations within those blocks are declared in the named scope.

// A namespace defines a scope.
// In order to access variables of the namespace outside it we have to use the scope operator ::

// The functionality of namespaces is especially useful in the case that there is a possibility that a global object or function uses the same identifier as another one.

// We can declare alternate names for existing namespaces according to the following format: namespace new_name = current_name;

namespace Namespace
{
    int a, b;
}

namespace first{
    namespace second{
        int n;
    }
}

namespace ns 
{ 
    void print();

    class geek
    { 
    public: 
        void display()
        { 
            std::cout << "ns::geek::display()" << std::endl; 
        } 
    }; 
}

void ns::print(){
    std::cout << "Hello World" << std::endl;
}

int main(){
    ns::geek obj;
    obj.display();

    Namespace::a = 3;
    Namespace::b = 7;
    first::second::n = 12;

    namespace name = ns;
    return 0;
}

                                         using

#include <iostream>
using std::cout;

// The keyword using is used to introduce a name from a namespace into the current declarative region.
// This directive tells the compiler that the subsequent code is making use of names in the specified namespace.
// using and using namespace have validity only in the same block in which they are stated or in the entire code if they are used directly in the global scope.
// The ‘using’ directive can also be used to refer to a particular item within a namespace.
// Names introduced in a using directive obey normal scope rules. The name is visible from the point of the using directive to the end of the scope in which the directive is found. Entities with the same name defined in an outer scope are hidden.

namespace first{
    void func(){
        cout << "Inside First" << std::endl;
    }
    namespace second{
        void func(){
            cout << "Inside Second" << std::endl;
        }
    }
}

int main(){
    using namespace first::second;
    func();
    using namespace first;
    first::func();
    return 0;
}

                                      Smart Pointers

#include <iostream>
#include <memory>
using namespace std;

// Using Smart Pointers, we can make pointers to work in a way that we don't need to explicitly call delete.
// A smart pointer is a wrapper class over a pointer with an operator like * and -> overloaded.
// You cannot copy unique pointers because if one dies it will free the memory.

// Unique pointer uses new and delete itself.
// It deletes itself to prevent memory leak when the scope ends.

// Shared_ptr works by creating a reference count and allocates another block of memory for it.
// When the reference counter reaches zero it frees the memory.

// Weak pointer is a form of shared_ptr.
// It does not increases the reference counter.

class Entity
{
    public:
    Entity()
    {
        std::cout << "Created Entity" << std::endl;
    }
    ~Entity()
    {
        std::cout << "Destroyed Entity" << std::endl;
    }
    void print(){}
};

int main()
{
    {
        std::shared_ptr<Entity> e;
        {
        // std::unique_ptr<Entity> e(new Entity());
            std::unique_ptr<Entity> unique = std::make_unique<Entity>();
            unique->print();

            std::shared_ptr<Entity> shared = std::make_shared<Entity>();
            e = shared;

            std::weak_ptr<Entity> weak = shared;
        }
    }
    return 0;
}

                                         Dynamic Allocation

// https://youtu.be/q8j8EqCZcWM

// Dynamic memory allocation is a way in which the size of a data structure can be changed during the runtime.
// The memory is allocated in the heap segment.
// malloc, calloc, realloc, free

/*
    Malloc

-> It stands for memory allocation.
-> It reserves a block of memory with the given amount of bytes.
-> The return value is a void pointer to the allocated space.
-> Therefore the void pointer needs to be cast to the appropriate type as per the requirements.
-> If the space is insufficient, allocation of memory fails and it returns a NULL pointer.
-> All the values at allocated memory are initialized to garbage values.

syntax: type* name = (ptr_type*) malloc(size_in_bytes);
*/

/*
    Calloc

-> It stands for contiguous allocation.
-> It reserves n blocks of memory with the given amount of bytes.
-> The return value is a void pointer to the allocated space.
-> If the allocation fails, it returns a nullptr.
-> All the values are initialized to 0

syntax: ptr = (ptr_type*) calloc(n, size_in_bytes);
*/

/*
    Realloc

-> It stands for reallocation.
-> If the dynamically allocated memory is insufficient we can change the size of previously allocated memory using it.

syntax: ptr = (ptr_type*) realloc(ptr, new_size_in_bytes);
*/

/*
    Free

-> Free is used to free the allocated memory
-> If the dynamically allocated memory is not required anymore, we can free it.

syntax: free(ptr);
*/

#include <iostream>

int main()
{
    int* mptr = (int*) malloc(sizeof(int) * 5);
    for(int i = 0; i < 5; i++){
        std::cout << &mptr[i] << std::endl;
        std::cout << mptr[i] << std::endl;
    }
    free(mptr);

    int* cptr = (int*) calloc(5, sizeof(int));
    for (int i = 0; i < 5; i++){
        std::cout << cptr[i] << std::endl;
    }

    std::cout << "Realloc Starts" << std::endl;
    int* rptr = (int*) realloc(cptr, 7*sizeof(int));
    for(int i = 0; i < 7; i++){
        std::cout << rptr[i] << std::endl;
    }
    free(rptr);
    free(cptr);
    return 0;
}

                                                Chars

// https://www.learncpp.com/cpp-tutorial/chars/
// char = A single character, enclosed in single quotes(').
// The char data type is an integral type, meaning the underlying value is stored as an integer
// http://www.asciitable.com/
// The fixed width integer int8_t is usually treated the same as a signed char in C++, so it will generally print as a char instead of an integer.

/*
    Types

- char = 1 byte
- wchar_t = 2 bytes(minimum is 1)
- char16_t = 2 bytes
- char32_t = 4 bytes

*/

#include <iostream>

char c = 'y';
char a {'a'}; // preferred
char a2 {97}; // not preferred
wchar_t wc = 'y';
char16_t c2 = 'y';
char32_t c4 = 'y';

int main(){
    std::cout << sizeof(c) << std::endl;
    std::cout << sizeof(wc) << std::endl;
    std::cout << sizeof(c2) << std::endl;
    std::cout << sizeof(c4) << std::endl;

    std::cout << static_cast<int>(c) << std::endl; // It will print the ascii code for 'y'
}

                                                    Bool

// bool = Boolean variables are variables that can have only two possible values: true, and false.
// To declare a Boolean variable, we use the keyword bool.
// To initialize or assign a true or false value to a Boolean variable, we use the keywords true and false.
// Default is false.

// The logical NOT operator (!) can be used to flip a Boolean value from true to false, or false to true.

// Boolean values are not actually stored in Boolean variables as the words “true” or “false”. Instead, they are stored as integers: true becomes the integer 1, and false becomes the integer 0. Similarly, when Boolean values are evaluated, they don’t actually evaluate to “true” or “false”. They evaluate to the integers 0 (false) or 1 (true). Because Booleans actually store integers, they are considered an integral type.

#include <iostream>
using namespace std;

// If you want std::cout to print “true” or “false” instead of 0 or 1, you can use std::boolalpha. You can use std::noboolalpha to turn it back off.

int main(){
    bool b1 = true;
    bool b2 = false;
    bool b3 {!false};
    bool b4 {}; // default

    cout << b1 << '\n'; // 1
    cout << b2 << '\n'; // 0

    cout << boolalpha;

    cout << b3 << '\n'; // true
    cout << b4 << '\n'; // false

    cout << noboolalpha;

    bool b5 = 0;
    bool b6 = 1;

    return 0;
}

                                                       Clock and Time

#include <iostream>
#include <ctime>
#include <cmath>
using namespace std;

// In order to compute the processor time, the difference between values returned by two different calls to clock(), one at the start and other at the end of the program is used.
//  To convert the value to seconds, it needs to be divided by a macro CLOCKS_PER_SEC.

// The clock() time may advance faster or slower than the actual wall clock. It depends on how the operating system allocates the resources for the process.
// If the processor is shared by other processes, the clock() time may advance slower than the wall clock. While if the current process is executed in a multithreaded system, the clock() time may advance faster than wall clock.

// The clock() function is defined in the ctime header file. The clock() function returns the approximate processor time that is consumed by the program.

// Syntax: clock_t clock( void ); 
// Return Value: This function returns the approximate processor time that is consumed by the program and on failure function returns -1 that is casted to the type clock_t.

int main(){
    float y;
    clock_t time;
    time = clock();
    for(int i = 0; i < 100000; i++){
        y = pow(i, 5);
    }
    time = clock() - time;
    cout << "Time taken: " << (float) time/CLOCKS_PER_SEC << " seconds" << endl;

    time = clock();
    for(int i = 0; i < 100000; i++){
        y = i*i*i*i*i;
    }
    time = clock() - time;
    cout << "Time taken: " << (float) time/CLOCKS_PER_SEC << " seconds" << endl;
    return 0;
}

#include <chrono>
#include <iostream>

int main(){
    auto start = std::chrono::high_resolution_clock::now();
    // funtions and statements
    auto finish = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed = finish - start;
    std::cout << "Elapsed time: " << elapsed.count() << std::endl;
}

                                                        unsigned

// Unsigned integers are integers that can only hold non-negative whole numbers.
// To define an unsigned integer, we use the unsigned keyword. By convention, this is placed before the type

unsigned short us;
unsigned int ui;
unsigned long ul;
unsigned long long ull;

// A 1-byte unsigned integer has a range of 0 to 255. Compare this to the 1-byte signed integer range of -128 to 127. Both can store 256 different values, but signed integers use half of their range for negative numbers, whereas unsigned integers can store positive numbers that are twice as large.
// An n-bit unsigned variable has a range of 0 to (2n)-1.

/*
    Unsigned integer overflow

-> By definition, unsigned integers cannot overflow. Instead, if a value is out of range, it is divided by one greater than the largest number of the type, and only the remainder kept.
-> Example: The number 280 is too big to fit in our 1-byte range of 0 to 255. 1 greater than the largest number of the type is 256. Therefore, we divide 280 by 256, getting 1 remainder 24. The remainder of 24 is what is stored.
-> It’s possible to wrap around the other direction as well. 0 is representable in a 1-byte integer, so that’s fine. -1 is not representable, so it wraps around to the top of the range, producing the value 255. -2 wraps around to 254. And so forth.
// modulo wrapping

*/

Comments