// File: Address.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

library Address {
    function isContract(address account) internal view returns (bool) {
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            codehash := extcodehash(account)
        }
        return (codehash != accountHash && codehash != 0x0);
    }

    function sendValue(address payable recipient, uint256 amount) internal {
        require(
            address(this).balance >= amount,
            "Thunder EV : insufficient balance"
        );

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{value: amount}("");
        require(
            success,
            "Thunder EV : unable to send value, recipient may have reverted"
        );
    }

    function functionCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return functionCall(target, data, "Thunder EV : low-level call failed");
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, errorMessage);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                "Thunder EV : low-level call with value failed"
            );
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(
            address(this).balance >= value,
            "Thunder EV : insufficient balance for call"
        );
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 weiValue,
        string memory errorMessage
    ) private returns (bytes memory) {
        require(isContract(target), "Thunder EV : call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value: weiValue}(
            data
        );
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: DateTime.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./Variables.sol";
import "./SafeMath.sol";

library DateTime {

    // Contract imports
    using SafeMath for uint256;

    uint256 constant DAY_IN_SECONDS = 86400;
    uint256 constant YEAR_IN_SECONDS = 31536000;
    uint256 constant LEAP_YEAR_IN_SECONDS = 31622400;

    uint256 constant HOUR_IN_SECONDS = 3600;
    uint256 constant MINUTE_IN_SECONDS = 60;

    uint16 constant ORIGIN_YEAR = 1970;

    function isLeapYear(uint16 year) public pure returns (bool) {
        if (year % 4 != 0) {
            return false;
        }
        if (year % 100 != 0) {
            return true;
        }
        if (year % 400 != 0) {
            return false;
        }
        return true;
    }

    function leapYearsBefore(uint256 year) public pure returns (uint256) {
        uint256 localyear = year;
        localyear -= 1;
        return localyear / 4 - localyear / 100 + localyear / 400;
    }

    function getDaysInMonth(uint8 month, uint16 year)
        public
        pure
        returns (uint8)
    {
        if (
            month == 1 ||
            month == 3 ||
            month == 5 ||
            month == 7 ||
            month == 8 ||
            month == 10 ||
            month == 12
        ) {
            return 31;
        } else if (month == 4 || month == 6 || month == 9 || month == 11) {
            return 30;
        } else if (isLeapYear(year)) {
            return 29;
        } else {
            return 28;
        }
    }

    function parseTimestamp(uint256 timestamp)
        public
        pure
        returns (Variables._DateTime memory dt)
    {
        uint256 secondsAccountedFor = 0;
        uint256 buf;
        uint8 i;

        // Year
        dt.year = getYear(timestamp);
        buf = leapYearsBefore(dt.year) - leapYearsBefore(ORIGIN_YEAR);

        secondsAccountedFor += LEAP_YEAR_IN_SECONDS * buf;
        secondsAccountedFor += YEAR_IN_SECONDS * (dt.year - ORIGIN_YEAR - buf);

        // Month
        uint256 secondsInMonth;
        for (i = 1; i <= 12; i++) {
            secondsInMonth = DAY_IN_SECONDS * getDaysInMonth(i, dt.year);
            if (secondsInMonth + secondsAccountedFor > timestamp) {
                dt.month = i;
                break;
            }
            secondsAccountedFor += secondsInMonth;
        }

        // Day
        for (i = 1; i <= getDaysInMonth(dt.month, dt.year); i++) {
            if (DAY_IN_SECONDS + secondsAccountedFor > timestamp) {
                dt.day = i;
                break;
            }
            secondsAccountedFor += DAY_IN_SECONDS;
        }

        // Hour
        dt.hour = getHour(timestamp);

        // Minute
        dt.minute = getMinute(timestamp);

        // Second
        dt.second = getSecond(timestamp);

        // Day of week.
        dt.weekday = getWeekday(timestamp);
    }

    function getYear(uint256 timestamp) public pure returns (uint16) {
        uint256 secondsAccountedFor = 0;
        uint16 year;
        uint256 numLeapYears;

        // Year
        year = uint16(ORIGIN_YEAR + timestamp / YEAR_IN_SECONDS);
        numLeapYears = leapYearsBefore(year) - leapYearsBefore(ORIGIN_YEAR);

        secondsAccountedFor += LEAP_YEAR_IN_SECONDS * numLeapYears;
        secondsAccountedFor += YEAR_IN_SECONDS * (year - ORIGIN_YEAR - numLeapYears);

        while (secondsAccountedFor > timestamp) {
            if (isLeapYear(uint16(year - 1))) {
                secondsAccountedFor -= LEAP_YEAR_IN_SECONDS;
            } else {
                secondsAccountedFor -= YEAR_IN_SECONDS;
            }
            year -= 1;
        }
        return year;
    }

    function getMonth(uint256 timestamp) public pure returns (uint8) {
        return parseTimestamp(timestamp).month;
    }

    function getDay(uint256 timestamp) public pure returns (uint8) {
        return parseTimestamp(timestamp).day;
    }

    function getHour(uint256 timestamp) public pure returns (uint8) {
        return uint8((timestamp / 60 / 60) % 24);
    }

    function getMinute(uint256 timestamp) public pure returns (uint8) {
        return uint8((timestamp / 60) % 60);
    }

    function getSecond(uint256 timestamp) public pure returns (uint8) {
        return uint8(timestamp % 60);
    }

    function getWeekday(uint256 timestamp) public pure returns (uint8) {
        return uint8((timestamp / DAY_IN_SECONDS + 4) % 7);
    }

    function toTimestamp(
        uint16 year,
        uint8 month,
        uint8 day
    ) public pure returns (uint256 timestamp) {
        return toTimestamp(year, month, day, 0, 0, 0);
    }

    function toTimestamp(
        uint16 year,
        uint8 month,
        uint8 day,
        uint8 hour
    ) public pure returns (uint256 timestamp) {
        return toTimestamp(year, month, day, hour, 0, 0);
    }

    function toTimestamp(
        uint16 year,
        uint8 month,
        uint8 day,
        uint8 hour,
        uint8 minute
    ) public pure returns (uint256 timestamp) {
        return toTimestamp(year, month, day, hour, minute, 0);
    }

    function toTimestamp(
        uint16 year,
        uint8 month,
        uint8 day,
        uint8 hour,
        uint8 minute,
        uint8 second
    ) public pure returns (uint256 timestamp) {
        uint16 i;

        // Year
        for (i = ORIGIN_YEAR; i < year; i++) {
            if (isLeapYear(i)) {
                timestamp += LEAP_YEAR_IN_SECONDS;
            } else {
                timestamp += YEAR_IN_SECONDS;
            }
        }

        // Month
        uint8[12] memory monthDayCounts;
        monthDayCounts[0] = 31;
        if (isLeapYear(year)) {
            monthDayCounts[1] = 29;
        } else {
            monthDayCounts[1] = 28;
        }
        monthDayCounts[2] = 31;
        monthDayCounts[3] = 30;
        monthDayCounts[4] = 31;
        monthDayCounts[5] = 30;
        monthDayCounts[6] = 31;
        monthDayCounts[7] = 31;
        monthDayCounts[8] = 30;
        monthDayCounts[9] = 31;
        monthDayCounts[10] = 30;
        monthDayCounts[11] = 31;

        for (i = 1; i < month; i++) {
            timestamp += DAY_IN_SECONDS * monthDayCounts[i - 1];
        }

        // Day
        timestamp += DAY_IN_SECONDS * (day - 1);

        // Hour
        timestamp += HOUR_IN_SECONDS * (hour);

        // Minute
        timestamp += MINUTE_IN_SECONDS * (minute);

        // Second
        timestamp += second;

        return timestamp;
    }

    function toTimestampFromDateTime(Variables._DateTime memory date)
        public
        pure
        returns (uint256 timestamp)
    {
        uint16 year = date.year;
        uint8 month = date.month;
        uint8 day = date.day;
        uint8 hour = date.hour;
        uint8 minute = date.minute;
        uint8 second = date.second;
        uint16 i;

        // Year
        for (i = ORIGIN_YEAR; i < year; i++) {
            if (isLeapYear(i)) {
                timestamp += LEAP_YEAR_IN_SECONDS;
            } else {
                timestamp += YEAR_IN_SECONDS;
            }
        }

        // Month
        uint8[12] memory monthDayCounts;
        monthDayCounts[0] = 31;
        if (isLeapYear(year)) {
            monthDayCounts[1] = 29;
        } else {
            monthDayCounts[1] = 28;
        }
        monthDayCounts[2] = 31;
        monthDayCounts[3] = 30;
        monthDayCounts[4] = 31;
        monthDayCounts[5] = 30;
        monthDayCounts[6] = 31;
        monthDayCounts[7] = 31;
        monthDayCounts[8] = 30;
        monthDayCounts[9] = 31;
        monthDayCounts[10] = 30;
        monthDayCounts[11] = 31;

        for (i = 1; i < month; i++) {
            timestamp += DAY_IN_SECONDS * monthDayCounts[i - 1];
        }

        // Day
        timestamp += DAY_IN_SECONDS * (day - 1);

        // Hour
        timestamp += HOUR_IN_SECONDS * (hour);

        // Minute
        timestamp += MINUTE_IN_SECONDS * (minute);

        // Second
        timestamp += second;

        return timestamp;
    }
}

// File: IBEP20.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IBEP20 {

    function totalSupply() external view returns (uint256);

    function decimals() external view returns (uint8);

    function symbol() external view returns (string memory);

    function name() external view returns (string memory);

    function getOwner() external view returns (address);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Burn(address indexed burner, uint256 value);

    event Mint(address indexed minter, uint256 value);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    function allowance(address owner, address spender) external view returns (uint256);

    // Custom Event for making log entry for contract
    event FeeDistributionUpdate(
        uint256 _total_beneficiary_count,
        uint256 _distributed_amount,
        uint256 _total_eligible_circulation,
        uint256 _timestamp
    );

    event ContributionAddedToContributionDistributionVariable(uint256 contribution);

    event ContributionDeductionAndBurningLog(
        uint256 _contribution_amount,
        uint256 _burn_amount,
        uint256 _marketing_share,
        uint256 _development_share
    );

}
// File: Ownable.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

abstract contract Context {

    function _msgSender() internal view returns (address) {
        return msg.sender;
    }

    function _msgData() internal view returns (bytes memory) {
        this; 
        return msg.data;
    }
}

contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    constructor() {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    function owner() public view returns (address) {
        return _owner;
    }

    modifier onlyOwner() {
        require(_owner == _msgSender(), "Thunder EV : This function is only accesible by owner.");
        _;
    }

    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    function _transferOwnership(address newOwner) internal {
        require(
            newOwner != address(0),
            "Thunder EV : Can not transfer ownership to ZERO address."
        );
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: SafeMath.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

library SafeMath {

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "Thunder EV : (SafeMatch) addition overflow");

        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "Thunder EV : (SafeMatch) subtraction overflow");
    }

    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "Thunder EV : (SafeMatch) multiplication overflow");

        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "Thunder EV : (SafeMatch) division by zero");
    }

    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "Thunder EV : (SafeMatch) modulo by zero");
    }

    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}
// File: Thev.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./DateTime.sol";
import "./Address.sol";
import "./SafeMath.sol";
import "./IBEP20.sol";
import "./Ownable.sol";
import "./Variables.sol";

// Contract contains no comments or less comments due to chain contract file size issue.

contract THEV is Context, IBEP20, Ownable {
    // Contract imports
    using SafeMath for uint256;
    using Address for address;

    string private _name =  Variables._name;
    string private _symbol =  Variables._symbol;
    uint8 private _decimals = Variables._decimals;
    uint256 private _initial_total_supply =  Variables._initial_total_supply;
    uint256 private _total_supply = Variables._initial_total_supply;

    address private _owner;

    uint256 private _burning_till_block_now = 0; // initial burning token count is 0
    uint256 private _pending_fees_to_distribute = 0; // contribution collection till block.timestamp, after last distribution

    mapping(address => Variables.wallet_details) private _wallets;
    address[] private _holders;
    uint256 private _total_holders = 0;
    address[] private _directors;
    uint256 private _total_directors = 0;
    address[] private _investors;
    uint256 private _total_investors = 0;

    mapping(address => mapping (address => uint256)) private _allowances;

    constructor() {
        // initial wallet adding process on contract launch
        _owner = msg.sender;
        _wallets[msg.sender] = Variables.wallet_details(
            _total_supply,
            block.timestamp,
            0, 0, 0, 0, 0, 0, 0, 0, 0,
            false,
            false
        );
        _total_holders += 1;
        _holders.push(msg.sender);

        // adding development wallet
        _wallets[Variables._development_wallet] = Variables.wallet_details(
            _total_supply,
            block.timestamp,
            0, 0, 0, 0, 0, 0, 0, 0, 0,
            false,
            false
        );
        _total_holders += 1;
        _holders.push(Variables._development_wallet);

        // adding marketing wallet
        _wallets[Variables._marketing_wallet] = Variables.wallet_details(
            _total_supply,
            block.timestamp,
            0, 0, 0, 0, 0, 0, 0, 0, 0,
            false,
            false
        );
        _total_holders += 1;
        _holders.push(Variables._marketing_wallet);
        emit Transfer(address(0), msg.sender, _total_supply);
    }

    function name() public view override returns (string memory) {
        return _name;
    }

    function symbol() public view override returns (string memory) {
        return _symbol;
    }

    function decimals() public view override returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _total_supply;
    }

    function initialTotalSupply() public view returns (uint256) {
        return _initial_total_supply;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _wallets[account].balance;
    }

    function getOwner() external view override returns (address) {
        return owner();
    }

    function burningTillNow() public view returns (uint256) {
        return _burning_till_block_now;
    }

    function pendingFeeToDistribute() public view returns (uint256) {
        return  _pending_fees_to_distribute;
    }

    function directorCount() public view returns (uint256) {
        return _total_directors;
    }

    function investorCount() public view returns (uint256) {
        return _total_investors;
    }

    function getWalletDetails(address account) public view returns (Variables.wallet_details memory) {
        return _wallets[account];
    }

    function addDirectorWallet(address account) public onlyOwner returns (bool) {
        require(_wallets[account].is_director == false, "Thunder EV : Wallet Is Already Director.");
        require(_wallets[account].balance > 0, "Thunder EV : Wallet Is Empty To Create Director.");
        _wallets[account].total_lock_amount = _wallets[account].balance;
        _wallets[account].total_release_amount = 0;
        _wallets[account].is_director = true;
        _wallets[account].is_investor = false;
        _wallets[account].locked_on = block.timestamp;
        _wallets[account].next_release_time = block.timestamp + ( Variables._director_lock_days * 1 seconds );
        _wallets[account].current_release_iteration = 0;
        _wallets[account].current_release_amount = 0;
        _total_directors += 1;
        _directors.push(account);
        return true;
    }

    function addInvestorWallet(address account) public onlyOwner returns (bool) {
        require(_wallets[account].is_director == false, "Thunder EV : Wallet Is Already Director.");
        require(_wallets[account].balance > 0, "Thunder EV : Wallet Is Empty To Create Investor.");
        _wallets[account].total_lock_amount = _wallets[account].balance;
        _wallets[account].total_release_amount = 0;
        _wallets[account].is_director = false;
        _wallets[account].is_investor = true;
        _wallets[account].locked_on = block.timestamp;
        _wallets[account].next_release_time = block.timestamp + ( Variables._investor_lock_days * 1 seconds );
        _wallets[account].current_release_iteration = 0;
        _wallets[account].current_release_amount = 0;
        _total_investors += 1;
        _investors.push(account);
        return true;
    }

    function burnToken(uint256 amount) public onlyOwner returns (bool) {
        _wallets[_owner].balance = _wallets[_owner].balance.sub(amount, "Thunder EV : Burn Amount Exceeds Balance");
        _total_supply = _total_supply.sub(amount * 10**_decimals);
        emit Transfer(_owner, address(0), amount * 10**_decimals);
        return true;
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    // Need to check condition for approval method.
    function _approve( address owner, address spender, uint256 amount ) private {
        require(owner != address(0), "Thunder EV : Approve from the zero address");
        require(spender != address(0), "Thunder EV : Approve to the zero address");
        require(_wallets[owner].balance >= amount, "Thunder EV : Can not allow more than balance.");
        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "Thunder EV :transfer amount exceeds allowance"));
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "Thunder EV :decreased allowance below zero"));
        return true;
    }

    function _contribution_airdrop() internal {
        uint256 total_eligible_token = 0;
        uint256 distribution_till_block_now = 0;
        uint256 amount_to_transfer;
        uint256 eligibale_account_counter = 0;
        if (_pending_fees_to_distribute >= Variables._fees_distribution_after) {
            for (uint256 i = 0; i < _total_holders; i++) {
                if (
                    _wallets[_holders[i]].balance >=
                    Variables._fees_distribution_participation_eligibility
                ) {
                    eligibale_account_counter += 1;
                    total_eligible_token = total_eligible_token.add(
                        _wallets[_holders[i]].balance
                    );
                }
            }
            for (uint256 i = 0; i < _total_holders; i++) {
                if (
                    _wallets[_holders[i]].balance >=
                    Variables._fees_distribution_participation_eligibility
                ) {
                    amount_to_transfer = (
                        _pending_fees_to_distribute.mul(
                            (_wallets[_holders[i]].balance.mul(10**_decimals))
                                .div(total_eligible_token)
                        )
                    ).div(10**_decimals);
                    _wallets[_holders[i]].balance = _wallets[_holders[i]]
                        .balance
                        .add(amount_to_transfer);
                    distribution_till_block_now += amount_to_transfer;
                }
            }
            emit FeeDistributionUpdate(
                eligibale_account_counter,
                distribution_till_block_now,
                total_eligible_token,
                block.timestamp
            );
            _pending_fees_to_distribute = _pending_fees_to_distribute.sub(
                distribution_till_block_now
            );
        }
    }

    function _update_locking_conditions() internal {
        for (uint256 i = 0; i < _total_directors; i++) {
            if ( _wallets[_directors[i]].current_release_iteration < Variables._director_total_release_iteration ) {
                if ( _wallets[_directors[i]].next_release_time <= block.timestamp ) {
                    // get multiplier
                    uint256 multiplier = (
                        (
                            (block.timestamp - _wallets[_directors[i]].next_release_time) / 1 seconds
                        ) / Variables._director_release_every_days_after_locking
                    );
                    uint256 release_amount = ( ( _wallets[_directors[i]].total_lock_amount * Variables._director_release_percentage ) / 100 ) * multiplier;
                    if ( ( _wallets[_directors[i]].total_lock_amount - _wallets[_directors[i]].current_release_amount ) < release_amount ) {
                        release_amount = _wallets[_directors[i]].total_lock_amount;
                    }
                    _wallets[_directors[i]].total_release_amount += release_amount;
                    _wallets[_directors[i]].next_release_time += ( Variables._director_release_every_days_after_locking * multiplier * 1 seconds );
                    _wallets[_directors[i]].current_release_iteration += (1 * multiplier);
                }
            }
        }
        for (uint256 i = 0; i < _total_investors; i++) {
            if ( _wallets[_investors[i]].current_release_iteration < Variables._investor_total_release_iteration ) {
                if ( _wallets[_investors[i]].next_release_time <= block.timestamp ) {
                    uint256 multiplier = (
                        (
                            (block.timestamp - _wallets[_investors[i]].next_release_time) / 1 seconds
                        ) / Variables._investor_release_every_days_after_locking
                    );
                    uint256 release_amount = ( ( _wallets[_investors[i]].total_lock_amount * Variables._investor_release_percentage ) / 100 ) * multiplier;
                    if ( ( _wallets[_investors[i]].total_lock_amount - _wallets[_investors[i]].current_release_amount ) < release_amount ) {
                        release_amount = _wallets[_investors[i]].total_lock_amount;
                    }
                    _wallets[_investors[i]].total_release_amount += release_amount;
                    _wallets[_investors[i]].next_release_time += ( Variables._investor_release_every_days_after_locking * multiplier * 1 seconds );
                    _wallets[_investors[i]].current_release_iteration += (1 * multiplier);
                }
            }
        }
    }

    function _check_sending_conditions(address sender, uint256 amount) internal {
        if (sender != _owner) {
            if ( (_wallets[sender].last_sent_time + ( 24 * 1 seconds )) < block.timestamp ) {
                require(amount < _wallets[sender].max_sending_allowed_in_timeperiod, "Thunder EV : Can Not Trasfter more than allowed limit");
                _wallets[sender].total_sent_in_timeperiod = amount;
                _wallets[sender].last_sent_time = block.timestamp;
            } else {
                require(
                    (amount + _wallets[sender].total_sent_in_timeperiod) < _wallets[sender].max_sending_allowed_in_timeperiod,
                    "Thunder EV : Can Not Trasfter more than allowed limit"
                );
                _wallets[sender].total_sent_in_timeperiod += amount;
            }
        }
    }

    function _after_sending_condition_update(address reciever) internal {
        _wallets[reciever].max_sending_allowed_in_timeperiod = _wallets[reciever].balance * Variables._others_24_hours_transfer_limit / 100;
    }

    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal {
        require(sender != address(0), "Thunder EV : transfer from the zero address");
        require(recipient != address(0), "Thunder EV : transfer to the zero address");
        if (sender.isContract()) {
            require(
                _wallets[sender].balance >= amount,
                "Thunder EV : transfer amount exceeds balance"
            );
        } else {
            require(
                _wallets[sender].balance >= amount + ((amount * Variables._fees_percentage)/100),
                "Thunder EV : transfer amount exceeds balance"
            );
        }

        _update_locking_conditions(); // Updating locking conditions
        _check_sending_conditions(sender, amount); // checking user sending conditions


        if ( _wallets[sender].is_director || _wallets[sender].is_investor ) {
            // Checking balance with blocked amount condition
            require(
                ( _wallets[sender].balance - ( _wallets[sender].total_lock_amount - _wallets[sender].total_release_amount ) ) >= amount,
                "Thunder EV : transfer amount exceeds allowed balance"
            );
        }

        if (_wallets[recipient].balance == 0) {
            _wallets[recipient] = Variables.wallet_details(
                0,
                block.timestamp,
                0, 0, 0, 0, 0, 0, 0, 0, 0,
                false,
                false
            );
            _total_holders += 1;
            _holders.push(recipient);
        }

        _wallets[sender].balance = _wallets[sender].balance.sub(amount);
        _wallets[recipient].balance = _wallets[recipient].balance.add(
            amount
        );
        emit Transfer(sender, recipient, amount);

        uint256 _fees_amount = (amount * Variables._fees_percentage)/100;

        if (sender.isContract()) {
            _wallets[recipient].balance = _wallets[recipient].balance.sub(_fees_amount);
            emit Burn(recipient, (_fees_amount * Variables._burning_from_fees_percentage) / 100);
            emit Transfer(recipient, address(0), (_fees_amount * Variables._burning_from_fees_percentage) / 100);
        } else {
            _wallets[sender].balance = _wallets[sender].balance.sub((amount * Variables._fees_percentage)/100);
            emit Burn(sender, (_fees_amount * Variables._burning_from_fees_percentage) / 100);
            emit Transfer(sender, address(0), (_fees_amount * Variables._burning_from_fees_percentage) / 100);
        }
        _total_supply = _total_supply.sub(((_fees_amount * Variables._burning_from_fees_percentage) / 100));

        _wallets[Variables._development_wallet].balance = (
            _wallets[Variables._development_wallet].balance.add((_fees_amount * Variables._development_sharing_percentage_from_fees_percentage) / 100)
        );
        emit Transfer(
            recipient,
            Variables._development_wallet,
            (_fees_amount * Variables._marketing_sharing_percentage_from_fees_percentage) / 100
        );

        _wallets[Variables._marketing_wallet].balance = (
            _wallets[Variables._marketing_wallet].balance.add((_fees_amount * Variables._marketing_sharing_percentage_from_fees_percentage) / 100)
        );
        emit Transfer(
            recipient,
            Variables._marketing_wallet,
            (_fees_amount * Variables._marketing_sharing_percentage_from_fees_percentage) / 100
        );

        emit ContributionDeductionAndBurningLog(
            ( _fees_amount * Variables._redistribution_from_fees_percentage ) / 100,
            ( _fees_amount * Variables._burning_from_fees_percentage ) / 100,
            ( _fees_amount * Variables._marketing_sharing_percentage_from_fees_percentage ) / 100,
            ( _fees_amount * Variables._development_sharing_percentage_from_fees_percentage ) / 100
        );

        _pending_fees_to_distribute = _pending_fees_to_distribute.add((_fees_amount * Variables._redistribution_from_fees_percentage ) / 100);
        _contribution_airdrop(); // check and make airdrops of contributions

        _after_sending_condition_update(recipient);
    }
}
// File: Variables.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

library Variables {
    // Baisc contract variable declaration
    string public constant _name = "THEV";
    string public constant _symbol = "THEV";
    uint8 public constant _decimals = 18;
    uint256 public constant _initial_total_supply = 2000000000 * 10**_decimals;

    struct _DateTime {
        uint16 year;
        uint8 month;
        uint8 day;
        uint8 hour;
        uint8 minute;
        uint8 second;
        uint8 weekday;
    }

    struct wallet_details {
        uint256 balance;
        uint256 joining_date;
        uint256 locked_on;

        // General allowance variable
        uint256 last_sent_time;
        uint256 max_sending_allowed_in_timeperiod;
        uint256 total_sent_in_timeperiod;

        // condition specific variables
        uint256 total_lock_amount;
        uint256 total_release_amount;
        uint256 next_release_time;
        uint256 current_release_iteration;
        uint256 current_release_amount;
        bool is_investor;
        bool is_director;
    }

    // investor variables
    uint16 public constant _investor_lock_days = 180;
    uint16 public constant _investor_release_percentage = 25;
    uint16 public constant _investor_total_release_iteration = 4;
    uint16 public constant _investor_release_every_days_after_locking = 30;

    // director variables
    uint16 public constant _director_lock_days = 730;
    uint16 public constant _director_release_percentage = 10;
    uint16 public constant _director_total_release_iteration = 10;
    uint16 public constant _director_release_every_days_after_locking = 365;

    // Burning Variables
    uint16 public constant _fees_percentage = 2;
    uint16 public constant _burning_from_fees_percentage = 10;
    uint16 public constant _development_sharing_percentage_from_fees_percentage = 35;
    uint16 public constant _marketing_sharing_percentage_from_fees_percentage = 35;
    uint16 public constant _redistribution_from_fees_percentage = 20;

    // All Token Holders Variable
    uint16 public constant _others_24_hours_transfer_limit = 25;

    // Fees Variables
    // Now To All Holders, if _Fees_distribution_eligibility > 0 then only distributed to holders holding greater amount
    uint256 public constant _fees_distribution_participation_eligibility = 1000 * 10**_decimals;
    uint256 public constant _fees_distribution_after = 1000 * 10**_decimals;

    // Pre Defined Wallet
    address public constant _development_wallet = 0xEa18f98cc0c1745515e912d2A69721e37FCc57C7;
    address public constant _marketing_wallet = 0x42B0d316E79b0D85A5F87142a60d15D9dc6291D2;

}