Compare commits
40 commits
Author | SHA1 | Date | |
---|---|---|---|
|
daa2dfc077 | ||
|
69af73e315 | ||
|
2769a3a0ad | ||
|
1dae994364 | ||
|
6130bdee66 | ||
|
8abebb692a | ||
|
4bb3629a76 | ||
|
cc34c03648 | ||
|
c5e034c3ac | ||
|
686fd07e08 | ||
|
abec11b35b | ||
|
42f69296f6 | ||
|
d4970c8d76 | ||
|
57d5f34ee5 | ||
|
1c88d49452 | ||
|
7a62bebf76 | ||
|
70170ea995 | ||
|
d10300509e | ||
|
7dd7f5610b | ||
|
94d807fc67 | ||
|
8c8930f5c5 | ||
|
fe00d47e02 | ||
|
c6eb4cb6ae | ||
|
5b669caddb | ||
|
b843fc98e0 | ||
|
fdae90ad9f | ||
|
744d0f7a3a | ||
|
f9f3674fde | ||
|
7d93d5dbfa | ||
|
e4e27d421a | ||
|
3645b14f3c | ||
|
dbd64f4545 | ||
|
53b9f8e4a3 | ||
|
1f44b48fde | ||
|
74ce672841 | ||
|
63265d5cfb | ||
|
bdc3832fd1 | ||
|
b72f78839c | ||
|
43568e947f | ||
|
3f1ca7ecc1 |
40 changed files with 1357 additions and 247 deletions
274
.clang-format
Normal file
274
.clang-format
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignArrayOfStructures: None
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: true
|
||||||
|
AlignConsecutiveBitFields:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveDeclarations:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveMacros:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveShortCaseStatements:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCaseArrows: false
|
||||||
|
AlignCaseColons: false
|
||||||
|
AlignConsecutiveTableGenBreakingDAGArgColons:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveTableGenCondOperatorColons:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveTableGenDefinitionColons:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignFunctionPointers: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments:
|
||||||
|
Kind: Always
|
||||||
|
OverEmptyLines: 0
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowBreakBeforeNoexceptSpecifier: Never
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseExpressionOnASingleLine: true
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortCompoundRequirementOnASingleLine: true
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AttributeMacros:
|
||||||
|
- __capability
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakAdjacentStringLiterals: true
|
||||||
|
BreakAfterAttributes: Leave
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakAfterReturnType: None
|
||||||
|
BreakArrays: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeConceptDeclarations: Always
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInlineASMColon: OnlyMultiline
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakFunctionDefinitionParameters: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakStringLiterals: true
|
||||||
|
BreakTemplateDeclarations: MultiLine
|
||||||
|
ColumnLimit: 80
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
EmptyLineAfterAccessModifier: Never
|
||||||
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IfMacros:
|
||||||
|
- KJ_IF_MAYBE
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentExternBlock: AfterExternBlock
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentRequiresClause: true
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
InsertBraces: false
|
||||||
|
InsertNewlineAtEOF: false
|
||||||
|
InsertTrailingCommas: None
|
||||||
|
IntegerLiteralSeparator:
|
||||||
|
Binary: 0
|
||||||
|
BinaryMinDigits: 0
|
||||||
|
Decimal: 0
|
||||||
|
DecimalMinDigits: 0
|
||||||
|
Hex: 0
|
||||||
|
HexMinDigits: 0
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLines:
|
||||||
|
AtEndOfFile: false
|
||||||
|
AtStartOfBlock: true
|
||||||
|
AtStartOfFile: true
|
||||||
|
LambdaBodyIndentation: Signature
|
||||||
|
LineEnding: DeriveLF
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MainIncludeChar: Quote
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PackConstructorInitializers: BinPack
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakOpenParenthesis: 0
|
||||||
|
PenaltyBreakScopeResolution: 500
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyIndentedWhitespace: 0
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
PPIndentWidth: -1
|
||||||
|
QualifierAlignment: Leave
|
||||||
|
ReferenceAlignment: Pointer
|
||||||
|
ReflowComments: true
|
||||||
|
RemoveBracesLLVM: false
|
||||||
|
RemoveParentheses: Leave
|
||||||
|
RemoveSemicolon: false
|
||||||
|
RequiresClausePosition: OwnLine
|
||||||
|
RequiresExpressionIndentation: OuterScope
|
||||||
|
SeparateDefinitionBlocks: Leave
|
||||||
|
ShortNamespaceLines: 1
|
||||||
|
SkipMacroDefinitionBody: false
|
||||||
|
SortIncludes: CaseSensitive
|
||||||
|
SortJavaStaticImport: Before
|
||||||
|
SortUsingDeclarations: LexicographicNumeric
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceAroundPointerQualifiers: Default
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeJsonColon: false
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeParensOptions:
|
||||||
|
AfterControlStatements: true
|
||||||
|
AfterForeachMacros: true
|
||||||
|
AfterFunctionDefinitionName: false
|
||||||
|
AfterFunctionDeclarationName: false
|
||||||
|
AfterIfMacros: true
|
||||||
|
AfterOverloadedOperator: false
|
||||||
|
AfterPlacementOperator: true
|
||||||
|
AfterRequiresInClause: false
|
||||||
|
AfterRequiresInExpression: false
|
||||||
|
BeforeNonEmptyParentheses: false
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: Never
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 1
|
||||||
|
Maximum: -1
|
||||||
|
SpacesInParens: Never
|
||||||
|
SpacesInParensOptions:
|
||||||
|
ExceptDoubleParentheses: false
|
||||||
|
InCStyleCasts: false
|
||||||
|
InConditionalStatements: false
|
||||||
|
InEmptyParentheses: false
|
||||||
|
Other: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Latest
|
||||||
|
StatementAttributeLikeMacros:
|
||||||
|
- Q_EMIT
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TableGenBreakInsideDAGArg: DontBreak
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Never
|
||||||
|
VerilogBreakBetweenInstancePorts: true
|
||||||
|
WhitespaceSensitiveMacros:
|
||||||
|
- BOOST_PP_STRINGIZE
|
||||||
|
- CF_SWIFT_NAME
|
||||||
|
- NS_SWIFT_NAME
|
||||||
|
- PP_STRINGIZE
|
||||||
|
- STRINGIZE
|
||||||
|
...
|
||||||
|
|
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*a.out*
|
||||||
|
build
|
||||||
|
.cache/
|
||||||
|
words.txt
|
||||||
|
compile_commands.json
|
||||||
|
lab2/edit
|
||||||
|
lab2/spell
|
||||||
|
lab3/tabletest
|
||||||
|
./vscode
|
2
Makefile
Normal file
2
Makefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
format:
|
||||||
|
find . -regex '.*\.\(c\|cpp\|cc\|cxx\|h\|hpp\|hh\|hxx\)' -exec clang-format {} +
|
BIN
cpplabs.pdf
Normal file
BIN
cpplabs.pdf
Normal file
Binary file not shown.
2
lab1/.gitignore
vendored
Normal file
2
lab1/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
hello
|
||||||
|
test_editor
|
51
lab1/Justfile
Normal file
51
lab1/Justfile
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
all: a1 a2 a3 a4 cmake buggy
|
||||||
|
|
||||||
|
# Build the A1 lab exercise
|
||||||
|
a1:
|
||||||
|
g++ -o hello hello.cc
|
||||||
|
./hello
|
||||||
|
|
||||||
|
# Build the A2 lab exercise
|
||||||
|
a2:
|
||||||
|
g++ -std=c++11 -o separate_main separate_main.cc separate_fn.cc
|
||||||
|
|
||||||
|
# Build the A3 lab exercise
|
||||||
|
a3:
|
||||||
|
g++ -std=c++11 -c separate_main.cc
|
||||||
|
g++ -std=c++11 -c separate_fn.cc
|
||||||
|
g++ -std=c++11 -o separate_main separate_main.o separate_fn.o
|
||||||
|
|
||||||
|
# Build the A4 lab exercise
|
||||||
|
a4:
|
||||||
|
@echo "Will fail"
|
||||||
|
-g++ -std=c++11 -o separate_main separate_main.cc
|
||||||
|
|
||||||
|
# Build the A5 lab exercise
|
||||||
|
a5:
|
||||||
|
g++ -c -O2 -Wall -Wextra -pedantic-errors -Wold-style-cast -std=c++11 hello.cc
|
||||||
|
|
||||||
|
# Build the cmake exercise
|
||||||
|
cmake:
|
||||||
|
rm -rf ./cmake-example/build # Start over
|
||||||
|
cd ./cmake-example && mkdir -p build
|
||||||
|
cd ./cmake-example/build && cmake ..
|
||||||
|
cd ./cmake-example/build && make -j$(nproc)
|
||||||
|
./cmake-example/build/SimpleMain
|
||||||
|
|
||||||
|
buggy:
|
||||||
|
cd ./buggy_programs && make -j4
|
||||||
|
cd ./buggy_programs && mkdir -p build
|
||||||
|
cd ./buggy_programs && cmake -B build
|
||||||
|
cd ./buggy_programs && make -C build
|
||||||
|
|
||||||
|
valgrind: buggy
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./dangling
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./bounds
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./bounds-heap
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./leak
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./sum
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./sum_alt
|
||||||
|
cd ./buggy_programs/build/ && valgrind ./ub
|
||||||
|
|
||||||
|
clean:
|
||||||
|
git clean -fdx
|
6
lab1/buggy_programs/.gitignore
vendored
Normal file
6
lab1/buggy_programs/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
bounds
|
||||||
|
bounds-heap
|
||||||
|
dangling
|
||||||
|
leak
|
||||||
|
sum
|
||||||
|
ub
|
1
lab1/cmake-example/.gitignore
vendored
Normal file
1
lab1/cmake-example/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
build
|
|
@ -1,10 +1,9 @@
|
||||||
#include<coding.h>
|
#include <coding.h>
|
||||||
|
|
||||||
unsigned char encode(unsigned char c)
|
unsigned char encode(unsigned char c) {
|
||||||
{
|
return ++c;
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
unsigned char decode(unsigned char c)
|
|
||||||
{
|
unsigned char decode(unsigned char c) {
|
||||||
return c;
|
return --c;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,29 @@ using std::string;
|
||||||
|
|
||||||
using size_type = Editor::size_type;
|
using size_type = Editor::size_type;
|
||||||
|
|
||||||
size_type Editor::get_size() const
|
size_type Editor::get_size() const { return text.size(); }
|
||||||
{
|
|
||||||
return text.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_type Editor::find_left_par(size_type pos) const {
|
Editor::size_type Editor::find_left_par(size_type pos) const {
|
||||||
return string::npos;
|
char right_par = text[pos];
|
||||||
|
char left_par;
|
||||||
|
|
||||||
|
// Determine the matching left parenthesis for the given right parenthesis
|
||||||
|
switch (right_par) {
|
||||||
|
case ')': left_par = '('; break;
|
||||||
|
case ']': left_par = '['; break;
|
||||||
|
case '}': left_par = '{'; break;
|
||||||
|
default: return string::npos; // Not a valid right parenthesis
|
||||||
|
}
|
||||||
|
|
||||||
|
int balance = 1; // Start with the right parenthesis at text[pos]
|
||||||
|
for (size_type i = pos; i-- > 0;) {
|
||||||
|
if (text[i] == left_par) {
|
||||||
|
balance--;
|
||||||
|
if (balance == 0) return i; // Found the matching left parenthesis
|
||||||
|
} else if (text[i] == right_par) {
|
||||||
|
balance++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string::npos; // No matching left parenthesis found
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Editor {
|
class Editor {
|
||||||
public:
|
public:
|
||||||
using size_type = std::string::size_type;
|
using size_type = std::string::size_type;
|
||||||
/* Creates a text editor containing the text t */
|
/* Creates a text editor containing the text t */
|
||||||
Editor(const std::string& t) : text(t) {}
|
Editor(const std::string &t) : text(t) {}
|
||||||
|
|
||||||
/* Get the size of the current contents */
|
/* Get the size of the current contents */
|
||||||
size_type get_size() const;
|
size_type get_size() const;
|
||||||
|
@ -20,7 +20,7 @@ public:
|
||||||
size_type find_left_par(size_type pos) const;
|
size_type find_left_par(size_type pos) const;
|
||||||
|
|
||||||
// ... functions to edit the text (insert and delete characters)
|
// ... functions to edit the text (insert and delete characters)
|
||||||
private:
|
private:
|
||||||
std::string text;
|
std::string text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
5
lab1/hello.cc
Normal file
5
lab1/hello.cc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
std::cout << "Hello" << std::endl;
|
||||||
|
}
|
37
lab2/Makefile
Normal file
37
lab2/Makefile
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -std=c++17
|
||||||
|
#CXXFLAGS += -Werror
|
||||||
|
|
||||||
|
SRC = $(wildcard *.cc)
|
||||||
|
HDR = $(wildcard *.h)
|
||||||
|
OBJ = $(SRC:.cc=.o)
|
||||||
|
|
||||||
|
all: spell edit $(OBJ)
|
||||||
|
|
||||||
|
edit: test_edit_distance.o edit_distance.o
|
||||||
|
@echo "Building & linking $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
spell: spell.o word.o dictionary.o edit_distance.o
|
||||||
|
@echo "Building & linking $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
%.o:%.cc
|
||||||
|
@echo "Building $@"
|
||||||
|
@$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||||
|
|
||||||
|
lint: clang-tidy cppcheck clang-format
|
||||||
|
|
||||||
|
clang-tidy:
|
||||||
|
clang-tidy $(SRC) -- $(CXXFLAGS)
|
||||||
|
|
||||||
|
cppcheck:
|
||||||
|
cppcheck --enable=all --language=c++ --std=c++17 --suppress=missingIncludeSystem -I/usr/include $(SRC) $(HDR)
|
||||||
|
|
||||||
|
clang-format:
|
||||||
|
clang-format -i $(SRC) $(HDR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o spell edit
|
||||||
|
|
||||||
|
.PHONY: clean all lint clang-tidy cppcheck clang-format
|
|
@ -1,22 +1,131 @@
|
||||||
#include <string>
|
#include "dictionary.h"
|
||||||
#include <vector>
|
#include "edit_distance.h"
|
||||||
|
#include "word.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <set>
|
||||||
#include "word.h"
|
#include <string>
|
||||||
#include "dictionary.h"
|
#include <vector>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
Dictionary::Dictionary() {
|
Dictionary::Dictionary() {}
|
||||||
}
|
|
||||||
|
|
||||||
bool Dictionary::contains(const string& word) const {
|
bool Dictionary::contains(const string &word) const {
|
||||||
|
auto l = word.length();
|
||||||
|
Word w = Word(word);
|
||||||
|
if (std::find(this->words[l].begin(), this->words[l].end(), w) !=
|
||||||
|
std::end(this->words[l])) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> Dictionary::get_suggestions(const string& word) const {
|
std::vector<string> Dictionary::get_suggestions(const string &word) const {
|
||||||
vector<string> suggestions;
|
vector<string> suggestions;
|
||||||
|
add_trigram_suggestions(suggestions, word);
|
||||||
|
trim_suggestions(suggestions, word);
|
||||||
|
rank_suggestions(suggestions, word);
|
||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Dictionary::add_trigram_suggestions(std::vector<std::string> &suggestions,
|
||||||
|
const std::string &word) const {
|
||||||
|
// Get trigrams of the input word
|
||||||
|
Word input_word(word);
|
||||||
|
const std::vector<std::string> &input_trigrams = input_word.get_trigrams();
|
||||||
|
|
||||||
|
// Iterate through all words in the dictionary
|
||||||
|
for (int i = 0; i < MAXLEN; ++i) {
|
||||||
|
for (const Word &dict_word : words[i]) {
|
||||||
|
// Get the trigrams of the dictionary word
|
||||||
|
const std::vector<std::string> &dict_word_trigrams =
|
||||||
|
dict_word.get_trigrams();
|
||||||
|
|
||||||
|
// Count how many trigrams match
|
||||||
|
unsigned int match_count = dict_word.get_matches(input_trigrams);
|
||||||
|
|
||||||
|
// If there are any matches, add the word to suggestions
|
||||||
|
if (match_count > 0) {
|
||||||
|
suggestions.push_back(dict_word.get_word());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dictionary::rank_suggestions(std::vector<std::string> &suggestions,
|
||||||
|
const std::string &word) const {
|
||||||
|
// Sort suggestions based on the levenshtein distance
|
||||||
|
std::sort(suggestions.begin(), suggestions.end(),
|
||||||
|
[&](const std::string &a, const std::string &b) {
|
||||||
|
auto dist_a = edit_distance(a, word);
|
||||||
|
auto dist_b = edit_distance(b, word);
|
||||||
|
return dist_a < dist_b;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dictionary::trim_suggestions(std::vector<std::string> &suggestions,
|
||||||
|
const std::string &word) const {
|
||||||
|
// Remove duplicates using a set
|
||||||
|
std::set<std::string> unique_suggestions(suggestions.begin(),
|
||||||
|
suggestions.end());
|
||||||
|
suggestions.assign(unique_suggestions.begin(), unique_suggestions.end());
|
||||||
|
|
||||||
|
// Remove the input word from the suggestions list (if present)
|
||||||
|
suggestions.erase(std::remove(suggestions.begin(), suggestions.end(), word),
|
||||||
|
suggestions.end());
|
||||||
|
|
||||||
|
auto l = word.length();
|
||||||
|
std::cout << "WTF" << l << std::endl;
|
||||||
|
|
||||||
|
// Example: Remove any suggestions that are not within 1 string length
|
||||||
|
suggestions.erase(std::remove_if(suggestions.begin(), suggestions.end(),
|
||||||
|
[l](const std::string &s) {
|
||||||
|
return s.length() > (l + 1) ||
|
||||||
|
s.length() < (l - 1);
|
||||||
|
}),
|
||||||
|
suggestions.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Dictionary::spit(path p) {
|
||||||
|
std::ofstream file(p);
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "Error opening file! " << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int a = 0; a < MAXLEN; a++) {
|
||||||
|
for (auto &word : words[a]) {
|
||||||
|
file << word;
|
||||||
|
file << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.flush();
|
||||||
|
file.close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Dictionary::slurp(path p) {
|
||||||
|
std::ifstream file(p.string());
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "Error opening file! " << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
if (line.empty())
|
||||||
|
continue;
|
||||||
|
// Words larger than max gets placed in the topmost bucket
|
||||||
|
words[std::min(line.size(), static_cast<size_t>(MAXLEN) - 1)].push_back(
|
||||||
|
Word(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,29 @@
|
||||||
#ifndef DICTIONARY_H
|
#pragma once
|
||||||
#define DICTIONARY_H
|
|
||||||
|
|
||||||
|
#include "word.h"
|
||||||
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Dictionary {
|
#define MAXLEN 30
|
||||||
public:
|
|
||||||
Dictionary();
|
|
||||||
bool contains(const std::string& word) const;
|
|
||||||
std::vector<std::string> get_suggestions(const std::string& word) const;
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
// using std::vector;
|
||||||
|
using std::filesystem::path;
|
||||||
|
|
||||||
|
class Dictionary {
|
||||||
|
public:
|
||||||
|
Dictionary();
|
||||||
|
void add_trigram_suggestions(std::vector<std::string> &suggestions,
|
||||||
|
const std::string &word) const;
|
||||||
|
void rank_suggestions(std::vector<std::string> &suggestions,
|
||||||
|
const std::string &word) const;
|
||||||
|
void trim_suggestions(std::vector<std::string> &suggestions,
|
||||||
|
const std::string &word) const;
|
||||||
|
bool contains(const std::string &word) const;
|
||||||
|
std::vector<std::string> get_suggestions(const std::string &word) const;
|
||||||
|
int slurp(path p);
|
||||||
|
int spit(path p);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Word> words[MAXLEN];
|
||||||
|
};
|
||||||
|
|
35
lab2/edit_distance.cc
Normal file
35
lab2/edit_distance.cc
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int edit_distance(const std::string &s1, const std::string &s2) {
|
||||||
|
size_t m = s1.size();
|
||||||
|
size_t n = s2.size();
|
||||||
|
|
||||||
|
// Create a 2D DP table
|
||||||
|
std::vector<std::vector<size_t>> dp(m + 1, std::vector<size_t>(n + 1));
|
||||||
|
|
||||||
|
// Fill the base cases
|
||||||
|
for (size_t i = 0; i <= m; ++i)
|
||||||
|
dp[i][0] = i; // Deletion cost
|
||||||
|
|
||||||
|
for (size_t j = 0; j <= n; ++j)
|
||||||
|
dp[0][j] = j; // Insertion cost
|
||||||
|
|
||||||
|
// Fill the DP table
|
||||||
|
for (size_t i = 1; i <= m; ++i) {
|
||||||
|
for (size_t j = 1; j <= n; ++j) {
|
||||||
|
if (s1[i - 1] == s2[j - 1]) {
|
||||||
|
dp[i][j] = dp[i - 1][j - 1]; // No operation needed
|
||||||
|
} else {
|
||||||
|
dp[i][j] = 1 + std::min({
|
||||||
|
dp[i - 1][j], // Deletion
|
||||||
|
dp[i][j - 1], // Insertion
|
||||||
|
dp[i - 1][j - 1] // Substitution
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<int>(dp[m][n]);
|
||||||
|
}
|
17
lab2/edit_distance.h
Normal file
17
lab2/edit_distance.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes the edit distance (Levenshtein distance) between two strings.
|
||||||
|
*
|
||||||
|
* The edit distance is defined as the minimum number of single-character edits
|
||||||
|
* (insertions, deletions, or substitutions) required to transform one string
|
||||||
|
* into the other.
|
||||||
|
*
|
||||||
|
* This implementation uses dynamic programming to compute the distance
|
||||||
|
* efficiently.
|
||||||
|
*
|
||||||
|
* @param s1 The first string.
|
||||||
|
* @param s2 The second string.
|
||||||
|
* @return The edit distance between the two strings.
|
||||||
|
*/
|
||||||
|
int edit_distance(const std::string &s1, const std::string &s2);
|
|
@ -1,18 +1,18 @@
|
||||||
|
#include "dictionary.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cctype>
|
|
||||||
#include "dictionary.h"
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
|
||||||
using std::cin;
|
using std::cin;
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
void check_word(const string& word, const Dictionary& dict)
|
void check_word(const string &word, const Dictionary &dict) {
|
||||||
{
|
|
||||||
if (dict.contains(word)) {
|
if (dict.contains(word)) {
|
||||||
cout << "Correct." << endl;
|
cout << "Correct." << endl;
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,17 +21,21 @@ void check_word(const string& word, const Dictionary& dict)
|
||||||
cout << "Wrong, no suggestions." << endl;
|
cout << "Wrong, no suggestions." << endl;
|
||||||
} else {
|
} else {
|
||||||
cout << "Wrong. Suggestions:" << endl;
|
cout << "Wrong. Suggestions:" << endl;
|
||||||
for (const auto& w : suggestions) {
|
for (const auto &w : suggestions) {
|
||||||
cout << " " << w << endl;
|
cout << " " << w << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
string word;
|
string word;
|
||||||
|
dict.slurp(std::filesystem::path("/usr/share/dict/words"));
|
||||||
|
// dict.spit(std::filesystem::path("words.txt"));
|
||||||
|
|
||||||
while (cin >> word) {
|
while (cin >> word) {
|
||||||
transform(word.begin(), word.end(), word.begin(), ::tolower);
|
std::transform(word.begin(), word.end(), word.begin(), ::tolower);
|
||||||
check_word(word, dict);
|
check_word(word, dict);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
bool do_test(const std::string& x, const std::string& y, int expected)
|
bool do_test(const std::string &x, const std::string &y, int expected) {
|
||||||
{
|
|
||||||
auto actual = edit_distance(x, y);
|
auto actual = edit_distance(x, y);
|
||||||
if (actual != expected) {
|
if (actual != expected) {
|
||||||
std::cout << "*** WRONG: distance(" << x << ", " << y << ") was "
|
std::cout << "*** WRONG: distance(" << x << ", " << y << ") was "
|
||||||
|
@ -19,8 +18,7 @@ bool do_test(const std::string& x, const std::string& y, int expected)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main() {
|
||||||
{
|
|
||||||
int res = do_test("foobar", "foobar", 0);
|
int res = do_test("foobar", "foobar", 0);
|
||||||
res += do_test("x", "x", 0);
|
res += do_test("x", "x", 0);
|
||||||
res += do_test("baz", "bar", 1);
|
res += do_test("baz", "bar", 1);
|
||||||
|
|
63
lab2/word.cc
63
lab2/word.cc
|
@ -1,16 +1,63 @@
|
||||||
|
#include "word.h"
|
||||||
|
#include "dictionary.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "word.h"
|
|
||||||
|
|
||||||
using std::vector;
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
Word::Word(const string& w, const vector<string>& t) {}
|
Word::Word(const string &w, const vector<string> &t) : word(w), trigrams(t) {
|
||||||
|
std::sort(trigrams.begin(), trigrams.end());
|
||||||
string Word::get_word() const {
|
|
||||||
return string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Word::get_matches(const vector<string>& t) const {
|
std::vector<std::string> get_trigrams(const std::string &text) {
|
||||||
return 0;
|
std::vector<std::string> trigrams;
|
||||||
|
if (text.size() < 3) {
|
||||||
|
return trigrams; // Return an empty vector if the input is too short
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i <= text.size() - 3; ++i) {
|
||||||
|
trigrams.push_back(
|
||||||
|
text.substr(i, 3)); // Extract a substring of length 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return trigrams;
|
||||||
|
}
|
||||||
|
|
||||||
|
Word::Word(const std::string &w) : word(w) {
|
||||||
|
this->trigrams = ::get_trigrams(w);
|
||||||
|
std::sort(trigrams.begin(), trigrams.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
string Word::get_word() const { return word; }
|
||||||
|
|
||||||
|
vector<std::string> Word::get_trigrams() const { return trigrams; }
|
||||||
|
|
||||||
|
unsigned int Word::get_matches(const vector<string> &t) const {
|
||||||
|
unsigned int matches = 0;
|
||||||
|
|
||||||
|
for (const auto &trigram : t) {
|
||||||
|
if (std::binary_search(trigrams.begin(), trigrams.end(), trigram)) {
|
||||||
|
++matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, const Word &w) {
|
||||||
|
auto space = string(" ");
|
||||||
|
out << w.word;
|
||||||
|
out << space;
|
||||||
|
out << w.trigrams.size();
|
||||||
|
for (const auto &tri : w.trigrams) {
|
||||||
|
out << space << tri;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Word &lhs, const Word &rhs) {
|
||||||
|
return lhs.word == rhs.word &&
|
||||||
|
std::equal(lhs.trigrams.begin(), lhs.trigrams.end(),
|
||||||
|
rhs.trigrams.begin());
|
||||||
}
|
}
|
||||||
|
|
33
lab2/word.h
33
lab2/word.h
|
@ -1,21 +1,34 @@
|
||||||
#ifndef WORD_H
|
#pragma once
|
||||||
#define WORD_H
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Contains a word and its trigrams
|
||||||
|
*/
|
||||||
class Word {
|
class Word {
|
||||||
public:
|
public:
|
||||||
/* Creates a word w with the sorted trigrams t */
|
/** Creates a word w with the sorted trigrams t */
|
||||||
Word(const std::string& w, const std::vector<std::string>& t);
|
Word(const std::string &w, const std::vector<std::string> &t);
|
||||||
|
|
||||||
/* Returns the word */
|
/** Creates a word w and derives the trigrams internally */
|
||||||
|
Word(const std::string &w);
|
||||||
|
|
||||||
|
/** Returns the word */
|
||||||
std::string get_word() const;
|
std::string get_word() const;
|
||||||
|
|
||||||
/* Returns how many of the trigrams in t that are present
|
/** Returns trigrams */
|
||||||
|
std::vector<std::string> get_trigrams() const;
|
||||||
|
|
||||||
|
/** Returns how many of the trigrams in t that are present
|
||||||
in this word's trigram vector */
|
in this word's trigram vector */
|
||||||
unsigned int get_matches(const std::vector<std::string>& t) const;
|
unsigned int get_matches(const std::vector<std::string> &t) const;
|
||||||
private:
|
|
||||||
|
private:
|
||||||
|
const std::string word;
|
||||||
|
std::vector<std::string> trigrams;
|
||||||
|
friend std::ostream &operator<<(std::ostream &out, const Word &o);
|
||||||
|
friend bool operator==(const Word &lhs, const Word &rhs);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
bool operator==(const Word &lhs, const Word &rhs);
|
||||||
|
|
33
lab3/Makefile
Normal file
33
lab3/Makefile
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -std=c++17
|
||||||
|
#CXXFLAGS += -Werror
|
||||||
|
|
||||||
|
SRC = $(wildcard *.cc)
|
||||||
|
HDR = $(wildcard *.h)
|
||||||
|
OBJ = $(SRC:.cc=.o)
|
||||||
|
|
||||||
|
all: tabletest $(OBJ)
|
||||||
|
|
||||||
|
tabletest: $(OBJ)
|
||||||
|
@echo "Building & linking $@"
|
||||||
|
@$(CXX) $(CXXFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
%.o:%.cc
|
||||||
|
@echo "Building $@"
|
||||||
|
@$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||||
|
|
||||||
|
lint: clang-tidy cppcheck clang-format
|
||||||
|
|
||||||
|
clang-tidy:
|
||||||
|
clang-tidy $(SRC) -- $(CXXFLAGS)
|
||||||
|
|
||||||
|
cppcheck:
|
||||||
|
cppcheck --enable=all --language=c++ --std=c++17 --suppress=missingIncludeSystem -I/usr/include $(SRC) $(HDR)
|
||||||
|
|
||||||
|
clang-format:
|
||||||
|
clang-format -i $(SRC) $(HDR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o spell edit
|
||||||
|
|
||||||
|
.PHONY: clean all lint clang-tidy cppcheck clang-format
|
|
@ -1,6 +1,5 @@
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const User& u)
|
std::ostream &operator<<(std::ostream &os, const User &u) {
|
||||||
{
|
return os << "(" << u.getCardNbr() << ") " << u.getName();
|
||||||
return os << "(" << u.getCardNbr() << ") "<< u.getName();
|
|
||||||
}
|
}
|
||||||
|
|
30
lab3/User.h
30
lab3/User.h
|
@ -8,20 +8,26 @@ using std::cout;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
public:
|
public:
|
||||||
User() :cardNbr{0},name{"default"}{}
|
User() : cardNbr{0}, name{"default"} {}
|
||||||
User(int c, std::string n) :cardNbr{c},name{n} {}
|
User(int c, std::string n) : cardNbr{c}, name{n} {}
|
||||||
~User() {cardNbr=-2; name="--------------------";} // overwrite values for security reasons
|
~User() {
|
||||||
User(const User& u) =default;
|
cardNbr = -2;
|
||||||
User& operator=(const User&) =default;
|
name = "--------------------";
|
||||||
int getCardNbr() const {return cardNbr;}
|
} // overwrite values for security reasons
|
||||||
std::string getName() const {return name;}
|
User(const User &u) = default;
|
||||||
bool operator==(const User& u) const {return cardNbr == u.cardNbr && name == u.name;}
|
User &operator=(const User &) = default;
|
||||||
bool operator!=(const User& u) const {return ! (u == *this);}
|
int getCardNbr() const { return cardNbr; }
|
||||||
private:
|
std::string getName() const { return name; }
|
||||||
|
bool operator==(const User &u) const {
|
||||||
|
return cardNbr == u.cardNbr && name == u.name;
|
||||||
|
}
|
||||||
|
bool operator!=(const User &u) const { return !(u == *this); }
|
||||||
|
|
||||||
|
private:
|
||||||
int cardNbr;
|
int cardNbr;
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const User& u);
|
std::ostream &operator<<(std::ostream &os, const User &u);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
#include "UserTable.h"
|
#include "UserTable.h"
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
const User UserTable::user_not_found = User{-1,"Not Found"};
|
const User UserTable::user_not_found = User{-1, "Not Found"};
|
||||||
|
|
||||||
UserTable::UserTable() :users{new User[capacity]} { }
|
UserTable::UserTable() : users{new User[capacity]} {}
|
||||||
|
|
||||||
UserTable::UserTable(const std::string& fname) :UserTable{}
|
UserTable::UserTable(const std::string &fname) : UserTable{} {
|
||||||
{
|
|
||||||
std::ifstream ufile(fname);
|
std::ifstream ufile(fname);
|
||||||
|
|
||||||
if(ufile.is_open()) {
|
if (ufile.is_open()) {
|
||||||
while(ufile) {
|
while (ufile) {
|
||||||
int cn;
|
int cn;
|
||||||
if(ufile >> cn ) {
|
if (ufile >> cn) {
|
||||||
ufile.ignore(); // skip space
|
ufile.ignore(); // skip space
|
||||||
char n[80];
|
char n[80];
|
||||||
ufile.getline(n,80);
|
ufile.getline(n, 80);
|
||||||
addUser(User(cn,n));
|
addUser(User(cn, n));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -26,38 +24,39 @@ UserTable::UserTable(const std::string& fname) :UserTable{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserTable::addUser(const User& u)
|
void UserTable::addUser(const User &u) {
|
||||||
{
|
|
||||||
// gör tabellen större vid behov
|
// gör tabellen större vid behov
|
||||||
ensureCapacity(n+1);
|
ensureCapacity(n + 1);
|
||||||
|
|
||||||
// 1. Hitta rätt plats
|
// 1. Hitta rätt plats
|
||||||
int pos{0};
|
int pos{0};
|
||||||
while ( (pos < n) && (users[pos].getCardNbr() < u.getCardNbr())){
|
while ((pos < n) && (users[pos].getCardNbr() < u.getCardNbr())) {
|
||||||
++pos;
|
++pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
//2. skapa lucka i vektorn
|
// 2. skapa lucka i vektorn
|
||||||
for(int i=n; i > pos; --i){
|
for (int i = n; i > pos; --i) {
|
||||||
users[i] = users[i-1];
|
users[i] = users[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
//3. stoppa in den nya användaren i luckan
|
// 3. stoppa in den nya användaren i luckan
|
||||||
users[pos] = u;
|
users[pos] = u;
|
||||||
|
|
||||||
|
++n;
|
||||||
}
|
}
|
||||||
|
|
||||||
User UserTable::find(int c) const
|
User UserTable::find(int c) const {
|
||||||
{
|
|
||||||
// binärsökning (baserad på Holm, 2007)
|
// binärsökning (baserad på Holm, 2007)
|
||||||
|
|
||||||
int low = 0;
|
int low = 0;
|
||||||
int high = n - 1;
|
int high = n - 1;
|
||||||
int mid = -1;
|
int mid = -1;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
while (low < high && ! found) {
|
while (low < high && !found) {
|
||||||
mid = (low + high) / 2;
|
mid = (low + high) / 2;
|
||||||
//
|
//
|
||||||
int midnbr = users[mid].getCardNbr();
|
const int midnbr = users[mid].getCardNbr();
|
||||||
if (midnbr = c) {
|
if (c == midnbr) {
|
||||||
found = true;
|
found = true;
|
||||||
} else if (users[mid].getCardNbr() < c) {
|
} else if (users[mid].getCardNbr() < c) {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
|
@ -68,37 +67,31 @@ User UserTable::find(int c) const
|
||||||
|
|
||||||
return found ? users[mid] : user_not_found;
|
return found ? users[mid] : user_not_found;
|
||||||
}
|
}
|
||||||
User UserTable::find(std::string name) const
|
User UserTable::find(std::string name) const {
|
||||||
{
|
auto it = std::find_if(users, users + n, [&name](const User &u) {
|
||||||
for (int i = 0; i != n; ++i) {
|
return u.getName() == name;
|
||||||
if (users[i].getName() == name) {
|
});
|
||||||
return users[i];
|
|
||||||
} else {
|
// If it is at the 'end' of users, the result is not found
|
||||||
return user_not_found;
|
return (it != users+n) ? *it : user_not_found;
|
||||||
}
|
|
||||||
}
|
|
||||||
return user_not_found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserTable::ensureCapacity(int s)
|
void UserTable::ensureCapacity(int s) {
|
||||||
{
|
if (s > capacity) {
|
||||||
if(s>capacity) {
|
while (s > capacity) {
|
||||||
while(s > capacity) {
|
capacity *= 4;
|
||||||
capacity*=4;
|
|
||||||
}
|
}
|
||||||
auto tmp = new User[capacity];
|
auto tmp = new User[capacity];
|
||||||
std::copy(users, users+n, tmp);
|
std::copy(users, users + n, tmp);
|
||||||
delete[] users;
|
delete[] users;
|
||||||
users=tmp;
|
users = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
void UserTable::print(std::ostream& os) const
|
void UserTable::print(std::ostream &os) const {
|
||||||
{
|
|
||||||
os << "-------------" << std::endl;
|
os << "-------------" << std::endl;
|
||||||
for(int i = 0; i != getNbrUsers(); ++i) {
|
for (int i = 0; i != getNbrUsers(); ++i) {
|
||||||
const auto& u = users[i];
|
const auto &u = users[i];
|
||||||
os << "(" <<u.getCardNbr() << ") " << u.getName() << std::endl;
|
os << "(" << u.getCardNbr() << ") " << u.getName() << std::endl;
|
||||||
}
|
}
|
||||||
os << "=============" << std::endl;
|
os << "=============" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -108,8 +101,7 @@ void UserTable::print(std::ostream& os) const
|
||||||
* Om något kortnummer inte kunde sökas upp returneras detta. Annars, om
|
* Om något kortnummer inte kunde sökas upp returneras detta. Annars, om
|
||||||
* alla sökningar lyckades, returneras 0.
|
* alla sökningar lyckades, returneras 0.
|
||||||
*/
|
*/
|
||||||
int testFindNbr(const UserTable ut)
|
int testFindNbr(const UserTable ut) {
|
||||||
{
|
|
||||||
for (int i = 0; i < ut.n; i++) {
|
for (int i = 0; i < ut.n; i++) {
|
||||||
int nbr = ut.users[i].getCardNbr();
|
int nbr = ut.users[i].getCardNbr();
|
||||||
User found = ut.find(nbr);
|
User found = ut.find(nbr);
|
||||||
|
@ -119,4 +111,3 @@ int testFindNbr(const UserTable ut)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,28 +5,27 @@
|
||||||
|
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
|
|
||||||
class UserTable{
|
class UserTable {
|
||||||
public:
|
public:
|
||||||
UserTable();
|
UserTable();
|
||||||
UserTable(const std::string&);
|
UserTable(const std::string &);
|
||||||
~UserTable() {delete[] users;}
|
~UserTable() { delete[] users; }
|
||||||
|
|
||||||
void addUser(const User&);
|
void addUser(const User &);
|
||||||
User find(int) const;
|
User find(int) const;
|
||||||
User find(std::string) const;
|
User find(std::string) const;
|
||||||
|
|
||||||
int getNbrUsers() const {
|
int getNbrUsers() const { return n; }
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(std::ostream&) const;
|
void print(std::ostream &) const;
|
||||||
|
|
||||||
static const User user_not_found;
|
static const User user_not_found;
|
||||||
private:
|
|
||||||
|
private:
|
||||||
int capacity{1000};
|
int capacity{1000};
|
||||||
void ensureCapacity(int);
|
void ensureCapacity(int);
|
||||||
int n{0};
|
int n{0};
|
||||||
User* users;
|
User *users;
|
||||||
|
|
||||||
friend int testFindNbr(const UserTable ut);
|
friend int testFindNbr(const UserTable ut);
|
||||||
};
|
};
|
||||||
|
|
42
lab3/UserTableTest.cc
Normal file
42
lab3/UserTableTest.cc
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include "UserTable.h"
|
||||||
|
#include "User.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
{ /* User Related Tests */
|
||||||
|
// Two identical users
|
||||||
|
User u1 = User(1234, "Name Nameson");
|
||||||
|
User u2 = User(1234, "Name Nameson");
|
||||||
|
|
||||||
|
assert(u1.getName() == u2.getName());
|
||||||
|
assert(u1.getCardNbr() == u2.getCardNbr());
|
||||||
|
|
||||||
|
// Two different users
|
||||||
|
User u3 = User(1200, "Name Surname");
|
||||||
|
User u4 = User(1201, "Name Nameson");
|
||||||
|
|
||||||
|
assert(u3.getName() != u4.getName());
|
||||||
|
assert(u3.getCardNbr() != u4.getCardNbr());
|
||||||
|
}
|
||||||
|
{ /* Table Related Tests */
|
||||||
|
UserTable tbl1 = UserTable();
|
||||||
|
assert(tbl1.find("Jens Holmgren") == UserTable::user_not_found);
|
||||||
|
tbl1.addUser(User(1200, "Table Yes"));
|
||||||
|
assert(tbl1.getNbrUsers() == 1);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Assumes 'users.txt' is present, containing
|
||||||
|
// line separated records formatted as:
|
||||||
|
// "89524 Adam Abrahamsson"
|
||||||
|
UserTable tbl = UserTable("users.txt");
|
||||||
|
assert(tbl.getNbrUsers() > 0);
|
||||||
|
assert(tbl.find("Jens Holmgren") != UserTable::user_not_found);
|
||||||
|
|
||||||
|
tbl.addUser(User(1200, "Name Surname"));
|
||||||
|
User ufind1 = tbl.find(1200);
|
||||||
|
assert(ufind1 != UserTable::user_not_found);
|
||||||
|
|
||||||
|
User ufind2 = tbl.find("Name Surname");
|
||||||
|
assert(ufind2 != UserTable::user_not_found);
|
||||||
|
}
|
||||||
|
}
|
59
lab4/.vscode/settings.json
vendored
Normal file
59
lab4/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"charconv": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"regex": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"format": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"numbers": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"text_encoding": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"variant": "cpp"
|
||||||
|
}
|
||||||
|
}
|
74
lab4/Sieve.cc
Normal file
74
lab4/Sieve.cc
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Sieve {
|
||||||
|
private:
|
||||||
|
std::string sieve;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructor to initialize sieve with 'P' (prime assumption)
|
||||||
|
Sieve(size_t limit) : sieve(limit + 1, 'P') {
|
||||||
|
// 0 and 1 are not primes
|
||||||
|
sieve[0] = 'C';
|
||||||
|
if (limit > 0) sieve[1] = 'C';
|
||||||
|
|
||||||
|
// Sieve of Eratosthenes
|
||||||
|
for (size_t i = 2; i * i <= limit; ++i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
for (size_t j = i * i; j <= limit; j += i) {
|
||||||
|
sieve[j] = 'C';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get primes as a vector of integers
|
||||||
|
std::vector<int> getPrimes() const {
|
||||||
|
std::vector<int> primes;
|
||||||
|
for (size_t i = 2; i < sieve.size(); ++i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
primes.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return primes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print all primes in the range
|
||||||
|
void printPrimesInRange(int start, int end) const {
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
std::cout << i << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the largest prime below a limit
|
||||||
|
int largestPrimeBelow(int limit) const {
|
||||||
|
for (int i = limit; i >= 2; --i) {
|
||||||
|
if (sieve[i] == 'P') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1; // If no prime is found
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main function for testing
|
||||||
|
int main() {
|
||||||
|
const int limit = 100000;
|
||||||
|
|
||||||
|
// Create a Sieve instance for the range 0 to limit
|
||||||
|
Sieve sieve(limit);
|
||||||
|
|
||||||
|
// Print primes between 1 and 200
|
||||||
|
std::cout << "Primes between 1 and 200:" << std::endl;
|
||||||
|
sieve.printPrimesInRange(1, 200);
|
||||||
|
|
||||||
|
// Find and print the largest prime below 100,000
|
||||||
|
int largestPrime = sieve.largestPrimeBelow(limit);
|
||||||
|
std::cout << "Largest prime below " << limit << ": " << largestPrime << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
32
lab4/TagRemover.cc
Normal file
32
lab4/TagRemover.cc
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class TagRemover {
|
||||||
|
private:
|
||||||
|
std::string content;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TagRemover(std::istream& input) {
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(input, line)) {
|
||||||
|
content += line + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(std::ostream& output) const {
|
||||||
|
std::string result = std::regex_replace(content, std::regex("<[^>]*>"), "");
|
||||||
|
result = std::regex_replace(result, std::regex("<"), "<");
|
||||||
|
result = std::regex_replace(result, std::regex(">"), ">");
|
||||||
|
result = std::regex_replace(result, std::regex(" "), " ");
|
||||||
|
result = std::regex_replace(result, std::regex("&"), "&");
|
||||||
|
|
||||||
|
output << result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
TagRemover tr(std::cin);
|
||||||
|
tr.print(std::cout);
|
||||||
|
return 0;
|
||||||
|
}
|
70
lab4/date.cc
70
lab4/date.cc
|
@ -1,30 +1,86 @@
|
||||||
#include <ctime> // time and localtime
|
#include <ctime> // för tid och localtime
|
||||||
|
#include <iomanip> // för setw och setfill
|
||||||
|
#include <sstream> // för inputhantering
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
|
||||||
|
// Konstruktor: dagens datum
|
||||||
Date::Date() {
|
Date::Date() {
|
||||||
time_t timer = time(0); // time in seconds since 1970-01-01
|
time_t timer = time(0); // tid i sekunder sedan 1970-01-01
|
||||||
tm* locTime = localtime(&timer); // broken-down time
|
tm* locTime = localtime(&timer); // lokal tid
|
||||||
year = 1900 + locTime->tm_year;
|
year = 1900 + locTime->tm_year;
|
||||||
month = 1 + locTime->tm_mon;
|
month = 1 + locTime->tm_mon;
|
||||||
day = locTime->tm_mday;
|
day = locTime->tm_mday;
|
||||||
}
|
}
|
||||||
|
|
||||||
Date::Date(int y, int m, int d) {}
|
// Konstruktor: specifikt datum
|
||||||
|
Date::Date(int y, int m, int d) : year(y), month(m), day(d) {}
|
||||||
|
|
||||||
|
// Get-funktioner
|
||||||
int Date::getYear() const {
|
int Date::getYear() const {
|
||||||
return 0;
|
return year;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Date::getMonth() const {
|
int Date::getMonth() const {
|
||||||
return 0;
|
return month;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Date::getDay() const {
|
int Date::getDay() const {
|
||||||
return 0;
|
return day;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gå till nästa dag
|
||||||
void Date::next() {
|
void Date::next() {
|
||||||
|
day++;
|
||||||
|
if (day > daysPerMonth[month - 1] + (month == 2 && isLeapYear(year))) {
|
||||||
|
day = 1;
|
||||||
|
month++;
|
||||||
|
if (month > 12) {
|
||||||
|
month = 1;
|
||||||
|
year++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kontrollera om ett år är ett skottår
|
||||||
|
bool Date::isLeapYear(int year) {
|
||||||
|
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded operator<< (output)
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Date& date) {
|
||||||
|
os << std::setw(4) << std::setfill('0') << date.getYear() << '-'
|
||||||
|
<< std::setw(2) << std::setfill('0') << date.getMonth() << '-'
|
||||||
|
<< std::setw(2) << std::setfill('0') << date.getDay();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded operator>> (input)
|
||||||
|
std::istream& operator>>(std::istream& is, Date& date) {
|
||||||
|
std::string input;
|
||||||
|
is >> input;
|
||||||
|
|
||||||
|
std::istringstream iss(input);
|
||||||
|
char dash1, dash2;
|
||||||
|
int y, m, d;
|
||||||
|
|
||||||
|
if (iss >> y >> dash1 >> m >> dash2 >> d && dash1 == '-' && dash2 == '-') {
|
||||||
|
// Validera månad och dag
|
||||||
|
if (m >= 1 && m <= 12) {
|
||||||
|
int maxDay = Date::daysPerMonth[m - 1];
|
||||||
|
if (m == 2 && Date::isLeapYear(y)) {
|
||||||
|
maxDay = 29; // Februari har 29 dagar under skottår
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d >= 1 && d <= maxDay) {
|
||||||
|
date = Date(y, m, d); // Sätt datumet om det är giltigt
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ogiltig inmatning
|
||||||
|
is.setstate(std::ios_base::failbit);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
26
lab4/date.h
26
lab4/date.h
|
@ -1,19 +1,27 @@
|
||||||
#ifndef DATE_H
|
#ifndef DATE_H
|
||||||
#define DATE_H
|
#define DATE_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
class Date {
|
class Date {
|
||||||
public:
|
public:
|
||||||
Date(); // today's date
|
Date(); // dagens datum
|
||||||
Date(int y, int m, int d); // yyyy-mm-dd
|
Date(int y, int m, int d); // yyyy-mm-dd
|
||||||
int getYear() const; // get the year
|
int getYear() const; // returnerar året
|
||||||
int getMonth() const; // get the month
|
int getMonth() const; // returnerar månaden
|
||||||
int getDay() const; // get the day
|
int getDay() const; // returnerar dagen
|
||||||
void next(); // advance to next day
|
void next(); // går till nästa dag
|
||||||
|
|
||||||
|
// Overloaded operators
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const Date& date);
|
||||||
|
friend std::istream& operator>>(std::istream& is, Date& date);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int year; // the year (four digits)
|
int year; // året (fyra siffror)
|
||||||
int month; // the month (1-12)
|
int month; // månaden (1-12)
|
||||||
int day; // the day (1-..)
|
int day; // dagen (1-...)
|
||||||
static int daysPerMonth[12]; // number of days in each month
|
static int daysPerMonth[12]; // antal dagar i varje månad
|
||||||
|
static bool isLeapYear(int year); // kontrollera skottår
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,64 +1,40 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for setw and setfill
|
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
using std::cout;
|
|
||||||
using std::endl;
|
|
||||||
using std::setw;
|
|
||||||
using std::setfill;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prints the date d in the format yyyy-mm-dd. You shall replace this
|
|
||||||
* function with an overloaded operator<<, and add an overloaded operator>>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void print(const Date& d) {
|
|
||||||
cout << setw(4) << setfill('0') << d.getYear() << '-';
|
|
||||||
cout << setw(2) << setfill('0') << d.getMonth() << '-';
|
|
||||||
cout << setw(2) << setfill('0') << d.getDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Check input and output of dates. Uncomment the following when you
|
// Test input och output
|
||||||
// have added operator>> and operator<<.
|
|
||||||
/*
|
|
||||||
bool cont = true;
|
bool cont = true;
|
||||||
while (cont) {
|
while (cont) {
|
||||||
cout << "Type a date: ";
|
std::cout << "Type a date: ";
|
||||||
Date aDate;
|
Date aDate;
|
||||||
cin >> aDate;
|
std::cin >> aDate;
|
||||||
if (cin.eof()) {
|
|
||||||
|
if (std::cin.eof()) {
|
||||||
cont = false;
|
cont = false;
|
||||||
} else if (!cin.good()) {
|
} else if (!std::cin.good()) {
|
||||||
cout << "Wrong input format" << endl;
|
std::cout << "Wrong input format" << std::endl;
|
||||||
// restore stream state and ignore the rest of the line
|
std::cin.clear();
|
||||||
cin.clear();
|
std::cin.ignore(10000, '\n');
|
||||||
cin.ignore(10000, '\n');
|
} else {
|
||||||
}
|
std::cout << "Output: " << aDate << std::endl;
|
||||||
else {
|
|
||||||
cout << "Output: " << aDate << endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Check 'next' by creating an object describing today's date, then
|
// Testa 'next' med dagens datum
|
||||||
// printing dates more than a month ahead
|
std::cout << "--- Today and more than a month ahead:" << std::endl;
|
||||||
cout << "--- Today and more than a month ahead:" << endl;
|
|
||||||
Date d1;
|
Date d1;
|
||||||
print(d1);
|
std::cout << d1 << std::endl;
|
||||||
cout << endl;
|
for (int i = 1; i <= 35; ++i) {
|
||||||
for (int i = 1; i <= 35 ; ++i) {
|
|
||||||
d1.next();
|
d1.next();
|
||||||
print(d1);
|
std::cout << d1 << std::endl;
|
||||||
cout << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check so 'next' functions correctly from one year to the next
|
// Testa 'next' från nyårsafton
|
||||||
cout << "--- New Year's Eve and the next day:" << endl;
|
std::cout << "--- New Year's Eve and the next day:" << std::endl;
|
||||||
Date d2(2013, 12, 31);
|
Date d2(2013, 12, 31);
|
||||||
print(d2);
|
std::cout << d2 << std::endl;
|
||||||
cout << endl;
|
|
||||||
d2.next();
|
d2.next();
|
||||||
print(d2);
|
std::cout << d2 << std::endl;
|
||||||
cout << endl;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
56
lab4/makefile
Normal file
56
lab4/makefile
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Compiler
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
TARGETS = TagRemover Sieve date_test toString_test string_cast_test
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
SRCS_TAGREMOVER = TagRemover.cc
|
||||||
|
SRCS_SIEVE = Sieve.cc
|
||||||
|
SRCS_DATE = date.cc
|
||||||
|
SRCS_DATE_TEST = date_test.cc
|
||||||
|
SRCS_TOSTRING_TEST = toString_test.cc date.cc
|
||||||
|
SRCS_STRING_CAST_TEST = string_cast_test.cc date.cc
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
OBJS_TAGREMOVER = $(SRCS_TAGREMOVER:.cc=.o)
|
||||||
|
OBJS_SIEVE = $(SRCS_SIEVE:.cc=.o)
|
||||||
|
OBJS_DATE = $(SRCS_DATE:.cc=.o)
|
||||||
|
OBJS_DATE_TEST = $(SRCS_DATE_TEST:.cc=.o)
|
||||||
|
OBJS_TOSTRING_TEST = $(SRCS_TOSTRING_TEST:.cc=.o)
|
||||||
|
OBJS_STRING_CAST_TEST = $(SRCS_STRING_CAST_TEST:.cc=.o)
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
# Rule to build TagRemover
|
||||||
|
TagRemover: $(OBJS_TAGREMOVER)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build Sieve
|
||||||
|
Sieve: $(OBJS_SIEVE)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build date_test
|
||||||
|
date_test: $(OBJS_DATE_TEST) $(OBJS_DATE)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build toString_test
|
||||||
|
toString_test: $(OBJS_TOSTRING_TEST)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to build string_cast_test
|
||||||
|
string_cast_test: $(OBJS_STRING_CAST_TEST)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
# Rule to compile source files into object files
|
||||||
|
%.o: %.cc
|
||||||
|
$(CXX) $(CXXFLAGS) -c $<
|
||||||
|
|
||||||
|
# Clean up build artifacts
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS_TAGREMOVER) $(OBJS_SIEVE) $(OBJS_DATE) $(OBJS_DATE_TEST) $(OBJS_TOSTRING_TEST) $(OBJS_STRING_CAST_TEST) $(TARGETS)
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
|
.PHONY: all clean
|
61
lab4/reflektion.txt
Normal file
61
lab4/reflektion.txt
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
1. In your tests, how did you test the error handling (e.g., that a wrong string_cast actually
|
||||||
|
throws?)
|
||||||
|
|
||||||
|
Genom att använda en ogiltig inmatning, som "abc" eller "123abc", och verifiera att std::invalid_argument kastas korrekt.
|
||||||
|
|
||||||
|
|
||||||
|
2. In TagRemover, why do you think the constructor takes an istream instead of just the
|
||||||
|
filename?
|
||||||
|
|
||||||
|
Det gör klassen mer flexibel eftersom den kan arbeta med vilken ström som helst, inte bara filer. Till exempel kan den användas med std::cin eller std::stringstream.
|
||||||
|
|
||||||
|
|
||||||
|
3. In TagRemover, did you process the file line by line, or did you first read the entire file?
|
||||||
|
What are the pros and cons of these two approaches?
|
||||||
|
|
||||||
|
Jag läste hela filen först eftersom det är enklare att använda regex för att bearbeta hela texten på en gång.
|
||||||
|
Fördelen är att det är mer effektivt för regex-matchning, men nackdelen är att det kräver mer minne för stora filer.
|
||||||
|
|
||||||
|
|
||||||
|
4. How do you read the entire contents of an std::istream into a std::string without using
|
||||||
|
a for or while loop?
|
||||||
|
|
||||||
|
Genom att använda:
|
||||||
|
std::string content((std::istreambuf_iterator<char>(istream)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
|
||||||
|
5. In TagRemover, do you have duplicate code for translating the special characters? If so, how
|
||||||
|
would you refactor your code to avoid duplicate code?
|
||||||
|
|
||||||
|
Ja, det finns duplicerad kod för varje specialtecken. Jag skulle använda en std::map för att lagra mönster och ersättningar och iterera genom den:
|
||||||
|
std::map<std::string, std::string> replacements = {
|
||||||
|
{"<", "<"}, {">", ">"}, {" ", " "}, {"&", "&"}
|
||||||
|
};
|
||||||
|
for (const auto& [pattern, replacement] : replacements) {
|
||||||
|
result = std::regex_replace(result, std::regex(pattern), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
6. How do you check if an input or output operation on a stream (e.g., operator>> or
|
||||||
|
operator<<) has failed?
|
||||||
|
|
||||||
|
Genom att använda std::istream::fail() eller std::ostream::fail().
|
||||||
|
|
||||||
|
|
||||||
|
7. How do you know if you have reached the end of an istream?
|
||||||
|
|
||||||
|
Genom att använda std::istream::eof().
|
||||||
|
|
||||||
|
|
||||||
|
8. Does string_cast<int>("123kalle") return the value 123 or does it throw an exception?
|
||||||
|
How do you implement each of those behaviours?
|
||||||
|
|
||||||
|
Det kastar ett undantag eftersom std::istringstream misslyckas med att konsumera hela strängen. För att tillåta delvis inläsning skulle vi behöva anpassa funktionen.
|
||||||
|
|
||||||
|
|
||||||
|
9. When calling the function template toString, the template type argument is not ex-
|
||||||
|
plicitly given in the call. For string_cast, on the other hand, you have to specify
|
||||||
|
string_cast<int> or string_cast<Date>. What is the difference? When should explicit
|
||||||
|
template arguments be given to function templates?
|
||||||
|
|
||||||
|
toString använder <<-operatören, som automatiskt identifierar typen av objekt, medan string_cast kräver en explicit typ eftersom det behöver veta vad strängen ska konverteras till.
|
14
lab4/string_cast.h
Normal file
14
lab4/string_cast.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept> // För std::invalid_argument
|
||||||
|
|
||||||
|
// Template function for string_cast
|
||||||
|
template <typename T>
|
||||||
|
T string_cast(const std::string& str) {
|
||||||
|
std::istringstream iss(str);
|
||||||
|
T value;
|
||||||
|
if (!(iss >> value) || !(iss.eof())) {
|
||||||
|
throw std::invalid_argument("Invalid conversion from string: " + str);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
29
lab4/string_cast_test.cc
Normal file
29
lab4/string_cast_test.cc
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include "date.h" // För Date-klassen
|
||||||
|
#include "string_cast.h" // Inkludera string_cast
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
// Testa string_cast med int
|
||||||
|
int i = string_cast<int>("123");
|
||||||
|
std::cout << "Integer: " << i << std::endl;
|
||||||
|
|
||||||
|
// Testa string_cast med double
|
||||||
|
double d = string_cast<double>("12.34");
|
||||||
|
std::cout << "Double: " << d << std::endl;
|
||||||
|
|
||||||
|
// Testa string_cast med Date
|
||||||
|
Date date = string_cast<Date>("2015-01-10");
|
||||||
|
std::cout << "Date: " << date << std::endl;
|
||||||
|
|
||||||
|
// Testa ogiltig konvertering
|
||||||
|
int invalid = string_cast<int>("abc");
|
||||||
|
std::cout << "Invalid conversion: " << invalid << std::endl;
|
||||||
|
|
||||||
|
} catch (const std::invalid_argument& e) {
|
||||||
|
std::cout << "Error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
10
lab4/toString.h
Normal file
10
lab4/toString.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Template function to convert an object to a string
|
||||||
|
template <typename T>
|
||||||
|
std::string toString(const T& obj) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << obj;
|
||||||
|
return oss.str();
|
||||||
|
}
|
22
lab4/toString_test.cc
Normal file
22
lab4/toString_test.cc
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "date.h" // Inkludera Date-klassen från tidigare lösning
|
||||||
|
#include "toString.h" // Inkludera toString-mallen
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Testa med ett primitivt datatyper
|
||||||
|
double d = 1.234;
|
||||||
|
int i = 42;
|
||||||
|
std::string strDouble = toString(d);
|
||||||
|
std::string strInt = toString(i);
|
||||||
|
|
||||||
|
std::cout << "Double as string: " << strDouble << std::endl;
|
||||||
|
std::cout << "Integer as string: " << strInt << std::endl;
|
||||||
|
|
||||||
|
// Testa med Date-klassen
|
||||||
|
Date today(2023, 12, 11); // Skapa ett datumobjekt
|
||||||
|
std::string strDate = toString(today);
|
||||||
|
|
||||||
|
std::cout << "Date as string: " << strDate << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue