Compare commits

...

49 Commits

Author SHA1 Message Date
JamesFlare1212
a109046498 solve hw-9 2025-04-15 22:10:48 -04:00
Jidong Xiao
9ec5d3d32c fixing the path 2025-04-15 22:10:48 -04:00
Jidong Xiao
727b4fccd0 updated with pointers 2025-04-15 22:10:48 -04:00
Jidong Xiao
6670134276 not 8, but 6 2025-04-15 22:10:48 -04:00
Jidong Xiao
7eab384626 renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
728024087b renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
81a4824b0a renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
999bf68f77 renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
06c33e577b revising notes 2025-04-15 22:10:48 -04:00
Jidong Xiao
34d5900d2c adding polymorphism notes 2025-04-15 22:10:48 -04:00
Jidong Xiao
f858d57e31 format the numbers 2025-04-15 22:10:48 -04:00
Jidong Xiao
8102db4dac adding info about memory size for virtual functions 2025-04-15 22:10:48 -04:00
Jidong Xiao
63ddc1cc22 more about poly 2025-04-15 22:10:48 -04:00
Jidong Xiao
12e95dedfc re numbering 2025-04-15 22:10:48 -04:00
Jidong Xiao
114bc00e8b re-formatting the diamond 2025-04-15 22:10:48 -04:00
Jidong Xiao
33cf2cbeaa add pre 2025-04-15 22:10:48 -04:00
Jidong Xiao
e2c9cf9566 adding the diamond problem 2025-04-15 22:10:48 -04:00
Jidong Xiao
3514476080 show the importance of member initializer lists 2025-04-15 22:10:48 -04:00
Jidong Xiao
58b8a67d55 removing outdated content 2025-04-15 22:10:48 -04:00
Jidong Xiao
456cdb961a renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
628104fa39 updating the ordering rules for spring 26 semester 2025-04-15 22:10:48 -04:00
Jidong Xiao
877576afc5 include the pre order animation 2025-04-15 22:10:48 -04:00
Jidong Xiao
03021491c5 adding pre order animation 2025-04-15 22:10:48 -04:00
Jidong Xiao
5f34a80e42 fixing konvas path 2025-04-15 22:10:48 -04:00
Jidong Xiao
73b0ece069 check current first 2025-04-15 22:10:48 -04:00
Jidong Xiao
53549accd0 fixing the file paths 2025-04-15 22:10:48 -04:00
Samson Kempiak
7682f722f1 edit hw readme to add better line breaks 2025-04-15 22:10:48 -04:00
Jidong Xiao
3cf7288c34 adding post order animation 2025-04-15 22:10:48 -04:00
Jidong Xiao
302a2f176d adding morris post order 2025-04-15 22:10:48 -04:00
Jidong Xiao
bb38a699a0 include 2025-04-15 22:10:48 -04:00
Jidong Xiao
bfc95ce254 adding name hiding code 2025-04-15 22:10:48 -04:00
Jidong Xiao
89609d0937 adding name hiding example 2025-04-15 22:10:48 -04:00
Jidong Xiao
107a578154 max heap, not min heap 2025-04-15 22:10:48 -04:00
Jidong Xiao
1c454d2ecd not the push, but the sort 2025-04-15 22:10:48 -04:00
Jidong Xiao
425b39cf7f adding heap sort animation 2025-04-15 22:10:48 -04:00
Jidong Xiao
8e49abd24a adding heap sort animation 2025-04-15 22:10:48 -04:00
Jidong Xiao
d8190eeea8 update test 3 2025-04-15 22:10:48 -04:00
Jidong Xiao
235168710e about multi level inheritance 2025-04-15 22:10:48 -04:00
Jidong Xiao
28a8f7d285 adding student test 5 2025-04-15 22:10:48 -04:00
Jidong Xiao
110f6cc319 adding exercise 2 2025-04-15 22:10:48 -04:00
Jidong Xiao
7b9c74b098 adding exercise 1 2025-04-15 22:10:48 -04:00
Jidong Xiao
34f972e3f4 destructor 2025-04-15 22:10:48 -04:00
Jidong Xiao
7f7b936341 adding student program 2025-04-15 22:10:48 -04:00
Jidong Xiao
21fa2c03de renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
9096194d90 adding inheritance notes 2025-04-15 22:10:48 -04:00
Jidong Xiao
a46c3caa59 clarification 2025-04-15 22:10:48 -04:00
Jidong Xiao
1311832955 clarify the rules 2025-04-15 22:10:48 -04:00
Jidong Xiao
0f6391d57a renaming 2025-04-15 22:10:48 -04:00
Jidong Xiao
ae3b122fb1 adding hw10 2025-04-15 22:10:48 -04:00
103 changed files with 5904 additions and 664 deletions

16
.vscode/launch.json vendored
View File

@@ -135,6 +135,22 @@
"MIMode": "gdb", "MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb", "miDebuggerPath": "/usr/bin/gdb",
"preLaunchTask": "C/C++: g++ build active file" "preLaunchTask": "C/C++: g++ build active file"
},
{
"name": "nytrends",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [
"inputs/input_large9.json",
"output.txt",
"hashtag"
],
"cwd": "${fileDirname}",
"environment": [],
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"preLaunchTask": "C/C++: g++ build active file"
} }
] ]
} }

View File

@@ -78,6 +78,10 @@
"unordered_set": "cpp", "unordered_set": "cpp",
"regex": "cpp", "regex": "cpp",
"cinttypes": "cpp", "cinttypes": "cpp",
"__node_handle": "cpp" "__node_handle": "cpp",
"shared_mutex": "cpp",
"cfenv": "cpp",
"locale": "cpp",
"filesystem": "cpp"
} }
} }

View File

@@ -0,0 +1,439 @@
//AUTHOR: Chloe Lee
// initial array to be sorted
let array = [5, 3, 8, 1, 9, 4];
let currentStep = 0;
let animationSteps = [];
// heap sort code
const codeLines = [
"void heapSort(int arr[], int n) {",
" for (int i = n / 2 - 1; i >= 0; i--)",
" heapify(arr, n, i);",
" for (int i = n - 1; i > 0; i--) {",
" std::swap(arr[0], arr[i]);",
" heapify(arr, i, 0);",
" }",
"}",
"",
"void heapify(int arr[], int n, int i) {",
" int largest = i;",
" int left = 2 * i + 1;",
" int right = 2 * i + 2;",
" if (left < n && arr[left] > arr[largest])",
" largest = left;",
" if (right < n && arr[right] > arr[largest])",
" largest = right;",
" if (largest != i) {",
" std::swap(arr[i], arr[largest]);",
" heapify(arr, n, largest);",
" }",
"}"
];
function initialize() {
// Display code with line numbers
const codeElement = document.getElementById('code');
let codeHTML = '';
codeLines.forEach((line, index) => {
// how much padding to add
const paddingLeft = line.startsWith(' ') ? '20px' : '0';
// add the line number for the code
codeHTML += `<div id="code-line-${index}">`;
if (!line.trim()) {
codeHTML += '&nbsp;';
} else {
const lineNum = index;
// align single digit lines better
const formattedLineNum = lineNum < 10 ? ` ${lineNum}` : lineNum;
codeHTML += `${formattedLineNum}. ${line}`;
}
codeHTML += '</div>';
});
codeElement.innerHTML = codeHTML;
generateAnimationSteps();
// initial array display
updateArrayDisplay();
// initial heap tree
drawHeapTree();
// set up next step buttons
document.getElementById('arrayNextBtn').addEventListener('click', nextStep);
document.getElementById('treeNextBtn').addEventListener('click', nextStep);
}
// highlight a specific line of code
function highlightLine(lineIndex) {
// reset all lines
for (let i = 0; i < codeLines.length; i++) {
const line = document.getElementById(`code-line-${i}`);
if (line) {
line.style.backgroundColor = 'transparent';
line.style.fontWeight = 'normal';
}
}
// highlight the specified line
const highlightedLine = document.getElementById(`code-line-${lineIndex}`);
if (highlightedLine) {
highlightedLine.style.fontWeight = 'bold';
}
}
// update the array display
function updateArrayDisplay() {
const arrayContainer = document.getElementById('arrayContainer');
const descriptionElement = document.getElementById('arrayDescription');
arrayContainer.innerHTML = '';
// create array visualization
array.forEach((value, index) => {
const elementContainer = document.createElement('div');
elementContainer.style.display = 'inline-block';
elementContainer.style.marginRight = '5px';
const element = document.createElement('div');
element.className = 'array-element';
// highlight right lines, and nodes based on current step
if (animationSteps[currentStep] && animationSteps[currentStep].comparing && animationSteps[currentStep].comparing.includes(index)) {
element.classList.add('highlight');
} else if (animationSteps[currentStep] && animationSteps[currentStep].swapping && animationSteps[currentStep].swapping.includes(index)) {
element.classList.add('swap');
} else if (animationSteps[currentStep] && animationSteps[currentStep].sorted && animationSteps[currentStep].sorted.includes(index)) {
element.classList.add('done');
}
element.textContent = value;
elementContainer.appendChild(element);
// add index below
const indexElement = document.createElement('div');
indexElement.className = 'array-index';
indexElement.textContent = index;
elementContainer.appendChild(indexElement);
arrayContainer.appendChild(elementContainer);
});
// update the description
if (animationSteps[currentStep] && animationSteps[currentStep].description) {
descriptionElement.textContent = animationSteps[currentStep].description;
} else {
descriptionElement.textContent = '';
}
}
// draw heap tree on the canvas
function drawHeapTree() {
const canvas = document.getElementById('treeCanvas');
// canvas with context
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
const heapSize = animationSteps[currentStep] ? (animationSteps[currentStep].heapSize || array.length) : array.length;
// tree node positions (x, y coordinates for each node)
const positions = [
[250, 50], // index 0
[150, 120], // index 1
[350, 120], // index 2
[100, 190], // index 3
[200, 190], // index 4
[300, 190], // index 5
];
// draw edges/arrows
for (let i = 0; i < Math.min(array.length, positions.length); i++) {
if (i > 0 && i < heapSize) {
const parentIndex = Math.floor((i - 1) / 2);
if (parentIndex < heapSize) {
// starting point for the line from parent to child
const startX = positions[parentIndex][0];
// bottom of parent node
const startY = positions[parentIndex][1] + 20;
const endX = positions[i][0];
// top of child node
const endY = positions[i][1] - 20;
// draw the lines for the arrows
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
// draw arrows
const angle = Math.atan2(endY - startY, endX - startX);
const arrowSize = 10;
ctx.beginPath();
ctx.moveTo(endX, endY);
ctx.lineTo(
endX - arrowSize * Math.cos(angle - Math.PI / 6),
endY - arrowSize * Math.sin(angle - Math.PI / 6)
);
ctx.lineTo(
endX - arrowSize * Math.cos(angle + Math.PI / 6),
endY - arrowSize * Math.sin(angle + Math.PI / 6)
);
ctx.closePath();
ctx.fillStyle = '#555';
ctx.fill();
}
}
}
// !! subject to change !!
for (let i = 0; i < Math.min(array.length, positions.length); i++) {
if (i < heapSize) {
// determine node color based on current operation
let nodeColor = '#ddd';
let borderColor = '#888';
if (animationSteps[currentStep] && animationSteps[currentStep].comparing &&
animationSteps[currentStep].comparing.includes(i)) {
nodeColor = '#ffcf4d';
borderColor = '#e8a800';
} else if (animationSteps[currentStep] && animationSteps[currentStep].swapping &&
animationSteps[currentStep].swapping.includes(i)) {
nodeColor = '#ff7675';
borderColor = '#d63031';
} else if (animationSteps[currentStep] && animationSteps[currentStep].sorted &&
animationSteps[currentStep].sorted.includes(i)) {
nodeColor = '#81ecec';
borderColor = '#00cec9';
}
// draw rectangle with round corners
const width = 40;
const height = 40;
const x = positions[i][0] - width/2;
const y = positions[i][1] - height/2;
const radius = 5;
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
// style rectangle
ctx.fillStyle = nodeColor;
ctx.fill();
ctx.strokeStyle = borderColor;
ctx.lineWidth = 2;
ctx.stroke();
// draw text in the heap tree
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = 'bold 16px Times New Roman';
ctx.fillText(array[i].toString(), positions[i][0], positions[i][1]);
}
}
}
function generateAnimationSteps() {
animationSteps = [];
let tempArray = [...array];
const n = tempArray.length;
animationSteps.push({
array: [...tempArray],
line: 0,
description: 'Starting heap sort with array [' + tempArray.join(', ') + ']',
heapSize: n
});
// build max-heap
animationSteps.push({
array: [...tempArray],
line: 2,
description: 'Building max heap',
heapSize: n
});
let sortedIndices = [];
for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
animationSteps.push({
array: [...tempArray],
line: 3,
description: `Heapifying subtree rooted at index ${i}`,
heapSize: n,
comparing: [i]
});
heapifyWithAnimation(tempArray, n, i, sortedIndices);
}
animationSteps.push({
array: [...tempArray],
line: 6,
description: 'Max heap built. Extracting elements one by one',
heapSize: n
});
for (let i = n - 1; i > 0; i--) {
animationSteps.push({
array: [...tempArray],
line: 8,
description: `Swapping root (${tempArray[0]}) with element at index ${i} (${tempArray[i]})`,
heapSize: i + 1,
swapping: [0, i]
});
[tempArray[0], tempArray[i]] = [tempArray[i], tempArray[0]];
// this index is sorted
sortedIndices.push(i);
animationSteps.push({
array: [...tempArray],
line: 8,
description: `Element ${tempArray[i]} is now in its correct position`,
heapSize: i,
sorted: [...sortedIndices]
});
animationSteps.push({
array: [...tempArray],
line: 11,
description: `Heapifying reduced heap (size ${i})`,
heapSize: i
});
heapifyWithAnimation(tempArray, i, 0, sortedIndices);
}
sortedIndices.push(0);
animationSteps.push({
array: [...tempArray],
line: 13,
description: 'Heap sort complete! Array is now sorted: [' + tempArray.join(', ') + ']',
heapSize: n,
sorted: sortedIndices
});
}
// heapify function that records animation steps
function heapifyWithAnimation(arr, n, i, sortedIndices) {
let largest = i;
const left = 2 * i + 1;
const right = 2 * i + 2;
animationSteps.push({
array: [...arr],
line: 16,
description: `Heapifying at index ${i}`,
heapSize: n,
comparing: [i],
sorted: sortedIndices ? [...sortedIndices] : []
});
// compare with left child
if (left < n) {
animationSteps.push({
array: [...arr],
line: 21,
description: `Comparing ${arr[i]} (index ${i}) with left child ${arr[left]} (index ${left})`,
heapSize: n,
comparing: [i, left],
sorted: sortedIndices ? [...sortedIndices] : []
});
if (arr[left] > arr[largest]) {
largest = left;
animationSteps.push({
array: [...arr],
line: 22,
description: `Left child ${arr[left]} is larger than current largest ${arr[i]}`,
heapSize: n,
comparing: [largest],
sorted: sortedIndices ? [...sortedIndices] : []
});
}
}
// compare with right child
if (right < n) {
animationSteps.push({
array: [...arr],
line: 25,
description: `Comparing ${arr[largest]} (index ${largest}) with right child ${arr[right]} (index ${right})`,
heapSize: n,
comparing: [largest, right],
sorted: sortedIndices ? [...sortedIndices] : []
});
if (arr[right] > arr[largest]) {
largest = right;
animationSteps.push({
array: [...arr],
line: 26,
description: `Right child ${arr[right]} is larger than current largest ${arr[largest === i ? arr[i] : arr[left]]}`,
heapSize: n,
comparing: [largest],
sorted: sortedIndices ? [...sortedIndices] : []
});
}
}
// if largest is not root, swap and heapify
if (largest !== i) {
animationSteps.push({
array: [...arr],
line: 30,
description: `Swapping ${arr[i]} (index ${i}) with ${arr[largest]} (index ${largest})`,
heapSize: n,
swapping: [i, largest],
sorted: sortedIndices ? [...sortedIndices] : []
});
[arr[i], arr[largest]] = [arr[largest], arr[i]];
animationSteps.push({
array: [...arr],
line: 33,
description: `Recursively heapifying the affected subtree rooted at index ${largest}`,
heapSize: n,
comparing: [largest],
sorted: sortedIndices ? [...sortedIndices] : []
});
heapifyWithAnimation(arr, n, largest, sortedIndices);
}
}
function nextStep() {
if (currentStep < animationSteps.length - 1) {
currentStep++;
// update the array to current state
if (animationSteps[currentStep] && animationSteps[currentStep].array) {
array = [...animationSteps[currentStep].array];
}
// highlight code line
if (animationSteps[currentStep] && animationSteps[currentStep].line !== undefined) {
highlightLine(animationSteps[currentStep].line);
}
// update visuals
updateArrayDisplay();
drawHeapTree();
}
}
// initialize on page load
window.onload = initialize;

View File

@@ -0,0 +1,142 @@
<!-- AUTHOR: Chloe Lee -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Heap Sort Visualization</title>
<script src="../../konva.js"></script>
<style>
body {
font-family: 'Times New Roman', Times, serif, sans-serif;
padding: 20px;
background-color: white;
}
h1 {
font-size: 24px;
margin-bottom: 10px;
}
p {
margin-bottom: 20px;
}
.container {
display: flex;
flex-direction: column;
gap: 30px;
max-width: 1200px;
margin: 0 auto;
}
.code-block {
background-color: #e6e6e6;
border-radius: 10px;
padding: 20px;
box-shadow: 5px 5px 10px rgba(0,0,0,0.2);
width: 100%;
max-width: 600px;
margin: 0 auto;
}
.visualization-container {
display: flex;
justify-content: space-between;
gap: 20px;
}
.visualization-box {
background-color: #e6e6e6;
border-radius: 10px;
padding: 20px;
box-shadow: 5px 5px 10px rgba(0,0,0,0.2);
flex: 1;
min-height: 200px;
position: relative;
}
button.next-step {
font-family: 'Times New Roman', Times, serif, sans-serif;
margin: 0;
padding: 5px 10px;
font-size: 14px;
background-color: #f0f0f0;
border: 1px solid #999;
border-radius: 3px;
cursor: pointer;
position: absolute;
left: 20px;
top: 20px;
}
.array-container {
display: flex;
justify-content: center;
margin-top: 60px;
}
.array-element {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
background-color: #ddd;
border: 2px solid #888;
border-radius: 5px;
margin: 0 2px;
font-weight: bold;
}
.array-index {
text-align: center;
margin-top: 5px;
font-size: 14px;
}
.highlight {
background-color: #ffcf4d;
border-color: #e8a800;
}
.swap {
background-color: #ff7675;
border-color: #d63031;
}
.done {
background-color: #81ecec;
border-color: #00cec9;
}
.description {
margin-top: 20px;
text-align: center;
font-size: 14px;
height: 40px;
}
#code{
margin: 0;
font-family: monospace;
font-size: 14px;
}
#treeCanvas{
margin-top: 30px;
}
</style>
</head>
<body>
<div class="container">
<div>
<h1>Heap Sort Visualization</h1>
<p>This animation shows how the heap sort algorithm works by building a max heap and then extracting elements one by one. Click the "next step" button to run the animation.</p>
</div>
<!-- block of C++ code-->
<div class="code-block" id="code-block">
<pre id="code"></pre>
</div>
<div class="visualization-container">
<!-- array goes here -->
<div class="visualization-box">
<button id="arrayNextBtn" class="next-step">Next Step</button>
<div id="arrayContainer" class="array-container"></div>
<div id="arrayDescription" class="description"></div>
</div>
<!-- heap tree goes here -->
<div class="visualization-box">
<button id="treeNextBtn" class="next-step">Next Step</button>
<canvas id="treeCanvas" width="500" height="300"></canvas>
</div>
</div>
</div>
<script src="heap-sort.js"></script>
</body>
</html>

View File

@@ -0,0 +1,594 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Morris PostOrder Traversal Animation & Code Display</title>
<style>
html, body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
h1 { text-align: center; margin-top: 10px; margin-bottom: 5px; }
p { text-align: center; margin: 5px 0 15px; }
.visualization-container {
display: flex;
justify-content: space-between;
max-width: 1200px;
margin: 0 auto;
height: 88vh;
}
.code-container {
background-color: #f0f0f0;
border-radius: 10px;
padding: 15px;
width: 48%;
overflow-y: auto;
height: 88vh;
font-size: 15px;
}
pre {
margin: 0;
background-color: #f0f0f0;
overflow-x: auto;
}
code {
font-family: Consolas, "Courier New", monospace;
white-space: pre;
}
.line-number {
color: #666;
display: inline-block;
min-width: 30px;
}
.tree-container {
background-color: #f0f0f0;
border-radius: 10px;
padding: 15px;
width: 48%;
height: 88vh;
position: relative;
display: flex;
flex-direction: column;
}
#nextStep {
margin: 10px auto;
padding: 8px 15px;
font-size: 16px;
width: fit-content;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
display: block;
}
#nextStep:hover { background-color: #45a049; }
#nextStep:disabled { background-color: #cccccc; cursor: not-allowed; }
#container { flex-grow: 1; width: 100%; }
.explanation {
margin-top: 10px;
font-size: 14px;
padding: 10px;
background-color: #e8e8e8;
border-radius: 5px;
}
.color-key {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 5px;
padding: 8px;
background-color: #e8e8e8;
border-radius: 5px;
}
.color-item {
display: flex;
align-items: center;
margin-right: 10px;
font-size: 13px;
}
.color-box {
width: 16px;
height: 16px;
border-radius: 3px;
margin-right: 4px;
border: 1px solid #888;
}
.color-arrow {
position: relative;
width: 24px;
height: 3px;
margin-right: 4px;
}
.color-arrow:after {
content: "";
position: absolute;
right: -2px;
top: -4px;
border-left: 6px solid;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
}
.dotted-line {
width: 24px;
height: 0;
border-top: 2px dashed #ff5722;
margin-right: 4px;
}
</style>
<!-- Load Konva.js -->
<script src="https://cdn.jsdelivr.net/npm/konva@8.3.13/konva.min.js"></script>
</head>
<body>
<h1>Morris Post-Order Traversal Visualization</h1>
<p>This animation shows how the Morris post-order traversal algorithm works without using a stack or recursion. Click the "Next Step" button to run the animation.</p>
<div class="visualization-container">
<div class="code-container">
<pre><code><span class="line-number">0.</span> <span style="color:blue">void</span> postorderTraversal(<span style="color:blue">TreeNode</span>* root) {
<span class="line-number">1.</span> <span style="color:blue">TreeNode</span>* current = root;
<span class="line-number">2.</span> <span style="color:blue">TreeNode</span>* rightmost;
<span class="line-number">3.</span> <span style="color:blue">while</span> (current != <span style="color:blue">nullptr</span>) {
<span class="line-number">4.</span> <span style="color:blue">if</span> (current->left != <span style="color:blue">nullptr</span>) {
<span class="line-number">5.</span> rightmost = current->left;
<span class="line-number">6.</span> <span style="color:blue">while</span> (rightmost->right != <span style="color:blue">nullptr</span> && rightmost->right != current) {
<span class="line-number">7.</span> rightmost = rightmost->right;
<span class="line-number">8.</span> }
<span class="line-number">9.</span> <span style="color:blue">if</span> (rightmost->right == <span style="color:blue">nullptr</span>) {
<span class="line-number">10.</span> rightmost->right = current;
<span class="line-number">11.</span> current = current->left;
<span class="line-number">12.</span> } <span style="color:blue">else</span> {
<span class="line-number">13.</span> rightmost->right = <span style="color:blue">nullptr</span>;
<span class="line-number">14.</span> reverseTraverseRightEdge(current->left);
<span class="line-number">15.</span> current = current->right;
<span class="line-number">16.</span> }
<span class="line-number">17.</span> } <span style="color:blue">else</span> {
<span class="line-number">18.</span> current = current->right;
<span class="line-number">19.</span> }
<span class="line-number">20.</span> }
<span class="line-number">21.</span> reverseTraverseRightEdge(root); <span style="color:green">// final right edge</span>
<span class="line-number">22.</span> <span style="color:blue">return</span>;
<span class="line-number">23.</span> }
<span class="line-number">24.</span>
<span class="line-number">25.</span> <span style="color:blue">TreeNode</span>* reverse(<span style="color:blue">TreeNode</span>* head) {
<span class="line-number">26.</span> <span style="color:blue">TreeNode</span>* prev = <span style="color:blue">nullptr</span>;
<span class="line-number">27.</span> <span style="color:blue">TreeNode</span>* next = <span style="color:blue">nullptr</span>;
<span class="line-number">28.</span> <span style="color:blue">while</span> (head != <span style="color:blue">nullptr</span>) {
<span class="line-number">29.</span> next = head->right;
<span class="line-number">30.</span> head->right = prev;
<span class="line-number">31.</span> prev = head;
<span class="line-number">32.</span> head = next;
<span class="line-number">33.</span> }
<span class="line-number">34.</span> <span style="color:blue">return</span> prev;
<span class="line-number">35.</span> }
<span class="line-number">36.</span>
<span class="line-number">37.</span> <span style="color:blue">void</span> reverseTraverseRightEdge(<span style="color:blue">TreeNode</span>* head) {
<span class="line-number">38.</span> <span style="color:blue">TreeNode</span>* tail = reverse(head);
<span class="line-number">39.</span> <span style="color:blue">TreeNode</span>* current = tail;
<span class="line-number">40.</span> <span style="color:blue">while</span> (current != <span style="color:blue">nullptr</span>) {
<span class="line-number">41.</span> std::cout << current->val << " ";
<span class="line-number">42.</span> current = current->right;
<span class="line-number">43.</span> }
<span class="line-number">44.</span> reverse(tail); <span style="color:green">// restore structure</span>
<span class="line-number">45.</span> }</code></pre>
</div>
<div class="tree-container">
<div class="color-key">
<div class="color-item">
<div class="color-box" style="background-color: #ffeb3b;"></div>
<span>Current Node</span>
</div>
<div class="color-item">
<div class="color-box" style="background-color: #2196f3;"></div>
<span>Rightmost Node</span>
</div>
<div class="color-item">
<div class="color-box" style="background-color: #4caf50;"></div>
<span>Visited Node</span>
</div>
<div class="color-item">
<div class="color-box" style="background-color: #03a9f4;"></div>
<span>Thread Created</span>
</div>
<div class="color-item">
<div class="color-arrow" style="background-color: #888;"></div>
<span>Tree Edge</span>
</div>
<div class="color-item">
<div class="dotted-line"></div>
<span>Thread Edge</span>
</div>
<div class="color-item">
<div class="color-arrow" style="background-color: #e91e63;"></div>
<span>Reversed Edge</span>
</div>
</div>
<button id="nextStep">Next Step</button>
<div id="container"></div>
<div class="explanation" id="treeExplanation">
Post-order traversal output: <span id="output"></span>
</div>
</div>
</div>
<script>
// Wait for the "Next Step" button click.
function waitForNext() {
return new Promise(resolve => {
const btn = document.getElementById('nextStep');
btn.disabled = false;
btn.onclick = () => {
btn.disabled = true;
resolve();
};
});
}
// Append text to the output.
function appendOutput(text) {
const out = document.getElementById('output');
out.innerText += text;
}
// Update the explanation text.
function updateExplanation(text) {
document.getElementById('treeExplanation').innerHTML =
'Post-order traversal output: <span id="output">' +
document.getElementById('output').innerText + '</span><br>' + text;
}
// Tree node class using Konva for visualization.
class TreeNode {
constructor(val, x, y) {
this.val = val;
this.left = null;
this.right = null;
this.x = x;
this.y = y;
this.shape = new Konva.Rect({
x: x - 20,
y: y - 20,
width: 40,
height: 40,
fill: 'white',
stroke: '#888',
strokeWidth: 2,
cornerRadius: 4
});
this.label = new Konva.Text({
x: x - 7,
y: y - 10,
text: String(val),
fontSize: 20,
fontFamily: 'Arial',
fill: 'black'
});
this.leftEdge = null;
this.rightEdge = null;
this.threadEdge = null;
this.reversedEdges = [];
}
}
// Initialize Konva stage and layer.
const stage = new Konva.Stage({
container: 'container',
width: document.querySelector('.tree-container').clientWidth - 30,
height: document.querySelector('.tree-container').clientHeight - 140
});
const layer = new Konva.Layer();
stage.add(layer);
// Calculate connection points for arrow drawing.
function calculateConnectionPoints(fromNode, toNode) {
const nodeRadius = 20;
const dx = toNode.x - fromNode.x;
const dy = toNode.y - fromNode.y;
const angle = Math.atan2(dy, dx);
return {
fromX: fromNode.x + nodeRadius * Math.cos(angle),
fromY: fromNode.y + nodeRadius * Math.sin(angle),
toX: toNode.x - nodeRadius * Math.cos(angle),
toY: toNode.y - nodeRadius * Math.sin(angle)
};
}
// Create an arrow between two nodes.
function createArrow(fromNode, toNode, color = '#888', dashed = false) {
const points = calculateConnectionPoints(fromNode, toNode);
const arrow = new Konva.Arrow({
points: [points.fromX, points.fromY, points.toX, points.toY],
pointerLength: 10,
pointerWidth: 8,
fill: color,
stroke: color,
strokeWidth: dashed ? 1 : 2,
dashEnabled: dashed,
dash: dashed ? [5, 5] : null
});
layer.add(arrow);
return arrow;
}
// Draw an edge between two nodes.
function drawEdge(parent, child, isLeft = true) {
const arrow = createArrow(parent, child);
layer.add(arrow);
if (isLeft) { parent.leftEdge = arrow; }
else { parent.rightEdge = arrow; }
arrow.moveToBottom();
return arrow;
}
// Create and remove thread edges.
function createThreadEdge(fromNode, toNode) {
if (fromNode.threadEdge) { fromNode.threadEdge.destroy(); }
const threadArrow = createArrow(fromNode, toNode, '#ff5722', true);
fromNode.threadEdge = threadArrow;
threadArrow.moveToTop();
layer.draw();
return threadArrow;
}
function removeThreadEdge(node) {
if (node && node.threadEdge) {
node.threadEdge.destroy();
node.threadEdge = null;
layer.draw();
}
}
// Highlight and unhighlight a node.
function highlightNode(node, color = '#8bc34a') {
if (node && node.shape) { node.shape.to({ fill: color, duration: 0.25 }); }
}
function unhighlightNode(node, color = 'white') {
if (node && node.shape) { node.shape.to({ fill: color, duration: 0.25 }); }
}
// Calculate tree layout.
const stageWidth = stage.width();
const stageHeight = stage.height();
const centerX = stageWidth / 2;
const topY = 40;
const levelHeight = stageHeight / 4;
// Build the 9-node binary tree.
const node1 = new TreeNode(1, centerX, topY);
const node2 = new TreeNode(2, centerX - stageWidth/4, topY + levelHeight);
const node3 = new TreeNode(3, centerX + stageWidth/4, topY + levelHeight);
const node4 = new TreeNode(4, centerX - stageWidth/3, topY + 2*levelHeight);
const node5 = new TreeNode(5, centerX - stageWidth/6, topY + 2*levelHeight);
const node6 = new TreeNode(6, centerX - stageWidth/4, topY + 3*levelHeight);
const node7 = new TreeNode(7, centerX - stageWidth/12, topY + 3*levelHeight);
const node8 = new TreeNode(8, centerX + stageWidth/3, topY + 2*levelHeight);
const node9 = new TreeNode(9, centerX + stageWidth/4, topY + 3*levelHeight);
node1.left = node2;
node1.right = node3;
node2.left = node4;
node2.right = node5;
node5.left = node6;
node5.right = node7;
node3.right = node8;
node8.left = node9;
const nodes = [node1, node2, node3, node4, node5, node6, node7, node8, node9];
nodes.forEach(n => {
layer.add(n.shape);
layer.add(n.label);
});
drawEdge(node1, node2, true);
drawEdge(node1, node3, false);
drawEdge(node2, node4, true);
drawEdge(node2, node5, false);
drawEdge(node5, node6, true);
drawEdge(node5, node7, false);
drawEdge(node3, node8, false);
drawEdge(node8, node9, true);
nodes.forEach(n => {
n.shape.moveToTop();
n.label.moveToTop();
});
layer.draw();
// Visualize the reversal process.
async function visualizeReversal(head) {
const connections = [];
let current = head;
while (current !== null && current.right !== null) {
connections.push({ from: current, to: current.right });
current = current.right;
}
for (const conn of connections) {
if (conn.from.rightEdge) {
conn.from.rightEdge.destroy();
conn.from.rightEdge = null;
}
}
let prev = null;
current = head;
while (current !== null) {
const next = current.right;
if (prev !== null) {
const reversedEdge = createArrow(current, prev, '#e91e63');
current.reversedEdges.push(reversedEdge);
reversedEdge.moveToTop();
}
prev = current;
current = next;
layer.draw();
await waitForNext();
}
return prev;
}
// Restore the original tree structure.
async function visualizeRestore(head) {
nodes.forEach(node => {
if (node.reversedEdges) {
node.reversedEdges.forEach(edge => edge.destroy());
node.reversedEdges = [];
}
});
let current = head;
while (current !== null && current.right !== null) {
if (!current.rightEdge) {
current.rightEdge = createArrow(current, current.right, '#888');
current.rightEdge.moveToBottom();
}
current = current.right;
}
nodes.forEach(n => {
n.shape.moveToTop();
n.label.moveToTop();
});
layer.draw();
}
// Reverse a chain of right pointers.
async function reverseChain(head) {
updateExplanation("Visualizing the reversal of pointers");
await waitForNext();
const reversedHead = await visualizeReversal(head);
let prev = null, curr = head, next;
while (curr !== null) {
next = curr.right;
curr.right = prev;
prev = curr;
curr = next;
}
return prev;
}
// Restore the chain.
async function restoreChain(head) {
updateExplanation("Visualizing the restoration of original pointers");
await waitForNext();
await visualizeRestore(head);
let prev = null, curr = head, next;
while (curr !== null) {
next = curr.right;
curr.right = prev;
prev = curr;
curr = next;
}
return prev;
}
// Reverse traverse the right edge of a subtree.
async function reverseTraverseRightEdge(head) {
if (!head) return;
updateExplanation("Reversing the right edge starting from node " + head.val);
await waitForNext();
let tail = await reverseChain(head);
layer.draw();
updateExplanation("Visiting nodes along the reversed right edge");
let cur = tail;
while (cur !== null) {
highlightNode(cur, '#4caf50');
layer.draw();
appendOutput(cur.val + " ");
updateExplanation("Visited node: " + cur.val);
await waitForNext();
unhighlightNode(cur, 'white');
layer.draw();
cur = cur.right;
}
updateExplanation("Restoring the original right edge");
await waitForNext();
await restoreChain(tail);
layer.draw();
}
// Morris PostOrder Traversal Animation.
async function postorderTraversal(root) {
let current = root;
while (current !== null) {
highlightNode(current, '#ffeb3b');
layer.draw();
if (current.left !== null) {
updateExplanation("Current: " + current.val + " - Has left child, finding rightmost in left subtree");
await waitForNext();
let rightmost = current.left;
highlightNode(rightmost, '#2196f3');
layer.draw();
while (rightmost.right !== null && rightmost.right !== current) {
updateExplanation("Moving to right: " + rightmost.val);
await waitForNext();
unhighlightNode(rightmost, 'white');
rightmost = rightmost.right;
highlightNode(rightmost, '#2196f3');
layer.draw();
}
if (rightmost.right === null) {
updateExplanation("Rightmost: " + rightmost.val + " - Creating thread to current: " + current.val);
await waitForNext();
createThreadEdge(rightmost, current);
rightmost.right = current;
highlightNode(rightmost, '#03a9f4');
layer.draw();
await waitForNext();
unhighlightNode(rightmost, 'white');
unhighlightNode(current, 'white');
current = current.left;
} else {
updateExplanation("Thread detected at rightmost: " + rightmost.val + " - Removing thread and processing subtree");
await waitForNext();
removeThreadEdge(rightmost);
rightmost.right = null;
unhighlightNode(rightmost, 'white');
layer.draw();
await reverseTraverseRightEdge(current.left);
unhighlightNode(current, 'white');
current = current.right;
}
} else {
updateExplanation("Current: " + current.val + " - No left child, moving right");
await waitForNext();
unhighlightNode(current, 'white');
current = current.right;
}
}
updateExplanation("Processing final right edge");
await waitForNext();
await reverseTraverseRightEdge(root);
updateExplanation("Morris Post-Order Traversal complete");
await waitForNext();
updateExplanation("Final post-order traversal result: " + document.getElementById('output').innerText);
nodes.forEach(n => {
n.shape.moveToTop();
n.label.moveToTop();
});
layer.draw();
}
window.addEventListener('resize', function() {
stage.width(document.querySelector('.tree-container').clientWidth - 30);
stage.height(document.querySelector('.tree-container').clientHeight - 140);
layer.draw();
});
window.onload = function() {
document.getElementById('nextStep').disabled = false;
postorderTraversal(node1);
};
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,9 +5,6 @@ In this assignment you will develop a program to deliver notifications to users
## Learning Objectives ## Learning Objectives
- Practice using C++ inheritance and polymorphism. - Practice using C++ inheritance and polymorphism.
- Practice using C++ exceptions.
- Practice using std::queue.
- Practice using std::stack.
## Background ## Background
@@ -23,22 +20,22 @@ And there are many more. In this assignment, your program will support five type
On Instagram, on the "Settings and privacy" page, users can choose to turn on or turn off each of these notifications, as shown in the following five screenshots: On Instagram, on the "Settings and privacy" page, users can choose to turn on or turn off each of these notifications, as shown in the following five screenshots:
To turn on or off like notifications: To turn on or off like notifications:\
![alt text](images/instagram_likes.png "Instagram Likes") ![alt text](images/instagram_likes.png "Instagram Likes")
To turn on or off tag notifications: To turn on or off tag notifications:\
![alt text](images/instagram_tags.jpg "Instagram Tags") ![alt text](images/instagram_tags.jpg "Instagram Tags")
To turn on or off comment notifications: To turn on or off comment notifications:\
![alt text](images/instagram_comments.png "Instagram Comments") ![alt text](images/instagram_comments.png "Instagram Comments")
To turn on or off follow notifications: To turn on or off follow notifications:\
![alt text](images/instagram_follows.png "Instagram Follows") ![alt text](images/instagram_follows.png "Instagram Follows")
To turn on or off message request notifications: To turn on or off message request notifications:\
![alt text](images/instagram_message_requests.png "Instagram Message Requests") ![alt text](images/instagram_message_requests.png "Instagram Message Requests")
Users can also decide to pause all notifications: Users can also decide to pause all notifications:\
![alt text](images/instagram_pause_all.png "Instagram Pause All") ![alt text](images/instagram_pause_all.png "Instagram Pause All")
## Supported Commands ## Supported Commands
@@ -290,19 +287,46 @@ After these lines, the whole content of the json file will be stored as a string
## Program Requirements & Submission Details ## Program Requirements & Submission Details
In this assignment, you can use any data structures we have learned in this course, such as std::string, std::vector, std::list, std::map, std::set, std::pair, std::unordered_map, std::unordered_set, std::stack, std::queue, std::priority_queue. std::stack and std::queue fit very well with this assignment, but it's okay if you decide not to use them. In this assignment, you can use any data structures we have learned in this course. **There is no restrictions on what you can or cannot use.**
**You must use try/throw/catch to handle exceptions in your code**. You do not need to do so everywhere in your code. You will only lose points if you do not use it at all. **The only requirement is: you must define a class called Notification, and use this class as the base class to derive classes for various types of notifications.**
Use good coding style when you design and implement your program. Organize your program into functions: dont put all the code in main! Be sure to read the [Homework Policies](https://www.cs.rpi.edu/academics/courses/fall23/csci1200/homework_policies.php) as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and dont forget to comment your code! Use the provided template [README.txt](./README.txt) file for notes you want the grader to read. <!--**You must use try/throw/catch to handle exceptions in your code**. You do not need to do so everywhere in your code. You will only lose points if you do not use it at all.-->
You must do this assignment on your own, as described in the [Collaboration Policy & Academic Integrity](https://www.cs.rpi.edu/academics/courses/fall23/csci1200/academic_integrity.php) page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
**Due Date**: 12/07/2023, Thursday, 23:59pm. Use good coding style when you design and implement your program. Organize your program into functions: dont put all the code in main! Be sure to read the [Homework Policies](https://www.cs.rpi.edu/academics/courses/spring25/csci1200/homework_policies.php) as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and dont forget to comment your code! Use the provided template [README.txt](./README.txt) file for notes you want the grader to read.
You must do this assignment on your own, as described in the [Collaboration Policy & Academic Integrity](https://www.cs.rpi.edu/academics/courses/spring25/csci1200/academic_integrity.php) page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
<!--## Instructor's Code **Due Date**: 04/17/2025, Thursday, 22:00pm.
You can test (but not view) the instructor's code here: [instructor code](http://cs.rpi.edu/~xiaoj8/ds/trends/). ## FAQs
-->
q1: What bonus do I get if my program outperforms the instructor's program on the leaderboard?
a1: If by Thursday night 10pm (which is the submission deadline), your program outperforms the instructor's program, you have many options. You can choose one of these:
- Drop the lowest test score - replace it with your highest test score.
- Drop 2 of the lowest homeworks - replace them with your highest homework score.
You will receive an email asking you about which option you want to choose, or if you want to propose a different option. You will be asked to describe your optimization techniques, and you must have at least one technique which is unique - not mentioned by 5 (or more than 5) other students.
**Definition of Outperform:** The term outperform is defined as: Your program must either runs less amount of time than the instructor's code; or runs the same amount of time but consumes less memory than the instructor's code. Same run time, same memory usage, but submitty shows you at a higher position will not be counted as outperforming.
q2: What if my program outperforms the instructor's program on two leaderboards (2 out of the 3: hw6, hw9, hw10)?
a2: You can skip the final exam; we will apply the highest test score among your test 1, 2, and 3, as your final exam score. The "skip final" option is exclusive and can't be used with other options.
q3: What if my program ranks higher than the instructor's program on all 3 leaderboards (3 out of the 3: hw6, hw9, hw10)?
a3: You will receive an A for this course immediately if determined that you worked independently on these 3 homeworks and it is you - not someone else, who wrote the code and all 3 are your original work. The instructor reserves the right to not give you an A if the following red flags are noticed: 1. your test scores are consistently low and all the tests show that you are very unfamiliar with C++ and the lab TA/mentors conclude that your coding skills are clearly below class average. 2. when asked what optimization techniques you used, you could not name anything unique - everything you say is mentioned by at least 5 other students.
**Definition of work independently:** Doing your own research, asking chatgpt or other AI tools, are okay; collaborating with other students in this class, sharing your techniques with other students in this class, are not okay and are not considered as an independent, original work.
q4: How many submissions can I make?
a4: 60. Submitty will deduct points once you submit more than 60 times. **To make it a fair game, students who make more than 60 submissions are disqualified from the competition automatically and thus won't receive any rewards.**
q5: Can I still compete on the leaderboard after the Thursday night 10pm deadline?
a5: No. The leaderboard will be closed shortly after the submission deadline, meaning that you won't even be able to see a rank after the deadline. Students who want to take a screenshot of their position on the leaderboard are encouraged to do so before the 10pm deadline.
## Rubric ## Rubric
@@ -315,20 +339,18 @@ You can test (but not view) the instructor's code here: [instructor code](http:/
- No credit (significantly incomplete implementation) (-6) - No credit (significantly incomplete implementation) (-6)
- Putting almost everything in the main function. It's better to create separate functions for different tasks. (-2) - Putting almost everything in the main function. It's better to create separate functions for different tasks. (-2)
- Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2) - Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2)
- Missing include guards in the .h file. (Or does not declare them correctly) (-1)
- Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1) - Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1)
- Improper uses or omissions of const and reference. (-1) - Improper uses or omissions of const and reference. (-1)
- At least one function is excessively long (i.e., more than 200 lines).
- Overly cramped, excessive whitespace, or poor indentation. (-1) - Overly cramped, excessive whitespace, or poor indentation. (-1)
- Poor file organization: Puts more than one class in a file (okay for very small helper classes) (-1) - Poor file organization: Puts more than one class in a file (okay for very small helper classes). (-1)
- Poor variable names. (-1) - Poor choice of variable names: non-descriptive names (e.g. 'vec', 'str', 'var'), single-letter variable names (except single loop counter), etc. (-1)
- Uses global variables. (-1) - DATA REPRESENTATION (8 pts)
- Contains useless comments like commented-out code, terminal commands, or silly notes. (-1) - Does not define the Notification base class. (-8)
- DATA REPRESENTATION (6 pts) - Does not define any of the five derived Notification classes. (-8)
- Does not define the Notification base class. (-6) - One of the five derived Notification classes is missing. (-2)
- Does not define any of the five derived Notification classes. (-6) - Two of the five derived Notification classes are missing. (-4)
- One of the five derived Notification classes is missing. (-1)
- Two of the five derived Notification classes are missing. (-2)
- Three or more of the five derived Notification classes are missing. (-6) - Three or more of the five derived Notification classes are missing. (-6)
- Member variables are public. (-2) <!-- - Member variables are public. (-2)
- Exceptions (2 pts) -- - Exceptions (2 pts)
- Does not use try/throw/catch anywhere in the code. (-2) - Does not use try/throw/catch anywhere in the code. (-2)-->

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB

View File

Before

Width:  |  Height:  |  Size: 770 KiB

After

Width:  |  Height:  |  Size: 770 KiB

View File

@@ -0,0 +1,4 @@
#pragma once
const int TOP_K_CANDIDATES = 3;
const int TOP_N_OUTPUT = 20;

View File

@@ -0,0 +1,34 @@
#pragma once
#include <string>
#include "TopKVideoHolder.h"
struct HashtagInfo {
const std::string* name = nullptr;
long totalViews = 0;
int usageCount = 0;
TopKVideoHolder topVideos;
HashtagInfo() = default;
explicit HashtagInfo(const std::string* n) : name(n), totalViews(0), usageCount(0) {}
};
struct CompareHashtagPtr {
bool operator()(const HashtagInfo* a, const HashtagInfo* b) const {
if (a->usageCount != b->usageCount) return a->usageCount > b->usageCount;
if (a->totalViews != b->totalViews) return a->totalViews > b->totalViews;
if (a->name && b->name) return *a->name < *b->name;
if (a->name) return true;
return false;
}
};
struct CompareHashtagPtrForHeap {
bool operator()(const HashtagInfo* a, const HashtagInfo* b) const {
if (a->usageCount != b->usageCount) return a->usageCount > b->usageCount;
if (a->totalViews != b->totalViews) return a->totalViews > b->totalViews;
if (a->name && b->name) return *a->name > *b->name;
if (a->name) return false;
return true;
}
};

View File

@@ -191,7 +191,7 @@ your program should produce an output similar to what TikTok does (of course we
this basically is the trending hashtags, each is associated with some videos. In your output, these videos should be sorted in a descending order, based on how many views the video has received. this basically is the trending hashtags, each is associated with some videos. In your output, these videos should be sorted in a descending order, based on how many views the video has received.
More specifically, you should print the top 20 trending hashtags, and then for each hashtag, print 3 videos which use this hashtag in its post text. If a hashtag is used in 100 videos, select the 3 (out of these 100) most viewed videos. Print the most viewed video first, and then print the next most viewed video, and then the third most viewed video. In the case where two videos both use this hashtag have the same view count, pick the one which appears first in the input json file. However, if both needs to be printed, meaning that both are in the top 3, then check the video id (as an std::string object); if a.videoId > b.videoId, then display video a first. More specifically, you should print the top 20 trending hashtags, and then for each hashtag, print 3 videos which use this hashtag in its post text. If a hashtag is used in 100 videos, select the 3 (out of these 100) most viewed videos. Print the most viewed video first, and then print the next most viewed video, and then the third most viewed video. In the case where two videos both use this hashtag have the same view count, then break the tie by comparing the video id (as an std::string object); if a.videoId > b.videoId, then display video a first.
Definition of the top 20 trending hashtags: this should be the based on the usage of the hashtag - how many times in total each hashtag is used. When two hashtags are both used for the same amount of times, break the tie by the total view count of the videos associated with each hashtag. And if still a tie, break the tie by comparing the hashtag names, i.e., apply the less than operator (<) to the two names - both are std::strings, the hashtag whose name is less than the name of the other hashtag should be the winner and should be displayed first. Definition of the top 20 trending hashtags: this should be the based on the usage of the hashtag - how many times in total each hashtag is used. When two hashtags are both used for the same amount of times, break the tie by the total view count of the videos associated with each hashtag. And if still a tie, break the tie by comparing the hashtag names, i.e., apply the less than operator (<) to the two names - both are std::strings, the hashtag whose name is less than the name of the other hashtag should be the winner and should be displayed first.
@@ -225,7 +225,7 @@ your program should produce an output similar to what TikTok does (of course we
this basically is the trending sounds, each is associated with some videos. In your output, these videos should be sorted in a descending order, based on how many views the video has received. this basically is the trending sounds, each is associated with some videos. In your output, these videos should be sorted in a descending order, based on how many views the video has received.
More specifically, you should print the top 20 trending sounds, and then for each sound, print 3 videos which use this sound. If a sound is used in 100 videos, select the 3 (out of these 100) most viewed videos. Print the most viewed video first, and then print the next most viewed video, and then the third most viewed video. In the case where two videos both use this sound have the same view count, pick the one which appears first in the input json file. However, if both needs to be printed, meaning that both are in the top 3, then check the video id (as an std::string object); if a.videoId > b.videoId, then display video a first. More specifically, you should print the top 20 trending sounds, and then for each sound, print 3 videos which use this sound. If a sound is used in 100 videos, select the 3 (out of these 100) most viewed videos. Print the most viewed video first, and then print the next most viewed video, and then the third most viewed video. In the case where two videos both use this sound have the same view count, then break the tie by comparing the video id (as an std::string object); if a.videoId > b.videoId, then display video a first.
Definition of the top 20 trending sounds: this should be the based on the total view count of the videos which use this sound. If there is a tie, break the tie by comparing the music id, i.e., apply the less than operator (<) to the two music ids - both are std::strings, the sound whose music id is less than the music id of the other sound should be the winner, and should be displayed first. Definition of the top 20 trending sounds: this should be the based on the total view count of the videos which use this sound. If there is a tie, break the tie by comparing the music id, i.e., apply the less than operator (<) to the two music ids - both are std::strings, the sound whose music id is less than the music id of the other sound should be the winner, and should be displayed first.
@@ -297,9 +297,9 @@ You can test (but not view) the instructor's code here: [instructor code](http:/
## FAQs ## FAQs
q1: For cases where a hashtag appears twice on a line does that count as two separate URLs? q1: Some videos appear multiple times in the same json file, how shall we handle such cases?
a1: Yes, a hashtag appears 2 times in one post will be counted as 2 times. This is the reason why some URLs appear twice in the output as the top 3 videos of some hashtag - as in that #foryou case in tiny1. (This is just to simplify your implementation). a1: If a video appears multiple times in the same json file, only the first occurrence should be considered, and this means that when parsing the json file, and if you find a duplicated video ID, then that line which contains the duplicated video ID should not be parsed at all.
q2: What bonus do I get if my program ranks higher than the instructor's program on the leaderboard? q2: What bonus do I get if my program ranks higher than the instructor's program on the leaderboard?
@@ -320,13 +320,13 @@ a4: 60. Submitty will deduct points once you submit more than 60 times.
## Rubric ## Rubric
17 pts 15 pts
- README.txt Completed (3 pts) - README.txt Completed (3 pts)
- One of name, collaborators, or hours not filled in. (-1) - One of name, collaborators, or hours not filled in. (-1)
- Two or more of name, collaborators, or hours not filled in. (-2) - Two or more of name, collaborators, or hours not filled in. (-2)
- No reflection. (-1) - No reflection. (-1)
- IMPLEMENTATION AND CODING STYLE (8 pts) - IMPLEMENTATION AND CODING STYLE (6 pts)
- No credit (significantly incomplete implementation) (-8) - No credit (significantly incomplete implementation) (-6)
- Putting almost everything in the main function. It's better to create separate functions for different tasks. (-2) - Putting almost everything in the main function. It's better to create separate functions for different tasks. (-2)
- Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2) - Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2)
- Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1) - Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1)

View File

@@ -1,7 +1,7 @@
HOMEWORK 10: Tiktok Trends HOMEWORK 9: Tiktok Trends
NAME: < insert name > NAME: Jinshan Zhou
COLLABORATORS AND OTHER RESOURCES: COLLABORATORS AND OTHER RESOURCES:
@@ -10,17 +10,21 @@ List the names of everyone you talked to about this assignment
LMS, etc.), and all of the resources (books, online reference LMS, etc.), and all of the resources (books, online reference
material, etc.) you consulted in completing this assignment. material, etc.) you consulted in completing this assignment.
< insert collaborators / resources > A lot, like using Top K for better sorting. Difference IO cases (not useful).
StringCache (not useful). shared_ptr (not really help) and may websites, cases
that I don't remember anymore.
Remember: Your implementation for this assignment must be done on your Remember: Your implementation for this assignment must be done on your
own, as described in "Academic Integrity for Homework" handout. own, as described in "Academic Integrity for Homework" handout.
ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: < insert # hours > ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: over 20 hr, 8 hr for complete
more than 10 hr just for optimization.
MISC. COMMENTS TO GRADER: MISC. COMMENTS TO GRADER:
(optional, please be concise!) The program is a bit messy. Since, I tried too many techniques. Some are broken
changes, so I have to add patch to it. Myself is also a bit lose to my code.
## Reflection and Self Assessment ## Reflection and Self Assessment
@@ -32,5 +36,11 @@ What parts of the assignment did you find challenging? Is there anything that
finally "clicked" for you in the process of working on this assignment? How well finally "clicked" for you in the process of working on this assignment? How well
did the development and testing process go for you? did the development and testing process go for you?
< insert reflection > This was definitely the most challenging assignment I've ever seen, and the
hard part was identifying performance issues and optimizing them. It was not
easy, I used various tools like perf and found that the JSON part had the biggest
overhead. Optimizing it showed immediate results, but then I hit a bottleneck,
so I started using various schemes, and most of them didn't work. Then I had to
push back and design the business process from scratch and finally got inspired
by my professor to use unordered set to get the best performance
(last 0.1 seconds).

View File

@@ -0,0 +1,35 @@
#pragma once
#include <string>
#include "TopKVideoHolder.h"
struct SoundInfo {
const std::string* musicId = nullptr;
const std::string* musicName = nullptr;
const std::string* musicAuthor = nullptr;
long totalViews = 0;
TopKVideoHolder topVideos;
SoundInfo() = default;
SoundInfo(const std::string* id, const std::string* name, const std::string* author)
: musicId(id), musicName(name), musicAuthor(author), totalViews(0) {}
};
struct CompareSoundPtr {
bool operator()(const SoundInfo* a, const SoundInfo* b) const {
if (a->totalViews != b->totalViews) return a->totalViews > b->totalViews;
if (a->musicId && b->musicId) return *a->musicId < *b->musicId;
if (a->musicId) return true;
return false;
}
};
struct CompareSoundPtrForHeap {
bool operator()(const SoundInfo* a, const SoundInfo* b) const {
if (a->totalViews != b->totalViews) return a->totalViews > b->totalViews;
if (a->musicId && b->musicId) return *a->musicId > *b->musicId;
if (a->musicId) return false;
return true;
}
};

View File

@@ -0,0 +1,15 @@
#include "StringInterner.h"
const std::string* StringInterner::intern(const std::string& str) {
std::pair<std::unordered_set<std::string>::iterator, bool> result = pool.insert(str);
return &(*result.first);
}
const std::string* StringInterner::intern(std::string&& str) {
std::pair<std::unordered_set<std::string>::iterator, bool> result = pool.insert(std::move(str));
return &(*result.first);
}
const std::string* StringInterner::getEmptyString() {
return intern("");
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <string>
#include <unordered_set>
#include <utility>
class StringInterner {
private:
std::unordered_set<std::string> pool;
public:
const std::string* intern(const std::string& str);
const std::string* intern(std::string&& str);
const std::string* getEmptyString();
};

View File

@@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <functional>
struct StringPtrHash {
size_t operator()(const std::string* s) const {
return std::hash<std::string>()(*s);
}
};
struct StringPtrEqual {
bool operator()(const std::string* a, const std::string* b) const {
if (a == b) return true;
if (!a || !b) return false;
return *a == *b;
}
};

View File

@@ -0,0 +1,32 @@
#include "TopKVideoHolder.h"
void TopKVideoHolder::add(const VideoInfo& video) {
if (pq.size() < K) {
pq.push(video);
} else {
if (VideoCompareWorse()(video, pq.top())) {
pq.pop();
pq.push(video);
}
}
}
std::vector<VideoInfo> TopKVideoHolder::getSortedVideos() {
std::vector<VideoInfo> sortedVideos;
size_t current_size = pq.size();
if (current_size == 0) return sortedVideos;
sortedVideos.reserve(current_size);
while (!pq.empty()) {
sortedVideos.push_back(pq.top());
pq.pop();
}
std::sort(sortedVideos.begin(), sortedVideos.end(), VideoInfo::compareForFinalSort);
return sortedVideos;
}
bool TopKVideoHolder::empty() const { return pq.empty(); }
size_t TopKVideoHolder::size() const { return pq.size(); }

View File

@@ -0,0 +1,19 @@
#pragma once
#include <vector>
#include <queue>
#include <algorithm>
#include "VideoInfo.h"
#include "Constants.h"
class TopKVideoHolder {
private:
std::priority_queue<VideoInfo, std::vector<VideoInfo>, VideoCompareWorse> pq;
static const size_t K = TOP_K_CANDIDATES;
public:
void add(const VideoInfo& video);
std::vector<VideoInfo> getSortedVideos();
bool empty() const;
size_t size() const;
};

265
hws/tiktok_trends/Utils.cpp Normal file
View File

@@ -0,0 +1,265 @@
#include "Utils.h"
#include <iostream> // For potential cerr usage, although not directly in these functions
#include <cctype>
#include <cstring>
#include <algorithm> // For std::min
bool parseQuotedStringValue(const std::string& str, size_t& pos, std::string& value) {
const size_t strLen = str.length();
value.clear();
if (pos >= strLen || str[pos] != '"') return false;
++pos;
const size_t startPos = pos;
const char* strData = str.data();
while (pos < strLen && strData[pos] != '"') {
++pos;
}
if (pos >= strLen) return false;
value.assign(strData + startPos, pos - startPos);
++pos;
return true;
}
bool parseUnquotedValue(const std::string& str, size_t& pos, std::string& value) {
const size_t strLen = str.length();
value.clear();
const size_t startPos = pos;
const char* strData = str.data();
while (pos < strLen && strData[pos] != ',' && strData[pos] != '}' && strData[pos] != ']' && !std::isspace(static_cast<unsigned char>(strData[pos]))) {
++pos;
}
if (startPos == pos) return false;
value.assign(strData + startPos, pos - startPos);
return true;
}
bool extractValue(const std::string& line, const std::string& key, std::string& value) {
const std::string searchKey = "\"" + key + "\":";
const char* found_pos = strstr(line.c_str(), searchKey.c_str());
if (!found_pos) return false;
size_t pos = (found_pos - line.c_str()) + searchKey.length();
const size_t lineLen = line.length();
while (pos < lineLen && std::isspace(static_cast<unsigned char>(line[pos]))) {
++pos;
}
if (pos >= lineLen) return false;
if (line[pos] == '"') {
return parseQuotedStringValue(line, pos, value);
} else {
return parseUnquotedValue(line, pos, value);
}
}
bool extractSubObject(const std::string& line, const std::string& key, std::string& subObj) {
const std::string searchKey = "\"" + key + "\":";
const char* found_pos = strstr(line.c_str(), searchKey.c_str());
if (!found_pos) return false;
size_t pos = (found_pos - line.c_str()) + searchKey.length();
const size_t lineLen = line.length();
while (pos < lineLen && std::isspace(static_cast<unsigned char>(line[pos]))) ++pos;
if (pos >= lineLen || line[pos] != '{') return false;
const size_t startBracePos = pos;
int braceCount = 1;
++pos;
const char* lineData = line.data();
bool inString = false;
char prevChar = 0;
while (pos < lineLen && braceCount > 0) {
const char c = lineData[pos];
if (c == '"' && prevChar != '\\') {
inString = !inString;
} else if (!inString) {
if (c == '{') {
++braceCount;
} else if (c == '}') {
--braceCount;
}
}
prevChar = (prevChar == '\\' && c == '\\') ? 0 : c;
++pos;
}
if (braceCount == 0) {
subObj.assign(lineData + startBracePos, pos - startBracePos);
return true;
}
return false;
}
bool parseLongLong(const std::string& s, long& result) {
result = 0;
if (s.empty()) return false;
const char* ptr = s.c_str();
bool negative = false;
long current_val = 0;
if (*ptr == '-') {
negative = true;
++ptr;
}
if (!*ptr) return false;
while (*ptr) {
if (*ptr >= '0' && *ptr <= '9') {
long digit = (*ptr - '0');
current_val = current_val * 10 + digit;
} else {
return false;
}
++ptr;
}
result = negative ? -current_val : current_val;
return true;
}
bool parseLineForHashtags(const std::string& line, int inputOrder, StringInterner& interner,
VideoInfo& outVideo, std::string& outText)
{
outText.clear();
std::string id_str, coverUrl_str, webVideoUrl_str, playCount_str;
if (!extractValue(line, "id", id_str) || id_str.empty()) return false;
long playCount = 0;
if (extractValue(line, "playCount", playCount_str)) {
parseLongLong(playCount_str, playCount);
}
extractValue(line, "text", outText);
extractValue(line, "webVideoUrl", webVideoUrl_str);
std::string videoMetaSub;
if (extractSubObject(line, "videoMeta", videoMetaSub)) {
extractValue(videoMetaSub, "coverUrl", coverUrl_str);
}
outVideo = VideoInfo(
interner.intern(std::move(id_str)),
interner.intern(std::move(coverUrl_str)),
interner.intern(std::move(webVideoUrl_str)),
playCount,
inputOrder
);
return true;
}
bool parseLineForSounds(const std::string& line, int inputOrder, StringInterner& interner,
VideoInfo& outVideo,
const std::string*& outMusicIdPtr,
const std::string*& outMusicNamePtr,
const std::string*& outMusicAuthorPtr)
{
std::string id_str, coverUrl_str, webVideoUrl_str, playCount_str;
std::string musicId_str, musicName_str, musicAuthor_str;
if (!extractValue(line, "id", id_str) || id_str.empty()) return false;
long playCount = 0;
if (extractValue(line, "playCount", playCount_str)) {
parseLongLong(playCount_str, playCount);
}
std::string musicMetaSub;
if (extractSubObject(line, "musicMeta", musicMetaSub)) {
extractValue(musicMetaSub, "musicId", musicId_str);
extractValue(musicMetaSub, "musicName", musicName_str);
extractValue(musicMetaSub, "musicAuthor", musicAuthor_str);
}
if (musicId_str.empty()) {
return false;
}
extractValue(line, "webVideoUrl", webVideoUrl_str);
std::string videoMetaSub;
if (extractSubObject(line, "videoMeta", videoMetaSub)) {
extractValue(videoMetaSub, "coverUrl", coverUrl_str);
}
outVideo = VideoInfo(
interner.intern(std::move(id_str)),
interner.intern(std::move(coverUrl_str)),
interner.intern(std::move(webVideoUrl_str)),
playCount,
inputOrder
);
outMusicIdPtr = interner.intern(std::move(musicId_str));
outMusicNamePtr = interner.intern(std::move(musicName_str));
outMusicAuthorPtr = interner.intern(std::move(musicAuthor_str));
return true;
}
void extractHashtags(const std::string& text,
std::unordered_map<const std::string*, HashtagInfo, StringPtrHash, StringPtrEqual>& hashtagData,
StringInterner& interner,
const VideoInfo& video)
{
const size_t textLen = text.length();
const char* textData = text.data();
size_t pos = 0;
std::string tag_buffer;
tag_buffer.reserve(50);
while (pos < textLen) {
while (pos < textLen && textData[pos] != '#') {
pos++;
}
if (pos >= textLen) break;
size_t start = pos + 1;
if (start >= textLen) break;
size_t end = start;
while (end < textLen && (std::isalnum(static_cast<unsigned char>(textData[end])) || textData[end] == '_')) {
end++;
}
if (end > start) {
tag_buffer.assign(textData + start, end - start);
const std::string* hashtagPtr = interner.intern(tag_buffer);
typedef std::unordered_map<const std::string*, HashtagInfo, StringPtrHash, StringPtrEqual> HashtagMapType;
HashtagMapType::iterator it = hashtagData.find(hashtagPtr);
if (it == hashtagData.end()) {
std::pair<HashtagMapType::iterator, bool> emplace_result =
hashtagData.emplace(hashtagPtr, HashtagInfo(hashtagPtr));
it = emplace_result.first;
}
it->second.usageCount++;
it->second.totalViews += video.playCount;
it->second.topVideos.add(video);
}
pos = end;
}
}
void extractSortAndPrintTop3Videos(std::ofstream& fout, TopKVideoHolder& topVideos) {
std::vector<VideoInfo> sortedTopVideos = topVideos.getSortedVideos();
int videosToPrint = std::min(static_cast<int>(sortedTopVideos.size()), TOP_K_CANDIDATES);
for (int i = 0; i < videosToPrint; ++i) {
const VideoInfo& video = sortedTopVideos[i];
fout << "cover url: " << (video.coverUrl && !video.coverUrl->empty() ? *video.coverUrl : "null") << "\n";
fout << "web video url: " << (video.webVideoUrl && !video.webVideoUrl->empty() ? *video.webVideoUrl : "null") << "\n";
}
}

33
hws/tiktok_trends/Utils.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include "StringInterner.h"
#include "VideoInfo.h"
#include "HashtagInfo.h"
#include "SoundInfo.h"
#include "StringPtrUtils.h" // Needed for HashtagMapType/SoundMapType in function signatures
bool parseQuotedStringValue(const std::string& str, size_t& pos, std::string& value);
bool parseUnquotedValue(const std::string& str, size_t& pos, std::string& value);
bool extractValue(const std::string& line, const std::string& key, std::string& value);
bool extractSubObject(const std::string& line, const std::string& key, std::string& subObj);
bool parseLongLong(const std::string& s, long& result);
bool parseLineForHashtags(const std::string& line, int inputOrder, StringInterner& interner,
VideoInfo& outVideo, std::string& outText);
bool parseLineForSounds(const std::string& line, int inputOrder, StringInterner& interner,
VideoInfo& outVideo,
const std::string*& outMusicIdPtr,
const std::string*& outMusicNamePtr,
const std::string*& outMusicAuthorPtr);
void extractHashtags(const std::string& text,
std::unordered_map<const std::string*, HashtagInfo, StringPtrHash, StringPtrEqual>& hashtagData,
StringInterner& interner,
const VideoInfo& video);
void extractSortAndPrintTop3Videos(std::ofstream& fout, TopKVideoHolder& topVideos);

View File

@@ -0,0 +1,37 @@
#pragma once
#include <string>
#include <algorithm>
#include "Constants.h"
struct VideoInfo {
const std::string* videoId = nullptr;
const std::string* coverUrl = nullptr;
const std::string* webVideoUrl = nullptr;
long playCount = 0;
int inputOrder = -1;
VideoInfo() = default;
VideoInfo(const std::string* id, const std::string* cover, const std::string* web,
long plays, int order)
: videoId(id), coverUrl(cover), webVideoUrl(web), playCount(plays), inputOrder(order) {}
static bool compareForFinalSort(const VideoInfo& a, const VideoInfo& b) {
if (a.playCount != b.playCount) return a.playCount > b.playCount;
if (a.videoId && b.videoId && *a.videoId != *b.videoId) return *a.videoId < *b.videoId;
return a.inputOrder < b.inputOrder;
}
bool operator<(const VideoInfo& other) const {
if (playCount != other.playCount) return playCount > other.playCount;
return inputOrder < other.inputOrder;
}
};
struct VideoCompareWorse {
bool operator()(const VideoInfo& a, const VideoInfo& b) const {
if (a.playCount != b.playCount) return a.playCount > b.playCount;
return a.inputOrder < b.inputOrder;
}
};

256
hws/tiktok_trends/main.cpp Normal file
View File

@@ -0,0 +1,256 @@
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <functional>
#include <utility>
#include <cstring>
#include "Constants.h"
#include "StringInterner.h"
#include "StringPtrUtils.h"
#include "VideoInfo.h"
#include "TopKVideoHolder.h"
#include "HashtagInfo.h"
#include "SoundInfo.h"
#include "Utils.h"
bool processHashtags(const std::string& filename, std::ofstream& outputFile) {
std::ifstream inputFile(filename);
if (!inputFile.is_open()) {
std::cerr << "Cannot open input file: " << filename << std::endl;
return false;
}
StringInterner interner;
std::unordered_map<const std::string*, HashtagInfo, StringPtrHash, StringPtrEqual> hashtagData;
hashtagData.reserve(250000);
std::string line;
int inputOrderCounter = 0;
VideoInfo currentVideo;
std::string text_buffer;
while (std::getline(inputFile, line)) {
if (line.length() < 10) continue;
inputOrderCounter++;
if (parseLineForHashtags(line, inputOrderCounter, interner, currentVideo, text_buffer)) {
if (!text_buffer.empty()) {
extractHashtags(text_buffer, hashtagData, interner, currentVideo);
}
}
}
inputFile.close();
std::priority_queue<HashtagInfo*, std::vector<HashtagInfo*>, CompareHashtagPtrForHeap> top20Hashtags;
typedef std::unordered_map<const std::string*, HashtagInfo, StringPtrHash, StringPtrEqual> HashtagMapType;
for (HashtagMapType::iterator it = hashtagData.begin(); it != hashtagData.end(); ++it) {
HashtagInfo* currentHashtagPtr = &(it->second);
if (top20Hashtags.size() < TOP_N_OUTPUT) {
top20Hashtags.push(currentHashtagPtr);
} else {
const HashtagInfo* topPtr = top20Hashtags.top();
bool is_better = CompareHashtagPtr()(currentHashtagPtr, topPtr);
if (is_better) {
top20Hashtags.pop();
top20Hashtags.push(currentHashtagPtr);
}
}
}
std::vector<HashtagInfo*> finalTop20;
finalTop20.reserve(top20Hashtags.size());
while (!top20Hashtags.empty()) {
finalTop20.push_back(top20Hashtags.top());
top20Hashtags.pop();
}
std::sort(finalTop20.begin(), finalTop20.end(), CompareHashtagPtr());
outputFile << "trending hashtags:\n\n";
for (size_t i = 0; i < finalTop20.size(); ++i) {
HashtagInfo* currentHashtag = finalTop20[i];
outputFile << "========================\n";
outputFile << "#" << (currentHashtag->name ? *currentHashtag->name : "null") << "\n";
outputFile << "used " << currentHashtag->usageCount << " times\n";
outputFile << currentHashtag->totalViews << " views\n\n";
extractSortAndPrintTop3Videos(outputFile, currentHashtag->topVideos);
outputFile << "========================";
if (i < finalTop20.size() - 1) {
outputFile << "\n";
} else {
outputFile << "\n";
}
}
return true;
}
bool processSounds(const std::string& filename, std::ofstream& outputFile) {
std::ifstream inputFile(filename);
if (!inputFile.is_open()) {
std::cerr << "Cannot open input file: " << filename << std::endl;
return false;
}
StringInterner interner;
std::unordered_map<const std::string*, SoundInfo, StringPtrHash, StringPtrEqual> soundData;
soundData.reserve(50000);
std::string line;
int inputOrderCounter = 0;
VideoInfo currentVideo;
const std::string* musicIdPtr = nullptr;
const std::string* musicNamePtr = nullptr;
const std::string* musicAuthorPtr = nullptr;
while (std::getline(inputFile, line)) {
if (line.length() < 10) continue;
inputOrderCounter++;
musicIdPtr = nullptr;
musicNamePtr = nullptr;
musicAuthorPtr = nullptr;
if (parseLineForSounds(line, inputOrderCounter, interner, currentVideo,
musicIdPtr, musicNamePtr, musicAuthorPtr))
{
if (musicIdPtr == nullptr || musicIdPtr->empty()) {
continue;
}
typedef std::unordered_map<const std::string*, SoundInfo, StringPtrHash, StringPtrEqual> SoundMapType;
SoundMapType::iterator it = soundData.find(musicIdPtr);
if (it == soundData.end()) {
std::pair<SoundMapType::iterator, bool> emplace_result =
soundData.emplace(musicIdPtr, SoundInfo(musicIdPtr, musicNamePtr, musicAuthorPtr));
it = emplace_result.first;
}
it->second.totalViews += currentVideo.playCount;
if (it->second.musicName->empty() && !musicNamePtr->empty()) {
it->second.musicName = musicNamePtr;
}
if (it->second.musicAuthor->empty() && !musicAuthorPtr->empty()) {
it->second.musicAuthor = musicAuthorPtr;
}
it->second.topVideos.add(currentVideo);
}
}
inputFile.close();
std::priority_queue<SoundInfo*, std::vector<SoundInfo*>, CompareSoundPtrForHeap> top20Sounds;
typedef std::unordered_map<const std::string*, SoundInfo, StringPtrHash, StringPtrEqual> SoundMapType;
for (SoundMapType::iterator it = soundData.begin(); it != soundData.end(); ++it) {
SoundInfo* currentSoundPtr = &(it->second);
if (top20Sounds.size() < TOP_N_OUTPUT) {
top20Sounds.push(currentSoundPtr);
} else {
const SoundInfo* topPtr = top20Sounds.top();
bool is_better = CompareSoundPtr()(currentSoundPtr, topPtr);
if (is_better) {
top20Sounds.pop();
top20Sounds.push(currentSoundPtr);
}
}
}
std::vector<SoundInfo*> finalTop20;
finalTop20.reserve(top20Sounds.size());
while (!top20Sounds.empty()) {
finalTop20.push_back(top20Sounds.top());
top20Sounds.pop();
}
std::sort(finalTop20.begin(), finalTop20.end(), CompareSoundPtr());
outputFile << "trending sounds:\n\n";
for (size_t i = 0; i < finalTop20.size(); ++i) {
SoundInfo* currentSound = finalTop20[i];
outputFile << "========================\n";
if (currentSound->musicName == nullptr || currentSound->musicName->empty()) {
outputFile << "\n";
} else {
outputFile << *currentSound->musicName << "\n";
}
outputFile << currentSound->totalViews << " views\n";
if (currentSound->musicAuthor == nullptr || currentSound->musicAuthor->empty()) {
outputFile << "\n";
} else {
outputFile << *currentSound->musicAuthor << "\n";
}
outputFile << "music id: " << (currentSound->musicId && !currentSound->musicId->empty() ? *currentSound->musicId : "null") << "\n";
if (!currentSound->topVideos.empty()) {
outputFile << "\n";
}
extractSortAndPrintTop3Videos(outputFile, currentSound->topVideos);
outputFile << "========================";
if (i < finalTop20.size() - 1) {
outputFile << "\n";
} else {
outputFile << "\n";
}
}
return true;
}
int main(int argc, char* argv[]) {
if (argc != 4) {
std::cerr << "Usage: nytrends.exe <input.json> <output.txt> <mode>\n";
std::cerr << "Mode can be 'hashtag' or 'sound'\n";
return 1;
}
std::string inputFileName = argv[1];
std::string outputFileName = argv[2];
std::string mode = argv[3];
std::ofstream outputFile(outputFileName);
if (!outputFile.is_open()) {
std::cerr << "Error: Cannot open output file " << outputFileName << std::endl;
return 1;
}
std::ios_base::sync_with_stdio(false);
bool success = false;
if (mode == "hashtag") {
success = processHashtags(inputFileName, outputFile);
} else if (mode == "sound") {
success = processSounds(inputFileName, outputFile);
} else {
std::cerr << "Error: Invalid mode '" << mode << "'. Must be 'hashtag' or 'sound'." << std::endl;
outputFile.close();
return 1;
}
outputFile.close();
return success ? 0 : 1;
}

View File

@@ -0,0 +1,242 @@
trending hashtags:
========================
#fyp
used 7600 times
261199234341 views
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/567504ab3e4648dea968213ce979f281?x-expires=1700449200&x-signature=bjGEgY4bdEVOMMHQa2S0qrzNCQY%3D
web video url: https://www.tiktok.com/@bellapoarch/video/6862153058223197445
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/567504ab3e4648dea968213ce979f281?x-expires=1700449200&x-signature=bjGEgY4bdEVOMMHQa2S0qrzNCQY%3D
web video url: https://www.tiktok.com/@bellapoarch/video/6862153058223197445
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/oAJCgD1khIANGRcTLhqQZNCi3ohAuAzoyEdIaf?x-expires=1700449200&x-signature=hu1Kg0Cpz%2BzVRXqYkv%2Fl6E8%2Ftgk%3D
web video url: https://www.tiktok.com/@tool_tips/video/7212981630904864005
========================
========================
#foryou
used 2765 times
92282640558 views
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/c4c7c98ecb5f4a8980ed7d58cdea2df3_1676378432?x-expires=1700449200&x-signature=QIchR40Etr%2BAjbAuzYbwTKnD7dA%3D
web video url: https://www.tiktok.com/@gorillatiks/video/7199990500512894213
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/d57bd10bd2594b148d48c5443d5571b0?x-expires=1700449200&x-signature=Z%2FTgQwhQ9eSmRMF3cBmH%2BdVHve8%3D
web video url: https://www.tiktok.com/@honeycats77/video/7190528800352980267
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/d57bd10bd2594b148d48c5443d5571b0?x-expires=1700449200&x-signature=Z%2FTgQwhQ9eSmRMF3cBmH%2BdVHve8%3D
web video url: https://www.tiktok.com/@honeycats77/video/7190528800352980267
========================
========================
#viral
used 1759 times
59270543842 views
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/o4n0aDMCxQPkleFE5JnbeaoIw9uEBRQiTkIzAB?x-expires=1700449200&x-signature=zOxX4QIMqL%2BNOyl6R57PLiVKb%2BE%3D
web video url: https://www.tiktok.com/@dada_ahoufe_/video/7247202774696447238
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/o4n0aDMCxQPkleFE5JnbeaoIw9uEBRQiTkIzAB?x-expires=1700449200&x-signature=zOxX4QIMqL%2BNOyl6R57PLiVKb%2BE%3D
web video url: https://www.tiktok.com/@dada_ahoufe_/video/7247202774696447238
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/oYRUDAt9kFIA2SIwCWomEVfha623AyrLzxgaAo?x-expires=1700449200&x-signature=xVeyOReZuykD9rFS4KFcN%2FFL44g%3D
web video url: https://www.tiktok.com/@carrosseriereparation/video/7217942797360303365
========================
========================
#makeuptutorial
used 1709 times
22311707100 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/ea183fe6de594a319ba917d1ffbff11b?x-expires=1700503200&x-signature=9L4ypK162uI%2BirECcDcFqctjvn8%3D
web video url: https://www.tiktok.com/@dollievision/video/7208244986666585386
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/ea183fe6de594a319ba917d1ffbff11b?x-expires=1700503200&x-signature=9L4ypK162uI%2BirECcDcFqctjvn8%3D
web video url: https://www.tiktok.com/@dollievision/video/7208244986666585386
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/5461c70dd8ee4a0d891e7f2529f6b8ea_1670789072?x-expires=1700503200&x-signature=TqqnBqyBh5cnb150Ri0jXfwaL9s%3D
web video url: https://www.tiktok.com/@alicekingmakeup/video/7175984394950167813
========================
========================
#couplestiktok
used 1610 times
14706422100 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/693775bbddac4df4ad008ff880041fbc?x-expires=1700503200&x-signature=UU8VVoLrIaXIVFnYLf3jl8IYO%2BE%3D
web video url: https://www.tiktok.com/@misiaaa621/video/7149368989611773227
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/693775bbddac4df4ad008ff880041fbc?x-expires=1700503200&x-signature=UU8VVoLrIaXIVFnYLf3jl8IYO%2BE%3D
web video url: https://www.tiktok.com/@misiaaa621/video/7149368989611773227
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/4b83dfa1cc0c47318e408a6bcde34bb6_1677549193?x-expires=1700503200&x-signature=%2FN8FYhRpVldGSaxbP6dgYeEroqI%3D
web video url: https://www.tiktok.com/@debbiekval/video/7205018880622857515
========================
========================
#lifehack
used 1585 times
33681856600 views
cover url: https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/d6a1c1c323614919975fad3ee1c1ef9e~tplv-dmt-logom:tos-alisg-i-0000/4124427fcd3045968ac1c3136bd92d6c.image?x-expires=1700452800&x-signature=qCaN1hrF7pqQ0kvZJnlFnc9jI6Q%3D
web video url: https://www.tiktok.com/@tresorfie/video/7039091515863403778
cover url: https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/d6a1c1c323614919975fad3ee1c1ef9e~tplv-dmt-logom:tos-alisg-i-0000/4124427fcd3045968ac1c3136bd92d6c.image?x-expires=1700449200&x-signature=WSl3XKN1HPXy7jpguj8v0AaI3FU%3D
web video url: https://www.tiktok.com/@tresorfie/video/7039091515863403778
cover url: https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/501627c6b36849e282740c764611f2a7_1634994542~tplv-dmt-logom:tos-alisg-pv-0037/f3273e6f3e92421d860be8f5e72ac0bd.image?x-expires=1700452800&x-signature=DkwRLgyyY5ec0757c1hCq372yJM%3D
web video url: https://www.tiktok.com/@issei0806/video/7022248055625846018
========================
========================
#funnyvideos
used 1573 times
67029374400 views
cover url: https://p16-sign-useast2a.tiktokcdn.com/obj/tos-useast2a-p-0037-euttp/b0fed04ac06b45f58a9c3add061342dd_1686566824?x-expires=1700449200&x-signature=HTd5Yy2XA1y%2Bn0Gy2PnX9t%2FNpw4%3D
web video url: https://www.tiktok.com/@funnnyh/video/7243749070475496731
cover url: https://p16-sign-useast2a.tiktokcdn.com/obj/tos-useast2a-p-0037-euttp/b0fed04ac06b45f58a9c3add061342dd_1686566824?x-expires=1700449200&x-signature=HTd5Yy2XA1y%2Bn0Gy2PnX9t%2FNpw4%3D
web video url: https://www.tiktok.com/@funnnyh/video/7243749070475496731
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-useast2a-p-0037-euttp/b0fed04ac06b45f58a9c3add061342dd_1686566824?x-expires=1700449200&x-signature=vv04JjjwKgR1P3t117v%2B5HMvnpI%3D
web video url: https://www.tiktok.com/@funnnyh/video/7243749070475496731
========================
========================
#foryoupage
used 1550 times
49067115500 views
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/d57bd10bd2594b148d48c5443d5571b0?x-expires=1700449200&x-signature=Z%2FTgQwhQ9eSmRMF3cBmH%2BdVHve8%3D
web video url: https://www.tiktok.com/@honeycats77/video/7190528800352980267
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/d57bd10bd2594b148d48c5443d5571b0?x-expires=1700449200&x-signature=Z%2FTgQwhQ9eSmRMF3cBmH%2BdVHve8%3D
web video url: https://www.tiktok.com/@honeycats77/video/7190528800352980267
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/oYRUDAt9kFIA2SIwCWomEVfha623AyrLzxgaAo?x-expires=1700449200&x-signature=xVeyOReZuykD9rFS4KFcN%2FFL44g%3D
web video url: https://www.tiktok.com/@carrosseriereparation/video/7217942797360303365
========================
========================
#newyorkcity
used 1545 times
8642836600 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/08b82fdf19d5468e91a032b30e527861_1692785637?x-expires=1700452800&x-signature=%2F5sW2i0xTGXJJj2PKbwI6VXywWI%3D
web video url: https://www.tiktok.com/@erikconover/video/7270458709065731370
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/1ea2545bde2645ec8f1106a4b9de6c2e_1648602515?x-expires=1700452800&x-signature=HNr6UCQsc4q0m%2FFShx%2FJJNWb1Jg%3D
web video url: https://www.tiktok.com/@thekatieromero/video/7080693879141485870
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/c898b993f308477e92334437f9f0e1e1?x-expires=1700452800&x-signature=ZNHG9%2FMBw9qp5DSm%2BNnbhwX6xK8%3D
web video url: https://www.tiktok.com/@thekatieromero/video/7105552208422374699
========================
========================
#ifweeverbrokeup
used 1543 times
1337044198 views
cover url: https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/ee79eb2bea6445739ed71cef3e9b84b6_1686646723?x-expires=1700456400&x-signature=0MADrs89I23eeCudb%2FJxkI%2FJbR8%3D
web video url: https://www.tiktok.com/@zanmangloopyofficial/video/7244092495129218312
cover url: https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/ee79eb2bea6445739ed71cef3e9b84b6_1686646723?x-expires=1700456400&x-signature=0MADrs89I23eeCudb%2FJxkI%2FJbR8%3D
web video url: https://www.tiktok.com/@zanmangloopyofficial/video/7244092495129218312
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/7895be3e406d435ba0db9e6f5db349e2?x-expires=1700456400&x-signature=ahrkOjcQRlDhAOf1upGEu%2B2ECYU%3D
web video url: https://www.tiktok.com/@bebopandbebe/video/7238437685537328426
========================
========================
#springcleaning
used 1416 times
2156123000 views
cover url: https://p16-sign-va.tiktokcdn.com/tos-maliva-p-0068/56ce7e79491a4b27b371517ce134fa82_1631381225~tplv-dmt-logom:tos-maliva-p-0000/415cfd01b3484fb38f7b088aa6efda67.image?x-expires=1700503200&x-signature=YL4yGwa%2F1gZ59cKHMov7ficsK9E%3D
web video url: https://www.tiktok.com/@livecomposed/video/7006728991067491589
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/4c54b4d1332c4000a615d9c5fc172be8_1677780053?x-expires=1700503200&x-signature=aD4zRpPLbhn9wz4vtTQWZRa2I1U%3D
web video url: https://www.tiktok.com/@atmeikasa/video/7206010371004583214
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/4c54b4d1332c4000a615d9c5fc172be8_1677780053?x-expires=1700503200&x-signature=aD4zRpPLbhn9wz4vtTQWZRa2I1U%3D
web video url: https://www.tiktok.com/@atmeikasa/video/7206010371004583214
========================
========================
#funny
used 1382 times
53648909500 views
cover url: https://p16-sign-useast2a.tiktokcdn.com/obj/tos-useast2a-p-0037-euttp/b0fed04ac06b45f58a9c3add061342dd_1686566824?x-expires=1700449200&x-signature=HTd5Yy2XA1y%2Bn0Gy2PnX9t%2FNpw4%3D
web video url: https://www.tiktok.com/@funnnyh/video/7243749070475496731
cover url: https://p16-sign-useast2a.tiktokcdn.com/obj/tos-useast2a-p-0037-euttp/b0fed04ac06b45f58a9c3add061342dd_1686566824?x-expires=1700449200&x-signature=HTd5Yy2XA1y%2Bn0Gy2PnX9t%2FNpw4%3D
web video url: https://www.tiktok.com/@funnnyh/video/7243749070475496731
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-useast2a-p-0037-euttp/b0fed04ac06b45f58a9c3add061342dd_1686566824?x-expires=1700449200&x-signature=vv04JjjwKgR1P3t117v%2B5HMvnpI%3D
web video url: https://www.tiktok.com/@funnnyh/video/7243749070475496731
========================
========================
#happymonday
used 1308 times
741991700 views
cover url: https://p16-sign-va.tiktokcdn.com/tos-maliva-p-0068/oQgeH8BRJnj20JEFoQ5tAf1MIb976nBD89QiFB~tplv-dmt-logom:tos-useast2a-v-0068/4763cd9418ac4d7faccbf52906bcf43c.image?x-expires=1700449200&x-signature=DPRcWm2Xhpe7r2HmxxGBzOyhwVs%3D
web video url: https://www.tiktok.com/@joinparallel.io/video/7192338389255916806
cover url: https://p16-sign-va.tiktokcdn.com/tos-maliva-p-0068/oQgeH8BRJnj20JEFoQ5tAf1MIb976nBD89QiFB~tplv-dmt-logom:tos-useast2a-v-0068/4763cd9418ac4d7faccbf52906bcf43c.image?x-expires=1700449200&x-signature=DPRcWm2Xhpe7r2HmxxGBzOyhwVs%3D
web video url: https://www.tiktok.com/@joinparallel.io/video/7192338389255916806
cover url: https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/002965b791d641d5b2f3d86ee0019604_1675130079?x-expires=1700449200&x-signature=TMCJOBJCXYeu5rsjFWpohtGwT8M%3D
web video url: https://www.tiktok.com/@mondayhaircare/video/7194628805414161665
========================
========================
#nyc
used 990 times
5577241000 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/08b82fdf19d5468e91a032b30e527861_1692785637?x-expires=1700452800&x-signature=%2F5sW2i0xTGXJJj2PKbwI6VXywWI%3D
web video url: https://www.tiktok.com/@erikconover/video/7270458709065731370
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/1ea2545bde2645ec8f1106a4b9de6c2e_1648602515?x-expires=1700452800&x-signature=HNr6UCQsc4q0m%2FFShx%2FJJNWb1Jg%3D
web video url: https://www.tiktok.com/@thekatieromero/video/7080693879141485870
cover url: https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/c898b993f308477e92334437f9f0e1e1?x-expires=1700452800&x-signature=ZNHG9%2FMBw9qp5DSm%2BNnbhwX6xK8%3D
web video url: https://www.tiktok.com/@thekatieromero/video/7105552208422374699
========================
========================
#makeup
used 976 times
15309874500 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/ea183fe6de594a319ba917d1ffbff11b?x-expires=1700503200&x-signature=9L4ypK162uI%2BirECcDcFqctjvn8%3D
web video url: https://www.tiktok.com/@dollievision/video/7208244986666585386
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/ea183fe6de594a319ba917d1ffbff11b?x-expires=1700503200&x-signature=9L4ypK162uI%2BirECcDcFqctjvn8%3D
web video url: https://www.tiktok.com/@dollievision/video/7208244986666585386
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/1404c560d1e74fe7881a0a4ae6414de5_1652622380?x-expires=1700449200&x-signature=m%2BIawkKwQBwnUaTqBTTMtqLQPZo%3D
web video url: https://www.tiktok.com/@mimles/video/7097959048515013894
========================
========================
#trending
used 721 times
21692028406 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/bbdfd4ef0c4040b2bf9c52e9bb81d770?x-expires=1700449200&x-signature=fMm4z9wGlJCa1VFXvU5jQ0ot6tA%3D
web video url: https://www.tiktok.com/@phuonglinh.ido/video/7215533760039865646
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/c92407e5bca34ce78eb17db878630adc?x-expires=1700449200&x-signature=836u0V7z2PC7tFMLlsvVDFDU1wU%3D
web video url: https://www.tiktok.com/@asmr.mus/video/7212985350124375342
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/c92407e5bca34ce78eb17db878630adc?x-expires=1700449200&x-signature=836u0V7z2PC7tFMLlsvVDFDU1wU%3D
web video url: https://www.tiktok.com/@asmr.mus/video/7212985350124375342
========================
========================
#comedy
used 579 times
14364510900 views
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/49a912da569f4c69b3658762357f3922_1572472757?x-expires=1700449200&x-signature=rCokiz5pbl88BrzDzX3AB1LFCXg%3D
web video url: https://www.tiktok.com/@kisonkee/video/6753718966637677830
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/49a912da569f4c69b3658762357f3922_1572472757?x-expires=1700449200&x-signature=rCokiz5pbl88BrzDzX3AB1LFCXg%3D
web video url: https://www.tiktok.com/@kisonkee/video/6753718966637677830
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/o0EnyBknREPO4GdeDo4nxAIJRFJfbfzAzGQSDf?x-expires=1700449200&x-signature=zuGbpMoTS01F4waRsGo2r2AoVxk%3D
web video url: https://www.tiktok.com/@ricoanimations0/video/7241573984590957830
========================
========================
#newyork
used 555 times
3126420800 views
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/4a878de5dbe241b5b5e25635f4200a51_1650915958?x-expires=1700449200&x-signature=1l%2F8aGh0jktub1R%2BX23PAe64Dys%3D
web video url: https://www.tiktok.com/@mdmotivator/video/7090629995546070277
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/4a878de5dbe241b5b5e25635f4200a51_1650915958?x-expires=1700449200&x-signature=1l%2F8aGh0jktub1R%2BX23PAe64Dys%3D
web video url: https://www.tiktok.com/@mdmotivator/video/7090629995546070277
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/08b82fdf19d5468e91a032b30e527861_1692785637?x-expires=1700452800&x-signature=%2F5sW2i0xTGXJJj2PKbwI6VXywWI%3D
web video url: https://www.tiktok.com/@erikconover/video/7270458709065731370
========================
========================
#couple
used 439 times
5628511600 views
cover url: https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx/014d01e5b7f848fc8f8899e88e8fa483?x-expires=1700449200&x-signature=IMmyHEigmMoVtLEuTWPZwe%2Fksb0%3D
web video url: https://www.tiktok.com/@mamalindy/video/7079555791962885419
cover url: https://p16-sign-useast2a.tiktokcdn.com/obj/tos-useast2a-p-0037-aiso/cf3359fa45444f0994cf0dcc1c201b2d_1681930130?x-expires=1700449200&x-signature=tABQmhr%2FtklzlsNqYWGZnNrxwhI%3D
web video url: https://www.tiktok.com/@kajsablock/video/7223834852305456410
cover url: https://p16-sign-useast2a.tiktokcdn.com/obj/tos-useast2a-p-0037-aiso/cf3359fa45444f0994cf0dcc1c201b2d_1681930130?x-expires=1700449200&x-signature=tABQmhr%2FtklzlsNqYWGZnNrxwhI%3D
web video url: https://www.tiktok.com/@kajsablock/video/7223834852305456410
========================
========================
#fy
used 397 times
16901215000 views
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/ef952b508c8043bb8b4ba98e3db850fb_1679074109?x-expires=1700449200&x-signature=AOKtuDMNxX%2BU2b1dRfBvofZLZfk%3D
web video url: https://www.tiktok.com/@noelgoescrazy/video/7211568359798803717
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/ef952b508c8043bb8b4ba98e3db850fb_1679074109?x-expires=1700449200&x-signature=AOKtuDMNxX%2BU2b1dRfBvofZLZfk%3D
web video url: https://www.tiktok.com/@noelgoescrazy/video/7211568359798803717
cover url: https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/509605b7a901400589cd15d6731aaf8c_1677431421?x-expires=1700449200&x-signature=LoywgvGN5XLKIpvwV2UnR6pml6s%3D
web video url: https://www.tiktok.com/@noelgoescrazy/video/7204513074097769733
========================

456
hws/tiktok_trends/test.py Normal file
View File

@@ -0,0 +1,456 @@
import subprocess
import os
import filecmp
import glob
import sys # Import sys for platform detection
import time
import shutil
import re # Import re for regex parsing on macOS
# --- Configuration ---
CXX = "g++"
CXXFLAGS = ["-Wall", "-O2", "-std=c++11"]
EXECUTABLE = "./nytrends.exe"
SOURCE_FILES_PATTERN = "*.cpp"
INPUT_DIR = "inputs"
EXPECTED_OUTPUT_DIR = "outputs"
TEMP_OUTPUT_FILE = "output_unit_test.txt"
TEST_TIMEOUT = 120
# Configuration for memory measurement
MEASURE_MEMORY = True # Master switch
TIME_COMMAND = "/usr/bin/time"
# --- Platform Specific Time Config ---
TIME_COMMAND_MODE = None # Will be 'linux' or 'macos' or None
LINUX_TIME_FORMAT = "%M" # Format specifier for Max RSS (KB) on Linux
LINUX_TIME_OUTPUT_FILE = "time_mem_output.tmp" # Temp file for Linux time output
MACOS_MEM_REGEX = re.compile(r"^\s*(\d+)\s+maximum resident set size", re.IGNORECASE | re.MULTILINE)
# Configuration for suppressing program output
SUPPRESS_PROGRAM_OUTPUT = True
# ANSI Color Codes
# ... (colors remain the same) ...
COLOR_GREEN = '\033[92m'
COLOR_RED = '\033[91m'
COLOR_YELLOW = '\033[93m'
COLOR_BLUE = '\033[94m'
COLOR_RESET = '\033[0m'
# --- Helper Functions ---
def print_color(text, color):
"""Prints text in a specified color."""
print(f"{color}{text}{COLOR_RESET}")
def check_time_command():
"""
Check if /usr/bin/time command exists and is usable for memory measurement
based on the OS. Sets TIME_COMMAND_MODE. Returns True if usable, False otherwise.
"""
global TIME_COMMAND_MODE
if not shutil.which(TIME_COMMAND):
print_color(f"Warning: '{TIME_COMMAND}' not found. Memory measurement disabled.", COLOR_YELLOW)
TIME_COMMAND_MODE = None
return False
platform = sys.platform
test_command = []
capture_stderr = False
if platform.startswith("linux"):
test_command = [TIME_COMMAND, '-f', LINUX_TIME_FORMAT, 'true']
capture_stderr = False # Output goes to stdout/stderr, just check exit code
TIME_COMMAND_MODE = "linux"
print(f"Detected Linux platform. Testing {TIME_COMMAND} with '-f {LINUX_TIME_FORMAT}'...")
elif platform == "darwin": # macOS
test_command = [TIME_COMMAND, '-l', 'true']
capture_stderr = True # Need to capture stderr to check output format
TIME_COMMAND_MODE = "macos"
print(f"Detected macOS platform. Testing {TIME_COMMAND} with '-l'...")
else:
print_color(f"Warning: Unsupported platform '{platform}' for memory measurement. Disabled.", COLOR_YELLOW)
TIME_COMMAND_MODE = None
return False
try:
# Run test command
process = subprocess.run(test_command,
capture_output=True, # Capture both stdout/stderr
text=True,
check=True, # Raise exception on non-zero exit
timeout=3)
# Additional check for macOS output format
if TIME_COMMAND_MODE == "macos":
if MACOS_MEM_REGEX.search(process.stderr):
print_color(f"Memory measurement enabled using '{TIME_COMMAND} -l'.", COLOR_GREEN)
return True # Format looks okay
else:
print_color(f"Warning: '{TIME_COMMAND} -l' output format not recognized (missing 'maximum resident set size'). Memory measurement disabled.", COLOR_YELLOW)
TIME_COMMAND_MODE = None
return False
else: # Linux check passed if check=True didn't raise exception
print_color(f"Memory measurement enabled using '{TIME_COMMAND} -f {LINUX_TIME_FORMAT}'.", COLOR_GREEN)
return True
except subprocess.CalledProcessError as e:
# This is where the original macOS error occurred
print_color(f"Warning: {TIME_COMMAND} test command failed (exit code {e.returncode}). Memory measurement disabled.", COLOR_YELLOW)
if e.stderr: print(f"Stderr:\n{e.stderr}")
TIME_COMMAND_MODE = None
return False
except FileNotFoundError: # Should have been caught by shutil.which, but belt-and-suspenders
print_color(f"Warning: '{TIME_COMMAND}' not found during test run. Memory measurement disabled.", COLOR_YELLOW)
TIME_COMMAND_MODE = None
return False
except Exception as e:
print_color(f"Warning: An unexpected error occurred while testing {TIME_COMMAND}. Memory measurement disabled. Error: {e}", COLOR_YELLOW)
TIME_COMMAND_MODE = None
return False
# --- compile_program() remains the same ---
def compile_program():
"""Compiles the C++ source files."""
print_color(f"--- Starting Compilation ---", COLOR_BLUE)
source_files = glob.glob(SOURCE_FILES_PATTERN)
if not source_files:
print_color(f"Error: No source files found matching pattern '{SOURCE_FILES_PATTERN}'.", COLOR_RED)
return False
compile_command = [CXX] + CXXFLAGS + ["-o", os.path.basename(EXECUTABLE)] + source_files
command_str = " ".join(compile_command)
print(f"Running: {command_str}")
try:
start_time = time.perf_counter()
process = subprocess.run(compile_command, check=False, capture_output=True, text=True)
end_time = time.perf_counter()
duration = end_time - start_time
if process.returncode == 0:
print_color(f"Compilation successful (took {duration:.3f}s).", COLOR_GREEN)
if process.stderr:
print_color("Compiler Warnings/Messages:", COLOR_YELLOW)
print(process.stderr)
return True
else:
print_color(f"Compilation failed with exit code {process.returncode} (took {duration:.3f}s).", COLOR_RED)
print_color("Compiler Error Output:", COLOR_RED)
print(process.stderr if process.stderr else "(No compiler error output captured)")
return False
except FileNotFoundError:
print_color(f"Error: Compiler '{CXX}' not found.", COLOR_RED)
return False
except Exception as e:
print_color(f"An unexpected error occurred during compilation: {e}", COLOR_RED)
return False
def run_test(test_name, input_file, expected_output_file, argument):
"""
Runs test, measures time/memory (platform-specific), suppresses output.
Returns: tuple (passed: bool, reason: str, duration: float | None, memory_kb: int | None)
"""
global MEASURE_MEMORY, TIME_COMMAND_MODE # Access potentially updated flags
print_color(f"--- Running {test_name} ---", COLOR_BLUE)
duration = None
memory_kb = None
captured_stderr_for_mem = None # Store stderr specifically for macos parsing
# Prerequisite checks
if not os.path.exists(input_file): return False, "Input file missing", None, None
if not os.path.exists(expected_output_file): return False, "Expected output file missing", None, None
if not os.path.exists(EXECUTABLE): return False, "Executable not found", None, None
# --- Command Construction & subprocess args ---
base_command = [EXECUTABLE, input_file, TEMP_OUTPUT_FILE, argument]
run_command = []
subprocess_kwargs = { # Base arguments for subprocess.run
"check": False,
"timeout": TEST_TIMEOUT
}
if MEASURE_MEMORY and TIME_COMMAND_MODE: # Check both desire and capability
if TIME_COMMAND_MODE == "linux":
run_command = [TIME_COMMAND, '-f', LINUX_TIME_FORMAT, '-o', LINUX_TIME_OUTPUT_FILE] + base_command
if os.path.exists(LINUX_TIME_OUTPUT_FILE):
try: os.remove(LINUX_TIME_OUTPUT_FILE)
except OSError: pass
# For Linux, memory info goes to file, handle stdout/stderr normally based on suppression
subprocess_kwargs["stdout"] = subprocess.DEVNULL if SUPPRESS_PROGRAM_OUTPUT else None
subprocess_kwargs["stderr"] = subprocess.DEVNULL if SUPPRESS_PROGRAM_OUTPUT else None
elif TIME_COMMAND_MODE == "macos":
run_command = [TIME_COMMAND, '-l'] + base_command
# On macOS, need to capture stderr for parsing memory, stdout handles suppression
subprocess_kwargs["stdout"] = subprocess.DEVNULL if SUPPRESS_PROGRAM_OUTPUT else None
subprocess_kwargs["stderr"] = subprocess.PIPE # Capture stderr for parsing
subprocess_kwargs["text"] = True # Decode captured stderr
else: # Not measuring memory or platform unsupported
run_command = base_command
subprocess_kwargs["stdout"] = subprocess.DEVNULL if SUPPRESS_PROGRAM_OUTPUT else None
subprocess_kwargs["stderr"] = subprocess.DEVNULL if SUPPRESS_PROGRAM_OUTPUT else None
command_str = " ".join(run_command)
print(f"Executing: {command_str}")
# --- Execution and Measurement ---
if os.path.exists(TEMP_OUTPUT_FILE):
try: os.remove(TEMP_OUTPUT_FILE)
except OSError as e: print_color(f"Warning: Could not remove {TEMP_OUTPUT_FILE}: {e}", COLOR_YELLOW)
try:
start_time = time.perf_counter()
process = subprocess.run(run_command, **subprocess_kwargs)
end_time = time.perf_counter()
duration = end_time - start_time
print(f"Execution Time: {duration:.3f} seconds")
# --- Process Memory Output (Platform Specific) ---
if MEASURE_MEMORY and TIME_COMMAND_MODE:
if TIME_COMMAND_MODE == "linux":
if os.path.exists(LINUX_TIME_OUTPUT_FILE):
try:
with open(LINUX_TIME_OUTPUT_FILE, 'r') as f_time:
mem_str = f_time.read().strip()
if mem_str:
memory_kb = int(mem_str) # Already in KB
print(f"Peak Memory Usage: {memory_kb} KB")
else: print_color(f"Warning: {LINUX_TIME_OUTPUT_FILE} was empty.", COLOR_YELLOW)
except (ValueError, IOError) as e: print_color(f"Warning: Could not parse memory (Linux) from {LINUX_TIME_OUTPUT_FILE}: {e}", COLOR_YELLOW)
finally:
try: os.remove(LINUX_TIME_OUTPUT_FILE)
except OSError: pass
else: print_color(f"Warning: {LINUX_TIME_OUTPUT_FILE} was not created.", COLOR_YELLOW)
elif TIME_COMMAND_MODE == "macos":
# Parse memory from captured stderr (process.stderr)
if process.stderr:
match = MACOS_MEM_REGEX.search(process.stderr)
if match:
try:
mem_bytes = int(match.group(1))
memory_kb = mem_bytes // 1024 # Convert Bytes to KB
print(f"Peak Memory Usage: {memory_kb} KB ({mem_bytes} Bytes)")
except (ValueError, IndexError):
print_color(f"Warning: Could not parse memory value (macOS) from captured output.", COLOR_YELLOW)
# Optional: print process.stderr here for debugging
# print(f"--- time -l stderr ---\n{process.stderr}\n----------------------")
else:
print_color(f"Warning: 'maximum resident set size' not found in 'time -l' output (macOS).", COLOR_YELLOW)
# Optional: print process.stderr here for debugging
# print(f"--- time -l stderr ---\n{process.stderr}\n----------------------")
else:
print_color(f"Warning: No stderr captured from 'time -l' (macOS).", COLOR_YELLOW)
# --- Check Program Result ---
if process.returncode != 0:
print_color(f"Test failed: Program exited with non-zero status {process.returncode}.", COLOR_RED)
# Note: program's own stderr might be in process.stderr ONLY if not suppressed AND on macOS
# It's generally hidden now by design.
return False, "Runtime error", duration, memory_kb
if not os.path.exists(TEMP_OUTPUT_FILE):
print_color(f"Test failed: Program finished successfully but did not create '{TEMP_OUTPUT_FILE}'.", COLOR_RED)
return False, "Output file not created", duration, memory_kb
# --- Compare Output File ---
if filecmp.cmp(TEMP_OUTPUT_FILE, expected_output_file, shallow=False):
print_color(f"Test Result: PASSED", COLOR_GREEN)
return True, "Passed", duration, memory_kb
else:
# ... (diff printing remains the same) ...
print_color(f"Test Result: FAILED - Output mismatch.", COLOR_RED)
print_color(f" Expected: {expected_output_file}", COLOR_YELLOW)
print_color(f" Actual: {TEMP_OUTPUT_FILE}", COLOR_YELLOW)
try:
diff_proc = subprocess.run(['diff', '-u', expected_output_file, TEMP_OUTPUT_FILE], capture_output=True, text=True)
print_color("--- Diff ---", COLOR_YELLOW)
print(diff_proc.stdout if diff_proc.stdout else "(No differences found by diff, might be whitespace or encoding issues)")
print_color("------------", COLOR_YELLOW)
except FileNotFoundError: print_color("Could not run 'diff' command.", COLOR_YELLOW)
except Exception as diff_e: print_color(f"Error running diff: {diff_e}", COLOR_YELLOW)
return False, "Output mismatch", duration, memory_kb
# --- Exception Handling ---
except subprocess.TimeoutExpired:
end_time = time.perf_counter()
duration = end_time - start_time
print_color(f"Test failed: Program timed out after {duration:.3f}s (limit: {TEST_TIMEOUT}s).", COLOR_RED)
# Attempt to parse memory ONLY if macOS and stderr might have been partially captured (unlikely but possible)
if MEASURE_MEMORY and TIME_COMMAND_MODE == "macos" and process and process.stderr:
match = MACOS_MEM_REGEX.search(process.stderr)
if match:
try: memory_kb = int(match.group(1)) // 1024
except: memory_kb = None # Ignore parsing errors on timeout
# Clean up Linux temp file if it exists
if MEASURE_MEMORY and TIME_COMMAND_MODE == "linux" and os.path.exists(LINUX_TIME_OUTPUT_FILE):
try: os.remove(LINUX_TIME_OUTPUT_FILE)
except OSError: pass
return False, "Timeout", duration, memory_kb
except Exception as e:
print_color(f"An unexpected error occurred during test execution: {e}", COLOR_RED)
# Clean up Linux temp file if it exists
if MEASURE_MEMORY and TIME_COMMAND_MODE == "linux" and os.path.exists(LINUX_TIME_OUTPUT_FILE):
try: os.remove(LINUX_TIME_OUTPUT_FILE)
except OSError: pass
return False, f"Execution exception: {e}", None, None
finally:
# General cleanup (Linux temp file might still exist if parsing failed)
if MEASURE_MEMORY and TIME_COMMAND_MODE == "linux" and os.path.exists(LINUX_TIME_OUTPUT_FILE):
try: os.remove(LINUX_TIME_OUTPUT_FILE)
except OSError: pass
# --- Main Execution ---
if __name__ == "__main__":
# 0. Check if memory measurement is desired AND possible
user_wants_memory_measurement = MEASURE_MEMORY
if user_wants_memory_measurement:
can_actually_measure = check_time_command()
MEASURE_MEMORY = can_actually_measure # Update based on check
else:
MEASURE_MEMORY = False
print_color("Memory measurement explicitly disabled by configuration.", COLOR_YELLOW)
if SUPPRESS_PROGRAM_OUTPUT:
print_color("Program stdout/stderr will be suppressed during tests.", COLOR_BLUE)
# 1. Compile
if not compile_program():
print_color("\nCompilation failed. Aborting tests.", COLOR_RED)
sys.exit(1)
# 2. Define Test Cases
# ... (test_bases and arguments_to_test remain the same) ...
test_bases = [
("1", "tiny1"), ("2", "tiny2"), ("3", "small1"), ("4", "small2"),
("5", "medium1"), ("6", "medium2"), ("7", "large1"), ("8", "large2"),
("9", "large3"), ("10", "large4"), ("11", "large5"), ("12", "large6"),
("13", "large7"), ("14", "large8"), ("15", "large9"),
]
arguments_to_test = ["hashtag", "sound"]
results = {"passed": 0, "failed": 0, "skipped": 0}
failed_tests = []
test_durations = []
test_memory_usages = []
# 3. Run Tests
print_color("\n--- Starting Test Execution ---", COLOR_BLUE)
total_start_time = time.perf_counter()
for id_prefix, base_name in test_bases:
for i, argument in enumerate(arguments_to_test, 1):
# ... (construct test names/paths) ...
test_id = f"{id_prefix}.{i}"
test_name = f"Test Case {test_id}: input {base_name}, {argument}"
input_filename = os.path.join(INPUT_DIR, f"input_{base_name}.json")
expected_output_filename = os.path.join(EXPECTED_OUTPUT_DIR, f"output_{base_name}_{argument}.txt")
passed, reason, duration, memory_kb = run_test(test_name, input_filename, expected_output_filename, argument)
# ... (Update results logic remains the same, relies on memory_kb being None if not measured) ...
if passed:
results["passed"] += 1
if duration is not None: test_durations.append(duration)
if MEASURE_MEMORY and memory_kb is not None: test_memory_usages.append(memory_kb)
elif reason in ["Input file missing", "Expected output file missing", "Executable not found"]:
results["skipped"] += 1
else:
results["failed"] += 1
duration_str = f" ({duration:.3f}s)" if duration is not None else ""
mem_str = f", {memory_kb} KB" if MEASURE_MEMORY and memory_kb is not None else ""
failed_tests.append(f"{test_name} ({reason}{duration_str}{mem_str})")
print("-" * 40)
total_end_time = time.perf_counter()
total_test_suite_duration = total_end_time - total_start_time
# 4. Clean up
# ... (same cleanup logic) ...
print_color("--- Cleaning Up ---", COLOR_BLUE)
if os.path.exists(TEMP_OUTPUT_FILE):
try:
os.remove(TEMP_OUTPUT_FILE)
print(f"Removed temporary output file: {TEMP_OUTPUT_FILE}")
except OSError as e: print_color(f"Warning: Could not remove {TEMP_OUTPUT_FILE}: {e}", COLOR_YELLOW)
if os.path.exists(EXECUTABLE):
try:
os.remove(EXECUTABLE)
print(f"Removed executable: {EXECUTABLE}")
except OSError as e: print_color(f"Warning: Could not remove {EXECUTABLE}: {e}", COLOR_YELLOW)
# 5. Print Summary
# ... (summary printing logic remains the same) ...
# Note: Memory summary section only appears if MEASURE_MEMORY is True at the end.
print_color("\n--- Test Summary ---", COLOR_BLUE)
print_color(f"Passed: {results['passed']}", COLOR_GREEN)
print_color(f"Failed: {results['failed']}", COLOR_RED if results['failed'] > 0 else COLOR_GREEN)
print_color(f"Skipped: {results['skipped']}", COLOR_YELLOW if results['skipped'] > 0 else COLOR_GREEN)
total_run = results['passed'] + results['failed']
total_defined = total_run + results['skipped']
print(f"Total Tests Defined: {total_defined}")
print(f"Total Tests Run: {total_run}")
print(f"Total Test Suite Execution Time: {total_test_suite_duration:.3f}s")
# Performance Summary
if test_durations:
# ... (same calculation and printing) ...
total_passed_time = sum(test_durations)
avg_time = total_passed_time / len(test_durations)
max_time = max(test_durations)
min_time = min(test_durations)
print("\n--- Performance Summary (Passed Tests) ---")
print(f"Total execution time (passed tests): {total_passed_time:.3f}s")
print(f"Average execution time per test: {avg_time:.3f}s")
print(f"Fastest test execution time: {min_time:.3f}s")
print(f"Slowest test execution time: {max_time:.3f}s")
# Memory Summary
if MEASURE_MEMORY: # Check final flag state
if test_memory_usages:
# ... (same calculation and printing) ...
total_mem_kb = sum(test_memory_usages)
avg_mem_kb = total_mem_kb / len(test_memory_usages)
max_mem_kb = max(test_memory_usages)
min_mem_kb = min(test_memory_usages)
total_mem_mb = total_mem_kb / 1024
total_mem_gb = total_mem_mb / 1024
if total_mem_gb > 1: total_mem_str = f"{total_mem_gb:.2f} GB"
elif total_mem_mb > 1: total_mem_str = f"{total_mem_mb:.2f} MB"
else: total_mem_str = f"{total_mem_kb} KB"
print("\n--- Memory Usage Summary (Passed Tests) ---")
print(f"Cumulative peak memory (passed tests): {total_mem_str} ({total_mem_kb} KB)")
print(f"Average peak memory per test: {avg_mem_kb:.1f} KB")
print(f"Lowest peak memory usage: {min_mem_kb} KB")
print(f"Highest peak memory usage: {max_mem_kb} KB")
else:
print("\n--- Memory Usage Summary (Passed Tests) ---")
print("(No memory usage data collected for passed tests - check warnings)")
# Final Result
if failed_tests:
print_color("\n--- Failed Test Cases ---", COLOR_RED)
for test in failed_tests:
print(f" - {test}")
sys.exit(1)
# ... (rest of exit logic remains the same) ...
elif results['passed'] == 0 and results['skipped'] == total_defined:
print_color("\nWarning: No tests were executed (all skipped).", COLOR_YELLOW)
sys.exit(0)
elif results['passed'] > 0 :
print_color("\nAll executed tests passed successfully!", COLOR_GREEN)
sys.exit(0)
else:
print_color("\nNo tests passed.", COLOR_YELLOW)
sys.exit(1 if results['failed'] > 0 else 0)

View File

@@ -1,4 +1,4 @@
# Lab 13 — Multiple Inheritance & Exceptions # Lab 11 — Multiple Inheritance & Exceptions
For this lab you will build a class inheritance structure to match the hierarchy of classic geometric shapes. For this lab you will build a class inheritance structure to match the hierarchy of classic geometric shapes.
The finished program will read lists of 2D point coordinates from a file and determine the shape described The finished program will read lists of 2D point coordinates from a file and determine the shape described

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -166,6 +166,8 @@ Preorder Traversal using Morris Traversal:
1 2 4 5 6 7 3 8 9 1 2 4 5 6 7 3 8 9
``` ```
Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/trees/morris/morris_pre_order.html) to understand how this works.
## 21.6 Morris Traversal - Post Order ## 21.6 Morris Traversal - Post Order
Post order is different, and we need to write some helper functions here. Post order is different, and we need to write some helper functions here.
@@ -238,6 +240,8 @@ Postorder Traversal using Morris Traversal:
4 6 7 5 2 9 8 3 1 4 6 7 5 2 9 8 3 1
``` ```
Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/trees/morris/morrisPostOrder.html) to understand how this works.
## Time and Space Complexity in Morris Traversal (in-order, pre-order, post-order) ## Time and Space Complexity in Morris Traversal (in-order, pre-order, post-order)
- Time Complexity: O(N) (each node is visited at most twice) - Time Complexity: O(N) (each node is visited at most twice)

View File

@@ -222,5 +222,6 @@ In addition to the above solution, there are also other variations of the heap s
- [Leetcode problem 912: Sort an Array](https://leetcode.com/problems/sort-an-array/). - [Leetcode problem 912: Sort an Array](https://leetcode.com/problems/sort-an-array/).
- Solution: [p912_heapsort_array.cpp](../../leetcode/p912_heapsort_array.cpp). - Solution: [p912_heapsort_array.cpp](../../leetcode/p912_heapsort_array.cpp).
- Solution 2: - max heap: [p912_heapsort_array_heapify.cpp](../../leetcode/p912_heapsort_array_heapify.cpp). - Solution 2: - max heap: [p912_heapsort_array_heapify.cpp](../../leetcode/p912_heapsort_array_heapify.cpp).
- Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/heap/sort/index.html) to see how this sort works.
- Solution 3: - min heap: [p912_heapsort_array_min_heap.cpp](../../leetcode/p912_heapsort_array_min_heap.cpp). - Solution 3: - min heap: [p912_heapsort_array_min_heap.cpp](../../leetcode/p912_heapsort_array_min_heap.cpp).
- Solution 4: - min heap, with functor: [p912_heapsort_array_functor.cpp](../../leetcode/p912_heapsort_array_functor.cpp). - Solution 4: - min heap, with functor: [p912_heapsort_array_functor.cpp](../../leetcode/p912_heapsort_array_functor.cpp).

View File

@@ -0,0 +1,299 @@
# Lecture 25 --- C++ Inheritance and Polymorphism
## 25.1 C++ Inheritance
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class (called a derived or child class) to acquire properties and behaviors (methods) from another class (called a base or parent class). This promotes code reusability and establishes a hierarchical relationship between classes.
## 25.2 Key Concepts
- Base Class: The class whose properties and methods are inherited.
- Derived Class: The class that inherits from the base class.
- Access Specifiers: Determine the accessibility of base class members in the derived class.
## 25.3 Types of Inheritance
### 25.3.1 Single Inheritance
A derived class inherits from one base class.
```cpp
class Base {
// Base class members
};
class Derived : public Base {
// Derived class members
};
```
### 25.3.2 Multiple Inheritance
A derived class inherits from more than one base class.
```cpp
class Base1 {
// Base1 members
};
class Base2 {
// Base2 members
};
class Derived : public Base1, public Base2 {
// Derived class members
};
```
### 25.3.3 Multilevel Inheritance
A class is derived from a class which is also derived from another class.
```cpp
class Base {
// Base class members
};
class Intermediate : public Base {
// Intermediate class members
};
class Derived : public Intermediate {
// Derived class members
};
```
### 25.3.4 Hierarchical Inheritance
Multiple classes are derived from a single base class.
```cpp
class Base {
// Base class members
};
class Derived1 : public Base {
// Derived1 members
};
class Derived2 : public Base {
// Derived2 members
};
```
## 25.4 Access Specifiers in Inheritance: public, protected, and private
The access specifier used during inheritance affects the accessibility of base class members in the derived class. In C++, the access specifier used during inheritance (public, protected, or private) determines how the base class's members are accessible in the derived class and to external code. Understanding these distinctions is crucial for designing class hierarchies that enforce appropriate encapsulation and access control.
| Inheritance Type | Base Class `public` Members in Derived | Base Class `protected` Members in Derived | Base Class `private` Members in Derived |
|------------------|----------------------------------------|-------------------------------------------|-----------------------------------------|
| `public` | `public` | `protected` | Not accessible |
| `protected` | `protected` | `protected` | Not accessible |
| `private` | `private` | `private` | Not accessible |
```cpp
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class PublicDerived : public Base {
// publicVar is public
// protectedVar is protected
// privateVar is not accessible
};
class ProtectedDerived : protected Base {
// publicVar is protected
// protectedVar is protected
// privateVar is not accessible
};
class PrivateDerived : private Base {
// publicVar is private
// protectedVar is private
// privateVar is not accessible
};
```
### 25.4.1 Default Inheritance:
If no access specifier is provided:
- For class, inheritance is private by default.
- For struct, inheritance is public by default.
### 25.4.2 Access to Base Class Members:
private members of the base class are never accessible directly in the derived class, regardless of the inheritance type.
## 25.5 Constructors and Destructors
- Constructor Invocation: When a derived class object is created, the base class constructor is invoked first, followed by the derived class constructor.
- Destructor Invocation: When a derived class object is destroyed, the derived class destructor is invoked first, followed by the base class destructor.
- Explicit Constructor Call: If the base class constructor requires parameters, the derived class constructor must explicitly call it using an initializer list.
```cpp
class Base {
public:
Base(int x) {
// Constructor implementation
}
};
class Derived : public Base {
public:
Derived(int x) : Base(x) {
// Derived class constructor implementation
}
};
```
### Common Pitfall:
- Member initializer lists play an important role in inheritance in C++.
- If you try to initialize the base class inside the constructor body (not the initializer list), it wont work — the base class has already been default-constructed by that point! This is way, for the following two programs:
[program1](constructor_test1.cpp) will compile; but [program2](constructor_test2.cpp) won't compile.
## 25.6 Name Hiding
In C++, when a derived class defines a member function with the same name as one in its base class, the derived class's function hides all base class functions with that name, regardless of their signatures. This behavior is known as name hiding.
```cpp
#include <iostream>
class A {
public:
void func(int x) {
std::cout << "A::func(int): " << x << "\n";
}
};
class B : public A {
public:
void func(double y) {
std::cout << "B::func(double): " << y << "\n";
}
};
int main() {
B b;
b.func(10); // Calls B::func(double)
b.func(10.5); // Calls B::func(double)
return 0;
}
```
This above program [name_hiding.cpp](name_hiding.cpp) prints:
```console
$ g++ name_hiding.cpp
$ ./a.out
B::func(double): 10
B::func(double): 10.5
```
## 25.6 Example Programs:
We develop this student_test.cpp program.
### version 1
[students/student_test1.cpp](students/student_test1.cpp) In this version, there is only one class: class Human
### version 2
[students/student_test2.cpp](students/student_test2.cpp) In this version, there is the base class - Human, and the child class - Student, but the child class doesn't have its own member variables, and its only member function is the constructor.
### version 3
[students/student_test3.cpp](students/student_test3.cpp) In this version, the child class has its own member functions introduce() and sleep(); and because these functions need to access the parent class's member variables, so we changed the member variables from private to protected.
### version 4
[students/student_test4.cpp](students/student_test4.cpp) In this version, we added the destructor to both the child class and the parent class. This program shows that when the child class object is destroyed, first its own destructor gets called, and then its parent destructor gets called.
### version 5
[students/student_test5.cpp](students/student_test5.cpp) In this version, we added the CSStudent class, and that introduces multilevel inheritance in the program.
## 25.7 What will be printed when running this program?
```cpp
#include <iostream>
class A {
public:
A() {
std::cout << "A";
}
~A() {
std::cout << "A";
}
};
class B : public A {
public:
B() {
std::cout << "B";
}
~B() {
std::cout << "B";
}
};
int main() {
{
A a;
B b;
}
std::cout << std::endl;
return 0;
}
```
## 25.8 What will be printed when running this program?
```cpp
#include <iostream>
class A {
public:
A() {
std::cout << "A";
}
~A() {
std::cout << "A";
}
};
class B : public A {
public:
B() {
std::cout << "B";
}
~B() {
std::cout << "B";
}
};
int main() {
{
B* p = new B;
B b;
delete p;
}
std::cout << std::endl;
return 0;
}
```

View File

@@ -0,0 +1,18 @@
#include <iostream>
class Base {
public:
Base(int x) {
std::cout << "Base constructor: " << x << std::endl;
}
};
class Derived : public Base {
public:
Derived(int x) : Base(x) { // member initializer list calls Base constructor
std::cout << "Derived constructor" << std::endl;
}
};
int main(){
}

View File

@@ -0,0 +1,19 @@
#include <iostream>
class Base {
public:
Base(int x) {
std::cout << "Base constructor: " << x << std::endl;
}
};
class Derived : public Base {
public:
Derived(int x) { // member initializer list calls Base constructor
Base(x);
std::cout << "Derived constructor" << std::endl;
}
};
int main(){
}

View File

@@ -0,0 +1,31 @@
#include <iostream>
class A {
public:
A() {
std::cout << "A";
}
~A() {
std::cout << "A";
}
};
class B : public A {
public:
B() {
std::cout << "B";
}
~B() {
std::cout << "B";
}
};
int main() {
{
A a;
B b;
}
std::cout << std::endl;
return 0;
}

View File

@@ -0,0 +1,32 @@
#include <iostream>
class A {
public:
A() {
std::cout << "A";
}
~A() {
std::cout << "A";
}
};
class B : public A {
public:
B() {
std::cout << "B";
}
~B() {
std::cout << "B";
}
};
int main() {
{
B* p = new B;
B b;
delete p;
}
std::cout << std::endl;
return 0;
}

View File

@@ -0,0 +1,22 @@
#include <iostream>
class A {
public:
void func(int x) {
std::cout << "A::func(int): " << x << "\n";
}
};
class B : public A {
public:
void func(double y) {
std::cout << "B::func(double): " << y << "\n";
}
};
int main() {
B b;
b.func(10); // Calls B::func(double)
b.func(10.5); // Calls B::func(double)
return 0;
}

View File

@@ -0,0 +1,35 @@
#include <iostream>
#include <string>
class Human {
private:
std::string name;
int age;
int sleep_hours;
public:
Human(std::string n, int a, int s) : name(n), age(a), sleep_hours(s) {}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old.\n";
}
void sleep() {
std::cout << name << " is " << age << " years old who sleeps " << sleep_hours << " hours a night.\n";
}
};
int main() {
// Creating instances of each class with member variables
Human h("Alice", 30, 8);
// Introducing Humans
std::cout << "--- Human introducing ---\n";
h.introduce(); // Output: Hello, I am Alice, and I am 30 years old.
// Showing sleep behavior
std::cout << "--- Human sleep ---\n";
h.sleep(); // Output: Alice is 30 years old and sleeps 8 hours a night.
return 0;
}

View File

@@ -0,0 +1,54 @@
#include <iostream>
#include <string>
class Human {
private:
std::string name;
int age;
int sleep_hours;
public:
Human(std::string n, int a, int s) : name(n), age(a), sleep_hours(s) {}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old.\n";
}
void sleep() {
std::cout << name << " is " << age << " years old who sleeps " << sleep_hours << " hours a night.\n";
}
};
class Student : public Human {
public:
/* in C++, when a derived class inherits from a base class,
* the base class's constructor must be called to initialize its members.
* This is because the base class may contain private or protected members
* that are not directly accessible by the derived class.
* Therefore, the derived class relies on the base class's constructor to properly initialize these members. */
Student(std::string n, int a, int s) : Human(n, a, s) {}
};
int main() {
// Creating instances of each class with member variables
Human h("Alice", 30, 8);
Student s("Bob", 20, 5);
// Introducing Humans
std::cout << "--- Human introducing ---\n";
h.introduce(); // Output: Hello, I am Alice, and I am 30 years old.
std::cout << "--- Student introducing ---\n";
s.introduce(); // Output: I am a student. My name is Bob, and I am 20 years old.
// Showing sleep behavior
std::cout << "--- Human sleep ---\n";
h.sleep(); // Output: Alice is 30 years old and sleeps 8 hours a night.
std::cout << "--- Student sleep ---\n";
s.sleep(); // Output: Bob is a student, and they sleep 5 hours a night.
return 0;
}

View File

@@ -0,0 +1,58 @@
#include <iostream>
#include <string>
class Human {
// change this to protect so that these variables can be accessed by the derived class.
protected:
std::string name;
int age;
int sleep_hours;
public:
Human(std::string n, int a, int s) : name(n), age(a), sleep_hours(s) {}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old.\n";
}
void sleep() {
std::cout << name << " is " << age << " years old who sleeps " << sleep_hours << " hours a night.\n";
}
};
class Student : public Human {
public:
Student(std::string n, int a, int s) : Human(n, a, s) {}
// note that the Student class never defines the name and age variables, but we can use them here, because of inheritance.
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old. Im majoring in 'How did I get here?' with a minor in 'It sounded easier when I signed up.'\n";
}
void sleep() {
std::cout << name << " is a college student who sleeps " << sleep_hours << " hours a night, and sleep 2 hours during boring lectures.\n";
}
};
int main() {
// Creating instances of each class with member variables
Human h("Alice", 30, 8);
Student s("Bob", 20, 5);
// Introducing Humans
std::cout << "--- Human introducing ---\n";
h.introduce(); // Output: Hello, I am Alice, and I am 30 years old.
std::cout << "--- Student introducing ---\n";
s.introduce(); // Output: I am a student. My name is Bob, and I am 20 years old.
// Showing sleep behavior
std::cout << "--- Human sleep ---\n";
h.sleep(); // Output: Alice is 30 years old and sleeps 8 hours a night.
std::cout << "--- Student sleep ---\n";
s.sleep(); // Output: Bob is a student, and they sleep 5 hours a night.
return 0;
}

View File

@@ -0,0 +1,63 @@
#include <iostream>
#include <string>
class Human {
// change this to protect so that these variables can be accessed by the derived class.
protected:
std::string name;
int age;
int sleep_hours;
public:
Human(std::string n, int a, int s) : name(n), age(a), sleep_hours(s) {}
~Human(){
std::cout << "Human Destructor" << std::endl;
}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old.\n";
}
void sleep() {
std::cout << name << " is " << age << " years old who sleeps " << sleep_hours << " hours a night.\n";
}
};
class Student : public Human {
public:
Student(std::string n, int a, int s) : Human(n, a, s) {}
~Student(){
std::cout << "Student Destructor" << std::endl;
}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old. Im majoring in 'How did I get here?' with a minor in 'It sounded easier when I signed up.'\n";
}
void sleep() {
std::cout << name << " is a college student who sleeps " << sleep_hours << " hours a night, and sleep 2 hours during boring lectures.\n";
}
};
int main() {
// Creating instances of each class with member variables
Human h("Alice", 30, 8);
Student s("Bob", 20, 5);
// Introducing Humans
std::cout << "--- Human introducing ---\n";
h.introduce(); // Output: Hello, I am Alice, and I am 30 years old.
std::cout << "--- Student introducing ---\n";
s.introduce(); // Output: I am a student. My name is Bob, and I am 20 years old.
// Showing sleep behavior
std::cout << "--- Human sleep ---\n";
h.sleep(); // Output: Alice is 30 years old and sleeps 8 hours a night.
std::cout << "--- Student sleep ---\n";
s.sleep(); // Output: Bob is a student, and they sleep 5 hours a night.
return 0;
}

View File

@@ -0,0 +1,88 @@
#include <iostream>
#include <string>
class Human {
// change this to protect so that these variables can be accessed by the derived class.
protected:
std::string name;
int age;
int sleep_hours;
public:
Human(std::string n, int a, int s) : name(n), age(a), sleep_hours(s) {}
~Human(){
std::cout << "Human Destructor" << std::endl;
}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old.\n";
}
void sleep() {
std::cout << name << " is " << age << " years old who sleeps " << sleep_hours << " hours a night.\n";
}
};
class Student : public Human {
public:
Student(std::string n, int a, int s) : Human(n, a, s) {}
~Student(){
std::cout << "Student Destructor" << std::endl;
}
void introduce() {
std::cout << "Hello, I am " << name << ", and I am " << age << " years old. Im majoring in 'How did I get here?' with a minor in 'It sounded easier when I signed up.'\n";
}
void sleep() {
std::cout << name << " is a college student who sleeps " << sleep_hours << " hours a night, and sleep 2 hours during boring lectures.\n";
}
};
class CSStudent : public Student {
public:
// note that CSStudent constructor needs to call Student constructor.
CSStudent(std::string n, int a, int s) : Student(n, a, s) {}
~CSStudent(){
std::cout << "CS Student Destructor" << std::endl;
}
void introduce() {
std::cout << "I am a CS student. My name is " << name << ", and I am " << age << " years old.\n";
}
void sleep() {
std::cout << name << " is a CS student who sleeps " << sleep_hours << " hours a night. "
<< "Or maybe, just a few minutes if the code compiles!\n";
}
};
int main() {
// Creating instances of each class with member variables
Human h("Alice", 30, 8);
Student s("Bob", 20, 5);
CSStudent cs("Charlie", 22, 2);
// Introducing Humans
std::cout << "--- Human introducing ---\n";
h.introduce(); // Output: Hello, I am Alice, and I am 30 years old.
std::cout << "--- Student introducing ---\n";
s.introduce(); // Output: I am a student. My name is Bob, and I am 20 years old.
std::cout << "--- CS Student introducing ---\n";
cs.introduce(); // Output: I am a CS student. My name is Charlie, and I am 22 years old.
// Showing sleep behavior
std::cout << "--- Human sleep ---\n";
h.sleep(); // Output: Alice is 30 years old and sleeps 8 hours a night.
std::cout << "--- Student sleep ---\n";
s.sleep(); // Output: Bob is a student, and they sleep 5 hours a night.
std::cout << "--- CS Student introducing ---\n";
cs.sleep(); // Output: I am a CS student. My name is Charlie, and I am 22 years old.
return 0;
}

View File

@@ -1,339 +0,0 @@
# Lecture 26 --- C++ Inheritance and Polymorphism
## Todays Lecture
- Inheritance is a relationship among classes. Examples: bank accounts, polygons, stack & list
- Basic mechanisms of inheritance
- Types of inheritance
- Is-A, Has-A, As-A relationships among classes.
- Polymorphism
## 26.1 Motivating Example: Bank Accounts
- Consider different types of bank accounts:
Savings accounts
Checking accounts
Time withdrawal accounts (like savings accounts, except that only the interest can be withdrawn)
- If you were designing C++ classes to represent each of these, what member functions might be repeated among the different classes? What member functions would be unique to a given class?
- To avoid repeating common member functions and member variables, we will create a class hierarchy, where the common members are placed in a base class and specialized members are placed in derived classes.
## 26.2 Accounts Hierarchy
- Account is the base class of the hierarchy.
- SavingsAccount is a derived class from Account. SavingsAccount has inherited member variables & functions
and ordinarily-defined member variables & functions.
- The member variable balance in base class Account is protected, which means:
balance is NOT publicly accessible outside the class, but it is accessible in the derived classes.
if balance was declared as private, then SavingsAccount member functions could not access it.
- When using objects of type SavingsAccount, the inherited and derived members are treated exactly the same
and are not distinguishable.
- CheckingAccount is also a derived class from base class Account.
- TimeAccount is derived from SavingsAccount. SavingsAccount is its base class and Account is its indirect base class
## 26.3 Exercise: Draw the Accounts Class Hierarchy
```cpp
#include <iostream>
// Note we've inlined all the functions (even though some are > 1 line of code)
class Account {
public:
Account(double bal = 0.0) : balance(bal) {}
void deposit(double amt) { balance += amt; }
double get_balance() const { return balance; }
protected:
double balance; // account balance
};
class SavingsAccount : public Account {
public:
SavingsAccount(double bal = 0.0, double pct = 5.0) : Account(bal), rate(pct/100.0) {}
double compound() { // computes and deposits interest
double interest = balance * rate;
balance += interest;
return interest;
}
double withdraw(double amt) { // if overdraft ==> return 0, else return amount
if (amt > balance) {
return 0.0;
}else {
balance -= amt;
return amt;
}
}
protected:
double rate; // periodic interest rate
};
class CheckingAccount : public Account {
public:
CheckingAccount(double bal = 0.0, double lim = 500.0, double chg = 0.5) : Account(bal), limit(lim), charge(chg) {}
double cash_check(double amt) {
assert (amt > 0);
if (balance < limit && (amt + charge <= balance)) {
balance -= amt + charge;
return amt + charge;
} else if (balance >= limit && amt <= balance) {
balance -= amt;
return amt;
} else {
return 0.0;
}
}
protected:
double limit; // lower limit for free checking
double charge; // per check charge
};
class TimeAccount : public SavingsAccount {
public:
TimeAccount(double bal = 0.0, double pct = 5.0) : SavingsAccount(bal, pct), funds_avail(0.0) {}
// redefines 2 member functions from SavingsAccount
double compound() {
double interest = SavingsAccount::compound();
funds_avail += interest;
return interest;
}
double withdraw(double amt) {
if (amt <= funds_avail) {
funds_avail -= amt;
balance -= amt;
return amt;
} else {
return 0.0;
}
}
double get_avail() const { return funds_avail; };
protected:
double funds_avail; // amount available for withdrawal
};
```
```cpp
Account a(100); //<---one balance member, not related to c1
CheckingAccount c1(100, 366, 0.4); //c1 has it's own CheckingAccount + Account objects <---one balance member
```
## 26.4 Constructors and Destructors
- Constructors of a derived class call the base class constructor immediately, before doing ANYTHING else.
The only thing you can control is which constructor is called and what the arguments will be. Thus when
a TimeAccount is created 3 constructors are called: the Account constructor, then the SavingsAccount
constructor, and then finally the TimeAccount constructor.
- The reverse is true for destructors: derived class destructors do their jobs first and then base class destructors
are called at the end, automatically. Note: destructors for classes which have derived classes must be marked
virtual for this chain of calls to happen.
## 26.5 Overriding Member Functions in Derived Classes
- A derived class can redefine member functions in the base class. The function prototype must be identical, not even the use of const can be different.
- For example, see TimeAccount::compound and TimeAccount::withdraw.
- Once a function is redefined it is not possible to call the base class function, unless it is explicitly called. As an example, the call to SavingsAccount::compound inside of TimeAccount::compound.
## 26.6 Public, Private and Protected Inheritance
- Notice the line
```cpp
class Savings_Account : public Account {
```
This specifies that the member functions and variables from Account do not change their public, protected or private status in SavingsAccount. This is called public inheritance.
- protected and private inheritance are other options:
With protected inheritance, public members becomes protected and other members are unchanged
With private inheritance, all members become private.
## 26.7 Stack Inheriting from List
- For another example of inheritance, lets re-implement the stack class as a derived class of std::list:
```cpp
template <class T>
class stack : private std::list<T> {
public:
stack() {}
stack(stack<T> const& other) : std::list<T>(other) {}
~stack() {}
void push(T const& value) { this->push_back(value); }
void pop() { this->pop_back(); }
T const& top() const { return this->back(); }
int size() { return std::list<T>::size(); }
bool empty() { return std::list<T>::empty(); }
};
```
- Private inheritance hides the std::list&lt;T&gt; member functions from the outside world. However, these member functions are still available to the member functions of the stack&lt;T&gt; class.
- Note: no member variables are defined — the only member variables needed are in the list class.
- When the stack member function uses the same name as the base class (list) member function, the name of the base class followed by :: must be provided to indicate that the base class member function is to be used.
- The copy constructor just uses the copy constructor of the base class, without any special designation because the stack object is a list object as well.
## 26.8 Is-A, Has-A, As-A Relationships Among Classes
- When trying to determine the relationship between (hypothetical) classes C1 and C2, try to think of a logical relationship between them that can be written:
C1 is a C2,
C1 has a C2, or
C1 is implemented as a C2
- If writing “C1 is-a C2” is best, for example: “a savings account is an account”, then C1 should be a derived class (a subclass) of C2.
- If writing “C1 has-a C2” is best, for example: “a cylinder has a circle as its base”, then class C1 should have a member variable of type C2.
- In the case of “C1 is implemented as-a C2”, for example: “the stack is implemented as a list”, then C1 should be derived from C2, but with private inheritance. This is by far the least common case!
## 26.9 Exercise: 2D Geometric Primitives
Create a class hierarchy of geometric objects, such as: triangle, isosceles triangle, right triangle, quadrilateral, square,
rhombus, kite, trapezoid, circle, ellipse, etc. How should this hierarchy be arranged? What member variables and
member functions should be in each class?
## 26.10 Multiple Inheritance
- When sketching a class hierarchy for geometric objects, you may have wanted to specify relationships that were more complex... in particular some objects may wish to inherit from more than one base class.
- This is called multiple inheritance and can make many implementation details significantly more hairy. Different programming languages offer different variations of multiple inheritance.
- See [example 1](multiple_inheritance1.cpp) and [example 2](multiple_inheritance2.cpp).
- And see [example 3](multiple_level_inheritance.cpp) for a multiple level inheritance example.
Note:
![alt text](Note_multipleInheritance.png "MultipleInheritance_note")
## 26.11 Introduction to Polymorphism
- Lets consider a small class hierarchy version of polygonal objects:
```cpp
class Polygon {
public:
Polygon() {}
virtual ~Polygon() {}
int NumVerts() { return verts.size(); }
virtual double Area() = 0;
virtual bool IsSquare() { return false; }
protected:
vector<Point> verts;
};
class Triangle : public Polygon {
public:
Triangle(Point pts[3]) {
for (int i = 0; i < 3; i++) verts.push_back(pts[i]); }
double Area();
};
class Quadrilateral : public Polygon {
public:
Quadrilateral(Point pts[4]) {
for (int i = 0; i < 4; i++) verts.push_back(pts[i]); }
double Area();
double LongerDiagonal();
bool IsSquare() { return (SidesEqual() && AnglesEqual()); }
private:
bool SidesEqual();
bool AnglesEqual();
};
```
- Functions that are common, at least have a common interface, are in Polygon.
- Some of these functions are marked virtual, which means that when they are redefined by a derived class, this new definition will be used, even for pointers to base class objects.
- Some of these virtual functions, those whose declarations are followed by = 0 are pure virtual, which means
they must be redefined in a derived class.
Any class that has pure virtual functions is called “abstract”.
Objects of abstract types may not be created — only pointers to these objects may be created.
- Functions that are specific to a particular object type are declared in the derived class prototype.
## 26.12 A Polymorphic List of Polygon Objects
- Now instead of two separate lists of polygon objects, we can create one “polymorphic” list:
```cpp
std::list<Polygon*> polygons;
```
- Objects are constructed using new and inserted into the list:
```cpp
Polygon *p_ptr = new Triangle( .... );
polygons.push_back(p_ptr);
p_ptr = new Quadrilateral( ... );
polygons.push_back(p_ptr);
Triangle *t_ptr = new Triangle( .... );
polygons.push_back(t_ptr);
```
Note: Weve used the same pointer variable (p_ptr) to point to objects of two different types.
## 26.13 Accessing Objects Through a Polymorphic List of Pointers
- Lets sum the areas of all the polygons:
```cpp
double area = 0;
for (std::list<Polygon*>::iterator i = polygons.begin(); i!=polygons.end(); ++i){
area += (*i)->Area();
}
```
Which Area function is called? If *i points to a Triangle object then the function defined in the Triangle
class would be called. If *i points to a Quadrilateral object then Quadrilateral::Area will be called.
- Heres code to count the number of squares in the list:
```cpp
int count = 0;
for (std::list<Polygon*>::iterator i = polygons.begin(); i!=polygons.end(); ++i){
count += (*i)->IsSquare();
}
```
If Polygon::IsSquare had not been declared virtual then the function defined in Polygon would always be
called! In general, given a pointer to type T we start at T and look “up” the hierarchy for the closest function
definition (this can be done at compile time). If that function has been declared virtual, we will start this
search instead at the actual type of the object (this requires additional work at runtime) in case it has been
redefined in a derived class of type T.
- To use a function in Quadrilateral that is not declared in Polygon, you must “cast” the pointer. The pointer
*q will be NULL if *i is not a Quadrilateral object.
```cpp
for (std::list<Polygon*>::iterator i = polygons.begin(); i!=polygons.end(); ++i) {
Quadrilateral *q = dynamic_cast<Quadrilateral*> (*i);
if (q) std::cout << "diagonal: " << q->LongerDiagonal() << std::endl;
}
```
## 26.14 Exercise
What is the output of the following [program](exercise.cpp)?
```cpp
#include <iostream>
class Base {
public:
Base() {}
virtual void A() { std::cout << "Base A "; }
void B() { std::cout << "Base B "; }
};
class One : public Base {
public:
One() {}
void A() { std::cout << "One A "; }
void B() { std::cout << "One B "; }
};
class Two : public Base {
public:
Two() {}
void A() { std::cout << "Two A "; }
void B() { std::cout << "Two B "; }
};
int main() {
Base* a[3];
a[0] = new Base;
a[1] = new One;
a[2] = new Two;
for (unsigned int i=0; i<3; ++i) {
a[i]->A();
a[i]->B();
}
std::cout << std::endl;
return 0;
}
```
## 26.15 Exercise
What is the output of the following [program](virtual.cpp)?

View File

Before

Width:  |  Height:  |  Size: 398 KiB

After

Width:  |  Height:  |  Size: 398 KiB

View File

@@ -0,0 +1,477 @@
# Lecture 26 --- C++ Inheritance and Polymorphism
## 26.1 Multiple Inheritance
- Multiple inheritance allows a class to inherit from more than one base class.
- The following [program](multiple_inheritance1.cpp) is an example:
```cpp
#include <iostream>
class B
{
public:
B(int b1):b(b1){}
protected:
int b;
};
class C
{
public:
C(int c1):c(c1){}
protected:
int c;
};
class D: public B, public C
{
public:
D(int b1, int c1):B(b1),C(c1),d(b1+c1){}
void print(){
std::cout << "d is " << d << std::endl;
}
protected:
int d;
};
int main(){
D* dOjbect = new D(2,3);
dOjbect->print();
return 0;
}
```
## 26.2 The Diamond Problem
- The Diamond Problem occurs in multiple inheritance when two classes inherit from the same base class, and a fourth class inherits from both of those.
```
Human
/ \
Student Worker
\ /
CSStudent
```
- Both Student and Worker inherit from Human.
```cpp
class Human {
public:
void speak();
};
class Student : public Human {};
class Worker : public Human {};
class CSStudent : public Student, public Worker {};
```
- CSStudent inherits from both Student and Worker.
- Compiler sees two Human base classes.
- This leads to duplicate data and ambiguous member resolution.
```cpp
CSStudent cs;
cs.speak(); // ❌ Ambiguous: which Human::speak()?
```
### Solution: Virtual Inheritance
- Use the virtual keyword when inheriting the common base class.
```cpp
class Student : virtual public Human {};
class Worker : virtual public Human {};
class CSStudent : public Student, public Worker {};
CSStudent cs;
cs.speak(); // ✅ No ambiguity
```
- How it works:
- Compiler ensures only one shared instance of Human.
- Student and Worker do not create their own copies of Human.
- Memory layout uses pointers behind the scenes to share the base.
### Constructor Order with Virtual Inheritance
When virtual inheritance is involved:
Most derived class (e.g., CSStudent) is responsible for calling the base (Human) constructor.
```cpp
class Human {
public:
Human(int age) {}
};
class Student : virtual public Human {
public:
Student() : Human(0) {} // ❌ Not allowed to call Human(int) here
};
class CSStudent : public Student {
public:
CSStudent() : Human(21), Student() {} // ✅ Human constructor called here
};
```
## 26.3 Introduction to Polymorphism
- Polymorphism means "many forms".
- Polymorphism allows one interface to be used for different data types or classes. In C++, it enables objects to be treated as instances of their base type while still calling derived class methods.
- A function marked with the virtual keyword in the base class allows derived classes to override it. At runtime, the correct version of the function is called based on the object type.
- Virtual functions only come into play with pointers or references to the base class.
### 26.3.1 Case 1: Direct Object (No Need for virtual)
```cpp
class Base {
public:
void show() {
std::cout << "Base\n";
}
};
class Derived : public Base {
public:
void show() {
std::cout << "Derived\n";
}
};
int main() {
Derived d;
d.show(); // ✅ "Derived" is printed. No virtual needed.
}
```
### 26.3.2 Case 2: Base Pointer or Reference (Needs virtual)
```cpp
class Base {
public:
virtual void show() {
std::cout << "Base\n";
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived\n";
}
};
int main() {
Base* b = new Derived();
b->show(); // ✅ "Derived" is printed because `show()` is virtual.
}
```
- Without virtual, Base::show() would be called, even though b points to a Derived.
- The override keyword tells the compiler: "I intend to override a virtual function from the base class."
- You dont strictly need the override keyword here — the program will still work as expected because Base::show() is declared virtual, and Derived::show() has the same signature. But using override is highly recommended: when override is used, and if you make a mistake, like mismatching the function signature (even by accident), the compiler will catch it.
### 26.3.3 Virtual Functions in Multi-level Inheritance
- virtual functions are automatically inherited in C++, even if you don't repeat the virtual keyword in the derived class.
```cpp
#include <iostream>
class Base {
public:
virtual void greet() {
std::cout << "Base greet\n";
}
};
class Mid : public Base {
public:
void greet() { // still virtual, even without saying "virtual"
std::cout << "Mid greet\n";
}
};
class Derived : public Mid {
public:
void greet() override {
std::cout << "Derived greet\n";
}
};
int main() {
Base* obj = new Derived();
obj->greet(); // ✅ prints "Derived greet"
}
```
## 26.4 Exercise
What is the output of the following [program](exercise.cpp)?
```cpp
#include <iostream>
class Base {
public:
Base() {}
virtual void A() { std::cout << "Base A "; }
void B() { std::cout << "Base B "; }
};
class One : public Base {
public:
One() {}
void A() { std::cout << "One A "; }
void B() { std::cout << "One B "; }
};
class Two : public Base {
public:
Two() {}
void A() { std::cout << "Two A "; }
void B() { std::cout << "Two B "; }
};
int main() {
Base* a[3];
a[0] = new Base;
a[1] = new One;
a[2] = new Two;
for (unsigned int i=0; i<3; ++i) {
a[i]->A();
a[i]->B();
}
std::cout << std::endl;
return 0;
}
```
## 26.5 Exercise
What is the output of the following [program](virtual.cpp)?
## 26.6 Memory Usage of Virtual Functions
Given the following [program](virtual2.cpp), what is the memory size of each class?
```cpp
#include <iostream>
class Human {
};
class Student {
int age;
};
class CollegeStudent {
int age;
void print(){
std::cout << "I am a college student." << std::endl;
}
};
class CSStudent {
int age;
virtual void print(){
std::cout << "I am a CS student." << std::endl;
}
};
int main(){
std::cout << "memory size of Human class is: " << sizeof(Human) << std::endl;
std::cout << "memory size of Student class is: " << sizeof(Student) << std::endl;
std::cout << "memory size of College Student class is: " << sizeof(CollegeStudent) << std::endl;
std::cout << "memory size of CS Student class is: " << sizeof(CSStudent) << std::endl;
return 0;
}
```
### 26.6.1 Empty Class
- An empty C++ class takes one byte because the C++ standard requires that each distinct object has a unique address in memory.
- If an empty class had size 0, then multiple instances of that class could end up having the same memory address, which would break basic assumptions in C++ like this:
```cpp
class Empty {};
Empty a, b;
std::cout << (&a == &b); // This should be false!
```
- To make sure that &a != &b, the compiler gives each object at least one byte of storage, even if the class doesnt contain any data.
### 26.6.2 Class CSStudent: Total size breakdown
- int age
Size: 4 bytes
- Virtual function (print)
- This makes the class polymorphic, so the compiler adds a vptr (virtual table pointer, also know as vtable pointer).
- On a 64-bit machine, a pointer is 8 bytes.
- Padding/alignment
- The compiler aligns data to certain boundaries for performance.
- Typical alignment for a class with a pointer is 8 bytes, so the 4-byte int is padded with 4 extra bytes.
### 26.6.3 Static Dispatch vs Dynamic Dispatch
- When you call a non-virtual member function like print() in the CollegeStudent class, the compiler resolves the call at compile time. This is known as static dispatch or early binding.
```cpp
CollegeStudent alice;
alice.print();
```
Here's what happens under the hood:
- At compile time, the compiler sees that alice is of type CollegeStudent.
- It knows the exact location of CollegeStudent::print() in the compiled binary (it's in the .text segment).
- So it generates a direct call to that specific memory address. Like *call 0x123456* where *0x123456* is the address of the print() function.
- This is why the object doesnt need to store any pointer to the function — the compiler already knows which function to call!
- If print() were marked virtual, like in the CSStudent class, then the call would become runtime-resolved using a vtable. Then:
- The object now gets a hidden pointer to a vtable (a lookup table of function pointers).
- When you call print(), the program:
- Looks up the function pointer in the vtable.
- Calls the function via that pointer.
- This is called dynamic dispatch or late binding.
Question: What if class CSStudent is defined as this, what would be the memory size of a CSStudent object?
```cpp
class CSStudent {
int age;
virtual void print(){
std::cout << "I am a CS student." << std::endl;
}
virtual void print2(){
std::cout << "I am still a CS student." << std::endl;
}
virtual void print3(){
std::cout << "I am still a CS student." << std::endl;
}
};
```
To understand the problem, compile this [program](virtual3.cpp), and use the tool *pahole* to examine the memory information.
```console
$ g++ -g virtual3.cpp
$ pahole a.out
class CSStudent {
public:
void ~CSStudent(class CSStudent *, int);
void CSStudent(class CSStudent *, );
void CSStudent(class CSStudent *, const class CSStudent &);
void CSStudent(class CSStudent *);
int ()(void) * * _vptr.CSStudent; /* 0 8 */
int age; /* 8 4 */
virtual void print(class CSStudent *);
virtual void print2(class CSStudent *);
virtual void print3(class CSStudent *);
/* vtable has 3 entries: {
[0] = print((null)),
[1] = print2((null)),
[2] = print3((null)),
} */
/* size: 16, cachelines: 1, members: 2 */
/* padding: 4 */
/* last cacheline: 16 bytes */
};
```
The numbers "0 8" mean that the virtual table pointer starts at offset 0 of the class, and it has 8 bytes; the numbers "8 4" means that the variable *age* starts at offset 8 and it has 4 bytes.
## 26.7 Virtual Destructor
- If the destructor of the base class is not virtual, deleting an object through a base pointer results in undefined behavior — most likely, the derived classs destructor wont be called, which leads to resource leaks.
### Example (Wrong: no virtual destructor)
```cpp
#include <iostream>
class Base {
public:
~Base() {
std::cout << "Base destructor\n";
}
};
class Derived : public Base {
public:
~Derived() {
std::cout << "Derived destructor\n";
}
};
int main() {
Base* b = new Derived();
delete b; // ⚠️ Only Base destructor is called!
}
```
- ❌ Derived's destructor is not called — this causes resource leaks if Derived manages any resources.
### ✅ Example (Correct: virtual destructor)
```cpp
#include <iostream>
class Base {
public:
virtual ~Base() {
std::cout << "Base destructor\n";
}
};
class Derived : public Base {
public:
~Derived() override {
std::cout << "Derived destructor\n";
}
};
int main() {
Base* b = new Derived();
delete b; // ✅ Both destructors are called
}
```

View File

@@ -0,0 +1,29 @@
#include <iostream>
class Human {
public:
Human() { std::cout << "Human constructor\n"; }
};
class Student : public Human {
public:
Student() { std::cout << "Student constructor\n"; }
};
class Worker : public Human {
public:
Worker() { std::cout << "Worker constructor\n"; }
};
class CSStudent : public Student, public Worker {
public:
CSStudent() { std::cout << "CSStudent constructor\n"; }
};
// problem with this program: Human constructor runs twice!
int main() {
// CSStudent has two copies of Human, one via Student and one via Worker.
// Ambiguity: If we try to access a Human member from CSStudent, the compiler doesnt know which one we mean.
CSStudent cs;
return 0;
}

View File

@@ -0,0 +1,32 @@
#include <iostream>
class Human {
public:
Human() { std::cout << "Human constructor\n"; }
};
// The virutal keyword tells the compiler:
// "Hey, if this class is used in a diamond-like hierarchy, only one shared Human base should exist, no matter how many paths lead to it."
// The compiler then: Makes sure that only one instance of Human exists inside CSStudent.
class Student : virtual public Human {
public:
Student() { std::cout << "Student constructor\n"; }
};
class Worker : virtual public Human {
public:
Worker() { std::cout << "Worker constructor\n"; }
};
class CSStudent : public Student, public Worker {
public:
CSStudent() { std::cout << "CSStudent constructor\n"; }
};
// problem with this program: Human constructor runs twice!
int main() {
// CSStudent has two copies of Human, one via Student and one via Worker.
// Ambiguity: If we try to access a Human member from CSStudent, the compiler doesnt know which one we mean.
CSStudent cs;
return 0;
}

Some files were not shown because too many files have changed in this diff Show More