Aditya commited on
Commit
4a3f117
·
0 Parent(s):

Add LLM vs RAG vs Fine-Tuning comparison project

Browse files
.gitattributes ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ checkpoint-25/tokenizer.json filter=lfs diff=lfs merge=lfs -text
2
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
3
+ *.pt filter=lfs diff=lfs merge=lfs -text
4
+ *.pth filter=lfs diff=lfs merge=lfs -text
5
+ *.bin filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment variables
2
+ .env
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.py[cod]
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ *.egg
11
+
12
+ # Model artifacts
13
+ finetuned_model/
14
+
15
+ # Results
16
+ results/
17
+
18
+ # IDE
19
+ .vscode/
20
+ .idea/
21
+
22
+ # OS
23
+ .DS_Store
24
+ Thumbs.db
README.md ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: TinyLlama/TinyLlama-1.1B-Chat-v1.0
3
+ library_name: peft
4
+ pipeline_tag: text-generation
5
+ tags:
6
+ - base_model:adapter:TinyLlama/TinyLlama-1.1B-Chat-v1.0
7
+ - lora
8
+ - transformers
9
+ ---
10
+
11
+ # Model Card for Model ID
12
+
13
+ <!-- Provide a quick summary of what the model is/does. -->
14
+
15
+
16
+
17
+ ## Model Details
18
+
19
+ ### Model Description
20
+
21
+ <!-- Provide a longer summary of what this model is. -->
22
+
23
+
24
+
25
+ - **Developed by:** [More Information Needed]
26
+ - **Funded by [optional]:** [More Information Needed]
27
+ - **Shared by [optional]:** [More Information Needed]
28
+ - **Model type:** [More Information Needed]
29
+ - **Language(s) (NLP):** [More Information Needed]
30
+ - **License:** [More Information Needed]
31
+ - **Finetuned from model [optional]:** [More Information Needed]
32
+
33
+ ### Model Sources [optional]
34
+
35
+ <!-- Provide the basic links for the model. -->
36
+
37
+ - **Repository:** [More Information Needed]
38
+ - **Paper [optional]:** [More Information Needed]
39
+ - **Demo [optional]:** [More Information Needed]
40
+
41
+ ## Uses
42
+
43
+ <!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
44
+
45
+ ### Direct Use
46
+
47
+ <!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
48
+
49
+ [More Information Needed]
50
+
51
+ ### Downstream Use [optional]
52
+
53
+ <!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
54
+
55
+ [More Information Needed]
56
+
57
+ ### Out-of-Scope Use
58
+
59
+ <!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->
60
+
61
+ [More Information Needed]
62
+
63
+ ## Bias, Risks, and Limitations
64
+
65
+ <!-- This section is meant to convey both technical and sociotechnical limitations. -->
66
+
67
+ [More Information Needed]
68
+
69
+ ### Recommendations
70
+
71
+ <!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
72
+
73
+ Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.
74
+
75
+ ## How to Get Started with the Model
76
+
77
+ Use the code below to get started with the model.
78
+
79
+ [More Information Needed]
80
+
81
+ ## Training Details
82
+
83
+ ### Training Data
84
+
85
+ <!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
86
+
87
+ [More Information Needed]
88
+
89
+ ### Training Procedure
90
+
91
+ <!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
92
+
93
+ #### Preprocessing [optional]
94
+
95
+ [More Information Needed]
96
+
97
+
98
+ #### Training Hyperparameters
99
+
100
+ - **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
101
+
102
+ #### Speeds, Sizes, Times [optional]
103
+
104
+ <!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
105
+
106
+ [More Information Needed]
107
+
108
+ ## Evaluation
109
+
110
+ <!-- This section describes the evaluation protocols and provides the results. -->
111
+
112
+ ### Testing Data, Factors & Metrics
113
+
114
+ #### Testing Data
115
+
116
+ <!-- This should link to a Dataset Card if possible. -->
117
+
118
+ [More Information Needed]
119
+
120
+ #### Factors
121
+
122
+ <!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
123
+
124
+ [More Information Needed]
125
+
126
+ #### Metrics
127
+
128
+ <!-- These are the evaluation metrics being used, ideally with a description of why. -->
129
+
130
+ [More Information Needed]
131
+
132
+ ### Results
133
+
134
+ [More Information Needed]
135
+
136
+ #### Summary
137
+
138
+
139
+
140
+ ## Model Examination [optional]
141
+
142
+ <!-- Relevant interpretability work for the model goes here -->
143
+
144
+ [More Information Needed]
145
+
146
+ ## Environmental Impact
147
+
148
+ <!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
149
+
150
+ Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
151
+
152
+ - **Hardware Type:** [More Information Needed]
153
+ - **Hours used:** [More Information Needed]
154
+ - **Cloud Provider:** [More Information Needed]
155
+ - **Compute Region:** [More Information Needed]
156
+ - **Carbon Emitted:** [More Information Needed]
157
+
158
+ ## Technical Specifications [optional]
159
+
160
+ ### Model Architecture and Objective
161
+
162
+ [More Information Needed]
163
+
164
+ ### Compute Infrastructure
165
+
166
+ [More Information Needed]
167
+
168
+ #### Hardware
169
+
170
+ [More Information Needed]
171
+
172
+ #### Software
173
+
174
+ [More Information Needed]
175
+
176
+ ## Citation [optional]
177
+
178
+ <!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
179
+
180
+ **BibTeX:**
181
+
182
+ [More Information Needed]
183
+
184
+ **APA:**
185
+
186
+ [More Information Needed]
187
+
188
+ ## Glossary [optional]
189
+
190
+ <!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
191
+
192
+ [More Information Needed]
193
+
194
+ ## More Information [optional]
195
+
196
+ [More Information Needed]
197
+
198
+ ## Model Card Authors [optional]
199
+
200
+ [More Information Needed]
201
+
202
+ ## Model Card Contact
203
+
204
+ [More Information Needed]
205
+ ### Framework versions
206
+
207
+ - PEFT 0.18.1
data/docs/backtracking.txt ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BACKTRACKING
2
+ ============
3
+ Backtracking explores all possible solutions by building candidates incrementally.
4
+ If a candidate fails, it backtracks and tries the next option.
5
+
6
+ Template:
7
+ def backtrack(state, choices):
8
+ if is_solution(state):
9
+ record(state)
10
+ return
11
+ for choice in choices:
12
+ if is_valid(state, choice):
13
+ make_choice(state, choice)
14
+ backtrack(state, remaining_choices)
15
+ undo_choice(state, choice) # BACKTRACK
16
+
17
+ --- Generate All Subsets ---
18
+ def subsets(nums):
19
+ result = []
20
+ def backtrack(start, current):
21
+ result.append(current[:]) # record every state
22
+ for i in range(start, len(nums)):
23
+ current.append(nums[i])
24
+ backtrack(i + 1, current)
25
+ current.pop() # backtrack
26
+ backtrack(0, [])
27
+ return result
28
+
29
+ print(subsets([1, 2, 3]))
30
+ # [[], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]]
31
+
32
+ --- Generate All Permutations ---
33
+ def permute(nums):
34
+ result = []
35
+ def backtrack(current):
36
+ if len(current) == len(nums):
37
+ result.append(current[:])
38
+ return
39
+ for num in nums:
40
+ if num not in current:
41
+ current.append(num)
42
+ backtrack(current)
43
+ current.pop() # backtrack
44
+ backtrack([])
45
+ return result
46
+
47
+ print(permute([1, 2, 3]))
48
+ # [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
49
+
50
+ --- Combination Sum ---
51
+ Find all combinations that sum to target. Numbers can be reused.
52
+
53
+ def combination_sum(candidates, target):
54
+ result = []
55
+ candidates.sort()
56
+ def backtrack(start, current, remaining):
57
+ if remaining == 0:
58
+ result.append(current[:])
59
+ return
60
+ for i in range(start, len(candidates)):
61
+ if candidates[i] > remaining:
62
+ break
63
+ current.append(candidates[i])
64
+ backtrack(i, current, remaining - candidates[i]) # i (not i+1) allows reuse
65
+ current.pop()
66
+ backtrack(0, [], target)
67
+ return result
68
+
69
+ print(combination_sum([2, 3, 6, 7], 7))
70
+ # [[2, 2, 3], [7]]
71
+
72
+ --- N-Queens Problem ---
73
+ Place N queens on NxN board so no two queens attack each other.
74
+
75
+ def solve_n_queens(n):
76
+ result = []
77
+ cols = set()
78
+ diag1 = set() # row - col
79
+ diag2 = set() # row + col
80
+
81
+ board = [['.' for _ in range(n)] for _ in range(n)]
82
+
83
+ def backtrack(row):
84
+ if row == n:
85
+ result.append([''.join(r) for r in board])
86
+ return
87
+ for col in range(n):
88
+ if col in cols or (row - col) in diag1 or (row + col) in diag2:
89
+ continue
90
+ cols.add(col)
91
+ diag1.add(row - col)
92
+ diag2.add(row + col)
93
+ board[row][col] = 'Q'
94
+ backtrack(row + 1)
95
+ cols.remove(col) # backtrack
96
+ diag1.remove(row - col)
97
+ diag2.remove(row + col)
98
+ board[row][col] = '.'
99
+
100
+ backtrack(0)
101
+ return result
102
+
103
+ solutions = solve_n_queens(4)
104
+ print(f"Number of solutions for 4-Queens: {len(solutions)}") # 2
105
+ for sol in solutions:
106
+ for row in sol:
107
+ print(row)
108
+ print()
109
+
110
+ --- Sudoku Solver ---
111
+ Fill 9x9 grid so each row, column, and 3x3 box has digits 1-9.
112
+
113
+ def solve_sudoku(board):
114
+ def is_valid(board, row, col, num):
115
+ num = str(num)
116
+ if num in board[row]:
117
+ return False
118
+ if num in [board[r][col] for r in range(9)]:
119
+ return False
120
+ box_r, box_c = 3 * (row // 3), 3 * (col // 3)
121
+ for r in range(box_r, box_r + 3):
122
+ for c in range(box_c, box_c + 3):
123
+ if board[r][c] == num:
124
+ return False
125
+ return True
126
+
127
+ def backtrack():
128
+ for r in range(9):
129
+ for c in range(9):
130
+ if board[r][c] == '.':
131
+ for num in range(1, 10):
132
+ if is_valid(board, r, c, num):
133
+ board[r][c] = str(num)
134
+ if backtrack():
135
+ return True
136
+ board[r][c] = '.' # backtrack
137
+ return False
138
+ return True
139
+
140
+ backtrack()
141
+
142
+ --- Word Search in Grid ---
143
+ Find if word exists in 2D grid by moving up/down/left/right.
144
+
145
+ def word_search(board, word):
146
+ rows, cols = len(board), len(board[0])
147
+
148
+ def backtrack(r, c, index):
149
+ if index == len(word):
150
+ return True
151
+ if r < 0 or r >= rows or c < 0 or c >= cols:
152
+ return False
153
+ if board[r][c] != word[index]:
154
+ return False
155
+ temp = board[r][c]
156
+ board[r][c] = '#' # mark visited
157
+ found = (backtrack(r+1, c, index+1) or
158
+ backtrack(r-1, c, index+1) or
159
+ backtrack(r, c+1, index+1) or
160
+ backtrack(r, c-1, index+1))
161
+ board[r][c] = temp # backtrack (restore)
162
+ return found
163
+
164
+ for r in range(rows):
165
+ for c in range(cols):
166
+ if backtrack(r, c, 0):
167
+ return True
168
+ return False
169
+
170
+ board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
171
+ print(word_search(board, "ABCCED")) # True
172
+ print(word_search(board, "SEE")) # True
173
+ print(word_search(board, "ABCB")) # False
174
+
175
+ --- Rat in a Maze ---
176
+ Find path from (0,0) to (n-1,n-1) in a grid (1=open, 0=blocked).
177
+
178
+ def rat_in_maze(maze):
179
+ n = len(maze)
180
+ solution = [[0]*n for _ in range(n)]
181
+
182
+ def is_safe(r, c):
183
+ return 0 <= r < n and 0 <= c < n and maze[r][c] == 1
184
+
185
+ def solve(r, c):
186
+ if r == n-1 and c == n-1:
187
+ solution[r][c] = 1
188
+ return True
189
+ if is_safe(r, c):
190
+ solution[r][c] = 1
191
+ if solve(r+1, c): return True
192
+ if solve(r, c+1): return True
193
+ solution[r][c] = 0 # backtrack
194
+ return False
195
+ return False
196
+
197
+ if solve(0, 0):
198
+ return solution
199
+ return []
200
+
201
+ maze = [[1,0,0,0],[1,1,0,1],[0,1,0,0],[1,1,1,1]]
202
+ path = rat_in_maze(maze)
203
+ for row in path:
204
+ print(row)
data/docs/binary_search.txt ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SEARCHING ALGORITHMS
2
+ ====================
3
+
4
+ --- Linear Search ---
5
+ Check every element one by one until the target is found.
6
+ Time: O(n). Space: O(1). Works on unsorted arrays.
7
+
8
+ def linear_search(arr, target):
9
+ for i, val in enumerate(arr):
10
+ if val == target:
11
+ return i
12
+ return -1
13
+
14
+ print(linear_search([5, 3, 8, 1, 9], 8)) # Output: 2
15
+ print(linear_search([5, 3, 8, 1, 9], 7)) # Output: -1
16
+
17
+ --- Binary Search ---
18
+ Works on SORTED arrays. Compare target with middle element.
19
+ If smaller, search left half. If larger, search right half.
20
+ Time: O(log n). Space: O(1) iterative, O(log n) recursive.
21
+
22
+ # Iterative
23
+ def binary_search(arr, target):
24
+ low, high = 0, len(arr) - 1
25
+ while low <= high:
26
+ mid = (low + high) // 2
27
+ if arr[mid] == target:
28
+ return mid
29
+ elif arr[mid] < target:
30
+ low = mid + 1
31
+ else:
32
+ high = mid - 1
33
+ return -1
34
+
35
+ arr = [1, 3, 5, 7, 9, 11, 13]
36
+ print(binary_search(arr, 7)) # Output: 3
37
+ print(binary_search(arr, 10)) # Output: -1
38
+
39
+ # Recursive
40
+ def binary_search_recursive(arr, target, low, high):
41
+ if low > high:
42
+ return -1
43
+ mid = (low + high) // 2
44
+ if arr[mid] == target:
45
+ return mid
46
+ elif arr[mid] < target:
47
+ return binary_search_recursive(arr, target, mid + 1, high)
48
+ else:
49
+ return binary_search_recursive(arr, target, low, mid - 1)
50
+
51
+ arr = [2, 4, 6, 8, 10, 12]
52
+ print(binary_search_recursive(arr, 10, 0, len(arr) - 1)) # Output: 4
53
+
54
+ --- Find First and Last Position ---
55
+ Find first and last occurrence of target in sorted array.
56
+
57
+ def find_first(arr, target):
58
+ low, high, result = 0, len(arr) - 1, -1
59
+ while low <= high:
60
+ mid = (low + high) // 2
61
+ if arr[mid] == target:
62
+ result = mid
63
+ high = mid - 1 # keep searching left
64
+ elif arr[mid] < target:
65
+ low = mid + 1
66
+ else:
67
+ high = mid - 1
68
+ return result
69
+
70
+ def find_last(arr, target):
71
+ low, high, result = 0, len(arr) - 1, -1
72
+ while low <= high:
73
+ mid = (low + high) // 2
74
+ if arr[mid] == target:
75
+ result = mid
76
+ low = mid + 1 # keep searching right
77
+ elif arr[mid] < target:
78
+ low = mid + 1
79
+ else:
80
+ high = mid - 1
81
+ return result
82
+
83
+ arr = [1, 2, 2, 2, 3, 4]
84
+ print(find_first(arr, 2)) # Output: 1
85
+ print(find_last(arr, 2)) # Output: 3
86
+
87
+ --- Search in Rotated Sorted Array ---
88
+ Array was sorted then rotated (e.g., [4,5,6,7,0,1,2]).
89
+
90
+ def search_rotated(arr, target):
91
+ low, high = 0, len(arr) - 1
92
+ while low <= high:
93
+ mid = (low + high) // 2
94
+ if arr[mid] == target:
95
+ return mid
96
+ if arr[low] <= arr[mid]: # left half sorted
97
+ if arr[low] <= target < arr[mid]:
98
+ high = mid - 1
99
+ else:
100
+ low = mid + 1
101
+ else: # right half sorted
102
+ if arr[mid] < target <= arr[high]:
103
+ low = mid + 1
104
+ else:
105
+ high = mid - 1
106
+ return -1
107
+
108
+ print(search_rotated([4, 5, 6, 7, 0, 1, 2], 0)) # Output: 4
109
+ print(search_rotated([4, 5, 6, 7, 0, 1, 2], 3)) # Output: -1
110
+
111
+ --- Square Root using Binary Search ---
112
+ Find integer square root of a number without using sqrt().
113
+
114
+ def sqrt_binary(n):
115
+ if n < 2:
116
+ return n
117
+ low, high, result = 1, n // 2, 0
118
+ while low <= high:
119
+ mid = (low + high) // 2
120
+ if mid * mid == n:
121
+ return mid
122
+ elif mid * mid < n:
123
+ result = mid
124
+ low = mid + 1
125
+ else:
126
+ high = mid - 1
127
+ return result
128
+
129
+ print(sqrt_binary(16)) # Output: 4
130
+ print(sqrt_binary(20)) # Output: 4 (floor)
data/docs/css_flexbox.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CSS Flexbox (Flexible Box Layout) is a layout model for arranging items in a container.
2
+ Activated by setting display: flex on a container element.
3
+
4
+ Container properties:
5
+ - flex-direction: row (default, horizontal) or column (vertical).
6
+ - justify-content: aligns items on main axis. Values: flex-start, flex-end, center, space-between, space-around.
7
+ - align-items: aligns items on cross axis. Values: stretch (default), flex-start, flex-end, center.
8
+ - flex-wrap: nowrap (default) or wrap to allow items to move to next line.
9
+ - gap: space between flex items.
10
+
11
+ Item properties:
12
+ - flex-grow: how much item grows relative to siblings. 0 means no grow.
13
+ - flex-shrink: how much item shrinks when space is limited.
14
+ - flex-basis: initial size of item before flex calculations.
15
+ - align-self: overrides align-items for a single item.
16
+
17
+ Shorthand: flex: grow shrink basis (e.g., flex: 1 1 auto)
data/docs/dynamic_programming.txt ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ DYNAMIC PROGRAMMING (DP)
2
+ ========================
3
+ DP solves problems by breaking them into overlapping subproblems and storing results to avoid recomputation.
4
+
5
+ Two approaches:
6
+ - Top-Down (Memoization): recursion + cache
7
+ - Bottom-Up (Tabulation): fill a table iteratively
8
+
9
+ Key properties needed:
10
+ 1. Optimal substructure: optimal solution built from optimal subproblems.
11
+ 2. Overlapping subproblems: same subproblem solved multiple times.
12
+
13
+ --- Fibonacci Number ---
14
+ Classic DP example. fib(n) = fib(n-1) + fib(n-2)
15
+
16
+ # Naive recursion - O(2^n)
17
+ def fib_naive(n):
18
+ if n <= 1:
19
+ return n
20
+ return fib_naive(n-1) + fib_naive(n-2)
21
+
22
+ # Memoization - O(n)
23
+ def fib_memo(n, memo={}):
24
+ if n in memo:
25
+ return memo[n]
26
+ if n <= 1:
27
+ return n
28
+ memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo)
29
+ return memo[n]
30
+
31
+ # Tabulation - O(n)
32
+ def fib_dp(n):
33
+ if n <= 1:
34
+ return n
35
+ dp = [0] * (n + 1)
36
+ dp[1] = 1
37
+ for i in range(2, n + 1):
38
+ dp[i] = dp[i-1] + dp[i-2]
39
+ return dp[n]
40
+
41
+ print(fib_dp(10)) # Output: 55
42
+ print(fib_dp(20)) # Output: 6765
43
+
44
+ --- 0/1 Knapsack Problem ---
45
+ Given items with weights and values, maximize value in a bag of capacity W.
46
+ Each item can be taken once (0/1). Time: O(n*W). Space: O(n*W).
47
+
48
+ def knapsack(weights, values, capacity):
49
+ n = len(weights)
50
+ dp = [[0] * (capacity + 1) for _ in range(n + 1)]
51
+ for i in range(1, n + 1):
52
+ for w in range(capacity + 1):
53
+ # Don't take item i
54
+ dp[i][w] = dp[i-1][w]
55
+ # Take item i if it fits
56
+ if weights[i-1] <= w:
57
+ dp[i][w] = max(dp[i][w], dp[i-1][w - weights[i-1]] + values[i-1])
58
+ return dp[n][capacity]
59
+
60
+ weights = [1, 2, 3, 5]
61
+ values = [1, 6, 10, 16]
62
+ capacity = 7
63
+ print(knapsack(weights, values, capacity)) # Output: 22
64
+
65
+ --- Longest Common Subsequence (LCS) ---
66
+ Find length of longest subsequence present in both strings.
67
+ Time: O(m*n). Space: O(m*n).
68
+
69
+ def lcs(s1, s2):
70
+ m, n = len(s1), len(s2)
71
+ dp = [[0] * (n + 1) for _ in range(m + 1)]
72
+ for i in range(1, m + 1):
73
+ for j in range(1, n + 1):
74
+ if s1[i-1] == s2[j-1]:
75
+ dp[i][j] = dp[i-1][j-1] + 1
76
+ else:
77
+ dp[i][j] = max(dp[i-1][j], dp[i][j-1])
78
+ return dp[m][n]
79
+
80
+ print(lcs("ABCBDAB", "BDCABA")) # Output: 4 (BCBA)
81
+
82
+ --- Longest Increasing Subsequence (LIS) ---
83
+ Find length of longest strictly increasing subsequence.
84
+ Time: O(n^2). Space: O(n).
85
+
86
+ def lis(arr):
87
+ n = len(arr)
88
+ dp = [1] * n
89
+ for i in range(1, n):
90
+ for j in range(i):
91
+ if arr[j] < arr[i]:
92
+ dp[i] = max(dp[i], dp[j] + 1)
93
+ return max(dp)
94
+
95
+ print(lis([10, 9, 2, 5, 3, 7, 101, 18])) # Output: 4 (2,3,7,101)
96
+
97
+ --- Coin Change Problem ---
98
+ Find minimum number of coins to make amount. Coins can be reused.
99
+ Time: O(amount * len(coins)). Space: O(amount).
100
+
101
+ def coin_change(coins, amount):
102
+ dp = [float('inf')] * (amount + 1)
103
+ dp[0] = 0
104
+ for i in range(1, amount + 1):
105
+ for coin in coins:
106
+ if coin <= i:
107
+ dp[i] = min(dp[i], dp[i - coin] + 1)
108
+ return dp[amount] if dp[amount] != float('inf') else -1
109
+
110
+ print(coin_change([1, 5, 6, 9], 11)) # Output: 2 (coins 5+6)
111
+ print(coin_change([2], 3)) # Output: -1 (impossible)
112
+
113
+ --- Subset Sum Problem ---
114
+ Check if any subset of array sums to target.
115
+ Time: O(n * target). Space: O(n * target).
116
+
117
+ def subset_sum(arr, target):
118
+ n = len(arr)
119
+ dp = [[False] * (target + 1) for _ in range(n + 1)]
120
+ for i in range(n + 1):
121
+ dp[i][0] = True # sum 0 always possible
122
+ for i in range(1, n + 1):
123
+ for j in range(1, target + 1):
124
+ dp[i][j] = dp[i-1][j]
125
+ if arr[i-1] <= j:
126
+ dp[i][j] = dp[i][j] or dp[i-1][j - arr[i-1]]
127
+ return dp[n][target]
128
+
129
+ print(subset_sum([3, 34, 4, 12, 5, 2], 9)) # Output: True (4+5)
130
+ print(subset_sum([3, 34, 4, 12, 5, 2], 30)) # Output: False
131
+
132
+ --- Matrix Chain Multiplication ---
133
+ Find minimum operations to multiply a chain of matrices.
134
+ Time: O(n^3). Space: O(n^2).
135
+
136
+ def matrix_chain(dims):
137
+ n = len(dims) - 1
138
+ dp = [[0] * n for _ in range(n)]
139
+ for length in range(2, n + 1):
140
+ for i in range(n - length + 1):
141
+ j = i + length - 1
142
+ dp[i][j] = float('inf')
143
+ for k in range(i, j):
144
+ cost = dp[i][k] + dp[k+1][j] + dims[i]*dims[k+1]*dims[j+1]
145
+ dp[i][j] = min(dp[i][j], cost)
146
+ return dp[0][n-1]
147
+
148
+ dims = [40, 20, 30, 10, 30]
149
+ print(matrix_chain(dims)) # Output: 26000
150
+
151
+ --- Edit Distance (Levenshtein) ---
152
+ Minimum operations (insert, delete, replace) to convert one string to another.
153
+ Time: O(m*n). Space: O(m*n).
154
+
155
+ def edit_distance(s1, s2):
156
+ m, n = len(s1), len(s2)
157
+ dp = [[0] * (n + 1) for _ in range(m + 1)]
158
+ for i in range(m + 1):
159
+ dp[i][0] = i
160
+ for j in range(n + 1):
161
+ dp[0][j] = j
162
+ for i in range(1, m + 1):
163
+ for j in range(1, n + 1):
164
+ if s1[i-1] == s2[j-1]:
165
+ dp[i][j] = dp[i-1][j-1]
166
+ else:
167
+ dp[i][j] = 1 + min(dp[i-1][j], # delete
168
+ dp[i][j-1], # insert
169
+ dp[i-1][j-1]) # replace
170
+ return dp[m][n]
171
+
172
+ print(edit_distance("horse", "ros")) # Output: 3
173
+ print(edit_distance("intention", "execution")) # Output: 5
data/docs/graph_algorithms.txt ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GRAPH ALGORITHMS
2
+ ================
3
+ A graph has vertices (nodes) connected by edges. Used to model networks, maps, dependencies.
4
+
5
+ Types:
6
+ - Directed / Undirected
7
+ - Weighted / Unweighted
8
+ - Cyclic / Acyclic (DAG = Directed Acyclic Graph)
9
+
10
+ Representations:
11
+ - Adjacency Matrix: 2D array. O(1) edge lookup, O(V^2) space.
12
+ - Adjacency List: dict/list of lists. O(V+E) space. Most common.
13
+
14
+ # Adjacency List representation
15
+ graph = {
16
+ 0: [1, 2],
17
+ 1: [0, 3, 4],
18
+ 2: [0],
19
+ 3: [1],
20
+ 4: [1]
21
+ }
22
+
23
+ --- BFS (Breadth-First Search) ---
24
+ Explore level by level using a queue. Finds shortest path in unweighted graphs.
25
+ Time: O(V+E). Space: O(V).
26
+
27
+ from collections import deque
28
+
29
+ def bfs(graph, start):
30
+ visited = set([start])
31
+ queue = deque([start])
32
+ order = []
33
+ while queue:
34
+ node = queue.popleft()
35
+ order.append(node)
36
+ for neighbor in graph[node]:
37
+ if neighbor not in visited:
38
+ visited.add(neighbor)
39
+ queue.append(neighbor)
40
+ return order
41
+
42
+ graph = {0:[1,2], 1:[0,3,4], 2:[0], 3:[1], 4:[1]}
43
+ print(bfs(graph, 0)) # Output: [0, 1, 2, 3, 4]
44
+
45
+ --- DFS (Depth-First Search) ---
46
+ Explore as deep as possible before backtracking. Uses stack (or recursion).
47
+ Time: O(V+E). Space: O(V).
48
+
49
+ # Iterative DFS
50
+ def dfs_iterative(graph, start):
51
+ visited = set()
52
+ stack = [start]
53
+ order = []
54
+ while stack:
55
+ node = stack.pop()
56
+ if node not in visited:
57
+ visited.add(node)
58
+ order.append(node)
59
+ for neighbor in graph[node]:
60
+ if neighbor not in visited:
61
+ stack.append(neighbor)
62
+ return order
63
+
64
+ # Recursive DFS
65
+ def dfs_recursive(graph, node, visited=None):
66
+ if visited is None:
67
+ visited = set()
68
+ visited.add(node)
69
+ result = [node]
70
+ for neighbor in graph[node]:
71
+ if neighbor not in visited:
72
+ result += dfs_recursive(graph, neighbor, visited)
73
+ return result
74
+
75
+ print(dfs_iterative(graph, 0)) # Output: [0, 2, 1, 4, 3]
76
+ print(dfs_recursive(graph, 0)) # Output: [0, 1, 3, 4, 2]
77
+
78
+ --- Shortest Path: Dijkstra's Algorithm ---
79
+ Find shortest path from source to all nodes in a weighted graph (non-negative weights).
80
+ Time: O((V+E) log V) with min-heap. Space: O(V).
81
+
82
+ import heapq
83
+
84
+ def dijkstra(graph, start):
85
+ # graph: {node: [(neighbor, weight), ...]}
86
+ dist = {node: float('inf') for node in graph}
87
+ dist[start] = 0
88
+ heap = [(0, start)] # (distance, node)
89
+ while heap:
90
+ d, u = heapq.heappop(heap)
91
+ if d > dist[u]:
92
+ continue
93
+ for v, w in graph[u]:
94
+ if dist[u] + w < dist[v]:
95
+ dist[v] = dist[u] + w
96
+ heapq.heappush(heap, (dist[v], v))
97
+ return dist
98
+
99
+ wgraph = {
100
+ 'A': [('B', 1), ('C', 4)],
101
+ 'B': [('C', 2), ('D', 5)],
102
+ 'C': [('D', 1)],
103
+ 'D': []
104
+ }
105
+ print(dijkstra(wgraph, 'A'))
106
+ # Output: {'A': 0, 'B': 1, 'C': 3, 'D': 4}
107
+
108
+ --- Cycle Detection in Directed Graph ---
109
+ Use DFS with a recursion stack to detect back edges.
110
+
111
+ def has_cycle_directed(graph):
112
+ visited = set()
113
+ rec_stack = set()
114
+
115
+ def dfs(node):
116
+ visited.add(node)
117
+ rec_stack.add(node)
118
+ for neighbor in graph.get(node, []):
119
+ if neighbor not in visited:
120
+ if dfs(neighbor):
121
+ return True
122
+ elif neighbor in rec_stack:
123
+ return True
124
+ rec_stack.remove(node)
125
+ return False
126
+
127
+ for node in graph:
128
+ if node not in visited:
129
+ if dfs(node):
130
+ return True
131
+ return False
132
+
133
+ g1 = {0: [1], 1: [2], 2: [0]} # cycle
134
+ g2 = {0: [1], 1: [2], 2: []} # no cycle
135
+ print(has_cycle_directed(g1)) # True
136
+ print(has_cycle_directed(g2)) # False
137
+
138
+ --- Topological Sort (Kahn's Algorithm) ---
139
+ Linear ordering of vertices in a DAG such that for every edge u->v, u comes before v.
140
+ Time: O(V+E). Space: O(V).
141
+
142
+ from collections import deque
143
+
144
+ def topological_sort(graph, num_nodes):
145
+ in_degree = [0] * num_nodes
146
+ for u in graph:
147
+ for v in graph[u]:
148
+ in_degree[v] += 1
149
+ queue = deque([i for i in range(num_nodes) if in_degree[i] == 0])
150
+ order = []
151
+ while queue:
152
+ node = queue.popleft()
153
+ order.append(node)
154
+ for neighbor in graph.get(node, []):
155
+ in_degree[neighbor] -= 1
156
+ if in_degree[neighbor] == 0:
157
+ queue.append(neighbor)
158
+ return order if len(order) == num_nodes else [] # empty = cycle
159
+
160
+ dag = {0: [1, 2], 1: [3], 2: [3], 3: []}
161
+ print(topological_sort(dag, 4)) # Output: [0, 1, 2, 3] or [0, 2, 1, 3]
162
+
163
+ --- Minimum Spanning Tree: Kruskal's Algorithm ---
164
+ Connect all nodes with minimum total edge weight. Uses Union-Find.
165
+ Time: O(E log E). Space: O(V).
166
+
167
+ def kruskal(num_nodes, edges):
168
+ # edges: [(weight, u, v), ...]
169
+ edges.sort()
170
+ parent = list(range(num_nodes))
171
+
172
+ def find(x):
173
+ while parent[x] != x:
174
+ parent[x] = parent[parent[x]]
175
+ x = parent[x]
176
+ return x
177
+
178
+ def union(x, y):
179
+ px, py = find(x), find(y)
180
+ if px == py:
181
+ return False
182
+ parent[px] = py
183
+ return True
184
+
185
+ mst_cost = 0
186
+ mst_edges = []
187
+ for w, u, v in edges:
188
+ if union(u, v):
189
+ mst_cost += w
190
+ mst_edges.append((u, v, w))
191
+ return mst_cost, mst_edges
192
+
193
+ edges = [(4,0,1),(2,0,2),(3,1,2),(1,1,3),(5,2,3)]
194
+ print(kruskal(4, edges))
195
+ # Output: (6, [(1, 3, 1), (0, 2, 2), (1, 2, 3)])
196
+
197
+ --- Bellman-Ford Algorithm ---
198
+ Shortest path with negative weights. Detects negative cycles.
199
+ Time: O(V*E). Space: O(V).
200
+
201
+ def bellman_ford(graph, num_nodes, start):
202
+ # graph: [(u, v, weight), ...]
203
+ dist = [float('inf')] * num_nodes
204
+ dist[start] = 0
205
+ for _ in range(num_nodes - 1):
206
+ for u, v, w in graph:
207
+ if dist[u] + w < dist[v]:
208
+ dist[v] = dist[u] + w
209
+ # Check for negative cycles
210
+ for u, v, w in graph:
211
+ if dist[u] + w < dist[v]:
212
+ return None # negative cycle
213
+ return dist
214
+
215
+ edges = [(0,1,4),(0,2,5),(1,2,-3),(2,3,4)]
216
+ print(bellman_ford(edges, 4, 0))
217
+ # Output: [0, 4, 1, 5]
data/docs/greedy_algorithms.txt ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GREEDY ALGORITHMS
2
+ =================
3
+ Greedy algorithms make the locally optimal choice at each step hoping to find a global optimum.
4
+ They do NOT reconsider past choices (unlike DP).
5
+
6
+ When to use greedy: when local optimum leads to global optimum (greedy choice property + optimal substructure).
7
+
8
+ --- Activity Selection Problem ---
9
+ Select maximum number of non-overlapping activities.
10
+ Sort by end time, always pick activity that ends earliest.
11
+ Time: O(n log n).
12
+
13
+ def activity_selection(activities):
14
+ # activities: [(start, end), ...]
15
+ activities.sort(key=lambda x: x[1]) # sort by end time
16
+ selected = [activities[0]]
17
+ last_end = activities[0][1]
18
+ for start, end in activities[1:]:
19
+ if start >= last_end:
20
+ selected.append((start, end))
21
+ last_end = end
22
+ return selected
23
+
24
+ acts = [(1,4),(3,5),(0,6),(5,7),(3,9),(5,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
25
+ print(activity_selection(acts))
26
+ # [(1,4),(5,7),(8,11),(12,16)] — 4 activities
27
+
28
+ --- Fractional Knapsack ---
29
+ Take fractions of items to maximize value. Unlike 0/1 knapsack, items can be split.
30
+ Sort by value/weight ratio. Greedy works here (not for 0/1).
31
+ Time: O(n log n).
32
+
33
+ def fractional_knapsack(weights, values, capacity):
34
+ items = sorted(zip(values, weights), key=lambda x: x[0]/x[1], reverse=True)
35
+ total_value = 0
36
+ for value, weight in items:
37
+ if capacity >= weight:
38
+ total_value += value
39
+ capacity -= weight
40
+ else:
41
+ total_value += value * (capacity / weight)
42
+ break
43
+ return round(total_value, 2)
44
+
45
+ weights = [10, 20, 30]
46
+ values = [60, 100, 120]
47
+ print(fractional_knapsack(weights, values, 50)) # Output: 240.0
48
+
49
+ --- Coin Change (Greedy — only works for canonical coin systems) ---
50
+ For standard denominations (1,5,10,25 cents), greedy gives minimum coins.
51
+ Note: greedy FAILS for arbitrary denominations — use DP instead.
52
+
53
+ def coin_change_greedy(coins, amount):
54
+ coins.sort(reverse=True)
55
+ count = 0
56
+ result = []
57
+ for coin in coins:
58
+ while amount >= coin:
59
+ amount -= coin
60
+ count += 1
61
+ result.append(coin)
62
+ return count, result
63
+
64
+ print(coin_change_greedy([1, 5, 10, 25], 41))
65
+ # Output: (4, [25, 10, 5, 1])
66
+
67
+ --- Job Scheduling to Maximize Profit ---
68
+ Schedule jobs with deadlines to maximize total profit. Each job takes 1 unit of time.
69
+ Time: O(n^2).
70
+
71
+ def job_scheduling(jobs):
72
+ # jobs: [(id, deadline, profit), ...]
73
+ jobs.sort(key=lambda x: x[2], reverse=True) # sort by profit
74
+ max_deadline = max(j[1] for j in jobs)
75
+ slots = [None] * (max_deadline + 1)
76
+ total_profit = 0
77
+ for job_id, deadline, profit in jobs:
78
+ for t in range(min(deadline, max_deadline), 0, -1):
79
+ if slots[t] is None:
80
+ slots[t] = job_id
81
+ total_profit += profit
82
+ break
83
+ scheduled = [s for s in slots if s]
84
+ return scheduled, total_profit
85
+
86
+ jobs = [('a',2,100),('b',1,19),('c',2,27),('d',1,25),('e',3,15)]
87
+ print(job_scheduling(jobs)) # (['c', 'a', 'e'], 142)
88
+
89
+ --- Huffman Encoding ---
90
+ Build optimal prefix-free codes for data compression.
91
+ Characters with higher frequency get shorter codes.
92
+ Time: O(n log n).
93
+
94
+ import heapq
95
+
96
+ def huffman_encoding(freq):
97
+ heap = [[f, [ch, ""]] for ch, f in freq.items()]
98
+ heapq.heapify(heap)
99
+ while len(heap) > 1:
100
+ lo = heapq.heappop(heap)
101
+ hi = heapq.heappop(heap)
102
+ for pair in lo[1:]:
103
+ pair[1] = '0' + pair[1]
104
+ for pair in hi[1:]:
105
+ pair[1] = '1' + pair[1]
106
+ heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
107
+ return sorted(heap[0][1:], key=lambda x: len(x[1]))
108
+
109
+ freq = {'a': 5, 'b': 9, 'c': 12, 'd': 13, 'e': 16, 'f': 45}
110
+ codes = huffman_encoding(freq)
111
+ for char, code in codes:
112
+ print(f"{char}: {code}")
113
+ # f: 0
114
+ # c: 100
115
+ # d: 101
116
+ # a: 1100
117
+ # b: 1101
118
+ # e: 111
119
+
120
+ --- Minimum Platforms (Greedy) ---
121
+ Find minimum number of platforms needed at a railway station.
122
+ Time: O(n log n).
123
+
124
+ def min_platforms(arrivals, departures):
125
+ arrivals.sort()
126
+ departures.sort()
127
+ platforms = 1
128
+ max_platforms = 1
129
+ i = 1
130
+ j = 0
131
+ while i < len(arrivals) and j < len(departures):
132
+ if arrivals[i] <= departures[j]:
133
+ platforms += 1
134
+ i += 1
135
+ else:
136
+ platforms -= 1
137
+ j += 1
138
+ max_platforms = max(max_platforms, platforms)
139
+ return max_platforms
140
+
141
+ arr = [900, 940, 950, 1100, 1500, 1800]
142
+ dep = [910, 1200, 1120, 1130, 1900, 2000]
143
+ print(min_platforms(arr, dep)) # Output: 3
144
+
145
+ --- Gas Station (Circular Route) ---
146
+ Find starting gas station to complete a circular route. Greedy: if total gas >= total cost, solution exists.
147
+ Time: O(n). Space: O(1).
148
+
149
+ def can_complete_circuit(gas, cost):
150
+ total_tank = 0
151
+ curr_tank = 0
152
+ start = 0
153
+ for i in range(len(gas)):
154
+ diff = gas[i] - cost[i]
155
+ total_tank += diff
156
+ curr_tank += diff
157
+ if curr_tank < 0:
158
+ start = i + 1
159
+ curr_tank = 0
160
+ return start if total_tank >= 0 else -1
161
+
162
+ gas = [1, 2, 3, 4, 5]
163
+ cost = [3, 4, 5, 1, 2]
164
+ print(can_complete_circuit(gas, cost)) # Output: 3
165
+
166
+ --- Greedy vs Dynamic Programming ---
167
+ Greedy: fast O(n log n), works when greedy choice is provably optimal.
168
+ DP: slower O(n^2) or O(n*W), works for all optimization problems.
169
+ Example: Fractional Knapsack → Greedy. 0/1 Knapsack → DP.
data/docs/hashing.txt ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ HASHING AND HASH TABLES
2
+ =======================
3
+ A hash table stores key-value pairs. Uses a hash function to map keys to indices.
4
+ Average time: O(1) for insert, delete, search. Worst case: O(n) with collisions.
5
+
6
+ Python's dict and set are hash tables.
7
+
8
+ --- Hash Table Basics ---
9
+ # Python dict = hash map
10
+ phone_book = {}
11
+ phone_book["Alice"] = "555-1234"
12
+ phone_book["Bob"] = "555-5678"
13
+
14
+ print(phone_book["Alice"]) # "555-1234"
15
+ print("Alice" in phone_book) # True
16
+ del phone_book["Bob"]
17
+ print(phone_book.get("Bob", "N/A")) # "N/A"
18
+
19
+ # Iterate
20
+ for name, number in phone_book.items():
21
+ print(name, number)
22
+
23
+ --- Build a Hash Map from Scratch ---
24
+ class HashMap:
25
+ def __init__(self, size=1000):
26
+ self.size = size
27
+ self.buckets = [[] for _ in range(size)]
28
+
29
+ def _hash(self, key):
30
+ return hash(key) % self.size
31
+
32
+ def put(self, key, value):
33
+ idx = self._hash(key)
34
+ for i, (k, v) in enumerate(self.buckets[idx]):
35
+ if k == key:
36
+ self.buckets[idx][i] = (key, value)
37
+ return
38
+ self.buckets[idx].append((key, value))
39
+
40
+ def get(self, key):
41
+ idx = self._hash(key)
42
+ for k, v in self.buckets[idx]:
43
+ if k == key:
44
+ return v
45
+ return None
46
+
47
+ def remove(self, key):
48
+ idx = self._hash(key)
49
+ self.buckets[idx] = [(k, v) for k, v in self.buckets[idx] if k != key]
50
+
51
+ hm = HashMap()
52
+ hm.put("name", "Alice")
53
+ hm.put("age", 25)
54
+ print(hm.get("name")) # Alice
55
+ hm.remove("age")
56
+ print(hm.get("age")) # None
57
+
58
+ --- Two Sum Problem ---
59
+ Find two numbers that add up to target. Classic hash map problem.
60
+ Time: O(n). Space: O(n).
61
+
62
+ def two_sum(nums, target):
63
+ seen = {} # value → index
64
+ for i, num in enumerate(nums):
65
+ complement = target - num
66
+ if complement in seen:
67
+ return [seen[complement], i]
68
+ seen[num] = i
69
+ return []
70
+
71
+ print(two_sum([2, 7, 11, 15], 9)) # [0, 1]
72
+ print(two_sum([3, 2, 4], 6)) # [1, 2]
73
+
74
+ --- Group Anagrams ---
75
+ Group words that are anagrams of each other.
76
+ Time: O(n * k log k) where k = max word length.
77
+
78
+ from collections import defaultdict
79
+
80
+ def group_anagrams(words):
81
+ groups = defaultdict(list)
82
+ for word in words:
83
+ key = ''.join(sorted(word)) # sorted chars as key
84
+ groups[key].append(word)
85
+ return list(groups.values())
86
+
87
+ print(group_anagrams(["eat","tea","tan","ate","nat","bat"]))
88
+ # [["eat","tea","ate"], ["tan","nat"], ["bat"]]
89
+
90
+ --- Frequency Counter ---
91
+ Count frequency of each element.
92
+
93
+ from collections import Counter
94
+
95
+ def top_k_frequent(nums, k):
96
+ count = Counter(nums)
97
+ return [x for x, _ in count.most_common(k)]
98
+
99
+ print(top_k_frequent([1,1,1,2,2,3], 2)) # [1, 2]
100
+
101
+ --- Longest Consecutive Sequence ---
102
+ Find length of longest consecutive elements sequence.
103
+ Time: O(n). Space: O(n).
104
+
105
+ def longest_consecutive(nums):
106
+ num_set = set(nums)
107
+ best = 0
108
+ for num in num_set:
109
+ if num - 1 not in num_set: # start of sequence
110
+ length = 1
111
+ while num + length in num_set:
112
+ length += 1
113
+ best = max(best, length)
114
+ return best
115
+
116
+ print(longest_consecutive([100,4,200,1,3,2])) # 4 (1,2,3,4)
117
+
118
+ --- Subarray Sum Equals K ---
119
+ Count subarrays with sum equal to k. Uses prefix sum + hash map.
120
+ Time: O(n). Space: O(n).
121
+
122
+ def subarray_sum(nums, k):
123
+ count = 0
124
+ prefix = 0
125
+ freq = {0: 1} # prefix_sum → frequency
126
+ for num in nums:
127
+ prefix += num
128
+ if prefix - k in freq:
129
+ count += freq[prefix - k]
130
+ freq[prefix] = freq.get(prefix, 0) + 1
131
+ return count
132
+
133
+ print(subarray_sum([1, 1, 1], 2)) # 2
134
+ print(subarray_sum([1, 2, 3], 3)) # 2
135
+
136
+ --- Check if Two Strings are Anagram ---
137
+ def is_anagram(s, t):
138
+ return Counter(s) == Counter(t)
139
+
140
+ print(is_anagram("anagram", "nagaram")) # True
141
+ print(is_anagram("rat", "car")) # False
142
+
143
+ --- First Non-Repeating Character ---
144
+ def first_unique(s):
145
+ count = Counter(s)
146
+ for i, ch in enumerate(s):
147
+ if count[ch] == 1:
148
+ return i
149
+ return -1
150
+
151
+ print(first_unique("leetcode")) # 0 (l)
152
+ print(first_unique("aabb")) # -1
153
+
154
+ --- Hash Set Uses ---
155
+ # O(1) membership test
156
+ visited = set()
157
+ visited.add(5)
158
+ print(5 in visited) # True
159
+ print(10 in visited) # False
160
+
161
+ # Remove duplicates
162
+ arr = [1, 2, 2, 3, 3, 4]
163
+ unique = list(set(arr))
164
+ print(unique) # [1, 2, 3, 4]
165
+
166
+ # Intersection, Union
167
+ a = {1, 2, 3, 4}
168
+ b = {3, 4, 5, 6}
169
+ print(a & b) # {3, 4} intersection
170
+ print(a | b) # {1,2,3,4,5,6} union
171
+ print(a - b) # {1, 2} difference
data/docs/javascript_promises.txt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ JavaScript Promises handle asynchronous operations in a cleaner way than callbacks.
2
+
3
+ Promise States:
4
+ - Pending: initial state, operation not complete.
5
+ - Fulfilled: operation completed successfully.
6
+ - Rejected: operation failed.
7
+
8
+ Creating a Promise:
9
+ const myPromise = new Promise((resolve, reject) => {
10
+ if (success) resolve(result);
11
+ else reject(error);
12
+ });
13
+
14
+ Consuming a Promise:
15
+ myPromise
16
+ .then(result => console.log(result))
17
+ .catch(error => console.error(error))
18
+ .finally(() => console.log('always runs'));
19
+
20
+ async/await syntax (ES2017) is syntactic sugar over promises:
21
+ async function fetchData() {
22
+ try {
23
+ const data = await fetch(url);
24
+ const json = await data.json();
25
+ return json;
26
+ } catch (error) {
27
+ console.error(error);
28
+ }
29
+ }
30
+
31
+ Promise utility methods:
32
+ - Promise.all([p1, p2]): runs in parallel, resolves when all resolve.
33
+ - Promise.race([p1, p2]): resolves/rejects with first settled promise.
34
+ - Promise.allSettled(): waits for all, returns all results regardless of success/failure.
data/docs/linked_list.txt ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ LINKED LISTS
2
+ ============
3
+ A linked list stores elements in nodes. Each node holds data and a pointer to the next node.
4
+ Unlike arrays, nodes are not stored in contiguous memory.
5
+
6
+ Advantages: O(1) insert/delete at head, dynamic size.
7
+ Disadvantages: O(n) access by index, extra memory for pointers.
8
+
9
+ --- Node Definition ---
10
+ class Node:
11
+ def __init__(self, data):
12
+ self.data = data
13
+ self.next = None
14
+
15
+ --- Singly Linked List ---
16
+ Each node points to the next. Last node points to None.
17
+
18
+ class LinkedList:
19
+ def __init__(self):
20
+ self.head = None
21
+
22
+ def insert_at_head(self, data):
23
+ node = Node(data)
24
+ node.next = self.head
25
+ self.head = node
26
+
27
+ def insert_at_tail(self, data):
28
+ node = Node(data)
29
+ if not self.head:
30
+ self.head = node
31
+ return
32
+ curr = self.head
33
+ while curr.next:
34
+ curr = curr.next
35
+ curr.next = node
36
+
37
+ def delete(self, data):
38
+ if not self.head:
39
+ return
40
+ if self.head.data == data:
41
+ self.head = self.head.next
42
+ return
43
+ curr = self.head
44
+ while curr.next and curr.next.data != data:
45
+ curr = curr.next
46
+ if curr.next:
47
+ curr.next = curr.next.next
48
+
49
+ def search(self, data):
50
+ curr = self.head
51
+ while curr:
52
+ if curr.data == data:
53
+ return True
54
+ curr = curr.next
55
+ return False
56
+
57
+ def print_list(self):
58
+ curr = self.head
59
+ result = []
60
+ while curr:
61
+ result.append(curr.data)
62
+ curr = curr.next
63
+ print(result)
64
+
65
+ ll = LinkedList()
66
+ ll.insert_at_tail(1)
67
+ ll.insert_at_tail(2)
68
+ ll.insert_at_tail(3)
69
+ ll.insert_at_head(0)
70
+ ll.print_list() # [0, 1, 2, 3]
71
+ ll.delete(2)
72
+ ll.print_list() # [0, 1, 3]
73
+
74
+ --- Reverse a Linked List ---
75
+ Time: O(n). Space: O(1).
76
+
77
+ def reverse(head):
78
+ prev = None
79
+ curr = head
80
+ while curr:
81
+ next_node = curr.next
82
+ curr.next = prev
83
+ prev = curr
84
+ curr = next_node
85
+ return prev # new head
86
+
87
+ --- Detect Cycle (Floyd's Algorithm) ---
88
+ Use slow and fast pointers. If they meet, there is a cycle.
89
+ Time: O(n). Space: O(1).
90
+
91
+ def has_cycle(head):
92
+ slow = fast = head
93
+ while fast and fast.next:
94
+ slow = slow.next
95
+ fast = fast.next.next
96
+ if slow == fast:
97
+ return True
98
+ return False
99
+
100
+ --- Find Middle of Linked List ---
101
+ Time: O(n). Space: O(1).
102
+
103
+ def find_middle(head):
104
+ slow = fast = head
105
+ while fast and fast.next:
106
+ slow = slow.next
107
+ fast = fast.next.next
108
+ return slow # slow is at middle
109
+
110
+ --- Merge Two Sorted Linked Lists ---
111
+ Time: O(n + m). Space: O(1).
112
+
113
+ def merge_sorted(l1, l2):
114
+ dummy = Node(0)
115
+ curr = dummy
116
+ while l1 and l2:
117
+ if l1.data <= l2.data:
118
+ curr.next = l1
119
+ l1 = l1.next
120
+ else:
121
+ curr.next = l2
122
+ l2 = l2.next
123
+ curr = curr.next
124
+ curr.next = l1 or l2
125
+ return dummy.next
126
+
127
+ --- Remove Nth Node From End ---
128
+ Use two pointers separated by n steps.
129
+ Time: O(n). Space: O(1).
130
+
131
+ def remove_nth_from_end(head, n):
132
+ dummy = Node(0)
133
+ dummy.next = head
134
+ fast = slow = dummy
135
+ for _ in range(n + 1):
136
+ fast = fast.next
137
+ while fast:
138
+ slow = slow.next
139
+ fast = fast.next
140
+ slow.next = slow.next.next
141
+ return dummy.next
142
+
143
+ --- Check if Palindrome ---
144
+ Time: O(n). Space: O(1).
145
+
146
+ def is_palindrome(head):
147
+ # Find middle
148
+ slow = fast = head
149
+ while fast and fast.next:
150
+ slow = slow.next
151
+ fast = fast.next.next
152
+ # Reverse second half
153
+ prev = None
154
+ curr = slow
155
+ while curr:
156
+ nxt = curr.next
157
+ curr.next = prev
158
+ prev = curr
159
+ curr = nxt
160
+ # Compare halves
161
+ left, right = head, prev
162
+ while right:
163
+ if left.data != right.data:
164
+ return False
165
+ left = left.next
166
+ right = right.next
167
+ return True
168
+
169
+ --- Doubly Linked List ---
170
+ Each node has a pointer to both next and previous nodes.
171
+
172
+ class DNode:
173
+ def __init__(self, data):
174
+ self.data = data
175
+ self.next = None
176
+ self.prev = None
177
+
178
+ class DoublyLinkedList:
179
+ def __init__(self):
180
+ self.head = None
181
+
182
+ def insert_at_head(self, data):
183
+ node = DNode(data)
184
+ if self.head:
185
+ self.head.prev = node
186
+ node.next = self.head
187
+ self.head = node
188
+
189
+ def delete(self, node):
190
+ if node.prev:
191
+ node.prev.next = node.next
192
+ else:
193
+ self.head = node.next
194
+ if node.next:
195
+ node.next.prev = node.prev
196
+
197
+ --- Circular Linked List ---
198
+ Last node points back to head forming a circle.
199
+
200
+ def is_circular(head):
201
+ if not head:
202
+ return False
203
+ curr = head.next
204
+ while curr and curr != head:
205
+ curr = curr.next
206
+ return curr == head
data/docs/react_hooks.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ React Hooks are functions that let you use state and lifecycle features in functional components.
2
+ Introduced in React 16.8 to replace class components.
3
+
4
+ useState: manages local component state.
5
+ Syntax: const [state, setState] = useState(initialValue)
6
+ Example: const [count, setCount] = useState(0)
7
+
8
+ useEffect: handles side effects like API calls, subscriptions, DOM updates.
9
+ Runs after every render by default.
10
+ Syntax: useEffect(() => { /* effect */ }, [dependencies])
11
+ Empty dependency array [] means run only once on mount.
12
+
13
+ useContext: consume React context without wrapping in Consumer component.
14
+ Syntax: const value = useContext(MyContext)
15
+
16
+ useRef: holds mutable value that does not trigger re-render. Also used to access DOM elements.
17
+
18
+ Rules of Hooks:
19
+ 1. Only call hooks at the top level (not inside loops or conditions).
20
+ 2. Only call hooks from React function components or custom hooks.
data/docs/recursion.txt ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ RECURSION
2
+ =========
3
+ Recursion is when a function calls itself to solve a smaller version of the same problem.
4
+ Every recursive function needs:
5
+ 1. Base case — condition to stop recursion.
6
+ 2. Recursive case — function calls itself with smaller input.
7
+
8
+ --- Factorial ---
9
+ n! = n * (n-1) * ... * 1
10
+
11
+ def factorial(n):
12
+ if n == 0 or n == 1: # base case
13
+ return 1
14
+ return n * factorial(n - 1) # recursive case
15
+
16
+ print(factorial(5)) # 120
17
+ print(factorial(0)) # 1
18
+
19
+ # Iterative equivalent
20
+ def factorial_iter(n):
21
+ result = 1
22
+ for i in range(2, n + 1):
23
+ result *= i
24
+ return result
25
+
26
+ --- Fibonacci ---
27
+ fib(n) = fib(n-1) + fib(n-2), fib(0)=0, fib(1)=1
28
+
29
+ def fib(n):
30
+ if n <= 1:
31
+ return n
32
+ return fib(n-1) + fib(n-2)
33
+
34
+ print([fib(i) for i in range(8)]) # [0, 1, 1, 2, 3, 5, 8, 13]
35
+
36
+ --- Sum of Array ---
37
+ def array_sum(arr):
38
+ if not arr:
39
+ return 0
40
+ return arr[0] + array_sum(arr[1:])
41
+
42
+ print(array_sum([1, 2, 3, 4, 5])) # 15
43
+
44
+ --- Power Function ---
45
+ def power(base, exp):
46
+ if exp == 0:
47
+ return 1
48
+ if exp % 2 == 0:
49
+ half = power(base, exp // 2)
50
+ return half * half # O(log n) — fast power
51
+ return base * power(base, exp - 1)
52
+
53
+ print(power(2, 10)) # 1024
54
+ print(power(3, 5)) # 243
55
+
56
+ --- Reverse a String ---
57
+ def reverse_string(s):
58
+ if len(s) <= 1:
59
+ return s
60
+ return reverse_string(s[1:]) + s[0]
61
+
62
+ print(reverse_string("hello")) # "olleh"
63
+
64
+ --- Check Palindrome ---
65
+ def is_palindrome(s):
66
+ if len(s) <= 1:
67
+ return True
68
+ if s[0] != s[-1]:
69
+ return False
70
+ return is_palindrome(s[1:-1])
71
+
72
+ print(is_palindrome("racecar")) # True
73
+ print(is_palindrome("hello")) # False
74
+
75
+ --- Tower of Hanoi ---
76
+ Move n disks from source to destination using auxiliary peg.
77
+ Minimum moves = 2^n - 1.
78
+
79
+ def hanoi(n, source, destination, auxiliary):
80
+ if n == 1:
81
+ print(f"Move disk 1 from {source} to {destination}")
82
+ return
83
+ hanoi(n-1, source, auxiliary, destination)
84
+ print(f"Move disk {n} from {source} to {destination}")
85
+ hanoi(n-1, auxiliary, destination, source)
86
+
87
+ hanoi(3, 'A', 'C', 'B')
88
+ # Moves disk 1 from A to C
89
+ # Moves disk 2 from A to B
90
+ # Moves disk 1 from C to B
91
+ # ... (7 total moves)
92
+
93
+ --- Binary Search (Recursive) ---
94
+ def binary_search(arr, target, low, high):
95
+ if low > high:
96
+ return -1
97
+ mid = (low + high) // 2
98
+ if arr[mid] == target:
99
+ return mid
100
+ elif arr[mid] < target:
101
+ return binary_search(arr, target, mid + 1, high)
102
+ else:
103
+ return binary_search(arr, target, low, mid - 1)
104
+
105
+ arr = [1, 3, 5, 7, 9]
106
+ print(binary_search(arr, 7, 0, len(arr)-1)) # 3
107
+
108
+ --- Flatten Nested List ---
109
+ def flatten(lst):
110
+ result = []
111
+ for item in lst:
112
+ if isinstance(item, list):
113
+ result.extend(flatten(item))
114
+ else:
115
+ result.append(item)
116
+ return result
117
+
118
+ print(flatten([1, [2, [3, 4], 5], 6])) # [1, 2, 3, 4, 5, 6]
119
+
120
+ --- Generate All Subsets (Power Set) ---
121
+ def subsets(nums):
122
+ if not nums:
123
+ return [[]]
124
+ rest = subsets(nums[1:])
125
+ return rest + [[nums[0]] + s for s in rest]
126
+
127
+ print(subsets([1, 2, 3]))
128
+ # [[], [3], [2], [2,3], [1], [1,3], [1,2], [1,2,3]]
129
+
130
+ --- Generate Permutations ---
131
+ def permutations(nums):
132
+ if len(nums) <= 1:
133
+ return [nums]
134
+ result = []
135
+ for i, num in enumerate(nums):
136
+ rest = nums[:i] + nums[i+1:]
137
+ for perm in permutations(rest):
138
+ result.append([num] + perm)
139
+ return result
140
+
141
+ print(permutations([1, 2, 3]))
142
+ # [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
143
+
144
+ --- Recursion vs Iteration ---
145
+ Recursion: cleaner code, natural for tree/graph problems, risk of stack overflow.
146
+ Iteration: better performance, no stack overflow, sometimes harder to read.
147
+ Use recursion when the problem is naturally recursive (trees, divide-and-conquer).
148
+ Convert to iteration for very deep recursion using an explicit stack.
data/docs/rest_api.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ REST API (Representational State Transfer) is an architectural style for web services.
2
+ REST uses standard HTTP methods to perform operations on resources.
3
+
4
+ HTTP Methods:
5
+ - GET: retrieve/read resource. No request body.
6
+ - POST: create new resource. Data sent in request body.
7
+ - PUT: update existing resource completely.
8
+ - PATCH: partially update a resource.
9
+ - DELETE: remove a resource.
10
+
11
+ HTTP Status Codes:
12
+ - 200 OK: request succeeded.
13
+ - 201 Created: new resource created successfully.
14
+ - 400 Bad Request: invalid request from client.
15
+ - 401 Unauthorized: authentication required.
16
+ - 403 Forbidden: authenticated but not authorized.
17
+ - 404 Not Found: resource does not exist.
18
+ - 500 Internal Server Error: server-side error.
19
+
20
+ REST principles: Stateless, Client-Server, Cacheable, Uniform Interface.
21
+ JSON is the most common data format for REST API responses.
data/docs/sorting_algorithms.txt ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SORTING ALGORITHMS
2
+ ==================
3
+
4
+ --- Bubble Sort ---
5
+ Repeatedly compare adjacent elements and swap if out of order. Largest element bubbles to end each pass.
6
+ Time: O(n^2) average/worst, O(n) best (already sorted). Space: O(1). Stable.
7
+
8
+ def bubble_sort(arr):
9
+ n = len(arr)
10
+ for i in range(n):
11
+ swapped = False
12
+ for j in range(0, n - i - 1):
13
+ if arr[j] > arr[j + 1]:
14
+ arr[j], arr[j + 1] = arr[j + 1], arr[j]
15
+ swapped = True
16
+ if not swapped:
17
+ break # already sorted
18
+ return arr
19
+
20
+ print(bubble_sort([64, 34, 25, 12, 22, 11, 90]))
21
+ # Output: [11, 12, 22, 25, 34, 64, 90]
22
+
23
+ --- Selection Sort ---
24
+ Find the minimum element and place it at the beginning. Repeat for remaining array.
25
+ Time: O(n^2) all cases. Space: O(1). Not stable.
26
+
27
+ def selection_sort(arr):
28
+ n = len(arr)
29
+ for i in range(n):
30
+ min_idx = i
31
+ for j in range(i + 1, n):
32
+ if arr[j] < arr[min_idx]:
33
+ min_idx = j
34
+ arr[i], arr[min_idx] = arr[min_idx], arr[i]
35
+ return arr
36
+
37
+ print(selection_sort([64, 25, 12, 22, 11]))
38
+ # Output: [11, 12, 22, 25, 64]
39
+
40
+ --- Insertion Sort ---
41
+ Build sorted array one element at a time. Take each element and insert it into the correct position.
42
+ Time: O(n^2) worst, O(n) best. Space: O(1). Stable. Best for small or nearly sorted data.
43
+
44
+ def insertion_sort(arr):
45
+ for i in range(1, len(arr)):
46
+ key = arr[i]
47
+ j = i - 1
48
+ while j >= 0 and arr[j] > key:
49
+ arr[j + 1] = arr[j]
50
+ j -= 1
51
+ arr[j + 1] = key
52
+ return arr
53
+
54
+ print(insertion_sort([12, 11, 13, 5, 6]))
55
+ # Output: [5, 6, 11, 12, 13]
56
+
57
+ --- Merge Sort ---
58
+ Divide array in half, recursively sort both halves, then merge the sorted halves.
59
+ Time: O(n log n) all cases. Space: O(n). Stable. Best for linked lists and large datasets.
60
+
61
+ def merge_sort(arr):
62
+ if len(arr) <= 1:
63
+ return arr
64
+ mid = len(arr) // 2
65
+ left = merge_sort(arr[:mid])
66
+ right = merge_sort(arr[mid:])
67
+ return merge(left, right)
68
+
69
+ def merge(left, right):
70
+ result = []
71
+ i = j = 0
72
+ while i < len(left) and j < len(right):
73
+ if left[i] <= right[j]:
74
+ result.append(left[i]); i += 1
75
+ else:
76
+ result.append(right[j]); j += 1
77
+ result.extend(left[i:])
78
+ result.extend(right[j:])
79
+ return result
80
+
81
+ print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
82
+ # Output: [3, 9, 10, 27, 38, 43, 82]
83
+
84
+ --- Quick Sort ---
85
+ Pick a pivot, partition array so all elements less than pivot are on left, greater on right. Recurse.
86
+ Time: O(n log n) average, O(n^2) worst. Space: O(log n). Not stable. Fastest in practice.
87
+
88
+ def quick_sort(arr, low=0, high=None):
89
+ if high is None:
90
+ high = len(arr) - 1
91
+ if low < high:
92
+ pi = partition(arr, low, high)
93
+ quick_sort(arr, low, pi - 1)
94
+ quick_sort(arr, pi + 1, high)
95
+ return arr
96
+
97
+ def partition(arr, low, high):
98
+ pivot = arr[high]
99
+ i = low - 1
100
+ for j in range(low, high):
101
+ if arr[j] <= pivot:
102
+ i += 1
103
+ arr[i], arr[j] = arr[j], arr[i]
104
+ arr[i + 1], arr[high] = arr[high], arr[i + 1]
105
+ return i + 1
106
+
107
+ print(quick_sort([10, 7, 8, 9, 1, 5]))
108
+ # Output: [1, 5, 7, 8, 9, 10]
109
+
110
+ --- Heap Sort ---
111
+ Build a max-heap from the array. Repeatedly extract the maximum and place at end.
112
+ Time: O(n log n) all cases. Space: O(1). Not stable.
113
+
114
+ def heap_sort(arr):
115
+ n = len(arr)
116
+ for i in range(n // 2 - 1, -1, -1):
117
+ heapify(arr, n, i)
118
+ for i in range(n - 1, 0, -1):
119
+ arr[0], arr[i] = arr[i], arr[0]
120
+ heapify(arr, i, 0)
121
+ return arr
122
+
123
+ def heapify(arr, n, i):
124
+ largest = i
125
+ left = 2 * i + 1
126
+ right = 2 * i + 2
127
+ if left < n and arr[left] > arr[largest]:
128
+ largest = left
129
+ if right < n and arr[right] > arr[largest]:
130
+ largest = right
131
+ if largest != i:
132
+ arr[i], arr[largest] = arr[largest], arr[i]
133
+ heapify(arr, n, largest)
134
+
135
+ print(heap_sort([12, 11, 13, 5, 6, 7]))
136
+ # Output: [5, 6, 7, 11, 12, 13]
137
+
138
+ --- Counting Sort ---
139
+ Count occurrences of each element. Works only on non-negative integers within a known range.
140
+ Time: O(n + k) where k is the range. Space: O(k). Stable.
141
+
142
+ def counting_sort(arr):
143
+ if not arr:
144
+ return arr
145
+ max_val = max(arr)
146
+ count = [0] * (max_val + 1)
147
+ for num in arr:
148
+ count[num] += 1
149
+ result = []
150
+ for i, c in enumerate(count):
151
+ result.extend([i] * c)
152
+ return result
153
+
154
+ print(counting_sort([4, 2, 2, 8, 3, 3, 1]))
155
+ # Output: [1, 2, 2, 3, 3, 4, 8]
156
+
157
+ --- Comparison Summary ---
158
+ Algorithm | Best | Average | Worst | Space | Stable
159
+ Bubble Sort | O(n) | O(n^2) | O(n^2) | O(1) | Yes
160
+ Selection Sort| O(n^2) | O(n^2) | O(n^2) | O(1) | No
161
+ Insertion Sort| O(n) | O(n^2) | O(n^2) | O(1) | Yes
162
+ Merge Sort | O(n log n)| O(n log n)| O(n log n)| O(n) | Yes
163
+ Quick Sort | O(n log n)| O(n log n)| O(n^2) | O(log n)| No
164
+ Heap Sort | O(n log n)| O(n log n)| O(n log n)| O(1) | No
165
+ Counting Sort | O(n+k) | O(n+k) | O(n+k) | O(k) | Yes
data/docs/stack_queue.txt ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ STACK AND QUEUE
2
+ ===============
3
+
4
+ --- STACK ---
5
+ LIFO — Last In First Out. Think of a stack of plates.
6
+ Operations: push (add), pop (remove top), peek (view top). All O(1).
7
+
8
+ Uses: undo/redo, function call stack, expression evaluation, DFS, balanced brackets.
9
+
10
+ # Stack using Python list
11
+ class Stack:
12
+ def __init__(self):
13
+ self.items = []
14
+
15
+ def push(self, item):
16
+ self.items.append(item)
17
+
18
+ def pop(self):
19
+ if self.is_empty():
20
+ raise IndexError("Stack is empty")
21
+ return self.items.pop()
22
+
23
+ def peek(self):
24
+ if self.is_empty():
25
+ raise IndexError("Stack is empty")
26
+ return self.items[-1]
27
+
28
+ def is_empty(self):
29
+ return len(self.items) == 0
30
+
31
+ def size(self):
32
+ return len(self.items)
33
+
34
+ s = Stack()
35
+ s.push(10)
36
+ s.push(20)
37
+ s.push(30)
38
+ print(s.peek()) # 30
39
+ print(s.pop()) # 30
40
+ print(s.size()) # 2
41
+
42
+ --- Balanced Brackets using Stack ---
43
+ Check if brackets (, ), {, }, [, ] are balanced.
44
+
45
+ def is_balanced(expr):
46
+ stack = []
47
+ pairs = {')': '(', '}': '{', ']': '['}
48
+ for ch in expr:
49
+ if ch in '({[':
50
+ stack.append(ch)
51
+ elif ch in ')}]':
52
+ if not stack or stack[-1] != pairs[ch]:
53
+ return False
54
+ stack.pop()
55
+ return len(stack) == 0
56
+
57
+ print(is_balanced("({[]})")) # True
58
+ print(is_balanced("({[})")) # False
59
+ print(is_balanced("((()))")) # True
60
+
61
+ --- Next Greater Element using Stack ---
62
+ For each element, find the next element greater than it.
63
+ Time: O(n). Space: O(n).
64
+
65
+ def next_greater(arr):
66
+ n = len(arr)
67
+ result = [-1] * n
68
+ stack = [] # stores indices
69
+ for i in range(n):
70
+ while stack and arr[stack[-1]] < arr[i]:
71
+ result[stack.pop()] = arr[i]
72
+ stack.append(i)
73
+ return result
74
+
75
+ print(next_greater([4, 5, 2, 25])) # [5, 25, 25, -1]
76
+
77
+ --- Evaluate Postfix Expression ---
78
+ Postfix: operands come before operator (e.g. "2 3 +" = 5).
79
+
80
+ def evaluate_postfix(expression):
81
+ stack = []
82
+ for token in expression.split():
83
+ if token.lstrip('-').isdigit():
84
+ stack.append(int(token))
85
+ else:
86
+ b = stack.pop()
87
+ a = stack.pop()
88
+ if token == '+': stack.append(a + b)
89
+ elif token == '-': stack.append(a - b)
90
+ elif token == '*': stack.append(a * b)
91
+ elif token == '/': stack.append(int(a / b))
92
+ return stack[0]
93
+
94
+ print(evaluate_postfix("2 3 1 * + 9 -")) # Output: -4
95
+
96
+ --- Min Stack ---
97
+ Stack that supports getMin() in O(1).
98
+
99
+ class MinStack:
100
+ def __init__(self):
101
+ self.stack = []
102
+ self.min_stack = []
103
+
104
+ def push(self, val):
105
+ self.stack.append(val)
106
+ if not self.min_stack or val <= self.min_stack[-1]:
107
+ self.min_stack.append(val)
108
+
109
+ def pop(self):
110
+ val = self.stack.pop()
111
+ if val == self.min_stack[-1]:
112
+ self.min_stack.pop()
113
+
114
+ def get_min(self):
115
+ return self.min_stack[-1]
116
+
117
+ ms = MinStack()
118
+ ms.push(5); ms.push(3); ms.push(7); ms.push(2)
119
+ print(ms.get_min()) # 2
120
+ ms.pop()
121
+ print(ms.get_min()) # 3
122
+
123
+ --- QUEUE ---
124
+ FIFO — First In First Out. Think of a line at a ticket counter.
125
+ Operations: enqueue (add rear), dequeue (remove front), peek. All O(1).
126
+
127
+ Uses: BFS, scheduling, print queues, sliding window.
128
+
129
+ from collections import deque
130
+
131
+ class Queue:
132
+ def __init__(self):
133
+ self.items = deque()
134
+
135
+ def enqueue(self, item):
136
+ self.items.append(item)
137
+
138
+ def dequeue(self):
139
+ if self.is_empty():
140
+ raise IndexError("Queue is empty")
141
+ return self.items.popleft()
142
+
143
+ def peek(self):
144
+ return self.items[0]
145
+
146
+ def is_empty(self):
147
+ return len(self.items) == 0
148
+
149
+ def size(self):
150
+ return len(self.items)
151
+
152
+ q = Queue()
153
+ q.enqueue(10)
154
+ q.enqueue(20)
155
+ q.enqueue(30)
156
+ print(q.dequeue()) # 10
157
+ print(q.peek()) # 20
158
+
159
+ --- Circular Queue ---
160
+ Fixed-size queue where rear wraps around to front.
161
+
162
+ class CircularQueue:
163
+ def __init__(self, capacity):
164
+ self.queue = [None] * capacity
165
+ self.front = self.rear = -1
166
+ self.capacity = capacity
167
+ self.size = 0
168
+
169
+ def enqueue(self, item):
170
+ if self.size == self.capacity:
171
+ raise OverflowError("Queue is full")
172
+ self.rear = (self.rear + 1) % self.capacity
173
+ self.queue[self.rear] = item
174
+ if self.front == -1:
175
+ self.front = 0
176
+ self.size += 1
177
+
178
+ def dequeue(self):
179
+ if self.size == 0:
180
+ raise IndexError("Queue is empty")
181
+ item = self.queue[self.front]
182
+ self.front = (self.front + 1) % self.capacity
183
+ self.size -= 1
184
+ return item
185
+
186
+ --- Deque (Double-Ended Queue) ---
187
+ Insert and delete from both ends in O(1).
188
+
189
+ from collections import deque
190
+ d = deque()
191
+ d.appendleft(1) # add to front
192
+ d.append(2) # add to rear
193
+ d.appendleft(0)
194
+ print(d) # deque([0, 1, 2])
195
+ d.popleft() # remove from front → 0
196
+ d.pop() # remove from rear → 2
197
+ print(d) # deque([1])
198
+
199
+ --- Priority Queue ---
200
+ Elements are dequeued based on priority, not insertion order.
201
+ Python's heapq is a min-heap by default.
202
+
203
+ import heapq
204
+
205
+ pq = []
206
+ heapq.heappush(pq, (3, 'low'))
207
+ heapq.heappush(pq, (1, 'high'))
208
+ heapq.heappush(pq, (2, 'medium'))
209
+ print(heapq.heappop(pq)) # (1, 'high')
210
+ print(heapq.heappop(pq)) # (2, 'medium')
211
+
212
+ # Max-heap: negate priorities
213
+ heapq.heappush(pq, (-5, 'top priority'))
214
+ print(heapq.heappop(pq)) # (-5, 'top priority')
215
+
216
+ --- Difference: Stack vs Queue ---
217
+ Stack: LIFO — last added is first removed. Uses: DFS, backtracking, undo.
218
+ Queue: FIFO — first added is first removed. Uses: BFS, scheduling, buffering.
data/docs/string_algorithms.txt ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ STRING ALGORITHMS
2
+ =================
3
+ Strings are sequences of characters. Most string operations in Python run in O(n).
4
+
5
+ --- Basic String Operations ---
6
+ s = "hello world"
7
+ print(len(s)) # 11
8
+ print(s.upper()) # HELLO WORLD
9
+ print(s.lower()) # hello world
10
+ print(s.split()) # ['hello', 'world']
11
+ print(s.replace("world", "Python")) # hello Python
12
+ print(s.startswith("hello")) # True
13
+ print(s.endswith("world")) # True
14
+ print(s.find("world")) # 6 (index)
15
+ print(s.count("l")) # 3
16
+ print(s[::-1]) # dlrow olleh (reverse)
17
+ print("".join(reversed(s))) # same as above
18
+
19
+ --- Reverse Words in a Sentence ---
20
+ def reverse_words(sentence):
21
+ return ' '.join(sentence.split()[::-1])
22
+
23
+ print(reverse_words("Hello World Python")) # Python World Hello
24
+
25
+ --- Check Palindrome ---
26
+ def is_palindrome(s):
27
+ s = ''.join(c.lower() for c in s if c.isalnum())
28
+ return s == s[::-1]
29
+
30
+ print(is_palindrome("A man a plan a canal Panama")) # True
31
+ print(is_palindrome("race a car")) # False
32
+
33
+ --- Anagram Check ---
34
+ Two strings are anagrams if they have same characters with same frequency.
35
+
36
+ from collections import Counter
37
+
38
+ def is_anagram(s, t):
39
+ return Counter(s) == Counter(t)
40
+
41
+ print(is_anagram("listen", "silent")) # True
42
+ print(is_anagram("hello", "world")) # False
43
+
44
+ --- Count Character Frequency ---
45
+ def char_frequency(s):
46
+ freq = {}
47
+ for ch in s:
48
+ freq[ch] = freq.get(ch, 0) + 1
49
+ return freq
50
+
51
+ print(char_frequency("abracadabra"))
52
+ # {'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
53
+
54
+ --- KMP String Matching ---
55
+ Find all occurrences of pattern in text.
56
+ Time: O(n + m). Space: O(m). Efficient, no unnecessary comparisons.
57
+
58
+ def kmp_search(text, pattern):
59
+ n, m = len(text), len(pattern)
60
+ if m == 0:
61
+ return [0]
62
+
63
+ # Build failure function
64
+ fail = [0] * m
65
+ j = 0
66
+ for i in range(1, m):
67
+ while j > 0 and pattern[i] != pattern[j]:
68
+ j = fail[j - 1]
69
+ if pattern[i] == pattern[j]:
70
+ j += 1
71
+ fail[i] = j
72
+
73
+ # Search
74
+ matches = []
75
+ j = 0
76
+ for i in range(n):
77
+ while j > 0 and text[i] != pattern[j]:
78
+ j = fail[j - 1]
79
+ if text[i] == pattern[j]:
80
+ j += 1
81
+ if j == m:
82
+ matches.append(i - m + 1)
83
+ j = fail[j - 1]
84
+ return matches
85
+
86
+ print(kmp_search("aabaacaadaabaaba", "aaba")) # [0, 9, 12]
87
+
88
+ --- Rabin-Karp Rolling Hash ---
89
+ Find pattern in text using hashing. Avg O(n+m), worst O(nm).
90
+
91
+ def rabin_karp(text, pattern):
92
+ n, m = len(text), len(pattern)
93
+ BASE, MOD = 256, 101
94
+ pat_hash = text_hash = 0
95
+ h = pow(BASE, m - 1, MOD)
96
+ matches = []
97
+
98
+ for i in range(m):
99
+ pat_hash = (BASE * pat_hash + ord(pattern[i])) % MOD
100
+ text_hash = (BASE * text_hash + ord(text[i])) % MOD
101
+
102
+ for i in range(n - m + 1):
103
+ if pat_hash == text_hash:
104
+ if text[i:i+m] == pattern: # verify
105
+ matches.append(i)
106
+ if i < n - m:
107
+ text_hash = (BASE*(text_hash - ord(text[i])*h) + ord(text[i+m])) % MOD
108
+ if text_hash < 0:
109
+ text_hash += MOD
110
+ return matches
111
+
112
+ print(rabin_karp("GEEKS FOR GEEKS", "GEEKS")) # [0, 10]
113
+
114
+ --- Longest Common Prefix ---
115
+ def longest_common_prefix(strs):
116
+ if not strs:
117
+ return ""
118
+ prefix = strs[0]
119
+ for s in strs[1:]:
120
+ while not s.startswith(prefix):
121
+ prefix = prefix[:-1]
122
+ if not prefix:
123
+ return ""
124
+ return prefix
125
+
126
+ print(longest_common_prefix(["flower","flow","flight"])) # "fl"
127
+ print(longest_common_prefix(["dog","racecar","car"])) # ""
128
+
129
+ --- Valid Parentheses ---
130
+ def is_valid_parentheses(s):
131
+ stack = []
132
+ pairs = {')':'(', '}':'{', ']':'['}
133
+ for ch in s:
134
+ if ch in '({[':
135
+ stack.append(ch)
136
+ elif ch in ')}]':
137
+ if not stack or stack[-1] != pairs[ch]:
138
+ return False
139
+ stack.pop()
140
+ return not stack
141
+
142
+ print(is_valid_parentheses("()[]{}")) # True
143
+ print(is_valid_parentheses("([)]")) # False
144
+
145
+ --- Roman to Integer ---
146
+ def roman_to_int(s):
147
+ values = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
148
+ result = 0
149
+ for i in range(len(s)):
150
+ if i + 1 < len(s) and values[s[i]] < values[s[i+1]]:
151
+ result -= values[s[i]]
152
+ else:
153
+ result += values[s[i]]
154
+ return result
155
+
156
+ print(roman_to_int("III")) # 3
157
+ print(roman_to_int("LVIII")) # 58
158
+ print(roman_to_int("MCMXCIV"))# 1994
159
+
160
+ --- String Compression ---
161
+ Compress "aabcccdddd" to "a2b1c3d4".
162
+
163
+ def compress(s):
164
+ result = []
165
+ i = 0
166
+ while i < len(s):
167
+ ch = s[i]
168
+ count = 0
169
+ while i < len(s) and s[i] == ch:
170
+ count += 1
171
+ i += 1
172
+ result.append(ch + str(count))
173
+ compressed = ''.join(result)
174
+ return compressed if len(compressed) < len(s) else s
175
+
176
+ print(compress("aabcccdddd")) # a2b1c3d4
177
+ print(compress("abcd")) # abcd (no gain)
178
+
179
+ --- Zigzag String Conversion ---
180
+ Convert string to zigzag pattern across rows.
181
+
182
+ def convert_zigzag(s, numRows):
183
+ if numRows == 1 or numRows >= len(s):
184
+ return s
185
+ rows = [''] * numRows
186
+ row, step = 0, 1
187
+ for ch in s:
188
+ rows[row] += ch
189
+ if row == 0: step = 1
190
+ if row == numRows - 1: step = -1
191
+ row += step
192
+ return ''.join(rows)
193
+
194
+ print(convert_zigzag("PAYPALISHIRING", 3)) # PAHNAPLSIIGYIR
data/docs/trees.txt ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TREES AND BINARY TREES
2
+ ======================
3
+ A tree is a hierarchical data structure. Root is the topmost node.
4
+ Each node has at most one parent and zero or more children.
5
+ Leaf nodes have no children.
6
+
7
+ --- Binary Tree Node ---
8
+ class TreeNode:
9
+ def __init__(self, val=0, left=None, right=None):
10
+ self.val = val
11
+ self.left = left
12
+ self.right = right
13
+
14
+ # Build a sample tree:
15
+ # 1
16
+ # / \
17
+ # 2 3
18
+ # / \ \
19
+ # 4 5 6
20
+ root = TreeNode(1)
21
+ root.left = TreeNode(2)
22
+ root.right = TreeNode(3)
23
+ root.left.left = TreeNode(4)
24
+ root.left.right = TreeNode(5)
25
+ root.right.right = TreeNode(6)
26
+
27
+ --- Tree Traversals ---
28
+ # Inorder (Left, Root, Right) — gives sorted output for BST
29
+ def inorder(root):
30
+ if not root:
31
+ return []
32
+ return inorder(root.left) + [root.val] + inorder(root.right)
33
+
34
+ # Preorder (Root, Left, Right) — used to copy the tree
35
+ def preorder(root):
36
+ if not root:
37
+ return []
38
+ return [root.val] + preorder(root.left) + preorder(root.right)
39
+
40
+ # Postorder (Left, Right, Root) — used to delete the tree
41
+ def postorder(root):
42
+ if not root:
43
+ return []
44
+ return postorder(root.left) + postorder(root.right) + [root.val]
45
+
46
+ # Level Order (BFS)
47
+ from collections import deque
48
+ def level_order(root):
49
+ if not root:
50
+ return []
51
+ result, queue = [], deque([root])
52
+ while queue:
53
+ node = queue.popleft()
54
+ result.append(node.val)
55
+ if node.left: queue.append(node.left)
56
+ if node.right: queue.append(node.right)
57
+ return result
58
+
59
+ print(inorder(root)) # [4, 2, 5, 1, 3, 6]
60
+ print(preorder(root)) # [1, 2, 4, 5, 3, 6]
61
+ print(postorder(root)) # [4, 5, 2, 6, 3, 1]
62
+ print(level_order(root)) # [1, 2, 3, 4, 5, 6]
63
+
64
+ --- Tree Height ---
65
+ def height(root):
66
+ if not root:
67
+ return 0
68
+ return 1 + max(height(root.left), height(root.right))
69
+
70
+ print(height(root)) # Output: 3
71
+
72
+ --- Count Nodes ---
73
+ def count_nodes(root):
74
+ if not root:
75
+ return 0
76
+ return 1 + count_nodes(root.left) + count_nodes(root.right)
77
+
78
+ --- Binary Search Tree (BST) ---
79
+ Left child < Parent < Right child.
80
+ Insert, Search, Delete: O(log n) average, O(n) worst (skewed).
81
+
82
+ # Insert into BST
83
+ def bst_insert(root, val):
84
+ if not root:
85
+ return TreeNode(val)
86
+ if val < root.val:
87
+ root.left = bst_insert(root.left, val)
88
+ else:
89
+ root.right = bst_insert(root.right, val)
90
+ return root
91
+
92
+ # Search in BST
93
+ def bst_search(root, val):
94
+ if not root or root.val == val:
95
+ return root
96
+ if val < root.val:
97
+ return bst_search(root.left, val)
98
+ return bst_search(root.right, val)
99
+
100
+ # Delete from BST
101
+ def bst_delete(root, val):
102
+ if not root:
103
+ return None
104
+ if val < root.val:
105
+ root.left = bst_delete(root.left, val)
106
+ elif val > root.val:
107
+ root.right = bst_delete(root.right, val)
108
+ else:
109
+ if not root.left:
110
+ return root.right
111
+ if not root.right:
112
+ return root.left
113
+ # Find inorder successor (smallest in right subtree)
114
+ curr = root.right
115
+ while curr.left:
116
+ curr = curr.left
117
+ root.val = curr.val
118
+ root.right = bst_delete(root.right, curr.val)
119
+ return root
120
+
121
+ bst = None
122
+ for v in [5, 3, 7, 1, 4, 6, 8]:
123
+ bst = bst_insert(bst, v)
124
+ print(inorder(bst)) # [1, 3, 4, 5, 6, 7, 8]
125
+
126
+ --- Check if Balanced ---
127
+ A tree is balanced if height difference of left and right subtrees is at most 1 for every node.
128
+
129
+ def is_balanced(root):
130
+ def check(node):
131
+ if not node:
132
+ return 0
133
+ left = check(node.left)
134
+ right = check(node.right)
135
+ if left == -1 or right == -1 or abs(left - right) > 1:
136
+ return -1
137
+ return 1 + max(left, right)
138
+ return check(root) != -1
139
+
140
+ --- Lowest Common Ancestor (LCA) ---
141
+ Find the lowest node that has both p and q as descendants.
142
+
143
+ def lca(root, p, q):
144
+ if not root or root == p or root == q:
145
+ return root
146
+ left = lca(root.left, p, q)
147
+ right = lca(root.right, p, q)
148
+ if left and right:
149
+ return root # p and q on different sides
150
+ return left or right
151
+
152
+ --- Level Order by Levels ---
153
+ Return nodes grouped by level.
154
+
155
+ def level_order_levels(root):
156
+ if not root:
157
+ return []
158
+ result, queue = [], deque([root])
159
+ while queue:
160
+ level = []
161
+ for _ in range(len(queue)):
162
+ node = queue.popleft()
163
+ level.append(node.val)
164
+ if node.left: queue.append(node.left)
165
+ if node.right: queue.append(node.right)
166
+ result.append(level)
167
+ return result
168
+
169
+ print(level_order_levels(root))
170
+ # Output: [[1], [2, 3], [4, 5, 6]]
171
+
172
+ --- Diameter of Binary Tree ---
173
+ Longest path between any two nodes (path may not pass through root).
174
+
175
+ def diameter(root):
176
+ max_d = [0]
177
+ def depth(node):
178
+ if not node:
179
+ return 0
180
+ left = depth(node.left)
181
+ right = depth(node.right)
182
+ max_d[0] = max(max_d[0], left + right)
183
+ return 1 + max(left, right)
184
+ depth(root)
185
+ return max_d[0]
186
+
187
+ --- Invert Binary Tree ---
188
+ Mirror the tree — swap left and right children of every node.
189
+
190
+ def invert_tree(root):
191
+ if not root:
192
+ return None
193
+ root.left, root.right = invert_tree(root.right), invert_tree(root.left)
194
+ return root
195
+
196
+ --- AVL Tree (Self-Balancing BST) ---
197
+ Keeps height O(log n) by rotating after every insert/delete.
198
+ Balance factor = height(left) - height(right). Must be -1, 0, or 1.
199
+
200
+ def get_height(node):
201
+ return node.height if node else 0
202
+
203
+ def get_balance(node):
204
+ return get_height(node.left) - get_height(node.right) if node else 0
205
+
206
+ # Right rotation
207
+ def rotate_right(y):
208
+ x = y.left
209
+ T2 = x.right
210
+ x.right = y
211
+ y.left = T2
212
+ y.height = 1 + max(get_height(y.left), get_height(y.right))
213
+ x.height = 1 + max(get_height(x.left), get_height(x.right))
214
+ return x
215
+
216
+ # Left rotation
217
+ def rotate_left(x):
218
+ y = x.right
219
+ T2 = y.left
220
+ y.left = x
221
+ x.right = T2
222
+ x.height = 1 + max(get_height(x.left), get_height(x.right))
223
+ y.height = 1 + max(get_height(y.left), get_height(y.right))
224
+ return y
data/docs/two_pointers.txt ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TWO POINTERS AND SLIDING WINDOW
2
+ ================================
3
+
4
+ --- TWO POINTERS TECHNIQUE ---
5
+ Use two pointers (left and right) to scan an array from both ends or at different speeds.
6
+ Reduces O(n^2) brute force to O(n).
7
+
8
+ --- Two Sum (Sorted Array) ---
9
+ Find two numbers that add up to target in a sorted array.
10
+ Time: O(n). Space: O(1).
11
+
12
+ def two_sum_sorted(arr, target):
13
+ left, right = 0, len(arr) - 1
14
+ while left < right:
15
+ total = arr[left] + arr[right]
16
+ if total == target:
17
+ return [left, right]
18
+ elif total < target:
19
+ left += 1
20
+ else:
21
+ right -= 1
22
+ return []
23
+
24
+ print(two_sum_sorted([1, 2, 3, 4, 6], 6)) # [1, 3] (2+4)
25
+
26
+ --- Reverse an Array In-Place ---
27
+ def reverse_array(arr):
28
+ left, right = 0, len(arr) - 1
29
+ while left < right:
30
+ arr[left], arr[right] = arr[right], arr[left]
31
+ left += 1
32
+ right -= 1
33
+ return arr
34
+
35
+ print(reverse_array([1, 2, 3, 4, 5])) # [5, 4, 3, 2, 1]
36
+
37
+ --- Remove Duplicates from Sorted Array ---
38
+ Time: O(n). Space: O(1).
39
+
40
+ def remove_duplicates(arr):
41
+ if not arr:
42
+ return 0
43
+ slow = 0
44
+ for fast in range(1, len(arr)):
45
+ if arr[fast] != arr[slow]:
46
+ slow += 1
47
+ arr[slow] = arr[fast]
48
+ return slow + 1 # length of unique part
49
+
50
+ arr = [1, 1, 2, 2, 3, 4, 4]
51
+ k = remove_duplicates(arr)
52
+ print(arr[:k]) # [1, 2, 3, 4]
53
+
54
+ --- Container With Most Water ---
55
+ Find two lines that form container holding most water.
56
+ Time: O(n). Space: O(1).
57
+
58
+ def max_water(heights):
59
+ left, right = 0, len(heights) - 1
60
+ max_area = 0
61
+ while left < right:
62
+ area = min(heights[left], heights[right]) * (right - left)
63
+ max_area = max(max_area, area)
64
+ if heights[left] < heights[right]:
65
+ left += 1
66
+ else:
67
+ right -= 1
68
+ return max_area
69
+
70
+ print(max_water([1,8,6,2,5,4,8,3,7])) # Output: 49
71
+
72
+ --- Three Sum ---
73
+ Find all triplets that sum to zero.
74
+ Time: O(n^2). Space: O(n).
75
+
76
+ def three_sum(nums):
77
+ nums.sort()
78
+ result = []
79
+ for i in range(len(nums) - 2):
80
+ if i > 0 and nums[i] == nums[i-1]:
81
+ continue # skip duplicates
82
+ left, right = i + 1, len(nums) - 1
83
+ while left < right:
84
+ total = nums[i] + nums[left] + nums[right]
85
+ if total == 0:
86
+ result.append([nums[i], nums[left], nums[right]])
87
+ while left < right and nums[left] == nums[left+1]: left += 1
88
+ while left < right and nums[right] == nums[right-1]: right -= 1
89
+ left += 1
90
+ right -= 1
91
+ elif total < 0:
92
+ left += 1
93
+ else:
94
+ right -= 1
95
+ return result
96
+
97
+ print(three_sum([-1, 0, 1, 2, -1, -4]))
98
+ # [[-1, -1, 2], [-1, 0, 1]]
99
+
100
+ --- Trapping Rain Water ---
101
+ Calculate water trapped between bars.
102
+ Time: O(n). Space: O(1).
103
+
104
+ def trap_water(height):
105
+ left, right = 0, len(height) - 1
106
+ left_max = right_max = 0
107
+ water = 0
108
+ while left < right:
109
+ if height[left] < height[right]:
110
+ if height[left] >= left_max:
111
+ left_max = height[left]
112
+ else:
113
+ water += left_max - height[left]
114
+ left += 1
115
+ else:
116
+ if height[right] >= right_max:
117
+ right_max = height[right]
118
+ else:
119
+ water += right_max - height[right]
120
+ right -= 1
121
+ return water
122
+
123
+ print(trap_water([0,1,0,2,1,0,1,3,2,1,2,1])) # Output: 6
124
+
125
+ --- Slow/Fast Pointer: Detect Cycle ---
126
+ def has_cycle(head):
127
+ slow = fast = head
128
+ while fast and fast.next:
129
+ slow = slow.next
130
+ fast = fast.next.next
131
+ if slow == fast:
132
+ return True
133
+ return False
134
+
135
+ --- SLIDING WINDOW TECHNIQUE ---
136
+ Maintain a window of fixed or variable size. Slide it across array.
137
+ Avoids recomputing the entire window each step.
138
+
139
+ --- Maximum Sum Subarray of Size K (Fixed Window) ---
140
+ Time: O(n). Space: O(1).
141
+
142
+ def max_sum_subarray(arr, k):
143
+ window_sum = sum(arr[:k])
144
+ max_sum = window_sum
145
+ for i in range(k, len(arr)):
146
+ window_sum += arr[i] - arr[i - k]
147
+ max_sum = max(max_sum, window_sum)
148
+ return max_sum
149
+
150
+ print(max_sum_subarray([2,1,5,1,3,2], 3)) # Output: 9 (5+1+3)
151
+
152
+ --- Longest Substring Without Repeating Characters (Variable Window) ---
153
+ Time: O(n). Space: O(k) where k = charset size.
154
+
155
+ def length_of_longest_substring(s):
156
+ char_index = {}
157
+ left = 0
158
+ max_len = 0
159
+ for right, ch in enumerate(s):
160
+ if ch in char_index and char_index[ch] >= left:
161
+ left = char_index[ch] + 1
162
+ char_index[ch] = right
163
+ max_len = max(max_len, right - left + 1)
164
+ return max_len
165
+
166
+ print(length_of_longest_substring("abcabcbb")) # 3 (abc)
167
+ print(length_of_longest_substring("pwwkew")) # 3 (wke)
168
+
169
+ --- Minimum Size Subarray Sum ---
170
+ Find smallest subarray with sum >= target.
171
+ Time: O(n). Space: O(1).
172
+
173
+ def min_subarray_len(target, nums):
174
+ left = 0
175
+ curr_sum = 0
176
+ min_len = float('inf')
177
+ for right in range(len(nums)):
178
+ curr_sum += nums[right]
179
+ while curr_sum >= target:
180
+ min_len = min(min_len, right - left + 1)
181
+ curr_sum -= nums[left]
182
+ left += 1
183
+ return min_len if min_len != float('inf') else 0
184
+
185
+ print(min_subarray_len(7, [2,3,1,2,4,3])) # Output: 2 (4+3)
186
+
187
+ --- Longest Substring with K Distinct Characters ---
188
+ Time: O(n). Space: O(k).
189
+
190
+ def longest_k_distinct(s, k):
191
+ from collections import defaultdict
192
+ char_count = defaultdict(int)
193
+ left = max_len = 0
194
+ for right, ch in enumerate(s):
195
+ char_count[ch] += 1
196
+ while len(char_count) > k:
197
+ char_count[s[left]] -= 1
198
+ if char_count[s[left]] == 0:
199
+ del char_count[s[left]]
200
+ left += 1
201
+ max_len = max(max_len, right - left + 1)
202
+ return max_len
203
+
204
+ print(longest_k_distinct("araaci", 2)) # Output: 4 (araa)
data/faiss_index/index.faiss ADDED
Binary file (44.6 kB). View file
 
data/faiss_index/index.pkl ADDED
Binary file (11.7 kB). View file
 
data/finetune_data.jsonl ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"instruction": "What is binary search?", "input": "", "output": "Binary search is an efficient searching algorithm for sorted arrays. It works by repeatedly dividing the search interval in half. Time complexity: O(log n), Space complexity: O(1). Requires the array to be sorted beforehand."}
2
+ {"instruction": "What is the time complexity of binary search?", "input": "", "output": "Binary search has a time complexity of O(log n) because it halves the search space with each step. Best case is O(1) when the middle element is the target. Space complexity is O(1) for iterative implementation."}
3
+ {"instruction": "What is the difference between stack and queue?", "input": "", "output": "Stack uses LIFO (Last In First Out) - the last element added is the first to be removed. Operations: push and pop. Queue uses FIFO (First In First Out) - the first element added is the first to be removed. Operations: enqueue and dequeue. Both have O(1) operations."}
4
+ {"instruction": "Explain merge sort.", "input": "", "output": "Merge sort is a divide-and-conquer sorting algorithm. It splits the array in half, recursively sorts each half, then merges the two sorted halves. Time complexity: O(n log n) in all cases. Space complexity: O(n). It is a stable sorting algorithm, making it good for sorting linked lists."}
5
+ {"instruction": "What is quicksort?", "input": "", "output": "Quicksort is a divide-and-conquer algorithm that picks a pivot element and partitions the array into elements less than and greater than the pivot, then recursively sorts each partition. Average time: O(n log n), Worst case: O(n^2) when pivot is always smallest or largest element. Space: O(log n)."}
6
+ {"instruction": "What is a linked list?", "input": "", "output": "A linked list is a linear data structure where elements are stored in nodes. Each node contains data and a pointer to the next node. Types: Singly (one direction), Doubly (both directions), Circular (last points to first). Advantage: dynamic size, O(1) insertion at head. Disadvantage: no random access, O(n) search."}
7
+ {"instruction": "What is a Binary Search Tree?", "input": "", "output": "A Binary Search Tree (BST) is a binary tree where for every node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater. This property enables O(log n) average search, insert, and delete operations. Worst case is O(n) for unbalanced trees."}
8
+ {"instruction": "What is dynamic programming?", "input": "", "output": "Dynamic programming solves complex problems by breaking them into overlapping subproblems and storing results to avoid recomputation. Two approaches: Memoization (top-down, recursion with cache) and Tabulation (bottom-up, iterative). Requires optimal substructure and overlapping subproblems. Examples: Fibonacci, Knapsack, LCS, Coin Change."}
9
+ {"instruction": "What are React hooks?", "input": "", "output": "React hooks are functions that let you use state and lifecycle features in functional components, introduced in React 16.8. Key hooks: useState for state management, useEffect for side effects like API calls, useContext for consuming context, useRef for DOM references. Rules: only call hooks at top level, only in React functions."}
10
+ {"instruction": "What is useState in React?", "input": "", "output": "useState is a React hook for managing local component state. Syntax: const [state, setState] = useState(initialValue). It returns the current state value and a setter function. Calling setState triggers a re-render with the new value. Example: const [count, setCount] = useState(0) then setCount(count + 1) to increment."}
11
+ {"instruction": "What is useEffect in React?", "input": "", "output": "useEffect is a React hook for handling side effects like API calls, subscriptions, and DOM updates. It runs after every render by default. The dependency array controls when it runs: empty array [] runs only on mount, [value] runs when value changes. Return a cleanup function to run on unmount. Syntax: useEffect(() => { effect }, [deps])."}
12
+ {"instruction": "What is a REST API?", "input": "", "output": "REST API (Representational State Transfer) is an architectural style for web services using standard HTTP methods. GET retrieves resources, POST creates new resources, PUT updates resources completely, PATCH partially updates, DELETE removes resources. REST is stateless meaning each request contains all needed information. JSON is the most common response format."}
13
+ {"instruction": "What HTTP methods does REST use?", "input": "", "output": "REST uses five main HTTP methods: GET for reading/retrieving data, POST for creating new resources, PUT for completely updating an existing resource, PATCH for partially updating a resource, and DELETE for removing a resource. These map to CRUD operations: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE)."}
14
+ {"instruction": "What does HTTP status code 404 mean?", "input": "", "output": "HTTP status code 404 means Not Found. It indicates the server cannot find the requested resource. The URL may be wrong, the resource may have been deleted, or it may never have existed. Other important codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 500 (Internal Server Error)."}
15
+ {"instruction": "What is CSS Flexbox?", "input": "", "output": "CSS Flexbox is a layout model that makes it easy to arrange items in a container. Activated by display: flex on the container. Key properties: flex-direction (row or column), justify-content (main axis alignment: flex-start, center, space-between), align-items (cross axis: stretch, center, flex-start), flex-wrap (allow wrapping). Items can have flex-grow to fill available space."}
16
+ {"instruction": "What is a JavaScript Promise?", "input": "", "output": "A JavaScript Promise represents an asynchronous operation with three states: pending (initial), fulfilled (success), rejected (failure). Created with new Promise((resolve, reject) => {}). Consumed with .then() for success and .catch() for errors. async/await is syntactic sugar that makes promises look synchronous. Promise.all() runs multiple promises in parallel."}
17
+ {"instruction": "What is inorder traversal?", "input": "", "output": "Inorder traversal visits tree nodes in Left, Root, Right order. For a Binary Search Tree, inorder traversal produces nodes in ascending sorted order. Algorithm: recursively visit left subtree, then process current node, then recursively visit right subtree. Time complexity: O(n) where n is number of nodes."}
18
+ {"instruction": "What is the difference between BFS and DFS?", "input": "", "output": "BFS (Breadth-First Search) explores level by level using a queue data structure. It finds shortest path in unweighted graphs. DFS (Depth-First Search) explores as deep as possible using a stack or recursion. BFS is better for shortest path problems; DFS is better for cycle detection, topological sort, and finding connected components. Both have O(V+E) time complexity."}
19
+ {"instruction": "What is memoization?", "input": "", "output": "Memoization is a top-down dynamic programming technique that caches results of expensive function calls and returns the cached result when the same inputs occur again. It uses recursion combined with a hash map or array to store computed values. For example, Fibonacci with memoization runs in O(n) instead of O(2^n). It trades space for time."}
20
+ {"instruction": "What is flex-grow in CSS?", "input": "", "output": "flex-grow is a CSS Flexbox property that defines how much a flex item grows relative to other items when extra space is available in the container. A value of 0 means the item will not grow. A value of 1 means the item takes equal share of available space. If one item has flex-grow: 2 and another has flex-grow: 1, the first gets twice as much extra space."}
data/prepare_docs.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ docs = {
4
+ "binary_search.txt": """Binary Search is an efficient algorithm for finding an item in a sorted list.
5
+ It works by repeatedly dividing the search interval in half.
6
+ Time Complexity: O(log n). Space Complexity: O(1).
7
+ Steps:
8
+ 1) Find the middle element.
9
+ 2) If target equals middle, return index.
10
+ 3) If target < middle, search left half.
11
+ 4) Else search right half.
12
+ Requires the array to be sorted before searching.
13
+ Best case: O(1) when middle element is the target.
14
+ """,
15
+
16
+ "stack_queue.txt": """Stack is a linear data structure following LIFO (Last In First Out) principle.
17
+ Operations: push() adds element, pop() removes top element, peek() views top.
18
+ All stack operations run in O(1) time.
19
+ Use stack for: undo operations, expression evaluation, function call stack, DFS traversal.
20
+
21
+ Queue follows FIFO (First In First Out) principle.
22
+ Operations: enqueue() adds to rear, dequeue() removes from front.
23
+ All queue operations run in O(1) time.
24
+ Use queue for: BFS traversal, scheduling, print spooler, task queues.
25
+
26
+ Key difference: Stack removes most recently added item; Queue removes oldest item.
27
+ """,
28
+
29
+ "sorting_algorithms.txt": """Bubble Sort: repeatedly swaps adjacent elements if out of order.
30
+ Time: O(n^2) average and worst. Space: O(1). Stable sort.
31
+
32
+ Merge Sort: divide array in half, sort each half recursively, merge results.
33
+ Time: O(n log n) all cases. Space: O(n). Stable sort. Best for linked lists.
34
+
35
+ Quick Sort: picks a pivot element, partitions array around it, recursively sorts partitions.
36
+ Time: O(n log n) average, O(n^2) worst case. Space: O(log n). Not stable.
37
+
38
+ Insertion Sort: builds sorted array one element at a time by inserting each into correct position.
39
+ Time: O(n^2) worst, O(n) best. Space: O(1). Good for small or nearly sorted arrays.
40
+
41
+ Heap Sort: builds a max-heap, repeatedly extracts maximum element.
42
+ Time: O(n log n). Space: O(1). Not stable.
43
+ """,
44
+
45
+ "linked_list.txt": """Linked List is a linear data structure where elements are stored in nodes.
46
+ Each node contains: data field + pointer to next node.
47
+
48
+ Types:
49
+ - Singly Linked List: each node points to next node only.
50
+ - Doubly Linked List: each node has pointers to both next and previous nodes.
51
+ - Circular Linked List: last node points back to first node.
52
+
53
+ Advantages over arrays: dynamic size, O(1) insertion/deletion at head.
54
+ Disadvantages: no random access (must traverse), extra memory for pointers.
55
+
56
+ Operations: Insert at head O(1), Search O(n), Delete O(n), Access by index O(n).
57
+ """,
58
+
59
+ "trees.txt": """Binary Tree: each node has at most 2 children called left and right child.
60
+ Root is the topmost node. Leaf nodes have no children.
61
+
62
+ Binary Search Tree (BST): left child < parent node < right child.
63
+ Search, Insert, Delete all take O(log n) average, O(n) worst case.
64
+
65
+ Tree Traversals:
66
+ - Inorder (Left, Root, Right): gives sorted order for BST.
67
+ - Preorder (Root, Left, Right): used to copy tree.
68
+ - Postorder (Left, Right, Root): used to delete tree.
69
+ - Level Order: BFS traversal level by level.
70
+
71
+ Height of tree: number of edges on longest path from root to leaf.
72
+ Balanced BST examples: AVL Tree, Red-Black Tree — guarantee O(log n) operations.
73
+ """,
74
+
75
+ "dynamic_programming.txt": """Dynamic Programming (DP) solves problems by breaking into overlapping subproblems.
76
+ Stores results of subproblems to avoid recomputation (memoization).
77
+
78
+ Key properties required:
79
+ 1. Optimal substructure: optimal solution built from optimal subproblem solutions.
80
+ 2. Overlapping subproblems: same subproblems solved multiple times.
81
+
82
+ Approaches:
83
+ - Top-down (Memoization): recursion + cache results in a table.
84
+ - Bottom-up (Tabulation): fill table iteratively from smallest subproblems.
85
+
86
+ Classic DP problems:
87
+ - Fibonacci: O(2^n) naive → O(n) with DP.
88
+ - 0/1 Knapsack: maximize value within weight limit.
89
+ - Longest Common Subsequence (LCS).
90
+ - Coin Change: minimum coins to make a sum.
91
+ - Longest Increasing Subsequence.
92
+ """,
93
+
94
+ "react_hooks.txt": """React Hooks are functions that let you use state and lifecycle features in functional components.
95
+ Introduced in React 16.8 to replace class components.
96
+
97
+ useState: manages local component state.
98
+ Syntax: const [state, setState] = useState(initialValue)
99
+ Example: const [count, setCount] = useState(0)
100
+
101
+ useEffect: handles side effects like API calls, subscriptions, DOM updates.
102
+ Runs after every render by default.
103
+ Syntax: useEffect(() => { /* effect */ }, [dependencies])
104
+ Empty dependency array [] means run only once on mount.
105
+
106
+ useContext: consume React context without wrapping in Consumer component.
107
+ Syntax: const value = useContext(MyContext)
108
+
109
+ useRef: holds mutable value that does not trigger re-render. Also used to access DOM elements.
110
+
111
+ Rules of Hooks:
112
+ 1. Only call hooks at the top level (not inside loops or conditions).
113
+ 2. Only call hooks from React function components or custom hooks.
114
+ """,
115
+
116
+ "rest_api.txt": """REST API (Representational State Transfer) is an architectural style for web services.
117
+ REST uses standard HTTP methods to perform operations on resources.
118
+
119
+ HTTP Methods:
120
+ - GET: retrieve/read resource. No request body.
121
+ - POST: create new resource. Data sent in request body.
122
+ - PUT: update existing resource completely.
123
+ - PATCH: partially update a resource.
124
+ - DELETE: remove a resource.
125
+
126
+ HTTP Status Codes:
127
+ - 200 OK: request succeeded.
128
+ - 201 Created: new resource created successfully.
129
+ - 400 Bad Request: invalid request from client.
130
+ - 401 Unauthorized: authentication required.
131
+ - 403 Forbidden: authenticated but not authorized.
132
+ - 404 Not Found: resource does not exist.
133
+ - 500 Internal Server Error: server-side error.
134
+
135
+ REST principles: Stateless, Client-Server, Cacheable, Uniform Interface.
136
+ JSON is the most common data format for REST API responses.
137
+ """,
138
+
139
+ "css_flexbox.txt": """CSS Flexbox (Flexible Box Layout) is a layout model for arranging items in a container.
140
+ Activated by setting display: flex on a container element.
141
+
142
+ Container properties:
143
+ - flex-direction: row (default, horizontal) or column (vertical).
144
+ - justify-content: aligns items on main axis. Values: flex-start, flex-end, center, space-between, space-around.
145
+ - align-items: aligns items on cross axis. Values: stretch (default), flex-start, flex-end, center.
146
+ - flex-wrap: nowrap (default) or wrap to allow items to move to next line.
147
+ - gap: space between flex items.
148
+
149
+ Item properties:
150
+ - flex-grow: how much item grows relative to siblings. 0 means no grow.
151
+ - flex-shrink: how much item shrinks when space is limited.
152
+ - flex-basis: initial size of item before flex calculations.
153
+ - align-self: overrides align-items for a single item.
154
+
155
+ Shorthand: flex: grow shrink basis (e.g., flex: 1 1 auto)
156
+ """,
157
+
158
+ "javascript_promises.txt": """JavaScript Promises handle asynchronous operations in a cleaner way than callbacks.
159
+
160
+ Promise States:
161
+ - Pending: initial state, operation not complete.
162
+ - Fulfilled: operation completed successfully.
163
+ - Rejected: operation failed.
164
+
165
+ Creating a Promise:
166
+ const myPromise = new Promise((resolve, reject) => {
167
+ if (success) resolve(result);
168
+ else reject(error);
169
+ });
170
+
171
+ Consuming a Promise:
172
+ myPromise
173
+ .then(result => console.log(result))
174
+ .catch(error => console.error(error))
175
+ .finally(() => console.log('always runs'));
176
+
177
+ async/await syntax (ES2017) is syntactic sugar over promises:
178
+ async function fetchData() {
179
+ try {
180
+ const data = await fetch(url);
181
+ const json = await data.json();
182
+ return json;
183
+ } catch (error) {
184
+ console.error(error);
185
+ }
186
+ }
187
+
188
+ Promise utility methods:
189
+ - Promise.all([p1, p2]): runs in parallel, resolves when all resolve.
190
+ - Promise.race([p1, p2]): resolves/rejects with first settled promise.
191
+ - Promise.allSettled(): waits for all, returns all results regardless of success/failure.
192
+ """,
193
+
194
+ "graph_algorithms.txt": """Graph is a data structure with nodes (vertices) connected by edges.
195
+ Types: Directed/Undirected, Weighted/Unweighted, Cyclic/Acyclic.
196
+
197
+ Representations:
198
+ - Adjacency Matrix: 2D array. O(1) edge lookup, O(V^2) space.
199
+ - Adjacency List: array of lists. O(V+E) space. Better for sparse graphs.
200
+
201
+ BFS (Breadth-First Search):
202
+ Explores level by level using a queue. Time: O(V+E).
203
+ Used for: shortest path in unweighted graph, level-order traversal.
204
+
205
+ DFS (Depth-First Search):
206
+ Explores as far as possible using stack/recursion. Time: O(V+E).
207
+ Used for: cycle detection, topological sort, connected components.
208
+
209
+ Dijkstra's Algorithm: shortest path in weighted graph with non-negative edges.
210
+ Time: O((V+E) log V) with priority queue.
211
+
212
+ Topological Sort: linear ordering of vertices where each directed edge u→v means u comes before v.
213
+ Only valid for Directed Acyclic Graphs (DAGs).
214
+ """
215
+ }
216
+
217
+ os.makedirs("docs", exist_ok=True)
218
+ for filename, content in docs.items():
219
+ with open(f"docs/{filename}", "w", encoding="utf-8") as f:
220
+ f.write(content.strip())
221
+
222
+ print(f"Created {len(docs)} documents in docs/")
data/prepare_finetune.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ qa_pairs = [
5
+ {"instruction": "What is binary search?",
6
+ "input": "",
7
+ "output": "Binary search is an efficient searching algorithm for sorted arrays. It works by repeatedly dividing the search interval in half. Time complexity: O(log n), Space complexity: O(1). Requires the array to be sorted beforehand."},
8
+
9
+ {"instruction": "What is the time complexity of binary search?",
10
+ "input": "",
11
+ "output": "Binary search has a time complexity of O(log n) because it halves the search space with each step. Best case is O(1) when the middle element is the target. Space complexity is O(1) for iterative implementation."},
12
+
13
+ {"instruction": "What is the difference between stack and queue?",
14
+ "input": "",
15
+ "output": "Stack uses LIFO (Last In First Out) - the last element added is the first to be removed. Operations: push and pop. Queue uses FIFO (First In First Out) - the first element added is the first to be removed. Operations: enqueue and dequeue. Both have O(1) operations."},
16
+
17
+ {"instruction": "Explain merge sort.",
18
+ "input": "",
19
+ "output": "Merge sort is a divide-and-conquer sorting algorithm. It splits the array in half, recursively sorts each half, then merges the two sorted halves. Time complexity: O(n log n) in all cases. Space complexity: O(n). It is a stable sorting algorithm, making it good for sorting linked lists."},
20
+
21
+ {"instruction": "What is quicksort?",
22
+ "input": "",
23
+ "output": "Quicksort is a divide-and-conquer algorithm that picks a pivot element and partitions the array into elements less than and greater than the pivot, then recursively sorts each partition. Average time: O(n log n), Worst case: O(n^2) when pivot is always smallest or largest element. Space: O(log n)."},
24
+
25
+ {"instruction": "What is a linked list?",
26
+ "input": "",
27
+ "output": "A linked list is a linear data structure where elements are stored in nodes. Each node contains data and a pointer to the next node. Types: Singly (one direction), Doubly (both directions), Circular (last points to first). Advantage: dynamic size, O(1) insertion at head. Disadvantage: no random access, O(n) search."},
28
+
29
+ {"instruction": "What is a Binary Search Tree?",
30
+ "input": "",
31
+ "output": "A Binary Search Tree (BST) is a binary tree where for every node, all values in the left subtree are less than the node's value, and all values in the right subtree are greater. This property enables O(log n) average search, insert, and delete operations. Worst case is O(n) for unbalanced trees."},
32
+
33
+ {"instruction": "What is dynamic programming?",
34
+ "input": "",
35
+ "output": "Dynamic programming solves complex problems by breaking them into overlapping subproblems and storing results to avoid recomputation. Two approaches: Memoization (top-down, recursion with cache) and Tabulation (bottom-up, iterative). Requires optimal substructure and overlapping subproblems. Examples: Fibonacci, Knapsack, LCS, Coin Change."},
36
+
37
+ {"instruction": "What are React hooks?",
38
+ "input": "",
39
+ "output": "React hooks are functions that let you use state and lifecycle features in functional components, introduced in React 16.8. Key hooks: useState for state management, useEffect for side effects like API calls, useContext for consuming context, useRef for DOM references. Rules: only call hooks at top level, only in React functions."},
40
+
41
+ {"instruction": "What is useState in React?",
42
+ "input": "",
43
+ "output": "useState is a React hook for managing local component state. Syntax: const [state, setState] = useState(initialValue). It returns the current state value and a setter function. Calling setState triggers a re-render with the new value. Example: const [count, setCount] = useState(0) then setCount(count + 1) to increment."},
44
+
45
+ {"instruction": "What is useEffect in React?",
46
+ "input": "",
47
+ "output": "useEffect is a React hook for handling side effects like API calls, subscriptions, and DOM updates. It runs after every render by default. The dependency array controls when it runs: empty array [] runs only on mount, [value] runs when value changes. Return a cleanup function to run on unmount. Syntax: useEffect(() => { effect }, [deps])."},
48
+
49
+ {"instruction": "What is a REST API?",
50
+ "input": "",
51
+ "output": "REST API (Representational State Transfer) is an architectural style for web services using standard HTTP methods. GET retrieves resources, POST creates new resources, PUT updates resources completely, PATCH partially updates, DELETE removes resources. REST is stateless meaning each request contains all needed information. JSON is the most common response format."},
52
+
53
+ {"instruction": "What HTTP methods does REST use?",
54
+ "input": "",
55
+ "output": "REST uses five main HTTP methods: GET for reading/retrieving data, POST for creating new resources, PUT for completely updating an existing resource, PATCH for partially updating a resource, and DELETE for removing a resource. These map to CRUD operations: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE)."},
56
+
57
+ {"instruction": "What does HTTP status code 404 mean?",
58
+ "input": "",
59
+ "output": "HTTP status code 404 means Not Found. It indicates the server cannot find the requested resource. The URL may be wrong, the resource may have been deleted, or it may never have existed. Other important codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 500 (Internal Server Error)."},
60
+
61
+ {"instruction": "What is CSS Flexbox?",
62
+ "input": "",
63
+ "output": "CSS Flexbox is a layout model that makes it easy to arrange items in a container. Activated by display: flex on the container. Key properties: flex-direction (row or column), justify-content (main axis alignment: flex-start, center, space-between), align-items (cross axis: stretch, center, flex-start), flex-wrap (allow wrapping). Items can have flex-grow to fill available space."},
64
+
65
+ {"instruction": "What is a JavaScript Promise?",
66
+ "input": "",
67
+ "output": "A JavaScript Promise represents an asynchronous operation with three states: pending (initial), fulfilled (success), rejected (failure). Created with new Promise((resolve, reject) => {}). Consumed with .then() for success and .catch() for errors. async/await is syntactic sugar that makes promises look synchronous. Promise.all() runs multiple promises in parallel."},
68
+
69
+ {"instruction": "What is inorder traversal?",
70
+ "input": "",
71
+ "output": "Inorder traversal visits tree nodes in Left, Root, Right order. For a Binary Search Tree, inorder traversal produces nodes in ascending sorted order. Algorithm: recursively visit left subtree, then process current node, then recursively visit right subtree. Time complexity: O(n) where n is number of nodes."},
72
+
73
+ {"instruction": "What is the difference between BFS and DFS?",
74
+ "input": "",
75
+ "output": "BFS (Breadth-First Search) explores level by level using a queue data structure. It finds shortest path in unweighted graphs. DFS (Depth-First Search) explores as deep as possible using a stack or recursion. BFS is better for shortest path problems; DFS is better for cycle detection, topological sort, and finding connected components. Both have O(V+E) time complexity."},
76
+
77
+ {"instruction": "What is memoization?",
78
+ "input": "",
79
+ "output": "Memoization is a top-down dynamic programming technique that caches results of expensive function calls and returns the cached result when the same inputs occur again. It uses recursion combined with a hash map or array to store computed values. For example, Fibonacci with memoization runs in O(n) instead of O(2^n). It trades space for time."},
80
+
81
+ {"instruction": "What is flex-grow in CSS?",
82
+ "input": "",
83
+ "output": "flex-grow is a CSS Flexbox property that defines how much a flex item grows relative to other items when extra space is available in the container. A value of 0 means the item will not grow. A value of 1 means the item takes equal share of available space. If one item has flex-grow: 2 and another has flex-grow: 1, the first gets twice as much extra space."},
84
+ ]
85
+
86
+ output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "finetune_data.jsonl")
87
+ with open(output_path, "w", encoding="utf-8") as f:
88
+ for pair in qa_pairs:
89
+ f.write(json.dumps(pair) + "\n")
90
+
91
+ print(f"Saved {len(qa_pairs)} training examples to data/finetune_data.jsonl")
data/prepare_questions.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import os
3
+
4
+ questions = [
5
+ "What is binary search?",
6
+ "What is the time complexity of binary search?",
7
+ "When can you use binary search?",
8
+ "Explain bubble sort.",
9
+ "What is the difference between stack and queue?",
10
+ "What is LIFO?",
11
+ "What is FIFO?",
12
+ "Explain merge sort.",
13
+ "What is quicksort?",
14
+ "What is the worst case time complexity of quicksort?",
15
+ "What is a linked list?",
16
+ "What is the difference between array and linked list?",
17
+ "What is a doubly linked list?",
18
+ "What is a binary tree?",
19
+ "What is a Binary Search Tree?",
20
+ "What is inorder traversal?",
21
+ "What is the difference between BFS and DFS?",
22
+ "What is dynamic programming?",
23
+ "What is memoization?",
24
+ "What is the difference between memoization and tabulation?",
25
+ "Explain the coin change problem.",
26
+ "What is React?",
27
+ "What are React hooks?",
28
+ "What is useState in React?",
29
+ "What is useEffect in React?",
30
+ "What is a REST API?",
31
+ "What HTTP methods does REST use?",
32
+ "What does HTTP status code 404 mean?",
33
+ "What does HTTP status code 200 mean?",
34
+ "What is CSS Flexbox?",
35
+ "What does justify-content do in Flexbox?",
36
+ "What is a JavaScript Promise?",
37
+ "What are the three states of a Promise?",
38
+ "What is async/await in JavaScript?",
39
+ "What is Promise.all()?",
40
+ "What is insertion sort?",
41
+ "What is heap sort?",
42
+ "What is a graph in data structures?",
43
+ "What is Dijkstra algorithm?",
44
+ "What is topological sort?",
45
+ "What is the difference between stack and heap memory?",
46
+ "What is a circular linked list?",
47
+ "What is an AVL tree?",
48
+ "What is the difference between preorder and postorder traversal?",
49
+ "What is the knapsack problem?",
50
+ "What is the longest common subsequence?",
51
+ "What is flex-grow in CSS?",
52
+ "What is useContext in React?",
53
+ "What is useRef in React?",
54
+ "What is the difference between PUT and PATCH in REST?",
55
+ ]
56
+
57
+ os.makedirs(os.path.dirname(os.path.abspath(__file__)), exist_ok=True)
58
+
59
+ output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "questions.csv")
60
+ with open(output_path, "w", newline="", encoding="utf-8") as f:
61
+ writer = csv.writer(f)
62
+ writer.writerow(["id", "question"])
63
+ for i, q in enumerate(questions, 1):
64
+ writer.writerow([i, q])
65
+
66
+ print(f"Created {len(questions)} questions in data/questions.csv")
data/questions.csv ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ id,question
2
+ 1,What is binary search?
3
+ 2,What is the time complexity of binary search?
4
+ 3,When can you use binary search?
5
+ 4,Explain bubble sort.
6
+ 5,What is the difference between stack and queue?
7
+ 6,What is LIFO?
8
+ 7,What is FIFO?
9
+ 8,Explain merge sort.
10
+ 9,What is quicksort?
11
+ 10,What is the worst case time complexity of quicksort?
12
+ 11,What is a linked list?
13
+ 12,What is the difference between array and linked list?
14
+ 13,What is a doubly linked list?
15
+ 14,What is a binary tree?
16
+ 15,What is a Binary Search Tree?
17
+ 16,What is inorder traversal?
18
+ 17,What is the difference between BFS and DFS?
19
+ 18,What is dynamic programming?
20
+ 19,What is memoization?
21
+ 20,What is the difference between memoization and tabulation?
22
+ 21,Explain the coin change problem.
23
+ 22,What is React?
24
+ 23,What are React hooks?
25
+ 24,What is useState in React?
26
+ 25,What is useEffect in React?
27
+ 26,What is a REST API?
28
+ 27,What HTTP methods does REST use?
29
+ 28,What does HTTP status code 404 mean?
30
+ 29,What does HTTP status code 200 mean?
31
+ 30,What is CSS Flexbox?
32
+ 31,What does justify-content do in Flexbox?
33
+ 32,What is a JavaScript Promise?
34
+ 33,What are the three states of a Promise?
35
+ 34,What is async/await in JavaScript?
36
+ 35,What is Promise.all()?
37
+ 36,What is insertion sort?
38
+ 37,What is heap sort?
39
+ 38,What is a graph in data structures?
40
+ 39,What is Dijkstra algorithm?
41
+ 40,What is topological sort?
42
+ 41,What is the difference between stack and heap memory?
43
+ 42,What is a circular linked list?
44
+ 43,What is an AVL tree?
45
+ 44,What is the difference between preorder and postorder traversal?
46
+ 45,What is the knapsack problem?
47
+ 46,What is the longest common subsequence?
48
+ 47,What is flex-grow in CSS?
49
+ 48,What is useContext in React?
50
+ 49,What is useRef in React?
51
+ 50,What is the difference between PUT and PATCH in REST?
demo.py ADDED
@@ -0,0 +1,409 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ from system1_baseline import ask_baseline
4
+ from system2_rag import ask_rag, load_vectorstore, build_vectorstore
5
+
6
+ st.set_page_config(
7
+ page_title="CodeSage",
8
+ page_icon="🧙",
9
+ layout="wide"
10
+ )
11
+
12
+ # ── Session state init ────────────────────────────────────────────────────────
13
+ if "ratings" not in st.session_state:
14
+ st.session_state.ratings = [] # list of rating dicts
15
+ if "last_results" not in st.session_state:
16
+ st.session_state.last_results = None # most recent answers
17
+ if "rating_submitted" not in st.session_state:
18
+ st.session_state.rating_submitted = False
19
+
20
+ # ── Custom CSS ────────────────────────────────────────────────────────────────
21
+ st.markdown("""
22
+ <style>
23
+ /* System 1 — Blue */
24
+ .card-blue {
25
+ background: linear-gradient(135deg, #0d1f3c, #102a4c);
26
+ border: 1px solid #2563eb;
27
+ border-top: 4px solid #3b82f6;
28
+ border-radius: 12px;
29
+ padding: 20px;
30
+ margin-top: 10px;
31
+ min-height: 180px;
32
+ }
33
+ .card-blue .answer { color: #93c5fd; font-size: 15px; line-height: 1.7; }
34
+ .card-blue .time { color: #60a5fa; font-size: 13px; margin-top: 12px; }
35
+
36
+ /* System 2 — Teal/Green */
37
+ .card-teal {
38
+ background: linear-gradient(135deg, #052e1c, #063d24);
39
+ border: 1px solid #059669;
40
+ border-top: 4px solid #10b981;
41
+ border-radius: 12px;
42
+ padding: 20px;
43
+ margin-top: 10px;
44
+ min-height: 180px;
45
+ }
46
+ .card-teal .answer { color: #6ee7b7; font-size: 15px; line-height: 1.7; }
47
+ .card-teal .time { color: #34d399; font-size: 13px; margin-top: 12px; }
48
+ .card-teal .context { background: #063d24; border: 1px solid #065f46; border-radius: 8px;
49
+ padding: 12px; margin-top: 14px; color: #a7f3d0;
50
+ font-size: 13px; line-height: 1.6; }
51
+ .card-teal .ctx-label { color: #6ee7b7; font-size: 12px; font-weight: 600;
52
+ margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.5px; }
53
+
54
+ /* System 3 — Purple */
55
+ .card-purple {
56
+ background: linear-gradient(135deg, #1a0a2e, #220d3b);
57
+ border: 1px solid #7c3aed;
58
+ border-top: 4px solid #8b5cf6;
59
+ border-radius: 12px;
60
+ padding: 20px;
61
+ margin-top: 10px;
62
+ min-height: 180px;
63
+ }
64
+ .card-purple .answer { color: #c4b5fd; font-size: 15px; line-height: 1.7; }
65
+ .card-purple .time { color: #a78bfa; font-size: 13px; margin-top: 12px; }
66
+
67
+ /* Section headers */
68
+ .sys-header { font-size: 18px; font-weight: 700; margin-bottom: 2px; }
69
+ .sys-blue { color: #3b82f6; }
70
+ .sys-teal { color: #10b981; }
71
+ .sys-purple { color: #8b5cf6; }
72
+
73
+ /* Rating box */
74
+ .rating-box {
75
+ background: #111827;
76
+ border: 1px solid #374151;
77
+ border-radius: 12px;
78
+ padding: 18px 20px;
79
+ margin-top: 8px;
80
+ }
81
+
82
+ /* Eval table */
83
+ .eval-table {
84
+ width: 100%;
85
+ border-collapse: collapse;
86
+ font-size: 14px;
87
+ margin-top: 10px;
88
+ }
89
+ .eval-table th {
90
+ padding: 12px 16px;
91
+ text-align: center;
92
+ font-weight: 700;
93
+ font-size: 15px;
94
+ border-bottom: 2px solid #374151;
95
+ }
96
+ .eval-table th:first-child { text-align: left; color: #9ca3af; }
97
+ .eval-table th.col-blue { color: #3b82f6; }
98
+ .eval-table th.col-teal { color: #10b981; }
99
+ .eval-table th.col-purple { color: #8b5cf6; }
100
+ .eval-table td {
101
+ padding: 11px 16px;
102
+ text-align: center;
103
+ border-bottom: 1px solid #1f2937;
104
+ color: #d1d5db;
105
+ }
106
+ .eval-table td:first-child { text-align: left; color: #9ca3af; font-weight: 600; }
107
+ .eval-table tr:hover td { background: #111827; }
108
+ .badge {
109
+ display: inline-block;
110
+ padding: 3px 10px;
111
+ border-radius: 12px;
112
+ font-weight: 700;
113
+ font-size: 13px;
114
+ }
115
+ .badge-green { background: #052e1c; color: #10b981; border: 1px solid #059669; }
116
+ .badge-yellow { background: #1c1a05; color: #f59e0b; border: 1px solid #d97706; }
117
+ .badge-red { background: #2e0505; color: #ef4444; border: 1px solid #dc2626; }
118
+ .badge-grey { background: #1f2937; color: #6b7280; border: 1px solid #374151; }
119
+ .metric-note { color: #6b7280; font-size: 12px; font-weight: 400; }
120
+ </style>
121
+ """, unsafe_allow_html=True)
122
+
123
+ # ── Title ─────────────────────────────────────────────────────────────────────
124
+ st.title("🧙 CODESAGE")
125
+ st.markdown(
126
+ "**Comparative Study — RAG vs Fine-Tuning for Domain-Specific QA** \n"
127
+ "Ask the same question to all 3 systems and compare their answers."
128
+ )
129
+
130
+ # ── Load vector store once ────────────────────────────────────────────────────
131
+ @st.cache_resource(show_spinner="Building knowledge base...")
132
+ def get_vectorstore():
133
+ if not os.path.exists("data/faiss_index"):
134
+ return build_vectorstore()
135
+ return load_vectorstore()
136
+
137
+ vs = get_vectorstore()
138
+
139
+ # ── Input ─────────────────────────────────────────────────────────────────────
140
+ st.divider()
141
+ question = st.text_input(
142
+ "Enter your question:",
143
+ placeholder="e.g. What is binary search? / What are React hooks? / Explain merge sort."
144
+ )
145
+
146
+ sample_questions = [
147
+ "What is binary search?",
148
+ "What is the difference between stack and queue?",
149
+ "Explain merge sort.",
150
+ "What are React hooks?",
151
+ "What is a REST API?",
152
+ "What is dynamic programming?",
153
+ ]
154
+
155
+ st.markdown("**Quick questions:**")
156
+ cols = st.columns(len(sample_questions))
157
+ for i, sq in enumerate(sample_questions):
158
+ if cols[i].button(sq, key=f"sq_{i}"):
159
+ question = sq
160
+
161
+ # ── Run systems ───────────────────────────────────────────────────────────────
162
+ if st.button("Ask All 3 Systems", type="primary") and question:
163
+ st.session_state.rating_submitted = False # reset for new question
164
+ col1, col2, col3 = st.columns(3)
165
+
166
+ with col1:
167
+ st.markdown('<p class="sys-header sys-blue">System 1 — Baseline LLM</p>', unsafe_allow_html=True)
168
+ st.caption("Prompt only. No extra knowledge.")
169
+ with st.spinner("Generating..."):
170
+ r1 = ask_baseline(question)
171
+
172
+ with col2:
173
+ st.markdown('<p class="sys-header sys-teal">System 2 — RAG Chatbot</p>', unsafe_allow_html=True)
174
+ st.caption("Retrieves from knowledge base, then generates.")
175
+ with st.spinner("Retrieving + generating..."):
176
+ r2 = ask_rag(question, vs)
177
+
178
+ with col3:
179
+ st.markdown('<p class="sys-header sys-purple">System 3 — Fine-Tuned Model</p>', unsafe_allow_html=True)
180
+ st.caption("TinyLlama fine-tuned on programming Q&A via LoRA.")
181
+ if os.path.exists("./finetuned_model"):
182
+ with st.spinner("Generating..."):
183
+ from system3_inference import ask_finetuned
184
+ r3 = ask_finetuned(question)
185
+ else:
186
+ r3 = {"answer": "Model not available.", "response_time": 0}
187
+
188
+ # persist results so they survive reruns
189
+ st.session_state.last_results = {
190
+ "question": question,
191
+ "r1": r1, "r2": r2, "r3": r3,
192
+ }
193
+
194
+ # ── Always render answers if they exist ───────────────────────────────────────
195
+ if st.session_state.last_results:
196
+ res = st.session_state.last_results
197
+ r1, r2, r3 = res["r1"], res["r2"], res["r3"]
198
+
199
+ st.divider()
200
+ col1, col2, col3 = st.columns(3)
201
+
202
+ with col1:
203
+ st.markdown('<p class="sys-header sys-blue">System 1 — Baseline LLM</p>', unsafe_allow_html=True)
204
+ st.caption("Prompt only. No extra knowledge.")
205
+ st.markdown(f"""
206
+ <div class="card-blue">
207
+ <div class="answer">{r1['answer']}</div>
208
+ <div class="time">⏱️ Response Time: {r1['response_time']}s</div>
209
+ </div>
210
+ """, unsafe_allow_html=True)
211
+
212
+ with col2:
213
+ st.markdown('<p class="sys-header sys-teal">System 2 — RAG Chatbot</p>', unsafe_allow_html=True)
214
+ st.caption("Retrieves from knowledge base, then generates.")
215
+ st.markdown(f"""
216
+ <div class="card-teal">
217
+ <div class="answer">{r2['answer']}</div>
218
+ <div class="time">⏱️ Response Time: {r2['response_time']}s</div>
219
+ <div class="context">
220
+ <div class="ctx-label">📄 Context retrieved from knowledge base</div>
221
+ {r2['context_used']}
222
+ </div>
223
+ </div>
224
+ """, unsafe_allow_html=True)
225
+
226
+ with col3:
227
+ st.markdown('<p class="sys-header sys-purple">System 3 — Fine-Tuned Model</p>', unsafe_allow_html=True)
228
+ st.caption("TinyLlama fine-tuned on programming Q&A via LoRA.")
229
+ if r3["answer"] == "Model not available.":
230
+ st.warning(
231
+ "Fine-tuned model not ready yet. \n"
232
+ "Run `system3_finetune_colab.ipynb` on Google Colab, "
233
+ "download `finetuned_model.zip`, and unzip it here."
234
+ )
235
+ else:
236
+ st.markdown(f"""
237
+ <div class="card-purple">
238
+ <div class="answer">{r3['answer']}</div>
239
+ <div class="time">⏱️ Response Time: {r3['response_time']}s</div>
240
+ </div>
241
+ """, unsafe_allow_html=True)
242
+
243
+ # ── Rating section ─────────────────────────────────────────────────────────────
244
+ if st.session_state.last_results and not st.session_state.rating_submitted:
245
+ st.divider()
246
+ st.subheader("Rate the Answers")
247
+ st.caption("Your ratings are used to build the live evaluation table below.")
248
+
249
+ rc1, rc2, rc3 = st.columns(3)
250
+
251
+ with rc1:
252
+ st.markdown("**System 1 — Baseline LLM**")
253
+ c1_correct = st.slider("Correctness (1–5)", 1, 5, 3, key="c1_correct")
254
+ c1_halluc = st.checkbox("Hallucination detected?", key="c1_halluc")
255
+ c1_complete= st.slider("Completeness (1–5)", 1, 5, 3, key="c1_complete")
256
+
257
+ with rc2:
258
+ st.markdown("**System 2 — RAG Chatbot**")
259
+ c2_correct = st.slider("Correctness (1–5)", 1, 5, 3, key="c2_correct")
260
+ c2_halluc = st.checkbox("Hallucination detected?", key="c2_halluc")
261
+ c2_complete= st.slider("Completeness (1–5)", 1, 5, 3, key="c2_complete")
262
+
263
+ with rc3:
264
+ st.markdown("**System 3 — Fine-Tuned**")
265
+ c3_correct = st.slider("Correctness (1–5)", 1, 5, 3, key="c3_correct")
266
+ c3_halluc = st.checkbox("Hallucination detected?", key="c3_halluc")
267
+ c3_complete= st.slider("Completeness (1–5)", 1, 5, 3, key="c3_complete")
268
+
269
+ if st.button("Submit Ratings", type="primary"):
270
+ res = st.session_state.last_results
271
+ st.session_state.ratings.append({
272
+ "question": res["question"],
273
+ # correctness
274
+ "c1_correct": c1_correct,
275
+ "c2_correct": c2_correct,
276
+ "c3_correct": c3_correct,
277
+ # hallucination (1 = yes, 0 = no)
278
+ "c1_halluc": 1 if c1_halluc else 0,
279
+ "c2_halluc": 1 if c2_halluc else 0,
280
+ "c3_halluc": 1 if c3_halluc else 0,
281
+ # completeness
282
+ "c1_complete": c1_complete,
283
+ "c2_complete": c2_complete,
284
+ "c3_complete": c3_complete,
285
+ # response times (auto)
286
+ "c1_time": res["r1"]["response_time"],
287
+ "c2_time": res["r2"]["response_time"],
288
+ "c3_time": res["r3"]["response_time"],
289
+ })
290
+ st.session_state.rating_submitted = True
291
+ st.success(f"Rating saved! Total questions rated: {len(st.session_state.ratings)}")
292
+ st.rerun()
293
+
294
+ # ── Metrics table (live) ───────────────────────────────────────────────────────
295
+ st.divider()
296
+ st.subheader("Evaluation Metrics Comparison")
297
+
298
+ ratings = st.session_state.ratings
299
+ n = len(ratings)
300
+
301
+ if n == 0:
302
+ st.caption("No ratings yet. Ask a question and rate the answers to populate this table.")
303
+
304
+ # show placeholder dashes
305
+ def badge(val, _n):
306
+ return f'<span class="badge badge-grey">—</span>'
307
+ else:
308
+ st.caption(f"Based on **{n} question(s)** rated by you. Response time is measured automatically.")
309
+
310
+ def avg(key):
311
+ return round(sum(r[key] for r in ratings) / n, 2)
312
+
313
+ def pct(key):
314
+ return round(sum(r[key] for r in ratings) / n * 100)
315
+
316
+ def color_correct(v):
317
+ if v >= 4.0: return "badge-green"
318
+ if v >= 3.0: return "badge-yellow"
319
+ return "badge-red"
320
+
321
+ def color_halluc(v): # lower = better
322
+ if v <= 15: return "badge-green"
323
+ if v <= 30: return "badge-yellow"
324
+ return "badge-red"
325
+
326
+ def color_complete(v):
327
+ if v >= 4.0: return "badge-green"
328
+ if v >= 3.0: return "badge-yellow"
329
+ return "badge-red"
330
+
331
+ def color_time(v): # lower = better
332
+ if v <= 1.0: return "badge-green"
333
+ if v <= 2.0: return "badge-yellow"
334
+ return "badge-red"
335
+
336
+ v_c1 = avg("c1_correct"); v_c2 = avg("c2_correct"); v_c3 = avg("c3_correct")
337
+ v_h1 = pct("c1_halluc"); v_h2 = pct("c2_halluc"); v_h3 = pct("c3_halluc")
338
+ v_p1 = avg("c1_complete"); v_p2 = avg("c2_complete"); v_p3 = avg("c3_complete")
339
+ v_t1 = avg("c1_time"); v_t2 = avg("c2_time"); v_t3 = avg("c3_time")
340
+
341
+ def badge(val, cls):
342
+ return f'<span class="badge {cls}">{val}</span>'
343
+
344
+ row_correct = (badge(f"{v_c1}/5", color_correct(v_c1)),
345
+ badge(f"{v_c2}/5", color_correct(v_c2)),
346
+ badge(f"{v_c3}/5", color_correct(v_c3)))
347
+ row_halluc = (badge(f"{v_h1}%", color_halluc(v_h1)),
348
+ badge(f"{v_h2}%", color_halluc(v_h2)),
349
+ badge(f"{v_h3}%", color_halluc(v_h3)))
350
+ row_complete = (badge(f"{v_p1}/5", color_complete(v_p1)),
351
+ badge(f"{v_p2}/5", color_complete(v_p2)),
352
+ badge(f"{v_p3}/5", color_complete(v_p3)))
353
+ row_time = (badge(f"{v_t1}s", color_time(v_t1)),
354
+ badge(f"{v_t2}s", color_time(v_t2)),
355
+ badge(f"{v_t3}s", color_time(v_t3)))
356
+
357
+ if n == 0:
358
+ dash = '<span class="badge badge-grey">—</span>'
359
+ row_correct = row_halluc = row_complete = row_time = (dash, dash, dash)
360
+
361
+ st.markdown(f"""
362
+ <table class="eval-table">
363
+ <thead>
364
+ <tr>
365
+ <th>Metric</th>
366
+ <th class="col-blue">System 1<br><span style="font-weight:400;font-size:12px">Baseline LLM</span></th>
367
+ <th class="col-teal">System 2<br><span style="font-weight:400;font-size:12px">RAG Chatbot</span></th>
368
+ <th class="col-purple">System 3<br><span style="font-weight:400;font-size:12px">Fine-Tuned</span></th>
369
+ </tr>
370
+ </thead>
371
+ <tbody>
372
+ <tr>
373
+ <td>Correctness Score <span class="metric-note">(1–5, user rated)</span></td>
374
+ <td>{row_correct[0]}</td><td>{row_correct[1]}</td><td>{row_correct[2]}</td>
375
+ </tr>
376
+ <tr>
377
+ <td>Hallucination Rate <span class="metric-note">(%, user rated)</span></td>
378
+ <td>{row_halluc[0]}</td><td>{row_halluc[1]}</td><td>{row_halluc[2]}</td>
379
+ </tr>
380
+ <tr>
381
+ <td>Answer Completeness <span class="metric-note">(1–5, user rated)</span></td>
382
+ <td>{row_complete[0]}</td><td>{row_complete[1]}</td><td>{row_complete[2]}</td>
383
+ </tr>
384
+ <tr>
385
+ <td>Avg Response Time <span class="metric-note">(seconds, auto)</span></td>
386
+ <td>{row_time[0]}</td><td>{row_time[1]}</td><td>{row_time[2]}</td>
387
+ </tr>
388
+ </tbody>
389
+ </table>
390
+ """, unsafe_allow_html=True)
391
+
392
+ if n > 0:
393
+ if st.button("Clear All Ratings"):
394
+ st.session_state.ratings = []
395
+ st.rerun()
396
+
397
+ # ── About ──────────────────────────────────────────────────────────────────────
398
+ st.divider()
399
+ with st.expander("About CodeSage"):
400
+ st.markdown("""
401
+ **Project:** CodeSage — Comparative Study of RAG and Fine-Tuning for Domain-Specific Question Answering
402
+ **Domain:** Programming Tutor (DSA + Web Development)
403
+
404
+ | System | Description |
405
+ |--------|-------------|
406
+ | Baseline | Llama 3.1 8B (via Groq) with only a system prompt |
407
+ | RAG | FAISS vector store + HuggingFace embeddings + Llama 3.1 8B (via Groq) |
408
+ | Fine-tuned | TinyLlama 1.1B fine-tuned via LoRA on 20 programming Q&A pairs |
409
+ """)
evaluate.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Runs all 3 systems on the test questions and saves results to results/evaluation_results.csv.
3
+ After running, open the CSV and manually fill in:
4
+ - correctness_score (1-5): 1=wrong, 3=partial, 5=perfect
5
+ - hallucination (Yes/No): Yes if the answer contains false information
6
+ """
7
+ import csv
8
+ import os
9
+ import time
10
+
11
+ from system1_baseline import ask_baseline
12
+ from system2_rag import ask_rag, load_vectorstore, build_vectorstore
13
+
14
+ QUESTIONS_PATH = "data/questions.csv"
15
+ RESULTS_PATH = "results/evaluation_results.csv"
16
+ NUM_QUESTIONS = 30 # increase to 50 when ready
17
+
18
+
19
+ def load_questions():
20
+ questions = []
21
+ with open(QUESTIONS_PATH, encoding="utf-8") as f:
22
+ reader = csv.DictReader(f)
23
+ for row in reader:
24
+ questions.append(row["question"])
25
+ return questions[:NUM_QUESTIONS]
26
+
27
+
28
+ def run_evaluation():
29
+ os.makedirs("results", exist_ok=True)
30
+
31
+ # Load RAG vectorstore
32
+ if not os.path.exists("data/faiss_index"):
33
+ print("Building FAISS index first...")
34
+ vs = build_vectorstore()
35
+ else:
36
+ print("Loading existing FAISS index...")
37
+ vs = load_vectorstore()
38
+
39
+ questions = load_questions()
40
+ print(f"\nEvaluating {len(questions)} questions across 3 systems...\n")
41
+
42
+ results = []
43
+
44
+ for i, q in enumerate(questions, 1):
45
+ print(f"[{i}/{len(questions)}] {q}")
46
+
47
+ r1 = ask_baseline(q)
48
+ time.sleep(0.5) # avoid rate limiting
49
+ r2 = ask_rag(q, vs)
50
+ time.sleep(0.5)
51
+
52
+ # System 3 — placeholder until fine-tuned model is ready
53
+ try:
54
+ from system3_inference import ask_finetuned
55
+ r3 = ask_finetuned(q)
56
+ except Exception:
57
+ r3 = {"answer": "Model not available yet.", "response_time": 0}
58
+
59
+ results.append({
60
+ "id": i,
61
+ "question": q,
62
+ # Baseline
63
+ "baseline_answer": r1["answer"],
64
+ "baseline_time": r1["response_time"],
65
+ "baseline_correctness": "", # Fill manually: 1-5
66
+ "baseline_hallucination": "", # Fill manually: Yes/No
67
+ # RAG
68
+ "rag_answer": r2["answer"],
69
+ "rag_time": r2["response_time"],
70
+ "rag_correctness": "",
71
+ "rag_hallucination": "",
72
+ # Fine-tuned
73
+ "finetuned_answer": r3["answer"],
74
+ "finetuned_time": r3["response_time"],
75
+ "finetuned_correctness": "",
76
+ "finetuned_hallucination": "",
77
+ })
78
+
79
+ print(f" Baseline ({r1['response_time']}s): {r1['answer'][:70]}...")
80
+ print(f" RAG ({r2['response_time']}s): {r2['answer'][:70]}...")
81
+ print()
82
+
83
+ # Save CSV
84
+ with open(RESULTS_PATH, "w", newline="", encoding="utf-8") as f:
85
+ writer = csv.DictWriter(f, fieldnames=results[0].keys())
86
+ writer.writeheader()
87
+ writer.writerows(results)
88
+
89
+ print(f"Saved {len(results)} results to {RESULTS_PATH}")
90
+ print("\nNext step: open the CSV and fill in 'correctness' (1-5) and 'hallucination' (Yes/No) columns manually.")
91
+
92
+
93
+ def print_summary():
94
+ """Print average scores after you have manually filled in the CSV."""
95
+ if not os.path.exists(RESULTS_PATH):
96
+ print("No results file found. Run evaluate.py first.")
97
+ return
98
+
99
+ baseline_scores, rag_scores, ft_scores = [], [], []
100
+ baseline_hall, rag_hall, ft_hall = [], [], []
101
+ baseline_times, rag_times, ft_times = [], [], []
102
+
103
+ with open(RESULTS_PATH, encoding="utf-8") as f:
104
+ reader = csv.DictReader(f)
105
+ for row in reader:
106
+ if row["baseline_correctness"]:
107
+ baseline_scores.append(float(row["baseline_correctness"]))
108
+ baseline_hall.append(1 if row["baseline_hallucination"].strip().lower() == "yes" else 0)
109
+ baseline_times.append(float(row["baseline_time"]) if row["baseline_time"] else 0)
110
+ if row["rag_correctness"]:
111
+ rag_scores.append(float(row["rag_correctness"]))
112
+ rag_hall.append(1 if row["rag_hallucination"].strip().lower() == "yes" else 0)
113
+ rag_times.append(float(row["rag_time"]) if row["rag_time"] else 0)
114
+ if row["finetuned_correctness"]:
115
+ ft_scores.append(float(row["finetuned_correctness"]))
116
+ ft_hall.append(1 if row["finetuned_hallucination"].strip().lower() == "yes" else 0)
117
+ ft_times.append(float(row["finetuned_time"]) if row["finetuned_time"] else 0)
118
+
119
+ def avg(lst): return round(sum(lst) / len(lst), 2) if lst else "N/A"
120
+ def pct(lst): return f"{round(sum(lst)/len(lst)*100)}%" if lst else "N/A"
121
+
122
+ print("\n===== EVALUATION SUMMARY =====")
123
+ print(f"{'Metric':<30} {'Baseline':>12} {'RAG':>12} {'Fine-tuned':>12}")
124
+ print("-" * 66)
125
+ print(f"{'Avg Correctness (1-5)':<30} {avg(baseline_scores):>12} {avg(rag_scores):>12} {avg(ft_scores):>12}")
126
+ print(f"{'Hallucination Rate':<30} {pct(baseline_hall):>12} {pct(rag_hall):>12} {pct(ft_hall):>12}")
127
+ print(f"{'Avg Response Time (s)':<30} {avg(baseline_times):>12} {avg(rag_times):>12} {avg(ft_times):>12}")
128
+
129
+
130
+ if __name__ == "__main__":
131
+ import sys
132
+ if len(sys.argv) > 1 and sys.argv[1] == "summary":
133
+ print_summary()
134
+ else:
135
+ run_evaluation()
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ openai>=1.0.0
2
+ langchain>=0.2.0
3
+ langchain-community>=0.2.0
4
+ faiss-cpu>=1.7.4
5
+ sentence-transformers>=2.7.0
6
+ transformers>=4.40.0
7
+ datasets>=2.19.0
8
+ peft>=0.10.0
9
+ accelerate>=0.29.0
10
+ streamlit>=1.35.0
11
+ pandas>=2.0.0
12
+ python-dotenv>=1.0.0
13
+ tqdm>=4.66.0
system1_baseline.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ from openai import OpenAI
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ client = OpenAI(
9
+ api_key=os.getenv("GROQ_API_KEY"),
10
+ base_url="https://api.groq.com/openai/v1",
11
+ )
12
+
13
+ MODEL = "llama-3.1-8b-instant"
14
+
15
+ SYSTEM_PROMPT = (
16
+ "You are a programming tutor specializing in Data Structures, Algorithms, "
17
+ "and Web Development. Answer questions clearly and concisely."
18
+ )
19
+
20
+
21
+ def ask_baseline(question: str) -> dict:
22
+ start = time.time()
23
+
24
+ response = client.chat.completions.create(
25
+ model=MODEL,
26
+ messages=[
27
+ {"role": "system", "content": SYSTEM_PROMPT},
28
+ {"role": "user", "content": question},
29
+ ],
30
+ max_tokens=300,
31
+ temperature=0.3,
32
+ )
33
+
34
+ elapsed = round(time.time() - start, 2)
35
+ answer = response.choices[0].message.content.strip()
36
+
37
+ return {
38
+ "system": "Baseline",
39
+ "question": question,
40
+ "answer": answer,
41
+ "response_time": elapsed,
42
+ }
43
+
44
+
45
+ if __name__ == "__main__":
46
+ test_q = "What is binary search?"
47
+ print(f"Question: {test_q}\n")
48
+ result = ask_baseline(test_q)
49
+ print(f"Answer:\n{result['answer']}")
50
+ print(f"\nResponse time: {result['response_time']}s")
system2_rag.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ from dotenv import load_dotenv
4
+ from langchain_community.document_loaders import DirectoryLoader, TextLoader
5
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
6
+ from langchain_community.vectorstores import FAISS
7
+ from langchain_huggingface import HuggingFaceEmbeddings
8
+ from openai import OpenAI
9
+
10
+ load_dotenv()
11
+
12
+ client = OpenAI(
13
+ api_key=os.getenv("GROQ_API_KEY"),
14
+ base_url="https://api.groq.com/openai/v1",
15
+ )
16
+
17
+ MODEL = "llama-3.1-8b-instant"
18
+
19
+ DOCS_PATH = "data/docs"
20
+ INDEX_PATH = "data/faiss_index"
21
+ EMBED_MODEL = "all-MiniLM-L6-v2"
22
+
23
+ SYSTEM_PROMPT = (
24
+ "You are a programming tutor. Use only the provided context to answer. "
25
+ "If the answer is not in the context, say 'I don't have that in my knowledge base.'"
26
+ )
27
+
28
+
29
+ def build_vectorstore() -> FAISS:
30
+ print("Loading documents...")
31
+ loader = DirectoryLoader(DOCS_PATH, glob="**/*.txt", loader_cls=TextLoader,
32
+ loader_kwargs={"encoding": "utf-8"})
33
+ documents = loader.load()
34
+ print(f"Loaded {len(documents)} documents.")
35
+
36
+ splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=60)
37
+ chunks = splitter.split_documents(documents)
38
+ print(f"Split into {len(chunks)} chunks.")
39
+
40
+ embeddings = HuggingFaceEmbeddings(model_name=EMBED_MODEL)
41
+ vectorstore = FAISS.from_documents(chunks, embeddings)
42
+ vectorstore.save_local(INDEX_PATH)
43
+ print(f"Vector store saved to {INDEX_PATH}/")
44
+ return vectorstore
45
+
46
+
47
+ def load_vectorstore() -> FAISS:
48
+ embeddings = HuggingFaceEmbeddings(model_name=EMBED_MODEL)
49
+ return FAISS.load_local(INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
50
+
51
+
52
+ def ask_rag(question: str, vectorstore: FAISS) -> dict:
53
+ start = time.time()
54
+
55
+ # Retrieve top-3 relevant chunks
56
+ docs = vectorstore.similarity_search(question, k=3)
57
+ context = "\n\n".join([doc.page_content for doc in docs])
58
+
59
+ prompt = f"""Use the following context to answer the question accurately.
60
+
61
+ Context:
62
+ {context}
63
+
64
+ Question: {question}
65
+ Answer:"""
66
+
67
+ response = client.chat.completions.create(
68
+ model=MODEL,
69
+ messages=[
70
+ {"role": "system", "content": SYSTEM_PROMPT},
71
+ {"role": "user", "content": prompt},
72
+ ],
73
+ max_tokens=300,
74
+ temperature=0.3,
75
+ )
76
+
77
+ elapsed = round(time.time() - start, 2)
78
+ answer = response.choices[0].message.content.strip()
79
+
80
+ return {
81
+ "system": "RAG",
82
+ "question": question,
83
+ "answer": answer,
84
+ "context_used": context,
85
+ "response_time": elapsed,
86
+ }
87
+
88
+
89
+ if __name__ == "__main__":
90
+ if not os.path.exists(INDEX_PATH):
91
+ vs = build_vectorstore()
92
+ else:
93
+ print("Loading existing vector store...")
94
+ vs = load_vectorstore()
95
+
96
+ test_q = "What is binary search?"
97
+ print(f"\nQuestion: {test_q}\n")
98
+ result = ask_rag(test_q, vs)
99
+ print(f"Answer:\n{result['answer']}")
100
+ print(f"\nContext used:\n{result['context_used'][:300]}...")
101
+ print(f"\nResponse time: {result['response_time']}s")
system3_finetune_colab.ipynb ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# System 3 — Fine-Tuning with LoRA\n",
8
+ "**Run this notebook on Google Colab with a T4 GPU.**\n",
9
+ "Runtime → Change runtime type → T4 GPU"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": null,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "!pip install transformers datasets peft accelerate bitsandbytes -q"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": null,
24
+ "metadata": {},
25
+ "outputs": [],
26
+ "source": [
27
+ "# Upload finetune_data.jsonl from your data/ folder before running this cell\n",
28
+ "from google.colab import files\n",
29
+ "uploaded = files.upload() # select data/finetune_data.jsonl"
30
+ ]
31
+ },
32
+ {
33
+ "cell_type": "code",
34
+ "execution_count": null,
35
+ "metadata": {},
36
+ "outputs": [],
37
+ "source": [
38
+ "import json\n",
39
+ "import torch\n",
40
+ "from datasets import Dataset\n",
41
+ "from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling\n",
42
+ "from peft import LoraConfig, get_peft_model, TaskType\n",
43
+ "\n",
44
+ "# Load dataset\n",
45
+ "data = []\n",
46
+ "with open('finetune_data.jsonl') as f:\n",
47
+ " for line in f:\n",
48
+ " data.append(json.loads(line))\n",
49
+ "\n",
50
+ "print(f'Loaded {len(data)} training examples')\n",
51
+ "\n",
52
+ "def format_prompt(example):\n",
53
+ " return f\"### Instruction:\\n{example['instruction']}\\n\\n### Response:\\n{example['output']}\"\n",
54
+ "\n",
55
+ "dataset = Dataset.from_list([{'text': format_prompt(d)} for d in data])\n",
56
+ "print(dataset[0]['text'][:200])"
57
+ ]
58
+ },
59
+ {
60
+ "cell_type": "code",
61
+ "execution_count": null,
62
+ "metadata": {},
63
+ "outputs": [],
64
+ "source": [
65
+ "MODEL_NAME = 'TinyLlama/TinyLlama-1.1B-Chat-v1.0'\n",
66
+ "\n",
67
+ "tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
68
+ "tokenizer.pad_token = tokenizer.eos_token\n",
69
+ "\n",
70
+ "model = AutoModelForCausalLM.from_pretrained(\n",
71
+ " MODEL_NAME,\n",
72
+ " torch_dtype=torch.float16,\n",
73
+ " device_map='auto'\n",
74
+ ")\n",
75
+ "print('Model loaded.')"
76
+ ]
77
+ },
78
+ {
79
+ "cell_type": "code",
80
+ "execution_count": null,
81
+ "metadata": {},
82
+ "outputs": [],
83
+ "source": [
84
+ "lora_config = LoraConfig(\n",
85
+ " task_type=TaskType.CAUSAL_LM,\n",
86
+ " r=8,\n",
87
+ " lora_alpha=32,\n",
88
+ " target_modules=['q_proj', 'v_proj'],\n",
89
+ " lora_dropout=0.05,\n",
90
+ ")\n",
91
+ "model = get_peft_model(model, lora_config)\n",
92
+ "model.print_trainable_parameters()"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "code",
97
+ "execution_count": null,
98
+ "metadata": {},
99
+ "outputs": [],
100
+ "source": [
101
+ "def tokenize(examples):\n",
102
+ " return tokenizer(examples['text'], truncation=True, max_length=512, padding='max_length')\n",
103
+ "\n",
104
+ "tokenized = dataset.map(tokenize, batched=True)\n",
105
+ "\n",
106
+ "training_args = TrainingArguments(\n",
107
+ " output_dir='./finetuned_model',\n",
108
+ " num_train_epochs=5,\n",
109
+ " per_device_train_batch_size=4,\n",
110
+ " save_steps=50,\n",
111
+ " logging_steps=10,\n",
112
+ " learning_rate=2e-4,\n",
113
+ " fp16=True,\n",
114
+ " report_to='none'\n",
115
+ ")\n",
116
+ "\n",
117
+ "trainer = Trainer(\n",
118
+ " model=model,\n",
119
+ " args=training_args,\n",
120
+ " train_dataset=tokenized,\n",
121
+ " data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)\n",
122
+ ")\n",
123
+ "\n",
124
+ "trainer.train()\n",
125
+ "model.save_pretrained('./finetuned_model')\n",
126
+ "tokenizer.save_pretrained('./finetuned_model')\n",
127
+ "print('Fine-tuning complete!')"
128
+ ]
129
+ },
130
+ {
131
+ "cell_type": "code",
132
+ "execution_count": null,
133
+ "metadata": {},
134
+ "outputs": [],
135
+ "source": [
136
+ "# Test the fine-tuned model\n",
137
+ "def ask_finetuned(question):\n",
138
+ " prompt = f'### Instruction:\\n{question}\\n\\n### Response:\\n'\n",
139
+ " inputs = tokenizer(prompt, return_tensors='pt').to(model.device)\n",
140
+ " with torch.no_grad():\n",
141
+ " outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.7,\n",
142
+ " do_sample=True, pad_token_id=tokenizer.eos_token_id)\n",
143
+ " full = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
144
+ " return full.split('### Response:\\n')[-1].strip()\n",
145
+ "\n",
146
+ "print(ask_finetuned('What is binary search?'))"
147
+ ]
148
+ },
149
+ {
150
+ "cell_type": "code",
151
+ "execution_count": null,
152
+ "metadata": {},
153
+ "outputs": [],
154
+ "source": [
155
+ "# Download the fine-tuned model\n",
156
+ "import shutil\n",
157
+ "shutil.make_archive('finetuned_model', 'zip', './finetuned_model')\n",
158
+ "from google.colab import files\n",
159
+ "files.download('finetuned_model.zip')"
160
+ ]
161
+ }
162
+ ],
163
+ "metadata": {
164
+ "kernelspec": {
165
+ "display_name": "Python 3",
166
+ "language": "python",
167
+ "name": "python3"
168
+ },
169
+ "language_info": {
170
+ "name": "python",
171
+ "version": "3.10.0"
172
+ }
173
+ },
174
+ "nbformat": 4,
175
+ "nbformat_minor": 5
176
+ }
system3_inference.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Run inference with the fine-tuned model.
3
+ After running system3_finetune_colab.ipynb on Google Colab:
4
+ 1. Download finetuned_model.zip from Colab
5
+ 2. Unzip into this project root as finetuned_model/
6
+ 3. Then run this file
7
+ """
8
+ import os
9
+ import time
10
+ import torch
11
+ from transformers import AutoTokenizer, AutoModelForCausalLM
12
+ from peft import PeftModel
13
+
14
+ BASE_MODEL = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
15
+ FINETUNED_PATH = "./finetuned_model"
16
+
17
+ _model = None
18
+ _tokenizer = None
19
+
20
+
21
+ def load_model():
22
+ global _model, _tokenizer
23
+ if _model is not None:
24
+ return _model, _tokenizer
25
+
26
+ print("Loading fine-tuned model (this takes ~30s first time)...")
27
+ _tokenizer = AutoTokenizer.from_pretrained(FINETUNED_PATH)
28
+ base = AutoModelForCausalLM.from_pretrained(BASE_MODEL, torch_dtype=torch.float32)
29
+ _model = PeftModel.from_pretrained(base, FINETUNED_PATH)
30
+ _model.eval()
31
+ print("Fine-tuned model ready.")
32
+ return _model, _tokenizer
33
+
34
+
35
+ def ask_finetuned(question: str) -> dict:
36
+ if not os.path.exists(FINETUNED_PATH):
37
+ return {
38
+ "system": "Fine-tuned",
39
+ "question": question,
40
+ "answer": "Fine-tuned model not found. Run system3_finetune_colab.ipynb on Google Colab first, then unzip finetuned_model.zip here.",
41
+ "response_time": 0,
42
+ }
43
+
44
+ model, tokenizer = load_model()
45
+ start = time.time()
46
+
47
+ prompt = f"### Instruction:\n{question}\n\n### Response:\n"
48
+ inputs = tokenizer(prompt, return_tensors="pt")
49
+
50
+ with torch.no_grad():
51
+ outputs = model.generate(
52
+ **inputs,
53
+ max_new_tokens=100,
54
+ do_sample=False,
55
+ pad_token_id=tokenizer.eos_token_id,
56
+ )
57
+
58
+ full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
59
+ answer = full_output.split("### Response:\n")[-1].strip()
60
+ elapsed = round(time.time() - start, 2)
61
+
62
+ return {
63
+ "system": "Fine-tuned",
64
+ "question": question,
65
+ "answer": answer,
66
+ "response_time": elapsed,
67
+ }
68
+
69
+
70
+ if __name__ == "__main__":
71
+ test_q = "What is binary search?"
72
+ print(f"Question: {test_q}\n")
73
+ result = ask_finetuned(test_q)
74
+ print(f"Answer:\n{result['answer']}")
75
+ print(f"\nResponse time: {result['response_time']}s")