function buy (address _referral, uint _countryId) public payable active {
    require(msg.value >= Utils.regularTicketPrice());
    require(msg.value <= 100000 ether);
    require(codesOfAddress[_referral].length > 0);
    require(_countryId != MAIN_SCHEME);
    require(Utils.validCountryId(_countryId));

    (uint tickets, uint excess) = Utils.ticketsForWithExcess(msg.value);
    uint value = msg.value.sub(excess);

    require(tickets > 0);
    require(value.add(excess) == msg.value);

    Info storage info = infoOfAddress[msg.sender];

    if (info.countryId == 0) {
      info.referral = _referral;
      info.countryId = _countryId;
    }

    uint vdivs = Utils.percentageOf(value, TO_DIVIDENDS);
    uint vreferral = Utils.percentageOf(value, TO_REFERRAL);
    uint vdevs = Utils.percentageOf(value, TO_DEVELOPERS);
    uint vcountry = Utils.percentageOf(value, TO_COUNTRY);
    uint vpot = value.sub(vdivs).sub(vreferral).sub(vdevs).sub(vcountry);

    assert(vdivs.add(vreferral).add(vdevs).add(vcountry).add(vpot) == value);

    buyShares(MAIN_SCHEME, msg.sender, tickets, vdivs);
    buyShares(info.countryId, msg.sender, tickets, vcountry);

    creditVault(MAIN_SCHEME, info.referral, vreferral);
    creditVault(MAIN_SCHEME, addressOfOwner, vdevs);

    if (excess > 0) {
      creditVault(MAIN_SCHEME, msg.sender, excess);
    }

    uint goldenTickets = value.div(Utils.goldenTicketPrice(totalPot));
    if (goldenTickets > 0) {
      endTime = endTime.add(goldenTickets.mul(addedTime)) > block.timestamp.add(maxTime) ?
        block.timestamp.add(maxTime) : endTime.add(goldenTickets.mul(addedTime));
      addressOfCaptain = msg.sender;
      emit Promoted(addressOfCaptain, goldenTickets, endTime);
    }

    totalPot = totalPot.add(vpot);

    emit Bought(msg.sender, info.referral, info.countryId, tickets, value, excess);
  }