From 6a5baaa48dac5618b795145c4fd53091fbe73609 Mon Sep 17 00:00:00 2001 From: Karl Zschiebsch Date: Fri, 27 Oct 2023 15:56:56 +0200 Subject: [PATCH] Add value types --- .github/ISSUE_TEMPLATE/bug_report.md | 76 +-- .github/ISSUE_TEMPLATE/feature_request.md | 40 +- .github/dependabot.yml | 22 +- .github/workflows/codeql.yml | 152 ++--- .github/workflows/dependency-review.yml | 40 +- .github/workflows/maven.yml | 62 +- .gitignore | 332 +++++----- LICENSE => LICENSE.md | 42 +- README.md | 96 +-- pom.xml | 108 ++-- qodana.yaml | 31 + src/main/java/org/scvis/Calculator.java | 155 +++-- src/main/java/org/scvis/math/Border2D.java | 176 +++--- src/main/java/org/scvis/math/Border3D.java | 183 +++--- src/main/java/org/scvis/math/Equations.java | 147 +++++ src/main/java/org/scvis/math/Matrix.java | 235 ++++--- src/main/java/org/scvis/math/Point2D.java | 577 +++++++++--------- src/main/java/org/scvis/math/Point3D.java | 546 +++++++++-------- src/main/java/org/scvis/math/Polygon.java | 332 +++++----- src/main/java/org/scvis/math/Rectangle.java | 84 +++ src/main/java/org/scvis/math/Stochastic.java | 102 ++-- src/main/java/org/scvis/math/Triangle.java | 107 ++++ src/main/java/org/scvis/math/Vector.java | 267 ++++---- .../java/org/scvis/parser/ArrayValue.java | 77 +++ .../java/org/scvis/parser/BaseOperator.java | 167 +++-- src/main/java/org/scvis/parser/Brackets.java | 83 ++- src/main/java/org/scvis/parser/Constant.java | 23 - .../org/scvis/parser/EvaluationException.java | 16 +- src/main/java/org/scvis/parser/Function.java | 133 ++-- .../java/org/scvis/parser/NumberValue.java | 52 ++ src/main/java/org/scvis/parser/Operator.java | 135 ++-- .../java/org/scvis/parser/StringValue.java | 41 ++ src/main/java/org/scvis/parser/Token.java | 30 +- .../java/org/scvis/parser/TokenEvaluator.java | 131 ++-- .../java/org/scvis/parser/TokenParser.java | 338 +++++----- src/main/java/org/scvis/parser/Value.java | 91 ++- .../java/org/scvis/math/TestEquations.java | 66 ++ src/test/java/org/scvis/math/TestMatrix.java | 74 +-- src/test/java/org/scvis/math/TestPoint2D.java | 63 +- src/test/java/org/scvis/math/TestPoint3D.java | 45 +- src/test/java/org/scvis/math/TestPolygon.java | 36 +- .../java/org/scvis/math/TestRectangle.java | 53 ++ .../java/org/scvis/math/TestStochastic.java | 100 +-- .../java/org/scvis/math/TestTriangle.java | 48 ++ src/test/java/org/scvis/math/TestVector.java | 108 ++-- .../org/scvis/parser/TestTokenEvaluator.java | 34 +- .../org/scvis/parser/TestTokenParser.java | 30 +- 47 files changed, 3470 insertions(+), 2416 deletions(-) rename LICENSE => LICENSE.md (98%) create mode 100644 qodana.yaml create mode 100644 src/main/java/org/scvis/math/Equations.java create mode 100644 src/main/java/org/scvis/math/Rectangle.java create mode 100644 src/main/java/org/scvis/math/Triangle.java create mode 100644 src/main/java/org/scvis/parser/ArrayValue.java delete mode 100644 src/main/java/org/scvis/parser/Constant.java create mode 100644 src/main/java/org/scvis/parser/NumberValue.java create mode 100644 src/main/java/org/scvis/parser/StringValue.java create mode 100644 src/test/java/org/scvis/math/TestEquations.java create mode 100644 src/test/java/org/scvis/math/TestRectangle.java create mode 100644 src/test/java/org/scvis/math/TestTriangle.java diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..6867cf8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,38 +1,38 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 50dc547..21cf61e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,20 +1,20 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'columns always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'columns always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d4cd657..df01233 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,11 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "maven" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "maven" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f7ca1e8..b50b728 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,76 +1,76 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '39 9 * * 4' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'java' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '39 9 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index fe461b4..1a8ffe2 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,20 +1,20 @@ -# Dependency Review Action -# -# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. -# -# Source repository: https://github.com/actions/dependency-review-action -# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement -name: 'Dependency Review' -on: [pull_request] - -permissions: - contents: read - -jobs: - dependency-review: - runs-on: ubuntu-latest - steps: - - name: 'Checkout Repository' - uses: actions/checkout@v3 - - name: 'Dependency Review' - uses: actions/dependency-review-action@v2 +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v2 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c97b5b3..7ead6cd 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,31 +1,31 @@ -# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Java CI with Maven - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - java-version: '11' - distribution: 'temurin' - cache: maven - - name: Build with Maven - run: mvn install +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Java CI with Maven + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn install diff --git a/.gitignore b/.gitignore index d08798a..79b98f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,167 +1,167 @@ -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* - -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -# https://github.com/takari/maven-wrapper#usage-without-binary-jar -.mvn/wrapper/maven-wrapper.jar - -# Eclipse m2e generated files -# Eclipse Core -.project -# JDT-specific (Eclipse Java Development Tools) -.classpath - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Local History for Visual Studio -.localhistory/ - -# Visual Studio History (VSHistory) files -.vshistory/ - -# VS Code files for those working on multiple tools -.vscode/* -*.code-workspace - -# Local History for Visual Studio Code -.history/ - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - -# JetBrains Rider -*.sln.iml - -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# File-based project format -*.iws - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# VS Code files for those working on multiple tools +.vscode/* +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# File-based project format +*.iws + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser \ No newline at end of file diff --git a/LICENSE b/LICENSE.md similarity index 98% rename from LICENSE rename to LICENSE.md index ae3c414..4cf4c8b 100644 --- a/LICENSE +++ b/LICENSE.md @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2023 Karl Zschiebsch - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2023 Karl Zschiebsch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 1431466..d796f15 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,49 @@ -

ScVis

- -

-Release -CodeQL -Dependency Review -Java CI with Maven -

- -The Calculator Java Library is a simple utility for interpreting and evaluating mathematical expressions provided as input strings. This library allows you to parse and evaluate arithmetic expressions and provides a command-line interface for interactive use. - -## Features - -- Implementation for Vectors, Matrices, Points, and Probabilities -- Tokenizes and evaluates mathematical expressions - -## Getting Started - -### Installation - -Check out [Jitpack](https://jitpack.io/#karl-zschiebsch/scvis) to get it for your Gradle or Maven project. - -### Usage - -Use it to parse expressions. -```java -import org.scvis.Calculator; - -... - -// Interpret and evaluate mathematical expressions ... -Calculator.interpret("sqrt(144)-(2+(8-3)!)"); // Returns [22.0] -Calculator.interpret("bernoulli_pdf(0.5, 2, 2); 10!"); // Returns [0.25, 3628800] - -// ... or create and start the interactive mode for continuous input -Calculator calculator = new Calculator(); -calculator.runAndServe(); -``` - -Perform calculations with vectors, matrices, points, and probabilities. -```java -import org.scvis.math.Vector; - -... - -new Vector(new double[]{2, 4, 7}).add(new Vector(new double[]{8, 3, 2})); // Returns [10, 7, 9] - -new Vector(new double[]{3, 4}).length(); // Returns 5.0 +

ScVis

+ +

+Release +CodeQL +Dependency Review +Java CI with Maven +

+ +The Calculator Java Library is a simple utility for interpreting and evaluating mathematical expressions provided as input strings. This library allows you to parse and evaluate arithmetic expressions and provides a command-line interface for interactive use. + +## Features + +- Implementation for Vectors, Matrices, Points, and Probabilities +- Tokenizes and evaluates mathematical expressions + +## Getting Started + +### Installation + +Check out [Jitpack](https://jitpack.io/#karl-zschiebsch/scvis) to get it for your Gradle or Maven project. + +### Usage + +Use it to parse expressions. +```java +import org.scvis.Calculator; + +... + +// Interpret and evaluate mathematical expressions ... +Calculator.interpret("sqrt(144)-(2+(8-3)!)"); // Returns [22.0] +Calculator.interpret("bernoulli_pdf(0.5, 2, 2); 10!"); // Returns [0.25, 3628800] + +// ... or create and start the interactive mode for continuous input +Calculator calculator = new Calculator(); +calculator.runAndServe(); +``` + +Perform calculations with vectors, matrices, points, and probabilities. +```java +import org.scvis.math.Vector; + +... + +new Vector(new double[]{2, 4, 7}).add(new Vector(new double[]{8, 3, 2})); // Returns [10, 7, 9] + +new Vector(new double[]{3, 4}).length(); // Returns 5.0 ``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index d928cdf..0574a9e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,55 +1,55 @@ - - 4.0.0 - io.scvis - scvis - 0.1.0 - - UTF-8 - 11 - 1.53.0 - ${project.javaVersion} - ${project.javaVersion} - ${project.javaVersion} - - - - - com.google.code.findbugs - jsr305 - 3.0.2 - - - - org.junit.jupiter - junit-jupiter-api - 5.10.0 - test - - - org.junit.jupiter - junit-jupiter-params - 5.10.0 - test - - - - - - - kr.motd.maven - os-maven-plugin - 1.7.1 - - - initialize - - detect - - - - - - + + 4.0.0 + io.scvis + scvis + 0.1.0 + + UTF-8 + 11 + 1.53.0 + ${project.javaVersion} + ${project.javaVersion} + ${project.javaVersion} + + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + + org.junit.jupiter + junit-jupiter-api + 5.10.0 + test + + + org.junit.jupiter + junit-jupiter-params + 5.10.0 + test + + + + + + + kr.motd.maven + os-maven-plugin + 1.7.1 + + + initialize + + detect + + + + + + \ No newline at end of file diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..0df6b7f --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,31 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +projectJDK: temurin-11 #(Applied in CI/CD pipeline) + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-jvm-community:latest diff --git a/src/main/java/org/scvis/Calculator.java b/src/main/java/org/scvis/Calculator.java index 7b95576..ed25134 100644 --- a/src/main/java/org/scvis/Calculator.java +++ b/src/main/java/org/scvis/Calculator.java @@ -1,64 +1,91 @@ -package org.scvis; - -import org.scvis.parser.TokenEvaluator; -import org.scvis.parser.TokenParser; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import java.io.*; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Scanner; - -public class Calculator { - - private final @Nonnull InputStream input; - - private final @Nonnull OutputStream output; - - public Calculator(@Nonnull InputStream input, @Nonnull OutputStream output) { - this.input = input; - this.output = output; - } - - public Calculator() { - this(System.in, System.out); - } - - @CheckReturnValue - @Nonnull - public static List interpret(@Nonnull String line) { - TokenParser parser = new TokenParser(); - parser.tokenize(line); - return new TokenEvaluator(parser.getOperators(), parser.getTokens()).evaluate(); - } - - public void runAndServe() throws IOException { - try(Scanner scanner = new Scanner(input)) { - while (true) { - output.write(">>> ".getBytes()); - String line = scanner.nextLine(); - if (Objects.equals(line, "exit")) break; - try { - Iterator iterator = interpret(line).listIterator(); - StringBuilder builder = new StringBuilder(); - while (iterator.hasNext()) { - builder.append(iterator.next()); - if (iterator.hasNext()) builder.append("; "); - } - builder.append("\n"); - output.write(builder.toString().getBytes()); - } catch (Exception e) { - String msg = e.getClass().getSimpleName() + ": " + e.getMessage() + "\n"; - output.write(msg.getBytes()); - } - } - } - } - - public static void main(String[] args) throws IOException { - Calculator calculator = new Calculator(); - calculator.runAndServe(); - } -} +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package org.scvis; + +import org.scvis.parser.EvaluationException; +import org.scvis.parser.TokenEvaluator; +import org.scvis.parser.TokenParser; +import org.scvis.parser.Value; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.io.*; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Scanner; + +public class Calculator { + + private final @Nonnull InputStream input; + + private final @Nonnull OutputStream output; + + public Calculator(@Nonnull InputStream input, @Nonnull OutputStream output) { + this.input = input; + this.output = output; + } + + public Calculator() { + this(System.in, System.out); + } + + @CheckReturnValue + @Nonnull + public static List> interpret(@Nonnull String line) { + TokenParser parser = new TokenParser(); + parser.tokenize(line); + return new TokenEvaluator(parser.getOperators(), parser.getTokens()).evaluate(); + } + + public void runAndServe() throws IOException { + try (Scanner scanner = new Scanner(input)) { + while (true) { + output.write(">>> ".getBytes()); + String line = scanner.nextLine(); + if (Objects.equals(line, "exit")) break; + try { + Iterator> iterator = interpret(line).listIterator(); + StringBuilder builder = new StringBuilder(); + while (iterator.hasNext()) { + builder.append(iterator.next().get()); + if (iterator.hasNext()) builder.append("; "); + } + builder.append("\n"); + output.write(builder.toString().getBytes()); + } catch (EvaluationException e) { + String msg = e.getClass().getSimpleName() + ": " + e.getMessage() + "\n"; + output.write(msg.getBytes()); + } + } + } + } + + public static void main(String[] args) throws IOException { + Calculator calculator = new Calculator(); + calculator.runAndServe(); + } +} diff --git a/src/main/java/org/scvis/math/Border2D.java b/src/main/java/org/scvis/math/Border2D.java index 9910bdd..e87d020 100644 --- a/src/main/java/org/scvis/math/Border2D.java +++ b/src/main/java/org/scvis/math/Border2D.java @@ -1,89 +1,87 @@ -package org.scvis.math; - -import java.io.Serializable; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; - -/** - * The Border2D interface represents a 2D border or shape in geometry. It - * provides methods for containment, intersection, translation, rotation, and - * obtaining the centroid. - * - * @author karlz - */ -public interface Border2D extends Serializable { - /** - * Checks if the specified point is contained within the border. - * - * @param point2D the 2D vector representing the point - * @return true if the point is contained within the border, false otherwise - */ - @CheckReturnValue - boolean contains(@Nonnull Point2D point2D); - - /** - * Checks if the border intersects with the specified border. - * - * @param border2D the 2D border to check intersection with - * @return true if the borders intersect, false otherwise - */ - @CheckReturnValue - boolean intersects(@Nonnull Border2D border2D); - - /** - * Translates the border by the specified translation values. - * - * @param x the translation along the x-axis - * @param y the translation along the y-axis - * @return a new Border2D object representing the translated border - */ - @CheckReturnValue - @Nonnull - Border2D translate(double x, double y); - - /** - * Translates the border by the specified 2D vector. - * - * @param v the translation vector - * @return a new Border2D object representing the translated border - */ - @CheckReturnValue - @Nonnull - default Border2D translate(@Nonnull Point2D v) { - return translate(v.getX(), v.getY()); - } - - /** - * Rotates the border around the specified center point by the specified angle - * in radians. - * - * @param center the center point of rotation - * @param a the angle of rotation in radians - * @return a new Border2D object representing the rotated border - */ - @CheckReturnValue - @Nonnull - Border2D rotate(@Nonnull Point2D center, double a); - - /** - * Rotates the border around its centroid by the specified angle in radians. - * - * @param a the angle of rotation in radians - * @return a new Border2D object representing the rotated border - */ - @CheckReturnValue - @Nonnull - default Border2D rotate(double a) { - return rotate(centroid(), a); - } - - /** - * Calculates and retrieves the centroid of the border. - * - * @return the centroid of the border as a Vector2D - */ - @CheckReturnValue - @Nonnull - Point2D centroid(); -} +package org.scvis.math; + +import java.io.Serializable; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * The Border2D interface represents a 2D border or shape in geometry. It provides methods for containment, + * intersection, translation, rotation, and obtaining the centroid. + * + * @author karlz + */ +public interface Border2D> extends Serializable { + /** + * Checks if the specified point is contained within the border. + * + * @param point2D the 2D vector representing the point + * @return true if the point is contained within the border, false otherwise + */ + @CheckReturnValue + boolean contains(@Nonnull Point2D point2D); + + /** + * Checks if the border intersects with the specified border. + * + * @param border2D the 2D border to check intersection with + * @return true if the borders intersect, false otherwise + */ + @CheckReturnValue + boolean intersects(@Nonnull B border2D); + + /** + * Translates the border by the specified translation values. + * + * @param x the translation along the x-axis + * @param y the translation along the y-axis + * @return a new Border2D object representing the translated border + */ + @CheckReturnValue + @Nonnull + B translate(double x, double y); + + /** + * Translates the border by the specified 2D vector. + * + * @param v the translation vector + * @return a new Border2D object representing the translated border + */ + @CheckReturnValue + @Nonnull + default B translate(@Nonnull Point2D v) { + return translate(v.getX(), v.getY()); + } + + /** + * Rotates the border around the specified center point by the specified angle in radians. + * + * @param center the center point of rotation + * @param a the angle of rotation in radians + * @return a new Border2D object representing the rotated border + */ + @CheckReturnValue + @Nonnull + B rotate(@Nonnull Point2D center, double a); + + /** + * Rotates the border around its centroid by the specified angle in radians. + * + * @param a the angle of rotation in radians + * @return a new Border2D object representing the rotated border + */ + @CheckReturnValue + @Nonnull + default B rotate(double a) { + return rotate(centroid(), a); + } + + /** + * Calculates and retrieves the centroid of the border. + * + * @return the centroid of the border as a Vector2D + */ + @CheckReturnValue + @Nonnull + Point2D centroid(); +} diff --git a/src/main/java/org/scvis/math/Border3D.java b/src/main/java/org/scvis/math/Border3D.java index 731ec4d..5a9b3de 100644 --- a/src/main/java/org/scvis/math/Border3D.java +++ b/src/main/java/org/scvis/math/Border3D.java @@ -1,93 +1,90 @@ -package org.scvis.math; - -import java.io.Serializable; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; - -/** - * The Border3D interface represents a 3D border or shape in geometry. It - * provides methods for containment, intersection, translation, rotation, and - * obtaining the centroid. - * - * @author karlz - */ -public interface Border3D extends Serializable { - /** - * Checks if the specified point is contained within the border. - * - * @param point3D the 3D vector representing the point - * @return true if the point is contained within the border, false otherwise - */ - @CheckReturnValue - boolean contains(@Nonnull Point3D point3D); - - /** - * Checks if the border intersects with the specified border. - * - * @param border3D the 3D border to check intersection with - * @return true if the borders intersect, false otherwise - */ - @CheckReturnValue - boolean intersects(@Nonnull Border3D border3D); - - /** - * Translates the border by the specified translation values along the x, y, and - * z axes. - * - * @param x the translation along the x-axis - * @param y the translation along the y-axis - * @param z the translation along the z-axis - * @return a new Border3D object representing the translated border - */ - @CheckReturnValue - @Nonnull - Border3D translate(double x, double y, double z); - - /** - * Translates the border by the specified 3D vector. - * - * @param v the translation vector - * @return a new Border3D object representing the translated border - */ - @CheckReturnValue - @Nonnull - default Border3D translate(@Nonnull Point3D v) { - return translate(v.getX(), v.getY(), v.getZ()); - } - - /** - * Rotates the border around the specified center point by the specified angles - * in radians. - * - * @param center the center point of rotation - * @param a the angle of rotation around the x-axis in radians - * @param b the angle of rotation around the y-axis in radians - * @return a new Border3D object representing the rotated border - */ - @CheckReturnValue - @Nonnull - Border3D rotate(@Nonnull Point3D center, double a, double b); - - /** - * Rotates the border around its centroid by the specified angles in radians. - * - * @param a the angle of rotation around the x-axis in radians - * @param b the angle of rotation around the y-axis in radians - * @return a new Border3D object representing the rotated border - */ - @CheckReturnValue - @Nonnull - default Border3D rotate(double a, double b) { - return rotate(centroid(), a, b); - } - - /** - * Calculates and retrieves the centroid of the border. - * - * @return the centroid of the border as a Point3D - */ - @CheckReturnValue - @Nonnull - Point3D centroid(); -} +package org.scvis.math; + +import java.io.Serializable; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * The Border3D interface represents a 3D border or shape in geometry. It provides methods for containment, + * intersection, translation, rotation, and obtaining the centroid. + * + * @author karlz + */ +public interface Border3D> extends Serializable { + /** + * Checks if the specified point is contained within the border. + * + * @param point3D the 3D vector representing the point + * @return true if the point is contained within the border, false otherwise + */ + @CheckReturnValue + boolean contains(@Nonnull Point3D point3D); + + /** + * Checks if the border intersects with the specified border. + * + * @param border3D the 3D border to check intersection with + * @return true if the borders intersect, false otherwise + */ + @CheckReturnValue + boolean intersects(@Nonnull B border3D); + + /** + * Translates the border by the specified translation values along the x, y, and z axes. + * + * @param x the translation along the x-axis + * @param y the translation along the y-axis + * @param z the translation along the z-axis + * @return a new Border3D object representing the translated border + */ + @CheckReturnValue + @Nonnull + B translate(double x, double y, double z); + + /** + * Translates the border by the specified 3D vector. + * + * @param v the translation vector + * @return a new Border3D object representing the translated border + */ + @CheckReturnValue + @Nonnull + default B translate(@Nonnull Point3D v) { + return translate(v.getX(), v.getY(), v.getZ()); + } + + /** + * Rotates the border around the specified center point by the specified angles in radians. + * + * @param center the center point of rotation + * @param a the angle of rotation around the x-axis in radians + * @param b the angle of rotation around the y-axis in radians + * @return a new Border3D object representing the rotated border + */ + @CheckReturnValue + @Nonnull + B rotate(@Nonnull Point3D center, double a, double b); + + /** + * Rotates the border around its centroid by the specified angles in radians. + * + * @param a the angle of rotation around the x-axis in radians + * @param b the angle of rotation around the y-axis in radians + * @return a new Border3D object representing the rotated border + */ + @CheckReturnValue + @Nonnull + default B rotate(double a, double b) { + return rotate(centroid(), a, b); + } + + /** + * Calculates and retrieves the centroid of the border. + * + * @return the centroid of the border as a Point3D + */ + @CheckReturnValue + @Nonnull + Point3D centroid(); +} diff --git a/src/main/java/org/scvis/math/Equations.java b/src/main/java/org/scvis/math/Equations.java new file mode 100644 index 0000000..c29abdc --- /dev/null +++ b/src/main/java/org/scvis/math/Equations.java @@ -0,0 +1,147 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +public final class Equations { + + private Equations() { + throw new UnsupportedOperationException(); + } + + @CheckReturnValue + @Nonnull + public static Vector solve(@Nonnull Matrix variables, @Nonnull Vector values) { + return new Vector(solve(variables.getEntries(), values.getElements())); + } + + @CheckReturnValue + @Nonnull + public static double[] solve(@Nonnull double[][] variables, @Nonnull double[] values) { + int size = values.length; + for (int k = 0; k < size; k++) { + /* find pivot row */ + int max = k; + for (int i = k + 1; i < size; i++) + if (Math.abs(variables[i][k]) > Math.abs(variables[max][k])) + max = i; + + /* swap row in A matrix */ + double[] temp = variables[k]; + variables[k] = variables[max]; + variables[max] = temp; + + /* swap corresponding values in constants matrix */ + double t = values[k]; + values[k] = values[max]; + values[max] = t; + + /* pivot within A and B */ + for (int i = k + 1; i < size; i++) { + double factor = variables[i][k] / variables[k][k]; + values[i] -= factor * values[k]; + for (int j = k; j < size; j++) + variables[i][j] -= factor * variables[k][j]; + } + } + + /* back substitution */ + double[] solution = new double[size]; + for (int i = size - 1; i >= 0; i--) { + double sum = 0.0; + for (int j = i + 1; j < size; j++) + sum += variables[i][j] * solution[j]; + solution[i] = (values[i] - sum) / variables[i][i]; + } + return solution; + } + + /** + * Solves an equation in the form of: + *

+ * + * ax + by = c
dx + ey = f + *
+ * + * @param a the factor ax + * @param b the factor by + * @param c the constant c + * @param d the factor dx + * @param e the factor ey + * @param f the constant f + * @return an array containing x and y, or an empty array if there is no solution + */ + public static double[] solve(double a, double b, double c, double d, double e, double f) { + double y = (d * c - a * f) / (d * b - a * e); + double x = (c - b * y) / a; + if (a * x + b * y == c && d * x + e * y == f) return new double[]{x, y}; + return new double[]{}; + } + + /** + * Solves an equation in the form of:

+ * ax^2 + bx + c = 0 + * + * + * @param a the factor a + * @param b the factor b + * @param c the constant c + * @return an array containing all possible different solutions for x + */ + @CheckReturnValue + @Nonnull + public static double[] solve(double a, double b, double c) { + if (a == 0) return (c == 0 ? new double[]{c} : new double[]{}); + double l = -b / (2 * a); + double r = Math.sqrt(b * b - 4 * a * c) / (2 * a); + if (Double.isNaN(r)) return new double[]{}; + if (r == 0) return new double[]{l}; + return new double[]{l + r, l - r}; + } + + /** + * Solves an equation in the form of: + *

+ * + * x^2 + px + q = 0 + * + * + * @param p the factor p + * @param q the constant q + * @return an array containing all possible different solutions for x + */ + @CheckReturnValue + @Nonnull + public static double[] solve(double p, double q) { + double l = -p / 2; + double r = Math.sqrt(p * p / 4 - q); + if (Double.isNaN(r)) return new double[]{}; + if (r == 0) return new double[]{l}; + return new double[]{l + r, l - r}; + } +} diff --git a/src/main/java/org/scvis/math/Matrix.java b/src/main/java/org/scvis/math/Matrix.java index ce830c5..c665655 100644 --- a/src/main/java/org/scvis/math/Matrix.java +++ b/src/main/java/org/scvis/math/Matrix.java @@ -1,101 +1,134 @@ -package org.scvis.math; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import java.util.Arrays; - -public class Matrix { - - private final @Nonnull double[][] entries; - - private final int columns; - private final int rows; - - public Matrix(int columns, int rows) { - if (columns < 1 || rows < 1) - throw new ArithmeticException("Dimensions must be larger than 0, got m = " + columns + " and n = " + rows); - this.entries = new double[columns][rows]; - this.columns = columns; - this.rows = rows; - } - - public Matrix(@Nonnull double[][] entries) { - this.entries = entries; - this.columns = entries.length; - this.rows = entries[0].length; - } - - @Nonnull - public Matrix add(@Nonnull Matrix other) { - if (columns != other.columns && rows != other.rows) throw new ArithmeticException(); - for (int x = 0; x < columns; x++) { - for (int y = 0; y < rows; y++) { - entries[x][y] += other.entries[x][y]; - } - } - return this; - } - - @Nonnull - public Matrix subtract(@Nonnull Matrix other) { - if (columns != other.columns && rows != other.rows) throw new ArithmeticException(); - for (int x = 0; x < columns; x++) { - for (int y = 0; y < rows; y++) { - entries[x][y] -= other.entries[x][y]; - } - } - return this; - } - - @Nonnull - public Matrix scale(double scale) { - for (int x = 0; x < columns; x++) - for (int y = 0; y < rows; y++) - entries[columns][rows] *= scale; - return this; - } - - @CheckReturnValue - @Nonnull - public Matrix multiply(@Nonnull Matrix other) { - if (columns != other.rows) throw new ArithmeticException(); - Matrix created = new Matrix(rows, other.columns); - for (int x = 0; x < columns; x++) { - for (int y = 0; y < rows; y++) { - double sum = 0; - for (int i = 0; i < columns; i++) { - sum += entries[i][y] * other.entries[x][i]; - } - created.entries[x][y] = sum; - } - } - return created; - } - - @Override - public String toString() { - StringBuilder build = new StringBuilder(); - build.append("["); - for (int x = 0; x < columns; x++) { - build.append("\n\t").append(Arrays.toString(entries[x])); - } - build.append("\n]"); - return build.toString(); - } - - @CheckReturnValue - @Nonnull - public double[][] getEntries() { - return entries; - } - - @CheckReturnValue - public int getColumns() { - return columns; - } - - @CheckReturnValue - public int getRows() { - return rows; - } -} +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.util.Arrays; + +public class Matrix { + + private final @Nonnull double[][] entries; + + private final int columns; + private final int rows; + + public Matrix(int columns, int rows) { + if (columns < 1 || rows < 1) + throw new ArithmeticException("Dimensions must be larger than 0, got m = " + columns + " and n = " + rows); + this.entries = new double[columns][rows]; + this.columns = columns; + this.rows = rows; + } + + public Matrix(@Nonnull double[][] entries) { + this.entries = entries; + this.columns = entries.length; + this.rows = entries[0].length; + } + + @Nonnull + public Matrix add(@Nonnull Matrix other) { + if (columns != other.columns && rows != other.rows) throw new ArithmeticException(); + for (int x = 0; x < columns; x++) { + for (int y = 0; y < rows; y++) { + entries[x][y] += other.entries[x][y]; + } + } + return this; + } + + @Nonnull + public Matrix subtract(@Nonnull Matrix other) { + if (columns != other.columns && rows != other.rows) throw new ArithmeticException(); + for (int x = 0; x < columns; x++) { + for (int y = 0; y < rows; y++) { + entries[x][y] -= other.entries[x][y]; + } + } + return this; + } + + @Nonnull + public Matrix scale(double scale) { + for (int x = 0; x < columns; x++) + for (int y = 0; y < rows; y++) + entries[columns][rows] *= scale; + return this; + } + + @CheckReturnValue + @Nonnull + public Matrix multiply(@Nonnull Matrix other) { + if (columns != other.rows) throw new ArithmeticException(); + Matrix created = new Matrix(rows, other.columns); + for (int x = 0; x < columns; x++) { + for (int y = 0; y < rows; y++) { + double sum = 0; + for (int i = 0; i < columns; i++) { + sum += entries[i][y] * other.entries[x][i]; + } + created.entries[x][y] = sum; + } + } + return created; + } + + @Override + public String toString() { + StringBuilder build = new StringBuilder(); + build.append("["); + for (int x = 0; x < columns; x++) { + build.append("\n\t").append(Arrays.toString(entries[x])); + } + build.append("\n]"); + return build.toString(); + } + + public void setEntry(int c, int r, double v) { + entries[c][r] = v; + } + + @CheckReturnValue + public double getEntry(int c, int r) { + return entries[c][r]; + } + + @CheckReturnValue + @Nonnull + public double[][] getEntries() { + return entries; + } + + @CheckReturnValue + public int getColumns() { + return columns; + } + + @CheckReturnValue + public int getRows() { + return rows; + } +} diff --git a/src/main/java/org/scvis/math/Point2D.java b/src/main/java/org/scvis/math/Point2D.java index 2dc0b91..4112728 100644 --- a/src/main/java/org/scvis/math/Point2D.java +++ b/src/main/java/org/scvis/math/Point2D.java @@ -1,296 +1,283 @@ -package org.scvis.math; - -import java.io.Serializable; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * The Point2D class represents a 2-dimensional point in geometry. - * - * @author karlz - */ -@Immutable -public class Point2D implements Serializable { - - private static final long serialVersionUID = -9019588241960612260L; - - public static final @Nonnull Point2D ZERO = new Point2D(0.0, 0.0); - - /** - * The x coordinate of the vector. - */ - private final double x; - - /** - * The y coordinate of the vector. - */ - private final double y; - - /** - * Creates a Vector2D object with the given x and y coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public Point2D(double x, double y) { - this.x = x; - this.y = y; - } - - /** - * Adds the components of another vector to this vector and returns a new vector - * as the result. - * - * @param v the vector to add - * @return a new vector representing the sum of this vector and the given vector - */ - @CheckReturnValue - @Nonnull - public Point2D add(@Nonnull Point2D v) { - return add(v.getX(), v.getY()); - } - - /** - * Adds the specified coordinates to this vector and returns a new vector as the - * result. - * - * @param x the x-coordinate to add - * @param y the y-coordinate to add - * @return a new vector representing the sum of this vector and the specified - * coordinates - */ - @CheckReturnValue - @Nonnull - public Point2D add(double x, double y) { - return new Point2D(this.x + x, this.y + y); - } - - /** - * Subtracts the components of another vector from this vector and returns a new - * vector as the result. - * - * @param v the vector to subtract - * @return a new vector representing the difference between this vector and the - * given vector - */ - @CheckReturnValue - @Nonnull - public Point2D subtract(@Nonnull Point2D v) { - return subtract(v.getX(), v.getY()); - } - - /** - * Subtracts the given x and y coordinates from this vector and returns a new - * Vector2D object. - * - * @param x the x coordinate to subtract - * @param y the y coordinate to subtract - * @return a new Vector2D object representing the difference of the two vectors - */ - @CheckReturnValue - @Nonnull - public Point2D subtract(double x, double y) { - return new Point2D(this.x - x, this.y - y); - } - - /** - * Multiplies this vector by a scalar value and returns a new vector as the - * result. - * - * @param s the scalar value to multiply by - * @return a new vector representing the product of this vector and the scalar - * value - */ - @CheckReturnValue - @Nonnull - public Point2D multiply(double s) { - return new Point2D(x * s, y * s); - } - - /** - * Calculates the dot product between this vector and another vector. - * - * @param v the vector to calculate the dot product with - * @return the dot product between this vector and the given vector - */ - @CheckReturnValue - public double dotProduct(@Nonnull Point2D v) { - return dotProduct(v.x, v.y); - } - - /** - * Calculates the dot product between this vector and the specified coordinates. - * - * @param x the x-coordinate of the vector - * @param y the y-coordinate of the vector - * @return the dot product between this vector and the specified coordinates - */ - @CheckReturnValue - public double dotProduct(double x, double y) { - return this.x * x + this.y * y; - } - - /** - * Calculates the distance between this vector and the given vector. - * - * @param v the vector to calculate the distance to - * @return the distance between the two vectors - */ - @CheckReturnValue - public double distance(@Nonnull Point2D v) { - return distance(v.x, v.y); - } - - /** - * Calculates the distance between this vector and the vector with the given x - * and y coordinates. - * - * @param x the x coordinate of the other vector - * @param y the y coordinate of the other vector - * @return the distance between the two vectors - */ - public double distance(double x, double y) { - double dx = this.x - x; - double dy = this.y - y; - return Math.sqrt(dx * dx + dy * dy); - } - - /** - * Returns a normalized version of this vector. If the vector is zero based, a - * zero vector is returned. - * - * @return a new vector representing the normalized version of this vector - */ - @CheckReturnValue - @Nonnull - public Point2D normalize() { - double mag = magnitude(); - if (mag == 0.0) { - return ZERO; - } - return new Point2D(x / mag, y / mag); - } - - /** - * Calculates the midpoint between this vector and the specified coordinates. - * - * @param v the vector - * @return a new vector representing the midpoint between this vector and the - * specified coordinates - */ - @CheckReturnValue - @Nonnull - public Point2D midpoint(@Nonnull Point2D v) { - return midpoint(v.x, v.y); - } - - /** - * Calculates the midpoint between this vector and the vector with the given x - * and y coordinates. - * - * @param x the x coordinate of the other vector - * @param y the y coordinate of the other vector - * @return the midpoint between the two vectors - */ - @CheckReturnValue - @Nonnull - public Point2D midpoint(double x, double y) { - return new Point2D(x + (this.x - x) / 2.0, - y + (this.y - y) / 2.0); - } - - /** - * Calculates the angle between this vector and another vector. - * - * @param v the vector to calculate the angle to - * @return the angle between this vector and the given vector - */ - @CheckReturnValue - public double angle(@Nonnull Point2D v) { - return angle(v.getX(), v.getY()); - } - - /** - * Calculates the angle between this vector and the specified coordinates. - * - * @param x the x-coordinate of the vector - * @param y the y-coordinate of the vector - * @return the angle between this vector and the specified coordinates - */ - @CheckReturnValue - public double angle(double x, double y) { - return Math.atan2(this.y - y, this.x - x); - } - - /** - * Rotates this vector by the specified angle and returns a new rotated vector. - * - * @param a the angle to rotate by, in radians - * @return a new vector representing the result of rotating this vector by the - * specified angle - */ - @CheckReturnValue - @Nonnull - public Point2D rotate(double a) { - return new Point2D(Math.cos(a) * x - Math.sin(a) * y, Math.sin(a) * x + Math.cos(a) * y); - } - - /** - * Calculates the magnitude (length) of this vector. - * - * @return the magnitude of the vector - */ - @CheckReturnValue - public double magnitude() { - return Math.sqrt(x * x + y * y); - } - - /** - * Returns the x coordinate of this vector. - * - * @return the x coordinate - */ - @CheckReturnValue - public double getX() { - return x; - } - - /** - * Returns the x coordinate of this vector. - * - * @return the x coordinate - */ - @CheckReturnValue - public double getY() { - return y; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj == null) - return false; - if (!(obj instanceof Point2D)) - return false; - if (obj == this) - return true; - Point2D vec = (Point2D) obj; - return x == vec.x && y == vec.y; - } - - private transient int hash; - - @Override - public int hashCode() { - if (hash == 0) - hash = super.hashCode(); - return hash; - } - - @Override - public String toString() { - return "Vector2D [x = " + x + ", y = " + y + "]"; - } +package org.scvis.math; + +import java.io.Serializable; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * The Point2D class represents a 2-dimensional point in geometry. + * + * @author karlz + */ +@Immutable +public class Point2D implements Serializable { + + private static final long serialVersionUID = -9019588241960612260L; + + public static final @Nonnull Point2D ZERO = new Point2D(0.0, 0.0); + + /** + * The x coordinate of the vector. + */ + private final double x; + + /** + * The y coordinate of the vector. + */ + private final double y; + + /** + * Creates a Vector2D object with the given x and y coordinates. + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Point2D(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * Adds the components of another vector to this vector and returns a new vector as the result. + * + * @param v the vector to add + * @return a new vector representing the sum of this vector and the given vector + */ + @CheckReturnValue + @Nonnull + public Point2D add(@Nonnull Point2D v) { + return add(v.getX(), v.getY()); + } + + /** + * Adds the specified coordinates to this vector and returns a new vector as the result. + * + * @param x the x-coordinate to add + * @param y the y-coordinate to add + * @return a new vector representing the sum of this vector and the specified coordinates + */ + @CheckReturnValue + @Nonnull + public Point2D add(double x, double y) { + return new Point2D(this.x + x, this.y + y); + } + + /** + * Subtracts the components of another vector from this vector and returns a new vector as the result. + * + * @param v the vector to subtract + * @return a new vector representing the difference between this vector and the given vector + */ + @CheckReturnValue + @Nonnull + public Point2D subtract(@Nonnull Point2D v) { + return subtract(v.getX(), v.getY()); + } + + /** + * Subtracts the given x and y coordinates from this vector and returns a new Vector2D object. + * + * @param x the x coordinate to subtract + * @param y the y coordinate to subtract + * @return a new Vector2D object representing the difference of the two vectors + */ + @CheckReturnValue + @Nonnull + public Point2D subtract(double x, double y) { + return new Point2D(this.x - x, this.y - y); + } + + /** + * Multiplies this vector by a scalar value and returns a new vector as the result. + * + * @param s the scalar value to multiply by + * @return a new vector representing the product of this vector and the scalar value + */ + @CheckReturnValue + @Nonnull + public Point2D multiply(double s) { + return new Point2D(x * s, y * s); + } + + /** + * Calculates the dot product between this vector and another vector. + * + * @param v the vector to calculate the dot product with + * @return the dot product between this vector and the given vector + */ + @CheckReturnValue + public double dotProduct(@Nonnull Point2D v) { + return dotProduct(v.x, v.y); + } + + /** + * Calculates the dot product between this vector and the specified coordinates. + * + * @param x the x-coordinate of the vector + * @param y the y-coordinate of the vector + * @return the dot product between this vector and the specified coordinates + */ + @CheckReturnValue + public double dotProduct(double x, double y) { + return this.x * x + this.y * y; + } + + /** + * Calculates the distance between this vector and the given vector. + * + * @param v the vector to calculate the distance to + * @return the distance between the two vectors + */ + @CheckReturnValue + public double distance(@Nonnull Point2D v) { + return distance(v.x, v.y); + } + + /** + * Calculates the distance between this vector and the vector with the given x and y coordinates. + * + * @param x the x coordinate of the other vector + * @param y the y coordinate of the other vector + * @return the distance between the two vectors + */ + public double distance(double x, double y) { + double dx = this.x - x; + double dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); + } + + /** + * Returns a normalized version of this vector. If the vector is zero based, a zero vector is returned. + * + * @return a new vector representing the normalized version of this vector + */ + @CheckReturnValue + @Nonnull + public Point2D normalize() { + double mag = magnitude(); + if (mag == 0.0) { + return ZERO; + } + return new Point2D(x / mag, y / mag); + } + + /** + * Calculates the midpoint between this vector and the specified coordinates. + * + * @param v the vector + * @return a new vector representing the midpoint between this vector and the specified coordinates + */ + @CheckReturnValue + @Nonnull + public Point2D midpoint(@Nonnull Point2D v) { + return midpoint(v.x, v.y); + } + + /** + * Calculates the midpoint between this vector and the vector with the given x and y coordinates. + * + * @param x the x coordinate of the other vector + * @param y the y coordinate of the other vector + * @return the midpoint between the two vectors + */ + @CheckReturnValue + @Nonnull + public Point2D midpoint(double x, double y) { + return new Point2D(x + (this.x - x) / 2.0, + y + (this.y - y) / 2.0); + } + + /** + * Calculates the angle between this vector and another vector. + * + * @param v the vector to calculate the angle to + * @return the angle between this vector and the given vector + */ + @CheckReturnValue + public double angle(@Nonnull Point2D v) { + return angle(v.getX(), v.getY()); + } + + /** + * Calculates the angle between this vector and the specified coordinates. + * + * @param x the x-coordinate of the vector + * @param y the y-coordinate of the vector + * @return the angle between this vector and the specified coordinates + */ + @CheckReturnValue + public double angle(double x, double y) { + return Math.atan2(this.y - y, this.x - x); + } + + /** + * Rotates this vector by the specified angle and returns a new rotated vector. + * + * @param a the angle to rotate by, in radians + * @return a new vector representing the result of rotating this vector by the specified angle + */ + @CheckReturnValue + @Nonnull + public Point2D rotate(double a) { + return new Point2D(Math.cos(a) * x - Math.sin(a) * y, Math.sin(a) * x + Math.cos(a) * y); + } + + /** + * Calculates the magnitude (length) of this vector. + * + * @return the magnitude of the vector + */ + @CheckReturnValue + public double magnitude() { + return Math.sqrt(x * x + y * y); + } + + /** + * Returns the x coordinate of this vector. + * + * @return the x coordinate + */ + @CheckReturnValue + public double getX() { + return x; + } + + /** + * Returns the x coordinate of this vector. + * + * @return the x coordinate + */ + @CheckReturnValue + public double getY() { + return y; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj == null) + return false; + if (!(obj instanceof Point2D)) + return false; + if (obj == this) + return true; + Point2D vec = (Point2D) obj; + return x == vec.x && y == vec.y; + } + + private transient int hash; + + @Override + public int hashCode() { + if (hash == 0) + hash = super.hashCode(); + return hash; + } + + @Override + public String toString() { + return "Vector2D [x = " + x + ", y = " + y + "]"; + } } \ No newline at end of file diff --git a/src/main/java/org/scvis/math/Point3D.java b/src/main/java/org/scvis/math/Point3D.java index 9fcc568..9fec61c 100644 --- a/src/main/java/org/scvis/math/Point3D.java +++ b/src/main/java/org/scvis/math/Point3D.java @@ -1,260 +1,286 @@ -package org.scvis.math; - -import java.io.Serializable; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * The Point3D class represents a 3-dimensional vector in geometry. - * - * @author karlz - */ -@Immutable -public class Point3D implements Serializable { - - private static final long serialVersionUID = -4200231978633862588L; - - public static final @Nonnull Point3D ZERO = new Point3D(0.0, 0.0, 0.0); - - /** - * The x coordinate of the vector.} - */ - private final double x; - - /** - * The y coordinate of the vector.} - */ - private final double y; - - /** - * The z coordinate of the vector.} - */ - private final double z; - - /** - * Creates a Point3D object with the given x, y and z coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Point3D(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - /** - * Adds the components of another vector to this vector and returns a new vector - * as the result. - * - * @param v the vector to add - * @return a new vector representing the sum of this vector and the given vector - */ - @Nonnull - @CheckReturnValue - public Point3D add(@Nonnull Point3D v) { - return add(v.getX(), v.getY(), v.getZ()); - } - - /** - * Adds the specified coordinates to this vector and returns a new vector as the - * result. - * - * @param x the x-coordinate to add - * @param y the y-coordinate to add - * @param z the z-coordinate to add - * @return a new vector representing the sum of this vector and the specified - * coordinates - */ - @Nonnull - @CheckReturnValue - public Point3D add(double x, double y, double z) { - return new Point3D(this.x + x, this.y + y, this.z + z); - } - - /** - * Subtracts the components of another vector from this vector and returns a new - * vector as the result. - * - * @param v the vector to subtract - * @return a new vector representing the difference between this vector and the - * given vector - */ - @Nonnull - @CheckReturnValue - public Point3D subtract(@Nonnull Point3D v) { - return subtract(v.getX(), v.getY(), v.getZ()); - } - - /** - * Subtracts the specified coordinates from this vector and returns a new vector - * as the result. - * - * @param x the x-coordinate to subtract - * @param y the y-coordinate to subtract - * @param z the z-coordinate to subtract - * @return a new vector representing the difference between this vector and the - * specified coordinates - */ - @Nonnull - @CheckReturnValue - public Point3D subtract(double x, double y, double z) { - return new Point3D(this.x - x, this.y - y, this.z - z); - } - - /** - * Multiplies this vector by a scalar value and returns a new vector as the - * result. - * - * @param s the scalar value to multiply by - * @return a new vector representing the product of this vector and the scalar - * value - */ - @Nonnull - @CheckReturnValue - public Point3D multiply(double s) { - return new Point3D(x * s, y * s, z * s); - } - - /** - * Calculates the distance between this vector and another vector. - * - * @param v the vector to calculate the distance to - * @return the distance between this vector and the given vector - */ - @CheckReturnValue - public double distance(@Nonnull Point3D v) { - return distance(v.x, v.y, v.z); - } - - /** - * Calculates the distance between this vector and the specified coordinates. - * - * @param x the x-coordinate of the vector - * @param y the y-coordinate of the vector - * @param z the z-coordinate of the vector - * @return the distance between this vector and the specified coordinates - */ - @CheckReturnValue - public double distance(double x, double y, double z) { - double dx = this.x - x; - double dy = this.x - y; - double dz = this.z - z; - return Math.sqrt(Math.sqrt(dx * dx + dy * dy) + dz * dz); - } - - /** - * Returns a normalized version of this vector. If the vector has a magnitude of - * 0, the zero vector is returned. - * - * @return a new vector representing the normalized version of this vector - */ - @Nonnull - @CheckReturnValue - public Point3D normalize() { - double mag = magnitude(); - if (mag == 0.0) { - return ZERO; - } - return new Point3D(x / mag, y / mag, z / mag); - } - - /** - * Calculates the midpoint between this vector and another vector. - * - * @param v the vector to calculate the midpoint with - * @return a new vector representing the midpoint between this vector and the - * given vector - */ - @Nonnull - @CheckReturnValue - public Point3D midpoint(@Nonnull Point3D v) { - return midpoint(v.x, v.y, v.z); - } - - /** - * Calculates the midpoint between this vector and the specified coordinates. - * - * @param x the x-coordinate of the vector - * @param y the y-coordinate of the vector - * @param z the z-coordinate of the vector - * @return a new vector representing the midpoint between this vector and the - * specified coordinates - */ - @Nonnull - @CheckReturnValue - public Point3D midpoint(double x, double y, double z) { - return new Point3D(x + (this.x - x) / 2.0, y + (this.y - y) / 2.0, z + (this.z - z) / 2); - } - - /** - * Calculates the magnitude (length) of this vector. - * - * @return the magnitude of this vector - */ - public double magnitude() { - return Math.sqrt(x * x + y * y + z * z); - } - - /** - * Returns the x-coordinate of this vector. - * - * @return the x-coordinate - */ - public double getX() { - return x; - } - - /** - * Returns the y-coordinate of this vector. - * - * @return the y-coordinate - */ - public double getY() { - return y; - } - - /** - * Returns the z-coordinate of this vector. - * - * @return the z-coordinate - */ - public double getZ() { - return z; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj == null) return false; - if (!(obj instanceof Point3D)) - return false; - if (obj == this) - return true; - Point3D vec = (Point3D) obj; - return x == vec.x && y == vec.y && z == vec.z; - } - - @Nonnull - @CheckReturnValue - public Vector toVector() { - return new Vector(new double[]{x, y, z}); - } - - private transient int hash; - - @Override - public int hashCode() { - if (hash == 0) - hash = super.hashCode(); - return hash; - } - - @Override - public String toString() { - return "Point3D [x = " + x + ", y = " + y + ", z = " + z + "]"; - } -} +package org.scvis.math; + +import java.io.Serializable; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * The Point3D class represents a 3-dimensional vector in geometry. + * + * @author karlz + */ +@Immutable +public class Point3D implements Serializable { + + private static final long serialVersionUID = -4200231978633862588L; + + public static final @Nonnull Point3D ZERO = new Point3D(0.0, 0.0, 0.0); + + /** + * The x coordinate of the vector.} + */ + private final double x; + + /** + * The y coordinate of the vector.} + */ + private final double y; + + /** + * The z coordinate of the vector.} + */ + private final double z; + + /** + * Creates a Point3D object with the given x, y and z coordinates. + * + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + */ + public Point3D(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Adds the components of another vector to this vector and returns a new vector as the result. + * + * @param v the vector to add + * @return a new vector representing the sum of this vector and the given vector + */ + @Nonnull + @CheckReturnValue + public Point3D add(@Nonnull Point3D v) { + return add(v.getX(), v.getY(), v.getZ()); + } + + /** + * Adds the specified coordinates to this vector and returns a new vector as the result. + * + * @param x the x-coordinate to add + * @param y the y-coordinate to add + * @param z the z-coordinate to add + * @return a new vector representing the sum of this vector and the specified coordinates + */ + @Nonnull + @CheckReturnValue + public Point3D add(double x, double y, double z) { + return new Point3D(this.x + x, this.y + y, this.z + z); + } + + /** + * Subtracts the components of another vector from this vector and returns a new vector as the result. + * + * @param v the vector to subtract + * @return a new vector representing the difference between this vector and the given vector + */ + @Nonnull + @CheckReturnValue + public Point3D subtract(@Nonnull Point3D v) { + return subtract(v.getX(), v.getY(), v.getZ()); + } + + /** + * Subtracts the specified coordinates from this vector and returns a new vector as the result. + * + * @param x the x-coordinate to subtract + * @param y the y-coordinate to subtract + * @param z the z-coordinate to subtract + * @return a new vector representing the difference between this vector and the specified coordinates + */ + @Nonnull + @CheckReturnValue + public Point3D subtract(double x, double y, double z) { + return new Point3D(this.x - x, this.y - y, this.z - z); + } + + /** + * Multiplies this vector by a scalar value and returns a new vector as the result. + * + * @param s the scalar value to multiply by + * @return a new vector representing the product of this vector and the scalar value + */ + @Nonnull + @CheckReturnValue + public Point3D multiply(double s) { + return new Point3D(x * s, y * s, z * s); + } + + /** + * Calculates the distance between this vector and another vector. + * + * @param v the vector to calculate the distance to + * @return the distance between this vector and the given vector + */ + @CheckReturnValue + public double distance(@Nonnull Point3D v) { + return distance(v.x, v.y, v.z); + } + + /** + * Calculates the distance between this vector and the specified coordinates. + * + * @param x the x-coordinate of the vector + * @param y the y-coordinate of the vector + * @param z the z-coordinate of the vector + * @return the distance between this vector and the specified coordinates + */ + @CheckReturnValue + public double distance(double x, double y, double z) { + double dx = this.x - x; + double dy = this.x - y; + double dz = this.z - z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + + /** + * Returns a normalized version of this vector. If the vector has a magnitude of 0, the zero vector is returned. + * + * @return a new vector representing the normalized version of this vector + */ + @Nonnull + @CheckReturnValue + public Point3D normalize() { + double mag = magnitude(); + if (mag == 0.0) { + return ZERO; + } + return new Point3D(x / mag, y / mag, z / mag); + } + + /** + * Calculates the midpoint between this vector and another vector. + * + * @param v the vector to calculate the midpoint with + * @return a new vector representing the midpoint between this vector and the given vector + */ + @Nonnull + @CheckReturnValue + public Point3D midpoint(@Nonnull Point3D v) { + return midpoint(v.x, v.y, v.z); + } + + /** + * Calculates the midpoint between this vector and the specified coordinates. + * + * @param x the x-coordinate of the vector + * @param y the y-coordinate of the vector + * @param z the z-coordinate of the vector + * @return a new vector representing the midpoint between this vector and the specified coordinates + */ + @Nonnull + @CheckReturnValue + public Point3D midpoint(double x, double y, double z) { + return new Point3D(x + (this.x - x) / 2.0, y + (this.y - y) / 2.0, z + (this.z - z) / 2); + } + + /** + * Calculates the magnitude (length) of this vector. + * + * @return the magnitude of this vector + */ + @CheckReturnValue + public double magnitude() { + return Math.sqrt(x * x + y * y + z * z); + } + + /** + * Checks if the given point is a scaled product from this point. + * + * @param v the point to check + * @return true if it is scaled, false otherwise + */ + @CheckReturnValue + public boolean isMultiplicative(@Nonnull Point3D v) { + if (magnitude() == 0) + return v.magnitude() == 0; + double scale = getX() != 0 ? v.getX() / getX() : (getY() != 0 ? v.getY() / getY() : v.getZ() / getZ()); + return v.getX() == scale * getX() && v.getY() == scale * getY() && v.getZ() == scale * getZ(); + } + + /** + * For a point v, it finds the scalar s for this point p, where v=s¡p is true. + * + * @param v the point + * @return returns the scalar or nan of no scalar exist + */ + @CheckReturnValue + public double getMultiplicative(@Nonnull Point3D v) { + if (magnitude() == 0) { + return v.magnitude() == 0 ? 0 : Double.NaN; + } + double scale = getX() != 0 ? v.getX() / getX() : (getY() != 0 ? v.getY() / getY() : v.getZ() / getZ()); + if (v.getX() == scale * getX() && v.getY() == scale * getY() && v.getZ() == scale * getZ()) + return scale; + return Double.NaN; + } + + /** + * Returns the x-coordinate of this vector. + * + * @return the x-coordinate + */ + @CheckReturnValue + public double getX() { + return x; + } + + /** + * Returns the y-coordinate of this vector. + * + * @return the y-coordinate + */ + @CheckReturnValue + public double getY() { + return y; + } + + /** + * Returns the z-coordinate of this vector. + * + * @return the z-coordinate + */ + @CheckReturnValue + public double getZ() { + return z; + } + + @CheckReturnValue + @Override + public boolean equals(@Nullable Object obj) { + if (obj == null) return false; + if (!(obj instanceof Point3D)) + return false; + if (obj == this) + return true; + Point3D vec = (Point3D) obj; + return x == vec.x && y == vec.y && z == vec.z; + } + + @Nonnull + @CheckReturnValue + public Vector toVector() { + return new Vector(x, y, z); + } + + private transient int hash; + + @CheckReturnValue + @Override + public int hashCode() { + if (hash == 0) + hash = super.hashCode(); + return hash; + } + + @CheckReturnValue + @Override + public String toString() { + return "Point3D [x = " + x + ", y = " + y + ", z = " + z + "]"; + } +} diff --git a/src/main/java/org/scvis/math/Polygon.java b/src/main/java/org/scvis/math/Polygon.java index 2f66ff2..499578f 100644 --- a/src/main/java/org/scvis/math/Polygon.java +++ b/src/main/java/org/scvis/math/Polygon.java @@ -1,169 +1,163 @@ -package org.scvis.math; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.annotation.Nonnull; - -/** - * Represents a polygon shape. - */ -public class Polygon implements Border2D { - - private static final long serialVersionUID = -3362147056076306244L; - - private final @Nonnull List points; - private final double minX; - private final double minY; - private final double maxX; - private final double maxY; - - /** - * Constructs a polygon with the specified list of points. - * - * @param points the points of the polygon - */ - public Polygon(@Nonnull List points) { - this.points = points; - minX = Collections.min(points, (a, b) -> (int) (a.getX() - b.getX())).getX(); - minY = Collections.min(points, (a, b) -> (int) (a.getY() - b.getY())).getY(); - maxX = Collections.max(points, (a, b) -> (int) (a.getX() - b.getX())).getX(); - maxY = Collections.max(points, (a, b) -> (int) (a.getY() - b.getY())).getY(); - } - - /** - * Checks whether a given vector is inside or outside a simple polygon using the - * Jordan test. - *

- * This method counts the number of intersections for a ray passing from the - * exterior of the polygon to any point: If odd, it shows that the point lies - * inside the polygon; if even, the point lies outside the polygon. - * - * @param polygon the list of vectors defining the polygon. - * @param point2D the vector or point to check. - * @return true if the vector is inside, false otherwise. - */ - public static boolean contains(@Nonnull List polygon, @Nonnull Point2D point2D) { - boolean inside = false; - for (int i = 0; i < polygon.size(); i++) { - int j = (i + 1) % polygon.size(); - if (crosses(polygon.get(i), polygon.get(j), point2D)) - inside = !inside; - } - return inside; - } - - /** - * Checks if a ray passing from the exterior of the polygon crosses a line AB of - * the polygon. - * - * @param a the first point of the line. - * @param b the second point of the line. - * @param point2D the tested point. - * @return true if the ray crosses the line AB - */ - public static boolean crosses(@Nonnull Point2D a, @Nonnull Point2D b, Point2D point2D) { - if (point2D.getY() == a.getY() && a.getY() == b.getY()) - return a.getX() <= point2D.getX() && point2D.getX() <= b.getX() - || b.getX() <= point2D.getX() && point2D.getX() <= a.getX(); - if (point2D.getY() == a.getY() && point2D.getX() == b.getX()) - return true; - if (a.getY() > b.getY()) { - var t = a; - a = b; - b = t; - } - if (point2D.getY() <= a.getY() || point2D.getY() > b.getY()) - return false; - double x = (a.getX() - point2D.getX()) * (b.getY() - point2D.getY()) - - (a.getY() - point2D.getY()) * (b.getX() - point2D.getX()); - return (x >= 0); - } - - @Override - public boolean contains(@Nonnull Point2D point2D) { - if (point2D.getX() < minX || point2D.getX() > maxX) - return false; - if (point2D.getY() < minY || point2D.getY() > maxY) - return false; - return contains(points, point2D); - } - - @Override - public boolean intersects(@Nonnull Border2D border2D) { - if (border2D instanceof Polygon) { - Polygon poly = (Polygon) border2D; - for (Point2D point : poly.getPoints()) - if (contains(point)) - return true; - } - for (Point2D point : getPoints()) - if (contains(point)) - return true; - return false; - } - - @Override - @Nonnull - public Polygon translate(@Nonnull Point2D v) { - return translate(v.getX(), v.getY()); - } - - @Override - @Nonnull - public Polygon translate(double x, double y) { - List translated = new ArrayList<>(); - for (Point2D point : this.points) - translated.add(point.add(x, y)); - return new Polygon(translated); - } - - @Override - @Nonnull - public Polygon rotate(double a) { - return rotate(centroid(), a); - } - - @Override - @Nonnull - public Polygon rotate(@Nonnull Point2D center, double a) { - List rotated = new ArrayList<>(); - for (Point2D point : this.points) - rotated.add(point.subtract(center).rotate(a).add(center)); - return new Polygon(rotated); - } - - private Point2D center; - - @Override - @Nonnull - public Point2D centroid() { - if (center == null) { - double x = 0; - double y = 0; - for (Point2D point2D : points) { - x += point2D.getX(); - y += point2D.getY(); - } - center = new Point2D(x / points.size(), y / points.size()); - } - return center; - } - - /** - * Retrieves the points of the polygon. - * - * @return the points of the polygon - */ - @Nonnull - public List getPoints() { - return points; - } - - @Override - public String toString() { - return "Polygon [points = " + points + ", maxX = " + maxX + ", maxY = " + maxY + ", minX = " + minX - + ", minY = " + minY + "]"; - } -} +package org.scvis.math; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.annotation.Nonnull; + +/** + * Represents a polygon shape. + */ +public class Polygon implements Border2D { + + private static final long serialVersionUID = -3362147056076306244L; + + private final @Nonnull List points; + private final double minX; + private final double minY; + private final double maxX; + private final double maxY; + + /** + * Constructs a polygon with the specified list of points. + * + * @param points the points of the polygon + */ + public Polygon(@Nonnull List points) { + this.points = points; + minX = Collections.min(points, (a, b) -> (int) (a.getX() - b.getX())).getX(); + minY = Collections.min(points, (a, b) -> (int) (a.getY() - b.getY())).getY(); + maxX = Collections.max(points, (a, b) -> (int) (a.getX() - b.getX())).getX(); + maxY = Collections.max(points, (a, b) -> (int) (a.getY() - b.getY())).getY(); + } + + /** + * Checks whether a given vector is inside or outside a simple polygon using the Jordan test. + *

+ * This method counts the number of intersections for a ray passing from the exterior of the polygon to any point: + * If odd, it shows that the point lies inside the polygon; if even, the point lies outside the polygon. + * + * @param polygon the list of vectors defining the polygon. + * @param point2D the vector or point to check. + * @return true if the vector is inside, false otherwise. + */ + public static boolean contains(@Nonnull List polygon, @Nonnull Point2D point2D) { + boolean inside = false; + for (int i = 0; i < polygon.size(); i++) { + int j = (i + 1) % polygon.size(); + if (crosses(polygon.get(i), polygon.get(j), point2D)) + inside = !inside; + } + return inside; + } + + /** + * Checks if a ray passing from the exterior of the polygon crosses a line AB of the polygon. + * + * @param a the first point of the line. + * @param b the second point of the line. + * @param point2D the tested point. + * @return true if the ray crosses the line AB + */ + public static boolean crosses(@Nonnull Point2D a, @Nonnull Point2D b, Point2D point2D) { + if (point2D.getY() == a.getY() && a.getY() == b.getY()) + return a.getX() <= point2D.getX() && point2D.getX() <= b.getX() + || b.getX() <= point2D.getX() && point2D.getX() <= a.getX(); + if (point2D.getY() == a.getY() && point2D.getX() == b.getX()) + return true; + if (a.getY() > b.getY()) { + var t = a; + a = b; + b = t; + } + if (point2D.getY() <= a.getY() || point2D.getY() > b.getY()) + return false; + double x = (a.getX() - point2D.getX()) * (b.getY() - point2D.getY()) + - (a.getY() - point2D.getY()) * (b.getX() - point2D.getX()); + return (x >= 0); + } + + @Override + public boolean contains(@Nonnull Point2D point2D) { + if (point2D.getX() < minX || point2D.getX() > maxX) + return false; + if (point2D.getY() < minY || point2D.getY() > maxY) + return false; + return contains(points, point2D); + } + + @Override + public boolean intersects(@Nonnull Polygon poly) { + for (Point2D point : poly.getPoints()) + if (contains(point)) + return true; + for (Point2D point : getPoints()) + if (poly.contains(point)) + return true; + return false; + } + + @Override + @Nonnull + public Polygon translate(@Nonnull Point2D v) { + return translate(v.getX(), v.getY()); + } + + @Override + @Nonnull + public Polygon translate(double x, double y) { + List translated = new ArrayList<>(); + for (Point2D point : this.points) + translated.add(point.add(x, y)); + return new Polygon(translated); + } + + @Override + @Nonnull + public Polygon rotate(double a) { + return rotate(centroid(), a); + } + + @Override + @Nonnull + public Polygon rotate(@Nonnull Point2D center, double a) { + List rotated = new ArrayList<>(); + for (Point2D point : this.points) + rotated.add(point.subtract(center).rotate(a).add(center)); + return new Polygon(rotated); + } + + private Point2D center; + + @Override + @Nonnull + public Point2D centroid() { + if (center == null) { + double x = 0; + double y = 0; + for (Point2D point2D : points) { + x += point2D.getX(); + y += point2D.getY(); + } + center = new Point2D(x / points.size(), y / points.size()); + } + return center; + } + + /** + * Retrieves the points of the polygon. + * + * @return the points of the polygon + */ + @Nonnull + public List getPoints() { + return points; + } + + @Override + public String toString() { + return "Polygon [points = " + points + ", maxX = " + maxX + ", maxY = " + maxY + ", minX = " + minX + + ", minY = " + minY + "]"; + } +} diff --git a/src/main/java/org/scvis/math/Rectangle.java b/src/main/java/org/scvis/math/Rectangle.java new file mode 100644 index 0000000..fb104e9 --- /dev/null +++ b/src/main/java/org/scvis/math/Rectangle.java @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import javax.annotation.Nonnull; + +public class Rectangle implements Border2D { + + private final double minX; + private final double minY; + private final double maxX; + private final double maxY; + + public Rectangle(double startX, double startY, double width, double height) { + if (width <= 0 || height <= 0) + throw new IllegalArgumentException("Width and height must be positive"); + this.minX = startX; + this.minY = startY; + this.maxX = startX + width; + this.maxY = startY + height; + } + + public Rectangle(double width, double height) { + this(0, 0, width, height); + } + + @Override + public boolean contains(@Nonnull Point2D point2D) { + return minX <= point2D.getX() && maxX >= point2D.getX() && minY <= point2D.getY() && maxY >= point2D.getY(); + } + + @Override + public boolean intersects(@Nonnull Rectangle border2D) { + return false; + } + + @Nonnull + @Override + public Rectangle translate(double x, double y) { + return new Rectangle(minX + x, minY + y, maxX + x, maxY + y); + } + + @Nonnull + @Override + public Rectangle rotate(@Nonnull Point2D center, double a) { + throw new UnsupportedOperationException(); + } + + @Nonnull + @Override + public Point2D centroid() { + return new Point2D(minX + 0.5 * getWidth(), minY + 0.5 * getHeight()); + } + + public double getWidth() { + return maxX - minX; + } + + public double getHeight() { + return maxY - minY; + } +} diff --git a/src/main/java/org/scvis/math/Stochastic.java b/src/main/java/org/scvis/math/Stochastic.java index 873cca1..36e06d7 100644 --- a/src/main/java/org/scvis/math/Stochastic.java +++ b/src/main/java/org/scvis/math/Stochastic.java @@ -1,51 +1,51 @@ -package org.scvis.math; - -public final class Stochastic { - - private Stochastic() { - throw new UnsupportedOperationException(); - } - - public static double factorial(int n) { - double sum = 1; - while (n > 0) { - sum *= n; - n--; - } - return sum; - } - - public static double binom(int n, int k) { - return factorial(n) / (factorial(k) * factorial(n - k)); - } - - public static double combU(int n, int k) { - return binom(n, k); - } - - public static double combA(int n, int k){ - return binom(n + k - 1, k); - } - - public static double bernoulliPdf(double p, int n, int k) { - return binom(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k); - } - - public static double bernoulliCdf(double p, int n, int min, int max) { - double sum = 0; - while (min <= max) { - sum += bernoulliPdf(p, n, min); - min ++; - } - return sum; - } - - public static int minimalAttempts(double p, double min, int n) { - double sum = bernoulliPdf(p, n, 0); - int k = 0; - while (sum < min) { - sum += bernoulliPdf(p, n, ++k); - } - return k; - } -} +package org.scvis.math; + +public final class Stochastic { + + private Stochastic() { + throw new UnsupportedOperationException(); + } + + public static double factorial(int n) { + double sum = 1; + while (n > 0) { + sum *= n; + n--; + } + return sum; + } + + public static double binom(int n, int k) { + return factorial(n) / (factorial(k) * factorial(n - k)); + } + + public static double combU(int n, int k) { + return binom(n, k); + } + + public static double combA(int n, int k) { + return binom(n + k - 1, k); + } + + public static double bernoulliPdf(double p, int n, int k) { + return binom(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k); + } + + public static double bernoulliCdf(double p, int n, int min, int max) { + double sum = 0; + while (min <= max) { + sum += bernoulliPdf(p, n, min); + min++; + } + return sum; + } + + public static int minimalAttempts(double p, double min, int n) { + double sum = bernoulliPdf(p, n, 0); + int k = 0; + while (sum < min) { + sum += bernoulliPdf(p, n, ++k); + } + return k; + } +} diff --git a/src/main/java/org/scvis/math/Triangle.java b/src/main/java/org/scvis/math/Triangle.java new file mode 100644 index 0000000..287dfed --- /dev/null +++ b/src/main/java/org/scvis/math/Triangle.java @@ -0,0 +1,107 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class Triangle implements Border3D { + + private final @Nonnull Point3D p0; + private final @Nonnull Point3D p1; + private final @Nonnull Point3D p2; + + public Triangle(@Nonnull Point3D p0, @Nonnull Point3D p1, @Nonnull Point3D p2) { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + } + + @CheckReturnValue + @Override + public boolean contains(@Nonnull Point3D p) { + Point3D ab = p1.subtract(p0); + Point3D ac = p2.subtract(p0); + Point3D pp = p.subtract(p0); + double[] solutions = Equations.solve(ab.getX(), ac.getX(), pp.getX(), ab.getY(), ac.getY(), pp.getY()); + if (solutions.length == 0) return false; + double x = solutions[0]; + double y = solutions[1]; + if (ab.getZ() * x + ac.getZ() * y == pp.getZ()) { + return x >= 0 && y >= 0 && x + y <= 1; + } + return false; + } + + @CheckReturnValue + @Override + public boolean intersects(@Nonnull Triangle tri) { + Point3D ab = p1.subtract(p0); + Point3D ac = p2.subtract(p0); + Point3D de = tri.p1.subtract(tri.p0); // line vector + + Point3D df = tri.p2.subtract(tri.p0); // check after + + double[] solution = Equations.solve( + new double[][]{{ab.getX(), ac.getX(), -de.getX()}, {ab.getY(), ac.getY(), -de.getY()}, + {ab.getZ(), ac.getZ(), -de.getZ()}}, + new double[]{tri.p0.getX() - p0.getX(), tri.p0.getY() - p0.getY(), tri.p0.getZ() - p0.getZ()}); + + throw new UnsupportedOperationException(); + } + + @CheckReturnValue + @Nonnull + @Override + public Triangle translate(double x, double y, double z) { + return new Triangle(p0.add(x, y, z), p1.add(x, y, z), p2.add(x, y, z)); + } + + @CheckReturnValue + @Nonnull + @Override + public Triangle rotate(@Nonnull Point3D center, double a, double b) { + throw new UnsupportedOperationException(); + } + + @CheckReturnValue + @Nonnull + @Override + public Point3D centroid() { + return new Point3D((p0.getX() + p1.getX() + p2.getX()) / 3, (p0.getY() + p1.getY() + p2.getY()) / 3, + (p0.getZ() + p1.getZ() + p2.getZ()) / 3); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj == null) return false; + if (obj instanceof Triangle) { + Triangle tri = (Triangle) obj; + return p0.equals(tri.p0) && p1.equals(tri.p1) || p2.equals(tri.p2); + } + return false; + } +} diff --git a/src/main/java/org/scvis/math/Vector.java b/src/main/java/org/scvis/math/Vector.java index d0d2232..8ee8c1a 100644 --- a/src/main/java/org/scvis/math/Vector.java +++ b/src/main/java/org/scvis/math/Vector.java @@ -1,127 +1,140 @@ -package org.scvis.math; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import java.io.Serializable; -import java.util.Arrays; - -public class Vector implements Serializable { - - @Nonnull - private final double[] elements; - - private final int size; - - public Vector(@Nonnull double[] elements) { - this.elements = elements; - this.size = elements.length; - } - - public Vector(int size) { - this.elements = new double[size]; - this.size = size; - } - - @Nonnull - public Vector add(@Nonnull Vector another) { - for (int i = 0; i < size; i++) { - elements[i] += another.elements[i]; - } - return this; - } - - @Nonnull - public Vector subtract(@Nonnull Vector another) { - for (int i = 0; i < size; i++) { - elements[i] -= another.elements[i]; - } - return this; - } - - @CheckReturnValue - @Nonnull - public Vector difference(@Nonnull Vector another) { - Vector created = new Vector(size); - for (int i = 0; i < size; i++) { - created.elements[i] = elements[i] - another.elements[i]; - } - return created; - } - - @CheckReturnValue - public double distance(@Nonnull Vector another) { - double distance = 0; - for (int i = 0; i < size; i++) { - double dif = elements[i] - another.elements[i]; - distance += dif * dif; - } - return Math.sqrt(distance); - } - - @CheckReturnValue - public double magnitude() { - double magnitude = 0; - for (double element : getElements()) - magnitude += element * element; - return Math.sqrt(magnitude); - } - - @CheckReturnValue - public double length() { - return magnitude(); - } - - @Nonnull - public Vector normalized() { - double mag = magnitude(); - if (mag != 0) { - for (int i = 0; i < size; i++) - elements[i] /= mag; - } - return this; - } - - @Override - public String toString() { - return Arrays.toString(elements); - } - - @CheckReturnValue - @Nonnull - public double[] getElements() { - return elements; - } - - public int getSize() { - return size; - } - - transient int hash = 0; - - @Override - public int hashCode() { - if (hash != 0) return hash; - double code = 43; - for (double element : elements) { - code = code * size + element; - } - hash = (int) code; - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (obj == this) return true; - if (!(obj instanceof Vector)) - return false; - - Vector cast = (Vector) obj; - if (size != cast.size) return false; - for (int i = 0; i < size; i++) { - if (elements[i] != cast.elements[i]) - return false; - } - return true; - } -} +package org.scvis.math; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.io.Serializable; +import java.util.Arrays; + +public class Vector implements Serializable { + + @CheckReturnValue + @Nonnull + public static Matrix toMatrix(@Nonnull Vector vector, @Nonnull Vector... vectors) { + int size = vector.getSize(); + Matrix matrix = new Matrix(vectors.length + 1, size); + System.arraycopy(vector.getElements(), 0, matrix.getEntries()[0], 0, size); + for (int i = 0; i < vectors.length; i++) { + if (vectors[i].getSize() != size) throw new IllegalArgumentException(); + System.arraycopy(vectors[i].getElements(), 0, matrix.getEntries()[i + 1], 0, size); + } + return matrix; + } + + @Nonnull + private final double[] elements; + + private final int size; + + public Vector(@Nonnull double... elements) { + this.elements = elements; + this.size = elements.length; + } + + public Vector(int size) { + this.elements = new double[size]; + this.size = size; + } + + @Nonnull + public Vector add(@Nonnull Vector another) { + for (int i = 0; i < size; i++) { + elements[i] += another.elements[i]; + } + return this; + } + + @Nonnull + public Vector subtract(@Nonnull Vector another) { + for (int i = 0; i < size; i++) { + elements[i] -= another.elements[i]; + } + return this; + } + + @CheckReturnValue + @Nonnull + public Vector difference(@Nonnull Vector another) { + Vector created = new Vector(size); + for (int i = 0; i < size; i++) { + created.elements[i] = elements[i] - another.elements[i]; + } + return created; + } + + @CheckReturnValue + public double distance(@Nonnull Vector another) { + double distance = 0; + for (int i = 0; i < size; i++) { + double dif = elements[i] - another.elements[i]; + distance += dif * dif; + } + return Math.sqrt(distance); + } + + @CheckReturnValue + public double magnitude() { + double magnitude = 0; + for (double element : getElements()) + magnitude += element * element; + return Math.sqrt(magnitude); + } + + @CheckReturnValue + public double length() { + return magnitude(); + } + + @Nonnull + public Vector normalized() { + double mag = magnitude(); + if (mag != 0) { + for (int i = 0; i < size; i++) + elements[i] /= mag; + } + return this; + } + + @Override + public String toString() { + return Arrays.toString(elements); + } + + @CheckReturnValue + @Nonnull + public double[] getElements() { + return elements; + } + + public int getSize() { + return size; + } + + transient int hash = 0; + + @Override + public int hashCode() { + if (hash != 0) return hash; + double code = 43; + for (double element : elements) { + code = code * size + element; + } + hash = (int) code; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + if (!(obj instanceof Vector)) + return false; + + Vector cast = (Vector) obj; + if (size != cast.size) return false; + for (int i = 0; i < size; i++) { + if (elements[i] != cast.elements[i]) + return false; + } + return true; + } +} diff --git a/src/main/java/org/scvis/parser/ArrayValue.java b/src/main/java/org/scvis/parser/ArrayValue.java new file mode 100644 index 0000000..2c3a59c --- /dev/null +++ b/src/main/java/org/scvis/parser/ArrayValue.java @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.parser; + +import javax.annotation.Nonnull; +import java.util.*; + +public class ArrayValue implements Value[]> { + + private static final Class ANY = Object.class; + + private final @Nonnull Value[] values; + private final @Nonnull Class typing; + private final int size; + + public ArrayValue(Value... values) { + this.values = values; + this.size = values.length; + this.typing = size > 0 ? values[0].getClass() : ANY; + check(); + } + + public ArrayValue(TokenParser parser) { + TokenEvaluator evaluator = new TokenEvaluator(parser.getOperators(), parser.getTokens()); + List> list = evaluator.evaluate(); + + this.values = list.toArray(new Value[0]); + this.size = list.size(); + this.typing = size > 0 ? values[0].getClass() : ANY; + check(); + } + + private void check() { + for (Value value : get()) { + if (!(getTyping().isInstance(value))) + throw new EvaluationException( + "Array requires " + getTyping().getSimpleName() + ", got " + value.getClass().getSimpleName()); + } + } + + @Nonnull + @Override + public Value[] get() { + return values; + } + + @Nonnull + public Class getTyping() { + return typing; + } + + public int getSize() { + return size; + } +} \ No newline at end of file diff --git a/src/main/java/org/scvis/parser/BaseOperator.java b/src/main/java/org/scvis/parser/BaseOperator.java index 02f80b4..beaff8d 100644 --- a/src/main/java/org/scvis/parser/BaseOperator.java +++ b/src/main/java/org/scvis/parser/BaseOperator.java @@ -1,64 +1,103 @@ -package org.scvis.parser; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import java.util.function.BiFunction; -import java.util.function.BinaryOperator; - -@Immutable -public class BaseOperator implements Operator { - - public static final Operator ADD = new BaseOperator((a, b) -> - new Constant(a.get().doubleValue() + b.get().doubleValue()), "+", 1); - public static final Operator SUBTRACT = new BaseOperator((a, b) -> - new Constant(a.get().doubleValue() - b.get().doubleValue()), "-", 1); - public static final Operator MULTIPLY = new BaseOperator((a, b) -> - new Constant(a.get().doubleValue() * b.get().doubleValue()), "*", 2); - public static final Operator DIVIDE = new BaseOperator((a, b) -> - new Constant(a.get().doubleValue() / b.get().doubleValue()), "/", 2); - public static final Operator MOD = new BaseOperator((a, b) -> - new Constant(a.get().doubleValue() % b.get().doubleValue()), "%", 3); - public static final Operator POW = new BaseOperator((a, b) -> - new Constant(Math.pow(a.get().doubleValue(), b.get().doubleValue())), "^", 4); - public static final Operator EQU = new BaseOperator((a, b) -> new Constant(a.get().equals(b.get()) ? 1 : 0), - "=", 0); - - private final @Nonnull BiFunction function; - private final @Nonnull String ident; - private final int priority; - private final boolean sign; - - public BaseOperator(@Nonnull BinaryOperator function, @Nonnull String ident, int priority) { - this.function = function; - this.ident = ident; - this.priority = priority; - this.sign = priority == 1; - } - - @CheckReturnValue - @Nonnull - @Override - public Value evaluate(@Nonnull Value left, @Nonnull Value right) { - return function.apply(left, right); - } - - @CheckReturnValue - @Override - public int priority() { - return priority; - } - - @CheckReturnValue - @Override - public boolean isSign() { - return sign; - } - - @CheckReturnValue - @Nonnull - @Override - public String toString() { - return ident; - } -} +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package org.scvis.parser; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; + +/** + * The BaseOperator class is a definition template for the default operators of values. It contains the + * definition for addition, subtraction, multiplication, division, + * + * @author karlz + * @see Operator + */ +@Immutable +public class BaseOperator implements Operator { + + public static final Operator ADD = new BaseOperator((a, b) -> + new NumberValue(a.getDouble() + b.getDouble()), "+", 1); + public static final Operator SUBTRACT = new BaseOperator((a, b) -> + new NumberValue(a.getDouble() - b.getDouble()), "-", 1); + public static final Operator MULTIPLY = new BaseOperator((a, b) -> + new NumberValue(a.getDouble() * b.getDouble()), "*", 2); + public static final Operator DIVIDE = new BaseOperator((a, b) -> + new NumberValue(a.getDouble() / b.getDouble()), "/", 2); + public static final Operator MOD = new BaseOperator((a, b) -> + new NumberValue(a.getDouble() % b.getDouble()), "%", 3); + public static final Operator POW = new BaseOperator((a, b) -> + new NumberValue(Math.pow(a.getDouble(), b.getDouble())), "^", 4); + public static final Operator EQU = new BaseOperator((a, b) -> new NumberValue(a.get().equals(b.get()) ? 1 : 0), + "=", 0); + + private final @Nonnull BiFunction, Value, Value> function; + private final @Nonnull String representation; + private final int priority; + private final boolean sign; + + /** + * Creates a new base operator with a binary operator + * + * @param function the binary operator + * @param representation the string that represents this operator + * @param priority the priority + */ + public BaseOperator(@Nonnull BinaryOperator> function, @Nonnull String representation, int priority) { + this.function = function; + this.representation = representation; + this.priority = priority; + this.sign = priority == 1; + } + + @CheckReturnValue + @Nonnull + @Override + public Value evaluate(@Nonnull Value left, @Nonnull Value right) { + return function.apply(left, right); + } + + @CheckReturnValue + @Override + public int priority() { + return priority; + } + + @CheckReturnValue + @Override + public boolean isSign() { + return sign; + } + + @CheckReturnValue + @Nonnull + @Override + public String toString() { + return representation; + } +} diff --git a/src/main/java/org/scvis/parser/Brackets.java b/src/main/java/org/scvis/parser/Brackets.java index 15f9541..a028637 100644 --- a/src/main/java/org/scvis/parser/Brackets.java +++ b/src/main/java/org/scvis/parser/Brackets.java @@ -1,23 +1,60 @@ -package org.scvis.parser; - -import javax.annotation.Nonnull; -import java.util.List; - -public class Brackets implements Value { - - private final @Nonnull Number value; - - public Brackets(@Nonnull List operators, @Nonnull List tokens) { - List values = new TokenEvaluator(operators, tokens).evaluate(); - if (values.size() != 1) { - throw new EvaluationException("Brackets wrap one effective value, got " + values.size()); - } - this.value = values.get(0); - } - - @Nonnull - @Override - public Number get() { - return value; - } -} +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.parser; + +import javax.annotation.Nonnull; +import java.util.List; + +/** + * Brackets are a type of value that wraps one or more expressions. + * + * @author karlz + * @see org.scvis.parser.Value + */ +public class Brackets implements Value { + + private final @Nonnull Object value; + + /** + * Creates a new brackets object from a list of operators and tokens. The list of tokens must contain the list of + * tokens. + * + * @param operators the list of operators + * @param tokens the list of tokens + */ + public Brackets(@Nonnull List operators, @Nonnull List tokens) { + List> values = new TokenEvaluator(operators, tokens).evaluate(); + if (values.size() != 1) { + throw new EvaluationException("Brackets wrap one effective value, got " + values.size()); + } + this.value = values.get(0).get(); + } + + @Nonnull + @Override + public Object get() { + return value; + } +} diff --git a/src/main/java/org/scvis/parser/Constant.java b/src/main/java/org/scvis/parser/Constant.java deleted file mode 100644 index 68389f4..0000000 --- a/src/main/java/org/scvis/parser/Constant.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.scvis.parser; - -import javax.annotation.Nonnull; -import java.util.Map; - -public class Constant implements Value { - private final @Nonnull Number value; - - public static final Map CONSTANT_MAP = Map.of( - "e", new Constant(Math.E), "pi", new Constant(Math.PI)); - - public static final Constant ZERO = new Constant(0.0); - - public Constant(@Nonnull Number value) { - this.value = value; - } - - @Nonnull - @Override - public Number get() { - return value; - } -} diff --git a/src/main/java/org/scvis/parser/EvaluationException.java b/src/main/java/org/scvis/parser/EvaluationException.java index 49d4ce7..dbc04a7 100644 --- a/src/main/java/org/scvis/parser/EvaluationException.java +++ b/src/main/java/org/scvis/parser/EvaluationException.java @@ -1,8 +1,8 @@ -package org.scvis.parser; - -public class EvaluationException extends RuntimeException { - - public EvaluationException(String message) { - super(message); - } -} +package org.scvis.parser; + +public class EvaluationException extends RuntimeException { + + public EvaluationException(String message) { + super(message); + } +} diff --git a/src/main/java/org/scvis/parser/Function.java b/src/main/java/org/scvis/parser/Function.java index add5840..987c3a3 100644 --- a/src/main/java/org/scvis/parser/Function.java +++ b/src/main/java/org/scvis/parser/Function.java @@ -1,66 +1,67 @@ -package org.scvis.parser; - -import org.scvis.math.Stochastic; - -import javax.annotation.Nonnull; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class Function implements Value { - - private final @Nonnull Number value; - - protected static final @Nonnull Map EVALUATOR_MAP = new HashMap<>(); - static { - EVALUATOR_MAP.putAll(Map.of( - "sin", checked(a -> Math.sin(a[0].doubleValue()), 1), - "cos", checked(a -> Math.cos(a[0].doubleValue()), 1), - "tan", checked(a -> Math.tan(a[0].doubleValue()), 1), - "asin", checked(a -> Math.asin(a[0].doubleValue()), 1), - "acos", checked(a -> Math.acos(a[0].doubleValue()), 1), - "atan", checked(a -> Math.atan(a[0].doubleValue()), 1), - "sqrt", checked(a -> Math.sqrt(a[0].doubleValue()), 1), - "abs", checked(a -> Math.abs(a[0].doubleValue()), 1), - "signum", checked(a -> Math.signum(a[0].doubleValue()), 1), - "hypot", checked(a -> Math.hypot(a[0].doubleValue(), a[1].doubleValue()), 2) - )); - EVALUATOR_MAP.putAll(Map.of( - "fac", checked(a -> Stochastic.factorial(a[0].intValue()), 1), - "binom", checked(a -> Stochastic.binom(a[0].intValue(), a[1].intValue()), 2), - "bernoulli_pdf", checked(a -> Stochastic.bernoulliPdf(a[0].doubleValue(), a[1].intValue(), - a[2].intValue()), 3), - "bernoulli_cdf", checked(a -> Stochastic.bernoulliCdf(a[0].doubleValue(), a[1].intValue(), - a[2].intValue(), a[3].intValue()), 4), - "minimal_attempts", checked(a -> Stochastic.minimalAttempts(a[0].doubleValue(), - a[1].doubleValue(), a[2].intValue()), 3) - )); - } - - public Function(@Nonnull String name, @Nonnull List operators, @Nonnull List tokens) { - Evaluator evaluator = EVALUATOR_MAP.get(name.toLowerCase()); - if (evaluator == null) - throw new EvaluationException("No method found for " + name); - List values = new TokenEvaluator(operators, tokens).evaluate(); - value = evaluator.evaluate(values.toArray(new Number[0])); - } - - @FunctionalInterface - public interface Evaluator { - Number evaluate(Number... args); - } - - public static Evaluator checked(Evaluator evaluator, int count) { - return args -> { - if (count != args.length) - throw new EvaluationException("Expected " + count + " arguments, got " + args.length); - return evaluator.evaluate(args); - }; - } - - @Nonnull - @Override - public Number get() { - return value; - } -} +package org.scvis.parser; + +import org.scvis.math.Stochastic; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Function implements Value { + + private final @Nonnull Object value; + + protected static final @Nonnull Map EVALUATOR_MAP = new HashMap<>(); + + static { + EVALUATOR_MAP.putAll(Map.of( + "sin", checked(a -> Math.sin(a[0].getDouble()), 1), + "cos", checked(a -> Math.cos(a[0].getDouble()), 1), + "tan", checked(a -> Math.tan(a[0].getDouble()), 1), + "asin", checked(a -> Math.asin(a[0].getDouble()), 1), + "acos", checked(a -> Math.acos(a[0].getDouble()), 1), + "atan", checked(a -> Math.atan(a[0].getDouble()), 1), + "sqrt", checked(a -> Math.sqrt(a[0].getDouble()), 1), + "abs", checked(a -> Math.abs(a[0].getDouble()), 1), + "signum", checked(a -> Math.signum(a[0].getDouble()), 1), + "hypot", checked(a -> Math.hypot(a[0].getDouble(), a[1].getDouble()), 2) + )); + EVALUATOR_MAP.putAll(Map.of( + "fac", checked(a -> Stochastic.factorial(a[0].getInteger()), 1), + "binom", checked(a -> Stochastic.binom(a[0].getInteger(), a[1].getInteger()), 2), + "bernoulli_pdf", checked(a -> Stochastic.bernoulliPdf(a[0].getDouble(), a[1].getInteger(), + a[2].getInteger()), 3), + "bernoulli_cdf", checked(a -> Stochastic.bernoulliCdf(a[0].getDouble(), a[1].getInteger(), + a[2].getInteger(), a[3].getInteger()), 4), + "minimal_attempts", checked(a -> Stochastic.minimalAttempts(a[0].getDouble(), + a[1].getDouble(), a[2].getInteger()), 3) + )); + } + + public Function(@Nonnull String name, @Nonnull List operators, @Nonnull List tokens) { + Evaluator evaluator = EVALUATOR_MAP.get(name.toLowerCase()); + if (evaluator == null) + throw new EvaluationException("No method found for " + name); + List> values = new TokenEvaluator(operators, tokens).evaluate(); + value = evaluator.evaluate(values.toArray(new Value[0])); + } + + @FunctionalInterface + public interface Evaluator { + Object evaluate(Value... args); + } + + public static Evaluator checked(Evaluator evaluator, int count) { + return args -> { + if (count != args.length) + throw new EvaluationException("Expected " + count + " arguments, got " + args.length); + return evaluator.evaluate(args); + }; + } + + @Nonnull + @Override + public Object get() { + return value; + } +} diff --git a/src/main/java/org/scvis/parser/NumberValue.java b/src/main/java/org/scvis/parser/NumberValue.java new file mode 100644 index 0000000..4157b09 --- /dev/null +++ b/src/main/java/org/scvis/parser/NumberValue.java @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.parser; + +import javax.annotation.Nonnull; +import java.util.Map; + +public class NumberValue implements Value { + + + /** + * A map of all constants. + */ + public static final Map CONSTANT_MAP = Map.of( + "e", new NumberValue(Math.E), "pi", new NumberValue(Math.PI)); + + public static final NumberValue ZERO = new NumberValue(0.0); + + private final @Nonnull Number value; + + public NumberValue(@Nonnull Number value) { + this.value = value; + } + + @Nonnull + @Override + public Number get() { + return value; + } +} diff --git a/src/main/java/org/scvis/parser/Operator.java b/src/main/java/org/scvis/parser/Operator.java index 345d40b..837c047 100644 --- a/src/main/java/org/scvis/parser/Operator.java +++ b/src/main/java/org/scvis/parser/Operator.java @@ -1,40 +1,95 @@ -package org.scvis.parser; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; - -public interface Operator extends Token, Comparable { - - Operator SEPARATOR = new Operator() { - @Nonnull - @Override - public Value evaluate(@Nonnull Value left, @Nonnull Value right) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isSign() { - return false; - } - - @Override - public int priority() { - return -1; - } - }; - - @CheckReturnValue - @Nonnull - Value evaluate(@Nonnull Value left, @Nonnull Value right); - - @CheckReturnValue - boolean isSign(); - - @CheckReturnValue - int priority(); - - @Override - default int compareTo(@Nonnull Operator o) { - return o.priority() - priority(); - } -} +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.parser; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * An operator always evaluates two values and returns a new value. Therefore, ! is not an operator, + * because it only evaluates one value. Each operator can also be a sign. A sign can take 0 as a left value + * if no left value is given. If it can not get a left or right value, a {@link EvaluationException} is thrown. + * + * @author karlz + * @see Token + * @see Comparable + * @see EvaluationException + */ +public interface Operator extends Token, Comparable { + + /** + * Default implementation of a separator operator. A separator split arguments of functions or separates expressions + * to parse. The chars , and ; are assigned to this operator. + */ + Operator SEPARATOR = new Operator() { + @Nonnull + @Override + public Value evaluate(@Nonnull Value left, @Nonnull Value right) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSign() { + return false; + } + + @Override + public int priority() { + return -1; + } + }; + + /** + * Evaluates the left and right value and returns the new value. + * + * @param left the left value + * @param right the right value + * @return the new evaluated value + */ + @CheckReturnValue + @Nonnull + Value evaluate(@Nonnull Value left, @Nonnull Value right); + + /** + * A sign is an operator that can take 0 as a left value. + * + * @return if this operator is a sign + */ + @CheckReturnValue + boolean isSign(); + + /** + * The priority is the sequence, in which the values are evaluated. + * + * @return the priority + */ + @CheckReturnValue + int priority(); + + @Override + default int compareTo(@Nonnull Operator o) { + return o.priority() - priority(); + } +} diff --git a/src/main/java/org/scvis/parser/StringValue.java b/src/main/java/org/scvis/parser/StringValue.java new file mode 100644 index 0000000..0966797 --- /dev/null +++ b/src/main/java/org/scvis/parser/StringValue.java @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.parser; + +import javax.annotation.Nonnull; + +public class StringValue implements Value { + private final @Nonnull String value; + + public StringValue(@Nonnull String value) { + this.value = value; + } + + @Nonnull + @Override + public String get() { + return value; + } +} diff --git a/src/main/java/org/scvis/parser/Token.java b/src/main/java/org/scvis/parser/Token.java index 5013b11..8683150 100644 --- a/src/main/java/org/scvis/parser/Token.java +++ b/src/main/java/org/scvis/parser/Token.java @@ -1,12 +1,18 @@ -package org.scvis.parser; - -public interface Token { - - default boolean isOperator() { - return this instanceof Operator; - } - - default boolean isValue() { - return this instanceof Value; - } -} +package org.scvis.parser; + +/** + * A token has a specified function in an expression. + * + * @author karlz + */ +public interface Token { + + /** + * Checks if this token is a value. + * + * @return true if this is a value, false otherwise + */ + default boolean isValue() { + return this instanceof Value; + } +} diff --git a/src/main/java/org/scvis/parser/TokenEvaluator.java b/src/main/java/org/scvis/parser/TokenEvaluator.java index 20f2e6a..7048ffc 100644 --- a/src/main/java/org/scvis/parser/TokenEvaluator.java +++ b/src/main/java/org/scvis/parser/TokenEvaluator.java @@ -1,64 +1,67 @@ -package org.scvis.parser; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import java.util.*; - -public class TokenEvaluator { - - private final @Nonnull List operators; - private final @Nonnull List tokens; - - public TokenEvaluator(@Nonnull List operators, @Nonnull List tokens) { - this.operators = operators; - Collections.sort(operators); - this.tokens = tokens; - } - - private void eliminate() { - for (Operator operator : operators) { - if (operator == Operator.SEPARATOR) break; - eliminate(operator); - } - operators.clear(); - } - - private void eliminate(Operator operator) { - int index = tokens.indexOf(operator); - if (index == 0) { - Token right = tokens.get(1); - if (operator.isSign() && right.isValue()) { - Value evaluated = operator.evaluate(Constant.ZERO, (Value) right); - tokens.remove(0); - tokens.set(0, evaluated); - } else { - throw new EvaluationException("Operators that are not signs require a left and right value"); - } - } else { - Token left = tokens.get(index - 1); - Token right = tokens.get(index + 1); - if (left.isValue() && right.isValue()) { - Value evaluated = operator.evaluate((Value) left, (Value) right); - tokens.remove(index + 1); - tokens.remove(index); - tokens.set(index - 1, evaluated); - } else { - throw new EvaluationException("Tokens left and right must be values"); - } - } - } - - @CheckReturnValue - @Nonnull - public List evaluate() { - eliminate(); - ArrayList values = new ArrayList<>(tokens.size() / 2 + 1); - int i = 0; - for (Token token : tokens) { - if (i % 2 == 0) values.add(((Value) token).get()); - else if (token != Operator.SEPARATOR) throw new EvaluationException("Expected separator, got " + token); - i++; - } - return values; - } -} +package org.scvis.parser; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.util.*; + +public class TokenEvaluator { + + private final @Nonnull List operators; + private final @Nonnull List tokens; + + public TokenEvaluator(@Nonnull List operators, @Nonnull List tokens) { + this.operators = operators; + Collections.sort(operators); + this.tokens = tokens; + } + + private void eliminate() { + for (Operator operator : operators) { + if (operator == Operator.SEPARATOR) break; + eliminate(operator); + } + operators.clear(); + } + + private void eliminate(Operator operator) { + int index = tokens.indexOf(operator); + if (index == -1) + throw new EvaluationException("Operator must be present in tokens"); + if (index == 0) { + Token right = tokens.get(1); + if (operator.isSign() && right.isValue()) { + Value evaluated = operator.evaluate(NumberValue.ZERO, (Value) right); + tokens.remove(0); + tokens.set(0, evaluated); + } else { + throw new EvaluationException("Operators that are not signs require a left and right value"); + } + } else { + Token left = tokens.get(index - 1); + Token right = tokens.get(index + 1); + if (left.isValue() && right.isValue()) { + Value evaluated = operator.evaluate((Value) left, (Value) right); + tokens.remove(index + 1); + tokens.remove(index); + tokens.set(index - 1, evaluated); + } else { + throw new EvaluationException("Tokens left and right must be values"); + } + } + } + + @CheckReturnValue + @Nonnull + public List> evaluate() { + eliminate(); + ArrayList> values = new ArrayList<>(tokens.size() / 2 + 1); + int i = 0; + for (Token token : tokens) { + if (i % 2 == 0) values.add(((Value) token)); + else if (token != Operator.SEPARATOR) + throw new EvaluationException("Expected separator, got " + token.getClass().getSimpleName()); + i++; + } + return values; + } +} diff --git a/src/main/java/org/scvis/parser/TokenParser.java b/src/main/java/org/scvis/parser/TokenParser.java index 07378f4..ff60468 100644 --- a/src/main/java/org/scvis/parser/TokenParser.java +++ b/src/main/java/org/scvis/parser/TokenParser.java @@ -1,148 +1,190 @@ -package org.scvis.parser; - -import org.scvis.math.Stochastic; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; -import java.util.*; - -public class TokenParser { - - public static final @Nonnull Map CHARACTER_OPERATOR_MAP = Map.of( - '+', BaseOperator.ADD, '-', BaseOperator.SUBTRACT, '*', BaseOperator.MULTIPLY, - '/', BaseOperator.DIVIDE, '%', BaseOperator.MOD, '^', BaseOperator.POW, - ',', Operator.SEPARATOR, ';', Operator.SEPARATOR, '=', BaseOperator.EQU); - - private int pos; - - private final @Nonnull List operators = new ArrayList<>(); - - private final @Nonnull List tokens = new ArrayList<>(); - - public void tokenize(@Nonnull String string) { - tokenize(string.toCharArray(), 0); - } - - public void tokenize(@Nonnull char[] chars, int start) { - for (pos = start; pos < chars.length; pos++) { - char c = chars[pos]; - if (Character.isDigit(c) || c == '.') { - tokens.add(parseNumber(chars)); - } else if (Character.isAlphabetic(c)) { - tokens.add(parseText(chars)); - } else if (CHARACTER_OPERATOR_MAP.containsKey(c)) { - Operator operator = CHARACTER_OPERATOR_MAP.get(c); - operators.add(operator); - tokens.add(operator); - } else { - switch (c) { - case '!': - applyFactorial(); - break; - case '(': - tokens.add(parseBrackets(chars)); - break; - case ')': - return; - case ' ': - break; - default: - throw new EvaluationException("Could not parse char " + c); - } - } - } - } - - private void applyFactorial() { - int index = tokens.size() - 1; - Token last = tokens.get(index); - if (last.isValue()) { - tokens.set(index, new Constant(Stochastic.factorial(((Value) last).get().intValue()))); - } else { - throw new EvaluationException("Factorial requires a left value"); - } - } - - @CheckReturnValue - @Nonnull - private Constant parseVar(@Nonnull String name) { - Constant constant = Constant.CONSTANT_MAP.get(name.toLowerCase()); - if (constant == null) - throw new EvaluationException("No variable found for " + name); - return constant; - } - - @CheckReturnValue - @Nonnull - private TokenParser parseSubTokens(@Nonnull char[] chars) { - TokenParser parser = new TokenParser(); - parser.tokenize(chars, pos + 1); - pos = parser.pos; - return parser; - } - - @CheckReturnValue - @Nonnull - private Brackets parseBrackets(@Nonnull char[] chars) { - TokenParser parser = parseSubTokens(chars); - return new Brackets(parser.operators, parser.tokens); - } - - @CheckReturnValue - @Nonnull - private Function parseFunction(@Nonnull String name, @Nonnull char[] chars) { - TokenParser parser = parseSubTokens(chars); - return new Function(name, parser.operators, parser.tokens); - } - - @CheckReturnValue - @Nonnull - private Token parseText(@Nonnull char[] chars) { - StringBuilder build = new StringBuilder(); - for (;pos < chars.length; pos++) { - char c = chars[pos]; - if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '_') - build.append(chars[pos]); - else break; - } - if (pos < chars.length && chars[pos] == '(') { - return parseFunction(build.toString(), chars); - } else { - pos--; - return parseVar(build.toString()); - } - } - - @CheckReturnValue - @Nonnull - private Token parseNumber(@Nonnull char[] chars) { - double build = 0; - int real = -1; - for (;pos < chars.length; pos++) { - char c = chars[pos]; - if (Character.isDigit(c)) { - build = build * 10.0 + Character.digit(c, 10); - } else if (c == '.') { - if (real > -1) - throw new EvaluationException("Could not parse number by " + c); - real = pos; - } else { - pos--; - break; - } - } - if (real > -1) - build /= Math.pow(10.0, pos - real - 1); - return new Constant(build); - } - - @Nonnull - public List getOperators() { - return operators; - } - - @Nonnull - public List getTokens() { - return tokens; - } -} +package org.scvis.parser; + +import org.scvis.math.Stochastic; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.util.*; + +/** + * TokenParser parses a string into tokens and operators. The tokens contain all operators. + * + * @author karlz + * @see Token + * @see Value + * @see Operator + */ +public class TokenParser { + + /** + * A map of all characters to its corresponding value. You can add or change this mapping for your own purpose. + */ + public static final @Nonnull Map CHARACTER_OPERATOR_MAP = Map.of( + '+', BaseOperator.ADD, '-', BaseOperator.SUBTRACT, '*', BaseOperator.MULTIPLY, + '/', BaseOperator.DIVIDE, '%', BaseOperator.MOD, '^', BaseOperator.POW, + ',', Operator.SEPARATOR, ';', Operator.SEPARATOR, '=', BaseOperator.EQU); + + private int pos; + + private final @Nonnull List operators = new ArrayList<>(); + + private final @Nonnull List tokens = new ArrayList<>(); + + /** + * Parses a string from the beginning and stores the tokens. + * + * @param string the string to evaluate + */ + public void tokenize(@Nonnull String string) { + tokenize(string.toCharArray(), 0); + } + + /** + * Parses a char array from the specified start index and stores the tokens. + * + * @param chars the char array + * @param start the start index + * @throws EvaluationException if the start index is < 0 + */ + public void tokenize(@Nonnull char[] chars, int start) { + if (start < 0) + throw new EvaluationException("Start index must >= 0"); + for (pos = start; pos < chars.length; pos++) { + char c = chars[pos]; + if (Character.isDigit(c) || c == '.') { + tokens.add(parseNumber(chars)); + } else if (Character.isAlphabetic(c)) { + tokens.add(parseText(chars)); + } else if (CHARACTER_OPERATOR_MAP.containsKey(c)) { + Operator operator = CHARACTER_OPERATOR_MAP.get(c); + operators.add(operator); + tokens.add(operator); + } else { // Parse one specified char or throw an exception + switch (c) { + case '!': + applyFactorial(); + break; + case '(': + tokens.add(parseBrackets(chars)); + break; + case ')': + return; + case ' ': + break; + default: + throw new EvaluationException("Could not parse char " + c); + } + } + } + } + + /** + * Call this method always if you want to parse multiple strings. If not, this may raise an exception. + * + * @throws EvaluationException TODO + */ + public void chain() { + if (tokens.isEmpty()) + throw new EvaluationException("Can not chain parsing before anything was parsed"); + else if (tokens.size() != operators.size() + 1) + throw new EvaluationException("The token list must be by one element greater then the operator list"); + else if (tokens.get(tokens.size() - 1) == Operator.SEPARATOR || operators.get(operators.size() - 1) == + Operator.SEPARATOR) + throw new EvaluationException("You can not chain again before evaluating something"); + tokens.add(Operator.SEPARATOR); + operators.add(Operator.SEPARATOR); + } + + private void applyFactorial() { + int index = tokens.size() - 1; + Token last = tokens.get(index); + if (last.isValue()) { + tokens.set(index, new NumberValue(Stochastic.factorial(((NumberValue) last).get().intValue()))); + } else { + throw new EvaluationException("Factorial requires a left value"); + } + } + + @CheckReturnValue + @Nonnull + private NumberValue parseVar(@Nonnull String name) { + NumberValue numberValue = NumberValue.CONSTANT_MAP.get(name.toLowerCase()); + if (numberValue == null) + throw new EvaluationException("No variable found for " + name); + return numberValue; + } + + @CheckReturnValue + @Nonnull + private TokenParser parseSubTokens(@Nonnull char[] chars) { + TokenParser parser = new TokenParser(); + parser.tokenize(chars, pos + 1); + pos = parser.pos; + return parser; + } + + @CheckReturnValue + @Nonnull + private Brackets parseBrackets(@Nonnull char[] chars) { + TokenParser parser = parseSubTokens(chars); + return new Brackets(parser.operators, parser.tokens); + } + + @CheckReturnValue + @Nonnull + private Function parseFunction(@Nonnull String name, @Nonnull char[] chars) { + TokenParser parser = parseSubTokens(chars); + return new Function(name, parser.operators, parser.tokens); + } + + @CheckReturnValue + @Nonnull + private Token parseText(@Nonnull char[] chars) { + StringBuilder build = new StringBuilder(); + for (; pos < chars.length; pos++) { + char c = chars[pos]; + if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '_') + build.append(chars[pos]); + else break; + } + if (pos < chars.length && chars[pos] == '(') { + return parseFunction(build.toString(), chars); + } else { + pos--; + return parseVar(build.toString()); + } + } + + @CheckReturnValue + @Nonnull + private Token parseNumber(@Nonnull char[] chars) { + double build = 0; + int real = -1; + for (; pos < chars.length; pos++) { + char c = chars[pos]; + if (Character.isDigit(c)) { + build = build * 10.0 + Character.digit(c, 10); + } else if (c == '.') { + if (real > -1) + throw new EvaluationException("Could not parse number by " + c); + real = pos; + } else { + pos--; + break; + } + } + if (real > -1) + build /= Math.pow(10.0, pos - real - 1); + return new NumberValue(build); + } + + @Nonnull + public List getOperators() { + return operators; + } + + @Nonnull + public List getTokens() { + return tokens; + } +} diff --git a/src/main/java/org/scvis/parser/Value.java b/src/main/java/org/scvis/parser/Value.java index 202cc44..67f0168 100644 --- a/src/main/java/org/scvis/parser/Value.java +++ b/src/main/java/org/scvis/parser/Value.java @@ -1,11 +1,80 @@ -package org.scvis.parser; - -import javax.annotation.CheckReturnValue; -import javax.annotation.Nonnull; - -public interface Value extends Token { - - @CheckReturnValue - @Nonnull - Number get(); -} +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package org.scvis.parser; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * The value interface defines any token that can return an associated value. This can be static values like + * {@link NumberValue} or dynamics values like {@link Function}. + * + * @author karlz + * @see Token + */ +public interface Value extends Token { + + static Value toValue(final I value) { + return () -> value; + } + + /** + * The associated number value. + * + * @return the value. + */ + @CheckReturnValue + @Nonnull + T get(); + + default double getDouble() { + if (get() instanceof Number) { + return ((Number) get()).doubleValue(); + } + throw new EvaluationException("Value must be a number"); + } + + default int getInteger() { + if (get() instanceof Number) { + return ((Number) get()).intValue(); + } + throw new EvaluationException("Value must be a number"); + } + + default String getString() { + if (get() instanceof String) { + return (String) get(); + } + throw new EvaluationException("Value must be a string"); + } + + default Value[] getArray() { + if (get() instanceof Value[]) { + return (Value[]) get(); + } + throw new EvaluationException("Value must be an array"); + } +} diff --git a/src/test/java/org/scvis/math/TestEquations.java b/src/test/java/org/scvis/math/TestEquations.java new file mode 100644 index 0000000..f36c187 --- /dev/null +++ b/src/test/java/org/scvis/math/TestEquations.java @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TestEquations { + + @Test + void solveMatrix() { + assertArrayEquals(new double[]{1, -1}, + Equations.solve(Vector.toMatrix(new Vector(2.0, 3.0), new Vector(5.0, 3.0)), new Vector(-1.0, 2.0)) + .getElements()); + } + + @Test + void solveArray() { + assertArrayEquals(new double[]{1, -1}, Equations.solve(new double[][]{{2, 3}, {5, 3}}, new double[]{-1, 2})); + } + + @Test + void solveABCDEF() { + assertArrayEquals(new double[]{1, -1}, Equations.solve(2, 3, -1, 5, 3, 2)); + assertArrayEquals(new double[]{5, 3}, Equations.solve(1, 3, 14, 4, -2, 14)); + } + + @Test + void solvePQ() { + assertArrayEquals(new double[]{3, -8}, Equations.solve(5, -24)); + } + + @Test + void solveABC() { + assertEquals(0, Equations.solve(2, 4, 5).length); + + double[] solutions = Equations.solve(2, 4, -16); + double x0 = solutions[0]; + assertEquals(0, 2 * x0 * x0 + 4 * x0 - 16); + double x1 = solutions[1]; + assertEquals(0, 2 * x1 * x1 + 4 * x1 - 16); + } +} \ No newline at end of file diff --git a/src/test/java/org/scvis/math/TestMatrix.java b/src/test/java/org/scvis/math/TestMatrix.java index 8784d6d..628ed53 100644 --- a/src/test/java/org/scvis/math/TestMatrix.java +++ b/src/test/java/org/scvis/math/TestMatrix.java @@ -1,37 +1,37 @@ -package org.scvis.math; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class TestMatrix { - - @Test - void add() { - Matrix a = new Matrix(new double[][]{{1, 2}, {3, 4}}); - Matrix b = new Matrix(new double[][]{{5, 6}, {7, 8}}); - - Assertions.assertDoesNotThrow(( ) -> { - System.out.println(a.add(b)); - }); - } - - @Test - void subtract() { - Matrix a = new Matrix(new double[][]{{1, 2}, {3, 4}}); - Matrix b = new Matrix(new double[][]{{5, 6}, {7, 8}}); - - Assertions.assertDoesNotThrow(( ) -> { - System.out.println(a.subtract(b)); - }); - } - - @Test - void multiply() { - Matrix a = new Matrix(new double[][]{{1, 2}, {3, 4}}); - Matrix b = new Matrix(new double[][]{{5, 6}, {7, 8}}); - - Assertions.assertDoesNotThrow(() -> { - System.out.println(a.multiply(b)); - }); - } -} +package org.scvis.math; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class TestMatrix { + + @Test + void add() { + Matrix a = new Matrix(new double[][]{{1, 2}, {3, 4}}); + Matrix b = new Matrix(new double[][]{{5, 6}, {7, 8}}); + + Assertions.assertDoesNotThrow(() -> { + System.out.println(a.add(b)); + }); + } + + @Test + void subtract() { + Matrix a = new Matrix(new double[][]{{1, 2}, {3, 4}}); + Matrix b = new Matrix(new double[][]{{5, 6}, {7, 8}}); + + Assertions.assertDoesNotThrow(() -> { + System.out.println(a.subtract(b)); + }); + } + + @Test + void multiply() { + Matrix a = new Matrix(new double[][]{{1, 2}, {3, 4}}); + Matrix b = new Matrix(new double[][]{{5, 6}, {7, 8}}); + + Assertions.assertDoesNotThrow(() -> { + System.out.println(a.multiply(b)); + }); + } +} diff --git a/src/test/java/org/scvis/math/TestPoint2D.java b/src/test/java/org/scvis/math/TestPoint2D.java index d4b8d9d..46c196d 100644 --- a/src/test/java/org/scvis/math/TestPoint2D.java +++ b/src/test/java/org/scvis/math/TestPoint2D.java @@ -1,31 +1,32 @@ -package org.scvis.math; - -import java.util.stream.Stream; - -import javax.annotation.Nonnull; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class TestPoint2D { - - @ParameterizedTest - @MethodSource("vectors") - void subtract(@Nonnull Point2D exp, @Nonnull Point2D val) { - assertEquals(exp, val.subtract(new Point2D(100, 100))); - } - - @Test - void distance() { - assertEquals(Math.sqrt(2), new Point2D(1, 1).distance(Point2D.ZERO)); - } - - static Stream vectors() { - return Stream.of(Arguments.of(new Point2D(100, 200), new Point2D(200, 300)), - Arguments.of(new Point2D(400, 200), new Point2D(500, 300)), - Arguments.of(new Point2D(0, 0), new Point2D(100, 100))); - } -} +package org.scvis.math; + +import java.util.stream.Stream; + +import javax.annotation.Nonnull; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class TestPoint2D { + + @ParameterizedTest + @MethodSource("vectors") + void subtract(@Nonnull Point2D exp, @Nonnull Point2D val) { + assertEquals(exp, val.subtract(new Point2D(100, 100))); + } + + @Test + void distance() { + assertEquals(Math.sqrt(2), new Point2D(1, 1).distance(Point2D.ZERO)); + } + + static Stream vectors() { + return Stream.of(Arguments.of(new Point2D(100, 200), new Point2D(200, 300)), + Arguments.of(new Point2D(400, 200), new Point2D(500, 300)), + Arguments.of(new Point2D(0, 0), new Point2D(100, 100))); + } +} diff --git a/src/test/java/org/scvis/math/TestPoint3D.java b/src/test/java/org/scvis/math/TestPoint3D.java index a94bdda..9933c12 100644 --- a/src/test/java/org/scvis/math/TestPoint3D.java +++ b/src/test/java/org/scvis/math/TestPoint3D.java @@ -1,4 +1,41 @@ -package org.scvis.math; - -public class TestPoint3D { -} +package org.scvis.math; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class TestPoint3D { + + @Test + void isMultiplicative() { + assertTrue(new Point3D(2, 4, 6).isMultiplicative(new Point3D(4, 8, 12))); + assertFalse(new Point3D(2, 4, 6).isMultiplicative(new Point3D(4, 8, 11))); + assertTrue(new Point3D(0, 0, 0).isMultiplicative(new Point3D(0, 0, 0))); + assertFalse(new Point3D(0, 0, 0).isMultiplicative(new Point3D(1, 0, 0))); + } + + @Test + void getMultiplicative() { + assertEquals(2, new Point3D(2, 4, 6).getMultiplicative(new Point3D(4, 8, 12))); + assertEquals(Double.NaN, new Point3D(2, 4, 6).getMultiplicative(new Point3D(4, 8, 11))); + assertEquals(0, new Point3D(0, 0, 0).getMultiplicative(new Point3D(0, 0, 0))); + assertEquals(Double.NaN, new Point3D(0, 0, 0).getMultiplicative(new Point3D(1, 0, 0))); + } + + @Test + void midpoint() { + assertEquals(new Point3D(2, 2, 2), new Point3D(1, 1, 1).midpoint(new Point3D(3, 3, 3))); + } + + @Test + void normalize() { + assertEquals(new Point3D(1, 0, 0), new Point3D(1, 0, 0).normalize()); + assertEquals(new Point3D(1 / Math.sqrt(2), 1 / Math.sqrt(2), 0), new Point3D(1, 1, 0).normalize()); + assertEquals(Point3D.ZERO, new Point3D(0, 0, 0).normalize()); + } + + @Test + void magnitude() { + assertEquals(Math.sqrt(3), new Point3D(1, 1, 1).magnitude()); + } +} diff --git a/src/test/java/org/scvis/math/TestPolygon.java b/src/test/java/org/scvis/math/TestPolygon.java index be7f9e3..d7cd810 100644 --- a/src/test/java/org/scvis/math/TestPolygon.java +++ b/src/test/java/org/scvis/math/TestPolygon.java @@ -1,18 +1,18 @@ -package org.scvis.math; - -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -class TestPolygon { - - @Test - void contains() { - Polygon polygon = new Polygon(List.of(new Point2D(0, 0), new Point2D(10, 0), - new Point2D(10, 10), new Point2D(0, 10))); - assertTrue(polygon.contains(new Point2D(8, 8))); - assertFalse(polygon.contains(new Point2D(11, 0))); - } -} +package org.scvis.math; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class TestPolygon { + + @Test + void contains() { + Polygon polygon = new Polygon(List.of(new Point2D(0, 0), new Point2D(10, 0), + new Point2D(10, 10), new Point2D(0, 10))); + assertTrue(polygon.contains(new Point2D(8, 8))); + assertFalse(polygon.contains(new Point2D(11, 0))); + } +} diff --git a/src/test/java/org/scvis/math/TestRectangle.java b/src/test/java/org/scvis/math/TestRectangle.java new file mode 100644 index 0000000..b7aaaa7 --- /dev/null +++ b/src/test/java/org/scvis/math/TestRectangle.java @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TestRectangle { + + @Test + void contains() { + assertTrue(new Rectangle(200, 300).contains(new Point2D(100, 50))); + assertTrue(new Rectangle(100, 100).contains(new Point2D(100, 100))); + } + + @Test + void intersects() { + } + + @Test + void translate() { + assertEquals(523, new Rectangle(100, 100).translate(423, 0).getWidth()); + assertEquals(523, new Rectangle(100, 100).translate(new Point2D(423, 0)).getWidth()); + } + + @Test + void centroid() { + assertEquals(new Point2D(50, 50), new Rectangle(100, 100).centroid()); + } +} \ No newline at end of file diff --git a/src/test/java/org/scvis/math/TestStochastic.java b/src/test/java/org/scvis/math/TestStochastic.java index 4154f75..f4ac6f5 100644 --- a/src/test/java/org/scvis/math/TestStochastic.java +++ b/src/test/java/org/scvis/math/TestStochastic.java @@ -1,50 +1,50 @@ -package org.scvis.math; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -import static org.junit.jupiter.api.Assertions.*; - -class TestStochastic { - - @ParameterizedTest - @CsvSource("0.25, 0.5, 2, 2") - void bernoulliPdf(double exp, double p, int n, int k) { - assertEquals(exp, Stochastic.bernoulliPdf(p, n, k)); - } - - - @ParameterizedTest - @CsvSource("1.0, 0.5, 2, 0, 2") - void bernoulliCdf(double exp, double p, int n, int min, int max) { - assertEquals(exp, Stochastic.bernoulliCdf(p, n, min, max)); - } - - @ParameterizedTest - @CsvSource({"6, 3", "40_320, 8", "1_307_674_368_000, 15"}) - void factorial(long exp, int val) { - assertEquals(exp, Stochastic.factorial(val)); - } - - - @ParameterizedTest - @CsvSource({"3, 3, 2", "4, 4, 3", "3, 3, 1", "6, 4, 2"}) - void combU(int exp, int n, int k) { - assertEquals(exp, Stochastic.combU(n, k)); - } - - @ParameterizedTest - @CsvSource({"6, 3, 2", "20, 4, 3", "3, 3, 1", "10, 4, 2"}) - void combA(int exp, int n, int k) { - assertEquals(exp, Stochastic.combA(n, k)); - } - - @ParameterizedTest - @CsvSource({"1, 0.5, 0.5, 2", "2, 0.5, 1.0, 2", "3, 0.2, 0.8, 10"}) - void minimalAttempts(int exp, double p, double min, int n) { - int result = Stochastic.minimalAttempts(p, min, n); - assertEquals(exp, result); - assertTrue(Stochastic.bernoulliCdf(p, n, 0, result) >= min); - assertFalse(Stochastic.bernoulliCdf(p, n, 0, result - 1) >= min); - } -} +package org.scvis.math; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.*; + +class TestStochastic { + + @ParameterizedTest + @CsvSource("0.25, 0.5, 2, 2") + void bernoulliPdf(double exp, double p, int n, int k) { + assertEquals(exp, Stochastic.bernoulliPdf(p, n, k)); + } + + + @ParameterizedTest + @CsvSource("1.0, 0.5, 2, 0, 2") + void bernoulliCdf(double exp, double p, int n, int min, int max) { + assertEquals(exp, Stochastic.bernoulliCdf(p, n, min, max)); + } + + @ParameterizedTest + @CsvSource({"6, 3", "40_320, 8", "1_307_674_368_000, 15"}) + void factorial(long exp, int val) { + assertEquals(exp, Stochastic.factorial(val)); + } + + + @ParameterizedTest + @CsvSource({"3, 3, 2", "4, 4, 3", "3, 3, 1", "6, 4, 2"}) + void combU(int exp, int n, int k) { + assertEquals(exp, Stochastic.combU(n, k)); + } + + @ParameterizedTest + @CsvSource({"6, 3, 2", "20, 4, 3", "3, 3, 1", "10, 4, 2"}) + void combA(int exp, int n, int k) { + assertEquals(exp, Stochastic.combA(n, k)); + } + + @ParameterizedTest + @CsvSource({"1, 0.5, 0.5, 2", "2, 0.5, 1.0, 2", "3, 0.2, 0.8, 10"}) + void minimalAttempts(int exp, double p, double min, int n) { + int result = Stochastic.minimalAttempts(p, min, n); + assertEquals(exp, result); + assertTrue(Stochastic.bernoulliCdf(p, n, 0, result) >= min); + assertFalse(Stochastic.bernoulliCdf(p, n, 0, result - 1) >= min); + } +} diff --git a/src/test/java/org/scvis/math/TestTriangle.java b/src/test/java/org/scvis/math/TestTriangle.java new file mode 100644 index 0000000..a1f5add --- /dev/null +++ b/src/test/java/org/scvis/math/TestTriangle.java @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2023 Karl Zschiebsch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.scvis.math; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TestTriangle { + + @Test + void contains() { + assertTrue(new Triangle(new Point3D(0, 0, 0), new Point3D(1, 0, 0), new Point3D(0, 1, 0)).contains( + new Point3D(0.2, 0.2, 0))); + } + + @Test + void translate() { + assertEquals(new Triangle(new Point3D(5, 0, 0), new Point3D(6, 0, 0), new Point3D(5, 1, 0)), + new Triangle(new Point3D(0, 0, 0), new Point3D(1, 0, 0), new Point3D(0, 1, 0)).translate(5, 0, 0)); + } + + @Test + void centroid() { + } +} \ No newline at end of file diff --git a/src/test/java/org/scvis/math/TestVector.java b/src/test/java/org/scvis/math/TestVector.java index 800e552..f3b0166 100644 --- a/src/test/java/org/scvis/math/TestVector.java +++ b/src/test/java/org/scvis/math/TestVector.java @@ -1,51 +1,57 @@ -package org.scvis.math; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -class TestVector { - - @ParameterizedTest - @CsvSource("1,2,3,4,5,6,5,7,9") - void add(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz) { - Vector a = new Vector(new double[]{ax, ay, az}); - Vector b = new Vector(new double[]{bx, by, bz}); - - Assertions.assertEquals(new Vector(new double[]{cx, cy, cz}), a.add(b)); - } - - @ParameterizedTest - @CsvSource("4,5,6,1,2,3,3,3,3") - void subtract(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz) { - Vector a = new Vector(new double[]{ax, ay, az}); - Vector b = new Vector(new double[]{bx, by, bz}); - - Assertions.assertEquals(new Vector(new double[]{cx, cy, cz}), a.subtract(b)); - Assertions.assertEquals(new Vector(new double[]{cx, cy, cz}), a.difference(b)); - } - - @ParameterizedTest - @CsvSource("1,0,0,1,0,0") - void normalized(double ax, double ay, double az, double bx, double by, double bz) { - Assertions.assertEquals(new Vector(new double[]{bx, by, bz}), - new Vector(new double[]{ax, ay, az}).normalized()); - } - - @Test - void size() { - Assertions.assertEquals(5, new Vector(new double[]{1, 2, 3, 4, 5}).getSize()); - } - - @Test - void length() { - Assertions.assertEquals(Math.sqrt(2), new Vector(new double[]{1, 1}).length()); - } - - @Test - void distance() { - Assertions.assertEquals(Math.sqrt(2), new Vector(new double[]{1, 1}) - .distance(new Vector(new double[]{2, 2}))); - } -} +package org.scvis.math; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class TestVector { + + @Test + void toMatrix() { + System.out.println(Vector.toMatrix(new Point3D(1, 2, 3).toVector(), new Point3D(4, 5, 6).toVector(), + new Point3D(7, 8, 9).toVector())); + } + + @ParameterizedTest + @CsvSource("1,2,3,4,5,6,5,7,9") + void add(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz) { + Vector a = new Vector(ax, ay, az); + Vector b = new Vector(bx, by, bz); + + Assertions.assertEquals(new Vector(cx, cy, cz), a.add(b)); + } + + @ParameterizedTest + @CsvSource("4,5,6,1,2,3,3,3,3") + void subtract(double ax, double ay, double az, double bx, double by, double bz, double cx, double cy, double cz) { + Vector a = new Vector(ax, ay, az); + Vector b = new Vector(bx, by, bz); + + Assertions.assertEquals(new Vector(cx, cy, cz), a.subtract(b)); + Assertions.assertEquals(new Vector(cx, cy, cz), a.difference(b)); + } + + @ParameterizedTest + @CsvSource("1,0,0,1,0,0") + void normalized(double ax, double ay, double az, double bx, double by, double bz) { + Assertions.assertEquals(new Vector(bx, by, bz), + new Vector(ax, ay, az).normalized()); + } + + @Test + void size() { + Assertions.assertEquals(5, new Vector(1, 2, 3, 4, 5).getSize()); + } + + @Test + void length() { + Assertions.assertEquals(Math.sqrt(2), new Vector(1, 1).length()); + } + + @Test + void distance() { + Assertions.assertEquals(Math.sqrt(2), new Vector(1, 1) + .distance(new Vector(2, 2))); + } +} diff --git a/src/test/java/org/scvis/parser/TestTokenEvaluator.java b/src/test/java/org/scvis/parser/TestTokenEvaluator.java index 4e34cef..b0edf2d 100644 --- a/src/test/java/org/scvis/parser/TestTokenEvaluator.java +++ b/src/test/java/org/scvis/parser/TestTokenEvaluator.java @@ -1,17 +1,17 @@ -package org.scvis.parser; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -class TestTokenEvaluator { - - @Test - void evaluate() { - Assertions.assertEquals(List.of(23.0), new TokenEvaluator(new ArrayList<>( List.of(BaseOperator.ADD, - BaseOperator.MULTIPLY)), new ArrayList<>(List.of(new Constant(3), new Constant(5), - new Constant(4)))).evaluate()); - } -} +package org.scvis.parser; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +class TestTokenEvaluator { + + @Test + void evaluate() { + Assertions.assertEquals(List.of(23.0), new TokenEvaluator(new ArrayList<>(List.of(BaseOperator.ADD, + BaseOperator.MULTIPLY)), new ArrayList<>(List.of(new NumberValue(3), new NumberValue(5), + new NumberValue(4)))).evaluate()); + } +} diff --git a/src/test/java/org/scvis/parser/TestTokenParser.java b/src/test/java/org/scvis/parser/TestTokenParser.java index c2ee870..3aef864 100644 --- a/src/test/java/org/scvis/parser/TestTokenParser.java +++ b/src/test/java/org/scvis/parser/TestTokenParser.java @@ -1,15 +1,15 @@ -package org.scvis.parser; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class TestTokenParser { - - @Test - void parse() { - TokenParser parser = new TokenParser(); - Assertions.assertDoesNotThrow(() -> parser.tokenize("5*4+3")); - Assertions.assertEquals(5, parser.getTokens().size()); - Assertions.assertEquals(2, parser.getOperators().size()); - } -} +package org.scvis.parser; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class TestTokenParser { + + @Test + void parse() { + TokenParser parser = new TokenParser(); + Assertions.assertDoesNotThrow(() -> parser.tokenize("5*4+3")); + Assertions.assertEquals(5, parser.getTokens().size()); + Assertions.assertEquals(2, parser.getOperators().size()); + } +}