/*****************************************************************************
 *                                McPAT/CACTI
 *                      SOFTWARE LICENSE AGREEMENT
 *            Copyright 2012 Hewlett-Packard Development Company, L.P.
 *            Copyright (c) 2010-2013 Advanced Micro Devices, Inc.
 *                          All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ***************************************************************************/




#include <cassert>
#include <cmath>
#include <iostream>

#include "basic_circuit.h"
#include "parameter.h"

uint32_t _log2(uint64_t num) {
    uint32_t log2 = 0;

    if (num == 0) {
        std::cerr << "log0?" << std::endl;
        exit(1);
    }

    while (num > 1) {
        num = (num >> 1);
        log2++;
    }

    return log2;
}


bool is_pow2(int64_t val) {
    if (val <= 0) {
        return false;
    } else if (val == 1) {
        return true;
    } else {
        return (_log2(val) != _log2(val - 1));
    }
}


int powers (int base, int n) {
    int i, p;

    p = 1;
    for (i = 1; i <= n; ++i)
        p *= base;
    return p;
}

/*----------------------------------------------------------------------*/

double logtwo (double x) {
    assert(x > 0);
    return ((double) (log (x) / log (2.0)));
}

/*----------------------------------------------------------------------*/


double gate_C(
    double width,
    double wirelength,
    bool   _is_dram,
    bool   _is_cell,
    bool   _is_wl_tr) {
    const TechnologyParameter::DeviceType * dt;

    if (_is_dram && _is_cell) {
        dt = &g_tp.dram_acc;   //DRAM cell access transistor
    } else if (_is_dram && _is_wl_tr) {
        dt = &g_tp.dram_wl;    //DRAM wordline transistor
    } else if (!_is_dram && _is_cell) {
        dt = &g_tp.sram_cell;  // SRAM cell access transistor
    } else {
        dt = &g_tp.peri_global;
    }

    return (dt->C_g_ideal + dt->C_overlap + 3*dt->C_fringe)*width + dt->l_phy*Cpolywire;
}


// returns gate capacitance in Farads
// actually this function is the same as gate_C() now
double gate_C_pass(
    double width,       // gate width in um (length is Lphy_periph_global)
    double wirelength,  // poly wire length going to gate in lambda
    bool   _is_dram,
    bool   _is_cell,
    bool   _is_wl_tr) {
    // v5.0
    const TechnologyParameter::DeviceType * dt;

    if ((_is_dram) && (_is_cell)) {
        dt = &g_tp.dram_acc;   //DRAM cell access transistor
    } else if ((_is_dram) && (_is_wl_tr)) {
        dt = &g_tp.dram_wl;    //DRAM wordline transistor
    } else if ((!_is_dram) && _is_cell) {
        dt = &g_tp.sram_cell;  // SRAM cell access transistor
    } else {
        dt = &g_tp.peri_global;
    }

    return (dt->C_g_ideal + dt->C_overlap + 3*dt->C_fringe)*width + dt->l_phy*Cpolywire;
}



double drain_C_(
    double width,
    int nchannel,
    int stack,
    int next_arg_thresh_folding_width_or_height_cell,
    double fold_dimension,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    double w_folded_tr;
    const  TechnologyParameter::DeviceType * dt;

    if ((_is_dram) && (_is_cell)) {
        dt = &g_tp.dram_acc;   // DRAM cell access transistor
    } else if ((_is_dram) && (_is_wl_tr)) {
        dt = &g_tp.dram_wl;    // DRAM wordline transistor
    } else if ((!_is_dram) && _is_cell) {
        dt = &g_tp.sram_cell;  // SRAM cell access transistor
    } else {
        dt = &g_tp.peri_global;
    }

    double c_junc_area = dt->C_junc;
    double c_junc_sidewall = dt->C_junc_sidewall;
    double c_fringe    = 2 * dt->C_fringe;
    double c_overlap   = 2 * dt->C_overlap;
    double drain_C_metal_connecting_folded_tr = 0;

    // determine the width of the transistor after folding (if it is getting folded)
    if (next_arg_thresh_folding_width_or_height_cell == 0) {
        // interpret fold_dimension as the the folding width threshold
        // i.e. the value of transistor width above which the transistor gets folded
        w_folded_tr = fold_dimension;
    } else { // interpret fold_dimension as the height of the cell that this transistor is part of.
        double h_tr_region  = fold_dimension - 2 * g_tp.HPOWERRAIL;
        // TODO : w_folded_tr must come from Component::compute_gate_area()
        double ratio_p_to_n = 2.0 / (2.0 + 1.0);
        if (nchannel) {
            w_folded_tr = (1 - ratio_p_to_n) * (h_tr_region - g_tp.MIN_GAP_BET_P_AND_N_DIFFS);
        } else {
            w_folded_tr = ratio_p_to_n * (h_tr_region - g_tp.MIN_GAP_BET_P_AND_N_DIFFS);
        }
    }
    int num_folded_tr = (int) (ceil(width / w_folded_tr));

    if (num_folded_tr < 2) {
        w_folded_tr = width;
    }

    double total_drain_w = (g_tp.w_poly_contact + 2 * g_tp.spacing_poly_to_contact) +  // only for drain
                           (stack - 1) * g_tp.spacing_poly_to_poly;
    double drain_h_for_sidewall = w_folded_tr;
    double total_drain_height_for_cap_wrt_gate = w_folded_tr + 2 * w_folded_tr * (stack - 1);
    if (num_folded_tr > 1) {
        total_drain_w += (num_folded_tr - 2) * (g_tp.w_poly_contact + 2 * g_tp.spacing_poly_to_contact) +
                         (num_folded_tr - 1) * ((stack - 1) * g_tp.spacing_poly_to_poly);

        if (num_folded_tr % 2 == 0) {
            drain_h_for_sidewall = 0;
        }
        total_drain_height_for_cap_wrt_gate *= num_folded_tr;
        drain_C_metal_connecting_folded_tr   = g_tp.wire_local.C_per_um * total_drain_w;
    }

    double drain_C_area     = c_junc_area * total_drain_w * w_folded_tr;
    double drain_C_sidewall = c_junc_sidewall * (drain_h_for_sidewall + 2 * total_drain_w);
    double drain_C_wrt_gate = (c_fringe + c_overlap) * total_drain_height_for_cap_wrt_gate;

    return (drain_C_area + drain_C_sidewall + drain_C_wrt_gate + drain_C_metal_connecting_folded_tr);
}


double tr_R_on(
    double width,
    int nchannel,
    int stack,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    const TechnologyParameter::DeviceType * dt;

    if ((_is_dram) && (_is_cell)) {
        dt = &g_tp.dram_acc;   //DRAM cell access transistor
    } else if ((_is_dram) && (_is_wl_tr)) {
        dt = &g_tp.dram_wl;    //DRAM wordline transistor
    } else if ((!_is_dram) && _is_cell) {
        dt = &g_tp.sram_cell;  // SRAM cell access transistor
    } else {
        dt = &g_tp.peri_global;
    }

    double restrans = (nchannel) ? dt->R_nch_on : dt->R_pch_on;
    return (stack * restrans / width);
}


/* This routine operates in reverse: given a resistance, it finds
 * the transistor width that would have this R.  It is used in the
 * data wordline to estimate the wordline driver size. */

// returns width in um
double R_to_w(
    double res,
    int   nchannel,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    const TechnologyParameter::DeviceType * dt;

    if ((_is_dram) && (_is_cell)) {
        dt = &g_tp.dram_acc;   //DRAM cell access transistor
    } else if ((_is_dram) && (_is_wl_tr)) {
        dt = &g_tp.dram_wl;    //DRAM wordline transistor
    } else if ((!_is_dram) && (_is_cell)) {
        dt = &g_tp.sram_cell;  // SRAM cell access transistor
    } else {
        dt = &g_tp.peri_global;
    }

    double restrans = (nchannel) ? dt->R_nch_on : dt->R_pch_on;
    return (restrans / res);
}


double pmos_to_nmos_sz_ratio(
    bool _is_dram,
    bool _is_wl_tr) {
    double p_to_n_sizing_ratio;
    if ((_is_dram) && (_is_wl_tr)) { //DRAM wordline transistor
        p_to_n_sizing_ratio = g_tp.dram_wl.n_to_p_eff_curr_drv_ratio;
    } else { //DRAM or SRAM all other transistors
        p_to_n_sizing_ratio = g_tp.peri_global.n_to_p_eff_curr_drv_ratio;
    }
    return p_to_n_sizing_ratio;
}


// "Timing Models for MOS Circuits" by Mark Horowitz, 1984
double horowitz(
    double inputramptime, // input rise time
    double tf,            // time constant of gate
    double vs1,           // threshold voltage
    double vs2,           // threshold voltage
    int    rise) {        // whether input rises or fall
    if (inputramptime == 0 && vs1 == vs2) {
        return tf * (vs1 < 1 ? -log(vs1) : log(vs1));
    }
    double a, b, td;

    a = inputramptime / tf;
    if (rise == RISE) {
        b = 0.5;
        td = tf * sqrt(log(vs1) * log(vs1) + 2 * a * b * (1.0 - vs1)) +
            tf * (log(vs1) - log(vs2));
    } else {
        b = 0.4;
        td = tf * sqrt(log(1.0 - vs1) * log(1.0 - vs1) + 2 * a * b * (vs1)) +
            tf * (log(1.0 - vs1) - log(1.0 - vs2));
    }
    return (td);
}

double cmos_Ileak(
    double nWidth,
    double pWidth,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    TechnologyParameter::DeviceType * dt;

    if ((!_is_dram) && (_is_cell)) { //SRAM cell access transistor
        dt = &(g_tp.sram_cell);
    } else if ((_is_dram) && (_is_wl_tr)) { //DRAM wordline transistor
        dt = &(g_tp.dram_wl);
    } else { //DRAM or SRAM all other transistors
        dt = &(g_tp.peri_global);
    }
    return nWidth*dt->I_off_n + pWidth*dt->I_off_p;
}


double simplified_nmos_leakage(
    double nwidth,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    TechnologyParameter::DeviceType * dt;

    if ((!_is_dram) && (_is_cell)) { //SRAM cell access transistor
        dt = &(g_tp.sram_cell);
    } else if ((_is_dram) && (_is_wl_tr)) { //DRAM wordline transistor
        dt = &(g_tp.dram_wl);
    } else { //DRAM or SRAM all other transistors
        dt = &(g_tp.peri_global);
    }
    return nwidth * dt->I_off_n;
}

int factorial(int n, int m) {
    int fa = m, i;
    for (i = m + 1; i <= n; i++)
        fa *= i;
    return fa;
}

int combination(int n, int m) {
    int ret;
    ret = factorial(n, m + 1) / factorial(n - m);
    return ret;
}

double simplified_pmos_leakage(
    double pwidth,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    TechnologyParameter::DeviceType * dt;

    if ((!_is_dram) && (_is_cell)) { //SRAM cell access transistor
        dt = &(g_tp.sram_cell);
    } else if ((_is_dram) && (_is_wl_tr)) { //DRAM wordline transistor
        dt = &(g_tp.dram_wl);
    } else { //DRAM or SRAM all other transistors
        dt = &(g_tp.peri_global);
    }
    return pwidth * dt->I_off_p;
}

double cmos_Ig_n(
    double nWidth,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    TechnologyParameter::DeviceType * dt;

    if ((!_is_dram) && (_is_cell)) { //SRAM cell access transistor
        dt = &(g_tp.sram_cell);
    } else if ((_is_dram) && (_is_wl_tr)) { //DRAM wordline transistor
        dt = &(g_tp.dram_wl);
    } else { //DRAM or SRAM all other transistors
        dt = &(g_tp.peri_global);
    }
    return nWidth*dt->I_g_on_n;
}

double cmos_Ig_p(
    double pWidth,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr) {
    TechnologyParameter::DeviceType * dt;

    if ((!_is_dram) && (_is_cell)) { //SRAM cell access transistor
        dt = &(g_tp.sram_cell);
    } else if ((_is_dram) && (_is_wl_tr)) { //DRAM wordline transistor
        dt = &(g_tp.dram_wl);
    } else { //DRAM or SRAM all other transistors
        dt = &(g_tp.peri_global);
    }
    return pWidth*dt->I_g_on_p;
}

double cmos_Isub_leakage(
    double nWidth,
    double pWidth,
    int    fanin,
    enum Gate_type g_type,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr,
    enum Half_net_topology topo) {
    assert (fanin >= 1);
    double nmos_leak = simplified_nmos_leakage(nWidth, _is_dram, _is_cell, _is_wl_tr);
    double pmos_leak = simplified_pmos_leakage(pWidth, _is_dram, _is_cell, _is_wl_tr);
    double Isub = 0;
    int    num_states;
    int    num_off_tx;

    num_states = int(pow(2.0, fanin));

    switch (g_type) {
    case nmos:
        if (fanin == 1) {
            Isub = nmos_leak / num_states;
        } else {
            if (topo == parallel) {
                //only when all tx are off, leakage power is non-zero.
                //The possibility of this state is 1/num_states
                Isub = nmos_leak * fanin / num_states;
            } else {
                for (num_off_tx = 1; num_off_tx <= fanin; num_off_tx++) {
                    //when num_off_tx ==0 there is no leakage power
                    Isub += nmos_leak * pow(UNI_LEAK_STACK_FACTOR,
                                            (num_off_tx - 1)) *
                        combination(fanin, num_off_tx);
                }
                Isub /= num_states;
            }

        }
        break;
    case pmos:
        if (fanin == 1) {
            Isub = pmos_leak / num_states;
        } else {
            if (topo == parallel) {
                //only when all tx are off, leakage power is non-zero.
                //The possibility of this state is 1/num_states
                Isub = pmos_leak * fanin / num_states;
            } else {
                for (num_off_tx = 1; num_off_tx <= fanin; num_off_tx++) {
                    //when num_off_tx ==0 there is no leakage power
                    Isub += pmos_leak * pow(UNI_LEAK_STACK_FACTOR,
                                            (num_off_tx - 1)) *
                        combination(fanin, num_off_tx);
                }
                Isub /= num_states;
            }

        }
        break;
    case inv:
        Isub = (nmos_leak + pmos_leak) / 2;
        break;
    case nand:
        Isub += fanin * pmos_leak;//the pullup network
        for (num_off_tx = 1; num_off_tx <= fanin; num_off_tx++) {
            // the pulldown network
            Isub += nmos_leak * pow(UNI_LEAK_STACK_FACTOR,
                                    (num_off_tx - 1)) *
                combination(fanin, num_off_tx);
        }
        Isub /= num_states;
        break;
    case nor:
        for (num_off_tx = 1; num_off_tx <= fanin; num_off_tx++) {
             // the pullup network
            Isub += pmos_leak * pow(UNI_LEAK_STACK_FACTOR,
                                    (num_off_tx - 1)) *
                combination(fanin, num_off_tx);
        }
        Isub += fanin * nmos_leak;//the pulldown network
        Isub /= num_states;
        break;
    case tri:
        Isub += (nmos_leak + pmos_leak) / 2;//enabled
        //disabled upper bound of leakage power
        Isub += nmos_leak * UNI_LEAK_STACK_FACTOR;
        Isub /= 2;
        break;
    case tg:
        Isub = (nmos_leak + pmos_leak) / 2;
        break;
    default:
        assert(0);
        break;
    }

    return Isub;
}


double cmos_Ig_leakage(
    double nWidth,
    double pWidth,
    int    fanin,
    enum Gate_type g_type,
    bool _is_dram,
    bool _is_cell,
    bool _is_wl_tr,
    enum Half_net_topology topo) {
    assert (fanin >= 1);
    double nmos_leak = cmos_Ig_n(nWidth, _is_dram, _is_cell, _is_wl_tr);
    double pmos_leak = cmos_Ig_p(pWidth, _is_dram, _is_cell, _is_wl_tr);
    double Ig_on = 0;
    int    num_states;
    int    num_on_tx;

    num_states = int(pow(2.0, fanin));

    switch (g_type) {
    case nmos:
        if (fanin == 1) {
            Ig_on = nmos_leak / num_states;
        } else {
            if (topo == parallel) {
                for (num_on_tx = 1; num_on_tx <= fanin; num_on_tx++) {
                    Ig_on += nmos_leak * combination(fanin, num_on_tx) *
                        num_on_tx;
                }
            } else {
                //pull down network when all TXs are on.
                Ig_on += nmos_leak * fanin;
                //num_on_tx is the number of on tx
                for (num_on_tx = 1; num_on_tx < fanin; num_on_tx++) {
                     //when num_on_tx=[1,n-1]
                    //TODO: this is a approximation now, a precise computation
                    //will be very complicated.
                    Ig_on += nmos_leak * combination(fanin, num_on_tx) *
                        num_on_tx / 2;
                }
                Ig_on /= num_states;
            }
        }
        break;
    case pmos:
        if (fanin == 1) {
            Ig_on = pmos_leak / num_states;
        } else {
            if (topo == parallel) {
                for (num_on_tx = 1; num_on_tx <= fanin; num_on_tx++) {
                    Ig_on += pmos_leak * combination(fanin, num_on_tx) *
                        num_on_tx;
                }
            } else {
                //pull down network when all TXs are on.
                Ig_on += pmos_leak * fanin;
                //num_on_tx is the number of on tx
                for (num_on_tx = 1; num_on_tx < fanin; num_on_tx++) {
                    //when num_on_tx=[1,n-1]
                    //TODO: this is a approximation now, a precise computation
                    //will be very complicated.
                    Ig_on += pmos_leak * combination(fanin, num_on_tx) *
                        num_on_tx / 2;
                }
                Ig_on /= num_states;
            }
        }
        break;

    case inv:
        Ig_on = (nmos_leak + pmos_leak) / 2;
        break;
    case nand:
      //pull up network
      //when num_on_tx=[1,n]
      for (num_on_tx = 1; num_on_tx <= fanin; num_on_tx++) {
          Ig_on += pmos_leak * combination(fanin, num_on_tx) * num_on_tx;
      }

      //pull down network
      Ig_on += nmos_leak * fanin;//pull down network when all TXs are on.
      //num_on_tx is the number of on tx
      for (num_on_tx = 1; num_on_tx < fanin; num_on_tx++) {
          //when num_on_tx=[1,n-1]
          //TODO: this is a approximation now, a precise computation will be
          //very complicated.
          Ig_on += nmos_leak * combination(fanin, num_on_tx) * num_on_tx / 2;
        }
        Ig_on /= num_states;
        break;
    case nor:
        // num_on_tx is the number of on tx in pull up network
        Ig_on += pmos_leak * fanin;//pull up network when all TXs are on.
        for (num_on_tx = 1; num_on_tx < fanin; num_on_tx++) {
            Ig_on += pmos_leak * combination(fanin, num_on_tx) * num_on_tx / 2;

        }
        //pull down network
        for (num_on_tx = 1; num_on_tx <= fanin; num_on_tx++) {
            //when num_on_tx=[1,n]
            Ig_on += nmos_leak * combination(fanin, num_on_tx) * num_on_tx;
        }
        Ig_on /= num_states;
        break;
    case tri:
        Ig_on += (2 * nmos_leak + 2 * pmos_leak) / 2;//enabled
        //disabled upper bound of leakage power
        Ig_on += (nmos_leak + pmos_leak) / 2;
        Ig_on /= 2;
        break;
    case tg:
        Ig_on = (nmos_leak + pmos_leak) / 2;
        break;
    default:
        assert(0);
        break;
    }

    return Ig_on;
}

double shortcircuit_simple(
    double vt,
    double velocity_index,
    double c_in,
    double c_out,
    double w_nmos,
    double w_pmos,
    double i_on_n,
    double i_on_p,
    double i_on_n_in,
    double i_on_p_in,
    double vdd) {

    double p_short_circuit, p_short_circuit_discharge, p_short_circuit_charge, p_short_circuit_discharge_low, p_short_circuit_discharge_high, p_short_circuit_charge_low, p_short_circuit_charge_high; //this is actually energy
    double fo_n, fo_p, fanout, beta_ratio, vt_to_vdd_ratio;

    fo_n	= i_on_n / i_on_n_in;
    fo_p	= i_on_p / i_on_p_in;
    fanout	= c_out / c_in;
    beta_ratio = i_on_p / i_on_n;
    vt_to_vdd_ratio = vt / vdd;

    //p_short_circuit_discharge_low 	= 10/3*(pow(0.5-vt_to_vdd_ratio,3.0)/pow(velocity_index,2.0)/pow(2.0,3*vt_to_vdd_ratio*vt_to_vdd_ratio))*c_in*vdd*vdd*fo_p*fo_p/fanout/beta_ratio;
    p_short_circuit_discharge_low =
        10 / 3 * (pow(((vdd - vt) - vt_to_vdd_ratio), 3.0) /
                  pow(velocity_index, 2.0) / pow(2.0, 3 * vt_to_vdd_ratio *
                                                 vt_to_vdd_ratio)) * c_in *
        vdd * vdd * fo_p * fo_p / fanout / beta_ratio;
    p_short_circuit_charge_low =
        10 / 3 * (pow(((vdd - vt) - vt_to_vdd_ratio), 3.0) /
                  pow(velocity_index, 2.0) / pow(2.0, 3 * vt_to_vdd_ratio *
                                                 vt_to_vdd_ratio)) * c_in *
        vdd * vdd * fo_n * fo_n / fanout * beta_ratio;
//	double t1, t2, t3, t4, t5;
//	t1=pow(((vdd-vt)-vt_to_vdd_ratio),3);
//	t2=pow(velocity_index,2.0);
//	t3=pow(2.0,3*vt_to_vdd_ratio*vt_to_vdd_ratio);
//	t4=t1/t2/t3;
//	cout <<t1<<"t1\n"<<t2<<"t2\n"<<t3<<"t3\n"<<t4<<"t4\n"<<fanout<<endl;

    p_short_circuit_discharge_high =
        pow(((vdd - vt) - vt_to_vdd_ratio), 1.5) * c_in * vdd * vdd *
        fo_p / 10 / pow(2, 3 * vt_to_vdd_ratio + 2 * velocity_index);
    p_short_circuit_charge_high = pow(((vdd - vt) - vt_to_vdd_ratio), 1.5) *
        c_in * vdd * vdd * fo_n / 10 / pow(2, 3 * vt_to_vdd_ratio + 2 *
                                           velocity_index);

//	t1=pow(((vdd-vt)-vt_to_vdd_ratio),1.5);
//	t2=pow(2, 3*vt_to_vdd_ratio+2*velocity_index);
//	t3=t1/t2;
//	cout <<t1<<"t1\n"<<t2<<"t2\n"<<t3<<"t3\n"<<t4<<"t4\n"<<fanout<<endl;
//	p_short_circuit_discharge = 1.0/(1.0/p_short_circuit_discharge_low + 1.0/p_short_circuit_discharge_high);
//	p_short_circuit_charge = 1/(1/p_short_circuit_charge_low + 1/p_short_circuit_charge_high); //harmmoic mean cannot be applied simple formulas.

    p_short_circuit_discharge = p_short_circuit_discharge_low;
    p_short_circuit_charge = p_short_circuit_charge_low;
    p_short_circuit = (p_short_circuit_discharge + p_short_circuit_charge) / 2;

    return (p_short_circuit);
}

double shortcircuit(
    double vt,
    double velocity_index,
    double c_in,
    double c_out,
    double w_nmos,
    double w_pmos,
    double i_on_n,
    double i_on_p,
    double i_on_n_in,
    double i_on_p_in,
    double vdd) {

    //this is actually energy
    double p_short_circuit = 0, p_short_circuit_discharge;
    double fo_n, fo_p, fanout, beta_ratio, vt_to_vdd_ratio;
    double f_alpha, k_v, e, g_v_alpha, h_v_alpha;

    fo_n = i_on_n / i_on_n_in;
    fo_p = i_on_p / i_on_p_in;
    fanout = 1;
    beta_ratio = i_on_p / i_on_n;
    vt_to_vdd_ratio = vt / vdd;
    e =	2.71828;
    f_alpha	= 1 / (velocity_index + 2) - velocity_index /
        (2 * (velocity_index + 3)) + velocity_index / (velocity_index + 4) *
        (velocity_index / 2 - 1);
    k_v	= 0.9 / 0.8 + (vdd - vt) / 0.8 * log(10 * (vdd - vt) / e);
    g_v_alpha =	(velocity_index + 1) *
        pow((1 - velocity_index), velocity_index) *
        pow((1 - velocity_index), velocity_index / 2) / f_alpha /
        pow((1 - velocity_index - velocity_index),
            (velocity_index / 2 + velocity_index + 2));
    h_v_alpha	=   pow(2, velocity_index) * (velocity_index + 1) *
        pow((1 - velocity_index), velocity_index) /
        pow((1 - velocity_index - velocity_index), (velocity_index + 1));

    //p_short_circuit_discharge_low 	= 10/3*(pow(0.5-vt_to_vdd_ratio,3.0)/pow(velocity_index,2.0)/pow(2.0,3*vt_to_vdd_ratio*vt_to_vdd_ratio))*c_in*vdd*vdd*fo_p*fo_p/fanout/beta_ratio;
//	p_short_circuit_discharge_low 	= 10/3*(pow(((vdd-vt)-vt_to_vdd_ratio),3.0)/pow(velocity_index,2.0)/pow(2.0,3*vt_to_vdd_ratio*vt_to_vdd_ratio))*c_in*vdd*vdd*fo_p*fo_p/fanout/beta_ratio;
//	p_short_circuit_charge_low 		= 10/3*(pow(((vdd-vt)-vt_to_vdd_ratio),3.0)/pow(velocity_index,2.0)/pow(2.0,3*vt_to_vdd_ratio*vt_to_vdd_ratio))*c_in*vdd*vdd*fo_n*fo_n/fanout*beta_ratio;
//	double t1, t2, t3, t4, t5;
//	t1=pow(((vdd-vt)-vt_to_vdd_ratio),3);
//	t2=pow(velocity_index,2.0);
//	t3=pow(2.0,3*vt_to_vdd_ratio*vt_to_vdd_ratio);
//	t4=t1/t2/t3;
//
//	cout <<t1<<"t1\n"<<t2<<"t2\n"<<t3<<"t3\n"<<t4<<"t4\n"<<fanout<<endl;
//
//
//	p_short_circuit_discharge_high 	= pow(((vdd-vt)-vt_to_vdd_ratio),1.5)*c_in*vdd*vdd*fo_p/10/pow(2, 3*vt_to_vdd_ratio+2*velocity_index);
//	p_short_circuit_charge_high 	= pow(((vdd-vt)-vt_to_vdd_ratio),1.5)*c_in*vdd*vdd*fo_n/10/pow(2, 3*vt_to_vdd_ratio+2*velocity_index);
//
//	p_short_circuit_discharge = 1.0/(1.0/p_short_circuit_discharge_low + 1.0/p_short_circuit_discharge_high);
//	p_short_circuit_charge = 1/(1/p_short_circuit_charge_low + 1/p_short_circuit_charge_high);
//
//	p_short_circuit = (p_short_circuit_discharge + p_short_circuit_charge)/2;
//
//	p_short_circuit = p_short_circuit_discharge;

    p_short_circuit_discharge = k_v * vdd * vdd * c_in * fo_p * fo_p /
        ((vdd - vt) * g_v_alpha * fanout * beta_ratio / 2 / k_v + h_v_alpha *
         fo_p);
    return (p_short_circuit);
}