diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..53017a6 --- /dev/null +++ b/.clang-format @@ -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 +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..655eb89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.o +*.d +*a.out* +build +.cache/ +words.txt +compile_commands.json +lab2/edit +lab2/spell +lab3/tabletest +./vscode \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..51257f5 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +format: + find . -regex '.*\.\(c\|cpp\|cc\|cxx\|h\|hpp\|hh\|hxx\)' -exec clang-format {} + diff --git a/cpplabs.pdf b/cpplabs.pdf new file mode 100644 index 0000000..178567b Binary files /dev/null and b/cpplabs.pdf differ diff --git a/lab1/.gitignore b/lab1/.gitignore new file mode 100644 index 0000000..6f6e224 --- /dev/null +++ b/lab1/.gitignore @@ -0,0 +1,2 @@ +hello +test_editor diff --git a/lab1/Justfile b/lab1/Justfile new file mode 100644 index 0000000..d7b08bc --- /dev/null +++ b/lab1/Justfile @@ -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 diff --git a/lab1/buggy_programs/.gitignore b/lab1/buggy_programs/.gitignore new file mode 100644 index 0000000..eb7d7dc --- /dev/null +++ b/lab1/buggy_programs/.gitignore @@ -0,0 +1,6 @@ +bounds +bounds-heap +dangling +leak +sum +ub diff --git a/lab1/buggy_programs/CMakeLists.txt b/lab1/buggy_programs/CMakeLists.txt index c3a3b00..a11369d 100644 --- a/lab1/buggy_programs/CMakeLists.txt +++ b/lab1/buggy_programs/CMakeLists.txt @@ -3,7 +3,7 @@ # It uses generator expressions, to set additional flags when the build type # is Debug. # -# To try this out, first create a build directory for a release build, +# To try this out, first create a build directory for a release build, # and do a release build, e.g., # % mkdir build-rel # % cd build-rel @@ -21,7 +21,7 @@ # # where SRC_DIR is the directory containing the source and CMakeLists.txt, # e.g., .. if your build directories are placed in this directory. -# +# # Run the examples and verify that the sanitizers find the errors. # # If you want to see the actual commands run during the build, for instance diff --git a/lab1/cmake-example/.gitignore b/lab1/cmake-example/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/lab1/cmake-example/.gitignore @@ -0,0 +1 @@ +build diff --git a/lab1/coding.cc b/lab1/coding.cc index 6b28096..1b51ffd 100644 --- a/lab1/coding.cc +++ b/lab1/coding.cc @@ -1,10 +1,9 @@ -#include +#include -unsigned char encode(unsigned char c) -{ - return c; +unsigned char encode(unsigned char c) { + return ++c; } -unsigned char decode(unsigned char c) -{ - return c; + +unsigned char decode(unsigned char c) { + return --c; } diff --git a/lab1/editor.cc b/lab1/editor.cc index 82ec95b..fca27d4 100644 --- a/lab1/editor.cc +++ b/lab1/editor.cc @@ -6,11 +6,29 @@ using std::string; using size_type = Editor::size_type; -size_type Editor::get_size() const -{ - return text.size(); -} +size_type Editor::get_size() const { return text.size(); } -size_type Editor::find_left_par(size_type pos) const { - return string::npos; +Editor::size_type Editor::find_left_par(size_type pos) const { + 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 } diff --git a/lab1/editor.h b/lab1/editor.h index f1a576a..5d6ac7b 100644 --- a/lab1/editor.h +++ b/lab1/editor.h @@ -4,24 +4,24 @@ #include class Editor { -public: + public: using size_type = std::string::size_type; - /* Creates a text editor containing the text t */ - Editor(const std::string& t) : text(t) {} + /* Creates a text editor containing the text t */ + Editor(const std::string &t) : text(t) {} /* Get the size of the current contents */ size_type get_size() const; - /* - * Text[pos] contains a right parentheses. Returns the position of - * the corresponding left parentheses, or string::npos if there - * is no match. - */ - size_type find_left_par(size_type pos) const; + /* + * Text[pos] contains a right parentheses. Returns the position of + * the corresponding left parentheses, or string::npos if there + * is no match. + */ + size_type find_left_par(size_type pos) const; - // ... functions to edit the text (insert and delete characters) -private: - std::string text; + // ... functions to edit the text (insert and delete characters) + private: + std::string text; }; #endif diff --git a/lab1/hello.cc b/lab1/hello.cc new file mode 100644 index 0000000..b873300 --- /dev/null +++ b/lab1/hello.cc @@ -0,0 +1,5 @@ +#include + +int main(int argc, char *argv[]) { + std::cout << "Hello" << std::endl; +} diff --git a/lab2/Makefile b/lab2/Makefile new file mode 100644 index 0000000..aaa709f --- /dev/null +++ b/lab2/Makefile @@ -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 diff --git a/lab2/dictionary.cc b/lab2/dictionary.cc index 79bb69e..2e999c8 100644 --- a/lab2/dictionary.cc +++ b/lab2/dictionary.cc @@ -1,22 +1,131 @@ -#include -#include +#include "dictionary.h" +#include "edit_distance.h" +#include "word.h" +#include #include #include -#include -#include "word.h" -#include "dictionary.h" +#include +#include +#include using std::string; using std::vector; -Dictionary::Dictionary() { +Dictionary::Dictionary() {} + +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 false; } -bool Dictionary::contains(const string& word) const { - return true; +std::vector Dictionary::get_suggestions(const string &word) const { + vector suggestions; + add_trigram_suggestions(suggestions, word); + trim_suggestions(suggestions, word); + rank_suggestions(suggestions, word); + return suggestions; } -vector Dictionary::get_suggestions(const string& word) const { - vector suggestions; - return suggestions; +void Dictionary::add_trigram_suggestions(std::vector &suggestions, + const std::string &word) const { + // Get trigrams of the input word + Word input_word(word); + const std::vector &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 &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 &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 &suggestions, + const std::string &word) const { + // Remove duplicates using a set + std::set 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(MAXLEN) - 1)].push_back( + Word(line)); + } + + file.close(); + return 0; } diff --git a/lab2/dictionary.h b/lab2/dictionary.h index b56f143..31eada4 100644 --- a/lab2/dictionary.h +++ b/lab2/dictionary.h @@ -1,15 +1,29 @@ -#ifndef DICTIONARY_H -#define DICTIONARY_H +#pragma once +#include "word.h" +#include #include #include -class Dictionary { -public: - Dictionary(); - bool contains(const std::string& word) const; - std::vector get_suggestions(const std::string& word) const; -private: -}; +#define MAXLEN 30 -#endif +// using std::vector; +using std::filesystem::path; + +class Dictionary { + public: + Dictionary(); + void add_trigram_suggestions(std::vector &suggestions, + const std::string &word) const; + void rank_suggestions(std::vector &suggestions, + const std::string &word) const; + void trim_suggestions(std::vector &suggestions, + const std::string &word) const; + bool contains(const std::string &word) const; + std::vector get_suggestions(const std::string &word) const; + int slurp(path p); + int spit(path p); + + private: + std::vector words[MAXLEN]; +}; diff --git a/lab2/edit_distance.cc b/lab2/edit_distance.cc new file mode 100644 index 0000000..7f635c9 --- /dev/null +++ b/lab2/edit_distance.cc @@ -0,0 +1,35 @@ +#include +#include +#include + +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> dp(m + 1, std::vector(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(dp[m][n]); +} diff --git a/lab2/edit_distance.h b/lab2/edit_distance.h new file mode 100644 index 0000000..6ddc48a --- /dev/null +++ b/lab2/edit_distance.h @@ -0,0 +1,17 @@ +#include + +/** + * @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); diff --git a/lab2/spell.cc b/lab2/spell.cc index dfb820b..38f0249 100644 --- a/lab2/spell.cc +++ b/lab2/spell.cc @@ -1,18 +1,18 @@ +#include "dictionary.h" +#include +#include +#include #include #include -#include #include -#include -#include "dictionary.h" -using std::string; -using std::vector; using std::cin; using std::cout; 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)) { cout << "Correct." << endl; } else { @@ -21,18 +21,22 @@ void check_word(const string& word, const Dictionary& dict) cout << "Wrong, no suggestions." << endl; } else { cout << "Wrong. Suggestions:" << endl; - for (const auto& w : suggestions) { + for (const auto &w : suggestions) { cout << " " << w << endl; } } } } + int main() { - Dictionary dict; - string word; - while (cin >> word) { - transform(word.begin(), word.end(), word.begin(), ::tolower); + Dictionary dict; + string word; + dict.slurp(std::filesystem::path("/usr/share/dict/words")); + // dict.spit(std::filesystem::path("words.txt")); + + while (cin >> word) { + std::transform(word.begin(), word.end(), word.begin(), ::tolower); check_word(word, dict); - } + } return 0; } diff --git a/lab2/test_edit_distance.cc b/lab2/test_edit_distance.cc index 1f44719..982badf 100644 --- a/lab2/test_edit_distance.cc +++ b/lab2/test_edit_distance.cc @@ -8,8 +8,7 @@ #include -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); if (actual != expected) { 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; } -int main() -{ +int main() { int res = do_test("foobar", "foobar", 0); res += do_test("x", "x", 0); res += do_test("baz", "bar", 1); diff --git a/lab2/word.cc b/lab2/word.cc index 20b9417..293f66e 100644 --- a/lab2/word.cc +++ b/lab2/word.cc @@ -1,16 +1,63 @@ +#include "word.h" +#include "dictionary.h" +#include #include #include -#include "word.h" -using std::vector; using std::string; +using std::vector; -Word::Word(const string& w, const vector& t) {} - -string Word::get_word() const { - return string(); +Word::Word(const string &w, const vector &t) : word(w), trigrams(t) { + std::sort(trigrams.begin(), trigrams.end()); } -unsigned int Word::get_matches(const vector& t) const { - return 0; +std::vector get_trigrams(const std::string &text) { + std::vector 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 Word::get_trigrams() const { return trigrams; } + +unsigned int Word::get_matches(const vector &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()); } diff --git a/lab2/word.h b/lab2/word.h index 9fb0716..27b92af 100644 --- a/lab2/word.h +++ b/lab2/word.h @@ -1,21 +1,34 @@ -#ifndef WORD_H -#define WORD_H +#pragma once #include #include +/* + * Contains a word and its trigrams + */ class Word { -public: - /* Creates a word w with the sorted trigrams t */ - Word(const std::string& w, const std::vector& t); - - /* Returns the word */ - std::string get_word() const; - - /* Returns how many of the trigrams in t that are present - in this word's trigram vector */ - unsigned int get_matches(const std::vector& t) const; -private: + public: + /** Creates a word w with the sorted trigrams t */ + Word(const std::string &w, const std::vector &t); + + /** Creates a word w and derives the trigrams internally */ + Word(const std::string &w); + + /** Returns the word */ + std::string get_word() const; + + /** Returns trigrams */ + std::vector get_trigrams() const; + + /** Returns how many of the trigrams in t that are present + in this word's trigram vector */ + unsigned int get_matches(const std::vector &t) const; + + private: + const std::string word; + std::vector 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); diff --git a/lab3/Makefile b/lab3/Makefile new file mode 100644 index 0000000..e6d004c --- /dev/null +++ b/lab3/Makefile @@ -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 diff --git a/lab3/User.cc b/lab3/User.cc index 0268bd2..d45437e 100644 --- a/lab3/User.cc +++ b/lab3/User.cc @@ -1,6 +1,5 @@ #include "User.h" -std::ostream& operator<<(std::ostream& os, const User& u) -{ - return os << "(" << u.getCardNbr() << ") "<< u.getName(); +std::ostream &operator<<(std::ostream &os, const User &u) { + return os << "(" << u.getCardNbr() << ") " << u.getName(); } diff --git a/lab3/User.h b/lab3/User.h index cbedfe0..b4a8868 100644 --- a/lab3/User.h +++ b/lab3/User.h @@ -8,20 +8,26 @@ using std::cout; using std::endl; class User { -public: - User() :cardNbr{0},name{"default"}{} - User(int c, std::string n) :cardNbr{c},name{n} {} - ~User() {cardNbr=-2; name="--------------------";} // overwrite values for security reasons - User(const User& u) =default; - User& operator=(const User&) =default; - int getCardNbr() const {return cardNbr;} - 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: + public: + User() : cardNbr{0}, name{"default"} {} + User(int c, std::string n) : cardNbr{c}, name{n} {} + ~User() { + cardNbr = -2; + name = "--------------------"; + } // overwrite values for security reasons + User(const User &u) = default; + User &operator=(const User &) = default; + int getCardNbr() const { return cardNbr; } + 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; std::string name; }; -std::ostream& operator<<(std::ostream& os, const User& u); +std::ostream &operator<<(std::ostream &os, const User &u); #endif diff --git a/lab3/UserTable.cc b/lab3/UserTable.cc index 91db558..542cd0d 100644 --- a/lab3/UserTable.cc +++ b/lab3/UserTable.cc @@ -1,24 +1,22 @@ #include "UserTable.h" -#include #include +#include -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); - if(ufile.is_open()) { - while(ufile) { + if (ufile.is_open()) { + while (ufile) { int cn; - if(ufile >> cn ) { + if (ufile >> cn) { ufile.ignore(); // skip space char n[80]; - ufile.getline(n,80); - addUser(User(cn,n)); - + ufile.getline(n, 80); + addUser(User(cn, n)); } } } 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 - ensureCapacity(n+1); + ensureCapacity(n + 1); + // 1. Hitta rätt plats int pos{0}; - while ( (pos < n) && (users[pos].getCardNbr() < u.getCardNbr())){ + while ((pos < n) && (users[pos].getCardNbr() < u.getCardNbr())) { ++pos; } - //2. skapa lucka i vektorn - for(int i=n; i > pos; --i){ - users[i] = users[i-1]; + // 2. skapa lucka i vektorn + for (int i = n; i > pos; --i) { + 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; + + ++n; } -User UserTable::find(int c) const -{ +User UserTable::find(int c) const { // binärsökning (baserad på Holm, 2007) int low = 0; int high = n - 1; int mid = -1; bool found = false; - while (low < high && ! found) { + while (low < high && !found) { mid = (low + high) / 2; // - int midnbr = users[mid].getCardNbr(); - if (midnbr = c) { + const int midnbr = users[mid].getCardNbr(); + if (c == midnbr) { found = true; } else if (users[mid].getCardNbr() < c) { low = mid + 1; @@ -68,48 +67,41 @@ User UserTable::find(int c) const return found ? users[mid] : user_not_found; } -User UserTable::find(std::string name) const -{ - for (int i = 0; i != n; ++i) { - if (users[i].getName() == name) { - return users[i]; - } else { - return user_not_found; - } - } - return user_not_found; +User UserTable::find(std::string name) const { + auto it = std::find_if(users, users + n, [&name](const User &u) { + return u.getName() == name; + }); + + // If it is at the 'end' of users, the result is not found + return (it != users+n) ? *it : user_not_found; } -void UserTable::ensureCapacity(int s) -{ - if(s>capacity) { - while(s > capacity) { - capacity*=4; +void UserTable::ensureCapacity(int s) { + if (s > capacity) { + while (s > capacity) { + capacity *= 4; } auto tmp = new User[capacity]; - std::copy(users, users+n, tmp); + std::copy(users, users + n, tmp); delete[] users; - users=tmp; + users = tmp; } - } -void UserTable::print(std::ostream& os) const -{ +void UserTable::print(std::ostream &os) const { os << "-------------" << std::endl; - for(int i = 0; i != getNbrUsers(); ++i) { - const auto& u = users[i]; - os << "(" < + +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); + } +} diff --git a/lab4/.vscode/settings.json b/lab4/.vscode/settings.json new file mode 100644 index 0000000..b73e5a3 --- /dev/null +++ b/lab4/.vscode/settings.json @@ -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" + } +} \ No newline at end of file diff --git a/lab4/Sieve.cc b/lab4/Sieve.cc new file mode 100644 index 0000000..9360a66 --- /dev/null +++ b/lab4/Sieve.cc @@ -0,0 +1,74 @@ +#include +#include +#include + +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 getPrimes() const { + std::vector 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; +} diff --git a/lab4/TagRemover.cc b/lab4/TagRemover.cc new file mode 100644 index 0000000..3f28d28 --- /dev/null +++ b/lab4/TagRemover.cc @@ -0,0 +1,32 @@ +#include +#include +#include + +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; +} \ No newline at end of file diff --git a/lab4/date.cc b/lab4/date.cc index ee9b34a..bf6e37d 100644 --- a/lab4/date.cc +++ b/lab4/date.cc @@ -1,30 +1,86 @@ -#include // time and localtime +#include // för tid och localtime +#include // för setw och setfill +#include // för inputhantering #include "date.h" int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +// Konstruktor: dagens datum Date::Date() { - time_t timer = time(0); // time in seconds since 1970-01-01 - tm* locTime = localtime(&timer); // broken-down time - year = 1900 + locTime->tm_year; - month = 1 + locTime->tm_mon; - day = locTime->tm_mday; + time_t timer = time(0); // tid i sekunder sedan 1970-01-01 + tm* locTime = localtime(&timer); // lokal tid + year = 1900 + locTime->tm_year; + month = 1 + locTime->tm_mon; + 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 { - return 0; + return year; } int Date::getMonth() const { - return 0; + return month; } int Date::getDay() const { - return 0; + return day; } +// Gå till nästa dag 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; +} diff --git a/lab4/date.h b/lab4/date.h index 9cfecaa..25b57df 100644 --- a/lab4/date.h +++ b/lab4/date.h @@ -1,19 +1,27 @@ #ifndef DATE_H #define DATE_H +#include + class Date { public: - Date(); // today's date - Date(int y, int m, int d); // yyyy-mm-dd - int getYear() const; // get the year - int getMonth() const; // get the month - int getDay() const; // get the day - void next(); // advance to next day + Date(); // dagens datum + Date(int y, int m, int d); // yyyy-mm-dd + int getYear() const; // returnerar året + int getMonth() const; // returnerar månaden + int getDay() const; // returnerar dagen + 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: - int year; // the year (four digits) - int month; // the month (1-12) - int day; // the day (1-..) - static int daysPerMonth[12]; // number of days in each month + int year; // året (fyra siffror) + int month; // månaden (1-12) + int day; // dagen (1-...) + static int daysPerMonth[12]; // antal dagar i varje månad + static bool isLeapYear(int year); // kontrollera skottår }; #endif diff --git a/lab4/date_test.cc b/lab4/date_test.cc index abd24c9..c66ee07 100644 --- a/lab4/date_test.cc +++ b/lab4/date_test.cc @@ -1,64 +1,40 @@ #include -#include // for setw and setfill #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() { - // Check input and output of dates. Uncomment the following when you - // have added operator>> and operator<<. - /* - bool cont = true; - while (cont) { - cout << "Type a date: "; - Date aDate; - cin >> aDate; - if (cin.eof()) { - cont = false; - } else if (!cin.good()) { - cout << "Wrong input format" << endl; - // restore stream state and ignore the rest of the line - cin.clear(); - cin.ignore(10000, '\n'); - } - else { - cout << "Output: " << aDate << endl; - } - } - */ - - // Check 'next' by creating an object describing today's date, then - // printing dates more than a month ahead - cout << "--- Today and more than a month ahead:" << endl; - Date d1; - print(d1); - cout << endl; - for (int i = 1; i <= 35 ; ++i) { - d1.next(); - print(d1); - cout << endl; - } - - // Check so 'next' functions correctly from one year to the next - cout << "--- New Year's Eve and the next day:" << endl; - Date d2(2013, 12, 31); - print(d2); - cout << endl; - d2.next(); - print(d2); - cout << endl; + // Test input och output + bool cont = true; + while (cont) { + std::cout << "Type a date: "; + Date aDate; + std::cin >> aDate; + + if (std::cin.eof()) { + cont = false; + } else if (!std::cin.good()) { + std::cout << "Wrong input format" << std::endl; + std::cin.clear(); + std::cin.ignore(10000, '\n'); + } else { + std::cout << "Output: " << aDate << std::endl; + } + } + + // Testa 'next' med dagens datum + std::cout << "--- Today and more than a month ahead:" << std::endl; + Date d1; + std::cout << d1 << std::endl; + for (int i = 1; i <= 35; ++i) { + d1.next(); + std::cout << d1 << std::endl; + } + + // Testa 'next' från nyårsafton + std::cout << "--- New Year's Eve and the next day:" << std::endl; + Date d2(2013, 12, 31); + std::cout << d2 << std::endl; + d2.next(); + std::cout << d2 << std::endl; + + return 0; } diff --git a/lab4/makefile b/lab4/makefile new file mode 100644 index 0000000..ca93933 --- /dev/null +++ b/lab4/makefile @@ -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 diff --git a/lab4/reflektion.txt b/lab4/reflektion.txt new file mode 100644 index 0000000..048257c --- /dev/null +++ b/lab4/reflektion.txt @@ -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(istream)), std::istreambuf_iterator()); + + +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 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("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 or string_cast. 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. diff --git a/lab4/string_cast.h b/lab4/string_cast.h new file mode 100644 index 0000000..cc258be --- /dev/null +++ b/lab4/string_cast.h @@ -0,0 +1,14 @@ +#include +#include +#include // För std::invalid_argument + +// Template function for string_cast +template +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; +} diff --git a/lab4/string_cast_test.cc b/lab4/string_cast_test.cc new file mode 100644 index 0000000..23a812a --- /dev/null +++ b/lab4/string_cast_test.cc @@ -0,0 +1,29 @@ +#include +#include +#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("123"); + std::cout << "Integer: " << i << std::endl; + + // Testa string_cast med double + double d = string_cast("12.34"); + std::cout << "Double: " << d << std::endl; + + // Testa string_cast med Date + Date date = string_cast("2015-01-10"); + std::cout << "Date: " << date << std::endl; + + // Testa ogiltig konvertering + int invalid = string_cast("abc"); + std::cout << "Invalid conversion: " << invalid << std::endl; + + } catch (const std::invalid_argument& e) { + std::cout << "Error: " << e.what() << std::endl; + } + + return 0; +} diff --git a/lab4/toString.h b/lab4/toString.h new file mode 100644 index 0000000..c4f0154 --- /dev/null +++ b/lab4/toString.h @@ -0,0 +1,10 @@ +#include +#include + +// Template function to convert an object to a string +template +std::string toString(const T& obj) { + std::ostringstream oss; + oss << obj; + return oss.str(); +} diff --git a/lab4/toString_test.cc b/lab4/toString_test.cc new file mode 100644 index 0000000..4e9860f --- /dev/null +++ b/lab4/toString_test.cc @@ -0,0 +1,22 @@ +#include +#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; +}