library NTA3DKeysCalc {
    using SafeMath for *;
    uint256 constant private keyPriceTrigger = 50000 * 1e18;
    uint256 constant private keyPriceFirst = 0.0005 ether;
    uint256 constant private keyPriceAdd = 0.0001 ether;
    /**
     * @dev calculates number of keys received given X eth
     * _curEth current amount of eth in contract
     * _newEth eth being spent
     * @return amount of ticket purchased
     */
    function keysRec(uint256 _curKeys, uint256 _allEth)
        internal
        pure
        returns (uint256)
    {
        return(keys(_curKeys, _allEth));
    }
    function ethRec(uint256 _curKeys, uint256 _sellKeys)
        internal
        pure
        returns (uint256)
    {
        return(eth(_sellKeys.add(_curKeys)).sub(eth(_curKeys)));
    }
    function keys(uint256 _keys, uint256 _eth)
        internal
        pure
        returns(uint256)
    {
        uint256 _times = _keys / keyPriceTrigger;
        uint i = 0;
        uint256 eth1;
        uint256 eth2;
        uint256 price;
        uint256 key2;
        for(i = _times;i < i + 200; i++) {
            if(eth(keyPriceTrigger * (i + 1)) >=  _eth) {
                if(i == 0) eth1 = 0;
                else eth1 = eth(keyPriceTrigger * i);
                eth2 = _eth.sub(eth1);
                price = i.mul(keyPriceAdd).add(keyPriceFirst);
                key2 = (eth2 / price).mul(1e18);
                return ((keyPriceTrigger * i + key2).sub0(_keys));
                break;
            }
        }
        //too large
        require(false, "too large eth in");

    }
     //tested
    function eth(uint256 _keys)
        internal
        pure
        returns(uint256)
    {
        uint256 _times = _keys / keyPriceTrigger;//keyPriceTrigger;
        uint256 _remain = _keys % keyPriceTrigger;//keyPriceTrigger;
        uint256 _price = _times.mul(keyPriceAdd).add(keyPriceFirst);
        if (_times == 0) {
            return (keyPriceFirst.mul(_keys / 1e18));
        } else {
            uint256 _up = (_price.sub(keyPriceFirst)).mul(_remain / 1e18);
            uint256 _down = (_keys / 1e18).mul(keyPriceFirst);
            uint256 _add = (_times.mul(_times).sub(_times) / 2).mul(keyPriceAdd).mul(keyPriceTrigger / 1e18);
            return (_up + _down + _add);
        }
    }
}
function eth(uint256 _keys)
    internal
    pure
    returns(uint256)
{
    uint256 _times = _keys / keyPriceTrigger;//keyPriceTrigger;
    uint256 _remain = _keys % keyPriceTrigger;//keyPriceTrigger;
    uint256 _price = _times.mul(keyPriceAdd).add(keyPriceFirst);
    if (_times == 0) {
        return (keyPriceFirst.mul(_keys / 1e18));
    } else {
        uint256 _up = (_price.sub(keyPriceFirst)).mul(_remain / 1e18);
        uint256 _down = (_keys / 1e18).mul(keyPriceFirst);
        uint256 _add = (_times.mul(_times).sub(_times) / 2).mul(keyPriceAdd).mul(keyPriceTrigger / 1e18);
        return (_up + _down + _add);
    }
}

//tested