pragma solidity ^0.4.18;



contract ERC20Basic {
  uint256 public totalSupply;
  function balanceOf(address who) public view returns (uint256);
  function transfer(address to, uint256 value) public returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
}




library SafeMath {
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    
    uint256 c = a / b;
    
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}




contract BasicToken is ERC20Basic {
  using SafeMath for uint256;

  mapping(address => uint256) balances;

  
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);

    
    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    Transfer(msg.sender, _to, _value);
    return true;
  }

  
  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balances[_owner];
  }

}




contract ERC20 is ERC20Basic {
  function allowance(address owner, address spender) public view returns (uint256);
  function transferFrom(address from, address to, uint256 value) public returns (bool);
  function approve(address spender, uint256 value) public returns (bool);
  event Approval(address indexed owner, address indexed spender, uint256 value);
}




contract StandardToken is ERC20, BasicToken {

  mapping (address => mapping (address => uint256)) internal allowed;


  
  function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);

    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    Transfer(_from, _to, _value);
    return true;
  }

  
  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
    return true;
  }

  
  function allowance(address _owner, address _spender) public view returns (uint256) {
    return allowed[_owner][_spender];
  }

  
  function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
    allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
    Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

  function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
    uint oldValue = allowed[msg.sender][_spender];
    if (_subtractedValue > oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
    }
    Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }

}




contract Ownable {
  address public owner;


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


  
  function Ownable() public {
    owner = msg.sender;
  }


  
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }


  
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

}



library SafeERC20 {
  function safeTransfer(ERC20Basic token, address to, uint256 value) internal {
    assert(token.transfer(to, value));
  }

  function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
    assert(token.transferFrom(from, to, value));
  }

  function safeApprove(ERC20 token, address spender, uint256 value) internal {
    assert(token.approve(spender, value));
  }
}





contract MintableToken is StandardToken, Ownable {
  event Mint(address indexed to, uint256 amount);
  event MintFinished();

  bool public mintingFinished = false;


  modifier canMint() {
    require(!mintingFinished);
    _;
  }

  
  function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
    totalSupply = totalSupply.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    Mint(_to, _amount);
    Transfer(address(0), _to, _amount);
    return true;
  }

  
  function finishMinting() onlyOwner canMint public returns (bool) {
    mintingFinished = true;
    MintFinished();
    return true;
  }
}



contract FreezableToken is StandardToken {
    mapping (address => uint64) internal roots;

    mapping (bytes32 => uint64) internal chains;

    event Freezed(address indexed to, uint64 release, uint amount);
    event Released(address indexed owner, uint amount);

    
    function getFreezingSummaryOf(address _addr) public constant returns (uint tokenAmount, uint freezingCount) {
        uint count;
        uint total;
        uint64 release = roots[_addr];
        while (release != 0) {
            count ++;
            total += balanceOf(address(keccak256(toKey(_addr, release))));
            release = chains[toKey(_addr, release)];
        }

        return (total, count);
    }

    
    function getFreezing(address _addr, uint _index) public constant returns (uint64 _release, uint _balance) {
        uint64 release = roots[_addr];
        for (uint i = 0; i < _index; i ++) {
            release = chains[toKey(_addr, release)];
        }
        return (release, balanceOf(address(keccak256(toKey(_addr, release)))));
    }

    
    function freezeTo(address _to, uint _amount, uint64 _until) public {
        bytes32 currentKey = toKey(_to, _until);
        transfer(address(keccak256(currentKey)), _amount);

        freeze(_to, _until);
        Freezed(_to, _until, _amount);
    }

    
    function releaseOnce() public {
        uint64 head = roots[msg.sender];
        require(head != 0);
        require(uint64(block.timestamp) > head);
        bytes32 currentKey = toKey(msg.sender, head);

        uint64 next = chains[currentKey];

        address currentAddress = address(keccak256(currentKey));
        uint amount = balances[currentAddress];
        delete balances[currentAddress];

        balances[msg.sender] += amount;

        if (next == 0) {
            delete roots[msg.sender];
        }
        else {
            roots[msg.sender] = next;
        }
        Released(msg.sender, amount);
    }

    
    function releaseAll() public returns (uint tokens) {
        uint release;
        uint balance;
        (release, balance) = getFreezing(msg.sender, 0);
        while (release != 0 && block.timestamp > release) {
            releaseOnce();
            tokens += balance;
            (release, balance) = getFreezing(msg.sender, 0);
        }
    }

    function toKey(address _addr, uint _release) internal constant returns (bytes32 result) {
        
        result = 0x5749534800000000000000000000000000000000000000000000000000000000;
        assembly {
            result := or(result, mul(_addr, 0x10000000000000000))
            result := or(result, _release)
        }
    }

    function freeze(address _to, uint64 _until) internal {
        require(_until > block.timestamp);
        uint64 head = roots[_to];

        if (head == 0) {
            roots[_to] = _until;
            return;
        }

        bytes32 headKey = toKey(_to, head);
        uint parent;
        bytes32 parentKey;

        while (head != 0 && _until > head) {
            parent = head;
            parentKey = headKey;

            head = chains[headKey];
            headKey = toKey(_to, head);
        }

        if (_until == head) {
            return;
        }

        if (head != 0) {
            chains[toKey(_to, _until)] = head;
        }

        if (parent == 0) {
            roots[_to] = _until;
        }
        else {
            chains[parentKey] = _until;
        }
    }
}


contract BurnableToken is StandardToken {

    event Burn(address indexed burner, uint256 value);

    
    function burn(uint256 _value) public {
        require(_value > 0);
        require(_value <= balances[msg.sender]);
        
        

        address burner = msg.sender;
        balances[burner] = balances[burner].sub(_value);
        totalSupply = totalSupply.sub(_value);
        Burn(burner, _value);
    }
}




contract Pausable is Ownable {
  event Pause();
  event Unpause();

  bool public paused = false;


  
  modifier whenNotPaused() {
    require(!paused);
    _;
  }

  
  modifier whenPaused() {
    require(paused);
    _;
  }

  
  function pause() onlyOwner whenNotPaused public {
    paused = true;
    Pause();
  }

  
  function unpause() onlyOwner whenPaused public {
    paused = false;
    Unpause();
  }
}




contract TokenTimelock {
  using SafeERC20 for ERC20Basic;

  
  ERC20Basic public token;

  
  address public beneficiary;

  
  uint64 public releaseTime;

  function TokenTimelock(ERC20Basic _token, address _beneficiary, uint64 _releaseTime) public {
    require(_releaseTime > now);
    token = _token;
    beneficiary = _beneficiary;
    releaseTime = _releaseTime;
  }

  
  function release() public {
    require(now >= releaseTime);

    uint256 amount = token.balanceOf(this);
    require(amount > 0);

    token.safeTransfer(beneficiary, amount);
  }
}



contract FreezableMintableToken is FreezableToken, MintableToken {
    
    function mintAndFreeze(address _to, uint _amount, uint64 _until) public onlyOwner {
        bytes32 currentKey = toKey(_to, _until);
        mint(address(keccak256(currentKey)), _amount);

        freeze(_to, _until);
        Freezed(_to, _until, _amount);
    }
}

contract usingConsts {
    uint constant TOKEN_DECIMALS = 8;
    uint8 constant TOKEN_DECIMALS_UINT8 = 8;
    uint constant TOKEN_DECIMAL_MULTIPLIER = 10 ** TOKEN_DECIMALS;

    string constant TOKEN_NAME = "Bionic";
    string constant TOKEN_SYMBOL = "BNC";
    bool constant PAUSED = true;
    address constant TARGET_USER = 0xaf85B35ee044C049e2FCfb61dE7fe434a8050B3e;
    uint constant START_TIME = 1521676848;
    bool constant CONTINUE_MINTING = true;
}



contract MainToken is usingConsts, FreezableMintableToken, BurnableToken, Pausable {
    function MainToken() {
    }

    function name() constant public returns (string _name) {
        return TOKEN_NAME;
    }

    function symbol() constant public returns (string _symbol) {
        return TOKEN_SYMBOL;
    }

    function decimals() constant public returns (uint8 _decimals) {
        return TOKEN_DECIMALS_UINT8;
    }

    function transferFrom(address _from, address _to, uint256 _value) returns (bool _success) {
        require(!paused);
        return super.transferFrom(_from, _to, _value);
    }

    function transfer(address _to, uint256 _value) returns (bool _success) {
        require(!paused);
        return super.transfer(_to, _value);
    }
}