Requesting random number on Chainlink Testnet and getting zeroes - random

I have been working on something that encapsulates chainlink random number on nfts to provide them to users, using a chainlink subscription and adding my smart contract as a comsumer. When working on testnet I am finding some times it gives me zero numbers and not a random one. This behaviour is random. Subscription is funded and smart contract added as a comsumer.
May this happen on mainnet also?
On most cases this kind of errors are only testnet timeouts, or other stuff related to slower infrastructure.
I wish to implement something to prevent shipping the NFT when zeroes are recevied:
I tried to put assert(randomNumber!=[zero]); but I got some errors, as gas stimation functions on wallets can ensure this, so they set highest fee.
Any suggestions to keep this secured?
This is the main testnet smart contract bsc address https://testnet.bscscan.com/address/0x74623BaE2c3AcC39Db224c236229dc4d5aD1F64d#code
is there any warranty the request random number operation finishes succesfully, or how can I take care of it on my code?
I have posted chainlink related functions too, where I slice the numbers to get them one by one in the lottery.
Subscription is funded and smart contract added as a comsumer. geting some times zero numbers and not a random one
these are the two functions
function closeSaleGetWinner()
external
nonReentrant
onlyRole(OPERATOR_ROLE)
{
requestRandomWords();
require(
isDrawLive,
"There's not a live draw neither ticket sale is opened"
);
uint256[6] memory numberOfMatches;
uint256 drawPrice;
// set the total pool price to BUSD smart contract balance
drawPrice =
checkBUSDContractBalance() *
allDraws[currentDraw].poolPercentage; // use 1 * 10**18 for use the whole pool
// close ticket sale and get random winner number from s_randomWords
isDrawLive = false;
//assert(s_randomWords[0]!=0);
allDraws[currentDraw].winnerNumber = s_slicedRandomWords;
//unchecked {
uint256 j;
Ticket storage _ticket;
uint256 _match;
// check and update matched numbers per ticket on allTickets structure
for (uint256 i = 0; i < drawToTickets[currentDraw].length; i++) {
j = drawToTickets[currentDraw][i];
_ticket = allTickets[j];
_match = checkWinner(_ticket);
_ticket.matchedNumbers = _match;
numberOfMatches[_match] = numberOfMatches[_match] + 1;
}
// after storing number of winners with [_match] matches calculate win per group
// it's time to overwrite reward variable with
// 1st the part of the price from the total pool for #i number of matches
// 2nd divide e
for (uint256 i = 0; i < 5; i++) {
allDraws[currentDraw].reward[i] =
allDraws[currentDraw].reward[i] *
drawPrice;
if (numberOfMatches[i + 1] > 0) {
allDraws[currentDraw].reward[i] = SafeMath.div(
allDraws[currentDraw].reward[i],
numberOfMatches[i + 1]
);
} else {
allDraws[currentDraw].reward[i] = 0;
}
}
// once stored delete random generated number for further checks
delete (s_randomWords);
delete (s_slicedRandomWords);
}
/*
compares the ticket number with the winner number of the draw and returns a value
representing matched number between 0 to 5 (6 values)
*/
function checkWinner(Ticket storage t) internal view returns (uint256) {
uint256 _match = 0;
// we go and compare digit by digit storing number of consecutive matches and stopping when
// there are no more coincidences
uint256[5] memory ticketNumber = t.number;
uint256[5] memory winnerNumber = allDraws[_drawIdCounter.current() - 1]
.winnerNumber;
for (uint256 i = 0; i < 5; i++) {
// If there exists any distinct
// lastTicketDigit, then return No
if (ticketNumber[i] == winnerNumber[i]) {
_match = _match + 1;
} else return _match;
}
return _match;
}
function buyRandomTicket() public nonReentrant {
uint256[] storage myTickets = accounts[msg.sender].myTicketsHistory;
require(isDrawLive, "Sorry there's no live draw by now");
require(!Address.isContract(msg.sender), "only EOA allowed");
address user = address(msg.sender);
uint256 _value = allDraws[currentDraw].ticketPrice;
// check balance and user allowance
uint256 busdUserBalance = BUSD.balanceOf(msg.sender);
require(busdUserBalance >= _value, "Not enough balance");
uint256 allowance = BUSD.allowance(msg.sender, address(this));
require(allowance >= _value, "Check the BUSD allowance");
uint256 devsReward = _value.mul(5).div(100);
uint256 otherReward = _value.mul(20).div(100);
// get payment
BUSD.safeTransferFrom(msg.sender, address(this), _value);
requestRandomWords();
BUSD.safeTransfer(w1, devsReward);
BUSD.safeTransfer(w2, devsReward);
BUSD.safeTransfer(w3, otherReward);
/*pay referral*/
if (accounts[msg.sender].referrer != address(0))
payReferral(_value, address(msg.sender));
Ticket memory randomTicket = Ticket(
user,
currentDraw,
_ticketIdCounter.current(),
s_slicedRandomWords,
false,
0
);
//require(s_randomWords[0]!=0);
uint256[] storage drawTickets = drawToTickets[currentDraw];
// add to the mapping to store tickets sold for a draw
drawTickets.push(randomTicket.ticketID);
myTickets.push(randomTicket.ticketID);
allTickets.push(randomTicket);
safeMint(user);
// set to 0 random storage variable for further checks
delete (s_randomWords);
delete (s_slicedRandomWords);
}
/* CHAINLINK FUNCTIONS */
// Assumes the subscription is funded sufficiently.
function requestRandomWords() internal {
// Will revert if subscription is not set and funded.
s_requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
}
function fulfillRandomWords(
uint256, /* requestId */
uint256[] memory randomWords
) internal override {
uint256 _lastDigit;
s_randomWords = randomWords;
uint256 aux = randomWords[0];
for (uint256 i = 0; i < 5; i++) {
_lastDigit = aux % 10;
s_slicedRandomWords[i] = _lastDigit;
aux = aux / 10;
}
}
```

Related

Solving problem using Zoho Deluge script in custom function

I need an output as follows for 1200+ accounts:
accounts_count = {
"a": 120,
"b": 45,
"z": 220
}
So it will take the first Letter of an account name and make count for all account with their initial letter.
How can I solve it using Zoho Deluge script in a custom function?
The hard part is getting all the contacts due to the 200 record limit in zoho
I'm not going to provide the full solution here, just the harder parts.
// this is the maximum number of records that you can query at once
max = 200;
// set to a value slightly higher than you need
max_contacts = 1500;
// round up one
n = max_contacts / max + 1;
// This is how many times you will loop (convert to int)
iterations = n.toNumber();
// this is a zoho hack to create a range of the length we want
counter = leftpad("1",iterations).replaceAll(" ","1,").toList();
// set paging
page = 1;
// TODO: initialize an alphabet map
//result = Map( {a: 0, b:0....});
// set to true when done so you're not wasting API Calls
done = false;
for each i in counter
{
// prevent extra crm calls
if(!done)
{
response = zoho.crm.getRecords("Contacts",page,max);
if(!isEmpty(response))
{
if(isNull(response.get(0)) && !isNull(response.get("code")))
{
info "Error:";
info response;
}
else
{
for each user in response
{
// this is the easy part: get the first letter (lowercase)
// get the current value and add one
// set the value again
// set in result map
}
}
}
else
{
done = true;
}
}
// increment the page
page = page + 1;
}

how to overwrite repeated feild values in google buffer protocol

my question is about,how to overwrite the repeated feild in google buffer protocol
Example
message seltMeasureParam
{
repeated integer val = 1;
}
I want to fill val with 255 up to 8000 times using for loop, then i want to fill values at some particular positions within range 4000.
Filling 255 8000 times can be filled easily, i want to know how to fill val at some particular sub-range within 4000 range.
please help in this, thanks in advance
This will depend entirely on the particular language / library that you are using. For example, if I use protobuf-net, the code-gen for repeated int32 val = 1; will generate (for that member) either (syntax="proto2"; or no syntax specified):
[global::ProtoBuf.ProtoMember(1, Name = #"val")]
public int[] Vals { get; set; }
or (syntax="proto3";)
[global::ProtoBuf.ProtoMember(1, Name = #"val", IsPacked = true)]
public int[] Vals { get; set; }
So then your code is simply:
var arr = new int[8000];
for(int i = 0; i < arr.Length; i++) arr[i] = 255;
obj.Vals = arr;
and then later: just set .Vals[someIndex] = someValue; with your other values.
How that works in other libraries and languages will vary depending on the API exposed in that framework.

How to modify a StopLoss of an active trade?

I have trouble with modifying the stoploss of a running trade using MQL5. Selecting the order works out for me. But if I try to access the variables ( for instance OrderTicket() & OrderOpenPrice() ), it always returns 0.00000:
2017.06.01 00:06:32.114 2016.04.08 00:00:00 failed modify buy 0.00 sl: 0.00000, tp: 0.00000 -> sl: 1.41594, tp: 0.00000 [Invalid request]
Here's my stoploss modyfing void:
void modifyStops() {
int total = OrdersTotal(); // total number of placed pending orders
Print( total + " Orders on the line!!!" );
//--- Over all placed pending orders
for ( int i = 0; i < total; i++ )
{ bool isOrderSelected = OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
if ( isOrderSelected )
{ // TODO: Check the Trades to contain the correct Order Number
Print( "Symbol & Magicnumber matching" );
double newStopLoss;
// Update the stop loss
if ( OrderType() == OP_BUY )
{
newStopLoss = addTolerance( SL );
}
else if ( OrderType() == OP_SELL )
{
newStopLoss = minusTolerance( SL );
}
newStopLoss = NormalizeDouble( newStopLoss, Digits );
Print( "NEW STOP LOSS::::=====> ", Symbol(), " at ", newStopLoss );
if ( !OrderModify( OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, Green ) )
{
Print( "OrderModify returned the error of ", GetLastError() );
}
}
}
The CTrade class isn't working properly for me. I tried to implement the code you've posted - however: It still doesn't seem to work out.
Unfortunately I implemented that in my EA and the OrderGetTicket(i) returns zero when the trade is live. So my void looks like this:
void modifyStops() {
for(int i=0; i<PositionsTotal();i++)
{
ulong ticket;
if((ticket=PositionGetTicket(i))>0)
{
//--- return order properties
double open_price =PositionGetDouble(POSITION_PRICE_OPEN);
datetime time_open =(datetime)PositionGetInteger(POSITION_TIME);
string symbol =PositionGetString(POSITION_SYMBOL);
int order_magic =PositionGetInteger(POSITION_MAGIC);
double volume =PositionGetDouble(POSITION_VOLUME);
double stoploss =PositionGetDouble(POSITION_SL);
double takeprofit =PositionGetDouble(POSITION_TP);
ENUM_ORDER_TYPE type =EnumToString(ENUM_ORDER_TYPE(PositionGetInteger(POSITION_TYPE)));
//--- prepare and show information about the order
printf("#ticket %d %s %G %s at %G, with sl: %G tp: %G was set up at %s",
ticket, // order ticket
type, // type
volume, // placed volume
symbol, // symbol
open_price, // specified open price
stoploss, //
takeprofit, //
TimeToString(time_open) // time of order placing
);
}
}
}
And the printf function returns nothing:
2017.06.02 01:42:26.910 2016.04.07 00:00:00 #ticket 1 (non-string passed) 0 at 0, with sl: 0 tp: 0 was set up at 1970.01.01 00:00
I can't believe it's that hard to simply modify an SL in MQL5. That's horribly. However I need to get through it to test my strategy over several pairs...
Do you have another idea? I set up trades with the following code:
void createPendingOrder(ENUM_ORDER_TYPE orderType, double lots, double stopLoss) {
MqlTradeRequest request={0};
MqlTradeResult result={0};
//--- parameters to place a pending order
request.action =TRADE_ACTION_PENDING; // type of trade operation
request.symbol =Symbol(); // symbol
request.volume =lots; // volume of 0.1 lot
//request.deviation=2; // allowed deviation from the price
request.magic =224466 ; // MagicNumber of the order
//int offset = 3; // offset from the current price to place the order, in points
double price; // order triggering price
double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // value of point
int digits=SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // number of decimal places (precision)
//--- checking the type of operation
if(orderType==ORDER_TYPE_BUY_STOP)
{
request.type =ORDER_TYPE_BUY_STOP; // order type
price =entryPrice;
request.price =NormalizeDouble(price,digits); // normalized opening price
request.sl =stopLoss;
}
else if(orderType==ORDER_TYPE_SELL_STOP)
{
request.type =ORDER_TYPE_SELL_STOP; // order type
price =entryPrice;
request.price =NormalizeDouble(price,digits); // normalized opening price
request.sl =stopLoss;
}
else Alert("This example is only for placing pending orders"); // if not pending order is selected
//--- send the request
if(!OrderSend(request,result))
PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code
//--- information about the operation
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
}
Would it for instance be possible to save the result object in a Array and then access the running trade through that object?
Your problem is you are trying to run MQL4 code in MQL5.
There are no OrderModify(), OrderOpenPrice(), OrderTicket() in MQL5!!!
See the documentation here on how to select, query values and modify trades.
you will need to be using OrderGetDouble(), OrderGetInteger() and OrderGetString() to query open price, stop loss etc.
e.g.
if((ticket=OrderGetTicket(i))>0)
{
//--- return order properties
open_price =OrderGetDouble(ORDER_PRICE_OPEN);
time_setup =(datetime)OrderGetInteger(ORDER_TIME_SETUP);
symbol =OrderGetString(ORDER_SYMBOL);
order_magic =OrderGetInteger(ORDER_MAGIC);
positionID =OrderGetInteger(ORDER_POSITION_ID);
initial_volume=OrderGetDouble(ORDER_VOLUME_INITIAL);
type =EnumToString(ENUM_ORDER_TYPE(OrderGetInteger(ORDER_TYPE)));
//--- prepare and show information about the order
printf("#ticket %d %s %G %s at %G was set up at %s",
ticket, // order ticket
type, // type
initial_volume, // placed volume
symbol, // symbol
open_price, // specified open price
TimeToString(time_setup)// time of order placing
);
}
Orders are modified using the OrderSend() function https://www.mql5.com/en/docs/trading/ordersend
Update
MQL5 uses a much more complex system of Orders, Positions, Deals and historyOrders. An MQL5 community article attempts to explain how they all relate to one another.
Orders = Pending trades (Buy Stop, Buy Limit, Sell Stop, Sell Limit)
Positions = Open trades (Buy, Sell)
HistoryOrders = Closed/Deleted Trades
Deals = transactions that make up an order/position
To loop through and look at pending Orders:
for(int i=0; i<OrdersTotal();i++)
{
if((ticket=OrderGetTicket(i))>0)
{
//--- return order properties
open_price =OrderGetDouble(ORDER_PRICE_OPEN);
time_setup =(datetime)OrderGetInteger(ORDER_TIME_SETUP);
symbol =OrderGetString(ORDER_SYMBOL);
order_magic =OrderGetInteger(ORDER_MAGIC);
positionID =OrderGetInteger(ORDER_POSITION_ID);
initial_volume=OrderGetDouble(ORDER_VOLUME_INITIAL);
type =EnumToString(ENUM_ORDER_TYPE(OrderGetInteger(ORDER_TYPE)));
//--- prepare and show information about the order
printf("#ticket %d %s %G %s at %G was set up at %s",
ticket, // order ticket
type, // type
initial_volume, // placed volume
symbol, // symbol
open_price, // specified open price
TimeToString(time_setup)// time of order placing
);
}
}
To loop through and look at open trades:
for(int i=0; i<PositionsTotal();i++)
{
if((ticket= PositionGetTicket(i))>0)
{
//--- return order properties
open_price =PositionGetDouble(POSITION_PRICE_OPEN);
time_open =(datetime)PositionGetInteger(POSITION_TIME);
symbol =PositionGetString(POSITION_SYMBOL);
order_magic =PositionGetInteger(POSITION_MAGIC);
volume =PositionGetDouble(POSITION_VOLUME);
stoploss =PositionGetDouble(POSITION_SL);
takeprofit =PositionGetDouble(POSITION_TP);
type =EnumToString(ENUM_ORDER_TYPE(PositionGetInteger(POSITION_TYPE)));
//--- prepare and show information about the order
printf("#ticket %d %s %G %s at %G, with sl: %G tp: %G was set up at %s",
ticket, // order ticket
type, // type
volume, // placed volume
symbol, // symbol
open_price, // specified open price
stoploss, //
takeprofit, //
TimeToString(time_open) // time of order placing
);
}
}
Hopefully, someone else will be able to provide a better explanation of Orders, Positions, Deals, history Orders as they still give me a headache.
To make it simpler I usually just create an instance of the CTrade Class
Update2
Minimal example of trailSL for buy Positions using standard trade functions and Ctrade class
Init
Ctrade *m_trade;
CSymbolInfo *m_symbol;
Void OnInit()
{
m_trade = new Ctrade();
m_trade.SetExpertMagicNumber(100);
m_symbol = new CSymbolInfo();
m_symbol.Name(Symbol());
}
void OnTick()
{
m_symbol.RefreshRates();
}
Trail SL of Buy trade with standard functions
void modifysl()
{
ulong ticket;
MqlTradeRequest request = {0};
MqlTradeResult result = {0};
double newsl;
for(int i=0; i<PositionsTotal();i++)
{
ticket=PositionGetTicket(i);
if(ticket>0)
{
request.action = TRADE_ACTION_SLTP; // type of trade operation
request.position = ticket; // ticket of the position
request.symbol = PositionGetString(POSITION_SYMBOL); // symbol
request.sl = PositionGetDouble(POSITION_SL); // Stop Loss of the position
request.tp = PositionGetDouble(POSITION_TP); // Take Profit of the position
request.magic = 100; // MagicNumber of the position
newsl = NormalizeDouble(m_symbol.Bid()-100*m_symbol.Point(),
m_symbol.Digits());
if(newsl>request.sl)
{
request.sl = newsl;
if(!OrderSend(request,result))
{
PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code
}
//--- information about the operation
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
}
}
}
}
Trail Buy Position StopLoss using CTrade
void modifyslCtrade()
{
ulong ticket;
MqlTradeRequest request= {0};
MqlTradeResult response ={0};
double newsl;
for(int i=0; i<PositionsTotal();i++)
{
ticket=PositionGetTicket(i);
if(ticket>0)
{
newsl = NormalizeDouble(m_symbol.Bid()-100*m_symbol.Point(),
m_symbol.Digits());
if(newsl>PositionGetDouble(POSITION_SL))
{
m_trade.PositionModify(ticket,
newsl,
PositionGetDouble(POSITION_TP));
}
}
}
}

How do I shuffle nodes in a linked list?

I just started a project for my Java2 class and I've come to a complete stop. I just can't get
my head around this method. Especially when the assignment does NOT let us use any other DATA STRUCTURE or shuffle methods from java at all.
So I have a Deck.class in which I've already created a linked list containing 52 nodes that hold 52 cards.
public class Deck {
private Node theDeck;
private int numCards;
public Deck ()
{
while(numCards < 52)
{
theDeck = new Node (new Card(numCards), theDeck);
numCards++;
}
}
public void shuffleDeck()
{
int rNum;
int count = 0;
Node current = theDeck;
Card tCard;
int range = 0;
while(count != 51)
{
// Store whatever is inside the current node in a temp variable
tCard = current.getItem();
// Generate a random number between 0 -51
rNum = (int)(Math.random()* 51);
// Send current on a loop a random amount of times
for (int i=0; i < rNum; i ++)
current = current.getNext(); ******<-- (Btw this is the line I'm getting my error, i sort of know why but idk how to stop it.)
// So wherever current landed get that item stored in that node and store it in the first on
theDeck.setItem(current.getItem());
// Now make use of the temp variable at the beginning and store it where current landed
current.setItem(tCard);
// Send current back to the beginning of the deck
current = theDeck;
// I've created a counter for another loop i want to do
count++;
// Send current a "count" amount of times for a loop so that it doesn't shuffle the cards that have been already shuffled.
for(int i=0; i<count; i++)
current = current.getNext(); ****<-- Not to sure about this last loop because if i don't shuffle the cards that i've already shuffled it will not count as a legitimate shuffle? i think? ****Also this is where i sometimes get a nullpointerexception****
}
}
}
Now I get different kinds of errors
When I call on this method:
it will sometimes shuffle just 2 cards but at times it will shuffle 3 - 5 cards then give me a NullPointerException.
I've pointed out where it gives me this error with asterisks in my code above
at one point I got it to shuffle 13 cards but then everytime it did that it didn't quite shuffle them the right way. one card kept always repeating.
at another point I got all 52 cards to go through the while loop but again it repeated one card various times.
So I really need some input in what I'm doing wrong. Towards the end of my code I think my logic is completely wrong but I can't seem to figure out a way around it.
Seems pretty long-winded.
I'd go with something like the following:
public void shuffleDeck() {
for(int i=0; i<52; i++) {
int card = (int) (Math.random() * (52-i));
deck.addLast(deck.remove(card));
}
}
So each card just gets moved to the back of the deck in a random order.
If you are authorized to use a secondary data structure, one way is simply to compute a random number within the number of remaining cards, select that card, move it to the end of the secondary structure until empty, then replace your list with the secondary list.
My implementation shuffles a linked list using a divide-and-conquer algorithm
public class LinkedListShuffle
{
public static DataStructures.Linear.LinkedListNode<T> Shuffle<T>(DataStructures.Linear.LinkedListNode<T> firstNode) where T : IComparable<T>
{
if (firstNode == null)
throw new ArgumentNullException();
if (firstNode.Next == null)
return firstNode;
var middle = GetMiddle(firstNode);
var rightNode = middle.Next;
middle.Next = null;
var mergedResult = ShuffledMerge(Shuffle(firstNode), Shuffle(rightNode));
return mergedResult;
}
private static DataStructures.Linear.LinkedListNode<T> ShuffledMerge<T>(DataStructures.Linear.LinkedListNode<T> leftNode, DataStructures.Linear.LinkedListNode<T> rightNode) where T : IComparable<T>
{
var dummyHead = new DataStructures.Linear.LinkedListNode<T>();
DataStructures.Linear.LinkedListNode<T> curNode = dummyHead;
var rnd = new Random((int)DateTime.Now.Ticks);
while (leftNode != null || rightNode != null)
{
var rndRes = rnd.Next(0, 2);
if (rndRes == 0)
{
if (leftNode != null)
{
curNode.Next = leftNode;
leftNode = leftNode.Next;
}
else
{
curNode.Next = rightNode;
rightNode = rightNode.Next;
}
}
else
{
if (rightNode != null)
{
curNode.Next = rightNode;
rightNode = rightNode.Next;
}
else
{
curNode.Next = leftNode;
leftNode = leftNode.Next;
}
}
curNode = curNode.Next;
}
return dummyHead.Next;
}
private static DataStructures.Linear.LinkedListNode<T> GetMiddle<T>(DataStructures.Linear.LinkedListNode<T> firstNode) where T : IComparable<T>
{
if (firstNode.Next == null)
return firstNode;
DataStructures.Linear.LinkedListNode<T> fast, slow;
fast = slow = firstNode;
while (fast.Next != null && fast.Next.Next != null)
{
slow = slow.Next;
fast = fast.Next.Next;
}
return slow;
}
}
Just came across this and decided to post a more concise solution which allows you to specify how much shuffling you want to do.
For the purposes of the answer, you have a linked list containing PlayingCard objects;
LinkedList<PlayingCard> deck = new LinkedList<PlayingCard>();
And to shuffle them use something like this;
public void shuffle(Integer swaps) {
for (int i=0; i < swaps; i++) {
deck.add(deck.remove((int)(Math.random() * deck.size())));
}
}
The more swaps you do, the more randomised the list will be.

How do I create a URL shortener? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Closed 1 year ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
I want to create a URL shortener service where you can write a long URL into an input field and the service shortens the URL to "http://www.example.org/abcdef".
Instead of "abcdef" there can be any other string with six characters containing a-z, A-Z and 0-9. That makes 56~57 billion possible strings.
My approach:
I have a database table with three columns:
id, integer, auto-increment
long, string, the long URL the user entered
short, string, the shortened URL (or just the six characters)
I would then insert the long URL into the table. Then I would select the auto-increment value for "id" and build a hash of it. This hash should then be inserted as "short". But what sort of hash should I build? Hash algorithms like MD5 create too long strings. I don't use these algorithms, I think. A self-built algorithm will work, too.
My idea:
For "http://www.google.de/" I get the auto-increment id 239472. Then I do the following steps:
short = '';
if divisible by 2, add "a"+the result to short
if divisible by 3, add "b"+the result to short
... until I have divisors for a-z and A-Z.
That could be repeated until the number isn't divisible any more. Do you think this is a good approach? Do you have a better idea?
Due to the ongoing interest in this topic, I've published an efficient solution to GitHub, with implementations for JavaScript, PHP, Python and Java. Add your solutions if you like :)
I would continue your "convert number to string" approach. However, you will realize that your proposed algorithm fails if your ID is a prime and greater than 52.
Theoretical background
You need a Bijective Function f. This is necessary so that you can find a inverse function g('abc') = 123 for your f(123) = 'abc' function. This means:
There must be no x1, x2 (with x1 ≠ x2) that will make f(x1) = f(x2),
and for every y you must be able to find an x so that f(x) = y.
How to convert the ID to a shortened URL
Think of an alphabet we want to use. In your case, that's [a-zA-Z0-9]. It contains 62 letters.
Take an auto-generated, unique numerical key (the auto-incremented id of a MySQL table for example).
For this example, I will use 12510 (125 with a base of 10).
Now you have to convert 12510 to X62 (base 62).
12510 = 2×621 + 1×620 = [2,1]
This requires the use of integer division and modulo. A pseudo-code example:
digits = []
while num > 0
remainder = modulo(num, 62)
digits.push(remainder)
num = divide(num, 62)
digits = digits.reverse
Now map the indices 2 and 1 to your alphabet. This is how your mapping (with an array for example) could look like:
0 → a
1 → b
...
25 → z
...
52 → 0
61 → 9
With 2 → c and 1 → b, you will receive cb62 as the shortened URL.
http://shor.ty/cb
How to resolve a shortened URL to the initial ID
The reverse is even easier. You just do a reverse lookup in your alphabet.
e9a62 will be resolved to "4th, 61st, and 0th letter in the alphabet".
e9a62 = [4,61,0] = 4×622 + 61×621 + 0×620 = 1915810
Now find your database-record with WHERE id = 19158 and do the redirect.
Example implementations (provided by commenters)
C++
Python
Ruby
Haskell
C#
CoffeeScript
Perl
Why would you want to use a hash?
You can just use a simple translation of your auto-increment value to an alphanumeric value. You can do that easily by using some base conversion. Say you character space (A-Z, a-z, 0-9, etc.) has 62 characters, convert the id to a base-40 number and use the characters as the digits.
public class UrlShortener {
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final int BASE = ALPHABET.length();
public static String encode(int num) {
StringBuilder sb = new StringBuilder();
while ( num > 0 ) {
sb.append( ALPHABET.charAt( num % BASE ) );
num /= BASE;
}
return sb.reverse().toString();
}
public static int decode(String str) {
int num = 0;
for ( int i = 0; i < str.length(); i++ )
num = num * BASE + ALPHABET.indexOf(str.charAt(i));
return num;
}
}
Not an answer to your question, but I wouldn't use case-sensitive shortened URLs. They are hard to remember, usually unreadable (many fonts render 1 and l, 0 and O and other characters very very similar that they are near impossible to tell the difference) and downright error prone. Try to use lower or upper case only.
Also, try to have a format where you mix the numbers and characters in a predefined form. There are studies that show that people tend to remember one form better than others (think phone numbers, where the numbers are grouped in a specific form). Try something like num-char-char-num-char-char. I know this will lower the combinations, especially if you don't have upper and lower case, but it would be more usable and therefore useful.
My approach: Take the Database ID, then Base36 Encode it. I would NOT use both Upper AND Lowercase letters, because that makes transmitting those URLs over the telephone a nightmare, but you could of course easily extend the function to be a base 62 en/decoder.
Here is my PHP 5 class.
<?php
class Bijective
{
public $dictionary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
public function __construct()
{
$this->dictionary = str_split($this->dictionary);
}
public function encode($i)
{
if ($i == 0)
return $this->dictionary[0];
$result = '';
$base = count($this->dictionary);
while ($i > 0)
{
$result[] = $this->dictionary[($i % $base)];
$i = floor($i / $base);
}
$result = array_reverse($result);
return join("", $result);
}
public function decode($input)
{
$i = 0;
$base = count($this->dictionary);
$input = str_split($input);
foreach($input as $char)
{
$pos = array_search($char, $this->dictionary);
$i = $i * $base + $pos;
}
return $i;
}
}
A Node.js and MongoDB solution
Since we know the format that MongoDB uses to create a new ObjectId with 12 bytes.
a 4-byte value representing the seconds since the Unix epoch,
a 3-byte machine identifier,
a 2-byte process id
a 3-byte counter (in your machine), starting with a random value.
Example (I choose a random sequence)
a1b2c3d4e5f6g7h8i9j1k2l3
a1b2c3d4 represents the seconds since the Unix epoch,
4e5f6g7 represents machine identifier,
h8i9 represents process id
j1k2l3 represents the counter, starting with a random value.
Since the counter will be unique if we are storing the data in the same machine we can get it with no doubts that it will be duplicate.
So the short URL will be the counter and here is a code snippet assuming that your server is running properly.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create a schema
const shortUrl = new Schema({
long_url: { type: String, required: true },
short_url: { type: String, required: true, unique: true },
});
const ShortUrl = mongoose.model('ShortUrl', shortUrl);
// The user can request to get a short URL by providing a long URL using a form
app.post('/shorten', function(req ,res){
// Create a new shortUrl */
// The submit form has an input with longURL as its name attribute.
const longUrl = req.body["longURL"];
const newUrl = ShortUrl({
long_url : longUrl,
short_url : "",
});
const shortUrl = newUrl._id.toString().slice(-6);
newUrl.short_url = shortUrl;
console.log(newUrl);
newUrl.save(function(err){
console.log("the new URL is added");
})
});
I keep incrementing an integer sequence per domain in the database and use Hashids to encode the integer into a URL path.
static hashids = Hashids(salt = "my app rocks", minSize = 6)
I ran a script to see how long it takes until it exhausts the character length. For six characters it can do 164,916,224 links and then goes up to seven characters. Bitly uses seven characters. Under five characters looks weird to me.
Hashids can decode the URL path back to a integer but a simpler solution is to use the entire short link sho.rt/ka8ds3 as a primary key.
Here is the full concept:
function addDomain(domain) {
table("domains").insert("domain", domain, "seq", 0)
}
function addURL(domain, longURL) {
seq = table("domains").where("domain = ?", domain).increment("seq")
shortURL = domain + "/" + hashids.encode(seq)
table("links").insert("short", shortURL, "long", longURL)
return shortURL
}
// GET /:hashcode
function handleRequest(req, res) {
shortURL = req.host + "/" + req.param("hashcode")
longURL = table("links").where("short = ?", shortURL).get("long")
res.redirect(301, longURL)
}
C# version:
public class UrlShortener
{
private static String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static int BASE = 62;
public static String encode(int num)
{
StringBuilder sb = new StringBuilder();
while ( num > 0 )
{
sb.Append( ALPHABET[( num % BASE )] );
num /= BASE;
}
StringBuilder builder = new StringBuilder();
for (int i = sb.Length - 1; i >= 0; i--)
{
builder.Append(sb[i]);
}
return builder.ToString();
}
public static int decode(String str)
{
int num = 0;
for ( int i = 0, len = str.Length; i < len; i++ )
{
num = num * BASE + ALPHABET.IndexOf( str[(i)] );
}
return num;
}
}
You could hash the entire URL, but if you just want to shorten the id, do as marcel suggested. I wrote this Python implementation:
https://gist.github.com/778542
Take a look at https://hashids.org/ it is open source and in many languages.
Their page outlines some of the pitfalls of other approaches.
If you don't want re-invent the wheel ... http://lilurl.sourceforge.net/
// simple approach
$original_id = 56789;
$shortened_id = base_convert($original_id, 10, 36);
$un_shortened_id = base_convert($shortened_id, 36, 10);
alphabet = map(chr, range(97,123)+range(65,91)) + map(str,range(0,10))
def lookup(k, a=alphabet):
if type(k) == int:
return a[k]
elif type(k) == str:
return a.index(k)
def encode(i, a=alphabet):
'''Takes an integer and returns it in the given base with mappings for upper/lower case letters and numbers 0-9.'''
try:
i = int(i)
except Exception:
raise TypeError("Input must be an integer.")
def incode(i=i, p=1, a=a):
# Here to protect p.
if i <= 61:
return lookup(i)
else:
pval = pow(62,p)
nval = i/pval
remainder = i % pval
if nval <= 61:
return lookup(nval) + incode(i % pval)
else:
return incode(i, p+1)
return incode()
def decode(s, a=alphabet):
'''Takes a base 62 string in our alphabet and returns it in base10.'''
try:
s = str(s)
except Exception:
raise TypeError("Input must be a string.")
return sum([lookup(i) * pow(62,p) for p,i in enumerate(list(reversed(s)))])a
Here's my version for whomever needs it.
Why not just translate your id to a string? You just need a function that maps a digit between, say, 0 and 61 to a single letter (upper/lower case) or digit. Then apply this to create, say, 4-letter codes, and you've got 14.7 million URLs covered.
Here is a decent URL encoding function for PHP...
// From http://snipplr.com/view/22246/base62-encode--decode/
private function base_encode($val, $base=62, $chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
$str = '';
do {
$i = fmod($val, $base);
$str = $chars[$i] . $str;
$val = ($val - $i) / $base;
} while($val > 0);
return $str;
}
Don't know if anyone will find this useful - it is more of a 'hack n slash' method, yet is simple and works nicely if you want only specific chars.
$dictionary = "abcdfghjklmnpqrstvwxyz23456789";
$dictionary = str_split($dictionary);
// Encode
$str_id = '';
$base = count($dictionary);
while($id > 0) {
$rem = $id % $base;
$id = ($id - $rem) / $base;
$str_id .= $dictionary[$rem];
}
// Decode
$id_ar = str_split($str_id);
$id = 0;
for($i = count($id_ar); $i > 0; $i--) {
$id += array_search($id_ar[$i-1], $dictionary) * pow($base, $i - 1);
}
Did you omit O, 0, and i on purpose?
I just created a PHP class based on Ryan's solution.
<?php
$shorty = new App_Shorty();
echo 'ID: ' . 1000;
echo '<br/> Short link: ' . $shorty->encode(1000);
echo '<br/> Decoded Short Link: ' . $shorty->decode($shorty->encode(1000));
/**
* A nice shorting class based on Ryan Charmley's suggestion see the link on Stack Overflow below.
* #author Svetoslav Marinov (Slavi) | http://WebWeb.ca
* #see http://stackoverflow.com/questions/742013/how-to-code-a-url-shortener/10386945#10386945
*/
class App_Shorty {
/**
* Explicitly omitted: i, o, 1, 0 because they are confusing. Also use only lowercase ... as
* dictating this over the phone might be tough.
* #var string
*/
private $dictionary = "abcdfghjklmnpqrstvwxyz23456789";
private $dictionary_array = array();
public function __construct() {
$this->dictionary_array = str_split($this->dictionary);
}
/**
* Gets ID and converts it into a string.
* #param int $id
*/
public function encode($id) {
$str_id = '';
$base = count($this->dictionary_array);
while ($id > 0) {
$rem = $id % $base;
$id = ($id - $rem) / $base;
$str_id .= $this->dictionary_array[$rem];
}
return $str_id;
}
/**
* Converts /abc into an integer ID
* #param string
* #return int $id
*/
public function decode($str_id) {
$id = 0;
$id_ar = str_split($str_id);
$base = count($this->dictionary_array);
for ($i = count($id_ar); $i > 0; $i--) {
$id += array_search($id_ar[$i - 1], $this->dictionary_array) * pow($base, $i - 1);
}
return $id;
}
}
?>
public class TinyUrl {
private final String characterMap = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private final int charBase = characterMap.length();
public String covertToCharacter(int num){
StringBuilder sb = new StringBuilder();
while (num > 0){
sb.append(characterMap.charAt(num % charBase));
num /= charBase;
}
return sb.reverse().toString();
}
public int covertToInteger(String str){
int num = 0;
for(int i = 0 ; i< str.length(); i++)
num += characterMap.indexOf(str.charAt(i)) * Math.pow(charBase , (str.length() - (i + 1)));
return num;
}
}
class TinyUrlTest{
public static void main(String[] args) {
TinyUrl tinyUrl = new TinyUrl();
int num = 122312215;
String url = tinyUrl.covertToCharacter(num);
System.out.println("Tiny url: " + url);
System.out.println("Id: " + tinyUrl.covertToInteger(url));
}
}
This is what I use:
# Generate a [0-9a-zA-Z] string
ALPHABET = map(str,range(0, 10)) + map(chr, range(97, 123) + range(65, 91))
def encode_id(id_number, alphabet=ALPHABET):
"""Convert an integer to a string."""
if id_number == 0:
return alphabet[0]
alphabet_len = len(alphabet) # Cache
result = ''
while id_number > 0:
id_number, mod = divmod(id_number, alphabet_len)
result = alphabet[mod] + result
return result
def decode_id(id_string, alphabet=ALPHABET):
"""Convert a string to an integer."""
alphabet_len = len(alphabet) # Cache
return sum([alphabet.index(char) * pow(alphabet_len, power) for power, char in enumerate(reversed(id_string))])
It's very fast and can take long integers.
For a similar project, to get a new key, I make a wrapper function around a random string generator that calls the generator until I get a string that hasn't already been used in my hashtable. This method will slow down once your name space starts to get full, but as you have said, even with only 6 characters, you have plenty of namespace to work with.
I have a variant of the problem, in that I store web pages from many different authors and need to prevent discovery of pages by guesswork. So my short URLs add a couple of extra digits to the Base-62 string for the page number. These extra digits are generated from information in the page record itself and they ensure that only 1 in 3844 URLs are valid (assuming 2-digit Base-62). You can see an outline description at http://mgscan.com/MBWL.
Very good answer, I have created a Golang implementation of the bjf:
package bjf
import (
"math"
"strings"
"strconv"
)
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func Encode(num string) string {
n, _ := strconv.ParseUint(num, 10, 64)
t := make([]byte, 0)
/* Special case */
if n == 0 {
return string(alphabet[0])
}
/* Map */
for n > 0 {
r := n % uint64(len(alphabet))
t = append(t, alphabet[r])
n = n / uint64(len(alphabet))
}
/* Reverse */
for i, j := 0, len(t) - 1; i < j; i, j = i + 1, j - 1 {
t[i], t[j] = t[j], t[i]
}
return string(t)
}
func Decode(token string) int {
r := int(0)
p := float64(len(token)) - 1
for i := 0; i < len(token); i++ {
r += strings.Index(alphabet, string(token[i])) * int(math.Pow(float64(len(alphabet)), p))
p--
}
return r
}
Hosted at github: https://github.com/xor-gate/go-bjf
Implementation in Scala:
class Encoder(alphabet: String) extends (Long => String) {
val Base = alphabet.size
override def apply(number: Long) = {
def encode(current: Long): List[Int] = {
if (current == 0) Nil
else (current % Base).toInt :: encode(current / Base)
}
encode(number).reverse
.map(current => alphabet.charAt(current)).mkString
}
}
class Decoder(alphabet: String) extends (String => Long) {
val Base = alphabet.size
override def apply(string: String) = {
def decode(current: Long, encodedPart: String): Long = {
if (encodedPart.size == 0) current
else decode(current * Base + alphabet.indexOf(encodedPart.head),encodedPart.tail)
}
decode(0,string)
}
}
Test example with Scala test:
import org.scalatest.{FlatSpec, Matchers}
class DecoderAndEncoderTest extends FlatSpec with Matchers {
val Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"A number with base 10" should "be correctly encoded into base 62 string" in {
val encoder = new Encoder(Alphabet)
encoder(127) should be ("cd")
encoder(543513414) should be ("KWGPy")
}
"A base 62 string" should "be correctly decoded into a number with base 10" in {
val decoder = new Decoder(Alphabet)
decoder("cd") should be (127)
decoder("KWGPy") should be (543513414)
}
}
Function based in Xeoncross Class
function shortly($input){
$dictionary = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9'];
if($input===0)
return $dictionary[0];
$base = count($dictionary);
if(is_numeric($input)){
$result = [];
while($input > 0){
$result[] = $dictionary[($input % $base)];
$input = floor($input / $base);
}
return join("", array_reverse($result));
}
$i = 0;
$input = str_split($input);
foreach($input as $char){
$pos = array_search($char, $dictionary);
$i = $i * $base + $pos;
}
return $i;
}
Here is a Node.js implementation that is likely to bit.ly. generate a highly random seven-character string.
It uses Node.js crypto to generate a highly random 25 charset rather than randomly selecting seven characters.
var crypto = require("crypto");
exports.shortURL = new function () {
this.getShortURL = function () {
var sURL = '',
_rand = crypto.randomBytes(25).toString('hex'),
_base = _rand.length;
for (var i = 0; i < 7; i++)
sURL += _rand.charAt(Math.floor(Math.random() * _rand.length));
return sURL;
};
}
My Python 3 version
base_list = list("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
base = len(base_list)
def encode(num: int):
result = []
if num == 0:
result.append(base_list[0])
while num > 0:
result.append(base_list[num % base])
num //= base
print("".join(reversed(result)))
def decode(code: str):
num = 0
code_list = list(code)
for index, code in enumerate(reversed(code_list)):
num += base_list.index(code) * base ** index
print(num)
if __name__ == '__main__':
encode(341413134141)
decode("60FoItT")
For a quality Node.js / JavaScript solution, see the id-shortener module, which is thoroughly tested and has been used in production for months.
It provides an efficient id / URL shortener backed by pluggable storage defaulting to Redis, and you can even customize your short id character set and whether or not shortening is idempotent. This is an important distinction that not all URL shorteners take into account.
In relation to other answers here, this module implements the Marcel Jackwerth's excellent accepted answer above.
The core of the solution is provided by the following Redis Lua snippet:
local sequence = redis.call('incr', KEYS[1])
local chars = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz'
local remaining = sequence
local slug = ''
while (remaining > 0) do
local d = (remaining % 60)
local character = string.sub(chars, d + 1, d + 1)
slug = character .. slug
remaining = (remaining - d) / 60
end
redis.call('hset', KEYS[2], slug, ARGV[1])
return slug
Why not just generate a random string and append it to the base URL? This is a very simplified version of doing this in C#.
static string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static string baseUrl = "https://google.com/";
private static string RandomString(int length)
{
char[] s = new char[length];
Random rnd = new Random();
for (int x = 0; x < length; x++)
{
s[x] = chars[rnd.Next(chars.Length)];
}
Thread.Sleep(10);
return new String(s);
}
Then just add the append the random string to the baseURL:
string tinyURL = baseUrl + RandomString(5);
Remember this is a very simplified version of doing this and it's possible the RandomString method could create duplicate strings. In production you would want to take in account for duplicate strings to ensure you will always have a unique URL. I have some code that takes account for duplicate strings by querying a database table I could share if anyone is interested.
This is my initial thoughts, and more thinking can be done, or some simulation can be made to see if it works well or any improvement is needed:
My answer is to remember the long URL in the database, and use the ID 0 to 9999999999999999 (or however large the number is needed).
But the ID 0 to 9999999999999999 can be an issue, because
it can be shorter if we use hexadecimal, or even base62 or base64. (base64 just like YouTube using A-Z a-z 0-9 _ and -)
if it increases from 0 to 9999999999999999 uniformly, then hackers can visit them in that order and know what URLs people are sending each other, so it can be a privacy issue
We can do this:
have one server allocate 0 to 999 to one server, Server A, so now Server A has 1000 of such IDs. So if there are 20 or 200 servers constantly wanting new IDs, it doesn't have to keep asking for each new ID, but rather asking once for 1000 IDs
for the ID 1, for example, reverse the bits. So 000...00000001 becomes 10000...000, so that when converted to base64, it will be non-uniformly increasing IDs each time.
use XOR to flip the bits for the final IDs. For example, XOR with 0xD5AA96...2373 (like a secret key), and the some bits will be flipped. (whenever the secret key has the 1 bit on, it will flip the bit of the ID). This will make the IDs even harder to guess and appear more random
Following this scheme, the single server that allocates the IDs can form the IDs, and so can the 20 or 200 servers requesting the allocation of IDs. The allocating server has to use a lock / semaphore to prevent two requesting servers from getting the same batch (or if it is accepting one connection at a time, this already solves the problem). So we don't want the line (queue) to be too long for waiting to get an allocation. So that's why allocating 1000 or 10000 at a time can solve the issue.

Resources