From c58f3f4fa6abf8f6cbf1c4eade32ce4e1539472c Mon Sep 17 00:00:00 2001 From: Gurkirst Singh Date: Sat, 5 Oct 2024 04:22:29 +0530 Subject: [PATCH 1/2] remove `.vscode/settings.json` and add to git ignore --- .gitignore | 2 +- .vscode/settings.json | 33 --------------------------------- 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index a343eb4..14c0b24 100644 --- a/.gitignore +++ b/.gitignore @@ -57,7 +57,7 @@ _deps ### VisualStudioCode ### .vscode/* -!.vscode/settings.json +.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 67d9b30..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cmake.configureOnOpen": false, - "cmake.configureOnEdit": false, - "[cmake]": { - "editor.formatOnSave": true, - "editor.defaultFormatter": "twxs.cmake" - }, - "[cpp]": { - "editor.formatOnSave": true, - "editor.insertSpaces": false, - "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", - "editor.tabSize": 2 - }, - "[json]": { - "editor.quickSuggestions": { - "strings": true - }, - "editor.suggest.insertMode": "replace", - "editor.formatOnSave": true, - "editor.defaultFormatter": "vscode.json-language-features" - }, - "[jsonc]": { - "editor.quickSuggestions": { - "strings": true - }, - "editor.suggest.insertMode": "replace", - "editor.formatOnSave": true, - "editor.defaultFormatter": "vscode.json-language-features" - }, - "clangd.arguments": [ - "--header-insertion=never" - ] -} \ No newline at end of file From e71e2e824f5a576cbceb6f6b76bc6f8e7293af25 Mon Sep 17 00:00:00 2001 From: Gurkirst Singh Date: Sat, 5 Oct 2024 05:05:10 +0530 Subject: [PATCH 2/2] implement v3 of library with tests --- .clang-format | 8 + .clang-tidy | 8 + .../apt-sources/ubuntu-archive-keyring.gpg | Bin 0 -> 3607 bytes .github/apt-sources/ubuntu.sources | 44 + .github/workflows/docs.yaml | 26 +- .github/workflows/unit_testing.yaml | 124 +-- CMakeLists.txt | 24 +- README.md | 146 ++-- examples/CMakeLists.txt | 6 +- examples/main.cpp | 32 +- headers/firefly/utilities.hpp | 274 ++++++ headers/firefly/vector.hpp | 822 +++++++++++++----- scripts/test_generator.sh | 46 - src/CMakeLists.txt | 2 - src/vector/CMakeLists.txt | 4 - src/vector/add.cpp | 35 - src/vector/angle_with.cpp | 15 - src/vector/anti_parallel.cpp | 5 - src/vector/area.cpp | 11 - src/vector/cross.cpp | 17 - src/vector/dot.cpp | 14 - src/vector/elem_sum.cpp | 10 - src/vector/indexers.cpp | 32 - src/vector/is_normalized.cpp | 7 - src/vector/is_orthogonal.cpp | 7 - src/vector/is_parallel.cpp | 26 - src/vector/is_same.cpp | 18 - src/vector/is_sparse.cpp | 17 - src/vector/is_zero.cpp | 10 - src/vector/magnitude.cpp | 9 - src/vector/normalize.cpp | 11 - src/vector/scale.cpp | 19 - src/vector/size.cpp | 6 - src/vector/subtract.cpp | 13 - src/vector/vector.cpp | 15 - src/vector/view.cpp | 29 - tests/CMakeLists.txt | 7 +- tests/utilities/CMakeLists.txt | 1 + tests/utilities/utilities.cpp | 411 +++++++++ tests/vector/CMakeLists.txt | 1 + tests/vector/add.cpp | 102 ++- tests/vector/angle_with.cpp | 39 - tests/vector/anti_parallel.cpp | 14 - tests/vector/area.cpp | 42 - tests/vector/constructor.cpp | 33 +- tests/vector/cross.cpp | 64 -- tests/vector/dot.cpp | 53 -- tests/vector/elem_sum.cpp | 22 - tests/vector/indexers.cpp | 26 - tests/vector/is_normalized.cpp | 38 - tests/vector/is_orthogonal.cpp | 44 - tests/vector/is_parallel.cpp | 34 - tests/vector/is_same.cpp | 22 - tests/vector/is_sparse.cpp | 40 - tests/vector/is_zero.cpp | 22 - tests/vector/magnitude.cpp | 28 - tests/vector/misc.cpp | 104 +++ tests/vector/normalize.cpp | 25 - tests/vector/product.cpp | 238 +++++ tests/vector/scale.cpp | 41 - tests/vector/size.cpp | 16 - tests/vector/subtract.cpp | 100 ++- tests/vector/view.cpp | 16 - 63 files changed, 1986 insertions(+), 1489 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .github/apt-sources/ubuntu-archive-keyring.gpg create mode 100644 .github/apt-sources/ubuntu.sources create mode 100644 headers/firefly/utilities.hpp delete mode 100755 scripts/test_generator.sh delete mode 100644 src/CMakeLists.txt delete mode 100644 src/vector/CMakeLists.txt delete mode 100644 src/vector/add.cpp delete mode 100644 src/vector/angle_with.cpp delete mode 100644 src/vector/anti_parallel.cpp delete mode 100644 src/vector/area.cpp delete mode 100644 src/vector/cross.cpp delete mode 100644 src/vector/dot.cpp delete mode 100644 src/vector/elem_sum.cpp delete mode 100644 src/vector/indexers.cpp delete mode 100644 src/vector/is_normalized.cpp delete mode 100644 src/vector/is_orthogonal.cpp delete mode 100644 src/vector/is_parallel.cpp delete mode 100644 src/vector/is_same.cpp delete mode 100644 src/vector/is_sparse.cpp delete mode 100644 src/vector/is_zero.cpp delete mode 100644 src/vector/magnitude.cpp delete mode 100644 src/vector/normalize.cpp delete mode 100644 src/vector/scale.cpp delete mode 100644 src/vector/size.cpp delete mode 100644 src/vector/subtract.cpp delete mode 100644 src/vector/vector.cpp delete mode 100644 src/vector/view.cpp create mode 100644 tests/utilities/CMakeLists.txt create mode 100644 tests/utilities/utilities.cpp create mode 100644 tests/vector/CMakeLists.txt delete mode 100644 tests/vector/angle_with.cpp delete mode 100644 tests/vector/anti_parallel.cpp delete mode 100644 tests/vector/area.cpp delete mode 100644 tests/vector/cross.cpp delete mode 100644 tests/vector/dot.cpp delete mode 100644 tests/vector/elem_sum.cpp delete mode 100644 tests/vector/indexers.cpp delete mode 100644 tests/vector/is_normalized.cpp delete mode 100644 tests/vector/is_orthogonal.cpp delete mode 100644 tests/vector/is_parallel.cpp delete mode 100644 tests/vector/is_same.cpp delete mode 100644 tests/vector/is_sparse.cpp delete mode 100644 tests/vector/is_zero.cpp delete mode 100644 tests/vector/magnitude.cpp create mode 100644 tests/vector/misc.cpp delete mode 100644 tests/vector/normalize.cpp create mode 100644 tests/vector/product.cpp delete mode 100644 tests/vector/scale.cpp delete mode 100644 tests/vector/size.cpp delete mode 100644 tests/vector/view.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..be7d7b4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +BasedOnStyle: LLVM +ColumnLimit: 120 +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: false + AfterClass: false +AlwaysBreakTemplateDeclarations: Yes +AllowShortFunctionsOnASingleLine: Empty \ No newline at end of file diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..7ce4430 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,8 @@ +--- + +Checks: 'clang-diagnostic-*,clang-analyzer-*' +HeaderFileExtensions: + - hpp +ImplementationFileExtensions: + - cpp + diff --git a/.github/apt-sources/ubuntu-archive-keyring.gpg b/.github/apt-sources/ubuntu-archive-keyring.gpg new file mode 100644 index 0000000000000000000000000000000000000000..1585fd18651875e996152ee0c2e59282cc530a75 GIT binary patch literal 3607 zcmb8xX*d*&+6VBNF$QBB%1&sIZR~4g8`(yL$i8cajCGJDm8{vbjuDY1yX>-MCn9T> z7?fRhqQt4^Jn!|sAI|ljbMDXgeSi92zw3_&QG@l%o;(1;0K~`^9Vvscg=mL#)ZA?-v&CeGK8EUGEsLGbAo7px&b6ntYF`llwr$CrSlX zk!60TQ4O!Z^nC|d%uRgkPeYSE5{i=zesmD{Ng(L(@(*rgbzs8xz8}TQ%#09I%Z^__ zNbI*CBh=}yHi!~2PwD*QtHOWRvnC0ytr*0HIu5WTV?mM6sK-{C_ zB`3jgcZyaUcK1VhYWjS;jhL1;k&-vT(ko~sGhgON5oKJ#aO;d7TZ=AK}&%4yWr zJ&Sxdm0!-8c=WdTGulvAdB!s1*`HVWCuDDzWRK^Z+vxb@Wbw=Q_+k+(5x!uy4w&bT z>cfqVMH1+nb#}H%(Al?dI%;=0Z97bMZ@viAxA@Dc-|*VC}qdPF^ZLf1Qaq zxMP+6{s{W#$0(2-_!1BVxJnB86FU$a351e@oQwnnVIT)lKqchm;XZV^$yHqsgoj#z{#^Hme zb|W=QG@mp3z_b@nC%8LpH(JfkbRi*?N1;Af(^w^I$lHRdf9OV-M~b19 zV|B*bY3Q=kgf(8VQB1MbEm6T`bT+W-+bsb#wIe^)d@u+lzQeLA^sqOXxZc9UNGrL)ef5=jI4>E9FEemG$+@N5B3LVH z?^mSvS`$rUo5j+TUj>aw(6kJ~oDQV)pQLO@7}czKhWBEkGOx~WWV%sDBU62eFYo|pDQhI*YRa;yP& z_pkQQJv6R_07!mE6M+AVtT@bXvYPU8oA!w}sNGhY-_wWiWFiheFb$7sS)GjvNrm1) z&5ad!dUDEiA8#nQ@724vPJ@#s+3L-Va<~rZc7qN74P{l9pGwJEI#d;yXIB z9J);D>n1Nr821Ska?7S>*yhG8UY73X^onlV7%eYT>aD|a?u1+^zsu{aV66419S!MD z%dT##<&dj*k8!YsSE~IWq{;Y;k!2o>z_^1Piymq^yxs3|vWio70wopZ=&5`1361sg z?OXor}^Zmj-G^KN0Ti9qK9$V zaROZ+)+VwuB|}xgHD~mdb{@7|w9&W+A+$ozHRCb{6g{Cpn+g5P?+qvI)bzh4;~WkR z4|oBxh>;vL38lr+zamTZZ?cf;T-sPW^uII9!O{88*8eA#>~FE40Pa6x#bJJnMf#Um zqzq6Ba!S%aUIG3WtZAKByE{v;=UOT-KvZn%-3ACjqO##A%a>D_;hvy&NSJ@)!PTi6 zW(nPJfjh7>y^|EJ&mR2*WaQx}zmY9x*g2wwglVQ3zpRt;l0ZJJ5YD`!u$H5hrskw+ zW5q9NDSI04q1~Tl5Zf1~;1Ox^gs(LTUE?ki&%WNO61hXr}LmO`Utbub0!!^%2Fcr`;r}?_#+D04JF8xbp?}LxGj2+ncraToVj|{a}Id&PBna>dRGj0WzRaWkYY3I z>RtC`_@L1NZ&a2?S^KFGBdpSw)K~XT0h`T=A!(3mELVU zO7OfqwNc3WZKj#X%;?tDqF#T-+}!V#bClsmT^>PO&-|a7E{Z)DTYv(1@;T40zIut&}MT{CR&XG*touc<)QthTm+Alj&k>?px zV1dqL?J^pE^g(vi(f&2b*s`dmfEEq!p?+tH-quKiknRWm&{FQ+ zo6na`zj_bH+c)bKo38WB?_zK+89fsWL5>AgpYvi0f>I`Sq1h3GN6opSQh?mu8br3P z*nkjF^yDjL$LA+dZGHz~vm=|k22l9hi-r|P9hMrX|2zxl9voODau#NEAF_^b-{$~R zyU)5uJH*@;df#9vNasPT@q=(+G|6C5PnRiU#%d+At;KF!7fHTA^NAqIAqO1g!*%IURxeR?K>DG^V-=M5nt0W!JYCoo0Tw>= z>4>_1^&ChXdJxk@Iw@DbEFIew+01;%WE&_7M(xlQ={$!-updates - Major bug fix updates produced after the final release of the +## distribution. +## -backports - software from this repository may not have been tested as +## extensively as that contained in the main release, although it includes +## newer versions of some applications which may provide useful features. +## Also, please note that software in backports WILL NOT receive any review +## or updates from the Ubuntu security team. +## Components: Aside from main, the following components can be added to the list +## restricted - Software that may not be under a free license, or protected by patents. +## universe - Community maintained packages. Software in this repository receives maintenance +## from volunteers in the Ubuntu community, or a 10 year security maintenance +## commitment from Canonical when an Ubuntu Pro subscription is attached. +## multiverse - Community maintained of restricted. Software from this repository is +## ENTIRELY UNSUPPORTED by the Ubuntu team, and may not be under a free +## licence. Please satisfy yourself as to your rights to use the software. +## Also, please note that software in multiverse WILL NOT receive any +## review or updates from the Ubuntu security team. +## +## See the sources.list(5) manual page for further settings. +Types: deb +URIs: http://archive.ubuntu.com/ubuntu/ +Suites: noble noble-updates noble-backports +Components: main universe restricted multiverse +Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg + +## Ubuntu security updates. Aside from URIs and Suites, +## this should mirror your choices in the previous section. +Types: deb +URIs: http://security.ubuntu.com/ubuntu/ +Suites: noble-security +Components: main universe restricted multiverse +Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index e91c4d7..1c7e7e8 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -1,8 +1,8 @@ name: Deploy docs on: - push: - branches: ["master"] + release: + types: published workflow_dispatch: permissions: @@ -21,28 +21,20 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: submodules: true - - name: Install doxygen - run: | + - run: | sudo apt install graphviz -y wget https://github.com/doxygen/doxygen/releases/download/Release_1_9_7/doxygen-1.9.7.linux.bin.tar.gz tar xf doxygen-1.9.7.linux.bin.tar.gz - - name: Setup Pages - uses: actions/configure-pages@v3 - - name: Pre-processing README.md - run: | - sed -i 's|\(tests/CMakeLists\.txt#L5\)|https://github.com/tbhaxor/firefly/blob/master/\1|' README.md - - name: Build documentation + - uses: actions/configure-pages@v3 + - name: build documentation run: | ./doxygen-1.9.7/bin/doxygen touch docs/html/.nojekyll - - name: Upload artifact - uses: actions/upload-pages-artifact@v2 + - uses: actions/upload-pages-artifact@v2 with: - path: 'docs/html' - - name: Deploy to GitHub Pages + path: "docs/html" + - uses: actions/deploy-pages@v2 id: deployment - uses: actions/deploy-pages@v2 diff --git a/.github/workflows/unit_testing.yaml b/.github/workflows/unit_testing.yaml index 500b4a6..c1f83cb 100644 --- a/.github/workflows/unit_testing.yaml +++ b/.github/workflows/unit_testing.yaml @@ -1,124 +1,40 @@ name: Unit Testing on: - push: - branches: [master] pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize] jobs: - ensure_tests: - name: Ensure No Missing Tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - name: Checkout repository - - run: | - ./scripts/test_generator.sh - name: Run test generator script - - unit_test_gcc_linux: - name: Linux (GCC=v${{ matrix.version }}, Double=${{ matrix.double-precision }}) - needs: [ ensure_tests ] + unit_test_gcc: strategy: matrix: - version: [9, 10, 11, 12, 13] - double-precision: [ON, OFF] + version: [13, 14] runs-on: ubuntu-latest - env: - CC: gcc-${{ matrix.version }} - CXX: g++-${{ matrix.version }} steps: - uses: actions/checkout@v3 - name: Checkout repository - - uses: egor-tensin/setup-gcc@v1.3 - name: Install GCC - with: - version: ${{ matrix.version }} - run: | - cmake -Bbuild -DFirefly_ENABLE_TESTS=ON -DFirefly_ENABLE_DOUBLE_PRECISION=${{ matrix.double-precision }} -DFirefly_ENABLE_EXAMPLES=ON - cmake --build build - name: Configure and build binaries + sudo cp .github/apt-sources/ubuntu.sources /etc/apt/sources.list.d/ + sudo cp .github/apt-sources/ubuntu-archive-keyring.gpg /usr/share/keyrings/ubuntu-archive-keyring.gpg + sudo apt update + sudo apt install build-essential gcc-${{ matrix.version }} g++-${{ matrix.version }} -y - run: | - ctest --output-on-failure - name: Run tests - working-directory: build/tests - - unit_test_llvm_linux: - name: Linux (LLVM=v${{ matrix.version }}, Double=${{ matrix.double-precision }}) - needs: [ ensure_tests ] + cmake -Bbuild -DCMAKE_C_COMPILER=gcc-${{ matrix.version }} -DCMAKE_CXX_COMPILER=g++-${{ matrix.version }} -DFirefly_ENABLE_TESTS=ON -DFirefly_ENABLE_EXAMPLES=ON + cmake --build build + - run: ctest --test-dir build/tests --output-on-failure + + unit_test_llvm: strategy: matrix: - version: [11, 12, 13, 14, 15, 16] - double-precision: [ON, OFF] + version: [16, 17, 18] runs-on: ubuntu-latest - env: - CC: clang-${{ matrix.version }} - CXX: clang++-${{ matrix.version }} - steps: - - uses: actions/checkout@v3 - name: Checkout repository - - uses: egor-tensin/setup-clang@v1.4 - name: Install LLVM - with: - version: ${{ matrix.version }} - - run: | - cmake -Bbuild -DFirefly_ENABLE_TESTS=ON -DFirefly_ENABLE_DOUBLE_PRECISION=${{ matrix.double-precision }} -DFirefly_ENABLE_EXAMPLES=ON - cmake --build build - name: Configure and build binaries - - run: | - ctest --output-on-failure - name: Run tests - working-directory: build/tests - - unit_test_gcc_macos: - name: MacOS (GCC=v${{ matrix.version }}, Double=${{ matrix.double-precision }}) - needs: [ ensure_tests ] - strategy: - matrix: - version: [10, 11, 12, 13] - double-precision: [ON, OFF] - runs-on: macos-latest - env: - CC: gcc-${{ matrix.version }} - CXX: g++-${{ matrix.version }} - steps: - - uses: actions/checkout@v3 - name: Checkout repository - - run: | - brew install gcc@${{ matrix.version }} - name: Install GCC - - run: | - cmake -Bbuild -DFirefly_ENABLE_TESTS=ON -DFirefly_ENABLE_DOUBLE_PRECISION=${{ matrix.double-precision }} -DFirefly_ENABLE_EXAMPLES=ON - cmake --build build - name: Configure and build binaries - - run: | - ctest --output-on-failure - name: Run tests - working-directory: build/tests - - unit_test_llvm_macos: - name: MacOS (LLVM=v${{ matrix.version }}, Double=${{ matrix.double-precision }}) - needs: [ ensure_tests ] - strategy: - matrix: - version: [12, 13, 14, 15, 16] - double-precision: [ON, OFF] - runs-on: macos-latest - env: - CC: /usr/local/opt/llvm@${{ matrix.version }}/bin/clang - CXX: /usr/local/opt/llvm@${{ matrix.version }}/bin/clang++ steps: - uses: actions/checkout@v3 - name: Checkout repository - - run: | - brew install llvm@${{ matrix.version }} - name: Install LLVM - run: | - cmake -Bbuild -DFirefly_ENABLE_TESTS=ON -DFirefly_ENABLE_DOUBLE_PRECISION=${{ matrix.double-precision }} -DFirefly_ENABLE_EXAMPLES=ON - cmake --build build - name: Configure and build binaries + sudo cp .github/apt-sources/ubuntu.sources /etc/apt/sources.list.d/ + sudo cp .github/apt-sources/ubuntu-archive-keyring.gpg /usr/share/keyrings/ubuntu-archive-keyring.gpg + sudo apt update + sudo apt install build-essential clang-${{ matrix.version }} -y - run: | - ctest --output-on-failure - name: Run tests - working-directory: build/tests + cmake -Bbuild -DCMAKE_C_COMPILER=clang-${{ matrix.version }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.version }} -DFirefly_ENABLE_TESTS=ON -DFirefly_ENABLE_EXAMPLES=ON + cmake --build build + - run: ctest --test-dir build/tests --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index e0a35e6..6f92d50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,31 +1,20 @@ cmake_minimum_required(VERSION 3.10) project(firefly) -set(PROJECT_VERSION 2.1.0) -set(CMAKE_CXX_STANDARD 17) + +set(PROJECT_VERSION 3.0.0) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) + cmake_policy(SET CMP0135 NEW) +cmake_policy(SET CMP0076 NEW) -option(Firefly_ENABLE_DOUBLE_PRECISION "Whether or not to enable double precision. If this is false, float will be used." ON) option(Firefly_ENABLE_EXAMPLES "Whether or not to enable examples" OFF) option(Firefly_ENABLE_TESTS "Whether or not to enable tests" OFF) -if (${Firefly_ENABLE_DOUBLE_PRECISION}) - add_definitions(-DDOUBLE_PRECISION=1) -endif() - include_directories(headers) - -add_library(${PROJECT_NAME} SHARED) - -if(MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) -else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror) -endif() - -add_subdirectory(src) +add_library(${PROJECT_NAME} INTERFACE) if (${Firefly_ENABLE_EXAMPLES}) message(STATUS "Enabling examples build") @@ -50,5 +39,4 @@ if (${Firefly_ENABLE_TESTS}) add_subdirectory(tests) endif() -install(TARGETS ${PROJECT_NAME}) install(DIRECTORY headers/ DESTINATION include) diff --git a/README.md b/README.md index e1eaf84..8096323 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,120 @@ # Firefly -

- LibFirefly - A standalone C++ Library for vectors calculations | Product Hunt -

+Firefly is a header-only standalone C++ vector calculation library. It performs basic arithmetic operations on vectors and introduces utility functions for advanced functionalities. The library supports templates with arithmetic type precision, and currently, it is only available on CPU systems. -This is a standalone C++ vector calculation library. It performs addition, subtraction, scalar multiplication, magnitude, normalisation, dot product, cross product, area of parallelogram, area of triangle, and angle between two vectors. The library supports both float and double precision and cuurrently it is only available on the CPU systems. +The library was designed to help people learn C++ and its concepts. It's a simple implementation, but it's a good place to start if you want to learn more about linear algebra and C++ -The library was designed to help people learn C++ and its concepts. It's a simple implementation, but it's a good place to start if you want to learn more about linear algebra and C++. +## Features -## Supported Platforms and Compilers +Firefly is loaded with a bunch of cool features designed to make your vector calculations a breeze: -**Linux** GCC v9+ or Clang v11+
-**MacOS** GCC v10+ or Clang v12+
+### Core Features + +- **Header-Only:** Simply include the header files and you're good to go! No need to worry about linking libraries. +- **Template Support:** Works seamlessly with various arithmetic types (e.g., int, float, double) and even complex numbers (std::complex). +- **Arithmetic Operations** Perform basic arithmetic operations like addition, subtraction, and scaling on your vectors effortlessly. + +### Advanced Functionalities + +- Geometric Calculations: Compute dot products, cross products (for 3D vectors), and easily normalize vectors. +- Utility Functions: + - Determine the angle between two vectors. + - Check if vectors are parallel, anti-parallel, or orthogonal. + - Calculate the area of a parallelogram or triangle formed by two vectors. + - Project or reject a vector onto or from another vector. + - Compute the Euclidean distance between two vectors. + - Reflect a vector across another vector. + - Rotate a 2D vector by a specified angle. + - Calculate the scalar projection of a vector onto another vector. + - Perform linear interpolation (Lerp) between two vectors. + +## Supported Compilers and Standard + +- GCC v13+ with c++-23 +- LLVM v16+ with c++-23 ## Build and Install -> **Note** Ensure CMake 3.10+ and either Make or Ninja build systems are installed before following the steps. +> [!Note] +> Ensure CMake 3.10+ and either Make or Ninja build systems are installed before following the steps. 1. Clone the repository - ~~~console + + ```console git clone --depth=1 --branch=master https://github.com/tbhaxor/firefly.git firefly - ~~~ + ``` 2. Configure the cmake build - ~~~console + + ```console cmake -Bbuild -DFirefly_ENABLE_EXAMPLES=ON - ~~~ + ```
- | CMake Options | Type | Description | - | :-----------: | :--: | :---------- | - | Firefly_ENABLE_EXAMPLES | Boolean | Adds the `examples/` directory in the compile target. (default: `OFF`) | - | Firefly_ENABLE_DOUBLE_PRECISION | Boolean | Enables `double` type instead of `float` when enabled. (default: `ON`) | - | Firefly_ENABLE_TESTS | Boolean | Download gtest and configures it to enable test. Check [Testing](#testing) section below. (default: `OFF`) | + | CMake Options | Type | Description | + | :---------------------: | :-----: | :--------------------------------------------------------------------------------------------------------- | + | Firefly_ENABLE_EXAMPLES | Boolean | Adds the `examples/` directory in the compile target. (default: `OFF`) | + | Firefly_ENABLE_TESTS | Boolean | Download gtest and configures it to enable test. Check [Testing](#testing) section below. (default: `OFF`) |
- + 3. Build the code and install it - ~~~console - cmake --build build -j `nproc` - sudo cmake --build build --target install/strip - ~~~ + ```console + cmake --build build + sudo cmake --install build + ``` ## Testing By default tests are disabled, you can enable them with `-DFirefly_ENABLE_TESTS` and run using ctest, as shown below. -~~~console +```console cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -DFirefly_ENABLE_TESTS=ON -cmake --build build +cmake --build build ctest --test-dir build/tests --verbose -~~~ - -> **Note** If you are using `ASSERT_NEAR` in your test cases, I advice using `Firefly_TEST_EPSILON` macro defined [here](tests/CMakeLists.txt#L5). +``` ## Example Usage -~~~cpp +```cpp #include -#include - +#include #include int main() { - // define two vectors - std::vector vec1{1, 2, 3, 4}; - std::vector vec2{2, 3, 4, 1}; - - // define firefly vectors from std::vector - Firefly::Vector v1(vec1); - Firefly::Vector v2(vec2); - - // add two firefly vectors and - // returns Firefly::Vector type - auto vec_addition = v1 + v2; - - // print out v1 on - std::cout << v1 << std::endl; // [1, 2, 3, 4] - std::cout << v2 << std::endl; // [2, 3, 4, 1] - std::cout << vec_addition << std::endl; // [3, 5, 7, 5] + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 6}; + auto v3 = v1 + v2; + firefly::vector, 5> v4{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}}; + auto v5 = v4.as_type(); + + std::cout << v1 << std::endl; + std::cout << v2 << std::endl; + std::cout << v3 << std::endl; + std::cout << v4 << std::endl; + std::cout << v5 << std::endl; + std::cout << v1.norm() << std::endl; + std::cout << v1.to_normalized().norm() << std::endl; + std::cout << firefly::utilities::vector::angle_between(v1, v2) << std::endl + << std::boolalpha << firefly::utilities::vector::are_orthogonal(v1, v2) << std::endl + << firefly::utilities::vector::are_parallel(v1, v1) << std::endl + << firefly::utilities::vector::are_anti_parallel(v1, -v1) << std::endl + << std::noboolalpha; } -~~~ - -### Build directly from compiler - -~~~console -g++ main.cpp -lfirefly -o mycode -./mycode -~~~ - -Or with double precision - -~~~console -g++ main.cpp -DDOUBLE_PRECISION=1 -lfirefly -o mycode -./mycode -~~~ - -### Build using CMake - -~~~cmake -target_link_libraries(${PROJECT_NAME} PUBLIC firefly) -~~~ +``` ## Future Plans - Implement Kokkos for HPC platforms +## Scope of Contribution + +- First of all, if you can help me implement [future plans](#future-plans), I would be thankful to you +- Documentation using doxygen. I am new to it, you may help me add more professional documentation +- Add more test cases. I may have left some of the edge cases that you might have come across. + ## Contact -Email: tbhaxor _at_ proton _dot_ me
Twitter: @tbhaxor
-LinkedIn: @tbhaxor +LinkedIn: @tbhaxor diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bb3a752..1492065 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,8 +2,4 @@ cmake_minimum_required(VERSION 3.10) project(example) -set(CXX_STANDARD 17) - -add_executable(${PROJECT_NAME} main.cpp) - -target_link_libraries(${PROJECT_NAME} firefly) \ No newline at end of file +add_executable(example main.cpp) \ No newline at end of file diff --git a/examples/main.cpp b/examples/main.cpp index 4ee429b..5a860c1 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,23 +1,29 @@ -#include +#include +#include #include -#include - -#include "firefly/vector.hpp" int main() { - std::vector vec1{6, 8}; - std::vector vec2{-8, 6}; - - Firefly::Vector v1{vec1}; - Firefly::Vector v2{vec2}; - + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 6}; auto v3 = v1 + v2; + firefly::vector, 5> v4{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}}; + auto v5 = v4.as_type(); std::cout << v1 << std::endl; std::cout << v2 << std::endl; std::cout << v3 << std::endl; + std::cout << v4 << std::endl; + std::cout << v5 << std::endl; + std::cout << v1.norm() << std::endl; + std::cout << v1.to_normalized().norm() << std::endl; + std::cout << firefly::utilities::vector::angle_between(v1, v2) << std::endl + << std::boolalpha << firefly::utilities::vector::are_orthogonal(v1, firefly::vector{-2, 1}) + << std::endl + << firefly::utilities::vector::are_parallel(v1, v1) << std::endl + << firefly::utilities::vector::are_anti_parallel(v1, -v1) << std::endl + << std::noboolalpha; - std::cout << v1.Normalize() << "\t" << v1.Normalize().Magnitude() << "\t" - << std::boolalpha << v1.IsNormalized() << "\t" - << v1.Normalize().IsNormalized() << std::noboolalpha << std::endl; + firefly::vector x{1, 2}; + firefly::vector y{3, 4}; + std::cout << firefly::utilities::vector::projection(v1, v2) << std::endl; } diff --git a/headers/firefly/utilities.hpp b/headers/firefly/utilities.hpp new file mode 100644 index 0000000..4327967 --- /dev/null +++ b/headers/firefly/utilities.hpp @@ -0,0 +1,274 @@ +#include "firefly/vector.hpp" + +namespace firefly::utilities::vector { + +/** + * @brief Calculates the angle between two vectors in radians. + * + * This function computes the angle between two vectors, `v1` and `v2`, of the same length. + * It uses the dot product and the norms of the vectors to determine the cosine of the angle. + * + * @tparam T Type of the first vector. Must be an arithmetic type. + * @tparam U Type of the second vector. Must be an arithmetic type. + * @tparam Length Length of both vectors. + * + * @param v1 First vector of type `firefly::vector`. + * @param v2 Second vector of type `firefly::vector`. + * @param delta A small tolerance value for numerical stability (default is 1e-6). + * + * @throws std::logic_error when norm is zero. This usually happens when zero vector is passed + * @return The angle between the two vectors in radians. The result is clamped between -1.0 and 1.0. + * + * @note Only arithmetic types are allowed for T and U. + */ +template +[[nodiscard]] constexpr auto angle_between(firefly::vector const &v1, firefly::vector const &v2, + double delta = 1e-6) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v, "Only arithmetic types are allowed."); + if (v1.norm() == 0 || v2.norm() == 0) { + return M_PI_2; + } + + auto rad = std::acos(std::clamp((v1.to_normalized() * v2.to_normalized()) / 1, -1.0, 1.0)); + return rad < delta ? 0.0 : rad; +} + +/** + * @brief Checks if two vectors are anti-parallel. + * + * Two vectors are considered anti-parallel if they are in opposite direction. + * + * @tparam T Type of the first vector. + * @tparam U Type of the second vector. + * @tparam Length Length of both vectors. + * + * @param v1 First vector of type `firefly::vector`. + * @param v2 Second vector of type `firefly::vector`. + * + * @throws std::logic_error when norm is zero. This usually happens when zero vector is passed + * @return true if the vectors are anti-parallel, false otherwise. + * + * @note Only arithmetic types are allowed for T and U. + */ +template +bool are_anti_parallel(firefly::vector const &v1, firefly::vector const &v2) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v, "Only arithmetic types are allowed."); + return v1.to_normalized() == -v2.to_normalized(); +} + +/** + * @brief Checks if two vectors are parallel. + * + * Two vectors are considered parallel if they are same or opposite directions. + * + * @tparam T Type of the first vector. + * @tparam U Type of the second vector. + * @tparam Length Length of both vectors. + * + * @param v1 First vector of type `firefly::vector`. + * @param v2 Second vector of type `firefly::vector`. + * + * @throws std::logic_error when norm is zero. This usually happens when zero vector is passed + * @return true if the vectors are parallel, false otherwise. + * + * @note Only arithmetic types are allowed for T and U. + */ +template +bool are_parallel(firefly::vector const &v1, firefly::vector const &v2) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v, "Only arithmetic types are allowed."); + return (v1.to_normalized() == v2.to_normalized()) || are_anti_parallel(v1, v2); +} + +/** + * @brief Checks if two- vectors are orthogonal. + * + * Two vectors are considered orthogonal if the angle between them is close to 90 degrees (or π/2 radians). + * + * @tparam T Type of the first vector. + * @tparam U Type of the second vector. + * @tparam Length Length of both vectors. + + * @param v1 First vector of type `firefly::vector`. + * @param v2 Second vector of type `firefly::vector`. + * @param delta A small tolerance value for numerical stability (default is 1e-6). + + * @throws std::logic_error when norm is zero. This usually happens when zero vector is passed + * @return true if the vectors are orthogonal, false otherwise. + * + * @note Only arithmetic types are allowed for T and U. + */ +template +bool are_orthogonal(firefly::vector const &v1, firefly::vector const &v2, double delta = 1e-6) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v, "Only arithmetic types are allowed."); + return std::fabs(angle_between(v1, v2) - M_PI_2) < delta; +} + +/** + * @brief Computes the area of the parallelogram formed by two vectors. + * + * @tparam T The type of the elements in the first vector. + * @tparam U The type of the elements in the second vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param v1 The first vector. + * @param v2 The second vector. + * + * @return The area of the parallelogram formed by v1 and v2. + * + * @note Only arithmetic types are allowed for T and U. + */ +template +[[nodiscard]] constexpr auto area_parallelogram(firefly::vector const &v1, + firefly::vector const &v2) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v, "Only arithmetic types are allowed."); + return v1.cross(v2).norm(); +} + +/** + * @brief Computes the area of the triangle formed by two vectors. + * + * @tparam T The type of the elements in the first vector. + * @tparam U The type of the elements in the second vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param v1 The first vector. + * @param v2 The second vector. + * + * @return The area of the triangle formed by v1 and v2. + * + * @note Only arithmetic types are allowed for T and U. + */ +template +[[nodiscard]] constexpr auto area_triangle(firefly::vector const &v1, firefly::vector const &v2) { + static_assert(std::is_arithmetic_v && std::is_arithmetic_v, "Only arithmetic types are allowed."); + return area_parallelogram(v1, v2) / 2; +} + +/** + * @brief Projects a source vector onto a target vector. + * + * @tparam T The type of the elements in the source vector. + * @tparam U The type of the elements in the target vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param source_vector The vector being projected. + * @param target_vector The vector onto which the source_vector is projected. + * + * @return The projection of source_vector onto target_vector. + */ +template +[[nodiscard]] constexpr auto projection(firefly::vector const &source_vector, + firefly::vector const &target_vector) { + return target_vector * ((source_vector * target_vector) / (target_vector * target_vector)); +} + +/** + * @brief Rejects a source vector from a target vector. + * + * @tparam T The type of the elements in the source vector. + * @tparam U The type of the elements in the target vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param source_vector The vector being rejected. + * @param target_vector The vector from which the source_vector is rejected. + * + * @return The component of source_vector orthogonal to target_vector. + */ +template +[[nodiscard]] constexpr auto rejection(firefly::vector const &source_vector, + firefly::vector const &target_vector) { + return source_vector - projection(source_vector, target_vector); +} + +/** + * @brief Computes the Euclidean distance between two vectors. + * + * @tparam T The type of the elements in the first vector. + * @tparam U The type of the elements in the second vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param vector_a The first vector. + * @param vector_b The second vector. + * + * @return The distance between vector_a and vector_b. + */ +template +[[nodiscard]] constexpr auto distance(firefly::vector const &vector_a, + firefly::vector const &vector_b) { + return (vector_a - vector_b).norm(); +} + +/** + * @brief Reflects a source vector across a target vector. + * + * @tparam T The type of the elements in the source vector. + * @tparam U The type of the elements in the target vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param source_vector The vector being reflected. + * @param target_vector The vector across which the source_vector is reflected. + * + * @return The reflected vector of source_vector across target_vector. + */ +template +[[nodiscard]] constexpr auto reflection(firefly::vector const &source_vector, + firefly::vector const &target_vector) { + return projection(source_vector, target_vector) * 2 - source_vector; +} + +/** + * @brief Rotates a 2D vector by a given angle (in radians). + * + * @tparam T The type of the elements in the vector. + * + * @param vector The 2D vector to rotate. + * @param angle_rad The angle in radians to rotate the vector. + * + * @return The rotated vector. + */ +template +[[nodiscard]] constexpr auto rotate_2d(firefly::vector const &vector, double angle_rad) { + T x = vector[0] * std::cos(angle_rad) - vector[1] * std::sin(angle_rad); + T y = vector[0] * std::sin(angle_rad) + vector[1] * std::cos(angle_rad); + return firefly::vector{x, y}; +} + +/** + * @brief Computes the scalar projection of a source vector onto a target + * vector. + * + * @tparam T The type of the elements in the source vector. + * @tparam U The type of the elements in the target vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param source_vector The vector being projected. + * @param target_vector The vector onto which the source_vector is projected. + * + * @return The scalar projection of source_vector onto target_vector. + */ +template +[[nodiscard]] constexpr auto scalar_projection(firefly::vector const &source_vector, + firefly::vector const &target_vector) { + return source_vector.dot(target_vector) / target_vector.norm(); +} + +/** + * @brief Performs linear interpolation (Lerp) between two vectors. + * + * @tparam T The type of the elements in the first vector. + * @tparam U The type of the elements in the second vector. + * @tparam Length The number of dimensions of the vectors. + * + * @param vector_a The first vector. + * @param vector_b The second vector. + * @param t The interpolation parameter, typically in the range [0, 1]. + * + * @return The interpolated vector between vector_a and vector_b. + */ +template +[[nodiscard]] constexpr auto lerp(firefly::vector const &vector_a, + firefly::vector const &vector_b, double t) { + return vector_a * (1 - t) + vector_b * t; +} + +} // namespace firefly::utilities::vector \ No newline at end of file diff --git a/headers/firefly/vector.hpp b/headers/firefly/vector.hpp index e47f1a1..cafa90e 100644 --- a/headers/firefly/vector.hpp +++ b/headers/firefly/vector.hpp @@ -1,321 +1,679 @@ #pragma once +#include +#include +#include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include -#ifdef DOUBLE_PRECISION -using Real = double; -#else -using Real = float; -#endif +namespace firefly { -namespace Firefly { +/** + * @brief Customised common_type implementation that handles cases where one or both types may be arithmetic or + * std::complex. + * + * This behaves like std::common_type but adds special handling when either type or both types are std::complex. + * In such cases, the resulting type will be std::complex of the larger type between the arithmetic type and the value + * type of the std::complex. + * + * @tparam T1 First type + * @tparam T2 Second type + */ +template +struct common_type : std::common_type {}; /** - * @class Vector - * @brief Represents a mathematical vector in n-dimensional space. + * @brief Specialisation of common_type for cases where the first type is arithmetic and the second is std::complex. * - * The Vector class provides various operations on vectors, such as addition, - * subtraction, scaling, dot product, cross product, normalization, angle - * calculation, and more. + * This results in a std::complex type where the value type is the larger of the arithmetic type and the complex's value + * type. + * + * @tparam T1 Arithmetic type + * @tparam T2 Value type of the std::complex */ -class Vector { - using VectorType = std::vector; +template +struct common_type> { + /// @brief The resulting type is std::complex with the common type of T1 and T2. + using type = std::complex>; +}; - VectorType m_vec; - Vector(Vector &&) noexcept; +/** + * @brief Specialisation of common_type for cases where the first type is std::complex and the second is arithmetic. + * + * This results in a std::complex type where the value type is the larger of the complex's value type and the arithmetic + * type. + * + * @tparam T1 Value type of the std::complex + * @tparam T2 Arithmetic type + */ +template +struct common_type, T2> { + /// @brief The resulting type is std::complex with the common type of T1 and T2. + using type = std::complex>; +}; -public: - Vector() = delete; +/** + * @brief Specialisation of common_type for cases where both types are std::complex. + * + * This results in a std::complex type where the value type is the larger of the two underlying complex types. + * + * @tparam T1 Value type of the first std::complex + * @tparam T2 Value type of the second std::complex + */ +template +struct common_type, std::complex> { + /// @brief The resulting type is std::complex with the common type of T1 and T2. + using type = std::complex>; +}; - ~Vector() = default; +/** + * @brief Helper alias template for the common_type structure, similar to std::common_type_t. + * + * Provides an alias for the type resulting from the common_type structure. + * + * @tparam T1 First type + * @tparam T2 Second type + */ +template +using common_type_t = typename common_type::type; +/** + * @brief Concept that ensures the type is a complex number type. + * + * This concept checks if the type `T` is a specialization of the `std::complex` template, + * and the underlying value type (i.e., `T::value_type`) must also satisfy the `ArithmeticType` concept. + * It ensures that `T` is a complex number with an arithmetic value type. + * + * @tparam T The type to check. + */ +template +concept complex_type = std::is_same_v, std::complex> && + std::is_arithmetic_v; - /** - * @brief Constructs a vector of a specific size. - * @param _size The size of the vector. - */ - explicit Vector(std::size_t _size); - /** - * @brief Constructs a vector from a given VectorType (std::vector). - * @param _vec The VectorType representing the vector. - */ - explicit Vector(VectorType const &_vec); - /** - * @brief Copy constructor to create a new vector from an existing one. - * @param _fvec The source vector to copy from. - */ - Vector(Vector const &_fvec); +/** + * @brief Concept that ensures the type is either arithmetic or a complex type. + * + * This concept is satisfied if the type `T` is either an arithmetic type + * (such as `int`, `float`, etc.) or a complex type (i.e., a specialization of `std::complex` with an arithmetic value + * type). It is used to ensure that operations work with both real numbers and complex numbers. + * + * @tparam T The type to check. + */ +template +concept vector_type = std::is_arithmetic_v || complex_type; - /** - * @brief Returns a string representation of the vector. - * @return A string representation of the vector. - */ - [[nodiscard]] std::string View() const; - /** - * @brief Overloaded stream insertion operator to print the vector to an - * output stream. - * @param os The output stream. - * @param _fvec The vector to print. - * @return The output stream after printing the vector. - */ - friend std::ostream &operator<<(std::ostream &os, Vector const &_fvec); +/** + * @brief Trait to determine if a type is a std::complex type. + * + * This struct is specialized for std::complex types, returning true_type for complex types + * and false_type for all other types. + * + * @tparam T The type to check. + */ +template +struct is_complex : std::false_type {}; + +/** + * @brief Specialization of is_complex for std::complex types. + * + * This specialization returns true_type for any type that is std::complex. + * + * @tparam T The underlying type of the complex number. + */ +template +struct is_complex> : std::true_type {}; + +/** + * @brief Helper variable template for is_complex. + * + * This variable evaluates to true if T is a std::complex type, otherwise false. + * + * @tparam T The type to check. + */ +template +inline constexpr bool is_complex_v = is_complex::value; + +/** + * @class vector + * @brief Represents a mathematical vector in n-dimensional space. + */ +template // +class vector : private std::array { + +public: + using value_type = T; + using std::array::begin; + using std::array::end; + using std::array::cbegin; + using std::array::cend; + using std::array::rbegin; + using std::array::rend; + using std::array::crbegin; + using std::array::crend; + using std::array::empty; + using std::array::size; + using std::array::operator[]; /** - * @brief Retrieves the value at a specific index in the vector (getter). - * @param idx The index of the element to retrieve. - * @return The value at the specified index. + * @brief Default constructor that initialises all elements of the vector to zero. + * + * This constructor initialises the vector by calling the constructor with a value of 0. */ - [[nodiscard]] Real At(std::size_t idx) const; + [[nodiscard]] constexpr vector() : std::array() { + std::fill(begin(), end(), T{}); + } + /** - * @brief Retrieves a reference to the value at a specific index in the - * vector (setter). - * @param idx The index of the element to retrieve. - * @return A reference to the value at the specified index. - */ - [[nodiscard]] Real &At(std::size_t idx); + * @brief Constructor that initializes the vector using an initializer list. + * + * @param list An initializer list containing the elements to initialize the vector. + * @throw std::out_of_range if the initializer list size exceeds the vector Length. + */ + [[nodiscard]] constexpr vector(std::initializer_list const &list) : std::array() { + if (list.size() > Length) { + throw std::out_of_range("Initializer list size must match vector Length"); + } + std::copy(list.begin(), list.end(), begin()); + } + /** - * @brief Overloaded subscript operator to access the value at a specific - * index (getter). - * @param idx The index of the element to retrieve. - * @return The value at the specified index. + * @brief Constructor that initialises all elements of the vector to a given value. + * + * This constructor fills the vector with the specified value for all elements. + * It leverages `std::fill` to ensure that all entries in the array are initialised with `value`. + * + * @param value The value used to initialise all elements of the vector. */ - [[nodiscard]] Real operator[](std::size_t idx) const; + [[nodiscard]] constexpr vector(T const value) : std::array() { + std::fill(begin(), end(), value); + } + /** - * @brief Overloaded subscript operator to access the value at a specific - * index (setter). - * @param idx The index of the element to retrieve. - * @return A reference to the value at the specified index. - */ - [[nodiscard]] Real &operator[](std::size_t idx); + * @brief Adds two vectors element-wise and returns the result. + * + * This function performs element-wise addition of two vectors and returns a new vector + * of the result. The result's type is deduced using `std::common_type` to ensure compatibility + * between different types of vectors. + * + * @tparam U The type of elements in the other vector being added. + * @param other The vector to add to the current vector. + * @return A new vector containing the element-wise sum of the two vectors. + */ + template + [[nodiscard]] constexpr auto add(vector const &other) const { + vector, Length> result; + std::transform(cbegin(), cend(), other.cbegin(), result.begin(), + [](auto const &a, auto const &b) { return common_type_t(a) + common_type_t(b); }); + + return result; + } /** - * @brief Returns the size (number of elements) of the vector. - * @return The size of the vector. - */ - [[nodiscard]] std::size_t Size() const; + * @brief Adds a scalar to each element of the vector and returns the result. + * + * This function adds a scalar value to every element in the vector and returns a new vector + * containing the result. The result's type is determined using `std::common_type` to ensure + * compatibility between the vector's element type and the scalar type. + * + * @tparam U The type of the scalar value being added. + * @param scalar The scalar value to add to each element of the vector. + * @return A new vector where each element is the result of adding the scalar to the corresponding element. + */ + template + [[nodiscard]] constexpr auto add(U const scalar) const { + vector, Length> result; + std::transform(cbegin(), cend(), result.begin(), + [scalar](auto const &a) { return common_type_t(a) + common_type_t(scalar); }); + return result; + } /** - * @brief Performs element-wise vector addition with another vector. - * @param _fvec The vector to add. - * @return New vector containing the result of the addition. - */ - [[nodiscard]] Vector Add(Vector const &_fvec) const; + * @brief Adds two vectors element-wise using the `+` operator. + * + * This operator overload allows element-wise addition of two vectors using the `+` operator. + * The result is calculated by calling the `add` function. + * + * @tparam U The type of elements in the other vector being added. + * @param other The vector to add to the current vector. + * @return A new vector containing the element-wise sum of the two vectors. + */ + template + [[nodiscard]] constexpr auto operator+(vector const &other) const { + return add(other); + } + /** - * @brief Performs scalar addition (using broadcasting) with a real value. - * @param _scalar The scalar value to add. - * @return New vector containing the result of the addition. - */ - [[nodiscard]] Vector Add(Real const &_scalar) const; + * @brief Adds a scalar to each element of the vector using the `+` operator. + * + * This operator overload adds a scalar to every element of the vector using the `+` operator. + * The result is calculated by calling the `add` function. + * + * @tparam U The type of the scalar value being added. + * @param scalar The scalar value to add to each element of the vector. + * @return A new vector where each element is the result of adding the scalar to the corresponding element. + */ + template + [[nodiscard]] constexpr auto operator+(U const scalar) const { + return add(scalar); + } + /** - * @brief Overloaded operator to perform vector addition. Internally calls - * Add(). - * @param _fvec The vector to add. - * @return New vector containing the result of the addition. - */ - [[nodiscard]] Vector operator+(Vector const &_fvec) const; + * @brief Performs element-wise addition of two vectors using the `+=` operator. + * + * This operator overload allows element-wise addition of two vectors, updating the current vector + * with the result of the addition. + * + * @tparam U The type of elements in the other vector being added. + * @param other The vector to add to the current vector. + * @return A reference to the current vector after the addition. + */ + template + constexpr auto &operator+=(vector const &other) { + std::transform(cbegin(), cend(), other.cbegin(), begin(), + [](auto const &a, auto const &b) { return common_type_t(a) + common_type_t(b); }); + return *this; + } + /** - * @brief Overloaded operator to perform scalar addition. Internally calls - * Add(). - * @param _scalar The scalar value to add. - * @return New vector containing the result of the addition. - */ - [[nodiscard]] Vector operator+(Real const &_scalar) const; + * @brief Adds a scalar to each element of the vector using the `+=` operator. + * + * This operator overload allows a scalar to be added to every element of the vector, updating the current vector + * with the result of the addition. + * + * @tparam U The type of the scalar value being added. + * @param scalar The scalar value to add to each element of the vector. + * @return A reference to the current vector after the addition. + */ + template + constexpr auto &operator+=(U const scalar) { + std::transform(cbegin(), cend(), begin(), + [scalar](T const &el) { return common_type_t(el) + common_type_t(scalar); }); + return *this; + } /** - * @brief Performs element-wise scaling of vector by a real value. - * @param _scalar The scalar value to scale the vector by. - * @return New vector containing the scaled result. - */ - [[nodiscard]] Vector Scale(Real const &_scalar) const; + * @brief Subtracts another vector from this vector. + * + * This function performs element-wise subtraction of another vector from the current vector + * and returns the result by adding the negative of the other vector. + * + * @tparam U The type of elements in the other vector being subtracted. + * @param other The vector to subtract from the current vector. + * @return A new vector containing the result of the element-wise subtraction. + */ + template + [[nodiscard]] constexpr auto subtract(vector const &other) const { + return add(-other); + } /** - * @brief Overloaded operator to perform vector scaling. - * @param _scalar The scalar value to scale the vector by. - * @return New vector containing the scaled result. - */ - [[nodiscard]] Vector operator*(Real const &_scalar) const; + * @brief Subtracts a scalar from each element of the vector. + * + * This function subtracts a scalar value from every element in the vector and returns a new vector + * containing the result. + * + * @tparam U The type of the scalar value being subtracted. + * @param scalar The scalar value to subtract from each element of the vector. + * @return A new vector where each element is the result of subtracting the scalar from the corresponding element. + */ + template + [[nodiscard]] constexpr auto subtract(U const scalar) const { + return add(-scalar); + } /** - * @brief Performs element-wise vector subtraction with another vector. - * @param _fvec The vector to subtract. - * @return New vector containing the result of the subtraction. - */ - [[nodiscard]] Vector Subtract(Vector const &_fvec) const; + * @brief Subtracts another vector from this vector using the `-` operator. + * + * This operator overload allows element-wise subtraction of another vector from the current vector + * using the `-` operator. + * + * @tparam U The type of elements in the other vector being subtracted. + * @param other The vector to subtract from the current vector. + * @return A new vector containing the result of the element-wise subtraction. + */ + template + [[nodiscard]] constexpr auto operator-(vector const &other) const { + return subtract(other); + } + /** - * @brief Performs scalar subtraction (using broadcasting) with a real value. - * @param _scalar The scalar value to subtract. - * @return New vector containing the result of the subtraction. - */ - [[nodiscard]] Vector Subtract(Real const &_scalar) const; + * @brief Subtracts a scalar from each element of the vector using the `-` operator. + * + * This operator overload allows a scalar to be subtracted from every element of the vector + * using the `-` operator. + * + * @tparam U The type of the scalar value being subtracted. + * @param scalar The scalar value to subtract from each element of the vector. + * @return A new vector where each element is the result of subtracting the scalar from the corresponding element. + */ + template + [[nodiscard]] constexpr auto operator-(U const scalar) const { + return subtract(scalar); + } + /** - * @brief Overloaded operator to perform vector subtraction. Internally it - * will use Subtract(). - * @param _fvec The vector to subtract. - * @return New vector containing the result of the subtraction. - */ - [[nodiscard]] Vector operator-(Vector const &_fvec) const; + * @brief Performs element-wise subtraction of another vector using the `-=` operator. + * + * This operator overload allows element-wise subtraction of another vector from the current vector, + * updating the current vector with the result. + * + * @tparam U The type of elements in the other vector being subtracted. + * @param other The vector to subtract from the current vector. + * @return A reference to the current vector after the subtraction. + */ + template + constexpr auto &operator-=(vector const &other) { + std::transform(cbegin(), cend(), other.cbegin(), begin(), + [](auto const &a, auto const &b) { return common_type_t(a) - common_type_t(b); }); + return *this; + } + /** - * @brief Overloaded operator to perform scalar subtraction. Internally it - * will use Subtract(). - * @param _scalar The scalar value to subtract. - * @return New vector containing the result of the subtraction. - */ - [[nodiscard]] Vector operator-(Real const &_scalar) const; + * @brief Subtracts a scalar from each element of the vector using the `-=` operator. + * + * This operator overload allows a scalar to be subtracted from every element of the vector, + * updating the current vector with the result. + * + * @tparam U The type of the scalar value being subtracted. + * @param scalar The scalar value to subtract from each element of the vector. + * @return A reference to the current vector after the subtraction. + */ + template + constexpr auto &operator-=(U const scalar) { + std::transform(cbegin(), cend(), begin(), + [scalar](auto const &el) { return common_type_t(el) - common_type_t(scalar); }); + return *this; + } /** - * @brief Overload unary negation operator to negate the original vector, - * giving anti-parallel vector. + * @brief Calculates the dot product of two vectors. * - * It negates all components of the vector, effectively reversing its - * direction while keeping its magnitude unchanged. The resulting vector - * points in the opposite direction of the original vector. + * This function computes the dot product of the current vector with another vector. + * The dot product is the sum of the products of corresponding elements from both vectors. + * It returns a single scalar value representing the result. * - * For a 2D vector v = (x, y), the negation -v = (-x, -y). - * For a 3D vector v = (x, y, z), the negation -v = (-x, -y, -z). + * The function uses `std::transform_reduce` to efficiently calculate the dot product, + * applying element-wise multiplication and accumulating the result. * - * @note The original Vector object remains unchanged after using this - * operator. + * @tparam U The type of the elements in the other vector. + * @param other The vector with which the dot product is computed. This vector must have the same Length as the + * current vector. + * @return The scalar result of the dot product, with type `common_type_t`. * - * @return A new Vector object with all components negated. - */ - [[nodiscard]] Vector operator-() const; + * @throws std::invalid_argument if the vectors have different sizes (if this condition is checked elsewhere). + */ + template + [[nodiscard]] constexpr auto dot(vector const &other) const { + return std::transform_reduce( + cbegin(), cend(), other.cbegin(), common_type_t(0), std::plus<>(), + [](auto const &a, auto const &b) { return common_type_t(a) * common_type_t(b); }); + } /** - * @brief Calculates the dot product between this vector and another vector. - * @param _fvec The other vector to calculate the dot product with. - * @return The dot product value. - */ - [[nodiscard]] Real Dot(Vector const &_fvec) const; + * @brief Calculates the cross product of two 3D vectors. + * + * This function computes the cross product of the current vector with another vector. + * It is only valid for 3D vectors, and a static assertion is used to enforce this. + * + * @tparam U The type of elements in the other vector. + * @param other The vector to compute the cross product with. + * @return A new vector representing the cross product. + * @throws std::logic_error if the Length of the vector is not 3. + */ + template + [[nodiscard]] constexpr auto cross(vector const &other) const { + static_assert(Length == 3, "Cross product is only allowed for 3D vectors."); + vector, Length> cross; + + cross[0] = (*this)[1] * other[2] - (*this)[2] * other[1]; + cross[1] = (*this)[2] * other[0] - (*this)[0] * other[2]; + cross[2] = (*this)[0] * other[1] - (*this)[1] * other[0]; + + return cross; + } /** - * @brief Calculates the cross product using Levi-Civita symbol between this - * vector and another vector. Both vectors must have three dimensions. + * @brief Scales the vector by a given scalar. * - * @param _fvec The other vector to calculate the cross product with. - * @return New vector containing the result of the - * cross product. - * @see https://en.wikipedia.org/wiki/Levi-Civita_symbol#Vector_cross_product - */ - [[nodiscard]] Vector Cross(Vector const &_fvec) const; + * This function multiplies each element of the vector by a scalar value and returns + * a new vector containing the result. + * + * @tparam U The type of the scalar value. + * @param scalar The scalar value to scale the vector by. + * @return A new vector where each element is scaled by the scalar. + */ + template + [[nodiscard]] constexpr auto scale(U const scalar) const { + vector, Length> result; + std::transform(cbegin(), cend(), result.begin(), + [scalar](auto const &el) { return common_type_t(el) * common_type_t(scalar); }); + return result; + } /** - * @brief Calculates the sum of all elements in the vector. - * @return The sum of all elements. - */ - [[nodiscard]] Real ElemSum() const; + * @brief Calculates the dot product using the `*` operator. + * + * This operator overload allows the use of the `*` operator to calculate the dot product + * of two vectors. + * + * @tparam U The type of elements in the other vector. + * @param other The vector to compute the dot product with. + * @return The dot product as a scalar value. + */ + template + [[nodiscard]] constexpr auto operator*(vector const &other) const { + return dot(other); + } /** - * @brief Calculates the magnitude (Euclidean norm) of the vector. - * @return The magnitude of the vector. - */ - [[nodiscard]] Real Magnitude() const; + * @brief Scales the vector using the `*` operator. + * + * This operator overload allows the use of the `*` operator to scale the vector by a scalar value. + * + * @tparam U The type of the scalar value. + * @param scalar The scalar value to scale the vector by. + * @return A new vector where each element is scaled by the scalar. + */ + template + [[nodiscard]] constexpr auto operator*(U const scalar) const { + return scale(scalar); + } /** - * @brief Scales the vector 1/|v| and returns a new vector that is the - * normalized version (|v| = 1) of this vector. - * @return A new normalized vector. - */ - [[nodiscard]] Vector Normalize() const; + * @brief Scales the vector in place using the `*=` operator. + * + * This operator overload allows in-place scaling of the vector by a scalar value. + * + * @tparam U The type of the scalar value. + * @param scalar The scalar value to scale the vector by. + * @return A reference to the current vector after scaling. + */ + template + constexpr auto &operator*=(U const scalar) { + std::transform(cbegin(), cend(), begin(), + [scalar](auto const &el) { return common_type_t(el) * common_type_t(scalar); }); + return *this; + } /** - * @brief Calculates the angle between this vector and another vector in - * radians. - * @param _fvec The other vector to calculate the angle with. - * @return The angle between the two vectors in radians. - */ - [[nodiscard]] Real AngleWith(Vector const &_fvec) const; + * @brief Scales the vector by the inverse of a scalar using the `/` operator. + * + * This operator overload allows scaling the vector by the reciprocal of a scalar value. + * + * @tparam U The type of the scalar value. + * @param scalar The scalar value to scale the vector by. + * @return A new vector where each element is scaled by the reciprocal of the scalar. + */ + template + [[nodiscard]] constexpr auto operator/(U const scalar) const { + return scale(1 / scalar); + } /** - * @brief Checks if this vector is the same as another vector. + * @brief Scales the vector in place using the `/=` operator. * - * This function compares the current vector with another vector to determine - * if they are the same. The comparison is based on the values of the vector's - * components. + * This operator overload allows in-place scaling of the vector by the reciprocal of a scalar value. * - * @param _fvec Another vector to compare with. - * @return Returns true if the vectors are the same, otherwise false. - */ - [[nodiscard]] bool IsSame(Vector const &_fvec) const; + * @tparam U The type of the scalar value. + * @param scalar The scalar value to scale the vector by. + * @return A reference to the current vector after scaling. + */ + template + constexpr auto &operator/=(U const scalar) { + std::transform(cbegin(), cend(), begin(), + [scalar](T const &el) { return common_type_t(el) * (1 / common_type_t(scalar)); }); + return *this; + } /** - * @brief Compares two vectors for equality. + * @brief Negates the vector. * - * This operator compares the current vector with another vector to check for - * equality. It internally calls the IsSame function to perform the - * comparison. The comparison is based on the values of the vector's - * components. + * This function scales the vector by -1, effectively returning the negated vector. * - * @param _fvec Another vector to compare with. - * @return Returns true if the vectors are equal, otherwise false. - * @see IsSame + * @return A new vector representing the negated value of the current vector. */ - [[nodiscard]] bool operator==(Vector const &_fvec) const; + [[nodiscard]] constexpr auto operator-() const { + return scale(-1); + } /** - * @brief Checks if the vector is sparse. + * @brief Computes the Euclidean magnitude (Length) of the vector. * - * A vector is considered sparse if the count of zero elements is greater that - * the count of non-zero elements. This function calculates the counts of zero - * and non-zero elements in the vector and compares them to determine - * sparsity. + * This function calculates the Euclidean magnitude of the vector. + * For real number vectors, the magnitude is computed as the square root + * of the dot product of the vector with itself. For complex number vectors, + * the magnitude is calculated as the square root of the sum of the squared + * magnitudes of each element (using `std::norm` for the squared modulus). * - * @return Returns true if the vector is sparse, i.e., the count of zero - * elements is greater than or equal to the count of non-zero elements. - */ - [[nodiscard]] bool IsSparse() const; + * If the vector contains complex numbers, the function uses + * `std::transform_reduce` to compute the sum of the squared magnitudes + * of each element. For real numbers, the function uses the `dot` product + * of the vector with itself and returns the square root of that result. + * + * @return The magnitude of the vector as a scalar value. + */ + [[nodiscard]] constexpr auto norm() const { + if constexpr (is_complex_v) { + return std::sqrt(std::transform_reduce(cbegin(), cend(), 0.0, std::plus<>(), [](const auto &val) { + return std::norm(val); // |val|^2 = val * conj(val) + })); + } else { + return std::sqrt(dot(*this)); + } + } /** - * @brief Checks if the vector is a zero vector (all elements are zero). - * @return True if the vector is a zero vector, otherwise false. - */ - [[nodiscard]] bool IsZero() const; + * @brief Normalizes the vector. + * + * This function returns a new vector that is the normalized version of the current vector, + * which has a magnitude of 1. This is done by scaling the vector by the inverse of its magnitude. + * + * @throws std::logic_error when norm is zero. This usually happens when zero vector is passed + * @return A new vector that is the normalized form of the current vector. + */ + [[nodiscard]] constexpr auto to_normalized() const { + auto _norm = norm(); + if (_norm == 0) { + throw std::logic_error("zero norm results in divide by zero"); + } + return scale(1 / norm()); + } /** - * @brief Checks if the vector is a unit vector (normalized with a magnitude - * of 1). - * @return True if the vector is a unit vector, otherwise false. + * @brief Compares two vectors for equality. + * + * This function checks if the current vector is equal to another vector by comparing their elements. + * + * @param other The vector to compare with. + * @return `true` if the vectors are equal, otherwise `false`. */ - [[nodiscard]] bool IsNormalized() const; + [[nodiscard]] constexpr auto is_equal(vector const &other) const { + return std::equal(cbegin(), cend(), other.cbegin()); + } + /** - * @brief Checks if this vector is parallel to another vector. + * @brief Equality operator for vectors. * - * Two vectors are parallel if their direction ratios are proportional. + * This operator overload allows the use of the `==` operator to compare two vectors for equality. * - * @param _fvec Another vector to compare with. - * @return Returns true if the vectors are parallel, otherwise false. - * @note If one of the vectors is the zero vector, they are considered - * parallel. - * @see https://www.cuemath.com/geometry/direction-ratio/ + * @param other The vector to compare with. + * @return `true` if the vectors are equal, otherwise `false`. */ - [[nodiscard]] bool IsParallel(Vector const &_fvec) const; + [[nodiscard]] constexpr auto operator==(vector const &other) const { + return is_equal(other); + } + /** - * @brief Checks if this vector is orthogonal (perpendicular) to another - * vector. + * @brief Converts the vector elements to a different type, handling complex numbers. * - * Two vectors are considered orthogonal if their dot product is 0, - * indicating a 90-degree angle between them. + * If the elements are of type `std::complex`, it multiplies the element by its conjugate and + * then casts the result to the specified type. For other types, it performs a direct cast. * - * @param _fvec The other vector to check for orthogonality. - * @return True if the vectors are orthogonal, otherwise false. + * @tparam AsType The type to which the elements will be cast. + * @return A new vector with elements of the specified type. */ - [[nodiscard]] bool IsOrthogonal(Vector const &_fvec) const; + template + [[nodiscard]] vector constexpr const as_type() const { + vector result; + + std::transform(cbegin(), cend(), result.begin(), [](T el) { + if constexpr (std::is_same_v>) { + return static_cast(el.real() * el.real() + el.imag() * el.imag()); + } else { + return static_cast(el); + } + }); + + return result; + } /** - * @brief Calculates the area of the parallelogram spanned by this vector and - * another vector. - * @param _fvec The other vector forming the parallelogram. - * @return The area of the parallelogram. - * @see https://www.cuemath.com/measurement/area-of-parallelogram/ + * @brief Converts the vector to a string representation. + * + * This function creates a string representation of the vector in the format "[el1, el2, ..., elN]". + * + * @return A string representation of the vector. */ - [[nodiscard]] Real ParallelogramArea(Vector const &_fvec) const; + [[nodiscard]] constexpr std::string view(int precision = 20) const { + bool f_is_first = false; + std::stringstream ss; + + ss << std::setprecision(precision) << "["; + if (!empty()) { + for (auto const &el : *this) { + if (!f_is_first) { + ss << el; + f_is_first = true; + } else { + ss << ", " << el; + } + } + } + ss << "]"; + + return ss.str(); + } + /** - * @brief Calculates the area of the triangle formed by this vector and - * another vector. - * @param _fvec The other vector forming the triangle. - * @return The area of the triangle. + * @brief Stream insertion operator for vectors. + * + * This operator overload allows a vector to be inserted into an output stream, + * outputting the vector in its string representation format. + * + * @param os The output stream. + * @param other The vector to be output. + * @return The output stream with the vector representation. */ - [[nodiscard]] Real TriangleArea(Vector const &_fvec) const; + friend std::ostream &operator<<(std::ostream &os, vector const &other) { + os << other.view(); + return os; + } }; -} // namespace Firefly \ No newline at end of file +} // namespace firefly diff --git a/scripts/test_generator.sh b/scripts/test_generator.sh deleted file mode 100755 index ffb97ee..0000000 --- a/scripts/test_generator.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -git_dir=$(git rev-parse --show-toplevel) - -: "${SRC_DIR_BASE:=src}" -: "${TESTS_DIR_BASE:=tests}" - -src_dir="$git_dir/$SRC_DIR_BASE" -unimplemented_tests='false' - -# shellcheck disable=SC2044 -for src_file in $(find "$src_dir" -type f -name "*.cpp" | sort); do - test_file="${src_file//$SRC_DIR_BASE/$TESTS_DIR_BASE}" - test_name=$(basename "$test_file" | awk -F'.' '{print $1}') - test_suite_name=$(basename "$(dirname "$test_file")") - if [[ "$test_name" == "$test_suite_name" ]]; then - ext_name=$(basename "$test_file" | awk -F'.' '{print $2}') - test_file="${test_file//"${test_name}.${ext_name}"/"constructor.${ext_name}"}" - test_name='constructor' - fi - test_suite_name="$(tr '[:lower:]' '[:upper:]' <<< "${test_suite_name:0:1}")${test_suite_name:1}" - if [[ ! -f "$test_file" ]]; then - mkdir -p "$(dirname "$test_file")" - if [[ -n "$CI" ]]; then - unimplemented_tests='true' - continue - fi - - cat < "$test_file" -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST($test_suite_name, ${test_name}__example) { - ASSERT_TRUE(true); -} -EOF - echo "Created test TEST($test_suite_name, ${test_name}__example) in '$test_file'" - fi -done - -if $unimplemented_tests; then - echo "[x] Some tests are not implemented. Run this script on your local environment to generate it/" - exit 1 -fi \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 0b8f2d9..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ - -add_subdirectory(vector) \ No newline at end of file diff --git a/src/vector/CMakeLists.txt b/src/vector/CMakeLists.txt deleted file mode 100644 index e877afc..0000000 --- a/src/vector/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ - -file(GLOB SOURCES *.cpp) - -target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/src/vector/add.cpp b/src/vector/add.cpp deleted file mode 100644 index dd1ef0d..0000000 --- a/src/vector/add.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -Vector Vector::Add(Vector const &_fvec) const { - if (m_vec.size() != _fvec.m_vec.size()) { - throw std::length_error("Rank of two vectors must be equal."); - } - - Vector sum{m_vec.size()}; - - std::transform(m_vec.cbegin(), m_vec.cend(), _fvec.m_vec.begin(), - sum.m_vec.begin(), std::plus()); - - return sum; -} - -Vector Vector::Add(Real const &_scalar) const { - Vector sum{VectorType(m_vec.size(), _scalar)}; - - std::transform(m_vec.cbegin(), m_vec.cend(), sum.m_vec.cbegin(), - sum.m_vec.begin(), std::plus()); - - return sum; -} - -Vector Vector::operator+(Real const &_scalar) const { - return this->Add(_scalar); -} - -Vector Vector::operator+(Vector const &_fvec) const { return this->Add(_fvec); } -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/angle_with.cpp b/src/vector/angle_with.cpp deleted file mode 100644 index e371c31..0000000 --- a/src/vector/angle_with.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -Real Vector::AngleWith(Vector const &_fvec) const { - if (this->IsZero() || _fvec.IsZero()) { - throw std::logic_error("Can not find angle with zero vector."); - } - - return std::acos(this->Dot(_fvec) / (this->Magnitude() * _fvec.Magnitude())); -} - -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/anti_parallel.cpp b/src/vector/anti_parallel.cpp deleted file mode 100644 index 2d10078..0000000 --- a/src/vector/anti_parallel.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -Vector Vector::operator-() const { return this->Scale(-1); } -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/area.cpp b/src/vector/area.cpp deleted file mode 100644 index cdd3367..0000000 --- a/src/vector/area.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -Real Vector::ParallelogramArea(Vector const &_fvec) const { - return this->Cross(_fvec).Magnitude(); -} - -Real Vector::TriangleArea(Vector const &_fvec) const { - return this->ParallelogramArea(_fvec) / 2; -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/cross.cpp b/src/vector/cross.cpp deleted file mode 100644 index 97682da..0000000 --- a/src/vector/cross.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -Vector Vector::Cross(Vector const &_fvec) const { - if (m_vec.size() != 3 || _fvec.m_vec.size() != 3) { - throw std::length_error("Both vectors must be three-dimensional."); - } - - Vector cross{m_vec.size()}; - - cross[0] = m_vec[1] * _fvec[2] - m_vec[2] * _fvec[1]; - cross[1] = m_vec[2] * _fvec[0] - m_vec[0] * _fvec[2]; - cross[2] = m_vec[0] * _fvec[1] - m_vec[1] * _fvec[0]; - - return cross; -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/dot.cpp b/src/vector/dot.cpp deleted file mode 100644 index ecad639..0000000 --- a/src/vector/dot.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -Real Vector::Dot(Vector const &_fvec) const { - if (m_vec.size() != _fvec.m_vec.size()) { - throw std::length_error("Size of two vectors must be equal."); - } - - return std::inner_product(m_vec.cbegin(), m_vec.cend(), _fvec.m_vec.cbegin(), - static_cast(0)); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/elem_sum.cpp b/src/vector/elem_sum.cpp deleted file mode 100644 index 0f2fe3b..0000000 --- a/src/vector/elem_sum.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { - -Real Vector::ElemSum() const { - return std::accumulate(m_vec.cbegin(), m_vec.cend(), static_cast(0)); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/indexers.cpp b/src/vector/indexers.cpp deleted file mode 100644 index 538dcf4..0000000 --- a/src/vector/indexers.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { - -Real Vector::At(std::size_t idx) const { - // negative error will be underflown because of unsigned - if (idx >= m_vec.size()) { - throw std::length_error( - "Index out of range. Requested index: " + std::to_string(idx) + - ", Vector size: " + std::to_string(m_vec.size())); - } - - return m_vec[idx]; -} - -Real &Vector::At(std::size_t idx) { - // negative error will be underflown because of unsigned - if (idx >= m_vec.size()) { - throw std::length_error( - "Index out of range. Requested index: " + std::to_string(idx) + - ", Vector size: " + std::to_string(m_vec.size())); - } - - return m_vec[idx]; -} - -Real Vector::operator[](std::size_t idx) const { return this->At(idx); } - -Real &Vector::operator[](std::size_t idx) { return this->At(idx); } -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/is_normalized.cpp b/src/vector/is_normalized.cpp deleted file mode 100644 index 5f37b5d..0000000 --- a/src/vector/is_normalized.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -bool Vector::IsNormalized() const { - return static_cast(this->Magnitude()) == static_cast(1); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/is_orthogonal.cpp b/src/vector/is_orthogonal.cpp deleted file mode 100644 index 8838d46..0000000 --- a/src/vector/is_orthogonal.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -bool Vector::IsOrthogonal(Vector const &_fvec) const { - return this->Dot(_fvec) == 0; -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/is_parallel.cpp b/src/vector/is_parallel.cpp deleted file mode 100644 index 71595eb..0000000 --- a/src/vector/is_parallel.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -bool Vector::IsParallel(const Vector &_fvec) const { - if (m_vec.size() != _fvec.m_vec.size()) { - return false; - } - - if (this->IsZero() || _fvec.IsZero() || *this == _fvec) { - return true; - } - - auto const ratio = m_vec[0] / _fvec.m_vec[0]; - for (size_t i = 1; i < m_vec.size(); i++) { - if (m_vec[i] / _fvec.m_vec[i] != ratio) { - return false; - } - } - - return true; -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/is_same.cpp b/src/vector/is_same.cpp deleted file mode 100644 index 8fa4204..0000000 --- a/src/vector/is_same.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -bool Vector::IsSame(Vector const &_fvec) const { - if (m_vec.size() != m_vec.size()) { - return false; - } - - return std::equal(m_vec.cbegin(), m_vec.cend(), _fvec.m_vec.cbegin()); -} - -bool Vector::operator==(Vector const &_fvec) const { - return this->IsSame(_fvec); -} - -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/is_sparse.cpp b/src/vector/is_sparse.cpp deleted file mode 100644 index 6e0ff26..0000000 --- a/src/vector/is_sparse.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -bool Vector::IsSparse() const { - if (m_vec.empty()) { - throw std::length_error("Can't determine sparseness of empty vector"); - } - - size_t const zero_count = std::count_if(m_vec.cbegin(), m_vec.cend(), - [](Real const &v) { return v == 0; }); - - return (m_vec.size() - zero_count) < zero_count; -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/is_zero.cpp b/src/vector/is_zero.cpp deleted file mode 100644 index 878b3c9..0000000 --- a/src/vector/is_zero.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -bool Vector::IsZero() const { - return std::all_of(m_vec.cbegin(), m_vec.cend(), - [](Real const &_el) { return _el == 0; }); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/magnitude.cpp b/src/vector/magnitude.cpp deleted file mode 100644 index d4a1ee6..0000000 --- a/src/vector/magnitude.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -Real Vector::Magnitude() const { - return static_cast(std::sqrt(this->Dot(*this))); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/normalize.cpp b/src/vector/normalize.cpp deleted file mode 100644 index 40cbae5..0000000 --- a/src/vector/normalize.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -Vector Vector::Normalize() const { - if (this->IsZero()) { - throw std::logic_error("Zero vector can't be normalized"); - } - - return this->Scale(1 / this->Magnitude()); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/scale.cpp b/src/vector/scale.cpp deleted file mode 100644 index 741bbfa..0000000 --- a/src/vector/scale.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -Vector Vector::Scale(Real const &_scalar) const { - Vector scaled{VectorType(m_vec.size(), _scalar)}; - - std::transform(m_vec.cbegin(), m_vec.cend(), scaled.m_vec.cbegin(), - scaled.m_vec.begin(), std::multiplies()); - - return scaled; -} - -Vector Vector::operator*(Real const &_scalar) const { - return this->Scale(_scalar); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/size.cpp b/src/vector/size.cpp deleted file mode 100644 index 2f2178e..0000000 --- a/src/vector/size.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { - -std::size_t Vector::Size() const { return m_vec.size(); } -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/subtract.cpp b/src/vector/subtract.cpp deleted file mode 100644 index 27da377..0000000 --- a/src/vector/subtract.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "firefly/vector.hpp" - -namespace Firefly { -Vector Vector::Subtract(Vector const &_fvec) const { return *this + -_fvec; } -Vector Vector::Subtract(Real const &_scalar) const { return *this + -_scalar; } - -Vector Vector::operator-(Vector const &_fvec) const { - return this->Subtract(_fvec); -} -Vector Vector::operator-(Real const &_scalar) const { - return this->Subtract(_scalar); -} -} // namespace Firefly \ No newline at end of file diff --git a/src/vector/vector.cpp b/src/vector/vector.cpp deleted file mode 100644 index 3deaf7f..0000000 --- a/src/vector/vector.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { - -Vector::Vector(std::size_t _size) : m_vec(_size) {} - -Vector::Vector(VectorType const &_vec) : m_vec(_vec) {} - -Vector::Vector(Vector const &_fvec) : m_vec(_fvec.m_vec) {} - -Vector::Vector(Vector &&_fvec) noexcept { m_vec = std::move(_fvec.m_vec); } - -} // namespace Firefly diff --git a/src/vector/view.cpp b/src/vector/view.cpp deleted file mode 100644 index 6c682b8..0000000 --- a/src/vector/view.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include "firefly/vector.hpp" - -namespace Firefly { -std::string Vector::View() const { - std::stringstream ss; - ss << "["; - - if (!m_vec.empty()) { - // Print the first element without a leading comma - ss << m_vec[0]; - - // Print the remaining elements with a leading comma - for (size_t i = 1; i < m_vec.size(); ++i) { - ss << ", " << m_vec[i]; - } - } - - ss << "]"; - return ss.str(); -} - -std::ostream &operator<<(std::ostream &os, Firefly::Vector const &_fvec) { - os << _fvec.View(); - return os; -} - -} // namespace Firefly diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index de13f3f..9c74369 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,8 @@ -file(GLOB_RECURSE SOURCES "*.cpp") +add_executable(FireflyTests) + +add_subdirectory(vector) +add_subdirectory(utilities) -add_executable(FireflyTests ${SOURCES}) target_link_libraries(FireflyTests PRIVATE GTest::gtest_main firefly) -target_compile_definitions(FireflyTests PRIVATE -DFirefly_TEST_EPSILON=1e-3) gtest_discover_tests(FireflyTests) \ No newline at end of file diff --git a/tests/utilities/CMakeLists.txt b/tests/utilities/CMakeLists.txt new file mode 100644 index 0000000..3adc3e8 --- /dev/null +++ b/tests/utilities/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(FireflyTests PRIVATE utilities.cpp) \ No newline at end of file diff --git a/tests/utilities/utilities.cpp b/tests/utilities/utilities.cpp new file mode 100644 index 0000000..fe632b3 --- /dev/null +++ b/tests/utilities/utilities.cpp @@ -0,0 +1,411 @@ +#include "firefly/utilities.hpp" +#include "firefly/vector.hpp" +#include "gtest/gtest.h" + +TEST(utilities, angle_between__normal_vectors_in_radians) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_NEAR(firefly::utilities::vector::angle_between(v1, v2), 0.17985349979247847, 1e-6); +} + +TEST(utilities, angle_between__orthogonal_vectors_is_90) { + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_DOUBLE_EQ(firefly::utilities::vector::angle_between(v1, v2), M_PI_2); +} + +TEST(utilities, angle_between__parallel_vectors_0) { + firefly::vector v1{1, 2}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_NEAR(firefly::utilities::vector::angle_between(v1, v1), 0, 1e-05); +} + +TEST(utilities, are_orthogonal__normal_orthogonal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE(firefly::utilities::vector::are_orthogonal(v1, v2)); +} + +TEST(utilities, are_orthogonal__normal_non_orthogonal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_FALSE(firefly::utilities::vector::are_orthogonal(v1, v2)); +} + +TEST(utilities, are_orthogonal__one_vector_is_zero) { + firefly::vector v1{0, 0}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE(firefly::utilities::vector::are_orthogonal(v1, v2)); +} + +TEST(utilities, are_orthogonal__both_vectors_are_zero) { + firefly::vector v1{0, 0}; + firefly::vector v2{0, 0}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE(firefly::utilities::vector::are_orthogonal(v1, v2)); +} + +TEST(utilities, are_parallel__normal_parallel_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{2, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE(firefly::utilities::vector::are_parallel(v1, v2)); +} + +TEST(utilities, are_parallel__normal_anti_parallel_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-2, -4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE(firefly::utilities::vector::are_parallel(v1, v2)); +} + +TEST(utilities, are_parallel__normal_non_parallel_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_FALSE(firefly::utilities::vector::are_parallel(v1, v2)); +} + +TEST(utilities, are_parallel__one_vector_is_zero_throws_logic_error) { + firefly::vector v1{0, 0}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_THROW({ firefly::utilities::vector::are_parallel(v1, v2); }, std::logic_error); +} + +TEST(utilities, are_parallel__both_vectors_are_zero_throws_logic_error) { + firefly::vector v1{0, 0}; + firefly::vector v2{0, 0}; + + ASSERT_THROW({ firefly::utilities::vector::are_parallel(v1, v2); }, std::logic_error); +} + +TEST(utilities, are_anti_parallel__normal_anti_parallel_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-2, -4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE(firefly::utilities::vector::are_anti_parallel(v1, v2)); +} + +TEST(utilities, are_anti_parallel__normal_parallel_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{2, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_FALSE(firefly::utilities::vector::are_anti_parallel(v1, v2)); +} + +TEST(utilities, are_anti_parallel__normal_non_parallel_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_FALSE(firefly::utilities::vector::are_anti_parallel(v1, v2)); +} + +TEST(utilities, are_anti_parallel__one_vector_is_zero_throws_logic_error) { + firefly::vector v1{0, 0}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_THROW({ firefly::utilities::vector::are_anti_parallel(v1, v2); }, std::logic_error); +} + +TEST(utilities, are_anti_parallel__both_vectors_are_zero_throws_logic_error) { + firefly::vector v1{0, 0}; + firefly::vector v2{0, 0}; + + ASSERT_THROW({ firefly::utilities::vector::are_anti_parallel(v1, v2); }, std::logic_error); +} + +TEST(utilities, area_parallelogram__normal_vectors) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{4, 5, 6}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_DOUBLE_EQ(firefly::utilities::vector::area_parallelogram(v1, v2), 7.3484692283495345); +} + +TEST(utilities, area_parallelogram__parallel_vectors) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{2, 4, 6}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_DOUBLE_EQ(firefly::utilities::vector::area_parallelogram(v1, v2), 0); +} + +TEST(utilities, area_triangle__normal_vectors) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{4, 5, 6}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_DOUBLE_EQ(firefly::utilities::vector::area_triangle(v1, v2), 3.6742346141747673); +} + +TEST(utilities, area_triangle__parallel_vectors) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{2, 4, 6}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(firefly::utilities::vector::area_triangle(v1, v2), 0); +} + +TEST(utilities, projection__normal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto projection_v1_onto_v2 = firefly::utilities::vector::projection(v1, v2); + ASSERT_DOUBLE_EQ(projection_v1_onto_v2[0], 1.32); + ASSERT_DOUBLE_EQ(projection_v1_onto_v2[1], 1.76); +} + +TEST(utilities, projection__orthogonal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto projection_v1_onto_v2 = firefly::utilities::vector::projection(v1, v2); + ASSERT_DOUBLE_EQ(projection_v1_onto_v2[0], 0); + ASSERT_DOUBLE_EQ(projection_v1_onto_v2[1], 0); +} + +TEST(utilities, projection__complex_normal_vectors) { + firefly::vector, 2> v1{std::complex(1, 2), std::complex(3, 4)}; + firefly::vector, 2> v2{std::complex(5, 6), std::complex(7, 8)}; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + auto projection_v1_onto_v2 = firefly::utilities::vector::projection(v1, v2); + ASSERT_DOUBLE_EQ(std::real(projection_v1_onto_v2[0]), 1.7465961665565102); + ASSERT_DOUBLE_EQ(std::imag(projection_v1_onto_v2[0]), 2.6313284864507605); + ASSERT_DOUBLE_EQ(std::real(projection_v1_onto_v2[1]), 2.4627891606080636); + ASSERT_DOUBLE_EQ(std::imag(projection_v1_onto_v2[1]), 3.5230667547918046); +} + +TEST(utilities, rejection__normal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto rejection_v1_onto_v2 = firefly::utilities::vector::rejection(v1, v2); + ASSERT_DOUBLE_EQ(rejection_v1_onto_v2[0], -0.32); + ASSERT_DOUBLE_EQ(rejection_v1_onto_v2[1], 0.24); +} + +TEST(utilities, rejection__orthogonal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto rejection_v1_onto_v2 = firefly::utilities::vector::rejection(v1, v2); + ASSERT_DOUBLE_EQ(rejection_v1_onto_v2[0], 1); + ASSERT_DOUBLE_EQ(rejection_v1_onto_v2[1], 2); +} + +TEST(utilities, rejection__complex_normal_vectors) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + firefly::vector, 2> v2{{5, 6}, {7, 8}}; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + auto rejection_v1_onto_v2 = firefly::utilities::vector::rejection(v1, v2); + ASSERT_DOUBLE_EQ(std::real(rejection_v1_onto_v2[0]), -0.74659616655651018); + ASSERT_DOUBLE_EQ(std::imag(rejection_v1_onto_v2[0]), -0.63132848645076045); + ASSERT_DOUBLE_EQ(std::real(rejection_v1_onto_v2[1]), 0.53721083939193637); + ASSERT_DOUBLE_EQ(std::imag(rejection_v1_onto_v2[1]), 0.47693324520819536); +} + +TEST(utilities, distance__normal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(firefly::utilities::vector::distance(v1, v2), 2.8284271247461903); +} + +TEST(utilities, distance__same_vectors) { + firefly::vector v1{1, 2}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(firefly::utilities::vector::distance(v1, v1), 0); +} + +TEST(utilities, distance__complex_normal_vectors) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + firefly::vector, 2> v2{{5, 6}, {7, 8}}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_DOUBLE_EQ(firefly::utilities::vector::distance(v1, v2), 8); +} + +TEST(utilities, distance__complex_same_vectors) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_DOUBLE_EQ(firefly::utilities::vector::distance(v1, v1), 0); +} + +TEST(utilities, reflection__normal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto reflection_v1_onto_v2 = firefly::utilities::vector::reflection(v1, v2); + ASSERT_DOUBLE_EQ(reflection_v1_onto_v2[0], -1); + ASSERT_DOUBLE_EQ(reflection_v1_onto_v2[1], -2); +} + +TEST(utilities, reflection__orthogonal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto reflection_v1_onto_v2 = firefly::utilities::vector::reflection(v1, v2); + ASSERT_DOUBLE_EQ(reflection_v1_onto_v2[0], -1); + ASSERT_DOUBLE_EQ(reflection_v1_onto_v2[1], -2); +} + +TEST(utilities, reflection__complex_normal_vectors) { + firefly::vector, 2> v1{std::complex(1, 2), std::complex(3, 4)}; + firefly::vector, 2> v2{std::complex(5, 6), std::complex(7, 8)}; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + auto reflection_v1_onto_v2 = firefly::utilities::vector::reflection(v1, v2); + ASSERT_DOUBLE_EQ(std::real(reflection_v1_onto_v2[0]), 2.4931923331130204); + ASSERT_DOUBLE_EQ(std::imag(reflection_v1_onto_v2[0]), 3.2626569729015209); + ASSERT_DOUBLE_EQ(std::real(reflection_v1_onto_v2[1]), 1.9255783212161273); + ASSERT_DOUBLE_EQ(std::imag(reflection_v1_onto_v2[1]), 3.0461335095836093); +} + +TEST(utilities, rotate_2d__normal_vector) { + firefly::vector v1{1, 2}; + + ASSERT_TRUE((std::is_same_v>)); + + auto rotated_v1 = firefly::utilities::vector::rotate_2d(v1, M_PI_2); + ASSERT_DOUBLE_EQ(rotated_v1[0], -2); + ASSERT_DOUBLE_EQ(rotated_v1[1], 1); +} + +TEST(utilities, rotate_2d__zero_vector) { + firefly::vector v1{0, 0}; + + ASSERT_TRUE((std::is_same_v>)); + + auto rotated_v1 = firefly::utilities::vector::rotate_2d(v1, M_PI_2); + ASSERT_DOUBLE_EQ(rotated_v1[0], 0); + ASSERT_DOUBLE_EQ(rotated_v1[1], 0); +} + +TEST(utilities, rotate_2d__complex_vector) { + firefly::vector, 2> v1{std::complex(1, 2), std::complex(3, 4)}; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + auto rotated_v1 = firefly::utilities::vector::rotate_2d(v1, M_PI_2); + ASSERT_DOUBLE_EQ(rotated_v1[0].real(), -3); + ASSERT_DOUBLE_EQ(rotated_v1[0].imag(), -4); + ASSERT_DOUBLE_EQ(rotated_v1[1].real(), 1); + ASSERT_DOUBLE_EQ(rotated_v1[1].imag(), 2); +} + +TEST(utilities, rotate_2d__complex_zero_vector) { + firefly::vector, 2> v1{{0, 0}, {0, 0}}; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + auto rotated_v1 = firefly::utilities::vector::rotate_2d(v1, M_PI_2); + ASSERT_DOUBLE_EQ(rotated_v1[0].real(), 0); + ASSERT_DOUBLE_EQ(rotated_v1[0].imag(), 0); + ASSERT_DOUBLE_EQ(rotated_v1[1].real(), 0); + ASSERT_DOUBLE_EQ(rotated_v1[1].imag(), 0); +} + +TEST(utilities, scalar_projection__normal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v)); + + auto scalar_projection_v1_onto_v2 = firefly::utilities::vector::scalar_projection(v1, v2); + ASSERT_DOUBLE_EQ(scalar_projection_v1_onto_v2, 2.2); +} + +TEST(utilities, scalar_projection__orthogonal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{-8, 4}; + + ASSERT_TRUE((std::is_same_v)); + + auto scalar_projection_v1_onto_v2 = firefly::utilities::vector::scalar_projection(v1, v2); + ASSERT_DOUBLE_EQ(scalar_projection_v1_onto_v2, 0); +} + +TEST(utilities, scalar_projection__complex_normal_vectors) { + firefly::vector, 2> v1{std::complex(1, 2), std::complex(3, 4)}; + firefly::vector, 2> v2{std::complex(5, 6), std::complex(7, 8)}; + + ASSERT_TRUE((std::is_same_v>)); + + auto scalar_projection_v1_onto_v2 = firefly::utilities::vector::scalar_projection(v1, v2); + ASSERT_DOUBLE_EQ(std::real(scalar_projection_v1_onto_v2), -1.364576478442026); + ASSERT_DOUBLE_EQ(std::imag(scalar_projection_v1_onto_v2), 5.1550666963365428); +} + +TEST(utilities, lerp__normal_vectors) { + firefly::vector v1{1, 2}; + firefly::vector v2{3, 4}; + + ASSERT_TRUE((std::is_same_v>)); + + auto lerp_v1_v2 = firefly::utilities::vector::lerp(v1, v2, 0.5); + ASSERT_DOUBLE_EQ(lerp_v1_v2[0], 2); + ASSERT_DOUBLE_EQ(lerp_v1_v2[1], 3); +} + +TEST(utilities, lerp__complex_normal_vectors) { + firefly::vector, 2> v1{std::complex(1, 2), std::complex(3, 4)}; + firefly::vector, 2> v2{std::complex(5, 6), std::complex(7, 8)}; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + auto lerp_v1_v2 = firefly::utilities::vector::lerp(v1, v2, 0.5); + ASSERT_DOUBLE_EQ(lerp_v1_v2[0].real(), 3); + ASSERT_DOUBLE_EQ(lerp_v1_v2[0].imag(), 4); + ASSERT_DOUBLE_EQ(lerp_v1_v2[1].real(), 5); + ASSERT_DOUBLE_EQ(lerp_v1_v2[1].imag(), 6); +} \ No newline at end of file diff --git a/tests/vector/CMakeLists.txt b/tests/vector/CMakeLists.txt new file mode 100644 index 0000000..dd950ad --- /dev/null +++ b/tests/vector/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(FireflyTests PRIVATE add.cpp constructor.cpp misc.cpp product.cpp subtract.cpp) \ No newline at end of file diff --git a/tests/vector/add.cpp b/tests/vector/add.cpp index 52bf889..2812faf 100644 --- a/tests/vector/add.cpp +++ b/tests/vector/add.cpp @@ -1,36 +1,92 @@ -#include - #include "firefly/vector.hpp" #include "gtest/gtest.h" -TEST(Vector, add__different_size__error) { - Firefly::Vector v1{{1, 2, 3}}; - Firefly::Vector v2{{1, 2, 3, 4}}; +TEST(vector, add__different_types_return_common_type_and_performs_element_wise) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{1, 2, 3}; + + auto v3 = v1 + v2; + ASSERT_TRUE((std::is_same_v>)); - ASSERT_THROW({ auto _ = v1 + v2; }, std::length_error); + ASSERT_EQ(v1[0] + v2[0], v3[0]); + ASSERT_EQ(v1[1] + v2[1], v3[1]); + ASSERT_EQ(v1[2] + v2[2], v3[2]); } -TEST(Vector, add__vector) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{5, 6, 7, 8}}; +TEST(vector, add__scalar_broadcasting) { + firefly::vector v1{1, 2, 3}; + auto v2 = v1 + 10; - ASSERT_NO_THROW({ - auto v3 = v1 + v2; + ASSERT_EQ(v1[0] + 10, v2[0]); + ASSERT_EQ(v1[1] + 10, v2[1]); + ASSERT_EQ(v1[2] + 10, v2[2]); +} - ASSERT_EQ(v3.Size(), v1.Size()); - for (auto i = 0; i < v2.Size(); i++) { - ASSERT_EQ(v3.At(i), v1.At(i) + v2.At(i)); - } - }); +TEST(vector, add__assign_updates_current_vector) { + firefly::vector v1{1, 2, 3}; + v1 += 10; + + ASSERT_EQ(v1[0] - 10, 1); + ASSERT_EQ(v1[1] - 10, 2); + ASSERT_EQ(v1[2] - 10, 3); } -TEST(Vector, add__scalar) { - Firefly::Vector v1{{1, 2, 3, 4}}; +TEST(vector, add__works_with_functions) { + firefly::vector v1{1, 2, 3}; + v1 = v1.add(10); + + ASSERT_EQ(v1[0] - 10, 1); + ASSERT_EQ(v1[1] - 10, 2); + ASSERT_EQ(v1[2] - 10, 3); - ASSERT_NO_THROW({ - auto v2 = v1 + 10; + v1 = v1.add(v1); - ASSERT_EQ(v2.Size(), v1.Size()); - ASSERT_EQ(v2.ElemSum(), 40 + v1.ElemSum()); - }); + ASSERT_EQ(v1[0], 22); + ASSERT_EQ(v1[1], 24); + ASSERT_EQ(v1[2], 26); + + auto v2 = v1.add(1.2); + + ASSERT_FLOAT_EQ(v2[0], 23.2); + ASSERT_FLOAT_EQ(v2[1], 25.2); + ASSERT_FLOAT_EQ(v2[2], 27.2); } + +TEST(vector, add__complex_complex_returns_complex) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + firefly::vector, 2> v2{{5, 6}, {7, 8}}; + auto v3 = v1 + v2; + ASSERT_TRUE((std::is_same_v, 2>>)); +} + +TEST(vector, add__complex_complex_different_type_returns_bigger_type) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + firefly::vector, 2> v2{{1, 2}, {3, 4}}; + auto v3 = v1 + v2; + + ASSERT_TRUE((std::is_same_v, 2>>)); +} + +TEST(vector, add__complex_scalar_returns_complex) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + auto v2 = v1 + 1.1f; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + ASSERT_FLOAT_EQ(std::real(v2[0]), 2.1); + ASSERT_FLOAT_EQ(std::imag(v2[0]), 2); + ASSERT_FLOAT_EQ(std::real(v2[1]), 4.1); + ASSERT_FLOAT_EQ(std::imag(v2[1]), 4); +} + +TEST(vector, add__complex_to_scalar_return_complex_of_common_type) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + auto v2 = v1 + 1.1f; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + ASSERT_FLOAT_EQ(std::real(v2[0]), 2.1); + ASSERT_FLOAT_EQ(std::imag(v2[0]), 2); + ASSERT_FLOAT_EQ(std::real(v2[1]), 4.1); + ASSERT_FLOAT_EQ(std::imag(v2[1]), 4); +} \ No newline at end of file diff --git a/tests/vector/angle_with.cpp b/tests/vector/angle_with.cpp deleted file mode 100644 index 174479c..0000000 --- a/tests/vector/angle_with.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "firefly/vector.hpp" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -TEST(Vector, angle_with__zero_vector__error) { - Firefly::Vector v{10}; - - ASSERT_THROW({ auto _ = v.AngleWith(v); }, std::logic_error); -} - -TEST(Vector, angle_with__perp_vector__90) { - Firefly::Vector v1{{3, 4}}; - Firefly::Vector v2{{-4, 3}}; - - ASSERT_NO_THROW( - { ASSERT_NEAR(v1.AngleWith(v2), M_PI_2, Firefly_TEST_EPSILON); }); -} - -TEST(Vector, angle_with__parallel_vector__180) { - Firefly::Vector v{{1, 3}}; - - ASSERT_NO_THROW({ EXPECT_NEAR(v.AngleWith(v), 0, Firefly_TEST_EPSILON); }); -} - -TEST(Vector, angle_with__opp_vector__180) { - using namespace ::testing; - Firefly::Vector v{{3, -1}}; - - ASSERT_NO_THROW({ ASSERT_NEAR(v.AngleWith(-v), M_PI, 0.01); }); -} - -TEST(Vector, angle_with__regular_vector) { - using namespace ::testing; - Firefly::Vector v1{{3, 7}}; - Firefly::Vector v2{{2, 0}}; - - ASSERT_NO_THROW( - { ASSERT_THAT(v1.AngleWith(v2), AnyOf(Ge(-M_PI), Le(M_PI))); }); -} diff --git a/tests/vector/anti_parallel.cpp b/tests/vector/anti_parallel.cpp deleted file mode 100644 index dbdc100..0000000 --- a/tests/vector/anti_parallel.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, subtract__unary__make_opposite) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_NO_THROW({ - ASSERT_TRUE(v.IsParallel(-v)); - ASSERT_NEAR(v.AngleWith(-v), M_PI, Firefly_TEST_EPSILON); - }); -} diff --git a/tests/vector/area.cpp b/tests/vector/area.cpp deleted file mode 100644 index 1680c86..0000000 --- a/tests/vector/area.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, parallelogram_area__non_3d__error) { - Firefly::Vector v{2}; - - ASSERT_THROW({ auto _ = v.ParallelogramArea(v); }, std::length_error); -} - -TEST(Vector, parallelogram_area__orthogonal__rect_area) { - Firefly::Vector v1{{1, 0, 1}}; - Firefly::Vector v2{{0, 1, 0}}; - - ASSERT_NO_THROW({ - ASSERT_EQ(v1.ParallelogramArea(v2), v1.Magnitude() * v2.Magnitude()); - }); -} - -TEST(Vector, parallelogram_area__parallel__0) { - Firefly::Vector v1{{1, 0, 1}}; - auto v2 = v1 * 10; - - ASSERT_NO_THROW({ ASSERT_EQ(v1.ParallelogramArea(v2), 0); }); -} - -TEST(Vector, parallelogram_area__regular__cross_mag) { - Firefly::Vector v1{{1, 0, 3}}; - Firefly::Vector v2{{1, 0, 1}}; - - ASSERT_NO_THROW( - { ASSERT_EQ(v1.ParallelogramArea(v2), v1.Cross(v2).Magnitude()); }); -} - -TEST(Vector, triangle_area__regular__half_parallelogram) { - Firefly::Vector v1{{1, 0, 3}}; - Firefly::Vector v2{{1, 0, 1}}; - - ASSERT_NO_THROW( - { ASSERT_EQ(v1.TriangleArea(v2), v1.ParallelogramArea(v2) / 2); }); -} \ No newline at end of file diff --git a/tests/vector/constructor.cpp b/tests/vector/constructor.cpp index 2e8a36c..35c6ad3 100644 --- a/tests/vector/constructor.cpp +++ b/tests/vector/constructor.cpp @@ -3,29 +3,26 @@ #include "firefly/vector.hpp" #include "gtest/gtest.h" -TEST(Vector, construct__from_size) { - Firefly::Vector v{0}; +TEST(vector, constructor__default_initialize_zeroes) { + firefly::vector v1{}; - ASSERT_EQ(v.Size(), 0); + ASSERT_EQ(v1[0], 0); + ASSERT_EQ(v1[1], 0); + ASSERT_EQ(v1[2], 0); } -TEST(Vector, construct__with_size__zero) { - Firefly::Vector v{4}; - - ASSERT_EQ(v.Size(), 4); - ASSERT_TRUE(v.IsZero()); +TEST(vector, constructor__throws_when_list_length_does_not_match_template) { + ASSERT_THROW(({ firefly::vector v1{1, 2, 3}; }), std::out_of_range); } -TEST(Vector, construct__from_std_vector) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_EQ(v.Size(), 4); - ASSERT_EQ(v.ElemSum(), 10); +TEST(vector, constructor__does_not_throw_with_valid_length) { + ASSERT_NO_THROW(({ firefly::vector v1{1, 2}; })); } -TEST(Vector, construct__from_firefly_vector) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{v1}; +TEST(vector, constructor__value_initizes_with_that_values) { + firefly::vector v1(3); - ASSERT_EQ(v1, v2); -} \ No newline at end of file + ASSERT_EQ(v1[0], 3); + ASSERT_EQ(v1[1], 3); + ASSERT_EQ(v1[2], 3); +} diff --git a/tests/vector/cross.cpp b/tests/vector/cross.cpp deleted file mode 100644 index 6206b33..0000000 --- a/tests/vector/cross.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -#include "firefly/vector.hpp" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -TEST(Vector, cross__non_3d__error) { - Firefly::Vector v{2}; - - ASSERT_THROW({ auto _ = v.Cross(v); }, std::length_error); -} - -TEST(Vector, cross__zero__zero) { - Firefly::Vector v{3}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.Cross(v).IsZero()); }); -} - -TEST(Vector, cross__zero_non_zero__zero) { - Firefly::Vector v1{3}; - Firefly::Vector v2{{1, 2, 3}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v1.Cross(v2).IsZero()); }); -} - -TEST(Vector, cross__orthogonal__mag_product) { - Firefly::Vector v1{{1, 0, 0}}; - Firefly::Vector v2{{0, 1, 1}}; - - ASSERT_NO_THROW({ - auto crossed = v1.Cross(v2); - - ASSERT_TRUE(crossed.IsOrthogonal(v1)); - ASSERT_TRUE(crossed.IsOrthogonal(v2)); - ASSERT_STREQ(crossed.View().c_str(), "[0, -1, 1]"); - ASSERT_EQ(crossed.Magnitude(), v1.Magnitude() * v2.Magnitude()); - }); -} - -TEST(Vector, cross__parallel__zero) { - Firefly::Vector v1{{1, 0, 0}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v1.Cross(v1).IsZero()); }); -} - -TEST(Vector, cross__regular) { - using namespace testing; - Firefly::Vector v1{{1, 3, 1}}; - Firefly::Vector v2{{1, 3, 4}}; - - ASSERT_NO_THROW({ - auto crossed = v1.Cross(v2); - - ASSERT_TRUE(v1.IsOrthogonal(crossed)); - ASSERT_TRUE(v2.IsOrthogonal(crossed)); - ASSERT_FALSE(crossed.IsZero()); - - auto angle = - std::asin(crossed.Magnitude() / (v1.Magnitude() * v2.Magnitude())); - - ASSERT_THAT(angle, AllOf(Gt(-M_PI), Lt(M_PI))); - }); -} \ No newline at end of file diff --git a/tests/vector/dot.cpp b/tests/vector/dot.cpp deleted file mode 100644 index fe6d288..0000000 --- a/tests/vector/dot.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, dot__different_size__error) { - Firefly::Vector v1{10}; - Firefly::Vector v2{5}; - - ASSERT_THROW({ auto _ = v1.Dot(v2); }, std::length_error); -} - -TEST(Vector, dot__zero__zero) { - Firefly::Vector v1{5}; - Firefly::Vector v2{5}; - - ASSERT_EQ(v1.Dot(v2), 0); -} - -TEST(Vector, dot__zero_non_zero__zero) { - Firefly::Vector v1{{1, 2, 3, 4, 5}}; - Firefly::Vector v2{5}; - - ASSERT_EQ(v1.Dot(v2), 0); -} - -TEST(Vector, dot__orthogonal__zero) { - Firefly::Vector v1{{5, -4}}; - Firefly::Vector v2{{4, 5}}; - - ASSERT_EQ(v1.Dot(v2), 0); -} - -TEST(Vector, dot__same__mag_sqed) { - Firefly::Vector v1{{3, -4}}; - - ASSERT_EQ(v1.Dot(v1), std::pow(v1.Magnitude(), 2)); -} - -TEST(Vector, dot__parallel__maq_multiplied) { - Firefly::Vector v1{{3, -4}}; - auto v2 = v1 * 4; - - ASSERT_EQ(v1.Dot(v2), v1.Magnitude() * v2.Magnitude()); -} - -TEST(Vector, dot__regular__non_zero) { - Firefly::Vector v1{{3, -4}}; - Firefly::Vector v2{{4, 5}}; - - ASSERT_EQ(v1.Dot(v2), -8); -} \ No newline at end of file diff --git a/tests/vector/elem_sum.cpp b/tests/vector/elem_sum.cpp deleted file mode 100644 index 06126e4..0000000 --- a/tests/vector/elem_sum.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, elemsum__empty__zero) { - Firefly::Vector v{0}; - - ASSERT_EQ(v.ElemSum(), 0); -} - -TEST(Vector, elemsum__zero__zero) { - Firefly::Vector v{10}; - - ASSERT_EQ(v.ElemSum(), 0); -} - -TEST(Vector, elemsum__non_zero__non_zero) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_EQ(v.ElemSum(), 10); -} diff --git a/tests/vector/indexers.cpp b/tests/vector/indexers.cpp deleted file mode 100644 index 44738d5..0000000 --- a/tests/vector/indexers.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" -#include - -TEST(Vector, indexers__empty__error) { - Firefly::Vector v{0}; - - ASSERT_THROW({ auto _ = v[0]; }, std::length_error); -} - -TEST(Vector, indexers__negative__error) { - Firefly::Vector v{0}; - ASSERT_THROW({ auto _ = v[-1]; }, std::length_error); -} - -TEST(Vector, indexers__valid_input__return_reference) { - Firefly::Vector v{5}; - - ASSERT_NO_THROW({ - ASSERT_EQ(v[3], 0); - v[3] = 10; - ASSERT_EQ(v[3], 10); - }); -} \ No newline at end of file diff --git a/tests/vector/is_normalized.cpp b/tests/vector/is_normalized.cpp deleted file mode 100644 index 5e49710..0000000 --- a/tests/vector/is_normalized.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, is_normalized__empty__false) { - Firefly::Vector v{0}; - - ASSERT_FALSE(v.IsNormalized()); -} - -TEST(Vector, is_normalized__zero__false) { - Firefly::Vector v{10}; - - ASSERT_FALSE(v.IsNormalized()); -} - -TEST(Vector, is_normalized__normalize_check__true) { - Firefly::Vector v{{56, 33}}; - - ASSERT_TRUE(v.Normalize().IsNormalized()); -} - -TEST(Vector, is_normalized__normalized__true) { - Firefly::Vector v{{0.6, 0.8}}; - - ASSERT_TRUE(v.IsNormalized()); -} - -TEST(Vector, is_normalized__basis__true) { - Firefly::Vector v1{{1, 0, 0}}; - Firefly::Vector v2{{0, 1, 0}}; - Firefly::Vector v3{{0, 0, 1}}; - - ASSERT_TRUE(v1.IsNormalized()); - ASSERT_TRUE(v2.IsNormalized()); - ASSERT_TRUE(v3.IsNormalized()); -} diff --git a/tests/vector/is_orthogonal.cpp b/tests/vector/is_orthogonal.cpp deleted file mode 100644 index 3581d71..0000000 --- a/tests/vector/is_orthogonal.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, is_orthogonal__different_size__error) { - Firefly::Vector v1{0}; - Firefly::Vector v2{10}; - - ASSERT_THROW({ auto _ = v1.IsOrthogonal(v2); }, std::length_error); -} - -TEST(Vector, is_orthogonal__empty__true) { - Firefly::Vector v{0}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsOrthogonal(v)); }); -} - -TEST(Vector, is_orthogonal__zero__true) { - Firefly::Vector v{10}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsOrthogonal(v)); }); -} - -TEST(Vector, is_orthogonal__non_zero_zero__true) { - Firefly::Vector v1{3}; - Firefly::Vector v2{{1, 2, 3}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v1.IsOrthogonal(v2)); }); -} - -TEST(Vector, is_orthogonal__valid__true) { - Firefly::Vector v1{{1, 0, 1}}; - Firefly::Vector v2{{{0, 1, 0}}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v1.IsOrthogonal(v2)); }); -} - -TEST(Vector, is_orthogonal__otherwise__false) { - Firefly::Vector v1{{1, 0, 1}}; - Firefly::Vector v2{{{0, 1, 1}}}; - - ASSERT_NO_THROW({ ASSERT_FALSE(v1.IsOrthogonal(v2)); }); -} \ No newline at end of file diff --git a/tests/vector/is_parallel.cpp b/tests/vector/is_parallel.cpp deleted file mode 100644 index 8adef63..0000000 --- a/tests/vector/is_parallel.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, is_parallel__unequal_size__false) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{1, 2, 3}}; - - ASSERT_FALSE(v1.IsParallel(v2)); -} - -TEST(Vector, is_parallel__zero__true) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsParallel(v * 0)); }); -} - -TEST(Vector, is_parallel__same__true) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsParallel(v)); }); -} - -TEST(Vector, is_parallel__opposite__true) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsParallel(-v)); }); -} - -TEST(Vector, is_parallel__otherwise__false) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{2, 2, 3, 4}}; - - ASSERT_NO_THROW({ ASSERT_FALSE(v1.IsParallel(-v2)); }); -} \ No newline at end of file diff --git a/tests/vector/is_same.cpp b/tests/vector/is_same.cpp deleted file mode 100644 index 4458c68..0000000 --- a/tests/vector/is_same.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, is_same__unequal_size__false) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{1, 2, 3}}; - - ASSERT_FALSE(v1 == v2); -} - -TEST(Vector, is_same__all_equal__true) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_TRUE(v == v); -} - -TEST(Vector, is_same__any_one_diff__false) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{1, 2, 3, 5}}; - - ASSERT_FALSE(v1 == v2); -} diff --git a/tests/vector/is_sparse.cpp b/tests/vector/is_sparse.cpp deleted file mode 100644 index 87605e0..0000000 --- a/tests/vector/is_sparse.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, is_sparse__empty__error) { - Firefly::Vector v{0}; - - ASSERT_THROW({ auto _ = v.IsSparse(); }, std::length_error); -} - -TEST(Vector, is_sparse__zero__true) { - Firefly::Vector v{10}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsSparse()); }); -} - -TEST(Vector, is_sparse__all_non_zero__false) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_NO_THROW({ ASSERT_FALSE(v.IsSparse()); }); -} - -TEST(Vector, is_sparse__zeros_gt_non_zero__true) { - Firefly::Vector v{{1, 2, 0, 0, 0}}; - - ASSERT_NO_THROW({ ASSERT_TRUE(v.IsSparse()); }); -} - -TEST(Vector, is_sparse__zeros_lt_non_zero__false) { - Firefly::Vector v{{1, 2, 3, 0, 0}}; - - ASSERT_NO_THROW({ ASSERT_FALSE(v.IsSparse()); }); -} - -TEST(Vector, is_sparse__zeros_eq_non_zero__false) { - Firefly::Vector v{{1, 2, 3, 0, 0, 0}}; - - ASSERT_NO_THROW({ ASSERT_FALSE(v.IsSparse()); }); -} diff --git a/tests/vector/is_zero.cpp b/tests/vector/is_zero.cpp deleted file mode 100644 index fb4418f..0000000 --- a/tests/vector/is_zero.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, is_zero__empty__true) { - Firefly::Vector v{0}; - - ASSERT_TRUE(v.IsZero()); -} - -TEST(Vector, is_zero__zero__true) { - Firefly::Vector v{10}; - - ASSERT_TRUE(v.IsZero()); -} - -TEST(Vector, is_zero__any_non_zero__false) { - Firefly::Vector v{{1, 0, 0}}; - - ASSERT_FALSE(v.IsZero()); -} diff --git a/tests/vector/magnitude.cpp b/tests/vector/magnitude.cpp deleted file mode 100644 index 097a742..0000000 --- a/tests/vector/magnitude.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, magnitude__empty__zero) { - Firefly::Vector v{0}; - - ASSERT_EQ(v.Magnitude(), 0); -} - -TEST(Vector, magnitude__zero__zero) { - Firefly::Vector v{10}; - - ASSERT_EQ(v.Magnitude(), 0); -} - -TEST(Vector, magnitude__non_zero__non_zero) { - Firefly::Vector v{{3, 4}}; - - ASSERT_EQ(v.Magnitude(), 5); -} - -TEST(Vector, magnitude__normalized__1) { - Firefly::Vector v{{0.6, 0.8}}; - - ASSERT_EQ(v.Magnitude(), 1); -} diff --git a/tests/vector/misc.cpp b/tests/vector/misc.cpp new file mode 100644 index 0000000..a0a8c77 --- /dev/null +++ b/tests/vector/misc.cpp @@ -0,0 +1,104 @@ +#include + +#include "firefly/utilities.hpp" +#include "firefly/vector.hpp" +#include "gtest/gtest.h" + +TEST(vector, misc__norm_arithmetic_returns_arithmetic) { + firefly::vector v1{3, 4}; + firefly::vector v2{-3, -4}; + firefly::vector v3{6, 8}; + + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE((std::is_same_v)); + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(v1.norm(), 5); + ASSERT_DOUBLE_EQ(v2.norm(), 5); + ASSERT_DOUBLE_EQ(v3.norm(), 10); +} + +TEST(vector, misc__norm_complex_returns_arithmetic) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(v1.norm(), 5.4772255750516612); +} + +TEST(vector, misc__to_normalized_works_with_arithmetic) { + firefly::vector v1{3, 4}; + auto v2 = v1.to_normalized(); + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(v2[0], v1[0] / v1.norm()); + ASSERT_DOUBLE_EQ(v2[1], v1[1] / v1.norm()); +} + +TEST(vector, misc__to_normalized_norm_is_1) { + firefly::vector v1{3, 4}; + auto v2 = v1.to_normalized(); + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(v2.norm(), 1); +} + +TEST(vector, misc__to_normalized_complex_returns_complex_vector) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + auto v2 = v1.to_normalized(); + + ASSERT_TRUE((std::is_same_v, 2>>)); + + ASSERT_DOUBLE_EQ(std::real(v2[0]), std::real(v1[0]) / v1.norm()); + ASSERT_DOUBLE_EQ(std::imag(v2[0]), std::imag(v1[0]) / v1.norm()); + ASSERT_DOUBLE_EQ(std::real(v2[1]), std::real(v1[1]) / v1.norm()); + ASSERT_DOUBLE_EQ(std::imag(v2[1]), std::imag(v1[1]) / v1.norm()); +} + +TEST(vector, misc__to_normalized_complex_norm_returns_1) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + auto v2 = v1.to_normalized(); + + ASSERT_TRUE((std::is_same_v, 2>>)); + + ASSERT_DOUBLE_EQ(v2.norm(), 1); +} + +TEST(vector, misc__equals_works_with_arithmetic) { + firefly::vector v1{1, 2}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_TRUE(v1 == v1); + ASSERT_FALSE(v1 == -v1); +} + +TEST(vector, misc__equals_works_with_complex) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_TRUE(v1 == v1); + ASSERT_FALSE(v1 == -v1); +} + +// TODO: Add test for as_type + +TEST(vector, misc__view_returns_bracket_enclosed) { + firefly::vector v1{1, 2}; + firefly::vector v2; + + ASSERT_STREQ(v1.view().c_str(), "[1, 2]"); + ASSERT_STREQ(v2.view().c_str(), "[]"); +} + +TEST(firefly, misc__common_type_returns_appropriate_types) { + ASSERT_TRUE((std::is_same_v>, std::complex>)); + ASSERT_TRUE((std::is_same_v>, std::complex>)); + ASSERT_TRUE((std::is_same_v, int>, std::complex>)); + ASSERT_TRUE( + (std::is_same_v, std::complex>, std::complex>)); + ASSERT_TRUE((std::is_same_v, double>)); +} \ No newline at end of file diff --git a/tests/vector/normalize.cpp b/tests/vector/normalize.cpp deleted file mode 100644 index 2851aa6..0000000 --- a/tests/vector/normalize.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, normalize__empty__error) { - Firefly::Vector v{0}; - - ASSERT_THROW({ auto _ = v.Normalize(); }, std::logic_error); -} - -TEST(Vector, normalize__zero__error) { - Firefly::Vector v{10}; - - ASSERT_THROW({ auto _ = v.Normalize(); }, std::logic_error); -} - -TEST(Vector, normalize__regular) { - Firefly::Vector v{{3, 4}}; - - ASSERT_NO_THROW({ - ASSERT_EQ(v.Normalize().Magnitude(), 1); - ASSERT_EQ(v.Normalize(), v * (1 / v.Magnitude())); - }); -} diff --git a/tests/vector/product.cpp b/tests/vector/product.cpp new file mode 100644 index 0000000..f595be6 --- /dev/null +++ b/tests/vector/product.cpp @@ -0,0 +1,238 @@ +#include + +#include "firefly/utilities.hpp" +#include "firefly/vector.hpp" +#include "gtest/gtest.h" + +TEST(vector, scale__zero_returns_zero_vector) { + firefly::vector v1{1, 2, 3, 4}; + v1 *= 0; + + ASSERT_EQ(v1[0], 0); + ASSERT_EQ(v1[1], 0); + ASSERT_EQ(v1[2], 0); + ASSERT_EQ(v1[3], 0); +} + +TEST(vector, scale__type_cast) { + firefly::vector v1{1, 2, 3, 4}; + auto v2 = v1 * 1.5; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(v2[0], 1.5); + ASSERT_DOUBLE_EQ(v2[1], 3); + ASSERT_DOUBLE_EQ(v2[2], 4.5); + ASSERT_DOUBLE_EQ(v2[3], 6); +} + +TEST(vector, scale__no_type_cast_when_inplace) { + firefly::vector v1{1, 2, 3, 4}; + v1 *= 1.5; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_EQ(v1[0], 1); + ASSERT_EQ(v1[1], 3); + ASSERT_EQ(v1[2], 4); + ASSERT_EQ(v1[3], 6); +} + +TEST(vector, scale__with_negative_numbers) { + firefly::vector v1{1, 2, 3, 4}; + auto v2 = v1 * -1.5; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(v2[0], -1.5); + ASSERT_DOUBLE_EQ(v2[1], -3); + ASSERT_DOUBLE_EQ(v2[2], -4.5); + ASSERT_DOUBLE_EQ(v2[3], -6); +} + +TEST(vector, scale__with_neg_1_makes_it_anti_parallel) { + firefly::vector v1{1, 2, 3, 4}; + auto v2 = v1 * -1; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_EQ(v2[0], -1); + ASSERT_EQ(v2[1], -2); + ASSERT_EQ(v2[2], -3); + ASSERT_EQ(v2[3], -4); +} + +TEST(vector, scale__complex_type) { + firefly::vector, 4> v1{{1, 2}, {3, 4}, {5, 6}, {7, 8}}; + auto v2 = v1 * -1.5; + + ASSERT_TRUE((std::is_same_v, 4>>)); + + ASSERT_DOUBLE_EQ(std::real(v2[0]), -1.5); + ASSERT_DOUBLE_EQ(std::imag(v2[0]), -3); + ASSERT_DOUBLE_EQ(std::real(v2[1]), -4.5); + ASSERT_DOUBLE_EQ(std::imag(v2[1]), -6); + ASSERT_DOUBLE_EQ(std::real(v2[2]), -7.5); + ASSERT_DOUBLE_EQ(std::imag(v2[2]), -9); + ASSERT_DOUBLE_EQ(std::real(v2[3]), -10.5); + ASSERT_DOUBLE_EQ(std::imag(v2[3]), -12); +} + +TEST(vector, scale__works_with_unary_operator) { + firefly::vector v1{1, 2, 3, 4}; + + ASSERT_TRUE(firefly::utilities::vector::are_anti_parallel(v1, -v1)); +} + +TEST(vector, scale__reciprocal_works) { + firefly::vector v1{1, 2, 3, 4}; + auto v2 = v1 / v1.norm(); + + ASSERT_DOUBLE_EQ(v2.norm(), 1); +} + +TEST(vector, scale__reciprocal_works_inplace) { + firefly::vector v1{1, 2, 3, 4}; + v1 /= v1.norm(); + ASSERT_DOUBLE_EQ(v1.norm(), 1); +} + +TEST(vector, cross__zero_returns_zero) { + firefly::vector v1; + firefly::vector v2; + auto v3 = v1.cross(v2); + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_EQ(v3[0], 0); + ASSERT_EQ(v3[1], 0); + ASSERT_EQ(v3[2], 0); +} + +TEST(vector, cross__zero_non_zero_returns_zero) { + firefly::vector v1; + firefly::vector v2{1, 2, 3}; + auto v3 = v1.cross(v2); + + ASSERT_TRUE((std::is_same_v>)); + ASSERT_EQ(v3[0], 0); + ASSERT_EQ(v3[1], 0); + ASSERT_EQ(v3[2], 0); +} + +TEST(vector, cross__orthogonal_returns_product_of_magnitude_of_vectors) { + firefly::vector v1{1, 0, 0}; + firefly::vector v2{0, 1, 1}; + auto v3 = v1.cross(v2); + + ASSERT_EQ(v3.norm(), v1.norm() * v2.norm()); + ASSERT_EQ(v3[0], 0); + ASSERT_EQ(v3[1], -1); + ASSERT_EQ(v3[2], 1); +} + +TEST(vector, cross__parallel_vectors_returns_zero) { + firefly::vector v1{1, 0, 0}; + v1 = v1.cross(v1); + + ASSERT_EQ(v1[0], 0); + ASSERT_EQ(v1[1], 0); + ASSERT_EQ(v1[2], 0); +} + +TEST(vector, cross__regular_with_different_types) { + firefly::vector v1{1, 0, 0}; + firefly::vector v2{1, std::sqrt(3), 0}; + auto v3 = v1.cross(v2); + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(v3[0], 0); + ASSERT_DOUBLE_EQ(v3[1], 0); + ASSERT_NEAR(v3[2], 1.7320508075688772, 1.73205); +} + +TEST(vector, dot__regular_returns_scalar) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{4, 5, 6}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_EQ(v1 * v2, 32); +} + +TEST(vector, dot__different_type_auto_cast) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{4, 5, 6}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(v1 * v2, 32); +} + +TEST(vector, dot__complex_scalar_returns_complex) { + firefly::vector v1{1, 2, 3}; + firefly::vector, 3> v2{{1, 2}, {3, 4}, {5, 6}}; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_EQ(std::real(v1 * v2), 22); + ASSERT_EQ(std::imag(v1 * v2), 28); +} + +TEST(vector, dot__complex_scalar_different_type_auto_cast_returns_complex) { + firefly::vector v1{1, 2, 3}; + firefly::vector, 3> v2{{1, 2}, {3, 4}, {5, 6}}; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(std::real(v1 * v2), 22); + ASSERT_DOUBLE_EQ(std::imag(v1 * v2), 28); +} + +TEST(vector, dot__complex_complex_same_type_return_same_complex) { + firefly::vector, 3> v1{{1, 2}, {3, 4}, {5, 6}}; + firefly::vector, 3> v2{{1, 2}, {3, 4}, {5, 6}}; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_EQ(std::real(v1 * v2), -21); + ASSERT_EQ(std::imag(v1 * v2), 88); +} + +TEST(vector, dot__complex_complex_different_types_return_auto_complex) { + firefly::vector, 3> v1{{1, 2}, {3, 4}, {5, 6}}; + firefly::vector, 3> v2{{1, 2}, {3, 4}, {5, 6}}; + + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_DOUBLE_EQ(std::real(v1 * v2), -21); + ASSERT_DOUBLE_EQ(std::imag(v1 * v2), 88); +} + +TEST(vector, dot__orthogonal_returns_zero) { + firefly::vector v1{1, 2, 1}; + firefly::vector v2{1, 2, -5}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_EQ(v1 * v2, 0); +} + +TEST(vector, dot__orthogonal_same_return_magnitude_powered) { + firefly::vector v1{1, 2, 1}; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(v1 * v1, std::pow(v1.norm(), 2)); +} + +TEST(vector, dot__parallel_vectors_return_magnitude_multiplied) { + firefly::vector v1{1, 2, 1}; + auto v2 = v1 * 2; + + ASSERT_TRUE((std::is_same_v)); + + ASSERT_DOUBLE_EQ(v1 * v2, v1.norm() * v2.norm()); + ASSERT_DOUBLE_EQ(v1 * v2, std::pow(v1.norm(), 2) * 2); +} \ No newline at end of file diff --git a/tests/vector/scale.cpp b/tests/vector/scale.cpp deleted file mode 100644 index a5fa15d..0000000 --- a/tests/vector/scale.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, scale__zero__zero_vector) { - Firefly::Vector v{{1, 2, 3, 4}}; - - ASSERT_TRUE((v * 0).IsZero()); -} - -TEST(Vector, scale__any_with_zero_vector__zero_vector) { - Firefly::Vector v{10}; - - ASSERT_TRUE((v * 10).IsZero()); -} - -TEST(Vector, scale__positive) { - Firefly::Vector v1{{1, 2, 3}}; - Firefly::Vector v2{{10, 20, 30}}; - Firefly::Vector v3 = v1 * 10; - - ASSERT_EQ(v3, v2); - ASSERT_EQ(v3.AngleWith(v1), 0); - ASSERT_TRUE(v3.IsParallel(v1)); -} - -TEST(Vector, scale__positive_n__n_times_magnitude) { - Firefly::Vector v{{1, 2, 3}}; - - ASSERT_NEAR((v * 10).Magnitude(), v.Magnitude() * 10, Firefly_TEST_EPSILON); -} - -TEST(Vector, scale__negative__opposite) { - Firefly::Vector v1{{1, 2, 3}}; - Firefly::Vector v2 = v1 * -1; - - ASSERT_EQ(v2, -v1); - ASSERT_NEAR(v1.AngleWith(v2), M_PI, Firefly_TEST_EPSILON); - ASSERT_TRUE(v1.IsParallel(v2)); -} \ No newline at end of file diff --git a/tests/vector/size.cpp b/tests/vector/size.cpp deleted file mode 100644 index 57d2f9b..0000000 --- a/tests/vector/size.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, size__empty__zero) { - Firefly::Vector v{0}; - - ASSERT_EQ(v.Size(), 0); -} - -TEST(Vector, size__non_empty__non_zero) { - Firefly::Vector v{10}; - - ASSERT_EQ(v.Size(), 10); -} diff --git a/tests/vector/subtract.cpp b/tests/vector/subtract.cpp index 068caa0..1dc34d3 100644 --- a/tests/vector/subtract.cpp +++ b/tests/vector/subtract.cpp @@ -1,36 +1,92 @@ -#include -#include - #include "firefly/vector.hpp" #include "gtest/gtest.h" -TEST(Vector, subtract__different_size__error) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{1, 2, 3}}; +TEST(vector, subtract__different_types_return_common_type_and_performs_element_wise) { + firefly::vector v1{1, 2, 3}; + firefly::vector v2{1, 2, 3}; + + auto v3 = v1 - v2; + ASSERT_TRUE((std::is_same_v>)); + + ASSERT_EQ(v1[0] - v2[0], v3[0]); + ASSERT_EQ(v1[1] - v2[1], v3[1]); + ASSERT_EQ(v1[2] - v2[2], v3[2]); +} + +TEST(vector, subtract__scalar_broadcasting) { + firefly::vector v1{1, 2, 3}; + auto v2 = v1 - 10; - ASSERT_THROW({ auto _ = v1 - v2; }, std::length_error); + ASSERT_EQ(v1[0] - 10, v2[0]); + ASSERT_EQ(v1[1] - 10, v2[1]); + ASSERT_EQ(v1[2] - 10, v2[2]); } -TEST(Vector, subtract__same__zero) { - Firefly::Vector v1{{1, 2, 3, 4}}; +TEST(vector, subtract__assign_updates_current_vector) { + firefly::vector v1{1, 2, 3}; + auto v2 = v1 -= 10; + + ASSERT_EQ(v1[0], -9); + ASSERT_EQ(v1[1], -8); + ASSERT_EQ(v1[2], -7); +} + +TEST(vector, subtract__works_with_functions) { + firefly::vector v1{1, 2, 3}; + v1 = v1.subtract(10); + + ASSERT_EQ(v1[0], -9); + ASSERT_EQ(v1[1], -8); + ASSERT_EQ(v1[2], -7); - ASSERT_NO_THROW({ ASSERT_TRUE((v1 - v1).IsZero()); }); + v1 = v1.subtract(v1); + + ASSERT_EQ(v1[0], 0); + ASSERT_EQ(v1[1], 0); + ASSERT_EQ(v1[2], 0); + + auto v2 = v1.subtract(1.2); + + ASSERT_FLOAT_EQ(v2[0], -1.2); + ASSERT_FLOAT_EQ(v2[1], -1.2); + ASSERT_FLOAT_EQ(v2[2], -1.2); +} + +TEST(vector, subtract__complex_complex_returns_complex) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + firefly::vector, 2> v2{{5, 6}, {7, 8}}; + auto v3 = v1 - v2; + ASSERT_TRUE((std::is_same_v, 2>>)); } -TEST(Vector, subtract__different__non_zero) { - Firefly::Vector v1{{1, 2, 3, 4}}; - Firefly::Vector v2{{1, 3, 3, 4}}; +TEST(vector, subtract__complex_complex_different_type_returns_bigger_type) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + firefly::vector, 2> v2{{1, 2}, {3, 4}}; + auto v3 = v1 - v2; - ASSERT_NO_THROW({ ASSERT_FALSE((v1 - v2).IsZero()); }); + ASSERT_TRUE((std::is_same_v, 2>>)); } -TEST(Vector, subtract__scalar__element_wise) { - Firefly::Vector v{{1, 2, 3, 4}}; +TEST(vector, subtract__complex_scalar_returns_complex) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + auto v2 = v1 - 1.1f; - ASSERT_NO_THROW({ - auto sub = v - 1; - for (auto i = 0; i < v.Size(); i++) { - ASSERT_EQ(v[i] - 1, sub[i]); - } - }); + ASSERT_TRUE((std::is_same_v, 2>>)); + + ASSERT_FLOAT_EQ(std::real(v2[0]), -0.1); + ASSERT_FLOAT_EQ(std::imag(v2[0]), 2); + ASSERT_FLOAT_EQ(std::real(v2[1]), 1.9); + ASSERT_FLOAT_EQ(std::imag(v2[1]), 4); } + +TEST(vector, subtract__complex_to_scalar_return_complex_of_common_type) { + firefly::vector, 2> v1{{1, 2}, {3, 4}}; + auto v2 = v1 - 1.1f; + + ASSERT_TRUE((std::is_same_v, 2>>)); + + ASSERT_FLOAT_EQ(std::real(v2[0]), -0.1); + ASSERT_FLOAT_EQ(std::imag(v2[0]), 2); + ASSERT_FLOAT_EQ(std::real(v2[1]), 1.9); + ASSERT_FLOAT_EQ(std::imag(v2[1]), 4); +} \ No newline at end of file diff --git a/tests/vector/view.cpp b/tests/vector/view.cpp deleted file mode 100644 index c9ced74..0000000 --- a/tests/vector/view.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -#include "firefly/vector.hpp" -#include "gtest/gtest.h" - -TEST(Vector, view__empty) { - Firefly::Vector v{0}; - - ASSERT_STREQ(v.View().c_str(), "[]"); -} - -TEST(Vector, view__non_empty) { - Firefly::Vector v{3}; - - ASSERT_STREQ(v.View().c_str(), "[0, 0, 0]"); -}