pragma solidity ^0.4.18;




/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
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;
  }
}





/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/179
 */
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);
}








/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
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);
}









/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  using SafeMath for uint256;

  mapping(address => uint256) balances;

  /**
  * @dev transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  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;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balances[_owner];
  }

}









/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * @dev https://github.com/ethereum/EIPs/issues/20
 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract StandardToken is ERC20, BasicToken {

  mapping (address => mapping (address => uint256)) internal allowed;


  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amount of tokens to be transferred
   */
  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;
  }

  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   *
   * Beware that changing an allowance with this method brings the risk that someone may use both the old
   * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
   * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
    return true;
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(address _owner, address _spender) public view returns (uint256) {
    return allowed[_owner][_spender];
  }

  /**
   * approve should be called when allowed[_spender] == 0. To increment
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   */
  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;
  }

}




/**
 * @title Math
 * @dev Assorted math operations
 */

library Math {
  function max64(uint64 a, uint64 b) internal pure returns (uint64) {
    return a >= b ? a : b;
  }

  function min64(uint64 a, uint64 b) internal pure returns (uint64) {
    return a < b ? a : b;
  }

  function max256(uint256 a, uint256 b) internal pure returns (uint256) {
    return a >= b ? a : b;
  }

  function min256(uint256 a, uint256 b) internal pure returns (uint256) {
    return a < b ? a : b;
  }
}






/**
 * @title Burnable Token
 * @dev Token that can be irreversibly burned (destroyed).
 */
contract BurnableToken is StandardToken {

    event Burn(address indexed burner, uint256 value);

    /**
     * @dev Burns a specific amount of tokens.
     * @param _value The amount of token to be burned.
     */
    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);
    }
}





/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;


  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);


  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }


  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }


  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

}









contract Jewel {
    function incise(address owner, uint256 value) external returns (uint);
}

contract DayQualitys {
    function getAreaQualityByDay(uint32 time, uint32 area) external returns (uint32);
}

contract Mineral is BurnableToken, Ownable {

    string public name = "Mineral";
    string public symbol = "ORE";
    uint8 public decimals = 18;

    uint256 public constant INITIAL_SUPPLY = 800 * 1000 * 1000 * (10 ** uint256(decimals));

    uint public constant MINER_4_HOURS = 0.0005 ether;
    uint public constant MINER_8_HOURS = 0.001 ether;
    uint public constant MINER_24_HOURS = 0.003 ether;

    mapping(address => uint[][72]) public deployRange;

    
    
    uint public timeScale = 1; 

    
    
    mapping(uint32 => uint32[3][72]) private areaHourDeployed;

    
    struct AreaHourDeployed {
        uint32[72] lastCollectHour;
        
        mapping(uint32 => uint32[3][72]) hour; 
    }
    
    
    mapping(address => AreaHourDeployed) private userAreaHourDeployed;

    
    uint8 public constant CHECK_POINT_HOUR = 4;

    
    
    mapping(uint32 => uint32[72]) private areaCheckPoints;

    
    mapping(uint32 => uint) private dayAverageOutput;

    
    struct AreaCheckPoint {
        
        mapping(uint32 => uint32[72]) hour;
    }

    
    
    mapping(address => AreaCheckPoint) private userAreaCheckPoints;

    uint256 amountEther;

    
    mapping (address => uint) public remainEther;

    uint32 public constractDeployTime = uint32(now) / 1 hours * 1 hours;

    mapping(address => uint) activeArea; 
    
    bool enableWhiteList = true;
    mapping(address => bool) whiteUserList;    
    address serverAddress;

    address coldWallet;

    bool enableCheckArea = true;

    Jewel public jewelContract;
    DayQualitys public dayQualitysContract;

    event Pause();
    event Unpause();

    bool public paused = false;

    function Mineral() public {
        totalSupply = INITIAL_SUPPLY;
        balances[this] = 300 * 1000 * 1000 * (10 ** uint256(decimals));
        balances[parseAddr("0x22de6b7F8b6119bA8E62FB4165834eA00adb6f3E")] = 110 * 1000 * 1000 * (10 ** uint256(decimals));
        balances[parseAddr("0xA3eCD9F46CCfE4D27D747C9c7469990df7412d48")] = 30 * 1000 * 1000 * (10 ** uint256(decimals));
        balances[parseAddr("0x686824DB069586aC0aD8060816F1D66A0EE8297b")] = 60 * 1000 * 1000 * (10 ** uint256(decimals));
        balances[parseAddr("0x9E8eA5C674EBd85868215ceFab9c108Ab9ceA702")] = 150 * 1000 * 1000 * (10 ** uint256(decimals));
        balances[parseAddr("0x4706f5d2a0d4D4eE5A37dDE1438C7de774A2A184")] = 150 * 1000 * 1000 * (10 ** uint256(decimals));
        dayAverageOutput[0] = 241920 * 10 ** uint256(decimals);
    }

    function parseAddr(string _a) internal returns (address){
        bytes memory tmp = bytes(_a);
        uint160 iaddr = 0;
        uint160 b1;
        uint160 b2;
        for (uint i=2; i<2+2*20; i+=2){
            iaddr *= 256;
            b1 = uint160(tmp[i]);
            b2 = uint160(tmp[i+1]);
            if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87;
            else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48;
            if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87;
            else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48;
            iaddr += (b1*16+b2);
        }
        return address(iaddr);
    }

    /*
    function setTimeScale(uint scale) public onlyOwner {
        timeScale = scale;
    }

    
    function setConstractDeployTime(uint32 time) public onlyOwner {
        constractDeployTime = time;
    }*/

    function setColdWallet(address _coldWallet) public onlyOwner {
        coldWallet = _coldWallet;
    }

    function disableWhiteUserList() public onlyOwner {
        enableWhiteList = false;
    }

    function disableCheckArea() public onlyOwner {
        enableCheckArea = false;
    }

    modifier checkWhiteList() {
        if (enableWhiteList) {
            require(whiteUserList[msg.sender]);
        }
        _;
    }

    function setServerAddress(address addr) public onlyOwner {
        serverAddress = addr;
    }

    function authUser(string addr) public {
        require(msg.sender == serverAddress || msg.sender == owner);
        address s = bytesToAddress(bytes(addr));
        whiteUserList[s] = true;
    }

    function bytesToAddress (bytes b) internal view returns (address) {
        uint result = 0;
        for (uint i = 0; i < b.length; i++) {
            uint c = uint(b[i]);
            if (c >= 48 && c <= 57) {
                result = result * 16 + (c - 48);
            }
            if(c >= 65 && c <= 90) {
                result = result * 16 + (c - 55);
            }
            if(c >= 97 && c <= 122) {
                result = result * 16 + (c - 87);
            }
        }
        return address(result);
    }

    function setDayQualitys(address dayQualitys) public onlyOwner {
        dayQualitysContract = DayQualitys(dayQualitys);
    }

    function getMyDeployAt(uint32 area, uint32 hour) public view returns (uint32[3]) {
        return userAreaHourDeployed[msg.sender].hour[hour][area];
    }

    function getMyMinersAt(uint32 area, uint32 hour) public view returns (uint32) {
        return _getUserMinersAt(msg.sender, area, hour);
    }

    function _getUserMinersAt(address user, uint32 area, uint32 hour) internal view returns(uint32) {
        //now start from start's nearest check point
        uint32 nc = hour/CHECK_POINT_HOUR*CHECK_POINT_HOUR;
        if (userAreaCheckPoints[user].hour[nc][area] == 0 && userAreaCheckPoints[user].hour[nc + CHECK_POINT_HOUR][area] == 0) {
            return 0;
        }
        uint32 h = 0;
        int64 userInc = 0;
        uint32[3] storage ptUser;
        AreaHourDeployed storage _userAreaHourDeployed = userAreaHourDeployed[user];
        
        for (h = nc; h <= hour; ++h) {
            
            
            
            ptUser = _userAreaHourDeployed.hour[h][area];
            userInc += ptUser[0] + ptUser[1] + ptUser[2] - _userAreaHourDeployed.hour[h - 4][area][0] - 
                _userAreaHourDeployed.hour[h - 8][area][1] - _userAreaHourDeployed.hour[h - 24][area][2];
        }
        return userAreaCheckPoints[user].hour[nc][area] + uint32(userInc);
    }

    function getDeployAt(uint32 area, uint32 hour) public view returns (uint32[3]) {
        return areaHourDeployed[hour][area];
    }


    function getMinersAt(uint32 area, uint32 hour) public view returns (uint32) {
        return _getMinersAt(area, hour);
    }

    function _getMinersAt(uint32 area, uint32 hour) internal view returns (uint32) {
        //now start from start's nearest check point
        uint32 nc = hour/CHECK_POINT_HOUR*CHECK_POINT_HOUR;
        uint32 h = 0;
        int64 userInc = 0;
        int64 totalInc = 0;
        uint32[3] storage ptArea;
        
        for (h = nc; h <= hour; ++h) {
            
            
            
            ptArea = areaHourDeployed[h][area];
            totalInc += ptArea[0] + ptArea[1] + ptArea[2] - areaHourDeployed[h - 4][area][0] - areaHourDeployed[h - 8][area][1] - areaHourDeployed[h - 24][area][2];
        }

        return areaCheckPoints[nc][area] + uint32(totalInc);
    }

    function updateArea(uint areaId) internal pure returns (uint) {
        
        uint row = areaId / 8;
        uint colum = areaId % 8;

        uint result = uint(1) << areaId;
        if (row-1 >= 0) {
            result |= uint(1) << ((row-1)*8+colum);
        }
        if (row+1 < 9) {
            result |= uint(1) << ((row+1)*8+colum);
        }
        if (colum-1 >= 0) {
            result |= uint(1) << (row*8+colum-1);
        }
        if (colum+1 < 8) {
            result |= uint(1) << (row*8+colum+1);
        }
        
        return result;
    }

    function checkArea(uint32[] area, address user) internal {
        if (enableCheckArea) {
            uint[] memory distinctArea = new uint[](area.length);
            uint distinctAreaLength = 0;
        
            for (uint i = 0; i < area.length; i++) {
                bool find = false;
                for (uint j = 0; j < distinctAreaLength; j++) {
                    if (distinctArea[j] == area[i]) {
                        find = true;
                        break;
                    }
                }     
                if (!find) {
                    distinctArea[distinctAreaLength] = area[i];
                    distinctAreaLength += 1;
                }
            }

            if (activeArea[user] == 0) {
                require(distinctAreaLength == 1);
                activeArea[user] = updateArea(distinctArea[0]);
            } else {
                uint userActiveArea = activeArea[user];
                uint updateActiveArea = userActiveArea;
                for (i = 0; i < distinctAreaLength; i++) {
                    require(userActiveArea & uint(1) << distinctArea[i] > 0);
                    updateActiveArea = updateActiveArea | updateArea(distinctArea[i]);
                }

                activeArea[user] = updateActiveArea;
            }
        }
    }

    function deployMiners(address user, uint32[] area, uint32[] period, uint32[] count) public checkWhiteList whenNotPaused payable {
        require(area.length > 0);
        require(area.length == period.length);
        require(area.length == count.length);
        address _user = user;
        if (_user == address(0)) {
            _user = msg.sender;
        }
        
        uint32 _hour = uint32((now - constractDeployTime) * timeScale / 1 hours);

        checkArea(area, user);
        
        uint payment = _deployMiners(_user, _hour, area, period, count);
        _updateCheckPoints(_user, _hour, area, period, count);

        require(payment <= msg.value);
        remainEther[msg.sender] += (msg.value - payment);
        if (coldWallet != address(0)) {
            coldWallet.transfer(payment);
        } else {
            amountEther += payment;
        }
        
    }

    /*function deployMinersTest(uint32 _hour, address user, uint32[] area, uint32[] period, uint32[] count) public checkWhiteList payable {
        require(area.length > 0);
        require(area.length == period.length);
        require(area.length == count.length);
        address _user = user;
        if (_user == address(0)) {
            _user = msg.sender;
        }
        

        checkArea(area, user);
        
        uint payment = _deployMiners(_user, _hour, area, period, count);
        _updateCheckPoints(_user, _hour, area, period, count);

        require(payment <= msg.value);
        remainEther[msg.sender] += (msg.value - payment);
        amountEther += payment;
    }*/

    function _deployMiners(address _user, uint32 _hour, uint32[] memory area, uint32[] memory period, uint32[] memory count) internal returns(uint){
        uint payment = 0;
        uint32 minerCount = 0;
        uint32[3][72] storage _areaDeployed = areaHourDeployed[_hour];
        uint32[3][72] storage _userAreaDeployed = userAreaHourDeployed[_user].hour[_hour];
        
        
        for (uint index = 0; index < area.length; ++index) {
            require (period[index] == 4 || period[index] == 8 || period[index] == 24);
            if (period[index] == 4) {
                _areaDeployed[area[index]][0] += count[index];
                _userAreaDeployed[area[index]][0] += count[index];
                payment += count[index] * MINER_4_HOURS;
            } else if (period[index] == 8) {
                _areaDeployed[area[index]][1] += count[index];
                _userAreaDeployed[area[index]][1] += count[index];
                payment += count[index] * MINER_8_HOURS;
            } else if (period[index] == 24) {
                _areaDeployed[area[index]][2] += count[index];
                _userAreaDeployed[area[index]][2] += count[index];
                payment += count[index] * MINER_24_HOURS;
            }
            minerCount += count[index];
            DeployMiner(_user, area[index], _hour, _hour + period[index], count[index]);

            adjustDeployRange(area[index], _hour, _hour + period[index]);
        }
        return payment;
    }   

    function adjustDeployRange(uint area, uint start, uint end) internal {
        uint len = deployRange[msg.sender][area].length;
        if (len == 0) {
            deployRange[msg.sender][area].push(start | (end << 128));
        } else {
            uint s = uint128(deployRange[msg.sender][area][len - 1]);
            uint e = uint128(deployRange[msg.sender][area][len - 1] >> 128);
            
            if (start >= s && start < e) {
                end = e > end ? e : end;
                deployRange[msg.sender][area][len - 1] = s | (end << 128);
            } else {
                deployRange[msg.sender][area].push(start | (end << 128));
            }
        }
    }

    function getDeployArrayLength(uint area) public view returns (uint) {
        return deployRange[msg.sender][area].length;
    }
    
    function getDeploy(uint area, uint index) public view returns (uint,uint) {
        uint s = uint128(deployRange[msg.sender][area][index]);
        uint e = uint128(deployRange[msg.sender][area][index] >> 128);
        return (s, e);
    }

    function _updateCheckPoints(address _user, uint32 _hour, uint32[] memory area, uint32[] memory period, uint32[] memory count) internal {
        uint32 _area = 0;
        uint32 _count = 0;
        uint32 ce4 = _hour + 4;
        uint32 ce8 = _hour + 8;
        uint32 ce24 = _hour + 24;
        uint32 cs = (_hour/CHECK_POINT_HOUR+1)*CHECK_POINT_HOUR;
        AreaCheckPoint storage _userAreaCheckPoints = userAreaCheckPoints[_user];
        uint32 cp = 0;
        for (uint index = 0; index < area.length; ++index) {
            _area = area[index];
            _count = count[index];
            if (period[index] == 4) {
                for (cp = cs; cp <= ce4; cp += CHECK_POINT_HOUR) {
                    areaCheckPoints[cp][_area] += _count;
                    _userAreaCheckPoints.hour[cp][_area] += _count;
                }
            } else if (period[index] == 8) {
                for (cp = cs; cp <= ce8; cp += CHECK_POINT_HOUR) {
                    areaCheckPoints[cp][_area] += _count;
                    _userAreaCheckPoints.hour[cp][_area] += _count;
                }
            } else if (period[index] == 24) {
                for (cp = cs; cp <= ce24; cp += CHECK_POINT_HOUR) {
                    areaCheckPoints[cp][_area] += _count;
                    _userAreaCheckPoints.hour[cp][_area] += _count;
                }
            }
        }
    }

    

    event DeployMiner(address addr, uint32 area, uint32 start, uint32 end, uint32 count);

    event Collect(address addr, uint32 area, uint32 start, uint32 end, uint areaCount);

    function getMyLastCollectHour(uint32 area) public view returns (uint32){
        return userAreaHourDeployed[msg.sender].lastCollectHour[area];
    }

    
    
    function collect(address user, uint32[] area) public  checkWhiteList whenNotPaused {
        require(address(dayQualitysContract) != address(0));
        uint32 current = uint32((now - constractDeployTime) * timeScale / 1 hours);
        require(area.length > 0);
        address _user = user;
        if (_user == address(0)) {
            _user = msg.sender;
        }
        uint total = 0;
        
        for (uint a = 0; a < area.length; ++a) {
            uint len = deployRange[msg.sender][area[a]].length;
            bool finish = true;
            for (uint i = 0; i < len; i += 1) {
                uint s = uint128(deployRange[msg.sender][area[a]][i]);
                uint e = uint128(deployRange[msg.sender][area[a]][i] >> 128);
                if (current < e && current >= s ) {
                    total += _collect(_user, uint32(s), current, area[a]);
                    
                    deployRange[msg.sender][area[a]][i] = current | (e << 128);
                    finish = false;
                } else if (current >= e) {
                    total += _collect(_user, uint32(s), uint32(e), area[a]);
                }
            }
            
            if (finish) {
                deployRange[msg.sender][area[a]].length = 0;
            } else {
                deployRange[msg.sender][area[a]][0] = deployRange[msg.sender][area[a]][len - 1];
                deployRange[msg.sender][area[a]].length = 1;
            }
        }    

        ERC20(this).transfer(_user, total);
    }

    function _collect(address _user, uint32 start, uint32 end, uint32 area) internal returns (uint) {
        uint result = 0;
        uint32 writeCount = 1;
        uint income = 0;
        uint32[] memory totalMiners = new uint32[](CHECK_POINT_HOUR);
        uint32[] memory userMiners = new uint32[](CHECK_POINT_HOUR);
        uint32 ps = start/CHECK_POINT_HOUR*CHECK_POINT_HOUR+CHECK_POINT_HOUR;
        if (ps >= end) {
            
            (income, writeCount) = _collectMinersByCheckPoints(_user, area, start, end, totalMiners, userMiners, writeCount);
            result += income;
        } else {
            
            (income, writeCount) = _collectMinersByCheckPoints(_user, area, start, ps, totalMiners, userMiners, writeCount);
            result += income;

            while (ps < end) {
                (income, writeCount) = _collectMinersByCheckPoints(_user, area, ps, uint32(Math.min64(end, ps + CHECK_POINT_HOUR)), totalMiners, userMiners, writeCount);
                result += income;

                ps += CHECK_POINT_HOUR;
            }
        }
        Collect(_user, area, start, end, result);
        return result;
    }

    function _collectMinersByCheckPoints(address _user, uint32 area, uint32 start, uint32 end, uint32[] memory totalMiners, uint32[] memory userMiners, uint32 _writeCount) internal returns (uint income, uint32 writeCount) {
        //now start from start's nearest check point
        writeCount = _writeCount;
        income = 0;
        
        
        if (userAreaCheckPoints[_user].hour[start/CHECK_POINT_HOUR*CHECK_POINT_HOUR][area] == 0 && userAreaCheckPoints[_user].hour[start/CHECK_POINT_HOUR*CHECK_POINT_HOUR + CHECK_POINT_HOUR][area] == 0) {
            return;
        }
        _getMinersByCheckPoints(_user, area, start, end, totalMiners, userMiners);
        uint ao = dayAverageOutput[start / 24];
        if (ao == 0) {
            uint32 d = start / 24;
            for (; d >= 0; --d) {
                if (dayAverageOutput[d] != 0) {
                    break;
                }
            } 
            ao = dayAverageOutput[d];
            for (d = d+1; d <= start / 24; ++d) {
                ao = ao*9996/10000;
                if ((start / 24 - d) < writeCount) {
                    dayAverageOutput[d] = ao;
                }
            }
            if (writeCount > (start / 24 - d - 1)) {
                writeCount = writeCount - (start / 24 - d - 1);
            } else {
                writeCount = 0;
            }
        }

        uint week = dayQualitysContract.getAreaQualityByDay(uint32(start * 1 hours + constractDeployTime), area);
        require(week > 0);

        ao = week * ao / 10 / 24 / 72;
        
        income = _getTotalIncomeAt(end - start, userMiners, totalMiners, ao, week);

        if (week == 10) { 
            income = income * 8 / 10;
        } else if (week == 5) { 
            income = income * 6 / 10;
        } 
    }

    function _getTotalIncomeAt(uint32 hourLength, uint32[] memory userMiners, uint32[] memory totalMiners, uint areaOutput, uint week) internal view returns(uint) {
        uint income = 0;
        for (uint i = 0; i < hourLength; ++i) {
            if (userMiners[i] != 0 && totalMiners[i] != 0) {
                income += (Math.min256(10 ** uint256(decimals), areaOutput / totalMiners[i]) * userMiners[i]);
            }
        }
        return income;
    } 

    function _getMinersByCheckPoints(address _user, uint32 area, uint32 start, uint32 end, uint32[] memory totalMiners, uint32[] memory userMiners) internal view {
        require((end - start) <= CHECK_POINT_HOUR);
        //now start from start's nearest check point
        uint32 h = 0;
        int64 userInc = 0;
        int64 totalInc = 0;
        uint32[3] storage ptUser;
        uint32[3] storage ptArea;
        AreaHourDeployed storage _userAreaHourDeployed = userAreaHourDeployed[_user];
        
        for (h = start/CHECK_POINT_HOUR*CHECK_POINT_HOUR; h <= start; ++h) {
            
            
            
            ptUser = _userAreaHourDeployed.hour[h][area];
            ptArea = areaHourDeployed[h][area];
            totalInc += ptArea[0] + ptArea[1] + ptArea[2] - areaHourDeployed[h - 4][area][0] - areaHourDeployed[h - 8][area][1] - areaHourDeployed[h - 24][area][2];
            userInc += ptUser[0] + ptUser[1] + ptUser[2] - _userAreaHourDeployed.hour[h - 4][area][0] - _userAreaHourDeployed.hour[h - 8][area][1] - _userAreaHourDeployed.hour[h - 24][area][2];
        }

        totalMiners[0] = areaCheckPoints[start/CHECK_POINT_HOUR*CHECK_POINT_HOUR][area] + uint32(totalInc);
        userMiners[0] = userAreaCheckPoints[_user].hour[start/CHECK_POINT_HOUR*CHECK_POINT_HOUR][area] + uint32(userInc);

        uint32 i = 1;
        for (h = start + 1; h < end; ++h) {
            
            
            
            ptUser = _userAreaHourDeployed.hour[h][area];
            ptArea = areaHourDeployed[h][area];
            totalMiners[i] = totalMiners[i-1] + ptArea[0] + ptArea[1] + ptArea[2] - areaHourDeployed[h - 4][area][0] - areaHourDeployed[h - 8][area][1] - areaHourDeployed[h - 24][area][2];
            userMiners[i] = userMiners[i-1] + ptUser[0] + ptUser[1] + ptUser[2] - _userAreaHourDeployed.hour[h - 4][area][0] - _userAreaHourDeployed.hour[h - 8][area][1] - _userAreaHourDeployed.hour[h - 24][area][2];
            ++i;
        }
    }

    
    function withdraw() public {
        uint remain = remainEther[msg.sender]; 
        require(remain > 0);
        remainEther[msg.sender] = 0;

        msg.sender.transfer(remain);
    }

    
    function withdrawMinerFee() public onlyOwner {
        require(amountEther > 0);
        owner.transfer(amountEther);
        amountEther = 0;
    }

    modifier whenNotPaused() {
        require(!paused);
        _;
    }

    modifier whenPaused() {
        require(paused);
        _;
    }

    function pause() onlyOwner whenNotPaused public {
        paused = true;
        Pause();
    }

    function unpause() onlyOwner whenPaused public {
        paused = false;
        Unpause();
    }

    function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
        return super.transfer(_to, _value);
    }

    function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) {
        return super.transferFrom(_from, _to, _value);
    }

    function setJewelContract(address jewel) public onlyOwner {
        jewelContract = Jewel(jewel);
    }

    function incise(uint256 value) public returns (uint) {
        require(jewelContract != address(0));

        uint256 balance = balances[msg.sender];
        require(balance >= value);
        uint256 count = (value / (10 ** uint256(decimals)));
        require(count >= 1);

        uint ret = jewelContract.incise(msg.sender, count);

        burn(count * 10 ** uint256(decimals));

        return ret;
    }
}