// Author : Ozgur Cobanoglu, 5 May 2005, Torino
// Ozgur.Cobanoglu@cern.ch, cobanogl@to.infn.it
//
// This simple code corresponds to my hand calculations for the
// course of microelectronics lectured by Angelo Rivetti in 2005.
//
// This code provides some simple numerical methods for the problems
// which have already been solved analitically.

#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define separator printf("______________________________[Page:%d]\n", sepCou++);
#define ERR printf("FATAL ERROR !..\n"); exit(1);
#define db   printf("---------\n");
#define dbdb printf("+++++++++\n");

bool FUNCTION_TRACING_ON = false; // set this to true whereas needed
int sepCou=2; // the first page that has a question is 2

// global technological parameters gathered from page 1

double MNCOX   = 200;       // microA/Vsquare, uA/V^2
double MPCOX   = 50;        // microA/Vsquare, uA/V^2
double VTHN    = 0.5;       // volt
double VTHP    = -0.5;      // volt
double GAMMAN  = 0.5;       // sqrt(volt)
double GAMMAP  = 0.5;       // sqrt(volt)
double LAMBDAN = 0.02;      // 1/volt
double LAMBDAP = 0.02;      // 1/volt
double _2QF    = 0.6;       // volt
double TOX     = 5;         // nm
double COX     = 5;         // fF/microMeterSquare, fF/umV^2
double KN      = MNCOX;     // alias
double KP      = MPCOX;     // alias

//______________________________________________________________________________
double Vth(double vsb, const char* type="nmos")
{
    double vth;
    
    if (!strcmp(type, "nmos")) {
        vth = VTHN+GAMMAN*(sqrt(fabs(_2QF+vsb))-sqrt(fabs(_2QF)));
    } else if (!strcmp(type, "pmos")) {
        vth = VTHP+GAMMAP*(sqrt(fabs(_2QF+vsb))-sqrt(fabs(_2QF)));
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON)
		printf("## Function Vth     ## Vsb:%f type:%s [Vth=%f]\n",
		vsb, type, vth);
    
    return vth;
}

//______________________________________________________________________________
double Ids(double VGS,
		   double VDS,
		   double W,
		   double L,
		   double vsb=0,
		   const char* type="nmos")
{
    double ids;
    char region[3];
    double VTH = Vth(vsb, type);
    
    if (!strcmp(type, "nmos")) { // decide region
        if (VGS>VTH && VDS>VGS-VTH) {
            sprintf(region, "sat");
        } else if (VGS>VTH && VDS<=VGS-VTH) {
            sprintf(region, "tri");
        } else if (VGS<VTH) {
            sprintf(region, "off");
        } else {
            ERR
        }
    } else if (!strcmp(type, "pmos")) {
        if (VGS<VTH && -VDS>-VGS-fabs(VTH)) {
        //if (VGS<VTH && -VDS>-VGS-VTH) {
            sprintf(region, "sat");
        } else if (VGS<VTH && -VDS<=-VGS-fabs(VTH)) {
        //} else if (VGS<VTH && -VDS<=-VGS-VTH) {
            sprintf(region, "tri");
        } else if (VGS>VTH) {
            sprintf(region, "off");
        } else {
            ERR
        }
    }
    
    if (!strcmp(region, "sat")) { // calculate Ids
        if (!strcmp(type, "nmos")) {
            ids = 0.5*MNCOX*(W/L)*(VGS-VTH)*(VGS-VTH)*(1+LAMBDAN*VDS);
        } else if (!strcmp(type, "pmos")) {
            ids = -0.5*MPCOX*(W/L)*(fabs(VGS)-fabs(VTH))\
			      *(fabs(VGS)-fabs(VTH))*(1+LAMBDAP*fabs(VDS));
        } else {
            ERR
        }
    } else if (!strcmp(region, "tri")) {
        if (!strcmp(type, "nmos")) {
            ids = MNCOX*(W/L)*((VGS-VTH)*VDS-0.5*VDS*VDS);
        } else if (!strcmp(type, "pmos")) {
            ids = -MPCOX*(W/L)*((VGS-fabs(VTH))*VDS-0.5*VDS*VDS);
        } else {
            ERR
        }
    } else if (!strcmp(region, "off")) {
        ids = 0;
    } else {
        ERR;
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Ids     ## Vsb:%f Vth:%f Vgs:%f Vds:%f W/L:%f Type:%s Region:%s [Ids=%f uA]\n", vsb, VTH, VGS, VDS, W/L, type, region, ids);
    
    return ids;
}

//______________________________________________________________________________
double gm_1(double W,
            double L,
			double Vgs,
			double Vds,
			double Vsb=0,
			const char* type="nmos")
{
// Razavi, page 21, eq.(2.17), channel length modulated

    double gm;
    double VTH = Vth(Vsb, type);
    
    if (!strcmp(type, "nmos")) {
        gm = MNCOX*(W/L)*(Vgs-VTH)*(1+LAMBDAN*Vds);
    } else if (!strcmp(type, "pmos")){
        gm = MPCOX*(W/L)*(Vgs-VTH)*(1+LAMBDAP*Vds);
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Gm_1    ## Vsb:%f \
	                         Vth:%f Vgs:%f W/L:%f Type:%s [gm_1=%f uMho]\n", \
							 Vsb, VTH, Vgs, W/L, type, gm);
    
    return gm;
}

//______________________________________________________________________________
double gm_2(double Id,
		    double W,
			double L,
			double Vds,
			const char* type="nmos")
{
// Razavi, page 21, eq.(2.18), channel length modulated

    double gm;
    
    if (!strcmp(type, "nmos")) {
        gm = sqrt((2*MNCOX*(W/L)*Id)/(1+LAMBDAN*Vds));
    } else if (!strcmp(type, "pmos")) {
        gm = sqrt((2*MPCOX*(W/L)*Id)/(1+LAMBDAP*Vds));
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Gm_2    ## Id:%f \
	                         W/L:%f Type:%s [gm_2=%f uMho]\n", Id,\
							 W/L, type, gm);
    
    return gm;
}

//______________________________________________________________________________
double gm_3(double Id,
		    double Vgs,
			double Vsb=0,
			const char* type="nmos")
{
// Razavi, page 21, eq.(2.19)

    double gm;
    double VTH = Vth(Vsb, type);
    
    gm = (2*Id)/(Vgs-VTH);
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Gm_3    ## Id:%f \
	                         Vgs:%f Vsb:%f Type:%s [gm_3=%f uMho]\n", \
							 Id, Vgs, Vsb, type, gm);
    
    return gm; // uMho or uS
}

//______________________________________________________________________________
double gm_triodeRegion(double Vds,
				       double W,
					   double L,
					   double Vsb=0,
					   const char* type="nmos")
{
// Razavi, page 22, eq.(2.21)

    double gm;
    double VTH = Vth(Vsb, type);
    
    if (!strcmp(type, "nmos")) {
        gm = MNCOX*(W/L)*Vds;
    } else if (!strcmp(type, "pmos")) {
        gm = MPCOX*(W/L)*Vds;
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Gm_t    ## Vds:%f Vsb:%f Type:%s [gm_t=%f uMho]\n", Vds, Vsb, type, gm);
    
    return gm;
}

//______________________________________________________________________________
double Vgs_sat(double Id,
			   double W,
			   double L,
			   double Vds,
			   double Vsb=0,
			   const char* type="nmos")
{
// Derived from the clasic Id formule for saturation region

    double vgs, VTH = Vth(Vsb, type);
    
    if (!strcmp(type, "nmos")) {
        vgs = VTH+sqrt((2*Id*L)/(MNCOX*W*(1+LAMBDAN*Vds)));
    } else if (!strcmp(type, "pmos")) {
        vgs = -(fabs(VTH)+sqrt((-2*Id*L)/(MPCOX*W*(1+LAMBDAP*Vds))));
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Vgs_sat ## Id:%f W/L:%f/%f Vsb:%f Type:%s [Vgs_sat=%f]\n", Id, W, L, Vsb, type, vgs);
    
    return vgs;
}

//______________________________________________________________________________
double Vgs_tri(double Id,
			   double W,
			   double L,
			   double Vds,
			   double Vsb=0,
			   const char* type="nmos")
{
// Derived from the clasic Id formulae for triode region

    double vgs, VTH = Vth(Vsb, type);
    
    if (!strcmp(type, "nmos")) {
        vgs = VTH+((Id*L)/(MNCOX*W)+Vds*Vds*0.5)/Vds;
    } else if (!strcmp(type, "pmos")) {
        vgs = -(fabs(VTH)+((Id*L)/(MPCOX*W)+Vds*Vds*0.5)/Vds);
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Vgs_tri ## Id:%f Vds:%f W/L:%f/%f Vsb:%f Type:%s [Vgs_tri=%f]\n", Id, Vds, W, L, Vsb, type, vgs);

    return vgs;
}

//______________________________________________________________________________
double r0(double Id,
		  const char* type="nmos")
{
// [Id]=uA

    double r;
    
    if (!strcmp(type, "nmos")) {
        r=1/(LAMBDAN*Id*0.000001);
    } else if (!strcmp(type, "pmos")) {
        r=1/(LAMBDAP*Id*0.000001);
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function r0     ## Id:%f type:%s [r0=%f MOhm]\n", Id, type, r/1000000);
    
    return r; // Ohm
}

//______________________________________________________________________________
double gmb(double gm,
		   double Vsb,
		   const char* type="nmos")
{
    double g;
    
    if (!strcmp(type, "nmos")) {
        g=gm*(GAMMAN/(2*sqrt(_2QF+Vsb)));
    } else if (!strcmp(type, "pmos")) {
        g=gm*(GAMMAP/(2*sqrt(_2QF+Vsb)));
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function gmb    ## gm:%f Vsb:%f type:%s [gmb=%f]\n", gm, Vsb, type, g);
    
    return g;
}

//______________________________________________________________________________
double Vds_sat(double Id,
			   double W,
			   double L,
			   double Vgs,
			   double Vsb=0,
			   const char* type="nmos")
{
// Derved from the Id formula

    double v, VTH = Vth(Vsb, type);
    
    if (!strcmp(type, "nmos")) {
        v = ((2*Id*L)/(MNCOX*W*(Vgs-VTH)*(Vgs-VTH))-1)/LAMBDAN;
    } else if (!strcmp(type, "pmos")) {
        v = ((2*Id*L)/(MPCOX*W*(Vgs-VTH)*(Vgs-VTH))-1)/LAMBDAP;
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Vds_sat ## Id:%f W/L:%f/%f Vgs:%f Vsb:%f Type:%s [Vds_sat=%f]\n", Id, W, L, Vgs, Vsb, type, v);
    
    return v;
}

//______________________________________________________________________________
double Vds_tri(double Id,
			   double W,
			   double L,
			   double Vgs,
			   double Vsb=0,
			   const char* type="nmos")
{
// Derved from the Id formula

    double v, VTH = Vth(Vsb, type);
    
    if (!strcmp(type, "nmos")) {
        v = (Vgs-VTH)*(1+Vgs-VTH)-(2*Id*L)/(MNCOX*W);
    } else if (!strcmp(type, "pmos")) {
        v = (Vgs-VTH)*(1+Vgs-VTH)-(2*Id*L)/(MPCOX*W);
    } else {
        ERR
    }
    
    // function tracing
    if (FUNCTION_TRACING_ON) printf("## Function Vds_tri ## Id:%f W/L:%f/%f Vgs:%f Vsb:%f Type:%s [Vds_tri=%f]\n", Id, W, L, Vgs, Vsb, type, v);
    
    return v;    
}

//______________________________________________________________________________
double Av_CS_sd(double gm, double Rd, double Rs)
{
// CS stage with source degeneration, page 62, eq. 3.56
// [gm]=uMho, [Rd, Rs]=Ohm

    return -Rd/(1000000/gm+Rs);
}

//______________________________________________________________________________
double Ron()
{
// Page 18, eq. 2.11
    
    return 0.0;
}

//______________________________________________________________________________
void kahve()
{
// For pre-printing a vertical space so that the output is more readable.

    printf("        (   )       \t\ttracing : %d \n", FUNCTION_TRACING_ON);
	printf("          )         \t\tMNCOX   = %f microA/Vsquare, uA/V^2\n",\
                                                                    MNCOX);
	printf("        ( (         \t\tMPCOX   = %f microA/Vsquare, uA/V^2\n",
                                                                 MPCOX   );
	printf("         ))         \t\tVTHN    = %f volt \n", VTHN);
	printf("    _..,-(--,.._    \t\tVTHP    = %f volt\n", VTHP);
	printf(" .-;'-.,____,.-';   \t\tGAMMAN  = %f sqrt(volt)\n", GAMMAN);
	printf("(( |            |   \t\tGAMMAP  = %f sqrt(volt)\n", GAMMAP);
	printf(" '))            ;   \t\tLAMBDAN = %f 1/volt\n", LAMBDAN);
	printf("   '\\          /   \t\tLAMBDAP = %f 1/volt\n", LAMBDAP);
	printf(" .-'`',.____.,'`'-. \t\t_2QF    = %f volt\n", _2QF);
	printf("(     '------'     )\t\tTOX     = %f nm\n", TOX);
	printf(" '-=..________..--' \t\tCOX     = %f fF/microMeterSquare, fF/umV^2\
                                                 \n", COX);
}

//______________________________________________________________________________
int main()
{
    kahve();
    
    separator // page 2   
    if (false) { 
        printf("<page 2 figure 1> Ids=%f uA\n\n", Ids(1.3, 1, 10, 1));
        printf("<page 2 figure 2> Ids=%f uA\n\n", Ids(1.3, 0.3, 10, 2));
        printf("<page 2 figure 3> Ids=%f uA\n\n", Ids(0.3, 0.3, 10, 2));
    }
    
    separator // page 3
    if (false) {
        printf("<page 3 figure 1> Ids=%f uA\n\n", Ids(0.8, 0.8, 10, 0.4, 0.5));
        printf("<page 3 figure 2> Ids=%f uA\n\n", Ids(-0.7, -2, 2, 0.4, 0,\
		                                          "pmos"));
        printf("<page 3 figure 3> Ids=%f uA\n\n", Ids(-0.7, -2, 2, 1, -0.5,\
		                                          "pmos"));
    }
    
    separator // page 4
    if (false) {
        // Vout could be Vdd/2 = 1.25 V. I will find the Vgate value
		// that guarantees Id=20uA
        printf("<page 4 figure 1> Vgate:%f mV, gm_1=%f uMho, gm_2=%f uMho,\
		    gm_3=%f uMho, Av:%f M\n\n",
            1000*Vgs_sat(20, 10, 0.4, 1.25),
            gm_1(10, 0.4, Vgs_sat(20, 10, 0.4, 1.25), 1.25),
            gm_2(20, 10, 0.4, 1.25),
            gm_3(20, Vgs_sat(20, 10, 0.4, 1.25)),
			-gm_2(20, 10, 0.4, 1.25)*r0(20)/1000000000
        );
        // Do the same thing for the second figure
        printf("<page 4 figure 2> Vgate=%f mV, gm_1=%f uMho, gm_2=%f uMho,\
		    gm_3=%f uMho, Av:%f M\n\n",
            1000*Vgs_sat(20, 1, 2, 1.25),
            gm_1(1, 2, Vgs_sat(20, 1, 2, 1.25), 1.25),
            gm_2(20, 1, 2, 1.25),
            gm_3(20, Vgs_sat(20, 1, 2, 1.25)),
			-gm_2(20, 1, 2, 1.25)*r0(20)/1000000000
        );
        // For finding the minimum Vds while keeping constant Id=20uA,
		// I will search for such a
        // Vds value that makes the Veff equal to Vth of the transistor.
		// Plot the output below.
        if (false) {
            for (double i=1.25 ; i>-0.01 ; i-=0.01) { // i represents the Vds
                printf("%f\t%f\n", i, Vgs_tri(20, 10, 0.4, i));
            }
        }
        // When Vds is approximately 89.4mV, Vgs is forced to decrease
		// below Vth so the transistor becomes off, for the first figure.
		// For the second one :
        if (false) {
            for (double i=2.5 ; i>0.1 ; i-=0.05) { // i represents the Vds
                printf("%f\t%f\n", i, Vgs_tri(20, 1, 2, i));
            }
        }
        // When Vds is approximately 632mV, Vgs is forced to
		// decrease below Vth so the transistor becomes off,
		// for the second figure on page 4.
    }

    separator // page 5
    // Since I still want Vout to be 1.25 V, current flowing through
	// 100K resistor is 12.5uA
    // 12.5+20=32.5uA, this is the value that I want my transistor to
	// sink, as a result Vgate must change :
    if (false) {
        printf("<page 5 figure 1> Vgate=%f V\n\n", Vgs_sat(32.5, 10, 0.4,\
		                                           1.25));
        printf("<page 5 figure 2> Vgate=%f V\n\n", Vgs_sat(32.5, 1, 2, 1.25));
    }
    
    // page razavi 22
    // Just to be sure that the functions are working;
	// solved Razavi, example 2.2
    // Set FUNCTION_TRACING_ON to false and plot the output of this part
    // (W/L and Vb are arbitrarily choosen as 10/1 and 0.55V respectively)
    // See "pic/example_2.2_page_22.gif"
    if (false) {
        printf("Example 2.2, page 22, Razavi\n\n");
        for (double i=0 ; i<2 ; i+=0.1) { // i represents Vds
            if (i<=0.55) printf("%f\t%f\n", i, gm_triodeRegion(i, 10.0, 1.0));
            if (i>0.55)  printf("%f\t%f\n", i, gm_1(10, 1, 1, i));
        }
    }
    
    separator // page 6
    if (false) {
        // I would like Vout to be 1.25V so the current flowing 
		// through the resistor is 12.5uA
        // The current the transistor must sink is 20+12.5=32.5uA
        printf("<page 6 figure 1> Vgate=%f V\n\n", 2.5+Vgs_sat(-32.5, 10, 0.4,\
		                                         -1.25, 0, "pmos"));
        printf("<page 6 figure 2> Vgate=%f V\n\n", 2.5+Vgs_sat(-32.5, 1, 2,\
		                                         -1.25, 0, "pmos"));
        // If Vgate increases, the transistor tends to flow less current
		// while the ideal current source
        // tends to keep the current constant. At the end, the negotiation
		// is done as follows :
        // Resistor current would decrease, Vout would decrease too.
        // If Vgate decreases, transistor tends to flow more current while
		// ideal current source tends to
        // keep it as it is. In this case resistor current and Vout increase.
        // In both cases, the small signal which exeeds a certain limit
		// would be truncated at output to fit into
        // the "window" defined by Vdd and Vout.
        // Vgates are approximately found to be 1.769060 V and 0.367007 V
		// respectively.
    }
    
    separator // page 7
    // Arbitrary method 1
    // First, find the Rbias, for 10uA I must find the gate, that is, also Vds
    // { Curly paranthezies are used to isolate the variables declared 
	// within this part from the rest of the code }
    if (false) {
        double firstGuess = Vgs_sat(10, 2, 1, 0); // I ignored channel length
        double selfExistantSecondGuess = Vgs_sat(10, 2, 1, firstGuess);
		// this must get closer to the real result
        printf("<page 7 the only figure> Vgs(M3)=%f mV\n", \
			1000*Vgs_sat(10, 2, 1, selfExistantSecondGuess)); // even better
        // The voltage difference between Rbias is 2.5-0.722010=1.77799V
		// and I=10uA and V=RI so : 
        printf("<page 7 the only figure> Rbias=%f KOhm\n\n",
		    1000*(2.5-Vgs_sat(10, 2, 1, selfExistantSecondGuess))/10);
    // Arbitrary method 2
        // I want Vout to be 1.25V so Vgate for M2 is as follows :
        double vgs, id, rbias;
        vgs=Vgs_sat(50, 10, 1, 1.25);
        printf("<page 7 the only figure> Vgs(M2):%f mV\n", vgs*1000);
        // This is also the Vgate and Vds for M3, let me check if the
		// current is 10uA for M3
        id=Ids(vgs, vgs, 2, 1);
        printf("<page 7 the only figure> Id(M3):%f uA\n", id);
        // As seen at the output Id(M3)~9.896754uA, reasonably close to
		// 10uA. So Rbias is (V=IR) :
        rbias=(2.5-vgs)/id;
        printf("<page 7 the only figure> Rbias=%f KOhm\n", rbias*1000);
        // The two arbitrary methods are close to each other as a verification.
        // Let us calculate the Vgate for M1 driver transistor :
        printf("<page 7 the only figure> Vgate(M1):%f V\n", \
			2.5+Vgs_sat(-50, 20, 1, -1.25, 0, "pmos"));
        // Vgate=1.168V is calculated; coherent with CAD that gives 1.46V
        // Now the second part : use an NMOS device as the input transistor.
        // Rbias, Vgate of M2 and M3 do not change since they are isolated
		// from the input device
        // But I have to re-calculate the Vgate of M1 which is the input 
		// device (attention : bulk effect)
        printf("<page 7 second part> Vgate:%f V\n\n",\
			1.25+Vgs_sat(50, 20, 1, 1.25, 1.25));
        // Note that Vth=0.792775 (bigger because of bulk effect) and
		// Vgate=2.2
    }
    
    separator // page 8
    // I want 50uA so the voltage on 4KOhm(20Kohm) resistor is 0.2mV(1V),
	// Vsb=0.2V and Vds=1.3V, Vs=0.2
    // Vgate=Vgs+Vs
    // For calculating voltage gain I must know gm then call Av_CS_sd()
	// function so :
    if (false) {
        double gm    = gm_2(50, 40, 1, 1.3, "nmos");
        double Av    = Av_CS_sd(gm, 20000, 4000);
        double Vgate = 0.2+Vgs_sat(50, 40, 1, 1.3, 0.2, "nmos");
        printf("<page 8 figure 1> Vgate:%f V Av:%f\n", Vgate, Av);
    }
    // For figure 2, Vsb=2.3-2.5=-0.2V, Vout=1V, Vs=2.3V so :
    // For calculating voltage gain first I must know gm :
    if (false) {
        double gm    = gm_2(50, 40, 1, -1.3, "pmos");
        double Av    = Av_CS_sd(gm, 20000, 4000);
        double Vgate = 2.3+Vgs_sat(-50, 40, 1, -1.3, -0.2, "pmos");
        printf("<page 8 figure 2> Vgate:%f V Av:%f\n", Vgate, Av);
    }
    // Vsb=0, Vs=2.3V  so :
    // For calculating voltage gain first I must know gm :
    if (false) {
        double gm    = gm_2(50, 40, 1, -1.3, "pmos");
        double Av    = Av_CS_sd(gm, 20000, 4000);
        double Vgate = 2.3+Vgs_sat(-50, 40, 1, -1.3, 0, "pmos");
        printf("<page 8 figure 3> Vgate:%f V Av:%f\n\n", Vgate, Av);
    }
    
    separator // page 9
    if (false) {
        // I am going to scan the whole range, I will find the Id value that
		// requires Vgate to be 1.5 and
        // in this case I will get the Vth, Vout and V1 values since I will
		// know the Id value.
        // Vout=2.5V-RdId, V1=RsId => Vds=Vout-V1=2.5-15000Id
        if (false) {
            double Vgate;
            for (double Id=1.0 ; Id<250.0 ; Id+=1.0) {
                Vgate=0.005*Id+Vgs_sat(Id, 20, 1, 2.5-0.015*Id,\
				               0.005*Id, "nmos");
                if (Vgate<1.6 && Vgate>1.4) printf("Id=%f\tVgate=%f\n", Id,\
				                            Vgate);
            }
        }
        // As seen at the out put of the above loop, when Id=120uA, Vgate
		// becomes ~1.5 and in this case Vth~0.66V
        // So Vout=1.3V, V1=0.6V. Find the gm :
        printf("<page 9 the only figure> gm:%f uMho\n\n", gm_3(120, 0.9, 0.6));
    }
    
    separator // page 10
    if (false) {
        // I must know Id. I will use the same technic
        // as used in the previous exemple.
        if (false) {
            double Vgate;
            for (double Id=0 ; Id<10000 ; Id+=0.01) {
                Vgate=Vgs_sat(Id, 20, 1, 2.5-0.01*Id, 0, "nmos");        
                if (Vgate<1.50001 && Vgate>1.49999)
					printf("Id=%f\tVgate=%f\n", Id, Vgate);
            }
        }
        // As seen at the out put of the above loop, when Id=1.5mA, 
		// Vgate becomes 1.5 but in this case Vout=-17.5V which is impossible !
        // That means transistor is in triod region and the corresponding
		// formula must be used.
		if (false) {
            double Vgate;
            for (double Id=100 ; Id<500 ; Id+=0.01) {
                Vgate=Vgs_tri(Id, 20, 1, 2.5-Id/100, 0, "nmos");        
                if (Vgate<1.51 && Vgate>1.49)
					printf("Id=%f\tVgate=%f\n", Id, Vgate);
            }
        }
		// Id=243.71 is found from the output of the above loop.
		// So Vout=2.5-Rd*Id=63mV and the transistor is in triode region so :
        printf("<page 10 figure 1> gm:%f uMho\n", gm_3(243.71, 1.5));
        // yielding gm=487.42uMho and  Av=~0 since the transistor is in
		// triode region, it is attenuating.
        // For figure 2, I can find Vout. Put Vgs=Vgate-Vs and take Vs out.
		// Solving that :
        // Vout=0.8204171436 Volt is found or numericaly I can search for
		// Vout that makes the Vgate 1.5V :
        // Remember Vgs=Vgate-Vs => Vgate=Vgs+Vs
        if (false) {
            double Vgate;
            for (double Vout=0 ; Vout<2.5 ; Vout+=0.001) {
                Vgate=Vout+Vgs_sat(100, 30, 1, 2.5-Vout, Vout, "nmos");
                if (Vgate<1.51 && Vgate>1.49) {
                    printf("Vgate=%f\tVout=%f\n", Vgate, Vout);
                }
            }
        }
        // yielding Vout=650mV
    }
  
    separator // page 11
    if (false) {
        // I must find Rbias for 30 uA :
        double ids;
        for (double Rbias=58561 ; Rbias<58562 ; Rbias+=0.01) {
            ids = Ids(2.5-3*0.00001*Rbias, 2.5-3*0.00001*Rbias, 10, 2, 0,\
			      "nmos");
            if (ids<30.0001 && ids>29.9999) printf("Ids:%f\tRbias:%f\n", \
			                                ids, Rbias);
        }
    // yielding 58561.71 Ohm, that is, approximately 58.5 KOhm    
    }
    
    separator // page 12
    if (false) {
        // Rbias must be found for 100 uA.
        if (false) {
			double ids;
        	for (double Rbias=0 ; Rbias<120000 ; Rbias+=1) {
            	ids = Ids(0.0001*Rbias-2.5, 0.0001*Rbias-2.5, 30, 3, 0, "pmos");
            	if (ids>-100.1 && ids<-99.9)
					printf("Ids:%f\tRbias:%f\n", ids, Rbias);
        	}
		}
    // yielding 13.745KOhm
		if (false) {
			double ids0, ids1, ids2;
            ids0 = Ids(1.375-2.5, 1.375-2.5, 30, 3, 0, "pmos");
			printf("1st, Ids:%f uA\n", ids0);
            ids1 = Ids(1.375-2.5, -1, 60, 3, 0, "pmos");
			printf("2nd, Ids:%f uA\n", ids1);
            ids2 = Ids(1.375-2.5, -2, 60, 3, 0, "pmos");
			printf("3rd, Ids:%f uA\n", ids2);
			printf("<Page 12> DeltaI:%f uA\n", fabs(ids2-ids1));
		}
	// Yielding :
	// 1st, Ids:-99.853516 uA
	// 2nd, Ids:-199.218750 uA
	// 3rd, Ids:-203.125000 uA
	// DeltaI=3.906250 uA
    }
    
    separator // page 13
    if (false) {
        // For 40 uA, Vgate must be found for T3 (Vgs=Vgate-Vs => Vgate=Vgs+Vs)
        double ids;
        if (false) {
            for (double Vgs=0.6 ; Vgs<2.5 ; Vgs+=0.00001) {
                ids = Ids(Vgs, Vgs, 20, 2, 0, "nmos");
                if (ids>39.995 && ids<40.005) printf("Ids:%f\tVgate:%f\n",
				                              ids, 0.0+Vgs);
            }
            // yielding Vgate~698.62mV and then calculate the same thing for
			// T4 since we know source of T4 now
        }
        if (false) {
            for (double Vgs=0.1 ; Vgs<1.0 ; Vgs+=0.00001) {
                ids = Ids(Vgs, Vgs, 20, 2, 0.69862, "nmos");
                if (ids>39.995 && ids<40.005) printf("Ids:%f\tVgate:%f\n",\
				                              ids, 0.69862+Vgs);
            }
            // yielding Vd(T4)~1.58V
        }
        // Now we will find the minimum Vd(T2) which can keep T2 in
		// saturation region
        double VTH = Vth(0.69862, "nmos");
        printf("<page 13 the only figure> Vd(T2):%f V \n\n",\
		                           0.69862+1.58-0.68962-VTH);
        // yielding Vth(T2)~907mV
    }
    
    separator // page 14
    if (false) {
        // I want to find the Vgs and also Vds value that makes 40uA
		// possible for the transistor
		double ids;
        if (true) for (double i=1.0 ; i<3.3 ; i+=0.0001) {
            ids=Ids(i, i, 3.0, 2.0);
            if (ids<40.01 && ids>39.99) printf("|   Vgs:%f\tIds:%f\n", i, ids);
        }
        // which yealds Vgs=Vds=1.01V (The gate of T2 is at this potential)
		// Do the same for T1
		if (true) for (double i=0.0 ; i<3.3 ; i+=0.0001) {
            ids=Ids(i, i, 20.0, 2.0);
            if (ids<40.1 && ids>39.9) printf(" |  Vgs:%f\tIds:%f\n", i, ids);
        }
		// which yields 699mV. Do the same thing for T2; I am able to 
		// do this because the bulk effect is ignorable
		if (true) for (double i=0.0 ; i<3.3 ; i+=0.0001) {
            ids=Ids(i, i, 20.0, 2.0);
            if (ids<40.1 && ids>39.9) printf("  | Vgs:%f\tIds:%f\n", i, ids);
        }
		// which yields 699mV. Vgs=Vg-Vs => Vs=Vg-Vgs
		// Vs=1.02-0.699=0.321 So both the transistors operate in
		// triode region.
		double r01 = r0(40)/1000000, r02=r0(40)/1000000;
		double gm1 = gm_triodeRegion(0.321, 20, 2);
		double gm2 = gm_triodeRegion(0.4, 20, 2, 0.321);
		printf("<Page 14> r01:%fM r02:%fM gm1:%fuS gm2:%fuS R:%fM\n",
		      r01, r02, gm1, gm2, r01+r02);
		// yielding gm1=642uS and gm2=800uS.
    }
    
    separator // page 15
    if (false) {
        // First I need the Vgate value of T5
        for (double i=-2.5 ; i<2.5 ; i+=0.0001) {
        	double ids=fabs(Ids(i, i, 6.0, 1.0, 0, "pmos"));
        	if (ids<70.1 && ids>69.9) printf("Vgs:%f\tIds:%f\n", i, ids);
        }
        // which yields ~-1.175V, Vgate=2.5-1.175=1.325V
		// For T2, calculate Vgs=-0.8 and Vg=1.7V with exactly the same way.
		// Then 830mV is found as the value that can keep T3 in saturation
		// so the correct current mirror functionality could be achieved.
    }
        
    return 0;
}