बिटवाइज़ ऑपरेशन के व्यावहारिक अनुप्रयोग [बंद]


79
  1. आपने किसके लिए बिटवाइज़ ऑपरेशन का उपयोग किया है?
  2. वे इतना आसान क्यों हैं?
  3. किसी कृपया एक बहुत ही सरल ट्यूटोरियल की सिफारिश कर सकते हैं?

जवाबों:


83

हालांकि सभी को लगता है कि झंडे usecase पर झुके हुए हैं, लेकिन यह बिटवाइज़ ऑपरेटरों का एकमात्र अनुप्रयोग नहीं है (हालाँकि संभवतः सबसे आम)। इसके अलावा C # एक उच्च स्तर की भाषा है जिसे अन्य तकनीकों का उपयोग शायद ही कभी किया जाएगा, लेकिन यह अभी भी उन्हें जानने के लायक है। यहाँ मैं क्या सोच सकता हूँ:


<<और >>ऑपरेटरों जल्दी से गुणा 2. बेशक की शक्ति से, .NET JIT अनुकूलक शायद यह आप (और साथ ही किसी अन्य भाषा की किसी भी सभ्य संकलक) के लिए क्या कर सकते हैं होगा, लेकिन अगर आप वास्तव में हर माइक्रोसेकंड पर fretting रहे हैं, आप बस यह सुनिश्चित करने के लिए लिख सकते हैं।

इन ऑपरेटरों के लिए एक और आम उपयोग दो 16-बिट पूर्णांक को एक 32-बिट पूर्णांक में सामान करना है। पसंद:

int Result = (shortIntA << 16 ) | shortIntB;

यह Win32 फ़ंक्शंस के साथ सीधे इंटरफेसिंग के लिए आम है, जो कभी-कभी विरासत के कारणों के लिए इस ट्रिक का उपयोग करते हैं।

और, निश्चित रूप से, ये ऑपरेटर तब उपयोगी होते हैं जब आप अनुभवहीन को भ्रमित करना चाहते हैं, जैसे कि होमवर्क के प्रश्न का उत्तर प्रदान करते समय। :)

किसी भी वास्तविक कोड में, हालांकि आप इसके बजाय गुणा का उपयोग करके बहुत बेहतर होंगे, क्योंकि यह एक बहुत बेहतर पठनीयता है और JIT इसे किसी भी तरह के प्रदर्शन के दंड के रूप में वैसे भी निर्देश shlऔर shrनिर्देश देता है।


^ऑपरेटर (XOR) के साथ कुछ जिज्ञासु चालें व्यवहार करें । निम्न गुणों के कारण यह वास्तव में एक बहुत शक्तिशाली ऑपरेटर है:

  • A^B == B^A
  • A^B^A == B
  • यदि आप जानते हैं A^Bतो यह बताना असंभव है कि क्या Aऔर क्या Bहैं, लेकिन यदि आप उनमें से एक को जानते हैं, तो आप दूसरे की गणना कर सकते हैं।
  • ऑपरेटर गुणन / विभाजन / जोड़ / घटाव जैसे किसी भी ओवरफ्लो से पीड़ित नहीं होता है।

कुछ चालें मैंने इस ऑपरेटर का उपयोग करके देखी हैं:

एक मध्यस्थ चर के बिना दो पूर्णांक चर को स्वैप करना:

A = A^B // A is now XOR of A and B
B = A^B // B is now the original A
A = A^B // A is now the original B

प्रति आइटम केवल एक अतिरिक्त चर के साथ संदेह से जुड़ी सूची। इसका C # में बहुत कम उपयोग होगा, लेकिन यह एंबेडेड सिस्टम की निम्न स्तर की प्रोग्रामिंग के लिए काम आ सकता है, जहाँ हर बाइट मायने रखती है।

विचार यह है कि आप पहले आइटम के लिए पॉइंटर का ट्रैक रखते हैं; अंतिम आइटम के लिए सूचक; और हर आइटम के लिए आप का ध्यान रखें pointer_to_previous ^ pointer_to_next। इस तरह आप सूची को किसी भी छोर से पार कर सकते हैं, फिर भी ओवरहेड एक पारंपरिक लिंक्ड सूची का आधा हिस्सा है। यहां ट्रैवर्सिंग के लिए C ++ कोड दिया गया है:

ItemStruct *CurrentItem = FirstItem, *PreviousItem=NULL;
while (  CurrentItem != NULL )
{
    // Work with CurrentItem->Data

    ItemStruct *NextItem = CurrentItem->XorPointers ^ PreviousItem;
    PreviousItem = CurrentItem;
    CurrentItem = NextItem;
}

अंत से आगे बढ़ने के लिए आपको बस पहली पंक्ति को बदलने की जरूरत FirstItemहै LastItem। वहीं एक और मेमोरी सेविंग है।

एक अन्य स्थान जहां मैं ^सी # में नियमित आधार पर ऑपरेटर का उपयोग करता हूं , जब मुझे अपने प्रकार के लिए एक हैशकोड की गणना करनी होती है जो एक संयुक्त प्रकार है। पसंद:

class Person
{
    string FirstName;
    string LastName;
    int Age;

    public int override GetHashCode()
    {
        return (FirstName == null ? 0 : FirstName.GetHashCode()) ^
            (LastName == null ? 0 : LastName.GetHashCode()) ^
            Age.GetHashCode();
    }
}

13
+1, सुंदर एक्सोर स्वैप। धन्यवाद।
ग्रोज अष्ट Gro

4
Xor-list दृष्टिकोण के लिए +1, इससे पहले नहीं देखा था।
स्नोमार्क

2
दिलचस्प है कि यह रचनात्मक नहीं लेबल किया गया था :)
एलेक्स गॉर्डन

"जब आप अनुभवहीन को भ्रमित करना चाहते हैं" +1 बहुत अधिक वरिष्ठ देवों / वास्तुकारों के एमओ को रास्ते में लाने के लिए। मैंने वास्तव में सोचा था कि अनुभव के साथ विनम्र व्यावहारिकता आएगी, लेकिन अफसोस नहीं। ब्रोगमर्स ने पदभार संभाल लिया है और दुनिया इसके लिए बदतर है।
दावोस

और यह +1000 "किसी भी वास्तविक कोड में, हालांकि आप इसके बजाय गुणा का उपयोग करके बहुत बेहतर होंगे, क्योंकि यह एक बहुत बेहतर पठनीयता है और जेआईटी इसे वैसे भी हिला देने और निर्देश देने का अनुकूलन करता है, ताकि कोई प्रदर्शन जुर्माना न हो।" कोड गोल्फ वास्तविक कोड में इतना प्रचलित क्यों है?
दावोस

74

मैं अपने अनुप्रयोगों में सुरक्षा के लिए बिटवाइज़ ऑपरेटरों का उपयोग करता हूं। मैं एक Enum के अंदर विभिन्न स्तरों को संग्रहीत करूँगा:

[Flags]
public enum SecurityLevel
{
    User = 1, // 0001
    SuperUser = 2, // 0010
    QuestionAdmin = 4, // 0100
    AnswerAdmin = 8 // 1000
}

और फिर एक उपयोगकर्ता को उनके स्तर असाइन करें:

// Set User Permissions to 1010
//
//   0010
// | 1000
//   ----
//   1010
User.Permissions = SecurityLevel.SuperUser | SecurityLevel.AnswerAdmin;

और फिर की जा रही कार्रवाई में अनुमतियों की जाँच करें:

// Check if the user has the required permission group
//
//   1010
// & 1000
//   ----
//   1000
if( (User.Permissions & SecurityLevel.AnswerAdmin) == SecurityLevel.AnswerAdmin )
{
    // Allowed
}

1
@ अन्याय, बहुत बहुत धन्यवाद। क्या आप इस उपयोगकर्ता को समझा सकते हैं SecurityLevel.AnswerAdmin;
एलेक्स गॉर्डन 15

ये सिर्फ झंडे वाली दुश्मनी हैं।
डेव

@ जेनी - कोड टिप्पणियों में स्पष्टीकरण जोड़ा गया।
जस्टिन निसेनर 15

1
@ दाब - बिल्कुल। लेकिन जब आप ध्वजांकित एनम का उपयोग करते हैं, तो आप मानों के खिलाफ जांच करने के लिए बिटवाइज़ ऑपरेटर्स का उपयोग कर रहे हैं। आसान और काफी सरल। मेरी राय में, बिल्कुल वही जो ओपी ने मांगा था।
जस्टिन नेस्नर

7
@ जस्टिन: हालांकि अब अप्रचलित है, Enum.HasFlag: msdn.microsoft.com/en-us/library/system.enum.hasflag.aspx
रीड

16

मुझे नहीं पता कि आप कितने सुडोकू हैं, इसे हल करना कितना व्यावहारिक है, लेकिन चलो मान लेते हैं।

कल्पना कीजिए कि आप एक सुडोकू सॉल्वर या सिर्फ एक साधारण कार्यक्रम लिखना चाहते हैं, जो आपको बोर्ड दिखाता है और आपको पहेली को स्वयं हल करने देता है, लेकिन यह सुनिश्चित करता है कि चालें कानूनी हैं।

बोर्ड को संभवतः दो-आयामी सरणी द्वारा दर्शाया जाएगा जैसे:

uint [, ] theBoard = new uint[9, 9];

मूल्य का 0मतलब है कि सेल अभी भी खाली है और सीमा [1u, 9u] से मान बोर्ड में वास्तविक मूल्य हैं।

अब कल्पना करें कि आप जांचना चाहते हैं कि कुछ चाल कानूनी है या नहीं। जाहिर है आप इसे कुछ छोरों के साथ कर सकते हैं, लेकिन बिटकॉम्स आपको चीजों को बहुत तेज करने की अनुमति देते हैं। एक साधारण कार्यक्रम में जो यह सुनिश्चित करता है कि नियमों का पालन किया जाता है, इससे कोई फर्क नहीं पड़ता, लेकिन एक विलायक में यह हो सकता है।

आप बिटमास्क के सरणियों को बनाए रख सकते हैं, उन संख्याओं के बारे में जानकारी संग्रहीत करें जो पहले से ही प्रत्येक पंक्ति में डाली गई हैं, प्रत्येक कॉलम ए और प्रत्येक 3x3 बॉक्स।

uint [] maskForNumbersSetInRow = new uint[9];

uint [] maskForNumbersSetInCol = new uint[9];

uint [, ] maskForNumbersSetInBox = new uint[3, 3];

संख्या से बिटपटन तक की मैपिंग, उस नंबर सेट के अनुरूप एक बिट के साथ, बहुत सरल है

1 -> 00000000 00000000 00000000 00000001
2 -> 00000000 00000000 00000000 00000010
3 -> 00000000 00000000 00000000 00000100
...
9 -> 00000000 00000000 00000001 00000000

सी # में, आप इस तरह से bitpattern गणना कर सकता है ( valueएक है uint):

uint bitpattern = 1u << (int)(value - 1u);

1uबिटपटेन के अनुरूप ऊपर की लाइन में 00000000 00000000 00000000 00000001बाईं ओर शिफ्ट किया जाता है value - 1। यदि, उदाहरण के लिए value == 5, आपको मिलता है

00000000 00000000 00000000 00010000

शुरुआत में, प्रत्येक पंक्ति, स्तंभ और बॉक्स के लिए मुखौटा है 0। हर बार जब आप बोर्ड पर कुछ नंबर डालते हैं, तो आप मास्क को अपडेट करते हैं, इसलिए नए मूल्य के अनुरूप बिट सेट हो जाता है।

मान लें कि आप पंक्ति 3 में मान 5 डालें (पंक्तियाँ और स्तंभ 0 से क्रमांकित हैं)। पंक्ति 3 के लिए मास्क को स्टोर किया जाता है maskForNumbersSetInRow[3]। चलो यह भी मानते हैं कि डालने से पहले {1, 2, 4, 7, 9}पंक्ति में पहले से ही संख्याएं थीं 3. मुखौटा में थोड़ा पैटर्न maskForNumbersSetInRow[3]इस तरह दिखता है:

00000000 00000000 00000001 01001011
bits above correspond to:9  7  4 21

लक्ष्य इस मास्क में मूल्य 5 के अनुरूप बिट सेट करना है। आप इसे बिटवाइस या ऑपरेटर ( |) का उपयोग करके कर सकते हैं । पहले आप मान 5 के अनुरूप थोड़ा पैटर्न बनाते हैं

uint bitpattern = 1u << 4; // 1u << (int)(value - 1u)

और फिर आप operator |मास्क में बिट सेट करने के लिए उपयोग करते हैंmaskForNumbersSetInRow[3]

maskForNumbersSetInRow[3] = maskForNumbersSetInRow[3] | bitpattern;

या छोटे रूप का उपयोग करना

maskForNumbersSetInRow[3] |= bitpattern;

00000000 00000000 00000001 01001011
                 |
00000000 00000000 00000000 00010000
                 =
00000000 00000000 00000001 01011011

अब आपका मुखौटा इंगित करता है कि {1, 2, 4, 5, 7, 9}इस पंक्ति (पंक्ति 3) में मान हैं ।

यदि आप जांचना चाहते हैं, अगर कुछ मूल्य पंक्ति में है, तो आप यह operator &जांचने के लिए उपयोग कर सकते हैं कि क्या संबंधित बिट मास्क में सेट है। यदि उस ऑपरेटर का परिणाम मुखौटा पर लागू होता है और उस मूल्य के अनुरूप एक सा पैटर्न, गैर-शून्य है, तो मूल्य पहले से ही पंक्ति में है। यदि परिणाम 0 है तो मान पंक्ति में नहीं है।

उदाहरण के लिए, यदि आप जाँचना चाहते हैं कि क्या मान 3 पंक्ति में है, तो आप इस तरह से कर सकते हैं:

uint bitpattern = 1u << 2; // 1u << (int)(value - 1u)
bool value3IsInRow = ((maskForNumbersSetInRow[3] & bitpattern) != 0);

00000000 00000000 00000001 01001011 // the mask
                 |
00000000 00000000 00000000 00000100 // bitpattern for the value 3
                 =
00000000 00000000 00000000 00000000 // the result is 0. value 3 is not in the row.

नीचे बोर्ड में एक नया मान सेट करने की विधियाँ हैं, उपयुक्त बिटकॉम्स को बनाए रखना और अगर कोई क़ानूनी है तो जाँच के लिए।

public void insertNewValue(int row, int col, uint value)
{

    if(!isMoveLegal(row, col, value))
        throw ...

    theBoard[row, col] = value;

    uint bitpattern = 1u << (int)(value - 1u);

    maskForNumbersSetInRow[row] |= bitpattern;

    maskForNumbersSetInCol[col] |= bitpattern;

    int boxRowNumber = row / 3;
    int boxColNumber = col / 3;

    maskForNumbersSetInBox[boxRowNumber, boxColNumber] |= bitpattern;

}

मुखौटे होने पर, आप जाँच सकते हैं कि क्या इस तरह कानूनी है:

public bool isMoveLegal(int row, int col, uint value)
{

    uint bitpattern = 1u << (int)(value - 1u);

    int boxRowNumber = row / 3;
    int boxColNumber = col / 3;

    uint combinedMask = maskForNumbersSetInRow[row] | maskForNumbersSetInCol[col]
                        | maskForNumbersSetInBox[boxRowNumber, boxColNumber];

    return ((theBoard[row, col] == 0) && ((combinedMask & bitpattern) == 0u);
}

3
वास्तव में दिलचस्प सामान लेकिन थोड़े मेरे सिर पर थोड़ा सा उड़ा। ऐसा लगता है कि यहां जो कुछ चल रहा है उससे थोड़ा सा निर्माण हो सकता है (कुछ उदाहरणों के साथ कुछ टिप्पणियां)। मुखौटे के निर्माण में थोड़ी सी शिफ्ट और इस तरह से मेरे लिए थोड़ा सा दुस्साहस है। बस आप स्पष्ट रूप से जवाब देने में कुछ समय लगा रहे हैं।
मार्क


3

यदि आपको कभी हार्डवेयर के साथ संवाद करने की आवश्यकता होती है, तो आपको किसी बिंदु पर बिट ट्विडलिंग का उपयोग करने की आवश्यकता होगी।

पिक्सेल मान के RGB मान निकालना।

इतनी सारी चीजें


2
  1. उन्हें एक सीमित आकार के चर के माध्यम से एक फ़ंक्शन में कई तर्कों को पारित करने के लिए उपयोग किया जा सकता है।
  2. फायदे कम मेमोरी ओवरहेड, या कम मेमोरी लागत हैं: इसलिए प्रदर्शन में वृद्धि हुई है।
  3. मैं मौके पर एक ट्यूटोरियल नहीं लिख सकता, लेकिन वे वहाँ हैं मुझे यकीन है।

whoops, मैंने अभी देखा था कि आपने इस C # को टैग किया था, मुझे लगा कि यह C ++ है। ... धीमा पढ़ना चाहिए ... :)
सी जॉनसन

2

उनका उपयोग विभिन्न अनुप्रयोगों के पूरे भार के लिए किया जा सकता है, यहां एक प्रश्न है जो मैंने पहले यहां पोस्ट किया है, जो बिटवाइज़ ऑपरेशन का उपयोग करता है:

बिटवाइंड एंड, बिटवाइज इनक्लूसिव या प्रश्न, जावा में

अन्य उदाहरणों के लिए, एक नज़र (कहे) पर ध्यान दें।

मेरे उदाहरण में, मैं एक द्विआधारी संख्या की सीमा को -128 ... 127 से 0..255 तक बदलने के लिए बिटवाइज़ ऑपरेशंस का उपयोग कर रहा था (यह हस्ताक्षरित से हस्ताक्षर किए बिना बदल रहा है)।

एमएसएन लेख यहां ->

http://msdn.microsoft.com/en-us/library/6a71f45d%28VS.71%29.aspx

उपयोगी है।

और, हालांकि यह लिंक:

http://weblogs.asp.net/alessandro/archive/2007/10/02/bitwise-operators-in-c-or-xor-and-amp-amp-not.aspx

बहुत तकनीकी है, यह सब कुछ कवर कर रहा है।

HTH


2

जब भी आपके पास वस्तुओं के संयोजन में 1 या अधिक का विकल्प होता है तो बिटवाइज़ आमतौर पर एक आसान फ़िक्स होता है।

कुछ उदाहरणों में सुरक्षा बिट्स (जस्टिन के नमूने पर प्रतीक्षा करना), शेड्यूलिंग दिन आदि शामिल हैं।


2

मुझे कहना होगा कि डेटा को संपीड़ित करने के लिए सबसे आम उपयोग बिटफ़िल्ड को संशोधित करना है। आप इसे ज्यादातर पैकेट वाले किफायती बनाने के कार्यक्रमों में देखते हैं।

का उदाहरण नेटवर्क संपीड़न bitfields का उपयोग कर


2

C # के लिए मैं सबसे ज्यादा इस्तेमाल की जाने वाली चीजों में से एक हैशकोड का उत्पादन कर रहा हूं। कुछ हद तक अच्छे हैशिंग तरीके हैं जो उनका उपयोग करते हैं। उदाहरण के लिए, एक X Y के साथ सह-समन्वित वर्ग के लिए जो मेरे द्वारा उपयोग किए जाने वाले दोनों प्रकार के थे:

public override int GetHashCode()
{
  return x ^ ((y << 16) | y >> 16);
}

यह जल्दी से एक संख्या उत्पन्न करता है जो समान वस्तु द्वारा उत्पादित होने पर समान होने की गारंटी देता है (यह मानते हुए कि समानता का अर्थ है कि दोनों वस्तुओं की तुलना में X और Y दोनों पैरामीटर समान हैं) जबकि निम्न-मूल्यवान वस्तुओं के लिए क्लैशिंग पैटर्न का उत्पादन नहीं किया जा सकता है (होने की संभावना है) अधिकांश अनुप्रयोगों में सबसे आम)।

एक अन्य ध्वज गणना है। उदाहरण के लिएRegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase

कुछ निम्न-स्तर के ऑपरेशन हैं जो आमतौर पर आवश्यक नहीं होते हैं जब आप .NET जैसे फ्रेमवर्क के खिलाफ कोडिंग करते हैं (जैसे C # में मुझे UTF-8 को UTF-16 में बदलने के लिए कोड लिखने की आवश्यकता नहीं होगी, यह मेरे लिए है रूपरेखा), लेकिन निश्चित रूप से किसी को उस कोड को लिखना था।

कुछ बिट-ट्विडलिंग तकनीक हैं, जैसे निकटतम बाइनरी नंबर को गोल करना (जैसे 1010 से 10000 तक गोल):

        unchecked
        {
            --x;
            x |= (x >> 1);
            x |= (x >> 2);
            x |= (x >> 4);
            x |= (x >> 8);
            x |= (x >> 16);
            return ++x;
        }

जब आपको उनकी आवश्यकता होती है तो वे उपयोगी होते हैं, लेकिन यह बहुत सामान्य नहीं होता है।

अंत में, आप उन्हें गणित को माइक्रो-ऑप्टिमाइज़ करने के लिए भी उपयोग कर सकते हैं जैसे कि << 1इसके बजाय, * 2लेकिन मैं इसमें केवल यह कहना चाहता हूं कि यह आमतौर पर एक बुरा विचार है क्योंकि यह वास्तविक कोड के इरादे को छुपाता है, प्रदर्शन में लगभग कुछ भी नहीं बचाता है, और कुछ सूक्ष्म कीड़े छिपा सकता है ।


1
तकनीकी रूप से, बिट-शिफ्ट ऑपरेटरों को बिट-बुद्धिमान ऑपरेटर नहीं माना जा सकता है। विकिपीडिया लेख के बिट-शिफ्ट सेक्शन को क्यों देखें: en.wikipedia.org/wiki/Bitwise_operation
जस्टिन नेस्नर

1

बाइनरी सॉर्ट। ऐसे मुद्दे थे जहां कार्यान्वयन एक बिटशिफ्ट ऑपरेटर के बजाय एक डिवीजन ऑपरेटर का उपयोग कर रहा था। 10,000 से ऊपर के संग्रह में आने के बाद बीएस को असफल होना पड़ा


1

आप विभिन्न कारणों से उनका उपयोग करेंगे:

  • भंडारण (और जाँच!) विकल्प झंडे को स्मृति कुशल तरीके से
  • यदि कम्प्यूटेशनल प्रोग्रामिंग कर रहे हैं, तो आप गणितीय संचालकों (साइड-इफेक्ट्स से सावधान) के बजाय बिटवाइज़ ऑपरेशंस का उपयोग करके अपने कुछ संचालन को अनुकूलित करने पर विचार कर सकते हैं।
  • ग्रे कोड !
  • संचित मूल्यों का निर्माण

मुझे यकीन है कि आप दूसरों के बारे में सोच सकते हैं।

कहा जा रहा है, कभी-कभी आपको खुद से पूछने की जरूरत है: क्या स्मृति और प्रदर्शन को बढ़ावा देने लायक प्रयास है। उस तरह के कोड को लिखने के बाद, उसे थोड़ी देर के लिए आराम करने दें और वापस आ जाएं। यदि आप इसके साथ संघर्ष करते हैं, तो अधिक रखरखाव योग्य कोड के साथ फिर से लिखें।

दूसरी ओर, कभी-कभी यह बिटवाइज़ ऑपरेशंस (थिंक क्रिप्टोग्राफी) का उपयोग करने के लिए सही समझ में आता है।

बेहतर अभी तक, यह किसी और ने पढ़ा है, और बड़े पैमाने पर दस्तावेज़।


1

खेल!

वापस दिनों में, मैंने इसका इस्तेमाल एक रिवर्सी खिलाड़ी के टुकड़ों का प्रतिनिधित्व करने के लिए किया। यह 8X8 है, इसलिए इसने मुझे एक longप्रकार का लिया , और उदाहरण के लिए, यदि आप जानना चाहते हैं कि बोर्ड पर सभी टुकड़े कहाँ हैं - तो आप orदोनों खिलाड़ी टुकड़े करते हैं।
यदि आप किसी खिलाड़ी के सभी संभव कदम चाहते हैं, तो दाईं ओर कहें - आप >>खिलाड़ी के टुकड़ों को एक-एक करके दर्शाते हैं, और ANDयह जांचने के लिए प्रतिद्वंद्वी के टुकड़ों के साथ कि क्या अब आम 1 हैं (इसका मतलब है कि आपके दाईं ओर प्रतिद्वंद्वी टुकड़ा है)। फिर तुम वही करते रहना। यदि आप अपने टुकड़ों में वापस आते हैं - कोई चाल नहीं। यदि आपको एक स्पष्ट बिट मिलता है - आप वहां जा सकते हैं, और रास्ते में सभी टुकड़ों पर कब्जा कर सकते हैं।
(यह तकनीक मोटे तौर पर शतरंज सहित कई प्रकार के बोर्ड गेम में उपयोग की जाती है)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.