/**
 * WWWW.LADYLUCKCOIN.COM
 * 
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&@&#*,(&@@&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&(,&@@&&@@@@@@@@@@@@@@@@&@@@@@@@@@@@@@@@@@@@@&@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@&@@%*(@@@@@@@&@@%(,...,,,.,,#%@@&@@@@@@@@@@@@@@@@&@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@&&(*&@&@&@@&**#@@&&@@@@@@@@@@@@@@@@@&&&@@@@@@@@@@&*,@&(,,@@
 * @@@@@@@@@&  .%@@@@@@%*(@&@&@&*(&@@@@@@@@&&*.(&@@@@@@@@@@@@@@@@@@@@@@@@&&*..@@@@@
 * @@@@@@@@@@&        .#@&@&&(#&@@@@@@@@&@.       &@@@@@@@@@@@@@@@@&@#@&@%*#@@@&@@@
 * @@@@@@@@@@@@%             #@@@@@@&@@@@.        &@@@@@@@@@@@@@@&&*  .&@@@@@@@@@@@
 * @@@@@@@@@@@@@(&((@#.            (&@@@@*        @@@@@@@@@@@&#    (@@&&@@@@@@@@@@@
 * @@@@@@@@@@@@&&      .(@@@@#,         (@      .&@@@@@@@%.     #&@@&@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@**&            ,%@@@&,    ,     .&&.       .&@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@&@**&@%#*%@@&@@@&%(*.   (@(            &&,%@@@&@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@&#*(@@@*             .,*(#*              @@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@  @@@@*@@(***(((##@@@@@@@.             *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@**@@@&*@@@@@@@@@@@@@@@@@@   %         (@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@**@@@&*@@@@@@@@@@@@@@@@@&  .@%       .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@**@@@@*@@@@@@@@@@@@@@@@*  #&&        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@**(@@@#*@@@@@@@@@@@@@@@@   @&         ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@**(@@@*#@@@@@@@@@@@@&@@  #@           *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@(**@&&@*(@@@@@@@@@@@@&*  @(            @&@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@&(**@@@@(*@@@@@@@@@@@@  (@(            *@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@&(**&@@@&*(@@@@@@@@@*  &@#             @@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@%(((@&@@%*(@@@@@@@  @@@%             @@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@&@(**(@&&&&((%&&@&@(&@@&             @@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@(***&@@@@&(((&@@&@&@             @@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@#*(*(@@&@@@&(,,*#@,            @@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@&@@(****&@@@@@@@@@#      ..   .@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@&&*,,,,*&@@@@*      %.   *@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&*....        @.   (@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&@@(          @    (@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,            @@    (@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@&@@@#               %@,    (&@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@&@@@.                 @@@     ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@(                   &@@@,      @@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@&&.                  (&&&@&@&       &@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@&@@&@@@@@@@@@@@@@@@@&&&@@@@@@@@&@&@@@&&@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@  ██╗░░░░░░█████╗░██████╗░██╗░░░██╗██╗░░░░░██╗░░░██╗░█████╗░██╗░░██╗ @@@@@@
 * @@@@@  ██║░░░░░██╔══██╗██╔══██╗╚██╗░██╔╝██║░░░░░██║░░░██║██╔══██╗██║░██╔╝ @@@@@@
 * @@@@@  ██║░░░░░███████║██║░░██║░╚████╔╝░██║░░░░░██║░░░██║██║░░╚═╝█████═╝░ @@@@@@
 * @@@@@  ██║░░░░░██╔══██║██║░░██║░░╚██╔╝░░██║░░░░░██║░░░██║██║░░██╗██╔═██╗░ @@@@@@
 * @@@@@  ███████╗██║░░██║██████╔╝░░░██║░░░███████╗╚██████╔╝╚█████╔╝██║░╚██╗ @@@@@@
 * @@@@@  ╚══════╝╚═╝░░╚═╝╚═════╝░░░░╚═╝░░░╚══════╝░╚═════╝░░╚════╝░╚═╝░░╚═╝ @@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 *
 * SPDX-License-Identifier: MIT
 * 
 */
 
pragma solidity ^0.8.0;
                     
abstract contract Initializable {

    
    bool private _initialized;

    bool private _initializing;

    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

contract VRFRequestIDBase {
    
  function makeVRFInputSeed(bytes32 _keyHash, uint256 _userSeed,
    address _requester, uint256 _nonce)
    internal pure returns (uint256)
  {
    return  uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce)));
  }
  
  function makeRequestId(
    bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));
  }
  
}

interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);
  function approve(address spender, uint256 value) external returns (bool success);
  function balanceOf(address owner) external view returns (uint256 balance);
  function decimals() external view returns (uint8 decimalPlaces);
  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
  function increaseApproval(address spender, uint256 subtractedValue) external;
  function name() external view returns (string memory tokenName);
  function symbol() external view returns (string memory tokenSymbol);
  function totalSupply() external view returns (uint256 totalTokensIssued);
  function transfer(address to, uint256 value) external returns (bool success);
  function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
  function transferFrom(address from, address to, uint256 value) external returns (bool success);
}

library SafeMathChainlink {

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a, "SafeMath: addition overflow");

    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b <= a, "SafeMath: subtraction overflow");
    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, "SafeMath: multiplication overflow");

    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b > 0, "SafeMath: division by zero");
    uint256 c = a / b;

    return c;
  }

  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0, "SafeMath: modulo by zero");
    return a % b;
  }
}

abstract contract VRFConsumerBase is Initializable, VRFRequestIDBase {

  using SafeMathChainlink for uint256;

  function fulfillRandomness(bytes32 requestId, uint256 randomness)
    internal virtual;

  function requestRandomness(bytes32 _keyHash, uint256 _fee, uint256 _seed)
    internal returns (bytes32 requestId)
  {
    LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, _seed));

    uint256 vRFSeed  = makeVRFInputSeed(_keyHash, _seed, address(this), nonces[_keyHash]);

    nonces[_keyHash] = nonces[_keyHash].add(1);
    return makeRequestId(_keyHash, vRFSeed);
  }

  LinkTokenInterface  internal LINK;
  address  private vrfCoordinator;

  mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces;

    function __VRFConsumerBase_init() internal initializer {
        __VRFConsumerBase_init_unchained();
    }
    function __VRFConsumerBase_init_unchained() internal initializer {
        vrfCoordinator = 0xf0d54349aDdcf704F77AE15b96510dEA15cb7952;
        LINK = LinkTokenInterface(0x514910771AF9Ca656af840dff83E8264EcF986CA);
    }

  function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {
    require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");
    fulfillRandomness(requestId, randomness);
  }

}

abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
    uint256[50] private __gap;
}

abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    event Paused(address account);

    event Unpaused(address account);

    bool private _paused;

    function __Pausable_init() internal initializer {
        __Context_init_unchained();
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal initializer {
        _paused = false;
    }

    function paused() public view virtual returns (bool) {
        return _paused;
    }

    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
    uint256[49] private __gap;
}

abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }


    function owner() public view virtual returns (address) {
        return _owner;
    }

    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    uint256[49] private __gap;
}

library MathUpgradeable {
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}

library ArraysUpgradeable {
    function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        if (array.length == 0) {
            return 0;
        }

        uint256 low = 0;
        uint256 high = array.length;

        while (low < high) {
            uint256 mid = MathUpgradeable.average(low, high);

            if (array[mid] > element) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        if (low > 0 && array[low - 1] == element) {
            return low - 1;
        } else {
            return low;
        }
    }
}

library AddressUpgradeable {
    function isContract(address account) internal view returns (bool) {
        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: 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, "Address: 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, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            if (returndata.length > 0) {
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

interface IERC20Upgradeable {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    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 Approval(address indexed owner, address indexed spender, uint256 value);
}

contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable {
    
    using ArraysUpgradeable for uint256[];
    using AddressUpgradeable for address;
    
    mapping(address => bool) internal _isHolder;
    mapping(uint256 => mapping(address => address)) internal _groupNextHolder;
    mapping (address => mapping (address => uint256)) private _allowances;
    
    mapping (address => uint256) internal  _balances;
    mapping (uint256 => uint256) internal _groupHolders;
    mapping (address => uint256) internal _addressGroup;
    mapping (address => uint256) internal _weekLotto;  

    uint256[] internal GroupIDList;

    uint256 private _totalSupply;
    string private _name;
    string private _symbol;
    
    uint256 public ExchangeSupply;
    uint256 public HoldersNum;    
    uint256 public WeekCounter;

    address public MegaPot;
    address public LuckPot;
    address public DevWallet;
    address public MarketingWallet;
    
    address constant MOJO = address(1);

    function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
        __Context_init_unchained();
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
        _name = name_;
        _symbol = symbol_;
        MarketingWallet = address(0x05dD722A8478715eC4ab7CDdecB82D598A313072);
        MegaPot = address(0x83F9B450a5d71dC6CBeef6D8E7214181FAf5D41f);
        LuckPot = address(0xc4e017a03Cee1F129E93052e6b150466102B3aF5);        
        DevWallet = address(0x538Ca3c8ad2F0e50730E51D730b907ab993EC326); 
        
    }

    function name() public view virtual returns (string memory) {
        return _name;
    }

    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual returns (uint8) {
        return 18;
    }


    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    function EligibleSupply() public view virtual returns (uint256){
        uint256 ExcludedSupply = _totalSupply - ExchangeSupply - balanceOf(MegaPot) - balanceOf(LuckPot) - balanceOf(DevWallet) - balanceOf(MarketingWallet); 
        return ExcludedSupply;
    }

    function balanceOf(address account) public view virtual override returns (uint256) {
        
        if (_isHolder[account] && _weekLotto[account] < WeekCounter && account != DevWallet && account != MegaPot && account != LuckPot && account != MarketingWallet && !account.isContract()){
            uint256 temp = _balances[account];
            
            uint256 WeekDelta = WeekCounter - _weekLotto[account];
            temp = (temp * (99 ** WeekDelta))/(100** WeekDelta);
            return temp;
            
        } else {
            return _balances[account];
        }
    }
 
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        _approve(sender, _msgSender(), currentAllowance - amount);

        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        _approve(_msgSender(), spender, currentAllowance - subtractedValue);

        return true;
    }
    

    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);
        
        uint256 feeAmount =  amount/50;
        uint256 gasAmount = amount/100;
        
        if (sender == MegaPot || sender == LuckPot || sender == DevWallet || sender == MarketingWallet || recipient == DevWallet || recipient == MegaPot || recipient == LuckPot || recipient == MarketingWallet) {
            feeAmount = 0;
            gasAmount = 0;
        }        

        uint256 senderBalance = balanceOf(sender);
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        
        uint256 receiveAmount = amount - feeAmount - gasAmount;
        
        _balances[sender] = senderBalance - amount;
        _balances[recipient] += receiveAmount;
        _balances[MegaPot] += feeAmount;
        _balances[MarketingWallet] += gasAmount;
        
        emit Transfer(sender, recipient, amount);
        emit Transfer(sender, MegaPot, feeAmount);        
        emit Transfer(sender, MarketingWallet, gasAmount);

    }

    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");
        

        _beforeTokenTransfer(address(0), account, amount);
        
        
        if(_totalSupply == 0){
            _totalSupply += amount;
        }
        
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");    

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");


        
        _balances[account] = accountBalance - amount;
        
        if(account != MegaPot || account != LuckPot){
            _totalSupply -= amount;
        }


        emit Transfer(account, address(0), amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function AddGroupHolder(address holder) internal {
        if(_isHolder[holder] == false && holder != address(0) && holder != MegaPot && holder != LuckPot && holder != DevWallet && holder != MarketingWallet && !holder.isContract()){
            
            _isHolder[holder] = true;
            
            uint256 GroupNumber = 1;
            while (_groupHolders[GroupNumber] >=750){
                GroupNumber++;
            }
            
            if(_groupNextHolder[GroupNumber][MOJO] == address(0)){
                _groupNextHolder[GroupNumber][MOJO] = MOJO;
                GroupIDList.push(GroupNumber);
            }
            _groupNextHolder[GroupNumber][holder] = _groupNextHolder[GroupNumber][MOJO];
            _groupNextHolder[GroupNumber][MOJO] = holder;
            
            _addressGroup[holder] = GroupNumber;
            _groupHolders[GroupNumber]++;
            
            HoldersNum++;
            
            _weekLotto[holder] = WeekCounter;

        }
    }
    
    function RemoveGroupHolder(address holder) internal {
        if(_isHolder[holder] == true && holder != address(0) && holder != MegaPot && holder != LuckPot && holder != DevWallet && holder != MarketingWallet && !holder.isContract()){
            
            _isHolder[holder] = false;
            
            uint256 GroupNumber = _addressGroup[holder];
            
            address prevHolder = GetPrevHolder(holder);
            
            _groupNextHolder[GroupNumber][prevHolder] = _groupNextHolder[GroupNumber][holder];
            _groupNextHolder[GroupNumber][holder] = address(0);

            _addressGroup[holder] = 0;
            _groupHolders[GroupNumber] = _groupHolders[GroupNumber] - 1;
            
            HoldersNum = HoldersNum - 1;
            
            
            _weekLotto[holder] = 0;
                        
    }
    }
    
    function GetPrevHolder(address holder) internal view returns(address){
        
        address currentAddress = MOJO;
        uint256 GroupNumber = _addressGroup[holder];        
        
        while(_groupNextHolder[GroupNumber][currentAddress] != MOJO){
            if(_groupNextHolder[GroupNumber][currentAddress] == holder){
                return currentAddress;
            }
            currentAddress = _groupNextHolder[GroupNumber][currentAddress];
        }
        return address(0);
    }
    
    function GetGroupHolders(uint256 GroupNumber) internal view returns (address[] memory){
        address[] memory holders = new address[](_groupHolders[GroupNumber]);
        address currentAddress = _groupNextHolder[GroupNumber][MOJO];
        for(uint256 i = 0; currentAddress != MOJO; ++i){
            holders[i] = currentAddress;
            currentAddress = _groupNextHolder[GroupNumber][currentAddress];
        }
        return holders;
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {

    if(to.isContract()){ExchangeSupply  =ExchangeSupply + amount;}
    if(from.isContract()){ExchangeSupply =ExchangeSupply - amount;}
    
    _balances[from] = balanceOf(from);
    _weekLotto[from] = WeekCounter;
    _balances[to] - balanceOf(to);
    _weekLotto[to] = WeekCounter;
    
    
    if (from == address(0)) {
      AddGroupHolder(to);
      
    } else if (to == address(0)) {
      if (balanceOf(from) - amount == 0){RemoveGroupHolder(from);}
      
    } else {
      AddGroupHolder(to);
      if (balanceOf(from) - amount == 0){RemoveGroupHolder(from);}
    }

    }
    uint256[45] private __gap;
}

contract LadyLuck is Initializable, ERC20Upgradeable, OwnableUpgradeable, PausableUpgradeable, VRFConsumerBase  {
    
    bytes32 internal keyHash;
    uint256 internal fee;
    uint256 public randomResult;
    
    uint public LastJackpotTime;
    uint public NextJackpotTime;
    uint256 internal JackpotFreq;
    
    uint256 public JackpotStarted;  
    uint256 internal PreviousRandomNumber;
    uint256[] internal GroupBalanceList;
    
    uint256 internal WinningGroupMega; 
    address public WinningWalletMega;
    uint256 public WinningNumberMega;

    uint256 internal WinningGroupLuck;     
    address public WinningWalletLuck;
    uint256 public WinningNumberLuck;

    uint256 internal RollingGroupTotal;    
    
    function initialize() public virtual initializer {
        
        __ERC20_init("Lady Luck", "LUCK");
        
        __Context_init();
        __Pausable_init();
        __Ownable_init();
    
        __VRFConsumerBase_init();
        
        keyHash = 0xAA77729D3466CA35AE8D28B3BBAC7CC36A5031EFDC430821C02BC31A238AF445;
        fee = 2 * 10 ** 18; // 0.1 LINK        
        
        _mint(DevWallet, 1000000000 * 10 ** decimals());
        WinningWalletMega = MegaPot;
        WinningWalletLuck = LuckPot;
        
        JackpotFreq = 604000;//One Week;        
        LastJackpotTime = block.timestamp;
        NextJackpotTime = block.timestamp + JackpotFreq;   
    }
    
    function distributeToken(address[] memory addresses, uint256 _value) public onlyOwner {
	    for( uint256 i=0;i<addresses.length;i++){
	        transfer(addresses[i], _value);
	    }
	}
    
    function PrepareLottery() public onlyOwner {
        /////require(block.timestamp > NextJackpotTime);
        uint256 NumberOfGroups = ShowGroupIDList().length;          
        require(JackpotStarted < NumberOfGroups, "Lottery Has Already Been Prepared");
        
        JackpotStarted++;

        if(JackpotStarted == 1){
            _pause();
            delete GroupBalanceList;
            uint256 temp = GetTrueGroupBalance(JackpotStarted);
            GroupBalanceList.push(temp);
            RollingGroupTotal = temp;
            
        } else if(JackpotStarted <= NumberOfGroups){
            uint256 temp = GetTrueGroupBalance(JackpotStarted);
            GroupBalanceList.push(temp);
            RollingGroupTotal += temp;
        }
        
        if(JackpotStarted == NumberOfGroups){
            PreviousRandomNumber = randomResult;
            getRandomNumber(block.timestamp*block.difficulty);
        }
    }

        
    function RunLuckLottery() public onlyOwner {
        uint256[] memory GroupIDList = ShowGroupIDList();  
        require(JackpotStarted == GroupIDList.length, "Please Prepare Lottery Or End Mega Lottery");        
        require(PreviousRandomNumber != randomResult, "Please Wait For Chainlink To Provide The Random Number");
        
        uint256 FirstRandomResult = uint256(keccak256(abi.encode(randomResult, 0)));
        WinningNumberLuck = FirstRandomResult % RollingGroupTotal + 1;

        uint256 i = 0;
        WinningGroupLuck = 0;
        uint256 RollingTotal = 0;        
        while (WinningGroupLuck == 0){
            uint256 CurrentGroupBalance = GroupBalanceList[i];
            if (RollingTotal < WinningNumberLuck && WinningNumberLuck <= RollingTotal + CurrentGroupBalance){
                WinningGroupLuck = GroupIDList[i];
            } else {
                RollingTotal += CurrentGroupBalance;
            }
            i++;
        }

        address[] memory GroupWinnerList = GetGroupHolders(WinningGroupLuck);
        
        for (uint j=0; j<GroupWinnerList.length; j++) {
            uint256 CurrentUserBalance = balanceOf(GroupWinnerList[j]);
            if (RollingTotal < WinningNumberLuck && WinningNumberLuck <= RollingTotal + CurrentUserBalance){
                  WinningWalletLuck = GroupWinnerList[j];
            }
            RollingTotal += CurrentUserBalance; 
        }
        JackpotStarted++;
    }
    
    
    function RunMegaLottery() public onlyOwner {

        uint256[] memory GroupIDList = ShowGroupIDList(); 
        require(JackpotStarted == GroupIDList.length + 1, "Please Prepare Lottery & End Luck Lottery");

        uint256 SecondRandomResult = uint256(keccak256(abi.encode(randomResult, 1)));
        WinningNumberMega = SecondRandomResult % RollingGroupTotal + 1;
        
        uint256 i = 0;
        WinningGroupMega = 0;
        uint256 RollingTotal = 0;        
        while (WinningGroupMega == 0){
            uint256 CurrentGroupBalance = GroupBalanceList[i];
            if (RollingTotal < WinningNumberMega && WinningNumberMega <= RollingTotal + CurrentGroupBalance){
                WinningGroupMega = GroupIDList[i];
            } else {
                RollingTotal += CurrentGroupBalance;
            }
            i++;
        }
        
        address[] memory GroupWinnerList = GetGroupHolders(WinningGroupMega);
        
        for (uint j=0; j<GroupWinnerList.length; j++) {
            uint256 CurrentUserBalance = balanceOf(GroupWinnerList[j]);
            if (RollingTotal < WinningNumberMega && WinningNumberMega <= RollingTotal + CurrentUserBalance){
                  WinningWalletMega = GroupWinnerList[j];
            }
            RollingTotal += CurrentUserBalance; 
        }
        
        WeekCounter++;
        
        _unpause();
        
        uint256 MegaPotBalance = balanceOf(MegaPot);
        _burn(MegaPot, MegaPotBalance);
        _mint(WinningWalletMega, MegaPotBalance);

        _mint(LuckPot, RollingGroupTotal/100);
    
        LastJackpotTime = block.timestamp;
        NextJackpotTime = LastJackpotTime + JackpotFreq;

        JackpotStarted = 0;
    }
    
    function PayLuckWinner() public onlyOwner {
        uint256 LuckPotBalance = balanceOf(LuckPot);
        _burn(LuckPot, LuckPotBalance);
        _mint(WinningWalletLuck, LuckPotBalance);

    }
    
    function GetTrueGroupBalance(uint256 Groups) internal view returns(uint256){
        uint256 Temp; 
        address[] memory TempList = GetGroupHolders(Groups); 
        for (uint j=0; j<TempList.length; j++) {
            Temp += balanceOf(TempList[j]);
        }
        return Temp;
    }    
    
    function ShowGroupIDList() internal view virtual returns(uint256[] memory){
        return GroupIDList;
    }

    function CurrentTime() public view virtual returns (uint){
        return block.timestamp;
    }     
    
    function ChangeLotteryFreq(uint256 Freq) public onlyOwner {
        JackpotFreq = Freq;
    }
    
    function ResetLotteryStarted() public onlyOwner {
        JackpotStarted = 0;
    }
    
    function CurrentMegaPotSize() public view returns(uint256){
        return balanceOf(MegaPot);
    }    
    
    function CurrentLuckPotSize() public view returns(uint256){
        return balanceOf(LuckPot);
    }       
    
     function getRandomNumber(uint256 userProvidedSeed) internal returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
        return requestRandomness(keyHash, fee, userProvidedSeed);
    }

    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResult = randomness;
    }

    function withdrawLink() public onlyOwner {
        require(LINK.transfer(msg.sender, LINK.balanceOf(address(this))), "Unable to transfer");
    }   
    
    function _beforeTokenTransfer(address from, address to, uint256 amount)
        internal
        whenNotPaused
        override(ERC20Upgradeable)
    {
        super._beforeTokenTransfer(from, to, amount);
    }
        
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }    

    function pause() public onlyOwner{
        _pause();
    }

    function unpause() public onlyOwner{
        _unpause();
    }
}