├── images ├── Sample Tree.png └── asterisk_green.svg ├── README.md ├── Hackathon_Que_Notes.md ├── Hackathon_Part_A.md └── tree_of_space.cpp /images/Sample Tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-singh-kanyal/JusPay/HEAD/images/Sample Tree.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JusPay 2 | 3 | - [Coding Problems]() - pending 4 | - [Hackthon Problem Statement](./Hackathon_Part_A.md) 5 | - [Hackthon Question Review Notes](./Hackathon_Que_Notes.md) 6 | - -------------------------------------------------------------------------------- /images/asterisk_green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Hackathon_Que_Notes.md: -------------------------------------------------------------------------------- 1 | # Question Review 2 | 3 | >The problem “Locking the Tree of Space” involves designing and implementing three operations on an M-Ary tree: 4 | 5 | - **`lock(x, uid)`:** Grants exclusive access to the subtree rooted at `x` for user `uid`. Succeeds only if no ancestors or descendants of `x` are locked by any user. 6 | - **`unlock(x, uid)`:** Reverts the lock on `x` by user `uid`. Succeeds only if `x` was previously locked by `uid`. 7 | - **`upgradeLock(x, uid)`:** Upgrades a lock from a descendant of `x` to `x` itself for user `uid`. Succeeds only if: 8 | - `x` is not currently locked. 9 | - All locked descendants of `x` are locked by `uid`. 10 | - `x` has at least one locked descendant (if none, return `false`). 11 | 12 | >**Assumptions:** 13 | 14 | 1. **Unique User IDs:** Each user has a unique identifier (`uid`) for distinction. And all user IDs in queries are valid. 15 | 2. **Valid Node Names:** All node names in queries are valid and exist in the tree. 16 | 3. **Synchronization:** The system handles concurrent operations to prevent race conditions. 17 | 4. **Error Handling:** The system handles operation failures; the system does not crash and instead returns a meaningful error message. 18 | 5. **Memory:** No memory limitations exist for storing the tree. 19 | 6. **Upgrade Restrictions:** Descendants are unlocked when their ancestor is upgraded. Upgrade fails if `x` has any locked ancestors. 20 | 21 | >**Points to Remember:** 22 | 23 | - Tree is fully balanced (each node has 0 or `m` children). 24 | - Queries are formatted as `Operation_Type NodeName Userld`  25 | (1 for lock, 2 for unlock, 3 for upgrade). 26 | - Expected time complexities: 27 | - lock/unlock: `O(logm(N))` 28 | - upgradeLock: `O(numberOfLockedNodes * logm(N))` 29 | - Output: `true` for successful operations, `false` otherwise. 30 | 31 | >**Use Cases:** 32 | 33 | The problem “Locking The Tree of Space” can be applied in several real-world scenarios for JusPay, especially considering its role as a fintech company that deals with online payments. Here are some potential applications: 34 | 35 | 1. **Transaction Processing:** Ensure smooth concurrent processing of multiple transactions by locking relevant nodes (representing transaction stages) in the tree. This prevents data inconsistencies and accidental modifications. 36 | 2. **User Session Management:** Manage active user sessions and prevent resource conflicts by locking corresponding nodes in the tree. This ensures exclusive access to services and resources until the session ends. 37 | 3. **Resource Allocation:** Optimize resource allocation in cloud environments by locking nodes representing resources (CPU, memory, storage) when assigned to tasks. This prevents overbooking and ensures task completion without resource conflicts. 38 | 4. **Database Operations:** Maintain data integrity during concurrent database operations (updates, deletes) by locking relevant table nodes (Users, Orders, Items) in the tree. This serializes operations and prevents data corruption. 39 | 5. **Payment Gateway Integration:** Facilitate smooth payment processing through JusPay's integrated gateways by locking the chosen gateway node in the tree. This prevents concurrent access and ensures successful transaction completion on the chosen platform. 40 | 41 | >**a. Selection and Locking:** 42 | >- When Customer A reaches the payment gateway selection step, the corresponding node in the tree (representing all available gateways) is locked. 43 | >- This prevents other customers from choosing any gateway until A makes their selection. 44 | >- Once A selects Visa, the specific Visa node within the tree is locked, granting A exclusive access to its processing resources. 45 | 46 | >**b. Concurrent Access Prevention:** 47 | >- While A's transaction progresses through Visa's processing steps (e.g., authorization, settlement), the Visa node remains locked. 48 | >- This prevents other customers from choosing Visa during this period, ensuring A's transaction receives undivided attention from the gateway. 49 | 50 | >**c. Controlled Resource Allocation:** 51 | >- Locking the Visa node also allocates necessary resources like processing power and network bandwidth specifically for A's transaction. 52 | >- This prevents resource overload from concurrent transactions on the same gateway, ensuring smooth and efficient processing for A. 53 | 54 | >**d. Unlock and Release:** 55 | >- Upon successful completion of A's transaction, both the general gateway node and the Visa node are unlocked. 56 | >- This releases the allocated resources and makes them available for other customers seeking to use Visa or any other gateway. 57 | 58 | >**Additional Benefits:** 59 | >- This locking mechanism minimizes the risk of conflicts or errors that might arise from concurrent transactions on the same gateway. 60 | >- It also improves overall system performance by preventing resource overload and ensuring efficient utilization of gateway resources. -------------------------------------------------------------------------------- /Hackathon_Part_A.md: -------------------------------------------------------------------------------- 1 | # **Locking The Tree of Space** 2 | 3 | You have a world map represented as an M-Ary tree (sample tree below) 4 | 5 | 12 | 13 | ![Sample Tree](./images/Sample%20Tree.png) 14 | 15 | You need to define three operations on it: 16 | 17 | ```cpp 18 | 1. lock(X, uid) 19 | 2. unlock(X, uid) 20 | 3. upgradeLock(X, uid) 21 | ``` 22 | 23 | where `X` the name of a node in the tree (that would be unique) and `uid` is the user who is performing the operation. 24 | **Input format**: x is country name (string) and uid is user number (number). 25 | 26 | **Here are the definitions for the operations:** 27 | 28 | >**`lock(x, uid)`** 29 | Lock takes an exclusive access on the subtree rooted at `X`. It is formally defined like this: Once `lock(x, uid)` succeeds, then: 30 |
  • `lock(A, anyUserid)` should fail (returns false), where `A` is a descendent of `X`,
  • 31 |
  • `lock(B, anyUserld)` should fail (returns false), where `X` is a descendent of `B`,
  • 32 |
  • Lock operation cannot be performed on a node which is already locked i.e. `lock(x, anyUserld)` should fail (returns false).
  • 33 | 36 | 37 | >**`unlock(X, uid)`** 38 | Unlock reverts what was done by the lock operation. It can only be called on same node on which user `uid` had called a Lock on before. Returns true if it is successful. 39 | 40 | >**`upgradeLock(x, uid)`** 41 | It helps the user `uid` upgrade their lock to an ancestor node. It is only possible if the node `X` already has locked descendants and all of them are only locked by the same user `uid`. Upgrade should fail if there is any node which is descendant of X that is locked by a different user. Successful Upgrade will 'lock' the node. `upgradeLock` call shouldn't violate the consistency model that Lock/Unlock function requires. 42 | 43 | **Notes** 44 | 45 | 1. The number of nodes in the tree `N` is very large. So, optimize the time complexity for the above algorithms. 46 | 2. The below section contains the input format. 47 | - The first line contains the **number** of Nodes in the tree (`N`). 48 | - The second line contains **number** of children per node (value `m` in m-ary Tree). 49 | - The third line contains **number** of queries (`Q`). 50 | - Next **`N`** lines contains the **`NodeName`** (string) in the m-Ary Tree. 51 | - Next **`Q`** lines contains queries which are in format:  52 | **`Operation Type` `NodeName` `Userld`** 53 | - **Operation Type** 54 | 55 | `1` for lock 56 | `2` for unlock 57 | `3` for upgradeLock 58 | 59 | - **`NodeName`** - Name of any node (unique) in m-Ary Tree. 60 | - **`Userld`** - Integer value representing a unique user. 61 | 62 | **Example input** 63 | 64 | ```cpp 65 | 7 66 | 2 67 | 3 68 | World 69 | Asia 70 | Africa 71 | China 72 | India 73 | South Africa 74 | Egypt 75 | 1 China 9 76 | 2 India 9 77 | 3 Asia 9 78 | ``` 79 | 80 | With the above input you represent a 2-ary tree with 7 nodes as follows: 81 | 82 | ``` 83 | World 84 | / \ 85 | Asia Africa 86 | / \ / \ 87 | China India South Africa Egypt 88 | ``` 89 | 90 | **Additional Notes** 91 | 92 | 1. Here `1 China 3` indicates the following '`Operation Type` `NodeName` `Userld`'. 93 | 2. The tree is always fully balanced. 94 | 3. Constraints on the inputs are as follows: 95 | 1 < **N** < 5 * 10^5 96 | 1 < **m** < 30 97 | 1 < **Q** <5 * 10^5 98 | 1 < length of **NodeName** < 20 99 | 4. Optimize the time complexity: 100 | - Lock - O(logmN) 101 | - Unlock - O(logmN) 102 | - UpgradeLock - O(numberOfLockedNodes * logmN) 103 | 5. Lock operation on already locked node should fail. 104 | 6. Once Upgrade lock(X, uid) succeeds on X. It is equivalent to X being locked by uid. So, Lock(A/B, anyuser) should fail as per the definition of Lock and Unlock(X, uid) should also work. 105 | 7. Upgrade lock operation on a node having no locked descendants should fail and upgrade lock on already locked node should also fail. 106 | 107 | **Example input** 108 | 109 | ```cpp 110 | 7 111 | 2 112 | 5 113 | World 114 | Asia 115 | Africa 116 | China 117 | India 118 | SouthAfrica 119 | Egypt 120 | 1 China 9 121 | 1 India 9 122 | 3 Asia 9 123 | 2 India 9 124 | 2 Asia 9 125 | ``` 126 | 127 | **Example input** 128 | 129 | ```cpp 130 | true 131 | true 132 | true 133 | false 134 | true 135 | ``` 136 | 137 | Query 1: `1 China 9` ⇒ This operation is success as initially China is unlocked. 138 | 139 | Query 2: `1 India 9` ⇒ This should be success as none of ancestors and descendants of India are locked. 140 | 141 | Query 3: `3 Asia 9` ⇒ This also should be success as upgrade operation is done by same user who has locked descendants. 142 | 143 | Query 4: `2 India 9` ⇒ This should fail as the India is now not locked 144 | 145 | Query 5: `2 Asia 9` ⇒ This should be success as Asia was earlier (refer Query 3) locked by user 9. 146 | -------------------------------------------------------------------------------- /tree_of_space.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | struct Node 8 | { 9 | string label; 10 | vector children; 11 | Node *parent; 12 | int ancestorLocked, descendantLocked, userID; 13 | bool isLocked; 14 | 15 | Node(string name, Node *parentNode) 16 | { 17 | label = name; 18 | parent = parentNode; 19 | ancestorLocked = descendantLocked = userID = 0; 20 | isLocked = false; 21 | } 22 | 23 | void addChildren(vector childLabels, Node *parentNode) 24 | { 25 | for (auto &childLabel : childLabels) 26 | { 27 | children.push_back(new Node(childLabel, parentNode)); 28 | } 29 | } 30 | }; 31 | 32 | // Print the Tree 33 | // void printTree(Node *root) 34 | // { 35 | // cout << "Parent: " << root->label << "\n"; 36 | // cout << "Children: \n"; 37 | // for (auto child : root->children) 38 | // { 39 | // cout << child->label << " ancestorLocked: " << child->ancestorLocked 40 | // << " descendantLocked: " << child->descendantLocked << " isLocked: " 41 | // << child->isLocked << " userID: " << child->userID << "\n"; 42 | // } 43 | // cout << "\n"; 44 | // for (auto child : root->children) 45 | // { 46 | // printTree(child); 47 | // } 48 | // } 49 | 50 | Node *buildTree(Node *root, int &numChildren, vector &nodeLabels); 51 | 52 | class LockingTree 53 | { 54 | private: 55 | Node *root; 56 | unordered_map labelToNode; 57 | vector outputLog; 58 | 59 | public: 60 | LockingTree(Node *treeRoot) { root = treeRoot; } 61 | Node *getRoot() { return root; } 62 | 63 | void fillLabelToNode(Node *currentNode) 64 | { 65 | if (!currentNode) 66 | return; 67 | labelToNode[currentNode->label] = currentNode; 68 | for (auto child : currentNode->children) 69 | fillLabelToNode(child); 70 | } 71 | 72 | void updateDescendant(Node *currentNode, int value) 73 | { 74 | for (auto child : currentNode->children) 75 | { 76 | child->ancestorLocked += value; 77 | updateDescendant(child, value); 78 | } 79 | } 80 | 81 | bool checkDescendantsLocked(Node *currentNode, int &id, 82 | vector &lockedNodes) 83 | { 84 | if (currentNode->isLocked) 85 | { 86 | if (currentNode->userID != id) 87 | return false; 88 | lockedNodes.push_back(currentNode); 89 | } 90 | 91 | if (currentNode->descendantLocked == 0) 92 | return true; 93 | 94 | bool result = true; 95 | 96 | for (auto child : currentNode->children) 97 | { 98 | result &= checkDescendantsLocked(child, id, lockedNodes); 99 | if (!result) 100 | return false; 101 | } 102 | 103 | return result; 104 | } 105 | 106 | bool lockNode(string label, int id) 107 | { 108 | Node *targetNode = labelToNode[label]; 109 | 110 | if (targetNode->isLocked) 111 | return false; 112 | 113 | if (targetNode->ancestorLocked != 0 || targetNode->descendantLocked != 0) 114 | return false; 115 | 116 | Node *currentNode = targetNode->parent; 117 | 118 | while (currentNode) 119 | { 120 | currentNode->descendantLocked++; 121 | currentNode = currentNode->parent; 122 | } 123 | 124 | updateDescendant(targetNode, 1); 125 | targetNode->isLocked = true; 126 | targetNode->userID = id; 127 | 128 | return true; 129 | } 130 | 131 | bool unlockNode(string label, int id) 132 | { 133 | Node *targetNode = labelToNode[label]; 134 | 135 | if (!targetNode->isLocked) 136 | return false; 137 | 138 | if (targetNode->isLocked && targetNode->userID != id) 139 | return false; 140 | 141 | Node *currentNode = targetNode->parent; 142 | 143 | while (currentNode) 144 | { 145 | currentNode->descendantLocked--; 146 | currentNode = currentNode->parent; 147 | } 148 | 149 | updateDescendant(targetNode, -1); 150 | targetNode->isLocked = false; 151 | 152 | return true; 153 | } 154 | 155 | bool upgradeNode(string label, int id) 156 | { 157 | Node *targetNode = labelToNode[label]; 158 | 159 | if (targetNode->isLocked) 160 | return false; 161 | 162 | if (targetNode->ancestorLocked != 0 || targetNode->descendantLocked == 0) 163 | return false; 164 | 165 | vector lockedDescendants; 166 | 167 | if (checkDescendantsLocked(targetNode, id, lockedDescendants)) 168 | { 169 | for (auto lockedDescendant : lockedDescendants) 170 | { 171 | unlockNode(lockedDescendant->label, id); 172 | } 173 | } 174 | else 175 | return false; 176 | 177 | lockNode(label, id); 178 | 179 | return true; 180 | } 181 | 182 | void processQueries(vector>> queries) 183 | { 184 | for (auto query : queries) 185 | { 186 | int opcode = query.first; 187 | string nodeLabel = query.second.first; 188 | int userId = query.second.second; 189 | 190 | switch (opcode) 191 | { 192 | case 1: 193 | lockNode(nodeLabel, userId) ? outputLog.push_back("true") 194 | : outputLog.push_back("false"); 195 | break; 196 | case 2: 197 | unlockNode(nodeLabel, userId) ? outputLog.push_back("true") 198 | : outputLog.push_back("false"); 199 | break; 200 | case 3: 201 | upgradeNode(nodeLabel, userId) ? outputLog.push_back("true") 202 | : outputLog.push_back("false"); 203 | break; 204 | } 205 | } 206 | } 207 | 208 | void printOutputLog() 209 | { 210 | for (const string &result : outputLog) 211 | { 212 | cout << result << "\n"; 213 | } 214 | } 215 | }; 216 | 217 | int main() 218 | { 219 | int numNodes, numChildren, numQueries; 220 | cin >> numNodes >> numChildren >> numQueries; 221 | 222 | vector nodeLabels(numNodes); 223 | 224 | for (int i = 0; i < numNodes; i++) 225 | cin >> nodeLabels[i]; 226 | 227 | Node *rootNode = new Node(nodeLabels[0], nullptr); 228 | rootNode = buildTree(rootNode, numChildren, nodeLabels); 229 | 230 | LockingTree lockingTree(rootNode); 231 | lockingTree.fillLabelToNode(lockingTree.getRoot()); 232 | 233 | vector>> queries(numQueries); 234 | 235 | for (int i = 0; i < numQueries; i++) 236 | { 237 | cin >> queries[i].first >> queries[i].second.first >> 238 | queries[i].second.second; 239 | } 240 | 241 | lockingTree.processQueries(queries); 242 | lockingTree.printOutputLog(); 243 | return 0; 244 | } 245 | 246 | // build the tree 247 | Node *buildTree(Node *root, int &numChildren, vector &nodeLabels) 248 | { 249 | queue q; 250 | q.push(root); 251 | 252 | int startIndex = 1; 253 | 254 | while (!q.empty()) 255 | { 256 | Node *currentNode = q.front(); 257 | q.pop(); 258 | 259 | if (startIndex >= nodeLabels.size()) 260 | continue; 261 | 262 | vector tempChildrenLabels; 263 | 264 | for (int i = startIndex; i < startIndex + numChildren; i++) 265 | tempChildrenLabels.push_back(nodeLabels[i]); 266 | 267 | currentNode->addChildren(tempChildrenLabels, currentNode); 268 | startIndex += numChildren; 269 | 270 | for (auto child : currentNode->children) 271 | q.push(child); 272 | } 273 | 274 | return root; 275 | } 276 | 277 | // INPUT 278 | // n = total number of nodes 279 | // m = number of child per node 280 | // q = number of queries 281 | // next 'n' lines = node name in string 282 | // next 'q' lines = queries with (opcode, string, uid) 283 | // opcode => 1 = Lock, 2 = Unlock, 3 = Upgrade 284 | 285 | // 7 286 | // 2 287 | // 5 288 | // World 289 | // Asia 290 | // Africa 291 | // China 292 | // India 293 | // SouthAfrica 294 | // Egypt 295 | // 1 China 9 296 | // 1 India 9 297 | // 3 Asia 9 298 | // 2 India 9 299 | // 2 Asia 9 300 | 301 | // Output 302 | // true 303 | // true 304 | // true 305 | // false 306 | // true 307 | --------------------------------------------------------------------------------